From b11bd834c761ee8722d2c7527ea3549a108620e4 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Jan 2020 12:14:03 +0900 Subject: [PATCH 0001/1472] Changed makefile to be local --- build/Makefile | 10 +- build/Makefile_org | 386 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 391 insertions(+), 5 deletions(-) create mode 100755 build/Makefile_org diff --git a/build/Makefile b/build/Makefile index 3b719d4db..2ffaa097a 100755 --- a/build/Makefile +++ b/build/Makefile @@ -38,19 +38,19 @@ # Define core directory below which everything resides. This is the # parent directory of the 'build' directory -##F_MASTER = +F_MASTER =/Users/amedin/Research/summa # Define the Fortran Compiler. If you are using gfortran, then this needs # to be version 6 or higher. This variable is simply used to select the right # compiler flags in the ifeq statements in this Makefile. The compiler # executable is set separately as FC_EXE # Currently this is either gfortran or ifort -##FC = +FC = gfortran # Define the path for the compiler executable. This is the actual executable # that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin-gfortran-mp-6 # FC and FC_EXE have to be consistent -##FC_EXE = +FC_EXE =/opt/local/bin/gfortran # Define the NetCDF and LAPACK libraries and path to include files. # INCLUDES needs to be of the form (no quotes around the string): @@ -59,8 +59,8 @@ # LIBRARIES = '-L -lnetcdff -L -lblas -L -l' # If none of this makes sense, please talk to your system # administrator. -##INCLUDES = -##LIBRARIES = +INCLUDES = -I/opt/local/include +LIBRARIES = -L/opt/local/lib -lnetcdff -llapack -lblas -latlas # Eventually we plan move to a real configure script, but for now we like # to keep track of successful compilations of SUMMA on different platforms diff --git a/build/Makefile_org b/build/Makefile_org new file mode 100755 index 000000000..3b719d4db --- /dev/null +++ b/build/Makefile_org @@ -0,0 +1,386 @@ +#======================================================================== +# Makefile to compile SUMMA +#======================================================================== +# +# Recommended use: Copy this file to Makefile.local, edit it to your +# heart's content, and then run `make -f build/Makefile.local` from +# your top level SUMMA directory. Don't include the Makefile.local in +# any pull requests you make. +# +# Note that Makefile configurations that we commonly use can be found on +# the SUMMA wiki at: +# https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration +# feel free to add yours to that page. +# +# To troubleshoot your paths and setup, type 'make check' +# +# At a minimum you will need to set the following: +# * F_MASTER - top level summa directory +# * FC - compiler suite +# * FC_EXE - compiler executable +# * INCLUDES - path to include files +# * LIBRARIES - path to and libraries to include +# +# Some further options can be specified for OpenMP, etc. See in Part 0 of +# the Makefile. You don't need to make any changes in PART 1 and +# following unless you are doing SUMMA development and changed what +# needs to be compiled + +#======================================================================== +# PART 0: User-configurable part +#======================================================================== + +# The variables can be specified in one of two ways: +# * delete the '##' in front of the variable, fill out the entry, +# save the file and run make +# * make no changes to this file, but specify the variables in your +# environment before you run make + +# Define core directory below which everything resides. This is the +# parent directory of the 'build' directory +##F_MASTER = + +# Define the Fortran Compiler. If you are using gfortran, then this needs +# to be version 6 or higher. This variable is simply used to select the right +# compiler flags in the ifeq statements in this Makefile. The compiler +# executable is set separately as FC_EXE +# Currently this is either gfortran or ifort +##FC = + +# Define the path for the compiler executable. This is the actual executable +# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin-gfortran-mp-6 +# FC and FC_EXE have to be consistent +##FC_EXE = + +# Define the NetCDF and LAPACK libraries and path to include files. +# INCLUDES needs to be of the form (no quotes around the string): +# INCLUDES = -I -I -I<...> -I +# LIBRARIES needs to be of the form ( no quotes around the string): +# LIBRARIES = '-L -lnetcdff -L -lblas -L -l' +# If none of this makes sense, please talk to your system +# administrator. +##INCLUDES = +##LIBRARIES = + +# Eventually we plan move to a real configure script, but for now we like +# to keep track of successful compilations of SUMMA on different platforms +# and with different compilers. If you are successful compiling SUMMA, +# please add your configuration (operating system and compiler plus +# part 0 of the Makefile) to the SUMMA wiki on github. + +# Define compiler flags. If you use a different compiler, +# you will need to figure out what the equivalent flags are +# and may need to update this section + +# ------------ define compiler flags ---------------------------------------- + +# define open MP flags +isOpenMP = +FLAGS_OMP = +LIBOPENMP = + +# Define compiler flags. If you use a different compiler, +# you will need to figure out what the equivalent flags are +# and may need to update this section + +# gfortran compiler flags +ifeq "$(FC)" "gfortran" + + ifeq "$(isOpenMP)" "yes" + FLAGS_OMP = -fopenmp + endif + +# Production runs +FLAGS_NOAH = -O3 -ffree-form -fdefault-real-8 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) + +# Debug runs +#FLAGS_NOAH = -p -g -ffree-form -fdefault-real-8 -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument +#FLAGS_COMM = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds +#FLAGS_SUMMA = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds + +endif + +# ifort compiler flags +ifeq "$(FC)" "ifort" + + ifeq "$(isOpenMP)" "yes" + FLAGS_OMP = -qopenmp + endif + +# Production runs +FLAGS_NOAH = -O3 -autodouble -noerror_limit -FR -auto -fltconsistency $(FLAGS_OMP) +FLAGS_COMM = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) +FLAGS_SUMMA = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) + +# Debug runs +#FLAGS_NOAH = -O0 -p -g -warn nounused -autodouble -noerror_limit -FR -auto -WB -traceback -fltconsistency +#FLAGS_COMM = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 +#FLAGS_SUMMA = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 +endif + +#======================================================================== +# PART 1: Define directory paths +#======================================================================== + +# Core directory that contains source code +F_KORE_DIR = $(F_MASTER)/build/source + +# Location of the compiled modules +MOD_PATH = $(F_MASTER)/build + +# Define the directory for the executables +EXE_PATH = $(F_MASTER)/bin + +#======================================================================== +# PART 2: Assemble all of the SUMMA sub-routines +#======================================================================== + +# Define directories +DRIVER_DIR = $(F_KORE_DIR)/driver +HOOKUP_DIR = $(F_KORE_DIR)/hookup +NETCDF_DIR = $(F_KORE_DIR)/netcdf +DSHARE_DIR = $(F_KORE_DIR)/dshare +NUMREC_DIR = $(F_KORE_DIR)/numrec +NOAHMP_DIR = $(F_KORE_DIR)/noah-mp +ENGINE_DIR = $(F_KORE_DIR)/engine + +# utilities +SUMMA_NRUTIL= \ + nrtype.f90 \ + f2008funcs.f90 \ + nr_utility.f90 +NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) + +# +# Numerical recipes procedures +# NOTE: all numerical recipes procedures are now replaced with free versions +SUMMA_NRPROC= \ + expIntegral.f90 \ + spline_int.f90 +NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) + +# Hook-up modules (set files and directory paths) +SUMMA_HOOKUP= \ + summaFileManager.f90 +HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) + +# Data modules +SUMMA_DATAMS= \ + multiconst.f90 \ + var_lookup.f90 \ + data_types.f90 \ + globalData.f90 \ + flxMapping.f90 \ + get_ixname.f90 \ + ascii_util.f90 \ + popMetadat.f90 \ + outpt_stat.f90 +DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) + +# utility modules +SUMMA_UTILMS= \ + time_utils.f90 \ + mDecisions.f90 \ + snow_utils.f90 \ + soil_utils.f90 \ + updatState.f90 \ + matrixOper.f90 +UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) + +# Model guts +SUMMA_MODGUT= \ + MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) + +# Solver +SUMMA_SOLVER= \ + vegPhenlgy.f90 \ + diagn_evar.f90 \ + stomResist.f90 \ + groundwatr.f90 \ + vegSWavRad.f90 \ + vegNrgFlux.f90 \ + ssdNrgFlux.f90 \ + vegLiqFlux.f90 \ + snowLiqFlx.f90 \ + soilLiqFlx.f90 \ + bigAquifer.f90 \ + computFlux.f90 \ + computResid.f90 \ + computJacob.f90 \ + eval8summa.f90 \ + summaSolve.f90 \ + systemSolv.f90 \ + varSubstep.f90 \ + opSplittin.f90 \ + coupled_em.f90 \ + run_oneHRU.f90 \ + run_oneGRU.f90 +SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) + +# Define routines for SUMMA preliminaries +SUMMA_PRELIM= \ + conv_funcs.f90 \ + sunGeomtry.f90 \ + convE2Temp.f90 \ + allocspace.f90 \ + checkStruc.f90 \ + childStruc.f90 \ + ffile_info.f90 \ + read_attrb.f90 \ + read_pinit.f90 \ + pOverwrite.f90 \ + read_param.f90 \ + paramCheck.f90 \ + check_icond.f90 +PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) + +SUMMA_NOAHMP= \ + module_model_constants.F \ + module_sf_noahutl.F \ + module_sf_myjsfc.F \ + module_sf_sfclay.F \ + module_sf_noahlsm.F \ + module_sf_noahmplsm.F +NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) + +# Define routines for the SUMMA model runs +SUMMA_MODRUN = \ + indexState.f90 \ + getVectorz.f90 \ + updateVars.f90 \ + var_derive.f90 \ + read_force.f90 \ + derivforce.f90 \ + snowAlbedo.f90 \ + canopySnow.f90 \ + tempAdjust.f90 \ + snwCompact.f90 \ + layerMerge.f90 \ + layerDivide.f90 \ + volicePack.f90 \ + qTimeDelay.f90 +MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) + +# Define routines for the solver +SUMMA_MSOLVE = \ + +# Define NetCDF routines +SUMMA_NETCDF = \ + netcdf_util.f90 \ + def_output.f90 \ + modelwrite.f90 \ + read_icond.f90 +NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) + +# ... stitch together common programs +COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) + +# ... stitch together SUMMA programs +SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) + +# Define the driver routine +SUMMA_DRIVER= \ + summa_type.f90 \ + summa_util.f90 \ + summa_alarms.f90 \ + summa_globalData.f90 \ + summa_defineOutput.f90 \ + summa_init.f90 \ + summa_setup.f90 \ + summa_restart.f90 \ + summa_forcing.f90 \ + summa_modelRun.f90 \ + summa_writeOutput.f90 \ + summa_driver.f90 +DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) + +# Define the executable +DRIVER__EX = summa.exe + +# Define version number +VERSIONFILE = $(DRIVER_DIR)/summaversion.inc +VERSION = $(shell git tag | tail -n 1) +BULTTIM = $(shell date) +GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') +GITHASH = $(shell git rev-parse HEAD) + +#======================================================================== +# PART 3: Checks +#====================================================================== +# make sure that the paths are defined. These are just some high level checks +ifndef F_MASTER + $(error F_MASTER is undefined) +endif +ifndef FC + $(error FC is undefined: Specify your compiler) +endif +ifndef FC_EXE + $(error FC_EXE is undefined: Specify your compiler executable) +endif +ifndef FLAGS_SUMMA + $(error Specify flags for your compiler: $(FC)) +endif +ifndef INCLUDES + $(error INCLUDES is undefined) +endif +ifndef LIBRARIES + $(error LIBRARIES is undefined) +endif + +#======================================================================== +# PART 4: compilation +#====================================================================== + +# Compile +all: compile_noah compile_comm compile_summa link clean install + +check: + $(info) + $(info Displaying make variables:) + $(info F_MASTER : $(F_MASTER)) + $(info EXE_PATH : $(EXE_PATH)) + $(info FC : $(FC)) + $(info INCLUDES : $(INCLUDES)) + $(info LIBRARIES : $(LIBRARIES)) + $(info FLAGS_NOAH : $(FLAGS_NOAH)) + $(info FLAGS_COMM : $(FLAGS_COMM)) + $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) + $(info) + +# update version information +update_version: + echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) + echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) + +# compile Noah-MP routines +compile_noah: + $(FC_EXE) $(FLAGS_NOAH) -c $(NOAHMP) + +# compile common routines +compile_comm: + $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) + +# compile SUMMA routines +compile_summa: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ + $(INCLUDES) + +# link routines +link: + $(FC_EXE) -g *.o $(LIBRARIES) -o $(DRIVER__EX) + +# Remove object files +clean: + rm -f *.o + rm -f *.mod + rm -f soil_veg_gen_parm__genmod.f90 + +# Copy the executable to the bin directory +install: + @mkdir -p $(EXE_PATH) + @mv $(DRIVER__EX) $(EXE_PATH) + $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) From 6565e1157795c1c4795caf1ba846528f99ba38a6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 23 Mar 2020 10:49:29 +0900 Subject: [PATCH 0002/1472] more makefiles --- build/Makefile_cheyenne | 371 +++++++++++++++++++++++++++++ build/{Makefile => Makefile_mac} | 0 build/build_summa | 3 + build/my_makefile | 386 +++++++++++++++++++++++++++++++ 4 files changed, 760 insertions(+) create mode 100755 build/Makefile_cheyenne rename build/{Makefile => Makefile_mac} (100%) create mode 100755 build/build_summa create mode 100755 build/my_makefile diff --git a/build/Makefile_cheyenne b/build/Makefile_cheyenne new file mode 100755 index 000000000..8557d7b8e --- /dev/null +++ b/build/Makefile_cheyenne @@ -0,0 +1,371 @@ +#======================================================================== +# Makefile to compile SUMMA +#======================================================================== +F_MASTER=.. +FC=gfortran +FC_ENV=gfortran-cheyenne +FC_EXE=/glade/u/apps/ch/opt/ncarcompilers/0.5.0/gnu/8.3.0/gfortran + + +# define open MP flags +isOpenMP = +FLAGS_OMP = +LIBOPENMP = + +# Define compiler flags. If you use a different compiler, +# you will need to figure out what the equivalent flags are +# and may need to update this section + +# gfortran +ifeq "$(FC)" "gfortran" + + ifeq "$(isOpenMP)" "yes" + FLAGS_OMP = -fopenmp + endif + + FLAGS_NOAH = -O3 -ffree-form -fdefault-real-8 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) + FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) + FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) + + #FLAGS_NOAH = -p -g -ffree-form -fdefault-real-8 -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument + #FLAGS_COMM = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds + #FLAGS_SUMMA = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds + +endif + + +# ifort +ifeq "$(FC)" "ifort" + + ifeq "$(isOpenMP)" "yes" + FLAGS_OMP = -qopenmp + endif + + FLAGS_NOAH = -O3 -autodouble -noerror_limit -FR -auto -fltconsistency $(FLAGS_OMP) + FLAGS_COMM = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) + FLAGS_SUMMA = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) + + #FLAGS_NOAH = -O0 -p -g -warn nounused -autodouble -noerror_limit -FR -auto -WB -traceback -fltconsistency + #FLAGS_COMM = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 + #FLAGS_SUMMA = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 +endif + +# ------------ define compiler libraries ------------------------------------ + +# Intel fortran compiler +ifeq "$(FC_ENV)" "ifort-cheyenne" + + ifeq "$(isOpenMP)" "yes" + LIBOPENMP = -liomp5 + endif + + INCLUDES = -I$(NCAR_INC_NETCDF) + LIBRARIES = -L$(NCAR_LDFLAGS_NETCDF) -lnetcdff + + # Production runs + FLAGS_NOAH = -O3 -autodouble -noerror_limit -FR -auto -fltconsistency $(FLAGS_OMP) + FLAGS_COMM = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) + FLAGS_SUMMA = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) + + # Debug runs + #FLAGS_NOAH = -O0 -p -g -warn nounused -autodouble -noerror_limit -FR -auto -WB -traceback -fltconsistency + #FLAGS_COMM = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 + #FLAGS_SUMMA = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 +endif + +# gfortran compiler on cheyenne +ifeq "$(FC_ENV)" "gfortran-cheyenne" + ifeq "$(isOpenMP)" "yes" + LIBOPENMP = -lgomp + endif + + INCLUDES = -I$(NCAR_INC_NETCDF) -I$(NCAR_INC_OPENBLAS) + LIBRARIES = -L$(NCAR_LDFLAGS_NETCDF) -lnetcdff -L$(NCAR_LDFLAGS_OPENBLAS) $(NCAR_LIBS_OPENBLAS) + + endif + + # Intel fortran compiler + ifeq "$(FC_ENV)" "ifort-cheyenne" + + ifeq "$(isOpenMP)" "yes" + LIBOPENMP = -liomp5 + endif + + INCLUDES = -I$(NCAR_INC_NETCDF) + LIBRARIES = -L$(NCAR_LDFLAGS_NETCDF) -lnetcdff + + FLAGS_NOAH = -O3 -ffree-form -fdefault-real-8 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) + FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) + FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) + + #FLAGS_NOAH = -p -g -ffree-form -fdefault-real-8 -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument + #FLAGS_COMM = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds + #FLAGS_SUMMA = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds + +endif + + +#======================================================================== +# PART 1: Define directory paths +#======================================================================== + +# Core directory that contains source code +F_KORE_DIR = $(F_MASTER)/build/source + +# Location of the compiled modules +MOD_PATH = $(F_MASTER)/build + +# Define the directory for the executables +EXE_PATH = $(F_MASTER)/bin + +#======================================================================== +# PART 2: Assemble all of the SUMMA sub-routines +#======================================================================== + +# Define directories +DRIVER_DIR = $(F_KORE_DIR)/driver +HOOKUP_DIR = $(F_KORE_DIR)/hookup +NETCDF_DIR = $(F_KORE_DIR)/netcdf +DSHARE_DIR = $(F_KORE_DIR)/dshare +NUMREC_DIR = $(F_KORE_DIR)/numrec +NOAHMP_DIR = $(F_KORE_DIR)/noah-mp +ENGINE_DIR = $(F_KORE_DIR)/engine + +# utilities +SUMMA_NRUTIL= \ + nrtype.f90 \ + f2008funcs.f90 \ + nr_utility.f90 +NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) + +# +# Numerical recipes procedures +# NOTE: all numerical recipes procedures are now replaced with free versions +SUMMA_NRPROC= \ + expIntegral.f90 \ + spline_int.f90 +NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) + +# Hook-up modules (set files and directory paths) +SUMMA_HOOKUP= \ + summaFileManager.f90 +HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) + +# Data modules +SUMMA_DATAMS= \ + multiconst.f90 \ + var_lookup.f90 \ + data_types.f90 \ + globalData.f90 \ + flxMapping.f90 \ + get_ixname.f90 \ + ascii_util.f90 \ + popMetadat.f90 \ + outpt_stat.f90 +DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) + +# utility modules +SUMMA_UTILMS= \ + time_utils.f90 \ + mDecisions.f90 \ + snow_utils.f90 \ + soil_utils.f90 \ + updatState.f90 \ + matrixOper.f90 +UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) + +# Model guts +SUMMA_MODGUT= \ + MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) + +# Solver +SUMMA_SOLVER= \ + vegPhenlgy.f90 \ + diagn_evar.f90 \ + stomResist.f90 \ + groundwatr.f90 \ + vegSWavRad.f90 \ + vegNrgFlux.f90 \ + ssdNrgFlux.f90 \ + vegLiqFlux.f90 \ + snowLiqFlx.f90 \ + soilLiqFlx.f90 \ + bigAquifer.f90 \ + computFlux.f90 \ + computResid.f90 \ + computJacob.f90 \ + eval8summa.f90 \ + summaSolve.f90 \ + systemSolv.f90 \ + varSubstep.f90 \ + opSplittin.f90 \ + coupled_em.f90 \ + run_oneHRU.f90 \ + run_oneGRU.f90 +SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) + +# Define routines for SUMMA preliminaries +SUMMA_PRELIM= \ + conv_funcs.f90 \ + sunGeomtry.f90 \ + convE2Temp.f90 \ + allocspace.f90 \ + checkStruc.f90 \ + childStruc.f90 \ + ffile_info.f90 \ + read_attrb.f90 \ + read_pinit.f90 \ + pOverwrite.f90 \ + read_param.f90 \ + paramCheck.f90 \ + check_icond.f90 +PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) + +SUMMA_NOAHMP= \ + module_model_constants.F \ + module_sf_noahutl.F \ + module_sf_myjsfc.F \ + module_sf_sfclay.F \ + module_sf_noahlsm.F \ + module_sf_noahmplsm.F +NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) + +# Define routines for the SUMMA model runs +SUMMA_MODRUN = \ + indexState.f90 \ + getVectorz.f90 \ + updateVars.f90 \ + var_derive.f90 \ + read_force.f90 \ + derivforce.f90 \ + snowAlbedo.f90 \ + canopySnow.f90 \ + tempAdjust.f90 \ + snwCompact.f90 \ + layerMerge.f90 \ + layerDivide.f90 \ + volicePack.f90 \ + qTimeDelay.f90 +MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) + +# Define routines for the solver +SUMMA_MSOLVE = \ + +# Define NetCDF routines +SUMMA_NETCDF = \ + netcdf_util.f90 \ + def_output.f90 \ + modelwrite.f90 \ + read_icond.f90 +NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) + +# ... stitch together common programs +COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) + +# ... stitch together SUMMA programs +SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) + +# Define the driver routine +SUMMA_DRIVER= \ + summa_type.f90 \ + summa_util.f90 \ + summa_alarms.f90 \ + summa_globalData.f90 \ + summa_defineOutput.f90 \ + summa_init.f90 \ + summa_setup.f90 \ + summa_restart.f90 \ + summa_forcing.f90 \ + summa_modelRun.f90 \ + summa_writeOutput.f90 \ + summa_driver.f90 +DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) + +# Define the executable +DRIVER__EX = summa.exe + +# Define version number +VERSIONFILE = $(DRIVER_DIR)/summaversion.inc +VERSION = $(shell git tag | tail -n 1) +BULTTIM = $(shell date) +GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') +GITHASH = $(shell git rev-parse HEAD) + +#======================================================================== +# PART 3: Checks +#====================================================================== +# make sure that the paths are defined. These are just some high level checks +ifndef F_MASTER + $(error F_MASTER is undefined) +endif +ifndef FC + $(error FC is undefined: Specify your compiler) +endif +ifndef FC_EXE + $(error FC_EXE is undefined: Specify your compiler executable) +endif +ifndef FLAGS_SUMMA + $(error Specify flags for your compiler: $(FC)) +endif +ifndef INCLUDES + $(error INCLUDES is undefined) +endif +ifndef LIBRARIES + $(error LIBRARIES is undefined) +endif + +#======================================================================== +# PART 4: compilation +#====================================================================== + +# Compile +all: compile_noah compile_comm compile_summa link clean install + +check: + $(info) + $(info Displaying make variables:) + $(info F_MASTER : $(F_MASTER)) + $(info EXE_PATH : $(EXE_PATH)) + $(info FC : $(FC)) + $(info INCLUDES : $(INCLUDES)) + $(info LIBRARIES : $(LIBRARIES)) + $(info FLAGS_NOAH : $(FLAGS_NOAH)) + $(info FLAGS_COMM : $(FLAGS_COMM)) + $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) + $(info) + +# update version information +update_version: + echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) + echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) + +# compile Noah-MP routines +compile_noah: + $(FC_EXE) $(FLAGS_NOAH) -c $(NOAHMP) + +# compile common routines +compile_comm: + $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) + +# compile SUMMA routines +compile_summa: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ + $(INCLUDES) + +# link routines +link: + $(FC_EXE) -g *.o $(LIBRARIES) -o $(DRIVER__EX) + +# Remove object files +clean: + rm -f *.o + rm -f *.mod + rm -f soil_veg_gen_parm__genmod.f90 + +# Copy the executable to the bin directory +install: + @mkdir -p $(EXE_PATH) + @mv $(DRIVER__EX) $(EXE_PATH) + $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) diff --git a/build/Makefile b/build/Makefile_mac similarity index 100% rename from build/Makefile rename to build/Makefile_mac diff --git a/build/build_summa b/build/build_summa new file mode 100755 index 000000000..c0b0667a4 --- /dev/null +++ b/build/build_summa @@ -0,0 +1,3 @@ +module load gnu +module load openblas +make -f my_makefile diff --git a/build/my_makefile b/build/my_makefile new file mode 100755 index 000000000..2ffaa097a --- /dev/null +++ b/build/my_makefile @@ -0,0 +1,386 @@ +#======================================================================== +# Makefile to compile SUMMA +#======================================================================== +# +# Recommended use: Copy this file to Makefile.local, edit it to your +# heart's content, and then run `make -f build/Makefile.local` from +# your top level SUMMA directory. Don't include the Makefile.local in +# any pull requests you make. +# +# Note that Makefile configurations that we commonly use can be found on +# the SUMMA wiki at: +# https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration +# feel free to add yours to that page. +# +# To troubleshoot your paths and setup, type 'make check' +# +# At a minimum you will need to set the following: +# * F_MASTER - top level summa directory +# * FC - compiler suite +# * FC_EXE - compiler executable +# * INCLUDES - path to include files +# * LIBRARIES - path to and libraries to include +# +# Some further options can be specified for OpenMP, etc. See in Part 0 of +# the Makefile. You don't need to make any changes in PART 1 and +# following unless you are doing SUMMA development and changed what +# needs to be compiled + +#======================================================================== +# PART 0: User-configurable part +#======================================================================== + +# The variables can be specified in one of two ways: +# * delete the '##' in front of the variable, fill out the entry, +# save the file and run make +# * make no changes to this file, but specify the variables in your +# environment before you run make + +# Define core directory below which everything resides. This is the +# parent directory of the 'build' directory +F_MASTER =/Users/amedin/Research/summa + +# Define the Fortran Compiler. If you are using gfortran, then this needs +# to be version 6 or higher. This variable is simply used to select the right +# compiler flags in the ifeq statements in this Makefile. The compiler +# executable is set separately as FC_EXE +# Currently this is either gfortran or ifort +FC = gfortran + +# Define the path for the compiler executable. This is the actual executable +# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin-gfortran-mp-6 +# FC and FC_EXE have to be consistent +FC_EXE =/opt/local/bin/gfortran + +# Define the NetCDF and LAPACK libraries and path to include files. +# INCLUDES needs to be of the form (no quotes around the string): +# INCLUDES = -I -I -I<...> -I +# LIBRARIES needs to be of the form ( no quotes around the string): +# LIBRARIES = '-L -lnetcdff -L -lblas -L -l' +# If none of this makes sense, please talk to your system +# administrator. +INCLUDES = -I/opt/local/include +LIBRARIES = -L/opt/local/lib -lnetcdff -llapack -lblas -latlas + +# Eventually we plan move to a real configure script, but for now we like +# to keep track of successful compilations of SUMMA on different platforms +# and with different compilers. If you are successful compiling SUMMA, +# please add your configuration (operating system and compiler plus +# part 0 of the Makefile) to the SUMMA wiki on github. + +# Define compiler flags. If you use a different compiler, +# you will need to figure out what the equivalent flags are +# and may need to update this section + +# ------------ define compiler flags ---------------------------------------- + +# define open MP flags +isOpenMP = +FLAGS_OMP = +LIBOPENMP = + +# Define compiler flags. If you use a different compiler, +# you will need to figure out what the equivalent flags are +# and may need to update this section + +# gfortran compiler flags +ifeq "$(FC)" "gfortran" + + ifeq "$(isOpenMP)" "yes" + FLAGS_OMP = -fopenmp + endif + +# Production runs +FLAGS_NOAH = -O3 -ffree-form -fdefault-real-8 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) + +# Debug runs +#FLAGS_NOAH = -p -g -ffree-form -fdefault-real-8 -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument +#FLAGS_COMM = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds +#FLAGS_SUMMA = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds + +endif + +# ifort compiler flags +ifeq "$(FC)" "ifort" + + ifeq "$(isOpenMP)" "yes" + FLAGS_OMP = -qopenmp + endif + +# Production runs +FLAGS_NOAH = -O3 -autodouble -noerror_limit -FR -auto -fltconsistency $(FLAGS_OMP) +FLAGS_COMM = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) +FLAGS_SUMMA = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) + +# Debug runs +#FLAGS_NOAH = -O0 -p -g -warn nounused -autodouble -noerror_limit -FR -auto -WB -traceback -fltconsistency +#FLAGS_COMM = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 +#FLAGS_SUMMA = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 +endif + +#======================================================================== +# PART 1: Define directory paths +#======================================================================== + +# Core directory that contains source code +F_KORE_DIR = $(F_MASTER)/build/source + +# Location of the compiled modules +MOD_PATH = $(F_MASTER)/build + +# Define the directory for the executables +EXE_PATH = $(F_MASTER)/bin + +#======================================================================== +# PART 2: Assemble all of the SUMMA sub-routines +#======================================================================== + +# Define directories +DRIVER_DIR = $(F_KORE_DIR)/driver +HOOKUP_DIR = $(F_KORE_DIR)/hookup +NETCDF_DIR = $(F_KORE_DIR)/netcdf +DSHARE_DIR = $(F_KORE_DIR)/dshare +NUMREC_DIR = $(F_KORE_DIR)/numrec +NOAHMP_DIR = $(F_KORE_DIR)/noah-mp +ENGINE_DIR = $(F_KORE_DIR)/engine + +# utilities +SUMMA_NRUTIL= \ + nrtype.f90 \ + f2008funcs.f90 \ + nr_utility.f90 +NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) + +# +# Numerical recipes procedures +# NOTE: all numerical recipes procedures are now replaced with free versions +SUMMA_NRPROC= \ + expIntegral.f90 \ + spline_int.f90 +NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) + +# Hook-up modules (set files and directory paths) +SUMMA_HOOKUP= \ + summaFileManager.f90 +HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) + +# Data modules +SUMMA_DATAMS= \ + multiconst.f90 \ + var_lookup.f90 \ + data_types.f90 \ + globalData.f90 \ + flxMapping.f90 \ + get_ixname.f90 \ + ascii_util.f90 \ + popMetadat.f90 \ + outpt_stat.f90 +DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) + +# utility modules +SUMMA_UTILMS= \ + time_utils.f90 \ + mDecisions.f90 \ + snow_utils.f90 \ + soil_utils.f90 \ + updatState.f90 \ + matrixOper.f90 +UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) + +# Model guts +SUMMA_MODGUT= \ + MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) + +# Solver +SUMMA_SOLVER= \ + vegPhenlgy.f90 \ + diagn_evar.f90 \ + stomResist.f90 \ + groundwatr.f90 \ + vegSWavRad.f90 \ + vegNrgFlux.f90 \ + ssdNrgFlux.f90 \ + vegLiqFlux.f90 \ + snowLiqFlx.f90 \ + soilLiqFlx.f90 \ + bigAquifer.f90 \ + computFlux.f90 \ + computResid.f90 \ + computJacob.f90 \ + eval8summa.f90 \ + summaSolve.f90 \ + systemSolv.f90 \ + varSubstep.f90 \ + opSplittin.f90 \ + coupled_em.f90 \ + run_oneHRU.f90 \ + run_oneGRU.f90 +SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) + +# Define routines for SUMMA preliminaries +SUMMA_PRELIM= \ + conv_funcs.f90 \ + sunGeomtry.f90 \ + convE2Temp.f90 \ + allocspace.f90 \ + checkStruc.f90 \ + childStruc.f90 \ + ffile_info.f90 \ + read_attrb.f90 \ + read_pinit.f90 \ + pOverwrite.f90 \ + read_param.f90 \ + paramCheck.f90 \ + check_icond.f90 +PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) + +SUMMA_NOAHMP= \ + module_model_constants.F \ + module_sf_noahutl.F \ + module_sf_myjsfc.F \ + module_sf_sfclay.F \ + module_sf_noahlsm.F \ + module_sf_noahmplsm.F +NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) + +# Define routines for the SUMMA model runs +SUMMA_MODRUN = \ + indexState.f90 \ + getVectorz.f90 \ + updateVars.f90 \ + var_derive.f90 \ + read_force.f90 \ + derivforce.f90 \ + snowAlbedo.f90 \ + canopySnow.f90 \ + tempAdjust.f90 \ + snwCompact.f90 \ + layerMerge.f90 \ + layerDivide.f90 \ + volicePack.f90 \ + qTimeDelay.f90 +MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) + +# Define routines for the solver +SUMMA_MSOLVE = \ + +# Define NetCDF routines +SUMMA_NETCDF = \ + netcdf_util.f90 \ + def_output.f90 \ + modelwrite.f90 \ + read_icond.f90 +NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) + +# ... stitch together common programs +COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) + +# ... stitch together SUMMA programs +SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) + +# Define the driver routine +SUMMA_DRIVER= \ + summa_type.f90 \ + summa_util.f90 \ + summa_alarms.f90 \ + summa_globalData.f90 \ + summa_defineOutput.f90 \ + summa_init.f90 \ + summa_setup.f90 \ + summa_restart.f90 \ + summa_forcing.f90 \ + summa_modelRun.f90 \ + summa_writeOutput.f90 \ + summa_driver.f90 +DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) + +# Define the executable +DRIVER__EX = summa.exe + +# Define version number +VERSIONFILE = $(DRIVER_DIR)/summaversion.inc +VERSION = $(shell git tag | tail -n 1) +BULTTIM = $(shell date) +GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') +GITHASH = $(shell git rev-parse HEAD) + +#======================================================================== +# PART 3: Checks +#====================================================================== +# make sure that the paths are defined. These are just some high level checks +ifndef F_MASTER + $(error F_MASTER is undefined) +endif +ifndef FC + $(error FC is undefined: Specify your compiler) +endif +ifndef FC_EXE + $(error FC_EXE is undefined: Specify your compiler executable) +endif +ifndef FLAGS_SUMMA + $(error Specify flags for your compiler: $(FC)) +endif +ifndef INCLUDES + $(error INCLUDES is undefined) +endif +ifndef LIBRARIES + $(error LIBRARIES is undefined) +endif + +#======================================================================== +# PART 4: compilation +#====================================================================== + +# Compile +all: compile_noah compile_comm compile_summa link clean install + +check: + $(info) + $(info Displaying make variables:) + $(info F_MASTER : $(F_MASTER)) + $(info EXE_PATH : $(EXE_PATH)) + $(info FC : $(FC)) + $(info INCLUDES : $(INCLUDES)) + $(info LIBRARIES : $(LIBRARIES)) + $(info FLAGS_NOAH : $(FLAGS_NOAH)) + $(info FLAGS_COMM : $(FLAGS_COMM)) + $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) + $(info) + +# update version information +update_version: + echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) + echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) + +# compile Noah-MP routines +compile_noah: + $(FC_EXE) $(FLAGS_NOAH) -c $(NOAHMP) + +# compile common routines +compile_comm: + $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) + +# compile SUMMA routines +compile_summa: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ + $(INCLUDES) + +# link routines +link: + $(FC_EXE) -g *.o $(LIBRARIES) -o $(DRIVER__EX) + +# Remove object files +clean: + rm -f *.o + rm -f *.mod + rm -f soil_veg_gen_parm__genmod.f90 + +# Copy the executable to the bin directory +install: + @mkdir -p $(EXE_PATH) + @mv $(DRIVER__EX) $(EXE_PATH) + $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) From 5860b1008377a3fa7ed321d78fe8ffbdf812245a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 2 Jul 2020 16:59:59 +0900 Subject: [PATCH 0003/1472] Fixing error in precision in sunGeomtry --- build/source/engine/sunGeomtry.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/sunGeomtry.f90 b/build/source/engine/sunGeomtry.f90 index daf8bc87c..bc668c9e1 100755 --- a/build/source/engine/sunGeomtry.f90 +++ b/build/source/engine/sunGeomtry.f90 @@ -99,7 +99,7 @@ SUBROUTINE CLRSKY_RAD(MONTH,DAY,HOUR,DT,SLOPE,AZI,LAT,HRI,COSZEN) ! In such cases AUX > 1 or AUX < -1. Fix AUX at (-)1 in those cases, to fix sunrise at 00.00 or 24.00 of the current day (instead of some time before/after the current day) AUX=-TAN(LAT1)*TAN(D) IF(abs(AUX) > 1.) THEN - TD=ACOS(SIGN(1., AUX)) + TD=ACOS(SIGN(1.D0., AUX)) ELSE TD=ACOS(AUX) END IF @@ -140,7 +140,7 @@ SUBROUTINE CLRSKY_RAD(MONTH,DAY,HOUR,DT,SLOPE,AZI,LAT,HRI,COSZEN) ! In such cases AUX > 1 or AUX < -1. Fix AUX at (-)1 in those cases AUX=-TAN(LAT1)*TAN(D) IF(abs(AUX) > 1.) THEN - TD=ACOS(SIGN(1., AUX)) + TD=ACOS(SIGN(1.D0, AUX)) ELSE TD=ACOS(AUX) END IF From 55a476c3187774681557540f1b778711c9a3c2a4 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 2 Jul 2020 17:04:40 +0900 Subject: [PATCH 0004/1472] fixed bug in my last fix --- build/source/engine/sunGeomtry.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/sunGeomtry.f90 b/build/source/engine/sunGeomtry.f90 index bc668c9e1..f8a5da878 100755 --- a/build/source/engine/sunGeomtry.f90 +++ b/build/source/engine/sunGeomtry.f90 @@ -99,7 +99,7 @@ SUBROUTINE CLRSKY_RAD(MONTH,DAY,HOUR,DT,SLOPE,AZI,LAT,HRI,COSZEN) ! In such cases AUX > 1 or AUX < -1. Fix AUX at (-)1 in those cases, to fix sunrise at 00.00 or 24.00 of the current day (instead of some time before/after the current day) AUX=-TAN(LAT1)*TAN(D) IF(abs(AUX) > 1.) THEN - TD=ACOS(SIGN(1.D0., AUX)) + TD=ACOS(SIGN(1.D0, AUX)) ELSE TD=ACOS(AUX) END IF From ed2688431e1068d43c7317f8984e4a4bec7f0bd0 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 3 Aug 2020 16:04:37 +0900 Subject: [PATCH 0005/1472] put makefile back in --- build/{Makefile_org => Makefile} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename build/{Makefile_org => Makefile} (100%) diff --git a/build/Makefile_org b/build/Makefile similarity index 100% rename from build/Makefile_org rename to build/Makefile From 58a5d36abb3351279e68413b0fd3402e3309f97d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 3 Aug 2020 16:11:14 +0900 Subject: [PATCH 0006/1472] Merge screwed up sunGeometry.f90 --- build/source/engine/sunGeomtry.f90 | 8 -------- 1 file changed, 8 deletions(-) diff --git a/build/source/engine/sunGeomtry.f90 b/build/source/engine/sunGeomtry.f90 index 8a159f019..28703ad40 100755 --- a/build/source/engine/sunGeomtry.f90 +++ b/build/source/engine/sunGeomtry.f90 @@ -99,11 +99,7 @@ SUBROUTINE CLRSKY_RAD(MONTH,DAY,HOUR,DT,SLOPE,AZI,LAT,HRI,COSZEN) ! In such cases AUX > 1 or AUX < -1. Fix AUX at (-)1 in those cases, to fix sunrise at 00.00 or 24.00 of the current day (instead of some time before/after the current day) AUX=-TAN(LAT1)*TAN(D) IF(abs(AUX) > 1.) THEN -<<<<<<< HEAD - TD=ACOS(SIGN(1.D0, AUX)) -======= TD=ACOS(SIGN(1._dp, AUX)) ->>>>>>> 1b6a476f1b6c776b635c6e027e8b8b5bb268588d ELSE TD=ACOS(AUX) END IF @@ -144,11 +140,7 @@ SUBROUTINE CLRSKY_RAD(MONTH,DAY,HOUR,DT,SLOPE,AZI,LAT,HRI,COSZEN) ! In such cases AUX > 1 or AUX < -1. Fix AUX at (-)1 in those cases AUX=-TAN(LAT1)*TAN(D) IF(abs(AUX) > 1.) THEN -<<<<<<< HEAD - TD=ACOS(SIGN(1.D0, AUX)) -======= TD=ACOS(SIGN(1._dp, AUX)) ->>>>>>> 1b6a476f1b6c776b635c6e027e8b8b5bb268588d ELSE TD=ACOS(AUX) END IF From 54c4ad625fff4fe19d7b88442588307f42b981b2 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 30 Sep 2020 14:20:41 +0900 Subject: [PATCH 0007/1472] Fixing convert script --- utils/convert_summa_config_v2_v3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/convert_summa_config_v2_v3.py b/utils/convert_summa_config_v2_v3.py index 032b68350..a1862d17c 100755 --- a/utils/convert_summa_config_v2_v3.py +++ b/utils/convert_summa_config_v2_v3.py @@ -87,7 +87,7 @@ def fm_v2_parse(ifile): fm_values = [] fm_comments = [] for line in iter(fm_txt.splitlines()): - if line.startswith('!'): + if line.startswith('!') or not len(line.strip()): continue m = re.match('^([^\\{}]*)\\{}(.*)$'.format(comment_sep, comment_sep), line) if m and m.group(1): # The line contains a hash / comment From 1dea6f312e59994a34c072b705ddc9f861859bce Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 28 Jan 2021 12:41:40 -0600 Subject: [PATCH 0008/1472] ida files in engine --- build/Makefile | 57 +- build/s_Makefile | 388 ++++++ build/source/engine/computEnthalpy.f90 | 140 +++ build/source/engine/computHeatCap.f90 | 301 +++++ build/source/engine/computJacobFida.f90 | 862 ++++++++++++++ build/source/engine/computJacobFidaCpVar.f90 | 639 ++++++++++ build/source/engine/computResidFida.f90 | 257 ++++ build/source/engine/computThermConduct.f90 | 317 +++++ build/source/engine/convTestFida.f90 | 236 ++++ build/source/engine/diagn_evar.f90 | 2 +- build/source/engine/eval8FidaJac.f90 | 413 +++++++ build/source/engine/eval8summaFida.f90 | 729 +++++++++++ build/source/engine/evalEqnsFida.f90 | 165 +++ build/source/engine/evalJacFida.f90 | 132 ++ build/source/engine/fidaSolver.f90 | 840 +++++++++++++ build/source/engine/fida_datatypes.f90 | 84 ++ build/source/engine/findDiscontinuity.f90 | 99 ++ build/source/engine/opSplittin.f90 | 111 +- build/source/engine/soilCmpresFida.f90 | 135 +++ build/source/engine/soil_utils_fida.f90 | 234 ++++ build/source/engine/sysSolvFida.f90 | 601 ++++++++++ build/source/engine/t2enthalpy.f90 | 522 ++++++++ build/source/engine/tolFida.f90 | 291 +++++ build/source/engine/updatStateFida.f90 | 330 +++++ build/source/engine/updateVarsFida.f90 | 655 ++++++++++ build/source/engine/updateVarsFida2.f90 | 800 +++++++++++++ build/source/engine/varExtrFida.f90 | 514 ++++++++ build/source/engine/varSubstepFida.f90 | 1127 ++++++++++++++++++ 28 files changed, 10933 insertions(+), 48 deletions(-) create mode 100644 build/s_Makefile create mode 100644 build/source/engine/computEnthalpy.f90 create mode 100644 build/source/engine/computHeatCap.f90 create mode 100644 build/source/engine/computJacobFida.f90 create mode 100644 build/source/engine/computJacobFidaCpVar.f90 create mode 100644 build/source/engine/computResidFida.f90 create mode 100644 build/source/engine/computThermConduct.f90 create mode 100644 build/source/engine/convTestFida.f90 create mode 100644 build/source/engine/eval8FidaJac.f90 create mode 100644 build/source/engine/eval8summaFida.f90 create mode 100644 build/source/engine/evalEqnsFida.f90 create mode 100644 build/source/engine/evalJacFida.f90 create mode 100644 build/source/engine/fidaSolver.f90 create mode 100644 build/source/engine/fida_datatypes.f90 create mode 100644 build/source/engine/findDiscontinuity.f90 create mode 100644 build/source/engine/soilCmpresFida.f90 create mode 100644 build/source/engine/soil_utils_fida.f90 create mode 100644 build/source/engine/sysSolvFida.f90 create mode 100644 build/source/engine/t2enthalpy.f90 create mode 100644 build/source/engine/tolFida.f90 create mode 100644 build/source/engine/updatStateFida.f90 create mode 100644 build/source/engine/updateVarsFida.f90 create mode 100644 build/source/engine/updateVarsFida2.f90 create mode 100644 build/source/engine/varExtrFida.f90 create mode 100644 build/source/engine/varSubstepFida.f90 diff --git a/build/Makefile b/build/Makefile index 77dc52b2a..c4c6dd144 100644 --- a/build/Makefile +++ b/build/Makefile @@ -2,13 +2,13 @@ # Makefile to compile SUMMA #======================================================================== # -# Recommended use: Copy this file to Makefile.local, edit it to your +# Recommended use: Copy this file to Makefile.local, edit it to your # heart's content, and then run `make -f build/Makefile.local` from # your top level SUMMA directory. Don't include the Makefile.local in # any pull requests you make. # # Note that Makefile configurations that we commonly use can be found on -# the SUMMA wiki at: +# the SUMMA wiki at: # https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration # feel free to add yours to that page. # @@ -20,10 +20,10 @@ # * FC_EXE - compiler executable # * INCLUDES - path to include files # * LIBRARIES - path to and libraries to include -# +# # Some further options can be specified for OpenMP, etc. See in Part 0 of -# the Makefile. You don't need to make any changes in PART 1 and -# following unless you are doing SUMMA development and changed what +# the Makefile. You don't need to make any changes in PART 1 and +# following unless you are doing SUMMA development and changed what # needs to be compiled #======================================================================== @@ -31,14 +31,14 @@ #======================================================================== # The variables can be specified in one of two ways: -# * delete the '##' in front of the variable, fill out the entry, +# * delete the '##' in front of the variable, fill out the entry, # save the file and run make # * make no changes to this file, but specify the variables in your # environment before you run make # Define core directory below which everything resides. This is the # parent directory of the 'build' directory -F_MASTER = /home/stiff/summa +F_MASTER = /home/stiff/summa-develop # Define the Fortran Compiler. If you are using gfortran, then this needs # to be version 6 or higher. This variable is simply used to select the right @@ -52,7 +52,7 @@ FC = gfortran # FC and FC_EXE have to be consistent FC_EXE = /usr/bin/gfortran-7 -# Define the NetCDF and LAPACK libraries and path to include files. +# Define the NetCDF and LAPACK libraries and path to include files. # INCLUDES needs to be of the form (no quotes around the string): # INCLUDES = -I -I -I<...> -I # LIBRARIES needs to be of the form ( no quotes around the string): @@ -64,7 +64,7 @@ INCLUDES = -I/usr/include -I/usr/share/doc/liblapack3 -I/usr/lib/x86_64-linux-gn LDFLAGS += -L/usr/local/lib LIBRARIES = -llapack -lgfortran -lnetcdff -lnetcdf -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod \ -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod - + # Eventually we plan move to a real configure script, but for now we like # to keep track of successful compilations of SUMMA on different platforms # and with different compilers. If you are successful compiling SUMMA, @@ -148,6 +148,8 @@ DSHARE_DIR = $(F_KORE_DIR)/dshare NUMREC_DIR = $(F_KORE_DIR)/numrec NOAHMP_DIR = $(F_KORE_DIR)/noah-mp ENGINE_DIR = $(F_KORE_DIR)/engine +# reza +USEFIDA_DIR = $(F_KORE_DIR)/engine/use_fida # utilities SUMMA_NRUTIL= \ @@ -166,7 +168,6 @@ NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) # Hook-up modules (set files and directory paths) SUMMA_HOOKUP= \ - ascii_util.f90 \ summaFileManager.f90 HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) @@ -178,6 +179,7 @@ SUMMA_DATAMS= \ globalData.f90 \ flxMapping.f90 \ get_ixname.f90 \ + ascii_util.f90 \ popMetadat.f90 \ outpt_stat.f90 DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) @@ -188,7 +190,9 @@ SUMMA_UTILMS= \ mDecisions.f90 \ snow_utils.f90 \ soil_utils.f90 \ + soil_utils_fida.f90 \ updatState.f90 \ + updatStateFida.f90 \ matrixOper.f90 UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) @@ -209,13 +213,33 @@ SUMMA_SOLVER= \ snowLiqFlx.f90 \ soilLiqFlx.f90 \ bigAquifer.f90 \ + soilCmpresFida.f90 \ computFlux.f90 \ computResid.f90 \ computJacob.f90 \ eval8summa.f90 \ summaSolve.f90 \ systemSolv.f90 \ - varSubstep.f90 \ + fida_datatypes.f90 \ + tolFida.f90 \ + computEnthalpy.f90 \ + computHeatCap.f90 \ + computThermConduct.f90 \ + printResidFida.f90 \ + computResidFida.f90 \ + eval8summaFida.f90 \ + evalEqnsFida.f90 \ + computJacobFida.f90 \ + computJacobFidaCpVar.f90 \ + eval8FidaJac.f90 \ + evalJacFida.f90 \ + nonlinSolvFida.f90 \ + convTestFida.f90 \ + findDiscontinuity.f90 \ + fidaSolver.f90 \ + sysSolvFida.f90 \ + varSubstep.f90 \ + varSubstepFida.f90 \ opSplittin.f90 \ coupled_em.f90 \ run_oneHRU.f90 \ @@ -242,16 +266,21 @@ PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) SUMMA_NOAHMP= \ module_model_constants.F \ module_sf_noahutl.F \ + module_sf_myjsfc.F \ + module_sf_sfclay.F \ module_sf_noahlsm.F \ module_sf_noahmplsm.F - NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) # Define routines for the SUMMA model runs SUMMA_MODRUN = \ indexState.f90 \ getVectorz.f90 \ + varExtrFida.f90 \ + t2enthalpy.f90 \ updateVars.f90 \ + updateVarsFida.f90 \ + updateVarsFida2.f90 \ var_derive.f90 \ read_force.f90 \ derivforce.f90 \ @@ -337,6 +366,7 @@ endif # Compile all: compile_noah compile_comm compile_summa link clean install +part: compile_noah compile_comm compile_summa check: $(info) @@ -363,8 +393,9 @@ compile_noah: $(FC_EXE) $(FLAGS_NOAH) -c $(NOAHMP) # compile common routines + compile_comm: - $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) + $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) # compile SUMMA routines compile_summa: update_version diff --git a/build/s_Makefile b/build/s_Makefile new file mode 100644 index 000000000..77dc52b2a --- /dev/null +++ b/build/s_Makefile @@ -0,0 +1,388 @@ +#======================================================================== +# Makefile to compile SUMMA +#======================================================================== +# +# Recommended use: Copy this file to Makefile.local, edit it to your +# heart's content, and then run `make -f build/Makefile.local` from +# your top level SUMMA directory. Don't include the Makefile.local in +# any pull requests you make. +# +# Note that Makefile configurations that we commonly use can be found on +# the SUMMA wiki at: +# https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration +# feel free to add yours to that page. +# +# To troubleshoot your paths and setup, type 'make check' +# +# At a minimum you will need to set the following: +# * F_MASTER - top level summa directory +# * FC - compiler suite +# * FC_EXE - compiler executable +# * INCLUDES - path to include files +# * LIBRARIES - path to and libraries to include +# +# Some further options can be specified for OpenMP, etc. See in Part 0 of +# the Makefile. You don't need to make any changes in PART 1 and +# following unless you are doing SUMMA development and changed what +# needs to be compiled + +#======================================================================== +# PART 0: User-configurable part +#======================================================================== + +# The variables can be specified in one of two ways: +# * delete the '##' in front of the variable, fill out the entry, +# save the file and run make +# * make no changes to this file, but specify the variables in your +# environment before you run make + +# Define core directory below which everything resides. This is the +# parent directory of the 'build' directory +F_MASTER = /home/stiff/summa + +# Define the Fortran Compiler. If you are using gfortran, then this needs +# to be version 6 or higher. This variable is simply used to select the right +# compiler flags in the ifeq statements in this Makefile. The compiler +# executable is set separately as FC_EXE +# Currently this is either gfortran or ifort +FC = gfortran + +# Define the path for the compiler executable. This is the actual executable +# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin-gfortran-mp-6 +# FC and FC_EXE have to be consistent +FC_EXE = /usr/bin/gfortran-7 + +# Define the NetCDF and LAPACK libraries and path to include files. +# INCLUDES needs to be of the form (no quotes around the string): +# INCLUDES = -I -I -I<...> -I +# LIBRARIES needs to be of the form ( no quotes around the string): +# LIBRARIES = '-L -lnetcdff -L -lblas -L -l' +# If none of this makes sense, please talk to your system +# administrator. +INCLUDES = -I/usr/include -I/usr/share/doc/liblapack3 -I/usr/lib/x86_64-linux-gnu -I/usr/local/include \ + -I/usr/local/lib -I/usr/local/fortran +LDFLAGS += -L/usr/local/lib +LIBRARIES = -llapack -lgfortran -lnetcdff -lnetcdf -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod \ + -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod + +# Eventually we plan move to a real configure script, but for now we like +# to keep track of successful compilations of SUMMA on different platforms +# and with different compilers. If you are successful compiling SUMMA, +# please add your configuration (operating system and compiler plus +# part 0 of the Makefile) to the SUMMA wiki on github. + +# Define compiler flags. If you use a different compiler, +# you will need to figure out what the equivalent flags are +# and may need to update this section + +# ------------ define compiler flags ---------------------------------------- + +# define open MP flags +isOpenMP = +FLAGS_OMP = +LIBOPENMP = + +# Define compiler flags. If you use a different compiler, +# you will need to figure out what the equivalent flags are +# and may need to update this section + +# gfortran compiler flags +ifeq "$(FC)" "gfortran" + + ifeq "$(isOpenMP)" "yes" + FLAGS_OMP = -fopenmp + endif + +# Production runs +FLAGS_NOAH = -O3 -ffree-form -fdefault-real-8 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) + +# Debug runs +#FLAGS_NOAH = -p -g -ffree-form -fdefault-real-8 -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument +#FLAGS_COMM = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds +#FLAGS_SUMMA = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds + +endif + +# ifort compiler flags +ifeq "$(FC)" "ifort" + + ifeq "$(isOpenMP)" "yes" + FLAGS_OMP = -qopenmp + endif + +# Production runs +FLAGS_NOAH = -O3 -autodouble -noerror_limit -FR -auto -fltconsistency $(FLAGS_OMP) +FLAGS_COMM = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) +FLAGS_SUMMA = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) + +# Debug runs +#FLAGS_NOAH = -O0 -p -g -warn nounused -autodouble -noerror_limit -FR -auto -WB -traceback -fltconsistency +#FLAGS_COMM = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 +#FLAGS_SUMMA = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 +endif + +#======================================================================== +# PART 1: Define directory paths +#======================================================================== + +# Core directory that contains source code +F_KORE_DIR = $(F_MASTER)/build/source + +# Location of the compiled modules +MOD_PATH = $(F_MASTER)/build + +# Define the directory for the executables +EXE_PATH = $(F_MASTER)/bin + +#======================================================================== +# PART 2: Assemble all of the SUMMA sub-routines +#======================================================================== + +# Define directories +DRIVER_DIR = $(F_KORE_DIR)/driver +HOOKUP_DIR = $(F_KORE_DIR)/hookup +NETCDF_DIR = $(F_KORE_DIR)/netcdf +DSHARE_DIR = $(F_KORE_DIR)/dshare +NUMREC_DIR = $(F_KORE_DIR)/numrec +NOAHMP_DIR = $(F_KORE_DIR)/noah-mp +ENGINE_DIR = $(F_KORE_DIR)/engine + +# utilities +SUMMA_NRUTIL= \ + nrtype.f90 \ + f2008funcs.f90 \ + nr_utility.f90 +NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) + +# +# Numerical recipes procedures +# NOTE: all numerical recipes procedures are now replaced with free versions +SUMMA_NRPROC= \ + expIntegral.f90 \ + spline_int.f90 +NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) + +# Hook-up modules (set files and directory paths) +SUMMA_HOOKUP= \ + ascii_util.f90 \ + summaFileManager.f90 +HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) + +# Data modules +SUMMA_DATAMS= \ + multiconst.f90 \ + var_lookup.f90 \ + data_types.f90 \ + globalData.f90 \ + flxMapping.f90 \ + get_ixname.f90 \ + popMetadat.f90 \ + outpt_stat.f90 +DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) + +# utility modules +SUMMA_UTILMS= \ + time_utils.f90 \ + mDecisions.f90 \ + snow_utils.f90 \ + soil_utils.f90 \ + updatState.f90 \ + matrixOper.f90 +UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) + +# Model guts +SUMMA_MODGUT= \ + MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) + +# Solver +SUMMA_SOLVER= \ + vegPhenlgy.f90 \ + diagn_evar.f90 \ + stomResist.f90 \ + groundwatr.f90 \ + vegSWavRad.f90 \ + vegNrgFlux.f90 \ + ssdNrgFlux.f90 \ + vegLiqFlux.f90 \ + snowLiqFlx.f90 \ + soilLiqFlx.f90 \ + bigAquifer.f90 \ + computFlux.f90 \ + computResid.f90 \ + computJacob.f90 \ + eval8summa.f90 \ + summaSolve.f90 \ + systemSolv.f90 \ + varSubstep.f90 \ + opSplittin.f90 \ + coupled_em.f90 \ + run_oneHRU.f90 \ + run_oneGRU.f90 +SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) + +# Define routines for SUMMA preliminaries +SUMMA_PRELIM= \ + conv_funcs.f90 \ + sunGeomtry.f90 \ + convE2Temp.f90 \ + allocspace.f90 \ + checkStruc.f90 \ + childStruc.f90 \ + ffile_info.f90 \ + read_attrb.f90 \ + read_pinit.f90 \ + pOverwrite.f90 \ + read_param.f90 \ + paramCheck.f90 \ + check_icond.f90 +PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) + +SUMMA_NOAHMP= \ + module_model_constants.F \ + module_sf_noahutl.F \ + module_sf_noahlsm.F \ + module_sf_noahmplsm.F + +NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) + +# Define routines for the SUMMA model runs +SUMMA_MODRUN = \ + indexState.f90 \ + getVectorz.f90 \ + updateVars.f90 \ + var_derive.f90 \ + read_force.f90 \ + derivforce.f90 \ + snowAlbedo.f90 \ + canopySnow.f90 \ + tempAdjust.f90 \ + snwCompact.f90 \ + layerMerge.f90 \ + layerDivide.f90 \ + volicePack.f90 \ + qTimeDelay.f90 +MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) + +# Define routines for the solver +SUMMA_MSOLVE = \ + +# Define NetCDF routines +SUMMA_NETCDF = \ + netcdf_util.f90 \ + def_output.f90 \ + modelwrite.f90 \ + read_icond.f90 +NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) + +# ... stitch together common programs +COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) + +# ... stitch together SUMMA programs +SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) + +# Define the driver routine +SUMMA_DRIVER= \ + summa_type.f90 \ + summa_util.f90 \ + summa_alarms.f90 \ + summa_globalData.f90 \ + summa_defineOutput.f90 \ + summa_init.f90 \ + summa_setup.f90 \ + summa_restart.f90 \ + summa_forcing.f90 \ + summa_modelRun.f90 \ + summa_writeOutput.f90 \ + summa_driver.f90 +DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) + +# Define the executable +DRIVER__EX = summa.exe + +# Define version number +VERSIONFILE = $(DRIVER_DIR)/summaversion.inc +VERSION = $(shell git tag | tail -n 1) +BULTTIM = $(shell date) +GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') +GITHASH = $(shell git rev-parse HEAD) + +#======================================================================== +# PART 3: Checks +#====================================================================== +# make sure that the paths are defined. These are just some high level checks +ifndef F_MASTER + $(error F_MASTER is undefined) +endif +ifndef FC + $(error FC is undefined: Specify your compiler) +endif +ifndef FC_EXE + $(error FC_EXE is undefined: Specify your compiler executable) +endif +ifndef FLAGS_SUMMA + $(error Specify flags for your compiler: $(FC)) +endif +ifndef INCLUDES + $(error INCLUDES is undefined) +endif +ifndef LIBRARIES + $(error LIBRARIES is undefined) +endif + +#======================================================================== +# PART 4: compilation +#====================================================================== + +# Compile +all: compile_noah compile_comm compile_summa link clean install + +check: + $(info) + $(info Displaying make variables:) + $(info F_MASTER : $(F_MASTER)) + $(info EXE_PATH : $(EXE_PATH)) + $(info FC : $(FC)) + $(info INCLUDES : $(INCLUDES)) + $(info LIBRARIES : $(LIBRARIES)) + $(info FLAGS_NOAH : $(FLAGS_NOAH)) + $(info FLAGS_COMM : $(FLAGS_COMM)) + $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) + $(info) + +# update version information +update_version: + echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) + echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) + +# compile Noah-MP routines +compile_noah: + $(FC_EXE) $(FLAGS_NOAH) -c $(NOAHMP) + +# compile common routines +compile_comm: + $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) + +# compile SUMMA routines +compile_summa: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ + $(INCLUDES) + +# link routines +link: + $(FC_EXE) -g *.o $(LIBRARIES) -o $(DRIVER__EX) + +# Remove object files +clean: + rm -f *.o + rm -f *.mod + rm -f soil_veg_gen_parm__genmod.f90 + +# Copy the executable to the bin directory +install: + @mkdir -p $(EXE_PATH) + @mv $(DRIVER__EX) $(EXE_PATH) + $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) diff --git a/build/source/engine/computEnthalpy.f90 b/build/source/engine/computEnthalpy.f90 new file mode 100644 index 000000000..ec3ae1498 --- /dev/null +++ b/build/source/engine/computEnthalpy.f90 @@ -0,0 +1,140 @@ + +module computEnthalpy_module + +! data types +USE nrtype + +! derived types to define the data structures +USE data_types,only:& + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength ! data vector with variable length dimension (dp) + +! named variables +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookFLUX ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements +USE var_lookup,only:iLookPARAM ! named variables for structure elements + +! access the global print flag +USE globalData,only:globalPrintFlag + +! access missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing real number +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil + +! constants +USE multiconst,only:& + Tfreeze, & ! freezing temperature (K) + LH_fus, & ! latent heat of fusion (J kg-1) + LH_vap, & ! latent heat of vaporization (J kg-1) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water, & ! intrinsic density of liquid water (kg m-3) + iden_air, & + ! specific heat + Cp_air, & ! specific heat of air (J kg-1 K-1) + Cp_ice, & ! specific heat of ice (J kg-1 K-1) + Cp_soil, & ! specific heat of soil (J kg-1 K-1) + Cp_water ! specific heat of liquid water (J kg-1 K-1) +! privacy +implicit none +private +public::computEnthalpy +public::computEnthalpyPrime +contains + + ! ********************************************************************************************************** + ! public subroutine computEnthalpy + ! ********************************************************************************************************** + subroutine computEnthalpy(& + ! input + indx_data, & + nLayers, & + mLayerTemp, & + mLayerVolFracIce, & + mLayerHeatCap, & + ! output + mLayerEnthalpy & + ) + ! -------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input: model control + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + integer(i4b),intent(in) :: nLayers ! number of snow layers + real(dp),intent(in) :: mLayerTemp(:) ! temperature of each snow/soil layer (K) + real(dp),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice (-) + real(dp),intent(in) :: mLayerHeatCap(:) + real(dp),intent(out) :: mLayerEnthalpy(:) + + ! local variables + integer(i4b) :: iLayer + + ! -------------------------------------------------------------------------------------------------------------------------------- + + associate(& + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of layer + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat & ! intent(in): [i4b(:)] indices for energy states + ) + ! (loop through non-missing energy state variables in the snow+soil domain) + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) + select case( layerType(iLayer) ) + case(iname_snow) + mLayerEnthalpy(iLayer) = mLayerHeatCap(iLayer)*mLayerTemp(iLayer) - LH_fus*iden_ice * mLayerVolFracIce(iLayer) + case(iname_soil) + mLayerEnthalpy(iLayer) = mLayerHeatCap(iLayer)*mLayerTemp(iLayer) - LH_fus*iden_water * mLayerVolFracIce(iLayer) + end select + end do ! looping through non-missing energy state variables in the snow+soil domain + + end associate + + end subroutine computEnthalpy + + ! ********************************************************************************************************** + ! public subroutine computEnthalpyPrime + ! ********************************************************************************************************** + subroutine computEnthalpyPrime(& + ! input + indx_data, & + nLayers, & + mLayerTempPrime, & + mLayerVolFracIcePrime, & + mLayerHeatCap, & + ! output + mLayerEnthalpyPrime & + ) + ! -------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input: model control + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + integer(i4b),intent(in) :: nLayers ! number of snow layers + real(dp),intent(in) :: mLayerTempPrime(:) ! temperature of each snow/soil layer (K) + real(dp),intent(in) :: mLayerVolFracIcePrime(:) ! volumetric fraction of ice (-) + real(dp),intent(in) :: mLayerHeatCap(:) + real(dp),intent(out) :: mLayerEnthalpyPrime(:) + + ! local variables + integer(i4b) :: iLayer + + ! -------------------------------------------------------------------------------------------------------------------------------- + + associate(& + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of layer + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat & ! intent(in): [i4b(:)] indices for energy states + ) + ! (loop through non-missing energy state variables in the snow+soil domain) + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) + select case( layerType(iLayer) ) + case(iname_snow) + mLayerEnthalpyPrime(iLayer) = mLayerHeatCap(iLayer)*mLayerTempPrime(iLayer) - LH_fus*iden_ice * mLayerVolFracIcePrime(iLayer) + case(iname_soil) + mLayerEnthalpyPrime(iLayer) = mLayerHeatCap(iLayer)*mLayerTempPrime(iLayer) - LH_fus*iden_water * mLayerVolFracIcePrime(iLayer) + end select + end do ! looping through non-missing energy state variables in the snow+soil domain + + end associate + + end subroutine computEnthalpyPrime + +end module computEnthalpy_module diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 new file mode 100644 index 000000000..55439da21 --- /dev/null +++ b/build/source/engine/computHeatCap.f90 @@ -0,0 +1,301 @@ +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2015 NCAR/RAL +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . + +module computHeatCap_module + +! data types +USE nrtype + +! derived types to define the data structures +USE data_types,only:& + var_d, & ! data vector (dp) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength ! data vector with variable length dimension (dp) + +! named variables defining elements in the data structures +USE var_lookup,only:iLookPARAM,iLookDIAG,iLookINDEX ! named variables for structure elements + +! physical constants +USE multiconst,only:& + iden_air, & ! intrinsic density of air (kg m-3) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water, & ! intrinsic density of water (kg m-3) + ! specific heat + Cp_air, & ! specific heat of air (J kg-1 K-1) + Cp_ice, & ! specific heat of ice (J kg-1 K-1) + Cp_soil, & ! specific heat of soil (J kg-1 K-1) + Cp_water ! specific heat of liquid water (J kg-1 K-1) +! named variables to describe the state variable type +USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space +USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy +USE globalData,only:iname_watCanopy ! named variable defining the mass of total water on the vegetation canopy +USE globalData,only:iname_liqCanopy ! named variable defining the mass of liquid water on the vegetation canopy +USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers +USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers +USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers +USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers +USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers +USE globalData,only:iname_watAquifer ! named variable defining the water storage in the aquifer + +! missing values +USE globalData,only:integerMissing ! missing integer + +! named variables that define the layer type +USE globalData,only:iname_snow ! snow +USE globalData,only:iname_soil ! soil + + +! privacy +implicit none +private +public::computHeatCap +public::computStatMult + +contains + + + ! ********************************************************************************************************** + ! public subroutine computHeatCap: compute diagnostic energy variables (thermal conductivity and heat capacity) + ! ********************************************************************************************************** + subroutine computHeatCap(& + ! input: control variables + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) + ! input: state variables + scalarCanopyIce, & ! intent(in) + scalarCanopyLiquid, & ! intent(in) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + scalarCanopyIcePrime, & ! intent(in) + scalarCanopyLiquidPrime, & ! intent(in) + mLayerVolFracIcePrime, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqPrime, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + ! input data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + ! output + heatCapVeg, & + mLayerHeatCap, & + heatCapVegPrime, & + mLayerHeatCapPrime, & + ! output: error control + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! provide access to external subroutines + USE snow_utils_module,only:tcond_snow ! compute thermal conductivity of snow + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! input: model control + logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + real(dp),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) + real(dp),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) + real(dp),intent(in) :: scalarCanopyLiquid + real(dp),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) + real(dp),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) + real(dp),intent(in) :: scalarCanopyIcePrime ! trial value of canopy ice content (kg m-2) + real(dp),intent(in) :: scalarCanopyLiquidPrime + real(dp),intent(in) :: mLayerVolFracLiqPrime(:) ! trial vector of volumetric liquid water content (-) + real(dp),intent(in) :: mLayerVolFracIcePrime(:) ! trial vector of volumetric ice water content (-) + ! input/output: data structures + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model layer indices + ! output: error control + real(qp),intent(out) :: heatCapVeg + real(qp),intent(out) :: mLayerHeatCap(:) + real(qp),intent(out) :: heatCapVegPrime + real(qp),intent(out) :: mLayerHeatCapPrime(:) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: iSoil ! index of soil layer + ! -------------------------------------------------------------------------------------------------------------------------------- + ! associate variables in data structure + associate(& + ! input: coordinate variables + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers + layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) + ! input: heat capacity and thermal conductivity + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) + ! input: depth varying soil parameters + iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) + ) ! end associate statement + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="computHeatCap/" + + ! initialize the soil layer + iSoil=integerMissing + + ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) + if(computeVegFlux)then + heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component + Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component + Cp_ice*scalarCanopyIce/canopyDepth ! ice component + + heatCapVegPrime = Cp_water*scalarCanopyLiquidPrime/canopyDepth + & ! liquid water component + Cp_ice*scalarCanopyIcePrime/canopyDepth ! ice component + end if + + ! loop through layers + do iLayer=1,nLayers + + ! get the soil layer + if(iLayer>nSnow) iSoil = iLayer-nSnow + + ! ***** + ! * compute the volumetric heat capacity of each layer (J m-3 K-1)... + ! ******************************************************************* + select case(layerType(iLayer)) + ! * soil + case(iname_soil) + mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._dp - theta_sat(iSoil) ) + & ! soil component + iden_ice * Cp_Ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component + + mLayerHeatCapPrime(iLayer) = iden_ice * Cp_Ice * mLayerVolFracIcePrime(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiqPrime(iLayer) + & ! liquid water component + iden_air * Cp_air * (-mLayerVolFracIcePrime(iLayer) - mLayerVolFracLiqPrime(iLayer)) ! air component + ! * snow + case(iname_snow) + mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( 1._dp - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component + + mLayerHeatCapPrime(iLayer) = iden_ice * Cp_ice * mLayerVolFracIcePrime(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiqPrime(iLayer) + & ! liquid water component + iden_air * Cp_air * (-mLayerVolFracIcePrime(iLayer) - mLayerVolFracLiqPrime(iLayer)) ! air component + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return + end select + + end do ! looping through layers + !pause + + ! end association to variables in the data structure + end associate + + end subroutine computHeatCap + + ! ********************************************************************************************************** + ! public subroutine computStatMult: get scale factors + ! ********************************************************************************************************** + subroutine computStatMult(& + heatCapVeg, & + mLayerHeatCap, & + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output + sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- + USE nr_utility_module,only:arth ! get a sequence of numbers arth(start, incr, count) + USE f2008funcs_module,only:findIndex ! finds the index of the first value within a vector + ! -------------------------------------------------------------------------------------------------------------------------------- + ! input: data structures + real(qp),intent(out) :: heatCapVeg + real(qp),intent(out) :: mLayerHeatCap(:) + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + ! output: state vectors + real(qp),intent(out) :: sMul(:) ! NOTE: qp ! multiplier for state vector (used in the residual calculations) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + ! -------------------------------------------------------------------------------------------------------------------------------- + ! state subsets + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + integer(i4b) :: ixStateSubset ! index within the state subset + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + associate(& + ! model diagnostic variables + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp] canopy depth (m) + volHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(in) : [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) + ! indices defining specific model states + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy hydrology state variable (mass) + ! vector of energy and hydrology indices for the snow and soil domains + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in) : [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in) : [i4b] number of hydrology state variables in the snow+soil domain + ! type of model state variabless + ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in) : [i4b(:)] [state subset] type of desired model state variables + ! number of layers + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in) : [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in) : [i4b] number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in) : [i4b] total number of layers + ) ! end association with variables in the data structures + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='computStatMult/' + + ! ----- + ! * define components of derivative matrices that are constant over a time step (substep)... + ! ------------------------------------------------------------------------------------------ + + ! define the multiplier for the state vector for residual calculations (vegetation canopy) + ! NOTE: Use the "where" statement to generalize to multiple canopy layers (currently one canopy layer) + + where(ixStateType_subset==iname_nrgCanair) sMul = Cp_air*iden_air ! volumetric heat capacity of air (J m-3 K-1) + where(ixStateType_subset==iname_nrgCanopy) sMul = heatCapVeg ! volumetric heat capacity of the vegetation (J m-3 K-1) + where(ixStateType_subset==iname_watCanopy) sMul = 1._dp ! nothing else on the left hand side + where(ixStateType_subset==iname_liqCanopy) sMul = 1._dp ! nothing else on the left hand side + + + ! define the energy multiplier and diagonal elements for the state vector for residual calculations (snow-soil domain) + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + ixStateSubset = ixSnowSoilNrg(iLayer) ! index within the state vector + sMul(ixStateSubset) = mLayerHeatCap(iLayer) ! transfer volumetric heat capacity to the state multiplier + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! define the hydrology multiplier and diagonal elements for the state vector for residual calculations (snow-soil domain) + if(nSnowSoilHyd>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + ixStateSubset = ixSnowSoilHyd(iLayer) ! index within the state vector + sMul(ixStateSubset) = 1._dp ! state multiplier = 1 (nothing else on the left-hand-side) + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! define the scaling factor and diagonal elements for the aquifer + where(ixStateType_subset==iname_watAquifer) sMul = 1._dp + + ! ------------------------------------------------------------------------------------------ + ! ------------------------------------------------------------------------------------------ + + end associate + ! end association to variables in the data structure where vector length does not change + end subroutine computStatMult + + + +end module computHeatCap_module diff --git a/build/source/engine/computJacobFida.f90 b/build/source/engine/computJacobFida.f90 new file mode 100644 index 000000000..43c698d6e --- /dev/null +++ b/build/source/engine/computJacobFida.f90 @@ -0,0 +1,862 @@ +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2015 NCAR/RAL +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . + +module computJacobFida_module + +! data types +USE nrtype + +! derived types to define the data structures +USE data_types,only:& + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength ! data vector with variable length dimension (dp) + +! named variables for structure elements +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements +USE var_lookup,only:iLookDERIV ! named variables for structure elements + +! look-up values for the form of Richards' equation +USE mDecisions_module,only: & + moisture, & ! moisture-based form of Richards' equation + mixdform ! mixed form of Richards' equation + + +! access the global print flag +USE globalData,only:globalPrintFlag + +! access missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing real number + +! domain types +USE globalData,only:iname_veg ! named variables for vegetation +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil + +! named variables to describe the state variable type +USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space +USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy +USE globalData,only:iname_watCanopy ! named variable defining the mass of water on the vegetation canopy +USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers +USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers +USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers +USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers +USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers + +! access named variables to describe the form and structure of the matrices used in the numerical solver +USE globalData,only: ku ! number of super-diagonal bands +USE globalData,only: kl ! number of sub-diagonal bands +USE globalData,only: ixDiag ! index for the diagonal band +USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix +USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix +USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix +USE globalData,only: iJac1 ! first layer of the Jacobian to print +USE globalData,only: iJac2 ! last layer of the Jacobian to print + +! constants +USE multiconst,only:& + LH_fus, & ! latent heat of fusion (J kg-1) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water ! intrinsic density of liquid water (kg m-3) + +implicit none +! define constants +real(dp),parameter :: verySmall=tiny(1.0_dp) ! a very small number +integer(i4b),parameter :: ixBandOffset=kl+ku+1 ! offset in the band Jacobian matrix + +private +public::computJacobFida +contains + + ! ********************************************************************************************************** + ! public subroutine computJacobFida: compute the Jacobian matrix + ! ********************************************************************************************************** + subroutine computJacobFida(& + ! input: model control + cj, & ! intent(in) + dt, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + computeBaseflow, & ! intent(in): flag to indicate if we need to compute baseflow + ixMatrix, & ! intent(in): form of the Jacobian matrix + specificStorage, & ! intent(in) + theta_sat, & ! intent(in) + ixRichards, & ! intent(in): choice of option for Richards' equation + ! input: data structures + indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables + dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! input: state variables + mLayerTemp, & ! intent(in) + mLayerTempPrime, & ! intent(in) + mLayerMatricHeadPrime, & ! intent(in) + mLayerMatricHeadLiqPrime, & ! intent(in) + mLayerVolFracWatPrime, & ! intent(in) + scalarCanopyTemp, & ! intent(in) + scalarCanopyTempPrime, & ! intent(in) derivative value for temperature of the vegetation canopy (K) + scalarCanopyWatPrime, & ! intetn(in) + mLayerd2Theta_dTk2, & ! intetn(in) + d2VolTot_d2Psi0, & ! intetn(in) + dFracLiqSnow_dTk, & ! intent(in) + d2Theta_dTkCanopy2, & ! intent(in) + dFracLiqVeg_dTkCanopy, & ! intent(in) + ! input-output: Jacobian and its diagonal + dMat, & ! intent(inout): diagonal of the Jacobian matrix + aJac, & ! intent(out): Jacobian matrix + ! output: error control + err,message) ! intent(out): error code and error message + ! ----------------------------------------------------------------------------------------------------------------- + implicit none + ! input: model control + real(dp),intent(in) :: cj + real(dp),intent(in) :: dt ! length of the time step (seconds) + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: computeBaseflow ! flag to indicate if computing baseflow + integer(i4b),intent(in) :: ixMatrix ! form of the Jacobian matrix + real(dp),intent(in) :: specificStorage ! specific storage coefficient (m-1) + real(dp),intent(in) :: theta_sat(:) ! soil porosity (-) + integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation + ! input: data structures + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + real(dp),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + ! input: state variables + real(dp),intent(in) :: mLayerTemp(:) + real(dp),intent(in) :: mLayerTempPrime(:) + real(dp),intent(in) :: mLayerMatricHeadPrime(:) + real(dp),intent(in) :: mLayerMatricHeadLiqPrime(:) + real(dp),intent(in) :: mLayerd2Theta_dTk2(:) + real(dp),intent(in) :: mLayerVolFracWatPrime(:) + real(dp),intent(in) :: scalarCanopyTemp + real(dp),intent(in) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) + real(dp),intent(in) :: scalarCanopyWatPrime + real(dp),intent(in) :: d2VolTot_d2Psi0(:) + real(dp),intent(in) :: dFracLiqSnow_dTk(:) + real(dp),intent(in) :: d2Theta_dTkCanopy2 + real(dp),intent(in) :: dFracLiqVeg_dTkCanopy + ! input-output: Jacobian and its diagonal + real(dp),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix + real(dp),intent(out) :: aJac(:,:) ! Jacobian matrix + ! output variables + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------- + ! * local variables + ! -------------------------------------------------------------- + ! indices of model state variables + integer(i4b) :: jState ! index of state within the state subset + integer(i4b) :: qState ! index of cross-derivative state variable for baseflow + integer(i4b) :: nrgState ! energy state variable + integer(i4b) :: watState ! hydrology state variable + integer(i4b) :: nState ! number of state variables + ! indices of model layers + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: jLayer ! index of model layer within the full state vector (hydrology) + integer(i4b) :: pLayer ! indices of soil layers (used for the baseflow derivatives) + ! conversion factors + real(dp) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative + ! -------------------------------------------------------------- + ! associate variables from data structures + associate(& + ! indices of model state variables + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain + ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer + ! vectors of indices for specfic state types within specific sub-domains IN THE FULL STATE VECTOR + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + ! vector of energy indices for the snow and soil domains + ! NOTE: states not in the subset are equal to integerMissing + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow domain + ixSoilOnlyNrg => indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the soil domain + ! vector of hydrology indices for the snow and soil domains + ! NOTE: states not in the subset are equal to integerMissing + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow domain + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the soil domain + ! number of state variables of a specific type + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain + nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + ! type and index of model control volume + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain + ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the domain (iname_veg, iname_snow, iname_soil) + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for specific model domains + ! mapping between states and model layers + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] list of indices in the full state vector that are in the state subset + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset in each element of the full state vector + ! derivatives in net vegetation energy fluxes w.r.t. relevant state variables + dCanairNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. canopy air temperature + dCanairNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. canopy temperature + dCanairNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dGroundTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. ground temperature + dCanopyNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. canopy air temperature + dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. canopy temperature + dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. ground temperature + dCanopyNetFlux_dCanLiq => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in net canopy fluxes w.r.t. canopy liquid water content + dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net ground flux w.r.t. canopy air temperature + dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net ground flux w.r.t. canopy temperature + dGroundNetFlux_dCanLiq => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in net ground fluxes w.r.t. canopy liquid water content + ! derivatives in evaporative fluxes w.r.t. relevant state variables + dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy air temperature + dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy temperature + dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. ground temperature + dCanopyEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy liquid water content + dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy air temperature + dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy temperature + dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. ground temperature + dGroundEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy liquid water content + ! derivatives in canopy water w.r.t canopy temperature + dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(in): [dp] derivative of canopy liquid storage w.r.t. temperature + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + ! derivatives in canopy liquid fluxes w.r.t. canopy water + scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ,& ! intent(in): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water + ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below + dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above + dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below + ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above + iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(in): [dp(:)] derivative in vertical liquid water flux at layer interfaces + ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above + dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t matric head + ! derivative in baseflow flux w.r.t. aquifer storage + dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] erivative in baseflow flux w.r.t. aquifer storage (s-1) + ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables + dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above + dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk )%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + ! diagnostic variables + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] bulk volumetric heat capacity in each snow and soil layer (J m-3 K-1) + scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl)%dat(1) ,& ! intent(in): [dp] soil control on infiltration, zero or one + ! canopy and layer depth + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat & ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ) ! making association with data in structures + ! -------------------------------------------------------------- + ! initialize error control + err=0; message='computJacobFida/' + + ! ********************************************************************************************************************************************************* + ! ********************************************************************************************************************************************************* + ! * PART 0: PRELIMINARIES (INITIALIZE JACOBIAN AND COMPUTE TIME-VARIABLE DIAGONAL TERMS) + ! ********************************************************************************************************************************************************* + ! ********************************************************************************************************************************************************* + + ! get the number of state variables + nState = size(dMat) + + ! initialize the Jacobian + ! NOTE: this needs to be done every time, since Jacobian matrix is modified in the solver + aJac(:,:) = 0._dp ! analytical Jacobian matrix + + ! compute terms in the Jacobian for vegetation (excluding fluxes) + ! NOTE: energy for vegetation is computed *within* the iteration loop as it includes phase change + if(ixVegNrg/=integerMissing)then + dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy ) * cj & + + LH_fus*iden_water * scalarCanopyTempPrime * d2Theta_dTkCanopy2 & + + LH_fus * dFracLiqVeg_dTkCanopy * scalarCanopyWatPrime / canopyDepth ! volumetric heat capacity of the vegetation (J m-3 K-1) + end if + + ! compute additional terms for the Jacobian for the snow-soil domain (excluding fluxes) + ! NOTE: energy for snow+soil is computed *within* the iteration loop as it includes phase change + do iLayer=1,nLayers + if(ixSnowSoilNrg(iLayer)/=integerMissing) then + dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj & + + LH_fus*iden_water * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & + + LH_fus*iden_water * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) + end if + end do + + ! compute additional terms for the Jacobian for the soil domain (excluding fluxes) + do iLayer=1,nSoil +! print *, '2' + if(ixSoilOnlyHyd(iLayer)/=integerMissing)then + dMat(ixSoilOnlyHyd(iLayer)) = ( dVolTot_dPsi0(iLayer) + dCompress_dPsi(iLayer) ) * cj + d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) + + if(ixRichards==mixdform)then + dMat(ixSoilOnlyHyd(iLayer)) = dMat(ixSoilOnlyHyd(iLayer)) + specificStorage * dVolTot_dPsi0(iLayer) * mLayerMatricHeadPrime(iLayer) / theta_sat(iLayer) + end if + + end if + end do + + + + ! define the form of the matrix + select case(ixMatrix) + + ! ********************************************************************************************************************************************************* + ! ********************************************************************************************************************************************************* + ! * PART 1: BAND MATRIX + ! ********************************************************************************************************************************************************* + ! ********************************************************************************************************************************************************* + case(ixBandMatrix) ! ixBandMatrix ixFullMatrix + + ! check + if(size(aJac,1)/=nBands .or. size(aJac,2)/=size(dMat))then + message=trim(message)//'unexpected shape of the Jacobian matrix: expect aJac(nBands,nState)' + err=20; return + end if + + ! ----- + ! * energy and liquid fluxes over vegetation... + ! --------------------------------------------- + if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) + + ! * diagonal elements for the vegetation canopy (-) + if(ixCasNrg/=integerMissing) aJac(ixDiag,ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) * cj + if(ixVegNrg/=integerMissing) aJac(ixDiag,ixVegNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanopyTemp) + dMat(ixVegNrg) + if(ixVegHyd/=integerMissing) aJac(ixDiag,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanLiq - scalarCanopyLiqDeriv)*dt + 1._dp * cj ! ixVegHyd: CORRECT + + ! * cross-derivative terms w.r.t. canopy water + if(ixVegHyd/=integerMissing)then + ! cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) + if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixCasNrg),ixCasNrg) = -dCanopyEvaporation_dTCanair*dt ! ixCasNrg: CORRECT + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixVegNrg),ixVegNrg) = -dCanopyEvaporation_dTCanopy*dt + dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy ! ixVegNrg: CORRECT + if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixTopNrg),ixTopNrg) = -dCanopyEvaporation_dTGround*dt ! ixTopNrg: CORRECT + ! cross-derivative terms w.r.t. canopy water (kg-1 m2) + if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water + ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) + ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (dt/canopyDepth) *(-dCanopyNetFlux_dCanLiq) + cj * (-1._dp + scalarFracLiqVeg)*LH_fus/canopyDepth + LH_fus * iden_water * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy ! dF/dLiq + if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanLiq) + endif + + ! cross-derivative terms between surface hydrology and the temperature of the vegetation canopy (K-1) + if(ixVegNrg/=integerMissing)then + if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarCanopyLiqDeriv*dCanLiq_dTcanopy)/iden_water + endif + + ! cross-derivative terms w.r.t. the temperature of the canopy air space (J m-3 K-1) + if(ixCasNrg/=integerMissing)then + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixCasNrg,ixVegNrg),ixVegNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanopyTemp) + if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixCasNrg,ixTopNrg),ixTopNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dGroundTemp) + endif + + ! cross-derivative terms w.r.t. the temperature of the vegetation canopy (J m-3 K-1) + if(ixVegNrg/=integerMissing)then + if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixCasNrg),ixCasNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanairTemp) + if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixTopNrg),ixTopNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dGroundTemp) + endif + + ! cross-derivative terms w.r.t. the temperature of the surface (J m-3 K-1) + if(ixTopNrg/=integerMissing)then + if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanairTemp) + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanopyTemp) + endif + + endif ! if there is a need to compute energy fluxes within vegetation + + ! ----- + ! * energy fluxes for the snow+soil domain... + ! ------------------------------------------- + if(nSnowSoilNrg>0)then + do iLayer=1,nLayers ! loop through all layers in the snow+soil domain + + ! check if the state is in the subset + if(ixSnowSoilNrg(iLayer)==integerMissing) cycle + + ! - define index within the state subset and the full state vector + jState = ixSnowSoilNrg(iLayer) ! index within the state subset + + ! - diagonal elements + aJac(ixDiag,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) + + ! - lower-diagonal elements + if(iLayer > 1)then + if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowSoilNrg(iLayer-1),jState),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) + endif + + ! - upper diagonal elements + if(iLayer < nLayers)then + if(ixSnowSoilNrg(iLayer+1)/=integerMissing) aJac(ixOffDiag(ixSnowSoilNrg(iLayer+1),jState),jState) = (dt/mLayerDepth(iLayer+1))*(-dNrgFlux_dTempAbove(iLayer ) ) + endif + + end do ! (looping through energy states in the snow+soil domain) + endif ! (if the subset includes energy state variables in the snow+soil domain) + + ! ----- + ! * liquid water fluxes for the snow domain... + ! -------------------------------------------- + if(nSnowOnlyHyd>0)then + do iLayer=1,nSnow ! loop through layers in the snow domain + + ! - check that the snow layer is desired + if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle + + ! - define state indices for the current layer + watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset + + ! compute factor to convert liquid water derivative to total water derivative + select case( ixHydType(iLayer) ) + case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) + case default; convLiq2tot = 1._dp + end select + + ! - diagonal elements + aJac(ixDiag,watState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot + dMat(watState) * cj + + ! - lower-diagonal elements + if(iLayer > 1)then + if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyHyd(iLayer-1),watState),watState) = 0._dp ! sub-diagonal: no dependence on other layers + endif + + ! - upper diagonal elements + if(iLayer < nSnow)then + if(ixSnowOnlyHyd(iLayer+1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyHyd(iLayer+1),watState),watState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot ! dVol(below)/dLiq(above) -- (-) + endif + + ! - compute cross-derivative terms for energy + ! NOTE: increase in volumetric liquid water content balanced by a decrease in volumetric ice content + if(nSnowOnlyNrg>0)then + + ! (define the energy state) + nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector + if(nrgstate/=integerMissing)then ! (energy state for the current layer is within the state subset) + + ! (cross-derivative terms for the current layer) + aJac(ixOffDiag(nrgState,watState),watState) = -(1._dp - mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & + + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) + aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) + + ! (cross-derivative terms for the layer below) + if(iLayer < nSnow)then + aJac(ixOffDiag(ixSnowOnlyHyd(iLayer+1),nrgState),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 + endif ! (if there is a water state in the layer below the current layer in the given state subset) + + endif ! (if the energy state for the current layer is within the state subset) + endif ! (if state variables exist for energy in snow+soil layers) + + end do ! (looping through liquid water states in the snow domain) + endif ! (if the subset includes hydrology state variables in the snow domain) + + ! ----- + ! * liquid water fluxes for the soil domain... + ! -------------------------------------------- + if(nSoilOnlyHyd>0)then + do iLayer=1,nSoil + + ! - check that the soil layer is desired + if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle + + ! - define state indices + watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset + + ! - define indices of the soil layers + jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + + ! - compute the diagonal elements + ! all terms *excluding* baseflow + aJac(ixDiag,watState) = (dt/mLayerDepth(jLayer))*(-dq_dHydStateBelow(iLayer-1) + dq_dHydStateAbove(iLayer)) + dMat(watState) + + ! - compute the lower-diagonal elements + if(iLayer > 1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),watState),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) + endif + + ! - compute the upper-diagonal elements + if(iLayer0 .and. nSoilOnlyNrg>0)then + do iLayer=1,nSoilOnlyHyd + + ! - check that the soil layer is desired + if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle + + ! - define index of hydrology state variable within the state subset + watState = ixSoilOnlyHyd(iLayer) + + ! - define indices of the soil layers + jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + + ! - define the energy state variable + nrgState = ixNrgLayer(jLayer) ! index within the full state vector + + ! only compute derivatives if the energy state for the current layer is within the state subset + if(nrgstate/=integerMissing)then + + ! - compute the Jacobian for the layer itself + aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance + + ! - include derivatives w.r.t. ground evaporation + if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer + if(computeVegFlux)then + aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanLiq/iden_water) ! dVol/dLiq (kg m-2)-1 + aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) + aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) + endif + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) + endif + + ! melt-freeze: compute derivative in energy with respect to mass + if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present + aJac(ixOffDiag(nrgState,watState),watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water*cj - LH_fus*iden_water * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + else + aJac(ixOffDiag(nrgState,watState),watState) = 0._dp + endif + + ! - compute lower diagonal elements + if(iLayer>1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),nrgState),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 + endif + + ! compute upper-diagonal elements + if(iLayer0)then + do iLayer=1,nLayers ! loop through all layers in the snow+soil domain + + ! check if the state is in the subset + if(ixSnowSoilNrg(iLayer)==integerMissing) cycle + + ! - define index within the state subset and the full state vector + jState = ixSnowSoilNrg(iLayer) ! index within the state subset + + ! - diagonal elements + aJac(jState,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) + + ! - lower-diagonal elements + if(iLayer > 1)then + if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixSnowSoilNrg(iLayer-1),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) + endif + + ! - upper diagonal elements + if(iLayer < nLayers)then + if(ixSnowSoilNrg(iLayer+1)/=integerMissing) aJac(ixSnowSoilNrg(iLayer+1),jState) = (dt/mLayerDepth(iLayer+1))*(-dNrgFlux_dTempAbove(iLayer ) ) + endif + + end do ! (looping through energy states in the snow+soil domain) + endif ! (if the subset includes energy state variables in the snow+soil domain) + + ! ----- + ! * liquid water fluxes for the snow domain... + ! -------------------------------------------- + if(nSnowOnlyHyd>0)then + do iLayer=1,nSnow ! loop through layers in the snow domain + + ! - check that the snow layer is desired + if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle + + ! - define state indices for the current layer + watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset + + ! compute factor to convert liquid water derivative to total water derivative + select case( ixHydType(iLayer) ) + case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) + case default; convLiq2tot = 1._dp + end select + + ! - diagonal elements + aJac(watState,watState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot + dMat(watState) * cj + + ! - lower-diagonal elements + if(iLayer > 1)then + if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSnowOnlyHyd(iLayer-1),watState) = 0._dp ! sub-diagonal: no dependence on other layers + endif + + ! - upper diagonal elements + if(iLayer < nSnow)then + if(ixSnowOnlyHyd(iLayer+1)/=integerMissing) aJac(ixSnowOnlyHyd(iLayer+1),watState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot ! dVol(below)/dLiq(above) -- (-) + endif + + ! - compute cross-derivative terms for energy + ! NOTE: increase in volumetric liquid water content balanced by a decrease in volumetric ice content + if(nSnowOnlyNrg>0)then + + ! (define the energy state) + nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector + if(nrgstate/=integerMissing)then ! (energy state for the current layer is within the state subset) + + ! (cross-derivative terms for the current layer) + ! print *, '3' + aJac(nrgState,watState) = (-1._dp + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & + + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) + aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) + + ! (cross-derivative terms for the layer below) + if(iLayer < nSnow)then + aJac(ixSnowOnlyHyd(iLayer+1),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 + endif ! (if there is a water state in the layer below the current layer in the given state subset) + + endif ! (if the energy state for the current layer is within the state subset) + endif ! (if state variables exist for energy in snow+soil layers) + + end do ! (looping through liquid water states in the snow domain) + endif ! (if the subset includes hydrology state variables in the snow domain) + + ! ----- + ! * liquid water fluxes for the soil domain... + ! -------------------------------------------- + if(nSoilOnlyHyd>0)then + + do iLayer=1,nSoil + + ! - check that the soil layer is desired + if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle + + ! - define state indices + watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset + + ! - define indices of the soil layers + jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + + ! - compute the diagonal elements + ! all terms *excluding* baseflow + aJac(watState,watState) = (dt/mLayerDepth(jLayer))*(-dq_dHydStateBelow(iLayer-1) + dq_dHydStateAbove(iLayer)) + dMat(watState) + + ! - compute the lower-diagonal elements + if(iLayer > 1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) + endif + + ! - compute the upper-diagonal elements + if(iLayer0 .and. nSoilOnlyNrg>0)then + do iLayer=1,nSoilOnlyHyd + + ! - check that the soil layer is desired + if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle + + ! - define index of hydrology state variable within the state subset + watState = ixSoilOnlyHyd(iLayer) + + ! - define indices of the soil layers + jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + + ! - define the energy state variable + nrgState = ixNrgLayer(jLayer) ! index within the full state vector + + ! only compute derivatives if the energy state for the current layer is within the state subset + if(nrgstate/=integerMissing)then + + ! - compute the Jacobian for the layer itself + aJac(watState,nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance + + ! - include derivatives w.r.t. ground evaporation + if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer + if(computeVegFlux)then + aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanLiq/iden_water) ! dVol/dLiq (kg m-2)-1 + aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) + aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) + endif + aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) + endif + + ! melt-freeze: compute derivative in energy with respect to mass + if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present + ! print *, '4' + aJac(nrgState,watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water*cj - LH_fus*iden_water * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) ! reza not sure about this ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + else + aJac(nrgState,watState) = 0._dp + endif + + ! - compute lower diagonal elements + if(iLayer>1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 + endif + + ! compute upper-diagonal elements + if(iLayer. + +module computJacobFidaCpVar_module + +! data types +USE nrtype + +! derived types to define the data structures +USE data_types,only:& + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength ! data vector with variable length dimension (dp) + +! named variables for structure elements +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements +USE var_lookup,only:iLookDERIV ! named variables for structure elements + +! look-up values for the form of Richards' equation +USE mDecisions_module,only: & + moisture, & ! moisture-based form of Richards' equation + mixdform ! mixed form of Richards' equation + + +! access the global print flag +USE globalData,only:globalPrintFlag + +! access missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing real number + +! domain types +USE globalData,only:iname_veg ! named variables for vegetation +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil + +! named variables to describe the state variable type +USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space +USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy +USE globalData,only:iname_watCanopy ! named variable defining the mass of water on the vegetation canopy +USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers +USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers +USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers +USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers +USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers + +! access named variables to describe the form and structure of the matrices used in the numerical solver +USE globalData,only: ku ! number of super-diagonal bands +USE globalData,only: kl ! number of sub-diagonal bands +USE globalData,only: ixDiag ! index for the diagonal band +USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix +USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix +USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix +USE globalData,only: iJac1 ! first layer of the Jacobian to print +USE globalData,only: iJac2 ! last layer of the Jacobian to print + +! constants +USE multiconst,only:& + LH_fus, & ! latent heat of fusion (J kg-1) + iden_air, & ! intrinsic density of air (kg m-3) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water, & ! intrinsic density of water (kg m-3) + ! specific heat + Cp_air, & ! specific heat of air (J kg-1 K-1) + Cp_ice, & ! specific heat of ice (J kg-1 K-1) + Cp_soil, & ! specific heat of soil (J kg-1 K-1) + Cp_water ! specific heat of liquid water (J kg-1 K-1) + +implicit none +! define constants +real(dp),parameter :: verySmall=tiny(1.0_dp) ! a very small number +integer(i4b),parameter :: ixBandOffset=kl+ku+1 ! offset in the band Jacobian matrix + +private +public::computJacobFidaCpVar +contains + + ! ********************************************************************************************************** + ! public subroutine computJacobFidaCpVar: compute the Jacobian matrix + ! ********************************************************************************************************** + subroutine computJacobFidaCpVar(& + ! input: model control + cj, & ! intent(in) + dt, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + computeBaseflow, & ! intent(in): flag to indicate if we need to compute baseflow + ixMatrix, & ! intent(in): form of the Jacobian matrix + specificStorage, & ! intent(in) + theta_sat, & ! intent(in) + ixRichards, & ! intent(in): choice of option for Richards' equation + ! input: data structures + indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables + dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! input: state variables + mLayerTemp, & ! intent(in) + mLayerTempPrime, & ! intent(in) + mLayerMatricHeadPrime, & ! intent(in) + mLayerMatricHeadLiqPrime, & ! intent(in) + mLayerVolFracWat, & ! intent(in) + mLayerVolFracWatPrime, & ! intent(in) + scalarCanopyTemp, & ! intent(in) + scalarCanopyTempPrime, & ! intent(in) derivative value for temperature of the vegetation canopy (K) + scalarCanopyWatPrime, & ! intetn(in) + mLayerd2Theta_dTk2, & ! intetn(in) + d2VolTot_d2Psi0, & ! intetn(in) + dFracLiqSnow_dTk, & ! intent(in) + d2Theta_dTkCanopy2, & ! intent(in) + dFracLiqVeg_dTkCanopy, & ! intent(in) + ! input-output: Jacobian and its diagonal + dMat, & ! intent(inout): diagonal of the Jacobian matrix + aJac, & ! intent(out): Jacobian matrix + ! output: error control + err,message) ! intent(out): error code and error message + ! ----------------------------------------------------------------------------------------------------------------- + implicit none + ! input: model control + real(dp),intent(in) :: cj + real(dp),intent(in) :: dt ! length of the time step (seconds) + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: computeBaseflow ! flag to indicate if computing baseflow + integer(i4b),intent(in) :: ixMatrix ! form of the Jacobian matrix + real(dp),intent(in) :: specificStorage ! specific storage coefficient (m-1) + real(dp),intent(in) :: theta_sat(:) ! soil porosity (-) + integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation + ! input: data structures + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + real(dp),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + ! input: state variables + real(dp),intent(in) :: mLayerTemp(:) + real(dp),intent(in) :: mLayerTempPrime(:) + real(dp),intent(in) :: mLayerMatricHeadPrime(:) + real(dp),intent(in) :: mLayerMatricHeadLiqPrime(:) + real(dp),intent(in) :: mLayerd2Theta_dTk2(:) + real(dp),intent(in) :: mLayerVolFracWatPrime(:) + real(dp),intent(in) :: mLayerVolFracWat(:) + real(dp),intent(in) :: scalarCanopyTemp + real(dp),intent(in) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) + real(dp),intent(in) :: scalarCanopyWatPrime + real(dp),intent(in) :: d2VolTot_d2Psi0(:) + real(dp),intent(in) :: dFracLiqSnow_dTk(:) + real(dp),intent(in) :: d2Theta_dTkCanopy2 + real(dp),intent(in) :: dFracLiqVeg_dTkCanopy + ! input-output: Jacobian and its diagonal + real(dp),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix + real(dp),intent(out) :: aJac(:,:) ! Jacobian matrix + ! output variables + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------- + ! * local variables + ! -------------------------------------------------------------- + ! indices of model state variables + integer(i4b) :: jState ! index of state within the state subset + integer(i4b) :: qState ! index of cross-derivative state variable for baseflow + integer(i4b) :: nrgState ! energy state variable + integer(i4b) :: watState ! hydrology state variable + integer(i4b) :: nState ! number of state variables + ! indices of model layers + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: jLayer ! index of model layer within the full state vector (hydrology) + integer(i4b) :: pLayer ! indices of soil layers (used for the baseflow derivatives) + ! conversion factors + real(dp) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative + real(dp) :: beta1 + real(dp) :: beta2 + real(dp) :: gamma1 + real(dp) :: liqSnow + ! -------------------------------------------------------------- + ! associate variables from data structures + associate(& + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): layer type (iname_soil or iname_snow) + ! indices of model state variables + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain + ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer + ! vectors of indices for specfic state types within specific sub-domains IN THE FULL STATE VECTOR + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + ! vector of energy indices for the snow and soil domains + ! NOTE: states not in the subset are equal to integerMissing + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow domain + ixSoilOnlyNrg => indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the soil domain + ! vector of hydrology indices for the snow and soil domains + ! NOTE: states not in the subset are equal to integerMissing + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow domain + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the soil domain + ! number of state variables of a specific type + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain + nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + ! type and index of model control volume + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain + ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the domain (iname_veg, iname_snow, iname_soil) + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for specific model domains + ! mapping between states and model layers + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] list of indices in the full state vector that are in the state subset + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset in each element of the full state vector + ! derivatives in net vegetation energy fluxes w.r.t. relevant state variables + dCanairNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. canopy air temperature + dCanairNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. canopy temperature + dCanairNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dGroundTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. ground temperature + dCanopyNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. canopy air temperature + dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. canopy temperature + dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. ground temperature + dCanopyNetFlux_dCanLiq => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in net canopy fluxes w.r.t. canopy liquid water content + dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net ground flux w.r.t. canopy air temperature + dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net ground flux w.r.t. canopy temperature + dGroundNetFlux_dCanLiq => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in net ground fluxes w.r.t. canopy liquid water content + ! derivatives in evaporative fluxes w.r.t. relevant state variables + dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy air temperature + dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy temperature + dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. ground temperature + dCanopyEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy liquid water content + dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy air temperature + dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy temperature + dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. ground temperature + dGroundEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy liquid water content + ! derivatives in canopy water w.r.t canopy temperature + dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(in): [dp] derivative of canopy liquid storage w.r.t. temperature + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + ! derivatives in canopy liquid fluxes w.r.t. canopy water + scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ,& ! intent(in): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water + ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below + dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above + dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below + ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above + iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(in): [dp(:)] derivative in vertical liquid water flux at layer interfaces + ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above + dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t matric head + ! derivative in baseflow flux w.r.t. aquifer storage + dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] erivative in baseflow flux w.r.t. aquifer storage (s-1) + ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables + dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above + dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk )%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + ! diagnostic variables + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] bulk volumetric heat capacity in each snow and soil layer (J m-3 K-1) + scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl)%dat(1) ,& ! intent(in): [dp] soil control on infiltration, zero or one + ! canopy and layer depth + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat & ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ) ! making association with data in structures + ! -------------------------------------------------------------- + ! initialize error control + err=0; message='computJacobFidaCpVar/' + + ! ********************************************************************************************************************************************************* + ! ********************************************************************************************************************************************************* + ! * PART 0: PRELIMINARIES (INITIALIZE JACOBIAN AND COMPUTE TIME-VARIABLE DIAGONAL TERMS) + ! ********************************************************************************************************************************************************* + ! ********************************************************************************************************************************************************* + + ! get the number of state variables + nState = size(dMat) + + beta1 = iden_water * Cp_Ice - iden_water * iden_air * Cp_air / iden_Ice + beta2 = iden_water * Cp_water - iden_air * Cp_air + gamma1 = beta2 - beta1 + + + ! initialize the Jacobian + ! NOTE: this needs to be done every time, since Jacobian matrix is modified in the solver + aJac(:,:) = 0._dp ! analytical Jacobian matrix + + ! compute terms in the Jacobian for vegetation (excluding fluxes) + ! NOTE: energy for vegetation is computed *within* the iteration loop as it includes phase change + if(ixVegNrg/=integerMissing)then + dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy ) * cj & + + LH_fus*iden_water * scalarCanopyTempPrime * d2Theta_dTkCanopy2 & + + LH_fus * dFracLiqVeg_dTkCanopy * scalarCanopyWatPrime / canopyDepth ! volumetric heat capacity of the vegetation (J m-3 K-1) + end if + + ! compute additional terms for the Jacobian for the snow-soil domain (excluding fluxes) + ! NOTE: energy for snow+soil is computed *within* the iteration loop as it includes phase change + do iLayer=1,nLayers + select case(layerType(iLayer)) + case(iname_snow); liqSnow = mLayerFracLiqSnow(iLayer) + case default; liqSnow = 0 + end select + if(ixSnowSoilNrg(iLayer)/=integerMissing) then + dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + (LH_fus*iden_water + gamma1*mLayerTemp(iLayer))*mLayerdTheta_dTk(iLayer) ) * cj & + + LH_fus*iden_water * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & + + LH_fus*iden_water * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) & + + mLayerVolFracWatPrime(iLayer) * ( beta1 + gamma1*liqSnow + gamma1*dFracLiqSnow_dTk(iLayer)*mLayerTemp(iLayer) ) & + + gamma1*mLayerTempPrime(iLayer) * ( mLayerd2Theta_dTk2(iLayer)*mLayerTemp(iLayer) + 2*mLayerdTheta_dTk(iLayer) ) + end if + end do + + ! compute additional terms for the Jacobian for the soil domain (excluding fluxes) + do iLayer=1,nSoil +! print *, '2' + if(ixSoilOnlyHyd(iLayer)/=integerMissing)then + dMat(ixSoilOnlyHyd(iLayer)) = ( dVolTot_dPsi0(iLayer) + dCompress_dPsi(iLayer) ) * cj + d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) + + if(ixRichards==mixdform)then + dMat(ixSoilOnlyHyd(iLayer)) = dMat(ixSoilOnlyHyd(iLayer)) + specificStorage * dVolTot_dPsi0(iLayer) * mLayerMatricHeadPrime(iLayer) / theta_sat(iLayer) + end if + + end if + end do + + + + ! define the form of the matrix + select case(ixMatrix) + + ! FULL MATRIX + ! ********************************************************************************************************************************************************* + ! ********************************************************************************************************************************************************* + case(ixFullMatrix) ! ixFullMatrix ixBandMatrix + + ! check + if(size(aJac,1)/=size(dMat) .or. size(aJac,2)/=size(dMat))then + message=trim(message)//'unexpected shape of the Jacobian matrix: expect aJac(nState,nState)' + err=20; return + end if + + ! ----- + ! * energy and liquid fluxes over vegetation... + ! --------------------------------------------- + if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) + + ! * liquid water fluxes for vegetation canopy (-) + if(ixVegHyd/=integerMissing) aJac(ixVegHyd,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanLiq - scalarCanopyLiqDeriv)*dt + 1._dp * cj + ! * cross-derivative terms for canopy water + if(ixVegHyd/=integerMissing)then + ! cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) + if(ixCasNrg/=integerMissing) aJac(ixVegHyd,ixCasNrg) = -dCanopyEvaporation_dTCanair*dt + if(ixVegNrg/=integerMissing) aJac(ixVegHyd,ixVegNrg) = -dCanopyEvaporation_dTCanopy*dt + dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy + if(ixTopNrg/=integerMissing) aJac(ixVegHyd,ixTopNrg) = -dCanopyEvaporation_dTGround*dt + ! cross-derivative terms w.r.t. canopy water (kg-1 m2) + if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water + ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) + ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 + if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (dt/canopyDepth) *(-dCanopyNetFlux_dCanLiq) + cj * (-1._dp + scalarFracLiqVeg)*LH_fus/canopyDepth + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth ! dF/dLiq + if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanLiq) + endif + + ! cross-derivative terms w.r.t. canopy temperature (K-1) + if(ixVegNrg/=integerMissing)then + if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegNrg) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarCanopyLiqDeriv*dCanLiq_dTcanopy)/iden_water + endif + + ! energy fluxes with the canopy air space (J m-3 K-1) + if(ixCasNrg/=integerMissing)then + aJac(ixCasNrg,ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) * cj + if(ixVegNrg/=integerMissing) aJac(ixCasNrg,ixVegNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanopyTemp) + if(ixTopNrg/=integerMissing) aJac(ixCasNrg,ixTopNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dGroundTemp) + endif + + ! energy fluxes with the vegetation canopy (J m-3 K-1) + if(ixVegNrg/=integerMissing)then + if(ixCasNrg/=integerMissing) aJac(ixVegNrg,ixCasNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanairTemp) + aJac(ixVegNrg,ixVegNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanopyTemp) + dMat(ixVegNrg) + if(ixTopNrg/=integerMissing) aJac(ixVegNrg,ixTopNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dGroundTemp) + endif + + ! energy fluxes with the surface (J m-3 K-1) + if(ixTopNrg/=integerMissing)then + if(ixCasNrg/=integerMissing) aJac(ixTopNrg,ixCasNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanairTemp) + if(ixVegNrg/=integerMissing) aJac(ixTopNrg,ixVegNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanopyTemp) + endif + + endif ! if there is a need to compute energy fluxes within vegetation + + ! ----- + ! * energy fluxes for the snow+soil domain... + ! ------------------------------------------- + if(nSnowSoilNrg>0)then + do iLayer=1,nLayers ! loop through all layers in the snow+soil domain + + ! check if the state is in the subset + if(ixSnowSoilNrg(iLayer)==integerMissing) cycle + + ! - define index within the state subset and the full state vector + jState = ixSnowSoilNrg(iLayer) ! index within the state subset + + ! - diagonal elements + aJac(jState,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) + + ! - lower-diagonal elements + if(iLayer > 1)then + if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixSnowSoilNrg(iLayer-1),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) + endif + + ! - upper diagonal elements + if(iLayer < nLayers)then + if(ixSnowSoilNrg(iLayer+1)/=integerMissing) aJac(ixSnowSoilNrg(iLayer+1),jState) = (dt/mLayerDepth(iLayer+1))*(-dNrgFlux_dTempAbove(iLayer ) ) + endif + + end do ! (looping through energy states in the snow+soil domain) + endif ! (if the subset includes energy state variables in the snow+soil domain) + + ! ----- + ! * liquid water fluxes for the snow domain... + ! -------------------------------------------- + if(nSnowOnlyHyd>0)then + do iLayer=1,nSnow ! loop through layers in the snow domain + + ! - check that the snow layer is desired + if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle + + ! - define state indices for the current layer + watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset + + ! compute factor to convert liquid water derivative to total water derivative + select case( ixHydType(iLayer) ) + case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) + case default; convLiq2tot = 1._dp + end select + + ! - diagonal elements + aJac(watState,watState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot + dMat(watState) * cj + + ! - lower-diagonal elements + if(iLayer > 1)then + if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSnowOnlyHyd(iLayer-1),watState) = 0._dp ! sub-diagonal: no dependence on other layers + endif + + ! - upper diagonal elements + if(iLayer < nSnow)then + if(ixSnowOnlyHyd(iLayer+1)/=integerMissing) aJac(ixSnowOnlyHyd(iLayer+1),watState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot ! dVol(below)/dLiq(above) -- (-) + endif + + ! - compute cross-derivative terms for energy + ! NOTE: increase in volumetric liquid water content balanced by a decrease in volumetric ice content + if(nSnowOnlyNrg>0)then + + ! (define the energy state) + nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector + if(nrgstate/=integerMissing)then ! (energy state for the current layer is within the state subset) + + ! (cross-derivative terms for the current layer) + ! print *, '3' + aJac(nrgState,watState) = ( -LH_fus*iden_water*(1._dp - mLayerFracLiqSnow(iLayer)) & + + ( beta1 + gamma1*mLayerFracLiqSnow(iLayer) ) * mLayerTemp(iLayer) ) * cj & + + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) & + + ( gamma1*dFracLiqSnow_dTk(iLayer)*mLayerTemp(iLayer) + beta1 + gamma1*mLayerFracLiqSnow(iLayer) ) & + * mLayerTempPrime(iLayer) ! (dF/dLiq) + ! (cross-derivative terms for the layer below) + if(iLayer < nSnow)then + aJac(ixSnowOnlyHyd(iLayer+1),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 + endif ! (if there is a water state in the layer below the current layer in the given state subset) + + endif ! (if the energy state for the current layer is within the state subset) + endif ! (if state variables exist for energy in snow+soil layers) + + end do ! (looping through liquid water states in the snow domain) + endif ! (if the subset includes hydrology state variables in the snow domain) + + ! ----- + ! * liquid water fluxes for the soil domain... + ! -------------------------------------------- + if(nSoilOnlyHyd>0)then + + do iLayer=1,nSoil + + ! - check that the soil layer is desired + if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle + + ! - define state indices + watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset + + ! - define indices of the soil layers + jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + + ! - compute the diagonal elements + ! all terms *excluding* baseflow + aJac(watState,watState) = (dt/mLayerDepth(jLayer))*(-dq_dHydStateBelow(iLayer-1) + dq_dHydStateAbove(iLayer)) + dMat(watState) + + ! - compute the lower-diagonal elements + if(iLayer > 1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) + endif + + ! - compute the upper-diagonal elements + if(iLayer0 .and. nSoilOnlyNrg>0)then + do iLayer=1,nSoilOnlyHyd + + ! - check that the soil layer is desired + if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle + + ! - define index of hydrology state variable within the state subset + watState = ixSoilOnlyHyd(iLayer) + + ! - define indices of the soil layers + jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + + ! - define the energy state variable + nrgState = ixNrgLayer(jLayer) ! index within the full state vector + + ! only compute derivatives if the energy state for the current layer is within the state subset + if(nrgstate/=integerMissing)then + + ! - compute the Jacobian for the layer itself + aJac(watState,nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance + + ! - include derivatives w.r.t. ground evaporation + if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer + if(computeVegFlux)then + aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanLiq/iden_water) ! dVol/dLiq (kg m-2)-1 + aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) + aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) + endif + aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) + endif + + ! melt-freeze: compute derivative in energy with respect to mass + if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present + ! print *, '4' + aJac(nrgState,watState) = dVolTot_dPsi0(iLayer)*(beta2*mLayerTemp(iLayer) - LH_fus*iden_water)*cj & + + (beta2 - LH_fus*iden_water)*d2VolTot_d2Psi0(iLayer)*mLayerMatricHeadPrime(iLayer) & + + beta2*mLayerTempPrime(iLayer)*dVolTot_dPsi0(iLayer) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + else + aJac(nrgState,watState) = 0._dp + endif + + ! - compute lower diagonal elements + if(iLayer>1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 + endif + + ! compute upper-diagonal elements + if(iLayer diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! model fluxes (sink terms in the soil domain) + mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(in): [dp] transpiration loss from each soil layer (m s-1) + mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(in): [dp(:)] baseflow from each soil layer (m s-1) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in storage associated with compression of the soil matrix (-) + ! number of state variables of a specific type + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + ! model indices + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the soil subdomain + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of hydrology states in snow+soil domain + layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] named variables defining the type of layer in snow+soil domain + ) ! association to necessary variables for the residual computations + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="computResidFida/" + + + ! --- + ! * compute sink terms... + ! ----------------------- + + ! intialize additional terms on the RHS as zero + rAdd(:) = 0._dp + + ! compute energy associated with melt freeze for the vegetation canopy + if(ixVegNrg/=integerMissing) rAdd(ixVegNrg) = rAdd(ixVegNrg) + LH_fus*scalarCanopyIcePrime/canopyDepth ! energy associated with melt/freeze (J m-3) + ! compute energy associated with melt/freeze for snow + ! NOTE: allow expansion of ice during melt-freeze for snow; deny expansion of ice during melt-freeze for soil + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + select case( layerType(iLayer) ) + case(iname_snow) + rAdd( ixSnowSoilNrg(iLayer) ) = rAdd( ixSnowSoilNrg(iLayer) ) + LH_fus*iden_ice * mLayerVolFracIcePrime(iLayer) + case(iname_soil); rAdd( ixSnowSoilNrg(iLayer) ) = rAdd( ixSnowSoilNrg(iLayer) ) + LH_fus*iden_water * mLayerVolFracIcePrime(iLayer) + end select + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + + ! sink terms soil hydrology (-) + ! NOTE 1: state variable is volumetric water content, so melt-freeze is not included + ! NOTE 2: ground evaporation was already included in the flux at the upper boundary + ! NOTE 3: rAdd(ixSnowOnlyWat)=0, and is defined in the initialization above + ! NOTE 4: same sink terms for matric head and liquid matric potential + if(nSoilOnlyHyd>0)then + do concurrent (iLayer=1:nSoil,ixSoilOnlyHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) + rAdd( ixSoilOnlyHyd(iLayer) ) = rAdd( ixSoilOnlyHyd(iLayer) ) + (mLayerTranspire(iLayer) - mLayerBaseflow(iLayer) )/mLayerDepth(iLayer+nSnow) - mLayerCompress(iLayer) + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! --- + ! * compute the residual vector... + ! -------------------------------- + + ! compute the residual vector for the vegetation canopy + ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision + ! --> energy balance + if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*scalarCanairTempPrime - ( fVec(ixCasNrg) + rAdd(ixCasNrg) ) + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = heatCapVegPrime * scalarCanopyTempTrial + sMul(ixVegNrg) * scalarCanopyTempPrime - ( fVec(ixVegNrg) + rAdd(ixVegNrg) ) + ! --> mass balance + if(ixVegHyd/=integerMissing)then + scalarCanopyHydPrime = merge(scalarCanopyWatPrime, scalarCanopyLiqPrime, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) + rVec(ixVegHyd) = sMul(ixVegHyd)*scalarCanopyHydPrime - ( fVec(ixVegHyd) + rAdd(ixVegHyd) ) + endif + + ! compute the residual vector for the snow and soil sub-domains for energy + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + rVec( ixSnowSoilNrg(iLayer) ) = mLayerEnthalpyPrime(iLayer) - fVec( ixSnowSoilNrg(iLayer) ) + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! compute the residual vector for the snow and soil sub-domains for hydrology + ! NOTE: residual depends on choice of state variable + if(nSnowSoilHyd>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) + ! (get the correct state variable) + mLayerVolFracHydPrime(iLayer) = merge(mLayerVolFracWatPrime(iLayer), mLayerVolFracLiqPrime(iLayer), (ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_matLayer) ) + ! (compute the residual) + rVec( ixSnowSoilHyd(iLayer) ) = mLayerVolFracHydPrime(iLayer) - ( fVec( ixSnowSoilHyd(iLayer) ) + rAdd( ixSnowSoilHyd(iLayer) ) ) + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! compute the residual vector for the aquifer + if(ixAqWat/=integerMissing) rVec(ixAqWat) = sMul(ixAqWat)*scalarAquiferStoragePrime - ( fVec(ixAqWat) + rAdd(ixAqWat) ) + + ! print result + if(globalPrintFlag)then + write(*,'(a,1x,100(e12.5,1x))') 'rVec = ', rVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) + write(*,'(a,1x,100(e12.5,1x))') 'fVec = ', fVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) + !print*, 'PAUSE:'; read(*,*) + endif + + ! print *, 'rVec = ', rVec(indx_data%var(iLookINDEX%ixMatOnly)%dat) + ! check + if(any(isNan(rVec)))then +! call printResidFida(nSnow, nSoil, nLayers, indx_data, rAdd, rVec) + message=trim(message)//'we found NaN' + err=20; return + endif + + ! end association with the necessary variabiles for the residual calculations + end associate + + end subroutine computResidFida + +end module computResidFida_module diff --git a/build/source/engine/computThermConduct.f90 b/build/source/engine/computThermConduct.f90 new file mode 100644 index 000000000..4d35ed0dd --- /dev/null +++ b/build/source/engine/computThermConduct.f90 @@ -0,0 +1,317 @@ + +module computThermConduct_module + +! data types +USE nrtype + +! derived types to define the data structures +USE data_types,only:& + var_d, & ! data vector (dp) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength ! data vector with variable length dimension (dp) + +! named variables defining elements in the data structures +USE var_lookup,only:iLookPARAM,iLookPROG,iLookDIAG,iLookINDEX ! named variables for structure elements +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure + +! physical constants +USE multiconst,only:& + iden_air, & ! intrinsic density of air (kg m-3) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water, & ! intrinsic density of water (kg m-3) + ! specific heat + Cp_air, & ! specific heat of air (J kg-1 K-1) + Cp_ice, & ! specific heat of ice (J kg-1 K-1) + Cp_soil, & ! specific heat of soil (J kg-1 K-1) + Cp_water, & ! specific heat of liquid water (J kg-1 K-1) + ! thermal conductivity + lambda_air, & ! thermal conductivity of air (J s-1 m-1) + lambda_ice, & ! thermal conductivity of ice (J s-1 m-1) + lambda_water ! thermal conductivity of water (J s-1 m-1) + +! missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing real number + +! named variables that define the layer type +USE globalData,only:iname_snow ! snow +USE globalData,only:iname_soil ! soil + +! provide access to named variables for thermal conductivity of soil +USE globalData,only:model_decisions ! model decision structure + +! decisions for thermal conductivity of soil +USE mDecisions_module,only:Smirnova2000 ! option for temporally constant thermal conductivity + +! decisions for thermal conductivity of soil +USE mDecisions_module,only: funcSoilWet, & ! function of soil wetness + mixConstit, & ! mixture of constituents + hanssonVZJ ! test case for the mizoguchi lab experiment, Hansson et al. VZJ 2004 + +! privacy +implicit none +private +public::computThermConduct + +! algorithmic parameters +real(dp),parameter :: valueMissing=-9999._dp ! missing value, used when diagnostic or state variables are undefined +real(dp),parameter :: verySmall=1.e-6_dp ! used as an additive constant to check if substantial difference among real numbers +real(dp),parameter :: mpe=1.e-6_dp ! prevents overflow error if division by zero +real(dp),parameter :: dx=1.e-6_dp ! finite difference increment +contains + + + ! ********************************************************************************************************** + ! public subroutine computThermConduct: compute diagnostic energy variables (thermal conductivity and heat capacity) + ! ********************************************************************************************************** + subroutine computThermConduct(& + ! input: control variables + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) + ! input: state variables + scalarCanopyIce, & ! intent(in): canopy ice content (kg m-2) + scalarCanopyLiquid, & ! intent(in): canopy liquid water content (kg m-2) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + ! input/output: data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + ! output: error control + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! provide access to external subroutines + USE snow_utils_module,only:tcond_snow ! compute thermal conductivity of snow + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! input: model control + logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + real(dp),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) + real(dp),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) + real(dp),intent(in) :: scalarCanopyLiquid + real(dp),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) + real(dp),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) + ! input/output: data structures + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model layer indices + type(var_dlength),intent(in) :: prog_data ! model prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for a local HRU + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: iSoil ! index of soil layer + real(dp) :: TCn ! thermal conductivity below the layer interface (W m-1 K-1) + real(dp) :: TCp ! thermal conductivity above the layer interface (W m-1 K-1) + real(dp) :: zdn ! height difference between interface and lower value (m) + real(dp) :: zdp ! height difference between interface and upper value (m) + real(dp) :: bulkden_soil ! bulk density of soil (kg m-3) + real(dp) :: lambda_drysoil ! thermal conductivity of dry soil (W m-1) + real(dp) :: lambda_wetsoil ! thermal conductivity of wet soil (W m-1) + real(dp) :: lambda_wet ! thermal conductivity of the wet material + real(dp) :: relativeSat ! relative saturation (-) + real(dp) :: kerstenNum ! the Kersten number (-), defining weight applied to conductivity of the wet medium + real(dp) :: den ! denominator in the thermal conductivity calculations + ! local variables to reproduce the thermal conductivity of Hansson et al. VZJ 2005 + real(dp),parameter :: c1=0.55_dp ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(dp),parameter :: c2=0.8_dp ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(dp),parameter :: c3=3.07_dp ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(dp),parameter :: c4=0.13_dp ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(dp),parameter :: c5=4._dp ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(dp),parameter :: f1=13.05_dp ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(dp),parameter :: f2=1.06_dp ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(dp) :: fArg,xArg ! temporary variables (see Hansson et al. VZJ 2005 for details) + ! -------------------------------------------------------------------------------------------------------------------------------- + ! associate variables in data structure + associate(& + ! input: model decisions + ixThCondSnow => model_decisions(iLookDECISIONS%thCondSnow)%iDecision, & ! intent(in): choice of method for thermal conductivity of snow + ixThCondSoil => model_decisions(iLookDECISIONS%thCondSoil)%iDecision, & ! intent(in): choice of method for thermal conductivity of soil + ! input: coordinate variables + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1), & ! intent(in): number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers + layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) + mLayerHeight => prog_data%var(iLookPROG%mLayerHeight)%dat, & ! intent(in): height at the mid-point of each layer (m) + iLayerHeight => prog_data%var(iLookPROG%iLayerHeight)%dat, & ! intent(in): height at the interface of each layer (m) + ! input: heat capacity and thermal conductivity + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) + fixedThermalCond_snow => mpar_data%var(iLookPARAM%fixedThermalCond_snow)%dat(1), & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) + ! input: depth varying soil parameters + iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) + thCond_soil => mpar_data%var(iLookPARAM%thCond_soil)%dat, & ! intent(in): thermal conductivity of soil (W m-1 K-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): soil porosity (-) + frac_sand => mpar_data%var(iLookPARAM%frac_sand)%dat, & ! intent(in): fraction of sand (-) + frac_silt => mpar_data%var(iLookPARAM%frac_silt)%dat, & ! intent(in): fraction of silt (-) + frac_clay => mpar_data%var(iLookPARAM%frac_clay)%dat, & ! intent(in): fraction of clay (-) + ! output: diagnostic variables + scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1), & ! intent(out): volumetric heat capacity of the vegetation (J m-3 K-1) + mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat, & ! intent(out): volumetric heat capacity in each layer (J m-3 K-1) + mLayerThermalC => diag_data%var(iLookDIAG%mLayerThermalC)%dat, & ! intent(out): thermal conductivity at the mid-point of each layer (W m-1 K-1) + iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(out): thermal conductivity at the interface of each layer (W m-1 K-1) + mLayerVolFracAir => diag_data%var(iLookDIAG%mLayerVolFracAir)%dat & ! intent(out): volumetric fraction of air in each layer (-) + ) ! end associate statement + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="computThermConduct/" + + ! initialize the soil layer + iSoil=integerMissing + + ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) + if(computeVegFlux)then + scalarBulkVolHeatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component + Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component + Cp_ice*scalarCanopyIce/canopyDepth ! ice component + else + scalarBulkVolHeatCapVeg = valueMissing + end if + !print*, 'computThermConduct: scalarBulkVolHeatCapVeg = ', scalarBulkVolHeatCapVeg + + ! loop through layers + do iLayer=1,nLayers + + ! get the soil layer + if(iLayer>nSnow) iSoil = iLayer-nSnow + + ! compute the thermal conductivity of dry and wet soils (W m-1) + ! NOTE: this is actually constant over the simulation, and included here for clarity + if(ixThCondSoil == funcSoilWet .and. layerType(iLayer)==iname_soil)then + bulkden_soil = iden_soil(iSoil)*( 1._dp - theta_sat(iSoil) ) + lambda_drysoil = (0.135_dp*bulkden_soil + 64.7_dp) / (iden_soil(iSoil) - 0.947_dp*bulkden_soil) + lambda_wetsoil = (8.80_dp*frac_sand(iSoil) + 2.92_dp*frac_clay(iSoil)) / (frac_sand(iSoil) + frac_clay(iSoil)) + end if + + ! ***** + ! * compute the volumetric fraction of air in each layer... + ! ********************************************************* + select case(layerType(iLayer)) + case(iname_soil); mLayerVolFracAir(iLayer) = theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) + case(iname_snow); mLayerVolFracAir(iLayer) = 1._dp - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute volumetric fraction of air'; return + end select + + ! ***** + ! * compute the volumetric heat capacity of each layer (J m-3 K-1)... + ! ******************************************************************* + select case(layerType(iLayer)) + ! * soil + case(iname_soil) + mLayerVolHtCapBulk(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._dp - theta_sat(iSoil) ) + & ! soil component + iden_ice * Cp_Ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * mLayerVolFracAir(iLayer) ! air component + ! * snow + case(iname_snow) + mLayerVolHtCapBulk(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * mLayerVolFracAir(iLayer) ! air component + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return + end select + + ! ***** + ! * compute the thermal conductivity of snow and soil at the mid-point of each layer... + ! ************************************************************************************* + select case(layerType(iLayer)) + + ! ***** soil + case(iname_soil) + + ! select option for thermal conductivity of soil + select case(ixThCondSoil) + + ! ** function of soil wetness + case(funcSoilWet) + + ! compute the thermal conductivity of the wet material (W m-1) + lambda_wet = lambda_wetsoil**( 1._dp - theta_sat(iSoil) ) * lambda_water**theta_sat(iSoil) * lambda_ice**(theta_sat(iSoil) - mLayerVolFracLiq(iLayer)) + relativeSat = (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer))/theta_sat(iSoil) ! relative saturation + ! compute the Kersten number (-) + if(relativeSat > 0.1_dp)then ! log10(0.1) = -1 + kerstenNum = log10(relativeSat) + 1._dp + else + kerstenNum = 0._dp ! dry thermal conductivity + endif + ! ...and, compute the thermal conductivity + mLayerThermalC(iLayer) = kerstenNum*lambda_wet + (1._dp - kerstenNum)*lambda_drysoil + + ! ** mixture of constituents + case(mixConstit) + mLayerThermalC(iLayer) = thCond_soil(iSoil) * ( 1._dp - theta_sat(iSoil) ) + & ! soil component + lambda_ice * mLayerVolFracIce(iLayer) + & ! ice component + lambda_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + lambda_air * mLayerVolFracAir(iLayer) ! air component + + ! ** test case for the mizoguchi lab experiment, Hansson et al. VZJ 2004 + case(hanssonVZJ) + fArg = 1._dp + f1*mLayerVolFracIce(iLayer)**f2 + xArg = mLayerVolFracLiq(iLayer) + fArg*mLayerVolFracIce(iLayer) + mLayerThermalC(iLayer) = c1 + c2*xArg + (c1 - c4)*exp(-(c3*xArg)**c5) + + ! ** check + case default; err=20; message=trim(message)//'unable to identify option for thermal conductivity of soil'; return + + end select ! option for the thermal conductivity of soil + + ! ***** snow + case(iname_snow) + ! temporally constant thermal conductivity + if(ixThCondSnow==Smirnova2000)then + mLayerThermalC(iLayer) = fixedThermalCond_snow + ! thermal conductivity as a function of snow density + else + call tcond_snow(mLayerVolFracIce(iLayer)*iden_ice, & ! input: snow density (kg m-3) + mLayerThermalC(iLayer), & ! output: thermal conductivity (W m-1 K-1) + err,cmessage) ! output: error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + endif + + ! * error check + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute thermal conductivity'; return + + end select + !print*, 'iLayer, mLayerThermalC(iLayer) = ', iLayer, mLayerThermalC(iLayer) + + end do ! looping through layers + !pause + + ! ***** + ! * compute the thermal conductivity of snow at the interface of each layer... + ! **************************************************************************** + do iLayer=1,nLayers-1 ! (loop through layers) + ! get temporary variables + TCn = mLayerThermalC(iLayer) ! thermal conductivity below the layer interface (W m-1 K-1) + TCp = mLayerThermalC(iLayer+1) ! thermal conductivity above the layer interface (W m-1 K-1) + zdn = iLayerHeight(iLayer) - mLayerHeight(iLayer) ! height difference between interface and lower value (m) + zdp = mLayerHeight(iLayer+1) - iLayerHeight(iLayer) ! height difference between interface and upper value (m) + den = TCn*zdp + TCp*zdn ! denominator + ! compute thermal conductivity + if(TCn+TCp > epsilon(TCn))then + iLayerThermalC(iLayer) = (TCn*TCp*(zdn + zdp)) / den + else + iLayerThermalC(iLayer) = (TCn*zdn + TCp*zdp) / (zdn + zdp) + endif + !write(*,'(a,1x,i4,1x,10(f9.3,1x))') 'iLayer, TCn, TCp, zdn, zdp, iLayerThermalC(iLayer) = ', iLayer, TCn, TCp, zdn, zdp, iLayerThermalC(iLayer) + end do ! looping through layers + + ! special case of hansson + if(ixThCondSoil==hanssonVZJ)then + iLayerThermalC(0) = 28._dp*(0.5_dp*(iLayerHeight(1) - iLayerHeight(0))) + else + iLayerThermalC(0) = mLayerThermalC(1) + end if + + ! assume the thermal conductivity at the domain boundaries is equal to the thermal conductivity of the layer + iLayerThermalC(nLayers) = mLayerThermalC(nLayers) + + ! end association to variables in the data structure + end associate + + end subroutine computThermConduct + + +end module computThermConduct_module diff --git a/build/source/engine/convTestFida.f90 b/build/source/engine/convTestFida.f90 new file mode 100644 index 000000000..45cdfc39f --- /dev/null +++ b/build/source/engine/convTestFida.f90 @@ -0,0 +1,236 @@ + + +module convTestFida_module + + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use nrtype + use fida_datatypes + USE globalData,only:model_decisions ! model decision structure + USE globalData,only:flux_meta ! metadata on the model fluxes + ! access missing values + USE globalData,only:integerMissing ! missing integer + USE globalData,only:realMissing ! missing double precision number + + ! indices that define elements of the data structures + USE var_lookup,only:iLookPARAM ! named variables for structure elements + USE var_lookup,only:iLookPROG ! named variables for structure elements + USE var_lookup,only:iLookINDEX ! named variables for structure elements + + USE multiconst,only:& + Cp_air, & ! specific heat of air (J kg-1 K-1) + iden_air, & ! intrinsic density of air (kg m-3) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water ! intrinsic density of liquid water (kg m-3) + + ! provide access to the derived types to define the data structures + USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (dp) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (dp) + model_options ! defines the model decisions + + + + ! privacy + implicit none + private::checkConvFida + public::convTestFida + + +contains + + ! ********************************************************************************************************** + ! public function convTestFida: + ! ********************************************************************************************************** + ! Return values: + ! 0 = success, + ! -1 = fail + ! ---------------------------------------------------------------- + integer(c_int) function convTestFida(NLS, sunvec_ycor, sunvec_del, tol, sunvec_ewt, user_data) & + result(ierr) bind(C,name='convTestFida') + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use fida_mod ! Fortran interface to IDA + use fnvector_serial_mod ! Fortran interface to serial N_Vector + use fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix + use fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver + use fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix + use fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver + use fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver + use fsundials_matrix_mod ! Fortran interface to generic SUNMatrix + use fsundials_nvector_mod ! Fortran interface to generic N_Vector + use fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver + use fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver + use nrtype + use fida_datatypes + + + !======= Declarations ========= + implicit none + + ! calling variables + type(SUNNonLinearSolver) :: NLS + type(N_Vector) :: sunvec_ycor + type(N_Vector) :: sunvec_del + real(dp), value :: tol + type(N_Vector) :: sunvec_ewt + type(c_ptr), value :: user_data + + + + ! pointers to data in SUNDIALS vectors + type(eqnsData), pointer :: eqns_data ! equations data + real(dp), pointer :: xVec(:) + real(dp), pointer :: xInc(:) + logical(lgt) :: isConv + integer(i4b) :: mSoil ! number of soil layers in solution vector + + ! get equations data from user-defined data + call c_f_pointer(user_data, eqns_data) + xVec => FN_VGetArrayPointer(sunvec_ycor) + xInc => FN_VGetArrayPointer(sunvec_del) + + ! get the number of soil layers in the solution vector + mSoil = size(eqns_data%indx_data%var(iLookINDEX%ixMatOnly)%dat) + + isConv = .true. + + + call checkConvFida(xVec,xInc,eqns_data%nSnow,eqns_data%resVec,eqns_data%mpar_data,eqns_data%prog_data,eqns_data%indx_data,eqns_data%scalarSolution,msoil,isConv) + + ! convergence check + if(isConv)then + ierr = 0 + else + ierr = SUN_NLS_CONTINUE + endif + + return + + end function convTestFida + + ! ********************************************************************************************************* + ! private subroutine checkConvFida: check convergence based on the residual vector + ! ********************************************************************************************************* + + subroutine checkConvFida(xVec,xInc,nSnow,resVec,mpar_data,prog_data,indx_data,scalarSolution,msoil,isConv) + + implicit none + + real(dp),intent(in) :: xVec(:) + real(dp),intent(in) :: xInc(:) + integer(i4b),intent(in) :: nSnow + real(qp),intent(in) :: resVec(:) ! residual vector + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + integer(i4b),intent(in) :: mSoil ! number of soil layers in solution vector + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + logical(lgt),intent(out) :: isConv + ! locals + real(dp),dimension(mSoil) :: psiScale ! scaling factor for matric head + real(dp),parameter :: xSmall=1.e-0_dp ! a small offset + real(dp),parameter :: scalarTighten=0.1_dp ! scaling factor for the scalar solution + real(dp) :: soilWatbalErr ! error in the soil water balance + real(dp) :: canopy_max ! absolute value of the residual in canopy water (kg m-2) + real(dp),dimension(1) :: energy_max ! maximum absolute value of the energy residual (J m-3) + real(dp),dimension(1) :: liquid_max ! maximum absolute value of the volumetric liquid water content residual (-) + real(dp),dimension(1) :: matric_max ! maximum absolute value of the matric head iteration increment (m) + real(dp) :: aquifer_max ! absolute value of the residual in aquifer water (m) + logical(lgt) :: canopyConv ! flag for canopy water balance convergence + logical(lgt) :: watbalConv ! flag for soil water balance convergence + logical(lgt) :: liquidConv ! flag for residual convergence + logical(lgt) :: matricConv ! flag for matric head convergence + logical(lgt) :: energyConv ! flag for energy convergence + logical(lgt) :: aquiferConv ! flag for aquifer water balance convergence + + real(dp) :: absTolWatVeg = 1e-6 + real(dp) :: absTolWatSnowSoil = 1e-6 + real(dp) :: relTolMatric = 1e-7 + real(dp) :: absTolAquifr = 1e-6 + real(dp) :: absTolWatBal = 1e-6 + real(dp) :: absTolEerngy = 1e-2 + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + ! association to variables in the data structures + + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + + ! check convergence based on the canopy water balance + if(indx_data%var(iLookINDEX%ixVegHyd)%dat(1)/=integerMissing)then + canopy_max = real(abs(resVec(indx_data%var(iLookINDEX%ixVegHyd)%dat(1))), dp)*iden_water + canopyConv = (canopy_max < absTolWatVeg) ! absolute error in canopy water balance (mm) + else + canopy_max = realMissing + canopyConv = .true. + endif + + ! check convergence based on the residuals for energy (J m-3) + if(size(indx_data%var(iLookINDEX%ixNrgOnly)%dat)>0)then + energy_max = real(maxval(abs( resVec(indx_data%var(iLookINDEX%ixNrgOnly)%dat) )), dp) + energyConv = (energy_max(1) < absTolEerngy) ! (based on the residual) + else + energy_max = realMissing + energyConv = .true. + endif + + ! check convergence based on the residuals for volumetric liquid water content (-) + if(size(indx_data%var(iLookINDEX%ixHydOnly)%dat)>0)then + liquid_max = real(maxval(abs( resVec(indx_data%var(iLookINDEX%ixHydOnly)%dat) ) ), dp) + ! (tighter convergence for the scalar solution) + if(scalarSolution)then + liquidConv = (liquid_max(1) < absTolWatSnowSoil * scalarTighten) ! (based on the residual) + else + liquidConv = (liquid_max(1) < absTolWatSnowSoil) ! (based on the residual) + endif + else + liquid_max = realMissing + liquidConv = .true. + endif + + ! check convergence based on the iteration increment for matric head + ! NOTE: scale by matric head to avoid unnecessairly tight convergence when there is no water + if(size(indx_data%var(iLookINDEX%ixMatOnly)%dat)>0)then + psiScale = abs( xVec(indx_data%var(iLookINDEX%ixMatOnly)%dat) ) + xSmall ! avoid divide by zero + matric_max = maxval(abs( xInc(indx_data%var(iLookINDEX%ixMatOnly)%dat)/psiScale ) ) + matricConv = (matric_max(1) < relTolMatric) ! NOTE: based on iteration increment + else + matric_max = realMissing + matricConv = .true. + endif + + ! check convergence based on the soil water balance error (m) + if(size(indx_data%var(iLookINDEX%ixMatOnly)%dat)>0)then + soilWatBalErr = sum( real(resVec(indx_data%var(iLookINDEX%ixMatOnly)%dat), dp)*prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow + indx_data%var(iLookINDEX%ixMatricHead)%dat ) ) + watbalConv = (abs(soilWatbalErr) < absTolWatBal) ! absolute error in total soil water balance (m) + else + soilWatbalErr = realMissing + watbalConv = .true. + endif + + ! check convergence based on the aquifer storage + if(indx_data%var(iLookINDEX%ixAqWat)%dat(1)/=integerMissing)then + aquifer_max = real(abs(resVec(indx_data%var(iLookINDEX%ixAqWat)%dat(1))), dp)*iden_water + aquiferConv = (aquifer_max < absTolAquifr) ! absolute error in aquifer water balance (mm) + else + aquifer_max = realMissing + aquiferConv = .true. + endif + +! print *, 'canopyConv = ', canopyConv +! print *, 'matricConv = ', matricConv +! print *, 'liquidConv = ', liquidConv +! print *, 'energyConv = ', energyConv +! print *, 'aquiferConv = ', aquiferConv +! print *, '-----------------------------------' + + isConv = (watbalConv .and. canopyConv .and. matricConv .and. liquidConv .and. energyConv .and. aquiferConv) + + + end subroutine checkConvFida + + +end module convTestFida_module diff --git a/build/source/engine/diagn_evar.f90 b/build/source/engine/diagn_evar.f90 index eed2c4584..e2ca80a1b 100755 --- a/build/source/engine/diagn_evar.f90 +++ b/build/source/engine/diagn_evar.f90 @@ -1,5 +1,5 @@ ! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington +! Copyright (C) 2014-2015 NCAR/RAL ! ! This file is part of SUMMA ! diff --git a/build/source/engine/eval8FidaJac.f90 b/build/source/engine/eval8FidaJac.f90 new file mode 100644 index 000000000..572230879 --- /dev/null +++ b/build/source/engine/eval8FidaJac.f90 @@ -0,0 +1,413 @@ + +module eval8FidaJac_module + +! data types +USE nrtype + +! access missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing double precision number +USE globalData,only:quadMissing ! missing quadruple precision number + +! access the global print flag +USE globalData,only:globalPrintFlag + +! define access to state variables to print +USE globalData,only: iJac1 ! first layer of the Jacobian to print +USE globalData,only: iJac2 ! last layer of the Jacobian to print + +! domain types +USE globalData,only:iname_veg ! named variables for vegetation +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil + +! named variables to describe the state variable type +USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space +USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy +USE globalData,only:iname_watCanopy ! named variable defining the mass of water on the vegetation canopy +USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers +USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers +USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers +USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers +USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers +USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix +USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix +USE globalData,only:model_decisions ! model decision structure + +! constants +USE multiconst,only:& + Tfreeze, & ! temperature at freezing (K) + LH_fus, & ! latent heat of fusion (J kg-1) + LH_vap, & ! latent heat of vaporization (J kg-1) + LH_sub, & ! latent heat of sublimation (J kg-1) + Cp_air, & ! specific heat of air (J kg-1 K-1) + iden_air, & ! intrinsic density of air (kg m-3) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water ! intrinsic density of liquid water (kg m-3) + +! provide access to the derived types to define the data structures +USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (dp) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (dp) + model_options ! defines the model decisions + +! indices that define elements of the data structures +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure +USE var_lookup,only:iLookPARAM ! named variables for structure elements +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookFLUX ! named variables for structure elements +USE var_lookup,only:iLookDERIV ! named variables for structure elements + +! look-up values for the choice of groundwater representation (local-column, or single-basin) +USE mDecisions_module,only: & + localColumn, & ! separate groundwater representation in each local soil column + singleBasin ! single groundwater store over the entire basin + +! look-up values for the choice of groundwater parameterization +USE mDecisions_module,only: & + qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization + bigBucket, & ! a big bucket (lumped aquifer model) + noExplicit ! no explicit groundwater parameterization + +! look-up values for the form of Richards' equation +USE mDecisions_module,only: & + moisture, & ! moisture-based form of Richards' equation + mixdform ! mixed form of Richards' equation + +implicit none +private +public::eval8FidaJac + +contains + + ! ********************************************************************************************************** + ! public subroutine eval8FidaJac: compute the residual vector and the Jacobian matrix + ! ********************************************************************************************************** + subroutine eval8FidaJac(& + ! input: model control + cj, & ! intent(in) + dt, & ! intent(in): time step + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + ixMatrix, & ! intent(in) + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + heatCapVaries, & ! intent(in): flag to indicate if heat capacity varies in the current substep + ! input: state vectors + stateVec, & ! intent(in): model state vector + stateVecPrime, & ! intent(in): derivative of model state vector + sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + indx_data, & ! intent(inout): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input: baseflow + dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! output: flux and residual vectors + dMat, & ! intent(inou): diagonal of Jacobian Matrix + Jac, & ! intent(out): jacobian matrix + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- + ! provide access to subroutines + USE varExtrFida_module, only:varExtract ! extract variables from the state vector + USE varExtrFida_module, only:varExtractFida + USE updateVarsFida2_module, only:updateVarsFida2 ! update prognostic variables + USE computJacobFida_module,only:computJacobFida + USE computJacobFidaCpVar_module,only:computJacobFidaCpVar + implicit none + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + ! input: model control + real(dp),intent(in) :: cj + real(dp),intent(in) :: dt ! time step + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + logical(lgt),intent(in) :: heatCapVaries + ! input: state vectors + real(dp),intent(in) :: stateVec(:) ! model state vector + real(dp),intent(in) :: stateVecPrime(:) ! model state vector + real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + ! input: data structures + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(var_dlength), intent(in) :: mpar_data ! model parameters + type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU + ! output: data structures + type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + real(dp),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + ! output: Jacobian + real(dp), intent(inout) :: dMat(:) + real(dp), intent(out) :: Jac(:,:) ! jacobian matrix + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + ! -------------------------------------------------------------------------------------------------------------------------------- + ! state variables + real(dp) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(dp) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(dp) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(dp),dimension(nLayers) :: mLayerTempTrial ! trial value for temperature of layers in the snow and soil domains (K) + real(dp),dimension(nLayers) :: mLayerVolFracWatTrial ! trial value for volumetric fraction of total water (-) + real(dp),dimension(nSoil) :: mLayerMatricHeadTrial ! trial value for total water matric potential (m) + real(dp),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) + real(dp) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + ! diagnostic variables + real(dp) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(dp) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(dp),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) + real(dp),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) + + ! derivative of state variables + real(dp) :: scalarCanairTempPrime ! derivative value for temperature of the canopy air space (K) + real(dp) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) + real(dp) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) + real(dp),dimension(nLayers) :: mLayerTempPrime ! derivative value for temperature of layers in the snow and soil domains (K) + real(dp),dimension(nLayers) :: mLayerVolFracWatPrime ! derivative value for volumetric fraction of total water (-) + real(dp),dimension(nSoil) :: mLayerMatricHeadPrime ! derivative value for total water matric potential (m) + real(dp),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! derivative value for liquid water matric potential (m) + real(dp) :: scalarAquiferStoragePrime ! derivative value of storage of water in the aquifer (m) + ! derivative of diagnostic variables + real(dp) :: scalarCanopyLiqPrime ! derivative value for mass of liquid water on the vegetation canopy (kg m-2) + real(dp) :: scalarCanopyIcePrime ! derivative value for mass of ice on the vegetation canopy (kg m-2) + real(dp),dimension(nLayers) :: mLayerVolFracLiqPrime ! derivative value for volumetric fraction of liquid water (-) + real(dp),dimension(nLayers) :: mLayerVolFracIcePrime ! derivative value for volumetric fraction of ice (-) + ! other local variables + character(LEN=256) :: cmessage ! error message of downwind routine + real(dp) :: dt1 + real(dp) :: mLayerd2Theta_dTk2(nLayers) + real(dp) :: d2VolTot_d2Psi0(nLayers) + real(dp) :: dFracLiqSnow_dTk(nLayers) + real(dp) :: d2Theta_dTkCanopy2 + real(dp) :: dFracLiqVeg_dTkCanopy + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! association to variables in the data structures + ! -------------------------------------------------------------------------------------------------------------------------------- + associate(& + ! model decisions + ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation + ! soil parameters + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) + ! model state variables + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) + ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision & + ) ! association to variables in the data structures + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="eval8FidaJac/" + + + ! extract variables from the model state vector + call varExtract(& + ! input + stateVec, & ! intent(in): model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) + mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) + ! output: variables for the aquifer + scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + + + ! extract derivative of variables from derivative of the model state vector + call varExtractFida(& + ! input + stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy + scalarCanairTempPrime, & ! intent(out): derivative of canopy air temperature (K) + scalarCanopyTempPrime, & ! intent(out): derivative of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(out): derivative of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(out): derivative of canopy liquid water (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempPrime, & ! intent(out): derivative of layer temperature (K) + mLayerVolFracWatPrime, & ! intent(out): derivative of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(out): derivative of volumetric liquid water content (-) + mLayerMatricHeadPrime, & ! intent(out): derivative of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(out): derivative of liquid water matric potential (m) + ! output: variables for the aquifer + scalarAquiferStoragePrime,& ! intent(out): derivative of storage of water in the aquifer (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + + + + call updateVarsFida2(& + ! input + .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output: variables for the vegetation canopy + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2 + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempPrime, & ! + mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) + mLayerVolFracIcePrime, & ! + mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) + mLayerd2Theta_dTk2, & ! intetn(out) + d2VolTot_d2Psi0, & ! intetn(out) + dFracLiqSnow_dTk, & ! intent(out) + d2Theta_dTkCanopy2, & ! intent(out) + dFracLiqVeg_dTkCanopy, & ! intent(out) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + + + ! ----- + ! * compute the Jacobian matrix... + ! -------------------------------- + + ! compute the analytical Jacobian matrix + ! NOTE: The derivatives were computed in the previous call to computFlux + ! This occurred either at the call to eval8summaFida at the start of sysSolveFida + ! or in the call to eval8summaFida in the previous iteration + dt1 = 1.0 + if(heatCapVaries)then + call computJacobFidaCpVar(& + ! input: model control + cj, & ! intent(in) + dt1, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + (ixGroundwater==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow + ixMatrix, & ! intent(in): form of the Jacobian matrix + specificStorage, & ! intent(in): specific storage coefficient (m-1) + theta_sat, & ! intent(in): soil porosity (-) + ixRichards, & ! intent(in): choice of option for Richards' equation + ! input: data structures + indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables + dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! input: state variables + mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) + mLayerTempPrime, & ! intent(in) + mLayerMatricHeadPrime, & ! intent(in) + mLayerMatricHeadLiqPrime, & ! intent(in) + mLayerVolFracWatTrial, & ! intent(in) + mLayerVolFracWatPrime, & ! intent(in) + scalarCanopyTempTrial, & ! intent(in) + scalarCanopyTempPrime, & ! intent(in) derivative value for temperature of the vegetation canopy (K) + scalarCanopyWatPrime, & ! intetn(in) + mLayerd2Theta_dTk2, & ! intent(in) + d2VolTot_d2Psi0, & ! intent(in) + dFracLiqSnow_dTk, & ! intent(in) + d2Theta_dTkCanopy2, & ! intent(in) + dFracLiqVeg_dTkCanopy, & ! intent(in) + ! input-output: Jacobian and its diagonal + dMat, & ! intent(inout): diagonal of the Jacobian matrix + Jac, & ! intent(out): Jacobian matrix + ! output: error control + err,cmessage) ! intent(out): error code and error message + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + else + call computJacobFida(& + ! input: model control + cj, & ! intent(in) + dt1, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + (ixGroundwater==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow + ixMatrix, & ! intent(in): form of the Jacobian matrix + specificStorage, & ! intent(in): specific storage coefficient (m-1) + theta_sat, & ! intent(in): soil porosity (-) + ixRichards, & ! intent(in): choice of option for Richards' equation + ! input: data structures + indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables + dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! input: state variables + mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) + mLayerTempPrime, & ! intent(in) + mLayerMatricHeadPrime, & ! intent(in) + mLayerMatricHeadLiqPrime, & ! intent(in) + mLayerVolFracWatPrime, & ! intent(in) + scalarCanopyTempTrial, & ! intent(in) + scalarCanopyTempPrime, & ! intent(in) derivative value for temperature of the vegetation canopy (K) + scalarCanopyWatPrime, & ! intetn(in) + mLayerd2Theta_dTk2, & ! intent(in) + d2VolTot_d2Psi0, & ! intent(in) + dFracLiqSnow_dTk, & ! intent(in) + d2Theta_dTkCanopy2, & ! intent(in) + dFracLiqVeg_dTkCanopy, & ! intent(in) + ! input-output: Jacobian and its diagonal + dMat, & ! intent(inout): diagonal of the Jacobian matrix + Jac, & ! intent(out): Jacobian matrix + ! output: error control + err,cmessage) ! intent(out): error code and error message + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + endif + + + + ! end association with the information in the data structures + end associate + + + end subroutine eval8FidaJac +end module eval8FidaJac_module diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 new file mode 100644 index 000000000..9b62f1c91 --- /dev/null +++ b/build/source/engine/eval8summaFida.f90 @@ -0,0 +1,729 @@ + +module eval8summaFida_module + +! data types +USE nrtype + +! access missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing double precision number +USE globalData,only:quadMissing ! missing quadruple precision number + +! access the global print flag +USE globalData,only:globalPrintFlag + +! define access to state variables to print +USE globalData,only: iJac1 ! first layer of the Jacobian to print +USE globalData,only: iJac2 ! last layer of the Jacobian to print + +! domain types +USE globalData,only:iname_veg ! named variables for vegetation +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil + +! named variables to describe the state variable type +USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space +USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy +USE globalData,only:iname_watCanopy ! named variable defining the mass of water on the vegetation canopy +USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers +USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers +USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers +USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers +USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers +USE globalData,only:model_decisions ! model decision structure + + +! constants +USE multiconst,only:& + Tfreeze, & ! temperature at freezing (K) + LH_fus, & ! latent heat of fusion (J kg-1) + LH_vap, & ! latent heat of vaporization (J kg-1) + LH_sub, & ! latent heat of sublimation (J kg-1) + Cp_air, & ! specific heat of air (J kg-1 K-1) + iden_air, & ! intrinsic density of air (kg m-3) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water ! intrinsic density of liquid water (kg m-3) + +! provide access to the derived types to define the data structures +USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (dp) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (dp) + zlookup, & + model_options ! defines the model decisions + +! indices that define elements of the data structures +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure +USE var_lookup,only:iLookPARAM ! named variables for structure elements +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookFLUX ! named variables for structure elements +USE var_lookup,only:iLookDERIV ! named variables for structure elements + +! look-up values for the choice of groundwater representation (local-column, or single-basin) +USE mDecisions_module,only: & + localColumn, & ! separate groundwater representation in each local soil column + singleBasin ! single groundwater store over the entire basin + +! look-up values for the choice of groundwater parameterization +USE mDecisions_module,only: & + qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization + bigBucket, & ! a big bucket (lumped aquifer model) + noExplicit ! no explicit groundwater parameterization + +! look-up values for the form of Richards' equation +USE mDecisions_module,only: & + moisture, & ! moisture-based form of Richards' equation + mixdform ! mixed form of Richards' equation + +implicit none +private +public::eval8summaFida + +contains + + ! ********************************************************************************************************** + ! public subroutine eval8summaFida: compute the residual vector and the Jacobian matrix + ! ********************************************************************************************************** + subroutine eval8summaFida(& + ! input: model control + dt_cur, & + dt, & ! intent(in): time step + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + nState, & ! intent(in): total number of state variables + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + heatCapVaries, & ! intent(in): flag to indicate if heat capacity is constant in the current subset + ! input: state vectors + stateVec, & ! intent(in): model state vector + stateVecPrime, & ! intent(in): derivative of model state vector + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in) + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + indx_data, & ! intent(inout): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + ! output: flux and residual vectors + mLayerMatricHeadLiqTrial, & !intent(inout) + mLayerMatricHeadLiqPrev, & !intent(in) + mLayerMatricHeadTrial, & !intent(inout) + mLayerMatricHeadPrev, & !intent(in) + mLayerVolFracWatTrial, & !intent(inout) + mLayerVolFracWatPrev, & !intent(inout) + mLayerVolFracIceTrial, & !intent(inout) + mLayerVolFracIcePrev, & !intent(inout) + mLayerHeatCapPrev, & + mLayerHeatCapTrial, & + mLayerEnthalpyPrev, & ! intent(in) + mLayerEnthalpyTrial, & ! intent(out) + feasible, & ! intent(out): flag to denote the feasibility of the solution + fluxVec, & ! intent(out): flux vector + resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + resVec, & ! intent(out): residual vector + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- + ! provide access to subroutines + USE varExtrFida_module, only:varExtract ! extract variables from the state vector + USE varExtrFida_module, only:varExtractFida + USE updateVarsFida_module, only:updateVarsFida ! update prognostic variables + USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy + USE soilCmpresFida_module, only:soilCmpresFida ! compute soil compression + USE computFlux_module, only:computFlux ! compute fluxes given a state vector + USE computHeatCap_module,only:computHeatCap ! compute diagnostic energy variables -- thermal conductivity and heat capacity + USE computHeatCap_module, only:computStatMult + USE computResidFida_module,only:computResidFida ! compute residuals given a state vector + USE computThermConduct_module,only:computThermConduct + USE computEnthalpy_module,only:computEnthalpy + USE computEnthalpy_module,only:computEnthalpyPrime + implicit none + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + ! input: model control + real(dp),intent(in) :: dt_cur + real(dp),intent(in) :: dt ! time step + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + integer,intent(in) :: nState ! total number of state variables + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + logical(lgt),intent(in) :: heatCapVaries + ! input: state vectors + real(dp),intent(in) :: stateVec(:) ! model state vector + real(dp),intent(in) :: stateVecPrime(:) ! model state vector + real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + ! input: data structures + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_i), intent(in) :: type_data ! type of vegetation and soil + type(var_d), intent(in) :: attr_data ! spatial attributes + type(var_dlength), intent(in) :: mpar_data ! model parameters + type(var_d), intent(in) :: forc_data ! model forcing data + type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin + type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU + ! output: data structures + type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + real(dp),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + ! output: flux and residual vectors + real(dp),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) + real(dp),intent(in) :: mLayerMatricHeadLiqPrev(:) + real(dp),intent(inout) :: mLayerMatricHeadTrial(:) ! trial value for liquid water matric potential (m) + real(dp),intent(in) :: mLayerMatricHeadPrev(:) + real(dp),intent(inout) :: mLayerVolFracWatTrial(:) + real(dp),intent(in) :: mLayerVolFracWatPrev(:) + real(dp),intent(inout) :: mLayerVolFracIceTrial(:) + real(dp),intent(in) :: mLayerVolFracIcePrev(:) + real(dp),intent(in) :: mLayerHeatCapPrev(:) + real(dp),intent(inout) :: mLayerHeatCapTrial(:) + real(dp),intent(in) :: mLayerEnthalpyPrev(:) + real(dp),intent(out) :: mLayerEnthalpyTrial(:) + logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution + real(dp),intent(out) :: fluxVec(:) ! flux vector + real(dp),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation + real(qp),intent(out) :: resVec(:) ! NOTE: qp ! residual vector + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + ! -------------------------------------------------------------------------------------------------------------------------------- + ! state variables + real(dp) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(dp) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(dp) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(dp),dimension(nLayers) :: mLayerTempTrial ! trial value for temperature of layers in the snow and soil domains (K) + real(dp) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + ! diagnostic variables + real(dp) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(dp) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(dp),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) + + ! derivative of state variables + real(dp) :: scalarCanairTempPrime ! derivative value for temperature of the canopy air space (K) + real(dp) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) + real(dp) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) + real(dp),dimension(nLayers) :: mLayerTempPrime ! derivative value for temperature of layers in the snow and soil domains (K) + real(dp),dimension(nLayers) :: mLayerVolFracWatPrime ! derivative value for volumetric fraction of total water (-) + real(dp),dimension(nSoil) :: mLayerMatricHeadPrime ! derivative value for total water matric potential (m) + real(dp),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! derivative value for liquid water matric potential (m) + real(dp) :: scalarAquiferStoragePrime ! derivative value of storage of water in the aquifer (m) + ! derivative of diagnostic variables + real(dp) :: scalarCanopyLiqPrime ! derivative value for mass of liquid water on the vegetation canopy (kg m-2) + real(dp) :: scalarCanopyIcePrime ! derivative value for mass of ice on the vegetation canopy (kg m-2) + real(dp),dimension(nLayers) :: mLayerVolFracLiqPrime ! derivative value for volumetric fraction of liquid water (-) + real(dp),dimension(nLayers) :: mLayerVolFracIcePrime ! derivative value for volumetric fraction of ice (-) + ! enthalpy + real(dp) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) + real(dp) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) + real(dp),dimension(nLayers) :: mLayerEnthalpyPrime ! enthalpy of each snow+soil layer (J m-3) + ! other local variables + integer(i4b) :: iLayer ! index of model layer in the snow+soil domain + integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain + integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine + integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) + real(dp) :: xMin,xMax ! minimum and maximum values for water content + real(dp),parameter :: canopyTempMax=500._dp ! expected maximum value for the canopy temperature (K) + real(dp),dimension(nState) :: rVecScaled ! scaled residual vector + character(LEN=256) :: cmessage ! error message of downwind routine + real(qp) :: heatCapVeg + real(qp) :: heatCapVegPrime + real(qp),dimension(nLayers) :: mLayerHeatCapPrime + integer(i4b),parameter :: analytical=1 + integer(i4b),parameter :: lookup_tab=2 + integer(i4b),parameter :: analytic_numeric=3 + integer(i4b) :: enthalpy=lookup_tab ! analytical or lookup_tab or analytic_numeric + + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! association to variables in the data structures + ! -------------------------------------------------------------------------------------------------------------------------------- + associate(& + ! model decisions + ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ! soil parameters + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) + ! canopy and layer depth + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! model state variables + scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + ! soil compression + scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in storage associated with compression of the soil matrix (-) + ! derivatives + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) + ! mapping + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) + ! indices + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable (nrg) + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable (nrg) + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain + layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) + ) ! association to variables in the data structures + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="eval8summaFida/" + + ! check the feasibility of the solution + feasible=.true. + + ! check that the canopy air space temperature is reasonable + if(ixCasNrg/=integerMissing)then + if(stateVec(ixCasNrg) > canopyTempMax) feasible=.false. + endif + + ! check that the canopy air space temperature is reasonable + if(ixVegNrg/=integerMissing)then + if(stateVec(ixVegNrg) > canopyTempMax) feasible=.false. + endif + + ! check canopy liquid water is not negative + if(ixVegHyd/=integerMissing)then + if(stateVec(ixVegHyd) < 0._dp) feasible=.false. + end if + + ! check snow temperature is below freezing + if(count(ixSnowOnlyNrg/=integerMissing)>0)then + if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. + endif + +! print *, 'Tfreeze = ', Tfreeze +! print *, 'stateVec = ', stateVec(1:nLayers) +! print *, '===========================================================' + + ! loop through non-missing hydrology state variables in the snow+soil domain + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) + + ! check the minimum and maximum water constraints + if(ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_liqLayer)then + + ! --> minimum + if (layerType(iLayer) == iname_soil) then + xMin = theta_sat(iLayer-nSnow) + else + xMin = 0._dp + endif + + ! --> maximum + select case( layerType(iLayer) ) + case(iname_snow); xMax = merge(iden_ice, 1._dp - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) + case(iname_soil); xMax = merge(theta_sat(iLayer-nSnow), theta_sat(iLayer-nSnow) - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) + end select + + ! --> check + if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. + ! if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax + + endif ! if water states + + end do ! loop through non-missing hydrology state variables in the snow+soil domain + + ! early return for non-feasible solutions + if(.not.feasible)then + fluxVec(:) = realMissing + resVec(:) = quadMissing + return + end if + + ! get the start and end indices for the soil compression calculations + if(scalarSolution)then + jState = pack(ixControlVolume, ixMapFull2Subset/=integerMissing) + ixBeg = jState(1) + ixEnd = jState(1) + else + ixBeg = 1 + ixEnd = nSoil + endif + + + + ! extract variables from the model state vector + call varExtract(& + ! input + stateVec, & ! intent(in): model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) + mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) + ! output: variables for the aquifer + scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + + + + call varExtractFida(& + ! input + stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy + scalarCanairTempPrime, & ! intent(out): derivative of canopy air temperature (K) + scalarCanopyTempPrime, & ! intent(out): derivative of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(out): derivative of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(out): derivative of canopy liquid water (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempPrime, & ! intent(out): derivative of layer temperature (K) + mLayerVolFracWatPrime, & ! intent(out): derivative of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(out): derivative of volumetric liquid water content (-) + mLayerMatricHeadPrime, & ! intent(out): derivative of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(out): derivative of liquid water matric potential (m) + ! output: variables for the aquifer + scalarAquiferStoragePrime,& ! intent(out): derivative of storage of water in the aquifer (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + + + + call updateVarsFida(& + ! input + dt_cur, & + .false., & ! intent(in): logical flag to adjust temperature to account for the energy + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + mLayerVolFracWatPrev, & ! intent(in) + mLayerMatricHeadPrev, & ! intent(in) + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output: variables for the vegetation canopy + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempPrime, & ! + mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) + mLayerVolFracIcePrime, & ! + mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + + ! print the water content + if(globalPrintFlag)then + if(iJac1 FN_VGetArrayPointer(sunvec_y) + stateVecPrime => FN_VGetArrayPointer(sunvec_yp) + rVec => FN_VGetArrayPointer(sunvec_r) + + retval = FIDAGetCurrentStep(eqns_data%ida_mem, stepsize_next) + if (retval /= 0) then + print *, 'Error in FIDAGetCurrentStep, retval = ', retval, '; halting' + stop 1 + end if + + + + ! compute the flux and the residual vector for a given state vector + call eval8summaFida(& + ! input: model control + stepsize_next(1), & + eqns_data%dt, & + eqns_data%nSnow, & ! intent(in): number of snow layers + eqns_data%nSoil, & ! intent(in): number of soil layers + eqns_data%nLayers, & ! intent(in): number of layers + eqns_data%nState, & ! intent(in): number of state variables in the current subset + eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + eqns_data%firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution + eqns_data%heatCapVaries, & ! intent(in): flag to indicate if heat capacity is constant in the current subset + ! input: state vectors + stateVec, & ! intent(in): model state vector + stateVecPrime, & ! intent(in): model state vector + eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + eqns_data%lookup_data, & + eqns_data%type_data, & ! intent(in): type of vegetation and soil + eqns_data%attr_data, & ! intent(in): spatial attributes + eqns_data%mpar_data, & ! intent(in): model parameters + eqns_data%forc_data, & ! intent(in): model forcing data + eqns_data%bvar_data, & ! intent(in): average model variables for the entire basin + eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + eqns_data%indx_data, & ! intent(inou): index data + eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU + eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) + eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian + eqns_data%mLayerMatricHeadLiqTrial, & + eqns_data%mLayerMatricHeadLiqPrev, & + eqns_data%mLayerMatricHeadTrial, & + eqns_data%mLayerMatricHeadPrev, & + eqns_data%mLayerVolFracWatTrial, & + eqns_data%mLayerVolFracWatPrev, & + eqns_data%mLayerVolFracIceTrial, & + eqns_data%mLayerVolFracIcePrev, & + eqns_data%mLayerHeatCapPrev, & + eqns_data%mLayerHeatCapTrial, & + eqns_data%mLayerEnthalpyPrev, & ! intent(in) + eqns_data%mLayerEnthalpyTrial, & ! intent(out) + ! output + feasible, & ! intent(out): flag to denote the feasibility of the solution + eqns_data%fluxVec, & ! intent(out): flux vector + eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec, & ! intent(out): residual vector + eqns_data%err,eqns_data%message) ! intent(out): error control + + if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif + if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif + if(.not.feasible)then; eqns_data%message=trim(eqns_data%message)//'state vector not feasible'; ierr = 1; return; endif + + ! return success + ierr = 0 + return + + end function evalEqnsFida + + +end module evalEqnsFida_module diff --git a/build/source/engine/evalJacFida.f90 b/build/source/engine/evalJacFida.f90 new file mode 100644 index 000000000..c2da52ca6 --- /dev/null +++ b/build/source/engine/evalJacFida.f90 @@ -0,0 +1,132 @@ + + +module evalJacFida_module + + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use nrtype + use fida_datatypes + USE globalData,only:model_decisions ! model decision structure + ! provide access to the derived types to define the data structures + USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (dp) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (dp) + model_options ! defines the model decisions + + + + ! privacy + implicit none + private + public::evalJacFida + + +contains + + ! ********************************************************************************************************** + ! public function evalJacFida: compute the residual vector F(t,y,y') required for FIDA solver + ! ********************************************************************************************************** + ! Return values: + ! 0 = success, + ! 1 = recoverable error, + ! -1 = non-recoverable error + ! ---------------------------------------------------------------- + integer(c_int) function evalJacFida(t, cj, sunvec_y, sunvec_yp, sunvec_r, & + sunmat_J, user_data, sunvec_temp1, sunvec_temp2, sunvec_temp3) & + result(ierr) bind(C,name='evalJacFida') + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use fsundials_nvector_mod + use fsundials_matrix_mod + use fnvector_serial_mod + use fsunmatrix_dense_mod + use nrtype + use fida_datatypes + use eval8FidaJac_module,only:eval8FidaJac + !======= Declarations ========= + implicit none + + ! calling variables + real(dp), value :: t ! current time + real(dp), value :: cj ! step size scaling factor + type(N_Vector) :: sunvec_y ! solution N_Vector + type(N_Vector) :: sunvec_yp ! derivative N_Vector + type(N_Vector) :: sunvec_r ! residual N_Vector + type(SUNMatrix) :: sunmat_J ! Jacobian SUNMatrix + type(c_ptr), value :: user_data ! user-defined data + type(N_Vector) :: sunvec_temp1 ! temporary N_Vectors + type(N_Vector) :: sunvec_temp2 + type(N_Vector) :: sunvec_temp3 + + ! pointers to data in SUNDIALS vectors + real(dp), pointer :: stateVec(:) + real(dp), pointer :: stateVecPrime(:) + real(dp), pointer :: rVec(:) + real(dp), pointer :: Jac(:,:) + type(eqnsData), pointer :: eqns_data ! equations data + + + + !======= Internals ============ + + ! get equations data from user-defined data + call c_f_pointer(user_data, eqns_data) + + + ! get data arrays from SUNDIALS vectors + stateVec => FN_VGetArrayPointer(sunvec_y) + stateVecPrime => FN_VGetArrayPointer(sunvec_yp) + rVec => FN_VGetArrayPointer(sunvec_r) + Jac(1:eqns_data%nState, 1:eqns_data%nState) => FSUNDenseMatrix_Data(sunmat_J) + + + + ! compute the flux and the residual vector for a given state vector + ! NOTE 1: The derivatives computed in eval8summaFida are used to calculate the Jacobian matrix for the first iteration + ! NOTE 2: The Jacobian matrix together with the residual vector is used to calculate the first iteration increment + call eval8FidaJac(& + ! input: model control + cj, & ! intent(in) + eqns_data%dt, & ! intent(in) + eqns_data%nSnow, & ! intent(in): number of snow layers + eqns_data%nSoil, & ! intent(in): number of soil layers + eqns_data%nLayers, & ! intent(in): number of layers + eqns_data%ixMatrix, & ! intent(in) + eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution + eqns_data%heatCapVaries, & ! intent(in): flag to indicate if heat capacity varies in the current substep + ! input: state vectors + stateVec, & ! intent(in): model state vector + stateVecPrime, & ! intent(in): model state vector + eqns_data%sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + eqns_data%mpar_data, & ! intent(in): model parameters + eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + eqns_data%indx_data, & ! intent(inou): index data + eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU + eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input: baseflow + eqns_data%dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! output + eqns_data%dMat, & ! intetn(inout) + Jac, & ! intent(out): jacobain matrix + eqns_data%err,eqns_data%message) ! intent(out): error control + + if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif + if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif + ! return success + ierr = 0 + return + + + + end function evalJacFida + + +end module evalJacFida_module diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 new file mode 100644 index 000000000..bce6c966c --- /dev/null +++ b/build/source/engine/fidaSolver.f90 @@ -0,0 +1,840 @@ + + + +module fidaSolver_module + + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use nrtype + use fida_datatypes + +! access the global print flag +USE globalData,only:globalPrintFlag + +! access missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing double precision number +USE globalData,only:quadMissing ! missing quadruple precision number + +! access matrix information +USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix +USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix +USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix +USE globalData,only: iJac1 ! first layer of the Jacobian to print +USE globalData,only: iJac2 ! last layer of the Jacobian to print +USE globalData,only: ku ! number of super-diagonal bands +USE globalData,only: kl ! number of sub-diagonal bands + +! domain types +USE globalData,only:iname_veg ! named variables for vegetation +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil + +! state variable type +USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space +USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy +USE globalData,only:iname_watCanopy ! named variable defining the mass of total water on the vegetation canopy +USE globalData,only:iname_liqCanopy ! named variable defining the mass of liquid water on the vegetation canopy +USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers +USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers +USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers +USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers +USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers +USE globalData,only:model_decisions ! model decision structure + +! global metadata +USE globalData,only:flux_meta ! metadata on the model fluxes +USE globalData,only:diag_meta ! metadata on the model diagnostic variables +USE globalData,only:prog_meta ! metadata on the model prognostic variables +USE globalData,only:deriv_meta ! metadata on the model derivatives + +! constants +USE multiconst,only:& + LH_fus, & ! latent heat of fusion (J K-1) + Tfreeze, & ! temperature at freezing (K) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water ! intrinsic density of liquid water (kg m-3) + +! provide access to indices that define elements of the data structures +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookFLUX ! named variables for structure elements +USE var_lookup,only:iLookFORCE ! named variables for structure elements +USE var_lookup,only:iLookPARAM ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure + USE var_lookup,only:iLookDERIV ! named variables for structure elements + +! provide access to the derived types to define the data structures +USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (dp) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (dp) + zLookup, & + model_options ! defines the model decisions + +! look-up values for the choice of groundwater representation (local-column, or single-basin) +USE mDecisions_module,only: & + localColumn, & ! separate groundwater representation in each local soil column + singleBasin ! single groundwater store over the entire basin + +! look-up values for the choice of groundwater parameterization +USE mDecisions_module,only: & + qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization + bigBucket, & ! a big bucket (lumped aquifer model) + noExplicit ! no explicit groundwater parameterization + + + + ! privacy + implicit none + private + public::fidaSolver + + +contains + + !------------------- + ! * subroutine fidaSolver: solve F(y,y') = 0 by FIDA. Here, y is the state vector + ! ------------------ + + subroutine fidaSolver( & + dt, & ! end of the current time step + h_init, & ! intent(in) initial stepsize + atol, & ! absolute telerance + rtol, & ! relative tolerance + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + nState, & ! intent(in): total number of state variables + ixMatrix, & ! intent(in): type of matrix (dense or banded) + ixQuadrature, & ! intent(in): type of quadrature method for approximating average flux + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + heatCapVaries, & ! intent(in): flag to indicate if heat capacity is constant in the current substep + ! input: state vectors + stateVecInit, & ! intent(in): initial state vector + fScale, & ! intent(in): function scaling vector + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + dMat, & ! intent(inout) + numDiscon, & ! intent(in) + ! input: data structures + ! model_decisions, & ! intent(in): model decisions + lookup_data, & + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + indx_data, & ! intent(in): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_temp, & ! intent(inout) + flux_data, & ! intent(inout): model fluxes for a local HRU + flux_sum, & ! intent(inout) + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + mLayerCmpress_sum, & + ! output + tret, & ! time which the solution is returned, if successfull tret = tend + dt_last, & + t_last, & + stepsize_past, & + stateVec, & ! intent(out): model state vector + stateVecPrime, & ! intent(out): derivative of model state vector + fluxVec, & ! intent(out): flux vector + resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec, & + err,message & ! intent(out): error control + ) + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use nrtype + use fida_datatypes + use evalEqnsFida_module,only:evalEqnsFida + use allocspace_module,only:allocLocal ! allocate local data structures + + + use fida_mod ! Fortran interface to IDA + use fnvector_serial_mod ! Fortran interface to serial N_Vector + use fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix + use fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver + use fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix + use fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver + use fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver + use fsundials_matrix_mod ! Fortran interface to generic SUNMatrix + use fsundials_nvector_mod ! Fortran interface to generic N_Vector + use fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver + use fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver + use evalEqnsFida_module,only:evalEqnsFida ! ODE functions + use evalJacFida_module,only:evalJacFida + use tolFida_module,only:computWeightFida + use convTestFida_module,only:convTestFida + use eval8summaFida_module,only:eval8summaFida + USE computEnthalpy_module,only:computEnthalpy +! use findDiscontinuity_module,only:findDiscontinuity + + !======= Declarations ========= + implicit none + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! calling variables + ! -------------------------------------------------------------------------------------------------------------------------------- + ! input: model control + real(qp),intent(in) :: dt + real(qp),intent(in) :: h_init + real(qp),intent(inout) :: atol(:) + real(qp),intent(inout) :: rtol(:) + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + integer(c_long),intent(in) :: nState ! total number of state variables + integer(i4b) :: ixMatrix ! form of matrix (dense or banded) + integer(i4b) :: ixQuadrature ! type of quadrature method for approximating average flux + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + logical(lgt),intent(in) :: heatCapVaries !flag to indicate if heat capacity is constant in the current substep + ! input: state vectors + real(dp),intent(in) :: stateVecInit(:) ! model state vector + real(dp),intent(in) :: fScale(:) ! function scaling vector + real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + real(dp), intent(inout) :: dMat(:) + integer(i4b), intent(in) :: numDiscon + ! input: data structures +! type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_i), intent(in) :: type_data ! type of vegetation and soil + type(var_d), intent(in) :: attr_data ! spatial attributes + type(var_dlength), intent(in) :: mpar_data ! model parameters + type(var_d), intent(in) :: forc_data ! model forcing data + type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin + type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU + ! output: data structures + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_temp ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: flux_sum + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + real(dp),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + real(dp),intent(inout) :: mLayerCmpress_sum(:) + ! output: flux and residual vectors + real(dp),intent(inout) :: stateVec(:) ! model state vector + real(dp),intent(inout) :: stateVecPrime(:) ! model state vector + real(dp),intent(out) :: fluxVec(:) ! flux vector + real(dp),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation + real(dp),intent(out) :: rVec(:) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + real(qp),intent(out) :: tret(1) + real(qp),intent(out) :: dt_last(1) + real(qp),intent(out) :: t_last(1) + real(qp),intent(out) :: stepsize_past + logical(lgt) :: scalling_on + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + ! -------------------------------------------------------------------------------------------------------------------------------- + type(N_Vector), pointer :: sunvec_y ! sundials solution vector + type(N_Vector), pointer :: sunvec_yp ! sundials derivative vector + type(N_Vector), pointer :: sunvec_av ! sundials tolerance vector + type(SUNMatrix), pointer :: sunmat_A ! sundials matrix + type(SUNLinearSolver), pointer :: sunlinsol_LS ! sundials linear solver + type(SUNNonLinearSolver), pointer :: sunnonlin_NLS ! sundials nonlinear solver + type(c_ptr) :: ida_mem ! IDA memory + type(eqnsData), target :: eqns_data + integer(i4b) :: retval + logical(lgt) :: feasible ! feasibility flag + real(qp) :: t0 + integer (kind = 8) :: maxstep + integer(kind = 8) :: mu, lu + real(qp) :: h_max + real(qp) :: coef_nonlin + integer(i4b) :: lsflag + integer(i4b) :: iLayer ! index of layer in the snow+soil domain + integer(i4b) :: iState ! index of model state + real(dp) :: idenIW + real(c_double) :: stepsize_cur(1) + integer(i4b),parameter :: ixRectangular=1 ! reza: should put them in a data type later + integer(i4b),parameter :: ixTrapezoidal=2 + integer(i4b) :: iVar + logical(lgt) :: startQuadrature + globalVars: associate(& + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat & ! intent(in): + ) + + + + + !======= Internals ============ + + + + ! fill eqns_data which will be required later to call eval8summaFida + eqns_data%dt = dt + eqns_data%nSnow = nSnow + eqns_data%nSoil = nSoil + eqns_data%nLayers = nLayers + eqns_data%nState = nState + eqns_data%ixMatrix = ixMatrix + eqns_data%ixQuadrature = ixQuadrature + eqns_data%firstSubStep = firstSubStep + eqns_data%firstFluxCall = firstFluxCall + eqns_data%firstSplitOper = firstSplitOper + eqns_data%computeVegFlux = computeVegFlux + eqns_data%scalarSolution = scalarSolution + eqns_data%heatCapVaries = heatCapVaries + + allocate( eqns_data%atol(nState) ) + eqns_data%atol = atol + + allocate( eqns_data%rtol(nState) ) + eqns_data%rtol = rtol + + allocate( eqns_data%fScale(nState) ) + eqns_data%fScale = fScale + + + allocate( eqns_data%sMul(nState) ) + eqns_data%sMul = sMul + + allocate( eqns_data%dMat(nState) ) + eqns_data%dMat = dMat + + ! allocate space for the temporary prognostic variable structure + call allocLocal(prog_meta(:),eqns_data%prog_data,nSnow,nSoil,err,message) + if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif + eqns_data%prog_data = prog_data + + ! allocate space for the temporary diagnostic variable structure + call allocLocal(diag_meta(:),eqns_data%diag_data,nSnow,nSoil,err,message) + if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif + eqns_data%diag_data = diag_data + + ! allocate space for the temporary flux variable structure + call allocLocal(flux_meta(:),eqns_data%flux_temp,nSnow,nSoil,err,message) + if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif + eqns_data%flux_temp = flux_temp + + ! allocate space for the temporary flux variable structure + call allocLocal(flux_meta(:),eqns_data%flux_data,nSnow,nSoil,err,message) + if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif + eqns_data%flux_data = flux_data + + ! allocate space for the temporary flux variable structure + call allocLocal(flux_meta(:),eqns_data%flux_sum,nSnow,nSoil,err,message) + if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif + eqns_data%flux_sum = flux_sum + + ! allocate space for the derivative structure + call allocLocal(deriv_meta(:),eqns_data%deriv_data,nSnow,nSoil,err,message) + if(err/=0)then; err=20; message=trim(message)//trim(message); return; end if + eqns_data%deriv_data = deriv_data + + eqns_data%lookup_data = lookup_data + eqns_data%type_data = type_data + eqns_data%attr_data = attr_data + eqns_data%mpar_data = mpar_data + eqns_data%forc_data = forc_data + eqns_data%bvar_data = bvar_data + eqns_data%indx_data = indx_data + eqns_data%ixSaturation = ixSaturation + + ! allocate space for the baseflow derivatives + ! NOTE: needs allocation because only used when baseflow sinks are active + if(model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel)then + allocate(eqns_data%dBaseflow_dMatric(nSoil,nSoil),stat=err) ! baseflow depends on total storage in the soil column + else + allocate(eqns_data%dBaseflow_dMatric(0,0),stat=err) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix + end if + + eqns_data%dBaseflow_dMatric = dBaseflow_dMatric + + allocate( eqns_data%mLayerCmpress_sum(nSoil) ) + eqns_data%mLayerCmpress_sum = mLayerCmpress_sum + + allocate( eqns_data%mLayerMatricHeadLiqTrial(nSoil) ) + allocate( eqns_data%mLayerMatricHeadLiqPrev(nSoil) ) + + allocate( eqns_data%mLayerMatricHeadTrial(nSoil) ) + allocate( eqns_data%mLayerMatricHeadPrev(nSoil) ) + + allocate( eqns_data%mLayerVolFracWatTrial(nLayers) ) + allocate( eqns_data%mLayerVolFracWatPrev(nLayers) ) + + allocate( eqns_data%mLayerVolFracIceTrial(nLayers) ) + allocate( eqns_data%mLayerVolFracIcePrev(nLayers) ) + + allocate( eqns_data%mLayerHeatCapTrial(nLayers) ) + allocate( eqns_data%mLayerHeatCapPrev(nLayers) ) + + allocate( eqns_data%mLayerEnthalpyTrial(nLayers) ) + allocate( eqns_data%mLayerEnthalpyPrev(nLayers) ) + + allocate( eqns_data%fluxVec(nState) ) + eqns_data%fluxVec = fluxVec + + allocate( eqns_data%resSink(nState) ) + eqns_data%resSink = resSink + + + allocate( eqns_data%resVec(nState) ) + + eqns_data%err = err + eqns_data%message = message + eqns_data%startQuadrature = .true. + + + + ! create serial vectors + sunvec_y => FN_VMake_Serial(nState, stateVec) + if (.not. associated(sunvec_y)) then + print *, 'ERROR: sunvec = NULL' + stop 1 + end if + + sunvec_yp => FN_VMake_Serial(nState, stateVecPrime) + if (.not. associated(sunvec_yp)) then + print *, 'ERROR: sunvec = NULL' + stop 1 + end if + + ! Initialize solution vectors reza: we should provide and pass stateVecPrimeInit later + call setInitialCondition(nState, stateVecInit, sunvec_y, sunvec_yp) + + + ! Call FIDACreate and FIDAInit to initialize IDA memory + ida_mem = FIDACreate() + if (.not. c_associated(ida_mem)) then + print *, 'ERROR: ida_mem = NULL' + stop 1 + end if + + eqns_data%ida_mem = ida_mem + + + retval = FIDASetUserData(ida_mem, c_loc(eqns_data)) + if (retval /= 0) then + print *, 'Error in FIDASetUserData, retval = ', retval, '; halting' + stop 1 + end if + + t0 = 0._dp + retval = FIDAInit(ida_mem, c_funloc(evalEqnsFida), t0, sunvec_y, sunvec_yp) + if (retval /= 0) then + print *, 'Error in FIDAInit, retval = ', retval, '; halting' + stop 1 + end if + + ! set tolerances + retval = FIDAWFtolerances(ida_mem, c_funloc(computWeightFida)) + if (retval /= 0) then + print *, 'Error in FIDAWFtolerances, retval = ', retval, '; halting' + stop 1 + end if + + ! specify the root function +! retval = FIDARootInit(ida_mem, numDiscon, c_funloc(findDiscontinuity)) +! if (retval /= 0) then +! print *, 'Error in FIDARootInit, retval = ', retval, '; halting' +! stop 1 +! end if + + ! define the form of the matrix + select case(ixMatrix) + case(ixBandMatrix) + mu = ku; lu = kl; + ! Create banded SUNMatrix for use in linear solves + sunmat_A => FSUNBandMatrix(nState, mu, lu) + if (.not. associated(sunmat_A)) then + print *, 'ERROR: sunmat = NULL' + stop 1 + end if + + ! Create banded SUNLinearSolver object + sunlinsol_LS => FSUNLinSol_Band(sunvec_y, sunmat_A) + if (.not. associated(sunlinsol_LS)) then + print *, 'ERROR: sunlinsol = NULL' + stop 1 + end if + + case(ixFullMatrix) + ! Create dense SUNMatrix for use in linear solves + sunmat_A => FSUNDenseMatrix(nState, nState) + if (.not. associated(sunmat_A)) then + print *, 'ERROR: sunmat = NULL' + stop 1 + end if + + ! Create dense SUNLinearSolver object + sunlinsol_LS => FSUNDenseLinearSolver(sunvec_y, sunmat_A) + if (.not. associated(sunlinsol_LS)) then + print *, 'ERROR: sunlinsol = NULL' + stop 1 + end if + + ! check + case default; print *, 'unable to identify option for the type of matrix'; stop 1 + + end select ! form of matrix + + ! Attach the matrix and linear solver + retval = FIDASetLinearSolver(ida_mem, sunlinsol_LS, sunmat_A); + if (retval /= 0) then + print *, 'Error in FIDASetLinearSolver, retval = ', retval, '; halting' + stop 1 + end if + + + + if(ixMatrix == ixFullMatrix)then + ! Set the user-supplied Jacobian routine + retval = FIDASetJacFn(ida_mem, c_funloc(evalJacFida)) + if (retval /= 0) then + print *, 'Error in FIDASetJacFn, retval = ', retval, '; halting' + stop 1 + end if + + endif + + ! Create Newton SUNNonlinearSolver object. IDA uses a + ! Newton SUNNonlinearSolver by default, so it is not necessary + ! to create it and attach it. It is done here for demonstration purposes. + sunnonlin_NLS => FSUNNonlinSol_Newton(sunvec_y) + if (.not. associated(sunnonlin_NLS)) then + print *, 'ERROR: sunnonlinsol = NULL' + stop 1 + end if + + + ! Attach the nonlinear solver + retval = FIDASetNonlinearSolver(ida_mem, sunnonlin_NLS) + if (retval /= 0) then + print *, 'Error in FIDASetNonlinearSolver, retval = ', retval, '; halting' + stop 1 + end if + +! Set the user-supplied nonlinear solver convergence test function +! retval = FSUNNonlinSolSetConvTestFn(sunnonlin_NLS, c_funloc(convTestFida), c_loc(eqns_data)); +! if (retval /= 0) then +! print *, 'Error in FSUNNonlinSolSetConvTestFn, retval = ', retval, '; halting' +! stop 1 +! end if + + + ! Set the maximum BDF order, default = 5 + retval = FIDASetMaxOrd(ida_mem, 1) + if (retval /= 0) then + print *, 'Error in FIDASetMaxOrd, retval = ', retval, '; halting' + stop 1 + end if + + + ! Set Coeff. in the nonlinear convergence test, default = 0.33 + coef_nonlin = 1e-1 + retval = FIDASetNonlinConvCoef(ida_mem, coef_nonlin) + if (retval /= 0) then + print *, 'Error in FIDASetNonlinConvCoef, retval = ', retval, '; halting' + stop 1 + end if + + + ! Set maximun number of nonliear iterations, default = 4 + retval = FIDASetMaxNonlinIters(ida_mem, 100) + if (retval /= 0) then + print *, 'Error in IDASetMaxNonlinIters, retval = ', retval, '; halting' + stop 1 + end if + + ! Set maximum number of convergence test failures, default = 10 + retval = FIDASetMaxConvFails(ida_mem, 50) + if (retval /= 0) then + print *, 'Error in FIDASetMaxConvFails, retval = ', retval, '; halting' + stop 1 + end if + + ! Set maximum number of error test failures, default = 10 + retval = FIDASetMaxErrTestFails(ida_mem, 50) + if (retval /= 0) then + print *, 'Error in FIDASetMaxErrTestFails, retval = ', retval, '; halting' + stop 1 + end if + + ! Set maximum number of steps, dafault = 500 + maxstep = 999999 + retval = FIDASetMaxNumSteps(ida_mem, maxstep) + if (retval /= 0) then + print *, 'Error in FIDASetMaxNumSteps, retval = ', retval, '; halting' + stop 1 + end if + + ! Set maximum stepsize, dafault = infinity + ! h_max = 1 + ! retval = FIDASetMaxStep(ida_mem, h_max) + ! if (retval /= 0) then + ! print *, 'Error in FIDASetMaxSteps, retval = ', retval, '; halting' + ! stop 1 + ! end if + + ! Set initial stepsize + retval = FIDASetInitStep(ida_mem, h_init) + if (retval /= 0) then + print *, 'Error in FIDASetInitStep, retval = ', retval, '; halting' + stop 1 + end if + + retval = FIDASetStopTime(ida_mem, dt) + if (retval /= 0) then + print *, 'Error in FIDASetStopTime, retval = ', retval, '; halting' + stop 1 + end if + + + ! retval = FIDASetLinearSolutionScaling(ida_mem, 0) ! scalling_on 1 and off 0 + + tret(1) = t0 + eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) + eqns_data%mLayerVolFracIcePrev(:) = diag_data%var(iLookPROG%mLayerVolFracWat)%dat(:) + eqns_data%mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) ! matric head at t0 + eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) + eqns_data%mLayerHeatCapPrev(:) = diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat(:) + eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) + + + do while(tret(1) < dt) + ! call IDASolve + retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) + + ! get the last stepsize + retval = FIDAGetLastStep(ida_mem, stepsize_cur) + if (retval /= 0) then + print *, 'Error in FIDAGetLastStep, retval = ', retval, '; halting' + stop 1 + end if + + ! compute the flux and the residual vector for a given state vector + call eval8summaFida(& + ! input: model control + stepsize_cur(1), & + eqns_data%dt, & + eqns_data%nSnow, & ! intent(in): number of snow layers + eqns_data%nSoil, & ! intent(in): number of soil layers + eqns_data%nLayers, & ! intent(in): number of layers + eqns_data%nState, & ! intent(in): number of state variables in the current subset + eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + eqns_data%firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution + eqns_data%heatCapVaries, & ! intent(in): flag to indicate if heat capacity is constant in the current subset + ! input: state vectors + stateVec, & ! intent(in): model state vector + stateVecPrime, & ! intent(in): model state vector + eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + eqns_data%lookup_data, & + eqns_data%type_data, & ! intent(in): type of vegetation and soil + eqns_data%attr_data, & ! intent(in): spatial attributes + eqns_data%mpar_data, & ! intent(in): model parameters + eqns_data%forc_data, & ! intent(in): model forcing data + eqns_data%bvar_data, & ! intent(in): average model variables for the entire basin + eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + eqns_data%indx_data, & ! intent(inou): index data + eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU + eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) + eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian + eqns_data%mLayerMatricHeadLiqTrial, & + eqns_data%mLayerMatricHeadLiqPrev, & + eqns_data%mLayerMatricHeadTrial, & + eqns_data%mLayerMatricHeadPrev, & + eqns_data%mLayerVolFracWatTrial, & + eqns_data%mLayerVolFracWatPrev, & + eqns_data%mLayerVolFracIceTrial, & + eqns_data%mLayerVolFracIcePrev, & + eqns_data%mLayerHeatCapPrev, & + eqns_data%mLayerHeatCapTrial, & + eqns_data%mLayerEnthalpyPrev, & ! intent(in) + eqns_data%mLayerEnthalpyTrial, & ! intent(out) + ! output + feasible, & ! intent(out): flag to denote the feasibility of the solution + eqns_data%fluxVec, & ! intent(out): flux vector + eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec, & ! intent(out): residual vector + eqns_data%err,eqns_data%message) ! intent(out): error control + + select case(ixQuadrature) + ! sum of flux + case(ixRectangular) + do iVar=1,size(flux_meta) + flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * stepsize_cur(1) + end do + case(ixTrapezoidal) + if(startQuadrature)then + do iVar=1,size(flux_meta) + flux_sum%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) * stepsize_cur(1) + end do + startQuadrature = .false. + else + do iVar=1,size(flux_meta) + flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:) & + * ( stepsize_past + stepsize_cur(1) ) + end do + endif + do iVar=1,size(flux_meta) + flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + end do + stepsize_past = stepsize_cur(1) + case default; err=20; message=trim(message)//'expect case to be ixRecangular, ixTrapezoidal'; return + end select +! sum of mLayerCmpress + mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & + * ( eqns_data%mLayerMatricHeadLiqTrial(:) - eqns_data%mLayerMatricHeadLiqPrev(:) ) + eqns_data%mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) + eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) + eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) + eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) + eqns_data%mLayerHeatCapPrev(:) = eqns_data%mLayerHeatCapTrial(:) + eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) + + ! just for experiment: computing water balance error for the current substep +! if(size(eqns_data%indx_data%var(iLookINDEX%ixMatOnly)%dat)>0)then + ! soilWatBalErr = sum( real(rVec(eqns_data%indx_data%var(iLookINDEX%ixMatOnly)%dat), dp)*eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat(eqns_data%nSnow + eqns_data%indx_data%var(iLookINDEX%ixMatricHead)%dat ) ) +! endif + ! print *, soilWatBalErr + + end do ! while loop on one_step mode + + + firstFluxCall = eqns_data%firstFluxCall + fluxVec = eqns_data%fluxVec + diag_data = eqns_data%diag_data + flux_temp = eqns_data%flux_temp + flux_data = eqns_data%flux_data + deriv_data = eqns_data%deriv_data + dBaseflow_dMatric = eqns_data%dBaseflow_dMatric + ixSaturation = eqns_data%ixSaturation + resSink = eqns_data%resSink + stepsize_past = eqns_data%stepsize_past + err = eqns_data%err + message = eqns_data%message + + + retval = FIDAGetLastStep(ida_mem, dt_last) + if (retval /= 0) then + print *, 'Error in FIDAGetLastStep, retval = ', retval, '; halting' + stop 1 + end if + + retval = FIDAGetCurrentTime(ida_mem, t_last) + if (retval /= 0) then + print *, 'Error in FIDAGetCurrentTime, retval = ', retval, '; halting' + stop 1 + end if + + + ! free memory + deallocate(eqns_data%fScale) + deallocate(eqns_data%sMul) + deallocate(eqns_data%dMat) + deallocate(eqns_data%dBaseflow_dMatric) + deallocate(eqns_data%mLayerCmpress_sum) + deallocate(eqns_data%mLayerMatricHeadLiqTrial) + deallocate(eqns_data%mLayerMatricHeadLiqPrev) + deallocate(eqns_data%mLayerMatricHeadTrial) + deallocate(eqns_data%mLayerMatricHeadPrev) + deallocate( eqns_data%fluxVec ) + deallocate( eqns_data%resSink ) + deallocate( eqns_data%resVec ) + deallocate( eqns_data%mLayerVolFracWatTrial ) + deallocate( eqns_data%mLayerVolFracWatPrev ) + deallocate( eqns_data%mLayerVolFracIceTrial ) + deallocate( eqns_data%mLayerVolFracIcePrev ) + deallocate( eqns_data%mLayerHeatCapTrial ) + deallocate( eqns_data%mLayerHeatCapPrev ) + deallocate( eqns_data%mLayerEnthalpyTrial ) + deallocate( eqns_data%mLayerEnthalpyPrev ) + + call FIDAFree(ida_mem) + retval = FSUNNonlinSolFree(sunnonlin_NLS) + retval = FSUNLinSolFree(sunlinsol_LS) + call FSUNMatDestroy(sunmat_A) + call FN_VDestroy(sunvec_y) + call FN_VDestroy(sunvec_yp) + + ! end associate statements + end associate globalVars + + + + end subroutine fidaSolver + + +! ---------------------------------------------------------------- +! SetInitialCondition: routine to initialize u and up vectors. +! ---------------------------------------------------------------- +subroutine setInitialCondition(neq, y, sunvec_u, sunvec_up) + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use fsundials_nvector_mod + use fnvector_serial_mod + + !======= Declarations ========= + implicit none + + ! calling variables + type(N_Vector) :: sunvec_u ! solution N_Vector + type(N_Vector) :: sunvec_up ! derivative N_Vector + integer(c_long) :: neq + real(dp) :: y(neq) + + ! pointers to data in SUNDIALS vectors + real(c_double), pointer :: uu(:) + real(c_double), pointer :: up(:) + + + !======= Internals ============ + + ! get data arrays from SUNDIALS vectors + uu(1:neq) => FN_VGetArrayPointer(sunvec_u) + up(1:neq) => FN_VGetArrayPointer(sunvec_up) + + + uu = y + up = 0._dp + + +end subroutine setInitialCondition + +end module fidaSolver_module + + + + + + + diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 new file mode 100644 index 000000000..8fb8c967f --- /dev/null +++ b/build/source/engine/fida_datatypes.f90 @@ -0,0 +1,84 @@ + + +module fida_datatypes + +! data types +USE nrtype +use, intrinsic :: iso_c_binding + +! provide access to the derived types to define the data structures +USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (dp) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (dp) + zLookup, & ! data vector with variable length dimension (dp) + model_options ! defines the model decisions + +implicit none + + type eqnsData + real(dp) :: dt ! time step + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers + integer(i4b) :: nLayers ! total number of layers + integer :: nState ! total number of state variables + integer(i4b) :: ixMatrix ! form of matrix (dense or banded) + integer(i4b) :: ixQuadrature ! type of quadrature method for approximating average flux + logical(lgt) :: startQuadrature + logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution + logical(lgt) :: heatCapVaries + real(dp), allocatable :: fScale(:) ! function scaling vector + real(qp), allocatable :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + real(dp), allocatable :: dMat(:) + type(zLookup) :: lookup_data ! lookup tables + type(var_i) :: type_data ! type of vegetation and soil + type(var_d) :: attr_data ! spatial attributes + type(var_dlength) :: mpar_data ! model parameters + type(var_d) :: forc_data ! model forcing data + type(var_dlength) :: bvar_data ! model variables for the local basin + type(var_dlength) :: prog_data ! prognostic variables for a local HRU + type(var_ilength) :: indx_data ! indices defining model states and layers + type(var_dlength) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength) :: flux_temp ! model fluxes for a local HRU + type(var_dlength) :: flux_data ! model fluxes for a local HRU + type(var_dlength) :: flux_sum + type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + real(dp), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + real(dp), allocatable :: mLayerCmpress_sum(:) + real(dp), allocatable :: mLayerMatricHeadLiqTrial(:) + real(dp), allocatable :: mLayerMatricHeadLiqPrev(:) + real(dp), allocatable :: mLayerMatricHeadTrial(:) + real(dp), allocatable :: mLayerMatricHeadPrev(:) + real(dp), allocatable :: mLayerVolFracWatTrial(:) ! trial value for volumetric fraction of total water (-) + real(dp), allocatable :: mLayerVolFracWatPrev(:) + real(dp), allocatable :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of total water (-) + real(dp), allocatable :: mLayerVolFracIcePrev(:) + real(dp), allocatable :: mLayerHeatCapTrial(:) ! trial value for volumetric fraction of total water (-) + real(dp), allocatable :: mLayerHeatCapPrev(:) + real(dp), allocatable :: mLayerEnthalpyTrial(:) + real(dp), allocatable :: mLayerEnthalpyPrev(:) + real(dp), allocatable :: fluxVec(:) ! flux vector + real(qp), allocatable :: resSink(:) + real(qp), allocatable :: resVec(:) + real(dp), allocatable :: atol(:) + real(dp), allocatable :: rtol(:) + integer(i4b) :: err ! error code + character(len = 50) :: message ! error message + type(c_ptr) :: ida_mem + real(dp) :: t_cur + real(dp) :: stepsize_past + end type eqnsData + + +end module fida_datatypes + + + + + diff --git a/build/source/engine/findDiscontinuity.f90 b/build/source/engine/findDiscontinuity.f90 new file mode 100644 index 000000000..fce75a1ec --- /dev/null +++ b/build/source/engine/findDiscontinuity.f90 @@ -0,0 +1,99 @@ + + +module findDiscontinuity_module + + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use nrtype + use fida_datatypes + USE globalData,only:model_decisions ! model decision structure + USE globalData,only:flux_meta ! metadata on the model fluxes + ! provide access to the derived types to define the data structures + USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (dp) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (dp) + model_options ! defines the model decisions + USE multiconst,only:iden_water ! intrinsic density of liquid water (kg m-3) + USE var_lookup,only:iLookDIAG + USE var_lookup,only:iLookPROG + + + ! privacy + implicit none + private + public::findDiscontinuity + + +contains + + ! ********************************************************************************************************** + ! public function findDiscontinuity: the root function + ! ********************************************************************************************************** + ! Return values: + ! 0 = success, + ! 1 = recoverable error, + ! -1 = non-recoverable error + ! ---------------------------------------------------------------- + integer(c_int) function findDiscontinuity(tres, sunvec_y, sunvec_yp, fval, user_data) & + result(ierr) bind(C,name='findDiscontinuity') + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use fida_mod + use fsundials_nvector_mod + use fnvector_serial_mod + use nrtype + use fida_datatypes + use varExtrFida_module, only:residDiscontinuity + + !======= Declarations ========= + implicit none + + ! calling variables + real(dp), value :: tres ! current time + type(N_Vector) :: sunvec_y ! solution N_Vector y + type(N_Vector) :: sunvec_yp ! derivative N_Vector y' + real(c_double) :: fval(1000) ! root function values + type(c_ptr), value :: user_data ! user-defined data + + + ! pointers to data in SUNDIALS vectors + type(eqnsData), pointer :: eqns_data ! equations data + real(dp), pointer :: stateVec(:) + real(dp), pointer :: stateVecPrime(:) + integer(i4b) :: retval + + + + + !======= Internals ============ + + ! get equations data from user-defined data + call c_f_pointer(user_data, eqns_data) + + ! get data arrays from SUNDIALS vectors + stateVec => FN_VGetArrayPointer(sunvec_y) + ! stateVecPrime => FN_VGetArrayPointer(sunvec_yp) + + call residDiscontinuity(& + ! input + stateVec, & ! intent(in): model state vector (mixed units) + eqns_data%diag_data, & ! intent(in): model diagnostic variables for a local HRU + eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU + eqns_data%indx_data, & ! intent(in): indices defining model states and layers + ! output + fval, & ! intent(out) + eqns_data%err,eqns_data%message) ! intent(out): error control + + + ! return success + ierr = 0 + return + + end function findDiscontinuity + + +end module findDiscontinuity_module diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 3020ae20f..9bacd1f8b 100755 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -1,5 +1,5 @@ ! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington +! Copyright (C) 2014-2015 NCAR/RAL ! ! This file is part of SUMMA ! @@ -100,6 +100,7 @@ module opSplittin_module var_flagVec, & ! data vector with variable length dimension (i4b) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (dp) + zLookup, & ! data vector with variable length dimension (dp) model_options ! defines the model decisions ! look-up values for the choice of groundwater representation (local-column, or single-basin) @@ -121,7 +122,7 @@ module opSplittin_module ! named variables for the coupling method integer(i4b),parameter :: fullyCoupled=1 ! 1st try: fully coupled solution integer(i4b),parameter :: stateTypeSplit=2 ! 2nd try: separate solutions for each state type -integer(i4b),parameter :: nCoupling=2 ! number of possible solutions +!integer(i4b),parameter :: nCoupling=2 !reza Now it is a variable ! number of possible solutions ! named variables for the state variable split integer(i4b),parameter :: nrgSplit=1 ! order in sequence for the energy operation @@ -162,7 +163,7 @@ module opSplittin_module ! (1) Attempt different solutions in the following order: (a) fully coupled; (b) split by state type and by ! domain type for a given energy and mass split (vegetation, snow, and soil); and (c) scalar solution ! for a given state type and domain subset. - ! (2) For a given split, compute a variable number of substeps (in varSubstep). + ! (2) For a given split, compute a variable number of substeps (in varSubstepFida). ! ********************************************************************************************************** subroutine opSplittin(& ! input: model control @@ -183,6 +184,7 @@ subroutine opSplittin(& diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU bvar_data, & ! intent(in): model variables for the local basin + lookup_data, & ! intent(in): lookup tables model_decisions,& ! intent(in): model decisions ! output: model control dtMultiplier, & ! intent(out): substep multiplier (-) @@ -199,6 +201,7 @@ subroutine opSplittin(& ! population/extraction of state vectors USE indexState_module,only:indexSplit ! get state indices USE varSubstep_module,only:varSubstep ! complete substeps for a given split + USE varSubstepFida_module,only:varSubstepFida ! complete substeps for a given split ! identify name of variable type (for error message) USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages implicit none @@ -223,6 +226,7 @@ subroutine opSplittin(& type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin + type(zLookup), intent(in) :: lookup_data ! lookup tables type(model_options),intent(in) :: model_decisions(:) ! model decisions ! output: model control real(dp),intent(out) :: dtMultiplier ! substep multiplier (-) @@ -291,6 +295,10 @@ subroutine opSplittin(& logical(lgt) :: doAdjustTemp ! flag to adjust temperature after the mass split logical(lgt) :: failedMinimumStep ! flag to denote failure of substepping for a given split integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + integer(i4b),parameter :: ida=1 + integer(i4b),parameter :: be=2 + integer(i4b) :: solver=ida ! be or ida + integer(i4b) :: nCoupling ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- @@ -355,6 +363,8 @@ subroutine opSplittin(& ! --------------------------------------------------------------------------------------- ! initialize error control err=0; message="opSplittin/" + + nCoupling=solver ! reza ! ***** ! (0) PRELIMINARIES... @@ -481,7 +491,7 @@ subroutine opSplittin(& end select ! operator splitting option ! state splitting loop - stateTypeSplitLoop: do iStateTypeSplit=1,nStateTypeSplit + stateTypeSplit: do iStateTypeSplit=1,nStateTypeSplit !print*, 'iStateTypeSplit, nStateTypeSplit = ', iStateTypeSplit, nStateTypeSplit @@ -753,13 +763,15 @@ subroutine opSplittin(& ! * solve variable subset for one time step... ! -------------------------------------------- - !print*, trim(message)//'before varSubstep: nSubset = ', nSubset + !print*, trim(message)//'before varSubstepFida: nSubset = ', nSubset ! keep track of the number of scalar solutions if(ixSolution==scalar) numberScalarSolutions = numberScalarSolutions + 1 - + ! solve variable subset for one full time step - call varSubstep(& + select case(solver) + case(ida) + call varSubstepFida(& ! input: model control dt, & ! intent(inout) : time step (s) dtInit, & ! intent(in) : initial time step (seconds) @@ -775,6 +787,7 @@ subroutine opSplittin(& fluxCount, & ! intent(inout) : number of times fluxes are updated (should equal nsubstep) ! input/output: data structures model_decisions, & ! intent(in) : model decisions + lookup_data, & ! intent(in) : lookup tables type_data, & ! intent(in) : type of vegetation and soil attr_data, & ! intent(in) : spatial attributes forc_data, & ! intent(in) : model forcing data @@ -793,14 +806,56 @@ subroutine opSplittin(& reduceCoupledStep, & ! intent(out) : flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt err,cmessage) ! intent(out) : error code and error message + case(be) + call varSubstep(& + ! input: model control + dt, & ! intent(inout) : time step (s) + dtInit, & ! intent(in) : initial time step (seconds) + dt_min, & ! intent(in) : minimum time step (seconds) + nSubset, & ! intent(in) : total number of variables in the state subset + doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature + firstSubStep, & ! intent(in) : flag to denote first sub-step + firstFluxCall, & ! intent(inout) : flag to indicate if we are processing the first flux call + computeVegFlux, & ! intent(in) : flag to denote if computing energy flux over vegetation + (ixSolution==scalar), & ! intent(in) : flag to denote computing the scalar solution + iStateSplit, & ! intent(in) : index of the layer in the splitting operation + fluxMask, & ! intent(in) : mask for the fluxes used in this given state subset + fluxCount, & ! intent(inout) : number of times fluxes are updated (should equal nsubstep) + ! input/output: data structures + model_decisions, & ! intent(in) : model decisions + lookup_data, & ! intent(in) : lookup tables + type_data, & ! intent(in) : type of vegetation and soil + attr_data, & ! intent(in) : spatial attributes + forc_data, & ! intent(in) : model forcing data + mpar_data, & ! intent(in) : model parameters + indx_data, & ! intent(inout) : index data + prog_data, & ! intent(inout) : model prognostic variables for a local HRU + diag_data, & ! intent(inout) : model diagnostic variables for a local HRU + flux_data, & ! intent(inout) : model fluxes for a local HRU + deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables + bvar_data, & ! intent(in) : model variables for the local basin + ! output: control + ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) + dtMultiplier, & ! intent(out) : substep multiplier (-) + nSubsteps, & ! intent(out) : number of substeps taken for a given split + failedMinimumStep, & ! intent(out) : flag for failed substeps + reduceCoupledStep, & ! intent(out) : flag to reduce the length of the coupled step + tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt + err,cmessage) ! intent(out) : error code and error message + ! check + case default; err=20; message=trim(message)//'expect case to be ida or be'; return + end select + + + if(err/=0)then message=trim(message)//trim(cmessage) if(err>0) return endif ! (check for errors) - !print*, trim(message)//'after varSubstep: scalarSnowDrainage = ', flux_data%var(iLookFLUX%scalarSnowDrainage)%dat - !print*, trim(message)//'after varSubstep: iLayerLiqFluxSnow = ', flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat - !print*, trim(message)//'after varSubstep: iLayerLiqFluxSoil = ', flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat + !print*, trim(message)//'after varSubstepFida: scalarSnowDrainage = ', flux_data%var(iLookFLUX%scalarSnowDrainage)%dat + !print*, trim(message)//'after varSubstepFida: iLayerLiqFluxSnow = ', flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat + !print*, trim(message)//'after varSubstepFida: iLayerLiqFluxSoil = ', flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ! check !if(ixSolution==scalar)then @@ -813,12 +868,12 @@ subroutine opSplittin(& ! check !if(ixCoupling/=fullyCoupled)then ! print*, 'dt = ', dt - ! print*, 'after varSubstep: err = ', err - ! print*, 'after varSubstep: cmessage = ', trim(cmessage) - ! print*, 'after varSubstep: computeVegFlux = ', computeVegFlux - ! print*, 'after varSubstep: stateMask = ', stateMask - ! print*, 'after varSubstep: coupling = ', (ixCoupling==fullyCoupled) - ! print*, 'after varSubstep: scalar solve = ', (ixSolution==scalar) + ! print*, 'after varSubstepFida: err = ', err + ! print*, 'after varSubstepFida: cmessage = ', trim(cmessage) + ! print*, 'after varSubstepFida: computeVegFlux = ', computeVegFlux + ! print*, 'after varSubstepFida: stateMask = ', stateMask + ! print*, 'after varSubstepFida: coupling = ', (ixCoupling==fullyCoupled) + ! print*, 'after varSubstepFida: scalar solve = ', (ixSolution==scalar) ! print*, 'iStateTypeSplit, nStateTypeSplit = ', iStateTypeSplit, nStateTypeSplit ! print*, 'iDomainSplit, nDomainSplit = ', iDomainSplit, nDomainSplit ! print*, 'nSubset = ', nSubset @@ -935,7 +990,7 @@ subroutine opSplittin(& where(ixStateType(ixHydLayer) ==iname_lmpLayer) ixStateType(ixHydLayer) =iname_matLayer endif ! if modifying state variables for the mass split - end do stateTypeSplitLoop ! state type splitting loop + end do stateTypeSplit ! state type splitting loop ! check !if(ixCoupling/=fullyCoupled)then @@ -946,20 +1001,8 @@ subroutine opSplittin(& ! ========================================================================================================================================== ! success = exit the coupling loop - ! terminate DO loop early if fullyCoupled returns a solution, - ! so that the loop does not proceed to ixCoupling = stateTypeSplit - if(ixCoupling==fullyCoupled .and. .not. failure) exit coupling - - ! if we reach stateTypeSplit, terminating the DO loop here is cleaner - ! than letting the loop complete, because in the latter case the coupling - ! loop will end with ixCoupling = nCoupling+1 = 3 (a FORTRAN loop - ! increments the index variable at the end of each iteration and stops - ! the loop if the index > specified stop value). Variable ixCoupling is - ! used for error reporting in coupled_em.f90 in the balance checks and - ! we thus need to make sure ixCoupling is not incremented to be larger - ! than nCoupling. - if(ixCoupling==stateTypeSplit .and. .not. failure) exit coupling - + if(ixCoupling==fullyCoupled .and. .not.failure) exit coupling + end do coupling ! coupling method ! check that all state variables were updated @@ -981,9 +1024,9 @@ subroutine opSplittin(& if(ixCoupling/=fullyCoupled .or. nSubsteps>1) dtMultiplier=0.5_dp ! compute the melt in each snow and soil layer - if(nSnow>0) mLayerMeltFreeze( 1:nSnow ) = -(mLayerVolFracIce( 1:nSnow ) - mLayerVolFracIceInit( 1:nSnow ))*iden_ice - mLayerMeltFreeze(nSnow+1:nLayers) = -(mLayerVolFracIce(nSnow+1:nLayers) - mLayerVolFracIceInit(nSnow+1:nLayers))*iden_water - + if(nSnow>0) diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat( 1:nSnow ) = -(mLayerVolFracIce( 1:nSnow ) - mLayerVolFracIceInit( 1:nSnow ))*iden_ice + diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(nSnow+1:nLayers) = -(mLayerVolFracIce(nSnow+1:nLayers) - mLayerVolFracIceInit(nSnow+1:nLayers))*iden_water + ! end associate statements end associate globalVars diff --git a/build/source/engine/soilCmpresFida.f90 b/build/source/engine/soilCmpresFida.f90 new file mode 100644 index 000000000..cf72409e3 --- /dev/null +++ b/build/source/engine/soilCmpresFida.f90 @@ -0,0 +1,135 @@ + +module soilCmpresFida_module + +! data types +USE nrtype + +! provide access to the derived types to define the data structures +USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (dp) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (dp) + model_options ! defines the model decisions + +! indices that define elements of the data structures +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure +USE var_lookup,only:iLookPARAM ! named variables for structure elements +USE var_lookup,only:iLookFORCE ! named variables for structure elements +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookFLUX ! named variables for structure elements +USE var_lookup,only:iLookDERIV ! named variables for structure elements + +! missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing real number + +! layer types +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil + +! access the global print flag +USE globalData,only:globalPrintFlag + +! control parameters +USE globalData,only:verySmall ! a very small number +USE globalData,only:veryBig ! a very big number +USE globalData,only:dx ! finite difference increment + +! constants +USE multiconst,only:& + gravity, & ! acceleration of gravity (m s-2) + Tfreeze, & ! temperature at freezing (K) + LH_fus, & ! latent heat of fusion (J kg-1) + LH_vap, & ! latent heat of vaporization (J kg-1) + LH_sub, & ! latent heat of sublimation (J kg-1) + Cp_air, & ! specific heat of air (J kg-1 K-1) + iden_air, & ! intrinsic density of air (kg m-3) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water ! intrinsic density of liquid water (kg m-3) + +! look-up values for the choice of groundwater representation (local-column, or single-basin) +USE mDecisions_module,only: & + localColumn, & ! separate groundwater representation in each local soil column + singleBasin ! single groundwater store over the entire basin + +! look-up values for the choice of groundwater parameterization +USE mDecisions_module,only: & + qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization + bigBucket, & ! a big bucket (lumped aquifer model) + noExplicit ! no explicit groundwater parameterization + +! look-up values for the form of Richards' equation +USE mDecisions_module,only: & + moisture, & ! moisture-based form of Richards' equation + mixdform ! mixed form of Richards' equation + +! look-up values for the choice of boundary conditions for hydrology +USE mDecisions_module,only: & + prescribedHead, & ! prescribed head (volumetric liquid water content for mixed form of Richards' eqn) + funcBottomHead, & ! function of matric head in the lower-most layer + freeDrainage, & ! free drainage + liquidFlux, & ! liquid water flux + zeroFlux ! zero flux + +implicit none +private +public::soilCmpresFida +contains + + + + ! ********************************************************************************************************** + ! public subroutine soilCmpres: compute soil compressibility (-) and its derivative w.r.t matric head (m-1) + ! ********************************************************************************************************** + subroutine soilCmpresFida(& + ! input: + ixRichards, & ! intent(in): choice of option for Richards' equation + ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers + mLayerMatricHeadPrime, & ! intent(in): matric head at the start of the time step (m) + mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) + mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice content in each soil layer (-) + specificStorage, & ! intent(in): specific storage coefficient (m-1) + theta_sat, & ! intent(in): soil porosity (-) + ! output: + compress, & ! intent(out): compressibility of the soil matrix (-) + dCompress_dPsi, & ! intent(out): derivative in compressibility w.r.t. matric head (m-1) + err,message) ! intent(out): error code and error message + implicit none + ! input: + integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation + integer(i4b),intent(in) :: ixBeg,ixEnd ! start and end indices defining desired layers + real(dp),intent(in) :: mLayerMatricHeadPrime(:) ! matric head at the start of the time step (m) + real(dp),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) + real(dp),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) + real(dp),intent(in) :: specificStorage ! specific storage coefficient (m-1) + real(dp),intent(in) :: theta_sat(:) ! soil porosity (-) + ! output: + real(dp),intent(inout) :: compress(:) ! soil compressibility (-) + real(dp),intent(inout) :: dCompress_dPsi(:) ! derivative in soil compressibility w.r.t. matric head (m-1) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local variables + integer(i4b) :: iLayer ! index of soil layer + ! -------------------------------------------------------------- + ! initialize error control + err=0; message='soilCmpresFida/' + ! (only compute for the mixed form of Richards' equation) + if(ixRichards==mixdform)then + do iLayer=1,size(mLayerMatricHeadPrime) + if(iLayer>=ixBeg .and. iLayer<=ixEnd)then + ! compute the derivative for the compressibility term (m-1) + dCompress_dPsi(iLayer) = specificStorage*(mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer))/theta_sat(iLayer) + ! compute the compressibility term (-) + compress(iLayer) = mLayerMatricHeadPrime(iLayer) * dCompress_dPsi(iLayer) + endif + end do + else + compress(:) = 0._dp + dCompress_dPsi(:) = 0._dp + end if + end subroutine soilCmpresFida + +end module soilCmpresFida_module diff --git a/build/source/engine/soil_utils_fida.f90 b/build/source/engine/soil_utils_fida.f90 new file mode 100644 index 000000000..994e12b02 --- /dev/null +++ b/build/source/engine/soil_utils_fida.f90 @@ -0,0 +1,234 @@ +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2015 NCAR/RAL +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . + +module soil_utils_fida_module + +! data types +USE nrtype + +USE multiconst,only: gravity, & ! acceleration of gravity (m s-2) + Tfreeze, & ! temperature at freezing (K) + LH_fus, & ! latent heat of fusion (J kg-1, or m2 s-2) + R_wv ! gas constant for water vapor (J kg-1 K-1; [J = Pa m3]) +USE soil_utils_module,only:matricHead +USE soil_utils_module,only:dPsi_dTheta +USE soil_utils_module,only:volFracLiq +USE soil_utils_module,only:dTheta_dPsi + +! privacy +implicit none +private + +! routines to make public + +public::liquidHeadFida +public::d2Theta_dPsi2 +public::d2Theta_dTk2 + +! constant parameters +real(dp),parameter :: verySmall=epsilon(1.0_dp) ! a very small number (used to avoid divide by zero) +contains + + + ! ****************************************************************************************************************************** + ! public subroutine: compute the liquid water matric potential (and the derivatives w.r.t. total matric potential and temperature) + ! ****************************************************************************************************************************** + subroutine liquidHeadFida(& + ! input + matricHeadTotal ,& ! intent(in) : total water matric potential (m) + matricHeadTotalPrime ,& ! intent(in) + volFracLiq ,& ! intent(in) : volumetric fraction of liquid water (-) + volFracIce ,& ! intent(in) : volumetric fraction of ice (-) + vGn_alpha,vGn_n,theta_sat,theta_res,vGn_m,& ! intent(in) : soil parameters + dVolTot_dPsi0 ,& ! intent(in) : derivative in the soil water characteristic (m-1) + dTheta_dT ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) + tempPrime ,& ! intent(in) + volFracLiqPrime ,& ! intent(in) + volFracIcePrime ,& ! intent(in) + ! output + matricHeadLiq ,& ! intent(out) : liquid water matric potential (m) + matricHeadLiqPrime ,& ! intent(out) + dPsiLiq_dPsi0 ,& ! intent(out) : derivative in the liquid water matric potential w.r.t. the total water matric potential (-) + dPsiLiq_dTemp ,& ! intent(out) : derivative in the liquid water matric potential w.r.t. temperature (m K-1) + err,message) ! intent(out) : error control + ! computes the liquid water matric potential (and the derivatives w.r.t. total matric potential and temperature) + implicit none + ! input + real(dp),intent(in) :: matricHeadTotal ! total water matric potential (m) + real(dp),intent(in) :: matricHeadTotalPrime + real(dp),intent(in) :: volFracLiq ! volumetric fraction of liquid water (-) + real(dp),intent(in) :: volFracIce ! volumetric fraction of ice (-) + real(dp),intent(in) :: vGn_alpha,vGn_n,theta_sat,theta_res,vGn_m ! soil parameters + real(dp),intent(in) ,optional :: dVolTot_dPsi0 ! derivative in the soil water characteristic (m-1) + real(dp),intent(in) ,optional :: dTheta_dT ! derivative in volumetric total water w.r.t. temperature (K-1) + real(dp),intent(in) :: TempPrime + real(dp),intent(in) :: volFracLiqPrime + real(dp),intent(in) :: volFracIcePrime + ! output + real(dp),intent(out) :: matricHeadLiq ! liquid water matric potential (m) + real(dp),intent(out) :: matricHeadLiqPrime + real(dp),intent(out) ,optional :: dPsiLiq_dPsi0 ! derivative in the liquid water matric potential w.r.t. the total water matric potential (-) + real(dp),intent(out) ,optional :: dPsiLiq_dTemp ! derivative in the liquid water matric potential w.r.t. temperature (m K-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local + real(dp) :: xNum,xDen ! temporary variables (numeratir, denominator) + real(dp) :: effSat ! effective saturation (-) + real(dp) :: dPsiLiq_dEffSat ! derivative in liquid water matric potential w.r.t. effective saturation (m) + real(dp) :: dEffSat_dTemp ! derivative in effective saturation w.r.t. temperature (K-1) + real(dp) :: dEffSat_dFracLiq + real(dp) :: effSatPrime + ! ------------------------------------------------------------------------------------------------------------------------------ + ! initialize error control + err=0; message='liquidHeadFida/' + + ! ** partially frozen soil + if(volFracIce > verySmall .and. matricHeadTotal < 0._dp)then ! check that ice exists and that the soil is unsaturated + + + ! ----- + ! - compute liquid water matric potential... + ! ------------------------------------------ + + ! - compute effective saturation + ! NOTE: include ice content as part of the solid porosity - major effect of ice is to reduce the pore size; ensure that effSat=1 at saturation + ! (from Zhao et al., J. Hydrol., 1997: Numerical analysis of simultaneous heat and mass transfer...) + xNum = volFracLiq - theta_res + xDen = theta_sat - volFracIce - theta_res + effSat = xNum/xDen ! effective saturation + + ! - matric head associated with liquid water + matricHeadLiq = matricHead(effSat,vGn_alpha,0._dp,1._dp,vGn_n,vGn_m) ! argument is effective saturation, so theta_res=0 and theta_sat=1 + if (effSat < 1._dp .and. effSat > 0._dp)then + effSatPrime = (volFracLiqPrime * xDen + volFracIcePrime * xNum) / xDen**2._dp + matricHeadLiqPrime = -( 1._dp/(vGn_alpha*vGn_n*vGn_m) ) * effSat**(-1._dp-1._dp/vGn_m) * ( effSat**(-1._dp/vGn_m) - 1._dp )**(-1._dp+1._dp/vGn_n) * effSatPrime + else + matricHeadLiqPrime = 0._dp + endif + + + ! compute derivative in liquid water matric potential w.r.t. effective saturation (m) + if(present(dPsiLiq_dPsi0).or.present(dPsiLiq_dTemp))then + dPsiLiq_dEffSat = dPsi_dTheta(effSat,vGn_alpha,0._dp,1._dp,vGn_n,vGn_m) + endif + + ! ----- + ! - compute derivative in the liquid water matric potential w.r.t. the total water matric potential... + ! ---------------------------------------------------------------------------------------------------- + + ! check if the derivative is desired + if(present(dPsiLiq_dTemp))then + ! (check required input derivative is present) + if(.not.present(dVolTot_dPsi0))then + message=trim(message)//'dVolTot_dPsi0 argument is missing' + err=20; return + endif + + ! (compute derivative in the liquid water matric potential w.r.t. the total water matric potential) + dPsiLiq_dPsi0 = dVolTot_dPsi0*dPsiLiq_dEffSat*xNum/(xDen**2._dp) + ! matricHeadLiqPrime = dPsiLiq_dPsi0 * matricHeadTotalPrime + + endif ! if dPsiLiq_dTemp is desired + + ! ----- + ! - compute the derivative in the liquid water matric potential w.r.t. temperature... + ! ----------------------------------------------------------------------------------- + + ! check if the derivative is desired + if(present(dPsiLiq_dTemp))then + + ! (check required input derivative is present) + if(.not.present(dTheta_dT))then + message=trim(message)//'dTheta_dT argument is missing' + err=20; return + endif + ! (compute the derivative in the liquid water matric potential w.r.t. temperature) + dEffSat_dTemp = -dTheta_dT*xNum/(xDen**2._dp) + dTheta_dT/xDen + dPsiLiq_dTemp = dPsiLiq_dEffSat*dEffSat_dTemp + ! matricHeadLiqPrime = dPsiLiq_dTemp * tempPrime + + + + endif ! if dPsiLiq_dTemp is desired + + ! ** unfrozen soil + else ! (no ice) + matricHeadLiq = matricHeadTotal + matricHeadLiqPrime = matricHeadTotalPrime + if(present(dPsiLiq_dTemp)) dPsiLiq_dPsi0 = 1._dp ! derivative=1 because values are identical + if(present(dPsiLiq_dTemp)) dPsiLiq_dTemp = 0._dp ! derivative=0 because no impact of temperature for unfrozen conditions + end if ! (if ice exists) + + end subroutine liquidHeadFida + + + ! ****************************************************************************************************************************** + ! public function dTheta_dPsi: compute the derivative of the soil water characteristic (m-1) + ! ****************************************************************************************************************************** + function d2Theta_dPsi2(psi,alpha,theta_res,theta_sat,n,m) + implicit none + real(dp),intent(in) :: psi ! soil water suction (m) + real(dp),intent(in) :: alpha ! scaling parameter (m-1) + real(dp),intent(in) :: theta_res ! residual volumetric water content (-) + real(dp),intent(in) :: theta_sat ! porosity (-) + real(dp),intent(in) :: n ! vGn "n" parameter (-) + real(dp),intent(in) :: m ! vGn "m" parameter (-) + real(dp) :: d2Theta_dPsi2 ! derivative of the soil water characteristic (m-1) + real(dp) :: mult_fcn + real(dp) :: mult_fcnp + if(psi<0._dp)then + mult_fcn = (-m*n*alpha*(alpha*psi)**(n-1._dp)) * ( 1._dp + (psi*alpha)**n )**(-1._dp) + mult_fcnp = -m*n*alpha*(n-1._dp)*alpha*(alpha*psi)**(n-2._dp)*( 1._dp + (psi*alpha)**n )**(-1._dp) - & + ( n*alpha*(alpha*psi)**(n-1._dp)*(1._dp + (psi*alpha)**n)**(-2._dp) ) * ( -m*n*alpha*(alpha*psi)**(n-1._dp) ) + d2Theta_dPsi2 = mult_fcn * dTheta_dPsi(psi,alpha,theta_res,theta_sat,n,m) + & + mult_fcnp * ( volFracLiq(psi,alpha,theta_res,theta_sat,n,m) - theta_res ) + else + d2Theta_dPsi2 = 0._dp + end if + end function d2Theta_dPsi2 + + + ! ****************************************************************************************************************************** + ! public function dTheta_dTk: differentiate the freezing curve w.r.t. temperature + ! ****************************************************************************************************************************** + function d2Theta_dTk2(Tk,theta_res,theta_sat,alpha,n,m) + implicit none + real(dp),intent(in) :: Tk ! temperature (K) + real(dp),intent(in) :: theta_res ! residual liquid water content (-) + real(dp),intent(in) :: theta_sat ! porosity (-) + real(dp),intent(in) :: alpha ! vGn scaling parameter (m-1) + real(dp),intent(in) :: n ! vGn "n" parameter (-) + real(dp),intent(in) :: m ! vGn "m" parameter (-) + real(dp) :: d2Theta_dTk2 ! derivative of the freezing curve w.r.t. temperature (K-1) + ! local variables + real(dp) :: kappa ! constant (m K-1) + real(dp) :: xtemp ! alpha*kappa*(Tk-Tfreeze) -- dimensionless variable (used more than once) + ! compute kappa (m K-1) + kappa = LH_fus/(gravity*Tfreeze) ! NOTE: J = kg m2 s-2 + ! define a tempory variable that is used more than once (-) + xtemp = alpha*kappa*(Tk-Tfreeze) + ! differentiate the freezing curve w.r.t. temperature -- making use of the chain rule + d2Theta_dTk2 = (-alpha*kappa*m*n*alpha*kappa)* (theta_sat - theta_res) * ( (n-1)*xtemp**(n - 2._dp) * (1._dp + xtemp**n)**(-m - 1._dp) & + + n*(-m-1)*xtemp**(2*n - 2._dp) * (1._dp + xtemp**n)**(-m - 2._dp) ) + end function d2Theta_dTk2 + + +end module soil_utils_fida_module diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 new file mode 100644 index 000000000..c7d6171e1 --- /dev/null +++ b/build/source/engine/sysSolvFida.f90 @@ -0,0 +1,601 @@ + + +module sysSolvFida_module + +! data types +USE nrtype + +! access the global print flag +USE globalData,only:globalPrintFlag + +! access missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing double precision number +USE globalData,only:quadMissing ! missing quadruple precision number + +! access matrix information +USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix +USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix +USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix +USE globalData,only: iJac1 ! first layer of the Jacobian to print +USE globalData,only: iJac2 ! last layer of the Jacobian to print + +! domain types +USE globalData,only:iname_veg ! named variables for vegetation +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil + +! state variable type +USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space +USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy +USE globalData,only:iname_watCanopy ! named variable defining the mass of total water on the vegetation canopy +USE globalData,only:iname_liqCanopy ! named variable defining the mass of liquid water on the vegetation canopy +USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers +USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers +USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers +USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers +USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers + +! global metadata +USE globalData,only:flux_meta ! metadata on the model fluxes + +! constants +USE multiconst,only:& + LH_fus, & ! latent heat of fusion (J K-1) + Tfreeze, & ! temperature at freezing (K) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water ! intrinsic density of liquid water (kg m-3) + +! provide access to indices that define elements of the data structures +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookFLUX ! named variables for structure elements +USE var_lookup,only:iLookFORCE ! named variables for structure elements +USE var_lookup,only:iLookPARAM ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure + USE var_lookup,only:iLookDERIV ! named variables for structure elements + +! provide access to the derived types to define the data structures +USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (dp) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (dp) + zLookup, & ! data vector with variable length dimension (dp) + model_options ! defines the model decisions + +! look-up values for the choice of groundwater representation (local-column, or single-basin) +USE mDecisions_module,only: & + localColumn, & ! separate groundwater representation in each local soil column + singleBasin ! single groundwater store over the entire basin + +! look-up values for the choice of groundwater parameterization +USE mDecisions_module,only: & + qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization + bigBucket, & ! a big bucket (lumped aquifer model) + noExplicit ! no explicit groundwater parameterization + + +! safety: set private unless specified otherwise +implicit none +private +public::sysSolvFida + +! control parameters +real(dp),parameter :: valueMissing=-9999._dp ! missing value +real(dp),parameter :: verySmall=1.e-12_dp ! a very small number (used to check consistency) +real(dp),parameter :: veryBig=1.e+20_dp ! a very big number +real(dp),parameter :: dx = 1.e-8_dp ! finite difference increment + +contains + + + ! ********************************************************************************************************** + ! public subroutine sysSolvFida: run the coupled energy-mass model for one timestep + ! ********************************************************************************************************** + subroutine sysSolvFida(& + ! input: model control + dt, & ! intent(in): time step (s) + nState, & ! intent(in): total number of state variables + firstSubStep, & ! intent(in): flag to denote first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution + ! input/output: data structures + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): index data + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_temp, & ! intent(inout): model fluxes for a local HRU + bvar_data, & ! intent(in): model variables for the local basin + model_decisions, & ! intent(in): model decisions + stateVecInit, & ! intent(inout): initial state vector + ! output + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + untappedMelt, & ! intent(out): un-tapped melt energy (J m-3 s-1) + stateVecTrial, & ! intent(out): updated state vector + stateVecPrime, & ! intent(out): updated state vector + reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step + tooMuchMelt, & ! intent(out): flag to denote that there was too much melt + niter, & ! intent(out): number of iterations taken + err,message) ! intent(out): error code and error message + ! --------------------------------------------------------------------------------------- + ! structure allocations + USE allocspace_module,only:allocLocal ! allocate local data structures + ! simulation of fluxes and residuals given a trial state vector + USE eval8summa_module,only:eval8summa ! simulation of fluxes and residuals given a trial state vector + USE eval8summaFida_module,only:eval8summaFida + USE summaSolve_module,only:summaSolve ! calculate the iteration increment, evaluate the new state, and refine if necessary + USE getVectorz_module,only:getScaling ! get the scaling vectors + USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy + USE tolFida_module,only:popTolFida + USE fidaSolver_module,only:fidaSolver +! use varExtrFida_module, only:countDiscontinuity + use, intrinsic :: iso_c_binding + implicit none + ! --------------------------------------------------------------------------------------- + ! * dummy variables + ! --------------------------------------------------------------------------------------- + ! input: model control + real(dp),intent(in) :: dt ! time step (seconds) + integer(i4b),intent(in) :: nState ! total number of state variables + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call + logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + ! input/output: data structures + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_i),intent(in) :: type_data ! type of vegetation and soil + type(var_d),intent(in) :: attr_data ! spatial attributes + type(var_d),intent(in) :: forc_data ! model forcing data + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU + type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_temp ! model fluxes for a local HRU + type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin + type(model_options),intent(in) :: model_decisions(:) ! model decisions + real(dp),intent(in) :: stateVecInit(:) ! initial state vector (mixed units) + ! output: model control + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + real(dp),intent(out) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) + real(dp),intent(out) :: stateVecTrial(:) ! trial state vector (mixed units) + real(dp),intent(out) :: stateVecPrime(:) ! trial state vector (mixed units) + logical(lgt),intent(out) :: reduceCoupledStep ! flag to reduce the length of the coupled step + logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that there was too much melt + integer(i4b),intent(out) :: niter ! number of iterations taken + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ********************************************************************************************************************************************************* + ! ********************************************************************************************************************************************************* + ! --------------------------------------------------------------------------------------- + ! * general local variables + ! --------------------------------------------------------------------------------------- + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iVar ! index of variable + integer(i4b) :: iLayer ! index of layer in the snow+soil domain + integer(i4b) :: iState ! index of model state + integer(i4b) :: local_ixGroundwater ! local index for groundwater representation + real(dp) :: bulkDensity ! bulk density of a given layer (kg m-3) + real(dp) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) + real(dp),parameter :: tempAccelerate=0.00_dp ! factor to force initial canopy temperatures to be close to air temperature + real(dp),parameter :: xMinCanopyWater=0.0001_dp ! minimum value to initialize canopy water (kg m-2) + real(dp),parameter :: tinyStep=0.000001_dp ! stupidly small time step (s) + integer(i4b),parameter :: ixRectangular=1 + integer(i4b),parameter :: ixTrapezoidal=2 + + ! ------------------------------------------------------------------------------------------------------ + ! * model solver + ! ------------------------------------------------------------------------------------------------------ + logical(lgt),parameter :: forceFullMatrix=.true. ! flag to force the use of the full Jacobian matrix + logical(lgt),parameter :: compAverageFlux=.true. + logical(lgt),parameter :: heatCapVaries=.false. + integer(i4b) :: ixQuadrature=ixRectangular ! type of quadrature method to approximate average fluxes + integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) + type(var_dlength) :: flux_init ! model fluxes at the start of the time step + real(dp),allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! NOTE: allocatable, since not always needed + real(dp) :: stateVecNew(nState) ! new state vector (mixed units) + real(dp) :: fluxVec0(nState) ! flux vector (mixed units) + real(dp) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) + real(dp) :: xScale(nState) ! characteristic scale of the state vector (mixed units) + real(dp) :: dMat(nState) ! diagonal matrix (excludes flux derivatives) + real(qp) :: sMul(nState) ! NOTE: qp ! multiplier for state vector for the residual calculations + real(qp) :: rVec(nState) ! NOTE: qp ! residual vector + real(dp) :: rAdd(nState) ! additional terms in the residual vector + real(dp) :: fOld ! function values (-); NOTE: dimensionless because scaled + logical(lgt) :: converged ! convergence flag + logical(lgt) :: feasible ! feasibility flag + real(dp) :: resSinkNew(nState) ! additional terms in the residual vector + real(dp) :: fluxVecNew(nState) ! new flux vector + ! input: reza : fida solver needs more data + real(dp) :: t0 ! beginning of the current time step + real(dp) :: tout ! end of the current time step + real(dp) :: tret(1) + real(dp) :: t_last(1) + real(dp) :: dt_last(1) + real(dp) :: atol(nState) ! absolute telerance + real(dp) :: rtol(nState) ! relative tolerance + integer(c_long) :: nState8 ! just to match nState to integer8 + real(qp) :: h_init + type(var_dlength) :: flux_sum + real(qp) :: stepsize_past + integer(i4b) :: tol_iter + integer(i4b) :: numDiscon + real(dp), allocatable :: mLayerCmpress_sum(:) + real(dp),dimension(indx_data%var(iLookINDEX%nLayers)%dat(1)) :: mLayerEnthalpy ! enthalpy of each snow+soil layer (J m-3) + + + nState8 = nState + + + ! --------------------------------------------------------------------------------------- + ! point to variables in the data structures + ! --------------------------------------------------------------------------------------- + globalVars: associate(& + ! model decisions + ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization + ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) + ! check the need to merge snow layers + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) +relConvTol_liquid => mpar_data%var(iLookPARAM%relConvTol_liquid)%dat(1) ,& ! intent(in): [dp] absolute convergence tolerance for vol frac liq water (-) + relConvTol_matric => mpar_data%var(iLookPARAM%relConvTol_matric)%dat(1) ,& ! intent(in): [dp] absolute convergence tolerance for matric head (m) + relConvTol_energy => mpar_data%var(iLookPARAM%relConvTol_energy)%dat(1) ,& ! intent(in): [dp] absolute convergence tolerance for + ! model states for the vegetation canopy + relConvTol_aquifr => mpar_data%var(iLookPARAM%relConvTol_aquifr)%dat(1) ,& ! intent(in): + ! accelerate solution for temperature + airtemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ! vector of energy and hydrology indices for the snow and soil domains + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the soil domain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology state variables in the snow+soil domain + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology state variables in the soil domain + ! mapping from full domain to the sub-domain + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b] mapping of full state vector to the state subset + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b] index of control volume for different domains (veg, snow, soil) + ! type of state and domain for a given variable + ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] type of desired model state variables + ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] domain for desired model state variables + ! layer geometry + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in): [i4b] total number of layers + ) + ! --------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="sysSolvFida/" + + ! ***** + ! (0) PRELIMINARIES... + ! ******************** + + ! ----- + ! * initialize... + ! --------------- + + ! check + if(dt < tinyStep)then + message=trim(message)//'dt is tiny' + err=20; return + endif + + ! initialize the flags + tooMuchMelt = .false. ! too much melt + reduceCoupledStep = .false. ! need to reduce the length of the coupled step + + + ! modify the groundwater representation for this single-column implementation + select case(ixSpatialGroundwater) + case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale + case(localColumn); local_ixGroundwater = ixGroundwater ! go with the specified decision + case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return + end select ! (modify the groundwater representation for this single-column implementation) + + ! allocate space for the model fluxes at the start of the time step + call allocLocal(flux_meta(:),flux_init,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! allocate space for the baseflow derivatives + ! NOTE: needs allocation because only used when baseflow sinks are active + if(ixGroundwater==qbaseTopmodel)then + allocate(dBaseflow_dMatric(nSoil,nSoil),stat=err) ! baseflow depends on total storage in the soil column, hence on matric head in every soil layer + else + allocate(dBaseflow_dMatric(0,0),stat=err) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix + end if + if(err/=0)then; err=20; message=trim(message)//'unable to allocate space for the baseflow derivatives'; return; end if + + + ! identify the matrix solution method + ! (the type of matrix used to solve the linear system A.X=B) + if(local_ixGroundwater==qbaseTopmodel .or. scalarSolution .or. forceFullMatrix)then + ixMatrix=ixFullMatrix ! named variable to denote the full Jacobian matrix + else + ixMatrix=ixBandMatrix ! named variable to denote the band-diagonal matrix + endif + + ! initialize the model fluxes (some model fluxes are not computed in the iterations) + do iVar=1,size(flux_temp%var) + flux_init%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) + end do + + ! ************************************************************************************************************************** + ! *** NUMERICAL SOLUTION FOR A GIVEN SUBSTEP AND SPLIT ********************************************************************* + ! ************************************************************************************************************************** + + ! ----- + ! * get scaling vectors... + ! ------------------------ + + ! initialize state vectors + call getScaling(& + ! input + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output + fScale, & ! intent(out): function scaling vector (mixed units) + xScale, & ! intent(out): variable scaling vector (mixed units) + sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) + dMat, & ! intent(out): diagonal of the Jacobian matrix (excludes fluxes) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + + ! initialize the trial state vectors + stateVecTrial = stateVecInit + + ! need to intialize canopy water at a positive value + if(ixVegHyd/=integerMissing)then + if(stateVecTrial(ixVegHyd) < xMinCanopyWater) stateVecTrial(ixVegHyd) = stateVecTrial(ixVegHyd) + xMinCanopyWater + endif + + ! try to accelerate solution for energy + if(ixCasNrg/=integerMissing) stateVecTrial(ixCasNrg) = stateVecInit(ixCasNrg) + (airtemp - stateVecInit(ixCasNrg))*tempAccelerate + if(ixVegNrg/=integerMissing) stateVecTrial(ixVegNrg) = stateVecInit(ixVegNrg) + (airtemp - stateVecInit(ixVegNrg))*tempAccelerate + + ! compute the flux and the residual vector for a given state vector + ! NOTE 1: The derivatives computed in eval8summa are used to calculate the Jacobian matrix for the first iteration + ! NOTE 2: The Jacobian matrix together with the residual vector is used to calculate the first iteration increment + + call eval8summa(& + ! input: model control + dt, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): number of layers + nState, & ! intent(in): number of state variables in the current subset + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVecTrial, & ! intent(in): model state vector + fScale, & ! intent(in): function scaling vector + sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): index data + ! input-output: data structures + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + ! output + feasible, & ! intent(out): flag to denote the feasibility of the solution + fluxVec0, & ! intent(out): flux vector + rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec, & ! intent(out): residual vector + fOld, & ! intent(out): function evaluation + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif + + + ! copy over the initial flux structure since some model fluxes are not computed in the iterations + do concurrent ( iVar=1:size(flux_meta) ) + flux_temp%var(iVar)%dat(:) = flux_init%var(iVar)%dat(:) + end do + + ! allocate space for the temporary flux_sum structure + call allocLocal(flux_meta(:),flux_sum,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! allocate space for mLayerCmpress_sum + allocate( mLayerCmpress_sum(nSoil) ) + + + + ! check the need to merge snow layers + if(nSnow>0)then + ! compute the energy required to melt the top snow layer (J m-2) + bulkDensity = mLayerVolFracIce(1)*iden_ice + mLayerVolFracLiq(1)*iden_water + volEnthalpy = temp2ethpy(mLayerTemp(1),bulkDensity,snowfrz_scale) + ! set flag and error codes for too much melt + if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt)then + tooMuchMelt=.true. + message=trim(message)//'net flux in the top snow layer can melt all the snow in the top layer' + err=-20; return ! negative error code to denote a warning + endif + endif + + + ! get absolute tolerances vector + call popTolFida(& + ! input + nState, & ! intent(in): number of desired state variables + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + mpar_data, & ! intent(in) + ! output + atol, & ! intent(out): absolute tolerances vector (mixed units) + rtol, & + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + +! call countDiscontinuity(& +! ! input +! stateVecTrial, & ! intent(in): model state vector (mixed units) +! diag_data, & ! intent(in): model diagnostic variables for a local HRU +! prog_data, & ! intent(in): model prognostic variables for a local HRU +! indx_data, & ! intent(in): indices defining model states and layers +! ! output +! numDiscon, & ! intent(out) +! err,message) ! intent(out): error control + !------------------- + ! * solving F(y,y') = 0 by FIDA. Here, y is the state vector + ! ------------------ + + h_init = 0 + atol = 1e-6 + rtol = 1e-6 + + do tol_iter=1,5 + + ! initialize flux_sum + do concurrent ( iVar=1:size(flux_meta) ) + flux_sum%var(iVar)%dat(:) = 0._dp + end do + + mLayerCmpress_sum(:) = 0._dp + + call fidaSolver(& + dt, & ! current time step(entire) + h_init, & ! initial stepsize + atol, & ! absolute telerance + rtol, & ! relative tolerance + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): number of layers + nState8, & ! intent(in): number of state variables in the current subset + ixMatrix, & ! intent(in): type of matrix (dense or banded) + ixQuadrature, & ! intent(in) + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + heatCapVaries, & ! intent(in): flag to indicate if heat capacity is constant in the current substep + ! input: state vectors + stateVecTrial, & ! intent(in): model state vector + fScale, & ! intent(in): function scaling vector + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + dMat, & ! intent(inout) + numDiscon, & ! intent(in) + ! input: data structures +! model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): index data + ! input-output: data structures + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_init, & ! intent(inou): + flux_temp, & ! intent(inout): model fluxes for a local HRU (initial flux structure) + flux_sum, & ! intent(inout) + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + mLayerCmpress_sum, & + ! output + tret, & ! time which the solution is returned, if successfull tret = tout + dt_last, & ! last stepsize + t_last, & + stepsize_past, & + stateVecNew, & ! intent(out): model state vector + stateVecPrime, & ! intent(out): derivative of model state vector + fluxVecNew, & ! intent(out): flux vector + resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec, & + err,cmessage) ! intent(out): error control +! if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + if (tret(1) == dt .and. err == 0)then + exit + else + atol = atol * 0.1 + rtol = rtol * 0.1 + endif + + end do ! iteration over tolerances + + + + ! check if fida is successful + if( tret(1) /= dt .or. .not.feasible )then + message=trim(message)//'fida not successful' + reduceCoupledStep = .true. + return + endif + +! write (2,*) rVec(indx_data%var(iLookINDEX%ixNrgOnly)%dat) + + if (compAverageFlux)then + select case(ixQuadrature) + case(ixRectangular) + ! add the last part of the integral, then divide by dt. Now we have average flux + do iVar=1,size(flux_meta) + flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / dt + end do + case(ixTrapezoidal) + ! add the last part of the integral, then divide by dt. Now we have average flux + do iVar=1,size(flux_meta) + flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) + flux_init%var(iVar)%dat(:) * (dt_last(1) + stepsize_past) & + + flux_temp%var(iVar)%dat(:) * dt_last(1) ) / (2.0*dt) + end do + ! check + case default; err=20; message=trim(message)//'expect case to be ixRecangular, ixTrapezoidal'; return + end select + + diag_data%var(iLookDIAG%mLayerCompress)%dat(:) = mLayerCmpress_sum(:) + endif + + ! compute the total change in storage associated with compression of the soil matrix (kg m-2) + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sum(diag_data%var(iLookDIAG%mLayerCompress)%dat(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water + + + stateVecTrial = stateVecNew + + + ! set untapped melt energy to zero + untappedMelt(:) = 0._dp + + deallocate(mLayerCmpress_sum) + + + ! end associate statements + end associate globalVars + + end subroutine sysSolvFida + +end module sysSolvFida_module diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 new file mode 100644 index 000000000..6896bfb3b --- /dev/null +++ b/build/source/engine/t2enthalpy.f90 @@ -0,0 +1,522 @@ +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . + +module t2enthalpy_module + +! constants +USE multiconst, only: gravity, & ! gravitational acceleration (m s-1) + Tfreeze, & ! freezing point of water (K) + Cp_soil,Cp_water,Cp_ice,Cp_air,& ! specific heat of soil, water and ice (J kg-1 K-1) + iden_water,iden_ice,iden_air,& ! intrinsic density of water and ice (kg m-3) + LH_fus ! latent heat of fusion (J kg-1) + +! data types +USE nrtype +USE data_types,only:var_iLength ! var(:)%dat(:) +USE data_types,only:var_dLength ! var(:)%dat(:) +USE data_types,only:zLookup ! z(:)%var(:)%lookup(:) + +! indices within parameter structure +USE var_lookup,only:iLookPARAM ! named variables to define structure element +USE var_lookup,only:iLookINDEX ! named variables to define structure element +USE var_lookup,only:iLookLOOKUP ! named variables to define structure element +USE var_lookup,only:iLookDIAG ! named variables for structure elements + +! data dimensions +USE var_lookup,only:maxvarLookup ! maximum number of variables in the lookup tables + +! domain types +USE globalData,only:iname_cas ! named variables for canopy air space +USE globalData,only:iname_veg ! named variables for vegetation canopy +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil +USE globalData,only:iname_aquifer ! named variables for the aquifer + +! named variables to describe the state variable type +USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space +USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy +USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers + +! missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing real number + +! privacy +implicit none +private +public::T2E_lookup +public::t2enthalpy + +! define the look-up table used to compute temperature based on enthalpy +contains + + + ! ************************************************************************************************************************ + ! public subroutine T2E_lookup: define a look-up table to compute enthalpy based on temperature + ! ************************************************************************************************************************ + subroutine T2E_lookup(nSoil, & ! intent(in): number of soil layers + mpar_data, & ! intent(in): parameter data structure + lookup_data, & ! intent(inout): lookup table data structure + err,message) + USE nr_utility_module,only:arth ! use to build vectors with regular increments + USE spline_int_module,only:spline,splint ! use for cubic spline interpolation + USE soil_utils_module,only:volFracLiq ! use to compute the volumetric fraction of liquid water + implicit none + ! declare dummy variables + integer(i4b),intent(in) :: nSoil + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(zLookup),intent(inout) :: lookup_data ! lookup tables + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! declare local variables + character(len=128) :: cmessage ! error message in downwind routine + logical(lgt),parameter :: doTest=.false. ! flag to test + integer(i4b),parameter :: nLook=100 ! number of elements in the lookup table + integer(i4b),parameter :: nIntegr8=10000 ! number of points used in the numerical integration + real(dp),parameter :: T_lower=260.0_dp ! lowest temperature value where all liquid water is assumed frozen (K) + real(dp),dimension(nLook) :: xTemp ! temporary vector + real(dp) :: xIncr ! temporary increment + real(dp) :: T_incr ! temperature increment + real(dp),parameter :: T_test=272.9742_dp ! test value for temperature (K) + real(dp) :: E_test ! test value for enthalpy (J m-3) + integer(i4b) :: iVar ! loop through variables + integer(i4b) :: iSoil ! loop through soil layers + integer(i4b) :: iLook ! loop through lookup table + integer(i4b) :: jIntegr8 ! index for numerical integration + logical(lgt) :: check ! flag to check allocation + real(dp) :: vGn_m ! van Genuchten "m" parameter (-) + real(dp) :: vFracLiq ! volumetric fraction of liquid water (-) + real(dp) :: vFracIce ! volumetric fraction of ice (-) + real(dp) :: matricHead ! matric head (m) + ! initialize error control + err=0; message="T2E_lookup/" + + ! get the values of temperature for the lookup table + xIncr = 1._dp/real(nLook-1, kind(dp)) + xTemp = T_lower + (Tfreeze - T_lower)*arth(0._dp,xIncr,nLook)**0.25_dp ! use **0.25 to give more values near freezing + + ! ----- + ! * allocate space for the lookup table... + ! ---------------------------------------- + + ! initialize checks + check=.false. + + ! allocate space for soil layers + if(allocated(lookup_data%z))then; check=.true.; else; allocate(lookup_data%z(nSoil), stat=err); endif + if(check) then; err=20; message=trim(message)//'lookup table z dimension was unexpectedly allocated already'; return; end if + if(err/=0)then; err=20; message=trim(message)//'problem allocating lookup table z dimension dimension'; return; end if + + ! allocate space for the variables in the lookup table + do iSoil=1,nSoil + if(allocated(lookup_data%z(iSoil)%var))then; check=.true.; else; allocate(lookup_data%z(iSoil)%var(maxvarLookup), stat=err); endif + if(check) then; err=20; message=trim(message)//'lookup table var dimension was unexpectedly allocated already'; return; end if + if(err/=0)then; err=20; message=trim(message)//'problem allocating lookup table var dimension dimension'; return; end if + + ! allocate space for the values in the lookup table + do iVar=1,maxvarLookup + if(allocated(lookup_data%z(iSoil)%var(iVar)%lookup))then; check=.true.; else; allocate(lookup_data%z(iSoil)%var(iVar)%lookup(nLook), stat=err); endif + if(check) then; err=20; message=trim(message)//'lookup table value dimension was unexpectedly allocated already'; return; end if + if(err/=0)then; err=20; message=trim(message)//'problem allocating lookup table vaule dimension dimension'; return; end if + + end do ! (looping through variables) + end do ! (looping through soil layers) + + ! loop through soil layers + do iSoil=1,nSoil + + ! ----- + ! * make association to variables in the data structures... + ! --------------------------------------------------------- + + associate(& + + ! associate model parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) , & ! scaling parameter for freezing (K-1) + soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(iSoil) , & ! intrinsic soil density (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(iSoil) , & ! soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(iSoil) , & ! volumetric residual water content (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(iSoil) , & ! van Genuchten "alpha" parameter (m-1) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(iSoil) , & ! van Genuchten "n" parameter (-) + + ! associate values in the lookup table + Tk => lookup_data%z(iSoil)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) + Ey => lookup_data%z(iSoil)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) + E2 => lookup_data%z(iSoil)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + + ) ! end associate statement + + ! compute vGn_m + vGn_m = 1._dp - 1._dp/vGn_n + + ! ----- + ! * populate the lookup table... + ! ------------------------------ + + ! initialize temperature and enthalpy + Tk(nLook) = Tfreeze + Ey(nLook) = 0._dp + + ! loop through lookup table + do iLook=(nLook-1),1,-1 + + ! update temperature and enthalpy + Tk(iLook) = Tk(iLook+1) + Ey(iLook) = Ey(iLook+1) + + ! get the temperature increment for the numerical integration + T_incr = (xTemp(iLook)-xTemp(iLook+1))/real(nIntegr8, kind(dp)) + + ! numerical integration between different values of the lookup table + do jIntegr8=1,nIntegr8 + + ! update temperature + Tk(iLook) = Tk(iLook) + T_incr + + ! compute the volumetric liquid water and ice content + ! NOTE: assume saturation + matricHead = (LH_fus/gravity)*(Tk(iLook) - Tfreeze)/Tfreeze + vFracLiq = volFracLiq(matricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + vFracIce = theta_sat - vFracLiq + + ! compute enthalpy + ! NOTE: assume intrrinsic density of ice is the intrinsic density of water + ! NOTE: kg m-3 J kg-1 K-1 K + Ey(iLook) = Ey(iLook) + iden_water*Cp_water*vFracLiq*T_incr + iden_water*Cp_ice*vFracIce*T_incr + + end do ! numerical integration + + end do ! loop through lookup table + + ! use cubic spline interpolation to obtain enthalpy values at the desired values of temperature + call spline(Tk,Ey,1.e30_dp,1.e30_dp,E2,err,cmessage) ! get the second derivatives + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! check + !print*, 'Tk = ', Tk + !print*, 'Ey = ', Ey + !print*, 'E2 = ', E2 + + ! test + if(doTest)then + + ! calculate enthalpy + call splint(Tk,Ey,E2,T_test,E_test,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! write values + print*, 'doTest = ', doTest + print*, 'T_test = ', T_test ! temperature (K) + print*, 'E_test = ', E_test ! enthalpy (J m-3) + print*, 'theta_sat = ', theta_sat ! soil porosity (-) + print*, 'theta_res = ', theta_res ! volumetric residual water content (-) + print*, 'vGn_alpha = ', vGn_alpha ! van Genuchten "alpha" parameter (m-1) + print*, 'vGn_n = ', vGn_n ! van Genuchten "n" parameter (-) + print*, trim(message)//'PAUSE: Set doTest=.false. to complete simulations' + read(*,*) + + endif ! if testing + + ! end asssociation to variables in the data structures + end associate + + end do ! (looping through soil layers) + end subroutine T2E_lookup + + + ! ************************************************************************************************************************ + ! public subroutine t2enthalpy: compute enthalpy from temperature and total water content + ! ************************************************************************************************************************ + subroutine t2enthalpy(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in) + ! output: enthalpy + scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,message) ! intent(out): error control + ! ------------------------------------------------------------------------------------------------------------------------- + ! downwind routines + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water + USE spline_int_module,only:splint ! use for cubic spline interpolation + implicit none + ! delare dummy variables + ! ------------------------------------------------------------------------------------------------------------------------- + ! input: data structures + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model indices + type(zLookup),intent(in) :: lookup_data ! lookup tables + ! input: state variables for the vegetation canopy + real(dp),intent(in) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) + real(dp),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(dp),intent(in) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + real(dp),intent(in) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + real(dp),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(dp),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(dp),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(dp),intent(in) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric fraction of Ice (-) + ! output: enthalpy + real(dp),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) + real(dp),intent(out) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) + real(dp),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------------------------------- + ! declare local variables + character(len=128) :: cmessage ! error message in downwind routine + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixDomainType ! name of a given model domain + integer(i4b) :: ixControlIndex ! index within a given model domain + real(dp) :: vGn_m ! van Genuchten "m" parameter (-) + real(dp) :: Tcrit ! temperature where all water is unfrozen (K) + real(dp) :: psiLiq ! matric head of liquid water (m) + real(dp) :: vFracWat ! volumetric fraction of total water, liquid+ice (-) + real(dp) :: vFracLiq ! volumetric fraction of liquid water (-) + real(dp) :: vFracIce ! volumetric fraction of ice (-) + real(dp) :: enthTemp ! enthalpy at the temperature of the control volume (J m-3) + real(dp) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) + real(dp) :: enthPhase ! enthalpy associated with phase change (J m-3) + real(dp) :: enthWater ! enthalpy of total water (J m-3) + real(dp) :: enthSoil ! enthalpy of soil particles (J m-3) + real(dp) :: enthMix ! enthalpy of the mixed region, liquid+ice (J m-3) + real(dp) :: enthLiq ! enthalpy of the liquid region (J m-3) + real(dp) :: enthAir ! enthalpy of air (J m-3) + real(dp) :: diffT + real(dp) :: integral + real(dp) :: enthIce + real(dp) :: enthVeg + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + generalVars: associate(& + ! number of model layers, and layer type + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + ! mapping between the full state vector and the state subset + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ! type of domain, type of state variable, and index of control volume within domain + ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) ! end associate statement + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message="t2enthalpy/" + + ! loop through model state variables + do iState=1,size(ixMapSubset2Full) + + ! ----- + ! - compute indices... + ! -------------------- + + ! get domain type, and index of the control volume within the domain + ixFullVector = ixMapSubset2Full(iState) ! index within full state vector + ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) + ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain + + ! check an energy state + if(ixStateType(ixFullVector)==iname_nrgCanair .or. ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer)then + + ! get the layer index + select case(ixDomainType) + case(iname_cas); iLayer = integerMissing + case(iname_veg); iLayer = integerMissing + case(iname_snow); iLayer = ixControlIndex + case(iname_soil); iLayer = ixControlIndex + nSnow + case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + + ! identify domain + select case(ixDomainType) + + ! ----- + ! - canopy air space... + ! --------------------- + case(iname_cas) + scalarCanairEnthalpy = Cp_air * iden_air * (scalarCanairTempTrial - Tfreeze) + ! ----- + ! - vegetation canopy... + ! ----------------------- + case(iname_veg) + ! association to necessary variables for vegetation + vegVars: associate(& + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): [dp] canopy depth (m) + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) + + diffT = scalarCanopyTempTrial - Tfreeze + enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth + if(diffT>=0._dp)then + enthLiq = Cp_water * scalarCanopyWatTrial * diffT / canopyDepth + enthIce = 0._dp + enthPhase = 0._dp + else + integral = (1._dp/snowfrz_scale) * atan(snowfrz_scale * diffT) + enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth + enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth + enthPhase = LH_fus * scalarCanopyIceTrial / canopyDepth + end if + + scalarCanopyEnthalpy = enthVeg + enthLiq + enthIce - enthPhase + + ! end association + end associate vegVars + + ! ----- + ! - snow... + ! --------- + case(iname_snow) + + ! association to necessary variables for snow + snowVars: associate(& + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) + + diffT = mLayerTempTrial(iLayer) - Tfreeze + if(diffT>=0._dp)then + enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * diffT + enthIce = 0._dp + enthAir = iden_air * Cp_air * ( 1._dp - mLayerVolFracWatTrial(iLayer) ) * diffT + enthPhase = 0._dp + else + integral = (1._dp/snowfrz_scale) * atan(snowfrz_scale * diffT) + enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral + enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) + enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) + enthPhase = iden_ice * LH_fus * mLayerVolFracIceTrial(iLayer) + end if + + mLayerEnthalpy(iLayer) = enthLiq + enthIce + enthAir - enthPhase + + ! end association + end associate snowVars + + ! ----- + ! - soil... + ! --------- + case(iname_soil) + + ! make association to variables in the data structures... + soilVars: associate(& + + ! associate model parameters + soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex) , & ! intrinsic soil density (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex) , & ! soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex) , & ! volumetric residual water content (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) + + ! associate values in the lookup table + Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) + Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) + E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + + ) ! end associate statement + + ! diagnostic variables + vGn_m = 1._dp - 1._dp/vGn_n + Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) + vFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + + + ! *** compute enthalpy of water for unfrozen conditions + if(mlayerTempTrial(iLayer) > Tcrit)then + enthWater = iden_water*Cp_water*vFracWat*(mlayerTempTrial(iLayer) - Tfreeze) ! valid for temperatures below freezing also + enthPhase = 0._dp + + ! *** compute enthalpy of water for frozen conditions + else + ! calculate enthalpy at the temperature (cubic spline interpolation) + call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),enthTemp,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! calculate enthalpy at the critical temperature (cubic spline interpolation) + call splint(Tk,Ey,E2,Tcrit,enthTcrit,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! calculate the enthalpy of water + enthMix = enthTemp - enthTcrit ! enthalpy of the liquid+ice mix + enthLiq = iden_water*Cp_water*vFracWat*(Tcrit - Tfreeze) + enthWater = enthMix + enthLiq + + ! *** compute the enthalpy associated with phase change + psiLiq = (mLayerTempTrial(iLayer) - Tfreeze)*LH_fus/(gravity*Tfreeze) + vFracLiq = volFracLiq(psiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + vFracIce = vFracWat - vFracLiq + enthPhase = iden_water*LH_fus*vFracIce + + endif ! (if frozen conditions) + + ! *** compute the enthalpy of soil + enthSoil = soil_dens_intr*Cp_soil*(1._dp - theta_sat)*(mlayerTempTrial(iLayer) - Tfreeze) + + ! *** compute the enthalpy of air + enthAir = iden_air*Cp_air*(1._dp - theta_sat - vFracWat)*(mlayerTempTrial(iLayer) - Tfreeze) + + ! *** compute the total enthalpy (J m-3) + mLayerEnthalpy(iLayer) = enthSoil + enthWater + enthAir - enthPhase + +! print*, iLayer, enthSoil, enthWater, enthAir, enthPhase + + end associate soilVars + + ! ----- + ! - checks... + ! ----------- + case(iname_aquifer); cycle ! aquifer: do nothing + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + + end if ! if an energy layer + end do ! looping through state variables + + end associate generalVars + + end subroutine t2enthalpy + +end module t2enthalpy_module diff --git a/build/source/engine/tolFida.f90 b/build/source/engine/tolFida.f90 new file mode 100644 index 000000000..477308576 --- /dev/null +++ b/build/source/engine/tolFida.f90 @@ -0,0 +1,291 @@ + + + +module tolFida_module + + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use nrtype + use fida_datatypes + +! missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing real number + +! access the global print flag +USE globalData,only:globalPrintFlag + +! domain types +USE globalData,only:iname_cas ! named variables for canopy air space +USE globalData,only:iname_veg ! named variables for vegetation canopy +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil + +! named variables to describe the state variable type +USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space +USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy +USE globalData,only:iname_watCanopy ! named variable defining the mass of total water on the vegetation canopy +USE globalData,only:iname_liqCanopy ! named variable defining the mass of liquid water on the vegetation canopy +USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers +USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers +USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers +USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers +USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers +USE globalData,only:iname_watAquifer ! named variable defining the water storage in the aquifer + +! metadata for information in the data structures +USE globalData,only:indx_meta ! metadata for the variables in the index structure + +! constants +USE multiconst,only:& + gravity, & ! acceleration of gravity (m s-2) + Tfreeze, & ! temperature at freezing (K) + Cp_air, & ! specific heat of air (J kg-1 K-1) + LH_fus, & ! latent heat of fusion (J kg-1) + iden_air, & ! intrinsic density of air (kg m-3) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water ! intrinsic density of liquid water (kg m-3) + +! provide access to the derived types to define the data structures +USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (dp) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength ! data vector with variable length dimension (dp) + +! provide access to indices that define elements of the data structures +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookDERIV ! named variables for structure elements +USE var_lookup,only:iLookPARAM ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements + + + ! privacy + implicit none + private + public::computWeightFida + public::popTolFida + + +contains + + ! ********************************************************************************************************** + ! public function computWeightFida: compute w_i = 1 / ( rtol_i * y_i + atol_i ) + ! ********************************************************************************************************** + ! Return values: + ! 0 = success, + ! -1 = non-recoverable error, NaN or negative values + ! ---------------------------------------------------------------- + integer(c_int) function computWeightFida(sunvec_y, sunvec_ewt, user_data) & + result(ierr) bind(C,name='computWeightFida') + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use fsundials_nvector_mod + use fnvector_serial_mod + use nrtype + use fida_datatypes + + !======= Declarations ========= + implicit none + + ! calling variables + type(N_Vector) :: sunvec_y ! solution N_Vector y + type(N_Vector) :: sunvec_ewt ! derivative N_Vector W + type(c_ptr), value :: user_data ! user-defined data + + + ! pointers to data in SUNDIALS vectors + type(eqnsData), pointer :: tol_data ! equations data + real(dp), pointer :: stateVec(:) + real(dp), pointer :: weightVec(:) + integer(c_int) :: iState + + !======= Internals ============ + + ! get equations data from user-defined data + call c_f_pointer(user_data, tol_data) + + + ! get data arrays from SUNDIALS vectors + stateVec => FN_VGetArrayPointer(sunvec_y) + weightVec => FN_VGetArrayPointer(sunvec_ewt) + + + do iState = 1,tol_data%nState + weightVec(iState) = tol_data%rtol(iState) * abs( stateVec(iState) ) + tol_data%atol(iState) + weightVec(iState) = 1._dp / weightVec(iState) + end do + + ierr = 0 + return + + end function computWeightFida + + + ! ********************************************************************************************************** + ! public subroutine popTolFida: populate tolerances for state vectors + ! ********************************************************************************************************** + subroutine popTolFida(& + ! input: data structures + nState, & ! intent(in): number of desired state variables + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + mpar_data, & ! intent(in) + ! output + absTol, & ! intent(out): model state vector + relTol, & + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- + ! input: data structures + integer(i4b),intent(in) :: nState ! number of desired state variables + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(in) :: mpar_data ! model parameters + ! output + real(dp),intent(out) :: absTol(:) ! model state vector (mixed units) + real(dp),intent(out) :: relTol(:) ! model state vector (mixed units) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + ! -------------------------------------------------------------------------------------------------------------------------------- + ! state subsets + integer(i4b) :: iState ! index of state within the snow+soil domain + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + integer(i4b) :: ixStateSubset ! index within the state subset + logical(lgt),dimension(nState) :: tolFlag ! flag to denote that the state is populated + real(dp) :: absTolTempCas = 1e-6 + real(dp) :: relTolTempCas = 1e-6 + real(dp) :: absTolTempVeg = 1e-6 + real(dp) :: relTolTempVeg = 1e-6 + real(dp) :: absTolWatVeg = 1e-6 + real(dp) :: relTolWatVeg = 1e-6 + real(dp) :: absTolTempSoilSnow = 1e-6 + real(dp) :: relTolTempSoilSnow = 1e-6 + real(dp) :: absTolWatSnow = 1e-6 + real(dp) :: relTolWatSnow = 1e-6 + real(dp) :: absTolMatric = 1e-6 + real(dp) :: relTolMatric = 1e-6 + real(dp) :: absTolAquifr = 1e-6 + real(dp) :: relTolAquifr = 1e-6 + + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + fixedLength: associate(& + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in) : [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in) : [dp] temperature of the vegetation canopy (K) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in) : [dp] mass of total water on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in) : [dp] mass of liquid water on the vegetation canopy (kg m-2) + ! model state variable vectors for the snow-soil layers + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in) : [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in) : [dp(:)] volumetric fraction of total water (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in) : [dp(:)] volumetric fraction of liquid water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in) : [dp(:)] matric head (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in) : [dp(:)] matric potential of liquid water (m) + ! model state variables for the aquifer + scalarAquiferStorage=> prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in) : [dp] storage of water in the aquifer (m) + ! indices defining specific model states + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy hydrology state variable (mass) + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of aquifer storage state variable + ! vector of energy and hydrology indices for the snow and soil domains + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in) : [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in) : [i4b] number of hydrology state variables in the snow+soil domain + ! type of model state variabless + ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in) : [i4b(:)] [state subset] type of desired model state variables + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in) : [i4b(:)] index of the type of hydrology states in snow+soil domain + ! number of layers + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in) : [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in) : [i4b] number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in) : [i4b] total number of layers + ) ! end association with variables in the data structures + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='popTolFida/' + + ! ----- + ! * initialize state vectors... + ! ----------------------------- + + ! initialize flags + tolFlag(:) = .false. + + ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer canopy + do concurrent (iState=1:size(ixCasNrg),ixCasNrg(iState)/=integerMissing) + absTol( ixCasNrg(iState) ) = absTolTempCas ! transfer canopy air temperature to the state vector + relTol( ixCasNrg(iState) ) = relTolTempCas + tolFlag( ixCasNrg(iState) ) = .true. ! flag to denote that tolerances are populated + end do + + ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer canopy + do concurrent (iState=1:size(ixVegNrg),ixVegNrg(iState)/=integerMissing) + absTol( ixVegNrg(iState) ) = absTolTempVeg ! transfer vegetation temperature to the state vector + relTol( ixVegNrg(iState) ) = relTolTempVeg ! transfer vegetation temperature to the state vector + tolFlag( ixVegNrg(iState) ) = .true. ! flag to denote that tolerances are populated + end do + + ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer canopy + do concurrent (iState=1:size(ixVegHyd),ixVegHyd(iState)/=integerMissing) + tolFlag( ixVegHyd(iState) ) = .true. ! flag to denote that tolerances are populated + select case(ixStateType_subset( ixVegHyd(iState) )) + case(iname_watCanopy); absTol( ixVegHyd(iState) ) = absTolWatVeg ; relTol( ixVegHyd(iState) ) = relTolWatVeg + case(iname_liqCanopy); absTol( ixVegHyd(iState) ) = absTolWatVeg ; relTol( ixVegHyd(iState) ) = relTolWatVeg ! transfer liquid canopy water to the state vector + case default; tolFlag( ixVegHyd(iState) ) = .false. ! flag to denote that tolerances are populated + end select + end do + + ! tolerance for tempreture of the snow and soil domain + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + ixStateSubset = ixSnowSoilNrg(iLayer) ! index within the state vector + absTol(ixStateSubset) = absTolTempSoilSnow ! transfer temperature from a layer to the state vector + relTol(ixStateSubset) = relTolTempSoilSnow + tolFlag(ixStateSubset) = .true. ! flag to denote that tolerances are populated + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! NOTE: ixVolFracWat and ixVolFracLiq can also include states in the soil domain, hence enable primary variable switching + if(nSnowSoilHyd>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) + ixStateSubset = ixSnowSoilHyd(iLayer) ! index within the state vector + tolFlag(ixStateSubset) = .true. ! flag to denote that tolerances are populated + select case( ixHydType(iLayer) ) + case(iname_watLayer); absTol(ixStateSubset) = absTolWatSnow ; relTol(ixStateSubset) = relTolWatSnow + case(iname_liqLayer); absTol(ixStateSubset) = absTolWatSnow ; relTol(ixStateSubset) = relTolWatSnow + case(iname_matLayer); absTol(ixStateSubset) = absTolMatric ; relTol(ixStateSubset) = relTolMatric + case(iname_lmpLayer); absTol(ixStateSubset) = absTolMatric ; relTol(ixStateSubset) = relTolMatric + case default; tolFlag(ixStateSubset) = .false. ! flag to denote that tolerances are populated + end select + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! build the state vector for the aquifer storage + ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer aquifer + do concurrent (iState=1:size(ixAqWat),ixAqWat(iState)/=integerMissing) + absTol( ixAqWat(iState) ) = absTolAquifr ! transfer aquifer storage to the state vector + relTol( ixAqWat(iState) ) = relTolAquifr + tolFlag( ixAqWat(iState) ) = .true. ! flag to denote that tolerances are populated + end do + + ! check that we specified tolerances for all state variables + if(count(tolFlag)/=nState)then + print*, 'tolFlag = ', tolFlag + message=trim(message)//'tolerances not specified for some state variables' + err=20; return + endif + + end associate fixedLength ! end association to variables in the data structure where vector length does not change + end subroutine popTolFida + + +end module tolFida_module diff --git a/build/source/engine/updatStateFida.f90 b/build/source/engine/updatStateFida.f90 new file mode 100644 index 000000000..576ee2828 --- /dev/null +++ b/build/source/engine/updatStateFida.f90 @@ -0,0 +1,330 @@ + +module updatStateFida_module +USE nrtype +! physical constants +USE multiconst,only:& + Tfreeze, & ! freezing point of pure water (K) + iden_air, & ! intrinsic density of air (kg m-3) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water, & ! intrinsic density of water (kg m-3) + gravity, & ! gravitational acceleteration (m s-2) + LH_fus ! latent heat of fusion (J kg-1) +implicit none +private +public::updateSnowFida +public::updateSoilFida +public::updateSoilFida2 +public::updateVegFida + +real(dp),parameter :: verySmall=epsilon(1.0_dp) ! a very small number (used to avoid divide by zero) + +contains + + + ! ************************************************************************************************************* + ! public subroutine updateVegFida: compute phase change impacts on volumetric liquid water and ice + ! Input: Theta * canopyDepth * iden_water + ! Outputs: VolFracLiq * canopyDepth * iden_water and VolFracIce * canopyDepth * iden_ice + ! ************************************************************************************************************* + subroutine updateVegFida(& + ! input + Temp ,& ! intent(in): temperature (K) + Theta ,& ! intent(in): volume fraction of total water (-) + snowfrz_scale ,& ! intent(in): scaling parameter for the snow freezing curve (K-1) + TempPrime ,& ! intent(in): temperature (K) + ThetaPrime ,& ! intent(in): volume fraction of total water (-) + ! output + VolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) + VolFracIce ,& ! intent(out): volumetric fraction of ice (-) + VolFracLiqPrime ,& ! intent(out): volumetric fraction of liquid water (-) + VolFracIcePrime ,& ! intent(out): volumetric fraction of ice (-) + fLiq ,& ! intent(out): fraction of liquid water (-) + err,message) ! intent(out): error control + ! utility routines + USE snow_utils_module,only:fracliquid ! compute volumetric fraction of liquid water + USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) + implicit none + ! input variables + real(dp),intent(in) :: Temp ! temperature (K) + real(dp),intent(in) :: Theta ! volume fraction of total water (-) + real(dp),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) + real(dp),intent(in) :: TempPrime ! temperature (K) + real(dp),intent(in) :: ThetaPrime ! volume fraction of total water (-) + ! output variables + real(dp),intent(out) :: VolFracLiq ! volumetric fraction of liquid water (-) + real(dp),intent(out) :: VolFracIce ! volumetric fraction of ice (-) + real(dp),intent(out) :: VolFracLiqPrime ! volumetric fraction of liquid water (-) + real(dp),intent(out) :: VolFracIcePrime ! volumetric fraction of ice (-) + real(dp),intent(out) :: fLiq ! fraction of liquid water (-) + ! error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! initialize error control + err=0; message="updateVegFida/" + + ! compute the volumetric fraction of liquid water and ice (-) + fLiq = fracliquid(Temp,snowfrz_scale) + VolFracLiq = fLiq*Theta + VolFracIce = (1._dp - fLiq)*Theta + VolFracLiqPrime = fLiq * ThetaPrime + dFracLiq_dTk(Temp,snowfrz_scale) * Theta * TempPrime + VolFracIcePrime = ( ThetaPrime - VolFracLiqPrime ) + + + end subroutine updateVegFida + + + ! ************************************************************************************************************* + ! public subroutine updateSnowFida: compute phase change impacts on volumetric liquid water and ice + ! ************************************************************************************************************* + subroutine updateSnowFida(& + ! input + mLayerTemp ,& ! intent(in): temperature (K) + mLayerTheta ,& ! intent(in): volume fraction of total water (-) + snowfrz_scale ,& ! intent(in): scaling parameter for the snow freezing curve (K-1) + mLayerTempPrime ,& ! intent(in): temperature (K) + mLayerThetaPrime ,& ! intent(in): volume fraction of total water (-) + ! output + mLayerVolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) + mLayerVolFracIce ,& ! intent(out): volumetric fraction of ice (-) + mLayerVolFracLiqPrime ,& ! intent(out): volumetric fraction of liquid water (-) + mLayerVolFracIcePrime ,& ! intent(out): volumetric fraction of ice (-) + fLiq ,& ! intent(out): fraction of liquid water (-) + err,message) ! intent(out): error control + ! utility routines + USE snow_utils_module,only:fracliquid ! compute volumetric fraction of liquid water + USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) + implicit none + ! input variables + real(dp),intent(in) :: mLayerTemp ! temperature (K) + real(dp),intent(in) :: mLayerTheta ! volume fraction of total water (-) + real(dp),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) + real(dp),intent(in) :: mLayerTempPrime ! temperature (K) + real(dp),intent(in) :: mLayerThetaPrime ! volume fraction of total water (-) + ! output variables + real(dp),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) + real(dp),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) + real(dp),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water (-) + real(dp),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice (-) + real(dp),intent(out) :: fLiq ! fraction of liquid water (-) + ! error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! initialize error control + err=0; message="updateSnowFida/" + + ! compute the volumetric fraction of liquid water and ice (-) + fLiq = fracliquid(mLayerTemp,snowfrz_scale) + mLayerVolFracLiq = fLiq*mLayerTheta + mLayerVolFracIce = (1._dp - fLiq)*mLayerTheta*(iden_water/iden_ice) + mLayerVolFracLiqPrime = fLiq * mLayerThetaPrime + dFracLiq_dTk(mLayerTemp,snowfrz_scale) * mLayerTheta * mLayerTempPrime + mLayerVolFracIcePrime = ( mLayerThetaPrime - mLayerVolFracLiqPrime ) * (iden_water/iden_ice) + + + end subroutine updateSnowFida + + ! ************************************************************************************************************* + ! public subroutine updateSoilFida: compute phase change impacts on matric head and volumetric liquid water and ice + ! ************************************************************************************************************* + subroutine updateSoilFida(& + ! input + dt_cur, & + mLayerTemp ,& ! intent(in): temperature vector (K) + mLayerMatricHead ,& ! intent(in): matric head (m) + mLayerMatricHeadPrev ,& + mLayerVolFracWatPrev ,& + mLayerTempPrime ,& ! intent(in) + mLayerMatricHeadPrime, & ! intent(in) + vGn_alpha ,& ! intent(in): van Genutchen "alpha" parameter + vGn_n ,& ! intent(in): van Genutchen "n" parameter + theta_sat ,& ! intent(in): soil porosity (-) + theta_res ,& ! intent(in): soil residual volumetric water content (-) + vGn_m ,& ! intent(in): van Genutchen "m" parameter (-) + ! output + mLayerVolFracWat ,& ! intent(out): volumetric fraction of total water (-) + mLayerVolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) + mLayerVolFracIce ,& ! intent(out): volumetric fraction of ice (-) + mLayerVolFracWatPrime ,& ! intent(out): volumetric fraction of total water (-) + mLayerVolFracLiqPrime ,& ! intent(out): volumetric fraction of liquid water (-) + mLayerVolFracIcePrime ,& ! intent(out): volumetric fraction of ice (-) + err,message) ! intent(out): error control + ! utility routines + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water based on matric head + USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric liquid water content + USE soil_utils_module,only:dTheta_dPsi + implicit none + ! input variables + real(dp),intent(in) :: dt_cur + real(dp),intent(in) :: mLayerTemp ! estimate of temperature (K) + real(dp),intent(in) :: mLayerMatricHead ! matric head (m) + real(dp),intent(in) :: mLayerMatricHeadPrev ! matric head (m) + real(dp),intent(in) :: mLayerVolFracWatPrev + real(dp),intent(in) :: mLayerTempPrime + real(dp),intent(in) :: mLayerMatricHeadPrime ! matric head (m) + real(dp),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter + real(dp),intent(in) :: vGn_n ! van Genutchen "n" parameter + real(dp),intent(in) :: theta_sat ! soil porosity (-) + real(dp),intent(in) :: theta_res ! soil residual volumetric water content (-) + real(dp),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) + ! output variables + real(dp),intent(out) :: mLayerVolFracWat ! fractional volume of total water (-) + real(dp),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) + real(dp),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) + real(dp),intent(out) :: mLayerVolFracWatPrime ! fractional volume of total water (-) + real(dp),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water (-) + real(dp),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice (-) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! define local variables + real(dp) :: TcSoil ! critical soil temperature when all water is unfrozen (K) + real(dp) :: xConst ! constant in the freezing curve function (m K-1) + real(dp) :: mLayerPsiLiq ! liquid water matric potential (m) + ! initialize error control + err=0; message="updateSoilFida/" + + ! compute fractional **volume** of total water (liquid plus ice) + mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) +! mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime +! mLayerVolFracWatPrime = mLayerMatricHeadPrime * (mLayerVolFracWat - mLayerVolFracWatPrev) / (mLayerMatricHead - mLayerMatricHeadPrev + verySmall) + mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev) / dt_cur + + + + + if(mLayerVolFracWat > theta_sat)then; err=20; message=trim(message)//'volume of liquid and ice exceeds porosity'; return; end if + + ! compute the critical soil temperature where all water is unfrozen (K) + ! (eq 17 in Dall'Amico 2011) + TcSoil = Tfreeze + min(mLayerMatricHead,0._dp)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) + + ! *** compute volumetric fraction of liquid water and ice for partially frozen soil + if(mLayerTemp < TcSoil)then ! (check if soil temperature is less than the critical temperature) + ! - volumetric liquid water content (-) + ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice + ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations + xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) + mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution + mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + if(mLayerPsiLiq<0._dp)then + mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime + else + mLayerVolFracLiqPrime = 0._dp + endif + + ! - volumetric ice content (-) + mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq + mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime + + + ! *** compute volumetric fraction of liquid water and ice for unfrozen soil + else + + ! all water is unfrozen + mLayerVolFracLiq = mLayerVolFracWat + mLayerVolFracIce = 0._dp + + mLayerVolFracLiqPrime = mLayerVolFracWatPrime + mLayerVolFracIcePrime = 0._dp + + end if ! (check if soil is partially frozen) + + + end subroutine updateSoilFida + + ! ************************************************************************************************************* + ! public subroutine updateSoilFida: compute phase change impacts on matric head and volumetric liquid water and ice + ! ************************************************************************************************************* + subroutine updateSoilFida2(& + ! input + mLayerTemp ,& ! intent(in): temperature vector (K) + mLayerMatricHead ,& ! intent(in): matric head (m) + mLayerTempPrime ,& ! intent(in) + mLayerMatricHeadPrime, & ! intent(in) + vGn_alpha ,& ! intent(in): van Genutchen "alpha" parameter + vGn_n ,& ! intent(in): van Genutchen "n" parameter + theta_sat ,& ! intent(in): soil porosity (-) + theta_res ,& ! intent(in): soil residual volumetric water content (-) + vGn_m ,& ! intent(in): van Genutchen "m" parameter (-) + ! output + mLayerVolFracWat ,& ! intent(out): volumetric fraction of total water (-) + mLayerVolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) + mLayerVolFracIce ,& ! intent(out): volumetric fraction of ice (-) + mLayerVolFracWatPrime ,& ! intent(out): volumetric fraction of total water (-) + mLayerVolFracLiqPrime ,& ! intent(out): volumetric fraction of liquid water (-) + mLayerVolFracIcePrime ,& ! intent(out): volumetric fraction of ice (-) + err,message) ! intent(out): error control + ! utility routines + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water based on matric head + USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric liquid water content + USE soil_utils_module,only:dTheta_dPsi + implicit none + ! input variables + real(dp),intent(in) :: mLayerTemp ! estimate of temperature (K) + real(dp),intent(in) :: mLayerMatricHead ! matric head (m) + real(dp),intent(in) :: mLayerTempPrime + real(dp),intent(in) :: mLayerMatricHeadPrime ! matric head (m) + real(dp),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter + real(dp),intent(in) :: vGn_n ! van Genutchen "n" parameter + real(dp),intent(in) :: theta_sat ! soil porosity (-) + real(dp),intent(in) :: theta_res ! soil residual volumetric water content (-) + real(dp),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) + ! output variables + real(dp),intent(out) :: mLayerVolFracWat ! fractional volume of total water (-) + real(dp),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) + real(dp),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) + real(dp),intent(out) :: mLayerVolFracWatPrime ! fractional volume of total water (-) + real(dp),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water (-) + real(dp),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice (-) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! define local variables + real(dp) :: TcSoil ! critical soil temperature when all water is unfrozen (K) + real(dp) :: xConst ! constant in the freezing curve function (m K-1) + real(dp) :: mLayerPsiLiq ! liquid water matric potential (m) + ! initialize error control + err=0; message="updateSoilFida2/" + + ! compute fractional **volume** of total water (liquid plus ice) + mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime + + if(mLayerVolFracWat > theta_sat)then; err=20; message=trim(message)//'volume of liquid and ice exceeds porosity'; return; end if + + + ! compute the critical soil temperature where all water is unfrozen (K) + ! (eq 17 in Dall'Amico 2011) + TcSoil = Tfreeze + min(mLayerMatricHead,0._dp)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) + + ! *** compute volumetric fraction of liquid water and ice for partially frozen soil + if(mLayerTemp < TcSoil)then ! (check if soil temperature is less than the critical temperature) + ! - volumetric liquid water content (-) + ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice + ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations + xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) + mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution + mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + if(mLayerPsiLiq<0._dp)then + mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime + else + mLayerVolFracLiqPrime = 0._dp + endif + + ! - volumetric ice content (-) + mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq + mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime + + + ! *** compute volumetric fraction of liquid water and ice for unfrozen soil + else + + ! all water is unfrozen + mLayerVolFracLiq = mLayerVolFracWat + mLayerVolFracIce = 0._dp + + mLayerVolFracLiqPrime = mLayerVolFracWatPrime + mLayerVolFracIcePrime = 0._dp + + end if ! (check if soil is partially frozen) + + + end subroutine updateSoilFida2 +end module updatStateFida_module diff --git a/build/source/engine/updateVarsFida.f90 b/build/source/engine/updateVarsFida.f90 new file mode 100644 index 000000000..3b76d86e1 --- /dev/null +++ b/build/source/engine/updateVarsFida.f90 @@ -0,0 +1,655 @@ +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2015 NCAR/RAL +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . + +module updateVarsFida_module + +! data types +USE nrtype + +! missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing real number + +! access the global print flag +USE globalData,only:globalPrintFlag + +! domain types +USE globalData,only:iname_cas ! named variables for canopy air space +USE globalData,only:iname_veg ! named variables for vegetation canopy +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil +USE globalData,only:iname_aquifer ! named variables for the aquifer + +! named variables to describe the state variable type +USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space +USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy +USE globalData,only:iname_watCanopy ! named variable defining the mass of total water on the vegetation canopy +USE globalData,only:iname_liqCanopy ! named variable defining the mass of liquid water on the vegetation canopy +USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers +USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers +USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers +USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers +USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers + +! metadata for information in the data structures +USE globalData,only:indx_meta ! metadata for the variables in the index structure + +! constants +USE multiconst,only:& + gravity, & ! acceleration of gravity (m s-2) + Tfreeze, & ! temperature at freezing (K) + Cp_air, & ! specific heat of air (J kg-1 K-1) + LH_fus, & ! latent heat of fusion (J kg-1) + iden_air, & ! intrinsic density of air (kg m-3) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water ! intrinsic density of liquid water (kg m-3) + +! provide access to the derived types to define the data structures +USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (dp) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength ! data vector with variable length dimension (dp) + +! provide access to indices that define elements of the data structures +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookDERIV ! named variables for structure elements +USE var_lookup,only:iLookPARAM ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements + +! provide access to routines to update states +USE updatStateFida_module,only:updateVegFida ! update snow states +USE updatStateFida_module,only:updateSnowFida ! update snow states +USE updatStateFida_module,only:updateSoilFida ! update soil states + +! provide access to functions for the constitutive functions and derivatives +USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) +USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) +USE soil_utils_module,only:dTheta_dTk ! differentiate the freezing curve w.r.t. temperature (soil) +USE soil_utils_module,only:dTheta_dPsi ! derivative in the soil water characteristic (soil) +USE soil_utils_module,only:dPsi_dTheta ! derivative in the soil water characteristic (soil) +USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content +USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water +USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists +USE soil_utils_fida_module,only:liquidHeadFida ! compute the liquid water matric potential + +! IEEE checks +USE, intrinsic :: ieee_arithmetic ! check values (NaN, etc.) + +implicit none +private +public::updateVarsFida + +contains + + ! ********************************************************************************************************** + ! public subroutine updateVarsFida: compute diagnostic variables + ! ********************************************************************************************************** + subroutine updateVarsFida(& + ! input + dt_cur, & + do_adjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + mLayerVolFracWatPrev, & ! intent(in) + mLayerMatricHeadPrev, & ! intent(in) + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output: variables for the vegetation canopy + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempPrime, & ! reza + mLayerVolFracWatPrime, & ! reza + mLayerVolFracLiqPrime, & ! reza + mLayerVolFracIcePrime, & ! reza + mLayerMatricHeadPrime, & ! reza + mLayerMatricHeadLiqPrime, & ! reza + ! output: error control + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input + real(dp),intent(in) :: dt_cur + logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze + type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + real(dp),intent(in) :: mLayerMatricHeadPrev(:) + real(dp),intent(in) :: mLayerVolFracWatPrev(:) + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + ! output: variables for the vegetation canopy + real(dp),intent(inout) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(dp),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + real(dp),intent(inout) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) + real(dp),intent(inout) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + + real(dp),intent(inout) :: scalarCanopyTempPrime ! trial value of canopy temperature (K) + real(dp),intent(inout) :: scalarCanopyWatPrime ! trial value of canopy total water (kg m-2) + real(dp),intent(inout) :: scalarCanopyLiqPrime ! trial value of canopy liquid water (kg m-2) + real(dp),intent(inout) :: scalarCanopyIcePrime ! trial value of canopy ice content (kg m-2) + ! output: variables for the snow-soil domain + real(dp),intent(inout) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(dp),intent(inout) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(dp),intent(inout) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) + real(dp),intent(inout) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric ice water content (-) + real(dp),intent(inout) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(dp),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) + + real(dp),intent(inout) :: mLayerTempPrime(:) + real(dp),intent(inout) :: mLayerVolFracWatPrime(:) ! reza + real(dp),intent(inout) :: mLayerVolFracLiqPrime(:) ! reza + real(dp),intent(inout) :: mLayerVolFracIcePrime(:) ! reza + real(dp),intent(inout) :: mLayerMatricHeadPrime(:) ! reza + real(dp),intent(inout) :: mLayerMatricHeadLiqPrime(:) ! reza + + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! general local variables + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixDomainType ! name of a given model domain + integer(i4b) :: ixControlIndex ! index within a given model domain + integer(i4b) :: ixOther,ixOtherLocal ! index of the coupled state variable within the (full, local) vector + logical(lgt) :: isCoupled ! .true. if a given variable shared another state variable in the same control volume + logical(lgt) :: isNrgState ! .true. if a given variable is an energy state + logical(lgt),allocatable :: computedCoupling(:) ! .true. if computed the coupling for a given state variable + real(dp) :: scalarVolFracLiq ! volumetric fraction of liquid water (-) + real(dp) :: scalarVolFracIce ! volumetric fraction of ice (-) + real(dp) :: scalarVolFracLiqPrime ! volumetric fraction of liquid water (-) + real(dp) :: scalarVolFracIcePrime ! volumetric fraction of ice (-) + real(dp) :: Tcrit ! critical soil temperature below which ice exists (K) + real(dp) :: xTemp ! temporary temperature (K) + real(dp) :: effSat ! effective saturation (-) + real(dp) :: avPore ! available pore space (-) + character(len=256) :: cMessage ! error message of downwind routine + logical(lgt),parameter :: printFlag=.false. ! flag to turn on printing + ! iterative solution for temperature + real(dp) :: meltNrg ! energy for melt+freeze (J m-3) + real(dp) :: residual ! residual in the energy equation (J m-3) + real(dp) :: derivative ! derivative in the energy equation (J m-3 K-1) + real(dp) :: tempInc ! iteration increment (K) + integer(i4b) :: iter ! iteration index + integer(i4b) :: niter ! number of iterations + integer(i4b),parameter :: maxiter=100 ! maximum number of iterations + real(dp),parameter :: nrgConvTol=1.e-4_dp ! convergence tolerance for energy (J m-3) + real(dp),parameter :: tempConvTol=1.e-6_dp ! convergence tolerance for temperature (K) + real(dp) :: critDiff ! temperature difference from critical (K) + real(dp) :: tempMin ! minimum bracket for temperature (K) + real(dp) :: tempMax ! maximum bracket for temperature (K) + logical(lgt) :: bFlag ! flag to denote that iteration increment was constrained using bi-section + real(dp),parameter :: epsT=1.e-7_dp ! small interval above/below critical temperature (K) + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + associate(& + ! number of model layers, and layer type + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! indices defining model states and layers + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ! indices in the full vector for specific domains + ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain + ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + ! mapping between the full state vector and the state subset + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ! type of domain, type of state variable, and index of control volume within domain + ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ! depth-varying model parameters + vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) + ! model diagnostic variables (heat capacity) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(in): [dp ] volumetric heat capacity of the vegetation (J m-3 K-1) + mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] volumetric heat capacity in each layer (J m-3 K-1) + ! model diagnostic variables (fraction of liquid water) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) + ! model states for the vegetation canopy + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variable vectors for the snow-soil layers + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] total water matric potential (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) + ! model diagnostic variables from a previous solution + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + ! derivatives + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential + dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) + dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(out): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) & ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature + ) ! association with variables in the data structures + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message='updateVarsFida/' + + ! allocate space and assign values to the flag vector + allocate(computedCoupling(size(ixMapSubset2Full)),stat=err) ! .true. if computed the coupling for a given state variable + if(err/=0)then; message=trim(message)//'problem allocating computedCoupling'; return; endif + computedCoupling(:)=.false. + + ! loop through model state variables + do iState=1,size(ixMapSubset2Full) + + ! check the need for the computations + if(computedCoupling(iState)) cycle + + ! ----- + ! - compute indices... + ! -------------------- + + ! get domain type, and index of the control volume within the domain + ixFullVector = ixMapSubset2Full(iState) ! index within full state vector + ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) + ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain + + ! get the layer index + select case(ixDomainType) + case(iname_cas); cycle ! canopy air space: do nothing + case(iname_veg); iLayer = 0 + case(iname_snow); iLayer = ixControlIndex + case(iname_soil); iLayer = ixControlIndex + nSnow + case(iname_aquifer); cycle ! aquifer: do nothing + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + + ! get the index of the other (energy or mass) state variable within the full state vector + select case(ixDomainType) + case(iname_veg) ; ixOther = merge(ixHydCanopy(1), ixNrgCanopy(1), ixStateType(ixFullVector)==iname_nrgCanopy) + case(iname_snow, iname_soil); ixOther = merge(ixHydLayer(iLayer),ixNrgLayer(iLayer),ixStateType(ixFullVector)==iname_nrgLayer) + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select + + ! get the index in the local state vector + ixOtherLocal = ixMapFull2Subset(ixOther) ! ixOtherLocal could equal integerMissing + if(ixOtherLocal/=integerMissing) computedCoupling(ixOtherLocal)=.true. + + ! check if we have a coupled solution + isCoupled = (ixOtherLocal/=integerMissing) + + ! check if we are an energy state + isNrgState = (ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer) + + if(printFlag)then + print*, 'iState = ', iState, size(ixMapSubset2Full) + print*, 'ixFullVector = ', ixFullVector + print*, 'ixDomainType = ', ixDomainType + print*, 'ixControlIndex = ', ixControlIndex + print*, 'ixOther = ', ixOther + print*, 'ixOtherLocal = ', ixOtherLocal + print*, 'do_adjustTemp = ', do_adjustTemp + print*, 'isCoupled = ', isCoupled + print*, 'isNrgState = ', isNrgState + endif + + + + ! ======================================================================================================================================= + ! ======================================================================================================================================= + ! ======================================================================================================================================= + ! ======================================================================================================================================= + ! ======================================================================================================================================= + ! ======================================================================================================================================= + + ! update hydrology state variables for the uncoupled solution + if(.not.isNrgState .and. .not.isCoupled)then + + stop 1 + + ! update the total water from volumetric liquid water + if(ixStateType(ixFullVector)==iname_liqCanopy .or. ixStateType(ixFullVector)==iname_liqLayer)then + select case(ixDomainType) + case(iname_veg) + scalarCanopyWatTrial = scalarCanopyLiqTrial + scalarCanopyIceTrial + scalarCanopyWatPrime = scalarCanopyLiqPrime + scalarCanopyIcePrime + case(iname_snow) + mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer)*iden_ice/iden_water + mLayerVolFracWatPrime(iLayer) = mLayerVolFracLiqPrime(iLayer) + mLayerVolFracIcePrime(iLayer)*iden_ice/iden_water + case(iname_soil) + mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer) ! no volume expansion + mLayerVolFracWatPrime(iLayer) = mLayerVolFracLiqPrime(iLayer) + mLayerVolFracIcePrime(iLayer) + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, or iname_soil'; return + end select + endif + + ! update the total water and the total water matric potential + if(ixDomainType==iname_soil)then + select case( ixStateType(ixFullVector) ) + ! --> update the total water from the liquid water matric potential + case(iname_lmpLayer) + + effSat = volFracLiq(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._dp,1._dp,vGn_n(ixControlIndex),vGn_m(ixControlIndex)) ! effective saturation + avPore = theta_sat(ixControlIndex) - mLayerVolFracIceTrial(iLayer) - theta_res(ixControlIndex) ! available pore space + mLayerVolFracLiqTrial(iLayer) = effSat*avPore + theta_res(ixControlIndex) + mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer) ! no volume expansion + mLayerVolFracWatPrime(iLayer) = mLayerVolFracLiqPrime(iLayer) + mLayerVolFracIcePrime(iLayer) + mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + mLayerMatricHeadPrime(ixControlIndex) = dPsi_dTheta(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) * mLayerVolFracWatPrime(iLayer) + !write(*,'(a,1x,i4,1x,3(f20.10,1x))') 'mLayerVolFracLiqTrial(iLayer) 1 = ', iLayer, mLayerVolFracLiqTrial(iLayer), mLayerVolFracIceTrial(iLayer), mLayerVolFracWatTrial(iLayer) + ! --> update the total water from the total water matric potential + case(iname_matLayer) + + mLayerVolFracWatTrial(iLayer) = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + mLayerVolFracWatPrime(iLayer) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) *mLayerMatricHeadPrime(ixControlIndex) + ! --> update the total water matric potential (assume already have mLayerVolFracWatTrial given block above) + case(iname_liqLayer, iname_watLayer) + + mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + mLayerMatricHeadPrime(ixControlIndex) = dPsi_dTheta(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) * mLayerVolFracWatPrime(iLayer) + case default; err=20; message=trim(message)//'expect iname_lmpLayer, iname_matLayer, iname_liqLayer, or iname_watLayer'; return + end select + endif ! if in the soil domain + + endif ! if hydrology state variable or uncoupled solution + + + ! compute the critical soil temperature below which ice exists + select case(ixDomainType) + case(iname_veg, iname_snow); Tcrit = Tfreeze + case(iname_soil); Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select + + ! initialize temperature + select case(ixDomainType) + case(iname_veg); xTemp = scalarCanopyTempTrial + case(iname_snow, iname_soil); xTemp = mLayerTempTrial(iLayer) + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select + + ! define brackets for the root + ! NOTE: start with an enormous range; updated quickly in the iterations + tempMin = xTemp - 10._dp + tempMax = xTemp + 10._dp + + ! get iterations (set to maximum iterations if adjusting the temperature) + niter = merge(maxiter, 1, do_adjustTemp) + + ! iterate + iterations: do iter=1,niter + + ! restrict temperature + if(xTemp <= tempMin .or. xTemp >= tempMax)then + xTemp = 0.5_dp*(tempMin + tempMax) ! new value + bFlag = .true. + else + bFlag = .false. + endif + + ! ----- + ! - compute derivatives... + ! ------------------------ + + ! compute the derivative in total water content w.r.t. total water matric potential (m-1) + ! NOTE 1: valid for frozen and unfrozen conditions + ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi + if(ixDomainType==iname_soil)then + select case( ixStateType(ixFullVector) ) + case(iname_lmpLayer); dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._dp,1._dp,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + case default; dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + end select + endif + + ! compute the derivative in liquid water content w.r.t. temperature + ! --> partially frozen: dependence of liquid water on temperature + if(xTemp unfrozen: no dependence of liquid water on temperature + else + select case(ixDomainType) + case(iname_veg); dTheta_dTkCanopy = 0._dp + case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._dp + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select ! domain type + endif + + + + + ! ----- + ! - update volumetric fraction of liquid water and ice... + ! => case of hydrology state uncoupled with energy (and when not adjusting the temperature)... + ! ----------------------------------------------------------------------------------------------- + + ! case of hydrology state uncoupled with energy (and when not adjusting the temperature) + if(.not.do_adjustTemp .and. .not.isNrgState .and. .not.isCoupled)then + ! compute the fraction of snow + select case(ixDomainType) + case(iname_veg); scalarFracLiqVeg = fracliquid(xTemp,snowfrz_scale) + case(iname_snow); mLayerFracLiqSnow(iLayer) = fracliquid(xTemp,snowfrz_scale) + case(iname_soil) ! do nothing + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select ! domain type + + ! ----- + ! - update volumetric fraction of liquid water and ice... + ! => case of energy state or coupled solution (or adjusting the temperature)... + ! -------------------------------------------------------------------------------- + + ! case of energy state OR coupled solution (or adjusting the temperature) + elseif(do_adjustTemp .or. ( (isNrgState .or. isCoupled) ) )then + + ! identify domain type + select case(ixDomainType) + + ! *** vegetation canopy + case(iname_veg) + ! compute mass of liquid water and ice + call updateVegFida(& + xTemp, & ! intent(in) : temperature (K) + scalarCanopyWatTrial, & ! intent(in) : mass of total water (-) + snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) + scalarCanopyTempPrime, & ! intent(in) + scalarCanopyWatPrime, & ! intent(in) : mass of total water (-) + scalarCanopyLiqTrial, & ! intent(out) : trial mass of liquid water (-) + scalarCanopyIceTrial, & ! intent(out) : trial mass of ice (-) + scalarCanopyLiqPrime, & ! intent(out) : trial mass of liquid water (-) + scalarCanopyIcePrime, & ! intent(out) : trial mass of ice (-) + scalarFracLiqVeg, & ! intent(out) : fraction of liquid water (-) + err,cmessage) ! intent(out) : error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! *** snow layers + case(iname_snow) + + call updateSnowFida(& + xTemp, & ! intent(in) : temperature (K) + mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) + snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) + mLayerTempPrime(iLayer), & ! + mLayerVolFracWatPrime(iLayer), & ! intent(in) + mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) + mLayerVolFracLiqPrime(iLayer), & ! intent(out) + mLayerVolFracIcePrime(iLayer), & ! intent(out) + mLayerFracLiqSnow(iLayer), & ! intent(out) : fraction of liquid water (-) + err,cmessage) ! intent(out) : error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! *** soil layers + case(iname_soil) + + ! compute volumetric fraction of liquid water and ice + call updateSoilFida(& + dt_cur, & + xTemp, & ! intent(in) : temperature (K) + mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) + mLayerMatricHeadPrev(ixControlIndex), & ! intent(in) + mLayerVolFracWatPrev(iLayer), & ! intent(in) + mLayerTempPrime(iLayer), & + mLayerMatricHeadPrime(ixControlIndex), & + ! intent(in) : soil parameters + vGn_alpha(ixControlIndex), & + vGn_n(ixControlIndex), & + theta_sat(ixControlIndex), & + theta_res(ixControlIndex), & + vGn_m(ixControlIndex), & + mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) + mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) + mLayerVolFracWatPrime(iLayer), & + mLayerVolFracLiqPrime(iLayer), & + mLayerVolFracIcePrime(iLayer), & + err,cmessage) ! intent(out) : error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! check + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + + end select ! domain type + + ! final check + else + + ! do nothing (input = output) -- and check that we got here correctly + if( (isNrgState .or. isCoupled) )then + scalarVolFracLiq = realMissing + scalarVolFracIce = realMissing + else + message=trim(message)//'unexpected else branch' + err=20; return + endif + + endif ! if energy state or solution is coupled + + ! ----- + ! - update temperatures... + ! ------------------------ + + + end do iterations ! iterating + + ! save temperature + select case(ixDomainType) + case(iname_veg); scalarCanopyTempTrial = xTemp + case(iname_snow, iname_soil); mLayerTempTrial(iLayer) = xTemp + end select + + ! ======================================================================================================================================= + ! ======================================================================================================================================= + + ! ----- + ! - compute the liquid water matric potential (and necessay derivatives)... + ! ------------------------------------------------------------------------- + + ! only for soil + if(ixDomainType==iname_soil)then + + ! check liquid water + if(mLayerVolFracLiqTrial(iLayer) > theta_sat(ixControlIndex) )then + message=trim(message)//'liquid water greater than porosity' + err=20; return + endif + + ! case of hydrology state uncoupled with energy + if(.not.isNrgState .and. .not.isCoupled)then + + ! derivatives relating liquid water matric potential to total water matric potential and temperature + dPsiLiq_dPsi0(ixControlIndex) = 1._dp ! exact correspondence (psiLiq=psi0) + dPsiLiq_dTemp(ixControlIndex) = 0._dp ! no relationship between liquid water matric potential and temperature + + ! case of energy state or coupled solution + else + ! compute the liquid matric potential (and the derivatives w.r.t. total matric potential and temperature) + call liquidHeadFida(& + ! input + mLayerMatricHeadTrial(ixControlIndex) ,& ! intent(in) : total water matric potential (m) + mLayerMatricHeadPrime(ixControlIndex) ,& ! + mLayerVolFracLiqTrial(iLayer) ,& ! intent(in) : volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : volumetric fraction of ice (-) + vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in) : soil parameters + dVolTot_dPsi0(ixControlIndex) ,& ! intent(in) : derivative in the soil water characteristic (m-1) + mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) + mLayerTempPrime(ixControlIndex) ,& + mLayerVolFracLiqPrime(iLayer) ,& + mLayerVolFracIcePrime(iLayer) ,& + ! output + mLayerMatricHeadLiqTrial(ixControlIndex) ,& ! intent(out): liquid water matric potential (m) + mLayerMatricHeadLiqPrime(ixControlIndex) ,& ! + dPsiLiq_dPsi0(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. the total water matric potential (-) + dPsiLiq_dTemp(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. temperature (m K-1) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + endif ! switch between hydrology and energy state + + endif ! if domain is soil + + end do ! looping through state variables + + ! deallocate space + deallocate(computedCoupling,stat=err) ! .true. if computed the coupling for a given state variable + if(err/=0)then; message=trim(message)//'problem deallocating computedCoupling'; return; endif + + ! end association to the variables in the data structures + end associate + + end subroutine updateVarsFida + + +end module updateVarsFida_module diff --git a/build/source/engine/updateVarsFida2.f90 b/build/source/engine/updateVarsFida2.f90 new file mode 100644 index 000000000..2739f80de --- /dev/null +++ b/build/source/engine/updateVarsFida2.f90 @@ -0,0 +1,800 @@ +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2015 NCAR/RAL +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . + +module updateVarsFida2_module + +! data types +USE nrtype + +! missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing real number + +! access the global print flag +USE globalData,only:globalPrintFlag + +! domain types +USE globalData,only:iname_cas ! named variables for canopy air space +USE globalData,only:iname_veg ! named variables for vegetation canopy +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil +USE globalData,only:iname_aquifer ! named variables for the aquifer + +! named variables to describe the state variable type +USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space +USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy +USE globalData,only:iname_watCanopy ! named variable defining the mass of total water on the vegetation canopy +USE globalData,only:iname_liqCanopy ! named variable defining the mass of liquid water on the vegetation canopy +USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers +USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers +USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers +USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers +USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers + +! metadata for information in the data structures +USE globalData,only:indx_meta ! metadata for the variables in the index structure + +! constants +USE multiconst,only:& + gravity, & ! acceleration of gravity (m s-2) + Tfreeze, & ! temperature at freezing (K) + Cp_air, & ! specific heat of air (J kg-1 K-1) + LH_fus, & ! latent heat of fusion (J kg-1) + iden_air, & ! intrinsic density of air (kg m-3) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water ! intrinsic density of liquid water (kg m-3) + +! provide access to the derived types to define the data structures +USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (dp) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength ! data vector with variable length dimension (dp) + +! provide access to indices that define elements of the data structures +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookDERIV ! named variables for structure elements +USE var_lookup,only:iLookPARAM ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements + +! provide access to routines to update states +USE updatStateFida_module,only:updateVegFida ! update snow states +USE updatStateFida_module,only:updateSnowFida ! update snow states +USE updatStateFida_module,only:updateSoilFida2 ! update soil states + +! provide access to functions for the constitutive functions and derivatives +USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) +USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) +USE soil_utils_module,only:dTheta_dTk ! differentiate the freezing curve w.r.t. temperature (soil) +USE soil_utils_module,only:dTheta_dPsi ! derivative in the soil water characteristic (soil) +USE soil_utils_module,only:dPsi_dTheta ! derivative in the soil water characteristic (soil) +USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content +USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water +USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists +USE soil_utils_fida_module,only:liquidHeadFida ! compute the liquid water matric potential +USE soil_utils_fida_module,only:d2Theta_dPsi2 +USE soil_utils_fida_module,only:d2Theta_dTk2 + +! IEEE checks +USE, intrinsic :: ieee_arithmetic ! check values (NaN, etc.) + +implicit none +private +public::updateVarsFida2 + +contains + + ! ********************************************************************************************************** + ! public subroutine updateVarsFida2: compute diagnostic variables + ! ********************************************************************************************************** + subroutine updateVarsFida2(& + ! input + do_adjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output: variables for the vegetation canopy + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempPrime, & ! reza + mLayerVolFracWatPrime, & ! reza + mLayerVolFracLiqPrime, & ! reza + mLayerVolFracIcePrime, & ! reza + mLayerMatricHeadPrime, & ! reza + mLayerMatricHeadLiqPrime, & ! reza + mLayerd2Theta_dTk2, & ! reza + d2VolTot_d2Psi0, & + dFracLiqSnow_dTk, & + d2Theta_dTkCanopy2, & + dFracLiqVeg_dTkCanopy, & + ! output: error control + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input + logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze + type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + ! output: variables for the vegetation canopy + real(dp),intent(inout) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(dp),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + real(dp),intent(inout) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) + real(dp),intent(inout) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + real(dp),intent(inout) :: scalarCanopyTempPrime ! trial value of canopy temperature (K) + real(dp),intent(inout) :: scalarCanopyWatPrime ! trial value of canopy total water (kg m-2) + real(dp),intent(inout) :: scalarCanopyLiqPrime ! trial value of canopy liquid water (kg m-2) + real(dp),intent(inout) :: scalarCanopyIcePrime ! trial value of canopy ice content (kg m-2) + ! output: variables for the snow-soil domain + real(dp),intent(inout) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(dp),intent(inout) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(dp),intent(inout) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) + real(dp),intent(inout) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric ice water content (-) + real(dp),intent(inout) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(dp),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) + + real(dp),intent(inout) :: mLayerTempPrime(:) + real(dp),intent(inout) :: mLayerVolFracWatPrime(:) ! reza + real(dp),intent(inout) :: mLayerVolFracLiqPrime(:) ! reza + real(dp),intent(inout) :: mLayerVolFracIcePrime(:) ! reza + real(dp),intent(inout) :: mLayerMatricHeadPrime(:) ! reza + real(dp),intent(inout) :: mLayerMatricHeadLiqPrime(:) ! reza + real(dp),intent(out) :: mLayerd2Theta_dTk2(:) ! reza + real(dp),intent(out) :: d2VolTot_d2Psi0(:) ! reza + real(dp),intent(out) :: dFracLiqSnow_dTk(:) ! reza + real(dp),intent(out) :: d2Theta_dTkCanopy2 + real(dp),intent(out) :: dFracLiqVeg_dTkCanopy + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! general local variables + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixDomainType ! name of a given model domain + integer(i4b) :: ixControlIndex ! index within a given model domain + integer(i4b) :: ixOther,ixOtherLocal ! index of the coupled state variable within the (full, local) vector + logical(lgt) :: isCoupled ! .true. if a given variable shared another state variable in the same control volume + logical(lgt) :: isNrgState ! .true. if a given variable is an energy state + logical(lgt),allocatable :: computedCoupling(:) ! .true. if computed the coupling for a given state variable + real(dp) :: scalarVolFracLiq ! volumetric fraction of liquid water (-) + real(dp) :: scalarVolFracIce ! volumetric fraction of ice (-) + real(dp) :: scalarVolFracLiqPrime ! volumetric fraction of liquid water (-) + real(dp) :: scalarVolFracIcePrime ! volumetric fraction of ice (-) + real(dp) :: Tcrit ! critical soil temperature below which ice exists (K) + real(dp) :: xTemp ! temporary temperature (K) + real(dp) :: effSat ! effective saturation (-) + real(dp) :: avPore ! available pore space (-) + character(len=256) :: cMessage ! error message of downwind routine + logical(lgt),parameter :: printFlag=.false. ! flag to turn on printing + ! iterative solution for temperature + real(dp) :: meltNrg ! energy for melt+freeze (J m-3) + real(dp) :: residual ! residual in the energy equation (J m-3) + real(dp) :: derivative ! derivative in the energy equation (J m-3 K-1) + real(dp) :: tempInc ! iteration increment (K) + integer(i4b) :: iter ! iteration index + integer(i4b) :: niter ! number of iterations + integer(i4b),parameter :: maxiter=100 ! maximum number of iterations + real(dp),parameter :: nrgConvTol=1.e-4_dp ! convergence tolerance for energy (J m-3) + real(dp),parameter :: tempConvTol=1.e-6_dp ! convergence tolerance for temperature (K) + real(dp) :: critDiff ! temperature difference from critical (K) + real(dp) :: tempMin ! minimum bracket for temperature (K) + real(dp) :: tempMax ! maximum bracket for temperature (K) + logical(lgt) :: bFlag ! flag to denote that iteration increment was constrained using bi-section + real(dp),parameter :: epsT=1.e-7_dp ! small interval above/below critical temperature (K) + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + associate(& + ! number of model layers, and layer type + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! indices defining model states and layers + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ! indices in the full vector for specific domains + ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain + ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + ! mapping between the full state vector and the state subset + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ! type of domain, type of state variable, and index of control volume within domain + ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ! depth-varying model parameters + vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) + ! model diagnostic variables (heat capacity) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(in): [dp ] volumetric heat capacity of the vegetation (J m-3 K-1) + mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] volumetric heat capacity in each layer (J m-3 K-1) + ! model diagnostic variables (fraction of liquid water) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) + ! model states for the vegetation canopy + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variable vectors for the snow-soil layers + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] total water matric potential (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) + ! model diagnostic variables from a previous solution + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + ! derivatives + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential + dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) + dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(out): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) & ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature + ) ! association with variables in the data structures + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message='updateVarsFida2/' + + ! allocate space and assign values to the flag vector + allocate(computedCoupling(size(ixMapSubset2Full)),stat=err) ! .true. if computed the coupling for a given state variable + if(err/=0)then; message=trim(message)//'problem allocating computedCoupling'; return; endif + computedCoupling(:)=.false. + + ! loop through model state variables + do iState=1,size(ixMapSubset2Full) + + ! check the need for the computations + if(computedCoupling(iState)) cycle + + ! ----- + ! - compute indices... + ! -------------------- + + ! get domain type, and index of the control volume within the domain + ixFullVector = ixMapSubset2Full(iState) ! index within full state vector + ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) + ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain + + ! get the layer index + select case(ixDomainType) + case(iname_cas); cycle ! canopy air space: do nothing + case(iname_veg); iLayer = 0 + case(iname_snow); iLayer = ixControlIndex + case(iname_soil); iLayer = ixControlIndex + nSnow + case(iname_aquifer); cycle ! aquifer: do nothing + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + + ! get the index of the other (energy or mass) state variable within the full state vector + select case(ixDomainType) + case(iname_veg) ; ixOther = merge(ixHydCanopy(1), ixNrgCanopy(1), ixStateType(ixFullVector)==iname_nrgCanopy) + case(iname_snow, iname_soil); ixOther = merge(ixHydLayer(iLayer),ixNrgLayer(iLayer),ixStateType(ixFullVector)==iname_nrgLayer) + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select + + ! get the index in the local state vector + ixOtherLocal = ixMapFull2Subset(ixOther) ! ixOtherLocal could equal integerMissing + if(ixOtherLocal/=integerMissing) computedCoupling(ixOtherLocal)=.true. + + ! check if we have a coupled solution + isCoupled = (ixOtherLocal/=integerMissing) + + ! check if we are an energy state + isNrgState = (ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer) + + if(printFlag)then + print*, 'iState = ', iState, size(ixMapSubset2Full) + print*, 'ixFullVector = ', ixFullVector + print*, 'ixDomainType = ', ixDomainType + print*, 'ixControlIndex = ', ixControlIndex + print*, 'ixOther = ', ixOther + print*, 'ixOtherLocal = ', ixOtherLocal + print*, 'do_adjustTemp = ', do_adjustTemp + print*, 'isCoupled = ', isCoupled + print*, 'isNrgState = ', isNrgState + endif + + + + ! ======================================================================================================================================= + ! ======================================================================================================================================= + ! ======================================================================================================================================= + ! ======================================================================================================================================= + ! ======================================================================================================================================= + ! ======================================================================================================================================= + + ! update hydrology state variables for the uncoupled solution + if(.not.isNrgState .and. .not.isCoupled)then + + ! update the total water from volumetric liquid water + if(ixStateType(ixFullVector)==iname_liqCanopy .or. ixStateType(ixFullVector)==iname_liqLayer)then + select case(ixDomainType) + case(iname_veg) + scalarCanopyWatTrial = scalarCanopyLiqTrial + scalarCanopyIceTrial + scalarCanopyWatPrime = scalarCanopyLiqPrime + scalarCanopyIcePrime + case(iname_snow) + mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer)*iden_ice/iden_water + mLayerVolFracWatPrime(iLayer) = mLayerVolFracLiqPrime(iLayer) + mLayerVolFracIcePrime(iLayer)*iden_ice/iden_water + case(iname_soil) + mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer) ! no volume expansion + mLayerVolFracWatPrime(iLayer) = mLayerVolFracLiqPrime(iLayer) + mLayerVolFracIcePrime(iLayer) + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, or iname_soil'; return + end select + endif + + ! update the total water and the total water matric potential + if(ixDomainType==iname_soil)then + select case( ixStateType(ixFullVector) ) + ! --> update the total water from the liquid water matric potential + case(iname_lmpLayer) + + effSat = volFracLiq(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._dp,1._dp,vGn_n(ixControlIndex),vGn_m(ixControlIndex)) ! effective saturation + avPore = theta_sat(ixControlIndex) - mLayerVolFracIceTrial(iLayer) - theta_res(ixControlIndex) ! available pore space + mLayerVolFracLiqTrial(iLayer) = effSat*avPore + theta_res(ixControlIndex) + mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer) ! no volume expansion + mLayerVolFracWatPrime(iLayer) = mLayerVolFracLiqPrime(iLayer) + mLayerVolFracIcePrime(iLayer) + mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + mLayerMatricHeadPrime(ixControlIndex) = dPsi_dTheta(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) * mLayerVolFracWatPrime(iLayer) + !write(*,'(a,1x,i4,1x,3(f20.10,1x))') 'mLayerVolFracLiqTrial(iLayer) 1 = ', iLayer, mLayerVolFracLiqTrial(iLayer), mLayerVolFracIceTrial(iLayer), mLayerVolFracWatTrial(iLayer) + ! --> update the total water from the total water matric potential + case(iname_matLayer) + + mLayerVolFracWatTrial(iLayer) = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + mLayerVolFracWatPrime(iLayer) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) *mLayerMatricHeadPrime(ixControlIndex) + ! --> update the total water matric potential (assume already have mLayerVolFracWatTrial given block above) + case(iname_liqLayer, iname_watLayer) + + mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + mLayerMatricHeadPrime(ixControlIndex) = dPsi_dTheta(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) * mLayerVolFracWatPrime(iLayer) + case default; err=20; message=trim(message)//'expect iname_lmpLayer, iname_matLayer, iname_liqLayer, or iname_watLayer'; return + end select + endif ! if in the soil domain + + endif ! if hydrology state variable or uncoupled solution + + + ! compute the critical soil temperature below which ice exists + select case(ixDomainType) + case(iname_veg, iname_snow); Tcrit = Tfreeze + case(iname_soil); Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select + + ! initialize temperature + select case(ixDomainType) + case(iname_veg); xTemp = scalarCanopyTempTrial + case(iname_snow, iname_soil); xTemp = mLayerTempTrial(iLayer) + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select + + ! define brackets for the root + ! NOTE: start with an enormous range; updated quickly in the iterations + tempMin = xTemp - 10._dp + tempMax = xTemp + 10._dp + + ! get iterations (set to maximum iterations if adjusting the temperature) + niter = merge(maxiter, 1, do_adjustTemp) + + ! iterate + iterations: do iter=1,niter + + ! restrict temperature + if(xTemp <= tempMin .or. xTemp >= tempMax)then + xTemp = 0.5_dp*(tempMin + tempMax) ! new value + bFlag = .true. + else + bFlag = .false. + endif + + ! ----- + ! - compute derivatives... + ! ------------------------ + + ! compute the derivative in total water content w.r.t. total water matric potential (m-1) + ! NOTE 1: valid for frozen and unfrozen conditions + ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi + if(ixDomainType==iname_soil)then + select case( ixStateType(ixFullVector) ) + case(iname_lmpLayer) + dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._dp,1._dp,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._dp,1._dp,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + case default + dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),& + vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + end select + endif + + ! compute the derivative in liquid water content w.r.t. temperature + ! --> partially frozen: dependence of liquid water on temperature + if(xTemp unfrozen: no dependence of liquid water on temperature + else + select case(ixDomainType) + case(iname_veg); dTheta_dTkCanopy = 0._dp; d2Theta_dTkCanopy2 = 0; dFracLiqVeg_dTkCanopy = 0 + case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._dp; mLayerd2Theta_dTk2(iLayer) = 0._dp; dFracLiqSnow_dTk(iLayer) = 0 + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select ! domain type + endif + + + + + ! ----- + ! - update volumetric fraction of liquid water and ice... + ! => case of hydrology state uncoupled with energy (and when not adjusting the temperature)... + ! ----------------------------------------------------------------------------------------------- + + ! case of hydrology state uncoupled with energy (and when not adjusting the temperature) + if(.not.do_adjustTemp .and. .not.isNrgState .and. .not.isCoupled)then + + ! compute the fraction of snow + select case(ixDomainType) + case(iname_veg); scalarFracLiqVeg = fracliquid(xTemp,snowfrz_scale) + case(iname_snow); mLayerFracLiqSnow(iLayer) = fracliquid(xTemp,snowfrz_scale) + case(iname_soil) ! do nothing + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select ! domain type + + ! ----- + ! - update volumetric fraction of liquid water and ice... + ! => case of energy state or coupled solution (or adjusting the temperature)... + ! -------------------------------------------------------------------------------- + + ! case of energy state OR coupled solution (or adjusting the temperature) + elseif(do_adjustTemp .or. ( (isNrgState .or. isCoupled) ) )then + + ! identify domain type + select case(ixDomainType) + + ! *** vegetation canopy + case(iname_veg) + ! compute volumetric fraction of liquid water and ice + call updateVegFida(& + xTemp, & ! intent(in) : temperature (K) + scalarCanopyWatTrial, & ! intent(in) : volumetric fraction of total water (-) + snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) + scalarCanopyTempPrime, & ! intent(in) + scalarCanopyWatPrime, & ! intent(in) : volumetric fraction of total water (-) + scalarVolFracLiq, & ! intent(out) : trial volumetric fraction of liquid water (-) + scalarVolFracIce, & ! intent(out) : trial volumetric fraction if ice (-) + scalarVolFracLiqPrime, & ! intent(out) : trial volumetric fraction of liquid water (-) + scalarVolFracIcePrime, & ! intent(out) : trial volumetric fraction if ice (-) + scalarFracLiqVeg, & ! intent(out) : fraction of liquid water (-) + err,cmessage) ! intent(out) : error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! *** snow layers + case(iname_snow) + + call updateSnowFida(& + xTemp, & ! intent(in) : temperature (K) + mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) + snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) + mLayerTempPrime(iLayer), & ! + mLayerVolFracWatPrime(iLayer), & ! intent(in) + mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) + mLayerVolFracLiqPrime(iLayer), & ! intent(out) + mLayerVolFracIcePrime(iLayer), & ! intent(out) + mLayerFracLiqSnow(iLayer), & ! intent(out) : fraction of liquid water (-) + err,cmessage) ! intent(out) : error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! *** soil layers + case(iname_soil) + + ! compute volumetric fraction of liquid water and ice + call updateSoilFida2(& + xTemp, & ! intent(in) : temperature (K) + mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) + mLayerTempPrime(iLayer), & + mLayerMatricHeadPrime(ixControlIndex), & + ! intent(in) : soil parameters + vGn_alpha(ixControlIndex), & + vGn_n(ixControlIndex), & + theta_sat(ixControlIndex), & + theta_res(ixControlIndex), & + vGn_m(ixControlIndex), & + mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) + mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) + mLayerVolFracWatPrime(iLayer), & + mLayerVolFracLiqPrime(iLayer), & + mLayerVolFracIcePrime(iLayer), & + err,cmessage) ! intent(out) : error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! check + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + + end select ! domain type + + ! final check + else + + ! do nothing (input = output) -- and check that we got here correctly + if( (isNrgState .or. isCoupled) )then + scalarVolFracLiq = realMissing + scalarVolFracIce = realMissing + else + message=trim(message)//'unexpected else branch' + err=20; return + endif + + endif ! if energy state or solution is coupled + + ! ----- + ! - update temperatures... + ! ------------------------ + + ! check the need to adjust temperature + if(do_adjustTemp)then + + ! get the melt energy + meltNrg = merge(LH_fus*iden_ice, LH_fus*iden_water, ixDomainType==iname_snow) + + ! compute the residual and the derivative + select case(ixDomainType) + + ! * vegetation + case(iname_veg) + call xTempSolve(& + ! constant over iterations + meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) + heatCap = scalarBulkVolHeatCapVeg ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) + tempInit = scalarCanopyTemp ,& ! intent(in) : initial temperature (K) + volFracIceInit = scalarCanopyIce/(iden_water*canopyDepth),& ! intent(in) : initial volumetric fraction of ice (-) + ! trial values + xTemp = xTemp ,& ! intent(inout) : trial value of temperature + dLiq_dT = dTheta_dTkCanopy ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) + volFracIceTrial = scalarVolFracIce ,& ! intent(in) : trial value for volumetric fraction of ice + ! residual and derivative + residual = residual ,& ! intent(out) : residual (J m-3) + derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) + + ! * snow and soil + case(iname_snow, iname_soil) + call xTempSolve(& + ! constant over iterations + meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) + heatCap = mLayerVolHtCapBulk(iLayer) ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) + tempInit = mLayerTemp(iLayer) ,& ! intent(in) : initial temperature (K) + volFracIceInit = mLayerVolFracIce(iLayer) ,& ! intent(in) : initial volumetric fraction of ice (-) + ! trial values + xTemp = xTemp ,& ! intent(inout) : trial value of temperature + dLiq_dT = mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) + volFracIceTrial = mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : trial value for volumetric fraction of ice + ! residual and derivative + residual = residual ,& ! intent(out) : residual (J m-3) + derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) + + ! * check + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + + end select ! domain type + + ! check validity of residual + if( ieee_is_nan(residual) )then + message=trim(message)//'residual is not valid' + err=20; return + endif + + ! update bracket + if(residual < 0._dp)then + tempMax = min(xTemp,tempMax) + else + tempMin = max(tempMin,xTemp) + end if + + ! compute iteration increment + tempInc = residual/derivative ! K + + ! check + if(globalPrintFlag)& + write(*,'(i4,1x,e20.10,1x,5(f20.10,1x),L1)') iter, residual, xTemp-Tcrit, tempInc, Tcrit, tempMin, tempMax, bFlag + + ! check convergence + if(abs(residual) < nrgConvTol .or. abs(tempInc) < tempConvTol) exit iterations + + ! add constraints for snow temperature + if(ixDomainType==iname_veg .or. ixDomainType==iname_snow)then + if(tempInc > Tcrit - xTemp) tempInc=(Tcrit - xTemp)*0.5_dp ! simple bi-section method + endif ! if the domain is vegetation or snow + + ! deal with the discontinuity between partially frozen and unfrozen soil + if(ixDomainType==iname_soil)then + ! difference from the temperature below which ice exists + critDiff = Tcrit - xTemp + ! --> initially frozen (T < Tcrit) + if(critDiff > 0._dp)then + if(tempInc > critDiff) tempInc = critDiff + epsT ! set iteration increment to slightly above critical temperature + ! --> initially unfrozen (T > Tcrit) + else + if(tempInc < critDiff) tempInc = critDiff - epsT ! set iteration increment to slightly below critical temperature + endif + endif ! if the domain is soil + + ! update the temperature trial + xTemp = xTemp + tempInc + + ! check failed convergence + if(iter==maxiter)then + message=trim(message)//'failed to converge' + err=-20; return ! negative error code = try to recover + endif + + endif ! if adjusting the temperature + + end do iterations ! iterating + + ! save temperature + select case(ixDomainType) + case(iname_veg); scalarCanopyTempTrial = xTemp + case(iname_snow, iname_soil); mLayerTempTrial(iLayer) = xTemp + end select + + ! ======================================================================================================================================= + ! ======================================================================================================================================= + + ! ----- + ! - compute the liquid water matric potential (and necessay derivatives)... + ! ------------------------------------------------------------------------- + + ! only for soil + if(ixDomainType==iname_soil)then + + ! check liquid water + if(mLayerVolFracLiqTrial(iLayer) > theta_sat(ixControlIndex) )then + message=trim(message)//'liquid water greater than porosity' + err=20; return + endif + + ! case of hydrology state uncoupled with energy + if(.not.isNrgState .and. .not.isCoupled)then + + ! derivatives relating liquid water matric potential to total water matric potential and temperature + dPsiLiq_dPsi0(ixControlIndex) = 1._dp ! exact correspondence (psiLiq=psi0) + dPsiLiq_dTemp(ixControlIndex) = 0._dp ! no relationship between liquid water matric potential and temperature + + ! case of energy state or coupled solution + else + ! compute the liquid matric potential (and the derivatives w.r.t. total matric potential and temperature) + call liquidHeadFida(& + ! input + mLayerMatricHeadTrial(ixControlIndex) ,& ! intent(in) : total water matric potential (m) + mLayerMatricHeadPrime(ixControlIndex) ,& ! + mLayerVolFracLiqTrial(iLayer) ,& ! intent(in) : volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : volumetric fraction of ice (-) + vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in) : soil parameters + dVolTot_dPsi0(ixControlIndex) ,& ! intent(in) : derivative in the soil water characteristic (m-1) + mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) + mLayerTempPrime(ixControlIndex) ,& + mLayerVolFracLiqPrime(iLayer) ,& + mLayerVolFracIcePrime(iLayer) ,& + ! output + mLayerMatricHeadLiqTrial(ixControlIndex) ,& ! intent(out): liquid water matric potential (m) + mLayerMatricHeadLiqPrime(ixControlIndex) ,& ! + dPsiLiq_dPsi0(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. the total water matric potential (-) + dPsiLiq_dTemp(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. temperature (m K-1) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + endif ! switch between hydrology and energy state + + endif ! if domain is soil + + end do ! looping through state variables + + ! deallocate space + deallocate(computedCoupling,stat=err) ! .true. if computed the coupling for a given state variable + if(err/=0)then; message=trim(message)//'problem deallocating computedCoupling'; return; endif + + ! end association to the variables in the data structures + end associate + + end subroutine updateVarsFida2 + + + ! ********************************************************************************************************** + ! private subroutine xTempSolve: compute residual and derivative for temperature + ! ********************************************************************************************************** + subroutine xTempSolve(& + ! input: constant over iterations + meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) + heatCap ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) + tempInit ,& ! intent(in) : initial temperature (K) + volFracIceInit ,& ! intent(in) : initial volumetric fraction of ice (-) + ! input-output: trial values + xTemp ,& ! intent(inout) : trial value of temperature + dLiq_dT ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) + volFracIceTrial ,& ! intent(in) : trial value for volumetric fraction of ice + ! output: residual and derivative + residual ,& ! intent(out) : residual (J m-3) + derivative ) ! intent(out) : derivative (J m-3 K-1) + implicit none + ! input: constant over iterations + real(dp),intent(in) :: meltNrg ! energy for melt+freeze (J m-3) + real(dp),intent(in) :: heatCap ! volumetric heat capacity (J m-3 K-1) + real(dp),intent(in) :: tempInit ! initial temperature (K) + real(dp),intent(in) :: volFracIceInit ! initial volumetric fraction of ice (-) + ! input-output: trial values + real(dp),intent(inout) :: xTemp ! trial value for temperature + real(dp),intent(in) :: dLiq_dT ! derivative in liquid water content w.r.t. temperature (K-1) + real(dp),intent(in) :: volFracIceTrial ! trial value for the volumetric fraction of ice (-) + ! output: residual and derivative + real(dp),intent(out) :: residual ! residual (J m-3) + real(dp),intent(out) :: derivative ! derivative (J m-3 K-1) + ! subroutine starts here + residual = -heatCap*(xTemp - tempInit) + meltNrg*(volFracIceTrial - volFracIceInit) ! J m-3 + derivative = heatCap + LH_fus*iden_water*dLiq_dT ! J m-3 K-1 + end subroutine xTempSolve + +end module updateVarsFida2_module diff --git a/build/source/engine/varExtrFida.f90 b/build/source/engine/varExtrFida.f90 new file mode 100644 index 000000000..9e64f52e6 --- /dev/null +++ b/build/source/engine/varExtrFida.f90 @@ -0,0 +1,514 @@ + + +module varExtrFida_module + +! data types +USE nrtype + +! missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing real number + +! access the global print flag +USE globalData,only:globalPrintFlag + +! domain types +USE globalData,only:iname_cas ! named variables for canopy air space +USE globalData,only:iname_veg ! named variables for vegetation canopy +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil + +! named variables to describe the state variable type +USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space +USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy +USE globalData,only:iname_watCanopy ! named variable defining the mass of total water on the vegetation canopy +USE globalData,only:iname_liqCanopy ! named variable defining the mass of liquid water on the vegetation canopy +USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers +USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers +USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers +USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers +USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers +USE globalData,only:iname_watAquifer ! named variable defining the water storage in the aquifer + +! metadata for information in the data structures +USE globalData,only:indx_meta ! metadata for the variables in the index structure + +! constants +USE multiconst,only:& + gravity, & ! acceleration of gravity (m s-2) + Tfreeze, & ! temperature at freezing (K) + Cp_air, & ! specific heat of air (J kg-1 K-1) + LH_fus, & ! latent heat of fusion (J kg-1) + iden_air, & ! intrinsic density of air (kg m-3) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water ! intrinsic density of liquid water (kg m-3) + +! provide access to the derived types to define the data structures +USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (dp) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength ! data vector with variable length dimension (dp) + +! provide access to indices that define elements of the data structures +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookDERIV ! named variables for structure elements +USE var_lookup,only:iLookPARAM ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements + +! provide access to routines to update states +USE updatState_module,only:updateSnow ! update snow states +USE updatState_module,only:updateSoil ! update soil states + +! provide access to functions for the constitutive functions and derivatives +USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) +USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) +USE soil_utils_module,only:dTheta_dTk ! differentiate the freezing curve w.r.t. temperature (soil) +USE soil_utils_module,only:dTheta_dPsi ! derivative in the soil water characteristic (soil) +USE soil_utils_module,only:dPsi_dTheta ! derivative in the soil water characteristic (soil) +USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content +USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water +USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists +USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential + +implicit none +private +public::varExtract +public::varExtractFida +public::residDiscontinuity +public::countDiscontinuity + + +contains + + + ! ********************************************************************************************************** + ! public subroutine varExtract: extract variables from the state vector and compute diagnostic variables + ! ********************************************************************************************************** + subroutine varExtract(& + ! input + stateVec, & ! intent(in): model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) + mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) + ! output: variables for the aquifer + scalarAquiferStorageTrial, & ! intent(out): trial value of storage of water in the aquifer (m) + ! output: error control + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input + real(dp),intent(in) :: stateVec(:) ! model state vector (mixed units) + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + ! output: variables for the vegetation canopy + real(dp),intent(out) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) + real(dp),intent(out) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(dp),intent(out) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + real(dp),intent(out) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) + ! output: variables for the snow-soil domain + real(dp),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(dp),intent(out) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(dp),intent(out) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) + real(dp),intent(out) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(dp),intent(out) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) + ! output: variables for the aquifer + real(dp),intent(out) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + associate(& + ! number of model layers, and layer type + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + ! indices defining model states and layers + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of the squifer storage state variable + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for energy states in the snow+soil subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for hydrology states in the snow+soil subdomain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + ! indices defining type of model state variables + ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] type of desired model state variables + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat & ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain +)! association with variables in the data structures + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message='varExtract/' + + ! *** extract state variables for the vegetation canopy + + + ! check if computing the vegetation flux + if(ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing .or. ixVegHyd/=integerMissing)then + + ! extract temperature of the canopy air space + if(ixCasNrg/=integerMissing) scalarCanairTempTrial = stateVec(ixCasNrg) + + ! extract canopy temperature + if(ixVegNrg/=integerMissing) scalarCanopyTempTrial = stateVec(ixVegNrg) + + ! extract intercepted water + if(ixVegHyd/=integerMissing)then + select case( ixStateType_subset(ixVegHyd) ) + case(iname_liqCanopy); scalarCanopyLiqTrial = stateVec(ixVegHyd) + case(iname_watCanopy); scalarCanopyWatTrial = stateVec(ixVegHyd) + case default; err=20; message=trim(message)//'case not found: expect iname_liqCanopy or iname_watCanopy'; return + end select + endif + + endif ! not computing the vegetation flux + + ! *** extract state variables from the snow+soil sub-domain + + + ! overwrite with the energy values from the state vector + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + mLayerTempTrial(iLayer) = stateVec( ixSnowSoilNrg(iLayer) ) + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! overwrite with the hydrology values from the state vector + if(nSnowSoilHyd>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) + select case( ixHydType(iLayer) ) + case(iname_watLayer); mLayerVolFracWatTrial(iLayer) = stateVec( ixSnowSoilHyd(iLayer) ) ! total water state variable for snow+soil layers + case(iname_liqLayer); mLayerVolFracLiqTrial(iLayer) = stateVec( ixSnowSoilHyd(iLayer) ) ! liquid water state variable for snow+soil layers + case(iname_matLayer); mLayerMatricHeadTrial(iLayer-nSnow) = stateVec( ixSnowSoilHyd(iLayer) ) ! total water matric potential variable for soil layers + case(iname_lmpLayer); mLayerMatricHeadLiqTrial(iLayer-nSnow) = stateVec( ixSnowSoilHyd(iLayer) ) ! liquid matric potential state variable for soil layers + case default ! do nothing + end select + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! extract temperature of the canopy air space + if(ixAqWat/=integerMissing) scalarAquiferStorageTrial = stateVec(ixAqWat) + + end associate + + end subroutine varExtract + + ! ********************************************************************************************************** + ! public subroutine varExtractFida: extract variables from the state vector and compute diagnostic variables + ! ********************************************************************************************************** + subroutine varExtractFida(& + ! input + stateVecPrime, & ! intent(in): model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy + scalarCanairTempPrime, & ! intent(out): trial value of canopy air temperature (K) + scalarCanopyTempPrime, & ! intent(out): trial value of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(out): trial value of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(out): trial value of canopy liquid water (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempPrime, & ! intent(out): trial vector of layer temperature (K) + mLayerVolFracWatPrime, & ! intent(out): trial vector of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(out): trial vector of volumetric liquid water content (-) + mLayerMatricHeadPrime, & ! intent(out): trial vector of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(out): trial vector of liquid water matric potential (m) + ! output: variables for the aquifer + scalarAquiferStoragePrime, & ! intent(out): trial value of storage of water in the aquifer (m) + ! output: error control + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input + real(dp),intent(in) :: stateVecPrime(:) ! model state vector (mixed units) + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + ! output: variables for the vegetation canopy + real(dp),intent(out) :: scalarCanairTempPrime ! trial value of canopy air temperature (K) + real(dp),intent(out) :: scalarCanopyTempPrime ! trial value of canopy temperature (K) + real(dp),intent(out) :: scalarCanopyWatPrime ! trial value of canopy total water (kg m-2) + real(dp),intent(out) :: scalarCanopyLiqPrime ! trial value of canopy liquid water (kg m-2) + ! output: variables for the snow-soil domain + real(dp),intent(out) :: mLayerTempPrime(:) ! trial vector of layer temperature (K) + real(dp),intent(out) :: mLayerVolFracWatPrime(:) ! trial vector of volumetric total water content (-) + real(dp),intent(out) :: mLayerVolFracLiqPrime(:) ! trial vector of volumetric liquid water content (-) + real(dp),intent(out) :: mLayerMatricHeadPrime(:) ! trial vector of total water matric potential (m) + real(dp),intent(out) :: mLayerMatricHeadLiqPrime(:) ! trial vector of liquid water matric potential (m) + ! output: variables for the aquifer + real(dp),intent(out) :: scalarAquiferStoragePrime ! trial value of storage of water in the aquifer (m) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + associate(& + ! number of model layers, and layer type + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + ! indices defining model states and layers + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of the squifer storage state variable + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for energy states in the snow+soil subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for hydrology states in the snow+soil subdomain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + ! indices defining type of model state variables + ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] type of desired model state variables + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat & ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain +) ! association with variables in the data structures + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message='varExtractFida/' + + + ! check if computing the vegetation flux + if(ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing .or. ixVegHyd/=integerMissing)then + + ! extract temperature of the canopy air space + if(ixCasNrg/=integerMissing) scalarCanairTempPrime = stateVecPrime(ixCasNrg) + + ! extract canopy temperature + if(ixVegNrg/=integerMissing) scalarCanopyTempPrime = stateVecPrime(ixVegNrg) + + ! extract intercepted water + if(ixVegHyd/=integerMissing)then + select case( ixStateType_subset(ixVegHyd) ) + case(iname_liqCanopy); scalarCanopyLiqPrime = stateVecPrime(ixVegHyd) + case(iname_watCanopy); scalarCanopyWatPrime = stateVecPrime(ixVegHyd) + case default; err=20; message=trim(message)//'case not found: expect iname_liqCanopy or iname_watCanopy'; return + end select + endif + + endif ! not computing the vegetation flux + + + ! overwrite with the energy values from the state vector + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + mLayerTempPrime(iLayer) = stateVecPrime( ixSnowSoilNrg(iLayer) ) + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! overwrite with the hydrology values from the state vector + if(nSnowSoilHyd>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) + select case( ixHydType(iLayer) ) + case(iname_watLayer); mLayerVolFracWatPrime(iLayer) = stateVecPrime( ixSnowSoilHyd(iLayer) ) ! total water state variable for snow+soil layers + case(iname_liqLayer); mLayerVolFracLiqPrime(iLayer) = stateVecPrime( ixSnowSoilHyd(iLayer) ) ! liquid water state variable for snow+soil layers + case(iname_matLayer); mLayerMatricHeadPrime(iLayer-nSnow) = stateVecPrime( ixSnowSoilHyd(iLayer) ) ! total water matric potential variable for soil layers + case(iname_lmpLayer); mLayerMatricHeadLiqPrime(iLayer-nSnow) = stateVecPrime( ixSnowSoilHyd(iLayer) ) ! liquid matric potential state variable for soil layers + case default ! do nothing + end select + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! extract temperature of the canopy air space + if(ixAqWat/=integerMissing) scalarAquiferStoragePrime = stateVecPrime(ixAqWat) + + end associate + + end subroutine varExtractFida + + + ! ********************************************************************************************************** + ! public subroutine residDiscontinuity: + ! ********************************************************************************************************** + subroutine residDiscontinuity(& + ! input + stateVec, & ! intent(in): model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output + resid, & ! intent(out) + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input + real(dp),intent(in) :: stateVec(:) ! model state vector (mixed units) + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + real(qp),intent(out) :: resid(:) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + integer(i4b) :: iCount + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + associate(& + ! number of model layers, and layer type + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + ! indices defining model states and layers + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of the squifer storage state variable + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for energy states in the snow+soil subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for hydrology states in the snow+soil subdomain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + ! indices defining type of model state variables + ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] type of desired model state variables + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat & ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain +)! association with variables in the data structures + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message='residDiscontinuity/' + + iCount = 1 + + + ! check if computing the vegetation flux + if(ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing)then + + ! temperature of the canopy air space + if(ixCasNrg/=integerMissing)then + resid(iCount) = stateVec(ixCasNrg) - Tfreeze ! scalarCanairTempTrial - Tfreeze + iCount = iCount + 1 + endif + + ! canopy temperature + if(ixVegNrg/=integerMissing)then + resid(iCount) = stateVec(ixVegNrg) - Tfreeze ! scalarCanopyTempTrial - Tfreeze + iCount = iCount + 1 + endif + + + endif ! not computing the vegetation flux + + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + resid( iCount ) = stateVec( ixSnowSoilNrg(iLayer) ) - Tfreeze ! mLayerTempTrial(iLayer) - Tfreeze + iCount = iCount + 1 + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + end associate + + + end subroutine residDiscontinuity + + ! ********************************************************************************************************** + ! public subroutine countDiscontinuity: + ! ********************************************************************************************************** + subroutine countDiscontinuity(& + ! input + stateVec, & ! intent(in): model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output + countD, & ! intent(out) + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input + real(dp),intent(in) :: stateVec(:) ! model state vector (mixed units) + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + integer(i4b),intent(out) :: countD + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + associate(& + ! number of model layers, and layer type + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + ! indices defining model states and layers + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of the squifer storage state variable + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for energy states in the snow+soil subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for hydrology states in the snow+soil subdomain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + ! indices defining type of model state variables + ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] type of desired model state variables + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat & ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain +)! association with variables in the data structures + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message='countDiscontinuity/' + + ! *** extract state variables for the vegetation canopy + + countD = 0 + ! check if computing the vegetation flux + if(ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing)then + + ! temperature of the canopy air space + if(ixCasNrg/=integerMissing) countD = countD + 1 + + ! canopy temperature + if(ixVegNrg/=integerMissing) countD = countD + 1 + + + endif ! not computing the vegetation flux + + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + countD = countD + 1 + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + end associate + + end subroutine countDiscontinuity + + + +end module varExtrFida_module diff --git a/build/source/engine/varSubstepFida.f90 b/build/source/engine/varSubstepFida.f90 new file mode 100644 index 000000000..40f25a091 --- /dev/null +++ b/build/source/engine/varSubstepFida.f90 @@ -0,0 +1,1127 @@ +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2015 NCAR/RAL +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . + +module varSubstepFida_module + +! data types +USE nrtype + +! access missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing double precision number +USE globalData,only:quadMissing ! missing quadruple precision number + +! access the global print flag +USE globalData,only:globalPrintFlag + +! domain types +USE globalData,only:iname_cas ! named variables for the canopy air space +USE globalData,only:iname_veg ! named variables for vegetation +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil + +! global metadata +USE globalData,only:flux_meta ! metadata on the model fluxes + +! derived types to define the data structures +USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (dp) + var_flagVec, & ! data vector with variable length dimension (i4b) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (dp) + zLookup, & ! data vector with variable length dimension (dp) + model_options ! defines the model decisions + +! provide access to indices that define elements of the data structures +USE var_lookup,only:iLookFLUX ! named variables for structure elements +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookPARAM ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements + +! look up structure for variable types +USE var_lookup,only:iLookVarType + +! constants +USE multiconst,only:& + Tfreeze, & ! freezing temperature (K) + LH_fus, & ! latent heat of fusion (J kg-1) + LH_vap, & ! latent heat of vaporization (J kg-1) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water, & ! intrinsic density of liquid water (kg m-3) + ! specific heat + Cp_air, & ! specific heat of air (J kg-1 K-1) + Cp_ice, & ! specific heat of ice (J kg-1 K-1) + Cp_soil, & ! specific heat of soil (J kg-1 K-1) + Cp_water ! specific heat of liquid water (J kg-1 K-1) + +! safety: set private unless specified otherwise +implicit none +private +public::varSubstepFida + +! algorithmic parameters +real(dp),parameter :: verySmall=1.e-6_dp ! used as an additive constant to check if substantial difference among real numbers + +contains + + + ! ********************************************************************************************************** + ! public subroutine varSubstepFida: run the model for a collection of substeps for a given state subset + ! ********************************************************************************************************** + subroutine varSubstepFida(& + ! input: model control + dt, & ! intent(in) : time step (s) + dtInit, & ! intent(in) : initial time step (seconds) + dt_min, & ! intent(in) : minimum time step (seconds) + nState, & ! intent(in) : total number of state variables + doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature + firstSubStep, & ! intent(in) : flag to denote first sub-step + firstFluxCall, & ! intent(inout) : flag to indicate if we are processing the first flux call + computeVegFlux, & ! intent(in) : flag to denote if computing energy flux over vegetation + scalarSolution, & ! intent(in) : flag to denote implementing the scalar solution + iStateSplit, & ! intent(in) : index of the state in the splitting operation + fluxMask, & ! intent(in) : mask for the fluxes used in this given state subset + fluxCount, & ! intent(inout) : number of times that fluxes are updated (should equal nSubsteps) + ! input/output: data structures + model_decisions, & ! intent(in) : model decisions + lookup_data, & ! intent(in) : lookup tables + type_data, & ! intent(in) : type of vegetation and soil + attr_data, & ! intent(in) : spatial attributes + forc_data, & ! intent(in) : model forcing data + mpar_data, & ! intent(in) : model parameters + indx_data, & ! intent(inout) : index data + prog_data, & ! intent(inout) : model prognostic variables for a local HRU + diag_data, & ! intent(inout) : model diagnostic variables for a local HRU + flux_data, & ! intent(inout) : model fluxes for a local HRU + deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables + bvar_data, & ! intent(in) : model variables for the local basin + ! output: model control + ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) + dtMultiplier, & ! intent(out) : substep multiplier (-) + nSubsteps, & ! intent(out) : number of substeps taken for a given split + failedMinimumStep, & ! intent(out) : flag to denote success of substepping for a given split + reduceCoupledStep, & ! intent(out) : flag to denote need to reduce the length of the coupled step + tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt + err,message) ! intent(out) : error code and error message + ! --------------------------------------------------------------------------------------- + ! structure allocations + USE allocspace_module,only:allocLocal ! allocate local data structures + ! simulation of fluxes and residuals given a trial state vector + USE systemSolv_module,only:systemSolv ! solve the system of equations for one time step + USE getVectorz_module,only:popStateVec ! populate the state vector + USE getVectorz_module,only:varExtract ! extract variables from the state vector + USE updateVarsFida_module,only:updateVarsFida ! update prognostic variables + USE varExtrFida_module, only:varExtractFida + ! identify name of variable type (for error message) + USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages + USE sysSolvFida_module,only:sysSolvFida + implicit none + ! --------------------------------------------------------------------------------------- + ! * dummy variables + ! --------------------------------------------------------------------------------------- + ! input: model control + real(dp),intent(in) :: dt ! time step (seconds) + real(dp),intent(in) :: dtInit ! initial time step (seconds) + real(dp),intent(in) :: dt_min ! minimum time step (seconds) + integer(i4b),intent(in) :: nState ! total number of state variables + logical(lgt),intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) + logical(lgt),intent(in) :: scalarSolution ! flag to denote implementing the scalar solution + integer(i4b),intent(in) :: iStateSplit ! index of the state in the splitting operation + type(var_flagVec),intent(in) :: fluxMask ! flags to denote if the flux is calculated in the given state subset + type(var_ilength),intent(inout) :: fluxCount ! number of times that the flux is updated (should equal nSubsteps) + ! input/output: data structures + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_i),intent(in) :: type_data ! type of vegetation and soil + type(var_d),intent(in) :: attr_data ! spatial attributes + type(var_d),intent(in) :: forc_data ! model forcing data + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU + type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin + ! output: model control + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + real(dp),intent(out) :: dtMultiplier ! substep multiplier (-) + integer(i4b),intent(out) :: nSubsteps ! number of substeps taken for a given split + logical(lgt),intent(out) :: failedMinimumStep ! flag to denote success of substepping for a given split + logical(lgt),intent(out) :: reduceCoupledStep ! flag to denote need to reduce the length of the coupled step + logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! --------------------------------------------------------------------------------------- + ! * general local variables + ! --------------------------------------------------------------------------------------- + ! error control + character(LEN=256) :: cmessage ! error message of downwind routine + ! general local variables + integer(i4b) :: iVar ! index of variables in data structures + integer(i4b) :: iSoil ! index of soil layers + integer(i4b) :: ixLayer ! index in a given domain + integer(i4b), dimension(1) :: ixMin,ixMax ! bounds of a given flux vector + ! time stepping + real(dp) :: dtSum ! sum of time from successful steps (seconds) + real(dp) :: dt_wght ! weight given to a given flux calculation + real(dp) :: dtSubstep ! length of a substep (s) + ! adaptive sub-stepping for the explicit solution + logical(lgt) :: failedSubstep ! flag to denote success of substepping for a given split + real(dp),parameter :: safety=0.85_dp ! safety factor in adaptive sub-stepping + real(dp),parameter :: reduceMin=0.1_dp ! mimimum factor that time step is reduced + real(dp),parameter :: increaseMax=4.0_dp ! maximum factor that time step is increased + ! adaptive sub-stepping for the implicit solution + integer(i4b) :: niter ! number of iterations taken + integer(i4b),parameter :: n_inc=5 ! minimum number of iterations to increase time step + integer(i4b),parameter :: n_dec=15 ! maximum number of iterations to decrease time step + real(dp),parameter :: F_inc = 1.25_dp ! factor used to increase time step + real(dp),parameter :: F_dec = 0.90_dp ! factor used to decrease time step + ! state and flux vectors + real(dp) :: untappedMelt(nState) ! un-tapped melt energy (J m-3 s-1) + real(dp) :: stateVecInit(nState) ! initial state vector (mixed units) + real(dp) :: stateVecTrial(nState) ! trial state vector (mixed units) + real(dp) :: stateVecPrime(nState) ! trial state vector (mixed units) + type(var_dlength) :: flux_temp ! temporary model fluxes + ! flags + logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt) :: checkMassBalance ! flag to check the mass balance + logical(lgt) :: checkNrgBalance + logical(lgt) :: waterBalanceError ! flag to denote that there is a water balance error + logical(lgt) :: nrgFluxModified ! flag to denote that the energy fluxes were modified + ! energy fluxes + real(dp) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) + real(dp) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + real(dp) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) + real(dp) :: sumSoilCompress + real(dp),allocatable :: sumLayerCompress(:) + ! --------------------------------------------------------------------------------------- + ! point to variables in the data structures + ! --------------------------------------------------------------------------------------- + globalVars: associate(& + ! number of layers + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! mapping between state vectors and control volumes + ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat ,& ! intent(in): [i4b(:)] list of indices for all active layers (inactive=integerMissing) + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) + ! model state variables (vegetation canopy) + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout): [dp] temperature of the vegetation canopy (K) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout): [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout): [dp] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout): [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variables (snow and soil domains) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat & ! intent(inout): [dp(:)] matric potential of liquid water (m) + ) ! end association with variables in the data structures + ! ********************************************************************************************************************************************************* + ! ********************************************************************************************************************************************************* + ! Procedure starts here + + ! initialize error control + err=0; message='varSubstepFida/' + + ! initialize flag for the success of the substepping + failedMinimumStep=.false. + + ! initialize the length of the substep + dtSubstep = dtInit + + ! allocate space for the temporary model flux structure + call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! initialize the model fluxes (some model fluxes are not computed in the iterations) + do iVar=1,size(flux_data%var) + flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + end do + + ! initialize the total energy fluxes (modified in updateProgFida) + sumCanopyEvaporation = 0._dp ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = 0._dp ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = 0._dp ! sensible heat flux from the canopy to the canopy air space (W m-2) + sumSoilCompress = 0._dp ! total soil compression + allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._dp ! soil compression by layer + + ! define the first flux call in a splitting operation + firstSplitOper = (.not.scalarSolution .or. iStateSplit==1) + + ! initialize subStep + dtSum = 0._dp ! keep track of the portion of the time step that is completed + nSubsteps = 0 + + ! loop through substeps + ! NOTE: continuous do statement with exit clause + substeps: do + + ! initialize error control + err=0; message='varSubstepFida/' + + !write(*,'(a,1x,3(f13.2,1x))') '***** new subStep: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt + !print*, 'scalarCanopyIce = ', prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) + !print*, 'scalarCanopyTemp = ', prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) + + ! ----- + ! * populate state vectors... + ! --------------------------- + + ! initialize state vectors + call popStateVec(& + ! input + nState, & ! intent(in): number of desired state variables + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output + stateVecInit, & ! intent(out): initial model state vector (mixed units) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + +! print *, 'dt in varSubstepFida = ', dtSubstep + ! ----- + ! * iterative solution... + ! ----------------------- + ! solve the system of equations for a given state subset + call sysSolvFida(& + ! input: model control + dtSubstep, & ! intent(in): time step (s) + nState, & ! intent(in): total number of state variables + firstSubStep, & ! intent(in): flag to denote first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution + ! input/output: data structures + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): index data + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_temp, & ! intent(inout): model fluxes for a local HRU + bvar_data, & ! intent(in): model variables for the local basin + model_decisions, & ! intent(in): model decisions + stateVecInit, & ! intent(in): initial state vector + ! output: model control + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + untappedMelt, & ! intent(out): un-tapped melt energy (J m-3 s-1) + stateVecTrial, & ! intent(out): updated state vector + stateVecPrime, & ! intent(out): updated state vector + reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step + tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt + niter, & ! intent(out): number of iterations taken + err,cmessage) ! intent(out): error code and error message + + if(err/=0)then + message=trim(message)//trim(cmessage) + if(err>0) return + endif + + + + ! if too much melt or need to reduce length of the coupled step then return + ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry + if(tooMuchMelt .or. reduceCoupledStep) return + + ! identify failure + failedSubstep = (err<0) + + ! check + if(globalPrintFlag)then + print*, 'niter, failedSubstep, dtSubstep = ', niter, failedSubstep, dtSubstep + print*, trim(cmessage) + endif + + ! reduce step based on failure + if(failedSubstep)then + err=0; message='varSubstepFida/' ! recover from failed convergence + dtMultiplier = 0.5_dp ! system failure: step halving + else + + ! ** implicit Euler: adjust step length based on iteration count + if(nitern_dec)then + dtMultiplier = F_dec + else + dtMultiplier = 1._dp + endif + + endif ! switch between failure and success + + ! check if we failed the substep + if(failedSubstep)then + + ! check that the substep is greater than the minimum step + if(dtSubstep*dtMultiplier exit, and either (1) try another solution method; or (2) reduce coupled step + failedMinimumStep=.true. + exit subSteps + + else ! step is still OK + dtSubstep = dtSubstep*dtMultiplier + cycle subSteps + endif ! if step is less than the minimum + + endif ! if failed the substep + + ! ----- + ! * update model fluxes... + ! ------------------------ + + ! NOTE: if we get to here then we are accepting the step + + ! NOTE: we get to here if iterations are successful + if(err/=0)then + message=trim(message)//'expect err=0 if updating fluxes' + return + endif + + ! identify the need to check the mass balance + checkMassBalance = .true. ! (.not.scalarSolution) + checkNrgBalance = .false. + + ! update prognostic variables + call updateProgFida(dtSubstep,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control + lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures + waterBalanceError,nrgFluxModified,err,cmessage) ! output: flags and error control + if(err/=0)then + message=trim(message)//trim(cmessage) + if(err>0) return + endif + + + + ! if water balance error then reduce the length of the coupled step + if(waterBalanceError)then + message=trim(message)//'water balance error' + reduceCoupledStep=.true. + err=-20; return + endif + + if(globalPrintFlag)& + print*, trim(cmessage)//': dt = ', dtSubstep + + ! recover from errors in prognostic update + if(err<0)then + + ! modify step + err=0 ! error recovery + dtSubstep = dtSubstep/2._dp + + ! check minimum: fail minimum step if there is an error in the update + if(dtSubstep0) then + ! scalar compression + if(.not.scalarSolution .or. iStateSplit==nSoil)& + sumSoilCompress = sumSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression + ! vector compression + do iSoil=1,nSoil + if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& + sumLayerCompress(iSoil) = sumLayerCompress(iSoil) + diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) ! soil compression in layers + end do + endif + + ! print progress + if(globalPrintFlag)& + write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt + + ! increment fluxes + dt_wght = dtSubstep/dt ! (define weight applied to each splitting operation) + do iVar=1,size(flux_meta) + if(count(fluxMask%var(iVar)%dat)>0) then + + !print*, flux_meta(iVar)%varname, fluxMask%var(iVar)%dat + + ! ** no domain splitting + if(count(ixLayerActive/=integerMissing)==nLayers)then + flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght + fluxCount%var(iVar)%dat(:) = fluxCount%var(iVar)%dat(:) + 1 + + ! ** domain splitting + else + ixMin=lbound(flux_data%var(iVar)%dat) + ixMax=ubound(flux_data%var(iVar)%dat) + do ixLayer=ixMin(1),ixMax(1) + if(fluxMask%var(iVar)%dat(ixLayer)) then + flux_data%var(iVar)%dat(ixLayer) = flux_data%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght + fluxCount%var(iVar)%dat(ixLayer) = fluxCount%var(iVar)%dat(ixLayer) + 1 + endif + end do + endif ! (domain splitting) + + endif ! (if the flux is desired) + end do ! (loop through fluxes) + + ! ------------------------------------------------------ + ! ------------------------------------------------------ + + ! increment the number of substeps + nSubsteps = nSubsteps+1 + + ! increment the sub-step legth + dtSum = dtSum + dtSubstep + !print*, 'dtSum, dtSubstep, dt, nSubsteps = ', dtSum, dtSubstep, dt, nSubsteps + + ! check that we have completed the sub-step + if(dtSum >= dt-verySmall)then + failedMinimumStep=.false. + exit subSteps + endif + + ! adjust length of the sub-step (make sure that we don't exceed the step) + dtSubstep = min(dt - dtSum, max(dtSubstep*dtMultiplier, dt_min) ) + + end do substeps ! time steps for variable-dependent sub-stepping + + ! save the energy fluxes + flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt ! canopy evaporation/condensation (kg m-2 s-1) + flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt ! sensible heat flux from the canopy to the canopy air space (W m-2) + + ! save the soil compression diagnostics + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress + do iSoil=1,nSoil + if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& + diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) = sumLayerCompress(iSoil) + end do + deallocate(sumLayerCompress) + + ! end associate statements + end associate globalVars + + ! update error codes + if(failedMinimumStep)then + err=-20 ! negative = recoverable error + message=trim(message)//'failed minimum step' + endif + + + end subroutine varSubstepFida + + + ! ********************************************************************************************************** + ! private subroutine updateProgFida: update prognostic variables + ! ********************************************************************************************************** + subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control + lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures + waterBalanceError,nrgFluxModified,err,message) ! output: flags and error control + USE getVectorz_module,only:varExtract ! extract variables from the state vector + USE updateVarsFida_module,only:updateVarsFida ! update prognostic variables + USE varExtrFida_module, only:varExtractFida + USE computEnthalpy_module,only:computEnthalpy + USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy + implicit none + ! model control + real(dp) ,intent(in) :: dt ! time step (s) + integer(i4b) ,intent(in) :: nSnow ! number of snow layers + integer(i4b) ,intent(in) :: nSoil ! number of soil layers + integer(i4b) ,intent(in) :: nLayers ! total number of layers + logical(lgt) ,intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature + logical(lgt) ,intent(in) :: computeVegFlux ! flag to compute the vegetation flux + real(dp) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) + real(dp) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) + real(dp) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) + logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance + logical(lgt) ,intent(in) :: checkNrgBalance ! flag to check the energy balance + ! data structures + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! indices for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + ! flags and error control + logical(lgt) ,intent(out) :: waterBalanceError ! flag to denote that there is a water balance error + logical(lgt) ,intent(out) :: nrgFluxModified ! flag to denote that the energy fluxes were modified + integer(i4b) ,intent(out) :: err ! error code + character(*) ,intent(out) :: message ! error message + ! ================================================================================================================== + ! general + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: ixSubset ! index within the state subset + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixControlIndex ! index within a given domain + real(dp) :: volMelt ! volumetric melt (kg m-3) + real(dp),parameter :: verySmall=epsilon(1._dp)*2._dp ! a very small number (deal with precision issues) + ! mass balance + real(dp) :: canopyBalance0,canopyBalance1 ! canopy storage at start/end of time step + real(dp) :: soilBalance0,soilBalance1 ! soil storage at start/end of time step + real(dp) :: vertFlux ! change in storage due to vertical fluxes + real(dp) :: tranSink,baseSink,compSink ! change in storage due to sink terms + real(dp) :: liqError ! water balance error + real(dp) :: fluxNet ! net water fluxes (kg m-2 s-1) + real(dp) :: superflousWat ! superflous water used for evaporation (kg m-2 s-1) + real(dp) :: superflousNrg ! superflous energy that cannot be used for evaporation (W m-2 [J m-2 s-1]) + character(LEN=256) :: cmessage ! error message of downwind routine + ! trial state variables + real(dp) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(dp) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(dp) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(dp),dimension(nLayers) :: mLayerTempTrial ! trial vector for temperature of layers in the snow and soil domains (K) + real(dp),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector for volumetric fraction of total water (-) + real(dp),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector for total water matric potential (m) + real(dp),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector for liquid water matric potential (m) + real(dp) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) + ! diagnostic variables + real(dp) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(dp) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(dp),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric fraction of liquid water (-) + real(dp),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric fraction of ice (-) + ! derivative of state variables + real(dp) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) + real(dp) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) + real(dp) :: scalarCanopyWatPrime ! trial value for liquid water storage in the canopy (kg m-2) + real(dp),dimension(nLayers) :: mLayerTempPrime ! trial vector for temperature of layers in the snow and soil domains (K) + real(dp),dimension(nLayers) :: mLayerVolFracWatPrime ! trial vector for volumetric fraction of total water (-) + real(dp),dimension(nSoil) :: mLayerMatricHeadPrime ! trial vector for total water matric potential (m) + real(dp),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! trial vector for liquid water matric potential (m) + real(dp) :: scalarAquiferStoragePrime ! trial value for storage of water in the aquifer (m) + ! diagnostic variables + real(dp) :: scalarCanopyLiqPrime ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(dp) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(dp),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector for volumetric fraction of liquid water (-) + real(dp),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector for volumetric fraction of ice (-) + real(dp) :: enthalpy0, enthalpy1 ! amount of energy required to bring the temperature to a reference value + real(dp) :: nrgError + integer(i4b) :: iLayer + real(dp),dimension(nLayers) :: left, mLayerNrgError + real(qp),dimension(nLayers) :: mLayerHeatCap + real(dp) :: scalarCanairEnthalpyTrial ! enthalpy of the canopy air space (J m-3) + real(dp) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3 + real(dp),dimension(nLayers) :: mLayerEnthalpyTrial + ! ------------------------------------------------------------------------------------------------------------------- + + ! ------------------------------------------------------------------------------------------------------------------- + ! point to flux variables in the data structure + associate(& + ! get indices for mass balance + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in) : [i4b] index of canopy hydrology state variable (mass) + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the soil domain + ! get indices for the un-tapped melt + ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in) : [i4b(:)] list of indices for all energy states + ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in) : [i4b(:)] indices defining the domain of the state (iname_veg, iname_snow, iname_soil) + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in) : [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in) : [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ! water fluxes + scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in) : [dp] rainfall rate (kg m-2 s-1) + scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(in) : [dp] rain reaches ground without touching the canopy (kg m-2 s-1) + scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(in) : [dp] canopy evaporation/condensation (kg m-2 s-1) + scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(in) : [dp] canopy transpiration (kg m-2 s-1) + scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(in) : [dp] drainage liquid water from vegetation canopy (kg m-2 s-1) + iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(in) : [dp(0:)] vertical liquid water flux at soil layer interfaces (-) + iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(in) : + mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) + mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(in) : [dp(:)] transpiration loss from each soil layer (m s-1) + mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(in) : [dp(:)] baseflow from each soil layer (m s-1) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in) : [dp(:)] change in storage associated with compression of the soil matrix (-) + scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the vegetation canopy (kg m-2 s-1) + scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the snow surface (kg m-2 s-1) + ! energy fluxes + scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ,& ! intent(in) : [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ,& ! intent(in) : [dp] sensible heat flux from the canopy to the canopy air space (W m-2) + ! domain depth + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in) : [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in) : [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! model state variables (vegetation canopy) + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the vegetation canopy (K) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout) : [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout) : [dp] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout) : [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variables (snow and soil domains) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout) : [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout) : [dp(:)] matric head (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(inout) : [dp(:)] matric potential of liquid water (m) +! enthalpy + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(inout): [dp(:)] enthalpy of the snow+soil layers (J m-3) + ! model state variables (aquifer) + scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(inout) : [dp(:)] storage of water in the aquifer (m) + ! error tolerance + absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1) & ! intent(in) : [dp] absolute convergence tolerance for vol frac liq water (-) + ) ! associating flux variables in the data structure + ! ------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='updateProgFida/' + + ! initialize water balancmLayerVolFracWatTriale error + waterBalanceError=.false. + + ! get storage at the start of the step + canopyBalance0 = merge(scalarCanopyWat, realMissing, computeVegFlux) + soilBalance0 = sum( (mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) + + ! ----- + ! * update states... + ! ------------------ + ! extract states from the state vector + call varExtract(& + ! input + stateVecTrial, & ! intent(in): model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(out): trial value of canopy ice content (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) + ! output: variables for the aquifer + scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + call varExtractFida(& + ! input + stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy + scalarCanairTempPrime, & ! intent(out): derivative of canopy air temperature (K) + scalarCanopyTempPrime, & ! intent(out): derivative of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(out): derivative of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(out): derivative of canopy liquid water (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempPrime, & ! intent(out): derivative of layer temperature (K) + mLayerVolFracWatPrime, & ! intent(out): derivative of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(out): derivative of volumetric liquid water content (-) + mLayerMatricHeadPrime, & ! intent(out): derivative of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(out): derivative of liquid water matric potential (m) + ! output: variables for the aquifer + scalarAquiferStoragePrime,& ! intent(out): derivative of storage of water in the aquifer (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + + !print*, 'after varExtract: scalarCanopyTempTrial =', scalarCanopyTempTrial ! trial value of canopy temperature (K) + !print*, 'after varExtract: scalarCanopyWatTrial =', scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + !print*, 'after varExtract: scalarCanopyLiqTrial =', scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) + !print*, 'after varExtract: scalarCanopyIceTrial =', scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + ! update diagnostic variables + call updateVarsFida(& + ! input + dt, & + doAdjustTemp, & ! intent(in): logical flag to adjust temperature to accou melt+freeze + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + mLayerVolFracWatTrial, & + mLayerMatricHeadTrial, & + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output: variables for the vegetation canopy + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempPrime, & ! + mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) + mLayerVolFracIcePrime, & ! + mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! ---- + ! * check energy balance + !------------------------ + + if(checkNrgBalance)then + ! compute enthalpy at t_{n+1} + call t2enthalpy(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) + ! output: enthalpy + scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + do concurrent (iLayer=1:nLayers) + left(iLayer) = (mLayerEnthalpyTrial(iLayer) - mLayerEnthalpy(iLayer)) * mLayerDepth(iLayer) + end do + + do concurrent (iLayer=1:nLayers) + mLayerNrgError(iLayer) = left(iLayer) + ( (iLayerNrgFlux(iLayer) - iLayerNrgFlux(iLayer-1)) )*dt + end do + +! write(1,*) mLayerNrgError(1:nLayers) +! write(2,*) mLayerEnthalpyTrial(1:nLayers) +! write(3,*) left(1:nLayers) + + endif + + ! ----- + ! * check mass balance... + ! ----------------------- + + ! NOTE: should not need to do this, since mass balance is checked in the solver + if(checkMassBalance)then + + ! check mass balance for the canopy + if(ixVegHyd/=integerMissing)then + + ! handle cases where fluxes empty the canopy + fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + if(-fluxNet*dt > canopyBalance0)then + + ! --> first add water + canopyBalance1 = canopyBalance0 + (scalarRainfall - scalarThroughfallRain)*dt + + ! --> next, remove canopy evaporation -- put the unsatisfied evap into sensible heat + canopyBalance1 = canopyBalance1 + scalarCanopyEvaporation*dt + if(canopyBalance1 < 0._dp)then + ! * get superfluous water and energy + superflousWat = -canopyBalance1/dt ! kg m-2 s-1 + superflousNrg = superflousWat*LH_vap ! W m-2 (J m-2 s-1) + ! * update fluxes and states + canopyBalance1 = 0._dp + scalarCanopyEvaporation = scalarCanopyEvaporation + superflousWat + scalarLatHeatCanopyEvap = scalarLatHeatCanopyEvap + superflousNrg + scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg + endif + + ! --> next, remove canopy drainage + canopyBalance1 = canopyBalance1 - scalarCanopyLiqDrainage*dt + if(canopyBalance1 < 0._dp)then + superflousWat = -canopyBalance1/dt ! kg m-2 s-1 + canopyBalance1 = 0._dp + scalarCanopyLiqDrainage = scalarCanopyLiqDrainage + superflousWat + endif + + ! update the trial state + scalarCanopyWatTrial = canopyBalance1 + + ! set the modification flag + nrgFluxModified = .true. + + else + canopyBalance1 = canopyBalance0 + fluxNet*dt + nrgFluxModified = .false. + endif ! cases where fluxes empty the canopy + + ! check the mass balance + fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial + if(abs(liqError) > absConvTol_liquid*10._dp*iden_water)then ! *10 because of precision issues + write(*,'(a,1x,f20.10)') 'dt = ', dt + write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 + write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 + write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt + write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt + write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt + write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt + write(*,'(a,1x,f20.10)') 'liqError = ', liqError + waterBalanceError = .true. + return + endif ! if there is a water balance error + endif ! if veg canopy + + ! check mass balance for soil + ! NOTE: fatal errors, though possible to recover using negative error codes + if(count(ixSoilOnlyHyd/=integerMissing)==nSoil)then + soilBalance1 = sum( (mLayerVolFracLiqTrial(nSnow+1:nLayers) + mLayerVolFracIceTrial(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) + vertFlux = -(iLayerLiqFluxSoil(nSoil) - iLayerLiqFluxSoil(0))*dt ! m s-1 --> m + tranSink = sum(mLayerTranspire)*dt ! m s-1 --> m + baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m + compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m + liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) + ! write(1,*) liqError + if(abs(liqError) > absConvTol_liquid*10._dp)then ! *10 because of precision issues + write(*,'(a,1x,f20.10)') 'dt = ', dt + write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 + write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 + write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux + write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink + write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink + write(*,'(a,1x,f20.10)') 'compSink = ', compSink + write(*,'(a,1x,f20.10)') 'liqError = ', liqError + write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid + waterBalanceError = .true. + return + endif ! if there is a water balance error + endif ! if hydrology states exist in the soil domain + +endif ! if checking the mass balance + + ! ----- + ! * remove untapped melt energy... + ! -------------------------------- + + ! only work with energy state variables + if(size(ixNrgOnly)>0)then ! energy state variables exist + + ! loop through energy state variables + do iState=1,size(ixNrgOnly) + + ! get index of the control volume within the domain + ixSubset = ixNrgOnly(iState) ! index within the state subset + ixFullVector = ixMapSubset2Full(ixSubset) ! index within full state vector + ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain + + ! compute volumetric melt (kg m-3) + volMelt = dt*untappedMelt(ixSubset)/LH_fus ! (kg m-3) + + ! update ice content + select case( ixDomainType(ixFullVector) ) + case(iname_cas); cycle ! do nothing, since there is no snow stored in the canopy air space + case(iname_veg); scalarCanopyIceTrial = scalarCanopyIceTrial - volMelt*canopyDepth ! (kg m-2) + case(iname_snow); mLayerVolFracIceTrial(ixControlIndex) = mLayerVolFracIceTrial(ixControlIndex) - volMelt/iden_ice ! (-) + case(iname_soil); mLayerVolFracIceTrial(ixControlIndex+nSnow) = mLayerVolFracIceTrial(ixControlIndex+nSnow) - volMelt/iden_water ! (-) + case default; err=20; message=trim(message)//'unable to identify domain type [remove untapped melt energy]'; return + end select + + ! update liquid water content + select case( ixDomainType(ixFullVector) ) + case(iname_cas); cycle ! do nothing, since there is no snow stored in the canopy air space + case(iname_veg); scalarCanopyLiqTrial = scalarCanopyLiqTrial + volMelt*canopyDepth ! (kg m-2) + case(iname_snow); mLayerVolFracLiqTrial(ixControlIndex) = mLayerVolFracLiqTrial(ixControlIndex) + volMelt/iden_water ! (-) + case(iname_soil); mLayerVolFracLiqTrial(ixControlIndex+nSnow) = mLayerVolFracLiqTrial(ixControlIndex+nSnow) + volMelt/iden_water ! (-) + case default; err=20; message=trim(message)//'unable to identify domain type [remove untapped melt energy]'; return + end select + + end do ! looping through energy variables + + ! ======================================================================================================== + + ! *** ice + + ! --> check if we removed too much water + if(scalarCanopyIceTrial < 0._dp .or. any(mLayerVolFracIceTrial < 0._dp) )then + + ! ** + ! canopy within numerical precision + if(scalarCanopyIceTrial < 0._dp)then + + if(scalarCanopyIceTrial > -verySmall)then + scalarCanopyLiqTrial = scalarCanopyLiqTrial - scalarCanopyIceTrial + scalarCanopyIceTrial = 0._dp + + ! encountered an inconsistency: spit the dummy + else + print*, 'dt = ', dt + print*, 'untappedMelt = ', untappedMelt + print*, 'untappedMelt*dt = ', untappedMelt*dt + print*, 'scalarCanopyiceTrial = ', scalarCanopyIceTrial + message=trim(message)//'melted more than the available water' + err=20; return + endif ! (inconsistency) + + endif ! if checking the canopy + ! ** + ! snow+soil within numerical precision + do iState=1,size(mLayerVolFracIceTrial) + + ! snow layer within numerical precision + if(mLayerVolFracIceTrial(iState) < 0._dp)then + + if(mLayerVolFracIceTrial(iState) > -verySmall)then + mLayerVolFracLiqTrial(iState) = mLayerVolFracLiqTrial(iState) - mLayerVolFracIceTrial(iState) + mLayerVolFracIceTrial(iState) = 0._dp + + ! encountered an inconsistency: spit the dummy + else + print*, 'dt = ', dt + print*, 'untappedMelt = ', untappedMelt + print*, 'untappedMelt*dt = ', untappedMelt*dt + print*, 'mLayerVolFracIceTrial = ', mLayerVolFracIceTrial + message=trim(message)//'melted more than the available water' + err=20; return + endif ! (inconsistency) + + endif ! if checking a snow layer + + end do ! (looping through state variables) + + endif ! (if we removed too much water) + + ! ======================================================================================================== + + ! *** liquid water + + ! --> check if we removed too much water + if(scalarCanopyLiqTrial < 0._dp .or. any(mLayerVolFracLiqTrial < 0._dp) )then + + ! ** + ! canopy within numerical precision + if(scalarCanopyLiqTrial < 0._dp)then + + if(scalarCanopyLiqTrial > -verySmall)then + scalarCanopyIceTrial = scalarCanopyIceTrial - scalarCanopyLiqTrial + scalarCanopyLiqTrial = 0._dp + + + ! encountered an inconsistency: spit the dummy + else + print*, 'dt = ', dt + print*, 'untappedMelt = ', untappedMelt + print*, 'untappedMelt*dt = ', untappedMelt*dt + print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial + message=trim(message)//'frozen more than the available water' + err=20; return + endif ! (inconsistency) + + endif ! checking the canopy + + ! ** + ! snow+soil within numerical precision + do iState=1,size(mLayerVolFracLiqTrial) + + ! snow layer within numerical precision + if(mLayerVolFracLiqTrial(iState) < 0._dp)then + + if(mLayerVolFracLiqTrial(iState) > -verySmall)then + mLayerVolFracIceTrial(iState) = mLayerVolFracIceTrial(iState) - mLayerVolFracLiqTrial(iState) + mLayerVolFracLiqTrial(iState) = 0._dp + + ! encountered an inconsistency: spit the dummy + else + print*, 'dt = ', dt + print*, 'untappedMelt = ', untappedMelt + print*, 'untappedMelt*dt = ', untappedMelt*dt + print*, 'mLayerVolFracLiqTrial = ', mLayerVolFracLiqTrial + message=trim(message)//'frozen more than the available water' + err=20; return + endif ! (inconsistency) + + endif ! checking a snow layer + + end do ! (looping through state variables) + + endif ! (if we removed too much water) + + endif ! (if energy state variables exist) + + ! ----- + ! * update enthalpy as a diagnostic variable... + ! -------------------------------- + mLayerEnthalpy = mLayerEnthalpyTrial + + ! ----- + ! * update prognostic variables... + ! -------------------------------- + + ! update state variables for the vegetation canopy + scalarCanairTemp = scalarCanairTempTrial ! trial value of canopy air temperature (K) + scalarCanopyTemp = scalarCanopyTempTrial ! trial value of canopy temperature (K) + scalarCanopyWat = scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + scalarCanopyLiq = scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) + scalarCanopyIce = scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + + ! update state variables for the snow+soil domain + mLayerTemp = mLayerTempTrial ! trial vector of layer temperature (K) + mLayerVolFracWat = mLayerVolFracWatTrial ! trial vector of volumetric total water content (-) + mLayerVolFracLiq = mLayerVolFracLiqTrial ! trial vector of volumetric liquid water content (-) + mLayerVolFracIce = mLayerVolFracIceTrial ! trial vector of volumetric ice water content (-) + mLayerMatricHead = mLayerMatricHeadTrial ! trial vector of matric head (m) + mLayerMatricHeadLiq = mLayerMatricHeadLiqTrial ! trial vector of matric head (m) + + ! update state variables for the aquifer + scalarAquiferStorage = scalarAquiferStorageTrial + + ! end associations to info in the data structures + end associate + + end subroutine updateProgFida + +end module varSubstepFida_module From 944b8507e480d2f1e52a8181b254f11c2f5b24eb Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sat, 6 Feb 2021 00:47:57 -0600 Subject: [PATCH 0009/1472] enthalpy added --- build/Makefile | 10 +- build/s_Makefile | 388 ------------------------- build/source/driver/summa_init.f90 | 31 +- build/source/driver/summa_modelRun.f90 | 4 + build/source/driver/summa_setup.f90 | 21 +- build/source/driver/summa_type.f90 | 10 +- build/source/dshare/data_types.f90 | 21 ++ build/source/dshare/get_ixname.f90 | 51 +++- build/source/dshare/globalData.f90 | 33 ++- build/source/dshare/popMetadat.f90 | 45 ++- build/source/dshare/var_lookup.f90 | 13 + build/source/engine/allocspace.f90 | 8 +- build/source/engine/checkStruc.f90 | 65 +++-- build/source/engine/coupled_em.f90 | 12 +- build/source/netcdf/def_output.f90 | 29 +- 15 files changed, 237 insertions(+), 504 deletions(-) delete mode 100644 build/s_Makefile diff --git a/build/Makefile b/build/Makefile index c4c6dd144..146164959 100644 --- a/build/Makefile +++ b/build/Makefile @@ -38,7 +38,7 @@ # Define core directory below which everything resides. This is the # parent directory of the 'build' directory -F_MASTER = /home/stiff/summa-develop +F_MASTER = /home/stiff/summa # Define the Fortran Compiler. If you are using gfortran, then this needs # to be version 6 or higher. This variable is simply used to select the right @@ -274,12 +274,12 @@ NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) # Define routines for the SUMMA model runs SUMMA_MODRUN = \ - indexState.f90 \ - getVectorz.f90 \ + indexState.f90 \ + getVectorz.f90 \ varExtrFida.f90 \ t2enthalpy.f90 \ - updateVars.f90 \ - updateVarsFida.f90 \ + updateVars.f90 \ + updateVarsFida.f90 \ updateVarsFida2.f90 \ var_derive.f90 \ read_force.f90 \ diff --git a/build/s_Makefile b/build/s_Makefile deleted file mode 100644 index 77dc52b2a..000000000 --- a/build/s_Makefile +++ /dev/null @@ -1,388 +0,0 @@ -#======================================================================== -# Makefile to compile SUMMA -#======================================================================== -# -# Recommended use: Copy this file to Makefile.local, edit it to your -# heart's content, and then run `make -f build/Makefile.local` from -# your top level SUMMA directory. Don't include the Makefile.local in -# any pull requests you make. -# -# Note that Makefile configurations that we commonly use can be found on -# the SUMMA wiki at: -# https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration -# feel free to add yours to that page. -# -# To troubleshoot your paths and setup, type 'make check' -# -# At a minimum you will need to set the following: -# * F_MASTER - top level summa directory -# * FC - compiler suite -# * FC_EXE - compiler executable -# * INCLUDES - path to include files -# * LIBRARIES - path to and libraries to include -# -# Some further options can be specified for OpenMP, etc. See in Part 0 of -# the Makefile. You don't need to make any changes in PART 1 and -# following unless you are doing SUMMA development and changed what -# needs to be compiled - -#======================================================================== -# PART 0: User-configurable part -#======================================================================== - -# The variables can be specified in one of two ways: -# * delete the '##' in front of the variable, fill out the entry, -# save the file and run make -# * make no changes to this file, but specify the variables in your -# environment before you run make - -# Define core directory below which everything resides. This is the -# parent directory of the 'build' directory -F_MASTER = /home/stiff/summa - -# Define the Fortran Compiler. If you are using gfortran, then this needs -# to be version 6 or higher. This variable is simply used to select the right -# compiler flags in the ifeq statements in this Makefile. The compiler -# executable is set separately as FC_EXE -# Currently this is either gfortran or ifort -FC = gfortran - -# Define the path for the compiler executable. This is the actual executable -# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin-gfortran-mp-6 -# FC and FC_EXE have to be consistent -FC_EXE = /usr/bin/gfortran-7 - -# Define the NetCDF and LAPACK libraries and path to include files. -# INCLUDES needs to be of the form (no quotes around the string): -# INCLUDES = -I -I -I<...> -I -# LIBRARIES needs to be of the form ( no quotes around the string): -# LIBRARIES = '-L -lnetcdff -L -lblas -L -l' -# If none of this makes sense, please talk to your system -# administrator. -INCLUDES = -I/usr/include -I/usr/share/doc/liblapack3 -I/usr/lib/x86_64-linux-gnu -I/usr/local/include \ - -I/usr/local/lib -I/usr/local/fortran -LDFLAGS += -L/usr/local/lib -LIBRARIES = -llapack -lgfortran -lnetcdff -lnetcdf -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod \ - -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod - -# Eventually we plan move to a real configure script, but for now we like -# to keep track of successful compilations of SUMMA on different platforms -# and with different compilers. If you are successful compiling SUMMA, -# please add your configuration (operating system and compiler plus -# part 0 of the Makefile) to the SUMMA wiki on github. - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# ------------ define compiler flags ---------------------------------------- - -# define open MP flags -isOpenMP = -FLAGS_OMP = -LIBOPENMP = - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# gfortran compiler flags -ifeq "$(FC)" "gfortran" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -fopenmp - endif - -# Production runs -FLAGS_NOAH = -O3 -ffree-form -fdefault-real-8 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) - -# Debug runs -#FLAGS_NOAH = -p -g -ffree-form -fdefault-real-8 -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -#FLAGS_COMM = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -#FLAGS_SUMMA = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds - -endif - -# ifort compiler flags -ifeq "$(FC)" "ifort" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -qopenmp - endif - -# Production runs -FLAGS_NOAH = -O3 -autodouble -noerror_limit -FR -auto -fltconsistency $(FLAGS_OMP) -FLAGS_COMM = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) - -# Debug runs -#FLAGS_NOAH = -O0 -p -g -warn nounused -autodouble -noerror_limit -FR -auto -WB -traceback -fltconsistency -#FLAGS_COMM = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -#FLAGS_SUMMA = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -endif - -#======================================================================== -# PART 1: Define directory paths -#======================================================================== - -# Core directory that contains source code -F_KORE_DIR = $(F_MASTER)/build/source - -# Location of the compiled modules -MOD_PATH = $(F_MASTER)/build - -# Define the directory for the executables -EXE_PATH = $(F_MASTER)/bin - -#======================================================================== -# PART 2: Assemble all of the SUMMA sub-routines -#======================================================================== - -# Define directories -DRIVER_DIR = $(F_KORE_DIR)/driver -HOOKUP_DIR = $(F_KORE_DIR)/hookup -NETCDF_DIR = $(F_KORE_DIR)/netcdf -DSHARE_DIR = $(F_KORE_DIR)/dshare -NUMREC_DIR = $(F_KORE_DIR)/numrec -NOAHMP_DIR = $(F_KORE_DIR)/noah-mp -ENGINE_DIR = $(F_KORE_DIR)/engine - -# utilities -SUMMA_NRUTIL= \ - nrtype.f90 \ - f2008funcs.f90 \ - nr_utility.f90 -NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) - -# -# Numerical recipes procedures -# NOTE: all numerical recipes procedures are now replaced with free versions -SUMMA_NRPROC= \ - expIntegral.f90 \ - spline_int.f90 -NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) - -# Hook-up modules (set files and directory paths) -SUMMA_HOOKUP= \ - ascii_util.f90 \ - summaFileManager.f90 -HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) - -# Data modules -SUMMA_DATAMS= \ - multiconst.f90 \ - var_lookup.f90 \ - data_types.f90 \ - globalData.f90 \ - flxMapping.f90 \ - get_ixname.f90 \ - popMetadat.f90 \ - outpt_stat.f90 -DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) - -# utility modules -SUMMA_UTILMS= \ - time_utils.f90 \ - mDecisions.f90 \ - snow_utils.f90 \ - soil_utils.f90 \ - updatState.f90 \ - matrixOper.f90 -UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) - -# Model guts -SUMMA_MODGUT= \ - MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) - -# Solver -SUMMA_SOLVER= \ - vegPhenlgy.f90 \ - diagn_evar.f90 \ - stomResist.f90 \ - groundwatr.f90 \ - vegSWavRad.f90 \ - vegNrgFlux.f90 \ - ssdNrgFlux.f90 \ - vegLiqFlux.f90 \ - snowLiqFlx.f90 \ - soilLiqFlx.f90 \ - bigAquifer.f90 \ - computFlux.f90 \ - computResid.f90 \ - computJacob.f90 \ - eval8summa.f90 \ - summaSolve.f90 \ - systemSolv.f90 \ - varSubstep.f90 \ - opSplittin.f90 \ - coupled_em.f90 \ - run_oneHRU.f90 \ - run_oneGRU.f90 -SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) - -# Define routines for SUMMA preliminaries -SUMMA_PRELIM= \ - conv_funcs.f90 \ - sunGeomtry.f90 \ - convE2Temp.f90 \ - allocspace.f90 \ - checkStruc.f90 \ - childStruc.f90 \ - ffile_info.f90 \ - read_attrb.f90 \ - read_pinit.f90 \ - pOverwrite.f90 \ - read_param.f90 \ - paramCheck.f90 \ - check_icond.f90 -PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) - -SUMMA_NOAHMP= \ - module_model_constants.F \ - module_sf_noahutl.F \ - module_sf_noahlsm.F \ - module_sf_noahmplsm.F - -NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) - -# Define routines for the SUMMA model runs -SUMMA_MODRUN = \ - indexState.f90 \ - getVectorz.f90 \ - updateVars.f90 \ - var_derive.f90 \ - read_force.f90 \ - derivforce.f90 \ - snowAlbedo.f90 \ - canopySnow.f90 \ - tempAdjust.f90 \ - snwCompact.f90 \ - layerMerge.f90 \ - layerDivide.f90 \ - volicePack.f90 \ - qTimeDelay.f90 -MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) - -# Define routines for the solver -SUMMA_MSOLVE = \ - -# Define NetCDF routines -SUMMA_NETCDF = \ - netcdf_util.f90 \ - def_output.f90 \ - modelwrite.f90 \ - read_icond.f90 -NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) - -# ... stitch together common programs -COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) - -# ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) - -# Define the driver routine -SUMMA_DRIVER= \ - summa_type.f90 \ - summa_util.f90 \ - summa_alarms.f90 \ - summa_globalData.f90 \ - summa_defineOutput.f90 \ - summa_init.f90 \ - summa_setup.f90 \ - summa_restart.f90 \ - summa_forcing.f90 \ - summa_modelRun.f90 \ - summa_writeOutput.f90 \ - summa_driver.f90 -DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) - -# Define the executable -DRIVER__EX = summa.exe - -# Define version number -VERSIONFILE = $(DRIVER_DIR)/summaversion.inc -VERSION = $(shell git tag | tail -n 1) -BULTTIM = $(shell date) -GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') -GITHASH = $(shell git rev-parse HEAD) - -#======================================================================== -# PART 3: Checks -#====================================================================== -# make sure that the paths are defined. These are just some high level checks -ifndef F_MASTER - $(error F_MASTER is undefined) -endif -ifndef FC - $(error FC is undefined: Specify your compiler) -endif -ifndef FC_EXE - $(error FC_EXE is undefined: Specify your compiler executable) -endif -ifndef FLAGS_SUMMA - $(error Specify flags for your compiler: $(FC)) -endif -ifndef INCLUDES - $(error INCLUDES is undefined) -endif -ifndef LIBRARIES - $(error LIBRARIES is undefined) -endif - -#======================================================================== -# PART 4: compilation -#====================================================================== - -# Compile -all: compile_noah compile_comm compile_summa link clean install - -check: - $(info) - $(info Displaying make variables:) - $(info F_MASTER : $(F_MASTER)) - $(info EXE_PATH : $(EXE_PATH)) - $(info FC : $(FC)) - $(info INCLUDES : $(INCLUDES)) - $(info LIBRARIES : $(LIBRARIES)) - $(info FLAGS_NOAH : $(FLAGS_NOAH)) - $(info FLAGS_COMM : $(FLAGS_COMM)) - $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) - $(info) - -# update version information -update_version: - echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) - echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) - -# compile Noah-MP routines -compile_noah: - $(FC_EXE) $(FLAGS_NOAH) -c $(NOAHMP) - -# compile common routines -compile_comm: - $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) - -# compile SUMMA routines -compile_summa: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ - $(INCLUDES) - -# link routines -link: - $(FC_EXE) -g *.o $(LIBRARIES) -o $(DRIVER__EX) - -# Remove object files -clean: - rm -f *.o - rm -f *.mod - rm -f soil_veg_gen_parm__genmod.f90 - -# Copy the executable to the bin directory -install: - @mkdir -p $(EXE_PATH) - @mv $(DRIVER__EX) $(EXE_PATH) - $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) diff --git a/build/source/driver/summa_init.f90 b/build/source/driver/summa_init.f90 index 1f65af736..df8673059 100755 --- a/build/source/driver/summa_init.f90 +++ b/build/source/driver/summa_init.f90 @@ -34,6 +34,7 @@ module summa_init USE globalData,only:mpar_meta,indx_meta ! metadata structures USE globalData,only:bpar_meta,bvar_meta ! metadata structures USE globalData,only:averageFlux_meta ! metadata for time-step average fluxes +USE globalData,only:lookup_meta ! statistics metadata structures USE globalData,only:statForc_meta ! child metadata for stats @@ -114,7 +115,8 @@ subroutine summa_initialize(summa1_struc, err, message) ! --------------------------------------------------------------------------------------- ! associate to elements in the data structure summaVars: associate(& - + ! lookup table data structure + lookupStruct => summa1_struc%lookupStruct , & ! x%gru(:)%hru(:)%z(:)%var(:)%lookup(:) -- lookup tables ! statistics structures forcStat => summa1_struc%forcStat , & ! x%gru(:)%hru(:)%var(:)%dat -- model forcing data progStat => summa1_struc%progStat , & ! x%gru(:)%hru(:)%var(:)%dat -- model prognostic (state) variables @@ -234,19 +236,20 @@ subroutine summa_initialize(summa1_struc, err, message) do iStruct=1,size(structInfo) ! allocate space select case(trim(structInfo(iStruct)%structName)) - case('time'); call allocGlobal(time_meta, timeStruct, err, cmessage) ! model forcing data - case('forc'); call allocGlobal(forc_meta, forcStruct, err, cmessage) ! model forcing data - case('attr'); call allocGlobal(attr_meta, attrStruct, err, cmessage) ! local attributes for each HRU - case('type'); call allocGlobal(type_meta, typeStruct, err, cmessage) ! local classification of soil veg etc. for each HRU - case('id' ); call allocGlobal(id_meta, idStruct, err, message) ! local values of hru and gru IDs - case('mpar'); call allocGlobal(mpar_meta, mparStruct, err, cmessage) ! model parameters - case('indx'); call allocGlobal(indx_meta, indxStruct, err, cmessage) ! model variables - case('prog'); call allocGlobal(prog_meta, progStruct, err, cmessage) ! model prognostic (state) variables - case('diag'); call allocGlobal(diag_meta, diagStruct, err, cmessage) ! model diagnostic variables - case('flux'); call allocGlobal(flux_meta, fluxStruct, err, cmessage) ! model fluxes - case('bpar'); call allocGlobal(bpar_meta, bparStruct, err, cmessage) ! basin-average parameters - case('bvar'); call allocGlobal(bvar_meta, bvarStruct, err, cmessage) ! basin-average variables - case('deriv'); cycle + case('time' ); call allocGlobal(time_meta, timeStruct, err, cmessage) ! model forcing data + case('forc' ); call allocGlobal(forc_meta, forcStruct, err, cmessage) ! model forcing data + case('attr' ); call allocGlobal(attr_meta, attrStruct, err, cmessage) ! local attributes for each HRU + case('type' ); call allocGlobal(type_meta, typeStruct, err, cmessage) ! local classification of soil veg etc. for each HRU + case('id' ); call allocGlobal(id_meta, idStruct, err, message) ! local values of hru and gru IDs + case('mpar' ); call allocGlobal(mpar_meta, mparStruct, err, cmessage) ! model parameters + case('indx' ); call allocGlobal(indx_meta, indxStruct, err, cmessage) ! model variables + case('prog' ); call allocGlobal(prog_meta, progStruct, err, cmessage) ! model prognostic (state) variables + case('diag' ); call allocGlobal(diag_meta, diagStruct, err, cmessage) ! model diagnostic variables + case('flux' ); call allocGlobal(flux_meta, fluxStruct, err, cmessage) ! model fluxes + case('bpar' ); call allocGlobal(bpar_meta, bparStruct, err, cmessage) ! basin-average parameters + case('bvar' ); call allocGlobal(bvar_meta, bvarStruct, err, cmessage) ! basin-average variables + case('lookup'); call allocGlobal(lookup_meta, lookupStruct, err, cmessage) ! basin-average variables + case('deriv' ); cycle case default; err=20; message='unable to find structure name: '//trim(structInfo(iStruct)%structName) end select ! check errors diff --git a/build/source/driver/summa_modelRun.f90 b/build/source/driver/summa_modelRun.f90 index 5080921c4..cc3777d03 100755 --- a/build/source/driver/summa_modelRun.f90 +++ b/build/source/driver/summa_modelRun.f90 @@ -219,6 +219,9 @@ subroutine summa_runPhysics(modelTimeStep, summa1_struc, err, message) bparStruct => summa1_struc%bparStruct , & ! x%gru(:)%var(:) -- basin-average parameters bvarStruct => summa1_struc%bvarStruct , & ! x%gru(:)%var(:)%dat -- basin-average variables + ! lookup table structure + lookupStruct => summa1_struc%lookupStruct , & ! x%gru(:)%hru(:)%z(:)%var(:)%lookup -- lookup-tables + ! run time variables greenVegFrac_monthly => summa1_struc%greenVegFrac_monthly, & ! fraction of green vegetation in each month (0-1) computeVegFlux => summa1_struc%computeVegFlux , & ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) @@ -250,6 +253,7 @@ subroutine summa_runPhysics(modelTimeStep, summa1_struc, err, message) typeStruct%gru(iGRU), & ! intent(in): local classification of soil veg etc. for each HRU idStruct%gru(iGRU), & ! intent(in): local classification of soil veg etc. for each HRU attrStruct%gru(iGRU), & ! intent(in): local attributes for each HRU + lookupStruct%gru(iGRU), & ! intent(in): local lookup tables for each HRU ! data structures (input-output) mparStruct%gru(iGRU), & ! intent(inout): local model parameters indxStruct%gru(iGRU), & ! intent(inout): model indices diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index 14aa86b27..c23f9784c 100755 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -29,7 +29,9 @@ module summa_setup USE var_lookup,only:iLookATTR ! look-up values for local attributes USE var_lookup,only:iLookTYPE ! look-up values for classification of veg, soils etc. USE var_lookup,only:iLookPARAM ! look-up values for local column model parameters -USE var_lookup,only:iLookID ! look-up values for local column model parameters +USE var_lookup,only:iLookINDEX ! look-up values for local column model indices +USE var_lookup,only:iLookLOOKUP ! look-up values for local column lookup tables +USE var_lookup,only:iLookID ! look-up values for local column model ids USE var_lookup,only:iLookBVAR ! look-up values for basin-average model variables USE var_lookup,only:iLookDECISIONS ! look-up values for model decisions USE globalData,only:urbanVegCategory ! vegetation category for urban areas @@ -70,6 +72,7 @@ subroutine summa_paramSetup(summa1_struc, err, message) USE pOverwrite_module,only:pOverwrite ! module to overwrite default parameter values with info from the Noah tables USE read_param_module,only:read_param ! module to read model parameter sets USE ConvE2Temp_module,only:E2T_lookup ! module to calculate a look-up table for the temperature-enthalpy conversion + USE t2enthalpy_module,only:T2E_lookup ! module to calculate a look-up table for the temperature-enthalpy conversion USE var_derive_module,only:fracFuture ! module to calculate the fraction of runoff in future time steps (time delay histogram) USE module_sf_noahmplsm,only:read_mp_veg_parameters ! module to read NOAH vegetation tables ! global data structures @@ -126,7 +129,10 @@ subroutine summa_paramSetup(summa1_struc, err, message) ! basin-average structures bparStruct => summa1_struc%bparStruct , & ! x%gru(:)%var(:) -- basin-average parameters bvarStruct => summa1_struc%bvarStruct , & ! x%gru(:)%var(:)%dat -- basin-average variables - + + ! lookup table structure + lookupStruct => summa1_struc%lookupStruct , & ! x%gru(:)%hru(:)%z(:)%var(:)%lookup -- lookup-tables + ! miscellaneous variables upArea => summa1_struc%upArea , & ! area upslope of each HRU nGRU => summa1_struc%nGRU , & ! number of grouped response units @@ -280,10 +286,19 @@ subroutine summa_paramSetup(summa1_struc, err, message) call paramCheck(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! calculate a look-up table for the temperature-enthalpy conversion + ! calculate a look-up table for the temperature-enthalpy conversion: snow + ! NOTE1: this should eventually be replaced by the more general routine below + ! NOTE2: this does not actually need to be called for each HRU and GRU call E2T_lookup(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + ! calculate a lookup table to compute enthalpy from temperature + call T2E_lookup(gru_struc(iGRU)%hruInfo(iHRU)%nSoil, & ! intent(in): number of soil layers + mparStruct%gru(iGRU)%hru(iHRU), & ! intent(in): parameter data structure + lookupStruct%gru(iGRU)%hru(iHRU), & ! intent(inout): lookup table data structure + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + ! overwrite the vegetation height HVT(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex)) = mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%heightCanopyTop)%dat(1) HVB(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex)) = mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%heightCanopyBottom)%dat(1) diff --git a/build/source/driver/summa_type.f90 b/build/source/driver/summa_type.f90 index e44418816..81a90df1b 100755 --- a/build/source/driver/summa_type.f90 +++ b/build/source/driver/summa_type.f90 @@ -42,10 +42,12 @@ MODULE summa_type gru_doubleVec, & ! x%gru(:)%var(:)%dat (dp) ! gru+hru dimension gru_hru_int, & ! x%gru(:)%hru(:)%var(:) (i4b) - gru_hru_int8, & ! x%gru(:)%hru(:)%var(:) (i8b) + gru_hru_int8, & ! x%gru(:)%hru(:)%var(:) (i8b) gru_hru_double, & ! x%gru(:)%hru(:)%var(:) (dp) gru_hru_intVec, & ! x%gru(:)%hru(:)%var(:)%dat (i4b) - gru_hru_doubleVec ! x%gru(:)%hru(:)%var(:)%dat (dp) + gru_hru_doubleVec, & ! x%gru(:)%hru(:)%var(:)%dat (dp) + ! gru+hru+z dimension + gru_hru_z_vLookup ! x%gru(:)%hru(:)%z(:)%var(:)%lookup(:) (dp) implicit none private @@ -53,7 +55,9 @@ MODULE summa_type ! * master summa data type ! ***************************************************************************** type, public :: summa1_type_dec - + ! define the lookup tables + type(gru_hru_z_vLookup) :: lookupStruct ! x%gru(:)%hru(:)%z(:)%var(:)%lookup(:) -- lookup tables + ! define the statistics structures type(gru_hru_doubleVec) :: forcStat ! x%gru(:)%hru(:)%var(:)%dat -- model forcing data type(gru_hru_doubleVec) :: progStat ! x%gru(:)%hru(:)%var(:)%dat -- model prognostic (state) variables diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index cf20b1e89..b199437f1 100755 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -127,6 +127,27 @@ MODULE data_types ! *********************************************************************************************************** ! Define hierarchal derived data types ! *********************************************************************************************************** + ! define derived types to hold look-up tables for each soil layer + ! ** double precision type + type, public :: dLookup + real(dp),allocatable :: lookup(:) ! lookup(:) + endtype dLookup + ! ** double precision type for a variable number of soil layers; variable length + type, public :: vLookup + type(dLookup),allocatable :: var(:) ! var(:)%lookup(:) + endtype vLookup + ! ** double precision type for a variable number of soil layers + type, public :: zLookup + type(vLookup),allocatable :: z(:) ! z(:)%var(:)%lookup(:) + endtype zLookup + ! ** double precision type for a variable number of soil layers + type, public :: hru_z_vLookup + type(zLookup),allocatable :: hru(:) ! hru(:)%z(:)%var(:)%lookup(:) + endtype hru_z_vLookup + ! ** double precision type for a variable number of soil layers + type, public :: gru_hru_z_vLookup + type(hru_z_vLookup),allocatable :: gru(:) ! gru(:)%hru(:)%z(:)%var(:)%lookup(:) + endtype gru_hru_z_vLookup ! define derived types to hold multivariate data for a single variable (different variables have different length) ! NOTE: use derived types here to facilitate adding the "variable" dimension ! ** double precision type diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 1d136c8ac..3bb3f4df1 100755 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -488,6 +488,10 @@ function get_ixdiag(varName) case('scalarLambda_wetsoil' ); get_ixdiag = iLookDIAG%scalarLambda_wetsoil ! thermal conductivity of wet soil (W m-1) case('mLayerThermalC' ); get_ixdiag = iLookDIAG%mLayerThermalC ! thermal conductivity at the mid-point of each layer (W m-1 K-1) case('iLayerThermalC' ); get_ixdiag = iLookDIAG%iLayerThermalC ! thermal conductivity at the interface of each layer (W m-1 K-1) + ! enthalpy + case('scalarCanairEnthalpy' ); get_ixdiag = iLookDIAG%scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) + case('scalarCanopyEnthalpy' ); get_ixdiag = iLookDIAG%scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) + case('mLayerEnthalpy' ); get_ixdiag = iLookDIAG%mLayerEnthalpy ! enthalpy of the snow+soil layers (J m-3) ! forcing case('scalarVPair' ); get_ixdiag = iLookDIAG%scalarVPair ! vapor pressure of the air above the vegetation canopy (Pa) case('scalarVP_CanopyAir' ); get_ixdiag = iLookDIAG%scalarVP_CanopyAir ! vapor pressure of the canopy air space (Pa) @@ -980,19 +984,20 @@ subroutine get_ixUnknown(varName,typeName,vDex,err,message) ! poll variable index plus return which structure it was found in do iStruc = 1,size(structInfo) select case(trim(structInfo(iStruc)%structName)) - case ('time' ); vDex = get_ixTime(trim(varName)) - case ('forc' ); vDex = get_ixForce(trim(varName)) - case ('attr' ); vDex = get_ixAttr(trim(varName)) - case ('type' ); vDex = get_ixType(trim(varName)) - case ('id' ); vDex = get_ixId(trim(varName)) - case ('mpar' ); vDex = get_ixParam(trim(varName)) - case ('indx' ); vDex = get_ixIndex(trim(varName)) - case ('prog' ); vDex = get_ixProg(trim(varName)) - case ('diag' ); vDex = get_ixDiag(trim(varName)) - case ('flux' ); vDex = get_ixFlux(trim(varName)) - case ('bpar' ); vDex = get_ixBpar(trim(varName)) - case ('bvar' ); vDex = get_ixBvar(trim(varName)) - case ('deriv'); vDex = get_ixDeriv(trim(varName)) + case ('time' ); vDex = get_ixTime(trim(varName)) + case ('forc' ); vDex = get_ixForce(trim(varName)) + case ('attr' ); vDex = get_ixAttr(trim(varName)) + case ('type' ); vDex = get_ixType(trim(varName)) + case ('id' ); vDex = get_ixId(trim(varName)) + case ('mpar' ); vDex = get_ixParam(trim(varName)) + case ('indx' ); vDex = get_ixIndex(trim(varName)) + case ('prog' ); vDex = get_ixProg(trim(varName)) + case ('diag' ); vDex = get_ixDiag(trim(varName)) + case ('flux' ); vDex = get_ixFlux(trim(varName)) + case ('bpar' ); vDex = get_ixBpar(trim(varName)) + case ('bvar' ); vDex = get_ixBvar(trim(varName)) + case ('deriv'); vDex = get_ixDeriv(trim(varName)) + case ('lookup'); vDex = get_ixLookup(trim(varName)) end select if (vDex>0) then; typeName=trim(structInfo(iStruc)%structName); return; end if end do @@ -1001,6 +1006,26 @@ subroutine get_ixUnknown(varName,typeName,vDex,err,message) err=20;message=trim(message)//'variable '//trim(varName)//' is not found in any structure'; return end subroutine get_ixUnknown + + ! ******************************************************************************************************************* + ! public function get_ixfreq: get the index of the named variables for the output frequencies + ! ******************************************************************************************************************* + function get_ixLookup(varName) + USE var_lookup,only:iLookLOOKUP ! indices of the named variables + implicit none + ! define dummy variables + character(*), intent(in) :: varName ! variable name + integer(i4b) :: get_ixLookup ! index of the named variable + ! get the index of the named variables + select case(trim(varName)) + case('temperature'); get_ixLookup = iLookLOOKUP%temperature ! temperature (K) + case('enthalpy' ); get_ixLookup = iLookLOOKUP%enthalpy ! enthalpy (J m-3) + case('deriv2' ); get_ixLookup = iLookLOOKUP%deriv2 ! secind derivative of the interpolating function + ! get to here if cannot find the variable + case default + get_ixLookup = integerMissing + end select + end function get_ixLookup ! ******************************************************************************************************************* ! public function get_ixfreq: get the index of the named variables for the output frequencies diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 index 68300c427..e5d5eca8d 100755 --- a/build/source/dshare/globalData.f90 +++ b/build/source/dshare/globalData.f90 @@ -53,6 +53,7 @@ MODULE globalData USE var_lookup,only:maxvarBpar ! basin-average parameters: maximum number variables USE var_lookup,only:maxvarDecisions ! maximum number of decisions USE var_lookup,only:maxvarFreq ! maximum number of output files + USE var_lookup,only:maxvarLookup ! maximum number of variables in the lookup tables implicit none private @@ -173,21 +174,22 @@ MODULE globalData real(dp),parameter,public :: dx = 1.e-8_dp ! finite difference increment ! define summary information on all data structures - integer(i4b),parameter :: nStruct=13 ! number of data structures + integer(i4b),parameter :: nStruct=14 ! number of data structures type(struct_info),parameter,public,dimension(nStruct) :: structInfo=(/& - struct_info('time', 'TIME' , maxvarTime ), & ! the time data structure - struct_info('forc', 'FORCE', maxvarForc ), & ! the forcing data structure - struct_info('attr', 'ATTR' , maxvarAttr ), & ! the attribute data structure - struct_info('type', 'TYPE' , maxvarType ), & ! the type data structure - struct_info('id' , 'ID' , maxvarId ), & ! the type data structure - struct_info('mpar', 'PARAM', maxvarMpar ), & ! the model parameter data structure - struct_info('bpar', 'BPAR' , maxvarBpar ), & ! the basin parameter data structure - struct_info('bvar', 'BVAR' , maxvarBvar ), & ! the basin variable data structure - struct_info('indx', 'INDEX', maxvarIndx ), & ! the model index data structure - struct_info('prog', 'PROG', maxvarProg ), & ! the prognostic (state) variable data structure - struct_info('diag', 'DIAG' , maxvarDiag ), & ! the diagnostic variable data structure - struct_info('flux', 'FLUX' , maxvarFlux ), & ! the flux data structure - struct_info('deriv', 'DERIV', maxvarDeriv) /) ! the model derivative data structure + struct_info('time', 'TIME' , maxvarTime ), & ! the time data structure + struct_info('forc', 'FORCE', maxvarForc ), & ! the forcing data structure + struct_info('attr', 'ATTR' , maxvarAttr ), & ! the attribute data structure + struct_info('type', 'TYPE' , maxvarType ), & ! the type data structure + struct_info('id' , 'ID' , maxvarId ), & ! the type data structure + struct_info('mpar', 'PARAM', maxvarMpar ), & ! the model parameter data structure + struct_info('bpar', 'BPAR' , maxvarBpar ), & ! the basin parameter data structure + struct_info('bvar', 'BVAR' , maxvarBvar ), & ! the basin variable data structure + struct_info('indx', 'INDEX', maxvarIndx ), & ! the model index data structure + struct_info('prog', 'PROG', maxvarProg ), & ! the prognostic (state) variable data structure + struct_info('diag', 'DIAG' , maxvarDiag ), & ! the diagnostic variable data structure + struct_info('flux', 'FLUX' , maxvarFlux ), & ! the flux data structure + struct_info('deriv', 'DERIV', maxvarDeriv ), & ! the model derivative data structure + struct_info('lookup', 'LOOKUP', maxvarLookup) /) ! the lookup table data structure ! fixed model decisions logical(lgt) , parameter, public :: overwriteRSMIN=.false. ! flag to overwrite RSMIN @@ -209,13 +211,14 @@ MODULE globalData type(var_info),save,public :: forc_meta(maxvarForc) ! model forcing data type(var_info),save,public :: attr_meta(maxvarAttr) ! local attributes type(var_info),save,public :: type_meta(maxvarType) ! local classification of veg, soil, etc. - type(var_info),save,public :: id_meta(maxvarId) ! local classification of veg, soil, etc. + type(var_info),save,public :: id_meta(maxvarId) ! local HRU id type(var_info),save,public :: mpar_meta(maxvarMpar) ! local model parameters for each HRU type(var_info),save,public :: indx_meta(maxvarIndx) ! local model indices for each HRU type(var_info),save,public :: prog_meta(maxvarProg) ! local state variables for each HRU type(var_info),save,public :: diag_meta(maxvarDiag) ! local diagnostic variables for each HRU type(var_info),save,public :: flux_meta(maxvarFlux) ! local model fluxes for each HRU type(var_info),save,public :: deriv_meta(maxvarDeriv) ! local model derivatives for each HRU + type(var_info),save,public :: lookup_meta(maxvarLookup) ! local lookup tables for each HRU type(var_info),save,public :: bpar_meta(maxvarBpar) ! basin parameters for aggregated processes type(var_info),save,public :: bvar_meta(maxvarBvar) ! basin variables for aggregated processes diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 37eb775d3..6e28f4145 100755 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -31,22 +31,24 @@ subroutine popMetadat(err,message) USE globalData, only: diag_meta ! data structure for local diagnostic variables USE globalData, only: flux_meta ! data structure for local flux variables USE globalData, only: deriv_meta ! data structure for local flux derivatives + USE globalData, only: lookup_meta ! data structure for lookup tables ! structures of named variables - USE var_lookup, only: iLookTIME ! named variables for time data structure - USE var_lookup, only: iLookFORCE ! named variables for forcing data structure - USE var_lookup, only: iLookTYPE ! named variables for categorical attribute data structure - USE var_lookup, only: iLookID ! named variables for hru and gru ID metadata - USE var_lookup, only: iLookATTR ! named variables for real valued attribute data structure - USE var_lookup, only: iLookPARAM ! named variables for local parameter data structure - USE var_lookup, only: iLookBPAR ! named variables for basin parameter data structure - USE var_lookup, only: iLookBVAR ! named variables for basin model variable data structure - USE var_lookup, only: iLookINDEX ! named variables for index variable data structure - USE var_lookup, only: iLookPROG ! named variables for local state variables - USE var_lookup, only: iLookDIAG ! named variables for local diagnostic variables - USE var_lookup, only: iLookFLUX ! named variables for local flux variables - USE var_lookup, only: iLookDERIV ! named variables for local flux derivatives - USE var_lookup, only: maxvarFreq ! number of output frequencies - USE var_lookup, only: maxvarStat ! number of statistics + USE var_lookup, only: iLookTIME ! named variables for time data structure + USE var_lookup, only: iLookFORCE ! named variables for forcing data structure + USE var_lookup, only: iLookTYPE ! named variables for categorical attribute data structure + USE var_lookup, only: iLookID ! named variables for hru and gru ID metadata + USE var_lookup, only: iLookATTR ! named variables for real valued attribute data structure + USE var_lookup, only: iLookPARAM ! named variables for local parameter data structure + USE var_lookup, only: iLookBPAR ! named variables for basin parameter data structure + USE var_lookup, only: iLookBVAR ! named variables for basin model variable data structure + USE var_lookup, only: iLookINDEX ! named variables for index variable data structure + USE var_lookup, only: iLookPROG ! named variables for local state variables + USE var_lookup, only: iLookDIAG ! named variables for local diagnostic variables + USE var_lookup, only: iLookFLUX ! named variables for local flux variables + USE var_lookup, only: iLookDERIV ! named variables for local flux derivatives + USE var_lookup, only: iLookLOOKUP ! named variables for lookup tables + USE var_lookup, only: maxvarFreq ! number of output frequencies + USE var_lookup, only: maxvarStat ! number of statistics USE get_ixName_module,only:get_ixVarType ! to turn vartype strings to integers implicit none ! dummy variables @@ -350,6 +352,10 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%scalarLambda_wetsoil) = var_info('scalarLambda_wetsoil' , 'thermal conductivity of wet soil' , 'W m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%mLayerThermalC) = var_info('mLayerThermalC' , 'thermal conductivity at the mid-point of each layer' , 'W m-1 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%iLayerThermalC) = var_info('iLayerThermalC' , 'thermal conductivity at the interface of each layer' , 'W m-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) +! enthalpy + diag_meta(iLookDIAG%scalarCanairEnthalpy) = var_info('scalarCanairEnthalpy' , 'enthalpy of the canopy air space' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarCanopyEnthalpy) = var_info('scalarCanopyEnthalpy' , 'enthalpy of the vegetation canopy' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%mLayerEnthalpy) = var_info('mLayerEnthalpy' , 'enthalpy of the snow+soil layers' , 'J m-3' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) ! forcing diag_meta(iLookDIAG%scalarVPair) = var_info('scalarVPair' , 'vapor pressure of the air above the vegetation canopy' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarVP_CanopyAir) = var_info('scalarVP_CanopyAir' , 'vapor pressure of the canopy air space' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) @@ -593,6 +599,15 @@ subroutine popMetadat(err,message) bvar_meta(iLookBVAR%averageInstantRunoff) = var_info('averageInstantRunoff' , 'instantaneous runoff' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) bvar_meta(iLookBVAR%averageRoutedRunoff) = var_info('averageRoutedRunoff' , 'routed runoff' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! ----- + ! * lookup tables... + ! ------------------ + + ! temperature and enthalpy + lookup_meta(iLookLOOKUP%temperature) = var_info('temperature' , 'value of temperature in the lookup table' , 'K' , get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + lookup_meta(iLookLOOKUP%enthalpy) = var_info('enthalpy' , 'value of enthalpy in the lookup table' , 'J m-3' , get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + lookup_meta(iLookLOOKUP%deriv2) = var_info('deriv2' , 'second derivatives of the interpolating function' , 'mixed' , get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + ! ----- ! * model indices... ! ------------------ diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index a4be930ef..e8f1de696 100755 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -365,6 +365,10 @@ MODULE var_lookup integer(i4b) :: scalarLambda_wetsoil = integerMissing ! thermal conductivity of wet soil (W m-1 K-1) integer(i4b) :: mLayerThermalC = integerMissing ! thermal conductivity at the mid-point of each layer (W m-1 K-1) integer(i4b) :: iLayerThermalC = integerMissing ! thermal conductivity at the interface of each layer (W m-1 K-1) + ! enthalpy + integer(i4b) :: scalarCanairEnthalpy = integerMissing ! enthalpy of the canopy air space (J m-3) + integer(i4b) :: scalarCanopyEnthalpy = integerMissing ! enthalpy of the vegetation canopy (J m-3) + integer(i4b) :: mLayerEnthalpy = integerMissing ! enthalpy of the snow+soil layers (J m-3) ! forcing integer(i4b) :: scalarVPair = integerMissing ! vapor pressure of the air above the vegetation canopy (Pa) integer(i4b) :: scalarVP_CanopyAir = integerMissing ! vapor pressure of the canopy air space (Pa) @@ -752,6 +756,15 @@ MODULE var_lookup integer(i4b) :: timestep = integerMissing ! timestep-level output (no temporal aggregation) endtype iLook_freq + ! *********************************************************************************************************** + ! (16) structure for looking up lookup tables + ! *********************************************************************************************************** + type, public :: iLook_vLookup + integer(i4b) :: temperature = integerMissing ! temperature (K) + integer(i4b) :: enthalpy = integerMissing ! enthalpy (J m-3) + integer(i4b) :: deriv2 = integerMissing ! second derivatives of the interpolating function + endtype iLook_vLookup + ! *********************************************************************************************************** ! (X) define data structures and maximum number of variables of each type ! *********************************************************************************************************** diff --git a/build/source/engine/allocspace.f90 b/build/source/engine/allocspace.f90 index 27e73300a..ad7a6aa01 100755 --- a/build/source/engine/allocspace.f90 +++ b/build/source/engine/allocspace.f90 @@ -46,7 +46,9 @@ module allocspace_module gru_hru_int8, & ! x%gru(:)%hru(:)%var(:) integer(8) gru_hru_double, & ! x%gru(:)%hru(:)%var(:) (dp) gru_hru_intVec, & ! x%gru(:)%hru(:)%var(:)%dat (i4b) - gru_hru_doubleVec ! x%gru(:)%hru(:)%var(:)%dat (dp) + gru_hru_doubleVec, & ! x%gru(:)%hru(:)%var(:)%dat (dp) + ! gru+hru+z dimension + gru_hru_z_vLookup ! x%gru(:)%hru(:)%z(:)%var(:)%lookup (dp) ! metadata structure USE data_types,only:var_info ! data type for metadata @@ -115,6 +117,8 @@ subroutine allocGlobal(metaStruct,dataStruct,err,message) class is (gru_hru_intVec); if(allocated(dataStruct%gru))then; check=.true.; else; allocate(dataStruct%gru(nGRU),stat=err); end if class is (gru_hru_double); if(allocated(dataStruct%gru))then; check=.true.; else; allocate(dataStruct%gru(nGRU),stat=err); end if class is (gru_hru_doubleVec); if(allocated(dataStruct%gru))then; check=.true.; else; allocate(dataStruct%gru(nGRU),stat=err); end if + ! gru+hru+z dimensions + class is (gru_hru_z_vLookup); if(allocated(dataStruct%gru))then; check=.true.; else; allocate(dataStruct%gru(nGRU),stat=err); end if end select ! check errors @@ -130,6 +134,7 @@ subroutine allocGlobal(metaStruct,dataStruct,err,message) class is (gru_hru_intVec); if(allocated(dataStruct%gru(iGRU)%hru))then; check=.true.; else; allocate(dataStruct%gru(iGRU)%hru(gru_struc(iGRU)%hruCount),stat=err); end if class is (gru_hru_double); if(allocated(dataStruct%gru(iGRU)%hru))then; check=.true.; else; allocate(dataStruct%gru(iGRU)%hru(gru_struc(iGRU)%hruCount),stat=err); end if class is (gru_hru_doubleVec); if(allocated(dataStruct%gru(iGRU)%hru))then; check=.true.; else; allocate(dataStruct%gru(iGRU)%hru(gru_struc(iGRU)%hruCount),stat=err); end if + class is (gru_hru_z_vLookup); if(allocated(dataStruct%gru(iGRU)%hru))then; check=.true.; else; allocate(dataStruct%gru(iGRU)%hru(gru_struc(iGRU)%hruCount),stat=err); end if class default ! do nothing: It is acceptable to not be any of these specified cases end select ! check errors @@ -158,6 +163,7 @@ subroutine allocGlobal(metaStruct,dataStruct,err,message) class is (gru_hru_intVec); call allocLocal(metaStruct,dataStruct%gru(iGRU)%hru(iHRU),nSnow,nSoil,err,cmessage); spatial=.true. class is (gru_hru_double); call allocLocal(metaStruct,dataStruct%gru(iGRU)%hru(iHRU),nSnow,nSoil,err,cmessage); spatial=.true. class is (gru_hru_doubleVec); call allocLocal(metaStruct,dataStruct%gru(iGRU)%hru(iHRU),nSnow,nSoil,err,cmessage); spatial=.true. + class is (gru_hru_z_vLookup); spatial=.true. ! (special case, allocate space separately later) class default; exit hruLoop end select diff --git a/build/source/engine/checkStruc.f90 b/build/source/engine/checkStruc.f90 index e9b0a8cdf..d2a3bfb8c 100755 --- a/build/source/engine/checkStruc.f90 +++ b/build/source/engine/checkStruc.f90 @@ -40,11 +40,13 @@ subroutine checkStruc(err,message) USE globalData,only:prog_meta,diag_meta,flux_meta,deriv_meta ! metadata structures USE globalData,only:mpar_meta,indx_meta ! metadata structures USE globalData,only:bpar_meta,bvar_meta ! metadata structures + USE globalData,only:lookup_meta ! metadata structures ! named variables defining strructure elements USE var_lookup,only:iLookTIME,iLookFORCE,iLookATTR,iLookTYPE,iLookID ! named variables showing the elements of each data structure USE var_lookup,only:iLookPROG,iLookDIAG,iLookFLUX,iLookDERIV ! named variables showing the elements of each data structure USE var_lookup,only:iLookPARAM,iLookINDEX ! named variables showing the elements of each data structure USE var_lookup,only:iLookBPAR,iLookBVAR ! named variables showing the elements of each data structure + USE var_lookup,only:iLookLOOKUP ! named variables showing the elements of each data structure implicit none ! dummy variables integer(i4b),intent(out) :: err ! error code @@ -70,19 +72,20 @@ subroutine checkStruc(err,message) ! convert the lookup structures to a character string ! expect the lookup structures to be a vector (1,2,3,...,n) select case(trim(structInfo(iStruct)%structName)) - case('time'); write(longString,*) iLookTIME - case('forc'); write(longString,*) iLookFORCE - case('attr'); write(longString,*) iLookATTR - case('type'); write(longString,*) iLookTYPE - case('id'); write(longString,*) iLookID - case('mpar'); write(longString,*) iLookPARAM - case('bpar'); write(longString,*) iLookBPAR - case('bvar'); write(longString,*) iLookBVAR - case('indx'); write(longString,*) iLookINDEX - case('prog'); write(longString,*) iLookPROG - case('diag'); write(longString,*) iLookDIAG - case('flux'); write(longString,*) iLookFLUX - case('deriv'); write(longString,*) iLookDERIV + case('time'); write(longString,*) iLookTIME + case('forc'); write(longString,*) iLookFORCE + case('attr'); write(longString,*) iLookATTR + case('type'); write(longString,*) iLookTYPE + case('id'); write(longString,*) iLookID + case('mpar'); write(longString,*) iLookPARAM + case('bpar'); write(longString,*) iLookBPAR + case('bvar'); write(longString,*) iLookBVAR + case('indx'); write(longString,*) iLookINDEX + case('prog'); write(longString,*) iLookPROG + case('diag'); write(longString,*) iLookDIAG + case('flux'); write(longString,*) iLookFLUX + case('deriv'); write(longString,*) iLookDERIV + case('lookup'); write(longString,*) iLookLOOKUP case default; err=20; message=trim(message)//'unable to identify lookup structure'; return end select ! check that the length of the lookup structure matches the number of variables in the data structure @@ -107,19 +110,20 @@ subroutine checkStruc(err,message) do iStruct=1,nStruct ! check that the metadata is fully populated select case(trim(structInfo(iStruct)%structName)) - case('time'); call checkPopulated(iStruct,time_meta,err,cmessage) - case('forc'); call checkPopulated(iStruct,forc_meta,err,cmessage) - case('attr'); call checkPopulated(iStruct,attr_meta,err,cmessage) - case('type'); call checkPopulated(iStruct,type_meta,err,cmessage) - case('id'); call checkPopulated(iStruct,id_meta, err,cmessage) - case('mpar'); call checkPopulated(iStruct,mpar_meta,err,cmessage) - case('bpar'); call checkPopulated(iStruct,bpar_meta,err,cmessage) - case('bvar'); call checkPopulated(iStruct,bvar_meta,err,cmessage) - case('indx'); call checkPopulated(iStruct,indx_meta,err,cmessage) - case('prog'); call checkPopulated(iStruct,prog_meta,err,cmessage) - case('diag'); call checkPopulated(iStruct,diag_meta,err,cmessage) - case('flux'); call checkPopulated(iStruct,flux_meta,err,cmessage) - case('deriv'); call checkPopulated(iStruct,deriv_meta,err,cmessage) + case('time'); call checkPopulated(iStruct,time_meta,err,cmessage) + case('forc'); call checkPopulated(iStruct,forc_meta,err,cmessage) + case('attr'); call checkPopulated(iStruct,attr_meta,err,cmessage) + case('type'); call checkPopulated(iStruct,type_meta,err,cmessage) + case('id'); call checkPopulated(iStruct,id_meta, err,cmessage) + case('mpar'); call checkPopulated(iStruct,mpar_meta,err,cmessage) + case('bpar'); call checkPopulated(iStruct,bpar_meta,err,cmessage) + case('bvar'); call checkPopulated(iStruct,bvar_meta,err,cmessage) + case('indx'); call checkPopulated(iStruct,indx_meta,err,cmessage) + case('prog'); call checkPopulated(iStruct,prog_meta,err,cmessage) + case('diag'); call checkPopulated(iStruct,diag_meta,err,cmessage) + case('flux'); call checkPopulated(iStruct,flux_meta,err,cmessage) + case('deriv'); call checkPopulated(iStruct,deriv_meta,err,cmessage) + case('lookup'); call checkPopulated(iStruct,lookup_meta,err,cmessage) case default; err=20; message=trim(message)//'unable to identify lookup structure'; return end select if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) @@ -155,7 +159,8 @@ subroutine checkPopulated(iStruct,metadata,err,message) ! check that this variable is populated if (trim(metadata(iVar)%varname)=='empty') then - write(message,'(a,i0,a)') trim(message)//trim(structInfo(iStruct)%structName)//'_meta structure is not populated for named variable # ',iVar, ' in structure iLook'//trim(structInfo(iStruct)%lookName) + write(message,'(a,i0,a)') trim(message)//trim(structInfo(iStruct)%structName)//'_meta structure is not populated for named variable # ',iVar, & + ' in structure iLook'//trim(structInfo(iStruct)%lookName) err=20; return end if @@ -165,7 +170,8 @@ subroutine checkPopulated(iStruct,metadata,err,message) ! check that the variable was found at all if (jVar==integerMissing) then - message = trim(message)//'cannot find variable '//trim(metadata(iVar)%varname)//' in structure '//trim(structInfo(iStruct)%structName)//'_meta; you need to add variable to get_ix'//trim(structInfo(iStruct)%structName) + message = trim(message)//'cannot find variable '//trim(metadata(iVar)%varname)//' in structure '//trim(structInfo(iStruct)%structName)//'_meta; '// & + 'you need to add variable to get_ix'//trim(structInfo(iStruct)%structName) err=20; return end if @@ -178,7 +184,8 @@ subroutine checkPopulated(iStruct,metadata,err,message) ! check that the variable index is correct ! This can occur because (1) the code in popMetadat is corrupt (e.g., mis-match in look-up variable); or (2) var_lookup is corrupt. if (jVar/=iVar) then - write(message,'(a,i0,a,i0,a)') trim(message)//'variable '//trim(metadata(iVar)%varname)//' has index ', iVar, ' (expect index ', jVar, '); problem possible in popMetadat, get_ix'//trim(structInfo(iStruct)%structName)//', or var_lookup' + write(message,'(a,i0,a,i0,a)') trim(message)//'variable '//trim(metadata(iVar)%varname)//' has index ', iVar, & + ' (expect index ', jVar, '); problem possible in popMetadat, get_ix'//trim(structInfo(iStruct)%structName)//', or var_lookup' err=20; return end if diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 88fd044da..940a15151 100755 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -33,10 +33,11 @@ module coupled_em_module ! data types USE data_types,only:& - var_i, & ! x%var(:) (i4b) - var_d, & ! x%var(:) (dp) - var_ilength, & ! x%var(:)%dat (i4b) - var_dlength ! x%var(:)%dat (dp) + var_i, & ! x%var(:) (i4b) + var_d, & ! x%var(:) (dp) + var_ilength, & ! x%var(:)%dat (i4b) + var_dlength, & ! x%var(:)%dat (dp) + zLookup ! x%z(:)%var(:)%lookup(:) (dp) ! named variables for parent structures USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure @@ -110,6 +111,7 @@ subroutine coupled_em(& forc_data, & ! intent(in): model forcing data mpar_data, & ! intent(in): model parameters bvar_data, & ! intent(in): basin-average variables + lookup_data, & ! intent(in): lookup tables ! data structures (input-output) indx_data, & ! intent(inout): model indices prog_data, & ! intent(inout): prognostic variables for a local HRU @@ -156,6 +158,7 @@ subroutine coupled_em(& type(var_d),intent(in) :: forc_data ! model forcing data type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_dlength),intent(in) :: bvar_data ! basin-average model variables + type(zLookup),intent(in) :: lookup_data ! lookup tables ! data structures (input-output) type(var_ilength),intent(inout) :: indx_data ! state vector geometry type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU @@ -736,6 +739,7 @@ subroutine coupled_em(& diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU bvar_data, & ! intent(in): model variables for the local basin + lookup_data, & ! intent(in): lookup tables model_decisions, & ! intent(in): model decisions ! output: model control dtMultiplier, & ! intent(out): substep multiplier (-) diff --git a/build/source/netcdf/def_output.f90 b/build/source/netcdf/def_output.f90 index 996eef1c1..c6c6e5190 100755 --- a/build/source/netcdf/def_output.f90 +++ b/build/source/netcdf/def_output.f90 @@ -151,23 +151,24 @@ subroutine def_output(summaVersion,buildTime,gitBranch,gitHash,nGRU,nHRU,nSoil,i ! define variables do iStruct = 1,size(structInfo) select case (trim(structInfo(iStruct)%structName)) - case('attr' ); call def_variab(ncid(iFreq),iFreq,needHRU, noTime,attr_meta, outputPrecision, err,cmessage) ! local attributes HRU - case('type' ); call def_variab(ncid(iFreq),iFreq,needHRU, noTime,type_meta, nf90_int, err,cmessage) ! local classification - case('mpar' ); call def_variab(ncid(iFreq),iFreq,needHRU, noTime,mpar_meta, outputPrecision, err,cmessage) ! model parameters - case('bpar' ); call def_variab(ncid(iFreq),iFreq,needGRU, noTime,bpar_meta, outputPrecision, err,cmessage) ! basin-average param - case('indx' ); call def_variab(ncid(iFreq),iFreq,needHRU,needTime,indx_meta, nf90_int, err,cmessage) ! model variables - case('deriv'); call def_variab(ncid(iFreq),iFreq,needHRU,needTime,deriv_meta,outputPrecision, err,cmessage) ! model derivatives - case('time' ); call def_variab(ncid(iFreq),iFreq, noHRU,needTime,time_meta, nf90_int, err,cmessage) ! model derivatives - case('forc' ); call def_variab(ncid(iFreq),iFreq,needHRU,needTime,forc_meta, outputPrecision, err,cmessage) ! model forcing data - case('prog' ); call def_variab(ncid(iFreq),iFreq,needHRU,needTime,prog_meta, outputPrecision, err,cmessage) ! model prognostics - case('diag' ); call def_variab(ncid(iFreq),iFreq,needHRU,needTime,diag_meta, outputPrecision, err,cmessage) ! model diagnostic variables - case('flux' ); call def_variab(ncid(iFreq),iFreq,needHRU,needTime,flux_meta, outputPrecision, err,cmessage) ! model fluxes - case('bvar' ); call def_variab(ncid(iFreq),iFreq,needGRU,needTime,bvar_meta, outputPrecision, err,cmessage) ! basin-average variables - case('id' ); cycle ! ids -- see write_hru_info() + case('attr' ); call def_variab(ncid(iFreq),iFreq,needHRU, noTime,attr_meta, outputPrecision, err,cmessage) ! local attributes HRU + case('type' ); call def_variab(ncid(iFreq),iFreq,needHRU, noTime,type_meta, nf90_int, err,cmessage) ! local classification + case('mpar' ); call def_variab(ncid(iFreq),iFreq,needHRU, noTime,mpar_meta, outputPrecision, err,cmessage) ! model parameters + case('bpar' ); call def_variab(ncid(iFreq),iFreq,needGRU, noTime,bpar_meta, outputPrecision, err,cmessage) ! basin-average param + case('indx' ); call def_variab(ncid(iFreq),iFreq,needHRU,needTime,indx_meta, nf90_int, err,cmessage) ! model variables + case('deriv' ); call def_variab(ncid(iFreq),iFreq,needHRU,needTime,deriv_meta,outputPrecision, err,cmessage) ! model derivatives + case('time' ); call def_variab(ncid(iFreq),iFreq, noHRU,needTime,time_meta, nf90_int, err,cmessage) ! model derivatives + case('forc' ); call def_variab(ncid(iFreq),iFreq,needHRU,needTime,forc_meta, outputPrecision, err,cmessage) ! model forcing data + case('prog' ); call def_variab(ncid(iFreq),iFreq,needHRU,needTime,prog_meta, outputPrecision, err,cmessage) ! model prognostics + case('diag' ); call def_variab(ncid(iFreq),iFreq,needHRU,needTime,diag_meta, outputPrecision, err,cmessage) ! model diagnostic variables + case('flux' ); call def_variab(ncid(iFreq),iFreq,needHRU,needTime,flux_meta, outputPrecision, err,cmessage) ! model fluxes + case('bvar' ); call def_variab(ncid(iFreq),iFreq,needGRU,needTime,bvar_meta, outputPrecision, err,cmessage) ! basin-average variables + case('id' ); cycle ! ids -- see write_hru_info() + case('lookup'); cycle ! ids -- see write_hru_info() case default; err=20; message=trim(message)//'unable to identify lookup structure'; end select ! error handling - if(err/=0)then;err=20;message=trim(message)//trim(cmessage)//'[structure = '//trim(structInfo(iStruct)%structName);return;end if + if(err/=0)then;err=20;message=trim(message)//'[structure = '//trim(structInfo(iStruct)%structName);return;end if end do ! iStruct ! write HRU dimension and ID for each output file From ac391bb8bc5e2b534a27b0fe828e2f5622a8c6d3 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sat, 6 Feb 2021 01:37:55 -0600 Subject: [PATCH 0010/1472] all enthalpy in new version set up --- build/Makefile | 13 +-- build/source/dshare/var_lookup.f90 | 6 +- build/source/engine/coupled_em.f90 | 2 +- build/source/engine/eval8summa.f90 | 40 ++++++- build/source/engine/nonlinSolvFida.f90 | 39 +++++++ build/source/engine/printResidFida.f90 | 149 +++++++++++++++++++++++++ build/source/engine/run_oneGRU.f90 | 7 +- build/source/engine/run_oneHRU.f90 | 18 +-- build/source/engine/summaSolve.f90 | 10 +- build/source/engine/systemSolv.f90 | 9 +- build/source/engine/updateVars.f90 | 24 ++-- build/source/engine/varSubstep.f90 | 33 +++--- 12 files changed, 290 insertions(+), 60 deletions(-) create mode 100644 build/source/engine/nonlinSolvFida.f90 create mode 100644 build/source/engine/printResidFida.f90 diff --git a/build/Makefile b/build/Makefile index 146164959..b7fb6e2a2 100644 --- a/build/Makefile +++ b/build/Makefile @@ -148,8 +148,6 @@ DSHARE_DIR = $(F_KORE_DIR)/dshare NUMREC_DIR = $(F_KORE_DIR)/numrec NOAHMP_DIR = $(F_KORE_DIR)/noah-mp ENGINE_DIR = $(F_KORE_DIR)/engine -# reza -USEFIDA_DIR = $(F_KORE_DIR)/engine/use_fida # utilities SUMMA_NRUTIL= \ @@ -162,13 +160,15 @@ NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) # Numerical recipes procedures # NOTE: all numerical recipes procedures are now replaced with free versions SUMMA_NRPROC= \ - expIntegral.f90 \ - spline_int.f90 + expIntegral.f90 \ + spline_int.f90 NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) # Hook-up modules (set files and directory paths) SUMMA_HOOKUP= \ - summaFileManager.f90 + ascii_util.f90 \ + summaFileManager.f90 + HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) # Data modules @@ -179,7 +179,6 @@ SUMMA_DATAMS= \ globalData.f90 \ flxMapping.f90 \ get_ixname.f90 \ - ascii_util.f90 \ popMetadat.f90 \ outpt_stat.f90 DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) @@ -266,8 +265,6 @@ PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) SUMMA_NOAHMP= \ module_model_constants.F \ module_sf_noahutl.F \ - module_sf_myjsfc.F \ - module_sf_sfclay.F \ module_sf_noahlsm.F \ module_sf_noahmplsm.F NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index e8f1de696..a74f5dfa7 100755 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -821,7 +821,7 @@ MODULE var_lookup 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,& 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,& 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,& - 81, 82, 83) + 81, 82, 83, 84, 85, 86) ! named variables: model fluxes type(iLook_flux), public,parameter :: iLookFLUX =iLook_flux ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& @@ -863,6 +863,9 @@ MODULE var_lookup ! number of possible output frequencies type(iLook_freq), public,parameter :: iLookFreq =ilook_freq ( 1, 2, 3, 4) + + ! named variables in the lookup table structure + type(iLook_vLookup), public,parameter :: iLookLOOKUP =ilook_vLookup ( 1, 2, 3) ! define maximum number of variables of each type integer(i4b),parameter,public :: maxvarDecisions = storage_size(iLookDECISIONS)/iLength @@ -882,6 +885,7 @@ MODULE var_lookup integer(i4b),parameter,public :: maxvarVarType = storage_size(iLookVarType)/iLength integer(i4b),parameter,public :: maxvarStat = storage_size(iLookStat)/iLength integer(i4b),parameter,public :: maxvarFreq = storage_size(iLookFreq)/iLength + integer(i4b),parameter,public :: maxvarLookup = storage_size(iLookLOOKUP)/iLength ! *********************************************************************************************************** ! (Y) define ancillary look-up structures diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 940a15151..94a610ee0 100755 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1117,7 +1117,7 @@ subroutine coupled_em(& newSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) delSWE = newSWE - (oldSWE - sfcMeltPond) massBalance = delSWE - (effSnowfall + effRainfall + averageSnowSublimation - averageSnowDrainage*iden_water)*data_step - if(abs(massBalance) > 1.d-6)then + if(abs(massBalance) > absConvTol_liquid*iden_water*10._dp)then print*, 'nSnow = ', nSnow print*, 'nSub = ', nSub write(*,'(a,1x,f20.10)') 'data_step = ', data_step diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index bd13c2435..872519883 100755 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -1,5 +1,5 @@ ! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington +! Copyright (C) 2014-2015 NCAR/RAL ! ! This file is part of SUMMA ! @@ -67,6 +67,7 @@ module eval8summa_module var_d, & ! data vector (dp) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (dp) + zLookup, & ! data vector with variable length dimension (dp) model_options ! defines the model decisions ! indices that define elements of the data structures @@ -121,6 +122,7 @@ subroutine eval8summa(& sMul, & ! intent(in): state vector multiplier (used in the residual calculations) ! input: data structures model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes mpar_data, & ! intent(in): model parameters @@ -146,6 +148,7 @@ subroutine eval8summa(& ! provide access to subroutines USE getVectorz_module, only:varExtract ! extract variables from the state vector USE updateVars_module, only:updateVars ! update prognostic variables + USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy USE computFlux_module, only:soilCmpres ! compute soil compression USE computFlux_module, only:computFlux ! compute fluxes given a state vector USE computResid_module,only:computResid ! compute residuals given a state vector @@ -169,6 +172,7 @@ subroutine eval8summa(& real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) ! input: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup), intent(in) :: lookup_data ! lookup tables type(var_i), intent(in) :: type_data ! type of vegetation and soil type(var_d), intent(in) :: attr_data ! spatial attributes type(var_dlength), intent(in) :: mpar_data ! model parameters @@ -209,6 +213,8 @@ subroutine eval8summa(& real(dp) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) real(dp),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) real(dp),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) + ! enthalpy + logical(lgt),parameter :: needEnthalpy=.true. ! flag to compute enthalpy ! other local variables integer(i4b) :: iLayer ! index of model layer in the snow+soil domain integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain @@ -242,6 +248,10 @@ subroutine eval8summa(& ! model diagnostic variables scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) +! enthalpy + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) ! soil compression scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2) mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in storage associated with compression of the soil matrix (-) @@ -364,6 +374,7 @@ subroutine eval8summa(& call updateVars(& ! input .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + lookup_data, & ! intent(in): lookup tables for a local HRU mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU @@ -384,6 +395,33 @@ subroutine eval8summa(& ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! compute enthalpy (J m-3) + if(needEnthalpy)then + call t2enthalpy(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) + ! output: enthalpy + scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + endif ! if computing enthalpy ! print the states in the canopy domain !print*, 'dt = ', dt diff --git a/build/source/engine/nonlinSolvFida.f90 b/build/source/engine/nonlinSolvFida.f90 new file mode 100644 index 000000000..974c29d4a --- /dev/null +++ b/build/source/engine/nonlinSolvFida.f90 @@ -0,0 +1,39 @@ +module nonlinSolvFida_module + + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use nrtype + + + + type nonlinSolvFn + procedure(sysFnFida), pointer, nopass :: fcn + end type nonlinSolvFn + + abstract interface + + integer(c_int) function sysFnFida(ycor, F, mem) result(ierr) bind(C,name='sysFnFida') + use, intrinsic :: iso_c_binding + use fida_mod ! Fortran interface to IDA + use fnvector_serial_mod ! Fortran interface to serial N_Vector + use fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix + use fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver + use fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix + use fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver + use fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver + use fsundials_matrix_mod ! Fortran interface to generic SUNMatrix + use fsundials_nvector_mod ! Fortran interface to generic N_Vector + use fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver + use fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver + use nrtype + use fida_datatypes + implicit none + type(N_Vector) :: ycor + type(N_Vector) :: F + type(c_ptr) :: mem + end function + + end interface + +end module nonlinSolvFida_module diff --git a/build/source/engine/printResidFida.f90 b/build/source/engine/printResidFida.f90 new file mode 100644 index 000000000..91286b811 --- /dev/null +++ b/build/source/engine/printResidFida.f90 @@ -0,0 +1,149 @@ + + +module printResidFida_module + +! data types +USE nrtype + +! derived types to define the data structures +USE data_types,only:& + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength ! data vector with variable length dimension (dp) + +! named variables +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookFLUX ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements + +! access the global print flag +USE globalData,only:globalPrintFlag + +! access missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing real number + +! define access to state variables to print +USE globalData,only: iJac1 ! first layer of the Jacobian to print +USE globalData,only: iJac2 ! last layer of the Jacobian to print + +! domain types +USE globalData,only:iname_veg ! named variables for vegetation +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil + +! named variables to describe the state variable type +USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space +USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy +USE globalData,only:iname_watCanopy ! named variable defining the mass of water on the vegetation canopy +USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers +USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers +USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers +USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers +USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers + +! constants +USE multiconst,only:& + LH_fus, & ! latent heat of fusion (J kg-1) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water ! intrinsic density of liquid water (kg m-3) +! privacy +implicit none +private +public::printResidFida +contains + + ! ********************************************************************************************************** + ! public subroutine printResidFida: compute the residual vector + ! ********************************************************************************************************** + subroutine printResidFida(& + ! input: model control + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + ! input: data structures + indx_data, & ! intent(in): index data + ! output + rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec) ! intent(out): residual vector + + ! -------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input: model control + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + ! output + real(dp),intent(in) :: rAdd(:) ! additional (sink) terms on the RHS of the state equation + real(qp),intent(in) :: rVec(:) ! NOTE: qp ! residual vector + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + ! -------------------------------------------------------------------------------------------------------------------------------- + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + ! -------------------------------------------------------------------------------------------------------------------------------- + ! link to the necessary variables for the residual computations + associate(& + ! number of state variables of a specific type + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + ! model indices + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the soil subdomain + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of hydrology states in snow+soil domain + layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] named variables defining the type of layer in snow+soil domain + ) ! association to necessary variables for the residual computations + ! -------------------------------------------------------------------------------------------------------------------------------- + + + if(ixVegNrg/=integerMissing) print *, 'rAdd(ixVegNrg) = ', rAdd(ixVegNrg) + + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) + select case( layerType(iLayer) ) + case(iname_snow) + print *, 'rAdd( ixSnowSoilNrg(iLayer) ) = ', rAdd( ixSnowSoilNrg(iLayer) ) + case(iname_soil); print *, 'rAdd( ixSnowSoilNrg(iLayer) ) = ', rAdd( ixSnowSoilNrg(iLayer) ) + end select + end do + endif + + if(nSoilOnlyHyd>0)then + do concurrent (iLayer=1:nSoil,ixSoilOnlyHyd(iLayer)/=integerMissing) + print *, 'rAdd( ixSoilOnlyHyd(iLayer) ) = ', rAdd( ixSoilOnlyHyd(iLayer) ) + end do + endif + + if(ixCasNrg/=integerMissing) print *, 'rVec(ixCasNrg) = ', rVec(ixCasNrg) + if(ixVegNrg/=integerMissing) print *, 'rVec(ixVegNrg) = ', rVec(ixVegNrg) + if(ixVegHyd/=integerMissing)then + print *, 'rVec(ixVegHyd) = ', rVec(ixVegHyd) + endif + + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) + print *, 'rVec( ixSnowSoilNrg(iLayer) ) = ', rVec( ixSnowSoilNrg(iLayer) ) + end do + endif + + if(nSnowSoilHyd>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) + print *, 'rVec( ixSnowSoilHyd(iLayer) ) = ', rVec( ixSnowSoilHyd(iLayer) ) + end do + endif + + if(ixAqWat/=integerMissing) print *, ' rVec(ixAqWat) = ', rVec(ixAqWat) + + end associate + + end subroutine printResidFida + +end module printResidFida_module diff --git a/build/source/engine/run_oneGRU.f90 b/build/source/engine/run_oneGRU.f90 index 653585ac7..ed5694678 100755 --- a/build/source/engine/run_oneGRU.f90 +++ b/build/source/engine/run_oneGRU.f90 @@ -38,7 +38,9 @@ module run_oneGRU_module hru_int8, & ! x%hru(:)%var(:) integer(8) hru_double, & ! x%hru(:)%var(:) (dp) hru_intVec, & ! x%hru(:)%var(:)%dat (i4b) - hru_doubleVec ! x%hru(:)%var(:)%dat (dp) + hru_doubleVec, & ! x%hru(:)%var(:)%dat (dp) + ! hru+z dimension + hru_z_vLookup ! x%hru(:)%z(:)%var(:)%lookup(:) ! provide access to the named variables that describe elements of parameter structures USE var_lookup,only:iLookTYPE ! look-up values for classification of veg, soils etc. @@ -81,6 +83,7 @@ subroutine run_oneGRU(& typeHRU, & ! intent(in): local classification of soil veg etc. for each HRU idHRU, & ! intent(in): local classification of hru and gru IDs attrHRU, & ! intent(in): local attributes for each HRU + lookupHRU, & ! intent(in): local lookup tables for each HRU ! data structures (input-output) mparHRU, & ! intent(inout): local model parameters indxHRU, & ! intent(inout): model indices @@ -110,6 +113,7 @@ subroutine run_oneGRU(& type(hru_int) , intent(in) :: typeHRU ! x%hru(:)%var(:) -- local classification of soil veg etc. for each HRU type(hru_int8) , intent(in) :: idHRU ! x%hru(:)%var(:) -- local classification of hru and gru IDs type(hru_double) , intent(in) :: attrHRU ! x%hru(:)%var(:) -- local attributes for each HRU + type(hru_z_vLookup) , intent(in) :: lookupHRU ! x%hru(:)%z(:)%var(:)%lookup(:) -- lookup values for each HRU ! data structures (input-output) type(hru_doubleVec) , intent(inout) :: mparHRU ! x%hru(:)%var(:)%dat -- local (HRU) model parameters type(hru_intVec) , intent(inout) :: indxHRU ! x%hru(:)%var(:)%dat -- model indices @@ -183,6 +187,7 @@ subroutine run_oneGRU(& timeVec, & ! intent(in): model time data typeHRU%hru(iHRU), & ! intent(in): local classification of soil veg etc. for each HRU attrHRU%hru(iHRU), & ! intent(in): local attributes for each HRU + lookupHRU%hru(iHRU), & ! intent(in): local lookup tables for each HRU bvarData, & ! intent(in): basin-average model variables ! data structures (input-output) mparHRU%hru(iHRU), & ! intent(inout): model parameters diff --git a/build/source/engine/run_oneHRU.f90 b/build/source/engine/run_oneHRU.f90 index 1632e1f77..dfab1f8a0 100755 --- a/build/source/engine/run_oneHRU.f90 +++ b/build/source/engine/run_oneHRU.f90 @@ -25,10 +25,11 @@ module run_oneHRU_module ! data types USE data_types,only:& - var_i, & ! x%var(:) (i4b) - var_d, & ! x%var(:) (dp) - var_ilength, & ! x%var(:)%dat (i4b) - var_dlength ! x%var(:)%dat (dp) + var_i, & ! x%var(:) (i4b) + var_d, & ! x%var(:) (dp) + var_ilength, & ! x%var(:)%dat (i4b) + var_dlength, & ! x%var(:)%dat (dp) + zLookup ! x%z(:)%var(:)%lookup(:) (dp) ! access vegetation data USE globalData,only:greenVegFrac_monthly ! fraction of green vegetation in each month (0-1) @@ -92,6 +93,7 @@ subroutine run_oneHRU(& timeVec, & ! intent(in): model time data typeData, & ! intent(in): local classification of soil veg etc. for each HRU attrData, & ! intent(in): local attributes for each HRU + lookupData, & ! intent(in): local lookup tables for each HRU bvarData, & ! intent(in): basin-average variables ! data structures (input-output) mparData, & ! intent(inout): local model parameters @@ -118,9 +120,10 @@ subroutine run_oneHRU(& logical(lgt) , intent(inout) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (false=no, true=yes) integer(i4b) , intent(inout) :: nSnow,nSoil,nLayers ! number of snow and soil layers ! data structures (input) - integer(i4b) , intent(in) :: timeVec(:) ! int vector -- model time data - type(var_i) , intent(in) :: typeData ! x%var(:) -- local classification of soil veg etc. for each HRU - type(var_d) , intent(in) :: attrData ! x%var(:) -- local attributes for each HRU + integer(i4b) , intent(in) :: timeVec(:) ! int vector -- model time data + type(var_i) , intent(in) :: typeData ! x%var(:) -- local classification of soil veg etc. for each HRU + type(var_d) , intent(in) :: attrData ! x%var(:) -- local attributes for each HRU + type(zLookup) , intent(in) :: lookupData ! x%z(:)%var(:)%lookup(:) -- local lookup tables for each HRU type(var_dlength) , intent(in) :: bvarData ! x%var(:)%dat -- basin-average variables ! data structures (input-output) type(var_dlength) , intent(inout) :: mparData ! x%var(:)%dat -- local (HRU) model parameters @@ -215,6 +218,7 @@ subroutine run_oneHRU(& forcData, & ! intent(in): model forcing data mparData, & ! intent(in): model parameters bvarData, & ! intent(in): basin-average model variables + lookupData, & ! intent(in): lookup tables ! data structures (input-output) indxData, & ! intent(inout): model indices progData, & ! intent(inout): model prognostic variables for a local HRU diff --git a/build/source/engine/summaSolve.f90 b/build/source/engine/summaSolve.f90 index ee4dfc886..3f98f560a 100755 --- a/build/source/engine/summaSolve.f90 +++ b/build/source/engine/summaSolve.f90 @@ -1,5 +1,5 @@ ! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington +! Copyright (C) 2014-2015 NCAR/RAL ! ! This file is part of SUMMA ! @@ -67,6 +67,7 @@ module summaSolve_module var_d, & ! data vector (dp) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (dp) + zLookup, & ! data vector with variable length dimension (dp) model_options ! defines the model decisions ! look-up values for the choice of groundwater parameterization @@ -108,6 +109,7 @@ subroutine summaSolve(& fOld, & ! intent(in): old function evaluation ! input: data structures model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes mpar_data, & ! intent(in): model parameters @@ -159,6 +161,7 @@ subroutine summaSolve(& real(dp),intent(in) :: fOld ! old function evaluation ! input: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup), intent(in) :: lookup_data ! lookup tables type(var_i), intent(in) :: type_data ! type of vegetation and soil type(var_d), intent(in) :: attr_data ! spatial attributes type(var_dlength), intent(in) :: mpar_data ! model parameters @@ -635,7 +638,7 @@ subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fl ! get brackets if they do not exist if( ieee_is_nan(xMin) .or. ieee_is_nan(xMax) )then - call getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,cmessage) + call getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,message) if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) endif @@ -653,7 +656,7 @@ subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fl ! compute the iteration increment stateVecNew = stateVecTrial + xInc - endif ! if the iteration increment is the same sign as the residual vector + endif ! if the iteration increment is the same sign as the residual vecto ! bi-section bracketsDefined = ( .not.ieee_is_nan(xMin) .and. .not.ieee_is_nan(xMax) ) ! check that the brackets are defined @@ -940,6 +943,7 @@ subroutine eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err sMul, & ! intent(in): state vector multiplier (used in the residual calculations) ! input: data structures model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes mpar_data, & ! intent(in): model parameters diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index aed91981e..51f7b9699 100755 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -1,5 +1,5 @@ ! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington +! Copyright (C) 2014-2015 NCAR/RAL ! ! This file is part of SUMMA ! @@ -79,6 +79,7 @@ module systemSolv_module var_d, & ! data vector (dp) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (dp) + zLookup, & ! data vector with variable length dimension (dp) model_options ! defines the model decisions ! look-up values for the choice of groundwater representation (local-column, or single-basin) @@ -119,6 +120,7 @@ subroutine systemSolv(& computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution ! input/output: data structures + lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes forc_data, & ! intent(in): model forcing data @@ -160,6 +162,7 @@ subroutine systemSolv(& logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input/output: data structures + type(zLookup),intent(in) :: lookup_data ! lookup tables type(var_i),intent(in) :: type_data ! type of vegetation and soil type(var_d),intent(in) :: attr_data ! spatial attributes type(var_d),intent(in) :: forc_data ! model forcing data @@ -381,6 +384,7 @@ subroutine systemSolv(& sMul, & ! intent(in): state vector multiplier (used in the residual calculations) ! input: data structures model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes mpar_data, & ! intent(in): model parameters @@ -474,6 +478,7 @@ subroutine systemSolv(& fOld, & ! intent(in): old function evaluation ! input: data structures model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes mpar_data, & ! intent(in): model parameters @@ -551,6 +556,8 @@ subroutine systemSolv(& stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt + resSinkNew(iState)) end do ! looping through non-missing water state variables in the soil domain endif + +! if ( allocated(dBaseflow_dMatric) ) deallocate(dBaseflow_dMatric) reza: does not change memcheck output ! end associate statements end associate globalVars diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 index c024f1cd2..7aaf5eee5 100755 --- a/build/source/engine/updateVars.f90 +++ b/build/source/engine/updateVars.f90 @@ -1,5 +1,5 @@ ! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington +! Copyright (C) 2014-2015 NCAR/RAL ! ! This file is part of SUMMA ! @@ -66,6 +66,7 @@ module updateVars_module var_i, & ! data vector (i4b) var_d, & ! data vector (dp) var_ilength, & ! data vector with variable length dimension (i4b) + zLookup, & ! data vector with variable length dimension (dp) var_dlength ! data vector with variable length dimension (dp) ! provide access to indices that define elements of the data structures @@ -75,6 +76,9 @@ module updateVars_module USE var_lookup,only:iLookPARAM ! named variables for structure elements USE var_lookup,only:iLookINDEX ! named variables for structure elements +! provide access to the routines to calculate enthalpy +USE t2enthalpy_module,only:t2enthalpy + ! provide access to routines to update states USE updatState_module,only:updateSnow ! update snow states USE updatState_module,only:updateSoil ! update soil states @@ -105,6 +109,7 @@ module updateVars_module subroutine updateVars(& ! input do_adjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + lookup_data, & ! intent(in): lookup tables for a local HRU mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU @@ -129,6 +134,7 @@ subroutine updateVars(& implicit none ! input logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze + type(zLookup), intent(in) :: lookup_data ! lookup tables for a local HRU type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU @@ -713,22 +719,6 @@ subroutine xTempSolve(& ! subroutine starts here residual = -heatCap*(xTemp - tempInit) + meltNrg*(volFracIceTrial - volFracIceInit) ! J m-3 derivative = heatCap + LH_fus*iden_water*dLiq_dT ! J m-3 K-1 - - ! check validity of residual ... - ! informational only: if nan, the sim will start to error out from calling routine - if( ieee_is_nan(residual) )then - print*, '--------' - print*, 'ERROR: residual is not valid in xTempSolve' - print*, 'heatCap', heatCap - print*, 'xTemp', xTemp - print*, 'tempInit', tempInit - print*, 'meltNrg', meltNrg - print*, 'volFracIceTrial', volFracIceTrial - print*, 'volFracIceInit', volFracIceInit - print*, 'dLiq_dT', dLiq_dT - print*, '--------' - endif - end subroutine xTempSolve end module updateVars_module diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index f82882d94..322a8bb56 100755 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -1,5 +1,5 @@ ! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington +! Copyright (C) 2014-2015 NCAR/RAL ! ! This file is part of SUMMA ! @@ -47,6 +47,7 @@ module varSubstep_module var_flagVec, & ! data vector with variable length dimension (i4b) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (dp) + zLookup, & ! data vector with variable length dimension (dp) model_options ! defines the model decisions ! provide access to indices that define elements of the data structures @@ -97,6 +98,7 @@ subroutine varSubstep(& fluxCount, & ! intent(inout) : number of times that fluxes are updated (should equal nSubsteps) ! input/output: data structures model_decisions, & ! intent(in) : model decisions + lookup_data, & ! intent(in) : lookup tables type_data, & ! intent(in) : type of vegetation and soil attr_data, & ! intent(in) : spatial attributes forc_data, & ! intent(in) : model forcing data @@ -144,6 +146,7 @@ subroutine varSubstep(& type(var_ilength),intent(inout) :: fluxCount ! number of times that the flux is updated (should equal nSubsteps) ! input/output: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup),intent(in) :: lookup_data ! lookup tables type(var_i),intent(in) :: type_data ! type of vegetation and soil type(var_d),intent(in) :: attr_data ! spatial attributes type(var_d),intent(in) :: forc_data ! model forcing data @@ -310,6 +313,7 @@ subroutine varSubstep(& computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution ! input/output: data structures + lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes forc_data, & ! intent(in): model forcing data @@ -398,15 +402,15 @@ subroutine varSubstep(& ! update prognostic variables call updateProg(dtSubstep,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,checkMassBalance, & ! input: model control - mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures - waterBalanceError,nrgFluxModified,tooMuchMelt,err,cmessage) ! output: flags and error control + lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures + waterBalanceError,nrgFluxModified,err,cmessage) ! output: flags and error control if(err/=0)then message=trim(message)//trim(cmessage) if(err>0) return endif ! if water balance error then reduce the length of the coupled step - if(waterBalanceError .or. tooMuchMelt)then + if(waterBalanceError)then message=trim(message)//'water balance error' reduceCoupledStep=.true. err=-20; return @@ -478,17 +482,8 @@ subroutine varSubstep(& ixMax=ubound(flux_data%var(iVar)%dat) do ixLayer=ixMin(1),ixMax(1) if(fluxMask%var(iVar)%dat(ixLayer)) then - - ! special case of the transpiration sink from soil layers: only computed for the top soil layer - if(iVar==iLookFlux%mLayerTranspire)then - if(ixLayer==1) flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght - - ! standard case - else - flux_data%var(iVar)%dat(ixLayer) = flux_data%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght - endif + flux_data%var(iVar)%dat(ixLayer) = flux_data%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght fluxCount%var(iVar)%dat(ixLayer) = fluxCount%var(iVar)%dat(ixLayer) + 1 - endif end do endif ! (domain splitting) @@ -546,8 +541,8 @@ end subroutine varSubstep ! private subroutine updateProg: update prognostic variables ! ********************************************************************************************************** subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,checkMassBalance, & ! input: model control - mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures - waterBalanceError,nrgFluxModified,tooMuchMelt,err,message) ! output: flags and error control + lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures + waterBalanceError,nrgFluxModified,err,message) ! output: flags and error control USE getVectorz_module,only:varExtract ! extract variables from the state vector USE updateVars_module,only:updateVars ! update prognostic variables implicit none @@ -562,6 +557,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe real(dp) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance ! data structures + type(zLookup), intent(in) :: lookup_data ! lookup tables type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! indices for a local HRU type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU @@ -571,7 +567,6 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! flags and error control logical(lgt) ,intent(out) :: waterBalanceError ! flag to denote that there is a water balance error logical(lgt) ,intent(out) :: nrgFluxModified ! flag to denote that the energy fluxes were modified - logical(lgt) ,intent(out) :: tooMuchMelt ! flag to denote that the energy fluxes were modified integer(i4b) ,intent(out) :: err ! error code character(*) ,intent(out) :: message ! error message ! ================================================================================================================== @@ -701,13 +696,11 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe !print*, 'after varExtract: scalarCanopyLiqTrial =', scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) !print*, 'after varExtract: scalarCanopyIceTrial =', scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - ! check if there was too much melt - if(nSnow>0) tooMuchMelt = (mLayerTempTrial(1)>Tfreeze) - ! update diagnostic variables call updateVars(& ! input doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + lookup_data, & ! intent(in): lookup tables for a local HRU mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU From d92f120cd356f7192b1362fbf7255be15aa84afb Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Mon, 8 Feb 2021 00:19:06 -0600 Subject: [PATCH 0011/1472] first set up --- build/source/driver/summa_driver.f90 | 7 +- build/source/engine/computHeatCap.f90 | 232 ++++++++++++-------- build/source/engine/coupled_em.f90 | 2 +- build/source/engine/eval8summaFida.f90 | 154 ++++++-------- build/source/engine/evalEqnsFida.f90 | 7 +- build/source/engine/fidaSolver.f90 | 56 ++++- build/source/engine/fida_datatypes.f90 | 3 + build/source/engine/sysSolvFida.f90 | 3 +- build/source/engine/t2enthalpy.f90 | 280 +++++++++++++++++++++++++ 9 files changed, 549 insertions(+), 195 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index cffec57ee..22e864b13 100755 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -53,6 +53,7 @@ program summa_driver ! error control integer(i4b) :: err=0 ! error code character(len=1024) :: message='' ! error message +integer(i4b) :: iStep ! ***************************************************************************** ! * preliminaries @@ -81,14 +82,14 @@ program summa_driver ! ***************************************************************************** ! * model simulation ! ***************************************************************************** - +iStep = 1 ! loop through time do modelTimeStep=1,numtim ! read model forcing data call summa_readForcing(modelTimeStep, summa1_struc(n), err, message) call handle_err(err, message) - + print *, 'step ---> ', iStep ! run the summa physics for one time step call summa_runPhysics(modelTimeStep, summa1_struc(n), err, message) call handle_err(err, message) @@ -96,7 +97,7 @@ program summa_driver ! write the model output call summa_writeOutputFiles(modelTimeStep, summa1_struc(n), err, message) call handle_err(err, message) - + iStep = iStep + 1 end do ! looping through time ! successful end diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index 55439da21..156f6a085 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -67,65 +67,54 @@ module computHeatCap_module private public::computHeatCap public::computStatMult +public::computHeatCapAnalytic contains ! ********************************************************************************************************** - ! public subroutine computHeatCap: compute diagnostic energy variables (thermal conductivity and heat capacity) + ! public subroutine computHeatCap: compute diagnostic energy variables (heat capacity) ! ********************************************************************************************************** subroutine computHeatCap(& + ! input data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) + nLayers, & ! intent(in): number of layers (soil+snow) + diag_data, & ! intent(in): model diagnostic variables for a local HRU ! input: state variables - scalarCanopyIce, & ! intent(in) - scalarCanopyLiquid, & ! intent(in) mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - scalarCanopyIcePrime, & ! intent(in) - scalarCanopyLiquidPrime, & ! intent(in) - mLayerVolFracIcePrime, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqPrime, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices + mLayerTempTrial, & ! intent(in): trial temperature + mLayerTempPrev, & ! intent(in): previous temperature + mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil + mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil ! output - heatCapVeg, & - mLayerHeatCap, & - heatCapVegPrime, & - mLayerHeatCapPrime, & + mLayerHeatCap, & ! intent(out): heat capacity for snow and soil ! output: error control err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------------- - ! provide access to external subroutines - USE snow_utils_module,only:tcond_snow ! compute thermal conductivity of snow - ! -------------------------------------------------------------------------------------------------------------------------------------- - ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - real(dp),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) - real(dp),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) - real(dp),intent(in) :: scalarCanopyLiquid - real(dp),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) - real(dp),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) - real(dp),intent(in) :: scalarCanopyIcePrime ! trial value of canopy ice content (kg m-2) - real(dp),intent(in) :: scalarCanopyLiquidPrime - real(dp),intent(in) :: mLayerVolFracLiqPrime(:) ! trial vector of volumetric liquid water content (-) - real(dp),intent(in) :: mLayerVolFracIcePrime(:) ! trial vector of volumetric ice water content (-) ! input/output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model layer indices - ! output: error control - real(qp),intent(out) :: heatCapVeg + ! input: + integer(i4b),intent(in) :: nLayers + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + real(dp),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) + real(dp),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) + real(dp),intent(in) :: mLayerTempTrial(:) + real(dp),intent(in) :: mLayerTempPrev(:) + real(dp),intent(in) :: mLayerEnthalpyTrial(:) + real(dp),intent(in) :: mLayerEnthalpyPrev(:) + ! output: real(qp),intent(out) :: mLayerHeatCap(:) - real(qp),intent(out) :: heatCapVegPrime - real(qp),intent(out) :: mLayerHeatCapPrime(:) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables - character(LEN=256) :: cmessage ! error message of downwind routine integer(i4b) :: iLayer ! index of model layer + real(dp) :: delT + real(dp) :: delEnt integer(i4b) :: iSoil ! index of soil layer ! -------------------------------------------------------------------------------------------------------------------------------- ! associate variables in data structure @@ -140,60 +129,35 @@ subroutine computHeatCap(& ! input: depth varying soil parameters iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) - ) ! end associate statement - ! -------------------------------------------------------------------------------------------------------------------------------- + ) ! end associate statemen ! initialize error control err=0; message="computHeatCap/" - ! initialize the soil layer - iSoil=integerMissing - - ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) - if(computeVegFlux)then - heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component - Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component - Cp_ice*scalarCanopyIce/canopyDepth ! ice component - - heatCapVegPrime = Cp_water*scalarCanopyLiquidPrime/canopyDepth + & ! liquid water component - Cp_ice*scalarCanopyIcePrime/canopyDepth ! ice component - end if - ! loop through layers do iLayer=1,nLayers - - ! get the soil layer - if(iLayer>nSnow) iSoil = iLayer-nSnow - - ! ***** - ! * compute the volumetric heat capacity of each layer (J m-3 K-1)... - ! ******************************************************************* - select case(layerType(iLayer)) - ! * soil - case(iname_soil) - mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._dp - theta_sat(iSoil) ) + & ! soil component - iden_ice * Cp_Ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component - - mLayerHeatCapPrime(iLayer) = iden_ice * Cp_Ice * mLayerVolFracIcePrime(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiqPrime(iLayer) + & ! liquid water component - iden_air * Cp_air * (-mLayerVolFracIcePrime(iLayer) - mLayerVolFracLiqPrime(iLayer)) ! air component - ! * snow - case(iname_snow) - mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( 1._dp - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component - - mLayerHeatCapPrime(iLayer) = iden_ice * Cp_ice * mLayerVolFracIcePrime(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiqPrime(iLayer) + & ! liquid water component - iden_air * Cp_air * (-mLayerVolFracIcePrime(iLayer) - mLayerVolFracLiqPrime(iLayer)) ! air component - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return - end select - + delT = mLayerTempTrial(iLayer) - mLayerTempPrev(iLayer) + delEnt = mLayerEnthalpyTrial(iLayer) - mLayerEnthalpyPrev(iLayer) + if(abs(delT) <= 1e-14_dp)then + ! get the soil layer + if(iLayer>nSnow) iSoil = iLayer-nSnow + select case(layerType(iLayer)) + ! * soil + case(iname_soil) + mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._dp - theta_sat(iSoil) ) + & ! soil component + iden_ice * Cp_Ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component + case(iname_snow) + mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( 1._dp - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return + end select + else + mLayerHeatCap(iLayer) = delEnt / delT + endif end do ! looping through layers - !pause - - ! end association to variables in the data structure + end associate end subroutine computHeatCap @@ -296,6 +260,108 @@ subroutine computStatMult(& ! end association to variables in the data structure where vector length does not change end subroutine computStatMult + ! ********************************************************************************************************** + ! public subroutine computHeatCap: compute diagnostic energy variables (thermal conductivity and heat capacity) + ! ********************************************************************************************************** + subroutine computHeatCapAnalytic(& + ! input: control variables + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) + ! input: state variables + scalarCanopyIce, & ! intent(in) + scalarCanopyLiquid, & ! intent(in) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + ! input data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + ! output + heatCapVeg, & + mLayerHeatCap, & + ! output: error control + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! provide access to external subroutines + USE snow_utils_module,only:tcond_snow ! compute thermal conductivity of snow + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! input: model control + logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + real(dp),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) + real(dp),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) + real(dp),intent(in) :: scalarCanopyLiquid + real(dp),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) + real(dp),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) + ! input/output: data structures + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model layer indices + ! output: error control + real(qp),intent(out) :: heatCapVeg + real(qp),intent(out) :: mLayerHeatCap(:) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: iSoil ! index of soil layer + ! -------------------------------------------------------------------------------------------------------------------------------- + ! associate variables in data structure + associate(& + ! input: coordinate variables + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers + layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) + ! input: heat capacity and thermal conductivity + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) + ! input: depth varying soil parameters + iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) + ) ! end associate statement + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="computHeatCapAnalytic/" + + ! initialize the soil layer + iSoil=integerMissing + + ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) + if(computeVegFlux)then + heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component + Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component + Cp_ice*scalarCanopyIce/canopyDepth ! ice component + end if + + ! loop through layers + do iLayer=1,nLayers + + ! get the soil layer + if(iLayer>nSnow) iSoil = iLayer-nSnow + + ! ***** + ! * compute the volumetric heat capacity of each layer (J m-3 K-1)... + ! ******************************************************************* + select case(layerType(iLayer)) + ! * soil + case(iname_soil) + mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._dp - theta_sat(iSoil) ) + & ! soil component + iden_ice * Cp_Ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component + case(iname_snow) + mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( 1._dp - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return + end select + + end do ! looping through layers + !pause + + ! end association to variables in the data structure + end associate + + end subroutine computHeatCapAnalytic end module computHeatCap_module diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 94a610ee0..8da232e5e 100755 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -682,7 +682,7 @@ subroutine coupled_em(& call diagn_evar(& ! input: control variables computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) + diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): canopy depth (m) ! input/output: data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 9b62f1c91..44c778457 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -89,6 +89,7 @@ module eval8summaFida_module ! ********************************************************************************************************** subroutine eval8summaFida(& ! input: model control + printON, & dt_cur, & dt, & ! intent(in): time step nSnow, & ! intent(in): number of snow layers @@ -123,6 +124,8 @@ subroutine eval8summaFida(& ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors + mLayerTempTrial, & ! intent(inout) + mLayerTempPrev, & ! intent(in) mLayerMatricHeadLiqTrial, & !intent(inout) mLayerMatricHeadLiqPrev, & !intent(in) mLayerMatricHeadTrial, & !intent(inout) @@ -145,7 +148,7 @@ subroutine eval8summaFida(& USE varExtrFida_module, only:varExtract ! extract variables from the state vector USE varExtrFida_module, only:varExtractFida USE updateVarsFida_module, only:updateVarsFida ! update prognostic variables - USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy + USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy USE soilCmpresFida_module, only:soilCmpresFida ! compute soil compression USE computFlux_module, only:computFlux ! compute fluxes given a state vector USE computHeatCap_module,only:computHeatCap ! compute diagnostic energy variables -- thermal conductivity and heat capacity @@ -158,6 +161,7 @@ subroutine eval8summaFida(& ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control + logical(lgt),intent(in) :: printON real(dp),intent(in) :: dt_cur real(dp),intent(in) :: dt ! time step integer(i4b),intent(in) :: nSnow ! number of snow layers @@ -192,6 +196,8 @@ subroutine eval8summaFida(& integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) real(dp),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors + real(dp),intent(inout) :: mLayerTempTrial(:) + real(dp),intent(in) :: mLayerTempPrev(:) real(dp),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) real(dp),intent(in) :: mLayerMatricHeadLiqPrev(:) real(dp),intent(inout) :: mLayerMatricHeadTrial(:) ! trial value for liquid water matric potential (m) @@ -218,7 +224,6 @@ subroutine eval8summaFida(& real(dp) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) real(dp) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) real(dp) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(dp),dimension(nLayers) :: mLayerTempTrial ! trial value for temperature of layers in the snow and soil domains (K) real(dp) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) ! diagnostic variables real(dp) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) @@ -540,31 +545,48 @@ subroutine eval8summaFida(& err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + ! compute trial enthalpy using look-up tables + call t2enthalpy_T(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value for canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) + ! output: enthalpy + scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - if (heatCapVaries)then ! *** compute volumetric heat capacity call computHeatCap(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) - ! input: state variables - scalarCanopyIceTrial, & ! intent(in) - scalarCanopyLiqTrial, & ! intent(in) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - scalarCanopyIcePrime, & ! intent(in) - scalarCanopyLiqPrime, & ! intent(in) - mLayerVolFracIcePrime, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqPrime, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - ! input data structures + ! input data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices + ! input: control variables + nLayers, & ! intent(in): number of layers (soil+snow) + diag_data, & ! intent(in) + ! input: state variables + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial temperature + mLayerTempPrev, & ! intent(in): previous temperature + mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil + mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil ! output - heatCapVeg, & ! intent(out) volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(out) volumetric heat capacity of soil and snow - heatCapVegPrime, & ! intent(out) derivarive of volumetric heat capacity of vegetation canopy - mLayerHeatCapPrime, & ! intent(out) derivative of volumetric heat capacity of soil and snow + mLayerHeatCapTrial, & ! intent(out) volumetric heat capacity of soil and snow err,message) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if ! compute multiplier of state vector @@ -599,31 +621,14 @@ subroutine eval8summaFida(& diag_data, & ! intent(inout): model diagnostic variables for a local HRU err,message) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if + + ! finite difference approximation of ice for the phase change + ! do concurrent (iLayer=1:nLayers) + ! mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur + ! end do -! for experiment: computing mLayerHeatCapPrime using finite difference -! if(dt_cur /= 0)then -! do concurrent (iLayer=1:nLayers) -! mLayerHeatCapPrime(iLayer) = ( mLayerHeatCapTrial(iLayer) - mLayerHeatCapPrev(iLayer) ) / dt_cur -! end do -! end if - - else - mLayerHeatCapTrial = mLayerHeatCapPrev - heatCapVegPrime = 0._dp - mLayerHeatCapPrime = 0._dp -end if - -! do concurrent (iLayer=1:nLayers) -! mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur -! end do - - - ! compute enthalpy (J m-3) - select case (enthalpy) - - case(analytical) - ! H' = Cp*T' - rho*L*(theta_ice)' - call computEnthalpyPrime(& + ! H' = Cp*T' - rho*L*(theta_ice)' + call computEnthalpyPrime(& ! input indx_data, & nLayers, & @@ -633,56 +638,12 @@ subroutine eval8summaFida(& ! output mLayerEnthalpyPrime & ) - - case(lookup_tab) - ! compute trial enthalpy using look-up tables - call t2enthalpy(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value for canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) - ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! compute numerical derivative of enthalpy - do concurrent (iLayer=1:nLayers) - mLayerEnthalpyPrime(iLayer) = ( mLayerEnthalpyTrial(iLayer) - mLayerEnthalpyPrev(iLayer) ) / dt_cur - end do - - case(analytic_numeric) - ! compute trial enthalpy = Cp*T - rho*L*theta_ice - ! Note: Cp is out of the d/dt. - call computEnthalpy(& - ! input - indx_data, & - nLayers, & - mLayerTempTrial, & - mLayerVolFracIceTrial, & - mLayerHeatCapPrev, & - ! output - mLayerEnthalpyTrial & - ) - ! compute numerical derivative of enthalpy - do concurrent (iLayer=1:nLayers) - mLayerEnthalpyPrime(iLayer) = ( mLayerEnthalpyTrial(iLayer) - mLayerEnthalpyPrev(iLayer) ) / dt_cur - end do - - end select + + + ! print *, 'mLayerTempPrime = ', mLayerTempPrime(:) + ! print *, 'mLayerVolFracIcePrime = ', mLayerVolFracIcePrime(:) + ! print *, 'mLayerHeatCapTrial = ', mLayerHeatCapTrial(:) + ! print *, 'mLayerEnthalpyPrime = ', mLayerEnthalpyPrime(:) ! compute the residual vector call computResidFida(& @@ -720,6 +681,9 @@ subroutine eval8summaFida(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + if(printON)then + print *, 'rvec = ', resVec(:) + endif ! end association with the information in the data structures end associate diff --git a/build/source/engine/evalEqnsFida.f90 b/build/source/engine/evalEqnsFida.f90 index cab7fa407..3a9bf9c79 100644 --- a/build/source/engine/evalEqnsFida.f90 +++ b/build/source/engine/evalEqnsFida.f90 @@ -94,10 +94,13 @@ integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_d stop 1 end if - + if(eqns_data%printON)then + print *, '1' + endif ! compute the flux and the residual vector for a given state vector call eval8summaFida(& + eqns_data%printON, & ! input: model control stepsize_next(1), & eqns_data%dt, & @@ -132,6 +135,8 @@ integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_d ! input-output: baseflow eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian + eqns_data%mLayerTempTrial, & + eqns_data%mLayerTempPrev, & eqns_data%mLayerMatricHeadLiqTrial, & eqns_data%mLayerMatricHeadLiqPrev, & eqns_data%mLayerMatricHeadTrial, & diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index bce6c966c..59a0922eb 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -274,6 +274,9 @@ subroutine fidaSolver( & integer(i4b),parameter :: ixTrapezoidal=2 integer(i4b) :: iVar logical(lgt) :: startQuadrature + integer(i4b) :: iStep + integer (kind=8) :: numfais(1) + integer (kind=8) :: nnumfais(1) globalVars: associate(& nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): @@ -381,6 +384,9 @@ subroutine fidaSolver( & allocate( eqns_data%mLayerVolFracWatTrial(nLayers) ) allocate( eqns_data%mLayerVolFracWatPrev(nLayers) ) + allocate( eqns_data%mLayerTempTrial(nLayers) ) + allocate( eqns_data%mLayerTempPrev(nLayers) ) + allocate( eqns_data%mLayerVolFracIceTrial(nLayers) ) allocate( eqns_data%mLayerVolFracIcePrev(nLayers) ) @@ -550,7 +556,7 @@ subroutine fidaSolver( & ! Set Coeff. in the nonlinear convergence test, default = 0.33 - coef_nonlin = 1e-1 + coef_nonlin = 1e-8 retval = FIDASetNonlinConvCoef(ida_mem, coef_nonlin) if (retval /= 0) then print *, 'Error in FIDASetNonlinConvCoef, retval = ', retval, '; halting' @@ -613,26 +619,52 @@ subroutine fidaSolver( & tret(1) = t0 eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) - eqns_data%mLayerVolFracIcePrev(:) = diag_data%var(iLookPROG%mLayerVolFracWat)%dat(:) - eqns_data%mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) ! matric head at t0 + eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) + eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) + eqns_data%mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) eqns_data%mLayerHeatCapPrev(:) = diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat(:) eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) - - + + + !********************************************************************************** + !****************************** Main Solver *************************************** + !********************************************************************************** + iStep = 1 + eqns_data%printON = .false. do while(tret(1) < dt) ! call IDASolve retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) + retval = FIDAGetNumErrTestFails(ida_mem, numfais) + retval = FIDAGetNumNonlinSolvConvFails(ida_mem, nnumfais) ! get the last stepsize retval = FIDAGetLastStep(ida_mem, stepsize_cur) if (retval /= 0) then print *, 'Error in FIDAGetLastStep, retval = ', retval, '; halting' stop 1 end if +! print *, 'stepsize = ', stepsize_cur(1) + +! if (stepsize_cur(1) < 2.79e-11_dp)then +! eqns_data%printON = .true. +! iStep = iStep + 1 +! print *, 'step_size = ', stepsize_cur(1) +! endif + + if(eqns_data%printON)then + print *, 'accuracy fails = ', numfais(1) + print *, 'convergence fails = ', nnumfais(1) + print *, '----------------------------------------' + endif + + if(iStep == 10) stop 1 + + ! compute the flux and the residual vector for a given state vector call eval8summaFida(& + eqns_data%printON, & ! input: model control stepsize_cur(1), & eqns_data%dt, & @@ -667,6 +699,8 @@ subroutine fidaSolver( & ! input-output: baseflow eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian + eqns_data%mLayerTempTrial, & + eqns_data%mLayerTempPrev, & eqns_data%mLayerMatricHeadLiqTrial, & eqns_data%mLayerMatricHeadLiqPrev, & eqns_data%mLayerMatricHeadTrial, & @@ -685,6 +719,9 @@ subroutine fidaSolver( & eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation rVec, & ! intent(out): residual vector eqns_data%err,eqns_data%message) ! intent(out): error control + +! print *, 'rVec = ', rVec(:) +! stop 1 select case(ixQuadrature) ! sum of flux @@ -713,18 +750,13 @@ subroutine fidaSolver( & ! sum of mLayerCmpress mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & * ( eqns_data%mLayerMatricHeadLiqTrial(:) - eqns_data%mLayerMatricHeadLiqPrev(:) ) + eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) eqns_data%mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) eqns_data%mLayerHeatCapPrev(:) = eqns_data%mLayerHeatCapTrial(:) eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) - - ! just for experiment: computing water balance error for the current substep -! if(size(eqns_data%indx_data%var(iLookINDEX%ixMatOnly)%dat)>0)then - ! soilWatBalErr = sum( real(rVec(eqns_data%indx_data%var(iLookINDEX%ixMatOnly)%dat), dp)*eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat(eqns_data%nSnow + eqns_data%indx_data%var(iLookINDEX%ixMatricHead)%dat ) ) -! endif - ! print *, soilWatBalErr end do ! while loop on one_step mode @@ -772,6 +804,8 @@ subroutine fidaSolver( & deallocate( eqns_data%mLayerVolFracWatTrial ) deallocate( eqns_data%mLayerVolFracWatPrev ) deallocate( eqns_data%mLayerVolFracIceTrial ) + deallocate( eqns_data%mLayerTempPrev ) + deallocate( eqns_data%mLayerTempTrial ) deallocate( eqns_data%mLayerVolFracIcePrev ) deallocate( eqns_data%mLayerHeatCapTrial ) deallocate( eqns_data%mLayerHeatCapPrev ) diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index 8fb8c967f..f2d250109 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -18,6 +18,7 @@ module fida_datatypes implicit none type eqnsData + logical(lgt) :: printON real(dp) :: dt ! time step integer(i4b) :: nSnow ! number of snow layers integer(i4b) :: nSoil ! number of soil layers @@ -63,6 +64,8 @@ module fida_datatypes real(dp), allocatable :: mLayerHeatCapPrev(:) real(dp), allocatable :: mLayerEnthalpyTrial(:) real(dp), allocatable :: mLayerEnthalpyPrev(:) + real(dp), allocatable :: mLayerTempTrial(:) + real(dp), allocatable :: mLayerTempPrev(:) real(dp), allocatable :: fluxVec(:) ! flux vector real(qp), allocatable :: resSink(:) real(qp), allocatable :: resVec(:) diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index c7d6171e1..7c4f525b5 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -549,7 +549,8 @@ subroutine sysSolvFida(& end do ! iteration over tolerances - +! print *, 'rVec = ', rVec(:) +! stop 1 ! check if fida is successful if( tret(1) /= dt .or. .not.feasible )then diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index 6896bfb3b..b5cfa12d9 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -63,6 +63,7 @@ module t2enthalpy_module private public::T2E_lookup public::t2enthalpy +public::t2enthalpy_T ! define the look-up table used to compute temperature based on enthalpy contains @@ -518,5 +519,284 @@ subroutine t2enthalpy(& end associate generalVars end subroutine t2enthalpy + + + ! ************************************************************************************************************************ + ! public subroutine t2enthalpy_T: compute enthalpy from temperature and total water content + ! ************************************************************************************************************************ + subroutine t2enthalpy_T(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in) + ! output: enthalpy + scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,message) ! intent(out): error control + ! ------------------------------------------------------------------------------------------------------------------------- + ! downwind routines + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water + USE spline_int_module,only:splint ! use for cubic spline interpolation + implicit none + ! delare dummy variables + ! ------------------------------------------------------------------------------------------------------------------------- + ! input: data structures + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model indices + type(zLookup),intent(in) :: lookup_data ! lookup tables + ! input: state variables for the vegetation canopy + real(dp),intent(in) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) + real(dp),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(dp),intent(in) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + real(dp),intent(in) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + real(dp),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(dp),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(dp),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(dp),intent(in) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric fraction of Ice (-) + ! output: enthalpy + real(dp),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) + real(dp),intent(out) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) + real(dp),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------------------------------- + ! declare local variables + character(len=128) :: cmessage ! error message in downwind routine + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixDomainType ! name of a given model domain + integer(i4b) :: ixControlIndex ! index within a given model domain + real(dp) :: vGn_m ! van Genuchten "m" parameter (-) + real(dp) :: Tcrit ! temperature where all water is unfrozen (K) + real(dp) :: psiLiq ! matric head of liquid water (m) + real(dp) :: vFracWat ! volumetric fraction of total water, liquid+ice (-) + real(dp) :: vFracLiq ! volumetric fraction of liquid water (-) + real(dp) :: vFracIce ! volumetric fraction of ice (-) + real(dp) :: enthTemp ! enthalpy at the temperature of the control volume (J m-3) + real(dp) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) + real(dp) :: enthPhase ! enthalpy associated with phase change (J m-3) + real(dp) :: enthWater ! enthalpy of total water (J m-3) + real(dp) :: enthSoil ! enthalpy of soil particles (J m-3) + real(dp) :: enthMix ! enthalpy of the mixed region, liquid+ice (J m-3) + real(dp) :: enthLiq ! enthalpy of the liquid region (J m-3) + real(dp) :: enthAir ! enthalpy of air (J m-3) + real(dp) :: diffT + real(dp) :: integral + real(dp) :: enthIce + real(dp) :: enthVeg + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + generalVars: associate(& + ! number of model layers, and layer type + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + ! mapping between the full state vector and the state subset + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ! type of domain, type of state variable, and index of control volume within domain + ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) ! end associate statement + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message="t2enthalpy_T/" + + ! loop through model state variables + do iState=1,size(ixMapSubset2Full) + + ! ----- + ! - compute indices... + ! -------------------- + + ! get domain type, and index of the control volume within the domain + ixFullVector = ixMapSubset2Full(iState) ! index within full state vector + ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) + ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain + + ! check an energy state + if(ixStateType(ixFullVector)==iname_nrgCanair .or. ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer)then + + ! get the layer index + select case(ixDomainType) + case(iname_cas); iLayer = integerMissing + case(iname_veg); iLayer = integerMissing + case(iname_snow); iLayer = ixControlIndex + case(iname_soil); iLayer = ixControlIndex + nSnow + case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + + ! identify domain + select case(ixDomainType) + + ! ----- + ! - canopy air space... + ! --------------------- + case(iname_cas) + scalarCanairEnthalpy = Cp_air * iden_air * (scalarCanairTempTrial - Tfreeze) + ! ----- + ! - vegetation canopy... + ! ----------------------- + case(iname_veg) + ! association to necessary variables for vegetation + vegVars: associate(& + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): [dp] canopy depth (m) + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) + + diffT = scalarCanopyTempTrial - Tfreeze + enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth + if(diffT>=0._dp)then + enthLiq = Cp_water * scalarCanopyWatTrial * diffT / canopyDepth + enthIce = 0._dp + enthPhase = 0._dp + else + integral = (1._dp/snowfrz_scale) * atan(snowfrz_scale * diffT) + enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth + enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth + enthPhase = LH_fus * scalarCanopyIceTrial / canopyDepth + end if + + scalarCanopyEnthalpy = enthVeg + enthLiq + enthIce + + ! end association + end associate vegVars + + ! ----- + ! - snow... + ! --------- + case(iname_snow) + + ! association to necessary variables for snow + snowVars: associate(& + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) + + diffT = mLayerTempTrial(iLayer) - Tfreeze + if(diffT>=0._dp)then + enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * diffT + enthIce = 0._dp + enthAir = iden_air * Cp_air * ( 1._dp - mLayerVolFracWatTrial(iLayer) ) * diffT + enthPhase = 0._dp + else + integral = (1._dp/snowfrz_scale) * atan(snowfrz_scale * diffT) + enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral + enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) + enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) + enthPhase = iden_ice * LH_fus * mLayerVolFracIceTrial(iLayer) + end if + + mLayerEnthalpy(iLayer) = enthLiq + enthIce + enthAir + + ! end association + end associate snowVars + + ! ----- + ! - soil... + ! --------- + case(iname_soil) + + ! make association to variables in the data structures... + soilVars: associate(& + + ! associate model parameters + soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex) , & ! intrinsic soil density (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex) , & ! soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex) , & ! volumetric residual water content (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) + + ! associate values in the lookup table + Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) + Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) + E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + + ) ! end associate statement + + ! diagnostic variables + vGn_m = 1._dp - 1._dp/vGn_n + Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) + vFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + + + ! *** compute enthalpy of water for unfrozen conditions + if(mlayerTempTrial(iLayer) > Tcrit)then + enthWater = iden_water*Cp_water*vFracWat*(mlayerTempTrial(iLayer) - Tfreeze) ! valid for temperatures below freezing also + enthPhase = 0._dp + + ! *** compute enthalpy of water for frozen conditions + else + ! calculate enthalpy at the temperature (cubic spline interpolation) + call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),enthTemp,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! calculate enthalpy at the critical temperature (cubic spline interpolation) + call splint(Tk,Ey,E2,Tcrit,enthTcrit,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! calculate the enthalpy of water + enthMix = enthTemp - enthTcrit ! enthalpy of the liquid+ice mix + enthLiq = iden_water*Cp_water*vFracWat*(Tcrit - Tfreeze) + enthWater = enthMix + enthLiq + + ! *** compute the enthalpy associated with phase change + psiLiq = (mLayerTempTrial(iLayer) - Tfreeze)*LH_fus/(gravity*Tfreeze) + vFracLiq = volFracLiq(psiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + vFracIce = vFracWat - vFracLiq + enthPhase = iden_water*LH_fus*vFracIce + + endif ! (if frozen conditions) + + ! *** compute the enthalpy of soil + enthSoil = soil_dens_intr*Cp_soil*(1._dp - theta_sat)*(mlayerTempTrial(iLayer) - Tfreeze) + + ! *** compute the enthalpy of air + enthAir = iden_air*Cp_air*(1._dp - theta_sat - vFracWat)*(mlayerTempTrial(iLayer) - Tfreeze) + + ! *** compute the total enthalpy (J m-3) + mLayerEnthalpy(iLayer) = enthSoil + enthWater + enthAir + +! print*, iLayer, enthSoil, enthWater, enthAir, enthPhase + + end associate soilVars + + ! ----- + ! - checks... + ! ----------- + case(iname_aquifer); cycle ! aquifer: do nothing + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + + end if ! if an energy layer + end do ! looping through state variables + + end associate generalVars + + end subroutine t2enthalpy_T end module t2enthalpy_module From 2fa3c4b665e5bff2578e1f92b93a78b79280b507 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Mon, 8 Feb 2021 00:27:39 -0600 Subject: [PATCH 0012/1472] minor edit --- build/source/engine/fidaSolver.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 59a0922eb..d7fcaef06 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -556,7 +556,7 @@ subroutine fidaSolver( & ! Set Coeff. in the nonlinear convergence test, default = 0.33 - coef_nonlin = 1e-8 + coef_nonlin = 1e-3 retval = FIDASetNonlinConvCoef(ida_mem, coef_nonlin) if (retval /= 0) then print *, 'Error in FIDASetNonlinConvCoef, retval = ', retval, '; halting' From 260a0db36af298dbde6216b8616ca70753b875c7 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 10 Feb 2021 17:19:26 -0600 Subject: [PATCH 0013/1472] some extra variables deleted from eval8summafida --- build/source/engine/eval8summaFida.f90 | 18 ++++-------------- build/source/engine/fidaSolver.f90 | 4 ++-- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 44c778457..2c9f588aa 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -259,11 +259,6 @@ subroutine eval8summaFida(& character(LEN=256) :: cmessage ! error message of downwind routine real(qp) :: heatCapVeg real(qp) :: heatCapVegPrime - real(qp),dimension(nLayers) :: mLayerHeatCapPrime - integer(i4b),parameter :: analytical=1 - integer(i4b),parameter :: lookup_tab=2 - integer(i4b),parameter :: analytic_numeric=3 - integer(i4b) :: enthalpy=lookup_tab ! analytical or lookup_tab or analytic_numeric ! -------------------------------------------------------------------------------------------------------------------------------- @@ -623,9 +618,9 @@ subroutine eval8summaFida(& if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if ! finite difference approximation of ice for the phase change - ! do concurrent (iLayer=1:nLayers) - ! mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur - ! end do + do concurrent (iLayer=1:nLayers) + mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur + end do ! H' = Cp*T' - rho*L*(theta_ice)' call computEnthalpyPrime(& @@ -638,12 +633,7 @@ subroutine eval8summaFida(& ! output mLayerEnthalpyPrime & ) - - - ! print *, 'mLayerTempPrime = ', mLayerTempPrime(:) - ! print *, 'mLayerVolFracIcePrime = ', mLayerVolFracIcePrime(:) - ! print *, 'mLayerHeatCapTrial = ', mLayerHeatCapTrial(:) - ! print *, 'mLayerEnthalpyPrime = ', mLayerEnthalpyPrime(:) + ! compute the residual vector call computResidFida(& diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index d7fcaef06..fa28f04b3 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -548,7 +548,7 @@ subroutine fidaSolver( & ! Set the maximum BDF order, default = 5 - retval = FIDASetMaxOrd(ida_mem, 1) + retval = FIDASetMaxOrd(ida_mem, 5) if (retval /= 0) then print *, 'Error in FIDASetMaxOrd, retval = ', retval, '; halting' stop 1 @@ -556,7 +556,7 @@ subroutine fidaSolver( & ! Set Coeff. in the nonlinear convergence test, default = 0.33 - coef_nonlin = 1e-3 + coef_nonlin = 0.33 retval = FIDASetNonlinConvCoef(ida_mem, coef_nonlin) if (retval /= 0) then print *, 'Error in FIDASetNonlinConvCoef, retval = ', retval, '; halting' From a46685f3fe3a0fdd1617e4dd1dd558d0ba58e706 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 10 Feb 2021 17:30:33 -0600 Subject: [PATCH 0014/1472] heatCapPrev deleted from fidaSolver --- build/source/engine/eval8summaFida.f90 | 5 +---- build/source/engine/evalEqnsFida.f90 | 2 -- build/source/engine/fidaSolver.f90 | 9 --------- build/source/engine/fida_datatypes.f90 | 2 -- 4 files changed, 1 insertion(+), 17 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 2c9f588aa..077fc2455 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -134,8 +134,6 @@ subroutine eval8summaFida(& mLayerVolFracWatPrev, & !intent(inout) mLayerVolFracIceTrial, & !intent(inout) mLayerVolFracIcePrev, & !intent(inout) - mLayerHeatCapPrev, & - mLayerHeatCapTrial, & mLayerEnthalpyPrev, & ! intent(in) mLayerEnthalpyTrial, & ! intent(out) feasible, & ! intent(out): flag to denote the feasibility of the solution @@ -206,8 +204,6 @@ subroutine eval8summaFida(& real(dp),intent(in) :: mLayerVolFracWatPrev(:) real(dp),intent(inout) :: mLayerVolFracIceTrial(:) real(dp),intent(in) :: mLayerVolFracIcePrev(:) - real(dp),intent(in) :: mLayerHeatCapPrev(:) - real(dp),intent(inout) :: mLayerHeatCapTrial(:) real(dp),intent(in) :: mLayerEnthalpyPrev(:) real(dp),intent(out) :: mLayerEnthalpyTrial(:) logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution @@ -247,6 +243,7 @@ subroutine eval8summaFida(& ! enthalpy real(dp) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) real(dp) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) + real(dp),dimension(nLayers) :: mLayerHeatCapTrial ! heat capacity of each snow+soil layer real(dp),dimension(nLayers) :: mLayerEnthalpyPrime ! enthalpy of each snow+soil layer (J m-3) ! other local variables integer(i4b) :: iLayer ! index of model layer in the snow+soil domain diff --git a/build/source/engine/evalEqnsFida.f90 b/build/source/engine/evalEqnsFida.f90 index 3a9bf9c79..b6a6f09c6 100644 --- a/build/source/engine/evalEqnsFida.f90 +++ b/build/source/engine/evalEqnsFida.f90 @@ -145,8 +145,6 @@ integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_d eqns_data%mLayerVolFracWatPrev, & eqns_data%mLayerVolFracIceTrial, & eqns_data%mLayerVolFracIcePrev, & - eqns_data%mLayerHeatCapPrev, & - eqns_data%mLayerHeatCapTrial, & eqns_data%mLayerEnthalpyPrev, & ! intent(in) eqns_data%mLayerEnthalpyTrial, & ! intent(out) ! output diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index fa28f04b3..e4d062759 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -390,9 +390,6 @@ subroutine fidaSolver( & allocate( eqns_data%mLayerVolFracIceTrial(nLayers) ) allocate( eqns_data%mLayerVolFracIcePrev(nLayers) ) - allocate( eqns_data%mLayerHeatCapTrial(nLayers) ) - allocate( eqns_data%mLayerHeatCapPrev(nLayers) ) - allocate( eqns_data%mLayerEnthalpyTrial(nLayers) ) allocate( eqns_data%mLayerEnthalpyPrev(nLayers) ) @@ -623,7 +620,6 @@ subroutine fidaSolver( & eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) eqns_data%mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) - eqns_data%mLayerHeatCapPrev(:) = diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat(:) eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) @@ -709,8 +705,6 @@ subroutine fidaSolver( & eqns_data%mLayerVolFracWatPrev, & eqns_data%mLayerVolFracIceTrial, & eqns_data%mLayerVolFracIcePrev, & - eqns_data%mLayerHeatCapPrev, & - eqns_data%mLayerHeatCapTrial, & eqns_data%mLayerEnthalpyPrev, & ! intent(in) eqns_data%mLayerEnthalpyTrial, & ! intent(out) ! output @@ -755,7 +749,6 @@ subroutine fidaSolver( & eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) - eqns_data%mLayerHeatCapPrev(:) = eqns_data%mLayerHeatCapTrial(:) eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) end do ! while loop on one_step mode @@ -807,8 +800,6 @@ subroutine fidaSolver( & deallocate( eqns_data%mLayerTempPrev ) deallocate( eqns_data%mLayerTempTrial ) deallocate( eqns_data%mLayerVolFracIcePrev ) - deallocate( eqns_data%mLayerHeatCapTrial ) - deallocate( eqns_data%mLayerHeatCapPrev ) deallocate( eqns_data%mLayerEnthalpyTrial ) deallocate( eqns_data%mLayerEnthalpyPrev ) diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index f2d250109..2430cc4ac 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -60,8 +60,6 @@ module fida_datatypes real(dp), allocatable :: mLayerVolFracWatPrev(:) real(dp), allocatable :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of total water (-) real(dp), allocatable :: mLayerVolFracIcePrev(:) - real(dp), allocatable :: mLayerHeatCapTrial(:) ! trial value for volumetric fraction of total water (-) - real(dp), allocatable :: mLayerHeatCapPrev(:) real(dp), allocatable :: mLayerEnthalpyTrial(:) real(dp), allocatable :: mLayerEnthalpyPrev(:) real(dp), allocatable :: mLayerTempTrial(:) From 7da40a26cc6d062006a33a70dc0b59c677027614 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 10 Feb 2021 17:47:22 -0600 Subject: [PATCH 0015/1472] matricHeadLiqPrev deleted from eval8summaFida --- build/source/engine/eval8summaFida.f90 | 2 -- build/source/engine/evalEqnsFida.f90 | 19 +++++++++---------- build/source/engine/fidaSolver.f90 | 10 ++++------ build/source/engine/fida_datatypes.f90 | 1 - 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 077fc2455..3d4bee895 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -127,7 +127,6 @@ subroutine eval8summaFida(& mLayerTempTrial, & ! intent(inout) mLayerTempPrev, & ! intent(in) mLayerMatricHeadLiqTrial, & !intent(inout) - mLayerMatricHeadLiqPrev, & !intent(in) mLayerMatricHeadTrial, & !intent(inout) mLayerMatricHeadPrev, & !intent(in) mLayerVolFracWatTrial, & !intent(inout) @@ -197,7 +196,6 @@ subroutine eval8summaFida(& real(dp),intent(inout) :: mLayerTempTrial(:) real(dp),intent(in) :: mLayerTempPrev(:) real(dp),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) - real(dp),intent(in) :: mLayerMatricHeadLiqPrev(:) real(dp),intent(inout) :: mLayerMatricHeadTrial(:) ! trial value for liquid water matric potential (m) real(dp),intent(in) :: mLayerMatricHeadPrev(:) real(dp),intent(inout) :: mLayerVolFracWatTrial(:) diff --git a/build/source/engine/evalEqnsFida.f90 b/build/source/engine/evalEqnsFida.f90 index b6a6f09c6..1b5ad7b9f 100644 --- a/build/source/engine/evalEqnsFida.f90 +++ b/build/source/engine/evalEqnsFida.f90 @@ -135,18 +135,17 @@ integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_d ! input-output: baseflow eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian - eqns_data%mLayerTempTrial, & - eqns_data%mLayerTempPrev, & - eqns_data%mLayerMatricHeadLiqTrial, & - eqns_data%mLayerMatricHeadLiqPrev, & - eqns_data%mLayerMatricHeadTrial, & - eqns_data%mLayerMatricHeadPrev, & + eqns_data%mLayerTempTrial, & + eqns_data%mLayerTempPrev, & + eqns_data%mLayerMatricHeadLiqTrial,& + eqns_data%mLayerMatricHeadTrial, & + eqns_data%mLayerMatricHeadPrev, & eqns_data%mLayerVolFracWatTrial, & - eqns_data%mLayerVolFracWatPrev, & + eqns_data%mLayerVolFracWatPrev, & eqns_data%mLayerVolFracIceTrial, & - eqns_data%mLayerVolFracIcePrev, & - eqns_data%mLayerEnthalpyPrev, & ! intent(in) - eqns_data%mLayerEnthalpyTrial, & ! intent(out) + eqns_data%mLayerVolFracIcePrev, & + eqns_data%mLayerEnthalpyPrev, & ! intent(in) + eqns_data%mLayerEnthalpyTrial, & ! intent(out) ! output feasible, & ! intent(out): flag to denote the feasibility of the solution eqns_data%fluxVec, & ! intent(out): flux vector diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index e4d062759..a518eda4f 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -277,6 +277,7 @@ subroutine fidaSolver( & integer(i4b) :: iStep integer (kind=8) :: numfais(1) integer (kind=8) :: nnumfais(1) + real(dp) :: mLayerMatricHeadLiqPrev(nSoil) globalVars: associate(& nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): @@ -376,7 +377,6 @@ subroutine fidaSolver( & eqns_data%mLayerCmpress_sum = mLayerCmpress_sum allocate( eqns_data%mLayerMatricHeadLiqTrial(nSoil) ) - allocate( eqns_data%mLayerMatricHeadLiqPrev(nSoil) ) allocate( eqns_data%mLayerMatricHeadTrial(nSoil) ) allocate( eqns_data%mLayerMatricHeadPrev(nSoil) ) @@ -618,7 +618,7 @@ subroutine fidaSolver( & eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) - eqns_data%mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) + mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) @@ -698,7 +698,6 @@ subroutine fidaSolver( & eqns_data%mLayerTempTrial, & eqns_data%mLayerTempPrev, & eqns_data%mLayerMatricHeadLiqTrial, & - eqns_data%mLayerMatricHeadLiqPrev, & eqns_data%mLayerMatricHeadTrial, & eqns_data%mLayerMatricHeadPrev, & eqns_data%mLayerVolFracWatTrial, & @@ -743,9 +742,9 @@ subroutine fidaSolver( & end select ! sum of mLayerCmpress mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & - * ( eqns_data%mLayerMatricHeadLiqTrial(:) - eqns_data%mLayerMatricHeadLiqPrev(:) ) + * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) - eqns_data%mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) + mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) @@ -788,7 +787,6 @@ subroutine fidaSolver( & deallocate(eqns_data%dBaseflow_dMatric) deallocate(eqns_data%mLayerCmpress_sum) deallocate(eqns_data%mLayerMatricHeadLiqTrial) - deallocate(eqns_data%mLayerMatricHeadLiqPrev) deallocate(eqns_data%mLayerMatricHeadTrial) deallocate(eqns_data%mLayerMatricHeadPrev) deallocate( eqns_data%fluxVec ) diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index 2430cc4ac..7212eac63 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -53,7 +53,6 @@ module fida_datatypes real(dp), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) real(dp), allocatable :: mLayerCmpress_sum(:) real(dp), allocatable :: mLayerMatricHeadLiqTrial(:) - real(dp), allocatable :: mLayerMatricHeadLiqPrev(:) real(dp), allocatable :: mLayerMatricHeadTrial(:) real(dp), allocatable :: mLayerMatricHeadPrev(:) real(dp), allocatable :: mLayerVolFracWatTrial(:) ! trial value for volumetric fraction of total water (-) From 208fea75d9e3d775ab7d271ae04ec0ea9983edcb Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 10 Feb 2021 19:32:57 -0600 Subject: [PATCH 0016/1472] printON delted --- build/source/engine/eval8summaFida.f90 | 7 +------ build/source/engine/evalEqnsFida.f90 | 6 +----- build/source/engine/fidaSolver.f90 | 24 +----------------------- build/source/engine/fida_datatypes.f90 | 1 - 4 files changed, 3 insertions(+), 35 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 3d4bee895..0ed73d9f3 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -89,7 +89,6 @@ module eval8summaFida_module ! ********************************************************************************************************** subroutine eval8summaFida(& ! input: model control - printON, & dt_cur, & dt, & ! intent(in): time step nSnow, & ! intent(in): number of snow layers @@ -158,7 +157,6 @@ subroutine eval8summaFida(& ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control - logical(lgt),intent(in) :: printON real(dp),intent(in) :: dt_cur real(dp),intent(in) :: dt ! time step integer(i4b),intent(in) :: nSnow ! number of snow layers @@ -665,10 +663,7 @@ subroutine eval8summaFida(& resVec, & ! intent(out): residual vector err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - if(printON)then - print *, 'rvec = ', resVec(:) - endif + ! end association with the information in the data structures end associate diff --git a/build/source/engine/evalEqnsFida.f90 b/build/source/engine/evalEqnsFida.f90 index 1b5ad7b9f..0e9892c71 100644 --- a/build/source/engine/evalEqnsFida.f90 +++ b/build/source/engine/evalEqnsFida.f90 @@ -93,14 +93,10 @@ integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_d print *, 'Error in FIDAGetCurrentStep, retval = ', retval, '; halting' stop 1 end if - - if(eqns_data%printON)then - print *, '1' - endif + ! compute the flux and the residual vector for a given state vector call eval8summaFida(& - eqns_data%printON, & ! input: model control stepsize_next(1), & eqns_data%dt, & diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index a518eda4f..6355439fe 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -625,42 +625,22 @@ subroutine fidaSolver( & !********************************************************************************** !****************************** Main Solver *************************************** - !********************************************************************************** - iStep = 1 - eqns_data%printON = .false. + !********************************************************************************** do while(tret(1) < dt) ! call IDASolve retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) - retval = FIDAGetNumErrTestFails(ida_mem, numfais) - retval = FIDAGetNumNonlinSolvConvFails(ida_mem, nnumfais) ! get the last stepsize retval = FIDAGetLastStep(ida_mem, stepsize_cur) if (retval /= 0) then print *, 'Error in FIDAGetLastStep, retval = ', retval, '; halting' stop 1 end if -! print *, 'stepsize = ', stepsize_cur(1) - -! if (stepsize_cur(1) < 2.79e-11_dp)then -! eqns_data%printON = .true. -! iStep = iStep + 1 -! print *, 'step_size = ', stepsize_cur(1) -! endif - - if(eqns_data%printON)then - print *, 'accuracy fails = ', numfais(1) - print *, 'convergence fails = ', nnumfais(1) - print *, '----------------------------------------' - endif - - if(iStep == 10) stop 1 ! compute the flux and the residual vector for a given state vector call eval8summaFida(& - eqns_data%printON, & ! input: model control stepsize_cur(1), & eqns_data%dt, & @@ -713,8 +693,6 @@ subroutine fidaSolver( & rVec, & ! intent(out): residual vector eqns_data%err,eqns_data%message) ! intent(out): error control -! print *, 'rVec = ', rVec(:) -! stop 1 select case(ixQuadrature) ! sum of flux diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index 7212eac63..85a2c8823 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -18,7 +18,6 @@ module fida_datatypes implicit none type eqnsData - logical(lgt) :: printON real(dp) :: dt ! time step integer(i4b) :: nSnow ! number of snow layers integer(i4b) :: nSoil ! number of soil layers From 25a43f27ec33f3ab09af472a4a269e9ff5517bfe Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 10 Feb 2021 19:55:28 -0600 Subject: [PATCH 0017/1472] more cleaning --- build/source/engine/fidaSolver.f90 | 4 +--- build/source/engine/sysSolvFida.f90 | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 6355439fe..d8de26952 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -41,7 +41,7 @@ module fidaSolver_module USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers -USE globalData,only:model_decisions ! model decision structure +USE globalData,only:model_decisions ! model decision structure ! global metadata USE globalData,only:flux_meta ! metadata on the model fluxes @@ -124,7 +124,6 @@ subroutine fidaSolver( & dMat, & ! intent(inout) numDiscon, & ! intent(in) ! input: data structures - ! model_decisions, & ! intent(in): model decisions lookup_data, & type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes @@ -213,7 +212,6 @@ subroutine fidaSolver( & real(dp), intent(inout) :: dMat(:) integer(i4b), intent(in) :: numDiscon ! input: data structures -! type(model_options),intent(in) :: model_decisions(:) ! model decisions type(zLookup),intent(in) :: lookup_data ! lookup tables type(var_i), intent(in) :: type_data ! type of vegetation and soil type(var_d), intent(in) :: attr_data ! spatial attributes diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 7c4f525b5..5067de92e 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -509,7 +509,6 @@ subroutine sysSolvFida(& dMat, & ! intent(inout) numDiscon, & ! intent(in) ! input: data structures -! model_decisions, & ! intent(in): model decisions lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes From 66f95e4f3c55d3c46a9d6e5565f3992e332486b9 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 10 Feb 2021 20:56:59 -0600 Subject: [PATCH 0018/1472] varSubstepFida cleaned --- build/source/engine/fidaSolver.f90 | 31 ++++++++++++-------------- build/source/engine/varSubstepFida.f90 | 28 ++++------------------- 2 files changed, 18 insertions(+), 41 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index d8de26952..35ab890c3 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -206,7 +206,7 @@ subroutine fidaSolver( & logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution logical(lgt),intent(in) :: heatCapVaries !flag to indicate if heat capacity is constant in the current substep ! input: state vectors - real(dp),intent(in) :: stateVecInit(:) ! model state vector + real(dp),intent(in) :: stateVecInit(:) ! model state vector real(dp),intent(in) :: fScale(:) ! function scaling vector real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(dp), intent(inout) :: dMat(:) @@ -259,23 +259,20 @@ subroutine fidaSolver( & integer(i4b) :: retval logical(lgt) :: feasible ! feasibility flag real(qp) :: t0 - integer (kind = 8) :: maxstep - integer(kind = 8) :: mu, lu - real(qp) :: h_max - real(qp) :: coef_nonlin - integer(i4b) :: lsflag - integer(i4b) :: iLayer ! index of layer in the snow+soil domain - integer(i4b) :: iState ! index of model state + integer (kind = 8) :: maxstep + integer(kind = 8) :: mu, lu + real(qp) :: h_max + real(qp) :: coef_nonlin + integer(i4b) :: lsflag + integer(i4b) :: iLayer ! index of layer in the snow+soil domain + integer(i4b) :: iState ! index of model state real(dp) :: idenIW - real(c_double) :: stepsize_cur(1) - integer(i4b),parameter :: ixRectangular=1 ! reza: should put them in a data type later - integer(i4b),parameter :: ixTrapezoidal=2 - integer(i4b) :: iVar - logical(lgt) :: startQuadrature - integer(i4b) :: iStep - integer (kind=8) :: numfais(1) - integer (kind=8) :: nnumfais(1) - real(dp) :: mLayerMatricHeadLiqPrev(nSoil) + real(c_double) :: stepsize_cur(1) + integer(i4b),parameter :: ixRectangular=1 ! reza: should put them in a data type later + integer(i4b),parameter :: ixTrapezoidal=2 + integer(i4b) :: iVar + logical(lgt) :: startQuadrature + real(dp) :: mLayerMatricHeadLiqPrev(nSoil) globalVars: associate(& nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): diff --git a/build/source/engine/varSubstepFida.f90 b/build/source/engine/varSubstepFida.f90 index 40f25a091..0d78d0577 100644 --- a/build/source/engine/varSubstepFida.f90 +++ b/build/source/engine/varSubstepFida.f90 @@ -412,7 +412,7 @@ subroutine varSubstepFida(& ! identify the need to check the mass balance checkMassBalance = .true. ! (.not.scalarSolution) - checkNrgBalance = .false. + checkNrgBalance = .true. ! update prognostic variables call updateProgFida(dtSubstep,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control @@ -637,14 +637,10 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt real(dp) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) real(dp),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector for volumetric fraction of liquid water (-) real(dp),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector for volumetric fraction of ice (-) - real(dp) :: enthalpy0, enthalpy1 ! amount of energy required to bring the temperature to a reference value - real(dp) :: nrgError - integer(i4b) :: iLayer - real(dp),dimension(nLayers) :: left, mLayerNrgError - real(qp),dimension(nLayers) :: mLayerHeatCap real(dp) :: scalarCanairEnthalpyTrial ! enthalpy of the canopy air space (J m-3) real(dp) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3 - real(dp),dimension(nLayers) :: mLayerEnthalpyTrial + real(dp),dimension(nLayers) :: mLayerEnthalpyTrial + integer(i4b) :: iLayer ! ------------------------------------------------------------------------------------------------------------------- ! ------------------------------------------------------------------------------------------------------------------- @@ -764,10 +760,6 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - !print*, 'after varExtract: scalarCanopyTempTrial =', scalarCanopyTempTrial ! trial value of canopy temperature (K) - !print*, 'after varExtract: scalarCanopyWatTrial =', scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - !print*, 'after varExtract: scalarCanopyLiqTrial =', scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) - !print*, 'after varExtract: scalarCanopyIceTrial =', scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) ! update diagnostic variables call updateVarsFida(& ! input @@ -809,7 +801,7 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt ! ---- ! * check energy balance !------------------------ - + ! NOTE: for now, we just compute enthalpy if(checkNrgBalance)then ! compute enthalpy at t_{n+1} call t2enthalpy(& @@ -835,18 +827,6 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - do concurrent (iLayer=1:nLayers) - left(iLayer) = (mLayerEnthalpyTrial(iLayer) - mLayerEnthalpy(iLayer)) * mLayerDepth(iLayer) - end do - - do concurrent (iLayer=1:nLayers) - mLayerNrgError(iLayer) = left(iLayer) + ( (iLayerNrgFlux(iLayer) - iLayerNrgFlux(iLayer-1)) )*dt - end do - -! write(1,*) mLayerNrgError(1:nLayers) -! write(2,*) mLayerEnthalpyTrial(1:nLayers) -! write(3,*) left(1:nLayers) endif From df8f539d89963f523bf67ff4b9f97b8dae35c8dc Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 10 Feb 2021 21:07:30 -0600 Subject: [PATCH 0019/1472] minor edit --- build/source/engine/eval8summaFida.f90 | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 0ed73d9f3..b87ff1862 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -533,7 +533,7 @@ subroutine eval8summaFida(& err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! compute trial enthalpy using look-up tables + ! compute H_T call t2enthalpy_T(& ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -558,7 +558,7 @@ subroutine eval8summaFida(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! *** compute volumetric heat capacity + ! *** compute volumetric heat capacity, C_p = dH_T/dT call computHeatCap(& ! input data structures mpar_data, & ! intent(in): model parameters @@ -577,11 +577,12 @@ subroutine eval8summaFida(& mLayerHeatCapTrial, & ! intent(out) volumetric heat capacity of soil and snow err,message) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if + ! compute multiplier of state vector call computStatMult(& ! input heatCapVeg, & ! intent(in) volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(in) volumetric heat capacity of soil and snow + mLayerHeatCapTrial, & ! intent(in) volumetric heat capacity of soil and snow diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output @@ -610,7 +611,7 @@ subroutine eval8summaFida(& err,message) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if - ! finite difference approximation of ice for the phase change + ! finite difference approximation of (theta_ice)' do concurrent (iLayer=1:nLayers) mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur end do From 4962e1de117981b5f3e987a04515aeefadd887eb Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 10 Feb 2021 22:04:29 -0600 Subject: [PATCH 0020/1472] Cp veg added to computHeatCap --- build/source/engine/computHeatCap.f90 | 69 +++++++++++++++++++------- build/source/engine/eval8summaFida.f90 | 49 +++++++++++------- build/source/engine/fidaSolver.f90 | 7 +-- 3 files changed, 86 insertions(+), 39 deletions(-) diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index 156f6a085..026f31405 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -76,30 +76,48 @@ module computHeatCap_module ! public subroutine computHeatCap: compute diagnostic energy variables (heat capacity) ! ********************************************************************************************************** subroutine computHeatCap(& + ! input: control variables + nLayers, & ! intent(in): number of layers (soil+snow) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - ! input: control variables - nLayers, & ! intent(in): number of layers (soil+snow) - diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + diag_data, & ! intent(in): model diagnostic variables for a local HRU ! input: state variables - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial temperature - mLayerTempPrev, & ! intent(in): previous temperature - mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil - mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil + scalarCanopyIce, & ! intent(in) + scalarCanopyLiquid, & ! intent(in) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial temperature + mLayerTempPrev, & ! intent(in): previous temperature + mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil + mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil ! output - mLayerHeatCap, & ! intent(out): heat capacity for snow and soil + heatCapVeg, & + mLayerHeatCap, & ! intent(out): heat capacity for snow and soil ! output: error control err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------------- + ! input: control variables + logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + real(dp),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) ! input/output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model layer indices ! input: integer(i4b),intent(in) :: nLayers type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + real(dp),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) + real(dp),intent(in) :: scalarCanopyLiquid + real(dp),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature + real(dp),intent(in) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) + real(dp),intent(in) :: scalarCanopyEnthalpyPrev ! intent(in): previous enthalpy of the vegetation canopy (J m-3) + real(dp),intent(in) :: scalarCanopyTempPrev ! Previous value of canopy temperature real(dp),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) real(dp),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) real(dp),intent(in) :: mLayerTempTrial(:) @@ -107,15 +125,16 @@ subroutine computHeatCap(& real(dp),intent(in) :: mLayerEnthalpyTrial(:) real(dp),intent(in) :: mLayerEnthalpyPrev(:) ! output: + real(qp),intent(out) :: heatCapVeg real(qp),intent(out) :: mLayerHeatCap(:) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables - integer(i4b) :: iLayer ! index of model layer - real(dp) :: delT - real(dp) :: delEnt - integer(i4b) :: iSoil ! index of soil layer + integer(i4b) :: iLayer ! index of model layer + real(dp) :: delT + real(dp) :: delEnt + integer(i4b) :: iSoil ! index of soil layer ! -------------------------------------------------------------------------------------------------------------------------------- ! associate variables in data structure associate(& @@ -132,11 +151,26 @@ subroutine computHeatCap(& ) ! end associate statemen ! initialize error control err=0; message="computHeatCap/" + + ! initialize the soil layer + iSoil=integerMissing + + ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) + if(computeVegFlux)then + delT = scalarCanopyTempTrial - scalarCanopyTempPrev + if(abs(delT) <= 1e-14_dp)then + heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component + Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component + Cp_ice*scalarCanopyIce/canopyDepth ! ice component + else + delEnt = scalarCanopyEnthalpyTrial - scalarCanopyEnthalpyPrev + heatCapVeg = delEnt / delT + end if + end if ! loop through layers do iLayer=1,nLayers delT = mLayerTempTrial(iLayer) - mLayerTempPrev(iLayer) - delEnt = mLayerEnthalpyTrial(iLayer) - mLayerEnthalpyPrev(iLayer) if(abs(delT) <= 1e-14_dp)then ! get the soil layer if(iLayer>nSnow) iSoil = iLayer-nSnow @@ -154,6 +188,7 @@ subroutine computHeatCap(& case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return end select else + delEnt = mLayerEnthalpyTrial(iLayer) - mLayerEnthalpyPrev(iLayer) mLayerHeatCap(iLayer) = delEnt / delT endif end do ! looping through layers diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index b87ff1862..6ca41bf6d 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -215,6 +215,7 @@ subroutine eval8summaFida(& ! state variables real(dp) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) real(dp) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(dp) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) real(dp) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) real(dp) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) ! diagnostic variables @@ -238,7 +239,8 @@ subroutine eval8summaFida(& real(dp),dimension(nLayers) :: mLayerVolFracIcePrime ! derivative value for volumetric fraction of ice (-) ! enthalpy real(dp) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - real(dp) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) + real(dp) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) + real(dp) :: scalarCanopyEnthalpyPrev ! previous value of enthalpy of the vegetation canopy (J m-3) real(dp),dimension(nLayers) :: mLayerHeatCapTrial ! heat capacity of each snow+soil layer real(dp),dimension(nLayers) :: mLayerEnthalpyPrime ! enthalpy of each snow+soil layer (J m-3) ! other local variables @@ -250,7 +252,7 @@ subroutine eval8summaFida(& real(dp),parameter :: canopyTempMax=500._dp ! expected maximum value for the canopy temperature (K) real(dp),dimension(nState) :: rVecScaled ! scaled residual vector character(LEN=256) :: cmessage ! error message of downwind routine - real(qp) :: heatCapVeg + real(qp) :: heatCapVegTrial real(qp) :: heatCapVegPrime @@ -552,36 +554,45 @@ subroutine eval8summaFida(& mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) ! output: enthalpy scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! *** compute volumetric heat capacity, C_p = dH_T/dT + ! *** compute volumetric heat capacity C_p = dH_T/dT call computHeatCap(& - ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices ! input: control variables - nLayers, & ! intent(in): number of layers (soil+snow) - diag_data, & ! intent(in) + nLayers, & ! intent(in): number of layers (soil+snow) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) + ! input data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + diag_data, & ! intent(in): model diagnostic variables for a local HRU ! input: state variables - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial temperature - mLayerTempPrev, & ! intent(in): previous temperature - mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil - mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil + scalarCanopyIceTrial, & ! intent(in): trial value for canopy ice content (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial temperature + mLayerTempPrev, & ! intent(in): previous temperature + mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil + mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil ! output - mLayerHeatCapTrial, & ! intent(out) volumetric heat capacity of soil and snow + heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil + ! output: error control err,message) ! intent(out): error control - if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if ! compute multiplier of state vector call computStatMult(& ! input - heatCapVeg, & ! intent(in) volumetric heat capacity of vegetation canopy + heatCapVegTrial, & ! intent(in) volumetric heat capacity of vegetation canopy mLayerHeatCapTrial, & ! intent(in) volumetric heat capacity of soil and snow diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers @@ -590,7 +601,7 @@ subroutine eval8summaFida(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) = heatCapVeg + diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) = heatCapVegTrial diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat(:) = mLayerHeatCapTrial(:) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 35ab890c3..0b7e329cf 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -620,6 +620,7 @@ subroutine fidaSolver( & !********************************************************************************** !****************************** Main Solver *************************************** + !************************* loop on one_step mode ********************************** !********************************************************************************** do while(tret(1) < dt) ! call IDASolve @@ -631,9 +632,7 @@ subroutine fidaSolver( & print *, 'Error in FIDAGetLastStep, retval = ', retval, '; halting' stop 1 end if - - - + ! compute the flux and the residual vector for a given state vector call eval8summaFida(& ! input: model control @@ -713,6 +712,7 @@ subroutine fidaSolver( & stepsize_past = stepsize_cur(1) case default; err=20; message=trim(message)//'expect case to be ixRecangular, ixTrapezoidal'; return end select + ! sum of mLayerCmpress mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) @@ -725,6 +725,7 @@ subroutine fidaSolver( & end do ! while loop on one_step mode + !****************************** End of Main Solver *************************************** firstFluxCall = eqns_data%firstFluxCall fluxVec = eqns_data%fluxVec From f7f02425a937a02d3a96c738659e40a01d95aab7 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 10 Feb 2021 22:10:31 -0600 Subject: [PATCH 0021/1472] minor edit --- build/source/engine/eval8summaFida.f90 | 2 +- build/source/engine/fida_datatypes.f90 | 108 ++++++++++++------------- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 6ca41bf6d..042df8f0d 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -587,7 +587,7 @@ subroutine eval8summaFida(& heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil ! output: error control - err,message) ! intent(out): error control + err,message) ! intent(out): error control ! compute multiplier of state vector call computStatMult(& diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index 85a2c8823..0dbce4405 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -18,60 +18,60 @@ module fida_datatypes implicit none type eqnsData - real(dp) :: dt ! time step - integer(i4b) :: nSnow ! number of snow layers - integer(i4b) :: nSoil ! number of soil layers - integer(i4b) :: nLayers ! total number of layers - integer :: nState ! total number of state variables - integer(i4b) :: ixMatrix ! form of matrix (dense or banded) - integer(i4b) :: ixQuadrature ! type of quadrature method for approximating average flux - logical(lgt) :: startQuadrature - logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution - logical(lgt) :: heatCapVaries - real(dp), allocatable :: fScale(:) ! function scaling vector - real(qp), allocatable :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) - real(dp), allocatable :: dMat(:) - type(zLookup) :: lookup_data ! lookup tables - type(var_i) :: type_data ! type of vegetation and soil - type(var_d) :: attr_data ! spatial attributes - type(var_dlength) :: mpar_data ! model parameters - type(var_d) :: forc_data ! model forcing data - type(var_dlength) :: bvar_data ! model variables for the local basin - type(var_dlength) :: prog_data ! prognostic variables for a local HRU - type(var_ilength) :: indx_data ! indices defining model states and layers - type(var_dlength) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength) :: flux_temp ! model fluxes for a local HRU - type(var_dlength) :: flux_data ! model fluxes for a local HRU - type(var_dlength) :: flux_sum - type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(dp), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - real(dp), allocatable :: mLayerCmpress_sum(:) - real(dp), allocatable :: mLayerMatricHeadLiqTrial(:) - real(dp), allocatable :: mLayerMatricHeadTrial(:) - real(dp), allocatable :: mLayerMatricHeadPrev(:) - real(dp), allocatable :: mLayerVolFracWatTrial(:) ! trial value for volumetric fraction of total water (-) - real(dp), allocatable :: mLayerVolFracWatPrev(:) - real(dp), allocatable :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of total water (-) - real(dp), allocatable :: mLayerVolFracIcePrev(:) - real(dp), allocatable :: mLayerEnthalpyTrial(:) - real(dp), allocatable :: mLayerEnthalpyPrev(:) - real(dp), allocatable :: mLayerTempTrial(:) - real(dp), allocatable :: mLayerTempPrev(:) - real(dp), allocatable :: fluxVec(:) ! flux vector - real(qp), allocatable :: resSink(:) - real(qp), allocatable :: resVec(:) - real(dp), allocatable :: atol(:) - real(dp), allocatable :: rtol(:) - integer(i4b) :: err ! error code - character(len = 50) :: message ! error message - type(c_ptr) :: ida_mem - real(dp) :: t_cur - real(dp) :: stepsize_past + real(dp) :: dt ! time step + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers + integer(i4b) :: nLayers ! total number of layers + integer :: nState ! total number of state variables + integer(i4b) :: ixMatrix ! form of matrix (dense or banded) + integer(i4b) :: ixQuadrature ! type of quadrature method for approximating average flux + logical(lgt) :: startQuadrature + logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution + logical(lgt) :: heatCapVaries + real(dp), allocatable :: fScale(:) ! function scaling vector + real(qp), allocatable :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + real(dp), allocatable :: dMat(:) + type(zLookup) :: lookup_data ! lookup tables + type(var_i) :: type_data ! type of vegetation and soil + type(var_d) :: attr_data ! spatial attributes + type(var_dlength) :: mpar_data ! model parameters + type(var_d) :: forc_data ! model forcing data + type(var_dlength) :: bvar_data ! model variables for the local basin + type(var_dlength) :: prog_data ! prognostic variables for a local HRU + type(var_ilength) :: indx_data ! indices defining model states and layers + type(var_dlength) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength) :: flux_temp ! model fluxes for a local HRU + type(var_dlength) :: flux_data ! model fluxes for a local HRU + type(var_dlength) :: flux_sum + type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + real(dp), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + real(dp), allocatable :: mLayerCmpress_sum(:) + real(dp), allocatable :: mLayerMatricHeadLiqTrial(:) + real(dp), allocatable :: mLayerMatricHeadTrial(:) + real(dp), allocatable :: mLayerMatricHeadPrev(:) + real(dp), allocatable :: mLayerVolFracWatTrial(:) ! trial value for volumetric fraction of total water (-) + real(dp), allocatable :: mLayerVolFracWatPrev(:) + real(dp), allocatable :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of total water (-) + real(dp), allocatable :: mLayerVolFracIcePrev(:) + real(dp), allocatable :: mLayerEnthalpyTrial(:) + real(dp), allocatable :: mLayerEnthalpyPrev(:) + real(dp), allocatable :: mLayerTempTrial(:) + real(dp), allocatable :: mLayerTempPrev(:) + real(dp), allocatable :: fluxVec(:) ! flux vector + real(qp), allocatable :: resSink(:) + real(qp), allocatable :: resVec(:) + real(dp), allocatable :: atol(:) + real(dp), allocatable :: rtol(:) + integer(i4b) :: err ! error code + character(len = 50) :: message ! error message + type(c_ptr) :: ida_mem + real(dp) :: t_cur + real(dp) :: stepsize_past end type eqnsData From 2e44bef414a7497878e0de488739967caf810319 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 10 Feb 2021 22:25:24 -0600 Subject: [PATCH 0022/1472] scalarCanopyEnthalpy in fidaSolver --- build/source/engine/fidaSolver.f90 | 53 ++++++++++++++------------ build/source/engine/fida_datatypes.f90 | 5 +++ 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 0b7e329cf..fd86b1a64 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -610,12 +610,13 @@ subroutine fidaSolver( & ! retval = FIDASetLinearSolutionScaling(ida_mem, 0) ! scalling_on 1 and off 0 tret(1) = t0 - eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) - eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) - eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) - mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) - eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) - eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) + eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) + eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) + eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) + eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) + eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) + eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) + mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) !********************************************************************************** @@ -684,7 +685,7 @@ subroutine fidaSolver( & feasible, & ! intent(out): flag to denote the feasibility of the solution eqns_data%fluxVec, & ! intent(out): flux vector eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector + rVec, & ! intent(out): residual vector eqns_data%err,eqns_data%message) ! intent(out): error control @@ -713,32 +714,34 @@ subroutine fidaSolver( & case default; err=20; message=trim(message)//'expect case to be ixRecangular, ixTrapezoidal'; return end select -! sum of mLayerCmpress + ! sum of mLayerCmpress mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) - eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) - mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) - eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) - eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) - eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) - eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) + ! save values of some quantities for next step + eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) + mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) + eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) + eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) + eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) + eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) + eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial end do ! while loop on one_step mode !****************************** End of Main Solver *************************************** - firstFluxCall = eqns_data%firstFluxCall - fluxVec = eqns_data%fluxVec - diag_data = eqns_data%diag_data - flux_temp = eqns_data%flux_temp - flux_data = eqns_data%flux_data - deriv_data = eqns_data%deriv_data + firstFluxCall = eqns_data%firstFluxCall + fluxVec = eqns_data%fluxVec + diag_data = eqns_data%diag_data + flux_temp = eqns_data%flux_temp + flux_data = eqns_data%flux_data + deriv_data = eqns_data%deriv_data dBaseflow_dMatric = eqns_data%dBaseflow_dMatric - ixSaturation = eqns_data%ixSaturation - resSink = eqns_data%resSink - stepsize_past = eqns_data%stepsize_past - err = eqns_data%err - message = eqns_data%message + ixSaturation = eqns_data%ixSaturation + resSink = eqns_data%resSink + stepsize_past = eqns_data%stepsize_past + err = eqns_data%err + message = eqns_data%message retval = FIDAGetLastStep(ida_mem, dt_last) diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index 0dbce4405..4ce240ece 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -49,6 +49,11 @@ module fida_datatypes type(var_dlength) :: flux_sum type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + real(dp) :: scalar + real(dp) :: scalarCanopyTempTrial ! intent(in): trial value of canopy temperature (K) + real(dp) :: scalarCanopyTempPrev ! intent(in): previous value of canopy temperature (K) + real(dp) :: scalarCanopyEnthalpyTrial ! intent(in): trial enthalpy of the vegetation canopy (J m-3) + real(dp) :: scalarCanopyEnthalpyPrev ! intent(in): previous enthalpy of the vegetation canopy (J m-3) real(dp), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) real(dp), allocatable :: mLayerCmpress_sum(:) real(dp), allocatable :: mLayerMatricHeadLiqTrial(:) From 3f6c5052f2ca8e330a01f7f888f6e102d09be7f9 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 10 Feb 2021 22:32:47 -0600 Subject: [PATCH 0023/1472] canopyTempPrev in fidaSolver --- build/source/engine/fidaSolver.f90 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index fd86b1a64..abedd3208 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -610,6 +610,8 @@ subroutine fidaSolver( & ! retval = FIDASetLinearSolutionScaling(ida_mem, 0) ! scalling_on 1 and off 0 tret(1) = t0 + ! need the following values for the first substep + eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) @@ -718,6 +720,7 @@ subroutine fidaSolver( & mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) ! save values of some quantities for next step + eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) From 6954269cc6a9da0de9b404926ad391ea15e7e6a3 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 10 Feb 2021 22:38:43 -0600 Subject: [PATCH 0024/1472] new variables in eval8summaFida --- build/source/engine/eval8summaFida.f90 | 12 ++++++++---- build/source/engine/evalEqnsFida.f90 | 4 ++++ build/source/engine/fidaSolver.f90 | 4 ++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 042df8f0d..9f6166dd9 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -123,6 +123,10 @@ subroutine eval8summaFida(& ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) mLayerTempTrial, & ! intent(inout) mLayerTempPrev, & ! intent(in) mLayerMatricHeadLiqTrial, & !intent(inout) @@ -191,6 +195,10 @@ subroutine eval8summaFida(& integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) real(dp),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors + real(dp) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(dp) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) + real(dp) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) + real(dp) :: scalarCanopyEnthalpyPrev ! previous value of enthalpy of the vegetation canopy (J m-3) real(dp),intent(inout) :: mLayerTempTrial(:) real(dp),intent(in) :: mLayerTempPrev(:) real(dp),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) @@ -214,8 +222,6 @@ subroutine eval8summaFida(& ! -------------------------------------------------------------------------------------------------------------------------------- ! state variables real(dp) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(dp) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(dp) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) real(dp) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) real(dp) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) ! diagnostic variables @@ -239,8 +245,6 @@ subroutine eval8summaFida(& real(dp),dimension(nLayers) :: mLayerVolFracIcePrime ! derivative value for volumetric fraction of ice (-) ! enthalpy real(dp) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - real(dp) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) - real(dp) :: scalarCanopyEnthalpyPrev ! previous value of enthalpy of the vegetation canopy (J m-3) real(dp),dimension(nLayers) :: mLayerHeatCapTrial ! heat capacity of each snow+soil layer real(dp),dimension(nLayers) :: mLayerEnthalpyPrime ! enthalpy of each snow+soil layer (J m-3) ! other local variables diff --git a/build/source/engine/evalEqnsFida.f90 b/build/source/engine/evalEqnsFida.f90 index 0e9892c71..46d787393 100644 --- a/build/source/engine/evalEqnsFida.f90 +++ b/build/source/engine/evalEqnsFida.f90 @@ -131,6 +131,10 @@ integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_d ! input-output: baseflow eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian + eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + eqns_data%scalarCanopyEnthalpyTrial,& ! intent(in): trial enthalpy of the vegetation canopy (J m-3) + eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) eqns_data%mLayerTempTrial, & eqns_data%mLayerTempPrev, & eqns_data%mLayerMatricHeadLiqTrial,& diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index abedd3208..4b375477d 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -672,6 +672,10 @@ subroutine fidaSolver( & ! input-output: baseflow eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian + eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + eqns_data%scalarCanopyEnthalpyTrial,& ! intent(in): trial enthalpy of the vegetation canopy (J m-3) + eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) eqns_data%mLayerTempTrial, & eqns_data%mLayerTempPrev, & eqns_data%mLayerMatricHeadLiqTrial, & From 02e3a680e0cf1773d1a11efb96dfa6b0f2b03c8a Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 10 Feb 2021 23:28:03 -0600 Subject: [PATCH 0025/1472] scalarCanopyEnthalpyPrime --- build/source/engine/computEnthalpy.f90 | 22 +++++++++++++--- build/source/engine/computResidFida.f90 | 6 ++--- build/source/engine/eval8summaFida.f90 | 34 +++++++++++++++---------- 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/build/source/engine/computEnthalpy.f90 b/build/source/engine/computEnthalpy.f90 index ec3ae1498..6feae444c 100644 --- a/build/source/engine/computEnthalpy.f90 +++ b/build/source/engine/computEnthalpy.f90 @@ -96,22 +96,34 @@ end subroutine computEnthalpy ! ********************************************************************************************************** subroutine computEnthalpyPrime(& ! input + computeVegFlux, & indx_data, & nLayers, & + canopyDepth, & ! intent(in): canopy depth (m) + scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K) + scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2) mLayerTempPrime, & mLayerVolFracIcePrime, & + heatCapVeg, & mLayerHeatCap, & ! output + scalarCanopyEnthalpyPrime, & mLayerEnthalpyPrime & ) ! -------------------------------------------------------------------------------------------------------------------------------- implicit none ! input: model control - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers integer(i4b),intent(in) :: nLayers ! number of snow layers - real(dp),intent(in) :: mLayerTempPrime(:) ! temperature of each snow/soil layer (K) - real(dp),intent(in) :: mLayerVolFracIcePrime(:) ! volumetric fraction of ice (-) + real(dp),intent(in) :: canopyDepth ! canopy depth (m) + real(dp),intent(in) :: scalarCanopyTempPrime ! Prime value for the temperature of the vegetation canopy (K) + real(dp),intent(in) :: scalarCanopyIcePrime ! Prime value for the ice on the vegetation canopy (kg m-2) + real(dp),intent(in) :: heatCapVeg + real(dp),intent(in) :: mLayerTempPrime(:) ! temperature of each snow/soil layer (K) + real(dp),intent(in) :: mLayerVolFracIcePrime(:) ! volumetric fraction of ice (-) real(dp),intent(in) :: mLayerHeatCap(:) + real(dp),intent(out) :: scalarCanopyEnthalpyPrime real(dp),intent(out) :: mLayerEnthalpyPrime(:) ! local variables @@ -123,6 +135,10 @@ subroutine computEnthalpyPrime(& layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of layer ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat & ! intent(in): [i4b(:)] indices for energy states ) + + if(computeVegFlux)then + scalarCanopyEnthalpyPrime = heatCapVeg * scalarCanopyTempPrime - LH_fus*scalarCanopyIcePrime/canopyDepth + end if ! (loop through non-missing energy state variables in the snow+soil domain) do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) select case( layerType(iLayer) ) diff --git a/build/source/engine/computResidFida.f90 b/build/source/engine/computResidFida.f90 index c1441f45d..ccecc23db 100644 --- a/build/source/engine/computResidFida.f90 +++ b/build/source/engine/computResidFida.f90 @@ -78,7 +78,7 @@ subroutine computResidFida(& mLayerVolFracIcePrime, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) mLayerVolFracWatPrime, & mLayerVolFracLiqPrime, & - heatCapVegPrime, & + scalarCanopyEnthalpyPrime, & mLayerEnthalpyPrime, & ! intent(in) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU @@ -113,7 +113,7 @@ subroutine computResidFida(& real(dp),intent(in) :: mLayerVolFracIcePrime(:) ! trial value for volumetric fraction of ice (-) real(dp),intent(in) :: mLayerVolFracLiqPrime(:) real(dp),intent(in) :: mLayerVolFracWatPrime(:) - real(qp),intent(in) :: heatCapVegPrime + real(qp),intent(in) :: scalarCanopyEnthalpyPrime real(qp),intent(in) :: mLayerEnthalpyPrime(:) ! input: data structures type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU @@ -206,7 +206,7 @@ subroutine computResidFida(& ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision ! --> energy balance if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*scalarCanairTempPrime - ( fVec(ixCasNrg) + rAdd(ixCasNrg) ) - if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = heatCapVegPrime * scalarCanopyTempTrial + sMul(ixVegNrg) * scalarCanopyTempPrime - ( fVec(ixVegNrg) + rAdd(ixVegNrg) ) + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = scalarCanopyEnthalpyPrime - fVec(ixVegNrg) ! --> mass balance if(ixVegHyd/=integerMissing)then scalarCanopyHydPrime = merge(scalarCanopyWatPrime, scalarCanopyLiqPrime, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 9f6166dd9..3cee35974 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -257,7 +257,7 @@ subroutine eval8summaFida(& real(dp),dimension(nState) :: rVecScaled ! scaled residual vector character(LEN=256) :: cmessage ! error message of downwind routine real(qp) :: heatCapVegTrial - real(qp) :: heatCapVegPrime + real(qp) :: scalarCanopyEnthalpyPrime ! -------------------------------------------------------------------------------------------------------------------------------- @@ -631,17 +631,23 @@ subroutine eval8summaFida(& mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur end do - ! H' = Cp*T' - rho*L*(theta_ice)' - call computEnthalpyPrime(& - ! input - indx_data, & - nLayers, & - mLayerTempPrime, & - mLayerVolFracIcePrime, & - mLayerHeatCapTrial, & - ! output - mLayerEnthalpyPrime & - ) + ! H' = Cp*T' - rho*L*(theta_ice)' + call computEnthalpyPrime(& + ! input + computeVegFlux, & + indx_data, & + nLayers, & + canopyDepth, & ! intent(in): canopy depth (m) + scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K) + scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2) + mLayerTempPrime, & + mLayerVolFracIcePrime, & + heatCapVegTrial, & + mLayerHeatCapTrial, & + ! output + scalarCanopyEnthalpyPrime, & + mLayerEnthalpyPrime & + ) ! compute the residual vector @@ -667,8 +673,8 @@ subroutine eval8summaFida(& mLayerVolFracIcePrime, & ! intent(in): Prime value for the volumetric ice in each snow and soil layer (-) mLayerVolFracWatPrime, & mLayerVolFracLiqPrime, & - heatCapVegPrime, & ! intent(in) derivarive of volumetric heat capacity of vegetation canopy - mLayerEnthalpyPrime, & ! intent(in) derivative of volumetric heat capacity of soil and snow + scalarCanopyEnthalpyPrime, & ! intent(in) derivarive of enthalpy of vegetation canopy + mLayerEnthalpyPrime, & ! intent(in) derivative of enthalpy of soil and snow ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU From 925c6232b2fa12ac2838e1bd526932e790290d79 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 11 Feb 2021 00:49:49 -0600 Subject: [PATCH 0026/1472] theta_ice prime of veg using finite diff --- build/source/engine/eval8summaFida.f90 | 14 +++++++++----- build/source/engine/evalEqnsFida.f90 | 2 ++ build/source/engine/fidaSolver.f90 | 6 +++++- build/source/engine/fida_datatypes.f90 | 2 ++ 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 3cee35974..685bbdf1e 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -125,6 +125,8 @@ subroutine eval8summaFida(& ! output: flux and residual vectors scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + scalarCanopyIceTrial, & + scalarCanopyIcePrev, & scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) mLayerTempTrial, & ! intent(inout) @@ -195,10 +197,12 @@ subroutine eval8summaFida(& integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) real(dp),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors - real(dp) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(dp) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) - real(dp) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) - real(dp) :: scalarCanopyEnthalpyPrev ! previous value of enthalpy of the vegetation canopy (J m-3) + real(dp),intent(inout) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(dp),intent(in) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) + real(dp),intent(inout) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(dp),intent(in) :: scalarCanopyIcePrev ! previous value for mass of ice on the vegetation canopy (kg m-2) + real(dp),intent(inout) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) + real(dp),intent(in) :: scalarCanopyEnthalpyPrev ! previous value of enthalpy of the vegetation canopy (J m-3) real(dp),intent(inout) :: mLayerTempTrial(:) real(dp),intent(in) :: mLayerTempPrev(:) real(dp),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) @@ -226,7 +230,6 @@ subroutine eval8summaFida(& real(dp) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) ! diagnostic variables real(dp) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(dp) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) real(dp),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) ! derivative of state variables @@ -627,6 +630,7 @@ subroutine eval8summaFida(& if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if ! finite difference approximation of (theta_ice)' + scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur do concurrent (iLayer=1:nLayers) mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur end do diff --git a/build/source/engine/evalEqnsFida.f90 b/build/source/engine/evalEqnsFida.f90 index 46d787393..9454a9cad 100644 --- a/build/source/engine/evalEqnsFida.f90 +++ b/build/source/engine/evalEqnsFida.f90 @@ -133,6 +133,8 @@ integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_d eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + eqns_data%scalarCanopyIceTrial, & + eqns_data%scalarCanopyIcePrev, & eqns_data%scalarCanopyEnthalpyTrial,& ! intent(in): trial enthalpy of the vegetation canopy (J m-3) eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) eqns_data%mLayerTempTrial, & diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 4b375477d..06834da23 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -540,7 +540,7 @@ subroutine fidaSolver( & ! Set the maximum BDF order, default = 5 - retval = FIDASetMaxOrd(ida_mem, 5) + retval = FIDASetMaxOrd(ida_mem, 1) if (retval /= 0) then print *, 'Error in FIDASetMaxOrd, retval = ', retval, '; halting' stop 1 @@ -612,6 +612,7 @@ subroutine fidaSolver( & tret(1) = t0 ! need the following values for the first substep eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) + eqns_data%scalarCanopyIcePrev = prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) @@ -674,6 +675,8 @@ subroutine fidaSolver( & eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + eqns_data%scalarCanopyIceTrial, & + eqns_data%scalarCanopyIcePrev, & eqns_data%scalarCanopyEnthalpyTrial,& ! intent(in): trial enthalpy of the vegetation canopy (J m-3) eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) eqns_data%mLayerTempTrial, & @@ -725,6 +728,7 @@ subroutine fidaSolver( & * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) ! save values of some quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial + eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index 4ce240ece..899fefa01 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -52,6 +52,8 @@ module fida_datatypes real(dp) :: scalar real(dp) :: scalarCanopyTempTrial ! intent(in): trial value of canopy temperature (K) real(dp) :: scalarCanopyTempPrev ! intent(in): previous value of canopy temperature (K) + real(dp) :: scalarCanopyIceTrial + real(dp) :: scalarCanopyIcePrev real(dp) :: scalarCanopyEnthalpyTrial ! intent(in): trial enthalpy of the vegetation canopy (J m-3) real(dp) :: scalarCanopyEnthalpyPrev ! intent(in): previous enthalpy of the vegetation canopy (J m-3) real(dp), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) From cfd7e70f21bb312479739f236bb9df945894c73b Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 11 Feb 2021 00:54:07 -0600 Subject: [PATCH 0027/1472] minor edti --- build/source/engine/eval8summaFida.f90 | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 685bbdf1e..2a2382a0f 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -123,23 +123,23 @@ subroutine eval8summaFida(& ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - scalarCanopyIceTrial, & - scalarCanopyIcePrev, & - scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) - scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + scalarCanopyIceTrial, & + scalarCanopyIcePrev, & + scalarCanopyEnthalpyTrial,& ! intent(in): trial enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpyPrev,& ! intent(in): previous enthalpy of the vegetation canopy (J m-3) mLayerTempTrial, & ! intent(inout) mLayerTempPrev, & ! intent(in) - mLayerMatricHeadLiqTrial, & !intent(inout) - mLayerMatricHeadTrial, & !intent(inout) - mLayerMatricHeadPrev, & !intent(in) - mLayerVolFracWatTrial, & !intent(inout) + mLayerMatricHeadLiqTrial,& !intent(inout) + mLayerMatricHeadTrial, & !intent(inout) + mLayerMatricHeadPrev, & !intent(in) + mLayerVolFracWatTrial, & !intent(inout) mLayerVolFracWatPrev, & !intent(inout) - mLayerVolFracIceTrial, & !intent(inout) + mLayerVolFracIceTrial, & !intent(inout) mLayerVolFracIcePrev, & !intent(inout) - mLayerEnthalpyPrev, & ! intent(in) - mLayerEnthalpyTrial, & ! intent(out) + mLayerEnthalpyPrev, & ! intent(in) + mLayerEnthalpyTrial, & ! intent(out) feasible, & ! intent(out): flag to denote the feasibility of the solution fluxVec, & ! intent(out): flux vector resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation From 0a47867aac26309b961f9b6bb632ad91e83f3db8 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 11 Feb 2021 22:26:36 -0600 Subject: [PATCH 0028/1472] minor edit --- build/source/engine/eval8summaFida.f90 | 8 ++++---- build/source/engine/fidaSolver.f90 | 4 ++-- build/source/engine/sysSolvFida.f90 | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 2a2382a0f..431a7d022 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -611,7 +611,7 @@ subroutine eval8summaFida(& diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) = heatCapVegTrial diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat(:) = mLayerHeatCapTrial(:) - + ! update thermal conductivity call computThermConduct(& ! input: control variables computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux @@ -629,7 +629,7 @@ subroutine eval8summaFida(& err,message) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if - ! finite difference approximation of (theta_ice)' + ! to conserve energy compute finite difference approximation of (theta_ice)' scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur do concurrent (iLayer=1:nLayers) mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur @@ -642,8 +642,8 @@ subroutine eval8summaFida(& indx_data, & nLayers, & canopyDepth, & ! intent(in): canopy depth (m) - scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K) - scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2) + scalarCanopyTempPrime, & ! intent(in): prime value for the temperature of the vegetation canopy (K) + scalarCanopyIcePrime, & ! intent(in): prime value for the ice on the vegetation canopy (kg m-2) mLayerTempPrime, & mLayerVolFracIcePrime, & heatCapVegTrial, & diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 06834da23..29f24747c 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -540,7 +540,7 @@ subroutine fidaSolver( & ! Set the maximum BDF order, default = 5 - retval = FIDASetMaxOrd(ida_mem, 1) + retval = FIDASetMaxOrd(ida_mem, 5) if (retval /= 0) then print *, 'Error in FIDASetMaxOrd, retval = ', retval, '; halting' stop 1 @@ -548,7 +548,7 @@ subroutine fidaSolver( & ! Set Coeff. in the nonlinear convergence test, default = 0.33 - coef_nonlin = 0.33 + coef_nonlin = 1e-2 !0.33 retval = FIDASetNonlinConvCoef(ida_mem, coef_nonlin) if (retval /= 0) then print *, 'Error in FIDASetNonlinConvCoef, retval = ', retval, '; halting' diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 5067de92e..30738ad6d 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -473,8 +473,8 @@ subroutine sysSolvFida(& ! ------------------ h_init = 0 - atol = 1e-6 - rtol = 1e-6 + atol = 1e-8 + rtol = 1e-8 do tol_iter=1,5 From af3b64406cf247f292993b37da95f93926d5fdb9 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 14 Feb 2021 18:38:43 -0600 Subject: [PATCH 0029/1472] opsplittin edited --- build/source/engine/fidaSolver.f90 | 2 +- build/source/engine/opSplittin.f90 | 18 +++++++++++------- build/source/engine/sysSolvFida.f90 | 4 ++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 29f24747c..fb756b78c 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -548,7 +548,7 @@ subroutine fidaSolver( & ! Set Coeff. in the nonlinear convergence test, default = 0.33 - coef_nonlin = 1e-2 !0.33 + coef_nonlin = 0.33 retval = FIDASetNonlinConvCoef(ida_mem, coef_nonlin) if (retval /= 0) then print *, 'Error in FIDASetNonlinConvCoef, retval = ', retval, '; halting' diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 9bacd1f8b..b44da974b 100755 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -122,7 +122,6 @@ module opSplittin_module ! named variables for the coupling method integer(i4b),parameter :: fullyCoupled=1 ! 1st try: fully coupled solution integer(i4b),parameter :: stateTypeSplit=2 ! 2nd try: separate solutions for each state type -!integer(i4b),parameter :: nCoupling=2 !reza Now it is a variable ! number of possible solutions ! named variables for the state variable split integer(i4b),parameter :: nrgSplit=1 ! order in sequence for the energy operation @@ -295,9 +294,9 @@ subroutine opSplittin(& logical(lgt) :: doAdjustTemp ! flag to adjust temperature after the mass split logical(lgt) :: failedMinimumStep ! flag to denote failure of substepping for a given split integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - integer(i4b),parameter :: ida=1 - integer(i4b),parameter :: be=2 - integer(i4b) :: solver=ida ! be or ida + integer(i4b),parameter :: IDA=1 + integer(i4b),parameter :: BE=2 + integer(i4b) :: solver=IDA ! BE or IDA integer(i4b) :: nCoupling ! --------------------------------------------------------------------------------------- ! point to variables in the data structures @@ -364,7 +363,12 @@ subroutine opSplittin(& ! initialize error control err=0; message="opSplittin/" - nCoupling=solver ! reza + ! we just solve the fully coupled problem by ida + select case(solver) + case(BE); nCoupling = 2 + case(IDA); nCoupling = 1 + case default; err=20; message=trim(message)//'expect case to be IDA or BE'; return + end select ! ***** ! (0) PRELIMINARIES... @@ -770,7 +774,7 @@ subroutine opSplittin(& ! solve variable subset for one full time step select case(solver) - case(ida) + case(IDA) call varSubstepFida(& ! input: model control dt, & ! intent(inout) : time step (s) @@ -806,7 +810,7 @@ subroutine opSplittin(& reduceCoupledStep, & ! intent(out) : flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt err,cmessage) ! intent(out) : error code and error message - case(be) + case(BE) call varSubstep(& ! input: model control dt, & ! intent(inout) : time step (s) diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 30738ad6d..457310e30 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -473,8 +473,8 @@ subroutine sysSolvFida(& ! ------------------ h_init = 0 - atol = 1e-8 - rtol = 1e-8 + atol = 1e-5 + rtol = 1e-5 do tol_iter=1,5 From 74e2f1e0f3a2709841f37a59597862f1751b4cd2 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 14 Feb 2021 19:13:52 -0600 Subject: [PATCH 0030/1472] minor edit --- build/source/engine/sysSolvFida.f90 | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 457310e30..00c0d5fa6 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -216,7 +216,6 @@ subroutine sysSolvFida(& logical(lgt) :: feasible ! feasibility flag real(dp) :: resSinkNew(nState) ! additional terms in the residual vector real(dp) :: fluxVecNew(nState) ! new flux vector - ! input: reza : fida solver needs more data real(dp) :: t0 ! beginning of the current time step real(dp) :: tout ! end of the current time step real(dp) :: tret(1) @@ -459,15 +458,7 @@ subroutine sysSolvFida(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) -! call countDiscontinuity(& -! ! input -! stateVecTrial, & ! intent(in): model state vector (mixed units) -! diag_data, & ! intent(in): model diagnostic variables for a local HRU -! prog_data, & ! intent(in): model prognostic variables for a local HRU -! indx_data, & ! intent(in): indices defining model states and layers -! ! output -! numDiscon, & ! intent(out) -! err,message) ! intent(out): error control + !------------------- ! * solving F(y,y') = 0 by FIDA. Here, y is the state vector ! ------------------ @@ -548,8 +539,6 @@ subroutine sysSolvFida(& end do ! iteration over tolerances -! print *, 'rVec = ', rVec(:) -! stop 1 ! check if fida is successful if( tret(1) /= dt .or. .not.feasible )then @@ -558,7 +547,6 @@ subroutine sysSolvFida(& return endif -! write (2,*) rVec(indx_data%var(iLookINDEX%ixNrgOnly)%dat) if (compAverageFlux)then select case(ixQuadrature) From 851619443c1f77390e8bc1daf459174a586f1b78 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 14 Feb 2021 19:22:29 -0600 Subject: [PATCH 0031/1472] minor edit --- build/source/engine/sysSolvFida.f90 | 2 -- build/source/engine/varSubstepFida.f90 | 19 ------------------- 2 files changed, 21 deletions(-) diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 00c0d5fa6..9030de720 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -124,7 +124,6 @@ subroutine sysSolvFida(& stateVecPrime, & ! intent(out): updated state vector reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out): flag to denote that there was too much melt - niter, & ! intent(out): number of iterations taken err,message) ! intent(out): error code and error message ! --------------------------------------------------------------------------------------- ! structure allocations @@ -172,7 +171,6 @@ subroutine sysSolvFida(& real(dp),intent(out) :: stateVecPrime(:) ! trial state vector (mixed units) logical(lgt),intent(out) :: reduceCoupledStep ! flag to reduce the length of the coupled step logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that there was too much melt - integer(i4b),intent(out) :: niter ! number of iterations taken integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! ********************************************************************************************************************************************************* diff --git a/build/source/engine/varSubstepFida.f90 b/build/source/engine/varSubstepFida.f90 index 0d78d0577..2ed660718 100644 --- a/build/source/engine/varSubstepFida.f90 +++ b/build/source/engine/varSubstepFida.f90 @@ -193,7 +193,6 @@ subroutine varSubstepFida(& real(dp),parameter :: reduceMin=0.1_dp ! mimimum factor that time step is reduced real(dp),parameter :: increaseMax=4.0_dp ! maximum factor that time step is increased ! adaptive sub-stepping for the implicit solution - integer(i4b) :: niter ! number of iterations taken integer(i4b),parameter :: n_inc=5 ! minimum number of iterations to increase time step integer(i4b),parameter :: n_dec=15 ! maximum number of iterations to decrease time step real(dp),parameter :: F_inc = 1.25_dp ! factor used to increase time step @@ -342,7 +341,6 @@ subroutine varSubstepFida(& stateVecPrime, & ! intent(out): updated state vector reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt - niter, & ! intent(out): number of iterations taken err,cmessage) ! intent(out): error code and error message if(err/=0)then @@ -359,27 +357,12 @@ subroutine varSubstepFida(& ! identify failure failedSubstep = (err<0) - ! check - if(globalPrintFlag)then - print*, 'niter, failedSubstep, dtSubstep = ', niter, failedSubstep, dtSubstep - print*, trim(cmessage) - endif - ! reduce step based on failure if(failedSubstep)then err=0; message='varSubstepFida/' ! recover from failed convergence dtMultiplier = 0.5_dp ! system failure: step halving else - ! ** implicit Euler: adjust step length based on iteration count - if(nitern_dec)then - dtMultiplier = F_dec - else - dtMultiplier = 1._dp - endif - endif ! switch between failure and success ! check if we failed the substep @@ -422,8 +405,6 @@ subroutine varSubstepFida(& message=trim(message)//trim(cmessage) if(err>0) return endif - - ! if water balance error then reduce the length of the coupled step if(waterBalanceError)then From f443683878b8e07a823f281f6cb7882ffde20ef7 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 14 Feb 2021 19:40:04 -0600 Subject: [PATCH 0032/1472] heatCapVar flag deleted --- build/source/engine/eval8FidaJac.f90 | 47 +------------------------- build/source/engine/eval8summaFida.f90 | 2 -- build/source/engine/evalEqnsFida.f90 | 1 - build/source/engine/evalJacFida.f90 | 1 - build/source/engine/fidaSolver.f90 | 4 --- build/source/engine/fida_datatypes.f90 | 1 - build/source/engine/sysSolvFida.f90 | 2 -- 7 files changed, 1 insertion(+), 57 deletions(-) diff --git a/build/source/engine/eval8FidaJac.f90 b/build/source/engine/eval8FidaJac.f90 index 572230879..3c6d07f41 100644 --- a/build/source/engine/eval8FidaJac.f90 +++ b/build/source/engine/eval8FidaJac.f90 @@ -97,7 +97,6 @@ subroutine eval8FidaJac(& ixMatrix, & ! intent(in) computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution - heatCapVaries, & ! intent(in): flag to indicate if heat capacity varies in the current substep ! input: state vectors stateVec, & ! intent(in): model state vector stateVecPrime, & ! intent(in): derivative of model state vector @@ -135,7 +134,6 @@ subroutine eval8FidaJac(& integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - logical(lgt),intent(in) :: heatCapVaries ! input: state vectors real(dp),intent(in) :: stateVec(:) ! model state vector real(dp),intent(in) :: stateVecPrime(:) ! model state vector @@ -320,49 +318,7 @@ subroutine eval8FidaJac(& ! This occurred either at the call to eval8summaFida at the start of sysSolveFida ! or in the call to eval8summaFida in the previous iteration dt1 = 1.0 - if(heatCapVaries)then - call computJacobFidaCpVar(& - ! input: model control - cj, & ! intent(in) - dt1, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - (ixGroundwater==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow - ixMatrix, & ! intent(in): form of the Jacobian matrix - specificStorage, & ! intent(in): specific storage coefficient (m-1) - theta_sat, & ! intent(in): soil porosity (-) - ixRichards, & ! intent(in): choice of option for Richards' equation - ! input: data structures - indx_data, & ! intent(in): index data - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! input: state variables - mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) - mLayerTempPrime, & ! intent(in) - mLayerMatricHeadPrime, & ! intent(in) - mLayerMatricHeadLiqPrime, & ! intent(in) - mLayerVolFracWatTrial, & ! intent(in) - mLayerVolFracWatPrime, & ! intent(in) - scalarCanopyTempTrial, & ! intent(in) - scalarCanopyTempPrime, & ! intent(in) derivative value for temperature of the vegetation canopy (K) - scalarCanopyWatPrime, & ! intetn(in) - mLayerd2Theta_dTk2, & ! intent(in) - d2VolTot_d2Psi0, & ! intent(in) - dFracLiqSnow_dTk, & ! intent(in) - d2Theta_dTkCanopy2, & ! intent(in) - dFracLiqVeg_dTkCanopy, & ! intent(in) - ! input-output: Jacobian and its diagonal - dMat, & ! intent(inout): diagonal of the Jacobian matrix - Jac, & ! intent(out): Jacobian matrix - ! output: error control - err,cmessage) ! intent(out): error code and error message - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - else - call computJacobFida(& + call computJacobFida(& ! input: model control cj, & ! intent(in) dt1, & ! intent(in): length of the time step (seconds) @@ -401,7 +357,6 @@ subroutine eval8FidaJac(& ! output: error control err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - endif diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 431a7d022..3f0c01b85 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -100,7 +100,6 @@ subroutine eval8summaFida(& firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution - heatCapVaries, & ! intent(in): flag to indicate if heat capacity is constant in the current subset ! input: state vectors stateVec, & ! intent(in): model state vector stateVecPrime, & ! intent(in): derivative of model state vector @@ -174,7 +173,6 @@ subroutine eval8summaFida(& logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - logical(lgt),intent(in) :: heatCapVaries ! input: state vectors real(dp),intent(in) :: stateVec(:) ! model state vector real(dp),intent(in) :: stateVecPrime(:) ! model state vector diff --git a/build/source/engine/evalEqnsFida.f90 b/build/source/engine/evalEqnsFida.f90 index 9454a9cad..5236bb859 100644 --- a/build/source/engine/evalEqnsFida.f90 +++ b/build/source/engine/evalEqnsFida.f90 @@ -109,7 +109,6 @@ integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_d eqns_data%firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution - eqns_data%heatCapVaries, & ! intent(in): flag to indicate if heat capacity is constant in the current subset ! input: state vectors stateVec, & ! intent(in): model state vector stateVecPrime, & ! intent(in): model state vector diff --git a/build/source/engine/evalJacFida.f90 b/build/source/engine/evalJacFida.f90 index c2da52ca6..1f5fbea6f 100644 --- a/build/source/engine/evalJacFida.f90 +++ b/build/source/engine/evalJacFida.f90 @@ -98,7 +98,6 @@ integer(c_int) function evalJacFida(t, cj, sunvec_y, sunvec_yp, sunvec_r, & eqns_data%ixMatrix, & ! intent(in) eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution - eqns_data%heatCapVaries, & ! intent(in): flag to indicate if heat capacity varies in the current substep ! input: state vectors stateVec, & ! intent(in): model state vector stateVecPrime, & ! intent(in): model state vector diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index fb756b78c..e52b1cbdb 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -116,7 +116,6 @@ subroutine fidaSolver( & firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution - heatCapVaries, & ! intent(in): flag to indicate if heat capacity is constant in the current substep ! input: state vectors stateVecInit, & ! intent(in): initial state vector fScale, & ! intent(in): function scaling vector @@ -204,7 +203,6 @@ subroutine fidaSolver( & logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - logical(lgt),intent(in) :: heatCapVaries !flag to indicate if heat capacity is constant in the current substep ! input: state vectors real(dp),intent(in) :: stateVecInit(:) ! model state vector real(dp),intent(in) :: fScale(:) ! function scaling vector @@ -301,7 +299,6 @@ subroutine fidaSolver( & eqns_data%firstSplitOper = firstSplitOper eqns_data%computeVegFlux = computeVegFlux eqns_data%scalarSolution = scalarSolution - eqns_data%heatCapVaries = heatCapVaries allocate( eqns_data%atol(nState) ) eqns_data%atol = atol @@ -651,7 +648,6 @@ subroutine fidaSolver( & eqns_data%firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution - eqns_data%heatCapVaries, & ! intent(in): flag to indicate if heat capacity is constant in the current subset ! input: state vectors stateVec, & ! intent(in): model state vector stateVecPrime, & ! intent(in): model state vector diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index 899fefa01..054090583 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -31,7 +31,6 @@ module fida_datatypes logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution - logical(lgt) :: heatCapVaries real(dp), allocatable :: fScale(:) ! function scaling vector real(qp), allocatable :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(dp), allocatable :: dMat(:) diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 9030de720..d481156fc 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -196,7 +196,6 @@ subroutine sysSolvFida(& ! ------------------------------------------------------------------------------------------------------ logical(lgt),parameter :: forceFullMatrix=.true. ! flag to force the use of the full Jacobian matrix logical(lgt),parameter :: compAverageFlux=.true. - logical(lgt),parameter :: heatCapVaries=.false. integer(i4b) :: ixQuadrature=ixRectangular ! type of quadrature method to approximate average fluxes integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) type(var_dlength) :: flux_init ! model fluxes at the start of the time step @@ -490,7 +489,6 @@ subroutine sysSolvFida(& firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution - heatCapVaries, & ! intent(in): flag to indicate if heat capacity is constant in the current substep ! input: state vectors stateVecTrial, & ! intent(in): model state vector fScale, & ! intent(in): function scaling vector From afb5a2d31f78b0e7b76573f11b2cecf198359066 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 14 Feb 2021 19:59:05 -0600 Subject: [PATCH 0033/1472] firstOpSplit flag deleted from fidaSolver --- build/source/engine/eval8summaFida.f90 | 6 +----- build/source/engine/evalEqnsFida.f90 | 1 - build/source/engine/fidaSolver.f90 | 4 ---- build/source/engine/fida_datatypes.f90 | 1 - build/source/engine/sysSolvFida.f90 | 9 ++++----- 5 files changed, 5 insertions(+), 16 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 3f0c01b85..352997126 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -97,7 +97,6 @@ subroutine eval8summaFida(& nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors @@ -170,7 +169,6 @@ subroutine eval8summaFida(& integer,intent(in) :: nState ! total number of state variables logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors @@ -475,8 +473,6 @@ subroutine eval8summaFida(& ! save the number of flux calls per time step indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) + 1 - - ! compute the fluxes for a given state vector call computFlux(& @@ -486,7 +482,7 @@ subroutine eval8summaFida(& nLayers, & ! intent(in): total number of layers firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(inout): flag to denote the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + .true., & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution scalarSfcMeltPond/dt, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) diff --git a/build/source/engine/evalEqnsFida.f90 b/build/source/engine/evalEqnsFida.f90 index 5236bb859..5e37f8c22 100644 --- a/build/source/engine/evalEqnsFida.f90 +++ b/build/source/engine/evalEqnsFida.f90 @@ -106,7 +106,6 @@ integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_d eqns_data%nState, & ! intent(in): number of state variables in the current subset eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - eqns_data%firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index e52b1cbdb..ca05d8e25 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -113,7 +113,6 @@ subroutine fidaSolver( & ixQuadrature, & ! intent(in): type of quadrature method for approximating average flux firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors @@ -200,7 +199,6 @@ subroutine fidaSolver( & integer(i4b) :: ixQuadrature ! type of quadrature method for approximating average flux logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors @@ -296,7 +294,6 @@ subroutine fidaSolver( & eqns_data%ixQuadrature = ixQuadrature eqns_data%firstSubStep = firstSubStep eqns_data%firstFluxCall = firstFluxCall - eqns_data%firstSplitOper = firstSplitOper eqns_data%computeVegFlux = computeVegFlux eqns_data%scalarSolution = scalarSolution @@ -645,7 +642,6 @@ subroutine fidaSolver( & eqns_data%nState, & ! intent(in): number of state variables in the current subset eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - eqns_data%firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index 054090583..43388f1ea 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -28,7 +28,6 @@ module fida_datatypes logical(lgt) :: startQuadrature logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution real(dp), allocatable :: fScale(:) ! function scaling vector diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index d481156fc..44c41237a 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -474,10 +474,10 @@ subroutine sysSolvFida(& mLayerCmpress_sum(:) = 0._dp call fidaSolver(& - dt, & ! current time step(entire) - h_init, & ! initial stepsize - atol, & ! absolute telerance - rtol, & ! relative tolerance + dt, & ! intent (in) current time step(entire) + h_init, & ! intent (in) initial stepsize + atol, & ! intent (in) absolute telerance + rtol, & ! intent (in) relative tolerance nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): number of layers @@ -486,7 +486,6 @@ subroutine sysSolvFida(& ixQuadrature, & ! intent(in) firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors From 9161f7f3a9246b8aee284f7a726be4a11ab6cba7 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 14 Feb 2021 20:04:09 -0600 Subject: [PATCH 0034/1472] fscale deleted --- build/source/engine/eval8summaFida.f90 | 1 - build/source/engine/fidaSolver.f90 | 7 ------- build/source/engine/fida_datatypes.f90 | 1 - build/source/engine/sysSolvFida.f90 | 1 - 4 files changed, 10 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 352997126..aa0ac6dec 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -253,7 +253,6 @@ subroutine eval8summaFida(& integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) real(dp) :: xMin,xMax ! minimum and maximum values for water content real(dp),parameter :: canopyTempMax=500._dp ! expected maximum value for the canopy temperature (K) - real(dp),dimension(nState) :: rVecScaled ! scaled residual vector character(LEN=256) :: cmessage ! error message of downwind routine real(qp) :: heatCapVegTrial real(qp) :: scalarCanopyEnthalpyPrime diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index ca05d8e25..eaa692be7 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -117,7 +117,6 @@ subroutine fidaSolver( & scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVecInit, & ! intent(in): initial state vector - fScale, & ! intent(in): function scaling vector sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout) numDiscon, & ! intent(in) @@ -203,7 +202,6 @@ subroutine fidaSolver( & logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors real(dp),intent(in) :: stateVecInit(:) ! model state vector - real(dp),intent(in) :: fScale(:) ! function scaling vector real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(dp), intent(inout) :: dMat(:) integer(i4b), intent(in) :: numDiscon @@ -302,10 +300,6 @@ subroutine fidaSolver( & allocate( eqns_data%rtol(nState) ) eqns_data%rtol = rtol - - allocate( eqns_data%fScale(nState) ) - eqns_data%fScale = fScale - allocate( eqns_data%sMul(nState) ) eqns_data%sMul = sMul @@ -761,7 +755,6 @@ subroutine fidaSolver( & ! free memory - deallocate(eqns_data%fScale) deallocate(eqns_data%sMul) deallocate(eqns_data%dMat) deallocate(eqns_data%dBaseflow_dMatric) diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index 43388f1ea..9a4dcb64d 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -30,7 +30,6 @@ module fida_datatypes logical(lgt) :: firstFluxCall ! flag to indicate if we are processing the first flux call logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution - real(dp), allocatable :: fScale(:) ! function scaling vector real(qp), allocatable :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(dp), allocatable :: dMat(:) type(zLookup) :: lookup_data ! lookup tables diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 44c41237a..9aad32f36 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -490,7 +490,6 @@ subroutine sysSolvFida(& scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVecTrial, & ! intent(in): model state vector - fScale, & ! intent(in): function scaling vector sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout) numDiscon, & ! intent(in) From fd48060f1e5f37b8e646b03c06f3c28c5e69d14d Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 14 Feb 2021 20:18:43 -0600 Subject: [PATCH 0035/1472] numDisc deleted --- build/source/engine/fidaSolver.f90 | 13 ++----------- build/source/engine/sysSolvFida.f90 | 14 +++++--------- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index eaa692be7..d73770752 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -102,7 +102,6 @@ module fidaSolver_module subroutine fidaSolver( & dt, & ! end of the current time step - h_init, & ! intent(in) initial stepsize atol, & ! absolute telerance rtol, & ! relative tolerance nSnow, & ! intent(in): number of snow layers @@ -119,7 +118,6 @@ subroutine fidaSolver( & stateVecInit, & ! intent(in): initial state vector sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout) - numDiscon, & ! intent(in) ! input: data structures lookup_data, & type_data, & ! intent(in): type of vegetation and soil @@ -187,7 +185,6 @@ subroutine fidaSolver( & ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control real(qp),intent(in) :: dt - real(qp),intent(in) :: h_init real(qp),intent(inout) :: atol(:) real(qp),intent(inout) :: rtol(:) integer(i4b),intent(in) :: nSnow ! number of snow layers @@ -204,7 +201,6 @@ subroutine fidaSolver( & real(dp),intent(in) :: stateVecInit(:) ! model state vector real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(dp), intent(inout) :: dMat(:) - integer(i4b), intent(in) :: numDiscon ! input: data structures type(zLookup),intent(in) :: lookup_data ! lookup tables type(var_i), intent(in) :: type_data ! type of vegetation and soil @@ -267,6 +263,7 @@ subroutine fidaSolver( & integer(i4b) :: iVar logical(lgt) :: startQuadrature real(dp) :: mLayerMatricHeadLiqPrev(nSoil) + real(qp) :: h_init globalVars: associate(& nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): @@ -438,13 +435,6 @@ subroutine fidaSolver( & stop 1 end if - ! specify the root function -! retval = FIDARootInit(ida_mem, numDiscon, c_funloc(findDiscontinuity)) -! if (retval /= 0) then -! print *, 'Error in FIDARootInit, retval = ', retval, '; halting' -! stop 1 -! end if - ! define the form of the matrix select case(ixMatrix) case(ixBandMatrix) @@ -582,6 +572,7 @@ subroutine fidaSolver( & ! end if ! Set initial stepsize + h_init = 0 retval = FIDASetInitStep(ida_mem, h_init) if (retval /= 0) then print *, 'Error in FIDASetInitStep, retval = ', retval, '; halting' diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 9aad32f36..e578f8ef9 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -225,7 +225,6 @@ subroutine sysSolvFida(& type(var_dlength) :: flux_sum real(qp) :: stepsize_past integer(i4b) :: tol_iter - integer(i4b) :: numDiscon real(dp), allocatable :: mLayerCmpress_sum(:) real(dp),dimension(indx_data%var(iLookINDEX%nLayers)%dat(1)) :: mLayerEnthalpy ! enthalpy of each snow+soil layer (J m-3) @@ -459,8 +458,7 @@ subroutine sysSolvFida(& !------------------- ! * solving F(y,y') = 0 by FIDA. Here, y is the state vector ! ------------------ - - h_init = 0 + atol = 1e-5 rtol = 1e-5 @@ -474,10 +472,9 @@ subroutine sysSolvFida(& mLayerCmpress_sum(:) = 0._dp call fidaSolver(& - dt, & ! intent (in) current time step(entire) - h_init, & ! intent (in) initial stepsize - atol, & ! intent (in) absolute telerance - rtol, & ! intent (in) relative tolerance + dt, & ! intent (in) current time step(entire) + atol, & ! intent (in) absolute telerance + rtol, & ! intent (in) relative tolerance nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): number of layers @@ -490,9 +487,8 @@ subroutine sysSolvFida(& scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVecTrial, & ! intent(in): model state vector - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout) - numDiscon, & ! intent(in) ! input: data structures lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil From 9565b460d70ed946477a28f9e4bcc18b437f54e9 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 14 Feb 2021 21:21:05 -0600 Subject: [PATCH 0036/1472] more edits --- build/source/engine/fidaSolver.f90 | 14 +++---------- build/source/engine/sysSolvFida.f90 | 31 +++++++++++++++-------------- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index d73770752..82bc45ad9 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -140,7 +140,6 @@ subroutine fidaSolver( & ! output tret, & ! time which the solution is returned, if successfull tret = tend dt_last, & - t_last, & stepsize_past, & stateVec, & ! intent(out): model state vector stateVecPrime, & ! intent(out): derivative of model state vector @@ -231,7 +230,6 @@ subroutine fidaSolver( & character(*),intent(out) :: message ! error message real(qp),intent(out) :: tret(1) real(qp),intent(out) :: dt_last(1) - real(qp),intent(out) :: t_last(1) real(qp),intent(out) :: stepsize_past logical(lgt) :: scalling_on @@ -616,11 +614,11 @@ subroutine fidaSolver( & stop 1 end if - ! compute the flux and the residual vector for a given state vector + ! compute the flux and the residual vector for a given state vector call eval8summaFida(& ! input: model control - stepsize_cur(1), & - eqns_data%dt, & + stepsize_cur(1), & ! intent(in): current stepsize + eqns_data%dt, & ! intent(in): data step eqns_data%nSnow, & ! intent(in): number of snow layers eqns_data%nSoil, & ! intent(in): number of soil layers eqns_data%nLayers, & ! intent(in): number of layers @@ -738,12 +736,6 @@ subroutine fidaSolver( & stop 1 end if - retval = FIDAGetCurrentTime(ida_mem, t_last) - if (retval /= 0) then - print *, 'Error in FIDAGetCurrentTime, retval = ', retval, '; halting' - stop 1 - end if - ! free memory deallocate(eqns_data%sMul) diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index e578f8ef9..776940b96 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -115,7 +115,7 @@ subroutine sysSolvFida(& flux_temp, & ! intent(inout): model fluxes for a local HRU bvar_data, & ! intent(in): model variables for the local basin model_decisions, & ! intent(in): model decisions - stateVecInit, & ! intent(inout): initial state vector + stateVecInit, & ! intent(in): initial state vector ! output deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) @@ -216,11 +216,10 @@ subroutine sysSolvFida(& real(dp) :: t0 ! beginning of the current time step real(dp) :: tout ! end of the current time step real(dp) :: tret(1) - real(dp) :: t_last(1) real(dp) :: dt_last(1) real(dp) :: atol(nState) ! absolute telerance real(dp) :: rtol(nState) ! relative tolerance - integer(c_long) :: nState8 ! just to match nState to integer8 + integer(c_long) :: nState8 ! just to match nState to integer8 real(qp) :: h_init type(var_dlength) :: flux_sum real(qp) :: stepsize_past @@ -454,14 +453,15 @@ subroutine sysSolvFida(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + ! just for experiment + atol = 1e-5 + rtol = 1e-5 + !------------------- ! * solving F(y,y') = 0 by FIDA. Here, y is the state vector ! ------------------ - atol = 1e-5 - rtol = 1e-5 - do tol_iter=1,5 ! initialize flux_sum @@ -469,6 +469,7 @@ subroutine sysSolvFida(& flux_sum%var(iVar)%dat(:) = 0._dp end do + ! initialize sum of compression of the soil matrix mLayerCmpress_sum(:) = 0._dp call fidaSolver(& @@ -486,7 +487,7 @@ subroutine sysSolvFida(& computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors - stateVecTrial, & ! intent(in): model state vector + stateVecTrial, & ! intent(in): model state vector at the beginning of the data time step sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout) ! input: data structures @@ -507,14 +508,13 @@ subroutine sysSolvFida(& ! input-output: baseflow ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - mLayerCmpress_sum, & ! output - tret, & ! time which the solution is returned, if successfull tret = tout - dt_last, & ! last stepsize - t_last, & - stepsize_past, & - stateVecNew, & ! intent(out): model state vector - stateVecPrime, & ! intent(out): derivative of model state vector + mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix + tret, & ! intent(out): time which the solution is returned, if successfull tret = tout + dt_last, & ! intent(out): last stepsize + stepsize_past, & ! intent(out): one stepsize before the last one + stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step + stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step fluxVecNew, & ! intent(out): flux vector resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equation rVec, & @@ -551,7 +551,7 @@ subroutine sysSolvFida(& flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) + flux_init%var(iVar)%dat(:) * (dt_last(1) + stepsize_past) & + flux_temp%var(iVar)%dat(:) * dt_last(1) ) / (2.0*dt) end do - ! check + ! check case default; err=20; message=trim(message)//'expect case to be ixRecangular, ixTrapezoidal'; return end select @@ -562,6 +562,7 @@ subroutine sysSolvFida(& diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sum(diag_data%var(iLookDIAG%mLayerCompress)%dat(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water + ! stateVecTrial = stateVecNew From 2c9043b7212e904dcb41fdedb4ca7b6cd7d37cdf Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 14 Feb 2021 21:30:28 -0600 Subject: [PATCH 0037/1472] nState8 in fidaSolver --- build/source/engine/fidaSolver.f90 | 8 +++++--- build/source/engine/sysSolvFida.f90 | 8 +------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 82bc45ad9..63bcff0d4 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -107,7 +107,7 @@ subroutine fidaSolver( & nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers - nState, & ! intent(in): total number of state variables + nStat, & ! intent(in): total number of state variables ixMatrix, & ! intent(in): type of matrix (dense or banded) ixQuadrature, & ! intent(in): type of quadrature method for approximating average flux firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step @@ -189,7 +189,7 @@ subroutine fidaSolver( & integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers - integer(c_long),intent(in) :: nState ! total number of state variables + integer(i4b),intent(in) :: nStat ! total number of state variables integer(i4b) :: ixMatrix ! form of matrix (dense or banded) integer(i4b) :: ixQuadrature ! type of quadrature method for approximating average flux logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step @@ -262,6 +262,7 @@ subroutine fidaSolver( & logical(lgt) :: startQuadrature real(dp) :: mLayerMatricHeadLiqPrev(nSoil) real(qp) :: h_init + integer(c_long) :: nState ! total number of state variables globalVars: associate(& nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): @@ -276,7 +277,7 @@ subroutine fidaSolver( & !======= Internals ============ - + nState = nStat ! fill eqns_data which will be required later to call eval8summaFida eqns_data%dt = dt eqns_data%nSnow = nSnow @@ -716,6 +717,7 @@ subroutine fidaSolver( & !****************************** End of Main Solver *************************************** + ! copy the output data firstFluxCall = eqns_data%firstFluxCall fluxVec = eqns_data%fluxVec diag_data = eqns_data%diag_data diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 776940b96..2fff6cbd8 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -219,16 +219,10 @@ subroutine sysSolvFida(& real(dp) :: dt_last(1) real(dp) :: atol(nState) ! absolute telerance real(dp) :: rtol(nState) ! relative tolerance - integer(c_long) :: nState8 ! just to match nState to integer8 - real(qp) :: h_init type(var_dlength) :: flux_sum real(qp) :: stepsize_past integer(i4b) :: tol_iter real(dp), allocatable :: mLayerCmpress_sum(:) - real(dp),dimension(indx_data%var(iLookINDEX%nLayers)%dat(1)) :: mLayerEnthalpy ! enthalpy of each snow+soil layer (J m-3) - - - nState8 = nState ! --------------------------------------------------------------------------------------- @@ -479,7 +473,7 @@ subroutine sysSolvFida(& nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): number of layers - nState8, & ! intent(in): number of state variables in the current subset + nState, & ! intent(in): number of state variables in the current subset ixMatrix, & ! intent(in): type of matrix (dense or banded) ixQuadrature, & ! intent(in) firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step From 08370621aa9dfb07030a52f9dc36552f6ac518a4 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 14 Feb 2021 21:45:16 -0600 Subject: [PATCH 0038/1472] fluxVec and resSink not output from fidaSolver --- build/source/engine/fidaSolver.f90 | 13 ++----------- build/source/engine/sysSolvFida.f90 | 2 -- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 63bcff0d4..1bb55bef6 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -143,8 +143,6 @@ subroutine fidaSolver( & stepsize_past, & stateVec, & ! intent(out): model state vector stateVecPrime, & ! intent(out): derivative of model state vector - fluxVec, & ! intent(out): flux vector - resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation rVec, & err,message & ! intent(out): error control ) @@ -222,8 +220,6 @@ subroutine fidaSolver( & ! output: flux and residual vectors real(dp),intent(inout) :: stateVec(:) ! model state vector real(dp),intent(inout) :: stateVecPrime(:) ! model state vector - real(dp),intent(out) :: fluxVec(:) ! flux vector - real(dp),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation real(dp),intent(out) :: rVec(:) ! output: error control integer(i4b),intent(out) :: err ! error code @@ -373,11 +369,8 @@ subroutine fidaSolver( & allocate( eqns_data%mLayerEnthalpyPrev(nLayers) ) allocate( eqns_data%fluxVec(nState) ) - eqns_data%fluxVec = fluxVec - allocate( eqns_data%resSink(nState) ) - eqns_data%resSink = resSink - + allocate( eqns_data%resVec(nState) ) @@ -718,15 +711,13 @@ subroutine fidaSolver( & !****************************** End of Main Solver *************************************** ! copy the output data - firstFluxCall = eqns_data%firstFluxCall - fluxVec = eqns_data%fluxVec + firstFluxCall = eqns_data%firstFluxCall diag_data = eqns_data%diag_data flux_temp = eqns_data%flux_temp flux_data = eqns_data%flux_data deriv_data = eqns_data%deriv_data dBaseflow_dMatric = eqns_data%dBaseflow_dMatric ixSaturation = eqns_data%ixSaturation - resSink = eqns_data%resSink stepsize_past = eqns_data%stepsize_past err = eqns_data%err message = eqns_data%message diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 2fff6cbd8..1ea4eab4e 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -509,8 +509,6 @@ subroutine sysSolvFida(& stepsize_past, & ! intent(out): one stepsize before the last one stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step - fluxVecNew, & ! intent(out): flux vector - resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equation rVec, & err,cmessage) ! intent(out): error control ! if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) From 5a1182c55cba11f4f0f6829ae5b7a7917e4da6b7 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 14 Feb 2021 21:55:14 -0600 Subject: [PATCH 0039/1472] some edits --- build/source/engine/fidaSolver.f90 | 7 +------ build/source/engine/sysSolvFida.f90 | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 1bb55bef6..fa520c6ba 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -143,7 +143,6 @@ subroutine fidaSolver( & stepsize_past, & stateVec, & ! intent(out): model state vector stateVecPrime, & ! intent(out): derivative of model state vector - rVec, & err,message & ! intent(out): error control ) @@ -220,7 +219,6 @@ subroutine fidaSolver( & ! output: flux and residual vectors real(dp),intent(inout) :: stateVec(:) ! model state vector real(dp),intent(inout) :: stateVecPrime(:) ! model state vector - real(dp),intent(out) :: rVec(:) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -259,6 +257,7 @@ subroutine fidaSolver( & real(dp) :: mLayerMatricHeadLiqPrev(nSoil) real(qp) :: h_init integer(c_long) :: nState ! total number of state variables + real(dp) :: rVec(nStat) globalVars: associate(& nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): @@ -370,9 +369,6 @@ subroutine fidaSolver( & allocate( eqns_data%fluxVec(nState) ) allocate( eqns_data%resSink(nState) ) - - - allocate( eqns_data%resVec(nState) ) eqns_data%err = err eqns_data%message = message @@ -740,7 +736,6 @@ subroutine fidaSolver( & deallocate(eqns_data%mLayerMatricHeadPrev) deallocate( eqns_data%fluxVec ) deallocate( eqns_data%resSink ) - deallocate( eqns_data%resVec ) deallocate( eqns_data%mLayerVolFracWatTrial ) deallocate( eqns_data%mLayerVolFracWatPrev ) deallocate( eqns_data%mLayerVolFracIceTrial ) diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 1ea4eab4e..43ed474a5 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -509,7 +509,6 @@ subroutine sysSolvFida(& stepsize_past, & ! intent(out): one stepsize before the last one stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step - rVec, & err,cmessage) ! intent(out): error control ! if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) if (tret(1) == dt .and. err == 0)then From 02ec870d27f0e5c8e352722ad698969fc873205a Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 14 Feb 2021 22:16:06 -0600 Subject: [PATCH 0040/1472] idaSucceeds flag --- build/source/engine/fidaSolver.f90 | 23 ++++++++++++++--------- build/source/engine/sysSolvFida.f90 | 10 +++++----- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index fa520c6ba..87b3e88c4 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -136,9 +136,9 @@ subroutine fidaSolver( & ! input-output: baseflow ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - mLayerCmpress_sum, & ! output - tret, & ! time which the solution is returned, if successfull tret = tend + idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step + mLayerCmpress_sum, & dt_last, & stepsize_past, & stateVec, & ! intent(out): model state vector @@ -222,7 +222,7 @@ subroutine fidaSolver( & ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message - real(qp),intent(out) :: tret(1) + logical(lgt),intent(out) :: idaSucceeds real(qp),intent(out) :: dt_last(1) real(qp),intent(out) :: stepsize_past logical(lgt) :: scalling_on @@ -258,6 +258,7 @@ subroutine fidaSolver( & real(qp) :: h_init integer(c_long) :: nState ! total number of state variables real(dp) :: rVec(nStat) + real(qp) :: tret(1) globalVars: associate(& nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): @@ -273,6 +274,7 @@ subroutine fidaSolver( & nState = nStat + idaSucceeds = .false. ! fill eqns_data which will be required later to call eval8summaFida eqns_data%dt = dt eqns_data%nSnow = nSnow @@ -631,15 +633,15 @@ subroutine fidaSolver( & eqns_data%bvar_data, & ! intent(in): average model variables for the entire basin eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU ! input-output: data structures - eqns_data%indx_data, & ! intent(inou): index data + eqns_data%indx_data, & ! intent(inou): index data eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow - eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian - eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + eqns_data%ixSaturation, & ! intent(inout):index of the lowest saturated layer (NOTE: only computed on the first iteration) + eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian + eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) eqns_data%scalarCanopyIceTrial, & eqns_data%scalarCanopyIcePrev, & eqns_data%scalarCanopyEnthalpyTrial,& ! intent(in): trial enthalpy of the vegetation canopy (J m-3) @@ -716,7 +718,10 @@ subroutine fidaSolver( & ixSaturation = eqns_data%ixSaturation stepsize_past = eqns_data%stepsize_past err = eqns_data%err - message = eqns_data%message + message = eqns_data%message + if( tret(1) == dt .and. feasible)then + idaSucceeds = .true. + endif retval = FIDAGetLastStep(ida_mem, dt_last) diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 43ed474a5..614c81d9d 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -209,7 +209,6 @@ subroutine sysSolvFida(& real(qp) :: rVec(nState) ! NOTE: qp ! residual vector real(dp) :: rAdd(nState) ! additional terms in the residual vector real(dp) :: fOld ! function values (-); NOTE: dimensionless because scaled - logical(lgt) :: converged ! convergence flag logical(lgt) :: feasible ! feasibility flag real(dp) :: resSinkNew(nState) ! additional terms in the residual vector real(dp) :: fluxVecNew(nState) ! new flux vector @@ -223,6 +222,7 @@ subroutine sysSolvFida(& real(qp) :: stepsize_past integer(i4b) :: tol_iter real(dp), allocatable :: mLayerCmpress_sum(:) + logical(lgt) :: idaSucceeds ! --------------------------------------------------------------------------------------- @@ -503,15 +503,15 @@ subroutine sysSolvFida(& ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) ! output + idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix - tret, & ! intent(out): time which the solution is returned, if successfull tret = tout dt_last, & ! intent(out): last stepsize stepsize_past, & ! intent(out): one stepsize before the last one stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step err,cmessage) ! intent(out): error control ! if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - if (tret(1) == dt .and. err == 0)then + if (idaSucceeds)then exit else atol = atol * 0.1 @@ -522,7 +522,7 @@ subroutine sysSolvFida(& ! check if fida is successful - if( tret(1) /= dt .or. .not.feasible )then + if( .not.idaSucceeds )then message=trim(message)//'fida not successful' reduceCoupledStep = .true. return @@ -532,7 +532,7 @@ subroutine sysSolvFida(& if (compAverageFlux)then select case(ixQuadrature) case(ixRectangular) - ! add the last part of the integral, then divide by dt. Now we have average flux + ! divide by dt. Now we have average flux do iVar=1,size(flux_meta) flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / dt end do From 94f408ba6338d014b025ffc6f7c12c23a6ded8c9 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 14 Feb 2021 22:34:44 -0600 Subject: [PATCH 0041/1472] some edit --- build/source/engine/fidaSolver.f90 | 20 ++++++-------------- build/source/engine/sysSolvFida.f90 | 4 ++-- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 87b3e88c4..b619dd684 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -249,7 +249,6 @@ subroutine fidaSolver( & integer(i4b) :: iLayer ! index of layer in the snow+soil domain integer(i4b) :: iState ! index of model state real(dp) :: idenIW - real(c_double) :: stepsize_cur(1) integer(i4b),parameter :: ixRectangular=1 ! reza: should put them in a data type later integer(i4b),parameter :: ixTrapezoidal=2 integer(i4b) :: iVar @@ -600,7 +599,7 @@ subroutine fidaSolver( & retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) ! get the last stepsize - retval = FIDAGetLastStep(ida_mem, stepsize_cur) + retval = FIDAGetLastStep(ida_mem, dt_last) if (retval /= 0) then print *, 'Error in FIDAGetLastStep, retval = ', retval, '; halting' stop 1 @@ -609,7 +608,7 @@ subroutine fidaSolver( & ! compute the flux and the residual vector for a given state vector call eval8summaFida(& ! input: model control - stepsize_cur(1), & ! intent(in): current stepsize + dt_last(1), & ! intent(in): current stepsize eqns_data%dt, & ! intent(in): data step eqns_data%nSnow, & ! intent(in): number of snow layers eqns_data%nSoil, & ! intent(in): number of soil layers @@ -669,24 +668,24 @@ subroutine fidaSolver( & ! sum of flux case(ixRectangular) do iVar=1,size(flux_meta) - flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * stepsize_cur(1) + flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * dt_last(1) end do case(ixTrapezoidal) if(startQuadrature)then do iVar=1,size(flux_meta) - flux_sum%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) * stepsize_cur(1) + flux_sum%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) * dt_last(1) end do startQuadrature = .false. else do iVar=1,size(flux_meta) flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:) & - * ( stepsize_past + stepsize_cur(1) ) + * ( stepsize_past + dt_last(1) ) end do endif do iVar=1,size(flux_meta) flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) end do - stepsize_past = stepsize_cur(1) + stepsize_past = dt_last(1) case default; err=20; message=trim(message)//'expect case to be ixRecangular, ixTrapezoidal'; return end select @@ -722,13 +721,6 @@ subroutine fidaSolver( & if( tret(1) == dt .and. feasible)then idaSucceeds = .true. endif - - - retval = FIDAGetLastStep(ida_mem, dt_last) - if (retval /= 0) then - print *, 'Error in FIDAGetLastStep, retval = ', retval, '; halting' - stop 1 - end if ! free memory diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 614c81d9d..21cf991ee 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -448,8 +448,8 @@ subroutine sysSolvFida(& if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) ! just for experiment - atol = 1e-5 - rtol = 1e-5 + atol = 1e-6 + rtol = 1e-6 !------------------- From 2654453cb4a716d7df2fb753c5290ef87a4e07d4 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 14 Feb 2021 22:52:19 -0600 Subject: [PATCH 0042/1472] more edit --- build/source/engine/fidaSolver.f90 | 10 +++++----- build/source/engine/sysSolvFida.f90 | 14 ++++++-------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index b619dd684..95f8da318 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -140,7 +140,7 @@ subroutine fidaSolver( & idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step mLayerCmpress_sum, & dt_last, & - stepsize_past, & + dt_past, & stateVec, & ! intent(out): model state vector stateVecPrime, & ! intent(out): derivative of model state vector err,message & ! intent(out): error control @@ -224,7 +224,7 @@ subroutine fidaSolver( & character(*),intent(out) :: message ! error message logical(lgt),intent(out) :: idaSucceeds real(qp),intent(out) :: dt_last(1) - real(qp),intent(out) :: stepsize_past + real(qp),intent(out) :: dt_past logical(lgt) :: scalling_on ! -------------------------------------------------------------------------------------------------------------------------------- @@ -679,13 +679,13 @@ subroutine fidaSolver( & else do iVar=1,size(flux_meta) flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:) & - * ( stepsize_past + dt_last(1) ) + * ( dt_past + dt_last(1) ) end do endif do iVar=1,size(flux_meta) flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) end do - stepsize_past = dt_last(1) + dt_past = dt_last(1) case default; err=20; message=trim(message)//'expect case to be ixRecangular, ixTrapezoidal'; return end select @@ -715,7 +715,7 @@ subroutine fidaSolver( & deriv_data = eqns_data%deriv_data dBaseflow_dMatric = eqns_data%dBaseflow_dMatric ixSaturation = eqns_data%ixSaturation - stepsize_past = eqns_data%stepsize_past + dt_past = eqns_data%stepsize_past err = eqns_data%err message = eqns_data%message if( tret(1) == dt .and. feasible)then diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 21cf991ee..dffd8e9a0 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -219,7 +219,7 @@ subroutine sysSolvFida(& real(dp) :: atol(nState) ! absolute telerance real(dp) :: rtol(nState) ! relative tolerance type(var_dlength) :: flux_sum - real(qp) :: stepsize_past + real(qp) :: dt_past integer(i4b) :: tol_iter real(dp), allocatable :: mLayerCmpress_sum(:) logical(lgt) :: idaSucceeds @@ -431,9 +431,8 @@ subroutine sysSolvFida(& err=-20; return ! negative error code to denote a warning endif endif - - - ! get absolute tolerances vector + + ! get tolerance vectors call popTolFida(& ! input nState, & ! intent(in): number of desired state variables @@ -451,7 +450,6 @@ subroutine sysSolvFida(& atol = 1e-6 rtol = 1e-6 - !------------------- ! * solving F(y,y') = 0 by FIDA. Here, y is the state vector ! ------------------ @@ -506,7 +504,7 @@ subroutine sysSolvFida(& idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix dt_last, & ! intent(out): last stepsize - stepsize_past, & ! intent(out): one stepsize before the last one + dt_past, & ! intent(out): one stepsize before the last one stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step err,cmessage) ! intent(out): error control @@ -539,7 +537,7 @@ subroutine sysSolvFida(& case(ixTrapezoidal) ! add the last part of the integral, then divide by dt. Now we have average flux do iVar=1,size(flux_meta) - flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) + flux_init%var(iVar)%dat(:) * (dt_last(1) + stepsize_past) & + flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) + flux_init%var(iVar)%dat(:) * (dt_last(1) + dt_past) & + flux_temp%var(iVar)%dat(:) * dt_last(1) ) / (2.0*dt) end do ! check @@ -553,7 +551,7 @@ subroutine sysSolvFida(& diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sum(diag_data%var(iLookDIAG%mLayerCompress)%dat(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water - ! + ! save the computed solution stateVecTrial = stateVecNew From dd86ab0b84cebb92db0565773dc468af5a816cff Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Mon, 15 Feb 2021 00:58:02 -0600 Subject: [PATCH 0043/1472] firstFluxCall always flase in fidaSolver --- build/source/engine/eval8summaFida.f90 | 5 ++--- build/source/engine/evalEqnsFida.f90 | 3 +-- build/source/engine/fidaSolver.f90 | 19 +++++++------------ build/source/engine/fida_datatypes.f90 | 1 - build/source/engine/sysSolvFida.f90 | 17 ++++++++--------- 5 files changed, 18 insertions(+), 27 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index aa0ac6dec..10a2d578a 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -96,7 +96,6 @@ subroutine eval8summaFida(& nLayers, & ! intent(in): total number of layers nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors @@ -168,7 +167,6 @@ subroutine eval8summaFida(& integer(i4b),intent(in) :: nLayers ! total number of layers integer,intent(in) :: nState ! total number of state variables logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors @@ -256,6 +254,7 @@ subroutine eval8summaFida(& character(LEN=256) :: cmessage ! error message of downwind routine real(qp) :: heatCapVegTrial real(qp) :: scalarCanopyEnthalpyPrime + logical(lgt) :: firstFluxCall ! -------------------------------------------------------------------------------------------------------------------------------- @@ -472,7 +471,7 @@ subroutine eval8summaFida(& ! save the number of flux calls per time step indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) + 1 - + firstFluxCall = .false. ! compute the fluxes for a given state vector call computFlux(& ! input-output: model control diff --git a/build/source/engine/evalEqnsFida.f90 b/build/source/engine/evalEqnsFida.f90 index 5e37f8c22..6cf7f33ba 100644 --- a/build/source/engine/evalEqnsFida.f90 +++ b/build/source/engine/evalEqnsFida.f90 @@ -105,7 +105,6 @@ integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_d eqns_data%nLayers, & ! intent(in): number of layers eqns_data%nState, & ! intent(in): number of state variables in the current subset eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors @@ -155,7 +154,7 @@ integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_d if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif - if(.not.feasible)then; eqns_data%message=trim(eqns_data%message)//'state vector not feasible'; ierr = 1; return; endif + if(.not.feasible)then; eqns_data%message=trim(eqns_data%message)//'state vector not feasible'; ierr = -1; return; endif ! return success ierr = 0 diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 95f8da318..d4f7745a4 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -101,9 +101,9 @@ module fidaSolver_module ! ------------------ subroutine fidaSolver( & - dt, & ! end of the current time step - atol, & ! absolute telerance - rtol, & ! relative tolerance + dt, & ! intent(in): data time step + atol, & ! intent(in): absolute telerance + rtol, & ! intent(in): relative tolerance nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers @@ -111,7 +111,6 @@ subroutine fidaSolver( & ixMatrix, & ! intent(in): type of matrix (dense or banded) ixQuadrature, & ! intent(in): type of quadrature method for approximating average flux firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors @@ -127,9 +126,9 @@ subroutine fidaSolver( & bvar_data, & ! intent(in): average model variables for the entire basin prog_data, & ! intent(in): model prognostic variables for a local HRU ! input-output: data structures - indx_data, & ! intent(in): index data + indx_data, & ! intent(in): index data diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_temp, & ! intent(inout) + flux_temp, & ! intent(inout): model fluxes for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU flux_sum, & ! intent(inout) deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables @@ -190,7 +189,6 @@ subroutine fidaSolver( & integer(i4b) :: ixMatrix ! form of matrix (dense or banded) integer(i4b) :: ixQuadrature ! type of quadrature method for approximating average flux logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors @@ -282,8 +280,7 @@ subroutine fidaSolver( & eqns_data%nState = nState eqns_data%ixMatrix = ixMatrix eqns_data%ixQuadrature = ixQuadrature - eqns_data%firstSubStep = firstSubStep - eqns_data%firstFluxCall = firstFluxCall + eqns_data%firstSubStep = firstSubStep eqns_data%computeVegFlux = computeVegFlux eqns_data%scalarSolution = scalarSolution @@ -615,7 +612,6 @@ subroutine fidaSolver( & eqns_data%nLayers, & ! intent(in): number of layers eqns_data%nState, & ! intent(in): number of state variables in the current subset eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors @@ -707,8 +703,7 @@ subroutine fidaSolver( & !****************************** End of Main Solver *************************************** - ! copy the output data - firstFluxCall = eqns_data%firstFluxCall + ! copy the output data diag_data = eqns_data%diag_data flux_temp = eqns_data%flux_temp flux_data = eqns_data%flux_data diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index 9a4dcb64d..22e00dd6b 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -27,7 +27,6 @@ module fida_datatypes integer(i4b) :: ixQuadrature ! type of quadrature method for approximating average flux logical(lgt) :: startQuadrature logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt) :: firstFluxCall ! flag to indicate if we are processing the first flux call logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution real(qp), allocatable :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index dffd8e9a0..d00d5c2b7 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -194,7 +194,7 @@ subroutine sysSolvFida(& ! ------------------------------------------------------------------------------------------------------ ! * model solver ! ------------------------------------------------------------------------------------------------------ - logical(lgt),parameter :: forceFullMatrix=.true. ! flag to force the use of the full Jacobian matrix + logical(lgt),parameter :: forceFullMatrix=.true. ! flag to force the use of the full Jacobian matrix logical(lgt),parameter :: compAverageFlux=.true. integer(i4b) :: ixQuadrature=ixRectangular ! type of quadrature method to approximate average fluxes integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) @@ -465,23 +465,22 @@ subroutine sysSolvFida(& mLayerCmpress_sum(:) = 0._dp call fidaSolver(& - dt, & ! intent (in) current time step(entire) + dt, & ! intent (in) data time step atol, & ! intent (in) absolute telerance rtol, & ! intent (in) relative tolerance nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): number of layers + nLayers, & ! intent(in): number of snow+soil layers nState, & ! intent(in): number of state variables in the current subset ixMatrix, & ! intent(in): type of matrix (dense or banded) - ixQuadrature, & ! intent(in) + ixQuadrature, & ! intent(in): type of quadrature method to approximate average fluxes firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVecTrial, & ! intent(in): model state vector at the beginning of the data time step sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - dMat, & ! intent(inout) + dMat, & ! intent(inout) diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil @@ -493,9 +492,9 @@ subroutine sysSolvFida(& indx_data, & ! intent(in): index data ! input-output: data structures diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_init, & ! intent(inou): - flux_temp, & ! intent(inout): model fluxes for a local HRU (initial flux structure) - flux_sum, & ! intent(inout) + flux_init, & ! intent(inou): model fluxes for a local HRU (initial flux structure) + flux_temp, & ! intent(inout): model fluxes for a local HRU + flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) From 31c599425c6ae9bcdaf59788613b87bc40a82ce4 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Mon, 15 Feb 2021 12:36:36 -0600 Subject: [PATCH 0044/1472] more cleaning --- build/source/engine/evalEqnsFida.f90 | 2 +- build/source/engine/fidaSolver.f90 | 2 +- build/source/engine/sysSolvFida.f90 | 50 ++++++---------------------- 3 files changed, 12 insertions(+), 42 deletions(-) diff --git a/build/source/engine/evalEqnsFida.f90 b/build/source/engine/evalEqnsFida.f90 index 6cf7f33ba..bd416ca2f 100644 --- a/build/source/engine/evalEqnsFida.f90 +++ b/build/source/engine/evalEqnsFida.f90 @@ -154,7 +154,7 @@ integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_d if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif - if(.not.feasible)then; eqns_data%message=trim(eqns_data%message)//'state vector not feasible'; ierr = -1; return; endif + if(.not.feasible)then; eqns_data%message=trim(eqns_data%message)//'state vector not feasible'; ierr = 1; return; endif ! return success ierr = 0 diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index d4f7745a4..fc4668da2 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -118,7 +118,7 @@ subroutine fidaSolver( & sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout) ! input: data structures - lookup_data, & + lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes mpar_data, & ! intent(in): model parameters diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index d00d5c2b7..30095ed6d 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -210,19 +210,14 @@ subroutine sysSolvFida(& real(dp) :: rAdd(nState) ! additional terms in the residual vector real(dp) :: fOld ! function values (-); NOTE: dimensionless because scaled logical(lgt) :: feasible ! feasibility flag - real(dp) :: resSinkNew(nState) ! additional terms in the residual vector - real(dp) :: fluxVecNew(nState) ! new flux vector - real(dp) :: t0 ! beginning of the current time step - real(dp) :: tout ! end of the current time step - real(dp) :: tret(1) - real(dp) :: dt_last(1) - real(dp) :: atol(nState) ! absolute telerance - real(dp) :: rtol(nState) ! relative tolerance - type(var_dlength) :: flux_sum - real(qp) :: dt_past - integer(i4b) :: tol_iter - real(dp), allocatable :: mLayerCmpress_sum(:) - logical(lgt) :: idaSucceeds + real(dp) :: dt_last(1) ! last stepsize taken by ida solver + real(qp) :: dt_past ! one step before the last stepsize taken by ida solver + real(dp) :: atol(nState) ! absolute telerance + real(dp) :: rtol(nState) ! relative tolerance + type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a data step + integer(i4b) :: tol_iter ! iteration index + real(dp), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix + logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step ! --------------------------------------------------------------------------------------- @@ -238,31 +233,10 @@ subroutine sysSolvFida(& mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) -relConvTol_liquid => mpar_data%var(iLookPARAM%relConvTol_liquid)%dat(1) ,& ! intent(in): [dp] absolute convergence tolerance for vol frac liq water (-) - relConvTol_matric => mpar_data%var(iLookPARAM%relConvTol_matric)%dat(1) ,& ! intent(in): [dp] absolute convergence tolerance for matric head (m) - relConvTol_energy => mpar_data%var(iLookPARAM%relConvTol_energy)%dat(1) ,& ! intent(in): [dp] absolute convergence tolerance for - ! model states for the vegetation canopy - relConvTol_aquifr => mpar_data%var(iLookPARAM%relConvTol_aquifr)%dat(1) ,& ! intent(in): - ! accelerate solution for temperature airtemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ! vector of energy and hydrology indices for the snow and soil domains - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the soil domain - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology state variables in the snow+soil domain - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology state variables in the soil domain - ! mapping from full domain to the sub-domain - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b] mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b] index of control volume for different domains (veg, snow, soil) - ! type of state and domain for a given variable - ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] type of desired model state variables - ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] domain for desired model state variables - ! layer geometry - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in): [i4b] total number of layers @@ -324,10 +298,6 @@ subroutine sysSolvFida(& flux_init%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) end do - ! ************************************************************************************************************************** - ! *** NUMERICAL SOLUTION FOR A GIVEN SUBSTEP AND SPLIT ********************************************************************* - ! ************************************************************************************************************************** - ! ----- ! * get scaling vectors... ! ------------------------ @@ -439,10 +409,10 @@ subroutine sysSolvFida(& prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers - mpar_data, & ! intent(in) + mpar_data, & ! intent(in): model parameters ! output atol, & ! intent(out): absolute tolerances vector (mixed units) - rtol, & + rtol, & ! intent(out): relative tolerances vector (mixed units) err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) From d4c7ed578c8c49a1a8f3730689d67724d2cdf5a8 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Mon, 15 Feb 2021 13:19:33 -0600 Subject: [PATCH 0045/1472] more edit --- build/source/engine/eval8summaFida.f90 | 2 +- build/source/engine/fidaSolver.f90 | 72 ++++++++++---------------- build/source/engine/sysSolvFida.f90 | 9 +--- build/source/engine/varSubstepFida.f90 | 4 +- 4 files changed, 30 insertions(+), 57 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 10a2d578a..083357bcd 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -471,7 +471,7 @@ subroutine eval8summaFida(& ! save the number of flux calls per time step indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) + 1 - firstFluxCall = .false. + firstFluxCall = .true. ! compute the fluxes for a given state vector call computFlux(& ! input-output: model control diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index fc4668da2..205f62356 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -72,7 +72,7 @@ module fidaSolver_module var_d, & ! data vector (dp) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (dp) - zLookup, & + zLookup, & ! data vector model_options ! defines the model decisions ! look-up values for the choice of groundwater representation (local-column, or single-basin) @@ -115,8 +115,8 @@ subroutine fidaSolver( & scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVecInit, & ! intent(in): initial state vector - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - dMat, & ! intent(inout) + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + dMat, & ! intent(inout): diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil @@ -130,18 +130,18 @@ subroutine fidaSolver( & diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_temp, & ! intent(inout): model fluxes for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU - flux_sum, & ! intent(inout) + flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) ! output idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step - mLayerCmpress_sum, & - dt_last, & - dt_past, & - stateVec, & ! intent(out): model state vector - stateVecPrime, & ! intent(out): derivative of model state vector + mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix + dt_last, & ! intent(out): last stepsize + dt_past, & ! intent(out): one stepsize before the last one + stateVec, & ! intent(out): model state vector + stateVecPrime, & ! intent(out): derivative of model state vector err,message & ! intent(out): error control ) @@ -150,27 +150,26 @@ subroutine fidaSolver( & use nrtype use fida_datatypes use evalEqnsFida_module,only:evalEqnsFida - use allocspace_module,only:allocLocal ! allocate local data structures - - - use fida_mod ! Fortran interface to IDA - use fnvector_serial_mod ! Fortran interface to serial N_Vector - use fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix - use fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver - use fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix - use fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver - use fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver - use fsundials_matrix_mod ! Fortran interface to generic SUNMatrix - use fsundials_nvector_mod ! Fortran interface to generic N_Vector - use fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver - use fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver - use evalEqnsFida_module,only:evalEqnsFida ! ODE functions - use evalJacFida_module,only:evalJacFida + use allocspace_module,only:allocLocal ! allocate local data structures + + + use fida_mod ! Fortran interface to IDA + use fnvector_serial_mod ! Fortran interface to serial N_Vector + use fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix + use fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver + use fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix + use fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver + use fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver + use fsundials_matrix_mod ! Fortran interface to generic SUNMatrix + use fsundials_nvector_mod ! Fortran interface to generic N_Vector + use fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver + use fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver + use evalEqnsFida_module,only:evalEqnsFida ! DAE/ODE functions + use evalJacFida_module,only:evalJacFida ! system Jacobian use tolFida_module,only:computWeightFida use convTestFida_module,only:convTestFida use eval8summaFida_module,only:eval8summaFida USE computEnthalpy_module,only:computEnthalpy -! use findDiscontinuity_module,only:findDiscontinuity !======= Declarations ========= implicit none @@ -223,7 +222,6 @@ subroutine fidaSolver( & logical(lgt),intent(out) :: idaSucceeds real(qp),intent(out) :: dt_last(1) real(qp),intent(out) :: dt_past - logical(lgt) :: scalling_on ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables @@ -243,11 +241,7 @@ subroutine fidaSolver( & integer(kind = 8) :: mu, lu real(qp) :: h_max real(qp) :: coef_nonlin - integer(i4b) :: lsflag - integer(i4b) :: iLayer ! index of layer in the snow+soil domain - integer(i4b) :: iState ! index of model state - real(dp) :: idenIW - integer(i4b),parameter :: ixRectangular=1 ! reza: should put them in a data type later + integer(i4b),parameter :: ixRectangular=1 integer(i4b),parameter :: ixTrapezoidal=2 integer(i4b) :: iVar logical(lgt) :: startQuadrature @@ -256,16 +250,6 @@ subroutine fidaSolver( & integer(c_long) :: nState ! total number of state variables real(dp) :: rVec(nStat) real(qp) :: tret(1) - globalVars: associate(& - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat & ! intent(in): - ) - - - !======= Internals ============ @@ -744,10 +728,6 @@ subroutine fidaSolver( & call FN_VDestroy(sunvec_y) call FN_VDestroy(sunvec_yp) - ! end associate statements - end associate globalVars - - end subroutine fidaSolver diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 30095ed6d..fa1287e55 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -119,7 +119,6 @@ subroutine sysSolvFida(& ! output deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - untappedMelt, & ! intent(out): un-tapped melt energy (J m-3 s-1) stateVecTrial, & ! intent(out): updated state vector stateVecPrime, & ! intent(out): updated state vector reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step @@ -166,7 +165,6 @@ subroutine sysSolvFida(& ! output: model control type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(dp),intent(out) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) real(dp),intent(out) :: stateVecTrial(:) ! trial state vector (mixed units) real(dp),intent(out) :: stateVecPrime(:) ! trial state vector (mixed units) logical(lgt),intent(out) :: reduceCoupledStep ! flag to reduce the length of the coupled step @@ -180,8 +178,6 @@ subroutine sysSolvFida(& ! --------------------------------------------------------------------------------------- character(LEN=256) :: cmessage ! error message of downwind routine integer(i4b) :: iVar ! index of variable - integer(i4b) :: iLayer ! index of layer in the snow+soil domain - integer(i4b) :: iState ! index of model state integer(i4b) :: local_ixGroundwater ! local index for groundwater representation real(dp) :: bulkDensity ! bulk density of a given layer (kg m-3) real(dp) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) @@ -523,10 +519,7 @@ subroutine sysSolvFida(& ! save the computed solution stateVecTrial = stateVecNew - - ! set untapped melt energy to zero - untappedMelt(:) = 0._dp - + deallocate(mLayerCmpress_sum) diff --git a/build/source/engine/varSubstepFida.f90 b/build/source/engine/varSubstepFida.f90 index 2ed660718..d5388697a 100644 --- a/build/source/engine/varSubstepFida.f90 +++ b/build/source/engine/varSubstepFida.f90 @@ -336,7 +336,6 @@ subroutine varSubstepFida(& ! output: model control deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - untappedMelt, & ! intent(out): un-tapped melt energy (J m-3 s-1) stateVecTrial, & ! intent(out): updated state vector stateVecPrime, & ! intent(out): updated state vector reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step @@ -348,7 +347,8 @@ subroutine varSubstepFida(& if(err>0) return endif - + ! set untapped melt energy to zero + untappedMelt(:) = 0._dp ! if too much melt or need to reduce length of the coupled step then return ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry From af6d1ff190aa8b3efd8e44bb993ff1aa94a5de71 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Mon, 15 Feb 2021 19:35:50 -0600 Subject: [PATCH 0046/1472] more edit --- build/source/engine/eval8summaFida.f90 | 2 +- build/source/engine/fidaSolver.f90 | 105 ++++++++++--------------- 2 files changed, 44 insertions(+), 63 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 083357bcd..29d9498ff 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -146,7 +146,7 @@ subroutine eval8summaFida(& ! provide access to subroutines USE varExtrFida_module, only:varExtract ! extract variables from the state vector USE varExtrFida_module, only:varExtractFida - USE updateVarsFida_module, only:updateVarsFida ! update prognostic variables + USE updateVarsFida_module, only:updateVarsFida ! update variables USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy USE soilCmpresFida_module, only:soilCmpresFida ! compute soil compression USE computFlux_module, only:computFlux ! compute fluxes given a state vector diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 205f62356..97d5c64e4 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -4,10 +4,10 @@ module fidaSolver_module - !======= Inclusions =========== - use, intrinsic :: iso_c_binding - use nrtype - use fida_datatypes +!======= Inclusions =========== +USE, intrinsic :: iso_c_binding +USE nrtype +USE fida_datatypes ! access the global print flag USE globalData,only:globalPrintFlag @@ -44,10 +44,10 @@ module fidaSolver_module USE globalData,only:model_decisions ! model decision structure ! global metadata -USE globalData,only:flux_meta ! metadata on the model fluxes -USE globalData,only:diag_meta ! metadata on the model diagnostic variables -USE globalData,only:prog_meta ! metadata on the model prognostic variables -USE globalData,only:deriv_meta ! metadata on the model derivatives +USE globalData,only:flux_meta ! metadata on the model fluxes +USE globalData,only:diag_meta ! metadata on the model diagnostic variables +USE globalData,only:prog_meta ! metadata on the model prognostic variables +USE globalData,only:deriv_meta ! metadata on the model derivatives ! constants USE multiconst,only:& @@ -59,12 +59,8 @@ module fidaSolver_module ! provide access to indices that define elements of the data structures USE var_lookup,only:iLookPROG ! named variables for structure elements USE var_lookup,only:iLookDIAG ! named variables for structure elements -USE var_lookup,only:iLookFLUX ! named variables for structure elements -USE var_lookup,only:iLookFORCE ! named variables for structure elements -USE var_lookup,only:iLookPARAM ! named variables for structure elements -USE var_lookup,only:iLookINDEX ! named variables for structure elements USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure - USE var_lookup,only:iLookDERIV ! named variables for structure elements +USE var_lookup,only:iLookDERIV ! named variables for structure elements ! provide access to the derived types to define the data structures USE data_types,only:& @@ -72,34 +68,25 @@ module fidaSolver_module var_d, & ! data vector (dp) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (dp) - zLookup, & ! data vector - model_options ! defines the model decisions + zLookup ! data vector -! look-up values for the choice of groundwater representation (local-column, or single-basin) -USE mDecisions_module,only: & - localColumn, & ! separate groundwater representation in each local soil column - singleBasin ! single groundwater store over the entire basin ! look-up values for the choice of groundwater parameterization -USE mDecisions_module,only: & - qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization - bigBucket, & ! a big bucket (lumped aquifer model) - noExplicit ! no explicit groundwater parameterization +USE mDecisions_module,only: qbaseTopmodel ! TOPMODEL-ish baseflow parameterization - ! privacy - implicit none - private - public::fidaSolver +! privacy + implicit none + private::setInitialCondition + public::fidaSolver contains !------------------- - ! * subroutine fidaSolver: solve F(y,y') = 0 by FIDA. Here, y is the state vector + ! * subroutine fidaSolver: solve F(y,y') = 0 by FIDA (y is the state vector) ! ------------------ - subroutine fidaSolver( & dt, & ! intent(in): data time step atol, & ! intent(in): absolute telerance @@ -115,7 +102,7 @@ subroutine fidaSolver( & scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVecInit, & ! intent(in): initial state vector - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + sMul, & ! intent(inout): state vector multiplier (USEd in the residual calculations) dMat, & ! intent(inout): diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures lookup_data, & ! intent(in): lookup tables @@ -146,29 +133,23 @@ subroutine fidaSolver( & ) !======= Inclusions =========== - use, intrinsic :: iso_c_binding - use nrtype - use fida_datatypes - use evalEqnsFida_module,only:evalEqnsFida - use allocspace_module,only:allocLocal ! allocate local data structures - - - use fida_mod ! Fortran interface to IDA - use fnvector_serial_mod ! Fortran interface to serial N_Vector - use fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix - use fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver - use fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix - use fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver - use fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver - use fsundials_matrix_mod ! Fortran interface to generic SUNMatrix - use fsundials_nvector_mod ! Fortran interface to generic N_Vector - use fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver - use fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver - use evalEqnsFida_module,only:evalEqnsFida ! DAE/ODE functions - use evalJacFida_module,only:evalJacFida ! system Jacobian - use tolFida_module,only:computWeightFida - use convTestFida_module,only:convTestFida - use eval8summaFida_module,only:eval8summaFida + USE fida_mod ! Fortran interface to IDA + USE fnvector_serial_mod ! Fortran interface to serial N_Vector + USE fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix + USE fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver + USE fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix + USE fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver + USE fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver + USE fsundials_matrix_mod ! Fortran interface to generic SUNMatrix + USE fsundials_nvector_mod ! Fortran interface to generic N_Vector + USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver + USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver + USE allocspace_module,only:allocLocal ! allocate local data structures + USE evalEqnsFida_module,only:evalEqnsFida ! DAE/ODE functions + USE evalJacFida_module,only:evalJacFida ! system Jacobian + USE tolFida_module,only:computWeightFida + USE convTestFida_module,only:convTestFida + USE eval8summaFida_module,only:eval8summaFida USE computEnthalpy_module,only:computEnthalpy !======= Declarations ========= @@ -202,20 +183,20 @@ subroutine fidaSolver( & type(var_d), intent(in) :: forc_data ! model forcing data type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU - ! output: data structures - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + type(var_ilength), intent(in) :: indx_data ! indices defining model states and layers + ! input-output: data structures type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: flux_temp ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout):: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: flux_sum type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) real(dp),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) real(dp),intent(inout) :: mLayerCmpress_sum(:) - ! output: flux and residual vectors - real(dp),intent(inout) :: stateVec(:) ! model state vector - real(dp),intent(inout) :: stateVecPrime(:) ! model state vector + ! output: state vectors + real(dp),intent(inout) :: stateVec(:) ! model state vector (y) + real(dp),intent(inout) :: stateVecPrime(:) ! model state vector (y') ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -738,9 +719,9 @@ end subroutine fidaSolver subroutine setInitialCondition(neq, y, sunvec_u, sunvec_up) !======= Inclusions =========== - use, intrinsic :: iso_c_binding - use fsundials_nvector_mod - use fnvector_serial_mod + USE, intrinsic :: iso_c_binding + USE fsundials_nvector_mod + USE fnvector_serial_mod !======= Declarations ========= implicit none From 158a7f78564aeb242837d0c5d57637769a117c86 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Mon, 15 Feb 2021 19:54:30 -0600 Subject: [PATCH 0047/1472] ixSaturation deleted from fidaSolver --- build/source/engine/eval8summaFida.f90 | 3 +-- build/source/engine/evalEqnsFida.f90 | 1 - build/source/engine/fidaSolver.f90 | 7 +------ build/source/engine/fida_datatypes.f90 | 1 - build/source/engine/opSplittin.f90 | 2 +- build/source/engine/sysSolvFida.f90 | 1 - 6 files changed, 3 insertions(+), 12 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 29d9498ff..1e8edde89 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -117,7 +117,6 @@ subroutine eval8summaFida(& flux_data, & ! intent(inout): model fluxes for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) @@ -188,7 +187,6 @@ subroutine eval8summaFida(& type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) real(dp),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors real(dp),intent(inout) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) @@ -255,6 +253,7 @@ subroutine eval8summaFida(& real(qp) :: heatCapVegTrial real(qp) :: scalarCanopyEnthalpyPrime logical(lgt) :: firstFluxCall + integer(i4b) :: ixSaturation ! index of the lowest saturated layer ! -------------------------------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/evalEqnsFida.f90 b/build/source/engine/evalEqnsFida.f90 index bd416ca2f..b389e3996 100644 --- a/build/source/engine/evalEqnsFida.f90 +++ b/build/source/engine/evalEqnsFida.f90 @@ -126,7 +126,6 @@ integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_d eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow - eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 97d5c64e4..649fe55da 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -120,7 +120,6 @@ subroutine fidaSolver( & flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) ! output idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step @@ -191,7 +190,6 @@ subroutine fidaSolver( & type(var_dlength),intent(inout) :: flux_sum type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) real(dp),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) real(dp),intent(inout) :: mLayerCmpress_sum(:) ! output: state vectors @@ -298,8 +296,7 @@ subroutine fidaSolver( & eqns_data%forc_data = forc_data eqns_data%bvar_data = bvar_data eqns_data%indx_data = indx_data - eqns_data%ixSaturation = ixSaturation - + ! allocate space for the baseflow derivatives ! NOTE: needs allocation because only used when baseflow sinks are active if(model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel)then @@ -598,7 +595,6 @@ subroutine fidaSolver( & eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow - eqns_data%ixSaturation, & ! intent(inout):index of the lowest saturated layer (NOTE: only computed on the first iteration) eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) @@ -674,7 +670,6 @@ subroutine fidaSolver( & flux_data = eqns_data%flux_data deriv_data = eqns_data%deriv_data dBaseflow_dMatric = eqns_data%dBaseflow_dMatric - ixSaturation = eqns_data%ixSaturation dt_past = eqns_data%stepsize_past err = eqns_data%err message = eqns_data%message diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index 22e00dd6b..e159b1a0a 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -44,7 +44,6 @@ module fida_datatypes type(var_dlength) :: flux_data ! model fluxes for a local HRU type(var_dlength) :: flux_sum type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) real(dp) :: scalar real(dp) :: scalarCanopyTempTrial ! intent(in): trial value of canopy temperature (K) real(dp) :: scalarCanopyTempPrev ! intent(in): previous value of canopy temperature (K) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index b44da974b..fc7f19e9c 100755 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -296,7 +296,7 @@ subroutine opSplittin(& integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) integer(i4b),parameter :: IDA=1 integer(i4b),parameter :: BE=2 - integer(i4b) :: solver=IDA ! BE or IDA + integer(i4b) :: solver=IDA ! BE or IDA integer(i4b) :: nCoupling ! --------------------------------------------------------------------------------------- ! point to variables in the data structures diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index fa1287e55..1141eea4d 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -463,7 +463,6 @@ subroutine sysSolvFida(& flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) ! output idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step From 528e25f1d3cde1636c25ca39eb234c94ee7ee47c Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Mon, 15 Feb 2021 20:29:42 -0600 Subject: [PATCH 0048/1472] extra flux data structures deleted from fida_datatype --- build/source/engine/eval8summaFida.f90 | 2 +- build/source/engine/fidaSolver.f90 | 23 +++++------------------ build/source/engine/fida_datatypes.f90 | 3 --- build/source/engine/sysSolvFida.f90 | 4 ++-- build/source/engine/systemSolv.f90 | 2 +- 5 files changed, 9 insertions(+), 25 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 1e8edde89..4a3fd0203 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -253,7 +253,7 @@ subroutine eval8summaFida(& real(qp) :: heatCapVegTrial real(qp) :: scalarCanopyEnthalpyPrime logical(lgt) :: firstFluxCall - integer(i4b) :: ixSaturation ! index of the lowest saturated layer + integer(i4b) :: ixSaturation ! index of the lowest saturated layer ! -------------------------------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 649fe55da..1aa066fb7 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -269,20 +269,10 @@ subroutine fidaSolver( & if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif eqns_data%diag_data = diag_data - ! allocate space for the temporary flux variable structure - call allocLocal(flux_meta(:),eqns_data%flux_temp,nSnow,nSoil,err,message) - if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif - eqns_data%flux_temp = flux_temp - ! allocate space for the temporary flux variable structure call allocLocal(flux_meta(:),eqns_data%flux_data,nSnow,nSoil,err,message) if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif eqns_data%flux_data = flux_data - - ! allocate space for the temporary flux variable structure - call allocLocal(flux_meta(:),eqns_data%flux_sum,nSnow,nSoil,err,message) - if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif - eqns_data%flux_sum = flux_sum ! allocate space for the derivative structure call allocLocal(deriv_meta(:),eqns_data%deriv_data,nSnow,nSoil,err,message) @@ -640,16 +630,16 @@ subroutine fidaSolver( & end do endif do iVar=1,size(flux_meta) - flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:) = eqns_data%flux_data%var(iVar)%dat(:) end do - dt_past = dt_last(1) case default; err=20; message=trim(message)//'expect case to be ixRecangular, ixTrapezoidal'; return end select + dt_past = dt_last(1) ! sum of mLayerCmpress mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) - ! save values of some quantities for next step + ! save required quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) @@ -665,12 +655,9 @@ subroutine fidaSolver( & !****************************** End of Main Solver *************************************** ! copy the output data - diag_data = eqns_data%diag_data - flux_temp = eqns_data%flux_temp + diag_data = eqns_data%diag_data flux_data = eqns_data%flux_data - deriv_data = eqns_data%deriv_data - dBaseflow_dMatric = eqns_data%dBaseflow_dMatric - dt_past = eqns_data%stepsize_past + deriv_data = eqns_data%deriv_data err = eqns_data%err message = eqns_data%message if( tret(1) == dt .and. feasible)then diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index e159b1a0a..7a83dfb72 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -40,9 +40,7 @@ module fida_datatypes type(var_dlength) :: prog_data ! prognostic variables for a local HRU type(var_ilength) :: indx_data ! indices defining model states and layers type(var_dlength) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength) :: flux_temp ! model fluxes for a local HRU type(var_dlength) :: flux_data ! model fluxes for a local HRU - type(var_dlength) :: flux_sum type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables real(dp) :: scalar real(dp) :: scalarCanopyTempTrial ! intent(in): trial value of canopy temperature (K) @@ -73,7 +71,6 @@ module fida_datatypes character(len = 50) :: message ! error message type(c_ptr) :: ida_mem real(dp) :: t_cur - real(dp) :: stepsize_past end type eqnsData diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 1141eea4d..47faafd15 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -458,7 +458,7 @@ subroutine sysSolvFida(& indx_data, & ! intent(in): index data ! input-output: data structures diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_init, & ! intent(inou): model fluxes for a local HRU (initial flux structure) + flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) flux_temp, & ! intent(inout): model fluxes for a local HRU flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables @@ -520,7 +520,7 @@ subroutine sysSolvFida(& deallocate(mLayerCmpress_sum) - + deallocate(dBaseflow_dMatric) ! end associate statements end associate globalVars diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 51f7b9699..342b08ee4 100755 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -557,7 +557,7 @@ subroutine systemSolv(& end do ! looping through non-missing water state variables in the soil domain endif -! if ( allocated(dBaseflow_dMatric) ) deallocate(dBaseflow_dMatric) reza: does not change memcheck output + if ( allocated(dBaseflow_dMatric) ) deallocate(dBaseflow_dMatric) ! end associate statements end associate globalVars From 1c20ba0cb5aa07d1b497b0ba7f410fb7fcf12598 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Mon, 15 Feb 2021 20:48:00 -0600 Subject: [PATCH 0049/1472] dBaseflow_dMatric deleted from fidaSolver --- build/source/engine/fidaSolver.f90 | 13 +++---------- build/source/engine/fida_datatypes.f90 | 1 - build/source/engine/sysSolvFida.f90 | 14 +++++--------- 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 1aa066fb7..495fbcc53 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -119,8 +119,6 @@ subroutine fidaSolver( & flux_data, & ! intent(inout): model fluxes for a local HRU flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) ! output idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix @@ -165,8 +163,8 @@ subroutine fidaSolver( & integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers integer(i4b),intent(in) :: nStat ! total number of state variables - integer(i4b) :: ixMatrix ! form of matrix (dense or banded) - integer(i4b) :: ixQuadrature ! type of quadrature method for approximating average flux + integer(i4b),intent(in) :: ixMatrix ! form of matrix (dense or banded) + integer(i4b),intent(in) :: ixQuadrature ! type of quadrature method for approximating average flux logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution @@ -189,8 +187,6 @@ subroutine fidaSolver( & type(var_dlength),intent(inout):: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: flux_sum type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - real(dp),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) real(dp),intent(inout) :: mLayerCmpress_sum(:) ! output: state vectors real(dp),intent(inout) :: stateVec(:) ! model state vector (y) @@ -241,8 +237,7 @@ subroutine fidaSolver( & eqns_data%nSoil = nSoil eqns_data%nLayers = nLayers eqns_data%nState = nState - eqns_data%ixMatrix = ixMatrix - eqns_data%ixQuadrature = ixQuadrature + eqns_data%ixMatrix = ixMatrix eqns_data%firstSubStep = firstSubStep eqns_data%computeVegFlux = computeVegFlux eqns_data%scalarSolution = scalarSolution @@ -294,8 +289,6 @@ subroutine fidaSolver( & else allocate(eqns_data%dBaseflow_dMatric(0,0),stat=err) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix end if - - eqns_data%dBaseflow_dMatric = dBaseflow_dMatric allocate( eqns_data%mLayerCmpress_sum(nSoil) ) eqns_data%mLayerCmpress_sum = mLayerCmpress_sum diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index 7a83dfb72..889aececf 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -24,7 +24,6 @@ module fida_datatypes integer(i4b) :: nLayers ! total number of layers integer :: nState ! total number of state variables integer(i4b) :: ixMatrix ! form of matrix (dense or banded) - integer(i4b) :: ixQuadrature ! type of quadrature method for approximating average flux logical(lgt) :: startQuadrature logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 47faafd15..033b97680 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -191,7 +191,6 @@ subroutine sysSolvFida(& ! * model solver ! ------------------------------------------------------------------------------------------------------ logical(lgt),parameter :: forceFullMatrix=.true. ! flag to force the use of the full Jacobian matrix - logical(lgt),parameter :: compAverageFlux=.true. integer(i4b) :: ixQuadrature=ixRectangular ! type of quadrature method to approximate average fluxes integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) type(var_dlength) :: flux_init ! model fluxes at the start of the time step @@ -443,7 +442,7 @@ subroutine sysSolvFida(& firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors + ! input: state vector stateVecTrial, & ! intent(in): model state vector at the beginning of the data time step sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout) diagonal of the Jacobian matrix (excludes fluxes) @@ -462,8 +461,6 @@ subroutine sysSolvFida(& flux_temp, & ! intent(inout): model fluxes for a local HRU flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) ! output idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix @@ -491,8 +488,8 @@ subroutine sysSolvFida(& endif - if (compAverageFlux)then - select case(ixQuadrature) + ! compute average flux + select case(ixQuadrature) case(ixRectangular) ! divide by dt. Now we have average flux do iVar=1,size(flux_meta) @@ -506,10 +503,9 @@ subroutine sysSolvFida(& end do ! check case default; err=20; message=trim(message)//'expect case to be ixRecangular, ixTrapezoidal'; return - end select + end select - diag_data%var(iLookDIAG%mLayerCompress)%dat(:) = mLayerCmpress_sum(:) - endif + diag_data%var(iLookDIAG%mLayerCompress)%dat(:) = mLayerCmpress_sum(:) ! compute the total change in storage associated with compression of the soil matrix (kg m-2) diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sum(diag_data%var(iLookDIAG%mLayerCompress)%dat(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water From 8acd22984284e8333c2fb89064007385591ce063 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Mon, 15 Feb 2021 21:48:44 -0600 Subject: [PATCH 0050/1472] startQuadrature deleted from fida_datatype --- build/source/engine/fidaSolver.f90 | 4 +--- build/source/engine/fida_datatypes.f90 | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 495fbcc53..53ffe9b02 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -313,9 +313,7 @@ subroutine fidaSolver( & allocate( eqns_data%fluxVec(nState) ) allocate( eqns_data%resSink(nState) ) - eqns_data%err = err - eqns_data%message = message - eqns_data%startQuadrature = .true. + startQuadrature = .true. diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index 889aececf..81e20db77 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -24,7 +24,6 @@ module fida_datatypes integer(i4b) :: nLayers ! total number of layers integer :: nState ! total number of state variables integer(i4b) :: ixMatrix ! form of matrix (dense or banded) - logical(lgt) :: startQuadrature logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution From ba1644684d8a92c5425d7199acb03140dd25317c Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Mon, 15 Feb 2021 22:06:35 -0600 Subject: [PATCH 0051/1472] convTestFida deleted --- build/Makefile | 3 +-- build/source/engine/fidaSolver.f90 | 25 +++++++++---------------- build/source/engine/fida_datatypes.f90 | 2 -- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/build/Makefile b/build/Makefile index b7fb6e2a2..390f9c0b7 100644 --- a/build/Makefile +++ b/build/Makefile @@ -233,8 +233,7 @@ SUMMA_SOLVER= \ eval8FidaJac.f90 \ evalJacFida.f90 \ nonlinSolvFida.f90 \ - convTestFida.f90 \ - findDiscontinuity.f90 \ + findDiscontinuity.f90 \ fidaSolver.f90 \ sysSolvFida.f90 \ varSubstep.f90 \ diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 53ffe9b02..83f3251cc 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -145,7 +145,6 @@ subroutine fidaSolver( & USE evalEqnsFida_module,only:evalEqnsFida ! DAE/ODE functions USE evalJacFida_module,only:evalJacFida ! system Jacobian USE tolFida_module,only:computWeightFida - USE convTestFida_module,only:convTestFida USE eval8summaFida_module,only:eval8summaFida USE computEnthalpy_module,only:computEnthalpy @@ -438,13 +437,6 @@ subroutine fidaSolver( & stop 1 end if -! Set the user-supplied nonlinear solver convergence test function -! retval = FSUNNonlinSolSetConvTestFn(sunnonlin_NLS, c_funloc(convTestFida), c_loc(eqns_data)); -! if (retval /= 0) then -! print *, 'Error in FSUNNonlinSolSetConvTestFn, retval = ', retval, '; halting' -! stop 1 -! end if - ! Set the maximum BDF order, default = 5 retval = FIDASetMaxOrd(ida_mem, 5) @@ -493,12 +485,12 @@ subroutine fidaSolver( & end if ! Set maximum stepsize, dafault = infinity - ! h_max = 1 - ! retval = FIDASetMaxStep(ida_mem, h_max) - ! if (retval /= 0) then - ! print *, 'Error in FIDASetMaxSteps, retval = ', retval, '; halting' - ! stop 1 - ! end if + h_max = dt + retval = FIDASetMaxStep(ida_mem, h_max) + if (retval /= 0) then + print *, 'Error in FIDASetMaxSteps, retval = ', retval, '; halting' + stop 1 + end if ! Set initial stepsize h_init = 0 @@ -508,14 +500,15 @@ subroutine fidaSolver( & stop 1 end if + ! enforce the solver to stop at the end of the data time step retval = FIDASetStopTime(ida_mem, dt) if (retval /= 0) then print *, 'Error in FIDASetStopTime, retval = ', retval, '; halting' stop 1 end if - - ! retval = FIDASetLinearSolutionScaling(ida_mem, 0) ! scalling_on 1 and off 0 + ! scalling_on 1 and off 0 + ! retval = FIDASetLinearSolutionScaling(ida_mem, 0) tret(1) = t0 ! need the following values for the first substep diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index 81e20db77..0f6cf655a 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -40,7 +40,6 @@ module fida_datatypes type(var_dlength) :: diag_data ! diagnostic variables for a local HRU type(var_dlength) :: flux_data ! model fluxes for a local HRU type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(dp) :: scalar real(dp) :: scalarCanopyTempTrial ! intent(in): trial value of canopy temperature (K) real(dp) :: scalarCanopyTempPrev ! intent(in): previous value of canopy temperature (K) real(dp) :: scalarCanopyIceTrial @@ -62,7 +61,6 @@ module fida_datatypes real(dp), allocatable :: mLayerTempPrev(:) real(dp), allocatable :: fluxVec(:) ! flux vector real(qp), allocatable :: resSink(:) - real(qp), allocatable :: resVec(:) real(dp), allocatable :: atol(:) real(dp), allocatable :: rtol(:) integer(i4b) :: err ! error code From 8d5f88dde28b944b8d66d3976d6d0ebbdde1af22 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Mon, 15 Feb 2021 22:11:10 -0600 Subject: [PATCH 0052/1472] more edit --- build/source/engine/fida_datatypes.f90 | 3 +-- build/source/engine/{ => left_overs}/convTestFida.f90 | 0 2 files changed, 1 insertion(+), 2 deletions(-) rename build/source/engine/{ => left_overs}/convTestFida.f90 (100%) diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index 0f6cf655a..e68eacd21 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -18,6 +18,7 @@ module fida_datatypes implicit none type eqnsData + type(c_ptr) :: ida_mem real(dp) :: dt ! time step integer(i4b) :: nSnow ! number of snow layers integer(i4b) :: nSoil ! number of soil layers @@ -65,8 +66,6 @@ module fida_datatypes real(dp), allocatable :: rtol(:) integer(i4b) :: err ! error code character(len = 50) :: message ! error message - type(c_ptr) :: ida_mem - real(dp) :: t_cur end type eqnsData diff --git a/build/source/engine/convTestFida.f90 b/build/source/engine/left_overs/convTestFida.f90 similarity index 100% rename from build/source/engine/convTestFida.f90 rename to build/source/engine/left_overs/convTestFida.f90 From 7588e65ed187e81f143970229ba00adc21caf0c6 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Mon, 15 Feb 2021 23:36:20 -0600 Subject: [PATCH 0053/1472] more edit --- build/source/engine/fidaSolver.f90 | 4 -- build/source/engine/fida_datatypes.f90 | 82 +++++++++++++------------- 2 files changed, 40 insertions(+), 46 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 83f3251cc..c8c0f66dd 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -289,9 +289,6 @@ subroutine fidaSolver( & allocate(eqns_data%dBaseflow_dMatric(0,0),stat=err) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix end if - allocate( eqns_data%mLayerCmpress_sum(nSoil) ) - eqns_data%mLayerCmpress_sum = mLayerCmpress_sum - allocate( eqns_data%mLayerMatricHeadLiqTrial(nSoil) ) allocate( eqns_data%mLayerMatricHeadTrial(nSoil) ) @@ -653,7 +650,6 @@ subroutine fidaSolver( & deallocate(eqns_data%sMul) deallocate(eqns_data%dMat) deallocate(eqns_data%dBaseflow_dMatric) - deallocate(eqns_data%mLayerCmpress_sum) deallocate(eqns_data%mLayerMatricHeadLiqTrial) deallocate(eqns_data%mLayerMatricHeadTrial) deallocate(eqns_data%mLayerMatricHeadPrev) diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index e68eacd21..df7aa720d 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -4,7 +4,7 @@ module fida_datatypes ! data types USE nrtype -use, intrinsic :: iso_c_binding +USE, intrinsic :: iso_c_binding ! provide access to the derived types to define the data structures USE data_types,only:& @@ -12,60 +12,58 @@ module fida_datatypes var_d, & ! data vector (dp) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (dp) - zLookup, & ! data vector with variable length dimension (dp) - model_options ! defines the model decisions + zLookup ! data vector with variable length dimension (dp) implicit none type eqnsData - type(c_ptr) :: ida_mem - real(dp) :: dt ! time step - integer(i4b) :: nSnow ! number of snow layers - integer(i4b) :: nSoil ! number of soil layers - integer(i4b) :: nLayers ! total number of layers - integer :: nState ! total number of state variables - integer(i4b) :: ixMatrix ! form of matrix (dense or banded) - logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution - real(qp), allocatable :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) - real(dp), allocatable :: dMat(:) - type(zLookup) :: lookup_data ! lookup tables - type(var_i) :: type_data ! type of vegetation and soil - type(var_d) :: attr_data ! spatial attributes - type(var_dlength) :: mpar_data ! model parameters - type(var_d) :: forc_data ! model forcing data - type(var_dlength) :: bvar_data ! model variables for the local basin - type(var_dlength) :: prog_data ! prognostic variables for a local HRU - type(var_ilength) :: indx_data ! indices defining model states and layers - type(var_dlength) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength) :: flux_data ! model fluxes for a local HRU - type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(dp) :: scalarCanopyTempTrial ! intent(in): trial value of canopy temperature (K) - real(dp) :: scalarCanopyTempPrev ! intent(in): previous value of canopy temperature (K) - real(dp) :: scalarCanopyIceTrial - real(dp) :: scalarCanopyIcePrev - real(dp) :: scalarCanopyEnthalpyTrial ! intent(in): trial enthalpy of the vegetation canopy (J m-3) - real(dp) :: scalarCanopyEnthalpyPrev ! intent(in): previous enthalpy of the vegetation canopy (J m-3) - real(dp), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - real(dp), allocatable :: mLayerCmpress_sum(:) + type(c_ptr) :: ida_mem ! IDA memory + real(dp) :: dt ! time step + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers + integer(i4b) :: nLayers ! total number of layers + integer :: nState ! total number of state variables + integer(i4b) :: ixMatrix ! form of matrix (dense or banded) + logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution + type(zLookup) :: lookup_data ! lookup tables + type(var_i) :: type_data ! type of vegetation and soil + type(var_d) :: attr_data ! spatial attributes + type(var_dlength) :: mpar_data ! model parameters + type(var_d) :: forc_data ! model forcing data + type(var_dlength) :: bvar_data ! model variables for the local basin + type(var_dlength) :: prog_data ! prognostic variables for a local HRU + type(var_ilength) :: indx_data ! indices defining model states and layers + type(var_dlength) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength) :: flux_data ! model fluxes for a local HRU + type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + real(dp) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(dp) :: scalarCanopyTempPrev ! previous value of canopy temperature (K) + real(dp) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + real(dp) :: scalarCanopyIcePrev ! value of canopy ice content (kg m-2) at previous step + real(dp) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) + real(dp) :: scalarCanopyEnthalpyPrev ! previous enthalpy of the vegetation canopy (J m-3) + real(qp), allocatable :: sMul(:) ! state vector multiplier (used in the residual calculations) + real(dp), allocatable :: dMat(:) ! diagonal of the Jacobian matrix + real(dp), allocatable :: fluxVec(:) ! flux vector + real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation + real(dp), allocatable :: atol(:) ! vector of absolute tolerances + real(dp), allocatable :: rtol(:) ! vector of relative tolerances real(dp), allocatable :: mLayerMatricHeadLiqTrial(:) real(dp), allocatable :: mLayerMatricHeadTrial(:) real(dp), allocatable :: mLayerMatricHeadPrev(:) - real(dp), allocatable :: mLayerVolFracWatTrial(:) ! trial value for volumetric fraction of total water (-) + real(dp), allocatable :: mLayerVolFracWatTrial(:) ! trial value for volumetric fraction of total water (-) real(dp), allocatable :: mLayerVolFracWatPrev(:) - real(dp), allocatable :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of total water (-) + real(dp), allocatable :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of total water (-) real(dp), allocatable :: mLayerVolFracIcePrev(:) real(dp), allocatable :: mLayerEnthalpyTrial(:) real(dp), allocatable :: mLayerEnthalpyPrev(:) real(dp), allocatable :: mLayerTempTrial(:) real(dp), allocatable :: mLayerTempPrev(:) - real(dp), allocatable :: fluxVec(:) ! flux vector - real(qp), allocatable :: resSink(:) - real(dp), allocatable :: atol(:) - real(dp), allocatable :: rtol(:) - integer(i4b) :: err ! error code - character(len = 50) :: message ! error message + real(dp), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + integer(i4b) :: err ! error code + character(len = 50) :: message ! error message end type eqnsData From cbbcc3b3cb3cc1d23b7a996c60060ac58bc7812b Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Mon, 15 Feb 2021 23:55:48 -0600 Subject: [PATCH 0054/1472] more edit on fida_datatype --- build/source/engine/fida_datatypes.f90 | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index df7aa720d..a2570f15d 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -50,17 +50,17 @@ module fida_datatypes real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation real(dp), allocatable :: atol(:) ! vector of absolute tolerances real(dp), allocatable :: rtol(:) ! vector of relative tolerances - real(dp), allocatable :: mLayerMatricHeadLiqTrial(:) - real(dp), allocatable :: mLayerMatricHeadTrial(:) - real(dp), allocatable :: mLayerMatricHeadPrev(:) + real(dp), allocatable :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) + real(dp), allocatable :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(dp), allocatable :: mLayerMatricHeadPrev(:) ! vector of total water matric potential (m) at previous step real(dp), allocatable :: mLayerVolFracWatTrial(:) ! trial value for volumetric fraction of total water (-) - real(dp), allocatable :: mLayerVolFracWatPrev(:) - real(dp), allocatable :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of total water (-) - real(dp), allocatable :: mLayerVolFracIcePrev(:) - real(dp), allocatable :: mLayerEnthalpyTrial(:) - real(dp), allocatable :: mLayerEnthalpyPrev(:) - real(dp), allocatable :: mLayerTempTrial(:) - real(dp), allocatable :: mLayerTempPrev(:) + real(dp), allocatable :: mLayerVolFracWatPrev(:) ! value for volumetric fraction of total water (-) at previous step + real(dp), allocatable :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) + real(dp), allocatable :: mLayerVolFracIcePrev(:) ! value for volumetric fraction of ice (-) at previous step + real(dp), allocatable :: mLayerEnthalpyTrial(:) ! trial enthalpy of snow and soil (J m-3) + real(dp), allocatable :: mLayerEnthalpyPrev(:) ! enthalpy of snow and soil (J m-3) at previous step + real(dp), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(dp), allocatable :: mLayerTempPrev(:) ! vector of layer temperature (K) at previous step real(dp), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) integer(i4b) :: err ! error code character(len = 50) :: message ! error message From 50e7d50eaaf6ad2349d35fff86ba2fa1ed0f366c Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 16 Feb 2021 11:17:09 -0600 Subject: [PATCH 0055/1472] editing error messages in fidaSolver --- build/source/engine/fidaSolver.f90 | 74 ++++++++--------------------- build/source/engine/sysSolvFida.f90 | 6 +-- 2 files changed, 22 insertions(+), 58 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index c8c0f66dd..35b56a77e 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -169,7 +169,7 @@ subroutine fidaSolver( & logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors real(dp),intent(in) :: stateVecInit(:) ! model state vector - real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + real(qp),intent(inout) :: sMul(:) ! state vector multiplier (used in the residual calculations) real(dp), intent(inout) :: dMat(:) ! input: data structures type(zLookup),intent(in) :: lookup_data ! lookup tables @@ -190,12 +190,12 @@ subroutine fidaSolver( & ! output: state vectors real(dp),intent(inout) :: stateVec(:) ! model state vector (y) real(dp),intent(inout) :: stateVecPrime(:) ! model state vector (y') - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message logical(lgt),intent(out) :: idaSucceeds real(qp),intent(out) :: dt_last(1) real(qp),intent(out) :: dt_past + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables @@ -227,7 +227,8 @@ subroutine fidaSolver( & !======= Internals ============ - + ! initialize error control + err=0; message="fidaSolver/" nState = nStat idaSucceeds = .false. ! fill eqns_data which will be required later to call eval8summaFida @@ -315,50 +316,32 @@ subroutine fidaSolver( & ! create serial vectors sunvec_y => FN_VMake_Serial(nState, stateVec) - if (.not. associated(sunvec_y)) then - print *, 'ERROR: sunvec = NULL' - stop 1 - end if + if (.not. associated(sunvec_y)) then; err=20; message='fidaSolver: sunvec = NULL'; return; endif sunvec_yp => FN_VMake_Serial(nState, stateVecPrime) - if (.not. associated(sunvec_yp)) then - print *, 'ERROR: sunvec = NULL' - stop 1 - end if + if (.not. associated(sunvec_yp)) then; err=20; message='fidaSolver: sunvec = NULL'; return; endif - ! Initialize solution vectors reza: we should provide and pass stateVecPrimeInit later + ! Initialize solution vectors call setInitialCondition(nState, stateVecInit, sunvec_y, sunvec_yp) ! Call FIDACreate and FIDAInit to initialize IDA memory ida_mem = FIDACreate() - if (.not. c_associated(ida_mem)) then - print *, 'ERROR: ida_mem = NULL' - stop 1 - end if + if (.not. c_associated(ida_mem)) then; err=20; message='fidaSolver: ida_mem = NULL'; return; endif eqns_data%ida_mem = ida_mem retval = FIDASetUserData(ida_mem, c_loc(eqns_data)) - if (retval /= 0) then - print *, 'Error in FIDASetUserData, retval = ', retval, '; halting' - stop 1 - end if + if (retval /= 0) then; err=20; message='fidaSolver: error in FIDASetUserData'; return; endif t0 = 0._dp retval = FIDAInit(ida_mem, c_funloc(evalEqnsFida), t0, sunvec_y, sunvec_yp) - if (retval /= 0) then - print *, 'Error in FIDAInit, retval = ', retval, '; halting' - stop 1 - end if + if (retval /= 0) then; err=20; message='fidaSolver: error in FIDAInit'; return; endif ! set tolerances retval = FIDAWFtolerances(ida_mem, c_funloc(computWeightFida)) - if (retval /= 0) then - print *, 'Error in FIDAWFtolerances, retval = ', retval, '; halting' - stop 1 - end if + if (retval /= 0) then; err=20; message='fidaSolver: error in FIDAWFtolerances'; return; endif ! define the form of the matrix select case(ixMatrix) @@ -366,32 +349,20 @@ subroutine fidaSolver( & mu = ku; lu = kl; ! Create banded SUNMatrix for use in linear solves sunmat_A => FSUNBandMatrix(nState, mu, lu) - if (.not. associated(sunmat_A)) then - print *, 'ERROR: sunmat = NULL' - stop 1 - end if + if (.not. associated(sunmat_A)) then; err=20; message='fidaSolver: sunmat = NULL'; return; endif ! Create banded SUNLinearSolver object sunlinsol_LS => FSUNLinSol_Band(sunvec_y, sunmat_A) - if (.not. associated(sunlinsol_LS)) then - print *, 'ERROR: sunlinsol = NULL' - stop 1 - end if + if (.not. associated(sunlinsol_LS)) then; err=20; message='fidaSolver: sunlinsol = NULL'; return; endif case(ixFullMatrix) ! Create dense SUNMatrix for use in linear solves sunmat_A => FSUNDenseMatrix(nState, nState) - if (.not. associated(sunmat_A)) then - print *, 'ERROR: sunmat = NULL' - stop 1 - end if + if (.not. associated(sunmat_A)) then; err=20; message='fidaSolver: sunmat = NULL'; return; endif ! Create dense SUNLinearSolver object sunlinsol_LS => FSUNDenseLinearSolver(sunvec_y, sunmat_A) - if (.not. associated(sunlinsol_LS)) then - print *, 'ERROR: sunlinsol = NULL' - stop 1 - end if + if (.not. associated(sunlinsol_LS)) then; err=20; message='fidaSolver: sunlinsol = NULL'; return; endif ! check case default; print *, 'unable to identify option for the type of matrix'; stop 1 @@ -400,21 +371,14 @@ subroutine fidaSolver( & ! Attach the matrix and linear solver retval = FIDASetLinearSolver(ida_mem, sunlinsol_LS, sunmat_A); - if (retval /= 0) then - print *, 'Error in FIDASetLinearSolver, retval = ', retval, '; halting' - stop 1 - end if + if (retval /= 0) then; err=20; message='fidaSolver: error in FIDASetLinearSolver'; return; endif if(ixMatrix == ixFullMatrix)then ! Set the user-supplied Jacobian routine retval = FIDASetJacFn(ida_mem, c_funloc(evalJacFida)) - if (retval /= 0) then - print *, 'Error in FIDASetJacFn, retval = ', retval, '; halting' - stop 1 - end if - + if (retval /= 0) then; err=20; message='fidaSolver: error in FIDASetJacFn'; return; endif endif ! Create Newton SUNNonlinearSolver object. IDA uses a diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 033b97680..00e62cb05 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -469,7 +469,7 @@ subroutine sysSolvFida(& stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step err,cmessage) ! intent(out): error control -! if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) if (idaSucceeds)then exit else @@ -482,7 +482,7 @@ subroutine sysSolvFida(& ! check if fida is successful if( .not.idaSucceeds )then - message=trim(message)//'fida not successful' + message=trim(message)//trim(cmessage) reduceCoupledStep = .true. return endif @@ -514,7 +514,7 @@ subroutine sysSolvFida(& ! save the computed solution stateVecTrial = stateVecNew - + ! free memory deallocate(mLayerCmpress_sum) deallocate(dBaseflow_dMatric) From f80e7064bf203da77db4747a85407fbdfc9ad14a Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 16 Feb 2021 11:37:00 -0600 Subject: [PATCH 0056/1472] before seperating set function in fidaSolver --- build/source/engine/fidaSolver.f90 | 2 -- 1 file changed, 2 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 35b56a77e..d1ac18b60 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -373,8 +373,6 @@ subroutine fidaSolver( & retval = FIDASetLinearSolver(ida_mem, sunlinsol_LS, sunmat_A); if (retval /= 0) then; err=20; message='fidaSolver: error in FIDASetLinearSolver'; return; endif - - if(ixMatrix == ixFullMatrix)then ! Set the user-supplied Jacobian routine retval = FIDASetJacFn(ida_mem, c_funloc(evalJacFida)) From 0343b4648e53a212328aa233b322f56cc514695c Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 16 Feb 2021 12:35:48 -0600 Subject: [PATCH 0057/1472] setSolverParams implemented --- build/source/engine/fidaSolver.f90 | 131 ++++++++++++----------------- 1 file changed, 56 insertions(+), 75 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index d1ac18b60..853e94fcd 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -79,6 +79,7 @@ module fidaSolver_module ! privacy implicit none private::setInitialCondition + private::setSolverParams public::fidaSolver @@ -379,85 +380,14 @@ subroutine fidaSolver( & if (retval /= 0) then; err=20; message='fidaSolver: error in FIDASetJacFn'; return; endif endif - ! Create Newton SUNNonlinearSolver object. IDA uses a - ! Newton SUNNonlinearSolver by default, so it is not necessary - ! to create it and attach it. It is done here for demonstration purposes. + ! Create Newton SUNNonlinearSolver object sunnonlin_NLS => FSUNNonlinSol_Newton(sunvec_y) - if (.not. associated(sunnonlin_NLS)) then - print *, 'ERROR: sunnonlinsol = NULL' - stop 1 - end if + if (.not. associated(sunnonlin_NLS)) then; err=20; message='fidaSolver: sunnonlinsol = NULL'; return; endif ! Attach the nonlinear solver retval = FIDASetNonlinearSolver(ida_mem, sunnonlin_NLS) - if (retval /= 0) then - print *, 'Error in FIDASetNonlinearSolver, retval = ', retval, '; halting' - stop 1 - end if - - - ! Set the maximum BDF order, default = 5 - retval = FIDASetMaxOrd(ida_mem, 5) - if (retval /= 0) then - print *, 'Error in FIDASetMaxOrd, retval = ', retval, '; halting' - stop 1 - end if - - - ! Set Coeff. in the nonlinear convergence test, default = 0.33 - coef_nonlin = 0.33 - retval = FIDASetNonlinConvCoef(ida_mem, coef_nonlin) - if (retval /= 0) then - print *, 'Error in FIDASetNonlinConvCoef, retval = ', retval, '; halting' - stop 1 - end if - - - ! Set maximun number of nonliear iterations, default = 4 - retval = FIDASetMaxNonlinIters(ida_mem, 100) - if (retval /= 0) then - print *, 'Error in IDASetMaxNonlinIters, retval = ', retval, '; halting' - stop 1 - end if - - ! Set maximum number of convergence test failures, default = 10 - retval = FIDASetMaxConvFails(ida_mem, 50) - if (retval /= 0) then - print *, 'Error in FIDASetMaxConvFails, retval = ', retval, '; halting' - stop 1 - end if - - ! Set maximum number of error test failures, default = 10 - retval = FIDASetMaxErrTestFails(ida_mem, 50) - if (retval /= 0) then - print *, 'Error in FIDASetMaxErrTestFails, retval = ', retval, '; halting' - stop 1 - end if - - ! Set maximum number of steps, dafault = 500 - maxstep = 999999 - retval = FIDASetMaxNumSteps(ida_mem, maxstep) - if (retval /= 0) then - print *, 'Error in FIDASetMaxNumSteps, retval = ', retval, '; halting' - stop 1 - end if - - ! Set maximum stepsize, dafault = infinity - h_max = dt - retval = FIDASetMaxStep(ida_mem, h_max) - if (retval /= 0) then - print *, 'Error in FIDASetMaxSteps, retval = ', retval, '; halting' - stop 1 - end if - - ! Set initial stepsize - h_init = 0 - retval = FIDASetInitStep(ida_mem, h_init) - if (retval /= 0) then - print *, 'Error in FIDASetInitStep, retval = ', retval, '; halting' - stop 1 - end if + if (retval /= 0) then; err=20; message='fidaSolver: error in FIDASetNonlinearSolver'; return; endif ! enforce the solver to stop at the end of the data time step retval = FIDASetStopTime(ida_mem, dt) @@ -466,6 +396,8 @@ subroutine fidaSolver( & stop 1 end if + call setSolverParams(dt, ida_mem, retval) + ! scalling_on 1 and off 0 ! retval = FIDASetLinearSolutionScaling(ida_mem, 0) @@ -597,7 +529,7 @@ subroutine fidaSolver( & !****************************** End of Main Solver *************************************** - ! copy the output data + ! copy to output data diag_data = eqns_data%diag_data flux_data = eqns_data%flux_data deriv_data = eqns_data%deriv_data @@ -674,6 +606,55 @@ subroutine setInitialCondition(neq, y, sunvec_u, sunvec_up) end subroutine setInitialCondition +! ---------------------------------------------------------------- +! setSolverParams: routine to set paprmeters in ida solver +! ---------------------------------------------------------------- +subroutine setSolverParams(dt,ida_mem,retval) + !======= Inclusions =========== + USE, intrinsic :: iso_c_binding + USE fida_mod ! Fortran interface to IDA +implicit none + + real(dp),intent(in) :: dt ! time step + type(c_ptr),intent(inout) :: ida_mem ! IDA memory + integer(i4b),intent(out) :: retval ! return value + + real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 + integer,parameter :: max_order = 5 ! maximum BDF order, default = 5 + integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 + integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 + integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 + integer(kind = 8),parameter :: max_step = 999999 ! maximum number of steps, dafault = 500 + real(qp),parameter :: h_init = 0 ! initial stepsize + real(qp) :: h_max ! maximum stepsize, dafault = infinity + + ! Set the maximum BDF order + retval = FIDASetMaxOrd(ida_mem, max_order) + + ! Set Coeff. in the nonlinear convergence test + retval = FIDASetNonlinConvCoef(ida_mem, coef_nonlin) + + ! Set maximun number of nonliear iterations + retval = FIDASetMaxNonlinIters(ida_mem, nonlin_iter) + + ! Set maximum number of convergence test failures + retval = FIDASetMaxConvFails(ida_mem, convtest_fail) + + ! Set maximum number of error test failures + retval = FIDASetMaxErrTestFails(ida_mem, acurtest_fail) + + ! Set maximum number of steps + retval = FIDASetMaxNumSteps(ida_mem, max_step) + + ! Set maximum stepsize + h_max = dt + retval = FIDASetMaxStep(ida_mem, h_max) + + ! Set initial stepsize + retval = FIDASetInitStep(ida_mem, h_init) + +end subroutine setSolverParams + end module fidaSolver_module From a4b5f8ebfe4c52b4ad5e5bce80ab8360901dc8f6 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 16 Feb 2021 13:06:46 -0600 Subject: [PATCH 0058/1472] more edit on fidaSolver --- build/source/engine/fidaSolver.f90 | 46 +++++++++++++++++------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 853e94fcd..4a7177b15 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -366,7 +366,7 @@ subroutine fidaSolver( & if (.not. associated(sunlinsol_LS)) then; err=20; message='fidaSolver: sunlinsol = NULL'; return; endif ! check - case default; print *, 'unable to identify option for the type of matrix'; stop 1 + case default; err=20; message='fidaSolver: error in type of matrix'; return end select ! form of matrix @@ -384,25 +384,19 @@ subroutine fidaSolver( & sunnonlin_NLS => FSUNNonlinSol_Newton(sunvec_y) if (.not. associated(sunnonlin_NLS)) then; err=20; message='fidaSolver: sunnonlinsol = NULL'; return; endif - ! Attach the nonlinear solver retval = FIDASetNonlinearSolver(ida_mem, sunnonlin_NLS) if (retval /= 0) then; err=20; message='fidaSolver: error in FIDASetNonlinearSolver'; return; endif - ! enforce the solver to stop at the end of the data time step + ! Enforce the solver to stop at the end of the data time step retval = FIDASetStopTime(ida_mem, dt) - if (retval /= 0) then - print *, 'Error in FIDASetStopTime, retval = ', retval, '; halting' - stop 1 - end if + if (retval /= 0) then; err=20; message='fidaSolver: error in FIDASetStopTime'; return; endif - call setSolverParams(dt, ida_mem, retval) + ! Set solver parameters such as maximum order, number of iterations, ... + call setSolverParams(dt, ida_mem, retval) + if (retval /= 0) then; err=20; message='fidaSolver: error in setSolverParams'; return; endif - ! scalling_on 1 and off 0 - ! retval = FIDASetLinearSolutionScaling(ida_mem, 0) - - tret(1) = t0 - ! need the following values for the first substep + ! need the following values for the first substep eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) eqns_data%scalarCanopyIcePrev = prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) @@ -412,12 +406,12 @@ subroutine fidaSolver( & eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) - - + !********************************************************************************** !****************************** Main Solver *************************************** !************************* loop on one_step mode ********************************** - !********************************************************************************** + !********************************************************************************** + tret(1) = t0 do while(tret(1) < dt) ! call IDASolve retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) @@ -615,9 +609,9 @@ subroutine setSolverParams(dt,ida_mem,retval) USE fida_mod ! Fortran interface to IDA implicit none - real(dp),intent(in) :: dt ! time step - type(c_ptr),intent(inout) :: ida_mem ! IDA memory - integer(i4b),intent(out) :: retval ! return value + real(dp),intent(in) :: dt ! time step + type(c_ptr),intent(inout) :: ida_mem ! IDA memory + integer(i4b),intent(out) :: retval ! return value real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 integer,parameter :: max_order = 5 ! maximum BDF order, default = 5 @@ -629,29 +623,41 @@ subroutine setSolverParams(dt,ida_mem,retval) real(qp) :: h_max ! maximum stepsize, dafault = infinity ! Set the maximum BDF order - retval = FIDASetMaxOrd(ida_mem, max_order) + retval = FIDASetMaxOrd(ida_mem, max_order) + if (retval /= 0) return ! Set Coeff. in the nonlinear convergence test retval = FIDASetNonlinConvCoef(ida_mem, coef_nonlin) + if (retval /= 0) return ! Set maximun number of nonliear iterations retval = FIDASetMaxNonlinIters(ida_mem, nonlin_iter) + if (retval /= 0) return ! Set maximum number of convergence test failures retval = FIDASetMaxConvFails(ida_mem, convtest_fail) + if (retval /= 0) return ! Set maximum number of error test failures retval = FIDASetMaxErrTestFails(ida_mem, acurtest_fail) + if (retval /= 0) return ! Set maximum number of steps retval = FIDASetMaxNumSteps(ida_mem, max_step) + if (retval /= 0) return ! Set maximum stepsize h_max = dt retval = FIDASetMaxStep(ida_mem, h_max) + if (retval /= 0) return ! Set initial stepsize retval = FIDASetInitStep(ida_mem, h_init) + if (retval /= 0) return + + ! scalling on 1 and off 0 + ! retval = FIDASetLinearSolutionScaling(ida_mem, 0) + ! if (retval /= 0) return end subroutine setSolverParams From 71a6b62b50e43e0187e3145df19149546ddfe4f8 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 16 Feb 2021 22:42:02 -0600 Subject: [PATCH 0059/1472] works well for laugh test not for wrrs --- build/source/engine/fidaSolver.f90 | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 4a7177b15..15a766b90 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -414,14 +414,11 @@ subroutine fidaSolver( & tret(1) = t0 do while(tret(1) < dt) ! call IDASolve - retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) + retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) + ! if( retval < 0 ) return ! get the last stepsize retval = FIDAGetLastStep(ida_mem, dt_last) - if (retval /= 0) then - print *, 'Error in FIDAGetLastStep, retval = ', retval, '; halting' - stop 1 - end if ! compute the flux and the residual vector for a given state vector call eval8summaFida(& From c8896ffb3acbfaf96ba0f513d3165043384489c4 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 17 Feb 2021 18:39:17 -0600 Subject: [PATCH 0060/1472] firstFluxCall in eval8summaFida false to make int work for umpqua --- build/source/engine/eval8summaFida.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 4a3fd0203..b217684b7 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -470,7 +470,7 @@ subroutine eval8summaFida(& ! save the number of flux calls per time step indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) + 1 - firstFluxCall = .true. + firstFluxCall = .false. ! compute the fluxes for a given state vector call computFlux(& ! input-output: model control From 6baccb69d9e78d9c30dda17490af4cf7bb781fb0 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 25 Feb 2021 22:45:46 -0600 Subject: [PATCH 0061/1472] minor edit --- build/source/engine/fidaSolver.f90 | 2 +- build/source/engine/sysSolvFida.f90 | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 15a766b90..04cd912b0 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -611,7 +611,7 @@ subroutine setSolverParams(dt,ida_mem,retval) integer(i4b),intent(out) :: retval ! return value real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 - integer,parameter :: max_order = 5 ! maximum BDF order, default = 5 + integer,parameter :: max_order = 1 ! maximum BDF order, default = 5 integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 00e62cb05..dd69d5ee6 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -412,8 +412,8 @@ subroutine sysSolvFida(& if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) ! just for experiment - atol = 1e-6 - rtol = 1e-6 + atol = 1e-8 + rtol = 1e-8 !------------------- ! * solving F(y,y') = 0 by FIDA. Here, y is the state vector From 81b62d2c3b3fdb34e15e16c77a5798bf73dc27ba Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 11 Mar 2021 10:53:49 -0600 Subject: [PATCH 0062/1472] minor edit --- build/source/engine/fidaSolver.f90 | 2 +- build/source/engine/sysSolvFida.f90 | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 04cd912b0..15a766b90 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -611,7 +611,7 @@ subroutine setSolverParams(dt,ida_mem,retval) integer(i4b),intent(out) :: retval ! return value real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 - integer,parameter :: max_order = 1 ! maximum BDF order, default = 5 + integer,parameter :: max_order = 5 ! maximum BDF order, default = 5 integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index dd69d5ee6..0de93c4df 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -412,8 +412,8 @@ subroutine sysSolvFida(& if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) ! just for experiment - atol = 1e-8 - rtol = 1e-8 +! atol = 1e-6 +! rtol = 1e-6 !------------------- ! * solving F(y,y') = 0 by FIDA. Here, y is the state vector From c060fc38019a3c53547fa54ad85fca210ff1185a Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 13 Apr 2021 19:30:38 -0600 Subject: [PATCH 0063/1472] Cp analytical in eval8summaFidaand computeResidFida --- build/source/engine/computResidFida.f90 | 4 +- build/source/engine/eval8summaFida.f90 | 85 +++++-------------------- 2 files changed, 18 insertions(+), 71 deletions(-) diff --git a/build/source/engine/computResidFida.f90 b/build/source/engine/computResidFida.f90 index ccecc23db..973268a03 100644 --- a/build/source/engine/computResidFida.f90 +++ b/build/source/engine/computResidFida.f90 @@ -206,7 +206,7 @@ subroutine computResidFida(& ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision ! --> energy balance if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*scalarCanairTempPrime - ( fVec(ixCasNrg) + rAdd(ixCasNrg) ) - if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = scalarCanopyEnthalpyPrime - fVec(ixVegNrg) + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime - ( fVec(ixVegNrg) + rAdd(ixVegNrg) ) ! --> mass balance if(ixVegHyd/=integerMissing)then scalarCanopyHydPrime = merge(scalarCanopyWatPrime, scalarCanopyLiqPrime, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) @@ -216,7 +216,7 @@ subroutine computResidFida(& ! compute the residual vector for the snow and soil sub-domains for energy if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - rVec( ixSnowSoilNrg(iLayer) ) = mLayerEnthalpyPrime(iLayer) - fVec( ixSnowSoilNrg(iLayer) ) + rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) ) * mLayerTempPrime(iLayer) - ( fVec( ixSnowSoilNrg(iLayer) ) + rAdd( ixSnowSoilNrg(iLayer) ) ) end do ! looping through non-missing energy state variables in the snow+soil domain endif diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index b217684b7..8c77291a8 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -149,7 +149,7 @@ subroutine eval8summaFida(& USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy USE soilCmpresFida_module, only:soilCmpresFida ! compute soil compression USE computFlux_module, only:computFlux ! compute fluxes given a state vector - USE computHeatCap_module,only:computHeatCap ! compute diagnostic energy variables -- thermal conductivity and heat capacity + USE computHeatCap_module,only:computHeatCapAnalytic ! compute heat capacity USE computHeatCap_module, only:computStatMult USE computResidFida_module,only:computResidFida ! compute residuals given a state vector USE computThermConduct_module,only:computThermConduct @@ -532,60 +532,25 @@ subroutine eval8summaFida(& dCompress_dPsi, & ! intent(inout): derivative in compressibility w.r.t. matric head (m-1) err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! compute H_T - call t2enthalpy_T(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value for canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) - ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! *** compute volumetric heat capacity C_p = dH_T/dT - call computHeatCap(& + + ! *** compute volumetric heat capacity C_p + call computHeatCapAnalytic(& ! input: control variables - nLayers, & ! intent(in): number of layers (soil+snow) - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) - ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - diag_data, & ! intent(in): model diagnostic variables for a local HRU + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value for canopy ice content (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) - scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial temperature - mLayerTempPrev, & ! intent(in): previous temperature - mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil - mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil + scalarCanopyIceTrial, & ! intent(in) + scalarCanopyLiqTrial, & ! intent(in) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + ! input data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices ! output - heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil + heatCapVegTrial, & + mLayerHeatCapTrial, & ! output: error control - err,message) ! intent(out): error control + err,message) ! intent(out): error control ! compute multiplier of state vector call computStatMult(& @@ -625,24 +590,6 @@ subroutine eval8summaFida(& do concurrent (iLayer=1:nLayers) mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur end do - - ! H' = Cp*T' - rho*L*(theta_ice)' - call computEnthalpyPrime(& - ! input - computeVegFlux, & - indx_data, & - nLayers, & - canopyDepth, & ! intent(in): canopy depth (m) - scalarCanopyTempPrime, & ! intent(in): prime value for the temperature of the vegetation canopy (K) - scalarCanopyIcePrime, & ! intent(in): prime value for the ice on the vegetation canopy (kg m-2) - mLayerTempPrime, & - mLayerVolFracIcePrime, & - heatCapVegTrial, & - mLayerHeatCapTrial, & - ! output - scalarCanopyEnthalpyPrime, & - mLayerEnthalpyPrime & - ) ! compute the residual vector From 875e7f0fc7fab724f000f2f0029f78c791ee3ca4 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 13 Apr 2021 23:26:19 -0600 Subject: [PATCH 0064/1472] computCm implemented --- build/source/engine/computHeatCap.f90 | 117 +++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 1 deletion(-) diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index 026f31405..9a79c7ccf 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -34,6 +34,7 @@ module computHeatCap_module ! physical constants USE multiconst,only:& + Tfreeze, & ! freezing point of water (K) iden_air, & ! intrinsic density of air (kg m-3) iden_ice, & ! intrinsic density of ice (kg m-3) iden_water, & ! intrinsic density of water (kg m-3) @@ -68,6 +69,7 @@ module computHeatCap_module public::computHeatCap public::computStatMult public::computHeatCapAnalytic +public::computCm contains @@ -140,7 +142,6 @@ subroutine computHeatCap(& associate(& ! input: coordinate variables nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) ! input: heat capacity and thermal conductivity specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) @@ -397,6 +398,120 @@ subroutine computHeatCapAnalytic(& end associate end subroutine computHeatCapAnalytic + + ! ********************************************************************************************************** + ! public subroutine computCm + ! ********************************************************************************************************** + subroutine computCm(& + nLayers, & ! intent(in): number of layers (soil+snow) + ! input: control variables + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) + ! input: state variables + scalarCanopyTemp, & ! intent(in) + mLayerTemp, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerMatricHead, & ! intent(in) + ! input data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + ! output + scalarCanopyCm, & + mLayerCm, & + ! output: error control + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! provide access to external subroutines + USE snow_utils_module,only:tcond_snow ! compute thermal conductivity of snow + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! input: model control + integer(i4b),intent(in) :: nLayers + logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + real(dp),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) + real(dp),intent(in) :: scalarCanopyTemp ! value of canopy ice content (kg m-2) + real(dp),intent(in) :: mLayerTemp(:) ! vector of volumetric liquid water content (-) + real(dp),intent(in) :: mLayerMatricHead(:) ! vector of total water matric potential (m) + ! input/output: data structures + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model layer indices + ! output: error control + real(qp),intent(out) :: scalarCanopyCm + real(qp),intent(out) :: mLayerCm(:) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: iSoil ! index of soil layer + real(dp) :: scalarCanopyG1 + real(dp) :: scalarCanopyG2 + real(dp) :: mLayerG1(nLayers) + real(dp) :: mLayerG2(nLayers) + real(dp) :: Tcrit ! temperature where all water is unfrozen (K) + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! associate variables in data structure + associate(& + ! input: coordinate variables + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers + layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) +snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) ! end associate statement + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="computCm/" + + ! initialize the soil layer + iSoil=integerMissing + + ! compute Cm of vegetation + if(computeVegFlux)then + scalarCanopyG2 = scalarCanopyTemp - Tfreeze + scalarCanopyG1 = (1._dp/snowfrz_scale) * atan(snowfrz_scale * scalarCanopyG2) + if(scalarCanopyTemp < Tfreeze)then + scalarCanopyCm = iden_water * Cp_water * scalarCanopyG1 + iden_water * Cp_ice * (scalarCanopyG2 - scalarCanopyG1) + else + scalarCanopyCm = iden_water * Cp_water * scalarCanopyG2 + end if + end if + + ! loop through layers + do iLayer=1,nLayers + + ! get the soil layer + if(iLayer>nSnow) iSoil = iLayer-nSnow + + ! ***** + ! * compute Cm of of each layer + ! ******************************************************************* + select case(layerType(iLayer)) + ! * soil + case(iname_soil) + mLayerG2(iLayer) = mLayerTemp(iLayer) - Tfreeze + Tcrit = crit_soilT( mLayerMatricHead(iSoil) ) + if( mLayerTemp(iLayer) < Tcrit)then + mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air) * mLayerG2(iLayer) + else + mLayerCm(iLayer) = (iden_water * Cp_water - iden_air * Cp_air) * mLayerG2(iLayer) + end if + + case(iname_snow) + mLayerG2(iLayer) = mLayerTemp(iLayer) - Tfreeze + mLayerG1(iLayer) = (1._dp/snowfrz_scale) * atan(snowfrz_scale * mLayerG2(iLayer)) + mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( mLayerG2(iLayer) - mLayerG1(iLayer) ) & + + (iden_water * Cp_water - iden_air * Cp_air) * mLayerG1(iLayer) + + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute Cm'; return + end select + + end do ! looping through layers + !pause + + ! end association to variables in the data structure + end associate + + end subroutine computCm end module computHeatCap_module From c5e87f8467f1feb83466e3d72fb8e40e0339912e Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 13 Apr 2021 23:37:49 -0600 Subject: [PATCH 0065/1472] computCm edited --- build/source/engine/computHeatCap.f90 | 37 ++++++++++++--------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index 9a79c7ccf..2afaebe29 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -403,10 +403,8 @@ end subroutine computHeatCapAnalytic ! public subroutine computCm ! ********************************************************************************************************** subroutine computCm(& - nLayers, & ! intent(in): number of layers (soil+snow) ! input: control variables computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) ! input: state variables scalarCanopyTemp, & ! intent(in) mLayerTemp, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) @@ -415,8 +413,8 @@ subroutine computCm(& mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices ! output - scalarCanopyCm, & - mLayerCm, & + scalarCanopyCm, & ! intent(out): Cm for vegetation + mLayerCm, & ! intent(out): Cm for soil and snow ! output: error control err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------------- @@ -425,9 +423,7 @@ subroutine computCm(& USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: model control - integer(i4b),intent(in) :: nLayers logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - real(dp),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) real(dp),intent(in) :: scalarCanopyTemp ! value of canopy ice content (kg m-2) real(dp),intent(in) :: mLayerTemp(:) ! vector of volumetric liquid water content (-) real(dp),intent(in) :: mLayerMatricHead(:) ! vector of total water matric potential (m) @@ -444,10 +440,8 @@ subroutine computCm(& character(LEN=256) :: cmessage ! error message of downwind routine integer(i4b) :: iLayer ! index of model layer integer(i4b) :: iSoil ! index of soil layer - real(dp) :: scalarCanopyG1 - real(dp) :: scalarCanopyG2 - real(dp) :: mLayerG1(nLayers) - real(dp) :: mLayerG2(nLayers) + real(dp) :: g1 + real(dp) :: g2 real(dp) :: Tcrit ! temperature where all water is unfrozen (K) ! -------------------------------------------------------------------------------------------------------------------------------- @@ -455,6 +449,7 @@ subroutine computCm(& associate(& ! input: coordinate variables nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) ) ! end associate statement @@ -467,12 +462,12 @@ subroutine computCm(& ! compute Cm of vegetation if(computeVegFlux)then - scalarCanopyG2 = scalarCanopyTemp - Tfreeze - scalarCanopyG1 = (1._dp/snowfrz_scale) * atan(snowfrz_scale * scalarCanopyG2) + g2 = scalarCanopyTemp - Tfreeze + g1 = (1._dp/snowfrz_scale) * atan(snowfrz_scale * g2) if(scalarCanopyTemp < Tfreeze)then - scalarCanopyCm = iden_water * Cp_water * scalarCanopyG1 + iden_water * Cp_ice * (scalarCanopyG2 - scalarCanopyG1) + scalarCanopyCm = iden_water * Cp_water * g1 + iden_water * Cp_ice * (g2 - g1) else - scalarCanopyCm = iden_water * Cp_water * scalarCanopyG2 + scalarCanopyCm = iden_water * Cp_water * g2 end if end if @@ -488,19 +483,19 @@ subroutine computCm(& select case(layerType(iLayer)) ! * soil case(iname_soil) - mLayerG2(iLayer) = mLayerTemp(iLayer) - Tfreeze + g2 = mLayerTemp(iLayer) - Tfreeze Tcrit = crit_soilT( mLayerMatricHead(iSoil) ) if( mLayerTemp(iLayer) < Tcrit)then - mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air) * mLayerG2(iLayer) + mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air) * g2 else - mLayerCm(iLayer) = (iden_water * Cp_water - iden_air * Cp_air) * mLayerG2(iLayer) + mLayerCm(iLayer) = (iden_water * Cp_water - iden_air * Cp_air) * g2 end if case(iname_snow) - mLayerG2(iLayer) = mLayerTemp(iLayer) - Tfreeze - mLayerG1(iLayer) = (1._dp/snowfrz_scale) * atan(snowfrz_scale * mLayerG2(iLayer)) - mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( mLayerG2(iLayer) - mLayerG1(iLayer) ) & - + (iden_water * Cp_water - iden_air * Cp_air) * mLayerG1(iLayer) + g2 = mLayerTemp(iLayer) - Tfreeze + g1 = (1._dp/snowfrz_scale) * atan(snowfrz_scale * g2) + mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( g2 - g1 ) & + + (iden_water * Cp_water - iden_air * Cp_air) * g1 case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute Cm'; return end select From fdf9114a5baf9423d0d43442e36bfdc9e815ad40 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 13 Apr 2021 23:46:34 -0600 Subject: [PATCH 0066/1472] computCm in eval8summaFida --- build/source/engine/eval8summaFida.f90 | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 8c77291a8..de0dc8a9e 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -150,6 +150,7 @@ subroutine eval8summaFida(& USE soilCmpresFida_module, only:soilCmpresFida ! compute soil compression USE computFlux_module, only:computFlux ! compute fluxes given a state vector USE computHeatCap_module,only:computHeatCapAnalytic ! compute heat capacity + USE computHeatCap_module,only:computCm USE computHeatCap_module, only:computStatMult USE computResidFida_module,only:computResidFida ! compute residuals given a state vector USE computThermConduct_module,only:computThermConduct @@ -254,6 +255,8 @@ subroutine eval8summaFida(& real(qp) :: scalarCanopyEnthalpyPrime logical(lgt) :: firstFluxCall integer(i4b) :: ixSaturation ! index of the lowest saturated layer + real(dp) :: scalarCanopyCmTrial + real(dp),dimension(nLayers) :: mLayerCmTrial ! -------------------------------------------------------------------------------------------------------------------------------- @@ -585,6 +588,22 @@ subroutine eval8summaFida(& err,message) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if + ! compute C_m + call computCm(& + ! input: control variables + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + ! input: state variables + scalarCanopyTempTrial, & ! intent(in) + mLayerTempTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerMatricHeadTrial, & ! intent(in) + ! input data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + ! output + scalarCanopyCmTrial, & ! intent(out): Cm for vegetation + mLayerCmTrial, & ! intent(out): Cm for soil and snow + err,message) ! intent(out): error control + ! to conserve energy compute finite difference approximation of (theta_ice)' scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur do concurrent (iLayer=1:nLayers) From 9a2aeb2a1e354b21da1c86d59b2beaace3e3f970 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 13 Apr 2021 23:57:32 -0600 Subject: [PATCH 0067/1472] Cm in computResidFida --- build/source/engine/computResidFida.f90 | 14 +++++++------- build/source/engine/eval8summaFida.f90 | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/build/source/engine/computResidFida.f90 b/build/source/engine/computResidFida.f90 index 973268a03..06bf654ec 100644 --- a/build/source/engine/computResidFida.f90 +++ b/build/source/engine/computResidFida.f90 @@ -78,8 +78,8 @@ subroutine computResidFida(& mLayerVolFracIcePrime, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) mLayerVolFracWatPrime, & mLayerVolFracLiqPrime, & - scalarCanopyEnthalpyPrime, & - mLayerEnthalpyPrime, & ! intent(in) + scalarCanopyCmTrial, & + mLayerCmTrial, & ! intent(in) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -112,9 +112,9 @@ subroutine computResidFida(& real(dp),intent(in) :: scalarCanopyLiqPrime real(dp),intent(in) :: mLayerVolFracIcePrime(:) ! trial value for volumetric fraction of ice (-) real(dp),intent(in) :: mLayerVolFracLiqPrime(:) - real(dp),intent(in) :: mLayerVolFracWatPrime(:) - real(qp),intent(in) :: scalarCanopyEnthalpyPrime - real(qp),intent(in) :: mLayerEnthalpyPrime(:) + real(dp),intent(in) :: mLayerVolFracWatPrime(:) + real(qp),intent(in) :: scalarCanopyCmTrial + real(qp),intent(in) :: mLayerCmTrial(:) ! input: data structures type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU @@ -206,7 +206,7 @@ subroutine computResidFida(& ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision ! --> energy balance if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*scalarCanairTempPrime - ( fVec(ixCasNrg) + rAdd(ixCasNrg) ) - if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime - ( fVec(ixVegNrg) + rAdd(ixVegNrg) ) + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime + scalarCanopyCmTrial * scalarCanopyWatPrime - ( fVec(ixVegNrg) + rAdd(ixVegNrg) ) ! --> mass balance if(ixVegHyd/=integerMissing)then scalarCanopyHydPrime = merge(scalarCanopyWatPrime, scalarCanopyLiqPrime, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) @@ -216,7 +216,7 @@ subroutine computResidFida(& ! compute the residual vector for the snow and soil sub-domains for energy if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) ) * mLayerTempPrime(iLayer) - ( fVec( ixSnowSoilNrg(iLayer) ) + rAdd( ixSnowSoilNrg(iLayer) ) ) + rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) ) * mLayerTempPrime(iLayer) + mLayerCmTrial(iLayer) * mLayerVolFracWatPrime(iLayer) - ( fVec( ixSnowSoilNrg(iLayer) ) + rAdd( ixSnowSoilNrg(iLayer) ) ) end do ! looping through non-missing energy state variables in the snow+soil domain endif diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index de0dc8a9e..c92d71450 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -634,8 +634,8 @@ subroutine eval8summaFida(& mLayerVolFracIcePrime, & ! intent(in): Prime value for the volumetric ice in each snow and soil layer (-) mLayerVolFracWatPrime, & mLayerVolFracLiqPrime, & - scalarCanopyEnthalpyPrime, & ! intent(in) derivarive of enthalpy of vegetation canopy - mLayerEnthalpyPrime, & ! intent(in) derivative of enthalpy of soil and snow + scalarCanopyCmTrial, & ! intent(in) Cm of vegetation canopy + mLayerCmTrial, & ! intent(in) Cm of soil and snow ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU From c5861ac7c94ade6a2a8544e605cdfd51816ebc0a Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 15 Apr 2021 23:18:37 -0600 Subject: [PATCH 0068/1472] fixed the issue with vegentation --- build/source/engine/computHeatCap.f90 | 5 +++-- build/source/engine/computResidFida.f90 | 2 +- build/source/engine/sysSolvFida.f90 | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index 2afaebe29..4b4830093 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -461,13 +461,14 @@ subroutine computCm(& iSoil=integerMissing ! compute Cm of vegetation + ! Note that scalarCanopyCm/iden_water is computed if(computeVegFlux)then g2 = scalarCanopyTemp - Tfreeze g1 = (1._dp/snowfrz_scale) * atan(snowfrz_scale * g2) if(scalarCanopyTemp < Tfreeze)then - scalarCanopyCm = iden_water * Cp_water * g1 + iden_water * Cp_ice * (g2 - g1) + scalarCanopyCm = Cp_water * g1 + Cp_ice * (g2 - g1) else - scalarCanopyCm = iden_water * Cp_water * g2 + scalarCanopyCm = Cp_water * g2 end if end if diff --git a/build/source/engine/computResidFida.f90 b/build/source/engine/computResidFida.f90 index 06bf654ec..080b2d811 100644 --- a/build/source/engine/computResidFida.f90 +++ b/build/source/engine/computResidFida.f90 @@ -206,7 +206,7 @@ subroutine computResidFida(& ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision ! --> energy balance if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*scalarCanairTempPrime - ( fVec(ixCasNrg) + rAdd(ixCasNrg) ) - if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime + scalarCanopyCmTrial * scalarCanopyWatPrime - ( fVec(ixVegNrg) + rAdd(ixVegNrg) ) + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime + scalarCanopyCmTrial * scalarCanopyWatPrime/canopyDepth - ( fVec(ixVegNrg) + rAdd(ixVegNrg) ) ! --> mass balance if(ixVegHyd/=integerMissing)then scalarCanopyHydPrime = merge(scalarCanopyWatPrime, scalarCanopyLiqPrime, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 0de93c4df..00e62cb05 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -412,8 +412,8 @@ subroutine sysSolvFida(& if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) ! just for experiment -! atol = 1e-6 -! rtol = 1e-6 + atol = 1e-6 + rtol = 1e-6 !------------------- ! * solving F(y,y') = 0 by FIDA. Here, y is the state vector From 3c0470760ce34b4a27bd1e2857a6370e5276f158 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 20 Apr 2021 19:38:22 -0600 Subject: [PATCH 0069/1472] temporarily in Cm=0 and not finite diff --- build/source/engine/eval8summaFida.f90 | 11 +++++++---- build/source/engine/fidaSolver.f90 | 2 +- build/source/engine/updatStateFida.f90 | 4 ++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index c92d71450..e43c0d571 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -605,10 +605,13 @@ subroutine eval8summaFida(& err,message) ! intent(out): error control ! to conserve energy compute finite difference approximation of (theta_ice)' - scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur - do concurrent (iLayer=1:nLayers) - mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur - end do + ! scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur + ! do concurrent (iLayer=1:nLayers) + ! mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur + ! end do + + scalarCanopyCmTrial = 0._dp + mLayerCmTrial = 0._dp ! compute the residual vector diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 15a766b90..04cd912b0 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -611,7 +611,7 @@ subroutine setSolverParams(dt,ida_mem,retval) integer(i4b),intent(out) :: retval ! return value real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 - integer,parameter :: max_order = 5 ! maximum BDF order, default = 5 + integer,parameter :: max_order = 1 ! maximum BDF order, default = 5 integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 diff --git a/build/source/engine/updatStateFida.f90 b/build/source/engine/updatStateFida.f90 index 576ee2828..ac6bbd884 100644 --- a/build/source/engine/updatStateFida.f90 +++ b/build/source/engine/updatStateFida.f90 @@ -183,9 +183,9 @@ subroutine updateSoilFida(& ! compute fractional **volume** of total water (liquid plus ice) mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) -! mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime + mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime ! mLayerVolFracWatPrime = mLayerMatricHeadPrime * (mLayerVolFracWat - mLayerVolFracWatPrev) / (mLayerMatricHead - mLayerMatricHeadPrev + verySmall) - mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev) / dt_cur + ! mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev) / dt_cur From bd2954d5266efcf01215edf314d099b6f4077cda Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 20 Apr 2021 20:41:46 -0600 Subject: [PATCH 0070/1472] back to previous --- build/source/engine/eval8summaFida.f90 | 12 ++++++------ build/source/engine/updatStateFida.f90 | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index e43c0d571..f35f8f8c4 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -605,13 +605,13 @@ subroutine eval8summaFida(& err,message) ! intent(out): error control ! to conserve energy compute finite difference approximation of (theta_ice)' - ! scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur - ! do concurrent (iLayer=1:nLayers) - ! mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur - ! end do + scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur + do concurrent (iLayer=1:nLayers) + mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur + end do - scalarCanopyCmTrial = 0._dp - mLayerCmTrial = 0._dp + ! scalarCanopyCmTrial = 0._dp + ! mLayerCmTrial = 0._dp ! compute the residual vector diff --git a/build/source/engine/updatStateFida.f90 b/build/source/engine/updatStateFida.f90 index ac6bbd884..0f14c25d9 100644 --- a/build/source/engine/updatStateFida.f90 +++ b/build/source/engine/updatStateFida.f90 @@ -183,9 +183,9 @@ subroutine updateSoilFida(& ! compute fractional **volume** of total water (liquid plus ice) mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime + ! mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime ! mLayerVolFracWatPrime = mLayerMatricHeadPrime * (mLayerVolFracWat - mLayerVolFracWatPrev) / (mLayerMatricHead - mLayerMatricHeadPrev + verySmall) - ! mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev) / dt_cur + mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev) / dt_cur From fe1974e8c93774de6c25df084054328aefe10307 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 20 Apr 2021 22:47:28 -0600 Subject: [PATCH 0071/1472] if statement to take out Cp and Cm --- build/source/engine/eval8summaFida.f90 | 16 ++++++++++------ build/source/engine/fidaSolver.f90 | 2 +- build/source/engine/sysSolvFida.f90 | 4 ++-- build/source/engine/updatStateFida.f90 | 2 +- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index f35f8f8c4..0fd86e9fc 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -535,6 +535,8 @@ subroutine eval8summaFida(& dCompress_dPsi, & ! intent(inout): derivative in compressibility w.r.t. matric head (m-1) err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + if(1==0)then ! *** compute volumetric heat capacity C_p call computHeatCapAnalytic(& @@ -605,13 +607,15 @@ subroutine eval8summaFida(& err,message) ! intent(out): error control ! to conserve energy compute finite difference approximation of (theta_ice)' - scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur - do concurrent (iLayer=1:nLayers) - mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur - end do + ! scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur + ! do concurrent (iLayer=1:nLayers) + ! mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur + ! end do + + end if - ! scalarCanopyCmTrial = 0._dp - ! mLayerCmTrial = 0._dp + scalarCanopyCmTrial = 0._dp + mLayerCmTrial = 0._dp ! compute the residual vector diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 04cd912b0..60d78198e 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -610,7 +610,7 @@ subroutine setSolverParams(dt,ida_mem,retval) type(c_ptr),intent(inout) :: ida_mem ! IDA memory integer(i4b),intent(out) :: retval ! return value - real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 + real(qp),parameter :: coef_nonlin = 1e-6 ! Coeff. in the nonlinear convergence test, default = 0.33 integer,parameter :: max_order = 1 ! maximum BDF order, default = 5 integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 00e62cb05..f8baece36 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -412,8 +412,8 @@ subroutine sysSolvFida(& if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) ! just for experiment - atol = 1e-6 - rtol = 1e-6 + atol = 1e-4 + rtol = 1e-4 !------------------- ! * solving F(y,y') = 0 by FIDA. Here, y is the state vector diff --git a/build/source/engine/updatStateFida.f90 b/build/source/engine/updatStateFida.f90 index 0f14c25d9..efac13b58 100644 --- a/build/source/engine/updatStateFida.f90 +++ b/build/source/engine/updatStateFida.f90 @@ -184,7 +184,7 @@ subroutine updateSoilFida(& ! compute fractional **volume** of total water (liquid plus ice) mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) ! mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime -! mLayerVolFracWatPrime = mLayerMatricHeadPrime * (mLayerVolFracWat - mLayerVolFracWatPrev) / (mLayerMatricHead - mLayerMatricHeadPrev + verySmall) + ! mLayerVolFracWatPrime = mLayerMatricHeadPrime * (mLayerVolFracWat - mLayerVolFracWatPrev) / (mLayerMatricHead - mLayerMatricHeadPrev + verySmall) mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev) / dt_cur From b91529a38efd76df0685e44cad8660966cb1af42 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 20 Apr 2021 23:28:29 -0600 Subject: [PATCH 0072/1472] just tol 1e-6 --- build/source/engine/fidaSolver.f90 | 2 +- build/source/engine/sysSolvFida.f90 | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 60d78198e..5e289ceeb 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -610,7 +610,7 @@ subroutine setSolverParams(dt,ida_mem,retval) type(c_ptr),intent(inout) :: ida_mem ! IDA memory integer(i4b),intent(out) :: retval ! return value - real(qp),parameter :: coef_nonlin = 1e-6 ! Coeff. in the nonlinear convergence test, default = 0.33 + real(qp),parameter :: coef_nonlin = 1e-4 ! Coeff. in the nonlinear convergence test, default = 0.33 integer,parameter :: max_order = 1 ! maximum BDF order, default = 5 integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index f8baece36..00e62cb05 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -412,8 +412,8 @@ subroutine sysSolvFida(& if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) ! just for experiment - atol = 1e-4 - rtol = 1e-4 + atol = 1e-6 + rtol = 1e-6 !------------------- ! * solving F(y,y') = 0 by FIDA. Here, y is the state vector From 956d5ccc89ae45147795f84b31654c4f78ef7998 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 23 Apr 2021 12:03:08 -0600 Subject: [PATCH 0073/1472] minor edit --- build/source/engine/fidaSolver.f90 | 4 ++-- build/source/engine/updatStateFida.f90 | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 5e289ceeb..15a766b90 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -610,8 +610,8 @@ subroutine setSolverParams(dt,ida_mem,retval) type(c_ptr),intent(inout) :: ida_mem ! IDA memory integer(i4b),intent(out) :: retval ! return value - real(qp),parameter :: coef_nonlin = 1e-4 ! Coeff. in the nonlinear convergence test, default = 0.33 - integer,parameter :: max_order = 1 ! maximum BDF order, default = 5 + real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 + integer,parameter :: max_order = 5 ! maximum BDF order, default = 5 integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 diff --git a/build/source/engine/updatStateFida.f90 b/build/source/engine/updatStateFida.f90 index efac13b58..34d30b4b8 100644 --- a/build/source/engine/updatStateFida.f90 +++ b/build/source/engine/updatStateFida.f90 @@ -183,7 +183,7 @@ subroutine updateSoilFida(& ! compute fractional **volume** of total water (liquid plus ice) mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - ! mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime + ! mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime ! mLayerVolFracWatPrime = mLayerMatricHeadPrime * (mLayerVolFracWat - mLayerVolFracWatPrev) / (mLayerMatricHead - mLayerMatricHeadPrev + verySmall) mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev) / dt_cur From defef1195a7851d34a4ee7e6a0ae8a955b9704b9 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sat, 24 Apr 2021 12:01:43 -0600 Subject: [PATCH 0074/1472] tempPrintFlag in globalData --- build/source/driver/summa_driver.f90 | 2 ++ build/source/dshare/globalData.f90 | 1 + build/source/engine/fidaSolver.f90 | 9 +++++++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 22e864b13..17ce05fa2 100755 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -39,6 +39,7 @@ program summa_driver USE summa_util, only: handle_err ! used to process errors ! global data USE globalData, only: numtim ! number of model time steps +USE globalData, only: tempPrintFlag implicit none ! ***************************************************************************** @@ -89,6 +90,7 @@ program summa_driver ! read model forcing data call summa_readForcing(modelTimeStep, summa1_struc(n), err, message) call handle_err(err, message) +! if(iStep >= 4524) tempPrintFlag=.true. print *, 'step ---> ', iStep ! run the summa physics for one time step call summa_runPhysics(modelTimeStep, summa1_struc(n), err, message) diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 index e5d5eca8d..92f2a0098 100755 --- a/build/source/dshare/globalData.f90 +++ b/build/source/dshare/globalData.f90 @@ -306,6 +306,7 @@ MODULE globalData integer(i4b),save,public :: urbanVegCategory ! vegetation category for urban areas logical(lgt),save,public :: doJacobian=.false. ! flag to compute the Jacobian logical(lgt),save,public :: globalPrintFlag=.false. ! flag to compute the Jacobian + logical(lgt),save,public :: tempPrintFlag=.false. integer(i4b),save,public :: chunksize=1024 ! chunk size for the netcdf read/write integer(i4b),save,public :: outputPrecision=nf90_double ! variable type diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 15a766b90..d0f0b5471 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -48,7 +48,7 @@ module fidaSolver_module USE globalData,only:diag_meta ! metadata on the model diagnostic variables USE globalData,only:prog_meta ! metadata on the model prognostic variables USE globalData,only:deriv_meta ! metadata on the model derivatives - +USE globalData, only: tempPrintFlag ! constants USE multiconst,only:& LH_fus, & ! latent heat of fusion (J K-1) @@ -476,6 +476,11 @@ subroutine fidaSolver( & rVec, & ! intent(out): residual vector eqns_data%err,eqns_data%message) ! intent(out): error control +! if(tempPrintFlag)then +! print *, 'rVec = ', rVec +! print *, '----------------------------------------------------------------' +! end if + select case(ixQuadrature) ! sum of flux @@ -611,7 +616,7 @@ subroutine setSolverParams(dt,ida_mem,retval) integer(i4b),intent(out) :: retval ! return value real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 - integer,parameter :: max_order = 5 ! maximum BDF order, default = 5 + integer,parameter :: max_order = 1 ! maximum BDF order, default = 5 integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 From 8f659985c682651973b36037f4d84261f49a4654 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 29 Apr 2021 00:07:59 -0600 Subject: [PATCH 0075/1472] sysSolveFida edited --- build/source/engine/fidaSolver.f90 | 2 +- build/source/engine/sysSolvFida.f90 | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index d0f0b5471..9a4fa9365 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -415,7 +415,7 @@ subroutine fidaSolver( & do while(tret(1) < dt) ! call IDASolve retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) - ! if( retval < 0 ) return + if( retval < 0 ) exit ! get the last stepsize retval = FIDAGetLastStep(ida_mem, dt_last) diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 00e62cb05..6c452df17 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -419,7 +419,7 @@ subroutine sysSolvFida(& ! * solving F(y,y') = 0 by FIDA. Here, y is the state vector ! ------------------ - do tol_iter=1,5 + do tol_iter=1,3 ! initialize flux_sum do concurrent ( iVar=1:size(flux_meta) ) @@ -482,8 +482,9 @@ subroutine sysSolvFida(& ! check if fida is successful if( .not.idaSucceeds )then + err = 20 message=trim(message)//trim(cmessage) - reduceCoupledStep = .true. +! reduceCoupledStep = .true. return endif From 221601ad0a9cb55925b5617b470261e2bd7f6baa Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 29 Apr 2021 23:57:46 -0600 Subject: [PATCH 0076/1472] recomputing heatCap deleted from thermalConduct --- build/source/engine/computHeatCap.f90 | 2 +- build/source/engine/computThermConduct.f90 | 32 +--------------------- build/source/engine/diagn_evar.f90 | 2 +- build/source/engine/eval8summaFida.f90 | 2 +- 4 files changed, 4 insertions(+), 34 deletions(-) diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index 4b4830093..b351f67ad 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -297,7 +297,7 @@ subroutine computStatMult(& end subroutine computStatMult ! ********************************************************************************************************** - ! public subroutine computHeatCap: compute diagnostic energy variables (thermal conductivity and heat capacity) + ! public subroutine computHeatCapAnalytic: compute diagnostic energy variables (heat capacity) ! ********************************************************************************************************** subroutine computHeatCapAnalytic(& ! input: control variables diff --git a/build/source/engine/computThermConduct.f90 b/build/source/engine/computThermConduct.f90 index 4d35ed0dd..f0d87feea 100644 --- a/build/source/engine/computThermConduct.f90 +++ b/build/source/engine/computThermConduct.f90 @@ -149,8 +149,6 @@ subroutine computThermConduct(& frac_silt => mpar_data%var(iLookPARAM%frac_silt)%dat, & ! intent(in): fraction of silt (-) frac_clay => mpar_data%var(iLookPARAM%frac_clay)%dat, & ! intent(in): fraction of clay (-) ! output: diagnostic variables - scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1), & ! intent(out): volumetric heat capacity of the vegetation (J m-3 K-1) - mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat, & ! intent(out): volumetric heat capacity in each layer (J m-3 K-1) mLayerThermalC => diag_data%var(iLookDIAG%mLayerThermalC)%dat, & ! intent(out): thermal conductivity at the mid-point of each layer (W m-1 K-1) iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(out): thermal conductivity at the interface of each layer (W m-1 K-1) mLayerVolFracAir => diag_data%var(iLookDIAG%mLayerVolFracAir)%dat & ! intent(out): volumetric fraction of air in each layer (-) @@ -162,16 +160,6 @@ subroutine computThermConduct(& ! initialize the soil layer iSoil=integerMissing - ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) - if(computeVegFlux)then - scalarBulkVolHeatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component - Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component - Cp_ice*scalarCanopyIce/canopyDepth ! ice component - else - scalarBulkVolHeatCapVeg = valueMissing - end if - !print*, 'computThermConduct: scalarBulkVolHeatCapVeg = ', scalarBulkVolHeatCapVeg - ! loop through layers do iLayer=1,nLayers @@ -194,25 +182,7 @@ subroutine computThermConduct(& case(iname_snow); mLayerVolFracAir(iLayer) = 1._dp - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute volumetric fraction of air'; return end select - - ! ***** - ! * compute the volumetric heat capacity of each layer (J m-3 K-1)... - ! ******************************************************************* - select case(layerType(iLayer)) - ! * soil - case(iname_soil) - mLayerVolHtCapBulk(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._dp - theta_sat(iSoil) ) + & ! soil component - iden_ice * Cp_Ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * mLayerVolFracAir(iLayer) ! air component - ! * snow - case(iname_snow) - mLayerVolHtCapBulk(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * mLayerVolFracAir(iLayer) ! air component - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return - end select - + ! ***** ! * compute the thermal conductivity of snow and soil at the mid-point of each layer... ! ************************************************************************************* diff --git a/build/source/engine/diagn_evar.f90 b/build/source/engine/diagn_evar.f90 index e2ca80a1b..9c716bb28 100755 --- a/build/source/engine/diagn_evar.f90 +++ b/build/source/engine/diagn_evar.f90 @@ -185,7 +185,7 @@ subroutine diagn_evar(& else scalarBulkVolHeatCapVeg = valueMissing end if - !print*, 'diagn_evar: scalarBulkVolHeatCapVeg = ', scalarBulkVolHeatCapVeg + ! loop through layers do iLayer=1,nLayers diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 0fd86e9fc..60e10ba30 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -536,7 +536,7 @@ subroutine eval8summaFida(& err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - if(1==0)then + if(1==1)then ! *** compute volumetric heat capacity C_p call computHeatCapAnalytic(& From d23e9480e4b6dd4f968b64b69d8d7d9dafdab434 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 30 Apr 2021 00:06:35 -0600 Subject: [PATCH 0077/1472] minor edit --- build/source/engine/computHeatCap.f90 | 3 --- 1 file changed, 3 deletions(-) diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index b351f67ad..d4791dec8 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -317,8 +317,6 @@ subroutine computHeatCapAnalytic(& ! output: error control err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------------- - ! provide access to external subroutines - USE snow_utils_module,only:tcond_snow ! compute thermal conductivity of snow ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: model control logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux @@ -419,7 +417,6 @@ subroutine computCm(& err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------------- ! provide access to external subroutines - USE snow_utils_module,only:tcond_snow ! compute thermal conductivity of snow USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: model control From d92c1ae6b5164e32570cea55e62dc1301d6a69c4 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 30 Apr 2021 00:43:22 -0600 Subject: [PATCH 0078/1472] heatCap and others before computFlux in eval8summaFida --- build/source/engine/computHeatCap.f90 | 2 +- build/source/engine/eval8summaFida.f90 | 153 +++++++++++++------------ 2 files changed, 79 insertions(+), 76 deletions(-) diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index d4791dec8..cf71fab86 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -270,7 +270,7 @@ subroutine computStatMult(& where(ixStateType_subset==iname_liqCanopy) sMul = 1._dp ! nothing else on the left hand side - ! define the energy multiplier and diagonal elements for the state vector for residual calculations (snow-soil domain) + ! define the energy multiplier for the state vector for residual calculations (snow-soil domain) if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) ixStateSubset = ixSnowSoilNrg(iLayer) ! index within the state vector diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 60e10ba30..ef2da3390 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -470,92 +470,28 @@ subroutine eval8summaFida(& if(iJac1 Date: Fri, 30 Apr 2021 00:50:53 -0600 Subject: [PATCH 0079/1472] minor edit --- build/source/engine/eval8summaFida.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index ef2da3390..76a5c20a6 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -550,8 +550,8 @@ subroutine eval8summaFida(& end if - scalarCanopyCmTrial = 0._dp - mLayerCmTrial = 0._dp +! scalarCanopyCmTrial = 0._dp +! mLayerCmTrial = 0._dp ! save the number of flux calls per time step indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) + 1 From c034dd4b1cf73ca5a15d6b4e3ff97d172c19c04d Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 30 Apr 2021 22:18:43 -0600 Subject: [PATCH 0080/1472] heatCap as assiciate in eval8summaFida --- build/source/engine/eval8summaFida.f90 | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 76a5c20a6..38a5fe961 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -241,7 +241,6 @@ subroutine eval8summaFida(& real(dp),dimension(nLayers) :: mLayerVolFracIcePrime ! derivative value for volumetric fraction of ice (-) ! enthalpy real(dp) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - real(dp),dimension(nLayers) :: mLayerHeatCapTrial ! heat capacity of each snow+soil layer real(dp),dimension(nLayers) :: mLayerEnthalpyPrime ! enthalpy of each snow+soil layer (J m-3) ! other local variables integer(i4b) :: iLayer ! index of model layer in the snow+soil domain @@ -251,7 +250,6 @@ subroutine eval8summaFida(& real(dp) :: xMin,xMax ! minimum and maximum values for water content real(dp),parameter :: canopyTempMax=500._dp ! expected maximum value for the canopy temperature (K) character(LEN=256) :: cmessage ! error message of downwind routine - real(qp) :: heatCapVegTrial real(qp) :: scalarCanopyEnthalpyPrime logical(lgt) :: firstFluxCall integer(i4b) :: ixSaturation ! index of the lowest saturated layer @@ -294,7 +292,9 @@ subroutine eval8summaFida(& ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain - layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) + heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& + mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -504,9 +504,6 @@ subroutine eval8summaFida(& sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) = heatCapVegTrial - diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat(:) = mLayerHeatCapTrial(:) ! update thermal conductivity call computThermConduct(& From 2bfcc568ff24e04806ebd11d54e674fb8fe1dc58 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 30 Apr 2021 23:08:10 -0600 Subject: [PATCH 0081/1472] updateCp and needCm flags in eval8summaFida --- build/source/engine/eval8summaFida.f90 | 29 +++++++++++++------------- build/source/engine/fidaSolver.f90 | 2 +- build/source/engine/sysSolvFida.f90 | 6 +++--- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 38a5fe961..df3880296 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -255,6 +255,9 @@ subroutine eval8summaFida(& integer(i4b) :: ixSaturation ! index of the lowest saturated layer real(dp) :: scalarCanopyCmTrial real(dp),dimension(nLayers) :: mLayerCmTrial + logical(lgt),parameter :: updateCp=.true. + logical(lgt),parameter :: needCm=.true. + ! -------------------------------------------------------------------------------------------------------------------------------- @@ -472,10 +475,9 @@ subroutine eval8summaFida(& endif - if(1==1)then - - ! *** compute volumetric heat capacity C_p - call computHeatCapAnalytic(& + if(updateCp)then + ! *** compute volumetric heat capacity C_p + call computHeatCapAnalytic(& ! input: control variables computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux canopyDepth, & ! intent(in): canopy depth (m) @@ -522,7 +524,10 @@ subroutine eval8summaFida(& diag_data, & ! intent(inout): model diagnostic variables for a local HRU err,message) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if - + end if ! updateCp + + + if(needCm)then ! compute C_m call computCm(& ! input: control variables @@ -538,17 +543,11 @@ subroutine eval8summaFida(& scalarCanopyCmTrial, & ! intent(out): Cm for vegetation mLayerCmTrial, & ! intent(out): Cm for soil and snow err,message) ! intent(out): error control + else + scalarCanopyCmTrial = 0._dp + mLayerCmTrial = 0._dp + end if ! needCm - ! to conserve energy compute finite difference approximation of (theta_ice)' - ! scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur - ! do concurrent (iLayer=1:nLayers) - ! mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur - ! end do - - end if - -! scalarCanopyCmTrial = 0._dp -! mLayerCmTrial = 0._dp ! save the number of flux calls per time step indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) + 1 diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 9a4fa9365..b21574e87 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -170,7 +170,7 @@ subroutine fidaSolver( & logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors real(dp),intent(in) :: stateVecInit(:) ! model state vector - real(qp),intent(inout) :: sMul(:) ! state vector multiplier (used in the residual calculations) + real(qp),intent(in) :: sMul(:) ! state vector multiplier (used in the residual calculations) real(dp), intent(inout) :: dMat(:) ! input: data structures type(zLookup),intent(in) :: lookup_data ! lookup tables diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 6c452df17..b7368afc4 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -412,11 +412,11 @@ subroutine sysSolvFida(& if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) ! just for experiment - atol = 1e-6 - rtol = 1e-6 +! atol = 1e-9 +! rtol = 1e-9 !------------------- - ! * solving F(y,y') = 0 by FIDA. Here, y is the state vector + ! * solving F(y,y') = 0 by IDA. Here, y is the state vector ! ------------------ do tol_iter=1,3 From b195df1a7a67f651f4f0e18f3045d40b8179ed75 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sat, 1 May 2021 22:53:28 -0600 Subject: [PATCH 0082/1472] iden_ice instead of iden_water in computJacobFida --- build/source/engine/computJacobFida.f90 | 20 ++++++++++++++------ build/source/engine/eval8FidaJac.f90 | 2 +- build/source/engine/eval8summaFida.f90 | 8 ++++---- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/build/source/engine/computJacobFida.f90 b/build/source/engine/computJacobFida.f90 index 43c698d6e..f1a8da9b3 100644 --- a/build/source/engine/computJacobFida.f90 +++ b/build/source/engine/computJacobFida.f90 @@ -269,7 +269,8 @@ subroutine computJacobFida(& scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl)%dat(1) ,& ! intent(in): [dp] soil control on infiltration, zero or one ! canopy and layer depth canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat & ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] named variables defining the type of layer in snow+soil domain ) ! making association with data in structures ! -------------------------------------------------------------- ! initialize error control @@ -300,9 +301,16 @@ subroutine computJacobFida(& ! NOTE: energy for snow+soil is computed *within* the iteration loop as it includes phase change do iLayer=1,nLayers if(ixSnowSoilNrg(iLayer)/=integerMissing) then - dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj & - + LH_fus*iden_water * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & - + LH_fus*iden_water * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) + select case( layerType(iLayer) ) + case(iname_snow) + dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_ice*mLayerdTheta_dTk(iLayer) ) * cj & + + LH_fus*iden_ice * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & + + LH_fus*iden_ice * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) + case(iname_soil) + dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj & + + LH_fus*iden_water * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & + + LH_fus*iden_water * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) + end select end if end do @@ -700,8 +708,8 @@ subroutine computJacobFida(& ! (cross-derivative terms for the current layer) ! print *, '3' - aJac(nrgState,watState) = (-1._dp + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & - + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) + aJac(nrgState,watState) = (-1._dp + mLayerFracLiqSnow(iLayer))*LH_fus*iden_ice * cj & + + LH_fus*iden_ice * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) ! (cross-derivative terms for the layer below) diff --git a/build/source/engine/eval8FidaJac.f90 b/build/source/engine/eval8FidaJac.f90 index 3c6d07f41..56287ef04 100644 --- a/build/source/engine/eval8FidaJac.f90 +++ b/build/source/engine/eval8FidaJac.f90 @@ -317,7 +317,7 @@ subroutine eval8FidaJac(& ! NOTE: The derivatives were computed in the previous call to computFlux ! This occurred either at the call to eval8summaFida at the start of sysSolveFida ! or in the call to eval8summaFida in the previous iteration - dt1 = 1.0 + dt1 = 1._qp call computJacobFida(& ! input: model control cj, & ! intent(in) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index df3880296..f5d416aa4 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -255,8 +255,8 @@ subroutine eval8summaFida(& integer(i4b) :: ixSaturation ! index of the lowest saturated layer real(dp) :: scalarCanopyCmTrial real(dp),dimension(nLayers) :: mLayerCmTrial - logical(lgt),parameter :: updateCp=.true. - logical(lgt),parameter :: needCm=.true. + logical(lgt),parameter :: updateCp=.false. + logical(lgt),parameter :: needCm=.false. @@ -544,8 +544,8 @@ subroutine eval8summaFida(& mLayerCmTrial, & ! intent(out): Cm for soil and snow err,message) ! intent(out): error control else - scalarCanopyCmTrial = 0._dp - mLayerCmTrial = 0._dp + scalarCanopyCmTrial = 0._qp + mLayerCmTrial = 0._qp end if ! needCm From 1f6731a612893427395be609f6a371c364038470 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sat, 1 May 2021 23:57:05 -0600 Subject: [PATCH 0083/1472] initialize some of the state variables before varExtract in eval8summaFida --- build/source/engine/eval8summaFida.f90 | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index f5d416aa4..c311a6649 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -374,7 +374,19 @@ subroutine eval8summaFida(& ixEnd = nSoil endif - + ! initialize to state variable from the last update + !scalarCanairTempTrial = scalarCanairTempPrev + scalarCanopyTempTrial = scalarCanopyTempPrev + !scalarCanopyWatTrial = scalarCanopyWatPrev + !scalarCanopyLiqTrial = scalarCanopyLiqPrev + scalarCanopyIceTrial = scalarCanopyIcePrev + mLayerTempTrial = mLayerTempPrev + mLayerVolFracWatTrial = mLayerVolFracWatPrev + !mLayerVolFracLiqTrial = mLayerVolFracLiqPrev + mLayerVolFracIceTrial = mLayerVolFracIcePrev + mLayerMatricHeadTrial = mLayerMatricHeadPrev ! total water matric potential + !mLayerMatricHeadLiqTrial = mLayerMatricHeadLiqPrev ! liquid water matric potential + !scalarAquiferStorageTrial = scalarAquiferStoragePrev ! extract variables from the model state vector call varExtract(& @@ -401,7 +413,6 @@ subroutine eval8summaFida(& if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - call varExtractFida(& ! input @@ -426,8 +437,6 @@ subroutine eval8summaFida(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - call updateVarsFida(& ! input From 2c25f04457efbd2d1bba310ad3ce481cfbd4f81d Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 5 May 2021 12:25:05 -0600 Subject: [PATCH 0084/1472] minor edit --- build/source/engine/eval8summaFida.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index c311a6649..df914f18c 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -255,8 +255,8 @@ subroutine eval8summaFida(& integer(i4b) :: ixSaturation ! index of the lowest saturated layer real(dp) :: scalarCanopyCmTrial real(dp),dimension(nLayers) :: mLayerCmTrial - logical(lgt),parameter :: updateCp=.false. - logical(lgt),parameter :: needCm=.false. + logical(lgt),parameter :: updateCp=.true. + logical(lgt),parameter :: needCm=.true. From 00ba9e5ba9804339711944a2fb9996c889acf649 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 6 May 2021 00:08:44 -0600 Subject: [PATCH 0085/1472] firstFluxCall in fidaSolver --- build/source/engine/eval8summaFida.f90 | 4 ++-- build/source/engine/evalEqnsFida.f90 | 1 + build/source/engine/fidaSolver.f90 | 2 ++ build/source/engine/fida_datatypes.f90 | 1 + 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index df914f18c..c89bcc624 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -96,6 +96,7 @@ subroutine eval8summaFida(& nLayers, & ! intent(in): total number of layers nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout) computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors @@ -167,6 +168,7 @@ subroutine eval8summaFida(& integer(i4b),intent(in) :: nLayers ! total number of layers integer,intent(in) :: nState ! total number of state variables logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(inout) :: firstFluxCall logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors @@ -251,7 +253,6 @@ subroutine eval8summaFida(& real(dp),parameter :: canopyTempMax=500._dp ! expected maximum value for the canopy temperature (K) character(LEN=256) :: cmessage ! error message of downwind routine real(qp) :: scalarCanopyEnthalpyPrime - logical(lgt) :: firstFluxCall integer(i4b) :: ixSaturation ! index of the lowest saturated layer real(dp) :: scalarCanopyCmTrial real(dp),dimension(nLayers) :: mLayerCmTrial @@ -560,7 +561,6 @@ subroutine eval8summaFida(& ! save the number of flux calls per time step indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) + 1 - firstFluxCall = .false. ! compute the fluxes for a given state vector call computFlux(& ! input-output: model control diff --git a/build/source/engine/evalEqnsFida.f90 b/build/source/engine/evalEqnsFida.f90 index b389e3996..a5c6aaad0 100644 --- a/build/source/engine/evalEqnsFida.f90 +++ b/build/source/engine/evalEqnsFida.f90 @@ -105,6 +105,7 @@ integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_d eqns_data%nLayers, & ! intent(in): number of layers eqns_data%nState, & ! intent(in): number of state variables in the current subset eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + eqns_data%firstFluxCall, & ! intent(inout) eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index b21574e87..11ab8b280 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -413,6 +413,7 @@ subroutine fidaSolver( & !********************************************************************************** tret(1) = t0 do while(tret(1) < dt) + eqns_data%firstFluxCall = .true. ! call IDASolve retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) if( retval < 0 ) exit @@ -430,6 +431,7 @@ subroutine fidaSolver( & eqns_data%nLayers, & ! intent(in): number of layers eqns_data%nState, & ! intent(in): number of state variables in the current subset eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + eqns_data%firstFluxCall, & ! intent(inout) eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index a2570f15d..6df36d44a 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -25,6 +25,7 @@ module fida_datatypes integer :: nState ! total number of state variables integer(i4b) :: ixMatrix ! form of matrix (dense or banded) logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt) :: firstFluxCall logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution type(zLookup) :: lookup_data ! lookup tables From bc5364b735c12e0d36964c017f9a3cd1c471bb99 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 6 May 2021 22:51:20 -0600 Subject: [PATCH 0086/1472] new tol --- build/source/engine/computFlux.f90 | 2 +- build/source/engine/eval8summaFida.f90 | 1 + build/source/engine/fidaSolver.f90 | 4 +++- build/source/engine/sysSolvFida.f90 | 4 ++-- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 75b8fc486..77ac86aae 100755 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -386,7 +386,7 @@ subroutine computFlux(& ! NOTE: used in the energy routines, which is called before the hydrology routines if(firstFluxCall)then if(nSnow > 0) iLayerLiqFluxSnow(0:nSnow) = 0._dp - iLayerLiqFluxSoil(0:nSoil) = 0._dp + iLayerLiqFluxSoil(0:nSoil) = 0._dp end if ! ***** diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index c89bcc624..2c1f0bfe1 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -559,6 +559,7 @@ subroutine eval8summaFida(& end if ! needCm +! print *, 'firstFluxCall = ', firstFluxCall ! save the number of flux calls per time step indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) + 1 ! compute the fluxes for a given state vector diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 11ab8b280..537f92066 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -522,6 +522,8 @@ subroutine fidaSolver( & eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial + +! print *, '------------------------------------------------' end do ! while loop on one_step mode @@ -618,7 +620,7 @@ subroutine setSolverParams(dt,ida_mem,retval) integer(i4b),intent(out) :: retval ! return value real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 - integer,parameter :: max_order = 1 ! maximum BDF order, default = 5 + integer,parameter :: max_order = 5 ! maximum BDF order, default = 5 integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index b7368afc4..697bae48a 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -412,8 +412,8 @@ subroutine sysSolvFida(& if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) ! just for experiment -! atol = 1e-9 -! rtol = 1e-9 + atol = 1e-9 + rtol = 1e-9 !------------------- ! * solving F(y,y') = 0 by IDA. Here, y is the state vector From 1f638abb853d9eefb54c7c4420194dd411342b3e Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 6 May 2021 23:40:43 -0600 Subject: [PATCH 0087/1472] tol back --- build/source/engine/fidaSolver.f90 | 4 +--- build/source/engine/sysSolvFida.f90 | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 537f92066..11ab8b280 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -522,8 +522,6 @@ subroutine fidaSolver( & eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial - -! print *, '------------------------------------------------' end do ! while loop on one_step mode @@ -620,7 +618,7 @@ subroutine setSolverParams(dt,ida_mem,retval) integer(i4b),intent(out) :: retval ! return value real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 - integer,parameter :: max_order = 5 ! maximum BDF order, default = 5 + integer,parameter :: max_order = 1 ! maximum BDF order, default = 5 integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 697bae48a..68d334abf 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -412,8 +412,8 @@ subroutine sysSolvFida(& if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) ! just for experiment - atol = 1e-9 - rtol = 1e-9 + atol = 1e-6 + rtol = 1e-6 !------------------- ! * solving F(y,y') = 0 by IDA. Here, y is the state vector From 45d01942a98600ba45bdf1e0b58b26f177cbf24d Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 7 May 2021 00:07:54 -0600 Subject: [PATCH 0088/1472] mLayerVolFracIcePrev in computFluxFida --- build/Makefile | 1 + build/source/engine/computFluxFida.f90 | 907 +++++++++++++++++++++++++ build/source/engine/eval8summaFida.f90 | 7 +- build/source/engine/fidaSolver.f90 | 2 +- build/source/engine/snowLiqFlx.f90 | 156 +++++ 5 files changed, 1069 insertions(+), 4 deletions(-) create mode 100755 build/source/engine/computFluxFida.f90 diff --git a/build/Makefile b/build/Makefile index 390f9c0b7..8e9a86271 100644 --- a/build/Makefile +++ b/build/Makefile @@ -214,6 +214,7 @@ SUMMA_SOLVER= \ bigAquifer.f90 \ soilCmpresFida.f90 \ computFlux.f90 \ + computFluxFida.f90 \ computResid.f90 \ computJacob.f90 \ eval8summa.f90 \ diff --git a/build/source/engine/computFluxFida.f90 b/build/source/engine/computFluxFida.f90 new file mode 100755 index 000000000..ce0c144a0 --- /dev/null +++ b/build/source/engine/computFluxFida.f90 @@ -0,0 +1,907 @@ +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . + +module computFluxFida_module + +! data types +USE nrtype + +! provide access to the derived types to define the data structures +USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (dp) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (dp) + model_options ! defines the model decisions + +! indices that define elements of the data structures +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure +USE var_lookup,only:iLookPARAM ! named variables for structure elements +USE var_lookup,only:iLookFORCE ! named variables for structure elements +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookFLUX ! named variables for structure elements +USE var_lookup,only:iLookDERIV ! named variables for structure elements + +! missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing real number + +! layer types +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil + +! access the global print flag +USE globalData,only:globalPrintFlag + +! control parameters +USE globalData,only:verySmall ! a very small number +USE globalData,only:veryBig ! a very big number +USE globalData,only:dx ! finite difference increment + +! constants +USE multiconst,only:& + gravity, & ! acceleration of gravity (m s-2) + Tfreeze, & ! temperature at freezing (K) + LH_fus, & ! latent heat of fusion (J kg-1) + LH_vap, & ! latent heat of vaporization (J kg-1) + LH_sub, & ! latent heat of sublimation (J kg-1) + Cp_air, & ! specific heat of air (J kg-1 K-1) + iden_air, & ! intrinsic density of air (kg m-3) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water ! intrinsic density of liquid water (kg m-3) + +! look-up values for the choice of groundwater representation (local-column, or single-basin) +USE mDecisions_module,only: & + localColumn, & ! separate groundwater representation in each local soil column + singleBasin ! single groundwater store over the entire basin + +! look-up values for the choice of groundwater parameterization +USE mDecisions_module,only: & + qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization + bigBucket, & ! a big bucket (lumped aquifer model) + noExplicit ! no explicit groundwater parameterization + +! look-up values for the form of Richards' equation +USE mDecisions_module,only: & + moisture, & ! moisture-based form of Richards' equation + mixdform ! mixed form of Richards' equation + +! look-up values for the choice of boundary conditions for hydrology +USE mDecisions_module,only: & + prescribedHead, & ! prescribed head (volumetric liquid water content for mixed form of Richards' eqn) + funcBottomHead, & ! function of matric head in the lower-most layer + freeDrainage, & ! free drainage + liquidFlux, & ! liquid water flux + zeroFlux ! zero flux + +implicit none +private +public::computFluxFida +public::soilCmpres +contains + + ! ********************************************************************************************************* + ! public subroutine computFluxFida: compute model fluxes + ! ********************************************************************************************************* + subroutine computFluxFida(& + ! input-output: model control + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to denote the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + drainageMeltPond, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) + ! input: state variables + mLayerVolFracIce, & ! intent(in) + scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) + scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) + mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) + mLayerMatricHeadLiqTrial, & ! intent(in): trial value for the liquid water matric potential in each soil layer (m) + scalarAquiferStorageTrial,& ! intent(in): trial value of storage of water in the aquifer (m) + ! input: diagnostic variables defining the liquid water and ice content + scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) + mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liquid water content in each snow and soil layer (-) + mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) + ! input: data structures + model_decisions, & ! intent(in): model decisions + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): index data + ! input-output: data structures + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: flux vector and baseflow derivatives + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + fluxVec, & ! intent(out): flux vector (mixed units) + ! output: error control + err,message) ! intent(out): error code and error message + ! provide access to flux subroutines + USE vegNrgFlux_module,only:vegNrgFlux ! compute energy fluxes over vegetation + USE ssdNrgFlux_module,only:ssdNrgFlux ! compute energy fluxes throughout the snow and soil subdomains + USE vegLiqFlux_module,only:vegLiqFlux ! compute liquid water fluxes through vegetation + USE snowLiqFlx_module,only:snowLiqFlxFida ! compute liquid water fluxes through snow + USE soilLiqFlx_module,only:soilLiqflx ! compute liquid water fluxes through soil + USE groundwatr_module,only:groundwatr ! compute the baseflow flux + USE bigAquifer_module,only:bigAquifer ! compute fluxes for the big aquifer + implicit none + ! --------------------------------------------------------------------------------------- + ! * dummy variables + ! --------------------------------------------------------------------------------------- + ! input-output: control + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + real(dp),intent(in) :: drainageMeltPond ! drainage from the surface melt pond (kg m-2 s-1) + ! input: state variables + real(dp),intent(in) :: mLayerVolFracIce(:) ! + real(dp),intent(in) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(dp),intent(in) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(dp),intent(in) :: mLayerTempTrial(:) ! trial value for temperature of each snow/soil layer (K) + real(dp),intent(in) :: mLayerMatricHeadLiqTrial(:) ! trial value for the liquid water matric potential (m) + real(dp),intent(in) :: scalarAquiferStorageTrial ! trial value of aquifer storage (m) + ! input: diagnostic variables + real(dp),intent(in) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(dp),intent(in) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(dp),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) + real(dp),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) + ! input: data structures + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(var_i), intent(in) :: type_data ! type of vegetation and soil + type(var_d), intent(in) :: attr_data ! spatial attributes + type(var_dlength), intent(in) :: mpar_data ! model parameters + type(var_d), intent(in) :: forc_data ! model forcing data + type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin + type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_ilength), intent(in) :: indx_data ! indices defining model states and layers + ! input-output: data structures + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + ! input-output: flux vector and baseflow derivatives + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + real(dp),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + real(dp),intent(out) :: fluxVec(:) ! model flux vector (mixed units) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! --------------------------------------------------------------------------------------- + ! * local variables + ! --------------------------------------------------------------------------------------- + integer(i4b) :: local_ixGroundwater ! local index for groundwater representation + integer(i4b) :: iLayer ! index of model layers + logical(lgt) :: doVegNrgFlux ! flag to compute the energy flux over vegetation + real(dp),dimension(nSoil) :: dHydCond_dMatric ! derivative in hydraulic conductivity w.r.t matric head (s-1) + character(LEN=256) :: cmessage ! error message of downwind routine + ! -------------------------------------------------------------- + ! initialize error control + err=0; message='computFluxFida/' + + ! ***** + ! (0) PRELIMINARIES... + ! ******************** + + ! get the necessary variables for the flux computations + associate(& + + ! model decisions + ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization + ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) + + ! domain boundary conditions + upperBoundTemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) + scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in): [dp] rainfall rate (kg m-2 s-1) + + ! canopy and layer depth + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + + ! indices of model state variables for the vegetation subdomain + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain + ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer + + ! indices of model state variables for the snow+soil domain + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] type of layer (iname_soil or iname_snow) + + ! number of state variables of a specific type + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain + nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + + ! derivatives + dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) + dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(in): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + + ! number of flux calls + numFluxCalls => diag_data%var(iLookDIAG%numFluxCalls)%dat(1) ,& ! intent(out): [dp] number of flux calls (-) + + ! net fluxes over the vegetation domain + scalarCanairNetNrgFlux => flux_data%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the canopy air space (W m-2) + scalarCanopyNetNrgFlux => flux_data%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the vegetation canopy (W m-2) + scalarGroundNetNrgFlux => flux_data%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the ground surface (W m-2) + scalarCanopyNetLiqFlux => flux_data%var(iLookFLUX%scalarCanopyNetLiqFlux)%dat(1) ,& ! intent(out): [dp] net liquid water flux for the vegetation canopy (kg m-2 s-1) + + ! net fluxes over the snow+soil domain + mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) + mLayerLiqFluxSnow => flux_data%var(iLookFLUX%mLayerLiqFluxSnow)%dat ,& ! intent(out): [dp] net liquid water flux for each snow layer (s-1) + mLayerLiqFluxSoil => flux_data%var(iLookFLUX%mLayerLiqFluxSoil)%dat ,& ! intent(out): [dp] net liquid water flux for each soil layer (s-1) + + ! evaporative fluxes + scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(out): [dp] canopy transpiration (kg m-2 s-1) + scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(out): [dp] canopy evaporation/condensation (kg m-2 s-1) + scalarGroundEvaporation => flux_data%var(iLookFLUX%scalarGroundEvaporation)%dat(1) ,& ! intent(out): [dp] ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(out): [dp(:)] transpiration loss from each soil layer (m s-1) + + ! fluxes for the snow+soil domain + iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(out): [dp(0:)] vertical energy flux at the interface of snow and soil layers + iLayerLiqFluxSnow => flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at snow layer interfaces (-) + iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at soil layer interfaces (-) + mLayerHydCond => flux_data%var(iLookFLUX%mLayerHydCond)%dat ,& ! intent(out): [dp(:)] hydraulic conductivity in each soil layer (m s-1) + mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(out): [dp(:)] baseflow from each soil layer (m s-1) + scalarSnowDrainage => flux_data%var(iLookFLUX%scalarSnowDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the snow profile (m s-1) + scalarSoilDrainage => flux_data%var(iLookFLUX%scalarSoilDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the soil profile (m s-1) + scalarSoilBaseflow => flux_data%var(iLookFLUX%scalarSoilBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the soil profile (m s-1) + + ! infiltration + scalarInfilArea => diag_data%var(iLookDIAG%scalarInfilArea )%dat(1) ,& ! intent(out): [dp] fraction of unfrozen area where water can infiltrate (-) + scalarFrozenArea => diag_data%var(iLookDIAG%scalarFrozenArea )%dat(1) ,& ! intent(out): [dp] fraction of area that is considered impermeable due to soil ice (-) + scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1) ,& ! intent(out): [dp] soil control on infiltration, zero or one + scalarMaxInfilRate => flux_data%var(iLookFLUX%scalarMaxInfilRate)%dat(1) ,& ! intent(out): [dp] maximum infiltration rate (m s-1) + scalarInfiltration => flux_data%var(iLookFLUX%scalarInfiltration)%dat(1) ,& ! intent(out): [dp] infiltration of water into the soil profile (m s-1) + + ! boundary fluxes in the soil domain + scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(out): [dp] rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(out): [dp] drainage of liquid water from the vegetation canopy (kg m-2 s-1) + scalarRainPlusMelt => flux_data%var(iLookFLUX%scalarRainPlusMelt)%dat(1) ,& ! intent(out): [dp] rain plus melt (m s-1) + scalarSurfaceRunoff => flux_data%var(iLookFLUX%scalarSurfaceRunoff)%dat(1) ,& ! intent(out): [dp] surface runoff (m s-1) + scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1) ,& ! intent(out): [dp] exfiltration from the soil profile (m s-1) + mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat ,& ! intent(out): [dp(:)] column outflow from each soil layer (m3 s-1) + + ! fluxes for the aquifer + scalarAquiferTranspire => flux_data%var(iLookFLUX%scalarAquiferTranspire)%dat(1) ,& ! intent(out): [dp] transpiration loss from the aquifer (m s-1 + scalarAquiferRecharge => flux_data%var(iLookFLUX%scalarAquiferRecharge)%dat(1) ,& ! intent(out): [dp] recharge to the aquifer (m s-1) + scalarAquiferBaseflow => flux_data%var(iLookFLUX%scalarAquiferBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the aquifer (m s-1) + + ! total runoff + scalarTotalRunoff => flux_data%var(iLookFLUX%scalarTotalRunoff)%dat(1) ,& ! intent(out): [dp] total runoff (m s-1) + + ! derivatives in net vegetation energy fluxes w.r.t. relevant state variables + dCanairNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy air temperature + dCanairNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy temperature + dCanairNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. ground temperature + dCanopyNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy air temperature + dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy temperature + dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. ground temperature + dCanopyNetFlux_dCanLiq => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in net canopy fluxes w.r.t. canopy liquid water content + dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy air temperature + dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy temperature + dGroundNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. ground temperature + dGroundNetFlux_dCanLiq => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in net ground fluxes w.r.t. canopy liquid water content + + ! derivatives in evaporative fluxes w.r.t. relevant state variables + dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy air temperature + dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy temperature + dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. ground temperature + dCanopyEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy liquid water content + dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy air temperature + dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy temperature + dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. ground temperature + dGroundEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy liquid water content + + ! derivatives in canopy water w.r.t canopy temperature + dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(out): [dp] derivative of canopy liquid storage w.r.t. temperature + + ! derivatives in canopy liquid fluxes w.r.t. canopy water + scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ,& ! intent(out): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water + scalarThroughfallRainDeriv => deriv_data%var(iLookDERIV%scalarThroughfallRainDeriv )%dat(1) ,& ! intent(out): [dp] derivative in throughfall w.r.t. canopy liquid water + scalarCanopyLiqDrainageDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDrainageDeriv)%dat(1) ,& ! intent(out): [dp] derivative in canopy drainage w.r.t. canopy liquid water + + ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below + dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above + dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below + + ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above + iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(out): [dp(:)] derivative in vertical liquid water flux at layer interfaces + + ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential + dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above + dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + mLayerdTheta_dPsi => deriv_data%var(iLookDERIV%mLayerdTheta_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. psi + mLayerdPsi_dTheta => deriv_data%var(iLookDERIV%mLayerdPsi_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. theta + dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in compressibility w.r.t matric head + + ! derivative in baseflow flux w.r.t. aquifer storage + dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] erivative in baseflow flux w.r.t. aquifer storage (s-1) + + ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables + dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above + dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat & ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + + ) ! association to data in structures + + ! ***** + ! * PRELIMINARIES... + ! ****************** + + !print*, '***** nSnowSoilNrg, nSnowOnlyNrg, nSoilOnlyNrg, nSnowSoilHyd, nSnowOnlyHyd, nSoilOnlyHyd = ', & + ! nSnowSoilNrg, nSnowOnlyNrg, nSoilOnlyNrg, nSnowSoilHyd, nSnowOnlyHyd, nSoilOnlyHyd + + ! increment the number of flux calls + numFluxCalls = numFluxCalls+1 + + ! modify the groundwater representation for this single-column implementation + select case(ixSpatialGroundwater) + case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale + case(localColumn); local_ixGroundwater = ixGroundwater ! go with the specified decision + case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return + end select ! (modify the groundwater representation for this single-column implementation) + + ! initialize liquid water fluxes throughout the snow and soil domains + ! NOTE: used in the energy routines, which is called before the hydrology routines + if(firstFluxCall)then + if(nSnow > 0) iLayerLiqFluxSnow(0:nSnow) = 0._dp + iLayerLiqFluxSoil(0:nSoil) = 0._dp + end if + + ! ***** + ! * CALCULATE ENERGY FLUXES OVER VEGETATION... + ! ********************************************* + + ! identify the need to calculate the energy flux over vegetation + doVegNrgFlux = (ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing .or. ixTopNrg/=integerMissing) + + ! check if there is a need to calculate the energy fluxes over vegetation + if(doVegNrgFlux)then + + ! derivative in canopy liquid storage w.r.t. canopy temperature + dCanLiq_dTcanopy = dTheta_dTkCanopy*iden_water*canopyDepth ! kg m-2 K-1 + + ! calculate the energy fluxes over vegetation + call vegNrgFlux(& + ! input: model control + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + ! input: model state variables + upperBoundTemp, & ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature + scalarCanairTempTrial, & ! intent(in): trial value of the canopy air space temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + mLayerTempTrial(1), & ! intent(in): trial value of ground temperature (K) + scalarCanopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) + ! input: model derivatives + dCanLiq_dTcanopy, & ! intent(in): derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) + ! input/output: data structures + type_data, & ! intent(in): type of vegetation and soil + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + bvar_data, & ! intent(in): model variables for the local basin + model_decisions, & ! intent(in): model decisions + ! output: liquid water fluxes associated with evaporation/transpiration + scalarCanopyTranspiration, & ! intent(out): canopy transpiration (kg m-2 s-1) + scalarCanopyEvaporation, & ! intent(out): canopy evaporation/condensation (kg m-2 s-1) + scalarGroundEvaporation, & ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + ! output: fluxes + scalarCanairNetNrgFlux, & ! intent(out): net energy flux for the canopy air space (W m-2) + scalarCanopyNetNrgFlux, & ! intent(out): net energy flux for the vegetation canopy (W m-2) + scalarGroundNetNrgFlux, & ! intent(out): net energy flux for the ground surface (W m-2) + ! output: flux derivatives + dCanairNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) + dCanairNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) + dCanairNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) + dCanopyNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) + dCanopyNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) + dCanopyNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) + dGroundNetFlux_dCanairTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) + dGroundNetFlux_dCanopyTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) + dGroundNetFlux_dGroundTemp, & ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + ! output: liquid water flux derivarives (canopy evap) + dCanopyEvaporation_dCanLiq, & ! intent(out): derivative in canopy evaporation w.r.t. canopy liquid water content (s-1) + dCanopyEvaporation_dTCanair, & ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTCanopy, & ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTGround, & ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + ! output: liquid water flux derivarives (ground evap) + dGroundEvaporation_dCanLiq, & ! intent(out): derivative in ground evaporation w.r.t. canopy liquid water content (s-1) + dGroundEvaporation_dTCanair, & ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTCanopy, & ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTGround, & ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + ! output: cross derivative terms + dCanopyNetFlux_dCanLiq, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dGroundNetFlux_dCanLiq, & ! intent(out): derivative in net ground fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + + ! check fluxes + if(globalPrintFlag)then + print*, '**' + write(*,'(a,1x,10(f30.20))') 'canopyDepth = ', canopyDepth + write(*,'(a,1x,10(f30.20))') 'mLayerDepth(1:2) = ', mLayerDepth(1:2) + write(*,'(a,1x,10(f30.20))') 'scalarCanairTempTrial = ', scalarCanairTempTrial ! trial value of the canopy air space temperature (K) + write(*,'(a,1x,10(f30.20))') 'scalarCanopyTempTrial = ', scalarCanopyTempTrial ! trial value of canopy temperature (K) + write(*,'(a,1x,10(f30.20))') 'mLayerTempTrial(1:2) = ', mLayerTempTrial(1:2) ! trial value of ground temperature (K) + write(*,'(a,1x,10(f30.20))') 'scalarCanairNetNrgFlux = ', scalarCanairNetNrgFlux + write(*,'(a,1x,10(f30.20))') 'scalarCanopyNetNrgFlux = ', scalarCanopyNetNrgFlux + write(*,'(a,1x,10(f30.20))') 'scalarGroundNetNrgFlux = ', scalarGroundNetNrgFlux + write(*,'(a,1x,10(f30.20))') 'dGroundNetFlux_dGroundTemp = ', dGroundNetFlux_dGroundTemp + endif ! if checking fluxes + + endif ! if calculating the energy fluxes over vegetation + + ! ***** + ! * CALCULATE ENERGY FLUXES THROUGH THE SNOW-SOIL DOMAIN... + ! ********************************************************** + + ! check the need to compute energy fluxes throughout the snow+soil domain + if(nSnowSoilNrg>0)then + + ! calculate energy fluxes at layer interfaces through the snow and soil domain + call ssdNrgFlux(& + ! input: model control + (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution + ! input: fluxes and derivatives at the upper boundary + scalarGroundNetNrgFlux, & ! intent(in): total flux at the ground surface (W m-2) + dGroundNetFlux_dGroundTemp, & ! intent(in): derivative in total ground surface flux w.r.t. ground temperature (W m-2 K-1) + ! input: liquid water fluxes throughout the snow and soil domains + iLayerLiqFluxSnow, & ! intent(in): liquid flux at the interface of each snow layer (m s-1) + iLayerLiqFluxSoil, & ! intent(in): liquid flux at the interface of each soil layer (m s-1) + ! input: trial value of model state variabes + mLayerTempTrial, & ! intent(in): trial temperature at the current iteration (K) + ! input-output: data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model indices + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output: fluxes and derivatives at all layer interfaces + iLayerNrgFlux, & ! intent(out): energy flux at the layer interfaces (W m-2) + dNrgFlux_dTempAbove, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (W m-2 K-1) + dNrgFlux_dTempBelow, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (W m-2 K-1) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! calculate net energy fluxes for each snow and soil layer (J m-3 s-1) + do iLayer=1,nLayers + mLayerNrgFlux(iLayer) = -(iLayerNrgFlux(iLayer) - iLayerNrgFlux(iLayer-1))/mLayerDepth(iLayer) + if(globalPrintFlag)then + if(iLayer < 10) write(*,'(a,1x,i4,1x,10(f25.15,1x))') 'iLayer, iLayerNrgFlux(iLayer-1:iLayer), mLayerNrgFlux(iLayer) = ', iLayer, iLayerNrgFlux(iLayer-1:iLayer), mLayerNrgFlux(iLayer) + endif + end do + + endif ! if computing energy fluxes throughout the snow+soil domain + + + ! ***** + ! * CALCULATE THE LIQUID FLUX THROUGH VEGETATION... + ! ************************************************** + + ! check the need to compute the liquid water fluxes through vegetation + if(ixVegHyd/=integerMissing)then + + ! calculate liquid water fluxes through vegetation + call vegLiqFlux(& + ! input + computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + scalarCanopyLiqTrial, & ! intent(in): trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) + scalarRainfall, & ! intent(in): rainfall rate (kg m-2 s-1) + ! input-output: data structures + mpar_data, & ! intent(in): model parameters + diag_data, & ! intent(in): local HRU diagnostic model variables + ! output + scalarThroughfallRain, & ! intent(out): rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + scalarCanopyLiqDrainage, & ! intent(out): drainage of liquid water from the vegetation canopy (kg m-2 s-1) + scalarThroughfallRainDeriv, & ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) + scalarCanopyLiqDrainageDeriv, & ! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! calculate the net liquid water flux for the vegetation canopy + scalarCanopyNetLiqFlux = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + + ! calculate the total derivative in the downward liquid flux + scalarCanopyLiqDeriv = scalarThroughfallRainDeriv + scalarCanopyLiqDrainageDeriv + + ! test + if(globalPrintFlag)then + print*, '**' + print*, 'scalarRainfall = ', scalarRainfall + print*, 'scalarThroughfallRain = ', scalarThroughfallRain + print*, 'scalarCanopyEvaporation = ', scalarCanopyEvaporation + print*, 'scalarCanopyLiqDrainage = ', scalarCanopyLiqDrainage + print*, 'scalarCanopyNetLiqFlux = ', scalarCanopyNetLiqFlux + print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial + endif + + endif ! computing the liquid water fluxes through vegetation + + ! ***** + ! * CALCULATE THE LIQUID FLUX THROUGH SNOW... + ! ******************************************** + + ! check the need to compute liquid water fluxes through snow + if(nSnowOnlyHyd>0)then + + ! compute liquid fluxes through snow + call snowLiqFlxFida(& + ! input: model control + nSnow, & ! intent(in): number of snow layers + firstFluxCall, & ! intent(in): the first flux call (compute variables that are constant over the iterations) + (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution + ! input: forcing for the snow domain + scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) + scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) + ! input: model state vector + mLayerVolFracIce(1:nSnow), & ! intent(in) + mLayerVolFracLiqTrial(1:nSnow), & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) + ! input-output: data structures + indx_data, & ! intent(in): model indices + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + ! output: fluxes and derivatives + iLayerLiqFluxSnow(0:nSnow), & ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) + iLayerLiqFluxSnowDeriv(0:nSnow), & ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! define forcing for the soil domain + scalarRainPlusMelt = iLayerLiqFluxSnow(nSnow) ! drainage from the base of the snowpack + + ! calculate net liquid water fluxes for each soil layer (s-1) + do iLayer=1,nSnow + mLayerLiqFluxSnow(iLayer) = -(iLayerLiqFluxSnow(iLayer) - iLayerLiqFluxSnow(iLayer-1))/mLayerDepth(iLayer) + !write(*,'(a,1x,i4,1x,2(f16.10,1x))') 'iLayer, mLayerLiqFluxSnow(iLayer), iLayerLiqFluxSnow(iLayer-1) = ', & + ! iLayer, mLayerLiqFluxSnow(iLayer), iLayerLiqFluxSnow(iLayer-1) + end do + + ! compute drainage from the soil zone (needed for mass balance checks) + scalarSnowDrainage = iLayerLiqFluxSnow(nSnow) + + else + + ! define forcing for the soil domain for the case of no snow layers + ! NOTE: in case where nSnowOnlyHyd==0 AND snow layers exist, then scalarRainPlusMelt is taken from the previous flux evaluation + if(nSnow==0)then + scalarRainPlusMelt = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water & ! liquid flux from the canopy (m s-1) + + drainageMeltPond/iden_water ! melt of the snow without a layer (m s-1) + endif ! if no snow layers + + endif + + ! ***** + ! * CALCULATE THE LIQUID FLUX THROUGH SOIL... + ! ******************************************** + + ! check the need to calculate the liquid flux through soil + if(nSoilOnlyHyd>0)then + + ! calculate the liquid flux through soil + call soilLiqFlx(& + ! input: model control + nSoil, & ! intent(in): number of soil layers + firstSplitOper, & ! intent(in): flag indicating first flux call in a splitting operation + (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution + .true., & ! intent(in): flag indicating if derivatives are desired + ! input: trial state variables + mLayerTempTrial(nSnow+1:nLayers), & ! intent(in): trial temperature at the current iteration (K) + mLayerMatricHeadLiqTrial(1:nSoil), & ! intent(in): liquid water matric potential (m) + mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of ice (-) + ! input: pre-computed deriavatives + mLayerdTheta_dTk(nSnow+1:nLayers), & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + dPsiLiq_dTemp(1:nSoil), & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) + ! input: fluxes + scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) + scalarGroundEvaporation, & ! intent(in): ground evaporation (kg m-2 s-1) + scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) + ! input-output: data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model indices + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output: diagnostic variables for surface runoff + scalarMaxInfilRate, & ! intent(inout): maximum infiltration rate (m s-1) + scalarInfilArea, & ! intent(inout): fraction of unfrozen area where water can infiltrate (-) + scalarFrozenArea, & ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) + scalarSurfaceRunoff, & ! intent(inout): surface runoff (m s-1) + ! output: diagnostic variables for model layers + mLayerdTheta_dPsi, & ! intent(inout): derivative in the soil water characteristic w.r.t. psi (m-1) + mLayerdPsi_dTheta, & ! intent(inout): derivative in the soil water characteristic w.r.t. theta (m) + dHydCond_dMatric, & ! intent(inout): derivative in hydraulic conductivity w.r.t matric head (s-1) + ! output: fluxes + scalarInfiltration, & ! intent(inout): surface infiltration rate (m s-1) -- controls on infiltration only computed for iter==1 + iLayerLiqFluxSoil, & ! intent(inout): liquid fluxes at layer interfaces (m s-1) + mLayerTranspire, & ! intent(inout): transpiration loss from each soil layer (m s-1) + mLayerHydCond, & ! intent(inout): hydraulic conductivity in each layer (m s-1) + ! output: derivatives in fluxes w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below (m s-1 or s-1) + dq_dHydStateAbove, & ! intent(inout): derivatives in the flux w.r.t. matric head in the layer above (s-1) + dq_dHydStateBelow, & ! intent(inout): derivatives in the flux w.r.t. matric head in the layer below (s-1) + ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) + dq_dNrgStateAbove, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) + dq_dNrgStateBelow, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! calculate net liquid water fluxes for each soil layer (s-1) + do iLayer=1,nSoil + mLayerLiqFluxSoil(iLayer) = -(iLayerLiqFluxSoil(iLayer) - iLayerLiqFluxSoil(iLayer-1))/mLayerDepth(iLayer+nSnow) + !if(iLayer<8) write(*,'(a,1x,2(i4,1x),3(e20.10),f12.7)') 'iLayerLiqFluxSoil(iLayer-1), iLayerLiqFluxSoil(iLayer), mLayerLiqFluxSoil(iLayer) = ', iLayer-1, iLayer, & + ! iLayerLiqFluxSoil(iLayer-1), iLayerLiqFluxSoil(iLayer), mLayerLiqFluxSoil(iLayer), mLayerDepth(iLayer+nSnow) + end do + + ! calculate the soil control on infiltration + if(nSnow==0) then + ! * case of infiltration into soil + if(scalarMaxInfilRate > scalarRainPlusMelt)then ! infiltration is not rate-limited + scalarSoilControl = (1._dp - scalarFrozenArea)*scalarInfilArea + else + scalarSoilControl = 0._dp ! (scalarRainPlusMelt exceeds maximum infiltration rate + endif + else + ! * case of infiltration into snow + scalarSoilControl = 1._dp + endif + + ! compute drainage from the soil zone (needed for mass balance checks) + scalarSoilDrainage = iLayerLiqFluxSoil(nSoil) + + ! expand derivatives to the total water matric potential + ! NOTE: arrays are offset because computing derivatives in interface fluxes, at the top and bottom of the layer respectively + if(globalPrintFlag) print*, 'dPsiLiq_dPsi0(1:nSoil) = ', dPsiLiq_dPsi0(1:nSoil) + dq_dHydStateAbove(1:nSoil) = dq_dHydStateAbove(1:nSoil) *dPsiLiq_dPsi0(1:nSoil) + dq_dHydStateBelow(0:nSoil-1) = dq_dHydStateBelow(0:nSoil-1)*dPsiLiq_dPsi0(1:nSoil) + + endif ! if calculating the liquid flux through soil + + ! ***** + ! * CALCULATE THE GROUNDWATER FLOW... + ! ************************************ + + ! check if computing soil hydrology + if(nSoilOnlyHyd>0)then + + ! set baseflow fluxes to zero if the baseflow routine is not used + if(local_ixGroundwater/=qbaseTopmodel)then + ! (diagnostic variables in the data structures) + scalarExfiltration = 0._dp ! exfiltration from the soil profile (m s-1) + mLayerColumnOutflow(:) = 0._dp ! column outflow from each soil layer (m3 s-1) + ! (variables needed for the numerical solution) + mLayerBaseflow(:) = 0._dp ! baseflow from each soil layer (m s-1) + + ! topmodel-ish shallow groundwater + else ! local_ixGroundwater==qbaseTopmodel + + ! check the derivative matrix is sized appropriately + if(size(dBaseflow_dMatric,1)/=nSoil .or. size(dBaseflow_dMatric,2)/=nSoil)then + message=trim(message)//'expect dBaseflow_dMatric to be nSoil x nSoil' + err=20; return + endif + + ! compute the baseflow flux + call groundwatr(& + ! input: model control + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + firstFluxCall, & ! intent(in): logical flag to compute index of the lowest saturated layer + ! input: state and diagnostic variables + mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) + mLayerMatricHeadLiqTrial, & ! intent(in): liquid water matric potential (m) + mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of ice (-) + ! input: data structures + attr_data, & ! intent(in): model attributes + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output + ixSaturation, & ! intent(inout) index of lowest saturated layer (NOTE: only computed on the first iteration) + mLayerBaseflow, & ! intent(out): baseflow from each soil layer (m s-1) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + endif ! computing baseflow flux + + ! compute total baseflow from the soil zone (needed for mass balance checks) + scalarSoilBaseflow = sum(mLayerBaseflow) + + ! compute total runoff + scalarTotalRunoff = scalarSurfaceRunoff + scalarSoilDrainage + scalarSoilBaseflow + + endif ! if computing soil hydrology + + + ! ***** + ! (7) CALCULATE FLUXES FOR THE DEEP AQUIFER... + ! ******************************************** + + ! check if computing aquifer fluxes + if(ixAqWat/=integerMissing)then + + ! identify modeling decision + if(local_ixGroundwater==bigBucket)then + + ! compute fluxes for the big bucket + call bigAquifer(& + ! input: state variables and fluxes + scalarAquiferStorageTrial, & ! intent(in): trial value of aquifer storage (m) + scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) + scalarSoilDrainage, & ! intent(in): soil drainage (m s-1) + ! input: diagnostic variables and parameters + mpar_data, & ! intent(in): model parameter structure + diag_data, & ! intent(in): diagnostic variable structure + ! output: fluxes + scalarAquiferTranspire, & ! intent(out): transpiration loss from the aquifer (m s-1) + scalarAquiferRecharge, & ! intent(out): recharge to the aquifer (m s-1) + scalarAquiferBaseflow, & ! intent(out): total baseflow from the aquifer (m s-1) + dBaseflow_dAquifer, & ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! compute total runoff (overwrite previously calculated value before considering aquifer) + scalarTotalRunoff = scalarSurfaceRunoff + scalarAquiferBaseflow + + ! if no aquifer, then fluxes are zero + else + scalarAquiferTranspire = 0._dp ! transpiration loss from the aquifer (m s-1) + scalarAquiferRecharge = 0._dp ! recharge to the aquifer (m s-1) + scalarAquiferBaseflow = 0._dp ! total baseflow from the aquifer (m s-1) + dBaseflow_dAquifer = 0._dp ! change in baseflow flux w.r.t. aquifer storage (s-1) + end if ! no aquifer + + endif ! if computing aquifer fluxes + + ! ***** + ! (X) WRAP UP... + ! ************* + + ! define model flux vector for the vegetation sub-domain + if(ixCasNrg/=integerMissing) fluxVec(ixCasNrg) = scalarCanairNetNrgFlux/canopyDepth + if(ixVegNrg/=integerMissing) fluxVec(ixVegNrg) = scalarCanopyNetNrgFlux/canopyDepth + if(ixVegHyd/=integerMissing) fluxVec(ixVegHyd) = scalarCanopyNetLiqFlux ! NOTE: solid fluxes are handled separately + + ! populate the flux vector for energy + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + fluxVec( ixSnowSoilNrg(iLayer) ) = mLayerNrgFlux(iLayer) + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! populate the flux vector for hydrology + ! NOTE: ixVolFracWat and ixVolFracLiq can also include states in the soil domain, hence enable primary variable switching + if(nSnowSoilHyd>0)then ! check if any hydrology states exist + do iLayer=1,nLayers + if(ixSnowSoilHyd(iLayer)/=integerMissing)then ! check if a given hydrology state exists + select case( layerType(iLayer) ) + case(iname_snow); fluxVec( ixSnowSoilHyd(iLayer) ) = mLayerLiqFluxSnow(iLayer) + case(iname_soil); fluxVec( ixSnowSoilHyd(iLayer) ) = mLayerLiqFluxSoil(iLayer-nSnow) + case default; err=20; message=trim(message)//'expect layerType to be either iname_snow or iname_soil'; return + end select + endif ! if a given hydrology state exists + end do ! looping through non-missing energy state variables in the snow+soil domain + endif ! if any hydrology states exist + + ! compute the flux vector for the aquifer + if(ixAqWat/=integerMissing) fluxVec(ixAqWat) = scalarAquiferTranspire + scalarAquiferRecharge - scalarAquiferBaseflow + + ! set the first flux call to false + firstFluxCall=.false. + + ! end association to variables in the data structures + end associate + + end subroutine computFluxFida + + + ! ********************************************************************************************************** + ! public subroutine soilCmpres: compute soil compressibility (-) and its derivative w.r.t matric head (m-1) + ! ********************************************************************************************************** + subroutine soilCmpres(& + ! input: + ixRichards, & ! intent(in): choice of option for Richards' equation + ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers + mLayerMatricHead, & ! intent(in): matric head at the start of the time step (m) + mLayerMatricHeadTrial, & ! intent(in): trial value of matric head (m) + mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) + mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice content in each soil layer (-) + specificStorage, & ! intent(in): specific storage coefficient (m-1) + theta_sat, & ! intent(in): soil porosity (-) + ! output: + compress, & ! intent(out): compressibility of the soil matrix (-) + dCompress_dPsi, & ! intent(out): derivative in compressibility w.r.t. matric head (m-1) + err,message) ! intent(out): error code and error message + implicit none + ! input: + integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation + integer(i4b),intent(in) :: ixBeg,ixEnd ! start and end indices defining desired layers + real(dp),intent(in) :: mLayerMatricHead(:) ! matric head at the start of the time step (m) + real(dp),intent(in) :: mLayerMatricHeadTrial(:) ! trial value for matric head (m) + real(dp),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) + real(dp),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) + real(dp),intent(in) :: specificStorage ! specific storage coefficient (m-1) + real(dp),intent(in) :: theta_sat(:) ! soil porosity (-) + ! output: + real(dp),intent(inout) :: compress(:) ! soil compressibility (-) + real(dp),intent(inout) :: dCompress_dPsi(:) ! derivative in soil compressibility w.r.t. matric head (m-1) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local variables + integer(i4b) :: iLayer ! index of soil layer + ! -------------------------------------------------------------- + ! initialize error control + err=0; message='soilCmpres/' + ! (only compute for the mixed form of Richards' equation) + if(ixRichards==mixdform)then + do iLayer=1,size(mLayerMatricHead) + if(iLayer>=ixBeg .and. iLayer<=ixEnd)then + ! compute the derivative for the compressibility term (m-1) + dCompress_dPsi(iLayer) = specificStorage*(mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer))/theta_sat(iLayer) + ! compute the compressibility term (-) + compress(iLayer) = (mLayerMatricHeadTrial(iLayer) - mLayerMatricHead(iLayer))*dCompress_dPsi(iLayer) + endif + end do + else + compress(:) = 0._dp + dCompress_dPsi(:) = 0._dp + end if + end subroutine soilCmpres + +end module computFluxFida_module diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 2c1f0bfe1..202cef3fb 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -134,7 +134,7 @@ subroutine eval8summaFida(& mLayerVolFracWatTrial, & !intent(inout) mLayerVolFracWatPrev, & !intent(inout) mLayerVolFracIceTrial, & !intent(inout) - mLayerVolFracIcePrev, & !intent(inout) + mLayerVolFracIcePrev, & !intent(in) mLayerEnthalpyPrev, & ! intent(in) mLayerEnthalpyTrial, & ! intent(out) feasible, & ! intent(out): flag to denote the feasibility of the solution @@ -149,7 +149,7 @@ subroutine eval8summaFida(& USE updateVarsFida_module, only:updateVarsFida ! update variables USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy USE soilCmpresFida_module, only:soilCmpresFida ! compute soil compression - USE computFlux_module, only:computFlux ! compute fluxes given a state vector + USE computFluxFida_module, only:computFluxFida ! compute fluxes given a state vector USE computHeatCap_module,only:computHeatCapAnalytic ! compute heat capacity USE computHeatCap_module,only:computCm USE computHeatCap_module, only:computStatMult @@ -563,7 +563,7 @@ subroutine eval8summaFida(& ! save the number of flux calls per time step indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) + 1 ! compute the fluxes for a given state vector - call computFlux(& + call computFluxFida(& ! input-output: model control nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -575,6 +575,7 @@ subroutine eval8summaFida(& scalarSolution, & ! intent(in): flag to indicate the scalar solution scalarSfcMeltPond/dt, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) ! input: state variables + mLayerVolFracIcePrev, & ! intent(in) scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 11ab8b280..c41cdbdbf 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -413,7 +413,7 @@ subroutine fidaSolver( & !********************************************************************************** tret(1) = t0 do while(tret(1) < dt) - eqns_data%firstFluxCall = .true. + eqns_data%firstFluxCall = .false. ! call IDASolve retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) if( retval < 0 ) exit diff --git a/build/source/engine/snowLiqFlx.f90 b/build/source/engine/snowLiqFlx.f90 index 53b4fb29a..124971ad2 100755 --- a/build/source/engine/snowLiqFlx.f90 +++ b/build/source/engine/snowLiqFlx.f90 @@ -43,6 +43,7 @@ module snowLiqFlx_module implicit none private public::snowLiqFlx +public::snowLiqFlxFida contains @@ -197,6 +198,161 @@ subroutine snowLiqFlx(& end associate end subroutine snowLiqFlx + + + + ! ************************************************************************************************ + ! public subroutine snowLiqFlxFida: compute liquid water flux through the snowpack + ! ************************************************************************************************ + subroutine snowLiqFlxFida(& + ! input: model control + nSnow, & ! intent(in): number of snow layers + firstFluxCall, & ! intent(in): the first flux call + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: forcing for the snow domain + scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) + scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) + ! input: model state vector + mLayerVolFracIce, & ! intent(in) + mLayerVolFracLiqTrial, & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) + ! input-output: data structures + indx_data, & ! intent(in): model indices + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + ! output: fluxes and derivatives + iLayerLiqFluxSnow, & ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) + iLayerLiqFluxSnowDeriv, & ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) + ! output: error control + err,message) ! intent(out): error control + implicit none + ! input: model control + integer(i4b),intent(in) :: nSnow ! number of snow layers + logical(lgt),intent(in) :: firstFluxCall ! the first flux call + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + ! input: forcing for the snow domain + real(dp),intent(in) :: scalarThroughfallRain ! computed throughfall rate (kg m-2 s-1) + real(dp),intent(in) :: scalarCanopyLiqDrainage ! computed drainage of liquid water (kg m-2 s-1) + ! input: model state vector + real(dp),intent(in) :: mLayerVolFracIce(:) + real(dp),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value of volumetric fraction of liquid water at the current iteration (-) + ! input-output: data structures + type(var_ilength),intent(in) :: indx_data ! model indices + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + ! output: fluxes and derivatives + real(dp),intent(inout) :: iLayerLiqFluxSnow(0:) ! vertical liquid water flux at layer interfaces (m s-1) + real(dp),intent(inout) :: iLayerLiqFluxSnowDeriv(0:) ! derivative in vertical liquid water flux at layer interfaces (m s-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------------------------------------------------ + ! local variables + integer(i4b) :: i ! search index for scalar solution + integer(i4b) :: iLayer ! layer index + integer(i4b) :: ixTop ! top layer in subroutine call + integer(i4b) :: ixBot ! bottom layer in subroutine call + real(dp) :: multResid ! multiplier for the residual water content (-) + real(dp),parameter :: residThrs=550._dp ! ice density threshold to reduce residual liquid water content (kg m-3) + real(dp),parameter :: residScal=10._dp ! scaling factor for residual liquid water content reduction factor (kg m-3) + real(dp),parameter :: maxVolIceContent=0.7_dp ! maximum volumetric ice content to store water (-) + real(dp) :: availCap ! available storage capacity [0,1] (-) + real(dp) :: relSaturn ! relative saturation [0,1] (-) + ! ------------------------------------------------------------------------------------------------------------------------------------------ + ! make association of local variables with information in the data structures + associate(& + ! input: layer indices + ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers + ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat, & ! intent(in): index in the state subset for hydrology state variables in the snow domain + ! input: snow properties and parameters + Fcapil => mpar_data%var(iLookPARAM%Fcapil)%dat(1), & ! intent(in): capillary retention as a fraction of the total pore volume (-) + k_snow => mpar_data%var(iLookPARAM%k_snow)%dat(1), & ! intent(in): hydraulic conductivity of snow (m s-1), 0.0055 = approx. 20 m/hr, from UEB + mw_exp => mpar_data%var(iLookPARAM%mw_exp)%dat(1), & ! intent(in): exponent for meltwater flow (-) + ! input/output: diagnostic variables -- only computed for the first iteration + mLayerPoreSpace => diag_data%var(iLookDIAG%mLayerPoreSpace)%dat, & ! intent(inout): pore space in each snow layer (-) + mLayerThetaResid => diag_data%var(iLookDIAG%mLayerThetaResid)%dat & ! intent(inout): esidual volumetric liquid water content in each snow layer (-) + ) ! association of local variables with information in the data structures + ! ------------------------------------------------------------------------------------------------------------------------------------------ + ! initialize error control + err=0; message='snowLiqFlxFida/' + + ! check that the input vectors match nSnow + if(size(mLayerVolFracLiqTrial)/=nSnow .or. size(mLayerVolFracIce)/=nSnow .or. & + size(iLayerLiqFluxSnow)/=nSnow+1 .or. size(iLayerLiqFluxSnowDeriv)/=nSnow+1) then + err=20; message=trim(message)//'size mismatch of input/output vectors'; return + end if + + ! check the meltwater exponent is >=1 + if(mw_exp<1._dp)then; err=20; message=trim(message)//'meltwater exponent < 1'; return; end if + + ! get the indices for the snow+soil layers + ixTop = integerMissing + if(scalarSolution)then + ! WARNING: Previously this was implemented as: + ! ixLayerDesired = pack(ixLayerState, ixSnowOnlyHyd/=integerMissing) + ! ixTop = ixLayerDesired(1) + ! ixBot = ixLayerDesired(1) + ! This implementation can result in a segfault when using JRDN layering. + ! The segfault occurs when trying to access `mw_exp` in: + ! iLayerLiqFluxSnow(iLayer) = k_snow*relSaturn**mw_exp + ! Debugging found that the `pack` statement caused `mw_exp` to no longer be accessible. + ! We have not been able to determine the underlying reason for this segfault. + do i=1,size(ixSnowOnlyHyd) + if(ixSnowOnlyHyd(i) /= integerMissing)then + ixTop=ixLayerState(i) + ixBot=ixTop + exit ! break out of loop once found + endif + end do + if(ixTop == integerMissing)then + err=20; message=trim(message)//'Unable to identify snow layer for scalar solution!'; return + end if + else + ixTop = 1 + ixBot = nSnow + endif + + ! define the liquid flux at the upper boundary (m s-1) + iLayerLiqFluxSnow(0) = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water + iLayerLiqFluxSnowDeriv(0) = 0._dp + + ! compute properties fixed over the time step + if(firstFluxCall)then + ! loop through snow layers + do iLayer=1,nSnow + ! compute the reduction in liquid water holding capacity at high snow density (-) + multResid = 1._dp / ( 1._dp + exp( (mLayerVolFracIce(iLayer)*iden_ice - residThrs) / residScal) ) + ! compute the pore space (-) + mLayerPoreSpace(iLayer) = 1._dp - mLayerVolFracIce(iLayer) + ! compute the residual volumetric liquid water content (-) + mLayerThetaResid(iLayer) = Fcapil*mLayerPoreSpace(iLayer) * multResid + end do ! (looping through snow layers) + end if ! (if the first flux call) + + ! compute fluxes + do iLayer=ixTop,ixBot ! (loop through snow layers) + ! check that flow occurs + if(mLayerVolFracLiqTrial(iLayer) > mLayerThetaResid(iLayer))then + ! compute the relative saturation (-) + availCap = mLayerPoreSpace(iLayer) - mLayerThetaResid(iLayer) ! available capacity + relSaturn = (mLayerVolFracLiqTrial(iLayer) - mLayerThetaResid(iLayer)) / availCap ! relative saturation + iLayerLiqFluxSnow(iLayer) = k_snow*relSaturn**mw_exp + iLayerLiqFluxSnowDeriv(iLayer) = ( (k_snow*mw_exp)/availCap ) * relSaturn**(mw_exp - 1._dp) + if(mLayerVolFracIce(iLayer) > maxVolIceContent)then ! NOTE: use start-of-step ice content, to avoid convergence problems + ! ** allow liquid water to pass through under very high ice density + iLayerLiqFluxSnow(iLayer) = iLayerLiqFluxSnow(iLayer) + iLayerLiqFluxSnow(iLayer-1) !NOTE: derivative may need to be updated in future. + end if + else ! flow does not occur + iLayerLiqFluxSnow(iLayer) = 0._dp + iLayerLiqFluxSnowDeriv(iLayer) = 0._dp + endif ! storage above residual content + end do ! loop through snow layers + + ! end association of local variables with information in the data structures + end associate + + end subroutine snowLiqFlxFida end module snowLiqFlx_module From b92dc96713ea685f8c4670f9b7da18d9e6a5be2b Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 7 May 2021 11:24:46 -0600 Subject: [PATCH 0089/1472] mLayerMatricHeadPrev in computFlux --- build/source/engine/computFluxFida.f90 | 4 +- build/source/engine/eval8summaFida.f90 | 3 +- build/source/engine/fidaSolver.f90 | 2 +- build/source/engine/vegNrgFluxFida.f90 | 3200 ++++++++++++++++++++++++ 4 files changed, 3206 insertions(+), 3 deletions(-) create mode 100755 build/source/engine/vegNrgFluxFida.f90 diff --git a/build/source/engine/computFluxFida.f90 b/build/source/engine/computFluxFida.f90 index ce0c144a0..7819d261e 100755 --- a/build/source/engine/computFluxFida.f90 +++ b/build/source/engine/computFluxFida.f90 @@ -115,6 +115,7 @@ subroutine computFluxFida(& drainageMeltPond, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) ! input: state variables mLayerVolFracIce, & ! intent(in) + mLayerMatricHead, & ! intent(in) scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) @@ -167,7 +168,8 @@ subroutine computFluxFida(& logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution real(dp),intent(in) :: drainageMeltPond ! drainage from the surface melt pond (kg m-2 s-1) ! input: state variables - real(dp),intent(in) :: mLayerVolFracIce(:) ! + real(dp),intent(in) :: mLayerVolFracIce(:) ! + real(dp),intent(in) :: mLayerMatricHead(:) ! real(dp),intent(in) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) real(dp),intent(in) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) real(dp),intent(in) :: mLayerTempTrial(:) ! trial value for temperature of each snow/soil layer (K) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 202cef3fb..0e60231c4 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -257,7 +257,7 @@ subroutine eval8summaFida(& real(dp) :: scalarCanopyCmTrial real(dp),dimension(nLayers) :: mLayerCmTrial logical(lgt),parameter :: updateCp=.true. - logical(lgt),parameter :: needCm=.true. + logical(lgt),parameter :: needCm=.false. @@ -576,6 +576,7 @@ subroutine eval8summaFida(& scalarSfcMeltPond/dt, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) ! input: state variables mLayerVolFracIcePrev, & ! intent(in) + mLayerMatricHeadPrev, & ! intent(in) scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index c41cdbdbf..11ab8b280 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -413,7 +413,7 @@ subroutine fidaSolver( & !********************************************************************************** tret(1) = t0 do while(tret(1) < dt) - eqns_data%firstFluxCall = .false. + eqns_data%firstFluxCall = .true. ! call IDASolve retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) if( retval < 0 ) exit diff --git a/build/source/engine/vegNrgFluxFida.f90 b/build/source/engine/vegNrgFluxFida.f90 new file mode 100755 index 000000000..bd55c7347 --- /dev/null +++ b/build/source/engine/vegNrgFluxFida.f90 @@ -0,0 +1,3200 @@ +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . + +module vegNrgFluxFida_module + +! data types +USE nrtype + +! derived types to define the data structures +USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (dp) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (dp) + model_options ! defines the model decisions + +! indices that define elements of the data structures +USE var_lookup,only:iLookTYPE ! named variables for structure elements +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookFLUX ! named variables for structure elements +USE var_lookup,only:iLookFORCE ! named variables for structure elements +USE var_lookup,only:iLookPARAM ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements +USE var_lookup,only:iLookBVAR ! named variables for structure elements +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure + +! constants +USE multiconst,only:gravity ! acceleration of gravity (m s-2) +USE multiconst,only:vkc ! von Karman's constant (-) +USE multiconst,only:w_ratio ! molecular ratio water to dry air (-) +USE multiconst,only:R_wv ! gas constant for water vapor (Pa K-1 m3 kg-1; J kg-1 K-1) +USE multiconst,only:Cp_air ! specific heat of air (J kg-1 K-1) +USE multiconst,only:Cp_ice ! specific heat of ice (J kg-1 K-1) +USE multiconst,only:Cp_soil ! specific heat of soil (J kg-1 K-1) +USE multiconst,only:Cp_water ! specific heat of liquid water (J kg-1 K-1) +USE multiconst,only:Tfreeze ! temperature at freezing (K) +USE multiconst,only:LH_fus ! latent heat of fusion (J kg-1) +USE multiconst,only:LH_vap ! latent heat of vaporization (J kg-1) +USE multiconst,only:LH_sub ! latent heat of sublimation (J kg-1) +USE multiconst,only:sb ! Stefan Boltzman constant (W m-2 K-4) +USE multiconst,only:iden_air ! intrinsic density of air (kg m-3) +USE multiconst,only:iden_ice ! intrinsic density of ice (kg m-3) +USE multiconst,only:iden_water ! intrinsic density of liquid water (kg m-3) + +! look-up values for method used to compute derivative +USE mDecisions_module,only: & + numerical, & ! numerical solution + analytical ! analytical solution + +! look-up values for choice of boundary conditions for thermodynamics +USE mDecisions_module,only: & + prescribedTemp, & ! prescribed temperature + energyFlux, & ! energy flux + zeroFlux ! zero flux + +! look-up values for the choice of parameterization for vegetation roughness length and displacement height +USE mDecisions_module,only: & + Raupach_BLM1994, & ! Raupach (BLM 1994) "Simplified expressions..." + CM_QJRMS1988, & ! Choudhury and Monteith (QJRMS 1988) "A four layer model for the heat budget..." + vegTypeTable ! constant parameters dependent on the vegetation type + +! look-up values for the choice of parameterization for canopy emissivity +USE mDecisions_module,only: & + simplExp, & ! simple exponential function + difTrans ! parameterized as a function of diffuse transmissivity + +! look-up values for the choice of canopy wind profile +USE mDecisions_module,only: & + exponential, & ! exponential wind profile extends to the surface + logBelowCanopy ! logarithmic profile below the vegetation canopy + +! look-up values for choice of stability function +USE mDecisions_module,only: & + standard, & ! standard MO similarity, a la Anderson (1976) + louisInversePower, & ! Louis (1979) inverse power function + mahrtExponential ! Mahrt (1987) exponential + +! look-up values for the choice of groundwater representation (local-column, or single-basin) +USE mDecisions_module,only: & + localColumn, & ! separate groundwater representation in each local soil column + singleBasin ! single groundwater store over the entire basin + +! ------------------------------------------------------------------------------------------------- +! privacy +implicit none +private +public::vegNrgFluxFida +! dimensions +integer(i4b),parameter :: nBands=2 ! number of spectral bands for shortwave radiation +! named variables +integer(i4b),parameter :: ist = 1 ! Surface type: IST=1 => soil; IST=2 => lake +integer(i4b),parameter :: isc = 4 ! Soil color type +integer(i4b),parameter :: ice = 0 ! Surface type: ICE=0 => soil; ICE=1 => sea-ice +! spatial indices +integer(i4b),parameter :: iLoc = 1 ! i-location +integer(i4b),parameter :: jLoc = 1 ! j-location +! algorithmic parameters +real(dp),parameter :: missingValue=-9999._dp ! missing value, used when diagnostic or state variables are undefined +real(dp),parameter :: verySmall=1.e-6_dp ! used as an additive constant to check if substantial difference among real numbers +real(dp),parameter :: tinyVal=epsilon(1._dp) ! used as an additive constant to check if substantial difference among real numbers +real(dp),parameter :: mpe=1.e-6_dp ! prevents overflow error if division by zero +real(dp),parameter :: dx=1.e-11_dp ! finite difference increment +! control +logical(lgt) :: printflag ! flag to turn on printing +contains + + ! ******************************************************************************************************* + ! public subroutine vegNrgFluxFida: muster program to compute energy fluxes at vegetation and ground surfaces + ! ******************************************************************************************************* + subroutine vegNrgFluxFida(& + ! input: model control + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + + ! input: model state variables + upperBoundTemp, & ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature + canairTempTrial, & ! intent(in): trial value of the canopy air space temperature (K) + canopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + groundTempTrial, & ! intent(in): trial value of ground temperature (K) + canopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) + canopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) + + ! input: model derivatives + dCanLiq_dTcanopy, & ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) + + ! input/output: data structures + type_data, & ! intent(in): type of vegetation and soil + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): state vector geometry + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + bvar_data, & ! intent(in): model variables for the local basin + model_decisions, & ! intent(in): model decisions + + ! output: liquid water fluxes associated with evaporation/transpiration (needed for coupling) + returnCanopyTranspiration, & ! intent(out): canopy transpiration (kg m-2 s-1) + returnCanopyEvaporation, & ! intent(out): canopy evaporation/condensation (kg m-2 s-1) + returnGroundEvaporation, & ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + + ! output: fluxes + canairNetFlux, & ! intent(out): net energy flux for the canopy air space (W m-2) + canopyNetFlux, & ! intent(out): net energy flux for the vegetation canopy (W m-2) + groundNetFlux, & ! intent(out): net energy flux for the ground surface (W m-2) + + ! output: energy flux derivatives + dCanairNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) + dCanairNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) + dCanairNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) + dCanopyNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) + dCanopyNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) + dCanopyNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) + dGroundNetFlux_dCanairTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) + dGroundNetFlux_dCanopyTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) + dGroundNetFlux_dGroundTemp, & ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + + ! output liquid water flux derivarives (canopy evap) + dCanopyEvaporation_dCanLiq, & ! intent(out): derivative in canopy evaporation w.r.t. canopy liquid water content (s-1) + dCanopyEvaporation_dTCanair, & ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTCanopy, & ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTGround, & ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + + ! output: liquid water flux derivarives (ground evap) + dGroundEvaporation_dCanLiq, & ! intent(out): derivative in ground evaporation w.r.t. canopy liquid water content (s-1) + dGroundEvaporation_dTCanair, & ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTCanopy, & ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTGround, & ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + + ! output: cross derivative terms + dCanopyNetFlux_dCanLiq, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dGroundNetFlux_dCanLiq, & ! intent(out): derivative in net ground fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + + ! output: error control + err,message) ! intent(out): error control + + ! utilities + USE expIntegral_module,only:expInt ! function to calculate the exponential integral + ! conversion functions + USE conv_funcs_module,only:satVapPress ! function to compute the saturated vapor pressure (Pa) + USE conv_funcs_module,only:getLatentHeatValue ! function to identify latent heat of vaporization/sublimation (J kg-1) + ! stomatal resistance + USE stomResist_module,only:stomResist ! subroutine to calculate stomatal resistance + USE vegNrgFlux_module,only:wettedFrac + ! compute energy and mass fluxes for vegetation + implicit none + + ! --------------------------------------------------------------------------------------- + ! * dummy variables + ! --------------------------------------------------------------------------------------- + ! input: model control + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(in) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + + ! input: model state variables + real(dp),intent(in) :: upperBoundTemp ! temperature of the upper boundary (K) --> NOTE: use air temperature + real(dp),intent(in) :: canairTempTrial ! trial value of canopy air space temperature (K) + real(dp),intent(in) :: canopyTempTrial ! trial value of canopy temperature (K) + real(dp),intent(in) :: groundTempTrial ! trial value of ground temperature (K) + real(dp),intent(in) :: canopyIceTrial ! trial value of mass of ice on the vegetation canopy (kg m-2) + real(dp),intent(in) :: canopyLiqTrial ! trial value of mass of liquid water on the vegetation canopy (kg m-2) + + ! input: model derivatives + real(dp),intent(in) :: dCanLiq_dTcanopy ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) + + ! input/output: data structures + type(var_i),intent(in) :: type_data ! type of vegetation and soil + type(var_d),intent(in) :: forc_data ! model forcing data + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! state vector geometry + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin + type(model_options),intent(in) :: model_decisions(:) ! model decisions + + ! output: liquid water fluxes associated with evaporation/transpiration (needed for coupling) + real(dp),intent(out) :: returnCanopyTranspiration ! canopy transpiration (kg m-2 s-1) + real(dp),intent(out) :: returnCanopyEvaporation ! canopy evaporation/condensation (kg m-2 s-1) + real(dp),intent(out) :: returnGroundEvaporation ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + + ! output: fluxes + real(dp),intent(out) :: canairNetFlux ! net energy flux for the canopy air space (W m-2) + real(dp),intent(out) :: canopyNetFlux ! net energy flux for the vegetation canopy (W m-2) + real(dp),intent(out) :: groundNetFlux ! net energy flux for the ground surface (W m-2) + + ! output: energy flux derivatives + real(dp),intent(out) :: dCanairNetFlux_dCanairTemp ! derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) + real(dp),intent(out) :: dCanairNetFlux_dCanopyTemp ! derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) + real(dp),intent(out) :: dCanairNetFlux_dGroundTemp ! derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) + real(dp),intent(out) :: dCanopyNetFlux_dCanairTemp ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) + real(dp),intent(out) :: dCanopyNetFlux_dCanopyTemp ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) + real(dp),intent(out) :: dCanopyNetFlux_dGroundTemp ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) + real(dp),intent(out) :: dGroundNetFlux_dCanairTemp ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) + real(dp),intent(out) :: dGroundNetFlux_dCanopyTemp ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) + real(dp),intent(out) :: dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + + ! output: liquid flux derivatives (canopy evap) + real(dp),intent(out) :: dCanopyEvaporation_dCanLiq ! derivative in canopy evaporation w.r.t. canopy liquid water content (s-1) + real(dp),intent(out) :: dCanopyEvaporation_dTCanair ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(dp),intent(out) :: dCanopyEvaporation_dTCanopy ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(dp),intent(out) :: dCanopyEvaporation_dTGround ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + + ! output: liquid flux derivatives (ground evap) + real(dp),intent(out) :: dGroundEvaporation_dCanLiq ! derivative in ground evaporation w.r.t. canopy liquid water content (s-1) + real(dp),intent(out) :: dGroundEvaporation_dTCanair ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(dp),intent(out) :: dGroundEvaporation_dTCanopy ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(dp),intent(out) :: dGroundEvaporation_dTGround ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + + ! output: cross derivative terms + real(dp),intent(out) :: dCanopyNetFlux_dCanLiq ! derivative in net canopy fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + real(dp),intent(out) :: dGroundNetFlux_dCanLiq ! derivative in net ground fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + + ! --------------------------------------------------------------------------------------- + ! * local variables + ! --------------------------------------------------------------------------------------- + ! local (general) + character(LEN=256) :: cmessage ! error message of downwind routine + real(dp) :: VAI ! vegetation area index (m2 m-2) + real(dp) :: exposedVAI ! exposed vegetation area index (m2 m-2) + real(dp) :: totalCanopyWater ! total water on the vegetation canopy (kg m-2) + real(dp) :: scalarAquiferStorage ! aquifer storage (m) + + ! local (compute numerical derivatives) + integer(i4b),parameter :: unperturbed=1 ! named variable to identify the case of unperturbed state variables + integer(i4b),parameter :: perturbStateGround=2 ! named variable to identify the case where we perturb the ground temperature + integer(i4b),parameter :: perturbStateCanopy=3 ! named variable to identify the case where we perturb the canopy temperature + integer(i4b),parameter :: perturbStateCanair=4 ! named variable to identify the case where we perturb the canopy air temperature + integer(i4b),parameter :: perturbStateCanLiq=5 ! named variable to identify the case where we perturb the canopy liquid water content + integer(i4b) :: itry ! index of flux evaluation + integer(i4b) :: nFlux ! number of flux evaluations + real(dp) :: groundTemp ! value of ground temperature used in flux calculations (may be perturbed) + real(dp) :: canopyTemp ! value of canopy temperature used in flux calculations (may be perturbed) + real(dp) :: canairTemp ! value of canopy air temperature used in flux calculations (may be perturbed) + real(dp) :: try0,try1 ! trial values to evaluate specific derivatives (testing only) + + ! local (saturation vapor pressure of veg) + real(dp) :: TV_celcius ! vegetaion temperature (C) + real(dp) :: TG_celcius ! ground temperature (C) + real(dp) :: dSVPCanopy_dCanopyTemp ! derivative in canopy saturated vapor pressure w.r.t. vegetation temperature (Pa/K) + real(dp) :: dSVPGround_dGroundTemp ! derivative in ground saturated vapor pressure w.r.t. ground temperature (Pa/K) + + ! local (wetted canopy area) + real(dp) :: fracLiquidCanopy ! fraction of liquid water in the canopy (-) + real(dp) :: canopyWetFraction ! trial value of the canopy wetted fraction (-) + real(dp) :: dCanopyWetFraction_dWat ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) + real(dp) :: dCanopyWetFraction_dT ! derivative in wetted fraction w.r.t. canopy temperature (K-1) + + ! local (longwave radiation) + real(dp) :: expi ! exponential integral + real(dp) :: scaleLAI ! scaled LAI (computing diffuse transmissivity) + real(dp) :: diffuseTrans ! diffuse transmissivity (-) + real(dp) :: groundEmissivity ! emissivity of the ground surface (-) + real(dp),parameter :: vegEmissivity=0.98_dp ! emissivity of vegetation (0.9665 in JULES) (-) + real(dp),parameter :: soilEmissivity=0.98_dp ! emmisivity of the soil (0.9665 in JULES) (-) + real(dp),parameter :: snowEmissivity=0.99_dp ! emissivity of snow (-) + real(dp) :: dLWNetCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) + real(dp) :: dLWNetGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) + real(dp) :: dLWNetCanopy_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) + real(dp) :: dLWNetGround_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) + + ! local (aerodynamic resistance) + real(dp) :: scalarCanopyStabilityCorrection_old ! stability correction for the canopy (-) + real(dp) :: scalarGroundStabilityCorrection_old ! stability correction for the ground surface (-) + + ! local (turbulent heat transfer) + real(dp) :: z0Ground ! roughness length of the ground (ground below the canopy or non-vegetated surface) (m) + real(dp) :: soilEvapFactor ! soil water control on evaporation from non-vegetated surfaces + real(dp) :: soilRelHumidity_noSnow ! relative humidity in the soil pores [0-1] + real(dp) :: scalarLeafConductance ! leaf conductance (m s-1) + real(dp) :: scalarCanopyConductance ! canopy conductance (m s-1) + real(dp) :: scalarGroundConductanceSH ! ground conductance for sensible heat (m s-1) + real(dp) :: scalarGroundConductanceLH ! ground conductance for latent heat -- includes soil resistance (m s-1) + real(dp) :: scalarEvapConductance ! conductance for evaporation (m s-1) + real(dp) :: scalarTransConductance ! conductance for transpiration (m s-1) + real(dp) :: scalarTotalConductanceSH ! total conductance for sensible heat (m s-1) + real(dp) :: scalarTotalConductanceLH ! total conductance for latent heat (m s-1) + real(dp) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + real(dp) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + real(dp) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + real(dp) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + real(dp) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + real(dp) :: turbFluxCanair ! total turbulent heat fluxes exchanged at the canopy air space (W m-2) + real(dp) :: turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) + real(dp) :: turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) + + ! local (turbulent heat transfer -- compute numerical derivatives) + ! (temporary scalar resistances when states are perturbed) + real(dp) :: trialLeafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) + real(dp) :: trialGroundResistance ! below canopy aerodynamic resistance (s m-1) + real(dp) :: trialCanopyResistance ! above canopy aerodynamic resistance (s m-1) + real(dp) :: notUsed_RiBulkCanopy ! bulk Richardson number for the canopy (-) + real(dp) :: notUsed_RiBulkGround ! bulk Richardson number for the ground surface (-) + real(dp) :: notUsed_z0Canopy ! roughness length of the vegetation canopy (m) + real(dp) :: notUsed_WindReductionFactor ! canopy wind reduction factor (-) + real(dp) :: notUsed_ZeroPlaneDisplacement ! zero plane displacement (m) + real(dp) :: notUsed_scalarCanopyStabilityCorrection ! stability correction for the canopy (-) + real(dp) :: notUsed_scalarGroundStabilityCorrection ! stability correction for the ground surface (-) + real(dp) :: notUsed_EddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) + real(dp) :: notUsed_FrictionVelocity ! friction velocity (m s-1) + real(dp) :: notUsed_WindspdCanopyTop ! windspeed at the top of the canopy (m s-1) + real(dp) :: notUsed_WindspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) + real(dp) :: notUsed_dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + real(dp) :: notUsed_dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + real(dp) :: notUsed_dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + real(dp) :: notUsed_dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + real(dp) :: notUsed_dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + + ! (fluxes after perturbations in model states -- canopy air space) + real(dp) :: turbFluxCanair_dStateCanair ! turbulent exchange from the canopy air space to the atmosphere, after canopy air temperature is perturbed (W m-2) + real(dp) :: turbFluxCanair_dStateCanopy ! turbulent exchange from the canopy air space to the atmosphere, after canopy temperature is perturbed (W m-2) + real(dp) :: turbFluxCanair_dStateGround ! turbulent exchange from the canopy air space to the atmosphere, after ground temperature is perturbed (W m-2) + real(dp) :: turbFluxCanair_dStateCanliq ! turbulent exchange from the canopy air space to the atmosphere, after canopy liquid water content is perturbed (W m-2) + ! (fluxes after perturbations in model states -- vegetation canopy) + real(dp) :: turbFluxCanopy_dStateCanair ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy air temperature is perturbed (W m-2) + real(dp) :: turbFluxCanopy_dStateCanopy ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy temperature is perturbed (W m-2) + real(dp) :: turbFluxCanopy_dStateGround ! total turbulent heat fluxes from the canopy to the canopy air space, after ground temperature is perturbed (W m-2) + real(dp) :: turbFluxCanopy_dStateCanLiq ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy liquid water content is perturbed (W m-2) + + ! (fluxes after perturbations in model states -- ground surface) + real(dp) :: turbFluxGround_dStateCanair ! total turbulent heat fluxes from the ground to the canopy air space, after canopy air temperature is perturbed (W m-2) + real(dp) :: turbFluxGround_dStateCanopy ! total turbulent heat fluxes from the ground to the canopy air space, after canopy temperature is perturbed (W m-2) + real(dp) :: turbFluxGround_dStateGround ! total turbulent heat fluxes from the ground to the canopy air space, after ground temperature is perturbed (W m-2) + real(dp) :: turbFluxGround_dStateCanLiq ! total turbulent heat fluxes from the ground to the canopy air space, after canopy liquid water content is perturbed (W m-2) + + ! (fluxes after perturbations in model states -- canopy evaporation) + real(dp) :: latHeatCanEvap_dStateCanair ! canopy evaporation after canopy air temperature is perturbed (W m-2) + real(dp) :: latHeatCanEvap_dStateCanopy ! canopy evaporation after canopy temperature is perturbed (W m-2) + real(dp) :: latHeatCanEvap_dStateGround ! canopy evaporation after ground temperature is perturbed (W m-2) + real(dp) :: latHeatCanEvap_dStateCanLiq ! canopy evaporation after canopy liquid water content is perturbed (W m-2) + + ! (flux derivatives -- canopy air space) + real(dp) :: dTurbFluxCanair_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(dp) :: dTurbFluxCanair_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) + real(dp) :: dTurbFluxCanair_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) + real(dp) :: dTurbFluxCanair_dCanLiq ! derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + + ! (flux derivatives -- vegetation canopy) + real(dp) :: dTurbFluxCanopy_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(dp) :: dTurbFluxCanopy_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + real(dp) :: dTurbFluxCanopy_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + real(dp) :: dTurbFluxCanopy_dCanLiq ! derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + + ! (flux derivatives -- ground surface) + real(dp) :: dTurbFluxGround_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(dp) :: dTurbFluxGround_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + real(dp) :: dTurbFluxGround_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + real(dp) :: dTurbFluxGround_dCanLiq ! derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + + ! (liquid water flux derivatives -- canopy evap) + real(dp) :: dLatHeatCanopyEvap_dCanLiq ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (W kg-1) + real(dp) :: dLatHeatCanopyEvap_dTCanair ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) + real(dp) :: dLatHeatCanopyEvap_dTCanopy ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) + real(dp) :: dLatHeatCanopyEvap_dTGround ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) + + ! (liquid water flux derivatives -- ground evap) + real(dp) :: dLatHeatGroundEvap_dCanLiq ! derivative in latent heat of ground evaporation w.r.t. canopy liquid water content (J kg-1 s-1) + real(dp) :: dLatHeatGroundEvap_dTCanair ! derivative in latent heat of ground evaporation w.r.t. canopy air temperature (W m-2 K-1) + real(dp) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) + real(dp) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) + + ! --------------------------------------------------------------------------------------- + ! point to variables in the data structure + ! --------------------------------------------------------------------------------------- + associate(& + + ! input: model decisions + ix_bcUpprTdyn => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision, & ! intent(in): [i4b] choice of upper boundary condition for thermodynamics + ix_fDerivMeth => model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): [i4b] choice of method to compute derivatives + ix_veg_traits => model_decisions(iLookDECISIONS%veg_traits)%iDecision, & ! intent(in): [i4b] choice of parameterization for vegetation roughness length and displacement height + ix_canopyEmis => model_decisions(iLookDECISIONS%canopyEmis)%iDecision, & ! intent(in): [i4b] choice of parameterization for canopy emissivity + ix_windPrfile => model_decisions(iLookDECISIONS%windPrfile)%iDecision, & ! intent(in): [i4b] choice of canopy wind profile + ix_astability => model_decisions(iLookDECISIONS%astability)%iDecision, & ! intent(in): [i4b] choice of stability function + ix_soilStress => model_decisions(iLookDECISIONS%soilStress)%iDecision, & ! intent(in): [i4b] choice of function for the soil moisture control on stomatal resistance + ix_groundwatr => model_decisions(iLookDECISIONS%groundwatr)%iDecision, & ! intent(in): [i4b] choice of groundwater parameterization + ix_stomResist => model_decisions(iLookDECISIONS%stomResist)%iDecision, & ! intent(in): [i4b] choice of function for stomatal resistance + ix_spatial_gw => model_decisions(iLookDECISIONS%spatial_gw)%iDecision, & ! intent(in): [i4b] choice of groundwater representation (local, basin) + + ! input: layer geometry + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1), & ! intent(in): [i4b] number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): [i4b] total number of layers + + ! input: physical attributes + vegTypeIndex => type_data%var(iLookTYPE%vegTypeIndex), & ! intent(in): [i4b] vegetation type index + soilTypeIndex => type_data%var(iLookTYPE%soilTypeIndex), & ! intent(in): [i4b] soil type index + + ! input: vegetation parameters + heightCanopyTop => mpar_data%var(iLookPARAM%heightCanopyTop)%dat(1), & ! intent(in): [dp] height at the top of the vegetation canopy (m) + heightCanopyBottom => mpar_data%var(iLookPARAM%heightCanopyBottom)%dat(1), & ! intent(in): [dp] height at the bottom of the vegetation canopy (m) + canopyWettingFactor => mpar_data%var(iLookPARAM%canopyWettingFactor)%dat(1), & ! intent(in): [dp] maximum wetted fraction of the canopy (-) + canopyWettingExp => mpar_data%var(iLookPARAM%canopyWettingExp)%dat(1), & ! intent(in): [dp] exponent in canopy wetting function (-) + scalarCanopyIceMax => diag_data%var(iLookDIAG%scalarCanopyIceMax)%dat(1), & ! intent(in): [dp] maximum interception storage capacity for ice (kg m-2) + scalarCanopyLiqMax => diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1), & ! intent(in): [dp] maximum interception storage capacity for liquid water (kg m-2) + + ! input: vegetation phenology + scalarLAI => diag_data%var(iLookDIAG%scalarLAI)%dat(1), & ! intent(in): [dp] one-sided leaf area index (m2 m-2) + scalarSAI => diag_data%var(iLookDIAG%scalarSAI)%dat(1), & ! intent(in): [dp] one-sided stem area index (m2 m-2) + scalarExposedLAI => diag_data%var(iLookDIAG%scalarExposedLAI)%dat(1), & ! intent(in): [dp] exposed leaf area index after burial by snow (m2 m-2) + scalarExposedSAI => diag_data%var(iLookDIAG%scalarExposedSAI)%dat(1), & ! intent(in): [dp] exposed stem area index after burial by snow (m2 m-2) + scalarGrowingSeasonIndex => diag_data%var(iLookDIAG%scalarGrowingSeasonIndex)%dat(1), & ! intent(in): [dp] growing season index (0=off, 1=on) + scalarFoliageNitrogenFactor => diag_data%var(iLookDIAG%scalarFoliageNitrogenFactor)%dat(1), & ! intent(in): [dp] foliage nitrogen concentration (1.0 = saturated) + + ! input: aerodynamic resistance parameters + z0Snow => mpar_data%var(iLookPARAM%z0Snow)%dat(1), & ! intent(in): [dp] roughness length of snow (m) + z0Soil => mpar_data%var(iLookPARAM%z0Soil)%dat(1), & ! intent(in): [dp] roughness length of soil (m) + z0CanopyParam => mpar_data%var(iLookPARAM%z0Canopy)%dat(1), & ! intent(in): [dp] roughness length of the canopy (m) + zpdFraction => mpar_data%var(iLookPARAM%zpdFraction)%dat(1), & ! intent(in): [dp] zero plane displacement / canopy height (-) + critRichNumber => mpar_data%var(iLookPARAM%critRichNumber)%dat(1), & ! intent(in): [dp] critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam => mpar_data%var(iLookPARAM%Louis79_bparam)%dat(1), & ! intent(in): [dp] parameter in Louis (1979) stability function + Louis79_cStar => mpar_data%var(iLookPARAM%Louis79_cStar)%dat(1), & ! intent(in): [dp] parameter in Louis (1979) stability function + Mahrt87_eScale => mpar_data%var(iLookPARAM%Mahrt87_eScale)%dat(1), & ! intent(in): [dp] exponential scaling factor in the Mahrt (1987) stability function + windReductionParam => mpar_data%var(iLookPARAM%windReductionParam)%dat(1), & ! intent(in): [dp] canopy wind reduction parameter (-) + leafExchangeCoeff => mpar_data%var(iLookPARAM%leafExchangeCoeff)%dat(1), & ! intent(in): [dp] turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) + leafDimension => mpar_data%var(iLookPARAM%leafDimension)%dat(1), & ! intent(in): [dp] characteristic leaf dimension (m) + + ! input: soil stress parameters + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(1), & ! intent(in): [dp] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(1), & ! intent(in): [dp] residual volumetric liquid water content (-) + plantWiltPsi => mpar_data%var(iLookPARAM%plantWiltPsi)%dat(1), & ! intent(in): [dp] matric head at wilting point (m) + soilStressParam => mpar_data%var(iLookPARAM%soilStressParam)%dat(1), & ! intent(in): [dp] parameter in the exponential soil stress function (-) + critSoilWilting => mpar_data%var(iLookPARAM%critSoilWilting)%dat(1), & ! intent(in): [dp] critical vol. liq. water content when plants are wilting (-) + critSoilTranspire => mpar_data%var(iLookPARAM%critSoilTranspire)%dat(1), & ! intent(in): [dp] critical vol. liq. water content when transpiration is limited (-) + critAquiferTranspire => mpar_data%var(iLookPARAM%critAquiferTranspire)%dat(1), & ! intent(in): [dp] critical aquifer storage value when transpiration is limited (m) + minStomatalResistance => mpar_data%var(iLookPARAM%minStomatalResistance)%dat(1), & ! intent(in): [dp] mimimum stomatal resistance (s m-1) + + ! input: forcing at the upper boundary + mHeight => diag_data%var(iLookDIAG%scalarAdjMeasHeight)%dat(1), & ! intent(in): [dp] measurement height (m) + airtemp => forc_data%var(iLookFORCE%airtemp), & ! intent(in): [dp] air temperature at some height above the surface (K) + windspd => forc_data%var(iLookFORCE%windspd), & ! intent(in): [dp] wind speed at some height above the surface (m s-1) + airpres => forc_data%var(iLookFORCE%airpres), & ! intent(in): [dp] air pressure at some height above the surface (Pa) + LWRadAtm => forc_data%var(iLookFORCE%LWRadAtm), & ! intent(in): [dp] downwelling longwave radiation at the upper boundary (W m-2) + scalarVPair => diag_data%var(iLookDIAG%scalarVPair)%dat(1), & ! intent(in): [dp] vapor pressure at some height above the surface (Pa) + scalarO2air => diag_data%var(iLookDIAG%scalarO2air)%dat(1), & ! intent(in): [dp] atmospheric o2 concentration (Pa) + scalarCO2air => diag_data%var(iLookDIAG%scalarCO2air)%dat(1), & ! intent(in): [dp] atmospheric co2 concentration (Pa) + scalarTwetbulb => diag_data%var(iLookDIAG%scalarTwetbulb)%dat(1), & ! intent(in): [dp] wetbulb temperature (K) + scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1), & ! intent(in): [dp] computed rainfall rate (kg m-2 s-1) + scalarSnowfall => flux_data%var(iLookFLUX%scalarSnowfall)%dat(1), & ! intent(in): [dp] computed snowfall rate (kg m-2 s-1) + scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1), & ! intent(in): [dp] rainfall through the vegetation canopy (kg m-2 s-1) + scalarThroughfallSnow => flux_data%var(iLookFLUX%scalarThroughfallSnow)%dat(1), & ! intent(in): [dp] snowfall through the vegetation canopy (kg m-2 s-1) + + ! input: water storage + ! NOTE: soil stress only computed at the start of the substep (firstFluxCall=.true.) + scalarSWE => prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(in): [dp] snow water equivalent on the ground (kg m-2) + scalarSnowDepth => prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(in): [dp] snow depth on the ground surface (m) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat, & ! intent(in): [dp(:)] volumetric fraction of liquid water in each layer (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat, & ! intent(in): [dp(:)] matric head in each soil layer (m) + localAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1), & ! intent(in): [dp] aquifer storage for the local column (m) + basinAquiferStorage => bvar_data%var(iLookBVAR%basin__AquiferStorage)%dat(1), & ! intent(in): [dp] aquifer storage for the single basin (m) + + ! input: shortwave radiation fluxes + scalarCanopySunlitLAI => diag_data%var(iLookDIAG%scalarCanopySunlitLAI)%dat(1), & ! intent(in): [dp] sunlit leaf area (-) + scalarCanopyShadedLAI => diag_data%var(iLookDIAG%scalarCanopyShadedLAI)%dat(1), & ! intent(in): [dp] shaded leaf area (-) + scalarCanopySunlitPAR => flux_data%var(iLookFLUX%scalarCanopySunlitPAR)%dat(1), & ! intent(in): [dp] average absorbed par for sunlit leaves (w m-2) + scalarCanopyShadedPAR => flux_data%var(iLookFLUX%scalarCanopyShadedPAR)%dat(1), & ! intent(in): [dp] average absorbed par for shaded leaves (w m-2) + scalarCanopyAbsorbedSolar => flux_data%var(iLookFLUX%scalarCanopyAbsorbedSolar)%dat(1), & ! intent(in): [dp] solar radiation absorbed by canopy (W m-2) + scalarGroundAbsorbedSolar => flux_data%var(iLookFLUX%scalarGroundAbsorbedSolar)%dat(1), & ! intent(in): [dp] solar radiation absorbed by ground (W m-2) + + ! output: fraction of wetted canopy area and fraction of snow on the ground + scalarCanopyWetFraction => diag_data%var(iLookDIAG%scalarCanopyWetFraction)%dat(1), & ! intent(out): [dp] fraction of canopy that is wet + scalarGroundSnowFraction => diag_data%var(iLookDIAG%scalarGroundSnowFraction)%dat(1), & ! intent(out): [dp] fraction of ground covered with snow (-) + + ! output: longwave radiation fluxes + scalarCanopyEmissivity => diag_data%var(iLookDIAG%scalarCanopyEmissivity)%dat(1), & ! intent(out): [dp] effective emissivity of the canopy (-) + scalarLWRadCanopy => flux_data%var(iLookFLUX%scalarLWRadCanopy)%dat(1), & ! intent(out): [dp] longwave radiation emitted from the canopy (W m-2) + scalarLWRadGround => flux_data%var(iLookFLUX%scalarLWRadGround)%dat(1), & ! intent(out): [dp] longwave radiation emitted at the ground surface (W m-2) + scalarLWRadUbound2Canopy => flux_data%var(iLookFLUX%scalarLWRadUbound2Canopy)%dat(1), & ! intent(out): [dp] downward atmospheric longwave radiation absorbed by the canopy (W m-2) + scalarLWRadUbound2Ground => flux_data%var(iLookFLUX%scalarLWRadUbound2Ground)%dat(1), & ! intent(out): [dp] downward atmospheric longwave radiation absorbed by the ground (W m-2) + scalarLWRadUbound2Ubound => flux_data%var(iLookFLUX%scalarLWRadUbound2Ubound)%dat(1), & ! intent(out): [dp] atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) + scalarLWRadCanopy2Ubound => flux_data%var(iLookFLUX%scalarLWRadCanopy2Ubound)%dat(1), & ! intent(out): [dp] longwave radiation emitted from canopy lost thru upper boundary (W m-2) + scalarLWRadCanopy2Ground => flux_data%var(iLookFLUX%scalarLWRadCanopy2Ground)%dat(1), & ! intent(out): [dp] longwave radiation emitted from canopy absorbed by the ground (W m-2) + scalarLWRadCanopy2Canopy => flux_data%var(iLookFLUX%scalarLWRadCanopy2Canopy)%dat(1), & ! intent(out): [dp] canopy longwave reflected from ground and absorbed by the canopy (W m-2) + scalarLWRadGround2Ubound => flux_data%var(iLookFLUX%scalarLWRadGround2Ubound)%dat(1), & ! intent(out): [dp] longwave radiation emitted from ground lost thru upper boundary (W m-2) + scalarLWRadGround2Canopy => flux_data%var(iLookFLUX%scalarLWRadGround2Canopy)%dat(1), & ! intent(out): [dp] longwave radiation emitted from ground and absorbed by the canopy (W m-2) + scalarLWNetCanopy => flux_data%var(iLookFLUX%scalarLWNetCanopy)%dat(1), & ! intent(out): [dp] net longwave radiation at the canopy (W m-2) + scalarLWNetGround => flux_data%var(iLookFLUX%scalarLWNetGround)%dat(1), & ! intent(out): [dp] net longwave radiation at the ground surface (W m-2) + scalarLWNetUbound => flux_data%var(iLookFLUX%scalarLWNetUbound)%dat(1), & ! intent(out): [dp] net longwave radiation at the upper boundary (W m-2) + + ! output: aerodynamic resistance + scalarZ0Canopy => diag_data%var(iLookDIAG%scalarZ0Canopy)%dat(1), & ! intent(out): [dp] roughness length of the canopy (m) + scalarWindReductionFactor => diag_data%var(iLookDIAG%scalarWindReductionFactor)%dat(1), & ! intent(out): [dp] canopy wind reduction factor (-) + scalarZeroPlaneDisplacement => diag_data%var(iLookDIAG%scalarZeroPlaneDisplacement)%dat(1), & ! intent(out): [dp] zero plane displacement (m) + scalarRiBulkCanopy => diag_data%var(iLookDIAG%scalarRiBulkCanopy)%dat(1), & ! intent(out): [dp] bulk Richardson number for the canopy (-) + scalarRiBulkGround => diag_data%var(iLookDIAG%scalarRiBulkGround)%dat(1), & ! intent(out): [dp] bulk Richardson number for the ground surface (-) + scalarEddyDiffusCanopyTop => flux_data%var(iLookFLUX%scalarEddyDiffusCanopyTop)%dat(1), & ! intent(out): [dp] eddy diffusivity for heat at the top of the canopy (m2 s-1) + scalarFrictionVelocity => flux_data%var(iLookFLUX%scalarFrictionVelocity)%dat(1), & ! intent(out): [dp] friction velocity (m s-1) + scalarWindspdCanopyTop => flux_data%var(iLookFLUX%scalarWindspdCanopyTop)%dat(1), & ! intent(out): [dp] windspeed at the top of the canopy (m s-1) + scalarWindspdCanopyBottom => flux_data%var(iLookFLUX%scalarWindspdCanopyBottom)%dat(1), & ! intent(out): [dp] windspeed at the height of the bottom of the canopy (m s-1) + scalarLeafResistance => flux_data%var(iLookFLUX%scalarLeafResistance)%dat(1), & ! intent(out): [dp] mean leaf boundary layer resistance per unit leaf area (s m-1) + scalarGroundResistance => flux_data%var(iLookFLUX%scalarGroundResistance)%dat(1), & ! intent(out): [dp] below canopy aerodynamic resistance (s m-1) + scalarCanopyResistance => flux_data%var(iLookFLUX%scalarCanopyResistance)%dat(1), & ! intent(out): [dp] above canopy aerodynamic resistance (s m-1) + + ! input/output: soil resistance -- intent(in) and intent(inout) because only called at the first flux call + mLayerRootDensity => diag_data%var(iLookDIAG%mLayerRootDensity)%dat, & ! intent(in): [dp] root density in each layer (-) + scalarAquiferRootFrac => diag_data%var(iLookDIAG%scalarAquiferRootFrac)%dat(1), & ! intent(in): [dp] fraction of roots below the lowest soil layer (-) + scalarTranspireLim => diag_data%var(iLookDIAG%scalarTranspireLim)%dat(1), & ! intent(inout): [dp] weighted average of the transpiration limiting factor (-) + mLayerTranspireLim => diag_data%var(iLookDIAG%mLayerTranspireLim)%dat, & ! intent(inout): [dp] transpiration limiting factor in each layer (-) + scalarTranspireLimAqfr => diag_data%var(iLookDIAG%scalarTranspireLimAqfr)%dat(1), & ! intent(inout): [dp] transpiration limiting factor for the aquifer (-) + scalarSoilRelHumidity => diag_data%var(iLookDIAG%scalarSoilRelHumidity)%dat(1), & ! intent(inout): [dp] relative humidity in the soil pores [0-1] + scalarSoilResistance => flux_data%var(iLookFLUX%scalarSoilResistance)%dat(1), & ! intent(inout): [dp] resistance from the soil (s m-1) + + ! input/output: stomatal resistance -- intent(inout) because only called at the first flux call + scalarStomResistSunlit => flux_data%var(iLookFLUX%scalarStomResistSunlit)%dat(1), & ! intent(inout): [dp] stomatal resistance for sunlit leaves (s m-1) + scalarStomResistShaded => flux_data%var(iLookFLUX%scalarStomResistShaded)%dat(1), & ! intent(inout): [dp] stomatal resistance for shaded leaves (s m-1) + scalarPhotosynthesisSunlit => flux_data%var(iLookFLUX%scalarPhotosynthesisSunlit)%dat(1), & ! intent(inout): [dp] sunlit photosynthesis (umolco2 m-2 s-1) + scalarPhotosynthesisShaded => flux_data%var(iLookFLUX%scalarPhotosynthesisShaded)%dat(1), & ! intent(inout): [dp] shaded photosynthesis (umolco2 m-2 s-1) + + ! output: turbulent heat fluxes + scalarLatHeatSubVapCanopy => diag_data%var(iLookDIAG%scalarLatHeatSubVapCanopy)%dat(1), & ! intent(inout): [dp] latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) + scalarLatHeatSubVapGround => diag_data%var(iLookDIAG%scalarLatHeatSubVapGround)%dat(1), & ! intent(inout): [dp] latent heat of sublimation/vaporization for the ground surface (J kg-1) + scalarSatVP_canopyTemp => diag_data%var(iLookDIAG%scalarSatVP_CanopyTemp)%dat(1), & ! intent(out): [dp] saturation vapor pressure at the temperature of the vegetation canopy (Pa) + scalarSatVP_groundTemp => diag_data%var(iLookDIAG%scalarSatVP_GroundTemp)%dat(1), & ! intent(out): [dp] saturation vapor pressure at the temperature of the ground surface (Pa) + scalarSenHeatTotal => flux_data%var(iLookFLUX%scalarSenHeatTotal)%dat(1), & ! intent(out): [dp] sensible heat from the canopy air space to the atmosphere (W m-2) + scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1), & ! intent(out): [dp] sensible heat flux from the canopy to the canopy air space (W m-2) + scalarSenHeatGround => flux_data%var(iLookFLUX%scalarSenHeatGround)%dat(1), & ! intent(out): [dp] sensible heat flux from ground surface below vegetation (W m-2) + scalarLatHeatTotal => flux_data%var(iLookFLUX%scalarLatHeatTotal)%dat(1), & ! intent(out): [dp] latent heat from the canopy air space to the atmosphere (W m-2) + scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1), & ! intent(out): [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + scalarLatHeatCanopyTrans => flux_data%var(iLookFLUX%scalarLatHeatCanopyTrans)%dat(1), & ! intent(out): [dp] latent heat flux for transpiration from the canopy to the canopy air space (W m-2) + scalarLatHeatGround => flux_data%var(iLookFLUX%scalarLatHeatGround)%dat(1), & ! intent(out): [dp] latent heat flux from ground surface below vegetation (W m-2) + + ! output: advective heat fluxes + scalarCanopyAdvectiveHeatFlux => flux_data%var(iLookFLUX%scalarCanopyAdvectiveHeatFlux)%dat(1), & ! intent(out): [dp] heat advected to the canopy surface with rain + snow (W m-2) + scalarGroundAdvectiveHeatFlux => flux_data%var(iLookFLUX%scalarGroundAdvectiveHeatFlux)%dat(1), & ! intent(out): [dp] heat advected to the ground surface with throughfall (W m-2) + + ! output: mass fluxes + scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1), & ! intent(out): [dp] canopy sublimation/frost (kg m-2 s-1) + scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! intent(out): [dp] snow sublimation/frost -- below canopy or non-vegetated (kg m-2 s-1) + + ! input/output: canopy air space variables + scalarVP_CanopyAir => diag_data%var(iLookDIAG%scalarVP_CanopyAir)%dat(1), & ! intent(inout): [dp] vapor pressure of the canopy air space (Pa) + scalarCanopyStabilityCorrection => diag_data%var(iLookDIAG%scalarCanopyStabilityCorrection)%dat(1),& ! intent(inout): [dp] stability correction for the canopy (-) + scalarGroundStabilityCorrection => diag_data%var(iLookDIAG%scalarGroundStabilityCorrection)%dat(1),& ! intent(inout): [dp] stability correction for the ground surface (-) + + ! output: liquid water fluxes + scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1), & ! intent(out): [dp] canopy transpiration (kg m-2 s-1) + scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1), & ! intent(out): [dp] canopy evaporation/condensation (kg m-2 s-1) + scalarGroundEvaporation => flux_data%var(iLookFLUX%scalarGroundEvaporation)%dat(1), & ! intent(out): [dp] ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + + ! output: derived fluxes + scalarTotalET => flux_data%var(iLookFLUX%scalarTotalET)%dat(1), & ! intent(out): [dp] total ET (kg m-2 s-1) + scalarNetRadiation => flux_data%var(iLookFLUX%scalarNetRadiation)%dat(1) & ! intent(out): [dp] net radiation (W m-2) + ) + ! --------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="vegNrgFluxFida/" + + ! initialize printflag + printflag = .false. + + ! identify the type of boundary condition for thermodynamics + select case(ix_bcUpprTdyn) + + ! ***** + ! (1) DIRICHLET OR ZERO FLUX BOUNDARY CONDITION... + ! ************************************************ + + ! NOTE: Vegetation fluxes are not computed in this case + + ! ** prescribed temperature or zero flux at the upper boundary of the snow-soil system + case(prescribedTemp,zeroFlux) + + ! derived fluxes + scalarTotalET = 0._dp ! total ET (kg m-2 s-1) + scalarNetRadiation = 0._dp ! net radiation (W m-2) + ! liquid water fluxes associated with evaporation/transpiration + scalarCanopyTranspiration = 0._dp ! canopy transpiration (kg m-2 s-1) + scalarCanopyEvaporation = 0._dp ! canopy evaporation/condensation (kg m-2 s-1) + scalarGroundEvaporation = 0._dp ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + ! solid water fluxes associated with sublimation/frost + scalarCanopySublimation = 0._dp ! sublimation from the vegetation canopy ((kg m-2 s-1) + scalarSnowSublimation = 0._dp ! sublimation from the snow surface ((kg m-2 s-1) + ! set canopy fluxes to zero (no canopy) + canairNetFlux = 0._dp ! net energy flux for the canopy air space (W m-2) + canopyNetFlux = 0._dp ! net energy flux for the vegetation canopy (W m-2) + ! set canopy derivatives to zero + dCanairNetFlux_dCanairTemp = 0._dp ! derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) + dCanairNetFlux_dCanopyTemp = 0._dp ! derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) + dCanairNetFlux_dGroundTemp = 0._dp ! derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) + dCanopyNetFlux_dCanairTemp = 0._dp ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) + dCanopyNetFlux_dCanopyTemp = 0._dp ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) + dCanopyNetFlux_dGroundTemp = 0._dp ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) + dGroundNetFlux_dCanairTemp = 0._dp ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) + dGroundNetFlux_dCanopyTemp = 0._dp ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) + ! set liquid flux derivatives to zero (canopy evap) + dCanopyEvaporation_dCanLiq = 0._dp ! derivative in canopy evaporation w.r.t. canopy liquid water content (s-1) + dCanopyEvaporation_dTCanair= 0._dp ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTCanopy= 0._dp ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTGround= 0._dp ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + ! set liquid flux derivatives to zero (ground evap) + dGroundEvaporation_dCanLiq = 0._dp ! derivative in ground evaporation w.r.t. canopy liquid water content (s-1) + dGroundEvaporation_dTCanair= 0._dp ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTCanopy= 0._dp ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTGround= 0._dp ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + + ! compute fluxes and derivatives -- separate approach for prescribed temperature and zero flux + if(ix_bcUpprTdyn == prescribedTemp)then + ! compute ground net flux (W m-2) + groundNetFlux = -diag_data%var(iLookDIAG%iLayerThermalC)%dat(0)*(groundTempTrial - upperBoundTemp)/(prog_data%var(iLookPROG%mLayerDepth)%dat(1)*0.5_dp) + ! compute derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + dGroundNetFlux_dGroundTemp = -diag_data%var(iLookDIAG%iLayerThermalC)%dat(0)/(prog_data%var(iLookPROG%mLayerDepth)%dat(1)*0.5_dp) + elseif(model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision == zeroFlux)then + groundNetFlux = 0._dp + dGroundNetFlux_dGroundTemp = 0._dp + else + err=20; message=trim(message)//'unable to identify upper boundary condition for thermodynamics: expect the case to be prescribedTemp or zeroFlux'; return + end if + + ! ***** + ! (2) NEUMANN BOUNDARY CONDITION... + ! ********************************* + + ! NOTE 1: This is the main routine for calculating vegetation fluxes + ! NOTE 2: This routine also calculates surface fluxes for the case where vegetation is buried with snow (or bare soil) + + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + ! ***** PRELIMINARIES ********************************************************************************************************************************************** + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + + ! * flux boundary condition + case(energyFlux) + + ! identify the appropriate groundwater variable + select case(ix_spatial_gw) + case(singleBasin); scalarAquiferStorage = basinAquiferStorage + case(localColumn); scalarAquiferStorage = localAquiferStorage + case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return + end select ! (modify the groundwater representation for this single-column implementation) + + ! set canopy stability corrections to the previous values + scalarCanopyStabilityCorrection_old = scalarCanopyStabilityCorrection ! stability correction for the canopy (-) + scalarGroundStabilityCorrection_old = scalarGroundStabilityCorrection ! stability correction for the ground surface (-) + + ! initialize variables to compute stomatal resistance + if(firstFluxCall .and. firstSubStep)then + ! vapor pressure in the canopy air space initialized as vapor pressure of air above the vegetation canopy + ! NOTE: this is needed for the stomatal resistance calculations + if(scalarVP_CanopyAir < 0._dp)then + scalarVP_CanopyAir = scalarVPair - 1._dp ! "small" offset used to assist in checking initial derivative calculations + end if + end if + + ! set latent heat of sublimation/vaporization for canopy and ground surface (Pa/K) + ! NOTE: variables are constant over the substep, to simplify relating energy and mass fluxes + if(firstFluxCall)then + scalarLatHeatSubVapCanopy = getLatentHeatValue(canopyTempTrial) + ! case when there is snow on the ground (EXCLUDE "snow without a layer" -- in this case, evaporate from the soil) + if(nSnow > 0)then + if(groundTempTrial > Tfreeze)then; err=20; message=trim(message)//'do not expect ground temperature > 0 when snow is on the ground'; return; end if + scalarLatHeatSubVapGround = LH_sub ! sublimation from snow + scalarGroundSnowFraction = 1._dp + ! case when the ground is snow-free + else + scalarLatHeatSubVapGround = LH_vap ! evaporation of water in the soil pores: this occurs even if frozen because of super-cooled water + scalarGroundSnowFraction = 0._dp + end if ! (if there is snow on the ground) + end if ! (if the first flux call) + !write(*,'(a,1x,10(f30.10,1x))') 'groundTempTrial, scalarLatHeatSubVapGround = ', groundTempTrial, scalarLatHeatSubVapGround + + ! compute the roughness length of the ground (ground below the canopy or non-vegetated surface) + z0Ground = z0soil*(1._dp - scalarGroundSnowFraction) + z0Snow*scalarGroundSnowFraction ! roughness length (m) + + ! compute the total vegetation area index (leaf plus stem) + VAI = scalarLAI + scalarSAI ! vegetation area index + exposedVAI = scalarExposedLAI + scalarExposedSAI ! exposed vegetation area index + + ! compute emissivity of the canopy (-) + if(computeVegFlux)then + select case(ix_canopyEmis) + ! *** simple exponential function + case(simplExp) + scalarCanopyEmissivity = 1._dp - exp(-exposedVAI) ! effective emissivity of the canopy (-) + ! *** canopy emissivity parameterized as a function of diffuse transmissivity + case(difTrans) + ! compute the exponential integral + scaleLAI = 0.5_dp*exposedVAI + expi = expInt(scaleLAI) + ! compute diffuse transmissivity (-) + diffuseTrans = (1._dp - scaleLAI)*exp(-scaleLAI) + (scaleLAI**2._dp)*expi + ! compute the canopy emissivity + scalarCanopyEmissivity = (1._dp - diffuseTrans)*vegEmissivity + ! *** check we found the correct option + case default + err=20; message=trim(message)//'unable to identify option for canopy emissivity'; return + end select + end if + + ! ensure canopy longwave fluxes are zero when not computing canopy fluxes + if(.not.computeVegFlux) scalarCanopyEmissivity=0._dp + + ! compute emissivity of the ground surface (-) + groundEmissivity = scalarGroundSnowFraction*snowEmissivity + (1._dp - scalarGroundSnowFraction)*soilEmissivity ! emissivity of the ground surface (-) + + ! compute the fraction of canopy that is wet + ! NOTE: we either sublimate or evaporate over the entire substep + if(computeVegFlux)then + + ! compute the fraction of liquid water in the canopy (-) + totalCanopyWater = canopyLiqTrial + canopyIceTrial + if(totalCanopyWater > tiny(1.0_dp))then + fracLiquidCanopy = canopyLiqTrial / (canopyLiqTrial + canopyIceTrial) + else + fracLiquidCanopy = 0._dp + end if + + ! get wetted fraction and derivatives + call wettedFrac(& + ! input + .true., & ! flag to denote if derivative is desired + (ix_fDerivMeth == numerical), & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) + (scalarLatHeatSubVapCanopy > LH_vap+verySmall), & ! flag to denote if the canopy is frozen + dCanLiq_dTcanopy, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) + fracLiquidCanopy, & ! fraction of liquid water on the canopy (-) + canopyLiqTrial, & ! canopy liquid water (kg m-2) + canopyIceTrial, & ! canopy ice (kg m-2) + scalarCanopyLiqMax, & ! maximum canopy liquid water (kg m-2) + scalarCanopyIceMax, & ! maximum canopy ice content (kg m-2) + canopyWettingFactor, & ! maximum wetted fraction of the canopy (-) + canopyWettingExp, & ! exponent in canopy wetting function (-) + ! output + scalarCanopyWetFraction, & ! canopy wetted fraction (-) + dCanopyWetFraction_dWat, & ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) + dCanopyWetFraction_dT, & ! derivative in wetted fraction w.r.t. canopy temperature (K-1) + err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + else + scalarCanopyWetFraction = 0._dp ! canopy wetted fraction (-) + dCanopyWetFraction_dWat = 0._dp ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) + dCanopyWetFraction_dT = 0._dp ! derivative in wetted fraction w.r.t. canopy temperature (K-1) + end if + !write(*,'(a,1x,L1,1x,f25.15,1x))') 'computeVegFlux, scalarCanopyWetFraction = ', computeVegFlux, scalarCanopyWetFraction + !print*, 'dCanopyWetFraction_dWat = ', dCanopyWetFraction_dWat + !print*, 'dCanopyWetFraction_dT = ', dCanopyWetFraction_dT + !print*, 'canopyLiqTrial = ', canopyLiqTrial + !print*, 'canopyIceTrial = ', canopyIceTrial + !print*, 'scalarCanopyLiqMax = ', scalarCanopyLiqMax + !print*, 'scalarCanopyIceMax = ', scalarCanopyIceMax + + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + ! ***** AERODYNAMIC RESISTANCE ***************************************************************************************************************************************** + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + + ! NOTE: compute for all iterations + + ! compute aerodynamic resistances + ! Refs: Choudhury and Monteith (4-layer model for heat budget of homogenous surfaces; QJRMS, 1988) + ! Niu and Yang (Canopy effects on snow processes; JGR, 2004) + ! Mahat et al. (Below-canopy turbulence in a snowmelt model, WRR, 2012) + call aeroResist(& + ! input: model control + computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) + (ix_fDerivMeth == analytical), & ! intent(in): logical flag if would like to compute analytical derivaties + ix_veg_traits, & ! intent(in): choice of parameterization for vegetation roughness length and displacement height + ix_windPrfile, & ! intent(in): choice of canopy wind profile + ix_astability, & ! intent(in): choice of stability function + ! input: above-canopy forcing data + mHeight, & ! intent(in): measurement height (m) + airtemp, & ! intent(in): air temperature at some height above the surface (K) + windspd, & ! intent(in): wind speed at some height above the surface (m s-1) + ! input: canopy and ground temperature + canairTempTrial, & ! intent(in): temperature of the canopy air space (K) + groundTempTrial, & ! intent(in): temperature of the ground surface (K) + ! input: diagnostic variables + exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) + scalarSnowDepth, & ! intent(in): snow depth (m) + ! input: parameters + z0Ground, & ! intent(in): roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) + z0CanopyParam, & ! intent(in): roughness length of the canopy (m) + zpdFraction, & ! intent(in): zero plane displacement / canopy height (-) + critRichNumber, & ! intent(in): critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam, & ! intent(in): parameter in Louis (1979) stability function + Mahrt87_eScale, & ! intent(in): exponential scaling factor in the Mahrt (1987) stability function + windReductionParam, & ! intent(in): canopy wind reduction parameter (-) + leafExchangeCoeff, & ! intent(in): turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) + leafDimension, & ! intent(in): characteristic leaf dimension (m) + heightCanopyTop, & ! intent(in): height at the top of the vegetation canopy (m) + heightCanopyBottom, & ! intent(in): height at the bottom of the vegetation canopy (m) + ! output: stability corrections + scalarRiBulkCanopy, & ! intent(out): bulk Richardson number for the canopy (-) + scalarRiBulkGround, & ! intent(out): bulk Richardson number for the ground surface (-) + scalarCanopyStabilityCorrection, & ! intent(out): stability correction for the canopy (-) + scalarGroundStabilityCorrection, & ! intent(out): stability correction for the ground surface (-) + ! output: scalar resistances + scalarZ0Canopy, & ! intent(out): roughness length of the canopy (m) + scalarWindReductionFactor, & ! intent(out): canopy wind reduction factor (-) + scalarZeroPlaneDisplacement, & ! intent(out): zero plane displacement (m) + scalarEddyDiffusCanopyTop, & ! intent(out): eddy diffusivity for heat at the top of the canopy (m2 s-1) + scalarFrictionVelocity, & ! intent(out): friction velocity (m s-1) + scalarWindspdCanopyTop, & ! intent(out): windspeed at the top of the canopy (m s-1) + scalarWindspdCanopyBottom, & ! intent(out): windspeed at the height of the bottom of the canopy (m s-1) + scalarLeafResistance, & ! intent(out): mean leaf boundary layer resistance per unit leaf area (s m-1) + scalarGroundResistance, & ! intent(out): below canopy aerodynamic resistance (s m-1) + scalarCanopyResistance, & ! intent(out): above canopy aerodynamic resistance (s m-1) + ! output: derivatives in scalar resistances + dGroundResistance_dTGround, & ! intent(out): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + dGroundResistance_dTCanopy, & ! intent(out): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + dGroundResistance_dTCanair, & ! intent(out): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + dCanopyResistance_dTCanopy, & ! intent(out): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + dCanopyResistance_dTCanair, & ! intent(out): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + ! output: error control + err,cmessage ) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + !print*, scalarLeafResistance, & ! mean leaf boundary layer resistance per unit leaf area (s m-1) + ! scalarGroundResistance, & ! below canopy aerodynamic resistance (s m-1) + ! scalarCanopyResistance, & ! above canopy aerodynamic resistance (s m-1) + ! '(leaf, ground, canopy)' + + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + ! ***** STOMATAL RESISTANCE ***************************************************************************************************************************************** + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + + ! stomatal resistance is constant over the SUBSTEP + ! NOTE: This is a simplification, as stomatal resistance does depend on canopy temperature + ! This "short-cut" made because: + ! (1) computations are expensive; + ! (2) derivative calculations are rather complex (iterations within the Ball-Berry routine); and + ! (3) stomatal resistance does not change rapidly + if(firstFluxCall)then + + ! compute the saturation vapor pressure for vegetation temperature + TV_celcius = canopyTempTrial - Tfreeze + call satVapPress(TV_celcius, scalarSatVP_CanopyTemp, dSVPCanopy_dCanopyTemp) + + ! compute soil moisture factor controlling stomatal resistance + call soilResist(& + ! input (model decisions) + ix_soilStress, & ! intent(in): choice of function for the soil moisture control on stomatal resistance + ix_groundwatr, & ! intent(in): groundwater parameterization + ! input (state variables) + mLayerMatricHead(1:nSoil), & ! intent(in): matric head in each soil layer (m) + mLayerVolFracLiq(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water in each soil layer (-) + scalarAquiferStorage, & ! intent(in): aquifer storage (m) + ! input (diagnostic variables) + mLayerRootDensity(1:nSoil), & ! intent(in): root density in each layer (-) + scalarAquiferRootFrac, & ! intent(in): fraction of roots below the lowest soil layer (-) + ! input (parameters) + plantWiltPsi, & ! intent(in): matric head at wilting point (m) + soilStressParam, & ! intent(in): parameter in the exponential soil stress function (-) + critSoilWilting, & ! intent(in): critical vol. liq. water content when plants are wilting (-) + critSoilTranspire, & ! intent(in): critical vol. liq. water content when transpiration is limited (-) + critAquiferTranspire, & ! intent(in): critical aquifer storage value when transpiration is limited (m) + ! output + scalarTranspireLim, & ! intent(out): weighted average of the transpiration limiting factor (-) + mLayerTranspireLim(1:nSoil), & ! intent(out): transpiration limiting factor in each layer (-) + scalarTranspireLimAqfr, & ! intent(out): transpiration limiting factor for the aquifer (-) + err,cmessage ) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + !print*, 'weighted average of the soil moiture factor controlling stomatal resistance (-) = ', scalarTranspireLim + + !write(*,'(a,1x,10(f20.10,1x))') 'canopyTempTrial, scalarSatVP_CanopyTemp, scalarVP_CanopyAir = ', & + ! canopyTempTrial, scalarSatVP_CanopyTemp, scalarVP_CanopyAir + + ! compute stomatal resistance + call stomResist(& + ! input (state and diagnostic variables) + canopyTempTrial, & ! intent(in): temperature of the vegetation canopy (K) + scalarSatVP_CanopyTemp, & ! intent(in): saturation vapor pressure at the temperature of the veg canopy (Pa) + scalarVP_CanopyAir, & ! intent(in): canopy air vapor pressure (Pa) + ! input: data structures + type_data, & ! intent(in): type of vegetation and soil + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + model_decisions, & ! intent(in): model decisions + ! input-output: data structures + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output: error control + err,cmessage ) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + end if ! (if the first flux call in a given sub-step) + + + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + ! ***** LONGWAVE RADIATION ***************************************************************************************************************************************** + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + + ! compute canopy longwave radiation balance + call longwaveBal(& + ! input: model control + ix_fDerivMeth, & ! intent(in): method used to calculate flux derivatives + computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation + ! input: canopy and ground temperature + canopyTempTrial, & ! intent(in): temperature of the vegetation canopy (K) + groundTempTrial, & ! intent(in): temperature of the ground surface (K) + ! input: canopy and ground emissivity + scalarCanopyEmissivity, & ! intent(in): canopy emissivity (-) + groundEmissivity, & ! intent(in): ground emissivity (-) + ! input: forcing + LWRadAtm, & ! intent(in): downwelling longwave radiation at the upper boundary (W m-2) + ! output: emitted radiation from the canopy and ground + scalarLWRadCanopy, & ! intent(out): longwave radiation emitted from the canopy (W m-2) + scalarLWRadGround, & ! intent(out): longwave radiation emitted at the ground surface (W m-2) + ! output: individual fluxes + scalarLWRadUbound2Canopy, & ! intent(out): downward atmospheric longwave radiation absorbed by the canopy (W m-2) + scalarLWRadUbound2Ground, & ! intent(out): downward atmospheric longwave radiation absorbed by the ground (W m-2) + scalarLWRadUbound2Ubound, & ! intent(out): atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) + scalarLWRadCanopy2Ubound, & ! intent(out): longwave radiation emitted from canopy lost thru upper boundary (W m-2) + scalarLWRadCanopy2Ground, & ! intent(out): longwave radiation emitted from canopy absorbed by the ground (W m-2) + scalarLWRadCanopy2Canopy, & ! intent(out): canopy longwave reflected from ground and absorbed by the canopy (W m-2) + scalarLWRadGround2Ubound, & ! intent(out): longwave radiation emitted from ground lost thru upper boundary (W m-2) + scalarLWRadGround2Canopy, & ! intent(out): longwave radiation emitted from ground and absorbed by the canopy (W m-2) + ! output: net fluxes + scalarLWNetCanopy, & ! intent(out): net longwave radiation at the canopy (W m-2) + scalarLWNetGround, & ! intent(out): net longwave radiation at the ground surface (W m-2) + scalarLWNetUbound, & ! intent(out): net longwave radiation at the upper boundary (W m-2) + ! output: flux derivatives + dLWNetCanopy_dTCanopy, & ! intent(out): derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) + dLWNetGround_dTGround, & ! intent(out): derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) + dLWNetCanopy_dTGround, & ! intent(out): derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) + dLWNetGround_dTCanopy, & ! intent(out): derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) + ! output: error control + err,cmessage ) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + !print*, 'dLWNetCanopy_dTGround = ', dLWNetCanopy_dTGround + + + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + ! ***** TURBULENT HEAT FLUXES ************************************************************************************************************************************** + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + + ! check the need to compute numerical derivatives + if(ix_fDerivMeth == numerical)then + nFlux=5 ! compute the derivatives using one-sided finite differences + else + nFlux=1 ! compute analytical derivatives + end if + + ! either one or multiple flux calls, depending on if using analytical or numerical derivatives + do itry=nFlux,1,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) + + ! ------------------------------------------------------------------------------------- + ! state perturbations for numerical deriavtives with one-sided finite differences + ! note: no perturbations performed using analytical derivatives (nFlux=1) + ! ------------------------------------------------------------------------------------- + + ! identify the type of perturbation + select case(itry) + + ! un-perturbed case + case(unperturbed) + groundTemp = groundTempTrial + canopyTemp = canopyTempTrial + canairTemp = canairTempTrial + canopyWetFraction = scalarCanopyWetFraction + + ! perturb ground temperature + case(perturbStateGround) + groundTemp = groundTempTrial + dx + canopyTemp = canopyTempTrial + canairTemp = canairTempTrial + canopyWetFraction = scalarCanopyWetFraction + + ! perturb canopy temperature + case(perturbStateCanopy) + groundTemp = groundTempTrial + canopyTemp = canopyTempTrial + dx + canairTemp = canairTempTrial + canopyWetFraction = scalarCanopyWetFraction + + ! perturb canopy air temperature + case(perturbStateCanair) + groundTemp = groundTempTrial + canopyTemp = canopyTempTrial + canairTemp = canairTempTrial + dx + canopyWetFraction = scalarCanopyWetFraction + + ! perturb canopy liquid water content + case(perturbStateCanLiq) + groundTemp = groundTempTrial + canopyTemp = canopyTempTrial + canairTemp = canairTempTrial + + ! perturbations in canopy liquid water content affect canopy wetted fraction + if(computeVegFlux)then + call wettedFrac(& + ! input + .false., & ! flag to denote if derivative is desired + (ix_fDerivMeth == numerical), & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) + (scalarLatHeatSubVapCanopy > LH_vap+verySmall), & ! flag to denote if the canopy is frozen + dCanLiq_dTcanopy, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) + fracLiquidCanopy, & ! fraction of liquid water on the canopy (-) + canopyLiqTrial+dx, & ! canopy liquid water (kg m-2) + canopyIceTrial, & ! canopy ice (kg m-2) + scalarCanopyLiqMax, & ! maximum canopy liquid water (kg m-2) + scalarCanopyIceMax, & ! maximum canopy ice content (kg m-2) + canopyWettingFactor, & ! maximum wetted fraction of the canopy (-) + canopyWettingExp, & ! exponent in canopy wetting function (-) + ! output + canopyWetFraction, & ! canopy wetted fraction (-) + dCanopyWetFraction_dWat, & ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) + dCanopyWetFraction_dT, & ! derivative in wetted fraction w.r.t. canopy temperature (K-1) + err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + else + canopyWetFraction = 0._dp + end if + !print*, 'wetted fraction derivative = ', (canopyWetFraction - scalarCanopyWetFraction)/dx + !pause + + ! check for an unknown perturbation + case default; err=10; message=trim(message)//"unknown perturbation"; return + + end select ! (type of perturbation) + + ! compute the saturation vapor pressure for vegetation temperature + ! NOTE: saturated vapor pressure derivatives don't seem that accurate.... + TV_celcius = canopyTemp - Tfreeze + call satVapPress(TV_celcius, scalarSatVP_CanopyTemp, dSVPCanopy_dCanopyTemp) + + ! compute the saturation vapor pressure for ground temperature + ! NOTE: saturated vapor pressure derivatives don't seem that accurate.... + TG_celcius = groundTemp - Tfreeze + call satVapPress(TG_celcius, scalarSatVP_GroundTemp, dSVPGround_dGroundTemp) + + ! ------------------------------------------------------------------------------------- + ! calculation block (unperturbed fluxes returned [computed last]) + ! ------------------------------------------------------------------------------------- + + ! re-compute aerodynamic resistances for perturbed cases + ! NOTE: unperturbed fluxes computed earlier, and not over-written + if(itry /= unperturbed)then + call aeroResist(& + ! input: model control + computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) + .false., & ! intent(in): logical flag if would like to compute analytical derivaties + ix_veg_traits, & ! intent(in): choice of parameterization for vegetation roughness length and displacement height + ix_windPrfile, & ! intent(in): choice of canopy wind profile + ix_astability, & ! intent(in): choice of stability function + ! input: above-canopy forcing data + mHeight, & ! intent(in): measurement height (m) + airtemp, & ! intent(in): air temperature at some height above the surface (K) + windspd, & ! intent(in): wind speed at some height above the surface (m s-1) + ! input: temperature (canopy, ground, canopy air space) + canairTemp, & ! intent(in): temperature of the canopy air space (K) + groundTemp, & ! intent(in): ground temperature (K) + ! input: diagnostic variables + exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) + scalarSnowDepth, & ! intent(in): snow depth (m) + ! input: parameters + z0Ground, & ! intent(in): roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) + z0CanopyParam, & ! intent(in): roughness length of the canopy (m) + zpdFraction, & ! intent(in): zero plane displacement / canopy height (-) + critRichNumber, & ! intent(in): critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam, & ! intent(in): parameter in Louis (1979) stability function + Mahrt87_eScale, & ! intent(in): exponential scaling factor in the Mahrt (1987) stability function + windReductionParam, & ! intent(in): canopy wind reduction parameter (-) + leafExchangeCoeff, & ! intent(in): turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) + leafDimension, & ! intent(in): characteristic leaf dimension (m) + heightCanopyTop, & ! intent(in): height at the top of the vegetation canopy (m) + heightCanopyBottom, & ! intent(in): height at the bottom of the vegetation canopy (m) + ! output: stability corrections + notUsed_RiBulkCanopy, & ! intent(out): bulk Richardson number for the canopy (-) + notUsed_RiBulkGround, & ! intent(out): bulk Richardson number for the ground surface (-) + notUsed_scalarCanopyStabilityCorrection, & ! intent(out): stability correction for the canopy (-) + notUsed_scalarGroundStabilityCorrection, & ! intent(out): stability correction for the ground surface (-) + ! output: scalar resistances + notUsed_z0Canopy, & ! intent(out): roughness length of the canopy (m) + notUsed_WindReductionFactor, & ! intent(out): canopy wind reduction factor (-) + notUsed_ZeroPlaneDisplacement, & ! intent(out): zero plane displacement (m) + notUsed_EddyDiffusCanopyTop, & ! intent(out): eddy diffusivity for heat at the top of the canopy (m2 s-1) + notUsed_FrictionVelocity, & ! intent(out): friction velocity (m s-1) + notUsed_WindspdCanopyTop, & ! intent(out): windspeed at the top of the canopy (m s-1) + notUsed_WindspdCanopyBottom, & ! intent(out): windspeed at the height of the bottom of the canopy (m s-1) + trialLeafResistance, & ! intent(out): mean leaf boundary layer resistance per unit leaf area (s m-1) + trialGroundResistance, & ! intent(out): below canopy aerodynamic resistance (s m-1) + trialCanopyResistance, & ! intent(out): above canopy aerodynamic resistance (s m-1) + ! output: derivatives in scalar resistances + notUsed_dGroundResistance_dTGround, & ! intent(out): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + notUsed_dGroundResistance_dTCanopy, & ! intent(out): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + notUsed_dGroundResistance_dTCanair, & ! intent(out): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + notUsed_dCanopyResistance_dTCanopy, & ! intent(out): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + notUsed_dCanopyResistance_dTCanair, & ! intent(out): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + ! output: error control + err,cmessage ) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + + ! assign scalar resistances for un-perturbed cases + else + trialLeafResistance = scalarLeafResistance + trialGroundResistance = scalarGroundResistance + trialCanopyResistance = scalarCanopyResistance + + end if ! (re-computing resistances for perturbed cases) + !print*, 'trialLeafResistance = ', trialLeafResistance + !print*, 'trialGroundResistance = ', trialGroundResistance + !print*, 'trialCanopyResistance = ', trialCanopyResistance + + ! compute the relative humidity in the top soil layer and the resistance at the ground surface + ! NOTE: computations are based on start-of-step values, so only compute for the first flux call + if(firstFluxCall)then + ! (soil water evaporation factor [0-1]) + soilEvapFactor = mLayerVolFracLiq(nSnow+1)/(theta_sat - theta_res) + ! (resistance from the soil [s m-1]) + scalarSoilResistance = scalarGroundSnowFraction*1._dp + (1._dp - scalarGroundSnowFraction)*EXP(8.25_dp - 4.225_dp*soilEvapFactor) ! Sellers (1992) + !scalarSoilResistance = scalarGroundSnowFraction*0._dp + (1._dp - scalarGroundSnowFraction)*exp(8.25_dp - 6.0_dp*soilEvapFactor) ! Niu adjustment to decrease resitance for wet soil + ! (relative humidity in the soil pores [0-1]) + if(mLayerMatricHead(1) > -1.e+6_dp)then ! avoid problems with numerical precision when soil is very dry + soilRelHumidity_noSnow = exp( (mLayerMatricHead(1)*gravity) / (groundTemp*R_wv) ) + else + soilRelHumidity_noSnow = 0._dp + end if ! (if matric head is very low) + scalarSoilRelHumidity = scalarGroundSnowFraction*1._dp + (1._dp - scalarGroundSnowFraction)*soilRelHumidity_noSnow + !print*, 'mLayerMatricHead(1), scalarSoilRelHumidity = ', mLayerMatricHead(1), scalarSoilRelHumidity + end if ! (if the first flux call) + + ! compute turbulent heat fluxes + call turbFluxes(& + ! input: model control + computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) + ix_fDerivMeth, & ! intent(in): method used to calculate flux derivatives + ! input: above-canopy forcing data + airtemp, & ! intent(in): air temperature at some height above the surface (K) + airpres, & ! intent(in): air pressure of the air above the vegetation canopy (Pa) + scalarVPair, & ! intent(in): vapor pressure of the air above the vegetation canopy (Pa) + ! input: latent heat of sublimation/vaporization + scalarLatHeatSubVapCanopy, & ! intent(in): latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) + scalarLatHeatSubVapGround, & ! intent(in): latent heat of sublimation/vaporization for the ground surface (J kg-1) + ! input: canopy/ground temperature and saturated vapor pressure + canairTemp, & ! intent(in): temperature of the canopy air space (K) + canopyTemp, & ! intent(in): canopy temperature (K) + groundTemp, & ! intent(in): ground temperature (K) + scalarSatVP_CanopyTemp, & ! intent(in): saturation vapor pressure at the temperature of the veg canopy (Pa) + scalarSatVP_GroundTemp, & ! intent(in): saturation vapor pressure at the temperature of the ground (Pa) + dSVPCanopy_dCanopyTemp, & ! intent(in): derivative in canopy saturation vapor pressure w.r.t. canopy temperature (Pa K-1) + dSVPGround_dGroundTemp, & ! intent(in): derivative in ground saturation vapor pressure w.r.t. ground temperature (Pa K-1) + ! input: diagnostic variables + exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) + canopyWetFraction, & ! intent(in): trial value for the fraction of canopy that is wet [0-1] + dCanopyWetFraction_dWat, & ! intent(in): derivative in the canopy wetted fraction w.r.t. total water content (kg-1 m-2) + dCanopyWetFraction_dT, & ! intent(in): derivative in wetted fraction w.r.t. canopy temperature (K-1) + scalarCanopySunlitLAI, & ! intent(in): sunlit leaf area (-) + scalarCanopyShadedLAI, & ! intent(in): shaded leaf area (-) + scalarSoilRelHumidity, & ! intent(in): relative humidity in the soil pores [0-1] + scalarSoilResistance, & ! intent(in): resistance from the soil (s m-1) + trialLeafResistance, & ! intent(in): mean leaf boundary layer resistance per unit leaf area (s m-1) + trialGroundResistance, & ! intent(in): below canopy aerodynamic resistance (s m-1) + trialCanopyResistance, & ! intent(in): above canopy aerodynamic resistance (s m-1) + scalarStomResistSunlit, & ! intent(in): stomatal resistance for sunlit leaves (s m-1) + scalarStomResistShaded, & ! intent(in): stomatal resistance for shaded leaves (s m-1) + ! input: derivatives in scalar resistances + dGroundResistance_dTGround, & ! intent(in): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + dGroundResistance_dTCanopy, & ! intent(in): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + dGroundResistance_dTCanair, & ! intent(in): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + dCanopyResistance_dTCanopy, & ! intent(in): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + dCanopyResistance_dTCanair, & ! intent(in): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + ! output: conductances (used to check derivative calculations) + scalarLeafConductance, & ! intent(out): leaf conductance (m s-1) + scalarCanopyConductance, & ! intent(out): canopy conductance (m s-1) + scalarGroundConductanceSH, & ! intent(out): ground conductance for sensible heat (m s-1) + scalarGroundConductanceLH, & ! intent(out): ground conductance for latent heat -- includes soil resistance (m s-1) + scalarEvapConductance, & ! intent(out): conductance for evaporation (m s-1) + scalarTransConductance, & ! intent(out): conductance for transpiration (m s-1) + scalarTotalConductanceSH, & ! intent(out): total conductance for sensible heat (m s-1) + scalarTotalConductanceLH, & ! intent(out): total conductance for latent heat (m s-1) + ! output: canopy air space variables + scalarVP_CanopyAir, & ! intent(out): vapor pressure of the canopy air space (Pa) + ! output: fluxes from the vegetation canopy + scalarSenHeatCanopy, & ! intent(out): sensible heat flux from the canopy to the canopy air space (W m-2) + scalarLatHeatCanopyEvap, & ! intent(out): latent heat flux associated with evaporation from the canopy to the canopy air space (W m-2) + scalarLatHeatCanopyTrans, & ! intent(out): latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) + ! output: fluxes from non-vegetated surfaces (ground surface below vegetation, bare ground, or snow covered vegetation) + scalarSenHeatGround, & ! intent(out): sensible heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) + scalarLatHeatGround, & ! intent(out): latent heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) + ! output: total heat fluxes to the atmosphere + scalarSenHeatTotal, & ! intent(out): total sensible heat flux to the atmosphere (W m-2) + scalarLatHeatTotal, & ! intent(out): total latent heat flux to the atmosphere (W m-2) + ! output: net fluxes + turbFluxCanair, & ! intent(out): net turbulent heat fluxes at the canopy air space (W m-2) + turbFluxCanopy, & ! intent(out): net turbulent heat fluxes at the canopy (W m-2) + turbFluxGround, & ! intent(out): net turbulent heat fluxes at the ground surface (W m-2) + ! output: energy flux derivatives + dTurbFluxCanair_dTCanair, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxCanair_dTCanopy, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxCanair_dTGround, & ! intent(out): derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanair, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanopy, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxCanopy_dTGround, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxGround_dTCanair, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxGround_dTCanopy, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxGround_dTGround, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + ! output: liquid flux derivatives (canopy evap) + dLatHeatCanopyEvap_dCanLiq, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (W kg-1) + dLatHeatCanopyEvap_dTCanair, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) + dLatHeatCanopyEvap_dTCanopy, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) + dLatHeatCanopyEvap_dTGround, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) + ! output: liquid flux derivatives (ground evap) + dLatHeatGroundEvap_dCanLiq, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy liquid water content (J kg-1 s-1) + dLatHeatGroundEvap_dTCanair, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy air temperature + dLatHeatGroundEvap_dTCanopy, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy temperature + dLatHeatGroundEvap_dTGround, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. ground temperature + ! output: cross derivatives + dTurbFluxCanair_dCanLiq, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dTurbFluxCanopy_dCanLiq, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dTurbFluxGround_dCanLiq, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + ! output: error control + err,cmessage ) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + !write(*,'(a,f25.15)') 'scalarSenHeatTotal = ', scalarSenHeatTotal + !write(*,'(a,f25.15)') 'scalarSenHeatCanopy = ', scalarSenHeatCanopy + !write(*,'(a,f25.15)') 'scalarLatHeatCanopyEvap = ', scalarLatHeatCanopyEvap + !write(*,'(a,f25.15)') 'scalarLatHeatCanopyTrans = ', scalarLatHeatCanopyTrans + + !print*, 'scalarSenHeatGround = ', scalarSenHeatGround + !print*, 'scalarLatHeatGround = ', scalarLatHeatGround + + !notUsed_scalarCanopyStabilityCorrection ! stability correction for the canopy (-) + !notUsed_scalarGroundStabilityCorrection ! stability correction for the ground surface (-) + !notUsed_EddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) + !notUsed_FrictionVelocity ! friction velocity (m s-1) + !notUsed_WindspdCanopyTop ! windspeed at the top of the canopy (m s-1) + !notUsed_WindspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) + !trialLeafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) + !trialGroundResistance ! below canopy aerodynamic resistance (s m-1) + !trialCanopyResistance ! above canopy aerodynamic resistance (s m-1) + + ! save perturbed fluxes + if(ix_fDerivMeth == numerical)then + select case(itry) ! (select type of perturbation) + case(unperturbed) + try0 = turbFluxGround + exit + case(perturbStateCanair) + turbFluxCanair_dStateCanair = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) + turbFluxCanopy_dStateCanair = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) + turbFluxGround_dStateCanair = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) + latHeatCanEvap_dStateCanair = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) + case(perturbStateCanopy) + turbFluxCanair_dStateCanopy = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) + turbFluxCanopy_dStateCanopy = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) + turbFluxGround_dStateCanopy = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) + latHeatCanEvap_dStateCanopy = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) + case(perturbStateGround) + try1 = turbFluxGround + turbFluxCanair_dStateGround = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) + turbFluxCanopy_dStateGround = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) + turbFluxGround_dStateGround = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) + latHeatCanEvap_dStateGround = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) + case(perturbStateCanLiq) + turbFluxCanair_dStateCanliq = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) + turbFluxCanopy_dStateCanLiq = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) + turbFluxGround_dStateCanLiq = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) + latHeatCanEvap_dStateCanliq = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) + case default; err=10; message=trim(message)//"unknown perturbation"; return + end select ! (type of perturbation) + end if ! (if numerical) + + end do ! (looping through different flux perturbations) + + ! test derivative + !if(ix_fDerivMeth == numerical) print*, 'try0, try1 = ', try0, try1 + !if(ix_fDerivMeth == numerical) print*, 'derivative = ', (ix_fDerivMeth == numerical), (try1 - try0)/dx + !if(ix_fDerivMeth == analytical) print*, 'derivative = ', (ix_fDerivMeth == numerical), dTurbFluxGround_dTGround + !pause + + ! compute numerical derivatives + if(ix_fDerivMeth == numerical)then + ! derivatives w.r.t. canopy air temperature + dTurbFluxCanair_dTCanair = (turbFluxCanair_dStateCanair - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanair = (turbFluxCanopy_dStateCanair - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxGround_dTCanair = (turbFluxGround_dStateCanair - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dLatHeatCanopyEvap_dTCanair = (latHeatCanEvap_dStateCanair - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) + ! derivatives w.r.t. canopy temperature + dTurbFluxCanair_dTCanopy = (turbFluxCanair_dStateCanopy - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanopy = (turbFluxCanopy_dStateCanopy - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxGround_dTCanopy = (turbFluxGround_dStateCanopy - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dLatHeatCanopyEvap_dTCanopy = (latHeatCanEvap_dStateCanopy - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) + ! derivatives w.r.t. ground temperature + dTurbFluxCanair_dTGround = (turbFluxCanair_dStateGround - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxCanopy_dTGround = (turbFluxCanopy_dStateGround - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxGround_dTGround = (turbFluxGround_dStateGround - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + dLatHeatCanopyEvap_dTGround = (latHeatCanEvap_dStateGround - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) + ! derivatives w.r.t. canopy liquid water content + dTurbFluxCanair_dCanLiq = (turbFluxCanair_dStateCanliq - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dTurbFluxCanopy_dCanLiq = (turbFluxCanopy_dStateCanLiq - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dTurbFluxGround_dCanLiq = (turbFluxGround_dStateCanLiq - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dLatHeatCanopyEvap_dCanLiq = (latHeatCanEvap_dStateCanliq - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (J kg-1 s-1) + end if + !if(heightCanopyBottom < scalarSnowDepth+z0Ground) pause 'bottom of the canopy is covered' + + ! test + !print*, (ix_fDerivMeth == numerical) + !print*, 'dTurbFluxCanair_dTCanair = ', dTurbFluxCanair_dTCanair + !print*, 'dTurbFluxCanair_dTCanopy = ', dTurbFluxCanair_dTCanopy + !print*, 'dTurbFluxCanair_dTGround = ', dTurbFluxCanair_dTGround + !print*, 'dTurbFluxCanopy_dTCanair = ', dTurbFluxCanopy_dTCanair + !print*, 'dTurbFluxCanopy_dTCanopy = ', dTurbFluxCanopy_dTCanopy + !print*, 'dTurbFluxCanopy_dTGround = ', dTurbFluxCanopy_dTGround + !print*, 'dTurbFluxGround_dTCanair = ', dTurbFluxGround_dTCanair + !print*, 'dTurbFluxGround_dTCanopy = ', dTurbFluxGround_dTCanopy + !print*, 'dTurbFluxGround_dTGround = ', dTurbFluxGround_dTGround + !print*, 'dLatHeatCanopyEvap_dCanLiq = ', dLatHeatCanopyEvap_dCanLiq + !print*, 'dLatHeatCanopyEvap_dTCanair = ', dLatHeatCanopyEvap_dTCanair + !print*, 'dLatHeatCanopyEvap_dTCanopy = ', dLatHeatCanopyEvap_dTCanopy + !print*, 'dLatHeatCanopyEvap_dTGround = ', dLatHeatCanopyEvap_dTGround + !print*, 'dTurbFluxCanair_dCanLiq = ', dTurbFluxCanair_dCanLiq + !print*, 'dTurbFluxCanopy_dCanLiq = ', dTurbFluxCanopy_dCanLiq + !print*, 'dTurbFluxGround_dCanLiq = ', dTurbFluxGround_dCanLiq + !print*, '*****' + !pause + + !print*, 'scalarRainfall, scalarThroughfallRain, scalarSnowfall, scalarThroughfallSnow, canopyTempTrial, scalarTwetbulb = ', & + ! scalarRainfall, scalarThroughfallRain, scalarSnowfall, scalarThroughfallSnow, canopyTempTrial, scalarTwetbulb + + ! compute the heat advected with precipitation (W m-2) + ! NOTE: fluxes are in kg m-2 s-1, so no need to use density of water/ice here + scalarCanopyAdvectiveHeatFlux = -Cp_water*(scalarRainfall - scalarThroughfallRain)*(canopyTempTrial - scalarTwetbulb) + & + (-Cp_ice)*(scalarSnowfall - scalarThroughfallSnow)*(canopyTempTrial - scalarTwetbulb) + scalarGroundAdvectiveHeatFlux = -Cp_water*scalarThroughfallRain*(groundTempTrial - scalarTwetbulb) + & + (-Cp_ice)*scalarThroughfallSnow*(groundTempTrial - scalarTwetbulb) !+ & + ! -Cp_water*scalarCanopyLiqDrainage *(groundTempTrial - canopyTempTrial) + & + ! -Cp_ice *scalarCanopySnowUnloading*(groundTempTrial - canopyTempTrial) + !print*, 'scalarRainfall, scalarThroughfallRain, scalarSnowfall, scalarThroughfallSnow = ', scalarRainfall, scalarThroughfallRain, scalarSnowfall, scalarThroughfallSnow + !print*, 'scalarCanopyAdvectiveHeatFlux, scalarGroundAdvectiveHeatFlux = ', scalarCanopyAdvectiveHeatFlux, scalarGroundAdvectiveHeatFlux + + ! compute the mass flux associated with transpiration and evaporation/sublimation (J m-2 s-1 --> kg m-2 s-1) + ! NOTE: remove water from the snow on the ground in preference to removing water from the water in soil pores + !print*, 'scalarLatHeatCanopyTrans = ', scalarLatHeatCanopyTrans + !print*, 'scalarLatHeatGround = ', scalarLatHeatGround + ! (canopy transpiration/sublimation) + if(scalarLatHeatSubVapCanopy > LH_vap+verySmall)then ! sublimation + scalarCanopyEvaporation = 0._dp + scalarCanopySublimation = scalarLatHeatCanopyEvap/LH_sub + if(scalarLatHeatCanopyTrans > 0._dp)then ! flux directed towards the veg + scalarCanopySublimation = scalarCanopySublimation + scalarLatHeatCanopyTrans/LH_sub ! frost + scalarCanopyTranspiration = 0._dp + else + scalarCanopyTranspiration = scalarLatHeatCanopyTrans/LH_vap ! transpiration is always vapor + end if + ! (canopy transpiration/evaporation) + else ! evaporation + scalarCanopyEvaporation = scalarLatHeatCanopyEvap/LH_vap + scalarCanopySublimation = 0._dp + if(scalarLatHeatCanopyTrans > 0._dp)then ! flux directed towards the veg + scalarCanopyEvaporation = scalarCanopyEvaporation + scalarLatHeatCanopyTrans/LH_vap + scalarCanopyTranspiration = 0._dp + else + scalarCanopyTranspiration = scalarLatHeatCanopyTrans/LH_vap + end if + end if + ! (ground evaporation/sublimation) + if(scalarLatHeatSubVapGround > LH_vap+verySmall)then ! sublimation + ! NOTE: this should only occur when we have formed snow layers, so check + if(nSnow == 0)then; err=20; message=trim(message)//'only expect snow sublimation when we have formed some snow layers'; return; end if + scalarGroundEvaporation = 0._dp ! ground evaporation is zero once the snowpack has formed + scalarSnowSublimation = scalarLatHeatGround/LH_sub + else + ! NOTE: this should only occur when we have no snow layers, so check + if(nSnow > 0)then; err=20; message=trim(message)//'only expect ground evaporation when there are no snow layers'; return; end if + scalarGroundEvaporation = scalarLatHeatGround/LH_vap + scalarSnowSublimation = 0._dp ! no sublimation from snow if no snow layers have formed + end if + !print*, 'scalarSnowSublimation, scalarLatHeatGround = ', scalarSnowSublimation, scalarLatHeatGround + + !print*, 'canopyWetFraction, scalarCanopyEvaporation = ', canopyWetFraction, scalarCanopyEvaporation + + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + ! ***** AND STITCH EVERYTHING TOGETHER ***************************************************************************************************************************** + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + + ! compute derived fluxes + scalarTotalET = scalarGroundEvaporation + scalarCanopyEvaporation + scalarCanopyTranspiration + scalarNetRadiation = scalarCanopyAbsorbedSolar + scalarLWNetCanopy + scalarGroundAbsorbedSolar + scalarLWNetGround + + ! compute net fluxes at the canopy and ground surface + canairNetFlux = turbFluxCanair + canopyNetFlux = scalarCanopyAbsorbedSolar + scalarLWNetCanopy + turbFluxCanopy + scalarCanopyAdvectiveHeatFlux + groundNetFlux = scalarGroundAbsorbedSolar + scalarLWNetGround + turbFluxGround + scalarGroundAdvectiveHeatFlux + !write(*,'(a,1x,10(e17.10,1x))') 'canopyNetFlux, groundNetFlux, scalarLWNetCanopy, turbFluxCanopy, turbFluxGround, scalarLWNetGround, scalarCanopyAdvectiveHeatFlux = ', & + ! canopyNetFlux, groundNetFlux, scalarLWNetCanopy, turbFluxCanopy, turbFluxGround, scalarLWNetGround, scalarCanopyAdvectiveHeatFlux + !write(*,'(a,1x,10(e20.14,1x))') 'groundNetFlux, scalarGroundAbsorbedSolar, scalarLWNetGround, turbFluxGround, scalarGroundAdvectiveHeatFlux = ', & + ! groundNetFlux, scalarGroundAbsorbedSolar, scalarLWNetGround, turbFluxGround, scalarGroundAdvectiveHeatFlux + + ! compute the energy derivatives + dCanairNetFlux_dCanairTemp = dTurbFluxCanair_dTCanair + dCanairNetFlux_dCanopyTemp = dTurbFluxCanair_dTCanopy + dCanairNetFlux_dGroundTemp = dTurbFluxCanair_dTGround + dCanopyNetFlux_dCanairTemp = dTurbFluxCanopy_dTCanair + dCanopyNetFlux_dCanopyTemp = dLWNetCanopy_dTCanopy + dTurbFluxCanopy_dTCanopy - Cp_water*(scalarRainfall - scalarThroughfallRain) - Cp_ice*(scalarSnowfall - scalarThroughfallSnow) + dCanopyNetFlux_dGroundTemp = dLWNetCanopy_dTGround + dTurbFluxCanopy_dTGround + dGroundNetFlux_dCanairTemp = dTurbFluxGround_dTCanair + dGroundNetFlux_dCanopyTemp = dLWNetGround_dTCanopy + dTurbFluxGround_dTCanopy + dGroundNetFlux_dGroundTemp = dLWNetGround_dTGround + dTurbFluxGround_dTGround - Cp_water*scalarThroughfallRain - Cp_ice*scalarThroughfallSnow + + ! check if evaporation or sublimation + if(scalarLatHeatSubVapCanopy < LH_vap+verySmall)then ! evaporation + + ! compute the liquid water derivarives + dCanopyEvaporation_dCanLiq = dLatHeatCanopyEvap_dCanLiq/LH_vap ! (s-1) + dCanopyEvaporation_dTCanair = dLatHeatCanopyEvap_dTCanair/LH_vap ! (kg m-2 s-1 K-1) + dCanopyEvaporation_dTCanopy = dLatHeatCanopyEvap_dTCanopy/LH_vap ! (kg m-2 s-1 K-1) + dCanopyEvaporation_dTGround = dLatHeatCanopyEvap_dTGround/LH_vap ! (kg m-2 s-1 K-1) + + ! sublimation + else + dCanopyEvaporation_dCanLiq = 0._dp ! (s-1) + dCanopyEvaporation_dTCanair = 0._dp ! (kg m-2 s-1 K-1) + dCanopyEvaporation_dTCanopy = 0._dp ! (kg m-2 s-1 K-1) + dCanopyEvaporation_dTGround = 0._dp ! (kg m-2 s-1 K-1) + end if + + ! compute the liquid water derivarives (ground evap) + dGroundEvaporation_dCanLiq = dLatHeatGroundEvap_dCanLiq/LH_vap ! (s-1) + dGroundEvaporation_dTCanair = dLatHeatGroundEvap_dTCanair/LH_vap ! (kg m-2 s-1 K-1) + dGroundEvaporation_dTCanopy = dLatHeatGroundEvap_dTCanopy/LH_vap ! (kg m-2 s-1 K-1) + dGroundEvaporation_dTGround = dLatHeatGroundEvap_dTGround/LH_vap ! (kg m-2 s-1 K-1) + + ! compute the cross derivative terms (only related to turbulent fluxes; specifically canopy evaporation and transpiration) + dCanopyNetFlux_dCanLiq = dTurbFluxCanopy_dCanLiq ! derivative in net canopy fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dGroundNetFlux_dCanLiq = dTurbFluxGround_dCanLiq ! derivative in net ground fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + + !print*, (ix_fDerivMeth == numerical) + !print*, 'dGroundNetFlux_dCanairTemp = ', dGroundNetFlux_dCanairTemp + !print*, 'dCanopyNetFlux_dCanopyTemp = ', dCanopyNetFlux_dCanopyTemp + !print*, 'dGroundNetFlux_dCanopyTemp = ', dGroundNetFlux_dCanopyTemp + !print*, 'dCanopyNetFlux_dGroundTemp = ', dCanopyNetFlux_dGroundTemp + !print*, 'dGroundNetFlux_dGroundTemp = ', dGroundNetFlux_dGroundTemp + !print*, 'dLWNetCanopy_dTGround = ', dLWNetCanopy_dTGround + !print*, 'dTurbFluxCanopy_dTGround = ', dTurbFluxCanopy_dTGround + !pause + + ! * check + case default; err=10; message=trim(message)//'unable to identify upper boundary condition for thermodynamics'; return + + ! end case statement + end select ! upper boundary condition for thermodynamics + + ! return liquid fluxes (needed for coupling) + returnCanopyTranspiration = scalarCanopyTranspiration ! canopy transpiration (kg m-2 s-1) + returnCanopyEvaporation = scalarCanopyEvaporation ! canopy evaporation/condensation (kg m-2 s-1) + returnGroundEvaporation = scalarGroundEvaporation ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + + ! end associations + end associate + + + end subroutine vegNrgFluxFida + + ! ******************************************************************************************************* + ! private subroutine wetFraction: compute fraction of canopy covered with liquid water + ! ******************************************************************************************************* + subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFactor,canopyWettingExp,canopyWetFraction,canopyWetFractionDeriv) + implicit none + ! dummy variables + logical(lgt),intent(in) :: derDesire ! flag to denote if analytical derivatives are desired + logical(lgt),intent(in) :: smoothing ! flag to denote if smoothing is required + real(dp),intent(in) :: canopyLiq ! liquid water content (kg m-2) + real(dp),intent(in) :: canopyMax ! liquid water content (kg m-2) + real(dp),intent(in) :: canopyWettingFactor ! maximum wetted fraction of the canopy (-) + real(dp),intent(in) :: canopyWettingExp ! exponent in canopy wetting function (-) + + real(dp),intent(out) :: canopyWetFraction ! canopy wetted fraction (-) + real(dp),intent(out) :: canopyWetFractionDeriv ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) + ! local variables + real(dp) :: relativeCanopyWater ! water stored on vegetation canopy, expressed as a fraction of maximum storage (-) + real(dp) :: rawCanopyWetFraction ! initial value of the canopy wet fraction (before smoothing) + real(dp) :: rawWetFractionDeriv ! derivative in canopy wet fraction w.r.t. storage (kg-1 m2) + real(dp) :: smoothFunc ! smoothing function used to improve numerical stability at times with limited water storage (-) + real(dp) :: smoothFuncDeriv ! derivative in the smoothing function w.r.t.canopy storage (kg-1 m2) + real(dp) :: verySmall=epsilon(1._dp) ! a very small number + ! -------------------------------------------------------------------------------------------------------------- + + ! compute relative canopy water + relativeCanopyWater = canopyLiq/canopyMax + !write(*,'(a,1x,e20.10,1x,2(f20.10,1x))') 'relativeCanopyWater, canopyLiq, canopyMax = ', relativeCanopyWater, canopyLiq, canopyMax + + ! compute an initial value of the canopy wet fraction + ! - canopy below value where canopy is 100% wet + if(relativeCanopyWater < 1._dp)then + rawCanopyWetFraction = canopyWettingFactor*(relativeCanopyWater**canopyWettingExp) + if(derDesire .and. relativeCanopyWater>verySmall)then + rawWetFractionDeriv = (canopyWettingFactor*canopyWettingExp/canopyMax)*relativeCanopyWater**(canopyWettingExp - 1._dp) + else + rawWetFractionDeriv = 0._dp + end if + + ! - canopy is at capacity (canopyWettingFactor) + else + rawCanopyWetFraction = canopyWettingFactor + rawWetFractionDeriv = 0._dp + end if + + ! smooth canopy wetted fraction + if(smoothing)then + call logisticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) + canopyWetFraction = rawCanopyWetFraction*smoothFunc ! logistic smoother + else + canopyWetFraction = rawCanopyWetFraction + canopyWetFractionDeriv = rawWetFractionDeriv + end if + + ! compute derivative (product rule) + if(derDesire .and. smoothing)then ! NOTE: raw derivative is used if not smoothing + canopyWetFractionDeriv = rawWetFractionDeriv*smoothFunc + rawCanopyWetFraction*smoothFuncDeriv + else + canopyWetFractionDeriv = 0._dp + end if + + end subroutine wetFraction + + + ! ******************************************************************************************************* + ! private subroutine logisticSmoother: compute the smoothing function + ! ******************************************************************************************************* + subroutine logisticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) + implicit none + ! dummy variables + logical(lgt),intent(in) :: derDesire ! flag to denote if analytical derivatives are desired + real(dp),intent(in) :: canopyLiq ! liquid water content (kg m-2) + real(dp),intent(out) :: smoothFunc ! smoothing function (-) + real(dp),intent(out) :: smoothFuncDeriv ! derivative in smoothing function (kg-1 m-2) + ! local variables + real(dp) :: xArg ! argument used in the smoothing function (-) + real(dp) :: expX ! exp(-xArg) -- used multiple times + real(dp),parameter :: smoothThresh=0.01_dp ! mid-point of the smoothing function (kg m-2) + real(dp),parameter :: smoothScale=0.001_dp ! scaling factor for the smoothing function (kg m-2) + real(dp),parameter :: xLimit=50._dp ! don't compute exponents for > xLimit + ! -------------------------------------------------------------------------------------------------------------- + ! compute argument in the smoothing function + xArg = (canopyLiq - smoothThresh)/smoothScale + + ! only compute smoothing function for small exponents + if(xArg > -xLimit .and. xArg < xLimit)then ! avoid huge exponents + expX = exp(-xarg) ! (also used in the derivative) + smoothFunc = 1._dp / (1._dp + expX) ! (logistic smoother) + if(derDesire)then + smoothFuncDeriv = expX / (smoothScale * (1._dp + expX)**2._dp) ! (derivative in the smoothing function) + else + smoothFuncDeriv = 0._dp + end if + + ! outside limits: special case of smooth exponents + else + if(xArg < 0._dp)then; smoothFunc = 0._dp ! xArg < -xLimit + else; smoothFunc = 1._dp ! xArg > xLimit + end if + smoothFuncDeriv = 0._dp + end if ! check for huge exponents + + end subroutine logisticSmoother + ! -------------------------------------------------------------------------------------------------------------- + + + ! ******************************************************************************************************* + ! private subroutine longwaveBal: compute longwave radiation balance at the canopy and ground surface + ! ******************************************************************************************************* + subroutine longwaveBal(& + ! input: model control + ixDerivMethod, & ! intent(in): choice of method used to compute derivative (analytical or numerical) + computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation + ! input: canopy and ground temperature + canopyTemp, & ! intent(in): canopy temperature (K) + groundTemp, & ! intent(in): ground temperature (K) + ! input: canopy and ground emissivity + emc, & ! intent(in): canopy emissivity (-) + emg, & ! intent(in): ground emissivity (-) + ! input: forcing + LWRadUbound, & ! intent(in): downwelling longwave radiation at the upper boundary (W m-2) + ! output: sources + LWRadCanopy, & ! intent(out): longwave radiation emitted from the canopy (W m-2) + LWRadGround, & ! intent(out): longwave radiation emitted at the ground surface (W m-2) + ! output: individual fluxes + LWRadUbound2Canopy, & ! intent(out): downward atmospheric longwave radiation absorbed by the canopy (W m-2) + LWRadUbound2Ground, & ! intent(out): downward atmospheric longwave radiation absorbed by the ground (W m-2) + LWRadUbound2Ubound, & ! intent(out): atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) + LWRadCanopy2Ubound, & ! intent(out): longwave radiation emitted from canopy lost thru upper boundary (W m-2) + LWRadCanopy2Ground, & ! intent(out): longwave radiation emitted from canopy absorbed by the ground (W m-2) + LWRadCanopy2Canopy, & ! intent(out): canopy longwave reflected from ground and absorbed by the canopy (W m-2) + LWRadGround2Ubound, & ! intent(out): longwave radiation emitted from ground lost thru upper boundary (W m-2) + LWRadGround2Canopy, & ! intent(out): longwave radiation emitted from ground and absorbed by the canopy (W m-2) + ! output: net fluxes + LWNetCanopy, & ! intent(out): net longwave radiation at the canopy (W m-2) + LWNetGround, & ! intent(out): net longwave radiation at the ground surface (W m-2) + LWNetUbound, & ! intent(out): net longwave radiation at the upper boundary (W m-2) + ! output: flux derivatives + dLWNetCanopy_dTCanopy, & ! intent(out): derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) + dLWNetGround_dTGround, & ! intent(out): derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) + dLWNetCanopy_dTGround, & ! intent(out): derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) + dLWNetGround_dTCanopy, & ! intent(out): derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) + ! output: error control + err,message ) ! intent(out): error control + ! ----------------------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input: model control + integer(i4b),intent(in) :: ixDerivMethod ! choice of method used to compute derivative (analytical or numerical) + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + ! input: canopy and ground temperature + real(dp),intent(in) :: canopyTemp ! canopy temperature (K) + real(dp),intent(in) :: groundTemp ! ground temperature (K) + ! input: canopy and ground emissivity + real(dp),intent(in) :: emc ! canopy emissivity (-) + real(dp),intent(in) :: emg ! ground emissivity (-) + ! input: forcing + real(dp),intent(in) :: LWRadUbound ! downwelling longwave radiation at the upper boundary (W m-2) + ! output: sources + real(dp),intent(out) :: LWRadCanopy ! longwave radiation emitted from the canopy (W m-2) + real(dp),intent(out) :: LWRadGround ! longwave radiation emitted at the ground surface (W m-2) + ! output: individual fluxes + real(dp),intent(out) :: LWRadUbound2Canopy ! downward atmospheric longwave radiation absorbed by the canopy (W m-2) + real(dp),intent(out) :: LWRadUbound2Ground ! downward atmospheric longwave radiation absorbed by the ground (W m-2) + real(dp),intent(out) :: LWRadUbound2Ubound ! atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) + real(dp),intent(out) :: LWRadCanopy2Ubound ! longwave radiation emitted from canopy lost thru upper boundary (W m-2) + real(dp),intent(out) :: LWRadCanopy2Ground ! longwave radiation emitted from canopy absorbed by the ground (W m-2) + real(dp),intent(out) :: LWRadCanopy2Canopy ! canopy longwave reflected from ground and absorbed by the canopy (W m-2) + real(dp),intent(out) :: LWRadGround2Ubound ! longwave radiation emitted from ground lost thru upper boundary (W m-2) + real(dp),intent(out) :: LWRadGround2Canopy ! longwave radiation emitted from ground and absorbed by the canopy (W m-2) + ! output: net fluxes + real(dp),intent(out) :: LWNetCanopy ! net longwave radiation at the canopy (W m-2) + real(dp),intent(out) :: LWNetGround ! net longwave radiation at the ground surface (W m-2) + real(dp),intent(out) :: LWNetUbound ! net longwave radiation at the upper boundary (W m-2) + ! output: flux derivatives + real(dp),intent(out) :: dLWNetCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) + real(dp),intent(out) :: dLWNetGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) + real(dp),intent(out) :: dLWNetCanopy_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) + real(dp),intent(out) :: dLWNetGround_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ----------------------------------------------------------------------------------------------------------------------------------------------- + ! local variables + integer(i4b),parameter :: unperturbed=1 ! named variable to identify the case of unperturbed state variables + integer(i4b),parameter :: perturbStateCanopy=2 ! named variable to identify the case where we perturb the canopy temperature + integer(i4b),parameter :: perturbStateGround=3 ! named variable to identify the case where we perturb the ground temperature + integer(i4b) :: itry ! index of flux evaluation + integer(i4b) :: nFlux ! number of flux evaluations + real(dp) :: TCan ! value of canopy temperature used in flux calculations (may be perturbed) + real(dp) :: TGnd ! value of ground temperature used in flux calculations (may be perturbed) + real(dp) :: fluxBalance ! check energy closure (W m-2) + real(dp),parameter :: fluxTolerance=1.e-10_dp ! tolerance for energy closure (W m-2) + real(dp) :: dLWRadCanopy_dTCanopy ! derivative in emitted radiation at the canopy w.r.t. canopy temperature + real(dp) :: dLWRadGround_dTGround ! derivative in emitted radiation at the ground w.r.t. ground temperature + real(dp) :: LWNetCanopy_dStateCanopy ! net lw canopy flux after perturbation in canopy temperature + real(dp) :: LWNetGround_dStateCanopy ! net lw ground flux after perturbation in canopy temperature + real(dp) :: LWNetCanopy_dStateGround ! net lw canopy flux after perturbation in ground temperature + real(dp) :: LWNetGround_dStateGround ! net lw ground flux after perturbation in ground temperature + ! ----------------------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='longwaveBal/' + + ! check the need to compute numerical derivatives + if(ixDerivMethod==numerical)then + nFlux=3 ! compute the derivatives using one-sided finite differences + else + nFlux=1 ! compute analytical derivatives + end if + + ! either one or multiple flux calls, depending on if using analytical or numerical derivatives + do itry=nFlux,1,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) + + !print*, 'perturbation: ', (itry==unperturbed), (itry==perturbStateCanopy), (itry==perturbStateGround) + + ! ------------------------------------------------------------------------------------- + ! state perturbations for numerical deriavtives with one-sided finite differences + ! note: no perturbations performed using analytical derivatives (nFlux=1) + ! ------------------------------------------------------------------------------------- + + ! identify the type of perturbation + select case(itry) + + ! un-perturbed case + case(unperturbed) + TCan = canopyTemp + TGnd = groundTemp + + ! perturb canopy temperature + case(perturbStateCanopy) + TCan = canopyTemp + dx + TGnd = groundTemp + + ! perturb ground temperature + case(perturbStateGround) + TCan = canopyTemp + TGnd = groundTemp + dx + + ! check for an unknown perturbation + case default; err=10; message=trim(message)//"unknown perturbation"; return + + end select ! (type of perturbation) + + ! ------------------------------------------------------------------------------------- + ! calculation block (unperturbed fluxes returned [computed last]) + ! ------------------------------------------------------------------------------------- + ! NOTE: emc should be set to zero when not computing canopy fluxes + + ! compute longwave fluxes from canopy and the ground + if(computeVegFlux)then + LWRadCanopy = emc*sb*TCan**4._dp ! longwave radiation emitted from the canopy (W m-2) + else + LWRadCanopy = 0._dp + end if + LWRadGround = emg*sb*TGnd**4._dp ! longwave radiation emitted at the ground surface (W m-2) + + ! compute fluxes originating from the atmosphere + LWRadUbound2Canopy = (emc + (1._dp - emc)*(1._dp - emg)*emc)*LWRadUbound ! downward atmospheric longwave radiation absorbed by the canopy (W m-2) + LWRadUbound2Ground = (1._dp - emc)*emg*LWRadUbound ! downward atmospheric longwave radiation absorbed by the ground (W m-2) + LWRadUbound2Ubound = (1._dp - emc)*(1._dp - emg)*(1._dp - emc)*LWRadUbound ! atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) + + ! compute fluxes originating from the canopy + LWRadCanopy2Ubound = (1._dp + (1._dp - emc)*(1._dp - emg))*LWRadCanopy ! longwave radiation emitted from canopy lost thru upper boundary (W m-2) + LWRadCanopy2Ground = emg*LWRadCanopy ! longwave radiation emitted from canopy absorbed by the ground (W m-2) + LWRadCanopy2Canopy = emc*(1._dp - emg)*LWRadCanopy ! canopy longwave reflected from ground and absorbed by the canopy (W m-2) + + ! compute fluxes originating from the ground surface + LWRadGround2Ubound = (1._dp - emc)*LWRadGround ! longwave radiation emitted from ground lost thru upper boundary (W m-2) + LWRadGround2Canopy = emc*LWRadGround ! longwave radiation emitted from ground and absorbed by the canopy (W m-2) + + ! compute net longwave radiation (W m-2) + LWNetCanopy = LWRadUbound2Canopy + LWRadGround2Canopy + LWRadCanopy2Canopy - 2._dp*LWRadCanopy ! canopy + LWNetGround = LWRadUbound2Ground + LWRadCanopy2Ground - LWRadGround ! ground surface + LWNetUbound = LWRadUbound - LWRadUbound2Ubound - LWRadCanopy2Ubound - LWRadGround2Ubound ! upper boundary + + !print*, 'LWRadCanopy = ', LWRadCanopy + !print*, 'LWRadGround = ', LWRadGround + + !print*, 'LWNetCanopy = ', LWNetCanopy + !print*, 'LWNetGround = ', LWNetGround + !print*, 'LWNetUbound = ', LWNetUbound + + ! check the flux balance + fluxBalance = LWNetUbound - (LWNetCanopy + LWNetGround) + if(abs(fluxBalance) > fluxTolerance)then + print*, 'fluxBalance = ', fluxBalance + print*, 'emg, emc = ', emg, emc + print*, 'TCan, TGnd = ', TCan, TGnd + print*, 'LWRadUbound = ', LWRadUbound + print*, 'LWRadCanopy = ', LWRadCanopy + print*, 'LWRadGround = ', LWRadGround + print*, 'LWRadUbound2Canopy = ', LWRadUbound2Canopy + print*, 'LWRadUbound2Ground = ', LWRadUbound2Ground + print*, 'LWRadUbound2Ubound = ', LWRadUbound2Ubound + print*, 'LWRadCanopy2Ubound = ', LWRadCanopy2Ubound + print*, 'LWRadCanopy2Ground = ', LWRadCanopy2Ground + print*, 'LWRadCanopy2Canopy = ', LWRadCanopy2Canopy + print*, 'LWRadGround2Ubound = ', LWRadGround2Ubound + print*, 'LWRadGround2Canopy = ', LWRadGround2Canopy + print*, 'LWNetCanopy = ', LWNetCanopy + print*, 'LWNetGround = ', LWNetGround + print*, 'LWNetUbound = ', LWNetUbound + message=trim(message)//'flux imbalance' + err=20; return + end if + + ! -------------------------------------------------------------------------------------- + ! save perturbed fluxes to calculate numerical derivatives (one-sided finite difference) + ! -------------------------------------------------------------------------------------- + if(ixDerivMethod==numerical)then + select case(itry) ! (select type of perturbation) + case(unperturbed); exit + case(perturbStateCanopy) + LWNetCanopy_dStateCanopy = LWNetCanopy + LWNetGround_dStateCanopy = LWNetGround + case(perturbStateGround) + LWNetCanopy_dStateGround = LWNetCanopy + LWNetGround_dStateGround = LWNetGround + case default; err=10; message=trim(message)//"unknown perturbation"; return + end select ! (type of perturbation) + end if ! (if numerical) + + end do ! looping through different perturbations + + ! ------------------------------------------------------------------------------------- + ! compute derivatives + ! ------------------------------------------------------------------------------------- + select case(ixDerivMethod) + + ! ***** analytical derivatives + case(analytical) + ! compute initial derivatives + dLWRadCanopy_dTCanopy = 4._dp*emc*sb*TCan**3._dp + dLWRadGround_dTGround = 4._dp*emg*sb*TGnd**3._dp + ! compute analytical derivatives + dLWNetCanopy_dTCanopy = (emc*(1._dp - emg) - 2._dp)*dLWRadCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) + dLWNetGround_dTGround = -dLWRadGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) + dLWNetCanopy_dTGround = emc*dLWRadGround_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) + dLWNetGround_dTCanopy = emg*dLWRadCanopy_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) + + ! ***** numerical derivatives + case(numerical) + ! compute numerical derivatives (one-sided finite differences) + dLWNetCanopy_dTCanopy = (LWNetCanopy_dStateCanopy - LWNetCanopy)/dx ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) + dLWNetGround_dTGround = (LWNetGround_dStateGround - LWNetGround)/dx ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) + dLWNetCanopy_dTGround = (LWNetCanopy_dStateGround - LWNetCanopy)/dx ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) + dLWNetGround_dTCanopy = (LWNetGround_dStateCanopy - LWNetGround)/dx ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) + + ! ***** error check + case default; err=10; message=trim(message)//"unknown method to calculate derivatives"; return + + end select ! (type of method to calculate derivatives) + + end subroutine longwaveBal + + + ! ******************************************************************************************************* + ! private subroutine aeroResist: compute aerodynamic resistances + ! ******************************************************************************************************* + subroutine aeroResist(& + ! input: model control + computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) + derivDesired, & ! intent(in): flag to indicate if analytical derivatives are desired + ixVegTraits, & ! intent(in): choice of parameterization for vegetation roughness length and displacement height + ixWindProfile, & ! intent(in): choice of canopy wind profile + ixStability, & ! intent(in): choice of stability function + ! input: above-canopy forcing data + mHeight, & ! intent(in): measurement height (m) + airtemp, & ! intent(in): air temperature at some height above the surface (K) + windspd, & ! intent(in): wind speed at some height above the surface (m s-1) + ! input: temperature (canopy, ground, canopy air space) + canairTemp, & ! intent(in): temperature of the canopy air space (K) + groundTemp, & ! intent(in): ground temperature (K) + ! input: diagnostic variables + exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) + snowDepth, & ! intent(in): snow depth (m) + ! input: parameters + z0Ground, & ! intent(in): roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) + z0CanopyParam, & ! intent(in): roughness length of the canopy (m) + zpdFraction, & ! intent(in): zero plane displacement / canopy height (-) + critRichNumber, & ! intent(in): critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam, & ! intent(in): parameter in Louis (1979) stability function + Mahrt87_eScale, & ! intent(in): exponential scaling factor in the Mahrt (1987) stability function + windReductionParam, & ! intent(in): canopy wind reduction parameter (-) + leafExchangeCoeff, & ! intent(in): turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) + leafDimension, & ! intent(in): characteristic leaf dimension (m) + heightCanopyTop, & ! intent(in): height at the top of the vegetation canopy (m) + heightCanopyBottom, & ! intent(in): height at the bottom of the vegetation canopy (m) + ! output: stability corrections + RiBulkCanopy, & ! intent(out): bulk Richardson number for the canopy (-) + RiBulkGround, & ! intent(out): bulk Richardson number for the ground surface (-) + canopyStabilityCorrection, & ! intent(out): stability correction for the canopy (-) + groundStabilityCorrection, & ! intent(out): stability correction for the ground surface (-) + ! output: scalar resistances + z0Canopy, & ! intent(out): roughness length of the canopy (m) + windReductionFactor, & ! intent(out): canopy wind reduction factor (-) + zeroPlaneDisplacement, & ! intent(out): zero plane displacement (m) + eddyDiffusCanopyTop, & ! intent(out): eddy diffusivity for heat at the top of the canopy (m2 s-1) + frictionVelocity, & ! intent(out): friction velocity (m s-1) + windspdCanopyTop, & ! intent(out): windspeed at the top of the canopy (m s-1) + windspdCanopyBottom, & ! intent(out): windspeed at the height of the bottom of the canopy (m s-1) + leafResistance, & ! intent(out): mean leaf boundary layer resistance per unit leaf area (s m-1) + groundResistance, & ! intent(out): below canopy aerodynamic resistance (s m-1) + canopyResistance, & ! intent(out): above canopy aerodynamic resistance (s m-1) + ! output: derivatives in scalar resistances + dGroundResistance_dTGround, & ! intent(out): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + dGroundResistance_dTCanopy, & ! intent(out): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + dGroundResistance_dTCanair, & ! intent(out): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + dCanopyResistance_dTCanopy, & ! intent(out): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + dCanopyResistance_dTCanair, & ! intent(out): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + ! output: error control + err,message ) ! intent(out): error control + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! compute aerodynamic resistances + ! Refs: Choudhury and Monteith (4-layer model for heat budget of homogenous surfaces; QJRMS, 1988) + ! Niu and Yang (Canopy effects on snow processes; JGR, 2004) + ! Mahat et al. (Below-canopy turbulence in a snowmelt model, WRR, 2012) + implicit none + ! input: model control + logical(lgt),intent(in) :: computeVegFlux ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) + logical(lgt),intent(in) :: derivDesired ! logical flag to indicate if analytical derivatives are desired + integer(i4b),intent(in) :: ixVegTraits ! choice of parameterization for vegetation roughness length and displacement height + integer(i4b),intent(in) :: ixWindProfile ! choice of canopy wind profile + integer(i4b),intent(in) :: ixStability ! choice of stability function + ! input: above-canopy forcing data + real(dp),intent(in) :: mHeight ! measurement height (m) + real(dp),intent(in) :: airtemp ! air temperature at some height above the surface (K) + real(dp),intent(in) :: windspd ! wind speed at some height above the surface (m s-1) + ! input: temperature (canopy, ground, canopy air space) + real(dp),intent(in) :: canairTemp ! temperature of the canopy air space (K) + real(dp),intent(in) :: groundTemp ! ground temperature (K) + ! input: diagnostic variables + real(dp),intent(in) :: exposedVAI ! exposed vegetation area index -- leaf plus stem (m2 m-2) + real(dp),intent(in) :: snowDepth ! snow depth (m) + ! input: parameters + real(dp),intent(in) :: z0Ground ! roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) + real(dp),intent(in) :: z0CanopyParam ! roughness length of the canopy (m) + real(dp),intent(in) :: zpdFraction ! zero plane displacement / canopy height (-) + real(dp),intent(in) :: critRichNumber ! critical value for the bulk Richardson number where turbulence ceases (-) + real(dp),intent(in) :: Louis79_bparam ! parameter in Louis (1979) stability function + real(dp),intent(in) :: Mahrt87_eScale ! exponential scaling factor in the Mahrt (1987) stability function + real(dp),intent(in) :: windReductionParam ! canopy wind reduction parameter (-) + real(dp),intent(in) :: leafExchangeCoeff ! turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) + real(dp),intent(in) :: leafDimension ! characteristic leaf dimension (m) + real(dp),intent(in) :: heightCanopyTop ! height at the top of the vegetation canopy (m) + real(dp),intent(in) :: heightCanopyBottom ! height at the bottom of the vegetation canopy (m) + ! output: stability corrections + real(dp),intent(out) :: RiBulkCanopy ! bulk Richardson number for the canopy (-) + real(dp),intent(out) :: RiBulkGround ! bulk Richardson number for the ground surface (-) + real(dp),intent(out) :: canopyStabilityCorrection ! stability correction for the canopy (-) + real(dp),intent(out) :: groundStabilityCorrection ! stability correction for the ground surface (-) + ! output: scalar resistances + real(dp),intent(out) :: z0Canopy ! roughness length of the vegetation canopy (m) + real(dp),intent(out) :: windReductionFactor ! canopy wind reduction factor (-) + real(dp),intent(out) :: zeroPlaneDisplacement ! zero plane displacement (m) + real(dp),intent(out) :: eddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) + real(dp),intent(out) :: frictionVelocity ! friction velocity (m s-1) + real(dp),intent(out) :: windspdCanopyTop ! windspeed at the top of the canopy (m s-1) + real(dp),intent(out) :: windspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) + real(dp),intent(out) :: leafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) + real(dp),intent(out) :: groundResistance ! below canopy aerodynamic resistance (s m-1) + real(dp),intent(out) :: canopyResistance ! above canopy aerodynamic resistance (s m-1) + ! output: derivatives in scalar resistances + real(dp),intent(out) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + real(dp),intent(out) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + real(dp),intent(out) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + real(dp),intent(out) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + real(dp),intent(out) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! local variables: general + character(LEN=256) :: cmessage ! error message of downwind routine + ! local variables: vegetation roughness and dispalcement height + real(dp),parameter :: oneThird=1._dp/3._dp ! 1/3 + real(dp),parameter :: twoThirds=2._dp/3._dp ! 2/3 + real(dp),parameter :: C_r = 0.3 ! roughness element drag coefficient (-) from Raupach (BLM, 1994) + real(dp),parameter :: C_s = 0.003_dp ! substrate surface drag coefficient (-) from Raupach (BLM, 1994) + real(dp),parameter :: approxDragCoef_max = 0.3_dp ! maximum value of the approximate drag coefficient (-) from Raupach (BLM, 1994) + real(dp),parameter :: psi_h = 0.193_dp ! roughness sub-layer influence function (-) from Raupach (BLM, 1994) + real(dp),parameter :: c_d1 = 7.5_dp ! scaling parameter used to define displacement height (-) from Raupach (BLM, 1994) + real(dp),parameter :: cd_CM = 0.2_dp ! mean drag coefficient for individual leaves (-) from Choudhury and Monteith (QJRMS, 1988) + real(dp) :: funcLAI ! temporary variable to calculate zero plane displacement for the canopy + real(dp) :: fracCanopyHeight ! zero plane displacement expressed as a fraction of canopy height + real(dp) :: approxDragCoef ! approximate drag coefficient used in the computation of canopy roughness length (-) + ! local variables: resistance + real(dp) :: canopyExNeut ! surface-atmosphere exchange coefficient under neutral conditions (-) + real(dp) :: groundExNeut ! surface-atmosphere exchange coefficient under neutral conditions (-) + real(dp) :: sfc2AtmExchangeCoeff_canopy ! surface-atmosphere exchange coefficient after stability corrections (-) + real(dp) :: groundResistanceNeutral ! ground resistance under neutral conditions (s m-1) + real(dp) :: windConvFactor_fv ! factor to convert friction velocity to wind speed at top of canopy (-) + real(dp) :: windConvFactor ! factor to convert wind speed at top of canopy to wind speed at a given height in the canopy (-) + real(dp) :: referenceHeight ! z0Canopy+zeroPlaneDisplacement (m) + real(dp) :: windspdRefHeight ! windspeed at the reference height (m/s) + real(dp) :: heightAboveGround ! height above the snow surface (m) + real(dp) :: heightCanopyTopAboveSnow ! height at the top of the vegetation canopy relative to snowpack (m) + real(dp) :: heightCanopyBottomAboveSnow ! height at the bottom of the vegetation canopy relative to snowpack (m) + real(dp),parameter :: xTolerance=0.1_dp ! tolerance to handle the transition from exponential to log-below canopy + ! local variables: derivatives + real(dp) :: dFV_dT ! derivative in friction velocity w.r.t. canopy air temperature + real(dp) :: dED_dT ! derivative in eddy diffusivity at the top of the canopy w.r.t. canopy air temperature + real(dp) :: dGR_dT ! derivative in neutral ground resistance w.r.t. canopy air temperature + real(dp) :: tmp1,tmp2 ! temporary variables used in calculation of ground resistance + real(dp) :: dCanopyStabilityCorrection_dRich ! derivative in stability correction w.r.t. Richardson number for the canopy (-) + real(dp) :: dGroundStabilityCorrection_dRich ! derivative in stability correction w.r.t. Richardson number for the ground surface (-) + real(dp) :: dCanopyStabilityCorrection_dAirTemp ! (not used) derivative in stability correction w.r.t. air temperature (K-1) + real(dp) :: dGroundStabilityCorrection_dAirTemp ! (not used) derivative in stability correction w.r.t. air temperature (K-1) + real(dp) :: dCanopyStabilityCorrection_dCasTemp ! derivative in canopy stability correction w.r.t. canopy air space temperature (K-1) + real(dp) :: dGroundStabilityCorrection_dCasTemp ! derivative in ground stability correction w.r.t. canopy air space temperature (K-1) + real(dp) :: dGroundStabilityCorrection_dSfcTemp ! derivative in ground stability correction w.r.t. surface temperature (K-1) + real(dp) :: singleLeafConductance ! leaf boundary layer conductance (m s-1) + real(dp) :: canopyLeafConductance ! leaf boundary layer conductance -- scaled up to the canopy (m s-1) + real(dp) :: leaf2CanopyScaleFactor ! factor to scale from the leaf to the canopy [m s-(1/2)] + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='aeroResist/' + + ! check that measurement height is above the top of the canopy + if(mHeight < heightCanopyTop)then + err=20; message=trim(message)//'measurement height is below the top of the canopy'; return + end if + + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! * compute vegetation poperties (could be done at the same time as phenology.. does not have to be in the flux routine!) + if(computeVegFlux) then ! (if vegetation is exposed) + + ! ***** identify zero plane displacement, roughness length, and surface temperature for the canopy (m) + ! First, calculate new coordinate system above snow - use these to scale wind profiles and resistances + ! NOTE: the new coordinate system makes zeroPlaneDisplacement and z0Canopy consistent + heightCanopyTopAboveSnow = heightCanopyTop - snowDepth + heightCanopyBottomAboveSnow = max(heightCanopyBottom - snowDepth, 0.0_dp) + select case(ixVegTraits) + + ! Raupach (BLM 1994) "Simplified expressions..." + case(Raupach_BLM1994) + ! (compute zero-plane displacement) + funcLAI = sqrt(c_d1*exposedVAI) + fracCanopyHeight = -(1._dp - exp(-funcLAI))/funcLAI + 1._dp + zeroPlaneDisplacement = fracCanopyHeight*(heightCanopyTopAboveSnow-heightCanopyBottomAboveSnow)+heightCanopyBottomAboveSnow + ! (coupute roughness length of the veg canopy) + approxDragCoef = min( sqrt(C_s + C_r*exposedVAI/2._dp), approxDragCoef_max) + z0Canopy = (1._dp - fracCanopyHeight) * exp(-vkc*approxDragCoef - psi_h) * (heightCanopyTopAboveSnow-heightCanopyBottomAboveSnow) + + ! Choudhury and Monteith (QJRMS 1988) "A four layer model for the heat budget..." + case(CM_QJRMS1988) + funcLAI = cd_CM*exposedVAI + zeroPlaneDisplacement = 1.1_dp*heightCanopyTopAboveSnow*log(1._dp + funcLAI**0.25_dp) + if(funcLAI < 0.2_dp)then + z0Canopy = z0Ground + 0.3_dp*heightCanopyTopAboveSnow*funcLAI**0.5_dp + else + z0Canopy = 0.3_dp*heightCanopyTopAboveSnow*(1._dp - zeroPlaneDisplacement/heightCanopyTopAboveSnow) + end if + + ! constant parameters dependent on the vegetation type + case(vegTypeTable) + zeroPlaneDisplacement = zpdFraction*heightCanopyTopAboveSnow ! zero-plane displacement (m) + z0Canopy = z0CanopyParam ! roughness length of the veg canopy (m) + + ! check + case default + err=10; message=trim(message)//"unknown parameterization for vegetation roughness length and displacement height"; return + + end select ! vegetation traits (z0, zpd) + + ! check zero plane displacement + if(zeroPlaneDisplacement < heightCanopyBottomAboveSnow)then + write(*,'(a,1x,10(f12.5,1x))') 'heightCanopyTop, snowDepth, heightCanopyTopAboveSnow, heightCanopyBottomAboveSnow, exposedVAI = ', & + heightCanopyTop, snowDepth, heightCanopyTopAboveSnow, heightCanopyBottomAboveSnow, exposedVAI + message=trim(message)//'zero plane displacement is below the canopy bottom' + err=20; return + endif + + ! check measurement height + if(mHeight < zeroPlaneDisplacement)then; err=20; message=trim(message)//'measurement height is below the displacement height'; return; end if + if(mHeight < z0Canopy)then; err=20; message=trim(message)//'measurement height is below the roughness length'; return; end if + + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! * compute resistance for the case where the canopy is exposed + ! compute the stability correction for resistance from canopy air space to air above the canopy (-) + call aStability(& + ! input + derivDesired, & ! input: logical flag to compute analytical derivatives + ixStability, & ! input: choice of stability function + ! input: forcing data, diagnostic and state variables + mHeight, & ! input: measurement height (m) + airTemp, & ! input: air temperature above the canopy (K) + canairTemp, & ! input: temperature of the canopy air space (K) + windspd, & ! input: wind speed above the canopy (m s-1) + ! input: stability parameters + critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam, & ! input: parameter in Louis (1979) stability function + Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function + ! output + RiBulkCanopy, & ! output: bulk Richardson number (-) + canopyStabilityCorrection, & ! output: stability correction for turbulent heat fluxes (-) + dCanopyStabilityCorrection_dRich, & ! output: derivative in stability correction w.r.t. Richardson number for the canopy (-) + dCanopyStabilityCorrection_dAirTemp, & ! output: (not used) derivative in stability correction w.r.t. air temperature (K-1) + dCanopyStabilityCorrection_dCasTemp, & ! output: derivative in stability correction w.r.t. canopy air space temperature (K-1) + err, cmessage ) ! output: error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + ! compute turbulent exchange coefficient (-) + canopyExNeut = (vkc**2._dp) / ( log((mHeight - zeroPlaneDisplacement)/z0Canopy))**2._dp ! coefficient under conditions of neutral stability + sfc2AtmExchangeCoeff_canopy = canopyExNeut*canopyStabilityCorrection ! after stability corrections + + ! compute the friction velocity (m s-1) + frictionVelocity = windspd * sqrt(sfc2AtmExchangeCoeff_canopy) + + ! compute the above-canopy resistance (s m-1) + canopyResistance = 1._dp/(sfc2AtmExchangeCoeff_canopy*windspd) + if(canopyResistance < 0._dp)then; err=20; message=trim(message)//'canopy resistance < 0'; return; end if + + ! compute windspeed at the top of the canopy above snow depth (m s-1) + ! NOTE: stability corrections cancel out + windConvFactor_fv = log((heightCanopyTopAboveSnow - zeroPlaneDisplacement)/z0Canopy) / log((mHeight - snowDepth - zeroPlaneDisplacement)/z0Canopy) + windspdCanopyTop = windspd*windConvFactor_fv + + ! compute the windspeed reduction + ! Refs: Norman et al. (Ag. Forest Met., 1995) -- citing Goudriaan (1977 manuscript "crop micrometeorology: a simulation study", Wageningen). + windReductionFactor = windReductionParam * exposedVAI**twoThirds * (heightCanopyTopAboveSnow - heightCanopyBottomAboveSnow)**oneThird / leafDimension**oneThird + + ! compute windspeed at the height z0Canopy+zeroPlaneDisplacement (m s-1) + referenceHeight = z0Canopy+zeroPlaneDisplacement + windConvFactor = exp(-windReductionFactor*(1._dp - (referenceHeight/heightCanopyTopAboveSnow))) + windspdRefHeight = windspdCanopyTop*windConvFactor + + ! compute windspeed at the bottom of the canopy relative to the snow depth (m s-1) + windConvFactor = exp(-windReductionFactor*(1._dp - (heightCanopyBottomAboveSnow/heightCanopyTopAboveSnow))) + windspdCanopyBottom = windspdCanopyTop*windConvFactor + + ! compute the leaf boundary layer resistance (s m-1) + singleLeafConductance = leafExchangeCoeff*sqrt(windspdCanopyTop/leafDimension) + leaf2CanopyScaleFactor = (2._dp/windReductionFactor) * (1._dp - exp(-windReductionFactor/2._dp)) ! factor to scale from the leaf to the canopy + canopyLeafConductance = singleLeafConductance*leaf2CanopyScaleFactor + leafResistance = 1._dp/(canopyLeafConductance) + if(leafResistance < 0._dp)then; err=20; message=trim(message)//'leaf resistance < 0'; return; end if + + ! compute eddy diffusivity for heat at the top of the canopy (m2 s-1) + ! Note: use of friction velocity here includes stability adjustments + ! Note: max used to avoid dividing by zero + eddyDiffusCanopyTop = max(vkc*FrictionVelocity*(heightCanopyTopAboveSnow - zeroPlaneDisplacement), mpe) + + ! compute the resistance between the surface and canopy air UNDER NEUTRAL CONDITIONS (s m-1) + + ! case 1: assume exponential profile extends from the snow depth plus surface roughness length to the displacement height plus vegetation roughness + if(ixWindProfile==exponential .or. heightCanopyBottomAboveSnowz0Ground+xTolerance + else + + ! compute the neutral ground resistance + ! (first, component between heightCanopyBottomAboveSnow and z0Canopy+zeroPlaneDisplacement) + tmp1 = exp(-windReductionFactor* heightCanopyBottomAboveSnow/heightCanopyTopAboveSnow) + tmp2 = exp(-windReductionFactor*(z0Canopy+zeroPlaneDisplacement)/heightCanopyTopAboveSnow) + groundResistanceNeutral = ( heightCanopyTopAboveSnow*exp(windReductionFactor) / (windReductionFactor*eddyDiffusCanopyTop) ) * (tmp1 - tmp2) + ! (add log-below-canopy component) + groundResistanceNeutral = groundResistanceNeutral + (1._dp/(max(0.1_dp,windspdCanopyBottom)*vkc**2._dp))*(log(heightCanopyBottomAboveSnow/z0Ground))**2._dp + + endif ! switch between exponential profile and log-below-canopy + + ! compute the stability correction for resistance from the ground to the canopy air space (-) + ! NOTE: here we are interested in the windspeed at height z0Canopy+zeroPlaneDisplacement + call aStability(& + ! input + derivDesired, & ! input: logical flag to compute analytical derivatives + ixStability, & ! input: choice of stability function + ! input: forcing data, diagnostic and state variables + referenceHeight, & ! input: height of the canopy air space temperature/wind (m) + canairTemp, & ! input: temperature of the canopy air space (K) + groundTemp, & ! input: temperature of the ground surface (K) + max(0.1_dp,windspdRefHeight), & ! input: wind speed at height z0Canopy+zeroPlaneDisplacement (m s-1) + ! input: stability parameters + critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam, & ! input: parameter in Louis (1979) stability function + Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function + ! output + RiBulkGround, & ! output: bulk Richardson number (-) + groundStabilityCorrection, & ! output: stability correction for turbulent heat fluxes (-) + dGroundStabilityCorrection_dRich, & ! output: derivative in stability correction w.r.t. Richardson number for the canopy (-) + dGroundStabilityCorrection_dCasTemp, & ! output: derivative in stability correction w.r.t. canopy air space temperature (K-1) + dGroundStabilityCorrection_dSfcTemp, & ! output: derivative in stability correction w.r.t. surface temperature (K-1) + err, cmessage ) ! output: error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + ! compute the ground resistance + groundResistance = groundResistanceNeutral / groundStabilityCorrection + if(groundResistance < 0._dp)then; err=20; message=trim(message)//'ground resistance < 0 [vegetation is present]'; return; end if + + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! * compute resistance for the case without a canopy (bare ground, or canopy completely buried with snow) + else + + ! no canopy, so set huge resistances (not used) + canopyResistance = 1.e12_dp ! not used: huge resistance, so conductance is essentially zero + leafResistance = 1.e12_dp ! not used: huge resistance, so conductance is essentially zero + + ! check that measurement height above the ground surface is above the roughness length + if(mHeight < snowDepth+z0Ground)then; err=20; message=trim(message)//'measurement height < snow depth + roughness length'; return; end if + + ! compute the resistance between the surface and canopy air UNDER NEUTRAL CONDITIONS (s m-1) + groundExNeut = (vkc**2._dp) / ( log((mHeight - snowDepth)/z0Ground)**2._dp) ! turbulent transfer coefficient under conditions of neutral stability (-) + groundResistanceNeutral = 1._dp / (groundExNeut*windspd) + + ! define height above the snow surface + heightAboveGround = mHeight - snowDepth + + ! check that measurement height above the ground surface is above the roughness length + if(heightAboveGround < z0Ground)then + print*, 'z0Ground = ', z0Ground + print*, 'mHeight = ', mHeight + print*, 'snowDepth = ', snowDepth + print*, 'heightAboveGround = ', heightAboveGround + message=trim(message)//'height above ground < roughness length [likely due to snow accumulation]' + err=20; return + end if + + ! compute ground stability correction + call aStability(& + ! input + derivDesired, & ! input: logical flag to compute analytical derivatives + ixStability, & ! input: choice of stability function + ! input: forcing data, diagnostic and state variables + heightAboveGround, & ! input: measurement height above the ground surface (m) + airtemp, & ! input: temperature above the ground surface (K) + groundTemp, & ! input: trial value of surface temperature -- "surface" is either canopy or ground (K) + windspd, & ! input: wind speed above the ground surface (m s-1) + ! input: stability parameters + critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam, & ! input: parameter in Louis (1979) stability function + Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function + ! output + RiBulkGround, & ! output: bulk Richardson number (-) + groundStabilityCorrection, & ! output: stability correction for turbulent heat fluxes (-) + dGroundStabilityCorrection_dRich, & ! output: derivative in stability correction w.r.t. Richardson number for the ground surface (-) + dGroundStabilityCorrection_dAirTemp, & ! output: (not used) derivative in stability correction w.r.t. air temperature (K-1) + dGroundStabilityCorrection_dSfcTemp, & ! output: derivative in stability correction w.r.t. surface temperature (K-1) + err, cmessage ) ! output: error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + ! compute the ground resistance (after stability corrections) + groundResistance = groundResistanceNeutral/groundStabilityCorrection + if(groundResistance < 0._dp)then; err=20; message=trim(message)//'ground resistance < 0 [no vegetation]'; return; end if + + ! set all canopy variables to missing (no canopy!) + z0Canopy = missingValue ! roughness length of the vegetation canopy (m) + RiBulkCanopy = missingValue ! bulk Richardson number for the canopy (-) + windReductionFactor = missingValue ! canopy wind reduction factor (-) + zeroPlaneDisplacement = missingValue ! zero plane displacement (m) + canopyStabilityCorrection = missingValue ! stability correction for the canopy (-) + eddyDiffusCanopyTop = missingValue ! eddy diffusivity for heat at the top of the canopy (m2 s-1) + frictionVelocity = missingValue ! friction velocity (m s-1) + windspdCanopyTop = missingValue ! windspeed at the top of the canopy (m s-1) + windspdCanopyBottom = missingValue ! windspeed at the height of the bottom of the canopy (m s-1) + + end if ! (if no canopy) + + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! * compute derivatives + if(derivDesired)then ! if analytical derivatives are desired + + ! derivatives for the vegetation canopy + if(computeVegFlux) then ! (if vegetation is exposed) + + ! ***** compute derivatives w.r.t. canopy temperature + ! NOTE: derivatives are zero because using canopy air space temperature + dCanopyResistance_dTCanopy = 0._dp ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + dGroundResistance_dTCanopy = 0._dp ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + + ! ***** compute derivatives w.r.t. ground temperature (s m-1 K-1) + dGroundResistance_dTGround = -(groundResistanceNeutral*dGroundStabilityCorrection_dSfcTemp)/(groundStabilityCorrection**2._dp) + + ! ***** compute derivatives w.r.t. temperature of the canopy air space (s m-1 K-1) + ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + dCanopyResistance_dTCanair = -dCanopyStabilityCorrection_dCasTemp/(windspd*canopyExNeut*canopyStabilityCorrection**2._dp) + ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + ! (compute derivative in NEUTRAL ground resistance w.r.t. canopy air temperature (s m-1 K-1)) + dFV_dT = windspd*canopyExNeut*dCanopyStabilityCorrection_dCasTemp/(sqrt(sfc2AtmExchangeCoeff_canopy)*2._dp) ! d(frictionVelocity)/d(canopy air temperature) + dED_dT = dFV_dT*vkc*(heightCanopyTopAboveSnow - zeroPlaneDisplacement) ! d(eddyDiffusCanopyTop)d(canopy air temperature) + dGR_dT = -dED_dT*(tmp1 - tmp2)*heightCanopyTopAboveSnow*exp(windReductionFactor) / (windReductionFactor*eddyDiffusCanopyTop**2._dp) ! d(groundResistanceNeutral)/d(canopy air temperature) + ! (stitch everything together -- product rule) + dGroundResistance_dTCanair = dGR_dT/groundStabilityCorrection - groundResistanceNeutral*dGroundStabilityCorrection_dCasTemp/(groundStabilityCorrection**2._dp) + + ! ***** compute resistances for non-vegetated surfaces (e.g., snow) + else + + ! set canopy derivatives to zero (non-vegetated, remember) + dCanopyResistance_dTCanopy = 0._dp + dGroundResistance_dTCanopy = 0._dp + + ! compute derivatives for ground resistance + dGroundResistance_dTGround = -dGroundStabilityCorrection_dSfcTemp/(windspd*groundExNeut*groundStabilityCorrection**2._dp) + + end if ! (switch between vegetated and non-vegetated surfaces) + + ! * analytical derivatives not desired + else + dGroundResistance_dTGround = missingValue + dGroundResistance_dTCanopy = missingValue + dCanopyResistance_dTCanopy = missingValue + end if + + ! test + !print*, 'dGroundResistance_dTGround = ', dGroundResistance_dTGround + !print*, 'dGroundResistance_dTCanopy = ', dGroundResistance_dTCanopy + !print*, 'dCanopyResistance_dTCanopy = ', dCanopyResistance_dTCanopy + !pause 'in aeroResist' + + end subroutine aeroResist + + + ! ******************************************************************************************************* + ! private subroutine soilResist: compute soil moisture factor controlling stomatal resistance + ! ******************************************************************************************************* + subroutine soilResist(& + ! input (model decisions) + ixSoilResist, & ! intent(in): choice of function for the soil moisture control on stomatal resistance + ixGroundwater, & ! intent(in): choice of groundwater representation + ! input (state variables) + mLayerMatricHead, & ! intent(in): matric head in each layer (m) + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water in each layer + scalarAquiferStorage, & ! intent(in): aquifer storage (m) + ! input (diagnostic variables) + mLayerRootDensity, & ! intent(in): root density in each layer (-) + scalarAquiferRootFrac, & ! intent(in): fraction of roots below the lowest unsaturated layer (-) + ! input (parameters) + plantWiltPsi, & ! intent(in): matric head at wilting point (m) + soilStressParam, & ! intent(in): parameter in the exponential soil stress function (-) + critSoilWilting, & ! intent(in): critical vol. liq. water content when plants are wilting (-) + critSoilTranspire, & ! intent(in): critical vol. liq. water content when transpiration is limited (-) + critAquiferTranspire, & ! intent(in): critical aquifer storage value when transpiration is limited (m) + ! output + wAvgTranspireLimitFac, & ! intent(out): weighted average of the transpiration limiting factor (-) + mLayerTranspireLimitFac, & ! intent(out): transpiration limiting factor in each layer (-) + aquiferTranspireLimitFac, & ! intent(out): transpiration limiting factor for the aquifer (-) + err,message) ! intent(out): error control + ! ----------------------------------------------------------------------------------------------------------------------------------------- + USE mDecisions_module, only: NoahType,CLM_Type,SiB_Type ! options for the choice of function for the soil moisture control on stomatal resistance + USE mDecisions_module, only: bigBucket ! named variable that defines the "bigBucket" groundwater parameterization + implicit none + ! input (model decisions) + integer(i4b),intent(in) :: ixSoilResist ! choice of function for the soil moisture control on stomatal resistance + integer(i4b),intent(in) :: ixGroundwater ! choice of groundwater representation + ! input (variables) + real(dp),intent(in) :: mLayerMatricHead(:) ! matric head in each layer (m) + real(dp),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid water in each layer (-) + real(dp),intent(in) :: scalarAquiferStorage ! aquifer storage (m) + ! input (diagnostic variables) + real(dp),intent(in) :: mLayerRootDensity(:) ! root density in each layer (-) + real(dp),intent(in) :: scalarAquiferRootFrac ! fraction of roots below the lowest unsaturated layer (-) + ! input (parameters) + real(dp),intent(in) :: plantWiltPsi ! matric head at wilting point (m) + real(dp),intent(in) :: soilStressParam ! parameter in the exponential soil stress function (-) + real(dp),intent(in) :: critSoilWilting ! critical vol. liq. water content when plants are wilting (-) + real(dp),intent(in) :: critSoilTranspire ! critical vol. liq. water content when transpiration is limited (-) + real(dp),intent(in) :: critAquiferTranspire ! critical aquifer storage value when transpiration is limited (m) + ! output + real(dp),intent(out) :: wAvgTranspireLimitFac ! intent(out): weighted average of the transpiration limiting factor (-) + real(dp),intent(out) :: mLayerTranspireLimitFac(:) ! intent(out): transpiration limiting factor in each layer (-) + real(dp),intent(out) :: aquiferTranspireLimitFac ! intent(out): transpiration limiting factor for the aquifer (-) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local variables + real(dp) :: gx ! stress function for the soil layers + real(dp),parameter :: verySmall=epsilon(gx) ! a very small number + integer(i4b) :: iLayer ! index of soil layer + ! initialize error control + err=0; message='soilResist/' + + ! ** compute the factor limiting transpiration for each soil layer (-) + wAvgTranspireLimitFac = 0._dp ! (initialize the weighted average) + do iLayer=1,size(mLayerMatricHead) + ! compute the soil stress function + select case(ixSoilResist) + case(NoahType) ! thresholded linear function of volumetric liquid water content + gx = (mLayerVolFracLiq(iLayer) - critSoilWilting) / (critSoilTranspire - critSoilWilting) + case(CLM_Type) ! thresholded linear function of matric head + if(mLayerMatricHead(iLayer) > plantWiltPsi)then + gx = 1._dp - mLayerMatricHead(iLayer)/plantWiltPsi + else + gx = 0._dp + end if + case(SiB_Type) ! exponential of the log of matric head + if(mLayerMatricHead(iLayer) < 0._dp)then ! (unsaturated) + gx = 1._dp - exp( -soilStressParam * ( log(plantWiltPsi/mLayerMatricHead(iLayer)) ) ) + else ! (saturated) + gx = 1._dp + end if + case default ! check identified the option + err=20; message=trim(message)//'cannot identify option for soil resistance'; return + end select + ! save the factor for the given layer (ensure between zero and one) + mLayerTranspireLimitFac(iLayer) = min( max(verySmall,gx), 1._dp) + ! compute the weighted average (weighted by root density) + wAvgTranspireLimitFac = wAvgTranspireLimitFac + mLayerTranspireLimitFac(iLayer)*mLayerRootDensity(iLayer) + end do ! (looping through soil layers) + + ! ** compute the factor limiting evaporation in the aquifer + if(scalarAquiferRootFrac > verySmall)then + ! check that aquifer root fraction is allowed + if(ixGroundwater /= bigBucket)then + message=trim(message)//'aquifer evaporation only allowed for the big groundwater bucket -- increase the soil depth to account for roots' + err=20; return + end if + ! compute the factor limiting evaporation for the aquifer + aquiferTranspireLimitFac = min(scalarAquiferStorage/critAquiferTranspire, 1._dp) + else ! (if there are roots in the aquifer) + aquiferTranspireLimitFac = 0._dp + end if + + ! compute the weighted average (weighted by root density) + wAvgTranspireLimitFac = wAvgTranspireLimitFac + aquiferTranspireLimitFac*scalarAquiferRootFrac + + end subroutine soilResist + + + ! ******************************************************************************** + ! private subroutine turbFluxes: compute turbulent heat fluxes + ! ******************************************************************************** + subroutine turbFluxes(& + ! input: model control + computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) + ixDerivMethod, & ! intent(in): choice of method used to compute derivative (analytical or numerical) + ! input: above-canopy forcing data + airtemp, & ! intent(in): air temperature at some height above the surface (K) + airpres, & ! intent(in): air pressure of the air above the vegetation canopy (Pa) + VPair, & ! intent(in): vapor pressure of the air above the vegetation canopy (Pa) + ! input: latent heat of sublimation/vaporization + latHeatSubVapCanopy, & ! intent(in): latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) + latHeatSubVapGround, & ! intent(in): latent heat of sublimation/vaporization for the ground surface (J kg-1) + ! input: canopy and ground temperature + canairTemp, & ! intent(in): temperature of the canopy air space (K) + canopyTemp, & ! intent(in): canopy temperature (K) + groundTemp, & ! intent(in): ground temperature (K) + satVP_CanopyTemp, & ! intent(in): saturation vapor pressure at the temperature of the veg canopy (Pa) + satVP_GroundTemp, & ! intent(in): saturation vapor pressure at the temperature of the ground (Pa) + dSVPCanopy_dCanopyTemp, & ! intent(in): derivative in canopy saturation vapor pressure w.r.t. canopy temperature (Pa K-1) + dSVPGround_dGroundTemp, & ! intent(in): derivative in ground saturation vapor pressure w.r.t. ground temperature (Pa K-1) + ! input: diagnostic variables + exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) + canopyWetFraction, & ! intent(in): fraction of canopy that is wet [0-1] + dCanopyWetFraction_dWat, & ! intent(in): derivative in the canopy wetted fraction w.r.t. total water content (kg-1 m-2) + dCanopyWetFraction_dT, & ! intent(in): derivative in wetted fraction w.r.t. canopy temperature (K-1) + canopySunlitLAI, & ! intent(in): sunlit leaf area (-) + canopyShadedLAI, & ! intent(in): shaded leaf area (-) + soilRelHumidity, & ! intent(in): relative humidity in the soil pores [0-1] + soilResistance, & ! intent(in): resistance from the soil (s m-1) + leafResistance, & ! intent(in): mean leaf boundary layer resistance per unit leaf area (s m-1) + groundResistance, & ! intent(in): below canopy aerodynamic resistance (s m-1) + canopyResistance, & ! intent(in): above canopy aerodynamic resistance (s m-1) + stomResistSunlit, & ! intent(in): stomatal resistance for sunlit leaves (s m-1) + stomResistShaded, & ! intent(in): stomatal resistance for shaded leaves (s m-1) + ! input: derivatives in scalar resistances + dGroundResistance_dTGround, & ! intent(in): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + dGroundResistance_dTCanopy, & ! intent(in): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + dGroundResistance_dTCanair, & ! intent(in): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + dCanopyResistance_dTCanopy, & ! intent(in): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + dCanopyResistance_dTCanair, & ! intent(in): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + ! output: conductances (used to check derivative calculations) + leafConductance, & ! intent(out): leaf conductance (m s-1) + canopyConductance, & ! intent(out): canopy conductance (m s-1) + groundConductanceSH, & ! intent(out): ground conductance for sensible heat (m s-1) + groundConductanceLH, & ! intent(out): ground conductance for latent heat -- includes soil resistance (m s-1) + evapConductance, & ! intent(out): conductance for evaporation (m s-1) + transConductance, & ! intent(out): conductance for transpiration (m s-1) + totalConductanceSH, & ! intent(out): total conductance for sensible heat (m s-1) + totalConductanceLH, & ! intent(out): total conductance for latent heat (m s-1) + ! output: canopy air space variables + VP_CanopyAir, & ! intent(out): vapor pressure of the canopy air space (Pa) + ! output: fluxes from the vegetation canopy + senHeatCanopy, & ! intent(out): sensible heat flux from the canopy to the canopy air space (W m-2) + latHeatCanopyEvap, & ! intent(out): latent heat flux associated with evaporation from the canopy to the canopy air space (W m-2) + latHeatCanopyTrans, & ! intent(out): latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) + ! output: fluxes from non-vegetated surfaces (ground surface below vegetation, bare ground, or snow covered vegetation) + senHeatGround, & ! intent(out): sensible heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) + latHeatGround, & ! intent(out): latent heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) + ! output: total heat fluxes to the atmosphere + senHeatTotal, & ! intent(out): total sensible heat flux to the atmosphere (W m-2) + latHeatTotal, & ! intent(out): total latent heat flux to the atmosphere (W m-2) + ! output: net fluxes + turbFluxCanair, & ! intent(out): net turbulent heat fluxes at the canopy air space (W m-2) + turbFluxCanopy, & ! intent(out): net turbulent heat fluxes at the canopy (W m-2) + turbFluxGround, & ! intent(out): net turbulent heat fluxes at the ground surface (W m-2) + ! output: flux derivatives + dTurbFluxCanair_dTCanair, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxCanair_dTCanopy, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxCanair_dTGround, & ! intent(out): derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanair, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanopy, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxCanopy_dTGround, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxGround_dTCanair, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxGround_dTCanopy, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxGround_dTGround, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + ! output: liquid flux derivatives (canopy evap) + dLatHeatCanopyEvap_dCanLiq, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (J kg-1 s-1) + dLatHeatCanopyEvap_dTCanair, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) + dLatHeatCanopyEvap_dTCanopy, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) + dLatHeatCanopyEvap_dTGround, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) + ! output: liquid flux derivatives (ground evap) + dLatHeatGroundEvap_dCanLiq, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy liquid water content (J kg-1 s-1) + dLatHeatGroundEvap_dTCanair, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy air temperature + dLatHeatGroundEvap_dTCanopy, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy temperature + dLatHeatGroundEvap_dTGround, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. ground temperature + ! output: cross derivatives + dTurbFluxCanair_dCanLiq, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dTurbFluxCanopy_dCanLiq, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dTurbFluxGround_dCanLiq, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + ! output: error control + err,message ) ! intent(out): error control + ! ----------------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input: model control + logical(lgt),intent(in) :: computeVegFlux ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) + integer(i4b),intent(in) :: ixDerivMethod ! choice of method used to compute derivative (analytical or numerical) + ! input: above-canopy forcing data + real(dp),intent(in) :: airtemp ! air temperature at some height above the surface (K) + real(dp),intent(in) :: airpres ! air pressure of the air above the vegetation canopy (Pa) + real(dp),intent(in) :: VPair ! vapor pressure of the air above the vegetation canopy (Pa) + ! input: latent heat of sublimation/vaporization + real(dp),intent(in) :: latHeatSubVapCanopy ! latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) + real(dp),intent(in) :: latHeatSubVapGround ! latent heat of sublimation/vaporization for the ground surface (J kg-1) + ! input: canopy and ground temperature + real(dp),intent(in) :: canairTemp ! temperature of the canopy air space (K) + real(dp),intent(in) :: canopyTemp ! canopy temperature (K) + real(dp),intent(in) :: groundTemp ! ground temperature (K) + real(dp),intent(in) :: satVP_CanopyTemp ! saturation vapor pressure at the temperature of the veg canopy (Pa) + real(dp),intent(in) :: satVP_GroundTemp ! saturation vapor pressure at the temperature of the ground (Pa) + real(dp),intent(in) :: dSVPCanopy_dCanopyTemp ! derivative in canopy saturation vapor pressure w.r.t. canopy temperature (Pa K-1) + real(dp),intent(in) :: dSVPGround_dGroundTemp ! derivative in ground saturation vapor pressure w.r.t. ground temperature (Pa K-1) + ! input: diagnostic variables + real(dp),intent(in) :: exposedVAI ! exposed vegetation area index -- leaf plus stem (m2 m-2) + real(dp),intent(in) :: canopyWetFraction ! fraction of canopy that is wet [0-1] + real(dp),intent(in) :: dCanopyWetFraction_dWat ! derivative in the canopy wetted fraction w.r.t. liquid water content (kg-1 m-2) + real(dp),intent(in) :: dCanopyWetFraction_dT ! derivative in the canopy wetted fraction w.r.t. canopy temperature (K-1) + real(dp),intent(in) :: canopySunlitLAI ! sunlit leaf area (-) + real(dp),intent(in) :: canopyShadedLAI ! shaded leaf area (-) + real(dp),intent(in) :: soilRelHumidity ! relative humidity in the soil pores [0-1] + real(dp),intent(in) :: soilResistance ! resistance from the soil (s m-1) + real(dp),intent(in) :: leafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) + real(dp),intent(in) :: groundResistance ! below canopy aerodynamic resistance (s m-1) + real(dp),intent(in) :: canopyResistance ! above canopy aerodynamic resistance (s m-1) + real(dp),intent(in) :: stomResistSunlit ! stomatal resistance for sunlit leaves (s m-1) + real(dp),intent(in) :: stomResistShaded ! stomatal resistance for shaded leaves (s m-1) + ! input: derivatives in scalar resistances + real(dp),intent(in) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + real(dp),intent(in) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + real(dp),intent(in) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + real(dp),intent(in) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + real(dp),intent(in) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + ! --------------------------------------------------------------------------------------------------------------------------------------------------------------- + ! output: conductances -- used to test derivatives + real(dp),intent(out) :: leafConductance ! leaf conductance (m s-1) + real(dp),intent(out) :: canopyConductance ! canopy conductance (m s-1) + real(dp),intent(out) :: groundConductanceSH ! ground conductance for sensible heat (m s-1) + real(dp),intent(out) :: groundConductanceLH ! ground conductance for latent heat -- includes soil resistance (m s-1) + real(dp),intent(out) :: evapConductance ! conductance for evaporation (m s-1) + real(dp),intent(out) :: transConductance ! conductance for transpiration (m s-1) + real(dp),intent(out) :: totalConductanceSH ! total conductance for sensible heat (m s-1) + real(dp),intent(out) :: totalConductanceLH ! total conductance for latent heat (m s-1) + ! output: canopy air space variables + real(dp),intent(out) :: VP_CanopyAir ! vapor pressure of the canopy air space (Pa) + ! output: fluxes from the vegetation canopy + real(dp),intent(out) :: senHeatCanopy ! sensible heat flux from the canopy to the canopy air space (W m-2) + real(dp),intent(out) :: latHeatCanopyEvap ! latent heat flux associated with evaporation from the canopy to the canopy air space (W m-2) + real(dp),intent(out) :: latHeatCanopyTrans ! latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) + ! output: fluxes from non-vegetated surfaces (ground surface below vegetation, bare ground, or snow covered vegetation) + real(dp),intent(out) :: senHeatGround ! sensible heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) + real(dp),intent(out) :: latHeatGround ! latent heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) + ! output: total heat fluxes to the atmosphere + real(dp),intent(out) :: senHeatTotal ! total sensible heat flux to the atmosphere (W m-2) + real(dp),intent(out) :: latHeatTotal ! total latent heat flux to the atmosphere (W m-2) + ! output: net fluxes + real(dp),intent(out) :: turbFluxCanair ! net turbulent heat fluxes at the canopy air space (W m-2) + real(dp),intent(out) :: turbFluxCanopy ! net turbulent heat fluxes at the canopy (W m-2) + real(dp),intent(out) :: turbFluxGround ! net turbulent heat fluxes at the ground surface (W m-2) + ! output: energy flux derivatives + real(dp),intent(out) :: dTurbFluxCanair_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(dp),intent(out) :: dTurbFluxCanair_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) + real(dp),intent(out) :: dTurbFluxCanair_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) + real(dp),intent(out) :: dTurbFluxCanopy_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(dp),intent(out) :: dTurbFluxCanopy_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + real(dp),intent(out) :: dTurbFluxCanopy_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + real(dp),intent(out) :: dTurbFluxGround_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(dp),intent(out) :: dTurbFluxGround_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + real(dp),intent(out) :: dTurbFluxGround_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + ! output: liquid flux derivatives (canopy evap) + real(dp),intent(out) :: dLatHeatCanopyEvap_dCanLiq ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (W kg-1) + real(dp),intent(out) :: dLatHeatCanopyEvap_dTCanair ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) + real(dp),intent(out) :: dLatHeatCanopyEvap_dTCanopy ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) + real(dp),intent(out) :: dLatHeatCanopyEvap_dTGround ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) + ! output: liquid flux derivatives (ground evap) + real(dp),intent(out) :: dLatHeatGroundEvap_dCanLiq ! derivative in latent heat of ground evaporation w.r.t. canopy liquid water content (J kg-1 s-1) + real(dp),intent(out) :: dLatHeatGroundEvap_dTCanair ! derivative in latent heat of ground evaporation w.r.t. canopy air temperature (W m-2 K-1) + real(dp),intent(out) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) + real(dp),intent(out) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) + ! output: cross derivatives + real(dp),intent(out) :: dTurbFluxCanair_dCanLiq ! derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + real(dp),intent(out) :: dTurbFluxCanopy_dCanLiq ! derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + real(dp),intent(out) :: dTurbFluxGround_dCanLiq ! derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! local variables -- general + real(dp) :: fpart1,fpart2 ! different parts of a function + real(dp) :: dPart0,dpart1,dpart2 ! derivatives for different parts of a function + ! local variables -- "constants" + real(dp) :: volHeatCapacityAir ! volumetric heat capacity of air (J m-3) + real(dp) :: latentHeatConstant ! latent heat constant (kg m-3 K-1) + ! local variables -- derivatives for energy conductances + real(dp) :: dEvapCond_dCanopyTemp ! derivative in evap conductance w.r.t. canopy temperature + real(dp) :: dTransCond_dCanopyTemp ! derivative in trans conductance w.r.t. canopy temperature + real(dp) :: dCanopyCond_dCanairTemp ! derivative in canopy conductance w.r.t. canopy air temperature + real(dp) :: dCanopyCond_dCanopyTemp ! derivative in canopy conductance w.r.t. canopy temperature + real(dp) :: dGroundCondSH_dCanairTemp ! derivative in ground conductance of sensible heat w.r.t. canopy air temperature + real(dp) :: dGroundCondSH_dCanopyTemp ! derivative in ground conductance of sensible heat w.r.t. canopy temperature + real(dp) :: dGroundCondSH_dGroundTemp ! derivative in ground conductance of sensible heat w.r.t. ground temperature + ! local variables -- derivatives for mass conductances + real(dp) :: dGroundCondLH_dCanairTemp ! derivative in ground conductance w.r.t. canopy air temperature + real(dp) :: dGroundCondLH_dCanopyTemp ! derivative in ground conductance w.r.t. canopy temperature + real(dp) :: dGroundCondLH_dGroundTemp ! derivative in ground conductance w.r.t. ground temperature + ! local variables -- derivatives for the canopy air space variables + real(dp) :: fPart_VP ! part of the function for vapor pressure of the canopy air space + real(dp) :: leafConductanceTr ! leaf conductance for transpiration (m s-1) + real(dp) :: dVPCanopyAir_dTCanair ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the canopy air space + real(dp) :: dVPCanopyAir_dTCanopy ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the canopy + real(dp) :: dVPCanopyAir_dTGround ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the ground + real(dp) :: dVPCanopyAir_dWetFrac ! derivative of vapor pressure in the canopy air space w.r.t. wetted fraction of the canopy + real(dp) :: dVPCanopyAir_dCanLiq ! derivative of vapor pressure in the canopy air space w.r.t. canopy liquid water content + ! local variables -- sensible heat flux derivatives + real(dp) :: dSenHeatTotal_dTCanair ! derivative in the total sensible heat flux w.r.t. canopy air temperature + real(dp) :: dSenHeatTotal_dTCanopy ! derivative in the total sensible heat flux w.r.t. canopy air temperature + real(dp) :: dSenHeatTotal_dTGround ! derivative in the total sensible heat flux w.r.t. ground temperature + real(dp) :: dSenHeatCanopy_dTCanair ! derivative in the canopy sensible heat flux w.r.t. canopy air temperature + real(dp) :: dSenHeatCanopy_dTCanopy ! derivative in the canopy sensible heat flux w.r.t. canopy temperature + real(dp) :: dSenHeatCanopy_dTGround ! derivative in the canopy sensible heat flux w.r.t. ground temperature + real(dp) :: dSenHeatGround_dTCanair ! derivative in the ground sensible heat flux w.r.t. canopy air temperature + real(dp) :: dSenHeatGround_dTCanopy ! derivative in the ground sensible heat flux w.r.t. canopy temperature + real(dp) :: dSenHeatGround_dTGround ! derivative in the ground sensible heat flux w.r.t. ground temperature + ! local variables -- latent heat flux derivatives + real(dp) :: dLatHeatCanopyTrans_dTCanair ! derivative in the canopy transpiration flux w.r.t. canopy air temperature + real(dp) :: dLatHeatCanopyTrans_dTCanopy ! derivative in the canopy transpiration flux w.r.t. canopy temperature + real(dp) :: dLatHeatCanopyTrans_dTGround ! derivative in the canopy transpiration flux w.r.t. ground temperature + ! local variables -- wetted fraction derivatives + real(dp) :: dLatHeatCanopyEvap_dWetFrac ! derivative in the latent heat of canopy evaporation w.r.t. canopy wet fraction (W m-2) + real(dp) :: dLatHeatCanopyTrans_dWetFrac ! derivative in the latent heat of canopy transpiration w.r.t. canopy wet fraction (W m-2) + real(dp) :: dLatHeatCanopyTrans_dCanLiq ! derivative in the latent heat of canopy transpiration w.r.t. canopy liquid water (J kg-1 s-1) + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='turbFluxes/' + + ! compute constants + volHeatCapacityAir = iden_air*cp_air ! volumetric heat capacity of air (J m-3) + latentHeatConstant = iden_air*w_ratio/airpres ! latent heat constant for (kg m-3 Pa-1) + + ! ***** + ! * compute conductances, and derivatives... + ! ****************************************** + + ! compute conductances for sensible heat (m s-1) + if(computeVegFlux)then + leafConductance = exposedVAI/leafResistance + leafConductanceTr = canopySunlitLAI/(leafResistance+stomResistSunlit) + canopyShadedLAI/(leafResistance+stomResistShaded) + canopyConductance = 1._dp/canopyResistance + else + leafConductance = 0._dp + canopyConductance = 0._dp + end if + groundConductanceSH = 1._dp/groundResistance + + ! compute total conductance for sensible heat + totalConductanceSH = leafConductance + groundConductanceSH + canopyConductance + + ! compute conductances for latent heat (m s-1) + if(computeVegFlux)then + evapConductance = canopyWetFraction*leafConductance + transConductance = (1._dp - canopyWetFraction) * leafConductanceTr + !write(*,'(a,10(f14.8,1x))') 'canopySunlitLAI, canopyShadedLAI, stomResistSunlit, stomResistShaded, leafResistance, canopyWetFraction = ', & + ! canopySunlitLAI, canopyShadedLAI, stomResistSunlit, stomResistShaded, leafResistance, canopyWetFraction + else + evapConductance = 0._dp + transConductance = 0._dp + end if + groundConductanceLH = 1._dp/(groundResistance + soilResistance) ! NOTE: soilResistance accounts for fractional snow, and =0 when snow cover is 100% + totalConductanceLH = evapConductance + transConductance + groundConductanceLH + canopyConductance + + ! check sensible heat conductance + if(totalConductanceSH < -tinyVal .or. groundConductanceSH < -tinyVal .or. canopyConductance < -tinyVal)then + message=trim(message)//'negative conductance for sensible heat' + err=20; return + endif + + ! check latent heat conductance + if(totalConductanceLH < tinyVal .or. groundConductanceLH < -tinyVal)then + message=trim(message)//'negative conductance for latent heat' + err=20; return + endif + + ! * compute derivatives + ! NOTE: it may be more efficient to compute these derivatives when computing resistances + if(ixDerivMethod == analytical)then + + ! compute derivatives in individual conductances for sensible heat w.r.t. canopy temperature (m s-1 K-1) + if(computeVegFlux)then + dEvapCond_dCanopyTemp = dCanopyWetFraction_dT*leafConductance ! derivative in evap conductance w.r.t. canopy temperature + dTransCond_dCanopyTemp = -dCanopyWetFraction_dT*leafConductanceTr ! derivative in trans conductance w.r.t. canopy temperature + dCanopyCond_dCanairTemp = -dCanopyResistance_dTCanair/canopyResistance**2._dp ! derivative in canopy conductance w.r.t. canopy air emperature + dCanopyCond_dCanopyTemp = -dCanopyResistance_dTCanopy/canopyResistance**2._dp ! derivative in canopy conductance w.r.t. canopy temperature + dGroundCondSH_dCanairTemp = -dGroundResistance_dTCanair/groundResistance**2._dp ! derivative in ground conductance w.r.t. canopy air temperature + dGroundCondSH_dCanopyTemp = -dGroundResistance_dTCanopy/groundResistance**2._dp ! derivative in ground conductance w.r.t. canopy temperature + dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2._dp ! derivative in ground conductance w.r.t. ground temperature + else + dEvapCond_dCanopyTemp = 0._dp ! derivative in evap conductance w.r.t. canopy temperature + dTransCond_dCanopyTemp = 0._dp ! derivative in trans conductance w.r.t. canopy temperature + dCanopyCond_dCanairTemp = 0._dp ! derivative in canopy conductance w.r.t. canopy air emperature + dCanopyCond_dCanopyTemp = 0._dp ! derivative in canopy conductance w.r.t. canopy temperature + dGroundCondSH_dCanairTemp = 0._dp ! derivative in ground conductance w.r.t. canopy air temperature + dGroundCondSH_dCanopyTemp = 0._dp ! derivative in ground conductance w.r.t. canopy temperature + dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2._dp ! derivative in ground conductance w.r.t. ground temperature + end if + + ! compute derivatives in individual conductances for latent heat w.r.t. canopy temperature (m s-1 K-1) + if(computeVegFlux)then + dGroundCondLH_dCanairTemp = -dGroundResistance_dTCanair/(groundResistance+soilResistance)**2._dp ! derivative in ground conductance w.r.t. canopy air temperature + dGroundCondLH_dCanopyTemp = -dGroundResistance_dTCanopy/(groundResistance+soilResistance)**2._dp ! derivative in ground conductance w.r.t. canopy temperature + dGroundCondLH_dGroundTemp = -dGroundResistance_dTGround/(groundResistance+soilResistance)**2._dp ! derivative in ground conductance w.r.t. ground temperature + else + dGroundCondLH_dCanairTemp = 0._dp ! derivative in ground conductance w.r.t. canopy air temperature + dGroundCondLH_dCanopyTemp = 0._dp ! derivative in ground conductance w.r.t. canopy temperature + dGroundCondLH_dGroundTemp = -dGroundResistance_dTGround/(groundResistance+soilResistance)**2._dp ! derivative in ground conductance w.r.t. ground temperature + end if + + end if ! (if computing analytical derivatives) + + ! ***** + ! * compute sensible and latent heat fluxes, and derivatives... + ! ************************************************************* + + ! * compute sensible and latent heat fluxes from the canopy to the canopy air space (W m-2) + if(computeVegFlux)then + + ! compute the vapor pressure in the canopy air space (Pa) + fPart_VP = canopyConductance*VPair + (evapConductance + transConductance)*satVP_CanopyTemp + groundConductanceLH*satVP_GroundTemp*soilRelHumidity + VP_CanopyAir = fPart_VP/totalConductanceLH + !write(*,'(a,10(f20.10,1x))') 'canopyConductance, evapConductance, transConductance, groundConductanceLH, soilRelHumidity = ', & + ! canopyConductance, evapConductance, transConductance, groundConductanceLH, soilRelHumidity + + ! compute sensible heat flux from the canopy air space to the atmosphere + ! NOTE: canairTemp is a state variable + senHeatTotal = -volHeatCapacityAir*canopyConductance*(canairTemp - airtemp) + !print*, 'canairTemp, airtemp, senHeatTotal = ', canairTemp, airtemp, senHeatTotal + + ! compute fluxes + senHeatCanopy = -volHeatCapacityAir*leafConductance*(canopyTemp - canairTemp) ! (positive downwards) + latHeatCanopyEvap = -latHeatSubVapCanopy*latentHeatConstant*evapConductance*(satVP_CanopyTemp - VP_CanopyAir) ! (positive downwards) + latHeatCanopyTrans = -LH_vap*latentHeatConstant*transConductance*(satVP_CanopyTemp - VP_CanopyAir) ! (positive downwards) + !write(*,'(a,10(f25.15,1x))') 'latHeatCanopyEvap, VP_CanopyAir = ', latHeatCanopyEvap, VP_CanopyAir + !write(*,'(a,10(f25.15,1x))') 'latHeatCanopyTrans, VP_CanopyAir = ', latHeatCanopyTrans, VP_CanopyAir + !write(*,'(a,10(f25.15,1x))') 'transConductance = ', transConductance + + ! check that energy for canopy evaporation does not exhaust the available water + ! NOTE: do this here, rather than enforcing solution constraints, because energy and mass solutions may be uncoupled + !if(latHeatSubVapCanopy > LH_vap+verySmall)then ! (sublimation) + ! maxFlux = -canopyIce*LH_sub/dt ! W m-2 + !else ! (evaporation) + ! maxFlux = -canopyLiquid*LH_vap/dt ! W m-2 + !end if + ! NOTE: fluxes are positive downwards + !if(latHeatCanopyEvap < maxFlux) latHeatCanopyEvap = maxFlux + !write(*,'(a,10(f20.10,1x))') 'maxFlux, latHeatCanopyEvap = ', maxFlux, latHeatCanopyEvap + + ! * no vegetation, so fluxes are zero + else + senHeatCanopy = 0._dp + latHeatCanopyEvap = 0._dp + latHeatCanopyTrans = 0._dp + end if + + ! compute sensible and latent heat fluxes from the ground to the canopy air space (W m-2) + if(computeVegFlux)then + senHeatGround = -volHeatCapacityAir*groundConductanceSH*(groundTemp - canairTemp) ! (positive downwards) + latHeatGround = -latHeatSubVapGround*latentHeatConstant*groundConductanceLH*(satVP_GroundTemp*soilRelHumidity - VP_CanopyAir) ! (positive downwards) + else + senHeatGround = -volHeatCapacityAir*groundConductanceSH*(groundTemp - airtemp) ! (positive downwards) + latHeatGround = -latHeatSubVapGround*latentHeatConstant*groundConductanceLH*(satVP_GroundTemp*soilRelHumidity - VPair) ! (positive downwards) + senHeatTotal = senHeatGround + end if + !write(*,'(a,10(f25.15,1x))') 'latHeatGround = ', latHeatGround + + ! compute latent heat flux from the canopy air space to the atmosphere + ! NOTE: VP_CanopyAir is a diagnostic variable + latHeatTotal = latHeatCanopyEvap + latHeatCanopyTrans + latHeatGround + + ! * compute derivatives + if(ixDerivMethod == analytical)then + + ! differentiate CANOPY fluxes + if(computeVegFlux)then + + ! compute derivatives of vapor pressure in the canopy air space w.r.t. all state variables + ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the canopy air space) + dPart1 = dCanopyCond_dCanairTemp*VPair + dGroundCondLH_dCanairTemp*satVP_GroundTemp*soilRelHumidity + dPart2 = -(dCanopyCond_dCanairTemp + dGroundCondLH_dCanairTemp)/(totalConductanceLH**2._dp) + dVPCanopyAir_dTCanair = dPart1/totalConductanceLH + fPart_VP*dPart2 + ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the canopy) + dPart0 = (evapConductance + transConductance)*dSVPCanopy_dCanopyTemp + (dEvapCond_dCanopyTemp + dTransCond_dCanopyTemp)*satVP_CanopyTemp + dPart1 = dCanopyCond_dCanopyTemp*VPair + dPart0 + dGroundCondLH_dCanopyTemp*satVP_GroundTemp*soilRelHumidity + dPart2 = -(dCanopyCond_dCanopyTemp + dEvapCond_dCanopyTemp + dTransCond_dCanopyTemp + dGroundCondLH_dCanopyTemp)/(totalConductanceLH**2._dp) + dVPCanopyAir_dTCanopy = dPart1/totalConductanceLH + fPart_VP*dPart2 + ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the ground) + dPart1 = dGroundCondLH_dGroundTemp*satVP_GroundTemp*soilRelHumidity + groundConductanceLH*dSVPGround_dGroundTemp*soilRelHumidity + dPart2 = -dGroundCondLH_dGroundTemp/(totalConductanceLH**2._dp) + dVPCanopyAir_dTGround = dPart1/totalConductanceLH + fPart_VP*dPart2 + ! (derivative of vapor pressure in the canopy air space w.r.t. wetted fraction of the canopy) + dPart1 = (leafConductance - leafConductanceTr)*satVP_CanopyTemp + dPart2 = -(leafConductance - leafConductanceTr)/(totalConductanceLH**2._dp) + dVPCanopyAir_dWetFrac = dPart1/totalConductanceLH + fPart_VP*dPart2 + dVPCanopyAir_dCanLiq = dVPCanopyAir_dWetFrac*dCanopyWetFraction_dWat + !write(*,'(a,5(f20.8,1x))') 'dVPCanopyAir_dTCanair, dVPCanopyAir_dTCanopy, dVPCanopyAir_dTGround, dVPCanopyAir_dWetFrac, dVPCanopyAir_dCanLiq = ', & + ! dVPCanopyAir_dTCanair, dVPCanopyAir_dTCanopy, dVPCanopyAir_dTGround, dVPCanopyAir_dWetFrac, dVPCanopyAir_dCanLiq + + ! sensible heat from the canopy to the atmosphere + dSenHeatTotal_dTCanair = -volHeatCapacityAir*canopyConductance - volHeatCapacityAir*dCanopyCond_dCanairTemp*(canairTemp - airtemp) + dSenHeatTotal_dTCanopy = -volHeatCapacityAir*dCanopyCond_dCanopyTemp*(canairTemp - airtemp) + dSenHeatTotal_dTGround = 0._dp + !write(*,'(a,3(f20.8,1x))') 'dSenHeatTotal_dTCanair, dSenHeatTotal_dTCanopy, dSenHeatTotal_dTGround = ', & + ! dSenHeatTotal_dTCanair, dSenHeatTotal_dTCanopy, dSenHeatTotal_dTGround + + ! sensible heat from the canopy to the canopy air space + dSenHeatCanopy_dTCanair = volHeatCapacityAir*leafConductance + dSenHeatCanopy_dTCanopy = -volHeatCapacityAir*leafConductance + dSenHeatCanopy_dTGround = 0._dp + !write(*,'(a,3(f20.8,1x))') 'dSenHeatCanopy_dTCanair, dSenHeatCanopy_dTCanopy, dSenHeatCanopy_dTGround = ', & + ! dSenHeatCanopy_dTCanair, dSenHeatCanopy_dTCanopy, dSenHeatCanopy_dTGround + + ! sensible heat from the ground to the canopy air space + dSenHeatGround_dTCanair = -volHeatCapacityAir*dGroundCondSH_dCanairTemp*(groundTemp - canairTemp) + volHeatCapacityAir*groundConductanceSH + dSenHeatGround_dTCanopy = -volHeatCapacityAir*dGroundCondSH_dCanopyTemp*(groundTemp - canairTemp) + dSenHeatGround_dTGround = -volHeatCapacityAir*dGroundCondSH_dGroundTemp*(groundTemp - canairTemp) - volHeatCapacityAir*groundConductanceSH + !write(*,'(a,3(f20.8,1x))') 'dSenHeatGround_dTCanair, dSenHeatGround_dTCanopy, dSenHeatGround_dTGround = ', & + ! dSenHeatGround_dTCanair, dSenHeatGround_dTCanopy, dSenHeatGround_dTGround + + ! latent heat associated with canopy evaporation + ! (initial calculations) + fPart1 = -latHeatSubVapCanopy*latentHeatConstant*evapConductance + dPart1 = -latHeatSubVapCanopy*latentHeatConstant*dEvapCond_dCanopyTemp + fPart2 = satVP_CanopyTemp - VP_CanopyAir + dPart2 = dSVPCanopy_dCanopyTemp - dVPCanopyAir_dTCanopy + ! (derivatives) + dLatHeatCanopyEvap_dTCanair = fPart1*(-dVPCanopyAir_dTCanair) + dLatHeatCanopyEvap_dTCanopy = fPart1*dpart2 + fPart2*dPart1 + dLatHeatCanopyEvap_dTGround = fPart1*(-dVPCanopyAir_dTGround) + !write(*,'(a,3(f20.8,1x))') 'dLatHeatCanopyEvap_dTCanair, dLatHeatCanopyEvap_dTCanopy, dLatHeatCanopyEvap_dTGround = ', & + ! dLatHeatCanopyEvap_dTCanair, dLatHeatCanopyEvap_dTCanopy, dLatHeatCanopyEvap_dTGround + + ! latent heat associated with canopy transpiration + ! (initial calculations) + fPart1 = -LH_vap*latentHeatConstant*transConductance + dPart1 = -LH_vap*latentHeatConstant*dTransCond_dCanopyTemp + ! (derivatives) + dLatHeatCanopyTrans_dTCanair = fPart1*(-dVPCanopyAir_dTCanair) + dLatHeatCanopyTrans_dTCanopy = fPart1*dPart2 + fPart2*dPart1 + dLatHeatCanopyTrans_dTGround = fPart1*(-dVPCanopyAir_dTGround) + !write(*,'(a,3(f20.8,1x))') 'dLatHeatCanopyTrans_dTCanair, dLatHeatCanopyTrans_dTCanopy, dLatHeatCanopyTrans_dTGround = ', & + ! dLatHeatCanopyTrans_dTCanair, dLatHeatCanopyTrans_dTCanopy, dLatHeatCanopyTrans_dTGround + + ! latent heat flux from the ground + fPart1 = -latHeatSubVapGround*latentHeatConstant*groundConductanceLH ! function of the first part + fPart2 = (satVP_GroundTemp*soilRelHumidity - VP_CanopyAir) ! function of the second part + dLatHeatGroundEvap_dTCanair = -latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dCanairTemp*fPart2 - dVPCanopyAir_dTCanair*fPart1 + dLatHeatGroundEvap_dTCanopy = -latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dCanopyTemp*fPart2 - dVPCanopyAir_dTCanopy*fPart1 + dLatHeatGroundEvap_dTGround = -latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dGroundTemp*fPart2 + (dSVPGround_dGroundTemp*soilRelHumidity - dVPCanopyAir_dTGround)*fPart1 + !write(*,'(a,3(f20.8,1x))') 'dLatHeatGroundEvap_dTCanair, dLatHeatGroundEvap_dTCanopy, dLatHeatGroundEvap_dTGround = ', & + ! dLatHeatGroundEvap_dTCanair, dLatHeatGroundEvap_dTCanopy, dLatHeatGroundEvap_dTGround + + ! latent heat associated with canopy evaporation w.r.t. wetted fraction of the canopy + dPart1 = -latHeatSubVapCanopy*latentHeatConstant*leafConductance + fPart1 = dPart1*canopyWetFraction + dLatHeatCanopyEvap_dWetFrac = dPart1*(satVP_CanopyTemp - VP_CanopyAir) + fPart1*(-dVPCanopyAir_dWetFrac) + + ! latent heat associated with canopy transpiration w.r.t. wetted fraction of the canopy + dPart1 = LH_vap*latentHeatConstant*leafConductanceTr ! NOTE: positive, since (1 - wetFrac) + fPart1 = -dPart1*(1._dp - canopyWetFraction) + dLatHeatCanopyTrans_dWetFrac = dPart1*(satVP_CanopyTemp - VP_CanopyAir) + fPart1*(-dVPCanopyAir_dWetFrac) + !print*, 'dLatHeatCanopyTrans_dWetFrac = ', dLatHeatCanopyTrans_dWetFrac + + ! latent heat associated with canopy transpiration w.r.t. canopy liquid water + dLatHeatCanopyTrans_dCanLiq = dLatHeatCanopyTrans_dWetFrac*dCanopyWetFraction_dWat ! (J s-1 kg-1) + !print*, 'dLatHeatCanopyTrans_dCanLiq = ', dLatHeatCanopyTrans_dCanLiq + + else ! canopy is undefined + + ! set derivatives for canopy fluxes to zero (no canopy, so fluxes are undefined) + dSenHeatTotal_dTCanair = 0._dp + dSenHeatTotal_dTCanopy = 0._dp + dSenHeatTotal_dTGround = 0._dp + dSenHeatCanopy_dTCanair = 0._dp + dSenHeatCanopy_dTCanopy = 0._dp + dSenHeatCanopy_dTGround = 0._dp + dLatHeatCanopyEvap_dTCanair = 0._dp + dLatHeatCanopyEvap_dTCanopy = 0._dp + dLatHeatCanopyEvap_dTGround = 0._dp + dLatHeatCanopyTrans_dTCanair = 0._dp + dLatHeatCanopyTrans_dTCanopy = 0._dp + dLatHeatCanopyTrans_dTGround = 0._dp + + ! set derivatives for wetted area and canopy transpiration to zero (no canopy, so fluxes are undefined) + dLatHeatCanopyEvap_dWetFrac = 0._dp + dLatHeatCanopyEvap_dCanLiq = 0._dp + dLatHeatCanopyTrans_dCanLiq = 0._dp + dVPCanopyAir_dCanLiq = 0._dp + + ! set derivatives for ground fluxes w.r.t canopy temperature to zero (no canopy, so fluxes are undefined) + dSenHeatGround_dTCanair = 0._dp + dSenHeatGround_dTCanopy = 0._dp + dLatHeatGroundEvap_dTCanair = 0._dp + dLatHeatGroundEvap_dTCanopy = 0._dp + + ! compute derivatives for the ground fluxes w.r.t. ground temperature + dSenHeatGround_dTGround = (-volHeatCapacityAir*dGroundCondSH_dGroundTemp)*(groundTemp - airtemp) + & ! d(ground sensible heat flux)/d(ground temp) + (-volHeatCapacityAir*groundConductanceSH) + dLatHeatGroundEvap_dTGround = (-latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dGroundTemp)*(satVP_GroundTemp*soilRelHumidity - VPair) + & ! d(ground latent heat flux)/d(ground temp) + (-latHeatSubVapGround*latentHeatConstant*groundConductanceLH)*dSVPGround_dGroundTemp*soilRelHumidity + + !print*, 'dGroundCondLH_dGroundTemp = ', dGroundCondLH_dGroundTemp + + end if ! (if canopy is defined) + + end if ! (if computing analytical derivatives) + + + ! ***** + ! * compute net turbulent fluxes, and derivatives... + ! ************************************************** + + ! compute net fluxes + turbFluxCanair = senHeatTotal - senHeatCanopy - senHeatGround ! net turbulent flux at the canopy air space (W m-2) + turbFluxCanopy = senHeatCanopy + latHeatCanopyEvap + latHeatCanopyTrans ! net turbulent flux at the canopy (W m-2) + turbFluxGround = senHeatGround + latHeatGround ! net turbulent flux at the ground surface (W m-2) + !write(*,'(a,1x,3(f20.10,1x))') 'senHeatCanopy, latHeatCanopyEvap, latHeatCanopyTrans = ', senHeatCanopy, latHeatCanopyEvap, latHeatCanopyTrans + + ! * compute derivatives + if(ixDerivMethod == analytical)then + ! (energy derivatives) + dTurbFluxCanair_dTCanair = dSenHeatTotal_dTCanair - dSenHeatCanopy_dTCanair - dSenHeatGround_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxCanair_dTCanopy = dSenHeatTotal_dTCanopy - dSenHeatCanopy_dTCanopy - dSenHeatGround_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxCanair_dTGround = dSenHeatTotal_dTGround - dSenHeatCanopy_dTGround - dSenHeatGround_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanair = dSenHeatCanopy_dTCanair + dLatHeatCanopyEvap_dTCanair + dLatHeatCanopyTrans_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanopy = dSenHeatCanopy_dTCanopy + dLatHeatCanopyEvap_dTCanopy + dLatHeatCanopyTrans_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxCanopy_dTGround = dSenHeatCanopy_dTGround + dLatHeatCanopyEvap_dTGround + dLatHeatCanopyTrans_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxGround_dTCanair = dSenHeatGround_dTCanair + dLatHeatGroundEvap_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxGround_dTCanopy = dSenHeatGround_dTCanopy + dLatHeatGroundEvap_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxGround_dTGround = dSenHeatGround_dTGround + dLatHeatGroundEvap_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + ! (liquid water derivatives) + dLatHeatCanopyEvap_dCanLiq = dLatHeatCanopyEvap_dWetFrac*dCanopyWetFraction_dWat ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water (W kg-1) + dLatHeatGroundEvap_dCanLiq = latHeatSubVapGround*latentHeatConstant*groundConductanceLH*dVPCanopyAir_dCanLiq ! derivative in latent heat of ground evaporation w.r.t. canopy liquid water (J kg-1 s-1) + ! (cross deriavtives) + dTurbFluxCanair_dCanLiq = 0._dp ! derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dTurbFluxCanopy_dCanLiq = dLatHeatCanopyEvap_dCanLiq + dLatHeatCanopyTrans_dCanLiq ! derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dTurbFluxGround_dCanLiq = dLatHeatGroundEvap_dCanLiq ! derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + else ! (just make sure we return something) + ! (energy derivatives) + dTurbFluxCanair_dTCanair = 0._dp + dTurbFluxCanair_dTCanopy = 0._dp + dTurbFluxCanair_dTGround = 0._dp + dTurbFluxCanopy_dTCanair = 0._dp + dTurbFluxCanopy_dTCanopy = 0._dp + dTurbFluxCanopy_dTGround = 0._dp + dTurbFluxGround_dTCanair = 0._dp + dTurbFluxGround_dTCanopy = 0._dp + dTurbFluxGround_dTGround = 0._dp + ! (liquid water derivatives) + dLatHeatCanopyEvap_dCanLiq = 0._dp + dLatHeatGroundEvap_dCanLiq = 0._dp + ! (cross deriavtives) + dTurbFluxCanair_dCanLiq = 0._dp + dTurbFluxCanopy_dCanLiq = 0._dp + dTurbFluxGround_dCanLiq = 0._dp + end if + + end subroutine turbFluxes + + + ! ******************************************************************************************************* + ! private subroutine aStability: compute stability corrections for turbulent heat fluxes (-) + ! ******************************************************************************************************* + subroutine aStability(& + ! input: control + computeDerivative, & ! input: logical flag to compute analytical derivatives + ixStability, & ! input: choice of stability function + ! input: forcing data, diagnostic and state variables + mHeight, & ! input: measurement height (m) + airTemp, & ! input: air temperature (K) + sfcTemp, & ! input: surface temperature (K) + windspd, & ! input: wind speed (m s-1) + ! input: stability parameters + critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam, & ! input: parameter in Louis (1979) stability function + Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function + ! output + RiBulk, & ! output: bulk Richardson number (-) + stabilityCorrection, & ! output: stability correction for turbulent heat fluxes (-) + dStabilityCorrection_dRich, & ! output: derivative in stability correction w.r.t. Richardson number (-) + dStabilityCorrection_dAirTemp, & ! output: derivative in stability correction w.r.t. temperature (K-1) + dStabilityCorrection_dSfcTemp, & ! output: derivative in stability correction w.r.t. temperature (K-1) + err, message ) ! output: error control + implicit none + ! input: control + logical(lgt),intent(in) :: computeDerivative ! flag to compute the derivative + integer(i4b),intent(in) :: ixStability ! choice of stability function + ! input: forcing data, diagnostic and state variables + real(dp),intent(in) :: mHeight ! measurement height (m) + real(dp),intent(in) :: airtemp ! air temperature (K) + real(dp),intent(in) :: sfcTemp ! surface temperature (K) + real(dp),intent(in) :: windspd ! wind speed (m s-1) + ! input: stability parameters + real(dp),intent(in) :: critRichNumber ! critical value for the bulk Richardson number where turbulence ceases (-) + real(dp),intent(in) :: Louis79_bparam ! parameter in Louis (1979) stability function + real(dp),intent(in) :: Mahrt87_eScale ! exponential scaling factor in the Mahrt (1987) stability function + ! output + real(dp),intent(out) :: RiBulk ! bulk Richardson number (-) + real(dp),intent(out) :: stabilityCorrection ! stability correction for turbulent heat fluxes (-) + real(dp),intent(out) :: dStabilityCorrection_dRich ! derivative in stability correction w.r.t. Richardson number (-) + real(dp),intent(out) :: dStabilityCorrection_dAirTemp ! derivative in stability correction w.r.t. air temperature (K-1) + real(dp),intent(out) :: dStabilityCorrection_dSfcTemp ! derivative in stability correction w.r.t. surface temperature (K-1) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local + real(dp), parameter :: verySmall=1.e-10_dp ! a very small number (avoid stability of zero) + real(dp) :: dRiBulk_dAirTemp ! derivative in the bulk Richardson number w.r.t. air temperature (K-1) + real(dp) :: dRiBulk_dSfcTemp ! derivative in the bulk Richardson number w.r.t. surface temperature (K-1) + real(dp) :: bPrime ! scaled "b" parameter for stability calculations in Louis (1979) + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='aStability/' + + ! compute the bulk Richardson number (-) + call bulkRichardson(& + ! input + airTemp, & ! input: air temperature (K) + sfcTemp, & ! input: surface temperature (K) + windspd, & ! input: wind speed (m s-1) + mHeight, & ! input: measurement height (m) + computeDerivative, & ! input: flag to compute the derivative + ! output + RiBulk, & ! output: bulk Richardson number (-) + dRiBulk_dAirTemp, & ! output: derivative in the bulk Richardson number w.r.t. air temperature (K-1) + dRiBulk_dSfcTemp, & ! output: derivative in the bulk Richardson number w.r.t. surface temperature (K-1) + err,message) ! output: error control + + ! set derivative to one if not computing it + if(.not.computeDerivative)then + dStabilityCorrection_dRich = 1._dp + dStabilityCorrection_dAirTemp = 1._dp + dStabilityCorrection_dSfcTemp = 1._dp + end if + + ! ***** process unstable cases + if(RiBulk<0._dp)then + ! compute surface-atmosphere exchange coefficient (-) + stabilityCorrection = (1._dp - 16._dp*RiBulk)**0.5_dp + ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) + if(computeDerivative)then + dStabilityCorrection_dRich = (-16._dp) * 0.5_dp*(1._dp - 16._dp*RiBulk)**(-0.5_dp) + dStabilityCorrection_dAirTemp = dRiBulk_dAirTemp * dStabilityCorrection_dRich + dStabilityCorrection_dSfcTemp = dRiBulk_dSfcTemp * dStabilityCorrection_dRich + end if + return + end if + + ! ***** process stable cases + select case(ixStability) + + ! ("standard" stability correction, a la Anderson 1976) + case(standard) + ! compute surface-atmosphere exchange coefficient (-) + if(RiBulk < critRichNumber) stabilityCorrection = (1._dp - 5._dp*RiBulk)**2._dp + if(RiBulk >= critRichNumber) stabilityCorrection = verySmall + ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) + if(computeDerivative)then + if(RiBulk < critRichNumber) dStabilityCorrection_dRich = (-5._dp) * 2._dp*(1._dp - 5._dp*RiBulk) + if(RiBulk >= critRichNumber) dStabilityCorrection_dRich = verySmall + end if + + ! (Louis 1979) + case(louisInversePower) + ! scale the "b" parameter for stable conditions + bprime = Louis79_bparam/2._dp + ! compute surface-atmosphere exchange coefficient (-) + stabilityCorrection = 1._dp / ( (1._dp + bprime*RiBulk)**2._dp ) + if(stabilityCorrection < epsilon(stabilityCorrection)) stabilityCorrection = epsilon(stabilityCorrection) + ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) + if(computeDerivative)then + dStabilityCorrection_dRich = bprime * (-2._dp)*(1._dp + bprime*RiBulk)**(-3._dp) + end if + + ! (Mahrt 1987) + case(mahrtExponential) + ! compute surface-atmosphere exchange coefficient (-) + stabilityCorrection = exp(-Mahrt87_eScale * RiBulk) + if(stabilityCorrection < epsilon(stabilityCorrection)) stabilityCorrection = epsilon(stabilityCorrection) + ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) + if(computeDerivative)then + dStabilityCorrection_dRich = (-Mahrt87_eScale) * exp(-Mahrt87_eScale * RiBulk) + end if + + ! (return error if the stability correction method is not found) + case default + err=10; message=trim(message)//"optionNotFound[stability correction]"; return + + end select + + ! get the stability correction with respect to air temperature and surface temperature + ! NOTE: air temperature is used for canopy air temperature, which is a model state variable + if(computeDerivative)then + dStabilityCorrection_dAirTemp = dRiBulk_dAirTemp * dStabilityCorrection_dRich + dStabilityCorrection_dSfcTemp = dRiBulk_dSfcTemp * dStabilityCorrection_dRich + end if + + end subroutine aStability + + + ! ******************************************************************************************************* + ! private subroutine bulkRichardson: compute bulk Richardson number + ! ******************************************************************************************************* + subroutine bulkRichardson(& + ! input + airTemp, & ! input: air temperature (K) + sfcTemp, & ! input: surface temperature (K) + windspd, & ! input: wind speed (m s-1) + mHeight, & ! input: measurement height (m) + computeDerivative, & ! input: flag to compute the derivative + ! output + RiBulk, & ! output: bulk Richardson number (-) + dRiBulk_dAirTemp, & ! output: derivative in the bulk Richardson number w.r.t. air temperature (K-1) + dRiBulk_dSfcTemp, & ! output: derivative in the bulk Richardson number w.r.t. surface temperature (K-1) + err,message) ! output: error control + implicit none + ! input + real(dp),intent(in) :: airtemp ! air temperature (K) + real(dp),intent(in) :: sfcTemp ! surface temperature (K) + real(dp),intent(in) :: windspd ! wind speed (m s-1) + real(dp),intent(in) :: mHeight ! measurement height (m) + logical(lgt),intent(in) :: computeDerivative ! flag to compute the derivative + ! output + real(dp),intent(inout) :: RiBulk ! bulk Richardson number (-) + real(dp),intent(out) :: dRiBulk_dAirTemp ! derivative in the bulk Richardson number w.r.t. air temperature (K-1) + real(dp),intent(out) :: dRiBulk_dSfcTemp ! derivative in the bulk Richardson number w.r.t. surface temperature (K-1) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local variables + real(dp) :: T_grad ! gradient in temperature between the atmosphere and surface (K) + real(dp) :: T_mean ! mean of the atmosphere and surface temperature (K) + real(dp) :: RiMult ! dimensionless scaling factor (-) + ! initialize error control + err=0; message='bulkRichardson/' + ! compute local variables + T_grad = airtemp - sfcTemp + T_mean = 0.5_dp*(airtemp + sfcTemp) + RiMult = (gravity*mHeight)/(windspd*windspd) + ! compute the Richardson number + RiBulk = (T_grad/T_mean) * RiMult + ! compute the derivative in the Richardson number + if(computeDerivative)then + dRiBulk_dAirTemp = RiMult/T_mean - RiMult*T_grad/(0.5_dp*((airtemp + sfcTemp)**2._dp)) + dRiBulk_dSfcTemp = -RiMult/T_mean - RiMult*T_grad/(0.5_dp*((airtemp + sfcTemp)**2._dp)) + else + dRiBulk_dAirTemp = 1._dp + dRiBulk_dSfcTemp = 1._dp + end if + end subroutine bulkRichardson + + +end module vegNrgFluxFida_module From 67ce63788ca93aebe026e9ad0581f8318614035b Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 7 May 2021 11:35:19 -0600 Subject: [PATCH 0090/1472] mLayerMatricHead in vegNrgFluxFida --- build/Makefile | 1 + build/source/engine/computFluxFida.f90 | 5 +++-- build/source/engine/vegNrgFluxFida.f90 | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/build/Makefile b/build/Makefile index 8e9a86271..bd1336fb3 100644 --- a/build/Makefile +++ b/build/Makefile @@ -207,6 +207,7 @@ SUMMA_SOLVER= \ groundwatr.f90 \ vegSWavRad.f90 \ vegNrgFlux.f90 \ + vegNrgFluxFida.f90 \ ssdNrgFlux.f90 \ vegLiqFlux.f90 \ snowLiqFlx.f90 \ diff --git a/build/source/engine/computFluxFida.f90 b/build/source/engine/computFluxFida.f90 index 7819d261e..33c1da92c 100755 --- a/build/source/engine/computFluxFida.f90 +++ b/build/source/engine/computFluxFida.f90 @@ -146,7 +146,7 @@ subroutine computFluxFida(& ! output: error control err,message) ! intent(out): error code and error message ! provide access to flux subroutines - USE vegNrgFlux_module,only:vegNrgFlux ! compute energy fluxes over vegetation + USE vegNrgFluxFida_module,only:vegNrgFluxFida ! compute energy fluxes over vegetation USE ssdNrgFlux_module,only:ssdNrgFlux ! compute energy fluxes throughout the snow and soil subdomains USE vegLiqFlux_module,only:vegLiqFlux ! compute liquid water fluxes through vegetation USE snowLiqFlx_module,only:snowLiqFlxFida ! compute liquid water fluxes through snow @@ -407,7 +407,7 @@ subroutine computFluxFida(& dCanLiq_dTcanopy = dTheta_dTkCanopy*iden_water*canopyDepth ! kg m-2 K-1 ! calculate the energy fluxes over vegetation - call vegNrgFlux(& + call vegNrgFluxFida(& ! input: model control firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call @@ -419,6 +419,7 @@ subroutine computFluxFida(& mLayerTempTrial(1), & ! intent(in): trial value of ground temperature (K) scalarCanopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) scalarCanopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) + mLayerMatricHead, & ! intent(in) ! input: model derivatives dCanLiq_dTcanopy, & ! intent(in): derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) ! input/output: data structures diff --git a/build/source/engine/vegNrgFluxFida.f90 b/build/source/engine/vegNrgFluxFida.f90 index bd55c7347..c3649ac19 100755 --- a/build/source/engine/vegNrgFluxFida.f90 +++ b/build/source/engine/vegNrgFluxFida.f90 @@ -138,6 +138,7 @@ subroutine vegNrgFluxFida(& groundTempTrial, & ! intent(in): trial value of ground temperature (K) canopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) canopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) + mLayerMatricHead, & ! intent(in) ! input: model derivatives dCanLiq_dTcanopy, & ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) @@ -219,6 +220,7 @@ subroutine vegNrgFluxFida(& real(dp),intent(in) :: groundTempTrial ! trial value of ground temperature (K) real(dp),intent(in) :: canopyIceTrial ! trial value of mass of ice on the vegetation canopy (kg m-2) real(dp),intent(in) :: canopyLiqTrial ! trial value of mass of liquid water on the vegetation canopy (kg m-2) + real(dp),intent(in) :: mLayerMatricHead(:) ! ! input: model derivatives real(dp),intent(in) :: dCanLiq_dTcanopy ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) @@ -508,7 +510,6 @@ subroutine vegNrgFluxFida(& scalarSWE => prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(in): [dp] snow water equivalent on the ground (kg m-2) scalarSnowDepth => prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(in): [dp] snow depth on the ground surface (m) mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat, & ! intent(in): [dp(:)] volumetric fraction of liquid water in each layer (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat, & ! intent(in): [dp(:)] matric head in each soil layer (m) localAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1), & ! intent(in): [dp] aquifer storage for the local column (m) basinAquiferStorage => bvar_data%var(iLookBVAR%basin__AquiferStorage)%dat(1), & ! intent(in): [dp] aquifer storage for the single basin (m) From 371009e496ffd58748b8c6d913d5e4280c070f5c Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 7 May 2021 11:51:15 -0600 Subject: [PATCH 0091/1472] mLayerVolFracLiqPrev in eval8summaFida --- build/source/engine/eval8summaFida.f90 | 5 ++++- build/source/engine/evalEqnsFida.f90 | 2 ++ build/source/engine/fidaSolver.f90 | 10 +++++++++- build/source/engine/fida_datatypes.f90 | 4 +++- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 0e60231c4..6556ace75 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -135,6 +135,8 @@ subroutine eval8summaFida(& mLayerVolFracWatPrev, & !intent(inout) mLayerVolFracIceTrial, & !intent(inout) mLayerVolFracIcePrev, & !intent(in) + mLayerVolFracLiqTrial, & !intent(inout) + mLayerVolFracLiqPrev, & !intent(in) mLayerEnthalpyPrev, & ! intent(in) mLayerEnthalpyTrial, & ! intent(out) feasible, & ! intent(out): flag to denote the feasibility of the solution @@ -207,6 +209,8 @@ subroutine eval8summaFida(& real(dp),intent(in) :: mLayerVolFracWatPrev(:) real(dp),intent(inout) :: mLayerVolFracIceTrial(:) real(dp),intent(in) :: mLayerVolFracIcePrev(:) + real(dp),intent(inout) :: mLayerVolFracLiqTrial(:) + real(dp),intent(in) :: mLayerVolFracLiqPrev(:) real(dp),intent(in) :: mLayerEnthalpyPrev(:) real(dp),intent(out) :: mLayerEnthalpyTrial(:) logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution @@ -225,7 +229,6 @@ subroutine eval8summaFida(& real(dp) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) ! diagnostic variables real(dp) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(dp),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) ! derivative of state variables real(dp) :: scalarCanairTempPrime ! derivative value for temperature of the canopy air space (K) diff --git a/build/source/engine/evalEqnsFida.f90 b/build/source/engine/evalEqnsFida.f90 index a5c6aaad0..c5d8e20f3 100644 --- a/build/source/engine/evalEqnsFida.f90 +++ b/build/source/engine/evalEqnsFida.f90 @@ -143,6 +143,8 @@ integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_d eqns_data%mLayerVolFracWatPrev, & eqns_data%mLayerVolFracIceTrial, & eqns_data%mLayerVolFracIcePrev, & + eqns_data%mLayerVolFracLiqTrial, & + eqns_data%mLayerVolFracLiqPrev, & eqns_data%mLayerEnthalpyPrev, & ! intent(in) eqns_data%mLayerEnthalpyTrial, & ! intent(out) ! output diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 11ab8b280..bf22e6643 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -305,6 +305,9 @@ subroutine fidaSolver( & allocate( eqns_data%mLayerVolFracIceTrial(nLayers) ) allocate( eqns_data%mLayerVolFracIcePrev(nLayers) ) + allocate( eqns_data%mLayerVolFracLiqTrial(nLayers) ) + allocate( eqns_data%mLayerVolFracLiqPrev(nLayers) ) + allocate( eqns_data%mLayerEnthalpyTrial(nLayers) ) allocate( eqns_data%mLayerEnthalpyPrev(nLayers) ) @@ -401,7 +404,8 @@ subroutine fidaSolver( & eqns_data%scalarCanopyIcePrev = prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) - eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) + eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) + eqns_data%mLayerVolFracLiqPrev(:) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(:) eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) @@ -469,6 +473,8 @@ subroutine fidaSolver( & eqns_data%mLayerVolFracWatPrev, & eqns_data%mLayerVolFracIceTrial, & eqns_data%mLayerVolFracIcePrev, & + eqns_data%mLayerVolFracLiqTrial, & + eqns_data%mLayerVolFracLiqPrev, & eqns_data%mLayerEnthalpyPrev, & ! intent(in) eqns_data%mLayerEnthalpyTrial, & ! intent(out) ! output @@ -520,6 +526,7 @@ subroutine fidaSolver( & eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) + eqns_data%mLayerVolFracLiqPrev(:) = eqns_data%mLayerVolFracLiqTrial(:) eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial @@ -553,6 +560,7 @@ subroutine fidaSolver( & deallocate( eqns_data%mLayerTempPrev ) deallocate( eqns_data%mLayerTempTrial ) deallocate( eqns_data%mLayerVolFracIcePrev ) + deallocate( eqns_data%mLayerVolFracLiqPrev ) deallocate( eqns_data%mLayerEnthalpyTrial ) deallocate( eqns_data%mLayerEnthalpyPrev ) diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index 6df36d44a..b1f6b331e 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -50,7 +50,7 @@ module fida_datatypes real(dp), allocatable :: fluxVec(:) ! flux vector real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation real(dp), allocatable :: atol(:) ! vector of absolute tolerances - real(dp), allocatable :: rtol(:) ! vector of relative tolerances + real(dp), allocatable :: rtol(:) ! vector of relative tolerances real(dp), allocatable :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) real(dp), allocatable :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) real(dp), allocatable :: mLayerMatricHeadPrev(:) ! vector of total water matric potential (m) at previous step @@ -58,6 +58,8 @@ module fida_datatypes real(dp), allocatable :: mLayerVolFracWatPrev(:) ! value for volumetric fraction of total water (-) at previous step real(dp), allocatable :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) real(dp), allocatable :: mLayerVolFracIcePrev(:) ! value for volumetric fraction of ice (-) at previous step + real(dp), allocatable :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of ice (-) + real(dp), allocatable :: mLayerVolFracLiqPrev(:) ! value for volumetric fraction of ice (-) at previous step real(dp), allocatable :: mLayerEnthalpyTrial(:) ! trial enthalpy of snow and soil (J m-3) real(dp), allocatable :: mLayerEnthalpyPrev(:) ! enthalpy of snow and soil (J m-3) at previous step real(dp), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) From 8fcf056ec0cb4536d58646c3df434465cfdde081 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 7 May 2021 12:09:13 -0600 Subject: [PATCH 0092/1472] mLayerVolFracLiq in vegNrgFluxFida --- build/source/engine/computFluxFida.f90 | 3 +++ build/source/engine/eval8summaFida.f90 | 1 + build/source/engine/vegNrgFluxFida.f90 | 3 ++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/build/source/engine/computFluxFida.f90 b/build/source/engine/computFluxFida.f90 index 33c1da92c..50a9ac570 100755 --- a/build/source/engine/computFluxFida.f90 +++ b/build/source/engine/computFluxFida.f90 @@ -115,6 +115,7 @@ subroutine computFluxFida(& drainageMeltPond, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) ! input: state variables mLayerVolFracIce, & ! intent(in) + mLayerVolFracLiq, & ! intent(in) mLayerMatricHead, & ! intent(in) scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) @@ -169,6 +170,7 @@ subroutine computFluxFida(& real(dp),intent(in) :: drainageMeltPond ! drainage from the surface melt pond (kg m-2 s-1) ! input: state variables real(dp),intent(in) :: mLayerVolFracIce(:) ! + real(dp),intent(in) :: mLayerVolFracLiq(:) ! real(dp),intent(in) :: mLayerMatricHead(:) ! real(dp),intent(in) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) real(dp),intent(in) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) @@ -420,6 +422,7 @@ subroutine computFluxFida(& scalarCanopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) scalarCanopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) mLayerMatricHead, & ! intent(in) + mLayerVolFracLiq, & ! intent(in) ! input: model derivatives dCanLiq_dTcanopy, & ! intent(in): derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) ! input/output: data structures diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 6556ace75..96e9bc965 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -580,6 +580,7 @@ subroutine eval8summaFida(& ! input: state variables mLayerVolFracIcePrev, & ! intent(in) mLayerMatricHeadPrev, & ! intent(in) + mLayerVolFracLiqPrev, & ! intent(in) scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) diff --git a/build/source/engine/vegNrgFluxFida.f90 b/build/source/engine/vegNrgFluxFida.f90 index c3649ac19..e1b0d82a4 100755 --- a/build/source/engine/vegNrgFluxFida.f90 +++ b/build/source/engine/vegNrgFluxFida.f90 @@ -139,6 +139,7 @@ subroutine vegNrgFluxFida(& canopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) canopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) mLayerMatricHead, & ! intent(in) + mLayerVolFracLiq, & ! intent(in) ! input: model derivatives dCanLiq_dTcanopy, & ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) @@ -221,6 +222,7 @@ subroutine vegNrgFluxFida(& real(dp),intent(in) :: canopyIceTrial ! trial value of mass of ice on the vegetation canopy (kg m-2) real(dp),intent(in) :: canopyLiqTrial ! trial value of mass of liquid water on the vegetation canopy (kg m-2) real(dp),intent(in) :: mLayerMatricHead(:) ! + real(dp),intent(in) :: mLayerVolFracLiq(:) ! input: model derivatives real(dp),intent(in) :: dCanLiq_dTcanopy ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) @@ -509,7 +511,6 @@ subroutine vegNrgFluxFida(& ! NOTE: soil stress only computed at the start of the substep (firstFluxCall=.true.) scalarSWE => prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(in): [dp] snow water equivalent on the ground (kg m-2) scalarSnowDepth => prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(in): [dp] snow depth on the ground surface (m) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat, & ! intent(in): [dp(:)] volumetric fraction of liquid water in each layer (-) localAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1), & ! intent(in): [dp] aquifer storage for the local column (m) basinAquiferStorage => bvar_data%var(iLookBVAR%basin__AquiferStorage)%dat(1), & ! intent(in): [dp] aquifer storage for the single basin (m) From a848ba83933fb9b4421ce35f5ccafa7479da5473 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 7 May 2021 22:37:06 -0600 Subject: [PATCH 0093/1472] scalarAquiferStoragePrev in eval8summaFida and computFluxFida --- build/source/engine/computFluxFida.f90 | 2 ++ build/source/engine/eval8summaFida.f90 | 8 ++++++-- build/source/engine/evalEqnsFida.f90 | 2 ++ build/source/engine/fidaSolver.f90 | 6 +++++- build/source/engine/fida_datatypes.f90 | 6 ++++-- 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/build/source/engine/computFluxFida.f90 b/build/source/engine/computFluxFida.f90 index 50a9ac570..f8feca456 100755 --- a/build/source/engine/computFluxFida.f90 +++ b/build/source/engine/computFluxFida.f90 @@ -114,6 +114,7 @@ subroutine computFluxFida(& scalarSolution, & ! intent(in): flag to indicate the scalar solution drainageMeltPond, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) ! input: state variables + scalarAquiferStorage, & ! intent(in) mLayerVolFracIce, & ! intent(in) mLayerVolFracLiq, & ! intent(in) mLayerMatricHead, & ! intent(in) @@ -169,6 +170,7 @@ subroutine computFluxFida(& logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution real(dp),intent(in) :: drainageMeltPond ! drainage from the surface melt pond (kg m-2 s-1) ! input: state variables + real(dp),intent(in) :: scalarAquiferStorage real(dp),intent(in) :: mLayerVolFracIce(:) ! real(dp),intent(in) :: mLayerVolFracLiq(:) ! real(dp),intent(in) :: mLayerMatricHead(:) ! diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 96e9bc965..9b3511c0c 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -137,6 +137,8 @@ subroutine eval8summaFida(& mLayerVolFracIcePrev, & !intent(in) mLayerVolFracLiqTrial, & !intent(inout) mLayerVolFracLiqPrev, & !intent(in) + scalarAquiferStorageTrial, & + scalarAquiferStoragePrev, & mLayerEnthalpyPrev, & ! intent(in) mLayerEnthalpyTrial, & ! intent(out) feasible, & ! intent(out): flag to denote the feasibility of the solution @@ -211,6 +213,8 @@ subroutine eval8summaFida(& real(dp),intent(in) :: mLayerVolFracIcePrev(:) real(dp),intent(inout) :: mLayerVolFracLiqTrial(:) real(dp),intent(in) :: mLayerVolFracLiqPrev(:) + real(dp),intent(inout) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + real(dp),intent(in) :: scalarAquiferStoragePrev ! previous value of storage of water in the aquifer (m) real(dp),intent(in) :: mLayerEnthalpyPrev(:) real(dp),intent(out) :: mLayerEnthalpyTrial(:) logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution @@ -226,7 +230,6 @@ subroutine eval8summaFida(& ! state variables real(dp) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) real(dp) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(dp) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) ! diagnostic variables real(dp) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) @@ -578,9 +581,10 @@ subroutine eval8summaFida(& scalarSolution, & ! intent(in): flag to indicate the scalar solution scalarSfcMeltPond/dt, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) ! input: state variables + scalarAquiferStoragePrev, & ! intent(in) mLayerVolFracIcePrev, & ! intent(in) - mLayerMatricHeadPrev, & ! intent(in) mLayerVolFracLiqPrev, & ! intent(in) + mLayerMatricHeadPrev, & ! intent(in) scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) diff --git a/build/source/engine/evalEqnsFida.f90 b/build/source/engine/evalEqnsFida.f90 index c5d8e20f3..29776526b 100644 --- a/build/source/engine/evalEqnsFida.f90 +++ b/build/source/engine/evalEqnsFida.f90 @@ -145,6 +145,8 @@ integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_d eqns_data%mLayerVolFracIcePrev, & eqns_data%mLayerVolFracLiqTrial, & eqns_data%mLayerVolFracLiqPrev, & + eqns_data%scalarAquiferStorageTrial, & + eqns_data%scalarAquiferStoragePrev, & eqns_data%mLayerEnthalpyPrev, & ! intent(in) eqns_data%mLayerEnthalpyTrial, & ! intent(out) ! output diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index bf22e6643..61b05eadb 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -408,6 +408,7 @@ subroutine fidaSolver( & eqns_data%mLayerVolFracLiqPrev(:) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(:) eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) + eqns_data%scalarAquiferStoragePrev = prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) @@ -474,7 +475,9 @@ subroutine fidaSolver( & eqns_data%mLayerVolFracIceTrial, & eqns_data%mLayerVolFracIcePrev, & eqns_data%mLayerVolFracLiqTrial, & - eqns_data%mLayerVolFracLiqPrev, & + eqns_data%mLayerVolFracLiqPrev, & + eqns_data%scalarAquiferStorageTrial, & + eqns_data%scalarAquiferStoragePrev, & eqns_data%mLayerEnthalpyPrev, & ! intent(in) eqns_data%mLayerEnthalpyTrial, & ! intent(out) ! output @@ -527,6 +530,7 @@ subroutine fidaSolver( & eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) eqns_data%mLayerVolFracLiqPrev(:) = eqns_data%mLayerVolFracLiqTrial(:) + eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index b1f6b331e..f4d255967 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -58,8 +58,10 @@ module fida_datatypes real(dp), allocatable :: mLayerVolFracWatPrev(:) ! value for volumetric fraction of total water (-) at previous step real(dp), allocatable :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) real(dp), allocatable :: mLayerVolFracIcePrev(:) ! value for volumetric fraction of ice (-) at previous step - real(dp), allocatable :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of ice (-) - real(dp), allocatable :: mLayerVolFracLiqPrev(:) ! value for volumetric fraction of ice (-) at previous step + real(dp), allocatable :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) + real(dp), allocatable :: mLayerVolFracLiqPrev(:) ! value for volumetric fraction of liquid water (-) at previous step + real(dp) :: scalarAquiferStoragePrev + real(dp) :: scalarAquiferStorageTrial real(dp), allocatable :: mLayerEnthalpyTrial(:) ! trial enthalpy of snow and soil (J m-3) real(dp), allocatable :: mLayerEnthalpyPrev(:) ! enthalpy of snow and soil (J m-3) at previous step real(dp), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) From 10de2a33e41f2f56970ff335aadc085661da195f Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 7 May 2021 22:46:45 -0600 Subject: [PATCH 0094/1472] scalarAquiferStoragePrev in vegNrgFluxFida --- build/source/engine/computFluxFida.f90 | 1 + build/source/engine/eval8summaFida.f90 | 6 +++--- build/source/engine/vegNrgFluxFida.f90 | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/build/source/engine/computFluxFida.f90 b/build/source/engine/computFluxFida.f90 index f8feca456..c562981ec 100755 --- a/build/source/engine/computFluxFida.f90 +++ b/build/source/engine/computFluxFida.f90 @@ -423,6 +423,7 @@ subroutine computFluxFida(& mLayerTempTrial(1), & ! intent(in): trial value of ground temperature (K) scalarCanopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) scalarCanopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) + scalarAquiferStorage, & ! intent(in) mLayerMatricHead, & ! intent(in) mLayerVolFracLiq, & ! intent(in) ! input: model derivatives diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 9b3511c0c..cb2615be3 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -389,16 +389,16 @@ subroutine eval8summaFida(& scalarCanopyIceTrial = scalarCanopyIcePrev mLayerTempTrial = mLayerTempPrev mLayerVolFracWatTrial = mLayerVolFracWatPrev - !mLayerVolFracLiqTrial = mLayerVolFracLiqPrev + mLayerVolFracLiqTrial = mLayerVolFracLiqPrev mLayerVolFracIceTrial = mLayerVolFracIcePrev mLayerMatricHeadTrial = mLayerMatricHeadPrev ! total water matric potential !mLayerMatricHeadLiqTrial = mLayerMatricHeadLiqPrev ! liquid water matric potential - !scalarAquiferStorageTrial = scalarAquiferStoragePrev + scalarAquiferStorageTrial = scalarAquiferStoragePrev ! extract variables from the model state vector call varExtract(& ! input - stateVec, & ! intent(in): model state vector (mixed units) + stateVec, & ! intent(in): model state vector (mixed units) diag_data, & ! intent(in): model diagnostic variables for a local HRU prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers diff --git a/build/source/engine/vegNrgFluxFida.f90 b/build/source/engine/vegNrgFluxFida.f90 index e1b0d82a4..d76365100 100755 --- a/build/source/engine/vegNrgFluxFida.f90 +++ b/build/source/engine/vegNrgFluxFida.f90 @@ -138,6 +138,7 @@ subroutine vegNrgFluxFida(& groundTempTrial, & ! intent(in): trial value of ground temperature (K) canopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) canopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) + localAquiferStorage, & ! intent(in) mLayerMatricHead, & ! intent(in) mLayerVolFracLiq, & ! intent(in) @@ -221,6 +222,7 @@ subroutine vegNrgFluxFida(& real(dp),intent(in) :: groundTempTrial ! trial value of ground temperature (K) real(dp),intent(in) :: canopyIceTrial ! trial value of mass of ice on the vegetation canopy (kg m-2) real(dp),intent(in) :: canopyLiqTrial ! trial value of mass of liquid water on the vegetation canopy (kg m-2) + real(dp),intent(in) :: localAquiferStorage real(dp),intent(in) :: mLayerMatricHead(:) ! real(dp),intent(in) :: mLayerVolFracLiq(:) @@ -511,7 +513,6 @@ subroutine vegNrgFluxFida(& ! NOTE: soil stress only computed at the start of the substep (firstFluxCall=.true.) scalarSWE => prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(in): [dp] snow water equivalent on the ground (kg m-2) scalarSnowDepth => prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(in): [dp] snow depth on the ground surface (m) - localAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1), & ! intent(in): [dp] aquifer storage for the local column (m) basinAquiferStorage => bvar_data%var(iLookBVAR%basin__AquiferStorage)%dat(1), & ! intent(in): [dp] aquifer storage for the single basin (m) ! input: shortwave radiation fluxes From 221143321ca61a7ca07f049013be4116d6ef6763 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 7 May 2021 23:04:39 -0600 Subject: [PATCH 0095/1472] minor edit --- build/source/engine/fidaSolver.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 61b05eadb..8cff49156 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -407,8 +407,8 @@ subroutine fidaSolver( & eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) eqns_data%mLayerVolFracLiqPrev(:) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(:) eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) - eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) eqns_data%scalarAquiferStoragePrev = prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) + eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) @@ -418,7 +418,7 @@ subroutine fidaSolver( & !********************************************************************************** tret(1) = t0 do while(tret(1) < dt) - eqns_data%firstFluxCall = .true. + eqns_data%firstFluxCall = .false. ! call IDASolve retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) if( retval < 0 ) exit From 35b7ca9ea2123dd0b70e35f28d4d2f32201c26bb Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 7 May 2021 23:55:47 -0600 Subject: [PATCH 0096/1472] trial states out instead of inout --- build/source/engine/computFluxFida.f90 | 2 +- build/source/engine/eval8summaFida.f90 | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/build/source/engine/computFluxFida.f90 b/build/source/engine/computFluxFida.f90 index c562981ec..189459a02 100755 --- a/build/source/engine/computFluxFida.f90 +++ b/build/source/engine/computFluxFida.f90 @@ -660,7 +660,7 @@ subroutine computFluxFida(& ! input-output: data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model indices - prog_data, & ! intent(inout): model prognostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU ! output: diagnostic variables for surface runoff diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index cb2615be3..6e913172f 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -196,24 +196,24 @@ subroutine eval8summaFida(& ! input-output: baseflow real(dp),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors - real(dp),intent(inout) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(dp),intent(out) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) real(dp),intent(in) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) - real(dp),intent(inout) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(dp),intent(out) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) real(dp),intent(in) :: scalarCanopyIcePrev ! previous value for mass of ice on the vegetation canopy (kg m-2) - real(dp),intent(inout) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) + real(dp),intent(out) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) real(dp),intent(in) :: scalarCanopyEnthalpyPrev ! previous value of enthalpy of the vegetation canopy (J m-3) - real(dp),intent(inout) :: mLayerTempTrial(:) + real(dp),intent(out) :: mLayerTempTrial(:) real(dp),intent(in) :: mLayerTempPrev(:) - real(dp),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) - real(dp),intent(inout) :: mLayerMatricHeadTrial(:) ! trial value for liquid water matric potential (m) + real(dp),intent(out) :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) + real(dp),intent(out) :: mLayerMatricHeadTrial(:) ! trial value for liquid water matric potential (m) real(dp),intent(in) :: mLayerMatricHeadPrev(:) - real(dp),intent(inout) :: mLayerVolFracWatTrial(:) + real(dp),intent(out) :: mLayerVolFracWatTrial(:) real(dp),intent(in) :: mLayerVolFracWatPrev(:) - real(dp),intent(inout) :: mLayerVolFracIceTrial(:) + real(dp),intent(out) :: mLayerVolFracIceTrial(:) real(dp),intent(in) :: mLayerVolFracIcePrev(:) - real(dp),intent(inout) :: mLayerVolFracLiqTrial(:) + real(dp),intent(out) :: mLayerVolFracLiqTrial(:) real(dp),intent(in) :: mLayerVolFracLiqPrev(:) - real(dp),intent(inout) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + real(dp),intent(out) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) real(dp),intent(in) :: scalarAquiferStoragePrev ! previous value of storage of water in the aquifer (m) real(dp),intent(in) :: mLayerEnthalpyPrev(:) real(dp),intent(out) :: mLayerEnthalpyTrial(:) @@ -262,7 +262,7 @@ subroutine eval8summaFida(& integer(i4b) :: ixSaturation ! index of the lowest saturated layer real(dp) :: scalarCanopyCmTrial real(dp),dimension(nLayers) :: mLayerCmTrial - logical(lgt),parameter :: updateCp=.true. + logical(lgt),parameter :: updateCp=.false. logical(lgt),parameter :: needCm=.false. From d60eb79f8b3d2cc4cec0290d976790acfe771176 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 9 May 2021 11:10:11 -0600 Subject: [PATCH 0097/1472] scalrSWE redundant in vegNrgFluxFida --- build/source/engine/computFluxFida.f90 | 2 +- build/source/engine/vegNrgFluxFida.f90 | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/build/source/engine/computFluxFida.f90 b/build/source/engine/computFluxFida.f90 index 189459a02..48296ff46 100755 --- a/build/source/engine/computFluxFida.f90 +++ b/build/source/engine/computFluxFida.f90 @@ -750,7 +750,7 @@ subroutine computFluxFida(& nLayers, & ! intent(in): total number of layers firstFluxCall, & ! intent(in): logical flag to compute index of the lowest saturated layer ! input: state and diagnostic variables - mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) + mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. matric head mLayerMatricHeadLiqTrial, & ! intent(in): liquid water matric potential (m) mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water (-) mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of ice (-) diff --git a/build/source/engine/vegNrgFluxFida.f90 b/build/source/engine/vegNrgFluxFida.f90 index d76365100..58dbcfdac 100755 --- a/build/source/engine/vegNrgFluxFida.f90 +++ b/build/source/engine/vegNrgFluxFida.f90 @@ -511,7 +511,6 @@ subroutine vegNrgFluxFida(& ! input: water storage ! NOTE: soil stress only computed at the start of the substep (firstFluxCall=.true.) - scalarSWE => prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(in): [dp] snow water equivalent on the ground (kg m-2) scalarSnowDepth => prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(in): [dp] snow depth on the ground surface (m) basinAquiferStorage => bvar_data%var(iLookBVAR%basin__AquiferStorage)%dat(1), & ! intent(in): [dp] aquifer storage for the single basin (m) From cacbce7bbd5c67b9514d66b3a3af9da67cba6b9b Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 9 May 2021 12:15:29 -0600 Subject: [PATCH 0098/1472] firstSplitOper in eval8summaFida --- build/source/engine/eval8summaFida.f90 | 4 +++- build/source/engine/evalEqnsFida.f90 | 1 + build/source/engine/fidaSolver.f90 | 5 ++++- build/source/engine/fida_datatypes.f90 | 1 + 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 6e913172f..f8ab7dc25 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -97,6 +97,7 @@ subroutine eval8summaFida(& nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(inout) + firstSplitOper, & ! intent(in) computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors @@ -173,6 +174,7 @@ subroutine eval8summaFida(& integer,intent(in) :: nState ! total number of state variables logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(inout) :: firstFluxCall + logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors @@ -576,7 +578,7 @@ subroutine eval8summaFida(& nLayers, & ! intent(in): total number of layers firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(inout): flag to denote the first flux call - .true., & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution scalarSfcMeltPond/dt, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) diff --git a/build/source/engine/evalEqnsFida.f90 b/build/source/engine/evalEqnsFida.f90 index 29776526b..43e29e8a4 100644 --- a/build/source/engine/evalEqnsFida.f90 +++ b/build/source/engine/evalEqnsFida.f90 @@ -106,6 +106,7 @@ integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_d eqns_data%nState, & ! intent(in): number of state variables in the current subset eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step eqns_data%firstFluxCall, & ! intent(inout) + eqns_data%firstSplitOper, & ! intent(in) eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 8cff49156..7511e4386 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -418,13 +418,15 @@ subroutine fidaSolver( & !********************************************************************************** tret(1) = t0 do while(tret(1) < dt) - eqns_data%firstFluxCall = .false. + eqns_data%firstFluxCall = .true. + eqns_data%firstSplitOper = .false. ! call IDASolve retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) if( retval < 0 ) exit ! get the last stepsize retval = FIDAGetLastStep(ida_mem, dt_last) + eqns_data%firstSplitOper = .true. ! compute the flux and the residual vector for a given state vector call eval8summaFida(& @@ -437,6 +439,7 @@ subroutine fidaSolver( & eqns_data%nState, & ! intent(in): number of state variables in the current subset eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step eqns_data%firstFluxCall, & ! intent(inout) + eqns_data%firstSplitOper, & ! intent(in) eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index f4d255967..9856dcfa6 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -26,6 +26,7 @@ module fida_datatypes integer(i4b) :: ixMatrix ! form of matrix (dense or banded) logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt) :: firstFluxCall + logical(lgt) :: firstSplitOper logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution type(zLookup) :: lookup_data ! lookup tables From e9abfd745fdd382861086318e253f8fe29ab5c84 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 9 May 2021 20:13:55 -0600 Subject: [PATCH 0099/1472] firstSplitOper inout in eval8summaFida --- build/source/engine/eval8summaFida.f90 | 4 +++- build/source/engine/fidaSolver.f90 | 5 ++--- build/source/engine/systemSolv.f90 | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index f8ab7dc25..d518eca77 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -174,7 +174,7 @@ subroutine eval8summaFida(& integer,intent(in) :: nState ! total number of state variables logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(inout) :: firstFluxCall - logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt),intent(inout) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors @@ -618,6 +618,8 @@ subroutine eval8summaFida(& err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + firstSplitOper = .false. + ! compute soil compressibility (-) and its derivative w.r.t. matric head (m) ! NOTE: we already extracted trial matrix head and volumetric liquid water as part of the flux calculations diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 7511e4386..b3edb83c1 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -418,15 +418,14 @@ subroutine fidaSolver( & !********************************************************************************** tret(1) = t0 do while(tret(1) < dt) - eqns_data%firstFluxCall = .true. - eqns_data%firstSplitOper = .false. + eqns_data%firstFluxCall = .false. + eqns_data%firstSplitOper = .true. ! call IDASolve retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) if( retval < 0 ) exit ! get the last stepsize retval = FIDAGetLastStep(ida_mem, dt_last) - eqns_data%firstSplitOper = .true. ! compute the flux and the residual vector for a given state vector call eval8summaFida(& diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 342b08ee4..326b40c69 100755 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -561,6 +561,7 @@ subroutine systemSolv(& ! end associate statements end associate globalVars + end subroutine systemSolv From 5ebeb27f14dc082db649bd7f295336beab8cda8b Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Mon, 10 May 2021 12:57:15 -0600 Subject: [PATCH 0100/1472] ixSaturation in fidaSolver --- build/source/engine/eval8summaFida.f90 | 3 ++- build/source/engine/evalEqnsFida.f90 | 3 ++- build/source/engine/fidaSolver.f90 | 14 ++++++++++---- build/source/engine/fida_datatypes.f90 | 1 + build/source/engine/sysSolvFida.f90 | 1 + 5 files changed, 16 insertions(+), 6 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index d518eca77..cd4784b52 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -142,6 +142,7 @@ subroutine eval8summaFida(& scalarAquiferStoragePrev, & mLayerEnthalpyPrev, & ! intent(in) mLayerEnthalpyTrial, & ! intent(out) + ixSaturation, & ! intent(inout) feasible, & ! intent(out): flag to denote the feasibility of the solution fluxVec, & ! intent(out): flux vector resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation @@ -219,6 +220,7 @@ subroutine eval8summaFida(& real(dp),intent(in) :: scalarAquiferStoragePrev ! previous value of storage of water in the aquifer (m) real(dp),intent(in) :: mLayerEnthalpyPrev(:) real(dp),intent(out) :: mLayerEnthalpyTrial(:) + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution real(dp),intent(out) :: fluxVec(:) ! flux vector real(dp),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation @@ -261,7 +263,6 @@ subroutine eval8summaFida(& real(dp),parameter :: canopyTempMax=500._dp ! expected maximum value for the canopy temperature (K) character(LEN=256) :: cmessage ! error message of downwind routine real(qp) :: scalarCanopyEnthalpyPrime - integer(i4b) :: ixSaturation ! index of the lowest saturated layer real(dp) :: scalarCanopyCmTrial real(dp),dimension(nLayers) :: mLayerCmTrial logical(lgt),parameter :: updateCp=.false. diff --git a/build/source/engine/evalEqnsFida.f90 b/build/source/engine/evalEqnsFida.f90 index 43e29e8a4..55c4d764b 100644 --- a/build/source/engine/evalEqnsFida.f90 +++ b/build/source/engine/evalEqnsFida.f90 @@ -149,7 +149,8 @@ integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_d eqns_data%scalarAquiferStorageTrial, & eqns_data%scalarAquiferStoragePrev, & eqns_data%mLayerEnthalpyPrev, & ! intent(in) - eqns_data%mLayerEnthalpyTrial, & ! intent(out) + eqns_data%mLayerEnthalpyTrial, & ! intent(out) + eqns_data%ixSaturation, & ! intent(inout) ! output feasible, & ! intent(out): flag to denote the feasibility of the solution eqns_data%fluxVec, & ! intent(out): flux vector diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index b3edb83c1..5dbc06340 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -121,6 +121,7 @@ subroutine fidaSolver( & flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output + ixSaturation, & ! intent(out) idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix dt_last, & ! intent(out): last stepsize @@ -189,6 +190,7 @@ subroutine fidaSolver( & type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables real(dp),intent(inout) :: mLayerCmpress_sum(:) ! output: state vectors + integer(i4b),intent(out) :: ixSaturation real(dp),intent(inout) :: stateVec(:) ! model state vector (y) real(dp),intent(inout) :: stateVecPrime(:) ! model state vector (y') logical(lgt),intent(out) :: idaSucceeds @@ -411,6 +413,7 @@ subroutine fidaSolver( & eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) + eqns_data%ixSaturation = ixSaturation !********************************************************************************** !****************************** Main Solver *************************************** @@ -482,6 +485,7 @@ subroutine fidaSolver( & eqns_data%scalarAquiferStoragePrev, & eqns_data%mLayerEnthalpyPrev, & ! intent(in) eqns_data%mLayerEnthalpyTrial, & ! intent(out) + eqns_data%ixSaturation, & ! output feasible, & ! intent(out): flag to denote the feasibility of the solution eqns_data%fluxVec, & ! intent(out): flux vector @@ -540,13 +544,15 @@ subroutine fidaSolver( & !****************************** End of Main Solver *************************************** - ! copy to output data - diag_data = eqns_data%diag_data - flux_data = eqns_data%flux_data - deriv_data = eqns_data%deriv_data + err = eqns_data%err message = eqns_data%message if( tret(1) == dt .and. feasible)then + ! copy to output data + diag_data = eqns_data%diag_data + flux_data = eqns_data%flux_data + deriv_data = eqns_data%deriv_data + ixSaturation = eqns_data%ixSaturation idaSucceeds = .true. endif diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index 9856dcfa6..d9d24a394 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -68,6 +68,7 @@ module fida_datatypes real(dp), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) real(dp), allocatable :: mLayerTempPrev(:) ! vector of layer temperature (K) at previous step real(dp), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + integer :: ixSaturation integer(i4b) :: err ! error code character(len = 50) :: message ! error message end type eqnsData diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 68d334abf..cc5ee2225 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -462,6 +462,7 @@ subroutine sysSolvFida(& flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix dt_last, & ! intent(out): last stepsize From 283277d2d4a51f1a976231f3074db070fc59b45a Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 25 May 2021 00:16:03 -0600 Subject: [PATCH 0101/1472] idaSucceeds changed in fidaSolver --- build/source/engine/fidaSolver.f90 | 14 +- build/source/engine/layerDivideFida.f90 | 487 ++++++++++++++++++++++++ build/source/engine/volicePackFida.f90 | 144 +++++++ 3 files changed, 640 insertions(+), 5 deletions(-) create mode 100755 build/source/engine/layerDivideFida.f90 create mode 100755 build/source/engine/volicePackFida.f90 diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 5dbc06340..7f33dbd4f 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -233,7 +233,7 @@ subroutine fidaSolver( & ! initialize error control err=0; message="fidaSolver/" nState = nStat - idaSucceeds = .false. + idaSucceeds = .true. ! fill eqns_data which will be required later to call eval8summaFida eqns_data%dt = dt eqns_data%nSnow = nSnow @@ -425,7 +425,10 @@ subroutine fidaSolver( & eqns_data%firstSplitOper = .true. ! call IDASolve retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) - if( retval < 0 ) exit + if( retval < 0 )then + idaSucceeds = .false. + exit + endif ! get the last stepsize retval = FIDAGetLastStep(ida_mem, dt_last) @@ -546,14 +549,15 @@ subroutine fidaSolver( & err = eqns_data%err - message = eqns_data%message - if( tret(1) == dt .and. feasible)then + message = eqns_data%message + if( .not. feasible) idaSucceeds = .false. + + if( idaSucceeds .eqv. .true.)then ! copy to output data diag_data = eqns_data%diag_data flux_data = eqns_data%flux_data deriv_data = eqns_data%deriv_data ixSaturation = eqns_data%ixSaturation - idaSucceeds = .true. endif diff --git a/build/source/engine/layerDivideFida.f90 b/build/source/engine/layerDivideFida.f90 new file mode 100755 index 000000000..62d2730b6 --- /dev/null +++ b/build/source/engine/layerDivideFida.f90 @@ -0,0 +1,487 @@ +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . + +module layerDivideFida_module + +! variable types +USE nrtype + +! physical constants +USE multiconst,only:& + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water ! intrinsic density of liquid water (kg m-3) + +! access named variables for snow and soil +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil + +! access missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing double precision number + +! access metadata +USE globalData,only:prog_meta,diag_meta,flux_meta,indx_meta ! metadata + +! access the derived types to define the data structures +USE data_types,only:& + var_d, & ! data vector (dp) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (dp) + model_options ! defines the model decisions + +! access named variables defining elements in the data structures +USE var_lookup,only:iLookPROG,iLookDIAG,iLookFLUX,iLookINDEX ! named variables for structure elements +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure +USE var_lookup,only:iLookPARAM ! named variables for elements of the parameter structure + +! define look-up values for the choice of method to combine and sub-divide snow layers +USE mDecisions_module,only:& + sameRulesAllLayers, & ! SNTHERM option: same combination/sub-dividion rules applied to all layers + rulesDependLayerIndex ! CLM option: combination/sub-dividion rules depend on layer index + +! define look-up values for the choice of canopy shortwave radiation method +USE mDecisions_module,only:& + noah_mp, & ! full Noah-MP implementation (including albedo) + CLM_2stream, & ! CLM 2-stream model (see CLM documentation) + UEB_2stream, & ! UEB 2-stream model (Mahat and Tarboton, WRR 2011) + NL_scatter, & ! Simplified method Nijssen and Lettenmaier (JGR 1999) + BeersLaw ! Beer's Law (as implemented in VIC) + +! define look-up values for the choice of albedo method +USE mDecisions_module,only:& ! identify model options for snow albedo + constantDecay, & ! constant decay in snow albedo (e.g., VIC, CLASS) + variableDecay ! variable decay in snow albedo (e.g., BATS approach, with destructive metamorphism + soot content) + +! privacy +implicit none +private +public::layerDivideFida + +contains + + ! *********************************************************************************************************** + ! public subroutine layerDivideFida: add new snowfall to the system, and increase number of snow layers if needed + ! *********************************************************************************************************** + subroutine layerDivideFida(& + ! input/output: model data structures + model_decisions, & ! intent(in): model decisions + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): type of each layer + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output + divideLayer, & ! intent(out): flag to denote that a layer was divided + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------- + ! computational modules + USE snow_utils_module,only:fracliquid,templiquid ! functions to compute temperature/liquid water + USE globalData,only:maxSnowLayers, & ! maximum number of snow layers + veryBig + implicit none + ! -------------------------------------------------------------------------------------------------------- + ! input/output: model data structures + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(inout) :: indx_data ! type of each layer + type(var_dlength),intent(inout) :: prog_data ! model prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model flux variables + ! output + logical(lgt),intent(out) :: divideLayer ! flag to denote that a layer was divided + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------- + ! define local variables + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers + integer(i4b) :: nLayers ! total number of layers + integer(i4b) :: iLayer ! layer index + integer(i4b) :: jLayer ! layer index + real(dp),dimension(4) :: zmax_lower ! lower value of maximum layer depth + real(dp),dimension(4) :: zmax_upper ! upper value of maximum layer depth + real(dp) :: zmaxCheck ! value of zmax for a given snow layer + integer(i4b) :: nCheck ! number of layers to check to divide + logical(lgt) :: createLayer ! flag to indicate we are creating a new snow layer + real(dp) :: depthOriginal ! original layer depth before sub-division (m) + real(dp),parameter :: fracTop=0.5_dp ! fraction of old layer used for the top layer + real(dp) :: surfaceLayerSoilTemp ! temperature of the top soil layer (K) + real(dp) :: maxFrozenSnowTemp ! maximum temperature when effectively all water is frozen (K) + real(dp),parameter :: unfrozenLiq=0.01_dp ! unfrozen liquid water used to compute maxFrozenSnowTemp (-) + real(dp) :: volFracWater ! volumetric fraction of total water, liquid and ice (-) + real(dp) :: fracLiq ! fraction of liquid water (-) + integer(i4b),parameter :: ixVisible=1 ! named variable to define index in array of visible part of the spectrum + integer(i4b),parameter :: ixNearIR=2 ! named variable to define index in array of near IR part of the spectrum + real(dp),parameter :: verySmall=1.e-10_dp ! a very small number (used for error checking) + ! -------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="layerDivideFida/" + ! -------------------------------------------------------------------------------------------------------- + ! associate variables in the data structures + associate(& + ! model decisions + ix_snowLayers => model_decisions(iLookDECISIONS%snowLayers)%iDecision, & ! decision for snow combination + ! model parameters (compute layer temperature) + fc_param => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! freezing curve parameter for snow (K-1) + ! model parameters (new snow density) + newSnowDenMin => mpar_data%var(iLookPARAM%newSnowDenMin)%dat(1), & ! minimum new snow density (kg m-3) + newSnowDenMult => mpar_data%var(iLookPARAM%newSnowDenMult)%dat(1), & ! multiplier for new snow density (kg m-3) + newSnowDenScal => mpar_data%var(iLookPARAM%newSnowDenScal)%dat(1), & ! scaling factor for new snow density (K) + ! model parameters (control the depth of snow layers) + zmax => mpar_data%var(iLookPARAM%zmax)%dat(1), & ! maximum layer depth (m) + zmaxLayer1_lower => mpar_data%var(iLookPARAM%zmaxLayer1_lower)%dat(1), & ! maximum layer depth for the 1st (top) layer when only 1 layer (m) + zmaxLayer2_lower => mpar_data%var(iLookPARAM%zmaxLayer2_lower)%dat(1), & ! maximum layer depth for the 2nd layer when only 2 layers (m) + zmaxLayer3_lower => mpar_data%var(iLookPARAM%zmaxLayer3_lower)%dat(1), & ! maximum layer depth for the 3rd layer when only 3 layers (m) + zmaxLayer4_lower => mpar_data%var(iLookPARAM%zmaxLayer4_lower)%dat(1), & ! maximum layer depth for the 4th layer when only 4 layers (m) + zmaxLayer1_upper => mpar_data%var(iLookPARAM%zmaxLayer1_upper)%dat(1), & ! maximum layer depth for the 1st (top) layer when > 1 layer (m) + zmaxLayer2_upper => mpar_data%var(iLookPARAM%zmaxLayer2_upper)%dat(1), & ! maximum layer depth for the 2nd layer when > 2 layers (m) + zmaxLayer3_upper => mpar_data%var(iLookPARAM%zmaxLayer3_upper)%dat(1), & ! maximum layer depth for the 3rd layer when > 3 layers (m) + zmaxLayer4_upper => mpar_data%var(iLookPARAM%zmaxLayer4_upper)%dat(1), & ! maximum layer depth for the 4th layer when > 4 layers (m) + ! diagnostic scalar variables + scalarSnowfall => flux_data%var(iLookFLUX%scalarSnowfall)%dat(1), & ! snowfall flux (kg m-2 s-1) + scalarSnowfallTemp => diag_data%var(iLookDIAG%scalarSnowfallTemp)%dat(1), & ! computed temperature of fresh snow (K) + scalarSnowDepth => prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! total snow depth (m) + scalarSWE => prog_data%var(iLookPROG%scalarSWE)%dat(1) & ! SWE (kg m-2) + ) ! end associate statement + + ! --------------------------------------------------------------------------------------------------- + + ! initialize flag to denote that a layer was divided + divideLayer=.false. + + ! identify algorithmic control parameters to syb-divide and combine snow layers + zmax_lower = (/zmaxLayer1_lower, zmaxLayer2_lower, zmaxLayer3_lower, zmaxLayer4_lower/) + zmax_upper = (/zmaxLayer1_upper, zmaxLayer2_upper, zmaxLayer3_upper, zmaxLayer4_upper/) + + ! initialize the number of snow layers + nSnow = indx_data%var(iLookINDEX%nSnow)%dat(1) + nSoil = indx_data%var(iLookINDEX%nSoil)%dat(1) + nLayers = indx_data%var(iLookINDEX%nLayers)%dat(1) + + ! ***** special case of no snow layers + if(nSnow==0)then + + ! check if create the first snow layer + select case(ix_snowLayers) + case(sameRulesAllLayers); createLayer = (scalarSnowDepth > zmax) + case(rulesDependLayerIndex); createLayer = (scalarSnowDepth > zmaxLayer1_lower) + case default; err=20; message=trim(message)//'unable to identify option to combine/sub-divide snow layers'; return + end select ! (option to combine/sub-divide snow layers) + + ! ** create a new snow layer + if(createLayer)then + + ! flag that the layers have changed + divideLayer=.true. + + ! add a layer to all model variables + iLayer=0 ! (layer to divide: 0 is the special case of "snow without a layer") + call addModelLayer(prog_data,prog_meta,iLayer,nSnow,nLayers,err,cmessage); if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + call addModelLayer(diag_data,diag_meta,iLayer,nSnow,nLayers,err,cmessage); if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + call addModelLayer(flux_data,flux_meta,iLayer,nSnow,nLayers,err,cmessage); if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + call addModelLayer(indx_data,indx_meta,iLayer,nSnow,nLayers,err,cmessage); if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + ! associate local variables to the information in the data structures + ! NOTE: need to do this here, since state vectors have just been modified + associate(& + ! coordinate variables + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! depth of each layer (m) + ! model state variables + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! temperature of each layer (K) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! volumetric fraction of ice in each layer (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat & ! volumetric fraction of liquid water in each layer (-) + ) ! (association of local variables to the information in the data structures) + + ! get the layer depth + mLayerDepth(1) = scalarSnowDepth + + ! compute surface layer temperature + surfaceLayerSoilTemp = mLayerTemp(2) ! temperature of the top soil layer (K) + maxFrozenSnowTemp = templiquid(unfrozenLiq,fc_param) ! snow temperature at fraction "unfrozenLiq" (K) + mLayerTemp(1) = min(maxFrozenSnowTemp,surfaceLayerSoilTemp) ! snow temperature (K) + + ! compute the fraction of liquid water associated with the layer temperature + fracLiq = fracliquid(mLayerTemp(1),fc_param) + + ! compute volumeteric fraction of liquid water and ice + volFracWater = (scalarSWE/scalarSnowDepth)/iden_water ! volumetric fraction of total water (liquid and ice) + mLayerVolFracIce(1) = (1._dp - fracLiq)*volFracWater*(iden_water/iden_ice) ! volumetric fraction of ice (-) + mLayerVolFracLiq(1) = fracLiq *volFracWater ! volumetric fraction of liquid water (-) + + ! end association with local variables to the information in the data structures) + end associate + + ! initialize albedo + ! NOTE: albedo is computed within the Noah-MP radiation routine + if(model_decisions(iLookDECISIONS%canopySrad)%iDecision /= noah_mp)then + select case(model_decisions(iLookDECISIONS%alb_method)%iDecision) + ! (constant decay rate -- albedo the same for all spectral bands) + case(constantDecay) + prog_data%var(iLookPROG%scalarSnowAlbedo)%dat(1) = mpar_data%var(iLookPARAM%albedoMax)%dat(1) + prog_data%var(iLookPROG%spectralSnowAlbedoDiffuse)%dat(:) = mpar_data%var(iLookPARAM%albedoMax)%dat(1) + ! (variable decay rate) + case(variableDecay) + prog_data%var(iLookPROG%spectralSnowAlbedoDiffuse)%dat(ixVisible) = mpar_data%var(iLookPARAM%albedoMaxVisible)%dat(1) + prog_data%var(iLookPROG%spectralSnowAlbedoDiffuse)%dat(ixNearIR) = mpar_data%var(iLookPARAM%albedoMaxNearIR)%dat(1) + prog_data%var(iLookPROG%scalarSnowAlbedo)%dat(1) = ( mpar_data%var(iLookPARAM%Frad_vis)%dat(1))*mpar_data%var(iLookPARAM%albedoMaxVisible)%dat(1) + & + (1._dp - mpar_data%var(iLookPARAM%Frad_vis)%dat(1))*mpar_data%var(iLookPARAM%albedoMaxNearIR)%dat(1) + case default; err=20; message=trim(message)//'unable to identify option for snow albedo'; return + end select ! identify option for snow albedo + ! set direct albedo to diffuse albedo + diag_data%var(iLookDIAG%spectralSnowAlbedoDirect)%dat(:) = prog_data%var(iLookPROG%spectralSnowAlbedoDiffuse)%dat(:) + end if ! (if NOT using the Noah-MP radiation routine) + + end if ! if creating a new layer + + ! end special case of nSnow=0 + ! ******************************************************************************************************************** + ! ******************************************************************************************************************** + + ! ***** sub-divide snow layers, if necessary + else ! if nSnow>0 + + ! identify the number of layers to check for need for sub-division + nCheck = min(nSnow, maxSnowLayers-1) ! the depth of the last layer, if it exists, does not have a maximum value + ! loop through all layers, and sub-divide a given layer, if necessary + do iLayer=1,nCheck + divideLayer=.false. + + ! identify the maximum depth of the layer + select case(ix_snowLayers) + case(sameRulesAllLayers) + if (nCheck >= maxSnowLayers-1) then + ! make sure we don't divide so make very big + zmaxCheck = veryBig + else + zmaxCheck = zmax + end if + case(rulesDependLayerIndex) + if(iLayer == nSnow)then + zmaxCheck = zmax_lower(iLayer) + else + zmaxCheck = zmax_upper(iLayer) + end if + case default; err=20; message=trim(message)//'unable to identify option to combine/sub-divide snow layers'; return + end select ! (option to combine/sub-divide snow layers) + + ! check the need to sub-divide + if(prog_data%var(iLookPROG%mLayerDepth)%dat(iLayer) > zmaxCheck)then + + ! flag that layers were divided + divideLayer=.true. + + ! add a layer to all model variables + call addModelLayer(prog_data,prog_meta,iLayer,nSnow,nLayers,err,cmessage); if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + call addModelLayer(diag_data,diag_meta,iLayer,nSnow,nLayers,err,cmessage); if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + call addModelLayer(flux_data,flux_meta,iLayer,nSnow,nLayers,err,cmessage); if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + call addModelLayer(indx_data,indx_meta,iLayer,nSnow,nLayers,err,cmessage); if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + ! define the layer depth + layerSplit: associate(mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat) + depthOriginal = mLayerDepth(iLayer) + mLayerDepth(iLayer) = fracTop*depthOriginal + mLayerDepth(iLayer+1) = (1._dp - fracTop)*depthOriginal + end associate layerSplit + + exit ! NOTE: only sub-divide one layer per substep + + end if ! (if sub-dividing layer) + + end do ! (looping through layers) + + end if ! if nSnow==0 + + ! update coordinates + if(divideLayer)then + + ! associate coordinate variables in data structure + geometry: associate(& + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! depth of the layer (m) + mLayerHeight => prog_data%var(iLookPROG%mLayerHeight)%dat ,& ! height of the layer mid-point (m) + iLayerHeight => prog_data%var(iLookPROG%iLayerHeight)%dat ,& ! height of the layer interface (m) + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! type of each layer (iname_snow or iname_soil) + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! total number of layers + ) ! (association of local variables with coordinate variab;es in data structures) + + ! update the layer type + layerType(1:nSnow+1) = iname_snow + layerType(nSnow+2:nLayers+1) = iname_soil + + ! identify the number of snow and soil layers, and check all is a-OK + nSnow = count(layerType==iname_snow) + nSoil = count(layerType==iname_soil) + nLayers = nSnow + nSoil + + ! re-set coordinate variables + iLayerHeight(0) = -scalarSnowDepth + do jLayer=1,nLayers + iLayerHeight(jLayer) = iLayerHeight(jLayer-1) + mLayerDepth(jLayer) + mLayerHeight(jLayer) = (iLayerHeight(jLayer-1) + iLayerHeight(jLayer))/2._dp + end do + + ! check + if(abs(sum(mLayerDepth(1:nSnow)) - scalarSnowDepth) > verySmall)then + print*, 'nSnow = ', nSnow + write(*,'(a,1x,f30.25,1x)') 'sum(mLayerDepth(1:nSnow)) = ', sum(mLayerDepth(1:nSnow)) + write(*,'(a,1x,f30.25,1x)') 'scalarSnowDepth = ', scalarSnowDepth + write(*,'(a,1x,f30.25,1x)') 'epsilon(scalarSnowDepth) = ', epsilon(scalarSnowDepth) + message=trim(message)//'sum of layer depths does not equal snow depth' + err=20; return + end if + + ! end association with coordinate variables in data structure + end associate geometry + + end if ! if dividing a layer + + ! end associate variables in data structure + end associate + + end subroutine layerDivideFida + + + ! ************************************************************************************************ + ! private subroutine addModelLayer: add an additional layer to all model vectors + ! ************************************************************************************************ + subroutine addModelLayer(dataStruct,metaStruct,ix_divide,nSnow,nLayers,err,message) + USE var_lookup,only:iLookVarType ! look up structure for variable typed + USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages + USE f2008funcs_module,only:cloneStruc ! used to "clone" data structures -- temporary replacement of the intrinsic allocate(a, source=b) + USE data_types,only:var_ilength,var_dlength ! data vectors with variable length dimension + USE data_types,only:var_info ! metadata structure + implicit none + ! --------------------------------------------------------------------------------------------- + ! input/output: data structures + class(*),intent(inout) :: dataStruct ! data structure + type(var_info),intent(in) :: metaStruct(:) ! metadata structure + ! input: snow layer indices + integer(i4b),intent(in) :: ix_divide ! index of the layer to divide + integer(i4b),intent(in) :: nSnow,nLayers ! number of snow layers, total number of layers + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! --------------------------------------------------------------------------------------------- + ! local variables + integer(i4b) :: ivar ! index of model variable + integer(i4b) :: ix_lower ! lower bound of the vector + integer(i4b) :: ix_upper ! upper bound of the vector + logical(lgt) :: stateVariable ! .true. if variable is a state variable + real(dp),allocatable :: tempVec_dp(:) ! temporary vector (double precision) + integer(i4b),allocatable :: tempVec_i4b(:) ! temporary vector (integer) + character(LEN=256) :: cmessage ! error message of downwind routine + ! --------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='addModelLayer/' + + ! ***** add a layer to each model variable + do ivar=1,size(metaStruct) + + ! define bounds + select case(metaStruct(ivar)%vartype) + case(iLookVarType%midSnow); ix_lower=1; ix_upper=nSnow + case(iLookVarType%midToto); ix_lower=1; ix_upper=nLayers + case(iLookVarType%ifcSnow); ix_lower=0; ix_upper=nSnow + case(iLookVarType%ifcToto); ix_lower=0; ix_upper=nLayers + case default; cycle + end select + + ! identify whether it is a state variable + select case(trim(metaStruct(ivar)%varname)) + case('mLayerDepth','mLayerTemp','mLayerVolFracIce','mLayerVolFracLiq'); stateVariable=.true. + case default; stateVariable=.false. + end select + + ! divide layers + select type(dataStruct) + + ! ** double precision + type is (var_dlength) + ! check allocated + if(.not.allocated(dataStruct%var(ivar)%dat))then; err=20; message='data vector is not allocated'; return; end if + ! assign the data vector to the temporary vector + call cloneStruc(tempVec_dp, ix_lower, source=dataStruct%var(ivar)%dat, err=err, message=cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + ! reallocate space for the new vector + deallocate(dataStruct%var(ivar)%dat,stat=err) + if(err/=0)then; err=20; message='problem in attempt to deallocate memory for data vector'; return; end if + allocate(dataStruct%var(ivar)%dat(ix_lower:ix_upper+1),stat=err) + if(err/=0)then; err=20; message='problem in attempt to reallocate memory for data vector'; return; end if + ! populate the state vector + if(stateVariable)then + if(ix_upper > 0)then ! (only copy data if the vector exists -- can be a variable for snow, with no layers) + if(ix_divide > 0)then + dataStruct%var(ivar)%dat(1:ix_divide) = tempVec_dp(1:ix_divide) ! copy data + dataStruct%var(ivar)%dat(ix_divide+1) = tempVec_dp(ix_divide) ! repeat data for the sub-divided layer + end if + if(ix_upper > ix_divide) & + dataStruct%var(ivar)%dat(ix_divide+2:ix_upper+1) = tempVec_dp(ix_divide+1:ix_upper) ! copy data + end if ! if the vector exists + ! not a state variable + else + dataStruct%var(ivar)%dat(:) = realMissing + end if + ! deallocate the temporary vector: strictly not necessary, but include to be safe + deallocate(tempVec_dp,stat=err) + if(err/=0)then; err=20; message='problem deallocating temporary data vector'; return; end if + + ! ** integer + type is (var_ilength) + ! check allocated + if(.not.allocated(dataStruct%var(ivar)%dat))then; err=20; message='data vector is not allocated'; return; end if + ! assign the data vector to the temporary vector + call cloneStruc(tempVec_i4b, ix_lower, source=dataStruct%var(ivar)%dat, err=err, message=cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + ! reallocate space for the new vector + deallocate(dataStruct%var(ivar)%dat,stat=err) + if(err/=0)then; err=20; message='problem in attempt to deallocate memory for data vector'; return; end if + allocate(dataStruct%var(ivar)%dat(ix_lower:ix_upper+1),stat=err) + if(err/=0)then; err=20; message='problem in attempt to reallocate memory for data vector'; return; end if + ! populate the state vector + if(stateVariable)then + if(ix_upper > 0)then ! (only copy data if the vector exists -- can be a variable for snow, with no layers) + if(ix_divide > 0)then + dataStruct%var(ivar)%dat(1:ix_divide) = tempVec_i4b(1:ix_divide) ! copy data + dataStruct%var(ivar)%dat(ix_divide+1) = tempVec_i4b(ix_divide) ! repeat data for the sub-divided layer + end if + if(ix_upper > ix_divide) & + dataStruct%var(ivar)%dat(ix_divide+2:ix_upper+1) = tempVec_i4b(ix_divide+1:ix_upper) ! copy data + end if ! if the vector exists + ! not a state variable + else + dataStruct%var(ivar)%dat(:) = integerMissing + end if + ! deallocate the temporary vector: strictly not necessary, but include to be safe + deallocate(tempVec_i4b,stat=err) + if(err/=0)then; err=20; message='problem deallocating temporary data vector'; return; end if + + ! check that we found the data type + class default; err=20; message=trim(message)//'unable to identify the data type'; return + + end select ! dependence on data types + + end do ! looping through variables + + end subroutine addModelLayer + +end module layerDivideFida_module diff --git a/build/source/engine/volicePackFida.f90 b/build/source/engine/volicePackFida.f90 new file mode 100755 index 000000000..e28f9c041 --- /dev/null +++ b/build/source/engine/volicePackFida.f90 @@ -0,0 +1,144 @@ +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . + +module volicePackFida_module + +! data types +USE nrtype + +! derived types to define the data structures +USE data_types,only:& + var_d, & ! data vector (dp) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (dp) + model_options ! defines the model decisions + +! named variables for snow and soil +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil + +! named variables for parent structures +USE var_lookup,only:iLookINDEX ! named variables for structure elements + +! physical constants +USE multiconst,only:& + Tfreeze, & ! freezing point (K) + LH_fus, & ! latent heat of fusion (J kg-1) + LH_vap, & ! latent heat of vaporization (J kg-1) + LH_sub, & ! latent heat of sublimation (J kg-1) + iden_air, & ! intrinsic density of air (kg m-3) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water ! intrinsic density of water (kg m-3) + +! privacy +implicit none +private +public::volicePackFida +public::newsnwfall + +contains + + + ! ************************************************************************************************ + ! public subroutine volicePackFida: combine and sub-divide layers if necessary) + ! ************************************************************************************************ + subroutine volicePackFida(& + ! input/output: model data structures + tooMuchMelt, & ! intent(in): flag to force merge of snow layers + model_decisions, & ! intent(in): model decisions + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): type of each layer + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output + modifiedLayers, & ! intent(out): flag to denote that layers were modified + err,message) ! intent(out): error control + ! ------------------------------------------------------------------------------------------------ + ! external subroutine + USE layerMerge_module,only:layerMerge ! merge snow layers if they are too thin + USE layerDivide_module,only:layerDivide ! sub-divide layers if they are too thick + implicit none + ! ------------------------------------------------------------------------------------------------ + ! input/output: model data structures + logical(lgt),intent(in) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(inout) :: indx_data ! type of each layer + type(var_dlength),intent(inout) :: prog_data ! model prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model flux variables + ! output + logical(lgt),intent(out) :: modifiedLayers ! flag to denote that we modified the layers + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------ + ! local variables + character(LEN=256) :: cmessage ! error message of downwind routine + logical(lgt) :: mergedLayers ! flag to denote that layers were merged + logical(lgt) :: divideLayer ! flag to denote that a layer was divided + ! initialize error control + err=0; message='volicePackFida/' + + ! divide snow layers if too thick + call layerDivide(& + ! input/output: model data structures + model_decisions, & ! intent(in): model decisions + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): type of each layer + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output + divideLayer, & ! intent(out): flag to denote that layers were modified + err,cmessage) ! intent(out): error control + if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if + + if(divideLayer) print *, 'divideLayer' + + ! merge snow layers if they are too thin + call layerMerge(& + ! input/output: model data structures + tooMuchMelt, & ! intent(in): flag to force merge of snow layers + model_decisions, & ! intent(in): model decisions + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): type of each layer + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output + mergedLayers, & ! intent(out): flag to denote that layers were modified + err,cmessage) ! intent(out): error control + if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if + + if(mergedLayers) print *, 'mergedLayers' + + ! update the number of layers + indx_data%var(iLookINDEX%nSnow)%dat(1) = count(indx_data%var(iLookINDEX%layerType)%dat==iname_snow) + indx_data%var(iLookINDEX%nSoil)%dat(1) = count(indx_data%var(iLookINDEX%layerType)%dat==iname_soil) + indx_data%var(iLookINDEX%nLayers)%dat(1) = indx_data%var(iLookINDEX%nSnow)%dat(1) + indx_data%var(iLookINDEX%nSoil)%dat(1) + + ! flag if layers were modified + modifiedLayers = (mergedLayers .or. divideLayer) + + end subroutine volicePackFida + + +end module volicePackFida_module From 093dfd704dd209289526a414f9d1c12ef7339bfc Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 25 May 2021 00:23:40 -0600 Subject: [PATCH 0102/1472] dt_out --- build/source/engine/fidaSolver.f90 | 3 +++ build/source/engine/sysSolvFida.f90 | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 7f33dbd4f..90983c17f 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -126,6 +126,7 @@ subroutine fidaSolver( & mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix dt_last, & ! intent(out): last stepsize dt_past, & ! intent(out): one stepsize before the last one + dt_out, & ! intent(out) stateVec, & ! intent(out): model state vector stateVecPrime, & ! intent(out): derivative of model state vector err,message & ! intent(out): error control @@ -196,6 +197,7 @@ subroutine fidaSolver( & logical(lgt),intent(out) :: idaSucceeds real(qp),intent(out) :: dt_last(1) real(qp),intent(out) :: dt_past + real(qp),intent(out) :: dt_out ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -558,6 +560,7 @@ subroutine fidaSolver( & flux_data = eqns_data%flux_data deriv_data = eqns_data%deriv_data ixSaturation = eqns_data%ixSaturation + dt_out = tret(1) endif diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index cc5ee2225..f064da1e6 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -207,6 +207,7 @@ subroutine sysSolvFida(& logical(lgt) :: feasible ! feasibility flag real(dp) :: dt_last(1) ! last stepsize taken by ida solver real(qp) :: dt_past ! one step before the last stepsize taken by ida solver + real(qp) :: dt_out real(dp) :: atol(nState) ! absolute telerance real(dp) :: rtol(nState) ! relative tolerance type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a data step @@ -467,6 +468,7 @@ subroutine sysSolvFida(& mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix dt_last, & ! intent(out): last stepsize dt_past, & ! intent(out): one stepsize before the last one + dt_out, & ! intent(out) stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step err,cmessage) ! intent(out): error control @@ -480,6 +482,9 @@ subroutine sysSolvFida(& end do ! iteration over tolerances + + if(dt /= dt_out) stop 1 + ! check if fida is successful if( .not.idaSucceeds )then From 0c216ff34fae554861cd9e52ba41134c7a1a64a8 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 25 May 2021 00:50:05 -0600 Subject: [PATCH 0103/1472] dt_out in varSubstep and opSplittin --- build/source/engine/opSplittin.f90 | 6 +++++- build/source/engine/sysSolvFida.f90 | 13 +++++++------ build/source/engine/varSubstepFida.f90 | 19 +++++++++++-------- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index fc7f19e9c..f185b48a1 100755 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -298,6 +298,7 @@ subroutine opSplittin(& integer(i4b),parameter :: BE=2 integer(i4b) :: solver=IDA ! BE or IDA integer(i4b) :: nCoupling + real(qp) :: dt_out ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- @@ -809,6 +810,7 @@ subroutine opSplittin(& failedMinimumStep, & ! intent(out) : flag for failed substeps reduceCoupledStep, & ! intent(out) : flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt + dt_out, & ! intent(out) err,cmessage) ! intent(out) : error code and error message case(BE) call varSubstep(& @@ -848,7 +850,9 @@ subroutine opSplittin(& err,cmessage) ! intent(out) : error code and error message ! check case default; err=20; message=trim(message)//'expect case to be ida or be'; return - end select + end select + + dt = dt_out diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index f064da1e6..97b7c3462 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -123,6 +123,7 @@ subroutine sysSolvFida(& stateVecPrime, & ! intent(out): updated state vector reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out): flag to denote that there was too much melt + dt_out, & ! intent(out) err,message) ! intent(out): error code and error message ! --------------------------------------------------------------------------------------- ! structure allocations @@ -169,6 +170,7 @@ subroutine sysSolvFida(& real(dp),intent(out) :: stateVecPrime(:) ! trial state vector (mixed units) logical(lgt),intent(out) :: reduceCoupledStep ! flag to reduce the length of the coupled step logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that there was too much melt + real(qp),intent(out) :: dt_out integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! ********************************************************************************************************************************************************* @@ -207,7 +209,6 @@ subroutine sysSolvFida(& logical(lgt) :: feasible ! feasibility flag real(dp) :: dt_last(1) ! last stepsize taken by ida solver real(qp) :: dt_past ! one step before the last stepsize taken by ida solver - real(qp) :: dt_out real(dp) :: atol(nState) ! absolute telerance real(dp) :: rtol(nState) ! relative tolerance type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a data step @@ -483,7 +484,7 @@ subroutine sysSolvFida(& end do ! iteration over tolerances - if(dt /= dt_out) stop 1 +! if(dt /= dt_out) stop 1 ! check if fida is successful @@ -498,15 +499,15 @@ subroutine sysSolvFida(& ! compute average flux select case(ixQuadrature) case(ixRectangular) - ! divide by dt. Now we have average flux + ! divide by dt_out. Now we have average flux do iVar=1,size(flux_meta) - flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / dt + flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / dt_out end do case(ixTrapezoidal) - ! add the last part of the integral, then divide by dt. Now we have average flux + ! add the last part of the integral, then divide by dt_out. Now we have average flux do iVar=1,size(flux_meta) flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) + flux_init%var(iVar)%dat(:) * (dt_last(1) + dt_past) & - + flux_temp%var(iVar)%dat(:) * dt_last(1) ) / (2.0*dt) + + flux_temp%var(iVar)%dat(:) * dt_last(1) ) / (2.0*dt_out) end do ! check case default; err=20; message=trim(message)//'expect case to be ixRecangular, ixTrapezoidal'; return diff --git a/build/source/engine/varSubstepFida.f90 b/build/source/engine/varSubstepFida.f90 index d5388697a..37b0f56cc 100644 --- a/build/source/engine/varSubstepFida.f90 +++ b/build/source/engine/varSubstepFida.f90 @@ -121,6 +121,7 @@ subroutine varSubstepFida(& failedMinimumStep, & ! intent(out) : flag to denote success of substepping for a given split reduceCoupledStep, & ! intent(out) : flag to denote need to reduce the length of the coupled step tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt + dt_out, & ! intent(out) err,message) ! intent(out) : error code and error message ! --------------------------------------------------------------------------------------- ! structure allocations @@ -171,6 +172,7 @@ subroutine varSubstepFida(& logical(lgt),intent(out) :: failedMinimumStep ! flag to denote success of substepping for a given split logical(lgt),intent(out) :: reduceCoupledStep ! flag to denote need to reduce the length of the coupled step logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt + real(qp),intent(out) :: dt_out integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! --------------------------------------------------------------------------------------- @@ -340,6 +342,7 @@ subroutine varSubstepFida(& stateVecPrime, & ! intent(out): updated state vector reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt + dt_out, & ! intent(out) err,cmessage) ! intent(out): error code and error message if(err/=0)then @@ -398,7 +401,7 @@ subroutine varSubstepFida(& checkNrgBalance = .true. ! update prognostic variables - call updateProgFida(dtSubstep,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control + call updateProgFida(dt_out,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures waterBalanceError,nrgFluxModified,err,cmessage) ! output: flags and error control if(err/=0)then @@ -436,13 +439,13 @@ subroutine varSubstepFida(& ! get the total energy fluxes (modified in updateProgFida) if(nrgFluxModified .or. indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then - sumCanopyEvaporation = sumCanopyEvaporation + dtSubstep*flux_temp%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dtSubstep*flux_temp%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy = sumSenHeatCanopy + dtSubstep*flux_temp%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) + sumCanopyEvaporation = sumCanopyEvaporation + dt_out*flux_temp%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dt_out*flux_temp%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = sumSenHeatCanopy + dt_out*flux_temp%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) else - sumCanopyEvaporation = sumCanopyEvaporation + dtSubstep*flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dtSubstep*flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy = sumSenHeatCanopy + dtSubstep*flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) + sumCanopyEvaporation = sumCanopyEvaporation + dt_out*flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dt_out*flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = sumSenHeatCanopy + dt_out*flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) endif ! if energy fluxes were modified ! get the total soil compression @@ -462,7 +465,7 @@ subroutine varSubstepFida(& write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt ! increment fluxes - dt_wght = dtSubstep/dt ! (define weight applied to each splitting operation) + dt_wght = dt_out/dt ! (define weight applied to each splitting operation) do iVar=1,size(flux_meta) if(count(fluxMask%var(iVar)%dat)>0) then From 52ce520a32657b22db6f4059cdeb4a4a1137703a Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 25 May 2021 22:35:08 -0600 Subject: [PATCH 0104/1472] fixed waterBalanceErr by dt_wght=1 in varSubstepFida --- build/source/engine/coupled_em.f90 | 6 ++++-- build/source/engine/fidaSolver.f90 | 5 ++++- build/source/engine/sysSolvFida.f90 | 4 +++- build/source/engine/varSubstepFida.f90 | 4 ++-- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 8da232e5e..4f758fe56 100755 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -726,7 +726,7 @@ subroutine coupled_em(& nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers nState, & ! intent(in): total number of layers - dt_sub, & ! intent(in): length of the model sub-step + dt_sub, & ! intent(inout): length of the model sub-step (nsub==1), & ! intent(in): logical flag to denote the first substep computeVegFlux, & ! intent(in): logical flag to compute fluxes within the vegetation canopy ! input/output: data structures @@ -747,6 +747,8 @@ subroutine coupled_em(& stepFailure, & ! intent(out): flag to denote that the coupled step failed ixSolution, & ! intent(out): solution method used in this iteration err,cmessage) ! intent(out): error code and error message + + print *, 'dt_sub = ', dt_sub ! check for all errors (error recovery within opSplittin) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if @@ -943,7 +945,7 @@ subroutine coupled_em(& endif ! adjust length of the sub-step (make sure that we don't exceed the step) - dt_sub = min(data_step - dt_solv, dt_sub) + dt_sub = data_step - dt_solv !min(data_step - dt_solv, dt_sub) !print*, 'dt_sub = ', dt_sub end do substeps ! (sub-step loop) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 90983c17f..5c11c8cde 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -432,6 +432,7 @@ subroutine fidaSolver( & exit endif + ! get the last stepsize retval = FIDAGetLastStep(ida_mem, dt_last) @@ -544,6 +545,8 @@ subroutine fidaSolver( & eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial + + if(tret(1) > 1000._qp) exit end do ! while loop on one_step mode @@ -554,7 +557,7 @@ subroutine fidaSolver( & message = eqns_data%message if( .not. feasible) idaSucceeds = .false. - if( idaSucceeds .eqv. .true.)then + if(idaSucceeds)then ! copy to output data diag_data = eqns_data%diag_data flux_data = eqns_data%flux_data diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 97b7c3462..9bfa732ff 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -459,7 +459,7 @@ subroutine sysSolvFida(& indx_data, & ! intent(in): index data ! input-output: data structures diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) + flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) flux_temp, & ! intent(inout): model fluxes for a local HRU flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables @@ -483,6 +483,8 @@ subroutine sysSolvFida(& end do ! iteration over tolerances + print *, 'dt_out in sysSolveFida', dt_out + ! if(dt /= dt_out) stop 1 diff --git a/build/source/engine/varSubstepFida.f90 b/build/source/engine/varSubstepFida.f90 index 37b0f56cc..f2be99d9c 100644 --- a/build/source/engine/varSubstepFida.f90 +++ b/build/source/engine/varSubstepFida.f90 @@ -308,7 +308,7 @@ subroutine varSubstepFida(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) -! print *, 'dt in varSubstepFida = ', dtSubstep + print *, 'dt in varSubstepFida = ', dtSubstep ! ----- ! * iterative solution... ! ----------------------- @@ -465,7 +465,7 @@ subroutine varSubstepFida(& write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt ! increment fluxes - dt_wght = dt_out/dt ! (define weight applied to each splitting operation) + dt_wght = 1._qp !dt_out/dt ! (define weight applied to each splitting operation) do iVar=1,size(flux_meta) if(count(fluxMask%var(iVar)%dat)>0) then From 5ad50ec35f42b9c380aff056ccdc49d972350717 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 25 May 2021 22:41:28 -0600 Subject: [PATCH 0105/1472] extra print statements deleted --- build/source/engine/coupled_em.f90 | 1 - build/source/engine/fidaSolver.f90 | 2 +- build/source/engine/sysSolvFida.f90 | 7 ++----- build/source/engine/varSubstepFida.f90 | 1 - 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 4f758fe56..c4981a00a 100755 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -748,7 +748,6 @@ subroutine coupled_em(& ixSolution, & ! intent(out): solution method used in this iteration err,cmessage) ! intent(out): error code and error message - print *, 'dt_sub = ', dt_sub ! check for all errors (error recovery within opSplittin) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 5c11c8cde..5d64f6d64 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -546,7 +546,7 @@ subroutine fidaSolver( & eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial - if(tret(1) > 1000._qp) exit + if(tret(1) > 1700._qp) exit end do ! while loop on one_step mode diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 9bfa732ff..8cd792ca6 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -483,11 +483,8 @@ subroutine sysSolvFida(& end do ! iteration over tolerances - print *, 'dt_out in sysSolveFida', dt_out - - -! if(dt /= dt_out) stop 1 - + + ! check if fida is successful if( .not.idaSucceeds )then diff --git a/build/source/engine/varSubstepFida.f90 b/build/source/engine/varSubstepFida.f90 index f2be99d9c..9e32a6c8e 100644 --- a/build/source/engine/varSubstepFida.f90 +++ b/build/source/engine/varSubstepFida.f90 @@ -308,7 +308,6 @@ subroutine varSubstepFida(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - print *, 'dt in varSubstepFida = ', dtSubstep ! ----- ! * iterative solution... ! ----------------------- From 7b60c6d45abce81ecb30f84ac893cfba4110c6ed Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 26 May 2021 23:30:51 -0600 Subject: [PATCH 0106/1472] doesLayerDivide implemented --- build/source/engine/fidaSolver.f90 | 2 +- build/source/engine/layerDivide.f90 | 162 ++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 1 deletion(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 5d64f6d64..5c11c8cde 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -546,7 +546,7 @@ subroutine fidaSolver( & eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial - if(tret(1) > 1700._qp) exit + if(tret(1) > 1000._qp) exit end do ! while loop on one_step mode diff --git a/build/source/engine/layerDivide.f90 b/build/source/engine/layerDivide.f90 index 6127fbf1e..65dc15017 100755 --- a/build/source/engine/layerDivide.f90 +++ b/build/source/engine/layerDivide.f90 @@ -73,10 +73,171 @@ module layerDivide_module implicit none private public::layerDivide +public::doesLayerDivide contains ! *********************************************************************************************************** + ! public subroutine doesLayerDivide: add new snowfall to the system, and increase number of snow layers if needed + ! *********************************************************************************************************** + subroutine doesLayerDivide(& + ! input/output: model data structures + model_decisions, & ! intent(in): model decisions + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): type of each layer + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output + divideLayer, & ! intent(out): flag to denote that a layer was divided + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------- + ! computational modules + USE snow_utils_module,only:fracliquid,templiquid ! functions to compute temperature/liquid water + USE globalData,only:maxSnowLayers, & ! maximum number of snow layers + veryBig + implicit none + ! -------------------------------------------------------------------------------------------------------- + ! input/output: model data structures + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(inout) :: indx_data ! type of each layer + type(var_dlength),intent(inout) :: prog_data ! model prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model flux variables + ! output + logical(lgt),intent(out) :: divideLayer ! flag to denote that a layer was divided + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------- + ! define local variables + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers + integer(i4b) :: nLayers ! total number of layers + integer(i4b) :: iLayer ! layer index + integer(i4b) :: jLayer ! layer index + real(dp),dimension(4) :: zmax_lower ! lower value of maximum layer depth + real(dp),dimension(4) :: zmax_upper ! upper value of maximum layer depth + real(dp) :: zmaxCheck ! value of zmax for a given snow layer + integer(i4b) :: nCheck ! number of layers to check to divide + logical(lgt) :: createLayer ! flag to indicate we are creating a new snow layer + real(dp) :: depthOriginal ! original layer depth before sub-division (m) + real(dp),parameter :: fracTop=0.5_dp ! fraction of old layer used for the top layer + real(dp) :: surfaceLayerSoilTemp ! temperature of the top soil layer (K) + real(dp) :: maxFrozenSnowTemp ! maximum temperature when effectively all water is frozen (K) + real(dp),parameter :: unfrozenLiq=0.01_dp ! unfrozen liquid water used to compute maxFrozenSnowTemp (-) + real(dp) :: volFracWater ! volumetric fraction of total water, liquid and ice (-) + real(dp) :: fracLiq ! fraction of liquid water (-) + integer(i4b),parameter :: ixVisible=1 ! named variable to define index in array of visible part of the spectrum + integer(i4b),parameter :: ixNearIR=2 ! named variable to define index in array of near IR part of the spectrum + real(dp),parameter :: verySmall=1.e-10_dp ! a very small number (used for error checking) + ! -------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="doesLayerDivide/" + ! -------------------------------------------------------------------------------------------------------- + ! associate variables in the data structures + associate(& + ! model decisions + ix_snowLayers => model_decisions(iLookDECISIONS%snowLayers)%iDecision, & ! decision for snow combination + ! model parameters (compute layer temperature) + fc_param => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! freezing curve parameter for snow (K-1) + ! model parameters (new snow density) + newSnowDenMin => mpar_data%var(iLookPARAM%newSnowDenMin)%dat(1), & ! minimum new snow density (kg m-3) + newSnowDenMult => mpar_data%var(iLookPARAM%newSnowDenMult)%dat(1), & ! multiplier for new snow density (kg m-3) + newSnowDenScal => mpar_data%var(iLookPARAM%newSnowDenScal)%dat(1), & ! scaling factor for new snow density (K) + ! model parameters (control the depth of snow layers) + zmax => mpar_data%var(iLookPARAM%zmax)%dat(1), & ! maximum layer depth (m) + zmaxLayer1_lower => mpar_data%var(iLookPARAM%zmaxLayer1_lower)%dat(1), & ! maximum layer depth for the 1st (top) layer when only 1 layer (m) + zmaxLayer2_lower => mpar_data%var(iLookPARAM%zmaxLayer2_lower)%dat(1), & ! maximum layer depth for the 2nd layer when only 2 layers (m) + zmaxLayer3_lower => mpar_data%var(iLookPARAM%zmaxLayer3_lower)%dat(1), & ! maximum layer depth for the 3rd layer when only 3 layers (m) + zmaxLayer4_lower => mpar_data%var(iLookPARAM%zmaxLayer4_lower)%dat(1), & ! maximum layer depth for the 4th layer when only 4 layers (m) + zmaxLayer1_upper => mpar_data%var(iLookPARAM%zmaxLayer1_upper)%dat(1), & ! maximum layer depth for the 1st (top) layer when > 1 layer (m) + zmaxLayer2_upper => mpar_data%var(iLookPARAM%zmaxLayer2_upper)%dat(1), & ! maximum layer depth for the 2nd layer when > 2 layers (m) + zmaxLayer3_upper => mpar_data%var(iLookPARAM%zmaxLayer3_upper)%dat(1), & ! maximum layer depth for the 3rd layer when > 3 layers (m) + zmaxLayer4_upper => mpar_data%var(iLookPARAM%zmaxLayer4_upper)%dat(1), & ! maximum layer depth for the 4th layer when > 4 layers (m) + ! diagnostic scalar variables + scalarSnowfall => flux_data%var(iLookFLUX%scalarSnowfall)%dat(1), & ! snowfall flux (kg m-2 s-1) + scalarSnowfallTemp => diag_data%var(iLookDIAG%scalarSnowfallTemp)%dat(1), & ! computed temperature of fresh snow (K) + scalarSnowDepth => prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! total snow depth (m) + scalarSWE => prog_data%var(iLookPROG%scalarSWE)%dat(1) & ! SWE (kg m-2) + ) ! end associate statement + + ! --------------------------------------------------------------------------------------------------- + + ! initialize flag to denote that a layer was divided + divideLayer=.false. + + ! identify algorithmic control parameters to syb-divide and combine snow layers + zmax_lower = (/zmaxLayer1_lower, zmaxLayer2_lower, zmaxLayer3_lower, zmaxLayer4_lower/) + zmax_upper = (/zmaxLayer1_upper, zmaxLayer2_upper, zmaxLayer3_upper, zmaxLayer4_upper/) + + ! initialize the number of snow layers + nSnow = indx_data%var(iLookINDEX%nSnow)%dat(1) + + ! ***** special case of no snow layers + if(nSnow==0)then + + ! check if create the first snow layer + select case(ix_snowLayers) + case(sameRulesAllLayers); createLayer = (scalarSnowDepth > zmax) + case(rulesDependLayerIndex); createLayer = (scalarSnowDepth > zmaxLayer1_lower) + case default; err=20; message=trim(message)//'unable to identify option to combine/sub-divide snow layers'; return + end select ! (option to combine/sub-divide snow layers) + + ! ** create a new snow layer + if(createLayer)then + ! flag that the layers have changed + divideLayer=.true. + return + end if + + ! ***** sub-divide snow layers, if necessary + else ! if nSnow>0 + + ! identify the number of layers to check for need for sub-division + nCheck = min(nSnow, maxSnowLayers-1) ! the depth of the last layer, if it exists, does not have a maximum value + ! loop through all layers, and sub-divide a given layer, if necessary + do iLayer=1,nCheck + divideLayer=.false. + + ! identify the maximum depth of the layer + select case(ix_snowLayers) + case(sameRulesAllLayers) + if (nCheck >= maxSnowLayers-1) then + ! make sure we don't divide so make very big + zmaxCheck = veryBig + else + zmaxCheck = zmax + end if + case(rulesDependLayerIndex) + if(iLayer == nSnow)then + zmaxCheck = zmax_lower(iLayer) + else + zmaxCheck = zmax_upper(iLayer) + end if + case default; err=20; message=trim(message)//'unable to identify option to combine/sub-divide snow layers'; return + end select ! (option to combine/sub-divide snow layers) + + ! check the need to sub-divide + if(prog_data%var(iLookPROG%mLayerDepth)%dat(iLayer) > zmaxCheck)then + ! flag that layers were divided + divideLayer=.true. + return + end if ! (if sub-dividing layer) + + end do ! (looping through layers) + + end if ! if nSnow==0 + + ! end associate variables in data structure + end associate + + end subroutine doesLayerDivide + + + ! *********************************************************************************************************** ! public subroutine layerDivide: add new snowfall to the system, and increase number of snow layers if needed ! *********************************************************************************************************** subroutine layerDivide(& @@ -361,6 +522,7 @@ subroutine layerDivide(& end subroutine layerDivide + ! ************************************************************************************************ ! private subroutine addModelLayer: add an additional layer to all model vectors ! ************************************************************************************************ From d3ed4e0f4582cdb73f8bfa4c015eccdfcbdc57b2 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 26 May 2021 23:55:42 -0600 Subject: [PATCH 0107/1472] doesLayerDivide edited --- build/source/engine/layerDivide.f90 | 48 ++++++----------------------- 1 file changed, 9 insertions(+), 39 deletions(-) diff --git a/build/source/engine/layerDivide.f90 b/build/source/engine/layerDivide.f90 index 65dc15017..50b989901 100755 --- a/build/source/engine/layerDivide.f90 +++ b/build/source/engine/layerDivide.f90 @@ -78,23 +78,21 @@ module layerDivide_module contains ! *********************************************************************************************************** - ! public subroutine doesLayerDivide: add new snowfall to the system, and increase number of snow layers if needed + ! public subroutine doesLayerDivide: check to see if we need to add new snowfall to the system ! *********************************************************************************************************** subroutine doesLayerDivide(& ! input/output: model data structures model_decisions, & ! intent(in): model decisions mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): type of each layer - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU + nSnow, & ! intent(in): number of snow layers + mLayerDepth, & ! intent(in): + scalarSnowDepth, & ! intent(in) ! output divideLayer, & ! intent(out): flag to denote that a layer was divided err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------- ! computational modules - USE snow_utils_module,only:fracliquid,templiquid ! functions to compute temperature/liquid water USE globalData,only:maxSnowLayers, & ! maximum number of snow layers veryBig implicit none @@ -102,10 +100,9 @@ subroutine doesLayerDivide(& ! input/output: model data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(inout) :: indx_data ! type of each layer - type(var_dlength),intent(inout) :: prog_data ! model prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model flux variables + integer(i4b),intent(in) :: nSnow ! number of snow layers + real(dp),intent(in) :: mLayerDepth(:) + real(dp),intent(in) :: scalarSnowDepth ! output logical(lgt),intent(out) :: divideLayer ! flag to denote that a layer was divided integer(i4b),intent(out) :: err ! error code @@ -113,9 +110,6 @@ subroutine doesLayerDivide(& ! -------------------------------------------------------------------------------------------------------- ! define local variables character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b) :: nSnow ! number of snow layers - integer(i4b) :: nSoil ! number of soil layers - integer(i4b) :: nLayers ! total number of layers integer(i4b) :: iLayer ! layer index integer(i4b) :: jLayer ! layer index real(dp),dimension(4) :: zmax_lower ! lower value of maximum layer depth @@ -123,16 +117,6 @@ subroutine doesLayerDivide(& real(dp) :: zmaxCheck ! value of zmax for a given snow layer integer(i4b) :: nCheck ! number of layers to check to divide logical(lgt) :: createLayer ! flag to indicate we are creating a new snow layer - real(dp) :: depthOriginal ! original layer depth before sub-division (m) - real(dp),parameter :: fracTop=0.5_dp ! fraction of old layer used for the top layer - real(dp) :: surfaceLayerSoilTemp ! temperature of the top soil layer (K) - real(dp) :: maxFrozenSnowTemp ! maximum temperature when effectively all water is frozen (K) - real(dp),parameter :: unfrozenLiq=0.01_dp ! unfrozen liquid water used to compute maxFrozenSnowTemp (-) - real(dp) :: volFracWater ! volumetric fraction of total water, liquid and ice (-) - real(dp) :: fracLiq ! fraction of liquid water (-) - integer(i4b),parameter :: ixVisible=1 ! named variable to define index in array of visible part of the spectrum - integer(i4b),parameter :: ixNearIR=2 ! named variable to define index in array of near IR part of the spectrum - real(dp),parameter :: verySmall=1.e-10_dp ! a very small number (used for error checking) ! -------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="doesLayerDivide/" @@ -141,12 +125,6 @@ subroutine doesLayerDivide(& associate(& ! model decisions ix_snowLayers => model_decisions(iLookDECISIONS%snowLayers)%iDecision, & ! decision for snow combination - ! model parameters (compute layer temperature) - fc_param => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! freezing curve parameter for snow (K-1) - ! model parameters (new snow density) - newSnowDenMin => mpar_data%var(iLookPARAM%newSnowDenMin)%dat(1), & ! minimum new snow density (kg m-3) - newSnowDenMult => mpar_data%var(iLookPARAM%newSnowDenMult)%dat(1), & ! multiplier for new snow density (kg m-3) - newSnowDenScal => mpar_data%var(iLookPARAM%newSnowDenScal)%dat(1), & ! scaling factor for new snow density (K) ! model parameters (control the depth of snow layers) zmax => mpar_data%var(iLookPARAM%zmax)%dat(1), & ! maximum layer depth (m) zmaxLayer1_lower => mpar_data%var(iLookPARAM%zmaxLayer1_lower)%dat(1), & ! maximum layer depth for the 1st (top) layer when only 1 layer (m) @@ -156,12 +134,7 @@ subroutine doesLayerDivide(& zmaxLayer1_upper => mpar_data%var(iLookPARAM%zmaxLayer1_upper)%dat(1), & ! maximum layer depth for the 1st (top) layer when > 1 layer (m) zmaxLayer2_upper => mpar_data%var(iLookPARAM%zmaxLayer2_upper)%dat(1), & ! maximum layer depth for the 2nd layer when > 2 layers (m) zmaxLayer3_upper => mpar_data%var(iLookPARAM%zmaxLayer3_upper)%dat(1), & ! maximum layer depth for the 3rd layer when > 3 layers (m) - zmaxLayer4_upper => mpar_data%var(iLookPARAM%zmaxLayer4_upper)%dat(1), & ! maximum layer depth for the 4th layer when > 4 layers (m) - ! diagnostic scalar variables - scalarSnowfall => flux_data%var(iLookFLUX%scalarSnowfall)%dat(1), & ! snowfall flux (kg m-2 s-1) - scalarSnowfallTemp => diag_data%var(iLookDIAG%scalarSnowfallTemp)%dat(1), & ! computed temperature of fresh snow (K) - scalarSnowDepth => prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! total snow depth (m) - scalarSWE => prog_data%var(iLookPROG%scalarSWE)%dat(1) & ! SWE (kg m-2) + zmaxLayer4_upper => mpar_data%var(iLookPARAM%zmaxLayer4_upper)%dat(1) & ! maximum layer depth for the 4th layer when > 4 layers (m) ) ! end associate statement ! --------------------------------------------------------------------------------------------------- @@ -173,9 +146,6 @@ subroutine doesLayerDivide(& zmax_lower = (/zmaxLayer1_lower, zmaxLayer2_lower, zmaxLayer3_lower, zmaxLayer4_lower/) zmax_upper = (/zmaxLayer1_upper, zmaxLayer2_upper, zmaxLayer3_upper, zmaxLayer4_upper/) - ! initialize the number of snow layers - nSnow = indx_data%var(iLookINDEX%nSnow)%dat(1) - ! ***** special case of no snow layers if(nSnow==0)then @@ -221,7 +191,7 @@ subroutine doesLayerDivide(& end select ! (option to combine/sub-divide snow layers) ! check the need to sub-divide - if(prog_data%var(iLookPROG%mLayerDepth)%dat(iLayer) > zmaxCheck)then + if(mLayerDepth(iLayer) > zmaxCheck)then ! flag that layers were divided divideLayer=.true. return From 0b5aab1e0c922eac6c4c9062558e3dddfcf0e53b Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 27 May 2021 00:14:15 -0600 Subject: [PATCH 0108/1472] doesLayerMerge implemented --- build/source/engine/layerMerge.f90 | 138 +++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/build/source/engine/layerMerge.f90 b/build/source/engine/layerMerge.f90 index 6169755dd..55c418d30 100755 --- a/build/source/engine/layerMerge.f90 +++ b/build/source/engine/layerMerge.f90 @@ -62,10 +62,148 @@ module layerMerge_module implicit none private public::layerMerge +public::doesLayerMerge contains + ! ***************************************************************************************************************** + ! public subroutine doesLayerMerge: Should we merge layers? (if the thickness is less than zmin) + ! ***************************************************************************************************************** + subroutine doesLayerMerge(& + ! input/output: model data structures + tooMuchMelt, & ! intent(in): flag to force merge of snow layers + model_decisions, & ! intent(in): model decisions + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): type of each layer + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output + mergedLayers, & ! intent(out): flag to denote that layers were merged + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------- + implicit none + ! -------------------------------------------------------------------------------------------------------- + ! input/output: model data structures + logical(lgt),intent(in) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(inout) :: indx_data ! type of each layer + type(var_dlength),intent(inout) :: prog_data ! model prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model flux variables + ! output + logical(lgt),intent(out) :: mergedLayers ! flag to denote that layers were merged + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------- + ! define local variables + character(LEN=256) :: cmessage ! error message of downwind routine + real(dp),dimension(5) :: zminLayer ! minimum layer depth in each layer (m) + logical(lgt) :: removeLayer ! flag to indicate need to remove a layer + integer(i4b) :: nCheck ! number of layers to check for combination + integer(i4b) :: iSnow ! index of snow layers (looping) + integer(i4b) :: jSnow ! index of snow layer identified for combination with iSnow + integer(i4b) :: kSnow ! index of the upper layer of the two layers identified for combination + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers + integer(i4b) :: nLayers ! total number of layers + ! initialize error control + err=0; message="doesLayerMerge/" + ! -------------------------------------------------------------------------------------------------------- + ! associate variables to the data structures + associate(& + + ! model decisions + ix_snowLayers => model_decisions(iLookDECISIONS%snowLayers)%iDecision, & ! decision for snow combination + + ! model parameters (control the depth of snow layers) + zmin => mpar_data%var(iLookPARAM%zmin)%dat(1), & ! minimum layer depth (m) + zminLayer1 => mpar_data%var(iLookPARAM%zminLayer1)%dat(1), & ! minimum layer depth for the 1st (top) layer (m) + zminLayer2 => mpar_data%var(iLookPARAM%zminLayer2)%dat(1), & ! minimum layer depth for the 2nd layer (m) + zminLayer3 => mpar_data%var(iLookPARAM%zminLayer3)%dat(1), & ! minimum layer depth for the 3rd layer (m) + zminLayer4 => mpar_data%var(iLookPARAM%zminLayer4)%dat(1), & ! minimum layer depth for the 4th layer (m) + zminLayer5 => mpar_data%var(iLookPARAM%zminLayer5)%dat(1), & ! minimum layer depth for the 5th (bottom) layer (m) + + ! diagnostic scalar variables + scalarSnowDepth => prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! total snow depth (m) + scalarSWE => prog_data%var(iLookPROG%scalarSWE)%dat(1) & ! SWE (kg m-2) + + ) ! end associate statement + ! -------------------------------------------------------------------------------------------------------- + + ! identify algorithmic control parameters to syb-divide and combine snow layers + zminLayer = (/zminLayer1, zminLayer2, zminLayer3, zminLayer4, zminLayer5/) + + ! intialize the modified layers flag + mergedLayers=.false. + + ! initialize the number of snow layers + nSnow = indx_data%var(iLookINDEX%nSnow)%dat(1) + nSoil = indx_data%var(iLookINDEX%nSoil)%dat(1) + nLayers = indx_data%var(iLookINDEX%nLayers)%dat(1) + + kSnow=0 ! initialize first layer to test (top layer) + do ! attempt to remove multiple layers in a single time step (continuous do loop with exit clause) + + ! special case of >5 layers: add an offset to use maximum threshold from layer above + if(ix_snowLayers == rulesDependLayerIndex .and. nSnow > 5)then + nCheck=5 + else + nCheck=nSnow + end if + + ! loop through snow layers + do iSnow=kSnow+1,nCheck + + ! associate local variables with the information in the data structures + ! NOTE: do this here, since the layer variables are re-defined + associate(& + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat , & ! depth of each layer (m) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat , & ! volumetric fraction of ice in each layer (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat & ! volumetric fraction of liquid water in each layer (-) + ) ! (associating local variables with the information in the data structures) + + ! check if the layer depth is less than the depth threshold + select case(ix_snowLayers) + case(sameRulesAllLayers); removeLayer = (mLayerDepth(iSnow) < zmin) + case(rulesDependLayerIndex); removeLayer = (mLayerDepth(iSnow) < zminLayer(iSnow)) + case default; err=20; message=trim(message)//'unable to identify option to combine/sub-divide snow layers'; return + end select ! (option to combine/sub-divide snow layers) + + ! check if we have too much melt + ! NOTE: assume that this is the top snow layer; need more trickery to relax this assumption + if(tooMuchMelt .and. iSnow==1) removeLayer=.true. + + ! check if need to remove a layer + if(removeLayer)then + ! flag that we modified a layer + mergedLayers=.true. + return + end if ! (if layer is below the mass threshold) + + kSnow=iSnow ! ksnow is used for completion test, so include here + + ! end association of local variables with the information in the data structures + end associate + + end do ! (looping through snow layers) + + !print*, 'ksnow = ', ksnow + + ! exit if finished + if(kSnow==nCheck)exit + + end do ! continuous do + + ! end association to variables in the data structure + end associate + + end subroutine doesLayerMerge + + ! ***************************************************************************************************************** ! public subroutine layerMerge: merge layers if the thickness is less than zmin ! ***************************************************************************************************************** From edf89da3efa8b8f5027852b8dd6df3b3a324267d Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 27 May 2021 00:25:00 -0600 Subject: [PATCH 0109/1472] doesLayerMerge edited --- build/source/engine/layerDivide.f90 | 1 - build/source/engine/layerMerge.f90 | 42 ++++------------------------- 2 files changed, 5 insertions(+), 38 deletions(-) diff --git a/build/source/engine/layerDivide.f90 b/build/source/engine/layerDivide.f90 index 50b989901..1ef9d56a4 100755 --- a/build/source/engine/layerDivide.f90 +++ b/build/source/engine/layerDivide.f90 @@ -109,7 +109,6 @@ subroutine doesLayerDivide(& character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------- ! define local variables - character(LEN=256) :: cmessage ! error message of downwind routine integer(i4b) :: iLayer ! layer index integer(i4b) :: jLayer ! layer index real(dp),dimension(4) :: zmax_lower ! lower value of maximum layer depth diff --git a/build/source/engine/layerMerge.f90 b/build/source/engine/layerMerge.f90 index 55c418d30..638588dca 100755 --- a/build/source/engine/layerMerge.f90 +++ b/build/source/engine/layerMerge.f90 @@ -75,10 +75,8 @@ subroutine doesLayerMerge(& tooMuchMelt, & ! intent(in): flag to force merge of snow layers model_decisions, & ! intent(in): model decisions mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): type of each layer - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU + nSnow, & ! intent(in): + mLayerDepth, & ! intent(inout): model prognostic variables for a local HRU ! output mergedLayers, & ! intent(out): flag to denote that layers were merged err,message) ! intent(out): error control @@ -90,47 +88,34 @@ subroutine doesLayerMerge(& logical(lgt),intent(in) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt type(model_options),intent(in) :: model_decisions(:) ! model decisions type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(inout) :: indx_data ! type of each layer - type(var_dlength),intent(inout) :: prog_data ! model prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model flux variables + integer(i4b),intent(in) :: nSnow + real(dp),intent(in) :: mLayerDepth(:) ! model prognostic variables for a local HRU ! output logical(lgt),intent(out) :: mergedLayers ! flag to denote that layers were merged integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------- ! define local variables - character(LEN=256) :: cmessage ! error message of downwind routine real(dp),dimension(5) :: zminLayer ! minimum layer depth in each layer (m) logical(lgt) :: removeLayer ! flag to indicate need to remove a layer integer(i4b) :: nCheck ! number of layers to check for combination integer(i4b) :: iSnow ! index of snow layers (looping) integer(i4b) :: jSnow ! index of snow layer identified for combination with iSnow integer(i4b) :: kSnow ! index of the upper layer of the two layers identified for combination - integer(i4b) :: nSnow ! number of snow layers - integer(i4b) :: nSoil ! number of soil layers - integer(i4b) :: nLayers ! total number of layers ! initialize error control err=0; message="doesLayerMerge/" ! -------------------------------------------------------------------------------------------------------- ! associate variables to the data structures associate(& - ! model decisions ix_snowLayers => model_decisions(iLookDECISIONS%snowLayers)%iDecision, & ! decision for snow combination - ! model parameters (control the depth of snow layers) zmin => mpar_data%var(iLookPARAM%zmin)%dat(1), & ! minimum layer depth (m) zminLayer1 => mpar_data%var(iLookPARAM%zminLayer1)%dat(1), & ! minimum layer depth for the 1st (top) layer (m) zminLayer2 => mpar_data%var(iLookPARAM%zminLayer2)%dat(1), & ! minimum layer depth for the 2nd layer (m) zminLayer3 => mpar_data%var(iLookPARAM%zminLayer3)%dat(1), & ! minimum layer depth for the 3rd layer (m) zminLayer4 => mpar_data%var(iLookPARAM%zminLayer4)%dat(1), & ! minimum layer depth for the 4th layer (m) - zminLayer5 => mpar_data%var(iLookPARAM%zminLayer5)%dat(1), & ! minimum layer depth for the 5th (bottom) layer (m) - - ! diagnostic scalar variables - scalarSnowDepth => prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! total snow depth (m) - scalarSWE => prog_data%var(iLookPROG%scalarSWE)%dat(1) & ! SWE (kg m-2) - + zminLayer5 => mpar_data%var(iLookPARAM%zminLayer5)%dat(1) & ! minimum layer depth for the 5th (bottom) layer (m) ) ! end associate statement ! -------------------------------------------------------------------------------------------------------- @@ -140,11 +125,6 @@ subroutine doesLayerMerge(& ! intialize the modified layers flag mergedLayers=.false. - ! initialize the number of snow layers - nSnow = indx_data%var(iLookINDEX%nSnow)%dat(1) - nSoil = indx_data%var(iLookINDEX%nSoil)%dat(1) - nLayers = indx_data%var(iLookINDEX%nLayers)%dat(1) - kSnow=0 ! initialize first layer to test (top layer) do ! attempt to remove multiple layers in a single time step (continuous do loop with exit clause) @@ -157,15 +137,6 @@ subroutine doesLayerMerge(& ! loop through snow layers do iSnow=kSnow+1,nCheck - - ! associate local variables with the information in the data structures - ! NOTE: do this here, since the layer variables are re-defined - associate(& - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat , & ! depth of each layer (m) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat , & ! volumetric fraction of ice in each layer (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat & ! volumetric fraction of liquid water in each layer (-) - ) ! (associating local variables with the information in the data structures) - ! check if the layer depth is less than the depth threshold select case(ix_snowLayers) case(sameRulesAllLayers); removeLayer = (mLayerDepth(iSnow) < zmin) @@ -186,9 +157,6 @@ subroutine doesLayerMerge(& kSnow=iSnow ! ksnow is used for completion test, so include here - ! end association of local variables with the information in the data structures - end associate - end do ! (looping through snow layers) !print*, 'ksnow = ', ksnow From f1b2ac3963f23a41ee2a6ab84ddaf9c006d8fe9b Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 27 May 2021 11:04:25 -0600 Subject: [PATCH 0110/1472] tooMuchMelt in fidaSolver --- build/source/engine/fidaSolver.f90 | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 5c11c8cde..bfb587400 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -61,6 +61,8 @@ module fidaSolver_module USE var_lookup,only:iLookDIAG ! named variables for structure elements USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure USE var_lookup,only:iLookDERIV ! named variables for structure elements +USE var_lookup,only:iLookFLUX ! named variables for structure elements +USE var_lookup,only:iLookPARAM ! named variables for structure elements ! provide access to the derived types to define the data structures USE data_types,only:& @@ -150,6 +152,7 @@ subroutine fidaSolver( & USE tolFida_module,only:computWeightFida USE eval8summaFida_module,only:eval8summaFida USE computEnthalpy_module,only:computEnthalpy + USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy !======= Declarations ========= implicit none @@ -229,6 +232,9 @@ subroutine fidaSolver( & integer(c_long) :: nState ! total number of state variables real(dp) :: rVec(nStat) real(qp) :: tret(1) + real(dp) :: bulkDensity ! bulk density of a given layer (kg m-3) + real(dp) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) + logical(lgt) :: tooMuchMelt !======= Internals ============ @@ -546,6 +552,16 @@ subroutine fidaSolver( & eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial + + ! check the need to merge snow layers + tooMuchMelt = .false. + if(eqns_data%nSnow>0)then + ! compute the energy required to melt the top snow layer (J m-2) + bulkDensity = eqns_data%mLayerVolFracIceTrial(1)*iden_ice + eqns_data%mLayerVolFracLiqTrial(1)*iden_water + volEnthalpy = temp2ethpy(eqns_data%mLayerTempTrial(1),bulkDensity,eqns_data%mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1)) + if(-volEnthalpy < eqns_data%flux_data%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_last(1)) tooMuchMelt = .true. + endif + if(tret(1) > 1000._qp) exit end do ! while loop on one_step mode From 93e17909e219fc021af1693e96edcd01189d1741 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 27 May 2021 16:27:58 -0600 Subject: [PATCH 0111/1472] most of the computSnowDepth done --- build/source/engine/computSnowDepth.f90 | 171 ++++++++++++++++++++++++ build/source/engine/fidaSolver.f90 | 2 +- 2 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 build/source/engine/computSnowDepth.f90 diff --git a/build/source/engine/computSnowDepth.f90 b/build/source/engine/computSnowDepth.f90 new file mode 100644 index 000000000..46bce6a85 --- /dev/null +++ b/build/source/engine/computSnowDepth.f90 @@ -0,0 +1,171 @@ +module computSnowDepth_module + +! data types +USE nrtype + +! physical constants +USE multiconst,only:& + Tfreeze, & ! temperature at freezing (K) + LH_fus, & ! latent heat of fusion (J kg-1) + LH_sub, & ! latent heat of sublimation (J kg-1) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water ! intrinsic density of liquid water (kg m-3) + +! data types +USE data_types,only:& + var_i, & ! x%var(:) (i4b) + var_d, & ! x%var(:) (dp) + var_ilength, & ! x%var(:)%dat (i4b) + var_dlength, & ! x%var(:)%dat (dp) + zLookup ! x%z(:)%var(:)%lookup(:) (dp) + +! named variables for parent structures +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookFLUX ! named variables for structure elements +USE var_lookup,only:iLookPARAM ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil + + +! privacy +implicit none +private +public::computeSnowDepth + +real(dp),parameter :: verySmall=1.e-6_dp ! used as an additive constant to check if substantial difference among real numbers + +contains + + ! ************************************************************************************************ + ! public subroutine coupled_em: run the coupled energy-mass model for one timestep + ! ************************************************************************************************ + subroutine computSnowDepth(& + dt_sub, & + nSnow, & ! intent(in) + mLayerVolFracLiq, & ! intent(in) + mLayerVolFracIce, & ! intent(in) + mLayerTemp, & ! intent(in) + mLayerMeltFreez, & ! intent(in) + mpar_data, & ! intent(in) + flux_data, & ! intent(in) + ! output + mLayerDepth, & ! intent(out) + scalarSnowDepth, & ! intent(out) + ! error control + err,message) ! intent(out): error control + + USE snwDensify_module,only:snwDensify ! snow densification (compaction and cavitation) + + implicit none + + integer(i4b),intent(in) :: nSnow ! number of snow layers + real(dp),intent(in) :: mLayerVolFracLiq(:) + real(dp),intent(in) :: mLayerVolFracIce(:) + real(dp),intent(in) :: mLayerTemp(:) + real(dp),intent(in) :: mLayerMeltFreez(:) + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU + real(dp),intent(out) :: mLayerDepth(:) + real(dp),intent(out) :: scalarSnowDepth + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + + ! local variables + character(len=256) :: cmessage ! error message + integer(i4b) :: iSnow ! index of snow layers + real(dp) :: massLiquid ! mass liquid water (kg m-2) + logical(lgt) :: doLayerMerge ! flag to denote the need to merge snow layers + + + associate(& + scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) & ! sublimation from the snow surface (kg m-2 s-1) + ) + + ! * compute change in ice content of the top snow layer due to sublimation... + ! --------------------------------------------------------------------------- + ! NOTE: this is done BEFORE densification + if(nSnow > 0)then ! snow layers exist + + ! try to remove ice from the top layer + iSnow=1 + + ! save the mass of liquid water (kg m-2) + massLiquid = mLayerDepth(iSnow)*mLayerVolFracLiq(iSnow)*iden_water + + ! add/remove the depth of snow gained/lost by frost/sublimation (m) + ! NOTE: assume constant density + mLayerDepth(iSnow) = mLayerDepth(iSnow) + dt_sub*scalarSnowSublimation/(mLayerVolFracIce(iSnow)*iden_ice) + + ! check that we did not remove the entire layer + if(mLayerDepth(iSnow) < verySmall)then + doLayerMerge = .true. + else + doLayerMerge = .false. + endif + + ! update the volumetric fraction of liquid water TODO: sould work on this part later +! mLayerVolFracLiq(iSnow) = massLiquid / (mLayerDepth(iSnow)*iden_water) + + ! no snow + else + + ! no snow: check that sublimation is zero + if(abs(scalarSnowSublimation) > verySmall)then + message=trim(message)//'sublimation of snow has been computed when no snow exists' + err=20; return + end if + + end if ! (if snow layers exist) + + ! *** account for compaction and cavitation in the snowpack... + ! ------------------------------------------------------------ + if(nSnow>0)then + call snwDensify(& + ! intent(in): variables + dt_sub, & ! intent(in): time step (s) + nSnow, & ! intent(in): number of snow layers + mLayerTemp(1:nSnow), & ! intent(in): temperature of each layer (K) + mLayerMeltFreeze(1:nSnow), & ! intent(in): volumetric melt in each layer (kg m-3) + ! intent(in): parameters + mpar_data%var(iLookPARAM%densScalGrowth)%dat(1), & ! intent(in): density scaling factor for grain growth (kg-1 m3) + mpar_data%var(iLookPARAM%tempScalGrowth)%dat(1), & ! intent(in): temperature scaling factor for grain growth (K-1) + mpar_data%var(iLookPARAM%grainGrowthRate)%dat(1), & ! intent(in): rate of grain growth (s-1) + mpar_data%var(iLookPARAM%densScalOvrbdn)%dat(1), & ! intent(in): density scaling factor for overburden pressure (kg-1 m3) + mpar_data%var(iLookPARAM%tempScalOvrbdn)%dat(1), & ! intent(in): temperature scaling factor for overburden pressure (K-1) + mpar_data%var(iLookPARAM%baseViscosity)%dat(1), & ! intent(in): viscosity coefficient at T=T_frz and snow density=0 (kg m-2 s) + ! intent(inout): state variables + mLayerDepth(1:nSnow), & ! intent(inout): depth of each layer (m) + mLayerVolFracLiq(1:nSnow), & ! intent(inout): volumetric fraction of liquid water after itertations (-) + mLayerVolFracIce(1:nSnow), & ! intent(inout): volumetric fraction of ice after itertations (-) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if + end if ! if snow layers exist + + ! update coordinate variables + call calcHeight(& + ! input/output: data structures + indx_data, & ! intent(in): layer type + prog_data, & ! intent(inout): model variables for a local HRU + ! output: error control + err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + ! recompute snow depth and SWE + if(nSnow > 0)then + prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow)) + prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow)*iden_water + & + prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice) & + * prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) + end if + + end associate + + end subroutine computSnowDepth + + +end module computSnowDepth_module + diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index bfb587400..cd8e4b24d 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -189,7 +189,7 @@ subroutine fidaSolver( & ! input-output: data structures type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: flux_temp ! model fluxes for a local HRU - type(var_dlength),intent(inout):: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout):: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: flux_sum type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables real(dp),intent(inout) :: mLayerCmpress_sum(:) From d156c6246d033acf420fea05d808823b1df69a9b Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 27 May 2021 23:01:20 -0600 Subject: [PATCH 0112/1472] some edits on computSnowDepth --- build/source/engine/computSnowDepth.f90 | 33 +++++++++---------------- build/source/engine/fidaSolver.f90 | 14 ++++++++++- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/build/source/engine/computSnowDepth.f90 b/build/source/engine/computSnowDepth.f90 index 46bce6a85..794c0dfb8 100644 --- a/build/source/engine/computSnowDepth.f90 +++ b/build/source/engine/computSnowDepth.f90 @@ -43,14 +43,15 @@ module computSnowDepth_module ! public subroutine coupled_em: run the coupled energy-mass model for one timestep ! ************************************************************************************************ subroutine computSnowDepth(& - dt_sub, & - nSnow, & ! intent(in) - mLayerVolFracLiq, & ! intent(in) - mLayerVolFracIce, & ! intent(in) - mLayerTemp, & ! intent(in) - mLayerMeltFreez, & ! intent(in) - mpar_data, & ! intent(in) - flux_data, & ! intent(in) + dt_sub, & + nSnow, & ! intent(in) + mLayerVolFracLiq, & ! intent(in) + mLayerVolFracIce, & ! intent(in) + mLayerTemp, & ! intent(in) + mLayerMeltFreez, & ! intent(in) + mpar_data, & ! intent(in) + flux_data, & ! intent(in) + diag_data, & ! intent(in) ! output mLayerDepth, & ! intent(out) scalarSnowDepth, & ! intent(out) @@ -128,7 +129,7 @@ subroutine computSnowDepth(& dt_sub, & ! intent(in): time step (s) nSnow, & ! intent(in): number of snow layers mLayerTemp(1:nSnow), & ! intent(in): temperature of each layer (K) - mLayerMeltFreeze(1:nSnow), & ! intent(in): volumetric melt in each layer (kg m-3) + diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(1:nSnow), & ! intent(in): volumetric melt in each layer (kg m-3) ! intent(in): parameters mpar_data%var(iLookPARAM%densScalGrowth)%dat(1), & ! intent(in): density scaling factor for grain growth (kg-1 m3) mpar_data%var(iLookPARAM%tempScalGrowth)%dat(1), & ! intent(in): temperature scaling factor for grain growth (K-1) @@ -145,21 +146,9 @@ subroutine computSnowDepth(& if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if end if ! if snow layers exist - ! update coordinate variables - call calcHeight(& - ! input/output: data structures - indx_data, & ! intent(in): layer type - prog_data, & ! intent(inout): model variables for a local HRU - ! output: error control - err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - ! recompute snow depth and SWE if(nSnow > 0)then - prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow)) - prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow)*iden_water + & - prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice) & - * prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) + scalarSnowDepth = sum( mLayerDepth(1:nSnow) ) end if end associate diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index cd8e4b24d..ff7f0fb5a 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -153,6 +153,7 @@ subroutine fidaSolver( & USE eval8summaFida_module,only:eval8summaFida USE computEnthalpy_module,only:computEnthalpy USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy +! USE var_derive_module,only:calcHeight ! module to calculate height at layer interfaces and layer mid-point !======= Declarations ========= implicit none @@ -561,8 +562,19 @@ subroutine fidaSolver( & volEnthalpy = temp2ethpy(eqns_data%mLayerTempTrial(1),bulkDensity,eqns_data%mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1)) if(-volEnthalpy < eqns_data%flux_data%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_last(1)) tooMuchMelt = .true. endif + + + + ! update coordinate variables +! call calcHeight(& + ! input/output: data structures +! indx_data, & ! intent(in): layer type +! prog_data, & ! intent(inout): model variables for a local HRU + ! output: error control +! err,cmessage) +! if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - if(tret(1) > 1000._qp) exit +! if(tret(1) > 1000._qp) exit end do ! while loop on one_step mode From 8834c7b374bba31c22e2584a22a206b591662676 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 27 May 2021 23:07:59 -0600 Subject: [PATCH 0113/1472] computSnowDepth debugged --- build/Makefile | 1 + build/source/engine/computSnowDepth.f90 | 17 +++++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/build/Makefile b/build/Makefile index bd1336fb3..a6d7fff1f 100644 --- a/build/Makefile +++ b/build/Makefile @@ -236,6 +236,7 @@ SUMMA_SOLVER= \ evalJacFida.f90 \ nonlinSolvFida.f90 \ findDiscontinuity.f90 \ + computSnowDepth.f90 \ fidaSolver.f90 \ sysSolvFida.f90 \ varSubstep.f90 \ diff --git a/build/source/engine/computSnowDepth.f90 b/build/source/engine/computSnowDepth.f90 index 794c0dfb8..bf71cc84c 100644 --- a/build/source/engine/computSnowDepth.f90 +++ b/build/source/engine/computSnowDepth.f90 @@ -33,7 +33,7 @@ module computSnowDepth_module ! privacy implicit none private -public::computeSnowDepth +public::computSnowDepth real(dp),parameter :: verySmall=1.e-6_dp ! used as an additive constant to check if substantial difference among real numbers @@ -45,8 +45,8 @@ module computSnowDepth_module subroutine computSnowDepth(& dt_sub, & nSnow, & ! intent(in) - mLayerVolFracLiq, & ! intent(in) - mLayerVolFracIce, & ! intent(in) + mLayerVolFracLiq, & ! intent(inout) + mLayerVolFracIce, & ! intent(inout) mLayerTemp, & ! intent(in) mLayerMeltFreez, & ! intent(in) mpar_data, & ! intent(in) @@ -61,14 +61,15 @@ subroutine computSnowDepth(& USE snwDensify_module,only:snwDensify ! snow densification (compaction and cavitation) implicit none - + real(qp),intent(in) :: dt_sub integer(i4b),intent(in) :: nSnow ! number of snow layers - real(dp),intent(in) :: mLayerVolFracLiq(:) - real(dp),intent(in) :: mLayerVolFracIce(:) + real(dp),intent(inout) :: mLayerVolFracLiq(:) + real(dp),intent(inout) :: mLayerVolFracIce(:) real(dp),intent(in) :: mLayerTemp(:) real(dp),intent(in) :: mLayerMeltFreez(:) type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU real(dp),intent(out) :: mLayerDepth(:) real(dp),intent(out) :: scalarSnowDepth integer(i4b),intent(out) :: err ! error code @@ -107,8 +108,8 @@ subroutine computSnowDepth(& doLayerMerge = .false. endif - ! update the volumetric fraction of liquid water TODO: sould work on this part later -! mLayerVolFracLiq(iSnow) = massLiquid / (mLayerDepth(iSnow)*iden_water) + ! update the volumetric fraction of liquid water + mLayerVolFracLiq(iSnow) = massLiquid / (mLayerDepth(iSnow)*iden_water) ! no snow else From 404ad925908637f4c0b5fc1a773c8cdf12cb01f3 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 27 May 2021 23:15:32 -0600 Subject: [PATCH 0114/1472] more edit --- build/source/engine/fidaSolver.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index ff7f0fb5a..9fe748a8e 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -153,6 +153,7 @@ subroutine fidaSolver( & USE eval8summaFida_module,only:eval8summaFida USE computEnthalpy_module,only:computEnthalpy USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy + USE computSnowDepth_module,only:computSnowDepth ! USE var_derive_module,only:calcHeight ! module to calculate height at layer interfaces and layer mid-point !======= Declarations ========= From 8715b90dd01e0b885f1bfad40f7e02dc9072cfc2 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 27 May 2021 23:55:08 -0600 Subject: [PATCH 0115/1472] computSnowDepth in fidaSolver but some issues should be fixed --- build/source/engine/computSnowDepth.f90 | 2 -- build/source/engine/fidaSolver.f90 | 40 ++++++++++++++++++------- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/build/source/engine/computSnowDepth.f90 b/build/source/engine/computSnowDepth.f90 index bf71cc84c..f2c361a6f 100644 --- a/build/source/engine/computSnowDepth.f90 +++ b/build/source/engine/computSnowDepth.f90 @@ -48,7 +48,6 @@ subroutine computSnowDepth(& mLayerVolFracLiq, & ! intent(inout) mLayerVolFracIce, & ! intent(inout) mLayerTemp, & ! intent(in) - mLayerMeltFreez, & ! intent(in) mpar_data, & ! intent(in) flux_data, & ! intent(in) diag_data, & ! intent(in) @@ -66,7 +65,6 @@ subroutine computSnowDepth(& real(dp),intent(inout) :: mLayerVolFracLiq(:) real(dp),intent(inout) :: mLayerVolFracIce(:) real(dp),intent(in) :: mLayerTemp(:) - real(dp),intent(in) :: mLayerMeltFreez(:) type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 9fe748a8e..313f1b1b5 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -237,6 +237,8 @@ subroutine fidaSolver( & real(dp) :: bulkDensity ! bulk density of a given layer (kg m-3) real(dp) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) logical(lgt) :: tooMuchMelt + real(dp) :: mLayerDepth(nLayers) + real(dp) :: scalarSnowDepth !======= Internals ============ @@ -424,6 +426,7 @@ subroutine fidaSolver( & eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) eqns_data%ixSaturation = ixSaturation + mLayerDepth(:) = prog_data%var(iLookPROG%mLayerDepth)%dat(:) !********************************************************************************** !****************************** Main Solver *************************************** @@ -541,6 +544,33 @@ subroutine fidaSolver( & ! sum of mLayerCmpress mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) + + + call computSnowDepth(& + dt_last(1), & + eqns_data%nSnow, & ! intent(in) + eqns_data%mLayerVolFracLiqTrial, & ! intent(inout) + eqns_data%mLayerVolFracIceTrial, & ! intent(inout) + eqns_data%mLayerTempTrial, & ! intent(in) + eqns_data%mpar_data, & ! intent(in) + eqns_data%flux_data, & ! intent(in) + eqns_data%diag_data, & ! intent(in) + ! output + mLayerDepth, & ! intent(out) + scalarSnowDepth, & ! intent(out) + ! error control + err,message) ! intent(out): error control + if(err/=0)then; err=55; return; end if + + ! check the need to merge snow layers + tooMuchMelt = .false. + if(eqns_data%nSnow>0)then + ! compute the energy required to melt the top snow layer (J m-2) + bulkDensity = eqns_data%mLayerVolFracIceTrial(1)*iden_ice + eqns_data%mLayerVolFracLiqTrial(1)*iden_water + volEnthalpy = temp2ethpy(eqns_data%mLayerTempTrial(1),bulkDensity,eqns_data%mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1)) + if(-volEnthalpy < eqns_data%flux_data%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_last(1)) tooMuchMelt = .true. + endif + ! save required quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial @@ -555,16 +585,6 @@ subroutine fidaSolver( & eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial - ! check the need to merge snow layers - tooMuchMelt = .false. - if(eqns_data%nSnow>0)then - ! compute the energy required to melt the top snow layer (J m-2) - bulkDensity = eqns_data%mLayerVolFracIceTrial(1)*iden_ice + eqns_data%mLayerVolFracLiqTrial(1)*iden_water - volEnthalpy = temp2ethpy(eqns_data%mLayerTempTrial(1),bulkDensity,eqns_data%mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1)) - if(-volEnthalpy < eqns_data%flux_data%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_last(1)) tooMuchMelt = .true. - endif - - ! update coordinate variables ! call calcHeight(& From 261cfd8cee105e0ccb3a420c8611e2f7be1ff715 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 28 May 2021 00:33:50 -0600 Subject: [PATCH 0116/1472] calcHeight in fidaSolver --- build/source/engine/fidaSolver.f90 | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 313f1b1b5..fa8ccb2d0 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -154,7 +154,7 @@ subroutine fidaSolver( & USE computEnthalpy_module,only:computEnthalpy USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy USE computSnowDepth_module,only:computSnowDepth -! USE var_derive_module,only:calcHeight ! module to calculate height at layer interfaces and layer mid-point + USE var_derive_module,only:calcHeight ! module to calculate height at layer interfaces and layer mid-point !======= Declarations ========= implicit none @@ -556,11 +556,20 @@ subroutine fidaSolver( & eqns_data%flux_data, & ! intent(in) eqns_data%diag_data, & ! intent(in) ! output - mLayerDepth, & ! intent(out) + eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(out) scalarSnowDepth, & ! intent(out) ! error control err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if + + ! update coordinate variables + call calcHeight(& + ! input/output: data structures + eqns_data%indx_data, & ! intent(in): layer type + eqns_data%prog_data, & ! intent(inout): model variables for a local HRU + ! output: error control + err,message) + if(err/=0)then; err=20; return; end if ! check the need to merge snow layers tooMuchMelt = .false. @@ -585,16 +594,6 @@ subroutine fidaSolver( & eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial - - ! update coordinate variables -! call calcHeight(& - ! input/output: data structures -! indx_data, & ! intent(in): layer type -! prog_data, & ! intent(inout): model variables for a local HRU - ! output: error control -! err,cmessage) -! if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - ! if(tret(1) > 1000._qp) exit end do ! while loop on one_step mode From 436f773f783b5e2334c0e4ea1267b66fb66662b6 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 28 May 2021 00:42:11 -0600 Subject: [PATCH 0117/1472] some edit --- build/source/engine/fidaSolver.f90 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index fa8ccb2d0..1de761135 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -155,6 +155,8 @@ subroutine fidaSolver( & USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy USE computSnowDepth_module,only:computSnowDepth USE var_derive_module,only:calcHeight ! module to calculate height at layer interfaces and layer mid-point + USE layerDivide_module,only:doesLayerDivide + Use layerMerge_module,only:doesLayerMerge !======= Declarations ========= implicit none From ee220e27d25247db12f5ade1fc00229abbe4d932 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 28 May 2021 00:49:08 -0600 Subject: [PATCH 0118/1472] doesLayerDivide in fidaSolver --- build/source/engine/fidaSolver.f90 | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 1de761135..fedd80005 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -241,6 +241,7 @@ subroutine fidaSolver( & logical(lgt) :: tooMuchMelt real(dp) :: mLayerDepth(nLayers) real(dp) :: scalarSnowDepth + logical(lgt) :: divideLayer !======= Internals ============ @@ -581,6 +582,20 @@ subroutine fidaSolver( & volEnthalpy = temp2ethpy(eqns_data%mLayerTempTrial(1),bulkDensity,eqns_data%mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1)) if(-volEnthalpy < eqns_data%flux_data%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_last(1)) tooMuchMelt = .true. endif + + if(tooMuchMelt) exit + + call doesLayerDivide(& + ! input/output: model data structures + model_decisions, & ! intent(in): model decisions + eqns_data%mpar_data, & ! intent(in): model parameters + eqns_data%nSnow, & ! intent(in): number of snow layers + eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): + scalarSnowDepth, & ! intent(in) + ! output + divideLayer, & ! intent(out): flag to denote that a layer was divided + err,message) ! intent(out): error control + if(divideLayer) exit ! save required quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial From d0b4b2d932fb4a4ed8d54e94a6d38dccaf35d33f Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 28 May 2021 01:06:05 -0600 Subject: [PATCH 0119/1472] doesLayerMerge in fidaSolver --- build/source/engine/fidaSolver.f90 | 72 +++++++++++++++++------------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index fedd80005..1002ae624 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -242,6 +242,7 @@ subroutine fidaSolver( & real(dp) :: mLayerDepth(nLayers) real(dp) :: scalarSnowDepth logical(lgt) :: divideLayer + logical(lgt) :: mergedLayers !======= Internals ============ @@ -550,19 +551,19 @@ subroutine fidaSolver( & call computSnowDepth(& - dt_last(1), & - eqns_data%nSnow, & ! intent(in) - eqns_data%mLayerVolFracLiqTrial, & ! intent(inout) - eqns_data%mLayerVolFracIceTrial, & ! intent(inout) - eqns_data%mLayerTempTrial, & ! intent(in) - eqns_data%mpar_data, & ! intent(in) - eqns_data%flux_data, & ! intent(in) - eqns_data%diag_data, & ! intent(in) + dt_last(1), & ! intent(in) + eqns_data%nSnow, & ! intent(in) + eqns_data%mLayerVolFracLiqTrial, & ! intent(inout) + eqns_data%mLayerVolFracIceTrial, & ! intent(inout) + eqns_data%mLayerTempTrial, & ! intent(in) + eqns_data%mpar_data, & ! intent(in) + eqns_data%flux_data, & ! intent(in) + eqns_data%diag_data, & ! intent(in) ! output - eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(out) - scalarSnowDepth, & ! intent(out) + eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(out) + scalarSnowDepth, & ! intent(out) ! error control - err,message) ! intent(out): error control + err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if ! update coordinate variables @@ -574,28 +575,41 @@ subroutine fidaSolver( & err,message) if(err/=0)then; err=20; return; end if - ! check the need to merge snow layers - tooMuchMelt = .false. - if(eqns_data%nSnow>0)then - ! compute the energy required to melt the top snow layer (J m-2) - bulkDensity = eqns_data%mLayerVolFracIceTrial(1)*iden_ice + eqns_data%mLayerVolFracLiqTrial(1)*iden_water - volEnthalpy = temp2ethpy(eqns_data%mLayerTempTrial(1),bulkDensity,eqns_data%mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1)) - if(-volEnthalpy < eqns_data%flux_data%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_last(1)) tooMuchMelt = .true. - endif + ! check the need to merge snow layers + tooMuchMelt = .false. + if(eqns_data%nSnow>0)then + ! compute the energy required to melt the top snow layer (J m-2) + bulkDensity = eqns_data%mLayerVolFracIceTrial(1)*iden_ice + eqns_data%mLayerVolFracLiqTrial(1)*iden_water + volEnthalpy = temp2ethpy(eqns_data%mLayerTempTrial(1),bulkDensity,eqns_data%mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1)) + if(-volEnthalpy < eqns_data%flux_data%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_last(1)) tooMuchMelt = .true. + endif - if(tooMuchMelt) exit + if(tooMuchMelt) exit - call doesLayerDivide(& + call doesLayerDivide(& ! input/output: model data structures - model_decisions, & ! intent(in): model decisions - eqns_data%mpar_data, & ! intent(in): model parameters - eqns_data%nSnow, & ! intent(in): number of snow layers - eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): - scalarSnowDepth, & ! intent(in) + model_decisions, & ! intent(in): model decisions + eqns_data%mpar_data, & ! intent(in): model parameters + eqns_data%nSnow, & ! intent(in): number of snow layers + eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): + scalarSnowDepth, & ! intent(in) ! output - divideLayer, & ! intent(out): flag to denote that a layer was divided - err,message) ! intent(out): error control + divideLayer, & ! intent(out): flag to denote that a layer was divided + err,message) ! intent(out): error control if(divideLayer) exit + + call doesLayerMerge(& + ! input/output: model data structures + tooMuchMelt, & ! intent(in): flag to force merge of snow layers + model_decisions, & ! intent(in): model decisions + eqns_data%mpar_data, & ! intent(in): model parameters + eqns_data%nSnow, & ! intent(in): + eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(inout): model prognostic variables for a local HRU + ! output + mergedLayers, & ! intent(out): flag to denote that layers were merged + err,message) ! intent(out): error control + + if(mergedLayers) exit ! save required quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial @@ -610,8 +624,6 @@ subroutine fidaSolver( & eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial - -! if(tret(1) > 1000._qp) exit end do ! while loop on one_step mode From 820ad340b2062d220b2891314b5043d57aeb687e Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 28 May 2021 01:16:57 -0600 Subject: [PATCH 0120/1472] minor edit on fidaSolver --- build/source/engine/fidaSolver.f90 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 1002ae624..38c85577b 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -586,6 +586,7 @@ subroutine fidaSolver( & if(tooMuchMelt) exit + divideLayer = .false. call doesLayerDivide(& ! input/output: model data structures model_decisions, & ! intent(in): model decisions @@ -598,6 +599,8 @@ subroutine fidaSolver( & err,message) ! intent(out): error control if(divideLayer) exit + + mergedLayers = .false. call doesLayerMerge(& ! input/output: model data structures tooMuchMelt, & ! intent(in): flag to force merge of snow layers From de3f15b9a3a9b0c8a2f542c79efc4882fbca25ff Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 28 May 2021 01:23:55 -0600 Subject: [PATCH 0121/1472] more edit on fidaSolver --- build/source/engine/computSnowDepth.f90 | 8 -------- build/source/engine/fidaSolver.f90 | 9 +++------ 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/build/source/engine/computSnowDepth.f90 b/build/source/engine/computSnowDepth.f90 index f2c361a6f..7aad009ef 100644 --- a/build/source/engine/computSnowDepth.f90 +++ b/build/source/engine/computSnowDepth.f90 @@ -77,7 +77,6 @@ subroutine computSnowDepth(& character(len=256) :: cmessage ! error message integer(i4b) :: iSnow ! index of snow layers real(dp) :: massLiquid ! mass liquid water (kg m-2) - logical(lgt) :: doLayerMerge ! flag to denote the need to merge snow layers associate(& @@ -99,13 +98,6 @@ subroutine computSnowDepth(& ! NOTE: assume constant density mLayerDepth(iSnow) = mLayerDepth(iSnow) + dt_sub*scalarSnowSublimation/(mLayerVolFracIce(iSnow)*iden_ice) - ! check that we did not remove the entire layer - if(mLayerDepth(iSnow) < verySmall)then - doLayerMerge = .true. - else - doLayerMerge = .false. - endif - ! update the volumetric fraction of liquid water mLayerVolFracLiq(iSnow) = massLiquid / (mLayerDepth(iSnow)*iden_water) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 38c85577b..a7d768ab6 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -514,11 +514,6 @@ subroutine fidaSolver( & rVec, & ! intent(out): residual vector eqns_data%err,eqns_data%message) ! intent(out): error control -! if(tempPrintFlag)then -! print *, 'rVec = ', rVec -! print *, '----------------------------------------------------------------' -! end if - select case(ixQuadrature) ! sum of flux @@ -574,10 +569,12 @@ subroutine fidaSolver( & ! output: error control err,message) if(err/=0)then; err=20; return; end if - + ! check the need to merge snow layers tooMuchMelt = .false. if(eqns_data%nSnow>0)then + ! check that we did not remove the entire layer + if(eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat(1) < 1.e-6_dp) exit ! compute the energy required to melt the top snow layer (J m-2) bulkDensity = eqns_data%mLayerVolFracIceTrial(1)*iden_ice + eqns_data%mLayerVolFracLiqTrial(1)*iden_water volEnthalpy = temp2ethpy(eqns_data%mLayerTempTrial(1),bulkDensity,eqns_data%mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1)) From 08d4aee3bbcad1b26c03868e33eaec40938918e1 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 28 May 2021 23:13:28 -0600 Subject: [PATCH 0122/1472] scalarSWE in computSnowLayer --- build/source/engine/computSnowDepth.f90 | 3 +++ build/source/engine/fidaSolver.f90 | 25 +++++++++++++------------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/build/source/engine/computSnowDepth.f90 b/build/source/engine/computSnowDepth.f90 index 7aad009ef..43a4afad0 100644 --- a/build/source/engine/computSnowDepth.f90 +++ b/build/source/engine/computSnowDepth.f90 @@ -54,6 +54,7 @@ subroutine computSnowDepth(& ! output mLayerDepth, & ! intent(out) scalarSnowDepth, & ! intent(out) + scalarSWE, & ! intetn(out) ! error control err,message) ! intent(out): error control @@ -70,6 +71,7 @@ subroutine computSnowDepth(& type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU real(dp),intent(out) :: mLayerDepth(:) real(dp),intent(out) :: scalarSnowDepth + real(dp),intent(out) :: scalarSWE integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -140,6 +142,7 @@ subroutine computSnowDepth(& ! recompute snow depth and SWE if(nSnow > 0)then scalarSnowDepth = sum( mLayerDepth(1:nSnow) ) + scalarSWE = sum( (mLayerVolFracLiq(1:nSnow)*iden_water + mLayerVolFracIce(1:nSnow)*iden_ice) * mLayerDepth(1:nSnow) ) end if end associate diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index a7d768ab6..b2169e947 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -546,19 +546,20 @@ subroutine fidaSolver( & call computSnowDepth(& - dt_last(1), & ! intent(in) - eqns_data%nSnow, & ! intent(in) - eqns_data%mLayerVolFracLiqTrial, & ! intent(inout) - eqns_data%mLayerVolFracIceTrial, & ! intent(inout) - eqns_data%mLayerTempTrial, & ! intent(in) - eqns_data%mpar_data, & ! intent(in) - eqns_data%flux_data, & ! intent(in) - eqns_data%diag_data, & ! intent(in) + dt_last(1), & ! intent(in) + eqns_data%nSnow, & ! intent(in) + eqns_data%mLayerVolFracLiqTrial, & ! intent(inout) + eqns_data%mLayerVolFracIceTrial, & ! intent(inout) + eqns_data%mLayerTempTrial, & ! intent(in) + eqns_data%mpar_data, & ! intent(in) + eqns_data%flux_data, & ! intent(in) + eqns_data%diag_data, & ! intent(in) ! output - eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(out) - scalarSnowDepth, & ! intent(out) + eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(out) + eqns_data%prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(out) + eqns_data%prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! error control - err,message) ! intent(out): error control + err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if ! update coordinate variables @@ -590,7 +591,7 @@ subroutine fidaSolver( & eqns_data%mpar_data, & ! intent(in): model parameters eqns_data%nSnow, & ! intent(in): number of snow layers eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): - scalarSnowDepth, & ! intent(in) + eqns_data%prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(in) ! output divideLayer, & ! intent(out): flag to denote that a layer was divided err,message) ! intent(out): error control From bbafcc948519a9a79877b857f9919d4b07a22a4b Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sat, 29 May 2021 00:30:33 -0600 Subject: [PATCH 0123/1472] checkSnow flag in fidaSolver --- build/source/engine/fidaSolver.f90 | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index b2169e947..69bfdb445 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -243,6 +243,7 @@ subroutine fidaSolver( & real(dp) :: scalarSnowDepth logical(lgt) :: divideLayer logical(lgt) :: mergedLayers + logical(lgt),parameter :: checkSnow = .false. !======= Internals ============ @@ -544,7 +545,7 @@ subroutine fidaSolver( & mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) - + if(checkSnow)then call computSnowDepth(& dt_last(1), & ! intent(in) eqns_data%nSnow, & ! intent(in) @@ -581,8 +582,12 @@ subroutine fidaSolver( & volEnthalpy = temp2ethpy(eqns_data%mLayerTempTrial(1),bulkDensity,eqns_data%mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1)) if(-volEnthalpy < eqns_data%flux_data%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_last(1)) tooMuchMelt = .true. endif + - if(tooMuchMelt) exit + if(tooMuchMelt)then + print *, 'tooMuchMelt, tret = ', tret(1) + exit + endif divideLayer = .false. call doesLayerDivide(& @@ -595,7 +600,10 @@ subroutine fidaSolver( & ! output divideLayer, & ! intent(out): flag to denote that a layer was divided err,message) ! intent(out): error control - if(divideLayer) exit + if(divideLayer) then + print *, 'divideLayer, tret = ', tret(1) + exit + endif mergedLayers = .false. @@ -610,7 +618,12 @@ subroutine fidaSolver( & mergedLayers, & ! intent(out): flag to denote that layers were merged err,message) ! intent(out): error control - if(mergedLayers) exit + if(mergedLayers) then + print *, 'mergedLayers, tret = ', tret(1) + exit + endif + + endif ! checkSnow ! save required quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial From eca64f40ca79159e99d0c755a8a282e396a99b60 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sat, 29 May 2021 01:24:54 -0600 Subject: [PATCH 0124/1472] more dt_out in varSubstepFida --- build/source/engine/fidaSolver.f90 | 2 ++ build/source/engine/varSubstepFida.f90 | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 69bfdb445..64d8d0251 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -624,6 +624,8 @@ subroutine fidaSolver( & endif endif ! checkSnow + + if(tret(1) > 1000) exit ! save required quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial diff --git a/build/source/engine/varSubstepFida.f90 b/build/source/engine/varSubstepFida.f90 index 9e32a6c8e..fa6314013 100644 --- a/build/source/engine/varSubstepFida.f90 +++ b/build/source/engine/varSubstepFida.f90 @@ -512,9 +512,9 @@ subroutine varSubstepFida(& end do substeps ! time steps for variable-dependent sub-stepping ! save the energy fluxes - flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt ! canopy evaporation/condensation (kg m-2 s-1) - flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt ! sensible heat flux from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt_out ! canopy evaporation/condensation (kg m-2 s-1) + flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt_out ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt_out ! sensible heat flux from the canopy to the canopy air space (W m-2) ! save the soil compression diagnostics diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress From 26afb37f8c11816c53b4d7a2314c07586d04fa26 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sat, 29 May 2021 13:18:16 -0600 Subject: [PATCH 0125/1472] implctMelt in fidaSolver --- build/source/engine/eval8summaFida.f90 | 2 +- build/source/engine/fidaSolver.f90 | 88 +++++++++++++++++++++++++- 2 files changed, 86 insertions(+), 4 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index cd4784b52..83d101dd0 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -265,7 +265,7 @@ subroutine eval8summaFida(& real(qp) :: scalarCanopyEnthalpyPrime real(dp) :: scalarCanopyCmTrial real(dp),dimension(nLayers) :: mLayerCmTrial - logical(lgt),parameter :: updateCp=.false. + logical(lgt),parameter :: updateCp=.true. logical(lgt),parameter :: needCm=.false. diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 64d8d0251..00f2b1a44 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -243,7 +243,7 @@ subroutine fidaSolver( & real(dp) :: scalarSnowDepth logical(lgt) :: divideLayer logical(lgt) :: mergedLayers - logical(lgt),parameter :: checkSnow = .false. + logical(lgt),parameter :: checkSnow = .true. !======= Internals ============ @@ -623,9 +623,28 @@ subroutine fidaSolver( & exit endif + ! *** compute melt of the "snow without a layer"... + ! ------------------------------------------------- + ! NOTE: forms a surface melt pond, which drains into the upper-most soil layer through the time step + ! (check for the special case of "snow without a layer") + if(eqns_data%nSnow==0)then + call implctMelt(& + ! input/output: integrated snowpack properties + eqns_data%prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(inout): snow water equivalent (kg m-2) + eqns_data%prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(inout): snow depth (m) + eqns_data%prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1), & ! intent(inout): surface melt pond (kg m-2) + ! input/output: properties of the upper-most soil layer + eqns_data%mLayerTempTrial(nSnow+1), & ! intent(inout): surface layer temperature (K) + eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1), & ! intent(inout): surface layer depth (m) + eqns_data%diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat(nSnow+1),& ! intent(inout): surface layer volumetric heat capacity (J m-3 K-1) + ! output: error control + err,message) ! intent(out): error control + if(err/=0)then; err=20; return; end if + end if + endif ! checkSnow - if(tret(1) > 1000) exit +! if(tret(1) > 1000) exit ! save required quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial @@ -786,7 +805,70 @@ subroutine setSolverParams(dt,ida_mem,retval) ! retval = FIDASetLinearSolutionScaling(ida_mem, 0) ! if (retval /= 0) return -end subroutine setSolverParams +end subroutine setSolverParams + + ! ********************************************************************************************************* + ! private subroutine implctMelt: compute melt of the "snow without a layer" + ! ********************************************************************************************************* + subroutine implctMelt(& + ! input/output: integrated snowpack properties + scalarSWE, & ! intent(inout): snow water equivalent (kg m-2) + scalarSnowDepth, & ! intent(inout): snow depth (m) + scalarSfcMeltPond, & ! intent(inout): surface melt pond (kg m-2) + ! input/output: properties of the upper-most soil layer + soilTemp, & ! intent(inout): surface layer temperature (K) + soilDepth, & ! intent(inout): surface layer depth (m) + soilHeatcap, & ! intent(inout): surface layer volumetric heat capacity (J m-3 K-1) + ! output: error control + err,message ) ! intent(out): error control + implicit none + ! input/output: integrated snowpack properties + real(dp),intent(inout) :: scalarSWE ! snow water equivalent (kg m-2) + real(dp),intent(inout) :: scalarSnowDepth ! snow depth (m) + real(dp),intent(inout) :: scalarSfcMeltPond ! surface melt pond (kg m-2) + ! input/output: properties of the upper-most soil layer + real(dp),intent(inout) :: soilTemp ! surface layer temperature (K) + real(dp),intent(inout) :: soilDepth ! surface layer depth (m) + real(dp),intent(inout) :: soilHeatcap ! surface layer volumetric heat capacity (J m-3 K-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local variables + real(dp) :: nrgRequired ! energy required to melt all the snow (J m-2) + real(dp) :: nrgAvailable ! energy available to melt the snow (J m-2) + real(dp) :: snwDensity ! snow density (kg m-3) + ! initialize error control + err=0; message='implctMelt/' + + if(scalarSWE > 0._dp)then + ! only melt if temperature of the top soil layer is greater than Tfreeze + if(soilTemp > Tfreeze)then + ! compute the energy required to melt all the snow (J m-2) + nrgRequired = scalarSWE*LH_fus + ! compute the energy available to melt the snow (J m-2) + nrgAvailable = soilHeatcap*(soilTemp - Tfreeze)*soilDepth + ! compute the snow density (not saved) + snwDensity = scalarSWE/scalarSnowDepth + ! compute the amount of melt, and update SWE (kg m-2) + if(nrgAvailable > nrgRequired)then + scalarSfcMeltPond = scalarSWE + scalarSWE = 0._dp + else + scalarSfcMeltPond = nrgAvailable/LH_fus + scalarSWE = scalarSWE - scalarSfcMeltPond + end if + ! update depth + scalarSnowDepth = scalarSWE/snwDensity + ! update temperature of the top soil layer (K) + soilTemp = soilTemp - (LH_fus*scalarSfcMeltPond/soilDepth)/soilHeatcap + else ! melt is zero if the temperature of the top soil layer is less than Tfreeze + scalarSfcMeltPond = 0._dp ! kg m-2 + end if ! (if the temperature of the top soil layer is greater than Tfreeze) + else ! melt is zero if the "snow without a layer" does not exist + scalarSfcMeltPond = 0._dp ! kg m-2 + end if ! (if the "snow without a layer" exists) + + end subroutine implctMelt end module fidaSolver_module From ddea3fe3cd9663f53d227abbaa32f45f91f991ef Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Mon, 31 May 2021 23:29:38 -0600 Subject: [PATCH 0126/1472] minor edit --- build/source/engine/computSnowDepth.f90 | 3 +++ build/source/engine/fidaSolver.f90 | 8 ++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/build/source/engine/computSnowDepth.f90 b/build/source/engine/computSnowDepth.f90 index 43a4afad0..fb352a61f 100644 --- a/build/source/engine/computSnowDepth.f90 +++ b/build/source/engine/computSnowDepth.f90 @@ -113,6 +113,7 @@ subroutine computSnowDepth(& end if end if ! (if snow layers exist) + ! *** account for compaction and cavitation in the snowpack... ! ------------------------------------------------------------ @@ -138,6 +139,8 @@ subroutine computSnowDepth(& err,cmessage) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if end if ! if snow layers exist + + ! recompute snow depth and SWE if(nSnow > 0)then diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 00f2b1a44..1350a7c4e 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -239,8 +239,6 @@ subroutine fidaSolver( & real(dp) :: bulkDensity ! bulk density of a given layer (kg m-3) real(dp) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) logical(lgt) :: tooMuchMelt - real(dp) :: mLayerDepth(nLayers) - real(dp) :: scalarSnowDepth logical(lgt) :: divideLayer logical(lgt) :: mergedLayers logical(lgt),parameter :: checkSnow = .true. @@ -431,7 +429,6 @@ subroutine fidaSolver( & eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) eqns_data%ixSaturation = ixSaturation - mLayerDepth(:) = prog_data%var(iLookPROG%mLayerDepth)%dat(:) !********************************************************************************** !****************************** Main Solver *************************************** @@ -640,11 +637,10 @@ subroutine fidaSolver( & ! output: error control err,message) ! intent(out): error control if(err/=0)then; err=20; return; end if - end if - + end if endif ! checkSnow -! if(tret(1) > 1000) exit +! if(tret(1) > 6700) exit ! save required quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial From 182df7cb7a1efa21db5ba388a7e340bafe0bd0a9 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 1 Jun 2021 00:08:50 -0600 Subject: [PATCH 0127/1472] scalarCanopyLiqTrial in fidaSolver --- build/source/engine/eval8summaFida.f90 | 9 +++-- build/source/engine/evalEqnsFida.f90 | 2 + build/source/engine/fidaSolver.f90 | 52 +++++++++++++++++++++++++- build/source/engine/fida_datatypes.f90 | 2 + 4 files changed, 59 insertions(+), 6 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 83d101dd0..e0ee19c80 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -125,6 +125,8 @@ subroutine eval8summaFida(& scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) scalarCanopyIceTrial, & scalarCanopyIcePrev, & + scalarCanopyLiqTrial, & + scalarCanopyLiqPrev, & scalarCanopyEnthalpyTrial,& ! intent(in): trial enthalpy of the vegetation canopy (J m-3) scalarCanopyEnthalpyPrev,& ! intent(in): previous enthalpy of the vegetation canopy (J m-3) mLayerTempTrial, & ! intent(inout) @@ -203,6 +205,8 @@ subroutine eval8summaFida(& real(dp),intent(in) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) real(dp),intent(out) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) real(dp),intent(in) :: scalarCanopyIcePrev ! previous value for mass of ice on the vegetation canopy (kg m-2) + real(dp),intent(out) :: scalarCanopyLiqTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(dp),intent(in) :: scalarCanopyLiqPrev ! previous value for mass of ice on the vegetation canopy (kg m-2) real(dp),intent(out) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) real(dp),intent(in) :: scalarCanopyEnthalpyPrev ! previous value of enthalpy of the vegetation canopy (J m-3) real(dp),intent(out) :: mLayerTempTrial(:) @@ -234,9 +238,6 @@ subroutine eval8summaFida(& ! state variables real(dp) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) real(dp) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - ! diagnostic variables - real(dp) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - ! derivative of state variables real(dp) :: scalarCanairTempPrime ! derivative value for temperature of the canopy air space (K) real(dp) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) @@ -388,7 +389,7 @@ subroutine eval8summaFida(& !scalarCanairTempTrial = scalarCanairTempPrev scalarCanopyTempTrial = scalarCanopyTempPrev !scalarCanopyWatTrial = scalarCanopyWatPrev - !scalarCanopyLiqTrial = scalarCanopyLiqPrev + scalarCanopyLiqTrial = scalarCanopyLiqPrev scalarCanopyIceTrial = scalarCanopyIcePrev mLayerTempTrial = mLayerTempPrev mLayerVolFracWatTrial = mLayerVolFracWatPrev diff --git a/build/source/engine/evalEqnsFida.f90 b/build/source/engine/evalEqnsFida.f90 index 55c4d764b..c72d6afe0 100644 --- a/build/source/engine/evalEqnsFida.f90 +++ b/build/source/engine/evalEqnsFida.f90 @@ -133,6 +133,8 @@ integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_d eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) eqns_data%scalarCanopyIceTrial, & eqns_data%scalarCanopyIcePrev, & + eqns_data%scalarCanopyLiqTrial, & + eqns_data%scalarCanopyLiqPrev, & eqns_data%scalarCanopyEnthalpyTrial,& ! intent(in): trial enthalpy of the vegetation canopy (J m-3) eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) eqns_data%mLayerTempTrial, & diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 1350a7c4e..d9487eb2a 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -52,6 +52,7 @@ module fidaSolver_module ! constants USE multiconst,only:& LH_fus, & ! latent heat of fusion (J K-1) + LH_sub, & ! latent heat of sublimation (J kg-1) Tfreeze, & ! temperature at freezing (K) iden_ice, & ! intrinsic density of ice (kg m-3) iden_water ! intrinsic density of liquid water (kg m-3) @@ -242,6 +243,8 @@ subroutine fidaSolver( & logical(lgt) :: divideLayer logical(lgt) :: mergedLayers logical(lgt),parameter :: checkSnow = .true. + real(dp) :: superflousSub ! superflous sublimation (kg m-2 s-1) + real(dp) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) !======= Internals ============ @@ -418,7 +421,8 @@ subroutine fidaSolver( & ! need the following values for the first substep eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) - eqns_data%scalarCanopyIcePrev = prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) + eqns_data%scalarCanopyIcePrev = prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) + eqns_data%scalarCanopyLiqPrev = prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) @@ -487,6 +491,8 @@ subroutine fidaSolver( & eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) eqns_data%scalarCanopyIceTrial, & eqns_data%scalarCanopyIcePrev, & + eqns_data%scalarCanopyLiqTrial, & + eqns_data%scalarCanopyLiqPrev, & eqns_data%scalarCanopyEnthalpyTrial,& ! intent(in): trial enthalpy of the vegetation canopy (J m-3) eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) eqns_data%mLayerTempTrial, & @@ -542,7 +548,49 @@ subroutine fidaSolver( & mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) - if(checkSnow)then + if(checkSnow)then + + ! *** remove ice due to sublimation... + ! -------------------------------------------------------------- + sublime: associate(& + scalarCanopySublimation => eqns_data%flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1), & ! sublimation from the vegetation canopy (kg m-2 s-1) + scalarSnowSublimation => eqns_data%flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! sublimation from the snow surface (kg m-2 s-1) + scalarLatHeatCanopyEvap => eqns_data%flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1), & ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + scalarSenHeatCanopy => eqns_data%flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1), & ! sensible heat flux from the canopy to the canopy air space (W m-2) + scalarLatHeatGround => eqns_data%flux_data%var(iLookFLUX%scalarLatHeatGround)%dat(1), & ! latent heat flux from ground surface below vegetation (W m-2) + scalarSenHeatGround => eqns_data%flux_data%var(iLookFLUX%scalarSenHeatGround)%dat(1) & ! sensible heat flux from ground surface below vegetation (W m-2) + ) ! associations to variables in data structures + + ! * compute change in canopy ice content due to sublimation... + ! ------------------------------------------------------------ + if(eqns_data%computeVegFlux)then + + ! remove mass of ice on the canopy + eqns_data%scalarCanopyIceTrial = eqns_data%scalarCanopyIceTrial + scalarCanopySublimation*dt_last(1) + + ! if removed all ice, take the remaining sublimation from water + if(eqns_data%scalarCanopyIceTrial < 0._dp)then + eqns_data%scalarCanopyLiqTrial = eqns_data%scalarCanopyLiqTrial + eqns_data%scalarCanopyIceTrial + eqns_data%scalarCanopyIceTrial = 0._dp + endif + + ! modify fluxes if there is insufficient canopy water to support the converged sublimation rate over the time step dt_last(1) + if(eqns_data%scalarCanopyLiqTrial < 0._dp)then + ! --> superfluous sublimation flux + superflousSub = -eqns_data%scalarCanopyLiqTrial/dt_last(1) ! kg m-2 s-1 + superflousNrg = superflousSub*LH_sub ! W m-2 (J m-2 s-1) + ! --> update fluxes and states + scalarCanopySublimation = scalarCanopySublimation + superflousSub + scalarLatHeatCanopyEvap = scalarLatHeatCanopyEvap + superflousNrg + scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg + eqns_data%scalarCanopyLiqTrial = 0._dp + endif + + end if ! (if computing the vegetation flux) + + end associate sublime + + call computSnowDepth(& dt_last(1), & ! intent(in) eqns_data%nSnow, & ! intent(in) diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index d9d24a394..e0fe45c74 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -44,6 +44,8 @@ module fida_datatypes real(dp) :: scalarCanopyTempPrev ! previous value of canopy temperature (K) real(dp) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) real(dp) :: scalarCanopyIcePrev ! value of canopy ice content (kg m-2) at previous step + real(dp) :: scalarCanopyLiqTrial ! trial value of canopy ice content (kg m-2) + real(dp) :: scalarCanopyLiqPrev ! value of canopy ice content (kg m-2) at previous step real(dp) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) real(dp) :: scalarCanopyEnthalpyPrev ! previous enthalpy of the vegetation canopy (J m-3) real(qp), allocatable :: sMul(:) ! state vector multiplier (used in the residual calculations) From 54d003419dee6205657bf0d15fcff70b392e5eeb Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 1 Jun 2021 11:51:41 -0600 Subject: [PATCH 0128/1472] computSnowDepth in varSubstepFida --- build/source/engine/fidaSolver.f90 | 21 +++++++++++++++------ build/source/engine/sysSolvFida.f90 | 2 +- build/source/engine/varSubstepFida.f90 | 20 +++++++++++++++++++- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index d9487eb2a..44536eea2 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -243,8 +243,11 @@ subroutine fidaSolver( & logical(lgt) :: divideLayer logical(lgt) :: mergedLayers logical(lgt),parameter :: checkSnow = .true. - real(dp) :: superflousSub ! superflous sublimation (kg m-2 s-1) - real(dp) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) + real(dp) :: superflousSub ! superflous sublimation (kg m-2 s-1) + real(dp) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) + real(dp) :: mLayerDepth(nLayers) + real(dp) :: scalarSnowDepth + real(dp) :: scalarSWE !======= Internals ============ @@ -433,6 +436,9 @@ subroutine fidaSolver( & eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) eqns_data%ixSaturation = ixSaturation + mLayerDepth = prog_data%var(iLookPROG%mLayerDepth)%dat(1) + scalarSnowDepth = prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) + scalarSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) !********************************************************************************** !****************************** Main Solver *************************************** @@ -589,7 +595,8 @@ subroutine fidaSolver( & end if ! (if computing the vegetation flux) end associate sublime - + +if( 1==0 )then call computSnowDepth(& dt_last(1), & ! intent(in) @@ -601,9 +608,9 @@ subroutine fidaSolver( & eqns_data%flux_data, & ! intent(in) eqns_data%diag_data, & ! intent(in) ! output - eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(out) - eqns_data%prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(out) - eqns_data%prog_data%var(iLookPROG%scalarSWE)%dat(1), & + mLayerDepth, & ! intent(out) + scalarSnowDepth, & ! intent(out) + scalarSWE, & ! error control err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if @@ -686,6 +693,7 @@ subroutine fidaSolver( & err,message) ! intent(out): error control if(err/=0)then; err=20; return; end if end if +endif endif ! checkSnow ! if(tret(1) > 6700) exit @@ -714,6 +722,7 @@ subroutine fidaSolver( & if( .not. feasible) idaSucceeds = .false. if(idaSucceeds)then + ! copy to output data diag_data = eqns_data%diag_data flux_data = eqns_data%flux_data diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 8cd792ca6..c35bfae40 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -516,7 +516,7 @@ subroutine sysSolvFida(& ! compute the total change in storage associated with compression of the soil matrix (kg m-2) diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sum(diag_data%var(iLookDIAG%mLayerCompress)%dat(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water - + ! save the computed solution stateVecTrial = stateVecNew diff --git a/build/source/engine/varSubstepFida.f90 b/build/source/engine/varSubstepFida.f90 index fa6314013..87b81c190 100644 --- a/build/source/engine/varSubstepFida.f90 +++ b/build/source/engine/varSubstepFida.f90 @@ -548,6 +548,7 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt USE varExtrFida_module, only:varExtractFida USE computEnthalpy_module,only:computEnthalpy USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy + USE computSnowDepth_module,only:computSnowDepth implicit none ! model control real(dp) ,intent(in) :: dt ! time step (s) @@ -1063,6 +1064,23 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt ! ----- ! * update prognostic variables... ! -------------------------------- + + call computSnowDepth(& + dt, & ! intent(in) + nSnow, & ! intent(in) + mLayerVolFracLiqTrial, & ! intent(inout) + mLayerVolFracIceTrial, & ! intent(inout) + mLayerTempTrial, & ! intent(in) + mpar_data, & ! intent(in) + flux_data, & ! intent(in) + diag_data, & ! intent(in) + ! output + prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(out) + prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(out) + prog_data%var(iLookPROG%scalarSWE)%dat(1), & + ! error control + err,message) ! intent(out): error control + if(err/=0)then; err=55; return; end if ! update state variables for the vegetation canopy scalarCanairTemp = scalarCanairTempTrial ! trial value of canopy air temperature (K) @@ -1081,7 +1099,7 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt ! update state variables for the aquifer scalarAquiferStorage = scalarAquiferStorageTrial - + ! end associations to info in the data structures end associate From be1622c9efd35857d247faf342d76f21e65e2a02 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 1 Jun 2021 20:50:07 -0600 Subject: [PATCH 0129/1472] computSnowDepth modified --- build/source/engine/computSnowDepth.f90 | 12 ------------ build/source/engine/coupled_em.f90 | 3 ++- build/source/engine/fidaSolver.f90 | 15 +++++++++++---- build/source/engine/varSubstepFida.f90 | 10 +++++++--- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/build/source/engine/computSnowDepth.f90 b/build/source/engine/computSnowDepth.f90 index fb352a61f..c85257a80 100644 --- a/build/source/engine/computSnowDepth.f90 +++ b/build/source/engine/computSnowDepth.f90 @@ -53,8 +53,6 @@ subroutine computSnowDepth(& diag_data, & ! intent(in) ! output mLayerDepth, & ! intent(out) - scalarSnowDepth, & ! intent(out) - scalarSWE, & ! intetn(out) ! error control err,message) ! intent(out): error control @@ -70,8 +68,6 @@ subroutine computSnowDepth(& type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU real(dp),intent(out) :: mLayerDepth(:) - real(dp),intent(out) :: scalarSnowDepth - real(dp),intent(out) :: scalarSWE integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -140,14 +136,6 @@ subroutine computSnowDepth(& if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if end if ! if snow layers exist - - - ! recompute snow depth and SWE - if(nSnow > 0)then - scalarSnowDepth = sum( mLayerDepth(1:nSnow) ) - scalarSWE = sum( (mLayerVolFracLiq(1:nSnow)*iden_water + mLayerVolFracIce(1:nSnow)*iden_ice) * mLayerDepth(1:nSnow) ) - end if - end associate end subroutine computSnowDepth diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index c4981a00a..0e34e8c0e 100755 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -828,6 +828,7 @@ subroutine coupled_em(& end if ! (if computing the vegetation flux) +if( 1==0 )then ! * compute change in ice content of the top snow layer due to sublimation... ! --------------------------------------------------------------------------- ! NOTE: this is done BEFORE densification @@ -867,7 +868,7 @@ subroutine coupled_em(& end if end if ! (if snow layers exist) - +endif end associate sublime ! *** account for compaction and cavitation in the snowpack... diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 44536eea2..7e960ea8e 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -595,8 +595,7 @@ subroutine fidaSolver( & end if ! (if computing the vegetation flux) end associate sublime - -if( 1==0 )then + call computSnowDepth(& dt_last(1), & ! intent(in) @@ -609,12 +608,20 @@ subroutine fidaSolver( & eqns_data%diag_data, & ! intent(in) ! output mLayerDepth, & ! intent(out) - scalarSnowDepth, & ! intent(out) - scalarSWE, & ! error control err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if +if( 1==0 )then + + + + ! recompute snow depth and SWE + if(eqns_data%nSnow > 0)then + scalarSnowDepth = sum( mLayerDepth(1:nSnow) ) + scalarSWE = sum( (eqns_data%mLayerVolFracLiqTrial(1:nSnow)*iden_water + eqns_data%mLayerVolFracIceTrial(1:nSnow)*iden_ice) * mLayerDepth(1:nSnow) ) + end if + ! update coordinate variables call calcHeight(& ! input/output: data structures diff --git a/build/source/engine/varSubstepFida.f90 b/build/source/engine/varSubstepFida.f90 index 87b81c190..8ceebe5aa 100644 --- a/build/source/engine/varSubstepFida.f90 +++ b/build/source/engine/varSubstepFida.f90 @@ -1075,12 +1075,16 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt flux_data, & ! intent(in) diag_data, & ! intent(in) ! output - prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(out) - prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(out) - prog_data%var(iLookPROG%scalarSWE)%dat(1), & + mLayerDepth, & ! intent(out) ! error control err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if + + ! recompute snow depth and SWE + if(nSnow > 0)then + prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( mLayerDepth(1:nSnow) ) + prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (mLayerVolFracLiqTrial(1:nSnow)*iden_water + mLayerVolFracIceTrial(1:nSnow)*iden_ice) * mLayerDepth(1:nSnow) ) + end if ! update state variables for the vegetation canopy scalarCanairTemp = scalarCanairTempTrial ! trial value of canopy air temperature (K) From 8931350b0f01ab15dabd8395c8d484fdc9dcaf2e Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 1 Jun 2021 22:29:51 -0600 Subject: [PATCH 0130/1472] computSnowDepth in coupled_em --- build/source/engine/coupled_em.f90 | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 0e34e8c0e..b49f525f7 100755 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -138,6 +138,7 @@ subroutine coupled_em(& USE tempAdjust_module,only:tempAdjust ! adjust snow temperature associated with new snowfall USE snwDensify_module,only:snwDensify ! snow densification (compaction and cavitation) USE var_derive_module,only:calcHeight ! module to calculate height at layer interfaces and layer mid-point + USE computSnowDepth_module,only:computSnowDepth ! look-up values for the numerical method USE mDecisions_module,only: & iterative, & ! iterative @@ -827,6 +828,26 @@ subroutine coupled_em(& endif end if ! (if computing the vegetation flux) + + if (1 == 0)then + + ! end associate sublime + call computSnowDepth(& + dt_sub, & ! intent(in) + nSnow, & ! intent(in) + mLayerVolFracLiq, & ! intent(inout) + mLayerVolFracIce, & ! intent(inout) + prog_data%var(iLookPROG%mLayerTemp)%dat, & ! intent(in) + mpar_data, & ! intent(in) + flux_data, & ! intent(in) + diag_data, & ! intent(in) + ! output + mLayerDepth, & ! intent(out) + ! error control + err,message) ! intent(out): error control + if(err/=0)then; err=55; return; end if + + endif if( 1==0 )then ! * compute change in ice content of the top snow layer due to sublimation... From 8edb51d53df32e6d05c2eff585c6c704257f9320 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 1 Jun 2021 22:39:47 -0600 Subject: [PATCH 0131/1472] computSnowDepth in coupled_em works fine --- build/source/engine/coupled_em.f90 | 75 +------------------------- build/source/engine/fidaSolver.f90 | 2 +- build/source/engine/varSubstepFida.f90 | 4 ++ 3 files changed, 7 insertions(+), 74 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index b49f525f7..16b994d5e 100755 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -829,10 +829,7 @@ subroutine coupled_em(& end if ! (if computing the vegetation flux) - if (1 == 0)then - - ! end associate sublime - call computSnowDepth(& + call computSnowDepth(& dt_sub, & ! intent(in) nSnow, & ! intent(in) mLayerVolFracLiq, & ! intent(inout) @@ -847,76 +844,8 @@ subroutine coupled_em(& err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if - endif - -if( 1==0 )then - ! * compute change in ice content of the top snow layer due to sublimation... - ! --------------------------------------------------------------------------- - ! NOTE: this is done BEFORE densification - if(nSnow > 0)then ! snow layers exist - - ! try to remove ice from the top layer - iSnow=1 - - ! save the mass of liquid water (kg m-2) - massLiquid = mLayerDepth(iSnow)*mLayerVolFracLiq(iSnow)*iden_water - - ! add/remove the depth of snow gained/lost by frost/sublimation (m) - ! NOTE: assume constant density - mLayerDepth(iSnow) = mLayerDepth(iSnow) + dt_sub*scalarSnowSublimation/(mLayerVolFracIce(iSnow)*iden_ice) - - ! check that we did not remove the entire layer - if(mLayerDepth(iSnow) < verySmall)then - stepFailure = .true. - doLayerMerge = .true. - dt_sub = max(dtSave/2._dp, minstep) - cycle substeps - else - stepFailure = .false. - doLayerMerge = .false. - endif - - ! update the volumetric fraction of liquid water - mLayerVolFracLiq(iSnow) = massLiquid / (mLayerDepth(iSnow)*iden_water) - - ! no snow - else - - ! no snow: check that sublimation is zero - if(abs(scalarSnowSublimation) > verySmall)then - message=trim(message)//'sublimation of snow has been computed when no snow exists' - err=20; return - end if - - end if ! (if snow layers exist) -endif end associate sublime - - ! *** account for compaction and cavitation in the snowpack... - ! ------------------------------------------------------------ - if(nSnow>0)then - call snwDensify(& - ! intent(in): variables - dt_sub, & ! intent(in): time step (s) - indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - prog_data%var(iLookPROG%mLayerTemp)%dat(1:nSnow), & ! intent(in): temperature of each layer (K) - diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(1:nSnow), & ! intent(in): volumetric melt in each layer (kg m-3) - ! intent(in): parameters - mpar_data%var(iLookPARAM%densScalGrowth)%dat(1), & ! intent(in): density scaling factor for grain growth (kg-1 m3) - mpar_data%var(iLookPARAM%tempScalGrowth)%dat(1), & ! intent(in): temperature scaling factor for grain growth (K-1) - mpar_data%var(iLookPARAM%grainGrowthRate)%dat(1), & ! intent(in): rate of grain growth (s-1) - mpar_data%var(iLookPARAM%densScalOvrbdn)%dat(1), & ! intent(in): density scaling factor for overburden pressure (kg-1 m3) - mpar_data%var(iLookPARAM%tempScalOvrbdn)%dat(1), & ! intent(in): temperature scaling factor for overburden pressure (K-1) - mpar_data%var(iLookPARAM%baseViscosity)%dat(1), & ! intent(in): viscosity coefficient at T=T_frz and snow density=0 (kg m-2 s) - ! intent(inout): state variables - prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow), & ! intent(inout): depth of each layer (m) - prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow), & ! intent(inout): volumetric fraction of liquid water after itertations (-) - prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow), & ! intent(inout): volumetric fraction of ice after itertations (-) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if - end if ! if snow layers exist - + ! update coordinate variables call calcHeight(& ! input/output: data structures diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 7e960ea8e..d3e95434c 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -242,7 +242,7 @@ subroutine fidaSolver( & logical(lgt) :: tooMuchMelt logical(lgt) :: divideLayer logical(lgt) :: mergedLayers - logical(lgt),parameter :: checkSnow = .true. + logical(lgt),parameter :: checkSnow = .false. real(dp) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(dp) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) real(dp) :: mLayerDepth(nLayers) diff --git a/build/source/engine/varSubstepFida.f90 b/build/source/engine/varSubstepFida.f90 index 8ceebe5aa..598904434 100644 --- a/build/source/engine/varSubstepFida.f90 +++ b/build/source/engine/varSubstepFida.f90 @@ -1065,6 +1065,8 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt ! * update prognostic variables... ! -------------------------------- + if(1==0)then + call computSnowDepth(& dt, & ! intent(in) nSnow, & ! intent(in) @@ -1085,6 +1087,8 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( mLayerDepth(1:nSnow) ) prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (mLayerVolFracLiqTrial(1:nSnow)*iden_water + mLayerVolFracIceTrial(1:nSnow)*iden_ice) * mLayerDepth(1:nSnow) ) end if + + endif ! update state variables for the vegetation canopy scalarCanairTemp = scalarCanairTempTrial ! trial value of canopy air temperature (K) From a2f7b27a11b29373008e3f0e86e2e20f6e36458b Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 1 Jun 2021 23:22:21 -0600 Subject: [PATCH 0132/1472] more edit on fidaSovler --- build/source/engine/coupled_em.f90 | 14 ++++---- build/source/engine/fidaSolver.f90 | 47 ++++++++++++-------------- build/source/engine/varSubstepFida.f90 | 6 ++-- 3 files changed, 32 insertions(+), 35 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 16b994d5e..f9d6959f2 100755 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -830,16 +830,16 @@ subroutine coupled_em(& end if ! (if computing the vegetation flux) call computSnowDepth(& - dt_sub, & ! intent(in) - nSnow, & ! intent(in) + dt_sub, & ! intent(in) + nSnow, & ! intent(in) mLayerVolFracLiq, & ! intent(inout) mLayerVolFracIce, & ! intent(inout) - prog_data%var(iLookPROG%mLayerTemp)%dat, & ! intent(in) - mpar_data, & ! intent(in) - flux_data, & ! intent(in) - diag_data, & ! intent(in) + prog_data%var(iLookPROG%mLayerTemp)%dat, & ! intent(in) + mpar_data, & ! intent(in) + flux_data, & ! intent(in) + diag_data, & ! intent(in) ! output - mLayerDepth, & ! intent(out) + mLayerDepth, & ! intent(out) ! error control err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index d3e95434c..0c8cdc662 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -242,7 +242,7 @@ subroutine fidaSolver( & logical(lgt) :: tooMuchMelt logical(lgt) :: divideLayer logical(lgt) :: mergedLayers - logical(lgt),parameter :: checkSnow = .false. + logical(lgt),parameter :: checkSnow = .true. real(dp) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(dp) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) real(dp) :: mLayerDepth(nLayers) @@ -554,7 +554,22 @@ subroutine fidaSolver( & mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) - if(checkSnow)then + + ! save required quantities for next step + eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial + eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial + eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) + mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) + eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) + eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) + eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) + eqns_data%mLayerVolFracLiqPrev(:) = eqns_data%mLayerVolFracLiqTrial(:) + eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial + eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) + eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial + + + if(checkSnow)then ! *** remove ice due to sublimation... ! -------------------------------------------------------------- @@ -607,19 +622,16 @@ subroutine fidaSolver( & eqns_data%flux_data, & ! intent(in) eqns_data%diag_data, & ! intent(in) ! output - mLayerDepth, & ! intent(out) + eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(out) ! error control err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if -if( 1==0 )then - - - + ! recompute snow depth and SWE if(eqns_data%nSnow > 0)then - scalarSnowDepth = sum( mLayerDepth(1:nSnow) ) - scalarSWE = sum( (eqns_data%mLayerVolFracLiqTrial(1:nSnow)*iden_water + eqns_data%mLayerVolFracIceTrial(1:nSnow)*iden_ice) * mLayerDepth(1:nSnow) ) + eqns_data%prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) + eqns_data%prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (eqns_data%mLayerVolFracLiqTrial(1:nSnow)*iden_water + eqns_data%mLayerVolFracIceTrial(1:nSnow)*iden_ice) * eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) end if ! update coordinate variables @@ -700,24 +712,9 @@ subroutine fidaSolver( & err,message) ! intent(out): error control if(err/=0)then; err=20; return; end if end if -endif + endif ! checkSnow -! if(tret(1) > 6700) exit - - ! save required quantities for next step - eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial - eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial - eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) - mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) - eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) - eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) - eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) - eqns_data%mLayerVolFracLiqPrev(:) = eqns_data%mLayerVolFracLiqTrial(:) - eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial - eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) - eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial - end do ! while loop on one_step mode diff --git a/build/source/engine/varSubstepFida.f90 b/build/source/engine/varSubstepFida.f90 index 598904434..6a9a3ee86 100644 --- a/build/source/engine/varSubstepFida.f90 +++ b/build/source/engine/varSubstepFida.f90 @@ -1065,8 +1065,8 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt ! * update prognostic variables... ! -------------------------------- - if(1==0)then - + + if(1==0)then call computSnowDepth(& dt, & ! intent(in) nSnow, & ! intent(in) @@ -1081,7 +1081,7 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt ! error control err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if - + ! recompute snow depth and SWE if(nSnow > 0)then prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( mLayerDepth(1:nSnow) ) From 2361b867147ce02f4671c7c8a78d95a6a7a4f29f Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 2 Jun 2021 12:37:32 -0600 Subject: [PATCH 0133/1472] mLayerDepth as prog_data in fidasolver --- build/source/engine/allocspace.f90 | 0 build/source/engine/bigAquifer.f90 | 0 build/source/engine/canopySnow.f90 | 0 build/source/engine/checkStruc.f90 | 0 build/source/engine/check_icond.f90 | 0 build/source/engine/childStruc.f90 | 0 build/source/engine/computFlux.f90 | 0 build/source/engine/computFluxFida.f90 | 0 build/source/engine/computJacob.f90 | 0 build/source/engine/computResid.f90 | 0 build/source/engine/convE2Temp.f90 | 0 build/source/engine/conv_funcs.f90 | 0 build/source/engine/coupled_em.f90 | 0 build/source/engine/derivforce.f90 | 0 build/source/engine/diagn_evar.f90 | 0 build/source/engine/eval8summa.f90 | 0 build/source/engine/eval8summaFida.f90 | 2 +- build/source/engine/expIntegral.f90 | 0 build/source/engine/f2008funcs.f90 | 0 build/source/engine/ffile_info.f90 | 0 build/source/engine/fidaSolver.f90 | 103 ++++-------------------- build/source/engine/getVectorz.f90 | 0 build/source/engine/groundwatr.f90 | 0 build/source/engine/indexState.f90 | 0 build/source/engine/layerDivide.f90 | 10 +-- build/source/engine/layerDivideFida.f90 | 0 build/source/engine/layerMerge.f90 | 10 +-- build/source/engine/mDecisions.f90 | 0 build/source/engine/matrixOper.f90 | 0 build/source/engine/nr_utility.f90 | 0 build/source/engine/nrtype.f90 | 0 build/source/engine/opSplittin.f90 | 0 build/source/engine/pOverwrite.f90 | 0 build/source/engine/paramCheck.f90 | 0 build/source/engine/qTimeDelay.f90 | 0 build/source/engine/read_attrb.f90 | 0 build/source/engine/read_force.f90 | 0 build/source/engine/read_param.f90 | 0 build/source/engine/read_pinit.f90 | 0 build/source/engine/run_oneGRU.f90 | 0 build/source/engine/run_oneHRU.f90 | 0 build/source/engine/snowAlbedo.f90 | 0 build/source/engine/snowLiqFlx.f90 | 0 build/source/engine/snow_utils.f90 | 0 build/source/engine/snwCompact.f90 | 2 +- build/source/engine/soilLiqFlx.f90 | 0 build/source/engine/soil_utils.f90 | 0 build/source/engine/spline_int.f90 | 0 build/source/engine/ssdNrgFlux.f90 | 0 build/source/engine/stomResist.f90 | 0 build/source/engine/summaSolve.f90 | 0 build/source/engine/sunGeomtry.f90 | 0 build/source/engine/systemSolv.f90 | 0 build/source/engine/tempAdjust.f90 | 0 build/source/engine/time_utils.f90 | 0 build/source/engine/updatState.f90 | 0 build/source/engine/updateVars.f90 | 0 build/source/engine/varSubstep.f90 | 0 build/source/engine/var_derive.f90 | 0 build/source/engine/vegLiqFlux.f90 | 0 build/source/engine/vegNrgFlux.f90 | 0 build/source/engine/vegNrgFluxFida.f90 | 0 build/source/engine/vegPhenlgy.f90 | 0 build/source/engine/vegSWavRad.f90 | 0 build/source/engine/volicePack.f90 | 4 + build/source/engine/volicePackFida.f90 | 0 66 files changed, 33 insertions(+), 98 deletions(-) mode change 100755 => 100644 build/source/engine/allocspace.f90 mode change 100755 => 100644 build/source/engine/bigAquifer.f90 mode change 100755 => 100644 build/source/engine/canopySnow.f90 mode change 100755 => 100644 build/source/engine/checkStruc.f90 mode change 100755 => 100644 build/source/engine/check_icond.f90 mode change 100755 => 100644 build/source/engine/childStruc.f90 mode change 100755 => 100644 build/source/engine/computFlux.f90 mode change 100755 => 100644 build/source/engine/computFluxFida.f90 mode change 100755 => 100644 build/source/engine/computJacob.f90 mode change 100755 => 100644 build/source/engine/computResid.f90 mode change 100755 => 100644 build/source/engine/convE2Temp.f90 mode change 100755 => 100644 build/source/engine/conv_funcs.f90 mode change 100755 => 100644 build/source/engine/coupled_em.f90 mode change 100755 => 100644 build/source/engine/derivforce.f90 mode change 100755 => 100644 build/source/engine/diagn_evar.f90 mode change 100755 => 100644 build/source/engine/eval8summa.f90 mode change 100755 => 100644 build/source/engine/expIntegral.f90 mode change 100755 => 100644 build/source/engine/f2008funcs.f90 mode change 100755 => 100644 build/source/engine/ffile_info.f90 mode change 100755 => 100644 build/source/engine/getVectorz.f90 mode change 100755 => 100644 build/source/engine/groundwatr.f90 mode change 100755 => 100644 build/source/engine/indexState.f90 mode change 100755 => 100644 build/source/engine/layerDivide.f90 mode change 100755 => 100644 build/source/engine/layerDivideFida.f90 mode change 100755 => 100644 build/source/engine/layerMerge.f90 mode change 100755 => 100644 build/source/engine/mDecisions.f90 mode change 100755 => 100644 build/source/engine/matrixOper.f90 mode change 100755 => 100644 build/source/engine/nr_utility.f90 mode change 100755 => 100644 build/source/engine/nrtype.f90 mode change 100755 => 100644 build/source/engine/opSplittin.f90 mode change 100755 => 100644 build/source/engine/pOverwrite.f90 mode change 100755 => 100644 build/source/engine/paramCheck.f90 mode change 100755 => 100644 build/source/engine/qTimeDelay.f90 mode change 100755 => 100644 build/source/engine/read_attrb.f90 mode change 100755 => 100644 build/source/engine/read_force.f90 mode change 100755 => 100644 build/source/engine/read_param.f90 mode change 100755 => 100644 build/source/engine/read_pinit.f90 mode change 100755 => 100644 build/source/engine/run_oneGRU.f90 mode change 100755 => 100644 build/source/engine/run_oneHRU.f90 mode change 100755 => 100644 build/source/engine/snowAlbedo.f90 mode change 100755 => 100644 build/source/engine/snowLiqFlx.f90 mode change 100755 => 100644 build/source/engine/snow_utils.f90 mode change 100755 => 100644 build/source/engine/snwCompact.f90 mode change 100755 => 100644 build/source/engine/soilLiqFlx.f90 mode change 100755 => 100644 build/source/engine/soil_utils.f90 mode change 100755 => 100644 build/source/engine/spline_int.f90 mode change 100755 => 100644 build/source/engine/ssdNrgFlux.f90 mode change 100755 => 100644 build/source/engine/stomResist.f90 mode change 100755 => 100644 build/source/engine/summaSolve.f90 mode change 100755 => 100644 build/source/engine/sunGeomtry.f90 mode change 100755 => 100644 build/source/engine/systemSolv.f90 mode change 100755 => 100644 build/source/engine/tempAdjust.f90 mode change 100755 => 100644 build/source/engine/time_utils.f90 mode change 100755 => 100644 build/source/engine/updatState.f90 mode change 100755 => 100644 build/source/engine/updateVars.f90 mode change 100755 => 100644 build/source/engine/varSubstep.f90 mode change 100755 => 100644 build/source/engine/var_derive.f90 mode change 100755 => 100644 build/source/engine/vegLiqFlux.f90 mode change 100755 => 100644 build/source/engine/vegNrgFlux.f90 mode change 100755 => 100644 build/source/engine/vegNrgFluxFida.f90 mode change 100755 => 100644 build/source/engine/vegPhenlgy.f90 mode change 100755 => 100644 build/source/engine/vegSWavRad.f90 mode change 100755 => 100644 build/source/engine/volicePack.f90 mode change 100755 => 100644 build/source/engine/volicePackFida.f90 diff --git a/build/source/engine/allocspace.f90 b/build/source/engine/allocspace.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/bigAquifer.f90 b/build/source/engine/bigAquifer.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/canopySnow.f90 b/build/source/engine/canopySnow.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/checkStruc.f90 b/build/source/engine/checkStruc.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/check_icond.f90 b/build/source/engine/check_icond.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/childStruc.f90 b/build/source/engine/childStruc.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/computFluxFida.f90 b/build/source/engine/computFluxFida.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/computResid.f90 b/build/source/engine/computResid.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/convE2Temp.f90 b/build/source/engine/convE2Temp.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/conv_funcs.f90 b/build/source/engine/conv_funcs.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/derivforce.f90 b/build/source/engine/derivforce.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/diagn_evar.f90 b/build/source/engine/diagn_evar.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index e0ee19c80..060762fcb 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -266,7 +266,7 @@ subroutine eval8summaFida(& real(qp) :: scalarCanopyEnthalpyPrime real(dp) :: scalarCanopyCmTrial real(dp),dimension(nLayers) :: mLayerCmTrial - logical(lgt),parameter :: updateCp=.true. + logical(lgt),parameter :: updateCp=.false. logical(lgt),parameter :: needCm=.false. diff --git a/build/source/engine/expIntegral.f90 b/build/source/engine/expIntegral.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/f2008funcs.f90 b/build/source/engine/f2008funcs.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/ffile_info.f90 b/build/source/engine/ffile_info.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 0c8cdc662..4556a90ab 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -156,8 +156,8 @@ subroutine fidaSolver( & USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy USE computSnowDepth_module,only:computSnowDepth USE var_derive_module,only:calcHeight ! module to calculate height at layer interfaces and layer mid-point - USE layerDivide_module,only:doesLayerDivide - Use layerMerge_module,only:doesLayerMerge + USE layerDivide_module,only:needDivideLayer + Use layerMerge_module,only:needMergeLayers !======= Declarations ========= implicit none @@ -436,7 +436,7 @@ subroutine fidaSolver( & eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) eqns_data%ixSaturation = ixSaturation - mLayerDepth = prog_data%var(iLookPROG%mLayerDepth)%dat(1) + mLayerDepth = prog_data%var(iLookPROG%mLayerDepth)%dat scalarSnowDepth = prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) scalarSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) @@ -569,50 +569,9 @@ subroutine fidaSolver( & eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial - if(checkSnow)then - - ! *** remove ice due to sublimation... - ! -------------------------------------------------------------- - sublime: associate(& - scalarCanopySublimation => eqns_data%flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1), & ! sublimation from the vegetation canopy (kg m-2 s-1) - scalarSnowSublimation => eqns_data%flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! sublimation from the snow surface (kg m-2 s-1) - scalarLatHeatCanopyEvap => eqns_data%flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1), & ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - scalarSenHeatCanopy => eqns_data%flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1), & ! sensible heat flux from the canopy to the canopy air space (W m-2) - scalarLatHeatGround => eqns_data%flux_data%var(iLookFLUX%scalarLatHeatGround)%dat(1), & ! latent heat flux from ground surface below vegetation (W m-2) - scalarSenHeatGround => eqns_data%flux_data%var(iLookFLUX%scalarSenHeatGround)%dat(1) & ! sensible heat flux from ground surface below vegetation (W m-2) - ) ! associations to variables in data structures - - ! * compute change in canopy ice content due to sublimation... - ! ------------------------------------------------------------ - if(eqns_data%computeVegFlux)then - - ! remove mass of ice on the canopy - eqns_data%scalarCanopyIceTrial = eqns_data%scalarCanopyIceTrial + scalarCanopySublimation*dt_last(1) - - ! if removed all ice, take the remaining sublimation from water - if(eqns_data%scalarCanopyIceTrial < 0._dp)then - eqns_data%scalarCanopyLiqTrial = eqns_data%scalarCanopyLiqTrial + eqns_data%scalarCanopyIceTrial - eqns_data%scalarCanopyIceTrial = 0._dp - endif - - ! modify fluxes if there is insufficient canopy water to support the converged sublimation rate over the time step dt_last(1) - if(eqns_data%scalarCanopyLiqTrial < 0._dp)then - ! --> superfluous sublimation flux - superflousSub = -eqns_data%scalarCanopyLiqTrial/dt_last(1) ! kg m-2 s-1 - superflousNrg = superflousSub*LH_sub ! W m-2 (J m-2 s-1) - ! --> update fluxes and states - scalarCanopySublimation = scalarCanopySublimation + superflousSub - scalarLatHeatCanopyEvap = scalarLatHeatCanopyEvap + superflousNrg - scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg - eqns_data%scalarCanopyLiqTrial = 0._dp - endif - - end if ! (if computing the vegetation flux) - - end associate sublime - - - call computSnowDepth(& + if(checkSnow)then + + call computSnowDepth(& dt_last(1), & ! intent(in) eqns_data%nSnow, & ! intent(in) eqns_data%mLayerVolFracLiqTrial, & ! intent(inout) @@ -622,7 +581,7 @@ subroutine fidaSolver( & eqns_data%flux_data, & ! intent(in) eqns_data%diag_data, & ! intent(in) ! output - eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(out) + eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(out) ! error control err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if @@ -633,15 +592,6 @@ subroutine fidaSolver( & eqns_data%prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) eqns_data%prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (eqns_data%mLayerVolFracLiqTrial(1:nSnow)*iden_water + eqns_data%mLayerVolFracIceTrial(1:nSnow)*iden_ice) * eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) end if - - ! update coordinate variables - call calcHeight(& - ! input/output: data structures - eqns_data%indx_data, & ! intent(in): layer type - eqns_data%prog_data, & ! intent(inout): model variables for a local HRU - ! output: error control - err,message) - if(err/=0)then; err=20; return; end if ! check the need to merge snow layers tooMuchMelt = .false. @@ -661,24 +611,24 @@ subroutine fidaSolver( & endif divideLayer = .false. - call doesLayerDivide(& + call needDivideLayer(& ! input/output: model data structures model_decisions, & ! intent(in): model decisions eqns_data%mpar_data, & ! intent(in): model parameters eqns_data%nSnow, & ! intent(in): number of snow layers - eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): - eqns_data%prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(in) + eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): + eqns_data%prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(in) ! output divideLayer, & ! intent(out): flag to denote that a layer was divided err,message) ! intent(out): error control if(divideLayer) then - print *, 'divideLayer, tret = ', tret(1) - exit + print *, 'divideLayer, tret = ', tret(1) + exit endif mergedLayers = .false. - call doesLayerMerge(& + call needMergeLayers(& ! input/output: model data structures tooMuchMelt, & ! intent(in): flag to force merge of snow layers model_decisions, & ! intent(in): model decisions @@ -690,29 +640,10 @@ subroutine fidaSolver( & err,message) ! intent(out): error control if(mergedLayers) then - print *, 'mergedLayers, tret = ', tret(1) - exit - endif - - ! *** compute melt of the "snow without a layer"... - ! ------------------------------------------------- - ! NOTE: forms a surface melt pond, which drains into the upper-most soil layer through the time step - ! (check for the special case of "snow without a layer") - if(eqns_data%nSnow==0)then - call implctMelt(& - ! input/output: integrated snowpack properties - eqns_data%prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(inout): snow water equivalent (kg m-2) - eqns_data%prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(inout): snow depth (m) - eqns_data%prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1), & ! intent(inout): surface melt pond (kg m-2) - ! input/output: properties of the upper-most soil layer - eqns_data%mLayerTempTrial(nSnow+1), & ! intent(inout): surface layer temperature (K) - eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1), & ! intent(inout): surface layer depth (m) - eqns_data%diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat(nSnow+1),& ! intent(inout): surface layer volumetric heat capacity (J m-3 K-1) - ! output: error control - err,message) ! intent(out): error control - if(err/=0)then; err=20; return; end if - end if - + print *, 'mergedLayers, tret = ', tret(1) + exit + endif + endif ! checkSnow diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/groundwatr.f90 b/build/source/engine/groundwatr.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/indexState.f90 b/build/source/engine/indexState.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/layerDivide.f90 b/build/source/engine/layerDivide.f90 old mode 100755 new mode 100644 index 1ef9d56a4..c640ae0a7 --- a/build/source/engine/layerDivide.f90 +++ b/build/source/engine/layerDivide.f90 @@ -73,14 +73,14 @@ module layerDivide_module implicit none private public::layerDivide -public::doesLayerDivide +public::needDivideLayer contains ! *********************************************************************************************************** - ! public subroutine doesLayerDivide: check to see if we need to add new snowfall to the system + ! public subroutine needDivideLayer: check to see if we need to add new snowfall to the system ! *********************************************************************************************************** - subroutine doesLayerDivide(& + subroutine needDivideLayer(& ! input/output: model data structures model_decisions, & ! intent(in): model decisions mpar_data, & ! intent(in): model parameters @@ -118,7 +118,7 @@ subroutine doesLayerDivide(& logical(lgt) :: createLayer ! flag to indicate we are creating a new snow layer ! -------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="doesLayerDivide/" + err=0; message="needDivideLayer/" ! -------------------------------------------------------------------------------------------------------- ! associate variables in the data structures associate(& @@ -203,7 +203,7 @@ subroutine doesLayerDivide(& ! end associate variables in data structure end associate - end subroutine doesLayerDivide + end subroutine needDivideLayer ! *********************************************************************************************************** diff --git a/build/source/engine/layerDivideFida.f90 b/build/source/engine/layerDivideFida.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/layerMerge.f90 b/build/source/engine/layerMerge.f90 old mode 100755 new mode 100644 index 638588dca..6b1096b43 --- a/build/source/engine/layerMerge.f90 +++ b/build/source/engine/layerMerge.f90 @@ -62,15 +62,15 @@ module layerMerge_module implicit none private public::layerMerge -public::doesLayerMerge +public::needMergeLayers contains ! ***************************************************************************************************************** - ! public subroutine doesLayerMerge: Should we merge layers? (if the thickness is less than zmin) + ! public subroutine needMergeLayers: Should we merge layers? (if the thickness is less than zmin) ! ***************************************************************************************************************** - subroutine doesLayerMerge(& + subroutine needMergeLayers(& ! input/output: model data structures tooMuchMelt, & ! intent(in): flag to force merge of snow layers model_decisions, & ! intent(in): model decisions @@ -103,7 +103,7 @@ subroutine doesLayerMerge(& integer(i4b) :: jSnow ! index of snow layer identified for combination with iSnow integer(i4b) :: kSnow ! index of the upper layer of the two layers identified for combination ! initialize error control - err=0; message="doesLayerMerge/" + err=0; message="needMergeLayers/" ! -------------------------------------------------------------------------------------------------------- ! associate variables to the data structures associate(& @@ -169,7 +169,7 @@ subroutine doesLayerMerge(& ! end association to variables in the data structure end associate - end subroutine doesLayerMerge + end subroutine needMergeLayers ! ***************************************************************************************************************** diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/matrixOper.f90 b/build/source/engine/matrixOper.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/nr_utility.f90 b/build/source/engine/nr_utility.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/nrtype.f90 b/build/source/engine/nrtype.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/pOverwrite.f90 b/build/source/engine/pOverwrite.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/paramCheck.f90 b/build/source/engine/paramCheck.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/qTimeDelay.f90 b/build/source/engine/qTimeDelay.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/read_attrb.f90 b/build/source/engine/read_attrb.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/read_force.f90 b/build/source/engine/read_force.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/read_param.f90 b/build/source/engine/read_param.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/read_pinit.f90 b/build/source/engine/read_pinit.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/run_oneGRU.f90 b/build/source/engine/run_oneGRU.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/run_oneHRU.f90 b/build/source/engine/run_oneHRU.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/snowAlbedo.f90 b/build/source/engine/snowAlbedo.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/snowLiqFlx.f90 b/build/source/engine/snowLiqFlx.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/snow_utils.f90 b/build/source/engine/snow_utils.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/snwCompact.f90 b/build/source/engine/snwCompact.f90 old mode 100755 new mode 100644 index 8f3441bce..92e767a5a --- a/build/source/engine/snwCompact.f90 +++ b/build/source/engine/snwCompact.f90 @@ -52,7 +52,7 @@ subroutine snwDensify(& grainGrowthRate, & ! intent(in): rate of grain growth (s-1) densScalOvrbdn, & ! intent(in): density scaling factor for overburden pressure (kg-1 m3) tempScalOvrbdn, & ! intent(in): temperature scaling factor for overburden pressure (K-1) - baseViscosity, & ! intent(in): viscosity coefficient at T=T_frz and snow density=0 (kg m-2 s) + baseViscosity, & ! intent(in): viscosity coefficient at T=T_frz and snow density=0 (kg m-2 s) ! intent(inout): state variables mLayerDepth, & ! intent(inout): depth of each layer (m) diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/soil_utils.f90 b/build/source/engine/soil_utils.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/spline_int.f90 b/build/source/engine/spline_int.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/stomResist.f90 b/build/source/engine/stomResist.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/summaSolve.f90 b/build/source/engine/summaSolve.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/sunGeomtry.f90 b/build/source/engine/sunGeomtry.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/tempAdjust.f90 b/build/source/engine/tempAdjust.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/time_utils.f90 b/build/source/engine/time_utils.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/updatState.f90 b/build/source/engine/updatState.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/var_derive.f90 b/build/source/engine/var_derive.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/vegLiqFlux.f90 b/build/source/engine/vegLiqFlux.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/vegNrgFluxFida.f90 b/build/source/engine/vegNrgFluxFida.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/vegPhenlgy.f90 b/build/source/engine/vegPhenlgy.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/vegSWavRad.f90 b/build/source/engine/vegSWavRad.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/volicePack.f90 b/build/source/engine/volicePack.f90 old mode 100755 new mode 100644 index 8267f4770..662b722ee --- a/build/source/engine/volicePack.f90 +++ b/build/source/engine/volicePack.f90 @@ -110,6 +110,8 @@ subroutine volicePack(& divideLayer, & ! intent(out): flag to denote that layers were modified err,cmessage) ! intent(out): error control if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if + + print *, 'in coupled_em divideLayer = ', divideLayer ! merge snow layers if they are too thin call layerMerge(& @@ -125,6 +127,8 @@ subroutine volicePack(& mergedLayers, & ! intent(out): flag to denote that layers were modified err,cmessage) ! intent(out): error control if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if + + print *, 'in coupled_em mergeLayers = ', mergedLayers ! update the number of layers indx_data%var(iLookINDEX%nSnow)%dat(1) = count(indx_data%var(iLookINDEX%layerType)%dat==iname_snow) diff --git a/build/source/engine/volicePackFida.f90 b/build/source/engine/volicePackFida.f90 old mode 100755 new mode 100644 From 2e5ca854f1f8ef0beca2b5b7d8d8dfb1e19e8535 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 2 Jun 2021 12:56:16 -0600 Subject: [PATCH 0134/1472] minor edit --- build/source/engine/coupled_em.f90 | 2 +- build/source/engine/fidaSolver.f90 | 4 +++- build/source/engine/volicePack.f90 | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index f9d6959f2..3c3c2eae1 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -727,7 +727,7 @@ subroutine coupled_em(& nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers nState, & ! intent(in): total number of layers - dt_sub, & ! intent(inout): length of the model sub-step + dt_sub, & ! intent(inout): length of the model sub-step (nsub==1), & ! intent(in): logical flag to denote the first substep computeVegFlux, & ! intent(in): logical flag to compute fluxes within the vegetation canopy ! input/output: data structures diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 4556a90ab..2747f3eaf 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -242,7 +242,7 @@ subroutine fidaSolver( & logical(lgt) :: tooMuchMelt logical(lgt) :: divideLayer logical(lgt) :: mergedLayers - logical(lgt),parameter :: checkSnow = .true. + logical(lgt),parameter :: checkSnow = .false. real(dp) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(dp) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) real(dp) :: mLayerDepth(nLayers) @@ -646,6 +646,8 @@ subroutine fidaSolver( & endif ! checkSnow + ! if(tret(1) > 500) exit + end do ! while loop on one_step mode diff --git a/build/source/engine/volicePack.f90 b/build/source/engine/volicePack.f90 index 662b722ee..c1617f1d2 100644 --- a/build/source/engine/volicePack.f90 +++ b/build/source/engine/volicePack.f90 @@ -111,7 +111,7 @@ subroutine volicePack(& err,cmessage) ! intent(out): error control if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if - print *, 'in coupled_em divideLayer = ', divideLayer +! print *, 'in coupled_em divideLayer = ', divideLayer ! merge snow layers if they are too thin call layerMerge(& @@ -128,7 +128,7 @@ subroutine volicePack(& err,cmessage) ! intent(out): error control if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if - print *, 'in coupled_em mergeLayers = ', mergedLayers +! print *, 'in coupled_em mergeLayers = ', mergedLayers ! update the number of layers indx_data%var(iLookINDEX%nSnow)%dat(1) = count(indx_data%var(iLookINDEX%layerType)%dat==iname_snow) From ee9c1e48d52ac8d87fff708211a28cefa7230682 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 2 Jun 2021 13:29:06 -0600 Subject: [PATCH 0135/1472] prog_data are independent from checkSnow decisions --- build/source/engine/fidaSolver.f90 | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 2747f3eaf..897ba1080 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -242,7 +242,7 @@ subroutine fidaSolver( & logical(lgt) :: tooMuchMelt logical(lgt) :: divideLayer logical(lgt) :: mergedLayers - logical(lgt),parameter :: checkSnow = .false. + logical(lgt),parameter :: checkSnow = .true. real(dp) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(dp) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) real(dp) :: mLayerDepth(nLayers) @@ -568,7 +568,9 @@ subroutine fidaSolver( & eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial - + + ! if(tret(1) > 500) exit + if(checkSnow)then call computSnowDepth(& @@ -581,7 +583,7 @@ subroutine fidaSolver( & eqns_data%flux_data, & ! intent(in) eqns_data%diag_data, & ! intent(in) ! output - eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(out) + mLayerDepth, & ! intent(out) ! error control err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if @@ -589,15 +591,15 @@ subroutine fidaSolver( & ! recompute snow depth and SWE if(eqns_data%nSnow > 0)then - eqns_data%prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) - eqns_data%prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (eqns_data%mLayerVolFracLiqTrial(1:nSnow)*iden_water + eqns_data%mLayerVolFracIceTrial(1:nSnow)*iden_ice) * eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) + scalarSnowDepth = sum( mLayerDepth(1:nSnow) ) + scalarSWE = sum( (eqns_data%mLayerVolFracLiqTrial(1:nSnow)*iden_water + eqns_data%mLayerVolFracIceTrial(1:nSnow)*iden_ice) * mLayerDepth(1:nSnow) ) end if ! check the need to merge snow layers tooMuchMelt = .false. if(eqns_data%nSnow>0)then ! check that we did not remove the entire layer - if(eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat(1) < 1.e-6_dp) exit + if(mLayerDepth(1) < 1.e-6_dp) exit ! compute the energy required to melt the top snow layer (J m-2) bulkDensity = eqns_data%mLayerVolFracIceTrial(1)*iden_ice + eqns_data%mLayerVolFracLiqTrial(1)*iden_water volEnthalpy = temp2ethpy(eqns_data%mLayerTempTrial(1),bulkDensity,eqns_data%mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1)) @@ -616,8 +618,8 @@ subroutine fidaSolver( & model_decisions, & ! intent(in): model decisions eqns_data%mpar_data, & ! intent(in): model parameters eqns_data%nSnow, & ! intent(in): number of snow layers - eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): - eqns_data%prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(in) + mLayerDepth, & ! intent(in): + scalarSnowDepth, & ! intent(in) ! output divideLayer, & ! intent(out): flag to denote that a layer was divided err,message) ! intent(out): error control @@ -634,7 +636,7 @@ subroutine fidaSolver( & model_decisions, & ! intent(in): model decisions eqns_data%mpar_data, & ! intent(in): model parameters eqns_data%nSnow, & ! intent(in): - eqns_data%prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(inout): model prognostic variables for a local HRU + mLayerDepth, & ! intent(inout): model prognostic variables for a local HRU ! output mergedLayers, & ! intent(out): flag to denote that layers were merged err,message) ! intent(out): error control @@ -646,8 +648,6 @@ subroutine fidaSolver( & endif ! checkSnow - ! if(tret(1) > 500) exit - end do ! while loop on one_step mode From 8f117b9fd3d96cd6cf51d0083a811403bb0469cf Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 3 Jun 2021 13:29:09 -0600 Subject: [PATCH 0136/1472] tret instead of dt_last for computing snow layers in fidaSolver --- build/source/engine/coupled_em.f90 | 9 +++++++++ build/source/engine/fidaSolver.f90 | 28 +++++++++++++++++++++------- build/source/engine/volicePack.f90 | 4 ++-- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 3c3c2eae1..911c7b1d0 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -844,6 +844,15 @@ subroutine coupled_em(& err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if + print *, '--------- coupled_em---------' + print *, 'dt_sub = ', dt_sub + print *, 'nSnow = ', nSnow + print *, 'mLayerVolFracLiq = ', mLayerVolFracLiq(:) + print *, 'mLayerVolFracIce = ', mLayerVolFracIce(:) + print *, 'mLayerTemp = ', prog_data%var(iLookPROG%mLayerTemp)%dat(:) + print *, 'mLayerDepth = ', mLayerDepth(:) + print *, '--------------------------------------------------------' + end associate sublime ! update coordinate variables diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 897ba1080..eea5052f4 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -550,6 +550,10 @@ subroutine fidaSolver( & end select dt_past = dt_last(1) + do iVar=1,size(flux_meta) + flux_data%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / tret(1) + end do + ! sum of mLayerCmpress mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) @@ -574,13 +578,13 @@ subroutine fidaSolver( & if(checkSnow)then call computSnowDepth(& - dt_last(1), & ! intent(in) + tret(1), & ! intent(in) eqns_data%nSnow, & ! intent(in) eqns_data%mLayerVolFracLiqTrial, & ! intent(inout) eqns_data%mLayerVolFracIceTrial, & ! intent(inout) eqns_data%mLayerTempTrial, & ! intent(in) eqns_data%mpar_data, & ! intent(in) - eqns_data%flux_data, & ! intent(in) + flux_data, & ! intent(in) eqns_data%diag_data, & ! intent(in) ! output mLayerDepth, & ! intent(out) @@ -603,7 +607,7 @@ subroutine fidaSolver( & ! compute the energy required to melt the top snow layer (J m-2) bulkDensity = eqns_data%mLayerVolFracIceTrial(1)*iden_ice + eqns_data%mLayerVolFracLiqTrial(1)*iden_water volEnthalpy = temp2ethpy(eqns_data%mLayerTempTrial(1),bulkDensity,eqns_data%mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1)) - if(-volEnthalpy < eqns_data%flux_data%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_last(1)) tooMuchMelt = .true. + if(-volEnthalpy < flux_data%var(iLookFLUX%mLayerNrgFlux)%dat(1)*tret(1)) tooMuchMelt = .true. endif @@ -624,8 +628,13 @@ subroutine fidaSolver( & divideLayer, & ! intent(out): flag to denote that a layer was divided err,message) ! intent(out): error control if(divideLayer) then - print *, 'divideLayer, tret = ', tret(1) - exit + print *, 'divideLayer, tret = ', tret(1) + print *, 'nSnow = ', eqns_data%nSnow + print *, 'mLayerVolFracLiq = ', eqns_data%mLayerVolFracLiqTrial(:) + print *, 'mLayerVolFracIce = ', eqns_data%mLayerVolFracIceTrial(:) + print *, 'mLayerTemp = ', eqns_data%mLayerTempTrial(:) + print *, 'mLayerDepth = ', mLayerDepth(:) + exit endif @@ -642,8 +651,13 @@ subroutine fidaSolver( & err,message) ! intent(out): error control if(mergedLayers) then - print *, 'mergedLayers, tret = ', tret(1) - exit + print *, 'mergedLayers, tret = ', tret(1) + print *, 'nSnow = ', eqns_data%nSnow + print *, 'mLayerVolFracLiq = ', eqns_data%mLayerVolFracLiqTrial(:) + print *, 'mLayerVolFracIce = ', eqns_data%mLayerVolFracIceTrial(:) + print *, 'mLayerTemp = ', eqns_data%mLayerTempTrial(:) + print *, 'mLayerDepth = ', mLayerDepth(:) + exit endif endif ! checkSnow diff --git a/build/source/engine/volicePack.f90 b/build/source/engine/volicePack.f90 index c1617f1d2..662b722ee 100644 --- a/build/source/engine/volicePack.f90 +++ b/build/source/engine/volicePack.f90 @@ -111,7 +111,7 @@ subroutine volicePack(& err,cmessage) ! intent(out): error control if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if -! print *, 'in coupled_em divideLayer = ', divideLayer + print *, 'in coupled_em divideLayer = ', divideLayer ! merge snow layers if they are too thin call layerMerge(& @@ -128,7 +128,7 @@ subroutine volicePack(& err,cmessage) ! intent(out): error control if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if -! print *, 'in coupled_em mergeLayers = ', mergedLayers + print *, 'in coupled_em mergeLayers = ', mergedLayers ! update the number of layers indx_data%var(iLookINDEX%nSnow)%dat(1) = count(indx_data%var(iLookINDEX%layerType)%dat==iname_snow) From 449e83769f28b3e567b2009e3a230c57365f1e31 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 3 Jun 2021 23:14:10 -0600 Subject: [PATCH 0137/1472] mLayerDepth from prog_data is constant in fidaSolver --- build/source/engine/computSnowDepth.f90 | 4 ++-- build/source/engine/coupled_em.f90 | 9 ++++----- build/source/engine/fidaSolver.f90 | 10 +++++++--- build/source/engine/volicePack.f90 | 4 ++-- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/build/source/engine/computSnowDepth.f90 b/build/source/engine/computSnowDepth.f90 index c85257a80..10217cf67 100644 --- a/build/source/engine/computSnowDepth.f90 +++ b/build/source/engine/computSnowDepth.f90 @@ -52,7 +52,7 @@ subroutine computSnowDepth(& flux_data, & ! intent(in) diag_data, & ! intent(in) ! output - mLayerDepth, & ! intent(out) + mLayerDepth, & ! intent(inout) ! error control err,message) ! intent(out): error control @@ -67,7 +67,7 @@ subroutine computSnowDepth(& type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - real(dp),intent(out) :: mLayerDepth(:) + real(dp),intent(inout) :: mLayerDepth(:) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 911c7b1d0..43ff4a4b3 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -719,6 +719,7 @@ subroutine coupled_em(& ! save input step dtSave = dt_sub !write(*,'(a,1x,3(f12.5,1x))') trim(message)//'before opSplittin: dt_init, dt_sub, dt_solv = ', dt_init, dt_sub, dt_solv + ! get the new solution call opSplittin(& @@ -839,20 +840,18 @@ subroutine coupled_em(& flux_data, & ! intent(in) diag_data, & ! intent(in) ! output - mLayerDepth, & ! intent(out) + mLayerDepth, & ! intent(inout) ! error control err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if - +if(1==0)then print *, '--------- coupled_em---------' - print *, 'dt_sub = ', dt_sub - print *, 'nSnow = ', nSnow print *, 'mLayerVolFracLiq = ', mLayerVolFracLiq(:) print *, 'mLayerVolFracIce = ', mLayerVolFracIce(:) print *, 'mLayerTemp = ', prog_data%var(iLookPROG%mLayerTemp)%dat(:) print *, 'mLayerDepth = ', mLayerDepth(:) print *, '--------------------------------------------------------' - +endif end associate sublime ! update coordinate variables diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index eea5052f4..dadc6351a 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -576,7 +576,7 @@ subroutine fidaSolver( & ! if(tret(1) > 500) exit if(checkSnow)then - + mLayerDepth(:) = prog_data%var(iLookPROG%mLayerDepth)%dat(:) call computSnowDepth(& tret(1), & ! intent(in) eqns_data%nSnow, & ! intent(in) @@ -627,13 +627,15 @@ subroutine fidaSolver( & ! output divideLayer, & ! intent(out): flag to denote that a layer was divided err,message) ! intent(out): error control - if(divideLayer) then + if(divideLayer .and. tret(1)>50) then print *, 'divideLayer, tret = ', tret(1) +if(1==0)then print *, 'nSnow = ', eqns_data%nSnow print *, 'mLayerVolFracLiq = ', eqns_data%mLayerVolFracLiqTrial(:) print *, 'mLayerVolFracIce = ', eqns_data%mLayerVolFracIceTrial(:) print *, 'mLayerTemp = ', eqns_data%mLayerTempTrial(:) print *, 'mLayerDepth = ', mLayerDepth(:) +endif exit endif @@ -650,13 +652,15 @@ subroutine fidaSolver( & mergedLayers, & ! intent(out): flag to denote that layers were merged err,message) ! intent(out): error control - if(mergedLayers) then + if(mergedLayers .and. tret(1)>50) then print *, 'mergedLayers, tret = ', tret(1) +if(1==0)then print *, 'nSnow = ', eqns_data%nSnow print *, 'mLayerVolFracLiq = ', eqns_data%mLayerVolFracLiqTrial(:) print *, 'mLayerVolFracIce = ', eqns_data%mLayerVolFracIceTrial(:) print *, 'mLayerTemp = ', eqns_data%mLayerTempTrial(:) print *, 'mLayerDepth = ', mLayerDepth(:) +endif exit endif diff --git a/build/source/engine/volicePack.f90 b/build/source/engine/volicePack.f90 index 662b722ee..c1617f1d2 100644 --- a/build/source/engine/volicePack.f90 +++ b/build/source/engine/volicePack.f90 @@ -111,7 +111,7 @@ subroutine volicePack(& err,cmessage) ! intent(out): error control if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if - print *, 'in coupled_em divideLayer = ', divideLayer +! print *, 'in coupled_em divideLayer = ', divideLayer ! merge snow layers if they are too thin call layerMerge(& @@ -128,7 +128,7 @@ subroutine volicePack(& err,cmessage) ! intent(out): error control if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if - print *, 'in coupled_em mergeLayers = ', mergedLayers +! print *, 'in coupled_em mergeLayers = ', mergedLayers ! update the number of layers indx_data%var(iLookINDEX%nSnow)%dat(1) = count(indx_data%var(iLookINDEX%layerType)%dat==iname_snow) From 890c4af60dbe20fcf4616e6bee24a3db97d6581b Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 4 Jun 2021 00:07:15 -0600 Subject: [PATCH 0138/1472] before mLayerMeltFreez fixing --- build/source/engine/coupled_em.f90 | 11 ++++++----- build/source/engine/fidaSolver.f90 | 26 +++++++++++++------------- build/source/engine/opSplittin.f90 | 4 +++- build/source/engine/volicePack.f90 | 4 ++-- 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 43ff4a4b3..f010a024b 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -844,14 +844,15 @@ subroutine coupled_em(& ! error control err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if -if(1==0)then +!if(1==0)then print *, '--------- coupled_em---------' + print *, 'mLayerMeltFreeze = ', diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(1:nSnow) print *, 'mLayerVolFracLiq = ', mLayerVolFracLiq(:) - print *, 'mLayerVolFracIce = ', mLayerVolFracIce(:) - print *, 'mLayerTemp = ', prog_data%var(iLookPROG%mLayerTemp)%dat(:) - print *, 'mLayerDepth = ', mLayerDepth(:) +! print *, 'mLayerVolFracIce = ', mLayerVolFracIce(:) +! print *, 'mLayerTemp = ', prog_data%var(iLookPROG%mLayerTemp)%dat(:) +! print *, 'mLayerDepth = ', mLayerDepth(:) print *, '--------------------------------------------------------' -endif +!endif end associate sublime ! update coordinate variables diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index dadc6351a..38765acd9 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -587,7 +587,7 @@ subroutine fidaSolver( & flux_data, & ! intent(in) eqns_data%diag_data, & ! intent(in) ! output - mLayerDepth, & ! intent(out) + mLayerDepth, & ! intent(inout) ! error control err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if @@ -629,13 +629,13 @@ subroutine fidaSolver( & err,message) ! intent(out): error control if(divideLayer .and. tret(1)>50) then print *, 'divideLayer, tret = ', tret(1) -if(1==0)then - print *, 'nSnow = ', eqns_data%nSnow +!if(1==0)then + print *, 'mLayerMeltFreeze = ', eqns_data%diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(1:nSnow) print *, 'mLayerVolFracLiq = ', eqns_data%mLayerVolFracLiqTrial(:) - print *, 'mLayerVolFracIce = ', eqns_data%mLayerVolFracIceTrial(:) - print *, 'mLayerTemp = ', eqns_data%mLayerTempTrial(:) - print *, 'mLayerDepth = ', mLayerDepth(:) -endif +! print *, 'mLayerVolFracIce = ', eqns_data%mLayerVolFracIceTrial(:) +! print *, 'mLayerTemp = ', eqns_data%mLayerTempTrial(:) +! print *, 'mLayerDepth = ', mLayerDepth(:) +!endif exit endif @@ -654,13 +654,13 @@ subroutine fidaSolver( & if(mergedLayers .and. tret(1)>50) then print *, 'mergedLayers, tret = ', tret(1) -if(1==0)then - print *, 'nSnow = ', eqns_data%nSnow +!if(1==0)then + print *, 'mLayerMeltFreeze = ', eqns_data%diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(1:nSnow) print *, 'mLayerVolFracLiq = ', eqns_data%mLayerVolFracLiqTrial(:) - print *, 'mLayerVolFracIce = ', eqns_data%mLayerVolFracIceTrial(:) - print *, 'mLayerTemp = ', eqns_data%mLayerTempTrial(:) - print *, 'mLayerDepth = ', mLayerDepth(:) -endif +! print *, 'mLayerVolFracIce = ', eqns_data%mLayerVolFracIceTrial(:) +! print *, 'mLayerTemp = ', eqns_data%mLayerTempTrial(:) +! print *, 'mLayerDepth = ', mLayerDepth(:) +!endif exit endif diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index f185b48a1..3361d09ef 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -1032,8 +1032,10 @@ subroutine opSplittin(& if(ixCoupling/=fullyCoupled .or. nSubsteps>1) dtMultiplier=0.5_dp ! compute the melt in each snow and soil layer - if(nSnow>0) diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat( 1:nSnow ) = -(mLayerVolFracIce( 1:nSnow ) - mLayerVolFracIceInit( 1:nSnow ))*iden_ice + if(nSnow>0)then + diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(1:nSnow) = -( mLayerVolFracIce(1:nSnow) - mLayerVolFracIceInit(1:nSnow) ) * iden_ice diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(nSnow+1:nLayers) = -(mLayerVolFracIce(nSnow+1:nLayers) - mLayerVolFracIceInit(nSnow+1:nLayers))*iden_water + endif ! end associate statements end associate globalVars diff --git a/build/source/engine/volicePack.f90 b/build/source/engine/volicePack.f90 index c1617f1d2..662b722ee 100644 --- a/build/source/engine/volicePack.f90 +++ b/build/source/engine/volicePack.f90 @@ -111,7 +111,7 @@ subroutine volicePack(& err,cmessage) ! intent(out): error control if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if -! print *, 'in coupled_em divideLayer = ', divideLayer + print *, 'in coupled_em divideLayer = ', divideLayer ! merge snow layers if they are too thin call layerMerge(& @@ -128,7 +128,7 @@ subroutine volicePack(& err,cmessage) ! intent(out): error control if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if -! print *, 'in coupled_em mergeLayers = ', mergedLayers + print *, 'in coupled_em mergeLayers = ', mergedLayers ! update the number of layers indx_data%var(iLookINDEX%nSnow)%dat(1) = count(indx_data%var(iLookINDEX%layerType)%dat==iname_snow) From 46799958a04558f929a90d5943235366cc4bc7ae Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 4 Jun 2021 00:27:40 -0600 Subject: [PATCH 0139/1472] computSnowDepth updated --- build/source/engine/computSnowDepth.f90 | 16 +++++--------- build/source/engine/coupled_em.f90 | 8 +++---- build/source/engine/fidaSolver.f90 | 12 +++++----- build/source/engine/varSubstepFida.f90 | 29 +------------------------ build/source/engine/volicePack.f90 | 4 ++-- 5 files changed, 18 insertions(+), 51 deletions(-) diff --git a/build/source/engine/computSnowDepth.f90 b/build/source/engine/computSnowDepth.f90 index 10217cf67..fc6170dc7 100644 --- a/build/source/engine/computSnowDepth.f90 +++ b/build/source/engine/computSnowDepth.f90 @@ -45,12 +45,12 @@ module computSnowDepth_module subroutine computSnowDepth(& dt_sub, & nSnow, & ! intent(in) + scalarSnowSublimation, & ! intent(in) mLayerVolFracLiq, & ! intent(inout) mLayerVolFracIce, & ! intent(inout) mLayerTemp, & ! intent(in) + mLayerMeltFreeze, & ! intent(in) mpar_data, & ! intent(in) - flux_data, & ! intent(in) - diag_data, & ! intent(in) ! output mLayerDepth, & ! intent(inout) ! error control @@ -61,12 +61,12 @@ subroutine computSnowDepth(& implicit none real(qp),intent(in) :: dt_sub integer(i4b),intent(in) :: nSnow ! number of snow layers + real(dp),intent(in) :: scalarSnowSublimation real(dp),intent(inout) :: mLayerVolFracLiq(:) real(dp),intent(inout) :: mLayerVolFracIce(:) real(dp),intent(in) :: mLayerTemp(:) + real(dp),intent(in) :: mLayerMeltFreeze(:) type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU real(dp),intent(inout) :: mLayerDepth(:) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -75,11 +75,6 @@ subroutine computSnowDepth(& character(len=256) :: cmessage ! error message integer(i4b) :: iSnow ! index of snow layers real(dp) :: massLiquid ! mass liquid water (kg m-2) - - - associate(& - scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) & ! sublimation from the snow surface (kg m-2 s-1) - ) ! * compute change in ice content of the top snow layer due to sublimation... ! --------------------------------------------------------------------------- @@ -119,7 +114,7 @@ subroutine computSnowDepth(& dt_sub, & ! intent(in): time step (s) nSnow, & ! intent(in): number of snow layers mLayerTemp(1:nSnow), & ! intent(in): temperature of each layer (K) - diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(1:nSnow), & ! intent(in): volumetric melt in each layer (kg m-3) + mLayerMeltFreeze(1:nSnow), & ! intent(in): volumetric melt in each layer (kg m-3) ! intent(in): parameters mpar_data%var(iLookPARAM%densScalGrowth)%dat(1), & ! intent(in): density scaling factor for grain growth (kg-1 m3) mpar_data%var(iLookPARAM%tempScalGrowth)%dat(1), & ! intent(in): temperature scaling factor for grain growth (K-1) @@ -136,7 +131,6 @@ subroutine computSnowDepth(& if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if end if ! if snow layers exist - end associate end subroutine computSnowDepth diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index f010a024b..778ce34ca 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -833,18 +833,18 @@ subroutine coupled_em(& call computSnowDepth(& dt_sub, & ! intent(in) nSnow, & ! intent(in) + scalarSnowSublimation, & ! intent(in) mLayerVolFracLiq, & ! intent(inout) mLayerVolFracIce, & ! intent(inout) prog_data%var(iLookPROG%mLayerTemp)%dat, & ! intent(in) + diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat, & ! intent(in) mpar_data, & ! intent(in) - flux_data, & ! intent(in) - diag_data, & ! intent(in) ! output mLayerDepth, & ! intent(inout) ! error control err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if -!if(1==0)then +if(1==0)then print *, '--------- coupled_em---------' print *, 'mLayerMeltFreeze = ', diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(1:nSnow) print *, 'mLayerVolFracLiq = ', mLayerVolFracLiq(:) @@ -852,7 +852,7 @@ subroutine coupled_em(& ! print *, 'mLayerTemp = ', prog_data%var(iLookPROG%mLayerTemp)%dat(:) ! print *, 'mLayerDepth = ', mLayerDepth(:) print *, '--------------------------------------------------------' -!endif +endif end associate sublime ! update coordinate variables diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 38765acd9..c6486152e 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -580,12 +580,12 @@ subroutine fidaSolver( & call computSnowDepth(& tret(1), & ! intent(in) eqns_data%nSnow, & ! intent(in) + flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! intent(in) eqns_data%mLayerVolFracLiqTrial, & ! intent(inout) eqns_data%mLayerVolFracIceTrial, & ! intent(inout) eqns_data%mLayerTempTrial, & ! intent(in) + eqns_data%diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat, & ! intent(in) eqns_data%mpar_data, & ! intent(in) - flux_data, & ! intent(in) - eqns_data%diag_data, & ! intent(in) ! output mLayerDepth, & ! intent(inout) ! error control @@ -629,13 +629,13 @@ subroutine fidaSolver( & err,message) ! intent(out): error control if(divideLayer .and. tret(1)>50) then print *, 'divideLayer, tret = ', tret(1) -!if(1==0)then +if(1==0)then print *, 'mLayerMeltFreeze = ', eqns_data%diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(1:nSnow) print *, 'mLayerVolFracLiq = ', eqns_data%mLayerVolFracLiqTrial(:) ! print *, 'mLayerVolFracIce = ', eqns_data%mLayerVolFracIceTrial(:) ! print *, 'mLayerTemp = ', eqns_data%mLayerTempTrial(:) ! print *, 'mLayerDepth = ', mLayerDepth(:) -!endif +endif exit endif @@ -654,13 +654,13 @@ subroutine fidaSolver( & if(mergedLayers .and. tret(1)>50) then print *, 'mergedLayers, tret = ', tret(1) -!if(1==0)then +if(1==0)then print *, 'mLayerMeltFreeze = ', eqns_data%diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(1:nSnow) print *, 'mLayerVolFracLiq = ', eqns_data%mLayerVolFracLiqTrial(:) ! print *, 'mLayerVolFracIce = ', eqns_data%mLayerVolFracIceTrial(:) ! print *, 'mLayerTemp = ', eqns_data%mLayerTempTrial(:) ! print *, 'mLayerDepth = ', mLayerDepth(:) -!endif +endif exit endif diff --git a/build/source/engine/varSubstepFida.f90 b/build/source/engine/varSubstepFida.f90 index 6a9a3ee86..945f2062b 100644 --- a/build/source/engine/varSubstepFida.f90 +++ b/build/source/engine/varSubstepFida.f90 @@ -548,7 +548,6 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt USE varExtrFida_module, only:varExtractFida USE computEnthalpy_module,only:computEnthalpy USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy - USE computSnowDepth_module,only:computSnowDepth implicit none ! model control real(dp) ,intent(in) :: dt ! time step (s) @@ -1064,33 +1063,7 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt ! ----- ! * update prognostic variables... ! -------------------------------- - - - if(1==0)then - call computSnowDepth(& - dt, & ! intent(in) - nSnow, & ! intent(in) - mLayerVolFracLiqTrial, & ! intent(inout) - mLayerVolFracIceTrial, & ! intent(inout) - mLayerTempTrial, & ! intent(in) - mpar_data, & ! intent(in) - flux_data, & ! intent(in) - diag_data, & ! intent(in) - ! output - mLayerDepth, & ! intent(out) - ! error control - err,message) ! intent(out): error control - if(err/=0)then; err=55; return; end if - - ! recompute snow depth and SWE - if(nSnow > 0)then - prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( mLayerDepth(1:nSnow) ) - prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (mLayerVolFracLiqTrial(1:nSnow)*iden_water + mLayerVolFracIceTrial(1:nSnow)*iden_ice) * mLayerDepth(1:nSnow) ) - end if - - endif - - ! update state variables for the vegetation canopy + ! update state variables for the vegetation canopy scalarCanairTemp = scalarCanairTempTrial ! trial value of canopy air temperature (K) scalarCanopyTemp = scalarCanopyTempTrial ! trial value of canopy temperature (K) scalarCanopyWat = scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) diff --git a/build/source/engine/volicePack.f90 b/build/source/engine/volicePack.f90 index 662b722ee..c1617f1d2 100644 --- a/build/source/engine/volicePack.f90 +++ b/build/source/engine/volicePack.f90 @@ -111,7 +111,7 @@ subroutine volicePack(& err,cmessage) ! intent(out): error control if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if - print *, 'in coupled_em divideLayer = ', divideLayer +! print *, 'in coupled_em divideLayer = ', divideLayer ! merge snow layers if they are too thin call layerMerge(& @@ -128,7 +128,7 @@ subroutine volicePack(& err,cmessage) ! intent(out): error control if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if - print *, 'in coupled_em mergeLayers = ', mergedLayers +! print *, 'in coupled_em mergeLayers = ', mergedLayers ! update the number of layers indx_data%var(iLookINDEX%nSnow)%dat(1) = count(indx_data%var(iLookINDEX%layerType)%dat==iname_snow) From cfa1756bec681cd0754a8b90524a1ba027f3a344 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 4 Jun 2021 00:40:59 -0600 Subject: [PATCH 0140/1472] fixed mLayerMeltFreeze in fidaSolver --- build/source/engine/fidaSolver.f90 | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index c6486152e..83dd97aee 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -248,6 +248,7 @@ subroutine fidaSolver( & real(dp) :: mLayerDepth(nLayers) real(dp) :: scalarSnowDepth real(dp) :: scalarSWE + real(dp) :: mLayerMeltFreeze(nLayers) !======= Internals ============ @@ -576,7 +577,13 @@ subroutine fidaSolver( & ! if(tret(1) > 500) exit if(checkSnow)then - mLayerDepth(:) = prog_data%var(iLookPROG%mLayerDepth)%dat(:) + mLayerDepth(:) = prog_data%var(iLookPROG%mLayerDepth)%dat(:) + ! compute the melt in each snow and soil layer + if(nSnow>0)then + mLayerMeltFreeze(1:nSnow) = -( eqns_data%mLayerVolFracIceTrial(1:nSnow) - prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow) ) * iden_ice + mLayerMeltFreeze(nSnow+1:nLayers) = -(eqns_data%mLayerVolFracIceTrial(nSnow+1:nLayers) - prog_data%var(iLookPROG%mLayerVolFracIce)%dat(nSnow+1:nLayers))*iden_water + endif + call computSnowDepth(& tret(1), & ! intent(in) eqns_data%nSnow, & ! intent(in) @@ -584,7 +591,7 @@ subroutine fidaSolver( & eqns_data%mLayerVolFracLiqTrial, & ! intent(inout) eqns_data%mLayerVolFracIceTrial, & ! intent(inout) eqns_data%mLayerTempTrial, & ! intent(in) - eqns_data%diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat, & ! intent(in) + mLayerMeltFreeze, & ! intent(in) eqns_data%mpar_data, & ! intent(in) ! output mLayerDepth, & ! intent(inout) @@ -630,7 +637,7 @@ subroutine fidaSolver( & if(divideLayer .and. tret(1)>50) then print *, 'divideLayer, tret = ', tret(1) if(1==0)then - print *, 'mLayerMeltFreeze = ', eqns_data%diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(1:nSnow) + print *, 'mLayerMeltFreeze = ', mLayerMeltFreeze(1:nSnow) print *, 'mLayerVolFracLiq = ', eqns_data%mLayerVolFracLiqTrial(:) ! print *, 'mLayerVolFracIce = ', eqns_data%mLayerVolFracIceTrial(:) ! print *, 'mLayerTemp = ', eqns_data%mLayerTempTrial(:) @@ -655,7 +662,7 @@ subroutine fidaSolver( & if(mergedLayers .and. tret(1)>50) then print *, 'mergedLayers, tret = ', tret(1) if(1==0)then - print *, 'mLayerMeltFreeze = ', eqns_data%diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(1:nSnow) + print *, 'mLayerMeltFreeze = ', mLayerMeltFreeze(1:nSnow) print *, 'mLayerVolFracLiq = ', eqns_data%mLayerVolFracLiqTrial(:) ! print *, 'mLayerVolFracIce = ', eqns_data%mLayerVolFracIceTrial(:) ! print *, 'mLayerTemp = ', eqns_data%mLayerTempTrial(:) From 03262da5fb1cb7d18e23273aeb2b76423c90fb4b Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 4 Jun 2021 00:56:44 -0600 Subject: [PATCH 0141/1472] divide and merge lagyers work fine --- build/source/engine/coupled_em.f90 | 6 +++--- build/source/engine/fidaSolver.f90 | 18 +++++++----------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 778ce34ca..5b62c080c 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -846,11 +846,11 @@ subroutine coupled_em(& if(err/=0)then; err=55; return; end if if(1==0)then print *, '--------- coupled_em---------' - print *, 'mLayerMeltFreeze = ', diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(1:nSnow) - print *, 'mLayerVolFracLiq = ', mLayerVolFracLiq(:) +! print *, 'mLayerMeltFreeze = ', diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(1:nSnow) +! print *, 'mLayerVolFracLiq = ', mLayerVolFracLiq(:) ! print *, 'mLayerVolFracIce = ', mLayerVolFracIce(:) ! print *, 'mLayerTemp = ', prog_data%var(iLookPROG%mLayerTemp)%dat(:) -! print *, 'mLayerDepth = ', mLayerDepth(:) + print *, 'mLayerDepth = ', mLayerDepth(:) print *, '--------------------------------------------------------' endif end associate sublime diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 83dd97aee..531d3606b 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -619,7 +619,7 @@ subroutine fidaSolver( & if(tooMuchMelt)then - print *, 'tooMuchMelt, tret = ', tret(1) +! print *, 'tooMuchMelt, tret = ', tret(1) exit endif @@ -635,14 +635,12 @@ subroutine fidaSolver( & divideLayer, & ! intent(out): flag to denote that a layer was divided err,message) ! intent(out): error control if(divideLayer .and. tret(1)>50) then - print *, 'divideLayer, tret = ', tret(1) -if(1==0)then - print *, 'mLayerMeltFreeze = ', mLayerMeltFreeze(1:nSnow) - print *, 'mLayerVolFracLiq = ', eqns_data%mLayerVolFracLiqTrial(:) +! print *, 'divideLayer, tret = ', tret(1) +! print *, 'mLayerMeltFreeze = ', mLayerMeltFreeze(1:nSnow) +! print *, 'mLayerVolFracLiq = ', eqns_data%mLayerVolFracLiqTrial(:) ! print *, 'mLayerVolFracIce = ', eqns_data%mLayerVolFracIceTrial(:) ! print *, 'mLayerTemp = ', eqns_data%mLayerTempTrial(:) ! print *, 'mLayerDepth = ', mLayerDepth(:) -endif exit endif @@ -660,14 +658,12 @@ subroutine fidaSolver( & err,message) ! intent(out): error control if(mergedLayers .and. tret(1)>50) then - print *, 'mergedLayers, tret = ', tret(1) -if(1==0)then - print *, 'mLayerMeltFreeze = ', mLayerMeltFreeze(1:nSnow) - print *, 'mLayerVolFracLiq = ', eqns_data%mLayerVolFracLiqTrial(:) +! print *, 'mergedLayers, tret = ', tret(1) +! print *, 'mLayerMeltFreeze = ', mLayerMeltFreeze(1:nSnow) +! print *, 'mLayerVolFracLiq = ', eqns_data%mLayerVolFracLiqTrial(:) ! print *, 'mLayerVolFracIce = ', eqns_data%mLayerVolFracIceTrial(:) ! print *, 'mLayerTemp = ', eqns_data%mLayerTempTrial(:) ! print *, 'mLayerDepth = ', mLayerDepth(:) -endif exit endif From 99f163c7225b047a173777e76369502eb246f140 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Mon, 7 Jun 2021 22:48:43 -0600 Subject: [PATCH 0142/1472] minor edit --- build/source/engine/fidaSolver.f90 | 70 +++++++++++++++--------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 531d3606b..da3638720 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -471,7 +471,7 @@ subroutine fidaSolver( & eqns_data%nState, & ! intent(in): number of state variables in the current subset eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step eqns_data%firstFluxCall, & ! intent(inout) - eqns_data%firstSplitOper, & ! intent(in) + eqns_data%firstSplitOper, & ! intent(in) eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors @@ -493,13 +493,13 @@ subroutine fidaSolver( & eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow - eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian - eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - eqns_data%scalarCanopyIceTrial, & - eqns_data%scalarCanopyIcePrev, & - eqns_data%scalarCanopyLiqTrial, & - eqns_data%scalarCanopyLiqPrev, & + eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian + eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + eqns_data%scalarCanopyIceTrial, & + eqns_data%scalarCanopyIcePrev, & + eqns_data%scalarCanopyLiqTrial, & + eqns_data%scalarCanopyLiqPrev, & eqns_data%scalarCanopyEnthalpyTrial,& ! intent(in): trial enthalpy of the vegetation canopy (J m-3) eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) eqns_data%mLayerTempTrial, & @@ -517,12 +517,12 @@ subroutine fidaSolver( & eqns_data%scalarAquiferStoragePrev, & eqns_data%mLayerEnthalpyPrev, & ! intent(in) eqns_data%mLayerEnthalpyTrial, & ! intent(out) - eqns_data%ixSaturation, & + eqns_data%ixSaturation, & ! output - feasible, & ! intent(out): flag to denote the feasibility of the solution - eqns_data%fluxVec, & ! intent(out): flux vector - eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector + feasible, & ! intent(out): flag to denote the feasibility of the solution + eqns_data%fluxVec, & ! intent(out): flux vector + eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec, & ! intent(out): residual vector eqns_data%err,eqns_data%message) ! intent(out): error control @@ -561,17 +561,17 @@ subroutine fidaSolver( & ! save required quantities for next step - eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial - eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial - eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) - mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) + eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial + eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial + eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) + mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) eqns_data%mLayerVolFracLiqPrev(:) = eqns_data%mLayerVolFracLiqTrial(:) eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) - eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial + eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial ! if(tret(1) > 500) exit @@ -585,18 +585,18 @@ subroutine fidaSolver( & endif call computSnowDepth(& - tret(1), & ! intent(in) - eqns_data%nSnow, & ! intent(in) + tret(1), & ! intent(in) + eqns_data%nSnow, & ! intent(in) flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! intent(in) - eqns_data%mLayerVolFracLiqTrial, & ! intent(inout) - eqns_data%mLayerVolFracIceTrial, & ! intent(inout) - eqns_data%mLayerTempTrial, & ! intent(in) - mLayerMeltFreeze, & ! intent(in) - eqns_data%mpar_data, & ! intent(in) - ! output - mLayerDepth, & ! intent(inout) - ! error control - err,message) ! intent(out): error control + eqns_data%mLayerVolFracLiqTrial, & ! intent(inout) + eqns_data%mLayerVolFracIceTrial, & ! intent(inout) + eqns_data%mLayerTempTrial, & ! intent(in) + mLayerMeltFreeze, & ! intent(in) + eqns_data%mpar_data, & ! intent(in) + ! output + mLayerDepth, & ! intent(inout) + ! error control + err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if @@ -627,16 +627,16 @@ subroutine fidaSolver( & call needDivideLayer(& ! input/output: model data structures model_decisions, & ! intent(in): model decisions - eqns_data%mpar_data, & ! intent(in): model parameters - eqns_data%nSnow, & ! intent(in): number of snow layers - mLayerDepth, & ! intent(in): - scalarSnowDepth, & ! intent(in) + eqns_data%mpar_data, & ! intent(in): model parameters + eqns_data%nSnow, & ! intent(in): number of snow layers + mLayerDepth, & ! intent(in): + scalarSnowDepth, & ! intent(in) ! output divideLayer, & ! intent(out): flag to denote that a layer was divided err,message) ! intent(out): error control if(divideLayer .and. tret(1)>50) then ! print *, 'divideLayer, tret = ', tret(1) -! print *, 'mLayerMeltFreeze = ', mLayerMeltFreeze(1:nSnow) +! print *, 'mLayerMeltFreeze = ', mLayerMeltFreeze(1:nSnow) ! print *, 'mLayerVolFracLiq = ', eqns_data%mLayerVolFracLiqTrial(:) ! print *, 'mLayerVolFracIce = ', eqns_data%mLayerVolFracIceTrial(:) ! print *, 'mLayerTemp = ', eqns_data%mLayerTempTrial(:) @@ -659,7 +659,7 @@ subroutine fidaSolver( & if(mergedLayers .and. tret(1)>50) then ! print *, 'mergedLayers, tret = ', tret(1) -! print *, 'mLayerMeltFreeze = ', mLayerMeltFreeze(1:nSnow) +! print *, 'mLayerMeltFreeze = ', mLayerMeltFreeze(1:nSnow) ! print *, 'mLayerVolFracLiq = ', eqns_data%mLayerVolFracLiqTrial(:) ! print *, 'mLayerVolFracIce = ', eqns_data%mLayerVolFracIceTrial(:) ! print *, 'mLayerTemp = ', eqns_data%mLayerTempTrial(:) From 328eb61cf1f62a314859575a90442dd65c64473e Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Mon, 7 Jun 2021 23:18:25 -0600 Subject: [PATCH 0143/1472] if condition in updateSoilFida --- build/source/engine/updatStateFida.f90 | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/build/source/engine/updatStateFida.f90 b/build/source/engine/updatStateFida.f90 index 34d30b4b8..fe91577ed 100644 --- a/build/source/engine/updatStateFida.f90 +++ b/build/source/engine/updatStateFida.f90 @@ -16,7 +16,7 @@ module updatStateFida_module public::updateSoilFida2 public::updateVegFida -real(dp),parameter :: verySmall=epsilon(1.0_dp) ! a very small number (used to avoid divide by zero) +real(dp),parameter :: verySmall=1e-14_dp ! a very small number (used to avoid divide by zero) contains @@ -183,9 +183,13 @@ subroutine updateSoilFida(& ! compute fractional **volume** of total water (liquid plus ice) mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - ! mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime - ! mLayerVolFracWatPrime = mLayerMatricHeadPrime * (mLayerVolFracWat - mLayerVolFracWatPrev) / (mLayerMatricHead - mLayerMatricHeadPrev + verySmall) - mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev) / dt_cur + ! mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime + if( abs(mLayerMatricHead - mLayerMatricHeadPrev) < verySmall )then + mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev) / dt_cur + else + mLayerVolFracWatPrime = mLayerMatricHeadPrime * (mLayerVolFracWat - mLayerVolFracWatPrev) / (mLayerMatricHead - mLayerMatricHeadPrev) + endif + From a77e8e3da6a06bb6e18762dd3f612ba2c3b79cf5 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 9 Jun 2021 00:56:39 -0600 Subject: [PATCH 0144/1472] computFlux instead of computFluxFida in eval8summaFida --- build/source/engine/eval8summaFida.f90 | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 060762fcb..e02b06919 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -157,7 +157,7 @@ subroutine eval8summaFida(& USE updateVarsFida_module, only:updateVarsFida ! update variables USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy USE soilCmpresFida_module, only:soilCmpresFida ! compute soil compression - USE computFluxFida_module, only:computFluxFida ! compute fluxes given a state vector + USE computFlux_module, only:computFlux ! compute fluxes given a state vector USE computHeatCap_module,only:computHeatCapAnalytic ! compute heat capacity USE computHeatCap_module,only:computCm USE computHeatCap_module, only:computStatMult @@ -573,7 +573,7 @@ subroutine eval8summaFida(& ! save the number of flux calls per time step indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) + 1 ! compute the fluxes for a given state vector - call computFluxFida(& + call computFlux(& ! input-output: model control nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -585,10 +585,6 @@ subroutine eval8summaFida(& scalarSolution, & ! intent(in): flag to indicate the scalar solution scalarSfcMeltPond/dt, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) ! input: state variables - scalarAquiferStoragePrev, & ! intent(in) - mLayerVolFracIcePrev, & ! intent(in) - mLayerVolFracLiqPrev, & ! intent(in) - mLayerMatricHeadPrev, & ! intent(in) scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) From 0bd170ecb6132db0c32e5ba6189bdf6f7ee49a42 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 29 Jun 2021 08:08:17 -0600 Subject: [PATCH 0145/1472] tempPrintFlag deleted --- build/source/driver/summa_driver.f90 | 3 +-- build/source/dshare/globalData.f90 | 1 - build/source/engine/fidaSolver.f90 | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 17ce05fa2..d4bb7006a 100755 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -39,7 +39,6 @@ program summa_driver USE summa_util, only: handle_err ! used to process errors ! global data USE globalData, only: numtim ! number of model time steps -USE globalData, only: tempPrintFlag implicit none ! ***************************************************************************** @@ -90,7 +89,7 @@ program summa_driver ! read model forcing data call summa_readForcing(modelTimeStep, summa1_struc(n), err, message) call handle_err(err, message) -! if(iStep >= 4524) tempPrintFlag=.true. + print *, 'step ---> ', iStep ! run the summa physics for one time step call summa_runPhysics(modelTimeStep, summa1_struc(n), err, message) diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 index 92f2a0098..e5d5eca8d 100755 --- a/build/source/dshare/globalData.f90 +++ b/build/source/dshare/globalData.f90 @@ -306,7 +306,6 @@ MODULE globalData integer(i4b),save,public :: urbanVegCategory ! vegetation category for urban areas logical(lgt),save,public :: doJacobian=.false. ! flag to compute the Jacobian logical(lgt),save,public :: globalPrintFlag=.false. ! flag to compute the Jacobian - logical(lgt),save,public :: tempPrintFlag=.false. integer(i4b),save,public :: chunksize=1024 ! chunk size for the netcdf read/write integer(i4b),save,public :: outputPrecision=nf90_double ! variable type diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index da3638720..32bb4d252 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -48,7 +48,7 @@ module fidaSolver_module USE globalData,only:diag_meta ! metadata on the model diagnostic variables USE globalData,only:prog_meta ! metadata on the model prognostic variables USE globalData,only:deriv_meta ! metadata on the model derivatives -USE globalData, only: tempPrintFlag + ! constants USE multiconst,only:& LH_fus, & ! latent heat of fusion (J K-1) From 8e55e9370c211c94edc482ec015f8acaf1915986 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 29 Jun 2021 12:13:14 -0600 Subject: [PATCH 0146/1472] enthalpyFD in eval8summaFida --- build/source/engine/eval8summaFida.f90 | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index e02b06919..2579c3e5d 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -264,10 +264,11 @@ subroutine eval8summaFida(& real(dp),parameter :: canopyTempMax=500._dp ! expected maximum value for the canopy temperature (K) character(LEN=256) :: cmessage ! error message of downwind routine real(qp) :: scalarCanopyEnthalpyPrime - real(dp) :: scalarCanopyCmTrial - real(dp),dimension(nLayers) :: mLayerCmTrial - logical(lgt),parameter :: updateCp=.false. - logical(lgt),parameter :: needCm=.false. + real(dp) :: scalarCanopyCmTrial + real(dp),dimension(nLayers) :: mLayerCmTrial + logical(lgt),parameter :: updateCp=.false. + logical(lgt),parameter :: enthalpyFD=.false. + logical(lgt),parameter :: needCm=.false. @@ -497,7 +498,10 @@ subroutine eval8summaFida(& if(updateCp)then ! *** compute volumetric heat capacity C_p - call computHeatCapAnalytic(& + if(enthalpyFD)then + ! do nothing + else + call computHeatCapAnalytic(& ! input: control variables computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux canopyDepth, & ! intent(in): canopy depth (m) @@ -514,6 +518,7 @@ subroutine eval8summaFida(& mLayerHeatCapTrial, & ! intent(out) ! output: error control err,message) ! intent(out): error control + endif ! compute multiplier of state vector call computStatMult(& From b9ac2b1f62e10ba7200adbe7696377b7c9f6b95b Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 29 Jun 2021 12:23:22 -0600 Subject: [PATCH 0147/1472] t2enthalpy_T and computHeatCap in eval8summaFida --- build/source/engine/computThermConduct.f90 | 2 +- build/source/engine/eval8summaFida.f90 | 59 ++++++++++++++++++++-- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/build/source/engine/computThermConduct.f90 b/build/source/engine/computThermConduct.f90 index f0d87feea..c578a26ad 100644 --- a/build/source/engine/computThermConduct.f90 +++ b/build/source/engine/computThermConduct.f90 @@ -87,7 +87,7 @@ subroutine computThermConduct(& ! input: model control logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux real(dp),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) - real(dp),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) + real(dp),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) real(dp),intent(in) :: scalarCanopyLiquid real(dp),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) real(dp),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 2579c3e5d..715f4efd6 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -158,6 +158,7 @@ subroutine eval8summaFida(& USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy USE soilCmpresFida_module, only:soilCmpresFida ! compute soil compression USE computFlux_module, only:computFlux ! compute fluxes given a state vector + USE computHeatCap_module,only:computHeatCap ! compute heat capacity USE computHeatCap_module,only:computHeatCapAnalytic ! compute heat capacity USE computHeatCap_module,only:computCm USE computHeatCap_module, only:computStatMult @@ -499,7 +500,59 @@ subroutine eval8summaFida(& if(updateCp)then ! *** compute volumetric heat capacity C_p if(enthalpyFD)then - ! do nothing + ! compute H_T + call t2enthalpy_T(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value for canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) + ! output: enthalpy + scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! *** compute volumetric heat capacity C_p = dH_T/dT + call computHeatCap(& + ! input: control variables + nLayers, & ! intent(in): number of layers (soil+snow) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) + ! input data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + diag_data, & ! intent(in): model diagnostic variables for a local HRU + ! input: state variables + scalarCanopyIceTrial, & ! intent(in): trial value for canopy ice content (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial temperature + mLayerTempPrev, & ! intent(in): previous temperature + mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil + mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil + ! output + heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil + ! output: error control + err,message) ! intent(out): error control else call computHeatCapAnalytic(& ! input: control variables @@ -640,9 +693,7 @@ subroutine eval8summaFida(& dCompress_dPsi, & ! intent(inout): derivative in compressibility w.r.t. matric head (m-1) err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - - + ! compute the residual vector call computResidFida(& From f9cdb95eec92c32ab718e9e68962ec847fb5eefa Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 29 Jun 2021 12:51:43 -0600 Subject: [PATCH 0148/1472] finite diffs of icePrime in eval8summaFida --- build/source/engine/eval8summaFida.f90 | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 715f4efd6..cff16962a 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -267,8 +267,8 @@ subroutine eval8summaFida(& real(qp) :: scalarCanopyEnthalpyPrime real(dp) :: scalarCanopyCmTrial real(dp),dimension(nLayers) :: mLayerCmTrial - logical(lgt),parameter :: updateCp=.false. - logical(lgt),parameter :: enthalpyFD=.false. + logical(lgt),parameter :: updateCp=.true. + logical(lgt),parameter :: enthalpyFD=.true. logical(lgt),parameter :: needCm=.false. @@ -553,6 +553,11 @@ subroutine eval8summaFida(& mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil ! output: error control err,message) ! intent(out): error control + ! to conserve energy compute finite difference approximation of (theta_ice)' + scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur + do concurrent (iLayer=1:nLayers) + mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur + end do else call computHeatCapAnalytic(& ! input: control variables @@ -584,6 +589,9 @@ subroutine eval8summaFida(& sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + + diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) = heatCapVegTrial + diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat(:) = mLayerHeatCapTrial(:) ! update thermal conductivity call computThermConduct(& @@ -596,12 +604,13 @@ subroutine eval8summaFida(& mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) ! input/output: data structures - mpar_data, & ! intent(in): model parameters + mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(inout): model diagnostic variables for a local HRU err,message) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if + end if ! updateCp From 41c9a46d294d5e5fc410a946c7cfb32cd9d4bb79 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 29 Jun 2021 13:06:26 -0600 Subject: [PATCH 0149/1472] minor edit --- build/source/engine/eval8summaFida.f90 | 66 +++++++++++++------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index cff16962a..e6db42de9 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -96,8 +96,8 @@ subroutine eval8summaFida(& nLayers, & ! intent(in): total number of layers nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout) - firstSplitOper, & ! intent(in) + firstFluxCall, & ! intent(inout) + firstSplitOper, & ! intent(in) computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors @@ -123,17 +123,17 @@ subroutine eval8summaFida(& ! output: flux and residual vectors scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - scalarCanopyIceTrial, & - scalarCanopyIcePrev, & - scalarCanopyLiqTrial, & - scalarCanopyLiqPrev, & + scalarCanopyIceTrial, & + scalarCanopyIcePrev, & + scalarCanopyLiqTrial, & + scalarCanopyLiqPrev, & scalarCanopyEnthalpyTrial,& ! intent(in): trial enthalpy of the vegetation canopy (J m-3) scalarCanopyEnthalpyPrev,& ! intent(in): previous enthalpy of the vegetation canopy (J m-3) mLayerTempTrial, & ! intent(inout) mLayerTempPrev, & ! intent(in) mLayerMatricHeadLiqTrial,& !intent(inout) mLayerMatricHeadTrial, & !intent(inout) - mLayerMatricHeadPrev, & !intent(in) + mLayerMatricHeadPrev, & !intent(in) mLayerVolFracWatTrial, & !intent(inout) mLayerVolFracWatPrev, & !intent(inout) mLayerVolFracIceTrial, & !intent(inout) @@ -141,10 +141,10 @@ subroutine eval8summaFida(& mLayerVolFracLiqTrial, & !intent(inout) mLayerVolFracLiqPrev, & !intent(in) scalarAquiferStorageTrial, & - scalarAquiferStoragePrev, & + scalarAquiferStoragePrev, & mLayerEnthalpyPrev, & ! intent(in) mLayerEnthalpyTrial, & ! intent(out) - ixSaturation, & ! intent(inout) + ixSaturation, & ! intent(inout) feasible, & ! intent(out): flag to denote the feasibility of the solution fluxVec, & ! intent(out): flux vector resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation @@ -268,7 +268,7 @@ subroutine eval8summaFida(& real(dp) :: scalarCanopyCmTrial real(dp),dimension(nLayers) :: mLayerCmTrial logical(lgt),parameter :: updateCp=.true. - logical(lgt),parameter :: enthalpyFD=.true. + logical(lgt),parameter :: enthalpyFD=.false. logical(lgt),parameter :: needCm=.false. @@ -529,28 +529,28 @@ subroutine eval8summaFida(& call computHeatCap(& ! input: control variables nLayers, & ! intent(in): number of layers (soil+snow) - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux canopyDepth, & ! intent(in): canopy depth (m) ! input data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices diag_data, & ! intent(in): model diagnostic variables for a local HRU ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value for canopy ice content (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value for canopy ice content (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) - scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial temperature - mLayerTempPrev, & ! intent(in): previous temperature - mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil - mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil + scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial temperature + mLayerTempPrev, & ! intent(in): previous temperature + mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil + mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil ! output - heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil + heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil ! output: error control err,message) ! intent(out): error control ! to conserve energy compute finite difference approximation of (theta_ice)' @@ -564,16 +564,16 @@ subroutine eval8summaFida(& computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux canopyDepth, & ! intent(in): canopy depth (m) ! input: state variables - scalarCanopyIceTrial, & ! intent(in) - scalarCanopyLiqTrial, & ! intent(in) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): fraction of liquid water at the start of the sub-step (-) + scalarCanopyIceTrial, & ! intent(in) + scalarCanopyLiqTrial, & ! intent(in) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): fraction of liquid water at the start of the sub-step (-) ! input data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices ! output - heatCapVegTrial, & ! intent(out) - mLayerHeatCapTrial, & ! intent(out) + heatCapVegTrial, & ! intent(out) + mLayerHeatCapTrial, & ! intent(out) ! output: error control err,message) ! intent(out): error control endif @@ -605,9 +605,9 @@ subroutine eval8summaFida(& mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) ! input/output: data structures mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU + indx_data, & ! intent(in): model layer indices + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU err,message) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if @@ -621,7 +621,7 @@ subroutine eval8summaFida(& computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux ! input: state variables scalarCanopyTempTrial, & ! intent(in) - mLayerTempTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) mLayerMatricHeadTrial, & ! intent(in) ! input data structures mpar_data, & ! intent(in): model parameters From 6956376b7876e08655a5b5fe64c3ad46ddf1d835 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 29 Jun 2021 22:52:20 -0600 Subject: [PATCH 0150/1472] _dp to _rkind --- build/source/engine/computFluxFida.f90 | 28 +- build/source/engine/computHeatCap.f90 | 24 +- build/source/engine/computJacobFida.f90 | 28 +- build/source/engine/computJacobFidaCpVar.f90 | 16 +- build/source/engine/computResidFida.f90 | 2 +- build/source/engine/computSnowDepth.f90 | 2 +- build/source/engine/computThermConduct.f90 | 46 +- build/source/engine/coupled_em.f90 | 2 +- build/source/engine/derivforce.f90 | 6 +- build/source/engine/eval8summaFida.f90 | 8 +- build/source/engine/fidaSolver.f90 | 14 +- build/source/engine/layerDivideFida.f90 | 26 +- build/source/engine/snowLiqFlx.f90 | 20 +- build/source/engine/soilCmpresFida.f90 | 4 +- build/source/engine/soil_utils_fida.f90 | 38 +- build/source/engine/sysSolvFida.f90 | 18 +- build/source/engine/t2enthalpy.f90 | 66 +-- build/source/engine/tolFida.f90 | 2 +- build/source/engine/updatStateFida.f90 | 26 +- build/source/engine/updateVarsFida.f90 | 24 +- build/source/engine/updateVarsFida2.f90 | 38 +- build/source/engine/varSubstepFida.f90 | 64 +-- build/source/engine/vegNrgFluxFida.f90 | 478 +++++++++---------- 23 files changed, 490 insertions(+), 490 deletions(-) diff --git a/build/source/engine/computFluxFida.f90 b/build/source/engine/computFluxFida.f90 index 48296ff46..fb9ff7132 100644 --- a/build/source/engine/computFluxFida.f90 +++ b/build/source/engine/computFluxFida.f90 @@ -393,8 +393,8 @@ subroutine computFluxFida(& ! initialize liquid water fluxes throughout the snow and soil domains ! NOTE: used in the energy routines, which is called before the hydrology routines if(firstFluxCall)then - if(nSnow > 0) iLayerLiqFluxSnow(0:nSnow) = 0._dp - iLayerLiqFluxSoil(0:nSoil) = 0._dp + if(nSnow > 0) iLayerLiqFluxSnow(0:nSnow) = 0._rkind + iLayerLiqFluxSoil(0:nSoil) = 0._rkind end if ! ***** @@ -698,13 +698,13 @@ subroutine computFluxFida(& if(nSnow==0) then ! * case of infiltration into soil if(scalarMaxInfilRate > scalarRainPlusMelt)then ! infiltration is not rate-limited - scalarSoilControl = (1._dp - scalarFrozenArea)*scalarInfilArea + scalarSoilControl = (1._rkind - scalarFrozenArea)*scalarInfilArea else - scalarSoilControl = 0._dp ! (scalarRainPlusMelt exceeds maximum infiltration rate + scalarSoilControl = 0._rkind ! (scalarRainPlusMelt exceeds maximum infiltration rate endif else ! * case of infiltration into snow - scalarSoilControl = 1._dp + scalarSoilControl = 1._rkind endif ! compute drainage from the soil zone (needed for mass balance checks) @@ -728,10 +728,10 @@ subroutine computFluxFida(& ! set baseflow fluxes to zero if the baseflow routine is not used if(local_ixGroundwater/=qbaseTopmodel)then ! (diagnostic variables in the data structures) - scalarExfiltration = 0._dp ! exfiltration from the soil profile (m s-1) - mLayerColumnOutflow(:) = 0._dp ! column outflow from each soil layer (m3 s-1) + scalarExfiltration = 0._rkind ! exfiltration from the soil profile (m s-1) + mLayerColumnOutflow(:) = 0._rkind ! column outflow from each soil layer (m3 s-1) ! (variables needed for the numerical solution) - mLayerBaseflow(:) = 0._dp ! baseflow from each soil layer (m s-1) + mLayerBaseflow(:) = 0._rkind ! baseflow from each soil layer (m s-1) ! topmodel-ish shallow groundwater else ! local_ixGroundwater==qbaseTopmodel @@ -810,10 +810,10 @@ subroutine computFluxFida(& ! if no aquifer, then fluxes are zero else - scalarAquiferTranspire = 0._dp ! transpiration loss from the aquifer (m s-1) - scalarAquiferRecharge = 0._dp ! recharge to the aquifer (m s-1) - scalarAquiferBaseflow = 0._dp ! total baseflow from the aquifer (m s-1) - dBaseflow_dAquifer = 0._dp ! change in baseflow flux w.r.t. aquifer storage (s-1) + scalarAquiferTranspire = 0._rkind ! transpiration loss from the aquifer (m s-1) + scalarAquiferRecharge = 0._rkind ! recharge to the aquifer (m s-1) + scalarAquiferBaseflow = 0._rkind ! total baseflow from the aquifer (m s-1) + dBaseflow_dAquifer = 0._rkind ! change in baseflow flux w.r.t. aquifer storage (s-1) end if ! no aquifer endif ! if computing aquifer fluxes @@ -908,8 +908,8 @@ subroutine soilCmpres(& endif end do else - compress(:) = 0._dp - dCompress_dPsi(:) = 0._dp + compress(:) = 0._rkind + dCompress_dPsi(:) = 0._rkind end if end subroutine soilCmpres diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index cf71fab86..cdc837e31 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -159,7 +159,7 @@ subroutine computHeatCap(& ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) if(computeVegFlux)then delT = scalarCanopyTempTrial - scalarCanopyTempPrev - if(abs(delT) <= 1e-14_dp)then + if(abs(delT) <= 1e-14_rkind)then heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component Cp_ice*scalarCanopyIce/canopyDepth ! ice component @@ -172,20 +172,20 @@ subroutine computHeatCap(& ! loop through layers do iLayer=1,nLayers delT = mLayerTempTrial(iLayer) - mLayerTempPrev(iLayer) - if(abs(delT) <= 1e-14_dp)then + if(abs(delT) <= 1e-14_rkind)then ! get the soil layer if(iLayer>nSnow) iSoil = iLayer-nSnow select case(layerType(iLayer)) ! * soil case(iname_soil) - mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._dp - theta_sat(iSoil) ) + & ! soil component + mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component iden_ice * Cp_Ice * mLayerVolFracIce(iLayer) + & ! ice component iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component case(iname_snow) mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( 1._dp - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component + iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return end select else @@ -266,8 +266,8 @@ subroutine computStatMult(& where(ixStateType_subset==iname_nrgCanair) sMul = Cp_air*iden_air ! volumetric heat capacity of air (J m-3 K-1) where(ixStateType_subset==iname_nrgCanopy) sMul = heatCapVeg ! volumetric heat capacity of the vegetation (J m-3 K-1) - where(ixStateType_subset==iname_watCanopy) sMul = 1._dp ! nothing else on the left hand side - where(ixStateType_subset==iname_liqCanopy) sMul = 1._dp ! nothing else on the left hand side + where(ixStateType_subset==iname_watCanopy) sMul = 1._rkind ! nothing else on the left hand side + where(ixStateType_subset==iname_liqCanopy) sMul = 1._rkind ! nothing else on the left hand side ! define the energy multiplier for the state vector for residual calculations (snow-soil domain) @@ -282,12 +282,12 @@ subroutine computStatMult(& if(nSnowSoilHyd>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) ixStateSubset = ixSnowSoilHyd(iLayer) ! index within the state vector - sMul(ixStateSubset) = 1._dp ! state multiplier = 1 (nothing else on the left-hand-side) + sMul(ixStateSubset) = 1._rkind ! state multiplier = 1 (nothing else on the left-hand-side) end do ! looping through non-missing energy state variables in the snow+soil domain endif ! define the scaling factor and diagonal elements for the aquifer - where(ixStateType_subset==iname_watAquifer) sMul = 1._dp + where(ixStateType_subset==iname_watAquifer) sMul = 1._rkind ! ------------------------------------------------------------------------------------------ ! ------------------------------------------------------------------------------------------ @@ -378,14 +378,14 @@ subroutine computHeatCapAnalytic(& select case(layerType(iLayer)) ! * soil case(iname_soil) - mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._dp - theta_sat(iSoil) ) + & ! soil component + mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component iden_ice * Cp_Ice * mLayerVolFracIce(iLayer) + & ! ice component iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component case(iname_snow) mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( 1._dp - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component + iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return end select @@ -461,7 +461,7 @@ subroutine computCm(& ! Note that scalarCanopyCm/iden_water is computed if(computeVegFlux)then g2 = scalarCanopyTemp - Tfreeze - g1 = (1._dp/snowfrz_scale) * atan(snowfrz_scale * g2) + g1 = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * g2) if(scalarCanopyTemp < Tfreeze)then scalarCanopyCm = Cp_water * g1 + Cp_ice * (g2 - g1) else @@ -491,7 +491,7 @@ subroutine computCm(& case(iname_snow) g2 = mLayerTemp(iLayer) - Tfreeze - g1 = (1._dp/snowfrz_scale) * atan(snowfrz_scale * g2) + g1 = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * g2) mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( g2 - g1 ) & + (iden_water * Cp_water - iden_air * Cp_air) * g1 diff --git a/build/source/engine/computJacobFida.f90 b/build/source/engine/computJacobFida.f90 index f1a8da9b3..2eff1522a 100644 --- a/build/source/engine/computJacobFida.f90 +++ b/build/source/engine/computJacobFida.f90 @@ -80,7 +80,7 @@ module computJacobFida_module implicit none ! define constants -real(dp),parameter :: verySmall=tiny(1.0_dp) ! a very small number +real(dp),parameter :: verySmall=tiny(1.0_rkind) ! a very small number integer(i4b),parameter :: ixBandOffset=kl+ku+1 ! offset in the band Jacobian matrix private @@ -287,7 +287,7 @@ subroutine computJacobFida(& ! initialize the Jacobian ! NOTE: this needs to be done every time, since Jacobian matrix is modified in the solver - aJac(:,:) = 0._dp ! analytical Jacobian matrix + aJac(:,:) = 0._rkind ! analytical Jacobian matrix ! compute terms in the Jacobian for vegetation (excluding fluxes) ! NOTE: energy for vegetation is computed *within* the iteration loop as it includes phase change @@ -353,7 +353,7 @@ subroutine computJacobFida(& ! * diagonal elements for the vegetation canopy (-) if(ixCasNrg/=integerMissing) aJac(ixDiag,ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) * cj if(ixVegNrg/=integerMissing) aJac(ixDiag,ixVegNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanopyTemp) + dMat(ixVegNrg) - if(ixVegHyd/=integerMissing) aJac(ixDiag,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanLiq - scalarCanopyLiqDeriv)*dt + 1._dp * cj ! ixVegHyd: CORRECT + if(ixVegHyd/=integerMissing) aJac(ixDiag,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanLiq - scalarCanopyLiqDeriv)*dt + 1._rkind * cj ! ixVegHyd: CORRECT ! * cross-derivative terms w.r.t. canopy water if(ixVegHyd/=integerMissing)then @@ -365,7 +365,7 @@ subroutine computJacobFida(& if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (dt/canopyDepth) *(-dCanopyNetFlux_dCanLiq) + cj * (-1._dp + scalarFracLiqVeg)*LH_fus/canopyDepth + LH_fus * iden_water * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy ! dF/dLiq + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (dt/canopyDepth) *(-dCanopyNetFlux_dCanLiq) + cj * (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth + LH_fus * iden_water * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy ! dF/dLiq if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanLiq) endif @@ -437,7 +437,7 @@ subroutine computJacobFida(& ! compute factor to convert liquid water derivative to total water derivative select case( ixHydType(iLayer) ) case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) - case default; convLiq2tot = 1._dp + case default; convLiq2tot = 1._rkind end select ! - diagonal elements @@ -445,7 +445,7 @@ subroutine computJacobFida(& ! - lower-diagonal elements if(iLayer > 1)then - if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyHyd(iLayer-1),watState),watState) = 0._dp ! sub-diagonal: no dependence on other layers + if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyHyd(iLayer-1),watState),watState) = 0._rkind ! sub-diagonal: no dependence on other layers endif ! - upper diagonal elements @@ -462,7 +462,7 @@ subroutine computJacobFida(& if(nrgstate/=integerMissing)then ! (energy state for the current layer is within the state subset) ! (cross-derivative terms for the current layer) - aJac(ixOffDiag(nrgState,watState),watState) = -(1._dp - mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & + aJac(ixOffDiag(nrgState,watState),watState) = -(1._rkind - mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) @@ -552,7 +552,7 @@ subroutine computJacobFida(& if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(ixOffDiag(nrgState,watState),watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water*cj - LH_fus*iden_water * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content else - aJac(ixOffDiag(nrgState,watState),watState) = 0._dp + aJac(ixOffDiag(nrgState,watState),watState) = 0._rkind endif ! - compute lower diagonal elements @@ -597,7 +597,7 @@ subroutine computJacobFida(& if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) ! * liquid water fluxes for vegetation canopy (-) - if(ixVegHyd/=integerMissing) aJac(ixVegHyd,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanLiq - scalarCanopyLiqDeriv)*dt + 1._dp * cj + if(ixVegHyd/=integerMissing) aJac(ixVegHyd,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanLiq - scalarCanopyLiqDeriv)*dt + 1._rkind * cj ! * cross-derivative terms for canopy water if(ixVegHyd/=integerMissing)then ! cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) @@ -608,7 +608,7 @@ subroutine computJacobFida(& if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (dt/canopyDepth) *(-dCanopyNetFlux_dCanLiq) + cj * (-1._dp + scalarFracLiqVeg)*LH_fus/canopyDepth + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth ! dF/dLiq + if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (dt/canopyDepth) *(-dCanopyNetFlux_dCanLiq) + cj * (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth ! dF/dLiq if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanLiq) endif @@ -682,7 +682,7 @@ subroutine computJacobFida(& ! compute factor to convert liquid water derivative to total water derivative select case( ixHydType(iLayer) ) case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) - case default; convLiq2tot = 1._dp + case default; convLiq2tot = 1._rkind end select ! - diagonal elements @@ -690,7 +690,7 @@ subroutine computJacobFida(& ! - lower-diagonal elements if(iLayer > 1)then - if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSnowOnlyHyd(iLayer-1),watState) = 0._dp ! sub-diagonal: no dependence on other layers + if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSnowOnlyHyd(iLayer-1),watState) = 0._rkind ! sub-diagonal: no dependence on other layers endif ! - upper diagonal elements @@ -708,7 +708,7 @@ subroutine computJacobFida(& ! (cross-derivative terms for the current layer) ! print *, '3' - aJac(nrgState,watState) = (-1._dp + mLayerFracLiqSnow(iLayer))*LH_fus*iden_ice * cj & + aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_ice * cj & + LH_fus*iden_ice * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) @@ -808,7 +808,7 @@ subroutine computJacobFida(& ! print *, '4' aJac(nrgState,watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water*cj - LH_fus*iden_water * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) ! reza not sure about this ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content else - aJac(nrgState,watState) = 0._dp + aJac(nrgState,watState) = 0._rkind endif ! - compute lower diagonal elements diff --git a/build/source/engine/computJacobFidaCpVar.f90 b/build/source/engine/computJacobFidaCpVar.f90 index 27e67dad5..d501a7d62 100644 --- a/build/source/engine/computJacobFidaCpVar.f90 +++ b/build/source/engine/computJacobFidaCpVar.f90 @@ -86,7 +86,7 @@ module computJacobFidaCpVar_module implicit none ! define constants -real(dp),parameter :: verySmall=tiny(1.0_dp) ! a very small number +real(dp),parameter :: verySmall=tiny(1.0_rkind) ! a very small number integer(i4b),parameter :: ixBandOffset=kl+ku+1 ! offset in the band Jacobian matrix private @@ -304,7 +304,7 @@ subroutine computJacobFidaCpVar(& ! initialize the Jacobian ! NOTE: this needs to be done every time, since Jacobian matrix is modified in the solver - aJac(:,:) = 0._dp ! analytical Jacobian matrix + aJac(:,:) = 0._rkind ! analytical Jacobian matrix ! compute terms in the Jacobian for vegetation (excluding fluxes) ! NOTE: energy for vegetation is computed *within* the iteration loop as it includes phase change @@ -365,7 +365,7 @@ subroutine computJacobFidaCpVar(& if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) ! * liquid water fluxes for vegetation canopy (-) - if(ixVegHyd/=integerMissing) aJac(ixVegHyd,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanLiq - scalarCanopyLiqDeriv)*dt + 1._dp * cj + if(ixVegHyd/=integerMissing) aJac(ixVegHyd,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanLiq - scalarCanopyLiqDeriv)*dt + 1._rkind * cj ! * cross-derivative terms for canopy water if(ixVegHyd/=integerMissing)then ! cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) @@ -376,7 +376,7 @@ subroutine computJacobFidaCpVar(& if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (dt/canopyDepth) *(-dCanopyNetFlux_dCanLiq) + cj * (-1._dp + scalarFracLiqVeg)*LH_fus/canopyDepth + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth ! dF/dLiq + if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (dt/canopyDepth) *(-dCanopyNetFlux_dCanLiq) + cj * (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth ! dF/dLiq if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanLiq) endif @@ -450,7 +450,7 @@ subroutine computJacobFidaCpVar(& ! compute factor to convert liquid water derivative to total water derivative select case( ixHydType(iLayer) ) case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) - case default; convLiq2tot = 1._dp + case default; convLiq2tot = 1._rkind end select ! - diagonal elements @@ -458,7 +458,7 @@ subroutine computJacobFidaCpVar(& ! - lower-diagonal elements if(iLayer > 1)then - if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSnowOnlyHyd(iLayer-1),watState) = 0._dp ! sub-diagonal: no dependence on other layers + if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSnowOnlyHyd(iLayer-1),watState) = 0._rkind ! sub-diagonal: no dependence on other layers endif ! - upper diagonal elements @@ -476,7 +476,7 @@ subroutine computJacobFidaCpVar(& ! (cross-derivative terms for the current layer) ! print *, '3' - aJac(nrgState,watState) = ( -LH_fus*iden_water*(1._dp - mLayerFracLiqSnow(iLayer)) & + aJac(nrgState,watState) = ( -LH_fus*iden_water*(1._rkind - mLayerFracLiqSnow(iLayer)) & + ( beta1 + gamma1*mLayerFracLiqSnow(iLayer) ) * mLayerTemp(iLayer) ) * cj & + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) & + ( gamma1*dFracLiqSnow_dTk(iLayer)*mLayerTemp(iLayer) + beta1 + gamma1*mLayerFracLiqSnow(iLayer) ) & @@ -579,7 +579,7 @@ subroutine computJacobFidaCpVar(& + (beta2 - LH_fus*iden_water)*d2VolTot_d2Psi0(iLayer)*mLayerMatricHeadPrime(iLayer) & + beta2*mLayerTempPrime(iLayer)*dVolTot_dPsi0(iLayer) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content else - aJac(nrgState,watState) = 0._dp + aJac(nrgState,watState) = 0._rkind endif ! - compute lower diagonal elements diff --git a/build/source/engine/computResidFida.f90 b/build/source/engine/computResidFida.f90 index 080b2d811..3ddb2fed2 100644 --- a/build/source/engine/computResidFida.f90 +++ b/build/source/engine/computResidFida.f90 @@ -170,7 +170,7 @@ subroutine computResidFida(& ! ----------------------- ! intialize additional terms on the RHS as zero - rAdd(:) = 0._dp + rAdd(:) = 0._rkind ! compute energy associated with melt freeze for the vegetation canopy if(ixVegNrg/=integerMissing) rAdd(ixVegNrg) = rAdd(ixVegNrg) + LH_fus*scalarCanopyIcePrime/canopyDepth ! energy associated with melt/freeze (J m-3) diff --git a/build/source/engine/computSnowDepth.f90 b/build/source/engine/computSnowDepth.f90 index fc6170dc7..be67e457c 100644 --- a/build/source/engine/computSnowDepth.f90 +++ b/build/source/engine/computSnowDepth.f90 @@ -35,7 +35,7 @@ module computSnowDepth_module private public::computSnowDepth -real(dp),parameter :: verySmall=1.e-6_dp ! used as an additive constant to check if substantial difference among real numbers +real(dp),parameter :: verySmall=1.e-6_rkind ! used as an additive constant to check if substantial difference among real numbers contains diff --git a/build/source/engine/computThermConduct.f90 b/build/source/engine/computThermConduct.f90 index c578a26ad..5e181606d 100644 --- a/build/source/engine/computThermConduct.f90 +++ b/build/source/engine/computThermConduct.f90 @@ -54,10 +54,10 @@ module computThermConduct_module public::computThermConduct ! algorithmic parameters -real(dp),parameter :: valueMissing=-9999._dp ! missing value, used when diagnostic or state variables are undefined -real(dp),parameter :: verySmall=1.e-6_dp ! used as an additive constant to check if substantial difference among real numbers -real(dp),parameter :: mpe=1.e-6_dp ! prevents overflow error if division by zero -real(dp),parameter :: dx=1.e-6_dp ! finite difference increment +real(dp),parameter :: valueMissing=-9999._rkind ! missing value, used when diagnostic or state variables are undefined +real(dp),parameter :: verySmall=1.e-6_rkind ! used as an additive constant to check if substantial difference among real numbers +real(dp),parameter :: mpe=1.e-6_rkind ! prevents overflow error if division by zero +real(dp),parameter :: dx=1.e-6_rkind ! finite difference increment contains @@ -116,13 +116,13 @@ subroutine computThermConduct(& real(dp) :: kerstenNum ! the Kersten number (-), defining weight applied to conductivity of the wet medium real(dp) :: den ! denominator in the thermal conductivity calculations ! local variables to reproduce the thermal conductivity of Hansson et al. VZJ 2005 - real(dp),parameter :: c1=0.55_dp ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(dp),parameter :: c2=0.8_dp ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(dp),parameter :: c3=3.07_dp ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(dp),parameter :: c4=0.13_dp ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(dp),parameter :: c5=4._dp ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(dp),parameter :: f1=13.05_dp ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(dp),parameter :: f2=1.06_dp ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(dp),parameter :: c1=0.55_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(dp),parameter :: c2=0.8_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(dp),parameter :: c3=3.07_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(dp),parameter :: c4=0.13_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(dp),parameter :: c5=4._rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(dp),parameter :: f1=13.05_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(dp),parameter :: f2=1.06_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) real(dp) :: fArg,xArg ! temporary variables (see Hansson et al. VZJ 2005 for details) ! -------------------------------------------------------------------------------------------------------------------------------- ! associate variables in data structure @@ -169,9 +169,9 @@ subroutine computThermConduct(& ! compute the thermal conductivity of dry and wet soils (W m-1) ! NOTE: this is actually constant over the simulation, and included here for clarity if(ixThCondSoil == funcSoilWet .and. layerType(iLayer)==iname_soil)then - bulkden_soil = iden_soil(iSoil)*( 1._dp - theta_sat(iSoil) ) - lambda_drysoil = (0.135_dp*bulkden_soil + 64.7_dp) / (iden_soil(iSoil) - 0.947_dp*bulkden_soil) - lambda_wetsoil = (8.80_dp*frac_sand(iSoil) + 2.92_dp*frac_clay(iSoil)) / (frac_sand(iSoil) + frac_clay(iSoil)) + bulkden_soil = iden_soil(iSoil)*( 1._rkind - theta_sat(iSoil) ) + lambda_drysoil = (0.135_rkind*bulkden_soil + 64.7_rkind) / (iden_soil(iSoil) - 0.947_rkind*bulkden_soil) + lambda_wetsoil = (8.80_rkind*frac_sand(iSoil) + 2.92_rkind*frac_clay(iSoil)) / (frac_sand(iSoil) + frac_clay(iSoil)) end if ! ***** @@ -179,7 +179,7 @@ subroutine computThermConduct(& ! ********************************************************* select case(layerType(iLayer)) case(iname_soil); mLayerVolFracAir(iLayer) = theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) - case(iname_snow); mLayerVolFracAir(iLayer) = 1._dp - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) + case(iname_snow); mLayerVolFracAir(iLayer) = 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute volumetric fraction of air'; return end select @@ -198,27 +198,27 @@ subroutine computThermConduct(& case(funcSoilWet) ! compute the thermal conductivity of the wet material (W m-1) - lambda_wet = lambda_wetsoil**( 1._dp - theta_sat(iSoil) ) * lambda_water**theta_sat(iSoil) * lambda_ice**(theta_sat(iSoil) - mLayerVolFracLiq(iLayer)) + lambda_wet = lambda_wetsoil**( 1._rkind - theta_sat(iSoil) ) * lambda_water**theta_sat(iSoil) * lambda_ice**(theta_sat(iSoil) - mLayerVolFracLiq(iLayer)) relativeSat = (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer))/theta_sat(iSoil) ! relative saturation ! compute the Kersten number (-) - if(relativeSat > 0.1_dp)then ! log10(0.1) = -1 - kerstenNum = log10(relativeSat) + 1._dp + if(relativeSat > 0.1_rkind)then ! log10(0.1) = -1 + kerstenNum = log10(relativeSat) + 1._rkind else - kerstenNum = 0._dp ! dry thermal conductivity + kerstenNum = 0._rkind ! dry thermal conductivity endif ! ...and, compute the thermal conductivity - mLayerThermalC(iLayer) = kerstenNum*lambda_wet + (1._dp - kerstenNum)*lambda_drysoil + mLayerThermalC(iLayer) = kerstenNum*lambda_wet + (1._rkind - kerstenNum)*lambda_drysoil ! ** mixture of constituents case(mixConstit) - mLayerThermalC(iLayer) = thCond_soil(iSoil) * ( 1._dp - theta_sat(iSoil) ) + & ! soil component + mLayerThermalC(iLayer) = thCond_soil(iSoil) * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component lambda_ice * mLayerVolFracIce(iLayer) + & ! ice component lambda_water * mLayerVolFracLiq(iLayer) + & ! liquid water component lambda_air * mLayerVolFracAir(iLayer) ! air component ! ** test case for the mizoguchi lab experiment, Hansson et al. VZJ 2004 case(hanssonVZJ) - fArg = 1._dp + f1*mLayerVolFracIce(iLayer)**f2 + fArg = 1._rkind + f1*mLayerVolFracIce(iLayer)**f2 xArg = mLayerVolFracLiq(iLayer) + fArg*mLayerVolFracIce(iLayer) mLayerThermalC(iLayer) = c1 + c2*xArg + (c1 - c4)*exp(-(c3*xArg)**c5) @@ -270,7 +270,7 @@ subroutine computThermConduct(& ! special case of hansson if(ixThCondSoil==hanssonVZJ)then - iLayerThermalC(0) = 28._dp*(0.5_dp*(iLayerHeight(1) - iLayerHeight(0))) + iLayerThermalC(0) = 28._rkind*(0.5_rkind*(iLayerHeight(1) - iLayerHeight(0))) else iLayerThermalC(0) = mLayerThermalC(1) end if diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 450c22129..1489c3dcb 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1070,7 +1070,7 @@ subroutine coupled_em(& newSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) delSWE = newSWE - (oldSWE - sfcMeltPond) massBalance = delSWE - (effSnowfall + effRainfall + averageSnowSublimation - averageSnowDrainage*iden_water)*data_step - if(abs(massBalance) > absConvTol_liquid*iden_water*10._dp)then + if(abs(massBalance) > absConvTol_liquid*iden_water*10._rkind)then print*, 'nSnow = ', nSnow print*, 'nSub = ', nSub write(*,'(a,1x,f20.10)') 'data_step = ', data_step diff --git a/build/source/engine/derivforce.f90 b/build/source/engine/derivforce.f90 index caa7e617d..b8f560ed2 100644 --- a/build/source/engine/derivforce.f90 +++ b/build/source/engine/derivforce.f90 @@ -238,11 +238,11 @@ subroutine derivforce(time_data,forc_data,attr_data,mpar_data,prog_data,diag_dat ! check slope/aspect intent for radiation calculation if(aspect == nr_realMissing)then - azimuth = 0._dp ! if aspect is not an input attribute, slope & azimuth = zero (flat Earth) - slope = 0._dp + azimuth = 0._rkind ! if aspect is not an input attribute, slope & azimuth = zero (flat Earth) + slope = 0._rkind else azimuth = aspect ! in degrees - slope = atan(abs(tan_slope))*180._dp/PI_D ! convert from m/m to degrees + slope = atan(abs(tan_slope))*180._rkind/PI_D ! convert from m/m to degrees endif ! compute the cosine of the solar zenith angle diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index e6db42de9..14c7b81e8 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -262,7 +262,7 @@ subroutine eval8summaFida(& integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) real(dp) :: xMin,xMax ! minimum and maximum values for water content - real(dp),parameter :: canopyTempMax=500._dp ! expected maximum value for the canopy temperature (K) + real(dp),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) character(LEN=256) :: cmessage ! error message of downwind routine real(qp) :: scalarCanopyEnthalpyPrime real(dp) :: scalarCanopyCmTrial @@ -331,7 +331,7 @@ subroutine eval8summaFida(& ! check canopy liquid water is not negative if(ixVegHyd/=integerMissing)then - if(stateVec(ixVegHyd) < 0._dp) feasible=.false. + if(stateVec(ixVegHyd) < 0._rkind) feasible=.false. end if ! check snow temperature is below freezing @@ -353,12 +353,12 @@ subroutine eval8summaFida(& if (layerType(iLayer) == iname_soil) then xMin = theta_sat(iLayer-nSnow) else - xMin = 0._dp + xMin = 0._rkind endif ! --> maximum select case( layerType(iLayer) ) - case(iname_snow); xMax = merge(iden_ice, 1._dp - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) + case(iname_snow); xMax = merge(iden_ice, 1._rkind - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) case(iname_soil); xMax = merge(theta_sat(iLayer-nSnow), theta_sat(iLayer-nSnow) - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) end select diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 32bb4d252..94d4db4a3 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -363,7 +363,7 @@ subroutine fidaSolver( & retval = FIDASetUserData(ida_mem, c_loc(eqns_data)) if (retval /= 0) then; err=20; message='fidaSolver: error in FIDASetUserData'; return; endif - t0 = 0._dp + t0 = 0._rkind retval = FIDAInit(ida_mem, c_funloc(evalEqnsFida), t0, sunvec_y, sunvec_yp) if (retval /= 0) then; err=20; message='fidaSolver: error in FIDAInit'; return; endif @@ -610,7 +610,7 @@ subroutine fidaSolver( & tooMuchMelt = .false. if(eqns_data%nSnow>0)then ! check that we did not remove the entire layer - if(mLayerDepth(1) < 1.e-6_dp) exit + if(mLayerDepth(1) < 1.e-6_rkind) exit ! compute the energy required to melt the top snow layer (J m-2) bulkDensity = eqns_data%mLayerVolFracIceTrial(1)*iden_ice + eqns_data%mLayerVolFracLiqTrial(1)*iden_water volEnthalpy = temp2ethpy(eqns_data%mLayerTempTrial(1),bulkDensity,eqns_data%mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1)) @@ -752,7 +752,7 @@ subroutine setInitialCondition(neq, y, sunvec_u, sunvec_up) uu = y - up = 0._dp + up = 0._rkind end subroutine setInitialCondition @@ -851,7 +851,7 @@ subroutine implctMelt(& ! initialize error control err=0; message='implctMelt/' - if(scalarSWE > 0._dp)then + if(scalarSWE > 0._rkind)then ! only melt if temperature of the top soil layer is greater than Tfreeze if(soilTemp > Tfreeze)then ! compute the energy required to melt all the snow (J m-2) @@ -863,7 +863,7 @@ subroutine implctMelt(& ! compute the amount of melt, and update SWE (kg m-2) if(nrgAvailable > nrgRequired)then scalarSfcMeltPond = scalarSWE - scalarSWE = 0._dp + scalarSWE = 0._rkind else scalarSfcMeltPond = nrgAvailable/LH_fus scalarSWE = scalarSWE - scalarSfcMeltPond @@ -873,10 +873,10 @@ subroutine implctMelt(& ! update temperature of the top soil layer (K) soilTemp = soilTemp - (LH_fus*scalarSfcMeltPond/soilDepth)/soilHeatcap else ! melt is zero if the temperature of the top soil layer is less than Tfreeze - scalarSfcMeltPond = 0._dp ! kg m-2 + scalarSfcMeltPond = 0._rkind ! kg m-2 end if ! (if the temperature of the top soil layer is greater than Tfreeze) else ! melt is zero if the "snow without a layer" does not exist - scalarSfcMeltPond = 0._dp ! kg m-2 + scalarSfcMeltPond = 0._rkind ! kg m-2 end if ! (if the "snow without a layer" exists) end subroutine implctMelt diff --git a/build/source/engine/layerDivideFida.f90 b/build/source/engine/layerDivideFida.f90 index 62d2730b6..3050eabff 100644 --- a/build/source/engine/layerDivideFida.f90 +++ b/build/source/engine/layerDivideFida.f90 @@ -123,15 +123,15 @@ subroutine layerDivideFida(& integer(i4b) :: nCheck ! number of layers to check to divide logical(lgt) :: createLayer ! flag to indicate we are creating a new snow layer real(dp) :: depthOriginal ! original layer depth before sub-division (m) - real(dp),parameter :: fracTop=0.5_dp ! fraction of old layer used for the top layer + real(dp),parameter :: fracTop=0.5_rkind ! fraction of old layer used for the top layer real(dp) :: surfaceLayerSoilTemp ! temperature of the top soil layer (K) real(dp) :: maxFrozenSnowTemp ! maximum temperature when effectively all water is frozen (K) - real(dp),parameter :: unfrozenLiq=0.01_dp ! unfrozen liquid water used to compute maxFrozenSnowTemp (-) + real(dp),parameter :: unfrozenLiq=0.01_rkind ! unfrozen liquid water used to compute maxFrozenSnowTemp (-) real(dp) :: volFracWater ! volumetric fraction of total water, liquid and ice (-) real(dp) :: fracLiq ! fraction of liquid water (-) integer(i4b),parameter :: ixVisible=1 ! named variable to define index in array of visible part of the spectrum integer(i4b),parameter :: ixNearIR=2 ! named variable to define index in array of near IR part of the spectrum - real(dp),parameter :: verySmall=1.e-10_dp ! a very small number (used for error checking) + real(dp),parameter :: verySmall=1.e-10_rkind ! a very small number (used for error checking) ! -------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="layerDivideFida/" @@ -224,7 +224,7 @@ subroutine layerDivideFida(& ! compute volumeteric fraction of liquid water and ice volFracWater = (scalarSWE/scalarSnowDepth)/iden_water ! volumetric fraction of total water (liquid and ice) - mLayerVolFracIce(1) = (1._dp - fracLiq)*volFracWater*(iden_water/iden_ice) ! volumetric fraction of ice (-) + mLayerVolFracIce(1) = (1._rkind - fracLiq)*volFracWater*(iden_water/iden_ice) ! volumetric fraction of ice (-) mLayerVolFracLiq(1) = fracLiq *volFracWater ! volumetric fraction of liquid water (-) ! end association with local variables to the information in the data structures) @@ -243,7 +243,7 @@ subroutine layerDivideFida(& prog_data%var(iLookPROG%spectralSnowAlbedoDiffuse)%dat(ixVisible) = mpar_data%var(iLookPARAM%albedoMaxVisible)%dat(1) prog_data%var(iLookPROG%spectralSnowAlbedoDiffuse)%dat(ixNearIR) = mpar_data%var(iLookPARAM%albedoMaxNearIR)%dat(1) prog_data%var(iLookPROG%scalarSnowAlbedo)%dat(1) = ( mpar_data%var(iLookPARAM%Frad_vis)%dat(1))*mpar_data%var(iLookPARAM%albedoMaxVisible)%dat(1) + & - (1._dp - mpar_data%var(iLookPARAM%Frad_vis)%dat(1))*mpar_data%var(iLookPARAM%albedoMaxNearIR)%dat(1) + (1._rkind - mpar_data%var(iLookPARAM%Frad_vis)%dat(1))*mpar_data%var(iLookPARAM%albedoMaxNearIR)%dat(1) case default; err=20; message=trim(message)//'unable to identify option for snow albedo'; return end select ! identify option for snow albedo ! set direct albedo to diffuse albedo @@ -299,7 +299,7 @@ subroutine layerDivideFida(& layerSplit: associate(mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat) depthOriginal = mLayerDepth(iLayer) mLayerDepth(iLayer) = fracTop*depthOriginal - mLayerDepth(iLayer+1) = (1._dp - fracTop)*depthOriginal + mLayerDepth(iLayer+1) = (1._rkind - fracTop)*depthOriginal end associate layerSplit exit ! NOTE: only sub-divide one layer per substep @@ -337,7 +337,7 @@ subroutine layerDivideFida(& iLayerHeight(0) = -scalarSnowDepth do jLayer=1,nLayers iLayerHeight(jLayer) = iLayerHeight(jLayer-1) + mLayerDepth(jLayer) - mLayerHeight(jLayer) = (iLayerHeight(jLayer-1) + iLayerHeight(jLayer))/2._dp + mLayerHeight(jLayer) = (iLayerHeight(jLayer-1) + iLayerHeight(jLayer))/2._rkind end do ! check @@ -387,7 +387,7 @@ subroutine addModelLayer(dataStruct,metaStruct,ix_divide,nSnow,nLayers,err,messa integer(i4b) :: ix_lower ! lower bound of the vector integer(i4b) :: ix_upper ! upper bound of the vector logical(lgt) :: stateVariable ! .true. if variable is a state variable - real(dp),allocatable :: tempVec_dp(:) ! temporary vector (double precision) + real(dp),allocatable :: tempVec_rkind(:) ! temporary vector (double precision) integer(i4b),allocatable :: tempVec_i4b(:) ! temporary vector (integer) character(LEN=256) :: cmessage ! error message of downwind routine ! --------------------------------------------------------------------------------------------- @@ -420,7 +420,7 @@ subroutine addModelLayer(dataStruct,metaStruct,ix_divide,nSnow,nLayers,err,messa ! check allocated if(.not.allocated(dataStruct%var(ivar)%dat))then; err=20; message='data vector is not allocated'; return; end if ! assign the data vector to the temporary vector - call cloneStruc(tempVec_dp, ix_lower, source=dataStruct%var(ivar)%dat, err=err, message=cmessage) + call cloneStruc(tempVec_rkind, ix_lower, source=dataStruct%var(ivar)%dat, err=err, message=cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! reallocate space for the new vector deallocate(dataStruct%var(ivar)%dat,stat=err) @@ -431,18 +431,18 @@ subroutine addModelLayer(dataStruct,metaStruct,ix_divide,nSnow,nLayers,err,messa if(stateVariable)then if(ix_upper > 0)then ! (only copy data if the vector exists -- can be a variable for snow, with no layers) if(ix_divide > 0)then - dataStruct%var(ivar)%dat(1:ix_divide) = tempVec_dp(1:ix_divide) ! copy data - dataStruct%var(ivar)%dat(ix_divide+1) = tempVec_dp(ix_divide) ! repeat data for the sub-divided layer + dataStruct%var(ivar)%dat(1:ix_divide) = tempVec_rkind(1:ix_divide) ! copy data + dataStruct%var(ivar)%dat(ix_divide+1) = tempVec_rkind(ix_divide) ! repeat data for the sub-divided layer end if if(ix_upper > ix_divide) & - dataStruct%var(ivar)%dat(ix_divide+2:ix_upper+1) = tempVec_dp(ix_divide+1:ix_upper) ! copy data + dataStruct%var(ivar)%dat(ix_divide+2:ix_upper+1) = tempVec_rkind(ix_divide+1:ix_upper) ! copy data end if ! if the vector exists ! not a state variable else dataStruct%var(ivar)%dat(:) = realMissing end if ! deallocate the temporary vector: strictly not necessary, but include to be safe - deallocate(tempVec_dp,stat=err) + deallocate(tempVec_rkind,stat=err) if(err/=0)then; err=20; message='problem deallocating temporary data vector'; return; end if ! ** integer diff --git a/build/source/engine/snowLiqFlx.f90 b/build/source/engine/snowLiqFlx.f90 index dcef17da7..e7bd7a5fc 100644 --- a/build/source/engine/snowLiqFlx.f90 +++ b/build/source/engine/snowLiqFlx.f90 @@ -254,9 +254,9 @@ subroutine snowLiqFlxFida(& integer(i4b) :: ixTop ! top layer in subroutine call integer(i4b) :: ixBot ! bottom layer in subroutine call real(dp) :: multResid ! multiplier for the residual water content (-) - real(dp),parameter :: residThrs=550._dp ! ice density threshold to reduce residual liquid water content (kg m-3) - real(dp),parameter :: residScal=10._dp ! scaling factor for residual liquid water content reduction factor (kg m-3) - real(dp),parameter :: maxVolIceContent=0.7_dp ! maximum volumetric ice content to store water (-) + real(dp),parameter :: residThrs=550._rkind ! ice density threshold to reduce residual liquid water content (kg m-3) + real(dp),parameter :: residScal=10._rkind ! scaling factor for residual liquid water content reduction factor (kg m-3) + real(dp),parameter :: maxVolIceContent=0.7_rkind ! maximum volumetric ice content to store water (-) real(dp) :: availCap ! available storage capacity [0,1] (-) real(dp) :: relSaturn ! relative saturation [0,1] (-) ! ------------------------------------------------------------------------------------------------------------------------------------------ @@ -284,7 +284,7 @@ subroutine snowLiqFlxFida(& end if ! check the meltwater exponent is >=1 - if(mw_exp<1._dp)then; err=20; message=trim(message)//'meltwater exponent < 1'; return; end if + if(mw_exp<1._rkind)then; err=20; message=trim(message)//'meltwater exponent < 1'; return; end if ! get the indices for the snow+soil layers ixTop = integerMissing @@ -315,16 +315,16 @@ subroutine snowLiqFlxFida(& ! define the liquid flux at the upper boundary (m s-1) iLayerLiqFluxSnow(0) = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water - iLayerLiqFluxSnowDeriv(0) = 0._dp + iLayerLiqFluxSnowDeriv(0) = 0._rkind ! compute properties fixed over the time step if(firstFluxCall)then ! loop through snow layers do iLayer=1,nSnow ! compute the reduction in liquid water holding capacity at high snow density (-) - multResid = 1._dp / ( 1._dp + exp( (mLayerVolFracIce(iLayer)*iden_ice - residThrs) / residScal) ) + multResid = 1._rkind / ( 1._rkind + exp( (mLayerVolFracIce(iLayer)*iden_ice - residThrs) / residScal) ) ! compute the pore space (-) - mLayerPoreSpace(iLayer) = 1._dp - mLayerVolFracIce(iLayer) + mLayerPoreSpace(iLayer) = 1._rkind - mLayerVolFracIce(iLayer) ! compute the residual volumetric liquid water content (-) mLayerThetaResid(iLayer) = Fcapil*mLayerPoreSpace(iLayer) * multResid end do ! (looping through snow layers) @@ -338,14 +338,14 @@ subroutine snowLiqFlxFida(& availCap = mLayerPoreSpace(iLayer) - mLayerThetaResid(iLayer) ! available capacity relSaturn = (mLayerVolFracLiqTrial(iLayer) - mLayerThetaResid(iLayer)) / availCap ! relative saturation iLayerLiqFluxSnow(iLayer) = k_snow*relSaturn**mw_exp - iLayerLiqFluxSnowDeriv(iLayer) = ( (k_snow*mw_exp)/availCap ) * relSaturn**(mw_exp - 1._dp) + iLayerLiqFluxSnowDeriv(iLayer) = ( (k_snow*mw_exp)/availCap ) * relSaturn**(mw_exp - 1._rkind) if(mLayerVolFracIce(iLayer) > maxVolIceContent)then ! NOTE: use start-of-step ice content, to avoid convergence problems ! ** allow liquid water to pass through under very high ice density iLayerLiqFluxSnow(iLayer) = iLayerLiqFluxSnow(iLayer) + iLayerLiqFluxSnow(iLayer-1) !NOTE: derivative may need to be updated in future. end if else ! flow does not occur - iLayerLiqFluxSnow(iLayer) = 0._dp - iLayerLiqFluxSnowDeriv(iLayer) = 0._dp + iLayerLiqFluxSnow(iLayer) = 0._rkind + iLayerLiqFluxSnowDeriv(iLayer) = 0._rkind endif ! storage above residual content end do ! loop through snow layers diff --git a/build/source/engine/soilCmpresFida.f90 b/build/source/engine/soilCmpresFida.f90 index cf72409e3..4e0ad625d 100644 --- a/build/source/engine/soilCmpresFida.f90 +++ b/build/source/engine/soilCmpresFida.f90 @@ -127,8 +127,8 @@ subroutine soilCmpresFida(& endif end do else - compress(:) = 0._dp - dCompress_dPsi(:) = 0._dp + compress(:) = 0._rkind + dCompress_dPsi(:) = 0._rkind end if end subroutine soilCmpresFida diff --git a/build/source/engine/soil_utils_fida.f90 b/build/source/engine/soil_utils_fida.f90 index 994e12b02..44b1c2c9f 100644 --- a/build/source/engine/soil_utils_fida.f90 +++ b/build/source/engine/soil_utils_fida.f90 @@ -43,7 +43,7 @@ module soil_utils_fida_module public::d2Theta_dTk2 ! constant parameters -real(dp),parameter :: verySmall=epsilon(1.0_dp) ! a very small number (used to avoid divide by zero) +real(dp),parameter :: verySmall=epsilon(1.0_rkind) ! a very small number (used to avoid divide by zero) contains @@ -101,7 +101,7 @@ subroutine liquidHeadFida(& err=0; message='liquidHeadFida/' ! ** partially frozen soil - if(volFracIce > verySmall .and. matricHeadTotal < 0._dp)then ! check that ice exists and that the soil is unsaturated + if(volFracIce > verySmall .and. matricHeadTotal < 0._rkind)then ! check that ice exists and that the soil is unsaturated ! ----- @@ -116,18 +116,18 @@ subroutine liquidHeadFida(& effSat = xNum/xDen ! effective saturation ! - matric head associated with liquid water - matricHeadLiq = matricHead(effSat,vGn_alpha,0._dp,1._dp,vGn_n,vGn_m) ! argument is effective saturation, so theta_res=0 and theta_sat=1 - if (effSat < 1._dp .and. effSat > 0._dp)then - effSatPrime = (volFracLiqPrime * xDen + volFracIcePrime * xNum) / xDen**2._dp - matricHeadLiqPrime = -( 1._dp/(vGn_alpha*vGn_n*vGn_m) ) * effSat**(-1._dp-1._dp/vGn_m) * ( effSat**(-1._dp/vGn_m) - 1._dp )**(-1._dp+1._dp/vGn_n) * effSatPrime + matricHeadLiq = matricHead(effSat,vGn_alpha,0._rkind,1._rkind,vGn_n,vGn_m) ! argument is effective saturation, so theta_res=0 and theta_sat=1 + if (effSat < 1._rkind .and. effSat > 0._rkind)then + effSatPrime = (volFracLiqPrime * xDen + volFracIcePrime * xNum) / xDen**2._rkind + matricHeadLiqPrime = -( 1._rkind/(vGn_alpha*vGn_n*vGn_m) ) * effSat**(-1._rkind-1._rkind/vGn_m) * ( effSat**(-1._rkind/vGn_m) - 1._rkind )**(-1._rkind+1._rkind/vGn_n) * effSatPrime else - matricHeadLiqPrime = 0._dp + matricHeadLiqPrime = 0._rkind endif ! compute derivative in liquid water matric potential w.r.t. effective saturation (m) if(present(dPsiLiq_dPsi0).or.present(dPsiLiq_dTemp))then - dPsiLiq_dEffSat = dPsi_dTheta(effSat,vGn_alpha,0._dp,1._dp,vGn_n,vGn_m) + dPsiLiq_dEffSat = dPsi_dTheta(effSat,vGn_alpha,0._rkind,1._rkind,vGn_n,vGn_m) endif ! ----- @@ -143,7 +143,7 @@ subroutine liquidHeadFida(& endif ! (compute derivative in the liquid water matric potential w.r.t. the total water matric potential) - dPsiLiq_dPsi0 = dVolTot_dPsi0*dPsiLiq_dEffSat*xNum/(xDen**2._dp) + dPsiLiq_dPsi0 = dVolTot_dPsi0*dPsiLiq_dEffSat*xNum/(xDen**2._rkind) ! matricHeadLiqPrime = dPsiLiq_dPsi0 * matricHeadTotalPrime endif ! if dPsiLiq_dTemp is desired @@ -161,7 +161,7 @@ subroutine liquidHeadFida(& err=20; return endif ! (compute the derivative in the liquid water matric potential w.r.t. temperature) - dEffSat_dTemp = -dTheta_dT*xNum/(xDen**2._dp) + dTheta_dT/xDen + dEffSat_dTemp = -dTheta_dT*xNum/(xDen**2._rkind) + dTheta_dT/xDen dPsiLiq_dTemp = dPsiLiq_dEffSat*dEffSat_dTemp ! matricHeadLiqPrime = dPsiLiq_dTemp * tempPrime @@ -173,8 +173,8 @@ subroutine liquidHeadFida(& else ! (no ice) matricHeadLiq = matricHeadTotal matricHeadLiqPrime = matricHeadTotalPrime - if(present(dPsiLiq_dTemp)) dPsiLiq_dPsi0 = 1._dp ! derivative=1 because values are identical - if(present(dPsiLiq_dTemp)) dPsiLiq_dTemp = 0._dp ! derivative=0 because no impact of temperature for unfrozen conditions + if(present(dPsiLiq_dTemp)) dPsiLiq_dPsi0 = 1._rkind ! derivative=1 because values are identical + if(present(dPsiLiq_dTemp)) dPsiLiq_dTemp = 0._rkind ! derivative=0 because no impact of temperature for unfrozen conditions end if ! (if ice exists) end subroutine liquidHeadFida @@ -194,14 +194,14 @@ function d2Theta_dPsi2(psi,alpha,theta_res,theta_sat,n,m) real(dp) :: d2Theta_dPsi2 ! derivative of the soil water characteristic (m-1) real(dp) :: mult_fcn real(dp) :: mult_fcnp - if(psi<0._dp)then - mult_fcn = (-m*n*alpha*(alpha*psi)**(n-1._dp)) * ( 1._dp + (psi*alpha)**n )**(-1._dp) - mult_fcnp = -m*n*alpha*(n-1._dp)*alpha*(alpha*psi)**(n-2._dp)*( 1._dp + (psi*alpha)**n )**(-1._dp) - & - ( n*alpha*(alpha*psi)**(n-1._dp)*(1._dp + (psi*alpha)**n)**(-2._dp) ) * ( -m*n*alpha*(alpha*psi)**(n-1._dp) ) + if(psi<0._rkind)then + mult_fcn = (-m*n*alpha*(alpha*psi)**(n-1._rkind)) * ( 1._rkind + (psi*alpha)**n )**(-1._rkind) + mult_fcnp = -m*n*alpha*(n-1._rkind)*alpha*(alpha*psi)**(n-2._rkind)*( 1._rkind + (psi*alpha)**n )**(-1._rkind) - & + ( n*alpha*(alpha*psi)**(n-1._rkind)*(1._rkind + (psi*alpha)**n)**(-2._rkind) ) * ( -m*n*alpha*(alpha*psi)**(n-1._rkind) ) d2Theta_dPsi2 = mult_fcn * dTheta_dPsi(psi,alpha,theta_res,theta_sat,n,m) + & mult_fcnp * ( volFracLiq(psi,alpha,theta_res,theta_sat,n,m) - theta_res ) else - d2Theta_dPsi2 = 0._dp + d2Theta_dPsi2 = 0._rkind end if end function d2Theta_dPsi2 @@ -226,8 +226,8 @@ function d2Theta_dTk2(Tk,theta_res,theta_sat,alpha,n,m) ! define a tempory variable that is used more than once (-) xtemp = alpha*kappa*(Tk-Tfreeze) ! differentiate the freezing curve w.r.t. temperature -- making use of the chain rule - d2Theta_dTk2 = (-alpha*kappa*m*n*alpha*kappa)* (theta_sat - theta_res) * ( (n-1)*xtemp**(n - 2._dp) * (1._dp + xtemp**n)**(-m - 1._dp) & - + n*(-m-1)*xtemp**(2*n - 2._dp) * (1._dp + xtemp**n)**(-m - 2._dp) ) + d2Theta_dTk2 = (-alpha*kappa*m*n*alpha*kappa)* (theta_sat - theta_res) * ( (n-1)*xtemp**(n - 2._rkind) * (1._rkind + xtemp**n)**(-m - 1._rkind) & + + n*(-m-1)*xtemp**(2*n - 2._rkind) * (1._rkind + xtemp**n)**(-m - 2._rkind) ) end function d2Theta_dTk2 diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index c35bfae40..46318afdd 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -83,10 +83,10 @@ module sysSolvFida_module public::sysSolvFida ! control parameters -real(dp),parameter :: valueMissing=-9999._dp ! missing value -real(dp),parameter :: verySmall=1.e-12_dp ! a very small number (used to check consistency) -real(dp),parameter :: veryBig=1.e+20_dp ! a very big number -real(dp),parameter :: dx = 1.e-8_dp ! finite difference increment +real(dp),parameter :: valueMissing=-9999._rkind ! missing value +real(dp),parameter :: verySmall=1.e-12_rkind ! a very small number (used to check consistency) +real(dp),parameter :: veryBig=1.e+20_rkind ! a very big number +real(dp),parameter :: dx = 1.e-8_rkind ! finite difference increment contains @@ -183,9 +183,9 @@ subroutine sysSolvFida(& integer(i4b) :: local_ixGroundwater ! local index for groundwater representation real(dp) :: bulkDensity ! bulk density of a given layer (kg m-3) real(dp) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) - real(dp),parameter :: tempAccelerate=0.00_dp ! factor to force initial canopy temperatures to be close to air temperature - real(dp),parameter :: xMinCanopyWater=0.0001_dp ! minimum value to initialize canopy water (kg m-2) - real(dp),parameter :: tinyStep=0.000001_dp ! stupidly small time step (s) + real(dp),parameter :: tempAccelerate=0.00_rkind ! factor to force initial canopy temperatures to be close to air temperature + real(dp),parameter :: xMinCanopyWater=0.0001_rkind ! minimum value to initialize canopy water (kg m-2) + real(dp),parameter :: tinyStep=0.000001_rkind ! stupidly small time step (s) integer(i4b),parameter :: ixRectangular=1 integer(i4b),parameter :: ixTrapezoidal=2 @@ -425,11 +425,11 @@ subroutine sysSolvFida(& ! initialize flux_sum do concurrent ( iVar=1:size(flux_meta) ) - flux_sum%var(iVar)%dat(:) = 0._dp + flux_sum%var(iVar)%dat(:) = 0._rkind end do ! initialize sum of compression of the soil matrix - mLayerCmpress_sum(:) = 0._dp + mLayerCmpress_sum(:) = 0._rkind call fidaSolver(& dt, & ! intent (in) data time step diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index b5cfa12d9..66ffac256 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -91,11 +91,11 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number of logical(lgt),parameter :: doTest=.false. ! flag to test integer(i4b),parameter :: nLook=100 ! number of elements in the lookup table integer(i4b),parameter :: nIntegr8=10000 ! number of points used in the numerical integration - real(dp),parameter :: T_lower=260.0_dp ! lowest temperature value where all liquid water is assumed frozen (K) + real(dp),parameter :: T_lower=260.0_rkind ! lowest temperature value where all liquid water is assumed frozen (K) real(dp),dimension(nLook) :: xTemp ! temporary vector real(dp) :: xIncr ! temporary increment real(dp) :: T_incr ! temperature increment - real(dp),parameter :: T_test=272.9742_dp ! test value for temperature (K) + real(dp),parameter :: T_test=272.9742_rkind ! test value for temperature (K) real(dp) :: E_test ! test value for enthalpy (J m-3) integer(i4b) :: iVar ! loop through variables integer(i4b) :: iSoil ! loop through soil layers @@ -110,8 +110,8 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number of err=0; message="T2E_lookup/" ! get the values of temperature for the lookup table - xIncr = 1._dp/real(nLook-1, kind(dp)) - xTemp = T_lower + (Tfreeze - T_lower)*arth(0._dp,xIncr,nLook)**0.25_dp ! use **0.25 to give more values near freezing + xIncr = 1._rkind/real(nLook-1, kind(dp)) + xTemp = T_lower + (Tfreeze - T_lower)*arth(0._rkind,xIncr,nLook)**0.25_rkind ! use **0.25 to give more values near freezing ! ----- ! * allocate space for the lookup table... @@ -165,7 +165,7 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number of ) ! end associate statement ! compute vGn_m - vGn_m = 1._dp - 1._dp/vGn_n + vGn_m = 1._rkind - 1._rkind/vGn_n ! ----- ! * populate the lookup table... @@ -173,7 +173,7 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number of ! initialize temperature and enthalpy Tk(nLook) = Tfreeze - Ey(nLook) = 0._dp + Ey(nLook) = 0._rkind ! loop through lookup table do iLook=(nLook-1),1,-1 @@ -207,7 +207,7 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number of end do ! loop through lookup table ! use cubic spline interpolation to obtain enthalpy values at the desired values of temperature - call spline(Tk,Ey,1.e30_dp,1.e30_dp,E2,err,cmessage) ! get the second derivatives + call spline(Tk,Ey,1.e30_rkind,1.e30_rkind,E2,err,cmessage) ! get the second derivatives if(err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check @@ -392,12 +392,12 @@ subroutine t2enthalpy(& diffT = scalarCanopyTempTrial - Tfreeze enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth - if(diffT>=0._dp)then + if(diffT>=0._rkind)then enthLiq = Cp_water * scalarCanopyWatTrial * diffT / canopyDepth - enthIce = 0._dp - enthPhase = 0._dp + enthIce = 0._rkind + enthPhase = 0._rkind else - integral = (1._dp/snowfrz_scale) * atan(snowfrz_scale * diffT) + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth enthPhase = LH_fus * scalarCanopyIceTrial / canopyDepth @@ -419,13 +419,13 @@ subroutine t2enthalpy(& ) diffT = mLayerTempTrial(iLayer) - Tfreeze - if(diffT>=0._dp)then + if(diffT>=0._rkind)then enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * diffT - enthIce = 0._dp - enthAir = iden_air * Cp_air * ( 1._dp - mLayerVolFracWatTrial(iLayer) ) * diffT - enthPhase = 0._dp + enthIce = 0._rkind + enthAir = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) ) * diffT + enthPhase = 0._rkind else - integral = (1._dp/snowfrz_scale) * atan(snowfrz_scale * diffT) + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) @@ -460,7 +460,7 @@ subroutine t2enthalpy(& ) ! end associate statement ! diagnostic variables - vGn_m = 1._dp - 1._dp/vGn_n + vGn_m = 1._rkind - 1._rkind/vGn_n Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) vFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) @@ -468,7 +468,7 @@ subroutine t2enthalpy(& ! *** compute enthalpy of water for unfrozen conditions if(mlayerTempTrial(iLayer) > Tcrit)then enthWater = iden_water*Cp_water*vFracWat*(mlayerTempTrial(iLayer) - Tfreeze) ! valid for temperatures below freezing also - enthPhase = 0._dp + enthPhase = 0._rkind ! *** compute enthalpy of water for frozen conditions else @@ -494,10 +494,10 @@ subroutine t2enthalpy(& endif ! (if frozen conditions) ! *** compute the enthalpy of soil - enthSoil = soil_dens_intr*Cp_soil*(1._dp - theta_sat)*(mlayerTempTrial(iLayer) - Tfreeze) + enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*(mlayerTempTrial(iLayer) - Tfreeze) ! *** compute the enthalpy of air - enthAir = iden_air*Cp_air*(1._dp - theta_sat - vFracWat)*(mlayerTempTrial(iLayer) - Tfreeze) + enthAir = iden_air*Cp_air*(1._rkind - theta_sat - vFracWat)*(mlayerTempTrial(iLayer) - Tfreeze) ! *** compute the total enthalpy (J m-3) mLayerEnthalpy(iLayer) = enthSoil + enthWater + enthAir - enthPhase @@ -671,12 +671,12 @@ subroutine t2enthalpy_T(& diffT = scalarCanopyTempTrial - Tfreeze enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth - if(diffT>=0._dp)then + if(diffT>=0._rkind)then enthLiq = Cp_water * scalarCanopyWatTrial * diffT / canopyDepth - enthIce = 0._dp - enthPhase = 0._dp + enthIce = 0._rkind + enthPhase = 0._rkind else - integral = (1._dp/snowfrz_scale) * atan(snowfrz_scale * diffT) + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth enthPhase = LH_fus * scalarCanopyIceTrial / canopyDepth @@ -698,13 +698,13 @@ subroutine t2enthalpy_T(& ) diffT = mLayerTempTrial(iLayer) - Tfreeze - if(diffT>=0._dp)then + if(diffT>=0._rkind)then enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * diffT - enthIce = 0._dp - enthAir = iden_air * Cp_air * ( 1._dp - mLayerVolFracWatTrial(iLayer) ) * diffT - enthPhase = 0._dp + enthIce = 0._rkind + enthAir = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) ) * diffT + enthPhase = 0._rkind else - integral = (1._dp/snowfrz_scale) * atan(snowfrz_scale * diffT) + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) @@ -739,7 +739,7 @@ subroutine t2enthalpy_T(& ) ! end associate statement ! diagnostic variables - vGn_m = 1._dp - 1._dp/vGn_n + vGn_m = 1._rkind - 1._rkind/vGn_n Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) vFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) @@ -747,7 +747,7 @@ subroutine t2enthalpy_T(& ! *** compute enthalpy of water for unfrozen conditions if(mlayerTempTrial(iLayer) > Tcrit)then enthWater = iden_water*Cp_water*vFracWat*(mlayerTempTrial(iLayer) - Tfreeze) ! valid for temperatures below freezing also - enthPhase = 0._dp + enthPhase = 0._rkind ! *** compute enthalpy of water for frozen conditions else @@ -773,10 +773,10 @@ subroutine t2enthalpy_T(& endif ! (if frozen conditions) ! *** compute the enthalpy of soil - enthSoil = soil_dens_intr*Cp_soil*(1._dp - theta_sat)*(mlayerTempTrial(iLayer) - Tfreeze) + enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*(mlayerTempTrial(iLayer) - Tfreeze) ! *** compute the enthalpy of air - enthAir = iden_air*Cp_air*(1._dp - theta_sat - vFracWat)*(mlayerTempTrial(iLayer) - Tfreeze) + enthAir = iden_air*Cp_air*(1._rkind - theta_sat - vFracWat)*(mlayerTempTrial(iLayer) - Tfreeze) ! *** compute the total enthalpy (J m-3) mLayerEnthalpy(iLayer) = enthSoil + enthWater + enthAir diff --git a/build/source/engine/tolFida.f90 b/build/source/engine/tolFida.f90 index 477308576..68e878c9b 100644 --- a/build/source/engine/tolFida.f90 +++ b/build/source/engine/tolFida.f90 @@ -116,7 +116,7 @@ integer(c_int) function computWeightFida(sunvec_y, sunvec_ewt, user_data) & do iState = 1,tol_data%nState weightVec(iState) = tol_data%rtol(iState) * abs( stateVec(iState) ) + tol_data%atol(iState) - weightVec(iState) = 1._dp / weightVec(iState) + weightVec(iState) = 1._rkind / weightVec(iState) end do ierr = 0 diff --git a/build/source/engine/updatStateFida.f90 b/build/source/engine/updatStateFida.f90 index fe91577ed..cc85a53eb 100644 --- a/build/source/engine/updatStateFida.f90 +++ b/build/source/engine/updatStateFida.f90 @@ -16,7 +16,7 @@ module updatStateFida_module public::updateSoilFida2 public::updateVegFida -real(dp),parameter :: verySmall=1e-14_dp ! a very small number (used to avoid divide by zero) +real(dp),parameter :: verySmall=1e-14_rkind ! a very small number (used to avoid divide by zero) contains @@ -65,7 +65,7 @@ subroutine updateVegFida(& ! compute the volumetric fraction of liquid water and ice (-) fLiq = fracliquid(Temp,snowfrz_scale) VolFracLiq = fLiq*Theta - VolFracIce = (1._dp - fLiq)*Theta + VolFracIce = (1._rkind - fLiq)*Theta VolFracLiqPrime = fLiq * ThetaPrime + dFracLiq_dTk(Temp,snowfrz_scale) * Theta * TempPrime VolFracIcePrime = ( ThetaPrime - VolFracLiqPrime ) @@ -115,7 +115,7 @@ subroutine updateSnowFida(& ! compute the volumetric fraction of liquid water and ice (-) fLiq = fracliquid(mLayerTemp,snowfrz_scale) mLayerVolFracLiq = fLiq*mLayerTheta - mLayerVolFracIce = (1._dp - fLiq)*mLayerTheta*(iden_water/iden_ice) + mLayerVolFracIce = (1._rkind - fLiq)*mLayerTheta*(iden_water/iden_ice) mLayerVolFracLiqPrime = fLiq * mLayerThetaPrime + dFracLiq_dTk(mLayerTemp,snowfrz_scale) * mLayerTheta * mLayerTempPrime mLayerVolFracIcePrime = ( mLayerThetaPrime - mLayerVolFracLiqPrime ) * (iden_water/iden_ice) @@ -198,7 +198,7 @@ subroutine updateSoilFida(& ! compute the critical soil temperature where all water is unfrozen (K) ! (eq 17 in Dall'Amico 2011) - TcSoil = Tfreeze + min(mLayerMatricHead,0._dp)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) + TcSoil = Tfreeze + min(mLayerMatricHead,0._rkind)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) ! *** compute volumetric fraction of liquid water and ice for partially frozen soil if(mLayerTemp < TcSoil)then ! (check if soil temperature is less than the critical temperature) @@ -208,10 +208,10 @@ subroutine updateSoilFida(& xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if(mLayerPsiLiq<0._dp)then + if(mLayerPsiLiq<0._rkind)then mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime else - mLayerVolFracLiqPrime = 0._dp + mLayerVolFracLiqPrime = 0._rkind endif ! - volumetric ice content (-) @@ -224,10 +224,10 @@ subroutine updateSoilFida(& ! all water is unfrozen mLayerVolFracLiq = mLayerVolFracWat - mLayerVolFracIce = 0._dp + mLayerVolFracIce = 0._rkind mLayerVolFracLiqPrime = mLayerVolFracWatPrime - mLayerVolFracIcePrime = 0._dp + mLayerVolFracIcePrime = 0._rkind end if ! (check if soil is partially frozen) @@ -296,7 +296,7 @@ subroutine updateSoilFida2(& ! compute the critical soil temperature where all water is unfrozen (K) ! (eq 17 in Dall'Amico 2011) - TcSoil = Tfreeze + min(mLayerMatricHead,0._dp)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) + TcSoil = Tfreeze + min(mLayerMatricHead,0._rkind)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) ! *** compute volumetric fraction of liquid water and ice for partially frozen soil if(mLayerTemp < TcSoil)then ! (check if soil temperature is less than the critical temperature) @@ -306,10 +306,10 @@ subroutine updateSoilFida2(& xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if(mLayerPsiLiq<0._dp)then + if(mLayerPsiLiq<0._rkind)then mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime else - mLayerVolFracLiqPrime = 0._dp + mLayerVolFracLiqPrime = 0._rkind endif ! - volumetric ice content (-) @@ -322,10 +322,10 @@ subroutine updateSoilFida2(& ! all water is unfrozen mLayerVolFracLiq = mLayerVolFracWat - mLayerVolFracIce = 0._dp + mLayerVolFracIce = 0._rkind mLayerVolFracLiqPrime = mLayerVolFracWatPrime - mLayerVolFracIcePrime = 0._dp + mLayerVolFracIcePrime = 0._rkind end if ! (check if soil is partially frozen) diff --git a/build/source/engine/updateVarsFida.f90 b/build/source/engine/updateVarsFida.f90 index 3b76d86e1..4004e0330 100644 --- a/build/source/engine/updateVarsFida.f90 +++ b/build/source/engine/updateVarsFida.f90 @@ -208,13 +208,13 @@ subroutine updateVarsFida(& integer(i4b) :: iter ! iteration index integer(i4b) :: niter ! number of iterations integer(i4b),parameter :: maxiter=100 ! maximum number of iterations - real(dp),parameter :: nrgConvTol=1.e-4_dp ! convergence tolerance for energy (J m-3) - real(dp),parameter :: tempConvTol=1.e-6_dp ! convergence tolerance for temperature (K) + real(dp),parameter :: nrgConvTol=1.e-4_rkind ! convergence tolerance for energy (J m-3) + real(dp),parameter :: tempConvTol=1.e-6_rkind ! convergence tolerance for temperature (K) real(dp) :: critDiff ! temperature difference from critical (K) real(dp) :: tempMin ! minimum bracket for temperature (K) real(dp) :: tempMax ! maximum bracket for temperature (K) logical(lgt) :: bFlag ! flag to denote that iteration increment was constrained using bi-section - real(dp),parameter :: epsT=1.e-7_dp ! small interval above/below critical temperature (K) + real(dp),parameter :: epsT=1.e-7_rkind ! small interval above/below critical temperature (K) ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures associate(& @@ -377,7 +377,7 @@ subroutine updateVarsFida(& ! --> update the total water from the liquid water matric potential case(iname_lmpLayer) - effSat = volFracLiq(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._dp,1._dp,vGn_n(ixControlIndex),vGn_m(ixControlIndex)) ! effective saturation + effSat = volFracLiq(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex)) ! effective saturation avPore = theta_sat(ixControlIndex) - mLayerVolFracIceTrial(iLayer) - theta_res(ixControlIndex) ! available pore space mLayerVolFracLiqTrial(iLayer) = effSat*avPore + theta_res(ixControlIndex) mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer) ! no volume expansion @@ -418,8 +418,8 @@ subroutine updateVarsFida(& ! define brackets for the root ! NOTE: start with an enormous range; updated quickly in the iterations - tempMin = xTemp - 10._dp - tempMax = xTemp + 10._dp + tempMin = xTemp - 10._rkind + tempMax = xTemp + 10._rkind ! get iterations (set to maximum iterations if adjusting the temperature) niter = merge(maxiter, 1, do_adjustTemp) @@ -429,7 +429,7 @@ subroutine updateVarsFida(& ! restrict temperature if(xTemp <= tempMin .or. xTemp >= tempMax)then - xTemp = 0.5_dp*(tempMin + tempMax) ! new value + xTemp = 0.5_rkind*(tempMin + tempMax) ! new value bFlag = .true. else bFlag = .false. @@ -444,7 +444,7 @@ subroutine updateVarsFida(& ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi if(ixDomainType==iname_soil)then select case( ixStateType(ixFullVector) ) - case(iname_lmpLayer); dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._dp,1._dp,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + case(iname_lmpLayer); dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore case default; dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) end select endif @@ -462,8 +462,8 @@ subroutine updateVarsFida(& ! --> unfrozen: no dependence of liquid water on temperature else select case(ixDomainType) - case(iname_veg); dTheta_dTkCanopy = 0._dp - case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._dp + case(iname_veg); dTheta_dTkCanopy = 0._rkind + case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return end select ! domain type endif @@ -610,8 +610,8 @@ subroutine updateVarsFida(& if(.not.isNrgState .and. .not.isCoupled)then ! derivatives relating liquid water matric potential to total water matric potential and temperature - dPsiLiq_dPsi0(ixControlIndex) = 1._dp ! exact correspondence (psiLiq=psi0) - dPsiLiq_dTemp(ixControlIndex) = 0._dp ! no relationship between liquid water matric potential and temperature + dPsiLiq_dPsi0(ixControlIndex) = 1._rkind ! exact correspondence (psiLiq=psi0) + dPsiLiq_dTemp(ixControlIndex) = 0._rkind ! no relationship between liquid water matric potential and temperature ! case of energy state or coupled solution else diff --git a/build/source/engine/updateVarsFida2.f90 b/build/source/engine/updateVarsFida2.f90 index 2739f80de..254be1bf9 100644 --- a/build/source/engine/updateVarsFida2.f90 +++ b/build/source/engine/updateVarsFida2.f90 @@ -212,13 +212,13 @@ subroutine updateVarsFida2(& integer(i4b) :: iter ! iteration index integer(i4b) :: niter ! number of iterations integer(i4b),parameter :: maxiter=100 ! maximum number of iterations - real(dp),parameter :: nrgConvTol=1.e-4_dp ! convergence tolerance for energy (J m-3) - real(dp),parameter :: tempConvTol=1.e-6_dp ! convergence tolerance for temperature (K) + real(dp),parameter :: nrgConvTol=1.e-4_rkind ! convergence tolerance for energy (J m-3) + real(dp),parameter :: tempConvTol=1.e-6_rkind ! convergence tolerance for temperature (K) real(dp) :: critDiff ! temperature difference from critical (K) real(dp) :: tempMin ! minimum bracket for temperature (K) real(dp) :: tempMax ! maximum bracket for temperature (K) logical(lgt) :: bFlag ! flag to denote that iteration increment was constrained using bi-section - real(dp),parameter :: epsT=1.e-7_dp ! small interval above/below critical temperature (K) + real(dp),parameter :: epsT=1.e-7_rkind ! small interval above/below critical temperature (K) ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures associate(& @@ -379,7 +379,7 @@ subroutine updateVarsFida2(& ! --> update the total water from the liquid water matric potential case(iname_lmpLayer) - effSat = volFracLiq(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._dp,1._dp,vGn_n(ixControlIndex),vGn_m(ixControlIndex)) ! effective saturation + effSat = volFracLiq(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex)) ! effective saturation avPore = theta_sat(ixControlIndex) - mLayerVolFracIceTrial(iLayer) - theta_res(ixControlIndex) ! available pore space mLayerVolFracLiqTrial(iLayer) = effSat*avPore + theta_res(ixControlIndex) mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer) ! no volume expansion @@ -420,8 +420,8 @@ subroutine updateVarsFida2(& ! define brackets for the root ! NOTE: start with an enormous range; updated quickly in the iterations - tempMin = xTemp - 10._dp - tempMax = xTemp + 10._dp + tempMin = xTemp - 10._rkind + tempMax = xTemp + 10._rkind ! get iterations (set to maximum iterations if adjusting the temperature) niter = merge(maxiter, 1, do_adjustTemp) @@ -431,7 +431,7 @@ subroutine updateVarsFida2(& ! restrict temperature if(xTemp <= tempMin .or. xTemp >= tempMax)then - xTemp = 0.5_dp*(tempMin + tempMax) ! new value + xTemp = 0.5_rkind*(tempMin + tempMax) ! new value bFlag = .true. else bFlag = .false. @@ -447,8 +447,8 @@ subroutine updateVarsFida2(& if(ixDomainType==iname_soil)then select case( ixStateType(ixFullVector) ) case(iname_lmpLayer) - dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._dp,1._dp,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore - d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._dp,1._dp,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore case default dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),& @@ -463,13 +463,13 @@ subroutine updateVarsFida2(& case(iname_veg) dFracLiqVeg_dTkCanopy = dFracLiq_dTk(xTemp,snowfrz_scale) dTheta_dTkCanopy = dFracLiqVeg_dTkCanopy * scalarCanopyWatTrial/(iden_water*canopyDepth) - d2Theta_dTkCanopy2 = 2._dp * snowfrz_scale**2._dp * ( (Tfreeze - xTemp) * 2._dp * fracliquid(xTemp,snowfrz_scale) * dFracLiq_dTk(xTemp,snowfrz_scale) - fracliquid(xTemp,snowfrz_scale)**2._dp ) * scalarCanopyWatTrial/(iden_water*canopyDepth) + d2Theta_dTkCanopy2 = 2._rkind * snowfrz_scale**2._rkind * ( (Tfreeze - xTemp) * 2._rkind * fracliquid(xTemp,snowfrz_scale) * dFracLiq_dTk(xTemp,snowfrz_scale) - fracliquid(xTemp,snowfrz_scale)**2._rkind ) * scalarCanopyWatTrial/(iden_water*canopyDepth) case(iname_snow) dFracLiqSnow_dTk(iLayer) = dFracLiq_dTk(xTemp,snowfrz_scale) mLayerdTheta_dTk(iLayer) = dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatTrial(iLayer) - mLayerd2Theta_dTk2(iLayer) = 2._dp * snowfrz_scale**2._dp * ( (Tfreeze - xTemp) * 2._dp * fracliquid(xTemp,snowfrz_scale) * dFracLiq_dTk(xTemp,snowfrz_scale) - fracliquid(xTemp,snowfrz_scale)**2._dp ) * mLayerVolFracWatTrial(iLayer) + mLayerd2Theta_dTk2(iLayer) = 2._rkind * snowfrz_scale**2._rkind * ( (Tfreeze - xTemp) * 2._rkind * fracliquid(xTemp,snowfrz_scale) * dFracLiq_dTk(xTemp,snowfrz_scale) - fracliquid(xTemp,snowfrz_scale)**2._rkind ) * mLayerVolFracWatTrial(iLayer) case(iname_soil) - dFracLiqSnow_dTk(iLayer) = 0._dp + dFracLiqSnow_dTk(iLayer) = 0._rkind mLayerdTheta_dTk(iLayer) = dTheta_dTk(xTemp,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) mLayerd2Theta_dTk2(iLayer) = d2Theta_dTk2(xTemp,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return @@ -478,8 +478,8 @@ subroutine updateVarsFida2(& ! --> unfrozen: no dependence of liquid water on temperature else select case(ixDomainType) - case(iname_veg); dTheta_dTkCanopy = 0._dp; d2Theta_dTkCanopy2 = 0; dFracLiqVeg_dTkCanopy = 0 - case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._dp; mLayerd2Theta_dTk2(iLayer) = 0._dp; dFracLiqSnow_dTk(iLayer) = 0 + case(iname_veg); dTheta_dTkCanopy = 0._rkind; d2Theta_dTkCanopy2 = 0; dFracLiqVeg_dTkCanopy = 0 + case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind; mLayerd2Theta_dTk2(iLayer) = 0._rkind; dFracLiqSnow_dTk(iLayer) = 0 case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return end select ! domain type endif @@ -648,7 +648,7 @@ subroutine updateVarsFida2(& endif ! update bracket - if(residual < 0._dp)then + if(residual < 0._rkind)then tempMax = min(xTemp,tempMax) else tempMin = max(tempMin,xTemp) @@ -666,7 +666,7 @@ subroutine updateVarsFida2(& ! add constraints for snow temperature if(ixDomainType==iname_veg .or. ixDomainType==iname_snow)then - if(tempInc > Tcrit - xTemp) tempInc=(Tcrit - xTemp)*0.5_dp ! simple bi-section method + if(tempInc > Tcrit - xTemp) tempInc=(Tcrit - xTemp)*0.5_rkind ! simple bi-section method endif ! if the domain is vegetation or snow ! deal with the discontinuity between partially frozen and unfrozen soil @@ -674,7 +674,7 @@ subroutine updateVarsFida2(& ! difference from the temperature below which ice exists critDiff = Tcrit - xTemp ! --> initially frozen (T < Tcrit) - if(critDiff > 0._dp)then + if(critDiff > 0._rkind)then if(tempInc > critDiff) tempInc = critDiff + epsT ! set iteration increment to slightly above critical temperature ! --> initially unfrozen (T > Tcrit) else @@ -721,8 +721,8 @@ subroutine updateVarsFida2(& if(.not.isNrgState .and. .not.isCoupled)then ! derivatives relating liquid water matric potential to total water matric potential and temperature - dPsiLiq_dPsi0(ixControlIndex) = 1._dp ! exact correspondence (psiLiq=psi0) - dPsiLiq_dTemp(ixControlIndex) = 0._dp ! no relationship between liquid water matric potential and temperature + dPsiLiq_dPsi0(ixControlIndex) = 1._rkind ! exact correspondence (psiLiq=psi0) + dPsiLiq_dTemp(ixControlIndex) = 0._rkind ! no relationship between liquid water matric potential and temperature ! case of energy state or coupled solution else diff --git a/build/source/engine/varSubstepFida.f90 b/build/source/engine/varSubstepFida.f90 index 945f2062b..92eb1e9ab 100644 --- a/build/source/engine/varSubstepFida.f90 +++ b/build/source/engine/varSubstepFida.f90 @@ -79,7 +79,7 @@ module varSubstepFida_module public::varSubstepFida ! algorithmic parameters -real(dp),parameter :: verySmall=1.e-6_dp ! used as an additive constant to check if substantial difference among real numbers +real(dp),parameter :: verySmall=1.e-6_rkind ! used as an additive constant to check if substantial difference among real numbers contains @@ -191,14 +191,14 @@ subroutine varSubstepFida(& real(dp) :: dtSubstep ! length of a substep (s) ! adaptive sub-stepping for the explicit solution logical(lgt) :: failedSubstep ! flag to denote success of substepping for a given split - real(dp),parameter :: safety=0.85_dp ! safety factor in adaptive sub-stepping - real(dp),parameter :: reduceMin=0.1_dp ! mimimum factor that time step is reduced - real(dp),parameter :: increaseMax=4.0_dp ! maximum factor that time step is increased + real(dp),parameter :: safety=0.85_rkind ! safety factor in adaptive sub-stepping + real(dp),parameter :: reduceMin=0.1_rkind ! mimimum factor that time step is reduced + real(dp),parameter :: increaseMax=4.0_rkind ! maximum factor that time step is increased ! adaptive sub-stepping for the implicit solution integer(i4b),parameter :: n_inc=5 ! minimum number of iterations to increase time step integer(i4b),parameter :: n_dec=15 ! maximum number of iterations to decrease time step - real(dp),parameter :: F_inc = 1.25_dp ! factor used to increase time step - real(dp),parameter :: F_dec = 0.90_dp ! factor used to decrease time step + real(dp),parameter :: F_inc = 1.25_rkind ! factor used to increase time step + real(dp),parameter :: F_dec = 0.90_rkind ! factor used to decrease time step ! state and flux vectors real(dp) :: untappedMelt(nState) ! un-tapped melt energy (J m-3 s-1) real(dp) :: stateVecInit(nState) ! initial state vector (mixed units) @@ -268,17 +268,17 @@ subroutine varSubstepFida(& end do ! initialize the total energy fluxes (modified in updateProgFida) - sumCanopyEvaporation = 0._dp ! canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap = 0._dp ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy = 0._dp ! sensible heat flux from the canopy to the canopy air space (W m-2) - sumSoilCompress = 0._dp ! total soil compression - allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._dp ! soil compression by layer + sumCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = 0._rkind ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = 0._rkind ! sensible heat flux from the canopy to the canopy air space (W m-2) + sumSoilCompress = 0._rkind ! total soil compression + allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._rkind ! soil compression by layer ! define the first flux call in a splitting operation firstSplitOper = (.not.scalarSolution .or. iStateSplit==1) ! initialize subStep - dtSum = 0._dp ! keep track of the portion of the time step that is completed + dtSum = 0._rkind ! keep track of the portion of the time step that is completed nSubsteps = 0 ! loop through substeps @@ -350,7 +350,7 @@ subroutine varSubstepFida(& endif ! set untapped melt energy to zero - untappedMelt(:) = 0._dp + untappedMelt(:) = 0._rkind ! if too much melt or need to reduce length of the coupled step then return ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry @@ -362,7 +362,7 @@ subroutine varSubstepFida(& ! reduce step based on failure if(failedSubstep)then err=0; message='varSubstepFida/' ! recover from failed convergence - dtMultiplier = 0.5_dp ! system failure: step halving + dtMultiplier = 0.5_rkind ! system failure: step halving else endif ! switch between failure and success @@ -423,7 +423,7 @@ subroutine varSubstepFida(& ! modify step err=0 ! error recovery - dtSubstep = dtSubstep/2._dp + dtSubstep = dtSubstep/2._rkind ! check minimum: fail minimum step if there is an error in the update if(dtSubstep next, remove canopy evaporation -- put the unsatisfied evap into sensible heat canopyBalance1 = canopyBalance1 + scalarCanopyEvaporation*dt - if(canopyBalance1 < 0._dp)then + if(canopyBalance1 < 0._rkind)then ! * get superfluous water and energy superflousWat = -canopyBalance1/dt ! kg m-2 s-1 superflousNrg = superflousWat*LH_vap ! W m-2 (J m-2 s-1) ! * update fluxes and states - canopyBalance1 = 0._dp + canopyBalance1 = 0._rkind scalarCanopyEvaporation = scalarCanopyEvaporation + superflousWat scalarLatHeatCanopyEvap = scalarLatHeatCanopyEvap + superflousNrg scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg @@ -845,9 +845,9 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt ! --> next, remove canopy drainage canopyBalance1 = canopyBalance1 - scalarCanopyLiqDrainage*dt - if(canopyBalance1 < 0._dp)then + if(canopyBalance1 < 0._rkind)then superflousWat = -canopyBalance1/dt ! kg m-2 s-1 - canopyBalance1 = 0._dp + canopyBalance1 = 0._rkind scalarCanopyLiqDrainage = scalarCanopyLiqDrainage + superflousWat endif @@ -865,7 +865,7 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt ! check the mass balance fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial - if(abs(liqError) > absConvTol_liquid*10._dp*iden_water)then ! *10 because of precision issues + if(abs(liqError) > absConvTol_liquid*10._rkind*iden_water)then ! *10 because of precision issues write(*,'(a,1x,f20.10)') 'dt = ', dt write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 @@ -890,7 +890,7 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) ! write(1,*) liqError - if(abs(liqError) > absConvTol_liquid*10._dp)then ! *10 because of precision issues + if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues write(*,'(a,1x,f20.10)') 'dt = ', dt write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 @@ -950,15 +950,15 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt ! *** ice ! --> check if we removed too much water - if(scalarCanopyIceTrial < 0._dp .or. any(mLayerVolFracIceTrial < 0._dp) )then + if(scalarCanopyIceTrial < 0._rkind .or. any(mLayerVolFracIceTrial < 0._rkind) )then ! ** ! canopy within numerical precision - if(scalarCanopyIceTrial < 0._dp)then + if(scalarCanopyIceTrial < 0._rkind)then if(scalarCanopyIceTrial > -verySmall)then scalarCanopyLiqTrial = scalarCanopyLiqTrial - scalarCanopyIceTrial - scalarCanopyIceTrial = 0._dp + scalarCanopyIceTrial = 0._rkind ! encountered an inconsistency: spit the dummy else @@ -976,11 +976,11 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt do iState=1,size(mLayerVolFracIceTrial) ! snow layer within numerical precision - if(mLayerVolFracIceTrial(iState) < 0._dp)then + if(mLayerVolFracIceTrial(iState) < 0._rkind)then if(mLayerVolFracIceTrial(iState) > -verySmall)then mLayerVolFracLiqTrial(iState) = mLayerVolFracLiqTrial(iState) - mLayerVolFracIceTrial(iState) - mLayerVolFracIceTrial(iState) = 0._dp + mLayerVolFracIceTrial(iState) = 0._rkind ! encountered an inconsistency: spit the dummy else @@ -1003,15 +1003,15 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt ! *** liquid water ! --> check if we removed too much water - if(scalarCanopyLiqTrial < 0._dp .or. any(mLayerVolFracLiqTrial < 0._dp) )then + if(scalarCanopyLiqTrial < 0._rkind .or. any(mLayerVolFracLiqTrial < 0._rkind) )then ! ** ! canopy within numerical precision - if(scalarCanopyLiqTrial < 0._dp)then + if(scalarCanopyLiqTrial < 0._rkind)then if(scalarCanopyLiqTrial > -verySmall)then scalarCanopyIceTrial = scalarCanopyIceTrial - scalarCanopyLiqTrial - scalarCanopyLiqTrial = 0._dp + scalarCanopyLiqTrial = 0._rkind ! encountered an inconsistency: spit the dummy @@ -1031,11 +1031,11 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt do iState=1,size(mLayerVolFracLiqTrial) ! snow layer within numerical precision - if(mLayerVolFracLiqTrial(iState) < 0._dp)then + if(mLayerVolFracLiqTrial(iState) < 0._rkind)then if(mLayerVolFracLiqTrial(iState) > -verySmall)then mLayerVolFracIceTrial(iState) = mLayerVolFracIceTrial(iState) - mLayerVolFracLiqTrial(iState) - mLayerVolFracLiqTrial(iState) = 0._dp + mLayerVolFracLiqTrial(iState) = 0._rkind ! encountered an inconsistency: spit the dummy else diff --git a/build/source/engine/vegNrgFluxFida.f90 b/build/source/engine/vegNrgFluxFida.f90 index 58dbcfdac..554913909 100644 --- a/build/source/engine/vegNrgFluxFida.f90 +++ b/build/source/engine/vegNrgFluxFida.f90 @@ -113,11 +113,11 @@ module vegNrgFluxFida_module integer(i4b),parameter :: iLoc = 1 ! i-location integer(i4b),parameter :: jLoc = 1 ! j-location ! algorithmic parameters -real(dp),parameter :: missingValue=-9999._dp ! missing value, used when diagnostic or state variables are undefined -real(dp),parameter :: verySmall=1.e-6_dp ! used as an additive constant to check if substantial difference among real numbers -real(dp),parameter :: tinyVal=epsilon(1._dp) ! used as an additive constant to check if substantial difference among real numbers -real(dp),parameter :: mpe=1.e-6_dp ! prevents overflow error if division by zero -real(dp),parameter :: dx=1.e-11_dp ! finite difference increment +real(dp),parameter :: missingValue=-9999._rkind ! missing value, used when diagnostic or state variables are undefined +real(dp),parameter :: verySmall=1.e-6_rkind ! used as an additive constant to check if substantial difference among real numbers +real(dp),parameter :: tinyVal=epsilon(1._rkind) ! used as an additive constant to check if substantial difference among real numbers +real(dp),parameter :: mpe=1.e-6_rkind ! prevents overflow error if division by zero +real(dp),parameter :: dx=1.e-11_rkind ! finite difference increment ! control logical(lgt) :: printflag ! flag to turn on printing contains @@ -321,9 +321,9 @@ subroutine vegNrgFluxFida(& real(dp) :: scaleLAI ! scaled LAI (computing diffuse transmissivity) real(dp) :: diffuseTrans ! diffuse transmissivity (-) real(dp) :: groundEmissivity ! emissivity of the ground surface (-) - real(dp),parameter :: vegEmissivity=0.98_dp ! emissivity of vegetation (0.9665 in JULES) (-) - real(dp),parameter :: soilEmissivity=0.98_dp ! emmisivity of the soil (0.9665 in JULES) (-) - real(dp),parameter :: snowEmissivity=0.99_dp ! emissivity of snow (-) + real(dp),parameter :: vegEmissivity=0.98_rkind ! emissivity of vegetation (0.9665 in JULES) (-) + real(dp),parameter :: soilEmissivity=0.98_rkind ! emmisivity of the soil (0.9665 in JULES) (-) + real(dp),parameter :: snowEmissivity=0.99_rkind ! emissivity of snow (-) real(dp) :: dLWNetCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) real(dp) :: dLWNetGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) real(dp) :: dLWNetCanopy_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) @@ -626,47 +626,47 @@ subroutine vegNrgFluxFida(& case(prescribedTemp,zeroFlux) ! derived fluxes - scalarTotalET = 0._dp ! total ET (kg m-2 s-1) - scalarNetRadiation = 0._dp ! net radiation (W m-2) + scalarTotalET = 0._rkind ! total ET (kg m-2 s-1) + scalarNetRadiation = 0._rkind ! net radiation (W m-2) ! liquid water fluxes associated with evaporation/transpiration - scalarCanopyTranspiration = 0._dp ! canopy transpiration (kg m-2 s-1) - scalarCanopyEvaporation = 0._dp ! canopy evaporation/condensation (kg m-2 s-1) - scalarGroundEvaporation = 0._dp ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + scalarCanopyTranspiration = 0._rkind ! canopy transpiration (kg m-2 s-1) + scalarCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) + scalarGroundEvaporation = 0._rkind ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) ! solid water fluxes associated with sublimation/frost - scalarCanopySublimation = 0._dp ! sublimation from the vegetation canopy ((kg m-2 s-1) - scalarSnowSublimation = 0._dp ! sublimation from the snow surface ((kg m-2 s-1) + scalarCanopySublimation = 0._rkind ! sublimation from the vegetation canopy ((kg m-2 s-1) + scalarSnowSublimation = 0._rkind ! sublimation from the snow surface ((kg m-2 s-1) ! set canopy fluxes to zero (no canopy) - canairNetFlux = 0._dp ! net energy flux for the canopy air space (W m-2) - canopyNetFlux = 0._dp ! net energy flux for the vegetation canopy (W m-2) + canairNetFlux = 0._rkind ! net energy flux for the canopy air space (W m-2) + canopyNetFlux = 0._rkind ! net energy flux for the vegetation canopy (W m-2) ! set canopy derivatives to zero - dCanairNetFlux_dCanairTemp = 0._dp ! derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) - dCanairNetFlux_dCanopyTemp = 0._dp ! derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) - dCanairNetFlux_dGroundTemp = 0._dp ! derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) - dCanopyNetFlux_dCanairTemp = 0._dp ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) - dCanopyNetFlux_dCanopyTemp = 0._dp ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) - dCanopyNetFlux_dGroundTemp = 0._dp ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - dGroundNetFlux_dCanairTemp = 0._dp ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) - dGroundNetFlux_dCanopyTemp = 0._dp ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) + dCanairNetFlux_dCanairTemp = 0._rkind ! derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) + dCanairNetFlux_dCanopyTemp = 0._rkind ! derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) + dCanairNetFlux_dGroundTemp = 0._rkind ! derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) + dCanopyNetFlux_dCanairTemp = 0._rkind ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) + dCanopyNetFlux_dCanopyTemp = 0._rkind ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) + dCanopyNetFlux_dGroundTemp = 0._rkind ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) + dGroundNetFlux_dCanairTemp = 0._rkind ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) + dGroundNetFlux_dCanopyTemp = 0._rkind ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) ! set liquid flux derivatives to zero (canopy evap) - dCanopyEvaporation_dCanLiq = 0._dp ! derivative in canopy evaporation w.r.t. canopy liquid water content (s-1) - dCanopyEvaporation_dTCanair= 0._dp ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTCanopy= 0._dp ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTGround= 0._dp ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dCanLiq = 0._rkind ! derivative in canopy evaporation w.r.t. canopy liquid water content (s-1) + dCanopyEvaporation_dTCanair= 0._rkind ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTCanopy= 0._rkind ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTGround= 0._rkind ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) ! set liquid flux derivatives to zero (ground evap) - dGroundEvaporation_dCanLiq = 0._dp ! derivative in ground evaporation w.r.t. canopy liquid water content (s-1) - dGroundEvaporation_dTCanair= 0._dp ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTCanopy= 0._dp ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTGround= 0._dp ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dCanLiq = 0._rkind ! derivative in ground evaporation w.r.t. canopy liquid water content (s-1) + dGroundEvaporation_dTCanair= 0._rkind ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTCanopy= 0._rkind ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTGround= 0._rkind ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) ! compute fluxes and derivatives -- separate approach for prescribed temperature and zero flux if(ix_bcUpprTdyn == prescribedTemp)then ! compute ground net flux (W m-2) - groundNetFlux = -diag_data%var(iLookDIAG%iLayerThermalC)%dat(0)*(groundTempTrial - upperBoundTemp)/(prog_data%var(iLookPROG%mLayerDepth)%dat(1)*0.5_dp) + groundNetFlux = -diag_data%var(iLookDIAG%iLayerThermalC)%dat(0)*(groundTempTrial - upperBoundTemp)/(prog_data%var(iLookPROG%mLayerDepth)%dat(1)*0.5_rkind) ! compute derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - dGroundNetFlux_dGroundTemp = -diag_data%var(iLookDIAG%iLayerThermalC)%dat(0)/(prog_data%var(iLookPROG%mLayerDepth)%dat(1)*0.5_dp) + dGroundNetFlux_dGroundTemp = -diag_data%var(iLookDIAG%iLayerThermalC)%dat(0)/(prog_data%var(iLookPROG%mLayerDepth)%dat(1)*0.5_rkind) elseif(model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision == zeroFlux)then - groundNetFlux = 0._dp - dGroundNetFlux_dGroundTemp = 0._dp + groundNetFlux = 0._rkind + dGroundNetFlux_dGroundTemp = 0._rkind else err=20; message=trim(message)//'unable to identify upper boundary condition for thermodynamics: expect the case to be prescribedTemp or zeroFlux'; return end if @@ -702,8 +702,8 @@ subroutine vegNrgFluxFida(& if(firstFluxCall .and. firstSubStep)then ! vapor pressure in the canopy air space initialized as vapor pressure of air above the vegetation canopy ! NOTE: this is needed for the stomatal resistance calculations - if(scalarVP_CanopyAir < 0._dp)then - scalarVP_CanopyAir = scalarVPair - 1._dp ! "small" offset used to assist in checking initial derivative calculations + if(scalarVP_CanopyAir < 0._rkind)then + scalarVP_CanopyAir = scalarVPair - 1._rkind ! "small" offset used to assist in checking initial derivative calculations end if end if @@ -715,17 +715,17 @@ subroutine vegNrgFluxFida(& if(nSnow > 0)then if(groundTempTrial > Tfreeze)then; err=20; message=trim(message)//'do not expect ground temperature > 0 when snow is on the ground'; return; end if scalarLatHeatSubVapGround = LH_sub ! sublimation from snow - scalarGroundSnowFraction = 1._dp + scalarGroundSnowFraction = 1._rkind ! case when the ground is snow-free else scalarLatHeatSubVapGround = LH_vap ! evaporation of water in the soil pores: this occurs even if frozen because of super-cooled water - scalarGroundSnowFraction = 0._dp + scalarGroundSnowFraction = 0._rkind end if ! (if there is snow on the ground) end if ! (if the first flux call) !write(*,'(a,1x,10(f30.10,1x))') 'groundTempTrial, scalarLatHeatSubVapGround = ', groundTempTrial, scalarLatHeatSubVapGround ! compute the roughness length of the ground (ground below the canopy or non-vegetated surface) - z0Ground = z0soil*(1._dp - scalarGroundSnowFraction) + z0Snow*scalarGroundSnowFraction ! roughness length (m) + z0Ground = z0soil*(1._rkind - scalarGroundSnowFraction) + z0Snow*scalarGroundSnowFraction ! roughness length (m) ! compute the total vegetation area index (leaf plus stem) VAI = scalarLAI + scalarSAI ! vegetation area index @@ -736,16 +736,16 @@ subroutine vegNrgFluxFida(& select case(ix_canopyEmis) ! *** simple exponential function case(simplExp) - scalarCanopyEmissivity = 1._dp - exp(-exposedVAI) ! effective emissivity of the canopy (-) + scalarCanopyEmissivity = 1._rkind - exp(-exposedVAI) ! effective emissivity of the canopy (-) ! *** canopy emissivity parameterized as a function of diffuse transmissivity case(difTrans) ! compute the exponential integral - scaleLAI = 0.5_dp*exposedVAI + scaleLAI = 0.5_rkind*exposedVAI expi = expInt(scaleLAI) ! compute diffuse transmissivity (-) - diffuseTrans = (1._dp - scaleLAI)*exp(-scaleLAI) + (scaleLAI**2._dp)*expi + diffuseTrans = (1._rkind - scaleLAI)*exp(-scaleLAI) + (scaleLAI**2._rkind)*expi ! compute the canopy emissivity - scalarCanopyEmissivity = (1._dp - diffuseTrans)*vegEmissivity + scalarCanopyEmissivity = (1._rkind - diffuseTrans)*vegEmissivity ! *** check we found the correct option case default err=20; message=trim(message)//'unable to identify option for canopy emissivity'; return @@ -753,10 +753,10 @@ subroutine vegNrgFluxFida(& end if ! ensure canopy longwave fluxes are zero when not computing canopy fluxes - if(.not.computeVegFlux) scalarCanopyEmissivity=0._dp + if(.not.computeVegFlux) scalarCanopyEmissivity=0._rkind ! compute emissivity of the ground surface (-) - groundEmissivity = scalarGroundSnowFraction*snowEmissivity + (1._dp - scalarGroundSnowFraction)*soilEmissivity ! emissivity of the ground surface (-) + groundEmissivity = scalarGroundSnowFraction*snowEmissivity + (1._rkind - scalarGroundSnowFraction)*soilEmissivity ! emissivity of the ground surface (-) ! compute the fraction of canopy that is wet ! NOTE: we either sublimate or evaporate over the entire substep @@ -764,10 +764,10 @@ subroutine vegNrgFluxFida(& ! compute the fraction of liquid water in the canopy (-) totalCanopyWater = canopyLiqTrial + canopyIceTrial - if(totalCanopyWater > tiny(1.0_dp))then + if(totalCanopyWater > tiny(1.0_rkind))then fracLiquidCanopy = canopyLiqTrial / (canopyLiqTrial + canopyIceTrial) else - fracLiquidCanopy = 0._dp + fracLiquidCanopy = 0._rkind end if ! get wetted fraction and derivatives @@ -792,9 +792,9 @@ subroutine vegNrgFluxFida(& if(err/=0)then; message=trim(message)//trim(cmessage); return; end if else - scalarCanopyWetFraction = 0._dp ! canopy wetted fraction (-) - dCanopyWetFraction_dWat = 0._dp ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) - dCanopyWetFraction_dT = 0._dp ! derivative in wetted fraction w.r.t. canopy temperature (K-1) + scalarCanopyWetFraction = 0._rkind ! canopy wetted fraction (-) + dCanopyWetFraction_dWat = 0._rkind ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) + dCanopyWetFraction_dT = 0._rkind ! derivative in wetted fraction w.r.t. canopy temperature (K-1) end if !write(*,'(a,1x,L1,1x,f25.15,1x))') 'computeVegFlux, scalarCanopyWetFraction = ', computeVegFlux, scalarCanopyWetFraction !print*, 'dCanopyWetFraction_dWat = ', dCanopyWetFraction_dWat @@ -1070,7 +1070,7 @@ subroutine vegNrgFluxFida(& if(err/=0)then; message=trim(message)//trim(cmessage); return; end if else - canopyWetFraction = 0._dp + canopyWetFraction = 0._rkind end if !print*, 'wetted fraction derivative = ', (canopyWetFraction - scalarCanopyWetFraction)/dx !pause @@ -1170,15 +1170,15 @@ subroutine vegNrgFluxFida(& ! (soil water evaporation factor [0-1]) soilEvapFactor = mLayerVolFracLiq(nSnow+1)/(theta_sat - theta_res) ! (resistance from the soil [s m-1]) - scalarSoilResistance = scalarGroundSnowFraction*1._dp + (1._dp - scalarGroundSnowFraction)*EXP(8.25_dp - 4.225_dp*soilEvapFactor) ! Sellers (1992) - !scalarSoilResistance = scalarGroundSnowFraction*0._dp + (1._dp - scalarGroundSnowFraction)*exp(8.25_dp - 6.0_dp*soilEvapFactor) ! Niu adjustment to decrease resitance for wet soil + scalarSoilResistance = scalarGroundSnowFraction*1._rkind + (1._rkind - scalarGroundSnowFraction)*EXP(8.25_rkind - 4.225_rkind*soilEvapFactor) ! Sellers (1992) + !scalarSoilResistance = scalarGroundSnowFraction*0._rkind + (1._rkind - scalarGroundSnowFraction)*exp(8.25_rkind - 6.0_rkind*soilEvapFactor) ! Niu adjustment to decrease resitance for wet soil ! (relative humidity in the soil pores [0-1]) - if(mLayerMatricHead(1) > -1.e+6_dp)then ! avoid problems with numerical precision when soil is very dry + if(mLayerMatricHead(1) > -1.e+6_rkind)then ! avoid problems with numerical precision when soil is very dry soilRelHumidity_noSnow = exp( (mLayerMatricHead(1)*gravity) / (groundTemp*R_wv) ) else - soilRelHumidity_noSnow = 0._dp + soilRelHumidity_noSnow = 0._rkind end if ! (if matric head is very low) - scalarSoilRelHumidity = scalarGroundSnowFraction*1._dp + (1._dp - scalarGroundSnowFraction)*soilRelHumidity_noSnow + scalarSoilRelHumidity = scalarGroundSnowFraction*1._rkind + (1._rkind - scalarGroundSnowFraction)*soilRelHumidity_noSnow !print*, 'mLayerMatricHead(1), scalarSoilRelHumidity = ', mLayerMatricHead(1), scalarSoilRelHumidity end if ! (if the first flux call) @@ -1398,21 +1398,21 @@ subroutine vegNrgFluxFida(& !print*, 'scalarLatHeatGround = ', scalarLatHeatGround ! (canopy transpiration/sublimation) if(scalarLatHeatSubVapCanopy > LH_vap+verySmall)then ! sublimation - scalarCanopyEvaporation = 0._dp + scalarCanopyEvaporation = 0._rkind scalarCanopySublimation = scalarLatHeatCanopyEvap/LH_sub - if(scalarLatHeatCanopyTrans > 0._dp)then ! flux directed towards the veg + if(scalarLatHeatCanopyTrans > 0._rkind)then ! flux directed towards the veg scalarCanopySublimation = scalarCanopySublimation + scalarLatHeatCanopyTrans/LH_sub ! frost - scalarCanopyTranspiration = 0._dp + scalarCanopyTranspiration = 0._rkind else scalarCanopyTranspiration = scalarLatHeatCanopyTrans/LH_vap ! transpiration is always vapor end if ! (canopy transpiration/evaporation) else ! evaporation scalarCanopyEvaporation = scalarLatHeatCanopyEvap/LH_vap - scalarCanopySublimation = 0._dp - if(scalarLatHeatCanopyTrans > 0._dp)then ! flux directed towards the veg + scalarCanopySublimation = 0._rkind + if(scalarLatHeatCanopyTrans > 0._rkind)then ! flux directed towards the veg scalarCanopyEvaporation = scalarCanopyEvaporation + scalarLatHeatCanopyTrans/LH_vap - scalarCanopyTranspiration = 0._dp + scalarCanopyTranspiration = 0._rkind else scalarCanopyTranspiration = scalarLatHeatCanopyTrans/LH_vap end if @@ -1421,13 +1421,13 @@ subroutine vegNrgFluxFida(& if(scalarLatHeatSubVapGround > LH_vap+verySmall)then ! sublimation ! NOTE: this should only occur when we have formed snow layers, so check if(nSnow == 0)then; err=20; message=trim(message)//'only expect snow sublimation when we have formed some snow layers'; return; end if - scalarGroundEvaporation = 0._dp ! ground evaporation is zero once the snowpack has formed + scalarGroundEvaporation = 0._rkind ! ground evaporation is zero once the snowpack has formed scalarSnowSublimation = scalarLatHeatGround/LH_sub else ! NOTE: this should only occur when we have no snow layers, so check if(nSnow > 0)then; err=20; message=trim(message)//'only expect ground evaporation when there are no snow layers'; return; end if scalarGroundEvaporation = scalarLatHeatGround/LH_vap - scalarSnowSublimation = 0._dp ! no sublimation from snow if no snow layers have formed + scalarSnowSublimation = 0._rkind ! no sublimation from snow if no snow layers have formed end if !print*, 'scalarSnowSublimation, scalarLatHeatGround = ', scalarSnowSublimation, scalarLatHeatGround @@ -1474,10 +1474,10 @@ subroutine vegNrgFluxFida(& ! sublimation else - dCanopyEvaporation_dCanLiq = 0._dp ! (s-1) - dCanopyEvaporation_dTCanair = 0._dp ! (kg m-2 s-1 K-1) - dCanopyEvaporation_dTCanopy = 0._dp ! (kg m-2 s-1 K-1) - dCanopyEvaporation_dTGround = 0._dp ! (kg m-2 s-1 K-1) + dCanopyEvaporation_dCanLiq = 0._rkind ! (s-1) + dCanopyEvaporation_dTCanair = 0._rkind ! (kg m-2 s-1 K-1) + dCanopyEvaporation_dTCanopy = 0._rkind ! (kg m-2 s-1 K-1) + dCanopyEvaporation_dTGround = 0._rkind ! (kg m-2 s-1 K-1) end if ! compute the liquid water derivarives (ground evap) @@ -1538,7 +1538,7 @@ subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFact real(dp) :: rawWetFractionDeriv ! derivative in canopy wet fraction w.r.t. storage (kg-1 m2) real(dp) :: smoothFunc ! smoothing function used to improve numerical stability at times with limited water storage (-) real(dp) :: smoothFuncDeriv ! derivative in the smoothing function w.r.t.canopy storage (kg-1 m2) - real(dp) :: verySmall=epsilon(1._dp) ! a very small number + real(dp) :: verySmall=epsilon(1._rkind) ! a very small number ! -------------------------------------------------------------------------------------------------------------- ! compute relative canopy water @@ -1547,18 +1547,18 @@ subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFact ! compute an initial value of the canopy wet fraction ! - canopy below value where canopy is 100% wet - if(relativeCanopyWater < 1._dp)then + if(relativeCanopyWater < 1._rkind)then rawCanopyWetFraction = canopyWettingFactor*(relativeCanopyWater**canopyWettingExp) if(derDesire .and. relativeCanopyWater>verySmall)then - rawWetFractionDeriv = (canopyWettingFactor*canopyWettingExp/canopyMax)*relativeCanopyWater**(canopyWettingExp - 1._dp) + rawWetFractionDeriv = (canopyWettingFactor*canopyWettingExp/canopyMax)*relativeCanopyWater**(canopyWettingExp - 1._rkind) else - rawWetFractionDeriv = 0._dp + rawWetFractionDeriv = 0._rkind end if ! - canopy is at capacity (canopyWettingFactor) else rawCanopyWetFraction = canopyWettingFactor - rawWetFractionDeriv = 0._dp + rawWetFractionDeriv = 0._rkind end if ! smooth canopy wetted fraction @@ -1574,7 +1574,7 @@ subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFact if(derDesire .and. smoothing)then ! NOTE: raw derivative is used if not smoothing canopyWetFractionDeriv = rawWetFractionDeriv*smoothFunc + rawCanopyWetFraction*smoothFuncDeriv else - canopyWetFractionDeriv = 0._dp + canopyWetFractionDeriv = 0._rkind end if end subroutine wetFraction @@ -1593,9 +1593,9 @@ subroutine logisticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) ! local variables real(dp) :: xArg ! argument used in the smoothing function (-) real(dp) :: expX ! exp(-xArg) -- used multiple times - real(dp),parameter :: smoothThresh=0.01_dp ! mid-point of the smoothing function (kg m-2) - real(dp),parameter :: smoothScale=0.001_dp ! scaling factor for the smoothing function (kg m-2) - real(dp),parameter :: xLimit=50._dp ! don't compute exponents for > xLimit + real(dp),parameter :: smoothThresh=0.01_rkind ! mid-point of the smoothing function (kg m-2) + real(dp),parameter :: smoothScale=0.001_rkind ! scaling factor for the smoothing function (kg m-2) + real(dp),parameter :: xLimit=50._rkind ! don't compute exponents for > xLimit ! -------------------------------------------------------------------------------------------------------------- ! compute argument in the smoothing function xArg = (canopyLiq - smoothThresh)/smoothScale @@ -1603,19 +1603,19 @@ subroutine logisticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) ! only compute smoothing function for small exponents if(xArg > -xLimit .and. xArg < xLimit)then ! avoid huge exponents expX = exp(-xarg) ! (also used in the derivative) - smoothFunc = 1._dp / (1._dp + expX) ! (logistic smoother) + smoothFunc = 1._rkind / (1._rkind + expX) ! (logistic smoother) if(derDesire)then - smoothFuncDeriv = expX / (smoothScale * (1._dp + expX)**2._dp) ! (derivative in the smoothing function) + smoothFuncDeriv = expX / (smoothScale * (1._rkind + expX)**2._rkind) ! (derivative in the smoothing function) else - smoothFuncDeriv = 0._dp + smoothFuncDeriv = 0._rkind end if ! outside limits: special case of smooth exponents else - if(xArg < 0._dp)then; smoothFunc = 0._dp ! xArg < -xLimit - else; smoothFunc = 1._dp ! xArg > xLimit + if(xArg < 0._rkind)then; smoothFunc = 0._rkind ! xArg < -xLimit + else; smoothFunc = 1._rkind ! xArg > xLimit end if - smoothFuncDeriv = 0._dp + smoothFuncDeriv = 0._rkind end if ! check for huge exponents end subroutine logisticSmoother @@ -1707,7 +1707,7 @@ subroutine longwaveBal(& real(dp) :: TCan ! value of canopy temperature used in flux calculations (may be perturbed) real(dp) :: TGnd ! value of ground temperature used in flux calculations (may be perturbed) real(dp) :: fluxBalance ! check energy closure (W m-2) - real(dp),parameter :: fluxTolerance=1.e-10_dp ! tolerance for energy closure (W m-2) + real(dp),parameter :: fluxTolerance=1.e-10_rkind ! tolerance for energy closure (W m-2) real(dp) :: dLWRadCanopy_dTCanopy ! derivative in emitted radiation at the canopy w.r.t. canopy temperature real(dp) :: dLWRadGround_dTGround ! derivative in emitted radiation at the ground w.r.t. ground temperature real(dp) :: LWNetCanopy_dStateCanopy ! net lw canopy flux after perturbation in canopy temperature @@ -1765,28 +1765,28 @@ subroutine longwaveBal(& ! compute longwave fluxes from canopy and the ground if(computeVegFlux)then - LWRadCanopy = emc*sb*TCan**4._dp ! longwave radiation emitted from the canopy (W m-2) + LWRadCanopy = emc*sb*TCan**4._rkind ! longwave radiation emitted from the canopy (W m-2) else - LWRadCanopy = 0._dp + LWRadCanopy = 0._rkind end if - LWRadGround = emg*sb*TGnd**4._dp ! longwave radiation emitted at the ground surface (W m-2) + LWRadGround = emg*sb*TGnd**4._rkind ! longwave radiation emitted at the ground surface (W m-2) ! compute fluxes originating from the atmosphere - LWRadUbound2Canopy = (emc + (1._dp - emc)*(1._dp - emg)*emc)*LWRadUbound ! downward atmospheric longwave radiation absorbed by the canopy (W m-2) - LWRadUbound2Ground = (1._dp - emc)*emg*LWRadUbound ! downward atmospheric longwave radiation absorbed by the ground (W m-2) - LWRadUbound2Ubound = (1._dp - emc)*(1._dp - emg)*(1._dp - emc)*LWRadUbound ! atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) + LWRadUbound2Canopy = (emc + (1._rkind - emc)*(1._rkind - emg)*emc)*LWRadUbound ! downward atmospheric longwave radiation absorbed by the canopy (W m-2) + LWRadUbound2Ground = (1._rkind - emc)*emg*LWRadUbound ! downward atmospheric longwave radiation absorbed by the ground (W m-2) + LWRadUbound2Ubound = (1._rkind - emc)*(1._rkind - emg)*(1._rkind - emc)*LWRadUbound ! atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) ! compute fluxes originating from the canopy - LWRadCanopy2Ubound = (1._dp + (1._dp - emc)*(1._dp - emg))*LWRadCanopy ! longwave radiation emitted from canopy lost thru upper boundary (W m-2) + LWRadCanopy2Ubound = (1._rkind + (1._rkind - emc)*(1._rkind - emg))*LWRadCanopy ! longwave radiation emitted from canopy lost thru upper boundary (W m-2) LWRadCanopy2Ground = emg*LWRadCanopy ! longwave radiation emitted from canopy absorbed by the ground (W m-2) - LWRadCanopy2Canopy = emc*(1._dp - emg)*LWRadCanopy ! canopy longwave reflected from ground and absorbed by the canopy (W m-2) + LWRadCanopy2Canopy = emc*(1._rkind - emg)*LWRadCanopy ! canopy longwave reflected from ground and absorbed by the canopy (W m-2) ! compute fluxes originating from the ground surface - LWRadGround2Ubound = (1._dp - emc)*LWRadGround ! longwave radiation emitted from ground lost thru upper boundary (W m-2) + LWRadGround2Ubound = (1._rkind - emc)*LWRadGround ! longwave radiation emitted from ground lost thru upper boundary (W m-2) LWRadGround2Canopy = emc*LWRadGround ! longwave radiation emitted from ground and absorbed by the canopy (W m-2) ! compute net longwave radiation (W m-2) - LWNetCanopy = LWRadUbound2Canopy + LWRadGround2Canopy + LWRadCanopy2Canopy - 2._dp*LWRadCanopy ! canopy + LWNetCanopy = LWRadUbound2Canopy + LWRadGround2Canopy + LWRadCanopy2Canopy - 2._rkind*LWRadCanopy ! canopy LWNetGround = LWRadUbound2Ground + LWRadCanopy2Ground - LWRadGround ! ground surface LWNetUbound = LWRadUbound - LWRadUbound2Ubound - LWRadCanopy2Ubound - LWRadGround2Ubound ! upper boundary @@ -1847,10 +1847,10 @@ subroutine longwaveBal(& ! ***** analytical derivatives case(analytical) ! compute initial derivatives - dLWRadCanopy_dTCanopy = 4._dp*emc*sb*TCan**3._dp - dLWRadGround_dTGround = 4._dp*emg*sb*TGnd**3._dp + dLWRadCanopy_dTCanopy = 4._rkind*emc*sb*TCan**3._rkind + dLWRadGround_dTGround = 4._rkind*emg*sb*TGnd**3._rkind ! compute analytical derivatives - dLWNetCanopy_dTCanopy = (emc*(1._dp - emg) - 2._dp)*dLWRadCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) + dLWNetCanopy_dTCanopy = (emc*(1._rkind - emg) - 2._rkind)*dLWRadCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) dLWNetGround_dTGround = -dLWRadGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) dLWNetCanopy_dTGround = emc*dLWRadGround_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) dLWNetGround_dTCanopy = emg*dLWRadCanopy_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) @@ -1990,14 +1990,14 @@ subroutine aeroResist(& ! local variables: general character(LEN=256) :: cmessage ! error message of downwind routine ! local variables: vegetation roughness and dispalcement height - real(dp),parameter :: oneThird=1._dp/3._dp ! 1/3 - real(dp),parameter :: twoThirds=2._dp/3._dp ! 2/3 + real(dp),parameter :: oneThird=1._rkind/3._rkind ! 1/3 + real(dp),parameter :: twoThirds=2._rkind/3._rkind ! 2/3 real(dp),parameter :: C_r = 0.3 ! roughness element drag coefficient (-) from Raupach (BLM, 1994) - real(dp),parameter :: C_s = 0.003_dp ! substrate surface drag coefficient (-) from Raupach (BLM, 1994) - real(dp),parameter :: approxDragCoef_max = 0.3_dp ! maximum value of the approximate drag coefficient (-) from Raupach (BLM, 1994) - real(dp),parameter :: psi_h = 0.193_dp ! roughness sub-layer influence function (-) from Raupach (BLM, 1994) - real(dp),parameter :: c_d1 = 7.5_dp ! scaling parameter used to define displacement height (-) from Raupach (BLM, 1994) - real(dp),parameter :: cd_CM = 0.2_dp ! mean drag coefficient for individual leaves (-) from Choudhury and Monteith (QJRMS, 1988) + real(dp),parameter :: C_s = 0.003_rkind ! substrate surface drag coefficient (-) from Raupach (BLM, 1994) + real(dp),parameter :: approxDragCoef_max = 0.3_rkind ! maximum value of the approximate drag coefficient (-) from Raupach (BLM, 1994) + real(dp),parameter :: psi_h = 0.193_rkind ! roughness sub-layer influence function (-) from Raupach (BLM, 1994) + real(dp),parameter :: c_d1 = 7.5_rkind ! scaling parameter used to define displacement height (-) from Raupach (BLM, 1994) + real(dp),parameter :: cd_CM = 0.2_rkind ! mean drag coefficient for individual leaves (-) from Choudhury and Monteith (QJRMS, 1988) real(dp) :: funcLAI ! temporary variable to calculate zero plane displacement for the canopy real(dp) :: fracCanopyHeight ! zero plane displacement expressed as a fraction of canopy height real(dp) :: approxDragCoef ! approximate drag coefficient used in the computation of canopy roughness length (-) @@ -2013,7 +2013,7 @@ subroutine aeroResist(& real(dp) :: heightAboveGround ! height above the snow surface (m) real(dp) :: heightCanopyTopAboveSnow ! height at the top of the vegetation canopy relative to snowpack (m) real(dp) :: heightCanopyBottomAboveSnow ! height at the bottom of the vegetation canopy relative to snowpack (m) - real(dp),parameter :: xTolerance=0.1_dp ! tolerance to handle the transition from exponential to log-below canopy + real(dp),parameter :: xTolerance=0.1_rkind ! tolerance to handle the transition from exponential to log-below canopy ! local variables: derivatives real(dp) :: dFV_dT ! derivative in friction velocity w.r.t. canopy air temperature real(dp) :: dED_dT ! derivative in eddy diffusivity at the top of the canopy w.r.t. canopy air temperature @@ -2046,27 +2046,27 @@ subroutine aeroResist(& ! First, calculate new coordinate system above snow - use these to scale wind profiles and resistances ! NOTE: the new coordinate system makes zeroPlaneDisplacement and z0Canopy consistent heightCanopyTopAboveSnow = heightCanopyTop - snowDepth - heightCanopyBottomAboveSnow = max(heightCanopyBottom - snowDepth, 0.0_dp) + heightCanopyBottomAboveSnow = max(heightCanopyBottom - snowDepth, 0.0_rkind) select case(ixVegTraits) ! Raupach (BLM 1994) "Simplified expressions..." case(Raupach_BLM1994) ! (compute zero-plane displacement) funcLAI = sqrt(c_d1*exposedVAI) - fracCanopyHeight = -(1._dp - exp(-funcLAI))/funcLAI + 1._dp + fracCanopyHeight = -(1._rkind - exp(-funcLAI))/funcLAI + 1._rkind zeroPlaneDisplacement = fracCanopyHeight*(heightCanopyTopAboveSnow-heightCanopyBottomAboveSnow)+heightCanopyBottomAboveSnow ! (coupute roughness length of the veg canopy) - approxDragCoef = min( sqrt(C_s + C_r*exposedVAI/2._dp), approxDragCoef_max) - z0Canopy = (1._dp - fracCanopyHeight) * exp(-vkc*approxDragCoef - psi_h) * (heightCanopyTopAboveSnow-heightCanopyBottomAboveSnow) + approxDragCoef = min( sqrt(C_s + C_r*exposedVAI/2._rkind), approxDragCoef_max) + z0Canopy = (1._rkind - fracCanopyHeight) * exp(-vkc*approxDragCoef - psi_h) * (heightCanopyTopAboveSnow-heightCanopyBottomAboveSnow) ! Choudhury and Monteith (QJRMS 1988) "A four layer model for the heat budget..." case(CM_QJRMS1988) funcLAI = cd_CM*exposedVAI - zeroPlaneDisplacement = 1.1_dp*heightCanopyTopAboveSnow*log(1._dp + funcLAI**0.25_dp) - if(funcLAI < 0.2_dp)then - z0Canopy = z0Ground + 0.3_dp*heightCanopyTopAboveSnow*funcLAI**0.5_dp + zeroPlaneDisplacement = 1.1_rkind*heightCanopyTopAboveSnow*log(1._rkind + funcLAI**0.25_rkind) + if(funcLAI < 0.2_rkind)then + z0Canopy = z0Ground + 0.3_rkind*heightCanopyTopAboveSnow*funcLAI**0.5_rkind else - z0Canopy = 0.3_dp*heightCanopyTopAboveSnow*(1._dp - zeroPlaneDisplacement/heightCanopyTopAboveSnow) + z0Canopy = 0.3_rkind*heightCanopyTopAboveSnow*(1._rkind - zeroPlaneDisplacement/heightCanopyTopAboveSnow) end if ! constant parameters dependent on the vegetation type @@ -2119,15 +2119,15 @@ subroutine aeroResist(& if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! compute turbulent exchange coefficient (-) - canopyExNeut = (vkc**2._dp) / ( log((mHeight - zeroPlaneDisplacement)/z0Canopy))**2._dp ! coefficient under conditions of neutral stability + canopyExNeut = (vkc**2._rkind) / ( log((mHeight - zeroPlaneDisplacement)/z0Canopy))**2._rkind ! coefficient under conditions of neutral stability sfc2AtmExchangeCoeff_canopy = canopyExNeut*canopyStabilityCorrection ! after stability corrections ! compute the friction velocity (m s-1) frictionVelocity = windspd * sqrt(sfc2AtmExchangeCoeff_canopy) ! compute the above-canopy resistance (s m-1) - canopyResistance = 1._dp/(sfc2AtmExchangeCoeff_canopy*windspd) - if(canopyResistance < 0._dp)then; err=20; message=trim(message)//'canopy resistance < 0'; return; end if + canopyResistance = 1._rkind/(sfc2AtmExchangeCoeff_canopy*windspd) + if(canopyResistance < 0._rkind)then; err=20; message=trim(message)//'canopy resistance < 0'; return; end if ! compute windspeed at the top of the canopy above snow depth (m s-1) ! NOTE: stability corrections cancel out @@ -2140,19 +2140,19 @@ subroutine aeroResist(& ! compute windspeed at the height z0Canopy+zeroPlaneDisplacement (m s-1) referenceHeight = z0Canopy+zeroPlaneDisplacement - windConvFactor = exp(-windReductionFactor*(1._dp - (referenceHeight/heightCanopyTopAboveSnow))) + windConvFactor = exp(-windReductionFactor*(1._rkind - (referenceHeight/heightCanopyTopAboveSnow))) windspdRefHeight = windspdCanopyTop*windConvFactor ! compute windspeed at the bottom of the canopy relative to the snow depth (m s-1) - windConvFactor = exp(-windReductionFactor*(1._dp - (heightCanopyBottomAboveSnow/heightCanopyTopAboveSnow))) + windConvFactor = exp(-windReductionFactor*(1._rkind - (heightCanopyBottomAboveSnow/heightCanopyTopAboveSnow))) windspdCanopyBottom = windspdCanopyTop*windConvFactor ! compute the leaf boundary layer resistance (s m-1) singleLeafConductance = leafExchangeCoeff*sqrt(windspdCanopyTop/leafDimension) - leaf2CanopyScaleFactor = (2._dp/windReductionFactor) * (1._dp - exp(-windReductionFactor/2._dp)) ! factor to scale from the leaf to the canopy + leaf2CanopyScaleFactor = (2._rkind/windReductionFactor) * (1._rkind - exp(-windReductionFactor/2._rkind)) ! factor to scale from the leaf to the canopy canopyLeafConductance = singleLeafConductance*leaf2CanopyScaleFactor - leafResistance = 1._dp/(canopyLeafConductance) - if(leafResistance < 0._dp)then; err=20; message=trim(message)//'leaf resistance < 0'; return; end if + leafResistance = 1._rkind/(canopyLeafConductance) + if(leafResistance < 0._rkind)then; err=20; message=trim(message)//'leaf resistance < 0'; return; end if ! compute eddy diffusivity for heat at the top of the canopy (m2 s-1) ! Note: use of friction velocity here includes stability adjustments @@ -2179,7 +2179,7 @@ subroutine aeroResist(& tmp2 = exp(-windReductionFactor*(z0Canopy+zeroPlaneDisplacement)/heightCanopyTopAboveSnow) groundResistanceNeutral = ( heightCanopyTopAboveSnow*exp(windReductionFactor) / (windReductionFactor*eddyDiffusCanopyTop) ) * (tmp1 - tmp2) ! (add log-below-canopy component) - groundResistanceNeutral = groundResistanceNeutral + (1._dp/(max(0.1_dp,windspdCanopyBottom)*vkc**2._dp))*(log(heightCanopyBottomAboveSnow/z0Ground))**2._dp + groundResistanceNeutral = groundResistanceNeutral + (1._rkind/(max(0.1_rkind,windspdCanopyBottom)*vkc**2._rkind))*(log(heightCanopyBottomAboveSnow/z0Ground))**2._rkind endif ! switch between exponential profile and log-below-canopy @@ -2193,7 +2193,7 @@ subroutine aeroResist(& referenceHeight, & ! input: height of the canopy air space temperature/wind (m) canairTemp, & ! input: temperature of the canopy air space (K) groundTemp, & ! input: temperature of the ground surface (K) - max(0.1_dp,windspdRefHeight), & ! input: wind speed at height z0Canopy+zeroPlaneDisplacement (m s-1) + max(0.1_rkind,windspdRefHeight), & ! input: wind speed at height z0Canopy+zeroPlaneDisplacement (m s-1) ! input: stability parameters critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) Louis79_bparam, & ! input: parameter in Louis (1979) stability function @@ -2209,7 +2209,7 @@ subroutine aeroResist(& ! compute the ground resistance groundResistance = groundResistanceNeutral / groundStabilityCorrection - if(groundResistance < 0._dp)then; err=20; message=trim(message)//'ground resistance < 0 [vegetation is present]'; return; end if + if(groundResistance < 0._rkind)then; err=20; message=trim(message)//'ground resistance < 0 [vegetation is present]'; return; end if ! ----------------------------------------------------------------------------------------------------------------------------------------- ! ----------------------------------------------------------------------------------------------------------------------------------------- @@ -2217,15 +2217,15 @@ subroutine aeroResist(& else ! no canopy, so set huge resistances (not used) - canopyResistance = 1.e12_dp ! not used: huge resistance, so conductance is essentially zero - leafResistance = 1.e12_dp ! not used: huge resistance, so conductance is essentially zero + canopyResistance = 1.e12_rkind ! not used: huge resistance, so conductance is essentially zero + leafResistance = 1.e12_rkind ! not used: huge resistance, so conductance is essentially zero ! check that measurement height above the ground surface is above the roughness length if(mHeight < snowDepth+z0Ground)then; err=20; message=trim(message)//'measurement height < snow depth + roughness length'; return; end if ! compute the resistance between the surface and canopy air UNDER NEUTRAL CONDITIONS (s m-1) - groundExNeut = (vkc**2._dp) / ( log((mHeight - snowDepth)/z0Ground)**2._dp) ! turbulent transfer coefficient under conditions of neutral stability (-) - groundResistanceNeutral = 1._dp / (groundExNeut*windspd) + groundExNeut = (vkc**2._rkind) / ( log((mHeight - snowDepth)/z0Ground)**2._rkind) ! turbulent transfer coefficient under conditions of neutral stability (-) + groundResistanceNeutral = 1._rkind / (groundExNeut*windspd) ! define height above the snow surface heightAboveGround = mHeight - snowDepth @@ -2265,7 +2265,7 @@ subroutine aeroResist(& ! compute the ground resistance (after stability corrections) groundResistance = groundResistanceNeutral/groundStabilityCorrection - if(groundResistance < 0._dp)then; err=20; message=trim(message)//'ground resistance < 0 [no vegetation]'; return; end if + if(groundResistance < 0._rkind)then; err=20; message=trim(message)//'ground resistance < 0 [no vegetation]'; return; end if ! set all canopy variables to missing (no canopy!) z0Canopy = missingValue ! roughness length of the vegetation canopy (m) @@ -2292,32 +2292,32 @@ subroutine aeroResist(& ! ***** compute derivatives w.r.t. canopy temperature ! NOTE: derivatives are zero because using canopy air space temperature - dCanopyResistance_dTCanopy = 0._dp ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - dGroundResistance_dTCanopy = 0._dp ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + dCanopyResistance_dTCanopy = 0._rkind ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + dGroundResistance_dTCanopy = 0._rkind ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) ! ***** compute derivatives w.r.t. ground temperature (s m-1 K-1) - dGroundResistance_dTGround = -(groundResistanceNeutral*dGroundStabilityCorrection_dSfcTemp)/(groundStabilityCorrection**2._dp) + dGroundResistance_dTGround = -(groundResistanceNeutral*dGroundStabilityCorrection_dSfcTemp)/(groundStabilityCorrection**2._rkind) ! ***** compute derivatives w.r.t. temperature of the canopy air space (s m-1 K-1) ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - dCanopyResistance_dTCanair = -dCanopyStabilityCorrection_dCasTemp/(windspd*canopyExNeut*canopyStabilityCorrection**2._dp) + dCanopyResistance_dTCanair = -dCanopyStabilityCorrection_dCasTemp/(windspd*canopyExNeut*canopyStabilityCorrection**2._rkind) ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) ! (compute derivative in NEUTRAL ground resistance w.r.t. canopy air temperature (s m-1 K-1)) - dFV_dT = windspd*canopyExNeut*dCanopyStabilityCorrection_dCasTemp/(sqrt(sfc2AtmExchangeCoeff_canopy)*2._dp) ! d(frictionVelocity)/d(canopy air temperature) + dFV_dT = windspd*canopyExNeut*dCanopyStabilityCorrection_dCasTemp/(sqrt(sfc2AtmExchangeCoeff_canopy)*2._rkind) ! d(frictionVelocity)/d(canopy air temperature) dED_dT = dFV_dT*vkc*(heightCanopyTopAboveSnow - zeroPlaneDisplacement) ! d(eddyDiffusCanopyTop)d(canopy air temperature) - dGR_dT = -dED_dT*(tmp1 - tmp2)*heightCanopyTopAboveSnow*exp(windReductionFactor) / (windReductionFactor*eddyDiffusCanopyTop**2._dp) ! d(groundResistanceNeutral)/d(canopy air temperature) + dGR_dT = -dED_dT*(tmp1 - tmp2)*heightCanopyTopAboveSnow*exp(windReductionFactor) / (windReductionFactor*eddyDiffusCanopyTop**2._rkind) ! d(groundResistanceNeutral)/d(canopy air temperature) ! (stitch everything together -- product rule) - dGroundResistance_dTCanair = dGR_dT/groundStabilityCorrection - groundResistanceNeutral*dGroundStabilityCorrection_dCasTemp/(groundStabilityCorrection**2._dp) + dGroundResistance_dTCanair = dGR_dT/groundStabilityCorrection - groundResistanceNeutral*dGroundStabilityCorrection_dCasTemp/(groundStabilityCorrection**2._rkind) ! ***** compute resistances for non-vegetated surfaces (e.g., snow) else ! set canopy derivatives to zero (non-vegetated, remember) - dCanopyResistance_dTCanopy = 0._dp - dGroundResistance_dTCanopy = 0._dp + dCanopyResistance_dTCanopy = 0._rkind + dGroundResistance_dTCanopy = 0._rkind ! compute derivatives for ground resistance - dGroundResistance_dTGround = -dGroundStabilityCorrection_dSfcTemp/(windspd*groundExNeut*groundStabilityCorrection**2._dp) + dGroundResistance_dTGround = -dGroundStabilityCorrection_dSfcTemp/(windspd*groundExNeut*groundStabilityCorrection**2._rkind) end if ! (switch between vegetated and non-vegetated surfaces) @@ -2396,7 +2396,7 @@ subroutine soilResist(& err=0; message='soilResist/' ! ** compute the factor limiting transpiration for each soil layer (-) - wAvgTranspireLimitFac = 0._dp ! (initialize the weighted average) + wAvgTranspireLimitFac = 0._rkind ! (initialize the weighted average) do iLayer=1,size(mLayerMatricHead) ! compute the soil stress function select case(ixSoilResist) @@ -2404,21 +2404,21 @@ subroutine soilResist(& gx = (mLayerVolFracLiq(iLayer) - critSoilWilting) / (critSoilTranspire - critSoilWilting) case(CLM_Type) ! thresholded linear function of matric head if(mLayerMatricHead(iLayer) > plantWiltPsi)then - gx = 1._dp - mLayerMatricHead(iLayer)/plantWiltPsi + gx = 1._rkind - mLayerMatricHead(iLayer)/plantWiltPsi else - gx = 0._dp + gx = 0._rkind end if case(SiB_Type) ! exponential of the log of matric head - if(mLayerMatricHead(iLayer) < 0._dp)then ! (unsaturated) - gx = 1._dp - exp( -soilStressParam * ( log(plantWiltPsi/mLayerMatricHead(iLayer)) ) ) + if(mLayerMatricHead(iLayer) < 0._rkind)then ! (unsaturated) + gx = 1._rkind - exp( -soilStressParam * ( log(plantWiltPsi/mLayerMatricHead(iLayer)) ) ) else ! (saturated) - gx = 1._dp + gx = 1._rkind end if case default ! check identified the option err=20; message=trim(message)//'cannot identify option for soil resistance'; return end select ! save the factor for the given layer (ensure between zero and one) - mLayerTranspireLimitFac(iLayer) = min( max(verySmall,gx), 1._dp) + mLayerTranspireLimitFac(iLayer) = min( max(verySmall,gx), 1._rkind) ! compute the weighted average (weighted by root density) wAvgTranspireLimitFac = wAvgTranspireLimitFac + mLayerTranspireLimitFac(iLayer)*mLayerRootDensity(iLayer) end do ! (looping through soil layers) @@ -2431,9 +2431,9 @@ subroutine soilResist(& err=20; return end if ! compute the factor limiting evaporation for the aquifer - aquiferTranspireLimitFac = min(scalarAquiferStorage/critAquiferTranspire, 1._dp) + aquiferTranspireLimitFac = min(scalarAquiferStorage/critAquiferTranspire, 1._rkind) else ! (if there are roots in the aquifer) - aquiferTranspireLimitFac = 0._dp + aquiferTranspireLimitFac = 0._rkind end if ! compute the weighted average (weighted by root density) @@ -2689,12 +2689,12 @@ subroutine turbFluxes(& if(computeVegFlux)then leafConductance = exposedVAI/leafResistance leafConductanceTr = canopySunlitLAI/(leafResistance+stomResistSunlit) + canopyShadedLAI/(leafResistance+stomResistShaded) - canopyConductance = 1._dp/canopyResistance + canopyConductance = 1._rkind/canopyResistance else - leafConductance = 0._dp - canopyConductance = 0._dp + leafConductance = 0._rkind + canopyConductance = 0._rkind end if - groundConductanceSH = 1._dp/groundResistance + groundConductanceSH = 1._rkind/groundResistance ! compute total conductance for sensible heat totalConductanceSH = leafConductance + groundConductanceSH + canopyConductance @@ -2702,14 +2702,14 @@ subroutine turbFluxes(& ! compute conductances for latent heat (m s-1) if(computeVegFlux)then evapConductance = canopyWetFraction*leafConductance - transConductance = (1._dp - canopyWetFraction) * leafConductanceTr + transConductance = (1._rkind - canopyWetFraction) * leafConductanceTr !write(*,'(a,10(f14.8,1x))') 'canopySunlitLAI, canopyShadedLAI, stomResistSunlit, stomResistShaded, leafResistance, canopyWetFraction = ', & ! canopySunlitLAI, canopyShadedLAI, stomResistSunlit, stomResistShaded, leafResistance, canopyWetFraction else - evapConductance = 0._dp - transConductance = 0._dp + evapConductance = 0._rkind + transConductance = 0._rkind end if - groundConductanceLH = 1._dp/(groundResistance + soilResistance) ! NOTE: soilResistance accounts for fractional snow, and =0 when snow cover is 100% + groundConductanceLH = 1._rkind/(groundResistance + soilResistance) ! NOTE: soilResistance accounts for fractional snow, and =0 when snow cover is 100% totalConductanceLH = evapConductance + transConductance + groundConductanceLH + canopyConductance ! check sensible heat conductance @@ -2732,30 +2732,30 @@ subroutine turbFluxes(& if(computeVegFlux)then dEvapCond_dCanopyTemp = dCanopyWetFraction_dT*leafConductance ! derivative in evap conductance w.r.t. canopy temperature dTransCond_dCanopyTemp = -dCanopyWetFraction_dT*leafConductanceTr ! derivative in trans conductance w.r.t. canopy temperature - dCanopyCond_dCanairTemp = -dCanopyResistance_dTCanair/canopyResistance**2._dp ! derivative in canopy conductance w.r.t. canopy air emperature - dCanopyCond_dCanopyTemp = -dCanopyResistance_dTCanopy/canopyResistance**2._dp ! derivative in canopy conductance w.r.t. canopy temperature - dGroundCondSH_dCanairTemp = -dGroundResistance_dTCanair/groundResistance**2._dp ! derivative in ground conductance w.r.t. canopy air temperature - dGroundCondSH_dCanopyTemp = -dGroundResistance_dTCanopy/groundResistance**2._dp ! derivative in ground conductance w.r.t. canopy temperature - dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2._dp ! derivative in ground conductance w.r.t. ground temperature + dCanopyCond_dCanairTemp = -dCanopyResistance_dTCanair/canopyResistance**2._rkind ! derivative in canopy conductance w.r.t. canopy air emperature + dCanopyCond_dCanopyTemp = -dCanopyResistance_dTCanopy/canopyResistance**2._rkind ! derivative in canopy conductance w.r.t. canopy temperature + dGroundCondSH_dCanairTemp = -dGroundResistance_dTCanair/groundResistance**2._rkind ! derivative in ground conductance w.r.t. canopy air temperature + dGroundCondSH_dCanopyTemp = -dGroundResistance_dTCanopy/groundResistance**2._rkind ! derivative in ground conductance w.r.t. canopy temperature + dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2._rkind ! derivative in ground conductance w.r.t. ground temperature else - dEvapCond_dCanopyTemp = 0._dp ! derivative in evap conductance w.r.t. canopy temperature - dTransCond_dCanopyTemp = 0._dp ! derivative in trans conductance w.r.t. canopy temperature - dCanopyCond_dCanairTemp = 0._dp ! derivative in canopy conductance w.r.t. canopy air emperature - dCanopyCond_dCanopyTemp = 0._dp ! derivative in canopy conductance w.r.t. canopy temperature - dGroundCondSH_dCanairTemp = 0._dp ! derivative in ground conductance w.r.t. canopy air temperature - dGroundCondSH_dCanopyTemp = 0._dp ! derivative in ground conductance w.r.t. canopy temperature - dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2._dp ! derivative in ground conductance w.r.t. ground temperature + dEvapCond_dCanopyTemp = 0._rkind ! derivative in evap conductance w.r.t. canopy temperature + dTransCond_dCanopyTemp = 0._rkind ! derivative in trans conductance w.r.t. canopy temperature + dCanopyCond_dCanairTemp = 0._rkind ! derivative in canopy conductance w.r.t. canopy air emperature + dCanopyCond_dCanopyTemp = 0._rkind ! derivative in canopy conductance w.r.t. canopy temperature + dGroundCondSH_dCanairTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy air temperature + dGroundCondSH_dCanopyTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy temperature + dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2._rkind ! derivative in ground conductance w.r.t. ground temperature end if ! compute derivatives in individual conductances for latent heat w.r.t. canopy temperature (m s-1 K-1) if(computeVegFlux)then - dGroundCondLH_dCanairTemp = -dGroundResistance_dTCanair/(groundResistance+soilResistance)**2._dp ! derivative in ground conductance w.r.t. canopy air temperature - dGroundCondLH_dCanopyTemp = -dGroundResistance_dTCanopy/(groundResistance+soilResistance)**2._dp ! derivative in ground conductance w.r.t. canopy temperature - dGroundCondLH_dGroundTemp = -dGroundResistance_dTGround/(groundResistance+soilResistance)**2._dp ! derivative in ground conductance w.r.t. ground temperature + dGroundCondLH_dCanairTemp = -dGroundResistance_dTCanair/(groundResistance+soilResistance)**2._rkind ! derivative in ground conductance w.r.t. canopy air temperature + dGroundCondLH_dCanopyTemp = -dGroundResistance_dTCanopy/(groundResistance+soilResistance)**2._rkind ! derivative in ground conductance w.r.t. canopy temperature + dGroundCondLH_dGroundTemp = -dGroundResistance_dTGround/(groundResistance+soilResistance)**2._rkind ! derivative in ground conductance w.r.t. ground temperature else - dGroundCondLH_dCanairTemp = 0._dp ! derivative in ground conductance w.r.t. canopy air temperature - dGroundCondLH_dCanopyTemp = 0._dp ! derivative in ground conductance w.r.t. canopy temperature - dGroundCondLH_dGroundTemp = -dGroundResistance_dTGround/(groundResistance+soilResistance)**2._dp ! derivative in ground conductance w.r.t. ground temperature + dGroundCondLH_dCanairTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy air temperature + dGroundCondLH_dCanopyTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy temperature + dGroundCondLH_dGroundTemp = -dGroundResistance_dTGround/(groundResistance+soilResistance)**2._rkind ! derivative in ground conductance w.r.t. ground temperature end if end if ! (if computing analytical derivatives) @@ -2799,9 +2799,9 @@ subroutine turbFluxes(& ! * no vegetation, so fluxes are zero else - senHeatCanopy = 0._dp - latHeatCanopyEvap = 0._dp - latHeatCanopyTrans = 0._dp + senHeatCanopy = 0._rkind + latHeatCanopyEvap = 0._rkind + latHeatCanopyTrans = 0._rkind end if ! compute sensible and latent heat fluxes from the ground to the canopy air space (W m-2) @@ -2828,20 +2828,20 @@ subroutine turbFluxes(& ! compute derivatives of vapor pressure in the canopy air space w.r.t. all state variables ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the canopy air space) dPart1 = dCanopyCond_dCanairTemp*VPair + dGroundCondLH_dCanairTemp*satVP_GroundTemp*soilRelHumidity - dPart2 = -(dCanopyCond_dCanairTemp + dGroundCondLH_dCanairTemp)/(totalConductanceLH**2._dp) + dPart2 = -(dCanopyCond_dCanairTemp + dGroundCondLH_dCanairTemp)/(totalConductanceLH**2._rkind) dVPCanopyAir_dTCanair = dPart1/totalConductanceLH + fPart_VP*dPart2 ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the canopy) dPart0 = (evapConductance + transConductance)*dSVPCanopy_dCanopyTemp + (dEvapCond_dCanopyTemp + dTransCond_dCanopyTemp)*satVP_CanopyTemp dPart1 = dCanopyCond_dCanopyTemp*VPair + dPart0 + dGroundCondLH_dCanopyTemp*satVP_GroundTemp*soilRelHumidity - dPart2 = -(dCanopyCond_dCanopyTemp + dEvapCond_dCanopyTemp + dTransCond_dCanopyTemp + dGroundCondLH_dCanopyTemp)/(totalConductanceLH**2._dp) + dPart2 = -(dCanopyCond_dCanopyTemp + dEvapCond_dCanopyTemp + dTransCond_dCanopyTemp + dGroundCondLH_dCanopyTemp)/(totalConductanceLH**2._rkind) dVPCanopyAir_dTCanopy = dPart1/totalConductanceLH + fPart_VP*dPart2 ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the ground) dPart1 = dGroundCondLH_dGroundTemp*satVP_GroundTemp*soilRelHumidity + groundConductanceLH*dSVPGround_dGroundTemp*soilRelHumidity - dPart2 = -dGroundCondLH_dGroundTemp/(totalConductanceLH**2._dp) + dPart2 = -dGroundCondLH_dGroundTemp/(totalConductanceLH**2._rkind) dVPCanopyAir_dTGround = dPart1/totalConductanceLH + fPart_VP*dPart2 ! (derivative of vapor pressure in the canopy air space w.r.t. wetted fraction of the canopy) dPart1 = (leafConductance - leafConductanceTr)*satVP_CanopyTemp - dPart2 = -(leafConductance - leafConductanceTr)/(totalConductanceLH**2._dp) + dPart2 = -(leafConductance - leafConductanceTr)/(totalConductanceLH**2._rkind) dVPCanopyAir_dWetFrac = dPart1/totalConductanceLH + fPart_VP*dPart2 dVPCanopyAir_dCanLiq = dVPCanopyAir_dWetFrac*dCanopyWetFraction_dWat !write(*,'(a,5(f20.8,1x))') 'dVPCanopyAir_dTCanair, dVPCanopyAir_dTCanopy, dVPCanopyAir_dTGround, dVPCanopyAir_dWetFrac, dVPCanopyAir_dCanLiq = ', & @@ -2850,14 +2850,14 @@ subroutine turbFluxes(& ! sensible heat from the canopy to the atmosphere dSenHeatTotal_dTCanair = -volHeatCapacityAir*canopyConductance - volHeatCapacityAir*dCanopyCond_dCanairTemp*(canairTemp - airtemp) dSenHeatTotal_dTCanopy = -volHeatCapacityAir*dCanopyCond_dCanopyTemp*(canairTemp - airtemp) - dSenHeatTotal_dTGround = 0._dp + dSenHeatTotal_dTGround = 0._rkind !write(*,'(a,3(f20.8,1x))') 'dSenHeatTotal_dTCanair, dSenHeatTotal_dTCanopy, dSenHeatTotal_dTGround = ', & ! dSenHeatTotal_dTCanair, dSenHeatTotal_dTCanopy, dSenHeatTotal_dTGround ! sensible heat from the canopy to the canopy air space dSenHeatCanopy_dTCanair = volHeatCapacityAir*leafConductance dSenHeatCanopy_dTCanopy = -volHeatCapacityAir*leafConductance - dSenHeatCanopy_dTGround = 0._dp + dSenHeatCanopy_dTGround = 0._rkind !write(*,'(a,3(f20.8,1x))') 'dSenHeatCanopy_dTCanair, dSenHeatCanopy_dTCanopy, dSenHeatCanopy_dTGround = ', & ! dSenHeatCanopy_dTCanair, dSenHeatCanopy_dTCanopy, dSenHeatCanopy_dTGround @@ -2908,7 +2908,7 @@ subroutine turbFluxes(& ! latent heat associated with canopy transpiration w.r.t. wetted fraction of the canopy dPart1 = LH_vap*latentHeatConstant*leafConductanceTr ! NOTE: positive, since (1 - wetFrac) - fPart1 = -dPart1*(1._dp - canopyWetFraction) + fPart1 = -dPart1*(1._rkind - canopyWetFraction) dLatHeatCanopyTrans_dWetFrac = dPart1*(satVP_CanopyTemp - VP_CanopyAir) + fPart1*(-dVPCanopyAir_dWetFrac) !print*, 'dLatHeatCanopyTrans_dWetFrac = ', dLatHeatCanopyTrans_dWetFrac @@ -2919,30 +2919,30 @@ subroutine turbFluxes(& else ! canopy is undefined ! set derivatives for canopy fluxes to zero (no canopy, so fluxes are undefined) - dSenHeatTotal_dTCanair = 0._dp - dSenHeatTotal_dTCanopy = 0._dp - dSenHeatTotal_dTGround = 0._dp - dSenHeatCanopy_dTCanair = 0._dp - dSenHeatCanopy_dTCanopy = 0._dp - dSenHeatCanopy_dTGround = 0._dp - dLatHeatCanopyEvap_dTCanair = 0._dp - dLatHeatCanopyEvap_dTCanopy = 0._dp - dLatHeatCanopyEvap_dTGround = 0._dp - dLatHeatCanopyTrans_dTCanair = 0._dp - dLatHeatCanopyTrans_dTCanopy = 0._dp - dLatHeatCanopyTrans_dTGround = 0._dp + dSenHeatTotal_dTCanair = 0._rkind + dSenHeatTotal_dTCanopy = 0._rkind + dSenHeatTotal_dTGround = 0._rkind + dSenHeatCanopy_dTCanair = 0._rkind + dSenHeatCanopy_dTCanopy = 0._rkind + dSenHeatCanopy_dTGround = 0._rkind + dLatHeatCanopyEvap_dTCanair = 0._rkind + dLatHeatCanopyEvap_dTCanopy = 0._rkind + dLatHeatCanopyEvap_dTGround = 0._rkind + dLatHeatCanopyTrans_dTCanair = 0._rkind + dLatHeatCanopyTrans_dTCanopy = 0._rkind + dLatHeatCanopyTrans_dTGround = 0._rkind ! set derivatives for wetted area and canopy transpiration to zero (no canopy, so fluxes are undefined) - dLatHeatCanopyEvap_dWetFrac = 0._dp - dLatHeatCanopyEvap_dCanLiq = 0._dp - dLatHeatCanopyTrans_dCanLiq = 0._dp - dVPCanopyAir_dCanLiq = 0._dp + dLatHeatCanopyEvap_dWetFrac = 0._rkind + dLatHeatCanopyEvap_dCanLiq = 0._rkind + dLatHeatCanopyTrans_dCanLiq = 0._rkind + dVPCanopyAir_dCanLiq = 0._rkind ! set derivatives for ground fluxes w.r.t canopy temperature to zero (no canopy, so fluxes are undefined) - dSenHeatGround_dTCanair = 0._dp - dSenHeatGround_dTCanopy = 0._dp - dLatHeatGroundEvap_dTCanair = 0._dp - dLatHeatGroundEvap_dTCanopy = 0._dp + dSenHeatGround_dTCanair = 0._rkind + dSenHeatGround_dTCanopy = 0._rkind + dLatHeatGroundEvap_dTCanair = 0._rkind + dLatHeatGroundEvap_dTCanopy = 0._rkind ! compute derivatives for the ground fluxes w.r.t. ground temperature dSenHeatGround_dTGround = (-volHeatCapacityAir*dGroundCondSH_dGroundTemp)*(groundTemp - airtemp) + & ! d(ground sensible heat flux)/d(ground temp) @@ -2983,27 +2983,27 @@ subroutine turbFluxes(& dLatHeatCanopyEvap_dCanLiq = dLatHeatCanopyEvap_dWetFrac*dCanopyWetFraction_dWat ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water (W kg-1) dLatHeatGroundEvap_dCanLiq = latHeatSubVapGround*latentHeatConstant*groundConductanceLH*dVPCanopyAir_dCanLiq ! derivative in latent heat of ground evaporation w.r.t. canopy liquid water (J kg-1 s-1) ! (cross deriavtives) - dTurbFluxCanair_dCanLiq = 0._dp ! derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dTurbFluxCanair_dCanLiq = 0._rkind ! derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) dTurbFluxCanopy_dCanLiq = dLatHeatCanopyEvap_dCanLiq + dLatHeatCanopyTrans_dCanLiq ! derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) dTurbFluxGround_dCanLiq = dLatHeatGroundEvap_dCanLiq ! derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) else ! (just make sure we return something) ! (energy derivatives) - dTurbFluxCanair_dTCanair = 0._dp - dTurbFluxCanair_dTCanopy = 0._dp - dTurbFluxCanair_dTGround = 0._dp - dTurbFluxCanopy_dTCanair = 0._dp - dTurbFluxCanopy_dTCanopy = 0._dp - dTurbFluxCanopy_dTGround = 0._dp - dTurbFluxGround_dTCanair = 0._dp - dTurbFluxGround_dTCanopy = 0._dp - dTurbFluxGround_dTGround = 0._dp + dTurbFluxCanair_dTCanair = 0._rkind + dTurbFluxCanair_dTCanopy = 0._rkind + dTurbFluxCanair_dTGround = 0._rkind + dTurbFluxCanopy_dTCanair = 0._rkind + dTurbFluxCanopy_dTCanopy = 0._rkind + dTurbFluxCanopy_dTGround = 0._rkind + dTurbFluxGround_dTCanair = 0._rkind + dTurbFluxGround_dTCanopy = 0._rkind + dTurbFluxGround_dTGround = 0._rkind ! (liquid water derivatives) - dLatHeatCanopyEvap_dCanLiq = 0._dp - dLatHeatGroundEvap_dCanLiq = 0._dp + dLatHeatCanopyEvap_dCanLiq = 0._rkind + dLatHeatGroundEvap_dCanLiq = 0._rkind ! (cross deriavtives) - dTurbFluxCanair_dCanLiq = 0._dp - dTurbFluxCanopy_dCanLiq = 0._dp - dTurbFluxGround_dCanLiq = 0._dp + dTurbFluxCanair_dCanLiq = 0._rkind + dTurbFluxCanopy_dCanLiq = 0._rkind + dTurbFluxGround_dCanLiq = 0._rkind end if end subroutine turbFluxes @@ -3054,7 +3054,7 @@ subroutine aStability(& integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local - real(dp), parameter :: verySmall=1.e-10_dp ! a very small number (avoid stability of zero) + real(dp), parameter :: verySmall=1.e-10_rkind ! a very small number (avoid stability of zero) real(dp) :: dRiBulk_dAirTemp ! derivative in the bulk Richardson number w.r.t. air temperature (K-1) real(dp) :: dRiBulk_dSfcTemp ! derivative in the bulk Richardson number w.r.t. surface temperature (K-1) real(dp) :: bPrime ! scaled "b" parameter for stability calculations in Louis (1979) @@ -3078,18 +3078,18 @@ subroutine aStability(& ! set derivative to one if not computing it if(.not.computeDerivative)then - dStabilityCorrection_dRich = 1._dp - dStabilityCorrection_dAirTemp = 1._dp - dStabilityCorrection_dSfcTemp = 1._dp + dStabilityCorrection_dRich = 1._rkind + dStabilityCorrection_dAirTemp = 1._rkind + dStabilityCorrection_dSfcTemp = 1._rkind end if ! ***** process unstable cases - if(RiBulk<0._dp)then + if(RiBulk<0._rkind)then ! compute surface-atmosphere exchange coefficient (-) - stabilityCorrection = (1._dp - 16._dp*RiBulk)**0.5_dp + stabilityCorrection = (1._rkind - 16._rkind*RiBulk)**0.5_rkind ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) if(computeDerivative)then - dStabilityCorrection_dRich = (-16._dp) * 0.5_dp*(1._dp - 16._dp*RiBulk)**(-0.5_dp) + dStabilityCorrection_dRich = (-16._rkind) * 0.5_rkind*(1._rkind - 16._rkind*RiBulk)**(-0.5_rkind) dStabilityCorrection_dAirTemp = dRiBulk_dAirTemp * dStabilityCorrection_dRich dStabilityCorrection_dSfcTemp = dRiBulk_dSfcTemp * dStabilityCorrection_dRich end if @@ -3102,24 +3102,24 @@ subroutine aStability(& ! ("standard" stability correction, a la Anderson 1976) case(standard) ! compute surface-atmosphere exchange coefficient (-) - if(RiBulk < critRichNumber) stabilityCorrection = (1._dp - 5._dp*RiBulk)**2._dp + if(RiBulk < critRichNumber) stabilityCorrection = (1._rkind - 5._rkind*RiBulk)**2._rkind if(RiBulk >= critRichNumber) stabilityCorrection = verySmall ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) if(computeDerivative)then - if(RiBulk < critRichNumber) dStabilityCorrection_dRich = (-5._dp) * 2._dp*(1._dp - 5._dp*RiBulk) + if(RiBulk < critRichNumber) dStabilityCorrection_dRich = (-5._rkind) * 2._rkind*(1._rkind - 5._rkind*RiBulk) if(RiBulk >= critRichNumber) dStabilityCorrection_dRich = verySmall end if ! (Louis 1979) case(louisInversePower) ! scale the "b" parameter for stable conditions - bprime = Louis79_bparam/2._dp + bprime = Louis79_bparam/2._rkind ! compute surface-atmosphere exchange coefficient (-) - stabilityCorrection = 1._dp / ( (1._dp + bprime*RiBulk)**2._dp ) + stabilityCorrection = 1._rkind / ( (1._rkind + bprime*RiBulk)**2._rkind ) if(stabilityCorrection < epsilon(stabilityCorrection)) stabilityCorrection = epsilon(stabilityCorrection) ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) if(computeDerivative)then - dStabilityCorrection_dRich = bprime * (-2._dp)*(1._dp + bprime*RiBulk)**(-3._dp) + dStabilityCorrection_dRich = bprime * (-2._rkind)*(1._rkind + bprime*RiBulk)**(-3._rkind) end if ! (Mahrt 1987) @@ -3184,17 +3184,17 @@ subroutine bulkRichardson(& err=0; message='bulkRichardson/' ! compute local variables T_grad = airtemp - sfcTemp - T_mean = 0.5_dp*(airtemp + sfcTemp) + T_mean = 0.5_rkind*(airtemp + sfcTemp) RiMult = (gravity*mHeight)/(windspd*windspd) ! compute the Richardson number RiBulk = (T_grad/T_mean) * RiMult ! compute the derivative in the Richardson number if(computeDerivative)then - dRiBulk_dAirTemp = RiMult/T_mean - RiMult*T_grad/(0.5_dp*((airtemp + sfcTemp)**2._dp)) - dRiBulk_dSfcTemp = -RiMult/T_mean - RiMult*T_grad/(0.5_dp*((airtemp + sfcTemp)**2._dp)) + dRiBulk_dAirTemp = RiMult/T_mean - RiMult*T_grad/(0.5_rkind*((airtemp + sfcTemp)**2._rkind)) + dRiBulk_dSfcTemp = -RiMult/T_mean - RiMult*T_grad/(0.5_rkind*((airtemp + sfcTemp)**2._rkind)) else - dRiBulk_dAirTemp = 1._dp - dRiBulk_dSfcTemp = 1._dp + dRiBulk_dAirTemp = 1._rkind + dRiBulk_dSfcTemp = 1._rkind end if end subroutine bulkRichardson From 6c3bc1733282b206353b7adfa8971cb842a22e05 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 29 Jun 2021 23:01:15 -0600 Subject: [PATCH 0151/1472] dp to rkind --- build/source/dshare/data_types.f90 | 2 +- build/source/engine/allocspace.f90 | 14 +- build/source/engine/bigAquifer.f90 | 2 +- build/source/engine/canopySnow.f90 | 4 +- build/source/engine/computEnthalpy.f90 | 28 +- build/source/engine/computFlux.f90 | 4 +- build/source/engine/computFluxFida.f90 | 54 +- build/source/engine/computHeatCap.f90 | 56 +- build/source/engine/computJacob.f90 | 2 +- build/source/engine/computJacobFida.f90 | 46 +- build/source/engine/computJacobFidaCpVar.f90 | 56 +- build/source/engine/computResid.f90 | 2 +- build/source/engine/computResidFida.f90 | 34 +- build/source/engine/computSnowDepth.f90 | 22 +- build/source/engine/computThermConduct.f90 | 60 +- build/source/engine/convE2Temp.f90 | 2 +- build/source/engine/coupled_em.f90 | 6 +- build/source/engine/derivforce.f90 | 2 +- build/source/engine/diagn_evar.f90 | 4 +- build/source/engine/eval8FidaJac.f90 | 78 +- build/source/engine/eval8summa.f90 | 6 +- build/source/engine/eval8summaFida.f90 | 104 +-- build/source/engine/evalEqnsFida.f90 | 12 +- build/source/engine/evalJacFida.f90 | 16 +- build/source/engine/fidaSolver.f90 | 56 +- build/source/engine/fida_datatypes.f90 | 64 +- build/source/engine/findDiscontinuity.f90 | 10 +- build/source/engine/getVectorz.f90 | 4 +- build/source/engine/groundwatr.f90 | 4 +- build/source/engine/layerDivide.f90 | 14 +- build/source/engine/layerDivideFida.f90 | 28 +- build/source/engine/layerMerge.f90 | 8 +- .../source/engine/left_overs/convTestFida.f90 | 236 ----- build/source/engine/opSplittin.f90 | 6 +- build/source/engine/paramCheck.f90 | 2 +- build/source/engine/printResidFida.f90 | 4 +- build/source/engine/read_attrb.f90 | 2 +- build/source/engine/read_force.f90 | 2 +- build/source/engine/run_oneGRU.f90 | 6 +- build/source/engine/run_oneHRU.f90 | 6 +- build/source/engine/snowAlbedo.f90 | 4 +- build/source/engine/snowLiqFlx.f90 | 28 +- build/source/engine/soilCmpresFida.f90 | 18 +- build/source/engine/soilLiqFlx.f90 | 4 +- build/source/engine/soil_utils_fida.f90 | 78 +- build/source/engine/ssdNrgFlux.f90 | 4 +- build/source/engine/stomResist.f90 | 4 +- build/source/engine/summaSolve.f90 | 6 +- build/source/engine/sysSolvFida.f90 | 56 +- build/source/engine/systemSolv.f90 | 6 +- build/source/engine/t2enthalpy.f90 | 140 +-- build/source/engine/tempAdjust.f90 | 4 +- build/source/engine/tolFida.f90 | 40 +- build/source/engine/updatStateFida.f90 | 120 +-- build/source/engine/updateVars.f90 | 6 +- build/source/engine/updateVarsFida.f90 | 86 +- build/source/engine/updateVarsFida2.f90 | 108 +-- build/source/engine/varExtrFida.f90 | 52 +- build/source/engine/varSubstep.f90 | 6 +- build/source/engine/varSubstepFida.f90 | 132 +-- build/source/engine/var_derive.f90 | 2 +- build/source/engine/vegLiqFlux.f90 | 4 +- build/source/engine/vegNrgFlux.f90 | 4 +- build/source/engine/vegNrgFluxFida.f90 | 834 +++++++++--------- build/source/engine/vegPhenlgy.f90 | 4 +- build/source/engine/vegSWavRad.f90 | 2 +- build/source/engine/volicePack.f90 | 4 +- build/source/engine/volicePackFida.f90 | 4 +- 68 files changed, 1296 insertions(+), 1532 deletions(-) delete mode 100644 build/source/engine/left_overs/convTestFida.f90 diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 3c49491ee..4d65e180f 100755 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -130,7 +130,7 @@ MODULE data_types ! define derived types to hold look-up tables for each soil layer ! ** double precision type type, public :: dLookup - real(dp),allocatable :: lookup(:) ! lookup(:) + real(rkind),allocatable :: lookup(:) ! lookup(:) endtype dLookup ! ** double precision type for a variable number of soil layers; variable length type, public :: vLookup diff --git a/build/source/engine/allocspace.f90 b/build/source/engine/allocspace.f90 index 5ad926035..3c35318fa 100644 --- a/build/source/engine/allocspace.f90 +++ b/build/source/engine/allocspace.f90 @@ -31,24 +31,24 @@ module allocspace_module ! no spatial dimension var_i, & ! x%var(:) (i4b) var_i8, & ! x%var(:) integer(8) - var_d, & ! x%var(:) (dp) + var_d, & ! x%var(:) (rkind) var_flagVec, & ! x%var(:)%dat (logical) var_ilength, & ! x%var(:)%dat (i4b) - var_dlength, & ! x%var(:)%dat (dp) + var_dlength, & ! x%var(:)%dat (rkind) ! gru dimension gru_int, & ! x%gru(:)%var(:) (i4b) gru_int8, & ! x%gru(:)%var(:) integer(8) - gru_double, & ! x%gru(:)%var(:) (dp) + gru_double, & ! x%gru(:)%var(:) (rkind) gru_intVec, & ! x%gru(:)%var(:)%dat (i4b) - gru_doubleVec, & ! x%gru(:)%var(:)%dat (dp) + gru_doubleVec, & ! x%gru(:)%var(:)%dat (rkind) ! gru+hru dimension gru_hru_int, & ! x%gru(:)%hru(:)%var(:) (i4b) gru_hru_int8, & ! x%gru(:)%hru(:)%var(:) integer(8) - gru_hru_double, & ! x%gru(:)%hru(:)%var(:) (dp) + gru_hru_double, & ! x%gru(:)%hru(:)%var(:) (rkind) gru_hru_intVec, & ! x%gru(:)%hru(:)%var(:)%dat (i4b) - gru_hru_doubleVec, & ! x%gru(:)%hru(:)%var(:)%dat (dp) + gru_hru_doubleVec, & ! x%gru(:)%hru(:)%var(:)%dat (rkind) ! gru+hru+z dimension - gru_hru_z_vLookup ! x%gru(:)%hru(:)%z(:)%var(:)%lookup (dp) + gru_hru_z_vLookup ! x%gru(:)%hru(:)%z(:)%var(:)%lookup (rkind) ! metadata structure USE data_types,only:var_info ! data type for metadata diff --git a/build/source/engine/bigAquifer.f90 b/build/source/engine/bigAquifer.f90 index 5b5ed0190..4a6e322c4 100644 --- a/build/source/engine/bigAquifer.f90 +++ b/build/source/engine/bigAquifer.f90 @@ -62,7 +62,7 @@ subroutine bigAquifer(& USE var_lookup,only:iLookDIAG ! named variables for structure elements USE var_lookup,only:iLookPARAM ! named variables for structure elements ! data types - USE data_types,only:var_dlength ! x%var(:)%dat (dp) + USE data_types,only:var_dlength ! x%var(:)%dat (rkind) ! ------------------------------------------------------------------------------------------------------------------------------------------------- implicit none ! input: state variables, fluxes, and parameters diff --git a/build/source/engine/canopySnow.f90 b/build/source/engine/canopySnow.f90 index 930227c9c..d3ea510fc 100644 --- a/build/source/engine/canopySnow.f90 +++ b/build/source/engine/canopySnow.f90 @@ -26,8 +26,8 @@ module canopySnow_module ! derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) - var_dlength, & ! data vector with variable length dimension (dp) + var_d, & ! data vector (rkind) + var_dlength, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions ! physical constants diff --git a/build/source/engine/computEnthalpy.f90 b/build/source/engine/computEnthalpy.f90 index 6feae444c..ba216ad2e 100644 --- a/build/source/engine/computEnthalpy.f90 +++ b/build/source/engine/computEnthalpy.f90 @@ -7,7 +7,7 @@ module computEnthalpy_module ! derived types to define the data structures USE data_types,only:& var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (dp) + var_dlength ! data vector with variable length dimension (rkind) ! named variables USE var_lookup,only:iLookPROG ! named variables for structure elements @@ -63,10 +63,10 @@ subroutine computEnthalpy(& ! input: model control type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers integer(i4b),intent(in) :: nLayers ! number of snow layers - real(dp),intent(in) :: mLayerTemp(:) ! temperature of each snow/soil layer (K) - real(dp),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice (-) - real(dp),intent(in) :: mLayerHeatCap(:) - real(dp),intent(out) :: mLayerEnthalpy(:) + real(rkind),intent(in) :: mLayerTemp(:) ! temperature of each snow/soil layer (K) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice (-) + real(rkind),intent(in) :: mLayerHeatCap(:) + real(rkind),intent(out) :: mLayerEnthalpy(:) ! local variables integer(i4b) :: iLayer @@ -116,15 +116,15 @@ subroutine computEnthalpyPrime(& logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers integer(i4b),intent(in) :: nLayers ! number of snow layers - real(dp),intent(in) :: canopyDepth ! canopy depth (m) - real(dp),intent(in) :: scalarCanopyTempPrime ! Prime value for the temperature of the vegetation canopy (K) - real(dp),intent(in) :: scalarCanopyIcePrime ! Prime value for the ice on the vegetation canopy (kg m-2) - real(dp),intent(in) :: heatCapVeg - real(dp),intent(in) :: mLayerTempPrime(:) ! temperature of each snow/soil layer (K) - real(dp),intent(in) :: mLayerVolFracIcePrime(:) ! volumetric fraction of ice (-) - real(dp),intent(in) :: mLayerHeatCap(:) - real(dp),intent(out) :: scalarCanopyEnthalpyPrime - real(dp),intent(out) :: mLayerEnthalpyPrime(:) + real(rkind),intent(in) :: canopyDepth ! canopy depth (m) + real(rkind),intent(in) :: scalarCanopyTempPrime ! Prime value for the temperature of the vegetation canopy (K) + real(rkind),intent(in) :: scalarCanopyIcePrime ! Prime value for the ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: heatCapVeg + real(rkind),intent(in) :: mLayerTempPrime(:) ! temperature of each snow/soil layer (K) + real(rkind),intent(in) :: mLayerVolFracIcePrime(:) ! volumetric fraction of ice (-) + real(rkind),intent(in) :: mLayerHeatCap(:) + real(rkind),intent(out) :: scalarCanopyEnthalpyPrime + real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! local variables integer(i4b) :: iLayer diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index bf683a2ee..b99f1ab1e 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -26,9 +26,9 @@ module computFlux_module ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions ! indices that define elements of the data structures diff --git a/build/source/engine/computFluxFida.f90 b/build/source/engine/computFluxFida.f90 index fb9ff7132..242335001 100644 --- a/build/source/engine/computFluxFida.f90 +++ b/build/source/engine/computFluxFida.f90 @@ -26,9 +26,9 @@ module computFluxFida_module ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions ! indices that define elements of the data structures @@ -168,22 +168,22 @@ subroutine computFluxFida(& logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - real(dp),intent(in) :: drainageMeltPond ! drainage from the surface melt pond (kg m-2 s-1) + real(rkind),intent(in) :: drainageMeltPond ! drainage from the surface melt pond (kg m-2 s-1) ! input: state variables - real(dp),intent(in) :: scalarAquiferStorage - real(dp),intent(in) :: mLayerVolFracIce(:) ! - real(dp),intent(in) :: mLayerVolFracLiq(:) ! - real(dp),intent(in) :: mLayerMatricHead(:) ! - real(dp),intent(in) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(dp),intent(in) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(dp),intent(in) :: mLayerTempTrial(:) ! trial value for temperature of each snow/soil layer (K) - real(dp),intent(in) :: mLayerMatricHeadLiqTrial(:) ! trial value for the liquid water matric potential (m) - real(dp),intent(in) :: scalarAquiferStorageTrial ! trial value of aquifer storage (m) + real(rkind),intent(in) :: scalarAquiferStorage + real(rkind),intent(in) :: mLayerVolFracIce(:) ! + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! + real(rkind),intent(in) :: mLayerMatricHead(:) ! + real(rkind),intent(in) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind),intent(in) :: mLayerTempTrial(:) ! trial value for temperature of each snow/soil layer (K) + real(rkind),intent(in) :: mLayerMatricHeadLiqTrial(:) ! trial value for the liquid water matric potential (m) + real(rkind),intent(in) :: scalarAquiferStorageTrial ! trial value of aquifer storage (m) ! input: diagnostic variables - real(dp),intent(in) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(dp),intent(in) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(dp),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) - real(dp),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) + real(rkind),intent(in) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) ! input: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions type(var_i), intent(in) :: type_data ! type of vegetation and soil @@ -199,8 +199,8 @@ subroutine computFluxFida(& type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! input-output: flux vector and baseflow derivatives integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(dp),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - real(dp),intent(out) :: fluxVec(:) ! model flux vector (mixed units) + real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + real(rkind),intent(out) :: fluxVec(:) ! model flux vector (mixed units) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -210,7 +210,7 @@ subroutine computFluxFida(& integer(i4b) :: local_ixGroundwater ! local index for groundwater representation integer(i4b) :: iLayer ! index of model layers logical(lgt) :: doVegNrgFlux ! flag to compute the energy flux over vegetation - real(dp),dimension(nSoil) :: dHydCond_dMatric ! derivative in hydraulic conductivity w.r.t matric head (s-1) + real(rkind),dimension(nSoil) :: dHydCond_dMatric ! derivative in hydraulic conductivity w.r.t matric head (s-1) character(LEN=256) :: cmessage ! error message of downwind routine ! -------------------------------------------------------------- ! initialize error control @@ -881,15 +881,15 @@ subroutine soilCmpres(& ! input: integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation integer(i4b),intent(in) :: ixBeg,ixEnd ! start and end indices defining desired layers - real(dp),intent(in) :: mLayerMatricHead(:) ! matric head at the start of the time step (m) - real(dp),intent(in) :: mLayerMatricHeadTrial(:) ! trial value for matric head (m) - real(dp),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) - real(dp),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) - real(dp),intent(in) :: specificStorage ! specific storage coefficient (m-1) - real(dp),intent(in) :: theta_sat(:) ! soil porosity (-) + real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head at the start of the time step (m) + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial value for matric head (m) + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) + real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) + real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) ! output: - real(dp),intent(inout) :: compress(:) ! soil compressibility (-) - real(dp),intent(inout) :: dCompress_dPsi(:) ! derivative in soil compressibility w.r.t. matric head (m-1) + real(rkind),intent(inout) :: compress(:) ! soil compressibility (-) + real(rkind),intent(inout) :: dCompress_dPsi(:) ! derivative in soil compressibility w.r.t. matric head (m-1) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local variables diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index cdc837e31..04bf46225 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -25,9 +25,9 @@ module computHeatCap_module ! derived types to define the data structures USE data_types,only:& - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (dp) + var_dlength ! data vector with variable length dimension (rkind) ! named variables defining elements in the data structures USE var_lookup,only:iLookPARAM,iLookDIAG,iLookINDEX ! named variables for structure elements @@ -107,25 +107,25 @@ subroutine computHeatCap(& ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: control variables logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - real(dp),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) + real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) ! input/output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model layer indices ! input: integer(i4b),intent(in) :: nLayers type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - real(dp),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) - real(dp),intent(in) :: scalarCanopyLiquid - real(dp),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature - real(dp),intent(in) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) - real(dp),intent(in) :: scalarCanopyEnthalpyPrev ! intent(in): previous enthalpy of the vegetation canopy (J m-3) - real(dp),intent(in) :: scalarCanopyTempPrev ! Previous value of canopy temperature - real(dp),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) - real(dp),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) - real(dp),intent(in) :: mLayerTempTrial(:) - real(dp),intent(in) :: mLayerTempPrev(:) - real(dp),intent(in) :: mLayerEnthalpyTrial(:) - real(dp),intent(in) :: mLayerEnthalpyPrev(:) + real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) + real(rkind),intent(in) :: scalarCanopyLiquid + real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature + real(rkind),intent(in) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(in) :: scalarCanopyEnthalpyPrev ! intent(in): previous enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(in) :: scalarCanopyTempPrev ! Previous value of canopy temperature + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) + real(rkind),intent(in) :: mLayerTempTrial(:) + real(rkind),intent(in) :: mLayerTempPrev(:) + real(rkind),intent(in) :: mLayerEnthalpyTrial(:) + real(rkind),intent(in) :: mLayerEnthalpyPrev(:) ! output: real(qp),intent(out) :: heatCapVeg real(qp),intent(out) :: mLayerHeatCap(:) @@ -134,8 +134,8 @@ subroutine computHeatCap(& ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables integer(i4b) :: iLayer ! index of model layer - real(dp) :: delT - real(dp) :: delEnt + real(rkind) :: delT + real(rkind) :: delEnt integer(i4b) :: iSoil ! index of soil layer ! -------------------------------------------------------------------------------------------------------------------------------- ! associate variables in data structure @@ -320,11 +320,11 @@ subroutine computHeatCapAnalytic(& ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: model control logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - real(dp),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) - real(dp),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) - real(dp),intent(in) :: scalarCanopyLiquid - real(dp),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) - real(dp),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) + real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) + real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) + real(rkind),intent(in) :: scalarCanopyLiquid + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) ! input/output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model layer indices @@ -421,9 +421,9 @@ subroutine computCm(& ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: model control logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - real(dp),intent(in) :: scalarCanopyTemp ! value of canopy ice content (kg m-2) - real(dp),intent(in) :: mLayerTemp(:) ! vector of volumetric liquid water content (-) - real(dp),intent(in) :: mLayerMatricHead(:) ! vector of total water matric potential (m) + real(rkind),intent(in) :: scalarCanopyTemp ! value of canopy ice content (kg m-2) + real(rkind),intent(in) :: mLayerTemp(:) ! vector of volumetric liquid water content (-) + real(rkind),intent(in) :: mLayerMatricHead(:) ! vector of total water matric potential (m) ! input/output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model layer indices @@ -437,9 +437,9 @@ subroutine computCm(& character(LEN=256) :: cmessage ! error message of downwind routine integer(i4b) :: iLayer ! index of model layer integer(i4b) :: iSoil ! index of soil layer - real(dp) :: g1 - real(dp) :: g2 - real(dp) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind) :: g1 + real(rkind) :: g2 + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) ! -------------------------------------------------------------------------------------------------------------------------------- ! associate variables in data structure diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 017a77a96..b02443771 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -26,7 +26,7 @@ module computJacob_module ! derived types to define the data structures USE data_types,only:& var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (dp) + var_dlength ! data vector with variable length dimension (rkind) ! named variables for structure elements USE var_lookup,only:iLookPROG ! named variables for structure elements diff --git a/build/source/engine/computJacobFida.f90 b/build/source/engine/computJacobFida.f90 index 2eff1522a..22102fc14 100644 --- a/build/source/engine/computJacobFida.f90 +++ b/build/source/engine/computJacobFida.f90 @@ -26,7 +26,7 @@ module computJacobFida_module ! derived types to define the data structures USE data_types,only:& var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (dp) + var_dlength ! data vector with variable length dimension (rkind) ! named variables for structure elements USE var_lookup,only:iLookPROG ! named variables for structure elements @@ -80,7 +80,7 @@ module computJacobFida_module implicit none ! define constants -real(dp),parameter :: verySmall=tiny(1.0_rkind) ! a very small number +real(rkind),parameter :: verySmall=tiny(1.0_rkind) ! a very small number integer(i4b),parameter :: ixBandOffset=kl+ku+1 ! offset in the band Jacobian matrix private @@ -131,40 +131,40 @@ subroutine computJacobFida(& ! ----------------------------------------------------------------------------------------------------------------- implicit none ! input: model control - real(dp),intent(in) :: cj - real(dp),intent(in) :: dt ! length of the time step (seconds) + real(rkind),intent(in) :: cj + real(rkind),intent(in) :: dt ! length of the time step (seconds) integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: computeBaseflow ! flag to indicate if computing baseflow integer(i4b),intent(in) :: ixMatrix ! form of the Jacobian matrix - real(dp),intent(in) :: specificStorage ! specific storage coefficient (m-1) - real(dp),intent(in) :: theta_sat(:) ! soil porosity (-) + real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) + real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation ! input: data structures type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(dp),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! input: state variables - real(dp),intent(in) :: mLayerTemp(:) - real(dp),intent(in) :: mLayerTempPrime(:) - real(dp),intent(in) :: mLayerMatricHeadPrime(:) - real(dp),intent(in) :: mLayerMatricHeadLiqPrime(:) - real(dp),intent(in) :: mLayerd2Theta_dTk2(:) - real(dp),intent(in) :: mLayerVolFracWatPrime(:) - real(dp),intent(in) :: scalarCanopyTemp - real(dp),intent(in) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) - real(dp),intent(in) :: scalarCanopyWatPrime - real(dp),intent(in) :: d2VolTot_d2Psi0(:) - real(dp),intent(in) :: dFracLiqSnow_dTk(:) - real(dp),intent(in) :: d2Theta_dTkCanopy2 - real(dp),intent(in) :: dFracLiqVeg_dTkCanopy + real(rkind),intent(in) :: mLayerTemp(:) + real(rkind),intent(in) :: mLayerTempPrime(:) + real(rkind),intent(in) :: mLayerMatricHeadPrime(:) + real(rkind),intent(in) :: mLayerMatricHeadLiqPrime(:) + real(rkind),intent(in) :: mLayerd2Theta_dTk2(:) + real(rkind),intent(in) :: mLayerVolFracWatPrime(:) + real(rkind),intent(in) :: scalarCanopyTemp + real(rkind),intent(in) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) + real(rkind),intent(in) :: scalarCanopyWatPrime + real(rkind),intent(in) :: d2VolTot_d2Psi0(:) + real(rkind),intent(in) :: dFracLiqSnow_dTk(:) + real(rkind),intent(in) :: d2Theta_dTkCanopy2 + real(rkind),intent(in) :: dFracLiqVeg_dTkCanopy ! input-output: Jacobian and its diagonal - real(dp),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix - real(dp),intent(out) :: aJac(:,:) ! Jacobian matrix + real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix + real(rkind),intent(out) :: aJac(:,:) ! Jacobian matrix ! output variables integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -182,7 +182,7 @@ subroutine computJacobFida(& integer(i4b) :: jLayer ! index of model layer within the full state vector (hydrology) integer(i4b) :: pLayer ! indices of soil layers (used for the baseflow derivatives) ! conversion factors - real(dp) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative + real(rkind) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative ! -------------------------------------------------------------- ! associate variables from data structures associate(& diff --git a/build/source/engine/computJacobFidaCpVar.f90 b/build/source/engine/computJacobFidaCpVar.f90 index d501a7d62..452beaee1 100644 --- a/build/source/engine/computJacobFidaCpVar.f90 +++ b/build/source/engine/computJacobFidaCpVar.f90 @@ -26,7 +26,7 @@ module computJacobFidaCpVar_module ! derived types to define the data structures USE data_types,only:& var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (dp) + var_dlength ! data vector with variable length dimension (rkind) ! named variables for structure elements USE var_lookup,only:iLookPROG ! named variables for structure elements @@ -86,7 +86,7 @@ module computJacobFidaCpVar_module implicit none ! define constants -real(dp),parameter :: verySmall=tiny(1.0_rkind) ! a very small number +real(rkind),parameter :: verySmall=tiny(1.0_rkind) ! a very small number integer(i4b),parameter :: ixBandOffset=kl+ku+1 ! offset in the band Jacobian matrix private @@ -138,41 +138,41 @@ subroutine computJacobFidaCpVar(& ! ----------------------------------------------------------------------------------------------------------------- implicit none ! input: model control - real(dp),intent(in) :: cj - real(dp),intent(in) :: dt ! length of the time step (seconds) + real(rkind),intent(in) :: cj + real(rkind),intent(in) :: dt ! length of the time step (seconds) integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: computeBaseflow ! flag to indicate if computing baseflow integer(i4b),intent(in) :: ixMatrix ! form of the Jacobian matrix - real(dp),intent(in) :: specificStorage ! specific storage coefficient (m-1) - real(dp),intent(in) :: theta_sat(:) ! soil porosity (-) + real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) + real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation ! input: data structures type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(dp),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! input: state variables - real(dp),intent(in) :: mLayerTemp(:) - real(dp),intent(in) :: mLayerTempPrime(:) - real(dp),intent(in) :: mLayerMatricHeadPrime(:) - real(dp),intent(in) :: mLayerMatricHeadLiqPrime(:) - real(dp),intent(in) :: mLayerd2Theta_dTk2(:) - real(dp),intent(in) :: mLayerVolFracWatPrime(:) - real(dp),intent(in) :: mLayerVolFracWat(:) - real(dp),intent(in) :: scalarCanopyTemp - real(dp),intent(in) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) - real(dp),intent(in) :: scalarCanopyWatPrime - real(dp),intent(in) :: d2VolTot_d2Psi0(:) - real(dp),intent(in) :: dFracLiqSnow_dTk(:) - real(dp),intent(in) :: d2Theta_dTkCanopy2 - real(dp),intent(in) :: dFracLiqVeg_dTkCanopy + real(rkind),intent(in) :: mLayerTemp(:) + real(rkind),intent(in) :: mLayerTempPrime(:) + real(rkind),intent(in) :: mLayerMatricHeadPrime(:) + real(rkind),intent(in) :: mLayerMatricHeadLiqPrime(:) + real(rkind),intent(in) :: mLayerd2Theta_dTk2(:) + real(rkind),intent(in) :: mLayerVolFracWatPrime(:) + real(rkind),intent(in) :: mLayerVolFracWat(:) + real(rkind),intent(in) :: scalarCanopyTemp + real(rkind),intent(in) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) + real(rkind),intent(in) :: scalarCanopyWatPrime + real(rkind),intent(in) :: d2VolTot_d2Psi0(:) + real(rkind),intent(in) :: dFracLiqSnow_dTk(:) + real(rkind),intent(in) :: d2Theta_dTkCanopy2 + real(rkind),intent(in) :: dFracLiqVeg_dTkCanopy ! input-output: Jacobian and its diagonal - real(dp),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix - real(dp),intent(out) :: aJac(:,:) ! Jacobian matrix + real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix + real(rkind),intent(out) :: aJac(:,:) ! Jacobian matrix ! output variables integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -190,11 +190,11 @@ subroutine computJacobFidaCpVar(& integer(i4b) :: jLayer ! index of model layer within the full state vector (hydrology) integer(i4b) :: pLayer ! indices of soil layers (used for the baseflow derivatives) ! conversion factors - real(dp) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative - real(dp) :: beta1 - real(dp) :: beta2 - real(dp) :: gamma1 - real(dp) :: liqSnow + real(rkind) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative + real(rkind) :: beta1 + real(rkind) :: beta2 + real(rkind) :: gamma1 + real(rkind) :: liqSnow ! -------------------------------------------------------------- ! associate variables from data structures associate(& diff --git a/build/source/engine/computResid.f90 b/build/source/engine/computResid.f90 index a5860f932..780e1b38c 100644 --- a/build/source/engine/computResid.f90 +++ b/build/source/engine/computResid.f90 @@ -26,7 +26,7 @@ module computResid_module ! derived types to define the data structures USE data_types,only:& var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (dp) + var_dlength ! data vector with variable length dimension (rkind) ! named variables USE var_lookup,only:iLookPROG ! named variables for structure elements diff --git a/build/source/engine/computResidFida.f90 b/build/source/engine/computResidFida.f90 index 3ddb2fed2..7fd02d8f7 100644 --- a/build/source/engine/computResidFida.f90 +++ b/build/source/engine/computResidFida.f90 @@ -8,7 +8,7 @@ module computResidFida_module ! derived types to define the data structures USE data_types,only:& var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (dp) + var_dlength ! data vector with variable length dimension (rkind) ! named variables USE var_lookup,only:iLookPROG ! named variables for structure elements @@ -98,21 +98,21 @@ subroutine computResidFida(& integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain ! input: flux vectors real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) - real(dp),intent(in) :: fVec(:) ! flux vector + real(rkind),intent(in) :: fVec(:) ! flux vector ! input: state variables (already disaggregated into scalars and vectors) - real(dp),intent(in) :: scalarCanopyTempTrial - real(dp),intent(in) :: mLayerTempTrial(:) - real(dp),intent(in) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) - real(dp),intent(in) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) - real(dp),intent(in) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) - real(dp),intent(in) :: mLayerTempPrime(:) ! trial value for temperature of each snow/soil layer (K) content - real(dp),intent(in) :: scalarAquiferStoragePrime ! trial value of aquifer storage (m) + real(rkind),intent(in) :: scalarCanopyTempTrial + real(rkind),intent(in) :: mLayerTempTrial(:) + real(rkind),intent(in) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) + real(rkind),intent(in) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) + real(rkind),intent(in) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) + real(rkind),intent(in) :: mLayerTempPrime(:) ! trial value for temperature of each snow/soil layer (K) content + real(rkind),intent(in) :: scalarAquiferStoragePrime ! trial value of aquifer storage (m) ! input: diagnostic variables defining the liquid water and ice content (function of state variables) - real(dp),intent(in) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(dp),intent(in) :: scalarCanopyLiqPrime - real(dp),intent(in) :: mLayerVolFracIcePrime(:) ! trial value for volumetric fraction of ice (-) - real(dp),intent(in) :: mLayerVolFracLiqPrime(:) - real(dp),intent(in) :: mLayerVolFracWatPrime(:) + real(rkind),intent(in) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: scalarCanopyLiqPrime + real(rkind),intent(in) :: mLayerVolFracIcePrime(:) ! trial value for volumetric fraction of ice (-) + real(rkind),intent(in) :: mLayerVolFracLiqPrime(:) + real(rkind),intent(in) :: mLayerVolFracWatPrime(:) real(qp),intent(in) :: scalarCanopyCmTrial real(qp),intent(in) :: mLayerCmTrial(:) ! input: data structures @@ -121,7 +121,7 @@ subroutine computResidFida(& type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers ! output - real(dp),intent(out) :: rAdd(:) ! additional (sink) terms on the RHS of the state equation + real(rkind),intent(out) :: rAdd(:) ! additional (sink) terms on the RHS of the state equation real(qp),intent(out) :: rVec(:) ! NOTE: qp ! residual vector integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -130,8 +130,8 @@ subroutine computResidFida(& ! -------------------------------------------------------------------------------------------------------------------------------- integer(i4b) :: iLayer ! index of layer within the snow+soil domain integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) - real(dp),dimension(nLayers) :: mLayerVolFracHydPrime ! vector of volumetric water content (-), either liquid water content or total water content - real(dp) :: scalarCanopyHydPrime ! trial value for canopy water (kg m-2), either liquid water content or total water content + real(rkind),dimension(nLayers) :: mLayerVolFracHydPrime ! vector of volumetric water content (-), either liquid water content or total water content + real(rkind) :: scalarCanopyHydPrime ! trial value for canopy water (kg m-2), either liquid water content or total water content ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! link to the necessary variables for the residual computations diff --git a/build/source/engine/computSnowDepth.f90 b/build/source/engine/computSnowDepth.f90 index be67e457c..32750a03a 100644 --- a/build/source/engine/computSnowDepth.f90 +++ b/build/source/engine/computSnowDepth.f90 @@ -14,10 +14,10 @@ module computSnowDepth_module ! data types USE data_types,only:& var_i, & ! x%var(:) (i4b) - var_d, & ! x%var(:) (dp) + var_d, & ! x%var(:) (rkind) var_ilength, & ! x%var(:)%dat (i4b) - var_dlength, & ! x%var(:)%dat (dp) - zLookup ! x%z(:)%var(:)%lookup(:) (dp) + var_dlength, & ! x%var(:)%dat (rkind) + zLookup ! x%z(:)%var(:)%lookup(:) (rkind) ! named variables for parent structures USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure @@ -35,7 +35,7 @@ module computSnowDepth_module private public::computSnowDepth -real(dp),parameter :: verySmall=1.e-6_rkind ! used as an additive constant to check if substantial difference among real numbers +real(rkind),parameter :: verySmall=1.e-6_rkind ! used as an additive constant to check if substantial difference among real numbers contains @@ -61,20 +61,20 @@ subroutine computSnowDepth(& implicit none real(qp),intent(in) :: dt_sub integer(i4b),intent(in) :: nSnow ! number of snow layers - real(dp),intent(in) :: scalarSnowSublimation - real(dp),intent(inout) :: mLayerVolFracLiq(:) - real(dp),intent(inout) :: mLayerVolFracIce(:) - real(dp),intent(in) :: mLayerTemp(:) - real(dp),intent(in) :: mLayerMeltFreeze(:) + real(rkind),intent(in) :: scalarSnowSublimation + real(rkind),intent(inout) :: mLayerVolFracLiq(:) + real(rkind),intent(inout) :: mLayerVolFracIce(:) + real(rkind),intent(in) :: mLayerTemp(:) + real(rkind),intent(in) :: mLayerMeltFreeze(:) type(var_dlength),intent(in) :: mpar_data ! model parameters - real(dp),intent(inout) :: mLayerDepth(:) + real(rkind),intent(inout) :: mLayerDepth(:) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local variables character(len=256) :: cmessage ! error message integer(i4b) :: iSnow ! index of snow layers - real(dp) :: massLiquid ! mass liquid water (kg m-2) + real(rkind) :: massLiquid ! mass liquid water (kg m-2) ! * compute change in ice content of the top snow layer due to sublimation... ! --------------------------------------------------------------------------- diff --git a/build/source/engine/computThermConduct.f90 b/build/source/engine/computThermConduct.f90 index 5e181606d..843806ea5 100644 --- a/build/source/engine/computThermConduct.f90 +++ b/build/source/engine/computThermConduct.f90 @@ -6,9 +6,9 @@ module computThermConduct_module ! derived types to define the data structures USE data_types,only:& - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (dp) + var_dlength ! data vector with variable length dimension (rkind) ! named variables defining elements in the data structures USE var_lookup,only:iLookPARAM,iLookPROG,iLookDIAG,iLookINDEX ! named variables for structure elements @@ -54,10 +54,10 @@ module computThermConduct_module public::computThermConduct ! algorithmic parameters -real(dp),parameter :: valueMissing=-9999._rkind ! missing value, used when diagnostic or state variables are undefined -real(dp),parameter :: verySmall=1.e-6_rkind ! used as an additive constant to check if substantial difference among real numbers -real(dp),parameter :: mpe=1.e-6_rkind ! prevents overflow error if division by zero -real(dp),parameter :: dx=1.e-6_rkind ! finite difference increment +real(rkind),parameter :: valueMissing=-9999._rkind ! missing value, used when diagnostic or state variables are undefined +real(rkind),parameter :: verySmall=1.e-6_rkind ! used as an additive constant to check if substantial difference among real numbers +real(rkind),parameter :: mpe=1.e-6_rkind ! prevents overflow error if division by zero +real(rkind),parameter :: dx=1.e-6_rkind ! finite difference increment contains @@ -86,11 +86,11 @@ subroutine computThermConduct(& ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: model control logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - real(dp),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) - real(dp),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) - real(dp),intent(in) :: scalarCanopyLiquid - real(dp),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) - real(dp),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) + real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) + real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) + real(rkind),intent(in) :: scalarCanopyLiquid + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) ! input/output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model layer indices @@ -104,26 +104,26 @@ subroutine computThermConduct(& character(LEN=256) :: cmessage ! error message of downwind routine integer(i4b) :: iLayer ! index of model layer integer(i4b) :: iSoil ! index of soil layer - real(dp) :: TCn ! thermal conductivity below the layer interface (W m-1 K-1) - real(dp) :: TCp ! thermal conductivity above the layer interface (W m-1 K-1) - real(dp) :: zdn ! height difference between interface and lower value (m) - real(dp) :: zdp ! height difference between interface and upper value (m) - real(dp) :: bulkden_soil ! bulk density of soil (kg m-3) - real(dp) :: lambda_drysoil ! thermal conductivity of dry soil (W m-1) - real(dp) :: lambda_wetsoil ! thermal conductivity of wet soil (W m-1) - real(dp) :: lambda_wet ! thermal conductivity of the wet material - real(dp) :: relativeSat ! relative saturation (-) - real(dp) :: kerstenNum ! the Kersten number (-), defining weight applied to conductivity of the wet medium - real(dp) :: den ! denominator in the thermal conductivity calculations + real(rkind) :: TCn ! thermal conductivity below the layer interface (W m-1 K-1) + real(rkind) :: TCp ! thermal conductivity above the layer interface (W m-1 K-1) + real(rkind) :: zdn ! height difference between interface and lower value (m) + real(rkind) :: zdp ! height difference between interface and upper value (m) + real(rkind) :: bulkden_soil ! bulk density of soil (kg m-3) + real(rkind) :: lambda_drysoil ! thermal conductivity of dry soil (W m-1) + real(rkind) :: lambda_wetsoil ! thermal conductivity of wet soil (W m-1) + real(rkind) :: lambda_wet ! thermal conductivity of the wet material + real(rkind) :: relativeSat ! relative saturation (-) + real(rkind) :: kerstenNum ! the Kersten number (-), defining weight applied to conductivity of the wet medium + real(rkind) :: den ! denominator in the thermal conductivity calculations ! local variables to reproduce the thermal conductivity of Hansson et al. VZJ 2005 - real(dp),parameter :: c1=0.55_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(dp),parameter :: c2=0.8_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(dp),parameter :: c3=3.07_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(dp),parameter :: c4=0.13_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(dp),parameter :: c5=4._rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(dp),parameter :: f1=13.05_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(dp),parameter :: f2=1.06_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(dp) :: fArg,xArg ! temporary variables (see Hansson et al. VZJ 2005 for details) + real(rkind),parameter :: c1=0.55_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c2=0.8_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c3=3.07_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: c4=0.13_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c5=4._rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: f1=13.05_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: f2=1.06_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind) :: fArg,xArg ! temporary variables (see Hansson et al. VZJ 2005 for details) ! -------------------------------------------------------------------------------------------------------------------------------- ! associate variables in data structure associate(& diff --git a/build/source/engine/convE2Temp.f90 b/build/source/engine/convE2Temp.f90 index 0408c3fca..ab6a9f5d1 100644 --- a/build/source/engine/convE2Temp.f90 +++ b/build/source/engine/convE2Temp.f90 @@ -22,7 +22,7 @@ module convE2Temp_module ! data types USE nrtype -USE data_types,only:var_dlength ! data vector with variable length dimension (dp): x%var(:)%dat(:) +USE data_types,only:var_dlength ! data vector with variable length dimension (rkind): x%var(:)%dat(:) ! constants USE multiconst, only: Tfreeze, & ! freezing point of water (K) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 1489c3dcb..2787c669b 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -34,10 +34,10 @@ module coupled_em_module ! data types USE data_types,only:& var_i, & ! x%var(:) (i4b) - var_d, & ! x%var(:) (dp) + var_d, & ! x%var(:) (rkind) var_ilength, & ! x%var(:)%dat (i4b) - var_dlength, & ! x%var(:)%dat (dp) - zLookup ! x%z(:)%var(:)%lookup(:) (dp) + var_dlength, & ! x%var(:)%dat (rkind) + zLookup ! x%z(:)%var(:)%lookup(:) (rkind) ! named variables for parent structures USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure diff --git a/build/source/engine/derivforce.f90 b/build/source/engine/derivforce.f90 index b8f560ed2..e3ed9dc48 100644 --- a/build/source/engine/derivforce.f90 +++ b/build/source/engine/derivforce.f90 @@ -22,7 +22,7 @@ module derivforce_module ! data types USE nrtype -USE data_types,only:var_dlength ! data structure: x%var(:)%dat (dp) +USE data_types,only:var_dlength ! data structure: x%var(:)%dat (rkind) ! model constants USE multiconst,only:Tfreeze ! freezing point of pure water (K) diff --git a/build/source/engine/diagn_evar.f90 b/build/source/engine/diagn_evar.f90 index b3a3b6fd0..df0d06467 100644 --- a/build/source/engine/diagn_evar.f90 +++ b/build/source/engine/diagn_evar.f90 @@ -25,9 +25,9 @@ module diagn_evar_module ! derived types to define the data structures USE data_types,only:& - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (dp) + var_dlength ! data vector with variable length dimension (rkind) ! named variables defining elements in the data structures USE var_lookup,only:iLookPARAM,iLookPROG,iLookDIAG,iLookINDEX ! named variables for structure elements diff --git a/build/source/engine/eval8FidaJac.f90 b/build/source/engine/eval8FidaJac.f90 index 56287ef04..0ac640fb4 100644 --- a/build/source/engine/eval8FidaJac.f90 +++ b/build/source/engine/eval8FidaJac.f90 @@ -48,9 +48,9 @@ module eval8FidaJac_module ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions ! indices that define elements of the data structures @@ -126,8 +126,8 @@ subroutine eval8FidaJac(& ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control - real(dp),intent(in) :: cj - real(dp),intent(in) :: dt ! time step + real(rkind),intent(in) :: cj + real(rkind),intent(in) :: dt ! time step integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers @@ -135,8 +135,8 @@ subroutine eval8FidaJac(& logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors - real(dp),intent(in) :: stateVec(:) ! model state vector - real(dp),intent(in) :: stateVecPrime(:) ! model state vector + real(rkind),intent(in) :: stateVec(:) ! model state vector + real(rkind),intent(in) :: stateVecPrime(:) ! model state vector real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) ! input: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions @@ -147,10 +147,10 @@ subroutine eval8FidaJac(& type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow - real(dp),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! output: Jacobian - real(dp), intent(inout) :: dMat(:) - real(dp), intent(out) :: Jac(:,:) ! jacobian matrix + real(rkind), intent(inout) :: dMat(:) + real(rkind), intent(out) :: Jac(:,:) ! jacobian matrix ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -158,42 +158,42 @@ subroutine eval8FidaJac(& ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- ! state variables - real(dp) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(dp) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(dp) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(dp),dimension(nLayers) :: mLayerTempTrial ! trial value for temperature of layers in the snow and soil domains (K) - real(dp),dimension(nLayers) :: mLayerVolFracWatTrial ! trial value for volumetric fraction of total water (-) - real(dp),dimension(nSoil) :: mLayerMatricHeadTrial ! trial value for total water matric potential (m) - real(dp),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) - real(dp) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial value for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial value for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial value for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) + real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) ! diagnostic variables - real(dp) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(dp) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(dp),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) - real(dp),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) + real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) ! derivative of state variables - real(dp) :: scalarCanairTempPrime ! derivative value for temperature of the canopy air space (K) - real(dp) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) - real(dp) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) - real(dp),dimension(nLayers) :: mLayerTempPrime ! derivative value for temperature of layers in the snow and soil domains (K) - real(dp),dimension(nLayers) :: mLayerVolFracWatPrime ! derivative value for volumetric fraction of total water (-) - real(dp),dimension(nSoil) :: mLayerMatricHeadPrime ! derivative value for total water matric potential (m) - real(dp),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! derivative value for liquid water matric potential (m) - real(dp) :: scalarAquiferStoragePrime ! derivative value of storage of water in the aquifer (m) + real(rkind) :: scalarCanairTempPrime ! derivative value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempPrime ! derivative value for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! derivative value for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! derivative value for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! derivative value for liquid water matric potential (m) + real(rkind) :: scalarAquiferStoragePrime ! derivative value of storage of water in the aquifer (m) ! derivative of diagnostic variables - real(dp) :: scalarCanopyLiqPrime ! derivative value for mass of liquid water on the vegetation canopy (kg m-2) - real(dp) :: scalarCanopyIcePrime ! derivative value for mass of ice on the vegetation canopy (kg m-2) - real(dp),dimension(nLayers) :: mLayerVolFracLiqPrime ! derivative value for volumetric fraction of liquid water (-) - real(dp),dimension(nLayers) :: mLayerVolFracIcePrime ! derivative value for volumetric fraction of ice (-) + real(rkind) :: scalarCanopyLiqPrime ! derivative value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIcePrime ! derivative value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! derivative value for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! derivative value for volumetric fraction of ice (-) ! other local variables character(LEN=256) :: cmessage ! error message of downwind routine - real(dp) :: dt1 - real(dp) :: mLayerd2Theta_dTk2(nLayers) - real(dp) :: d2VolTot_d2Psi0(nLayers) - real(dp) :: dFracLiqSnow_dTk(nLayers) - real(dp) :: d2Theta_dTkCanopy2 - real(dp) :: dFracLiqVeg_dTkCanopy + real(rkind) :: dt1 + real(rkind) :: mLayerd2Theta_dTk2(nLayers) + real(rkind) :: d2VolTot_d2Psi0(nLayers) + real(rkind) :: dFracLiqSnow_dTk(nLayers) + real(rkind) :: d2Theta_dTkCanopy2 + real(rkind) :: dFracLiqVeg_dTkCanopy ! -------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index c6a4e3c80..cc3927b93 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -64,10 +64,10 @@ module eval8summa_module ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) - zLookup, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions ! indices that define elements of the data structures diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8summaFida.f90 index 14c7b81e8..82091e888 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8summaFida.f90 @@ -47,9 +47,9 @@ module eval8summaFida_module ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) zlookup, & model_options ! defines the model decisions @@ -170,8 +170,8 @@ subroutine eval8summaFida(& ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control - real(dp),intent(in) :: dt_cur - real(dp),intent(in) :: dt ! time step + real(rkind),intent(in) :: dt_cur + real(rkind),intent(in) :: dt ! time step integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers @@ -182,8 +182,8 @@ subroutine eval8summaFida(& logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors - real(dp),intent(in) :: stateVec(:) ! model state vector - real(dp),intent(in) :: stateVecPrime(:) ! model state vector + real(rkind),intent(in) :: stateVec(:) ! model state vector + real(rkind),intent(in) :: stateVecPrime(:) ! model state vector real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) ! input: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions @@ -200,35 +200,35 @@ subroutine eval8summaFida(& type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow - real(dp),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors - real(dp),intent(out) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(dp),intent(in) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) - real(dp),intent(out) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(dp),intent(in) :: scalarCanopyIcePrev ! previous value for mass of ice on the vegetation canopy (kg m-2) - real(dp),intent(out) :: scalarCanopyLiqTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(dp),intent(in) :: scalarCanopyLiqPrev ! previous value for mass of ice on the vegetation canopy (kg m-2) - real(dp),intent(out) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) - real(dp),intent(in) :: scalarCanopyEnthalpyPrev ! previous value of enthalpy of the vegetation canopy (J m-3) - real(dp),intent(out) :: mLayerTempTrial(:) - real(dp),intent(in) :: mLayerTempPrev(:) - real(dp),intent(out) :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) - real(dp),intent(out) :: mLayerMatricHeadTrial(:) ! trial value for liquid water matric potential (m) - real(dp),intent(in) :: mLayerMatricHeadPrev(:) - real(dp),intent(out) :: mLayerVolFracWatTrial(:) - real(dp),intent(in) :: mLayerVolFracWatPrev(:) - real(dp),intent(out) :: mLayerVolFracIceTrial(:) - real(dp),intent(in) :: mLayerVolFracIcePrev(:) - real(dp),intent(out) :: mLayerVolFracLiqTrial(:) - real(dp),intent(in) :: mLayerVolFracLiqPrev(:) - real(dp),intent(out) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) - real(dp),intent(in) :: scalarAquiferStoragePrev ! previous value of storage of water in the aquifer (m) - real(dp),intent(in) :: mLayerEnthalpyPrev(:) - real(dp),intent(out) :: mLayerEnthalpyTrial(:) + real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind),intent(in) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) + real(rkind),intent(out) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: scalarCanopyIcePrev ! previous value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(out) :: scalarCanopyLiqTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: scalarCanopyLiqPrev ! previous value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(out) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(in) :: scalarCanopyEnthalpyPrev ! previous value of enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(out) :: mLayerTempTrial(:) + real(rkind),intent(in) :: mLayerTempPrev(:) + real(rkind),intent(out) :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) + real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial value for liquid water matric potential (m) + real(rkind),intent(in) :: mLayerMatricHeadPrev(:) + real(rkind),intent(out) :: mLayerVolFracWatTrial(:) + real(rkind),intent(in) :: mLayerVolFracWatPrev(:) + real(rkind),intent(out) :: mLayerVolFracIceTrial(:) + real(rkind),intent(in) :: mLayerVolFracIcePrev(:) + real(rkind),intent(out) :: mLayerVolFracLiqTrial(:) + real(rkind),intent(in) :: mLayerVolFracLiqPrev(:) + real(rkind),intent(out) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + real(rkind),intent(in) :: scalarAquiferStoragePrev ! previous value of storage of water in the aquifer (m) + real(rkind),intent(in) :: mLayerEnthalpyPrev(:) + real(rkind),intent(out) :: mLayerEnthalpyTrial(:) integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution - real(dp),intent(out) :: fluxVec(:) ! flux vector - real(dp),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation + real(rkind),intent(out) :: fluxVec(:) ! flux vector + real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation real(qp),intent(out) :: resVec(:) ! NOTE: qp ! residual vector ! output: error control integer(i4b),intent(out) :: err ! error code @@ -237,36 +237,36 @@ subroutine eval8summaFida(& ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- ! state variables - real(dp) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(dp) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) ! derivative of state variables - real(dp) :: scalarCanairTempPrime ! derivative value for temperature of the canopy air space (K) - real(dp) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) - real(dp) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) - real(dp),dimension(nLayers) :: mLayerTempPrime ! derivative value for temperature of layers in the snow and soil domains (K) - real(dp),dimension(nLayers) :: mLayerVolFracWatPrime ! derivative value for volumetric fraction of total water (-) - real(dp),dimension(nSoil) :: mLayerMatricHeadPrime ! derivative value for total water matric potential (m) - real(dp),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! derivative value for liquid water matric potential (m) - real(dp) :: scalarAquiferStoragePrime ! derivative value of storage of water in the aquifer (m) + real(rkind) :: scalarCanairTempPrime ! derivative value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempPrime ! derivative value for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! derivative value for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! derivative value for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! derivative value for liquid water matric potential (m) + real(rkind) :: scalarAquiferStoragePrime ! derivative value of storage of water in the aquifer (m) ! derivative of diagnostic variables - real(dp) :: scalarCanopyLiqPrime ! derivative value for mass of liquid water on the vegetation canopy (kg m-2) - real(dp) :: scalarCanopyIcePrime ! derivative value for mass of ice on the vegetation canopy (kg m-2) - real(dp),dimension(nLayers) :: mLayerVolFracLiqPrime ! derivative value for volumetric fraction of liquid water (-) - real(dp),dimension(nLayers) :: mLayerVolFracIcePrime ! derivative value for volumetric fraction of ice (-) + real(rkind) :: scalarCanopyLiqPrime ! derivative value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIcePrime ! derivative value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! derivative value for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! derivative value for volumetric fraction of ice (-) ! enthalpy - real(dp) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - real(dp),dimension(nLayers) :: mLayerEnthalpyPrime ! enthalpy of each snow+soil layer (J m-3) + real(rkind) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpyPrime ! enthalpy of each snow+soil layer (J m-3) ! other local variables integer(i4b) :: iLayer ! index of model layer in the snow+soil domain integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) - real(dp) :: xMin,xMax ! minimum and maximum values for water content - real(dp),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) + real(rkind) :: xMin,xMax ! minimum and maximum values for water content + real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) character(LEN=256) :: cmessage ! error message of downwind routine real(qp) :: scalarCanopyEnthalpyPrime - real(dp) :: scalarCanopyCmTrial - real(dp),dimension(nLayers) :: mLayerCmTrial + real(rkind) :: scalarCanopyCmTrial + real(rkind),dimension(nLayers) :: mLayerCmTrial logical(lgt),parameter :: updateCp=.true. logical(lgt),parameter :: enthalpyFD=.false. logical(lgt),parameter :: needCm=.false. diff --git a/build/source/engine/evalEqnsFida.f90 b/build/source/engine/evalEqnsFida.f90 index c72d6afe0..e4140c2cb 100644 --- a/build/source/engine/evalEqnsFida.f90 +++ b/build/source/engine/evalEqnsFida.f90 @@ -12,9 +12,9 @@ module evalEqnsFida_module ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions USE multiconst,only:iden_water ! intrinsic density of liquid water (kg m-3) USE var_lookup,only:iLookDIAG @@ -55,7 +55,7 @@ integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_d implicit none ! calling variables - real(dp), value :: tres ! current time t + real(rkind), value :: tres ! current time t type(N_Vector) :: sunvec_y ! solution N_Vector y type(N_Vector) :: sunvec_yp ! derivative N_Vector y' type(N_Vector) :: sunvec_r ! residual N_Vector F(t,y,y') @@ -64,9 +64,9 @@ integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_d ! pointers to data in SUNDIALS vectors type(eqnsData), pointer :: eqns_data ! equations data - real(dp), pointer :: stateVec(:) - real(dp), pointer :: stateVecPrime(:) - real(dp), pointer :: rVec(:) + real(rkind), pointer :: stateVec(:) + real(rkind), pointer :: stateVecPrime(:) + real(rkind), pointer :: rVec(:) logical(lgt) :: feasible integer(i4b) :: retval real(c_double) :: stepsize_next(1) diff --git a/build/source/engine/evalJacFida.f90 b/build/source/engine/evalJacFida.f90 index 1f5fbea6f..98af4a56e 100644 --- a/build/source/engine/evalJacFida.f90 +++ b/build/source/engine/evalJacFida.f90 @@ -11,9 +11,9 @@ module evalJacFida_module ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions @@ -51,8 +51,8 @@ integer(c_int) function evalJacFida(t, cj, sunvec_y, sunvec_yp, sunvec_r, & implicit none ! calling variables - real(dp), value :: t ! current time - real(dp), value :: cj ! step size scaling factor + real(rkind), value :: t ! current time + real(rkind), value :: cj ! step size scaling factor type(N_Vector) :: sunvec_y ! solution N_Vector type(N_Vector) :: sunvec_yp ! derivative N_Vector type(N_Vector) :: sunvec_r ! residual N_Vector @@ -63,10 +63,10 @@ integer(c_int) function evalJacFida(t, cj, sunvec_y, sunvec_yp, sunvec_r, & type(N_Vector) :: sunvec_temp3 ! pointers to data in SUNDIALS vectors - real(dp), pointer :: stateVec(:) - real(dp), pointer :: stateVecPrime(:) - real(dp), pointer :: rVec(:) - real(dp), pointer :: Jac(:,:) + real(rkind), pointer :: stateVec(:) + real(rkind), pointer :: stateVecPrime(:) + real(rkind), pointer :: rVec(:) + real(rkind), pointer :: Jac(:,:) type(eqnsData), pointer :: eqns_data ! equations data diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/fidaSolver.f90 index 94d4db4a3..49a0106dc 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/fidaSolver.f90 @@ -68,9 +68,9 @@ module fidaSolver_module ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) zLookup ! data vector @@ -179,9 +179,9 @@ subroutine fidaSolver( & logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors - real(dp),intent(in) :: stateVecInit(:) ! model state vector + real(rkind),intent(in) :: stateVecInit(:) ! model state vector real(qp),intent(in) :: sMul(:) ! state vector multiplier (used in the residual calculations) - real(dp), intent(inout) :: dMat(:) + real(rkind), intent(inout) :: dMat(:) ! input: data structures type(zLookup),intent(in) :: lookup_data ! lookup tables type(var_i), intent(in) :: type_data ! type of vegetation and soil @@ -197,11 +197,11 @@ subroutine fidaSolver( & type(var_dlength),intent(inout):: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: flux_sum type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(dp),intent(inout) :: mLayerCmpress_sum(:) + real(rkind),intent(inout) :: mLayerCmpress_sum(:) ! output: state vectors integer(i4b),intent(out) :: ixSaturation - real(dp),intent(inout) :: stateVec(:) ! model state vector (y) - real(dp),intent(inout) :: stateVecPrime(:) ! model state vector (y') + real(rkind),intent(inout) :: stateVec(:) ! model state vector (y) + real(rkind),intent(inout) :: stateVecPrime(:) ! model state vector (y') logical(lgt),intent(out) :: idaSucceeds real(qp),intent(out) :: dt_last(1) real(qp),intent(out) :: dt_past @@ -232,23 +232,23 @@ subroutine fidaSolver( & integer(i4b),parameter :: ixTrapezoidal=2 integer(i4b) :: iVar logical(lgt) :: startQuadrature - real(dp) :: mLayerMatricHeadLiqPrev(nSoil) + real(rkind) :: mLayerMatricHeadLiqPrev(nSoil) real(qp) :: h_init integer(c_long) :: nState ! total number of state variables - real(dp) :: rVec(nStat) + real(rkind) :: rVec(nStat) real(qp) :: tret(1) - real(dp) :: bulkDensity ! bulk density of a given layer (kg m-3) - real(dp) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) + real(rkind) :: bulkDensity ! bulk density of a given layer (kg m-3) + real(rkind) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) logical(lgt) :: tooMuchMelt logical(lgt) :: divideLayer logical(lgt) :: mergedLayers logical(lgt),parameter :: checkSnow = .true. - real(dp) :: superflousSub ! superflous sublimation (kg m-2 s-1) - real(dp) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) - real(dp) :: mLayerDepth(nLayers) - real(dp) :: scalarSnowDepth - real(dp) :: scalarSWE - real(dp) :: mLayerMeltFreeze(nLayers) + real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) + real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) + real(rkind) :: mLayerDepth(nLayers) + real(rkind) :: scalarSnowDepth + real(rkind) :: scalarSWE + real(rkind) :: mLayerMeltFreeze(nLayers) !======= Internals ============ @@ -737,7 +737,7 @@ subroutine setInitialCondition(neq, y, sunvec_u, sunvec_up) type(N_Vector) :: sunvec_u ! solution N_Vector type(N_Vector) :: sunvec_up ! derivative N_Vector integer(c_long) :: neq - real(dp) :: y(neq) + real(rkind) :: y(neq) ! pointers to data in SUNDIALS vectors real(c_double), pointer :: uu(:) @@ -766,7 +766,7 @@ subroutine setSolverParams(dt,ida_mem,retval) USE fida_mod ! Fortran interface to IDA implicit none - real(dp),intent(in) :: dt ! time step + real(rkind),intent(in) :: dt ! time step type(c_ptr),intent(inout) :: ida_mem ! IDA memory integer(i4b),intent(out) :: retval ! return value @@ -834,20 +834,20 @@ subroutine implctMelt(& err,message ) ! intent(out): error control implicit none ! input/output: integrated snowpack properties - real(dp),intent(inout) :: scalarSWE ! snow water equivalent (kg m-2) - real(dp),intent(inout) :: scalarSnowDepth ! snow depth (m) - real(dp),intent(inout) :: scalarSfcMeltPond ! surface melt pond (kg m-2) + real(rkind),intent(inout) :: scalarSWE ! snow water equivalent (kg m-2) + real(rkind),intent(inout) :: scalarSnowDepth ! snow depth (m) + real(rkind),intent(inout) :: scalarSfcMeltPond ! surface melt pond (kg m-2) ! input/output: properties of the upper-most soil layer - real(dp),intent(inout) :: soilTemp ! surface layer temperature (K) - real(dp),intent(inout) :: soilDepth ! surface layer depth (m) - real(dp),intent(inout) :: soilHeatcap ! surface layer volumetric heat capacity (J m-3 K-1) + real(rkind),intent(inout) :: soilTemp ! surface layer temperature (K) + real(rkind),intent(inout) :: soilDepth ! surface layer depth (m) + real(rkind),intent(inout) :: soilHeatcap ! surface layer volumetric heat capacity (J m-3 K-1) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local variables - real(dp) :: nrgRequired ! energy required to melt all the snow (J m-2) - real(dp) :: nrgAvailable ! energy available to melt the snow (J m-2) - real(dp) :: snwDensity ! snow density (kg m-3) + real(rkind) :: nrgRequired ! energy required to melt all the snow (J m-2) + real(rkind) :: nrgAvailable ! energy available to melt the snow (J m-2) + real(rkind) :: snwDensity ! snow density (kg m-3) ! initialize error control err=0; message='implctMelt/' diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/fida_datatypes.f90 index e0fe45c74..bb7219fde 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/fida_datatypes.f90 @@ -9,16 +9,16 @@ module fida_datatypes ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) - zLookup ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup ! data vector with variable length dimension (rkind) implicit none type eqnsData type(c_ptr) :: ida_mem ! IDA memory - real(dp) :: dt ! time step + real(rkind) :: dt ! time step integer(i4b) :: nSnow ! number of snow layers integer(i4b) :: nSoil ! number of soil layers integer(i4b) :: nLayers ! total number of layers @@ -40,36 +40,36 @@ module fida_datatypes type(var_dlength) :: diag_data ! diagnostic variables for a local HRU type(var_dlength) :: flux_data ! model fluxes for a local HRU type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(dp) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(dp) :: scalarCanopyTempPrev ! previous value of canopy temperature (K) - real(dp) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - real(dp) :: scalarCanopyIcePrev ! value of canopy ice content (kg m-2) at previous step - real(dp) :: scalarCanopyLiqTrial ! trial value of canopy ice content (kg m-2) - real(dp) :: scalarCanopyLiqPrev ! value of canopy ice content (kg m-2) at previous step - real(dp) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) - real(dp) :: scalarCanopyEnthalpyPrev ! previous enthalpy of the vegetation canopy (J m-3) + real(rkind) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind) :: scalarCanopyTempPrev ! previous value of canopy temperature (K) + real(rkind) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + real(rkind) :: scalarCanopyIcePrev ! value of canopy ice content (kg m-2) at previous step + real(rkind) :: scalarCanopyLiqTrial ! trial value of canopy ice content (kg m-2) + real(rkind) :: scalarCanopyLiqPrev ! value of canopy ice content (kg m-2) at previous step + real(rkind) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) + real(rkind) :: scalarCanopyEnthalpyPrev ! previous enthalpy of the vegetation canopy (J m-3) real(qp), allocatable :: sMul(:) ! state vector multiplier (used in the residual calculations) - real(dp), allocatable :: dMat(:) ! diagonal of the Jacobian matrix - real(dp), allocatable :: fluxVec(:) ! flux vector + real(rkind), allocatable :: dMat(:) ! diagonal of the Jacobian matrix + real(rkind), allocatable :: fluxVec(:) ! flux vector real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation - real(dp), allocatable :: atol(:) ! vector of absolute tolerances - real(dp), allocatable :: rtol(:) ! vector of relative tolerances - real(dp), allocatable :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) - real(dp), allocatable :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(dp), allocatable :: mLayerMatricHeadPrev(:) ! vector of total water matric potential (m) at previous step - real(dp), allocatable :: mLayerVolFracWatTrial(:) ! trial value for volumetric fraction of total water (-) - real(dp), allocatable :: mLayerVolFracWatPrev(:) ! value for volumetric fraction of total water (-) at previous step - real(dp), allocatable :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) - real(dp), allocatable :: mLayerVolFracIcePrev(:) ! value for volumetric fraction of ice (-) at previous step - real(dp), allocatable :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) - real(dp), allocatable :: mLayerVolFracLiqPrev(:) ! value for volumetric fraction of liquid water (-) at previous step - real(dp) :: scalarAquiferStoragePrev - real(dp) :: scalarAquiferStorageTrial - real(dp), allocatable :: mLayerEnthalpyTrial(:) ! trial enthalpy of snow and soil (J m-3) - real(dp), allocatable :: mLayerEnthalpyPrev(:) ! enthalpy of snow and soil (J m-3) at previous step - real(dp), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(dp), allocatable :: mLayerTempPrev(:) ! vector of layer temperature (K) at previous step - real(dp), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + real(rkind), allocatable :: atol(:) ! vector of absolute tolerances + real(rkind), allocatable :: rtol(:) ! vector of relative tolerances + real(rkind), allocatable :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) + real(rkind), allocatable :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind), allocatable :: mLayerMatricHeadPrev(:) ! vector of total water matric potential (m) at previous step + real(rkind), allocatable :: mLayerVolFracWatTrial(:) ! trial value for volumetric fraction of total water (-) + real(rkind), allocatable :: mLayerVolFracWatPrev(:) ! value for volumetric fraction of total water (-) at previous step + real(rkind), allocatable :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) + real(rkind), allocatable :: mLayerVolFracIcePrev(:) ! value for volumetric fraction of ice (-) at previous step + real(rkind), allocatable :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) + real(rkind), allocatable :: mLayerVolFracLiqPrev(:) ! value for volumetric fraction of liquid water (-) at previous step + real(rkind) :: scalarAquiferStoragePrev + real(rkind) :: scalarAquiferStorageTrial + real(rkind), allocatable :: mLayerEnthalpyTrial(:) ! trial enthalpy of snow and soil (J m-3) + real(rkind), allocatable :: mLayerEnthalpyPrev(:) ! enthalpy of snow and soil (J m-3) at previous step + real(rkind), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind), allocatable :: mLayerTempPrev(:) ! vector of layer temperature (K) at previous step + real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) integer :: ixSaturation integer(i4b) :: err ! error code character(len = 50) :: message ! error message diff --git a/build/source/engine/findDiscontinuity.f90 b/build/source/engine/findDiscontinuity.f90 index fce75a1ec..12d7c3f93 100644 --- a/build/source/engine/findDiscontinuity.f90 +++ b/build/source/engine/findDiscontinuity.f90 @@ -12,9 +12,9 @@ module findDiscontinuity_module ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions USE multiconst,only:iden_water ! intrinsic density of liquid water (kg m-3) USE var_lookup,only:iLookDIAG @@ -53,7 +53,7 @@ integer(c_int) function findDiscontinuity(tres, sunvec_y, sunvec_yp, fval, user_ implicit none ! calling variables - real(dp), value :: tres ! current time + real(rkind), value :: tres ! current time type(N_Vector) :: sunvec_y ! solution N_Vector y type(N_Vector) :: sunvec_yp ! derivative N_Vector y' real(c_double) :: fval(1000) ! root function values @@ -62,8 +62,8 @@ integer(c_int) function findDiscontinuity(tres, sunvec_y, sunvec_yp, fval, user_ ! pointers to data in SUNDIALS vectors type(eqnsData), pointer :: eqns_data ! equations data - real(dp), pointer :: stateVec(:) - real(dp), pointer :: stateVecPrime(:) + real(rkind), pointer :: stateVec(:) + real(rkind), pointer :: stateVecPrime(:) integer(i4b) :: retval diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index a25d5090a..e04f36fca 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -64,9 +64,9 @@ module getVectorz_module ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (dp) + var_dlength ! data vector with variable length dimension (rkind) ! provide access to indices that define elements of the data structures USE var_lookup,only:iLookDIAG ! named variables for structure elements diff --git a/build/source/engine/groundwatr.f90 b/build/source/engine/groundwatr.f90 index d4b5a12a1..33dda5296 100644 --- a/build/source/engine/groundwatr.f90 +++ b/build/source/engine/groundwatr.f90 @@ -28,8 +28,8 @@ module groundwatr_module ! derived types to define the data structures USE data_types,only:& - var_d, & ! data vector (dp) - var_dlength ! data vector with variable length dimension (dp) + var_d, & ! data vector (rkind) + var_dlength ! data vector with variable length dimension (rkind) ! named variables defining elements in the data structures USE var_lookup,only:iLookATTR ! named variables for structure elements diff --git a/build/source/engine/layerDivide.f90 b/build/source/engine/layerDivide.f90 index c30911f6a..ec9b2407f 100644 --- a/build/source/engine/layerDivide.f90 +++ b/build/source/engine/layerDivide.f90 @@ -41,9 +41,9 @@ module layerDivide_module ! access the derived types to define the data structures USE data_types,only:& - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions ! access named variables defining elements in the data structures @@ -101,8 +101,8 @@ subroutine needDivideLayer(& type(model_options),intent(in) :: model_decisions(:) ! model decisions type(var_dlength),intent(in) :: mpar_data ! model parameters integer(i4b),intent(in) :: nSnow ! number of snow layers - real(dp),intent(in) :: mLayerDepth(:) - real(dp),intent(in) :: scalarSnowDepth + real(rkind),intent(in) :: mLayerDepth(:) + real(rkind),intent(in) :: scalarSnowDepth ! output logical(lgt),intent(out) :: divideLayer ! flag to denote that a layer was divided integer(i4b),intent(out) :: err ! error code @@ -111,9 +111,9 @@ subroutine needDivideLayer(& ! define local variables integer(i4b) :: iLayer ! layer index integer(i4b) :: jLayer ! layer index - real(dp),dimension(4) :: zmax_lower ! lower value of maximum layer depth - real(dp),dimension(4) :: zmax_upper ! upper value of maximum layer depth - real(dp) :: zmaxCheck ! value of zmax for a given snow layer + real(rkind),dimension(4) :: zmax_lower ! lower value of maximum layer depth + real(rkind),dimension(4) :: zmax_upper ! upper value of maximum layer depth + real(rkind) :: zmaxCheck ! value of zmax for a given snow layer integer(i4b) :: nCheck ! number of layers to check to divide logical(lgt) :: createLayer ! flag to indicate we are creating a new snow layer ! -------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/layerDivideFida.f90 b/build/source/engine/layerDivideFida.f90 index 3050eabff..15626b0d3 100644 --- a/build/source/engine/layerDivideFida.f90 +++ b/build/source/engine/layerDivideFida.f90 @@ -41,9 +41,9 @@ module layerDivideFida_module ! access the derived types to define the data structures USE data_types,only:& - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions ! access named variables defining elements in the data structures @@ -117,21 +117,21 @@ subroutine layerDivideFida(& integer(i4b) :: nLayers ! total number of layers integer(i4b) :: iLayer ! layer index integer(i4b) :: jLayer ! layer index - real(dp),dimension(4) :: zmax_lower ! lower value of maximum layer depth - real(dp),dimension(4) :: zmax_upper ! upper value of maximum layer depth - real(dp) :: zmaxCheck ! value of zmax for a given snow layer + real(rkind),dimension(4) :: zmax_lower ! lower value of maximum layer depth + real(rkind),dimension(4) :: zmax_upper ! upper value of maximum layer depth + real(rkind) :: zmaxCheck ! value of zmax for a given snow layer integer(i4b) :: nCheck ! number of layers to check to divide logical(lgt) :: createLayer ! flag to indicate we are creating a new snow layer - real(dp) :: depthOriginal ! original layer depth before sub-division (m) - real(dp),parameter :: fracTop=0.5_rkind ! fraction of old layer used for the top layer - real(dp) :: surfaceLayerSoilTemp ! temperature of the top soil layer (K) - real(dp) :: maxFrozenSnowTemp ! maximum temperature when effectively all water is frozen (K) - real(dp),parameter :: unfrozenLiq=0.01_rkind ! unfrozen liquid water used to compute maxFrozenSnowTemp (-) - real(dp) :: volFracWater ! volumetric fraction of total water, liquid and ice (-) - real(dp) :: fracLiq ! fraction of liquid water (-) + real(rkind) :: depthOriginal ! original layer depth before sub-division (m) + real(rkind),parameter :: fracTop=0.5_rkind ! fraction of old layer used for the top layer + real(rkind) :: surfaceLayerSoilTemp ! temperature of the top soil layer (K) + real(rkind) :: maxFrozenSnowTemp ! maximum temperature when effectively all water is frozen (K) + real(rkind),parameter :: unfrozenLiq=0.01_rkind ! unfrozen liquid water used to compute maxFrozenSnowTemp (-) + real(rkind) :: volFracWater ! volumetric fraction of total water, liquid and ice (-) + real(rkind) :: fracLiq ! fraction of liquid water (-) integer(i4b),parameter :: ixVisible=1 ! named variable to define index in array of visible part of the spectrum integer(i4b),parameter :: ixNearIR=2 ! named variable to define index in array of near IR part of the spectrum - real(dp),parameter :: verySmall=1.e-10_rkind ! a very small number (used for error checking) + real(rkind),parameter :: verySmall=1.e-10_rkind ! a very small number (used for error checking) ! -------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="layerDivideFida/" @@ -387,7 +387,7 @@ subroutine addModelLayer(dataStruct,metaStruct,ix_divide,nSnow,nLayers,err,messa integer(i4b) :: ix_lower ! lower bound of the vector integer(i4b) :: ix_upper ! upper bound of the vector logical(lgt) :: stateVariable ! .true. if variable is a state variable - real(dp),allocatable :: tempVec_rkind(:) ! temporary vector (double precision) + real(rkind),allocatable :: tempVec_rkind(:) ! temporary vector (double precision) integer(i4b),allocatable :: tempVec_i4b(:) ! temporary vector (integer) character(LEN=256) :: cmessage ! error message of downwind routine ! --------------------------------------------------------------------------------------------- diff --git a/build/source/engine/layerMerge.f90 b/build/source/engine/layerMerge.f90 index da0e32592..b5b5276ea 100644 --- a/build/source/engine/layerMerge.f90 +++ b/build/source/engine/layerMerge.f90 @@ -41,9 +41,9 @@ module layerMerge_module ! access the derived types to define the data structures USE data_types,only:& - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions ! access named variables defining elements in the data structures @@ -89,14 +89,14 @@ subroutine needMergeLayers(& type(model_options),intent(in) :: model_decisions(:) ! model decisions type(var_dlength),intent(in) :: mpar_data ! model parameters integer(i4b),intent(in) :: nSnow - real(dp),intent(in) :: mLayerDepth(:) ! model prognostic variables for a local HRU + real(rkind),intent(in) :: mLayerDepth(:) ! model prognostic variables for a local HRU ! output logical(lgt),intent(out) :: mergedLayers ! flag to denote that layers were merged integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------- ! define local variables - real(dp),dimension(5) :: zminLayer ! minimum layer depth in each layer (m) + real(rkind),dimension(5) :: zminLayer ! minimum layer depth in each layer (m) logical(lgt) :: removeLayer ! flag to indicate need to remove a layer integer(i4b) :: nCheck ! number of layers to check for combination integer(i4b) :: iSnow ! index of snow layers (looping) diff --git a/build/source/engine/left_overs/convTestFida.f90 b/build/source/engine/left_overs/convTestFida.f90 deleted file mode 100644 index 45cdfc39f..000000000 --- a/build/source/engine/left_overs/convTestFida.f90 +++ /dev/null @@ -1,236 +0,0 @@ - - -module convTestFida_module - - - !======= Inclusions =========== - use, intrinsic :: iso_c_binding - use nrtype - use fida_datatypes - USE globalData,only:model_decisions ! model decision structure - USE globalData,only:flux_meta ! metadata on the model fluxes - ! access missing values - USE globalData,only:integerMissing ! missing integer - USE globalData,only:realMissing ! missing double precision number - - ! indices that define elements of the data structures - USE var_lookup,only:iLookPARAM ! named variables for structure elements - USE var_lookup,only:iLookPROG ! named variables for structure elements - USE var_lookup,only:iLookINDEX ! named variables for structure elements - - USE multiconst,only:& - Cp_air, & ! specific heat of air (J kg-1 K-1) - iden_air, & ! intrinsic density of air (kg m-3) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water ! intrinsic density of liquid water (kg m-3) - - ! provide access to the derived types to define the data structures - USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) - model_options ! defines the model decisions - - - - ! privacy - implicit none - private::checkConvFida - public::convTestFida - - -contains - - ! ********************************************************************************************************** - ! public function convTestFida: - ! ********************************************************************************************************** - ! Return values: - ! 0 = success, - ! -1 = fail - ! ---------------------------------------------------------------- - integer(c_int) function convTestFida(NLS, sunvec_ycor, sunvec_del, tol, sunvec_ewt, user_data) & - result(ierr) bind(C,name='convTestFida') - - !======= Inclusions =========== - use, intrinsic :: iso_c_binding - use fida_mod ! Fortran interface to IDA - use fnvector_serial_mod ! Fortran interface to serial N_Vector - use fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix - use fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver - use fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix - use fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver - use fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver - use fsundials_matrix_mod ! Fortran interface to generic SUNMatrix - use fsundials_nvector_mod ! Fortran interface to generic N_Vector - use fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver - use fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver - use nrtype - use fida_datatypes - - - !======= Declarations ========= - implicit none - - ! calling variables - type(SUNNonLinearSolver) :: NLS - type(N_Vector) :: sunvec_ycor - type(N_Vector) :: sunvec_del - real(dp), value :: tol - type(N_Vector) :: sunvec_ewt - type(c_ptr), value :: user_data - - - - ! pointers to data in SUNDIALS vectors - type(eqnsData), pointer :: eqns_data ! equations data - real(dp), pointer :: xVec(:) - real(dp), pointer :: xInc(:) - logical(lgt) :: isConv - integer(i4b) :: mSoil ! number of soil layers in solution vector - - ! get equations data from user-defined data - call c_f_pointer(user_data, eqns_data) - xVec => FN_VGetArrayPointer(sunvec_ycor) - xInc => FN_VGetArrayPointer(sunvec_del) - - ! get the number of soil layers in the solution vector - mSoil = size(eqns_data%indx_data%var(iLookINDEX%ixMatOnly)%dat) - - isConv = .true. - - - call checkConvFida(xVec,xInc,eqns_data%nSnow,eqns_data%resVec,eqns_data%mpar_data,eqns_data%prog_data,eqns_data%indx_data,eqns_data%scalarSolution,msoil,isConv) - - ! convergence check - if(isConv)then - ierr = 0 - else - ierr = SUN_NLS_CONTINUE - endif - - return - - end function convTestFida - - ! ********************************************************************************************************* - ! private subroutine checkConvFida: check convergence based on the residual vector - ! ********************************************************************************************************* - - subroutine checkConvFida(xVec,xInc,nSnow,resVec,mpar_data,prog_data,indx_data,scalarSolution,msoil,isConv) - - implicit none - - real(dp),intent(in) :: xVec(:) - real(dp),intent(in) :: xInc(:) - integer(i4b),intent(in) :: nSnow - real(qp),intent(in) :: resVec(:) ! residual vector - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - integer(i4b),intent(in) :: mSoil ! number of soil layers in solution vector - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - logical(lgt),intent(out) :: isConv - ! locals - real(dp),dimension(mSoil) :: psiScale ! scaling factor for matric head - real(dp),parameter :: xSmall=1.e-0_dp ! a small offset - real(dp),parameter :: scalarTighten=0.1_dp ! scaling factor for the scalar solution - real(dp) :: soilWatbalErr ! error in the soil water balance - real(dp) :: canopy_max ! absolute value of the residual in canopy water (kg m-2) - real(dp),dimension(1) :: energy_max ! maximum absolute value of the energy residual (J m-3) - real(dp),dimension(1) :: liquid_max ! maximum absolute value of the volumetric liquid water content residual (-) - real(dp),dimension(1) :: matric_max ! maximum absolute value of the matric head iteration increment (m) - real(dp) :: aquifer_max ! absolute value of the residual in aquifer water (m) - logical(lgt) :: canopyConv ! flag for canopy water balance convergence - logical(lgt) :: watbalConv ! flag for soil water balance convergence - logical(lgt) :: liquidConv ! flag for residual convergence - logical(lgt) :: matricConv ! flag for matric head convergence - logical(lgt) :: energyConv ! flag for energy convergence - logical(lgt) :: aquiferConv ! flag for aquifer water balance convergence - - real(dp) :: absTolWatVeg = 1e-6 - real(dp) :: absTolWatSnowSoil = 1e-6 - real(dp) :: relTolMatric = 1e-7 - real(dp) :: absTolAquifr = 1e-6 - real(dp) :: absTolWatBal = 1e-6 - real(dp) :: absTolEerngy = 1e-2 - ! ------------------------------------------------------------------------------------------------------------------------------------------------- - ! association to variables in the data structures - - ! ------------------------------------------------------------------------------------------------------------------------------------------------- - - ! check convergence based on the canopy water balance - if(indx_data%var(iLookINDEX%ixVegHyd)%dat(1)/=integerMissing)then - canopy_max = real(abs(resVec(indx_data%var(iLookINDEX%ixVegHyd)%dat(1))), dp)*iden_water - canopyConv = (canopy_max < absTolWatVeg) ! absolute error in canopy water balance (mm) - else - canopy_max = realMissing - canopyConv = .true. - endif - - ! check convergence based on the residuals for energy (J m-3) - if(size(indx_data%var(iLookINDEX%ixNrgOnly)%dat)>0)then - energy_max = real(maxval(abs( resVec(indx_data%var(iLookINDEX%ixNrgOnly)%dat) )), dp) - energyConv = (energy_max(1) < absTolEerngy) ! (based on the residual) - else - energy_max = realMissing - energyConv = .true. - endif - - ! check convergence based on the residuals for volumetric liquid water content (-) - if(size(indx_data%var(iLookINDEX%ixHydOnly)%dat)>0)then - liquid_max = real(maxval(abs( resVec(indx_data%var(iLookINDEX%ixHydOnly)%dat) ) ), dp) - ! (tighter convergence for the scalar solution) - if(scalarSolution)then - liquidConv = (liquid_max(1) < absTolWatSnowSoil * scalarTighten) ! (based on the residual) - else - liquidConv = (liquid_max(1) < absTolWatSnowSoil) ! (based on the residual) - endif - else - liquid_max = realMissing - liquidConv = .true. - endif - - ! check convergence based on the iteration increment for matric head - ! NOTE: scale by matric head to avoid unnecessairly tight convergence when there is no water - if(size(indx_data%var(iLookINDEX%ixMatOnly)%dat)>0)then - psiScale = abs( xVec(indx_data%var(iLookINDEX%ixMatOnly)%dat) ) + xSmall ! avoid divide by zero - matric_max = maxval(abs( xInc(indx_data%var(iLookINDEX%ixMatOnly)%dat)/psiScale ) ) - matricConv = (matric_max(1) < relTolMatric) ! NOTE: based on iteration increment - else - matric_max = realMissing - matricConv = .true. - endif - - ! check convergence based on the soil water balance error (m) - if(size(indx_data%var(iLookINDEX%ixMatOnly)%dat)>0)then - soilWatBalErr = sum( real(resVec(indx_data%var(iLookINDEX%ixMatOnly)%dat), dp)*prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow + indx_data%var(iLookINDEX%ixMatricHead)%dat ) ) - watbalConv = (abs(soilWatbalErr) < absTolWatBal) ! absolute error in total soil water balance (m) - else - soilWatbalErr = realMissing - watbalConv = .true. - endif - - ! check convergence based on the aquifer storage - if(indx_data%var(iLookINDEX%ixAqWat)%dat(1)/=integerMissing)then - aquifer_max = real(abs(resVec(indx_data%var(iLookINDEX%ixAqWat)%dat(1))), dp)*iden_water - aquiferConv = (aquifer_max < absTolAquifr) ! absolute error in aquifer water balance (mm) - else - aquifer_max = realMissing - aquiferConv = .true. - endif - -! print *, 'canopyConv = ', canopyConv -! print *, 'matricConv = ', matricConv -! print *, 'liquidConv = ', liquidConv -! print *, 'energyConv = ', energyConv -! print *, 'aquiferConv = ', aquiferConv -! print *, '-----------------------------------' - - isConv = (watbalConv .and. canopyConv .and. matricConv .and. liquidConv .and. energyConv .and. aquiferConv) - - - end subroutine checkConvFida - - -end module convTestFida_module diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index f736d819a..173a63bdd 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -96,11 +96,11 @@ module opSplittin_module ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_flagVec, & ! data vector with variable length dimension (i4b) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) - zLookup, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions ! look-up values for the choice of groundwater representation (local-column, or single-basin) diff --git a/build/source/engine/paramCheck.f90 b/build/source/engine/paramCheck.f90 index 0be4f5aca..03fec6a6a 100644 --- a/build/source/engine/paramCheck.f90 +++ b/build/source/engine/paramCheck.f90 @@ -39,7 +39,7 @@ subroutine paramCheck(mpar_data,err,message) USE globalData,only:model_decisions ! model decision structure USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure ! SUMMA look-up variables - USE data_types,only:var_dlength ! data vector with variable length dimension (dp): x%var(:)%dat(:) + USE data_types,only:var_dlength ! data vector with variable length dimension (rkind): x%var(:)%dat(:) USE var_lookup,only:iLookPARAM ! named variables for elements of the data structures implicit none ! define input diff --git a/build/source/engine/printResidFida.f90 b/build/source/engine/printResidFida.f90 index 91286b811..48ab47a71 100644 --- a/build/source/engine/printResidFida.f90 +++ b/build/source/engine/printResidFida.f90 @@ -8,7 +8,7 @@ module printResidFida_module ! derived types to define the data structures USE data_types,only:& var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (dp) + var_dlength ! data vector with variable length dimension (rkind) ! named variables USE var_lookup,only:iLookPROG ! named variables for structure elements @@ -75,7 +75,7 @@ subroutine printResidFida(& integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers ! output - real(dp),intent(in) :: rAdd(:) ! additional (sink) terms on the RHS of the state equation + real(rkind),intent(in) :: rAdd(:) ! additional (sink) terms on the RHS of the state equation real(qp),intent(in) :: rVec(:) ! NOTE: qp ! residual vector ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables diff --git a/build/source/engine/read_attrb.f90 b/build/source/engine/read_attrb.f90 index 8f13ac0f9..0eba82dd2 100644 --- a/build/source/engine/read_attrb.f90 +++ b/build/source/engine/read_attrb.f90 @@ -201,7 +201,7 @@ subroutine read_attrb(attrFile,nGRU,attrStruct,typeStruct,idStruct,err,message) ! provide access to derived data types USE data_types,only:gru_hru_int ! x%gru(:)%hru(:)%var(:) (i4b) USE data_types,only:gru_hru_int8 ! x%gru(:)%hru(:)%var(:) integer(8) - USE data_types,only:gru_hru_double ! x%gru(:)%hru(:)%var(:) (dp) + USE data_types,only:gru_hru_double ! x%gru(:)%hru(:)%var(:) (rkind) ! provide access to global data USE globalData,only:gru_struc ! gru-hru mapping structure USE globalData,only:attr_meta,type_meta,id_meta ! metadata structures diff --git a/build/source/engine/read_force.f90 b/build/source/engine/read_force.f90 index fbfa51baa..f519c8c85 100644 --- a/build/source/engine/read_force.f90 +++ b/build/source/engine/read_force.f90 @@ -24,7 +24,7 @@ module read_force_module USE nrtype ! variable types, etc. ! derived data types -USE data_types,only:gru_hru_double ! x%gru(:)%hru(:)%var(:) (dp) +USE data_types,only:gru_hru_double ! x%gru(:)%hru(:)%var(:) (rkind) ! constants USE multiconst,only:secprday ! number of seconds in a day diff --git a/build/source/engine/run_oneGRU.f90 b/build/source/engine/run_oneGRU.f90 index 613e5d873..6d40e68ab 100644 --- a/build/source/engine/run_oneGRU.f90 +++ b/build/source/engine/run_oneGRU.f90 @@ -32,13 +32,13 @@ module run_oneGRU_module gru2hru_map, & ! HRU info ! no spatial dimension var_ilength, & ! x%var(:)%dat (i4b) - var_dlength, & ! x%var(:)%dat (dp) + var_dlength, & ! x%var(:)%dat (rkind) ! hru dimension hru_int, & ! x%hru(:)%var(:) (i4b) hru_int8, & ! x%hru(:)%var(:) integer(8) - hru_double, & ! x%hru(:)%var(:) (dp) + hru_double, & ! x%hru(:)%var(:) (rkind) hru_intVec, & ! x%hru(:)%var(:)%dat (i4b) - hru_doubleVec, & ! x%hru(:)%var(:)%dat (dp) + hru_doubleVec, & ! x%hru(:)%var(:)%dat (rkind) ! hru+z dimension hru_z_vLookup ! x%hru(:)%z(:)%var(:)%lookup(:) diff --git a/build/source/engine/run_oneHRU.f90 b/build/source/engine/run_oneHRU.f90 index f30858e0d..e2de95b16 100644 --- a/build/source/engine/run_oneHRU.f90 +++ b/build/source/engine/run_oneHRU.f90 @@ -26,10 +26,10 @@ module run_oneHRU_module ! data types USE data_types,only:& var_i, & ! x%var(:) (i4b) - var_d, & ! x%var(:) (dp) + var_d, & ! x%var(:) (rkind) var_ilength, & ! x%var(:)%dat (i4b) - var_dlength, & ! x%var(:)%dat (dp) - zLookup ! x%z(:)%var(:)%lookup(:) (dp) + var_dlength, & ! x%var(:)%dat (rkind) + zLookup ! x%z(:)%var(:)%lookup(:) (rkind) ! access vegetation data USE globalData,only:greenVegFrac_monthly ! fraction of green vegetation in each month (0-1) diff --git a/build/source/engine/snowAlbedo.f90 b/build/source/engine/snowAlbedo.f90 index c1329f36b..922fcfd01 100644 --- a/build/source/engine/snowAlbedo.f90 +++ b/build/source/engine/snowAlbedo.f90 @@ -29,8 +29,8 @@ module snowAlbedo_module ! derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) - var_dlength, & ! data vector with variable length dimension (dp) + var_d, & ! data vector (rkind) + var_dlength, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions ! named variables defining elements in the data structures diff --git a/build/source/engine/snowLiqFlx.f90 b/build/source/engine/snowLiqFlx.f90 index e7bd7a5fc..312dc440c 100644 --- a/build/source/engine/snowLiqFlx.f90 +++ b/build/source/engine/snowLiqFlx.f90 @@ -35,8 +35,8 @@ module snowLiqFlx_module USE var_lookup,only:iLookDIAG ! named variables for structure elements ! data types -USE data_types,only:var_d ! x%var(:) (dp) -USE data_types,only:var_dlength ! x%var(:)%dat (dp) +USE data_types,only:var_d ! x%var(:) (rkind) +USE data_types,only:var_dlength ! x%var(:)%dat (rkind) USE data_types,only:var_ilength ! x%var(:)%dat (i4b) ! privacy @@ -231,19 +231,19 @@ subroutine snowLiqFlxFida(& logical(lgt),intent(in) :: firstFluxCall ! the first flux call logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: forcing for the snow domain - real(dp),intent(in) :: scalarThroughfallRain ! computed throughfall rate (kg m-2 s-1) - real(dp),intent(in) :: scalarCanopyLiqDrainage ! computed drainage of liquid water (kg m-2 s-1) + real(rkind),intent(in) :: scalarThroughfallRain ! computed throughfall rate (kg m-2 s-1) + real(rkind),intent(in) :: scalarCanopyLiqDrainage ! computed drainage of liquid water (kg m-2 s-1) ! input: model state vector - real(dp),intent(in) :: mLayerVolFracIce(:) - real(dp),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value of volumetric fraction of liquid water at the current iteration (-) + real(rkind),intent(in) :: mLayerVolFracIce(:) + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value of volumetric fraction of liquid water at the current iteration (-) ! input-output: data structures type(var_ilength),intent(in) :: indx_data ! model indices type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU ! output: fluxes and derivatives - real(dp),intent(inout) :: iLayerLiqFluxSnow(0:) ! vertical liquid water flux at layer interfaces (m s-1) - real(dp),intent(inout) :: iLayerLiqFluxSnowDeriv(0:) ! derivative in vertical liquid water flux at layer interfaces (m s-1) + real(rkind),intent(inout) :: iLayerLiqFluxSnow(0:) ! vertical liquid water flux at layer interfaces (m s-1) + real(rkind),intent(inout) :: iLayerLiqFluxSnowDeriv(0:) ! derivative in vertical liquid water flux at layer interfaces (m s-1) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -253,12 +253,12 @@ subroutine snowLiqFlxFida(& integer(i4b) :: iLayer ! layer index integer(i4b) :: ixTop ! top layer in subroutine call integer(i4b) :: ixBot ! bottom layer in subroutine call - real(dp) :: multResid ! multiplier for the residual water content (-) - real(dp),parameter :: residThrs=550._rkind ! ice density threshold to reduce residual liquid water content (kg m-3) - real(dp),parameter :: residScal=10._rkind ! scaling factor for residual liquid water content reduction factor (kg m-3) - real(dp),parameter :: maxVolIceContent=0.7_rkind ! maximum volumetric ice content to store water (-) - real(dp) :: availCap ! available storage capacity [0,1] (-) - real(dp) :: relSaturn ! relative saturation [0,1] (-) + real(rkind) :: multResid ! multiplier for the residual water content (-) + real(rkind),parameter :: residThrs=550._rkind ! ice density threshold to reduce residual liquid water content (kg m-3) + real(rkind),parameter :: residScal=10._rkind ! scaling factor for residual liquid water content reduction factor (kg m-3) + real(rkind),parameter :: maxVolIceContent=0.7_rkind ! maximum volumetric ice content to store water (-) + real(rkind) :: availCap ! available storage capacity [0,1] (-) + real(rkind) :: relSaturn ! relative saturation [0,1] (-) ! ------------------------------------------------------------------------------------------------------------------------------------------ ! make association of local variables with information in the data structures associate(& diff --git a/build/source/engine/soilCmpresFida.f90 b/build/source/engine/soilCmpresFida.f90 index 4e0ad625d..ced0c8c62 100644 --- a/build/source/engine/soilCmpresFida.f90 +++ b/build/source/engine/soilCmpresFida.f90 @@ -7,9 +7,9 @@ module soilCmpresFida_module ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions ! indices that define elements of the data structures @@ -101,14 +101,14 @@ subroutine soilCmpresFida(& ! input: integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation integer(i4b),intent(in) :: ixBeg,ixEnd ! start and end indices defining desired layers - real(dp),intent(in) :: mLayerMatricHeadPrime(:) ! matric head at the start of the time step (m) - real(dp),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) - real(dp),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) - real(dp),intent(in) :: specificStorage ! specific storage coefficient (m-1) - real(dp),intent(in) :: theta_sat(:) ! soil porosity (-) + real(rkind),intent(in) :: mLayerMatricHeadPrime(:) ! matric head at the start of the time step (m) + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) + real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) + real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) ! output: - real(dp),intent(inout) :: compress(:) ! soil compressibility (-) - real(dp),intent(inout) :: dCompress_dPsi(:) ! derivative in soil compressibility w.r.t. matric head (m-1) + real(rkind),intent(inout) :: compress(:) ! soil compressibility (-) + real(rkind),intent(inout) :: dCompress_dPsi(:) ! derivative in soil compressibility w.r.t. matric head (m-1) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local variables diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index 30a5cda2b..c036a8c89 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -23,9 +23,9 @@ module soilLiqFlx_module ! data types USE nrtype -USE data_types,only:var_d ! x%var(:) (dp) +USE data_types,only:var_d ! x%var(:) (rkind) USE data_types,only:var_ilength ! x%var(:)%dat (i4b) -USE data_types,only:var_dlength ! x%var(:)%dat (dp) +USE data_types,only:var_dlength ! x%var(:)%dat (rkind) ! missing values USE globalData,only:integerMissing ! missing integer diff --git a/build/source/engine/soil_utils_fida.f90 b/build/source/engine/soil_utils_fida.f90 index 44b1c2c9f..0c62bb893 100644 --- a/build/source/engine/soil_utils_fida.f90 +++ b/build/source/engine/soil_utils_fida.f90 @@ -43,7 +43,7 @@ module soil_utils_fida_module public::d2Theta_dTk2 ! constant parameters -real(dp),parameter :: verySmall=epsilon(1.0_rkind) ! a very small number (used to avoid divide by zero) +real(rkind),parameter :: verySmall=epsilon(1.0_rkind) ! a very small number (used to avoid divide by zero) contains @@ -71,31 +71,31 @@ subroutine liquidHeadFida(& ! computes the liquid water matric potential (and the derivatives w.r.t. total matric potential and temperature) implicit none ! input - real(dp),intent(in) :: matricHeadTotal ! total water matric potential (m) - real(dp),intent(in) :: matricHeadTotalPrime - real(dp),intent(in) :: volFracLiq ! volumetric fraction of liquid water (-) - real(dp),intent(in) :: volFracIce ! volumetric fraction of ice (-) - real(dp),intent(in) :: vGn_alpha,vGn_n,theta_sat,theta_res,vGn_m ! soil parameters - real(dp),intent(in) ,optional :: dVolTot_dPsi0 ! derivative in the soil water characteristic (m-1) - real(dp),intent(in) ,optional :: dTheta_dT ! derivative in volumetric total water w.r.t. temperature (K-1) - real(dp),intent(in) :: TempPrime - real(dp),intent(in) :: volFracLiqPrime - real(dp),intent(in) :: volFracIcePrime + real(rkind),intent(in) :: matricHeadTotal ! total water matric potential (m) + real(rkind),intent(in) :: matricHeadTotalPrime + real(rkind),intent(in) :: volFracLiq ! volumetric fraction of liquid water (-) + real(rkind),intent(in) :: volFracIce ! volumetric fraction of ice (-) + real(rkind),intent(in) :: vGn_alpha,vGn_n,theta_sat,theta_res,vGn_m ! soil parameters + real(rkind),intent(in) ,optional :: dVolTot_dPsi0 ! derivative in the soil water characteristic (m-1) + real(rkind),intent(in) ,optional :: dTheta_dT ! derivative in volumetric total water w.r.t. temperature (K-1) + real(rkind),intent(in) :: TempPrime + real(rkind),intent(in) :: volFracLiqPrime + real(rkind),intent(in) :: volFracIcePrime ! output - real(dp),intent(out) :: matricHeadLiq ! liquid water matric potential (m) - real(dp),intent(out) :: matricHeadLiqPrime - real(dp),intent(out) ,optional :: dPsiLiq_dPsi0 ! derivative in the liquid water matric potential w.r.t. the total water matric potential (-) - real(dp),intent(out) ,optional :: dPsiLiq_dTemp ! derivative in the liquid water matric potential w.r.t. temperature (m K-1) + real(rkind),intent(out) :: matricHeadLiq ! liquid water matric potential (m) + real(rkind),intent(out) :: matricHeadLiqPrime + real(rkind),intent(out) ,optional :: dPsiLiq_dPsi0 ! derivative in the liquid water matric potential w.r.t. the total water matric potential (-) + real(rkind),intent(out) ,optional :: dPsiLiq_dTemp ! derivative in the liquid water matric potential w.r.t. temperature (m K-1) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local - real(dp) :: xNum,xDen ! temporary variables (numeratir, denominator) - real(dp) :: effSat ! effective saturation (-) - real(dp) :: dPsiLiq_dEffSat ! derivative in liquid water matric potential w.r.t. effective saturation (m) - real(dp) :: dEffSat_dTemp ! derivative in effective saturation w.r.t. temperature (K-1) - real(dp) :: dEffSat_dFracLiq - real(dp) :: effSatPrime + real(rkind) :: xNum,xDen ! temporary variables (numeratir, denominator) + real(rkind) :: effSat ! effective saturation (-) + real(rkind) :: dPsiLiq_dEffSat ! derivative in liquid water matric potential w.r.t. effective saturation (m) + real(rkind) :: dEffSat_dTemp ! derivative in effective saturation w.r.t. temperature (K-1) + real(rkind) :: dEffSat_dFracLiq + real(rkind) :: effSatPrime ! ------------------------------------------------------------------------------------------------------------------------------ ! initialize error control err=0; message='liquidHeadFida/' @@ -185,15 +185,15 @@ end subroutine liquidHeadFida ! ****************************************************************************************************************************** function d2Theta_dPsi2(psi,alpha,theta_res,theta_sat,n,m) implicit none - real(dp),intent(in) :: psi ! soil water suction (m) - real(dp),intent(in) :: alpha ! scaling parameter (m-1) - real(dp),intent(in) :: theta_res ! residual volumetric water content (-) - real(dp),intent(in) :: theta_sat ! porosity (-) - real(dp),intent(in) :: n ! vGn "n" parameter (-) - real(dp),intent(in) :: m ! vGn "m" parameter (-) - real(dp) :: d2Theta_dPsi2 ! derivative of the soil water characteristic (m-1) - real(dp) :: mult_fcn - real(dp) :: mult_fcnp + real(rkind),intent(in) :: psi ! soil water suction (m) + real(rkind),intent(in) :: alpha ! scaling parameter (m-1) + real(rkind),intent(in) :: theta_res ! residual volumetric water content (-) + real(rkind),intent(in) :: theta_sat ! porosity (-) + real(rkind),intent(in) :: n ! vGn "n" parameter (-) + real(rkind),intent(in) :: m ! vGn "m" parameter (-) + real(rkind) :: d2Theta_dPsi2 ! derivative of the soil water characteristic (m-1) + real(rkind) :: mult_fcn + real(rkind) :: mult_fcnp if(psi<0._rkind)then mult_fcn = (-m*n*alpha*(alpha*psi)**(n-1._rkind)) * ( 1._rkind + (psi*alpha)**n )**(-1._rkind) mult_fcnp = -m*n*alpha*(n-1._rkind)*alpha*(alpha*psi)**(n-2._rkind)*( 1._rkind + (psi*alpha)**n )**(-1._rkind) - & @@ -211,16 +211,16 @@ end function d2Theta_dPsi2 ! ****************************************************************************************************************************** function d2Theta_dTk2(Tk,theta_res,theta_sat,alpha,n,m) implicit none - real(dp),intent(in) :: Tk ! temperature (K) - real(dp),intent(in) :: theta_res ! residual liquid water content (-) - real(dp),intent(in) :: theta_sat ! porosity (-) - real(dp),intent(in) :: alpha ! vGn scaling parameter (m-1) - real(dp),intent(in) :: n ! vGn "n" parameter (-) - real(dp),intent(in) :: m ! vGn "m" parameter (-) - real(dp) :: d2Theta_dTk2 ! derivative of the freezing curve w.r.t. temperature (K-1) + real(rkind),intent(in) :: Tk ! temperature (K) + real(rkind),intent(in) :: theta_res ! residual liquid water content (-) + real(rkind),intent(in) :: theta_sat ! porosity (-) + real(rkind),intent(in) :: alpha ! vGn scaling parameter (m-1) + real(rkind),intent(in) :: n ! vGn "n" parameter (-) + real(rkind),intent(in) :: m ! vGn "m" parameter (-) + real(rkind) :: d2Theta_dTk2 ! derivative of the freezing curve w.r.t. temperature (K-1) ! local variables - real(dp) :: kappa ! constant (m K-1) - real(dp) :: xtemp ! alpha*kappa*(Tk-Tfreeze) -- dimensionless variable (used more than once) + real(rkind) :: kappa ! constant (m K-1) + real(rkind) :: xtemp ! alpha*kappa*(Tk-Tfreeze) -- dimensionless variable (used more than once) ! compute kappa (m K-1) kappa = LH_fus/(gravity*Tfreeze) ! NOTE: J = kg m2 s-2 ! define a tempory variable that is used more than once (-) diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index 8ecb188d7..a988d4ef0 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -24,8 +24,8 @@ module ssdNrgFlux_module USE nrtype ! data types -USE data_types,only:var_d ! x%var(:) (dp) -USE data_types,only:var_dlength ! x%var(:)%dat (dp) +USE data_types,only:var_d ! x%var(:) (rkind) +USE data_types,only:var_dlength ! x%var(:)%dat (rkind) USE data_types,only:var_ilength ! x%var(:)%dat (i4b) ! physical constants diff --git a/build/source/engine/stomResist.f90 b/build/source/engine/stomResist.f90 index cf852679d..1934681e3 100644 --- a/build/source/engine/stomResist.f90 +++ b/build/source/engine/stomResist.f90 @@ -31,8 +31,8 @@ module stomResist_module ! derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) - var_dlength, & ! data vector with variable length dimension (dp) + var_d, & ! data vector (rkind) + var_dlength, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions ! indices that define elements of the data structures diff --git a/build/source/engine/summaSolve.f90 b/build/source/engine/summaSolve.f90 index d7aaa4f58..a8955bb0d 100644 --- a/build/source/engine/summaSolve.f90 +++ b/build/source/engine/summaSolve.f90 @@ -64,10 +64,10 @@ module summaSolve_module ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) - zLookup, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions ! look-up values for the choice of groundwater parameterization diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/sysSolvFida.f90 index 46318afdd..4149ae4e5 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/sysSolvFida.f90 @@ -59,10 +59,10 @@ module sysSolvFida_module ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) - zLookup, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions ! look-up values for the choice of groundwater representation (local-column, or single-basin) @@ -83,10 +83,10 @@ module sysSolvFida_module public::sysSolvFida ! control parameters -real(dp),parameter :: valueMissing=-9999._rkind ! missing value -real(dp),parameter :: verySmall=1.e-12_rkind ! a very small number (used to check consistency) -real(dp),parameter :: veryBig=1.e+20_rkind ! a very big number -real(dp),parameter :: dx = 1.e-8_rkind ! finite difference increment +real(rkind),parameter :: valueMissing=-9999._rkind ! missing value +real(rkind),parameter :: verySmall=1.e-12_rkind ! a very small number (used to check consistency) +real(rkind),parameter :: veryBig=1.e+20_rkind ! a very big number +real(rkind),parameter :: dx = 1.e-8_rkind ! finite difference increment contains @@ -143,7 +143,7 @@ subroutine sysSolvFida(& ! * dummy variables ! --------------------------------------------------------------------------------------- ! input: model control - real(dp),intent(in) :: dt ! time step (seconds) + real(rkind),intent(in) :: dt ! time step (seconds) integer(i4b),intent(in) :: nState ! total number of state variables logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call @@ -162,12 +162,12 @@ subroutine sysSolvFida(& type(var_dlength),intent(inout) :: flux_temp ! model fluxes for a local HRU type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin type(model_options),intent(in) :: model_decisions(:) ! model decisions - real(dp),intent(in) :: stateVecInit(:) ! initial state vector (mixed units) + real(rkind),intent(in) :: stateVecInit(:) ! initial state vector (mixed units) ! output: model control type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(dp),intent(out) :: stateVecTrial(:) ! trial state vector (mixed units) - real(dp),intent(out) :: stateVecPrime(:) ! trial state vector (mixed units) + real(rkind),intent(out) :: stateVecTrial(:) ! trial state vector (mixed units) + real(rkind),intent(out) :: stateVecPrime(:) ! trial state vector (mixed units) logical(lgt),intent(out) :: reduceCoupledStep ! flag to reduce the length of the coupled step logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that there was too much melt real(qp),intent(out) :: dt_out @@ -181,11 +181,11 @@ subroutine sysSolvFida(& character(LEN=256) :: cmessage ! error message of downwind routine integer(i4b) :: iVar ! index of variable integer(i4b) :: local_ixGroundwater ! local index for groundwater representation - real(dp) :: bulkDensity ! bulk density of a given layer (kg m-3) - real(dp) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) - real(dp),parameter :: tempAccelerate=0.00_rkind ! factor to force initial canopy temperatures to be close to air temperature - real(dp),parameter :: xMinCanopyWater=0.0001_rkind ! minimum value to initialize canopy water (kg m-2) - real(dp),parameter :: tinyStep=0.000001_rkind ! stupidly small time step (s) + real(rkind) :: bulkDensity ! bulk density of a given layer (kg m-3) + real(rkind) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) + real(rkind),parameter :: tempAccelerate=0.00_rkind ! factor to force initial canopy temperatures to be close to air temperature + real(rkind),parameter :: xMinCanopyWater=0.0001_rkind ! minimum value to initialize canopy water (kg m-2) + real(rkind),parameter :: tinyStep=0.000001_rkind ! stupidly small time step (s) integer(i4b),parameter :: ixRectangular=1 integer(i4b),parameter :: ixTrapezoidal=2 @@ -196,24 +196,24 @@ subroutine sysSolvFida(& integer(i4b) :: ixQuadrature=ixRectangular ! type of quadrature method to approximate average fluxes integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) type(var_dlength) :: flux_init ! model fluxes at the start of the time step - real(dp),allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! NOTE: allocatable, since not always needed - real(dp) :: stateVecNew(nState) ! new state vector (mixed units) - real(dp) :: fluxVec0(nState) ! flux vector (mixed units) - real(dp) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) - real(dp) :: xScale(nState) ! characteristic scale of the state vector (mixed units) - real(dp) :: dMat(nState) ! diagonal matrix (excludes flux derivatives) + real(rkind),allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! NOTE: allocatable, since not always needed + real(rkind) :: stateVecNew(nState) ! new state vector (mixed units) + real(rkind) :: fluxVec0(nState) ! flux vector (mixed units) + real(rkind) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) + real(rkind) :: xScale(nState) ! characteristic scale of the state vector (mixed units) + real(rkind) :: dMat(nState) ! diagonal matrix (excludes flux derivatives) real(qp) :: sMul(nState) ! NOTE: qp ! multiplier for state vector for the residual calculations real(qp) :: rVec(nState) ! NOTE: qp ! residual vector - real(dp) :: rAdd(nState) ! additional terms in the residual vector - real(dp) :: fOld ! function values (-); NOTE: dimensionless because scaled + real(rkind) :: rAdd(nState) ! additional terms in the residual vector + real(rkind) :: fOld ! function values (-); NOTE: dimensionless because scaled logical(lgt) :: feasible ! feasibility flag - real(dp) :: dt_last(1) ! last stepsize taken by ida solver + real(rkind) :: dt_last(1) ! last stepsize taken by ida solver real(qp) :: dt_past ! one step before the last stepsize taken by ida solver - real(dp) :: atol(nState) ! absolute telerance - real(dp) :: rtol(nState) ! relative tolerance + real(rkind) :: atol(nState) ! absolute telerance + real(rkind) :: rtol(nState) ! relative tolerance type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a data step integer(i4b) :: tol_iter ! iteration index - real(dp), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix + real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 18c9d8e80..256ce8481 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -76,10 +76,10 @@ module systemSolv_module ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) - zLookup, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions ! look-up values for the choice of groundwater representation (local-column, or single-basin) diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index 66ffac256..f16b6f777 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -91,26 +91,26 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number of logical(lgt),parameter :: doTest=.false. ! flag to test integer(i4b),parameter :: nLook=100 ! number of elements in the lookup table integer(i4b),parameter :: nIntegr8=10000 ! number of points used in the numerical integration - real(dp),parameter :: T_lower=260.0_rkind ! lowest temperature value where all liquid water is assumed frozen (K) - real(dp),dimension(nLook) :: xTemp ! temporary vector - real(dp) :: xIncr ! temporary increment - real(dp) :: T_incr ! temperature increment - real(dp),parameter :: T_test=272.9742_rkind ! test value for temperature (K) - real(dp) :: E_test ! test value for enthalpy (J m-3) + real(rkind),parameter :: T_lower=260.0_rkind ! lowest temperature value where all liquid water is assumed frozen (K) + real(rkind),dimension(nLook) :: xTemp ! temporary vector + real(rkind) :: xIncr ! temporary increment + real(rkind) :: T_incr ! temperature increment + real(rkind),parameter :: T_test=272.9742_rkind ! test value for temperature (K) + real(rkind) :: E_test ! test value for enthalpy (J m-3) integer(i4b) :: iVar ! loop through variables integer(i4b) :: iSoil ! loop through soil layers integer(i4b) :: iLook ! loop through lookup table integer(i4b) :: jIntegr8 ! index for numerical integration logical(lgt) :: check ! flag to check allocation - real(dp) :: vGn_m ! van Genuchten "m" parameter (-) - real(dp) :: vFracLiq ! volumetric fraction of liquid water (-) - real(dp) :: vFracIce ! volumetric fraction of ice (-) - real(dp) :: matricHead ! matric head (m) + real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) + real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) + real(rkind) :: vFracIce ! volumetric fraction of ice (-) + real(rkind) :: matricHead ! matric head (m) ! initialize error control err=0; message="T2E_lookup/" ! get the values of temperature for the lookup table - xIncr = 1._rkind/real(nLook-1, kind(dp)) + xIncr = 1._rkind/real(nLook-1, kind(rkind)) xTemp = T_lower + (Tfreeze - T_lower)*arth(0._rkind,xIncr,nLook)**0.25_rkind ! use **0.25 to give more values near freezing ! ----- @@ -183,7 +183,7 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number of Ey(iLook) = Ey(iLook+1) ! get the temperature increment for the numerical integration - T_incr = (xTemp(iLook)-xTemp(iLook+1))/real(nIntegr8, kind(dp)) + T_incr = (xTemp(iLook)-xTemp(iLook+1))/real(nIntegr8, kind(rkind)) ! numerical integration between different values of the lookup table do jIntegr8=1,nIntegr8 @@ -281,19 +281,19 @@ subroutine t2enthalpy(& type(var_ilength),intent(in) :: indx_data ! model indices type(zLookup),intent(in) :: lookup_data ! lookup tables ! input: state variables for the vegetation canopy - real(dp),intent(in) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) - real(dp),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(dp),intent(in) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - real(dp),intent(in) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + real(rkind),intent(in) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) + real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) ! input: variables for the snow-soil domain - real(dp),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(dp),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(dp),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(dp),intent(in) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric fraction of Ice (-) + real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric fraction of Ice (-) ! output: enthalpy - real(dp),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - real(dp),intent(out) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) - real(dp),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) + real(rkind),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) + real(rkind),intent(out) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -305,24 +305,24 @@ subroutine t2enthalpy(& integer(i4b) :: ixFullVector ! index within full state vector integer(i4b) :: ixDomainType ! name of a given model domain integer(i4b) :: ixControlIndex ! index within a given model domain - real(dp) :: vGn_m ! van Genuchten "m" parameter (-) - real(dp) :: Tcrit ! temperature where all water is unfrozen (K) - real(dp) :: psiLiq ! matric head of liquid water (m) - real(dp) :: vFracWat ! volumetric fraction of total water, liquid+ice (-) - real(dp) :: vFracLiq ! volumetric fraction of liquid water (-) - real(dp) :: vFracIce ! volumetric fraction of ice (-) - real(dp) :: enthTemp ! enthalpy at the temperature of the control volume (J m-3) - real(dp) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) - real(dp) :: enthPhase ! enthalpy associated with phase change (J m-3) - real(dp) :: enthWater ! enthalpy of total water (J m-3) - real(dp) :: enthSoil ! enthalpy of soil particles (J m-3) - real(dp) :: enthMix ! enthalpy of the mixed region, liquid+ice (J m-3) - real(dp) :: enthLiq ! enthalpy of the liquid region (J m-3) - real(dp) :: enthAir ! enthalpy of air (J m-3) - real(dp) :: diffT - real(dp) :: integral - real(dp) :: enthIce - real(dp) :: enthVeg + real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind) :: psiLiq ! matric head of liquid water (m) + real(rkind) :: vFracWat ! volumetric fraction of total water, liquid+ice (-) + real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) + real(rkind) :: vFracIce ! volumetric fraction of ice (-) + real(rkind) :: enthTemp ! enthalpy at the temperature of the control volume (J m-3) + real(rkind) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) + real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) + real(rkind) :: enthWater ! enthalpy of total water (J m-3) + real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) + real(rkind) :: enthMix ! enthalpy of the mixed region, liquid+ice (J m-3) + real(rkind) :: enthLiq ! enthalpy of the liquid region (J m-3) + real(rkind) :: enthAir ! enthalpy of air (J m-3) + real(rkind) :: diffT + real(rkind) :: integral + real(rkind) :: enthIce + real(rkind) :: enthVeg ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures generalVars: associate(& @@ -560,19 +560,19 @@ subroutine t2enthalpy_T(& type(var_ilength),intent(in) :: indx_data ! model indices type(zLookup),intent(in) :: lookup_data ! lookup tables ! input: state variables for the vegetation canopy - real(dp),intent(in) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) - real(dp),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(dp),intent(in) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - real(dp),intent(in) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + real(rkind),intent(in) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) + real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) ! input: variables for the snow-soil domain - real(dp),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(dp),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(dp),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(dp),intent(in) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric fraction of Ice (-) + real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric fraction of Ice (-) ! output: enthalpy - real(dp),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - real(dp),intent(out) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) - real(dp),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) + real(rkind),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) + real(rkind),intent(out) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -584,24 +584,24 @@ subroutine t2enthalpy_T(& integer(i4b) :: ixFullVector ! index within full state vector integer(i4b) :: ixDomainType ! name of a given model domain integer(i4b) :: ixControlIndex ! index within a given model domain - real(dp) :: vGn_m ! van Genuchten "m" parameter (-) - real(dp) :: Tcrit ! temperature where all water is unfrozen (K) - real(dp) :: psiLiq ! matric head of liquid water (m) - real(dp) :: vFracWat ! volumetric fraction of total water, liquid+ice (-) - real(dp) :: vFracLiq ! volumetric fraction of liquid water (-) - real(dp) :: vFracIce ! volumetric fraction of ice (-) - real(dp) :: enthTemp ! enthalpy at the temperature of the control volume (J m-3) - real(dp) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) - real(dp) :: enthPhase ! enthalpy associated with phase change (J m-3) - real(dp) :: enthWater ! enthalpy of total water (J m-3) - real(dp) :: enthSoil ! enthalpy of soil particles (J m-3) - real(dp) :: enthMix ! enthalpy of the mixed region, liquid+ice (J m-3) - real(dp) :: enthLiq ! enthalpy of the liquid region (J m-3) - real(dp) :: enthAir ! enthalpy of air (J m-3) - real(dp) :: diffT - real(dp) :: integral - real(dp) :: enthIce - real(dp) :: enthVeg + real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind) :: psiLiq ! matric head of liquid water (m) + real(rkind) :: vFracWat ! volumetric fraction of total water, liquid+ice (-) + real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) + real(rkind) :: vFracIce ! volumetric fraction of ice (-) + real(rkind) :: enthTemp ! enthalpy at the temperature of the control volume (J m-3) + real(rkind) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) + real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) + real(rkind) :: enthWater ! enthalpy of total water (J m-3) + real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) + real(rkind) :: enthMix ! enthalpy of the mixed region, liquid+ice (J m-3) + real(rkind) :: enthLiq ! enthalpy of the liquid region (J m-3) + real(rkind) :: enthAir ! enthalpy of air (J m-3) + real(rkind) :: diffT + real(rkind) :: integral + real(rkind) :: enthIce + real(rkind) :: enthVeg ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures generalVars: associate(& diff --git a/build/source/engine/tempAdjust.f90 b/build/source/engine/tempAdjust.f90 index cbc69963e..87da3cbc4 100644 --- a/build/source/engine/tempAdjust.f90 +++ b/build/source/engine/tempAdjust.f90 @@ -25,8 +25,8 @@ module tempAdjust_module ! derived types to define the data structures USE data_types,only:& - var_d, & ! data vector (dp) - var_dlength ! data vector with variable length dimension (dp) + var_d, & ! data vector (rkind) + var_dlength ! data vector with variable length dimension (rkind) ! named variables defining elements in the data structures USE var_lookup,only:iLookPARAM,iLookPROG,iLookDIAG ! named variables for structure elements diff --git a/build/source/engine/tolFida.f90 b/build/source/engine/tolFida.f90 index 68e878c9b..af46f1991 100644 --- a/build/source/engine/tolFida.f90 +++ b/build/source/engine/tolFida.f90 @@ -50,9 +50,9 @@ module tolFida_module ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (dp) + var_dlength ! data vector with variable length dimension (rkind) ! provide access to indices that define elements of the data structures USE var_lookup,only:iLookDIAG ! named variables for structure elements @@ -99,8 +99,8 @@ integer(c_int) function computWeightFida(sunvec_y, sunvec_ewt, user_data) & ! pointers to data in SUNDIALS vectors type(eqnsData), pointer :: tol_data ! equations data - real(dp), pointer :: stateVec(:) - real(dp), pointer :: weightVec(:) + real(rkind), pointer :: stateVec(:) + real(rkind), pointer :: weightVec(:) integer(c_int) :: iState !======= Internals ============ @@ -147,8 +147,8 @@ subroutine popTolFida(& type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers type(var_dlength),intent(in) :: mpar_data ! model parameters ! output - real(dp),intent(out) :: absTol(:) ! model state vector (mixed units) - real(dp),intent(out) :: relTol(:) ! model state vector (mixed units) + real(rkind),intent(out) :: absTol(:) ! model state vector (mixed units) + real(rkind),intent(out) :: relTol(:) ! model state vector (mixed units) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- @@ -159,20 +159,20 @@ subroutine popTolFida(& integer(i4b) :: iLayer ! index of layer within the snow+soil domain integer(i4b) :: ixStateSubset ! index within the state subset logical(lgt),dimension(nState) :: tolFlag ! flag to denote that the state is populated - real(dp) :: absTolTempCas = 1e-6 - real(dp) :: relTolTempCas = 1e-6 - real(dp) :: absTolTempVeg = 1e-6 - real(dp) :: relTolTempVeg = 1e-6 - real(dp) :: absTolWatVeg = 1e-6 - real(dp) :: relTolWatVeg = 1e-6 - real(dp) :: absTolTempSoilSnow = 1e-6 - real(dp) :: relTolTempSoilSnow = 1e-6 - real(dp) :: absTolWatSnow = 1e-6 - real(dp) :: relTolWatSnow = 1e-6 - real(dp) :: absTolMatric = 1e-6 - real(dp) :: relTolMatric = 1e-6 - real(dp) :: absTolAquifr = 1e-6 - real(dp) :: relTolAquifr = 1e-6 + real(rkind) :: absTolTempCas = 1e-6 + real(rkind) :: relTolTempCas = 1e-6 + real(rkind) :: absTolTempVeg = 1e-6 + real(rkind) :: relTolTempVeg = 1e-6 + real(rkind) :: absTolWatVeg = 1e-6 + real(rkind) :: relTolWatVeg = 1e-6 + real(rkind) :: absTolTempSoilSnow = 1e-6 + real(rkind) :: relTolTempSoilSnow = 1e-6 + real(rkind) :: absTolWatSnow = 1e-6 + real(rkind) :: relTolWatSnow = 1e-6 + real(rkind) :: absTolMatric = 1e-6 + real(rkind) :: relTolMatric = 1e-6 + real(rkind) :: absTolAquifr = 1e-6 + real(rkind) :: relTolAquifr = 1e-6 ! -------------------------------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/updatStateFida.f90 b/build/source/engine/updatStateFida.f90 index cc85a53eb..34c2b683c 100644 --- a/build/source/engine/updatStateFida.f90 +++ b/build/source/engine/updatStateFida.f90 @@ -16,7 +16,7 @@ module updatStateFida_module public::updateSoilFida2 public::updateVegFida -real(dp),parameter :: verySmall=1e-14_rkind ! a very small number (used to avoid divide by zero) +real(rkind),parameter :: verySmall=1e-14_rkind ! a very small number (used to avoid divide by zero) contains @@ -45,17 +45,17 @@ subroutine updateVegFida(& USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) implicit none ! input variables - real(dp),intent(in) :: Temp ! temperature (K) - real(dp),intent(in) :: Theta ! volume fraction of total water (-) - real(dp),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) - real(dp),intent(in) :: TempPrime ! temperature (K) - real(dp),intent(in) :: ThetaPrime ! volume fraction of total water (-) + real(rkind),intent(in) :: Temp ! temperature (K) + real(rkind),intent(in) :: Theta ! volume fraction of total water (-) + real(rkind),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) + real(rkind),intent(in) :: TempPrime ! temperature (K) + real(rkind),intent(in) :: ThetaPrime ! volume fraction of total water (-) ! output variables - real(dp),intent(out) :: VolFracLiq ! volumetric fraction of liquid water (-) - real(dp),intent(out) :: VolFracIce ! volumetric fraction of ice (-) - real(dp),intent(out) :: VolFracLiqPrime ! volumetric fraction of liquid water (-) - real(dp),intent(out) :: VolFracIcePrime ! volumetric fraction of ice (-) - real(dp),intent(out) :: fLiq ! fraction of liquid water (-) + real(rkind),intent(out) :: VolFracLiq ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: VolFracIce ! volumetric fraction of ice (-) + real(rkind),intent(out) :: VolFracLiqPrime ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: VolFracIcePrime ! volumetric fraction of ice (-) + real(rkind),intent(out) :: fLiq ! fraction of liquid water (-) ! error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -95,17 +95,17 @@ subroutine updateSnowFida(& USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) implicit none ! input variables - real(dp),intent(in) :: mLayerTemp ! temperature (K) - real(dp),intent(in) :: mLayerTheta ! volume fraction of total water (-) - real(dp),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) - real(dp),intent(in) :: mLayerTempPrime ! temperature (K) - real(dp),intent(in) :: mLayerThetaPrime ! volume fraction of total water (-) + real(rkind),intent(in) :: mLayerTemp ! temperature (K) + real(rkind),intent(in) :: mLayerTheta ! volume fraction of total water (-) + real(rkind),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) + real(rkind),intent(in) :: mLayerTempPrime ! temperature (K) + real(rkind),intent(in) :: mLayerThetaPrime ! volume fraction of total water (-) ! output variables - real(dp),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) - real(dp),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) - real(dp),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water (-) - real(dp),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice (-) - real(dp),intent(out) :: fLiq ! fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) + real(rkind),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice (-) + real(rkind),intent(out) :: fLiq ! fraction of liquid water (-) ! error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -153,31 +153,31 @@ subroutine updateSoilFida(& USE soil_utils_module,only:dTheta_dPsi implicit none ! input variables - real(dp),intent(in) :: dt_cur - real(dp),intent(in) :: mLayerTemp ! estimate of temperature (K) - real(dp),intent(in) :: mLayerMatricHead ! matric head (m) - real(dp),intent(in) :: mLayerMatricHeadPrev ! matric head (m) - real(dp),intent(in) :: mLayerVolFracWatPrev - real(dp),intent(in) :: mLayerTempPrime - real(dp),intent(in) :: mLayerMatricHeadPrime ! matric head (m) - real(dp),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter - real(dp),intent(in) :: vGn_n ! van Genutchen "n" parameter - real(dp),intent(in) :: theta_sat ! soil porosity (-) - real(dp),intent(in) :: theta_res ! soil residual volumetric water content (-) - real(dp),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) + real(rkind),intent(in) :: dt_cur + real(rkind),intent(in) :: mLayerTemp ! estimate of temperature (K) + real(rkind),intent(in) :: mLayerMatricHead ! matric head (m) + real(rkind),intent(in) :: mLayerMatricHeadPrev ! matric head (m) + real(rkind),intent(in) :: mLayerVolFracWatPrev + real(rkind),intent(in) :: mLayerTempPrime + real(rkind),intent(in) :: mLayerMatricHeadPrime ! matric head (m) + real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter + real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter + real(rkind),intent(in) :: theta_sat ! soil porosity (-) + real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) + real(rkind),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) ! output variables - real(dp),intent(out) :: mLayerVolFracWat ! fractional volume of total water (-) - real(dp),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) - real(dp),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) - real(dp),intent(out) :: mLayerVolFracWatPrime ! fractional volume of total water (-) - real(dp),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water (-) - real(dp),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice (-) + real(rkind),intent(out) :: mLayerVolFracWat ! fractional volume of total water (-) + real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) + real(rkind),intent(out) :: mLayerVolFracWatPrime ! fractional volume of total water (-) + real(rkind),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice (-) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! define local variables - real(dp) :: TcSoil ! critical soil temperature when all water is unfrozen (K) - real(dp) :: xConst ! constant in the freezing curve function (m K-1) - real(dp) :: mLayerPsiLiq ! liquid water matric potential (m) + real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) + real(rkind) :: xConst ! constant in the freezing curve function (m K-1) + real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) ! initialize error control err=0; message="updateSoilFida/" @@ -262,28 +262,28 @@ subroutine updateSoilFida2(& USE soil_utils_module,only:dTheta_dPsi implicit none ! input variables - real(dp),intent(in) :: mLayerTemp ! estimate of temperature (K) - real(dp),intent(in) :: mLayerMatricHead ! matric head (m) - real(dp),intent(in) :: mLayerTempPrime - real(dp),intent(in) :: mLayerMatricHeadPrime ! matric head (m) - real(dp),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter - real(dp),intent(in) :: vGn_n ! van Genutchen "n" parameter - real(dp),intent(in) :: theta_sat ! soil porosity (-) - real(dp),intent(in) :: theta_res ! soil residual volumetric water content (-) - real(dp),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) + real(rkind),intent(in) :: mLayerTemp ! estimate of temperature (K) + real(rkind),intent(in) :: mLayerMatricHead ! matric head (m) + real(rkind),intent(in) :: mLayerTempPrime + real(rkind),intent(in) :: mLayerMatricHeadPrime ! matric head (m) + real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter + real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter + real(rkind),intent(in) :: theta_sat ! soil porosity (-) + real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) + real(rkind),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) ! output variables - real(dp),intent(out) :: mLayerVolFracWat ! fractional volume of total water (-) - real(dp),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) - real(dp),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) - real(dp),intent(out) :: mLayerVolFracWatPrime ! fractional volume of total water (-) - real(dp),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water (-) - real(dp),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice (-) + real(rkind),intent(out) :: mLayerVolFracWat ! fractional volume of total water (-) + real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) + real(rkind),intent(out) :: mLayerVolFracWatPrime ! fractional volume of total water (-) + real(rkind),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice (-) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! define local variables - real(dp) :: TcSoil ! critical soil temperature when all water is unfrozen (K) - real(dp) :: xConst ! constant in the freezing curve function (m K-1) - real(dp) :: mLayerPsiLiq ! liquid water matric potential (m) + real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) + real(rkind) :: xConst ! constant in the freezing curve function (m K-1) + real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) ! initialize error control err=0; message="updateSoilFida2/" diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 index 097102169..a29350f08 100644 --- a/build/source/engine/updateVars.f90 +++ b/build/source/engine/updateVars.f90 @@ -64,10 +64,10 @@ module updateVars_module ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - zLookup, & ! data vector with variable length dimension (dp) - var_dlength ! data vector with variable length dimension (dp) + zLookup, & ! data vector with variable length dimension (rkind) + var_dlength ! data vector with variable length dimension (rkind) ! provide access to indices that define elements of the data structures USE var_lookup,only:iLookDIAG ! named variables for structure elements diff --git a/build/source/engine/updateVarsFida.f90 b/build/source/engine/updateVarsFida.f90 index 4004e0330..17fcc0fbd 100644 --- a/build/source/engine/updateVarsFida.f90 +++ b/build/source/engine/updateVarsFida.f90 @@ -64,9 +64,9 @@ module updateVarsFida_module ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (dp) + var_dlength ! data vector with variable length dimension (rkind) ! provide access to indices that define elements of the data structures USE var_lookup,only:iLookDIAG ! named variables for structure elements @@ -142,39 +142,39 @@ subroutine updateVarsFida(& ! -------------------------------------------------------------------------------------------------------------------------------- implicit none ! input - real(dp),intent(in) :: dt_cur + real(rkind),intent(in) :: dt_cur logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - real(dp),intent(in) :: mLayerMatricHeadPrev(:) - real(dp),intent(in) :: mLayerVolFracWatPrev(:) + real(rkind),intent(in) :: mLayerMatricHeadPrev(:) + real(rkind),intent(in) :: mLayerVolFracWatPrev(:) type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy - real(dp),intent(inout) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(dp),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - real(dp),intent(inout) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) - real(dp),intent(inout) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + real(rkind),intent(inout) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + real(rkind),intent(inout) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) + real(rkind),intent(inout) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - real(dp),intent(inout) :: scalarCanopyTempPrime ! trial value of canopy temperature (K) - real(dp),intent(inout) :: scalarCanopyWatPrime ! trial value of canopy total water (kg m-2) - real(dp),intent(inout) :: scalarCanopyLiqPrime ! trial value of canopy liquid water (kg m-2) - real(dp),intent(inout) :: scalarCanopyIcePrime ! trial value of canopy ice content (kg m-2) + real(rkind),intent(inout) :: scalarCanopyTempPrime ! trial value of canopy temperature (K) + real(rkind),intent(inout) :: scalarCanopyWatPrime ! trial value of canopy total water (kg m-2) + real(rkind),intent(inout) :: scalarCanopyLiqPrime ! trial value of canopy liquid water (kg m-2) + real(rkind),intent(inout) :: scalarCanopyIcePrime ! trial value of canopy ice content (kg m-2) ! output: variables for the snow-soil domain - real(dp),intent(inout) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(dp),intent(inout) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(dp),intent(inout) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) - real(dp),intent(inout) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric ice water content (-) - real(dp),intent(inout) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(dp),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) + real(rkind),intent(inout) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(inout) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(rkind),intent(inout) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(inout) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric ice water content (-) + real(rkind),intent(inout) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) - real(dp),intent(inout) :: mLayerTempPrime(:) - real(dp),intent(inout) :: mLayerVolFracWatPrime(:) ! reza - real(dp),intent(inout) :: mLayerVolFracLiqPrime(:) ! reza - real(dp),intent(inout) :: mLayerVolFracIcePrime(:) ! reza - real(dp),intent(inout) :: mLayerMatricHeadPrime(:) ! reza - real(dp),intent(inout) :: mLayerMatricHeadLiqPrime(:) ! reza + real(rkind),intent(inout) :: mLayerTempPrime(:) + real(rkind),intent(inout) :: mLayerVolFracWatPrime(:) ! reza + real(rkind),intent(inout) :: mLayerVolFracLiqPrime(:) ! reza + real(rkind),intent(inout) :: mLayerVolFracIcePrime(:) ! reza + real(rkind),intent(inout) :: mLayerMatricHeadPrime(:) ! reza + real(rkind),intent(inout) :: mLayerMatricHeadLiqPrime(:) ! reza ! output: error control integer(i4b),intent(out) :: err ! error code @@ -190,31 +190,31 @@ subroutine updateVarsFida(& logical(lgt) :: isCoupled ! .true. if a given variable shared another state variable in the same control volume logical(lgt) :: isNrgState ! .true. if a given variable is an energy state logical(lgt),allocatable :: computedCoupling(:) ! .true. if computed the coupling for a given state variable - real(dp) :: scalarVolFracLiq ! volumetric fraction of liquid water (-) - real(dp) :: scalarVolFracIce ! volumetric fraction of ice (-) - real(dp) :: scalarVolFracLiqPrime ! volumetric fraction of liquid water (-) - real(dp) :: scalarVolFracIcePrime ! volumetric fraction of ice (-) - real(dp) :: Tcrit ! critical soil temperature below which ice exists (K) - real(dp) :: xTemp ! temporary temperature (K) - real(dp) :: effSat ! effective saturation (-) - real(dp) :: avPore ! available pore space (-) + real(rkind) :: scalarVolFracLiq ! volumetric fraction of liquid water (-) + real(rkind) :: scalarVolFracIce ! volumetric fraction of ice (-) + real(rkind) :: scalarVolFracLiqPrime ! volumetric fraction of liquid water (-) + real(rkind) :: scalarVolFracIcePrime ! volumetric fraction of ice (-) + real(rkind) :: Tcrit ! critical soil temperature below which ice exists (K) + real(rkind) :: xTemp ! temporary temperature (K) + real(rkind) :: effSat ! effective saturation (-) + real(rkind) :: avPore ! available pore space (-) character(len=256) :: cMessage ! error message of downwind routine logical(lgt),parameter :: printFlag=.false. ! flag to turn on printing ! iterative solution for temperature - real(dp) :: meltNrg ! energy for melt+freeze (J m-3) - real(dp) :: residual ! residual in the energy equation (J m-3) - real(dp) :: derivative ! derivative in the energy equation (J m-3 K-1) - real(dp) :: tempInc ! iteration increment (K) + real(rkind) :: meltNrg ! energy for melt+freeze (J m-3) + real(rkind) :: residual ! residual in the energy equation (J m-3) + real(rkind) :: derivative ! derivative in the energy equation (J m-3 K-1) + real(rkind) :: tempInc ! iteration increment (K) integer(i4b) :: iter ! iteration index integer(i4b) :: niter ! number of iterations integer(i4b),parameter :: maxiter=100 ! maximum number of iterations - real(dp),parameter :: nrgConvTol=1.e-4_rkind ! convergence tolerance for energy (J m-3) - real(dp),parameter :: tempConvTol=1.e-6_rkind ! convergence tolerance for temperature (K) - real(dp) :: critDiff ! temperature difference from critical (K) - real(dp) :: tempMin ! minimum bracket for temperature (K) - real(dp) :: tempMax ! maximum bracket for temperature (K) + real(rkind),parameter :: nrgConvTol=1.e-4_rkind ! convergence tolerance for energy (J m-3) + real(rkind),parameter :: tempConvTol=1.e-6_rkind ! convergence tolerance for temperature (K) + real(rkind) :: critDiff ! temperature difference from critical (K) + real(rkind) :: tempMin ! minimum bracket for temperature (K) + real(rkind) :: tempMax ! maximum bracket for temperature (K) logical(lgt) :: bFlag ! flag to denote that iteration increment was constrained using bi-section - real(dp),parameter :: epsT=1.e-7_rkind ! small interval above/below critical temperature (K) + real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical temperature (K) ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures associate(& diff --git a/build/source/engine/updateVarsFida2.f90 b/build/source/engine/updateVarsFida2.f90 index 254be1bf9..74fa5a632 100644 --- a/build/source/engine/updateVarsFida2.f90 +++ b/build/source/engine/updateVarsFida2.f90 @@ -64,9 +64,9 @@ module updateVarsFida2_module ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (dp) + var_dlength ! data vector with variable length dimension (rkind) ! provide access to indices that define elements of the data structures USE var_lookup,only:iLookDIAG ! named variables for structure elements @@ -153,33 +153,33 @@ subroutine updateVarsFida2(& type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy - real(dp),intent(inout) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(dp),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - real(dp),intent(inout) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) - real(dp),intent(inout) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - real(dp),intent(inout) :: scalarCanopyTempPrime ! trial value of canopy temperature (K) - real(dp),intent(inout) :: scalarCanopyWatPrime ! trial value of canopy total water (kg m-2) - real(dp),intent(inout) :: scalarCanopyLiqPrime ! trial value of canopy liquid water (kg m-2) - real(dp),intent(inout) :: scalarCanopyIcePrime ! trial value of canopy ice content (kg m-2) + real(rkind),intent(inout) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + real(rkind),intent(inout) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) + real(rkind),intent(inout) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + real(rkind),intent(inout) :: scalarCanopyTempPrime ! trial value of canopy temperature (K) + real(rkind),intent(inout) :: scalarCanopyWatPrime ! trial value of canopy total water (kg m-2) + real(rkind),intent(inout) :: scalarCanopyLiqPrime ! trial value of canopy liquid water (kg m-2) + real(rkind),intent(inout) :: scalarCanopyIcePrime ! trial value of canopy ice content (kg m-2) ! output: variables for the snow-soil domain - real(dp),intent(inout) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(dp),intent(inout) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(dp),intent(inout) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) - real(dp),intent(inout) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric ice water content (-) - real(dp),intent(inout) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(dp),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) + real(rkind),intent(inout) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(inout) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(rkind),intent(inout) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(inout) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric ice water content (-) + real(rkind),intent(inout) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) - real(dp),intent(inout) :: mLayerTempPrime(:) - real(dp),intent(inout) :: mLayerVolFracWatPrime(:) ! reza - real(dp),intent(inout) :: mLayerVolFracLiqPrime(:) ! reza - real(dp),intent(inout) :: mLayerVolFracIcePrime(:) ! reza - real(dp),intent(inout) :: mLayerMatricHeadPrime(:) ! reza - real(dp),intent(inout) :: mLayerMatricHeadLiqPrime(:) ! reza - real(dp),intent(out) :: mLayerd2Theta_dTk2(:) ! reza - real(dp),intent(out) :: d2VolTot_d2Psi0(:) ! reza - real(dp),intent(out) :: dFracLiqSnow_dTk(:) ! reza - real(dp),intent(out) :: d2Theta_dTkCanopy2 - real(dp),intent(out) :: dFracLiqVeg_dTkCanopy + real(rkind),intent(inout) :: mLayerTempPrime(:) + real(rkind),intent(inout) :: mLayerVolFracWatPrime(:) ! reza + real(rkind),intent(inout) :: mLayerVolFracLiqPrime(:) ! reza + real(rkind),intent(inout) :: mLayerVolFracIcePrime(:) ! reza + real(rkind),intent(inout) :: mLayerMatricHeadPrime(:) ! reza + real(rkind),intent(inout) :: mLayerMatricHeadLiqPrime(:) ! reza + real(rkind),intent(out) :: mLayerd2Theta_dTk2(:) ! reza + real(rkind),intent(out) :: d2VolTot_d2Psi0(:) ! reza + real(rkind),intent(out) :: dFracLiqSnow_dTk(:) ! reza + real(rkind),intent(out) :: d2Theta_dTkCanopy2 + real(rkind),intent(out) :: dFracLiqVeg_dTkCanopy ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -194,31 +194,31 @@ subroutine updateVarsFida2(& logical(lgt) :: isCoupled ! .true. if a given variable shared another state variable in the same control volume logical(lgt) :: isNrgState ! .true. if a given variable is an energy state logical(lgt),allocatable :: computedCoupling(:) ! .true. if computed the coupling for a given state variable - real(dp) :: scalarVolFracLiq ! volumetric fraction of liquid water (-) - real(dp) :: scalarVolFracIce ! volumetric fraction of ice (-) - real(dp) :: scalarVolFracLiqPrime ! volumetric fraction of liquid water (-) - real(dp) :: scalarVolFracIcePrime ! volumetric fraction of ice (-) - real(dp) :: Tcrit ! critical soil temperature below which ice exists (K) - real(dp) :: xTemp ! temporary temperature (K) - real(dp) :: effSat ! effective saturation (-) - real(dp) :: avPore ! available pore space (-) + real(rkind) :: scalarVolFracLiq ! volumetric fraction of liquid water (-) + real(rkind) :: scalarVolFracIce ! volumetric fraction of ice (-) + real(rkind) :: scalarVolFracLiqPrime ! volumetric fraction of liquid water (-) + real(rkind) :: scalarVolFracIcePrime ! volumetric fraction of ice (-) + real(rkind) :: Tcrit ! critical soil temperature below which ice exists (K) + real(rkind) :: xTemp ! temporary temperature (K) + real(rkind) :: effSat ! effective saturation (-) + real(rkind) :: avPore ! available pore space (-) character(len=256) :: cMessage ! error message of downwind routine logical(lgt),parameter :: printFlag=.false. ! flag to turn on printing ! iterative solution for temperature - real(dp) :: meltNrg ! energy for melt+freeze (J m-3) - real(dp) :: residual ! residual in the energy equation (J m-3) - real(dp) :: derivative ! derivative in the energy equation (J m-3 K-1) - real(dp) :: tempInc ! iteration increment (K) + real(rkind) :: meltNrg ! energy for melt+freeze (J m-3) + real(rkind) :: residual ! residual in the energy equation (J m-3) + real(rkind) :: derivative ! derivative in the energy equation (J m-3 K-1) + real(rkind) :: tempInc ! iteration increment (K) integer(i4b) :: iter ! iteration index integer(i4b) :: niter ! number of iterations integer(i4b),parameter :: maxiter=100 ! maximum number of iterations - real(dp),parameter :: nrgConvTol=1.e-4_rkind ! convergence tolerance for energy (J m-3) - real(dp),parameter :: tempConvTol=1.e-6_rkind ! convergence tolerance for temperature (K) - real(dp) :: critDiff ! temperature difference from critical (K) - real(dp) :: tempMin ! minimum bracket for temperature (K) - real(dp) :: tempMax ! maximum bracket for temperature (K) + real(rkind),parameter :: nrgConvTol=1.e-4_rkind ! convergence tolerance for energy (J m-3) + real(rkind),parameter :: tempConvTol=1.e-6_rkind ! convergence tolerance for temperature (K) + real(rkind) :: critDiff ! temperature difference from critical (K) + real(rkind) :: tempMin ! minimum bracket for temperature (K) + real(rkind) :: tempMax ! maximum bracket for temperature (K) logical(lgt) :: bFlag ! flag to denote that iteration increment was constrained using bi-section - real(dp),parameter :: epsT=1.e-7_rkind ! small interval above/below critical temperature (K) + real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical temperature (K) ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures associate(& @@ -781,17 +781,17 @@ subroutine xTempSolve(& derivative ) ! intent(out) : derivative (J m-3 K-1) implicit none ! input: constant over iterations - real(dp),intent(in) :: meltNrg ! energy for melt+freeze (J m-3) - real(dp),intent(in) :: heatCap ! volumetric heat capacity (J m-3 K-1) - real(dp),intent(in) :: tempInit ! initial temperature (K) - real(dp),intent(in) :: volFracIceInit ! initial volumetric fraction of ice (-) + real(rkind),intent(in) :: meltNrg ! energy for melt+freeze (J m-3) + real(rkind),intent(in) :: heatCap ! volumetric heat capacity (J m-3 K-1) + real(rkind),intent(in) :: tempInit ! initial temperature (K) + real(rkind),intent(in) :: volFracIceInit ! initial volumetric fraction of ice (-) ! input-output: trial values - real(dp),intent(inout) :: xTemp ! trial value for temperature - real(dp),intent(in) :: dLiq_dT ! derivative in liquid water content w.r.t. temperature (K-1) - real(dp),intent(in) :: volFracIceTrial ! trial value for the volumetric fraction of ice (-) + real(rkind),intent(inout) :: xTemp ! trial value for temperature + real(rkind),intent(in) :: dLiq_dT ! derivative in liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: volFracIceTrial ! trial value for the volumetric fraction of ice (-) ! output: residual and derivative - real(dp),intent(out) :: residual ! residual (J m-3) - real(dp),intent(out) :: derivative ! derivative (J m-3 K-1) + real(rkind),intent(out) :: residual ! residual (J m-3) + real(rkind),intent(out) :: derivative ! derivative (J m-3 K-1) ! subroutine starts here residual = -heatCap*(xTemp - tempInit) + meltNrg*(volFracIceTrial - volFracIceInit) ! J m-3 derivative = heatCap + LH_fus*iden_water*dLiq_dT ! J m-3 K-1 diff --git a/build/source/engine/varExtrFida.f90 b/build/source/engine/varExtrFida.f90 index 9e64f52e6..339d1e2b0 100644 --- a/build/source/engine/varExtrFida.f90 +++ b/build/source/engine/varExtrFida.f90 @@ -46,9 +46,9 @@ module varExtrFida_module ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (dp) + var_dlength ! data vector with variable length dimension (rkind) ! provide access to indices that define elements of the data structures USE var_lookup,only:iLookDIAG ! named variables for structure elements @@ -111,23 +111,23 @@ subroutine varExtract(& ! -------------------------------------------------------------------------------------------------------------------------------- implicit none ! input - real(dp),intent(in) :: stateVec(:) ! model state vector (mixed units) + real(rkind),intent(in) :: stateVec(:) ! model state vector (mixed units) type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers ! output: variables for the vegetation canopy - real(dp),intent(out) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) - real(dp),intent(out) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(dp),intent(out) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - real(dp),intent(out) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) + real(rkind),intent(out) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) + real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind),intent(out) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + real(rkind),intent(out) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - real(dp),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(dp),intent(out) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(dp),intent(out) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) - real(dp),intent(out) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(dp),intent(out) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) + real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(out) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(rkind),intent(out) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind),intent(out) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) ! output: variables for the aquifer - real(dp),intent(out) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + real(rkind),intent(out) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -242,23 +242,23 @@ subroutine varExtractFida(& ! -------------------------------------------------------------------------------------------------------------------------------- implicit none ! input - real(dp),intent(in) :: stateVecPrime(:) ! model state vector (mixed units) + real(rkind),intent(in) :: stateVecPrime(:) ! model state vector (mixed units) type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers ! output: variables for the vegetation canopy - real(dp),intent(out) :: scalarCanairTempPrime ! trial value of canopy air temperature (K) - real(dp),intent(out) :: scalarCanopyTempPrime ! trial value of canopy temperature (K) - real(dp),intent(out) :: scalarCanopyWatPrime ! trial value of canopy total water (kg m-2) - real(dp),intent(out) :: scalarCanopyLiqPrime ! trial value of canopy liquid water (kg m-2) + real(rkind),intent(out) :: scalarCanairTempPrime ! trial value of canopy air temperature (K) + real(rkind),intent(out) :: scalarCanopyTempPrime ! trial value of canopy temperature (K) + real(rkind),intent(out) :: scalarCanopyWatPrime ! trial value of canopy total water (kg m-2) + real(rkind),intent(out) :: scalarCanopyLiqPrime ! trial value of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - real(dp),intent(out) :: mLayerTempPrime(:) ! trial vector of layer temperature (K) - real(dp),intent(out) :: mLayerVolFracWatPrime(:) ! trial vector of volumetric total water content (-) - real(dp),intent(out) :: mLayerVolFracLiqPrime(:) ! trial vector of volumetric liquid water content (-) - real(dp),intent(out) :: mLayerMatricHeadPrime(:) ! trial vector of total water matric potential (m) - real(dp),intent(out) :: mLayerMatricHeadLiqPrime(:) ! trial vector of liquid water matric potential (m) + real(rkind),intent(out) :: mLayerTempPrime(:) ! trial vector of layer temperature (K) + real(rkind),intent(out) :: mLayerVolFracWatPrime(:) ! trial vector of volumetric total water content (-) + real(rkind),intent(out) :: mLayerVolFracLiqPrime(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(out) :: mLayerMatricHeadPrime(:) ! trial vector of total water matric potential (m) + real(rkind),intent(out) :: mLayerMatricHeadLiqPrime(:) ! trial vector of liquid water matric potential (m) ! output: variables for the aquifer - real(dp),intent(out) :: scalarAquiferStoragePrime ! trial value of storage of water in the aquifer (m) + real(rkind),intent(out) :: scalarAquiferStoragePrime ! trial value of storage of water in the aquifer (m) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -358,7 +358,7 @@ subroutine residDiscontinuity(& ! -------------------------------------------------------------------------------------------------------------------------------- implicit none ! input - real(dp),intent(in) :: stateVec(:) ! model state vector (mixed units) + real(rkind),intent(in) :: stateVec(:) ! model state vector (mixed units) type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers @@ -446,7 +446,7 @@ subroutine countDiscontinuity(& ! -------------------------------------------------------------------------------------------------------------------------------- implicit none ! input - real(dp),intent(in) :: stateVec(:) ! model state vector (mixed units) + real(rkind),intent(in) :: stateVec(:) ! model state vector (mixed units) type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index bd0fedb57..392dba234 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -43,11 +43,11 @@ module varSubstep_module ! derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_flagVec, & ! data vector with variable length dimension (i4b) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) - zLookup, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions ! provide access to indices that define elements of the data structures diff --git a/build/source/engine/varSubstepFida.f90 b/build/source/engine/varSubstepFida.f90 index 92eb1e9ab..33c67466f 100644 --- a/build/source/engine/varSubstepFida.f90 +++ b/build/source/engine/varSubstepFida.f90 @@ -43,11 +43,11 @@ module varSubstepFida_module ! derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_flagVec, & ! data vector with variable length dimension (i4b) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) - zLookup, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions ! provide access to indices that define elements of the data structures @@ -79,7 +79,7 @@ module varSubstepFida_module public::varSubstepFida ! algorithmic parameters -real(dp),parameter :: verySmall=1.e-6_rkind ! used as an additive constant to check if substantial difference among real numbers +real(rkind),parameter :: verySmall=1.e-6_rkind ! used as an additive constant to check if substantial difference among real numbers contains @@ -140,9 +140,9 @@ subroutine varSubstepFida(& ! * dummy variables ! --------------------------------------------------------------------------------------- ! input: model control - real(dp),intent(in) :: dt ! time step (seconds) - real(dp),intent(in) :: dtInit ! initial time step (seconds) - real(dp),intent(in) :: dt_min ! minimum time step (seconds) + real(rkind),intent(in) :: dt ! time step (seconds) + real(rkind),intent(in) :: dtInit ! initial time step (seconds) + real(rkind),intent(in) :: dt_min ! minimum time step (seconds) integer(i4b),intent(in) :: nState ! total number of state variables logical(lgt),intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step @@ -167,7 +167,7 @@ subroutine varSubstepFida(& type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin ! output: model control integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(dp),intent(out) :: dtMultiplier ! substep multiplier (-) + real(rkind),intent(out) :: dtMultiplier ! substep multiplier (-) integer(i4b),intent(out) :: nSubsteps ! number of substeps taken for a given split logical(lgt),intent(out) :: failedMinimumStep ! flag to denote success of substepping for a given split logical(lgt),intent(out) :: reduceCoupledStep ! flag to denote need to reduce the length of the coupled step @@ -186,24 +186,24 @@ subroutine varSubstepFida(& integer(i4b) :: ixLayer ! index in a given domain integer(i4b), dimension(1) :: ixMin,ixMax ! bounds of a given flux vector ! time stepping - real(dp) :: dtSum ! sum of time from successful steps (seconds) - real(dp) :: dt_wght ! weight given to a given flux calculation - real(dp) :: dtSubstep ! length of a substep (s) + real(rkind) :: dtSum ! sum of time from successful steps (seconds) + real(rkind) :: dt_wght ! weight given to a given flux calculation + real(rkind) :: dtSubstep ! length of a substep (s) ! adaptive sub-stepping for the explicit solution logical(lgt) :: failedSubstep ! flag to denote success of substepping for a given split - real(dp),parameter :: safety=0.85_rkind ! safety factor in adaptive sub-stepping - real(dp),parameter :: reduceMin=0.1_rkind ! mimimum factor that time step is reduced - real(dp),parameter :: increaseMax=4.0_rkind ! maximum factor that time step is increased + real(rkind),parameter :: safety=0.85_rkind ! safety factor in adaptive sub-stepping + real(rkind),parameter :: reduceMin=0.1_rkind ! mimimum factor that time step is reduced + real(rkind),parameter :: increaseMax=4.0_rkind ! maximum factor that time step is increased ! adaptive sub-stepping for the implicit solution integer(i4b),parameter :: n_inc=5 ! minimum number of iterations to increase time step integer(i4b),parameter :: n_dec=15 ! maximum number of iterations to decrease time step - real(dp),parameter :: F_inc = 1.25_rkind ! factor used to increase time step - real(dp),parameter :: F_dec = 0.90_rkind ! factor used to decrease time step + real(rkind),parameter :: F_inc = 1.25_rkind ! factor used to increase time step + real(rkind),parameter :: F_dec = 0.90_rkind ! factor used to decrease time step ! state and flux vectors - real(dp) :: untappedMelt(nState) ! un-tapped melt energy (J m-3 s-1) - real(dp) :: stateVecInit(nState) ! initial state vector (mixed units) - real(dp) :: stateVecTrial(nState) ! trial state vector (mixed units) - real(dp) :: stateVecPrime(nState) ! trial state vector (mixed units) + real(rkind) :: untappedMelt(nState) ! un-tapped melt energy (J m-3 s-1) + real(rkind) :: stateVecInit(nState) ! initial state vector (mixed units) + real(rkind) :: stateVecTrial(nState) ! trial state vector (mixed units) + real(rkind) :: stateVecPrime(nState) ! trial state vector (mixed units) type(var_dlength) :: flux_temp ! temporary model fluxes ! flags logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation @@ -212,11 +212,11 @@ subroutine varSubstepFida(& logical(lgt) :: waterBalanceError ! flag to denote that there is a water balance error logical(lgt) :: nrgFluxModified ! flag to denote that the energy fluxes were modified ! energy fluxes - real(dp) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) - real(dp) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - real(dp) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) - real(dp) :: sumSoilCompress - real(dp),allocatable :: sumLayerCompress(:) + real(rkind) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) + real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) + real(rkind) :: sumSoilCompress + real(rkind),allocatable :: sumLayerCompress(:) ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- @@ -550,15 +550,15 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy implicit none ! model control - real(dp) ,intent(in) :: dt ! time step (s) + real(rkind) ,intent(in) :: dt ! time step (s) integer(i4b) ,intent(in) :: nSnow ! number of snow layers integer(i4b) ,intent(in) :: nSoil ! number of soil layers integer(i4b) ,intent(in) :: nLayers ! total number of layers logical(lgt) ,intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature logical(lgt) ,intent(in) :: computeVegFlux ! flag to compute the vegetation flux - real(dp) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) - real(dp) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) - real(dp) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) + real(rkind) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) + real(rkind) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) + real(rkind) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance logical(lgt) ,intent(in) :: checkNrgBalance ! flag to check the energy balance ! data structures @@ -580,49 +580,49 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt integer(i4b) :: ixSubset ! index within the state subset integer(i4b) :: ixFullVector ! index within full state vector integer(i4b) :: ixControlIndex ! index within a given domain - real(dp) :: volMelt ! volumetric melt (kg m-3) - real(dp),parameter :: verySmall=epsilon(1._rkind)*2._rkind ! a very small number (deal with precision issues) + real(rkind) :: volMelt ! volumetric melt (kg m-3) + real(rkind),parameter :: verySmall=epsilon(1._rkind)*2._rkind ! a very small number (deal with precision issues) ! mass balance - real(dp) :: canopyBalance0,canopyBalance1 ! canopy storage at start/end of time step - real(dp) :: soilBalance0,soilBalance1 ! soil storage at start/end of time step - real(dp) :: vertFlux ! change in storage due to vertical fluxes - real(dp) :: tranSink,baseSink,compSink ! change in storage due to sink terms - real(dp) :: liqError ! water balance error - real(dp) :: fluxNet ! net water fluxes (kg m-2 s-1) - real(dp) :: superflousWat ! superflous water used for evaporation (kg m-2 s-1) - real(dp) :: superflousNrg ! superflous energy that cannot be used for evaporation (W m-2 [J m-2 s-1]) + real(rkind) :: canopyBalance0,canopyBalance1 ! canopy storage at start/end of time step + real(rkind) :: soilBalance0,soilBalance1 ! soil storage at start/end of time step + real(rkind) :: vertFlux ! change in storage due to vertical fluxes + real(rkind) :: tranSink,baseSink,compSink ! change in storage due to sink terms + real(rkind) :: liqError ! water balance error + real(rkind) :: fluxNet ! net water fluxes (kg m-2 s-1) + real(rkind) :: superflousWat ! superflous water used for evaporation (kg m-2 s-1) + real(rkind) :: superflousNrg ! superflous energy that cannot be used for evaporation (W m-2 [J m-2 s-1]) character(LEN=256) :: cmessage ! error message of downwind routine ! trial state variables - real(dp) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(dp) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(dp) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(dp),dimension(nLayers) :: mLayerTempTrial ! trial vector for temperature of layers in the snow and soil domains (K) - real(dp),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector for volumetric fraction of total water (-) - real(dp),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector for total water matric potential (m) - real(dp),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector for liquid water matric potential (m) - real(dp) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) + real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial vector for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector for liquid water matric potential (m) + real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) ! diagnostic variables - real(dp) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(dp) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(dp),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric fraction of liquid water (-) - real(dp),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric fraction of ice (-) + real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric fraction of ice (-) ! derivative of state variables - real(dp) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) - real(dp) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) - real(dp) :: scalarCanopyWatPrime ! trial value for liquid water storage in the canopy (kg m-2) - real(dp),dimension(nLayers) :: mLayerTempPrime ! trial vector for temperature of layers in the snow and soil domains (K) - real(dp),dimension(nLayers) :: mLayerVolFracWatPrime ! trial vector for volumetric fraction of total water (-) - real(dp),dimension(nSoil) :: mLayerMatricHeadPrime ! trial vector for total water matric potential (m) - real(dp),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! trial vector for liquid water matric potential (m) - real(dp) :: scalarAquiferStoragePrime ! trial value for storage of water in the aquifer (m) + real(rkind) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatPrime ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempPrime ! trial vector for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! trial vector for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! trial vector for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! trial vector for liquid water matric potential (m) + real(rkind) :: scalarAquiferStoragePrime ! trial value for storage of water in the aquifer (m) ! diagnostic variables - real(dp) :: scalarCanopyLiqPrime ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(dp) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(dp),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector for volumetric fraction of liquid water (-) - real(dp),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector for volumetric fraction of ice (-) - real(dp) :: scalarCanairEnthalpyTrial ! enthalpy of the canopy air space (J m-3) - real(dp) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3 - real(dp),dimension(nLayers) :: mLayerEnthalpyTrial + real(rkind) :: scalarCanopyLiqPrime ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector for volumetric fraction of ice (-) + real(rkind) :: scalarCanairEnthalpyTrial ! enthalpy of the canopy air space (J m-3) + real(rkind) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3 + real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial integer(i4b) :: iLayer ! ------------------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/var_derive.f90 b/build/source/engine/var_derive.f90 index c60898870..2f579b777 100644 --- a/build/source/engine/var_derive.f90 +++ b/build/source/engine/var_derive.f90 @@ -25,7 +25,7 @@ module var_derive_module ! derived types to define the data structures USE data_types,only:var_ilength ! x%var(:)%dat (i4b) -USE data_types,only:var_dlength ! x%var(:)%dat (dp) +USE data_types,only:var_dlength ! x%var(:)%dat (rkind) ! named variables for snow and soil USE globalData,only:iname_snow ! named variables for snow diff --git a/build/source/engine/vegLiqFlux.f90 b/build/source/engine/vegLiqFlux.f90 index 59e1151b2..ae6184250 100644 --- a/build/source/engine/vegLiqFlux.f90 +++ b/build/source/engine/vegLiqFlux.f90 @@ -24,8 +24,8 @@ module vegLiqFlux_module USE nrtype ! data types -USE data_types,only:var_d ! x%var(:) (dp) -USE data_types,only:var_dlength ! x%var(:)%dat (dp) +USE data_types,only:var_d ! x%var(:) (rkind) +USE data_types,only:var_dlength ! x%var(:)%dat (rkind) ! named variables USE var_lookup,only:iLookPARAM,iLookDIAG ! named variables for structure elements diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 905732644..1471d0d04 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -26,9 +26,9 @@ module vegNrgFlux_module ! derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions ! indices that define elements of the data structures diff --git a/build/source/engine/vegNrgFluxFida.f90 b/build/source/engine/vegNrgFluxFida.f90 index 554913909..18c51a1a9 100644 --- a/build/source/engine/vegNrgFluxFida.f90 +++ b/build/source/engine/vegNrgFluxFida.f90 @@ -26,9 +26,9 @@ module vegNrgFluxFida_module ! derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions ! indices that define elements of the data structures @@ -113,11 +113,11 @@ module vegNrgFluxFida_module integer(i4b),parameter :: iLoc = 1 ! i-location integer(i4b),parameter :: jLoc = 1 ! j-location ! algorithmic parameters -real(dp),parameter :: missingValue=-9999._rkind ! missing value, used when diagnostic or state variables are undefined -real(dp),parameter :: verySmall=1.e-6_rkind ! used as an additive constant to check if substantial difference among real numbers -real(dp),parameter :: tinyVal=epsilon(1._rkind) ! used as an additive constant to check if substantial difference among real numbers -real(dp),parameter :: mpe=1.e-6_rkind ! prevents overflow error if division by zero -real(dp),parameter :: dx=1.e-11_rkind ! finite difference increment +real(rkind),parameter :: missingValue=-9999._rkind ! missing value, used when diagnostic or state variables are undefined +real(rkind),parameter :: verySmall=1.e-6_rkind ! used as an additive constant to check if substantial difference among real numbers +real(rkind),parameter :: tinyVal=epsilon(1._rkind) ! used as an additive constant to check if substantial difference among real numbers +real(rkind),parameter :: mpe=1.e-6_rkind ! prevents overflow error if division by zero +real(rkind),parameter :: dx=1.e-11_rkind ! finite difference increment ! control logical(lgt) :: printflag ! flag to turn on printing contains @@ -216,18 +216,18 @@ subroutine vegNrgFluxFida(& logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation ! input: model state variables - real(dp),intent(in) :: upperBoundTemp ! temperature of the upper boundary (K) --> NOTE: use air temperature - real(dp),intent(in) :: canairTempTrial ! trial value of canopy air space temperature (K) - real(dp),intent(in) :: canopyTempTrial ! trial value of canopy temperature (K) - real(dp),intent(in) :: groundTempTrial ! trial value of ground temperature (K) - real(dp),intent(in) :: canopyIceTrial ! trial value of mass of ice on the vegetation canopy (kg m-2) - real(dp),intent(in) :: canopyLiqTrial ! trial value of mass of liquid water on the vegetation canopy (kg m-2) - real(dp),intent(in) :: localAquiferStorage - real(dp),intent(in) :: mLayerMatricHead(:) ! - real(dp),intent(in) :: mLayerVolFracLiq(:) + real(rkind),intent(in) :: upperBoundTemp ! temperature of the upper boundary (K) --> NOTE: use air temperature + real(rkind),intent(in) :: canairTempTrial ! trial value of canopy air space temperature (K) + real(rkind),intent(in) :: canopyTempTrial ! trial value of canopy temperature (K) + real(rkind),intent(in) :: groundTempTrial ! trial value of ground temperature (K) + real(rkind),intent(in) :: canopyIceTrial ! trial value of mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: canopyLiqTrial ! trial value of mass of liquid water on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: localAquiferStorage + real(rkind),intent(in) :: mLayerMatricHead(:) ! + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! input: model derivatives - real(dp),intent(in) :: dCanLiq_dTcanopy ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) + real(rkind),intent(in) :: dCanLiq_dTcanopy ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) ! input/output: data structures type(var_i),intent(in) :: type_data ! type of vegetation and soil @@ -241,41 +241,41 @@ subroutine vegNrgFluxFida(& type(model_options),intent(in) :: model_decisions(:) ! model decisions ! output: liquid water fluxes associated with evaporation/transpiration (needed for coupling) - real(dp),intent(out) :: returnCanopyTranspiration ! canopy transpiration (kg m-2 s-1) - real(dp),intent(out) :: returnCanopyEvaporation ! canopy evaporation/condensation (kg m-2 s-1) - real(dp),intent(out) :: returnGroundEvaporation ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + real(rkind),intent(out) :: returnCanopyTranspiration ! canopy transpiration (kg m-2 s-1) + real(rkind),intent(out) :: returnCanopyEvaporation ! canopy evaporation/condensation (kg m-2 s-1) + real(rkind),intent(out) :: returnGroundEvaporation ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) ! output: fluxes - real(dp),intent(out) :: canairNetFlux ! net energy flux for the canopy air space (W m-2) - real(dp),intent(out) :: canopyNetFlux ! net energy flux for the vegetation canopy (W m-2) - real(dp),intent(out) :: groundNetFlux ! net energy flux for the ground surface (W m-2) + real(rkind),intent(out) :: canairNetFlux ! net energy flux for the canopy air space (W m-2) + real(rkind),intent(out) :: canopyNetFlux ! net energy flux for the vegetation canopy (W m-2) + real(rkind),intent(out) :: groundNetFlux ! net energy flux for the ground surface (W m-2) ! output: energy flux derivatives - real(dp),intent(out) :: dCanairNetFlux_dCanairTemp ! derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) - real(dp),intent(out) :: dCanairNetFlux_dCanopyTemp ! derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) - real(dp),intent(out) :: dCanairNetFlux_dGroundTemp ! derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) - real(dp),intent(out) :: dCanopyNetFlux_dCanairTemp ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) - real(dp),intent(out) :: dCanopyNetFlux_dCanopyTemp ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) - real(dp),intent(out) :: dCanopyNetFlux_dGroundTemp ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - real(dp),intent(out) :: dGroundNetFlux_dCanairTemp ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) - real(dp),intent(out) :: dGroundNetFlux_dCanopyTemp ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) - real(dp),intent(out) :: dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + real(rkind),intent(out) :: dCanairNetFlux_dCanairTemp ! derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) + real(rkind),intent(out) :: dCanairNetFlux_dCanopyTemp ! derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) + real(rkind),intent(out) :: dCanairNetFlux_dGroundTemp ! derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) + real(rkind),intent(out) :: dCanopyNetFlux_dCanairTemp ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) + real(rkind),intent(out) :: dCanopyNetFlux_dCanopyTemp ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) + real(rkind),intent(out) :: dCanopyNetFlux_dGroundTemp ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) + real(rkind),intent(out) :: dGroundNetFlux_dCanairTemp ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) + real(rkind),intent(out) :: dGroundNetFlux_dCanopyTemp ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) + real(rkind),intent(out) :: dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) ! output: liquid flux derivatives (canopy evap) - real(dp),intent(out) :: dCanopyEvaporation_dCanLiq ! derivative in canopy evaporation w.r.t. canopy liquid water content (s-1) - real(dp),intent(out) :: dCanopyEvaporation_dTCanair ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - real(dp),intent(out) :: dCanopyEvaporation_dTCanopy ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - real(dp),intent(out) :: dCanopyEvaporation_dTGround ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + real(rkind),intent(out) :: dCanopyEvaporation_dCanLiq ! derivative in canopy evaporation w.r.t. canopy liquid water content (s-1) + real(rkind),intent(out) :: dCanopyEvaporation_dTCanair ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(rkind),intent(out) :: dCanopyEvaporation_dTCanopy ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(rkind),intent(out) :: dCanopyEvaporation_dTGround ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) ! output: liquid flux derivatives (ground evap) - real(dp),intent(out) :: dGroundEvaporation_dCanLiq ! derivative in ground evaporation w.r.t. canopy liquid water content (s-1) - real(dp),intent(out) :: dGroundEvaporation_dTCanair ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - real(dp),intent(out) :: dGroundEvaporation_dTCanopy ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - real(dp),intent(out) :: dGroundEvaporation_dTGround ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + real(rkind),intent(out) :: dGroundEvaporation_dCanLiq ! derivative in ground evaporation w.r.t. canopy liquid water content (s-1) + real(rkind),intent(out) :: dGroundEvaporation_dTCanair ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(rkind),intent(out) :: dGroundEvaporation_dTCanopy ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(rkind),intent(out) :: dGroundEvaporation_dTGround ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) ! output: cross derivative terms - real(dp),intent(out) :: dCanopyNetFlux_dCanLiq ! derivative in net canopy fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - real(dp),intent(out) :: dGroundNetFlux_dCanLiq ! derivative in net ground fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + real(rkind),intent(out) :: dCanopyNetFlux_dCanLiq ! derivative in net canopy fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + real(rkind),intent(out) :: dGroundNetFlux_dCanLiq ! derivative in net ground fluxes w.r.t. canopy liquid water content (J kg-1 s-1) ! output: error control integer(i4b),intent(out) :: err ! error code @@ -286,10 +286,10 @@ subroutine vegNrgFluxFida(& ! --------------------------------------------------------------------------------------- ! local (general) character(LEN=256) :: cmessage ! error message of downwind routine - real(dp) :: VAI ! vegetation area index (m2 m-2) - real(dp) :: exposedVAI ! exposed vegetation area index (m2 m-2) - real(dp) :: totalCanopyWater ! total water on the vegetation canopy (kg m-2) - real(dp) :: scalarAquiferStorage ! aquifer storage (m) + real(rkind) :: VAI ! vegetation area index (m2 m-2) + real(rkind) :: exposedVAI ! exposed vegetation area index (m2 m-2) + real(rkind) :: totalCanopyWater ! total water on the vegetation canopy (kg m-2) + real(rkind) :: scalarAquiferStorage ! aquifer storage (m) ! local (compute numerical derivatives) integer(i4b),parameter :: unperturbed=1 ! named variable to identify the case of unperturbed state variables @@ -299,135 +299,135 @@ subroutine vegNrgFluxFida(& integer(i4b),parameter :: perturbStateCanLiq=5 ! named variable to identify the case where we perturb the canopy liquid water content integer(i4b) :: itry ! index of flux evaluation integer(i4b) :: nFlux ! number of flux evaluations - real(dp) :: groundTemp ! value of ground temperature used in flux calculations (may be perturbed) - real(dp) :: canopyTemp ! value of canopy temperature used in flux calculations (may be perturbed) - real(dp) :: canairTemp ! value of canopy air temperature used in flux calculations (may be perturbed) - real(dp) :: try0,try1 ! trial values to evaluate specific derivatives (testing only) + real(rkind) :: groundTemp ! value of ground temperature used in flux calculations (may be perturbed) + real(rkind) :: canopyTemp ! value of canopy temperature used in flux calculations (may be perturbed) + real(rkind) :: canairTemp ! value of canopy air temperature used in flux calculations (may be perturbed) + real(rkind) :: try0,try1 ! trial values to evaluate specific derivatives (testing only) ! local (saturation vapor pressure of veg) - real(dp) :: TV_celcius ! vegetaion temperature (C) - real(dp) :: TG_celcius ! ground temperature (C) - real(dp) :: dSVPCanopy_dCanopyTemp ! derivative in canopy saturated vapor pressure w.r.t. vegetation temperature (Pa/K) - real(dp) :: dSVPGround_dGroundTemp ! derivative in ground saturated vapor pressure w.r.t. ground temperature (Pa/K) + real(rkind) :: TV_celcius ! vegetaion temperature (C) + real(rkind) :: TG_celcius ! ground temperature (C) + real(rkind) :: dSVPCanopy_dCanopyTemp ! derivative in canopy saturated vapor pressure w.r.t. vegetation temperature (Pa/K) + real(rkind) :: dSVPGround_dGroundTemp ! derivative in ground saturated vapor pressure w.r.t. ground temperature (Pa/K) ! local (wetted canopy area) - real(dp) :: fracLiquidCanopy ! fraction of liquid water in the canopy (-) - real(dp) :: canopyWetFraction ! trial value of the canopy wetted fraction (-) - real(dp) :: dCanopyWetFraction_dWat ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) - real(dp) :: dCanopyWetFraction_dT ! derivative in wetted fraction w.r.t. canopy temperature (K-1) + real(rkind) :: fracLiquidCanopy ! fraction of liquid water in the canopy (-) + real(rkind) :: canopyWetFraction ! trial value of the canopy wetted fraction (-) + real(rkind) :: dCanopyWetFraction_dWat ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) + real(rkind) :: dCanopyWetFraction_dT ! derivative in wetted fraction w.r.t. canopy temperature (K-1) ! local (longwave radiation) - real(dp) :: expi ! exponential integral - real(dp) :: scaleLAI ! scaled LAI (computing diffuse transmissivity) - real(dp) :: diffuseTrans ! diffuse transmissivity (-) - real(dp) :: groundEmissivity ! emissivity of the ground surface (-) - real(dp),parameter :: vegEmissivity=0.98_rkind ! emissivity of vegetation (0.9665 in JULES) (-) - real(dp),parameter :: soilEmissivity=0.98_rkind ! emmisivity of the soil (0.9665 in JULES) (-) - real(dp),parameter :: snowEmissivity=0.99_rkind ! emissivity of snow (-) - real(dp) :: dLWNetCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) - real(dp) :: dLWNetGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) - real(dp) :: dLWNetCanopy_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) - real(dp) :: dLWNetGround_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: expi ! exponential integral + real(rkind) :: scaleLAI ! scaled LAI (computing diffuse transmissivity) + real(rkind) :: diffuseTrans ! diffuse transmissivity (-) + real(rkind) :: groundEmissivity ! emissivity of the ground surface (-) + real(rkind),parameter :: vegEmissivity=0.98_rkind ! emissivity of vegetation (0.9665 in JULES) (-) + real(rkind),parameter :: soilEmissivity=0.98_rkind ! emmisivity of the soil (0.9665 in JULES) (-) + real(rkind),parameter :: snowEmissivity=0.99_rkind ! emissivity of snow (-) + real(rkind) :: dLWNetCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dLWNetGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dLWNetCanopy_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dLWNetGround_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) ! local (aerodynamic resistance) - real(dp) :: scalarCanopyStabilityCorrection_old ! stability correction for the canopy (-) - real(dp) :: scalarGroundStabilityCorrection_old ! stability correction for the ground surface (-) + real(rkind) :: scalarCanopyStabilityCorrection_old ! stability correction for the canopy (-) + real(rkind) :: scalarGroundStabilityCorrection_old ! stability correction for the ground surface (-) ! local (turbulent heat transfer) - real(dp) :: z0Ground ! roughness length of the ground (ground below the canopy or non-vegetated surface) (m) - real(dp) :: soilEvapFactor ! soil water control on evaporation from non-vegetated surfaces - real(dp) :: soilRelHumidity_noSnow ! relative humidity in the soil pores [0-1] - real(dp) :: scalarLeafConductance ! leaf conductance (m s-1) - real(dp) :: scalarCanopyConductance ! canopy conductance (m s-1) - real(dp) :: scalarGroundConductanceSH ! ground conductance for sensible heat (m s-1) - real(dp) :: scalarGroundConductanceLH ! ground conductance for latent heat -- includes soil resistance (m s-1) - real(dp) :: scalarEvapConductance ! conductance for evaporation (m s-1) - real(dp) :: scalarTransConductance ! conductance for transpiration (m s-1) - real(dp) :: scalarTotalConductanceSH ! total conductance for sensible heat (m s-1) - real(dp) :: scalarTotalConductanceLH ! total conductance for latent heat (m s-1) - real(dp) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - real(dp) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - real(dp) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - real(dp) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - real(dp) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - real(dp) :: turbFluxCanair ! total turbulent heat fluxes exchanged at the canopy air space (W m-2) - real(dp) :: turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) - real(dp) :: turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) + real(rkind) :: z0Ground ! roughness length of the ground (ground below the canopy or non-vegetated surface) (m) + real(rkind) :: soilEvapFactor ! soil water control on evaporation from non-vegetated surfaces + real(rkind) :: soilRelHumidity_noSnow ! relative humidity in the soil pores [0-1] + real(rkind) :: scalarLeafConductance ! leaf conductance (m s-1) + real(rkind) :: scalarCanopyConductance ! canopy conductance (m s-1) + real(rkind) :: scalarGroundConductanceSH ! ground conductance for sensible heat (m s-1) + real(rkind) :: scalarGroundConductanceLH ! ground conductance for latent heat -- includes soil resistance (m s-1) + real(rkind) :: scalarEvapConductance ! conductance for evaporation (m s-1) + real(rkind) :: scalarTransConductance ! conductance for transpiration (m s-1) + real(rkind) :: scalarTotalConductanceSH ! total conductance for sensible heat (m s-1) + real(rkind) :: scalarTotalConductanceLH ! total conductance for latent heat (m s-1) + real(rkind) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + real(rkind) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + real(rkind) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + real(rkind) :: turbFluxCanair ! total turbulent heat fluxes exchanged at the canopy air space (W m-2) + real(rkind) :: turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) + real(rkind) :: turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) ! local (turbulent heat transfer -- compute numerical derivatives) ! (temporary scalar resistances when states are perturbed) - real(dp) :: trialLeafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) - real(dp) :: trialGroundResistance ! below canopy aerodynamic resistance (s m-1) - real(dp) :: trialCanopyResistance ! above canopy aerodynamic resistance (s m-1) - real(dp) :: notUsed_RiBulkCanopy ! bulk Richardson number for the canopy (-) - real(dp) :: notUsed_RiBulkGround ! bulk Richardson number for the ground surface (-) - real(dp) :: notUsed_z0Canopy ! roughness length of the vegetation canopy (m) - real(dp) :: notUsed_WindReductionFactor ! canopy wind reduction factor (-) - real(dp) :: notUsed_ZeroPlaneDisplacement ! zero plane displacement (m) - real(dp) :: notUsed_scalarCanopyStabilityCorrection ! stability correction for the canopy (-) - real(dp) :: notUsed_scalarGroundStabilityCorrection ! stability correction for the ground surface (-) - real(dp) :: notUsed_EddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) - real(dp) :: notUsed_FrictionVelocity ! friction velocity (m s-1) - real(dp) :: notUsed_WindspdCanopyTop ! windspeed at the top of the canopy (m s-1) - real(dp) :: notUsed_WindspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) - real(dp) :: notUsed_dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - real(dp) :: notUsed_dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - real(dp) :: notUsed_dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - real(dp) :: notUsed_dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - real(dp) :: notUsed_dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + real(rkind) :: trialLeafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) + real(rkind) :: trialGroundResistance ! below canopy aerodynamic resistance (s m-1) + real(rkind) :: trialCanopyResistance ! above canopy aerodynamic resistance (s m-1) + real(rkind) :: notUsed_RiBulkCanopy ! bulk Richardson number for the canopy (-) + real(rkind) :: notUsed_RiBulkGround ! bulk Richardson number for the ground surface (-) + real(rkind) :: notUsed_z0Canopy ! roughness length of the vegetation canopy (m) + real(rkind) :: notUsed_WindReductionFactor ! canopy wind reduction factor (-) + real(rkind) :: notUsed_ZeroPlaneDisplacement ! zero plane displacement (m) + real(rkind) :: notUsed_scalarCanopyStabilityCorrection ! stability correction for the canopy (-) + real(rkind) :: notUsed_scalarGroundStabilityCorrection ! stability correction for the ground surface (-) + real(rkind) :: notUsed_EddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) + real(rkind) :: notUsed_FrictionVelocity ! friction velocity (m s-1) + real(rkind) :: notUsed_WindspdCanopyTop ! windspeed at the top of the canopy (m s-1) + real(rkind) :: notUsed_WindspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) + real(rkind) :: notUsed_dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + real(rkind) :: notUsed_dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind) :: notUsed_dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + real(rkind) :: notUsed_dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind) :: notUsed_dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) ! (fluxes after perturbations in model states -- canopy air space) - real(dp) :: turbFluxCanair_dStateCanair ! turbulent exchange from the canopy air space to the atmosphere, after canopy air temperature is perturbed (W m-2) - real(dp) :: turbFluxCanair_dStateCanopy ! turbulent exchange from the canopy air space to the atmosphere, after canopy temperature is perturbed (W m-2) - real(dp) :: turbFluxCanair_dStateGround ! turbulent exchange from the canopy air space to the atmosphere, after ground temperature is perturbed (W m-2) - real(dp) :: turbFluxCanair_dStateCanliq ! turbulent exchange from the canopy air space to the atmosphere, after canopy liquid water content is perturbed (W m-2) + real(rkind) :: turbFluxCanair_dStateCanair ! turbulent exchange from the canopy air space to the atmosphere, after canopy air temperature is perturbed (W m-2) + real(rkind) :: turbFluxCanair_dStateCanopy ! turbulent exchange from the canopy air space to the atmosphere, after canopy temperature is perturbed (W m-2) + real(rkind) :: turbFluxCanair_dStateGround ! turbulent exchange from the canopy air space to the atmosphere, after ground temperature is perturbed (W m-2) + real(rkind) :: turbFluxCanair_dStateCanliq ! turbulent exchange from the canopy air space to the atmosphere, after canopy liquid water content is perturbed (W m-2) ! (fluxes after perturbations in model states -- vegetation canopy) - real(dp) :: turbFluxCanopy_dStateCanair ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy air temperature is perturbed (W m-2) - real(dp) :: turbFluxCanopy_dStateCanopy ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy temperature is perturbed (W m-2) - real(dp) :: turbFluxCanopy_dStateGround ! total turbulent heat fluxes from the canopy to the canopy air space, after ground temperature is perturbed (W m-2) - real(dp) :: turbFluxCanopy_dStateCanLiq ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy liquid water content is perturbed (W m-2) + real(rkind) :: turbFluxCanopy_dStateCanair ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy air temperature is perturbed (W m-2) + real(rkind) :: turbFluxCanopy_dStateCanopy ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy temperature is perturbed (W m-2) + real(rkind) :: turbFluxCanopy_dStateGround ! total turbulent heat fluxes from the canopy to the canopy air space, after ground temperature is perturbed (W m-2) + real(rkind) :: turbFluxCanopy_dStateCanLiq ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy liquid water content is perturbed (W m-2) ! (fluxes after perturbations in model states -- ground surface) - real(dp) :: turbFluxGround_dStateCanair ! total turbulent heat fluxes from the ground to the canopy air space, after canopy air temperature is perturbed (W m-2) - real(dp) :: turbFluxGround_dStateCanopy ! total turbulent heat fluxes from the ground to the canopy air space, after canopy temperature is perturbed (W m-2) - real(dp) :: turbFluxGround_dStateGround ! total turbulent heat fluxes from the ground to the canopy air space, after ground temperature is perturbed (W m-2) - real(dp) :: turbFluxGround_dStateCanLiq ! total turbulent heat fluxes from the ground to the canopy air space, after canopy liquid water content is perturbed (W m-2) + real(rkind) :: turbFluxGround_dStateCanair ! total turbulent heat fluxes from the ground to the canopy air space, after canopy air temperature is perturbed (W m-2) + real(rkind) :: turbFluxGround_dStateCanopy ! total turbulent heat fluxes from the ground to the canopy air space, after canopy temperature is perturbed (W m-2) + real(rkind) :: turbFluxGround_dStateGround ! total turbulent heat fluxes from the ground to the canopy air space, after ground temperature is perturbed (W m-2) + real(rkind) :: turbFluxGround_dStateCanLiq ! total turbulent heat fluxes from the ground to the canopy air space, after canopy liquid water content is perturbed (W m-2) ! (fluxes after perturbations in model states -- canopy evaporation) - real(dp) :: latHeatCanEvap_dStateCanair ! canopy evaporation after canopy air temperature is perturbed (W m-2) - real(dp) :: latHeatCanEvap_dStateCanopy ! canopy evaporation after canopy temperature is perturbed (W m-2) - real(dp) :: latHeatCanEvap_dStateGround ! canopy evaporation after ground temperature is perturbed (W m-2) - real(dp) :: latHeatCanEvap_dStateCanLiq ! canopy evaporation after canopy liquid water content is perturbed (W m-2) + real(rkind) :: latHeatCanEvap_dStateCanair ! canopy evaporation after canopy air temperature is perturbed (W m-2) + real(rkind) :: latHeatCanEvap_dStateCanopy ! canopy evaporation after canopy temperature is perturbed (W m-2) + real(rkind) :: latHeatCanEvap_dStateGround ! canopy evaporation after ground temperature is perturbed (W m-2) + real(rkind) :: latHeatCanEvap_dStateCanLiq ! canopy evaporation after canopy liquid water content is perturbed (W m-2) ! (flux derivatives -- canopy air space) - real(dp) :: dTurbFluxCanair_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(dp) :: dTurbFluxCanair_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) - real(dp) :: dTurbFluxCanair_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) - real(dp) :: dTurbFluxCanair_dCanLiq ! derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + real(rkind) :: dTurbFluxCanair_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanair_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanair_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanair_dCanLiq ! derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) ! (flux derivatives -- vegetation canopy) - real(dp) :: dTurbFluxCanopy_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(dp) :: dTurbFluxCanopy_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - real(dp) :: dTurbFluxCanopy_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - real(dp) :: dTurbFluxCanopy_dCanLiq ! derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + real(rkind) :: dTurbFluxCanopy_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanopy_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanopy_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanopy_dCanLiq ! derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) ! (flux derivatives -- ground surface) - real(dp) :: dTurbFluxGround_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(dp) :: dTurbFluxGround_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - real(dp) :: dTurbFluxGround_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - real(dp) :: dTurbFluxGround_dCanLiq ! derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + real(rkind) :: dTurbFluxGround_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dTurbFluxGround_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dTurbFluxGround_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dTurbFluxGround_dCanLiq ! derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) ! (liquid water flux derivatives -- canopy evap) - real(dp) :: dLatHeatCanopyEvap_dCanLiq ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (W kg-1) - real(dp) :: dLatHeatCanopyEvap_dTCanair ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) - real(dp) :: dLatHeatCanopyEvap_dTCanopy ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) - real(dp) :: dLatHeatCanopyEvap_dTGround ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dLatHeatCanopyEvap_dCanLiq ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (W kg-1) + real(rkind) :: dLatHeatCanopyEvap_dTCanair ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dLatHeatCanopyEvap_dTCanopy ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dLatHeatCanopyEvap_dTGround ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) ! (liquid water flux derivatives -- ground evap) - real(dp) :: dLatHeatGroundEvap_dCanLiq ! derivative in latent heat of ground evaporation w.r.t. canopy liquid water content (J kg-1 s-1) - real(dp) :: dLatHeatGroundEvap_dTCanair ! derivative in latent heat of ground evaporation w.r.t. canopy air temperature (W m-2 K-1) - real(dp) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) - real(dp) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dLatHeatGroundEvap_dCanLiq ! derivative in latent heat of ground evaporation w.r.t. canopy liquid water content (J kg-1 s-1) + real(rkind) :: dLatHeatGroundEvap_dTCanair ! derivative in latent heat of ground evaporation w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) ! --------------------------------------------------------------------------------------- ! point to variables in the data structure @@ -1525,20 +1525,20 @@ subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFact ! dummy variables logical(lgt),intent(in) :: derDesire ! flag to denote if analytical derivatives are desired logical(lgt),intent(in) :: smoothing ! flag to denote if smoothing is required - real(dp),intent(in) :: canopyLiq ! liquid water content (kg m-2) - real(dp),intent(in) :: canopyMax ! liquid water content (kg m-2) - real(dp),intent(in) :: canopyWettingFactor ! maximum wetted fraction of the canopy (-) - real(dp),intent(in) :: canopyWettingExp ! exponent in canopy wetting function (-) + real(rkind),intent(in) :: canopyLiq ! liquid water content (kg m-2) + real(rkind),intent(in) :: canopyMax ! liquid water content (kg m-2) + real(rkind),intent(in) :: canopyWettingFactor ! maximum wetted fraction of the canopy (-) + real(rkind),intent(in) :: canopyWettingExp ! exponent in canopy wetting function (-) - real(dp),intent(out) :: canopyWetFraction ! canopy wetted fraction (-) - real(dp),intent(out) :: canopyWetFractionDeriv ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) + real(rkind),intent(out) :: canopyWetFraction ! canopy wetted fraction (-) + real(rkind),intent(out) :: canopyWetFractionDeriv ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) ! local variables - real(dp) :: relativeCanopyWater ! water stored on vegetation canopy, expressed as a fraction of maximum storage (-) - real(dp) :: rawCanopyWetFraction ! initial value of the canopy wet fraction (before smoothing) - real(dp) :: rawWetFractionDeriv ! derivative in canopy wet fraction w.r.t. storage (kg-1 m2) - real(dp) :: smoothFunc ! smoothing function used to improve numerical stability at times with limited water storage (-) - real(dp) :: smoothFuncDeriv ! derivative in the smoothing function w.r.t.canopy storage (kg-1 m2) - real(dp) :: verySmall=epsilon(1._rkind) ! a very small number + real(rkind) :: relativeCanopyWater ! water stored on vegetation canopy, expressed as a fraction of maximum storage (-) + real(rkind) :: rawCanopyWetFraction ! initial value of the canopy wet fraction (before smoothing) + real(rkind) :: rawWetFractionDeriv ! derivative in canopy wet fraction w.r.t. storage (kg-1 m2) + real(rkind) :: smoothFunc ! smoothing function used to improve numerical stability at times with limited water storage (-) + real(rkind) :: smoothFuncDeriv ! derivative in the smoothing function w.r.t.canopy storage (kg-1 m2) + real(rkind) :: verySmall=epsilon(1._rkind) ! a very small number ! -------------------------------------------------------------------------------------------------------------- ! compute relative canopy water @@ -1587,15 +1587,15 @@ subroutine logisticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) implicit none ! dummy variables logical(lgt),intent(in) :: derDesire ! flag to denote if analytical derivatives are desired - real(dp),intent(in) :: canopyLiq ! liquid water content (kg m-2) - real(dp),intent(out) :: smoothFunc ! smoothing function (-) - real(dp),intent(out) :: smoothFuncDeriv ! derivative in smoothing function (kg-1 m-2) + real(rkind),intent(in) :: canopyLiq ! liquid water content (kg m-2) + real(rkind),intent(out) :: smoothFunc ! smoothing function (-) + real(rkind),intent(out) :: smoothFuncDeriv ! derivative in smoothing function (kg-1 m-2) ! local variables - real(dp) :: xArg ! argument used in the smoothing function (-) - real(dp) :: expX ! exp(-xArg) -- used multiple times - real(dp),parameter :: smoothThresh=0.01_rkind ! mid-point of the smoothing function (kg m-2) - real(dp),parameter :: smoothScale=0.001_rkind ! scaling factor for the smoothing function (kg m-2) - real(dp),parameter :: xLimit=50._rkind ! don't compute exponents for > xLimit + real(rkind) :: xArg ! argument used in the smoothing function (-) + real(rkind) :: expX ! exp(-xArg) -- used multiple times + real(rkind),parameter :: smoothThresh=0.01_rkind ! mid-point of the smoothing function (kg m-2) + real(rkind),parameter :: smoothScale=0.001_rkind ! scaling factor for the smoothing function (kg m-2) + real(rkind),parameter :: xLimit=50._rkind ! don't compute exponents for > xLimit ! -------------------------------------------------------------------------------------------------------------- ! compute argument in the smoothing function xArg = (canopyLiq - smoothThresh)/smoothScale @@ -1666,34 +1666,34 @@ subroutine longwaveBal(& integer(i4b),intent(in) :: ixDerivMethod ! choice of method used to compute derivative (analytical or numerical) logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation ! input: canopy and ground temperature - real(dp),intent(in) :: canopyTemp ! canopy temperature (K) - real(dp),intent(in) :: groundTemp ! ground temperature (K) + real(rkind),intent(in) :: canopyTemp ! canopy temperature (K) + real(rkind),intent(in) :: groundTemp ! ground temperature (K) ! input: canopy and ground emissivity - real(dp),intent(in) :: emc ! canopy emissivity (-) - real(dp),intent(in) :: emg ! ground emissivity (-) + real(rkind),intent(in) :: emc ! canopy emissivity (-) + real(rkind),intent(in) :: emg ! ground emissivity (-) ! input: forcing - real(dp),intent(in) :: LWRadUbound ! downwelling longwave radiation at the upper boundary (W m-2) + real(rkind),intent(in) :: LWRadUbound ! downwelling longwave radiation at the upper boundary (W m-2) ! output: sources - real(dp),intent(out) :: LWRadCanopy ! longwave radiation emitted from the canopy (W m-2) - real(dp),intent(out) :: LWRadGround ! longwave radiation emitted at the ground surface (W m-2) + real(rkind),intent(out) :: LWRadCanopy ! longwave radiation emitted from the canopy (W m-2) + real(rkind),intent(out) :: LWRadGround ! longwave radiation emitted at the ground surface (W m-2) ! output: individual fluxes - real(dp),intent(out) :: LWRadUbound2Canopy ! downward atmospheric longwave radiation absorbed by the canopy (W m-2) - real(dp),intent(out) :: LWRadUbound2Ground ! downward atmospheric longwave radiation absorbed by the ground (W m-2) - real(dp),intent(out) :: LWRadUbound2Ubound ! atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) - real(dp),intent(out) :: LWRadCanopy2Ubound ! longwave radiation emitted from canopy lost thru upper boundary (W m-2) - real(dp),intent(out) :: LWRadCanopy2Ground ! longwave radiation emitted from canopy absorbed by the ground (W m-2) - real(dp),intent(out) :: LWRadCanopy2Canopy ! canopy longwave reflected from ground and absorbed by the canopy (W m-2) - real(dp),intent(out) :: LWRadGround2Ubound ! longwave radiation emitted from ground lost thru upper boundary (W m-2) - real(dp),intent(out) :: LWRadGround2Canopy ! longwave radiation emitted from ground and absorbed by the canopy (W m-2) + real(rkind),intent(out) :: LWRadUbound2Canopy ! downward atmospheric longwave radiation absorbed by the canopy (W m-2) + real(rkind),intent(out) :: LWRadUbound2Ground ! downward atmospheric longwave radiation absorbed by the ground (W m-2) + real(rkind),intent(out) :: LWRadUbound2Ubound ! atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) + real(rkind),intent(out) :: LWRadCanopy2Ubound ! longwave radiation emitted from canopy lost thru upper boundary (W m-2) + real(rkind),intent(out) :: LWRadCanopy2Ground ! longwave radiation emitted from canopy absorbed by the ground (W m-2) + real(rkind),intent(out) :: LWRadCanopy2Canopy ! canopy longwave reflected from ground and absorbed by the canopy (W m-2) + real(rkind),intent(out) :: LWRadGround2Ubound ! longwave radiation emitted from ground lost thru upper boundary (W m-2) + real(rkind),intent(out) :: LWRadGround2Canopy ! longwave radiation emitted from ground and absorbed by the canopy (W m-2) ! output: net fluxes - real(dp),intent(out) :: LWNetCanopy ! net longwave radiation at the canopy (W m-2) - real(dp),intent(out) :: LWNetGround ! net longwave radiation at the ground surface (W m-2) - real(dp),intent(out) :: LWNetUbound ! net longwave radiation at the upper boundary (W m-2) + real(rkind),intent(out) :: LWNetCanopy ! net longwave radiation at the canopy (W m-2) + real(rkind),intent(out) :: LWNetGround ! net longwave radiation at the ground surface (W m-2) + real(rkind),intent(out) :: LWNetUbound ! net longwave radiation at the upper boundary (W m-2) ! output: flux derivatives - real(dp),intent(out) :: dLWNetCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) - real(dp),intent(out) :: dLWNetGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) - real(dp),intent(out) :: dLWNetCanopy_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) - real(dp),intent(out) :: dLWNetGround_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) + real(rkind),intent(out) :: dLWNetCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) + real(rkind),intent(out) :: dLWNetGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) + real(rkind),intent(out) :: dLWNetCanopy_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) + real(rkind),intent(out) :: dLWNetGround_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -1704,16 +1704,16 @@ subroutine longwaveBal(& integer(i4b),parameter :: perturbStateGround=3 ! named variable to identify the case where we perturb the ground temperature integer(i4b) :: itry ! index of flux evaluation integer(i4b) :: nFlux ! number of flux evaluations - real(dp) :: TCan ! value of canopy temperature used in flux calculations (may be perturbed) - real(dp) :: TGnd ! value of ground temperature used in flux calculations (may be perturbed) - real(dp) :: fluxBalance ! check energy closure (W m-2) - real(dp),parameter :: fluxTolerance=1.e-10_rkind ! tolerance for energy closure (W m-2) - real(dp) :: dLWRadCanopy_dTCanopy ! derivative in emitted radiation at the canopy w.r.t. canopy temperature - real(dp) :: dLWRadGround_dTGround ! derivative in emitted radiation at the ground w.r.t. ground temperature - real(dp) :: LWNetCanopy_dStateCanopy ! net lw canopy flux after perturbation in canopy temperature - real(dp) :: LWNetGround_dStateCanopy ! net lw ground flux after perturbation in canopy temperature - real(dp) :: LWNetCanopy_dStateGround ! net lw canopy flux after perturbation in ground temperature - real(dp) :: LWNetGround_dStateGround ! net lw ground flux after perturbation in ground temperature + real(rkind) :: TCan ! value of canopy temperature used in flux calculations (may be perturbed) + real(rkind) :: TGnd ! value of ground temperature used in flux calculations (may be perturbed) + real(rkind) :: fluxBalance ! check energy closure (W m-2) + real(rkind),parameter :: fluxTolerance=1.e-10_rkind ! tolerance for energy closure (W m-2) + real(rkind) :: dLWRadCanopy_dTCanopy ! derivative in emitted radiation at the canopy w.r.t. canopy temperature + real(rkind) :: dLWRadGround_dTGround ! derivative in emitted radiation at the ground w.r.t. ground temperature + real(rkind) :: LWNetCanopy_dStateCanopy ! net lw canopy flux after perturbation in canopy temperature + real(rkind) :: LWNetGround_dStateCanopy ! net lw ground flux after perturbation in canopy temperature + real(rkind) :: LWNetCanopy_dStateGround ! net lw canopy flux after perturbation in ground temperature + real(rkind) :: LWNetGround_dStateGround ! net lw ground flux after perturbation in ground temperature ! ----------------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='longwaveBal/' @@ -1940,49 +1940,49 @@ subroutine aeroResist(& integer(i4b),intent(in) :: ixWindProfile ! choice of canopy wind profile integer(i4b),intent(in) :: ixStability ! choice of stability function ! input: above-canopy forcing data - real(dp),intent(in) :: mHeight ! measurement height (m) - real(dp),intent(in) :: airtemp ! air temperature at some height above the surface (K) - real(dp),intent(in) :: windspd ! wind speed at some height above the surface (m s-1) + real(rkind),intent(in) :: mHeight ! measurement height (m) + real(rkind),intent(in) :: airtemp ! air temperature at some height above the surface (K) + real(rkind),intent(in) :: windspd ! wind speed at some height above the surface (m s-1) ! input: temperature (canopy, ground, canopy air space) - real(dp),intent(in) :: canairTemp ! temperature of the canopy air space (K) - real(dp),intent(in) :: groundTemp ! ground temperature (K) + real(rkind),intent(in) :: canairTemp ! temperature of the canopy air space (K) + real(rkind),intent(in) :: groundTemp ! ground temperature (K) ! input: diagnostic variables - real(dp),intent(in) :: exposedVAI ! exposed vegetation area index -- leaf plus stem (m2 m-2) - real(dp),intent(in) :: snowDepth ! snow depth (m) + real(rkind),intent(in) :: exposedVAI ! exposed vegetation area index -- leaf plus stem (m2 m-2) + real(rkind),intent(in) :: snowDepth ! snow depth (m) ! input: parameters - real(dp),intent(in) :: z0Ground ! roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) - real(dp),intent(in) :: z0CanopyParam ! roughness length of the canopy (m) - real(dp),intent(in) :: zpdFraction ! zero plane displacement / canopy height (-) - real(dp),intent(in) :: critRichNumber ! critical value for the bulk Richardson number where turbulence ceases (-) - real(dp),intent(in) :: Louis79_bparam ! parameter in Louis (1979) stability function - real(dp),intent(in) :: Mahrt87_eScale ! exponential scaling factor in the Mahrt (1987) stability function - real(dp),intent(in) :: windReductionParam ! canopy wind reduction parameter (-) - real(dp),intent(in) :: leafExchangeCoeff ! turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) - real(dp),intent(in) :: leafDimension ! characteristic leaf dimension (m) - real(dp),intent(in) :: heightCanopyTop ! height at the top of the vegetation canopy (m) - real(dp),intent(in) :: heightCanopyBottom ! height at the bottom of the vegetation canopy (m) + real(rkind),intent(in) :: z0Ground ! roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) + real(rkind),intent(in) :: z0CanopyParam ! roughness length of the canopy (m) + real(rkind),intent(in) :: zpdFraction ! zero plane displacement / canopy height (-) + real(rkind),intent(in) :: critRichNumber ! critical value for the bulk Richardson number where turbulence ceases (-) + real(rkind),intent(in) :: Louis79_bparam ! parameter in Louis (1979) stability function + real(rkind),intent(in) :: Mahrt87_eScale ! exponential scaling factor in the Mahrt (1987) stability function + real(rkind),intent(in) :: windReductionParam ! canopy wind reduction parameter (-) + real(rkind),intent(in) :: leafExchangeCoeff ! turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) + real(rkind),intent(in) :: leafDimension ! characteristic leaf dimension (m) + real(rkind),intent(in) :: heightCanopyTop ! height at the top of the vegetation canopy (m) + real(rkind),intent(in) :: heightCanopyBottom ! height at the bottom of the vegetation canopy (m) ! output: stability corrections - real(dp),intent(out) :: RiBulkCanopy ! bulk Richardson number for the canopy (-) - real(dp),intent(out) :: RiBulkGround ! bulk Richardson number for the ground surface (-) - real(dp),intent(out) :: canopyStabilityCorrection ! stability correction for the canopy (-) - real(dp),intent(out) :: groundStabilityCorrection ! stability correction for the ground surface (-) + real(rkind),intent(out) :: RiBulkCanopy ! bulk Richardson number for the canopy (-) + real(rkind),intent(out) :: RiBulkGround ! bulk Richardson number for the ground surface (-) + real(rkind),intent(out) :: canopyStabilityCorrection ! stability correction for the canopy (-) + real(rkind),intent(out) :: groundStabilityCorrection ! stability correction for the ground surface (-) ! output: scalar resistances - real(dp),intent(out) :: z0Canopy ! roughness length of the vegetation canopy (m) - real(dp),intent(out) :: windReductionFactor ! canopy wind reduction factor (-) - real(dp),intent(out) :: zeroPlaneDisplacement ! zero plane displacement (m) - real(dp),intent(out) :: eddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) - real(dp),intent(out) :: frictionVelocity ! friction velocity (m s-1) - real(dp),intent(out) :: windspdCanopyTop ! windspeed at the top of the canopy (m s-1) - real(dp),intent(out) :: windspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) - real(dp),intent(out) :: leafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) - real(dp),intent(out) :: groundResistance ! below canopy aerodynamic resistance (s m-1) - real(dp),intent(out) :: canopyResistance ! above canopy aerodynamic resistance (s m-1) + real(rkind),intent(out) :: z0Canopy ! roughness length of the vegetation canopy (m) + real(rkind),intent(out) :: windReductionFactor ! canopy wind reduction factor (-) + real(rkind),intent(out) :: zeroPlaneDisplacement ! zero plane displacement (m) + real(rkind),intent(out) :: eddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) + real(rkind),intent(out) :: frictionVelocity ! friction velocity (m s-1) + real(rkind),intent(out) :: windspdCanopyTop ! windspeed at the top of the canopy (m s-1) + real(rkind),intent(out) :: windspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) + real(rkind),intent(out) :: leafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) + real(rkind),intent(out) :: groundResistance ! below canopy aerodynamic resistance (s m-1) + real(rkind),intent(out) :: canopyResistance ! above canopy aerodynamic resistance (s m-1) ! output: derivatives in scalar resistances - real(dp),intent(out) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - real(dp),intent(out) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - real(dp),intent(out) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - real(dp),intent(out) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - real(dp),intent(out) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + real(rkind),intent(out) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + real(rkind),intent(out) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind),intent(out) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + real(rkind),intent(out) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind),intent(out) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -1990,45 +1990,45 @@ subroutine aeroResist(& ! local variables: general character(LEN=256) :: cmessage ! error message of downwind routine ! local variables: vegetation roughness and dispalcement height - real(dp),parameter :: oneThird=1._rkind/3._rkind ! 1/3 - real(dp),parameter :: twoThirds=2._rkind/3._rkind ! 2/3 - real(dp),parameter :: C_r = 0.3 ! roughness element drag coefficient (-) from Raupach (BLM, 1994) - real(dp),parameter :: C_s = 0.003_rkind ! substrate surface drag coefficient (-) from Raupach (BLM, 1994) - real(dp),parameter :: approxDragCoef_max = 0.3_rkind ! maximum value of the approximate drag coefficient (-) from Raupach (BLM, 1994) - real(dp),parameter :: psi_h = 0.193_rkind ! roughness sub-layer influence function (-) from Raupach (BLM, 1994) - real(dp),parameter :: c_d1 = 7.5_rkind ! scaling parameter used to define displacement height (-) from Raupach (BLM, 1994) - real(dp),parameter :: cd_CM = 0.2_rkind ! mean drag coefficient for individual leaves (-) from Choudhury and Monteith (QJRMS, 1988) - real(dp) :: funcLAI ! temporary variable to calculate zero plane displacement for the canopy - real(dp) :: fracCanopyHeight ! zero plane displacement expressed as a fraction of canopy height - real(dp) :: approxDragCoef ! approximate drag coefficient used in the computation of canopy roughness length (-) + real(rkind),parameter :: oneThird=1._rkind/3._rkind ! 1/3 + real(rkind),parameter :: twoThirds=2._rkind/3._rkind ! 2/3 + real(rkind),parameter :: C_r = 0.3 ! roughness element drag coefficient (-) from Raupach (BLM, 1994) + real(rkind),parameter :: C_s = 0.003_rkind ! substrate surface drag coefficient (-) from Raupach (BLM, 1994) + real(rkind),parameter :: approxDragCoef_max = 0.3_rkind ! maximum value of the approximate drag coefficient (-) from Raupach (BLM, 1994) + real(rkind),parameter :: psi_h = 0.193_rkind ! roughness sub-layer influence function (-) from Raupach (BLM, 1994) + real(rkind),parameter :: c_d1 = 7.5_rkind ! scaling parameter used to define displacement height (-) from Raupach (BLM, 1994) + real(rkind),parameter :: cd_CM = 0.2_rkind ! mean drag coefficient for individual leaves (-) from Choudhury and Monteith (QJRMS, 1988) + real(rkind) :: funcLAI ! temporary variable to calculate zero plane displacement for the canopy + real(rkind) :: fracCanopyHeight ! zero plane displacement expressed as a fraction of canopy height + real(rkind) :: approxDragCoef ! approximate drag coefficient used in the computation of canopy roughness length (-) ! local variables: resistance - real(dp) :: canopyExNeut ! surface-atmosphere exchange coefficient under neutral conditions (-) - real(dp) :: groundExNeut ! surface-atmosphere exchange coefficient under neutral conditions (-) - real(dp) :: sfc2AtmExchangeCoeff_canopy ! surface-atmosphere exchange coefficient after stability corrections (-) - real(dp) :: groundResistanceNeutral ! ground resistance under neutral conditions (s m-1) - real(dp) :: windConvFactor_fv ! factor to convert friction velocity to wind speed at top of canopy (-) - real(dp) :: windConvFactor ! factor to convert wind speed at top of canopy to wind speed at a given height in the canopy (-) - real(dp) :: referenceHeight ! z0Canopy+zeroPlaneDisplacement (m) - real(dp) :: windspdRefHeight ! windspeed at the reference height (m/s) - real(dp) :: heightAboveGround ! height above the snow surface (m) - real(dp) :: heightCanopyTopAboveSnow ! height at the top of the vegetation canopy relative to snowpack (m) - real(dp) :: heightCanopyBottomAboveSnow ! height at the bottom of the vegetation canopy relative to snowpack (m) - real(dp),parameter :: xTolerance=0.1_rkind ! tolerance to handle the transition from exponential to log-below canopy + real(rkind) :: canopyExNeut ! surface-atmosphere exchange coefficient under neutral conditions (-) + real(rkind) :: groundExNeut ! surface-atmosphere exchange coefficient under neutral conditions (-) + real(rkind) :: sfc2AtmExchangeCoeff_canopy ! surface-atmosphere exchange coefficient after stability corrections (-) + real(rkind) :: groundResistanceNeutral ! ground resistance under neutral conditions (s m-1) + real(rkind) :: windConvFactor_fv ! factor to convert friction velocity to wind speed at top of canopy (-) + real(rkind) :: windConvFactor ! factor to convert wind speed at top of canopy to wind speed at a given height in the canopy (-) + real(rkind) :: referenceHeight ! z0Canopy+zeroPlaneDisplacement (m) + real(rkind) :: windspdRefHeight ! windspeed at the reference height (m/s) + real(rkind) :: heightAboveGround ! height above the snow surface (m) + real(rkind) :: heightCanopyTopAboveSnow ! height at the top of the vegetation canopy relative to snowpack (m) + real(rkind) :: heightCanopyBottomAboveSnow ! height at the bottom of the vegetation canopy relative to snowpack (m) + real(rkind),parameter :: xTolerance=0.1_rkind ! tolerance to handle the transition from exponential to log-below canopy ! local variables: derivatives - real(dp) :: dFV_dT ! derivative in friction velocity w.r.t. canopy air temperature - real(dp) :: dED_dT ! derivative in eddy diffusivity at the top of the canopy w.r.t. canopy air temperature - real(dp) :: dGR_dT ! derivative in neutral ground resistance w.r.t. canopy air temperature - real(dp) :: tmp1,tmp2 ! temporary variables used in calculation of ground resistance - real(dp) :: dCanopyStabilityCorrection_dRich ! derivative in stability correction w.r.t. Richardson number for the canopy (-) - real(dp) :: dGroundStabilityCorrection_dRich ! derivative in stability correction w.r.t. Richardson number for the ground surface (-) - real(dp) :: dCanopyStabilityCorrection_dAirTemp ! (not used) derivative in stability correction w.r.t. air temperature (K-1) - real(dp) :: dGroundStabilityCorrection_dAirTemp ! (not used) derivative in stability correction w.r.t. air temperature (K-1) - real(dp) :: dCanopyStabilityCorrection_dCasTemp ! derivative in canopy stability correction w.r.t. canopy air space temperature (K-1) - real(dp) :: dGroundStabilityCorrection_dCasTemp ! derivative in ground stability correction w.r.t. canopy air space temperature (K-1) - real(dp) :: dGroundStabilityCorrection_dSfcTemp ! derivative in ground stability correction w.r.t. surface temperature (K-1) - real(dp) :: singleLeafConductance ! leaf boundary layer conductance (m s-1) - real(dp) :: canopyLeafConductance ! leaf boundary layer conductance -- scaled up to the canopy (m s-1) - real(dp) :: leaf2CanopyScaleFactor ! factor to scale from the leaf to the canopy [m s-(1/2)] + real(rkind) :: dFV_dT ! derivative in friction velocity w.r.t. canopy air temperature + real(rkind) :: dED_dT ! derivative in eddy diffusivity at the top of the canopy w.r.t. canopy air temperature + real(rkind) :: dGR_dT ! derivative in neutral ground resistance w.r.t. canopy air temperature + real(rkind) :: tmp1,tmp2 ! temporary variables used in calculation of ground resistance + real(rkind) :: dCanopyStabilityCorrection_dRich ! derivative in stability correction w.r.t. Richardson number for the canopy (-) + real(rkind) :: dGroundStabilityCorrection_dRich ! derivative in stability correction w.r.t. Richardson number for the ground surface (-) + real(rkind) :: dCanopyStabilityCorrection_dAirTemp ! (not used) derivative in stability correction w.r.t. air temperature (K-1) + real(rkind) :: dGroundStabilityCorrection_dAirTemp ! (not used) derivative in stability correction w.r.t. air temperature (K-1) + real(rkind) :: dCanopyStabilityCorrection_dCasTemp ! derivative in canopy stability correction w.r.t. canopy air space temperature (K-1) + real(rkind) :: dGroundStabilityCorrection_dCasTemp ! derivative in ground stability correction w.r.t. canopy air space temperature (K-1) + real(rkind) :: dGroundStabilityCorrection_dSfcTemp ! derivative in ground stability correction w.r.t. surface temperature (K-1) + real(rkind) :: singleLeafConductance ! leaf boundary layer conductance (m s-1) + real(rkind) :: canopyLeafConductance ! leaf boundary layer conductance -- scaled up to the canopy (m s-1) + real(rkind) :: leaf2CanopyScaleFactor ! factor to scale from the leaf to the canopy [m s-(1/2)] ! ----------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='aeroResist/' @@ -2370,27 +2370,27 @@ subroutine soilResist(& integer(i4b),intent(in) :: ixSoilResist ! choice of function for the soil moisture control on stomatal resistance integer(i4b),intent(in) :: ixGroundwater ! choice of groundwater representation ! input (variables) - real(dp),intent(in) :: mLayerMatricHead(:) ! matric head in each layer (m) - real(dp),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid water in each layer (-) - real(dp),intent(in) :: scalarAquiferStorage ! aquifer storage (m) + real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head in each layer (m) + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid water in each layer (-) + real(rkind),intent(in) :: scalarAquiferStorage ! aquifer storage (m) ! input (diagnostic variables) - real(dp),intent(in) :: mLayerRootDensity(:) ! root density in each layer (-) - real(dp),intent(in) :: scalarAquiferRootFrac ! fraction of roots below the lowest unsaturated layer (-) + real(rkind),intent(in) :: mLayerRootDensity(:) ! root density in each layer (-) + real(rkind),intent(in) :: scalarAquiferRootFrac ! fraction of roots below the lowest unsaturated layer (-) ! input (parameters) - real(dp),intent(in) :: plantWiltPsi ! matric head at wilting point (m) - real(dp),intent(in) :: soilStressParam ! parameter in the exponential soil stress function (-) - real(dp),intent(in) :: critSoilWilting ! critical vol. liq. water content when plants are wilting (-) - real(dp),intent(in) :: critSoilTranspire ! critical vol. liq. water content when transpiration is limited (-) - real(dp),intent(in) :: critAquiferTranspire ! critical aquifer storage value when transpiration is limited (m) + real(rkind),intent(in) :: plantWiltPsi ! matric head at wilting point (m) + real(rkind),intent(in) :: soilStressParam ! parameter in the exponential soil stress function (-) + real(rkind),intent(in) :: critSoilWilting ! critical vol. liq. water content when plants are wilting (-) + real(rkind),intent(in) :: critSoilTranspire ! critical vol. liq. water content when transpiration is limited (-) + real(rkind),intent(in) :: critAquiferTranspire ! critical aquifer storage value when transpiration is limited (m) ! output - real(dp),intent(out) :: wAvgTranspireLimitFac ! intent(out): weighted average of the transpiration limiting factor (-) - real(dp),intent(out) :: mLayerTranspireLimitFac(:) ! intent(out): transpiration limiting factor in each layer (-) - real(dp),intent(out) :: aquiferTranspireLimitFac ! intent(out): transpiration limiting factor for the aquifer (-) + real(rkind),intent(out) :: wAvgTranspireLimitFac ! intent(out): weighted average of the transpiration limiting factor (-) + real(rkind),intent(out) :: mLayerTranspireLimitFac(:) ! intent(out): transpiration limiting factor in each layer (-) + real(rkind),intent(out) :: aquiferTranspireLimitFac ! intent(out): transpiration limiting factor for the aquifer (-) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local variables - real(dp) :: gx ! stress function for the soil layers - real(dp),parameter :: verySmall=epsilon(gx) ! a very small number + real(rkind) :: gx ! stress function for the soil layers + real(rkind),parameter :: verySmall=epsilon(gx) ! a very small number integer(i4b) :: iLayer ! index of soil layer ! initialize error control err=0; message='soilResist/' @@ -2541,138 +2541,138 @@ subroutine turbFluxes(& logical(lgt),intent(in) :: computeVegFlux ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) integer(i4b),intent(in) :: ixDerivMethod ! choice of method used to compute derivative (analytical or numerical) ! input: above-canopy forcing data - real(dp),intent(in) :: airtemp ! air temperature at some height above the surface (K) - real(dp),intent(in) :: airpres ! air pressure of the air above the vegetation canopy (Pa) - real(dp),intent(in) :: VPair ! vapor pressure of the air above the vegetation canopy (Pa) + real(rkind),intent(in) :: airtemp ! air temperature at some height above the surface (K) + real(rkind),intent(in) :: airpres ! air pressure of the air above the vegetation canopy (Pa) + real(rkind),intent(in) :: VPair ! vapor pressure of the air above the vegetation canopy (Pa) ! input: latent heat of sublimation/vaporization - real(dp),intent(in) :: latHeatSubVapCanopy ! latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) - real(dp),intent(in) :: latHeatSubVapGround ! latent heat of sublimation/vaporization for the ground surface (J kg-1) + real(rkind),intent(in) :: latHeatSubVapCanopy ! latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) + real(rkind),intent(in) :: latHeatSubVapGround ! latent heat of sublimation/vaporization for the ground surface (J kg-1) ! input: canopy and ground temperature - real(dp),intent(in) :: canairTemp ! temperature of the canopy air space (K) - real(dp),intent(in) :: canopyTemp ! canopy temperature (K) - real(dp),intent(in) :: groundTemp ! ground temperature (K) - real(dp),intent(in) :: satVP_CanopyTemp ! saturation vapor pressure at the temperature of the veg canopy (Pa) - real(dp),intent(in) :: satVP_GroundTemp ! saturation vapor pressure at the temperature of the ground (Pa) - real(dp),intent(in) :: dSVPCanopy_dCanopyTemp ! derivative in canopy saturation vapor pressure w.r.t. canopy temperature (Pa K-1) - real(dp),intent(in) :: dSVPGround_dGroundTemp ! derivative in ground saturation vapor pressure w.r.t. ground temperature (Pa K-1) + real(rkind),intent(in) :: canairTemp ! temperature of the canopy air space (K) + real(rkind),intent(in) :: canopyTemp ! canopy temperature (K) + real(rkind),intent(in) :: groundTemp ! ground temperature (K) + real(rkind),intent(in) :: satVP_CanopyTemp ! saturation vapor pressure at the temperature of the veg canopy (Pa) + real(rkind),intent(in) :: satVP_GroundTemp ! saturation vapor pressure at the temperature of the ground (Pa) + real(rkind),intent(in) :: dSVPCanopy_dCanopyTemp ! derivative in canopy saturation vapor pressure w.r.t. canopy temperature (Pa K-1) + real(rkind),intent(in) :: dSVPGround_dGroundTemp ! derivative in ground saturation vapor pressure w.r.t. ground temperature (Pa K-1) ! input: diagnostic variables - real(dp),intent(in) :: exposedVAI ! exposed vegetation area index -- leaf plus stem (m2 m-2) - real(dp),intent(in) :: canopyWetFraction ! fraction of canopy that is wet [0-1] - real(dp),intent(in) :: dCanopyWetFraction_dWat ! derivative in the canopy wetted fraction w.r.t. liquid water content (kg-1 m-2) - real(dp),intent(in) :: dCanopyWetFraction_dT ! derivative in the canopy wetted fraction w.r.t. canopy temperature (K-1) - real(dp),intent(in) :: canopySunlitLAI ! sunlit leaf area (-) - real(dp),intent(in) :: canopyShadedLAI ! shaded leaf area (-) - real(dp),intent(in) :: soilRelHumidity ! relative humidity in the soil pores [0-1] - real(dp),intent(in) :: soilResistance ! resistance from the soil (s m-1) - real(dp),intent(in) :: leafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) - real(dp),intent(in) :: groundResistance ! below canopy aerodynamic resistance (s m-1) - real(dp),intent(in) :: canopyResistance ! above canopy aerodynamic resistance (s m-1) - real(dp),intent(in) :: stomResistSunlit ! stomatal resistance for sunlit leaves (s m-1) - real(dp),intent(in) :: stomResistShaded ! stomatal resistance for shaded leaves (s m-1) + real(rkind),intent(in) :: exposedVAI ! exposed vegetation area index -- leaf plus stem (m2 m-2) + real(rkind),intent(in) :: canopyWetFraction ! fraction of canopy that is wet [0-1] + real(rkind),intent(in) :: dCanopyWetFraction_dWat ! derivative in the canopy wetted fraction w.r.t. liquid water content (kg-1 m-2) + real(rkind),intent(in) :: dCanopyWetFraction_dT ! derivative in the canopy wetted fraction w.r.t. canopy temperature (K-1) + real(rkind),intent(in) :: canopySunlitLAI ! sunlit leaf area (-) + real(rkind),intent(in) :: canopyShadedLAI ! shaded leaf area (-) + real(rkind),intent(in) :: soilRelHumidity ! relative humidity in the soil pores [0-1] + real(rkind),intent(in) :: soilResistance ! resistance from the soil (s m-1) + real(rkind),intent(in) :: leafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) + real(rkind),intent(in) :: groundResistance ! below canopy aerodynamic resistance (s m-1) + real(rkind),intent(in) :: canopyResistance ! above canopy aerodynamic resistance (s m-1) + real(rkind),intent(in) :: stomResistSunlit ! stomatal resistance for sunlit leaves (s m-1) + real(rkind),intent(in) :: stomResistShaded ! stomatal resistance for shaded leaves (s m-1) ! input: derivatives in scalar resistances - real(dp),intent(in) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - real(dp),intent(in) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - real(dp),intent(in) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - real(dp),intent(in) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - real(dp),intent(in) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + real(rkind),intent(in) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + real(rkind),intent(in) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind),intent(in) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + real(rkind),intent(in) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind),intent(in) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) ! --------------------------------------------------------------------------------------------------------------------------------------------------------------- ! output: conductances -- used to test derivatives - real(dp),intent(out) :: leafConductance ! leaf conductance (m s-1) - real(dp),intent(out) :: canopyConductance ! canopy conductance (m s-1) - real(dp),intent(out) :: groundConductanceSH ! ground conductance for sensible heat (m s-1) - real(dp),intent(out) :: groundConductanceLH ! ground conductance for latent heat -- includes soil resistance (m s-1) - real(dp),intent(out) :: evapConductance ! conductance for evaporation (m s-1) - real(dp),intent(out) :: transConductance ! conductance for transpiration (m s-1) - real(dp),intent(out) :: totalConductanceSH ! total conductance for sensible heat (m s-1) - real(dp),intent(out) :: totalConductanceLH ! total conductance for latent heat (m s-1) + real(rkind),intent(out) :: leafConductance ! leaf conductance (m s-1) + real(rkind),intent(out) :: canopyConductance ! canopy conductance (m s-1) + real(rkind),intent(out) :: groundConductanceSH ! ground conductance for sensible heat (m s-1) + real(rkind),intent(out) :: groundConductanceLH ! ground conductance for latent heat -- includes soil resistance (m s-1) + real(rkind),intent(out) :: evapConductance ! conductance for evaporation (m s-1) + real(rkind),intent(out) :: transConductance ! conductance for transpiration (m s-1) + real(rkind),intent(out) :: totalConductanceSH ! total conductance for sensible heat (m s-1) + real(rkind),intent(out) :: totalConductanceLH ! total conductance for latent heat (m s-1) ! output: canopy air space variables - real(dp),intent(out) :: VP_CanopyAir ! vapor pressure of the canopy air space (Pa) + real(rkind),intent(out) :: VP_CanopyAir ! vapor pressure of the canopy air space (Pa) ! output: fluxes from the vegetation canopy - real(dp),intent(out) :: senHeatCanopy ! sensible heat flux from the canopy to the canopy air space (W m-2) - real(dp),intent(out) :: latHeatCanopyEvap ! latent heat flux associated with evaporation from the canopy to the canopy air space (W m-2) - real(dp),intent(out) :: latHeatCanopyTrans ! latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) + real(rkind),intent(out) :: senHeatCanopy ! sensible heat flux from the canopy to the canopy air space (W m-2) + real(rkind),intent(out) :: latHeatCanopyEvap ! latent heat flux associated with evaporation from the canopy to the canopy air space (W m-2) + real(rkind),intent(out) :: latHeatCanopyTrans ! latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) ! output: fluxes from non-vegetated surfaces (ground surface below vegetation, bare ground, or snow covered vegetation) - real(dp),intent(out) :: senHeatGround ! sensible heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) - real(dp),intent(out) :: latHeatGround ! latent heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) + real(rkind),intent(out) :: senHeatGround ! sensible heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) + real(rkind),intent(out) :: latHeatGround ! latent heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) ! output: total heat fluxes to the atmosphere - real(dp),intent(out) :: senHeatTotal ! total sensible heat flux to the atmosphere (W m-2) - real(dp),intent(out) :: latHeatTotal ! total latent heat flux to the atmosphere (W m-2) + real(rkind),intent(out) :: senHeatTotal ! total sensible heat flux to the atmosphere (W m-2) + real(rkind),intent(out) :: latHeatTotal ! total latent heat flux to the atmosphere (W m-2) ! output: net fluxes - real(dp),intent(out) :: turbFluxCanair ! net turbulent heat fluxes at the canopy air space (W m-2) - real(dp),intent(out) :: turbFluxCanopy ! net turbulent heat fluxes at the canopy (W m-2) - real(dp),intent(out) :: turbFluxGround ! net turbulent heat fluxes at the ground surface (W m-2) + real(rkind),intent(out) :: turbFluxCanair ! net turbulent heat fluxes at the canopy air space (W m-2) + real(rkind),intent(out) :: turbFluxCanopy ! net turbulent heat fluxes at the canopy (W m-2) + real(rkind),intent(out) :: turbFluxGround ! net turbulent heat fluxes at the ground surface (W m-2) ! output: energy flux derivatives - real(dp),intent(out) :: dTurbFluxCanair_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(dp),intent(out) :: dTurbFluxCanair_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) - real(dp),intent(out) :: dTurbFluxCanair_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) - real(dp),intent(out) :: dTurbFluxCanopy_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(dp),intent(out) :: dTurbFluxCanopy_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - real(dp),intent(out) :: dTurbFluxCanopy_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - real(dp),intent(out) :: dTurbFluxGround_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(dp),intent(out) :: dTurbFluxGround_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - real(dp),intent(out) :: dTurbFluxGround_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + real(rkind),intent(out) :: dTurbFluxCanair_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(rkind),intent(out) :: dTurbFluxCanair_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) + real(rkind),intent(out) :: dTurbFluxCanair_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) + real(rkind),intent(out) :: dTurbFluxCanopy_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(rkind),intent(out) :: dTurbFluxCanopy_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + real(rkind),intent(out) :: dTurbFluxCanopy_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + real(rkind),intent(out) :: dTurbFluxGround_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(rkind),intent(out) :: dTurbFluxGround_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + real(rkind),intent(out) :: dTurbFluxGround_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) ! output: liquid flux derivatives (canopy evap) - real(dp),intent(out) :: dLatHeatCanopyEvap_dCanLiq ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (W kg-1) - real(dp),intent(out) :: dLatHeatCanopyEvap_dTCanair ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) - real(dp),intent(out) :: dLatHeatCanopyEvap_dTCanopy ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) - real(dp),intent(out) :: dLatHeatCanopyEvap_dTGround ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) + real(rkind),intent(out) :: dLatHeatCanopyEvap_dCanLiq ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (W kg-1) + real(rkind),intent(out) :: dLatHeatCanopyEvap_dTCanair ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) + real(rkind),intent(out) :: dLatHeatCanopyEvap_dTCanopy ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) + real(rkind),intent(out) :: dLatHeatCanopyEvap_dTGround ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) ! output: liquid flux derivatives (ground evap) - real(dp),intent(out) :: dLatHeatGroundEvap_dCanLiq ! derivative in latent heat of ground evaporation w.r.t. canopy liquid water content (J kg-1 s-1) - real(dp),intent(out) :: dLatHeatGroundEvap_dTCanair ! derivative in latent heat of ground evaporation w.r.t. canopy air temperature (W m-2 K-1) - real(dp),intent(out) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) - real(dp),intent(out) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) + real(rkind),intent(out) :: dLatHeatGroundEvap_dCanLiq ! derivative in latent heat of ground evaporation w.r.t. canopy liquid water content (J kg-1 s-1) + real(rkind),intent(out) :: dLatHeatGroundEvap_dTCanair ! derivative in latent heat of ground evaporation w.r.t. canopy air temperature (W m-2 K-1) + real(rkind),intent(out) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) + real(rkind),intent(out) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) ! output: cross derivatives - real(dp),intent(out) :: dTurbFluxCanair_dCanLiq ! derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - real(dp),intent(out) :: dTurbFluxCanopy_dCanLiq ! derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - real(dp),intent(out) :: dTurbFluxGround_dCanLiq ! derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + real(rkind),intent(out) :: dTurbFluxCanair_dCanLiq ! derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + real(rkind),intent(out) :: dTurbFluxCanopy_dCanLiq ! derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + real(rkind),intent(out) :: dTurbFluxGround_dCanLiq ! derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! ----------------------------------------------------------------------------------------------------------------------------------------- ! local variables -- general - real(dp) :: fpart1,fpart2 ! different parts of a function - real(dp) :: dPart0,dpart1,dpart2 ! derivatives for different parts of a function + real(rkind) :: fpart1,fpart2 ! different parts of a function + real(rkind) :: dPart0,dpart1,dpart2 ! derivatives for different parts of a function ! local variables -- "constants" - real(dp) :: volHeatCapacityAir ! volumetric heat capacity of air (J m-3) - real(dp) :: latentHeatConstant ! latent heat constant (kg m-3 K-1) + real(rkind) :: volHeatCapacityAir ! volumetric heat capacity of air (J m-3) + real(rkind) :: latentHeatConstant ! latent heat constant (kg m-3 K-1) ! local variables -- derivatives for energy conductances - real(dp) :: dEvapCond_dCanopyTemp ! derivative in evap conductance w.r.t. canopy temperature - real(dp) :: dTransCond_dCanopyTemp ! derivative in trans conductance w.r.t. canopy temperature - real(dp) :: dCanopyCond_dCanairTemp ! derivative in canopy conductance w.r.t. canopy air temperature - real(dp) :: dCanopyCond_dCanopyTemp ! derivative in canopy conductance w.r.t. canopy temperature - real(dp) :: dGroundCondSH_dCanairTemp ! derivative in ground conductance of sensible heat w.r.t. canopy air temperature - real(dp) :: dGroundCondSH_dCanopyTemp ! derivative in ground conductance of sensible heat w.r.t. canopy temperature - real(dp) :: dGroundCondSH_dGroundTemp ! derivative in ground conductance of sensible heat w.r.t. ground temperature + real(rkind) :: dEvapCond_dCanopyTemp ! derivative in evap conductance w.r.t. canopy temperature + real(rkind) :: dTransCond_dCanopyTemp ! derivative in trans conductance w.r.t. canopy temperature + real(rkind) :: dCanopyCond_dCanairTemp ! derivative in canopy conductance w.r.t. canopy air temperature + real(rkind) :: dCanopyCond_dCanopyTemp ! derivative in canopy conductance w.r.t. canopy temperature + real(rkind) :: dGroundCondSH_dCanairTemp ! derivative in ground conductance of sensible heat w.r.t. canopy air temperature + real(rkind) :: dGroundCondSH_dCanopyTemp ! derivative in ground conductance of sensible heat w.r.t. canopy temperature + real(rkind) :: dGroundCondSH_dGroundTemp ! derivative in ground conductance of sensible heat w.r.t. ground temperature ! local variables -- derivatives for mass conductances - real(dp) :: dGroundCondLH_dCanairTemp ! derivative in ground conductance w.r.t. canopy air temperature - real(dp) :: dGroundCondLH_dCanopyTemp ! derivative in ground conductance w.r.t. canopy temperature - real(dp) :: dGroundCondLH_dGroundTemp ! derivative in ground conductance w.r.t. ground temperature + real(rkind) :: dGroundCondLH_dCanairTemp ! derivative in ground conductance w.r.t. canopy air temperature + real(rkind) :: dGroundCondLH_dCanopyTemp ! derivative in ground conductance w.r.t. canopy temperature + real(rkind) :: dGroundCondLH_dGroundTemp ! derivative in ground conductance w.r.t. ground temperature ! local variables -- derivatives for the canopy air space variables - real(dp) :: fPart_VP ! part of the function for vapor pressure of the canopy air space - real(dp) :: leafConductanceTr ! leaf conductance for transpiration (m s-1) - real(dp) :: dVPCanopyAir_dTCanair ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the canopy air space - real(dp) :: dVPCanopyAir_dTCanopy ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the canopy - real(dp) :: dVPCanopyAir_dTGround ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the ground - real(dp) :: dVPCanopyAir_dWetFrac ! derivative of vapor pressure in the canopy air space w.r.t. wetted fraction of the canopy - real(dp) :: dVPCanopyAir_dCanLiq ! derivative of vapor pressure in the canopy air space w.r.t. canopy liquid water content + real(rkind) :: fPart_VP ! part of the function for vapor pressure of the canopy air space + real(rkind) :: leafConductanceTr ! leaf conductance for transpiration (m s-1) + real(rkind) :: dVPCanopyAir_dTCanair ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the canopy air space + real(rkind) :: dVPCanopyAir_dTCanopy ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the canopy + real(rkind) :: dVPCanopyAir_dTGround ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the ground + real(rkind) :: dVPCanopyAir_dWetFrac ! derivative of vapor pressure in the canopy air space w.r.t. wetted fraction of the canopy + real(rkind) :: dVPCanopyAir_dCanLiq ! derivative of vapor pressure in the canopy air space w.r.t. canopy liquid water content ! local variables -- sensible heat flux derivatives - real(dp) :: dSenHeatTotal_dTCanair ! derivative in the total sensible heat flux w.r.t. canopy air temperature - real(dp) :: dSenHeatTotal_dTCanopy ! derivative in the total sensible heat flux w.r.t. canopy air temperature - real(dp) :: dSenHeatTotal_dTGround ! derivative in the total sensible heat flux w.r.t. ground temperature - real(dp) :: dSenHeatCanopy_dTCanair ! derivative in the canopy sensible heat flux w.r.t. canopy air temperature - real(dp) :: dSenHeatCanopy_dTCanopy ! derivative in the canopy sensible heat flux w.r.t. canopy temperature - real(dp) :: dSenHeatCanopy_dTGround ! derivative in the canopy sensible heat flux w.r.t. ground temperature - real(dp) :: dSenHeatGround_dTCanair ! derivative in the ground sensible heat flux w.r.t. canopy air temperature - real(dp) :: dSenHeatGround_dTCanopy ! derivative in the ground sensible heat flux w.r.t. canopy temperature - real(dp) :: dSenHeatGround_dTGround ! derivative in the ground sensible heat flux w.r.t. ground temperature + real(rkind) :: dSenHeatTotal_dTCanair ! derivative in the total sensible heat flux w.r.t. canopy air temperature + real(rkind) :: dSenHeatTotal_dTCanopy ! derivative in the total sensible heat flux w.r.t. canopy air temperature + real(rkind) :: dSenHeatTotal_dTGround ! derivative in the total sensible heat flux w.r.t. ground temperature + real(rkind) :: dSenHeatCanopy_dTCanair ! derivative in the canopy sensible heat flux w.r.t. canopy air temperature + real(rkind) :: dSenHeatCanopy_dTCanopy ! derivative in the canopy sensible heat flux w.r.t. canopy temperature + real(rkind) :: dSenHeatCanopy_dTGround ! derivative in the canopy sensible heat flux w.r.t. ground temperature + real(rkind) :: dSenHeatGround_dTCanair ! derivative in the ground sensible heat flux w.r.t. canopy air temperature + real(rkind) :: dSenHeatGround_dTCanopy ! derivative in the ground sensible heat flux w.r.t. canopy temperature + real(rkind) :: dSenHeatGround_dTGround ! derivative in the ground sensible heat flux w.r.t. ground temperature ! local variables -- latent heat flux derivatives - real(dp) :: dLatHeatCanopyTrans_dTCanair ! derivative in the canopy transpiration flux w.r.t. canopy air temperature - real(dp) :: dLatHeatCanopyTrans_dTCanopy ! derivative in the canopy transpiration flux w.r.t. canopy temperature - real(dp) :: dLatHeatCanopyTrans_dTGround ! derivative in the canopy transpiration flux w.r.t. ground temperature + real(rkind) :: dLatHeatCanopyTrans_dTCanair ! derivative in the canopy transpiration flux w.r.t. canopy air temperature + real(rkind) :: dLatHeatCanopyTrans_dTCanopy ! derivative in the canopy transpiration flux w.r.t. canopy temperature + real(rkind) :: dLatHeatCanopyTrans_dTGround ! derivative in the canopy transpiration flux w.r.t. ground temperature ! local variables -- wetted fraction derivatives - real(dp) :: dLatHeatCanopyEvap_dWetFrac ! derivative in the latent heat of canopy evaporation w.r.t. canopy wet fraction (W m-2) - real(dp) :: dLatHeatCanopyTrans_dWetFrac ! derivative in the latent heat of canopy transpiration w.r.t. canopy wet fraction (W m-2) - real(dp) :: dLatHeatCanopyTrans_dCanLiq ! derivative in the latent heat of canopy transpiration w.r.t. canopy liquid water (J kg-1 s-1) + real(rkind) :: dLatHeatCanopyEvap_dWetFrac ! derivative in the latent heat of canopy evaporation w.r.t. canopy wet fraction (W m-2) + real(rkind) :: dLatHeatCanopyTrans_dWetFrac ! derivative in the latent heat of canopy transpiration w.r.t. canopy wet fraction (W m-2) + real(rkind) :: dLatHeatCanopyTrans_dCanLiq ! derivative in the latent heat of canopy transpiration w.r.t. canopy liquid water (J kg-1 s-1) ! ----------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='turbFluxes/' @@ -3037,27 +3037,27 @@ subroutine aStability(& logical(lgt),intent(in) :: computeDerivative ! flag to compute the derivative integer(i4b),intent(in) :: ixStability ! choice of stability function ! input: forcing data, diagnostic and state variables - real(dp),intent(in) :: mHeight ! measurement height (m) - real(dp),intent(in) :: airtemp ! air temperature (K) - real(dp),intent(in) :: sfcTemp ! surface temperature (K) - real(dp),intent(in) :: windspd ! wind speed (m s-1) + real(rkind),intent(in) :: mHeight ! measurement height (m) + real(rkind),intent(in) :: airtemp ! air temperature (K) + real(rkind),intent(in) :: sfcTemp ! surface temperature (K) + real(rkind),intent(in) :: windspd ! wind speed (m s-1) ! input: stability parameters - real(dp),intent(in) :: critRichNumber ! critical value for the bulk Richardson number where turbulence ceases (-) - real(dp),intent(in) :: Louis79_bparam ! parameter in Louis (1979) stability function - real(dp),intent(in) :: Mahrt87_eScale ! exponential scaling factor in the Mahrt (1987) stability function + real(rkind),intent(in) :: critRichNumber ! critical value for the bulk Richardson number where turbulence ceases (-) + real(rkind),intent(in) :: Louis79_bparam ! parameter in Louis (1979) stability function + real(rkind),intent(in) :: Mahrt87_eScale ! exponential scaling factor in the Mahrt (1987) stability function ! output - real(dp),intent(out) :: RiBulk ! bulk Richardson number (-) - real(dp),intent(out) :: stabilityCorrection ! stability correction for turbulent heat fluxes (-) - real(dp),intent(out) :: dStabilityCorrection_dRich ! derivative in stability correction w.r.t. Richardson number (-) - real(dp),intent(out) :: dStabilityCorrection_dAirTemp ! derivative in stability correction w.r.t. air temperature (K-1) - real(dp),intent(out) :: dStabilityCorrection_dSfcTemp ! derivative in stability correction w.r.t. surface temperature (K-1) + real(rkind),intent(out) :: RiBulk ! bulk Richardson number (-) + real(rkind),intent(out) :: stabilityCorrection ! stability correction for turbulent heat fluxes (-) + real(rkind),intent(out) :: dStabilityCorrection_dRich ! derivative in stability correction w.r.t. Richardson number (-) + real(rkind),intent(out) :: dStabilityCorrection_dAirTemp ! derivative in stability correction w.r.t. air temperature (K-1) + real(rkind),intent(out) :: dStabilityCorrection_dSfcTemp ! derivative in stability correction w.r.t. surface temperature (K-1) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local - real(dp), parameter :: verySmall=1.e-10_rkind ! a very small number (avoid stability of zero) - real(dp) :: dRiBulk_dAirTemp ! derivative in the bulk Richardson number w.r.t. air temperature (K-1) - real(dp) :: dRiBulk_dSfcTemp ! derivative in the bulk Richardson number w.r.t. surface temperature (K-1) - real(dp) :: bPrime ! scaled "b" parameter for stability calculations in Louis (1979) + real(rkind), parameter :: verySmall=1.e-10_rkind ! a very small number (avoid stability of zero) + real(rkind) :: dRiBulk_dAirTemp ! derivative in the bulk Richardson number w.r.t. air temperature (K-1) + real(rkind) :: dRiBulk_dSfcTemp ! derivative in the bulk Richardson number w.r.t. surface temperature (K-1) + real(rkind) :: bPrime ! scaled "b" parameter for stability calculations in Louis (1979) ! ----------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='aStability/' @@ -3165,21 +3165,21 @@ subroutine bulkRichardson(& err,message) ! output: error control implicit none ! input - real(dp),intent(in) :: airtemp ! air temperature (K) - real(dp),intent(in) :: sfcTemp ! surface temperature (K) - real(dp),intent(in) :: windspd ! wind speed (m s-1) - real(dp),intent(in) :: mHeight ! measurement height (m) + real(rkind),intent(in) :: airtemp ! air temperature (K) + real(rkind),intent(in) :: sfcTemp ! surface temperature (K) + real(rkind),intent(in) :: windspd ! wind speed (m s-1) + real(rkind),intent(in) :: mHeight ! measurement height (m) logical(lgt),intent(in) :: computeDerivative ! flag to compute the derivative ! output - real(dp),intent(inout) :: RiBulk ! bulk Richardson number (-) - real(dp),intent(out) :: dRiBulk_dAirTemp ! derivative in the bulk Richardson number w.r.t. air temperature (K-1) - real(dp),intent(out) :: dRiBulk_dSfcTemp ! derivative in the bulk Richardson number w.r.t. surface temperature (K-1) + real(rkind),intent(inout) :: RiBulk ! bulk Richardson number (-) + real(rkind),intent(out) :: dRiBulk_dAirTemp ! derivative in the bulk Richardson number w.r.t. air temperature (K-1) + real(rkind),intent(out) :: dRiBulk_dSfcTemp ! derivative in the bulk Richardson number w.r.t. surface temperature (K-1) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local variables - real(dp) :: T_grad ! gradient in temperature between the atmosphere and surface (K) - real(dp) :: T_mean ! mean of the atmosphere and surface temperature (K) - real(dp) :: RiMult ! dimensionless scaling factor (-) + real(rkind) :: T_grad ! gradient in temperature between the atmosphere and surface (K) + real(rkind) :: T_mean ! mean of the atmosphere and surface temperature (K) + real(rkind) :: RiMult ! dimensionless scaling factor (-) ! initialize error control err=0; message='bulkRichardson/' ! compute local variables diff --git a/build/source/engine/vegPhenlgy.f90 b/build/source/engine/vegPhenlgy.f90 index 194463a78..7d3cd3dc2 100644 --- a/build/source/engine/vegPhenlgy.f90 +++ b/build/source/engine/vegPhenlgy.f90 @@ -31,8 +31,8 @@ module vegPhenlgy_module ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) - var_d, & ! data vector (dp) - var_dlength, & ! data vector with variable length dimension (dp) + var_d, & ! data vector (rkind) + var_dlength, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions ! named variables defining elements in the data structures diff --git a/build/source/engine/vegSWavRad.f90 b/build/source/engine/vegSWavRad.f90 index 45357f36b..c438af531 100644 --- a/build/source/engine/vegSWavRad.f90 +++ b/build/source/engine/vegSWavRad.f90 @@ -23,7 +23,7 @@ module vegSWavRad_module ! data types USE nrtype USE data_types,only:var_i ! x%var(:) (i4b) -USE data_types,only:var_dlength ! x%var(:)%dat (dp) +USE data_types,only:var_dlength ! x%var(:)%dat (rkind) ! physical constants USE multiconst,only:Tfreeze ! temperature at freezing (K) diff --git a/build/source/engine/volicePack.f90 b/build/source/engine/volicePack.f90 index d18383e1f..218dfbba0 100644 --- a/build/source/engine/volicePack.f90 +++ b/build/source/engine/volicePack.f90 @@ -25,9 +25,9 @@ module volicePack_module ! derived types to define the data structures USE data_types,only:& - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions ! named variables for snow and soil diff --git a/build/source/engine/volicePackFida.f90 b/build/source/engine/volicePackFida.f90 index e28f9c041..2859c3157 100644 --- a/build/source/engine/volicePackFida.f90 +++ b/build/source/engine/volicePackFida.f90 @@ -25,9 +25,9 @@ module volicePackFida_module ! derived types to define the data structures USE data_types,only:& - var_d, & ! data vector (dp) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) + var_dlength, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions ! named variables for snow and soil From f5342ab167dfede48474b75f6a2e898b1dabeda5 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 29 Jun 2021 23:18:14 -0600 Subject: [PATCH 0152/1472] varSubstep is ok --- build/source/engine/opSplittin.f90 | 4 ++-- build/source/engine/varSubstepFida.f90 | 17 +++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 173a63bdd..68a8614d4 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -296,9 +296,9 @@ subroutine opSplittin(& integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) integer(i4b),parameter :: IDA=1 integer(i4b),parameter :: BE=2 - integer(i4b) :: solver=IDA ! BE or IDA + integer(i4b) :: solver=IDA ! BE or IDA integer(i4b) :: nCoupling - real(qp) :: dt_out + real(qp) :: dt_out ! ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- diff --git a/build/source/engine/varSubstepFida.f90 b/build/source/engine/varSubstepFida.f90 index 33c67466f..f77f411cd 100644 --- a/build/source/engine/varSubstepFida.f90 +++ b/build/source/engine/varSubstepFida.f90 @@ -341,7 +341,7 @@ subroutine varSubstepFida(& stateVecPrime, & ! intent(out): updated state vector reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt - dt_out, & ! intent(out) + dt_out, & ! intent(out): time step (s) err,cmessage) ! intent(out): error code and error message if(err/=0)then @@ -550,15 +550,15 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy implicit none ! model control - real(rkind) ,intent(in) :: dt ! time step (s) + real(rkind) ,intent(in) :: dt ! time step (s) integer(i4b) ,intent(in) :: nSnow ! number of snow layers integer(i4b) ,intent(in) :: nSoil ! number of soil layers integer(i4b) ,intent(in) :: nLayers ! total number of layers logical(lgt) ,intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature logical(lgt) ,intent(in) :: computeVegFlux ! flag to compute the vegetation flux - real(rkind) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) - real(rkind) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) - real(rkind) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) + real(rkind) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) + real(rkind) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) + real(rkind) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance logical(lgt) ,intent(in) :: checkNrgBalance ! flag to check the energy balance ! data structures @@ -621,9 +621,8 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector for volumetric fraction of liquid water (-) real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector for volumetric fraction of ice (-) real(rkind) :: scalarCanairEnthalpyTrial ! enthalpy of the canopy air space (J m-3) - real(rkind) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3 - real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial - integer(i4b) :: iLayer + real(rkind) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! enthalpy of snow + soil (J m-3) ! ------------------------------------------------------------------------------------------------------------------- ! ------------------------------------------------------------------------------------------------------------------- @@ -1058,6 +1057,8 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt ! ----- ! * update enthalpy as a diagnostic variable... ! -------------------------------- + scalarCanairEnthalpy = scalarCanairEnthalpyTrial + scalarCanopyEnthalpy = scalarCanopyEnthalpyTrial mLayerEnthalpy = mLayerEnthalpyTrial ! ----- From 590f71ec36b9a13d14c06bc7d6cb7e754a517bf2 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 29 Jun 2021 23:47:16 -0600 Subject: [PATCH 0153/1472] renamed sysSolvFida to systemSolvSundials --- build/Makefile | 2 +- build/source/driver/Makefile | 0 build/source/driver/summa_alarms.f90 | 0 build/source/driver/summa_defineOutput.f90 | 0 build/source/driver/summa_driver.f90 | 0 build/source/driver/summa_forcing.f90 | 0 build/source/driver/summa_globalData.f90 | 0 build/source/driver/summa_init.f90 | 0 build/source/driver/summa_modelRun.f90 | 0 build/source/driver/summa_restart.f90 | 0 build/source/driver/summa_setup.f90 | 0 build/source/driver/summa_type.f90 | 0 build/source/driver/summa_util.f90 | 0 build/source/driver/summa_writeOutput.f90 | 0 build/source/dshare/data_types.f90 | 0 build/source/dshare/flxMapping.f90 | 0 build/source/dshare/get_ixname.f90 | 0 build/source/dshare/globalData.f90 | 0 build/source/dshare/multiconst.f90 | 0 build/source/dshare/outpt_stat.f90 | 0 build/source/dshare/popMetadat.f90 | 0 build/source/dshare/var_lookup.f90 | 0 .../{sysSolvFida.f90 => systemSolvSundials.f90} | 14 +++++++------- build/source/engine/varSubstepFida.f90 | 4 ++-- 24 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 build/source/driver/Makefile mode change 100755 => 100644 build/source/driver/summa_alarms.f90 mode change 100755 => 100644 build/source/driver/summa_defineOutput.f90 mode change 100755 => 100644 build/source/driver/summa_driver.f90 mode change 100755 => 100644 build/source/driver/summa_forcing.f90 mode change 100755 => 100644 build/source/driver/summa_globalData.f90 mode change 100755 => 100644 build/source/driver/summa_init.f90 mode change 100755 => 100644 build/source/driver/summa_modelRun.f90 mode change 100755 => 100644 build/source/driver/summa_restart.f90 mode change 100755 => 100644 build/source/driver/summa_setup.f90 mode change 100755 => 100644 build/source/driver/summa_type.f90 mode change 100755 => 100644 build/source/driver/summa_util.f90 mode change 100755 => 100644 build/source/driver/summa_writeOutput.f90 mode change 100755 => 100644 build/source/dshare/data_types.f90 mode change 100755 => 100644 build/source/dshare/flxMapping.f90 mode change 100755 => 100644 build/source/dshare/get_ixname.f90 mode change 100755 => 100644 build/source/dshare/globalData.f90 mode change 100755 => 100644 build/source/dshare/multiconst.f90 mode change 100755 => 100644 build/source/dshare/outpt_stat.f90 mode change 100755 => 100644 build/source/dshare/popMetadat.f90 mode change 100755 => 100644 build/source/dshare/var_lookup.f90 rename build/source/engine/{sysSolvFida.f90 => systemSolvSundials.f90} (99%) diff --git a/build/Makefile b/build/Makefile index ac34e9e7b..742cbc606 100644 --- a/build/Makefile +++ b/build/Makefile @@ -238,7 +238,7 @@ SUMMA_SOLVER= \ findDiscontinuity.f90 \ computSnowDepth.f90 \ fidaSolver.f90 \ - sysSolvFida.f90 \ + systemSolvSundials.f90 \ varSubstep.f90 \ varSubstepFida.f90 \ opSplittin.f90 \ diff --git a/build/source/driver/Makefile b/build/source/driver/Makefile new file mode 100644 index 000000000..e69de29bb diff --git a/build/source/driver/summa_alarms.f90 b/build/source/driver/summa_alarms.f90 old mode 100755 new mode 100644 diff --git a/build/source/driver/summa_defineOutput.f90 b/build/source/driver/summa_defineOutput.f90 old mode 100755 new mode 100644 diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 old mode 100755 new mode 100644 diff --git a/build/source/driver/summa_forcing.f90 b/build/source/driver/summa_forcing.f90 old mode 100755 new mode 100644 diff --git a/build/source/driver/summa_globalData.f90 b/build/source/driver/summa_globalData.f90 old mode 100755 new mode 100644 diff --git a/build/source/driver/summa_init.f90 b/build/source/driver/summa_init.f90 old mode 100755 new mode 100644 diff --git a/build/source/driver/summa_modelRun.f90 b/build/source/driver/summa_modelRun.f90 old mode 100755 new mode 100644 diff --git a/build/source/driver/summa_restart.f90 b/build/source/driver/summa_restart.f90 old mode 100755 new mode 100644 diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 old mode 100755 new mode 100644 diff --git a/build/source/driver/summa_type.f90 b/build/source/driver/summa_type.f90 old mode 100755 new mode 100644 diff --git a/build/source/driver/summa_util.f90 b/build/source/driver/summa_util.f90 old mode 100755 new mode 100644 diff --git a/build/source/driver/summa_writeOutput.f90 b/build/source/driver/summa_writeOutput.f90 old mode 100755 new mode 100644 diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 old mode 100755 new mode 100644 diff --git a/build/source/dshare/flxMapping.f90 b/build/source/dshare/flxMapping.f90 old mode 100755 new mode 100644 diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 old mode 100755 new mode 100644 diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 old mode 100755 new mode 100644 diff --git a/build/source/dshare/multiconst.f90 b/build/source/dshare/multiconst.f90 old mode 100755 new mode 100644 diff --git a/build/source/dshare/outpt_stat.f90 b/build/source/dshare/outpt_stat.f90 old mode 100755 new mode 100644 diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 old mode 100755 new mode 100644 diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/sysSolvFida.f90 b/build/source/engine/systemSolvSundials.f90 similarity index 99% rename from build/source/engine/sysSolvFida.f90 rename to build/source/engine/systemSolvSundials.f90 index 4149ae4e5..e41a3dced 100644 --- a/build/source/engine/sysSolvFida.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -1,6 +1,6 @@ -module sysSolvFida_module +module systemSolvSundials_module ! data types USE nrtype @@ -80,7 +80,7 @@ module sysSolvFida_module ! safety: set private unless specified otherwise implicit none private -public::sysSolvFida +public::systemSolvSundials ! control parameters real(rkind),parameter :: valueMissing=-9999._rkind ! missing value @@ -92,9 +92,9 @@ module sysSolvFida_module ! ********************************************************************************************************** - ! public subroutine sysSolvFida: run the coupled energy-mass model for one timestep + ! public subroutine systemSolvSundials: run the coupled energy-mass model for one timestep ! ********************************************************************************************************** - subroutine sysSolvFida(& + subroutine systemSolvSundials(& ! input: model control dt, & ! intent(in): time step (s) nState, & ! intent(in): total number of state variables @@ -240,7 +240,7 @@ subroutine sysSolvFida(& ) ! --------------------------------------------------------------------------------------- ! initialize error control - err=0; message="sysSolvFida/" + err=0; message="systemSolvSundials/" ! ***** ! (0) PRELIMINARIES... @@ -528,6 +528,6 @@ subroutine sysSolvFida(& ! end associate statements end associate globalVars - end subroutine sysSolvFida + end subroutine systemSolvSundials -end module sysSolvFida_module +end module systemSolvSundials_module diff --git a/build/source/engine/varSubstepFida.f90 b/build/source/engine/varSubstepFida.f90 index f77f411cd..73caea36b 100644 --- a/build/source/engine/varSubstepFida.f90 +++ b/build/source/engine/varSubstepFida.f90 @@ -134,7 +134,7 @@ subroutine varSubstepFida(& USE varExtrFida_module, only:varExtractFida ! identify name of variable type (for error message) USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages - USE sysSolvFida_module,only:sysSolvFida + USE systemSolvSundials_module,only:systemSolvSundials implicit none ! --------------------------------------------------------------------------------------- ! * dummy variables @@ -312,7 +312,7 @@ subroutine varSubstepFida(& ! * iterative solution... ! ----------------------- ! solve the system of equations for a given state subset - call sysSolvFida(& + call systemSolvSundials(& ! input: model control dtSubstep, & ! intent(in): time step (s) nState, & ! intent(in): total number of state variables From 79e61ec8914e1e1f0790ad0a02b823a05754eac4 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 29 Jun 2021 23:50:38 -0600 Subject: [PATCH 0154/1472] renamed fidaSolver to solveByIDA --- build/*.f90 | 0 build/Makefile | 2 +- .../engine/{fidaSolver.f90 => solveByIDA.f90} | 48 +++++++++---------- build/source/engine/systemSolvSundials.f90 | 4 +- 4 files changed, 27 insertions(+), 27 deletions(-) create mode 100644 build/*.f90 rename build/source/engine/{fidaSolver.f90 => solveByIDA.f90} (96%) diff --git a/build/*.f90 b/build/*.f90 new file mode 100644 index 000000000..e69de29bb diff --git a/build/Makefile b/build/Makefile index 742cbc606..dd549f8f9 100644 --- a/build/Makefile +++ b/build/Makefile @@ -237,7 +237,7 @@ SUMMA_SOLVER= \ nonlinSolvFida.f90 \ findDiscontinuity.f90 \ computSnowDepth.f90 \ - fidaSolver.f90 \ + solveByIDA.f90 \ systemSolvSundials.f90 \ varSubstep.f90 \ varSubstepFida.f90 \ diff --git a/build/source/engine/fidaSolver.f90 b/build/source/engine/solveByIDA.f90 similarity index 96% rename from build/source/engine/fidaSolver.f90 rename to build/source/engine/solveByIDA.f90 index 49a0106dc..4171c8504 100644 --- a/build/source/engine/fidaSolver.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -1,7 +1,7 @@ -module fidaSolver_module +module solveByIDA_module !======= Inclusions =========== @@ -83,15 +83,15 @@ module fidaSolver_module implicit none private::setInitialCondition private::setSolverParams - public::fidaSolver + public::solveByIDA contains !------------------- - ! * subroutine fidaSolver: solve F(y,y') = 0 by FIDA (y is the state vector) + ! * subroutine solveByIDA: solve F(y,y') = 0 by FIDA (y is the state vector) ! ------------------ - subroutine fidaSolver( & + subroutine solveByIDA( & dt, & ! intent(in): data time step atol, & ! intent(in): absolute telerance rtol, & ! intent(in): relative tolerance @@ -253,7 +253,7 @@ subroutine fidaSolver( & !======= Internals ============ ! initialize error control - err=0; message="fidaSolver/" + err=0; message="solveByIDA/" nState = nStat idaSucceeds = .true. ! fill eqns_data which will be required later to call eval8summaFida @@ -344,10 +344,10 @@ subroutine fidaSolver( & ! create serial vectors sunvec_y => FN_VMake_Serial(nState, stateVec) - if (.not. associated(sunvec_y)) then; err=20; message='fidaSolver: sunvec = NULL'; return; endif + if (.not. associated(sunvec_y)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif sunvec_yp => FN_VMake_Serial(nState, stateVecPrime) - if (.not. associated(sunvec_yp)) then; err=20; message='fidaSolver: sunvec = NULL'; return; endif + if (.not. associated(sunvec_yp)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif ! Initialize solution vectors call setInitialCondition(nState, stateVecInit, sunvec_y, sunvec_yp) @@ -355,21 +355,21 @@ subroutine fidaSolver( & ! Call FIDACreate and FIDAInit to initialize IDA memory ida_mem = FIDACreate() - if (.not. c_associated(ida_mem)) then; err=20; message='fidaSolver: ida_mem = NULL'; return; endif + if (.not. c_associated(ida_mem)) then; err=20; message='solveByIDA: ida_mem = NULL'; return; endif eqns_data%ida_mem = ida_mem retval = FIDASetUserData(ida_mem, c_loc(eqns_data)) - if (retval /= 0) then; err=20; message='fidaSolver: error in FIDASetUserData'; return; endif + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetUserData'; return; endif t0 = 0._rkind retval = FIDAInit(ida_mem, c_funloc(evalEqnsFida), t0, sunvec_y, sunvec_yp) - if (retval /= 0) then; err=20; message='fidaSolver: error in FIDAInit'; return; endif + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAInit'; return; endif ! set tolerances retval = FIDAWFtolerances(ida_mem, c_funloc(computWeightFida)) - if (retval /= 0) then; err=20; message='fidaSolver: error in FIDAWFtolerances'; return; endif + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAWFtolerances'; return; endif ! define the form of the matrix select case(ixMatrix) @@ -377,51 +377,51 @@ subroutine fidaSolver( & mu = ku; lu = kl; ! Create banded SUNMatrix for use in linear solves sunmat_A => FSUNBandMatrix(nState, mu, lu) - if (.not. associated(sunmat_A)) then; err=20; message='fidaSolver: sunmat = NULL'; return; endif + if (.not. associated(sunmat_A)) then; err=20; message='solveByIDA: sunmat = NULL'; return; endif ! Create banded SUNLinearSolver object sunlinsol_LS => FSUNLinSol_Band(sunvec_y, sunmat_A) - if (.not. associated(sunlinsol_LS)) then; err=20; message='fidaSolver: sunlinsol = NULL'; return; endif + if (.not. associated(sunlinsol_LS)) then; err=20; message='solveByIDA: sunlinsol = NULL'; return; endif case(ixFullMatrix) ! Create dense SUNMatrix for use in linear solves sunmat_A => FSUNDenseMatrix(nState, nState) - if (.not. associated(sunmat_A)) then; err=20; message='fidaSolver: sunmat = NULL'; return; endif + if (.not. associated(sunmat_A)) then; err=20; message='solveByIDA: sunmat = NULL'; return; endif ! Create dense SUNLinearSolver object sunlinsol_LS => FSUNDenseLinearSolver(sunvec_y, sunmat_A) - if (.not. associated(sunlinsol_LS)) then; err=20; message='fidaSolver: sunlinsol = NULL'; return; endif + if (.not. associated(sunlinsol_LS)) then; err=20; message='solveByIDA: sunlinsol = NULL'; return; endif ! check - case default; err=20; message='fidaSolver: error in type of matrix'; return + case default; err=20; message='solveByIDA: error in type of matrix'; return end select ! form of matrix ! Attach the matrix and linear solver retval = FIDASetLinearSolver(ida_mem, sunlinsol_LS, sunmat_A); - if (retval /= 0) then; err=20; message='fidaSolver: error in FIDASetLinearSolver'; return; endif + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetLinearSolver'; return; endif if(ixMatrix == ixFullMatrix)then ! Set the user-supplied Jacobian routine retval = FIDASetJacFn(ida_mem, c_funloc(evalJacFida)) - if (retval /= 0) then; err=20; message='fidaSolver: error in FIDASetJacFn'; return; endif + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetJacFn'; return; endif endif ! Create Newton SUNNonlinearSolver object sunnonlin_NLS => FSUNNonlinSol_Newton(sunvec_y) - if (.not. associated(sunnonlin_NLS)) then; err=20; message='fidaSolver: sunnonlinsol = NULL'; return; endif + if (.not. associated(sunnonlin_NLS)) then; err=20; message='solveByIDA: sunnonlinsol = NULL'; return; endif ! Attach the nonlinear solver retval = FIDASetNonlinearSolver(ida_mem, sunnonlin_NLS) - if (retval /= 0) then; err=20; message='fidaSolver: error in FIDASetNonlinearSolver'; return; endif + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetNonlinearSolver'; return; endif ! Enforce the solver to stop at the end of the data time step retval = FIDASetStopTime(ida_mem, dt) - if (retval /= 0) then; err=20; message='fidaSolver: error in FIDASetStopTime'; return; endif + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetStopTime'; return; endif ! Set solver parameters such as maximum order, number of iterations, ... call setSolverParams(dt, ida_mem, retval) - if (retval /= 0) then; err=20; message='fidaSolver: error in setSolverParams'; return; endif + if (retval /= 0) then; err=20; message='solveByIDA: error in setSolverParams'; return; endif ! need the following values for the first substep eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) @@ -717,7 +717,7 @@ subroutine fidaSolver( & call FN_VDestroy(sunvec_yp) - end subroutine fidaSolver + end subroutine solveByIDA ! ---------------------------------------------------------------- @@ -881,7 +881,7 @@ subroutine implctMelt(& end subroutine implctMelt -end module fidaSolver_module +end module solveByIDA_module diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index e41a3dced..53423fac0 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -135,7 +135,7 @@ subroutine systemSolvSundials(& USE getVectorz_module,only:getScaling ! get the scaling vectors USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy USE tolFida_module,only:popTolFida - USE fidaSolver_module,only:fidaSolver + USE solveByIDA_module,only:solveByIDA ! use varExtrFida_module, only:countDiscontinuity use, intrinsic :: iso_c_binding implicit none @@ -431,7 +431,7 @@ subroutine systemSolvSundials(& ! initialize sum of compression of the soil matrix mLayerCmpress_sum(:) = 0._rkind - call fidaSolver(& + call solveByIDA(& dt, & ! intent (in) data time step atol, & ! intent (in) absolute telerance rtol, & ! intent (in) relative tolerance From 87a9f24d7b9a67c1d65bd20c2490e9c7a0393b3b Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 29 Jun 2021 23:54:35 -0600 Subject: [PATCH 0155/1472] renamed evalEqnsFida to evalDAE4IDA --- build/Makefile | 2 +- .../engine/{evalEqnsFida.f90 => evalDAE4IDA.f90} | 14 +++++++------- build/source/engine/solveByIDA.f90 | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) rename build/source/engine/{evalEqnsFida.f90 => evalDAE4IDA.f90} (96%) diff --git a/build/Makefile b/build/Makefile index dd549f8f9..c7e0e6220 100644 --- a/build/Makefile +++ b/build/Makefile @@ -229,7 +229,7 @@ SUMMA_SOLVER= \ printResidFida.f90 \ computResidFida.f90 \ eval8summaFida.f90 \ - evalEqnsFida.f90 \ + evalDAE4IDA.f90 \ computJacobFida.f90 \ computJacobFidaCpVar.f90 \ eval8FidaJac.f90 \ diff --git a/build/source/engine/evalEqnsFida.f90 b/build/source/engine/evalDAE4IDA.f90 similarity index 96% rename from build/source/engine/evalEqnsFida.f90 rename to build/source/engine/evalDAE4IDA.f90 index e4140c2cb..fbcdb53fb 100644 --- a/build/source/engine/evalEqnsFida.f90 +++ b/build/source/engine/evalDAE4IDA.f90 @@ -1,6 +1,6 @@ -module evalEqnsFida_module +module evalDAE4IDA_module !======= Inclusions =========== @@ -26,21 +26,21 @@ module evalEqnsFida_module ! privacy implicit none private - public::evalEqnsFida + public::evalDAE4IDA contains ! ********************************************************************************************************** - ! public function evalEqnsFida: compute the residual vector F(t,y,y') required for FIDA solver + ! public function evalDAE4IDA: compute the residual vector F(t,y,y') required for FIDA solver ! ********************************************************************************************************** ! Return values: ! 0 = success, ! 1 = recoverable error, ! -1 = non-recoverable error ! ---------------------------------------------------------------- - integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_data) & - result(ierr) bind(C,name='evalEqnsFida') + integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_data) & + result(ierr) bind(C,name='evalDAE4IDA') !======= Inclusions =========== use, intrinsic :: iso_c_binding @@ -168,7 +168,7 @@ integer(c_int) function evalEqnsFida(tres, sunvec_y, sunvec_yp, sunvec_r, user_d ierr = 0 return - end function evalEqnsFida + end function evalDAE4IDA -end module evalEqnsFida_module +end module evalDAE4IDA_module diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 4171c8504..4869ff485 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -148,7 +148,7 @@ subroutine solveByIDA( & USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver USE allocspace_module,only:allocLocal ! allocate local data structures - USE evalEqnsFida_module,only:evalEqnsFida ! DAE/ODE functions + USE evalDAE4IDA_module,only:evalDAE4IDA ! DAE/ODE functions USE evalJacFida_module,only:evalJacFida ! system Jacobian USE tolFida_module,only:computWeightFida USE eval8summaFida_module,only:eval8summaFida @@ -364,7 +364,7 @@ subroutine solveByIDA( & if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetUserData'; return; endif t0 = 0._rkind - retval = FIDAInit(ida_mem, c_funloc(evalEqnsFida), t0, sunvec_y, sunvec_yp) + retval = FIDAInit(ida_mem, c_funloc(evalDAE4IDA), t0, sunvec_y, sunvec_yp) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAInit'; return; endif ! set tolerances From 9061d2a6d35e39c1102d24ceb9363743ae08e853 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 29 Jun 2021 23:57:34 -0600 Subject: [PATCH 0156/1472] renamed eval8summaFida eval8DAE --- build/Makefile | 2 +- .../engine/{eval8summaFida.f90 => eval8DAE.f90} | 14 +++++++------- build/source/engine/eval8FidaJac.f90 | 4 ++-- build/source/engine/evalDAE4IDA.f90 | 4 ++-- build/source/engine/evalJacFida.f90 | 2 +- build/source/engine/solveByIDA.f90 | 6 +++--- build/source/engine/systemSolvSundials.f90 | 2 +- 7 files changed, 17 insertions(+), 17 deletions(-) rename build/source/engine/{eval8summaFida.f90 => eval8DAE.f90} (99%) diff --git a/build/Makefile b/build/Makefile index c7e0e6220..71c9634fb 100644 --- a/build/Makefile +++ b/build/Makefile @@ -228,7 +228,7 @@ SUMMA_SOLVER= \ computThermConduct.f90 \ printResidFida.f90 \ computResidFida.f90 \ - eval8summaFida.f90 \ + eval8DAE.f90 \ evalDAE4IDA.f90 \ computJacobFida.f90 \ computJacobFidaCpVar.f90 \ diff --git a/build/source/engine/eval8summaFida.f90 b/build/source/engine/eval8DAE.f90 similarity index 99% rename from build/source/engine/eval8summaFida.f90 rename to build/source/engine/eval8DAE.f90 index 82091e888..555a298f9 100644 --- a/build/source/engine/eval8summaFida.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -1,5 +1,5 @@ -module eval8summaFida_module +module eval8DAE_module ! data types USE nrtype @@ -80,14 +80,14 @@ module eval8summaFida_module implicit none private -public::eval8summaFida +public::eval8DAE contains ! ********************************************************************************************************** - ! public subroutine eval8summaFida: compute the residual vector and the Jacobian matrix + ! public subroutine eval8DAE: compute the residual vector and the Jacobian matrix ! ********************************************************************************************************** - subroutine eval8summaFida(& + subroutine eval8DAE(& ! input: model control dt_cur, & dt, & ! intent(in): time step @@ -314,7 +314,7 @@ subroutine eval8summaFida(& ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="eval8summaFida/" + err=0; message="eval8DAE/" ! check the feasibility of the solution feasible=.true. @@ -745,5 +745,5 @@ subroutine eval8summaFida(& end associate - end subroutine eval8summaFida -end module eval8summaFida_module + end subroutine eval8DAE +end module eval8DAE_module diff --git a/build/source/engine/eval8FidaJac.f90 b/build/source/engine/eval8FidaJac.f90 index 0ac640fb4..3c001ad17 100644 --- a/build/source/engine/eval8FidaJac.f90 +++ b/build/source/engine/eval8FidaJac.f90 @@ -315,8 +315,8 @@ subroutine eval8FidaJac(& ! compute the analytical Jacobian matrix ! NOTE: The derivatives were computed in the previous call to computFlux - ! This occurred either at the call to eval8summaFida at the start of sysSolveFida - ! or in the call to eval8summaFida in the previous iteration + ! This occurred either at the call to eval8DAE at the start of sysSolveFida + ! or in the call to eval8DAE in the previous iteration dt1 = 1._qp call computJacobFida(& ! input: model control diff --git a/build/source/engine/evalDAE4IDA.f90 b/build/source/engine/evalDAE4IDA.f90 index fbcdb53fb..742246180 100644 --- a/build/source/engine/evalDAE4IDA.f90 +++ b/build/source/engine/evalDAE4IDA.f90 @@ -49,7 +49,7 @@ integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_da use fnvector_serial_mod use nrtype use fida_datatypes - use eval8summaFida_module,only:eval8summaFida + use eval8DAE_module,only:eval8DAE !======= Declarations ========= implicit none @@ -96,7 +96,7 @@ integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_da ! compute the flux and the residual vector for a given state vector - call eval8summaFida(& + call eval8DAE(& ! input: model control stepsize_next(1), & eqns_data%dt, & diff --git a/build/source/engine/evalJacFida.f90 b/build/source/engine/evalJacFida.f90 index 98af4a56e..21f6db159 100644 --- a/build/source/engine/evalJacFida.f90 +++ b/build/source/engine/evalJacFida.f90 @@ -86,7 +86,7 @@ integer(c_int) function evalJacFida(t, cj, sunvec_y, sunvec_yp, sunvec_r, & ! compute the flux and the residual vector for a given state vector - ! NOTE 1: The derivatives computed in eval8summaFida are used to calculate the Jacobian matrix for the first iteration + ! NOTE 1: The derivatives computed in eval8DAE are used to calculate the Jacobian matrix for the first iteration ! NOTE 2: The Jacobian matrix together with the residual vector is used to calculate the first iteration increment call eval8FidaJac(& ! input: model control diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 4869ff485..7ddc5bee9 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -151,7 +151,7 @@ subroutine solveByIDA( & USE evalDAE4IDA_module,only:evalDAE4IDA ! DAE/ODE functions USE evalJacFida_module,only:evalJacFida ! system Jacobian USE tolFida_module,only:computWeightFida - USE eval8summaFida_module,only:eval8summaFida + USE eval8DAE_module,only:eval8DAE USE computEnthalpy_module,only:computEnthalpy USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy USE computSnowDepth_module,only:computSnowDepth @@ -256,7 +256,7 @@ subroutine solveByIDA( & err=0; message="solveByIDA/" nState = nStat idaSucceeds = .true. - ! fill eqns_data which will be required later to call eval8summaFida + ! fill eqns_data which will be required later to call eval8DAE eqns_data%dt = dt eqns_data%nSnow = nSnow eqns_data%nSoil = nSoil @@ -461,7 +461,7 @@ subroutine solveByIDA( & retval = FIDAGetLastStep(ida_mem, dt_last) ! compute the flux and the residual vector for a given state vector - call eval8summaFida(& + call eval8DAE(& ! input: model control dt_last(1), & ! intent(in): current stepsize eqns_data%dt, & ! intent(in): data step diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 53423fac0..a14b4d771 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -130,7 +130,7 @@ subroutine systemSolvSundials(& USE allocspace_module,only:allocLocal ! allocate local data structures ! simulation of fluxes and residuals given a trial state vector USE eval8summa_module,only:eval8summa ! simulation of fluxes and residuals given a trial state vector - USE eval8summaFida_module,only:eval8summaFida + USE eval8DAE_module,only:eval8DAE USE summaSolve_module,only:summaSolve ! calculate the iteration increment, evaluate the new state, and refine if necessary USE getVectorz_module,only:getScaling ! get the scaling vectors USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy From 90c36580173daf22275cf442330bba3f192d6fe6 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 30 Jun 2021 00:02:01 -0600 Subject: [PATCH 0157/1472] renamed computResidFida computResidDAE --- build/Makefile | 2 +- .../{computResidFida.f90 => computResidDAE.f90} | 14 +++++++------- build/source/engine/eval8DAE.f90 | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) rename build/source/engine/{computResidFida.f90 => computResidDAE.f90} (98%) diff --git a/build/Makefile b/build/Makefile index 71c9634fb..2d1e8a37a 100644 --- a/build/Makefile +++ b/build/Makefile @@ -227,7 +227,7 @@ SUMMA_SOLVER= \ computHeatCap.f90 \ computThermConduct.f90 \ printResidFida.f90 \ - computResidFida.f90 \ + computResidDAE.f90 \ eval8DAE.f90 \ evalDAE4IDA.f90 \ computJacobFida.f90 \ diff --git a/build/source/engine/computResidFida.f90 b/build/source/engine/computResidDAE.f90 similarity index 98% rename from build/source/engine/computResidFida.f90 rename to build/source/engine/computResidDAE.f90 index 7fd02d8f7..da0a4acd7 100644 --- a/build/source/engine/computResidFida.f90 +++ b/build/source/engine/computResidDAE.f90 @@ -1,6 +1,6 @@ -module computResidFida_module +module computResidDAE_module ! data types USE nrtype @@ -50,13 +50,13 @@ module computResidFida_module ! privacy implicit none private -public::computResidFida +public::computResidDAE contains ! ********************************************************************************************************** - ! public subroutine computResidFida: compute the residual vector + ! public subroutine computResidDAE: compute the residual vector ! ********************************************************************************************************** - subroutine computResidFida(& + subroutine computResidDAE(& ! input: model control nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -162,7 +162,7 @@ subroutine computResidFida(& ) ! association to necessary variables for the residual computations ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="computResidFida/" + err=0; message="computResidDAE/" ! --- @@ -252,6 +252,6 @@ subroutine computResidFida(& ! end association with the necessary variabiles for the residual calculations end associate - end subroutine computResidFida + end subroutine computResidDAE -end module computResidFida_module +end module computResidDAE_module diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index 555a298f9..49e71e5bb 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -162,7 +162,7 @@ subroutine eval8DAE(& USE computHeatCap_module,only:computHeatCapAnalytic ! compute heat capacity USE computHeatCap_module,only:computCm USE computHeatCap_module, only:computStatMult - USE computResidFida_module,only:computResidFida ! compute residuals given a state vector + USE computResidDAE_module,only:computResidDAE ! compute residuals given a state vector USE computThermConduct_module,only:computThermConduct USE computEnthalpy_module,only:computEnthalpy USE computEnthalpy_module,only:computEnthalpyPrime @@ -705,7 +705,7 @@ subroutine eval8DAE(& ! compute the residual vector - call computResidFida(& + call computResidDAE(& ! input: model control nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers From 71ee2d72858535348880e31b0ff5183d8710240c Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 30 Jun 2021 00:05:28 -0600 Subject: [PATCH 0158/1472] renamed evalJacFida to evalJac4IDA --- build/Makefile | 2 +- .../engine/{evalJacFida.f90 => evalJac4IDA.f90} | 14 +++++++------- build/source/engine/solveByIDA.f90 | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) rename build/source/engine/{evalJacFida.f90 => evalJac4IDA.f90} (95%) diff --git a/build/Makefile b/build/Makefile index 2d1e8a37a..27bbc7927 100644 --- a/build/Makefile +++ b/build/Makefile @@ -233,7 +233,7 @@ SUMMA_SOLVER= \ computJacobFida.f90 \ computJacobFidaCpVar.f90 \ eval8FidaJac.f90 \ - evalJacFida.f90 \ + evalJac4IDA.f90 \ nonlinSolvFida.f90 \ findDiscontinuity.f90 \ computSnowDepth.f90 \ diff --git a/build/source/engine/evalJacFida.f90 b/build/source/engine/evalJac4IDA.f90 similarity index 95% rename from build/source/engine/evalJacFida.f90 rename to build/source/engine/evalJac4IDA.f90 index 21f6db159..f99bdc9ae 100644 --- a/build/source/engine/evalJacFida.f90 +++ b/build/source/engine/evalJac4IDA.f90 @@ -1,6 +1,6 @@ -module evalJacFida_module +module evalJac4IDA_module !======= Inclusions =========== @@ -21,22 +21,22 @@ module evalJacFida_module ! privacy implicit none private - public::evalJacFida + public::evalJac4IDA contains ! ********************************************************************************************************** - ! public function evalJacFida: compute the residual vector F(t,y,y') required for FIDA solver + ! public function evalJac4IDA: compute the residual vector F(t,y,y') required for FIDA solver ! ********************************************************************************************************** ! Return values: ! 0 = success, ! 1 = recoverable error, ! -1 = non-recoverable error ! ---------------------------------------------------------------- - integer(c_int) function evalJacFida(t, cj, sunvec_y, sunvec_yp, sunvec_r, & + integer(c_int) function evalJac4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & sunmat_J, user_data, sunvec_temp1, sunvec_temp2, sunvec_temp3) & - result(ierr) bind(C,name='evalJacFida') + result(ierr) bind(C,name='evalJac4IDA') !======= Inclusions =========== use, intrinsic :: iso_c_binding @@ -125,7 +125,7 @@ integer(c_int) function evalJacFida(t, cj, sunvec_y, sunvec_yp, sunvec_r, & - end function evalJacFida + end function evalJac4IDA -end module evalJacFida_module +end module evalJac4IDA_module diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 7ddc5bee9..6ccc573e0 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -149,7 +149,7 @@ subroutine solveByIDA( & USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver USE allocspace_module,only:allocLocal ! allocate local data structures USE evalDAE4IDA_module,only:evalDAE4IDA ! DAE/ODE functions - USE evalJacFida_module,only:evalJacFida ! system Jacobian + USE evalJac4IDA_module,only:evalJac4IDA ! system Jacobian USE tolFida_module,only:computWeightFida USE eval8DAE_module,only:eval8DAE USE computEnthalpy_module,only:computEnthalpy @@ -403,7 +403,7 @@ subroutine solveByIDA( & if(ixMatrix == ixFullMatrix)then ! Set the user-supplied Jacobian routine - retval = FIDASetJacFn(ida_mem, c_funloc(evalJacFida)) + retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetJacFn'; return; endif endif From e353d69f02272807e4d1c7e34126bc424e041949 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 30 Jun 2021 00:10:42 -0600 Subject: [PATCH 0159/1472] renamed eval8FidaJac to eval8JacDAE --- build/Makefile | 2 +- .../engine/{eval8FidaJac.f90 => eval8JacDAE.f90} | 14 +++++++------- build/source/engine/evalJac4IDA.f90 | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) rename build/source/engine/{eval8FidaJac.f90 => eval8JacDAE.f90} (99%) diff --git a/build/Makefile b/build/Makefile index 27bbc7927..a6920a3af 100644 --- a/build/Makefile +++ b/build/Makefile @@ -232,7 +232,7 @@ SUMMA_SOLVER= \ evalDAE4IDA.f90 \ computJacobFida.f90 \ computJacobFidaCpVar.f90 \ - eval8FidaJac.f90 \ + eval8JacDAE.f90 \ evalJac4IDA.f90 \ nonlinSolvFida.f90 \ findDiscontinuity.f90 \ diff --git a/build/source/engine/eval8FidaJac.f90 b/build/source/engine/eval8JacDAE.f90 similarity index 99% rename from build/source/engine/eval8FidaJac.f90 rename to build/source/engine/eval8JacDAE.f90 index 3c001ad17..13259a9ec 100644 --- a/build/source/engine/eval8FidaJac.f90 +++ b/build/source/engine/eval8JacDAE.f90 @@ -1,5 +1,5 @@ -module eval8FidaJac_module +module eval8JacDAE_module ! data types USE nrtype @@ -80,14 +80,14 @@ module eval8FidaJac_module implicit none private -public::eval8FidaJac +public::eval8JacDAE contains ! ********************************************************************************************************** - ! public subroutine eval8FidaJac: compute the residual vector and the Jacobian matrix + ! public subroutine eval8JacDAE: compute the residual vector and the Jacobian matrix ! ********************************************************************************************************** - subroutine eval8FidaJac(& + subroutine eval8JacDAE(& ! input: model control cj, & ! intent(in) dt, & ! intent(in): time step @@ -212,7 +212,7 @@ subroutine eval8FidaJac(& ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="eval8FidaJac/" + err=0; message="eval8JacDAE/" ! extract variables from the model state vector @@ -364,5 +364,5 @@ subroutine eval8FidaJac(& end associate - end subroutine eval8FidaJac -end module eval8FidaJac_module + end subroutine eval8JacDAE +end module eval8JacDAE_module diff --git a/build/source/engine/evalJac4IDA.f90 b/build/source/engine/evalJac4IDA.f90 index f99bdc9ae..bb14ac788 100644 --- a/build/source/engine/evalJac4IDA.f90 +++ b/build/source/engine/evalJac4IDA.f90 @@ -46,7 +46,7 @@ integer(c_int) function evalJac4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & use fsunmatrix_dense_mod use nrtype use fida_datatypes - use eval8FidaJac_module,only:eval8FidaJac + use eval8JacDAE_module,only:eval8JacDAE !======= Declarations ========= implicit none @@ -88,7 +88,7 @@ integer(c_int) function evalJac4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & ! compute the flux and the residual vector for a given state vector ! NOTE 1: The derivatives computed in eval8DAE are used to calculate the Jacobian matrix for the first iteration ! NOTE 2: The Jacobian matrix together with the residual vector is used to calculate the first iteration increment - call eval8FidaJac(& + call eval8JacDAE(& ! input: model control cj, & ! intent(in) eqns_data%dt, & ! intent(in) From 0895732deb628922f675510224357f2d32f6493e Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 30 Jun 2021 00:21:10 -0600 Subject: [PATCH 0160/1472] renamed computJacFida to computJacDAE --- build/Makefile | 3 +- .../{computJacobFida.f90 => computJacDAE.f90} | 14 +- build/source/engine/computJacobFidaCpVar.f90 | 639 ------------------ build/source/engine/eval8JacDAE.f90 | 5 +- 4 files changed, 10 insertions(+), 651 deletions(-) rename build/source/engine/{computJacobFida.f90 => computJacDAE.f90} (99%) delete mode 100644 build/source/engine/computJacobFidaCpVar.f90 diff --git a/build/Makefile b/build/Makefile index a6920a3af..fbcb684c3 100644 --- a/build/Makefile +++ b/build/Makefile @@ -230,8 +230,7 @@ SUMMA_SOLVER= \ computResidDAE.f90 \ eval8DAE.f90 \ evalDAE4IDA.f90 \ - computJacobFida.f90 \ - computJacobFidaCpVar.f90 \ + computJacDAE.f90 \ eval8JacDAE.f90 \ evalJac4IDA.f90 \ nonlinSolvFida.f90 \ diff --git a/build/source/engine/computJacobFida.f90 b/build/source/engine/computJacDAE.f90 similarity index 99% rename from build/source/engine/computJacobFida.f90 rename to build/source/engine/computJacDAE.f90 index 22102fc14..687ea25c9 100644 --- a/build/source/engine/computJacobFida.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -18,7 +18,7 @@ ! You should have received a copy of the GNU General Public License ! along with this program. If not, see . -module computJacobFida_module +module computJacDAE_module ! data types USE nrtype @@ -84,13 +84,13 @@ module computJacobFida_module integer(i4b),parameter :: ixBandOffset=kl+ku+1 ! offset in the band Jacobian matrix private -public::computJacobFida +public::computJacDAE contains ! ********************************************************************************************************** - ! public subroutine computJacobFida: compute the Jacobian matrix + ! public subroutine computJacDAE: compute the Jacobian matrix ! ********************************************************************************************************** - subroutine computJacobFida(& + subroutine computJacDAE(& ! input: model control cj, & ! intent(in) dt, & ! intent(in): length of the time step (seconds) @@ -274,7 +274,7 @@ subroutine computJacobFida(& ) ! making association with data in structures ! -------------------------------------------------------------- ! initialize error control - err=0; message='computJacobFida/' + err=0; message='computJacDAE/' ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* @@ -853,7 +853,7 @@ subroutine computJacobFida(& end associate - end subroutine computJacobFida + end subroutine computJacDAE ! ********************************************************************************************************** @@ -867,4 +867,4 @@ function ixOffDiag(jState,iState) ixOffDiag = ixBandOffset + jState - iState end function ixOffDiag -end module computJacobFida_module +end module computJacDAE_module diff --git a/build/source/engine/computJacobFidaCpVar.f90 b/build/source/engine/computJacobFidaCpVar.f90 deleted file mode 100644 index 452beaee1..000000000 --- a/build/source/engine/computJacobFidaCpVar.f90 +++ /dev/null @@ -1,639 +0,0 @@ -! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2015 NCAR/RAL -! -! This file is part of SUMMA -! -! For more information see: http://www.ral.ucar.edu/projects/summa -! -! This program is free software: you can redistribute it and/or modify -! it under the terms of the GNU General Public License as published by -! the Free Software Foundation, either version 3 of the License, or -! (at your option) any later version. -! -! This program is distributed in the hope that it will be useful, -! but WITHOUT ANY WARRANTY; without even the implied warranty of -! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -! GNU General Public License for more details. -! -! You should have received a copy of the GNU General Public License -! along with this program. If not, see . - -module computJacobFidaCpVar_module - -! data types -USE nrtype - -! derived types to define the data structures -USE data_types,only:& - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (rkind) - -! named variables for structure elements -USE var_lookup,only:iLookPROG ! named variables for structure elements -USE var_lookup,only:iLookDIAG ! named variables for structure elements -USE var_lookup,only:iLookINDEX ! named variables for structure elements -USE var_lookup,only:iLookDERIV ! named variables for structure elements - -! look-up values for the form of Richards' equation -USE mDecisions_module,only: & - moisture, & ! moisture-based form of Richards' equation - mixdform ! mixed form of Richards' equation - - -! access the global print flag -USE globalData,only:globalPrintFlag - -! access missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing real number - -! domain types -USE globalData,only:iname_veg ! named variables for vegetation -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil - -! named variables to describe the state variable type -USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space -USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy -USE globalData,only:iname_watCanopy ! named variable defining the mass of water on the vegetation canopy -USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers -USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers -USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers -USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers -USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers - -! access named variables to describe the form and structure of the matrices used in the numerical solver -USE globalData,only: ku ! number of super-diagonal bands -USE globalData,only: kl ! number of sub-diagonal bands -USE globalData,only: ixDiag ! index for the diagonal band -USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix -USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix -USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix -USE globalData,only: iJac1 ! first layer of the Jacobian to print -USE globalData,only: iJac2 ! last layer of the Jacobian to print - -! constants -USE multiconst,only:& - LH_fus, & ! latent heat of fusion (J kg-1) - iden_air, & ! intrinsic density of air (kg m-3) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water, & ! intrinsic density of water (kg m-3) - ! specific heat - Cp_air, & ! specific heat of air (J kg-1 K-1) - Cp_ice, & ! specific heat of ice (J kg-1 K-1) - Cp_soil, & ! specific heat of soil (J kg-1 K-1) - Cp_water ! specific heat of liquid water (J kg-1 K-1) - -implicit none -! define constants -real(rkind),parameter :: verySmall=tiny(1.0_rkind) ! a very small number -integer(i4b),parameter :: ixBandOffset=kl+ku+1 ! offset in the band Jacobian matrix - -private -public::computJacobFidaCpVar -contains - - ! ********************************************************************************************************** - ! public subroutine computJacobFidaCpVar: compute the Jacobian matrix - ! ********************************************************************************************************** - subroutine computJacobFidaCpVar(& - ! input: model control - cj, & ! intent(in) - dt, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - computeBaseflow, & ! intent(in): flag to indicate if we need to compute baseflow - ixMatrix, & ! intent(in): form of the Jacobian matrix - specificStorage, & ! intent(in) - theta_sat, & ! intent(in) - ixRichards, & ! intent(in): choice of option for Richards' equation - ! input: data structures - indx_data, & ! intent(in): index data - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! input: state variables - mLayerTemp, & ! intent(in) - mLayerTempPrime, & ! intent(in) - mLayerMatricHeadPrime, & ! intent(in) - mLayerMatricHeadLiqPrime, & ! intent(in) - mLayerVolFracWat, & ! intent(in) - mLayerVolFracWatPrime, & ! intent(in) - scalarCanopyTemp, & ! intent(in) - scalarCanopyTempPrime, & ! intent(in) derivative value for temperature of the vegetation canopy (K) - scalarCanopyWatPrime, & ! intetn(in) - mLayerd2Theta_dTk2, & ! intetn(in) - d2VolTot_d2Psi0, & ! intetn(in) - dFracLiqSnow_dTk, & ! intent(in) - d2Theta_dTkCanopy2, & ! intent(in) - dFracLiqVeg_dTkCanopy, & ! intent(in) - ! input-output: Jacobian and its diagonal - dMat, & ! intent(inout): diagonal of the Jacobian matrix - aJac, & ! intent(out): Jacobian matrix - ! output: error control - err,message) ! intent(out): error code and error message - ! ----------------------------------------------------------------------------------------------------------------- - implicit none - ! input: model control - real(rkind),intent(in) :: cj - real(rkind),intent(in) :: dt ! length of the time step (seconds) - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: computeBaseflow ! flag to indicate if computing baseflow - integer(i4b),intent(in) :: ixMatrix ! form of the Jacobian matrix - real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) - real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) - integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation - ! input: data structures - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - ! input: state variables - real(rkind),intent(in) :: mLayerTemp(:) - real(rkind),intent(in) :: mLayerTempPrime(:) - real(rkind),intent(in) :: mLayerMatricHeadPrime(:) - real(rkind),intent(in) :: mLayerMatricHeadLiqPrime(:) - real(rkind),intent(in) :: mLayerd2Theta_dTk2(:) - real(rkind),intent(in) :: mLayerVolFracWatPrime(:) - real(rkind),intent(in) :: mLayerVolFracWat(:) - real(rkind),intent(in) :: scalarCanopyTemp - real(rkind),intent(in) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) - real(rkind),intent(in) :: scalarCanopyWatPrime - real(rkind),intent(in) :: d2VolTot_d2Psi0(:) - real(rkind),intent(in) :: dFracLiqSnow_dTk(:) - real(rkind),intent(in) :: d2Theta_dTkCanopy2 - real(rkind),intent(in) :: dFracLiqVeg_dTkCanopy - ! input-output: Jacobian and its diagonal - real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix - real(rkind),intent(out) :: aJac(:,:) ! Jacobian matrix - ! output variables - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------- - ! * local variables - ! -------------------------------------------------------------- - ! indices of model state variables - integer(i4b) :: jState ! index of state within the state subset - integer(i4b) :: qState ! index of cross-derivative state variable for baseflow - integer(i4b) :: nrgState ! energy state variable - integer(i4b) :: watState ! hydrology state variable - integer(i4b) :: nState ! number of state variables - ! indices of model layers - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: jLayer ! index of model layer within the full state vector (hydrology) - integer(i4b) :: pLayer ! indices of soil layers (used for the baseflow derivatives) - ! conversion factors - real(rkind) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative - real(rkind) :: beta1 - real(rkind) :: beta2 - real(rkind) :: gamma1 - real(rkind) :: liqSnow - ! -------------------------------------------------------------- - ! associate variables from data structures - associate(& - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): layer type (iname_soil or iname_snow) - ! indices of model state variables - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain - ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer - ! vectors of indices for specfic state types within specific sub-domains IN THE FULL STATE VECTOR - ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain - ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain - ! vector of energy indices for the snow and soil domains - ! NOTE: states not in the subset are equal to integerMissing - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain - ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow domain - ixSoilOnlyNrg => indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the soil domain - ! vector of hydrology indices for the snow and soil domains - ! NOTE: states not in the subset are equal to integerMissing - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow domain - ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the soil domain - ! number of state variables of a specific type - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain - nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain - nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain - ! type and index of model control volume - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain - ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the domain (iname_veg, iname_snow, iname_soil) - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for specific model domains - ! mapping between states and model layers - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] list of indices in the full state vector that are in the state subset - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset in each element of the full state vector - ! derivatives in net vegetation energy fluxes w.r.t. relevant state variables - dCanairNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. canopy air temperature - dCanairNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. canopy temperature - dCanairNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dGroundTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. ground temperature - dCanopyNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. canopy air temperature - dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. canopy temperature - dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. ground temperature - dCanopyNetFlux_dCanLiq => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in net canopy fluxes w.r.t. canopy liquid water content - dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net ground flux w.r.t. canopy air temperature - dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net ground flux w.r.t. canopy temperature - dGroundNetFlux_dCanLiq => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in net ground fluxes w.r.t. canopy liquid water content - ! derivatives in evaporative fluxes w.r.t. relevant state variables - dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy air temperature - dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy temperature - dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. ground temperature - dCanopyEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy liquid water content - dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy air temperature - dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy temperature - dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. ground temperature - dGroundEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy liquid water content - ! derivatives in canopy water w.r.t canopy temperature - dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(in): [dp] derivative of canopy liquid storage w.r.t. temperature - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature - ! derivatives in canopy liquid fluxes w.r.t. canopy water - scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ,& ! intent(in): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water - ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below - dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above - dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below - ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above - iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(in): [dp(:)] derivative in vertical liquid water flux at layer interfaces - ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential - dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above - dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below - dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t matric head - ! derivative in baseflow flux w.r.t. aquifer storage - dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] erivative in baseflow flux w.r.t. aquifer storage (s-1) - ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables - dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above - dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk )%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - ! diagnostic variables - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) - scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) - mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] bulk volumetric heat capacity in each snow and soil layer (J m-3 K-1) - scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl)%dat(1) ,& ! intent(in): [dp] soil control on infiltration, zero or one - ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat & ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ) ! making association with data in structures - ! -------------------------------------------------------------- - ! initialize error control - err=0; message='computJacobFidaCpVar/' - - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - ! * PART 0: PRELIMINARIES (INITIALIZE JACOBIAN AND COMPUTE TIME-VARIABLE DIAGONAL TERMS) - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - - ! get the number of state variables - nState = size(dMat) - - beta1 = iden_water * Cp_Ice - iden_water * iden_air * Cp_air / iden_Ice - beta2 = iden_water * Cp_water - iden_air * Cp_air - gamma1 = beta2 - beta1 - - - ! initialize the Jacobian - ! NOTE: this needs to be done every time, since Jacobian matrix is modified in the solver - aJac(:,:) = 0._rkind ! analytical Jacobian matrix - - ! compute terms in the Jacobian for vegetation (excluding fluxes) - ! NOTE: energy for vegetation is computed *within* the iteration loop as it includes phase change - if(ixVegNrg/=integerMissing)then - dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy ) * cj & - + LH_fus*iden_water * scalarCanopyTempPrime * d2Theta_dTkCanopy2 & - + LH_fus * dFracLiqVeg_dTkCanopy * scalarCanopyWatPrime / canopyDepth ! volumetric heat capacity of the vegetation (J m-3 K-1) - end if - - ! compute additional terms for the Jacobian for the snow-soil domain (excluding fluxes) - ! NOTE: energy for snow+soil is computed *within* the iteration loop as it includes phase change - do iLayer=1,nLayers - select case(layerType(iLayer)) - case(iname_snow); liqSnow = mLayerFracLiqSnow(iLayer) - case default; liqSnow = 0 - end select - if(ixSnowSoilNrg(iLayer)/=integerMissing) then - dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + (LH_fus*iden_water + gamma1*mLayerTemp(iLayer))*mLayerdTheta_dTk(iLayer) ) * cj & - + LH_fus*iden_water * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & - + LH_fus*iden_water * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) & - + mLayerVolFracWatPrime(iLayer) * ( beta1 + gamma1*liqSnow + gamma1*dFracLiqSnow_dTk(iLayer)*mLayerTemp(iLayer) ) & - + gamma1*mLayerTempPrime(iLayer) * ( mLayerd2Theta_dTk2(iLayer)*mLayerTemp(iLayer) + 2*mLayerdTheta_dTk(iLayer) ) - end if - end do - - ! compute additional terms for the Jacobian for the soil domain (excluding fluxes) - do iLayer=1,nSoil -! print *, '2' - if(ixSoilOnlyHyd(iLayer)/=integerMissing)then - dMat(ixSoilOnlyHyd(iLayer)) = ( dVolTot_dPsi0(iLayer) + dCompress_dPsi(iLayer) ) * cj + d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) - - if(ixRichards==mixdform)then - dMat(ixSoilOnlyHyd(iLayer)) = dMat(ixSoilOnlyHyd(iLayer)) + specificStorage * dVolTot_dPsi0(iLayer) * mLayerMatricHeadPrime(iLayer) / theta_sat(iLayer) - end if - - end if - end do - - - - ! define the form of the matrix - select case(ixMatrix) - - ! FULL MATRIX - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - case(ixFullMatrix) ! ixFullMatrix ixBandMatrix - - ! check - if(size(aJac,1)/=size(dMat) .or. size(aJac,2)/=size(dMat))then - message=trim(message)//'unexpected shape of the Jacobian matrix: expect aJac(nState,nState)' - err=20; return - end if - - ! ----- - ! * energy and liquid fluxes over vegetation... - ! --------------------------------------------- - if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) - - ! * liquid water fluxes for vegetation canopy (-) - if(ixVegHyd/=integerMissing) aJac(ixVegHyd,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanLiq - scalarCanopyLiqDeriv)*dt + 1._rkind * cj - ! * cross-derivative terms for canopy water - if(ixVegHyd/=integerMissing)then - ! cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) - if(ixCasNrg/=integerMissing) aJac(ixVegHyd,ixCasNrg) = -dCanopyEvaporation_dTCanair*dt - if(ixVegNrg/=integerMissing) aJac(ixVegHyd,ixVegNrg) = -dCanopyEvaporation_dTCanopy*dt + dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy - if(ixTopNrg/=integerMissing) aJac(ixVegHyd,ixTopNrg) = -dCanopyEvaporation_dTGround*dt - ! cross-derivative terms w.r.t. canopy water (kg-1 m2) - if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water - ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) - ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (dt/canopyDepth) *(-dCanopyNetFlux_dCanLiq) + cj * (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth ! dF/dLiq - if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanLiq) - endif - - ! cross-derivative terms w.r.t. canopy temperature (K-1) - if(ixVegNrg/=integerMissing)then - if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegNrg) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarCanopyLiqDeriv*dCanLiq_dTcanopy)/iden_water - endif - - ! energy fluxes with the canopy air space (J m-3 K-1) - if(ixCasNrg/=integerMissing)then - aJac(ixCasNrg,ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) * cj - if(ixVegNrg/=integerMissing) aJac(ixCasNrg,ixVegNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanopyTemp) - if(ixTopNrg/=integerMissing) aJac(ixCasNrg,ixTopNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dGroundTemp) - endif - - ! energy fluxes with the vegetation canopy (J m-3 K-1) - if(ixVegNrg/=integerMissing)then - if(ixCasNrg/=integerMissing) aJac(ixVegNrg,ixCasNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanairTemp) - aJac(ixVegNrg,ixVegNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanopyTemp) + dMat(ixVegNrg) - if(ixTopNrg/=integerMissing) aJac(ixVegNrg,ixTopNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dGroundTemp) - endif - - ! energy fluxes with the surface (J m-3 K-1) - if(ixTopNrg/=integerMissing)then - if(ixCasNrg/=integerMissing) aJac(ixTopNrg,ixCasNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanairTemp) - if(ixVegNrg/=integerMissing) aJac(ixTopNrg,ixVegNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanopyTemp) - endif - - endif ! if there is a need to compute energy fluxes within vegetation - - ! ----- - ! * energy fluxes for the snow+soil domain... - ! ------------------------------------------- - if(nSnowSoilNrg>0)then - do iLayer=1,nLayers ! loop through all layers in the snow+soil domain - - ! check if the state is in the subset - if(ixSnowSoilNrg(iLayer)==integerMissing) cycle - - ! - define index within the state subset and the full state vector - jState = ixSnowSoilNrg(iLayer) ! index within the state subset - - ! - diagonal elements - aJac(jState,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) - - ! - lower-diagonal elements - if(iLayer > 1)then - if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixSnowSoilNrg(iLayer-1),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) - endif - - ! - upper diagonal elements - if(iLayer < nLayers)then - if(ixSnowSoilNrg(iLayer+1)/=integerMissing) aJac(ixSnowSoilNrg(iLayer+1),jState) = (dt/mLayerDepth(iLayer+1))*(-dNrgFlux_dTempAbove(iLayer ) ) - endif - - end do ! (looping through energy states in the snow+soil domain) - endif ! (if the subset includes energy state variables in the snow+soil domain) - - ! ----- - ! * liquid water fluxes for the snow domain... - ! -------------------------------------------- - if(nSnowOnlyHyd>0)then - do iLayer=1,nSnow ! loop through layers in the snow domain - - ! - check that the snow layer is desired - if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle - - ! - define state indices for the current layer - watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - - ! compute factor to convert liquid water derivative to total water derivative - select case( ixHydType(iLayer) ) - case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) - case default; convLiq2tot = 1._rkind - end select - - ! - diagonal elements - aJac(watState,watState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot + dMat(watState) * cj - - ! - lower-diagonal elements - if(iLayer > 1)then - if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSnowOnlyHyd(iLayer-1),watState) = 0._rkind ! sub-diagonal: no dependence on other layers - endif - - ! - upper diagonal elements - if(iLayer < nSnow)then - if(ixSnowOnlyHyd(iLayer+1)/=integerMissing) aJac(ixSnowOnlyHyd(iLayer+1),watState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot ! dVol(below)/dLiq(above) -- (-) - endif - - ! - compute cross-derivative terms for energy - ! NOTE: increase in volumetric liquid water content balanced by a decrease in volumetric ice content - if(nSnowOnlyNrg>0)then - - ! (define the energy state) - nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector - if(nrgstate/=integerMissing)then ! (energy state for the current layer is within the state subset) - - ! (cross-derivative terms for the current layer) - ! print *, '3' - aJac(nrgState,watState) = ( -LH_fus*iden_water*(1._rkind - mLayerFracLiqSnow(iLayer)) & - + ( beta1 + gamma1*mLayerFracLiqSnow(iLayer) ) * mLayerTemp(iLayer) ) * cj & - + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) & - + ( gamma1*dFracLiqSnow_dTk(iLayer)*mLayerTemp(iLayer) + beta1 + gamma1*mLayerFracLiqSnow(iLayer) ) & - * mLayerTempPrime(iLayer) ! (dF/dLiq) - ! (cross-derivative terms for the layer below) - if(iLayer < nSnow)then - aJac(ixSnowOnlyHyd(iLayer+1),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 - endif ! (if there is a water state in the layer below the current layer in the given state subset) - - endif ! (if the energy state for the current layer is within the state subset) - endif ! (if state variables exist for energy in snow+soil layers) - - end do ! (looping through liquid water states in the snow domain) - endif ! (if the subset includes hydrology state variables in the snow domain) - - ! ----- - ! * liquid water fluxes for the soil domain... - ! -------------------------------------------- - if(nSoilOnlyHyd>0)then - - do iLayer=1,nSoil - - ! - check that the soil layer is desired - if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle - - ! - define state indices - watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset - - ! - define indices of the soil layers - jLayer = iLayer+nSnow ! index of layer in the snow+soil vector - - ! - compute the diagonal elements - ! all terms *excluding* baseflow - aJac(watState,watState) = (dt/mLayerDepth(jLayer))*(-dq_dHydStateBelow(iLayer-1) + dq_dHydStateAbove(iLayer)) + dMat(watState) - - ! - compute the lower-diagonal elements - if(iLayer > 1)then - if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) - endif - - ! - compute the upper-diagonal elements - if(iLayer0 .and. nSoilOnlyNrg>0)then - do iLayer=1,nSoilOnlyHyd - - ! - check that the soil layer is desired - if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle - - ! - define index of hydrology state variable within the state subset - watState = ixSoilOnlyHyd(iLayer) - - ! - define indices of the soil layers - jLayer = iLayer+nSnow ! index of layer in the snow+soil vector - - ! - define the energy state variable - nrgState = ixNrgLayer(jLayer) ! index within the full state vector - - ! only compute derivatives if the energy state for the current layer is within the state subset - if(nrgstate/=integerMissing)then - - ! - compute the Jacobian for the layer itself - aJac(watState,nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance - - ! - include derivatives w.r.t. ground evaporation - if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer - if(computeVegFlux)then - aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanLiq/iden_water) ! dVol/dLiq (kg m-2)-1 - aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) - aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) - endif - aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) - endif - - ! melt-freeze: compute derivative in energy with respect to mass - if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present - ! print *, '4' - aJac(nrgState,watState) = dVolTot_dPsi0(iLayer)*(beta2*mLayerTemp(iLayer) - LH_fus*iden_water)*cj & - + (beta2 - LH_fus*iden_water)*d2VolTot_d2Psi0(iLayer)*mLayerMatricHeadPrime(iLayer) & - + beta2*mLayerTempPrime(iLayer)*dVolTot_dPsi0(iLayer) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content - else - aJac(nrgState,watState) = 0._rkind - endif - - ! - compute lower diagonal elements - if(iLayer>1)then - if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 - endif - - ! compute upper-diagonal elements - if(iLayer Date: Wed, 30 Jun 2021 00:27:27 -0600 Subject: [PATCH 0161/1472] renamed fida_datatypes to type4IDA --- build/Makefile | 2 +- build/source/engine/evalDAE4IDA.f90 | 4 ++-- build/source/engine/evalJac4IDA.f90 | 4 ++-- build/source/engine/findDiscontinuity.f90 | 4 ++-- build/source/engine/nonlinSolvFida.f90 | 2 +- build/source/engine/solveByIDA.f90 | 2 +- build/source/engine/tolFida.f90 | 4 ++-- build/source/engine/{fida_datatypes.f90 => type4IDA.f90} | 4 ++-- 8 files changed, 13 insertions(+), 13 deletions(-) rename build/source/engine/{fida_datatypes.f90 => type4IDA.f90} (99%) diff --git a/build/Makefile b/build/Makefile index fbcb684c3..30c1928be 100644 --- a/build/Makefile +++ b/build/Makefile @@ -221,7 +221,7 @@ SUMMA_SOLVER= \ eval8summa.f90 \ summaSolve.f90 \ systemSolv.f90 \ - fida_datatypes.f90 \ + type4IDA.f90 \ tolFida.f90 \ computEnthalpy.f90 \ computHeatCap.f90 \ diff --git a/build/source/engine/evalDAE4IDA.f90 b/build/source/engine/evalDAE4IDA.f90 index 742246180..d6f945ed7 100644 --- a/build/source/engine/evalDAE4IDA.f90 +++ b/build/source/engine/evalDAE4IDA.f90 @@ -6,7 +6,7 @@ module evalDAE4IDA_module !======= Inclusions =========== use, intrinsic :: iso_c_binding use nrtype - use fida_datatypes + use type4IDA USE globalData,only:model_decisions ! model decision structure USE globalData,only:flux_meta ! metadata on the model fluxes ! provide access to the derived types to define the data structures @@ -48,7 +48,7 @@ integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_da use fsundials_nvector_mod use fnvector_serial_mod use nrtype - use fida_datatypes + use type4IDA use eval8DAE_module,only:eval8DAE !======= Declarations ========= diff --git a/build/source/engine/evalJac4IDA.f90 b/build/source/engine/evalJac4IDA.f90 index bb14ac788..8f5e94218 100644 --- a/build/source/engine/evalJac4IDA.f90 +++ b/build/source/engine/evalJac4IDA.f90 @@ -6,7 +6,7 @@ module evalJac4IDA_module !======= Inclusions =========== use, intrinsic :: iso_c_binding use nrtype - use fida_datatypes + use type4IDA USE globalData,only:model_decisions ! model decision structure ! provide access to the derived types to define the data structures USE data_types,only:& @@ -45,7 +45,7 @@ integer(c_int) function evalJac4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & use fnvector_serial_mod use fsunmatrix_dense_mod use nrtype - use fida_datatypes + use type4IDA use eval8JacDAE_module,only:eval8JacDAE !======= Declarations ========= implicit none diff --git a/build/source/engine/findDiscontinuity.f90 b/build/source/engine/findDiscontinuity.f90 index 12d7c3f93..cd188dfe0 100644 --- a/build/source/engine/findDiscontinuity.f90 +++ b/build/source/engine/findDiscontinuity.f90 @@ -6,7 +6,7 @@ module findDiscontinuity_module !======= Inclusions =========== use, intrinsic :: iso_c_binding use nrtype - use fida_datatypes + use type4IDA USE globalData,only:model_decisions ! model decision structure USE globalData,only:flux_meta ! metadata on the model fluxes ! provide access to the derived types to define the data structures @@ -46,7 +46,7 @@ integer(c_int) function findDiscontinuity(tres, sunvec_y, sunvec_yp, fval, user_ use fsundials_nvector_mod use fnvector_serial_mod use nrtype - use fida_datatypes + use type4IDA use varExtrFida_module, only:residDiscontinuity !======= Declarations ========= diff --git a/build/source/engine/nonlinSolvFida.f90 b/build/source/engine/nonlinSolvFida.f90 index 974c29d4a..442a6f761 100644 --- a/build/source/engine/nonlinSolvFida.f90 +++ b/build/source/engine/nonlinSolvFida.f90 @@ -27,7 +27,7 @@ integer(c_int) function sysFnFida(ycor, F, mem) result(ierr) bind(C,name='sysFnF use fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver use fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver use nrtype - use fida_datatypes + use type4IDA implicit none type(N_Vector) :: ycor type(N_Vector) :: F diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 6ccc573e0..ff9c2e122 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -7,7 +7,7 @@ module solveByIDA_module !======= Inclusions =========== USE, intrinsic :: iso_c_binding USE nrtype -USE fida_datatypes +USE type4IDA ! access the global print flag USE globalData,only:globalPrintFlag diff --git a/build/source/engine/tolFida.f90 b/build/source/engine/tolFida.f90 index af46f1991..bafca7cf1 100644 --- a/build/source/engine/tolFida.f90 +++ b/build/source/engine/tolFida.f90 @@ -7,7 +7,7 @@ module tolFida_module !======= Inclusions =========== use, intrinsic :: iso_c_binding use nrtype - use fida_datatypes + use type4IDA ! missing values USE globalData,only:integerMissing ! missing integer @@ -86,7 +86,7 @@ integer(c_int) function computWeightFida(sunvec_y, sunvec_ewt, user_data) & use fsundials_nvector_mod use fnvector_serial_mod use nrtype - use fida_datatypes + use type4IDA !======= Declarations ========= implicit none diff --git a/build/source/engine/fida_datatypes.f90 b/build/source/engine/type4IDA.f90 similarity index 99% rename from build/source/engine/fida_datatypes.f90 rename to build/source/engine/type4IDA.f90 index bb7219fde..7251a2912 100644 --- a/build/source/engine/fida_datatypes.f90 +++ b/build/source/engine/type4IDA.f90 @@ -1,6 +1,6 @@ -module fida_datatypes +module type4IDA ! data types USE nrtype @@ -76,7 +76,7 @@ module fida_datatypes end type eqnsData -end module fida_datatypes +end module type4IDA From f86f27b8bd0c383e3fd4f243c27db055be50d4f1 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 30 Jun 2021 00:30:12 -0600 Subject: [PATCH 0162/1472] findDiscont deleted --- build/Makefile | 1 - build/source/engine/findDiscontinuity.f90 | 99 ----------------------- 2 files changed, 100 deletions(-) delete mode 100644 build/source/engine/findDiscontinuity.f90 diff --git a/build/Makefile b/build/Makefile index 30c1928be..0e090110f 100644 --- a/build/Makefile +++ b/build/Makefile @@ -234,7 +234,6 @@ SUMMA_SOLVER= \ eval8JacDAE.f90 \ evalJac4IDA.f90 \ nonlinSolvFida.f90 \ - findDiscontinuity.f90 \ computSnowDepth.f90 \ solveByIDA.f90 \ systemSolvSundials.f90 \ diff --git a/build/source/engine/findDiscontinuity.f90 b/build/source/engine/findDiscontinuity.f90 deleted file mode 100644 index cd188dfe0..000000000 --- a/build/source/engine/findDiscontinuity.f90 +++ /dev/null @@ -1,99 +0,0 @@ - - -module findDiscontinuity_module - - - !======= Inclusions =========== - use, intrinsic :: iso_c_binding - use nrtype - use type4IDA - USE globalData,only:model_decisions ! model decision structure - USE globalData,only:flux_meta ! metadata on the model fluxes - ! provide access to the derived types to define the data structures - USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - model_options ! defines the model decisions - USE multiconst,only:iden_water ! intrinsic density of liquid water (kg m-3) - USE var_lookup,only:iLookDIAG - USE var_lookup,only:iLookPROG - - - ! privacy - implicit none - private - public::findDiscontinuity - - -contains - - ! ********************************************************************************************************** - ! public function findDiscontinuity: the root function - ! ********************************************************************************************************** - ! Return values: - ! 0 = success, - ! 1 = recoverable error, - ! -1 = non-recoverable error - ! ---------------------------------------------------------------- - integer(c_int) function findDiscontinuity(tres, sunvec_y, sunvec_yp, fval, user_data) & - result(ierr) bind(C,name='findDiscontinuity') - - !======= Inclusions =========== - use, intrinsic :: iso_c_binding - use fida_mod - use fsundials_nvector_mod - use fnvector_serial_mod - use nrtype - use type4IDA - use varExtrFida_module, only:residDiscontinuity - - !======= Declarations ========= - implicit none - - ! calling variables - real(rkind), value :: tres ! current time - type(N_Vector) :: sunvec_y ! solution N_Vector y - type(N_Vector) :: sunvec_yp ! derivative N_Vector y' - real(c_double) :: fval(1000) ! root function values - type(c_ptr), value :: user_data ! user-defined data - - - ! pointers to data in SUNDIALS vectors - type(eqnsData), pointer :: eqns_data ! equations data - real(rkind), pointer :: stateVec(:) - real(rkind), pointer :: stateVecPrime(:) - integer(i4b) :: retval - - - - - !======= Internals ============ - - ! get equations data from user-defined data - call c_f_pointer(user_data, eqns_data) - - ! get data arrays from SUNDIALS vectors - stateVec => FN_VGetArrayPointer(sunvec_y) - ! stateVecPrime => FN_VGetArrayPointer(sunvec_yp) - - call residDiscontinuity(& - ! input - stateVec, & ! intent(in): model state vector (mixed units) - eqns_data%diag_data, & ! intent(in): model diagnostic variables for a local HRU - eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU - eqns_data%indx_data, & ! intent(in): indices defining model states and layers - ! output - fval, & ! intent(out) - eqns_data%err,eqns_data%message) ! intent(out): error control - - - ! return success - ierr = 0 - return - - end function findDiscontinuity - - -end module findDiscontinuity_module From 5eafed0840070474695c22b148d240662ec5cf38 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 30 Jun 2021 00:33:46 -0600 Subject: [PATCH 0163/1472] extra files deleted --- build/Makefile | 1 - build/source/engine/layerDivideFida.f90 | 487 ------------------------ build/source/engine/nonlinSolvFida.f90 | 39 -- 3 files changed, 527 deletions(-) delete mode 100644 build/source/engine/layerDivideFida.f90 delete mode 100644 build/source/engine/nonlinSolvFida.f90 diff --git a/build/Makefile b/build/Makefile index 0e090110f..c8c864428 100644 --- a/build/Makefile +++ b/build/Makefile @@ -233,7 +233,6 @@ SUMMA_SOLVER= \ computJacDAE.f90 \ eval8JacDAE.f90 \ evalJac4IDA.f90 \ - nonlinSolvFida.f90 \ computSnowDepth.f90 \ solveByIDA.f90 \ systemSolvSundials.f90 \ diff --git a/build/source/engine/layerDivideFida.f90 b/build/source/engine/layerDivideFida.f90 deleted file mode 100644 index 15626b0d3..000000000 --- a/build/source/engine/layerDivideFida.f90 +++ /dev/null @@ -1,487 +0,0 @@ -! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington -! -! This file is part of SUMMA -! -! For more information see: http://www.ral.ucar.edu/projects/summa -! -! This program is free software: you can redistribute it and/or modify -! it under the terms of the GNU General Public License as published by -! the Free Software Foundation, either version 3 of the License, or -! (at your option) any later version. -! -! This program is distributed in the hope that it will be useful, -! but WITHOUT ANY WARRANTY; without even the implied warranty of -! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -! GNU General Public License for more details. -! -! You should have received a copy of the GNU General Public License -! along with this program. If not, see . - -module layerDivideFida_module - -! variable types -USE nrtype - -! physical constants -USE multiconst,only:& - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water ! intrinsic density of liquid water (kg m-3) - -! access named variables for snow and soil -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil - -! access missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing double precision number - -! access metadata -USE globalData,only:prog_meta,diag_meta,flux_meta,indx_meta ! metadata - -! access the derived types to define the data structures -USE data_types,only:& - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - model_options ! defines the model decisions - -! access named variables defining elements in the data structures -USE var_lookup,only:iLookPROG,iLookDIAG,iLookFLUX,iLookINDEX ! named variables for structure elements -USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure -USE var_lookup,only:iLookPARAM ! named variables for elements of the parameter structure - -! define look-up values for the choice of method to combine and sub-divide snow layers -USE mDecisions_module,only:& - sameRulesAllLayers, & ! SNTHERM option: same combination/sub-dividion rules applied to all layers - rulesDependLayerIndex ! CLM option: combination/sub-dividion rules depend on layer index - -! define look-up values for the choice of canopy shortwave radiation method -USE mDecisions_module,only:& - noah_mp, & ! full Noah-MP implementation (including albedo) - CLM_2stream, & ! CLM 2-stream model (see CLM documentation) - UEB_2stream, & ! UEB 2-stream model (Mahat and Tarboton, WRR 2011) - NL_scatter, & ! Simplified method Nijssen and Lettenmaier (JGR 1999) - BeersLaw ! Beer's Law (as implemented in VIC) - -! define look-up values for the choice of albedo method -USE mDecisions_module,only:& ! identify model options for snow albedo - constantDecay, & ! constant decay in snow albedo (e.g., VIC, CLASS) - variableDecay ! variable decay in snow albedo (e.g., BATS approach, with destructive metamorphism + soot content) - -! privacy -implicit none -private -public::layerDivideFida - -contains - - ! *********************************************************************************************************** - ! public subroutine layerDivideFida: add new snowfall to the system, and increase number of snow layers if needed - ! *********************************************************************************************************** - subroutine layerDivideFida(& - ! input/output: model data structures - model_decisions, & ! intent(in): model decisions - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): type of each layer - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output - divideLayer, & ! intent(out): flag to denote that a layer was divided - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------- - ! computational modules - USE snow_utils_module,only:fracliquid,templiquid ! functions to compute temperature/liquid water - USE globalData,only:maxSnowLayers, & ! maximum number of snow layers - veryBig - implicit none - ! -------------------------------------------------------------------------------------------------------- - ! input/output: model data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(inout) :: indx_data ! type of each layer - type(var_dlength),intent(inout) :: prog_data ! model prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model flux variables - ! output - logical(lgt),intent(out) :: divideLayer ! flag to denote that a layer was divided - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------- - ! define local variables - character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b) :: nSnow ! number of snow layers - integer(i4b) :: nSoil ! number of soil layers - integer(i4b) :: nLayers ! total number of layers - integer(i4b) :: iLayer ! layer index - integer(i4b) :: jLayer ! layer index - real(rkind),dimension(4) :: zmax_lower ! lower value of maximum layer depth - real(rkind),dimension(4) :: zmax_upper ! upper value of maximum layer depth - real(rkind) :: zmaxCheck ! value of zmax for a given snow layer - integer(i4b) :: nCheck ! number of layers to check to divide - logical(lgt) :: createLayer ! flag to indicate we are creating a new snow layer - real(rkind) :: depthOriginal ! original layer depth before sub-division (m) - real(rkind),parameter :: fracTop=0.5_rkind ! fraction of old layer used for the top layer - real(rkind) :: surfaceLayerSoilTemp ! temperature of the top soil layer (K) - real(rkind) :: maxFrozenSnowTemp ! maximum temperature when effectively all water is frozen (K) - real(rkind),parameter :: unfrozenLiq=0.01_rkind ! unfrozen liquid water used to compute maxFrozenSnowTemp (-) - real(rkind) :: volFracWater ! volumetric fraction of total water, liquid and ice (-) - real(rkind) :: fracLiq ! fraction of liquid water (-) - integer(i4b),parameter :: ixVisible=1 ! named variable to define index in array of visible part of the spectrum - integer(i4b),parameter :: ixNearIR=2 ! named variable to define index in array of near IR part of the spectrum - real(rkind),parameter :: verySmall=1.e-10_rkind ! a very small number (used for error checking) - ! -------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="layerDivideFida/" - ! -------------------------------------------------------------------------------------------------------- - ! associate variables in the data structures - associate(& - ! model decisions - ix_snowLayers => model_decisions(iLookDECISIONS%snowLayers)%iDecision, & ! decision for snow combination - ! model parameters (compute layer temperature) - fc_param => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! freezing curve parameter for snow (K-1) - ! model parameters (new snow density) - newSnowDenMin => mpar_data%var(iLookPARAM%newSnowDenMin)%dat(1), & ! minimum new snow density (kg m-3) - newSnowDenMult => mpar_data%var(iLookPARAM%newSnowDenMult)%dat(1), & ! multiplier for new snow density (kg m-3) - newSnowDenScal => mpar_data%var(iLookPARAM%newSnowDenScal)%dat(1), & ! scaling factor for new snow density (K) - ! model parameters (control the depth of snow layers) - zmax => mpar_data%var(iLookPARAM%zmax)%dat(1), & ! maximum layer depth (m) - zmaxLayer1_lower => mpar_data%var(iLookPARAM%zmaxLayer1_lower)%dat(1), & ! maximum layer depth for the 1st (top) layer when only 1 layer (m) - zmaxLayer2_lower => mpar_data%var(iLookPARAM%zmaxLayer2_lower)%dat(1), & ! maximum layer depth for the 2nd layer when only 2 layers (m) - zmaxLayer3_lower => mpar_data%var(iLookPARAM%zmaxLayer3_lower)%dat(1), & ! maximum layer depth for the 3rd layer when only 3 layers (m) - zmaxLayer4_lower => mpar_data%var(iLookPARAM%zmaxLayer4_lower)%dat(1), & ! maximum layer depth for the 4th layer when only 4 layers (m) - zmaxLayer1_upper => mpar_data%var(iLookPARAM%zmaxLayer1_upper)%dat(1), & ! maximum layer depth for the 1st (top) layer when > 1 layer (m) - zmaxLayer2_upper => mpar_data%var(iLookPARAM%zmaxLayer2_upper)%dat(1), & ! maximum layer depth for the 2nd layer when > 2 layers (m) - zmaxLayer3_upper => mpar_data%var(iLookPARAM%zmaxLayer3_upper)%dat(1), & ! maximum layer depth for the 3rd layer when > 3 layers (m) - zmaxLayer4_upper => mpar_data%var(iLookPARAM%zmaxLayer4_upper)%dat(1), & ! maximum layer depth for the 4th layer when > 4 layers (m) - ! diagnostic scalar variables - scalarSnowfall => flux_data%var(iLookFLUX%scalarSnowfall)%dat(1), & ! snowfall flux (kg m-2 s-1) - scalarSnowfallTemp => diag_data%var(iLookDIAG%scalarSnowfallTemp)%dat(1), & ! computed temperature of fresh snow (K) - scalarSnowDepth => prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! total snow depth (m) - scalarSWE => prog_data%var(iLookPROG%scalarSWE)%dat(1) & ! SWE (kg m-2) - ) ! end associate statement - - ! --------------------------------------------------------------------------------------------------- - - ! initialize flag to denote that a layer was divided - divideLayer=.false. - - ! identify algorithmic control parameters to syb-divide and combine snow layers - zmax_lower = (/zmaxLayer1_lower, zmaxLayer2_lower, zmaxLayer3_lower, zmaxLayer4_lower/) - zmax_upper = (/zmaxLayer1_upper, zmaxLayer2_upper, zmaxLayer3_upper, zmaxLayer4_upper/) - - ! initialize the number of snow layers - nSnow = indx_data%var(iLookINDEX%nSnow)%dat(1) - nSoil = indx_data%var(iLookINDEX%nSoil)%dat(1) - nLayers = indx_data%var(iLookINDEX%nLayers)%dat(1) - - ! ***** special case of no snow layers - if(nSnow==0)then - - ! check if create the first snow layer - select case(ix_snowLayers) - case(sameRulesAllLayers); createLayer = (scalarSnowDepth > zmax) - case(rulesDependLayerIndex); createLayer = (scalarSnowDepth > zmaxLayer1_lower) - case default; err=20; message=trim(message)//'unable to identify option to combine/sub-divide snow layers'; return - end select ! (option to combine/sub-divide snow layers) - - ! ** create a new snow layer - if(createLayer)then - - ! flag that the layers have changed - divideLayer=.true. - - ! add a layer to all model variables - iLayer=0 ! (layer to divide: 0 is the special case of "snow without a layer") - call addModelLayer(prog_data,prog_meta,iLayer,nSnow,nLayers,err,cmessage); if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - call addModelLayer(diag_data,diag_meta,iLayer,nSnow,nLayers,err,cmessage); if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - call addModelLayer(flux_data,flux_meta,iLayer,nSnow,nLayers,err,cmessage); if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - call addModelLayer(indx_data,indx_meta,iLayer,nSnow,nLayers,err,cmessage); if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - ! associate local variables to the information in the data structures - ! NOTE: need to do this here, since state vectors have just been modified - associate(& - ! coordinate variables - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! depth of each layer (m) - ! model state variables - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! temperature of each layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! volumetric fraction of ice in each layer (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat & ! volumetric fraction of liquid water in each layer (-) - ) ! (association of local variables to the information in the data structures) - - ! get the layer depth - mLayerDepth(1) = scalarSnowDepth - - ! compute surface layer temperature - surfaceLayerSoilTemp = mLayerTemp(2) ! temperature of the top soil layer (K) - maxFrozenSnowTemp = templiquid(unfrozenLiq,fc_param) ! snow temperature at fraction "unfrozenLiq" (K) - mLayerTemp(1) = min(maxFrozenSnowTemp,surfaceLayerSoilTemp) ! snow temperature (K) - - ! compute the fraction of liquid water associated with the layer temperature - fracLiq = fracliquid(mLayerTemp(1),fc_param) - - ! compute volumeteric fraction of liquid water and ice - volFracWater = (scalarSWE/scalarSnowDepth)/iden_water ! volumetric fraction of total water (liquid and ice) - mLayerVolFracIce(1) = (1._rkind - fracLiq)*volFracWater*(iden_water/iden_ice) ! volumetric fraction of ice (-) - mLayerVolFracLiq(1) = fracLiq *volFracWater ! volumetric fraction of liquid water (-) - - ! end association with local variables to the information in the data structures) - end associate - - ! initialize albedo - ! NOTE: albedo is computed within the Noah-MP radiation routine - if(model_decisions(iLookDECISIONS%canopySrad)%iDecision /= noah_mp)then - select case(model_decisions(iLookDECISIONS%alb_method)%iDecision) - ! (constant decay rate -- albedo the same for all spectral bands) - case(constantDecay) - prog_data%var(iLookPROG%scalarSnowAlbedo)%dat(1) = mpar_data%var(iLookPARAM%albedoMax)%dat(1) - prog_data%var(iLookPROG%spectralSnowAlbedoDiffuse)%dat(:) = mpar_data%var(iLookPARAM%albedoMax)%dat(1) - ! (variable decay rate) - case(variableDecay) - prog_data%var(iLookPROG%spectralSnowAlbedoDiffuse)%dat(ixVisible) = mpar_data%var(iLookPARAM%albedoMaxVisible)%dat(1) - prog_data%var(iLookPROG%spectralSnowAlbedoDiffuse)%dat(ixNearIR) = mpar_data%var(iLookPARAM%albedoMaxNearIR)%dat(1) - prog_data%var(iLookPROG%scalarSnowAlbedo)%dat(1) = ( mpar_data%var(iLookPARAM%Frad_vis)%dat(1))*mpar_data%var(iLookPARAM%albedoMaxVisible)%dat(1) + & - (1._rkind - mpar_data%var(iLookPARAM%Frad_vis)%dat(1))*mpar_data%var(iLookPARAM%albedoMaxNearIR)%dat(1) - case default; err=20; message=trim(message)//'unable to identify option for snow albedo'; return - end select ! identify option for snow albedo - ! set direct albedo to diffuse albedo - diag_data%var(iLookDIAG%spectralSnowAlbedoDirect)%dat(:) = prog_data%var(iLookPROG%spectralSnowAlbedoDiffuse)%dat(:) - end if ! (if NOT using the Noah-MP radiation routine) - - end if ! if creating a new layer - - ! end special case of nSnow=0 - ! ******************************************************************************************************************** - ! ******************************************************************************************************************** - - ! ***** sub-divide snow layers, if necessary - else ! if nSnow>0 - - ! identify the number of layers to check for need for sub-division - nCheck = min(nSnow, maxSnowLayers-1) ! the depth of the last layer, if it exists, does not have a maximum value - ! loop through all layers, and sub-divide a given layer, if necessary - do iLayer=1,nCheck - divideLayer=.false. - - ! identify the maximum depth of the layer - select case(ix_snowLayers) - case(sameRulesAllLayers) - if (nCheck >= maxSnowLayers-1) then - ! make sure we don't divide so make very big - zmaxCheck = veryBig - else - zmaxCheck = zmax - end if - case(rulesDependLayerIndex) - if(iLayer == nSnow)then - zmaxCheck = zmax_lower(iLayer) - else - zmaxCheck = zmax_upper(iLayer) - end if - case default; err=20; message=trim(message)//'unable to identify option to combine/sub-divide snow layers'; return - end select ! (option to combine/sub-divide snow layers) - - ! check the need to sub-divide - if(prog_data%var(iLookPROG%mLayerDepth)%dat(iLayer) > zmaxCheck)then - - ! flag that layers were divided - divideLayer=.true. - - ! add a layer to all model variables - call addModelLayer(prog_data,prog_meta,iLayer,nSnow,nLayers,err,cmessage); if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - call addModelLayer(diag_data,diag_meta,iLayer,nSnow,nLayers,err,cmessage); if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - call addModelLayer(flux_data,flux_meta,iLayer,nSnow,nLayers,err,cmessage); if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - call addModelLayer(indx_data,indx_meta,iLayer,nSnow,nLayers,err,cmessage); if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - ! define the layer depth - layerSplit: associate(mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat) - depthOriginal = mLayerDepth(iLayer) - mLayerDepth(iLayer) = fracTop*depthOriginal - mLayerDepth(iLayer+1) = (1._rkind - fracTop)*depthOriginal - end associate layerSplit - - exit ! NOTE: only sub-divide one layer per substep - - end if ! (if sub-dividing layer) - - end do ! (looping through layers) - - end if ! if nSnow==0 - - ! update coordinates - if(divideLayer)then - - ! associate coordinate variables in data structure - geometry: associate(& - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! depth of the layer (m) - mLayerHeight => prog_data%var(iLookPROG%mLayerHeight)%dat ,& ! height of the layer mid-point (m) - iLayerHeight => prog_data%var(iLookPROG%iLayerHeight)%dat ,& ! height of the layer interface (m) - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! type of each layer (iname_snow or iname_soil) - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! total number of layers - ) ! (association of local variables with coordinate variab;es in data structures) - - ! update the layer type - layerType(1:nSnow+1) = iname_snow - layerType(nSnow+2:nLayers+1) = iname_soil - - ! identify the number of snow and soil layers, and check all is a-OK - nSnow = count(layerType==iname_snow) - nSoil = count(layerType==iname_soil) - nLayers = nSnow + nSoil - - ! re-set coordinate variables - iLayerHeight(0) = -scalarSnowDepth - do jLayer=1,nLayers - iLayerHeight(jLayer) = iLayerHeight(jLayer-1) + mLayerDepth(jLayer) - mLayerHeight(jLayer) = (iLayerHeight(jLayer-1) + iLayerHeight(jLayer))/2._rkind - end do - - ! check - if(abs(sum(mLayerDepth(1:nSnow)) - scalarSnowDepth) > verySmall)then - print*, 'nSnow = ', nSnow - write(*,'(a,1x,f30.25,1x)') 'sum(mLayerDepth(1:nSnow)) = ', sum(mLayerDepth(1:nSnow)) - write(*,'(a,1x,f30.25,1x)') 'scalarSnowDepth = ', scalarSnowDepth - write(*,'(a,1x,f30.25,1x)') 'epsilon(scalarSnowDepth) = ', epsilon(scalarSnowDepth) - message=trim(message)//'sum of layer depths does not equal snow depth' - err=20; return - end if - - ! end association with coordinate variables in data structure - end associate geometry - - end if ! if dividing a layer - - ! end associate variables in data structure - end associate - - end subroutine layerDivideFida - - - ! ************************************************************************************************ - ! private subroutine addModelLayer: add an additional layer to all model vectors - ! ************************************************************************************************ - subroutine addModelLayer(dataStruct,metaStruct,ix_divide,nSnow,nLayers,err,message) - USE var_lookup,only:iLookVarType ! look up structure for variable typed - USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages - USE f2008funcs_module,only:cloneStruc ! used to "clone" data structures -- temporary replacement of the intrinsic allocate(a, source=b) - USE data_types,only:var_ilength,var_dlength ! data vectors with variable length dimension - USE data_types,only:var_info ! metadata structure - implicit none - ! --------------------------------------------------------------------------------------------- - ! input/output: data structures - class(*),intent(inout) :: dataStruct ! data structure - type(var_info),intent(in) :: metaStruct(:) ! metadata structure - ! input: snow layer indices - integer(i4b),intent(in) :: ix_divide ! index of the layer to divide - integer(i4b),intent(in) :: nSnow,nLayers ! number of snow layers, total number of layers - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! --------------------------------------------------------------------------------------------- - ! local variables - integer(i4b) :: ivar ! index of model variable - integer(i4b) :: ix_lower ! lower bound of the vector - integer(i4b) :: ix_upper ! upper bound of the vector - logical(lgt) :: stateVariable ! .true. if variable is a state variable - real(rkind),allocatable :: tempVec_rkind(:) ! temporary vector (double precision) - integer(i4b),allocatable :: tempVec_i4b(:) ! temporary vector (integer) - character(LEN=256) :: cmessage ! error message of downwind routine - ! --------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='addModelLayer/' - - ! ***** add a layer to each model variable - do ivar=1,size(metaStruct) - - ! define bounds - select case(metaStruct(ivar)%vartype) - case(iLookVarType%midSnow); ix_lower=1; ix_upper=nSnow - case(iLookVarType%midToto); ix_lower=1; ix_upper=nLayers - case(iLookVarType%ifcSnow); ix_lower=0; ix_upper=nSnow - case(iLookVarType%ifcToto); ix_lower=0; ix_upper=nLayers - case default; cycle - end select - - ! identify whether it is a state variable - select case(trim(metaStruct(ivar)%varname)) - case('mLayerDepth','mLayerTemp','mLayerVolFracIce','mLayerVolFracLiq'); stateVariable=.true. - case default; stateVariable=.false. - end select - - ! divide layers - select type(dataStruct) - - ! ** double precision - type is (var_dlength) - ! check allocated - if(.not.allocated(dataStruct%var(ivar)%dat))then; err=20; message='data vector is not allocated'; return; end if - ! assign the data vector to the temporary vector - call cloneStruc(tempVec_rkind, ix_lower, source=dataStruct%var(ivar)%dat, err=err, message=cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - ! reallocate space for the new vector - deallocate(dataStruct%var(ivar)%dat,stat=err) - if(err/=0)then; err=20; message='problem in attempt to deallocate memory for data vector'; return; end if - allocate(dataStruct%var(ivar)%dat(ix_lower:ix_upper+1),stat=err) - if(err/=0)then; err=20; message='problem in attempt to reallocate memory for data vector'; return; end if - ! populate the state vector - if(stateVariable)then - if(ix_upper > 0)then ! (only copy data if the vector exists -- can be a variable for snow, with no layers) - if(ix_divide > 0)then - dataStruct%var(ivar)%dat(1:ix_divide) = tempVec_rkind(1:ix_divide) ! copy data - dataStruct%var(ivar)%dat(ix_divide+1) = tempVec_rkind(ix_divide) ! repeat data for the sub-divided layer - end if - if(ix_upper > ix_divide) & - dataStruct%var(ivar)%dat(ix_divide+2:ix_upper+1) = tempVec_rkind(ix_divide+1:ix_upper) ! copy data - end if ! if the vector exists - ! not a state variable - else - dataStruct%var(ivar)%dat(:) = realMissing - end if - ! deallocate the temporary vector: strictly not necessary, but include to be safe - deallocate(tempVec_rkind,stat=err) - if(err/=0)then; err=20; message='problem deallocating temporary data vector'; return; end if - - ! ** integer - type is (var_ilength) - ! check allocated - if(.not.allocated(dataStruct%var(ivar)%dat))then; err=20; message='data vector is not allocated'; return; end if - ! assign the data vector to the temporary vector - call cloneStruc(tempVec_i4b, ix_lower, source=dataStruct%var(ivar)%dat, err=err, message=cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - ! reallocate space for the new vector - deallocate(dataStruct%var(ivar)%dat,stat=err) - if(err/=0)then; err=20; message='problem in attempt to deallocate memory for data vector'; return; end if - allocate(dataStruct%var(ivar)%dat(ix_lower:ix_upper+1),stat=err) - if(err/=0)then; err=20; message='problem in attempt to reallocate memory for data vector'; return; end if - ! populate the state vector - if(stateVariable)then - if(ix_upper > 0)then ! (only copy data if the vector exists -- can be a variable for snow, with no layers) - if(ix_divide > 0)then - dataStruct%var(ivar)%dat(1:ix_divide) = tempVec_i4b(1:ix_divide) ! copy data - dataStruct%var(ivar)%dat(ix_divide+1) = tempVec_i4b(ix_divide) ! repeat data for the sub-divided layer - end if - if(ix_upper > ix_divide) & - dataStruct%var(ivar)%dat(ix_divide+2:ix_upper+1) = tempVec_i4b(ix_divide+1:ix_upper) ! copy data - end if ! if the vector exists - ! not a state variable - else - dataStruct%var(ivar)%dat(:) = integerMissing - end if - ! deallocate the temporary vector: strictly not necessary, but include to be safe - deallocate(tempVec_i4b,stat=err) - if(err/=0)then; err=20; message='problem deallocating temporary data vector'; return; end if - - ! check that we found the data type - class default; err=20; message=trim(message)//'unable to identify the data type'; return - - end select ! dependence on data types - - end do ! looping through variables - - end subroutine addModelLayer - -end module layerDivideFida_module diff --git a/build/source/engine/nonlinSolvFida.f90 b/build/source/engine/nonlinSolvFida.f90 deleted file mode 100644 index 442a6f761..000000000 --- a/build/source/engine/nonlinSolvFida.f90 +++ /dev/null @@ -1,39 +0,0 @@ -module nonlinSolvFida_module - - - !======= Inclusions =========== - use, intrinsic :: iso_c_binding - use nrtype - - - - type nonlinSolvFn - procedure(sysFnFida), pointer, nopass :: fcn - end type nonlinSolvFn - - abstract interface - - integer(c_int) function sysFnFida(ycor, F, mem) result(ierr) bind(C,name='sysFnFida') - use, intrinsic :: iso_c_binding - use fida_mod ! Fortran interface to IDA - use fnvector_serial_mod ! Fortran interface to serial N_Vector - use fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix - use fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver - use fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix - use fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver - use fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver - use fsundials_matrix_mod ! Fortran interface to generic SUNMatrix - use fsundials_nvector_mod ! Fortran interface to generic N_Vector - use fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver - use fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver - use nrtype - use type4IDA - implicit none - type(N_Vector) :: ycor - type(N_Vector) :: F - type(c_ptr) :: mem - end function - - end interface - -end module nonlinSolvFida_module From 446ddb33f8369670cf466415851ec9ce9bb490cd Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 30 Jun 2021 00:44:40 -0600 Subject: [PATCH 0164/1472] renamed tolFida tol4IDA --- build/Makefile | 3 +- build/source/engine/computResidDAE.f90 | 2 - build/source/engine/printResidFida.f90 | 149 ------------------ build/source/engine/solveByIDA.f90 | 4 +- build/source/engine/systemSolvSundials.f90 | 4 +- .../engine/{tolFida.f90 => tol4IDA.f90} | 24 +-- 6 files changed, 17 insertions(+), 169 deletions(-) delete mode 100644 build/source/engine/printResidFida.f90 rename build/source/engine/{tolFida.f90 => tol4IDA.f90} (97%) diff --git a/build/Makefile b/build/Makefile index c8c864428..3e8829a72 100644 --- a/build/Makefile +++ b/build/Makefile @@ -222,11 +222,10 @@ SUMMA_SOLVER= \ summaSolve.f90 \ systemSolv.f90 \ type4IDA.f90 \ - tolFida.f90 \ + tol4IDA.f90 \ computEnthalpy.f90 \ computHeatCap.f90 \ computThermConduct.f90 \ - printResidFida.f90 \ computResidDAE.f90 \ eval8DAE.f90 \ evalDAE4IDA.f90 \ diff --git a/build/source/engine/computResidDAE.f90 b/build/source/engine/computResidDAE.f90 index da0a4acd7..790d417db 100644 --- a/build/source/engine/computResidDAE.f90 +++ b/build/source/engine/computResidDAE.f90 @@ -90,7 +90,6 @@ subroutine computResidDAE(& rVec, & ! intent(out): residual vector err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- -! use printResidFida_module,only:printResidFida implicit none ! input: model control integer(i4b),intent(in) :: nSnow ! number of snow layers @@ -244,7 +243,6 @@ subroutine computResidDAE(& ! print *, 'rVec = ', rVec(indx_data%var(iLookINDEX%ixMatOnly)%dat) ! check if(any(isNan(rVec)))then -! call printResidFida(nSnow, nSoil, nLayers, indx_data, rAdd, rVec) message=trim(message)//'we found NaN' err=20; return endif diff --git a/build/source/engine/printResidFida.f90 b/build/source/engine/printResidFida.f90 deleted file mode 100644 index 48ab47a71..000000000 --- a/build/source/engine/printResidFida.f90 +++ /dev/null @@ -1,149 +0,0 @@ - - -module printResidFida_module - -! data types -USE nrtype - -! derived types to define the data structures -USE data_types,only:& - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (rkind) - -! named variables -USE var_lookup,only:iLookPROG ! named variables for structure elements -USE var_lookup,only:iLookDIAG ! named variables for structure elements -USE var_lookup,only:iLookFLUX ! named variables for structure elements -USE var_lookup,only:iLookINDEX ! named variables for structure elements - -! access the global print flag -USE globalData,only:globalPrintFlag - -! access missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing real number - -! define access to state variables to print -USE globalData,only: iJac1 ! first layer of the Jacobian to print -USE globalData,only: iJac2 ! last layer of the Jacobian to print - -! domain types -USE globalData,only:iname_veg ! named variables for vegetation -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil - -! named variables to describe the state variable type -USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space -USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy -USE globalData,only:iname_watCanopy ! named variable defining the mass of water on the vegetation canopy -USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers -USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers -USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers -USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers -USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers - -! constants -USE multiconst,only:& - LH_fus, & ! latent heat of fusion (J kg-1) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water ! intrinsic density of liquid water (kg m-3) -! privacy -implicit none -private -public::printResidFida -contains - - ! ********************************************************************************************************** - ! public subroutine printResidFida: compute the residual vector - ! ********************************************************************************************************** - subroutine printResidFida(& - ! input: model control - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - ! input: data structures - indx_data, & ! intent(in): index data - ! output - rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec) ! intent(out): residual vector - - ! -------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input: model control - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - ! output - real(rkind),intent(in) :: rAdd(:) ! additional (sink) terms on the RHS of the state equation - real(qp),intent(in) :: rVec(:) ! NOTE: qp ! residual vector - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - ! -------------------------------------------------------------------------------------------------------------------------------- - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - ! -------------------------------------------------------------------------------------------------------------------------------- - ! link to the necessary variables for the residual computations - associate(& - ! number of state variables of a specific type - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain - ! model indices - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain - ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the soil subdomain - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of hydrology states in snow+soil domain - layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] named variables defining the type of layer in snow+soil domain - ) ! association to necessary variables for the residual computations - ! -------------------------------------------------------------------------------------------------------------------------------- - - - if(ixVegNrg/=integerMissing) print *, 'rAdd(ixVegNrg) = ', rAdd(ixVegNrg) - - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) - select case( layerType(iLayer) ) - case(iname_snow) - print *, 'rAdd( ixSnowSoilNrg(iLayer) ) = ', rAdd( ixSnowSoilNrg(iLayer) ) - case(iname_soil); print *, 'rAdd( ixSnowSoilNrg(iLayer) ) = ', rAdd( ixSnowSoilNrg(iLayer) ) - end select - end do - endif - - if(nSoilOnlyHyd>0)then - do concurrent (iLayer=1:nSoil,ixSoilOnlyHyd(iLayer)/=integerMissing) - print *, 'rAdd( ixSoilOnlyHyd(iLayer) ) = ', rAdd( ixSoilOnlyHyd(iLayer) ) - end do - endif - - if(ixCasNrg/=integerMissing) print *, 'rVec(ixCasNrg) = ', rVec(ixCasNrg) - if(ixVegNrg/=integerMissing) print *, 'rVec(ixVegNrg) = ', rVec(ixVegNrg) - if(ixVegHyd/=integerMissing)then - print *, 'rVec(ixVegHyd) = ', rVec(ixVegHyd) - endif - - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) - print *, 'rVec( ixSnowSoilNrg(iLayer) ) = ', rVec( ixSnowSoilNrg(iLayer) ) - end do - endif - - if(nSnowSoilHyd>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) - print *, 'rVec( ixSnowSoilHyd(iLayer) ) = ', rVec( ixSnowSoilHyd(iLayer) ) - end do - endif - - if(ixAqWat/=integerMissing) print *, ' rVec(ixAqWat) = ', rVec(ixAqWat) - - end associate - - end subroutine printResidFida - -end module printResidFida_module diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index ff9c2e122..d890307a7 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -150,7 +150,7 @@ subroutine solveByIDA( & USE allocspace_module,only:allocLocal ! allocate local data structures USE evalDAE4IDA_module,only:evalDAE4IDA ! DAE/ODE functions USE evalJac4IDA_module,only:evalJac4IDA ! system Jacobian - USE tolFida_module,only:computWeightFida + USE tol4IDA_module,only:computWeight4IDA USE eval8DAE_module,only:eval8DAE USE computEnthalpy_module,only:computEnthalpy USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy @@ -368,7 +368,7 @@ subroutine solveByIDA( & if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAInit'; return; endif ! set tolerances - retval = FIDAWFtolerances(ida_mem, c_funloc(computWeightFida)) + retval = FIDAWFtolerances(ida_mem, c_funloc(computWeight4IDA)) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAWFtolerances'; return; endif ! define the form of the matrix diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index a14b4d771..9bf96e5bc 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -134,7 +134,7 @@ subroutine systemSolvSundials(& USE summaSolve_module,only:summaSolve ! calculate the iteration increment, evaluate the new state, and refine if necessary USE getVectorz_module,only:getScaling ! get the scaling vectors USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy - USE tolFida_module,only:popTolFida + USE tol4IDA_module,only:popTol4IDA USE solveByIDA_module,only:solveByIDA ! use varExtrFida_module, only:countDiscontinuity use, intrinsic :: iso_c_binding @@ -400,7 +400,7 @@ subroutine systemSolvSundials(& endif ! get tolerance vectors - call popTolFida(& + call popTol4IDA(& ! input nState, & ! intent(in): number of desired state variables prog_data, & ! intent(in): model prognostic variables for a local HRU diff --git a/build/source/engine/tolFida.f90 b/build/source/engine/tol4IDA.f90 similarity index 97% rename from build/source/engine/tolFida.f90 rename to build/source/engine/tol4IDA.f90 index bafca7cf1..9731117f5 100644 --- a/build/source/engine/tolFida.f90 +++ b/build/source/engine/tol4IDA.f90 @@ -1,7 +1,7 @@ -module tolFida_module +module tol4IDA_module !======= Inclusions =========== @@ -65,21 +65,21 @@ module tolFida_module ! privacy implicit none private - public::computWeightFida - public::popTolFida + public::computWeight4IDA + public::popTol4IDA contains ! ********************************************************************************************************** - ! public function computWeightFida: compute w_i = 1 / ( rtol_i * y_i + atol_i ) + ! public function computWeight4IDA: compute w_i = 1 / ( rtol_i * y_i + atol_i ) ! ********************************************************************************************************** ! Return values: ! 0 = success, ! -1 = non-recoverable error, NaN or negative values ! ---------------------------------------------------------------- - integer(c_int) function computWeightFida(sunvec_y, sunvec_ewt, user_data) & - result(ierr) bind(C,name='computWeightFida') + integer(c_int) function computWeight4IDA(sunvec_y, sunvec_ewt, user_data) & + result(ierr) bind(C,name='computWeight4IDA') !======= Inclusions =========== use, intrinsic :: iso_c_binding @@ -122,13 +122,13 @@ integer(c_int) function computWeightFida(sunvec_y, sunvec_ewt, user_data) & ierr = 0 return - end function computWeightFida + end function computWeight4IDA ! ********************************************************************************************************** - ! public subroutine popTolFida: populate tolerances for state vectors + ! public subroutine popTol4IDA: populate tolerances for state vectors ! ********************************************************************************************************** - subroutine popTolFida(& + subroutine popTol4IDA(& ! input: data structures nState, & ! intent(in): number of desired state variables prog_data, & ! intent(in): model prognostic variables for a local HRU @@ -211,7 +211,7 @@ subroutine popTolFida(& ) ! end association with variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message='popTolFida/' + err=0; message='popTol4IDA/' ! ----- ! * initialize state vectors... @@ -285,7 +285,7 @@ subroutine popTolFida(& endif end associate fixedLength ! end association to variables in the data structure where vector length does not change - end subroutine popTolFida + end subroutine popTol4IDA -end module tolFida_module +end module tol4IDA_module From fdaa73c7a353d562fd5f67db8ac597eea9302f88 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 30 Jun 2021 00:53:59 -0600 Subject: [PATCH 0165/1472] more edit --- build/source/engine/volicePackFida.f90 | 144 ------------------------- 1 file changed, 144 deletions(-) delete mode 100644 build/source/engine/volicePackFida.f90 diff --git a/build/source/engine/volicePackFida.f90 b/build/source/engine/volicePackFida.f90 deleted file mode 100644 index 2859c3157..000000000 --- a/build/source/engine/volicePackFida.f90 +++ /dev/null @@ -1,144 +0,0 @@ -! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington -! -! This file is part of SUMMA -! -! For more information see: http://www.ral.ucar.edu/projects/summa -! -! This program is free software: you can redistribute it and/or modify -! it under the terms of the GNU General Public License as published by -! the Free Software Foundation, either version 3 of the License, or -! (at your option) any later version. -! -! This program is distributed in the hope that it will be useful, -! but WITHOUT ANY WARRANTY; without even the implied warranty of -! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -! GNU General Public License for more details. -! -! You should have received a copy of the GNU General Public License -! along with this program. If not, see . - -module volicePackFida_module - -! data types -USE nrtype - -! derived types to define the data structures -USE data_types,only:& - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - model_options ! defines the model decisions - -! named variables for snow and soil -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil - -! named variables for parent structures -USE var_lookup,only:iLookINDEX ! named variables for structure elements - -! physical constants -USE multiconst,only:& - Tfreeze, & ! freezing point (K) - LH_fus, & ! latent heat of fusion (J kg-1) - LH_vap, & ! latent heat of vaporization (J kg-1) - LH_sub, & ! latent heat of sublimation (J kg-1) - iden_air, & ! intrinsic density of air (kg m-3) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water ! intrinsic density of water (kg m-3) - -! privacy -implicit none -private -public::volicePackFida -public::newsnwfall - -contains - - - ! ************************************************************************************************ - ! public subroutine volicePackFida: combine and sub-divide layers if necessary) - ! ************************************************************************************************ - subroutine volicePackFida(& - ! input/output: model data structures - tooMuchMelt, & ! intent(in): flag to force merge of snow layers - model_decisions, & ! intent(in): model decisions - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): type of each layer - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output - modifiedLayers, & ! intent(out): flag to denote that layers were modified - err,message) ! intent(out): error control - ! ------------------------------------------------------------------------------------------------ - ! external subroutine - USE layerMerge_module,only:layerMerge ! merge snow layers if they are too thin - USE layerDivide_module,only:layerDivide ! sub-divide layers if they are too thick - implicit none - ! ------------------------------------------------------------------------------------------------ - ! input/output: model data structures - logical(lgt),intent(in) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(inout) :: indx_data ! type of each layer - type(var_dlength),intent(inout) :: prog_data ! model prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model flux variables - ! output - logical(lgt),intent(out) :: modifiedLayers ! flag to denote that we modified the layers - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ------------------------------------------------------------------------------------------------ - ! local variables - character(LEN=256) :: cmessage ! error message of downwind routine - logical(lgt) :: mergedLayers ! flag to denote that layers were merged - logical(lgt) :: divideLayer ! flag to denote that a layer was divided - ! initialize error control - err=0; message='volicePackFida/' - - ! divide snow layers if too thick - call layerDivide(& - ! input/output: model data structures - model_decisions, & ! intent(in): model decisions - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): type of each layer - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output - divideLayer, & ! intent(out): flag to denote that layers were modified - err,cmessage) ! intent(out): error control - if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if - - if(divideLayer) print *, 'divideLayer' - - ! merge snow layers if they are too thin - call layerMerge(& - ! input/output: model data structures - tooMuchMelt, & ! intent(in): flag to force merge of snow layers - model_decisions, & ! intent(in): model decisions - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): type of each layer - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output - mergedLayers, & ! intent(out): flag to denote that layers were modified - err,cmessage) ! intent(out): error control - if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if - - if(mergedLayers) print *, 'mergedLayers' - - ! update the number of layers - indx_data%var(iLookINDEX%nSnow)%dat(1) = count(indx_data%var(iLookINDEX%layerType)%dat==iname_snow) - indx_data%var(iLookINDEX%nSoil)%dat(1) = count(indx_data%var(iLookINDEX%layerType)%dat==iname_soil) - indx_data%var(iLookINDEX%nLayers)%dat(1) = indx_data%var(iLookINDEX%nSnow)%dat(1) + indx_data%var(iLookINDEX%nSoil)%dat(1) - - ! flag if layers were modified - modifiedLayers = (mergedLayers .or. divideLayer) - - end subroutine volicePackFida - - -end module volicePackFida_module From 7373aa31f7c29d7c17b4fa5c958eccddae0f6475 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 30 Jun 2021 09:56:31 -0600 Subject: [PATCH 0166/1472] compuFluxFida deleted --- build/Makefile | 1 - build/source/engine/computFluxFida.f90 | 916 ------------------------- 2 files changed, 917 deletions(-) delete mode 100644 build/source/engine/computFluxFida.f90 diff --git a/build/Makefile b/build/Makefile index 3e8829a72..6a4664198 100644 --- a/build/Makefile +++ b/build/Makefile @@ -215,7 +215,6 @@ SUMMA_SOLVER= \ bigAquifer.f90 \ soilCmpresFida.f90 \ computFlux.f90 \ - computFluxFida.f90 \ computResid.f90 \ computJacob.f90 \ eval8summa.f90 \ diff --git a/build/source/engine/computFluxFida.f90 b/build/source/engine/computFluxFida.f90 deleted file mode 100644 index 242335001..000000000 --- a/build/source/engine/computFluxFida.f90 +++ /dev/null @@ -1,916 +0,0 @@ -! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington -! -! This file is part of SUMMA -! -! For more information see: http://www.ral.ucar.edu/projects/summa -! -! This program is free software: you can redistribute it and/or modify -! it under the terms of the GNU General Public License as published by -! the Free Software Foundation, either version 3 of the License, or -! (at your option) any later version. -! -! This program is distributed in the hope that it will be useful, -! but WITHOUT ANY WARRANTY; without even the implied warranty of -! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -! GNU General Public License for more details. -! -! You should have received a copy of the GNU General Public License -! along with this program. If not, see . - -module computFluxFida_module - -! data types -USE nrtype - -! provide access to the derived types to define the data structures -USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - model_options ! defines the model decisions - -! indices that define elements of the data structures -USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure -USE var_lookup,only:iLookPARAM ! named variables for structure elements -USE var_lookup,only:iLookFORCE ! named variables for structure elements -USE var_lookup,only:iLookPROG ! named variables for structure elements -USE var_lookup,only:iLookINDEX ! named variables for structure elements -USE var_lookup,only:iLookDIAG ! named variables for structure elements -USE var_lookup,only:iLookFLUX ! named variables for structure elements -USE var_lookup,only:iLookDERIV ! named variables for structure elements - -! missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing real number - -! layer types -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil - -! access the global print flag -USE globalData,only:globalPrintFlag - -! control parameters -USE globalData,only:verySmall ! a very small number -USE globalData,only:veryBig ! a very big number -USE globalData,only:dx ! finite difference increment - -! constants -USE multiconst,only:& - gravity, & ! acceleration of gravity (m s-2) - Tfreeze, & ! temperature at freezing (K) - LH_fus, & ! latent heat of fusion (J kg-1) - LH_vap, & ! latent heat of vaporization (J kg-1) - LH_sub, & ! latent heat of sublimation (J kg-1) - Cp_air, & ! specific heat of air (J kg-1 K-1) - iden_air, & ! intrinsic density of air (kg m-3) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water ! intrinsic density of liquid water (kg m-3) - -! look-up values for the choice of groundwater representation (local-column, or single-basin) -USE mDecisions_module,only: & - localColumn, & ! separate groundwater representation in each local soil column - singleBasin ! single groundwater store over the entire basin - -! look-up values for the choice of groundwater parameterization -USE mDecisions_module,only: & - qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization - bigBucket, & ! a big bucket (lumped aquifer model) - noExplicit ! no explicit groundwater parameterization - -! look-up values for the form of Richards' equation -USE mDecisions_module,only: & - moisture, & ! moisture-based form of Richards' equation - mixdform ! mixed form of Richards' equation - -! look-up values for the choice of boundary conditions for hydrology -USE mDecisions_module,only: & - prescribedHead, & ! prescribed head (volumetric liquid water content for mixed form of Richards' eqn) - funcBottomHead, & ! function of matric head in the lower-most layer - freeDrainage, & ! free drainage - liquidFlux, & ! liquid water flux - zeroFlux ! zero flux - -implicit none -private -public::computFluxFida -public::soilCmpres -contains - - ! ********************************************************************************************************* - ! public subroutine computFluxFida: compute model fluxes - ! ********************************************************************************************************* - subroutine computFluxFida(& - ! input-output: model control - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to denote the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - drainageMeltPond, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) - ! input: state variables - scalarAquiferStorage, & ! intent(in) - mLayerVolFracIce, & ! intent(in) - mLayerVolFracLiq, & ! intent(in) - mLayerMatricHead, & ! intent(in) - scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) - scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) - mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) - mLayerMatricHeadLiqTrial, & ! intent(in): trial value for the liquid water matric potential in each soil layer (m) - scalarAquiferStorageTrial,& ! intent(in): trial value of storage of water in the aquifer (m) - ! input: diagnostic variables defining the liquid water and ice content - scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) - mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liquid water content in each snow and soil layer (-) - mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) - ! input: data structures - model_decisions, & ! intent(in): model decisions - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): index data - ! input-output: data structures - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: flux vector and baseflow derivatives - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - fluxVec, & ! intent(out): flux vector (mixed units) - ! output: error control - err,message) ! intent(out): error code and error message - ! provide access to flux subroutines - USE vegNrgFluxFida_module,only:vegNrgFluxFida ! compute energy fluxes over vegetation - USE ssdNrgFlux_module,only:ssdNrgFlux ! compute energy fluxes throughout the snow and soil subdomains - USE vegLiqFlux_module,only:vegLiqFlux ! compute liquid water fluxes through vegetation - USE snowLiqFlx_module,only:snowLiqFlxFida ! compute liquid water fluxes through snow - USE soilLiqFlx_module,only:soilLiqflx ! compute liquid water fluxes through soil - USE groundwatr_module,only:groundwatr ! compute the baseflow flux - USE bigAquifer_module,only:bigAquifer ! compute fluxes for the big aquifer - implicit none - ! --------------------------------------------------------------------------------------- - ! * dummy variables - ! --------------------------------------------------------------------------------------- - ! input-output: control - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - real(rkind),intent(in) :: drainageMeltPond ! drainage from the surface melt pond (kg m-2 s-1) - ! input: state variables - real(rkind),intent(in) :: scalarAquiferStorage - real(rkind),intent(in) :: mLayerVolFracIce(:) ! - real(rkind),intent(in) :: mLayerVolFracLiq(:) ! - real(rkind),intent(in) :: mLayerMatricHead(:) ! - real(rkind),intent(in) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind),intent(in) :: mLayerTempTrial(:) ! trial value for temperature of each snow/soil layer (K) - real(rkind),intent(in) :: mLayerMatricHeadLiqTrial(:) ! trial value for the liquid water matric potential (m) - real(rkind),intent(in) :: scalarAquiferStorageTrial ! trial value of aquifer storage (m) - ! input: diagnostic variables - real(rkind),intent(in) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) - real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) - ! input: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(var_i), intent(in) :: type_data ! type of vegetation and soil - type(var_d), intent(in) :: attr_data ! spatial attributes - type(var_dlength), intent(in) :: mpar_data ! model parameters - type(var_d), intent(in) :: forc_data ! model forcing data - type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin - type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_ilength), intent(in) :: indx_data ! indices defining model states and layers - ! input-output: data structures - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! input-output: flux vector and baseflow derivatives - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - real(rkind),intent(out) :: fluxVec(:) ! model flux vector (mixed units) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! --------------------------------------------------------------------------------------- - ! * local variables - ! --------------------------------------------------------------------------------------- - integer(i4b) :: local_ixGroundwater ! local index for groundwater representation - integer(i4b) :: iLayer ! index of model layers - logical(lgt) :: doVegNrgFlux ! flag to compute the energy flux over vegetation - real(rkind),dimension(nSoil) :: dHydCond_dMatric ! derivative in hydraulic conductivity w.r.t matric head (s-1) - character(LEN=256) :: cmessage ! error message of downwind routine - ! -------------------------------------------------------------- - ! initialize error control - err=0; message='computFluxFida/' - - ! ***** - ! (0) PRELIMINARIES... - ! ******************** - - ! get the necessary variables for the flux computations - associate(& - - ! model decisions - ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization - ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) - - ! domain boundary conditions - upperBoundTemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) - scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in): [dp] rainfall rate (kg m-2 s-1) - - ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - - ! indices of model state variables for the vegetation subdomain - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain - ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer - - ! indices of model state variables for the snow+soil domain - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] type of layer (iname_soil or iname_snow) - - ! number of state variables of a specific type - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain - nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain - nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain - - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - - ! derivatives - dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) - dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(in): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature - - ! number of flux calls - numFluxCalls => diag_data%var(iLookDIAG%numFluxCalls)%dat(1) ,& ! intent(out): [dp] number of flux calls (-) - - ! net fluxes over the vegetation domain - scalarCanairNetNrgFlux => flux_data%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the canopy air space (W m-2) - scalarCanopyNetNrgFlux => flux_data%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the vegetation canopy (W m-2) - scalarGroundNetNrgFlux => flux_data%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the ground surface (W m-2) - scalarCanopyNetLiqFlux => flux_data%var(iLookFLUX%scalarCanopyNetLiqFlux)%dat(1) ,& ! intent(out): [dp] net liquid water flux for the vegetation canopy (kg m-2 s-1) - - ! net fluxes over the snow+soil domain - mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) - mLayerLiqFluxSnow => flux_data%var(iLookFLUX%mLayerLiqFluxSnow)%dat ,& ! intent(out): [dp] net liquid water flux for each snow layer (s-1) - mLayerLiqFluxSoil => flux_data%var(iLookFLUX%mLayerLiqFluxSoil)%dat ,& ! intent(out): [dp] net liquid water flux for each soil layer (s-1) - - ! evaporative fluxes - scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(out): [dp] canopy transpiration (kg m-2 s-1) - scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(out): [dp] canopy evaporation/condensation (kg m-2 s-1) - scalarGroundEvaporation => flux_data%var(iLookFLUX%scalarGroundEvaporation)%dat(1) ,& ! intent(out): [dp] ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(out): [dp(:)] transpiration loss from each soil layer (m s-1) - - ! fluxes for the snow+soil domain - iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(out): [dp(0:)] vertical energy flux at the interface of snow and soil layers - iLayerLiqFluxSnow => flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at snow layer interfaces (-) - iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at soil layer interfaces (-) - mLayerHydCond => flux_data%var(iLookFLUX%mLayerHydCond)%dat ,& ! intent(out): [dp(:)] hydraulic conductivity in each soil layer (m s-1) - mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(out): [dp(:)] baseflow from each soil layer (m s-1) - scalarSnowDrainage => flux_data%var(iLookFLUX%scalarSnowDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the snow profile (m s-1) - scalarSoilDrainage => flux_data%var(iLookFLUX%scalarSoilDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the soil profile (m s-1) - scalarSoilBaseflow => flux_data%var(iLookFLUX%scalarSoilBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the soil profile (m s-1) - - ! infiltration - scalarInfilArea => diag_data%var(iLookDIAG%scalarInfilArea )%dat(1) ,& ! intent(out): [dp] fraction of unfrozen area where water can infiltrate (-) - scalarFrozenArea => diag_data%var(iLookDIAG%scalarFrozenArea )%dat(1) ,& ! intent(out): [dp] fraction of area that is considered impermeable due to soil ice (-) - scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1) ,& ! intent(out): [dp] soil control on infiltration, zero or one - scalarMaxInfilRate => flux_data%var(iLookFLUX%scalarMaxInfilRate)%dat(1) ,& ! intent(out): [dp] maximum infiltration rate (m s-1) - scalarInfiltration => flux_data%var(iLookFLUX%scalarInfiltration)%dat(1) ,& ! intent(out): [dp] infiltration of water into the soil profile (m s-1) - - ! boundary fluxes in the soil domain - scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(out): [dp] rain that reaches the ground without ever touching the canopy (kg m-2 s-1) - scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(out): [dp] drainage of liquid water from the vegetation canopy (kg m-2 s-1) - scalarRainPlusMelt => flux_data%var(iLookFLUX%scalarRainPlusMelt)%dat(1) ,& ! intent(out): [dp] rain plus melt (m s-1) - scalarSurfaceRunoff => flux_data%var(iLookFLUX%scalarSurfaceRunoff)%dat(1) ,& ! intent(out): [dp] surface runoff (m s-1) - scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1) ,& ! intent(out): [dp] exfiltration from the soil profile (m s-1) - mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat ,& ! intent(out): [dp(:)] column outflow from each soil layer (m3 s-1) - - ! fluxes for the aquifer - scalarAquiferTranspire => flux_data%var(iLookFLUX%scalarAquiferTranspire)%dat(1) ,& ! intent(out): [dp] transpiration loss from the aquifer (m s-1 - scalarAquiferRecharge => flux_data%var(iLookFLUX%scalarAquiferRecharge)%dat(1) ,& ! intent(out): [dp] recharge to the aquifer (m s-1) - scalarAquiferBaseflow => flux_data%var(iLookFLUX%scalarAquiferBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the aquifer (m s-1) - - ! total runoff - scalarTotalRunoff => flux_data%var(iLookFLUX%scalarTotalRunoff)%dat(1) ,& ! intent(out): [dp] total runoff (m s-1) - - ! derivatives in net vegetation energy fluxes w.r.t. relevant state variables - dCanairNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy air temperature - dCanairNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy temperature - dCanairNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. ground temperature - dCanopyNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy air temperature - dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy temperature - dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. ground temperature - dCanopyNetFlux_dCanLiq => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in net canopy fluxes w.r.t. canopy liquid water content - dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy air temperature - dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy temperature - dGroundNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. ground temperature - dGroundNetFlux_dCanLiq => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in net ground fluxes w.r.t. canopy liquid water content - - ! derivatives in evaporative fluxes w.r.t. relevant state variables - dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy air temperature - dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy temperature - dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. ground temperature - dCanopyEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy liquid water content - dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy air temperature - dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy temperature - dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. ground temperature - dGroundEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy liquid water content - - ! derivatives in canopy water w.r.t canopy temperature - dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(out): [dp] derivative of canopy liquid storage w.r.t. temperature - - ! derivatives in canopy liquid fluxes w.r.t. canopy water - scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ,& ! intent(out): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water - scalarThroughfallRainDeriv => deriv_data%var(iLookDERIV%scalarThroughfallRainDeriv )%dat(1) ,& ! intent(out): [dp] derivative in throughfall w.r.t. canopy liquid water - scalarCanopyLiqDrainageDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDrainageDeriv)%dat(1) ,& ! intent(out): [dp] derivative in canopy drainage w.r.t. canopy liquid water - - ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below - dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above - dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below - - ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above - iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(out): [dp(:)] derivative in vertical liquid water flux at layer interfaces - - ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential - dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above - dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below - mLayerdTheta_dPsi => deriv_data%var(iLookDERIV%mLayerdTheta_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. psi - mLayerdPsi_dTheta => deriv_data%var(iLookDERIV%mLayerdPsi_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. theta - dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in compressibility w.r.t matric head - - ! derivative in baseflow flux w.r.t. aquifer storage - dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] erivative in baseflow flux w.r.t. aquifer storage (s-1) - - ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables - dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above - dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat & ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below - - ) ! association to data in structures - - ! ***** - ! * PRELIMINARIES... - ! ****************** - - !print*, '***** nSnowSoilNrg, nSnowOnlyNrg, nSoilOnlyNrg, nSnowSoilHyd, nSnowOnlyHyd, nSoilOnlyHyd = ', & - ! nSnowSoilNrg, nSnowOnlyNrg, nSoilOnlyNrg, nSnowSoilHyd, nSnowOnlyHyd, nSoilOnlyHyd - - ! increment the number of flux calls - numFluxCalls = numFluxCalls+1 - - ! modify the groundwater representation for this single-column implementation - select case(ixSpatialGroundwater) - case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale - case(localColumn); local_ixGroundwater = ixGroundwater ! go with the specified decision - case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return - end select ! (modify the groundwater representation for this single-column implementation) - - ! initialize liquid water fluxes throughout the snow and soil domains - ! NOTE: used in the energy routines, which is called before the hydrology routines - if(firstFluxCall)then - if(nSnow > 0) iLayerLiqFluxSnow(0:nSnow) = 0._rkind - iLayerLiqFluxSoil(0:nSoil) = 0._rkind - end if - - ! ***** - ! * CALCULATE ENERGY FLUXES OVER VEGETATION... - ! ********************************************* - - ! identify the need to calculate the energy flux over vegetation - doVegNrgFlux = (ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing .or. ixTopNrg/=integerMissing) - - ! check if there is a need to calculate the energy fluxes over vegetation - if(doVegNrgFlux)then - - ! derivative in canopy liquid storage w.r.t. canopy temperature - dCanLiq_dTcanopy = dTheta_dTkCanopy*iden_water*canopyDepth ! kg m-2 K-1 - - ! calculate the energy fluxes over vegetation - call vegNrgFluxFida(& - ! input: model control - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - ! input: model state variables - upperBoundTemp, & ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature - scalarCanairTempTrial, & ! intent(in): trial value of the canopy air space temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - mLayerTempTrial(1), & ! intent(in): trial value of ground temperature (K) - scalarCanopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) - scalarAquiferStorage, & ! intent(in) - mLayerMatricHead, & ! intent(in) - mLayerVolFracLiq, & ! intent(in) - ! input: model derivatives - dCanLiq_dTcanopy, & ! intent(in): derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) - ! input/output: data structures - type_data, & ! intent(in): type of vegetation and soil - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): index data - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - bvar_data, & ! intent(in): model variables for the local basin - model_decisions, & ! intent(in): model decisions - ! output: liquid water fluxes associated with evaporation/transpiration - scalarCanopyTranspiration, & ! intent(out): canopy transpiration (kg m-2 s-1) - scalarCanopyEvaporation, & ! intent(out): canopy evaporation/condensation (kg m-2 s-1) - scalarGroundEvaporation, & ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - ! output: fluxes - scalarCanairNetNrgFlux, & ! intent(out): net energy flux for the canopy air space (W m-2) - scalarCanopyNetNrgFlux, & ! intent(out): net energy flux for the vegetation canopy (W m-2) - scalarGroundNetNrgFlux, & ! intent(out): net energy flux for the ground surface (W m-2) - ! output: flux derivatives - dCanairNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) - dCanairNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) - dCanairNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) - dCanopyNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) - dCanopyNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) - dCanopyNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - dGroundNetFlux_dCanairTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) - dGroundNetFlux_dCanopyTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) - dGroundNetFlux_dGroundTemp, & ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - ! output: liquid water flux derivarives (canopy evap) - dCanopyEvaporation_dCanLiq, & ! intent(out): derivative in canopy evaporation w.r.t. canopy liquid water content (s-1) - dCanopyEvaporation_dTCanair, & ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTCanopy, & ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTGround, & ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - ! output: liquid water flux derivarives (ground evap) - dGroundEvaporation_dCanLiq, & ! intent(out): derivative in ground evaporation w.r.t. canopy liquid water content (s-1) - dGroundEvaporation_dTCanair, & ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTCanopy, & ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTGround, & ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - ! output: cross derivative terms - dCanopyNetFlux_dCanLiq, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dGroundNetFlux_dCanLiq, & ! intent(out): derivative in net ground fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - ! check fluxes - if(globalPrintFlag)then - print*, '**' - write(*,'(a,1x,10(f30.20))') 'canopyDepth = ', canopyDepth - write(*,'(a,1x,10(f30.20))') 'mLayerDepth(1:2) = ', mLayerDepth(1:2) - write(*,'(a,1x,10(f30.20))') 'scalarCanairTempTrial = ', scalarCanairTempTrial ! trial value of the canopy air space temperature (K) - write(*,'(a,1x,10(f30.20))') 'scalarCanopyTempTrial = ', scalarCanopyTempTrial ! trial value of canopy temperature (K) - write(*,'(a,1x,10(f30.20))') 'mLayerTempTrial(1:2) = ', mLayerTempTrial(1:2) ! trial value of ground temperature (K) - write(*,'(a,1x,10(f30.20))') 'scalarCanairNetNrgFlux = ', scalarCanairNetNrgFlux - write(*,'(a,1x,10(f30.20))') 'scalarCanopyNetNrgFlux = ', scalarCanopyNetNrgFlux - write(*,'(a,1x,10(f30.20))') 'scalarGroundNetNrgFlux = ', scalarGroundNetNrgFlux - write(*,'(a,1x,10(f30.20))') 'dGroundNetFlux_dGroundTemp = ', dGroundNetFlux_dGroundTemp - endif ! if checking fluxes - - endif ! if calculating the energy fluxes over vegetation - - ! ***** - ! * CALCULATE ENERGY FLUXES THROUGH THE SNOW-SOIL DOMAIN... - ! ********************************************************** - - ! check the need to compute energy fluxes throughout the snow+soil domain - if(nSnowSoilNrg>0)then - - ! calculate energy fluxes at layer interfaces through the snow and soil domain - call ssdNrgFlux(& - ! input: model control - (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution - ! input: fluxes and derivatives at the upper boundary - scalarGroundNetNrgFlux, & ! intent(in): total flux at the ground surface (W m-2) - dGroundNetFlux_dGroundTemp, & ! intent(in): derivative in total ground surface flux w.r.t. ground temperature (W m-2 K-1) - ! input: liquid water fluxes throughout the snow and soil domains - iLayerLiqFluxSnow, & ! intent(in): liquid flux at the interface of each snow layer (m s-1) - iLayerLiqFluxSoil, & ! intent(in): liquid flux at the interface of each soil layer (m s-1) - ! input: trial value of model state variabes - mLayerTempTrial, & ! intent(in): trial temperature at the current iteration (K) - ! input-output: data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model indices - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output: fluxes and derivatives at all layer interfaces - iLayerNrgFlux, & ! intent(out): energy flux at the layer interfaces (W m-2) - dNrgFlux_dTempAbove, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (W m-2 K-1) - dNrgFlux_dTempBelow, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (W m-2 K-1) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! calculate net energy fluxes for each snow and soil layer (J m-3 s-1) - do iLayer=1,nLayers - mLayerNrgFlux(iLayer) = -(iLayerNrgFlux(iLayer) - iLayerNrgFlux(iLayer-1))/mLayerDepth(iLayer) - if(globalPrintFlag)then - if(iLayer < 10) write(*,'(a,1x,i4,1x,10(f25.15,1x))') 'iLayer, iLayerNrgFlux(iLayer-1:iLayer), mLayerNrgFlux(iLayer) = ', iLayer, iLayerNrgFlux(iLayer-1:iLayer), mLayerNrgFlux(iLayer) - endif - end do - - endif ! if computing energy fluxes throughout the snow+soil domain - - - ! ***** - ! * CALCULATE THE LIQUID FLUX THROUGH VEGETATION... - ! ************************************************** - - ! check the need to compute the liquid water fluxes through vegetation - if(ixVegHyd/=integerMissing)then - - ! calculate liquid water fluxes through vegetation - call vegLiqFlux(& - ! input - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation - scalarCanopyLiqTrial, & ! intent(in): trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) - scalarRainfall, & ! intent(in): rainfall rate (kg m-2 s-1) - ! input-output: data structures - mpar_data, & ! intent(in): model parameters - diag_data, & ! intent(in): local HRU diagnostic model variables - ! output - scalarThroughfallRain, & ! intent(out): rain that reaches the ground without ever touching the canopy (kg m-2 s-1) - scalarCanopyLiqDrainage, & ! intent(out): drainage of liquid water from the vegetation canopy (kg m-2 s-1) - scalarThroughfallRainDeriv, & ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) - scalarCanopyLiqDrainageDeriv, & ! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! calculate the net liquid water flux for the vegetation canopy - scalarCanopyNetLiqFlux = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage - - ! calculate the total derivative in the downward liquid flux - scalarCanopyLiqDeriv = scalarThroughfallRainDeriv + scalarCanopyLiqDrainageDeriv - - ! test - if(globalPrintFlag)then - print*, '**' - print*, 'scalarRainfall = ', scalarRainfall - print*, 'scalarThroughfallRain = ', scalarThroughfallRain - print*, 'scalarCanopyEvaporation = ', scalarCanopyEvaporation - print*, 'scalarCanopyLiqDrainage = ', scalarCanopyLiqDrainage - print*, 'scalarCanopyNetLiqFlux = ', scalarCanopyNetLiqFlux - print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial - endif - - endif ! computing the liquid water fluxes through vegetation - - ! ***** - ! * CALCULATE THE LIQUID FLUX THROUGH SNOW... - ! ******************************************** - - ! check the need to compute liquid water fluxes through snow - if(nSnowOnlyHyd>0)then - - ! compute liquid fluxes through snow - call snowLiqFlxFida(& - ! input: model control - nSnow, & ! intent(in): number of snow layers - firstFluxCall, & ! intent(in): the first flux call (compute variables that are constant over the iterations) - (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution - ! input: forcing for the snow domain - scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) - scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) - ! input: model state vector - mLayerVolFracIce(1:nSnow), & ! intent(in) - mLayerVolFracLiqTrial(1:nSnow), & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) - ! input-output: data structures - indx_data, & ! intent(in): model indices - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output: fluxes and derivatives - iLayerLiqFluxSnow(0:nSnow), & ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) - iLayerLiqFluxSnowDeriv(0:nSnow), & ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! define forcing for the soil domain - scalarRainPlusMelt = iLayerLiqFluxSnow(nSnow) ! drainage from the base of the snowpack - - ! calculate net liquid water fluxes for each soil layer (s-1) - do iLayer=1,nSnow - mLayerLiqFluxSnow(iLayer) = -(iLayerLiqFluxSnow(iLayer) - iLayerLiqFluxSnow(iLayer-1))/mLayerDepth(iLayer) - !write(*,'(a,1x,i4,1x,2(f16.10,1x))') 'iLayer, mLayerLiqFluxSnow(iLayer), iLayerLiqFluxSnow(iLayer-1) = ', & - ! iLayer, mLayerLiqFluxSnow(iLayer), iLayerLiqFluxSnow(iLayer-1) - end do - - ! compute drainage from the soil zone (needed for mass balance checks) - scalarSnowDrainage = iLayerLiqFluxSnow(nSnow) - - else - - ! define forcing for the soil domain for the case of no snow layers - ! NOTE: in case where nSnowOnlyHyd==0 AND snow layers exist, then scalarRainPlusMelt is taken from the previous flux evaluation - if(nSnow==0)then - scalarRainPlusMelt = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water & ! liquid flux from the canopy (m s-1) - + drainageMeltPond/iden_water ! melt of the snow without a layer (m s-1) - endif ! if no snow layers - - endif - - ! ***** - ! * CALCULATE THE LIQUID FLUX THROUGH SOIL... - ! ******************************************** - - ! check the need to calculate the liquid flux through soil - if(nSoilOnlyHyd>0)then - - ! calculate the liquid flux through soil - call soilLiqFlx(& - ! input: model control - nSoil, & ! intent(in): number of soil layers - firstSplitOper, & ! intent(in): flag indicating first flux call in a splitting operation - (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution - .true., & ! intent(in): flag indicating if derivatives are desired - ! input: trial state variables - mLayerTempTrial(nSnow+1:nLayers), & ! intent(in): trial temperature at the current iteration (K) - mLayerMatricHeadLiqTrial(1:nSoil), & ! intent(in): liquid water matric potential (m) - mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of ice (-) - ! input: pre-computed deriavatives - mLayerdTheta_dTk(nSnow+1:nLayers), & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - dPsiLiq_dTemp(1:nSoil), & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) - ! input: fluxes - scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) - scalarGroundEvaporation, & ! intent(in): ground evaporation (kg m-2 s-1) - scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) - ! input-output: data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model indices - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output: diagnostic variables for surface runoff - scalarMaxInfilRate, & ! intent(inout): maximum infiltration rate (m s-1) - scalarInfilArea, & ! intent(inout): fraction of unfrozen area where water can infiltrate (-) - scalarFrozenArea, & ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) - scalarSurfaceRunoff, & ! intent(inout): surface runoff (m s-1) - ! output: diagnostic variables for model layers - mLayerdTheta_dPsi, & ! intent(inout): derivative in the soil water characteristic w.r.t. psi (m-1) - mLayerdPsi_dTheta, & ! intent(inout): derivative in the soil water characteristic w.r.t. theta (m) - dHydCond_dMatric, & ! intent(inout): derivative in hydraulic conductivity w.r.t matric head (s-1) - ! output: fluxes - scalarInfiltration, & ! intent(inout): surface infiltration rate (m s-1) -- controls on infiltration only computed for iter==1 - iLayerLiqFluxSoil, & ! intent(inout): liquid fluxes at layer interfaces (m s-1) - mLayerTranspire, & ! intent(inout): transpiration loss from each soil layer (m s-1) - mLayerHydCond, & ! intent(inout): hydraulic conductivity in each layer (m s-1) - ! output: derivatives in fluxes w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below (m s-1 or s-1) - dq_dHydStateAbove, & ! intent(inout): derivatives in the flux w.r.t. matric head in the layer above (s-1) - dq_dHydStateBelow, & ! intent(inout): derivatives in the flux w.r.t. matric head in the layer below (s-1) - ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) - dq_dNrgStateAbove, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) - dq_dNrgStateBelow, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! calculate net liquid water fluxes for each soil layer (s-1) - do iLayer=1,nSoil - mLayerLiqFluxSoil(iLayer) = -(iLayerLiqFluxSoil(iLayer) - iLayerLiqFluxSoil(iLayer-1))/mLayerDepth(iLayer+nSnow) - !if(iLayer<8) write(*,'(a,1x,2(i4,1x),3(e20.10),f12.7)') 'iLayerLiqFluxSoil(iLayer-1), iLayerLiqFluxSoil(iLayer), mLayerLiqFluxSoil(iLayer) = ', iLayer-1, iLayer, & - ! iLayerLiqFluxSoil(iLayer-1), iLayerLiqFluxSoil(iLayer), mLayerLiqFluxSoil(iLayer), mLayerDepth(iLayer+nSnow) - end do - - ! calculate the soil control on infiltration - if(nSnow==0) then - ! * case of infiltration into soil - if(scalarMaxInfilRate > scalarRainPlusMelt)then ! infiltration is not rate-limited - scalarSoilControl = (1._rkind - scalarFrozenArea)*scalarInfilArea - else - scalarSoilControl = 0._rkind ! (scalarRainPlusMelt exceeds maximum infiltration rate - endif - else - ! * case of infiltration into snow - scalarSoilControl = 1._rkind - endif - - ! compute drainage from the soil zone (needed for mass balance checks) - scalarSoilDrainage = iLayerLiqFluxSoil(nSoil) - - ! expand derivatives to the total water matric potential - ! NOTE: arrays are offset because computing derivatives in interface fluxes, at the top and bottom of the layer respectively - if(globalPrintFlag) print*, 'dPsiLiq_dPsi0(1:nSoil) = ', dPsiLiq_dPsi0(1:nSoil) - dq_dHydStateAbove(1:nSoil) = dq_dHydStateAbove(1:nSoil) *dPsiLiq_dPsi0(1:nSoil) - dq_dHydStateBelow(0:nSoil-1) = dq_dHydStateBelow(0:nSoil-1)*dPsiLiq_dPsi0(1:nSoil) - - endif ! if calculating the liquid flux through soil - - ! ***** - ! * CALCULATE THE GROUNDWATER FLOW... - ! ************************************ - - ! check if computing soil hydrology - if(nSoilOnlyHyd>0)then - - ! set baseflow fluxes to zero if the baseflow routine is not used - if(local_ixGroundwater/=qbaseTopmodel)then - ! (diagnostic variables in the data structures) - scalarExfiltration = 0._rkind ! exfiltration from the soil profile (m s-1) - mLayerColumnOutflow(:) = 0._rkind ! column outflow from each soil layer (m3 s-1) - ! (variables needed for the numerical solution) - mLayerBaseflow(:) = 0._rkind ! baseflow from each soil layer (m s-1) - - ! topmodel-ish shallow groundwater - else ! local_ixGroundwater==qbaseTopmodel - - ! check the derivative matrix is sized appropriately - if(size(dBaseflow_dMatric,1)/=nSoil .or. size(dBaseflow_dMatric,2)/=nSoil)then - message=trim(message)//'expect dBaseflow_dMatric to be nSoil x nSoil' - err=20; return - endif - - ! compute the baseflow flux - call groundwatr(& - ! input: model control - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - firstFluxCall, & ! intent(in): logical flag to compute index of the lowest saturated layer - ! input: state and diagnostic variables - mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. matric head - mLayerMatricHeadLiqTrial, & ! intent(in): liquid water matric potential (m) - mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of ice (-) - ! input: data structures - attr_data, & ! intent(in): model attributes - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output - ixSaturation, & ! intent(inout) index of lowest saturated layer (NOTE: only computed on the first iteration) - mLayerBaseflow, & ! intent(out): baseflow from each soil layer (m s-1) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - endif ! computing baseflow flux - - ! compute total baseflow from the soil zone (needed for mass balance checks) - scalarSoilBaseflow = sum(mLayerBaseflow) - - ! compute total runoff - scalarTotalRunoff = scalarSurfaceRunoff + scalarSoilDrainage + scalarSoilBaseflow - - endif ! if computing soil hydrology - - - ! ***** - ! (7) CALCULATE FLUXES FOR THE DEEP AQUIFER... - ! ******************************************** - - ! check if computing aquifer fluxes - if(ixAqWat/=integerMissing)then - - ! identify modeling decision - if(local_ixGroundwater==bigBucket)then - - ! compute fluxes for the big bucket - call bigAquifer(& - ! input: state variables and fluxes - scalarAquiferStorageTrial, & ! intent(in): trial value of aquifer storage (m) - scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) - scalarSoilDrainage, & ! intent(in): soil drainage (m s-1) - ! input: diagnostic variables and parameters - mpar_data, & ! intent(in): model parameter structure - diag_data, & ! intent(in): diagnostic variable structure - ! output: fluxes - scalarAquiferTranspire, & ! intent(out): transpiration loss from the aquifer (m s-1) - scalarAquiferRecharge, & ! intent(out): recharge to the aquifer (m s-1) - scalarAquiferBaseflow, & ! intent(out): total baseflow from the aquifer (m s-1) - dBaseflow_dAquifer, & ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! compute total runoff (overwrite previously calculated value before considering aquifer) - scalarTotalRunoff = scalarSurfaceRunoff + scalarAquiferBaseflow - - ! if no aquifer, then fluxes are zero - else - scalarAquiferTranspire = 0._rkind ! transpiration loss from the aquifer (m s-1) - scalarAquiferRecharge = 0._rkind ! recharge to the aquifer (m s-1) - scalarAquiferBaseflow = 0._rkind ! total baseflow from the aquifer (m s-1) - dBaseflow_dAquifer = 0._rkind ! change in baseflow flux w.r.t. aquifer storage (s-1) - end if ! no aquifer - - endif ! if computing aquifer fluxes - - ! ***** - ! (X) WRAP UP... - ! ************* - - ! define model flux vector for the vegetation sub-domain - if(ixCasNrg/=integerMissing) fluxVec(ixCasNrg) = scalarCanairNetNrgFlux/canopyDepth - if(ixVegNrg/=integerMissing) fluxVec(ixVegNrg) = scalarCanopyNetNrgFlux/canopyDepth - if(ixVegHyd/=integerMissing) fluxVec(ixVegHyd) = scalarCanopyNetLiqFlux ! NOTE: solid fluxes are handled separately - - ! populate the flux vector for energy - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - fluxVec( ixSnowSoilNrg(iLayer) ) = mLayerNrgFlux(iLayer) - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! populate the flux vector for hydrology - ! NOTE: ixVolFracWat and ixVolFracLiq can also include states in the soil domain, hence enable primary variable switching - if(nSnowSoilHyd>0)then ! check if any hydrology states exist - do iLayer=1,nLayers - if(ixSnowSoilHyd(iLayer)/=integerMissing)then ! check if a given hydrology state exists - select case( layerType(iLayer) ) - case(iname_snow); fluxVec( ixSnowSoilHyd(iLayer) ) = mLayerLiqFluxSnow(iLayer) - case(iname_soil); fluxVec( ixSnowSoilHyd(iLayer) ) = mLayerLiqFluxSoil(iLayer-nSnow) - case default; err=20; message=trim(message)//'expect layerType to be either iname_snow or iname_soil'; return - end select - endif ! if a given hydrology state exists - end do ! looping through non-missing energy state variables in the snow+soil domain - endif ! if any hydrology states exist - - ! compute the flux vector for the aquifer - if(ixAqWat/=integerMissing) fluxVec(ixAqWat) = scalarAquiferTranspire + scalarAquiferRecharge - scalarAquiferBaseflow - - ! set the first flux call to false - firstFluxCall=.false. - - ! end association to variables in the data structures - end associate - - end subroutine computFluxFida - - - ! ********************************************************************************************************** - ! public subroutine soilCmpres: compute soil compressibility (-) and its derivative w.r.t matric head (m-1) - ! ********************************************************************************************************** - subroutine soilCmpres(& - ! input: - ixRichards, & ! intent(in): choice of option for Richards' equation - ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers - mLayerMatricHead, & ! intent(in): matric head at the start of the time step (m) - mLayerMatricHeadTrial, & ! intent(in): trial value of matric head (m) - mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) - mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice content in each soil layer (-) - specificStorage, & ! intent(in): specific storage coefficient (m-1) - theta_sat, & ! intent(in): soil porosity (-) - ! output: - compress, & ! intent(out): compressibility of the soil matrix (-) - dCompress_dPsi, & ! intent(out): derivative in compressibility w.r.t. matric head (m-1) - err,message) ! intent(out): error code and error message - implicit none - ! input: - integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation - integer(i4b),intent(in) :: ixBeg,ixEnd ! start and end indices defining desired layers - real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head at the start of the time step (m) - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial value for matric head (m) - real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) - real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) - real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) - real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) - ! output: - real(rkind),intent(inout) :: compress(:) ! soil compressibility (-) - real(rkind),intent(inout) :: dCompress_dPsi(:) ! derivative in soil compressibility w.r.t. matric head (m-1) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! local variables - integer(i4b) :: iLayer ! index of soil layer - ! -------------------------------------------------------------- - ! initialize error control - err=0; message='soilCmpres/' - ! (only compute for the mixed form of Richards' equation) - if(ixRichards==mixdform)then - do iLayer=1,size(mLayerMatricHead) - if(iLayer>=ixBeg .and. iLayer<=ixEnd)then - ! compute the derivative for the compressibility term (m-1) - dCompress_dPsi(iLayer) = specificStorage*(mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer))/theta_sat(iLayer) - ! compute the compressibility term (-) - compress(iLayer) = (mLayerMatricHeadTrial(iLayer) - mLayerMatricHead(iLayer))*dCompress_dPsi(iLayer) - endif - end do - else - compress(:) = 0._rkind - dCompress_dPsi(:) = 0._rkind - end if - end subroutine soilCmpres - -end module computFluxFida_module From 983b4c389f625c3f639465c370283fcdd59e1a73 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 30 Jun 2021 10:01:12 -0600 Subject: [PATCH 0167/1472] vegNrgFluxFida deleted --- build/Makefile | 1 - build/source/engine/vegNrgFluxFida.f90 | 3202 ------------------------ 2 files changed, 3203 deletions(-) delete mode 100644 build/source/engine/vegNrgFluxFida.f90 diff --git a/build/Makefile b/build/Makefile index 6a4664198..ad5f5d3d8 100644 --- a/build/Makefile +++ b/build/Makefile @@ -207,7 +207,6 @@ SUMMA_SOLVER= \ groundwatr.f90 \ vegSWavRad.f90 \ vegNrgFlux.f90 \ - vegNrgFluxFida.f90 \ ssdNrgFlux.f90 \ vegLiqFlux.f90 \ snowLiqFlx.f90 \ diff --git a/build/source/engine/vegNrgFluxFida.f90 b/build/source/engine/vegNrgFluxFida.f90 deleted file mode 100644 index 18c51a1a9..000000000 --- a/build/source/engine/vegNrgFluxFida.f90 +++ /dev/null @@ -1,3202 +0,0 @@ -! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington -! -! This file is part of SUMMA -! -! For more information see: http://www.ral.ucar.edu/projects/summa -! -! This program is free software: you can redistribute it and/or modify -! it under the terms of the GNU General Public License as published by -! the Free Software Foundation, either version 3 of the License, or -! (at your option) any later version. -! -! This program is distributed in the hope that it will be useful, -! but WITHOUT ANY WARRANTY; without even the implied warranty of -! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -! GNU General Public License for more details. -! -! You should have received a copy of the GNU General Public License -! along with this program. If not, see . - -module vegNrgFluxFida_module - -! data types -USE nrtype - -! derived types to define the data structures -USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - model_options ! defines the model decisions - -! indices that define elements of the data structures -USE var_lookup,only:iLookTYPE ! named variables for structure elements -USE var_lookup,only:iLookPROG ! named variables for structure elements -USE var_lookup,only:iLookDIAG ! named variables for structure elements -USE var_lookup,only:iLookFLUX ! named variables for structure elements -USE var_lookup,only:iLookFORCE ! named variables for structure elements -USE var_lookup,only:iLookPARAM ! named variables for structure elements -USE var_lookup,only:iLookINDEX ! named variables for structure elements -USE var_lookup,only:iLookBVAR ! named variables for structure elements -USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure - -! constants -USE multiconst,only:gravity ! acceleration of gravity (m s-2) -USE multiconst,only:vkc ! von Karman's constant (-) -USE multiconst,only:w_ratio ! molecular ratio water to dry air (-) -USE multiconst,only:R_wv ! gas constant for water vapor (Pa K-1 m3 kg-1; J kg-1 K-1) -USE multiconst,only:Cp_air ! specific heat of air (J kg-1 K-1) -USE multiconst,only:Cp_ice ! specific heat of ice (J kg-1 K-1) -USE multiconst,only:Cp_soil ! specific heat of soil (J kg-1 K-1) -USE multiconst,only:Cp_water ! specific heat of liquid water (J kg-1 K-1) -USE multiconst,only:Tfreeze ! temperature at freezing (K) -USE multiconst,only:LH_fus ! latent heat of fusion (J kg-1) -USE multiconst,only:LH_vap ! latent heat of vaporization (J kg-1) -USE multiconst,only:LH_sub ! latent heat of sublimation (J kg-1) -USE multiconst,only:sb ! Stefan Boltzman constant (W m-2 K-4) -USE multiconst,only:iden_air ! intrinsic density of air (kg m-3) -USE multiconst,only:iden_ice ! intrinsic density of ice (kg m-3) -USE multiconst,only:iden_water ! intrinsic density of liquid water (kg m-3) - -! look-up values for method used to compute derivative -USE mDecisions_module,only: & - numerical, & ! numerical solution - analytical ! analytical solution - -! look-up values for choice of boundary conditions for thermodynamics -USE mDecisions_module,only: & - prescribedTemp, & ! prescribed temperature - energyFlux, & ! energy flux - zeroFlux ! zero flux - -! look-up values for the choice of parameterization for vegetation roughness length and displacement height -USE mDecisions_module,only: & - Raupach_BLM1994, & ! Raupach (BLM 1994) "Simplified expressions..." - CM_QJRMS1988, & ! Choudhury and Monteith (QJRMS 1988) "A four layer model for the heat budget..." - vegTypeTable ! constant parameters dependent on the vegetation type - -! look-up values for the choice of parameterization for canopy emissivity -USE mDecisions_module,only: & - simplExp, & ! simple exponential function - difTrans ! parameterized as a function of diffuse transmissivity - -! look-up values for the choice of canopy wind profile -USE mDecisions_module,only: & - exponential, & ! exponential wind profile extends to the surface - logBelowCanopy ! logarithmic profile below the vegetation canopy - -! look-up values for choice of stability function -USE mDecisions_module,only: & - standard, & ! standard MO similarity, a la Anderson (1976) - louisInversePower, & ! Louis (1979) inverse power function - mahrtExponential ! Mahrt (1987) exponential - -! look-up values for the choice of groundwater representation (local-column, or single-basin) -USE mDecisions_module,only: & - localColumn, & ! separate groundwater representation in each local soil column - singleBasin ! single groundwater store over the entire basin - -! ------------------------------------------------------------------------------------------------- -! privacy -implicit none -private -public::vegNrgFluxFida -! dimensions -integer(i4b),parameter :: nBands=2 ! number of spectral bands for shortwave radiation -! named variables -integer(i4b),parameter :: ist = 1 ! Surface type: IST=1 => soil; IST=2 => lake -integer(i4b),parameter :: isc = 4 ! Soil color type -integer(i4b),parameter :: ice = 0 ! Surface type: ICE=0 => soil; ICE=1 => sea-ice -! spatial indices -integer(i4b),parameter :: iLoc = 1 ! i-location -integer(i4b),parameter :: jLoc = 1 ! j-location -! algorithmic parameters -real(rkind),parameter :: missingValue=-9999._rkind ! missing value, used when diagnostic or state variables are undefined -real(rkind),parameter :: verySmall=1.e-6_rkind ! used as an additive constant to check if substantial difference among real numbers -real(rkind),parameter :: tinyVal=epsilon(1._rkind) ! used as an additive constant to check if substantial difference among real numbers -real(rkind),parameter :: mpe=1.e-6_rkind ! prevents overflow error if division by zero -real(rkind),parameter :: dx=1.e-11_rkind ! finite difference increment -! control -logical(lgt) :: printflag ! flag to turn on printing -contains - - ! ******************************************************************************************************* - ! public subroutine vegNrgFluxFida: muster program to compute energy fluxes at vegetation and ground surfaces - ! ******************************************************************************************************* - subroutine vegNrgFluxFida(& - ! input: model control - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - - ! input: model state variables - upperBoundTemp, & ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature - canairTempTrial, & ! intent(in): trial value of the canopy air space temperature (K) - canopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - groundTempTrial, & ! intent(in): trial value of ground temperature (K) - canopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) - canopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) - localAquiferStorage, & ! intent(in) - mLayerMatricHead, & ! intent(in) - mLayerVolFracLiq, & ! intent(in) - - ! input: model derivatives - dCanLiq_dTcanopy, & ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) - - ! input/output: data structures - type_data, & ! intent(in): type of vegetation and soil - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): state vector geometry - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - bvar_data, & ! intent(in): model variables for the local basin - model_decisions, & ! intent(in): model decisions - - ! output: liquid water fluxes associated with evaporation/transpiration (needed for coupling) - returnCanopyTranspiration, & ! intent(out): canopy transpiration (kg m-2 s-1) - returnCanopyEvaporation, & ! intent(out): canopy evaporation/condensation (kg m-2 s-1) - returnGroundEvaporation, & ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - - ! output: fluxes - canairNetFlux, & ! intent(out): net energy flux for the canopy air space (W m-2) - canopyNetFlux, & ! intent(out): net energy flux for the vegetation canopy (W m-2) - groundNetFlux, & ! intent(out): net energy flux for the ground surface (W m-2) - - ! output: energy flux derivatives - dCanairNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) - dCanairNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) - dCanairNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) - dCanopyNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) - dCanopyNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) - dCanopyNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - dGroundNetFlux_dCanairTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) - dGroundNetFlux_dCanopyTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) - dGroundNetFlux_dGroundTemp, & ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - - ! output liquid water flux derivarives (canopy evap) - dCanopyEvaporation_dCanLiq, & ! intent(out): derivative in canopy evaporation w.r.t. canopy liquid water content (s-1) - dCanopyEvaporation_dTCanair, & ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTCanopy, & ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTGround, & ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - - ! output: liquid water flux derivarives (ground evap) - dGroundEvaporation_dCanLiq, & ! intent(out): derivative in ground evaporation w.r.t. canopy liquid water content (s-1) - dGroundEvaporation_dTCanair, & ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTCanopy, & ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTGround, & ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - - ! output: cross derivative terms - dCanopyNetFlux_dCanLiq, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dGroundNetFlux_dCanLiq, & ! intent(out): derivative in net ground fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - - ! output: error control - err,message) ! intent(out): error control - - ! utilities - USE expIntegral_module,only:expInt ! function to calculate the exponential integral - ! conversion functions - USE conv_funcs_module,only:satVapPress ! function to compute the saturated vapor pressure (Pa) - USE conv_funcs_module,only:getLatentHeatValue ! function to identify latent heat of vaporization/sublimation (J kg-1) - ! stomatal resistance - USE stomResist_module,only:stomResist ! subroutine to calculate stomatal resistance - USE vegNrgFlux_module,only:wettedFrac - ! compute energy and mass fluxes for vegetation - implicit none - - ! --------------------------------------------------------------------------------------- - ! * dummy variables - ! --------------------------------------------------------------------------------------- - ! input: model control - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(in) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - - ! input: model state variables - real(rkind),intent(in) :: upperBoundTemp ! temperature of the upper boundary (K) --> NOTE: use air temperature - real(rkind),intent(in) :: canairTempTrial ! trial value of canopy air space temperature (K) - real(rkind),intent(in) :: canopyTempTrial ! trial value of canopy temperature (K) - real(rkind),intent(in) :: groundTempTrial ! trial value of ground temperature (K) - real(rkind),intent(in) :: canopyIceTrial ! trial value of mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: canopyLiqTrial ! trial value of mass of liquid water on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: localAquiferStorage - real(rkind),intent(in) :: mLayerMatricHead(:) ! - real(rkind),intent(in) :: mLayerVolFracLiq(:) - - ! input: model derivatives - real(rkind),intent(in) :: dCanLiq_dTcanopy ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) - - ! input/output: data structures - type(var_i),intent(in) :: type_data ! type of vegetation and soil - type(var_d),intent(in) :: forc_data ! model forcing data - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! state vector geometry - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin - type(model_options),intent(in) :: model_decisions(:) ! model decisions - - ! output: liquid water fluxes associated with evaporation/transpiration (needed for coupling) - real(rkind),intent(out) :: returnCanopyTranspiration ! canopy transpiration (kg m-2 s-1) - real(rkind),intent(out) :: returnCanopyEvaporation ! canopy evaporation/condensation (kg m-2 s-1) - real(rkind),intent(out) :: returnGroundEvaporation ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - - ! output: fluxes - real(rkind),intent(out) :: canairNetFlux ! net energy flux for the canopy air space (W m-2) - real(rkind),intent(out) :: canopyNetFlux ! net energy flux for the vegetation canopy (W m-2) - real(rkind),intent(out) :: groundNetFlux ! net energy flux for the ground surface (W m-2) - - ! output: energy flux derivatives - real(rkind),intent(out) :: dCanairNetFlux_dCanairTemp ! derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) - real(rkind),intent(out) :: dCanairNetFlux_dCanopyTemp ! derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dCanairNetFlux_dGroundTemp ! derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) - real(rkind),intent(out) :: dCanopyNetFlux_dCanairTemp ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) - real(rkind),intent(out) :: dCanopyNetFlux_dCanopyTemp ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dCanopyNetFlux_dGroundTemp ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - real(rkind),intent(out) :: dGroundNetFlux_dCanairTemp ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) - real(rkind),intent(out) :: dGroundNetFlux_dCanopyTemp ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - - ! output: liquid flux derivatives (canopy evap) - real(rkind),intent(out) :: dCanopyEvaporation_dCanLiq ! derivative in canopy evaporation w.r.t. canopy liquid water content (s-1) - real(rkind),intent(out) :: dCanopyEvaporation_dTCanair ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - real(rkind),intent(out) :: dCanopyEvaporation_dTCanopy ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - real(rkind),intent(out) :: dCanopyEvaporation_dTGround ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - - ! output: liquid flux derivatives (ground evap) - real(rkind),intent(out) :: dGroundEvaporation_dCanLiq ! derivative in ground evaporation w.r.t. canopy liquid water content (s-1) - real(rkind),intent(out) :: dGroundEvaporation_dTCanair ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - real(rkind),intent(out) :: dGroundEvaporation_dTCanopy ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - real(rkind),intent(out) :: dGroundEvaporation_dTGround ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - - ! output: cross derivative terms - real(rkind),intent(out) :: dCanopyNetFlux_dCanLiq ! derivative in net canopy fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - real(rkind),intent(out) :: dGroundNetFlux_dCanLiq ! derivative in net ground fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - - ! --------------------------------------------------------------------------------------- - ! * local variables - ! --------------------------------------------------------------------------------------- - ! local (general) - character(LEN=256) :: cmessage ! error message of downwind routine - real(rkind) :: VAI ! vegetation area index (m2 m-2) - real(rkind) :: exposedVAI ! exposed vegetation area index (m2 m-2) - real(rkind) :: totalCanopyWater ! total water on the vegetation canopy (kg m-2) - real(rkind) :: scalarAquiferStorage ! aquifer storage (m) - - ! local (compute numerical derivatives) - integer(i4b),parameter :: unperturbed=1 ! named variable to identify the case of unperturbed state variables - integer(i4b),parameter :: perturbStateGround=2 ! named variable to identify the case where we perturb the ground temperature - integer(i4b),parameter :: perturbStateCanopy=3 ! named variable to identify the case where we perturb the canopy temperature - integer(i4b),parameter :: perturbStateCanair=4 ! named variable to identify the case where we perturb the canopy air temperature - integer(i4b),parameter :: perturbStateCanLiq=5 ! named variable to identify the case where we perturb the canopy liquid water content - integer(i4b) :: itry ! index of flux evaluation - integer(i4b) :: nFlux ! number of flux evaluations - real(rkind) :: groundTemp ! value of ground temperature used in flux calculations (may be perturbed) - real(rkind) :: canopyTemp ! value of canopy temperature used in flux calculations (may be perturbed) - real(rkind) :: canairTemp ! value of canopy air temperature used in flux calculations (may be perturbed) - real(rkind) :: try0,try1 ! trial values to evaluate specific derivatives (testing only) - - ! local (saturation vapor pressure of veg) - real(rkind) :: TV_celcius ! vegetaion temperature (C) - real(rkind) :: TG_celcius ! ground temperature (C) - real(rkind) :: dSVPCanopy_dCanopyTemp ! derivative in canopy saturated vapor pressure w.r.t. vegetation temperature (Pa/K) - real(rkind) :: dSVPGround_dGroundTemp ! derivative in ground saturated vapor pressure w.r.t. ground temperature (Pa/K) - - ! local (wetted canopy area) - real(rkind) :: fracLiquidCanopy ! fraction of liquid water in the canopy (-) - real(rkind) :: canopyWetFraction ! trial value of the canopy wetted fraction (-) - real(rkind) :: dCanopyWetFraction_dWat ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) - real(rkind) :: dCanopyWetFraction_dT ! derivative in wetted fraction w.r.t. canopy temperature (K-1) - - ! local (longwave radiation) - real(rkind) :: expi ! exponential integral - real(rkind) :: scaleLAI ! scaled LAI (computing diffuse transmissivity) - real(rkind) :: diffuseTrans ! diffuse transmissivity (-) - real(rkind) :: groundEmissivity ! emissivity of the ground surface (-) - real(rkind),parameter :: vegEmissivity=0.98_rkind ! emissivity of vegetation (0.9665 in JULES) (-) - real(rkind),parameter :: soilEmissivity=0.98_rkind ! emmisivity of the soil (0.9665 in JULES) (-) - real(rkind),parameter :: snowEmissivity=0.99_rkind ! emissivity of snow (-) - real(rkind) :: dLWNetCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dLWNetGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dLWNetCanopy_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dLWNetGround_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) - - ! local (aerodynamic resistance) - real(rkind) :: scalarCanopyStabilityCorrection_old ! stability correction for the canopy (-) - real(rkind) :: scalarGroundStabilityCorrection_old ! stability correction for the ground surface (-) - - ! local (turbulent heat transfer) - real(rkind) :: z0Ground ! roughness length of the ground (ground below the canopy or non-vegetated surface) (m) - real(rkind) :: soilEvapFactor ! soil water control on evaporation from non-vegetated surfaces - real(rkind) :: soilRelHumidity_noSnow ! relative humidity in the soil pores [0-1] - real(rkind) :: scalarLeafConductance ! leaf conductance (m s-1) - real(rkind) :: scalarCanopyConductance ! canopy conductance (m s-1) - real(rkind) :: scalarGroundConductanceSH ! ground conductance for sensible heat (m s-1) - real(rkind) :: scalarGroundConductanceLH ! ground conductance for latent heat -- includes soil resistance (m s-1) - real(rkind) :: scalarEvapConductance ! conductance for evaporation (m s-1) - real(rkind) :: scalarTransConductance ! conductance for transpiration (m s-1) - real(rkind) :: scalarTotalConductanceSH ! total conductance for sensible heat (m s-1) - real(rkind) :: scalarTotalConductanceLH ! total conductance for latent heat (m s-1) - real(rkind) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - real(rkind) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - real(rkind) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - real(rkind) :: turbFluxCanair ! total turbulent heat fluxes exchanged at the canopy air space (W m-2) - real(rkind) :: turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) - real(rkind) :: turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) - - ! local (turbulent heat transfer -- compute numerical derivatives) - ! (temporary scalar resistances when states are perturbed) - real(rkind) :: trialLeafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) - real(rkind) :: trialGroundResistance ! below canopy aerodynamic resistance (s m-1) - real(rkind) :: trialCanopyResistance ! above canopy aerodynamic resistance (s m-1) - real(rkind) :: notUsed_RiBulkCanopy ! bulk Richardson number for the canopy (-) - real(rkind) :: notUsed_RiBulkGround ! bulk Richardson number for the ground surface (-) - real(rkind) :: notUsed_z0Canopy ! roughness length of the vegetation canopy (m) - real(rkind) :: notUsed_WindReductionFactor ! canopy wind reduction factor (-) - real(rkind) :: notUsed_ZeroPlaneDisplacement ! zero plane displacement (m) - real(rkind) :: notUsed_scalarCanopyStabilityCorrection ! stability correction for the canopy (-) - real(rkind) :: notUsed_scalarGroundStabilityCorrection ! stability correction for the ground surface (-) - real(rkind) :: notUsed_EddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) - real(rkind) :: notUsed_FrictionVelocity ! friction velocity (m s-1) - real(rkind) :: notUsed_WindspdCanopyTop ! windspeed at the top of the canopy (m s-1) - real(rkind) :: notUsed_WindspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) - real(rkind) :: notUsed_dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - real(rkind) :: notUsed_dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind) :: notUsed_dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - real(rkind) :: notUsed_dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind) :: notUsed_dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - - ! (fluxes after perturbations in model states -- canopy air space) - real(rkind) :: turbFluxCanair_dStateCanair ! turbulent exchange from the canopy air space to the atmosphere, after canopy air temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanair_dStateCanopy ! turbulent exchange from the canopy air space to the atmosphere, after canopy temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanair_dStateGround ! turbulent exchange from the canopy air space to the atmosphere, after ground temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanair_dStateCanliq ! turbulent exchange from the canopy air space to the atmosphere, after canopy liquid water content is perturbed (W m-2) - ! (fluxes after perturbations in model states -- vegetation canopy) - real(rkind) :: turbFluxCanopy_dStateCanair ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy air temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanopy_dStateCanopy ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanopy_dStateGround ! total turbulent heat fluxes from the canopy to the canopy air space, after ground temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanopy_dStateCanLiq ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy liquid water content is perturbed (W m-2) - - ! (fluxes after perturbations in model states -- ground surface) - real(rkind) :: turbFluxGround_dStateCanair ! total turbulent heat fluxes from the ground to the canopy air space, after canopy air temperature is perturbed (W m-2) - real(rkind) :: turbFluxGround_dStateCanopy ! total turbulent heat fluxes from the ground to the canopy air space, after canopy temperature is perturbed (W m-2) - real(rkind) :: turbFluxGround_dStateGround ! total turbulent heat fluxes from the ground to the canopy air space, after ground temperature is perturbed (W m-2) - real(rkind) :: turbFluxGround_dStateCanLiq ! total turbulent heat fluxes from the ground to the canopy air space, after canopy liquid water content is perturbed (W m-2) - - ! (fluxes after perturbations in model states -- canopy evaporation) - real(rkind) :: latHeatCanEvap_dStateCanair ! canopy evaporation after canopy air temperature is perturbed (W m-2) - real(rkind) :: latHeatCanEvap_dStateCanopy ! canopy evaporation after canopy temperature is perturbed (W m-2) - real(rkind) :: latHeatCanEvap_dStateGround ! canopy evaporation after ground temperature is perturbed (W m-2) - real(rkind) :: latHeatCanEvap_dStateCanLiq ! canopy evaporation after canopy liquid water content is perturbed (W m-2) - - ! (flux derivatives -- canopy air space) - real(rkind) :: dTurbFluxCanair_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanair_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanair_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanair_dCanLiq ! derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - - ! (flux derivatives -- vegetation canopy) - real(rkind) :: dTurbFluxCanopy_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanopy_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanopy_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanopy_dCanLiq ! derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - - ! (flux derivatives -- ground surface) - real(rkind) :: dTurbFluxGround_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dTurbFluxGround_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dTurbFluxGround_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dTurbFluxGround_dCanLiq ! derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - - ! (liquid water flux derivatives -- canopy evap) - real(rkind) :: dLatHeatCanopyEvap_dCanLiq ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (W kg-1) - real(rkind) :: dLatHeatCanopyEvap_dTCanair ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dLatHeatCanopyEvap_dTCanopy ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dLatHeatCanopyEvap_dTGround ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) - - ! (liquid water flux derivatives -- ground evap) - real(rkind) :: dLatHeatGroundEvap_dCanLiq ! derivative in latent heat of ground evaporation w.r.t. canopy liquid water content (J kg-1 s-1) - real(rkind) :: dLatHeatGroundEvap_dTCanair ! derivative in latent heat of ground evaporation w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) - - ! --------------------------------------------------------------------------------------- - ! point to variables in the data structure - ! --------------------------------------------------------------------------------------- - associate(& - - ! input: model decisions - ix_bcUpprTdyn => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision, & ! intent(in): [i4b] choice of upper boundary condition for thermodynamics - ix_fDerivMeth => model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): [i4b] choice of method to compute derivatives - ix_veg_traits => model_decisions(iLookDECISIONS%veg_traits)%iDecision, & ! intent(in): [i4b] choice of parameterization for vegetation roughness length and displacement height - ix_canopyEmis => model_decisions(iLookDECISIONS%canopyEmis)%iDecision, & ! intent(in): [i4b] choice of parameterization for canopy emissivity - ix_windPrfile => model_decisions(iLookDECISIONS%windPrfile)%iDecision, & ! intent(in): [i4b] choice of canopy wind profile - ix_astability => model_decisions(iLookDECISIONS%astability)%iDecision, & ! intent(in): [i4b] choice of stability function - ix_soilStress => model_decisions(iLookDECISIONS%soilStress)%iDecision, & ! intent(in): [i4b] choice of function for the soil moisture control on stomatal resistance - ix_groundwatr => model_decisions(iLookDECISIONS%groundwatr)%iDecision, & ! intent(in): [i4b] choice of groundwater parameterization - ix_stomResist => model_decisions(iLookDECISIONS%stomResist)%iDecision, & ! intent(in): [i4b] choice of function for stomatal resistance - ix_spatial_gw => model_decisions(iLookDECISIONS%spatial_gw)%iDecision, & ! intent(in): [i4b] choice of groundwater representation (local, basin) - - ! input: layer geometry - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1), & ! intent(in): [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): [i4b] total number of layers - - ! input: physical attributes - vegTypeIndex => type_data%var(iLookTYPE%vegTypeIndex), & ! intent(in): [i4b] vegetation type index - soilTypeIndex => type_data%var(iLookTYPE%soilTypeIndex), & ! intent(in): [i4b] soil type index - - ! input: vegetation parameters - heightCanopyTop => mpar_data%var(iLookPARAM%heightCanopyTop)%dat(1), & ! intent(in): [dp] height at the top of the vegetation canopy (m) - heightCanopyBottom => mpar_data%var(iLookPARAM%heightCanopyBottom)%dat(1), & ! intent(in): [dp] height at the bottom of the vegetation canopy (m) - canopyWettingFactor => mpar_data%var(iLookPARAM%canopyWettingFactor)%dat(1), & ! intent(in): [dp] maximum wetted fraction of the canopy (-) - canopyWettingExp => mpar_data%var(iLookPARAM%canopyWettingExp)%dat(1), & ! intent(in): [dp] exponent in canopy wetting function (-) - scalarCanopyIceMax => diag_data%var(iLookDIAG%scalarCanopyIceMax)%dat(1), & ! intent(in): [dp] maximum interception storage capacity for ice (kg m-2) - scalarCanopyLiqMax => diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1), & ! intent(in): [dp] maximum interception storage capacity for liquid water (kg m-2) - - ! input: vegetation phenology - scalarLAI => diag_data%var(iLookDIAG%scalarLAI)%dat(1), & ! intent(in): [dp] one-sided leaf area index (m2 m-2) - scalarSAI => diag_data%var(iLookDIAG%scalarSAI)%dat(1), & ! intent(in): [dp] one-sided stem area index (m2 m-2) - scalarExposedLAI => diag_data%var(iLookDIAG%scalarExposedLAI)%dat(1), & ! intent(in): [dp] exposed leaf area index after burial by snow (m2 m-2) - scalarExposedSAI => diag_data%var(iLookDIAG%scalarExposedSAI)%dat(1), & ! intent(in): [dp] exposed stem area index after burial by snow (m2 m-2) - scalarGrowingSeasonIndex => diag_data%var(iLookDIAG%scalarGrowingSeasonIndex)%dat(1), & ! intent(in): [dp] growing season index (0=off, 1=on) - scalarFoliageNitrogenFactor => diag_data%var(iLookDIAG%scalarFoliageNitrogenFactor)%dat(1), & ! intent(in): [dp] foliage nitrogen concentration (1.0 = saturated) - - ! input: aerodynamic resistance parameters - z0Snow => mpar_data%var(iLookPARAM%z0Snow)%dat(1), & ! intent(in): [dp] roughness length of snow (m) - z0Soil => mpar_data%var(iLookPARAM%z0Soil)%dat(1), & ! intent(in): [dp] roughness length of soil (m) - z0CanopyParam => mpar_data%var(iLookPARAM%z0Canopy)%dat(1), & ! intent(in): [dp] roughness length of the canopy (m) - zpdFraction => mpar_data%var(iLookPARAM%zpdFraction)%dat(1), & ! intent(in): [dp] zero plane displacement / canopy height (-) - critRichNumber => mpar_data%var(iLookPARAM%critRichNumber)%dat(1), & ! intent(in): [dp] critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam => mpar_data%var(iLookPARAM%Louis79_bparam)%dat(1), & ! intent(in): [dp] parameter in Louis (1979) stability function - Louis79_cStar => mpar_data%var(iLookPARAM%Louis79_cStar)%dat(1), & ! intent(in): [dp] parameter in Louis (1979) stability function - Mahrt87_eScale => mpar_data%var(iLookPARAM%Mahrt87_eScale)%dat(1), & ! intent(in): [dp] exponential scaling factor in the Mahrt (1987) stability function - windReductionParam => mpar_data%var(iLookPARAM%windReductionParam)%dat(1), & ! intent(in): [dp] canopy wind reduction parameter (-) - leafExchangeCoeff => mpar_data%var(iLookPARAM%leafExchangeCoeff)%dat(1), & ! intent(in): [dp] turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) - leafDimension => mpar_data%var(iLookPARAM%leafDimension)%dat(1), & ! intent(in): [dp] characteristic leaf dimension (m) - - ! input: soil stress parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(1), & ! intent(in): [dp] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(1), & ! intent(in): [dp] residual volumetric liquid water content (-) - plantWiltPsi => mpar_data%var(iLookPARAM%plantWiltPsi)%dat(1), & ! intent(in): [dp] matric head at wilting point (m) - soilStressParam => mpar_data%var(iLookPARAM%soilStressParam)%dat(1), & ! intent(in): [dp] parameter in the exponential soil stress function (-) - critSoilWilting => mpar_data%var(iLookPARAM%critSoilWilting)%dat(1), & ! intent(in): [dp] critical vol. liq. water content when plants are wilting (-) - critSoilTranspire => mpar_data%var(iLookPARAM%critSoilTranspire)%dat(1), & ! intent(in): [dp] critical vol. liq. water content when transpiration is limited (-) - critAquiferTranspire => mpar_data%var(iLookPARAM%critAquiferTranspire)%dat(1), & ! intent(in): [dp] critical aquifer storage value when transpiration is limited (m) - minStomatalResistance => mpar_data%var(iLookPARAM%minStomatalResistance)%dat(1), & ! intent(in): [dp] mimimum stomatal resistance (s m-1) - - ! input: forcing at the upper boundary - mHeight => diag_data%var(iLookDIAG%scalarAdjMeasHeight)%dat(1), & ! intent(in): [dp] measurement height (m) - airtemp => forc_data%var(iLookFORCE%airtemp), & ! intent(in): [dp] air temperature at some height above the surface (K) - windspd => forc_data%var(iLookFORCE%windspd), & ! intent(in): [dp] wind speed at some height above the surface (m s-1) - airpres => forc_data%var(iLookFORCE%airpres), & ! intent(in): [dp] air pressure at some height above the surface (Pa) - LWRadAtm => forc_data%var(iLookFORCE%LWRadAtm), & ! intent(in): [dp] downwelling longwave radiation at the upper boundary (W m-2) - scalarVPair => diag_data%var(iLookDIAG%scalarVPair)%dat(1), & ! intent(in): [dp] vapor pressure at some height above the surface (Pa) - scalarO2air => diag_data%var(iLookDIAG%scalarO2air)%dat(1), & ! intent(in): [dp] atmospheric o2 concentration (Pa) - scalarCO2air => diag_data%var(iLookDIAG%scalarCO2air)%dat(1), & ! intent(in): [dp] atmospheric co2 concentration (Pa) - scalarTwetbulb => diag_data%var(iLookDIAG%scalarTwetbulb)%dat(1), & ! intent(in): [dp] wetbulb temperature (K) - scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1), & ! intent(in): [dp] computed rainfall rate (kg m-2 s-1) - scalarSnowfall => flux_data%var(iLookFLUX%scalarSnowfall)%dat(1), & ! intent(in): [dp] computed snowfall rate (kg m-2 s-1) - scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1), & ! intent(in): [dp] rainfall through the vegetation canopy (kg m-2 s-1) - scalarThroughfallSnow => flux_data%var(iLookFLUX%scalarThroughfallSnow)%dat(1), & ! intent(in): [dp] snowfall through the vegetation canopy (kg m-2 s-1) - - ! input: water storage - ! NOTE: soil stress only computed at the start of the substep (firstFluxCall=.true.) - scalarSnowDepth => prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(in): [dp] snow depth on the ground surface (m) - basinAquiferStorage => bvar_data%var(iLookBVAR%basin__AquiferStorage)%dat(1), & ! intent(in): [dp] aquifer storage for the single basin (m) - - ! input: shortwave radiation fluxes - scalarCanopySunlitLAI => diag_data%var(iLookDIAG%scalarCanopySunlitLAI)%dat(1), & ! intent(in): [dp] sunlit leaf area (-) - scalarCanopyShadedLAI => diag_data%var(iLookDIAG%scalarCanopyShadedLAI)%dat(1), & ! intent(in): [dp] shaded leaf area (-) - scalarCanopySunlitPAR => flux_data%var(iLookFLUX%scalarCanopySunlitPAR)%dat(1), & ! intent(in): [dp] average absorbed par for sunlit leaves (w m-2) - scalarCanopyShadedPAR => flux_data%var(iLookFLUX%scalarCanopyShadedPAR)%dat(1), & ! intent(in): [dp] average absorbed par for shaded leaves (w m-2) - scalarCanopyAbsorbedSolar => flux_data%var(iLookFLUX%scalarCanopyAbsorbedSolar)%dat(1), & ! intent(in): [dp] solar radiation absorbed by canopy (W m-2) - scalarGroundAbsorbedSolar => flux_data%var(iLookFLUX%scalarGroundAbsorbedSolar)%dat(1), & ! intent(in): [dp] solar radiation absorbed by ground (W m-2) - - ! output: fraction of wetted canopy area and fraction of snow on the ground - scalarCanopyWetFraction => diag_data%var(iLookDIAG%scalarCanopyWetFraction)%dat(1), & ! intent(out): [dp] fraction of canopy that is wet - scalarGroundSnowFraction => diag_data%var(iLookDIAG%scalarGroundSnowFraction)%dat(1), & ! intent(out): [dp] fraction of ground covered with snow (-) - - ! output: longwave radiation fluxes - scalarCanopyEmissivity => diag_data%var(iLookDIAG%scalarCanopyEmissivity)%dat(1), & ! intent(out): [dp] effective emissivity of the canopy (-) - scalarLWRadCanopy => flux_data%var(iLookFLUX%scalarLWRadCanopy)%dat(1), & ! intent(out): [dp] longwave radiation emitted from the canopy (W m-2) - scalarLWRadGround => flux_data%var(iLookFLUX%scalarLWRadGround)%dat(1), & ! intent(out): [dp] longwave radiation emitted at the ground surface (W m-2) - scalarLWRadUbound2Canopy => flux_data%var(iLookFLUX%scalarLWRadUbound2Canopy)%dat(1), & ! intent(out): [dp] downward atmospheric longwave radiation absorbed by the canopy (W m-2) - scalarLWRadUbound2Ground => flux_data%var(iLookFLUX%scalarLWRadUbound2Ground)%dat(1), & ! intent(out): [dp] downward atmospheric longwave radiation absorbed by the ground (W m-2) - scalarLWRadUbound2Ubound => flux_data%var(iLookFLUX%scalarLWRadUbound2Ubound)%dat(1), & ! intent(out): [dp] atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) - scalarLWRadCanopy2Ubound => flux_data%var(iLookFLUX%scalarLWRadCanopy2Ubound)%dat(1), & ! intent(out): [dp] longwave radiation emitted from canopy lost thru upper boundary (W m-2) - scalarLWRadCanopy2Ground => flux_data%var(iLookFLUX%scalarLWRadCanopy2Ground)%dat(1), & ! intent(out): [dp] longwave radiation emitted from canopy absorbed by the ground (W m-2) - scalarLWRadCanopy2Canopy => flux_data%var(iLookFLUX%scalarLWRadCanopy2Canopy)%dat(1), & ! intent(out): [dp] canopy longwave reflected from ground and absorbed by the canopy (W m-2) - scalarLWRadGround2Ubound => flux_data%var(iLookFLUX%scalarLWRadGround2Ubound)%dat(1), & ! intent(out): [dp] longwave radiation emitted from ground lost thru upper boundary (W m-2) - scalarLWRadGround2Canopy => flux_data%var(iLookFLUX%scalarLWRadGround2Canopy)%dat(1), & ! intent(out): [dp] longwave radiation emitted from ground and absorbed by the canopy (W m-2) - scalarLWNetCanopy => flux_data%var(iLookFLUX%scalarLWNetCanopy)%dat(1), & ! intent(out): [dp] net longwave radiation at the canopy (W m-2) - scalarLWNetGround => flux_data%var(iLookFLUX%scalarLWNetGround)%dat(1), & ! intent(out): [dp] net longwave radiation at the ground surface (W m-2) - scalarLWNetUbound => flux_data%var(iLookFLUX%scalarLWNetUbound)%dat(1), & ! intent(out): [dp] net longwave radiation at the upper boundary (W m-2) - - ! output: aerodynamic resistance - scalarZ0Canopy => diag_data%var(iLookDIAG%scalarZ0Canopy)%dat(1), & ! intent(out): [dp] roughness length of the canopy (m) - scalarWindReductionFactor => diag_data%var(iLookDIAG%scalarWindReductionFactor)%dat(1), & ! intent(out): [dp] canopy wind reduction factor (-) - scalarZeroPlaneDisplacement => diag_data%var(iLookDIAG%scalarZeroPlaneDisplacement)%dat(1), & ! intent(out): [dp] zero plane displacement (m) - scalarRiBulkCanopy => diag_data%var(iLookDIAG%scalarRiBulkCanopy)%dat(1), & ! intent(out): [dp] bulk Richardson number for the canopy (-) - scalarRiBulkGround => diag_data%var(iLookDIAG%scalarRiBulkGround)%dat(1), & ! intent(out): [dp] bulk Richardson number for the ground surface (-) - scalarEddyDiffusCanopyTop => flux_data%var(iLookFLUX%scalarEddyDiffusCanopyTop)%dat(1), & ! intent(out): [dp] eddy diffusivity for heat at the top of the canopy (m2 s-1) - scalarFrictionVelocity => flux_data%var(iLookFLUX%scalarFrictionVelocity)%dat(1), & ! intent(out): [dp] friction velocity (m s-1) - scalarWindspdCanopyTop => flux_data%var(iLookFLUX%scalarWindspdCanopyTop)%dat(1), & ! intent(out): [dp] windspeed at the top of the canopy (m s-1) - scalarWindspdCanopyBottom => flux_data%var(iLookFLUX%scalarWindspdCanopyBottom)%dat(1), & ! intent(out): [dp] windspeed at the height of the bottom of the canopy (m s-1) - scalarLeafResistance => flux_data%var(iLookFLUX%scalarLeafResistance)%dat(1), & ! intent(out): [dp] mean leaf boundary layer resistance per unit leaf area (s m-1) - scalarGroundResistance => flux_data%var(iLookFLUX%scalarGroundResistance)%dat(1), & ! intent(out): [dp] below canopy aerodynamic resistance (s m-1) - scalarCanopyResistance => flux_data%var(iLookFLUX%scalarCanopyResistance)%dat(1), & ! intent(out): [dp] above canopy aerodynamic resistance (s m-1) - - ! input/output: soil resistance -- intent(in) and intent(inout) because only called at the first flux call - mLayerRootDensity => diag_data%var(iLookDIAG%mLayerRootDensity)%dat, & ! intent(in): [dp] root density in each layer (-) - scalarAquiferRootFrac => diag_data%var(iLookDIAG%scalarAquiferRootFrac)%dat(1), & ! intent(in): [dp] fraction of roots below the lowest soil layer (-) - scalarTranspireLim => diag_data%var(iLookDIAG%scalarTranspireLim)%dat(1), & ! intent(inout): [dp] weighted average of the transpiration limiting factor (-) - mLayerTranspireLim => diag_data%var(iLookDIAG%mLayerTranspireLim)%dat, & ! intent(inout): [dp] transpiration limiting factor in each layer (-) - scalarTranspireLimAqfr => diag_data%var(iLookDIAG%scalarTranspireLimAqfr)%dat(1), & ! intent(inout): [dp] transpiration limiting factor for the aquifer (-) - scalarSoilRelHumidity => diag_data%var(iLookDIAG%scalarSoilRelHumidity)%dat(1), & ! intent(inout): [dp] relative humidity in the soil pores [0-1] - scalarSoilResistance => flux_data%var(iLookFLUX%scalarSoilResistance)%dat(1), & ! intent(inout): [dp] resistance from the soil (s m-1) - - ! input/output: stomatal resistance -- intent(inout) because only called at the first flux call - scalarStomResistSunlit => flux_data%var(iLookFLUX%scalarStomResistSunlit)%dat(1), & ! intent(inout): [dp] stomatal resistance for sunlit leaves (s m-1) - scalarStomResistShaded => flux_data%var(iLookFLUX%scalarStomResistShaded)%dat(1), & ! intent(inout): [dp] stomatal resistance for shaded leaves (s m-1) - scalarPhotosynthesisSunlit => flux_data%var(iLookFLUX%scalarPhotosynthesisSunlit)%dat(1), & ! intent(inout): [dp] sunlit photosynthesis (umolco2 m-2 s-1) - scalarPhotosynthesisShaded => flux_data%var(iLookFLUX%scalarPhotosynthesisShaded)%dat(1), & ! intent(inout): [dp] shaded photosynthesis (umolco2 m-2 s-1) - - ! output: turbulent heat fluxes - scalarLatHeatSubVapCanopy => diag_data%var(iLookDIAG%scalarLatHeatSubVapCanopy)%dat(1), & ! intent(inout): [dp] latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) - scalarLatHeatSubVapGround => diag_data%var(iLookDIAG%scalarLatHeatSubVapGround)%dat(1), & ! intent(inout): [dp] latent heat of sublimation/vaporization for the ground surface (J kg-1) - scalarSatVP_canopyTemp => diag_data%var(iLookDIAG%scalarSatVP_CanopyTemp)%dat(1), & ! intent(out): [dp] saturation vapor pressure at the temperature of the vegetation canopy (Pa) - scalarSatVP_groundTemp => diag_data%var(iLookDIAG%scalarSatVP_GroundTemp)%dat(1), & ! intent(out): [dp] saturation vapor pressure at the temperature of the ground surface (Pa) - scalarSenHeatTotal => flux_data%var(iLookFLUX%scalarSenHeatTotal)%dat(1), & ! intent(out): [dp] sensible heat from the canopy air space to the atmosphere (W m-2) - scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1), & ! intent(out): [dp] sensible heat flux from the canopy to the canopy air space (W m-2) - scalarSenHeatGround => flux_data%var(iLookFLUX%scalarSenHeatGround)%dat(1), & ! intent(out): [dp] sensible heat flux from ground surface below vegetation (W m-2) - scalarLatHeatTotal => flux_data%var(iLookFLUX%scalarLatHeatTotal)%dat(1), & ! intent(out): [dp] latent heat from the canopy air space to the atmosphere (W m-2) - scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1), & ! intent(out): [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - scalarLatHeatCanopyTrans => flux_data%var(iLookFLUX%scalarLatHeatCanopyTrans)%dat(1), & ! intent(out): [dp] latent heat flux for transpiration from the canopy to the canopy air space (W m-2) - scalarLatHeatGround => flux_data%var(iLookFLUX%scalarLatHeatGround)%dat(1), & ! intent(out): [dp] latent heat flux from ground surface below vegetation (W m-2) - - ! output: advective heat fluxes - scalarCanopyAdvectiveHeatFlux => flux_data%var(iLookFLUX%scalarCanopyAdvectiveHeatFlux)%dat(1), & ! intent(out): [dp] heat advected to the canopy surface with rain + snow (W m-2) - scalarGroundAdvectiveHeatFlux => flux_data%var(iLookFLUX%scalarGroundAdvectiveHeatFlux)%dat(1), & ! intent(out): [dp] heat advected to the ground surface with throughfall (W m-2) - - ! output: mass fluxes - scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1), & ! intent(out): [dp] canopy sublimation/frost (kg m-2 s-1) - scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! intent(out): [dp] snow sublimation/frost -- below canopy or non-vegetated (kg m-2 s-1) - - ! input/output: canopy air space variables - scalarVP_CanopyAir => diag_data%var(iLookDIAG%scalarVP_CanopyAir)%dat(1), & ! intent(inout): [dp] vapor pressure of the canopy air space (Pa) - scalarCanopyStabilityCorrection => diag_data%var(iLookDIAG%scalarCanopyStabilityCorrection)%dat(1),& ! intent(inout): [dp] stability correction for the canopy (-) - scalarGroundStabilityCorrection => diag_data%var(iLookDIAG%scalarGroundStabilityCorrection)%dat(1),& ! intent(inout): [dp] stability correction for the ground surface (-) - - ! output: liquid water fluxes - scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1), & ! intent(out): [dp] canopy transpiration (kg m-2 s-1) - scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1), & ! intent(out): [dp] canopy evaporation/condensation (kg m-2 s-1) - scalarGroundEvaporation => flux_data%var(iLookFLUX%scalarGroundEvaporation)%dat(1), & ! intent(out): [dp] ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - - ! output: derived fluxes - scalarTotalET => flux_data%var(iLookFLUX%scalarTotalET)%dat(1), & ! intent(out): [dp] total ET (kg m-2 s-1) - scalarNetRadiation => flux_data%var(iLookFLUX%scalarNetRadiation)%dat(1) & ! intent(out): [dp] net radiation (W m-2) - ) - ! --------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="vegNrgFluxFida/" - - ! initialize printflag - printflag = .false. - - ! identify the type of boundary condition for thermodynamics - select case(ix_bcUpprTdyn) - - ! ***** - ! (1) DIRICHLET OR ZERO FLUX BOUNDARY CONDITION... - ! ************************************************ - - ! NOTE: Vegetation fluxes are not computed in this case - - ! ** prescribed temperature or zero flux at the upper boundary of the snow-soil system - case(prescribedTemp,zeroFlux) - - ! derived fluxes - scalarTotalET = 0._rkind ! total ET (kg m-2 s-1) - scalarNetRadiation = 0._rkind ! net radiation (W m-2) - ! liquid water fluxes associated with evaporation/transpiration - scalarCanopyTranspiration = 0._rkind ! canopy transpiration (kg m-2 s-1) - scalarCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) - scalarGroundEvaporation = 0._rkind ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - ! solid water fluxes associated with sublimation/frost - scalarCanopySublimation = 0._rkind ! sublimation from the vegetation canopy ((kg m-2 s-1) - scalarSnowSublimation = 0._rkind ! sublimation from the snow surface ((kg m-2 s-1) - ! set canopy fluxes to zero (no canopy) - canairNetFlux = 0._rkind ! net energy flux for the canopy air space (W m-2) - canopyNetFlux = 0._rkind ! net energy flux for the vegetation canopy (W m-2) - ! set canopy derivatives to zero - dCanairNetFlux_dCanairTemp = 0._rkind ! derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) - dCanairNetFlux_dCanopyTemp = 0._rkind ! derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) - dCanairNetFlux_dGroundTemp = 0._rkind ! derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) - dCanopyNetFlux_dCanairTemp = 0._rkind ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) - dCanopyNetFlux_dCanopyTemp = 0._rkind ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) - dCanopyNetFlux_dGroundTemp = 0._rkind ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - dGroundNetFlux_dCanairTemp = 0._rkind ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) - dGroundNetFlux_dCanopyTemp = 0._rkind ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) - ! set liquid flux derivatives to zero (canopy evap) - dCanopyEvaporation_dCanLiq = 0._rkind ! derivative in canopy evaporation w.r.t. canopy liquid water content (s-1) - dCanopyEvaporation_dTCanair= 0._rkind ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTCanopy= 0._rkind ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTGround= 0._rkind ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - ! set liquid flux derivatives to zero (ground evap) - dGroundEvaporation_dCanLiq = 0._rkind ! derivative in ground evaporation w.r.t. canopy liquid water content (s-1) - dGroundEvaporation_dTCanair= 0._rkind ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTCanopy= 0._rkind ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTGround= 0._rkind ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - - ! compute fluxes and derivatives -- separate approach for prescribed temperature and zero flux - if(ix_bcUpprTdyn == prescribedTemp)then - ! compute ground net flux (W m-2) - groundNetFlux = -diag_data%var(iLookDIAG%iLayerThermalC)%dat(0)*(groundTempTrial - upperBoundTemp)/(prog_data%var(iLookPROG%mLayerDepth)%dat(1)*0.5_rkind) - ! compute derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - dGroundNetFlux_dGroundTemp = -diag_data%var(iLookDIAG%iLayerThermalC)%dat(0)/(prog_data%var(iLookPROG%mLayerDepth)%dat(1)*0.5_rkind) - elseif(model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision == zeroFlux)then - groundNetFlux = 0._rkind - dGroundNetFlux_dGroundTemp = 0._rkind - else - err=20; message=trim(message)//'unable to identify upper boundary condition for thermodynamics: expect the case to be prescribedTemp or zeroFlux'; return - end if - - ! ***** - ! (2) NEUMANN BOUNDARY CONDITION... - ! ********************************* - - ! NOTE 1: This is the main routine for calculating vegetation fluxes - ! NOTE 2: This routine also calculates surface fluxes for the case where vegetation is buried with snow (or bare soil) - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! ***** PRELIMINARIES ********************************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - - ! * flux boundary condition - case(energyFlux) - - ! identify the appropriate groundwater variable - select case(ix_spatial_gw) - case(singleBasin); scalarAquiferStorage = basinAquiferStorage - case(localColumn); scalarAquiferStorage = localAquiferStorage - case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return - end select ! (modify the groundwater representation for this single-column implementation) - - ! set canopy stability corrections to the previous values - scalarCanopyStabilityCorrection_old = scalarCanopyStabilityCorrection ! stability correction for the canopy (-) - scalarGroundStabilityCorrection_old = scalarGroundStabilityCorrection ! stability correction for the ground surface (-) - - ! initialize variables to compute stomatal resistance - if(firstFluxCall .and. firstSubStep)then - ! vapor pressure in the canopy air space initialized as vapor pressure of air above the vegetation canopy - ! NOTE: this is needed for the stomatal resistance calculations - if(scalarVP_CanopyAir < 0._rkind)then - scalarVP_CanopyAir = scalarVPair - 1._rkind ! "small" offset used to assist in checking initial derivative calculations - end if - end if - - ! set latent heat of sublimation/vaporization for canopy and ground surface (Pa/K) - ! NOTE: variables are constant over the substep, to simplify relating energy and mass fluxes - if(firstFluxCall)then - scalarLatHeatSubVapCanopy = getLatentHeatValue(canopyTempTrial) - ! case when there is snow on the ground (EXCLUDE "snow without a layer" -- in this case, evaporate from the soil) - if(nSnow > 0)then - if(groundTempTrial > Tfreeze)then; err=20; message=trim(message)//'do not expect ground temperature > 0 when snow is on the ground'; return; end if - scalarLatHeatSubVapGround = LH_sub ! sublimation from snow - scalarGroundSnowFraction = 1._rkind - ! case when the ground is snow-free - else - scalarLatHeatSubVapGround = LH_vap ! evaporation of water in the soil pores: this occurs even if frozen because of super-cooled water - scalarGroundSnowFraction = 0._rkind - end if ! (if there is snow on the ground) - end if ! (if the first flux call) - !write(*,'(a,1x,10(f30.10,1x))') 'groundTempTrial, scalarLatHeatSubVapGround = ', groundTempTrial, scalarLatHeatSubVapGround - - ! compute the roughness length of the ground (ground below the canopy or non-vegetated surface) - z0Ground = z0soil*(1._rkind - scalarGroundSnowFraction) + z0Snow*scalarGroundSnowFraction ! roughness length (m) - - ! compute the total vegetation area index (leaf plus stem) - VAI = scalarLAI + scalarSAI ! vegetation area index - exposedVAI = scalarExposedLAI + scalarExposedSAI ! exposed vegetation area index - - ! compute emissivity of the canopy (-) - if(computeVegFlux)then - select case(ix_canopyEmis) - ! *** simple exponential function - case(simplExp) - scalarCanopyEmissivity = 1._rkind - exp(-exposedVAI) ! effective emissivity of the canopy (-) - ! *** canopy emissivity parameterized as a function of diffuse transmissivity - case(difTrans) - ! compute the exponential integral - scaleLAI = 0.5_rkind*exposedVAI - expi = expInt(scaleLAI) - ! compute diffuse transmissivity (-) - diffuseTrans = (1._rkind - scaleLAI)*exp(-scaleLAI) + (scaleLAI**2._rkind)*expi - ! compute the canopy emissivity - scalarCanopyEmissivity = (1._rkind - diffuseTrans)*vegEmissivity - ! *** check we found the correct option - case default - err=20; message=trim(message)//'unable to identify option for canopy emissivity'; return - end select - end if - - ! ensure canopy longwave fluxes are zero when not computing canopy fluxes - if(.not.computeVegFlux) scalarCanopyEmissivity=0._rkind - - ! compute emissivity of the ground surface (-) - groundEmissivity = scalarGroundSnowFraction*snowEmissivity + (1._rkind - scalarGroundSnowFraction)*soilEmissivity ! emissivity of the ground surface (-) - - ! compute the fraction of canopy that is wet - ! NOTE: we either sublimate or evaporate over the entire substep - if(computeVegFlux)then - - ! compute the fraction of liquid water in the canopy (-) - totalCanopyWater = canopyLiqTrial + canopyIceTrial - if(totalCanopyWater > tiny(1.0_rkind))then - fracLiquidCanopy = canopyLiqTrial / (canopyLiqTrial + canopyIceTrial) - else - fracLiquidCanopy = 0._rkind - end if - - ! get wetted fraction and derivatives - call wettedFrac(& - ! input - .true., & ! flag to denote if derivative is desired - (ix_fDerivMeth == numerical), & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) - (scalarLatHeatSubVapCanopy > LH_vap+verySmall), & ! flag to denote if the canopy is frozen - dCanLiq_dTcanopy, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) - fracLiquidCanopy, & ! fraction of liquid water on the canopy (-) - canopyLiqTrial, & ! canopy liquid water (kg m-2) - canopyIceTrial, & ! canopy ice (kg m-2) - scalarCanopyLiqMax, & ! maximum canopy liquid water (kg m-2) - scalarCanopyIceMax, & ! maximum canopy ice content (kg m-2) - canopyWettingFactor, & ! maximum wetted fraction of the canopy (-) - canopyWettingExp, & ! exponent in canopy wetting function (-) - ! output - scalarCanopyWetFraction, & ! canopy wetted fraction (-) - dCanopyWetFraction_dWat, & ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) - dCanopyWetFraction_dT, & ! derivative in wetted fraction w.r.t. canopy temperature (K-1) - err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - else - scalarCanopyWetFraction = 0._rkind ! canopy wetted fraction (-) - dCanopyWetFraction_dWat = 0._rkind ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) - dCanopyWetFraction_dT = 0._rkind ! derivative in wetted fraction w.r.t. canopy temperature (K-1) - end if - !write(*,'(a,1x,L1,1x,f25.15,1x))') 'computeVegFlux, scalarCanopyWetFraction = ', computeVegFlux, scalarCanopyWetFraction - !print*, 'dCanopyWetFraction_dWat = ', dCanopyWetFraction_dWat - !print*, 'dCanopyWetFraction_dT = ', dCanopyWetFraction_dT - !print*, 'canopyLiqTrial = ', canopyLiqTrial - !print*, 'canopyIceTrial = ', canopyIceTrial - !print*, 'scalarCanopyLiqMax = ', scalarCanopyLiqMax - !print*, 'scalarCanopyIceMax = ', scalarCanopyIceMax - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! ***** AERODYNAMIC RESISTANCE ***************************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - - ! NOTE: compute for all iterations - - ! compute aerodynamic resistances - ! Refs: Choudhury and Monteith (4-layer model for heat budget of homogenous surfaces; QJRMS, 1988) - ! Niu and Yang (Canopy effects on snow processes; JGR, 2004) - ! Mahat et al. (Below-canopy turbulence in a snowmelt model, WRR, 2012) - call aeroResist(& - ! input: model control - computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) - (ix_fDerivMeth == analytical), & ! intent(in): logical flag if would like to compute analytical derivaties - ix_veg_traits, & ! intent(in): choice of parameterization for vegetation roughness length and displacement height - ix_windPrfile, & ! intent(in): choice of canopy wind profile - ix_astability, & ! intent(in): choice of stability function - ! input: above-canopy forcing data - mHeight, & ! intent(in): measurement height (m) - airtemp, & ! intent(in): air temperature at some height above the surface (K) - windspd, & ! intent(in): wind speed at some height above the surface (m s-1) - ! input: canopy and ground temperature - canairTempTrial, & ! intent(in): temperature of the canopy air space (K) - groundTempTrial, & ! intent(in): temperature of the ground surface (K) - ! input: diagnostic variables - exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) - scalarSnowDepth, & ! intent(in): snow depth (m) - ! input: parameters - z0Ground, & ! intent(in): roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) - z0CanopyParam, & ! intent(in): roughness length of the canopy (m) - zpdFraction, & ! intent(in): zero plane displacement / canopy height (-) - critRichNumber, & ! intent(in): critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam, & ! intent(in): parameter in Louis (1979) stability function - Mahrt87_eScale, & ! intent(in): exponential scaling factor in the Mahrt (1987) stability function - windReductionParam, & ! intent(in): canopy wind reduction parameter (-) - leafExchangeCoeff, & ! intent(in): turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) - leafDimension, & ! intent(in): characteristic leaf dimension (m) - heightCanopyTop, & ! intent(in): height at the top of the vegetation canopy (m) - heightCanopyBottom, & ! intent(in): height at the bottom of the vegetation canopy (m) - ! output: stability corrections - scalarRiBulkCanopy, & ! intent(out): bulk Richardson number for the canopy (-) - scalarRiBulkGround, & ! intent(out): bulk Richardson number for the ground surface (-) - scalarCanopyStabilityCorrection, & ! intent(out): stability correction for the canopy (-) - scalarGroundStabilityCorrection, & ! intent(out): stability correction for the ground surface (-) - ! output: scalar resistances - scalarZ0Canopy, & ! intent(out): roughness length of the canopy (m) - scalarWindReductionFactor, & ! intent(out): canopy wind reduction factor (-) - scalarZeroPlaneDisplacement, & ! intent(out): zero plane displacement (m) - scalarEddyDiffusCanopyTop, & ! intent(out): eddy diffusivity for heat at the top of the canopy (m2 s-1) - scalarFrictionVelocity, & ! intent(out): friction velocity (m s-1) - scalarWindspdCanopyTop, & ! intent(out): windspeed at the top of the canopy (m s-1) - scalarWindspdCanopyBottom, & ! intent(out): windspeed at the height of the bottom of the canopy (m s-1) - scalarLeafResistance, & ! intent(out): mean leaf boundary layer resistance per unit leaf area (s m-1) - scalarGroundResistance, & ! intent(out): below canopy aerodynamic resistance (s m-1) - scalarCanopyResistance, & ! intent(out): above canopy aerodynamic resistance (s m-1) - ! output: derivatives in scalar resistances - dGroundResistance_dTGround, & ! intent(out): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - dGroundResistance_dTCanopy, & ! intent(out): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - dGroundResistance_dTCanair, & ! intent(out): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - dCanopyResistance_dTCanopy, & ! intent(out): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - dCanopyResistance_dTCanair, & ! intent(out): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - ! output: error control - err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - !print*, scalarLeafResistance, & ! mean leaf boundary layer resistance per unit leaf area (s m-1) - ! scalarGroundResistance, & ! below canopy aerodynamic resistance (s m-1) - ! scalarCanopyResistance, & ! above canopy aerodynamic resistance (s m-1) - ! '(leaf, ground, canopy)' - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! ***** STOMATAL RESISTANCE ***************************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - - ! stomatal resistance is constant over the SUBSTEP - ! NOTE: This is a simplification, as stomatal resistance does depend on canopy temperature - ! This "short-cut" made because: - ! (1) computations are expensive; - ! (2) derivative calculations are rather complex (iterations within the Ball-Berry routine); and - ! (3) stomatal resistance does not change rapidly - if(firstFluxCall)then - - ! compute the saturation vapor pressure for vegetation temperature - TV_celcius = canopyTempTrial - Tfreeze - call satVapPress(TV_celcius, scalarSatVP_CanopyTemp, dSVPCanopy_dCanopyTemp) - - ! compute soil moisture factor controlling stomatal resistance - call soilResist(& - ! input (model decisions) - ix_soilStress, & ! intent(in): choice of function for the soil moisture control on stomatal resistance - ix_groundwatr, & ! intent(in): groundwater parameterization - ! input (state variables) - mLayerMatricHead(1:nSoil), & ! intent(in): matric head in each soil layer (m) - mLayerVolFracLiq(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water in each soil layer (-) - scalarAquiferStorage, & ! intent(in): aquifer storage (m) - ! input (diagnostic variables) - mLayerRootDensity(1:nSoil), & ! intent(in): root density in each layer (-) - scalarAquiferRootFrac, & ! intent(in): fraction of roots below the lowest soil layer (-) - ! input (parameters) - plantWiltPsi, & ! intent(in): matric head at wilting point (m) - soilStressParam, & ! intent(in): parameter in the exponential soil stress function (-) - critSoilWilting, & ! intent(in): critical vol. liq. water content when plants are wilting (-) - critSoilTranspire, & ! intent(in): critical vol. liq. water content when transpiration is limited (-) - critAquiferTranspire, & ! intent(in): critical aquifer storage value when transpiration is limited (m) - ! output - scalarTranspireLim, & ! intent(out): weighted average of the transpiration limiting factor (-) - mLayerTranspireLim(1:nSoil), & ! intent(out): transpiration limiting factor in each layer (-) - scalarTranspireLimAqfr, & ! intent(out): transpiration limiting factor for the aquifer (-) - err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - !print*, 'weighted average of the soil moiture factor controlling stomatal resistance (-) = ', scalarTranspireLim - - !write(*,'(a,1x,10(f20.10,1x))') 'canopyTempTrial, scalarSatVP_CanopyTemp, scalarVP_CanopyAir = ', & - ! canopyTempTrial, scalarSatVP_CanopyTemp, scalarVP_CanopyAir - - ! compute stomatal resistance - call stomResist(& - ! input (state and diagnostic variables) - canopyTempTrial, & ! intent(in): temperature of the vegetation canopy (K) - scalarSatVP_CanopyTemp, & ! intent(in): saturation vapor pressure at the temperature of the veg canopy (Pa) - scalarVP_CanopyAir, & ! intent(in): canopy air vapor pressure (Pa) - ! input: data structures - type_data, & ! intent(in): type of vegetation and soil - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - model_decisions, & ! intent(in): model decisions - ! input-output: data structures - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output: error control - err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - end if ! (if the first flux call in a given sub-step) - - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! ***** LONGWAVE RADIATION ***************************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - - ! compute canopy longwave radiation balance - call longwaveBal(& - ! input: model control - ix_fDerivMeth, & ! intent(in): method used to calculate flux derivatives - computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation - ! input: canopy and ground temperature - canopyTempTrial, & ! intent(in): temperature of the vegetation canopy (K) - groundTempTrial, & ! intent(in): temperature of the ground surface (K) - ! input: canopy and ground emissivity - scalarCanopyEmissivity, & ! intent(in): canopy emissivity (-) - groundEmissivity, & ! intent(in): ground emissivity (-) - ! input: forcing - LWRadAtm, & ! intent(in): downwelling longwave radiation at the upper boundary (W m-2) - ! output: emitted radiation from the canopy and ground - scalarLWRadCanopy, & ! intent(out): longwave radiation emitted from the canopy (W m-2) - scalarLWRadGround, & ! intent(out): longwave radiation emitted at the ground surface (W m-2) - ! output: individual fluxes - scalarLWRadUbound2Canopy, & ! intent(out): downward atmospheric longwave radiation absorbed by the canopy (W m-2) - scalarLWRadUbound2Ground, & ! intent(out): downward atmospheric longwave radiation absorbed by the ground (W m-2) - scalarLWRadUbound2Ubound, & ! intent(out): atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) - scalarLWRadCanopy2Ubound, & ! intent(out): longwave radiation emitted from canopy lost thru upper boundary (W m-2) - scalarLWRadCanopy2Ground, & ! intent(out): longwave radiation emitted from canopy absorbed by the ground (W m-2) - scalarLWRadCanopy2Canopy, & ! intent(out): canopy longwave reflected from ground and absorbed by the canopy (W m-2) - scalarLWRadGround2Ubound, & ! intent(out): longwave radiation emitted from ground lost thru upper boundary (W m-2) - scalarLWRadGround2Canopy, & ! intent(out): longwave radiation emitted from ground and absorbed by the canopy (W m-2) - ! output: net fluxes - scalarLWNetCanopy, & ! intent(out): net longwave radiation at the canopy (W m-2) - scalarLWNetGround, & ! intent(out): net longwave radiation at the ground surface (W m-2) - scalarLWNetUbound, & ! intent(out): net longwave radiation at the upper boundary (W m-2) - ! output: flux derivatives - dLWNetCanopy_dTCanopy, & ! intent(out): derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) - dLWNetGround_dTGround, & ! intent(out): derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetCanopy_dTGround, & ! intent(out): derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetGround_dTCanopy, & ! intent(out): derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) - ! output: error control - err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - !print*, 'dLWNetCanopy_dTGround = ', dLWNetCanopy_dTGround - - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! ***** TURBULENT HEAT FLUXES ************************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - - ! check the need to compute numerical derivatives - if(ix_fDerivMeth == numerical)then - nFlux=5 ! compute the derivatives using one-sided finite differences - else - nFlux=1 ! compute analytical derivatives - end if - - ! either one or multiple flux calls, depending on if using analytical or numerical derivatives - do itry=nFlux,1,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) - - ! ------------------------------------------------------------------------------------- - ! state perturbations for numerical deriavtives with one-sided finite differences - ! note: no perturbations performed using analytical derivatives (nFlux=1) - ! ------------------------------------------------------------------------------------- - - ! identify the type of perturbation - select case(itry) - - ! un-perturbed case - case(unperturbed) - groundTemp = groundTempTrial - canopyTemp = canopyTempTrial - canairTemp = canairTempTrial - canopyWetFraction = scalarCanopyWetFraction - - ! perturb ground temperature - case(perturbStateGround) - groundTemp = groundTempTrial + dx - canopyTemp = canopyTempTrial - canairTemp = canairTempTrial - canopyWetFraction = scalarCanopyWetFraction - - ! perturb canopy temperature - case(perturbStateCanopy) - groundTemp = groundTempTrial - canopyTemp = canopyTempTrial + dx - canairTemp = canairTempTrial - canopyWetFraction = scalarCanopyWetFraction - - ! perturb canopy air temperature - case(perturbStateCanair) - groundTemp = groundTempTrial - canopyTemp = canopyTempTrial - canairTemp = canairTempTrial + dx - canopyWetFraction = scalarCanopyWetFraction - - ! perturb canopy liquid water content - case(perturbStateCanLiq) - groundTemp = groundTempTrial - canopyTemp = canopyTempTrial - canairTemp = canairTempTrial - - ! perturbations in canopy liquid water content affect canopy wetted fraction - if(computeVegFlux)then - call wettedFrac(& - ! input - .false., & ! flag to denote if derivative is desired - (ix_fDerivMeth == numerical), & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) - (scalarLatHeatSubVapCanopy > LH_vap+verySmall), & ! flag to denote if the canopy is frozen - dCanLiq_dTcanopy, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) - fracLiquidCanopy, & ! fraction of liquid water on the canopy (-) - canopyLiqTrial+dx, & ! canopy liquid water (kg m-2) - canopyIceTrial, & ! canopy ice (kg m-2) - scalarCanopyLiqMax, & ! maximum canopy liquid water (kg m-2) - scalarCanopyIceMax, & ! maximum canopy ice content (kg m-2) - canopyWettingFactor, & ! maximum wetted fraction of the canopy (-) - canopyWettingExp, & ! exponent in canopy wetting function (-) - ! output - canopyWetFraction, & ! canopy wetted fraction (-) - dCanopyWetFraction_dWat, & ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) - dCanopyWetFraction_dT, & ! derivative in wetted fraction w.r.t. canopy temperature (K-1) - err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - else - canopyWetFraction = 0._rkind - end if - !print*, 'wetted fraction derivative = ', (canopyWetFraction - scalarCanopyWetFraction)/dx - !pause - - ! check for an unknown perturbation - case default; err=10; message=trim(message)//"unknown perturbation"; return - - end select ! (type of perturbation) - - ! compute the saturation vapor pressure for vegetation temperature - ! NOTE: saturated vapor pressure derivatives don't seem that accurate.... - TV_celcius = canopyTemp - Tfreeze - call satVapPress(TV_celcius, scalarSatVP_CanopyTemp, dSVPCanopy_dCanopyTemp) - - ! compute the saturation vapor pressure for ground temperature - ! NOTE: saturated vapor pressure derivatives don't seem that accurate.... - TG_celcius = groundTemp - Tfreeze - call satVapPress(TG_celcius, scalarSatVP_GroundTemp, dSVPGround_dGroundTemp) - - ! ------------------------------------------------------------------------------------- - ! calculation block (unperturbed fluxes returned [computed last]) - ! ------------------------------------------------------------------------------------- - - ! re-compute aerodynamic resistances for perturbed cases - ! NOTE: unperturbed fluxes computed earlier, and not over-written - if(itry /= unperturbed)then - call aeroResist(& - ! input: model control - computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) - .false., & ! intent(in): logical flag if would like to compute analytical derivaties - ix_veg_traits, & ! intent(in): choice of parameterization for vegetation roughness length and displacement height - ix_windPrfile, & ! intent(in): choice of canopy wind profile - ix_astability, & ! intent(in): choice of stability function - ! input: above-canopy forcing data - mHeight, & ! intent(in): measurement height (m) - airtemp, & ! intent(in): air temperature at some height above the surface (K) - windspd, & ! intent(in): wind speed at some height above the surface (m s-1) - ! input: temperature (canopy, ground, canopy air space) - canairTemp, & ! intent(in): temperature of the canopy air space (K) - groundTemp, & ! intent(in): ground temperature (K) - ! input: diagnostic variables - exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) - scalarSnowDepth, & ! intent(in): snow depth (m) - ! input: parameters - z0Ground, & ! intent(in): roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) - z0CanopyParam, & ! intent(in): roughness length of the canopy (m) - zpdFraction, & ! intent(in): zero plane displacement / canopy height (-) - critRichNumber, & ! intent(in): critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam, & ! intent(in): parameter in Louis (1979) stability function - Mahrt87_eScale, & ! intent(in): exponential scaling factor in the Mahrt (1987) stability function - windReductionParam, & ! intent(in): canopy wind reduction parameter (-) - leafExchangeCoeff, & ! intent(in): turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) - leafDimension, & ! intent(in): characteristic leaf dimension (m) - heightCanopyTop, & ! intent(in): height at the top of the vegetation canopy (m) - heightCanopyBottom, & ! intent(in): height at the bottom of the vegetation canopy (m) - ! output: stability corrections - notUsed_RiBulkCanopy, & ! intent(out): bulk Richardson number for the canopy (-) - notUsed_RiBulkGround, & ! intent(out): bulk Richardson number for the ground surface (-) - notUsed_scalarCanopyStabilityCorrection, & ! intent(out): stability correction for the canopy (-) - notUsed_scalarGroundStabilityCorrection, & ! intent(out): stability correction for the ground surface (-) - ! output: scalar resistances - notUsed_z0Canopy, & ! intent(out): roughness length of the canopy (m) - notUsed_WindReductionFactor, & ! intent(out): canopy wind reduction factor (-) - notUsed_ZeroPlaneDisplacement, & ! intent(out): zero plane displacement (m) - notUsed_EddyDiffusCanopyTop, & ! intent(out): eddy diffusivity for heat at the top of the canopy (m2 s-1) - notUsed_FrictionVelocity, & ! intent(out): friction velocity (m s-1) - notUsed_WindspdCanopyTop, & ! intent(out): windspeed at the top of the canopy (m s-1) - notUsed_WindspdCanopyBottom, & ! intent(out): windspeed at the height of the bottom of the canopy (m s-1) - trialLeafResistance, & ! intent(out): mean leaf boundary layer resistance per unit leaf area (s m-1) - trialGroundResistance, & ! intent(out): below canopy aerodynamic resistance (s m-1) - trialCanopyResistance, & ! intent(out): above canopy aerodynamic resistance (s m-1) - ! output: derivatives in scalar resistances - notUsed_dGroundResistance_dTGround, & ! intent(out): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - notUsed_dGroundResistance_dTCanopy, & ! intent(out): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - notUsed_dGroundResistance_dTCanair, & ! intent(out): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - notUsed_dCanopyResistance_dTCanopy, & ! intent(out): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - notUsed_dCanopyResistance_dTCanair, & ! intent(out): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - ! output: error control - err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - - ! assign scalar resistances for un-perturbed cases - else - trialLeafResistance = scalarLeafResistance - trialGroundResistance = scalarGroundResistance - trialCanopyResistance = scalarCanopyResistance - - end if ! (re-computing resistances for perturbed cases) - !print*, 'trialLeafResistance = ', trialLeafResistance - !print*, 'trialGroundResistance = ', trialGroundResistance - !print*, 'trialCanopyResistance = ', trialCanopyResistance - - ! compute the relative humidity in the top soil layer and the resistance at the ground surface - ! NOTE: computations are based on start-of-step values, so only compute for the first flux call - if(firstFluxCall)then - ! (soil water evaporation factor [0-1]) - soilEvapFactor = mLayerVolFracLiq(nSnow+1)/(theta_sat - theta_res) - ! (resistance from the soil [s m-1]) - scalarSoilResistance = scalarGroundSnowFraction*1._rkind + (1._rkind - scalarGroundSnowFraction)*EXP(8.25_rkind - 4.225_rkind*soilEvapFactor) ! Sellers (1992) - !scalarSoilResistance = scalarGroundSnowFraction*0._rkind + (1._rkind - scalarGroundSnowFraction)*exp(8.25_rkind - 6.0_rkind*soilEvapFactor) ! Niu adjustment to decrease resitance for wet soil - ! (relative humidity in the soil pores [0-1]) - if(mLayerMatricHead(1) > -1.e+6_rkind)then ! avoid problems with numerical precision when soil is very dry - soilRelHumidity_noSnow = exp( (mLayerMatricHead(1)*gravity) / (groundTemp*R_wv) ) - else - soilRelHumidity_noSnow = 0._rkind - end if ! (if matric head is very low) - scalarSoilRelHumidity = scalarGroundSnowFraction*1._rkind + (1._rkind - scalarGroundSnowFraction)*soilRelHumidity_noSnow - !print*, 'mLayerMatricHead(1), scalarSoilRelHumidity = ', mLayerMatricHead(1), scalarSoilRelHumidity - end if ! (if the first flux call) - - ! compute turbulent heat fluxes - call turbFluxes(& - ! input: model control - computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) - ix_fDerivMeth, & ! intent(in): method used to calculate flux derivatives - ! input: above-canopy forcing data - airtemp, & ! intent(in): air temperature at some height above the surface (K) - airpres, & ! intent(in): air pressure of the air above the vegetation canopy (Pa) - scalarVPair, & ! intent(in): vapor pressure of the air above the vegetation canopy (Pa) - ! input: latent heat of sublimation/vaporization - scalarLatHeatSubVapCanopy, & ! intent(in): latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) - scalarLatHeatSubVapGround, & ! intent(in): latent heat of sublimation/vaporization for the ground surface (J kg-1) - ! input: canopy/ground temperature and saturated vapor pressure - canairTemp, & ! intent(in): temperature of the canopy air space (K) - canopyTemp, & ! intent(in): canopy temperature (K) - groundTemp, & ! intent(in): ground temperature (K) - scalarSatVP_CanopyTemp, & ! intent(in): saturation vapor pressure at the temperature of the veg canopy (Pa) - scalarSatVP_GroundTemp, & ! intent(in): saturation vapor pressure at the temperature of the ground (Pa) - dSVPCanopy_dCanopyTemp, & ! intent(in): derivative in canopy saturation vapor pressure w.r.t. canopy temperature (Pa K-1) - dSVPGround_dGroundTemp, & ! intent(in): derivative in ground saturation vapor pressure w.r.t. ground temperature (Pa K-1) - ! input: diagnostic variables - exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) - canopyWetFraction, & ! intent(in): trial value for the fraction of canopy that is wet [0-1] - dCanopyWetFraction_dWat, & ! intent(in): derivative in the canopy wetted fraction w.r.t. total water content (kg-1 m-2) - dCanopyWetFraction_dT, & ! intent(in): derivative in wetted fraction w.r.t. canopy temperature (K-1) - scalarCanopySunlitLAI, & ! intent(in): sunlit leaf area (-) - scalarCanopyShadedLAI, & ! intent(in): shaded leaf area (-) - scalarSoilRelHumidity, & ! intent(in): relative humidity in the soil pores [0-1] - scalarSoilResistance, & ! intent(in): resistance from the soil (s m-1) - trialLeafResistance, & ! intent(in): mean leaf boundary layer resistance per unit leaf area (s m-1) - trialGroundResistance, & ! intent(in): below canopy aerodynamic resistance (s m-1) - trialCanopyResistance, & ! intent(in): above canopy aerodynamic resistance (s m-1) - scalarStomResistSunlit, & ! intent(in): stomatal resistance for sunlit leaves (s m-1) - scalarStomResistShaded, & ! intent(in): stomatal resistance for shaded leaves (s m-1) - ! input: derivatives in scalar resistances - dGroundResistance_dTGround, & ! intent(in): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - dGroundResistance_dTCanopy, & ! intent(in): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - dGroundResistance_dTCanair, & ! intent(in): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - dCanopyResistance_dTCanopy, & ! intent(in): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - dCanopyResistance_dTCanair, & ! intent(in): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - ! output: conductances (used to check derivative calculations) - scalarLeafConductance, & ! intent(out): leaf conductance (m s-1) - scalarCanopyConductance, & ! intent(out): canopy conductance (m s-1) - scalarGroundConductanceSH, & ! intent(out): ground conductance for sensible heat (m s-1) - scalarGroundConductanceLH, & ! intent(out): ground conductance for latent heat -- includes soil resistance (m s-1) - scalarEvapConductance, & ! intent(out): conductance for evaporation (m s-1) - scalarTransConductance, & ! intent(out): conductance for transpiration (m s-1) - scalarTotalConductanceSH, & ! intent(out): total conductance for sensible heat (m s-1) - scalarTotalConductanceLH, & ! intent(out): total conductance for latent heat (m s-1) - ! output: canopy air space variables - scalarVP_CanopyAir, & ! intent(out): vapor pressure of the canopy air space (Pa) - ! output: fluxes from the vegetation canopy - scalarSenHeatCanopy, & ! intent(out): sensible heat flux from the canopy to the canopy air space (W m-2) - scalarLatHeatCanopyEvap, & ! intent(out): latent heat flux associated with evaporation from the canopy to the canopy air space (W m-2) - scalarLatHeatCanopyTrans, & ! intent(out): latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) - ! output: fluxes from non-vegetated surfaces (ground surface below vegetation, bare ground, or snow covered vegetation) - scalarSenHeatGround, & ! intent(out): sensible heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) - scalarLatHeatGround, & ! intent(out): latent heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) - ! output: total heat fluxes to the atmosphere - scalarSenHeatTotal, & ! intent(out): total sensible heat flux to the atmosphere (W m-2) - scalarLatHeatTotal, & ! intent(out): total latent heat flux to the atmosphere (W m-2) - ! output: net fluxes - turbFluxCanair, & ! intent(out): net turbulent heat fluxes at the canopy air space (W m-2) - turbFluxCanopy, & ! intent(out): net turbulent heat fluxes at the canopy (W m-2) - turbFluxGround, & ! intent(out): net turbulent heat fluxes at the ground surface (W m-2) - ! output: energy flux derivatives - dTurbFluxCanair_dTCanair, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxCanair_dTCanopy, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxCanair_dTGround, & ! intent(out): derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanair, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanopy, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxCanopy_dTGround, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxGround_dTCanair, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxGround_dTCanopy, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxGround_dTGround, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - ! output: liquid flux derivatives (canopy evap) - dLatHeatCanopyEvap_dCanLiq, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (W kg-1) - dLatHeatCanopyEvap_dTCanair, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTCanopy, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTGround, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) - ! output: liquid flux derivatives (ground evap) - dLatHeatGroundEvap_dCanLiq, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy liquid water content (J kg-1 s-1) - dLatHeatGroundEvap_dTCanair, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy air temperature - dLatHeatGroundEvap_dTCanopy, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy temperature - dLatHeatGroundEvap_dTGround, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. ground temperature - ! output: cross derivatives - dTurbFluxCanair_dCanLiq, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dTurbFluxCanopy_dCanLiq, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dTurbFluxGround_dCanLiq, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - ! output: error control - err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - !write(*,'(a,f25.15)') 'scalarSenHeatTotal = ', scalarSenHeatTotal - !write(*,'(a,f25.15)') 'scalarSenHeatCanopy = ', scalarSenHeatCanopy - !write(*,'(a,f25.15)') 'scalarLatHeatCanopyEvap = ', scalarLatHeatCanopyEvap - !write(*,'(a,f25.15)') 'scalarLatHeatCanopyTrans = ', scalarLatHeatCanopyTrans - - !print*, 'scalarSenHeatGround = ', scalarSenHeatGround - !print*, 'scalarLatHeatGround = ', scalarLatHeatGround - - !notUsed_scalarCanopyStabilityCorrection ! stability correction for the canopy (-) - !notUsed_scalarGroundStabilityCorrection ! stability correction for the ground surface (-) - !notUsed_EddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) - !notUsed_FrictionVelocity ! friction velocity (m s-1) - !notUsed_WindspdCanopyTop ! windspeed at the top of the canopy (m s-1) - !notUsed_WindspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) - !trialLeafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) - !trialGroundResistance ! below canopy aerodynamic resistance (s m-1) - !trialCanopyResistance ! above canopy aerodynamic resistance (s m-1) - - ! save perturbed fluxes - if(ix_fDerivMeth == numerical)then - select case(itry) ! (select type of perturbation) - case(unperturbed) - try0 = turbFluxGround - exit - case(perturbStateCanair) - turbFluxCanair_dStateCanair = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) - turbFluxCanopy_dStateCanair = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) - turbFluxGround_dStateCanair = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) - latHeatCanEvap_dStateCanair = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) - case(perturbStateCanopy) - turbFluxCanair_dStateCanopy = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) - turbFluxCanopy_dStateCanopy = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) - turbFluxGround_dStateCanopy = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) - latHeatCanEvap_dStateCanopy = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) - case(perturbStateGround) - try1 = turbFluxGround - turbFluxCanair_dStateGround = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) - turbFluxCanopy_dStateGround = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) - turbFluxGround_dStateGround = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) - latHeatCanEvap_dStateGround = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) - case(perturbStateCanLiq) - turbFluxCanair_dStateCanliq = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) - turbFluxCanopy_dStateCanLiq = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) - turbFluxGround_dStateCanLiq = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) - latHeatCanEvap_dStateCanliq = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) - case default; err=10; message=trim(message)//"unknown perturbation"; return - end select ! (type of perturbation) - end if ! (if numerical) - - end do ! (looping through different flux perturbations) - - ! test derivative - !if(ix_fDerivMeth == numerical) print*, 'try0, try1 = ', try0, try1 - !if(ix_fDerivMeth == numerical) print*, 'derivative = ', (ix_fDerivMeth == numerical), (try1 - try0)/dx - !if(ix_fDerivMeth == analytical) print*, 'derivative = ', (ix_fDerivMeth == numerical), dTurbFluxGround_dTGround - !pause - - ! compute numerical derivatives - if(ix_fDerivMeth == numerical)then - ! derivatives w.r.t. canopy air temperature - dTurbFluxCanair_dTCanair = (turbFluxCanair_dStateCanair - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanair = (turbFluxCanopy_dStateCanair - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxGround_dTCanair = (turbFluxGround_dStateCanair - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTCanair = (latHeatCanEvap_dStateCanair - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) - ! derivatives w.r.t. canopy temperature - dTurbFluxCanair_dTCanopy = (turbFluxCanair_dStateCanopy - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanopy = (turbFluxCanopy_dStateCanopy - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxGround_dTCanopy = (turbFluxGround_dStateCanopy - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTCanopy = (latHeatCanEvap_dStateCanopy - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) - ! derivatives w.r.t. ground temperature - dTurbFluxCanair_dTGround = (turbFluxCanair_dStateGround - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxCanopy_dTGround = (turbFluxCanopy_dStateGround - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxGround_dTGround = (turbFluxGround_dStateGround - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTGround = (latHeatCanEvap_dStateGround - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) - ! derivatives w.r.t. canopy liquid water content - dTurbFluxCanair_dCanLiq = (turbFluxCanair_dStateCanliq - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dTurbFluxCanopy_dCanLiq = (turbFluxCanopy_dStateCanLiq - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dTurbFluxGround_dCanLiq = (turbFluxGround_dStateCanLiq - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dLatHeatCanopyEvap_dCanLiq = (latHeatCanEvap_dStateCanliq - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (J kg-1 s-1) - end if - !if(heightCanopyBottom < scalarSnowDepth+z0Ground) pause 'bottom of the canopy is covered' - - ! test - !print*, (ix_fDerivMeth == numerical) - !print*, 'dTurbFluxCanair_dTCanair = ', dTurbFluxCanair_dTCanair - !print*, 'dTurbFluxCanair_dTCanopy = ', dTurbFluxCanair_dTCanopy - !print*, 'dTurbFluxCanair_dTGround = ', dTurbFluxCanair_dTGround - !print*, 'dTurbFluxCanopy_dTCanair = ', dTurbFluxCanopy_dTCanair - !print*, 'dTurbFluxCanopy_dTCanopy = ', dTurbFluxCanopy_dTCanopy - !print*, 'dTurbFluxCanopy_dTGround = ', dTurbFluxCanopy_dTGround - !print*, 'dTurbFluxGround_dTCanair = ', dTurbFluxGround_dTCanair - !print*, 'dTurbFluxGround_dTCanopy = ', dTurbFluxGround_dTCanopy - !print*, 'dTurbFluxGround_dTGround = ', dTurbFluxGround_dTGround - !print*, 'dLatHeatCanopyEvap_dCanLiq = ', dLatHeatCanopyEvap_dCanLiq - !print*, 'dLatHeatCanopyEvap_dTCanair = ', dLatHeatCanopyEvap_dTCanair - !print*, 'dLatHeatCanopyEvap_dTCanopy = ', dLatHeatCanopyEvap_dTCanopy - !print*, 'dLatHeatCanopyEvap_dTGround = ', dLatHeatCanopyEvap_dTGround - !print*, 'dTurbFluxCanair_dCanLiq = ', dTurbFluxCanair_dCanLiq - !print*, 'dTurbFluxCanopy_dCanLiq = ', dTurbFluxCanopy_dCanLiq - !print*, 'dTurbFluxGround_dCanLiq = ', dTurbFluxGround_dCanLiq - !print*, '*****' - !pause - - !print*, 'scalarRainfall, scalarThroughfallRain, scalarSnowfall, scalarThroughfallSnow, canopyTempTrial, scalarTwetbulb = ', & - ! scalarRainfall, scalarThroughfallRain, scalarSnowfall, scalarThroughfallSnow, canopyTempTrial, scalarTwetbulb - - ! compute the heat advected with precipitation (W m-2) - ! NOTE: fluxes are in kg m-2 s-1, so no need to use density of water/ice here - scalarCanopyAdvectiveHeatFlux = -Cp_water*(scalarRainfall - scalarThroughfallRain)*(canopyTempTrial - scalarTwetbulb) + & - (-Cp_ice)*(scalarSnowfall - scalarThroughfallSnow)*(canopyTempTrial - scalarTwetbulb) - scalarGroundAdvectiveHeatFlux = -Cp_water*scalarThroughfallRain*(groundTempTrial - scalarTwetbulb) + & - (-Cp_ice)*scalarThroughfallSnow*(groundTempTrial - scalarTwetbulb) !+ & - ! -Cp_water*scalarCanopyLiqDrainage *(groundTempTrial - canopyTempTrial) + & - ! -Cp_ice *scalarCanopySnowUnloading*(groundTempTrial - canopyTempTrial) - !print*, 'scalarRainfall, scalarThroughfallRain, scalarSnowfall, scalarThroughfallSnow = ', scalarRainfall, scalarThroughfallRain, scalarSnowfall, scalarThroughfallSnow - !print*, 'scalarCanopyAdvectiveHeatFlux, scalarGroundAdvectiveHeatFlux = ', scalarCanopyAdvectiveHeatFlux, scalarGroundAdvectiveHeatFlux - - ! compute the mass flux associated with transpiration and evaporation/sublimation (J m-2 s-1 --> kg m-2 s-1) - ! NOTE: remove water from the snow on the ground in preference to removing water from the water in soil pores - !print*, 'scalarLatHeatCanopyTrans = ', scalarLatHeatCanopyTrans - !print*, 'scalarLatHeatGround = ', scalarLatHeatGround - ! (canopy transpiration/sublimation) - if(scalarLatHeatSubVapCanopy > LH_vap+verySmall)then ! sublimation - scalarCanopyEvaporation = 0._rkind - scalarCanopySublimation = scalarLatHeatCanopyEvap/LH_sub - if(scalarLatHeatCanopyTrans > 0._rkind)then ! flux directed towards the veg - scalarCanopySublimation = scalarCanopySublimation + scalarLatHeatCanopyTrans/LH_sub ! frost - scalarCanopyTranspiration = 0._rkind - else - scalarCanopyTranspiration = scalarLatHeatCanopyTrans/LH_vap ! transpiration is always vapor - end if - ! (canopy transpiration/evaporation) - else ! evaporation - scalarCanopyEvaporation = scalarLatHeatCanopyEvap/LH_vap - scalarCanopySublimation = 0._rkind - if(scalarLatHeatCanopyTrans > 0._rkind)then ! flux directed towards the veg - scalarCanopyEvaporation = scalarCanopyEvaporation + scalarLatHeatCanopyTrans/LH_vap - scalarCanopyTranspiration = 0._rkind - else - scalarCanopyTranspiration = scalarLatHeatCanopyTrans/LH_vap - end if - end if - ! (ground evaporation/sublimation) - if(scalarLatHeatSubVapGround > LH_vap+verySmall)then ! sublimation - ! NOTE: this should only occur when we have formed snow layers, so check - if(nSnow == 0)then; err=20; message=trim(message)//'only expect snow sublimation when we have formed some snow layers'; return; end if - scalarGroundEvaporation = 0._rkind ! ground evaporation is zero once the snowpack has formed - scalarSnowSublimation = scalarLatHeatGround/LH_sub - else - ! NOTE: this should only occur when we have no snow layers, so check - if(nSnow > 0)then; err=20; message=trim(message)//'only expect ground evaporation when there are no snow layers'; return; end if - scalarGroundEvaporation = scalarLatHeatGround/LH_vap - scalarSnowSublimation = 0._rkind ! no sublimation from snow if no snow layers have formed - end if - !print*, 'scalarSnowSublimation, scalarLatHeatGround = ', scalarSnowSublimation, scalarLatHeatGround - - !print*, 'canopyWetFraction, scalarCanopyEvaporation = ', canopyWetFraction, scalarCanopyEvaporation - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! ***** AND STITCH EVERYTHING TOGETHER ***************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - - ! compute derived fluxes - scalarTotalET = scalarGroundEvaporation + scalarCanopyEvaporation + scalarCanopyTranspiration - scalarNetRadiation = scalarCanopyAbsorbedSolar + scalarLWNetCanopy + scalarGroundAbsorbedSolar + scalarLWNetGround - - ! compute net fluxes at the canopy and ground surface - canairNetFlux = turbFluxCanair - canopyNetFlux = scalarCanopyAbsorbedSolar + scalarLWNetCanopy + turbFluxCanopy + scalarCanopyAdvectiveHeatFlux - groundNetFlux = scalarGroundAbsorbedSolar + scalarLWNetGround + turbFluxGround + scalarGroundAdvectiveHeatFlux - !write(*,'(a,1x,10(e17.10,1x))') 'canopyNetFlux, groundNetFlux, scalarLWNetCanopy, turbFluxCanopy, turbFluxGround, scalarLWNetGround, scalarCanopyAdvectiveHeatFlux = ', & - ! canopyNetFlux, groundNetFlux, scalarLWNetCanopy, turbFluxCanopy, turbFluxGround, scalarLWNetGround, scalarCanopyAdvectiveHeatFlux - !write(*,'(a,1x,10(e20.14,1x))') 'groundNetFlux, scalarGroundAbsorbedSolar, scalarLWNetGround, turbFluxGround, scalarGroundAdvectiveHeatFlux = ', & - ! groundNetFlux, scalarGroundAbsorbedSolar, scalarLWNetGround, turbFluxGround, scalarGroundAdvectiveHeatFlux - - ! compute the energy derivatives - dCanairNetFlux_dCanairTemp = dTurbFluxCanair_dTCanair - dCanairNetFlux_dCanopyTemp = dTurbFluxCanair_dTCanopy - dCanairNetFlux_dGroundTemp = dTurbFluxCanair_dTGround - dCanopyNetFlux_dCanairTemp = dTurbFluxCanopy_dTCanair - dCanopyNetFlux_dCanopyTemp = dLWNetCanopy_dTCanopy + dTurbFluxCanopy_dTCanopy - Cp_water*(scalarRainfall - scalarThroughfallRain) - Cp_ice*(scalarSnowfall - scalarThroughfallSnow) - dCanopyNetFlux_dGroundTemp = dLWNetCanopy_dTGround + dTurbFluxCanopy_dTGround - dGroundNetFlux_dCanairTemp = dTurbFluxGround_dTCanair - dGroundNetFlux_dCanopyTemp = dLWNetGround_dTCanopy + dTurbFluxGround_dTCanopy - dGroundNetFlux_dGroundTemp = dLWNetGround_dTGround + dTurbFluxGround_dTGround - Cp_water*scalarThroughfallRain - Cp_ice*scalarThroughfallSnow - - ! check if evaporation or sublimation - if(scalarLatHeatSubVapCanopy < LH_vap+verySmall)then ! evaporation - - ! compute the liquid water derivarives - dCanopyEvaporation_dCanLiq = dLatHeatCanopyEvap_dCanLiq/LH_vap ! (s-1) - dCanopyEvaporation_dTCanair = dLatHeatCanopyEvap_dTCanair/LH_vap ! (kg m-2 s-1 K-1) - dCanopyEvaporation_dTCanopy = dLatHeatCanopyEvap_dTCanopy/LH_vap ! (kg m-2 s-1 K-1) - dCanopyEvaporation_dTGround = dLatHeatCanopyEvap_dTGround/LH_vap ! (kg m-2 s-1 K-1) - - ! sublimation - else - dCanopyEvaporation_dCanLiq = 0._rkind ! (s-1) - dCanopyEvaporation_dTCanair = 0._rkind ! (kg m-2 s-1 K-1) - dCanopyEvaporation_dTCanopy = 0._rkind ! (kg m-2 s-1 K-1) - dCanopyEvaporation_dTGround = 0._rkind ! (kg m-2 s-1 K-1) - end if - - ! compute the liquid water derivarives (ground evap) - dGroundEvaporation_dCanLiq = dLatHeatGroundEvap_dCanLiq/LH_vap ! (s-1) - dGroundEvaporation_dTCanair = dLatHeatGroundEvap_dTCanair/LH_vap ! (kg m-2 s-1 K-1) - dGroundEvaporation_dTCanopy = dLatHeatGroundEvap_dTCanopy/LH_vap ! (kg m-2 s-1 K-1) - dGroundEvaporation_dTGround = dLatHeatGroundEvap_dTGround/LH_vap ! (kg m-2 s-1 K-1) - - ! compute the cross derivative terms (only related to turbulent fluxes; specifically canopy evaporation and transpiration) - dCanopyNetFlux_dCanLiq = dTurbFluxCanopy_dCanLiq ! derivative in net canopy fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dGroundNetFlux_dCanLiq = dTurbFluxGround_dCanLiq ! derivative in net ground fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - - !print*, (ix_fDerivMeth == numerical) - !print*, 'dGroundNetFlux_dCanairTemp = ', dGroundNetFlux_dCanairTemp - !print*, 'dCanopyNetFlux_dCanopyTemp = ', dCanopyNetFlux_dCanopyTemp - !print*, 'dGroundNetFlux_dCanopyTemp = ', dGroundNetFlux_dCanopyTemp - !print*, 'dCanopyNetFlux_dGroundTemp = ', dCanopyNetFlux_dGroundTemp - !print*, 'dGroundNetFlux_dGroundTemp = ', dGroundNetFlux_dGroundTemp - !print*, 'dLWNetCanopy_dTGround = ', dLWNetCanopy_dTGround - !print*, 'dTurbFluxCanopy_dTGround = ', dTurbFluxCanopy_dTGround - !pause - - ! * check - case default; err=10; message=trim(message)//'unable to identify upper boundary condition for thermodynamics'; return - - ! end case statement - end select ! upper boundary condition for thermodynamics - - ! return liquid fluxes (needed for coupling) - returnCanopyTranspiration = scalarCanopyTranspiration ! canopy transpiration (kg m-2 s-1) - returnCanopyEvaporation = scalarCanopyEvaporation ! canopy evaporation/condensation (kg m-2 s-1) - returnGroundEvaporation = scalarGroundEvaporation ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - - ! end associations - end associate - - - end subroutine vegNrgFluxFida - - ! ******************************************************************************************************* - ! private subroutine wetFraction: compute fraction of canopy covered with liquid water - ! ******************************************************************************************************* - subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFactor,canopyWettingExp,canopyWetFraction,canopyWetFractionDeriv) - implicit none - ! dummy variables - logical(lgt),intent(in) :: derDesire ! flag to denote if analytical derivatives are desired - logical(lgt),intent(in) :: smoothing ! flag to denote if smoothing is required - real(rkind),intent(in) :: canopyLiq ! liquid water content (kg m-2) - real(rkind),intent(in) :: canopyMax ! liquid water content (kg m-2) - real(rkind),intent(in) :: canopyWettingFactor ! maximum wetted fraction of the canopy (-) - real(rkind),intent(in) :: canopyWettingExp ! exponent in canopy wetting function (-) - - real(rkind),intent(out) :: canopyWetFraction ! canopy wetted fraction (-) - real(rkind),intent(out) :: canopyWetFractionDeriv ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) - ! local variables - real(rkind) :: relativeCanopyWater ! water stored on vegetation canopy, expressed as a fraction of maximum storage (-) - real(rkind) :: rawCanopyWetFraction ! initial value of the canopy wet fraction (before smoothing) - real(rkind) :: rawWetFractionDeriv ! derivative in canopy wet fraction w.r.t. storage (kg-1 m2) - real(rkind) :: smoothFunc ! smoothing function used to improve numerical stability at times with limited water storage (-) - real(rkind) :: smoothFuncDeriv ! derivative in the smoothing function w.r.t.canopy storage (kg-1 m2) - real(rkind) :: verySmall=epsilon(1._rkind) ! a very small number - ! -------------------------------------------------------------------------------------------------------------- - - ! compute relative canopy water - relativeCanopyWater = canopyLiq/canopyMax - !write(*,'(a,1x,e20.10,1x,2(f20.10,1x))') 'relativeCanopyWater, canopyLiq, canopyMax = ', relativeCanopyWater, canopyLiq, canopyMax - - ! compute an initial value of the canopy wet fraction - ! - canopy below value where canopy is 100% wet - if(relativeCanopyWater < 1._rkind)then - rawCanopyWetFraction = canopyWettingFactor*(relativeCanopyWater**canopyWettingExp) - if(derDesire .and. relativeCanopyWater>verySmall)then - rawWetFractionDeriv = (canopyWettingFactor*canopyWettingExp/canopyMax)*relativeCanopyWater**(canopyWettingExp - 1._rkind) - else - rawWetFractionDeriv = 0._rkind - end if - - ! - canopy is at capacity (canopyWettingFactor) - else - rawCanopyWetFraction = canopyWettingFactor - rawWetFractionDeriv = 0._rkind - end if - - ! smooth canopy wetted fraction - if(smoothing)then - call logisticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) - canopyWetFraction = rawCanopyWetFraction*smoothFunc ! logistic smoother - else - canopyWetFraction = rawCanopyWetFraction - canopyWetFractionDeriv = rawWetFractionDeriv - end if - - ! compute derivative (product rule) - if(derDesire .and. smoothing)then ! NOTE: raw derivative is used if not smoothing - canopyWetFractionDeriv = rawWetFractionDeriv*smoothFunc + rawCanopyWetFraction*smoothFuncDeriv - else - canopyWetFractionDeriv = 0._rkind - end if - - end subroutine wetFraction - - - ! ******************************************************************************************************* - ! private subroutine logisticSmoother: compute the smoothing function - ! ******************************************************************************************************* - subroutine logisticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) - implicit none - ! dummy variables - logical(lgt),intent(in) :: derDesire ! flag to denote if analytical derivatives are desired - real(rkind),intent(in) :: canopyLiq ! liquid water content (kg m-2) - real(rkind),intent(out) :: smoothFunc ! smoothing function (-) - real(rkind),intent(out) :: smoothFuncDeriv ! derivative in smoothing function (kg-1 m-2) - ! local variables - real(rkind) :: xArg ! argument used in the smoothing function (-) - real(rkind) :: expX ! exp(-xArg) -- used multiple times - real(rkind),parameter :: smoothThresh=0.01_rkind ! mid-point of the smoothing function (kg m-2) - real(rkind),parameter :: smoothScale=0.001_rkind ! scaling factor for the smoothing function (kg m-2) - real(rkind),parameter :: xLimit=50._rkind ! don't compute exponents for > xLimit - ! -------------------------------------------------------------------------------------------------------------- - ! compute argument in the smoothing function - xArg = (canopyLiq - smoothThresh)/smoothScale - - ! only compute smoothing function for small exponents - if(xArg > -xLimit .and. xArg < xLimit)then ! avoid huge exponents - expX = exp(-xarg) ! (also used in the derivative) - smoothFunc = 1._rkind / (1._rkind + expX) ! (logistic smoother) - if(derDesire)then - smoothFuncDeriv = expX / (smoothScale * (1._rkind + expX)**2._rkind) ! (derivative in the smoothing function) - else - smoothFuncDeriv = 0._rkind - end if - - ! outside limits: special case of smooth exponents - else - if(xArg < 0._rkind)then; smoothFunc = 0._rkind ! xArg < -xLimit - else; smoothFunc = 1._rkind ! xArg > xLimit - end if - smoothFuncDeriv = 0._rkind - end if ! check for huge exponents - - end subroutine logisticSmoother - ! -------------------------------------------------------------------------------------------------------------- - - - ! ******************************************************************************************************* - ! private subroutine longwaveBal: compute longwave radiation balance at the canopy and ground surface - ! ******************************************************************************************************* - subroutine longwaveBal(& - ! input: model control - ixDerivMethod, & ! intent(in): choice of method used to compute derivative (analytical or numerical) - computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation - ! input: canopy and ground temperature - canopyTemp, & ! intent(in): canopy temperature (K) - groundTemp, & ! intent(in): ground temperature (K) - ! input: canopy and ground emissivity - emc, & ! intent(in): canopy emissivity (-) - emg, & ! intent(in): ground emissivity (-) - ! input: forcing - LWRadUbound, & ! intent(in): downwelling longwave radiation at the upper boundary (W m-2) - ! output: sources - LWRadCanopy, & ! intent(out): longwave radiation emitted from the canopy (W m-2) - LWRadGround, & ! intent(out): longwave radiation emitted at the ground surface (W m-2) - ! output: individual fluxes - LWRadUbound2Canopy, & ! intent(out): downward atmospheric longwave radiation absorbed by the canopy (W m-2) - LWRadUbound2Ground, & ! intent(out): downward atmospheric longwave radiation absorbed by the ground (W m-2) - LWRadUbound2Ubound, & ! intent(out): atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) - LWRadCanopy2Ubound, & ! intent(out): longwave radiation emitted from canopy lost thru upper boundary (W m-2) - LWRadCanopy2Ground, & ! intent(out): longwave radiation emitted from canopy absorbed by the ground (W m-2) - LWRadCanopy2Canopy, & ! intent(out): canopy longwave reflected from ground and absorbed by the canopy (W m-2) - LWRadGround2Ubound, & ! intent(out): longwave radiation emitted from ground lost thru upper boundary (W m-2) - LWRadGround2Canopy, & ! intent(out): longwave radiation emitted from ground and absorbed by the canopy (W m-2) - ! output: net fluxes - LWNetCanopy, & ! intent(out): net longwave radiation at the canopy (W m-2) - LWNetGround, & ! intent(out): net longwave radiation at the ground surface (W m-2) - LWNetUbound, & ! intent(out): net longwave radiation at the upper boundary (W m-2) - ! output: flux derivatives - dLWNetCanopy_dTCanopy, & ! intent(out): derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) - dLWNetGround_dTGround, & ! intent(out): derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetCanopy_dTGround, & ! intent(out): derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetGround_dTCanopy, & ! intent(out): derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) - ! output: error control - err,message ) ! intent(out): error control - ! ----------------------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input: model control - integer(i4b),intent(in) :: ixDerivMethod ! choice of method used to compute derivative (analytical or numerical) - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - ! input: canopy and ground temperature - real(rkind),intent(in) :: canopyTemp ! canopy temperature (K) - real(rkind),intent(in) :: groundTemp ! ground temperature (K) - ! input: canopy and ground emissivity - real(rkind),intent(in) :: emc ! canopy emissivity (-) - real(rkind),intent(in) :: emg ! ground emissivity (-) - ! input: forcing - real(rkind),intent(in) :: LWRadUbound ! downwelling longwave radiation at the upper boundary (W m-2) - ! output: sources - real(rkind),intent(out) :: LWRadCanopy ! longwave radiation emitted from the canopy (W m-2) - real(rkind),intent(out) :: LWRadGround ! longwave radiation emitted at the ground surface (W m-2) - ! output: individual fluxes - real(rkind),intent(out) :: LWRadUbound2Canopy ! downward atmospheric longwave radiation absorbed by the canopy (W m-2) - real(rkind),intent(out) :: LWRadUbound2Ground ! downward atmospheric longwave radiation absorbed by the ground (W m-2) - real(rkind),intent(out) :: LWRadUbound2Ubound ! atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) - real(rkind),intent(out) :: LWRadCanopy2Ubound ! longwave radiation emitted from canopy lost thru upper boundary (W m-2) - real(rkind),intent(out) :: LWRadCanopy2Ground ! longwave radiation emitted from canopy absorbed by the ground (W m-2) - real(rkind),intent(out) :: LWRadCanopy2Canopy ! canopy longwave reflected from ground and absorbed by the canopy (W m-2) - real(rkind),intent(out) :: LWRadGround2Ubound ! longwave radiation emitted from ground lost thru upper boundary (W m-2) - real(rkind),intent(out) :: LWRadGround2Canopy ! longwave radiation emitted from ground and absorbed by the canopy (W m-2) - ! output: net fluxes - real(rkind),intent(out) :: LWNetCanopy ! net longwave radiation at the canopy (W m-2) - real(rkind),intent(out) :: LWNetGround ! net longwave radiation at the ground surface (W m-2) - real(rkind),intent(out) :: LWNetUbound ! net longwave radiation at the upper boundary (W m-2) - ! output: flux derivatives - real(rkind),intent(out) :: dLWNetCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dLWNetGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) - real(rkind),intent(out) :: dLWNetCanopy_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) - real(rkind),intent(out) :: dLWNetGround_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ----------------------------------------------------------------------------------------------------------------------------------------------- - ! local variables - integer(i4b),parameter :: unperturbed=1 ! named variable to identify the case of unperturbed state variables - integer(i4b),parameter :: perturbStateCanopy=2 ! named variable to identify the case where we perturb the canopy temperature - integer(i4b),parameter :: perturbStateGround=3 ! named variable to identify the case where we perturb the ground temperature - integer(i4b) :: itry ! index of flux evaluation - integer(i4b) :: nFlux ! number of flux evaluations - real(rkind) :: TCan ! value of canopy temperature used in flux calculations (may be perturbed) - real(rkind) :: TGnd ! value of ground temperature used in flux calculations (may be perturbed) - real(rkind) :: fluxBalance ! check energy closure (W m-2) - real(rkind),parameter :: fluxTolerance=1.e-10_rkind ! tolerance for energy closure (W m-2) - real(rkind) :: dLWRadCanopy_dTCanopy ! derivative in emitted radiation at the canopy w.r.t. canopy temperature - real(rkind) :: dLWRadGround_dTGround ! derivative in emitted radiation at the ground w.r.t. ground temperature - real(rkind) :: LWNetCanopy_dStateCanopy ! net lw canopy flux after perturbation in canopy temperature - real(rkind) :: LWNetGround_dStateCanopy ! net lw ground flux after perturbation in canopy temperature - real(rkind) :: LWNetCanopy_dStateGround ! net lw canopy flux after perturbation in ground temperature - real(rkind) :: LWNetGround_dStateGround ! net lw ground flux after perturbation in ground temperature - ! ----------------------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='longwaveBal/' - - ! check the need to compute numerical derivatives - if(ixDerivMethod==numerical)then - nFlux=3 ! compute the derivatives using one-sided finite differences - else - nFlux=1 ! compute analytical derivatives - end if - - ! either one or multiple flux calls, depending on if using analytical or numerical derivatives - do itry=nFlux,1,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) - - !print*, 'perturbation: ', (itry==unperturbed), (itry==perturbStateCanopy), (itry==perturbStateGround) - - ! ------------------------------------------------------------------------------------- - ! state perturbations for numerical deriavtives with one-sided finite differences - ! note: no perturbations performed using analytical derivatives (nFlux=1) - ! ------------------------------------------------------------------------------------- - - ! identify the type of perturbation - select case(itry) - - ! un-perturbed case - case(unperturbed) - TCan = canopyTemp - TGnd = groundTemp - - ! perturb canopy temperature - case(perturbStateCanopy) - TCan = canopyTemp + dx - TGnd = groundTemp - - ! perturb ground temperature - case(perturbStateGround) - TCan = canopyTemp - TGnd = groundTemp + dx - - ! check for an unknown perturbation - case default; err=10; message=trim(message)//"unknown perturbation"; return - - end select ! (type of perturbation) - - ! ------------------------------------------------------------------------------------- - ! calculation block (unperturbed fluxes returned [computed last]) - ! ------------------------------------------------------------------------------------- - ! NOTE: emc should be set to zero when not computing canopy fluxes - - ! compute longwave fluxes from canopy and the ground - if(computeVegFlux)then - LWRadCanopy = emc*sb*TCan**4._rkind ! longwave radiation emitted from the canopy (W m-2) - else - LWRadCanopy = 0._rkind - end if - LWRadGround = emg*sb*TGnd**4._rkind ! longwave radiation emitted at the ground surface (W m-2) - - ! compute fluxes originating from the atmosphere - LWRadUbound2Canopy = (emc + (1._rkind - emc)*(1._rkind - emg)*emc)*LWRadUbound ! downward atmospheric longwave radiation absorbed by the canopy (W m-2) - LWRadUbound2Ground = (1._rkind - emc)*emg*LWRadUbound ! downward atmospheric longwave radiation absorbed by the ground (W m-2) - LWRadUbound2Ubound = (1._rkind - emc)*(1._rkind - emg)*(1._rkind - emc)*LWRadUbound ! atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) - - ! compute fluxes originating from the canopy - LWRadCanopy2Ubound = (1._rkind + (1._rkind - emc)*(1._rkind - emg))*LWRadCanopy ! longwave radiation emitted from canopy lost thru upper boundary (W m-2) - LWRadCanopy2Ground = emg*LWRadCanopy ! longwave radiation emitted from canopy absorbed by the ground (W m-2) - LWRadCanopy2Canopy = emc*(1._rkind - emg)*LWRadCanopy ! canopy longwave reflected from ground and absorbed by the canopy (W m-2) - - ! compute fluxes originating from the ground surface - LWRadGround2Ubound = (1._rkind - emc)*LWRadGround ! longwave radiation emitted from ground lost thru upper boundary (W m-2) - LWRadGround2Canopy = emc*LWRadGround ! longwave radiation emitted from ground and absorbed by the canopy (W m-2) - - ! compute net longwave radiation (W m-2) - LWNetCanopy = LWRadUbound2Canopy + LWRadGround2Canopy + LWRadCanopy2Canopy - 2._rkind*LWRadCanopy ! canopy - LWNetGround = LWRadUbound2Ground + LWRadCanopy2Ground - LWRadGround ! ground surface - LWNetUbound = LWRadUbound - LWRadUbound2Ubound - LWRadCanopy2Ubound - LWRadGround2Ubound ! upper boundary - - !print*, 'LWRadCanopy = ', LWRadCanopy - !print*, 'LWRadGround = ', LWRadGround - - !print*, 'LWNetCanopy = ', LWNetCanopy - !print*, 'LWNetGround = ', LWNetGround - !print*, 'LWNetUbound = ', LWNetUbound - - ! check the flux balance - fluxBalance = LWNetUbound - (LWNetCanopy + LWNetGround) - if(abs(fluxBalance) > fluxTolerance)then - print*, 'fluxBalance = ', fluxBalance - print*, 'emg, emc = ', emg, emc - print*, 'TCan, TGnd = ', TCan, TGnd - print*, 'LWRadUbound = ', LWRadUbound - print*, 'LWRadCanopy = ', LWRadCanopy - print*, 'LWRadGround = ', LWRadGround - print*, 'LWRadUbound2Canopy = ', LWRadUbound2Canopy - print*, 'LWRadUbound2Ground = ', LWRadUbound2Ground - print*, 'LWRadUbound2Ubound = ', LWRadUbound2Ubound - print*, 'LWRadCanopy2Ubound = ', LWRadCanopy2Ubound - print*, 'LWRadCanopy2Ground = ', LWRadCanopy2Ground - print*, 'LWRadCanopy2Canopy = ', LWRadCanopy2Canopy - print*, 'LWRadGround2Ubound = ', LWRadGround2Ubound - print*, 'LWRadGround2Canopy = ', LWRadGround2Canopy - print*, 'LWNetCanopy = ', LWNetCanopy - print*, 'LWNetGround = ', LWNetGround - print*, 'LWNetUbound = ', LWNetUbound - message=trim(message)//'flux imbalance' - err=20; return - end if - - ! -------------------------------------------------------------------------------------- - ! save perturbed fluxes to calculate numerical derivatives (one-sided finite difference) - ! -------------------------------------------------------------------------------------- - if(ixDerivMethod==numerical)then - select case(itry) ! (select type of perturbation) - case(unperturbed); exit - case(perturbStateCanopy) - LWNetCanopy_dStateCanopy = LWNetCanopy - LWNetGround_dStateCanopy = LWNetGround - case(perturbStateGround) - LWNetCanopy_dStateGround = LWNetCanopy - LWNetGround_dStateGround = LWNetGround - case default; err=10; message=trim(message)//"unknown perturbation"; return - end select ! (type of perturbation) - end if ! (if numerical) - - end do ! looping through different perturbations - - ! ------------------------------------------------------------------------------------- - ! compute derivatives - ! ------------------------------------------------------------------------------------- - select case(ixDerivMethod) - - ! ***** analytical derivatives - case(analytical) - ! compute initial derivatives - dLWRadCanopy_dTCanopy = 4._rkind*emc*sb*TCan**3._rkind - dLWRadGround_dTGround = 4._rkind*emg*sb*TGnd**3._rkind - ! compute analytical derivatives - dLWNetCanopy_dTCanopy = (emc*(1._rkind - emg) - 2._rkind)*dLWRadCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) - dLWNetGround_dTGround = -dLWRadGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetCanopy_dTGround = emc*dLWRadGround_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetGround_dTCanopy = emg*dLWRadCanopy_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) - - ! ***** numerical derivatives - case(numerical) - ! compute numerical derivatives (one-sided finite differences) - dLWNetCanopy_dTCanopy = (LWNetCanopy_dStateCanopy - LWNetCanopy)/dx ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) - dLWNetGround_dTGround = (LWNetGround_dStateGround - LWNetGround)/dx ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetCanopy_dTGround = (LWNetCanopy_dStateGround - LWNetCanopy)/dx ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetGround_dTCanopy = (LWNetGround_dStateCanopy - LWNetGround)/dx ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) - - ! ***** error check - case default; err=10; message=trim(message)//"unknown method to calculate derivatives"; return - - end select ! (type of method to calculate derivatives) - - end subroutine longwaveBal - - - ! ******************************************************************************************************* - ! private subroutine aeroResist: compute aerodynamic resistances - ! ******************************************************************************************************* - subroutine aeroResist(& - ! input: model control - computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) - derivDesired, & ! intent(in): flag to indicate if analytical derivatives are desired - ixVegTraits, & ! intent(in): choice of parameterization for vegetation roughness length and displacement height - ixWindProfile, & ! intent(in): choice of canopy wind profile - ixStability, & ! intent(in): choice of stability function - ! input: above-canopy forcing data - mHeight, & ! intent(in): measurement height (m) - airtemp, & ! intent(in): air temperature at some height above the surface (K) - windspd, & ! intent(in): wind speed at some height above the surface (m s-1) - ! input: temperature (canopy, ground, canopy air space) - canairTemp, & ! intent(in): temperature of the canopy air space (K) - groundTemp, & ! intent(in): ground temperature (K) - ! input: diagnostic variables - exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) - snowDepth, & ! intent(in): snow depth (m) - ! input: parameters - z0Ground, & ! intent(in): roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) - z0CanopyParam, & ! intent(in): roughness length of the canopy (m) - zpdFraction, & ! intent(in): zero plane displacement / canopy height (-) - critRichNumber, & ! intent(in): critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam, & ! intent(in): parameter in Louis (1979) stability function - Mahrt87_eScale, & ! intent(in): exponential scaling factor in the Mahrt (1987) stability function - windReductionParam, & ! intent(in): canopy wind reduction parameter (-) - leafExchangeCoeff, & ! intent(in): turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) - leafDimension, & ! intent(in): characteristic leaf dimension (m) - heightCanopyTop, & ! intent(in): height at the top of the vegetation canopy (m) - heightCanopyBottom, & ! intent(in): height at the bottom of the vegetation canopy (m) - ! output: stability corrections - RiBulkCanopy, & ! intent(out): bulk Richardson number for the canopy (-) - RiBulkGround, & ! intent(out): bulk Richardson number for the ground surface (-) - canopyStabilityCorrection, & ! intent(out): stability correction for the canopy (-) - groundStabilityCorrection, & ! intent(out): stability correction for the ground surface (-) - ! output: scalar resistances - z0Canopy, & ! intent(out): roughness length of the canopy (m) - windReductionFactor, & ! intent(out): canopy wind reduction factor (-) - zeroPlaneDisplacement, & ! intent(out): zero plane displacement (m) - eddyDiffusCanopyTop, & ! intent(out): eddy diffusivity for heat at the top of the canopy (m2 s-1) - frictionVelocity, & ! intent(out): friction velocity (m s-1) - windspdCanopyTop, & ! intent(out): windspeed at the top of the canopy (m s-1) - windspdCanopyBottom, & ! intent(out): windspeed at the height of the bottom of the canopy (m s-1) - leafResistance, & ! intent(out): mean leaf boundary layer resistance per unit leaf area (s m-1) - groundResistance, & ! intent(out): below canopy aerodynamic resistance (s m-1) - canopyResistance, & ! intent(out): above canopy aerodynamic resistance (s m-1) - ! output: derivatives in scalar resistances - dGroundResistance_dTGround, & ! intent(out): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - dGroundResistance_dTCanopy, & ! intent(out): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - dGroundResistance_dTCanair, & ! intent(out): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - dCanopyResistance_dTCanopy, & ! intent(out): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - dCanopyResistance_dTCanair, & ! intent(out): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - ! output: error control - err,message ) ! intent(out): error control - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! compute aerodynamic resistances - ! Refs: Choudhury and Monteith (4-layer model for heat budget of homogenous surfaces; QJRMS, 1988) - ! Niu and Yang (Canopy effects on snow processes; JGR, 2004) - ! Mahat et al. (Below-canopy turbulence in a snowmelt model, WRR, 2012) - implicit none - ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) - logical(lgt),intent(in) :: derivDesired ! logical flag to indicate if analytical derivatives are desired - integer(i4b),intent(in) :: ixVegTraits ! choice of parameterization for vegetation roughness length and displacement height - integer(i4b),intent(in) :: ixWindProfile ! choice of canopy wind profile - integer(i4b),intent(in) :: ixStability ! choice of stability function - ! input: above-canopy forcing data - real(rkind),intent(in) :: mHeight ! measurement height (m) - real(rkind),intent(in) :: airtemp ! air temperature at some height above the surface (K) - real(rkind),intent(in) :: windspd ! wind speed at some height above the surface (m s-1) - ! input: temperature (canopy, ground, canopy air space) - real(rkind),intent(in) :: canairTemp ! temperature of the canopy air space (K) - real(rkind),intent(in) :: groundTemp ! ground temperature (K) - ! input: diagnostic variables - real(rkind),intent(in) :: exposedVAI ! exposed vegetation area index -- leaf plus stem (m2 m-2) - real(rkind),intent(in) :: snowDepth ! snow depth (m) - ! input: parameters - real(rkind),intent(in) :: z0Ground ! roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) - real(rkind),intent(in) :: z0CanopyParam ! roughness length of the canopy (m) - real(rkind),intent(in) :: zpdFraction ! zero plane displacement / canopy height (-) - real(rkind),intent(in) :: critRichNumber ! critical value for the bulk Richardson number where turbulence ceases (-) - real(rkind),intent(in) :: Louis79_bparam ! parameter in Louis (1979) stability function - real(rkind),intent(in) :: Mahrt87_eScale ! exponential scaling factor in the Mahrt (1987) stability function - real(rkind),intent(in) :: windReductionParam ! canopy wind reduction parameter (-) - real(rkind),intent(in) :: leafExchangeCoeff ! turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) - real(rkind),intent(in) :: leafDimension ! characteristic leaf dimension (m) - real(rkind),intent(in) :: heightCanopyTop ! height at the top of the vegetation canopy (m) - real(rkind),intent(in) :: heightCanopyBottom ! height at the bottom of the vegetation canopy (m) - ! output: stability corrections - real(rkind),intent(out) :: RiBulkCanopy ! bulk Richardson number for the canopy (-) - real(rkind),intent(out) :: RiBulkGround ! bulk Richardson number for the ground surface (-) - real(rkind),intent(out) :: canopyStabilityCorrection ! stability correction for the canopy (-) - real(rkind),intent(out) :: groundStabilityCorrection ! stability correction for the ground surface (-) - ! output: scalar resistances - real(rkind),intent(out) :: z0Canopy ! roughness length of the vegetation canopy (m) - real(rkind),intent(out) :: windReductionFactor ! canopy wind reduction factor (-) - real(rkind),intent(out) :: zeroPlaneDisplacement ! zero plane displacement (m) - real(rkind),intent(out) :: eddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) - real(rkind),intent(out) :: frictionVelocity ! friction velocity (m s-1) - real(rkind),intent(out) :: windspdCanopyTop ! windspeed at the top of the canopy (m s-1) - real(rkind),intent(out) :: windspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) - real(rkind),intent(out) :: leafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) - real(rkind),intent(out) :: groundResistance ! below canopy aerodynamic resistance (s m-1) - real(rkind),intent(out) :: canopyResistance ! above canopy aerodynamic resistance (s m-1) - ! output: derivatives in scalar resistances - real(rkind),intent(out) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - real(rkind),intent(out) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind),intent(out) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - real(rkind),intent(out) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind),intent(out) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! local variables: general - character(LEN=256) :: cmessage ! error message of downwind routine - ! local variables: vegetation roughness and dispalcement height - real(rkind),parameter :: oneThird=1._rkind/3._rkind ! 1/3 - real(rkind),parameter :: twoThirds=2._rkind/3._rkind ! 2/3 - real(rkind),parameter :: C_r = 0.3 ! roughness element drag coefficient (-) from Raupach (BLM, 1994) - real(rkind),parameter :: C_s = 0.003_rkind ! substrate surface drag coefficient (-) from Raupach (BLM, 1994) - real(rkind),parameter :: approxDragCoef_max = 0.3_rkind ! maximum value of the approximate drag coefficient (-) from Raupach (BLM, 1994) - real(rkind),parameter :: psi_h = 0.193_rkind ! roughness sub-layer influence function (-) from Raupach (BLM, 1994) - real(rkind),parameter :: c_d1 = 7.5_rkind ! scaling parameter used to define displacement height (-) from Raupach (BLM, 1994) - real(rkind),parameter :: cd_CM = 0.2_rkind ! mean drag coefficient for individual leaves (-) from Choudhury and Monteith (QJRMS, 1988) - real(rkind) :: funcLAI ! temporary variable to calculate zero plane displacement for the canopy - real(rkind) :: fracCanopyHeight ! zero plane displacement expressed as a fraction of canopy height - real(rkind) :: approxDragCoef ! approximate drag coefficient used in the computation of canopy roughness length (-) - ! local variables: resistance - real(rkind) :: canopyExNeut ! surface-atmosphere exchange coefficient under neutral conditions (-) - real(rkind) :: groundExNeut ! surface-atmosphere exchange coefficient under neutral conditions (-) - real(rkind) :: sfc2AtmExchangeCoeff_canopy ! surface-atmosphere exchange coefficient after stability corrections (-) - real(rkind) :: groundResistanceNeutral ! ground resistance under neutral conditions (s m-1) - real(rkind) :: windConvFactor_fv ! factor to convert friction velocity to wind speed at top of canopy (-) - real(rkind) :: windConvFactor ! factor to convert wind speed at top of canopy to wind speed at a given height in the canopy (-) - real(rkind) :: referenceHeight ! z0Canopy+zeroPlaneDisplacement (m) - real(rkind) :: windspdRefHeight ! windspeed at the reference height (m/s) - real(rkind) :: heightAboveGround ! height above the snow surface (m) - real(rkind) :: heightCanopyTopAboveSnow ! height at the top of the vegetation canopy relative to snowpack (m) - real(rkind) :: heightCanopyBottomAboveSnow ! height at the bottom of the vegetation canopy relative to snowpack (m) - real(rkind),parameter :: xTolerance=0.1_rkind ! tolerance to handle the transition from exponential to log-below canopy - ! local variables: derivatives - real(rkind) :: dFV_dT ! derivative in friction velocity w.r.t. canopy air temperature - real(rkind) :: dED_dT ! derivative in eddy diffusivity at the top of the canopy w.r.t. canopy air temperature - real(rkind) :: dGR_dT ! derivative in neutral ground resistance w.r.t. canopy air temperature - real(rkind) :: tmp1,tmp2 ! temporary variables used in calculation of ground resistance - real(rkind) :: dCanopyStabilityCorrection_dRich ! derivative in stability correction w.r.t. Richardson number for the canopy (-) - real(rkind) :: dGroundStabilityCorrection_dRich ! derivative in stability correction w.r.t. Richardson number for the ground surface (-) - real(rkind) :: dCanopyStabilityCorrection_dAirTemp ! (not used) derivative in stability correction w.r.t. air temperature (K-1) - real(rkind) :: dGroundStabilityCorrection_dAirTemp ! (not used) derivative in stability correction w.r.t. air temperature (K-1) - real(rkind) :: dCanopyStabilityCorrection_dCasTemp ! derivative in canopy stability correction w.r.t. canopy air space temperature (K-1) - real(rkind) :: dGroundStabilityCorrection_dCasTemp ! derivative in ground stability correction w.r.t. canopy air space temperature (K-1) - real(rkind) :: dGroundStabilityCorrection_dSfcTemp ! derivative in ground stability correction w.r.t. surface temperature (K-1) - real(rkind) :: singleLeafConductance ! leaf boundary layer conductance (m s-1) - real(rkind) :: canopyLeafConductance ! leaf boundary layer conductance -- scaled up to the canopy (m s-1) - real(rkind) :: leaf2CanopyScaleFactor ! factor to scale from the leaf to the canopy [m s-(1/2)] - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='aeroResist/' - - ! check that measurement height is above the top of the canopy - if(mHeight < heightCanopyTop)then - err=20; message=trim(message)//'measurement height is below the top of the canopy'; return - end if - - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! * compute vegetation poperties (could be done at the same time as phenology.. does not have to be in the flux routine!) - if(computeVegFlux) then ! (if vegetation is exposed) - - ! ***** identify zero plane displacement, roughness length, and surface temperature for the canopy (m) - ! First, calculate new coordinate system above snow - use these to scale wind profiles and resistances - ! NOTE: the new coordinate system makes zeroPlaneDisplacement and z0Canopy consistent - heightCanopyTopAboveSnow = heightCanopyTop - snowDepth - heightCanopyBottomAboveSnow = max(heightCanopyBottom - snowDepth, 0.0_rkind) - select case(ixVegTraits) - - ! Raupach (BLM 1994) "Simplified expressions..." - case(Raupach_BLM1994) - ! (compute zero-plane displacement) - funcLAI = sqrt(c_d1*exposedVAI) - fracCanopyHeight = -(1._rkind - exp(-funcLAI))/funcLAI + 1._rkind - zeroPlaneDisplacement = fracCanopyHeight*(heightCanopyTopAboveSnow-heightCanopyBottomAboveSnow)+heightCanopyBottomAboveSnow - ! (coupute roughness length of the veg canopy) - approxDragCoef = min( sqrt(C_s + C_r*exposedVAI/2._rkind), approxDragCoef_max) - z0Canopy = (1._rkind - fracCanopyHeight) * exp(-vkc*approxDragCoef - psi_h) * (heightCanopyTopAboveSnow-heightCanopyBottomAboveSnow) - - ! Choudhury and Monteith (QJRMS 1988) "A four layer model for the heat budget..." - case(CM_QJRMS1988) - funcLAI = cd_CM*exposedVAI - zeroPlaneDisplacement = 1.1_rkind*heightCanopyTopAboveSnow*log(1._rkind + funcLAI**0.25_rkind) - if(funcLAI < 0.2_rkind)then - z0Canopy = z0Ground + 0.3_rkind*heightCanopyTopAboveSnow*funcLAI**0.5_rkind - else - z0Canopy = 0.3_rkind*heightCanopyTopAboveSnow*(1._rkind - zeroPlaneDisplacement/heightCanopyTopAboveSnow) - end if - - ! constant parameters dependent on the vegetation type - case(vegTypeTable) - zeroPlaneDisplacement = zpdFraction*heightCanopyTopAboveSnow ! zero-plane displacement (m) - z0Canopy = z0CanopyParam ! roughness length of the veg canopy (m) - - ! check - case default - err=10; message=trim(message)//"unknown parameterization for vegetation roughness length and displacement height"; return - - end select ! vegetation traits (z0, zpd) - - ! check zero plane displacement - if(zeroPlaneDisplacement < heightCanopyBottomAboveSnow)then - write(*,'(a,1x,10(f12.5,1x))') 'heightCanopyTop, snowDepth, heightCanopyTopAboveSnow, heightCanopyBottomAboveSnow, exposedVAI = ', & - heightCanopyTop, snowDepth, heightCanopyTopAboveSnow, heightCanopyBottomAboveSnow, exposedVAI - message=trim(message)//'zero plane displacement is below the canopy bottom' - err=20; return - endif - - ! check measurement height - if(mHeight < zeroPlaneDisplacement)then; err=20; message=trim(message)//'measurement height is below the displacement height'; return; end if - if(mHeight < z0Canopy)then; err=20; message=trim(message)//'measurement height is below the roughness length'; return; end if - - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! * compute resistance for the case where the canopy is exposed - ! compute the stability correction for resistance from canopy air space to air above the canopy (-) - call aStability(& - ! input - derivDesired, & ! input: logical flag to compute analytical derivatives - ixStability, & ! input: choice of stability function - ! input: forcing data, diagnostic and state variables - mHeight, & ! input: measurement height (m) - airTemp, & ! input: air temperature above the canopy (K) - canairTemp, & ! input: temperature of the canopy air space (K) - windspd, & ! input: wind speed above the canopy (m s-1) - ! input: stability parameters - critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam, & ! input: parameter in Louis (1979) stability function - Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function - ! output - RiBulkCanopy, & ! output: bulk Richardson number (-) - canopyStabilityCorrection, & ! output: stability correction for turbulent heat fluxes (-) - dCanopyStabilityCorrection_dRich, & ! output: derivative in stability correction w.r.t. Richardson number for the canopy (-) - dCanopyStabilityCorrection_dAirTemp, & ! output: (not used) derivative in stability correction w.r.t. air temperature (K-1) - dCanopyStabilityCorrection_dCasTemp, & ! output: derivative in stability correction w.r.t. canopy air space temperature (K-1) - err, cmessage ) ! output: error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - ! compute turbulent exchange coefficient (-) - canopyExNeut = (vkc**2._rkind) / ( log((mHeight - zeroPlaneDisplacement)/z0Canopy))**2._rkind ! coefficient under conditions of neutral stability - sfc2AtmExchangeCoeff_canopy = canopyExNeut*canopyStabilityCorrection ! after stability corrections - - ! compute the friction velocity (m s-1) - frictionVelocity = windspd * sqrt(sfc2AtmExchangeCoeff_canopy) - - ! compute the above-canopy resistance (s m-1) - canopyResistance = 1._rkind/(sfc2AtmExchangeCoeff_canopy*windspd) - if(canopyResistance < 0._rkind)then; err=20; message=trim(message)//'canopy resistance < 0'; return; end if - - ! compute windspeed at the top of the canopy above snow depth (m s-1) - ! NOTE: stability corrections cancel out - windConvFactor_fv = log((heightCanopyTopAboveSnow - zeroPlaneDisplacement)/z0Canopy) / log((mHeight - snowDepth - zeroPlaneDisplacement)/z0Canopy) - windspdCanopyTop = windspd*windConvFactor_fv - - ! compute the windspeed reduction - ! Refs: Norman et al. (Ag. Forest Met., 1995) -- citing Goudriaan (1977 manuscript "crop micrometeorology: a simulation study", Wageningen). - windReductionFactor = windReductionParam * exposedVAI**twoThirds * (heightCanopyTopAboveSnow - heightCanopyBottomAboveSnow)**oneThird / leafDimension**oneThird - - ! compute windspeed at the height z0Canopy+zeroPlaneDisplacement (m s-1) - referenceHeight = z0Canopy+zeroPlaneDisplacement - windConvFactor = exp(-windReductionFactor*(1._rkind - (referenceHeight/heightCanopyTopAboveSnow))) - windspdRefHeight = windspdCanopyTop*windConvFactor - - ! compute windspeed at the bottom of the canopy relative to the snow depth (m s-1) - windConvFactor = exp(-windReductionFactor*(1._rkind - (heightCanopyBottomAboveSnow/heightCanopyTopAboveSnow))) - windspdCanopyBottom = windspdCanopyTop*windConvFactor - - ! compute the leaf boundary layer resistance (s m-1) - singleLeafConductance = leafExchangeCoeff*sqrt(windspdCanopyTop/leafDimension) - leaf2CanopyScaleFactor = (2._rkind/windReductionFactor) * (1._rkind - exp(-windReductionFactor/2._rkind)) ! factor to scale from the leaf to the canopy - canopyLeafConductance = singleLeafConductance*leaf2CanopyScaleFactor - leafResistance = 1._rkind/(canopyLeafConductance) - if(leafResistance < 0._rkind)then; err=20; message=trim(message)//'leaf resistance < 0'; return; end if - - ! compute eddy diffusivity for heat at the top of the canopy (m2 s-1) - ! Note: use of friction velocity here includes stability adjustments - ! Note: max used to avoid dividing by zero - eddyDiffusCanopyTop = max(vkc*FrictionVelocity*(heightCanopyTopAboveSnow - zeroPlaneDisplacement), mpe) - - ! compute the resistance between the surface and canopy air UNDER NEUTRAL CONDITIONS (s m-1) - - ! case 1: assume exponential profile extends from the snow depth plus surface roughness length to the displacement height plus vegetation roughness - if(ixWindProfile==exponential .or. heightCanopyBottomAboveSnowz0Ground+xTolerance - else - - ! compute the neutral ground resistance - ! (first, component between heightCanopyBottomAboveSnow and z0Canopy+zeroPlaneDisplacement) - tmp1 = exp(-windReductionFactor* heightCanopyBottomAboveSnow/heightCanopyTopAboveSnow) - tmp2 = exp(-windReductionFactor*(z0Canopy+zeroPlaneDisplacement)/heightCanopyTopAboveSnow) - groundResistanceNeutral = ( heightCanopyTopAboveSnow*exp(windReductionFactor) / (windReductionFactor*eddyDiffusCanopyTop) ) * (tmp1 - tmp2) - ! (add log-below-canopy component) - groundResistanceNeutral = groundResistanceNeutral + (1._rkind/(max(0.1_rkind,windspdCanopyBottom)*vkc**2._rkind))*(log(heightCanopyBottomAboveSnow/z0Ground))**2._rkind - - endif ! switch between exponential profile and log-below-canopy - - ! compute the stability correction for resistance from the ground to the canopy air space (-) - ! NOTE: here we are interested in the windspeed at height z0Canopy+zeroPlaneDisplacement - call aStability(& - ! input - derivDesired, & ! input: logical flag to compute analytical derivatives - ixStability, & ! input: choice of stability function - ! input: forcing data, diagnostic and state variables - referenceHeight, & ! input: height of the canopy air space temperature/wind (m) - canairTemp, & ! input: temperature of the canopy air space (K) - groundTemp, & ! input: temperature of the ground surface (K) - max(0.1_rkind,windspdRefHeight), & ! input: wind speed at height z0Canopy+zeroPlaneDisplacement (m s-1) - ! input: stability parameters - critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam, & ! input: parameter in Louis (1979) stability function - Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function - ! output - RiBulkGround, & ! output: bulk Richardson number (-) - groundStabilityCorrection, & ! output: stability correction for turbulent heat fluxes (-) - dGroundStabilityCorrection_dRich, & ! output: derivative in stability correction w.r.t. Richardson number for the canopy (-) - dGroundStabilityCorrection_dCasTemp, & ! output: derivative in stability correction w.r.t. canopy air space temperature (K-1) - dGroundStabilityCorrection_dSfcTemp, & ! output: derivative in stability correction w.r.t. surface temperature (K-1) - err, cmessage ) ! output: error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - ! compute the ground resistance - groundResistance = groundResistanceNeutral / groundStabilityCorrection - if(groundResistance < 0._rkind)then; err=20; message=trim(message)//'ground resistance < 0 [vegetation is present]'; return; end if - - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! * compute resistance for the case without a canopy (bare ground, or canopy completely buried with snow) - else - - ! no canopy, so set huge resistances (not used) - canopyResistance = 1.e12_rkind ! not used: huge resistance, so conductance is essentially zero - leafResistance = 1.e12_rkind ! not used: huge resistance, so conductance is essentially zero - - ! check that measurement height above the ground surface is above the roughness length - if(mHeight < snowDepth+z0Ground)then; err=20; message=trim(message)//'measurement height < snow depth + roughness length'; return; end if - - ! compute the resistance between the surface and canopy air UNDER NEUTRAL CONDITIONS (s m-1) - groundExNeut = (vkc**2._rkind) / ( log((mHeight - snowDepth)/z0Ground)**2._rkind) ! turbulent transfer coefficient under conditions of neutral stability (-) - groundResistanceNeutral = 1._rkind / (groundExNeut*windspd) - - ! define height above the snow surface - heightAboveGround = mHeight - snowDepth - - ! check that measurement height above the ground surface is above the roughness length - if(heightAboveGround < z0Ground)then - print*, 'z0Ground = ', z0Ground - print*, 'mHeight = ', mHeight - print*, 'snowDepth = ', snowDepth - print*, 'heightAboveGround = ', heightAboveGround - message=trim(message)//'height above ground < roughness length [likely due to snow accumulation]' - err=20; return - end if - - ! compute ground stability correction - call aStability(& - ! input - derivDesired, & ! input: logical flag to compute analytical derivatives - ixStability, & ! input: choice of stability function - ! input: forcing data, diagnostic and state variables - heightAboveGround, & ! input: measurement height above the ground surface (m) - airtemp, & ! input: temperature above the ground surface (K) - groundTemp, & ! input: trial value of surface temperature -- "surface" is either canopy or ground (K) - windspd, & ! input: wind speed above the ground surface (m s-1) - ! input: stability parameters - critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam, & ! input: parameter in Louis (1979) stability function - Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function - ! output - RiBulkGround, & ! output: bulk Richardson number (-) - groundStabilityCorrection, & ! output: stability correction for turbulent heat fluxes (-) - dGroundStabilityCorrection_dRich, & ! output: derivative in stability correction w.r.t. Richardson number for the ground surface (-) - dGroundStabilityCorrection_dAirTemp, & ! output: (not used) derivative in stability correction w.r.t. air temperature (K-1) - dGroundStabilityCorrection_dSfcTemp, & ! output: derivative in stability correction w.r.t. surface temperature (K-1) - err, cmessage ) ! output: error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - ! compute the ground resistance (after stability corrections) - groundResistance = groundResistanceNeutral/groundStabilityCorrection - if(groundResistance < 0._rkind)then; err=20; message=trim(message)//'ground resistance < 0 [no vegetation]'; return; end if - - ! set all canopy variables to missing (no canopy!) - z0Canopy = missingValue ! roughness length of the vegetation canopy (m) - RiBulkCanopy = missingValue ! bulk Richardson number for the canopy (-) - windReductionFactor = missingValue ! canopy wind reduction factor (-) - zeroPlaneDisplacement = missingValue ! zero plane displacement (m) - canopyStabilityCorrection = missingValue ! stability correction for the canopy (-) - eddyDiffusCanopyTop = missingValue ! eddy diffusivity for heat at the top of the canopy (m2 s-1) - frictionVelocity = missingValue ! friction velocity (m s-1) - windspdCanopyTop = missingValue ! windspeed at the top of the canopy (m s-1) - windspdCanopyBottom = missingValue ! windspeed at the height of the bottom of the canopy (m s-1) - - end if ! (if no canopy) - - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! * compute derivatives - if(derivDesired)then ! if analytical derivatives are desired - - ! derivatives for the vegetation canopy - if(computeVegFlux) then ! (if vegetation is exposed) - - ! ***** compute derivatives w.r.t. canopy temperature - ! NOTE: derivatives are zero because using canopy air space temperature - dCanopyResistance_dTCanopy = 0._rkind ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - dGroundResistance_dTCanopy = 0._rkind ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - - ! ***** compute derivatives w.r.t. ground temperature (s m-1 K-1) - dGroundResistance_dTGround = -(groundResistanceNeutral*dGroundStabilityCorrection_dSfcTemp)/(groundStabilityCorrection**2._rkind) - - ! ***** compute derivatives w.r.t. temperature of the canopy air space (s m-1 K-1) - ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - dCanopyResistance_dTCanair = -dCanopyStabilityCorrection_dCasTemp/(windspd*canopyExNeut*canopyStabilityCorrection**2._rkind) - ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - ! (compute derivative in NEUTRAL ground resistance w.r.t. canopy air temperature (s m-1 K-1)) - dFV_dT = windspd*canopyExNeut*dCanopyStabilityCorrection_dCasTemp/(sqrt(sfc2AtmExchangeCoeff_canopy)*2._rkind) ! d(frictionVelocity)/d(canopy air temperature) - dED_dT = dFV_dT*vkc*(heightCanopyTopAboveSnow - zeroPlaneDisplacement) ! d(eddyDiffusCanopyTop)d(canopy air temperature) - dGR_dT = -dED_dT*(tmp1 - tmp2)*heightCanopyTopAboveSnow*exp(windReductionFactor) / (windReductionFactor*eddyDiffusCanopyTop**2._rkind) ! d(groundResistanceNeutral)/d(canopy air temperature) - ! (stitch everything together -- product rule) - dGroundResistance_dTCanair = dGR_dT/groundStabilityCorrection - groundResistanceNeutral*dGroundStabilityCorrection_dCasTemp/(groundStabilityCorrection**2._rkind) - - ! ***** compute resistances for non-vegetated surfaces (e.g., snow) - else - - ! set canopy derivatives to zero (non-vegetated, remember) - dCanopyResistance_dTCanopy = 0._rkind - dGroundResistance_dTCanopy = 0._rkind - - ! compute derivatives for ground resistance - dGroundResistance_dTGround = -dGroundStabilityCorrection_dSfcTemp/(windspd*groundExNeut*groundStabilityCorrection**2._rkind) - - end if ! (switch between vegetated and non-vegetated surfaces) - - ! * analytical derivatives not desired - else - dGroundResistance_dTGround = missingValue - dGroundResistance_dTCanopy = missingValue - dCanopyResistance_dTCanopy = missingValue - end if - - ! test - !print*, 'dGroundResistance_dTGround = ', dGroundResistance_dTGround - !print*, 'dGroundResistance_dTCanopy = ', dGroundResistance_dTCanopy - !print*, 'dCanopyResistance_dTCanopy = ', dCanopyResistance_dTCanopy - !pause 'in aeroResist' - - end subroutine aeroResist - - - ! ******************************************************************************************************* - ! private subroutine soilResist: compute soil moisture factor controlling stomatal resistance - ! ******************************************************************************************************* - subroutine soilResist(& - ! input (model decisions) - ixSoilResist, & ! intent(in): choice of function for the soil moisture control on stomatal resistance - ixGroundwater, & ! intent(in): choice of groundwater representation - ! input (state variables) - mLayerMatricHead, & ! intent(in): matric head in each layer (m) - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water in each layer - scalarAquiferStorage, & ! intent(in): aquifer storage (m) - ! input (diagnostic variables) - mLayerRootDensity, & ! intent(in): root density in each layer (-) - scalarAquiferRootFrac, & ! intent(in): fraction of roots below the lowest unsaturated layer (-) - ! input (parameters) - plantWiltPsi, & ! intent(in): matric head at wilting point (m) - soilStressParam, & ! intent(in): parameter in the exponential soil stress function (-) - critSoilWilting, & ! intent(in): critical vol. liq. water content when plants are wilting (-) - critSoilTranspire, & ! intent(in): critical vol. liq. water content when transpiration is limited (-) - critAquiferTranspire, & ! intent(in): critical aquifer storage value when transpiration is limited (m) - ! output - wAvgTranspireLimitFac, & ! intent(out): weighted average of the transpiration limiting factor (-) - mLayerTranspireLimitFac, & ! intent(out): transpiration limiting factor in each layer (-) - aquiferTranspireLimitFac, & ! intent(out): transpiration limiting factor for the aquifer (-) - err,message) ! intent(out): error control - ! ----------------------------------------------------------------------------------------------------------------------------------------- - USE mDecisions_module, only: NoahType,CLM_Type,SiB_Type ! options for the choice of function for the soil moisture control on stomatal resistance - USE mDecisions_module, only: bigBucket ! named variable that defines the "bigBucket" groundwater parameterization - implicit none - ! input (model decisions) - integer(i4b),intent(in) :: ixSoilResist ! choice of function for the soil moisture control on stomatal resistance - integer(i4b),intent(in) :: ixGroundwater ! choice of groundwater representation - ! input (variables) - real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head in each layer (m) - real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid water in each layer (-) - real(rkind),intent(in) :: scalarAquiferStorage ! aquifer storage (m) - ! input (diagnostic variables) - real(rkind),intent(in) :: mLayerRootDensity(:) ! root density in each layer (-) - real(rkind),intent(in) :: scalarAquiferRootFrac ! fraction of roots below the lowest unsaturated layer (-) - ! input (parameters) - real(rkind),intent(in) :: plantWiltPsi ! matric head at wilting point (m) - real(rkind),intent(in) :: soilStressParam ! parameter in the exponential soil stress function (-) - real(rkind),intent(in) :: critSoilWilting ! critical vol. liq. water content when plants are wilting (-) - real(rkind),intent(in) :: critSoilTranspire ! critical vol. liq. water content when transpiration is limited (-) - real(rkind),intent(in) :: critAquiferTranspire ! critical aquifer storage value when transpiration is limited (m) - ! output - real(rkind),intent(out) :: wAvgTranspireLimitFac ! intent(out): weighted average of the transpiration limiting factor (-) - real(rkind),intent(out) :: mLayerTranspireLimitFac(:) ! intent(out): transpiration limiting factor in each layer (-) - real(rkind),intent(out) :: aquiferTranspireLimitFac ! intent(out): transpiration limiting factor for the aquifer (-) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! local variables - real(rkind) :: gx ! stress function for the soil layers - real(rkind),parameter :: verySmall=epsilon(gx) ! a very small number - integer(i4b) :: iLayer ! index of soil layer - ! initialize error control - err=0; message='soilResist/' - - ! ** compute the factor limiting transpiration for each soil layer (-) - wAvgTranspireLimitFac = 0._rkind ! (initialize the weighted average) - do iLayer=1,size(mLayerMatricHead) - ! compute the soil stress function - select case(ixSoilResist) - case(NoahType) ! thresholded linear function of volumetric liquid water content - gx = (mLayerVolFracLiq(iLayer) - critSoilWilting) / (critSoilTranspire - critSoilWilting) - case(CLM_Type) ! thresholded linear function of matric head - if(mLayerMatricHead(iLayer) > plantWiltPsi)then - gx = 1._rkind - mLayerMatricHead(iLayer)/plantWiltPsi - else - gx = 0._rkind - end if - case(SiB_Type) ! exponential of the log of matric head - if(mLayerMatricHead(iLayer) < 0._rkind)then ! (unsaturated) - gx = 1._rkind - exp( -soilStressParam * ( log(plantWiltPsi/mLayerMatricHead(iLayer)) ) ) - else ! (saturated) - gx = 1._rkind - end if - case default ! check identified the option - err=20; message=trim(message)//'cannot identify option for soil resistance'; return - end select - ! save the factor for the given layer (ensure between zero and one) - mLayerTranspireLimitFac(iLayer) = min( max(verySmall,gx), 1._rkind) - ! compute the weighted average (weighted by root density) - wAvgTranspireLimitFac = wAvgTranspireLimitFac + mLayerTranspireLimitFac(iLayer)*mLayerRootDensity(iLayer) - end do ! (looping through soil layers) - - ! ** compute the factor limiting evaporation in the aquifer - if(scalarAquiferRootFrac > verySmall)then - ! check that aquifer root fraction is allowed - if(ixGroundwater /= bigBucket)then - message=trim(message)//'aquifer evaporation only allowed for the big groundwater bucket -- increase the soil depth to account for roots' - err=20; return - end if - ! compute the factor limiting evaporation for the aquifer - aquiferTranspireLimitFac = min(scalarAquiferStorage/critAquiferTranspire, 1._rkind) - else ! (if there are roots in the aquifer) - aquiferTranspireLimitFac = 0._rkind - end if - - ! compute the weighted average (weighted by root density) - wAvgTranspireLimitFac = wAvgTranspireLimitFac + aquiferTranspireLimitFac*scalarAquiferRootFrac - - end subroutine soilResist - - - ! ******************************************************************************** - ! private subroutine turbFluxes: compute turbulent heat fluxes - ! ******************************************************************************** - subroutine turbFluxes(& - ! input: model control - computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) - ixDerivMethod, & ! intent(in): choice of method used to compute derivative (analytical or numerical) - ! input: above-canopy forcing data - airtemp, & ! intent(in): air temperature at some height above the surface (K) - airpres, & ! intent(in): air pressure of the air above the vegetation canopy (Pa) - VPair, & ! intent(in): vapor pressure of the air above the vegetation canopy (Pa) - ! input: latent heat of sublimation/vaporization - latHeatSubVapCanopy, & ! intent(in): latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) - latHeatSubVapGround, & ! intent(in): latent heat of sublimation/vaporization for the ground surface (J kg-1) - ! input: canopy and ground temperature - canairTemp, & ! intent(in): temperature of the canopy air space (K) - canopyTemp, & ! intent(in): canopy temperature (K) - groundTemp, & ! intent(in): ground temperature (K) - satVP_CanopyTemp, & ! intent(in): saturation vapor pressure at the temperature of the veg canopy (Pa) - satVP_GroundTemp, & ! intent(in): saturation vapor pressure at the temperature of the ground (Pa) - dSVPCanopy_dCanopyTemp, & ! intent(in): derivative in canopy saturation vapor pressure w.r.t. canopy temperature (Pa K-1) - dSVPGround_dGroundTemp, & ! intent(in): derivative in ground saturation vapor pressure w.r.t. ground temperature (Pa K-1) - ! input: diagnostic variables - exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) - canopyWetFraction, & ! intent(in): fraction of canopy that is wet [0-1] - dCanopyWetFraction_dWat, & ! intent(in): derivative in the canopy wetted fraction w.r.t. total water content (kg-1 m-2) - dCanopyWetFraction_dT, & ! intent(in): derivative in wetted fraction w.r.t. canopy temperature (K-1) - canopySunlitLAI, & ! intent(in): sunlit leaf area (-) - canopyShadedLAI, & ! intent(in): shaded leaf area (-) - soilRelHumidity, & ! intent(in): relative humidity in the soil pores [0-1] - soilResistance, & ! intent(in): resistance from the soil (s m-1) - leafResistance, & ! intent(in): mean leaf boundary layer resistance per unit leaf area (s m-1) - groundResistance, & ! intent(in): below canopy aerodynamic resistance (s m-1) - canopyResistance, & ! intent(in): above canopy aerodynamic resistance (s m-1) - stomResistSunlit, & ! intent(in): stomatal resistance for sunlit leaves (s m-1) - stomResistShaded, & ! intent(in): stomatal resistance for shaded leaves (s m-1) - ! input: derivatives in scalar resistances - dGroundResistance_dTGround, & ! intent(in): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - dGroundResistance_dTCanopy, & ! intent(in): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - dGroundResistance_dTCanair, & ! intent(in): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - dCanopyResistance_dTCanopy, & ! intent(in): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - dCanopyResistance_dTCanair, & ! intent(in): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - ! output: conductances (used to check derivative calculations) - leafConductance, & ! intent(out): leaf conductance (m s-1) - canopyConductance, & ! intent(out): canopy conductance (m s-1) - groundConductanceSH, & ! intent(out): ground conductance for sensible heat (m s-1) - groundConductanceLH, & ! intent(out): ground conductance for latent heat -- includes soil resistance (m s-1) - evapConductance, & ! intent(out): conductance for evaporation (m s-1) - transConductance, & ! intent(out): conductance for transpiration (m s-1) - totalConductanceSH, & ! intent(out): total conductance for sensible heat (m s-1) - totalConductanceLH, & ! intent(out): total conductance for latent heat (m s-1) - ! output: canopy air space variables - VP_CanopyAir, & ! intent(out): vapor pressure of the canopy air space (Pa) - ! output: fluxes from the vegetation canopy - senHeatCanopy, & ! intent(out): sensible heat flux from the canopy to the canopy air space (W m-2) - latHeatCanopyEvap, & ! intent(out): latent heat flux associated with evaporation from the canopy to the canopy air space (W m-2) - latHeatCanopyTrans, & ! intent(out): latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) - ! output: fluxes from non-vegetated surfaces (ground surface below vegetation, bare ground, or snow covered vegetation) - senHeatGround, & ! intent(out): sensible heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) - latHeatGround, & ! intent(out): latent heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) - ! output: total heat fluxes to the atmosphere - senHeatTotal, & ! intent(out): total sensible heat flux to the atmosphere (W m-2) - latHeatTotal, & ! intent(out): total latent heat flux to the atmosphere (W m-2) - ! output: net fluxes - turbFluxCanair, & ! intent(out): net turbulent heat fluxes at the canopy air space (W m-2) - turbFluxCanopy, & ! intent(out): net turbulent heat fluxes at the canopy (W m-2) - turbFluxGround, & ! intent(out): net turbulent heat fluxes at the ground surface (W m-2) - ! output: flux derivatives - dTurbFluxCanair_dTCanair, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxCanair_dTCanopy, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxCanair_dTGround, & ! intent(out): derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanair, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanopy, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxCanopy_dTGround, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxGround_dTCanair, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxGround_dTCanopy, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxGround_dTGround, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - ! output: liquid flux derivatives (canopy evap) - dLatHeatCanopyEvap_dCanLiq, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (J kg-1 s-1) - dLatHeatCanopyEvap_dTCanair, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTCanopy, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTGround, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) - ! output: liquid flux derivatives (ground evap) - dLatHeatGroundEvap_dCanLiq, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy liquid water content (J kg-1 s-1) - dLatHeatGroundEvap_dTCanair, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy air temperature - dLatHeatGroundEvap_dTCanopy, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy temperature - dLatHeatGroundEvap_dTGround, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. ground temperature - ! output: cross derivatives - dTurbFluxCanair_dCanLiq, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dTurbFluxCanopy_dCanLiq, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dTurbFluxGround_dCanLiq, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - ! output: error control - err,message ) ! intent(out): error control - ! ----------------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) - integer(i4b),intent(in) :: ixDerivMethod ! choice of method used to compute derivative (analytical or numerical) - ! input: above-canopy forcing data - real(rkind),intent(in) :: airtemp ! air temperature at some height above the surface (K) - real(rkind),intent(in) :: airpres ! air pressure of the air above the vegetation canopy (Pa) - real(rkind),intent(in) :: VPair ! vapor pressure of the air above the vegetation canopy (Pa) - ! input: latent heat of sublimation/vaporization - real(rkind),intent(in) :: latHeatSubVapCanopy ! latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) - real(rkind),intent(in) :: latHeatSubVapGround ! latent heat of sublimation/vaporization for the ground surface (J kg-1) - ! input: canopy and ground temperature - real(rkind),intent(in) :: canairTemp ! temperature of the canopy air space (K) - real(rkind),intent(in) :: canopyTemp ! canopy temperature (K) - real(rkind),intent(in) :: groundTemp ! ground temperature (K) - real(rkind),intent(in) :: satVP_CanopyTemp ! saturation vapor pressure at the temperature of the veg canopy (Pa) - real(rkind),intent(in) :: satVP_GroundTemp ! saturation vapor pressure at the temperature of the ground (Pa) - real(rkind),intent(in) :: dSVPCanopy_dCanopyTemp ! derivative in canopy saturation vapor pressure w.r.t. canopy temperature (Pa K-1) - real(rkind),intent(in) :: dSVPGround_dGroundTemp ! derivative in ground saturation vapor pressure w.r.t. ground temperature (Pa K-1) - ! input: diagnostic variables - real(rkind),intent(in) :: exposedVAI ! exposed vegetation area index -- leaf plus stem (m2 m-2) - real(rkind),intent(in) :: canopyWetFraction ! fraction of canopy that is wet [0-1] - real(rkind),intent(in) :: dCanopyWetFraction_dWat ! derivative in the canopy wetted fraction w.r.t. liquid water content (kg-1 m-2) - real(rkind),intent(in) :: dCanopyWetFraction_dT ! derivative in the canopy wetted fraction w.r.t. canopy temperature (K-1) - real(rkind),intent(in) :: canopySunlitLAI ! sunlit leaf area (-) - real(rkind),intent(in) :: canopyShadedLAI ! shaded leaf area (-) - real(rkind),intent(in) :: soilRelHumidity ! relative humidity in the soil pores [0-1] - real(rkind),intent(in) :: soilResistance ! resistance from the soil (s m-1) - real(rkind),intent(in) :: leafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) - real(rkind),intent(in) :: groundResistance ! below canopy aerodynamic resistance (s m-1) - real(rkind),intent(in) :: canopyResistance ! above canopy aerodynamic resistance (s m-1) - real(rkind),intent(in) :: stomResistSunlit ! stomatal resistance for sunlit leaves (s m-1) - real(rkind),intent(in) :: stomResistShaded ! stomatal resistance for shaded leaves (s m-1) - ! input: derivatives in scalar resistances - real(rkind),intent(in) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - real(rkind),intent(in) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind),intent(in) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - real(rkind),intent(in) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind),intent(in) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - ! --------------------------------------------------------------------------------------------------------------------------------------------------------------- - ! output: conductances -- used to test derivatives - real(rkind),intent(out) :: leafConductance ! leaf conductance (m s-1) - real(rkind),intent(out) :: canopyConductance ! canopy conductance (m s-1) - real(rkind),intent(out) :: groundConductanceSH ! ground conductance for sensible heat (m s-1) - real(rkind),intent(out) :: groundConductanceLH ! ground conductance for latent heat -- includes soil resistance (m s-1) - real(rkind),intent(out) :: evapConductance ! conductance for evaporation (m s-1) - real(rkind),intent(out) :: transConductance ! conductance for transpiration (m s-1) - real(rkind),intent(out) :: totalConductanceSH ! total conductance for sensible heat (m s-1) - real(rkind),intent(out) :: totalConductanceLH ! total conductance for latent heat (m s-1) - ! output: canopy air space variables - real(rkind),intent(out) :: VP_CanopyAir ! vapor pressure of the canopy air space (Pa) - ! output: fluxes from the vegetation canopy - real(rkind),intent(out) :: senHeatCanopy ! sensible heat flux from the canopy to the canopy air space (W m-2) - real(rkind),intent(out) :: latHeatCanopyEvap ! latent heat flux associated with evaporation from the canopy to the canopy air space (W m-2) - real(rkind),intent(out) :: latHeatCanopyTrans ! latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) - ! output: fluxes from non-vegetated surfaces (ground surface below vegetation, bare ground, or snow covered vegetation) - real(rkind),intent(out) :: senHeatGround ! sensible heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) - real(rkind),intent(out) :: latHeatGround ! latent heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) - ! output: total heat fluxes to the atmosphere - real(rkind),intent(out) :: senHeatTotal ! total sensible heat flux to the atmosphere (W m-2) - real(rkind),intent(out) :: latHeatTotal ! total latent heat flux to the atmosphere (W m-2) - ! output: net fluxes - real(rkind),intent(out) :: turbFluxCanair ! net turbulent heat fluxes at the canopy air space (W m-2) - real(rkind),intent(out) :: turbFluxCanopy ! net turbulent heat fluxes at the canopy (W m-2) - real(rkind),intent(out) :: turbFluxGround ! net turbulent heat fluxes at the ground surface (W m-2) - ! output: energy flux derivatives - real(rkind),intent(out) :: dTurbFluxCanair_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(rkind),intent(out) :: dTurbFluxCanair_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dTurbFluxCanair_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) - real(rkind),intent(out) :: dTurbFluxCanopy_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(rkind),intent(out) :: dTurbFluxCanopy_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dTurbFluxCanopy_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - real(rkind),intent(out) :: dTurbFluxGround_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(rkind),intent(out) :: dTurbFluxGround_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dTurbFluxGround_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - ! output: liquid flux derivatives (canopy evap) - real(rkind),intent(out) :: dLatHeatCanopyEvap_dCanLiq ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (W kg-1) - real(rkind),intent(out) :: dLatHeatCanopyEvap_dTCanair ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) - real(rkind),intent(out) :: dLatHeatCanopyEvap_dTCanopy ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dLatHeatCanopyEvap_dTGround ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) - ! output: liquid flux derivatives (ground evap) - real(rkind),intent(out) :: dLatHeatGroundEvap_dCanLiq ! derivative in latent heat of ground evaporation w.r.t. canopy liquid water content (J kg-1 s-1) - real(rkind),intent(out) :: dLatHeatGroundEvap_dTCanair ! derivative in latent heat of ground evaporation w.r.t. canopy air temperature (W m-2 K-1) - real(rkind),intent(out) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) - ! output: cross derivatives - real(rkind),intent(out) :: dTurbFluxCanair_dCanLiq ! derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - real(rkind),intent(out) :: dTurbFluxCanopy_dCanLiq ! derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - real(rkind),intent(out) :: dTurbFluxGround_dCanLiq ! derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! local variables -- general - real(rkind) :: fpart1,fpart2 ! different parts of a function - real(rkind) :: dPart0,dpart1,dpart2 ! derivatives for different parts of a function - ! local variables -- "constants" - real(rkind) :: volHeatCapacityAir ! volumetric heat capacity of air (J m-3) - real(rkind) :: latentHeatConstant ! latent heat constant (kg m-3 K-1) - ! local variables -- derivatives for energy conductances - real(rkind) :: dEvapCond_dCanopyTemp ! derivative in evap conductance w.r.t. canopy temperature - real(rkind) :: dTransCond_dCanopyTemp ! derivative in trans conductance w.r.t. canopy temperature - real(rkind) :: dCanopyCond_dCanairTemp ! derivative in canopy conductance w.r.t. canopy air temperature - real(rkind) :: dCanopyCond_dCanopyTemp ! derivative in canopy conductance w.r.t. canopy temperature - real(rkind) :: dGroundCondSH_dCanairTemp ! derivative in ground conductance of sensible heat w.r.t. canopy air temperature - real(rkind) :: dGroundCondSH_dCanopyTemp ! derivative in ground conductance of sensible heat w.r.t. canopy temperature - real(rkind) :: dGroundCondSH_dGroundTemp ! derivative in ground conductance of sensible heat w.r.t. ground temperature - ! local variables -- derivatives for mass conductances - real(rkind) :: dGroundCondLH_dCanairTemp ! derivative in ground conductance w.r.t. canopy air temperature - real(rkind) :: dGroundCondLH_dCanopyTemp ! derivative in ground conductance w.r.t. canopy temperature - real(rkind) :: dGroundCondLH_dGroundTemp ! derivative in ground conductance w.r.t. ground temperature - ! local variables -- derivatives for the canopy air space variables - real(rkind) :: fPart_VP ! part of the function for vapor pressure of the canopy air space - real(rkind) :: leafConductanceTr ! leaf conductance for transpiration (m s-1) - real(rkind) :: dVPCanopyAir_dTCanair ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the canopy air space - real(rkind) :: dVPCanopyAir_dTCanopy ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the canopy - real(rkind) :: dVPCanopyAir_dTGround ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the ground - real(rkind) :: dVPCanopyAir_dWetFrac ! derivative of vapor pressure in the canopy air space w.r.t. wetted fraction of the canopy - real(rkind) :: dVPCanopyAir_dCanLiq ! derivative of vapor pressure in the canopy air space w.r.t. canopy liquid water content - ! local variables -- sensible heat flux derivatives - real(rkind) :: dSenHeatTotal_dTCanair ! derivative in the total sensible heat flux w.r.t. canopy air temperature - real(rkind) :: dSenHeatTotal_dTCanopy ! derivative in the total sensible heat flux w.r.t. canopy air temperature - real(rkind) :: dSenHeatTotal_dTGround ! derivative in the total sensible heat flux w.r.t. ground temperature - real(rkind) :: dSenHeatCanopy_dTCanair ! derivative in the canopy sensible heat flux w.r.t. canopy air temperature - real(rkind) :: dSenHeatCanopy_dTCanopy ! derivative in the canopy sensible heat flux w.r.t. canopy temperature - real(rkind) :: dSenHeatCanopy_dTGround ! derivative in the canopy sensible heat flux w.r.t. ground temperature - real(rkind) :: dSenHeatGround_dTCanair ! derivative in the ground sensible heat flux w.r.t. canopy air temperature - real(rkind) :: dSenHeatGround_dTCanopy ! derivative in the ground sensible heat flux w.r.t. canopy temperature - real(rkind) :: dSenHeatGround_dTGround ! derivative in the ground sensible heat flux w.r.t. ground temperature - ! local variables -- latent heat flux derivatives - real(rkind) :: dLatHeatCanopyTrans_dTCanair ! derivative in the canopy transpiration flux w.r.t. canopy air temperature - real(rkind) :: dLatHeatCanopyTrans_dTCanopy ! derivative in the canopy transpiration flux w.r.t. canopy temperature - real(rkind) :: dLatHeatCanopyTrans_dTGround ! derivative in the canopy transpiration flux w.r.t. ground temperature - ! local variables -- wetted fraction derivatives - real(rkind) :: dLatHeatCanopyEvap_dWetFrac ! derivative in the latent heat of canopy evaporation w.r.t. canopy wet fraction (W m-2) - real(rkind) :: dLatHeatCanopyTrans_dWetFrac ! derivative in the latent heat of canopy transpiration w.r.t. canopy wet fraction (W m-2) - real(rkind) :: dLatHeatCanopyTrans_dCanLiq ! derivative in the latent heat of canopy transpiration w.r.t. canopy liquid water (J kg-1 s-1) - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='turbFluxes/' - - ! compute constants - volHeatCapacityAir = iden_air*cp_air ! volumetric heat capacity of air (J m-3) - latentHeatConstant = iden_air*w_ratio/airpres ! latent heat constant for (kg m-3 Pa-1) - - ! ***** - ! * compute conductances, and derivatives... - ! ****************************************** - - ! compute conductances for sensible heat (m s-1) - if(computeVegFlux)then - leafConductance = exposedVAI/leafResistance - leafConductanceTr = canopySunlitLAI/(leafResistance+stomResistSunlit) + canopyShadedLAI/(leafResistance+stomResistShaded) - canopyConductance = 1._rkind/canopyResistance - else - leafConductance = 0._rkind - canopyConductance = 0._rkind - end if - groundConductanceSH = 1._rkind/groundResistance - - ! compute total conductance for sensible heat - totalConductanceSH = leafConductance + groundConductanceSH + canopyConductance - - ! compute conductances for latent heat (m s-1) - if(computeVegFlux)then - evapConductance = canopyWetFraction*leafConductance - transConductance = (1._rkind - canopyWetFraction) * leafConductanceTr - !write(*,'(a,10(f14.8,1x))') 'canopySunlitLAI, canopyShadedLAI, stomResistSunlit, stomResistShaded, leafResistance, canopyWetFraction = ', & - ! canopySunlitLAI, canopyShadedLAI, stomResistSunlit, stomResistShaded, leafResistance, canopyWetFraction - else - evapConductance = 0._rkind - transConductance = 0._rkind - end if - groundConductanceLH = 1._rkind/(groundResistance + soilResistance) ! NOTE: soilResistance accounts for fractional snow, and =0 when snow cover is 100% - totalConductanceLH = evapConductance + transConductance + groundConductanceLH + canopyConductance - - ! check sensible heat conductance - if(totalConductanceSH < -tinyVal .or. groundConductanceSH < -tinyVal .or. canopyConductance < -tinyVal)then - message=trim(message)//'negative conductance for sensible heat' - err=20; return - endif - - ! check latent heat conductance - if(totalConductanceLH < tinyVal .or. groundConductanceLH < -tinyVal)then - message=trim(message)//'negative conductance for latent heat' - err=20; return - endif - - ! * compute derivatives - ! NOTE: it may be more efficient to compute these derivatives when computing resistances - if(ixDerivMethod == analytical)then - - ! compute derivatives in individual conductances for sensible heat w.r.t. canopy temperature (m s-1 K-1) - if(computeVegFlux)then - dEvapCond_dCanopyTemp = dCanopyWetFraction_dT*leafConductance ! derivative in evap conductance w.r.t. canopy temperature - dTransCond_dCanopyTemp = -dCanopyWetFraction_dT*leafConductanceTr ! derivative in trans conductance w.r.t. canopy temperature - dCanopyCond_dCanairTemp = -dCanopyResistance_dTCanair/canopyResistance**2._rkind ! derivative in canopy conductance w.r.t. canopy air emperature - dCanopyCond_dCanopyTemp = -dCanopyResistance_dTCanopy/canopyResistance**2._rkind ! derivative in canopy conductance w.r.t. canopy temperature - dGroundCondSH_dCanairTemp = -dGroundResistance_dTCanair/groundResistance**2._rkind ! derivative in ground conductance w.r.t. canopy air temperature - dGroundCondSH_dCanopyTemp = -dGroundResistance_dTCanopy/groundResistance**2._rkind ! derivative in ground conductance w.r.t. canopy temperature - dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2._rkind ! derivative in ground conductance w.r.t. ground temperature - else - dEvapCond_dCanopyTemp = 0._rkind ! derivative in evap conductance w.r.t. canopy temperature - dTransCond_dCanopyTemp = 0._rkind ! derivative in trans conductance w.r.t. canopy temperature - dCanopyCond_dCanairTemp = 0._rkind ! derivative in canopy conductance w.r.t. canopy air emperature - dCanopyCond_dCanopyTemp = 0._rkind ! derivative in canopy conductance w.r.t. canopy temperature - dGroundCondSH_dCanairTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy air temperature - dGroundCondSH_dCanopyTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy temperature - dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2._rkind ! derivative in ground conductance w.r.t. ground temperature - end if - - ! compute derivatives in individual conductances for latent heat w.r.t. canopy temperature (m s-1 K-1) - if(computeVegFlux)then - dGroundCondLH_dCanairTemp = -dGroundResistance_dTCanair/(groundResistance+soilResistance)**2._rkind ! derivative in ground conductance w.r.t. canopy air temperature - dGroundCondLH_dCanopyTemp = -dGroundResistance_dTCanopy/(groundResistance+soilResistance)**2._rkind ! derivative in ground conductance w.r.t. canopy temperature - dGroundCondLH_dGroundTemp = -dGroundResistance_dTGround/(groundResistance+soilResistance)**2._rkind ! derivative in ground conductance w.r.t. ground temperature - else - dGroundCondLH_dCanairTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy air temperature - dGroundCondLH_dCanopyTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy temperature - dGroundCondLH_dGroundTemp = -dGroundResistance_dTGround/(groundResistance+soilResistance)**2._rkind ! derivative in ground conductance w.r.t. ground temperature - end if - - end if ! (if computing analytical derivatives) - - ! ***** - ! * compute sensible and latent heat fluxes, and derivatives... - ! ************************************************************* - - ! * compute sensible and latent heat fluxes from the canopy to the canopy air space (W m-2) - if(computeVegFlux)then - - ! compute the vapor pressure in the canopy air space (Pa) - fPart_VP = canopyConductance*VPair + (evapConductance + transConductance)*satVP_CanopyTemp + groundConductanceLH*satVP_GroundTemp*soilRelHumidity - VP_CanopyAir = fPart_VP/totalConductanceLH - !write(*,'(a,10(f20.10,1x))') 'canopyConductance, evapConductance, transConductance, groundConductanceLH, soilRelHumidity = ', & - ! canopyConductance, evapConductance, transConductance, groundConductanceLH, soilRelHumidity - - ! compute sensible heat flux from the canopy air space to the atmosphere - ! NOTE: canairTemp is a state variable - senHeatTotal = -volHeatCapacityAir*canopyConductance*(canairTemp - airtemp) - !print*, 'canairTemp, airtemp, senHeatTotal = ', canairTemp, airtemp, senHeatTotal - - ! compute fluxes - senHeatCanopy = -volHeatCapacityAir*leafConductance*(canopyTemp - canairTemp) ! (positive downwards) - latHeatCanopyEvap = -latHeatSubVapCanopy*latentHeatConstant*evapConductance*(satVP_CanopyTemp - VP_CanopyAir) ! (positive downwards) - latHeatCanopyTrans = -LH_vap*latentHeatConstant*transConductance*(satVP_CanopyTemp - VP_CanopyAir) ! (positive downwards) - !write(*,'(a,10(f25.15,1x))') 'latHeatCanopyEvap, VP_CanopyAir = ', latHeatCanopyEvap, VP_CanopyAir - !write(*,'(a,10(f25.15,1x))') 'latHeatCanopyTrans, VP_CanopyAir = ', latHeatCanopyTrans, VP_CanopyAir - !write(*,'(a,10(f25.15,1x))') 'transConductance = ', transConductance - - ! check that energy for canopy evaporation does not exhaust the available water - ! NOTE: do this here, rather than enforcing solution constraints, because energy and mass solutions may be uncoupled - !if(latHeatSubVapCanopy > LH_vap+verySmall)then ! (sublimation) - ! maxFlux = -canopyIce*LH_sub/dt ! W m-2 - !else ! (evaporation) - ! maxFlux = -canopyLiquid*LH_vap/dt ! W m-2 - !end if - ! NOTE: fluxes are positive downwards - !if(latHeatCanopyEvap < maxFlux) latHeatCanopyEvap = maxFlux - !write(*,'(a,10(f20.10,1x))') 'maxFlux, latHeatCanopyEvap = ', maxFlux, latHeatCanopyEvap - - ! * no vegetation, so fluxes are zero - else - senHeatCanopy = 0._rkind - latHeatCanopyEvap = 0._rkind - latHeatCanopyTrans = 0._rkind - end if - - ! compute sensible and latent heat fluxes from the ground to the canopy air space (W m-2) - if(computeVegFlux)then - senHeatGround = -volHeatCapacityAir*groundConductanceSH*(groundTemp - canairTemp) ! (positive downwards) - latHeatGround = -latHeatSubVapGround*latentHeatConstant*groundConductanceLH*(satVP_GroundTemp*soilRelHumidity - VP_CanopyAir) ! (positive downwards) - else - senHeatGround = -volHeatCapacityAir*groundConductanceSH*(groundTemp - airtemp) ! (positive downwards) - latHeatGround = -latHeatSubVapGround*latentHeatConstant*groundConductanceLH*(satVP_GroundTemp*soilRelHumidity - VPair) ! (positive downwards) - senHeatTotal = senHeatGround - end if - !write(*,'(a,10(f25.15,1x))') 'latHeatGround = ', latHeatGround - - ! compute latent heat flux from the canopy air space to the atmosphere - ! NOTE: VP_CanopyAir is a diagnostic variable - latHeatTotal = latHeatCanopyEvap + latHeatCanopyTrans + latHeatGround - - ! * compute derivatives - if(ixDerivMethod == analytical)then - - ! differentiate CANOPY fluxes - if(computeVegFlux)then - - ! compute derivatives of vapor pressure in the canopy air space w.r.t. all state variables - ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the canopy air space) - dPart1 = dCanopyCond_dCanairTemp*VPair + dGroundCondLH_dCanairTemp*satVP_GroundTemp*soilRelHumidity - dPart2 = -(dCanopyCond_dCanairTemp + dGroundCondLH_dCanairTemp)/(totalConductanceLH**2._rkind) - dVPCanopyAir_dTCanair = dPart1/totalConductanceLH + fPart_VP*dPart2 - ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the canopy) - dPart0 = (evapConductance + transConductance)*dSVPCanopy_dCanopyTemp + (dEvapCond_dCanopyTemp + dTransCond_dCanopyTemp)*satVP_CanopyTemp - dPart1 = dCanopyCond_dCanopyTemp*VPair + dPart0 + dGroundCondLH_dCanopyTemp*satVP_GroundTemp*soilRelHumidity - dPart2 = -(dCanopyCond_dCanopyTemp + dEvapCond_dCanopyTemp + dTransCond_dCanopyTemp + dGroundCondLH_dCanopyTemp)/(totalConductanceLH**2._rkind) - dVPCanopyAir_dTCanopy = dPart1/totalConductanceLH + fPart_VP*dPart2 - ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the ground) - dPart1 = dGroundCondLH_dGroundTemp*satVP_GroundTemp*soilRelHumidity + groundConductanceLH*dSVPGround_dGroundTemp*soilRelHumidity - dPart2 = -dGroundCondLH_dGroundTemp/(totalConductanceLH**2._rkind) - dVPCanopyAir_dTGround = dPart1/totalConductanceLH + fPart_VP*dPart2 - ! (derivative of vapor pressure in the canopy air space w.r.t. wetted fraction of the canopy) - dPart1 = (leafConductance - leafConductanceTr)*satVP_CanopyTemp - dPart2 = -(leafConductance - leafConductanceTr)/(totalConductanceLH**2._rkind) - dVPCanopyAir_dWetFrac = dPart1/totalConductanceLH + fPart_VP*dPart2 - dVPCanopyAir_dCanLiq = dVPCanopyAir_dWetFrac*dCanopyWetFraction_dWat - !write(*,'(a,5(f20.8,1x))') 'dVPCanopyAir_dTCanair, dVPCanopyAir_dTCanopy, dVPCanopyAir_dTGround, dVPCanopyAir_dWetFrac, dVPCanopyAir_dCanLiq = ', & - ! dVPCanopyAir_dTCanair, dVPCanopyAir_dTCanopy, dVPCanopyAir_dTGround, dVPCanopyAir_dWetFrac, dVPCanopyAir_dCanLiq - - ! sensible heat from the canopy to the atmosphere - dSenHeatTotal_dTCanair = -volHeatCapacityAir*canopyConductance - volHeatCapacityAir*dCanopyCond_dCanairTemp*(canairTemp - airtemp) - dSenHeatTotal_dTCanopy = -volHeatCapacityAir*dCanopyCond_dCanopyTemp*(canairTemp - airtemp) - dSenHeatTotal_dTGround = 0._rkind - !write(*,'(a,3(f20.8,1x))') 'dSenHeatTotal_dTCanair, dSenHeatTotal_dTCanopy, dSenHeatTotal_dTGround = ', & - ! dSenHeatTotal_dTCanair, dSenHeatTotal_dTCanopy, dSenHeatTotal_dTGround - - ! sensible heat from the canopy to the canopy air space - dSenHeatCanopy_dTCanair = volHeatCapacityAir*leafConductance - dSenHeatCanopy_dTCanopy = -volHeatCapacityAir*leafConductance - dSenHeatCanopy_dTGround = 0._rkind - !write(*,'(a,3(f20.8,1x))') 'dSenHeatCanopy_dTCanair, dSenHeatCanopy_dTCanopy, dSenHeatCanopy_dTGround = ', & - ! dSenHeatCanopy_dTCanair, dSenHeatCanopy_dTCanopy, dSenHeatCanopy_dTGround - - ! sensible heat from the ground to the canopy air space - dSenHeatGround_dTCanair = -volHeatCapacityAir*dGroundCondSH_dCanairTemp*(groundTemp - canairTemp) + volHeatCapacityAir*groundConductanceSH - dSenHeatGround_dTCanopy = -volHeatCapacityAir*dGroundCondSH_dCanopyTemp*(groundTemp - canairTemp) - dSenHeatGround_dTGround = -volHeatCapacityAir*dGroundCondSH_dGroundTemp*(groundTemp - canairTemp) - volHeatCapacityAir*groundConductanceSH - !write(*,'(a,3(f20.8,1x))') 'dSenHeatGround_dTCanair, dSenHeatGround_dTCanopy, dSenHeatGround_dTGround = ', & - ! dSenHeatGround_dTCanair, dSenHeatGround_dTCanopy, dSenHeatGround_dTGround - - ! latent heat associated with canopy evaporation - ! (initial calculations) - fPart1 = -latHeatSubVapCanopy*latentHeatConstant*evapConductance - dPart1 = -latHeatSubVapCanopy*latentHeatConstant*dEvapCond_dCanopyTemp - fPart2 = satVP_CanopyTemp - VP_CanopyAir - dPart2 = dSVPCanopy_dCanopyTemp - dVPCanopyAir_dTCanopy - ! (derivatives) - dLatHeatCanopyEvap_dTCanair = fPart1*(-dVPCanopyAir_dTCanair) - dLatHeatCanopyEvap_dTCanopy = fPart1*dpart2 + fPart2*dPart1 - dLatHeatCanopyEvap_dTGround = fPart1*(-dVPCanopyAir_dTGround) - !write(*,'(a,3(f20.8,1x))') 'dLatHeatCanopyEvap_dTCanair, dLatHeatCanopyEvap_dTCanopy, dLatHeatCanopyEvap_dTGround = ', & - ! dLatHeatCanopyEvap_dTCanair, dLatHeatCanopyEvap_dTCanopy, dLatHeatCanopyEvap_dTGround - - ! latent heat associated with canopy transpiration - ! (initial calculations) - fPart1 = -LH_vap*latentHeatConstant*transConductance - dPart1 = -LH_vap*latentHeatConstant*dTransCond_dCanopyTemp - ! (derivatives) - dLatHeatCanopyTrans_dTCanair = fPart1*(-dVPCanopyAir_dTCanair) - dLatHeatCanopyTrans_dTCanopy = fPart1*dPart2 + fPart2*dPart1 - dLatHeatCanopyTrans_dTGround = fPart1*(-dVPCanopyAir_dTGround) - !write(*,'(a,3(f20.8,1x))') 'dLatHeatCanopyTrans_dTCanair, dLatHeatCanopyTrans_dTCanopy, dLatHeatCanopyTrans_dTGround = ', & - ! dLatHeatCanopyTrans_dTCanair, dLatHeatCanopyTrans_dTCanopy, dLatHeatCanopyTrans_dTGround - - ! latent heat flux from the ground - fPart1 = -latHeatSubVapGround*latentHeatConstant*groundConductanceLH ! function of the first part - fPart2 = (satVP_GroundTemp*soilRelHumidity - VP_CanopyAir) ! function of the second part - dLatHeatGroundEvap_dTCanair = -latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dCanairTemp*fPart2 - dVPCanopyAir_dTCanair*fPart1 - dLatHeatGroundEvap_dTCanopy = -latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dCanopyTemp*fPart2 - dVPCanopyAir_dTCanopy*fPart1 - dLatHeatGroundEvap_dTGround = -latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dGroundTemp*fPart2 + (dSVPGround_dGroundTemp*soilRelHumidity - dVPCanopyAir_dTGround)*fPart1 - !write(*,'(a,3(f20.8,1x))') 'dLatHeatGroundEvap_dTCanair, dLatHeatGroundEvap_dTCanopy, dLatHeatGroundEvap_dTGround = ', & - ! dLatHeatGroundEvap_dTCanair, dLatHeatGroundEvap_dTCanopy, dLatHeatGroundEvap_dTGround - - ! latent heat associated with canopy evaporation w.r.t. wetted fraction of the canopy - dPart1 = -latHeatSubVapCanopy*latentHeatConstant*leafConductance - fPart1 = dPart1*canopyWetFraction - dLatHeatCanopyEvap_dWetFrac = dPart1*(satVP_CanopyTemp - VP_CanopyAir) + fPart1*(-dVPCanopyAir_dWetFrac) - - ! latent heat associated with canopy transpiration w.r.t. wetted fraction of the canopy - dPart1 = LH_vap*latentHeatConstant*leafConductanceTr ! NOTE: positive, since (1 - wetFrac) - fPart1 = -dPart1*(1._rkind - canopyWetFraction) - dLatHeatCanopyTrans_dWetFrac = dPart1*(satVP_CanopyTemp - VP_CanopyAir) + fPart1*(-dVPCanopyAir_dWetFrac) - !print*, 'dLatHeatCanopyTrans_dWetFrac = ', dLatHeatCanopyTrans_dWetFrac - - ! latent heat associated with canopy transpiration w.r.t. canopy liquid water - dLatHeatCanopyTrans_dCanLiq = dLatHeatCanopyTrans_dWetFrac*dCanopyWetFraction_dWat ! (J s-1 kg-1) - !print*, 'dLatHeatCanopyTrans_dCanLiq = ', dLatHeatCanopyTrans_dCanLiq - - else ! canopy is undefined - - ! set derivatives for canopy fluxes to zero (no canopy, so fluxes are undefined) - dSenHeatTotal_dTCanair = 0._rkind - dSenHeatTotal_dTCanopy = 0._rkind - dSenHeatTotal_dTGround = 0._rkind - dSenHeatCanopy_dTCanair = 0._rkind - dSenHeatCanopy_dTCanopy = 0._rkind - dSenHeatCanopy_dTGround = 0._rkind - dLatHeatCanopyEvap_dTCanair = 0._rkind - dLatHeatCanopyEvap_dTCanopy = 0._rkind - dLatHeatCanopyEvap_dTGround = 0._rkind - dLatHeatCanopyTrans_dTCanair = 0._rkind - dLatHeatCanopyTrans_dTCanopy = 0._rkind - dLatHeatCanopyTrans_dTGround = 0._rkind - - ! set derivatives for wetted area and canopy transpiration to zero (no canopy, so fluxes are undefined) - dLatHeatCanopyEvap_dWetFrac = 0._rkind - dLatHeatCanopyEvap_dCanLiq = 0._rkind - dLatHeatCanopyTrans_dCanLiq = 0._rkind - dVPCanopyAir_dCanLiq = 0._rkind - - ! set derivatives for ground fluxes w.r.t canopy temperature to zero (no canopy, so fluxes are undefined) - dSenHeatGround_dTCanair = 0._rkind - dSenHeatGround_dTCanopy = 0._rkind - dLatHeatGroundEvap_dTCanair = 0._rkind - dLatHeatGroundEvap_dTCanopy = 0._rkind - - ! compute derivatives for the ground fluxes w.r.t. ground temperature - dSenHeatGround_dTGround = (-volHeatCapacityAir*dGroundCondSH_dGroundTemp)*(groundTemp - airtemp) + & ! d(ground sensible heat flux)/d(ground temp) - (-volHeatCapacityAir*groundConductanceSH) - dLatHeatGroundEvap_dTGround = (-latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dGroundTemp)*(satVP_GroundTemp*soilRelHumidity - VPair) + & ! d(ground latent heat flux)/d(ground temp) - (-latHeatSubVapGround*latentHeatConstant*groundConductanceLH)*dSVPGround_dGroundTemp*soilRelHumidity - - !print*, 'dGroundCondLH_dGroundTemp = ', dGroundCondLH_dGroundTemp - - end if ! (if canopy is defined) - - end if ! (if computing analytical derivatives) - - - ! ***** - ! * compute net turbulent fluxes, and derivatives... - ! ************************************************** - - ! compute net fluxes - turbFluxCanair = senHeatTotal - senHeatCanopy - senHeatGround ! net turbulent flux at the canopy air space (W m-2) - turbFluxCanopy = senHeatCanopy + latHeatCanopyEvap + latHeatCanopyTrans ! net turbulent flux at the canopy (W m-2) - turbFluxGround = senHeatGround + latHeatGround ! net turbulent flux at the ground surface (W m-2) - !write(*,'(a,1x,3(f20.10,1x))') 'senHeatCanopy, latHeatCanopyEvap, latHeatCanopyTrans = ', senHeatCanopy, latHeatCanopyEvap, latHeatCanopyTrans - - ! * compute derivatives - if(ixDerivMethod == analytical)then - ! (energy derivatives) - dTurbFluxCanair_dTCanair = dSenHeatTotal_dTCanair - dSenHeatCanopy_dTCanair - dSenHeatGround_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxCanair_dTCanopy = dSenHeatTotal_dTCanopy - dSenHeatCanopy_dTCanopy - dSenHeatGround_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxCanair_dTGround = dSenHeatTotal_dTGround - dSenHeatCanopy_dTGround - dSenHeatGround_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanair = dSenHeatCanopy_dTCanair + dLatHeatCanopyEvap_dTCanair + dLatHeatCanopyTrans_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanopy = dSenHeatCanopy_dTCanopy + dLatHeatCanopyEvap_dTCanopy + dLatHeatCanopyTrans_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxCanopy_dTGround = dSenHeatCanopy_dTGround + dLatHeatCanopyEvap_dTGround + dLatHeatCanopyTrans_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxGround_dTCanair = dSenHeatGround_dTCanair + dLatHeatGroundEvap_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxGround_dTCanopy = dSenHeatGround_dTCanopy + dLatHeatGroundEvap_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxGround_dTGround = dSenHeatGround_dTGround + dLatHeatGroundEvap_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - ! (liquid water derivatives) - dLatHeatCanopyEvap_dCanLiq = dLatHeatCanopyEvap_dWetFrac*dCanopyWetFraction_dWat ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water (W kg-1) - dLatHeatGroundEvap_dCanLiq = latHeatSubVapGround*latentHeatConstant*groundConductanceLH*dVPCanopyAir_dCanLiq ! derivative in latent heat of ground evaporation w.r.t. canopy liquid water (J kg-1 s-1) - ! (cross deriavtives) - dTurbFluxCanair_dCanLiq = 0._rkind ! derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dTurbFluxCanopy_dCanLiq = dLatHeatCanopyEvap_dCanLiq + dLatHeatCanopyTrans_dCanLiq ! derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dTurbFluxGround_dCanLiq = dLatHeatGroundEvap_dCanLiq ! derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - else ! (just make sure we return something) - ! (energy derivatives) - dTurbFluxCanair_dTCanair = 0._rkind - dTurbFluxCanair_dTCanopy = 0._rkind - dTurbFluxCanair_dTGround = 0._rkind - dTurbFluxCanopy_dTCanair = 0._rkind - dTurbFluxCanopy_dTCanopy = 0._rkind - dTurbFluxCanopy_dTGround = 0._rkind - dTurbFluxGround_dTCanair = 0._rkind - dTurbFluxGround_dTCanopy = 0._rkind - dTurbFluxGround_dTGround = 0._rkind - ! (liquid water derivatives) - dLatHeatCanopyEvap_dCanLiq = 0._rkind - dLatHeatGroundEvap_dCanLiq = 0._rkind - ! (cross deriavtives) - dTurbFluxCanair_dCanLiq = 0._rkind - dTurbFluxCanopy_dCanLiq = 0._rkind - dTurbFluxGround_dCanLiq = 0._rkind - end if - - end subroutine turbFluxes - - - ! ******************************************************************************************************* - ! private subroutine aStability: compute stability corrections for turbulent heat fluxes (-) - ! ******************************************************************************************************* - subroutine aStability(& - ! input: control - computeDerivative, & ! input: logical flag to compute analytical derivatives - ixStability, & ! input: choice of stability function - ! input: forcing data, diagnostic and state variables - mHeight, & ! input: measurement height (m) - airTemp, & ! input: air temperature (K) - sfcTemp, & ! input: surface temperature (K) - windspd, & ! input: wind speed (m s-1) - ! input: stability parameters - critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam, & ! input: parameter in Louis (1979) stability function - Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function - ! output - RiBulk, & ! output: bulk Richardson number (-) - stabilityCorrection, & ! output: stability correction for turbulent heat fluxes (-) - dStabilityCorrection_dRich, & ! output: derivative in stability correction w.r.t. Richardson number (-) - dStabilityCorrection_dAirTemp, & ! output: derivative in stability correction w.r.t. temperature (K-1) - dStabilityCorrection_dSfcTemp, & ! output: derivative in stability correction w.r.t. temperature (K-1) - err, message ) ! output: error control - implicit none - ! input: control - logical(lgt),intent(in) :: computeDerivative ! flag to compute the derivative - integer(i4b),intent(in) :: ixStability ! choice of stability function - ! input: forcing data, diagnostic and state variables - real(rkind),intent(in) :: mHeight ! measurement height (m) - real(rkind),intent(in) :: airtemp ! air temperature (K) - real(rkind),intent(in) :: sfcTemp ! surface temperature (K) - real(rkind),intent(in) :: windspd ! wind speed (m s-1) - ! input: stability parameters - real(rkind),intent(in) :: critRichNumber ! critical value for the bulk Richardson number where turbulence ceases (-) - real(rkind),intent(in) :: Louis79_bparam ! parameter in Louis (1979) stability function - real(rkind),intent(in) :: Mahrt87_eScale ! exponential scaling factor in the Mahrt (1987) stability function - ! output - real(rkind),intent(out) :: RiBulk ! bulk Richardson number (-) - real(rkind),intent(out) :: stabilityCorrection ! stability correction for turbulent heat fluxes (-) - real(rkind),intent(out) :: dStabilityCorrection_dRich ! derivative in stability correction w.r.t. Richardson number (-) - real(rkind),intent(out) :: dStabilityCorrection_dAirTemp ! derivative in stability correction w.r.t. air temperature (K-1) - real(rkind),intent(out) :: dStabilityCorrection_dSfcTemp ! derivative in stability correction w.r.t. surface temperature (K-1) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! local - real(rkind), parameter :: verySmall=1.e-10_rkind ! a very small number (avoid stability of zero) - real(rkind) :: dRiBulk_dAirTemp ! derivative in the bulk Richardson number w.r.t. air temperature (K-1) - real(rkind) :: dRiBulk_dSfcTemp ! derivative in the bulk Richardson number w.r.t. surface temperature (K-1) - real(rkind) :: bPrime ! scaled "b" parameter for stability calculations in Louis (1979) - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='aStability/' - - ! compute the bulk Richardson number (-) - call bulkRichardson(& - ! input - airTemp, & ! input: air temperature (K) - sfcTemp, & ! input: surface temperature (K) - windspd, & ! input: wind speed (m s-1) - mHeight, & ! input: measurement height (m) - computeDerivative, & ! input: flag to compute the derivative - ! output - RiBulk, & ! output: bulk Richardson number (-) - dRiBulk_dAirTemp, & ! output: derivative in the bulk Richardson number w.r.t. air temperature (K-1) - dRiBulk_dSfcTemp, & ! output: derivative in the bulk Richardson number w.r.t. surface temperature (K-1) - err,message) ! output: error control - - ! set derivative to one if not computing it - if(.not.computeDerivative)then - dStabilityCorrection_dRich = 1._rkind - dStabilityCorrection_dAirTemp = 1._rkind - dStabilityCorrection_dSfcTemp = 1._rkind - end if - - ! ***** process unstable cases - if(RiBulk<0._rkind)then - ! compute surface-atmosphere exchange coefficient (-) - stabilityCorrection = (1._rkind - 16._rkind*RiBulk)**0.5_rkind - ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) - if(computeDerivative)then - dStabilityCorrection_dRich = (-16._rkind) * 0.5_rkind*(1._rkind - 16._rkind*RiBulk)**(-0.5_rkind) - dStabilityCorrection_dAirTemp = dRiBulk_dAirTemp * dStabilityCorrection_dRich - dStabilityCorrection_dSfcTemp = dRiBulk_dSfcTemp * dStabilityCorrection_dRich - end if - return - end if - - ! ***** process stable cases - select case(ixStability) - - ! ("standard" stability correction, a la Anderson 1976) - case(standard) - ! compute surface-atmosphere exchange coefficient (-) - if(RiBulk < critRichNumber) stabilityCorrection = (1._rkind - 5._rkind*RiBulk)**2._rkind - if(RiBulk >= critRichNumber) stabilityCorrection = verySmall - ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) - if(computeDerivative)then - if(RiBulk < critRichNumber) dStabilityCorrection_dRich = (-5._rkind) * 2._rkind*(1._rkind - 5._rkind*RiBulk) - if(RiBulk >= critRichNumber) dStabilityCorrection_dRich = verySmall - end if - - ! (Louis 1979) - case(louisInversePower) - ! scale the "b" parameter for stable conditions - bprime = Louis79_bparam/2._rkind - ! compute surface-atmosphere exchange coefficient (-) - stabilityCorrection = 1._rkind / ( (1._rkind + bprime*RiBulk)**2._rkind ) - if(stabilityCorrection < epsilon(stabilityCorrection)) stabilityCorrection = epsilon(stabilityCorrection) - ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) - if(computeDerivative)then - dStabilityCorrection_dRich = bprime * (-2._rkind)*(1._rkind + bprime*RiBulk)**(-3._rkind) - end if - - ! (Mahrt 1987) - case(mahrtExponential) - ! compute surface-atmosphere exchange coefficient (-) - stabilityCorrection = exp(-Mahrt87_eScale * RiBulk) - if(stabilityCorrection < epsilon(stabilityCorrection)) stabilityCorrection = epsilon(stabilityCorrection) - ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) - if(computeDerivative)then - dStabilityCorrection_dRich = (-Mahrt87_eScale) * exp(-Mahrt87_eScale * RiBulk) - end if - - ! (return error if the stability correction method is not found) - case default - err=10; message=trim(message)//"optionNotFound[stability correction]"; return - - end select - - ! get the stability correction with respect to air temperature and surface temperature - ! NOTE: air temperature is used for canopy air temperature, which is a model state variable - if(computeDerivative)then - dStabilityCorrection_dAirTemp = dRiBulk_dAirTemp * dStabilityCorrection_dRich - dStabilityCorrection_dSfcTemp = dRiBulk_dSfcTemp * dStabilityCorrection_dRich - end if - - end subroutine aStability - - - ! ******************************************************************************************************* - ! private subroutine bulkRichardson: compute bulk Richardson number - ! ******************************************************************************************************* - subroutine bulkRichardson(& - ! input - airTemp, & ! input: air temperature (K) - sfcTemp, & ! input: surface temperature (K) - windspd, & ! input: wind speed (m s-1) - mHeight, & ! input: measurement height (m) - computeDerivative, & ! input: flag to compute the derivative - ! output - RiBulk, & ! output: bulk Richardson number (-) - dRiBulk_dAirTemp, & ! output: derivative in the bulk Richardson number w.r.t. air temperature (K-1) - dRiBulk_dSfcTemp, & ! output: derivative in the bulk Richardson number w.r.t. surface temperature (K-1) - err,message) ! output: error control - implicit none - ! input - real(rkind),intent(in) :: airtemp ! air temperature (K) - real(rkind),intent(in) :: sfcTemp ! surface temperature (K) - real(rkind),intent(in) :: windspd ! wind speed (m s-1) - real(rkind),intent(in) :: mHeight ! measurement height (m) - logical(lgt),intent(in) :: computeDerivative ! flag to compute the derivative - ! output - real(rkind),intent(inout) :: RiBulk ! bulk Richardson number (-) - real(rkind),intent(out) :: dRiBulk_dAirTemp ! derivative in the bulk Richardson number w.r.t. air temperature (K-1) - real(rkind),intent(out) :: dRiBulk_dSfcTemp ! derivative in the bulk Richardson number w.r.t. surface temperature (K-1) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! local variables - real(rkind) :: T_grad ! gradient in temperature between the atmosphere and surface (K) - real(rkind) :: T_mean ! mean of the atmosphere and surface temperature (K) - real(rkind) :: RiMult ! dimensionless scaling factor (-) - ! initialize error control - err=0; message='bulkRichardson/' - ! compute local variables - T_grad = airtemp - sfcTemp - T_mean = 0.5_rkind*(airtemp + sfcTemp) - RiMult = (gravity*mHeight)/(windspd*windspd) - ! compute the Richardson number - RiBulk = (T_grad/T_mean) * RiMult - ! compute the derivative in the Richardson number - if(computeDerivative)then - dRiBulk_dAirTemp = RiMult/T_mean - RiMult*T_grad/(0.5_rkind*((airtemp + sfcTemp)**2._rkind)) - dRiBulk_dSfcTemp = -RiMult/T_mean - RiMult*T_grad/(0.5_rkind*((airtemp + sfcTemp)**2._rkind)) - else - dRiBulk_dAirTemp = 1._rkind - dRiBulk_dSfcTemp = 1._rkind - end if - end subroutine bulkRichardson - - -end module vegNrgFluxFida_module From d8376a325122434c7b7cadcf36e79a4566efec35 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 30 Jun 2021 10:05:34 -0600 Subject: [PATCH 0168/1472] varSubstepFida renamed to varSubstepSundials --- build/Makefile | 2 +- build/source/engine/opSplittin.f90 | 26 +++++++++---------- ...SubstepFida.f90 => varSubstepSundials.f90} | 18 ++++++------- 3 files changed, 23 insertions(+), 23 deletions(-) rename build/source/engine/{varSubstepFida.f90 => varSubstepSundials.f90} (99%) diff --git a/build/Makefile b/build/Makefile index ad5f5d3d8..062abad95 100644 --- a/build/Makefile +++ b/build/Makefile @@ -234,7 +234,7 @@ SUMMA_SOLVER= \ solveByIDA.f90 \ systemSolvSundials.f90 \ varSubstep.f90 \ - varSubstepFida.f90 \ + varSubstepSundials.f90 \ opSplittin.f90 \ coupled_em.f90 \ run_oneHRU.f90 \ diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 68a8614d4..62ed42d50 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -162,7 +162,7 @@ module opSplittin_module ! (1) Attempt different solutions in the following order: (a) fully coupled; (b) split by state type and by ! domain type for a given energy and mass split (vegetation, snow, and soil); and (c) scalar solution ! for a given state type and domain subset. - ! (2) For a given split, compute a variable number of substeps (in varSubstepFida). + ! (2) For a given split, compute a variable number of substeps (in varSubstepSundials). ! ********************************************************************************************************** subroutine opSplittin(& ! input: model control @@ -200,7 +200,7 @@ subroutine opSplittin(& ! population/extraction of state vectors USE indexState_module,only:indexSplit ! get state indices USE varSubstep_module,only:varSubstep ! complete substeps for a given split - USE varSubstepFida_module,only:varSubstepFida ! complete substeps for a given split + USE varSubstepSundials_module,only:varSubstepSundials ! complete substeps for a given split ! identify name of variable type (for error message) USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages implicit none @@ -768,7 +768,7 @@ subroutine opSplittin(& ! * solve variable subset for one time step... ! -------------------------------------------- - !print*, trim(message)//'before varSubstepFida: nSubset = ', nSubset + !print*, trim(message)//'before varSubstepSundials: nSubset = ', nSubset ! keep track of the number of scalar solutions if(ixSolution==scalar) numberScalarSolutions = numberScalarSolutions + 1 @@ -776,7 +776,7 @@ subroutine opSplittin(& ! solve variable subset for one full time step select case(solver) case(IDA) - call varSubstepFida(& + call varSubstepSundials(& ! input: model control dt, & ! intent(inout) : time step (s) dtInit, & ! intent(in) : initial time step (seconds) @@ -861,9 +861,9 @@ subroutine opSplittin(& if(err>0) return endif ! (check for errors) - !print*, trim(message)//'after varSubstepFida: scalarSnowDrainage = ', flux_data%var(iLookFLUX%scalarSnowDrainage)%dat - !print*, trim(message)//'after varSubstepFida: iLayerLiqFluxSnow = ', flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat - !print*, trim(message)//'after varSubstepFida: iLayerLiqFluxSoil = ', flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat + !print*, trim(message)//'after varSubstepSundials: scalarSnowDrainage = ', flux_data%var(iLookFLUX%scalarSnowDrainage)%dat + !print*, trim(message)//'after varSubstepSundials: iLayerLiqFluxSnow = ', flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat + !print*, trim(message)//'after varSubstepSundials: iLayerLiqFluxSoil = ', flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ! check !if(ixSolution==scalar)then @@ -876,12 +876,12 @@ subroutine opSplittin(& ! check !if(ixCoupling/=fullyCoupled)then ! print*, 'dt = ', dt - ! print*, 'after varSubstepFida: err = ', err - ! print*, 'after varSubstepFida: cmessage = ', trim(cmessage) - ! print*, 'after varSubstepFida: computeVegFlux = ', computeVegFlux - ! print*, 'after varSubstepFida: stateMask = ', stateMask - ! print*, 'after varSubstepFida: coupling = ', (ixCoupling==fullyCoupled) - ! print*, 'after varSubstepFida: scalar solve = ', (ixSolution==scalar) + ! print*, 'after varSubstepSundials: err = ', err + ! print*, 'after varSubstepSundials: cmessage = ', trim(cmessage) + ! print*, 'after varSubstepSundials: computeVegFlux = ', computeVegFlux + ! print*, 'after varSubstepSundials: stateMask = ', stateMask + ! print*, 'after varSubstepSundials: coupling = ', (ixCoupling==fullyCoupled) + ! print*, 'after varSubstepSundials: scalar solve = ', (ixSolution==scalar) ! print*, 'iStateTypeSplit, nStateTypeSplit = ', iStateTypeSplit, nStateTypeSplit ! print*, 'iDomainSplit, nDomainSplit = ', iDomainSplit, nDomainSplit ! print*, 'nSubset = ', nSubset diff --git a/build/source/engine/varSubstepFida.f90 b/build/source/engine/varSubstepSundials.f90 similarity index 99% rename from build/source/engine/varSubstepFida.f90 rename to build/source/engine/varSubstepSundials.f90 index 73caea36b..f129afc11 100644 --- a/build/source/engine/varSubstepFida.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -18,7 +18,7 @@ ! You should have received a copy of the GNU General Public License ! along with this program. If not, see . -module varSubstepFida_module +module varSubstepSundials_module ! data types USE nrtype @@ -76,7 +76,7 @@ module varSubstepFida_module ! safety: set private unless specified otherwise implicit none private -public::varSubstepFida +public::varSubstepSundials ! algorithmic parameters real(rkind),parameter :: verySmall=1.e-6_rkind ! used as an additive constant to check if substantial difference among real numbers @@ -85,9 +85,9 @@ module varSubstepFida_module ! ********************************************************************************************************** - ! public subroutine varSubstepFida: run the model for a collection of substeps for a given state subset + ! public subroutine varSubstepSundials: run the model for a collection of substeps for a given state subset ! ********************************************************************************************************** - subroutine varSubstepFida(& + subroutine varSubstepSundials(& ! input: model control dt, & ! intent(in) : time step (s) dtInit, & ! intent(in) : initial time step (seconds) @@ -250,7 +250,7 @@ subroutine varSubstepFida(& ! Procedure starts here ! initialize error control - err=0; message='varSubstepFida/' + err=0; message='varSubstepSundials/' ! initialize flag for the success of the substepping failedMinimumStep=.false. @@ -286,7 +286,7 @@ subroutine varSubstepFida(& substeps: do ! initialize error control - err=0; message='varSubstepFida/' + err=0; message='varSubstepSundials/' !write(*,'(a,1x,3(f13.2,1x))') '***** new subStep: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt !print*, 'scalarCanopyIce = ', prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) @@ -361,7 +361,7 @@ subroutine varSubstepFida(& ! reduce step based on failure if(failedSubstep)then - err=0; message='varSubstepFida/' ! recover from failed convergence + err=0; message='varSubstepSundials/' ! recover from failed convergence dtMultiplier = 0.5_rkind ! system failure: step halving else @@ -534,7 +534,7 @@ subroutine varSubstepFida(& endif - end subroutine varSubstepFida + end subroutine varSubstepSundials ! ********************************************************************************************************** @@ -1087,4 +1087,4 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt end subroutine updateProgFida -end module varSubstepFida_module +end module varSubstepSundials_module From 4d249aed759b833618fff08aab85f88dc362465a Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 30 Jun 2021 10:19:50 -0600 Subject: [PATCH 0169/1472] soilCmpresFida file deleted --- build/Makefile | 1 - build/source/engine/computFlux.f90 | 53 ++++++++++ build/source/engine/eval8DAE.f90 | 2 +- build/source/engine/soilCmpresFida.f90 | 135 ------------------------- 4 files changed, 54 insertions(+), 137 deletions(-) delete mode 100644 build/source/engine/soilCmpresFida.f90 diff --git a/build/Makefile b/build/Makefile index 062abad95..b78b1e766 100644 --- a/build/Makefile +++ b/build/Makefile @@ -212,7 +212,6 @@ SUMMA_SOLVER= \ snowLiqFlx.f90 \ soilLiqFlx.f90 \ bigAquifer.f90 \ - soilCmpresFida.f90 \ computFlux.f90 \ computResid.f90 \ computJacob.f90 \ diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index b99f1ab1e..ff80c31f4 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -97,6 +97,7 @@ module computFlux_module private public::computFlux public::soilCmpres +public::soilCmpresFida contains ! ********************************************************************************************************* @@ -904,4 +905,56 @@ subroutine soilCmpres(& end if end subroutine soilCmpres + + ! ********************************************************************************************************** + ! public subroutine soilCmpres: compute soil compressibility (-) and its derivative w.r.t matric head (m-1) + ! ********************************************************************************************************** + subroutine soilCmpresFida(& + ! input: + ixRichards, & ! intent(in): choice of option for Richards' equation + ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers + mLayerMatricHeadPrime, & ! intent(in): matric head at the start of the time step (m) + mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) + mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice content in each soil layer (-) + specificStorage, & ! intent(in): specific storage coefficient (m-1) + theta_sat, & ! intent(in): soil porosity (-) + ! output: + compress, & ! intent(out): compressibility of the soil matrix (-) + dCompress_dPsi, & ! intent(out): derivative in compressibility w.r.t. matric head (m-1) + err,message) ! intent(out): error code and error message +implicit none +! input: +integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation +integer(i4b),intent(in) :: ixBeg,ixEnd ! start and end indices defining desired layers +real(rkind),intent(in) :: mLayerMatricHeadPrime(:) ! matric head at the start of the time step (m) +real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) +real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) +real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) +real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) +! output: +real(rkind),intent(inout) :: compress(:) ! soil compressibility (-) +real(rkind),intent(inout) :: dCompress_dPsi(:) ! derivative in soil compressibility w.r.t. matric head (m-1) +integer(i4b),intent(out) :: err ! error code +character(*),intent(out) :: message ! error message +! local variables +integer(i4b) :: iLayer ! index of soil layer +! -------------------------------------------------------------- +! initialize error control +err=0; message='soilCmpresFida/' +! (only compute for the mixed form of Richards' equation) +if(ixRichards==mixdform)then + do iLayer=1,size(mLayerMatricHeadPrime) + if(iLayer>=ixBeg .and. iLayer<=ixEnd)then + ! compute the derivative for the compressibility term (m-1) + dCompress_dPsi(iLayer) = specificStorage*(mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer))/theta_sat(iLayer) + ! compute the compressibility term (-) + compress(iLayer) = mLayerMatricHeadPrime(iLayer) * dCompress_dPsi(iLayer) + endif + end do +else + compress(:) = 0._rkind + dCompress_dPsi(:) = 0._rkind +end if +end subroutine soilCmpresFida + end module computFlux_module diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index 49e71e5bb..c22d49d8c 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -156,7 +156,7 @@ subroutine eval8DAE(& USE varExtrFida_module, only:varExtractFida USE updateVarsFida_module, only:updateVarsFida ! update variables USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy - USE soilCmpresFida_module, only:soilCmpresFida ! compute soil compression + USE computFlux_module, only:soilCmpresFida ! compute soil compression USE computFlux_module, only:computFlux ! compute fluxes given a state vector USE computHeatCap_module,only:computHeatCap ! compute heat capacity USE computHeatCap_module,only:computHeatCapAnalytic ! compute heat capacity diff --git a/build/source/engine/soilCmpresFida.f90 b/build/source/engine/soilCmpresFida.f90 deleted file mode 100644 index ced0c8c62..000000000 --- a/build/source/engine/soilCmpresFida.f90 +++ /dev/null @@ -1,135 +0,0 @@ - -module soilCmpresFida_module - -! data types -USE nrtype - -! provide access to the derived types to define the data structures -USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - model_options ! defines the model decisions - -! indices that define elements of the data structures -USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure -USE var_lookup,only:iLookPARAM ! named variables for structure elements -USE var_lookup,only:iLookFORCE ! named variables for structure elements -USE var_lookup,only:iLookPROG ! named variables for structure elements -USE var_lookup,only:iLookINDEX ! named variables for structure elements -USE var_lookup,only:iLookDIAG ! named variables for structure elements -USE var_lookup,only:iLookFLUX ! named variables for structure elements -USE var_lookup,only:iLookDERIV ! named variables for structure elements - -! missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing real number - -! layer types -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil - -! access the global print flag -USE globalData,only:globalPrintFlag - -! control parameters -USE globalData,only:verySmall ! a very small number -USE globalData,only:veryBig ! a very big number -USE globalData,only:dx ! finite difference increment - -! constants -USE multiconst,only:& - gravity, & ! acceleration of gravity (m s-2) - Tfreeze, & ! temperature at freezing (K) - LH_fus, & ! latent heat of fusion (J kg-1) - LH_vap, & ! latent heat of vaporization (J kg-1) - LH_sub, & ! latent heat of sublimation (J kg-1) - Cp_air, & ! specific heat of air (J kg-1 K-1) - iden_air, & ! intrinsic density of air (kg m-3) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water ! intrinsic density of liquid water (kg m-3) - -! look-up values for the choice of groundwater representation (local-column, or single-basin) -USE mDecisions_module,only: & - localColumn, & ! separate groundwater representation in each local soil column - singleBasin ! single groundwater store over the entire basin - -! look-up values for the choice of groundwater parameterization -USE mDecisions_module,only: & - qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization - bigBucket, & ! a big bucket (lumped aquifer model) - noExplicit ! no explicit groundwater parameterization - -! look-up values for the form of Richards' equation -USE mDecisions_module,only: & - moisture, & ! moisture-based form of Richards' equation - mixdform ! mixed form of Richards' equation - -! look-up values for the choice of boundary conditions for hydrology -USE mDecisions_module,only: & - prescribedHead, & ! prescribed head (volumetric liquid water content for mixed form of Richards' eqn) - funcBottomHead, & ! function of matric head in the lower-most layer - freeDrainage, & ! free drainage - liquidFlux, & ! liquid water flux - zeroFlux ! zero flux - -implicit none -private -public::soilCmpresFida -contains - - - - ! ********************************************************************************************************** - ! public subroutine soilCmpres: compute soil compressibility (-) and its derivative w.r.t matric head (m-1) - ! ********************************************************************************************************** - subroutine soilCmpresFida(& - ! input: - ixRichards, & ! intent(in): choice of option for Richards' equation - ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers - mLayerMatricHeadPrime, & ! intent(in): matric head at the start of the time step (m) - mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) - mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice content in each soil layer (-) - specificStorage, & ! intent(in): specific storage coefficient (m-1) - theta_sat, & ! intent(in): soil porosity (-) - ! output: - compress, & ! intent(out): compressibility of the soil matrix (-) - dCompress_dPsi, & ! intent(out): derivative in compressibility w.r.t. matric head (m-1) - err,message) ! intent(out): error code and error message - implicit none - ! input: - integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation - integer(i4b),intent(in) :: ixBeg,ixEnd ! start and end indices defining desired layers - real(rkind),intent(in) :: mLayerMatricHeadPrime(:) ! matric head at the start of the time step (m) - real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) - real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) - real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) - real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) - ! output: - real(rkind),intent(inout) :: compress(:) ! soil compressibility (-) - real(rkind),intent(inout) :: dCompress_dPsi(:) ! derivative in soil compressibility w.r.t. matric head (m-1) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! local variables - integer(i4b) :: iLayer ! index of soil layer - ! -------------------------------------------------------------- - ! initialize error control - err=0; message='soilCmpresFida/' - ! (only compute for the mixed form of Richards' equation) - if(ixRichards==mixdform)then - do iLayer=1,size(mLayerMatricHeadPrime) - if(iLayer>=ixBeg .and. iLayer<=ixEnd)then - ! compute the derivative for the compressibility term (m-1) - dCompress_dPsi(iLayer) = specificStorage*(mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer))/theta_sat(iLayer) - ! compute the compressibility term (-) - compress(iLayer) = mLayerMatricHeadPrime(iLayer) * dCompress_dPsi(iLayer) - endif - end do - else - compress(:) = 0._rkind - dCompress_dPsi(:) = 0._rkind - end if - end subroutine soilCmpresFida - -end module soilCmpresFida_module From 14ca7c6931c7b173b4e66db7bdfad714bb78d625 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 30 Jun 2021 10:37:41 -0600 Subject: [PATCH 0170/1472] all Fida changed to Sundials --- build/Makefile | 8 ++-- build/source/engine/computFlux.f90 | 8 ++-- build/source/engine/computJacDAE.f90 | 2 +- build/source/engine/eval8DAE.f90 | 16 +++---- build/source/engine/eval8JacDAE.f90 | 12 ++--- build/source/engine/snowLiqFlx.f90 | 10 ++--- build/source/engine/soil_utils_fida.f90 | 8 ++-- build/source/engine/systemSolvSundials.f90 | 2 +- ...atStateFida.f90 => updatStateSundials.f90} | 44 +++++++++---------- ...ateVarsFida.f90 => updateVarsSundials.f90} | 30 ++++++------- ...eVarsFida2.f90 => updateVarsSundials2.f90} | 30 ++++++------- .../{varExtrFida.f90 => varExtrSundials.f90} | 14 +++--- build/source/engine/varSubstepSundials.f90 | 26 +++++------ 13 files changed, 105 insertions(+), 105 deletions(-) rename build/source/engine/{updatStateFida.f90 => updatStateSundials.f90} (94%) rename build/source/engine/{updateVarsFida.f90 => updateVarsSundials.f90} (98%) rename build/source/engine/{updateVarsFida2.f90 => updateVarsSundials2.f90} (98%) rename build/source/engine/{varExtrFida.f90 => varExtrSundials.f90} (99%) diff --git a/build/Makefile b/build/Makefile index b78b1e766..66edeb6c9 100644 --- a/build/Makefile +++ b/build/Makefile @@ -191,7 +191,7 @@ SUMMA_UTILMS= \ soil_utils.f90 \ soil_utils_fida.f90 \ updatState.f90 \ - updatStateFida.f90 \ + updatStateSundials.f90 \ matrixOper.f90 UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) @@ -268,11 +268,11 @@ NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) SUMMA_MODRUN = \ indexState.f90 \ getVectorz.f90 \ - varExtrFida.f90 \ + varExtrSundials.f90 \ t2enthalpy.f90 \ updateVars.f90 \ - updateVarsFida.f90 \ - updateVarsFida2.f90 \ + updateVarsSundials.f90 \ + updateVarsSundials2.f90 \ var_derive.f90 \ read_force.f90 \ derivforce.f90 \ diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index ff80c31f4..20c018613 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -97,7 +97,7 @@ module computFlux_module private public::computFlux public::soilCmpres -public::soilCmpresFida +public::soilCmpresSundials contains ! ********************************************************************************************************* @@ -909,7 +909,7 @@ end subroutine soilCmpres ! ********************************************************************************************************** ! public subroutine soilCmpres: compute soil compressibility (-) and its derivative w.r.t matric head (m-1) ! ********************************************************************************************************** - subroutine soilCmpresFida(& + subroutine soilCmpresSundials(& ! input: ixRichards, & ! intent(in): choice of option for Richards' equation ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers @@ -940,7 +940,7 @@ subroutine soilCmpresFida(& integer(i4b) :: iLayer ! index of soil layer ! -------------------------------------------------------------- ! initialize error control -err=0; message='soilCmpresFida/' +err=0; message='soilCmpresSundials/' ! (only compute for the mixed form of Richards' equation) if(ixRichards==mixdform)then do iLayer=1,size(mLayerMatricHeadPrime) @@ -955,6 +955,6 @@ subroutine soilCmpresFida(& compress(:) = 0._rkind dCompress_dPsi(:) = 0._rkind end if -end subroutine soilCmpresFida +end subroutine soilCmpresSundials end module computFlux_module diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 687ea25c9..ae9f0b9e7 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -842,7 +842,7 @@ subroutine computJacDAE(& end select ! type of matrix if(any(isNan(aJac)))then - print *, 'Nan in computeJacobFida' + print *, 'Nan in computeJacobSundials' stop 1 message=trim(message)//'we found NaN' err=20; return diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index c22d49d8c..374f7338c 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -152,11 +152,11 @@ subroutine eval8DAE(& err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- ! provide access to subroutines - USE varExtrFida_module, only:varExtract ! extract variables from the state vector - USE varExtrFida_module, only:varExtractFida - USE updateVarsFida_module, only:updateVarsFida ! update variables + USE varExtrSundials_module, only:varExtract ! extract variables from the state vector + USE varExtrSundials_module, only:varExtractSundials + USE updateVarsSundials_module, only:updateVarsSundials ! update variables USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy - USE computFlux_module, only:soilCmpresFida ! compute soil compression + USE computFlux_module, only:soilCmpresSundials ! compute soil compression USE computFlux_module, only:computFlux ! compute fluxes given a state vector USE computHeatCap_module,only:computHeatCap ! compute heat capacity USE computHeatCap_module,only:computHeatCapAnalytic ! compute heat capacity @@ -427,7 +427,7 @@ subroutine eval8DAE(& - call varExtractFida(& + call varExtractSundials(& ! input stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -451,7 +451,7 @@ subroutine eval8DAE(& if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - call updateVarsFida(& + call updateVarsSundials(& ! input dt_cur, & .false., & ! intent(in): logical flag to adjust temperature to account for the energy @@ -677,7 +677,7 @@ subroutine eval8DAE(& deriv_data, & ! intent(out): derivatives in model fluxes w.r.t. relevant state variables ! input-output: flux vector and baseflow derivatives ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later in computeJacobFida + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later in computeJacobSundials fluxVec, & ! intent(out): flux vector (mixed units) ! output: error control err,cmessage) ! intent(out): error code and error message @@ -688,7 +688,7 @@ subroutine eval8DAE(& ! compute soil compressibility (-) and its derivative w.r.t. matric head (m) ! NOTE: we already extracted trial matrix head and volumetric liquid water as part of the flux calculations - call soilCmpresFida(& + call soilCmpresSundials(& ! input: ixRichards, & ! intent(in): choice of option for Richards' equation ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers diff --git a/build/source/engine/eval8JacDAE.f90 b/build/source/engine/eval8JacDAE.f90 index c10224f2a..e4a5f8a27 100644 --- a/build/source/engine/eval8JacDAE.f90 +++ b/build/source/engine/eval8JacDAE.f90 @@ -117,9 +117,9 @@ subroutine eval8JacDAE(& err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- ! provide access to subroutines - USE varExtrFida_module, only:varExtract ! extract variables from the state vector - USE varExtrFida_module, only:varExtractFida - USE updateVarsFida2_module, only:updateVarsFida2 ! update prognostic variables + USE varExtrSundials_module, only:varExtract ! extract variables from the state vector + USE varExtrSundials_module, only:varExtractSundials + USE updateVarsSundials2_module, only:updateVarsSundials2 ! update prognostic variables USE computJacDAE_module,only:computJacDAE implicit none ! -------------------------------------------------------------------------------------------------------------------------------- @@ -241,7 +241,7 @@ subroutine eval8JacDAE(& ! extract derivative of variables from derivative of the model state vector - call varExtractFida(& + call varExtractSundials(& ! input stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -267,7 +267,7 @@ subroutine eval8JacDAE(& - call updateVarsFida2(& + call updateVarsSundials2(& ! input .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU @@ -314,7 +314,7 @@ subroutine eval8JacDAE(& ! compute the analytical Jacobian matrix ! NOTE: The derivatives were computed in the previous call to computFlux - ! This occurred either at the call to eval8DAE at the start of sysSolveFida + ! This occurred either at the call to eval8DAE at the start of sysSolveSundials ! or in the call to eval8DAE in the previous iteration dt1 = 1._qp call computJacDAE(& diff --git a/build/source/engine/snowLiqFlx.f90 b/build/source/engine/snowLiqFlx.f90 index 312dc440c..415aba608 100644 --- a/build/source/engine/snowLiqFlx.f90 +++ b/build/source/engine/snowLiqFlx.f90 @@ -43,7 +43,7 @@ module snowLiqFlx_module implicit none private public::snowLiqFlx -public::snowLiqFlxFida +public::snowLiqFlxSundials contains @@ -202,9 +202,9 @@ end subroutine snowLiqFlx ! ************************************************************************************************ - ! public subroutine snowLiqFlxFida: compute liquid water flux through the snowpack + ! public subroutine snowLiqFlxSundials: compute liquid water flux through the snowpack ! ************************************************************************************************ - subroutine snowLiqFlxFida(& + subroutine snowLiqFlxSundials(& ! input: model control nSnow, & ! intent(in): number of snow layers firstFluxCall, & ! intent(in): the first flux call @@ -275,7 +275,7 @@ subroutine snowLiqFlxFida(& ) ! association of local variables with information in the data structures ! ------------------------------------------------------------------------------------------------------------------------------------------ ! initialize error control - err=0; message='snowLiqFlxFida/' + err=0; message='snowLiqFlxSundials/' ! check that the input vectors match nSnow if(size(mLayerVolFracLiqTrial)/=nSnow .or. size(mLayerVolFracIce)/=nSnow .or. & @@ -352,7 +352,7 @@ subroutine snowLiqFlxFida(& ! end association of local variables with information in the data structures end associate - end subroutine snowLiqFlxFida + end subroutine snowLiqFlxSundials end module snowLiqFlx_module diff --git a/build/source/engine/soil_utils_fida.f90 b/build/source/engine/soil_utils_fida.f90 index 0c62bb893..a67bee3da 100644 --- a/build/source/engine/soil_utils_fida.f90 +++ b/build/source/engine/soil_utils_fida.f90 @@ -38,7 +38,7 @@ module soil_utils_fida_module ! routines to make public -public::liquidHeadFida +public::liquidHeadSundials public::d2Theta_dPsi2 public::d2Theta_dTk2 @@ -50,7 +50,7 @@ module soil_utils_fida_module ! ****************************************************************************************************************************** ! public subroutine: compute the liquid water matric potential (and the derivatives w.r.t. total matric potential and temperature) ! ****************************************************************************************************************************** - subroutine liquidHeadFida(& + subroutine liquidHeadSundials(& ! input matricHeadTotal ,& ! intent(in) : total water matric potential (m) matricHeadTotalPrime ,& ! intent(in) @@ -98,7 +98,7 @@ subroutine liquidHeadFida(& real(rkind) :: effSatPrime ! ------------------------------------------------------------------------------------------------------------------------------ ! initialize error control - err=0; message='liquidHeadFida/' + err=0; message='liquidHeadSundials/' ! ** partially frozen soil if(volFracIce > verySmall .and. matricHeadTotal < 0._rkind)then ! check that ice exists and that the soil is unsaturated @@ -177,7 +177,7 @@ subroutine liquidHeadFida(& if(present(dPsiLiq_dTemp)) dPsiLiq_dTemp = 0._rkind ! derivative=0 because no impact of temperature for unfrozen conditions end if ! (if ice exists) - end subroutine liquidHeadFida + end subroutine liquidHeadSundials ! ****************************************************************************************************************************** diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 9bf96e5bc..92665c9c6 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -136,7 +136,7 @@ subroutine systemSolvSundials(& USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy USE tol4IDA_module,only:popTol4IDA USE solveByIDA_module,only:solveByIDA -! use varExtrFida_module, only:countDiscontinuity +! use varExtrSundials_module, only:countDiscontinuity use, intrinsic :: iso_c_binding implicit none ! --------------------------------------------------------------------------------------- diff --git a/build/source/engine/updatStateFida.f90 b/build/source/engine/updatStateSundials.f90 similarity index 94% rename from build/source/engine/updatStateFida.f90 rename to build/source/engine/updatStateSundials.f90 index 34c2b683c..1622dfe6d 100644 --- a/build/source/engine/updatStateFida.f90 +++ b/build/source/engine/updatStateSundials.f90 @@ -1,5 +1,5 @@ -module updatStateFida_module +module updatStateSundials_module USE nrtype ! physical constants USE multiconst,only:& @@ -11,10 +11,10 @@ module updatStateFida_module LH_fus ! latent heat of fusion (J kg-1) implicit none private -public::updateSnowFida -public::updateSoilFida -public::updateSoilFida2 -public::updateVegFida +public::updateSnowSundials +public::updateSoilSundials +public::updateSoilSundials2 +public::updateVegSundials real(rkind),parameter :: verySmall=1e-14_rkind ! a very small number (used to avoid divide by zero) @@ -22,11 +22,11 @@ module updatStateFida_module ! ************************************************************************************************************* - ! public subroutine updateVegFida: compute phase change impacts on volumetric liquid water and ice + ! public subroutine updateVegSundials: compute phase change impacts on volumetric liquid water and ice ! Input: Theta * canopyDepth * iden_water ! Outputs: VolFracLiq * canopyDepth * iden_water and VolFracIce * canopyDepth * iden_ice ! ************************************************************************************************************* - subroutine updateVegFida(& + subroutine updateVegSundials(& ! input Temp ,& ! intent(in): temperature (K) Theta ,& ! intent(in): volume fraction of total water (-) @@ -60,7 +60,7 @@ subroutine updateVegFida(& integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! initialize error control - err=0; message="updateVegFida/" + err=0; message="updateVegSundials/" ! compute the volumetric fraction of liquid water and ice (-) fLiq = fracliquid(Temp,snowfrz_scale) @@ -70,13 +70,13 @@ subroutine updateVegFida(& VolFracIcePrime = ( ThetaPrime - VolFracLiqPrime ) - end subroutine updateVegFida + end subroutine updateVegSundials ! ************************************************************************************************************* - ! public subroutine updateSnowFida: compute phase change impacts on volumetric liquid water and ice + ! public subroutine updateSnowSundials: compute phase change impacts on volumetric liquid water and ice ! ************************************************************************************************************* - subroutine updateSnowFida(& + subroutine updateSnowSundials(& ! input mLayerTemp ,& ! intent(in): temperature (K) mLayerTheta ,& ! intent(in): volume fraction of total water (-) @@ -110,7 +110,7 @@ subroutine updateSnowFida(& integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! initialize error control - err=0; message="updateSnowFida/" + err=0; message="updateSnowSundials/" ! compute the volumetric fraction of liquid water and ice (-) fLiq = fracliquid(mLayerTemp,snowfrz_scale) @@ -120,12 +120,12 @@ subroutine updateSnowFida(& mLayerVolFracIcePrime = ( mLayerThetaPrime - mLayerVolFracLiqPrime ) * (iden_water/iden_ice) - end subroutine updateSnowFida + end subroutine updateSnowSundials ! ************************************************************************************************************* - ! public subroutine updateSoilFida: compute phase change impacts on matric head and volumetric liquid water and ice + ! public subroutine updateSoilSundials: compute phase change impacts on matric head and volumetric liquid water and ice ! ************************************************************************************************************* - subroutine updateSoilFida(& + subroutine updateSoilSundials(& ! input dt_cur, & mLayerTemp ,& ! intent(in): temperature vector (K) @@ -179,7 +179,7 @@ subroutine updateSoilFida(& real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) ! initialize error control - err=0; message="updateSoilFida/" + err=0; message="updateSoilSundials/" ! compute fractional **volume** of total water (liquid plus ice) mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) @@ -232,12 +232,12 @@ subroutine updateSoilFida(& end if ! (check if soil is partially frozen) - end subroutine updateSoilFida + end subroutine updateSoilSundials ! ************************************************************************************************************* - ! public subroutine updateSoilFida: compute phase change impacts on matric head and volumetric liquid water and ice + ! public subroutine updateSoilSundials: compute phase change impacts on matric head and volumetric liquid water and ice ! ************************************************************************************************************* - subroutine updateSoilFida2(& + subroutine updateSoilSundials2(& ! input mLayerTemp ,& ! intent(in): temperature vector (K) mLayerMatricHead ,& ! intent(in): matric head (m) @@ -285,7 +285,7 @@ subroutine updateSoilFida2(& real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) ! initialize error control - err=0; message="updateSoilFida2/" + err=0; message="updateSoilSundials2/" ! compute fractional **volume** of total water (liquid plus ice) mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) @@ -330,5 +330,5 @@ subroutine updateSoilFida2(& end if ! (check if soil is partially frozen) - end subroutine updateSoilFida2 -end module updatStateFida_module + end subroutine updateSoilSundials2 +end module updatStateSundials_module diff --git a/build/source/engine/updateVarsFida.f90 b/build/source/engine/updateVarsSundials.f90 similarity index 98% rename from build/source/engine/updateVarsFida.f90 rename to build/source/engine/updateVarsSundials.f90 index 17fcc0fbd..7dbeecb59 100644 --- a/build/source/engine/updateVarsFida.f90 +++ b/build/source/engine/updateVarsSundials.f90 @@ -18,7 +18,7 @@ ! You should have received a copy of the GNU General Public License ! along with this program. If not, see . -module updateVarsFida_module +module updateVarsSundials_module ! data types USE nrtype @@ -76,9 +76,9 @@ module updateVarsFida_module USE var_lookup,only:iLookINDEX ! named variables for structure elements ! provide access to routines to update states -USE updatStateFida_module,only:updateVegFida ! update snow states -USE updatStateFida_module,only:updateSnowFida ! update snow states -USE updatStateFida_module,only:updateSoilFida ! update soil states +USE updatStateSundials_module,only:updateVegSundials ! update snow states +USE updatStateSundials_module,only:updateSnowSundials ! update snow states +USE updatStateSundials_module,only:updateSoilSundials ! update soil states ! provide access to functions for the constitutive functions and derivatives USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) @@ -89,21 +89,21 @@ module updateVarsFida_module USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists -USE soil_utils_fida_module,only:liquidHeadFida ! compute the liquid water matric potential +USE soil_utils_fida_module,only:liquidHeadSundials ! compute the liquid water matric potential ! IEEE checks USE, intrinsic :: ieee_arithmetic ! check values (NaN, etc.) implicit none private -public::updateVarsFida +public::updateVarsSundials contains ! ********************************************************************************************************** - ! public subroutine updateVarsFida: compute diagnostic variables + ! public subroutine updateVarsSundials: compute diagnostic variables ! ********************************************************************************************************** - subroutine updateVarsFida(& + subroutine updateVarsSundials(& ! input dt_cur, & do_adjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze @@ -280,7 +280,7 @@ subroutine updateVarsFida(& ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message='updateVarsFida/' + err=0; message='updateVarsSundials/' ! allocate space and assign values to the flag vector allocate(computedCoupling(size(ixMapSubset2Full)),stat=err) ! .true. if computed the coupling for a given state variable @@ -500,7 +500,7 @@ subroutine updateVarsFida(& ! *** vegetation canopy case(iname_veg) ! compute mass of liquid water and ice - call updateVegFida(& + call updateVegSundials(& xTemp, & ! intent(in) : temperature (K) scalarCanopyWatTrial, & ! intent(in) : mass of total water (-) snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) @@ -517,7 +517,7 @@ subroutine updateVarsFida(& ! *** snow layers case(iname_snow) - call updateSnowFida(& + call updateSnowSundials(& xTemp, & ! intent(in) : temperature (K) mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) @@ -535,7 +535,7 @@ subroutine updateVarsFida(& case(iname_soil) ! compute volumetric fraction of liquid water and ice - call updateSoilFida(& + call updateSoilSundials(& dt_cur, & xTemp, & ! intent(in) : temperature (K) mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) @@ -616,7 +616,7 @@ subroutine updateVarsFida(& ! case of energy state or coupled solution else ! compute the liquid matric potential (and the derivatives w.r.t. total matric potential and temperature) - call liquidHeadFida(& + call liquidHeadSundials(& ! input mLayerMatricHeadTrial(ixControlIndex) ,& ! intent(in) : total water matric potential (m) mLayerMatricHeadPrime(ixControlIndex) ,& ! @@ -649,7 +649,7 @@ subroutine updateVarsFida(& ! end association to the variables in the data structures end associate - end subroutine updateVarsFida + end subroutine updateVarsSundials -end module updateVarsFida_module +end module updateVarsSundials_module diff --git a/build/source/engine/updateVarsFida2.f90 b/build/source/engine/updateVarsSundials2.f90 similarity index 98% rename from build/source/engine/updateVarsFida2.f90 rename to build/source/engine/updateVarsSundials2.f90 index 74fa5a632..387786be7 100644 --- a/build/source/engine/updateVarsFida2.f90 +++ b/build/source/engine/updateVarsSundials2.f90 @@ -18,7 +18,7 @@ ! You should have received a copy of the GNU General Public License ! along with this program. If not, see . -module updateVarsFida2_module +module updateVarsSundials2_module ! data types USE nrtype @@ -76,9 +76,9 @@ module updateVarsFida2_module USE var_lookup,only:iLookINDEX ! named variables for structure elements ! provide access to routines to update states -USE updatStateFida_module,only:updateVegFida ! update snow states -USE updatStateFida_module,only:updateSnowFida ! update snow states -USE updatStateFida_module,only:updateSoilFida2 ! update soil states +USE updatStateSundials_module,only:updateVegSundials ! update snow states +USE updatStateSundials_module,only:updateSnowSundials ! update snow states +USE updatStateSundials_module,only:updateSoilSundials2 ! update soil states ! provide access to functions for the constitutive functions and derivatives USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) @@ -89,7 +89,7 @@ module updateVarsFida2_module USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists -USE soil_utils_fida_module,only:liquidHeadFida ! compute the liquid water matric potential +USE soil_utils_fida_module,only:liquidHeadSundials ! compute the liquid water matric potential USE soil_utils_fida_module,only:d2Theta_dPsi2 USE soil_utils_fida_module,only:d2Theta_dTk2 @@ -98,14 +98,14 @@ module updateVarsFida2_module implicit none private -public::updateVarsFida2 +public::updateVarsSundials2 contains ! ********************************************************************************************************** - ! public subroutine updateVarsFida2: compute diagnostic variables + ! public subroutine updateVarsSundials2: compute diagnostic variables ! ********************************************************************************************************** - subroutine updateVarsFida2(& + subroutine updateVarsSundials2(& ! input do_adjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU @@ -284,7 +284,7 @@ subroutine updateVarsFida2(& ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message='updateVarsFida2/' + err=0; message='updateVarsSundials2/' ! allocate space and assign values to the flag vector allocate(computedCoupling(size(ixMapSubset2Full)),stat=err) ! .true. if computed the coupling for a given state variable @@ -517,7 +517,7 @@ subroutine updateVarsFida2(& ! *** vegetation canopy case(iname_veg) ! compute volumetric fraction of liquid water and ice - call updateVegFida(& + call updateVegSundials(& xTemp, & ! intent(in) : temperature (K) scalarCanopyWatTrial, & ! intent(in) : volumetric fraction of total water (-) snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) @@ -534,7 +534,7 @@ subroutine updateVarsFida2(& ! *** snow layers case(iname_snow) - call updateSnowFida(& + call updateSnowSundials(& xTemp, & ! intent(in) : temperature (K) mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) @@ -552,7 +552,7 @@ subroutine updateVarsFida2(& case(iname_soil) ! compute volumetric fraction of liquid water and ice - call updateSoilFida2(& + call updateSoilSundials2(& xTemp, & ! intent(in) : temperature (K) mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) mLayerTempPrime(iLayer), & @@ -727,7 +727,7 @@ subroutine updateVarsFida2(& ! case of energy state or coupled solution else ! compute the liquid matric potential (and the derivatives w.r.t. total matric potential and temperature) - call liquidHeadFida(& + call liquidHeadSundials(& ! input mLayerMatricHeadTrial(ixControlIndex) ,& ! intent(in) : total water matric potential (m) mLayerMatricHeadPrime(ixControlIndex) ,& ! @@ -760,7 +760,7 @@ subroutine updateVarsFida2(& ! end association to the variables in the data structures end associate - end subroutine updateVarsFida2 + end subroutine updateVarsSundials2 ! ********************************************************************************************************** @@ -797,4 +797,4 @@ subroutine xTempSolve(& derivative = heatCap + LH_fus*iden_water*dLiq_dT ! J m-3 K-1 end subroutine xTempSolve -end module updateVarsFida2_module +end module updateVarsSundials2_module diff --git a/build/source/engine/varExtrFida.f90 b/build/source/engine/varExtrSundials.f90 similarity index 99% rename from build/source/engine/varExtrFida.f90 rename to build/source/engine/varExtrSundials.f90 index 339d1e2b0..747c7ad07 100644 --- a/build/source/engine/varExtrFida.f90 +++ b/build/source/engine/varExtrSundials.f90 @@ -1,6 +1,6 @@ -module varExtrFida_module +module varExtrSundials_module ! data types USE nrtype @@ -75,7 +75,7 @@ module varExtrFida_module implicit none private public::varExtract -public::varExtractFida +public::varExtractSundials public::residDiscontinuity public::countDiscontinuity @@ -215,9 +215,9 @@ subroutine varExtract(& end subroutine varExtract ! ********************************************************************************************************** - ! public subroutine varExtractFida: extract variables from the state vector and compute diagnostic variables + ! public subroutine varExtractSundials: extract variables from the state vector and compute diagnostic variables ! ********************************************************************************************************** - subroutine varExtractFida(& + subroutine varExtractSundials(& ! input stateVecPrime, & ! intent(in): model state vector (mixed units) diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -290,7 +290,7 @@ subroutine varExtractFida(& ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message='varExtractFida/' + err=0; message='varExtractSundials/' ! check if computing the vegetation flux @@ -339,7 +339,7 @@ subroutine varExtractFida(& end associate - end subroutine varExtractFida + end subroutine varExtractSundials ! ********************************************************************************************************** @@ -511,4 +511,4 @@ end subroutine countDiscontinuity -end module varExtrFida_module +end module varExtrSundials_module diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index f129afc11..bd2f662fc 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -130,8 +130,8 @@ subroutine varSubstepSundials(& USE systemSolv_module,only:systemSolv ! solve the system of equations for one time step USE getVectorz_module,only:popStateVec ! populate the state vector USE getVectorz_module,only:varExtract ! extract variables from the state vector - USE updateVarsFida_module,only:updateVarsFida ! update prognostic variables - USE varExtrFida_module, only:varExtractFida + USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables + USE varExtrSundials_module, only:varExtractSundials ! identify name of variable type (for error message) USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages USE systemSolvSundials_module,only:systemSolvSundials @@ -267,7 +267,7 @@ subroutine varSubstepSundials(& flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) end do - ! initialize the total energy fluxes (modified in updateProgFida) + ! initialize the total energy fluxes (modified in updateProgSundials) sumCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) sumLatHeatCanopyEvap = 0._rkind ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) sumSenHeatCanopy = 0._rkind ! sensible heat flux from the canopy to the canopy air space (W m-2) @@ -400,7 +400,7 @@ subroutine varSubstepSundials(& checkNrgBalance = .true. ! update prognostic variables - call updateProgFida(dt_out,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control + call updateProgSundials(dt_out,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures waterBalanceError,nrgFluxModified,err,cmessage) ! output: flags and error control if(err/=0)then @@ -436,7 +436,7 @@ subroutine varSubstepSundials(& endif ! if errors in prognostic update - ! get the total energy fluxes (modified in updateProgFida) + ! get the total energy fluxes (modified in updateProgSundials) if(nrgFluxModified .or. indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then sumCanopyEvaporation = sumCanopyEvaporation + dt_out*flux_temp%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dt_out*flux_temp%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) @@ -538,14 +538,14 @@ end subroutine varSubstepSundials ! ********************************************************************************************************** - ! private subroutine updateProgFida: update prognostic variables + ! private subroutine updateProgSundials: update prognostic variables ! ********************************************************************************************************** - subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control + subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures waterBalanceError,nrgFluxModified,err,message) ! output: flags and error control USE getVectorz_module,only:varExtract ! extract variables from the state vector - USE updateVarsFida_module,only:updateVarsFida ! update prognostic variables - USE varExtrFida_module, only:varExtractFida + USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables + USE varExtrSundials_module, only:varExtractSundials USE computEnthalpy_module,only:computEnthalpy USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy implicit none @@ -680,7 +680,7 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt ) ! associating flux variables in the data structure ! ------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message='updateProgFida/' + err=0; message='updateProgSundials/' ! initialize water balancmLayerVolFracWatTriale error waterBalanceError=.false. @@ -718,7 +718,7 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - call varExtractFida(& + call varExtractSundials(& ! input stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -743,7 +743,7 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt ! update diagnostic variables - call updateVarsFida(& + call updateVarsSundials(& ! input dt, & doAdjustTemp, & ! intent(in): logical flag to adjust temperature to accou melt+freeze @@ -1085,6 +1085,6 @@ subroutine updateProgFida(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,unt ! end associations to info in the data structures end associate - end subroutine updateProgFida + end subroutine updateProgSundials end module varSubstepSundials_module From cb509488b41be926b24e9a82ba8c2b494a5db0d1 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 30 Jun 2021 10:41:50 -0600 Subject: [PATCH 0171/1472] updateVarsFida2 to updateVars4JacDAE --- build/Makefile | 2 +- build/source/engine/eval8JacDAE.f90 | 4 ++-- ...dateVarsSundials2.f90 => updateVars4JacDAE.f90} | 14 +++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) rename build/source/engine/{updateVarsSundials2.f90 => updateVars4JacDAE.f90} (99%) diff --git a/build/Makefile b/build/Makefile index 66edeb6c9..d3d321009 100644 --- a/build/Makefile +++ b/build/Makefile @@ -272,7 +272,7 @@ SUMMA_MODRUN = \ t2enthalpy.f90 \ updateVars.f90 \ updateVarsSundials.f90 \ - updateVarsSundials2.f90 \ + updateVars4JacDAE.f90 \ var_derive.f90 \ read_force.f90 \ derivforce.f90 \ diff --git a/build/source/engine/eval8JacDAE.f90 b/build/source/engine/eval8JacDAE.f90 index e4a5f8a27..4894c114d 100644 --- a/build/source/engine/eval8JacDAE.f90 +++ b/build/source/engine/eval8JacDAE.f90 @@ -119,7 +119,7 @@ subroutine eval8JacDAE(& ! provide access to subroutines USE varExtrSundials_module, only:varExtract ! extract variables from the state vector USE varExtrSundials_module, only:varExtractSundials - USE updateVarsSundials2_module, only:updateVarsSundials2 ! update prognostic variables + USE updateVars4JacDAE_module, only:updateVars4JacDAE ! update prognostic variables USE computJacDAE_module,only:computJacDAE implicit none ! -------------------------------------------------------------------------------------------------------------------------------- @@ -267,7 +267,7 @@ subroutine eval8JacDAE(& - call updateVarsSundials2(& + call updateVars4JacDAE(& ! input .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU diff --git a/build/source/engine/updateVarsSundials2.f90 b/build/source/engine/updateVars4JacDAE.f90 similarity index 99% rename from build/source/engine/updateVarsSundials2.f90 rename to build/source/engine/updateVars4JacDAE.f90 index 387786be7..45618f3a0 100644 --- a/build/source/engine/updateVarsSundials2.f90 +++ b/build/source/engine/updateVars4JacDAE.f90 @@ -18,7 +18,7 @@ ! You should have received a copy of the GNU General Public License ! along with this program. If not, see . -module updateVarsSundials2_module +module updateVars4JacDAE_module ! data types USE nrtype @@ -98,14 +98,14 @@ module updateVarsSundials2_module implicit none private -public::updateVarsSundials2 +public::updateVars4JacDAE contains ! ********************************************************************************************************** - ! public subroutine updateVarsSundials2: compute diagnostic variables + ! public subroutine updateVars4JacDAE: compute diagnostic variables ! ********************************************************************************************************** - subroutine updateVarsSundials2(& + subroutine updateVars4JacDAE(& ! input do_adjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU @@ -284,7 +284,7 @@ subroutine updateVarsSundials2(& ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message='updateVarsSundials2/' + err=0; message='updateVars4JacDAE/' ! allocate space and assign values to the flag vector allocate(computedCoupling(size(ixMapSubset2Full)),stat=err) ! .true. if computed the coupling for a given state variable @@ -760,7 +760,7 @@ subroutine updateVarsSundials2(& ! end association to the variables in the data structures end associate - end subroutine updateVarsSundials2 + end subroutine updateVars4JacDAE ! ********************************************************************************************************** @@ -797,4 +797,4 @@ subroutine xTempSolve(& derivative = heatCap + LH_fus*iden_water*dLiq_dT ! J m-3 K-1 end subroutine xTempSolve -end module updateVarsSundials2_module +end module updateVars4JacDAE_module From 705bd8adf134385743057a67dec26aeddce3ca29 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 30 Jun 2021 10:56:26 -0600 Subject: [PATCH 0172/1472] soil_util_fida to soil_utilSundials --- build/Makefile | 2 +- .../engine/{soil_utils_fida.f90 => soil_utilsSundials.f90} | 4 ++-- build/source/engine/systemSolvSundials.f90 | 2 +- build/source/engine/updateVars4JacDAE.f90 | 6 +++--- build/source/engine/updateVarsSundials.f90 | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) rename build/source/engine/{soil_utils_fida.f90 => soil_utilsSundials.f90} (99%) diff --git a/build/Makefile b/build/Makefile index d3d321009..c4c027bdf 100644 --- a/build/Makefile +++ b/build/Makefile @@ -189,7 +189,7 @@ SUMMA_UTILMS= \ mDecisions.f90 \ snow_utils.f90 \ soil_utils.f90 \ - soil_utils_fida.f90 \ + soil_utilsSundials.f90 \ updatState.f90 \ updatStateSundials.f90 \ matrixOper.f90 diff --git a/build/source/engine/soil_utils_fida.f90 b/build/source/engine/soil_utilsSundials.f90 similarity index 99% rename from build/source/engine/soil_utils_fida.f90 rename to build/source/engine/soil_utilsSundials.f90 index a67bee3da..ecb2a8276 100644 --- a/build/source/engine/soil_utils_fida.f90 +++ b/build/source/engine/soil_utilsSundials.f90 @@ -18,7 +18,7 @@ ! You should have received a copy of the GNU General Public License ! along with this program. If not, see . -module soil_utils_fida_module +module soil_utilsSundials_module ! data types USE nrtype @@ -231,4 +231,4 @@ function d2Theta_dTk2(Tk,theta_res,theta_sat,alpha,n,m) end function d2Theta_dTk2 -end module soil_utils_fida_module +end module soil_utilsSundials_module diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 92665c9c6..a43b2ca0b 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -486,7 +486,7 @@ subroutine systemSolvSundials(& - ! check if fida is successful + ! check if IDA is successful if( .not.idaSucceeds )then err = 20 message=trim(message)//trim(cmessage) diff --git a/build/source/engine/updateVars4JacDAE.f90 b/build/source/engine/updateVars4JacDAE.f90 index 45618f3a0..1c91cdfe4 100644 --- a/build/source/engine/updateVars4JacDAE.f90 +++ b/build/source/engine/updateVars4JacDAE.f90 @@ -89,9 +89,9 @@ module updateVars4JacDAE_module USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists -USE soil_utils_fida_module,only:liquidHeadSundials ! compute the liquid water matric potential -USE soil_utils_fida_module,only:d2Theta_dPsi2 -USE soil_utils_fida_module,only:d2Theta_dTk2 +USE soil_utilsSundials_module,only:liquidHeadSundials ! compute the liquid water matric potential +USE soil_utilsSundials_module,only:d2Theta_dPsi2 +USE soil_utilsSundials_module,only:d2Theta_dTk2 ! IEEE checks USE, intrinsic :: ieee_arithmetic ! check values (NaN, etc.) diff --git a/build/source/engine/updateVarsSundials.f90 b/build/source/engine/updateVarsSundials.f90 index 7dbeecb59..a7d02710e 100644 --- a/build/source/engine/updateVarsSundials.f90 +++ b/build/source/engine/updateVarsSundials.f90 @@ -89,7 +89,7 @@ module updateVarsSundials_module USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists -USE soil_utils_fida_module,only:liquidHeadSundials ! compute the liquid water matric potential +USE soil_utilsSundials_module,only:liquidHeadSundials ! compute the liquid water matric potential ! IEEE checks USE, intrinsic :: ieee_arithmetic ! check values (NaN, etc.) From 590d3df0a258c3b0b6c583bb97b87de364038490 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 30 Jun 2021 12:40:41 -0600 Subject: [PATCH 0173/1472] eval8DAE edited --- build/source/engine/eval8DAE.f90 | 158 +++++++++++++++---------------- 1 file changed, 76 insertions(+), 82 deletions(-) diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index 374f7338c..2b79da3af 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -89,24 +89,24 @@ module eval8DAE_module ! ********************************************************************************************************** subroutine eval8DAE(& ! input: model control - dt_cur, & - dt, & ! intent(in): time step + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): entire time step nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout) - firstSplitOper, & ! intent(in) + firstFluxCall, & ! intent(inout) flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(in) flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVec, & ! intent(in): model state vector stateVecPrime, & ! intent(in): derivative of model state vector - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in) + lookup_data, & ! intent(in) lookup data type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes mpar_data, & ! intent(in): model parameters @@ -118,33 +118,32 @@ subroutine eval8DAE(& diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow + ! input-output: dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - ! output: flux and residual vectors - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - scalarCanopyIceTrial, & - scalarCanopyIcePrev, & - scalarCanopyLiqTrial, & - scalarCanopyLiqPrev, & - scalarCanopyEnthalpyTrial,& ! intent(in): trial enthalpy of the vegetation canopy (J m-3) - scalarCanopyEnthalpyPrev,& ! intent(in): previous enthalpy of the vegetation canopy (J m-3) - mLayerTempTrial, & ! intent(inout) - mLayerTempPrev, & ! intent(in) - mLayerMatricHeadLiqTrial,& !intent(inout) - mLayerMatricHeadTrial, & !intent(inout) - mLayerMatricHeadPrev, & !intent(in) - mLayerVolFracWatTrial, & !intent(inout) - mLayerVolFracWatPrev, & !intent(inout) - mLayerVolFracIceTrial, & !intent(inout) - mLayerVolFracIcePrev, & !intent(in) - mLayerVolFracLiqTrial, & !intent(inout) - mLayerVolFracLiqPrev, & !intent(in) - scalarAquiferStorageTrial, & - scalarAquiferStoragePrev, & - mLayerEnthalpyPrev, & ! intent(in) - mLayerEnthalpyTrial, & ! intent(out) - ixSaturation, & ! intent(inout) + scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) + scalarCanopyTempPrev, & ! intent(in): value of canopy temperature (K) + scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) + scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpyPrev,& ! intent(in): value for enthalpy of the vegetation canopy (J m-3) + mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + mLayerTempPrev, & ! intent(in): vector of layer temperature (K) + mLayerMatricHeadLiqTrial,& !intent(out) trial value for liquid water matric potential (m) + mLayerMatricHeadTrial, & !intent(out) trial value for total water matric potential (m) + mLayerMatricHeadPrev, & !intent(in) value for total water matric potential (m) + mLayerVolFracWatTrial, & !intent(out) trial vector of volumetric total water content (-) + mLayerVolFracWatPrev, & !intent(in) vector of volumetric total water content (-) + mLayerVolFracIceTrial, & !intent(out) trial vector of volumetric ice water content (-) + mLayerVolFracIcePrev, & !intent(in) vector of volumetric ice water content (-) + mLayerVolFracLiqTrial, & !intent(out) trial vector of volumetric liquid water content (-) + mLayerVolFracLiqPrev, & !intent(in) vector of volumetric liquid water content (-) + scalarAquiferStorageTrial, & ! intent(out): trial value of storage of water in the aquifer (m) + scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) + mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) + mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) + ixSaturation, & ! intent(inout): index of the lowest saturated layer feasible, & ! intent(out): flag to denote the feasibility of the solution fluxVec, & ! intent(out): flux vector resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation @@ -170,8 +169,8 @@ subroutine eval8DAE(& ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control - real(rkind),intent(in) :: dt_cur - real(rkind),intent(in) :: dt ! time step + real(rkind),intent(in) :: dt_cur + real(rkind),intent(in) :: dt ! time step integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers @@ -182,8 +181,8 @@ subroutine eval8DAE(& logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors - real(rkind),intent(in) :: stateVec(:) ! model state vector - real(rkind),intent(in) :: stateVecPrime(:) ! model state vector + real(rkind),intent(in) :: stateVec(:) ! model state vector + real(rkind),intent(in) :: stateVecPrime(:) ! model state vector real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) ! input: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions @@ -200,35 +199,35 @@ subroutine eval8DAE(& type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow - real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors - real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind),intent(in) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) - real(rkind),intent(out) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: scalarCanopyIcePrev ! previous value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(out) :: scalarCanopyLiqTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: scalarCanopyLiqPrev ! previous value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(out) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(in) :: scalarCanopyEnthalpyPrev ! previous value of enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(out) :: mLayerTempTrial(:) - real(rkind),intent(in) :: mLayerTempPrev(:) - real(rkind),intent(out) :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) - real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial value for liquid water matric potential (m) - real(rkind),intent(in) :: mLayerMatricHeadPrev(:) - real(rkind),intent(out) :: mLayerVolFracWatTrial(:) - real(rkind),intent(in) :: mLayerVolFracWatPrev(:) - real(rkind),intent(out) :: mLayerVolFracIceTrial(:) - real(rkind),intent(in) :: mLayerVolFracIcePrev(:) - real(rkind),intent(out) :: mLayerVolFracLiqTrial(:) - real(rkind),intent(in) :: mLayerVolFracLiqPrev(:) - real(rkind),intent(out) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) - real(rkind),intent(in) :: scalarAquiferStoragePrev ! previous value of storage of water in the aquifer (m) - real(rkind),intent(in) :: mLayerEnthalpyPrev(:) - real(rkind),intent(out) :: mLayerEnthalpyTrial(:) + real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind),intent(in) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) + real(rkind),intent(out) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: scalarCanopyIcePrev ! previous value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(out) :: scalarCanopyLiqTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: scalarCanopyLiqPrev ! previous value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(out) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(in) :: scalarCanopyEnthalpyPrev ! previous value of enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(in) :: mLayerTempPrev(:) + real(rkind),intent(out) :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) + real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial value for total water matric potential (m) + real(rkind),intent(in) :: mLayerMatricHeadPrev(:) ! value for total water matric potential (m) + real(rkind),intent(out) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(rkind),intent(in) :: mLayerVolFracWatPrev(:) ! vector of volumetric total water content (-) + real(rkind),intent(out) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric ice water content (-) + real(rkind),intent(in) :: mLayerVolFracIcePrev(:) ! vector of volumetric ice water content (-) + real(rkind),intent(out) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(in) :: mLayerVolFracLiqPrev(:) ! vector of volumetric liquid water content (-) + real(rkind),intent(out) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + real(rkind),intent(in) :: scalarAquiferStoragePrev ! value of storage of water in the aquifer (m) + real(rkind),intent(in) :: mLayerEnthalpyPrev(:) ! vector of enthalpy for snow+soil layers (J m-3) + real(rkind),intent(out) :: mLayerEnthalpyTrial(:) ! trial vector of enthalpy for snow+soil layers (J m-3) integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution - real(rkind),intent(out) :: fluxVec(:) ! flux vector - real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation + real(rkind),intent(out) :: fluxVec(:) ! flux vector + real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation real(qp),intent(out) :: resVec(:) ! NOTE: qp ! residual vector ! output: error control integer(i4b),intent(out) :: err ! error code @@ -257,19 +256,18 @@ subroutine eval8DAE(& real(rkind) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) real(rkind),dimension(nLayers) :: mLayerEnthalpyPrime ! enthalpy of each snow+soil layer (J m-3) ! other local variables - integer(i4b) :: iLayer ! index of model layer in the snow+soil domain - integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain - integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine - integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) + integer(i4b) :: iLayer ! index of model layer in the snow+soil domain + integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain + integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine + integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) real(rkind) :: xMin,xMax ! minimum and maximum values for water content - real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) - character(LEN=256) :: cmessage ! error message of downwind routine - real(qp) :: scalarCanopyEnthalpyPrime - real(rkind) :: scalarCanopyCmTrial - real(rkind),dimension(nLayers) :: mLayerCmTrial - logical(lgt),parameter :: updateCp=.true. - logical(lgt),parameter :: enthalpyFD=.false. - logical(lgt),parameter :: needCm=.false. + real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) + character(LEN=256) :: cmessage ! error message of downwind routine + real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy + real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil + logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step + logical(lgt),parameter :: enthalpyFD=.false. ! flag to indicate if we compute Cp using dH_T/dT + logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains @@ -388,17 +386,14 @@ subroutine eval8DAE(& endif ! initialize to state variable from the last update - !scalarCanairTempTrial = scalarCanairTempPrev - scalarCanopyTempTrial = scalarCanopyTempPrev - !scalarCanopyWatTrial = scalarCanopyWatPrev - scalarCanopyLiqTrial = scalarCanopyLiqPrev - scalarCanopyIceTrial = scalarCanopyIcePrev + scalarCanopyTempTrial = scalarCanopyTempPrev + scalarCanopyLiqTrial = scalarCanopyLiqPrev + scalarCanopyIceTrial = scalarCanopyIcePrev mLayerTempTrial = mLayerTempPrev mLayerVolFracWatTrial = mLayerVolFracWatPrev mLayerVolFracLiqTrial = mLayerVolFracLiqPrev mLayerVolFracIceTrial = mLayerVolFracIcePrev - mLayerMatricHeadTrial = mLayerMatricHeadPrev ! total water matric potential - !mLayerMatricHeadLiqTrial = mLayerMatricHeadLiqPrev ! liquid water matric potential + mLayerMatricHeadTrial = mLayerMatricHeadPrev scalarAquiferStorageTrial = scalarAquiferStoragePrev ! extract variables from the model state vector @@ -636,7 +631,6 @@ subroutine eval8DAE(& end if ! needCm -! print *, 'firstFluxCall = ', firstFluxCall ! save the number of flux calls per time step indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) + 1 ! compute the fluxes for a given state vector From 545cd9abf2ed362c0cf5c5eadb47d9c47d7aad6a Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 30 Jun 2021 13:20:56 -0600 Subject: [PATCH 0174/1472] more edit on solvByIDA --- build/source/engine/eval8DAE.f90 | 42 ++--- build/source/engine/solveByIDA.f90 | 251 +++++++++++++---------------- 2 files changed, 135 insertions(+), 158 deletions(-) diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index 2b79da3af..dc8f1bb9f 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -97,7 +97,7 @@ subroutine eval8DAE(& nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(inout) flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(in) flag to indicate if we are processing the first flux call in a splitting operation + firstSplitOper, & ! intent(inout) flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors @@ -106,7 +106,7 @@ subroutine eval8DAE(& sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in) lookup data + lookup_data, & ! intent(in): lookup data type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes mpar_data, & ! intent(in): model parameters @@ -130,25 +130,25 @@ subroutine eval8DAE(& scalarCanopyEnthalpyPrev,& ! intent(in): value for enthalpy of the vegetation canopy (J m-3) mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) mLayerTempPrev, & ! intent(in): vector of layer temperature (K) - mLayerMatricHeadLiqTrial,& !intent(out) trial value for liquid water matric potential (m) - mLayerMatricHeadTrial, & !intent(out) trial value for total water matric potential (m) - mLayerMatricHeadPrev, & !intent(in) value for total water matric potential (m) - mLayerVolFracWatTrial, & !intent(out) trial vector of volumetric total water content (-) - mLayerVolFracWatPrev, & !intent(in) vector of volumetric total water content (-) - mLayerVolFracIceTrial, & !intent(out) trial vector of volumetric ice water content (-) - mLayerVolFracIcePrev, & !intent(in) vector of volumetric ice water content (-) - mLayerVolFracLiqTrial, & !intent(out) trial vector of volumetric liquid water content (-) - mLayerVolFracLiqPrev, & !intent(in) vector of volumetric liquid water content (-) - scalarAquiferStorageTrial, & ! intent(out): trial value of storage of water in the aquifer (m) - scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) - mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) - mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) - ixSaturation, & ! intent(inout): index of the lowest saturated layer - feasible, & ! intent(out): flag to denote the feasibility of the solution - fluxVec, & ! intent(out): flux vector - resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - resVec, & ! intent(out): residual vector - err,message) ! intent(out): error control + mLayerMatricHeadLiqTrial,& ! intent(out): trial value for liquid water matric potential (m) + mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) + mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) + mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) + mLayerVolFracWatPrev, & ! intent(in): vector of volumetric total water content (-) + mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) + mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) + mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) + mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) + scalarAquiferStorageTrial, & ! intent(out): trial value of storage of water in the aquifer (m) + scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) + mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) + mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) + ixSaturation, & ! intent(inout): index of the lowest saturated layer + feasible, & ! intent(out): flag to denote the feasibility of the solution + fluxVec, & ! intent(out): flux vector + resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + resVec, & ! intent(out): residual vector + err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- ! provide access to subroutines USE varExtrSundials_module, only:varExtract ! extract variables from the state vector diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index d890307a7..54717e30b 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -124,12 +124,12 @@ subroutine solveByIDA( & flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output - ixSaturation, & ! intent(out) - idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step + ixSaturation, & ! intent(out) + idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix dt_last, & ! intent(out): last stepsize - dt_past, & ! intent(out): one stepsize before the last one - dt_out, & ! intent(out) + dt_past, & ! intent(out): one stepsize before the last one + dt_out, & ! intent(out): time step stateVec, & ! intent(out): model state vector stateVecPrime, & ! intent(out): derivative of model state vector err,message & ! intent(out): error control @@ -166,9 +166,9 @@ subroutine solveByIDA( & ! calling variables ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control - real(qp),intent(in) :: dt - real(qp),intent(inout) :: atol(:) - real(qp),intent(inout) :: rtol(:) + real(qp),intent(in) :: dt ! data time step + real(qp),intent(inout) :: atol(:) ! vector of absolute tolerances + real(qp),intent(inout) :: rtol(:) ! vector of relative tolerances integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers @@ -179,9 +179,9 @@ subroutine solveByIDA( & logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors - real(rkind),intent(in) :: stateVecInit(:) ! model state vector - real(qp),intent(in) :: sMul(:) ! state vector multiplier (used in the residual calculations) - real(rkind), intent(inout) :: dMat(:) + real(rkind),intent(in) :: stateVecInit(:) ! model state vector + real(qp),intent(in) :: sMul(:) ! state vector multiplier (used in the residual calculations) + real(rkind), intent(inout) :: dMat(:) ! input: data structures type(zLookup),intent(in) :: lookup_data ! lookup tables type(var_i), intent(in) :: type_data ! type of vegetation and soil @@ -193,19 +193,19 @@ subroutine solveByIDA( & type(var_ilength), intent(in) :: indx_data ! indices defining model states and layers ! input-output: data structures type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_temp ! model fluxes for a local HRU - type(var_dlength),intent(inout):: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: flux_sum + type(var_dlength),intent(inout) :: flux_temp ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: flux_sum ! sum of fluxes type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(rkind),intent(inout) :: mLayerCmpress_sum(:) + real(rkind),intent(inout) :: mLayerCmpress_sum(:) ! sum of soil compress ! output: state vectors - integer(i4b),intent(out) :: ixSaturation - real(rkind),intent(inout) :: stateVec(:) ! model state vector (y) - real(rkind),intent(inout) :: stateVecPrime(:) ! model state vector (y') - logical(lgt),intent(out) :: idaSucceeds - real(qp),intent(out) :: dt_last(1) - real(qp),intent(out) :: dt_past - real(qp),intent(out) :: dt_out + integer(i4b),intent(out) :: ixSaturation ! index of the lowest saturated layer + real(rkind),intent(inout) :: stateVec(:) ! model state vector (y) + real(rkind),intent(inout) :: stateVecPrime(:) ! model state vector (y') + logical(lgt),intent(out) :: idaSucceeds ! flag to indicate if IDA is successful + real(qp),intent(out) :: dt_last(1) ! last time step + real(qp),intent(out) :: dt_past ! time step before tha last one + real(qp),intent(out) :: dt_out ! time step ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -213,45 +213,43 @@ subroutine solveByIDA( & ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- - type(N_Vector), pointer :: sunvec_y ! sundials solution vector - type(N_Vector), pointer :: sunvec_yp ! sundials derivative vector - type(N_Vector), pointer :: sunvec_av ! sundials tolerance vector - type(SUNMatrix), pointer :: sunmat_A ! sundials matrix - type(SUNLinearSolver), pointer :: sunlinsol_LS ! sundials linear solver - type(SUNNonLinearSolver), pointer :: sunnonlin_NLS ! sundials nonlinear solver - type(c_ptr) :: ida_mem ! IDA memory - type(eqnsData), target :: eqns_data - integer(i4b) :: retval - logical(lgt) :: feasible ! feasibility flag - real(qp) :: t0 - integer (kind = 8) :: maxstep - integer(kind = 8) :: mu, lu - real(qp) :: h_max - real(qp) :: coef_nonlin - integer(i4b),parameter :: ixRectangular=1 - integer(i4b),parameter :: ixTrapezoidal=2 - integer(i4b) :: iVar - logical(lgt) :: startQuadrature - real(rkind) :: mLayerMatricHeadLiqPrev(nSoil) + type(N_Vector), pointer :: sunvec_y ! sundials solution vector + type(N_Vector), pointer :: sunvec_yp ! sundials derivative vector + type(N_Vector), pointer :: sunvec_av ! sundials tolerance vector + type(SUNMatrix), pointer :: sunmat_A ! sundials matrix + type(SUNLinearSolver), pointer :: sunlinsol_LS ! sundials linear solver + type(SUNNonLinearSolver), pointer :: sunnonlin_NLS ! sundials nonlinear solver + type(c_ptr) :: ida_mem ! IDA memory + type(eqnsData), target :: eqns_data ! IDA type + integer(i4b) :: retval ! return value + logical(lgt) :: feasible ! feasibility flag + real(qp) :: t0 ! staring time + integer(kind = 8) :: mu, lu + real(qp) :: h_max + real(qp) :: coef_nonlin + integer(i4b),parameter :: ixRectangular=1 + integer(i4b),parameter :: ixTrapezoidal=2 + integer(i4b) :: iVar + logical(lgt) :: startQuadrature + real(rkind) :: mLayerMatricHeadLiqPrev(nSoil) real(qp) :: h_init integer(c_long) :: nState ! total number of state variables - real(rkind) :: rVec(nStat) + real(rkind) :: rVec(nStat) real(qp) :: tret(1) - real(rkind) :: bulkDensity ! bulk density of a given layer (kg m-3) - real(rkind) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) - logical(lgt) :: tooMuchMelt - logical(lgt) :: divideLayer - logical(lgt) :: mergedLayers - logical(lgt),parameter :: checkSnow = .true. - real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) - real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) - real(rkind) :: mLayerDepth(nLayers) - real(rkind) :: scalarSnowDepth - real(rkind) :: scalarSWE - real(rkind) :: mLayerMeltFreeze(nLayers) - - !======= Internals ============ - + real(rkind) :: bulkDensity ! bulk density of a given layer (kg m-3) + real(rkind) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) + logical(lgt) :: tooMuchMelt + logical(lgt) :: divideLayer + logical(lgt) :: mergedLayers + logical(lgt),parameter :: checkSnow = .true. + real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) + real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) + real(rkind) :: mLayerDepth(nLayers) + real(rkind) :: scalarSnowDepth + real(rkind) :: scalarSWE + real(rkind) :: mLayerMeltFreeze(nLayers) + ! ----------------------------------------------------------------------------------------------------- + ! initialize error control err=0; message="solveByIDA/" nState = nStat @@ -307,12 +305,11 @@ subroutine solveByIDA( & eqns_data%bvar_data = bvar_data eqns_data%indx_data = indx_data - ! allocate space for the baseflow derivatives - ! NOTE: needs allocation because only used when baseflow sinks are active + ! allocate space if(model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel)then - allocate(eqns_data%dBaseflow_dMatric(nSoil,nSoil),stat=err) ! baseflow depends on total storage in the soil column + allocate(eqns_data%dBaseflow_dMatric(nSoil,nSoil),stat=err) else - allocate(eqns_data%dBaseflow_dMatric(0,0),stat=err) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix + allocate(eqns_data%dBaseflow_dMatric(0,0),stat=err) end if allocate( eqns_data%mLayerMatricHeadLiqTrial(nSoil) ) @@ -424,22 +421,22 @@ subroutine solveByIDA( & if (retval /= 0) then; err=20; message='solveByIDA: error in setSolverParams'; return; endif ! need the following values for the first substep - eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) + eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) eqns_data%scalarCanopyIcePrev = prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) eqns_data%scalarCanopyLiqPrev = prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) - eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) + eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) eqns_data%mLayerVolFracLiqPrev(:) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(:) eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) eqns_data%scalarAquiferStoragePrev = prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) - eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) + eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) - mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) - eqns_data%ixSaturation = ixSaturation - mLayerDepth = prog_data%var(iLookPROG%mLayerDepth)%dat - scalarSnowDepth = prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) - scalarSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) + mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) + eqns_data%ixSaturation = ixSaturation + mLayerDepth = prog_data%var(iLookPROG%mLayerDepth)%dat + scalarSnowDepth = prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) + scalarSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) !********************************************************************************** !****************************** Main Solver *************************************** @@ -470,17 +467,17 @@ subroutine solveByIDA( & eqns_data%nLayers, & ! intent(in): number of layers eqns_data%nState, & ! intent(in): number of state variables in the current subset eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - eqns_data%firstFluxCall, & ! intent(inout) - eqns_data%firstSplitOper, & ! intent(in) + eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVec, & ! intent(in): model state vector stateVecPrime, & ! intent(in): model state vector - eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures model_decisions, & ! intent(in): model decisions - eqns_data%lookup_data, & + eqns_data%lookup_data, & ! intent(in): lookup data eqns_data%type_data, & ! intent(in): type of vegetation and soil eqns_data%attr_data, & ! intent(in): spatial attributes eqns_data%mpar_data, & ! intent(in): model parameters @@ -492,38 +489,38 @@ subroutine solveByIDA( & eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian - eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - eqns_data%scalarCanopyIceTrial, & - eqns_data%scalarCanopyIcePrev, & - eqns_data%scalarCanopyLiqTrial, & - eqns_data%scalarCanopyLiqPrev, & - eqns_data%scalarCanopyEnthalpyTrial,& ! intent(in): trial enthalpy of the vegetation canopy (J m-3) - eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) - eqns_data%mLayerTempTrial, & - eqns_data%mLayerTempPrev, & - eqns_data%mLayerMatricHeadLiqTrial, & - eqns_data%mLayerMatricHeadTrial, & - eqns_data%mLayerMatricHeadPrev, & - eqns_data%mLayerVolFracWatTrial, & - eqns_data%mLayerVolFracWatPrev, & - eqns_data%mLayerVolFracIceTrial, & - eqns_data%mLayerVolFracIcePrev, & - eqns_data%mLayerVolFracLiqTrial, & - eqns_data%mLayerVolFracLiqPrev, & - eqns_data%scalarAquiferStorageTrial, & - eqns_data%scalarAquiferStoragePrev, & - eqns_data%mLayerEnthalpyPrev, & ! intent(in) - eqns_data%mLayerEnthalpyTrial, & ! intent(out) - eqns_data%ixSaturation, & + ! input-output + eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian + eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) + eqns_data%scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) + eqns_data%scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + eqns_data%scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) + eqns_data%scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) + eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): value for enthalpy of the vegetation canopy (J m-3) + eqns_data%mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + eqns_data%mLayerTempPrev, & ! intent(in): vector of layer temperature (K) + eqns_data%mLayerMatricHeadLiqTrial, & ! intent(out): trial value for liquid water matric potential (m) + eqns_data%mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) + eqns_data%mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) + eqns_data%mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) + eqns_data%mLayerVolFracWatPrev, & ! intent(in): vector of volumetric total water content (-) + eqns_data%mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) + eqns_data%mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) + eqns_data%mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) + eqns_data%mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) + eqns_data%scalarAquiferStorageTrial, & ! intent(out): trial value of storage of water in the aquifer (m) + eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) + eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) + eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) + eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer ! output feasible, & ! intent(out): flag to denote the feasibility of the solution eqns_data%fluxVec, & ! intent(out): flux vector eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation rVec, & ! intent(out): residual vector - eqns_data%err,eqns_data%message) ! intent(out): error control + eqns_data%err,eqns_data%message) ! intent(out): error control select case(ixQuadrature) @@ -572,9 +569,7 @@ subroutine solveByIDA( & eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial - - - ! if(tret(1) > 500) exit + if(checkSnow)then mLayerDepth(:) = prog_data%var(iLookPROG%mLayerDepth)%dat(:) @@ -618,10 +613,7 @@ subroutine solveByIDA( & endif - if(tooMuchMelt)then -! print *, 'tooMuchMelt, tret = ', tret(1) - exit - endif + if(tooMuchMelt) exit divideLayer = .false. call needDivideLayer(& @@ -635,12 +627,6 @@ subroutine solveByIDA( & divideLayer, & ! intent(out): flag to denote that a layer was divided err,message) ! intent(out): error control if(divideLayer .and. tret(1)>50) then -! print *, 'divideLayer, tret = ', tret(1) -! print *, 'mLayerMeltFreeze = ', mLayerMeltFreeze(1:nSnow) -! print *, 'mLayerVolFracLiq = ', eqns_data%mLayerVolFracLiqTrial(:) -! print *, 'mLayerVolFracIce = ', eqns_data%mLayerVolFracIceTrial(:) -! print *, 'mLayerTemp = ', eqns_data%mLayerTempTrial(:) -! print *, 'mLayerDepth = ', mLayerDepth(:) exit endif @@ -650,22 +636,14 @@ subroutine solveByIDA( & ! input/output: model data structures tooMuchMelt, & ! intent(in): flag to force merge of snow layers model_decisions, & ! intent(in): model decisions - eqns_data%mpar_data, & ! intent(in): model parameters - eqns_data%nSnow, & ! intent(in): - mLayerDepth, & ! intent(inout): model prognostic variables for a local HRU + eqns_data%mpar_data, & ! intent(in): model parameters + eqns_data%nSnow, & ! intent(in): + mLayerDepth, & ! intent(inout): model prognostic variables for a local HRU ! output mergedLayers, & ! intent(out): flag to denote that layers were merged err,message) ! intent(out): error control - if(mergedLayers .and. tret(1)>50) then -! print *, 'mergedLayers, tret = ', tret(1) -! print *, 'mLayerMeltFreeze = ', mLayerMeltFreeze(1:nSnow) -! print *, 'mLayerVolFracLiq = ', eqns_data%mLayerVolFracLiqTrial(:) -! print *, 'mLayerVolFracIce = ', eqns_data%mLayerVolFracIceTrial(:) -! print *, 'mLayerTemp = ', eqns_data%mLayerTempTrial(:) -! print *, 'mLayerDepth = ', mLayerDepth(:) - exit - endif + if(mergedLayers .and. tret(1)>50) exit endif ! checkSnow @@ -680,13 +658,12 @@ subroutine solveByIDA( & if( .not. feasible) idaSucceeds = .false. if(idaSucceeds)then - ! copy to output data diag_data = eqns_data%diag_data flux_data = eqns_data%flux_data deriv_data = eqns_data%deriv_data - ixSaturation = eqns_data%ixSaturation - dt_out = tret(1) + ixSaturation = eqns_data%ixSaturation + dt_out = tret(1) endif @@ -766,18 +743,18 @@ subroutine setSolverParams(dt,ida_mem,retval) USE fida_mod ! Fortran interface to IDA implicit none - real(rkind),intent(in) :: dt ! time step - type(c_ptr),intent(inout) :: ida_mem ! IDA memory + real(rkind),intent(in) :: dt ! time step + type(c_ptr),intent(inout) :: ida_mem ! IDA memory integer(i4b),intent(out) :: retval ! return value - real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 - integer,parameter :: max_order = 1 ! maximum BDF order, default = 5 - integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 - integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 - integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 - integer(kind = 8),parameter :: max_step = 999999 ! maximum number of steps, dafault = 500 + real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 + integer,parameter :: max_order = 1 ! maximum BDF order, default = 5 + integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 + integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 + integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 + integer(kind = 8),parameter :: max_step = 999999 ! maximum number of steps, dafault = 500 real(qp),parameter :: h_init = 0 ! initial stepsize - real(qp) :: h_max ! maximum stepsize, dafault = infinity + real(qp) :: h_max ! maximum stepsize, dafault = infinity ! Set the maximum BDF order retval = FIDASetMaxOrd(ida_mem, max_order) From 3a3c5f78ecaf7f7ad9bd44886eff6ebf9dc8ed76 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 30 Jun 2021 13:28:25 -0600 Subject: [PATCH 0175/1472] more edit on solvByIDA --- build/source/engine/solveByIDA.f90 | 74 +++++++++++++++--------------- 1 file changed, 36 insertions(+), 38 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 54717e30b..1049af250 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -136,28 +136,28 @@ subroutine solveByIDA( & ) !======= Inclusions =========== - USE fida_mod ! Fortran interface to IDA - USE fnvector_serial_mod ! Fortran interface to serial N_Vector - USE fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix - USE fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver - USE fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix - USE fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver - USE fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver - USE fsundials_matrix_mod ! Fortran interface to generic SUNMatrix - USE fsundials_nvector_mod ! Fortran interface to generic N_Vector - USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver - USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver - USE allocspace_module,only:allocLocal ! allocate local data structures - USE evalDAE4IDA_module,only:evalDAE4IDA ! DAE/ODE functions - USE evalJac4IDA_module,only:evalJac4IDA ! system Jacobian - USE tol4IDA_module,only:computWeight4IDA - USE eval8DAE_module,only:eval8DAE - USE computEnthalpy_module,only:computEnthalpy - USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy - USE computSnowDepth_module,only:computSnowDepth - USE var_derive_module,only:calcHeight ! module to calculate height at layer interfaces and layer mid-point - USE layerDivide_module,only:needDivideLayer - Use layerMerge_module,only:needMergeLayers + USE fida_mod ! Fortran interface to IDA + USE fnvector_serial_mod ! Fortran interface to serial N_Vector + USE fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix + USE fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver + USE fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix + USE fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver + USE fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver + USE fsundials_matrix_mod ! Fortran interface to generic SUNMatrix + USE fsundials_nvector_mod ! Fortran interface to generic N_Vector + USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver + USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver + USE allocspace_module,only:allocLocal ! allocate local data structures + USE evalDAE4IDA_module,only:evalDAE4IDA ! DAE/ODE functions + USE evalJac4IDA_module,only:evalJac4IDA ! system Jacobian + USE tol4IDA_module,only:computWeight4IDA ! weigth required for tolerances + USE eval8DAE_module,only:eval8DAE ! residual of DAE + USE computEnthalpy_module,only:computEnthalpy ! enthalpy + USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy + USE computSnowDepth_module,only:computSnowDepth ! snow depth + USE var_derive_module,only:calcHeight ! height at layer interfaces and layer mid-point + USE layerDivide_module,only:needDivideLayer ! do we need to divide snow layers + Use layerMerge_module,only:needMergeLayers ! do we need to merge snow layers !======= Declarations ========= implicit none @@ -224,26 +224,24 @@ subroutine solveByIDA( & integer(i4b) :: retval ! return value logical(lgt) :: feasible ! feasibility flag real(qp) :: t0 ! staring time - integer(kind = 8) :: mu, lu - real(qp) :: h_max - real(qp) :: coef_nonlin - integer(i4b),parameter :: ixRectangular=1 + integer(kind = 8) :: mu, lu ! in banded matrix mode + integer(i4b),parameter :: ixRectangular=1 integer(i4b),parameter :: ixTrapezoidal=2 integer(i4b) :: iVar logical(lgt) :: startQuadrature real(rkind) :: mLayerMatricHeadLiqPrev(nSoil) real(qp) :: h_init - integer(c_long) :: nState ! total number of state variables + integer(c_long) :: nState ! total number of state variables real(rkind) :: rVec(nStat) real(qp) :: tret(1) - real(rkind) :: bulkDensity ! bulk density of a given layer (kg m-3) - real(rkind) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) + real(rkind) :: bulkDensity ! bulk density of a given layer (kg m-3) + real(rkind) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) logical(lgt) :: tooMuchMelt logical(lgt) :: divideLayer logical(lgt) :: mergedLayers logical(lgt),parameter :: checkSnow = .true. - real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) - real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) + real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) + real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) real(rkind) :: mLayerDepth(nLayers) real(rkind) :: scalarSnowDepth real(rkind) :: scalarSWE @@ -740,21 +738,21 @@ end subroutine setInitialCondition subroutine setSolverParams(dt,ida_mem,retval) !======= Inclusions =========== USE, intrinsic :: iso_c_binding - USE fida_mod ! Fortran interface to IDA + USE fida_mod ! Fortran interface to IDA implicit none - real(rkind),intent(in) :: dt ! time step - type(c_ptr),intent(inout) :: ida_mem ! IDA memory - integer(i4b),intent(out) :: retval ! return value + real(rkind),intent(in) :: dt ! time step + type(c_ptr),intent(inout) :: ida_mem ! IDA memory + integer(i4b),intent(out) :: retval ! return value real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 - integer,parameter :: max_order = 1 ! maximum BDF order, default = 5 + integer,parameter :: max_order = 1 ! maximum BDF order, default = 5 integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 integer(kind = 8),parameter :: max_step = 999999 ! maximum number of steps, dafault = 500 - real(qp),parameter :: h_init = 0 ! initial stepsize - real(qp) :: h_max ! maximum stepsize, dafault = infinity + real(qp),parameter :: h_init = 0 ! initial stepsize + real(qp) :: h_max ! maximum stepsize, dafault = infinity ! Set the maximum BDF order retval = FIDASetMaxOrd(ida_mem, max_order) From d7fb2a4468442275af474aec3577791d8855432e Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 30 Jun 2021 13:34:17 -0600 Subject: [PATCH 0176/1472] minor edit --- build/source/engine/eval8DAE.f90 | 3 - build/source/engine/systemSolvSundials.f90 | 64 +++++++++++----------- 2 files changed, 32 insertions(+), 35 deletions(-) diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index dc8f1bb9f..484909824 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -337,9 +337,6 @@ subroutine eval8DAE(& if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. endif -! print *, 'Tfreeze = ', Tfreeze -! print *, 'stateVec = ', stateVec(1:nLayers) -! print *, '===========================================================' ! loop through non-missing hydrology state variables in the snow+soil domain do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index a43b2ca0b..452ca1d75 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -134,8 +134,8 @@ subroutine systemSolvSundials(& USE summaSolve_module,only:summaSolve ! calculate the iteration increment, evaluate the new state, and refine if necessary USE getVectorz_module,only:getScaling ! get the scaling vectors USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy - USE tol4IDA_module,only:popTol4IDA - USE solveByIDA_module,only:solveByIDA + USE tol4IDA_module,only:popTol4IDA ! pop tolerances + USE solveByIDA_module,only:solveByIDA ! solve DAE by IDA ! use varExtrSundials_module, only:countDiscontinuity use, intrinsic :: iso_c_binding implicit none @@ -143,7 +143,7 @@ subroutine systemSolvSundials(& ! * dummy variables ! --------------------------------------------------------------------------------------- ! input: model control - real(rkind),intent(in) :: dt ! time step (seconds) + real(rkind),intent(in) :: dt ! time step (seconds) integer(i4b),intent(in) :: nState ! total number of state variables logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call @@ -162,15 +162,15 @@ subroutine systemSolvSundials(& type(var_dlength),intent(inout) :: flux_temp ! model fluxes for a local HRU type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin type(model_options),intent(in) :: model_decisions(:) ! model decisions - real(rkind),intent(in) :: stateVecInit(:) ! initial state vector (mixed units) + real(rkind),intent(in) :: stateVecInit(:) ! initial state vector (mixed units) ! output: model control type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: stateVecTrial(:) ! trial state vector (mixed units) - real(rkind),intent(out) :: stateVecPrime(:) ! trial state vector (mixed units) + real(rkind),intent(out) :: stateVecTrial(:) ! trial state vector (mixed units) + real(rkind),intent(out) :: stateVecPrime(:) ! trial state vector (mixed units) logical(lgt),intent(out) :: reduceCoupledStep ! flag to reduce the length of the coupled step logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that there was too much melt - real(qp),intent(out) :: dt_out + real(qp),intent(out) :: dt_out ! time step integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! ********************************************************************************************************************************************************* @@ -181,11 +181,11 @@ subroutine systemSolvSundials(& character(LEN=256) :: cmessage ! error message of downwind routine integer(i4b) :: iVar ! index of variable integer(i4b) :: local_ixGroundwater ! local index for groundwater representation - real(rkind) :: bulkDensity ! bulk density of a given layer (kg m-3) - real(rkind) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) - real(rkind),parameter :: tempAccelerate=0.00_rkind ! factor to force initial canopy temperatures to be close to air temperature - real(rkind),parameter :: xMinCanopyWater=0.0001_rkind ! minimum value to initialize canopy water (kg m-2) - real(rkind),parameter :: tinyStep=0.000001_rkind ! stupidly small time step (s) + real(rkind) :: bulkDensity ! bulk density of a given layer (kg m-3) + real(rkind) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) + real(rkind),parameter :: tempAccelerate=0.00_rkind ! factor to force initial canopy temperatures to be close to air temperature + real(rkind),parameter :: xMinCanopyWater=0.0001_rkind ! minimum value to initialize canopy water (kg m-2) + real(rkind),parameter :: tinyStep=0.000001_rkind ! stupidly small time step (s) integer(i4b),parameter :: ixRectangular=1 integer(i4b),parameter :: ixTrapezoidal=2 @@ -196,25 +196,25 @@ subroutine systemSolvSundials(& integer(i4b) :: ixQuadrature=ixRectangular ! type of quadrature method to approximate average fluxes integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) type(var_dlength) :: flux_init ! model fluxes at the start of the time step - real(rkind),allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! NOTE: allocatable, since not always needed - real(rkind) :: stateVecNew(nState) ! new state vector (mixed units) - real(rkind) :: fluxVec0(nState) ! flux vector (mixed units) - real(rkind) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) - real(rkind) :: xScale(nState) ! characteristic scale of the state vector (mixed units) - real(rkind) :: dMat(nState) ! diagonal matrix (excludes flux derivatives) + real(rkind),allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! NOTE: allocatable, since not always needed + real(rkind) :: stateVecNew(nState) ! new state vector (mixed units) + real(rkind) :: fluxVec0(nState) ! flux vector (mixed units) + real(rkind) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) + real(rkind) :: xScale(nState) ! characteristic scale of the state vector (mixed units) + real(rkind) :: dMat(nState) ! diagonal matrix (excludes flux derivatives) real(qp) :: sMul(nState) ! NOTE: qp ! multiplier for state vector for the residual calculations real(qp) :: rVec(nState) ! NOTE: qp ! residual vector - real(rkind) :: rAdd(nState) ! additional terms in the residual vector - real(rkind) :: fOld ! function values (-); NOTE: dimensionless because scaled + real(rkind) :: rAdd(nState) ! additional terms in the residual vector + real(rkind) :: fOld ! function values (-); NOTE: dimensionless because scaled logical(lgt) :: feasible ! feasibility flag - real(rkind) :: dt_last(1) ! last stepsize taken by ida solver - real(qp) :: dt_past ! one step before the last stepsize taken by ida solver - real(rkind) :: atol(nState) ! absolute telerance - real(rkind) :: rtol(nState) ! relative tolerance - type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a data step - integer(i4b) :: tol_iter ! iteration index - real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix - logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step + real(rkind) :: dt_last(1) ! last stepsize taken by ida solver + real(qp) :: dt_past ! one step before the last stepsize taken by ida solver + real(rkind) :: atol(nState) ! absolute telerance + real(rkind) :: rtol(nState) ! relative tolerance + type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a data step + integer(i4b) :: tol_iter ! iteration index + real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix + logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step ! --------------------------------------------------------------------------------------- @@ -406,10 +406,10 @@ subroutine systemSolvSundials(& prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers - mpar_data, & ! intent(in): model parameters + mpar_data, & ! intent(in): model parameters ! output atol, & ! intent(out): absolute tolerances vector (mixed units) - rtol, & ! intent(out): relative tolerances vector (mixed units) + rtol, & ! intent(out): relative tolerances vector (mixed units) err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) @@ -465,11 +465,11 @@ subroutine systemSolvSundials(& deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step + idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix dt_last, & ! intent(out): last stepsize dt_past, & ! intent(out): one stepsize before the last one - dt_out, & ! intent(out) + dt_out, & ! intent(out): time step stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step err,cmessage) ! intent(out): error control From e6c56eea41f967337fce930a7d139e4d57785bef Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 30 Jun 2021 23:11:24 -0600 Subject: [PATCH 0177/1472] ida-install directory added --- build/source/engine/eval8DAE.f90 | 2 +- ida-install/ida-5.4.0.tar.gz | Bin 0 -> 3456987 bytes ida-install/readme.md | 20 ++++++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 ida-install/ida-5.4.0.tar.gz create mode 100644 ida-install/readme.md diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index 484909824..18ebf9e77 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -266,7 +266,7 @@ subroutine eval8DAE(& real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step - logical(lgt),parameter :: enthalpyFD=.false. ! flag to indicate if we compute Cp using dH_T/dT + logical(lgt),parameter :: enthalpyFD=.true. ! flag to indicate if we compute Cp using dH_T/dT logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains diff --git a/ida-install/ida-5.4.0.tar.gz b/ida-install/ida-5.4.0.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..bc40222bb03883b9cc8264ec40e58d71683e5113 GIT binary patch literal 3456987 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

b{2s|PyA&tp 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 keVGngEhHVy-6ORY^L^&~tEbNII6CVHFV-ZQSv_ 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%0Dd`~2#WvBn>TkB`+}MuM;Dj3A0HBAq48nLsU4d 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 @A)AiNX8ffmA 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�YxlZ>|}~KG+i6pany|Up+p~W*w~DpB6ea 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≈M_WO0dt#Ba+i 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 z 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 z

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 h`KZ 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⋘ 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

4m*d~W*ZxI!(7uhtPx>{2^?~t#flu0l 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�C5>rXo0|kFaXrjy|Vu#?x5tZG~wN$kcH| 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 z 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

e^kr=?khG0 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

BhryoSgf%qcGon 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�fGU35PM!JW5YEvoFnj{#fA3jp%g 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

~yN+x$ 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

3f)($M4>NqPFGY^ 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

    M8xZU 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 z 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⩔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ù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�ToDAK!bhsl#)k3 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 z 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%0AurGv@Z`{Uy_eubLa+q*` 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%0AFX{O*@;M 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

    U$#RE;(0Y`cr3DS+Qd;ADbNYIr_-70 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%0DR`$^@IsHm<-$v$#o#5+XWrnt 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

    D!EckT=e+XmW$<=qdGT3?J@tdKb| z@~j2?W3BHc5cA$_6;jcaTOFD43KqRSXm?M~J~TmbL$YGxQx?}M_Pd7?l$+E5Smwam zVgmyMT~848q9x?z<;~4mS&j00fzge@ycKi+&cM5^41>eMeI*_-?tka|7fV7XAFt01 z?9@dC1&Pv8QBi5qrKF_5To<0i>Zj~OdkCic18a3n&Di(MN_s0JBO^IR(yO?4Vgr4B z{77UDP|FDkn6~&kpOI%NZ{AWqeE2Xl6bhZ5s*)Z!eM?JSo!4z}sO&Bh4!DE7lM&m#od?VzDF0V~MzKD3y zbQQ_7U={CYt2sJ3nX}Rg2}Pk<)v^GBQNPEeX_upar;g{Cn|i;PA?&{81AZ5OKNdZ> zLb}`j2~Jc(tozHCCo6)ova()O4bpv3-)w7Z%g`!SQB|#rdY%<|jgIb={{?dLo1=IH z(E1A(l>Ej8_u)-y1o964r`9}-A<(BpgD3|oZEFkAtN~oopPGvt<|vnZYTe^)2|)jd z%K`p3Fx(LuXmq=Y&8=I{rIg*+dEn@ULsGkQFM%6RP?(yV6LMIaiH*H#_|pj8fQ-;( z+wjop9x% z@J*WrCet9-uGR-`7D5%?s9OcJYiyv?_S&f@^>y)FVKVmRLbSB5mAZ7ol~GFMhXiDx zgMp*KF$)L?1fvop;)Z#yUCK}qQ&bFc+?RusdC(j~u<;gT_C(Y?j2@J}c|&diw@%B; z<7C1mrKZwe1Ur{logiAr5-8qL3a|=RwaEAnhrY%@0_(Ajjjy*iZI_gk6xiM!9UZrE z^-WFJ$y)mQSak4d5cbooBMl7EGt<+|EG&<$tn40QX}Cf!t9xi?RyuCxXyF#t*9+0< z!RqqEz&5#It+rN-D^gmGj2Iwv0$+j?cZKWh*|P;|h&-8SR05Kj8D#{26G8<2a=ig0 zh`k@1&CYwXmd+$iTe|_Q07X+PD~10vjQX7V1*2>2?MlDnD0H4WwPWwQ&X1+_^@}Qk zCviY;emN$GuEC#*r=z1gK}4OG?*sf@Z!a4C?k1$tu~us{>TGOmsBSE)h5;wV^73+I zHY}O_(9@eOR8r9^Uq*!2-oB+|o|>He$}F#}j3IZcB%xE>U!fGl2vD{wccy#`3)fu> zV6CTAR8&s0>WzI0(bCcaoBuZMp_SFt0rFA(1Nj7Chp5j2Te}h#8d_)h;V)u4+T%}| z%Zw{>+O{<_Gt)_ym((oKiXK(HAILq!wyOK|0^lYjJrRAgwh>k-eWz6(KfZnl9CcJtkv>( zqwX4XKF=KpY%j0VF_`$oM2+;$ckiySv1NvF7`9=6E zl&^m2gPPL^VHAxa4$fO&Svhv>*n*fK6VsiWFSPF8zoHCXx}_w3>W2s6C852Ikrxg> zUV$YN`!alvnwt75?tUEx3;d1|&DE^@^pQ896WK{s_rJQL?iTTkwy%EgI&!^zq= z$cRQ(R#ubz@Xgnb3rO8V3oQs&#_QKrm_z2~=5h_2uQM}0k~rwQCno`Dw~_tNNsNbo z4OnFFWQ~)f5Mlu@!MXqMc=%^~KKLb~25Wp0(D?W)%ON3Mn#-`x2ibWA1t}dStx{RG z8s~r*{@m2`p8m;Pf3f}Qbm@ErF6jF3^OEfBY?aiGVM@vZy*3J82^E|5Ij!6t0liw- zuyIZ@r~cJZ(GkD~`&w6LB`|ti7KF4uzT)tB{PW_%f*f-Qg|EkuuvroM9Pm^Jl2Wf8 zIXykiXb2n?@mPnTpkQlj>x~;Xg1y#PSB>-07cXAiRQU*9k~~;eY~EMEW!fnx+WWZ^ z`JuYHLBx!(#=@R0T*v?0Fjiz+Mp`;32UdFSbmC@Tw%&>2+9*MXw|LA5$N^h!VtSh5 z;zh}f*8u_V%sO4wHmnkAg5RQ!IOCh1Cdw&Hzxg z=IqXUj+-hVViL}O8n$Z)W>OTxE{+Dt0wEO=5^^e1ycX%Edy)p%@_jXwO{c&Vu5d08 z0}TVqG~uM^eFgN+1%g8gSP}Y5l$2)KY6NqFBudO7Kpo!3{gLK~f6bZL?4MbZuA!mm zw{H<4szBAY+yg)8n2OJs7$nDgL$S3U{bOeW+GDRs82Exx8Z@9+dYqVo!UXzYFT_Z9 zw%+XQY!+G zkz;CSXJ=sG`Q&(0ph)v}EB98}oSmH+6uC}U-)EO0xn)OD=##Ed*fxBYTEOVwo9^pc zDs0At*x!ImxT}*IFIFEyzHkOqv$3Lk4Z1(zk_?gtAO=A zgW%9mqV#nhqN9gOvpnq~5gWz(7ZDrur-+qNgA}N8w%=z+ff5U(gJAUa^#yof-HjSV zLeo|+U6Sr|+*zz^(vXmqZ9ab(05A!fVCtx`HzZ&^WZd-Oe35=-##3zx?QZ&RClHo! z1>HmJ`ndY$<{u9NwzlkJFx}}#4tDhGs3?nkw5y9tSy`Du)7v|C4h9DF&8fZ;w3w0L z=r05xqI!|(6}xL1VLb1vPFQWLbGR6R+xDz&orAU7_sgxbp{*WX_ltJHfGKD7WjS;=J8A zf@OoAPe*}Y-qQSPt>%xBa+J^xu*qpz|e36_yNg+8&03fy{ zoQzC@?$oJMNV|MTmWvldfhiv-v*QpHbeiqWhpv2A)p5d@!jC0vfyf4i2T%?t74`7& zFij53*6%ZNl$NyVQz#Y-Rg6nY_|}ezP2X5f;jPv0?(XhLEX|PD8v(ne!E%Sb-rlY4?Nxihi-%uh1U5&5@?I!0oCWWfV18nP zAqNH)C8*&eLR9FsP;GFJnT4^jd}>F(b^P^UNAg2b>ICwdnrTYRAk+<;qqLNiMtxBG zV9ibsP<}yA9seNsO2EH3MMV?45A}cc3;exv;ELd}qX^l#W)I?fft~dq5FhYYh>!b(EGaGB-Q6{|*;<*(let#~ z$s`6oK0bPSkMkQ96+%nr0M4h_Z%riv-9F^}!^x!L@x(p!v+*!P0N|}NHhG2?@VwN2 zNNBPDR|yRS;>TAcndNH~8mufVacSv3k;YL_RPC+J?keyxd(Qtps~fGLc8{dzxe>xF~= znGh64Nh!Dm$oY^i>&DH`R=T@JCRQYN`zuC_?oQ@!>LjKA$v>>G}gHQt;+Jd|- zDry{N)1qDBxcU66-D!M6n;jZxaaT$L5X;Q|m!8b_0VbD?AUQMhBHCkM|68^iKtq@T z)%y$Y71bksBxuJLf;+wtm{}+50`RYd<-pMOjg9F^NyKOgd;1cgA`TruyM}>*LGNtn z>o;$tGzd<4uvK$gq0aA`H}xW36st zVlso4kdivtAt5eah5Tc3epk?DT4@D8X6g`PWLS*=_in{rFc)TNX&HDnA5Fz8B_i^o z=E36XYJR@Y#>P{V{QFf$)CkCTvDx;kK;{*&sH7d7!;~-Zc^w|&4;(AlP9(<+;LIUj zWYEBbdmlc0NV-Ntqx*r#a18jdQrlUymKK%6kq@?k!NJ!ARPNt@=ZN=`MGuEvZpWw< zS_~|X)ZGa%-rU(-KA6Pt37h?7URyrN494h*)phWD_UzeJ+`{TAFxH=ue$;{{?BpBo z6iDU>d2M#LR>3Epa=G#Tv%zC&X>WA;*^?)Ulf8(?>HnKL-i6fhIoAIV)bTmikq}jF zV>7dv`T0t)RE~}UY!kE&a+WIbM)4jK9{JVTC;zy5KUTRGa%4Z7PhUFG}A)R#@{;NT$XG6btPDPo>{ z`}nG-r-yVflK;shx_o=C^lYXFOti?O%pAzNkeR+h*&glaYWpGA#Vy!B>0typEgU)bra=t{kHp>_3)t!ChE9kIxQx7hBk{ULIwC0OUO*Pfj zq%4-WeH#QgA^ zW@?WApq^`R3g36^s2tbYa_?<6R2TEGgkN(7uKQZ=(RJ!!rVtc;+0malmLVi#_2=g1 zi{o$0%gb$-n_s>>dHPO*0`KR5+?*VsD?~*_%~|!TJqRycxS(_|8oGNZ2R5AjHV^h~ zrYCoGb@gn}1T-5nkh8*ms*P@mYM8(ly63yBxOnrKlWsj$cby`q_ihAtSfhckuyF4c zUS9Q2NIxq6&cVU6IP!e#@W@C|VBiPmndIc;sHms|0d^GkE-%P$$y0deMJRL^c-;9; z+FQZSP_t7Cuv_XORrlY}Kd(#?gD-WaYe=I|`|=;U@4D3}9#))~=$V6H!Vy#B>~H`I z!7c|ve(4-E-`84`jg76d!zFxYtE} zGtd`-y04lOFjrwPSbT@h-co2@+0(@bXkaZ1L~)bfzwt> z14?sh+o!ZJvM9G`GF&(7_>u&-6@y3IAf$ZcSR|6)YoZoiBO@SwkVjNgOp9J}S9 zfUbv3vL!xIo|6w#S!&{+e~A#CR%_CzEimsRm@9vI^)k(+AO;}R$dE{+l%ZHDl?$ex zQo0s7w@#s;{99_BSYQ!Y)G2Gyowfw&9>fAqPhtV-|EReFg6lReOJiRp zp%AX;mk#&vya#NDNe`|gIDY(;q+{T`;w?DR!8tiL_CqJqNELA6wVPLQ0eskH?CYQ) zJ?@CjONz(=LdIPKz3C|aCv&pSTR}jM&K3@RD!ak;Z5KJCEy{dHR<;nGZvE=tabzu{ zo7Uq^p7$+mcwpCSN4}h!vtV#=9DVcJ0uQy>&l7Kj`Y;BGEAQO-)VB zrbvFtZ5dhFMA~NyRVT{QtMz=_xs;NX@`%r!t9o+&z)6ujyUB9-RC~T@*YxysHwp2- zs0hC(dp1yFv+|vmE`AKC;p%;nDTHu+mlnA6IH&gF11VTyeZdG>(&YU^pt|t@LIF7}V0Wm{Nd_~~`&F8xwWYE(Q*VNRY650j^ zoYrP~MsZC^$M#K;UJZCiVXyyuaQEuzgkcg5+NZx=68HD(;+|t}TEd}g1%VH7cK*?k zsyZ?Dc)nIJV08^Fo2? z?b-U|x5@uP;N)oy`fcA=;c)m)2mR)&SzuUh)k2qs@ftQ!5)lz4C?vNUZR}Qiq+VzR zOMGBe>=7fy6H#$)JGsq5v|Wq{MEo^jBr=ep`OjRL<@)OCYucisBAG0nkW1#(UmUyy zSXmo0FxF3=Y;SKXZUSyH4m(@N3P58C5U}n-fsKvL=n+Cf!mGHiBz?2p*{+&U7tely zKU3YGFHa|iDDg;Y0hkVktj}8gx7#g~a&x&2*HiJ&WJ=*Fov&#*} zob>0?$7Nk2NFB&C4(c&mTV9SM3=9fN`0trB@)KAQUJj};BIk{xY?}k~&m`&6_+1(8 z28*m{mEAo(bD8nAU^*#*@}UwN(>r&di_gtje>${5$f05BFmRwgG-`J}s{T%WBY@(y zwY5q60QoU0W!zuFXAW&+E_nz+vsnK_A}v_0cEn%!p5$hu<+Ez(Zei7E`)Q`AO~p^( z`%uB*J^lUtZEZ;7wri!kkt{4M?o+$KP?eUHfWH^Rid=dNp!$ofaC%0D!KE{-)pPrd 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

    xV82(R+ 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 d0J@A4SxMS2+i{W|Cxxn|C_$y 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 z brkl}|!2NabD$7@Xst3Y@XJCp0rO$nnrTyu=$=L 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 z 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ÍogRJG$}B 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%0AH7)i>Y(4l!B zoX-C4)}m=bw|)nMP0INns2>`Op5)1W&gc@byS_5{%paLHX;TNo3T`k5ZfR=S74pvT;i+6L` zTH|Dbh7PnwmySd-q^UWQPErYi9<<)r*AfA!-VU!AjAHV51Jq`6TZU{2T|CXSd5$cH z#g{NR>gip>1C(6QeQ6B6leJ!m=EegS+mj`*;i&$Xucf<+I$L&C+_YRm=g+bVzsuqfb4NFs+RLHHoa6qd<;rWAAW zSKZywkivvWOoyQ(?SR)iX!b;R639dV6h?2OiJNMxD5H9)FlzSWg7^(mvcX*|A3TP{ z?alICSk&d99WEqwi8HGij$+=Vu+ZX5wVp$f8J3pyP z63LCmc&^YzP6ro(X@fj_>a(ev7VCF)`ah3Qn}eYorWVLmV05q=__BYh_5Tv<+U_65 zRiPS~^nKY>-w2GC_N#U-FIWE*DXh_)5&+6Q>^^~309!z$zwN`=zN4jRq?Z&!f6Y0; z3%;p2!xQ?k0z6g!0sir+>e|rln*&EpkE&eC%|?=ro|E~o-o{J;d;Jh8*eidSNs>Lg zxbdfDx6cBNO_cDNM;dUAMym?@g%|~Y{73(+g@_+34FDxrVgVEjH&Q`j_;yfXoQ??Y^3E;c!{>{B%l5+D{(26{YByFGV(F+{oVay@ZE{>)xPT$O0JnD!|>lx^vdLLw(-!zzgI5(XdMk;@l&& zVC*nti9?|gL{peg3VhguA4XFztQ25cnD~T(%aND`g-tw0pNgS^x@ywux!o< zo|qS_6dMWJV#3Cu1-Ofi*A^OvbreHxn@46)s-6FMa5tl5s?`+OZ-g9*hQ*X>CIxDa z_YLed5I*Pg|32S*g?gBBWySD1I#~mKCe^>GS_Y>laR%5+sZkW*dNnV9XdXo?i z;!^v&7h-up&n*PkIuwk(%(Y=x_(j{-a$Vfz9-;G7Av92DdMWJ!EImrvj0ed8bUKfS z%JH%cD z@px(-V&W}FoN33z)YmW4c$kuGcNSBIlgBRQolD;0&{J+(lKEX732IY>doTwL4Hpk=!)7ZW|bU0cnUFpXkQ)Cq_%#n<)e85%1QI7 zy1v-D94;^~pKT7?pm@W~U=r6(VCRjr-q*&C2hQs(M$i58YGM)s4G^x)zKpiz;AB%P z-p#^%#Ccw8b|rVyfRDpNDX8F}(7>aUj|P^I0pm#nKz05Qa$G3z96%oJYiV)Ji0A+F z!Np{oAHX*F%QGAdb4;8K^L7529*0-FTl63zLPJh5Xz>mJNvTZK^nn7MNyZoj+KUOF z0mdU^3U`#PMSe{w1DiY=qV955 zVKK{eFb!Z#QeTyzPF3{otgVHTiO&qi2>JVn2)rnCE5BuYFAB6JUzE<1ITUqt%_H5n z;O!JajE+R1Ymh5BAiNzyYGBCu!~&iS)%CgdSZ`N(toeuY8o>z9jht8y>99mTTpBEi z*2q;|41YX#0ymQNOVee0oxbL!*+AWz1<+Yq*(M9FSAjEQ$DRst@~ymY^Kl zwuOY-*U1#JP~G>d4hmEZJ+%$_xwf{J$!BK^{(Ka#N~75)O2F>*&s~bV0yyOmepNu6 zzui_PKx9Z5RrU)6p^o&zCjAluM_GdSJZFzEC z4LDUzysIz=tV7qS&Dl4tRE<)@iubg>AhVi=)zmnc`h7vgeQ>dhr7@V1X7IwI)vk@z z%GyLlpmi3k853XWV+O}SB+MMX`8#)!f7}&-=H6TWWGw{qdN6AuS2}-saeX{^S4eqg zAlKJ$u!w61kCPPTGtM&Xv+uhk*b{pes=6VC6on7(9*TLu&t0oFNTo2 zW?1P81r=qt6v%h0C7dxCagV;!2V zn0!HwXYSu?rb$trODMxX z=KRqZOpcEW9+3AoyK|ncjtzhoT24@X<2cGRjRLF#!^Ll=nQn~MjX$6sY?(K8VNQ|c z9+@&XG%N!gfbW{RZ5YF}yuv~^2M~PkGsP?)-ii&??x|bSDzT#aP|otqnND!8OFw|( zKwHBpe;k`)uL?^3n9>wvfk6F=AVK|u&g7x0)?jPA^q-YeP{Ex%HWaY<+1obajXpH) z1~p<;!!WWXBab97bCksG(D+|bD9xHJ#M^_aovcZVYSzT3Hb5TylNi6lR?i!6M;498Qyi627j|p zB}*~k-yd&{%sSr~09Y2A&pqZA0Cow%;K>Db)$nqe#rHoEbe!5OPu8-2(Na)XDE}#b zHNzIGNkKEiyfII>Ce?2wm|2m#<;5Hc`8)`#dsWw0o|j5x`Gdi2^Z`K|2tkHq)pg}W zI8gU^l~Vo{Vb5k~m4n+cplh9_ijBjCt@%?0ct2^LtsWNyEO_5l9@Vwe$Vi$e1c$b1 ziJ-YnGr_i?#BiE>IFYho8}4@_qTy3lN&55c4V2tGk&HaaV($hc2kpTn8HzUHw$||X zj~u!r7WieXfywXFZ2Ka+xFBD5pX4DaNkBHPRYcQN;FfO8iT6`DyOYjlhbe82CpLhZ z-p2iz&2S!f%)$?nwZp#sQLNn4l%2HMrY#yQ1HMw!%@o}ibAy5;V#(~_(ajUn8L-eg z?M8&L0C*SJ%6mf?3c5nOG$WVEY58SwMfTJ66CRPBF<=zBbF+Obv_jIimKZej{HlGn zfaHf{lc6=4&b?__^<44O4eJ+B=h<5MXm^y&-6X#)w_7SWstnDxPc(JK03ZNlpO5iE zP=TXj4kp+pEC#%ja#3fem?gSLqBJzwvJnbKLzJG}LO3tWRcXXgYAPu8dlA#j-Yns6 zp8eae>T=|bo$Kvbc$yLObj`$>s04k@2Oc8LMAFfYpKO0M^XMugDj{PtiZs}5fJ`!AcKz^`;WelsvN8H~H7Bm@}xqGLz7 z_ZALvhaKj}R|n zMoNR*Lh7q-Z{yh9sfmG5r!K9|2_Nx;FFWaj(@SWuO|R+-XTEshC&p;_y(Lii&;2$9 zk?rl&Ac-^!I-HQ>t1JZLjoOU;iy&>q>5q$QyjPpo$&;x3!`quqG=<_0f;|z(!_X%6 zyV+=7Hyr+ag@(IcNsqVUX%bE_*y7LawVzV~_D08)q!p8dQ3x7SIWFG^fQHnLA*_|Jc#sMRW{x*zeSH}^4t&GYd~!1LSHuLkE;yX)OePwU3p zh0wXWV@pFRre|~c2y!z9Z?`Y zMfYV>N}CvXy-#Pb6GhPpCPE>=T6z9%yFw0b>+^MLOyBh=P|I^mA&l5rnPzYOoqjKW zj`@+s|HU<9{%`qT&i{M<_eMuL9-AHMe?B?b2o{0aiw6N?>yB%NLmRBKHGq$6T#(?! zB*d~T3cmq-JZje<*}67#m*!P+dNi!?OSnE%*GG-!oh?e%CwF-~V#f0S`+fb#PM?{@ zAcf;<^Z06J#F~hFLt!{3yGuuQSM{&0FAIvW>Sye4>hjZSN>(*IIeXTymq1;a()6XQ zu70=w=OQ~@=~$OZDiRGVFXG4d(NzG1%P_e%eHg#zw=qEkiCWi+1ablu8|)wXokB(S zEE@z9v=H6pk4Jrc zMh>ze7DHO}zpGi--km#yMEqU8uOG~40VnDRHmjmovb65W@7sUfZ!S#%+5EB;h;wp$ zMyuVC1W2t-Sv{Vw`2(S^#+|GojA6c(hy9ni#^J9gh0e09qJi5%1& z7FF?iEJLdGuQy$d7kmI_OanGA2T~VSZCopjOE&m~XE6{bn+bQw$1z{}>O1<4Q_h*H z-CfPXXBUg0{H_dsiDK>GiCm2PzYMEZu=9h$ZrO2xUGqk_4^{MR2Xk)09=1mC)ec_Wm*`W+ZteC1ko~#6ti-9cgd7I(N3efK3_BwT<`imxPRfuH#gfLf zYoK8yV)v42S62m81vnjPZDnRx7IgumTv|_jI+1FzLt6ws4To?Cn5iMyjxrvIA?$@{ zxM;g1rf6YkIHmg`oXjSsGC-axTQexR8iy@u5uGI?6u`cWKyoJ_(CzmCp)ajCEe>#W z0&u*Wj1H=GFBbOu%A=~S**iP+mkv0w4GE$cq6#UE3@r{Sa3cEvpAP6KD3j5z1bB%B zJLK*OAOJtqqhVX#aRK=ksRuE9MHu(s{XQb-rCPW$xnK~Yf`4pCmsXcHtpiSBC)PCV z8hXZ>#+{>1@h5Q8f2OwU7hK9UpRZO{F=KfsuOW?qIO`rgqer}WcTHRi;h_D`*m)j< zAa2w~&AfQwS5GS&N_xEk2ckp8*xX+6>Y>FBU-dRz#V&L39s)<`vJf%sq$5mfCdQiJp48FtRQbV_2iGI-BF z9HZHXkGaQsnf73JX@r9Hn{Mix&%TN>&qI|#r9RlDGDgTx2FQs4Yh8|DMD!eM!X4-E zC1GeG126yUgbT4s2t$%G-hZo~=~>JCH=FdJDOSvDm-Hg|QD|Ez;{`@x-B$@DE@&!^qN?bxlsHe`mAS?QN>f)#!xZVsMa}Y zdPkQ|OF6gnPHaxElzH~u+SVI+vtjrw@4oVI>V!Zo1%_5AR}wtb;v)BaQeo5H6adXu6eKytD-gad z^*D%+_m)ss5Hz1X{ijHICQKzE5vpsya$f0I@4zQ%-HNf1?U(6s4XxpF@N$|?Y=XtsgCs6O{u z*{}sqnyR6Jts4Xt!^QI2Qw0Inx%Fn~8Upq4VguE#FJG#xn0LAe#=A+KA?qk}R1#P+ zY}$SC3hR+_0uDo(A76~R`4|}1x={K)kg6z?J08AixMkXObw5P+UecX2g4oAF)cbY9 zOa3fa&OHI1>M`(*qu`I1XlNU)K&I} z_r?|+SErE4mk8G*yMp#jp)Y(QSc1)fq1OeYemV(`tp=Q45S%)q@SBIvWp=x>n@xPX z_ZkAo^5FJst6PunI5`e?6g*W;ik9y{U(T?+({5eSFFA*Cc!$$R7kv zqUl)s!E_`zS?Zr33|g6__#PURx7>{e8Wg!tH*1`}B8(l-4vzrO-H7mYA=i8aZj)8HN9C_`~yhD*hXv4 zDz0+)k}$|YBhVZOb|i*0mzU-&!N6(m8hY*h(Ci1zjuCB+`k;P_mX6p`+dc+A=o1HA z!F7~=hFTb={ZV4@H6vo70T)Oc7J?Z`@RBwxUO zNmzVadWN18e5yY-P^0CwLEPE*Y7y-mXj?Jh)g#(r0mn!l)cyRjyr_4I+`x2^?NWcZ zI2xvL;guplqxs<1tf7_Yx zDuAp{VH(G~FgCe=j^P}Td+cjx_mKpl#67%;y8t>dW27F)ZXlM^CXQz~9;|8l8@})y zK+*K)*Pvxm0mom+w{w1PR#g-)-KYC|oS2Sf2a?pI|30Z-T*vcV0hEOq_VdeT4I+pJ zDOM6T{9?^SwC}H8im1y6Qk)AK1N5P!lqqQ=@-%Y9d45| zSlVeb0NYm$TlMAK-!jMw^XRWOhGk`P5V#=L8OB9?apiVjOpE~!Mv8r?SH1Jif`(MO z;&6B9&id-N9K8B#(f=P~@7SXYv~-KMZQHhO8>?;Gwr$(CZQHiFnon!ByYD_X=jNW| z><{k`s8niHQpp&zW{s9_bKbC!2%_zF{V-Sbju{_0Ga$XTKZ3rxcMhWC5o3>-)1yY1 zDs7<^Y*BiqV-<|UFMIs1+&W|GIXzk`%w*L;I5SXD3w6O=R<M3m7z18Zkas8vGy%*X?-)+;*g%W)kdL0 zd7@~zn(D=*0%7|P^pDO1)ZcAcCu4y!2fTQnFkXnW&|k~1S(j=vKD}QK8wPcGM_Sn& z9ds7XnF&(_L47?ei|Nj#j`wL0Ux~v|Tfbgo2;GmG6<um)^TAd_)AV>=~ zY#8_baZ@f_jV9rf+NH195Z!=&7q(0WH(W9W2R}Kr*t-qMhMT9@YQlRAduEdzw=Q2* zRC6HZDEZwkupb+uyvivtT+VE-O~36bjJS*FOeI;Y?A9q$$j7}cv5TDIKXB!myHOD< zM+*&tnxqjXNy`Hu$kcrZK5p>vTSooOzn>J;n2R+_sy6VGwT9~~$*FOa)YNUHRE(8W zp7QyOhWVG#E`H!0H!9(j%blQN;+2E;ll}!vSB66uTyHEK;pAG~e6Lg}usb_$BlU<@ znk9o?&u*n&%z8J&38N$G1#^)MO|oz=w?B!X4c@vWHz=60<46jm?dV5*ZKe2cN@QbJiF^{sSreH&{Uz+H=t7*pb=x`e*|vdc<1-vyWro1bbj)EvGxmb|Fn2>aMfnvD1zn@M=)hp=^O1!#nA^rM1nq=bh|o*Vo2tMPQlK zo+VW*@+WF6MzZsxrpaD_i0MKKOogJ2~$;G4})B45OZ8=w#|Lvly`xx?4?xTS2jNuDRC6 z7O`5x7O*%q_NrEr@313e(0OqZ-x6fdb+pD5T($>r-QV8*g))*JPv9C!Udl@>Y=SDx zidwJuatar)Qh6Q|_J_yt8vB@DiDnPWuh-SOtZV3u{6&stC1Y=sFquU;im~N;!7@2vY<^KIUY|mP^ zg@@ivRlB9Y2TP1LPH=(6%aL4i#ha$>gd%= zlABJedJOXP67gSS5Dc;d%9+{&yOIOlK+VYcvBL_*QVuCh@Sn%gikQLa+-WMEIy!(F694L} zC|B56=Owrj4;-Q>Rs@o;Cl-M-46|}8?sJkiG}y>j?9nZmqghS5!!3>1UwCiK~1NXgD& zy>M*sKWa@!9t+74SCU@&DsOq~X?XiTa<>flxA7_|s{0UL)5l^`1r=0*J{O86G>8`i zIhh4MD~ee_PlsSvMzqjQnaEN?=z`NGrv=#nUmsQw)zToWOQLlwc_~68 z{Yu*;pw}xY;OyzP9e)G0H>M0GXZCWxU@jPAjbqd(8&TGv?G+QUpyVXYmA=#*k?ER0 z$D>e7IdjW3d$|%hd^PvB*Uj6fWwLV*dou6@Op=(Y;+#zV+9m=zMhtqlyMr2I_IY+3 z|H{NM_U!cV2ic0!e)Lz<{B~4dwi^-hpNbji8F!Y|3*_2&jdG?iBty~M<@{cegXcj| zz4q69XMdCj^pGJhurZx^ya7d9^iAL6{cW6D*ASb8N_($8_Le!q$qofEuRYFc`s-)* zcDOQ}5A0N)=c*->d$nnwm+iLONc2L(=EKxvQO~-^p}bhuii#AfG{};9#BySPnHEG! z;X-_iET3^E3Xm^LTW}B&07n(~^M*todjGh9arOh5P!x)vUfOGC$V(wTp2Wl~RjP!> zVaB}8k545Q@0CFz^N%xauT?2)aCuRtDuy9wun2EjhS=tf8M;M?Pf@BRZaEjE;d6JZK%q3#-_f8*pC$PJUu4X!*yc5 z0G&1|w~9@__@P#UTdPbEq`hsLhNa}GAIS-@OEYid z&x2R3KKKs@7)!I}#tYd?ub4KMSM}V=<%OZBOp6Fz!kP}uqS^;)#ZWQknCZ2i-Jk2Z zU$6t77c4H^rCc^z&s!yC7GQC^5j$%1Oe%IGwE3fpfaP)FoVp#t`V@^IUmClc;s5cW zh-}ZH0PlSOfwx!kb!>E{(BX9hIfO?q#sGw+c-G~HDON)rn>ZJONG(JyyBM-@FX(cn zZyTEs8r<&J%xwOQC87F;(|LN^?+9^C($Nxma#jS7tNaiM+(YQ3g;WHvC+#<}al0d= zH2OZuS<>McBiPo z@DBQ*nLv6w^4-S}?(PQ49w>}{ct@?6~(Bf1|Z%TeyXhUl!)zH;XBn~L-1 zr>DW2@p0M?KGN4cg1m|`*dx1|%e!;H6{ejYhH4Dm?S}i0)HAz09s;V5zK~yZaMw7S zO`mIYfeb^`?6?!tL#4-)@R$WASuLvJx~A4jL&_riaCvHUxiIwER3lamslKEl?_r2S z4zEET57EKT4OT;3wM8*~PR*(gmEJg-z(MIDgk)HSYg*r6)u!0 zIirpqW`>(O!)@)@r{205v4~TSpy31xTY%bWW{ONosZ&ZRbbZe#+CcvtlMTy3Jc|lm zNLCesMLAvAqGe^1qMf!9BYhg3E<^Ytzp^uzXDj;8VJ?Zncm~jHZY`-V%NlyqHt08| z3UB%eA0{ZZ#8(bI$%lZBdPZ4ScF2a|?p&&@X8$DN7A9uu;9-ufTU($2Q0|vWRtG2k zok~Ih3Z1oM*7cq*an>mP;et2%bDU>+jo^`p7%GMK_#bi~F<7GQD~|K%<%EnxWMUj5 z?;_FWREvcgn%^>}hz^SjOpE)4${0v1ko^KOR*67sDT0jamJNqVx(Ed+2NR~cElE~a z_)BW8<7?}GPg@E9g6jro=1O;+lu zbww+xRqywv39N@+7iTlUDB*mhi6hb^E!EG_+{9W2C`_mUh>zi^*)>1>-9kf1Z{!zg7K>51z#)%uwBXyJM3KV3D@I`NVEMe>}Z)f6VdWW`N;cAPr# zFaAx`m<)EyNa57yvV-c!i~4eBo~B&0f6V13wZd-yDKLX17wIP0kdIfMV?Bby$B=_5 zK@%xbmN&nMKT3{S8O`*8(IzTx40a9f3XCj%Z#O7u1N8O zqr)`x!4{M{1b99)WHcaPVu)q#-V*E9WKipKf~3N6 zDdXoD6r7|96osg)x;@Z$#ojsQS`m@{wx&h&#oaPVE}kTX_%z)gN?(KL^LCR;SmNxl zVTulW0aZ{#yi8Hkfdyp7)=<{*K97Y;h(sU9tM&RRxNL;hdVZSXHQNh3gXi}trCKGn zw);aqJ~P6IH>pa6Wx=}mUk^p&I3F~IisL*T<&H>YRtXOS*0a#rc4n~%couHvhYH8g zvNG|)H^oDQZD4$Gf6B-MZScN{etcFy9Yb?)?Po702PE6t`Qswcs|f6EVxQMe4QH_# zls0v6QeLk>O3%gawjjaKxf?5>EkQSRqgz>p#2?GH3ytIDJtJ4YOO*9(vyDZtiRxe7 zgp-;W26NWH#UmQm4NypAInYQr$0jT~cW3A{8AG^8&G^;bCG0RkY8HCnH_@9ag-A7) zC$(el79HyA(5|P^uJ$l)`;4Y$*Kh?5Z+^JJjK#VN#n)i;t(Q=J+EARU| zjun0S4dLn8kDN?)=7g!f+V=+Ax3`7ICC2%`s~G*&d#ZHjBok}|Oe^09Bb#2j#CxhD zes!gz5KSW_5l7t8ngt&f)*|aw<4SMHU!-wr_!qFKOec#HncV_{$EB<9=%0>)x^N04 zH{;I8Js-x?b3v@31fmedmof$vRPmn=4J$|l9U>TiS)I&Ts<-%XxGV_Zem!iyu7AR+ z+ws-St*P?ESijg5AOii}eoP!8x4*8Bju3RQ)yJxz7Uyx<50zLkDOSg}0b|ONmD^ysC|9HFIKCItTOX_9! z*lsq?UD@ijir-4c0=K3{ssU3^4I(?as&s{fBS#K{y%o1oWt93K52jP8xTowHXl2dNxXd~o-${h zFXgqm;aIWa#n&#jqd@taxaytvcj8qSNS|yn!`osT6AEl7B*aA!vT^L#oEYPvANl9C zW`=XXhw)YFbR1%ja-@ik1ifsz`J#96Lo#4F~JT!JHo!!6tAdkAE z7X)s%*>(oLfBFbppC8bwx3%Z&Fc-;jTOQ0$)XepPXarvWujh4qmMSA^bP^6qc1Asb z3)FS@(IVeR$SjY`Y*;mrDy1cD=JD*TPSX7#?bcf5M^s9MH9b(P2_ka_gJaJV?i8)jd8t)GvK#E)AsPp12*ZBPiJ>! zbx(v6B9agcFQ*t@p~{{)404>^?qu>SMNEH zs!B!+B8lsIm-HhYyw5ulvGaMd+@yH+m6%yWi4y~A8GaB-*0UJHh94^V(==3yDN}%i z$bgaAZF8ltFGv5t9*0!%XM8~v)TVIXmD2h&1|(b(w83tNn!)3uw+_aOjw%__RP!d9 z#tvYdwi9yvAUM52`Usjuac8e7c#Zt8`TdfRjYtfHD+jqEoe?fy%Bc7(=ljEKdEqTO zN%H8MIh~XZNA(2r0U4BQ^^>R<8C;);CSOS zDz#c>ll^x44an!9!2VkqqB6i`+D*+Hw=PV6kcs7dn|r-yMr{Q*j-F#l{s|?UQjXDY z05IWe{bkJNF@@}(63Ux$aF`!%i~#MvEcvSs_W_8tht4|Od_7fmY1$zx+(vX>|4kx_ zRnUW;BLWBlun&%I{4|@JgzW9T42^}3>5WhCxNPWtLR;uWfh=gLB$sa>f%W%i=e(;Rae)sl5W_dq>ib)#WY@GwAN2e~N3Ns(Fgz^Wu=Sz8W1cN#yh{oD3k z7h2!v~AzwElL({mS;antxI+Uu@mE{`90&KGMq4Y7NxR94C zV#`O4-q!*A0V8#9SYviib>5br49Zs7q1YQ~XfU5k2#yemgI4fz`0>XN3UN1@>Kq*u zkw|=M8oF)x0n_opn*@tg6h458#%;CT(j#7R$#*QRco z-GR7>*hMm4yYWzyS(cApZ03}hj(FprK=BxdtIh;hp#bi+O*sgJz`SH4RPBW^OBG?9 zVq%?Y>Oj`4@y)u^eYc&P#tL63cu_;au8<(SpO4nWCa0%T=wLF|&V73WhZ-E`5EE}J zGh&A+VuZlHE6U5XKLi{_8@_6Fk7C@Cjbu`sb$(&VNGbkVowiJ&f%;L#K|3IPg2i-c zKUreX)X0{_36{mZUEpDEgEgyoRCMwg)ZR7q!C;$`J#6RnUra_SPUo@iPn1j(2(9xZOk9mh_N8I`ChmTFn3nkZF~XU^koy<#y-?cReNAmB)-QN=iD!28e|FE-LpOC6(7(}l8j zE4l+>>ZqT{SnpbDtC&PLAZ_|TiPLd|n}kR|u`$?*#I!gJp1(%k&sK;r#+hMaRKX*f z-h}L_i|-Jc}mMH+K&uoS6(w;p0xWxAu4)72D-;5_{g5-DWBq#y>dB2 zGtB!*(yND~5g&Y2YP_C9*}?Cl)r<7Of{}L95-57-$YM$#Z^W{Q1;et?4h_ahvL)rU zl*V_V3$VEJKr>vf(M83gU>>lj7EN&;6*kZ0$Mj3g*);K8IGtL=1P6d$kKsG%oW=!Y zbT)&sz;CxL2s`sRVOzy?e!3gBn_(;e^tp}lT93PcBJdRC#7o7|X-HGRj|wxv3;4we zoLLpuk}2_Mw4iLs$i@{Dk_HE(xiB@DRKgRwnl0r0wA=mT?D$dwAo8+!<1&r}#|-wG zr&H%jDl;8AN>CHpkZ&ON7Z;0?i!Y3lPI4NFcL(J~Nyxw~^`=uXop*6dZ6k`qj15+u zagYCl$T+*iF8)#+yR+BnkF!TFc3}4&;mRwMHx+m*-x(f>(}62#tX1@3-GfgY_~OeQcgyAhm!B)17h#k%*tCAR2qLkTeq1wX{4(#(Jd5W(9B#BDpgW5uv4`LpAx zn+>sed6krK%>IpV#a#~NEoLd-{%prjhVbp8{nRmtzX&SdOT9HZd$?o`{p+Ke_*KV8 zUwL1Xt?)`T1KdgRLQV;~EhWwS5Msf?Qr8LV@~n)c#;}}HAY>nOge4XMXx1GNeNFYcVp&%xXL30lguXII*f%2m%+*G zCk~wm9G?IBl`N~U$=mmhX580=gzuC+Z45}FXXAd~P=iN9(&3S9tbNb|7#yVO9dIBf zf#Hd?KR?yhf)h}bKBX~Ex6>3;XSh#k!j#i6B0Y4|&jdB@8s&&tl$M^_rA~UXSwKW- zH5AOx71tx;A3&YmaVrtA{h?$XN0V#yA5J3S;|-xy910QJp^(TffxF{hravbINNvkk z$=SzO&iXTG4>s{2|7BH)^_`8J(GyMM&^)F=YfJJOc%vx_ot4TwVL6h~2}6L}objzs zTw228XluQ{$U&JT-_?xX{?x9~_J&*tg1|87iiE?}d-Vxu0G8A}L zhyjKOr_Hd(fSCf(Sx8`>2WZ#l)YJ~Q#A!AskC<{euELcnwj|e-g6=M48wRfwp=o-2uZ$VTYB+Intg;u3gGcSD;lf-xt*-d>T##CI z!vd(X4-dj{mTYHIEUT%rNmn0H)xoGS2ZX5LqzFe1lUatFr69gcT zj1vRo@=tb>LHm}K=+-JO!GWUmnT$jjvFJtl*+bj8CSMH{IlS~(K$yHvPe^s;sO%5Y>1z)5HIkP+&cem}8_13R`ZOE}^+1kE|s z=4@-cfBS{8Ek#IRCw(dHzslqP5$mZPbcVx*;ww<+hjzuA($iQ_=LfrPf%KsfZxj%3 z%eY~OoFK_b@iT?L zQ1mUoSG==VQuEtn*-91Yqoyqf*6!=CFX*6+y@?wN3}uvlpN+mUm$Q|x0`$LPE#iyC z7IT<&*wZq42)yIepI(l@R1uI;@O*lXBzgLrh&~by@OKYb5q07_vPa360t_D079MCK z#&gIRoKrv>P~h2vGusA3YA*0_dq(3{%L=8P<$?V<&Yk3;Iff(3w^GO+_h> zz8|?A+Jw?I7V_+R9OnKzf)P$Ezx@)8)Uz})SPc^J^liWM!=^#^0%6wuVvuZAC`kz- zzgI+pZIF!5kmOECOOeI@x@l28ve6ept8H&=fqF;mKU}6%I3==wq(@>+oCKnJt+T+p?st;Z2quL$q>KTlv-JKH8L32(?GqYC^Y zC4PS&D5#v$a)0c%V(V{g$4Av%iW02QIG%_M^Vxmf15;ZTwE4#zo9vC?v*t}6RBDl8 zXTUyur)!EiUMm~u7vJ!Nps$<;UjH@U#1ec`Bt8StQCW&G4?MhrNqNgc3DO;e8`Ce5 z`f2kxnu4#HW8V&9eSTfyxVE-=X8sB5Gk;=2oZ13)^lU6NQ+fHEdt;vfl30jyb?G%w z9|U?o0Fj8ukOfKU^X-+2o=BY1$rC&_5Tg5bwGU{N*}x@IB)0_B1sFN7gS{poR-kEUXFr1+pEz9jpyN!1jdnts_z&!7!LSi~sOg zwXYfrD}4~9Z-bZb%T^4=5SRx8GYOR!-E7@lNWWlvjt?inB!lqsWM)k`1Ex(O@yXNd z2aymG)sD6`piL(!+aYD)>OdUr=WpY+Za9y1?;=`;H3nrW%W-Ta8?UP}kjL71Sk3Mq z!4sxVR-1G{(=&CP5tJ{jFZqYjm6w823O0u#K|Rz;_C{T2|Gg6~VSRN%LQx`KJ1%Z$ z6og0?-@+Wn{0x43AEb?O>)-5v(i^BDe(L~iK!kstr)^Fa7F?>tM-s+(zQ_Fe@bi4# z94-1d6grx(^A*YGnWWNC6n61WG_Jmvkkm*e?5LI-M90F#( z`ivT@qYTytW1llkJfXlcwG^Cp)51Y#JEdI?XRP`bqQ_E!=J`zDJaHU86uug&0F=2O z>N8JDUn;t_6+ArXY{IfaV`>3djGR&s4zDmcTQmS3CQWj-e3Ox$X&ZJ<<@i~~9CthHQ25=f zsPJ9C|LG|BJuvk9lWjl;AsKA?(BOMh0|u>BMzv-AaROgIcvKZsQizuTQF%+f`~fiAvDms(P&_CsYq`>pW;3y>S;0nVo^i|ACgh-QL|?Es|8|qZn(rSVfC`wgFg~YeC7z}G`XI8{ zxNf-*IBg-{{bV|k0~h=ZGp~2zuow#6vi`TxB3SVK^LR7!8os~>yMDEGHb-ZPk(#?T znB{2X-j(YoIKVZ4q)Yh5tfLNG_~*Yo}8X3uN_A(lt zWZMppxMV9F+{g7pGsPW3ZrhupE|y|$fw4EA07yW$zt!4n1;*?G`)BeU^8$1JBolU1 z#(r2g*8nVjL1<<|yXb85=@@N6miZTQ7iOq5FT0bm%-fm1En0hNIxYP4DzJTJ`UMis zlI2Ep4tuVXNzzIIYbF?C4SNDjL)mR1P+1%cd`Jo=R5XWl%}|^DCc^G9E27bAtg~t2 z;SLIWjw98SuQLUpfL-2emBu;Di#&d#a;v7f(f8ik zNRakId32hBdtI5LlzcY(Y~_{eZ@Lf_?OeB>J-Q%$&rPF(u$S%*i&YbjWl{2ojT*=e zN3V z=8Z#`r)AC$Qus~CegGx~p=7f|*g=ewX3xe=@{{P9y6aM+XGs>_w>{9nv zS)E~P9{vtgeQWruLfG6@~r3ifEVMTa*&VQ4~2btA=Kqyt{Nx>gt@joMQiqy zVK|I%a|$`JHWamSb=+R89w-fz`zw53o9kK%2KWJ$PEq0olNqNwq1`7WF4=~_a!k3z zR!_UYCc~xhob?|hIqd!EDgPI}9jm>JR*>;rbOfPSFS;mP#7@mt^27h4jJ<3n@M}%S zbhoj%>H?n_6e*iE3SFoSp!M@aW)nWrTy!-^nxkn}=QsEhAbT4u5eO}Sfn_*LSmfq9 zatv?TL658(0fnIv(q8DKDKvFRx{DAL-deRo0OZGGD!S?y`&A*dM_axsEBq@I$N;r# zV=ar*WsSZVsGp1By5bwu0vVVeyNqOsi}iX%Cd50HipnUO4FIjs;m{Nb5dXd`jah$_ zAI3HHoI;IW@{zo$asP8rlTk;s!s+nQz2)z~ok*YvX%pEoR4Y{g=0J5tTE>PhLFQ4{4#lIq**9h z{K9HzQH*){rt$IfH{$9nJv&C+Do%HZkP2Bag5Zidj^nb2+pZW+*xUZs@uBh( zSmXb$kF|`32y&q>Wt0^-r8Eltlo_4yb27YIj;n%3@knE%vu&*sSYAB(Kt!LaG-S|Pk^-=^r3 zt%V(Mdn;7NhgC093IZL_ryLUlD3d|@=%2qj?1^MRGQtYZAp<=2K#-3h4^=HRj5Yi! z)i)UV7Of~)l1l9Z^Jc70TJ@M-$ORNWK~3t@?UUD@HlP+Y^J9E589+e&GtO8hB_OJZ zx)WKf^xd2$FhCJN=5slMMtDib!cpZ}51<*|sv96w8}lE}yoD^Lt%xN(FthjRyU@O- zSo}f8a!L-@9VQ|~DNw~(O;qXpx^5eP9QppBz){WVsBT@i+811i$c#^!4!UM{YzR7z zOe8)i>H1{C{Pli>G5|Fq*7$}AXp$k=>I|+O$;PM87voEkm2un|a>_O;l3;6OVWLxe zDG-Pgp$C@Yy`KFvb``!-&;bDZ&HAqD$hcM(_go2p6&0>HCs^_GM49PV=UHVb_XG`| zZ%aETXbcbhvpj?1Zf7)X4tc?IJv^nrb96z1*OSZ>)AIK>s_XDcfR9RIx-#&+3yS8w z`0=)-mVJ1FHU()JVo%Xe*vSQc0(ah|s3d`_-ui(18YmL{0;XWOSS9t)y0{)aT89qc zgZyIzciFwD96`YqYDHS!)#@#W;_1q{l=M(h#SW7hELXPIyxNc)6DdG5006u6*M^)s> zj(D*A`wY!VpRX+!c9(BL0Q^F5+CgkaM_vAPyQ@N8EW#NvYLj!UXUQU2JDXF>bhEpl zC6`~blPdmumcyD@vs|YM#Ic!#tEa!{AGf(P5*P^k@dE|C< zc}hKos+bgQnC#!Fn!%v=9 zs-kQ(FMZ*7aL=osso^IceFRrULDu_Q)U8sE=r9{oZzJU@$pOQleFx`n zkl*0ygVq=;-(Q8yOVtbwxv^Q*Vx>|;k49TE%G^36Ix{90syIbE{)D}@VDTfsAnCOw zZ~9TxO^NcG$ZI$bBs^DU?JP&JPS!GCO6JU_6^m`Y(SaP@q1Gi{)+1GBR$Clo(!IUv zG>u;UV#Hz8u(u+V?w2!4r_Qjbe=!<-!LI^d&gWAwxp`HjwVYV`@46(+)K zrFf#Nr1U1C8(<#MYeVq@ZF4;A*W5L4%~L$w`k`cfN%vAs9~V#B1E+fGJ})+OXJOAu zGX@kwwtMT)uUi$-fcD4@FC9wiuaX0ZBMKd>|7p?xXsGRn_xO4-kXIV@GIcb!-MO_h zOxXmV)~Ff7=ATfyPN**o)a{tXZ0_7{-(DnwYMAe_a)oX&q-L>FDfOaBDnkjX>VzfN zrYfRN<}eW@UiVVSZ0Iy+=x4*hfWSb<07q4wJA8>cpx`|g>@An${omY!<^NzFs$Py} zM2w2Y)+(-cFpLUB%uG!Gi+eb`xDs(OasK~?e5}m>;|KmRZ94~CPL!W|gCPfbZ?0%j zga}h4G^r~W2B8ofCxPdACYY`L_HGxl0y;w9-=F-gwA-}PG;*&zkY?)JLqM*6=xP?AYC{JLXzh}{NfvVFu8>- zu|b$vav(D)Dca>_KV3({>qub; zl%;Z0*Rl z23yU0x>{nKDh*!vtINooW2;69kO`L#A4zNtF_qecMJ5B1lCl8}u3gD1Iy-*^)2Za@ z4Wb2O77GF^qlC3+XaqzFfNoA5+UYR&v<)XLHz+svG}PDAxOjSSHU8W7eiSiOlaTUm z>+Pgs;Ip^2#hI5-v*qb(RlT#cQ8P2GJ6CRzv9@u2=TwC4!ImAzz*wUv;mrD2oo!HX z&qT*D(my+%aWeJn9g(yg%a=QU-m+x+8FX+U{H^<~K36^E)wcf%#^Z6`f=v*AQL+7g zyE6os`K_m6<-_^bhYuH2pnS4s83>QFnivAOxbgR888K)YxSXlDF%$F^>}R7!z=9N) zA1gv;0wLy&TwdA7bR}5lsk1&^jVvjij}WfdL^^TwXXhB}dB;J{S0@zgk~$`g%+V2>yp*4^39EtE->y?uK%EI$7<;;KmmU7YFl$^bgU-77PJ1Fh1rUU}pA! zpFUgv6Mr;f;4korxvGWu`-WaWcW@9)wUAKL#T`_SfG`J;b>ro%2$7cw?Q5V)@RXmv*fE;;U*%D{3A?{o` zI*1^Q5X@Kl7G)lhDO|A?T^yX^SS!-19jB8XBB3ij;BDc)dxj0kdx`XTW$M%{n32Hh zSH#-qnVY53o~yhw@@(f`l)x$qz-mf_r_bZENSe178)6I;88s)<_FyuO#^C->!~?it zqogVfA`^*Ld^2)*FA$tednsg@rOvd3=)1obt)nQ3&ggWRf_&|)ddoROM$GWk-q>jS zXJ0~&Pxk?udY^9b_pZZZU-yCPdSF>ypqakYKvfj1bpC~oGEFO$XuYO{45MN7f_6_J z%^Nk7$!q6#Zo)+xzCo;i-}^WBs^0jJfR6Aj8i{6VTX;)+n@~Il&q3VD)ALQ-B#fKN zK7EKy%Of$e=KT3!$`_W4a?)9kD=xU&KaYd0QvjaC1^e-7USv!AG`VZtnrnS$U-Rc0 z9HMd;#%pd`TAOMWz@I`H$G1#N9okPD0<&H9;?@%On2rmL7_8i|uOfpj&aC0Cq$Scp z39h%>o{DHicL?w?}lwg4+4%u+QgoT{x5fkZ4(Oehr?ViX_2nS_SM+NrUx%|V;f zjp8)q%1(;!=`qcx>w$&f2RoEoLzR`)(uY9QB{ykD)Fdk?O0O-Su4$l?%rgh0@H;!#aQxM8d!)je=*RxO&pGzo*OSV^#Aw=7IbX|YZS6&#k3A@$8bnN>8)!69Pu z62E>;j;R1y&WVKA>6z}A5Gk_*Y8$~VHFT7|LXMAjsC%$v!q`Ybs$Pf@FIb{|dU=~? zph9ZdmSeK;n$%&JjLC8OwBe7T2R~E}TT^rc)MoX`yadFxh4*Kn0OU(R1M!p~e zPmp^b`cVA0G-Z<^#FE;9hOWdr4|Qx#3q%U6Pr8Vx)hjc{gAG{fRS$-@}?jTguqOW`hM z5O!*mzTyPit?IzFt#B$L)4!V&4iFiB7^(;yc#udliMyy$&P(Q`@x;cw8YjPDt6Cf* z24M;UhKfOCbITaQ1>Wl4-Pr8B4go)XAcyB&q$nkkM@*JSME<&%O0B0Akt8#^I?_=0 zTsw=QTcIt6As*6eQNw-C-0-&Ul;M0{4DV#Eh#?}saN%I9(^#&^UCOcGQHXh9t3I^T zvZVGU(AUh^@Nzm*^#Vi{VKuZ;0riftv^&%1Z`%)w275gv&AlF?QH1c}4d)(r#bh|a zgw!Ov^G&ZmYvo#!M{8N@y zD~Uh1#X<-jseVB4{zxhL%>%N%zarc*LHJlhi6CiSB!b4(SErz?RR^eyOr90Hqz^1S z=_}=s)se+@MLtNOw;)FxM#%pBq)P!tD{+vUp-bUQ(MyWZ3{}pe_-x%IbBlL=9Ghxf zuWHlNW9#hlX6ApEE*l%_qI6o?&J!I7b#!ud{^)LM%lYy6r5hU=>vQrqpC@(M_YvJs z9qCY#6_L>@(E4fm0zR(N-I&(_zK0Hzj`31)&vvW^u)|E5`7d_0iFK~FBCf<$-1_+o z5A|r}taeto;ezT53`UXYK{i}>KTYm;PB4?ZVq;uQkNsr{O zJ_G?rFEsx}^yOszFT0a$od4t7<$Jn1F1sA*{;v&-CN<;7hX{`C&23v;?zzq!Ta${s ztnA_*D$a@@Bq=wW(+veKz?+4W_Eme;b#;EiphS@~@AGH8g|<#kKEw?By+{wQf(HX{ zPc~MJrBpJ?(=o4{m_z92!)e8d{k@(8op0B*pPB~x)D0FfALZM1-7^CNzYhxyakB@_ z<%!*)+<)T={QY14z3ki@98F>#3=}vQD!#f(_wmNIV>lYJk2{0J zQRVC`ilL)W)LVC_Yy55hvWc0YH5s|F!~7In&*@C4uQ~6GNO1GMIAK{=pOM#N*LCQqIbeKu zT-A5bBFb=X*IE0vMx9U+M%#bMZr0VMVXrAyC>AS{3rJoF6x}8LofS5x)4~@y5_W<} zmah?1!q&5_Tgi1W@bl60xWClha4h!zQ~c*rxe7Bd?V z(S21OZ!}X3*T>~FRD~^k{vJw|8iM*E^1ffL->f@mI)oA7Tr^^Zxhd89#L7`{n;ct4 zO75)8e}p+U!Oq0ASQ(8QfT}KMY;9{K7Qo_Zp`C}OzCx8L_t>hY;!-xI_pZfn@FOp! zGQrQt>4f#0xW0sqK$ZC@4sfnC8h{%rSY~!qa&NdXEWl_zz75Z)F19@aZz#^!A?Pw5 z**V^?%SuB1OONWa$*cuC-=oJ-8qa0Fv3J|eW!zO?@gmEk5t{$gbP3*o!7U#^pn zWtN5k#;hO?uLhNAIWWj&Xo)yN4+b~55cD@6m8q2z03vT@NTdBc;Q#Yd;cy`D{Rfe_ z?m+j^k-Z=S-ks5aRY$`h{rd82i=J9X$}FNC2&3%;2-M%QeT&1UHpo|KL8pG7eTo6& zvLY_4uOAMnm1{I-kpic#%|9(M-#_DZ*m~pEZmb1Ah)gZif$zb31u(gY>U>=<4 zHy(p^E?sF!Rcm_!xHsbC{Q_o)%QI?qqeudp17qMHG2l=%r@{NSU5c=AWT5p3wZ8Zf z2Vg@bjv5s5L48+z-JQ15bgSSg!?0kP&+ogvj*Sh60h1XYBIPh*Ffy2>I=FvyF`U(O zc%FW_GmsvsBs@gO6cv$^$WgG!%BDH)#Yl+ayh8+SYCiuW_jUgtM&2np7p95UjkRLi zZ*1GPZ6_yTE}^Qo;nN_#dXC z?l6rgB&-Z!+-x&+_)8b}H7_#qBra>h7<-SdkAJrM@*li=q=KRSTrhzy|191abh%!r zbsjfw+vf>5AP|>c3fj^kaPZ9gV2oQfhRJY4-E1K=@f1wbTAH#-2@#{bpbL=^)ZPUQ zY~8X%L#U#3CB`zb(`~e!Pz^ESd%@Wh{46Suv$LMd@USFsvh?qc&p4Z$qs45&UOl5S6-IxOkJ`qs5aNj zM!qqI8b^+@%8@mRN705JH(amt=u>V}=m*sD-KWnK_Q6N{Y3%^xFT%XL?;r*HXzd81 zym*MWcGv9p!P$8!lDO>-Ik}wsSACQoVxJnS&!?qy#Fuzeyv8cC)Bxr(Yc+i{m?)e% zGN8z4rGNvr%yrxl6_D)nBUCXYvt<-gJ9 zL+8^$=K+kLg-?OzVJzB^;g2OTcj1yL6aqQ5jfxgld4kl1{EQ%WIrwc#sXV0XUKr8P z?t#D;ldcy6`78xj$4{PC{PA$-G>fRpi|Inw=i0A!{){G`+#fDUjRIQ5TSbiu0>)Ru zhWU?(gqA-@YmZE)po3YE)-Ma^bv~Z^GE$;Ow*OXN^!IQ(vcDNCqI%}~`Aeg^)nps> zwJA)SoY#{bQ_I$^^?5@H5sbrC5Nv#;YeCi!J5;|+ZL6w}t}3kl+WH7Yde0jBk}xj2 zHAA>svsZcHqc;hH>lG7=jn!bgJ3qbHcpsD1sLN|P1GTiWk4PrbMb*0r%qz}ktHP#y zb^>+v4&lJ(JA9WK9=qV?mE#5L?k_2bWD|G6X)LC#E4RiQKmGy7E z3rjNe9<*6~4yLU$5!!BwvNAKQSsTDG8)Pm&tv(e&R z9ws~ce{Src&Hj4qmGWq~2;~#T@!>f{JI9TFJNlgjsk_zA4IIO#bSEXB*D6r?W_)ba zu1|OBDvOUo>Utjn7a3K8=(10{PX7~zruN5piGFKi(zTpYTMm*wt8Cu}44O-|Xtiwb zo1ArM?bZ-`NUJ-Ko!K(H*sUg|r#h3bm4%?)D9CzyD586tLEnSQAofq_2E)8W+Ka%EpMyfYP#-fSl+;ee z*w6$zIKu^@2*vJ3z@dS?i>=EgwD~Ve`(a&CqyH3w?fzw%M3#Rsf#lL+8Rno?p#Bqf zDToo8l66-X)i>;yq~tDS{txK#-%53r+w$7-pU8{f{0}=7yK?vmqMG#Y zC1fIUf;GiWWgtIsKtG<$l~u*I;%g4!AcDdi8YG+M1{YEgn*MFi+^*t2aw6jM2md*V z5ofSgs)Lvp()Ft?coQg2C;V+3$k3ToSiJFh46R^CK~s6W{xO}u@drFoVTMo65I49? z5u1??+PGX@#dWEv_#<@!PAVSgH4wY+NMO3So>WJMERpVMLoh1695Jta%d~QiR5U?s zI+A29n`VuX4VhSi!Q9wbdJEoNaLf-n`*)ml^NEb;3?yn8ym1o%Ei_A<iSTHZeVDpywjQhW$iHnGYv$@Ix zRy(KQ>b!)nY#!sBaVzk`Kt!n_HblqAe}I$a(C?uD2EopmZt*#{TgMNHMqh)C1NAKieqvycuN zp1L)DKT!S+;w~u)cBqpghidnHJ@5+bRq*9w!(k5HY-5o->2hduk^@v3G?@S7+fOaK zz>8RJb?|V8kYx(^yzC3=v{-z0Kb;j-0`V|`AS2R{Q)H7fw%CnyvmPMsZc+w1hPvw zYDcM~-0^=+lOi*d#)~HZ>@AaX0=}L?Z_MROW+vV#u2rSy=F<0tclrIC zzua5+dsV#o{OB+aP%c7L!-+7)d?K-lhl_{i+Ys02oy~grX@0;fmA^(wTf)YG_LHG*l4?O7uYJt# zTKVYrx&9j=`{n21s=+9~QrTd{rEQ_j7Z+t;U_VlJ`)u(Ao}!axQNQ;LDjE?eC0bG- zL0mys?riUR{O(yRA~{zAq*X=APJAy5FzG1IPepGqoqGx}^!ix-)QicZ=SBM4QFh~I zQ8A=yno*ZYN6Io|0=Ut=$iLU6k2NhSn{C@0}Lxcw|B4(fiC3`Xj5hE|h z2Npx{ZvZtI$S)}j+u`4AwU8$RRwe~M*yckb#k;MC4{|OH9!12`#U#mtBt3+wn-C_< zfJz(}c<3G;(@3k}fi4D%x~MVVO+-rlZt{^9i{Bbc94I18K_W$2K4Op{pLB;!GRys> zVX)MG3*w^_NmdC>l);BPvSlVv=feW9-Z^Yh8BDG2iOr8Vrp74Fe#4=_;%9uchMw75p=D)E2 z#W)>6#ea7Hzlo1T_B0I=Gk-wL!IJR7O1CmSH!088d<ab1eZc5=q0_Lg)%cL;k_Jl%K6Bk0>&5Lo z$W~{8)%*C7qAR{lJeeBl5)=ipG}Hb&#OptaA27*hrF->9>Ze1Rz+?GJKB%sfQ^yuI zjSF^ld;q`yINgjNQb!L}NQ{mnqa0Dk@d#EJy;}yv9t=aeI6HU}cF$3K&oRmVT(SCD zK6o;2e<5t2OLL8+jb{G(aSr*3z-X-zk?qRIaIA;v(MBK~xEeP-eQIg~&Zk(#Hd^x? zh%FGwJiD&YGa_fMW(~u0h$(eHsDoHxo zP)I6Qtb%W?4ns^tYl2)1tU&#~TGnX}1o9`o6Sh@J4&Rjl!_)KQ@^7d{k9+T4lwIE4 z)7No?Ke>K*Nb6K^ef`~&AYEz}IsexX1~sNv`<@JVZ(B(QPWH4WgJFYSm+wO>Ff<9= zqK=M8k?|lFdNNng5Wx=5`$fq9pV`2Fp7$r)#1n(Lo8UOC)GzCqC0WbDx-JKoqRFf4n1xxFh7hY6o`YkUQlxX%&yS0+qb>(^ryk%UG@{!iiaME>} znI=tf3S601l2UA5@}`2{4l|9H_lbGbXL>VV!P&Fu3o0{W%yd)3jwgiXhv^4Y6YpqO z!HayH&_Rnk9M^eCDj|3h%W%b3kV-C~6&nF6k`H=diIw;gstGF30+m$z&`K#QnPK72 z))_Lxm1u_}dMAJB^5+{0NDupUM@GO|xY&vfgx;fca}xxlm}zZP8qJT!9s-m>%kg+f zdE8%Elk=@_C6BSGw5j-&+b@?EI3>0AIhDY+Jw=aiE4;VU*gj(~W+93kPI}9~*rs`E z)y<;}sU$Jc>2x5@z*&%S?!p32k$FN8kre1M_W3|Q)B{cU{jJz=0+bXe z$Dt*dJR+dD%bEz<_tjU0IJ4nB8?Y#*$kUCA(w5Q1X*|p+3`IEdbVu2#GrLO~Z_3-& zj>l>`&zeA800^kxT%zkc%+HGA7D3fH;rZr&++h+%%gN~$lBF%2t=lkB>6FlHu~ypv z@GM28L*Avh`7oi?$#;(ue?c*Sqx}ONRl*5G*ZrM?LtBmXMSYITZ$Y1dUB+Ej)d@ae zLrVCbss4|Ot)o1+yMo~OQ>g8>+I`tY;KEA2v~Nu*7f_!{>Ej@TmJFd3pa~#Gv;gSD z;hWVy_VT`D865nYUbPwJ)~G5EvxHLG;g1Sq&Q?oT@R1*ytBiCA1cB$>Qi?H90a7v# z!t=Sx76x_z>rHdO0ATcqaV_w6qyRf9I@A^-cm?VEr3H%*Ewk9%i@B=esBYZwN#L+h zDIL||{mNW5xC|jFF~Zw;;qv5zY#1W*Nf(%y*zuJjfg(I7A%Y+=GK(u9?AiJf3rsze{|2s~|qK_klFht~mAWEn2X35WEV)O%DiEaq+T z0O&Qxe*B(2=DqxqRK%f^h;-$!$T&7@;KmcOGe4 z_I_~fFh&u}biuXQQxpmZw8Yk7J`5a_a}|F0wf2SFy%HZD79I!#E(vszbPzLfL92`J zslMtb#Xb{q77&?epv$iu&R~2|dsramqWEJm|A>e>HgRASlD;EE21%4JhrSt%S5!uf zq;qk>sXr?rRRvH-6gnA_YK%`oFBWQy2~+}c~pLo!n>1mKL5`x!GmP2#B$%E?K|3Ix-vg$e^SMxMdxyaDMsqB62Z zwO)iqwvp*v0c-0Axx}T$2R>Lv*_MC+HG{3HAoXHiPw~X?R!~0hCI&9duz>FK06S9Y zCgrUGye$*I9jt>BxxEuJ2b|nC9SqO=nQVKMU~SC(69jYKRb~u_MRI#??K9;qm8l{6 zcKF_4vC+`ziW8Erx4IJJfsNgry5G}V2P!folrHhNXZ=5uq;9`qy+3*Mq`tf4OaiI8 zXIl%SK$$pX|9*FWG54$!WP&T_3~med(s?die->X2J!I+genJJRfao8I@~LR{{PCj` z!Yzu8;ZX2$6ASV%7WV4)CNiqmk6o09J}RrDOmQxU%e9D# z7Sv#z*GEm0`Odjn4kxk?)G0?f)`r86^H#ldo~tkh5OsKS{)aR6lMF)plaBJw!H7|YlxL&}4 zG;P<-R!wy)rr^$IGC|T3MPEXeUu1_zb%`*+m!k$T>a6kIRCBh)rqO(GsSy2O>9FJA zx&*`efp(5dY}X5={WvTQvIihG6Pe!1Lv(Yz7wwb>(tMxzdpOzyKtqRAvKOZUc;H}!K3Y;*>kY{xRaBQ50qJqg9P+J?{HuAV#bA-R+1S(w zwzVNKJ80pvzO)xMTepvmbZO4XR{jk6l4OR^w*~yJwb18pa4$?s1CD>+|BrBB26QDz|3hM$+~lhfu=qlTm61--qUpLu*nFTdk1ER@&kV+pB<1! zC3TZl``^;_qW;(hV5uC(KAs5~R5WtYFoROH^X2IfbY!DpLwWSJHHY&&g_9<2qh8A% z98RtU?ZD?niiYdy?6;b`6ieaA8B`>wBi`ezfUU&oWUk8yGZA^+mM$i?sw;|U?6ME{ z3oc}qIg8jssch`bC$y)qLiTPLe6)&zv zEOP>e9#6Sko9qN)B6F`PF;Kf`Sd+gJrI@WwMD!T&$%EYK#3=wAuAtEF_!RrmEI^yV zXUI}F))4BMuO^E_f;j>ToLn@#b&KPX;6)Ybh!(7 z%B*_UUqVKl=00iQ0AVd0B)G>Gk0Mt*qwnScU65u`C^Ik-q_-MIyY8^eU;6vmL@j3n z5q8g8x%2psSu?f=)$^+&akx5i#LgE8jYXLd1{$v*QIag!PzHl)WPvZ zsIu(3DoU0QYFThxQ$14rWBdneWFPafps2ed>S=-s(xx;$FL-WfuNOMA47&g^Y9;&WU8SA=HI;vItkXcfO4v>eNdpGe@3_d`_pC0D^2d=+L+(tEjU$xka z3D3WNq50VtYj#s>h-BQt+de)>&D?zmP(jm`L@o|$xbVmwd*Y*r=8l}DN6Ka!0t#%$7kqyeyQqlAVI4_l+N$_15g5-2MBhwy?wfNhR+dN@PQTN<| z)x*>_IpE3SP(72*F&^x%?#7JYrOz<_b#4tv6dw6poR!6~13aD84&r9#$`GO2>7i6s zt!?k(Ufk!zmJZ4$?oY1A&+K!oKn|BVncPis&MUv;>Be@ob-Yk{MZzUbpM>Vuz94S( zqywgil${_u`~NWIh-T8Cu@ByZEYRSFr<8R9OTnXk!zc1hW*QDMmB4Bo7-9o%onEW& zt=bjzPljzh6H2AA8R*uMdWu*nXF+9Y6-)(A3n!pb0ta2(CM8y7 z$IIuHVb~@3&q99cBb+{E0Nq~7SC@^Jlcqg-ej_OS^LK}7iJC#IwZ!9Y=c!jSrXqSJ zO|@6d%+&>~<8+pTFxCHL-DKi*dUeZgxaxM?nCECrCxA!m6q35;(@)^#E;~Dl|58cz zHqMw%Gb%<;qK$hxb-DE8t0WRNxo@5M4i;y@7bsTIIML;Vg<#4CHjVcJLs*i-{traw zVEX?fIuj@R|1+XbYi~M|v?BSes^8HTMS>stN~S0uUwt&sC#7u0WZQ3SqW4*6Z_nn6=7M9Tk>1#gP`aJ(ZfK zp8vph@eR-f^yg zqhTeUzbQ>r?u6wWO_lT5s+Iw;l$Qw*QFWJ_rW3Umhp(-QF*>6MU381dV?x= zMY9~vd`dE)h~6wX-JNgbArh|{6BOv#;<{qvM*^##h=_rfi(Km%&8Ut#I6(%wvt~(} zTFhIvcPPp+WtZbNTJL6@pRB~}nkdTZ3*aLuTz$(V^cYj)fmI)$lG|geKK{YrsQ$E7 zG$?wxU<#ze??4@}fAmUN8aHt7kchN`$zGqmSwRtt;g(0!k&O-^=;%g}+`ouj->>M5 zvFB*cxpJq{{*19AQhW99MM~D@Iiu6!J?`X!|EU2}R&r_JIH0L5v&#JWhgfFy9ol0x5_(26RS6v9uu0S4>)TuY~O3Nnz5&we~m1a;6y9a@L zFrC-pZpm|mr@`~Or}c4mKt$SQ0oSEHV;~K*z^`0-vk|`e4|*QkZA!q9^a{N4Z{a=( z!jo2v{Bpcx2qqRmI;(sTsQgK0sF%UvSQFq;iNLE7Qf4pVIEjQsT`bco2`ctl#Gb#M z`wmLu_mBM76gMigXIQEFlw!k;uK^mvx(Y;6b2KvfY+(x;$bYE_;4k zn-NZ`=mjncyCW~y$6$a4Ib&byk5ME+q^K>v z;ap=~M3rI=!r{4EMYc8O;TPm%Vvule?pSs)F?y6O!j=^Ph9>Gv0K)t@0bjuudT#xU zFL87A8j=;n;YJw*71Zt1_>uq}8x?~g3i~gWziulqaNiE0KiD=KC=~A@$Pw!ck4U8Z z(UMp8HKK6b7Eu{~QHM>Mv5_I@F&;$js6fM;LQub9k)s8D`G7lR?_2u*3{lD$og$mRF^#{emWZ zCqJ5T7_^q>kkgKY{;-kKetm!02#L8~F{Lm609EBR?jzcNtcNb$u3p!e8+pJdvK}&d z$u<&~$8NAbW@`7rF}SfALBhLGoEl_okQ7d=O&(dLpnq+#*-o*bqpdP@o!q#`q@GRacdb^^a1tINzj;xYrmiOXk8pR8JRaOZSXFYeQoX4e z&DMLVt=?Rh=ZKB`;B?I?J-nadCBj}qCbt$#~Wm(1>Dy@k4X!E()zDO_+CWLf^jJy=>$W5U%N z?Gd!sHDc-=^Pt)Zso=@bW}PF}Q`j_b^J=mz^-#rl{gp?sd{p7yzfnsIP+!|=IVQu! zv4u&GpfobkQD_)pLvNsZceb2vsW%fAT)q(r)5MN;$Rm>--lPPKP;xb5O$btY9JC8V zL4**cqx2#p9?|*H&)G!M{M!cLX?715`fTRbigFpR4hMV?B^FlsISm>^C&W2C9#p%_ z-$M5kT62!_Xcl#qS$l{wV~Dag%xGoLhKccHDa%b0@l)&k#rW}%*BPR*DYQHYuY>)s zrxJ?~u1WPd1;yEf75W_$>+asn7%v{CBA+Uoz4e)ALwea}Zcnu&?G7)A%gOI~&b%vq zGRGjZQ7aJip+ss#cm14VNZV(8F1H{(53|AtW#IMv2!!NZ1}qUr>{NNq9H&m|KrnNY zr+(+38KQ$2&-%+jt{+bCM9Q(#;VNKH&x~OKvr3K9$<(N;uh#*Xi`p{wD)XX4+-~-K>1lmG_+ef+Q4F$*{zGCqV2vJ``X!QH~6y83- zG^ng+2K-fFV==-o=OTc&rP*<@pn?`;%?OX&T!bFyJmSm}!s6ZS&uek_jS0GMy@oBR5;aA3h&*egMhY>*EdqIq3t18PXi8)Ynd zC-vGuZtZj4I%&@eVgR@XVlNrR`GpG7I@~(dA197> zjeQb!9Ov&gdE$MuRblzXruWdE1m_~#%T#bb8PB57_HI27t&8G(J&IKQ*T0aeyna@T z+`mABu_T8#27+Kpd7uTp2xe7p8UB~pXZv59X&IUSU&Vfnwx-h|N1|_?et|ew;nc7s z5MBe0;_cTa6J?*^NT%Sw$_jNgwtYI>?g16o=t^t6fMnGtYhtTgZ8I9Hqbrzq4( z&n7B2)9&+8;uHcpx*9~u_K9DmBkpJH^Dg(t{ozv#I^erzu4V4OLpDKqxF@C$v2Yp93fC++&{zXS-zb$#^6<{~AKt0l^ z#2{Mw7eFu5I;m`g1a}fkWP4NynxU8KQ6+33a(-5Jx2aG`KfwLMgA7D{g!*;1Y;BFO zZoA7;3Q?5=z%3XZUkFu@<`vNiQzhS!`_j6(bv@nCEo{Jh2CDb!ely9-m&`m@S@)n$ z>TXXla4c3J5W*M-_#{r1EYP`V$)SNDgr@qm zQ~6>nSVyA8$=ZAYU_EoZyA|jtKcJ_&9MK@d8`jRBGh7Xhh6syA=PN0bX5If(_+(n% zp)UkXuRl&Pab+?6mD0gZiJV&0f-lW|L0^E#KgkWFq6l^MlyX_o4puP_()H`6P8-ul zOjt3i^P<%gH#3H;?cc3COzH5R&f(r8=l6L2fI|>noyG9rCdV&nLd{+OOiglR%#isv zH#Dxl6NAotJ>SoqE-7?0e($h5-IlI;8f3fAEP+C%E8n7H*5S$`<#WKud2ntB{hn;J*zBWlm0eYZm&;r|cio~SiOKe} z%`zN;yDg;s$d(P~{c*UT>h^_F!Q2hwA+ap0tGoeIun1mxc5DjA!8L#*y}g3Py^mx#yY zoLNY#uB&(d#fb8Lb6OirImH%?p%zNH>*{tS+X!np&;l_FMzSd^D4b5$b06*Pqj+!8G(!9s{?W52IE$^4-kVEFoX;ku9# ztB9}^j{uA%*k<}|N6tgubRNWGoZZOeiHI@*8Qv!H;RT+R2fvtzc7)Q~SHac@t{pVq zD4zIT;43UlkaF{@rETlPhMOm>jFwTZ_ArC+A=VXl8P$an|X$sE0kp>3Ttj_(YG{ECIlO6(6t_#vTTgsvD(*iVTzaGxw zZ>$ecbc#jMO5usEdvWC>+Cph(^&{)-xJSHmzAfLd;Fey}VeOf76S7i<{Bqx9?OThN z#@)mam?5x(ePeS8u7Ovt6JL;|?XAKUPfdB{SP`MJB|Hz{F$+;m6~zlEaF~HJ)TX;X zUzyAvT@$;TC|ZGHi{B_zVJzPq9x{G#en3V&>@sNCFjy^YSNC^}Pi$|QbS{!YCKn%L zuUz;5HS~VUmoqEgDj{NKR5K;hrxBvL3Y(q#w9@+G6%#^~e6)~664Ou_@I2%YSarP; zoZ5YPn08A7?yLZeCl)!`4NAT`3UftyQJo;sN}1-99QZHX$(~yZTZ~P#-xIE=c%A{G zOw(1hCySqt!iEfTb#aR9??UN1KK~X{TQMk$XwL_JKplJhOM3;n$eU!~LcKgxCKPqG zly7EC5kr_$2$Hgt5lFN^ADE<=H_R#|BQxI)YJ;#$oN@%(Ajz;mfZfh;K*IE*RCoVu z{WW1K|I`|^A~+kIDQ{M&@nYX)g(bx#$Ot#@$iNSN=mG3Yp%2~>)EjsX7kBV}ZB_~$ z6djHgvxS}n1oK^KTt7EiOLZ%t5%o6BK2B6+R`Er%ve_$}ndfM>GFbMk77zZ z0*Nih99t@Hn|b7kJ{?V^MOu^j-8{29PFev`3t=Q|(-$+@bXV6aW-M{?S!+P#A)}JT z19Cs(ia=nswLpWLqXUVrR<%DG+uNDw{tJPq^5gfaDGHvP>J>4U1Cz_nRK||K zW6I;Wf{5jNrUexSB5s{PHrhCP-JhPI{SquQ#&2KtI8k6{|8ZKFbBIR8zUP<`!X$`f(`l<{6JmzUEHmONKV zCMqbGMrvGXJ>)s2YxyrKr|d4_LJZodDkppBpuGbSIreAmpC}?goAZ=c%Xf7j{U9hLoI4@siO$J+5!vMywQuhx7LX^gWw^)sEUSt_>5h)yg0>5A!s+9(&u z(GPi!ZUWR7gmNi?semKn*Uj@Qf+cI>l{1^b1)%*M_49O3ldr%$EuA)x{`Q3LKcyN} zLSq$5Bi`WY8zAMC1{hSPc4CI#w{H2P9iy%Al{#@!9%fu^srXAcDwG@(%PNdxvldsm z9%FJTjuE>7F>A2*Cd+o8Kcm`NPz}`d^Rhf1i}&$x!Jf~UZJxS~=JH3=NFj#UcFW6- zDF%lZ81f%}70W+4d#!JN6*;n z=%el0o2L)8!^%RG*nM<#WE6UK^&17@w5s|{2Pq%LCRG|ab#_KMQ@j~Kn!G1EJsG z4;J4fM=`I&4(?0KVM0p{`2^h@Hwet(@8H98D?POZrtajXZmq2rls#-XHgGMH_pJhYJkr8AFF<$RfE{I1+;iD|+fotZ zJeq=G(Wx|*R5oELvB=ClrV=S5?tGAukgIPTS5QK~uJ%;9M$kUv)n5&y=%PUsl-*k0 zWNt-Pz}j4rKf7~2Z-|Sr!}%fg|Ea$fnhdP;J|7cv{1^;&t^>u=jpFdSl25VnVWT^v zW}9ivajL(uVZU)A}+5L}RWpN82<1K*?}V^7y_7y}F6|^^RxVKC;Ha>YT4zT-|pkYE#%xDYcP{ z8K*!%4Z?UU`2QpO2pj!-5!YkT41Eb?n!aQX6gz)ujh5&=OoumEh|UNccHdaZrShs# z7Mx(SDpC?-=WKR)y6~wIbGM$s{u7S^#HPNNTjP`Y1#8lbEd)|qQp2AVf)!uixc;c{ zk8CLLJ_w#SgR)K9T8N1B;4XsDSfX(D2QT+g5~y@k5l&;F@L+o2DC z=b7+>0o5QGDB@@?>e7py8v~{9kCs7*5;|_XAGX(hRU6Pfc%vu%fS`gu?W{3_ z|2@errZRK7&tm>z&RwZRCO+ifkBtjL!ELmsM6tis`G#;AAL&pCc?)=5J3+NsVll7V z=<&*%J(aenHYf<^WqAsU715JKgv4313n0f$X64k~zi%&topE+@Jz2bOeWJi=eFOyV zNk2_;G;_|KHW>VG?FDi4D^6KZL@o$SuBJ9HoCt3&X#+*_jw`9VQin=6*2BZ5@h+3* z1?K?~{-Gl*x4%ahrL}KCeRVn$nYM9!?J2pFLFR1aF@yA?Wt&Y#`-}0wv>y{N2Feh) z0lHQ`{KRSkEMT&aHGF?dV3-)3Vp#}%;_{tkJoT{isnya?2`fVivHOB4+*zxv7sLP~ zzA6V;M=-f7Yy5*>5pUK|{tv)Zx%$F1I+d}xVQ{4o{~l-Q=Ir`Hm(m~OiUK$6J^%Rm zM);!mOT?tbX&h)&0<(-CnweZQV#-cNNlT^B3k$?SefKK#=KIQh<2|E2>ns2MmVb+o zz&-aW=Zy*~`u_(9(rKJJal5ShmpE32yb>t~bLLi^=QiGdL~UegeOzb}AB=P0QW1`N zf`|^bRwYGA(4M1whoq%3sNThFj<#?FAn@BBueOA#^^#UaC2Tl@;h?_t}(v&7ED(oilhoD4F~&uh6$wLAPi~F)2NVSpVJ7C@)PDO`wjPe-b;wPNtD3}g%_dK z7#E%G4_Nqq^Hf0lFbOyJJhzmXR}uB`^uYh@PFYaFK&!{E=`Q4>-m_iWoo!J0X8frx zCZH`fmOLaU7~qL3zCYxYaD6*mOeog2ny(beFM5U5MQB-NF3m9&TVXzgY$2621H2vJ z{G5HKgM+1dV@CLje`kMCAng~$_>`r7cJ6fK>mr15tep^iI~31w(B0ATToaQXL?`1< z9~e^#s)bk_Ng{ng{~fXkhU|-n6tOSCc*|3y`$?MiVoI?^t+E}@&P?E}Svz?#dn9)e z-yC|FZQY-c$^)pg^kQ|z1BzH3zEeNCe7D1F=*Oo7|6wcDm=&AtCKR7ZRzgdk)ivA*}W`or63{(pv zuk3jinn!<<5dYxdi;C%jO)4M5Zs29wZt%GEyuQ;S`wck9i9pxs&Ckh$PL4w#Odw%N>!j9b!6=&LRNjon~h>M3)`eUIGeRr+L>j%nUp@R6jhIHLVWQ0^m6 z8`mr7P?3-w6svS8A(owLEv^xDZ@bwV-dUMoM1ufIt^Kn^bmJls43)s~$Fc%1s8sg$ z8;_hGiHn=R%x;y$XOl!smT@2&vB+!)v+!@FIR06{hbsb%SF;r^Wtw5|m&!5n`oM+C zMLOnhO1+HawOO!jsP(`k{@2oy+LAAptPRI{AHxaYT!T>eq1ZS;f&m#`WMC0D(SuNM zScZG(p8Dcf=6ko?Ck&P5vO6w=!~AB_5J0f12e;%sbv9YVDcT#`jO(k5&RU3TiriXc zZOkk+0$taHF7tTzJjZ??ux)@kaGT^E%az-RDiUeFbTTiL_x1OuKZ}Rx%#STuP8D`t z3FZ8UXl>vZ6p4rx>wmaood4r0oP~+w|Jfbe*49qi{EvA5&ujIvm7u3?9PJdy@x*S& z^+-2>U82+jP+~e(vP2++L|n3do|EMkR6O&Rp;Cp9LX+JYpHGj|HGahUx6D+JAK>7& zu8#C8G9w#2ZH*;|^9`;dla{^i=YgG3yMGOVIpK_D3g7Ys!aR!f-?=sK=i5cEm$Zf{ z3dvE_ipAI+o8-S9g9!FKz3&@tYBM6q9Ofo@H)cxHgPOp93nCTkWy@FekNjI(rAEH_ zj`GL0YbeE;e!-!Ibga7qlocwE(G!sqb?mJ>S?~HGXzZ;@UfKAta=}bR4}WAFY=6Ao z7~#dUN02BCMLrmMkLg=kI*S{v)b6*Ahri$_RA&FKrFP`^ls*XXdR1bmq%MJ8N*X7y zcs#bs^LKf^TEY}XVeyQNWsp9fm=-<+vSKq;O9nU7qoD008`{(6Srllhy#$NS53=^Q zFKdPV-yc2vB*ldI+9HUIkCe3}aNff{+85LiK9{BP(2Gy1OKHY=E~F$&7(6e6-{O@) zE9qVjA^+E%?;sF%325P zz^HfjP=Rrg3wu1MWXWj9GQxPIz_}m+MN23UNtbz`doVB<^(5@gz37S(QEM!d0wVu? zb+>LUj~h`1l{g`3zzx;nPc0k;S0J+eYyQ1$Y9M1{qk6yqzM5)bS#8Pa(%iP-6!wj~YA5*`F;EG-?3 zB9brV%kpQ5Tted`F7idrjB%q39W zL;t8KTIRW2TUhPwHEBf{dCjg#O&+mv2V{G+WSvY7+EIq{0OJd42TlJ}%%S zzCcL?+$5}Y9&uWJ5D4vU%z>pi0v+K;tn^^HqK09_vBiA^jCR|*7a7G;D z(`8IXc6%kGU`%dG#;blDB$z+|ct4yGF)Gq>rC+1L0%?=C5&<GbU;#2RJ< zn3c)W`T&w6;#zOuz|w~2J@tZfnYohWH94EUVE>HS(>maRokDV?Ku^&_-c;_J$9(+> z3_y46^#4)z4zR*(O}pT>ZQHhO+qP}n=DTg%w(WP@w(WbqFLQHeGAEgTlGUAbrB-#V zJWuzk?$y;bb;7(qc+7(&&wCd{Uqo$v{he&wy>#!L6ugbrfjPU|*Pb(-nLMRyrtFS|0$7e0gjXcAOoz;=XuUMv4svg^@b z4*-JUsfEuUs7U!u>q&6=9LC1Zszyk~lMr?ch4FF`QWt`P+aFfK!v6GjcPC)P|NCTS zGw=5l*ZsQEN7GJyq?e-2bHw*O&8A8F^w-lF`ou-Ay7AWT?H-Ho#OZkq8^nqcY(xMp z5vy2)Oevd{kqmJompiFJ;r0AZC9kRuxyvgRY`3Opmhsr}Mu;50Wu=#B&FAl2H& zHdL}$aj3?<{q(js`HzW4F_QTX7Niio@_=D5$QaHZ@4;1n1`i7BDpAU=0d)@l&fN@n z+~4DmHf+?)lb?d~IX0E0{3?((s0N+;5b+PIfmmHtx|R1Z{_%s-_>rH$NVZ1#W-zsP zg8*O=S|~Nm+8+*DF*FW9Bal38N4CQt0R1jP_3%wBWF%}0N?Ie5wzb;QH7J=>K~@m4 zT3R@7K6S+Pt|=<7`|QBhV#udyf}{&N@1|!i@T$_-Eb~oN^$1cGVX?>A-acmt6uZ%! z%ON*i8^A(F^OT1KsrC#x-0@icIZX1Q1m+=G)N#x;4Xvrow?c*68}?-sXM)YDVgTcBTz6)D2QnFe+x2tBa=K05W`h|?+Y}D zDB%$a(#R+y#sKm^ab$E`=M;5X;{IAW2)S;K@IzyC>Q;LqYrQ;PSB~FI*|wg@yX6xb z2oB87PSO6w7S5c1S&U`(kJXm&RFZ@_!bRYA<9g8dY*n`(3nBnyF+7He$Cw@KmamME zQC?eK(wfa-Y=&YH(RNRjMNOy6s~jjz9))upgikmwEA65^03vdSiA*)VYl&J!bS8Ll6J+E{M) zmz#&0BnJ$u?zYJYx=a?FsItO)LI!l%Tngz|8 z_OzyWsPbL6LoXQ~RME7q<3JXD?JdeRRD2Ehh4#L&Awy%vNd3n&&t83IHI~=tWT3io z(qkZBke4j3kcXnkX09>hne152u%*nxBZ6pge&cq3u}w@v!f7yJxxdCW5=73Dxv}cd z+H)41I0L(Ymr&w&3&dd#45(}qbKq9!Cb_%y;Eh8|X20#so&}K{ZIPBCB0rjqq zy`(~6tsW33B!2Tz0KN)wWj=b;URn>;W-@$0u{D$7E1~Ar&T-bbVS|6zKB0|=dUH%w>_~!()Y0I=#HK;IanU3Aj(XGm zYw5~qaZqS-Lvszj?~4WT$Rh#~fDw}U`rQIJW@{Y*+{WMnwWcJ~;qY+ow%7Of<}ihy z73n}}`LWE2sXbrC@hU5xgpMT6s6cFpU;vntp#5%x^oL16!Lr-XW+faqossF+gH&0b z%gq?6M5|&4N3Ab6!FPZ2jLEt&pO_cvJHoQbZ&vAu?86-3(jU!dGACwU7}4wZj~zKd zv0oo8Sqr;BC+<-|n%z&B5)0*;UxbUE8nu*IzEl{hUzLxm#tBc&PMWG)-XnlDw+dJ- zMj7qE*9woD`sncoI@un4uc2#%3heB|TJh>AQ;2@Ytk_68+=-<~C=dph0RQQ`iA%LG zkt+A?YqE5O2<9r_$5u&%&d9J)K1)aYD0JRaHJY_gXt0W?CGdHpzv4;(J}Vlm2i?N153#GW&5aSx>l?GzArpSDA43(nE6Auw3SD4E!1p zAGD27x6{D6_99lf=~ok|zwW``F}wE);+Aai_?W^?a^Oyz6cCC1==4W_GS}_v4)*9C z8FG~{^b+|F_r1Yw!>PoDlv7}1S{M2@4ouptgksy?6%MEfVX4~d#!fq&pI(x9=$53ITsx@iKlQ7O==hR{~S_gGf#xHURkUNVu%*Vglh`VbL) zq3f)-MxF>9X3;5SaL<(U=QbL7-V!3H*mqX9MMjlqPrNJv`3!w$Xni=KY_z3$(VYIk zrSSeueN9zT{CC-@o;p1=Qa5l!PS;!+f-U5ENS3@PDPe6k*uIhIptWl9lWH>`xBu5% zvv$>IVX%#yVV|5Kn7{AT3vj_Cu_n5YU6a|($qXcwG{U$$prN&cHwXnhh|Aq%2cC5# zb#aWFLRz>A?R8j=}*wi!UHEg z7uIek4kvWcG_<~p(7{0Ro>n>>-+iGm^5xfkG+b`|Zy~dzlTEsuf8#fwp8QcmxKU!S zBQKHi_z&|e<~)f;xSTUrW3OdC^wSv35I6@^Uk+zWmtZ@+m(xYbP!YH@y0FA*6O+fU zGcih ziw6P_%MV|?9Hy{T90bMt-rAnE%i7E_8cq-0g>M>Wzkv+h$H0R&j^B6f{mbin;bfF) z+i>rsk&9|xT=6-p_JeV@%1n=W$Dy7SP^Y^1`W0NNs-Clg$Rpey*b8?rL4f2)m@Fbj z#tX0;RXZefdc1XIlVEpgA7umYIN#uEGLgxhKUv8)174eG#<+T7I$3-m%?z-bu-e~* zYg*1M<%qu@2*E5ChNWZK>n~1%BbeNR6*4?jA~7?#+r3*`wU1pK(^$celiRkcTl_3{ ziyx>EUOLH(ZFam%+ZlVIM}LlMz8^{NRNla03EVSp6W;46W(<(l2K@mL9=~+{?On*f zW$rUF{*TiFu64GPiCYl;cl!q8n^4uxK8aO50pf}XbS{9h4ge`8tv_FM%)Mi-KMUUu zmoD&TCaE_)c#@9@x}V04E0&kG`)*b=nDDpb-_D|`cXarBdOqGN7>EZn_XK0?^kceB zy-a}32SXRGU7mbdYtjziFTm}FpJV^@V%eKDAXZBDXiSrJt*6|qQg`<3=tNic_6BMv zue)j6pRw7C)sn<-T-#41dmi6LIfqM#RN z*WJ|FrzkDM>zplnAo8DCY18iv*O_B~ICWv)o@{RWG=4{Lbq9dQbs{grU_@Tz+R>X& zc|HDYR9z5bMNmzy&G}oB`@DQfhao{t1vP#?_;TyjqBCDWP-zAK!8@r*3s=Dga&A<; z_Tlo=T3xy|b8om^K5bLy*N1#diXWEH8faG`wnhy`Og>(I<$qW}>HV}C&VcfJLi45* zkFrdNiAO84ultdtdl}DFW3~lqJkXops$;u_m$@KSX4phX_8Q7&{Y>83mYtGBRiCaD z%vEt5Yv0Us_8oT>zUiO`Pr-Ups#00tTZl)F%Fl0GRIitNZS$8xD>e>KF1h=Qa8&tSnoE8fm~s)rA-tyU10 zsT6TRZsR}y_yF5jZWXk#VUM5{z0c)zPMmnn9t9D|4wAU|ONr+LvV?LdC{Wtbo-K$3 z@TO7UKXH$Uj(`_8IUg*JBy3?;bJ<_mOl<;z-ybLnq6h>J;yznNsXOZ8DV64RyXf*B zN5|Z`wQx+{*B>K#(DNDV9O)c)5p%KpI-vKOBNH&DafXIV zcu2OMcMhhlq?Q^796yAlPqj2jp-bYn(mVs)F0nUe1#TfWSkw-Tv~he%#B6@YD66;T zeimxe5$W0yspx=&fiWI`9{2Tza(ri*Rwj%OwD78)cht?rCetSsPlIY@T36nF^YRHCC@^7T_O_K?ZRHx^d92{ zM7$caxyDT4a^q*svI!|(b?La(I&TSq1e-sNsZ)zA;G#v94)@X*N(DI_1+}33Q3wD{=Dz%6hLr1K8!h z0I!$}z_L3I(SAyvmw|F{ceJ%1$W51D&ep#E7v|z!kuAxuBLtTGYxjj6ptOBCRe$-U z=6$EciLWrHZ()b|4Egi$d0ZE?#e2VLkg4i?&omsO5p@Ve6znU=OLsP1OJ7(ul+L#2 z9AHb5W+Lp)Zrl}1|1Kih?Su;9b(S@Lb z_%eZfM>3u=r|&e;a?d%*CHh1@FhQ>* z7c3Ji45f~r;21TPV|gsq1)8y>mQhg`pRVHuDNuE)THlb)4l59_%Z`>vxKnV|oV%{zMC>Uu)kRt-u$o!?6O-u8QgO{%Q|0zIB6In($L{ zkILh2ll?yKB#bmF5xu=wXfOg&&BPLgok4hnCy`?n0e5XDF6h1KTMj09heI(^SYasn zMGl#s`#l(kJt*}2VyCbv?j)6johVzzR*6(4M5oj?5XTXTePL-YA=q_=4m zuojFv7(swi-T#R;iS8l<;fUs6SR%e{LEQX~lq}PU}kBZd(iabm1kFmf!e~_P|edaxLc8)UE#p%i2@7i1Cr{bnS< zz!OwJH$|P>I6#7MYZI^(!LN!Z1fcLW82p6C3((DLSIHvpR9{nh;Kf6kUws}8}Bq#6E)zaBCRz*i?ofy~BVqNjNMMw`uf zfoz1r-m$hkJ9{i++qR-id{zasbE;OOiaXdstaJoZ(QzT=X5O6F%$r0dJ-x~{eLK5U z7nXv+vpBCzU1nA9KEw9D1kdw=hT(D--C8@I;z=?bTZ5MduRwvm=;{V!Qf-E~th*i^idB#7|s+BV^rt=%_daaCdAHcpWv$ zyVRscE<7Am;R`i*#HfF>;--aVg02x0Q5V^pm4=uhTeEf!XP=q>?r55qY@nus_B>d^ z<4M9$rXvR1#G^6k(Zd*9nm3Qpx#{;kc&GKRQ@8tG>H<-B&{ zrAaDoF&C_8ih`mAuT=XWr~r=^gNUc}0T2cs2%u__}7Uj0G7RdZNE#df{ z=cU7i_ZvAnoRdU85}!qB`k6;U{^LdUGj2+dEIG3HTMV&rlm-?E^b6Zfp!4TnD(^6` z{7XJED+kB_I7I<--Dzv|K1V;09NfjSpu>SPRYj4Abcr;nvDvLSNu`pI;fQDu$Qq|G zqt5;H$pBp=NJ=dg9Ld>Des_%NcExySiuj~=?vJ><{`lWRsst*eorh8BLIF?3Ng%wk z9i~^lw~M!>hqN+!4<0@;6#@+?fvd#Wb1g1bP%_VcA-?R%ICLgB-^VgNdnw^$S!Mk+ zGv3abU>2C`Yf%g^q6f49YPfA}AiwTCosb{M?YfJ~>!|pcDagCLICMf|zr@SD*qdR> zAbu`wyl=;lIzzly5@3-EbWuZu$l<9KaeO{T`tTlo9<*_m`=K2%|ozKOsIRaEg1gQJp0V+*|uzQqCu=al61hJD4G_Pz>O6d;Ma)o|@ z_9g@ec2^$V+9P&#u)Iuj~j(vG%0CWusIsgkas`NBA{A2Ie$o)bPNte@lq zxgF4OsFdLimG&*bfKDiZGGnjo_^&Baz!I-?2P zFruXr(f;qp$v^-N0pfYyf|F9ZsSP3eN^No;0hCWU^qZ*0Vnw@+YRcMp^f;F)isSEG zEnbooxTAhT>$N}9u@y{*e`f`^5-ns}z+FWA3?law6DA`l9KC+0xBkXvAh&}YMlTI5 zOBesh>YGXg`%*nJ>n?o6K{EjArIEFDAKuK=YoV>i+TB}ZzQ*?Cjft=(?q zk)81PpB^i6A(Q{j8pq%(j{6tdN7WS@Sn04B-EMR!7Pjh=N=eU&%Bv zGtW1O6dEhiJh66j9sfCHxlqHRlnhk;h*sUwBp(t6WK_VZ1&yfIt8-0VDyFG+4?%Q- zYxatTx<={6rF$}!i{FUEToI(A*o_!NxV!rj2-;*17>-ICGejV_qSAkYlHzDm5Hf1+ zv3>f0L}W@;90aay4beF7rl6wE5+rbb5-vrs<7KqMBO(DM&o1wEt6{N#;kaUNE~!D< zUMdC~92);+f9qe_77?70B$EW1fNPT%=~)>c_0Fum8tyQg63YPiRN^!>UJ-x`Jdz;w z*>-={()O3M3OfPUd(UAZ&BbBm%kxcyFVH*+HkjoHw9uPBBHlMh9ih`1?7T-02v2G z)FcnlcHNyfnC&|J*zs@T|ECPv}E`5WpRs zY~qp6n#aim0vE14@QX5yhLgloaKw_0S{i5`rjYR!%vXk9Mm|^56N!@wVmydEDVi?? zrOKgLw-h}$SL@fs!|VC@^LV^`j0r*ym-bc0kHpdzHneJPKU*g^m)CVZl~=bzHz#NL zaJbZwa!q)*E?h>Q2e^3r_^5uz>v^~bPme*%+vW92>wN5efxub(%AA0GSK zgpH$@SX`8LeA3F8j&lHFjOS->Knb2^NREAIqP*PkzOsbhA9j#G8I?rlE(%BX9DAA8 z*W<-1ef0C;un+zhMrC${-#J$92-`CrqQF45OiR2a>mYK<9)$!dC18`)>D2_!$_P*4 zTdL(b6Ws-jYKcR__#_U*Fo+FXB+~nu8(iw<*bT?3VWA;DCQ`F_3$5=Lwk&hchkpzv zAcf#+n=#5rqS>{zKP-CnG!ELpV+CFfp}Cgr z`R^&>S*cxE;<6IVAk%YQzDl9%B(fochnbYIf#H=eprD|J<5?rF51hPvD()6$)e?&Z z1;!XSIolsfs3=;@s0z(1^z+psfxsxZ#E(ztcuaX&zYkMQ{mTdUXm^{m*~{;Ff7W$F z*NH_idkHg&%L6N!bsadk5BgTM_1C7(x*!t8!x-#+xD4D;ov!?CFtb=39rYsiVqvz- z!6-Wg&Qof9GzdK0+%hEJ`@<(nG?Gmtz;#Y&cBta5(W72h%XL1Q#F%(pzn^a*f5tra z_?KRjVgd12sy&#^@N}`iv9v|V6r{GVKtPZf^1?$o^IBT<9jyiTwOj!ca*BpU%^CN_ z%!vQoNL0bImz!-X(U38!6H5^odV^%UBw7X+bq@NT)E`MYD*u zJ47?)Q3Xb+K{8bbRc4lU?_Lb%js|_oE$>TG3+?nE>EcC4L`QTx&{J(8Ga`R$OIycU zhCAic)Ljj)EpKAT`UzGQ-A(*1GdgHS9NKe2x^%h=fwg2QuEvXBHwag>Z=!_W zWC^%d#Gc7LXC`qFW!|3s2$qQBLL|Vy`kLd#%SMRX5_GvqQU=FnR2Oy)`DR~5Mi6XQ z?=~P@Y{s|{uB^Sc{<*@+nQ5iT2Qa$0(?b%%>^uMTC+@|?aSkP2o&B+h$VbU~TJ zbC1cEqm zCgZ;an{1r_KMFZYJ8yCz^?s_Cza$h^(SXng9i61u98{MbRA}-ya;F{%L??{Ehqpe`knKK)U7>H0|7@>`7hZvacrkVv(-(if3VTlVe9rWfaK;i zI(*^g%xKD#@4X#c>akznXNODX-#!~Hm9mGj%{eL@m5j14Bbl=apqOXQZ0jaoPG2L) zazv9rA}`##fp@=S><3YdcBw)ace$7oj5$vv?M*kVR9DI-5+b{|=$%bF{`oV;1BZu` zbq)3Q=!g;?Lj9u>XE0$yXDLEqVLA%}S+X=sL>qP3fj~4a{YVYGhDEL*X7+b#oDY_K zbMayr?WBB#^OVHoLR)74`CWUvw<=)h)oVfRSsZJThSyUqy~5hV2wY=is<2$3lo6+S z85V~CGWhRTdA`5m-jluJ#ftC5CTddnjHZM8bdZao z7?ydOYV6il?VhSBdJ8Up0o0;VQ5LeXFi$%p{wAs#YJD-)y|HwwopaHj?(Pvuh4N)? zXjDX_$`=~@4X(cR=08b=cdj{wfL^nB(n-TToTe4GYcYtJlXkEgzT-oC-;OkH(JDcy zP<{y_?|Y%1ha7C?>I6;LYt%4&Lu_wC*pT0lNb5%gLJIB$HTE5=wxPS%yo}JK9t!^1 zvAy;^ex(vy^o?*J*e*Z&Mcn9yLPi8eEr+iuCp3_C4(T}60T!?d^s^yArJ$|hLlyY1 z10Suk%q-z#zuH|xXo)0+Ig5j9AW($|7-`>giW^doBp^~nrudY{ zy}XWZXSq&|r?-5R;vqwy3zcJy@aa`jZ?75Gn}wZ{(N&XVgwSHy--X+HUAs;C^t(Db znuGSWaX&mpvzH*U5V1p=;{ak29<3VqzHcJtnB8BdV0x*;P4sT(USts{P+}uj4`=*v zTCyC-FtY<0C8QxsKc8v0wYxf8bT4HioYF=Ss`r1>W~q%o)8DXI@K3P}luoKWJ1E#N zOHqtXt12zAyTWz5LnyS>FKPE2tVhC(^Kq|d0dSO-#s#_;_yg~j!2K^#1Xiv|86(B% zB8MJ-NZ{I4BF_S1h{v+{jI59p=%f9MD|#rK$T6|x0kR<2k`oCeAAH@!Fbk*}i;0+@ zO}L|p*A(ThuTVqQyM>db`|Jn~9{`(foO*dZg8*|4?gOO`S;&GOZe^_lWC)vjLP#DM z7S=4&HvWK{wX}gv1d}QU%XLTvscM3&U8S;02Bokua&@t}p3gzf$i-v~dgV79|OHg2553g?0#fxiyF;Kp__2YCwkE3toc{>QkCJgs$%zu#BlI zZl9DMIdRih7BZB>78Pd&uf}82i%;Jl#=HuZ05(9$zx^aU+B`17dOSA4ynT>k)|>qO zXS30&%upx3WW61Gj;9|{5*$XhRTrIHuU)5irv;lJ4}~;n7X&svu8XHIh!fP3nETUb z@HPe`&D#9C#VxH@ex zZjqPL--@$7BE*i?lvS1`ht1FmX7KlhnI7&u4bJVG@b_u|k=%Y+d<=2y&#&X1X^Ay` zBf5f;n6U3v@YP=VH{}}~HiW2r@JB1;nCmR`e0Efje>H>Le^u9tOS2^Q+->&lvjXgIukRb( zU$5;xy?8zRwV5FjVpR+mP~VJ_0GjP$SdhEuk$Oy1tT;01YdqPiVL5ptDw!DLO2TG* z#5)m0R8sjX=;0(V^v>zR@noyCye1{)jMHH*QxV+fxG_*xfe6=WYuT znLNODVKQ?unxm#?&J@`4o0RQ^!7=cxDwsa`c2$HFN5)?;LQX)=UE}9CZR~PO*1I!Nj6_N7x{vT`-6=!IwAi1?h8I&G5@OUo-mj6F!M~UzR6`@Z>)Ic>RU{n}*%bAzX$us%3tI+xnUg zZGwdl^D-buP~U1(vpZN1<^pHs^;0sdeKkS=sBaNvL~*~aS6Yy5m~7V>D5F{t*%VAc z;#gjac*dlEDtbZnrmAW(qcCkuJ>Fu1&HuoNV47zp3wRJ)2rx0?u+Q2bP$PET?7l}4 zPZ$4%s5J<9Twsf5hE;yiCFAAP71`0B&cZt%kYidQxN$;xMUOg@HITbE@SG}p!u;pV zE)C~KZX11hQU6)U1sB24He=h7+ZZd!#VN0Rs|Dohsut1E>{gXJbdR}t1HRV^7rN*i zhGY1NhjEV?eY7)bE7FX?Fe2P_TeGPnhWVM=Ba0YH1|lL4dCg_}Ny6U?i{bSS8xEL~ zjp8ATs|NoLkib8(NTv)_E!hm~Qq!=GX$C$&V+IJtgj#+9kbt?fstUWjkZZ%gT`bsh zunSVU!~tQDSKYL&cu*e!O^Pt+inQ$piJM`P6h8net?+P1CaWsGHf6|IVs)6cZZ$oe z`MTUd(^fs5o()!xy|YiKW=ZXq)JRLz^NDj3sUo*W& zy}z|Wj8LSPCUeqcmW1)Rc2u?Xd9TUbxq~wWDtwc99!H3hCoHFA5Lz2xL$T&IYC_e? zM&{2OrF6T*ao!tx`F>;zqE?A_0OV zqx8JH3bqtRVZ^c9x-!LVq z)~N=WW7xE%@zZ_rrsQB2ltkEtytT7cRi}mBB!W53 ziIdq&=R)X07_A?P09wVcooT4*;#{=VK*Q=6sLevX+`oaz`2Q+KuOV+_WoqmKMX&5? z&P~`#%<${%ets?;qFgT>cRg&?|BLBmPejSfzg$`A_@b{AFZi`u`+uc8xqE+#mzY z)-9?TFc>m9Wu!XEu*?KJRSN{Gu;Jb(iEZd`98b1u8TTg{6OGA5^_nTphQJWCP^u>! z!rTf7(wLPf<&f79)>axOA)7;yN~m#6>f3Y7*{i|}AKTNYPyEJ|f6{GxP2}+^WaAaJ zONLwpOH`9!OTk}bORW#g)>?(!;7*0&m}|2%JPvZ5DIb7>Bo2jt0hNjM{|4&6jq3kE z`yW6Vh5yfW%*65k7_za6fuWIsiOCV%#I!8!itFT@B%ou9p9J$yg5d|@)RyjR&uIXXjKl_`1EbTgpvl7Nrx+yA*!g@1SzBA& zkcfhkk`f^$QW>!U(iRS8UU@;-aSG{~3Hou_8R`Z#2e8W7i3u4siMa}zaSAmF8A(Z5 zKuc)pX~`KihpFia8j5jx<#~q{6$T~-hQFXlrG6P5k|iThg6|!Xo=|@8f1voFdMhY1 zN;=Y!U|?WkqF`WTU}R!qXny#)-g^0~KkYt^KYT{Jc5O5Iw0+qTVr69`qxJGytzSoB zx!p_bq7w5wm0d>Ne<;1xdUHE^{QX{u;+Q$H2Zfv$eWE_7%|<6`<}cZ=s?+JTNf;_WgXN{skiDf2l;w#QYEA|ECL47@Ky7qtDy= z17$%_HgxnuS(_*pZpk&uHd$t&Cx%TLF(nPdQAEr8^LnS3-gYRWk!(SQ^4Xls&Nu2| zJEPD&uj&OpecF0G?}U^+5}=WjM)vP}sG9lR)iA*n^gYKLpGRfUSrk#q{v8yl_Enu$ z8+MM#M-}$H7vBYJ-JEMLy=#IZq4vXGW%+rw1j%7PdoK@ap_&6ZiSs(oRK zgP(R)HJXoJ_AfQQXh<6uoovq`AJ+$Du)=*>mwi~*2ksKT7oW$={XuapIjurPp=0fNSVJ0reO z>81xDX~!ZyXI)?rD1%aM(c49oq$c3xGl9W*~| zhX_{jJQn!w0YqqZu&_juFcr+L`kOi;e>beIzI`?p9N`-zcwQCS2{xk^b9Nsi& zVH-Nh7FbnV-u2H|+C&(DzJ)=kMaU4fU%dFN;pu=bz)MXvg`|q@?cjQi*(=zEbnMR) zYW_(EJeb7Y71t#~!7u}>O0A6yc(}#t4Q`iP`?Kd^ahVz7TbT)KMLe4hM}@PvtgLE$ z6PXI%sj+^MOFNa{T9M~D0v041s&kzfz2%Mtnz||uS}RcofTU7nG0GzI6mczL^Tdey z*+&D#dRN+2yPL(S9nq)IpV&p|$Q}XP6OJ(d$6Y>k^MTWcSP14a+CXqmFgf}P&GO_j z7V+#XVKpXkCo^|jGJkS)Mk{V?9wOqA0(z1ptMNC-)1s!sqd$Px6s-+Z900d2G(Oet z&4UHOM(xbgdS51`5W{q~W3nJUbAP*%H@kP|X#;9AmJyhAbrzvAKhx5Y_j;NuU1a0V z_c1t%>>W$V8&SA5E|_If%oNVEO@WF{M&=AUPS z&CPg%WY4*vJzH^ypdYN#ef;L_>e_dR)p&rI9}aEpe6|V|$fmKyp!r{rMcXK5^hDSP*_wO|t7& zRc7S3D_GC-IxlY-YY+_6z;x+ojfW2pq1xcj0YL*wPaw6@b1!Q2I1vdxA;$4=>?p}M zAu9z~OVN7A_C2G1E7@2-R`Uxq9!A-3e`()>6bLzENqx){+OhYqjHKzz0bc3B%Gs|C z3%U21Fu-gtzy%RpPb^rmIg*aO9M>s!EoGP+SoPc2e1FgsXPILF{YdVG}0Z#--4Ev<2asS7n-`;GzUqDunND|{k zwbMu>5f1Tjg%xQc8f80`sEn|!N_g-GfbI!hlpuCj?>cKmnW_5B$J?m=GTwBAy_86% zt!YTcEHQB2pbEkI&D~THa?f*`$X-;R_LG=#Yn7SuKWRZj5j%+ z^S;+RgQzT5jG>D#Dd2{hxsENin{w?RGixs=dhTKm%tRjEOC6^h_0|OSRx2meYos2r ztEh4+O85e7J_ot*Cm=)U#mtCA*$t@E&BJOp`pt&aaJ~j!c^P5KZX<}+JFdA7!Cr*_YrI_ zv~HNOTPQy){w7!%j#G!bprAhNI!`v6S7Uerj+!zmqn?Nx*bsb1l7>}4BDWr~Kn7s4 z1!2uFNrQ>8A88?((ifpx7FA_K)1?_2hXO&p$e34^;pboHeQoD?LkigmRT#}Ld3#X!97Y*PNW5=fFjCW;chvKzggX zl;Z*td-szMj~ARv?z68~BoNY>Xen*Ruro;np)v&mk^3$4DraR*2ChPY;wqRj59}Gg z1_l{2Pf4_wkgX>|(y0qxMQHn<#FmPiojj!L@GWbTJ6wrF&VPktW(<~RjzfnAT0h=S z1ti6*pd=L_95(^knBWa^R{^-ZkOc-px(XyoS!jn??dOJyIDQ5Sw1Znz8P5O%wq8-n za`fMosytV~D=F~vDiHjw3W__|9nxSBW-Iq4R|>tEvI;YqA@uCl$X?A$Q3D?OfuU!g zAPU&{LcRu#>*`BR&3l^^-=F%{&iMh(zdee&ehcQL^lzG;OuL>bcpT6PbK+QQ+h*$h z82+RTlXlwURA9J=w_0T)z|w*$DvQ#{+BR|f)uRKd3YjHoJ@ueXYTjCGSk5V*sxfRe~^N!-l3CL zS1EiOjP6BmC*=9#;{EuBj_OSX^T43Kb3HMYMDUQr1EZ4Z-B$9t;;*&Z)f3sg3+LW? zxPO`>=j)BLQEM)=poxdWtqs#UjDCpk#`TDAQfQY|`QA@opA2*Lt~3PR{li>aX6t@y z_j~`ACP6E@M4$?z6iTzo@le?EMFpqAR)eW!W}M~cf&_F6ZpIF&D*j3$Y#y>nfuBA)W!%Pdf1L*8LbI#(-HnOIu*H)B zxV@xy+~yH~1tvvkErVTh+H9IWx@A)Zir;sXoxK^vqZQ*KH`7>Q( z2<&Kv8IAa+>F8}`rQww@v`+RqH#`ff#ApO|D zJ#|T9@!vIR+uKbjaLWzlq!T6y=Ps>L zL-ea21P}7Ix^2TigOkLNoN(X$K9--x>bMX^5EZr;;nXUOd5o#dOg+T_M%C=wws?Te=;6bzEE^QHxBzSEh!;@Z3D_D=iCBby1{>9Jo55dFURE#HFAzP$D2;gm*8BI)_y1O zpwXyJb>im$c>!RWhBFD@91327I7!QSUa1juvlg29q z7Q6v7DM@e`u2RX~kc>_zx&ADT1yMTGi`ht2R0*rQ*!3VJ{`L=jLdist=?9uFxDH?h z@`=W`67yE-=%9Pj7;_`3H6Zi#x}aq73A-TsIg1onv;nein)MB9b>xfJGY6^M`L&+5 zdJ8;_-_rT1`*+TR4!;mB4~CJ@l0^x)-_&$M`XnOt;4V*DT(y; zClfXXwe>J61>#kwn?>B+?bP0=EF17V%?fcujL_NF86x9FEmbbv0O`OC+VTME*d0fKv70We zmwgUlt=(guEP7_wlaACopmTUyQv| zj4nXaE;{Brwz0;xJ+sEPZQHi7#~oLZWsGWbs)X0^NGL1tLGhKf6(t4c!xm>jQsXU&1#IQWtVw?L4wxO?K? zkYuP28sp=*M>+eNl-$0MQF~qML&W7wKa-2egkk6C=7+w90?GUqK+sjd(u9cE1r$H% zxVf#&Ttit?^8#UxwG8oIAyxgaR8r*uSZ|OkdBnOmtlsP3S zRBHL^G}jAvU(C^TcEM3+yaTY(3t)(oQcwLV zedNZz9Eq~GmPU7U2Zzl>bH?2lz+RsJiUMNiOI9!m51vVfH-x8EeIb-3)T5t-%)3X8 zeC*Q13J6VEC5M#^*^My+0r;*e5j3?Pwd7V4v#I$;dd8G;@}0XiHtevT+*rcIXnX(8w&4CvhZ6Nv^maKQ68c={ zYXiPT51ewYTZ57*|8my~Z-{EYR)GI}S!{I!;i0_tD+!o%EDa`!4FUzk#6V(J+NmA8 zIYyEXO$@%9MdQ?X< z+7ha$-igMhtQR|4XpEn0F4Ck-|#%v{}SuN!py|}zixD;t>v^iR`2^- zyZ1-kEMn@rLB+#E9xF7@lf`4u1_03IhbNbu$`7XDrJyF!SI z%XI01+3l&R44bPIMJLm~Q0ATWWW^vJ4@D<&x72P78nx!cpnLbJarx&n5`vX$Y@%(@ zvOaV^>7sJn)W0qSljwMA21}<@T>!jNW_SC~C;3`cqa9nb@P(l{JCDai@z3RHH5{}5 z*Bs~7eANy)wet4m3IT#PTT^UEL7A+zZ06K_iS>>-sH+ z4E`;UeIjTyPwUl#q|pWU!?LDqfKbhAwR z2-Mirm7-j#U%)1D2vn)ksE!(u-pNMZ0y6^?RI(!J`XBx8dhJ2BMygRfJNZe)Kqe}X zJ>fU51`}IlOYfkpU%U+xjB*Z7)4wT0)d3~1`tHTbj+tWTlCDdC6pyhnzC+`H-s`Y( zlg9+s-rrW)d94FG+dboDRdCTctX^bK_#0@Cm9BUP%>vH754U=etK@&=z-{b3 z)=v6a47eJIcJz)_T6X$gB*+{o`@5yR7EQ7YHoJE?nA}8p=^$mXFk*RL~s@g#oR^!IgO)*v;wo<~F(ZXw3KiS+Y=!Z7lls*&6iOFrQOSx$wv2->5kydlG6Rd%MtS?dsVOIKB zLm7w`t+Z1r_bVD;+m8QoR0VO{A?mXMNkiCLcUrlb$p+GDwc)Rp2EOAy))vSWt57BR zw(~7w?$88V-v=KfalszLA)<4!zW6UNXou0~f<*Z0-oES$0oLZUjwfYQP0_%L-02ai zoL4$sz;{Br-t3J3dYa=0fyAez61Vr8@3}Mt>*J29JsVCO$XC0)`;I}u+EPe6w>W(N z+_K-=-Pl3~(K>nz*7F0#!7{i{m{!^OyT6aGJKYE%&eb=S)fr_rMiB^Z>^+1ZqCpGO zpdW1ge0~>~KQ}A&c48-<;$bAdLDFw+cXoZ<*~wCvUuk42UUqi3VhZqm7Dp!x~CyPCca5 zRpI?deHuQ7Ftsx6jJHN{Av92;5Uc-@)k)ety_-WAOJWO8MAk_$rJ@4lcB+i=?t8j{ zKgZ{5L($l&2i1HVv`&K**7m}I>qFgufS?zBBM#6nGJp~aN{ebBb`kfVbo%g>=~I-% zYFsq7n&~U7G`qF}OVYA|=7Tnn!&>xNT}fM{ONs;2?`Yh~Kv08ZyZJO$G;l4dtc)~( zbU^T02_T{4j`5e~f#$4s4716HE^%_tCNvZlnX9Fy`%blaoIX?fOTVKW_gJX=a zZiUV6T}DD3%7<{F()s0Mdx8EzP{!y7%})yPQfMSKzS*>Ht-6$<*h)gyi3xdtn~)dp zW7Wc+cu4yk94^of0YOC%S2Ju0Xnj%n!StLwn8SCHGeg+CGa)Grf*~@g68xTSgDAFO zNlP|Cm%JDY7O0wbv_F4VjH_g9lkR%L>{ZBvuHT9(Y>)K*l^j~ z@ku{*K!zH_bQyO$z~^F%g$WrTC0kU^#0c`9=R_$(bN#-zj4#WhN*WESsAq|8MWB#B zN^7$v!=FUHA?#I*4;RBDYJT`fzDT;}PMh3UgQSvK(4HDTW zd}X`FRf=_rlS#fMsUxVht@ZQa57#OC^mw!e7&tw7II@60C4|&^-3`wofGG-WstsY} zXoZT^<5wZ56!`7W9q^HPV>&ORxa)k^4O`y-|e29lpO(to_n$J2j^|RvH=$jY$G8XIZEs3(c+&a>82JU@tE|LO&1!Km2fn zfB0-GvCLi1Sp~VI1@H}32>cPGGP|6?c{_T^lP+Q170rH&H7Daz`bjirf zyZd;vN*+E`;@ql5&KkGP@c|5$k+JV8wNslIx2Qxm91Lr@7;j)7GM?pGq# zfX>g4E~J`%bbJJrv$$fMw20<~>qI1|2Yqz}LL9@gE*cOCaOgZ%k|-&c2eA0fB!balc5S}Qx(FI%A#-2&K+ZOze&)} zwU56@x>&lY%xgY7o8XIg{3icW;zA60wCf)@#X3Pw*z1tWiBOy_hzDV26;Ei!7i4q! zHbPh~F@+DfRgG;&FToI7T1PcO3KmvHBQgsjz&w_o8rIYVS7rLapv|X%!0@HDvdHyH>`n5AJPMSi z{e8Lv9-?=MJ5V3Y<4Mm5@4E^MkZ9y-XJ!Z!co})ai!i7P6TQ~_eV8~mobp3#*C0L< zY(A=h1LPZ0%L)pmuD<~l(&6Q$A`Id91?;#fkl07`_U@^-sd5VJO4!k_C%$9z|zXLyV%W{=^i+ulH=hS>feNbQA zUR>fbl@Of5TG2`@kVqtu$oxb7KSa?7Ry;74;{$h(Zhpu->DlR0D6rK9eR)WKY{U_H z8vLZbb!{CStT9XMymR!3LNn|&nRL=(MlY+E+jz=`m?iu4&Ly=;Ar=PfqOWOGRDGGM zx~L#6pl*hY{i#^jWY);CN}Ao7fO@5LCo`%p&`fKtoFk zAMObf>rCLg%B&$!vxzadlQiHYU<0U@w%JP2qzz}UE&t{9#1^iZp~6xJp%4)Tv2ETk zkcQ0MR1h77M0QR;K*>=YX%U4iy=t~cB08*VNq2}ib*wBkzrMyEqY)};1q6o2!WlGr z#E*UH#JRZ0SM(e2Ih7T3>15t{Q-llZ+eR2A)LQFxkXf95f~eabi;gQKPEbo8mbz}l zaM(CT0#iq-{CJ7!3mQBDOpQ+?UW~~y-xghZk?5sNI>R|qKBhX2Wk`;oKyJ^iVfnK! zfC3Ih4}eSY8Dd&jp}xEm4|`LlqZIXWQlGm#6UozU&46BKOmSlF*%T^q)|$Is=*aS8 zKEXx!kx6R>s-RGgWeC zV`l0XCjTx)0Z9bjPY$3hKD8kEnU-hO`WZv4xDaC#z>V%Pd!uCAA@AnTzq4;dF$5L% zFLOfR84xb}a2Q04bDvQMgcV&WLK^i=$#Y*~)?=A)Q8ou^3Rf4P-<#Yp0X z^%;LY6ZHvIxFith-;?(tq~#M>BSGybR=h+gHV1y_T{KN|N&eiwPL0Kgw7)4VG#4xv zaJzd-g-3e`e?8@FSXuJxFVtEpngn!Ob{yk#aMWeiScl9MITYWk4j*H6u1I-!oYB-M2i80jg0RF_ZCt~-318^i~ z5V2(k0`o-zuMGI-1Ov}oE@RI-MQ zV`J7avFhrC0yF9dxh~B&v&)_!?U*DY;an&H+E;XC9}v~zL!COv&N5;s7Z8y}tU^uz zI9cIPi_>djfiLBXH!AV|{A(#6OeAB#Qf256Yp`nqFC2ohLV>o`1G%}qAz+(=1Mb@9 z%xL`kq=({Z)+2n#%4P7npwMq&Ml0H(!S`!d9je*>V5>&2_ig&3qmL=O9gX6Mw6gZ( z@r0UHW;1!xIRXR5%r6Oq*Xg^9JUCEPU)2MD1?CFrt~-iX%Yk^INES&$p#{v9*!gph zTKx67|HBht*ba7Bk{AhKSdR3;g>t$<(8_@W+gB?6HW6SbtnY6XV1x%_gaP}lqUp~m z|9r;!sR>33}$Q6R^H{6Aav3D>`3i|^T=GvThyV@`MCav6$oW?a{=yg3Nj zo7vxO;S%i$tdg*2&`zW%Z|ZwbiH$fgtcUyH*2&*>L%^pM=LW}S4K>p;)=0SRORTxK zym#IIXo%mkKjmQHEA;~$C{>}giJ_j^QIk7HL1yqjN-lx9=5=9W-S$cYwbb-yu~ZUI`{SSra>WD8*PkTCC=lQ&r~%WRPtV1i!mzc>JI?VuauqF4O_ zA(rQ8`gtZ3$?X7$GOB5?jFFIC9VkzwSq0vGAF-~PK|6*kC`{2Js0%O4;j*M(e3C0@ z@$mfC#zIf*kRjtB*B{Vwa{dD|nq|pDzf+=P*n z^M8w*baSseZgRBTzQgb?s4!x~0>}GQ5-eo5jp{@sMr7xdr{{G1m1>%{lynxSl3Mlt z;)NAYKqeinzu*fGuFC?=`u6wF+MYSOS~X+p;^$MkOJQb}%Br*Aa=MdM4 zZ1Hc|HC0SwN%twqk-DP&Yq_Yw+3EfA3Jg_Q9bW8-9Bqre6SnXZiI_Ch5~gg@u&^rF?Qwx-1V=?wg4VQiTA`F>IT84Poq#hRw5 zLC!>l7|;J8P(`j969qF7@gPCptC6=CVmAkgsmQhEVeg_t-Qa3rNmWO(MnwR&|L7Cm6y2! zSVvr0Qb*?rIwuDkm#xofyE2fVEKu0`PFsPOkbz5v%TueM)hB)@j%--yMp6O4=%(;fbw2Rzl>`rCjf>!#FNUW(LLj1?P^9E_NDo8wD$PyQNz3Z-+ zB{5;zR>dE(Bq^%K2;JvEJJ5b$(E<~~Kby8>zT0ltn@<&x#=UAy1Ts}Rp2Cd}=L%=l z(c#&@y3CU$&18$3O6?buiizK(frE|tw{;QD;^_vPp5N^r=W%PB{k;02wa zNj@wZt`xBR0K`~9_41J9@!wLyNz`dr#P)*I$ei3c_%?AM^;>)~w_A4O)X(uB^NjH@ z5V=eeA#N7{+nO>oPU$ZBuJlT5&TQ54I-70IzM0r&jZ(3o8~ATq>3b4p}4c~3*o2k>IxQW-8K7*!M2h74d0 zQteT5a+Y|NfOU6ZXK==z*^4Tg$A7=adJV@u>A&udmc`LO@l?9Tev^%=y<4Qs345RY zL_}XCeCFDOA}lX+A~Dh-z=|;^A7mH1^vKd6=ga6{OaPOb0TF+Y!Ec@cW3GnBswef2G&-q7Tsfdx|_xp+7qB4jO!QV zEnrD%h`^$x>u!-N!VC&eqmrSoaW48$FT*O1Z`^_8%NEsD0#Lgoy6_(@MbT!b%4?i| z=9;^vt92r40_K31M=AzQjYR*>|ATzcSuP3D2=z?u>(+ysrvvBI!_IqZ`yN4qHaTBLVEurP6H-OSNVM}2UHd-{BOUNB}`siG>>3FB1x=+Uf+ zN`W_KRi*WiHcUycgLB7#SQ{H`t#g7AU`E-calBtGedt;q@=g%eC{u3pxC%~kLP z3>*&!Gbnk{G=SBD_B!?vJCyISqrVhp8p~zol5Ox>y9IYHcMC2+ng^&#Gb2Sf+iP(v zLt0{&wvE%CythOcV>K=r2&j(ihWvh(`)7q_6%Ii0Kc+}qw|h+jZ-r>Npis|78p&i_ zk$_E#{SvR09I2x+psQpwhDLAIWZ5MlA#ScuWUN}iN4aSx z0?(WYYy%Q6JB%;jltl-KVKyUc#l$C%T6x7I6OG!H!c&;qe4Q9d&D3RvceCRxS_LS* zQ5$^gTBo_E8;7p+P)wgPSnfGP^4{J;epV4e!+g0RUe+Pm1U1G$l!Qb{V|!)&-*4R| zA;j)KPP;Zf`?xCpa6wgczl2lL0SdV0M@N}~k*T$o&ka(cb=lFKF7DhX3t8(Y#AxmD zIkXKKkr)+ikU@5|rGW8;OsI#0*T<;GN;jpn160#Rxv+e}CE)RtWYbAe*URiDvw9@o z!(7$e2M^d4@70K@snz5BD=44vom_V8A34mNFq3(R46IHf>^6fm4WJ< zG7GkMVF*f4gu~J7)>+ap*>erK17U7n`9lao=ckT84_bK)25M21_N9y@dG#9$(|S9) z+PdAjfp9(QANYO*Wb%A}$)sn5^MEyDu*IMA>WwSw0gPK*A`^FUyoiw(PO|?-rV;VN zlq+c4E{_;ctdU2=kBkEKFd?Zm-i1{|M z&uXZu8=?%S)z8s(iF%D_5U8@_)*U-#sq-Lc3;;Rf=nn*2w6OFU3HL*^e_?A21AcYE zjH?L4{S*bI3q&fs9Jsmt-a(p=z6>2J3om(*L39nI0|Mgsa^;@{(Tox6+YmZile%LP ze)(c>52{F-aUv$L?wGWWfPIEpr$6w!{1)wPbXPH}429+1DvsEQ?YHV7V2(`fB04ZJ zAlVXe^R1WvLJ%QmxL?nqX9w$06osLga(4_sV~I1{3mvP`-7@0AFO(hu+cXP(x{DQJ z(umQ^`?IovjvLyUFT46F_nEwqQ|DY4&|~*@>9uR(`qW8RA9*r63xS zVIQox5BQBDvei)-T4-X%_X{hyKOQ_^w)RSwsAv51X}`Y70P^KZ2;z~oCHW>JaZJ?R zhHZ36z;z?sGwG=!gcJwdc#uyFY^*suPg=oOBRIiq!``hQheb`g_4atg;agZD@5$gd z+$Uzj^XkhNv(x{)|2Q)F`J-|9AacB5HXJ@5_oQ=)k9oCcxe~-TmM*~ee}6qrcHaR# zhblZ`Kkz2WeicX?!iU733jFj^$n{yavWKW4^0edoIQex}k!Po1NIv6mf0GH}8PeXR zM%@3=o1=H5c2yRl?xwXgfV3i?t4AGhSwL6xmAu5Vg9%XQGG&7J;exvXz@9N*lpOOD z<<#D+gTu9`TuAgMsD&c|Caqpe`jJUejx7<{KK!O|O+ zit6rNU+$70GUG%Az#Zu$-lJNtXVO;CG}31iKvLOp*Qlr%!RI5-bSYy`5h?=!1Ybt= zK`&(V{oDUsf-gLpO`ANnp?0R;Ms@H<2!&PuI;GN6^eZ=sg)qD<0Aqs(lWbq?boaJ# zarXjscX~Rww3Dm5tN?;kM7B`=h{r7rv{$#$9(G-l{Z3h6r8UP_#H>1VkU!!>1!}OR zY{#3<3AJYvkm!l9nz!9-A+iK!&TO{G^|e@sLPX=((yvCvc||uX@9O()^%bOg30Tm; zE5#0pv8!k-*iM+iGz=KBSLWFjY&H?klUF{PjREg&!vu4#?zc4aX z-fsJpdMRg#!Z~EK`<4aX>ftfGS|&E$kaOgOVBvgyGmOs2-YM&CHFcfaP|#35vJFvx zLt+uu&O1XHn|o}R82l-oD)+@+4A5HAb896vv^b)d`J8Gdl?s&PmwIe z`EQcESo2(jV?A+iu+mSL*IMkSRG6=tLcfPC6JpF?kLcoC6oPQe)`7?oA^4RtxXyIe z7dLI&uzqHOaAw%@&xwJU?FS0Uh{D{*-UHrV69NuYw}b0$!!%Jxw%*rIqAVwg_!8Sn z$F4HPK#A~3gx{h~iZEK)2(OR9K*DDctsuP5qL?QNxqu7iHH}6uxsS%6FaT49OPR>; ztS>~WzrFoX{~02>6U!+3>j9>Ne&I>nO6LB^m}%rAhTvSLb$*hdGColff-j?bPQlLc zioa+nkr;A#I+jj=IDE3J9agBx%xlWMj>GpVjBcm9V+*C8V|Vt5{OjxZeofh$!E z&ra-YW=X-=g0Sqk1&cFaQ{)*=q!!DkvQP8l0m9B6@o)Mv;3|}VF(eL1`3y9j>IvZ| zVZIGsW`SGbDtH%V07E?IL;Tf}XH?$l!A6)Oq8Pagej027d@aTIl~>j}Ddgro)poP8 zSR8BftdO2> z_+PKIuv&)w!|nqFBSSUXGXabui2>Z6*gOC}+uB0pJf(mbzoAwPcY=t&3e!<5_X$AILpeRKYus;HP{t%a(UhHM~(2_ zXa-K+Ly`=FJil9jf*zUdS4j$`%b)t>My1;-H80Qk{9-NBBg8`UOwkcN;I!%~esZwi z$Vgv&Dcyh1V$B{H33~MQ#`*P6i$BxIB9xr;w5RD=Bg}8Si4XR{&4QopK-u@^(jNAl zkbA~Jz?WLZn;CN7QsYf$p4c1b?$8y=dSCUg$}GJ4rzl#Y;zX;iiOyo&;T!7>L4Ts$`_te}wJ-18`$xhW+V^*MoSZ>Ln&)AmI z*j``+*_!_`nF{ItYChrCivEW3?S82xDY?m&(t%2c<~F>x8~EGODvRUjkA`TmrGd?B1yP2&JM&Ij9ynU z)=1rf0**KlEm|GE_IUT;z+Sbk=*F7gPM$;Ua`5kdUROb(dc8q$%+g+bjRtBT4V@f4 z?grz$ov>qlI`^eLTi@YkyQ0mUPMUYMt#$Fg9``N_gZRI79%GDlFq=6&n}JKQ#M>Ej znCpClu8aZ=SC8;J;k95hPvy ze4RxsHw4`ca^tg$y#DO)T?b#s4oqub^{?N+Z;r0@ga_HXfML4pjr%ZQAFdby8xOP+geHd`QCUW&g^S^Vpvln0m7H_F)Kz)m(Xv{rJzjZh5NxW zA(>knH=Mdib`>9$a&hSC|6ULSV2Qu#fNj^Y-hK@(^dvWH(7U0y*rqhkkTR(d6gLZ5V2 zyIZ-B5U(Lv?qLtu$A5YBJ8RE4MtuOJa>zy3@P`P_ai1O=QvIy?o2_z~A7F7lRJ*+6 zA6cLq(uEKeB!?R`5ZEPI3ZW9kP-nZL?S_b1e__e7hx!SrH)ks%qVh^+seQz?>Y443O@0VktQ%GEPf40$e5Oh?01p`mkt96cZ~ zUTq36kIwgmNnA+^A$3GCI5cxFvLR*eC$IZ_q$U5|8c8oD-c@*||mhRe!+DZ%s8f(tOkK6MlnocU_Z{nkbW46&|?e*F)R@2PBQD9kwczEF#!zKUx@tK z7mwka*VsfHC~2mfd}$vZG$tldD>hpU;Rr|WXNftdnKVJZA=hf@e%^|`-VPGx^ZV*gbm-fxZ@xnNO*oE!H6a%+?pv*snuznFk4}%?ZPNPn z6b7ozI9RCbPxL9$HK&NGrkd`j#0e4U<>k!E+B>J5vdnspXoh7VE8<#5*9~9DobGPN zpRWt;6OZo^6yCD-*{v#7+e)I%61X+y#IQpP#5;uKS0a28qr0 zNJtJ=NV5+(`Nw%VCVA7KmYGUvGcVh54_)RguIiWHnxh7F$wvDs7;yQT5Ri@WNw>h) zud>0Orr&bwvO&%(0H9WEbFO7SS6`vO?#!+aP@PukP>UV77s9wL-k_jQ3*#G({E4Lg zm>*8r@Ui~Q{td(Us<-9#uwF3Tq^)7oyiBWy?L7@HM=|xrgs($Y<>;^vMV5b144iw` zQoxu+;}OA*m4FsLmxqzoMr|RIfVy<_)}v-%v+1oFVnrLW8pz=AxkReB8~>z5)1#-D z*Wh9I$yRXxHiB_u(*(3fM*FUuB-S(5i{ zgHePfGV#wSS07sf80VkDMm)fmH0W2rmei~b^z#qALWsPi3m5fwUe(QkO?h_TGv+6b@(#t4Cxxde$E9A?#qsTK#uV1SUP$QO4+(ias zB-IU@sa?hCZX(1_=Qyps#;e%TEyia;FUO-5pH&K|<~WdBjoA_zGfrupoia=Gup2A9 znQT0Kt7b@X8s`e8tetoXmDD;oA~S6TRejb=3)_+@3#S+e(qD7M7aIX@E8nrc5>jjU zoj5N!BMqMRVSx1hfG1&Lg ze)lo9Wy^M^E5&He8Cs$(4KOkM@kqgqnu05Pe4j!&+Odc$M1Pf^Bqpmgq@Jb_sz$7x zkyqMe&sY5{#BW)^vV?GFuEyq=gRw>1(@)!Lfv_DhbOE*JoVsh$Bjp6ve(uYMtcgr7 zilVUj)K2xkq>^0Z2?J>x=}eP=ygA?A7BOE3Nd>oODon@tyl%4G!^PmQygSK&J}&WY zrwDolvr9X&iDx(N!4-8EhAIBj^|ya>>W+=uy>lGe7E-SV!H#!ebLP%AL}eddvOi=V zyQ^4DnuEW2Y{3M~Ztt2Y?52^%+uApZSdiLdGO#s!-^T6kWi#5-+4oXnr&yT%PuDif zt*a)hy_i(7ia+=Gk+@JzrN+((PJ;@DF|)4hj2-`k7FyRfz>a-fD@md7fVP#?&Fik^ zPnX%hM^|-LJK@R_6?d)vqL6oXBVzDJ=sm+Q;oe>lD`4GY^TfVy_&o-!zUezU6N0@O zAUd~Y>DI-HfXlW(#i*V=n)5$!!V0dvO?M;|!WZVg-MO6J0@a#dqOTPdq+`@Hb`6qn zUlp@5U*6$plgxIUn*3A(1AOyh=U4c|Sg?_1>&JwKEqK&Uz*eS(4gW{#7Ro z%UwN#6XT6lbT7~Ha7K-mn2C`&IIP_&Pg`B)D~i?Tg6n56$*_tXCB-|PS#!pg&FPvl zC90X_%l{$E$Iet#b=~;9N7d3bmDZuT3j%HzAey<3Q0@k}Cf*i-CXTMB9=XfppDP#i$PBZ-u%E zO`Wci>r}7P*a zl&x2Po|-{I3?No0TTlj=XP;9II|Jkz9j*8T7=05m|3`O2#3gvdXm-XIFyH<#G`m(u zRX2;Y0B*mcoejlS8+OzDs{>8mDil|p;QFHn)2-}}j;|1ye-M#DN;MZJNl z`4D`kUUH(mHWUylx~07}utto4 zDmSq`G?u93g((%o2Iq2FfU0F>9{Roz!)5ByIuS_eHy7}3&<;ON=5t=n5`%R`Ko}+q_XMrYK{-ii~p;&rWzKDDrrz#<1N~@{gY(KutL6M}BFr z6Z7W?#PpIHb;4q}={s~?dQ1FI`uQJ+iPvATzp5~jMQSer6;jaPI zFE~y|LF#|=bU6Qur^CYXzb^Mv{7*&-R1gq)`$_GX#e0?ZJ|PXS3x*a6b+99eus|QI zT3_(vv-lL05V-vEAWN1xC7t7jbmDKnx##L*l~n}=53@c5Ah9w)I5c6#C6Yec1e$}O zyDLtfbYaz$1;Dyn<7ZU%w6{q>4KpbO`$FK_M1T#M>zJOAGqAkcjE;9jiar)}%uPM= zB~B?HlE>sXjp z+vOP@4cYv&o);XMbPbkq`(=30xmfspdR@R3gvWx?Ttdz)TAlgJid)M*VY&|RNUuS?Fu+vk__WYNN zV*f8G2#g#o|LX?h*XmMsn;eKeuWD!Fpb5l^w_9608Dz3-b=&A?hXRw*L(L=|NlDgo z{JqR1*K0bZXBGkAFY;0cfbspi&z^LaC;YBCuU9hX+15w)Q4EFsd5-XdB#%cgdbyxA zsVIg#js5A=9SkP4SD~mD)a|p)FH7#%w?Hy{xFBL03RwVeZa))rV`I;po!89{20yeg z(_!~jMMCZnrN^WdpCPBmf1D6b?03oyYFbu1f~rw>1r5lz{cFo4l3=Sl>Bzt>`);Ay zXl<|=ukp;8YA4bZTa;@17o|&p+Z(B8ezwPvQh`_!pxPdPRWl>elLaoa>CPJ)T;l5A zz3Z8zc0RJppu;rD?3jpam(GBwFLLow^lI@|8s9{N+6MN_ac-p~TsO1NKNnh$Lo)D} zciGD44bH4_bPyEGd&OW}{R;jA}V)RUHat}c= z7Rtat7H;!cGzQg~7ROoLKf)n4x0SxN3M37V?Y4F2%G{g{EA{MH9H zyH%j&v>PGTz;HwCpy?sy=kJfBx3}|iO7jW2JsX>php(7iPC>(Lf~CWI%_sjry#%6s z`y=B<;4JD$;sGInB|ge876jz207mx?+Mc@K^2b|w@^?8V^4RjjG1!njo52K{BCXiD zKtT3f_H&=WJ_v*^cPnR(#G00c$+*=eJKALef|2B_tG+k)ebiB!VrNmW!y^Lt2L*Sz zChR*!-c%kxnbCKiW~$Ox4>(69daKANv|bx6r#9CS7nv=7UCCQID~>zXN>ME9yx_`} ze@ZcUJKSA*uReF5j-w-)q!`8F3iTyh(}V8$2)rxrZ9Wt>=-y=XHZ>00m#MMEXwFnh z)Qkw+#-M9*fQd#ts`DSX#@9ofG?k+xs=C>GPn}M+K9ghg^;8V(P zFkLL*V$2b{PO?+y7+q{ZprYs8d*I{T5HS@X(&HYcvvtb_aa;v}i7$4xOCHxdr@3py zpeaBi;b73n=*M*oQI-a3Rvj6iK(

    JYni7qKgAkpXZF=5rAoBK+#V-1wfBy4{8mN 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%0Ai93Sot*jmqr4%OaFRipOf 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

    )z!pAP0i&B6Y@gQ;wegP# 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%0AU-#c+`FF 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 atv!0{{@P#rYk&P6um1 Date: Wed, 30 Jun 2021 23:22:06 -0600 Subject: [PATCH 0178/1472] error messages and warning from sundials turned off --- build/source/engine/solveByIDA.f90 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 1049af250..96e701cee 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -440,6 +440,8 @@ subroutine solveByIDA( & !****************************** Main Solver *************************************** !************************* loop on one_step mode ********************************** !********************************************************************************** + retval = FIDASetErrFile(ida_mem, c_null_ptr); + retval = FIDASetNoInactiveRootWarn(ida_mem); tret(1) = t0 do while(tret(1) < dt) eqns_data%firstFluxCall = .false. From 49eaeed8890487b00da43aa834482dc469286411 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 30 Jun 2021 23:28:12 -0600 Subject: [PATCH 0179/1472] flag for disabling messages --- build/source/engine/solveByIDA.f90 | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 96e701cee..8401df597 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -240,6 +240,7 @@ subroutine solveByIDA( & logical(lgt) :: divideLayer logical(lgt) :: mergedLayers logical(lgt),parameter :: checkSnow = .true. + logical(lgt),parameter :: offErrWarnMessage = .true. real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) real(rkind) :: mLayerDepth(nLayers) @@ -417,6 +418,12 @@ subroutine solveByIDA( & ! Set solver parameters such as maximum order, number of iterations, ... call setSolverParams(dt, ida_mem, retval) if (retval /= 0) then; err=20; message='solveByIDA: error in setSolverParams'; return; endif + + ! Disable error messages and warnings + if(offErrWarnMessage) then + retval = FIDASetErrFile(ida_mem, c_null_ptr) + retval = FIDASetNoInactiveRootWarn(ida_mem) + endif ! need the following values for the first substep eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) @@ -440,8 +447,6 @@ subroutine solveByIDA( & !****************************** Main Solver *************************************** !************************* loop on one_step mode ********************************** !********************************************************************************** - retval = FIDASetErrFile(ida_mem, c_null_ptr); - retval = FIDASetNoInactiveRootWarn(ida_mem); tret(1) = t0 do while(tret(1) < dt) eqns_data%firstFluxCall = .false. From 314217e609bfcafffb6845a36027c2fa616e0c80 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 1 Jul 2021 00:16:28 -0600 Subject: [PATCH 0180/1472] trapezoidal deleted from solvByIDA --- build/source/engine/eval8DAE.f90 | 2 +- build/source/engine/solveByIDA.f90 | 34 ++++++------------------------ 2 files changed, 7 insertions(+), 29 deletions(-) diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index 18ebf9e77..681408eba 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -674,7 +674,7 @@ subroutine eval8DAE(& err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - firstSplitOper = .false. + firstSplitOper = .true. ! compute soil compressibility (-) and its derivative w.r.t. matric head (m) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 8401df597..ea45c63b3 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -225,8 +225,6 @@ subroutine solveByIDA( & logical(lgt) :: feasible ! feasibility flag real(qp) :: t0 ! staring time integer(kind = 8) :: mu, lu ! in banded matrix mode - integer(i4b),parameter :: ixRectangular=1 - integer(i4b),parameter :: ixTrapezoidal=2 integer(i4b) :: iVar logical(lgt) :: startQuadrature real(rkind) :: mLayerMatricHeadLiqPrev(nSoil) @@ -239,7 +237,7 @@ subroutine solveByIDA( & logical(lgt) :: tooMuchMelt logical(lgt) :: divideLayer logical(lgt) :: mergedLayers - logical(lgt),parameter :: checkSnow = .true. + logical(lgt),parameter :: checkSnow = .false. logical(lgt),parameter :: offErrWarnMessage = .true. real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) @@ -525,33 +523,13 @@ subroutine solveByIDA( & eqns_data%fluxVec, & ! intent(out): flux vector eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation rVec, & ! intent(out): residual vector - eqns_data%err,eqns_data%message) ! intent(out): error control + eqns_data%err,eqns_data%message) ! intent(out): error control - select case(ixQuadrature) - ! sum of flux - case(ixRectangular) - do iVar=1,size(flux_meta) - flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * dt_last(1) - end do - case(ixTrapezoidal) - if(startQuadrature)then - do iVar=1,size(flux_meta) - flux_sum%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) * dt_last(1) - end do - startQuadrature = .false. - else - do iVar=1,size(flux_meta) - flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:) & - * ( dt_past + dt_last(1) ) - end do - endif - do iVar=1,size(flux_meta) - flux_temp%var(iVar)%dat(:) = eqns_data%flux_data%var(iVar)%dat(:) - end do - case default; err=20; message=trim(message)//'expect case to be ixRecangular, ixTrapezoidal'; return - end select - dt_past = dt_last(1) + ! sum of fluxes + do iVar=1,size(flux_meta) + flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * dt_last(1) + end do do iVar=1,size(flux_meta) flux_data%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / tret(1) From b118718c002007364b375e9e1579f26561ffc0f3 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 1 Jul 2021 00:24:15 -0600 Subject: [PATCH 0181/1472] trapezoidal deleted from systemSolvSundials --- build/source/engine/solveByIDA.f90 | 2 -- build/source/engine/systemSolvSundials.f90 | 22 +++------------------- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index ea45c63b3..5769e4412 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -100,7 +100,6 @@ subroutine solveByIDA( & nLayers, & ! intent(in): total number of layers nStat, & ! intent(in): total number of state variables ixMatrix, & ! intent(in): type of matrix (dense or banded) - ixQuadrature, & ! intent(in): type of quadrature method for approximating average flux firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution @@ -174,7 +173,6 @@ subroutine solveByIDA( & integer(i4b),intent(in) :: nLayers ! total number of layers integer(i4b),intent(in) :: nStat ! total number of state variables integer(i4b),intent(in) :: ixMatrix ! form of matrix (dense or banded) - integer(i4b),intent(in) :: ixQuadrature ! type of quadrature method for approximating average flux logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 452ca1d75..18e50edb7 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -186,14 +186,11 @@ subroutine systemSolvSundials(& real(rkind),parameter :: tempAccelerate=0.00_rkind ! factor to force initial canopy temperatures to be close to air temperature real(rkind),parameter :: xMinCanopyWater=0.0001_rkind ! minimum value to initialize canopy water (kg m-2) real(rkind),parameter :: tinyStep=0.000001_rkind ! stupidly small time step (s) - integer(i4b),parameter :: ixRectangular=1 - integer(i4b),parameter :: ixTrapezoidal=2 ! ------------------------------------------------------------------------------------------------------ ! * model solver ! ------------------------------------------------------------------------------------------------------ logical(lgt),parameter :: forceFullMatrix=.true. ! flag to force the use of the full Jacobian matrix - integer(i4b) :: ixQuadrature=ixRectangular ! type of quadrature method to approximate average fluxes integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) type(var_dlength) :: flux_init ! model fluxes at the start of the time step real(rkind),allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! NOTE: allocatable, since not always needed @@ -440,7 +437,6 @@ subroutine systemSolvSundials(& nLayers, & ! intent(in): number of snow+soil layers nState, & ! intent(in): number of state variables in the current subset ixMatrix, & ! intent(in): type of matrix (dense or banded) - ixQuadrature, & ! intent(in): type of quadrature method to approximate average fluxes firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution @@ -496,21 +492,9 @@ subroutine systemSolvSundials(& ! compute average flux - select case(ixQuadrature) - case(ixRectangular) - ! divide by dt_out. Now we have average flux - do iVar=1,size(flux_meta) - flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / dt_out - end do - case(ixTrapezoidal) - ! add the last part of the integral, then divide by dt_out. Now we have average flux - do iVar=1,size(flux_meta) - flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) + flux_init%var(iVar)%dat(:) * (dt_last(1) + dt_past) & - + flux_temp%var(iVar)%dat(:) * dt_last(1) ) / (2.0*dt_out) - end do - ! check - case default; err=20; message=trim(message)//'expect case to be ixRecangular, ixTrapezoidal'; return - end select + do iVar=1,size(flux_meta) + flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / dt_out + end do diag_data%var(iLookDIAG%mLayerCompress)%dat(:) = mLayerCmpress_sum(:) From 6f564c071a8e847fda5cca4fa51e8ed3dda666c8 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 1 Jul 2021 00:31:43 -0600 Subject: [PATCH 0182/1472] dt_past and dt_last deleted --- build/source/engine/solveByIDA.f90 | 5 +---- build/source/engine/systemSolvSundials.f90 | 4 ---- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 5769e4412..96b8b8e4e 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -126,8 +126,6 @@ subroutine solveByIDA( & ixSaturation, & ! intent(out) idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix - dt_last, & ! intent(out): last stepsize - dt_past, & ! intent(out): one stepsize before the last one dt_out, & ! intent(out): time step stateVec, & ! intent(out): model state vector stateVecPrime, & ! intent(out): derivative of model state vector @@ -201,8 +199,6 @@ subroutine solveByIDA( & real(rkind),intent(inout) :: stateVec(:) ! model state vector (y) real(rkind),intent(inout) :: stateVecPrime(:) ! model state vector (y') logical(lgt),intent(out) :: idaSucceeds ! flag to indicate if IDA is successful - real(qp),intent(out) :: dt_last(1) ! last time step - real(qp),intent(out) :: dt_past ! time step before tha last one real(qp),intent(out) :: dt_out ! time step ! output: error control integer(i4b),intent(out) :: err ! error code @@ -222,6 +218,7 @@ subroutine solveByIDA( & integer(i4b) :: retval ! return value logical(lgt) :: feasible ! feasibility flag real(qp) :: t0 ! staring time + real(qp) :: dt_last(1) ! last time step integer(kind = 8) :: mu, lu ! in banded matrix mode integer(i4b) :: iVar logical(lgt) :: startQuadrature diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 18e50edb7..c486b6b0f 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -204,8 +204,6 @@ subroutine systemSolvSundials(& real(rkind) :: rAdd(nState) ! additional terms in the residual vector real(rkind) :: fOld ! function values (-); NOTE: dimensionless because scaled logical(lgt) :: feasible ! feasibility flag - real(rkind) :: dt_last(1) ! last stepsize taken by ida solver - real(qp) :: dt_past ! one step before the last stepsize taken by ida solver real(rkind) :: atol(nState) ! absolute telerance real(rkind) :: rtol(nState) ! relative tolerance type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a data step @@ -463,8 +461,6 @@ subroutine systemSolvSundials(& ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix - dt_last, & ! intent(out): last stepsize - dt_past, & ! intent(out): one stepsize before the last one dt_out, & ! intent(out): time step stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step From 86efdcb74c569dc66c5c08968f478f9e19a101ea Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 1 Jul 2021 00:43:55 -0600 Subject: [PATCH 0183/1472] minor edit --- build/source/engine/solveByIDA.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 96b8b8e4e..f6cb0be9c 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -526,9 +526,9 @@ subroutine solveByIDA( & flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * dt_last(1) end do - do iVar=1,size(flux_meta) - flux_data%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / tret(1) - end do + ! do iVar=1,size(flux_meta) + ! flux_data%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / tret(1) + ! end do ! sum of mLayerCmpress mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & From e698e2b64576be173fee76e78b68ca10ff151967 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Mon, 5 Jul 2021 12:23:48 -0600 Subject: [PATCH 0184/1472] more edit --- build/source/engine/systemSolvSundials.f90 | 4 ---- build/source/engine/tol4IDA.f90 | 12 ++++++------ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index c486b6b0f..dbfe94149 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -408,10 +408,6 @@ subroutine systemSolvSundials(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - ! just for experiment - atol = 1e-6 - rtol = 1e-6 - !------------------- ! * solving F(y,y') = 0 by IDA. Here, y is the state vector ! ------------------ diff --git a/build/source/engine/tol4IDA.f90 b/build/source/engine/tol4IDA.f90 index 9731117f5..a5f78c92a 100644 --- a/build/source/engine/tol4IDA.f90 +++ b/build/source/engine/tol4IDA.f90 @@ -147,18 +147,18 @@ subroutine popTol4IDA(& type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers type(var_dlength),intent(in) :: mpar_data ! model parameters ! output - real(rkind),intent(out) :: absTol(:) ! model state vector (mixed units) - real(rkind),intent(out) :: relTol(:) ! model state vector (mixed units) + real(rkind),intent(out) :: absTol(:) ! model state vector (mixed units) + real(rkind),intent(out) :: relTol(:) ! model state vector (mixed units) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- ! state subsets - integer(i4b) :: iState ! index of state within the snow+soil domain - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - integer(i4b) :: ixStateSubset ! index within the state subset - logical(lgt),dimension(nState) :: tolFlag ! flag to denote that the state is populated + integer(i4b) :: iState ! index of state within the snow+soil domain + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + integer(i4b) :: ixStateSubset ! index within the state subset + logical(lgt),dimension(nState) :: tolFlag ! flag to denote that the state is populated real(rkind) :: absTolTempCas = 1e-6 real(rkind) :: relTolTempCas = 1e-6 real(rkind) :: absTolTempVeg = 1e-6 From 3381d27c44c14570516abf00a145b91939732a37 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Mon, 5 Jul 2021 12:47:34 -0600 Subject: [PATCH 0185/1472] more edit --- build/source/engine/eval8DAE.f90 | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index 681408eba..5ccce4059 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -307,8 +307,8 @@ subroutine eval8DAE(& ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) - heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& - mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & + heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(out): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & ! intent(out): heat capacity for snow and soil ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -553,37 +553,34 @@ subroutine eval8DAE(& else call computHeatCapAnalytic(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) ! input: state variables scalarCanopyIceTrial, & ! intent(in) scalarCanopyLiqTrial, & ! intent(in) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): fraction of liquid water at the start of the sub-step (-) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): fraction of liquid water at the start of the sub-step (-) ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices ! output - heatCapVegTrial, & ! intent(out) - mLayerHeatCapTrial, & ! intent(out) + heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow ! output: error control - err,message) ! intent(out): error control + err,message) ! intent(out): error control endif ! compute multiplier of state vector call computStatMult(& ! input - heatCapVegTrial, & ! intent(in) volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(in) volumetric heat capacity of soil and snow + heatCapVegTrial, & ! intent(in): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(in): volumetric heat capacity of soil and snow diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) = heatCapVegTrial - diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat(:) = mLayerHeatCapTrial(:) ! update thermal conductivity call computThermConduct(& From af5ff1decaeeb43e76d4ced7d913005b77acf143 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 6 Jul 2021 11:42:22 -0600 Subject: [PATCH 0186/1472] minor edit --- build/*.f90 | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 build/*.f90 diff --git a/build/*.f90 b/build/*.f90 deleted file mode 100644 index e69de29bb..000000000 From 326aced9e9a97b1cff47ce02fbda46ed899ccd54 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 7 Jul 2021 12:43:05 -0600 Subject: [PATCH 0187/1472] comments added to evalDAE4IDA --- build/source/engine/evalDAE4IDA.f90 | 108 ++++++++++++++-------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/build/source/engine/evalDAE4IDA.f90 b/build/source/engine/evalDAE4IDA.f90 index d6f945ed7..168882e02 100644 --- a/build/source/engine/evalDAE4IDA.f90 +++ b/build/source/engine/evalDAE4IDA.f90 @@ -55,21 +55,21 @@ integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_da implicit none ! calling variables - real(rkind), value :: tres ! current time t - type(N_Vector) :: sunvec_y ! solution N_Vector y - type(N_Vector) :: sunvec_yp ! derivative N_Vector y' - type(N_Vector) :: sunvec_r ! residual N_Vector F(t,y,y') - type(c_ptr), value :: user_data ! user-defined data + real(rkind), value :: tres ! current time t + type(N_Vector) :: sunvec_y ! solution N_Vector y + type(N_Vector) :: sunvec_yp ! derivative N_Vector y' + type(N_Vector) :: sunvec_r ! residual N_Vector F(t,y,y') + type(c_ptr), value :: user_data ! user-defined data ! pointers to data in SUNDIALS vectors - type(eqnsData), pointer :: eqns_data ! equations data - real(rkind), pointer :: stateVec(:) - real(rkind), pointer :: stateVecPrime(:) - real(rkind), pointer :: rVec(:) - logical(lgt) :: feasible - integer(i4b) :: retval - real(c_double) :: stepsize_next(1) + type(eqnsData), pointer :: eqns_data ! equations data + real(rkind), pointer :: stateVec(:) + real(rkind), pointer :: stateVecPrime(:) + real(rkind), pointer :: rVec(:) + logical(lgt) :: feasible + integer(i4b) :: retval + real(c_double) :: stepsize_next(1) @@ -88,34 +88,34 @@ integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_da stateVecPrime => FN_VGetArrayPointer(sunvec_yp) rVec => FN_VGetArrayPointer(sunvec_r) - retval = FIDAGetCurrentStep(eqns_data%ida_mem, stepsize_next) - if (retval /= 0) then - print *, 'Error in FIDAGetCurrentStep, retval = ', retval, '; halting' - stop 1 - end if + retval = FIDAGetCurrentStep(eqns_data%ida_mem, stepsize_next) + if (retval /= 0) then + print *, 'Error in FIDAGetCurrentStep, retval = ', retval, '; halting' + stop 1 + end if ! compute the flux and the residual vector for a given state vector call eval8DAE(& ! input: model control - stepsize_next(1), & - eqns_data%dt, & + stepsize_next(1), & ! intent(in): current stepsize + eqns_data%dt, & ! intent(in): data step eqns_data%nSnow, & ! intent(in): number of snow layers eqns_data%nSoil, & ! intent(in): number of soil layers eqns_data%nLayers, & ! intent(in): number of layers eqns_data%nState, & ! intent(in): number of state variables in the current subset eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - eqns_data%firstFluxCall, & ! intent(inout) - eqns_data%firstSplitOper, & ! intent(in) + eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVec, & ! intent(in): model state vector stateVecPrime, & ! intent(in): model state vector - eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures model_decisions, & ! intent(in): model decisions - eqns_data%lookup_data, & + eqns_data%lookup_data, & ! intent(in): lookup data eqns_data%type_data, & ! intent(in): type of vegetation and soil eqns_data%attr_data, & ! intent(in): spatial attributes eqns_data%mpar_data, & ! intent(in): model parameters @@ -123,36 +123,36 @@ integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_da eqns_data%bvar_data, & ! intent(in): average model variables for the entire basin eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU ! input-output: data structures - eqns_data%indx_data, & ! intent(inou): index data + eqns_data%indx_data, & ! intent(inou): index data eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian - eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - eqns_data%scalarCanopyIceTrial, & - eqns_data%scalarCanopyIcePrev, & - eqns_data%scalarCanopyLiqTrial, & - eqns_data%scalarCanopyLiqPrev, & - eqns_data%scalarCanopyEnthalpyTrial,& ! intent(in): trial enthalpy of the vegetation canopy (J m-3) - eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) - eqns_data%mLayerTempTrial, & - eqns_data%mLayerTempPrev, & - eqns_data%mLayerMatricHeadLiqTrial,& - eqns_data%mLayerMatricHeadTrial, & - eqns_data%mLayerMatricHeadPrev, & - eqns_data%mLayerVolFracWatTrial, & - eqns_data%mLayerVolFracWatPrev, & - eqns_data%mLayerVolFracIceTrial, & - eqns_data%mLayerVolFracIcePrev, & - eqns_data%mLayerVolFracLiqTrial, & - eqns_data%mLayerVolFracLiqPrev, & - eqns_data%scalarAquiferStorageTrial, & - eqns_data%scalarAquiferStoragePrev, & - eqns_data%mLayerEnthalpyPrev, & ! intent(in) - eqns_data%mLayerEnthalpyTrial, & ! intent(out) - eqns_data%ixSaturation, & ! intent(inout) + eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) + eqns_data%scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) + eqns_data%scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + eqns_data%scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) + eqns_data%scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) + eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): value for enthalpy of the vegetation canopy (J m-3) + eqns_data%mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + eqns_data%mLayerTempPrev, & ! intent(in): vector of layer temperature (K) + eqns_data%mLayerMatricHeadLiqTrial,& ! intent(out): trial value for liquid water matric potential (m) + eqns_data%mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) + eqns_data%mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) + eqns_data%mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) + eqns_data%mLayerVolFracWatPrev, & ! intent(in): vector of volumetric total water content (-) + eqns_data%mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) + eqns_data%mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) + eqns_data%mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) + eqns_data%mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) + eqns_data%scalarAquiferStorageTrial, & ! intent(out): trial value of storage of water in the aquifer (m) + eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) + eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) + eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) + eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer ! output feasible, & ! intent(out): flag to denote the feasibility of the solution eqns_data%fluxVec, & ! intent(out): flux vector @@ -160,13 +160,13 @@ integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_da rVec, & ! intent(out): residual vector eqns_data%err,eqns_data%message) ! intent(out): error control - if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif - if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif - if(.not.feasible)then; eqns_data%message=trim(eqns_data%message)//'state vector not feasible'; ierr = 1; return; endif + if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif + if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif + if(.not.feasible)then; eqns_data%message=trim(eqns_data%message)//'state vector not feasible'; ierr = 1; return; endif - ! return success - ierr = 0 - return + ! return success + ierr = 0 + return end function evalDAE4IDA From 731fb01b6d5d499e7a00b1a4a6b5587d7e87b04e Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 8 Jul 2021 08:49:12 -0600 Subject: [PATCH 0188/1472] minor edit --- build/source/engine/eval8DAE.f90 | 2 +- build/source/engine/solveByIDA.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index 5ccce4059..98904d273 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -266,7 +266,7 @@ subroutine eval8DAE(& real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step - logical(lgt),parameter :: enthalpyFD=.true. ! flag to indicate if we compute Cp using dH_T/dT + logical(lgt),parameter :: enthalpyFD=.false. ! flag to indicate if we compute Cp using dH_T/dT logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index f6cb0be9c..369c7bfd7 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -232,7 +232,7 @@ subroutine solveByIDA( & logical(lgt) :: tooMuchMelt logical(lgt) :: divideLayer logical(lgt) :: mergedLayers - logical(lgt),parameter :: checkSnow = .false. + logical(lgt),parameter :: checkSnow = .true. logical(lgt),parameter :: offErrWarnMessage = .true. real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) From 5a29308f5d0a513c9c5d8a9b217c81ac5c81cb35 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 8 Jul 2021 09:40:13 -0600 Subject: [PATCH 0189/1472] minor edit --- build/source/engine/eval8DAE.f90 | 2 +- build/source/engine/solveByIDA.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index 98904d273..5ccce4059 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -266,7 +266,7 @@ subroutine eval8DAE(& real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step - logical(lgt),parameter :: enthalpyFD=.false. ! flag to indicate if we compute Cp using dH_T/dT + logical(lgt),parameter :: enthalpyFD=.true. ! flag to indicate if we compute Cp using dH_T/dT logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 369c7bfd7..f6cb0be9c 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -232,7 +232,7 @@ subroutine solveByIDA( & logical(lgt) :: tooMuchMelt logical(lgt) :: divideLayer logical(lgt) :: mergedLayers - logical(lgt),parameter :: checkSnow = .true. + logical(lgt),parameter :: checkSnow = .false. logical(lgt),parameter :: offErrWarnMessage = .true. real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) From 07686abb4783da8befc0dd327f3b15ca2e8f9efb Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Thu, 8 Jul 2021 10:03:20 -0600 Subject: [PATCH 0190/1472] computing H_T for the first step in systemSolvSundials --- build/source/engine/systemSolvSundials.f90 | 47 +++++++++++++++++++--- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index dbfe94149..4359799f0 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -136,7 +136,7 @@ subroutine systemSolvSundials(& USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy USE tol4IDA_module,only:popTol4IDA ! pop tolerances USE solveByIDA_module,only:solveByIDA ! solve DAE by IDA -! use varExtrSundials_module, only:countDiscontinuity + USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy use, intrinsic :: iso_c_binding implicit none ! --------------------------------------------------------------------------------------- @@ -219,10 +219,21 @@ subroutine systemSolvSundials(& ! model decisions ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) - ! check the need to merge snow layers - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + ! enthalpy + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) + ! model state variables + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variables (snow and soil domains) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) airtemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) @@ -318,6 +329,32 @@ subroutine systemSolvSundials(& ! try to accelerate solution for energy if(ixCasNrg/=integerMissing) stateVecTrial(ixCasNrg) = stateVecInit(ixCasNrg) + (airtemp - stateVecInit(ixCasNrg))*tempAccelerate if(ixVegNrg/=integerMissing) stateVecTrial(ixVegNrg) = stateVecInit(ixVegNrg) + (airtemp - stateVecInit(ixVegNrg))*tempAccelerate + + + ! compute H_T at the beginning of the data step + call t2enthalpy_T(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTemp, & ! intent(in): value of canopy air temperature (K) + scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) + scalarCanopyWat, & ! intent(in): value of canopy total water (kg m-2) + scalarCanopyIce, & ! intent(in): value for canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTemp, & ! intent(in): vector of layer temperature (K) + mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) + mLayerMatricHead, & ! intent(in): vector of total water matric potential (m) + mLayerVolFracIce, & ! intent(in): vector of volumetric fraction of ice (-) + ! output: enthalpy + scalarCanairEnthalpy, & ! intent(out): temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,cmessage) ! intent(out): error control +if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! compute the flux and the residual vector for a given state vector ! NOTE 1: The derivatives computed in eval8summa are used to calculate the Jacobian matrix for the first iteration From 0b26b87f0fc1ad7e05927ab09fbfc892ae6006df Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 13 Jul 2021 10:18:53 -0600 Subject: [PATCH 0191/1472] minor edit --- build/source/engine/eval8DAE.f90 | 26 +++++++++++++------------- build/source/engine/solveByIDA.f90 | 6 +++--- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index 5ccce4059..8ddbd4dc4 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -524,16 +524,16 @@ subroutine eval8DAE(& computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux canopyDepth, & ! intent(in): canopy depth (m) ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + diag_data, & ! intent(in): model diagnostic variables for a local HRU ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value for canopy ice content (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) - scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) + scalarCanopyIceTrial, & ! intent(in): trial value for canopy ice content (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) mLayerTempTrial, & ! intent(in): trial temperature @@ -546,10 +546,10 @@ subroutine eval8DAE(& ! output: error control err,message) ! intent(out): error control ! to conserve energy compute finite difference approximation of (theta_ice)' - scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur - do concurrent (iLayer=1:nLayers) - mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur - end do + ! scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur + ! do concurrent (iLayer=1:nLayers) + ! mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur + ! end do else call computHeatCapAnalytic(& ! input: control variables diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index f6cb0be9c..a61258773 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -232,8 +232,8 @@ subroutine solveByIDA( & logical(lgt) :: tooMuchMelt logical(lgt) :: divideLayer logical(lgt) :: mergedLayers - logical(lgt),parameter :: checkSnow = .false. - logical(lgt),parameter :: offErrWarnMessage = .true. + logical(lgt),parameter :: checkSnow = .true. + logical(lgt),parameter :: offErrWarnMessage = .false. real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) real(rkind) :: mLayerDepth(nLayers) @@ -518,7 +518,7 @@ subroutine solveByIDA( & eqns_data%fluxVec, & ! intent(out): flux vector eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation rVec, & ! intent(out): residual vector - eqns_data%err,eqns_data%message) ! intent(out): error control + eqns_data%err,eqns_data%message) ! intent(out): error control ! sum of fluxes From a7864fe3784d7da931c54fb18d92f53dd2897d13 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Tue, 13 Jul 2021 10:28:51 -0600 Subject: [PATCH 0192/1472] if statement for dt_cur too small in eval8DAE --- build/source/engine/eval8DAE.f90 | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index 8ddbd4dc4..94fe5dc2b 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -546,10 +546,12 @@ subroutine eval8DAE(& ! output: error control err,message) ! intent(out): error control ! to conserve energy compute finite difference approximation of (theta_ice)' - ! scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur - ! do concurrent (iLayer=1:nLayers) - ! mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur - ! end do + if(dt_cur > 1e-14_rkind) then + scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur + do concurrent (iLayer=1:nLayers) + mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur + end do + endif ! if dt_cur is not too samll else call computHeatCapAnalytic(& ! input: control variables From 78850e86885fa96fe24ed1a4bc861ecff2134820 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 14 Jul 2021 09:30:13 -0600 Subject: [PATCH 0193/1472] canopyLiqPrev updated at the end of each substep --- build/source/engine/solveByIDA.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index a61258773..9a994a331 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -538,6 +538,7 @@ subroutine solveByIDA( & ! save required quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial + eqns_data%scalarCanopyLiqPrev = eqns_data%scalarCanopyLiqTrial eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) From 761efc109b8e6b0f2e8c4058c1d332fae93e4ab0 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 14 Jul 2021 10:57:04 -0600 Subject: [PATCH 0194/1472] evalJac4IDA edited --- build/source/engine/evalJac4IDA.f90 | 57 ++++++++++++++--------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/build/source/engine/evalJac4IDA.f90 b/build/source/engine/evalJac4IDA.f90 index 8f5e94218..dbcfbb1a2 100644 --- a/build/source/engine/evalJac4IDA.f90 +++ b/build/source/engine/evalJac4IDA.f90 @@ -27,7 +27,7 @@ module evalJac4IDA_module contains ! ********************************************************************************************************** - ! public function evalJac4IDA: compute the residual vector F(t,y,y') required for FIDA solver + ! public function evalJac4IDA: the interface to compute the Jacobian matrix dF/dy + c dF/dy' for IDA solver ! ********************************************************************************************************** ! Return values: ! 0 = success, @@ -46,28 +46,28 @@ integer(c_int) function evalJac4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & use fsunmatrix_dense_mod use nrtype use type4IDA - use eval8JacDAE_module,only:eval8JacDAE + use eval8JacDAE_module,only:eval8JacDAE ! compute Jacobian matrix !======= Declarations ========= implicit none ! calling variables - real(rkind), value :: t ! current time - real(rkind), value :: cj ! step size scaling factor - type(N_Vector) :: sunvec_y ! solution N_Vector - type(N_Vector) :: sunvec_yp ! derivative N_Vector - type(N_Vector) :: sunvec_r ! residual N_Vector - type(SUNMatrix) :: sunmat_J ! Jacobian SUNMatrix - type(c_ptr), value :: user_data ! user-defined data - type(N_Vector) :: sunvec_temp1 ! temporary N_Vectors - type(N_Vector) :: sunvec_temp2 - type(N_Vector) :: sunvec_temp3 + real(rkind), value :: t ! current time + real(rkind), value :: cj ! step size scaling factor + type(N_Vector) :: sunvec_y ! solution N_Vector + type(N_Vector) :: sunvec_yp ! derivative N_Vector + type(N_Vector) :: sunvec_r ! residual N_Vector + type(SUNMatrix) :: sunmat_J ! Jacobian SUNMatrix + type(c_ptr), value :: user_data ! user-defined data + type(N_Vector) :: sunvec_temp1 ! temporary N_Vector + type(N_Vector) :: sunvec_temp2 ! temporary N_Vector + type(N_Vector) :: sunvec_temp3 ! temporary N_Vector ! pointers to data in SUNDIALS vectors - real(rkind), pointer :: stateVec(:) - real(rkind), pointer :: stateVecPrime(:) - real(rkind), pointer :: rVec(:) - real(rkind), pointer :: Jac(:,:) - type(eqnsData), pointer :: eqns_data ! equations data + real(rkind), pointer :: stateVec(:) ! state vector + real(rkind), pointer :: stateVecPrime(:)! derivative of the state vector + real(rkind), pointer :: rVec(:) ! residual vector + real(rkind), pointer :: Jac(:,:) ! Jacobian matrix + type(eqnsData), pointer :: eqns_data ! equations data @@ -82,20 +82,16 @@ integer(c_int) function evalJac4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & stateVecPrime => FN_VGetArrayPointer(sunvec_yp) rVec => FN_VGetArrayPointer(sunvec_r) Jac(1:eqns_data%nState, 1:eqns_data%nState) => FSUNDenseMatrix_Data(sunmat_J) - - - - ! compute the flux and the residual vector for a given state vector - ! NOTE 1: The derivatives computed in eval8DAE are used to calculate the Jacobian matrix for the first iteration - ! NOTE 2: The Jacobian matrix together with the residual vector is used to calculate the first iteration increment + + ! compute Jacobian matrix call eval8JacDAE(& ! input: model control - cj, & ! intent(in) - eqns_data%dt, & ! intent(in) + cj, & ! intent(in): this scalar changes whenever the step size or method order changes + eqns_data%dt, & ! intent(in): data step eqns_data%nSnow, & ! intent(in): number of snow layers eqns_data%nSoil, & ! intent(in): number of soil layers eqns_data%nLayers, & ! intent(in): number of layers - eqns_data%ixMatrix, & ! intent(in) + eqns_data%ixMatrix, & ! intent(in): type of matrix (dense or banded) eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors @@ -107,18 +103,19 @@ integer(c_int) function evalJac4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & eqns_data%mpar_data, & ! intent(in): model parameters eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU ! input-output: data structures - eqns_data%indx_data, & ! intent(inou): index data + eqns_data%indx_data, & ! intent(inou): index data eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! input: baseflow eqns_data%dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) ! output - eqns_data%dMat, & ! intetn(inout) - Jac, & ! intent(out): jacobain matrix - eqns_data%err,eqns_data%message) ! intent(out): error control + eqns_data%dMat, & ! intetn(inout):diagonal of the Jacobian matrix + Jac, & ! intent(out): jacobain matrix + eqns_data%err,eqns_data%message) ! intent(out): error control if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif + ! return success ierr = 0 return From 8a1008583f53b367729b36133c112556843ead38 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 14 Jul 2021 12:30:03 -0600 Subject: [PATCH 0195/1472] minor edit --- build/source/engine/computJacDAE.f90 | 62 ++++++++++++++-------------- build/source/engine/eval8JacDAE.f90 | 50 +++++++++++----------- build/source/engine/evalJac4IDA.f90 | 8 ++-- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index ae9f0b9e7..e52233dd1 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -81,7 +81,7 @@ module computJacDAE_module implicit none ! define constants real(rkind),parameter :: verySmall=tiny(1.0_rkind) ! a very small number -integer(i4b),parameter :: ixBandOffset=kl+ku+1 ! offset in the band Jacobian matrix +integer(i4b),parameter :: ixBandOffset=kl+ku+1 ! offset in the band Jacobian matrix private public::computJacDAE @@ -92,7 +92,7 @@ module computJacDAE_module ! ********************************************************************************************************** subroutine computJacDAE(& ! input: model control - cj, & ! intent(in) + cj, & ! intent(in): this scalar changes whenever the step size or method order changes dt, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -100,9 +100,9 @@ subroutine computJacDAE(& computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation computeBaseflow, & ! intent(in): flag to indicate if we need to compute baseflow ixMatrix, & ! intent(in): form of the Jacobian matrix - specificStorage, & ! intent(in) - theta_sat, & ! intent(in) - ixRichards, & ! intent(in): choice of option for Richards' equation + specificStorage, & ! intent(in): specific storage coefficient (m-1) + theta_sat, & ! intent(in): soil porosity (-) + ixRichards, & ! intent(in): choice of option for Richards' equation ! input: data structures indx_data, & ! intent(in): index data prog_data, & ! intent(in): model prognostic variables for a local HRU @@ -110,7 +110,7 @@ subroutine computJacDAE(& deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) ! input: state variables - mLayerTemp, & ! intent(in) + mLayerTemp, & ! intent(in): vector of layer temperature (K) mLayerTempPrime, & ! intent(in) mLayerMatricHeadPrime, & ! intent(in) mLayerMatricHeadLiqPrime, & ! intent(in) @@ -132,22 +132,22 @@ subroutine computJacDAE(& implicit none ! input: model control real(rkind),intent(in) :: cj - real(rkind),intent(in) :: dt ! length of the time step (seconds) - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: computeBaseflow ! flag to indicate if computing baseflow - integer(i4b),intent(in) :: ixMatrix ! form of the Jacobian matrix - real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) - real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) - integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation + real(rkind),intent(in) :: dt ! length of the time step (seconds) + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: computeBaseflow ! flag to indicate if computing baseflow + integer(i4b),intent(in) :: ixMatrix ! form of the Jacobian matrix + real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) + real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) + integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation ! input: data structures - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! input: state variables real(rkind),intent(in) :: mLayerTemp(:) real(rkind),intent(in) :: mLayerTempPrime(:) @@ -166,21 +166,21 @@ subroutine computJacDAE(& real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix real(rkind),intent(out) :: aJac(:,:) ! Jacobian matrix ! output variables - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! -------------------------------------------------------------- ! * local variables ! -------------------------------------------------------------- ! indices of model state variables - integer(i4b) :: jState ! index of state within the state subset - integer(i4b) :: qState ! index of cross-derivative state variable for baseflow - integer(i4b) :: nrgState ! energy state variable - integer(i4b) :: watState ! hydrology state variable - integer(i4b) :: nState ! number of state variables + integer(i4b) :: jState ! index of state within the state subset + integer(i4b) :: qState ! index of cross-derivative state variable for baseflow + integer(i4b) :: nrgState ! energy state variable + integer(i4b) :: watState ! hydrology state variable + integer(i4b) :: nState ! number of state variables ! indices of model layers - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: jLayer ! index of model layer within the full state vector (hydrology) - integer(i4b) :: pLayer ! indices of soil layers (used for the baseflow derivatives) + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: jLayer ! index of model layer within the full state vector (hydrology) + integer(i4b) :: pLayer ! indices of soil layers (used for the baseflow derivatives) ! conversion factors real(rkind) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative ! -------------------------------------------------------------- diff --git a/build/source/engine/eval8JacDAE.f90 b/build/source/engine/eval8JacDAE.f90 index 4894c114d..160f4bfe0 100644 --- a/build/source/engine/eval8JacDAE.f90 +++ b/build/source/engine/eval8JacDAE.f90 @@ -88,13 +88,13 @@ module eval8JacDAE_module ! public subroutine eval8JacDAE: compute the residual vector and the Jacobian matrix ! ********************************************************************************************************** subroutine eval8JacDAE(& - ! input: model control - cj, & ! intent(in) + ! input: model control + cj, & ! intent(in): this scalar changes whenever the step size or method order changes dt, & ! intent(in): time step nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers - ixMatrix, & ! intent(in) + ixMatrix, & ! intent(in): form of the Jacobian matrix computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors @@ -110,9 +110,9 @@ subroutine eval8JacDAE(& diag_data, & ! intent(inout): model diagnostic variables for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! input: baseflow - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors - dMat, & ! intent(inou): diagonal of Jacobian Matrix + dMat, & ! intent(inout): diagonal of Jacobian Matrix Jac, & ! intent(out): jacobian matrix err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- @@ -125,34 +125,34 @@ subroutine eval8JacDAE(& ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control - real(rkind),intent(in) :: cj + real(rkind),intent(in) :: cj ! this scalar changes whenever the step size or method order changes real(rkind),intent(in) :: dt ! time step - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors - real(rkind),intent(in) :: stateVec(:) ! model state vector + real(rkind),intent(in) :: stateVec(:) ! model state vector real(rkind),intent(in) :: stateVecPrime(:) ! model state vector - real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) ! input: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(var_dlength), intent(in) :: mpar_data ! model parameters - type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(var_dlength), intent(in) :: mpar_data ! model parameters + type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU ! output: data structures - type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! output: Jacobian real(rkind), intent(inout) :: dMat(:) - real(rkind), intent(out) :: Jac(:,:) ! jacobian matrix + real(rkind), intent(out) :: Jac(:,:) ! jacobian matrix ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- @@ -186,7 +186,7 @@ subroutine eval8JacDAE(& real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! derivative value for volumetric fraction of liquid water (-) real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! derivative value for volumetric fraction of ice (-) ! other local variables - character(LEN=256) :: cmessage ! error message of downwind routine + character(LEN=256) :: cmessage ! error message of downwind routine real(rkind) :: dt1 real(rkind) :: mLayerd2Theta_dTk2(nLayers) real(rkind) :: d2VolTot_d2Psi0(nLayers) @@ -319,7 +319,7 @@ subroutine eval8JacDAE(& dt1 = 1._qp call computJacDAE(& ! input: model control - cj, & ! intent(in) + cj, & ! intent(in): this scalar changes whenever the step size or method order changes dt1, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers diff --git a/build/source/engine/evalJac4IDA.f90 b/build/source/engine/evalJac4IDA.f90 index dbcfbb1a2..5ebecd99b 100644 --- a/build/source/engine/evalJac4IDA.f90 +++ b/build/source/engine/evalJac4IDA.f90 @@ -107,11 +107,11 @@ integer(c_int) function evalJac4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! input: baseflow - eqns_data%dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + eqns_data%dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) ! output - eqns_data%dMat, & ! intetn(inout):diagonal of the Jacobian matrix - Jac, & ! intent(out): jacobain matrix - eqns_data%err,eqns_data%message) ! intent(out): error control + eqns_data%dMat, & ! intetn(inout): diagonal of the Jacobian matrix + Jac, & ! intent(out): Jacobain matrix + eqns_data%err,eqns_data%message) ! intent(out): error control if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif From d7e3f6bce3a5c1adb213440a53fc10548b2d9be7 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 14 Jul 2021 16:10:21 -0600 Subject: [PATCH 0196/1472] minor edit --- build/source/engine/eval8DAE.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index 94fe5dc2b..98c964c2e 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -209,7 +209,7 @@ subroutine eval8DAE(& real(rkind),intent(in) :: scalarCanopyLiqPrev ! previous value for mass of ice on the vegetation canopy (kg m-2) real(rkind),intent(out) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) real(rkind),intent(in) :: scalarCanopyEnthalpyPrev ! previous value of enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) real(rkind),intent(in) :: mLayerTempPrev(:) real(rkind),intent(out) :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial value for total water matric potential (m) @@ -266,7 +266,7 @@ subroutine eval8DAE(& real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step - logical(lgt),parameter :: enthalpyFD=.true. ! flag to indicate if we compute Cp using dH_T/dT + logical(lgt),parameter :: enthalpyFD=.false. ! flag to indicate if we compute Cp using dH_T/dT logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains From 1c9153165ced762519167d1aead33c0d5bd1dcac Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 28 Jul 2021 12:51:11 -0600 Subject: [PATCH 0197/1472] printResidDAE subroutine added to computResidDAE for debugging --- build/source/engine/computResidDAE.f90 | 98 +++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 2 deletions(-) diff --git a/build/source/engine/computResidDAE.f90 b/build/source/engine/computResidDAE.f90 index 790d417db..35585ead2 100644 --- a/build/source/engine/computResidDAE.f90 +++ b/build/source/engine/computResidDAE.f90 @@ -49,7 +49,7 @@ module computResidDAE_module iden_water ! intrinsic density of liquid water (kg m-3) ! privacy implicit none -private +private::printResidDAE public::computResidDAE contains @@ -240,9 +240,9 @@ subroutine computResidDAE(& !print*, 'PAUSE:'; read(*,*) endif - ! print *, 'rVec = ', rVec(indx_data%var(iLookINDEX%ixMatOnly)%dat) ! check if(any(isNan(rVec)))then + call printResidDAE(nSnow,nSoil,nLayers,indx_data,rAdd,rVec) message=trim(message)//'we found NaN' err=20; return endif @@ -252,4 +252,98 @@ subroutine computResidDAE(& end subroutine computResidDAE + + ! ********************************************************************************************************** + ! private subroutine printResidDAE: print the residual vector mainly for debugging + ! ********************************************************************************************************** + subroutine printResidDAE( & + ! input: model control + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + ! input: data structures + indx_data, & ! intent(in): index data + ! output + rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec) ! intent(out): residual vector + +! -------------------------------------------------------------------------------------------------------------------------------- +implicit none +! input: model control +integer(i4b),intent(in) :: nSnow ! number of snow layers +integer(i4b),intent(in) :: nSoil ! number of soil layers +integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain +type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers +! output +real(rkind),intent(in) :: rAdd(:) ! additional (sink) terms on the RHS of the state equation +real(qp),intent(in) :: rVec(:) ! NOTE: qp ! residual vector +! -------------------------------------------------------------------------------------------------------------------------------- +! local variables +! -------------------------------------------------------------------------------------------------------------------------------- +integer(i4b) :: iLayer ! index of layer within the snow+soil domain +! -------------------------------------------------------------------------------------------------------------------------------- +! link to the necessary variables for the residual computations +associate(& +! number of state variables of a specific type +nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain +nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain +nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain +! model indices +ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable +ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable +ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) +ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer +ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain +ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain +ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the soil subdomain +ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) +ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain +ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of hydrology states in snow+soil domain +layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] named variables defining the type of layer in snow+soil domain +) ! association to necessary variables for the residual computations +! -------------------------------------------------------------------------------------------------------------------------------- + + +if(ixVegNrg/=integerMissing) print *, 'rAdd(ixVegNrg) = ', rAdd(ixVegNrg) + +if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) + select case( layerType(iLayer) ) + case(iname_snow) + print *, 'rAdd( ixSnowSoilNrg(iLayer) ) = ', rAdd( ixSnowSoilNrg(iLayer) ) + case(iname_soil); print *, 'rAdd( ixSnowSoilNrg(iLayer) ) = ', rAdd( ixSnowSoilNrg(iLayer) ) + end select + end do +endif + +if(nSoilOnlyHyd>0)then + do concurrent (iLayer=1:nSoil,ixSoilOnlyHyd(iLayer)/=integerMissing) + print *, 'rAdd( ixSoilOnlyHyd(iLayer) ) = ', rAdd( ixSoilOnlyHyd(iLayer) ) + end do +endif + +if(ixCasNrg/=integerMissing) print *, 'rVec(ixCasNrg) = ', rVec(ixCasNrg) +if(ixVegNrg/=integerMissing) print *, 'rVec(ixVegNrg) = ', rVec(ixVegNrg) +if(ixVegHyd/=integerMissing)then + print *, 'rVec(ixVegHyd) = ', rVec(ixVegHyd) +endif + +if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) + print *, 'rVec( ixSnowSoilNrg(iLayer) ) = ', rVec( ixSnowSoilNrg(iLayer) ) + end do +endif + +if(nSnowSoilHyd>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) + print *, 'rVec( ixSnowSoilHyd(iLayer) ) = ', rVec( ixSnowSoilHyd(iLayer) ) + end do +endif + +if(ixAqWat/=integerMissing) print *, ' rVec(ixAqWat) = ', rVec(ixAqWat) + +end associate + +end subroutine printResidDAE + end module computResidDAE_module From 1016f299cfcfb6c75e7486c19b762b5f4a995b66 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Wed, 28 Jul 2021 13:03:08 -0600 Subject: [PATCH 0198/1472] error message in eval8DAE fixed --- build/source/engine/eval8DAE.f90 | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index 98c964c2e..b9c7ff1fb 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -515,7 +515,7 @@ subroutine eval8DAE(& mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) ! output: error control err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! *** compute volumetric heat capacity C_p = dH_T/dT call computHeatCap(& @@ -544,7 +544,8 @@ subroutine eval8DAE(& heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil ! output: error control - err,message) ! intent(out): error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! to conserve energy compute finite difference approximation of (theta_ice)' if(dt_cur > 1e-14_rkind) then scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur @@ -569,7 +570,7 @@ subroutine eval8DAE(& heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow ! output: error control - err,message) ! intent(out): error control + err,cmessage) ! intent(out): error control endif ! compute multiplier of state vector @@ -599,7 +600,7 @@ subroutine eval8DAE(& indx_data, & ! intent(in): model layer indices prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(inout): model diagnostic variables for a local HRU - err,message) ! intent(out): error control + err,cmessage) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if end if ! updateCp @@ -620,7 +621,7 @@ subroutine eval8DAE(& ! output scalarCanopyCmTrial, & ! intent(out): Cm for vegetation mLayerCmTrial, & ! intent(out): Cm for soil and snow - err,message) ! intent(out): error control + err,cmessage) ! intent(out): error control else scalarCanopyCmTrial = 0._qp mLayerCmTrial = 0._qp From d170feb9d30566bc67967e63ec4e16bb930c15c8 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 30 Jul 2021 09:44:53 -0600 Subject: [PATCH 0199/1472] minor edit on computJacDAE --- build/source/engine/computJacDAE.f90 | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index e52233dd1..c32ad0fb6 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -270,7 +270,7 @@ subroutine computJacDAE(& ! canopy and layer depth canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] named variables defining the type of layer in snow+soil domain + layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] named variables defining the type of layer in snow+soil domain ) ! making association with data in structures ! -------------------------------------------------------------- ! initialize error control @@ -316,7 +316,6 @@ subroutine computJacDAE(& ! compute additional terms for the Jacobian for the soil domain (excluding fluxes) do iLayer=1,nSoil -! print *, '2' if(ixSoilOnlyHyd(iLayer)/=integerMissing)then dMat(ixSoilOnlyHyd(iLayer)) = ( dVolTot_dPsi0(iLayer) + dCompress_dPsi(iLayer) ) * cj + d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) @@ -706,8 +705,7 @@ subroutine computJacDAE(& nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector if(nrgstate/=integerMissing)then ! (energy state for the current layer is within the state subset) - ! (cross-derivative terms for the current layer) - ! print *, '3' + ! (cross-derivative terms for the current layer) aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_ice * cj & + LH_fus*iden_ice * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) @@ -805,8 +803,7 @@ subroutine computJacDAE(& ! melt-freeze: compute derivative in energy with respect to mass if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present - ! print *, '4' - aJac(nrgState,watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water*cj - LH_fus*iden_water * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) ! reza not sure about this ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + aJac(nrgState,watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water*cj - LH_fus*iden_water * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content else aJac(nrgState,watState) = 0._rkind endif @@ -842,8 +839,6 @@ subroutine computJacDAE(& end select ! type of matrix if(any(isNan(aJac)))then - print *, 'Nan in computeJacobSundials' - stop 1 message=trim(message)//'we found NaN' err=20; return endif From 6a59f69caafdf6a66fe3a85e4c5a52f3b2ffbc40 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 1 Aug 2021 13:35:08 -0600 Subject: [PATCH 0200/1472] documentation in sundials directory --- build/source/engine/eval8DAE.f90 | 2 +- build/source/engine/eval8JacDAE.f90 | 2 +- build/source/engine/evalDAE4IDA.f90 | 2 +- build/source/engine/solveByIDA.f90 | 4 ++-- ida-install/ida-5.4.0.tar.gz | Bin 3456987 -> 0 bytes ida-install/readme.md | 20 -------------------- sundials/flags_params.txt | 19 +++++++++++++++++++ sundials/installation.txt | 17 +++++++++++++++++ 8 files changed, 41 insertions(+), 25 deletions(-) delete mode 100644 ida-install/ida-5.4.0.tar.gz delete mode 100644 ida-install/readme.md create mode 100644 sundials/flags_params.txt create mode 100644 sundials/installation.txt diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index b9c7ff1fb..e5966776b 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -85,7 +85,7 @@ module eval8DAE_module contains ! ********************************************************************************************************** - ! public subroutine eval8DAE: compute the residual vector and the Jacobian matrix + ! public subroutine eval8DAE: compute the residual vector ! ********************************************************************************************************** subroutine eval8DAE(& ! input: model control diff --git a/build/source/engine/eval8JacDAE.f90 b/build/source/engine/eval8JacDAE.f90 index 160f4bfe0..ff61b8aba 100644 --- a/build/source/engine/eval8JacDAE.f90 +++ b/build/source/engine/eval8JacDAE.f90 @@ -85,7 +85,7 @@ module eval8JacDAE_module contains ! ********************************************************************************************************** - ! public subroutine eval8JacDAE: compute the residual vector and the Jacobian matrix + ! public subroutine eval8JacDAE: compute the Jacobian matrix ! ********************************************************************************************************** subroutine eval8JacDAE(& ! input: model control diff --git a/build/source/engine/evalDAE4IDA.f90 b/build/source/engine/evalDAE4IDA.f90 index 168882e02..35ffc0a62 100644 --- a/build/source/engine/evalDAE4IDA.f90 +++ b/build/source/engine/evalDAE4IDA.f90 @@ -32,7 +32,7 @@ module evalDAE4IDA_module contains ! ********************************************************************************************************** - ! public function evalDAE4IDA: compute the residual vector F(t,y,y') required for FIDA solver + ! public function evalDAE4IDA: compute the residual vector F(t,y,y') required for IDA solver ! ********************************************************************************************************** ! Return values: ! 0 = success, diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 9a994a331..06ef031bb 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -89,7 +89,7 @@ module solveByIDA_module contains !------------------- - ! * subroutine solveByIDA: solve F(y,y') = 0 by FIDA (y is the state vector) + ! * public subroutine solveByIDA: solve F(y,y') = 0 by IDA (y is the state vector) ! ------------------ subroutine solveByIDA( & dt, & ! intent(in): data time step @@ -714,7 +714,7 @@ subroutine setInitialCondition(neq, y, sunvec_u, sunvec_up) end subroutine setInitialCondition ! ---------------------------------------------------------------- -! setSolverParams: routine to set paprmeters in ida solver +! setSolverParams: private routine to set paprmeters in ida solver ! ---------------------------------------------------------------- subroutine setSolverParams(dt,ida_mem,retval) !======= Inclusions =========== diff --git a/ida-install/ida-5.4.0.tar.gz b/ida-install/ida-5.4.0.tar.gz deleted file mode 100644 index bc40222bb03883b9cc8264ec40e58d71683e5113..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3456987 zcmV(xKhsfZRrAw^MH5=pcu6|%G`N|s~~Eh36!DNFS~uWM{|-|zF>@9*=z z@9+Qr{NMRJcdogv^E!|9JdW>ioaa15JWg6!MoC6a){>aJ6sVFHy zEOIKUN~+2b{9R5#iTPJfPF`MBMG=x$R8>?^RZvipSApc@mE`3WSRlFoa!&r{!=TY| zRKSV{jzpo!|HtFNFfXrvF%MH1X#Vg2M?PC1TRRIqV~m-tI=CXz2@u5#vY?PjL^1(K zg>Ym%q-X5qMW7PMbRv!CWXP663M5cz&=%B|En6Uaa0%Z< zoC=b#B?LeUatd;i5O|0}NlEE~i3n84i$aAk0Rbc;0T0=F65-jsz@)kq{{RM^NcKiU zX1E|g)RO?25yABS6er6ciO`ng}laiDWQ{1m*+E^Jh-s zMWy&7LNI6qDjLF(NR%Mt8n8u~V~EB8iVlH?5veFj5E;Vb=s1W*r!qY03@T6ygN!Fo zflRXsnNt@U5tunAY%eff2(%y?ppODthN(#-Bw$!COzRMW14tAiok<0@f+xk_A66fO?8$T~B0WR~g@QSo_a+(xUJihD8ZaFVb|_dm zUKCg#@Emijs{^-&FgB)^diszwqNq8afh`34A@FT4H0(LESFkX^3+P1HK_I5AfN4B& zVWBN7FIotVPVlEe5@3Fsm2tf06VjwUL_>Lom z06RbuU?f=6c&0<(eJS8Am@?{Lv-%ITxWFnp;5TL^0wm}7HP*xYz3vthmBx|&5+moy z>tUGWQGb#*Kq!ZZ!!+dV=fv;8)grw=XwPH03}`5|)*&c+5LI6~eG zA{^*ZC=$cF*}RtO-&K}d!J(F*8*OqiR82;wG4N(|u2^N|uqUoaYy zqiE`oIWZWXm_lM2m`n(Q0-1>z(b;@V^T&mNm`NmoAWVU9ffOPhMPqpSAot)@fQBd@ zbQ~-TiG(C0Du}dm!hk>u^F+gevzAkZ4l128~g$}lgfivh;kRlfRAS%%Vc@^BHf7L0iFj?K4grRXc%6?>x|SAL8)F?itg(e5yiyi_ z2W6WikcKq7CT$2zMH>P6;Z+37Yy3M6IP8GJ@fq8?ah1RRJTvrAi?wrKS;Qw_X$fY|}gq0TbpGt1QC6@;22Gta~yB8Wh^fB*u} zT<|>21xiezF$}s7jM8CYe*Qe9_kckYW0~eHNlpYR*IrzIF$i8G4 zq+>`xzq&F&GBO^3ewq&&(gT>5K!R*!fY>2q5XEP)w=RxKAwjk>kOPH+htszV%7K|A z{2&_~o&nhss2(_GFoX*SxQ4<3?%;PlsUR45B8h`WU*uANRiH0RplvF&Siav}0HAyl z15Z$gOhB-w0hax}f!NYIARsQP%=BR?$$mgeD+25d7T`M-6c}j8kN|)>0n!NpWd$0t zVfYgKD3GoXSY;rP;?SVvqtNM)o(#~I4*`tF06~FL0JbIq6`@QCaJU208&ZKdU^pFE zj0~B{KxPCmpREsp1S|!ArFi-QNDqdXP<+4u3doBj{JcWVJq@V@ctTqe4xYdYI2BN> zE`_CR# zF$iNmuti~$g6~*d2#Ep)7y+-Q5@91_K~Y2i4hD$&P=Uug_<*7Ujl#k!+X=o6o63$v zCHfIydf=8funtm5>k^UmKW`JHfClEr(fnrN!{W>itmg^WD}UA~0Snk9fZ;&`9)Y+3 z3Jz6Ngg_95>Iaf503Aq@nePW6WzAoG^-oj(8(YC{iekDcVitJhf+qjo$sq+fIc1r} zoe2iBGl3%i)|pt0U1k3cyE3a#83?0m4q*X%;L;paxnOFzH6Vjhlt)8~NRt92S$NIE zgaKq#R8f>x+^!-6=~)_U!GRpaaeS~0or1hD|bc7Gh6fciI_z}*@vW)QXj z>01gE9n9npr$+|uH{WCy;AFVTK%~NmkqU|rX557V39uPJU~rI^Ajo*jfDnSQ!swa; z=P|QG!w8ZxE3Yo+HXJ!@ZSV?48G zMWCv~>XTNK)C$RAl$@N=-H z0H*h3;>p>y3WqE@i1zU62U!tbmw09~kT!?;;YKL*2ZVuRmlpwtG$>^jF$w1`FafxW zL?Dm<>nI-R>2E{rVmP%p?Ebq1fcjSn0Q%<%0K2#VMC=trgc$|)!g+dvjEBT)_(fPt zfp9k#l*)gkDWs)=Kt%wT$tb3E7jl(O$ZRb~XEs&wvwpG&S3=$(z=Coa7y;=>z~wRw zmqGMI&6*#Whe(^(94w+NQW)V8UzlqP{WdRPaF!RCD6qk7q5^EqH+`9qXOX>ULGAA~ z%DgWw_8j110idc3pb~Katt}J|poj&e(X)Mn+1YT&ZAAGnNiZYeZr*i)do0{UtTd~! zxh;r4oDy*rSWcu11NjheupMa3eihvNf%QPA1kBP$7OfAwdWha|#~l8SLkb~Ki;>|@ z4a_TIW#$mL`H!T=+2#fc(1)9WuuJ|?fzLV}G6U1mm=er3%5k1ZpJkB){2_`2A|Fr- z{FlOjErN97i2$fVwqUIj@X`RVOM8T%=s0glrhj_OE)Y=lQJ77BAhnklalUl@vkmW} zH(SjCiBY-)DxK*y2pX_tiY%e0YbnWG%P^3e8}GR=7=d+?vw41gLg;U&L(Oe*$;-&2 zP`Z?W5OCWEh^s3J<3}Yl{I3d9Et(O@j9^ga1Bn*;v+zE|Z-pgF9zbO}j6r~`dQ=#Y zFafWglB%2>$`;%;0N~XV?(%>-0)vLOE6FQtN1^n=iVA^&I|$N%-C3`KD*?dNEJB`L zSOmBq=mD63i`4)alOc*E(ol2gXs*2m@D^jXF953-n2N}Z^2kOI0S}iai&XyiI%4Vu zga?50nVzvYI5IsAW}4Z#K|{t6(-+r@X2z(au5@H}gmFcXoPzzBQj%?8Hkqvnp?!9`>(L#Id3_v=G%$Uk7uVAUra|_G^EDnSMP?vuQZDp`RpuXvvM!gc5|>C@;sT;98 z5%}i9i@+seAO?qKN#K#lB>o^9!5Inh13bZtNCxqV#Eda;S>^Ft76+qdJ7Tc?U=qkA zzsXGb4;P;V4Ui=V!nHdHHps3HhKBUG88q6W4Ff%jryrae=Xd#MXZ9dbycd8Q{0$WD z@K6N3DS(1qR!&})c}p6G57NH@H|g2^cIo*%A-yP30If(f6RE5u3K0n87D$9dpu?~N zS<6f_%@xXUl}IPeh2=T;!qf$Vd+C$~1nRnA=DGLbq&SPr5qE=^$gCC?qb6jB90k0M z7>Gk^Vt8HCWJFLX5uK1fuma36Y+(eUpyw&k=uG&4kVp2Qs3@dT{!7Y*YXT|*d4Tlr z7wZqiZh099asrdiMUGin7skjkffgd{zlhOHr&)l}$P!ohr;{^dF`RS%h{a6Qg-j0j zrh(AN7S3NC5uv9D=wbgO8uP$#KD+>SVSUbq6{M*K0tueT3=Q(I+bbY)&ue`FKK*4i zGssLjU>PXQioGbJ{6%sE1}sF2zg?F&=6YT%?c4$q`J*yfXnw#24!j43oS>LTuYbbD zx%EdTFdxoacENFAuS0>jf*cc>Tgc3rK`^GlTSe;3R?A{^2zQ~ua6JkE()0yI=g!&s z4_?pO$Z#7%014*_Qf($&^ z9U0C1j=bXzvyIFJszq4+UPNyObN8LOltA^00tlP}M_Nx1gc0x$oDezKGcWt>UMhjS zD2^{y1MES}9vfVux=YjCfz6m9p4mZj_eRJg?|`QOyZ#^5{|o(}S+xIO++Tn{|EB+= zD5os7rMM33%`#=8^A7%@J*}9zTUH?|pcp^oOf;7mR+yC{UJX?Y%gUUQ~f`@ED zy$28#88Qbbk^)B{$c{`zIsouVD>$Mc1mMOvSX)S{2g~@+MgW}a-~~4us^B{w%ws+< zmVr^~?b*WA=^w33O!t<9Ci|%u|K)eFtW-P9-3< zIy@iF1E6=L`eJGiU_+o%G_%lx=8q;UCN>NG7C3={4hcu}2Lb2rH~^FuX<&|n0Az2L z@UNIa%>uChBolK{`nS^;V6NFN4YN4}VjVyp@R`MV9nT-HT+BWqkp;YAG@ECTcsw5f z=Qsvs2AqhOuZNLBDTW&L%ko62T`P$#`%VsZjtY{uDX_3*l{RI2*vFH6A%L zIj4+y@Hp3^fcF%Tlc=ELg58P=yAGM@L^K*x1gw#(SWjOM z(s6>YM*5JhrInM7v7r$bGO{$&)3>pOFcx~ye1qK*+z`Rof;UA_@B@s66Qu8GWutFv z3t8Gg#^zRL#$YlqyA8$yYpibz>SGICGdn$F3qv#nCV;y!keRW$F%}HNTB2d1sK34j zSsFm*`Zl^o;44PQ*vuH~gfL=YjJ1Gi831A!$O>bFHP*E=!`MJpb~aX)w)zkd3#Dgl zt80caHrLma0n`CSNZ(%H0t?w1Va&|_R!W%n-(=L$2TWpg%=A$RB_O4qv5mei78YmW zN*8Dj@MngGY_0Tljo}M@M|~g+#>NRfJEyI_wH+7)9-{Ox<`_dDo5a6S39yN-osGUZ z%rnq~t(}f7));Gt)rSl%E%jhUqipqU>_O54xYEqh7SWcStv(vk!(cH8VK4_g$p~5K8=4s#>RagQ!`xWHvp5*T4PlIpu`N8n zm`TvP!NB&m#=XCKk%QjqTK)DzyFC3Qra@hVrG{Nh7pSoDF9#?K=dTc zAHB7cv6X@J2ypukh6C`S41F?z>Ky`CEdT5`8?&<`dwMueI!aGp8l$VMAT6hAfl*LZ zR8m4V7=GsqCPLJDn` zIT&yf;4M7n2@4bv>3+dYCk6?RbVR}2@DDAJU$z9|kX|7r2yP!TQ9lCqk^U}FDlh?o zh-?ZVzy6p*U~q9upnAf`_~$vAWd~sbE*pSnqJYhTAK{re5zV~z$5H*z@WDQY7Y_Ly z2o-q+L90PTWVd5>@0v`2C&W>Sq!9SHAB|3=Gw6glYlI-XHM6LaxjO=PUjbvYJ37d; zUbsLC^0N{T4j+b~OC|-)ABJ|K;}%|7A4(RsNS(QC3t`mS4#K3UcuIA4TQ={r%_v$Vbdd&p=vU zMj0hm-}JZ@r2x3SAv)yl&k@f1fLX;H6Vd-p+|t5ASi8Z z_~&*6oIfhKondQE>4bR8rX2!qWuIYTe=ekIQDT1Ua#Q{lp?0Cx$HoQi!LelC5Nam$>YWQjbG7n zPek9E=t=J8yzjN^1&bu=2z9r+-P7<2`47vaa0+FO6D`5=gM^ai7cHm{`vmdIbk~l% z%kMS|*nTWle)CP{ph~;IMvQ`MP&@m9bw4({`0f2%mCjE)Vzln=`z5_=Q+o*#{g|`1 zEW;b_w5V)l+nQQMT8C}XHxvDtDw)b7Ft*-u^jxj=i^JAyJ>*ew*>B~qp+c@ySsY)g zf|BISq%)69`1R(9{!S-4GDcVN2H%O@FhG z)*B&zz)+y;#f7C??eB*BODx+eLhd^~fIB-pL^D{M-N--nZQJ1oZma`--6JRv!elx#0LeO9dcd0kC|O&E2OYU%0RMIi`4<@>kCL^*>O0B+&mvgB-5?1W znMJ>fft%|D8oXQQMxuCA+&oAO0`g<5+1IQ9*0W%q*PojS{-J{&5u_MJm>nCGtd0&P z7;*t|1(H`%0RO4FqGWC6PpZMZS^-#LOQ1t8kStga5O}U1OgBVwBJ$iu6;f12CesF= z`IVutU0LDZu8DI;&bY+M+~Gdl?Y*unLZZa#4nfUDZRfeD5#7WeODFSMJzHt3+1O_` z6P=|C9npzD>MkWuV1Io3`Sbhdch?Sd)Lr}aGnY`*aqx4Y_%Ds3p_gd~eoXBdKo53Z z8sGiwULEDrC!6WvjhE6CK0H^ZsEsEVwUma1?f=rgyS-&;UElD=*z~UU77I$9VAk`h zwmrq~wB?<;AMVxPUe|i%x%~JGZ7PeLtP*B^RZ}eB(v#a8qq=F47Hn~LfnMWo+B*#L8&^7FcPq%;34b3mMtU^5 z>B{EK2RNMGRFf$Vl`0)8t{0?Bywl`pdE(C!+p;Fyt0$HllIx0jr}oARc6KW8kK||p|sH% zr%O*iHOl~q#~$2j#i^In9re8jbIQ?e=%=j68;kzi!u8?nL~31wlOg@K2G-zjb{DSo zJ%|sz6KK%$L)c7{ww7(zwvz$R!#3?qBi?)-nx@Q};pJ-e{Ux(0BXl_~o<+2m7!-vzq~H_*pos(uS6H zIsAUscV#U({}q}YNuQ2ZEvvC+c|sF|C>z)^JhpNAB*>4bZD%QybVTiCA3Ex zO}MwJA%Uk-4ssmuF16BOJFLAon|?TU^J@38Eu9KeHEa!gWWr0;!z=vmpW6PWMaYCo z_xPfa&RW7cR>q_IGW?*!9fQ^87Ysi1ZxS@W!P#_FT&68s{3t~{?UB3s`rB@C!ltS+ zTs{TZF^khK$J!;9ubWsEceP@;@zN5dBi21qX8rGLd=*p$p@0MS_riD^m!6IAI8^O4 zL8_Fo`4+RpFX6&FVf6cRS4*}^trEKy%#o+^1GT<$Nz|oVNmr~hL{=Q#|2Z*={Txc` zpiA7g%Ij8BEV4d3gC01ey>eG$&x&lU`oP-nBVM_sMLgvA-2>;L?eUenZ#(-bo<3UI zfBmYBM|g+dBwM=}$|^baRlsE_^JDzn6@7j2=?eDSyVx!*&(VA6Q0SPn{I%L=HHT`C zgQ5x-&h`1?0~Oar>n+zU*`u@Sjju^fgVRal zf~l(Ck`c|uu-H}KE{AP8w=!1sTwuG$OWEDZp=90+S`98 z{zd7v1kyU2yi|$UGHt>Rsf5YCPlok7`;}?_ZMvMn*+Ho*S0Db^=ooa>PyI6KvQ1Rv zIZ2bJl04L}W_y=qpIpwdg}79-$Ps!LdT_suI(8<>cY8$%KCI*u^}+**=3UL5o!F>j zrozt?-K0JTaJJEpviRT`eo1*@wcIR$G5sI#E6f`ci9Q%KrHLOSQ0KO@cqn0?NcBso zf$j5$)DJ%Cr#VET!{53`g%{}amzMAdy^?d>IMAzZc&$dr_fUcj2cLHT`Q?)4JD-?6 z#7#YW9Z@e-6uoZU&GX$`3i?fVWvi^UJku5ZQ`CS~>b7+69(-bco}lKbThxz)@*s-#nq)(f}e^u?%iFK&K4oA%ZLRkW4WXHqgg24!NqmcvtuKZ@nH8x4F$gE>;O-?HaU)}od1OAOTRxS?} zA0u@`dDWfBwa0lg=Z)IfbtB}3L_FPPC?vQ4<<$%MHHMI#k*;T~iLtm2c z={aIcQnvb=Tdv)I-l6->es{dWW8Q<>?tF&*QqA}`Nh{@wf;zCTpFFv>wMMi^s7e|C zGJVU)yGyMzzc6|?Xqf{~>zaa#p-g8Z8yl;zFvvu|jCG@~R$^d@3+2=|lbuQ~tDMAi za?AH|V9EOwA4ZkG7VVH>gs8doOk#V>5~s@*o+#1d1dUTY_E>YTY)-w?>9OnZ6V&b6 zd{bOAsdAIA&l$^vcjH%2Lt2jVq|<>u&zi6yciRU{QrsW7E#F_U?UhrZ_3+N9JtI#; zk{+gg%`5t_TCd@ij(Dqer;_s^E8fYodi_Ack5vy0th;XB+Vt(zK)lry;mX66_2pO8 z;!~dzwFoc8IO9@9hRw1rT8idZj~YV0+C4|Eb+k8--egQw20W8%sy(DYFnbu&Vp(xrfkJn`KoS2A7$~jxLyiAAWq{taO7LJM-OER!6P=SopYWI4Ja%%{8 zR;xZQo*GF?J>1*8-_pll(ZO_lEH0o`z-5o1eyQ8$QvAa)&b&#jlr2B8t2Bxdg~PWF zOr{Zbgw<%Qv+_=Rx!zW$NZ z!n0~sZc1GLb!?!|my(=lM)8##&8a48b7|*j?4*{ey+Wv3PJdiww4lyi>&=F{Km2Nt zuJiY$+VSbOet5zkn)q_<`hhZSnxTn;&&LA$;?cz3@6R$^f;@wp+SBv&9cp~5R&O5O zhV~^joZz}=3i8b4rx)v9DS5NbKTFk*4gsKhp6lZ zS_ZG)HkbVw0qKV6S>EIKse8$SFSEDcT@zS&z@& z`Xa5+wxvUDAhN)E_u$#{Yn`55xuqLg#QiLN6)7}mLwDkFym^v&#h70IJ0k7QwQZHJ zY;x=9TGtdl(!8@TjwhYoxPHR`uGk=`NHgXVPrqV|$+E(t12oeH`v>4)@^2vu0Z~%7jv{%#;UqIe&dIvh79`|D=B#u+L3-M|KOhi)+-1E|+VV z_)ZiWudO80>BkL^%ye#~q{3+jf z)DG&}dY|Bwx?iyl?MB<^QQ#6O)>+;cCs{SNx_E;A&N)dTy_=+G`mJ{=tl9Q}z2%8= z*^yrf$veVKuwuiqn}4wm$A6spYqg@N_SafhUS37zAJ@9}rnC+f-tuXOexq7FuI`ID z;w^#dA9J-I)kuD3-73uKiF(hHVfSl#Xz9gAhs%s&d$lVRI{tx|6tiAFBm$cT-aqX+Tqjzx0y;nkSPOZ`m3k_*~ec;F7$BwD=AL;28SXI>7 zyCypI&i;vNomK1nK3UdWRdq_`Fu&6!YG<|5`*O=pH7~aWzTG*hh5QC8!8Kd2*8i-y zTKdNRjz(u||MQLMB+EE;|I+HFL3PPPD=C3WL8nT7o^hdyS3e3W=**pYd-x=uB744y zJpS}P&A3f_!=7v=Dv8;%%di!pdD60%(7eiwPafpHRQ*b>u}iMOC-2IScUnm62xv= zYTY-!m1l|}c*{g(cQOm>$p_B;m(tI_$xJegh$+}1HImo9DyreWjp9+-F~lPD@nYoRqe*O811u?y*u<#JWzMGRPPd;J_1!7PM>O_) zHzSNs=*NdI-_q%>bwyRpolwtJG@9Sb<;1&nmB&(%_=5&*C7;omE7m;ET(UW5RnM0K zqe6MSjNk#gEq>P%xAT8`LH60GJ@DcN%cGSI8?xgQ1+M;=k&sq>FbkDY4GBpRJkof~p1L)g6Z zuA{vjSH<2pW)${Kd)(6{)AHAY#ALPkGDE{%uPLp+Y3j1(l1i=nMy??? z=<6G=^fwJb=my7~GkrC}@>c}q&=2m5-sk#dpfmiTa6+o5I&O8;iC%%1VkZeLrtJMk zrcn{D18M=O7>+vJ))zBXnC5j}nzgu`@iarpZZ*-J30`VC;thMnbVRa@FEoF6xU*lF zv3}|5<3YoxvUlr#_`d4&+ge^(u?gF=OJxEcep@cYsm?-%yZ3SG_r*_x-R!|(K zmCD>VjvaigKfI$)sp4*lG5dWr!If_@2^DO64y@h!P;|ZCJ2~@d%x?DXr=LcI-MY=w zzdm6!Po9vyMm88WD`}8JJ#l||{LL`~pOX&xFU_Y`Ri_Dk2s&x!MbQ%ZZeSa_Gxq{F z>a2yT&}w$RRVR$lZGPfHtF|`DvS1`mYZF! zMM>-v5s|7nn6iC*FH)!tr#SA(=xScV=6+cHe&p!$%iN!mPH&c6k!M$7w=NNAvn#NQkX3vFxc$ zX3;6sH}y0%TCQ7oIkz+OM{4putd|*O*VBwp>kUcV-6lWF__LR3C9iGLUY(oyA(9`P zF~#q%d0%;D{|5D-+xn5Z0hKOgEK>E6nm_+ zsn*7{?}$>AlB%Q3MD>wSAh`cz+TPKOl9m$s^pE2LqtZUjp zUeWcwx0sFJ?4z`r`My;tLbvce=;H?XvR|Ad#hW@9*IkkouIysZyB%IAGS2Rlc?{F2 zIfl2B|JWm(Qi*Ur0Aw9@1AUvH<341V5}fA7~iPj6a{)YiBw zTw(TM-|f|?M~(!pdsVgalZ{;=p^{fjMRlu5yinifFVh~G0z*7dO2*G=1^Nvqb`{L^ zKmo73%afc-FEliH-qF6=p~b^H`tn@y@@>y1I&eRCrMcV_qn%9{s}4QtT@|*G;767n z<*SvNiD}prhT5d9_G+C|Y_lQvrV*K~I=3vG)$VK@SiNI;w$^a+ioESLHF6W+l5m_H~tv@_CEq^kK|;MNhM4pF;WFn|!vPJKcA$(mGa6 zJ#|DdXs~S}v(U)bzTGBcipS}pl7fz$t1niyA2rM!ArT?I67!abArGv1s*vlEbYkn) zJ$lF8vu{Z1@;X;9aod$Pw0U^uE`)VDQs;5I^tj9GHCZ?OLZ|i!@|_&qr^>KeUhcDB zg-0&od$X!uPjA1cF>mdOot{`Bjy^+0y1rOnj24-F3TXxi;vS@yk5e_*}0RL9dIWzZ}k#IofR)U>+OnErUP;3d(YM%v0IjWtvP#r z!F6M2ug216S1Bd+Tkpwe(we{T7trC)sP#|XtXZ;SWtkhVFJm%OcYoW^%-JhTu3iWh zcPuLW!h5y2{xg-W#D#NM^I_LseT~woZ0bKz-(Dc~!OxKhD|f|XFMU5!hBxfU*>gCZ>&nlQBiemmzEvI;e|3*@ zbXnf}L{3{M&-B@`nya0kDluC7mVDrG&i<<-s3`uo`s!qOn5?lCAOKiKX6MZi+DiIM zv}N9ztAm#*ADupZASP?BlEjHPAEf1&^C)d9n7HoZ@Hj`y6)TzJf97_Se>UIkY-Rsv z(%n;!?XT=D?Z$o%EzU_3D-cq2`aINMRw{d`wT4=iImsXuMdgh?UmGD1?3kw6*>d)4 z(wYgK4bKz`yjKW2@>qV8RCt{qePx%p>*sWf+x0tdpM8kZ?}*I2kk#y8*488u&!OTb zkutR=;>7LNl4Rov+KHYU56}nl$vQVUFiB}|ABJ)cDvx1aTlHNXigR>&>0D~=nvZ#~ zJK}BaO!blWj6&m-eBYo6SL@H=NBA`CY=&nft{${hTeX2WSnDn7rXqbDTYCAObt3CY ze!+Nmr!GJKtfjmH-a0GlXcr$}T9GZeh8TEXJ(93hqj70cAB3vibFwvXwWCA&mGR70 zD~DUIXC!lP`pi5TMDdh43#nuAs*P{g#PP1ZC)vUGfmMHC+XnA7-(Gp23i{~%cIwg9 z534F{DyxF8-Ob;&A@Ex--K|k>OQRgO)0hIAvgZb+1N0$_;Zq-~UwqtjyMAfU%@w*o z($1Z%6nYYDaO{w1?waEvU#B}t+NQXpzh~g9j%`4%dXGxoUowJKOdEg67yl_bG$iob zR?m>D58pdfvN$R^8VAH|;R$DzZd$uk-uTPS_NQ`Jx_htkw|rY;CW}vt5ZY`!o!{&t z#!+r5x#5d**<`+U%OIy}`iAR?V`5TmM{C#=ZRCiu;9M@E3ho7C;7C0#RTG?zMEXsl*zV5k~R#}eT z4(p-%LiMrS6cO9R6tjbUdyN&y;gI>jTX^ANx zl=gY28N|x9)GPL9QPyOX>z9(TlpR@ZOEPp&@?N56zwU{DCv&htN9OjXRJ*qF3qxYm z>Gy}uxh%iU9lL~$rBKU;tIqSah~4h+IE;IS{x5kqpH`({MPJRZ;%xTqS^Vc-d+0T? z$M2q0o3T2=>#7}Seq$i_?1Y{_YFhizL*vuJcVBwCD);7HIeSU+TkU<>zK_4&m%9~8 zZoXl2v>~!%Pt&jEjs1cHHcRBpm+k%Je1mw7%G%YM*A;*9xt#WWHgB7;X4I4ht7qd= z%r2CNl|CWu-gNf&wQt4bK8Zv>4Lx@_ZttCt3-{S zM&y~4kx%(IJbtc@cul!w_xRK&`Q|*y6_+kPHN(i1PKdDferFxEF^Y7ou8)mTk|T*k zzV4*pd2T!iMJo?_PX#Y(hTk3$2NvllCzUX)7f;C-(*py|3(ns!zQb^-3Vz zU3ogn_h#6eerWQ>yR6uUTvfeSJBN&exk=@f{RfYWnqh%#MgMI|Kt%B0OI;tGc z>}?B{VejS(epJioBP4nq%pNe3=*PTxrWa>XU*KJ)`pDp(i{JF?_Re8G663pvRJYP| z>b{2C=FPDj`?GeiH4-xpxbcM%H$FB!E6&EBapQ8bc>^}Hdz$+!2gk=Of!zG+xUoi! zb}ILmC#hULD|L>A79D(Xsn~A4#;qQ$HBz=q*L>GW@y?0c{zOJrQT4cx_tL#rTcM-< zGjZn+AF+D9VuDfR;?HCIWi!vtvt1=U^(q9p`f~zTstz6{939#6=*;e&HyYCwWNkQC zS-L$99p-eanC`8u2&0=!y{moI?cj4^|5lc`^Y#5?ZxvwC6?Zx2B9P%v{VfR8t-#mQuV8fO>Qx#~E z+ERsgM|SM02tDv)*p#|~11g=C)Q@mqz5S+RlcA_$*YwGcGs~A8Bup%Sqea`gWa8oW zqwI~_(?VnMha&_~qK6DmmIa7N_E{)(my>FQZ*I6Y^=VJI!L zxg_&K)zRpzpr{-}e-><>fAx#|SB@m~h@W@R(G!tjcPzU)qZFU`G3)6=YKW7V1y^pC zVRbB_LESRvyYT~WF1obN)dC0^c59w3T6x<2Vv44eyP6cQ^C@_(8vu%buO=tV!1ZqtqTBbWZFED(1*7;?t0*HmdtKxkWX zjLY6K#xi`AhkGIVtX#;2F`m1sDxJJ6L4z%sO}mF*SBB_r%@P#jbMvfHt;-JU!yKxd zA>FkomRqlx)tQ`akZwn&o`2G=W#Zc55j*v{J0`I3!YDOF*C$^zc7-|1IUj8-kK<7h z;Yc5DYaU3lOOumJ_<)=IVAae+#*w!2cjMzK=gY1MpPK2~?0%*YiitgXF;?|mQ;M!t zRHxI$7^jm5$}H|NQrLsEcW+%cZnvAJ^@ z>pd>RaLVqH3_HP+y(>Dc)IXLC)ws4Djyb;R!b-lnL(wB@FZI6;z1`qq9uj+>WEUB6 zd7rX&m9gmB=bpmPHTcyTgDgKk75-Yw6}0C4ZozoX<~inj*>S`y{}z}1QQ4^E zpK>Rr4#-~Q`3yZ+vI*TW?33)di6h;LCBwVv^RKl#b$d=#7W<#Cw^`Scx2Nef_4}1| z5#hP#OEN9wpAN2ndU|u^_M-fK=Z^NKc!i{{^uIlFqdIrw1~5*hgS(Kd zjT)p1A5pUbwZ)=?IP(AG98Q6yJkJ9;uFm0gUQ5~6h)YI0h(0o_UMW?yTv->_GcD?W z8%QA{6KyyL9l)2!xcJ7Aw0Zw&1|cm`07hO-1~4t=V<|9m4Gbl5zk{u z-^Y*jf3upAK(7$K;rP-g6Q5B&caXI=qVPLm11sUiZYu#iOy33x35~I9V)T9 zc1o*KcLNE@@mrAFA7B-$fSsKo&~Y@1}5uQr66LJ^xjP z36DrQal_-SN8ilBnhVdzj;&sHygnnq+~0rG;iH+^+1WZaVrB;S#fsDvwJb5*PbExi zt7V^>*KWIEAHbgXqPp75-l*;}`l*Dyk(sfvIV*96>2u>+`+#T9tI-lqWo_#Nnz3>U za&k?}R_BDJ6dPwhMqwt4qeD<(lgTJ;RQYAelT|snQ@sNJBI$bL;ZXC zj{IrzwCB|Rs?u)%lnR$nRo>&~=Fzdc({KBItK!xO$|lF0NDr~4R|Wl)+-F~&R$O)< z?3+lO;`WUXbZCwHW2UxwRaF<%r)=2%PIU)|+vLpV=+TO(I&YVUD=I2%|L4W9Q+$WP>acIuc=;T|m!Ns7$-6^tmZep0eA0DX z{49&HkeZF(qfIf`;9$+x9Tg1awp0B#xZfMsYG274{GR80e}KF$zx)8-)8S`ly&YQG zq`r-cn%JvO+pUi*MFa-DMJFUBubV8Cb{p8EI{r#;D#pfsX@eb6`f9_oqb>iFAkI|`3gDb@ZUyT?# z(KN=UF63pn@~U1R^^s< zrodk9zDfttKB;Sz?|1SwRQkwm)PHz=J4rk@)M(&*48^Y8VOtP%N<=AcWv*_ zyqb>NKBjm5ujphI9x^K0a+zB>;z^~YftGQd{oM<_lOk;`n@{K*y2l>vFf^h%F=Xc7 zuJfFw{H#`dP}Bu6)}7r(ndr}VJDPm9miB#F7ujqTKwJP`K%u`m?a!ZlkXRJiM{=

    Q;x@BpEwBD6dL*vodfz-rA`s^d;OY7rz$k(y?cepp{b0w`Wr|}PWR1ryj-=0XNYe-Pu66escnd-N7xp2&kRWz>;N7IYCAr`H68oq1foDRD?->%L*wg=G`q^kP7J%M$$Prdh? zlo{AVPkX=Vt?2pOp0YOC%KbN1eKe@6pq~(=xvT3R;mTUFU#gBQrM)R$uZ-oq$dkkx zt&LyY#~zo}OuyOuVT)AU<Ze_adK~6z}PYta^Udt?+|sm1Os?naGN7pEvyAzB&~06#AuaX*q8^B`mJ#0On6 z-$TNg={*-JM4WZ7+*Khu4i_J`y=h$&QSqH7;qr3iZdRw-)6SyTKIaviR_m;-eJM|u z*eus9CT;!U$sS+v%S+23- zUSVI?g~FdpM(`Wd8Xx0YXl%!nBZil$<=XB@8=`33I`4FsussJ0u)@DwB8*PaLVJ2oKs^uwGJXpHP6(>ygA(FWKn!2#b1o z*7e&6+IO#WuaGb+O3(Qyuzzh3&+VT_SJ-^T2d}VB4lWAmd7;IUnwfoOb8mERXtLvt z9HC&;%kj>@f_N|M{QDwPn?o!C}CSo7rWzwB;MDg1Kxz@{^uC_Rp zZaJ2rRLk*l4YpnpCRDA!NrW4( zoZ#6c9)G>E*lgzFE}PPTs;9o3H`Jc?Eb-D1=yRIUAs(4*c{EaQIoeq)sJY^YfDjht zps#OY5;E-Rt-JBC7vn?LBUX`+@Q2X?V%y{GSy?5%4?Vx^E*R^UEblYq{`oegQuO|& zgI$*rY<}qMxtCZ?DEpY2`MShUgKTRN#a;EDc`gWQpPX?G%O!a+f2@`9iR_tRY( zfgyTA0teX$kDlpUzS%ISFVoEOSzs!1i}-sym#jw=SM!JjyY;zOvVr%GW*Dr^^!3y& z-kD+;vn=b`saVyg)+CRoZGnUq$~If{doRhUAMzW_?ow56O+XJScP`1yGTM*RTYo{h zr-9IQmCe)S_PHX1x0^H4dAh=TJM=sKm5-^rcoJ4GC8}$h-k#7}Vs?D@n&TQ)itF_2 zVx(f;EE(G#y=Ay}%eS*78=V7}-aXTzo{T$9rFjYDzFeiNBh6YV#xkb!qCa*hZCawl zx&@;mp2elr@fv+(v!V8xeB0~c1}j6N3%C=UE!J%n*KaskyR&p#ytqq{Z|#i~6Q5h} z9{5HPK2P#@CFZDYxa>74pIDl4#62VI)CWane1h=O)1PE7ZY;ib>D(jZFS+&)gp+DS zA7tx(IraJho9WOC`yV;ZU5?joh8($$9X_|06Qg<{?<8-pjW}*KMlCG$;|&W1JCmv} zOB}xJ+I4iTZLh7(MeYZt!yS zPVBKaqav5%6CFj5JyaPq^Jq5z^zQv5qo5Y7{Y>t_sqJsj29I0&emuCU z>jAs1K&w-r@x#^hlLJ}ot&dqZ%4J9w;`*?ZWlva2v_8Dsqh{&`?b7p{I(V8j@&-#G zOWsA#Fp z{vxi>dChdr@M93Y)}BAH%;hnQRA1r#@prMCciVdMT=I{(D0kU6?n#4B<@nPL`y+2CO7gorHC}tmkmFbJG0`)< z()-sNypkJHvns(#FH6*%I3zK1`)hR5DY34@J(+ctZ2WwZ8{bt%pV)VOT!?N#(il*@ zeKJBNvYP+pjrB)s>duGs(RCYwQb?yVfYT{$vwQtD4-4E#$1N=ghCBt7u22RmSWSj=7&DokZB)`YJ_Z z{{@t0lm+iD0K1-<*U`K!mhZPRvJ@tY)JYxi-XC`FNr0}onc@C)A#ZexgN3I0%5b+k ztRr8?csgFUNOP=xzq`rny;Q_nRqB8N7eRqQ5x&WSjfqP?B5Q)Wd;b4n?ybY3+PD94 zJhp!q_f&)=n;a}{}vg`4=gVxKGQ($J+G!?Y{(f?9KS zOv%0q`)$cqSPuJR^41Y4SIyVVoqb6yTX-0vDZ2IpE>BqEFWul%csc$pJnkB#Ij4`m zp8(6X|2j0AnJ4j#7)t*pGuF~a^&N@Af`g_&9O{M>L2n$Hbp zHQ$&x6KsGE@=47UFmH*7P@(L#xuLtrez0^gt_Ca9bUoLcs*d4oN9dZsBN35cZ;#xb zmyX+qgI#-b#tyHoZ6opus!Gc<8Y%YPVM|*UT%+K`BB6XSJF6rL_D3 z7q9P8WYFHHttLyK1BM#XEj#3yer)GYgQ1*yksq%&O<^4#2viRR68*cO;`$GUiiMqp z^B)^3Ar3tzjb)D%A3~}J3%RFb=21~)p zX^&{%eer~?$uhBlSJ@z+$AFy}rqOj})*P<3S;98C+r8UJZuY&LIv}vjtzE2hTcF<_ z9@1F%vV*3HJVZ~z>C-0~IU(gsuqH&8%eP*-sfDuT;jid=Idkko;|A&%C~p_2qNH|aqSotR#f^h zkh3MD`?28DSQTT&#+$hfGJ-mhLAH%<8ONtriOz+0zj=7$zI)as2Q+aXHJhk`TlOb8 zRW{1c+>Lpr-a<}(#cJbM&%F5d=5Fe|FO}G+U}bTuu#F1N>Y$T_rj3PQC*@#wwD~)~ zXQPRI`SoP3ESFY1Wyne$!y@7`K5M+X#~8TgIVoo8u7@vzXZ7lf{@I)xqVF#-jlO$f z&A+v>HKN{sqw;w|t^Ls#$7psz7DLJfQ&aH-E!*m8YKy&WvD5f$wNRL|edPsy6~$Zk zg}Pt!>lRqp7C!Z-?|ytvLXheMLw%8t0KsGEw>N{Nfwn%fH~Vvxsa-cFp3%Qj+8DFj zUJK8j&&v2*Xv@Z@gh^nF7W_*#;(Yy8N97`K=jQ=FMWuQwrxM*o9cx}dPq|;I_Uyo3gqgFG)GZH(4Km6Z zUPh}GY2^|`zka<^PyCU~)GKl08tL+BKNW_4u8e*A#C?0#D_Ji@gGCJU6PRCcKyH1X z@$4kRJ39BRCRFK_!O)gRo>BRZiT-0`mEcq6KQ8LsJxh;u3ia6ouhi@94VRY&TMAcl z+ekGTKfm(a_3!d|ILPYV;}=_|23wNBSJ&}j6=6OUYwGOhKF4#HC;6j#pRvSc$&A9- ztMl1755#NO`bzuPDHkq!=smn~QzvJCe{;Q^>X=3h?HafXeJ)ADKlBjzCu&DbgjR!aPs)F#UB*6wT-YxX1bVJ1NcQtoL{swi4@@oo;R)U8lfQydI4?LCkiqwiTc`j zhJ_}k?6#}97?nr$JJ&af0Zom|Uu2iZTvtcGt`F3km{-!Tn2HEXa}(~4o+BG%WI`wu zp{Fa2wlgO#-q2L~L{Ix!WhS7!ckTR3gH3ya<&m$jLE8KBA8z{>ik-8$RE`q+Vv^jY zTjH4Hu@Z5iEx_vVt+z&_6v0+2QBmtkQs^kE6F)g;5r>r|ft6J=&tmUh-RDoGa|66C z=E`#i6V<10USZyDo6jH!xpS@V>hL|<6ZcP_XXg+358b$b(f}7V)z*h! zVpcNL#Zb#V_WPHZMc~JOZ0tTe*CpmhhStwz;8vJ_e%9O!^CtMx&z&NlUnkx>>wq%M3_WLh&;0%v4y=tL_P!bWlRHKgjl?(c?A&E< zy*40v^}yO^;5z*B_7Q21;eis-rr*}>jc z3lf=@m6dh=_}TA|0cB;}L0*>$n0(0u4quY39_}rn`iMBqIy*a^Pk!YoSx>eG-rK8T zR}<`)Dz^%}wV{!^j42n64@UVJeA7DGRg(3UO%&?t?zVb#{PrjE_bVu^!_~Q*>M14c zP!LQv$7ZCbk3Bhl<3(a>YU+sRkE6F~&z+nN@?W1<-x8>Xur4aS_GI(tL{!f# z7x@b5jC8*139qzFG0)7*3}uqUjYUqMJ^tLI{AU|csmUm+KR)o%M>>D4*l8HsJAdVV zMV1eNxUe7T+Lssy<#SjMP^}ZgK~%&;4ZbS*)d!nu@Z6FlUA^rklA%jjSobMWO4-3j z*QDSvRVi8Om5zmFWrQd!tZh0Kp^?tp=xNr6EpH8O5D-X5;xqW*(MV73pT>F+f{<00 z%iG>CII@)9Ik0UMcXOVM{_-e|q1;iSNWEBle14jel!!}gHY{q#&E5f=%+r6jsrMpY zarJ?J#Y(ZXKiTZoc7;2$R++O(n0uslwUQl$`A!a6JI{~p8d0(4na4#JeY}X-9UI?i zQ`Sme=)F-bnD!M4qGAf^HeMu!xgKtR67@Eaa5am%F+sMxM!;locna%XF8qn20v{*k zI!kpOr>TQ6s@wFm=auk%t($MOa3N*i3#oI2Dw)a>I=B6o@e-78mUr82i>P47k}zdF z(rXUR6A=zUkn{D+HlYiX`vfj!Q6JUn z?eVoYg(eP#8lN~`7R&v|Wl-#jRgU~Pw}<#XK0Y@e1`67UC*PT{%Mbk)ez@(;py#rZ z9J&$je0Q=(^GH4fkx(^RZR4aXYYpp9UJnpJh|SzWoWin*R}+$LD{eW%momRUBN}q~ zQq}0mDOQFS2mQ<^4;a>bDK6y42Yl3pG-S6+yE06!O*7Nl38@tOCLZmlFw|!Ilsj(B zdNi$s#79r@e33R}F`9ugoW>e)E%G(LbU94eOCp9>;Sn7zC;AU8EQGcm+5CD z4qN0f|3CgQiG8-TlR~zjRXTZxxWWDM^u<@VFC2dn-y-73q`vUuM~kPS2u-S=Km9*{ zrFh{)bHL-cI#w6(@m<#r4^O)bx;vk-Ba3(5eM>(4Nny8R+vw_pcr3nKIK<{Z{j47uKbTlVyh_f`#nL)2FF)4N!f*3L zAo}3pK+~y)F50T6iaTFRXtt`3TrbBc9ACIsd^&ue+RQ~;Hi3|U84((K!+fwx1G?{| z57F}z^Fc7c`Xd!`b4P=iIdWu03D4t_Mds{;xNf3N49Cbo8W+9A_gVI;4~)#0a^27U zUP}}2O7Ygr%xv+%1R^->ro4(i+p-@U){>2Go5%eFb+TL~!wiX*-ZnY%bkTlBj4(9^ zP6YYRa;t1Lx>7$t6)*myP2={(X9|pNeblj}Y$n^oxFklAQ^b4Z4f-_kBQ7=&9mw%Y zUX{{@1V1D>s1sq6)F#b$ zP}hT>fk?2{TNHP@b>DhB#}hkD5S{YWoU9naXdLXuSH>wp6bz2aRp z*EPwPOBbqpZRSG1vBt*-F}ZAXp?Ma33;4H^-FGB$!arKa9aTJYkAYYwTjKpO)L*HV zMm%%WA8_TsrJ-QP%Bt#c_sh=ZX)Pbe0}QmXJ4zTR+k+*X{658?d~Y<8?zN|1#C?a^ z-0?aRV+exVq?>a_Xgyx%>pTXeO;#rA$e7g&>X(WWsmF%J44g-iEBt(hdFv{g0f*yc zic(+r3bDn7Q%tS5&G#n@1a96F&Gs`qVqY8gmEZ4wgjuMsEd$VVxMXzbch`d@Rq}iR z17YwKhl-`SsmbNe_fk;%+)1+3@UR1ET%528);XWde685Qs0af+h~18w$5#*oLJ*1` zkDTQF$#AWPLxG)jyoD82?%F}8&D3f~kySb()ZIxxqVe$~V_RibZc0zc=A$>k_HHZR z2#hMj)|@F0-iW+0`8Zd()p;5MwFc-qGki*|;Pz6{y7b;1D(3^fgH zkt!al_|-(*vz&D2uPnD*J}#C47WkTf8c0fwc1C1X=&hA#nmjBJe95P-=R0#*?=MnTFI0RUbc)TL)t8adR~D#S||O-Rf!Pm}->t z!vUi&tRZ31c?=s5n;YXQo!kfx_swhh=|nSkpE8+pFfD}%&QV@Sy4RQhLXf1Ai_H~z z?fEpjW>*qiqzRN?q1E-pK@xdI|K?!0h%c_QUcW!d{@QWYon9pf5L#P_A%v-y)MRp4?ik6sn`^$;Ja!e-f zt6i)Ph#3#WeFm6LWYuJn_oyXTM~vp1rI?s^bOH9{*1L)|2S!7PqEu83V@|t)2$Jq4=%AisU?yqz_mTGdP=rgN+)&6XTgTu70temyz zr>H@LaT;{G&Lt{QCMLR}Jy$UulrmBAVcHAP_Lq7`&bPB9?DechI`v^SUx>(Z!gQXH z%CgU(-^DH+6s~VJUIEDiVpN4;Lo2wKT=B}IwU$uiscbE z7jR8S@{7PI<8LvpQuE15N$H?uYKtn?>jbsjToOvQ`vat7gH84|!L5W$S)e{zU+>K5 z96c!mvEp$Kv;EEf_xsK@@k87B7t%s^PDHNjy8{P&bbQP$T6C3FkNn!ByoZJ> zgBs;gyqffN+p8HlIK0+_|w2646Z4wyDG856M{{vvn3qZa&QG z7W4_P&kb;$o#`?@TK!s*La64kI)o9ao!?o4{RBA2s~@v17Q&Ue46hVpi_<)G3ghmE zW)jC1$_nJ~>sp=}D8X&*2!2nw6B5-E~hC z4&c658PDfbgw)+_JH9GI2ntc1R_Oa(pblf2q;w&PLVnj#7cN~2&MOfzGBPqWOrx50 zaByI*+J3+3$HKw_hr`3d!g6zSgF|R&XfiTt{fM^E`tgG$mg8gJeB5?B;uli|?4pK_ z4!5eSEk<9hw?4gR;8bGTU!*4RHIC;))F2MH=@dkQ0ygzW;M-{Oe1{Jf?ScdMc6LM^ zrWzy7D>u4*A*^xEtH{(;TI2+cbX=whhjBLp+61hs93^_&ZrCM7)uX!ls41FLP&&?G zD)O5TfsZlo(f-;NZDshKB%-B?wdN$zkn4DO$-FMR*6^_KaHV*D7X*U}#o*%eXi`2$ z%-sUZhE)&&w|8{3{oeZUOv1Z&?>>I~s8Md~SIlcM@+pqbDRecGRiA%*%nE{-W4T(RQZqBx zx3;#{*5(Gwi#KQF`io5InVAE;yl$}>v@(Nyd3_%D?pwPQso1Z=dd)FAM@P(oaoNg3 zAi;FQ?u#sJiKrM=R8}qtI)d~jyLmI2>2M{$Ewl(+_ph_4qgwq@trXq~O{2JE{Fzik z9BwEtfB(ZtT*JY#jY^TF9Y*kLs1b%!SoeeAUSjIit_SV}nlku&e0=ou^j%-~)>;Gv z?3drkEe@3Ci+FM~QBqRsY%LBpE#i(tPpWJ?P1R$0tNQ4mLt0>AsM0BbljKD*0^IgtXC4>&wj~PtSkLzr8aJ3KW-70=36r7cJw%$;`<-_t z!%Is`#n4j|FJ@Uk{BinpQyfbdFUmlZ^W;=mJdEQ<2h~S3@5!w!cUC8RdwY|Ts21Y4 z1X(8h#t1+a@w@Jq%nNI<+s^m;`ufiGPW^UQtnT zpAqXgSsvuibY=7)Lulyl-c8TQkdEcv=+z0Zl=|^`SjT}_JX?%;<;n{JgNX~)rx}KE z05$S`&`61MugUM+5sIbX#C6+#-6^oYT!R=Y*J~m~Kdte=(c2gu9Yv$j?Ck8+)YRv3 zNr~AE0-MZC(0SkHdqIT46l`p4IL(LVyR)K;l=^6hInaJnB;jFZSoa5@n@hvP)oyMe zG0DitVx>|vZQS z*n&j1DGCV*b@%j`4^`}xPRE7t#oV`iHdNuTsVrn;vtf-R|3N~Q5muIKzw%5R(Wc88 zWv8xKc4|Z!)jGG4rCNCD$`$;c3phC7DkY_*dsKx?Oy)IlhF!K|Hx!QBI$>~PZfg;Z z&Kc`-uRoKbzJs)Y!Cvd~FLj&JzHd($uolhmJL@Qnkk!;Y{CEyo@&u)vrz@juYqyw z=FOX6(d86(c6WiHtS6#AYMZg96!tIZfHwLcd~ZusMP0zgW}v4JsWN1&5&Idf2bV5g znqcSW*Q{(WG9PAQpKgvV@9Mm2UV2!FXqz}$!3OBMV^wI z{av{3`kRY*N%PawDcPny*%~4uB34$bT^UN>u04Xm7IJ;l)6-c9&s`x-EyQl~Y;SLW z_wIHUTzN)l%^xMkiKW^L1;Bqa?Ua|F|NHq0#tFWB`9erY2v9Cde;iPGNR#8%;=;@f zy3)z++RHF{C61xSni>E}FwxXSJ#`Odj~m@NTJZ=3(C74n21AxCOs7@PO+xcs>uwyw z)d-Caq^hc_-R!}G7aJz~ooVuV8cmIjCRRs4Ux%)1RtC+OF^nr!?}j0DuLsEdSR&k;NO16ewSJvH$;QTprlw|Ik#szN zpC29`9W$d&AkxGQ`7~^bn(ylW=I{4b;aWKRx2`4FE&k&^MJ%Tf5ua;KU!y zhpWo%mWqUrl?0Z>B^1^RGSsnP189M>v(5P;3~{36f$CqkaJD@h^OW zE~dd#rJdP-VrZq?%xqVNT9Jutv3jxDV4-nOXP$}@r`bSiW*&4pwoe8?{s+nE(3!P) z-KHq^)gc@r4g>mRfd+nnq#$4io$}I#{kv=NN104g@%)tu3EH1uooyHcNF5y=t)`|H z9Ue|_{rWR$Tq1!9MM7#dkMdmKgT1XSfU|>xgLQSnDIICyiW!PofP=-v#lr+kZ%i@* zbe@QRjJaJ3l#G6k8UR_UUJS?@LaGlpa;VM2*)+L#rlQTob_9;r1VFhAmfHzz4Ln~J zN^GNbsPgpmj8#!lR7B`*aB*?zAuN|i9?m!`D=PzFc5ylQs9ghudVKt_9}(vh7=+*1 zUPEILL}?;0BqU@~QN)OBJ2`hd@SHJ|irr$4Bza`SU);Z#+DJmids9l7S%a zujjmWytvbazA(=#X+GSs%}?+37GR$hZB7o+sr7ABR4vA4Ut8+QiNEEyIem`a8i>1u z1ab}z#m&}yed#Q?NKWV~4403J@Vx{w#dHqPIwL(j9>mhpQdW`VzQqH7*Khw7=QjF^05>zKmn; zm;lPqakXr(jALXFHt=$_yBl+ocS5Y5d)e474Y^D;Lg;jLb&2m=d}oQD%c>DoY75-m9ytp6tDD?Y_c&A;9d24QnlVLZsfPR zHQ{=&tFoj$-Zm^8KngN=N>)bZcFxMm%Isu;%bsl-(8w`H5JN*lfMR2%C+m?2|Gwe4 z;-0Hh=Y3m*zZzHrM6U1m@88vmmxywQYA_t2*B~=@5$}!&x}`8LqeCh}8X6j)TIJ;APC!8jqggK;E63%{ zO@x(-N@OJbiINgeD-?(`D$6{Fy3RZ}DoGr`%I5m|{7_|ik|uzoCycO^4pC0M#t7Lb zPm=hq8j3q8cN3Xc$ZBaF0ZB%;@gBQ)zDh@|A65&0aauN;dd^NlRJ-lH;LDdUPwulE zzP-CER(|T#DLFu@090GA$lW9K^CvlQ*_>C2=Hs=Q4MiIw0blSk>3sFNmS?oPKBH&K zw>|P201&`zhtyCRSy}C)@8*S=OtW3L1B{U6d#K0&W2)}XQWF*yK1SS;&R-0o+~8Pw zxI9`jo8H;^1b(3i#sG7WGLew*-B`2%CWk{@OlX?Eeyoy?COa05THR z5Fl_^LS?hJxe($%W6ez`u<{kpXi-1S(@%dAZ(wjxG$O$PVYxaX8Z-dN0hvr|%ZyD#n0FrS ztJ~+u`MBH;_koQqD2-1h<#4SHZA}&@xUOdq2fqOH+{VfZxIoF_Di=ZXt~oJd2oQUD zr;Y)t84kf()=@d${{U|sXVOy$_;!4Bd9A=#7nO<^boykE6TE-?0%IMHe?i&fbiA%H zG!pw93Q_`f$Wt;iGvB={c2~=3q0qzQ+(L~WkM(yMC8c8P89De05cW+?O<7r4Va0FWyy@-m zs}0D=%FLvq_HfdL>}@SA4OdqK@CyhCkV=uyDO1c+1s*I6fw=3_5DMW%;$xzKf_AFx zAegn7O1FSOH&yj$QrTeuxilTyW^m)|^78UW8fqTrZ3`gtt*zIAtorsE7>FdRkxwu4 zU^7K!DnM^_ViR#lG}L(F5{&oe>j#(w#>A*=X)P@+ndcMvr3^(w;g{%G1})SdSSE~o{rqq)T?(Fq{NIdYWMl-gt$QL}v(gbs8t1xeNC!N= zY6I|OVXKq@ycCn+nvSA_ayyBZ)vgDbc8M$BmlA~og0H`>k#&vp2?$WiDOl()7Hs36J8L&jxJibO)71KdHsA2!3I@C0;9E&+i8CXO}Kt z4r}1a!#3rdum_H<*5KBCi`#T8`?LsIC8Zv~E}@|}PB5blSO@4Y>yWIjsHmq0>FVmb zQs7%sa$hvYWQb)0AcJa|j@Z>x$ol%gFM!&HT(&flz<)2Hi4IrVY#$6J5 zIpqQa*{4r$pH#cqFaHn%gS|KGqR-i|2Fd*25Lp9yu+ZSnZxBpVHmk?PzbW zUT$kz>kBb*5fpSkNUZKMdkSfNmRQYWJ8uAAFMbBGf)|+$a&vPN9tR+$$Tu+Xs-fNT z$Y;6kxeZTTGDW524>Caa4)C*W zo818P$z0ZhnvTzcU~yR0&8I;F^Vrr14P{PT?Wg#OS+kkW!5Jr30|lIfVpAlm@a{*Q zI{!#keSkb0-3tQ6cbD?G=(X(aOKi=`LG1aKYi5?2w5k{)t5@x5=SoUShS@4Q->CVED@dKj+JVV=3RLcHKH;rG_JC9#?H^gNLO2}jQvm!gkPf~bAN|zQ0S#tMlzMyx1M|n@EhjJjn~{h^8{5O)53<{8c)f$?0kHz5*l)g=!AX1llV%H&;+l z5ZrV<3gzSN9o*2P>6Dd_@Nkb<74A_5B=KFJ7XCdGGcz+&)2s?=Dypw>;Ld_g;qc@# z4gfhkJmnugd`L=~00{N^91g{QbTLXAzquHA19qJ5|?d^L*f=T$DOUlYXMmx@Srj0#RQBjfOvKV=QPOw%RWVn6%fsqFe(VfZB z8c!d~d33%ohlrF^c23UL)>bjvuQq>GG7>7NauWif<3U$AH&8spg@l z=AoHG5PjiadP+)+u%aOIA9-yI@w`GJM_|Jp`}yfdL&&AzVsV-}Ur1tBff*#Ymwpn6 z-?we4K8k^vnnSdtN!`5btuWkyIJ|w}Zf6_o#S|4~Wy#S|p}55NZ=Zi3I?2w=yi|iL zP=Lna(&U=GQyvr&lK*^K%2X0a8YChCjUYdqTUcP6y$g2$5jsP=d|g3xHCOFHq()UOUj#S%m15EAyQ;W zOi#ai=Z?qvEn+w%mfObL*Y`>F{AGLwz_v1sly~no3=ZP+Qn0hufhuo%mdP_gqeOw%JYteU6AE zP~w?(F#qC;ihXUI*`_D&?|u9BZM@b`-j(cB3DE>Jqz4F;46RK7q-nCX&+5Bw5fKsH zxIxv!dK>{MBS;^fv$NBrzbFf-Q0cf4%*PAxL5`Hqu^<#I zBK@Q=sl($2=F0WpvgB3a@nd%)bmC4FIMy~c80qMO2$8rp+=E~NMZ*%%v;r;P>gqC- z`ZgE(lc#_NOot~WCp$Pey#3=uTl*psdgi_n)cTXqzk13>*cE!HsU`$eNml=sA6q~4 z&DwNJj!M4JB~W?p?(SlfBq|<7L{=3F3JN-G*u?i`2JUI28PUpgwMdfHULGahmh6KSa^W)^6?4ziku>mA9mf#O6K`+ z=31byFE$-O%lSdOVTHSb5Hqw(U1$-JVlCWYWqCPJh|v%K)W^40kt)?XVwZgI882aD zpU43;V@*$wQXkHR3xQU^Nk4k@%69)%n}PNG_az`U`sPQ^lQ}=7j*sWIF#^?|bkLKn z5zV6Gp{n)6h7j4RdVI7PebmMh?S1B{;ctc{yYaspl1^6r=KBrMP~rN%waz=q;(at| z%+8Bic1DI;g?;YC{$EH(IoB5`9|HnGZ6?hMks|D-B41<(O*9|UcI@k0Jw$>tBy1Qz z_t@nzp=9(YL(~u2lf?l-0IF^C_U7sUX2g)3~Kna&Dx-(URBWz~clZ%QtC@5Yv z;xE#pz5{!ttDCe7*ZTPMrKhK-5d?tq;^JatWaO{*lhxvH?I&O&)N22<0L0HAdOQP_ zPFp~!B?SfrS&n=<1yw6?!^{=_1)SN5kHMJ@;QUGEc0vmn0r~f_?)H--a)CV*SHtvV zc&sfgSqS|a?puoX0)sT)33-A4?%lfwn&ac+T{%PlhP3y8Lt1K2GN1x4ywaWOFETZT zV73>lZ1$?I39ES6dcr)ggH}W7kc!l>rcYLfyNaXuSnAD0St|Zwl z4aLR94VK%5zcHO`2%{>th`)Wwe9fPZjzP76_TIg#$0@8|jIE`mWylHSMg!olix)5U z$>Itnw!K7HPSgiskTWt2&9$pGI0|%nqm-VW-W??408RZ_bgqstePT5*eRHKC%iG%8 zPGV9B2i{qv{PlTUM4o1jfb;gpvb*f;3J?jvax$d6M0j}Pw5Zm_k1)?Ga#FvwZ2Gxy zZ$G%KTswgbX^PY3u^fBMijt}FZ7}!wsUuVKR~a=cw*P{I=aDV?%`uouP%AzfNUuPF zIv5=t?O-w+z< zva&KMfDyx0F3+~)_+9tybx<;9;a{L9JCQI$ajOVmmy(i_04=8)Q&Lb^Sy=(sXZ8H~ zPdHPUG?Yq*MmakwN~I8mwHr%KzL1F{Afp=!`fFq77l+lM9v{jov7t~UPekZ zboTMYiD>>!J^z%`qn~KdQ4)7vf4+)`mpGJU4Jd+9tteG3TdjDpuh7^CotdF0oma?4 zAK(6?_dg#)*%C*CR_z;&VmH=(jYGr{(FJNyUthncvoka_l$h7vqV?UwiOSYhM6TP> z;o-qvd3pKX_V#yc?u|?4UFl)b(Q1Zv6M~Ahkpsh!j z|7VGNczD>3WYMWLpwCkC`2Ip}@wh`@W)O0xKd-6=z!C==8*P5{#yna+i(sa=^m&S{ z0ANI#C3pNH>=j3IbMtGObf^r#BEZucLGp|~y*PEe%=>H%IzUcPQ&TfOg?tI36wb@f zCxJu!?Cu+PGl({p0rXH)Bf54ip<78o;oH}*f#1Ib82=-s+RXPad#2A&%6~T3VGK1U zp5=B+{LiO+Z$P2YE5!HTnc42H&j2K_9Iq`cD+^xy67a~up|k?fk?KJn5CNioP<&YS zXm4*X0Ii+1=@WEH`ng5gHhd-57x*I8zB~s+!e9dM?QI56YTt@~0ct;?&od{-TvYU} z30-8IR0$bDH$1?P$==QC#@3w0_;jnX0Cl8Km@N+ zu?`oRs-cvB>P#S-3V4X2x?Z6v5|I>>KRe^UOG{r6TfkKll@`@NT1Ng)((C%xkVo+9se)VYi#e7eg1@{M^!elT=@Z>$i(1~; z2(vx?#~(>Rfy4v`z9ZQHDw%|5cMrHRf0Bc&#bwO4C=DwsYd(^XS-nKY`KuKAltt(9 z4yY>RAWPXpy(LSn*x%p(q=%EnNJ&NYxQ~R_{$uEd;{kAXT?)XWH#{}+RQ6aMQQ3>% z(47u32_wO1#|BYR(Zk_^-q~r4WCeWUIJX(E7G{Tz31jBvbuRNbX<3M_Gig=D zgojVh%oyf-+BP?&3~I~B1ZEPl8-^Y?1$!L%V{JBnaWCE@gxp3NIaqZ(IvqmHoP;*IWYik2G3n1m|EDZLZo|pjA zSwxTDZc%;V7EXTlUFqj?KnhY-oBMJ zmn(DJm_;zEbu#v3so5>`u@{-ZqqKigf%?YwNql z0qta>-)B;0@r-L~YVz~*jg5?eLIMuszCr74o1TwE#~Yg=1&&=F1L z|GD4r^3Q%lI`jHmkF>AQ9;Le^%6`2~7}d}K?*9AO*t2{hipy5FF=H$`sCpz4jRECx zpt9)QPoKY1Ursd~@~bngd~V<}>T1Zw<|ckfSYE~M_V$@GXHpU58RxKX9kbvk>6#z2 zE$x3G$cX`d@$mFZp_W%y%PdB}(8N2p17ym_VNj_BjAvwQ?1>F|rUwCT7x-VvB5Kc= zh=^3ChEcbzLA&E~{S$dwwDkM#DBIreyQ3SHA(n2eEja#){%t;#!29CSW&6j*|zWC zUjfgJ>9&x_H|XRH*^m3}?(Wu+IyJhYWEN4tM6u>$-^9fY@b$%}p2Qn2{YE0+O5rus zSMlHb3Afz;>?Z(8d)r3Gz7x36GKc$=7*sbxz&B&0Gmv4#T0yeTD8?RLYY`yelVwcAOwKS0(lcpRq>?5ACJ@;DPRVe3(fyxpY9{NB zKc8w0;-3f1{2%r-`88lUnw6y6qnXa(%vuQIp^lC}?{mt+``ETM%SHf#Xg>|iI@jU zcft{`JbZO!ypBlC!>atoNncDKBBmBxbK`izhVEj;tE4e5E-sKJ!I3SoJW4!5HJKSi zoMvp|GljG@8DV)y-Mh53UI7711Eu+~I}zPbQkV6%zP`TD&>M4ut*RcY??}6#djHB2 zNWDIuw67$O-E{ZXrdwJfSZd}uqwWM;eh2w$JNuMU4*mZh+Sv^M-j0^DOLeJV2&NWK z`e52$WYh#eD@m`$1E;SNDNDPudlFF8ASoSlaWJ!QsiK53%N<*CSjgI@o1 zO!L1_JzfNH16GEfkx>CZjE>blnDI~NrB$Wg4hW>DuK+tbe^mveG58bPBBG<`Mn|>t zJ%fURfa#CsG)HhYH2)wA9!5%fdT};#8k)LBC>2*Y?8@cKsQUWASX^Mc3Vi_tEmSzk ze~>7RZdT(WQfy8L!~NEP#^@KI7dfsG6EkFABP5)iosEi%%U1O;p(Nw}du87nsbrk| zPOI8AnAe!(^YyU{Ja&tn-38{uL%-%Fq2($qaeRdc^1An-WRBm-+X5#hbi1Gvsf@|) zP2VfR^0g<4Rd-}EV{ia-UUgpwY@#3fkJ&^I_hdrSMDNg#QRiQK8t+9>SD~z|K=+ZT zcpSvqdBF_?jst=EzsGE1ei6x+`Tx7mk!kU}>gO23I5yHl(#b{2F6e)nz`?L(Iy^Ky zTp;sdC1binta%Y!dH499DgM20ZFBR#wMOP3C9i&3gSU~#)*xx){01#z6fig&Vc1G% z@JEFwPf~?$3zHj0Pb~8}u45*hi3kbrNJi-zu5Yy^3NL+FYbwGPC-V-=KPC0}C%we4 z{#!?-@!4-3l^zlv+sve|!I9?uMOknLN^b7m?u9)e6Wc%SLwhpt^sTKjHlF3S3xsQY zJht;Pj5_s!Q)6>S%5&XWiHV6P;=u@Y;o`+0 z+w-`j4Dd=#x9w$Z8wDeybZA5$IU<1e+ZhOp&;8kym7#f| zR$;#aENNa|-oU_sZdVM~^CaBh_?Exze4G5<^J#2qy7~F-ujx-`ldg1yZbnL4T9HeM zLVj{eIG7$#C^@#?pOZP~qkwL=u(LC_oz9BE;nAOD-TQ$E|2i5YL`LQ+8QE~@bA6}PTy(yEN=k}WsnsM1 zQc_eD`T6zv^EiZ5VN2CVt|2sxjEs^l<3B0P$Zx|=g^MZS-}i9y(9h}hYvO3o9EUOt zu{fXxdjW$T9Ucr04uTs7I>V8=z^JRCsVS?V;EBqU4~-+DMHswLMl7E2Lv)mJRfK6X~x3ew86w9s$d-uN4k!Nv>bLt~F5EiH9?V@#UzO z%Bifct;r8Z|7uWmE4zhlS0c0JY&FmB8K786v2Le2_{$_$QHxb7D_RM{| z^Xm2MZ|sj?FkqBBIyw|kfu<9%y1(zc^Q_IPMTxG1e4s(*+e-0?dr{60l*+4hRJk}U zi~)%bZ;|HP2t^YCc(ayvzPHf z>{AifZr+?+T#Sp1e0!XSA*vp_dvS4b!E3%`0#!i5v#{jC8FUVIH|~UpmU4Z)1gtU> z6B8+?SqvyT5|!G;!E#Xfl57Xsf*k67$yYVzhkNBs(ZTiSJPsk zaPLqWiHO_qh1BJ};UDMQ*JXh55B${y{c-hgT+o_79V00zDKMFO8csSc)xkIDU#>I&-;4j^-tPiLfXIDVRlh*jOryaa%Nf4Y%`Zr zq++2wfO8^24Ujo6e-2>ZRohGGdD1ng+|#-)DiWF3>AxZjbpe&lmXKY4M27UyVEofDdNer@gDo1|ay} z<^o<&=-;^{%mAqdJ=#P{N=j1FkE;0jbaR(~+r7E;RW=&-91fW1(!bgyzMWnWGH~qx z&a4i0ZDwkLiwJe@_hzm**zBL2ob224`g-lFad1dTmYPS{xZ}v?zFpo!;DSv}vpzo! zjO#UoNXFsNo2u*_7acz8>gM9$-1vCM2g&Fvmp$(n8A`9Zp$!cUx~G5c17nTU<^6?B zem>$gGHnP!B4np4KCJyS+vLLk-))m9k8m9IklWG0ofZjI)u_y{#mFutMaA?b99-O% z=H~3QG&(vuF8k$?cMo5iEVedSc6D_PRygDX6WmwxB%Y$b+-?a-wFXGY6%a3z_3v#| z?N2-Y!-HbLG6jog{2yc^`{Ms{OqEWQLcTJ3d1__WX%#NOm^QqRkB@(b8NTbgxb#6H z@_hvh6q;96<+?W2)E;9!b28hLUkanIqehU2fuuDvfmCmQWA^s(X^9tL_7i)KaXZZe zIIRhD)mSN^9DDae}fWEAc`RqDT7WNlA6i&gx!9h!FaJawo)0`Qo zNk~c->aQ2W7+?@c+XP}$%>xbLaa>Q$Yv}1IPD*-gL>1dHW4&tYsVwyL=~JK^Vko+x zKp2?~R_zI*CD`iLH~`O0v_F0O>eU|(egOeW%!7a5SkvUQomWQ2{I#(*VTPH8I&m-Q z#{GrH1lO+z$HXw4r|`c@Na(nQAfM3!3?(BINR0|C3T?>-Brwr#!HFObSN~r&n#)jt zEc{TdW22+M`6o=+*cn0^{4~Kw>)A8mM*2G8>D{E0`%Q`zwbG}bI$ek4==90ab~S&-S;Q%`+lDF8{0Y*Hm@|qb4?N1NJs6W3Ie!T0mXR++xn=m>kOKweJ@wTnMyA+#?1l6Zf<^hE2 zA^2>Tyuo#fQJ*|_Ga(@%-(&=;VTWyd5Ii>chbg)EYBj0-|7nZ+0$#Tdhc+}cl#pos zNXojjwDkG&=h&sqiO8UHT3T8OVGi4iwUWz#Zj&BDn^!}jo;FtOUmcm}IjtMz*@1z9 zEJg9$23=Z67?C?rysIop$#Urp+~5Av+`;r@TOz)aCNKv?MwaQd-?RA4MPIzPyY<4+ z668Q@Yim}r)84w19P1odEHrvH%(yg>i*$8$)pW_<&#!})CDiKkftSj6f{!eG2T1qL z?~+(JyNu}u2%>LDIy$;S%UQY);T%?&tkFtEybpqE>uY^|rWVZ6(NTjT$(=dksc}O9 z?V7whyYcYS7@o>lt;jO<1wMn#IEo4{K0(ZU<6}WV0Z@OPMXVZCr^e(l3@UWS;IUz| zXle%mPq($Tff#=_@aVyV#fCuo24&Z_<)kD!HU_rSro9IG`c+dv>4iG{zECkZA5vdW z&{QRNJo@b|NyNy``jFFiH8;N=w6Zfro#Nj6GPvz31pHF58aX)}b?hwlmQ1Awv7x9PvJ87HcyK zQ}UvsUdhSHA*gp;^$LEDV4;ET!~q}OLw)9;LZFjLL`5wpa5(|<0J1OA?svt4?|<)H zcXE|$i=XaF7mMUpSu3`fmIdx4Dd|Rx`tX-GYf#THh|_+iJF^XzNt#=^-V3o9Hcl#D z6!?ZGuU=)OoFOL8{h{`Mwgt$vm}lN}BSA8Fd7PmF zAD>Ig(m{Ex12XF`A_H_Dmn_ymsprkL7OYu|a&>bPY9AaNlw_c%XUc}E&?glIl$HCx zhOy2sdC`gt_(TAq*g$uYna%&_b(Jb7ZYvmk_PczK2%LqQt+ zJT5l&?%q5!Ugh9Wf_xj!A`!`~=Lav{Uc#TtA;MPv+hmo`+y$snM0)wM3+fOyYseJb zKYYN0KuAhX4nioc^_w-P9t?eE7{PdXd9(CEB&oaAevW`kQJ zT}SVp%FWFMMxMDXxBF(fj^$N(LbX6fIDBUOYnY9VjTqPJ6&Zv=mTM?wtwP4j@}_VO zy)XpQNb5_I{hK#$+U^sczh$^mE3*lV8t~{03V+TR`y0!{=N<;*|I^7k7 z!RRyJ$We5(TL#S1T+kIw3zm+X!2eu>Wk;jowG%_%=Yy6 z+XLn3>Pj#U_l$~)a&~rhcXzL-s5sBFEzT~$*LflA;s+&nqBOy$Qc_YTCW}Cb&CJdc zHN|F#?SbfB-(YxPA88e^^K41%iANh}6bOFCIJKw*BA6|MRC9 zK6qtC#T^EQwAk2Kkjx1Qx|>U50_U}}WD=$A^8nI_pH|umyGTkZLKVh>_QQ&`g&3BU zlqe}F^&qeHUe4+4=*VHKSiAYqX?MG#;>kw=zA+H1DdPR8TlOL%XTzB&DBg%6lC3(G z!|9KlNvkm~+o=%&YaVZAsoYWt*93aY@G73+ac}33>JQW}Lhp8i#0)t$2>j4i-!UjL zFQZ_!-sI`Y$sgXm3uZM|H8(d`RlOxv0Kj8weO`=lc6L@T?2=(3swEVKPq023`iqX5YX4B%gxnoy`%4@5&X#H4s!XHA&h5lo5=wKahqw$3%sMqVKwv#1;poHq=_iNEhek zK4f667zjukrlE$?H-RgN6wuhoBZPWbKNk*|+~(p6S8Z#4{rX5EYGZ4Qm6es_(IZ{vP(VW=sP3K~6(A_z zk~?N(WZb`-Zp7z`=6xu{0ARy>~TV($*%Mva9x5{?`OJ# zES0JTimdk5^2&&Bae`+JozEvKD0n~i3>4f0O~`|Zmjo@yex)WRQUoTXrUG;bR1^Tr zytzD~$A^_9!=bO^6gS-5yC`BLTYD3DVDQ^N`^vx#KfSovTA}%g& zrGVXLX%u)Tdk2T95sGY2fS^WwxY?n-T`OVXhG@RGRcK$7&)Mg}_utI@0LHMrp4E7; zF-e#8(np{Xf94Rezh~YCs#t5qyrWCa39+?`9k}*=Zq5jeCL<-?8n7CI=D{2$67qiE zH;7~)Kd@S`9N&+pffPg?4rD2~0pF2^s<=$4=URV$?4N~>*E>c#hXk&O4E?B&G{hXg4yp#-tcV6v& zlGI=jN(rgp2GbOClX7z{I#U$t8tr0Hy399_{XiA#I!Q&fBiH5zMAmQY74i~hd%g%` znM(QAb4|hB!{f9=aF0lv4)`;5j{TXFlq&{<0mM2(HN_JC%vec5LBX8Plzggwb^qv< zsAr0Vcz-@VJ}hlsax(qZtk#CPxw*EzpF>4^qb_-Qtl>5jP2pL^g*cpLlIsJ_8cugi zbT%GIO9O|0y07lM(umFgNmD)%d?zU>DKWv7xlI#7!Aq6i`x9(vnBf85m3-Jx(ZY^ZYrHSR}Lsgj7O8 z0)apTz;n%%-6dVo91WeFYY__`v{(kIg@+Fx#$Fng$~jKnHYhZL^z-+ha)o*uLPGT* zhy&^K{)|lnwB24I0pM_3>rl?(O1oI{ zx1YtuoGf8dVF*6j3Knu16&1wwozxCgNx4-QjP`teg;m;EA_>2f7NQ;NGNmPZ+J|6vRuI+N#^D2@^*1^Yha>s8jsD6e2aX zAv>o}i-E*(7h?N!_@tyRqbQ|rJ+=`Q5%~}v9^Ojcg@1Wo!X7xU*-RqrsXlvYMa59V zdwoDy&t>Kk{pilr1gY0M6!Ch_2nNCf*p*(^-6WZ0!0nAV+C@iTFmezsi`&c1*=~v} zitdt1SthWfM~_kpc>2t>5aPx4r#lj*gk2a3_K!)L^uq8K7-(y210SrVbr*XEq-Ck~ z;^#+WYbz_UbO8QTxlMjNrCVHHW@=O13PXA&YYTYNhtV|q&x-c>CGFnWnAn@wdLhu94 zfVrJ_?@rm-*(oXY0j^oHji~g0{XC52W0D}Tz{eZ>tl=v(RQL-y)t%5*ZQ$jGn3_5H zzAn~c5+tz3>I4{yIXOPQO0~UTUA!)YzT)0D*_M`@c~}oApcN=$mFFlFYJRX}eQu!d zM45oCzS5DyazIYm#UsCeJ`m)uQRXCgLFt;t#uQEOW52Z&VJZH%u7ZTqfy`cTRdRQ8 z;|Fe5NJvOiQ&U2s6+Eqvx1LWYNhWR>=`?+czQmGBPszf95oSU6-W7?AW$LlDv&+Nb z++AI-PlXoc0&@6z@qGxF9d2#)CbvCce=H|&*(qx3_?@lwp#rnF6-|we_8=mxWnR8~ z2`oCmJ%ES$`aeO0`wAXEP<9bo_(zW0)*v>YK82c#!0#+CE#*U%we|U-NY&oHzIIEH zIUPt{v4*^}XU+ud*PTXBaH@2vzBQWbFF<>>T)>|?e?Evs2Q1Y))Z|Q8b~85!1KY8N zz~SMSz}zyHZ7+c@O?fXo*AYs&I1VVQ6k_AD}GoZATm2b|n=(gNOeW}~=A%@=rlCkjDescC`GfGh<4brTb~!UKz8H+GvT}+_6I`?0 z`NY)J)QX=YApwCPqH<`cte~K$yqq_tmEgpQdU$11)7;N*$voQ&E^clo()GhoH2mE{ zfEm>8-Mjbg+qX!k-GU}QeFKA-m>6HS!-ahb^YrucgU$g|5Ev?TFwobh8H;<`+u13r z4+GD9qB%02I8fMPV?kZ-SB~YZA|0eZy4z(XAwOy>aLmp%5q*w3?B%_-~YP37pd3kvw~{P{*=V&WppS;;J--nz!d{ybwfOG_5C8U8*C zOIkugcd-oD*WFzQ=j$x@^z>wTwviE2))R!(z>ELv z0z!Do2k4ya0YUkc`N~^_q&$xH3JS2<$L9h+ecJvtolg1Ta5=vq2{7fROD!$piKzU- zLP-kJqS?7Q(Y}DU%PUiJ14Y2xnvaKbST(?7ep9P?5=uO$IJGtA*>o-u*0CNw0)+hx zF;thQpBgC2OHVh}(6}ShDzmh_%=ect8L9b5>P#KyIVUz0>g&62?om$#1aLT%JD zqBQ!t;IYGZ8F1Agy>7DIxxRy-AH|sbXFlUrj7DRkuc+(DyGxcOoCRFlmM&dfQevS< zm}%I9l)Soo3U+9$IQ1~!r>E`ay_c)9%C8*5N09-XyL9PN9Op}bDuBK%EiDC{lo_Gv ztd*q^ZyqCWD8Rl0bn2RjbW&ANsK=o&XAOPV0<(wYyhF&p{SNTM3~(~xRt!k98_KR0rq!P7J5EKH8wV~uhIPZ#o;2N-|wu*_+x{`@=@~g@>SKGPf*>!}3gyRzvtjx^Nm}^{|7a0fUjFhCLx$4iA z6?2m^0($qeNd%|7E}3rvIwGOh)7O_w{?^*sy1jT`)p52r7omh(I6+WNy*-;Zv|&dP z9zkM`Llv*Ii5S|AH-@nD_^A&lLe#<2)eOc|a{IM0zYDIQ^Y94q{StUFbAQfN8= z?!rB&b;sQ9hAs@)fTpG<#^QwvfEbrAUBbW>o){+>lIwfjB-p3pFG%)2Bn>Urou+=2 z%Hh|_cZ{!}-xCF5JD~y^r9kglfwk7&-Wwz&&x(j|Fbnn}-HAzM6%>S?!AMEX@cCvF zqJ3Fev{G>`A@QQ&bck5%4*gZENDz>QH^1W<%L3*M_4H^RHbK5-$BK0NNR}10AqiCO z;U8JfTz3iqB2Mmc-@Jh5b>3rlb|i(ST(w}+UFj3XTvJ!o+%A}xUzWPtD~yT0{07zX z7Bk4}M}pP#zQy(P$B(R6-=7=+K7P^=>cr7AFmy8LU@~8pA6uijxI=X6ln-=buM-GA z7Z(?SnA2y@_$j!DhYtXE=(O?u1a!(w?J32l-K05m4*sWBQGH})NiOU|ewlR?fV!FDzA|hv#KQBRN zN1Bd%MjEj4y&}B9P{2BJ;1)_ zQGo4j?cpvf<-NV3gI-hZ@$E&I$;s2(kt|02-5niT`ue}X)knTHF59_wZ9x~|!Ws0` zg;OOSpFcZ)yzTYjLvmW$cn_QmQ0+4wJRXlW zG}OXSOuqyMc?;pXQ_&U0eVX+Q4XGbwf4VYe#)VF7G#@9pTQ z@JNTX9;oQBJ;AdFyL_4Q>V;o-{jFL`hz@oaX==dWuye%3fr{?H1&jFT_6-b#<2EPt zdQ0ur(|+4vBt9WEHJnbpG5Eo39#KPFoh~OA7s~AUDp0~*U91ltECRyLi3j1KiodWj zy_o6w>C??5+pXEWDWcJ*+q3=(0#Bb31a|lK{>&xXwV|Z+_i8!n9gq5Bh-dk%ZaP%x zL6fiBTW1*=8B3`hq$gvVaO%I8I^Sf(eb|Gx_m!z>LtS0e48K0i*Vh-^zcdPs=6d<% z2%Wcw$M>nrVXHKBbnh=kfCrT;Rg3aLWyHI*wYQ^wb9>fzYIX!)OM0!DG9k+IN=9~e zHV$Rjo$;nknlB<)SX4AF2?TmsZ0u88(*Oed2X|bhGPrnQWUZ$$7;!)`g<_E%K5L}#JVfh zv_$h^?P?CB#*cYq)s8JE!llFk2rs6#hyJOC7zCeyQ+qw|0vn^1QP~I=Bjz;;=6+=C z2=dsmW0NJ?edfmOY;23hG~-R-_@t!d-xmGLqb9nrc)X@cDYZi2JP;@R{QS_7PwCN($U)obRJ--P@^cp_Z{Rn6CZ|7t@uFTw zhVEOgL6L65!>5^^=y!!g7=EfJdU zU%h&D;q9}``g7`LkM|u&T4JJylE6O%QBi$FY#Vf+`PG8HXWli@m0+TmX?TM+Z%Tdr z`n4-vO-xYmC1LGlxx!P`)zy7CbT$a4(HjkLmq$+I?;dx_6FRsDF{t#WtMSk&XK9_L zw_hKy3OuJije5$@-%8QUxl9zIZD2sp)3-QMTTc!FW90ildO6|bLpam`x5!C{|2HFi zJN)>e=UpIB6!?s^SD&5}6q_YE)M$sNwI8Up!=KCz@EI8y?>>B>r=`_1G=$U9DVEw7 zw7P^C%FtaMnr#eWTAxebt`_;{nG=8HfjY{Zt9?W~cOaRUVO7tN#3cZZs`b6r52 zp6Gft*%E!@<2`^ik&%dtSwMljdrYEKvoh7*Lx)j$os$}*YU|+^n?qo8vMunSbndHE z1D0<3=g&KL?u>s8BOxOT4+^S9#1s}5I_>SQv<(hM;AqJ)iiER5v_`zVZ~UOGQX!3Q z@zcG@OJBn|q!s%5dXI{|nPzEhXFFGv8m(j!Q9Y z2%t3|Pn3!mK1+KmSJ7drjS7vw{R}!66U(8bs3@$apWZ+X-H`i=KGYvKzqID|!5qtJ z%Taf%yW#Z@gF3iof4<3i3u3^rMnk1vpSM(2y8PPhHAn~e+>M-Mh+$1La7>;w{GgnR zNA9H=LPwPx@oqr2wY9zf@F5o;f`ZFuyyg;q$^#9OV!qGM5B2xY3=c=O>N-0+Yhl!N zbgWi?ej|6>D6gn^LYqE!0srpfN9+-06%`alLP?2~j12K8+7Csunw*WO{QC8y=#Qx> zz1ZAaRz^lfqN1YI3WOIgU25y@o*W$wh>UEuAT$p_`HZ#xqtCIjv}F7DMh9gaYe`bl zd%2wv^1Ud3lB-usHpjzqDzlrpi=6fxO_Y8#@=#EycW0=V*shvdT6%eV2gweU*sU8o zZS@V8yMX*xbKFGr`Ue8)`5MAh3lhT1)WpP>?8JV$MqI$t?9UrU`Jq+-pv1+wOBxCH zqS0t9#y23qe0STXS3FVr?Rj!coR~pZ8nyy_oEsOK>T5DBB~+h?i08swymd=-(Rip- zMH>e4g&u(jXF-R0c+~SL09Opu^)&`%L2C8$M)h!)!5E;xWal4hVR%V|rDB*lji`Hwbk-z!+?Vw zz@huf_IBohsN{Ql&ZF;=*6xt~GVzwotI@iPmLpOhiaQ4e27nx7-Yc3fRmw9W+ma2q zEwM8h<2}g@=?Fz8rtF*?=6w;p5UU`-#|NFY0K5YWl8I@3wl63={A-pi#t?XGIce#P zi&Z8j&@g7e=E7f#CX+Yk8>2a6cQCv#5fIi?Op^HP2L+On*iW2nY$2a2E&ySa><@QY zVlEW!DEjf^2T=IH@`oKXJ>Hw{$|TF?;&4r&4@Kr5cHG)QkhL*n5Umk6ait!ek2(wI zi)nqt&Yq@_E%2-6G+Yy4{z?dN6|I}3E((Mt-s}bzII6nsA=_z$oSfXQDL?|PazhS! zdJ&c&XI#Pk@#e_+k&%PBke4td{w1xsu5LmHQhu(yhODTlXvl6s%o@WDf~KplFRZB; zSen_zCMhaU7*ia$Eg=%v$N2boLoeEfZU+a4%ahFd@eazt`8(i`GiqSBfn)z#X>#2d&tY;Talw<0pv+Q7%n*rw5!8Jkca*eAmy<4+J61 zl(>(R!3C9Z*F7LNi|s9MkOE=IZ8o==8WL~(?ZDe)aWf@FB!eYJxZ$0XoavO1Z$4e=RZQrs}VRHV^`_nDX2F*qq37@%F0PFm|U9n%wH! zB-RP@$j_fY2bAKYNO^zWYmXNTQgCmAW{i)tRKhE}y4DBDb+S+{v;-Jl4sqc~AD{bU zM*}8ufZ%*cGAGoDe@X8IO}&MqVO4HtNd)!tQAA|y(uF$q_S@-YyL7t@eEsRqP_U{@ z&Dt|(&U6Qnkr~0FqoX^fFH#D$uvkPF7KSPxzt-{)^L=h^?)&#>hmmx}T>XXNa>Fp> zCW)#wZy)k{GtVBU5+Q#&3S4s3R`|e0NrDliXgE6-=zo5ZwM?DnTe9-No8<*IBF%Bo zdq-d2(uM^TX=`gsA}{x0f3;RHMCB2~*;e!3u zk34hsY;o9y$EVQH9zZhiX+Q`L|k))THC$Qn3Sy)hZ=5b>znnuUlM&Hp}Ci z%M)hdA@|jf2{{0~a?njpNzuYYXG5E7kF4Cs-!CjvCL7DaVzDD2w&&~DuV-dv`tpp2 zO6-C>T`c{%gs^DX4@Z*^zIU!(2$vz#fK_d7y-Sb&f(%Ggx3}k1a4#w08uDKfDAcuE zpL089jzbZgJn0Q%5||qFe)leFy}GlL1rGPz4pJgKU6-TRp3qQHq0x~bvGP6O7FG-P z19z%05OW}D*_GKXN0ys;pm*tgZESQuM^y*qL_YkfF!w)H9X|yQ6_`yxqnevWfG+lC^M+#C zfNX6C4-{wEQ_-2~UvX`3AV<&tjZn}ULF&VY4^w^u_iAipWNB`$#n_XR*`}vwfm=;? z+WBxYQ8qayCT5dQ*qMKYK>bL|cZ>T^tqlnnG!o{VkqVjciBwD~pM@vf(0gbB$1_nY{2w}=ICng}3ds%*ky*>K)WJT%YejJ)~ zFVrC379Y{g)20WgNiwlc^h72gFsda!E02+~n>h!(eha~k+}-rhW$ueZ1D z6H${X$M%~*_Hd{PBrNt^Mpm`~9+Q?P8V^--q6-Q_9J~R1_w@Gu939mu1_uXQ zdf{*5UpAw#@$n*n+%bmAHJ|xE=!)s+=m3-MeSAjqo}mY|)cH~h$CQbP zh*-^ZQ;^w9NJvC8|JsU=bkyhr@f^5%hm|#t4-U=Vg2y;|u^&l*u7_)GZ!AhrfB$|I zd}96NyyMO<^%d6rB}w{IiD6-3F$A|dv9UN(%!xpJ{Fu$s_Rt?~ZqCes!Cn4KPpB8a zmRpXLH^s_AQ?7k}3WU?+`6eSTVZ#qHJ=x60>$J1NA7~ODfDTtB((~g-CMG5*Ybhy9 zii>5_RUH<8@XO|E)n9u5Msoec*=sZX1F z<1)3R$9_G}2k{mnHay(r^s#Ir6Qwqp&5DW&BM>dXyp|qSr#?+Y6o7jOg4yKy5%Uz( zCvJz{s;beF^+AqT!$%bEqa)cZ&KwNRN2tSGu~=v>A1vp?8y^)@$7#7PUb!MIF5c48 zA}%k#@%=5iu-VpFF7m4g!^Q$F0EQHqjCGPUO3tt#yEJD?h&(Y%F|_opnzj zI9RHIYVeS~tNuXok>9=3j6ZE5u5RR|!0*YX$B+DbUE?pU{v?!c^MIn16yF$dA>VYF zL6F#s;cirez_J!HG=v8l_U3p5ZFf2CkOU@3)2W(5U8K!nm-Dgc+?*U>Uhdw#yC9@d zO+Pl}>2vY=_29_JA+R8_vY60Nb|4*Cta>JQQwjj;czJoXC3Mq~3ahaAHHP+ebi^K@ zI@3$^T+orTXFufUKPr6v@#9B+etvuqUv$9&5m@;xkQ6O-Dmj)myXT z;^McV$>g69MT*$j+4bIwABOBNhuxZ?zO$r>kZbs;ap= z03xhN5H23C1-l}!&kVZYzP#y=q$wBp?Vrl)F7GaRUBioeic6>HJU)Dgc?50g z&h-S#UHyS1I|u|>K%p7l2e(>J9of<}({~cOWD3+4Ft^y8sKO614U!>TK6Ui;gfMRo?ijBtD=SYjj9_?W(uRe>f!qk(!@>UBUKz29c6d-|+BD$L#hJ zZ4{~+p$YJLe0;p1px}Um%%u+L1c&h5ay z%Eiq+2n?~Z4VAF4Fb${H)Bu_7^wb3KA)8-I)hh|<)HNSWmVWr~f%51cu!lq6%BF%}cu zfts2$7En}F>=SUprxlAH3`4rXONuH>eW2G#I<50;%`njw*l!~9JXcoC_s_t&cYq>= zF{qBtPSL>o+Ktzz&OP(Kgow1{BHr`j+eB?CT6If`-!#wY9VZJdDKQ7DY63qG-!Pn1Q} z+nsNsX=_{9fea{2;bPEi{mh>e55yE;P(VzC5y3x?X5^`ES6x|N31QMXyb^!7OV%{N zq->M^fO9g^(hF0be>|){EUQChBAcUM zFysKxUanX*U*8!3>b`u^SR2N(v*I5TqVV+TA-r?dAfNE@(YD`uph19^icnTjd6A}W z?&PjSCnhKu{JZzj-P2={x$irFA-TDCyVRU!0NOw$zkTMB@6M7iBz)7?hB-4BD7mMn zGmd$Y=AhI-_dXBq7>bEwh3xkfQ`l`jDSwj?p$~b>%OO@z==H^hatlaNVU*hD+ zlWp(GI4l>qVm^Nc;S*su$0jBw_8tKFN4ZQ?`OfxsuO968ZUOi^pQOKfMjC11Z=ER$ z?c)r9eBG+xd@ZOLp0oy%Yu7B7$G;XAbFL8!_Wh#z(%t=tiOK(*r>E!2(vo6{?J9sb z-yExFc*qQbl(d^Ku^Rqz)bCj@x5du@94ez0dzev{nCJlpT7f@as~sTb+H4T7uu~ez)qfaHh=f4(o#ViW@t+eEf}fL zc<^V=1;FyBPoK8$9Kx-Nc>aU+kGg<0ssj9>qO1(e^+WrOh1l4;yAOO61yCre*#okD zyq-mLn%~ zUDnk**AR+Tf0_uzu3xp1B@?adLC7Y!Hf!NSb@lXUX=#N71&1%^y_ZXGhfz@8zki>H zrz|=;Iwb}E@S(*_cP4r+t92*DFf1&LjEoE`A*56LZ7$1K>s}v#f?P%L-@cNg_07r% z4(wg)3By1AjEx=SXet2^Am_-~nSuEP0YgF(1k~!T(8hR}anAVy)3NvY2Ua9Ai>Rmf zmDxlSN`{ohFse8RVZ7t?6{WMcG2#t?aJw6XLvM6c)MM?xO>4XmEk{6OR%rqE)V z2r?K|H+dXCo%7t^K5NI53^zCT=nY3l$9wngt&|EPT$D}A zTz~ztHQAqL{-TM33Db}Dbaw+E6SgxnSXmhZ&?mP`btHH6qI5;)IcO6@2U1c}@*1OR zqbm43y3~gC{}HO?o_wzaoUQ9Uo=gsg+d*a;~#tPziLzGu6R91$`TTpHp|*;@2o8> z9#UoY|F8MEK=yyj&s5#K6Iy~+8*^$x0-7y!*$(@#tf;ju@ zCAq&e|57wxDxz*}XV=x$_14vOVX3aJuCr5?nwt7>?BDd(<;#@0L`e;Gb=Q$v?;4T% z`ua4a@!Ov)eVR-dL9`Gzonz2+b|b^9IsnwSQvdSk;2b3GiCRH zbM-Yr2ql3jE+?NvF3bb{js$>vOQ(!r!YZ9O&&82iDd;5QgNcZM=feA8A9G`$wLN$D zH%wmEg^IcXdfwL`IOAx=fINyL_%^wllOP?jUokGSnm5wZ%goN6*1%t9H&0kW9n^<= zDTYuPPeoH@neZ3P75b3Su9yUHI~MM!41W=wENpibbW@yk_+Ta-FT;C%FedGTYFmpY zoDauxQ{BjDZg}`{!K4=%$3_J3Y{bOG&0OoB;^V2SK_Jv5cPq95G@H%w{PN|o-gTg< zu00i$adUIi)6?VS}RO6qbp5pNLI>^Tr#YGsaG(8ju}eK<#t%$ywP+*3WhM1dHIQ`$gZ-*b-9I=)r+^XJbS5r%H>_&;1*^aGBEt|v$DGNU?EPttD({hg^N z>mJB|U?MJEy0lT1`s3TQxj@}1BBI9TW@BAl8hUz>7cZbw_y!r$)yh;YE53%YW+y># zw?7piapP(vnVgOiGyyQgfos9Fxm~hS5IF4 z$#l&Wk3pE9azHN9?@#>?)FHJDD!+zv1j(SgGu|xO;EGrG9tMT)jCk|hASGSaTwYqz zee3LeEaY#K^ek*A@;rNcdwa5Vq%OC%wg#m6duLpJ4{eP358V8%Z>%-uS-TMT&We=2 z$+sk#oSk@{9S&&NzBy>&r9F-uq%_ZJmR|m9F&~6k|A@g@63`LDR(RLMZg7rH|6rPijlDkF;Mp zJ=<7%@dEgA*MK~y&9R`iOF-crZ!e~miWC1J6)zfiO)^10_g5QMq!$yNmkP9XxTWJn zxRn<1R55gEHQSrZ;o;#zD)R$-md(}GZgWT@nDlA`efnW=EyG<-oj$E+XlQO~+R)sr zGp%g4y|Y6*Yx3$=C2s_&q2eHsP#urSiunW?EBZD?TaqnDSFQfRyu zEHg8+gHldTE(A4+7O6*?tI_}s`KK~#o(`7S1TPO1SwVq~krBgTb*KXgNI1gxapJjj ztYLgc#=_5U$*ZfY$4{OMxE1{OuDIgwvHtY9#!r6?a$^Mi60|sf^~XZb6a=iTH_Ba3 z<{1w$WD}L3X%*a?&Yr+qS44X=?k*NrNpc%_?lgl=sEez1;LL%lA zSoJgBA1f@atjgAh=}#NM;9}6Up@Msk6#&x9_nx0BS^cAt#x}Cnv2JhI#+k>{B(U;;p%&EADxDRt^ph zf`X?Qy*)h*(P#@((<~hB*8P`9uzU|5JdngkB>|5Fz2+7k0|Zl>T|{-IU)Dd4aFNFu!mumbzOifaH3wQ1<+bX8*> zm`{pN;Wsum)SPy5`AYG(8I<#*WRnY^xbH&}fCqZ?FJxE;ce&1LbaVFLEq#ykvWa>^ ztz1D8xVRkGtNt%ZF>CmLM2f9_sKq4vex$r$KXL_!MZ=uWQq5&WodNgrcVDWeruKg@ zrs@=$nj=biMpXCi;o6Z}8Nky#Njh`tR9#b3T*BO7iO99b&QZ%#_n=AYpzx3Tok*6F z^LZfS09+0~@hE~P9OJ6_M|Buz>+KTE|H*~cG&T~^GW;j{`?JxjO3{6dGxOoYhXU&) z(e1y>#GRFYFB50%Pq{I&nT-<#E`%5=Tt*-eR8&;JOdCE?q;yDL`j0hY`4W|D&02l^ z{WFu3G#}qL%O_{21_WFQpC?mdQki-Jv#3b2cg81Qaerjpy8%LMv+4Wy@6sCT>PMeV z65)fn9e$-KWEYN;d^nt3V>TOaY%=Ovz!fg(n_DmJ-)5y?SE$t=4{9HQkp3TeP$93)N~q zl6E~TlQGBfYxkn+vj_irWqiix#9UonnORua+1SP=CbF+4v;DozD5wuRdky~5yT9ch zm0?3zZf-7ERg3%;Z>N^}5AWX(+AqK8{obP!IRI2(KEh<_ z6VT;`-5IJSwrm_6wY9Y($spmj^2&D53&Z6ABpA!Kk+by;K=+I{hKx*33O*~y&#&`p z3rm>xT!`G;rdIKZk94&C^7<&i33ay>n|mH{xDBHfy?A=Q$XC4Ap|L}i&)(d>-YG_V zuRyPvN1^x`6=p%6OsYmr55pFK>>QmX?AnM&hIT?fP)&z1{UP zDM`t5WbAj1M0_}%mcO#j#BrOBo+@%nulmvFFcYW5c

    b{2s|PyA&tp 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 keVGngEhHVy-6ORY^L^&~tEbNII6CVHFV-ZQSv_ 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%0Dd`~2#WvBn>TkB`+}MuM;Dj3A0HBAq48nLsU4d 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 @A)AiNX8ffmA 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�YxlZ>|}~KG+i6pany|Up+p~W*w~DpB6ea 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≈M_WO0dt#Ba+i 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 z zRulW-==dTPF^R~5oa2CB%@Pp;PH3SRVp7t1cHAdMtGe5!kW6_wF6?1!p7z3-T}Zol zNT@u0Uv)cKQTnb>E(w~mR9+FqmNjHc5}@9=i_;1Swhtql6!@CmuyG72%<{?=MxI$= z+=;q)H~fc8qsr=PCW<+w!K>x^^Wyg@TjIV>u~O3-Kb5}40geXRA6b1QN&M9;FC7xH zb*Wrq@2QL25$X=%$zUi6~(V!wi(KTY7Ao?C&cUy?s8(Z&*2|6XEvg(7|9{`E%^ulB^{qMLiz zkZqaHjY@$hdEBV$t_+RRb}Mc{6sSlogxodEIgg;vgl@J-*{xN1{3RwA>&{jV0e#7z zn1yH07uUfVmb;+|8oHd-4fl>2LpUQJF1$f&F7uCszH~%bvX3;!SNwo=@iaHz{&|qN z4-o1+8fD;hsic)9E|{m%ZZxcpyE$p;$NiQ;#S)iCND;2|24Q1VWgCtcZy9FMM2iC+ zri*m6wlTP~$ZF>~-NuxyTdiD*XApTo6Z8jvfOZWAXw^J8<4)(AlsrdaDxll0hs;gt z+XrKH71tJJ!Mvj0QDXwnhKRu$f4`#cop4sl#7nef`cWzjQ| zQ^g;x6>ryF@UANuUgCEmyxR>)U>hK0M%ye&ma;pP8WxMGwRUyN)DnD%>8Ji|jStS1 zd_>0&&BgV+at~`&B4`?Jv6X#-Lkv3e8H*dlxkZaQ?>ZY$!oMCm4^t?-=?-ePe~Y2l zq&Nw5WxPq?MOL6$5wXK@ zlglPPxEaNi^P_c4a!Wc*TH zn7gs%!fc}dRW#_$>OK#v)qn zP^n#@OD8`)G+a5=8YndiB`-kOEkC%hmb;>GC4F67gGNtdDp+U6vX7}%E7}hVgEYa)s@>hOM6j^c1;W?NsEfwg$T7vu>s%uw7(Y323t`{y zT!2#wj?r1~(}+azj*9dLR_Ebpa|~A~b>rYh;mk}Ja@iPstMlAY4YW?X))|+3BI3NC zlAAF#r?Kv(3OM7#o6PWAc?yI6kt;!+igY)5-EJs$_oqxIzQfc_W1rklKG)|LbhwA_ zcJ}{s2hPRB!S=uKnwyD>_5Y36?q;efHkcgbVae{xK zO#uOkx4)@D@{16WE@)>5j;^lYL3l_)SlilBxv={%!ka+N15ixc;I`0jD;z@j$3Q=2 z+(ZnpLTeB~Z&M|pO(LB^`-c6w0|dsvVI;RfIR$BkT>JLE0db8uLPN-K-_g~tn1QhO zfLEZm0H5FDPu>p=5~yc)rtv|L(|xF*PSJm>K(rD3SpN#jA;da~JAjDFPZ&wY34ptz zAv*$yUWuM~2k#DrfyA-tfzE5k`gp}O`-$VEz>t6&epkrn>e$?IYk*o(g1NZ@3Z%ep z>pqT&|JIM}%yvC}IS;!jWza4^8Q6jcYHNR6k1TE^N+QBIIziA*{myi<9{kueL68EA zba!`?koW-AqXJ!<8x1|#AnUnH-aDK*Jn0ec=EX5d2CV4O8ahOvzuksjMFMpM086#A zu5Udlj`@@73J3&{4}(B6LF$P9YyUOxV-2qP8FZ%!5ZwkE?RF!1d2C4#VyrVIqS?KtY3a|MDhNKs~yX z`w!EI)))dc^uhFQRs5peJhq{%{}7R>$Regk}f2<{1$p!z}IL3c+s ze_`*I$3VaT8wY;^f`AC#2=5ut4-u*0_Jx0d#ew){{rkH@b^V5ZFLws95&F82{HC7m zO@4y?_XG^kqvdQ=l`lRh#pP$yrvo;^*vENgMGeNg0xJ9rS;_;zc&b} zuNTWroqMgnlT0A^|Jb$U0)yA(UIeXuGC4+7REBx0pVI2;2b0A!x*=K5eqB}0%t)An zD9PGH-LY@()KV^zUZ?0Pgxi=<>nM^FuB7Cpt^14_e|N(Ui+qcIhFCLqv^NuVWd#C=C<+Jn{KoF0xO-sKobCT^( z<{Obky{S$uZK6qgyeLovzGjV3O=>s z8uh@r`8A3iP{fQWY(;M$B$x5^;&M_5HTl1E>cvSstgKVf&31UYxHsKQGT=G0AGrr0Ied(kWGmJN*5X<%cq z7s)b^cFaFT^UU0~+QhWz4UIf+!IR=6&Uv%fKA7VYHr6Xp^xLYAzfDYORpYk!fs*mX zjMrs){%y_~t0^M=fk2*$NjK^+vkyYKKa>UUxt9n2koa|%;U3ZYS^~leX+Vss*Y%(2 zKI5~yD6{kXI_KZLwkFi~!F6=_J1byFR~tgW&%52WCzd{amB&Q`(_LO~bf9Z5+3@Z+ z`IOYrJi!rD`-s_16RjJfyF->NT35cAZY!bg7EjAhLMPn{B865Tx|x*o2eMqyH$AUk z`rU&Hxgi+iNYQrqNDuBv;o!DcLb9OP+D(&OA#oxbd%-Hz9bU-62+>@|{wV?936q%W zX#Hqs{D4`NyLN=NIYr5r(3>wk1tG0`h_E`iQOWF1`TQ(M&0u!qZr45aAaXLQNQrt# ze&;qVYpCGDu?50u^)l;tOtM*SS?iCWbw3J1IHhi>A1G8d8|R2;<*_>$BF+}#j0Ur8 z7UH1gj5w{$HfFFsx#EdpLIv6zFhI3k7FYs|(8X36v?~eB@)fP03f(LR3(_?b)M_pH zS#qJNW5^ZKGPNon44rYr97;AQVr#RoZ29hl@bzX0tnW%sO~wbV?H%Rbw9Jn-(yME5 zJu&BlsHw%f;17vE<7Is*_fL_%YhKgU`tVx~aDjglb`wd(=cQCNFCyY@aL#gcp<(jc zpYxZQMN%8>Pq@4~(jyi6O94mN-N7BH8dOybJF&b7c!VTS^ zQ91_%8wBmWOp*}rQHn+Mt(;<f0HzZzTL5;L=NEiFs8}NUcHsW`FZ2B zzHo7EMua`PLA3U0as$yy+u0$MsEw!e&pWscIH@%L1LNk`Hv9VAw)C^w$ zwgPmXUG%@h@ycpD6#lmoBG-!4<9##$am$?Q9%_hmSl+a=!qNK|@D2e9>*{Gmyq@e; ztZa2Pgr1*{H;1u2s^0j@I(kbfIJ!0Vv@%KF*vK9DV%ZYhW~s7{PiaQ{BKkA;r(msE zo0pSaA1a26BDpJ4_l)>V5ENH(DarHQqU#^y_Ecex)=(%6|JJY;`${ouE?|Z8jQ+)CehQH501(-;h7woWx8}Jx7v$YZl&nr>j~!5G z@`R45evyPw-pI#*;4|hev<=yIS`xwxwFQ$yNbTr>z3*|e*&?JrX>$G$``~J62LBzl zT3cHu4))oJNL7f60oVz{O`Rzf`QPEFSB^KP7En~^^B~Zln+1n^s!}5#HryubTeyfR zFfu*1;1+lR7Wfeb`HRy-AxBVbAXGXMNYP%rkN4Tp5!WhfSrk|j6?u-cXi=WT7iRA} z{#rD=s+w86YWeEO)E{0xwFW+}O4F`>OvdiSM4MV(uK?)A>^4J42bE@D8Asg-Ib8U& zeKZqByk3Fod-Bvl(`r{y2+SW({9qr=kZ_mmqDWMimJP!Z(}TUKou>|pT3w{sl8X=zpfh9oAiO<(d#;!ON@i!MIj*WgA6*3 z!dBP!ALZcwbnwhwHFP#$8qw z%Mt$i7IUdyk$R?Pq&52%UXyWkGVbU>N1)&8s*OXG8s-Y9$?SIZs`2-u9b@1L4&b`s zLGj0jn%{5RT5|RRDCauhQ2NBbuhJafy&NVxoEmv9sf*fJCMw8N99uD<9H~LNK;);z z36R}Kc{?SJbvy6-nXIFe_3k70G0FPrc+&#+EYMHNEl_GoKgTE25vKAZA#0-G559a`cq-!(O!BCq`2E3Qda3{reZ&fXg z(`cs!kNT15E#{T?Q}%~jVBVTy5=l{S|8|U9i#ELmeHLj?LcZC9W|hT_M*E7xow4Fd z#~o{lJxir+>y%d|KvjbS4l}D>zjBd14hI3PE(-eQ$~=@{#4LpaI#(){lF02_=9Wc3 zmm8QBJX*!1HkL+V6fM(K!f;6RXRs-;)V3IqiELg3C+OGSUGYw^;-*R?jdj5p_jpvy+#|>VJ!2$PABvZ zQERO`NXaE~1hLZkEyU+%PkZZ$f-|mH7Tl}NY2V4$Xn=>yv_cjC(liHtY+>JN(r?gS zlsV_$X)sHA=oobUsJbcBF{SjIIl{$NlWt~%-!Q!?be&zVcuN5qiMbY|o6ef|0xea| zwLG)%T`6bMltuNWf*Z9a5aC9qn=U@v;C1&bM8a(39Zb$ixo$yPojMaP{uxR7GM`1k zCbXq-d;FGSDeP@fA*y6{JwN_n*@oiiN|&@cp7^OxoM?~YWBggZm?%dX1y*f_GTw?u zsEgb=wfl3Zl~x1qP0a?K&quBN3*OqI>p`HKsRhldvF+um z8YV84J+(&7VK-}Y9>yfWKVOWgo`T9 z9CFBfGyU&CXB4vc-$FOuE*^TJX*(8Ays-m`jF&Wph7(E7ikRm8g)AAkmfeAf%4zMv zHi)R*x~IPc|6B>U54%OVxxkYcYPoqbp6`bjH4=*OZSG%!wCx#V@3Rw<3so6iB55D> z@qy1EXLhtkalHl8Y;q|kCa+&L9kF;==hklo^`Hap)`qH=S6o<{b5RNlUqV2< zZUr1lMSQ4(lbAz@U&tkD(&^;Uydoo=dme({=Hrl9#$-n$+GlDyePieE=AKX_F&BVr+0x;6T;6KGV&VI~`47h_(L z^|yG3-Htrk>EZHd6H?%|%T;_X)jb*->UJoLmENukDwLkLIxpe|BU-04pS=Cy)uEeF zXt})JHoA?O1G2HEtt)$84iBkgVl>C=wJw*+m+>7k_O(LHn2yohhYjx7`Hp*sEotkz z7!0tsm-+M2rOU-Bz7-L6wEyk%Pb;=Yn?yZc{^~1t8xOQR=}*GB5<`bT8hPPH{LMRV zEtxxls3JP;rP0TDFcs)$eG;mhBO@@Nv<>2_A*y&rpUVrmUVict@T2IAy)et-N#Tf{ z3RbX&gpr1L*FlcOqvyIDV;!Y(MF(1ccA7(>j~bgr-iAw5`%L~A%PF}hUm)(=LsNfe z@$(^*C`z$SNu6y4b50a|;;Uv?hKrxs1!z)y4BMATZg7rx;J$u~`9$}S`JAk);(SQ? z38!=fXF9ahs-tX&al!|^(ez^_i)oNM{<|R6S)}34-JqQWFhl6;2EzC?m(Ez>v;y_bK%g^iFuj}3KmAk zD{l3fvOMQqFpPHFxK6NwC;y!3ZE40wpF<$eq+m`8IjK!Jt!dN}+>Hu%jEqe9Y$wDH zreQXP-xLy)uFG56&$CLev0P1CQ}0%?-1DofAG)Zm%U;+_pbd>k$y9{Nd~+;|)xhVa@Nxg- z>zv~1AAgwP+Q{vXXstYV^d+mP8BZ5s-o{|+7=Dby>uSOrSsJwP zm+Isr`4Z4Qvn_6=?jUNlWTnK376-o5pehIg3!R_Er=NamB)oo#Yb}+#KC}jEp|+&2 zEO#ZRLxY)?-SX zy{mDs1vb4g1iNduLGgpfJXPVtX#w8drYJRo9+!R=*i&fl_$7NrY2=VWg&yFpKk$_b~+zk-KH_1957nxIRZnBY>OmLA|l z1La?@>veH42uT2L@s&rW)b3xlPX?Jr_Y=3{k^=85BS7qHSK-sD3#8%J5&(gW{KCgN zY<%`B0g{X6Oa5Q1HY5s`Pw9nI_vFkiZ;`0IXloU z&FA2wsd=wLmUDuaPwzRj_DpL16q?@qsG6u4b=KO#qc8q&29mCx#Bftgm)kzRt%!=7 z#B>iI@vTJ2Fo>8bdbh3JWh(4r< zuX!Oq_PX9Ay*U%^CTCFxCvckp`0NtoBgfY(E{55Ygq`$vlBMR{mzUE%O85@_mOr9A zmRbrsg-*mC#oIEeoug+AUv&&KGceJGl7DV}JBD0#rFHV}_Z-yNmcd=7_jcTj^EOUu z`96mf_5OB~QL#cfNhI`N{c-x1MCfKv&`~vA8)Gd^&vBloE0_R`=^fDlm*Yj%R9<++ zcn5&5)tVCzZ8A_EL3ChL+>(FJIbBy%J^U3A-QujcVIY-y2U>$w65}h-sBHPGC@$KGF8fy7qe>yH;rVC)#{cc%H^$OBit>`sC3 zx(0dCn$|VCmNbF{27xU$xZLU^Jeu2`k^!Cl2S3pm9XdZ+`j~dxuflQqsTQuv(W1C$ z7UkoCKofGulKS2mWvX|hfz)RPUj-kZtD>dLNeF?jHP>-X58puAUwdKh@xUF!g_jp4^peWK`i45%iYL>4pcf4%%9kd7LC@VA2aI#MQb^ELvM+_ieKNK+ zwHF9AQoZ41IL_A-s_RhHwpRhAOAB9u=_~1-hhQBO&XVQQl8o-QJ(`H-6pU?k2-ax% zNCf>fpDu7l?4B1upbb87?3CS+eo)Gj9h%J6;InI7FP0k9&=l4jsaL;~ef?+WsKKBC zAMesYfwfwOWQxso8D!O@O1Q_a;Q_r|ImJ-;ZtCj3S@As&>+I6Lff3kphn0h*(many z+*W)5OhB{0!J0DO#~&ta8W&M9Z{M(1x!p8{h3@^5GoLsGH-W)G=AU-T?XCvQfwC(K z?dewfUvsCY9#cI?^(ihBX}}rBBX01Y7Fp0*;ECOzjU+gjUl9lX4>%-Hl}F|~%2hfZ zgS?H-swthIHjN2J8m{2IV+-vzl*q7IS{;MMlD}D@`(RsM63q&lh5ZYQ(I})4%ahmW z#(8`I|B3HVBHAQ{(eS==8}hYq86(c9&1sT@ugIU;(j-XbM?Z8Nw~QGZuStD=R6@zM zjH_=QQ0(PhuMUMJ5+cHl!DKiVKzq94Ic5l6)B?Xm$^j#VcynVJ!ASVWqg#$ZHeK(- zP>wm$qv+tNR*d{PSd=1$R3tioX1OXKJH88_K!ktqDCZ~!uL6?zFZJ7|kA}9r@mqaZ z6rC%@To6TO&u9ueeQ@*(??Q?}x~ZPD)C!NebUr4rAJz&H_UE61)CXfL(8Dq3w-)hG z;dtY#CNsp|26-8k_-zw6yB$e%(eCr$!j$yM7Y1^Ts0O)w7hMowN9to-WS2*WzW;Wu{ zo#juK=aUIFZYo-OektD?Ldg!)>{I4V{kIG7o$~jp4Pfx_4(f)>X;Po4($JHB%8Bxr#;wT_ zu}IP`RyQ=>;6fQ4+y>itI73HA3RyjM;8eo$y29fIIFc(KdMuaj`A3hiDAa+5D!U2o z9`M0$WmTZ!Z(@57H|?SAuRsSnC?6@MA|l^WB_ zqJ4%rQtT?eDHk(^IJMwBkMWf+(*7AhKH_*lsy58y8H?#qv_3Y43thB+*wuuXgvR>< z`m^2-qxXmGyK*l>HyMJ|rvP`w;aLYQh z{)!d_Kw{~4Kf-V54*@FP>%y0)K4WF|ZUQ?~9XS!lmuAlNQknD;q7nLE!#Qtf8=$vaF%l3|E|wslF5zQ95tgG$rNE=&!(hX2vsPEFioY^!1hPW zwbmt3BihM7k9H0-sy#8Bhz56$3fwW3$-~_QiOM|nmf$`tp~}_VAF%sU%fbvuTQA%` z`!c=Dh`;Mv!!s3QS=+rc(j%pP@Q!kBXwCS8<} z@VQYw^Ap+>*1HGOs$d%o6~yd*&qtX{pY+1p%w}28bsc=14>4blKm*_uL1Heladp2u z?nq`b4DJf{nXO|+11=X#CJMKGKBTH&Pv~jlcOv3>ZjpsvG9w*DB&1hYdNF|pRV|Ea zC46y}j`vZI6L%1Zlf=l{Em%XQx2CL$D1O$qchSd(y>I+gnNcn*5M>@F(_KwHbWZd2 zRkCBNKkL!Z)sl)^fYs$67;$S3e5d@NT=d{%Iz=;QpL+;v;GDBm-`fT;?9#Q@1zJZC zbJiJB_uWrGB&l?v3nj7pbv^sqa<$e^++?gibPMZ?sdR%@d2T&}RiNUl$vr`3!mQef z&)ap#egCdC#*t{;*&p6^#9ZJBYNi?Lrj|R>T?t#EdrqQ?RjZ)dgB*g^~A*L9w^V`7_F$NrG}Uk<3|iJ#OfgIo=RVWz-RIYJd(?v#k3HM8b1 zv(+%*xR~58vO!eZ4-+zdwmED^W*KYs^;PMZ;HWmSk+;w6%Wr}`V4y>-dCmVG&+y+d zI|@e+0Z-o11hFl}raS!4KuuQYSZKm;G7^Fr1RK+H$(-XmL5rP4FlMhi#W^}0hoj_k>>U^M zkYNFSB|G>{Qk6`tvJ+A)@VHYtlqkJ-z~73fTa+~I@Ozzbsu3^Ma@?^Xfo2$p&)buF zg_ZnUvWIN`Ud^u8{_g?Vm-(+;MIo?J6Ey9M@zoSczyDD74$-+VOqgwK+j(Q0I48Dw zV%xTD+qP}nwr$%+|FheJZ_syMHLbO4t^HK#LT{LZVPJ?P@!}5>l18nq9O~JL~_a{4g^9Wn}s<=ZBGrg_+_1)%j@#SIW{vsfq5?3}6e| zyScfs>Blj{!*O*D=mdwtz(d&z=(MzRm9k5fn#oE}O#jur+*!7C_LaTfc85R;*(bN3lAc>KhoFf<;n9QbaY>hhSiCWMpD)B<}XDwK=x}{g?$=@cb87m`Ju3 z^NNVcD26omzD$nbEy9rF(0uYlz&BNyhKC=RD@b)f* zo`cAPV66w^@cdGRvhaa5HTBdXCud`06V0neC0U!-iVGpwGdZ^cmILST$I})l@!ORE zMe}dT^F192dm_lQ(LaBxMQ5o?Z^WHL26JOsQ_JwX%F=mlTT2hX1Fo%wEE^vKO}Y|r z`E^M7O$FuIyIlmXV`%uI*xK#%g=}d0v}B$e0<*G2YM4114Ui#^NqL!(iIPcS?M*cLz(0A; zBD<0v?rUiQaG6pR?S2f=;tP=1Lf-a2-F|3n`fp!#{REn%xRjXmqB1(yn=aHfI@tk_ zhy7kZSqOfNnjknp*fBUd)-yMP@FM`lb%uj4-l~>)t_A*R7`V)B zGBk(q_!f9@0O<$-nu(zU5Z3Zx{$$3fsR5Owkk0@a#wf{_DS{m=)M!##RnZqTdCMv?_zPV0+t0E!-wt;nMu7Ds^5KMqz znK3c_E}#2Pdk-l=X@6JlM^&)7m>Kq5t9Ctf5ifQ#w0~_tFM30RAntbi(fVt*VL{}6 z7q2{~@TSOXb!mQQX@6H!etTtp@sEFZu73YWEDVpF&_iDI^8D~2@M}up#`bFKY_1N! zJENiX)q?83_SR_R`NG$Mj4dq;zxb*eXZhEX5E+=-JmxkwIVChXL8en{a%QT1Rhs@7 zufFlvvZlBIjO!AQZ~s8_^$kpY@mbainHYR~M6-9Zp7f^p$<(5reyO7SGM_n@{AdOQ zWkat$z24P3*n#-P#PA~)txoi!j9QX($&^!#eb=r)FfeZTXX~~C=`212r}^W@`>3WT zUN&d?Wd0E6sDk_mZ2^%V`i4RF89fkwRA-MrAsPWQ`2X;$041&yjzQ=#{erFoOJn>B zxWuSF)U~>G`~Td#HLL&lX91B{^4%$yJQKcEES?~Gp<8?+_PqREB#hS;OMw1@?15?i z1=S7a{PM3A#Q*J6RhIGHtE4{tUrX}j|5_%V5xta;Ul1F5Djfb-r|kX}{Hv^uiYYd_ z`tZw=SX=zd_Uk+wz<&ckEea;oL+#P4yx3d$AN>W{z;PLV)%7meG^=Bez`!A8d6JdD zv7szA@X}5sOVT~niQ}j>|B2w-GO3->cC%iMTk)vVWsZ})$xOr<{dQ{)$&z^LAR_+t z-+_v+OeU5b&^%g$&6matpZBLUve>D7A=u8W=G4mQZJw>?}1z%fqmrgW$h{sZKI5J`7H!GnZ`4e9S_`494$^oOGQL*?P-7;M^s;boP^&UOz1U zZ`>rH@>r0pY^=JLhD7eDkO0zV}kS#i>jRVq}8cc|1`^W2lggaT}6k|vKG z)_a5vYMf+;eVzl-rd?PLbymaQGM5Ugow^5|C}!jA6rJ4 zGK|=jiB!ex^tGhlYCmk!R#)PqJF+-X{VCV=HeM>7#46?MPNuoaJ(}ha^O9Cc>EAcU z7@DhjDD4A zAx+LXBcDD=!;vffXcKjwj$B={#uZ`WLrX4uGZR`9;SR?X)SbIJv#L;K66~s|uHgNG zq?iE3s$$0y_4i#blecu zlahf}6W?+$K7MFalB+EnMhgXp^|5K4J~iY)x2Wb>`~f`hOo^WrMuY zRa~3wxS{MNpEd^(k(lBRk8-S*r#0c_J93Dju!Qg!ZG-R){jTurgMwGl6)6r`G+bYI z-0e{IROSE)Hh%c%2e2`PPdA^BNCy8!UyYrJt3x}pn`vQ812ZX&o)@wNwS$sBN^(G+ z!1@{4GQ?LUkVo|9Am+8u7n;08!O8(%xFM~NRVV0){ps`%E+x_NXtuPqe>NV*AP{=@ z;Ik#|IX|#0V^|b+EUiwSITJ2bf5JurXyw$fI#^*y{$9zPi1}quFYT~%>`PMkU&_&1 zzUn7r>&-Un!s5z_$N#mDPC#Ej8~AW<1_WW25)m%3X(pj{zX}=DCd@lQEM~3o-ZE0M zOiG|NRpVebruCH@I+~~vNT_G&nRo~E7~&f!VauZ@q{}mN@-nUw8y)dvvk#*6b?RKe zx46;e21->nqdJDZupGKpZ~G(iL~mzICLeKtRGhCxbJ?%Fl)>HKs~zeJ5j54r zCTe4nHXMV=Aktsp<^Y)CNUjiBw@jw=E2#@o-*eXqLzCwmI_MZJ>9aW62clx7UC&$A z+7ph&(L;t2F^PF5!jq`ym9fx~nD4j|z6JzETdne16oP3>iwS!pd`b89RfQrj_`h_f z2eg=EYjDPo{|%BO-^FT9p5RH3H6Mtpp}DDY*__pq*g z#C>_JIy29^a@?ZdWJw{%DuliqaOC0=$Nd0rd^#l7dLt)TRbhoSjskk#!>xgp7a#ST zog>YB8M&o%@PRFVI_bwRs&{QHfSpUc-e_2C{aMqqC$)fO0S)gQvO(mfl$Z?)%-1&9K+jm^xmKbFQzoDIIcnZ zP!0zf@sN3iPAri8C6A`+0fe_UhJ<9Z%8d*Uax~9sf|KXz(|!nu6DCS$ENGMu_zaz7 zX{gzY&Tpkp^PPhJ8Z8P@=0S$qO+RTtz-!i>TpBeUe&#zrHiF3;Pa*^C=v*vd2%eBRyH?(Ud|H_-TD_r@b9 zA${&cJ;G;TL$EADB@x-fSp5k7fYB%F)KA^oxGlI)aR$vNpbkvqY@~R9jm=X0QZ1b$8;Pdo)UC;*;%xT;y7k0K%Vu zxx7&V?+Pnb_zW>l+qiYwvs8 z+0CkvIUnOivR6n~;%d!UwCdvrLeArwbPZ`<-{in5)!7j&lF<-`unzJ<3?vtr5aE6YJ3P|TI_>S7H)A~Akk;xFT4n= z@^%+Hi>v-i6r*^uYc;5h&aUlB>Rn>5&f=^Xte|tc%p`DjlH67Uanyzsdrmh$Wiwsp zw$Y-VOA}A7cIZl<1+4m@l@&pgs7dzXg(Q~eDm1ex5+Y~|c-;GtA+)iDe<>bUY(F2z zZz{pJQ(D~<$Dx5)6){@=f$!1Yma$0B9g zAZ8Pyykhz-l2%VY3EnNiUNH*WT4{89P&0Le2D6Hb9p7p&EG0rjs`OFxeVS>&K1;$W z^^}m6e>d=+jC7l%paI^IDI-@vT6H(IBaNK)EI_^ec`@mqT56M{5b~joW^~e{tCM<1 zoP`WbeWi?uUVa_udrZ;6LzK=&xVt&=mdeDpwduzAn zu?D$aMYs47(yW)H?fF+V57K>s=PTM^Fy+=;sx)CU8S6R@KxF$!ZS+b2{t=UFy41pE z)VX^*?j{6NtCPUO?$q)KGKi_Shi^^D6k?ld-Z`nLO@H6Pf-;`BUGmv|8kQzfsk`kX zKKSXI30IHbQ=xy_6wI~_$B@)vdhNL@d2M;ivNHSeQ0Q7crwx*0TZq-X z?;g8SRY|OXa_45mk)~)UM0)`~*^LZ_+D+(fP&W-pOCl#bc`jVV?bf+OkW3Gya=G_a z^DkL<8>A~9z!5KTA%WtEbGn^1c{7Jn!vTo{upWS#qB7c8rKM<5cnhMcJJ!ALaU{Dp z77)iLR66K_YrgdLS<%9{5N8%cea9XWVoC9`q`_UfYffrtz%|8i+BJfiki9tVjZ-bZ z^~6Lyl%;q*cIuR`Cd~<2p_8Ue!Ox)%Wl*x!oq6Mnu!edqAnXw20$R(vj*n+fm`QsR zvL!~-P!@w-#fLlkEoPkXy`93yNH29uqo-@r+~{%88|$7XT$-0t<~za8EE)2Q@}R!O zk3PS=0Br{Q-4cZ4hGB)&uigW%D8sIg{9oKu{B|@^pbIoEWyfn*A$>okt_B$QxDfRw zP#!&Rjm$nsaP-Uv3Gs_luTGTf^t6u zT##I+X==Y3*A{iw2uqBngV|!&CLAv~EaEcG23r~DLzPP&M+|uuVcIrIeUSxud#En@ ziumd(O+0$^n&S|(;xRbJ5Ap+^zS@dhLFj3fEnZ(%k)19o>>uEuoeg$B=2f|meRXdR zjcFKjtoRYoU8_Z>Z;{ZX*H7t`XL!27HG%?#a!ntLy_}a-68cBf)vY9Y+4%yV-ie2C zILw1A{C&;|#b_d0t7Wap4AB~uSCfX&>}`6E+q7k_O$zV>Z#czraGF*y5_1RSL-~(l zx8>01w^}z9O zgfw}?O1sj(<>Y8hfvQeAhE_Y9oVGjo`13ux-&n*+f5FO8RD*{SgrAR@h-?M$2yXug zU_@OtIhV^ASLAu_fO117s1ktPY2(}^QCdbNd|8J8C{nvcttc+JczL_Sg#4#Pquhd^ z%Q&k=$q(s&s*|R_*m;eADGLJ780&>8g~lPRic%UULYm$H@v?F=d>5X3r%>@DI} z!z|HpNEe*)8Sf)LJ+>+uZavo{!R{b!RC&*=MEwU@SffH(iGpH#Nx=F8(pNh|6B}n2-p!Pa2k3WS+cP3Qj`=-NR>E7 zq^SbwRX+Kc=u^}z=3J@m(zY{RWEmqytz~qGQ2vkC+)g%g&3R6`Da)uCk z;sK7>{ZDYyg8_wxCkyXc-Jz?0N;Mp`SMSzW9>K(f3k*4mKMW*MMLw4uI{Kpl!<-lJOI52l3b{GpI$DI`Ls*GH)T-P$pP4H0i8%w zT&I2y@CR+>fLH0R!uE7~$`}Q@z{oQti@B8D)_ox~KbU$c$Ha7|f87XQPjX%A1S!1% zfUWE$<2vqg2W`V*HNdmG9R2tvo@}l7@3woOWG;t(L6r~BRq@aLX1Qf~74rVMuLM|j6+pQMJnd{(dGBK?u=5z%^9QW{(95UbDLoCj|VRYUOJ z1DNTlZ&H%F-&zIdov^yNW_%K-P(LX<++EL^JLyfhxjPbUt>l9+lSUemdz(F7q^KE< zC_EcKGfw4Cr(!3}`a+ZoYZAKMZ5eba_PmxSgkB~~B~7`B$jtbS6WXd7^Vh-w{}eh3 z>7?bC)q0mOHkv7#S(uE_Mwu+36vHMonE*K!@TI3{$TX#2Zz)|WN_q*KQS0LdVW7Qt z^W8fwFfCq3Qie3n$<9Aw6L0FHLgv1n5Dy)13-wr8GG3I1Dx2;g<4r@(rn)YktkYJi zQhhuuHS5s?;Pl0=y$A!K17Q?xhyRcem>Xlnr#KGd(1M_5RV_QP@v{WSrR(&B9#;?o zeEi`DDGKP_d&a;ULo&BWJIie?hCm32Z0rJ%dUR!??USAoC$G)I=n>JjQ3z&_N<||( zRU8H^%!yf&T#AWToZC%Wwe&rucZ2V?3{l=iwWrz2H-aOip?lg8afe=;??Uo7ETBzy z;$({iKCQW<=5)k%F>Tn3ww3T_0(*7Q`WMu{FAt^ z+WjEMc0<>ERB7vl_R=f4i6hL~RX&=z$|w&jXxZRPO{6$$&An;HQB{S%-ZOb}r)N>S zv}uQOS#ZiD`qxanx{`07OfI>gJ2HRIN~6M7&cQr}@^teW)*O?;?xIW<6`!Z^vQ@Z7O|7H@OKBp)gW+(D2I#YU@D(R}lV zXiE9jRIrDtz74#?)87TV^66OMnKcJr>&-T*5O#^LmUVAUoafNewrG*HcuNY{=g-sk z&jeFND=X!XJzoLaB#=9P2&)OMRANq-ECvJGwNal7{&`a=)GykMUH<)|KYFdM2r}+y z=nq;<0{%IEbQq*@X&d?U1Rl#$-R^+5S*y6oXX*obw_LsANS0xi+XgD)$qIH24o_9f zex#spHgCO1;Ui~%0zq;$0kB&;aXlNPe_mtqf@!HYZ$+OiE^Ej49l9(v8&@~ORjbMA z)Ijhw@9Ea0i)kVeUr-fC&koDgVCB0|=;*~g;h{)Pp*J)gHgOy+rl;GE;!1-2x4iq& zd6SW#&KAxqlEl}#A6_-HIqN#Q@lt4@#ni>b_V3Gc>;ou_6AzJ+l#VDkP^>02slRcr zVOigxta>W-wq2M$$K)4Y&Z?`67uBTF5rEU)+^Uebd0QH-!*4FpzhuViGmppPgwzDP z=ifmh;fTbrUh&a+$bDsPD5Hm8&oA}0gn1PI+VSN(E&8x?#Q{@=<&(HcPyXlYVl?gN*B16pCIN7?LWW~Rg#NLQUn zX&+WTbzRI$pGe~he|AG@cZR!Y5&4k~%WskXj_eZst1{?FXhCOndC zc^NIA=V%&e9r$ilof8b?-1$ITRwr-)%jRU^Jl8pqo}^`}q7{h&{%WVm?7VKMjc^E_ z=5aJ1U>^w(81AdE{CGMRNKMtOtrCoQ$~GBp%!69Aray0D zWA-Ffx$?FnFEi4Gb3KPQuKc8>96%^3pPShrc3Jz0Ylol_Y*#O(kkOaYuA0tolwq5hZY} z3PHx}HRfnBDHNpR%H$!Hq68;o*q1XOU)Fs{DDN4$(|a@3y`Kf;j@6e<^6BRf;kNO* zG!6TWhpCKI>D~uhV(>137j$>IWVJylv74_8^hOJ1z$w>6@6fTru zyt_H@y-_D;k|eJ9VC7mqy8a8ClazKiA^l~eG4h-PlrK@9#o*sZ`cW7Phh%d@uJ^5s z>7cQy8>x2Lz0Y2HsYl&jD4AjltY2XA((F^8to5wwK4}z37op@RM;)fjLhGs#M=GTA zvtJbO$=^5mSE0l_Vuf#^0D+lO{|I1kO!OC5A2WIE?0(NdPR}dT|2W zEu+iTUfRfmS`uO}b0HPX!A%r%Hu-WnUnXd^bK6NH10PaVUa8Mtp9Bv2J(bj$`Bu+V znya;M_CCl?B@3FWvCD67;Cg9}?su@Khxe2i?IvKcj&KXaq$<&zeQIYMU~E=~amf8h zCN4dCjYH}T?@i32YNd~>g;Eec#T4;BI5R35+hN8rWh3Y?7+E95z$`3)eMi~ej!fhVRpyHdt=n@ z7L)*_3KfolV4^AzPk^d#3-8Ftix~{zr1WM zZ3m;i2u+T!2^Nu=4RQ#s*#10+1NNi@Zt@*F7q@Y7#$#a(=mzT9$*3AoyB+ZmPV$Al z(L<^75#ztcV-ZR#1!~nlP@2DoXe%JyKG;P5BowOpRc1f=llNDq-0TM^vjTw+SPKDV zP1%szd|%dp5OSD_Sf*PA%y!MyKa&D3Q$b^`%BaU<`*eXuC_yL~t}U(y0(Fvue`pcb zAtg%XcdUbu_p9u`awZuSOB{zAiu5X(4&pPg9}Kd?XX{PbaIt@=_Y0NYz1H89wcaLw z+3#S<*+%|GO+kymxA8sK*qT>Bo#|Z-2+-^X&|+ zl~vGsS;63JS_t3pAjNI%-x8UWDT=!MXV_;NjlKOhG_bFX=wcBr+@3`f^mV%yntnla z)X)v%sonGFZe*>>Br50F7^DYz$+mMJc}WV)!ltal>7z1b@E*?TJa~(Zn5Enf*bAhqKlqja_BQjN_MA`z(RV zB;t;bVuv6HWpwVc?igrLtO`}}h9$~vF)o3o(4~%F}yO*hp6vu!$#Hb$jft zF??BliAj8~){&I}Z~ZhI5}KwaR~+Z_)czh(M^_Hx(%&tT@xu$3{Gr%O0C@1ssn(@M zuXu`aFumI-p`Pfqc$6UC3Ca+7HthX#oxFJw%qt*=Go`XGrbYXD`-g2x5kF)IXS|NS zEw&%>wgmrE#kV5t8P*<^3*^a%$-pORsL)n=7y%l+1!+OTL$4HTT{9Km$0$%!-n*AQ z*nK${=qKt;1*b8hLqDyk>VT1Bp=OwVe&^kYVVga_*R|A<^b{EL+UVTLVB2h;zqx6$ z@eiKQ%GVaSxXO8F0KekhK?1Dzi?cZmsAw{Yb^lh{bkXZfA1^NC@ke`@kJ|Wq%b7na zj#FJy=m);_P~a=&_z)e)X_EE>s=d~Y&Q zI{L6h&~^>5=Pre!PVjfn;E%{kRG$#UInUy7@nOi<^2*kTeT47=;exXU(|U^@j-mcl z`y^0#?ZN3e2$}nX2~qh;g*vbfqlXaQICV$8(-n#&GWSH_*`6$l57{}&n9IYK=j-0Fq@Oc0Rz&W*-}r<(rKn0>xv8H-l>u^(O9yVJSK6J2+hQmOM>T z;S_JR3=42PWVliNr8UANQHvcZ0v}`O`pzCSugKg&e@DJkS8V^q#YrJUR#6jCP5=PoD<~p2!uh9g8(cM+7$*;E|%`fc22d-b_-o5XtoOGI) z)>A2q1%~}G`YwWZkcpAJ)28aL;5YgEwtv^-OC3GAb~@gJUkl!3SEhfuY&M6UnSJS3 zqQIxt-|AikTSi+8G5JI(@hk($t>N7p?yJ?K(@K7ke&C5n1#)VCC1R<79YwE6G2DNF zq;TGjN_mLG9bX{<^|UtnLe7mQKoF@MG`j$PV-2&3Y8>G-j!*Z-o`Sm*lAjaZG-qpG zJfiU;OZ+`mQajYD!Y9)b(ks`QTwLb zx8qd^j}Y0RYO^abs7xfw*Js)U4{NNqxUJGi3c!FzilpLtejU5yki+niD#)~de$>hO za-X^X^VK6OIl`;s;m9kh;%^Mm&5SJH^U*Kovu}0W51#Q%SxN9?6BzOdD~0@9TQ@n$ z@txa9d~qg^wC!NNpnn4AfS2x;3FeEP&@UIJyP z($9l`B=m~bw|j@2w8h3ykP$U;jBcLL9_EWzFYwP}%cL2ic4~Kp5OH%?3i%jZ#V+*~KVAR?aI1OuC{*rQH zM07T4SyeUf`20nVdNk?W!}O6>MZ&hfbc}UGATrcD*P=OJu2qTQiz?NArZWJ~bS_X$ z6f8=CD+Mlh3(~+*-oW8y7HCiksiZknUuz;$yC*-?4ws;L(+xN|jc^`&X7y@t^@)b$ zP@sy6?|jPYD(YKxOg|{lmcAFdsI?KK&F6YZ_l@UL^omIW?Y<7HS(^?5vd$KdWEZW+ zev4lzs)@%{Idx-FwJRBM5g!-#rXa|)EyhA63!{yN+3{8+&f9J-slt38zOW`2%JTO^ z>?{y?FR=xaC;Wv@ACs6a@Yv6oqy{ae&cd(*#nRTDn_ej_i~4|UktK=ycDoIK7_D~Y`vF``(zV1N`EYP zO1n)E6Dz$0Sq^}eRhg?6ZNEK{=S&!pSPdvcV`Fdt(O}}w*qNc?ey8D=dboW0=<=>Z z{7mgXW&UkXea=ln3km%itx>?zOt))`CGQ=x<9j*xBjYD$Tx$_~Km?7?d}(N4nVh|v z9~dKfP#7|{O4O*->E|I0C6y?leBXDRqZezpr0X}amDX4cb|0fc+Sspo^D%j6#Dlyv zRDGTZ96uMzQH$@tf*kY|(BZ`mxPp!iAD_H94B7R-72;fB(TW4hMR0Qnu|ql2T76H^ z>+UviFXLbGMngH58XDKE+K370(o#J3Sv*K}vXn(WaIots=PrsRXG51SXkbA z1%7zOR_+nAjhCTi&$HR!&{WVN5C2xhC1K|ks=Aqfx?C<)k+fP^hUW+xRFhRRt0t+B`CU%}FG zI9%^g*j~}Tjv19LHXG+hq1wL2oAXSpvKy6!WujK`5Qt3@66pk^8eEY_nw8Tfop$Rd z1Mu>Ba^*hzD*DxneJ;3{M4hB68P+a}wtwL_IL-a6CqH0t;Oh5L?O3g76|J-sKMn`3 z)F0J@zatuKANEOsaH}#|y7Tg4iQfHoVJ+;a^2szXn;!LFv_6`U#m&t&O9Il@^NEMd zQN=={)0n=TR~2~834g{(R#{$d?}PUWrK5ngu2Q~HI+QYOWHElp4E*-L~~TZpX{g5Mb~}0F_%=YebUbkkh)?cS#vO!TL8OT z)kRA6>N632Sp!BO13RmxUi9hC9C<4zuYUeQajS+{8f+EKH=;A3BY->zB=Vn}g(+|S zd5}SpELBM@f-}rNz32&;&r3-)Tbz=6+B61=sml}**{2K?X;a0BpwxjR`JBU;F2>t9 za!Ak}QC%5>A&H;*hJ4aiFbwwU9R*0{g32u_>mnZvV$&(NVH$V%5%)tp9aDJ5b!pBg?`-^N%V+C$hMY$)97+>iaPw|m z@oGq;`qa%|P^wI?g8ztG{AKvh*Px7y|5wz4 z?Z2<9GO@D$|9A}w%dX^(i-{uCJTDUeBWPoI67?CiLOw$5NToot%;-3UH^MQMrC()f zabD1}tU!IKK%Gq2(Be?3lu!_9rO{!lq(Cb|>u{qDAijpnto|N^h9cM3p#c{O1;8Ll zi=K=z5)p$NdG4KF!@9{svBJFIgUj`=odZEc-`8)cwKO0_M3WAJ025$?8`@KngXoum z0dvdh;^YI$fJTBO0W|UZM!a~Sg(&PvU{O;Dpt7yQK;)`;nsVfE1@b{rBgGrH4$MR3bSU9v|T|6WF zMUfBV6oOEDKmu`zsHlK>AbF@ECu7*(HcBJXF}aR$T9m`WJ4z%`x&YDweW-V_@YH@r zC_s?|DdW#$Un)j$Kaijjzn`3NkU^5{vIH9nt3@JOTmj4-s4!C;FyQ$1P!mdmi6}3i$UFHoGGNz- zmz~00f`~dA72jtdO`NhfsiBp1LcTWh!j*>hSEg=3d0j&Uqmz4zi!s3L@M6HWJEPNcVE7lF2h8BcNPAp+PSXV0W zQs|>}5>AaoL`Vj9fE*LmkxGqDA;1_EJ1KW879t&xS8Gr-1%`YSDlDqtNE#GJ{n0>+ zi@GORyk&)t2TYSZV2>7NlvEuE>i%3r7aV2~H038VXcr(2d=wE5fJ;?KVw-e=c#eZR<0(c01pg_$jo3sAu(M z+gRqL^Jb>WHI!W^#eR(|@kps8l(*8;z1!gUvl_SyaN+q1#1XUB+Q0yPAunBnIvJqe z0MqWybyDtLnY}2$&z&_s?^&A^B`{DX!5$p zyR01ME@V5)F&L=NSCf-rT-|A(A~k=QbdzV95)41M8QQ|#Q}6k977p@>Pf^1Sza>4b zmset%YQ1hxy%&}5iLg`L{E(KrOf8ljj=r;B^4ZPSvWU8>g3Ub5I5ssypI4Qgn~7Dp z?!c;H=utbxT%UF7OERd@?SMI3F>&8ofttSKnWF~2siotvdDAz)skObLO%q=?LKZ== z2Ljp6cfF>rb@va~^7*E^;c9L8&}1U9n&Y|Kf^YkCleySMb!8f4N*N+1>FTTb$F{PI zS?9~NjF_xiz8Rpl-&Wov?jv1mpz#J{aOdAUwsp9(Lwm;E9OjGiH;1ccO{ETB9+VIUmJ(zUPXZUBn6($giYys);N|CU(Us#2esT$3ZMDZ zja=Ar?A5*u-UHjd^1t0~u}xCqqa;%);|tlz=l18<=ooiDtJb@wRz@~ct&4LQ+_@BT zUG^+%;noP>yi_q`SlaSu@{Qjrs?}*GYTQ5l`Ntal_{uB*<-Y2Kx*%9=q%`pO!o zoQB}bb=>m!y1t3?1Ao)?`OSuP`mWAy}5CkDr@2womT*C)|oesm=3CQ`YRY6?5Oq zO!oITs@hX?DS#V8jGo3PpY(xIch%Evv9cArc>u* zkLcO;Q-DtKdcn`x8McS*knU&*L6Hj^ggB?WI6dd<0ZdIdelGpS9}hZ=&+NGiolomz zZT}m^my?BMUt}R%Yo$q2+_)*)LU5e5qLiFF4yVb*K4xZ53 zCf>=ymWJ|gE+L#Hf?tEh_@_|GIfrGg%ejVXfAp6|Xr7E?NU<-r?|dkMah z-5-vO{7^i$qPp86R%x9p2g}n9RlM&3Sg`|ZLSeGp#lOKhhq{81r3yFuws@5v+*@7ymzGmweRHyh=(Hl9(FcGix(Z_hu^cluJ zmF62S%=6%=INVpBHAwC~;EV6z>Z(!7QyDf5R%$Ps%{QGJ?HLeD+{7oG6&Eb4&DXEc zx6EBSYMTt7a$+{w)<4^e;1U@<-WjDo;6)}>aQ_9lO#cnJ|5u)vm4W3yk6rqxVgZ(5R|7N!YS`Bge-p6{Ys5r9D)Q$A|;JSi2!s<+$xt-cL$2cQv<}2iv!W9 zs3qw^bmeBdU$=gBFF#dQBzMx?Ou@XfLfOQcljNQvc)8$Mk6UHH@CQ`2y_sF%wK?i z>~;ghv#&;xM+65dX&^p<%L6o7t?11 z!D_o4jtamuzT2#ds8bc~Xzxc-gWD_MLubc<7{2Xn-OC+x769-d^ZIHc*adYD4r9F_ z$_0=^wS9u7l}r%A?SQ|v&LW3^M+A@#1(ON;Gq?h#U@u7B>iz23U|#+}_1GPn-aQHs z$@4>gVKMNvfx`WQ-u3fy>+#ck08h=H@`3&~0qHT!5g-hMTj%rbbA4lIA}-sy_bm^sN_*CbHw=Enb`D=7$i0DHd$2m5^q z761TPq{P5zqkM1QjWgO%U$TV1@s;6r{2=rZ+E0AXKK{&n;x7*g-Z{t#vma1RBVr%}g@3&aGFsTtr+sU)V_ZCC zvi5$HV+J?|ljym|W4#Td2hM`{|NIG2#a;sUjn=!{`oa&;yGt1S;T(g-4Z&RI`;-&m zkAK0uKt(|Z6`V5*V*~PibCW_l109)MA~<=D+~yYm21ALI1wJr+0p$eD`$Yy~pn||~ z>TDUmS@%KueDd=PtVWX4;r*(?e*i^~v_3|Xzp4DR_(BsxJO&5=C!&g%In_Bj+fC{h z6E{-)PQ<~Dt8C=ABmK(xvtYMqIxm?p_EU0MN`AI)7-6bSZ?ylD9sg!9y%~&|b%6J* zZXM(0>)gm7>D1mDdjBcD7tRGu(L_y(29upqNLnk3nBN9-RWB^t7(c$o?~G=r^8T&3 z`ARz*7f~%BuR&iZSl!T3Pbe0eB{u&!P9$m>ubFidaZ25<{XdMF!qK1>U~9OO~i<$0{4rvTT=(h++(`Zt^%kd3Cr6FOg)>^ehkY(dDZze?!>UDcDKxxB{>^ZOMA_CLbN zbUJx*RC`LFrzV&Zmm>)8K`911#U?(Wl4N|on-;fYYIqA6&!9DA!=i2EwAZXBq7Tt3 z3~_!vi;9F;;-vT=*G{Xawe>_yOR}lgEUr?@sylb~v-Hl{iye zOawDwSiQa*+?|17ZY!YfMg98LyG>efhr32(tw6C0+_spDrH4=nVLe`c2y|Gz2lL8w zPdgf4XGaWhX`*XC9I{zDY;aVS&J!fRwfWsCShNT^0b$ZiViu!AZUek`Ko%M22p))W zduQb`AL|%>nAQ&Xus_c%b<<>mX3j}e-90$-cDXbi%X-cB-uE@Oy0X0Su9h9E>~}_Z zk68s~qw9qz_|Y`TtIePY+oW;kx<)1!`uT6XADiwobnJ96U7RMr%;g`c?)|M?tnLE` z>vcJ%F2oUKjLL!Q(d@B73O%U|p|ko#Cwb_FrB^tI$w3E|13L zRQ3iIsh9jEx1=Jf>l2V#celH!g$Y_FoAWO!SOEHJw`W;-qvMDhK)Ps~qin!qyP$wc zZ3@lB*cYTi)bWM>m;TlYzy)7}iZC0n(>{!E%VOUBVQ2D6GNY1&47OO!w>3i$*5N+< z{PWjNJu~tIROHx=?R1S!v|S^gXgR-&LtdSO6Pay7LSBuNYmRS6JrHu6oTNK5-UnRW zE;;AEDD^FFu73wt~uDp*oW^l~nYz15z!AMny;_PQJxi=x%1H4wX72w}hqt zATO5@KmHsLn4a<>h)dbVlE1l7dl45Bst-+ja^IxgAZa)}>!GwOUhDEad$g<2{a6vL zTMVb}xpGHfJI->FUck2xC$nfe;rD0COr$kis!GNm6b5QjkEb?fiQk#`H=jNrJ0>S@ zE500Z3Oy819dM@f!{p$(kay6QQIg2F?O|ysROh6|+gscA7n-E0+Ri)%O zu~zOC3O}e;Tm6l2`m^u7Rn4SjB4kJtY2-ES#&u>IqYt;KJB#=92BO%#?LBJf z943s5O@${c+Q3x6zRPoWGx=jKai+LL|7wEEwU8=-J<~>Kb$k;|w~Zp|aUpjYeL;Lw zs*(i(J@bt7QKm@BSx7;}vZ_!8vxbjZY~40a?EqR9$wld3^$8M)LK7ptRw*U3{Un*11+XwlVbx3`T0T}fJaPK zVz{-)@*z5}{JHsHYPY2&KaqdCDmg{nnFI1h1ZE$2KDQ(mG z#8#mR%83;;(yzwXyl;NLr^&UBV~b<>j-vmOMODb-=u!0#QL7W5-^2m* zy)kba9PUc9Gf`HV@~Jqm!^P-*`K`rYjN8|%3>G&na+;LABX~#sy8vkPbF?7svxDE89OHyE=vTme z^rzfz7FEj9d6bFfu6Fmp-x`zwLskvf2s_d837S{46O>=4f1I*+CCu0$sOo7L5T_E$ zz*S4ZEqQ9kjk7~gZpMO*<%!8_bSZqHj=JnhkI&QT_hJ|!wE0C`-CA^OkAK1s1DCFD z+|j*`+c~HCazi)?V}h5`*?TPH*m^J+?^JKNbdH|3?DMe&S*b$6EGgm=p?G>`Q!&~unsh~8{^zGidRirm6_2B)wvd zYR~i{rr-%dUFC_+mSt6eGxL-gh#NG#tKH!EV;S3f!~Pf^Xa91SYmvJkVo@CgPm7+YA=dpxs#$;eyHZI^(*uFZ#$*z6=;jg4==PHJpJ zgQT^<*U=xKC9$e7grT3J^KEqm1s}4SoA-9I>vO0^ny*hrmEKkb6fj{V239hQ;UhkL0($ z(MVJ=V>-dLJd zuTB=(7XHo4#*TTDzWtSgi(<+xEM;t6^A!CMFif)MFCClT(vS0FtI~ODqmP@a5QE_h zGprr7A z_ioSaim;OzobLp{5<+>2I!t59{Q88^&gi)ZGgG_pdPg+$wWRVCY?~4$U5@M@%^Bwi zaRt7#E^d^k(Spb%uTv0~V#XQ=sqiS`7(7&of}RsS41>02DsQFNfXmncPen?Mx6;UH z=NKmwHNT;U@8Z+R1ccDo7mJ_o2*Vnqa?e98o}yiG^SJrzfv0dDtuTk3$6%_LF_^5EtpqSC-DJ zOpt9~yuN1itb}NVWQ*sk*;({_m7oPVk`tE{mkG<;Dp6#Mr%hj< z1mhXQ^Jb=$dK}T^BOy8tV!9(`hsT@al&f^MVzst_GCNHWYQTBU$O) zh126|ZViu8Qfh}=?icNGnP1u6YgDMmql4w9j*)=#mIb2MNUCAlj;NHK@6AM`6-619 zEqXHG!X?pMm2_+TkMtnUlVis&5$M^)P;l72;6A$Gj1=Ia${ z!jDFmIKmMio zM^Y?Hw09y(H!}=l6qs>Uf!8|I@`qHnyP-vlJ=RgWD2-j;8n_3;d@ErW-;{SOK0^Xa zV{TNTA0GR`eB07wryWu*jEbq#*hRTIoI>yuP^t>84go9CKgu<}%@BMUbgYNQ2a7h( zu@ik|+VR4+Zq3W;C_`ek=N>v-c=q>DRnAs2p6&H*E%ujBs*q ze^fI?fiJ?l{aE|m2#>U7`V1Ea;3AoHek96ak?ihmtLf>SZ&7 zi}K3}V~@#O9H-m1A!)j+=vXt};GCHdg%@BQtkx>4y(8o4tX@dTuYqDoaJltQjboJz zU^ZosY0k21*b02Z1R_D7P-eQz>uE>|6}-~i7iX8nUe5{5W$pX-1+q#0+~uyRe?N=K z2{n(XfntZZ^heP^pN1G+f>0<>T08V?nh`9lOKq?w7(cXx`FA4+Df=%OpJPWtPoksU zi-2N~HZGi5cKMlE0m$Pz+6fx-Zv%ks%5)VP(0i;7m98>UTXT>`hI4as^tT41B{i~f z!}Cg5TbvQvil%RbU7CwW0aCGZ(3j5FCxt|V5G{0Ox+?65zIGoo;&PIQ61+cpvBOs) zPsXH=PTW&Rp|06PWJ=@bq5jP*9pVjDQaU}q+!n{5kpAd%o-;fmdr-TW>d!6`bO)gf zju4E~7Cq)FW#-!A##cL6;|72iw*+=v1)|Yw99^wyv=3OP4pXR)a5zbEsoodJNU&~} z8e5jHD<}v)^9EM-Y=8QPh;cV@W))yyJug?=d~|r>#F31|YwGCWGoXBhOF4D?p-qVq zl~0?KeJsjLG##Du81}Ee^9~meV3j)tmbnN5&Z6^_o4}>bbTsCXf0a8e5=I5U_qNVt zbt;y1XXncAQ4HOB)<2~ir$GATx9TsRK5?c?sp|(h$TCN8vGwUq1b5RD^>z_y%!=~& zr7KQKO=bU9P3m>a*e!*^k9!{VjQxRc6DBc?Qj3OoU zK9da2x!2QN_vS=)u!Fe1Cry#KKs}>fEbV%+Q&eu8Fqz!V5XY34r%+aWdN_X0am4zEvCU)h|l?4*iZMBC~$Fp8A7JquL9e)q8LWp_G& zJ8xtPYbhD{CK=jx2QGM`RYTmgS0C@zjZMZI$p)1QcpX5s zdOEw~iS}0x{QSo*=tZF_sG-$3#qo_+;p;@v#2G8W`eo{G-R~8EMpE{O&Ym6@(aE5x zh$rum0uq8>v#ridtcPI082IhpjUpFXKTY?yb2+>PuD2)I4L>Ym!ZZMU+RBqIt=&+X z#2FKWc{>h{S)0ITanzsemeRvh*fqhAwId-?7+jKw&5ZWhjyr8#VHNG#7*4)5CF@g2 z6)*Q38_QSmQ8Uke(o94yrnIWGf_>4c^(KiGjlY&$qAMf=OY0g zw%EVLaQ($byazZQilNu)`SRix@5Z>vXkJ&y$6`@1BzYs9*zeUdHwqZ>?wxpg7!?Qz;o=kB%TrzwLr z5$ClJQxdGS_l{qK?*Iv|=P!^UR-Qv_8_`EQ&TWe%;z9U}i#t0z ziHjuLHy(3t&)$E0qdh?k`ZHxlyjB7-1(gHDrjV@Ql0gEs($>?}-vE##F085mR8-(h zR8(AyEG?xqRXq25hQ^jQ`AtzY|Azn40)m<5r%?%$n4Ln_DFOrlJlMPdu)zIevhrfW z@={RyXD27$_(Ju~eEo|vx@t)HBQWsc!2osQD@gidb!S^wS!?5&|DGW7oASZyA08gM zf6}=4rvcBctqj0`$TK=N0dVj!Xr`|LG4-Lf@~hVTLh_ND9b8JaBnQg3feiN+xYqWW$vjYIa`*&A? zARGhS+XJWrW&y+<{X??}`9}~TKF8D_@PgX>{pP^>N2g!&?EhYWamLY}>CBj*q3eT+ zFvh3QjA82=Tfl*oi%~Olcy#uFrukF*U}vs%`Vst?+?ceyn4H1~`Y73e#iJ?!r}1Hb zyLix{&9ubS&`r^`zV>Juzu|y08d1g-fv)!fKsq|{|7ztHS24^1F}l#F__A7n19WTWH7D49cR-qt1g07DYPvJn?IlBOVynj`{*#u3EfYmoO+kvJ7 zR$K9p`4$Ka^H=!(HF}$A$@v3P1$$WotnTssc`ydfPa(lHzI__~*8Mb|ONgI~Pci&1 zJ@`2v9_q{a!y}VX`A6hLq4vLK8FR;j_x-^YS(<*`G5$WL0BZyQdjBayV=VnCU;o+x z&HsF{p9B8kNMXPj&>--?g&l`hYgePi>skNJDgUKS{oxJyiGB1XeEXpj-f&?I-*JZva#`{>66%h`Pob z2GmdZ3StMOe)K~qMhoGCP>edn1E~wYZzs*PiP=A-T!0r;v9pLj2ClbvA0 zx*OZZKfjL}zp?MqAcXnjmJTALb4L$>akwusq}x&*`_~ecv4=0A1HTr3yyB-MY#Qjh z>|Z$3d*5=rDfVAE!2mXao_svd{N>y*$Hp&>%Ws{CymA59=M(lF?w$*0^;{6ok3A9~X|zRs@%w8u$P`zAZ^A0fH1aV_qCA2vS2_HSVP zO~)T7L5j!s2zPkfnH~bt?iC#bM%r86A$~%4!0cb~XWd$9NEVYZSMA?ty#Vcz-+kY& z3?L9s0NQwzlRk7nyju%FjT?*MFs^i$k>JvD7bo6DhBsQy);8`uqS8fGQ{Y;T-LN8f z7rk0*$$70v+PL2bHj9r4jGe5imB+IWql9wawvB*pdjB2PpEI%Qn$%}xu!NHH+*?{w zoohUfHL`OTw@Ypk7G0+$;B1aqT(p^v-NvK%`gZN6E=wEGq|ye8QnhNd!TcGwYG?Qsur%Te|R3yYE3ga3ZdE8CK!f-WMG6+9~#1y)dbipj6^BK*BcZ4o^VCqIYw zbeq&|MYr6&X+a?G0N)6nZj+6Vp~=cw$XzHd0msSY(+|d(nucHPw~rB+)$T6v*7?tr zm+cAq(<}VhSzfp`d$DxW;Q1p{2`ACh+M&e?h-JVF)7_N%EF0xR28mS9IBHUBJDTsS z5~{=Gg!A338muS=m{(8N8I>{bO6{_x#6G2R(K<)0DJD-jJSKu4-sX^-kw?9Yk}j=n zv>U`~FJIkrI09bu*77mrMBH~RZu-_iVInON8my9c7thwua ztGU=i(%2RwhlJ&3p-72th|qxjo=6esM4@eACcnb>_*E$X>n3Xo7bp2L_4Eq+Wp$6q zIvc4?5bP#jo?aBPmH94e_rtFLWo{nkPU*kg?b64FI_B@`jU@<#{YZ(h7P;br^gX!g zc5U>+s0h#2NK5UdM})6V;YSjuQuuaXZh9cHU$BOlAXGK?Yjw2yiey$RnTBx0HxpWp zvz=78)^lJF?;`n>D1#f!+ZKKEj(CGGT;`R|V)5YBI5lK1x$TM$rCi1j%lu!*!)WqC zoGr`Cj%4h}4+&!tSMoqQFb)iLwJj0AXA}n2g3bYPG;s9=arv#lrsj6wmIn*I6$fRG z;ztU>^!Gmf1w$Kdx$uH1BW0!`y4PjN@i>8ru2g{q=dXKwVtYRUU*uw*1)c>5W-c-= zscdhI8AZ-uS{}O_0g9Ovq`L!2nr39(mW+HEtqy%pFpyj)??%c}2Z0n}t zG{M>^l@Nj54+-nb4NAmz8<f4{QStNWX%iMgq(m>8r9yFv z`LI`}Y6dy0zw#qAWs)K$f9~({3**Vs^#=0-?##g0IMKaVelfzu^o(*tvNkz}x;Hq! zRD6QTzpJpC6i6ctjXrn-^{JWj!bN{n#Du662ARc!Ug6W zDlIZFypMYZOQsPR22b1(8S;nU}@N5p?lhK<$xK&EiM_ zcVZ)EIifq(o%{po)FDSe%xph{;8s7GBn+`K*J2k3pxyL6cF~VowV2kh9f=iCTZ_C? zt;sL)jw$xoyT^Bn?X>jCDQ4uxpD+J3S^Qnf-7Q{m;}YQb-9v2jT=ir(Na(adgSF<- z;?@w!9=6s><@~i4{;9W;cxL&bHNSpH<6!k6_LbL&aJJ&LR?DztedNm^+A_{R!PTbE z59>>-aREi;|2<05)M3gfy_{_R`0Vm9+MnAb9z88yz0Cr$PQb@Z*uL{23mQse-1z7c z?EtOVo*_vK*Fdy$=uXvEWp>DUB3r~9W-K4a9==|rH6<`4-#m{Qs*O=Gyk>EMg+jhp zr^a+cRVp9#^UbfKsgK*?#H5Rg4b>_ds7`Fu`)o~4Y4M7^{bFLSDm%bN=&D|#LY z2z<&i?R`HO9lG7*vwLxRT~*FrlRQ;SuB$|j1~qOpZx{^C=)IX{TUvv#5dHc)_R^5u z6pqdjiTD$v8+9cCp-;6-BXqhMk|7zI9Q=~~av+Uh+YuJAS3fbDC6^ZlHoo0XChirb z7`wlhAEVghStO32RY$N1yl(5xA3W9_mZ)6PfuQQ%;8w)?Dn=k96Ja~TOTnpUHPYp8 z)b%ZG<@`x@v4fQ~F*khWmKOQ5hy&hB2d51feoeYMOx~n;%wEw#8t?w%+A(YtZfL`q z8`GUa2XW7p)~KklGPF%LS}Th+IG6&@3dVrCI>n)gLi<}?lqs3)TM1$zO(blpByO5y8-3!RfpLH)1*0ELlo^`)M@oJ7`+ zAe|LSPM0`^tdjNh9DFZb>0}{fF{-7+81nRcq`5PLlP$_$4FYCFxAfszay1{*oe|Yr zYRs&Vu6zJgGz76uh{46KYAE-(_Zobwq4n6AYc5<#Fg6>W5@&h(93>YX{HLB>{3L-p z$OBirmZ^;tb5`UrU1i~~6}{||c`7knbC|L<<5uQ-jZk@?(gt-TQx3JW3lwr}!7Qwc zR8kF<>KgyG(A5!wDZ#8Lc7>N2xzTFPIoRl@zuZ=po#3&nTJ;MX&1yz3SCX4WbgHk<;YWY-(!d( zz!K8ZqJycaBjpSwXlp-^)M2T$?q!#jw zlE1i#pL*e7M_$ujCR?zA1C0j#R^AmjhrdcfGgr2>R78QNx8-V}K$B0*g$ zK9G4?mnMxV%s1k~b|nhsfvdDj@;g=A4FW$M$ZfWZRyF$B)g`&Tn_!c%9Ox@Tf+K5wRWIfj7VSLRNbxNpyDTv{{$pZB zC+&IM#iizTQ&0Gg$_zjE23|P3 zq!wU;(a51K&4Voo_dr!UGF8^oC8HrUdEp2g%+=c5@Qe&id&1SrC}!Y_`+@eLm-a$S zU0Q_i6OE|WA(v892LxuCf&L=y8%gVbKbf;c zFH;*UKqa6fZoa3I+p05?e2{^%z;CgDC%iASdLJVrm{#MEbot|S|8)pzC^KP6Dl3UH zJhV335x}$eU$s^tV zB>g?gK7-eqdF*&maH?hy1T>g-o3N0jw=e#paLH_zK4D7-U=M(h8O{&JYQh(mkod5mQ@tuC*+(%+Oa~yj>07hsgME=hiFJ5qE-uQ8SMu!4 zU3gLwH=6F}Z_-8u6)~m(!8gy+=_k|qygd$PY9w20o`m`?IkS{$Rj2BC+0FUBsXzkN zqUk3ZvdhrDyn`?EjKDNwFGla$PB_yh0KEi=Bq+NaF>Dx>#=jqiXCpe~g8^;P?9ZGy zGo`|Qh6iY-7s_PdF4@-ig+4WZlgdOS-;&g$hV)xE+9|Z_&EXx)0=1S&Vy4>kLXEJFTpI9J%6Ere1r2m@iiD zLEO@rENcTGpVyGO756a7V0~fxpvoT-3ERSIpfy_=T^vrGOiFBVvI7@1jmRYsCDXxgB{QiETy;12O zL66+7m3?1Xtkf15ihTEyY?|yGgW0Q*V0lBp^Q$AED=8>dZ+jtGsezuVVRCr8*ojCQJx-SLqp3`3~Eg zL}Unf?1tlPaEk5jM}H5hLPpYiB%{1K3F~h+Q8t}66%hK6UxctC@hEk&>Cop|CMvWe z?o*1rA20YV1GN#IT|1Mi4jPc4b=QWOT&W_iw{$M~EUc<{PD0B9x)yr`p;UM#;MR%8#-#;xVtkC_?|mw)VfwW3Fw5`e7x-i2De zf{Wlm7rFj;0&_FG&N}xF5S88q4(c58HU|Ei_nEJ{5qXyCp(|1ft1k+{dh?JQ$H_n; zvjBs_Gnhk>4goCHlPd3$UV@TU+XB~_6wcfU)B22t%_GD}yg&7=fVD{_&(aEEq;2!n zgPs6X>W$R-u70Up@p@eR40|cxkkt;{QoHD$a2~M5-T|*$8EuS&Oa~8DO$WTnLYGC< zaV@F^nMK-&UI?82h=Mtqg(MTfZ)d$KTAe82yR=bITm*@`XYko8n{w8W7le~`k|_BH ze^L-`T6ti(7@XLXLX?2rpVy-$N>q}eiNX~xWV( zyZ=$j5Sx9JsBIM7cb&trI>G2tROL-b76R?+mTDUY5|rXYhXSVjM{s$px^ zqmb|O%0rF*&Q&_#`y1R0aH?gJiKL0WwMafhH))yUh~CIlq=tNqLzwwZRtUXl2J$sY zM;?yZnNwA*2SRnOWzFh*iV(QTUXxi&f@OV}mVtiBY;YdKvQL&VE+~NO+qYXNd``tC zc_1UsucxwCcwR7EIRt#w(`9Nq%b{5Ylh8-gp3BX5W#gDJiY)$>W$fuX+8eeq`4CgO zB2#$>51)WIGM_ngoGQdXEC;`#*MaDJh_}q@5sZP7OLJ-zZ}c*hSIbuXtkEyTc{H#2 zc>Be1-b$2X0?O6+PVubi(qkO1OGyxjPV_EOMSe99)O)#&NoM(P&LY%r=y4~ju=EnO z8v;wX%s08PS{0ohQz^+DsS&6eM(&%{q@-?Bn0;DzV~CdjZ*Xgbjd{JUUd-XO3QSfR z(JxAk}V7UdO}pm3NYh;u45JU@g+raIP>nh)9%8|V5%(L;b8lD+mOU*5MM#{xA z0U`Cm5~Cb|0T%+*4gA?%fnj2+V6=S_Wj1BUb5|r##DE!O77t{Y`h+-KMuRZ$J3eC< zFu$V*HtdFuUes5_gu=ep^dGFEK4GTb3dQ2ON(^od5>--x&tA}8(<8q;8v_fW%V;k> zLDl4vguIMvVOtT?xH1(_I88}1yInE^K5j>fR0yoeb5f3`e2shI#4D#nB2r=`!0gbA$ZixS&l=0o!tn+3O+ymaZC6+>kY zmrV)8;m;i-Oy0rsHHPTsmKF3SzjBU|ip#CEdEmzZnP2?bRXdN~Le1rhvWNacXj=%R z7zt~gP{B02_Sa9^N_l`o&#l}Xv8F>FVEKO*vm#wR9dR@`I~u6yZrpAWsJxbpr(Qvr z5vi3Nxqw7p+X2M8-8++hiP^oW@3-X{;UTN{Wd zhX77E{QpE(5%E3)wm@N@2|T_&oJ$fniSr%!J`d<4gC&Ex1lJrm2Eqa$;4`ZTq$GJ1 zq}~CYDvvQY7DNLt{z)2D_x%LZ@!x%^9ZPKxJz|ZZ5ivy!;QSg*b5aI&PDj6uVTo$L zrRTlq`Hevu+p_e^gtKTlluRb|)QPh{FsNm3@BlTs4|_!%RehYP4J>z+G0lNU46G~c zdlQPU?Y76B{& z@bH{7#1Pe~j9cb9i4is=6X(C6dR9hD6qwTWW&UVGgwb}5eqY_iEW@#trCSW;Tnwepm6d#PQpQwnvMa@A9lZ2?S58=XNMFqnEIN_OK$#K5xSisbP&9{jJKuD5w?6FS-s-LqI%Y2B#s z2OlgA)dSfA&qp+XJ99~;0X@DGde_h1PkSAbKqq>C0%=v?;CQz9;e3j7yc()#L46Cn zUbM$3tVeV$dxd5V5H5+j;OB428d5=k<#HSg-MIcra!OUR;O|j;(02hD!VMFfo=`=l zzuo{g(r@i9(`0z=Lxrp&{sB+?1MdO8e>UxQX-yfVNB1VCgikT#R~N}!UeZ7BbB=lJ5dfLq_XnZ2t|O=7cC@rgyD1u zdwY1G%nWuZ!}X?J7&5!KtpYQOjCQGOa!>v&E!$SktGRlS_T#*p>e&%3)LU8suJtCq zRbkB1>MD$$<&NF%D3(M$aW1sc_*xclyxwDrAMuZz|9A_(u)q9V?m^~q0 zHBL|8(!2l~NfsIW!jUEezO{w-gQFH{(h($Ii7ClRf*TwH#))p~`^=UG^~-{z=J)B5 zR&33ZeyO=WyM>>-| zRO77blxfxhSFMa{rej>EYj9hZ5gXI9Z~!sNh+dsXg&9<;u?3ZAg_GwI8A^))YWPfp z+efdq7#uUZB^{a&_EVmY$nOE0H-ZIPQ*4>u^oRH>TOcRd8kkfBqVR(LGT4k6!JKB+ZtcGc#MQqdKd)}hznpQ)P(?1P=r(^WB|0jsKN=4B_NOf}Xa}mZ1Vv`I7O>L|zB`-} z)I{>viNuQ|>INb(3?>2E7M>+?E9gX_KP*y~dZ~ zaAo9o)Jr$do}=&P9cfhv5@H{{Fizjm3+W!K>;xdJINT5#G3-d^gW^C+KD4i8_b;+t z@adx$iCdk}jQdUcE#;g7s5?9S4##6*t_RKg>INb1W5SpMTP|Cz)Whr;_NDT*el|yY z?mp0Gg&)JTbS{nmpr>vhEUIwczQNQA5kD=jGW#Fv=9;iYz{2G@1@W89^-$_|q(q&% zb|`8Cz-Qh^Y2}^*N5^6|A97eVglen|@+x0A@(3xt{9FJ%?~o~^5yfoJ@fce5;9^)m z95bK3dY-to*YviU43Sf#fKLO>#&cQYxS(lY3R9rd9v`Loju|Ul{%rri&XXh=c$A;K zs0a55JuAmB#iSiEemAuY#Z_M123RWd(`1;1!>6YdEQ4ZBwR5#qm{A;`g#86;f?Z(S z^4AT8G4;NJA!Lnl+t!vycEhS}oy^t2+ueyITyU9RW9B%OUl?8`}=#ROdS z1Hjsgj&OxSY#ZGmnSPmi2!a;`uqvhToQ`r0Iw!3}F)OEe5_QvSb{AP*$oH(6uJ?B= z8v-Wp=@SGo7^LN-!KUIDl10YlCgfLyn}=NEVIW%FcWErb9VAYTwSh{oMFKmu%nAlI<2?pD7_|E^)1GSAJ4)elStLLHDjVAiGo6fO$P+AQoXZ z9sJMJg$+ifNyZLohO-uAWR`~EnBg|j*h3#Mt{qA`1=zJ11!PG^6I+pkHk*sf@@(V| z&8<>_Br@hvi>#B(fKaT4*T!_l$XIuNEZVD6gMd|~rOt3QJXy1Bt1e7f=_o<7?fR;B zZo9o5Bfrz>+Ee@5tQy*FKYooI(S2OmdSaGTa^;tD(IzruXqoFAeY6y2QnV_pn*onC z2IUB4qml4-FXxni!P%m6&!>MFZ_y@u;JlMi_Y_2s6Rk`_((kC&LfDKE@hDC;^E1tc zy_gXMmC%zA5TrXlMB}ZuiQ0#V|foap=>Ie5`NNk>bgceaPMSf+b?%Egue; z2Z_%Ue?I-Q^mD8p4v+xfM>|~(cZ=}4T@Lwiq8@YMzO~RlR7!0sCET)4j^)pE>+hdh z*29iH>3dt?C&o?_VC*mQCz;L=^SKpZq`GN{ml)??+)R`47`--qFLS1X*m58_r;@kF zt!l&D)Ep?(D$!nsX?h-ui+%T(d?_s1qST9?i#Lt42o4uIP(n|e6`J{N;VIpv?NfZ8 zpgz7AePkiBLDI`=+~zW3pFtmpRom}ZXyKYQ`MUp@+qHe_eAlZZn>tTHddQgWQ68;;)xTZNy!-6ZJTCoF&!d0MQ$Y-p03(;(6CRWXs(OWb9{yNtfm)fW1}zT4({9s0zcK< z-tO-^gv}v0=_0jtL2@1sK>nccms0RBu`Bx&B1{9ymC^WAxSeBTccW}R2yT`_s_XX} zj=r_VR=RnhVt)E9fyIikz_s)Kl!h#@grMY(h6;*l>Q)I>J>y%-!)4|}lsWYN+g6v&`%T0|%;qlU zEbfspIBpmiGBw~}I!1a;X25LNm|he5g~jn=CmHFkyhaWgOs&u- z(6e8XsGg1QyTwMOG3c?sJDvzRtFGTH+0mr!5OChk3Yi{MFa5Ytv#H~hH2Wb{{*y=LaWHzX5AI)Yc7EMex5=xcLJTVxmkVPC`AlpSq$rw*x`a1H7sA%n`8POCw^O$Td?wpCFgM0TSi6d& z3zC0qtjX`}O*R|6YS8{t{tS{XX0X!bcjLOze;g50%Bs(`0JZOlxm^5}6t#Vqgt144 zyWl>!Z?BS3`0OcCfi_KUlfI`ehHh`~5uxuPS-|g1e2|Qi322+@)vv++>inUSwJtQ+ z$x=Y`KB)@Sk6!?tP9K!<^1S(T%ROD|&i@!c6H}SW^W<=P>RKkcA4nC@iQ@nF?r^2B zZ|H>hr9`54<|9txkjS}tf}EyO##XU*;-7p|gY}jbWZB@OI%e8xTnrv_{4FLOtlQ@* z28kJDBCT-<3LvYD!_nG$T1929?k3&Ca3Ewyc<)QlaMKY(-BaYUax3PuVm5lm9?;cB za#KB$NxHWtL-#V0tZ1AVIj zJve%`Pn1p`hLvY=LwnBkg8izU3CsW>3Q`KQeH%Y3+hHUNFQ>Oh&>U|Rca6PQ{658lYVrPg$I<4i75@P{%p7rKAR zh_2QC73`fT$@Z<=4KX!aS@kp>u9Utk2hf9exIWnII>E7t4x9A)BfAjw!g;Qrn|LRn z)S90@<*I+$*)@M2-_BddGCVUXqZ!aqm)?jwi}?cIYXKJe(W@xEiMg`_6~ zKKVNCZ+K@c4AkpqVSg*Upd7vsm-1=)s+=9U#e7daC&VgP{Ce5`{>H0IN2bgv66FU!a==TKSGtQ)oba)EUNNJJmC592{*6Lx-}>2L(~#bypJcw;2~v`?h9<4_+s_9E%A@ArH_C_akp_ zG!3t!A7YD!yD96Ghp91W!9~)(n&;qwPj=%HW9VMB8>y+sQKOy`LM9JKuCtE@BZn zp^0KPW`k^2i9oRFv29W|$>V`>YsIxYzLTEd;CQVy`Rnb@CfO$y_xY7<=e$MgU{8Kz zIivq12T|+jY&qxM)iTk75a>X}8%}f={UDxQz`zgl%9?0cIFvA#dq$u07w`O=gCd25 z!fmJwM>`zH{W>L1kTsx0nANP zQH)<|uqNm4E51@(qE^>|Iho4wdavr$^H^q`OBumP{lP!6*;mA*s$MF29j}o{jUx5u zi{Mmj)ODIH znQ7tH9=nuKv6>>ST~@MJWV!)*qxlGxZ-PwSP1JL<6*z1&3#6We4DEt9@P}q;6ePKiI0M6(I0`9MF>LF5@4uSIOgL?D1FJ>?VC;&sJ$X(0+mEyZC$AR!9n? zOZM9oP^s=iKq1QFp%P%juJAk9rS;13UEVJ0pvWvJFzFp`_*(RcPVaKY)*Umweg_*B zygF;)iCw>*9jd?$xD)=YEmG65IOn){%Zp?&Fy<~CR|Z4l;?fu~GsU;-c6nlmDeQ=b zMLHK9ohGpsvH{#A8dsgRz;O>WF0>}+5NA@bBbK!L_37+QDX6%XgSm^9JcFHK#)nPL2I)>_$=8cq)0=rDh!Sl<2}8> zN^q#Fphu*rXV1y;H!~%Qb&o8q;0E)nsW=Z|z^f_qM5HUWBL>GDr5FU zKy$9OJ5NMxHZQ0nb#cx*kx#plcV|4@_`^Tv5CyM#;=Xlr{i!}P8xK`ao8*_XDjUCN z9gh0kT;1G*`LI?lZe~H|Enf%sCBz}}$TIkiVKz<@_(hAEIBBL&^aQDnM$Ykq4)j6C zBE^kI}bT{LGk(~USxz@OT@=d!wcS0A$$vB92E<2=I&SfGeg)ss8$wGr=f-oVc!d4zu=0wDuSr74?v*yPZ#%)jJ9^)q$08ES?3vh0YtN_R zHtZ>3teQY89q!vx-;py-h_$hUJ`nRd)7l#ZBeNN8ck(O$72dd=#N)>%N>ggTOoPK^ z0U6ekjK62;UFN6Ke6IcN;zV~;P(5;JNGx|CqVvLAe=cL-c90hCAssk^2B`hecJj<1 zTq@`qaudSn&-@=CE|tXGGhCN3^yqX}wTWl1?BDzv2FiO-tmtlt6DynKAS=dfsKLj^ zNFB7mE6|e7|ILXF5G#-3K?%k$!{geW-dKi;Z(>gQOw3#wsuSxeu=-KL)&BIBq1$Zw zf`vK?#Z5!zn7FH0%K^V!^M32jUrb7C8%Yx&qIoa6D(n#nRhO{Ws)NlIX^RO6a3RD$ z+u}_+B%rGP!FAKsIDem-BY$|W9Gz;TxYaQ07V)PfyZ-Sh3LecQ*~WJn%I86yN0i zu^1Q_p@7Zskl@TmsTZ~2I`|hFR!famIwe+RR5duV(0n;W1owjz4In&ZD9L~Dz>PI9 z$&BaUXpRYYnW)t(CJ+Opr!8|PJ73t)lq^G4bKTLf`sY0E7W5(TQSOjtPa=(1?sdMg=b-y@#XL;_?njtG|kPQ3L+ROI9-CApVx zLR{yLx7NkSQj6N9Y)a))(sCD=_?=R()cNrXmOS~AE3=hn^&Bqddb$){B45@h#P3~o zw}ZNQ0#1BZi#`6qv-|8!z=5}2jg)l~I~O1#cJHLxZI=TM=j zp%lJ_W_K;sP&T!ccWGA*)jEvFFU5v~Iad8vgNWH}2H}J+ze(DNXXrFDFKdp z3Gp&)ze7pHt~lvwODbA$gVM2nh=34gjHmi|A|Tw(*vD(!w~&I5K$l-;mQxL<(|7w> zSZ(3C@CzDW@X*^(wC&>{J$o|K#q4ZJRNodDkzf}D!I&6CUaPyTYZP{uVpO>^g>NVr z09)S`(AI1Gm0vjwS1?O$D3^iZtmd@hsA+Y3Unt_AeA9@C&?vzQfN~3f~~-T zo6u~)mHQaxAx!9b`meG6J^c(xNb&0ds#?Z-49}QV>Uc*`d-J9HZI&rS*s1{aYe=S$ zp~+eQP+U_H+!{GteiIw(7OhVGF5Q05>Y41g+>EP~Wj)%3m`PtFA5aJ`D5~L$XD~sC zbp4S{W6x+KUD+MhAOU9c4)kfbdtWk`vT|&0dSyQC0udN-z)U328dKdml0X7y5C7Pe2PQ)Lo&K0Q@TTEC+I zLD`(3;2slvINogrJ;F}q$o9XlJ9k2X1hf{Sh!bs9h#~ny1569ODG192!&}HM3tf8f z@+BnLE$Ww9=+jf$?;?!DbXhXrOTpDkK=W$pK;U>>pF8^+d<=C=O^F2M)Sk6AF=B}q zJVbnrzFooR*fM<7d(EBG+yaDTkIz7-;-?e1K6S`UWyD&eAr|NQB zfmiA+zb5#<6q8B>SehxV@4NW85@QlqL{?=rEbA#c7{gHmX_RoU&s-hnvfL+#)|SG| z+&ZJs!)x)|gVMWrXpZJeId(uS?4vbB98Sy#b}n|<(FZb#fqO*y%$qj_>w%QDqu&J& zzK-iG#Fp~Rm{E4fwiXl*-pQgL;~=3X5k{q=1x8RUVLmy;R}Vg~TD&-ocLbP$%zOpY~;TY2nJ-S zn|N*4R1HEiM!jj4p@OJaglKgHa3hsU7}#H-O4>bA$Ni!>!L~^hp?N^kc&YD4UR+Y8 z5b^#q7)pd$LA6|=eT0QqTSd2M=AuX4yx*iQt!2SkzYWARt+9uV$_#fqsp$JTp}v`VLb`J3+#tU0g=biY&(i$9 zG~BZj?X@BDfSx~ZA-#>sf=jKEaiI7~F$Rc_dh(Q{Ll#Yz`%Lz4O{x{$ za105GKu*oYxvyEpx0L&0RnhsBxziX@jAfeLBSPSVt7nP6-XH1RVX&h8-8RRfndL$) zhlqChZxs#MW;+FV$a<8eQ*)*nm$huPO<{<2enl5?_}`yyn$yi)qnyz6KThBB>A@p2 zkDRx^dTTI?_Pt9oIhhBpmBKkL_K}6MzKzveEmBiITa|pXJrF|pYcRG?r^Y$HO&yZH z%v}ZFl#bFmp@9{8wHX3$JugR|z#}h&u*ureHJkiBSIVvRl&}WP!tCGvKx#EgO^PWc zZ>Yk)xHqSb{X>?uaV7jSIs=EkfgTyFr&g)aavqAONxOTYJD<-zoWeX_Mnc;Uut%2+M+7jqZifu%{=rf%f;%8e#vkXC;%tt=L4pvUJc)4IUaDtnd~@PYPFCT>@KFAeNjhV;`u59tt}DOh zwT9+3=xATIfT;Nz9q&|Ap5!MIux!WS6mUHHRA(P-G5WB^3+OO-YV5ROdQVKaw$B%m zs>w?8R4uLGpyX{1I1q}XQdAO6`Ag~VmZ|E*Z}TC>pVki}x9la=G28IJIMkmPW#b-6 z7LmGRFitXFH-1&{O^LB6*X7u^sS*0sJ7|wD)*v%ruKRE0%=HnEupS5efbYmA>#f11 zMecdM?{^nd*K_>L&G?wL-A$Mz5B3)W>vwuo`kex zkx1T}3qLuzCn!DdAU`NOkKyoYq`aAhkv|P6l$X8?o6V*yMY1%eIKwygb3%5m*!lvq_G=fN zk))OX6twI$-mOYEM(w%Xcq&PbUkT&WqRL!# zOsEZQ6!(wS3_Q=m3o;d-!^XO;`~gZg;xuoojEe++XC)tmOsa=l8P`M5>dqu4EjP3{ z0qJ0)q#`M}WgglURheG9B?&dRqaz!tVDi*;(ut;%6@frn6I}r1old zeL`JN4TIaP=6^6?(^nsq@ulyMv*y~P@-b{+L@qoI>isBcwgw=fPvSuACAriSzPyO_QWzs1;R{f;)LFkUXB~`DG7y{XWw@*9Y*BWd${RR_yhYYz#W@w*WoY*}p%E`^nEtu`aQRLrqt1RRbT`nQFIgfoQ z)DoF_oPCcTwD)+pK8kYQ^Yr4kxGwRNDk@2yJvnJIZ~&@SL4Yv0ad6Hu!)MmUCWzNHobfcoDx{k;ef6YxTZMZX^W}5{GVaw9aRHZb5MpUQ-ooZqb?Jj~Zyg5**N)06?ZNk2V&giI zpRumpjY3CT;fJ33)m;rlepC~OpLJ3UOEg*Suy`hVW_kNO?ykqOZ{Dayt3L`rk%aF1 z`{B9OLp{qT^SA_`3sw1R(>w1IdCx!s$@D`?H;E{ih0mW)q|e4<8W6OTjV*f~tSgRV zmS6dE<3Mm<*uj?~K)tgZN0_JbfzemlrXQELqM`a43lM6>SWklq1fE@!-#C*^`ySgK zr6ovJl}#=<)0iVh+Vh>*$+#2S>O&J8tG6rzHC8|ik}@l2){6J|nVPxgvc0q2Jjqs!qi^w`5?2nqr5{fi?|{$_Cy>G}#Dr%CR5dMEe^9=imB$xw$p~dP zt)v}ruDjF>z8~nl*ro0Qc3IdSIRCm!mPsjrjbO!#bYd2U?0NBg*E?{L@>>joEHxB< zg{_<5>Cx?fnT`gQ9RT?M>R2!xN!QF_5ZpigId0QA$XB%J*C#Fm#<5`6Qv*UX9-)ud zz=SDmWXx9-H9i=fPNzRa{rkC-zZRR3Po=1KG*O#=vJ2Z^9v0gdDZu$&IbGht+c22W zmm=ZOI}Mi8l}B)PdSqNXa89a7Z(^1@y7>7Q{Zi*RX?wRBTh%k3)9^^rzY+W?JQ(tU z@<@b^950fPwq-efjV6XCDFl3QNjfbsr5jirU{B;&`S4YAg9fuP!QOW@>tSnZMP$j7)fL_Oj zITt0a_qDOq5p4%QJI{EG9iok8OI2OE@9Q)aXAoH6 z&F8YBo}}D-)n+Y*{j2SK!xQ6m1#Jt0?bwFD<&Z4dhBYaR<)3@pMD9V7^GfRDSj36* z3}*tvogF2qUW=-yehMSA?vUZ?EEh`9;lCciZ*(Uo^{;gWuHgD<;iR2TY|O~$3%HH0 z>4oA!PvrrXpw2#_IIYCj-zS1v7@vd(ItUG#3hFZ5t*A!eb*)4-&58|-_~-V93OwIi zQ8Sv?>34i7m7ow>Zd|&5kON-EwywSU)p|Zd<0-fcD5SM94S3h@s2|f!Sxkk3P^m*t zoN&$$RdD(}sC*kZ6(b!8YxBj7mhfJ@wsF6k98;^$7E3G zfU*Y}(s7ih!f-5Gff?h(NqddHQrJW^nxTCsTG|Rt?or(1B&Wl3OIx_bL{%KJ!#UDt z!ElS`4y34FUl(uS~;ed3fUfc{JD zVr2%m3mVB#k7#Pnz9F`_HE1t&hnr^?WvsH?A{fy^%Gx4e>8Al@1OF6-9adJ<+&hT~2K&hT8 zA3nsLHGsgqaem$wznTI!PbhYQI5ee-xb8hup4w_e1ERZz4QTvF?RSVAN!(<@zjLrD zv|3rKO@-nO-eg|QDh6zfv8#moMv+p!ovjZ0F)alhqVe~|`LF?XY0+a&wBFSz+qa`v z4;2M{I#~(Xs)=ZW36trL7Dsmf9hfYP)V@Ju&NVfcG9&h^fWdnwzb@=M|Bl#B7@~~- z*RIPFVzm;zmSq;o20hpbDM>Q|#v?eHvOut1OR!E}udN^OA$U{EM&IY)R010mf47N% zIRlYesWyIy9P^7Gm`;aDhjZX_GhjPV9IOvbJ0j9JP2#XW>lC{b|w(nM%xNvOB%#`!1L&Xz%Kk`0pml0J>7}uu zEgT#xPZqh1Vv`Rh(^#Cg)jHJ;mRw3n4#U$AaY7(Wbb4n$jB(vMcXg{yyvO@h38{i1b9i(P`^x)MrmFj z&xW-@)IAB*=gD-sZXdBRxKPRzHy}VgA9U{DV^1l1yorN|NCa?ro#E3Hu_`v!o5WZjq?G*7UeOiC_-BiK~GQ zQKWf)$Mf%XzGQ`ifT8D&zbHz{RSYA5!dVN!fAK-ngSJgxU)p|3gH;H*OpTwC90b98 zjn%i?D&f93#g(7@Ia>2+S+`|oHWYer37wG*KFRa+UJD;@_h%T#lFOCdU*XPEZjXv9 z0&ssz&IadFjSd9_n0*^+#X-z(}sZ0y{W?BP$ z;#>A0sdukZtsj^_;zCrzkr2yV9uLH*1I4BwP1}NA6RCex;bUU{2V42Kfl1zY=@$Zp6e(0Fm%B4J- zO-fTgnN%-#m7#TLdEro@?pNX&p59xBan2jy8Tx@VZb>$8&bK+T=t%8VBWJ=$_2=QW zTs(=npVi}49s9Tq$Mh(B-p!$Yn8Fl3l^L5MhcFb8D^{$Y`Ey6x3x*5jHkXhZMJ_n< zG%h)(&7#-j*2-`+TM+U_F-N>ivFd4AJ^&`?g2K*69`Qhk{sr*X?@&KWxZlaiU;cFB zRyFlm*7QqmGc;40y<_L{D;sOK^xa}bCAS`}qt4yj%U)Bi z2p`6krkT-d1brge84qGnx{J^E>xP5@CpITqGsyU2C% z5ED^YI)S&~UUPRjIbb4-IdQE0CG3Ts`MGWa8j#07#rWY`v5)Y`O5)V|3SSn?;KqcMNM*F^XdY+IYn!cH2pR-cN% z8Z&I=$CD5~f%0pOL5a&doqbrFj@rvKGI7Vv{ui-dSA=M=8E5K;0EV91?z$Bce4!eU z6JA=PAyMS6?eA3)tqWEb2Q<8oLB-37t&?fY9UB2^Oo>i&P9tqnx5>=Al$jbmjt86O z-Bbd0R%C3q z@3Dxs&}ZO7CIWsmsL!rYgZ&FHweo;-{+-oWb_(m-A3_pUl;9T_-qMvlGP;{9NY8sk`7Z>Jax5~m%38<6qV!(jD*CZvsqiv+sdkgeidI{2~EX^ zO$60OD?dhyV;n?4FS|M?SzLGl1pN@~MPR*09no9p)UU;O^U%=Tb{X!gYf#=58ZbSW zXJ7#|aNt4xE;+W59+ud5O}8g=;FKXeR`emu!6{qbDpP~e z2F?3p+k%!VE9SB1Vt>>UUJ#z!fiTPk7lDauTK@G7Cto6kuffXuTS_(ZHicW$hddCI zDt8x!nBr}^fACx>zM9FSy&B11<1Rr}iQ)GRpsvFYcEfH~P!utW;o?3j-ny`RNuj>? zW*O1hasiF#R|CT1>mK&IijnUisRy0RKW!^Hb!%<+PjWd7NV5-G($@`K8BPvu`~HUQ zOx224xhiEcoEIp*6ZuWpQV)y+GQ%eD1Ty)@I4xPy)gwrp=9x_cMm!NkN;+lhi)juN z;MI%H%?qoIEfc?NsC z^C7)-R^UB{Xgb9_UDf9R;eb`5;4yX>n6Y4~OweF3^4CR*SZ_BA^NXTH(KJ=(>6TuV z+bb^~FLji1U|%24iUxA=j7^bfRyG{O3J@qX8G%gQGV-t^;t%!i#RRC;XRt&T zwT+7LUrrv}Ya9)*-Rw#&WXp5F{9H07H_3k0$JA|(P52w{T&;LCF$WYYFaddLgp8|H zh*GT$ggIQJNhK$7(Cw-(voST*H8yF$cWIV!p2irB1Yv2ql)f9*M*+Jl!19hh9OG)_ zD#$=Ym5C@qG=v(mP@f@apN(XgD_87EG4yL>Dy^zjca4XQVz39kFK{S((iicg!aTm6 z+s^;s+wFjI$rJYXavpr}K1=BX0X<1daA2xnR_7NVYj2u^5JX=09eQj$0kv8ZnP2wq z>Q8hg;i=0+a`znOS}!GV6+07>uQ^DYD7hTyrs61`HYpD-0q+Hr9(*OQX6}jk^c5#S zk*!5ifGK-@CYj~6NtK5E2gn3ebOvantupr7TssJl3^7o*5OQBIdhW&QNR|R)uvsRs zdf2S%mkVK(eaf7kc_E1rf%coY>}XkMzy(PB>V4g5aQ6d|e7*d164K+0_J+QVkU_A^#*K*krFF3-+o~uL z8oos>U}Z1Y=8rN4g@8!fsJ}~;p9VjP*qL5MaE99L$IpGc@)#;VIodO$;3|3;rM|mb zI%ITIZy6PHA6WqEx#vy7g-0vTMN?A5%rc8{TXxfVEJ&$blZ0|=gPPNxf*o&vK9o_4 z9%q&f;aV14+_W_YrJ#ZL!1@>FA#p2{em$>qjvA*#HECc}m8DhUiIcRl8?Rt}!J9lN zblRY!Yd`nQ*DAx`R~f9hCeueg4tw5TETqUw`A83)mh+HK*uGW=kL}B+Pop+1_Tm|Q zG(c+e3kI(^X1w#}-j7>)^fW4`AjV#27smkwH+YOXC>_mmf8WWd)O1rCnO#PuO7!1; zjM?y^)6;f|wT$e|nsn#T?!YQiB&!QPjB6 z==IGjK~oyk&vL31rzgJX9gye7#FvR(Xq3xwH|fJ%=tofrJo!jY;8=}6B4m5od^@{v zRpHedo@rg2dFh0pIYE)y$kq#Da7(#Z-!s*}7Plk-!R)rY_aYfSDG|il4r<||;YeIx z{Tut(g@PjS431rP7W%P2Pxh|g>5c}sMeaB7SQO{TUot`;9Eo-l&;H0H11PSIO5jh; zIZY}sTo}AY<{m`+>##x1R?6RCaI>0uA`S*`YHb^*;(g*>%!Bb-$jXdYm zL0E9t3*+Nq*XOppPmh!nS|QT`9s~w0W~6s^4Mqe-#6q62i|)9(=Z=Zf-%=T449zXw zl+^YaDKISu;L2pQ=eWMB)UU}@8j#^(hpq*tYg_COjL@oWTp~;foS(FSu$B%r)(O!U zi004LPp#x$&0B3DM`-NuXumYNQ?Du7-~QD)GP~wb*Rapbn+;n> zH(*B}VXrDy`Z8A$L{CH4YtvAC^G`$UdYH`_cMEbf_B^W}`(>UzH~3a>fqrpIg!aMC_445Q}o3$Pt!r0<6 zhC2=9VwaBtb#3P%Beah+GfzaN@ylfKO>kr3V5gmQ2YRzjw^TPWSa+31(HN;{Mg4f< zjk98G0gnh`Drdrkyn$WHDR_h0+ZFd=W)54E*FxDj98E0WiLljX>|xY2}nClKJ4UhAg`Z&J?G8n2-EVm5WJQxZFkE}tD%b1)3vtay0% zPiMJfn(l|3h}OWz{kL{7M_G-BdrPY6fzu_z9Ptd*X;bTNsC1ORkJt@a)~X*z7&5uE5z@djRE-`wt^I+?wIcafLwdFt|a7dSFVFh-d_& zBoZz`ibiL#&+imAvFTB0{(qNpM(3c`7B-4bzFXyuBU~oK5^Z28ckXiYM3Gk%52QA8 zuTA%~Hgwo=8F8cYi{k&9>{KX*9-n8X`;570={i{;x zcmhneb*}cHR^cGgYJk)P_we?*#KIPA+vadLM4{HHml*y~4qwFqubW~}f;?P84MSFS zp_r=0yIYZ);y~QxWVz-Kc=mYt3TE#Ziq_R(6 zPmB3Rn0+<79pe3M7H-mCIgcamdCL2m}6)7ULXalNll>g}$7(QQ( z9kwPv0tV_;1+|nz*#_gaegh=2ID63PS25@_&<<_hE%fl9JpCN4(J%i#e|VNT7Y&EZ zXhV!=VhBZnb;9hSZF+$Uj8n9*u)hG@@e&e`5~%Jw7GN2pr|TaUH>oDz&50QMyx&O)2iieyl7kRSSto9kVMEG9q=Y2!vkd zr2DmM^(@7WPOcxL`oR%BH11AbP|H}}@ujRYmG5zK!;h&6>w7z7pJbN;ecKElMhjDA zwMIHWXi0otWkR;hA<_6nV^}26=R|fgA{xdxG`#9kUMKC(wp8%V7?l!tq-$-*J1EPILx z|8g}FO_rdil|u_V*UOPcri@^C^`HYCfT!Dwv_cLlwcR z&$R4|{WoSui;0}u<5kJ4rBS;Ztid+GkUAdG&6I&vYirA9$l)bQpxZnW-^l?~qVCDi z+~cN0z{ZmbG}dZ5T~*?EI#~>|7Qq^?HOskehU0h0a@fLLP<*>mvCYRqB$c>@8Qvc) zaxQ_q8y!{f>j+yTr1gK7e^FLB=n*dqh4eJVAEw5CYkyPU8dTqs|L0RK@>TpbGgn#AZhjOYSurU8+wX; zHa3BvXl<=8k{U^ZZ5D^LYM=23zSFvEAj6`oC3iecK2>1`l)=~-rv16pL$)&ra(4n9 z6EvdyGwCF+{#sx)8ZVxHBn64oT9(~alK-ko8~Ak3`A>ga>}t08&62NbBB`DVx!7M> zQ~8248~%ib_vKq*WVz5aAYtrj zCa(%;-fODcVaQ2G!Qj2ExbP;Hzn@K(Kzb*4Bygu!9zB>zT-VWRu%n#)6?XKN7a@Gze;7zE zo@So!7SJ+9&f zc|URV7Q#$}@X<_DbzKJw+(PqDB$~$rM3p#zmR`rgJigZgLCzjVfE$j`ZqK;=fevS& zIDCd{#D+?bxoK2J7mr{E8`<%PqW2#`29yFKHd!$t<+~UK%{7BY$}1N12(0>31cj1` zVMy~WiFBcHcu;s5OO7U5XcFhWRrv2T^<(S+r7L}>py^O5`PZ` zqTu~1HB&L-d%(vt<#lxbw>%^IaQkcj7Hc^UMmJ?T_@%XtK*EVE^;2dUIrA z@bY$`o_4P~_l+mwl^``oJS>xFmtqu&90+M>Sqzb`+@%<-y6d=?P1$9`{OAX)+Naxa ze5|ASFH%`xT6wW6F=+C>1Na)IU4|D{&yQlmfN;IKn9<}D!iVqCvHCf_5#>e7Ex%(7 zcLSFT^N$9aQth`${L0TR=b?*%<0t9(8l^h&Q#qJuaze5Mw5%7f1r*ZYxQ9!h>@}!##v$$NR-O z7tk`r-mHaqmH2ZqN1y;OK+nHqcWsbI~OPG|Dzn3SlHNE|2N8kiIszs z?f;i+e+E~?+iV4o0)a-_#uq-i`7iYFtT+ERNrFU{j;~1D(7Z3ZXSvrtzjfYq<<{P^ zIwrGhr`F7W#L&5kQF%xb+H-1H@KO0lI0QxIGa|A3$H#@|#>e4g#7nIYY~erRu~TL7 zkIzkwET+EefRH7S=HGY7VO;&=|28#&OLl33rfUMpUT@3dZO6m_nTw0p{z5mq979Ba z=+VgnIX?ke@#rL=(q=^D^n@3MW^_D$GW|ZH3tGv78y+4W=700x6PbcKH#0(Pf*c3T zo)TrfqQ(ZU0-^Jmz$P?}{Zb1}*xX#(&dv1ipPq^%Haiuzxv(CU6@s^Gab|%g3&|5n zXe2=I*M$K;2;$BQRG}ixA ziG}q|b}ML2uZj-OjzXATU4wmM3Mj!!B;0?l^I`pTXwm5KaOl+i0;$02pf2@gGCDO_ zC^oe?Is!{Y{!kq6fPIdcLpVd!GchweJ2rp_A^;_HHE<0XoT%|#3jugCewh0__K(aQ zED#y}96*juj9|QfA+JurT)}~}@%04x^?%*J9KfcA!5AAD-6607r)h1CepLY+!Lq*t zw`sSQ2MF^Os!aH`0N0v$KH^*u?|perpS%n%@v%M+GXBTT9)6Y60WD3KsyVuAj3Xs^2DD)qr0; zncW+`W+I>y!q_Y3Mkbs-0OL16{bP^n_ZRF(BjuMP_SZo8kbZR)&_(t8^84E!$d0i} z@C)s^RvrD=^>cE)+t&Z`8)hBwv!TV65E&Xh^ZP8#1-kbtIKDdl(Pz@^h|=Z=nL(+| ziJSFHk_x!C2GC`+gwjba%uXEEX@Cok4UfM8d~dVNY@dIeIeTax95B!QoPJ&tjax_E z$pLm)EVw*C=jTvQ0cO6KC@i?R{m~bG=yHNL02{1+K?j?&uUrsMhdD??>ocg&wb;fc zV62cI=I`KjV7Ng55@dkTG2$T@W5kb;?yd7D!=WJvqvV&+G!VpuKm>X}(KlpI9?|Fj z8LtMz@401PkPSdssz3Q2xz>PrpWIOZ@){4wkI*e|>`%1jo&o?6X8ZH@o5>tJ(YMdz zF%LNSM8f{*I1~WBC7b?5^QADpF>?6%q5O&TY}5LJ?R{+c!1Sds{bqRM=gJaDxX795 zlkO3KhjRxY_{97Dp1h@f`iZ^(wr@@U?Rkvf82n@GX>$9*^esDmD(mtnTl#Ez82i>g zP^h}Sn%K9!f%;hE+}+^n+W4k9=;25M47~2@{(|qlcYX&>-#+vH)ceIhgK_<+TmvH4 z*GCrazp;NmGrTn3e)szTEP_NH2~5`yg*i+L^=2lHuOdB2xwtz&9KBqPezC69+;wJS z1Zfna=V+Ck*Zp#1JO#;-LdPw7Op`hT=!1LuW%x7mMmbW_dULxB5YUr*uU&?KX~Q^q z%sd#jSfu{_r3x4V&tLaDb;SCQdEi|NxzyYJ!N_vWh?jkK+JjwI3k|%>=Ux963Mxr50i*- zV;UdFrc2w&oa=Ce^Cg9a96V&Gc9Rh(1)47OtHm5UCAFN~G1->rD0I7M)m)kD$p`H( zkhu?;yz%)|OgL;6L&-Bt%9&7h;p>A?{g0XB$V1UDGo$*(_3W-=F>8I*MQzP4#5hsN zu1{~b>1G_A;COXaD00;u@iH?8(mu0cT?DtvIz%qqY1^P7N;Kd&6I<&*yn6Fd4o!3EBJ1)?F0NF@!8jf z8kK6j&?_s)F6KpOLz2Q{BmAvGA$AHKhSJlvF46Y+VRka%W4+2@82UELaQX_*`gvFA z3rr7-8krSPBm+}A6h&5N)WcJ`bLhj32Ny1Ehy{JlX%nu>BHq6!;qY+&k2q04)gkX1 z^id1;hsXRpt|Er_ubRx4WgU5??9{Q{%9aTDdGGk)qOLmo{R(&co|X!fyWDMnZMu;D zV}Q+DgE`^e)&nv z==OyN_re547%%f@ep_JEXJ?*C!j9-1=& z5G@SHww-*j(NV{?ZQHhO+qP}nwr!*1%@Y2QbOe&+O*R%te?j)s8|FRdKTkv zUGBF*MIfz~&NO)^!BKA7I_~s9;BI zNg+f(!2n(R3f47^-aK*qW;6*TA~0@WLbzRv9Sbso9?Bhii&cZpp$7S#l?%+!%fz8? zzCxbaWBN@yhU*l1%`|5fJ8gOe32{uWZmC5hkc!2NHYO@8j)f)OZmFKAj%B&D#5?4g z%p}S`{&W|0Afb9AMcnxauBELfze@h&oh+iZ9Uojx*qMV1)I6TymyBX4uS!*0&WVI6 z{9|D#336HQ6_=vtcpqG<8Xf3ui`*-tQ1y@B6U%E*Q%opt-z6hjQ@a`wPeOS^E0KHA zWtQx)aYz)CHD}+gN$X}a%o_j~{q!_;Ez`$CK2%G)j>;Y5TXi=Y&m@J4xE7L4ZD)Fw z6QumY#{@S}1H>vOyJDlT?8P8>s7P{LRAwR`!|zmwo}Oe-Sfll@ET_I==&}(9b;+G0 zK}h|O_qa0VvV$L6kV3KUYB*h6P@`-rm<TRldMoc z1`vHB3dKN77FZN^z%Pv)d$t6$@@M7?CRaMuHp1!Ny@ch~S{wcMHP;1kl!9?PiDqRR z+9dmeQG05WzK3g+G?-&bD{o_+1B*VU#5PPF9b8iAzd(U7Cl9BFPb_^b)asjt8I{HQ z@PWa5g`}fm70vz-PYF1z0x&*iQE93Y<7}&4c!Kg~Gc~(iX4i9%h!Y-xGc3rNdGnu} zh`(M`)i{lwlXIEuwN!ctf4$Q)JNj#?f-j9#Ex!k#H>BiwOG+=v;E9%ud2LwLDYmid zjkJ}D53^_MtFbi|ss`H>S5?nX5C9ELRKNL4*!gHx?^L8R!-vE0(1jS(+!{2Q!C}k3 zHF%g#mZ`r4HcQsrN*)q&eIMh9glt&6ZYeW$%b23O0co74x?O&p8wVCyvFn%wmSl>$ zc9;1AZP|}p;X8mqgiOwz+|Zxd=|xShy@#d_X5DReU<}(+l(Um56GTVu2RLH(T`tH$ zr{Sg#dI!%A4q=AtNSj<~fan%Q(Xb})ngI*lyB*7-MNyZFImz|$dof>A?vcjbWMI6yfX9RGXG_I+iOtZ#%x5-$2;~^8)%4FYnasN#FCs7mUc;jA>qle2jl6;9i zFC86AYQo0yOr2inksD$w-76r5!)tod8)%YmU;ahE-LX*3HVxgMX4 z=!TbCoj_fnVjg5#woW~;kt^CjZ%qt06A14Mo1*r2Ko{Tu893mrAV2e6+$*;xvLQ;k zvsk)wRnP&wq$FFccGl5M-Y&d%srL{LJm-o5%qD}GMoPUkZMxr1+u^5uq>rJlI(EPD zP*02WK5ktpn?Or7i7#*CxR|zim&?cSy!GZx`YmBke$m(_*q!S%s;BHV5 zznxU5VDC(sW8)yW?vV2|Cd`wVni#HN#rzn-A@89Hzo<}&oCUq9z4N84K=8-L-=#@}Cy`ThQoGCHs+M%8va*IA<_EIhk{u#(%G&@QhngjdJ z6|K=Q6!X)PZM~u?m`s8;7I7fGm|?Gc-|k{Q;pDAn4L*ErWYiXbKpB$I=@LhQrmP3A zo(ApdW84gLX|5@a@%|FSzFf2}rhq-tES6(2=2uj7Ag`je?E~}ejm}*EYTFG=E=gRiRwu#qME?X zWox6*%}c;c{i|GclhhGCT1KV^rOq)*QoS~UUlXCXl|^Noh+bQlDN2^GYXxT4ao*>P z-)T)zKQ?GMoDPy7++}6^%gK=5Zl$v`4hF$ZzA}NO1#^=LYv)fYzcg4 z2;U$B6%W9YM=BUf#;jaFJsSLDbi}nZ@0OQY9PEr}PT;FGf&TQV%~yXo9T9R(lIPY$ ziB9uD_LqZ+r?8H?DY!n4l8C?y#1M2bPkj!uTLrJju9(k2LmEBCV_S415VO>IJ^?&@ z;;$>G;PZwvg~K&eBaI?imNw{+6uupDHHPKNK70X+gH?NV2!Kv#2#XEKAdl(-QY%*s z#0-@H)ccG$+4Ia+XJf`uV{Bf9UmxL5OK&?OJs<+8n|N2ix{D8gd8)&Xx^^bM?nK1V z^^n3r&HF_G$RK!n1kuyjTnDCRuxnu*_ojcGyBRAm{9fb^RGHrK{5ltT7+y&Xq4-AC zuf$ewi;dHtRx23g@&`LemQ)y#w^dd#zb~*6wjAy%#81!A9jA5}^}1qK@xcnerexHB z|4-?oWDTrX6X@VvEu<8#)%yInbSigY3oIV}*zms6HWqBUk+D#kfo!2Do8mV4QQS$K zr6o24jGnuF41jOy&Rw!VmmNvolUx|m!pXn7-qdMrQ>sN0_Fn*^2ZD$vnOsEOyT0jqZzy`Qr;od zCM>(CgSkN;8Ut)XSGg0blthjWS}_2cN^9jbL*QNqRoUHBF{ULk&Gn+}MJg|^AcA^) ze5ojB-knN>!LukIiUVQG6-`5Y+-n+gGNyDKuZUb9dkyZcmLHtqZaD)%v$`|w{u=_e zWT;;FDZ#r>WJ@r~W%QHPmPWmw(7HPyE9DO4pOsu}c@~cYdfbUg&-_>!PP$zv^<-y_ zCLuEJx!KNfTfJ`-3#s~zSH8--ZM%3Po{}T6?35D=uyz6)c5yxLP{sou8PjCOq35@_ zH-;I&Um_A((IKh1Pj$%TGgSfQ{>43|Sl6wpT5U-Gc#lcd*`(KVS zu*njnc??~zMADh+Q~PAV{=2&=N$*a5c8_yZgA@gTN9jBeF`1XIsa}s5fl;NYK>6T! zu`vkU3I_y!?&fR=7LKQ=N0+LxacJj&WR}VN52`_*;lt0Ni>~Bx;+!@?`gU__BI&rE z+a&fOmm3IRMebJGcewM3#x(IqRYL&3NqJ8VP?fa6hIb@vW9>@wHncW6x!j^1?DRc_ zrD;zI0+LqQ)2g4%D>;9B9@e<;4U6#EN`1PT%ulvaNgg|D_~{$WCXG5EL~YU&!0r@) zj-Q3(1)P|7D`FAdOt^ypcFmnt+qk*PD{<344l6~h@C9bq89sueuvR`CPQGZI2WevI zs>A&D{+GvH`ouU_q4vX`NhJG)@%4^_t}F~uXs3%`6mMG}f0Zi+U4qCx_zJ@)LR%u} zLuo>dnWHXXmA>d8HUe**@Mr=2}Bvl#R_MoiTWilK&)MLpo;Tj^Ozc zHI^;o&T9K7goZ;+fUK{&EV#tuo6STYFSF-EMk!E~GjZ;F{~%G3;NP*Om!l3qZ|?X* zdM?vUf?xl?^Vxk<@ymRMIJ%ZkNB3y_oq~VhQk_aib@Hx7!BuMwYiv1}_2|uHm7F_k z@*-1F{rgUl$Ybj%yC zT`=c`nv*@y5R&^^V2_1@b_AMt?2vkXp%4kXupOu*=kdUp8#~&ucEZz8PKh=-tlA#4 zljOY3bGV|1*2{ew_jSbM+-|YQ&}r|wue%l?`V>z1RkdW-ohdu{Ct%Zd>{UZ?zMaI) zO-PI&i?5>ZYddMxOqQC3BVkq@s0COjvVBfoT&5`je@moSl5znlDa0&{$*RoTYgS+lXzE}X?D-bJz;%ajHd3k z7ULq7c#kV%6h7^(BcOU?@R==>p3|QAvF~7+aRoe)*t+gSXw;f$bMFQ#)B43;U0%!Q z)GfY#jKB*J{|fk_T|i$rA+?(gevUsjE(fSu=Ck`)ml#O_h*(Mm>T(xW5~b?6o4mXO zabR1-1qM09xc53h#3U5fbti`n&$maOg5dA`#T`?0SRQj`fO~nxkH#`}dLtbX zcSC5&L-@CZoXN4hHoAf>J^;lBD~Mpee2Yh($2qe@sv1wNdG^_0tg2*P*&&=C?_=z?m_ z!y}T#8|n(}UjAHJqnIy$Ly?Thn~C%X7|I{JMRL5=5M%Oik75jQ(N3FhP~r7!)+`dm zAzSiA-;}C3SNMRyl(gD>P^(R=kPlaiODvd0dB3yX?g>;jld>Qnv#m8ipaj{_8!1rE zHgQTUiQh~m!{k#UJVTUs{5aWt3|%@KU-X(y;Vhe=Vt*^Uwc9(Nl3?I=A5g%-ChDBP z4!8`N2JmfJqw(ODOuwRz;_<;dnI7>4ZhwUYo%L)a1LQ6 zpC^YO2TxUi6(;`Syo=N_ap4ei#X?m?n@azrZqr1v)islu#wNq=0etVE2RiwoR0Cd5 z+V$LT7j&Gm>6UsqfF@R-KB>hy%%<{&Ul&XfeS``K4qZ6Vwru8Q20}D_uYng}rg#4S zp*WK1G57Va(sY~;Xv*~{bjJxfX}sTBSxMXzT$M3E^6{4>N1OSZO2nZkXB>EY@7D8! zM-npwQYiQygYsztw?EG?D!kNgd)4l;Rm-8yw1D+y>UB{hlW|PWT@A z+K&=D??&##qIu7&@960xTaT>pxiGp^di)jHmQ|#LThXHt5oySgxp7_dA`p>c|HU7S zDTK@NeT131v-^`|9`2Oy*otwsec`&HmGo%a!x3dVasCh8J!5iy3huP1Qav7c6@vh<8S4dl!ZM z4I0Oo*_CZn_%+185(E1d5wim~a}ufjsHK;RA%bzKM?~YR#SS*pqeQsi9lTc#A&1zTdi`xzsyRuDt{rCN z6t8bt@Pb`i>R?b?Mv|^0WIHGLl2xA0>;rxi0l=QPnq;oaL zk>FOkx`*&^E_h+R2EPl~k4_Xbi>Dj)&h;I(UNszH8bcI+K9WDK$id{NOP4)69x38v z3r6Oslqu<-gYfe*dB?R{RVG<0xbqe>M5ZU|aaggMk9^2~q6^nAY?jrQPdKJ-POtv3 zipBOHw?0>2nZUf;^iR)66CA%roE zp7j59TmVz2ND+CSUM6{tW|TOW{36P&+}X%I17_HUg#Rpe#5h^UH5!ujpkcf=5D*$1;*Yi%ljq4w{6Ihyx9kB`+S)1dTz-!o8VBe=6(Y;csX`DSir!WM{}i4RT(4Fl)4gY|sq89``Zugj1fU z=P-08vL5qaNVq7pQIg<|QilCm$%;?8!^?mO&NjJ)b*d~5riKd%m%idodgSAh$~-J; zgQTi31o)K3O)YEt7vZ}yTUcVweKlg(m9i_|Q`e>S@wu^x55316Q<%_{NfgMP%ja(J zZ@{<AkM+Ae6ctmE8(068DP-LB z;Bn`0yb3XmS)Dpu8j-6o8OtMlAU>}G(X-TsS%wWXo8O*Bf|8e1fVcw4P4k6vZ(ooz z)#5Coi>-{#jf1}(80OkkB(W*^-kay9a_*H{u6~qT=d$P;1+Ey*&21ph>=k;a_i_zF zG=MiXKaTuKOk0339_3r6SbofJIB-EHts|swbg9dhevrSbCO3hY`s;6+YIWrH1E3h_fwC|J%gC9ZuCI7=fN{!((>W%Xl$hVrg-_!t zCxzQ(BF()D<9vWCU8y!p2ym&hJ)J43)bXr4?|YuGw--w;GHjAv1U8`)8=Y zS>7OGO1#f26bU*#=O~+OM0kkcM)movUGzs1tl?hkeOZyPva@b|afJYW5dSO)tk1(H zbf2`%F{9jUYG~>&j7Pca4A8*hD0b{<>)6)o;O3X>hR;`>%wI_l5E)kW33@z2T^M+E z!RQ+04SF1Mmp%3;}4C@+1>!K8TI{X9|4e?gPyj(`=AN|GS7U1}t(5zyR+ zOZd!2d&sj<1GA^CG`|xvLrEzwTx~X3y3!v(?l|U$2gtJCL{*XzV*>LRe17+Y7H|vP z9I%)I!aN0y?dJs7npd+eJq>Y&Z{n}Y(cuXT`<_^*AjMKM9#lVf5_`y`=h0=mL*C&9 zf|isV#M5?ldFYo;;f6_NV+C8PV=q9D_XLLK2cKDzd&%RoDXZ`;POSNT1m? z!r6`wZXe3+QnY!Nz`7sByd@;QS4{>z9xt|9g^sJAA}%+nJG1duXoX~a9`>R&xjz~h z5Xc=Riy+@Iccx-Z4&JpNC>SOM<}>Qq4oRucHG;c}H%hXL_hSTmmGeBw(DQuXESONG zsWW0T*z`$hBPRDxQ(Cd7jJP>MDjxR?W%0L=(Co|?5zD?T@%rug&fJ=TA4NA8W z{SO&v6(N!YCbfi734%DDGe?N`bs-P5?{+&SFXWsC$shE_10m!)vNT>BM_A%yuGpQ| zse5g^!BX!68UfGRcYXJ`V%6JQ@@l=OuX0IVf_U9C!SP>wZp|`6k*%Yd$&;w zLU**7Xj3Frxof?blbE>=NXxq=C6nrjpx15QRtX?XK|qoFb}*n5k9e6SfrWuF zQ%1u!yAg!5u3StjF1{Q)bAquC1gJMq^!o8@stExUv9Pwkn~RYW1WF~=RGcPxaiPAn z_nW4{fL#Z6xsjw3Z4HrjCB^8qs@u(6NxA{nYaZcAI?$qYbpoy`%+)b~eI_NDuF26{ zg-E9k%Qbb6E`0ZZIJudW$R4Vr$M=bu;)SQM7dZUC99|h1SU(RC(#?M|Ob?;bS$fd0ARfAwi%{J0=&W2iq(1G9 zf}_X3UXB-+(gA((FC4K3C#*rt$}D2$d%pCdAJ=pbs?4BL1JOt&AM+nl-x8XnJf->Z zRn4LI-E&lb_0%m|Q5NC*QfAi4w#)9#WLHBMZv`Z8BjT&XY)}e}!tD<4nzce)d%xs? zh@t?z>X_;79J-;m4)ir)m!@-8r{kpHC!d(t-pxtP6t}@d+x7^ie&E_~;Usmz;x4OC zTS=ln4KeEWgx$+?WAwJCEefM4(fjXg*KdTF8^o)wu$iE;AT(rmO>kzWVd?uesfwbx; zX!lQVhJwkk_Pqw65JXR;xa#(%Hkxt@fO3*&49D763VID55U)Yj9|n==Jp7vwzDAT0S1sB!w3c$n)p( zCuF74_t|nW8&*YBLBtec5%fi<>84d}rao(&4joLYz+;I5j>E((G5AgdKt9*Z0m-9C zLXcIbm2h^J(b%UKtzYM0@g9>#p~vocL5@3i<6OblBv);J2mf10hNd#CI+dZ(@q8dg z716FB+xQ$VXabX!4KgBjGCRYKB2ZtTf%o6ho!H9Q@(c~!Jk92UsoQStdo!pbyY#jr zarIIHF^b>W+~~qW>-iWJpw3{Ed{kmGdU*+N1k|I>Jz!{{xuicGPzD--V@YF_1Dk2) z^V80K4$3HECx^29gSf;edT$=c!t?&g9y`3P%{%p0w}hKRj|96u8o|28>hDQM><=Iv zhln*YuK4pV9A4CTSMZ)-dBJlcp8ZJclY+fLZG zoP2I~t-kt)9lr@F8=IhxJI^cFy8|k{NmplYT`3lEL-SXZ+CN1&104pIFwggYE2-F)aHE(l_Hm$cUpM9K7hX9T>&va}Y_e!=*eUco_!`vKI;V@Q%=z zm+sM!8!t>|#yqO;CWglS``W}IZYdLwZ0({P%V8StH%IbHSwqp_Cf zi{?MDpOhR7zrC%SNNqC{!#4jrDp}rFPiAJQVf1I4vBk_c-{{M*)T-g!syYBK+9SPZ z_MvMxK;k^suqpqr>S-`@29vw&UYR#`4jpPK;G|?t<^tv-fSoeOJmWb|o$+fkVbsN9 zb{V_k@BCc*KBZ;?zl?9BjYkyKl^xm?z!5CNN0Mc(EWWw%^2caWBr+dAQO8~-a6-B! zm9;(jdRXBiQR@#BPG9qA7`0F!?+3EjeW2EeZ*?w>N)_rOWsyXMa}%rlzcg9TA^QUe zW{36tyN8ab?EI+lZ`7goCA|Qr3N;ZKg|8V-^r~C4b)S(YQ!cx?xd3QXTyW=vmKFNi z{Zg)tZfX0Igan$We=Wdk6SvPOGECp^AZ+JpL0c4*tcTt^4^z`KZnrgk~a%Mx4c zN)OHqrGqfnsabe4)?%M>v7|e~^1U5xuHZ&5Y$gax!=}Q+M!1`wRhcZ<*Vxotq-*mU z(8F&2;R@}vT3PBS(@2Jg0S-61J_q5g9HFs-EGCN0ENayFbuOQ_pHrm)C=sI3ypkBN z!%R>tRb^#R;lnz25hOUM$I=TqtA~4FGLtNmDy!O6Zj@^J*&hN|9deanux)EzFnbgW zx=qEo1#inCf7sG0B55>p9*(`|xS?jx`PIUaLC)1)4~1cz&45fJs<-w0jtlhn5n> zEu;UVB+mchblY?Myq>@6Ra%KPF-0;S@_v$`jU>(zRaf)(etpi;;STP;EWig&an9-Y zi9)|{`~5U2sUE0QB^R7|PZ-u1)4O_y-UL&xe?IgHBr#JeV~c?JTe*ji=Tr1!_{^r*G zxtM@=KKuik&`oeAsy&Z?a;Dg_X9~z<995q#g_zbIKNQ&bH-$6vQg z4PYW^X8Db;%x6Ym_xZ!HES&^BXYu2#VhM#mH@lSB~kJi+6E zg(@YE0u*lW{*cPYl?#Pt8xgPSt<>LQ-suc=;=4jemPGN|lK`+qy%`fyH1RH7RMV?F z2EJ+|PI;lkKF60;h9@~#*O)O)BZWgU>Mj;gAdR~bL8ERW02tng+k#fMPaLl*%Ky4F) zvN528L{6;aOX9sE|9oFerkNCzk~yt^Q(a@{2!)kS^o2*gKzhycCpn3|>W&U}xJ+o{ zIE7Elx#vO3%`R#oKfuXn@}%K9)+8$N@J49)UV9kF-KTPJ<7Udx~x_ zcI-dbWuMTwb7R{HkE_tfk&7SSJ%K$XpuhRQt*8wQdt&7silyFI@^phOvR(W=TLAzg zP&Lg`eIE2bw%U1B6?$o`tnc8cU#uAmo}D(dW8_*kIxW~2ba&Ds_gKAOFEtz-sKF8? z1M!Wk|Z{M%P9I9Vo5#o4B4${-GMPLWJb0}eAf zacnP!A;XQPLd$KmVK}{8M4_gfe1(L~1ACtn+->0oat_{u-4e;Mk#egPHGxV(LUap= zLFwAWww}42>aMQ(w9sOadJHLw?p;FXE_AJmc>su_X3>C&6A(_E`B=<8mY#C^zO4!R z&R6sZ;a>ABbR`03E0fQkNUDZT*y07Ky`Urc0pkx=K>ps@#NFWd;_ z3Rc*GE;j-4`hMr49)J5US5JZmRXe0s`03b+nIpG%R;Rl&L{SBCa7Dz0aGEEwpNGsm z_cdZ_-Y`B?>)g3*uTVvu(-3u)ss^qmAt|T=K~G)~C*(%+@Lvg$Mssaex3~6 z9Zs)XG}GCHVtSK+uJ@4bQv**Mm()HGE&YX0n#FI-)0WP}=-yB0ou}X9Up~Wt#R-VN zVb~Jj11UaPdrgrPK581t2lf5x+Uy?4bLD|Lj(&XmHM=8rhKUA{K?Onr+;$NIUmQ$6R!x%i_R!cw8<|v-UYoc20;fgP^!t^Y^jqO zsL7&e)Gi~S4y=Z?dew?hl~;*~kWNT>75kPc*?N3lXUunrSQ@bWSu3peSR!&9O5WV1 z#PS!RANA@A{CSjkQ}7iCqaWfO86|%MjHRUE;p&QVY!$@1?%K7P>!?r&4)>(9IlNK33vJBR2|_dbMV@7SLV%2$ zweoL)1wvg8`qd+J>WVeCTStvY7kcR!Q)|11orS&{Q{0I6atv2~%m5~aU=8A`ev4Jg9~@ip8N8B3 z6Im8Vz6|x~K-SN?Mw4rAP)x;k|9Qd^g$2QfLGa=wuyxuvi=Vz)(>X3*`x_f>p|(6k z)|yveK)JR;a7kLmSkkYnj|$;!T)iU;TzAR=b4hFEMW1lC+e!#i%rzO65_y}BL6xBT ziiJIGHabSls{7-Ty;3sA9YXhyWAZ|SF{9*nu^E^>laezWPR_TDjk9k0T_bJe7vmVz z!9OKaCP>Y5OlA_4)x%4BWp-{?Q_5z%Q{!JI^bk>ej;Q0xmnU4IEII)bw0t)ItEhf& z<_3+WA)#Xd%ympCz4sneWk$By*cr$m&vk$@_0UTBQ!;jQ%B)c7m@ARz=uob9D`~Mx z!(-QYd8+76nWBoV(z-@GADYMPuDl@!T@Xby)QJwxFfPJ2Ko8KK4CHuafJ^28MYo^2 zhwrY87)3vN7Z~*)Qp^ zMV*e^BoUpAvBS4t*I8VbN}UrDipOn1?TWpx@CTeSD|DTXXEHM(_(90a*0M4ieVJH- z5UVu}@(K7)jN7dSck5C3j6{(gP*y%}#d$)i;1-}^S(8oFcg>4`nci6|ij=f8dfCD+ zuPFzOxBQ{D(JuG;+I!DF0Sh|9%o>BxUyX9i?GZrDivny2O>L&3R1@Vhgbg**FQ>TG z^qudcr{u_#HOa85Q&N69LXP436Wk`HKf3bq7cx`>uskAYzg7C!?#@yDlZ%<}} zsb*lDa)5w6o;;N*tkRdK(_{kvO;|gW3 zD|O$}s!i#<2k?UL*p?Ec23^ z#`<%VaR^$M0aPIU( zD9HaBVzT1L!wWdiSA+$$AisQ698xX%Z>U$95j(YbRl=%-CgdcrMBIuT&QIihy~XyD zua+>W-S>k+q){M=F zA%u3=Pcfx2O~0E~P?|KMrt=-iSMrt1hm9r^BLJA}M(}`h)%jL6{bZy{6(-32coh_@ z;Ol1x63=|vIj;92(e;8+eo8$;9T&cp<_rkSRGUN6Z_OchDQGOVUS?ye-q}6aPAfhO zmE~@qN3YH(jg2Wdp#PqqMz0C_s+B_gW2Y#gUfd8@DMkn%YI7m2h~?qwCeF z*p!rl>j>lAPEfm`$2*!Txx3ECJ)RpOfP?a7xrgQ=w){NB2O&)3N|!XOitFF0+qZ3M z+Ui8!>Nv%#P>L?b81|rA=xYCWRhkRqsI#I^ct)RRp{UCVkKJCrIgu0UCVevy$fM9X zy{=PO!)r?m6^FnatCEhm2a$l1f{xHo*ykt?n(lbR?RucxC_i3v^tu zYV^P4=->Y(N7+~y|4)woY8nYtWV_ zw!p28u3ocG`Qm^eq(p(j{DBCk2ycO)#o}St7x6&k+1#4IIe5*QS=@lk1?dKe*zbN} z28gV$PESe4XKybqCQNV7Cm~!KkWGw%-2->5fpLh`;-etPVeY#O{3AO^&gW*=0U zTlLrYPBP`K*yWrsk@hwAfm*=?wDWkj0jflp{Z5YlrQIm}E6`x?OX?SLpcwsnO~Cf` z4qy1U40n40{qcO+GjwzXv9hN~c8tRi`tYtmz{(~m>)YMi>OiMRRePbQ2(|NOeolx^ z^+6pYO#_4}S)j&b%fP2|@INcLRVA?N6Ei6@=-2mMBCmS}xn;DV4T*uAoPk8PwdH+R zNf2HBw0UEN-)`=7b`z4})3Pl%OqRUME}`=^Hp zk1y|ShkeM@kdXZ_))x@w}#SX^Ka|I|4>yx%$L-=UM= zxc$DOuf4xNec%(FTz|g`Oy7ile-*O00`a?l|G1x4cWp8MrWDL+0ln)f>CESGRlzla zbaDOEsqW5vc_5M!u_5@yAzxF&zJh2}1nkh-{E|-PIo|9^odSjWFXz|p@3q1Br+`9z z5$-&hYHafD;nmJXe+hwm>Sq7SQh_!EYku!C$`a`McSJ>Xuv6b>h-Y(i0};lasWgLl zd=D4^ap8hpzFvd4+sfLXk zJ~NC%>$m;jK5|1gLU?@@oxRtDc6NPD1wi&A{9qnK)^mMaR$8F%hcZPn zLE)j}{_$|PC}!r-vto|yDgpYVW!F9|sw7)(vx8zGct=6v%NpR45J@%ucD$}6kvZav zkZM&5&bs;at#?~%z`fz)6n4g1C6)6+iiBE( zMs^}Lg0v#Gy^Msg1_E?Jb@)zHRZd(o%e0$l@6|0KP!y_ALrmgtJ|>3w+l8l~QZx5IaXErJPl z4@+DZY|dqGbQ~dXb(uVBwqo0A?Zeg*CcH{Hd+?;uW>Ze{co)tV0qZa0*SJ;g$rNT@ zP(J=Y>XGxcY^tf>s&kL^jcaQFQ9!Q0)msFp-jjtY%pwL0Vb-nFY~>~~;)7mcGx{!K z)O7$L68dR%-f#KmD6-&YA!hOm&iqu4xlwOp-J@`7iML?|dZMs}JZHlih@>~Av@d9r0gP@m9*9C-69VL?3woH<0ynZL7XgihbwaQ zaaz&U&UY_Ce$*&mH8zUoKS-giE5%O1qP{zAm3(?#f%tXzB3Co5GYB48o51+XE-al3 z4EMB>$1{*dk#qJ^N(g7rqCMtuWD?N43Xb1vK?-f@!~{;&g`f=J_x2wI_Fu!e359eG zf1<-;(8JZB`ueP@&;<@eXZUhn!1AW!`+PM#GbSSH7O`!8sf|k8BbPccas~55yxSkoU0}!T?8ok@V}WSwSDE>h(j$4=W|txJn*#UjrIfKo6fc zD?q<%;rxf1oK5W0iW!sqf>5V>+tO zHBe2O>19H&J~d-TL?ma|kb?YL1RmQH%N+E!pvlJCJCY>JgUvsBH{o7><;Bev;kE*g zhVo2hxjrV88y1~or9d|D3dA)jx7>$FTe$-HmwWGXTxYZrq7FgIEwuL%KeO1!lEs^B zgt4wV0zH0g1cV?TOpA@zv+qKd35s#s8)ZlKI(raAhzKaY4@{%Y_A2Odv!={uPK=9b zeS)KzsD%vPj0uA^bVn>158D}DGKI2*RaMa7Z?vO!+ba*u@zOthe0yd2jc_o1cAQI@ zISnLb3vVu=K$#25jq}M$rkj-2^s1~n*xgC?$73F^Z;r!l_?S^pr`C_Efl700*yqIj zr06$Mthc6x5>0ryFjVRy?n9?Woaz3BkG{K(*j-*y^**0@K5mjRI>`EhnJ|E&lqqrY zalgj03u)D6V~9y4{{!JkyaSv>^(aAh9vrCF%)YfiRVOj!okFiN?eqwJH1fXt$>G51 z0}x}VlIlpgJ{HNW7=nFk1V)*8em|CK7q;GnD8J|yUkFy(dj0822Y2;%tn&}uE*NW~ zB%i{d|ES79y$2!TJ+7p#m|)d84}^d9el){C($_O`%O7Fd5TV$ zc|2=J8yfgsuBw|an90(46l&&ex<)Z-xRaxr_nPi0P#)<}%!z#>ng$-T{f4chdbW#E{3*33Q1q0}`C@%;LdNJ_{|rP8 z?@HJeNvO;P#)MZLm=uf8DT9gcZ*L)(N?DeC!*4F%e5F2kKY=nD59Tre`V|Fv0XuvKwGe`xLipBasUpN09*C z4ZL;Si?eZVX_}FPbHHGZyz-?lR9pCIN0y{v!E)iv!`R}mUEV0%Xv2S1Y-he)c-s14^KnV-q*BD66T+%jXWO}m&n!-Szq+& z^~0Nr7)Y7G22~3zbIR^pha~iyT)_G?Bpxt|2v*#=FG>%=0OK`J(WFI;Kb)Wusw*RR z3cbie#Eu6QHH(IquWGvCq~lY5`>kqRbxe@XTj@-dFRap?1T}M=!aJq5oJCNj(6i8o zA~0STt$TSNfy74An6e-3c*oz4n8Q^R$@(wW<=H|FqiU07TD}{SmGC+y18#6vL-Axa zhcNPQyfU|4rrilR9cOmqT&Jm;6$HS(bdR#r$9?^4vg@&Wfu!Z*)?!9?ouZg_ajC)Q z8JE_`bjnWgA6tkL+s2~fp1B2!b`R^4ymF{Fe-8u;SI#2jnWGh=cQ1^(0aF(8~a%yRgPN`EB_SC0(3Bu zaftVh{qB=-*(|sS>}^wZyoT%d$gwUo>TA{)ddr zj96PJOIk61P+I9yTfuG5**!o6aH|C#=<!3Gcw$YgU%7Xzw_~BA2dxkvV zAOc=E7!P{$z*3MyAS?>RqOEVfd!v<0PhUn8`3gk_k(jO4F}ZLM6byBCp*G>{f{a6C z{~aJlzNXZCn7LvXlIf*<6wr^#|8y!5E3lJG#c^5UT8-~E|{-`RtPcE4Yl_fJ}%tUS28m-R?uqp;k-ue2@GvlFqt32?t^znL^@_veR@{{uloE- zf2peC;?-pqWuahfKgk1UH#56>W~vc=omWVpe|{rH z2^&cMpKhsFI8(Im>Sz&dzX6%d?e-rQL=fnLDdRb~HSXt+Y9A=WK2IR3*-kZq&7z+C;7OBH5GM^9vXmS^M zD|4J=wR~xphYTFeeH(w5XNt)#P}}lGy-`UZ#BiMHtF)&3k<{Ek)7rRB?|PZg;5#acc4O>dgJ zk7|V(rIY<^$!+NcA5144A~se|=8o4vaIRj_-d|{qdW!|dY&a7(p69SH-4Q8Ca;1iC zSt{fVNyPDBV5Z#5U*O02pWR&Lc&74j2ACmF)35$v zdui?TzBNDm>sqkiHn z@=}-SIj0`R*%v5RvjESp&6P`F1jwKiGt{K?PLp-9i%|fl&!-nz=b1D_kbdZt>qbUl z%7)HT6;!@welX0&b1rHPg9F`@;Fg`KwifIi3K8k}zlmaQYRj zjYM$rx;~l-i4(UYZWPj|3ibvLfE1TOe0k!P-XvswBgW}vp3=R84m<_AhM~ebH;^jo z9C$+Y8F6+%#}Qu43PKt(PulMOB~duh@}t1jUZqe0xuA&+Gl1;gm4y7tAhC_KFdnml z-tRBt3ovR|#>LT*f@EwB4 z<*tI&^?fjyvf!DZ8T&Hzjb-@nk5=*vv@Lz!)JZ_X7J$_1?eFLpS9o?Y!H)_y0 zDYX6|XR#TMn2Th!CGHva>z*u@TpkOih7$$%Pb&;KM~0p+_7F@9->cRJ5+VWORnF+U0zj$+0{}`K%-jo{W+X4Lpkke(se=8s`}D-0ZK9-H(;P&=^9{xF^Q9EHEpbVQ>Go+3Y| z2?sN-eBZ?1#V)un%)m+m%ki zc_`^whbBn=j%WQPum?CJ5xH#lEC$$Zrfg41YZ5!wD7=Lqi_d4aMXLDZjw7-He|}FC z61fku(_=0YVWW}XeYVqjp8r-r0aSsQ0e{qT*N}zu6(2SvDqZiN!ctR;mjhvw+up^4 zQQoi*sy@eXP^a^l!98^TI>HLr_!=E&bs?&iTefR>k#TeItQ1eJkLb|TpdpAgcP&48 z4)aN8P$+x>N0AcSGY4|kiG8Zr9Z!NP2$b$stEV1klo3G-;Z-$Xzj&EwgPr zhKz1GT7reBJ9u*J;p-5IB_1c6!k0ydiQKHp1yo+kkGBTy}#gerhcP-PqB@h0|1r@_d;A!n84lO7OLC+fGC*oBhF0U+TGu zs*d-k5H}KHv8GUI12%uXKtuJj#rv}@`TS=08`9-X<DLR1hsD`{;wjbuW z5lXKQb*NxHv3d|v3t6ZeQruC~que~`kV5k%?Lgn2l(*1tAiPq%MO%qcb$jl&q|KS{DT0HeN)CcCJMeB> zvj3M$(o%xGuA0gkh$*R%XMDaeJYC5dT7)XXeQ_@3!s8ohc^o8ds`=U^!sXh(lf5Z( zJuJ-XR_%UgV6#2d%y_`pOO_xr#C+1hG^St0$mp?i@>@mK)RP+USl@sU$1u}pI~Xki zFl~Wp1eNnFZiVBYnQPJOh4`hRQo6mBZ*DItv$!^-pa(ZezyDW?Zs6D`=d&Q?zZ3*T zl64t>-IJMkR2y(TVHhBjYkPv3899Ya+u`Zi0$*4J%|TNaQZHZW;sWD8_N7hD`^Aq0cT^YXfIFf=N?+^&CNz@>Q7dl@ zZ3{mWKi87ZZ#0UFc@2SfzM_{++(%7e6-7~(g+#FL)!Huq&Yi-d+6s=#YI9ZC#Z}SY zP5uZHc_MN2|4tEn)0Q_Z)zPe4Md7xzk#X$OWPAIW!BUOg`qcfT$+MHL{vHRrL<)}} zL?Q7j=F3PY? z@3MIfb9Qv@MyQ`{w6abVnE}rzhpA(JJ{zG*G!3MHe3PRb;Y7dTInr^76Y| zUL48q6u|`J8QmzQBF~qm3Lk((kDDAuZA@2yP|MA*<`o66N%c`tQ%)nTNnAzr2FlKA zpP&ZHEC$%tR8}8Dj^&e;g#?B$~~XVESYYT6eYRk5W2>(w*6z?#|A;AKRL}F0Oe&ZdTP&47XhzqFO z+4yqg@qz0@V;n`myFBWZ_HKXj<5jT=6D=cf0xEyCc?T!_dV1nOUy3^ixeyt96U2W6C_8)R@kC!;*CX(tW9nQH|MX23C&HXg(Xy&;gYZJG0dBF56GM>_>e( z^wZy*H~$_3c!F5wHapT?@o0XEd&;Z^u4{WXQaTc1Fz{Vl9%P9?GIrTLLny1b=_=e^ z+i8bY+jT4#@NDtXewN5Cmp3G^Pg%j*6{9)2BvI&Dk@yK1qvb7fEwqd+*Gc8Y&~CTb zJTT)niR%W;9lD1+xNH%aj)7sS7>~LItY*X??k;QV;WdTL^V?dp*#+TCb@Tz4M9Av1 zw1qE*t5jW3V;I{RMn9vxudSs?X9n4XKL$hIK0YF=n>Kdyyo3Yb^)ny2 zW|<)5hE%5A8Ye9}?y5)mAqQ-oxB(sR*7h&2`3J~^ordX%c~=I{lS<AQPOf*1Z+$%m8n{q~5`1_r#U!<|;GZNG?O)L6vPqlDvx?;@Q)>d% zdSt;>910cOjZ|SYf=1^<7iQy|c=3Fdy0OH(XQOPt^4UB`P?l>}zJO6A&WueL5z?%) zIz+;ngFY*qo)uM~v$7MZcicL=>hjME-VPIz>=k_{NaB=eMXh~gxH}#k9*D5`v@<*5 z7j$Xu6yH7DM$HT&O-qzj1f__26anzH zGjxJg1n>8=&JT%D1ap}JDeIZ{dxqu5w89NN`5&jmGtF~~O2R?e?95mwvwcO<@hlHr zj$-3uF>13MqWQns{QOJz(b4foO^6rrnG`1vGg>1kWkv^%5_hY4*eWGGRnOR5z!*)4 zzn4;84JdamRS-i1t<~1XK{~@!kGk<$`0l+6r&wJCVa7^oO>w86`CB8Li3@K~J`F2w)K{OOPE}@#ucL6K ze%YgIExw4TW3|p7&4o&Hd$fX3=cQQVSQ{P^a@xPF?Yd!in1g?M z&q!K07X_?>5U_*j&JyqX5o3zBxdo>8b_@A8P9RS_H%93VJ6}>w?O{y>R;-xAD~`+@ zLJyvg)Q{&KZ!q#BX|<&9lGm@?1G_?>ap73|!xx1hAi)@aUG$-F1@DF~=vzm=Jei8{ zyIyhejD{Rne6(Ckodvo#N3*FU@X4?MzglYcRPokvyE6JBk%Yfe#$s)tscRel zMySsJ*z-6&@$|bhosEW*7-I0a^y|R{l{qyXW4xSwoY=y;93|v-#Ys`7m_9Nl| z2C3_LsynrA5!3Kx%ibjsA5=@q^#C=aMtnUu9d+q`d#mE9FwYzYox%8D@XBRf;Zp40-5(0BD%jXZ*vJ z3C0kU{6WrM27T6nwp{PdSBIH#gAAuegM=^$p{J<4tv=A!Ga2y~5h{@jlO$3jRKzYa z9a<;0LUN=wtv+A-;HI;$MLNEI>V0g9GkW&ycyb;c_$+DgXZKaGPUCb4+x>TsVk4ms zrgPd-d0hoJK)@l1##vA*+Ai$Lm~wkM`)dWrleHzEF3$MI?mivUPJ`2%M(B#<=DyIQtHKP7l)badIXD3*&h4uiN zzJSCia|!LacD0kQe#+Zh?!@`-eLXgGKF)TQ0)E5%_{IlM?c17`?s* zCv7O1j5c-^qIKt05^N3c(%ay;7Rp}n*-D)c*=I_45(I6P^4x%a$R(dlTC-*A+yd=S z>X8hXZYI`iq{xyemu!c&iuzt&A(`gOl*%SZC95~MwSVOzSdi?4;HnoT1-<`47c(wIJ*Qu@gvc5WV3tX_pik-96?+?$oTLy?=Dz2MuhMF@6I9X2sw3 zbnmX6?$rS4$60}pcEq?4mzIvLYNpOE(sEuMe)uzd4V}6)N3NtNqz9HPt>kEm))P{d zBHP@xXWTej$mkretu9@4v0^XE|tNxv&SqULW*ktfzFK- z8$M+Qoh7GP2Ts}+0U0s;_3 zxFl4#1W*8g0D=6WUaUR4gg?v{Sfj$V~7IAx28}>3@pm5$}9jX6PC^P{9 zA>rr~Hx9uajBC)QzdnEr0uqp`Xr4K+BhdMPO}>8d8=s=Rq)@y#x`eN7i3f(^BIOca3Bd;KvKQBn+yU}XU79o#-tv9YIS39e{3C!s4*#lo*j`8wz~mzr077S9U#HM| zyMRtS1TV<)g<9Bm-(o(!2Bhg1#O*aqC?R5S-=$J;XMoLo+wIJsS9L5yJNfk6+$J1U zQ_I&%U`-cvA0W)-C7i0t_Y|-V(f5`qyeN>cKYxCDI|slu6oA(T|GOXQ+M6rj9~h`_ z#=^_jS7*Ntfa^J0|F1kZ!>7o36o7|6pg6l*eczoQ_-_X>3If2opiMl0RXq=i=)0S1 zGp6;MSzK;6_A<0SlrXX!0KoUlx0gxmX(}zw>G4n7*WJM*cKW$FhPmUP?fYJQAka4S z{uDI;$URgfz=Ad?6!7po^q-&U0+_&GCD6NDHPtj6T>QHd&1dSbO8uDL(%H8ZhPJ=o zsV9&aGaAhPchA9k0Y$p;_AZ{7<<7dPjRD5u}RA22AVP)_&X z;DWb{SYbyUq!=~8YhJDBKA+qHeAou}&lgewgh@I2x+->LbHI1O zB!1TbeE_F`fxZm~(C1YU5I;P`J3fY{3R~=X92n5d*d77~ZKfYy1xQoC)~{k(emDid z@hR9NnQ$CN4>I68RKAHh^y}x0J^&v+OssGi!2Hbz@b$iJaNj8<4amNmgQM7YBhoK% zQ%qMDwx(Jic^vPn-q#)xSWr(tTQGByJ$8kB*ya4`lcWcj-0q7(2EFx_XoYrnFS9M} zEd^3HNp0lxK53iMN3mRv@spKgHo0TdTdlVn*6vXVE7>`V&gV~a^b)R)9Z~kL^bT`k zAFy~!#H9WV3SM??bwP=wlH_v<93tONL6WFw8OtXtk}a4O*Ks%W|3>_2ye;7LDFwVO2=ZKumV68x+%u8E5j0 zk3+deXxCj|%aUPJ;^>u|*qB4VlRuu-jK7g9?GMiLD#d~b&P|)XN1`*JI+{G^Ap4z( zw`Pa^jkaMO>erbz{XhJr{)$w#{yUrw%0oY#BKVQkFM zqVnK!BabYJ)n0IkU~QD8trivOtCK5(DkdLRUg9tatAXldH$g`=k)QKsw8K&i5s{Gd zuK0tM5<2<~J(Wy+8O&GjB6MW(2xC%7YcDS2x}SQRFtQ8s)0%)0gji~YvriJ)6s_au zkINw_gI~9!0QRDUAs>#xj=aV@RiazhSxOHcUq}hRsfyuHJCfI&!wmnPh%__J18n8) zzHS_SXqA7U7vYF=N)Be61T>%KH(`c*nA@7XdH9uE250+rBz#mX&?WffLp6TmqperG zdJiIL22#htyZ$so$=$+FmzO>YO5Wk0n7>xUTn`xR+3RMjq@bPNa$rBO!Xe^|Vc$yX zDHOwHlq$*JyW-Ie|F$xopTcs6zizrTRg~JeXC}LC;@1lJczm(A^UZO7^gp~&RCsz+ z#cz+d>u{3qR4|(m)Us{|axd&sStPkNh-lHpFVANe45m5~B|v?`+M1M5hL8`t9!&JH zvdX3hc4S3JuEH)!u(x|XZg?1ChSwMDS+qnQ&aj5G9rXtfR{c@>^t(qucC#NFN?c9% zQ;b@s8y4H}Jyvf`oGQog@jUE-YVi=NmoCBESfM-RD?@VS%`Y_fRHI@BYUlhr{8^up{8Sz|1*naMd^7@pl`=DNBmLB zP9274PmAVmw3X9(->YLzdp8~Jg1^A1ep#njzuPOq!ApNbdj(CrVC)?9Q8qv0&fvjw+V6}F?*&SNqz;uxk3*e@pN7&$N@OgQ7vfcnsY`!^vC@ctT23&4OEsjNABEcue5Z6N|F%4`d3p zPDU_69|NrRvbqnFc-6)EBI5!pbAy#lxgE8MrXl1vEos`#AaBp$g#(q3y0sJ3E(!tF zb*33i;nLZ!9&_u?#pza{8(~Sn@uZ~AEO(|=YN|59Wb|{8X;UG69K!Yz)l!=3GCo=2 z)L?+6A36e$6Mr+4I!|THp2SyUJ>Xsh_j3QWYghV(i!U@IbBUmxBb`ot7sJ7~$J4-c zS3Cpa#yXt3F&@lU>(+Aia+vA*y&Y6B24Au?4)xrq>&8C=mk;oflHWk%sfn-yzi2&e z2IqAR;{*z9 zzq?+=!d?p(D$*(j5KjXMyRy@}=d7NEY&b80r?gzPX5|V9YI+dAJbqG5>Bbbj!n>T{ zy>m8N$f*EHxDXl8WkRY$`Y>kBX)T}LbgP&=5gy*eM_gAczyVfAem%g-f|8El^W1Rf ze6~t0LlJQyhV_Lvlm2%3A723?tv(;!#OnZD3SKZ{?XIi_e2Yho&`2gz%I{#Tt44M%U{xoXrF_yNhC zukoD0au1UwLIe>t9D@m+siP2=9zqr4S}?rGOs=p@q=`k7b%|?q2Q1&Cl;0Y^plOl? z|HJbH4c^|86NJ`H$RP!&Aw*JThVtW+xC@Qh#!jtD%;}}zr&w4{`l|0nNTX+S=}*(! zkhJWf(ufr`cKsN+2=N*LO)tP9;|0g<+A0eq_n?}#*Dcp&ZSWi&4POIJo>l!wyc3{K;I3sxV2(^@mvI`x#E^}jw>4dxFdHixoT&s)q7lBXwr~Ni!As7x zHm9$^$?9ZduL`wx@_G&lO{lnkmw&IGK-QnAw6u?}gxI*me znTH~_rQZGiY;BH-txLH({8r-LhWIv>_g#edV(gJA3x-Qqcm7E`o*-dK${6L{0x33v zDDy0p0;%&GG%IbLG4*T01a#g*?|%~$?Ye3=@61#Y{^P#0C$p@I;j+Q)=eH*?#h62f z4sLN1*pFOS%}k}3hRYpj$fm7bMLlE9VUF)_R#To2C<)qkC=G;+Ci@d(v)o_o&2(Rx z51blfU>gc--$q`AdA=MsReEFXx4+rH)tgR(@*VtV--+xhpVm&~qJ!_h+oZO`??zV< z`^#;@gv$Dxf~u=emTTn@tNg&-@MqMVvrc-T4imgi!rIY=*4dssy?fs2*jo9NDw9v$ zK8Xx?A2>-X^9I!Q-@k9W6f3_Z)q(!>!QViSL&w@@ikQlKI(*_xTuKNX(+?Xw8ocgi z#7o-uAO~&?OpLqnO6QwQ0#ZR4-kXmOtPPWgNoSY$t)EL=on)i{8mq0}#HP1WSuz@R zu09iG0|AIxKtGEfo#h$+jXsILjoN34i69cm1BnJiCRT~A$OXNSZ?mzSeUsO)r5-(C zVp47FxtTVlad|aK(%uevwJYq0FGbhhdFY>SXpjVjd;xnH+zc z1?3CEH5>RRv^*~nHgbx|8m(iKn@J#0@Ly64!WVIv=i1=McxhNKoK9TAnJ+T z*gM7tOqCQ!9tt`%!Q6+R^vrO|JzhU+v5Ti74dQ+!QuUX@cT*6_p|~xL^1Dgb2UM~- zmGfe5#2_7ClLo~a3~vot6~@(7ziB=*Zj!3l=xBZ)H5zx)^UaN`{BYK^y~ThX;*XqX z;s#gD<=H}00mQMcN};*J&b{hY=FGAmf@G39kCOgh*Db=O^%tT7nYBu|rkSdP4O4B| z%E9K%i#m26*clf$W8a0D4aekR-cm}l!@-qjceE1=n#{m&Yek& zM4)KopW0RW3~Uq4@G!5gs_0Bf~OXdM%mYfDPd`V6lYi71C0jjqFcer~Do|IYRh{w6<$A zdSnVL!iLxgaNa^{ZO&nLfwoM{z~UFnANR6b!k1BG>!!c(fDvRz zsi`a2qPmXIiePINxiVe10-)8}31d#kzD-o}R_D9fT1{Br;DvQpVPDIPO2m_2f%r8w zR0JvS2#_0nv4ObN3bT!O0lR0;CRD5`z|bMirBxH?P@tv&c);=xn=DU8T>* zGXF|kd*WA)>_R3MGmP%ts{xL8FQ)6020OFT*`Gy@prCMBAA%-@Vw_$sp|EC$-M4VF zvaQDL!&mIiJ%WL9Tei%naydud=BRrKZ_yY|^9dHc+^^ zUjwE)eTEt(Ltr*~Z-Y0Cu0p>OT7S385@52kY9^&NywWZoa6iW~W|+4i!r5bH-!oh$ zO=YKTI~vc%wC_tE$oQ>78N1nh^}SrrYTQ^Zo&wcB%xub*jL!&v!wTg-(aI^M3SC-s z4M8W7YfeAZQ?ydd{blxKq<~m!kn0<5#UC`4k+FIEd-;edbuuxpmVDFi4f4dIZXcf% zX{dS(dgP`MV7+$*_MeQ(Fcd3%X zF4nKm2JK_>BraT5(b6nZDoMLjW)!=J%gk28q^fY>%IS~$LuRcz$<`qvR}*AqvrjUvoSA}ieqs*d(hOtw1w)3uUFFuDI^8avwbgPK zc&_50Vp0YuX<@#r<8H>T%jl(`clAKG@l7;fE6oS$@Dj<} zhM+fLv%OXDdCm>;>o~h_qhzA(-aP!y98g-nua@8`b6)Z`NG0D41+BK2i9U<1?=g56 z|MM70!{We*o3VJ)a#;&$++87z@{9wWf9wiMr*G5ej5^*(;a#R>>T@Qe-XU6Mj(L|| zB23e*LLb{_rN^|7;(zXIoMiN@o+CZpyLL+7+cu7i4Non&qZu}y37T}@#^P{N63$47 zB25j&k`4fIZbMOE*w#OYWoI1JjXY3JtyS_-2_h{*SNA*XWy|?5CpG1@9CdgC*Ov?J z;u0FJgrG>v!P(ZS-BDVapH@FJbP1aI>~1*HgE#jSGWt1T4lYt{Kij?@#TIksb^N+Q z$YjqyXywU&eOysD2!2=&F~b<`$wus4z)^1BHDz$M4S^+@hknIrsc0DEDN|2l6GuL* z;|ouMWs%khcW+5m{+@2_eI?&DKA`0aWJaGv$#g$h7F<@gRHu_=i_c|*^Y1en<(UMy zPBJR`{*cON%MK9>G+YNQA5_UCs$97-lLglK;F09k=wjEm(BqCAE49+PaBy!H8(Rd) z>oDXn7_@;M1yzj~9#$%vex^4iI&Fe^I;s@ybM@J7K~uF+)x{&rdpl_{Wnt}&QSQQX z*oAwpv*u(vR{1VU8mRBadBK`EZXOX;l||6P=5)Bt&6Nt8l)nGk=s0D{zQjf;zIGmH=%#Xj66>`JS|jC^>-3v!OTuE zg`szqCwF`qRbluvv_@x4=z>UMclnm$ z#)<)aSgySZ1~%az?N17*Z0fa*sv+x1lFf#J-DfMWxX>BF<{vcjsSg;d_aWi}j=pIk z{rg+)^kLanJZBQoRDTj^HBLpwVSn?)ep{p*E6f4*&0){cZ%(GFpvK*RE}^G?3q|5y zJvEAz-4{g}nsnvX-WK-w$~{GkzVAAfS#|8E)=U|5d1-43>mmUF_h8Lc(6zWxsj2v^ z;bKLvDWWvK27wbv2sjC%rKbCLCrbqA)^4&yz+C2DEelv3gFOg1-l_6wN4Wb5Y{;>x zN@ziB{esfN1IhS%EggxVXOZKqVcp$bt=SOE!Yz|pVMwnsAXJk!Z8&vee4duD76-#~5EmOv+TyoI6oXrbwRtVg-EF6}ky%$}H zkK>$mRNuFaV!+|4^dPd$;Uc(J{`h;<0*M&v#lVR!a&QAn%iwV+>tcE3?d8{L_r8L@ zJ=yPGxUU%s&0RnXlXTQSTRSB&ho3vovdYAm%2HT`&pjNsjy)`#XO>RG)RLFPu6_xT zSF-!!Gp{zRpAOKhTYq1)kYu1dcywBRmXXI0jE0iw2u!T(aO-av*U0CH2%y) zt$g%JmdlZHavy&ttxH5W>HooyQRg!+O^$?TE!fdD^L; zi(jQi1eqvaVYfGLtF7F?Hx6$#&@wpnQBAx5#TlNM3zdreU`eFqvAU+&XHvfb_cn5= zI~)345E*8a=g|3!BWOGHFxyVu+<$rFfZCVar8BkU!zVdwGB!xuVN=N}6L`5o^5%N7 zAX(Vuz5Kk)cma|&2E^0ffJ_lpe^3Qlseaa7s)c)d^k8OLJ>UgZHpR`dX_&a2mba6V z>Rc(18v6Z2HjT%UhoqkpWd;dB8TEytow60TiaET!+fxh>my5ySbMi0dl6K&%v`!Is z&y$y|x!n5Qr9Oz-yBB20m@Er0fdYx^1&!S;x^(5hq7b2Yn182|MX;a;M`VJDTdJaS zOGUxdQSp32yhUxO+r^S0iVC&=!ch-j48~&8CW!eC86>prlq(mdA;zfma5&;-EG65K zj809t_(mIu7d!68_$lF^nWFYHJSKBtIQ51+m&OpDR04Fp5ED5l%r})?7e& zvocY-HwJc7Z-SE3VW8_W`4OBm@Lko_+7-tHcai})isd%7VWZ+PRT5$&x)oTA;2LWi}>X+An&cA{R{Q+J5gzkrZZ{h?1 z)D8HfeIx%;BZRniW163ZyFG*o_7KpA1Mq@CftgoXITR^S*ajegd4`4vCOSy>&Id;T z0^KwO=+goRQbv6StM7cohZz-lkDCLG1`5A_dWw<= z2ky58VAz~ZxLX4f)vev%69~|=ordw{t+iM4=P`w;#~;Rv{vdo7_|IwqKpDinK6}aw z@Y4zg0s=r7h6Ak!<>uE9{!<=nA9(fKNPiOJpZ6#K#`uH;c=z-9eVafUVnDzFd-(+a z{R;nhj((P2k?Hu2{_YbH5#hkvA0noL*hfS;0Ed8pfC3z5fcO2YEdmezzBuA@TrqgH z_aD}~F?_waOXc~s_y39G=K%PtEdd^5K!?%)3p+>qCi-?wrw{nE>->}b=xg{^OZg*T z`0FB8aie&4%l@J7|3d`tdjL%=xgEMlOV4>f8A__8a9e5b##3=%+aOZ$thEb_-- zrRc|Dh(1l&M@9RYf)%I~oY#-X12H*!N4NNWSK#$g>r0(^(i7ls5X`p*~FKL#CC z#6Sr!9qtR@#SwVF)&Zs8J_7!G@?``C{EqX-2nqt|UHfNXSQzdnEg)UHX7{AWv(Z6} zfZ_9d3Rim2ce9rUh!G98g*Yoaa8n$ZO%;?Ww@11CaIArihC+711N9eb*H|fd*HDX} zPvYZBlZ?$+G|&6MdT1SCsC9qilG8*9+t;*GXeSv=GGp!XMC-LhzuilW!gRJJPU|*M zmuX{j$^cS8t-mJ75Dz@#+au6-#{S^|r)k?Z)H3lrBen>CHRYSkb$isXa(jb|PwDV8u0uu#A<06OpuoqH8wxg*<lj>!6RiT~WhH^QrKksH$+Gck;e4K% z)|RQz5|WL2T{)?umQ1B%@EpA3PPhJ_BSq)`w~V8xLD`p3Vna0`4s9(5Rmbk`5tVf<1}ON6)-r z2jD7bEIG#=P?HKT9Dlp1wcBk6(5FBu_D&9{!o=Mvj--UYjEylkSR3LhK&RO9j@=8c zMgKHbAb0`!{@6KB%_hYJn{n>Y3Y_V_T*SbI82R>xfdx{;?QW?Q;Hpz$SUHe;8+LN7 zY96+t*=Nn2+3X@IX~2d2Vs(qYM<1CEdcsnU`K>I6{29yz?mX?b%M?yF^s^c^uR|*Q7 zOUNqTD8u(RaZwE-q`?Oa&gFDq>72;=L-A~KS-Fvaus7QzI|Sjhb2?Q@qx$1+`z($> zp_$(s^-IiGbkAh1k}ds#EqIU&{FILtp$^*u2QvTPqPOIl7so^F$*qV)te4*|ZI5X; zkjh80*UNl#m8h;J!SUi^Ul4GO*}_;u@zkQ3(CM-q?|<$}T2rYAa>+vsxm|ZRll``) z9qlq;dQ)FLtmqwS_j5E%%*K{SS+jwq)so7(-x4RKXO4cyXUTp|(A5XR#^{_I30>iy zXJeC)^E0tLWALQ>b^Mhnly;12=*n4!71$b`+yzs&O2xm3W2?{F8dIg3Bh=GgHgrn{ z;o!@8%6po=c*Jg$)kj_w&8DeX*pZ%O>Y8+@i?BrYITKBT{tF292gr!f(#So`Xb&fC1C)Zn5{@I$-GgaYegd6QWT<>bz_z!o?Q5%TAds91RshmQsxk z!*KOrz-J_ygIlf9Vl4^VPO~`yrje`Gz1oPhcuX&-^ceNa%>GnT%J?K^okPdLQPzpQYP9VM7&`? zgVbX_(4ALmyd$__t9?Itj>Q#Z<|@~Zp-C7NGkyd21JC)t6pDc|abvsUz-_!ulv!5- zTMXnizoHl2L%XzYxO6*dVZkA_h$L3ZT2FZXbvq)uM;N^>j%Ht^3niC2p_%%yy)LxB zM26Wc_c85i84hc3ycjMY8Yb(G)0nlThkFDI18a>QzoJ5Qz>)LMhk4O@A4XfWcKV%) zQ+vrj-2-fSzcme(#|)$!A8zK>pILVv8DPfAe6q})=eAAMx@;gzQSqD&AdYr=Tv%O~ zp|YrtC`k81m4K8TbeEqU{3k|etccQWhxlYypS?QE>1MMpV63Hnn7nj+b(MDvvWao zakOf-eo9K3Vpz zAumy(%&q0*)yZoZ)!3M;Tl+9|W%AnyvW>YDj~h=67U-4OJ-1bczzVY#x%rq>PQ0S} zV7NBMtzie|?~0(@zEgkBt6`4zD?eU3*OqL+Rq(?af9R3qg_1_CSRx&m&Xk^e`eokF zB>W%7&Y{T`Kv}b8pR#S+wr!lUZQHi(Q?_l}w!O<%-|n8g!5zF=M*e}!h%a-k`vc|S zF7Go5b*T&C` zx+Zj$v(4}_c$J$=t5=?9YYlIQ3c>xvX~f10h@<8i7R#4WLQnn3knM#L4Z~{KkO(*B z`q5I$D)w)nV&-avK|7nMN}Jf;!p#2a>?Jev17mI9hv20jBA5mg%L*P*4pik+N_F8Z`0r+g{eIXMNc2lJq|T4gTZiUYZNr zIi4n8t-e16{=91SUK1L1ux{J15g(iBcP9LNI`gjSGMd7S-fErCyj=vM#C57Ii!HLx z-IyjPZ`2LR5Tx`JlP?oIIG-yUiNyWSC^skdslEd)1t#z7cAj@;R$W^~Hvv1Qh`zQ1 zK8Q8@x!de@#8-!JmNa)NTOfC9!NWTdcN-BR z*ev>g+>PX@jW`%OBh=jU%~-neB>ER(|29{u6AS@|H-Ro1HU%*Tn;1tt=M9DK&)pnz zo|X?8qe0s-+0n6uZL^ij#mrvs@NMHhvW;-Mh#ohWi0!sfXsJX55KyE8L7;;>Wu8}m zeL@rFXmo@(LJ7BR*3Z81Dff8IOL9c)wFcb+f*g1!UPB4QYv@~*y}hrTm_|$Ut%3zu z6&od&r6RinbftYp7&HMhO$A9dUVBtG;24d2SbzmKZ1i5VW#L6H>n1kKJj3~Vy|c!f z)}wYxMed<&_?ws&@>jqtJb&J|@k#)yM3n^f2Jw?8@1&4yrnn51S2P067U<;SF?3k1 z`Lv)N=$p18#Hb!xLj^3jLp<;m;192$CQIx1;A-z>)gIns^Gp4DC zE0EhaAo@Moyii^%ZqBZ1$0ZSlvbTFV4`_X-FwY~^&unrenA)k?zJ%BAv=irb+9fD2 zz950jn`(aH=(5UQNa(4`17dfw`aOFmy8(w^k)_PZopIn(nx8E4%%Q^Q?zFz(lLNaT zlDrot=DXpEu$(y3GjgSBFkV6Tj_jr_7T-*4?R!AEW#5_Q&+Sc!>GdaGj8lI3ztvF8 zMAaggCP1A@jS$CStLPKTl&`Nj#ktXr^%^MHQPyjmG6ihlPpR{qV@LjtEABV?ZD*C4 zixKz^7MSumpgbJY#KlWBegERR!n&IDA=o)5s+pE>_qSf2^Rtw{?V*NHjZ=t(nCnNo z&eT7vv#+VQ{Z+Q$Evcw9_RUMljaSXLTs-M-=XCoG;vbW0LfhNfQMfV?vNEpdInuAh z+PNC!MG51Ro>g+C#8$1Wuz2#(&q&Wa@h7NrkMymJm5s+YBVWcI_3Kezo_`s|d`_Qe zbf0UztA++4?)19H=C@r}Dot6M!E@UAeM5FgL{~mL1t6_dhbJbJ{MU3cvfTvP%b_^= z6VgSaemCC(_!&t@e%ow`OtO3-KdV!U%Y!a=r~{L3fS1%E_J5vYVpTjY-y`R3q*0H4 znf(VuhZ3@}#O3s4=3w6zB;(^cE8O1=`Vjf&1veDB{>1OSJ{F^6CTZ)^mnj5md8}t9 znmYV?V%?pB7!MEZPd}0*^xv%0RwiMMh|w;iDcO{=ui@t>s5{RuRj#co;SYbK10g|E zb?}fo0q5P@h!nf7*nLOL1nrMHrw{jWDH3~y5|CJVtiIArv;`^br_{EDcxt&6?M!LE z*E=A+)~0|v#oguDWD+*C#N-t*X>_wD<5j&v3dx-fAnG*CZpb04T_5G^Bz@Th%Z%TQ z37cjy{k+vXq#-bgq1ndHW7)}~N3-Hw8=9Sr95f00byzQX6!rqEV0z;qr--A$I(QHW z6pOYnh!T@8I*Hd?mzl2ds%%~O5?D-=cQ+Ge`!xeLO0d8n=H{1LkHtB<_)-U_7S1Z; z-fKW*)4bi`WQ=cgPC8cI<{-~N&EzsBGf(I?|`??X|Xa_TT}rr!@mA^l1^=M9~l&QKaJD(eG<~h za?vd8dBN3#0-cQBZtEuGlHRg@gaEu-iGOh9_N-!CCzhKSx=GGg8ibr_qGm1Q7!yR4 z^RdhR&+#JvzVt|nP><0>XqYCuV}1k0_HR_ShBeh^)_FDzF^9C(KV^eyt zQM*wDeaoZBqu1BJQ<*(p8DLjjk!|<_k~19sT1Nq;e3@lo%E+%2=kqy~Pv0K?=!T}7 z_C{kdeNMU%Bdd74sm${U-Zym%>yC^k6vjxO#_ZPA(a#4@RbnE?2KAOjt;>mYx^wD8#xu%BC+lJtTpc@TSML*nVvEj24W-LuwkffK2ri`&owIm z1WQz)wIs8jW>=4>T8r$NAmE0h<4geq$URV-%puGp-I1V}CAnOU^5Rsz1%?_Y#bq?g z{(5ar;XY|KR-t)S=(VCkVF6-+Z_>SWFMRYLU1t6>L|iWS*INZ%zS=kIkJfAl;{lpPNy&?9@mOygz_8$kkxt{Od6Zf@b;qb~Ymsiw84Dw$xqz@lgKJ-0@bh`4O1PgEEkkKp<@DjQJN=Rw2-IsrQK+B zeSOiJPK@#;K>l@|tYkTC@>8=hcJ_Romh>x6ByELyT2NB9w}HXy+-4>^Gl!Snd=uXb z*M;F%{FIIvOgTF+H?W!Xttge;OVh^QlFUMtb}%Jo;dK43)`S4cGEi71JaXn@iGPz~ zt3lnQ@j7|}#wT}nkR)|cRBqR9zkQUMhdaik!fcDvadK1%sbL4*C(V#6V6@|(^Bk7^ zU(L_mrJE$bagmP~ynhEilCBmFHa0y5GaiElfg2CDfI!s8h(Rb&|K6!=!wsEQm!Ty< zE)@4KbLpzR-|n7Cwcd)WZvrB2Vk+;PjnbtiOOY2Yk;sI@U*eF-YLdjVHk?o{j^-Z9 zBK*OXCVZf+iS3`}&w--VOkd-qPl(^09wE1^E--8==-Kl3>hZ=%rPJV@xS?>!q433v zLJ(A$#V3R=n@W}m3B8O%b2%4J%^q#c&XaFJ=IhG??k+`bjCg#q!i#ZSxni7>>lj*q zFbf9V0RB~h8d%d0XATF)XmKmm4Fa8NFfkbHsI8w! z=`V)Kit{8T0T;j+ob}DCroeN#t$a*$Ky<}_^CTmG`%MUNGZ#CXGsOfCKBrp&QNP8m z$-+`AKv}+y=>t(myj)i*`M@yV!9%&~u(Nl&+|iUogdT?Xxf?x$wn$bcZC4)Be%zEs zhRKMd@L{2+T5^on7Cl|5^$J3d<=%EJQ+BR5mD~;vI}mcql=z^;xq(6f&w4PLmj3sm zJRQT)G^I9y_P7V(VHPF=j}E?G#$%cgem_`dgm#*DJNC#wpTwuiahe7`(_=B&v4LF! zJ^$ag*OyZ#_mUFe(el0hZ7G@sQAI5~EvU_%?{uq>Z7lvD>MaZa2!al>&+nT^g*Dzwb@29$ znq~_uK4gXS3EATyh5^?=H+GLEh&>iPnm6JUO|9{ku6**yxCks@DcSUw=Urwm+#jtQ z&7AF@$cez|^mxFgd;-&^s7$r&HWuPr%IbFew32&PKI!$%_K4PgUO>WA>Q5QFUiAug zi`foP`P&en-VHm~UHTFo!Q#?d3?Dg;I!rEuEuZfovUgi4mPt)g4}Zb?J&SOxbVAdK zYF|WOIhdx`ZZ)3Os$()y{Ios4#6+jEj7xZ#!ctuUn8fIQxFUv2MeO#kL@Bci4wcJZ z?8Jf)h++_gjWu0NtNy3P5@3Jcxr;Ad(+|xvFf~3nnpN86WfjUR7`+g$$yxiTqs^9N z{*p~yhWAZRv*vh3y@XwYTvl@Jj5^+;(Y|t5jbU@&5!hF%%OKr~qa}VWUdP+O+5k!6 z##~U~<|d0J6mO&B03|(sY)&T(a4xvt0a{0!z1N(_%J9*xqK;SQZ3cPZ*SMKN5R48- zg=^i-Khn@IcNVk~Hi=#odKGnQ^M^41ou#PdTn8&jO=GgM&!sk`LFJ>98>P+KbxnZ1 zpgC^Oix++mng~W?u;mqwOX`96uVxBT#76Ygvvpjr&kA3;9m}l|0v=z725=OXi&KxC zM@Wo*{PlusczKWNxu?bvLIj3$oKxP_ZRu}BIJ&f_v;Gt%d(n`B9eN5j%AJ4>RrCXM<{UV<$+jm`vN64S|5bW+JXyFS5)G3pYTKvn4 zl-KE?gf8bLMf&*Ikb^@76CZuJZ5^~2K>`lLL^;)sE}m%=eXMtaiSFI$#IDb*<59>6 zFHJE>qsbdD4y8vs2cGPYHLt+zN$TNB5VS@=h_2!HhYKI-Hg(*OLl-u)+})hLpQMxw z|NVQ_DC%1NmgGrbs6!k8Hjk|=+u1Z-@x$&5P3vhk5|TA#m}CiUbN34)D_ctlk`Y={ zt^{78j;f9bpOKyoo6_dGrU2&{_sBqP;}Wc_xr)4dFJqbsRn>5JC)g`590wi5KNU7M z9*Bp?V)p&fMBPX-T>eqNf{2SzA$@tNC1I0KX{@l*!Jei6Mxnc#Mr);W`D2j`$fzT)UQ#@@F0uA3-1LeTWD|S zVggU~B^+<*@KK@+XMRuHZ`&>@XIS= zqH8uYR(fy({|02RMw17E6)z#1QBuda0VS!khD)NTCuPo$r{-nVy;j65;U`A?k z38bX+$ClNuH`VyH;SA96vg6gqfEsJ(R5jcQH`~%ZdNS(L$!%VUdl0qfgDXZv)sE%m zOrI;?U5@nAoSAQ$Pxrp49;MI*B5#MO7>3x_l3F#hv)kn?@4)t4-I3%onRFL}a@Y!N zommDodx!Y>)@*si{WBTcYMqbWUUQB+idx?03&9mb8u^IF&Ap4#7C&9<(f>OS6 zZ9aA>j<9xJYsUBc`V-Ozx;up<-e#Z95ToP+Nm>2nuc2Mk-inFz8?t|6$6(R>@X$-m zx4Bgtia=S8NcP?-X4L-@HM9zsu0XRL+1aw5-1ji#P zDgP}DXZ^4K2gd&qhI27-{nw9DCPrpf*8go7-U6q6)9m6t7JmDg=gfQdyY|t`W}W9*yW_pL)*B8eiQ=v*$C?JC z1rO$KaH5%7ytywn5A!zpZ_7$^WYjMq4=ATw2R4EU0znm$Pczsmizy|<3E-Ya1#xrwtwW5lr_If| z23z0I)z#e)Y>l&FgoGs`M-QMGoEVs9}Sa-oUaez{Grnb z&*bR{!tD>B2>dgQ3g_zc;1HG-hzF!F0esmA17ym>_+)P!+V@8j=yw{Te_-_f*5TLV z7alC|=LC+KEp%fuumFHq4&IMz1PVbZLsie!!PNjHsMxd@Mu;P^o-8m94%Psi$&2-+ z#sx)4RtW>d$M&Y+9ajpE$U`B)E)g+vi@xC>?wQp{x-5%pW(^z0%~kYUs{nEc#O{mT z#q+sGVf`nxLzwRu_!O}`^Zk~5I_GActq{@MyuNx^fLww2>35vAUmggb5OI8L4iab! zwBN2Y?YeKsx_v9~SJ~+cUBGq!#1gUzG(&(5_$4SKINxuc4^N2RARsxIWqs;VVZ@)r z1s*;CYj_#j0HifQaN!r_TMCZ-7e@fE7j*bf0i@3%J|xiF=i@C$FojpYo-2eYe+;VxUkB4vP6J;^a$+F#mKhaQ`d&+^6&Ni}}Md`5Sxu ztDTV26g#7DHQM*|OTadcZglh$KY(JHo12F&i1gA7G5^c90`l9_9A&@~(RA}`WtuCf z5KRcv;z7(pBY?wkyJr+pj5_?srWC}Hx#cUH$$yH?-#+UP7>s%#m$08c9gsRa;hn#W z!j-Ajw~H?)|H1~CN$pzD1BLW+Q$FNAS$ zgSI!&2cUH#|Nb=~w`_rZ72>U5!MHNf`Ttw46WmcH?hqv4^aJi7eWZ`yAdkW8dA@=3 zKb(IMj{nnm!T749%HrnX-{vGt+(J`pUSx5D?!#1PFv`|A^cTyvgKAr63 zpPcyaz9s#FQ?r&!S~gW#(f^uxrqZw*uC^FE79W0Q-lacFAM>)dB`#<=So~au0xs&v zLN{?v)~CH4yW8Ep=BDO!wi|D2yTW!BnxTz=$61EZnG+??h|s!@Un^T|E7_apNYPFE zi(kaJeA8e^9OD-uNaXpQ1&cydug2QbB3rHOV$>*?wt6pW;v$eIZ1u_A;+~Og-f}B} z1j}nemTF^u){*v_b~54Q6+opiz$uJ~6=rhuS0@<_wg5PJY_qon~Q zGdURU20Lf${yf1N933|X+YdtI+@-0tkyi+EEz8Khy2y3-fXuKD&b_!M>mC)k@*%WT zUgh6-P{4}Yc}21i-=TM{P|Q#%z9F*cN40@dDqJ%km}2L=aATF~Ip8f*fJivga(6N} zoa(jw(3y+W6m?>GIW9ZK_gwm%n@McEf7dmDNX6*=6z6j#{Zp$bZVKEli9tRy#o_Uj zl|fJ1BPmn8>4>w;JjQp2?B-{>Rtjk#Q0&i$RhH z`L##U#_8foRZIpV6X`jUq@(h_=M_7mT8vUJbOr=5?Pf=82moOf#E!EKK3ovK&PUhR zp~w8R8)?J<`Zg3K1EHQr%G)jy6>cVBw=8%>omdh@K=f@>7$fpNLN#cr78#prI8fX^ z7cG*D>OsqNU=xQs5>LnIHX(tJ44VfoW;M4JU5PtrK6DR!Deue+I*@kn)xvJDdmrHV zmt&M5Nso+MOP{GPhJ}qtiX(4qYbU+QYqY51cHjx5z|z=0yoLE)?t8x2evkt*Hf{U+ z0EyYi1x>J>=ICLih|1Z0ic0AN!RkHLX|aphK7nfn_4Qn2to50aauj(jJH9>V!Q)9U z1FwwEakAYfktF!kH8HJ$e+c@LpM_U~po=9(%?paK4xJ+^YPpW1IqX6(LJo0~L~w4` z4bx>jx4G#RYZggVwmj^qoO7XspAohsu}tjZ5ibq~ zk6#ioAT97DZDN!OAdbnNWawEq|=S667^lbwrKCBe!AbTiyn3_#yDTD z?5KDyUppH&MR&tPQ-t&8|;14xcqH0Rc^ ze39;1H5(%&ZopVZkn42-7U;V6*Xl0ftt=eHN3p=UPN#M7tm+y~;tW3aJXwwQYX|Q{ zj`XHG$fKT+{ySFE*s_8NV8?d%x&r?WdZ*fqRlE@oM(A$*km|X+2+1$qI($${5~O#W z`Pr|jfFNBX-A#-rhzn*~$@`T3RHbc)o5H@q zO-xa8H?NZYaNSbd8jo`F2^el~*xQ{%~X)q|}`Fl1OhN$EQ6QLS@u z*dRh7#6HWuM}V;6oKKp3fa~t})@NeidIW34BNG6=>?CqWxRZ3kXRP9#w>oFD z@SilL+s)CK*C2bjd(^&AZ4Ork2I6Du4n)JZYY$IXxHr<6ZjgD8Uis=dm^pQQjw>HV zFh&!^$4O0=pGG?D2zhJg4o`nJ-gPdzI(9dGhSyq}50pk{uz9lJTsC7&V$8Sl1-(mv z;L*Om-#QhCE_lV-Kq;z4Bv#qlg`-69ZdoQ4fAA|QOO^E?*B-t+Nr@bhSBoxsSq2>= z)kM!>&`7zFc>awJpfc1Eje9|y)?4#A7MbcTf*SJ|B7bm`b8G71$tgk;zcyg9)VbQ-DXiI38* z-VHJWp~v#*Ow2?hk5O^2ky!K&7#${KPT1VeI3GpB8W8e=M?qfBnt_rnsfs;*&@LPJ zaFvw%`BV`y`DAvg^6ugOMXW~riRu0m_!G6j$=Kl!#_2f2 zk{ZJRM=I`G&SM%*mekjZY^Ue~i9EB4>^Mk0kO+Rw^FHhrpNPj2{tDcF9q)U)(k;Ae zzjJuksdt}tf0oF|q7v1SDOTFO7X2+ukR5$6MNyY{5q!u^id!`}$+PG=-2WIr`JDHn z+42EzZfmzj{o?`-^cED5@=H_83HxXmcsy zg{JbNWU&t`wwk%(HjUfuFjhOH4V`!)~-aH&Y)>*hWinaC8U1 z{%=`885dnHOy$i!aJ<#dvN9qGmE|3<4wxBLp-}ZmR$tE|oWwB8jFNE=f_4gX$jD!exv1qt0eq(tVX9Edt~<1y zNmIZ{9rTlk83lT9cZO!Y-Fj)KoWnJt&AiWJwVem2maPFip z$z;L^$}I5B!F#Rhw&|mPZ=`Fwp^|U)wDMNQzXjsx#vTo~{Yr^%O3C>?@>#1MZEW7I z{T@m?tE9tVDe|gJ;b?fP`Hz+y32}yr)L`=0zI>Z9qc!1QwF|YAkltDp^=wCiEu?0q z*09ELOg?5BVdI2BZM>qC)cF_!9Za5!(x?`)4H=4gM5zIjIlzB6y)@jZFW{BX1zFwD z3vWH-4AI_c{`o8+e>%CPy4sW9tp#f&=9d=o92$_~y<>A#$;zk>c}eMVRFn$RKAo%< zis`JX>3zenTX~qzF})p9o5dMGoJC+sgL2o@}H@usbfPd`=Pc_8^sRhr-X*<$>daS z)MlRrn(n2Mz)Uzk$(3%tqtfT#6oJX1q6C9=t-%O55WSoiNcF8`1uL_W(bG9hhEC|Z zN{1>eo+Tc?;L_!|oId8wyG)y_pU-i(#{U^K1Kl>dVmF0$vP(I;LpkT+X4V{f-23>&iB3}dJ7Pg4r)BZ!s%!sHZFmx=(WAEoFYs{s2Qe(C43)g;yi>r@1o8OR}*|aN`2lf?L{!^T4rmaDWH`>Y5nSCZX6NsxRKPZC=u`&>Z$& zeVj<1m173fWdLjS?0BSPBt3Th5<@=}PSIjPh~ujpmm!Ij?0Pv$f#t%=%Vx_7*-b2q z8J1(*16n(LjKQe>p*c>}WjVpTTu`}sqkNVgxmJ{<*n8&!jEcYj;Ia%4hPCZuyG%C` zMQuW82fi@0@UKWM%2wxIPfj7R`U4!xi?d3I+52P2d(YCF8({b?9b>Mh8-AbLBW>R- zN)@DOCc5dfFCffv;Oae#i@7TBt%-JGu$z&JcwyShLH3flP76)!EfNjyaYfqEx&UA{Fx*o9#3=zd}ua2Pt?NU%jdt# zsz^Msl=ON?KTw@*eW0}xlx-k$=<+FSg4=Tj9})ZYFNu>3h37oFJK5W5K@DTkz)4rj zK(+tOZu@dR-g~gYG@`%@J!e1}&BAB*ShGA)W0U2bcF`aWyiel}8K?rSjp4kLqQIc+ zU2g3-ER9(qbuNxU#E_;$AITpGJU&_pb7{;afeyawRlvOw&OcgSfo%z~raS)N)n-)S zW1lCFUi16ntmZGf-$YjFgGC%G0$*RYsc=S_oAJK6Ra1rFbj}$TRiQ;$s8o!%;c#0L zEsPSJTr}jG^Re+mRNd^Ee*{eKj*|<#+bRH^Y1S1 z_mz(0Xc-fj**GP3$QkRwQgyA2NSWw;+^TFaJ)8&I?2S~}86;7^dY*0C_vFLotO4H^ zauW})LrxAD&ThhVDjnfbCvFLL{dzCruNmmOoD-0Aj+WxfyXAC~>oF22!$X1XPmeJ1 ziIn1=yIv#1gWHseOdq3)b5UG-TrBC7z`+)ckB6P{; zjNmbXX2GGkFNK;1L^LKblJAqr91?zbb6Fz~K6F|yE(I!rWS^O?c)~wdQxu*horw;H zqa)o<7kc=nl#Z|BsMqKR zWdc3k+_%}8W)ix@vqz&91JCvl1cral&aY?TP=9xPz?m(i^iN^At<{DHnF!i4!$TU- zpQKT>JpGzss$|Wi-!JQlZ`m$?7o6%53N6&E=P`48dSJ3s-o-_W{vM1wIF?~tZ9B$i zHT4;-W0QM0GkXn6%n7iPE0@O}X|d7{?-m}ucaAJSk5Envhg!^T7sKt8jyl+6$Fnqv zXL*P;c71Rbtu=YDa9htz(saA99^#*v_Y52$9iQqVmgH6Wl;2%sFJy8YMxI$%CTD7N z(qb)-x0Hjf{lxe3_jFOJpuaKl?45q>2Pf?#6CK$47`1TFgtV6spEfn|M?0RXC7)X>lI~g9q zD~(a1xhPWAbzNu#D>im& z8msl*B`Ia#?E2|egt{LvD_XPGW?|!>T1pViyMYi|2KV)pI5C7y5ntMDca^&xBotxDQ7Npr0?#;6e!zCsuC`vs@gk1mDd}`zK zwk;14w|#&*{s{RHQW)*gAu1s}x*l-^9qS^NZGod58m2OKb^nYxTm!SY+E@Pg>Y0KQ z$O6$lzzzxk#|tPH-J~v-8mYq*!+|9cdO>KNUi?RJk0t@RIgNreOD$j{qb0<8q7{W} zyR1lOu>UBT71=>iUErp)no=iPmjTgg>_K845Mpmp>sCOBR4=+8>&PO9F9F}a_1 za=g@hX3$zDOXQ7?cXTin>RlBD*VTg5T)r-L|JK0aS4?hp zUHRkE`WNeghKC<+h{;2++}>_R^I2fvwPOpE@^uoSP1``3=U)s1m_hPJ{cHdNJRms&l^zweUA^Q5=Qvhio0?C~TS;TC%W=8(DU7}ha z6l%sbrqgee?Wnq$_pZgL_Vcqjt+1&Im%@o&y2Q{#`?s^g%xy?~2C=k1`w8 z=r&*;up63@VG|!1@!sgDV}J`cvGQGl`gh?K`Latf{yLjl9#|=UQm;75peLS-qH%t0 zQf}GvnfUu8F8y|Igs`Us6VX;jG7RUBEo8{khxAX*?43_+>>2+R8;fWR#NNk>SSCIa zJ!a@r@+Ig1V2XkC(axDtU!_Ph%0;7GzEG@Hw-qhhE2hm93?Wt8btS(VVS}UT&0q4e z@B6?vq%W_sJXZjQu#PJ|!$Mc@VEq;U0^0_KH;&n-R7}uHOoDNo(luJRZm zhF2n+8c3a{C>bVa5@dbH&e$J^a4JTLP!^sZ9wrx2lUs$?WNY1jVyd|1G*~oeOfLF8 zVfgy^ky-Ilgtn)IR(e&Pj{f$#YWz7VeQYuw8OEz|gX*AbcSu@OTS74W90MB$%JM0H zT37LenXWF5uSdOs{e^JCuBJ=G%bjLUwOK4yfgSl-QGmF5dbKu+maC{4iFD4tAB*Jr zWJi+&9Mc z+yo=zkx&J97H;&e*?&OH!ZG8cQSKCl_ zpwavvj8 zjs^>}*P#o3UKxNr=_$n*_e&C5r0{|B;q(+tZl_1zTAjUVxfB9dEwxK*kb6*Ch1TeK zC4(`X3qF*1xDssKq=SSB3gF??>kx$3ZFn+)Kv$ghUSjOTHf^1w7+If=LM#m{;XA4S zXCPZ@9)+aqTi35!>!O#ti0!8L{@pmP!SfdiG&ciXz)W@ra)GuXOykFS+B2avC!WG_ z>O(kMs|crgF4rR^zwn$rqS3ee6cc>F{IK;~7{}}jeRhcZVLu8g^9Ciz9a>~Ew_`bk zuz~_w!^B%Fvy@v1ws zJ1b;%!ug-|Xr?$z)?f>mZ0T%mjvLjnY}Zh|NOAQL<{`)bP|mrG0-Dh?6_2RQeDB@L z1V6iz{y&Iva< zZfh}!UzSqP^eQB}W%k`}@Sdwth{@c0)RtcgZl{?3M+4aL{h@%ca7Lz~R+NSLlsLM^ zU;zKrdQCYV4m>D-3BJPAi#Vl5ieXaeOZajKtH(R{pU2Y^jI<`wQE5vV*-bJ62>d)q zg$ubleR0l`zUN69;7_@-_5ea|{!hZh6 zK?xgZ?30Z{26FJNsZv+HkqoLjW`=+)uaW8#XKb0l1le^JtXd25oSAvt;g}QJRsrDv z_L1fm2F^95?wrI>H{`DOsqv@y6Nj=X%8Z@xZkxoEgN7=FqbRQov2-5E2YOYV*3b@r zVTQBFkmmW-}JD?9@jEjY(eP(s*9g;c6Xk z3DHxFD3VmK9i{_E4MXLuw+lggT7mq?7aaw>`oh{%2)*n?UOM)+%;q9@uEq;(Q&uY% zvN2uv-Y%UMkFa9A!@dS5R1Tk3e;$8wuPM^i4^u~vdwr46}6hWLrn15 zNQ;0$YNBS5^pWU@M@YKN9SH}1`JS=wiCppD%oXar6>{K@G|Gxu8dU2MX`+1erQ6X- z6lK^>4J7JD&?!f1$MyH?OL}wP^uF}2Cw3pUC{r5SNklBumW1U1%^=_cANq56M|14! zE6lS~N45=IlNVhd_bI8l(f6AcW$7fLTtk$#_7;Qjm_qt@6bx&z)uW^~JcY5>eoy-$ zN8e&ibwUZ?4LKTnTb1bto(%M5s*jjrkoocAN!{azbCHCw+%a^+Lf7oz2S0b0>0)7# z{IW6EbzBIbi|q#EJ?0z(K9vfQJ(M`pj?G%5`^*?xI|R*UqGsr9=q3GLCWD!)c)t$? zF}XDopdF2~AQ2IGb#7G{YyP*lPA-~0rK{R{1V7hgD9%pEKUD}ARHYZTyM#8t+()KNxcvGVjOKOf5lXdb< z3ALvC77svzl8o5K>p;`=cp<_MHtsS$PeX3vcKJxqRHL4Drx->O%1VJs9|P|b!csIw z0jXvehf5tgvxQ?UVR5Tb-`>8Okt(ULc8cs$p>Goh{5+344){DI%gV;sX4?*9-h!5) z)uMCNk}kvdgs&FTM!@L9izlywRR!?x+^K4H4MYD3x;f+W%@9bx@|0DJ>2ygnvIhMK zR4U3o`9jvJ^(=u<1wlakZ6&pZ;C$g(#ZgD8{*KKCQXJp+!U&_9DOZ4Go*h>#{~7L_ z)>QBq?}S#!^6e1j( zU?y4pgJ!#j5cLq|+hpvT_aps^Tol1AZEIagqp2&CS|WUddz8Gl0Ik1`C?`SEueGec zPUZb6?m)A&Jv5;+KI<#Igzvpq&`=h?oUy|^7-t9DJwCXB*yKAvNNs2<&E|Y?Hjz$) z%#ii_w>6^I$@SV7xv$fd9HInQSkCty{A2yw-El341L204s3^M*uL)G$5f^$fc6pdQ z?1Fisi*lPA<<@)_nagkeM;gtJQCbo8jcu|->~hYm8Z9^brVgNUHZw=yHp`>H{E#703o$EC^N4$__{tdGUausd`s=Tuy&nR|+08v1$zj|J8 zvj||7xGeOZen$+b+IkADm+xaZ$An>x3t!lXi9K!v&65cPZ_Na8o4r{`?ZT~cUn-P_ z{EbHW?6k2$7|Ml4Re2t2@a&@$25-4)5Y94-8~ns`&*I}3bdwtj<^8=(cig7^ zu|?SN164Q^uc)37Uy}v#dsbMlCP@`j>`8ANdO6<6xLC`Ua*;h5u|l;`QDCxbmy}eQ ztRQRPSc4)I8>TTii8VNHzear)4yhqV z({|lDpwG{?)~dl|^9q^nGl@=EueA~N$00hR&QHK`C!U0Z@lO>~XiG|Bj<$x1)N3Cu zq{LCUAR0jk1;^*QeuGuNEjiCI-&}_yH8d|_GBPL@K{nTY|GxIUaj4;XVC0b_t{~|=ekCx6GP7nDJeO1f(T6>Z`S87QC$Eu8VwY_AD}9vVLgR3Wl?EWGq)P2w<36V4 z_>NokQ@}q5^6e?};u+mam#i&0A2rp=r+8+%#+M`t6h;4v8!d$gYkQM(bqG;TNlC`^ z@LT;uD6URr{_stgHGTl`Rs!9aW0)V!nXxVimb5Mr`oNGA`*nElmA6Y zb`4HP?WJTO!<)XA%q>&hm1n^)zdt4{TmX6uBzki(DzF)u{-aFV0_Gg*aCdxn5wz#{ zMe>eNAb%uom1MM8gT_*%kX7s^_w*Ms(|ULGzug|P|5p$XBh&u?@i4Km{Z|kV6Eo}o z8pPuP$(nPs+DiC$nKF`Jw2npewui+FmU+|wlODCIne&?`ENP+B+z z5ezBGK8fR&>x}QWZ}qiC%lfpV&iBUmw&&JbU~bx>F)$W96<6_v(G?Y$1PU2M8dUWu zhE7Nj2$7N+3@j?a!6~bs32J?h6S@p*8s(1!QOR#`;URReL45`_STNaTVFHjX0~in~ z3{a#@FzLTQ!2l>k%4ZIw6RJOop&mncKwiQ?wy4Nqb4 zgj7_}fqMWTGL8%iTo@scCqhi~3>-HJN-$tXaiIgom^}gEyK!z~M!@3WAU{99Fd|1Y z;^dN4o(9l9=olwxjDbZ)8OJp6U6n}y|L~ueGFBpF`G80k-0$=O%#(=wK#xHnO(5X# z!32xyXpBYRQ|NvGJmcyjIA;4$f`6jxFEJF*4=W@PGRQa0Ho>ia;QsjDEFvZ9$tk?h z$3UVykOU_P1f${_+TfcJ7!cyWAqHZwKykxwqFqEz5TkgqFL^u=MGa;!!Fsfx3WPt; zAzjAv!WNu|8nJ_Yg9cmGVJ?b79UTJ+n5+|ZU#bUrjFhdneuMt-YiN-VLJ(g)P9Y?C zxc;dJ`zQ1^Q6b(Q1I?Z)*_bet`PD(LKi2 zumT|W6rO%0Z3tQ@0Mw9Q0$2gjE+f$B5-d9EU4EEz#NOhp*dmvE1ex1MWh5_1XfH3jbkKC{C@Sp*oFO4bej{w>4RC#sG12`B| zlq4`%$#5VLA`~fnLINTv*f$G2CdhB{nE#s<5uOrIMc?Ac%f)R<&#y(m*<8RK$Pbnb zL8ux%T>qne;T~udaN#<~{I3?EPxr^4`WNlQAML~6{2w4x@T>LlOYF`68j*u@@D>5I zN{0c?4dzL4T}$wXV+HqR?fh~8cVEx$*LG!4uzsl^#AT5BFQSVEl*cOrZk&4f=OAvs z1KjdE!^&4^_D>cE7%ntu@%){>J7zTo;+sDEb%Ky?gF6zddRCtt<2Bs3vpyRs>dBq; zy6EsQFrtD&1OfC*bps8PAP^K!Dm>%fo)H8TERhmsH7t;}F=9W`iPXcDfshf9p|?$Z zJ;>N$A5mf#kRdDX7J9^){WZa-P!BI(!_ccX=DNv`-fx}@<}?NbnO2U7kSVcFo`g%z z8WqzAlpiLErtVdgTDsqO4p(rAj5npQ`YXIwG$CPBG-a?Hz zL3CZ`ytcY>7Z8Ww1r?b#ISIKCRi8D+YatoV7`EB^1HS2@oWd0?0}9vlm zrls_DF~^$yopjB2eihdFJZ8$vmE(VP$X-MH0~Sphpwc$-^nLCp5fgSSYscA)8#igo zn&yF>61bB&E%2OWLt2n6MG-;#KHTU)LLzHh`CcsLjSnm`y4xkE9)pkvMXdxB_W64X z<5Iwp>n7twq>y_8YUq$8n%fe%GwJlEEI`X}Im-rgyuR$|Y;}n|Aj10jFGK%_v2$1w z1wfEs+qP|+uWj45ZQHhO+qP}nw!3GJdzi!4q5h#FBeJS!)R*GPw`l9QL%oXN6>#!0 zEXstEXt|tR^=^v0=aIO5giJ=u$YLwjGQ?cA9>`9}XF#w~kB@@3x>$^>QA*IrHj<`= zDWPP_Pi36j>>HmWXiJDw1WhR>Ds=eN82Gd*(eQnU&{v7l2r+@Y?_D47Gb;~tkxYI@ zVTX3@)@+h)c;G`s!mVAPI;*qq(k`5=jBd$>vfPsB+fr!Dgy2a}RiTpo(!sVA()ild z_S5fq9QegnSVB;id| z`emSQL&PZ;Vz7D;=qdob7crXjRT}FO*(V5+WDJO$#0hcC#!M4sf&*ufbQWq^$A(fl zDnp@R--ecWOHD3YJ;@Ai1pLl1btTd}kKOF*&%53R(ZCMtev;*PUh?p-Z8y@r7%D?A zT9Ag-cXepFV=W7)8l^LFX&V8ma4&Tutk)x&cgrnm_$^F9;{9zuWS5p;ZtUIM+!w%G zi^gZ==%-&aGGZK;7vm(~nzWKk?d5u%O6-gY)mQu$cc52G23Z}!c#IV0+Wr(ZnGuKa zV+fQu+W?bOyKhbiBlRC$vqeIlxMjCzm$r7yY#_LVC>R7jR1Mep*4DwWRs5POoTE02 zy@BbY<||k*GG&dIIuZZ8Pe0yjw)A4keb)_4r%f8W#QKb3Jt-fzD~9i5QWfU58GI7a z5!xSKstgc>pMyL(f#{2_)ECkFG85|Ev63n+YL$1jn&_7LTa8SbqX)%J~Hs|xjm1tJwP@8yA zX2MxN<_Rv=(LnX@nQ3C+>{$0>%t@-iB&WTzPf6lPV&@5Hd&OBF{Um+7uiq&NJQq_X zS3Y(ku1yvAU0*vLN5^Ni=SQXjM!cKDiQ6U{_9i`ZcJ`Yx%p%$3(I%QbNrHmG-8g>6 zthgA{+;m`%ZEya{6|o-CQ3Gy$!rh z@hW+yU2~5>)xN`yh_R_8ghdxmp`EGKgoWemA3r;3?clN|!qf;68jZGFF6W|>V6B`B zaz?L1m?X9^({x)oMjJ>Z4aGJNCmogTqPi38nq=6*EgO?A$+^~v%Vw#{ZGs_3L4tyS z3`@a6QJ_tkKJb}N`X}miRQL&xZpRDSuw$6-%z&vTo~Z}z;j%dw_Ne%9Pl4?<&(@x! zE(Y27l(T4UV&OyGgs2>?Wm{2ALB$>3;;tD(!kcN#Bp;IVz!-OXTunek^F*Uigx;7BbB#xvROKF;V(~H(!K$WjZUbcQK+!1G6!|KJJIQ90`P?wURti2~`7@z^ zcq!}@Kj$e1OFcQ)cJ+ir0&PzMCe_aN9nt}L@wEoG^|`E?kmO9v>gvmCJcd` zkJ~MPM4H02aMP-)G#*Q3&UPjooOtb4)db_be?QvfrTU_O8(tFwx2;H)T|J8))ZRc* zu^|H3XCP`ZI^-V7F3IJn8AeZpMo{3Ore4sR-AHqSM&a2rrJ_Detxt8{j6Iz^d}Mc|jc2SYeRi_LWJR<%pny%)r^k`>P1 zF}mn$E+(>H%>e&`PmF-l)Ge-L=117&(jS&w!d}lpuVphW-58<~J?S+*pl`|%bme*R~~Ixvu6D?VwEZA#>7J`IT*8)J?K5q z)NiGI)ehkG>v*`4ObWuwPcHd%(HHSv`tRz!>j*s+{x#d?sR-*^RbR5E26P3Y?5+A+ ze%;PEz^IO*(~e07SuLyxGs4!boug|evn^?PaWRJ1emcxl30mPK$?HA0>moXlaHQ5f zK*FddC|{~?h8!JrqWEN;Xd@3(3n=G;n8~GIb3+^fFwuWQ*i>IKEBU+abz@%nb(3<= zo`S7XwI3+FiFxHwM9YP!}U^@F_e51t)AfFczQvctu(cWpvyky@|^Id7b~NPpHFQiZfN{jg8G)a1QXq!5d4-;E}PQ zqd&QKyS3#iuhF~?OkCxqo!J_*K#X%_z>?48B6wZ=9KS+!nT68tOAo+g-O|f_A!~#} zLM~La8~O^O9Z0xKFB#3b_jwyrF;Q-hQS!I|T%e$IsH((_cF=&Up&~L81^l*AvF0+? zHFys9if@J}r(^pbKF6hZ)ckb23>2mpl&hf>oq&6Mt=1WP;Jaf@b3_x9#=_#nhZLaUaH# z&c8~OI0`807{6x#n{Ndy40k67c0G(koT?N>XEMP@^E|_S3%MDHPZ1Ui%#+St`RLxy@r^MuzV6Rpp!qr4X`sd zh3kw_F16#r+ndjCoH*xi&%NVZzh&q$2JzeD53q5GT}oj}8P1=EvG5|9`jWeqAOvs) z;C1k)Q6Vx?r4%qkY}YjvSAb`<$i$(gQv`+ZLRO_QGr_gQ0z@$}ih5YtIJ661OY<@) znt^@VI@*sTub*aTeJz;Xm@5|<>il?`p38L&;g($JFEqLcZOZLT(wLINbHJ4>M{b@c z>Ku#XZBhVKw&H`oYuB|DAt3WmEBsxvEbZ98H8{S$n%?FrdaTT}$i|%^hY_PxSJA7L z-m)Q%HVC7W%lU%w0CVs93TgY$Fqm!Y5%@MPIOJSJm^oy)OghHB{k#?9%Dn6$+vYff zf8|=(f79qkQ*2})Kv7xa_AzGu7JjdGMbN!8TsxcV_{d78Z{K9%iM492seg!aQHMYT zw|PV8j$lc&K2Xrn@lHRG2emp=ypyl&D0znZ8!;0TD>{wDcsN{cvs zeK#CquW2<=u9^JIK0Wu06I=vylb+=K2$leYw`2T_CaN()B%CGF)=Fx!Ep)d%&r$|v z#YFM`Hcx8{voQvfA|M!(&p4($ZICg+m)h$IqB>Bl+UYs=U_d47;NH>uI7d(Y7>q5h$=oXbXvd5OwOnaD9=HFT z7}4ukW7!eKiO?RmK>whksk{jtKhF`@xoi?oq$30Uv)#~o9B-nRe6-=a#=>%IM$R8MCx~P9&}PtZvKQLJ5m^Wg=U;<@yIHFI9p|cj0x8+$r9dDnmLfVAa}s zGVMRX{o;0F?{ZS#(WNC7{{LX ztHn{%;O)ptW=t+!dhuW9av90Pjqfy-LWwJ}TLtVM!Y_^Ek_62DV_HJa%`7>D3-@My zs8qDvHTMToOOw6Q)x&fcoT1Wb(nH==K(6{7$v0Pj$I2A;@9~!wU5nG8RSe_oX z#L`d*rZ_;m+SAPg=9Crq!cUHwPStZTeB^pHekk0|BAdcnob11Ny?X~4k%EgMv6o9d znYn@M8?GY-g>MzyrBodTNJ~(CRJl<~#O;6tMzJ=9(TNoK$}_UKl2m=ie}}(Kb$KSY zbBne<%9$jK$+q6F#xsgZ8+gg9&AYXn81sk7yynJMoGC(bbo`IOLOtIyrKOjL37A2F zUx)>J%SEbK_fTb>+Ft5+`~HqB0~d!D1);M*>m3IHzC|w3y{q**veMyD3k8rlf0NC` z4`>b7(x#r@Cg&|*ZVhrKtn=AyZeuH(Ok+O;#D+kT41*>6UMGagSD!?+u_EzC$!V>@ z=STXJIitjPEjhPI`Z;ZZstcP&!+NwnCIvHb!JZF8v?1 zK-$T_@0?-Gi2R?ghnQ8A9J>ni=JQlvhkO(^*{q&Ytc!Tl4#B1b1>m2A7`liD43)z~ zOs|MhVZT?DN$D%Cbak1ehFQ}Kha7WfRAAn1d@y`dM9mcu>WEw`4lbH)CGR5@t&@o< zKNbF|>FX#ex#27XMVrJ-&VWvQi*Xx=w19Z-;;VXS+Zs#`va13b>ybsq>%BVJjM{v{ zI*85KiDA(cGYq#UK#%arRCAKBHQsizW`r!JxpX`woP)cquU5gy!@dB0NM#VHMh=a8 zc@D%CIQ?6Mt}pfsSsE{gwXC}CqTyncU3f?R*&69V)k0a0eDEM!x$xJp9jD|z^OxQC z5z5Jb6RBg5?N%;&Tr`{ritP}r)s>Kh)R{?XvD;vFrxyAMTIu{i6%QMhG-^jsBuA^! z|BxtNf^10rOl=mVC-Wi9=wDyf$Km3?B`Q2L)p_il!UiltuDK0NWb{j5C<^KLK zJ5}ub2eqlIvDA3>^l3-xq5wx{xA1gan7r^=Ew;3W@h+IL$D1dvb=bcz^EZ#g9@)?I^bB4FKbiA+ti-0(x=DR+ z1nC00d=sHYrw>)}QiS!ojaSBU(mwWZ^b@r|7eUT6*%~SVhH6o`*ZGlb7tR*!MY#3A zp<8VU8-MprQ_oD`|I_{#8q6AXbaMGqPar_4DRiJlivNtIPxt`^%@ZWVz2qXZrcUve z&HiA0D0u_l6;a3Aa;Lm;6Fx4R6K4W{HK?Wc@Ef#!5sVjfcC zO*E9uM(d6oEH<-iWRBXpmww5;$gEU`3l{lG50nA&i2& zqy5rUGhf0~*-RhbQ~hu>adym^7&t&^wTZdIaR#(4JeAc9+YL>@2&uNYf^Xr*5d_Jq zbaw!u{1pqHn{Tzy`$d}h(Rj(FjGeUry*BomGiBSX86meDJM4h0p5e%b#?scAAsowI z>yz~*va?aI{c*~T{{tU~O%N`5p&si5+@NxA8Z4K$V_c2J5G!LQ!s6-}yz@G=%uXJM zuh{P?)&hu!>E_yjKNLIfQJ}2``}|uyIc|F{iqkOAM&I>j2Zx-}QDzF-T%Bqav8On( z+@Uyt{9564Eh?n`V!QqhJH|Y8N25g5NiH%*N$cFJ!8?4{C7D6IqNVCs=aj&sAGfY7 zm4qJ!Wu4j)C$}JX`IGaM?NYHi?1|4MrtgFRHWXvD=59Q>pIEOcf^dND7XI*>lk)1w zAYJg(_}EnBVSCu0tNO8=>sK{KFlh>?aP^g|wS8tTw0+nCWztp&7X?V@BiC$17nf8R z7h_bR@hV>TB6}aS;~xLnCP&kXB3Zb*G8}XT*0HYeNxY;$ggkOOXPvf>Tr``L-V5;;>RN^`%_h9rl zr@GVvUAX5*`D`%>BM8g{Z}O9TLrR5Ut=%hO6Sjs*UATv1i~z}na@cqCyt&;Gw!|I! zmB2&&9=&z-WKTew^{usV*FPGp<%26{W0qI4cbYSU2IFoJzpQD-;6^BDp}e~#e(>e# zl@`uATAz15EVg06WYTjq`CiGxm*&%plwjrOeo4QinOAbc?j>jQ>p3?rTis>WQH6H_ zEdn&6i?KFEUWlm?-g}@P{jf2nmOWkY)x9-%D;ji09Ys&QVHd^Xkziagpg1!7p+~z~ zFjXDfju~!#sWsJW+OyXf$LKw4@2GAT-LA2}=m#ZUYIaZlTsl^u4XNSRPbpk;+W8j)8|(b~|xzhT-3V zY=h<` z_vs6;w{Br@O!-skf`KPLG$$_op^)OPsorPRZlojDyiW2aLCMPp<}5`#n|iR zSR)ZC=@$5kR5?a|Ot4$yFfO!S9@dQm(9~^Sd7%o=cUZjo4rP#^vT6o5?BWYJPTxcp zFQ-eHgrwb4c#kSb_9$)Pvf{9IT6GbX@vLsvf;+yqV?2E|e}K0D6Tbh8C35^vj|n5k z|M8e`aQx3x4@L$~7RLWCOKby`*VaO#i$>mtL_orVxczSg0$JzziD3gqSvg4BLEYTK z_HWU)3F83nRa{aPoAr2}}vV`<-0r`F8t&0|@;% zfn{WFtZxDk*cpa_^x;|o0hNnUGYV?uMF9{DU$+p1|6-LOq%UuiTE?!n39vyb`M=7^U6#%cKF0nn+bsp+}t0iFQ?99dh8 zKXUk&X7HZV5^vhxJHYATy|}&nKb)`$=<7MpKj2q}5YJ(N_;IuOb@BYDez6N08vv*K zOXd9`$AMr3e&n9b7{|ZuzP*3p^ws*kR?R*e0M-5eet%5wrx}>J!(+DmrvJDM(KIwX zSS2Jv{WO2u6t%Z!QTGRi2SD`=&kRB98ycK|+;jE1f4O6+5WeMb{oW@9rf&ee{nEbv zmHDMyf5U^D|7jp__4|z}wtAV;LGXX!7hW|vHe~Yht^ej-{H9I);ZOOAee@yy_)#ai zHa2`n$$hmA{E5LggKo9|7(GmDlb=7KQEc$s0Ui6vvwBBdteU} zpv^^_8Xg&&o_uS;xh8{k1x_mu$SY0tX*{)QU%mBi_!APSaEsmFVV`b9rM&n#&q+&{c_i#3@^`Kzcn^EIRN{^!SS;%pEdc4Y6HNxp4G4bcl?T&0zfkl z!+l-@x!*28>)W3Be}3tIxC7Ai?-lkT()*7b^M%9r8-Ch7cX6RL@eyGAgFb^F1JtX3 z19kw=?DOqA>1X}7jJN*!%~l)vfBokYZhm1oI{;~3=e!%fh}p5p{YMVq-?OJ5;NPzS ze}H-0&Y!`(=|(>2-}Qo6TpaD)dsTi7zn_x-LjF1j`3cnGDK7`Bb1L}#Rb2R|TuRod z(M$1-uCp%j-Dw#gV! z(GX0utk=N31mv>Vg?Yj&j-tq%%jg!Pg-+yuc+whi9mobwmEdKviiZZk;w& zEVJo%M_00gtSEMKm8-l(XJpBy+}rZeXdA(s8lM7jPn(OAgSyr!(Od1^r`Ms|HYd2t zIzdmrvKnWyL3*QJ@pn{`izEd@#!sio+RK!HNwY|JXE2!TO6Rd(BVXHiO=XlQ;!nB< zb-HILWPt!q5}#A7&bM4+SD4b^&JP|yJ&ide;_DwAA~asi_hN5m&pDFjt^8bJ_LM~2 zwf&Qe)Gz;8LdG96z5z|RJAd{$40yN`Qq^5 z6`5B6Mad^OA>;5;1M8zTnNAzJ1oE4 zDlN)Eg|;XIAl*Mf!~Qdmi8= z5zi6QinfqrndD$T#904aXJJOsYexulj%dt@@x2~Q{dx=hbjeq?sE@a^)$sAACE*-k zy|k$`;czBvJKap8nMVj5#sC^_(ZjhmLUSZ7?G!o>yQSN^Bw_9LQlX2SxL|NEJB-42 zrCxExG8v+>H1Al|vV+fUI_G9OXukrGhGx+ z%lN6N`R$MEJ}NT3Fx_&=gwOiCr;{it7t9%QLOL*{yVM*M1y zw*r%T4S~W~6F|v=3}p-mXx+s#8(7G{oVE0PfKWo|CE9zvS5qm19GwJwl!T-I5 zwP>QJ3Euu?7=zy4{@pkYqLHzUw#UHvx&bv6{EE61VN7wzS_gPV;uoPaxu!+>{5n+| znux_UcHKCqV9>iGDJXO6>Zc7^>owwy_|}D9ukoglga$hWpAqMRW;-q8lx|xz%6;;; z)d9%F*rHU3*VOx`N(%i&OwS*r&;B9iv_OwGG)&p#c6d&|3k>eyc(P-KWpboWoU_;p z;ax?>f|YvB+jNdayre$wZ~%{b_jslDsbOexKFT@XoH)kb0r*~NiqlDJAhYCZ8Q_9; zJ91*>I(($J8ySjmUucqMT5h){l~a|wmTRuH`r1Do*V>6KZiGBOW7g6|zjFPpjPq0+ zA7APGfb^HKxs}d+GhQ)zw_JBWQu2}wNW^$^v+c_X-4Y~G_i!W!vdUoC z{*iD&jC_UkfY(X|gyW~|h8Gv7hTz%9f_>ZnWEXMUIENkx6G0%cn42$V2MOu+el5Eh z`Z-9~Zbs>}6PCZ9cwA`Y%WZ_?J2fll01y=7XL_p?Kae9LDpDrPYTP*3rLU5FF&Bd) zWa=dxulRN_QlE^K1cEtQZWJgu^qh1&*0{v=qsT|-5}OtT(q=k%+$m**=;b{lNoctu zZuCHNXF6@kHff+&?acTu(vE%zQF+@OS?Oyk2j_z}X;NpsEJ6omUt^IS6|fVt1(CYT z&0aT07t7#8_0U`c-Lh$JQvMGjO!JIcy%*VA%edG=A-|nn@9x>(-u7~q+|gK_`@~gG zgjzmxM7PEJ{>nSp;&(Hv?AoLb@O5NtK@ZLZblLL3srqh9k!&OxW^u;M**9{z3#SV>sF)};a;5I6`(&16av&#jJv zbTA{DHdtJjC@rgQzQA%+RcZp{V;i&&q-2~^;^1p5LcFh$KkHEP()pM=Hnw_%zL?}0OAG|6IeVU9LlKNT9S znUh8XK@4oLBMAx@zvfh3dU$$jjx46{)?nr|GA!PQ_R)n7IkuRb9ax`WV0UYcxqc|d zy$(O7jT>eWavYk4R4%l7s%h=#^VGC|+V#m0ITS4UCnMCw3tP1B9=%qPIysN!u}EZ0 zQ*SSpC8%bPK8zz)WAKv5)0;wB>>wqHtD8uJ?2zU)chBBHx9**V{2D$u98pE59>D9ry9UAY>H#X`b3+0N&mg)IL>AoJ96}kT z+{TB}y1j_!NzY*x_6Mce{y23z3yK-7b=r=t61NS~VACQpv3P)vY57`+%wsq)kSjqQ z9l6m+?dE=VNy)d&e2^<5TcAw1&a)MzJ2uPO zq(*R~oA)0}8b(DUvS%%T$J-NFk5H&sb(IP`9p{bhW6W!7P{W%oS8%{8ZF?X{A_wm@@^ZYm)Zmi8BKp0`h z+}r)4mHH+JhHq<6^vk4kmjQHQP4D#{p&O>5-WAv?0;a>+CqQJ)0>hX6CkPjK7>_}D z$wc2#?E1J2QfttzlR{Ho_O%IeE6ce>T%V7%n&Ocft{vvZJl{2j(?p-ODW?-!izNz{ zmnOSnG&<~L7nSRU>hEee%06k2bt&}K& z5<4#x3kGM?0d^hZ(NVoM+(yKOWrcuy8^|xrQ%iuF4O$#0C80UI9AMw(conTBqqoI$ zmk952J4=}0nIdZAi|K8?8v1g0l6^*WU@`-|^PWo|L#JQIh07ny0$yzRH*;TLWXwNa z^isa2-bOO{J`K6YAG6#ih-FjYPnlzi&qpJYZhT&^7s=LuP%hZ_06FJQLrI1sYgFRKVwkvqVIBNJmU-nP&TJ zS%rHV+)x`FS=F&;{eus1o~rQ9S8Z4d9yor~)rh5~uH-2~Wa2&L!^^!sYn2Yrt4Pp% zo@qn%K+LF5uKbJCU_B>-r-#1euq4K{=MgL5`W*RMZ9~7wrW7XL8}i%rT{LGnL0vDG z@n_>}IAR4Qp#u$^Qj0uEI}gR^4fhSd=;T6YwQ^co=kUG;jNx%VHDpuQg2XMeO2g0k zyOOJkrXWS-rGEMTQjnWRDhn(QqXs4oU;IQe4 z577@>X^?5zJREK64Lh~-wQ+>quUy|YhtGrh>)YP~DTSd- zU}|a2^He(K_?#y=^fcp6_P#8xn}t+(6XG4-s<5?)cZL~xBvlMG0F11z5_(eCy*xk* z((=paC-OIvAB>lyT>yY3(<_X0Zs)|=i8l&@xb1fhD!j(Fy_5QLpw?R*OEnzY@h%+( znRUwy85XoS#MEkF{`B@Ftd-?VonIwhFhhw^eKcOuq};!Q|5#T0#~{#ii0pw~Q>h~Z zAFneAH|RQh^-iHXS=(=^e>9% zT1aAkfhwG5KS-M)BgE}$lgg%_@+BfE?v0`?A+}J zsc%o}2iUJ$bfU6*JvAYAIp+(%owwWjEzxe!<4%)z4N4pK`bbBwmSG#*sk^WJqi@Jt z-DvU7YZm~Ze_OIK&Rq}U%1Z(Z*ODlS6HTG{G%DBq-kW*c0*#9-wH^@5@Gi_N)^$+^ z&rg>`COM@zF4Y0Jhxe{3vlY``9oO^(z{h1hqCSa$b|EhXwfy+t#SbBGN2+Rfa3;1g zzG5l2x7#p+-DW6ypg5hF8PZri8BpX$os(7~1&lQciu1jgpFnJcxeV#BUTH50@Z|uz zf;&sc5c^*5KSFNF9C`!k3=Tgm3S-J)V7m%9CGstqbHV32nc3*vsBa_;~>8oFe7cBdq8gZbq6BWLzJ5z3+0YAxv?pYX#}gP)B;;Ychu8xob>>e-H{- z*HgP<$~w|FTcjyzbgy5GSd5ioqgV~@^!GMfjDb)ucr#By4h6s&y2q3usPBZ=d9s?1Yro z!8iu9`aH}VIodcGru7biX)&H8py`doJk5~3gZE(WNQ{uCBzLUS>&nzp_@$$}se+g|yNewCA2JJ64&}?ddqObn)~42=w)5_-U4A%yd2mdMFL^ z2Z0?IomCz~^B+p*!k^CJ$rAqI(VAp?fDw#Fn60L3`e30#1F58g^S`*8*)fq@^u39w zB#2iJ(Pn|97()nki>d4(?jszJJr^6}SHeQ=63;{{Z_m7ge;Q=**gWnxpE5Pji-d6~ zE@B?0))s9C(L*Dx_LVdtmcPsC1DKCRej$5Kc}o%%WxtKruuqgxb=s_Ve!VOoVwQHF z%*X<9H`a(PkTMB*$F%?G3u7pCf5hsph#xoh-XP)y{<4h^)ZL($v+FGIV^|=nHB8xU zS3UNngyN)?W2KM=k|E0H{CSR+br6+wMC`Kq*g}j59mR;og;X9PK`Bw^!_-g;H#xZd zo?Ua$Z+t6k3%Xu1;b|5YCh8Mi(f%|UFnMH{WMaN22v805_JvhlEPgr!y%2{=R8F9t z2t-{GI4Zssuv<$Ad=-M(vXFeY9CSsCs%2GfrcKJ23%hzA6{**P1qd27t^~Y0`ba-M z8;ONeIw&?l;WX4-U(ykkmJkV}+43NhpZvQLA5>+FXzz^`YV2V7Qc1J9Mn2`Z!98p) z7Mn9feGnrWl1Q&{#>X*`-l))9v?=9@noI;i8mnru*KgDrTc0?Z@&9x$yhLb!fa1~C zWxQ?Y0OcW_QiE(&RAYMOx>(9c%gPW1njz7Wts|W(q56GlP&YG?vOJ^YK7Poo9#a2dp+>vv@o+2H~i~J zvHn;(dE)}2HhG3NO1gWk%R~6A9{iT$X!dh0?-OoE=JiXS9A<%J8N*pFdt$JyU1A~k zztMT<_!hScijT!f=f#71K_}-xsrXo--~E+gpoah0?fiIUi$v0+tk|DP%qQj_X0*8G zPX1}~7clC^$r>nL@P7j%RE1+i*0%X55C2tP-on?&KKO|#nGMRi)2-1Fb9$QnBG2(Z zPR}kDLFIweRcR)P@j;wr)wq=Gq8>h$ftmbQxs~$Oj&5{6o*DuD2YRL!*5isS``$CZ zuHaOWG6p;99X|v(VVWkvmbfUlX9px9%`2N|TeKZ9>^vK6I02ra=-gF& z-!gGof5pL^nAT%|jHwS_y|kTj%(;-m2GVM^7QoYssTjpCV4_02y}?W#C)DPDU%E7{{fc;OD#F|N%^wUkT@ z3X;ePZaJu#mLP`u(YsX;MUZU=ADrwQ+c;<)9hJkDaZ2jQSyeR}jybtt(JSq979F{R zb)~q=P4Ew8<0L(kMo_4(H79!oQs!e6#m>2U z&&<^t6LF_I74TF?+01$3ESH>kL!Wy{>HU1 zq5eK!N9FUKNO9ZkxJyDP4Hf7Rl&Q?a@R)Q^S7~%TYD2N-VQ5mJz`~0HB-Rp>r{3T^ zHtE%TeT|5C{D`M4Yw4lJv_}|JgP!0h-MGe^R!>u`#)G?}d7z>-P6FDqxP|eeC&NSQ zeurhqI2e<3H}I!e8>yh{Wi(#P8vJP5{Fo+sk4ceE@y83 zj4cotSa0@6Pc=lidAsnjc~w~12VqEL$@bNUgh)VIVE0stewz+xgVU3(Ro^u4*$MgR zVtm1}!6xd&S|z)+3Ottc0%Wv%w%+=aAX+)h{9IUFKy+RaWohWGB!465sYbMm^#=pa zuf&5|K##7_y#Epx-3jJY38>Dhi-P8a%<>{vG)q{LPk>o>_@6I$J~dU`(~&|R9Iv%0 z;T(NJq5g#p0}lS4wJ;}# z$x886klA2kYe7(8M*;nXYnPN^t_l$i7PHzi(P;`Kz6{lh*ni4lB09&%HL3D8r^@)a zfdGm30T z9+0<{(~u3wB<(2NP(QG-OW~Cy-R+=>t?ayU$yzXg+_*lvG&QX8-tS1D$#xP)pu=;c zH4=U1To}WtGT#!&ktOq8dV}kwYSr4m>umzD!%Zjc|HtEy7AFBWA+gIsp(Z?7x-rYj zP*5Rl(u^6$@Tjo0ad!q|6WXrxmae{H$-xgvOD&=L5LNAtZNuJJ#sSu z2oqX^fRVT6MH9=iwk3E(gX`?HXvm(Gyux~Di@r<9+MkIWWM#14e^9ilDl+iXjVF60 z!4!Qk=M(ELelZe*`4MGI1Yr^U#j1n`wt1#1S?0ky>1@?aD6#ReMDuXq6;p!{2A>5| zq6?Rwb%bP^J?8Du= znMO+n=NeeqK9|01R@xIs@I5iS|I;0FX6W%zpt-!xf(Ev8uJB^C)6i>NS*J#7xVexH zUt*W)GiY>*-42L<6sF)~nPUj8X{MlVl31BXUfK)k=m={GS%D1-8hI)Fz95ZcOJ z(3TQ=bP|;_--H>Kr#B_6a*K6Oo5U9fxxK$32e31f5d>XNJtU?xYSBAia(6>2+|gT8 z0%HgseY=s+xhaLO6-=V0jvfQw$ zU-e_(ndAvNgBq6f7K9Pe!H3I_R^eO1|bDqAC6U#r=aydgW`& zf@{yUwjcQkW4*YM9}j-@&UP&!=`qs1`XwW2>yq%hNCygPJ=C~X0Mx;!5Z<%jUPQH7 zoQ`Kn3=8K~rr6c@i6a3=bbtF;kC5b5iM^@Q9q*uJ*5t%Pd67T# zYO?#Spct&O$NjWE&UG$t@nhJzC11F_lx1c6ry$a^b-Sgl={?G2P^*FnPrDQ4yvJE~ z^TJ;wsxSgF%~kTe619^3Llw5@F^EWE5W8MeGJ}5KSzmMh?%No!=n%D9#=m8k?J5LMd-^EaCpp%YV#sH9N+Z#@#U|-u36c#k7BHLrxv;3s8+pA z{8M*%P;VgYPQP^t>78Wruo$dm71gVL8)0m5wsxhVPl1|DQcdYA=U`O!sVa*&Q?Gbq4er25daW^MVizn0ji-nLxOV(i;#=SwbD3abpC>`MEGd^2(H{usHH10Mt!zOt|L$y02bwW_*S)%Ub8N1jP% znK+a$R5+3Kt-;R@@vD{?4o3aWSFTIZhjR({rPlJ+5UC?*v`_}Ry}cYwX^*Jh*^H|#Y^TYAQy zT>S1^tC+GT(#1k9>WXY-u23yuoY5!&W?|r(_@C6HE>X=Z^9hMg{~ zagB}+Q|>3@qJ>IItohK8{~fta5lLd#h8`E!Ptqgn8~xDgthy;|sHz7jxrkG8`vO?` z6lt`lq8A_hcqXM19K4Vq()6HXrb&NW~;+(egzBF$sGt($hKq&L)69r3gO`V6FeH}<)_yA3w)j$+3 z_PDAh$ZpN#C*1c_QT zU=3FFuI@ePszyWZ*d0iqt zBWMUwEm-1S`T8i3Re@WnalWOF6N*DDCf~q7!<;a%?I%=4*p~-gFS!7PRLIlaK!doSmU=hY}) z#{EAUBp(U$QNyE=LeWR>8qg^9$jW7$UutLm{+9hyY)$nmy}Kx|QiT-MXV0%>O#0QA zWlgsxZwvyLlw=9#u1rX8S*(`L;>ho9<7zdaS305^V{iJZv`^i>NSWKua#Taw@Je0C z%?tpMvvT4V4AT~eX7URSFM@!%KK|6BxCU|9+sl8^ z(dFgk&6p$9&BQUR4L!vGDI{dW3*n!^xZVS4gZQ_@DRQ^~{jB1!lCTRcLmYpUiw-YN z%`VS?LwT}q1k(i=uCJqcG>|WV-%TMbB&dK@Z~+ejAZUNE2cX_;T!3t6Yys(?w4WZt zNN@3`tgx^Wkd&+qoKUv#4IrDKz%)xyGxqxSF+i*hC$~bZJ{sfezO0C>3B8%`HUoq* zoS^?h&CclaepL%#o}q$GdQp00+x#n0lLK^z#HnVp#Z0|m+j z7Gi|OP1^^{+LIp;%(3mCt>=IHBH`UtKGs27art1~AM3doM;UD$u!kNXvm zu(bt}NeIOqz_Wx0A^lPLUWIG5%dMg18^ zZk~`lm$bg@`K~_c&pkSd(+4u3K(Gfw<;BDeh>cAJ9T*;kx%<84sWLC^^n-iv z45Z2SBko)8W&T^RF8K&RA`BQka(L>Nz?cRIa{$5K86@Bj7(XL^#R~mC^d)Kk!uHjD z@9$}7e*y+I^uqx}+q8^t26w6J_y5V{xj_Qzz|jNaTNkqZZ&3Yi*MHj|yubcOeyT$? zNNxoW0J+(r-Owhq%-gqA>EHtOJXE*9m2KyYW0|jWGj!LTvW3= zmJb|#3yG)!69f7`B6l}Y#Z2Z(H5T@ar}m(a8NFAfoAQ|Ht8<>ajHF?N9+i!tV^hA_ ziw-aKdX=0u4x4Ei|@uB0+)*731y`pZBwUkac$=z z*x1%23=jzU>FJ=@G&L%8)y>*H^0w}EGI3MS%C1KeFp*D(dSe4ryz6TR&fuhEmaM^N zXHcV3hdh^(T;B!p*@}a%FibMN%aozN36hx3yT}-_7B@U)#2ZC*c>^^WD3+W4$Jh;| z=~zc((}SNJ?!Uug4~)SJ=&X^okv$9_I1J@B!flznvf8&PADJM2%5B4QR%${9{O(UTj5Sx^n%q7NS@Fc?E7ZU-TyYj&qkI~uz z%PrQ^>!QeUZcHhMt3M_ir7{EPQ)*T-5gR4lA{MG;z5TEP4VhfJ7(!H*D=F7}N`cF# zG5XKBZb;;eT68Iha!X+9Iv3=Nz6QeY^oAvVdw~m$X*YR{@B$)b&lXlKMIZfdVkc#BhsF<16 z`b$c(fk+8XKb|-w^J@e2dfBJjc8|r>+GbmD_WCB{1zbafRF_hng;_R6ZJ}uG6bsQI zv645fcpW%q5N6!n?0BnVERB7+o4I#9%y0NkO9Rz=pji9~38Jo-z;mxQ}auR@AeY9R}S>E{^Gy`;9OMkqNdZ0_Jp znDz445w8PB7{glUbC*cmp~p)zCyh&x=f{fF^x%enK9JU}zRzUOcijYGcc7yi+)y?6xSr6@ zXlm(GN`1JI@AHG2bgi!hBNLzUL6V%?#qDd)BeL$l;)-CmNXRo%3xgtZgpSEDe+}k9 zi#xwWO-}dLh@!=vLz!-H_%iQ&228Tz)Y~7xKwBk%=Su(ZeZ!W=jJnKJI&z68= z$B~4Drje@KcW5{?n2~R$v;xvMBl4vr!~P>~Obu-m{yM9sO>}G^a_cXE&in}J;P?0x z!0mG=9)toHHw3(IX95{vIWOM}&w~>e^L=NP$$8$Tj&HRMUPZAnxC)+DR ziMWeUOFE}=SY3WgK=HL{sd&~qE3Go|&o&5W>Si@`ezMqN%4}U!1hofj&Am1qXAJTK zmH2NOme;dB{=%p5-Tj9~$-gs+3CZ5~p}V7II>XEO*rpRz+m@w;mE$0STw#P_#kl8W zd+D)q)K0uZvM*33!aDt?l>f{izXW{JF1*lue{y)TLO8N~0&iPg3;}e0wIhoRMQ0IcVE0{x~ z*X?*y8QE`ZGh5>|uJ{^ulfeEW2W$dK1}kp*_r2H^#`rB;1yuMCcyD61oM7waIKKo~ z{^$TV%1}z8SHDvjLhcC(*Z3#XP$H4u%2rL=?u%`CN;7-w(F}xBYU1(DQ|K@NoCT!5 zG(Ap_T?&LeU!a`M>qrv(HLd~N94P2~_0r+D;-Rs{a8vpi>~$U`Y% zWAVGT-_=fnmvJFx9q~Yg65W);u9GF_tf*cE+dpn2FY64A(+0S|RVSJ!b|L-C09DxB zYIaGMq6(kKXJ9X8WOOrZ^-5Kelnh&EpM&q8DbGI_a>Lk_Ym>pKn}0{-?!c-(52%#0 z_2zpVz}Yvw(v1D~H{aC^7`l(Pt}|PAKC2y@oXZs$1K*GpY`CjdCP+|JOaz^lB9Y(L zKzv49>T`Xa>p8;Bm_AQ}DxTfwt4@&Pt92!NAR6TfYo1dc3H54l==q>MKm=6X!lE=H z2BQC^4=ycq$YC-D#p=+wnB#N#$bkr6NCl~^60jxqF(Nd>$TRw~drjtO&ZTSVv_ zrs^&)6juHVYRC3-0!OJq(PZarAzWrSU?%lmih>U>rR_f%cIUr+YeB@JtJbhDs&O2` z3QdMl3l|@#T_Tjs#OTS#!k^!ar(I4aP4A~~19|*=iH0+a=-ZGDG_CY9x}l6G9xV~! z1Li|V|El2DA+tY41eJblmLT(nray7)C-%n}WQWmJHRkgaED>G(9&3C`=l0EdDSCL|p@P1rm8|ae%x4S?f?=>uLL9JN2fB1;7vLptu^vj8>ZEp#Pd7iL^57lZdy24TZnpT}wGs>76|;er z9Vw8}8r#~mLheAe1FfGR@u=~w%|M6w==;!uGa@0}>b|Nk0rd>tQEznbnAog-y|nPO z>n~Q@pw#6W{xT}uMqRlxgp#jkLtkcc6$VjC_7;}SR4J|h`Ro>6jmSx?^MgcOO+Mba z!&wPunIkt%_B*q);B}c%Q0Z(Tkje0C9?4}$6&a7M&s0`{hieBghB+zf={wK-iqrQ6~$W-VvoL2Tm^Zx6Y$BbAMW;KR$?r8_}9V6zcNcDCM~69bL4DO(6&sip%@J}2nf(cyADMv_F7Q9K1iM!y$1 z2GGbzl6!&9f;+XwWr%qXQW^>Pzvr4Nrkb-6cT@$7{tr4(E%nmvVm zG5%bMsF^4+66c}`Izi3y7fCNq%q|~GEfG8hx7kNkM2)tiFZTqoWR(b`I&IL1w2$Xt zrGnW!TR5UDhk7+#BJ=;$Pe z3H$yoO1rn^;bq7Ei^EQwAAiw!$YEwh2fj@7&QqeBS3T8lJ_&p^=ux`y;L{R=m_if( zpSo{K)3;3iLjQ>OO@_>zit&H*zj0NLukC@gKZ^E9dA7JdbdVm|>0VO6u>2dzF02bW zG=Tco(`}U|ALRjl@~Kx7U`2iK4R#57dez_=41d24DnG-pki>B5hEKs-s$ieGwmDP` zY4)(7e7u(wGV9Lgp0wIz(lma+Q1_A0XzeKGyn)mXS{$^Ubf58N_z^Kl3ORvRiQQXL z&hl$H^gSu&etND`3uhc@pSz^^_}yXj>+*YEX9; z4sjoPt6cj>5jL?s;C3PO?ukJ&&VHCr0h5+GJZs*({E%P4J{asM`Y+>Rc!X0^F#ci3 z>Q$+3=SDC~P8)pJVW&5`OjE`)IuSC{VXn|xpM?Dt)HmuuOEYHnl}m(V=h zuh7MtyMWV|77xeZYOs~N&eKGK%Soy9rf@2OzK7tA1^wZNwy=kxPr+{oG(~xAaV#4! zj6qaLB72(j(vY>H!^THD2$(I`4~cY;oWpOJ>`DEnV2u8a4U zqvp8koh@4dml6!fiHhv@gomsLrt(N&yleJDs)KB3vV9hsI|Q+G|IU$o|23|->EPdz zr_~g7srL6v;5rtIYt=$X*@?$X-ZTswai!2cVwI!gX)@CSGCDuze8$88ys{po3BZkc zo%}Ad%ts&h2X=K9X1pEoK^>=-3H5W9xO9L+R>{-#>6UbW*-@l<# z@Baw@*+-NZhxb!@&VOs`cHEI}Zs~NkxceI%7ZkH=?_7KY0&hlTYbgPzK}|u0l8STb zPAXlRqmQ*BgN6$EXNOzdbt%mxZn6cukhl1Zi^2PQ!H)Uq(me{ zKn$gTjge$ew#?Q2vB-ZXZFhmQoGf|WFIBpp+sRv1WSn#gi(HKxy+EffHIV(8@|`Ckx+`-O}X7@@9)a;Xud9il!99^Emj|p(DQ}qf#L8 z+SX{@z(CNhMct={E()uq(^zo5K(st$5+widNwmB${mDKWZ8>;p|LIv3AfHIGCwTP#jNt;_yo934_J zPk7>F1?3ztsnd;j_-07`Ke?Dsm$^-hK=*PClIHW#(W_5?-N=WCUz*uA;^#Q05F|pT zcAOn+_8ng7wPD`9(#XTIbUTVh%i?EWA~D z@RC%A{1sn(3`@d&t*;=2O5%+YcMo#c!QrDk+v1vDlJQA;sLb^TAyWZUlvt~EXv$8k z`t&AOd)A;8hIhka{ZU)9PIb&%%j=;-F~fe8Pb?RoeLTQX%q7o4OQW>asUOt)PanvW zNg`_B+@OjQOG%VCqO)zBifcN3-|tMak3ZOn8t`>g&hvuC zGK#g&_G~7*9xKqRQuVYR5zS(JeoWG;zm4&X282%~sR@&Rv`RyobJeOL_vfN_L^WO??@juJN^A?YA=7bcG2?55K26Hb-$@`K?6 z=MyKawPcO{J$4O^ilKGQDp?j$8dyb=Qt}c-7^MlpEP}q9e5Z(a6;k#_2L`)tcK_Y91xuW|iB@!m=Vkf_eQgZ-2;lOv&xs6>I%Ag%@R{JB!%uQHL zo>Oo`S_suyHX9kQTyJr}3*MPfGpH8&Ly^{Nh!;1~0{Jt=(+LTThdC)sDlNEC?S@0W zOgqR|Pwve;vzl~lj2qfXJ0dDeFqj>R*PI#{%vgR9*IdE#e-wXobe6z?j z7%4Rb7)68>Wt_u=U!sb+AB0AooUMh(c|~CObrisj{-w{BvVtKX^&M42JdCD0*Y{sq z`ZOZTAA3+JzA{QikrHe*Sc{4v^Fa2#os>5E39XE<-B~#ju5S27Nhv!v6aF{jd{)+f|f|_2jG3lQ|et ztDPI9FLGBOWKJBEL?6qzy#mKFf%VHd=VF-!1sxF#gul=zsozm0?o6Ma5`= zGRf7g`3MfU@t#N0Oq0JYq&nFOiI+zSF(%9kJ(mps2$lw>{L{g$X4XaLy`;gz^)KjD zd$r;n(_#9V<6GD)ayL*?kd(XZU+}$a7$fQf>@?3hEe7sXJ|r{Ua%HIG4pPngZ1nkG z;|o$bVQIw*JrWrC+D8e#qAN+)Pbg=o4UEF8|I5WpiREqdeFtsR&=X}hL`BlgDb{A6 zsr|WiI-Zl%pEc(a1Z3!Oe*T%!{uS|@=pO7e;g@z zmEfX`V&w6cQ-`p{W46brm!2S#y4OxJSFcmnjgK&^>u=VEre_SCI5frRz9I;T*haek zH8q(M=sMx^BwUC>`?hR({?$oN!!2PMd>)fZa^kqi9vCp`CKgdStuz=*sjyVUGVwa%kgWX6!|ifbe5mo|B{ z=mAu9P48i!@yDEg1^k+sKh0POSuXfREltCpO>mYJKF~H5lC4gGVXhgHCyLDDjV$&o zY|@oTzPcD#9MUcs1AP^ph+AU2X5~8mLKRvL3@QXJV133=N;^;%~W3iOl#W0Wm^ z1r8U>$O(%3_1M5UT`64*>ef2H#0;dc8=3<3C~J<@fMO3t0U!0MAd}e5chfU54@*-l zjMnvD;V&R5=9O@1-Ew9dGPZl2U$7Rr2;-4!0#|t=9|d1q+r^}c#mUQoym*YGYCtm* zb@p#gE(^lG^nTvri$gYUfzT3Fujn)rOVYC^%+Emmu8}zd19HM!^l=Da>ko_eLfukw zD!o0+&&F!wi~F ztD;T(n^O(H$D|w)ba{_a&Qr`we57n|%bFXPZ{$%n4llISw5p5y+u5d1&=8Ug!Ar4V z)wf!~ryYDGccueC^6n)cGT#9Qdl4(+g1vp|mk)3_S_NEL5V3Qoj;_`rdN*_IL=mk( z9mU;OB3=+aWBERJO)gil=(=MHPrNO`(O@{2WqTM_&$z2wl13)Lf=VOgkgF|;8$=sE z&Cb^I?`g1tss+`bH#|AhS6jfnu#eX9?HUN8+f}lsEMfch>?-HZS_DiXbSaGT66zs7 zp89rf073V$d#Jj1g?CRfrpdX`c)|wG?5aSCd@3N7GfKSV6s~cempP=iP6a2r`6E6k zJ|y~5Rxlwr9xo>%vaq3l4fcSN9%4sX$5#`B4j)OtVz0wSzB3d6prGHXDv0dJ1y(pD zlcLj{!bHly3Bm=m{mVjTwrNj$V`sWla&8p-0`b^}?p9;0=VjF7y7l=Q-sS??zc)T( zD(DQm5Ei!1pPm(Oy8=5pS0tyy?P<{$0e0hRD-CT|t^*TE4=3^GT5Hl3D0rud#T)dv z)W!j|LLAu6{_pOV^zP&-!W5Yx%c+Uf}<7InKBb@40PsL<*gPODRr9j*p%R~ zy(YIj4jiNY!+794pF6vaMZKQOd?>r%h?ozSFL|F)$iVgv>= zmt1Tg@z@*a=m9(&{qqMF)^Yz{4(5YPiRu?f^fCkjG$X(DSl{bwrxe|Vlx<VDY`#d6nSxz z?&yo1>=9X&0%w=Oqqs>g{W?K{9K1A~lj>Y+Zu0N*Fv81F{yMZt^ZT>qdh;RnL^22R zwI_IrtMUMg={4#wcZaQm#p5v6_4zmwr6;C0~vc5@FQ*O z5~m!8`6!-av)ZAB#kE9FBBd9R(vVWsER1*J;9~g-Ib3)kj64;c*1%RMGi-LaPWn4a zCk^WyVKXJ-;RXS%y#m#$?yWAF8mGypI5OoDgM8pu)S}^mdNi~bx#*^wZ&j?!jNm;slZ|DWXD zLd?>L?Xm8j&*Oep+DkMx9%cFmt=m9K+5Nv@U3h5=JO`{)YC&r1HyS1jzT(k5ER3(+ z$EkrBBaYKKH=%Bx8I`CkzUMd6D}29Ki$$*>deAzi;Pt=b!_m#9hn9oTo77y1ZWSPA zsegkH?EPNr>IK(}8eLRPJA}x@X9K@3_C-JC$!?jzx4&|eSU&$CxQEQFl^{@cbFR@B zpCW&EgKia>Fck9odGT_-w%YH)K9w6-yPTs4O4czKglQ1scH*U7+iZTGJ~%h!5A56Q zt`rL!hh3!{3?G>}wI>{pb<=F==FH!4f^h~^OOT!232+B#jE)OkrPVgjAxyW3(nUmU z9*GQt?-=XQh)hU`Rfa(+JKxdwa5yM}Wk?0)qqfcX7G5IVm#YVd!=w|dsQ+tRV<$lb#Fzz?Z0mn^5rC^3ikPtjn3Uk6Zvtl@x9`3B8JB@^e}J zeyH}76J4!~s4fY-Kt1*)TMg%w5*;|&nV$;s7u$*T)PtLcO=X3^?9 zB))U{|^vZiN zbFg^LOVnBoZtqiO`YTO$xVw>Ivg9jl62V>x!Ra; z5bf((Ok9f=0f(UiFFsn^{zic6>RqLtr8!fb1$NVJ_>ykoQeu$^%WZUOsWn1FX;Vr6(CDfW(``~%iVXG|yBwO4a-3yZ||pCqb<-D;2LV-S^-R6Ir4@U`>L z-_>+_e~4~AZ_cX%i3jOY#EQVItqz6tq=d_mVmM3M{L}l|ZGnTRuJ&aifp;I6=61YY zk_)!u#}RxTuo)zoZaNrfl;vz)!W&t~vD%R(HS#8a5(;iTrtEcfTfp3FqHo8Nn_W95 z%{V+)W6cri2Ao~7MeJ287Uic$;TymAkvVdxL~eVU{I4c~Ss-7ijX>C=tp+J~!%K`< zr-RCHmI51nO4YV}i!bsb5Q6#UG#D7(I32;t<#C*a(5vFU$+l@rGk9lMPjFw4Od!by z#99fXa2_(RKTc8!yR~mu8{we40T!L(Svl3e11a9!5o>(u>ZLu=D8`__+wA;M{8t< zkic#_EC`t?GRi>gh>qlXQaL7i?LE`HDB)YiyGC!^zt~MMHxw5UV8ZZ8TRn)Ce6OVU zDjdsHuXHPZS8F8XhafyulT#akRSu(s*&FEUt=mWIVq}wZJmSE{uU=~Q_;D7oe&Fdp zs%Eak$0>C72OY-j9ZuudnsUIzs+O~))WGtiro83j38iqCe{*#Fi@F?ynlrDTj5H}7 zF4JBa!Nf%k^s0^@O$VMd7?4@Nf-oe#!E-6;+&?RJ9>Z4FBA9g!^2j8HbiOKY!a*$0 zAk!W}1LD42w=hZ9yLPF2Jo1r8$rCpZLhB`4m|`Ai?w$Ek3%OpwZR?XsRW~}Fl;KLr zM4v!c6?#TmhGH<%vfFatJ~CML!{11~V7OUT&H(iDuOx!*nqoclt#YKvqUW1dSWg5u z_wR9_il%qSK}yqmL^@|-6F^cFHwi4vlCpnFpcNHQ%}VQTW}cQ84x@f%h?m6c%*)Nz_L{)cq% zL^4*9!JLULU}%b#by>eFzPc9<%$S!j(TPMzaY#wa)fiM(FSu_O%PKvuPff-aamrK} zatn*+zz?g0&O0crpzm=?8T%CJmP4L&#UkK2sn|7>?D8Ql1|^aesnP#B2X~;dH1QHN3BIhcr|w%O*2@Q0YO|<&6|MH*O1z& z?d5*Ackrb-z2)^*V!^6x_1SqC@-$?}yX<#Y- zcs@N{M1W*4nYDHe0%do~Q_+m{U-;o=!-B&qdA08PKAnZgP`zGX@C4ib?mARdFud_Z zbHD0OP|~v)Zm8k#3M7}1Q$tJOIsXI!z$;~JKo|rB3?Kcw zz$ETMl(Z@)J^?+A(c}vVIg2YB7zHpatsq^VKMGLR{xX=Dkopu9|1~6T!fPR(kQ_<` z&p)s`HHuOI;tIjj2|Nn~NQF~mY6Jc0W)uGd6OdJ!`q+{mPSx9l0*XrTilf6}`3E-kt{6p4bS#iI_BUoXrq(vVZ0|VTOae+GSO&0k5Bo1& zINX|?9i7Zw+nTV&ZG0ows$5dc#RAxt1hR#sL{Tw0(rC?M|)ZILCz&*kGj;Nt+A6?Ajk`-l1WGa}RUxMXCN?EFXX z0gzf$M1&Z?3CbG~m>LT|FgON&aC`{%_Tzv?Zc+_sehV*;>E(tRcw}rkVE7Ji*zTtc z_}IS`27TMtgdw`y0RdhCjOoT_25+K$jXwN(ZTz~w|8o8Xh`j^wAAhGMmsZza9QdF6 z;eG&5WA5s0dB1Hy^R?Bmo{-1F2XYO+{bgAL0Qx$hBh-4y{iC%EabHeKz>Oz|hPW!1dsSr4@LeVD2FCO9ve6 zcJ9|arGXPX^Sjlw1}_K5gp$%k;g9(pS);QfP>Vn@aSL<~U58-ye9hA6+moz^u zK$;Hs>!@&V3aF0xKTM{Ouais5gD7tb%7&S%fp3>#mdLD3(g38=d9*9NFR z5{UMJyM$1EQyD)cM?m9EkPc%2aNC2GmpXR}z^)X=Zvj}JX{HcBGT0>O=Wuiggkj|{ z9w%oQ_=~Ki9XzZ1E3xTI=c8Wcsvmn04KG0cfO1bzmisqNEAYpO?I0bLV*I~rWBx9x zVldqbin#>5bAw*j{Jb`B|1<%2eHSm|9T3eOKn?>QgF3%%ff^eED!+&!`kXU$>2d=A z(vbUPvJ5u`0ImR&jvs6x$GQG};2b$&`CnVCx0RhA;K7jHUmf{gXyT87BfcIU02Lb&8K!F58DkH6C3DgD=ZlaRwqPb9kyu*Vr z10bV;<~;DhuURVXm#8qlFmiV83vtW2l2dO$Tc>?s&$V|vlMc7dFBETgabzcR7WtUN zV5#rH@*}ID zxGF}|kW=BUIr)42s=j6sO7Uy3bb2KdoReBZq`@vmMBWn7PS=55o<8jXiM*fUB-ujY zXFdeO>Q}XovFtBSlQz}Tv8$bk`FIA9@lYQaCf0dNI!|0`7#^eDHDulrh9|JB@T4Pi zD@>8|ri|y{^>sOL5Dd+=%Q*2XXq^rgq$&k#L?|LJfmMm1cEQ8gQ!oU%o!#9fs)5Ej zlL>=|np{JEA*i{TJvw+jLFKQ9H#jr3FPqop&Cy|6diBtHkNjXG{8zK;i}d&VhjD$H z4~`-I2%Vq4Q8mp|lb5}awHEXE7<>r)s@)PaV3&d%Bj|ILFW7?9*#AXZzwuO>P&UmRssE17MjrelQ&oaP|@HcL`k@ zt+$1^ROls#v6FlDMb4bL256z*8s5y(?fo03*7Zv9Ng6H(OsQ_0oU`(dzbRIcfHd># zzgi)0F>Jrg873>cumW*f+pJ`Fv>tr4=N>PF*H%jx<&?GP2R)a@IJJ3BD)_T-8I9@= zaarqi;wsu3Ru=e8}H@Kg7&G_NxOX@Mk z*iT_JeWfCAnkV&sN_UHci4g=?awyxPNg*i0h{4Q$%-}|dW2AVf$fN4q6bk^@mHjd7 z;$UAjBdPavj~@csEYmm?vnUl)Q)ijKo2T>R3QEtGva0isW~ATf;Vlqd;Bp}dodt}Q zW1)@bn2&-#PIHc>zWsc8WtyT#Y!}woYC(*S--Cd3F}?X?) z?;tiy<`)%P=rC?xC2IWMuVl%+I_+$Ghf;mK-E{SF$Mnk|=|U*k@^!HL&2)d^Vv_El zmdY-$syhTvA`;l1F&o-%jCL>egLF7XdEz`QgrmH?GI-2knI=;g5$EHsUTtj-Vsk&@ zZ2NKy`n+&bT^+qu9(2v^o#YoU6MknL+(e5cW#vxv2-^R9#7Fw?Ro)daGet)jfbPs3 z$_*RS+}v~CC-T1Z8U!Rg8c_NXS2A5goB^=ca=nX`DSeKGQCks3|7v4XU}ob%%bB$(Z}(#}e7aY~@3d@I9p~S*qbr3x`PcMZ?PI zl5cSGkt){jM$|G~Tk60}6~skP9NY_L5-FBHtf?rbT6LVcHe367Q?Yemi~rzbB8amp zRH`s54z5`8BK819BHu0T0kY&7(RuXua#DeL8b2Gn%BgZuYT#lSDvYn$NW;%bz;Wtu zY6D~Uwf$rW#l>bHL|45W!Z-02+eCk(SY9jHLvh@~&y|ZWE)0A!ANZaf_u(5&>HD%e zl5a|h<=nUt!R0Ibav+)G@28|ER8t?fSe8+!kjSuy_0o*Tr*kfGW@Wy;y5-*eBiwVOT%y(_C6?#(N_dvcq7P= zPF^lmO zVWJ1FAuKYyzdKQ$v)>jQC5ZOe`tljsnszBD3pNHQW&XUUBa$qh3K;r{E%gtpHe>+T z2F0Mnux>@N?a>GZI|$b<_pGnFG8}HA4cO_kgq44*{aYDr=37^i-+p{>eDKB5aD}BN zLsK?j%=E0Td4J7>B>aSG50oN6UMhHon6{xNHFr1}RtkX##L_YJIPx=W5Gc*h@~;s{ ztLx{hckXzpv(7`}j+}>g^&0oGF~#9ErA*9kCcy+~i6@ks9Q0*tH^-M$`FW0s^Vf-3 z)K4s;I!vPaM#o~$3gpB_L@;{){@c2*OLf^0C+t}-C>$8s;BU*^Foi|no>v4#?~T~} zY=UjO=;4lC_4433e5tBV;mkn~EI|e7? zda4w8`XYHk>$|y?Jw~DI)fGgGhd;l~L&J;dol=fQ^ZYe%Ifvk^Mf4zKK&I8YK0*K^ z=`_H!Sw5xuFLgnt<%~(QLyb}>#I*X~eRhKr@dD{{z`!4>AISt;2ww09J3Rev#-|Gw zAMDr1NbSf_%Vc`EQ*F5T4~I#WO>JTpI6cVkz}L$LR0P(v#p-V_R=-FyPNNIO9pGje zDgAeeZGLAXkyu~_MX3(Po{{{bH*5R+2ljocvX&%Kd!q@BVJ|1SF)zHAY%=&67BP#6 zbThRIVs1C=6-&kqsb-T?{#N3cisNj6ifDhmzlIMpalBR~*@M#vAF)vEOb$~PoH&$c z{!5jbbY{g8D;cx*i++EM8p8pbEmROH#JTuHQdqV#Ot#d@|6%MLf`nn99ox2T+qUig z+O}=mwr$(CZQHgz|7@nF>TTZku9Hf#ITvGw=ber<0;Mg8v+!dRv_>V@j{{0G3Uje4 z%Oos238!o2jq_iUU)acJzhzw1(>c2bycaJ*V{HYZy)vDfz#Uss-D#yajOE$RC!zh3 z^fJu?i-uTF9Ty|#%yjy)7WP!5?9=WwCOOsn(HtaZyG8FT0I835^zFlGmMaeG`0iSA z+EFqbrunCFBV$2zc5#1~5AwqHMn*##s%_p3Y{drsmX?~QH}0ZD_$zUjeQ7l!cMBeg zCbPY|2Tie1_g=l*!j=;JyuCIb61?vaVxf6npL_#Egz^pOrKia$P#K|+40%c&Y@y^+ zR?RE-Pu5D^>C5JK-8Ci3IdQXe{cZzQAd+Y=`$#vR?~Rx<)l93(97JLklCxi{zq_s^ zG8>BEgW+1*3tQ$v0y`w|LHXg>@KoDBi4?42-(+(!L*o-?nDB)CrjKj>6%>NQB!)fmArJ*};-SBm~@f=+lHw4@O)hH{(Q2%0JwyDPfga~D~xR?Dt) z9T9|NrlgNTZgb<4_)u4<1ME39`#NY4#aJ>^Ua!d1&-i;bsk*ho(tQu&t4MsIlfC9k zcxLvKPzSl2?0#r5+o|I_QA`s>c0}0oa_-me9<`Bu6N^1bV_Wvjhvuj6V481ovSA;> zV3Ius(jXDmEF)Qt@~sF#_oX>2t96$4n2XhAt@o_0{w1nIN_v03_J@TUq1@BH$j>yC z0!INwA7ZY7p1yhOjmq4IR@+?^{QaR?eBQ+Lt!e-yjS_UmC|Vfd@!!1 zCp40Y`$qd+qe*7>0gOR67sga>$sKR}6y#$^i9{YlyOH~8iXEPi8rtVgwvzMYOSuCd zqPzQHYfdIJ3SGt~S!VYm21KZui0Nz%9)>Sj#tZR!ibgppl%NWu6EU&4orWSs|%&&%jX}g=Zj5UzyorRZ!3J zZAygQIBJ=~ymNRi$cji`jgv5nc#zUE-HNYgS5#1t4m@O{bj0#i>D939%lz^me`xls z

    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 h`KZ zKEMH>R-6$>>#DB{qsX`lD3I)On>&CucZxvqQR~S|_Ns|O0_5LLqSGBdCs!H)@ z$AZKZ$A-pEMP1c}LtzyuFYiU=;mV3;pj5XMM!>v+MzXpMe-9PT67WeCxb(@gNm4b+ zYWG37%16QjB{jhyM+TMqe(2W7#Cx1{q})0~lJ0+`MPO9JR0eT{J&J^qz%~ND22erile%0BRN%{^=|L zje=110mK915`_y77_KSfw^J&F;mI;CV}e-N`*|U^D`g-KCQ~FX{5=ij|?ng#@sc5T&nUb zR!bYv=+-t>U1X6_n_<)Gd z%_tMQp@D@31cEJy;z=N-21qIS1mrZxVgUZB4*3TFK(8%C)WezP6C3t~=>(Vo2SS@F zp{-(izwHR{)bLRSS6A<<9$x>1QGFJSm@)?^ue{tie_Gtf`8^vERfjIv1x1`=i1omh zLRy|9ti2fxz6%3KwKBk)txa8a-BCH1ucLOKFeJ({m#@ELig^#wvEppVzo5^(*0`xN z(G2Vp>du*=j~$u3G$7UG;i(=Pe}6SHOnMQ{!q&*1W2qZteI0i7!6@wk#xHER; z=tZMv^SfW3ygM>^GJW-YN(gxjJ4t*ZuScJ*OG5@fKyee;RNw56>p@JzTD8sSqsndmgdbOO1DhZ#g%AQcLCfSE{v!^u4!}H#m)k*0hd}8|5!;+B*qW4(&>S*u zEv!H&^~8>epA7??u>U{oeFuD7Me;E1?!c6zADxFBh)is`r5A!M$&L~gBgu)=P-N-Z z7LoOYo@B={4!uL@JpqmuNC+XJmxOY3NJ1|O2}d9hYT!bz-_Dlz_7%xCk-7VSzxa0- z?DyWz&d$!x&dkot?)uX0XOEXo-?Z-KBVI|K8h_t%&wk$GvqPuL&5W^ z!^>~9W{1>$@%e%+C(kXfdgtar147^57(C#MXG$)Zy&z~kEF4~a?5D+(x-Px-Uzfi1 zw+%mhchd9kg*|tjeBe0)@A+}#tW~#c9Qd{8kaJJJ=)A`+uBiX2IPguyDd*no-*?XV ztM-56i8Cv{d2_qXl~dn;aPP4%`I`4TWXl5^H=Q){unjx?Hor75e7A3#&fn>kCqMP> z+4abMUp&A3-7AK!+wVK?wVNKjf2sT3l}B~AH-2K<)+b*llA{@ zTr}yq^%L)Y`RDiN_)ZLtdF+bWQ(oBp+b2)HW_IiQk9>OE%oR7xe*L4(3)U~ccwqK#k9o)3I`oYRKQCPHVR)Z;htIEaynD_oPfz*y?4@TtS$}h--Esa+U+yigyl&@_ zwMVXb{+{E1dc%|6tE+m|L8GE?&p2z-XNO*NAAv%Rfk+R*_*ol+-EMn@~#W#S9)sxap5JOxZb?AarY@mP;J?H{rESJow31r z@ezS1D`p>NZ+zqKBX4{9&dC0qFKxMY_V{V1xp%v0hv}Pbt;5}?t$A*bb1ELVa%FPE zyW^IAcFyt#cMsNEKl=8R%GXMs{r4l6Z}Hsy;hqmae$>0~JXf^+_Z^21zj0pkJJw9q zVfTt>5C8ll;njWXEBCwn>J!$7<(K!pY1L&LPuSzY%bvab9N&l!_LCQuU2^)&vaUOV z*DoFZ!#8(6w(*T`r#*Cb^N_AlN6$U*%0>6?`r2!sN1i|XuDQ3&{yzD)4^LkG<=;y` zA2R>wNk{H;*2>n)Z`-SK@3&{()p5bly*vZQynV#-UpKt-@|}b3uyk8reqqb*ZQfTd zJb(O0S1+x8^qFP9tlR&9znr<>H*LE-aNx}I&Wzo8>T!o$IQW^DHyu>(X}WpAf||WA z{_u{2Ki+a+S##64&<>%d-J_Sk{r-+$e)x^;qJ`etbH<%??nMtQdcSq=iO(N;MBvu* z)>j?hbnxuyr+0pH;unK91gG5m`g4CEz)I|gkiI(lh(zdL8Xzwpl2XP!B$W0yC--?V#4Rl~s#jy~(Y9d4O&@x15m znR5NTUtKcltZ4`B*?8*L7p&eTvv}a);feM8$<*RFW+qn8@GzkNfV zc>JI>XBO>Kv2@MBfBEGfg9m-?ldI~_z2e{}M^`<2=?*uXzuS99PJb`B`FQtn z6VBgq`=^zwV_#STOHcUbyz`!1u*WW+j=c2Vi%XyQ$I!EP-R!ya;2GbYbLlU3>m?sl zJ8ND_y>!Q^yC1&G=^qTKo_OB!#tBbEXLO0rcW>WuU;3`Eo4@n!{(D1N*FHy_dh~*u z=MH&rO7<>K{kyMs-v6`nn@Q(1tZ{rYKj67%haGFiANI+(9ID_ec&JG&gUXvza;T{!Jz>8nKBJI}q-ym-*&mFsFh z7&ChCXEPUGxqR`Cwp*@!@0uN#T{7sG9cyoTb@Zvf{q>Z&S1nmQ`lD-RZ2J6~hK{{T z7qox#)4mHH%2r$;?)gM!YSp7b`KXJ&`CHu@@u1nAE#6V@-SYW)Ln8 zedUvFt-oIQT%#lM@L7Wox-~t&v!G$v?*_NO=|31nR+p1IrqBvzn!xGFvLNk z?W$`=iuEHqpKCno#^;{C^^ijsyg2)|yPmvs(BKxw{I`$0yu^{*d&Ci<=ek{w2)}ug zE3o0I8`8657kGwkUefv8Z53}FQgzrxB{H4yM8e1_?6RRcb~MY_~7*|MF)P@Iq9}*-hBN(2UM?`eb%aLHddcD z@7KFQ6x_J?_jml*Y>!_5&wXl#r9ORk%o=%yNp&`$=~cYM0@x!13*-DmEtWB*e9 zmxcD7cm8e19j6WmZd^Cyf<14$YwEI-TRT=>_Uulp?i%vJ=r_K6b?@s^BQ7hM`uxd4 z*^CQE)g|74y=%+pt2V6}YL|o0cTKqVg6O=Bv#m^6--{Jo4IWZ;d_dxA&fSabU~%rpU;jmffCsX4aGCM;|^SJm#j|7H`_T z+Pc>_XW#ntUzXqX%`R8}?dI!RK6w7BB|jYW>#=(tJf-WF)~{PfTt9(c3#&f$Zra=a!Dp?$B}e>z)M4ij+;HsZ{oXtO@TV@BWt+2a`BM|V9{#6&$KKGq(8{a)A zEIoVv##npVM@>guxnbfjNBwr>oZq`9Zcgp+&wJy`Ph7rw+LB`)9r5dL3lg?^CI^ywGAJ>%}zK7L;MVcxn`?=*#ffAGhV zD^~m~HSC*XW{>>s(U)GHH|zZCzCNkyk=Rt*ZLuG3ZJaazqGy|aTykJy*B8$lyXO8k z9(nt_XFooCuHXO8{m+*FblP6)rtMLC=my7~KU){Sv`Adi`Rv`V?7r`pw~HOeeYWoF zMfY@UZd`oX&BxrAnR17#<)tqd?|A1i+$zf|c z|M}TR4+g(EZ0yOh^RL54j`*bdl+(7~ZNKWvulsPJ2OII_b{)zWjEne|hcj zf#2W%&C*fg!ykNq_L3Xw-(KXV?7{KK%U8cKOfmLmU3xJo}NyxBuze z_WKVBU++I~(T|tz_2bmPJ`}rP$x)Nvefo!Csp+0MkKJ>@>vhNdGWD0P(Kp;TW_fVK zufKiq;>XhJw_h7`>zcQxEqrCu?<3~6PaS^kyT8nv?pZbcicjY5`dGtt=U44^aB|A# zi$;|`5Ud*8CC~cmh#M|>IXdF9)#H|Jy02}`n)F@IrdM5ltL2raqCemDe)|jICE*o6eRamV?|+-xR{ZOKj(_Fv7wx$G?=v%J zZuq%j#-N|4o;UTDACJ6r)f=&q*FG?0ZOLP!yT1EnvB`aA!pWheb| z*1^}!S-SBVUot-P(cqLH-~aiw1)F{MJk$O3bHl!`jsI49;mTp3zOwxZFAoacxNOo# z=VwOEz3$5gyIX(tox5q%(|`B-Hy!DaX8!WrM{mA;%~uz{`NP;((%mz5oqv&S&hzWW zzm_?1@o(R@4?gmdjdK^qj~g~}(5I8%`T{BGZ;Y7z;PY4gym{>{E&H7P*q5)@t~)+_ z+Otn5j`?k3-NvpSu*3{&sUE5yy1!UpRby;rOY*Hy9bUqCDgsQ^^voG z9d^7mbL7s`cL|jKrGDw2!WXv3pPByHi(|7tPJQdM(;hr~*M}#4vB$}aRxEz3^xOA} zcX(vd2ezLt-1(bjGd5?R=>Gc`qyF)>ZOh6{!Q~62i~jxDw1p49JIr#?`cdD`I_C3L z%jL>X((hd`>b-w_Kl`EwrT2Cp`O>=DmnR1+9yoK|8;4!n(eF9cPkFF){ z)>&P%R;+zAi&|YDANaFn-6x|S-urW=n3E^0TmRElU# zzA$m)Zc{fVf*T*HyJG5rzkTu3?>pMQ*t6KAqWEy$a<^wQty0=rQXYzD5+eF@rsH(248eCN|ysTo_z>122=sT|z z>*F`>LRl90us^7@dg(N>#HB$^`0q?i`0q1L!8p#KqkN`;AHT1H(9)wasP zl|x2U4INTeWgE)yMBk&adE)J&P*qVmyhNCpiey@4F(S0nc0+B|wu%yPO(+%Fhve~m zR1IT}kE%+gCc7|VaFx(o$H)AMUjMrldoKTY(hob2yLi?HfBeidj=f@G;P&xdHHYl* zVCL2{U5~u8@YnC>FCF-ZZ|L^-EjaP|k3ZPxJ>;Urt|M=`_sN5g7_#%Z`!5{7-Q=5{ zU!>YDsGm}7yZ7>%gZ_EpK6{^W$Nbcj6YsBo@}Ohu2Fxy2OvTWm&g+Kj%(0Ov}6?eY! z{K&h$n6_-ioF!*nd*}Q6NHe@Qjvr!Q@#4YLuDZo{Qt4}#mY(@#VD7CCufO;FA9odh zt_Ux_B+`_9=kqu3xZvGqR<3m&+Z5ljTX^i^AD$k*>BXaexoOF@hs{3q+=kskubq1F zVRt>bdEzZ^?y>CkwWV*w_P^@L18?5&$RW+8uA3gSK zF?!obdOh*Dam=x5L+db&}=pmprig)S|uC%10c&cFXq8 zA09vQjO|`rSzi9}>MiZ{_sWkPy!ZLDyzf`NxM1f~c6)iJ*AJYrbNzucc6s^0fA3u3 z@J(O7s%75A3*P?b#KTAbk~n_t-6JiF@7)~Psq&7)w=7tB^t^GWjoz)|@@K2xnl!rc z!yVf%KXCsqr+s(zk@J4~*RvPz^{M}h^cidZDb$UNys_nobMJe8uUFsx{FOBm*Uk3s zdGp-i^9SB?l%t5AnTt$d`{k6W=~QeWSETQ&HlNSF~=$>)H&abvy z_WG*lzia())oW+CCy#mkwAbHAZ+GdFYrcCtlls0Q5ZJ@McddW#Rr}6e5j^9>%bcGc zwEUsfSMT|czui3Qn?VO&bZ*_FhpoG*=G6<6Pdhdrwf?9xzdZZ%<-xV@mjCeaz;)}# z4qLnO&9wvK_ilIChr-fxgRS>|a{Rf~t;@c8b4>N?fAoO+r8+x_`b`Y z6|b!fUpC{{sTb64D1Y*xb&r4h?A+mH^U+*39c<$|yF zJ*NC{$B*5Ij{JD%l^3ZKXSFa=+;bN z=@COeKc}s@scyjitH#bKUhK3qIGZAN%ig07-u2wK_jGRh{@s-WKOXV%L#s#qdY8QF z*Ddq5%=_Vn@Ti3k|2ner-0_c}UOW5T&@nsx+-h0AV`=f8uTHq>s>ffQvGB}quAbd~ z%KUkytFBwNNIXaWWa6#%k&g}k+l18qtN!(u+e$7jdgtO2+ZPx8I`sUqEqA|E^^f#Z z;>q{M_x;ZiN7?RAEFWz>tXOg{ee9WQv|oF5)|v+YIeRA12h z_yEtp9{l0^Icu&g`S-4~t2c~&cj@}lm!7bcp7Z;r^LO`cU-I>pPo4aqwX@qtU-a+2 z4!Y>&S%WrgUi?mGzc)|uzu%U6WWdz2N3PrO*kiGu&$(vJ+J8qb-}R0!ioZW-T+R34 z>z)zz{e98$#F1Z|5cmHzz3{`(F858jy;|6(^v<0c){D~B9;=L`G954?Qb#h1_AaoM6P>TUMS`q}%9UNUBfQ(rrJm-EVw8Nc%t zZ=bMur~4jQJn3&v|G@)VFPySuuW>i+eO%jLFTK6&<)XG@54ms0=Wf`0_!m1}di%O# zKfU*Fvya?#_sKVZ=S~&R`FhSS?Z-?WB0O>S-zM%p{;QAYHaPaay+LiuN}a?y}2u4=ld+@|5kE`Imk*d-N}_?Y;h@aJ2Tx zb<-C$ANk9iR~C*~|K4wJ-SE+dOAk0THty8WxYIMwo#J`!w8V3#T0gAW?LWfdMFS!y ztaU$G^ZKuUtJ~u?pK4;v!D-xXDZ$$u*LTeF((OwHhZO6C2Q@(O zQq)}c6G_H|U?S2kCWU%2Ax?EA@#r zT7v9|imEa&UrGf}q$4dRqarpi(rr>&6nxkaiX_k|quF#k(}~(+v?XMSiRq-^6_G&} zLu*NBNXD@dlIgUekL-zrfIE?ev?z<|b}@z?17e~mpr%4bN|hzVb}=D%+zyv7=)!P) z{t)_0XpRFpnx(W56El%`LZ)r&osDIlcvMWvVwpQ8CNuHoxR@R()C8Sn)n$%EB#VqJ z)4{uvv4YSwB&7s`wpNN@WR-rc@K>}AeIPVt3q7~w~ zEFh7Q5VvT7R8|yWLWbMo$#`3~ElfX%Stq&zLAT!r9hDT3#R;weZWiKXn2An7N}_)p zf}BmIq;y7%!A!^^V$S-2f4s{P3J3iS0f#H>bO$gWvd1-UpFJ=cKW>kth&aMk+* zA;HoiNwE$jM}{7oNF)=s7OCA*qWRWm_qqfN1E+f#3NqS|dU zc>ze8y};;omllgn)?S(B@PV0GQk}4B4LOh;+(=+$we8 zb>IM5nnK%F8q1I7M5M)}6NkrT45<@Qgd~ z-s@+7!6KAfBS;tuiFi{Q4GxVAFACH#vWU?`m9GYKGC+N;0>kSKzm2x*+w$^5dCN5& zqG>AUZ!70&j5PF|E$O=)&E%XTWiQRfIWwM=r9{py)z_vYO&La7VXII_nlc$_1#6sa z7o$1*WiQQoMY6grl1ayBXSHaE zW+Ec0s(v8OX$OJYj!XbdWNX|e+OXK`z?gK1LMyVLfTWG`hS29BKV^}jtSY^jJQlda>MW8 zv7t|T!iRpQ#i&?t)Z>Xm20?^qOy~o=fRj>(O%SYLfg(E|X)scj7|{VSfn=swG78z+ zPH7~Pi5c+pqsAa`VjyPYG7>rN+CK1Oit=NzAo%M;XcJ<5MbOi5WWAOn$&iX&o}kN$ z0R&wkZEN}g5?Zy^0ts5L-4XDE?Tk%WWGWW`D%(baluFpb_C}&CJe3M?4Tw| zL3V0vRv|TnYEv0FHdK1bVfxhi(TddDIbo-(rlHR86xp>w_Y_ya{j~Pt zIPM0PT%lK7Fy4|xo7T|5W#HGf4jo(qi%up_tEZ%fiLPl&6)47+S85 z9S&uld)Bj?03>@Y8Y`S30H30<#*x7eJ1BpWk*`xMl+8M|nD|3X+Ljn}E*$6tJ72#| zX`6(UcnM@lp{JCnD0rih<89dlaiOXG3DHO@(iBf18v>CTdp?MGWdefQehQ(;&Ih|r z*z5PXkzoaduJI$wP9Zk2cM3u)SCR-ZDV<42l9=(*&5@{BIFFJRE~t3K?SoieJ)G3g zREN{BVQO>Oy(z_NhYdqq6>5I+LoVkqL!)?$vf_I?MVV;RsjC;8QIm zRiHb}s^&_Nf{CR_ALJrvI6>P<4qh;)VNcx93%b#*k8V3=RO5Cwg>0~$6^LXi_-+*w zB%1%*WUJ3+usu&YV*Y*9p@d}>v6N$RI6;xpom7b;#tK~$>_kT6k29RgmN%2HEwo>I zB7w|vdSn7SrkD{rTE#SR-m}%l@WIg{O<-{2Ll7}W5n+qe$q=M4wC!n75vbA%0HP3T z^bIy1c7B4!1<@a7Du+6lez3>X;}roRn~6u^?6=(Pz_$QfrN#w}#vO7y;NVE{N>dD! zK8Y7t+iILW;BsnP=`gjZ!3FtD8fCS(8gVd;xp4-EQDTx%2-XPgOz}1rio$D0aB5v zI>tSREL*&oblil`sMBJZYC#ybALAIv&XZ4o5VH1}0tGhHz#4_MfeD3>Y>OazszbLn z_}qCD92NIc`#g4TNzF!&ILj!_@8bw8Xs0LUjI$(Ag(!C;}I zi2*Gp5trKv&{H7|dT+g(Y2Ae|<>^8}P!)s9*pa31ehGg{OnU44VI=-~m(N?TAB?s; zbv&uG)D9)4c&bMT zLKM~+#Q(FrW)cRBnKz=Ir75wJ*e?>3L^(N7iP1_xk^vGs056WLHbxfIScu_#Yiw4L z|nhtm$_0?Pz-M2-fhyk zore7*eS+DPnD#V;o$g?$kkev3JgIjDJPpu|*6Ef94?JH60}R4{NM=u{-a}!9Iq(pE zou_lR&w+j;NlZ90+M?;2s;a6V&jRkO900nF=AqMt_8Mm}45#mgA@?wr9K8g47!k|9 z$T1==iaN)LH~@ti(Kxr$)hndkXu&9XSYlheEGi|CCMAP8h2!E9kTj`{c2|u~PHo~x zY|Ak~)LB1Hn9@fN1E-3Wmid)fZYqcLLt^Tflq^e1h&wr>#(a?!YE-1({t-FeKSSVn z^NSnrRtOt!9#P}XCuqDqh#7C+g^agvbL;J`fbr%NFWw%6i?{EhrH66k7cAa>6DwY= zQ1SLvr1X7ydLd5GS5(2r@g9V~xw#=~KK8LywbM->?fJKNGwA`G@8 z+ABSJ$LCRa-%%Rp-);EO^nk4bF(<0_fMcApixRvd>1b;_gBBVb-&x7mkwgiHA~Y&6 zjsP+zl6~Co$uQvLhd(7!u*aS1#YU7Pc3N$oaS75EDM?H>PT=Nk`%ZJ%2FF z0wrD}60dHjb{@37Gw#v@AT4{^K79rrTFzW3;0_AlmtaQmS{=-d9&Yg`L|coS($CI08mY#3Sv$~RzVcM4pjNhZ0F zawTNa)oAzDdt5;#dL1lIN==qlU?@?OjzNQXz|GB?@@m;6dMvAZT3pHZC^1PH;bB>F zqt)1`iFh5*rQ1roNm%``Vdb?WDsqmRB+Vw38XORl9dD+UP;zBHP~>ezIa3vM(Z4zZ z*7^gXfZYdhxq<4Vef1-TR0U(iG!3=lP*sMi%Z146E5YZrWM zoktwzq)-9=CSx*>QSTkO4FT%Cm}ZXsdNa-F^B$*}=V;wD_j+;>jNVUfj#=aJDCDfE z&J`21dpUombkxlsEf#`l3jzL3aBdB|Tx~6sXv9IyIKY%lfcY9R&rC95r#QgDwVX_I zisHBeNnhzvNPgC5Etw8>Yltbh82x)->O&$8@v)r)>eY1n8)Da*ix#RnnQ~$^Eg7q0p4+QQVPX zYbtDkCq=QZ@`xbKhooqNE}Srz82}CS(nl!)W->X@pjUIl+(vY2_&~@%G#>zeHEWw~0R zJvG-W%z?Q;(kUZZB0BU*x`w9co8g;U#A2=jpOw*Sm;Gd>Q%@T3kusAx(!UuZ%39Jc zBRw(9))LJp&ApCtF)VACi&=9B4BAZ3#2TI@6-PKPq+9Qc9OWM|~9Sw1M9-rW_ zBNc&qI2k^Dqojb-7jo>y`8kQzzUCeETj|TwyY{%m6DvX{CMHX`VtWPdZ0lu-UbH|4 zXfliM8<4ru#!QJ!M%u)(wn(xC&x?!}TbqclwEzy}uZa*cI9Lm~DA`Qw*X9EvWbt8P zgfn3@#mFfi$|NoVK21m+G@BTI0LiI1TUd@l$zsJIqTr#HF({fyR>VYP6}82a$QHs| zqR=6waW=G}V;Y~M)b2&#X#G8$><^J>7$LNgrqht!1h4RT;rIxbkR+>zAVJcXn(Hj;LxCXefCw7QCO`$HE@7?1>u$u@lW2MbN-Z&B6YP0ujct;o z7=?m#R0t@?#q|54VnrCF$UQc2txBO60p({r6EqGdPVT-i<^LSTp1>VZ+u(DA9rhq6 z@hliDnh8dN=4>*GB`}#2D`xC+WSWWT5)j!IQm6|sQdxrM1fD~-22b?B6$m=~0p$n^ zdN=P=tsg1n90**aQ4aslbV&S2#<8WM#S$=Uir{@6{ZE^{m#Y}3U{3AaIdAy}&GZX2 z-$;>Yf|PV+G4f+?%gR`gK%q^vjo`gH2OipV6!T6^FO?wb0zxMLG?8+jr8&AMm_`z^ zMA6k8QuAfvw9OPNKR@PuM;$}YjWa*8yr}oXwx{gc8BxvvJ1U#DQcTqecP2?GsP(MX zL1{HsB)x!jsP;lLy%;!PqP9pUNri>`dj$wPNQzQc7Mjuif}>+Ppg2c53r;55Y!j8OMaE)FDS?6@hq(gd)-q?<5eS8Y zE=NPa754eVlk5Q>+Q))ScCbPx=vRAvJ@FW+b)eJ=9XKn(5pdZclO39Tr_+~7qA(NKC zG(k)U&`8xOPWae_U>q)(Gn8uOl#IUTQt;Hs^pk+e7 zK$>r-oNWRe@GoJa)O3cGhwxiCOg-v3ct|-U^7tpY0@S5s`Z=!-Dy@8epNlr4b&%2L znDK%C9RYu(Nf9gt(hB4$g|$+GDU}ycRUSa$9FS-U_Fj&qkV6rQi-mCE#>0c_+n_>| z{DmD7Q?Q(5(=G!n;r@He0RAlufjLP3on?T7 z!3(zx^x*zG%Rs@R(4+b@mw{D#)kH6$<8{J=)x#VFVTZP zjmBiAxG~*yZ0;Z$P}j_lL35mQ3c2yZ8>y~18%0H<-MNyuVk7qE0#IJlZkA9(tnTL0 zbJ4rpL4V){LVqCq(Daz)u{u~^SkZuXEUE;Y%D8uJA#L=9OA|X}IPI_12SWBD2Fq}$ z*cNdnYEfnybdVkVoyC^KV*Q67$hrH-Uhyb)w0y~Yl_kTf|(XiiIQ*fZ!cd>8f@6Tuye;5nwmYk7kNpgj}MYlIQwFV6G(8q4Kbso{iU{KgemiZ=%DY z9Z$}ymvZ5viOV7!A7+ziWdSp6UxwPX4WPTU(wW!vjrd@sinyVxZdMT8C*o_x;@DxG zkihpX6L4w~k3@VXxXpup&qPEG8U;w^NVzCA)0&pDEv@A4Yf>({Bhm>ofz+H>n3hn( zgAm{rZFc~t{irqB)yq>6r;3RL`UBrzOB46_Y-z#+>Le?348tTT&b$J{2!#OweX@x8 zH2`S{h2jiQPJ-LQ)fJFp0lN%gFC3Ils!Gzb#FTkh-+~3V(#B4ZL@V{1oSm7)OOUF2 z+Og^QRp;|mpLGjmFjC+I2B2G`oss`)R&7C~lMhd6gjD(Gs>l**XH%shl%iNmBl%=_ zO}F7Ij0+-*3SY7rPdn0nXTzxxK0BHzrjtlxX6Lx9P9Wg#P)?cfLAkn6;PO7WJEb24 zF4l244p0ObJm;=$G1Ma`@4#zmuR|JILlEh>6I=nG3*yK@+osH{DX?vz23}v3BoJ>qiA@a>Hb1a+E+E?%uIl(uh0f~hS2Y^#Fq*2f+ep?NV ztDdZ~vuROls_vUr-y|n3hDv{CYk(C2EHpXlL!=OKK?tHPc3h%Y;%HZ#9>*01hT8hH zJw$T4L+V;n9^=KW)Z{m6YP1#!t*vafY;~ZhOGuR#tV#rqCk8Q~L|mvPn^n((a!;zs zqAkFoFU*e>PG2CRpyYqIuCN|a*js4T--2HLgN;J2fG>71lp-EWqy03K#@DU`!;#E~ z6cXQPQjx`q8mdMKpoy=Yp!27mQj^*cEPjRrksC@6Q`-Y~!w#g;uQeWfQ1B>&HLhPm z50*&;>OrI#Zi{D?9b^fUk&+D*+=K)dgF`2d30$!Yq!K96lZI$^imI_R28D}JO@PIw zseqwx)~!US8vwF`J@iHH?UoWpx8s^+$pAv-+A7h#+J=k4{4!kX!D3(A;}OHlcv2Ar zs$z*|E=-Zt-u96vCFBjvbPr`_U0h4w?D}1{qu+m z1t|-^CD1aLP|^wY)T=Fl+YoS2eGbvDR@5{}9fFs}0>c;?LuFx>7^G{{ZIW0GpXlrT zlI9Z<*&M}wb#0jZ)-f0Qiph0oz{&37oO0ACFkyhI3P$?~bCrp##$l>LmFrl=#JIia z@G~i^s@+%+)92kf%=R58NjE2kxSSG68j{qkOdn2`!WWe>#~pv$mMmfq8DcyfU?hu4 zISwI@B^K>y^A0ZO4x^hT-1ZfpO648qsvmV`N-7226EwfgrQ>lA#tK8S~uPS&G>fJMq-Q(~#$6 z(T}7Dv~#X_1C5Mp-Y~t&swZy>ul;PCru-6WqE#8FTf z(1p8$_00cXjxY8sPe)FOl;f+Ia1+(*Lx`urv|xnf+~HeKOr6KtjJ?<=Y*dCOg&G(s zqgaq(=?ZKb2gTkLFoPOGL^9;SI3*#WEzm@L9YeBQS$f`Q`(JT!Ir!5?p;e4P?h9T$ zW*0thZGT1ha9gcWYkH@t%*r01-Ap|ujPOJT^3{=Bb&ZJQi#Zb!azsr}BAen#s#MsD zlmpfN<%|&Jrd}7dEq}{;k8R*n!mx#yQ^wIX=rUK({Gj2Q6n7XSXR?E2bKnqthE78F7r?iIA?yL34DN14zR))RzDTS(Moxa z(V0U&ip*!iB8B0JxUOR~4_vQB^fibQh{OtRjeK$<+M+fgs63}{z$8X~bg+J=R`c9? z?3(`|dh|wiO~@(b{CpJ!LC>7j8c&c-#)ewZxottV3BA^xrc4{If5(j#qo9<8?7{Yr zET1I$G?K6hL5UVXq-cUDB_ZF%tk9L(DUISYT-46&Q-!qfgXoB;>JLm-V&3T3!(P9$ z!Gq-~`JkJ^DrYS@vJU8Ctyl>f)EcIUeycmU*cu#gR_Y(d!FWu`NXk|q&XbceaN#hf zW`dJ*sfyeu))KI2=I4B)nM{p^iw@_NVQN|J*>KFSXI^Fs!E}vsV;iGgdOka{?r9!GUnC@FyCG){An(k#3T0YMTN z?o7v9d?^;;DF~g&U|ov3+mx_uwL%i?iR1=ATp0i|R>jOnjMmXQ#7NF7!3cZea3oz? z#8r&ca)(hic$_#+q**C3i`cyiYM+XJt|ElGj3y%SHZg6>fd=Z1qXt8R)%@rWOo-_q z;P){pnuW3$5jI)nXdX!HbQ{`e(;ygCisOkQWwHc;9|jp*Xq-DJ1pT$4NoZ#g(Ci2~L?wSU)EkW#{#U4a$E%=q)H(78s z)(7BbZ+}2=BOTX`z#;enyD#K+1xp3u<#hY%N(BT0oTLbb$L)28z#Zo=#T^?P3jSKb z>k2r=p+D_4ZjU=O8TVN04*5`T;3`2K+J$<10PP(O9(zEjZwS=;A*me=&gl+1Ja)I& z<+LFTP_Ke(qRST&(5C9~K$oy^bfd-T9o$%j8W%zoM{nW2&{)a#41=P-f!^zLAQ~bp zJ*7gh-sNz^FRn%x8WK|9N-5N!>$nE=5qc>&?OuBw8lhDs4FW@nqah8NA1Hj`1c0Hnnfvjb5e z0L2mFKl2BG>->QbH&(&ts`I$(Ts{YhqXRgT+_eFN7ruQrECi3uR=v6D^v?aLQPN#l;2i z8bstmln#X*lj|nAeYk$1l^iFPz^cV1*>H&g;xKD#14$9>Y{3VVR%N9_NubUaBsfY) z>~XSPOk>G|%)41-a;7su&NM-Op%2Kf2rjmsp)oFBgG5RP89*T{F;YRnYBEA03O1M3OUEv%;hxDwVz!H8qH)Jfl%uA^Womab;OmMO8(q;E8mkMM$Xf z#F1EobRWSNp+~$ys2>&2K;1Qvj6tN2B4sEkctxa}g32Q_B%zgzzOm)ggM%W`-ZE0E zECKb$1Zvk}xWpo*u8n3~`4efL37NL7cQ%%Jh#6hx#?Fi8xR@R()C8Sn)n$$Zq->ZE z-jyswf+~VQj*9TXHO5IWMk$ODqLFATgtACcdgjthKEp>Bg651F^MKlds;t@w1Qs}p z71XUALs4-tJ@4Xt>qYb^GSh0!h;6B`aZq@~Hb_@9i!=a%Zr)H03^&3Y&}H7HTlJqn z<+4mJobZ7%&Q}q%2lp(;x}QEsH&>a{Al=P2_(;)wsMp^}7nk_l0YjAzm=i+jtUd5rjtFW=QK4vwRwvX0Gw< zgDckVb!s^P@Z}y7d?Rf!6MVVP5N`TLy~7uP8c@|BrHn|y+2Qw?qOE;SFpo+JbL6!j z!_qgl-ijgUaWsyiCRA%20{4*M-;`-KL7s1naMZWj3cNK~8IcY)xI?aBy*&_gk?>jL z5ORJ(1|+NUf#`#5ZIJ|rq(ntIy`Cd3E9;H(BSkkth2S>9U=MgR0P z&N&cV+Be@Iw}Sq82&~SAR;CbBN+UTntFha_+}&E9>CQKxt+vWc_5XSjjONWIGI6*Y zU@MJ*{Q7@YMfH#&`t$$F>TT!$f67l`_5YCq(oeAy7ZG!t?*G59`)ewgn=O5Ime|y( z26q(d{Bm2=$hN4FZBZlJqDHnwjr@60BL?|y9x{^YkVwb~xvdH->8u`}7b(f^dOMm8 zs}CyRqp1#{nTEv+QOk%!CfWlP@K2>+vjN(I(^c>C0kZ|+*9!OtzbhdJhdAa$x$7;O z7LkZRvW6T5A{zi|D~!ff?V*6TvkbaDCQSPkI$GmVe440K=Y)zjv|Oge=eOXL1tjAj z;W~nZpe_rnnXijzBlw7{szV%CcjeK;IrxbLRDOVV_!?7?`q0pbB#@#MhDx%`;Fc_# z0CgmCjtAgCe^8T*mgR#3I80hxjXc^KNwz?u7B2Y-I3ywzRcLEISJo0-8I;33DJTS_ zJ{gW=_;eo2>yEfA8osnlb_Fdkj#`|WOkWeQ=5&B*Cv;#iunz}c0*%B6&R52?rIyDw@K-m;QV^25+rQW!pz!lsn zCMGShvN?bytUX+Eif`j;OG(%q#wuSazIlz^5L7CNnP@2tSgrE~gHk$#%@rOf9TVu( zvNU#$UMpi|(d>Z+A6u8kfe7Ry0e|GN`|27Hz3>?9r0O3+SLQCWfh3($TFN+1?~?13 zvZUN28b?|zg8`CD%n%e-Dl!$px6G-@gV<-;7D4Kel!$T58fsfWe70xNMy{lJS~d&3jXohRvvI37(dOs!TLo0Wsr!uT=}b#idJ2#t#4 z+$`U7^5Ab>c?xB))ks-9ognwAcn}9O)k(D9lc? z^~3nn0bTY$-9#L9hSfSN+MfLrU157jARkl+m9!^({SdBXsvu1-1%yC99dLLf%B)W! z7#PO50tUG@SC}bwc1YRc)&;V5wm0^7syutg(11Ty7GdyC?Dro$)F zQ7IOV;b6ExkrsOY5zVOwj1~66U~*8TgnOw$4}(xpNJvr&U)W2sF*V?2A&aa!Cn=`=1oWQ}=gmBOc2H1gfnrq5d&~(;jVgOJDakSn3|1nlyIi6 z234|?6)FBXi~@WIB_RX@$A=M@Vs9G!f+2Ntc+gp=9bw4$N=fAgN@c1T=iqNj(Wcgd z@3=%Zm;y|{W8-psYE?R3Hd}D0rHXfVw}qn_eyKyc+oJJGny|D_y>>-)@-f`lVTW$8 zJQ=kJ<-)>H6C%h@68H|7Sg@(*A1a0LD&XmPGw^d8tvOEq?d8@*svORyq!2oy@Uz&J|*+^wM& zPAG3iiU_`QS+iHO%^j&|(S(RoG-XM3ihu^m?wO-T1E|~Jtid0|(^Mta5?nzH1e&QZ z!E_8ekey45?kgo%1+_XpAOw4*LU)r9sGEc74#Y`beizEO51H;n<^q^oiCjm@>VrOd zT4M?o#aj@Vh*W? zSrkRKpnBv}0Z9r2e)Z{iJ6d_%DYX*VZ>~H-%@g{8xjs)#9^ z4Oe9d={Qo8vkABlx)lw~CLlZ=6VMn~gKpN#(YPfd0mNQoy3s{*G>rCWCZ}nEB8BUq zHdS(9hFYYh6o{Bq)o`VH6(?1Q)qt$WNw6ppcOvnjTy7nevPqI_O*H!sT=)X5Dv{i) zg78j9(8u)76}S+ylXQhQ6v$mk#*xUkfsNfE4s0j)w6sW>j0m*=(qgHKqOMQ?XIqO& zd{GFKdSqnK%h50p`DkS*FcMDEsq=@cAvh2Rhd5zx_FAZ@;N*QiAZ^$V2EU~c&a|aK zlQ8?VTNnhT)Nz`8CkSUWLvCCr1I!L;eX%(lTa*tq#O|o0ZUc)qU>XyOxSg!nr-17_ z;CawN%Yi6*4h-C_X&;NHbCe(;m9%h;FOKA{MmJoN4M8`!aW^|HPKZ`~!px_<^WP{! zgLY2NqgmxB)hr*-#nxYUu5bCHUS*ATll6%Dpm!}Og>#5a9nT1?7z?09hXkeR`4!6F zyF)uyy5%na$=a5GC3iTA?1dlQY+qUg4F%vLMlyq9DBpuHvYCC}r`fKla4d?;4si3)_Kp8WK zB(z-c9faiCG}vPC^*@$GJQvgkm1W@kM2;8B^6~Avvn;#r&hw`Yz#4~q#`le z5C|Gq18zp}swX#Eld&Py4{2Xiu5lFASY3DnP>Z4Kr3CvxxjccB5;%+!>E(>xs-9)4 zMr})AF!?c$|ACG6Ps;zSM86IjqL2R{GGyqs_@6)HXR96KdOx`#9RLRejknR5AaFW4 zBkl1RJgSSNV@WYxus|UrF2?v4VC{YiYB6@dEo`YD!j_C=-WE! ze_AM!Q3!{)3s8sxNnDZOR)gEjAT|fwzis$xO0pW2Ls|6|I7&2NV3&?3GieDf#-J;i zK*m>Lz~>~k2Ub6L_E^F706r*`a20g`!oq`WBsnd^>@fA{qe+BIeu)o|6gd@Jk*)&r zhMs5AYdDfh&=d(&QM-hnGHs1s;>aXpK^qYgQ?l9y-?WR{;DV}}D}U9mSo}?yU}d=o zB?wkbqUYL4d`{THO5ifHT8+#gU?%wk6N0!i4h)U@LA;LsjkhEvT6db-tYqXY&V~Qc z_aj(=*l^l~biX7L#wE%m<7|>x{#oWVHJGL{CeuNsWb`e?BP4~pBZ)*ON5xWgB5gVN zqo|m0$O*86^$sSj4Ze+k{;T|hsFgNhpMik+z#bWh9E%n6x9xE5Dq0hm`jjZ^;7)L) z8mn3X7{?-!Gi0_N1A5T#5y? zmf3aZY(F7b-`n=^sS!ZX=L+@`0RGenAr$QO5JFsgP|#`OH8PbXo>vsxt5irK&P_tx z9}#!W*#x(t^30^3Fc{XCgN0$@`_G%`PFgCoiis2gsOMDlT>1+~TQ4Z&GEJ~LbUQSnIIOv|@ zB4F4VHIZL3#7E*CC5FsgY$WT@KsKSnjSqm#+NVVbQRqzZH-i~rup=bF0AnI}c~Bb` zJnFTN_XliVw-5b`tR(ykkx31(ZDL`rxn+k4V3>y~2W6&VwVHv&gYD`5hx+o#zK*~lk7D>l6+YpY{fw7QI$ zHG+?WVP)c%$m{cwLGno*Xta@NR7%J2)+y2RnRJ`X_Q#r_la(#+2kWUn>@iq$*hd0c z9C*Mig`ohoHuc>VDd`dPzNVe!pr6)yjo6T%jTqdd&n`aL||C32&V+V z8PLUgP@<{W5+pqaT=gD1vg|FwAd6rbWkI756EPV_1|-F9h|6d~0O7i7d`S!DHZYIr zcUCo28FMEN%xA+;B2m>~HQKBaq;<}>RS-g^;7bnHa5O6XCjrGgK+_5b9sYV(?$6Yv z2HsV)mBh)-(FsA0N-2D=bO$X^f>slp17f|C#f5R9V(V3jkmPokoQOu=O>}<98E1bOF%Lc@~jdfHpytqqJAH? zZN!MajGP-cKWa7T)a7%AU4ei<&@0iS#=42K(DKs;@dEcN60$@I_v2(Giypw?Q*mA0_85;aJ$>iVK33Nhps z_*Y%S`Y*k(IG~1y62i&YiIP@BS$*p$zGjSGoY{=;#^(WjV2=>0umKo_>O zZc-SDP5GDsT{tYlG?;{-j$Z{_K`2k6ydtnkOeP=vU}P#i!{HFTT#=;^Z^4*PyH44H zW5T!ER>=plQ`kW#vX9eJr(jWjwH}p`=}pD@_>O$7ltx^1uci?UgQm1uis)rOh}JTW zNKIo6XzFxfj4+K#0UVV=IovkQGODPU`5#7OVrFDp>oG-eVIY{aR(x|{x+PkQp&gBW zZJ#>5MCd|j)WhelbXrO~uscdjr$?d3NWIC(qwvMD=n3i>f9F;bs;oz0c<^s~X+= z&iO4KOKm(!Mq&b!h6h)SD(WsGG4V?Db(oxEhH<>I)xw2T;;V$!m`VJnaemQr773)t zVwNokF`{s+5@h!e<7gxLIUj69Z*Nr=E?EM6wg(dkR9;!I@48dYh;1aNIE%F!WXd8V z88ROu)QzReT3&*vmoVk)9uky$)jRA2eZ+vT7E}xxjxm^7sAJE9GN=iQ=4Mhoo9ay3CVV>urE#}n(?SCxm=WhF8lB3s zQ*G02N9$83b5WJRF;G-zhv-tG+##1&P%)!G)Q^b~t(YoWD@JE*>i_=VT}FA=Q4i}w zyRgRl-9B976Y|AmHSybC4{2%obBQn}uMA{1z5xK;G@XG|oxq?dB(+{gQtY7q0DWSb zl9h`61ax&`cu*!(WT}OGW(rMugS%tWQVOkJnr2H5v5?)8>WHLca_-s8Js$kV$d7C$ zNkij2>Ze9e;r{UADKU%v;IrdRal6M&vILl?7Q2Jh(vloAs$XFt2yyu^Lt;9E_Pnr& z@5e>EU<90Sbsm2WB5i%ZU+)TpCJXfPSw(jXGQPyAae}V3#bFV`(J&%`IE$GikV{IQ zbSqwxPwwbmM~a++>Bk)7T{s~o zTi7*7h?0gkDoHCf&Jb&@J!JPVlid$E0(MG!JYssHt9sN)jvR#TK!9jWC8kxMsVHZ% zP3#zw!~zn_A)y&Hy@H$sRe~b}vj5-%(T0qZbStT*QB109K+ZCld+d7ICOD`;Of^>B zx2-DvxrnEhfFKN-W3_OkZSa4GWWjJe# zJ0N&$yTDhEDOu1Y=u_82k?N;V4%8v!Pb>5li$V9L-MACd%w~`77*!XpJaMrw0g|qfP(-T|wNtYq$jV~H9@NA#8g2t}A%T`KiA=!< zqc{=<_(D;k1>;jlx5kHAb)oA6((3OSXIZfjCWV;Z$s8H_4})exUr%RnrQk` ziAX~`YD!Eh^|x?1T7;C?Lo}1MVOnEwiXOyqOY~Dv5$N%`QnEs3rNty62OX$>Z&OrH zGm7+Go*Qb3)Dne&ZbDojNnVMk78em`!4jeuE#QnxZELNe9p(~c1%n{ut#0+0oNq%< zo0Ow*-B!S}slKIAzPVYo1#{8VX)w%tZ);QHXezH5;N!w8_-sdUm8dp`;fjqs`xFwz z4&}T^RWEW3%WRgo4Xd|XmQGDG--md`sRxEp%l&CNhWZgn`9tGw$j~sqy@P9y9YVkq zJV3-1q_~{Qc-jVv&j<4z$8|mS2OMtFol=G&uhWPzFbAw>24)`D{$gMQ{Jadz4W(}k z>~x{=dEGu&u3?#SjX4MRLXYY@2g7{DdD0WTRuz+2BUM#%-5jHD!-CPdhR@P%wjO2~ zH%G(EtqO-hVaoB-18AZKYPDo*-!~4xP`wgI$!&n=CVYbVLq8L&6A#j(05CJGL7d^+ zm~ogOwhfd^HD$g4l~ZhrBXbwu;7nr^AR`f9zbx8Xgz~JME=Qb2_7lWf<<+(!<+M2o zxB{mQV#b;BJegLIHP^ zB?^xfSS0j-Xro|Wi?Vj|)h6A7@uwIHQXvqe_K%oskEf+%o2G~x4$nbrH8Xv$ z2dC_#IGF^lI_Idxo!#h#2JQ`i3u-3p(m9XNq4F z7@pf^V9p**m031Gd3%jH@3)u`qf}Uq?W|5@r97jYI>%d_;O@v+6lx`d1!r zyQOC%(B2i5n&83k94wZj7GNf6$g!IDd>SbEuhvU za*2F=|5!%ElTd_(7DH)*>TnQC9M6#JJy@aXn62>WVTg4!RzoA_&>O;);2WaP7=`My zd~s=w<^t^^QLC2f zm3NDFMYH=1?A-MptS1_t!cCd(JhRBKYPmpg+>=S6Q!jmQQ+;p5GA9)RExF>#Ek%}M z>bl3@14~4*8L34~isZ1UsZ%}gAqC#}>Iwpm2Pi`jQYvIm_YkAtEiuJ7wa! zi*N}1$f9YPvSY<)t0a^qg=qu2(EnJ_dN&IqE*4zWb86)M+*IcnlG#_P%u`A&&>v0U zWl7%Fu12DQU@$fIpxdFgmcvlH66a^;h2e12*OM}vIt-$z6x6sB^EapB(>MT1^PH_6 z4#D(?b{@^sFs#E@+c4amh_uMLnrrfLjm_Iqafn{-Y0O+Qu6E2ZaaI_zaq|PvWaIJx zl=NH5Y?H`E9XP>W3ENRuK3kimtP(z5A(|wfNOMMHCB-F3BY;4cB;Hq!P!=kDVqN63XclG-m{PK20O_~adJ0Js!fC*&J*#7v9K%Y<{7 zNMRK$#?*_?jZuq;C+uz?_!7(xd?-Zl4P#w^CZI)$=LknZ$1_S*cASP*suX+0m#zc9 z!aoVIjMWEFR{|fsj@DI}#EZf?yV6lkd=0(?W)z_2oU;=_$FSs1S-nV2d5Y!&a={Tv zt8JBn6(1sNv87iwWJv9~7_*-9;g3sLSXi&4$d;OKAU%V`^(gQMXfsm8$yR~HyWT*mS*d*)QdNk5{zp(&fpWQEG1wu-^zgf5KBfRJn3aCj9Yd(iUd z^uj$Nv{gzWCej{`*-TG}*zgIms++mD10$IAI65^rv>#AlBR3N$$vpNy11 zfEc_>N*M}@ebtJuo1$Me@$LXtw7&`8eF&$C^aXdbkO8l>*b+&{khv+7HS!1eqKBLr zJk3Tis?f1pl>&uLoFOm^kUY~9>zn1zz>D1JGkI9wG}ML`4kg*F^uiA{ot4v1A>35W zAlA7OE&4@Dw=)ZE3?_ga21TCf@oJu4{x3|HCK)PjpxTJ4?W8FUh~pjkaD4TIDF<9l z2?RJlFNNjO#7YnnA%NC!Q(BtIfi^wXAx;<-qfXsTZ*+B$yHRNAIIe07=euIV3)5yf zf*83L3Jvs4bT*A8TB?`vt-BatR%q>JTCylkgTT-EpiG6C2M*T9~YIu)7sibXk$)-5encmAW5U%@-Cvg}LPCk{%N+l(-36wNk589@P zafKr_nVl+&GEa*domb`!slgydb*H%-00R6Y$_w}*cc?HwkLIti z(^b<@r+Y?g_2h;D~Ae5%F{f!DBl>d;hS^# z@Q3s`WrvZgTcvur*J&xybf+bJD4iAg_e4MG1t>1VhJDM2Cm;~44-N$Mra_0q199O% zio`Ig?8$*?<&ck*ysXV@E@=m;dW%Q9~1XvgW1C_<9G#mb--o>OB;R)$k4U}`wSJC#U-IsYZJvj1 zo`?SMJZP=r|1*9C6MsTsy$0sqf^9y5zWM~z#2G$Y=MUKo#LU)*gtHJIeV8W=?lcVr zvh*g{#$q1JsV}!-h6kVKt5f~)MUH6s=+S~^-G--tECps}rkca@X&lQEc!P zdD_h8s<}f0OJ&JnA;T}%N}tivMFI-Ijlvztbc8e=7;$l0X$3UnNqO{3HX@=kCu~i$ zPrxk94=IatkEPY2h&@4yJo5593wy@)=ScYl6_Wurh%v_lbO&EvT!i0Ywi)haBbT0E1C`~3uZ-H*Yxnuye(gPs% z1A3vPlo|?1!HC?YA72@yxYp*gtnx7nuPYPOZRj5v?wWLvi?-Xf>LJ|?7m6B6l(n;Q zn4Ss``?f)Lsnl`a#|&sy)9fBOe3Ds#!7O-+D}@xrNExbo*}XI?Fqnbj13DoI=^wQw zLFo52Tk)RbGZ?{YtU!|9Ad#0L_-RS37`?I4S>qGUXcSB(87XYR?1C~P2onf}v⋘ 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

    4m*d~W*ZxI!(7uhtPx>{2^?~t#flu0l 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�C5>rXo0|kFaXrjy|Vu#?x5tZG~wN$kcH| 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 z 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

    e^kr=?khG0 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

    BhryoSgf%qcGon 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�fGU35PM!JW5YEvoFnj{#fA3jp%g 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

    ~yN+x$ z%Y18T9uwRA$nMsUU7SvjgBuMr6s0bM2Un%dvzOJ{>KKvcKXvoLe!4o^OG?~y4a|SR zebZ8;YIV^}a+kH&m95z5d`~a$v_Cqj0=eA}6B;2^@(o$QZG6hxIfFaP98oLS!HbS4 zwp9ZBAzeDHTwEEmVGA}Pc|-&dOSnm8*7!|+babRszJmBI333S>j{X?MBuLa?RW8=p%A8*z1FVP&taKWET4VhoR~Sfg zj5?H<1 zo%L#0wa-8GrLWgz!PQ!b!c4RMN4rzU;lQjV_HhLBnd1tDurG z4^dqUdc?k8%8OV75+qjRfm4EW^63r92xG!?ge@rvQ4fUW6?q8BDKzBYa6DkSd62Jr z=-By3f>GtF&nE5w;w?v`7Cmi&a8BH;mdgjrU+xp`iy}h1_CFiy3Oaio{xBn=+=5xs z1hPAnQ@iY+B2QA{SAPXSCQk2$B}-$c8zU3aJ;qZ*@78g37W) zMP?7U`L3;{Gz=rZ)3nstXk=_Z*8(NTCcQn76v)#wM1y23^YEF#_|;ocCzn#P`PZ1H zY2P=l!g$2052B&fYP@!h5H=OIHfVF{G~}%N6j2V3mDaccpX*>nou6azVJ@gEZB>cC z)95|TZuG7V-8>V3q7*<7E5Z^T@K>E#KvxR#jN}`3c0fE$t<@4Eufle&(Zm^~XOM~Z zr%3tuUy$kmRz(`TTKN2k;M+0wH`mpJV-Q*hIW&fIe#Y7TQP3~syD}sT-G*g=W%g)=BgpR;;?s zeyioh9gwcaQ%L*cOc^A9f@u$FhciILgYSm+J9A0~`9lo5*n?WWlEccqEE^mK!{sIL z=YS|Usy7lb1uF`6Eex=~OcSq^GgllPbV6S@&9B?f>+yWD$k$$xv(Q_XAR%$eZWB9F5Jz_G+t6N_p)skd-G<6i;lzfFK7Q_7+<&V+zKboYP( zlp)M`6|^oT$=;#^&gNf$BfispYwk8_W=bM$(y5SOLS=E~?&Rbp1&AOTs9nX%jN4Mh z8iq(B$C1^}Ybm(kU98$ipm7(}9u6GpcLnw&@~NWpi|H{Ddc`$~Z!i_sYL$mfzwP6_ zC%FbF#|sxKq08Z*#&(AWbzcZjUYz{v%d-q=s`0Z*)Qvia!Z+j~$(aYW(;GR@<)eZ9 z=>iD2=;R1Boy1B|GGX5HwpVHF9Fl_k!921Jvuqjy@6hkpUtLr7mxXdyD`P(L^}EP7 zo2G2SytNG*{&Hx4=Vc==aeSf|*)nO7Mh{6V#}dUGVnH4upJLK~R|SOnWqyR``4#bD zT4k`zV>~qG0V*YI+5DJ-+;=MI>h<)ea7hmL`j zKaLk zk%cF$akseCo8Ly8CZnwdn-A7A=h5GTE*I1JOYC)At5eln}?wy*Hd4BK<$(hEUK+3h*9KRQ1hI zgV*#1o==ymE?>i{*cUiOTpQ4Y#%xy$Sp@Jy|Kx2sO)3w~M+e{%HMNVGHVB!%MJHgO zSgtPPG7G@~xp0J-mWG+S9|8sknff5H`0kVXu(}+>-tbyMm&@_cz0?xb`{F$+@?#s{p z<)I30+S*!xqqr{>^sQSQJvNmxvn7U09=ct_uy0GekP!3xom1Lf-ZPpZy{f64BFChc z4?>vxs8$z2WIZ6DHF$YS%zBHM19?FRRHxi*gvsBMp;kY*XZ|C zVgrgA0PX!%WeFh1%1{{L3IH^c0=;giXYP_Q1YQk>eTftkB#dwwcD~BvyAdH4u0@8Z zW*%>eck1!CD2*pp+Dvlx$4WG(f-~h0#fyBhsMPM?zC0s~v&mB@7^nx{MM(e44@t)X z?_VD*fkW#eR7YO1rn=KSAsiq^|;URWrrk4OWaOSuW;(vb}!(-V)Xp2_lHxECfLeeIDgrg?^r-&`L$Jol>ILmz~r1 zD4$;dE=Zzhq+V`~e{f#zm%IISa;SsQYT#POK(6>OOuVE($E%b3$30ue_emWvLD171 zA>K8)f@9}!^t#b0^N2}o0$XP6(oUodUr>NZ9EUm_>0_c8Hc=c*Hp-6iCzr;Se4ux- z$0HWpL|>3aBapqlLm14Eq;F!Se%R4EGH~&$RQ(3F)Euj}Gb?gM1)>M7q;ke}^~D3* zRBIbm!(*_AZrpzJrapXL%x?KBl7m0d#Z1GpzB+K9PG+@1dI}I+>Q6R9A^(gXmvH|d z7=)~57xGgt2F}=rZS`-6A5q?s-Es&&qqFi{zj1^1%+b;V9hOwDv13j>rV%l?W~-Mp zRoM`@;d2!it)*Ip5NM$h60EEoZPkpPd5LAX*N)G>IC)qEp+B>}J$r+Ev+rn|Cq3RR zG%Fr5(*>xHd^TyBwa4?GZ@yN4w~47s)ceB^kpqHbIiI5z z;R3XAY2zG!nXlS;$Z+QjN+F{o^i1@JAHnav8Sggb(DLSHclA7k3c^QWhfc%slXu{1 zu3A6Jp%~z|dZXwS;9=9`d+YGd!J1-&v32|nY~K+$@l|qRUjZWn9VG8D3so=~CW@67 zJZ&M<_3<4#@A$`nJ>$Y$YXzIC=IIsu^Xpi*yQVsRQnL?pK1V&fLb^0#Bl)5nEqpeE zy0DHKC2exRc7y!IT5!XnO(syXzm~+No-sA_$NYW+$71d3o!fDmZ7u;P^mp2Uos(6c zfW;P=d*;j^mF%Kjky?Y61uq64CTixqCCm3Qo$LY^+b7KbF8oa*< zi{xyKP=VW!$-2lF~N)rosLay?fm*47!t#UB)ZM&eQHTw z9cjEOtyPFKx1!3RHa2lvnO+QdKxnji+U!Rb9v#Hj=W7a;=V$7F=`)Iz*cEZN zT;%}?Pj56029@R~$Q6czUX{ONj5KaJE{}3?f|#IFubes#87{a#FK7{Nfm(r5$xnM| zyufE(o3WY7z%T0_s~s$){Tix1Lm+?oDy*A`{zbWA`j>Cr%=B#k`OUjVOUrGO714LD zZhk#2MUDt~IA(a0Q+842l5}Tng14E+%RHjBqeMIfjcZ4*j|ttMP-N8MkQ)NPA!|Up z7lYs1p=U1&FR!QD{pBEr8EUE;!PLQ_MI2fwN?qO5xVe3e+E+)Enp1N^m{!hq@qyE- zzA#nIrKRLVCujRaWmW$AMLg0(mF<0jQd5pBv?4YSdPZ0z49ym?=3}7ud9+xxvy983?ofv zbmcM%16w5MmWno`fyv&D9d{y=SU-lG^%>Atq0b*8oIGXX4C!FA@&~Bdkq?1)P)Nkw zC%_wEuV!oHpkPvjX%Y=ab#>}nvo`{DWQnz+#M})$Q}wKRc2%X(*wmPe{S$>@$Xt31 zKRiPaFq^ip;m+-mAlp!p9{n#f)5j|CzeKP+rfPe*iw>72+he{tD(twKjNSL3bzgJJXoj#Hf!!G5& zBcYZ8GWA0yZZa|Dm&%F^7Lt<>KaI`=zh|KS030i4J9beeWNh z-nJ0HQr^zG%Gy1tUY~Y;0n(+9y`VPx(G@PSkqSm`3RL=gROZO2+9fn|sS5 znVwa?#e_Cbo4C*f+m*ub);#vL^f!xaY=q*hDF0&AGeM8d9NhZHsq=XGIP{JdLN|iO z2&~wn-{3-#^%eWEN6F+&T&-|GIxrnVm8{?+3E0@DBvo`)0V^}5?y<8dk$Q@6la!wa zw8pI{Nlde=lV(ez@9W%kB`w9(>E_9WxR$V+fqukIduZC4ivIzlFuF%kB!nQ<8T#N) zJUO8#7FJ{e$^|b=d_o}wOlP6 zRVybmkI9?EX^u@-{g%k{_IBYON>}Glsx#t`sLw02CymVzy$`D(tl_r6ll|c^iP_uZ z@mk>~``E~8Cb?L#IIjS^{)e+tY9XW@Qz1WCxITJk>u!dydzs{;E_|eMmNKI?cT6u^ ztD5{#Kn_)7QcBwbmtE7)c2@paW-*LedVB5>YiWl_I!=m#qrivU)tz;TCw6eiMOIFfCTSMVz~T2pa_OyVYy=Yc zG)!c+)?&-xe$n|$)JJmqCz)qO1i`~}Bvm>N4hAO$H2h32BrW%nVLz&<0owsS_#j~m zVsJjmPH+)H>_K&#skFIVa6!zH&@9k-q|`Q`+ z$K2=Tp{39VTLH}dCAWWg2w@uK!8#0LC^{Te#4Aa0m*!QL;P%U}pPBNG@;+<*D{toK z>LK%Ocnf1xW+DF!Up3G^&mzyBL_OfC7*GsIR-3spG)~vb zXft{tVGSOs4CP3_gAdqbap3&y8iNTOJ_?JVA{zzn%JtrG_p2j4a-~aaO{fm`9ctnh zhO7C*buGV|=njN3`HjhTmNFRFJflVcM9TkS?44qC0oEYEnSX5Cwr$&U#yDf!w(XfS zwr$(CZQI_v*=#nudmnC64`09aLw}uAB~?{^K-GWH-R!4$Yq)cHO7HS>A4gK1MwQO5 zp&z7W*MFl%$Mzrc5Xb{{0U~a^js9xxhc&W;Yg130(g@l1=qWO+tM!_LSb@rZWWaO_ zWsNF};y?s!&^S*jw||IyU5H-2m74QOZuXpK6P6#lXkVr{+OMtj2>h9t9GC@31~--Ej#IRPOf6M5>@#-w#sq*i z(x-C}>K9FqaFg;71GT@@l_rvpl%?vZKCJ4dAe^EpFxvJLU{o0aFN-+Haw2;UHpH3S zIr)ogkWY^ymrP|l&?5Oa8Q9huh9Qbj*R2@P9MkB25_eX$^a6Z2= z0Jm);Tq92f%CsjxZ*R7;1G2Np(8StzxP5O)NeYrvHx@r45yvma(dL(hkEKi`?`{&e^J2*Hh5D~;Q7{w3D~ zA_H#EVvR<8bkN zMb*u`sAFo(2&YaTmnQsVWOeONR?vFa3W>rs-^W_ezA!Y-1>Ke59kt~x80#OaYe7y? z9}fWltKl{=@Mq8cQJX2H=k+C$nEk|z&%6Do2Chrgy+@@{wOY=7jx4T>QsKGdPg zmH^|aB8E3cy{9=SiLTpFizMsIeIf3tnz$2i9HZh7&psfgirD^dWzz>LU=YDmQZ-v@ zEc*Kv3y)v223SE!6uE_WqoNeX_`zD`{A6QkvfUuqV%YF*PC}zJ0Z}LbJe`CCe-Bpx z;|l)SET;{5H7=G<8F$|;>(qxe!WaAYjP^}OF?zg2w$IG@c)W`R3}VyDyW)cAB|V}= zo8Prav)o3Bigc^$S;zSOJ47%!-^Gwr_qVp0915TXL?r!MY!z9r0;ThhAZZ zU-=W^tuRd>$H$p}z8Q&f5jZ=YjO;p!UGLg9>)Z07ITwQ+WXTo>TS$qurMA*m-FT^E z9OsVQ>i0OUvGw~Cx{UqZl}$oe{c4(^;foujRIzY55%(Z}%kN)BwjF}&VmeQkgv_M( z-HN_-6q)C<_W}48pQ2nbT>hv_>vKea^R4$u%8`E_#A2S3T}i_%Rifxi`rocLzS(q( zASts)RvNm~=%*Y)ww5T%U-a9})IYnc6nQo>J=+vrZvGP3e#>wn!2YQaoG=#<{u?oQ z_6X26C)%NPLE>O*0ea&T!Cccz+eU{0LW!ov@%GDbw8Da@X*DM*=KSPBUDZjWpyPoQ zAYTJ#`t-uCD~+jeF>lK$hRqMB|f({c|*Srt)TDF3AEhu$ie6$DJL zE!6&kjAWKj1j7)4#{qCd5OvbNZJ6*FQfs3wn4edNz@{;G!soKLt&M|c@vI97@UX=a zoM*v%Ma)t2U!%S<0~GW)J+a!=(?9bF0u23bL@ZLB7HB9IDn}M*J~1o&?uzHq0eWFS zPyVZvc7H7X1}G)ge;a1`^eNG47NmH^;_}fU;aDt@AS%P(`?tgaNTGlxePkJ3bZ0s> zXD`eRZB13Av`mw|CDZOg5butaQ2oTx?&R81j3SlNwvNU0+o zaRikDSfxQ< zOuHVjB41%GAV}(p!45SCRF?8^xyZ*C6t%ZcMw3!eJ)jQ+UvgXP<$1V1NSov1^tik| zJ;@W*yDZ|YXLuHfYHIk~-SHgHCX4()qPzKh-(IGb6~rQ7Gy ztj+GBxfjKux4Zr2JGj^FJ~CiQ-Jm?1nwgpF8(~J{{_v`a8!*#T^Z8xPluuk3tSy7-V(mt3 zIMCAR)QRmYvo=O<-BwNTtH!>oetfa%m|kNM#ydY$nXPOXhMHe$LP)xeg_C3dLzD9t=`QCjfNDr3jNC z@vh~~Ic<3w%+%Zalei^eDKB}WU7D`Zb<03XNMAE9AM5^Gs+; zyO@^WWU{1+d@C;&6v6H|^O}3pc$lvBk=*5e%SS)u8<$1X*yWSYeII5!*%xKnNIZxQ z2;d&wG!LWgmaA&pUnj~(hC^k*a&5cwdw1RXi35%z$9Xxv;n#K6Qa=j|h0NG78x3}^ z3dCE##{=&D9E}|m!fA+}m8F~znRFOQWXPow-EWIY87kYRO)Xe8&AwYSoF-z}P{+5Z z{GD8j=QQl?(W&pW$C}v*#Z`+l_t~T+#|XSSH?E0|)Y_F1p92IwKblHLEt~YNxd4>0 zlknsId>k0GeovGsU@WB4FpG9F=!PnOmO=-Bn9v-N2#LIz#~7MFmqUt?SbRi}>~E65() zV0u7VXt4EC_uC_ok`W^1X`4g+akPoN#fKDZiWehIHI;oG+76bASazk8?%YC??e8Hh zC|$KgXbd(rGP~dF_h#=;X6R;GNRMrsuIo&8R zp&q*w7pvOUb?(~l2Z2%Qmsp1LPu5JqDT`^IDEtB+dZy)9gR?h7zF&AZ!xPQ>n@?>T zdvt-vkFef-cD?>n4beYtM;JnxMrgy1s8Zy30&#fv?s947ixvqyBZEVuh`<^P7tMaF zyr{>~rysM?yd!kJ?lPOxNku!7f}rX%y+EEUHD$nqSGJGOQlpW(hb zXaU}mt|Of5NZrZr7L;M-o(P#_l{l3m3--tmW%Gy0ZK=Ry+7Z-~7Qobvr}o3OlYG=Q zuXULqb4A;K9IeQJ+{0w_0wAi@JnjV>8P3$Fo6-$cNexOvCgngSuW4Pgq*Z~o20lSt z1^dA7OXd%qA}L_<``N5qYRrpcl0mfy!5I5)WOxyVcx_U&rLi^_0vCMn=@8uyR~l;` z+wheGu3(e{e6AJ&c@h+CHRP0Z5sx5TsW}bBDb`mS$YOAs=Mji=(%G%K0 zx_oKfF44MS-$KYs`54PYQQZ{37^^}_XSKqm5f|uAG5Rf2{8N})Q3WrP>a;owqIdU3 zL>9L<9!)iG`qDfM9|O&GQ-^C2d%az!P|m9yAq${7sX#2uAHzw!{Kc@5q4E_R!-Mqc z+6*D@)G}o-gP-kFC0mnMN<~oBsyk#`Ga?)H$sU@bjd*wJRtbS8*JSUBpdbJ!oHg`H zx8tqk_nd_AtJIq{izX{l6Vu@T5^J0$EExTOqUvPb#sN9d8f+7rQP%0pE?ORMy`oW` zvQZGPy1`H3&JK0sQQn6Wg7{~hLUl~I>g%_iyj{MzeD7$Tv=elPc22A>WMH~$WfAQn zk2iS4FyNaN-en6hkA{62Lf4!OCGrt_U?U?`LzCVqJx}}=#As2#EzWEm77#FVx^9}+ z?)w%AvFIgNLXR)2&Zo%oaB~a$BNQ2;7KbTYsr*MRTSBU76gn!KF0hm}wp)I5SGN$0)f#SU_rGX^)W92^EI+;(@>r#&{0mWl4t}#$dHVES9C> zl7|vluc%o`0R=tS3-;hD_}rw)k)vbm`o=Ptno|l#1F+G6MEx|_-+Prexu110t~J36 zCkKDvvu=CK?Bd?}v5-D~a)JjuUZNy3P?kA`@|4L9h6v--b`E!QqI3BkY2T zMR(^3?jeV|Nwtr^?te?1sU{5TCvFzExmCD9^bWXU+q?gqafY7Kn|F)#Z4R395q36N$Lh(ncVl?m*L!m2;5@WW5Q0 zxEdBloD93kt@n_EPGM7wKeO!F%}#S4QuGGXkKmJsE#&^#b|n&ttGkvN-_SU(tQz-7 z$5JY^0n|=PJ)arfE`#@Bv~5Ex$o@*(_ey6j)vBq|7k@HqWEED9FgvQ{qewXvT6rp( z>To@g0gN|v zAmDa&;Hqloxvc$%8grBsy)gVZj-eSR=DUn*$u2BV(tVDkJU*UepV{C({{Fs-m;;Y* z`Kxi~=(-b4v~_Lp4C&pGUajir?y!GyM)xU^@m|a5k5R=D3)tb6V7QNmXoxK1U;!#; z_h;7Ye3y4~GP402)NSUdN1#1mukm+_%6*<{m0k2BZ?N`2$QR|x|CADL9A`NGbQ1!( z5v_=AkYca}67B^4=T$+;D7_AGXg(UXcrSO+sjZ(9;Q7?{NqDD^6(OQMBcPQ^Co79m z8;Gd>D~ND_&s49Y+GE5>YsREfHyAFyoAW>WxPv%v=td?;wSiB56P%O9M?qKMv=`I~ zV$1=0!|)be`@A}*w|gBl+6e<$j0qZ(r_-!eBR@b7iRzt8UY_$u{@aO1>FviU8<^j2c44+ekUjO5is_s~2avamtoz*~z$8nBvpkoeoeKd)-5o zfuWi@1}RqkWj(u=>0~|;?xk}t0s=$N!h1$Qt>q633Ud%!7T;II z34Q}{<-@V@OU$;lnG-s;H;NOZ3Zr-3>EtuTcnu{8fqJO*8Uy{2>kEVnLTM_<7-5!T}{3q-+K(Ssvr zPQ`PM_r!Opkt-t)oDsJaWgO0P9wb=#U zUeY{d3R*#odH!1?~X{L-R@%b;_1I02n>u`?lpDaG#W<|Mru^ zC4we?w}g2FuO3fzYQM#BkY|G$1uET`d0WH=sn$f z{FAq-3Ha%VQgF|#xlG)-HwAIpv35pg^*E>kO;--VrK^)8cm5(wnh6h%6!Dhz6c=?P zSss##j2h1mQVolLepia4;$tM%;l@OY&7R34+CJbq1-a(XRk)dJXW12mNG;1v)dBQ0O=$(o z_D5TZH*M%;%S6izAz1 zRKVcP^EM}gnt~UT$;WTu>%rLXhpe#oNPqAYf#@41XbA|C>!luM$QoN=d_bA$wTaxE zyC-af$(U+`JtSAUX`mzNHq(K{YABxo$Pw@^^#(F?VC&GuME^wz;rK5~ z2n!3#{~Qv!ttI1hID*u3sb1#@^^e(L3dhO$N_UmJBF4rdJLnojR2bP1Ss%#WyvL=fY6ZT|&@%wpoTNd3d%J}|yc(;~;-;d^LU_{NSyRApJh&vT#(54DZ zTgMJvK%CZeGDTAC>DWhY=NOKE?5w56b*a1dF;2|I{OtTt`Hr$S)3Du8NmZCZedA1$ z&ada^R-IX{fZ4B)LH{M+ zKR7!5I+Q)kF^#S{Vx!|NMI512l{%FgF33+>B{3y4U8+4=&JaAwHmae#WfVmkg+d3G z+_-7guyyyOvrFfJ)v~drO!~sFFSaT_%(`)$W67H4qkDQ~xW%8rao6t_Cv1eHV)F;i z+N|CXdnj{&A*%Xep_}_^zu-^X+a$s$#n%gd*p4^H|P%h`(qI!IblLHC2z|D%0!ZCC8WiC9+q*l#(n}Z1A zxR%C)h{$TDwQx=9-29&+ave)Ez?|azCfS;+YO9Od0!|KT;9s;NXAiiJ5Gm&@j*}4X^l3LD5D4>72LKf~S zjp(a5>JV-jqL^ndDlIRGQt!~ofa55k04Hoc&yqgEa+tIL%yvVIs~s4zHyE^>x#6VgSCoV;_h}gP}-FK@r9!H^dh8ZJC>oxt2YnlVth`l#!WWaiGis z-isQ?WXi&P@1si;w$VOy4c^NB!d~gdN1bhDMoEIHGXXk&G@c%hF67q8=hyzU?JhsQ zUEjmM!JTjLhU?U1S2+Es8Ra@kn)6o*a%1?UyfysaKp){bXxe|f$< zBW`VxZ85HocnMW|>Y4m^CFeh3+z13{bb{{pyS%h8QK~Gfl*m}66+ut01oRAXVBql< zTm%FBQ*X7OTeq+X3xq@ShTcsk6v)8NBSk0)u+K2wR1|srq3TYnHTH&G2x3~&-Pb<* z+8`)FrD2Ah!>Qp9(K>7hGno+a$sK>NN*+7t#L~gkH5H)*teh( z?Q$9OvIx--%MIaRG#va9T#v#cK8 z*>e<5fz4wSRC20tWZB9h_WpVPF{sY0m%YmMPSZYKR+Li%r^J%N$q@@4Gy!%!!t4!a z<9w0b$tsIo+s01Kr{-GY z&8R+?yb+tLTq;mk6GyKI_@8gJhUDkvl;+5%>4K>K1KUOIjj4pFwXE&S-bw7&X|r1UQyr)<{La5;WcX8m>+x(;+l$^{1Ijh*y-mc58YK~6#r z7gjm>Uinilg;=4lOr$t-a<)$hF5J}9Z(?3Olgv*t7Ixvh&Hii`Q5h>ggjkV_;4K9H z<$0C>0D;7-<43BM5H^mCGNEn(sS=|N%)s9Sx>1lim06*G1w!lH(yxoX&Yp0iwxI*Sm*N(I@_-k_$J~83LDS^sXCyDcC0L*%SS!=a5ji3*hK8VS6i%z5n+|B7BlAu+^DLE72lb^I}gV0GL^ywmxQ$|UBrlWr- zvxNSvOmjtz@DETxKmz52j(B)S1Zf2RVS0QwwiPyGYuSc}EXLZ*_%!y)k6a^Mel~}o zBQWs+%O7H_+K>P8o%N{;r|%?h5jy!HxFW=>@kTJVij&!$0$?x!f6R0eCG`pN{?*!( z0>h_jbGa2URBqFgVv187Fe=N-AJLAJ&gU5J#UUyQhP6m`TY8G6O4@MX?r_JC!0R*M z)j1U#6~1vg)xiZs^j%Lw4C)Rns&W^3LIcR+Bl-95XknY~F9Y*N?nJu)vq<Tu{M2Vo=iWN5h8Kmm??{%y}>%Ff*XUa3LslLSy_|2*3*S(%o zCj-G#ho#Rncq&`?XWpsE!zc+_XT>V>Nn40ZX|!IQd!ni_(+a$xIkQp7DANL4^Qk5s z^pFqdDkU)&>YPvo6gh0bkR}(qD=PTvq#hJDpq?;B(1Og+uqO;DRa=r72aX!Xo=gZ9 z1#QOni{uZC62 z1VbK`iC68?L`m;QD8Q)vziw&M2^5ky7%jL?V{q}8G>Xo z|MNQ?-TMFqT<*y|mSoEXxNfj9-x@!pl>_VR5i!sS{2dIon{5AL53ihsK)9MxpAHe< zz2TjxR|_h!w3>BQt%@s@s&*Npuv%Zu)Tiq0QJ@LUYItb^@nb5z5$^UG5cJv`%F6o7uJKu6r4Y37zdTl$iGA4yNzSqVW`*Wia4~G&lxOr z3SkCYBL)XHvorlioK=p>k~YcHK(FQ2u(H;~Is^wYsr4*oNJ0Vo9lT6M_%x{XtO(I| zponpA-4~5kcT7Yk$qxmSwmddJjiL&aSHz7W#>=_mUsh2?EwOS2SnL zeBV)V$r~Q+dQh~dz($0x5qMOtw+kjm73?uK4gGMuM-IHj1k`FELG|MY`&>vXoI&Ga z$@oWE0nEK-eY3Y~AY+-f2SAgGSDv9@BMSQv21DkcdwLvr9Ayd8_4=WDg~fX z7nmEfvJlRsH8^AhHh^D?+OFpTSl_|EZ=G?Y_s8}1w*PmOZo(*LdDF~Y!H*y{j#kypL991=}rlP?L@f8gT%B}Sb>2~J0_;K@ia zvOpaY&cpcg$y&vj0ADZqcJMmSCOK`S73Kuaf-kf1cyb&p%J7-3RpPn zFhv<7sxx5+c*B-e$QYZWCIZ;-p+&)a3YN)K`z8=gYu>0O(x^9an3SR>$t}FI#zmvk z#AIprv|*`JiOA7M!mzE;(7bJQp~WJmQ&AvkG*STv+;jWtWM@)>kZ;mxLu_1fh+#nC z@F)k>C*}z2772_*ftK!ONl!AtJ+3fe;$Vf(QL`!u(`Hx{>cP6MdoQ6O14#ip;V~ z`Deh`NE9zv=;9P4IePB(0BC&{_E%j8K04OCJt;0iPmI~wyRg|Gx zk4Mv)<4@nWyQa1uF9(LceCat`-W~Y9zmC55o5mdiy_JbogRX;6FvXX1RsM?o$YjR*lj%x$@%5<=60U*yoN2>?V4ib&h~x= z33TN8X6)%n;0phpp0jf>f6{oSj1_p7?E<01^$sn&&9i^5=SLXlF;egQQRn#n6!?K6 zdAb9vO$|@4))Ls2!=GZTg3OA*(Ajqd{M2cTVde;N@bzTnW>5Lg%Ej&Cb)0{$zgoTC z|0KpK^iGj4g4AW|>gW*>hWpsUbY|@8{pZ|Py;)v$Ye?`}|9WKgv z!*p=YRacRe$X#j#wE^RjK%@_VI8O4toxSZRE(p~&0@i6#7yejb0Mu{2>BOeW7FWD-VW3~4F0H|fc;6wBu+ z7gTYD;@}Iq{PE#cG#EKDFM119uvdxv^)MniYf9z;^q5KFX9?g|$Yxq4NH5G$Na$-z zOgOOmI1@1vT&CFJ=q?!46VxVHEXh$mH=Wpu$>03~XydfI*<%O|pg(9`RmP>G@@VrWtyu@SNPU4?btzy)NmhwcdvP_gY0 zO~TKmwm@QPn?6aKuev^ffWll^_`@qlDIq zc$q0*eQck5Ck!g66YS(%1oo~IE=)GWtC9x%ERIzIgo>pKuqx&bziKn8wd1+aGBaN^ z`ikvjdNLiBhbmv;SDx|Q+k&TfxgB59B8YwZU#(g5N`j_kl8p-_o9FNrPtsOT(oq5( zv#-yu$?4;%$2fY{8~d+4=|gyl-@3Of>B9asF~DQif6gQD(s0%M!5xW5W~A?h((7XG z@^>+WlRi8|_0koGjOKj&W;^8kyM-4Rx!1HBdT6;({Ez`;-K_i==h(SyylcEypq@DP zt|~p~xiV3M#l!m>2lh6n!q1tL-YHi|OEq^N9@pW|S=gWdQ zbHW<3s`?E}yVtUit0dmdrE+y zOCZET8cL)~Sn`!rB=*vrmrp!eWZ!td{yMPyX|60@tHB6LDRrGQlc1W1u<}xV;SD`L zdOEVMb#6ip{j`h_*~e8ZW+G|PBp@@KbuW;%l9Yd?c5}v;{fF^w6b_gHQ8EvJf(XqJ z7kyY=jOv!gBMV$2(I)5uu1_MzVc+NM%6di%XXXV1P?bR+%$0{BnC&(A3lyXmu8%+G zR8dH*^z3BeZkKx6Iyy5DX-y}qB^I9NKi3?yMI(sE*53(A^(=d>7k&w9ae`Z37L=?q8~hLqwJ4OVHqIiwvfrM%aOt zdTAy>WYeuWBUjg}V%~(7guRRvZ-^<}F7#ZO$1fJ`dI?4~V=)m{F{8C%F{z>)F^J!E zUdg2mSDPuj0Sij|c()Pp8lKhsdvRKQTJ;}nE%!wc-SoA zy=ZGmwmxkUqT8h9CO?nCa|&qK{?UrAj!$dhl5zTLF+;O8va}dMBBC%(pP#SOaGA)s z5s9?;Ns%FG#lKZw>k1>Tf6{MT8_{fc6`QXQp^(Wd;h8f}W*tviZg7_xK)*TL?-W1n zc)oEi1Jz7tNxpIbvan3rP-f$Ff9$|$_RIC{%GdeSP5=#m^7W5xq2TYq8BBxoRSPA` z&+R?F)oCxm0=o;kh>0|ZD@!uu4jK1;E1Ar6uFE#g5!_sV;kM zBB6>Nh#~Y)Hs&Zzzj>L0s@{@_OPRR9coL72Xr%f6ycq-j_Ag(&;W$VdDBP^@^RgCa z5W@|o=k56H^s+E_hq5tT)4E7gXP2IM@QFB?iA9>Wrg8XI#_8+yc{2nW?vUDFspYn#b0cLcHw#c*6%?KTyKY-^N&MQTkH1< zOtm3-z6yO!?2p&b_RX7nnsA&eRhz!_Wk>`VN`5}ycfXi<>)z=16FGKFJz4MXBJCv0 znwNDd)OY2B7cMMJ%&-CJVgkXEZDmExM1kdH;z#T$2NR5OuVDR7@;i5JtVz=qa`H@N z`nkxo?AzMYAEgRO7Ak<3F&cPSQTe}vjx=&9yRM2zVf1i!>TbE6W()1(>z{Gwp`sgw zmy0)1nD#C`G%9>su>+d3A`hp`BE~SAOUp{`Jfe(qLX9lQ{)4{i)oRDH;-!bf_h;2e zvF@%9dtb2_(r{jjGHB%Yz{gWK7v698{&i_#Rmbw}oH-FmO!~*N}*c&-^XJU^r!|J*|cSN7wk%5J>ik7<* z&V|X2^NgYb@bUJ7g&iNkP4inDj=~sXcn!#pJ6@MKjVUjvT3|^1Zform!f=%8m~O1j zjdkkvK5_hZ5E^y32VboXwe%vP%!ty7qZy)VFyRwbLnbz2^QU zP@I*Z^ZD-ZTdL&o8mx6sdHhpJhBIy5uyD@Ci-9hS?d5A=IZi zR5%Dtp~9-#Hg2dKmWPs`Jz?*5q;UHUCSN1QlJ6x+x(i}v9>td9s;z~63V%`$7c0XN zjJLn{BA$0Y=>W9Uu;qzG#$hk9IfGCxBbZM;t;c20Vo!-BI}f*-<^bE6%ozDt4AM~| z$PuD@16`@aO5^LyLPT8 zhd^x1Qxjfn;m98hBhcDw7mG{}7USnJop%HWA}J&gAmF3gvyO)GR36d@G|G>KBrb^aPx7k(7c zrgIJ4ip{x@EED+9x4U?cwU&$;bS-jL-AnbPHo3?1fQyJH0n@=N)rLIi%R~ziHyYD9 z&w>G;TgI4YkNV>Wd~NFeOS&~Yv>=7nVFgb--2k{{q&i)2$_ObD@Ocg)9{UDW1FHWF zSi-Gd3X8SX>{6lCQqu1~z(cRep7n<*2@P;-Cz9*l!3#33pb~VjY8BPoB0Q8N>>xy@ z^v}m;V}rJ`NMsPw)u*-QDD7A!ncUM)-Ec}i0{=wv3CbD3aR_s@i3@Mi7vGQ8C&DN+ zXQWAVq{k`K7QBq_5Q+^l(|Cr>hUO&YipPih{IhC*+V}H2L09RP%u(H1oVhd4aoU)<)kt2+ z{wl_8JYw#dRs2%aI-6H4#WRuS(Sj-l;*`A)q=-wr`?xE=>*wv}%O+C1hd!V|Z+*eZ z&QPE{Qm3(=ZhVCde@Y`SOXka|@_MeN)@&%LLZbOW_11TUs&c=R*kuVk zI!euWXNhdvkN|VXtT&TviWC|{&g;ljVEY>K3T8x#561WTAa)w>{9g8 z0oLgyK`NpW_uN`Dd59$|pqLh`7K_$rmHAQ=M%Jss;f?n=BCL9e$Hk4rlqru+UrCdpRo|I7YrS55s>>M^2k|k2@dJH+rU;@}|8&(5&36!ky(L$(tj6yUt#E<822z+k_ zS*u*rN z0IO!%>93#!lo{*>qhVwEy$=eg3T`@~GyY|N?6-wC9iV$u5xDF|1{N_E8AznR$^f@A zf@W(_7$s_OghB)?5{;iB`Bat{Yr@Z-F{c3g`H>RC;q-@mRjLKWeeNabwA8? z@#+aYY=AHTRY0o0X6AhW$^0#{9y4TGMl5@}3cX(>+AFyF)Cm?LGS+v6<--8Sn#2jCjDwzpPW z8E7^gHrmqMR|E^%YAGH@BLpY*&KO3;g?${X{8GVU(o|uiCB$%=Xf9JdWf4)rS0kTe`T||7S~AwYHSo5qsi)c-&FuWYJ0U+K~KI*2Hm*=S@w; zY0J?~&^82hQX=OJsY#-y%0bj)g`o6as5oq_HnQ6kLP__X4EbNsBCtPXx0R2dQ0W>&q11BaV1^X`z5tp z@~oO(*{*MKiUMh}YvpBBuU^q%vDssaHo~M!s`}5`(yLt0C|h6Tqh%CW^9??4DJQS8Po~>&DvvXp z0D{dMF)hf4+Hr09T>W8!YTpa+wz@Fs4(P`O^sbATQ<`05uqR};dv$)MYy<6~EeC5t zb5cYe_j^|6%|$4T?pFzQ&t9LOFU5!sag%J5a~fYz_By-iRz!NYb9BnI4uKBOmL zm-QZJ@1myaJf!Hm-*kh3b}zw!Xcyx;x9%#eHEz0CumtULmMl5-RY^1Rsr2)Os+l-M z=5y=jofOaYZM*o%YLEVMMK?(u2B)k`uIitS{vU!4R)-kn9TuaRpT^&8i% z*wwjen3}#+o8MDp1@LjZK}tR0Q`#NPo35D62gs06X`G(!wG?^a9JjEWx!`~dL-iv% z*CXBkgRygLu7qK_b!^+_#I~JG>|}Oq+uE^hOgOP^+tyApv29MybH3jn-m3lyUA^jB z)vJ3}a7&xya>|?tugm`YtGB%Mv(k6GYr-5Qb^mQK3FOodt^Mi+5ELa$SbGM6kEGvd z0?=c!o^q~QB~ie&-ov-Tr!XNhMsh+7*S?U9rJ5cf7od)s=9CFl@M%0~ZLG990?M6v zyc7n7P#we7sX7r%D0iV$L`HKe>Wlqv$s!pNkzU%}lm!jByK%0hW1QL1rJs0kGp)qf zt#^&LADosRV}QEnUAKOR_J?Qt?xP%iWfyR9tgi=!5++<*9E`(F*CLFx{*K_ZZlL$>qo z0C-&UCE;SaW}Kv^`ba|H91#mL4tK$F*Kz%sil&i7dr@Ja&Ml_gxpDUItW8L}fuhcN z3g=$jUG0JaxrKC)iQF*Q+&~&Z96}Bl;jNnvB?gm^u0|0}1fg7nTrwvk$x3I8O`fhQ zgNhqRA?MIzqiKua~2&d<%Mv8q&c8c%|^(_~h7(`}5Yrw%A}M2!JhJ64>#T?0KLug2^l=-M)*KYsvCIqULiHfqG>9 zgYRE_f~lo)mD9G>q|+#@lTU76e{nVS8PSZyT>4PJ3;IIm>%_rcaWEbyLWBDJU%dxd zA(&3{0RbMP_bqHrfJ0kG!yLwm+)UKebSRq|)gD&mF3;#K&8 z*!}X0k5UkFbQ!Wo#&j8Gtjm#^l#I~Car|;|a)+YV6#_K?h}lcqL~`WJ`SCnc_5i<+ ze+l(i<@cA2EnqZ!Q6(8HhCnyUBu;~WPYHi}`~!P-ZKEX3@A(i8`7$V4SW^R$5&Cr$ zVyBO>x_!Uz%0%P`_$&*0AV6BZ2+&5D{9Lp~&R=s1nofj_<%VpUiF1Zl$oH3=ey-19 ztC3J0vSTw{eJsDB_@jzbz%-d)VBI`M`cDOJ^g^8NXKc|z*7SlHvoKt}7_(7D0=sqV zJpsv`0deptuG@qx+DxGIk?_PEK^3I?`y7L#yQ9Wr{L*6 zk6;WSh;+~?gmdJvPh9a(cwnF-{Yc6R0X695zwu1+`mqG$ejuDes&jWxD@zSdK%S$BQ@yECTxP z#&g@#OoB-NQMxenGi4~T{QM|%x{;2!^)U75YGI-yIxG`DU=+r!^-eAWh)_HebWWud z{6z}+TfA6x92yZ4C3uBoWfBRmB;Z^U%do46d|dI}P7xg5s|c){B1$m=E?2SZ!e3_| z53K*+P$!;;_K;$3QLYh;-qqZWIR{)2?<{l zA?2MO@hDW@c3JYOls<%U-G$KF(5u&foKzs58c=_>v;|9eTK0llWmujfJLDVR6sCpq zck?G36Y&T&mL7~%fyU6~Xzh2~x?5o(eF#+~L$GH03w8ot_$p#MQCl(1@gh^z4YZKg zAwq}bxWTMeJ|aerLb+*rN0zrBJ{*#SfUp896&$=_@|qEZ-bs8`x%xFECYR<3Sf)`p zip5PPZDLo8UY`)B;yNGq=B`?CBE?0K!6pIPhWJJkzDUzCRsaF-5)e=C@MnAZFeu*J z*EwMit6HG7AZr5Ue5il+3=2s+DUW+&ASg5Ya)|@&RK%P!6frNFU*=~u_X*)w74rr{i8V7<5CJ$feUZ|Reek1(B1|hMLke%6h z8n5rx&I@*=PE_v9dp`WHyhfGkQ@ikmF zNGO_Org-DUzLatc7K_81<(OJ8ni_&;FJY*3ME(2p)o zk8iLLjq9^&g(~NI&UN`R-?97dU+*Cu*yW67Y>J0 zq~AwlmIrsUWE&VpN!p6QMtbBX)LR9K3sjH}oWqjzybmZ#^Fvi?&ddb?p}~+us2Si8 z6z@I?&4?+)?@e2+0AD zEm_)(S&$7pVi#b${GN|`RLe_Y_JoGOx3`*2ML6%-{?#?q8p&~QYL{dFzp~0%F!}BE z#Gi(F3hf(LfGepgo8iWkbf;# zig`2rAdZCM5IK{wu(x)@%rQd-8~Z zx(=;ZbQr^#7_!rdu~_-XqqRy@7cn7_Jm{)`3!LGX*2i1pqdZdAPaKk!ZrJ)L$`BHv z36AYtil)O^j3pPa9Hau;M=`9Us`Qa zX2e+;h#6+~8Q%4&??}~Jt)p|rvF>o1x8ghF98~A&8-Z;}4e>J6t5pJE~w^I4C*y&%wfh zo@9G^&GUmtBtH z=^A*UfHAZ5gshTj^;yl3_)O8mFBrGb=k!k}p^nWs>Jq7I%~p{XPW2}ggk;zY4(al0 zc+b@1(lFwK3Iy__RLz)|Ca}ib?tH*{Q0AeWm_XycoM(&y1#mC6!?kI8yp-$_vW8Wf zvIlM<94!jyLmWaaED@$595tL?69r0jUc2NgzN@061+pA5C#5`gxq7Ldn5k;#1RI2`sNRc5%r2hwLD;^zs-0+vW5;-MQ45k*=OnN1CRw>_LnI$t&(1o2 z1NABT-=l_F?M4JF3CytM2Blh;56A$~2_TqH)de}Hi_)x`z61Z{=g)l=Ru zzGPO!jBV70G-X7Ou-2$$m}#zj-n<^VG}#YH^XPDP{&no{SJCr#`<_0gD!=g>5<_fM zkMY}#MVIK^am8N+;shylPXvh}VHtu0 z1jwAF7L-6^K~$;)S|?5441HdVoAQ~O4py+MxfDfV~O{0xowyi8;AAU zTr$zA46zI#+cpFmT;TelRZLxZVU~9TA0ZJ&`GyqXqOKq8@BU(1B9|_j(v!K3s)@=+ z55Od-v~Or*=HMxOiR_YxH=v1vyH35QJY!gy&3o^{8FAi89ji@#vpLJ8Gv35pOQ>#p zqAFPaIh~8lm}3lsmg3HPyE5)_Q)1QLLYie9k<7lHS3$7BVP`753()?nmQ0yCM#Hq~ z`4i1wC3r*iP(MH{Apd@dP5>py&Vo{E-HYFdo$u;3l<7lc1cO-kzZ|>&iXO7FvHo8j zyCXe)R}eQ&z)RhHv7kQNa2Qy-V=boVR@_qcm02g~8JKRB6DQe8g-!c$@ge{wn}R{j zm(HJ;=T8A2%!`{l<%xn9e=WUt8c}>xmtc?w&lMe_g0Vt zYIEWo$|038CNtsiX@0<=q~8xZa1id~e?L(@?Lh4M#lEqZ9L9*I7Oyc4QJQve_P-vT z5`WmP;#hhc-QoJopSC!QrRn2O>Ew{B@86xB^J!y7Poaraw4+BPw#Sr*`kARk#~_)2 z6XVt2euj8=7ttJ`|1;$c#MxJMvxdKGl++|Ke72DX#a`yiS9_z*!#k$nFq+se6ev8Z&R-L=sFe$8;NZi2SU z|C-)8uNBdKHHtHpp8a0QDZO}g4rV4-hYT<@$ojI0l@ueF2^VU@h@=BSML7BG@ zR*_l%U`XOgZ15+-+eu0O{;?NE#of)Wn@o91u^-{XT%9di9NC zDTIBcRDD8O=ac}sGKCYXM=Sewh94IuvM`kJ-dl_rfgWdpib@qaXz>38`K0JRT%Khq zte~V%dvoXS*iH_}L)eY#p_`a>QoZ@>AWqr`gPLTYh+lpvYA?zV`meU>$Mg?_KJf|| zVh`!q`B^^(#_^c(UAvO+s{^{_FP2sZE5R`#1Bq^(1Mi8#fM!K90U@{%V$=G6H}^X0 zLA(|_?jzvnWP>LjV3AhCidFp#_ZeA8FmJidV_0f=q4cwH`eUysrRYf*FRe;?ALwcxMt$qN>E#AB@D4qP8v3 zM?XkIfHv3^(J=#+hQGm%aSoV674^JgoKclr1Wwo_Cd_k-HaZX*?#|*l&-4b5XW%?a4U7vg6oco_tW_gmH?!qA7&^@+6SS@sXB= zp*f5wm}!t?)V4B2al~%z0cq`0IDim-;lUPL-8vX-sdPnNcU@8VcwAAuh^gyu;6jO= zZPv1TCK+`$Vn9c|XU2UXUZ1Pt6}0s=euvr-z?Eb`KIU7|kB+pp;G&B5S+hK!I@YIt zw>=j%7w`#>Kbz3c?mJqskOX-x8C;_OZEOqF^i}<=YsQtqJf%m`6yeGP)3o85T^a*& zBEOjUktw{Gr%di6h%o7cAu_71HOl`IVrlVk7f#A9i*I7@Xkv{X4R8T8a`t)Mz?h+Z z&G@gmjaoI6G2u>Be_ab#Y*iD!_-A+Bg?68Gfk)BMNEVWRL^@LbD+rfo+yk4>UG&{U z4X;d;*Z4JU2N63_O!Pi^`m`B#qhov=iA~Tk;STzGdIKTSIw5~_IV5^#c8t4>G4uTsRIF^y;>-KrK#`B^V>))d z2u@)AQxk*|X4s9ID=^iY7@;s*zM_yp7+Q3c*G0>B%KwWJ56pv^|4I;cF5qgouDM4__(Bd9q>;pA;A`JzwsZX188ud=o8NgKc+M8r zhUE|>+L*&$D4$H|Ku(YM=(2PXF+8Yw07#`_8kZUpYH748j16;`s_M1oeq3ks*Z+NjuV}abksp&1 z*o|*vN?;T<{%pjF`H=^%I;Xk*hvN9<^mBXVX#&(H!MjqRf&)v=SV%hX5&GA#mrvmP zZ3U)5DsnKNG_(`0P;4ZhzEu>uO2Pweu)j$9`K(vy``^>sQD+w-x}@{Pb(?tuo{g)s z*$4oJ@~obfJpxS>0{l!zNZnPT3+ z!_I|&J-~`gejJ$bEC?ytG4==WBYGNznBD&J?vfmia9wQtBa|nb^l>Yg4)I5#QL;IJ z5p6~VVYaWc@JCHwu>f1IZs)vg@f9pqxCp6?Xu*bHM}s08frBAv>P*Vl>u179h{)5R zzkmKKby*atUi&iV6wSY3o>ZVJ1FQ_zy#ID%_!PnVx1bKrb*Q zk|4UT1>=Gd=7j`D=DOKH0>MXclh|Va#}R68L|^iYFXj-Geco&5YeZY=vS}}_J&H(t zeZCFLdQJYRK6;LCzq@~*D`ydFW}UPS!~akhyE_Hn7Enci^OMN!BH-;3)vKRZw~Ofl z?apP#-Siu$F}{Sdr-0wBoLh=nhxuE?2w{G3Q||Hn7?#^2-QTRU}il-z&qo zzBIc;*}SdhIEMt4&4XmC_7y8drV3VYmxCB9OGyaFl$SG&W(LT4@9Rze(YQ2QsKM58 zUdESdmGlbbUM1Y<69hY;U;rH!~F5~?9@W$Z?QreH)qT;^rZ)B1nWMuUMaf10kRY2al1-0D8#wgk9{gy{% zrb6R3yD=SJ^n4&={n1Ab+im0P<9g!DFcxI8OH}ie+Pk*H*z{ISX#8*a`6q5Pkk^hG zt=CzD%ep73X&YvdVv1u;(4d{i{C$j_AqTIyv07URI`gHh70sXw)tvw7WEqgx>Ev_s zKB&7yr;ixJfEVYJeWg0I65&}lr@Ui;Y-WvsBOQw*pYGL{(#I=V(Ju|tMQ~B7=Jj=a zQYgay2o48f2*L*huYLP`Tdd&^P5y%zpp@c8OOVN!ra$wBt5+SnB?ouvz!2l9%9fJV zytRLCk-vVs;4Q)+B4!9ncGTqJfBJAgeA#&O9uS8%jwB3@&HvKLljp&u61yil^{541^!( zPXJ3G95l?(g*(J{p1#C$VnM{E;S>(6mQqB+5N-{T`CM5dEu^Ui$0&+ zyP=NQKc0ShS`6DJGTu|A%+_Apr#am_&C6YUse?<3Ii#~4+S;r?f>{xI4sTzkaA9dP zU;~Mq$gbwvDY5Hy!kVPKZ!d2CF@|K}a^z1?{oc-YKl(Wl3U7to6ch4I^qTG-=mk{^ zBeN@PsaBzz33j0>TlPA0l4Lyab^T^7_!?(D(5~y-Dwqo^_-N3}Hzd7##DskMqvwa4 zbcl7@6{G(05kHHY#88`Jr@_#jDHbg9pBeHO&X%gqV(F?b%Xx&3H2|oiO3^^)$CW?m zZ1Fl17rF%}qW-FdJ0F!ZmF3QBPFa(aH(eyhbvC&lVej>5VUG4AdY9cIfM|A*&RAZs z1YyF+z7B!%9bKrF7gIbdFO;n4FSZePj@zHU{DNG%(U4twI$CthC?Zwi0OU$O5pRWH z{7QY`+1>b=Z<1DoD>ju9aGA|!WVEwFTTw3+4by&`ZX7n{;r^@myOzuf14x0I!@YPXn1_pRTZTIJX{ToJIUz z8WxzTx;!Ez^OOEcwqpe04ZN{=;R@|Rv`6YB)0J5=jNNfziS>*Bo)#z9B^pWSh@gnn z#gepOXpecO!laUbrV61Y$CnmWyH5Ok-9ABcDoGh@f=@D*^ieTTb15kQ9qdhy9g_k$ z{@a&NMiAt9^eGn!gFs<+&m;fyE^~r*p%o*y!Ykrssq>8BzFfd-mGRaH#8AF+^4no!OlH0jD4=7a!Vkqr?| zvdqU1bs?gpb{3uT<&3cs!`e7~oS>FdS06aIrAh)AEZ%vwIkxcBAU1^CI4l+;>#lw} zry2!C|4^sbn&#P9!tiIufL+^G6LlOWtSyBViO`2)9+@!_q=X|giRT!c{hYV=99nT{Jv}g5wAH|t=b@{2IOe4vI~vb%9E&#RUoxX zkTP#*P1UK5yzgoccxW z-m{|K!JF>~DUk8FMNP-04Q9$9*oZ2`a?qX40z&@&WLZ*NP#w#g*C{Gx<7`gP1}GZF za9v94Q!R3Or=KDCh03C-s3en_+~VRp1DJf?aT2eph^~+Ne=zU#yx(1(ews{DOB-b@ zFv1N`Pn|k5FIa-NyKfbz)Gz->l ze+|fMJ>4ofUsB5Dh|RQ1>Sj?Q9As_%bN}{cdb?)&L|Br{P2Ab7;{Altm%QFDon^8Z z!X$4?MlUc2sx{vR$Q+0&VtYI>y-6wNq(K^HZTQ<}vs-UGuQ9ntV!<0zG!H!;Qh4OH z&6rRK1`qA}gME!}$BAit5%_1MUx+J1U4+8pcbe<&oJwi)bU4_HB4dhC#`x z{m93n?WLUb;bI{s&G&pE#m!B=U-1;gGM(A0Imnh^@;lBGlTF0^zxI(dF9Z;r7 z%=8>EQB^Iyx82G)ziF~WAv0w-zuQ?M#!jud957D4?*WkDeaGd6YKY(>ky8szNs#attbzivt)Ajw4rDxL(GO#OSgX;OF60C)XKVSZ?OKz5-F(S)6uq^^cr=`V+n zXx1;np@56AyGu#&t(wl)Heb6`+Hwt1e>v4`doMs3->%{4u7|WW$+OzBmcv~4h&DmT z;vTgm$Ch{#52*gZp|;130TYPxTNN+Z83{3=j^KK}H+p%Vo1?J|8^x z<78vr-*>cq#|TEEK)SY<0mB@?o?Y-K#Wv5D^qT-B*i(){0J?3h2x2ltX|GTv4z6mtex<-c2;7vtnsu2sFFNf=2_oTj$iHVCkXU)-ma8F`x zKA0Zh`3nHw?(dKn_mz-#{)T4)Ed2Qp&4l(A|EC!DBbwek`jVLfLd6I6ENg=(!dHeL zw*B%8^lo7`*vY|YFaoB;-vvc=@ncZ#fr)c27wJcjL`av6pY*WI25&G-NgX>LNk z3`%0&f3!K43BVFTC##lN!)g`!IHOAU6#x}vt$;XtvvSLJlu^08i1+SyGm-}d1`_s2@484N_!EOzH9u|TlwtAl`6@qu;rPp7XQr7n%`)ReJ6fu1If`!Sb ze~3sxDEUZim|)_!7m)pv z57M}O8&hS%B^$JF70El29}qKNI4SM;{7yQ49)0&bYG($ijRNSfJLW;U7ZAWDw~9TN zzv_f2w?I8Fwa*ndtwG8WukM<`YHf3lw?$yfatXo&9^O0SUsM;tfm`$&DNi zOcHvXeZthHBkGAU)lAp`K2oJ6iUPUCh*-vp>L|`ABbFbL8W|Gq?0W^0ird>Jwiy{e zzwha3lC4G~VX!188iPUZ1&30u6;VNaY|hlE@PIh+*<^-)httOCoHKYrHQaSjCM&m- zm!W^+Gd@ITopE#eWfFx~alPU6+Y&-zO0lJ$EYpj6DJc;*IxqQV1ucHlriuO( zIv}6q33`FQO^;L%_++uT`)#>dS9>M~qjOwSBT^`ax`Z6$3+Tdh1a;;zj)V1uKroSf z!py!YKgjdBSHL>vb!~`sVHB;-9FIUru$v9`6E z#My_`HW*L)e#f$rF`_$!`11e?wFl5pt=g=Gp0s6m6-Mtl7*q#tZ-9^4U?8pVk8x1v zmhwLaWtj2OPnVF)cKr{_p(3++-Y(QXN;53!V4)(*+qD8cPx32?Ioe?qm>jTnFZl+-lX{2q zT9P$n-wf`jzXFUs&ttqFcc&1)6h2a}4eTdzGjKaW)yfW~##KnWP!n#*bN+js->6L= z(jor=eC+>~BIf4h_`d?*%Kw0Gi~IirU*;rc*(78VnAeeFQbICnnn_%fe%c|BKpevq zg>R31V?(5G+bU?yNxHFz!r2-(in=C-<{an9rXnSZaDd2sY;FiUJ|? zSE!%`@D}I7?XK&fV@hPr5tjlflfe(K)Rras&#sbs+7!0mlE;#nIgnV{pho$h@LfTE z*DWJoz7)HTT`O(h_ZhRAm3Nm@R%G!=@pT(O7M%m-kDs&u{Y8{*`Uy(L*~9?5(3+|E zq!gO(G$N$l5@{{&IrX}8SzYk}9-#3eFwb9=zE;R38KSQ+1-s%s8QW9CcPU%kB0I-7 zY}lAwBEY-~fN_x;~^gEKj)|NTpIJwr$7%#5l6Vz8}@2{%`LY4dS zwy-5R2(VkrzzUy8^BK3<<&9pHhZ4m-v_0}>d`8}e{6y9{luG}V_6c2mGGgdWP4@+pHiMTzFgvzW>oNMZ=UiO75BqP{wChEImKyUm^J zw_I~4X*&L$-6|`ckz6UC2ySS5zO<@0*>Glj2CJ0ZR#}>|$8MsEmz{EF-G~ui8eAye zPi}3GzW63%y&ciH*qjz-GL@Fv`=EMtZzfSDw9!E;L1XS&V7;+(XcO250X2NpEbnNO zk8{EpDG*nyk<4T0$uV3q*DdNnp}Jl(Jwcct3e%m`eYF6|3yG7(j5@BHEH#rMy@IUh z1i~S}w&e!@gPvRyN(+t@bihXE@wzQLDlxZ_3Ie3-vqeN#*XoCjD6tmY&%E(ri_>H( zNY#|BUp=u8V%k>@C$4KA0n+Sj-$Nc8%T4vv6jix3%9sDzYtc(MhPP0lF`l}2IK

    3f)($M4>NqPFGY^ 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

      M8xZU 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 z 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⩔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ù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�ToDAK!bhsl#)k3 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 z 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%0AurGv@Z`{Uy_eubLa+q*` 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%0AFX{O*@;M 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

      U$#RE;(0Y`cr3DS+Qd;ADbNYIr_-70 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%0DR`$^@IsHm<-$v$#o#5+XWrnt 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

      D!EckT=e+XmW$<=qdGT3?J@tdKb| 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%0D!KE{-)pPrd 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

      xV82(R+ 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 d0J@A4SxMS2+i{W|Cxxn|C_$y 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 z brkl}|!2NabD$7@Xst3Y@XJCp0rO$nnrTyu=$=L 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 z zi)@Ol@Is_b#;B=@Nf|}|h$G$4)Bm^qvjeZ!ubC@s|5G|Fu^5+SoT-jfpF5;v3D8?@ z?x6}V;IA?c4l@HC*<2|Wdmnqkp2JezTTsWJtTeO?`6nyy$nqdqeNpnpkD~O_A8XSz zj!FxsO|a!aw5foA@d0rd%5rtbifzthklOmq>&K^&M%;U=)dH(Z|IY59}f5+5enT0J3NFMN!w4?E*5{Kc*w9csO&QwXQl?U#!=}T zd7mfu2S&DR{716SZg#ECe(a0V=Y4y+rLxXci5Nm!vhrmmF7|dj>KgbeGSc%^w+sEN zKkI07{YUSPb;)8o%3wX1U;?F^vobk!1wg}U1V^Vnw)Qkmg+-`I!zo)e4ZO^N1YRS?big=sDe+(#-FoT? zl#>z-oANT3Xs8~G5hp{-B$uy4|I-GUDf!8dJ@czanu{(SK~SIpm1Mq1K4R3h(f}tWq;2^? z4P3Of&?Nd>d^CAYlY*?621F}0op&usHx>GLCqF*;@rK4c16L~6ZADUGO1Zb zk*ib!h5WbnrDQ%`r@QKg9HvF%oNEE|PO$Ytm-zC{m*nK0iOGa!y(|nqa!PC&P)YkZ zijSglQx(p*6mn}wzP<~gs63Ldc!)LGP$AA~fX`e!AMLu+(_#_bOqb4@c}C?id0G(H zaWhf1msVC1R7h=VT0)XQKp)LrHb@m+3-m7eq)kkp`jLouU27f#xh)K<%Z_$oRADfD zRSp@h9k@TM5-_-bLXSjRqD%o@^P-V^?(u37Xyl1YwDVl-WIwjt&mn5y1^5s@>n z>S+RF0DJNxTn)-yyXBep0iZXX;JOrQ;Gmc%fGRPnZ*zTV?<19 z4m6;%@k*B5BtU?S1C$4TfyP!2?Iz@vuB$|B&H_zQi0DfQ$bL$ag;?^JR#Ut^-wD{N5}u( zD%YICeVk&7D+>PH7>l`58{$$+z)tO!)fsG(=nm-NP*?Y0hL0 zr5)G3Pzbq1Ttz-v=Y2n#6Rq*`ZMjQ}908uaRrJkZw@06%$AYHdlH$b8nA!lecT<1B zE7rOx|C97_{4dhS#=-LcNFOIN3-kXJeGDwjjQ_u)uN71QXB~|;s;g_M9mJAG!@=D> z9SrMYl(gOZejkCv!vPv@AP6jdKMR3Cz`@tV>~?zl%YVgtMb)Lg<>b0&ZnNB=Kyl3| z{a=_?kVzqeyBi%`0fK;(XmT_@0LY^QK;X6o49up1yZF353sx}Q_zEmcD8$c{;3TTa z!SA%Bc02!9r2rv-D7-iTI()!YDFS*Q0s_Q77!c^+5riu$kc75JP%R)84uB{H$Z^;x z0jTS%Bk0y<2S5H(em;O1H5x#A0RjIO{s90ac>?wNBoL57gF_oICvU!m=>f!ikXE4J zE#F^i&_`-+S634Pd3ky|dU$9uHu&<2T1qJFKB!w8utm_eKwTbzb^l!$U=^BN0YCSV zfH8o@R^ZO>aZ3T)tsYt(K|k{WL@SWsb{;Vg!771z|I+~gEvRWg7Bj?SdDCM)oJ~NV z8Swt${wMd2{z4yeAfZ2qwuYu)ZVoL0pFsp|KiD+{P{jn5bGvs}6Ce%2Y9B5w&Q8JH z16m%MfjIP(c{o2+8^E}f1prMR_V<6=javO*Qx|qP_WFZ+bWYzAkGv+B5iO{z(|-|m z=Ka>lVckGAdEh8HP7c-H})EW^=d@{zZdJ2!7J1n?v2;&!Qc} z?E?b^eh2vgBtQck+nSDjaQC#vGoQl~ui1Zg0Q>8kS1|XVc0eZZuY)~*hn`-U9Y6xL zcXlAYr&=o_?s<-@Lu%jSch>s6OM@`J0pa zBR>9<-|uVs-edXg1D){V;QBQ*`yKlIW7Osd#P$7k@>s12|7!YGEtt~=_|RA9ozLg4 z25JQJ=J=^s9oRYageM_rjq{I3ye5Nw1=Fk!-l@I*MVZR)p1mV=1`!sxRA9fqQwQyz z1qJzyzwvCU!O7EyYcm)9H3Z_NpY_M51a1h@`rBo&>rDrMR#%6{XW4H`X#aQz=+&94 zwt#*76&?bRgM(Q9;RLzg&g1U~yNr7M64QqQ;3)N1_9p=311O)}lSshtw|sy{0&)27 zv$p58j6;5+AA>n;e#3rvrvdoKk^uy?N;E2T$kMIRJN$Z`g&3x!d6ZVNi*FsPFwU z<;f1C;)5HVvA42b?O7S(r_VTpSC4Ftqr)|&V35fzE);z{D;GH!<$f5;>+Q%HoX-Ct zQk(G%H1o%hch*yX0QQ!h-LY1!y18w~Tk>2IMv`)m?qRGP)0?!nONB)Lo|Za%*z6SJ zgw98CnuQ})OiQ)MkbMj0``D7Na4DL`*S#HK_zWiM5_hW$`axWn)17vcU?mc{vjoka z@N|o6>AYAFQ06y!@-JwJvHSY9EANRzDTiu?2uK^uFb=S5m2b40&_$M9z!+_jb-G&O z+5G8#Rg32KhO2=#1vA3!hh(0_GQc{(^#O^Dj-PH`P&OMlam&wa+WA?4aS*%Tw%DvZB z3}@0emAsb0?{I*4`g^wd>b%NJFc9jP?>q1FlvRpcPh`|}xmoMiOK>4uPhI=GGTTiU zr_~f8$`flGqAfPbV|rWr^lBRlR8|nfVZ8>`>0LD5#vp}P^Py5*5SIS&mX-{SHQrKL zH*-DE@t2BA_ys^Y}Vmo-8}woq4=Sb=M8_5ckIFipwz! z9=-RJ1*u6{-rGC+;ucxdmXAs(jghInW=aA8UnA!8iG#3puptJQj5ql?YU}q^*xrST zGZM<6tIplVt+HzsK9kT6nXxu-(43KoUV65Ie}I0j^LMHVPsHy%AL#_UnBBV-$tjrj zuydsDGjkaM;O;!HUVsO+Qw^$I*+hFJ(FWJVdG@RrqKaO#dwI0peljSP=k#JUjUCa- zu0%96rge6HVgqBS9Z84tqBX@kNAh(qUzsJAElA~-9LoB#pkAbt22&t5hEVRF)57<7 zh?k5y1iguVHl;@owq`RVQs34V1k|^~+&vYqV7*xD5{Xcu+oMmw_bKVE%5_LK`iw~x z4`d!zb;0B~3$2cBU^$83w&+WL8AY`0g?2<_-;0V+^RW{!=6eHj(p)lEJiR@A4%XSN zAYma^*@=8RGMgpn4^R@@nNg53avrfHdqb?Y#3pi!U(32lyXqb=4Rz9`M5!n;`c zfc$ZHJAhRhL-Xas=Zien@rayKO)XF1cmHfc!;pwGl$6dblHrT@1zO&Oa+ zG?U8Uwalc(M8tiIewBIP&pKP!iJ3H<@Nz$4+rjjjt|(0S^x-uU*Rm#SebK=YuJc_b zChhp{b2ZLBoyVobIL)b=oM=;Aej{8Bu8Ul?H@C~lYS&UOg6$a8jN@he1v1j(yJeW< z<7a~Y`_9!fxL&Av?1k7`;O5XQy$#7=_*pFPEt3rz5e*wAXavNHdsC|l-RhN{xL|t1;#{T%=FfjGeg>9Zh0IPQp(2%)m5U6 z-mo)ynAX(fKLl3F5u9%`%Tv1!#+=|Df!`G{%1I7Nw`PH^=EKdT!V@Fmg6$gi1}x(* zMjwgyGr-^h+eFyyfiKWVU>W>lHK=)YU1iPkRExx-BZkZUtu5=4_C=a-#<;0`_pU|K z>3*gpRXs=F!~#mZ>ZaFn)xgh%QC$@9&0cHmjSu%5HIR3`dhQMF{Kc5e{b-G3HUk$) z1D38fkK0{c3?|<45ORgDKGwQK6b~@h|I+l}>hNle?LV2ToCa zcI`*{6JCamJL&|Y2GONp_rg7oyRz1=PL4G5(e`M~;TJv>#>z_-yKrWR_~CCYIXHhn zltjZy15-tLeI}|#R7V?=+%>Jx4_D^nl|y4m+3pQ5 z`6Z2y+NyEHFR5*o-Xc@XYS8N3BG#C$zU?i_lUjdiN~2RX+wz_{UiXA-xcvFW%~5F?_!)N@cJ>_cDNuU6JpM(;lN|E{X2+;VxB0R@SwI%+ACy|4&gJs($M z$>#SgQu)2h0*L;ZExj8TqgOQZ-=w3@rMz~@=E~Y}`h;ZmFBpdUTCt`>2hTT`!c(6f zoUaXPC~L-1R1sN1P)vQ-Lmi`Dz*%+2iY1aiE1xEYt*dLhl#l1z29^BI;i^ zyEqdx_f$2NJXsS+r@= zRU`(czkP@KiwjmTlQL%&iF-fFYc)8M}y4XiwL;}6*RQP!1?WGQTsLq@qyeax0Eav%Vk~M8tlGqm;pLnRMfO0kcI9N>Z63-Y<9b8zYL0quBxLtED}sez zaiamj^jXZY8rl%}W>;aODX=o`7#s8mRZ-<{B86N=W*$&Z@_Yz}2NVL_fr?t)+2t$sf=s$LtMGbg8Flw$fcE##1&#-^sX-oIOaIsR0b z{iChML40wfFq~qSu)$VBX|FvZ89E4-D{eQGW?&sez~ewIkL{xUIG2re_Lh!zUXoHh zt?_h2T3HOoCxTFx+RCm?^5{$A7f1k6{WO1VjN8*Lo_*_M;cX__1od62r+q@~E}RO? z{X9j9717ftEe2t#ruQXR1$|GM(;I?89w+H&d<8?#vc~vZ4YteMyV=#fP+ow3KX}d- zMB5-nBNVCXYV$mlX5`pyF|>ri%m?qr8Kr!?YJi+tdr*=lR<1>;;%6bw9V(V!iF%zY zEw)5L3o`?Sfp#Mkk+5v3eqL_Ty@n2>HTD%2QAr_}Ajin_5+y4y8?v_lDJ&k)+HYbR zscZNoYi}z}GMA>%?cwy*XbIr|fr{H|Z8X{sHsjULEuPYDWV`&0ZqGL~a&Cfd%690T zzmTn}6-OzIreCS07e4S80@sVU1tf3m@fxAjQf<_)=b+%J9Qb4*agsX$v=ax{GD|x%>Iq2B( zx*~4$)%pfWTi(f|j*>FQv3q0m-mEC^R|5V=AUdf>gnt_YOehDXVGPADd zQe%Xzj*z-oC4er|*Y3g4%1|8ccV5Y9q$HtiD2vCg0>l`YW=d(0U2Uw_gRLU5_3OI7 z5;$9X#b^LLK*PUiSf-9{Xx*gq#e#^V#G}>nO^L!l5BDs*AUsOzzsWF5MMg8OCRY*uSi>lK33?V6$kRw|;5w9Qd{gHD z!c9>aCd0Eh!bZi2amm=mpAJyw*M;0916jt0`q#I9K6_@A!phDeEk(R=oy?(@?H9H) zTg>f0DR-~8_gJ?a<^DLB_m7`y%M8XOd#J(MDyZx=`m>oO3qvpXAk}e$%^_=w@!uG( z5UI+i!WCLDIKnL}j>8Zm5NPTW|K6Qs_k=wnevis?w*m0B^c#!JQud6ezfbW?ma{|n zt~G%!SuasozX9@;BAnU%n5&_gSAUu=kL|UelW&_{i&DixPF&o*Xy~Uj-SSPmC}T<9 z7dA0%cjfr9?rbxj)buz4_nra5tSDMcALlX){$0vAJu!t zWCWR9BY1v4vBj~iZz&pzz~`;YfNv3l5Gcz=InHcUXb193(cb`; z1B2&JPD?>GfCDkv(~W9_`}Z?ld&?&=bXfXqrn9!(MpS)md#dvc)T5Ei{hz+)NE~UU zg^a!YMjTYv4Ia>HN1_9y zYBAml(Qww4AgmHo0u*%=fL}!#d;UsnegV~f9e+3Na|&$9pCb7+!E?WyT>fg-mbcu0 zg0~1b<;SHR4!$aGQ!O1!W|`#>9ih&rvnL9bFkZmzavG>2Y3r|YNqT=2Y>fK=iEy$b zNcz+=hwoHsX(9nfUaWD6Q&KcRblDkXW}-?UmABuH4%QPP?}mg*79fgp08U#JH%Xl@ zz!!R@5Pe2!M+yr_=0w$-D%{>>kVnZqGaKmhS%5#HmgLB54NSQGAZ7nZw-{B6it>J( z$h>&y-MvimnTEUe<1~7sV_+YI@g-rT9hsj5dlSR-{t0Qeg(wr72an*s)wnb^eIQH3 zyqL$Ib=A|HUf!1b0CAC9C-p@TUBa}3@Ee}Z?RbG1eSL7M)~)Pq%WZ1rN7t+BK%-9TdmF z5{UHlj2)r;2sy5pe;1H>V)UnZa^Mas`uV`OzN;`2BO=p28lydl+KuY~iU)3Z_8s=Di&anZ$Ygw zB{~fY{TuK)(e^vW*tOe*?o!LNE}EPDMhE*}(=5rPD$KEewmhaVJ?IezCse1^ z3r$J4s-RPRseFpQ7_sK?IC4puKigdh2}7L_9Bp_Mzh=qtn^Rt6@ZVLAV-3ZWT23>; zTNs+Ne}|YOi1J@xdR@IfQI@6RGi95yP9xqd1N-}0@i*b4FReD+ItMiQk}pk2Jrl^& zWy=|ix?2X0EErf(?VUYnq_3=u%Q!Dtx9xeVbhe zeq}qBKHhDK7v7({Dr6q@DUI4#GEk?j;n@9U*P=+SMIWjrDs#Q$Iih17Xgn^Sg7l~& zT4RSx@yi_rUS<;t&$)_?#w4Sst13J7RURdAQ&3&}w;>iO`J!a*WpQD!ET%}8S&6<+ ziW@fEPGl?@(YD?@ZMJVz2TncPwpAdYK{czlWvTd7OW&#tlk#Vj&(FRXK#3F@{f!e* z{fpvXmED`Z5%F3T6VzGRzYy~{W(Fr(+1I{;CF3}$m8BsGS(?vvjIBXHTy+<|n>o7D zgHGmIwf~G))e;Q&unFfdbhb^msWh{vN$7q^WOJ5&zr4&T`*x&V-KNc{zuYyA)`|ZK- z^Wv`fi9GbV_3w}>vXfcxPo{!Il;{&a5503!AbdAvN)*2lWWRveHaloz>uJbx;YE2n z;fI|R*5x~`yF#CTp-L-9YM`?m`kMiu$R^?3l+?YeC$#FTyvOtCg~XU{t&jqWj(Uwt zsz}v}W;=Y`6Kms%%YBBLm~>sO zexy#RKd4owQgC|)6M60~p^65(t;RdI+S)b5)K)a-tRH?#anS=-#Vb!{CJW~TB^OF+iKvT#- zHeE3>aG1=Wd1ZNN?USLB%8ou5A+PC4C>vr6M*Mmtx(+c8+M@c1B1TS8s%yMhMUqoKrgRXyZVWUl7Pp?4cj83{Xi43IQ-OqeX?n|C%Thg&YgfJV8cl$6v2mhy5EEGn4P^Wh)Q{0VlH+MB^f#nnJClmHpT$70pfTLOl)5)Im|WFbamkkIzkk2akS> zfyEH!$o>Hmf$pfi6EKcf>8TEx&iUuf%T?^wifI(6b7g7aQ{PZod$F0Ce69DP<>o*d zQrj`i$n^-nn_(k@q`K+(xYB<8c+^`eg*hd$y*Dy&NfQC@W?5>s^}vOn)No1Df^HF| zP%+&b83&_&OiSKdkb_9wbd2I)s-WH;GjqpcVGC%fF_0k+tL{O~F&Kx9E1*V-q_Y#F zY~kp!xukX)NI&}KONNF!?^$_mzS>{#vEQidXrRpk?@+xLzGP+!%?mnD0yApb|(o=qrZ6IW+!Sc}cvLj3bNYf?yz-1(={`LniLf zo}!v;-Xe?jik}3BS5fi#G3-!|G16jYj>5TIFsuWlt|p{gejM4kp4l@qtcERxaqm6u zW!?7IU<{s~T5kLnNkz-U<6^p#XuF-3Jp9|$qpJDcvUNV$^$s{Ct=YRg$>5KnWn$2> zQkFwH%F0$)_$*nsAy?tBTs8$Tjba0x2#^$ot`MZ#$p_TyEKDQ_wjn|n(n-FPQ1I6U zJSgUZyZOE!!AP31aP$OF=bE|O#8?nwWVgEGvmP&A>-_=`xNOpIOVK2>sTy|O9qG7~ zaPDqA4s(W7(uRC!SQ81P8wi_}+2dTh#t^{MrCEfJvR1neNJ#cS4gA_01VG)rDkQTb z1SLvKi@jnM7&Co>r$;y_ao25l=7dmkq0PQ8Z&>D$bTpS*f_XR*iCtH_JDvbG!@J#; zNB=h+Au;mfo+YWXw8jBHtmnewJV^bbZ35K&Y|ti zt3Oc|mitd$9=}&VG+lGUB#&2^H42*`21!nn#I37?YTjPmFz}<&UPY{NDLlC>-_i~%pw`?+#<9rpn6MWfEQ(AUUh(A(}db%q5HEDlNXc=1Gr1*_UJ zp=5^fu+%)cSkI8xicS-ywvo44+X-fVpVv60RpBT-k4gHco?5E>o!PzS;IBGFcJId8bAd_2xBxGOWteXE3n# z(w9{kL4q=y>Hh8l{oVUJ=CZW^WZO{Wshv%Z)b$nvgN%?bjGsw&US2C~|Fy$C4P)Al zvMdZ(&cvHrF`{3VTt1hSayE z3DMuU4EMEJ9g=p*FmH%%u0&w_W#mHcF##LN$lnbV*^%XXb-!jm0~kJmWL+QYmA zwF={1O89tAz!y`BN)EK<@CQN!$wsYshOSV45A)IQf2zsHN(n3ZI!d*zjYmw5j6qen zsc1VHf?z5^1qe`0T&HZ)&tiUOLPQX@G9@M|1-kM7dsHs| zmg+mj)FGMZG|0GPD0)AWN9~J8g4SY0a@;ECP|-V3qIrT7#|L!qia4Y*!_S56)A*B{ zjxe9z*%XEOT90b5nq>Vcu$3SuLNNq;a5Bw@=_bZ_@~qrNyCS}LlVqw6 z07?gMxSGhBGI3rJYAuQ}S)AA`*e!x6?RU zv|*|fyBD~T>|84UN7U4o>rAkJD%ex_(gKu*W~FA2@+h?hkyvrv(){7CI>Q$wF9REZ z-@eS881mE;r7HO`-owyx=3A8#{Iot)J0*RHB~AkrTAA}A_Y<7d1EyibzTAzuN-vjJ z?yxR=O7{F<;{Qk4J9SqAs9n0TRk3Z`wvCEyCl%XC#Yx3kv2EM7xnkSy{hdA+eX%dP z|HM1yJLWT$h zzdAHI_Sas$UX!<04OT!j135Nksi$Y0XSf*u#II1^(sdt9nCL)+1&1vffBO z*EaJearKYm@4}5m*+QHel9L9r1206@FajcGSa?+iA7(*`POVtg=J+CIT<+KH2F5W` z%@vm#+mL{IT|gD1oW1cF*9}MwbdKcT$SCh-1kqFm@xR;i6?CQY<5%veP$-KV`lJ<#%z7RHhX&MXc#|O+9s!zm{A@U7XO@D z?m=`8h`AkW6_?Y}L-KL2CZtU>45abNbH@`PQdMU{s9|VPTg*krJ&osLHW%dB8=vm% z(vT8vL`4>d(izrZJV}l+m>~1IaZhm)#p03~VA`?Ph$rbvu_O++tEtRK;w7`C{KZyD z1-kV%B8tj5TMq56%8A*qbZD?$F&5YzN^fQA&c!_I*O{$^{3q!x=mEYNGgGV`c*E@@ zpfF#K=oH;#IlN_OIj{|H1$e{Lpu0Cu$W;)Gx=+jV*vDYwS}zx0ibnY7NCpIs`~Qra z=J9Ahy!Na7b^8tgNe-pA|8LwR2lId9CfT_D4{nl^nd84MA#gD>a}fXEa+8{n>gYPH zCAvDQPF4xHI-;_?MPeOL7Lhnc;ZQO(XXp^(9pow34iIMvsKw&e*bqBqFF!Y(KR&kI ztrpc8uFtJ6f*29fs{nTh)|IIuxo^adVX6ZNJbFn{{k35vs?5)K%Jdga(4GXc(4-qhE{Ol z=9(k}MHtJ_?5hEw-psl%FnbU2=-H}95Ca;)xx15-(X$(nGtjOc3CH`Oo+2nlfs8On zE-w)4p#IJ9o+ejd@7vf!pm@SW2thwA7yXyN3|6j*K_-9*^^hWaWgy691Y-n&ym)z~ z6`*r2kRm_awx7;G&`&oG5KzIOfYJ}TcP&zwM~}MZYKE!dW!!V{AYPD5k{RN_)FddP z0la?jy6}xJmnQe{LFK!ZoD~#HTfBdRHv`q6vM#(Jw(0=^ZqTi65pASEU#G~2TBNst zViElY(xq#bsbNS6i9T5W@KFAmRE5WKd!*kg-k7sUN!bo_M-bF6!IKmHkiBbAxZ97LJ|PbXFfb6lNCq$?aF;+~*zcJ*){x7eOO<4g2v2bN zAnJWskicHSAA^j-Hgo6@-jC0#Z=beL$$Q$rlDJ#Fc;8&)Ovn#VuXk`Dpk2YF!suE- z10XC*q`+M5rfQM^4bV?MO$-}k@W>w;OgFh7+O>z;GuLkl1jargSAn2>Y(!X3zSsdNy>e}H-ED)E)h6?N! zO?Z3`>F_J7AH-jf8X*V{G;aeQWNmN@)_+>*{xFnFz6C0z>}*{gfu_R6S?aQ-Mz0PPB*Z=e$%4Lt&a;mp}ZqJ)yM8 zmU>M^Rf5I~Tl-SJ!@`k*#Ejbd@W-mZkHU=;S!S}EPR(b44&}L`Lo>=$K;C&S^;&Y} zg=t^!XeHCf@XyBdF7p~}BW$-=EW_&b0ZF|(;c%E%ZG(rt;rfEr>t*Up8m)@kK(4u3 zS3MMqMgQ=kougfYrB%8!N*97B@>qapvV3liA9;X)vyUM|VivKHkY?H3teFDywabV{ zv8W-+j?AR2t>|E&m#iS~_%&58phQyOL`$`8OXy~sT*LF{i6H0E+(5O*zF$iNWgBZQ zJO_7nKFmF6w!J)%8R^~s@9a27G2*|}>nRkO~~$R;2phv*-h-_?LCFCh7^cxJ1~g_piie z^BShLgNo_+6NHc6g(v~?I13W_Gf&>*JcjLhDZo1#f$gCh^=NaMoGEQ7D<+P9(0iM& zcwaX;@XGA*mAJ)eM|q~(=C%8pLc%cx3pnMXD8b23R)2Fi3xO}+)y?{Mhd3GR&vPc89Qjx*c%Kk2fc#_UbY^4t{`VjuA9MiQx^tp7_BK0>Vj(-;X3wb0vXuTi zL82Nj_<~l6S#jp->djx>B+=#BI|ICV!5EKGMNbPgowCUnGxk1V^+;}4dSZY?;^!WJ zSVO@N-W|=crBCBG=-eXt4>(t)PKDyhu+)))3-2FGDkU^Y^cYj3Dt&oU+Y_$nv26YD zlwkO-$^$Lb3pRoEMzASWDr*b5VzUtGY<}*JOYYT1Y7$51m*>9+UZP(;zW5oVhviO> zby}F?-}$_gP_91He;okf34$^jUEuGBwH z#qTcSeevS|QYa>@k(uy;L{IctqAbi(Pbt64ZvW4eQ?R7{w^Sg z*IPWqwT`N6u0u7sD20=$ulEk_m8>LMnWs6H&d2vJjvt`l!$C3-yrx zOY*JGFv;v(!)SfGHixb0(q?yOzmXp^bkZz_VZnPX{%HE{*%;fQoIh@b8e6Y+&9UX> zfHalJdBis$@nU#~xjmHT*5ob}L*cS#Ft>irfAO;~RYuQX@C?Sj} z>Tot!|AP$66}1?-zmvESsQc*X zdDs8Jk8zCG8A5Q~`e7oDZ;~DB&FAy=Z@Sg<(Q#QmYG*LouR*rGql)>wfr~LxC8bDU z!sY8Vp=z z7r!);bFqwqH~jFW)HJFpm{xBVs9yQV7->$+5M17t@V!&wey1loNmvqTdU1&Ky4 zY@54i^v$ob)u)YFElw+LfxGl1Wjc?;FYA9R*PS3Zu`ZI57i+>Ncd(pyfmUMPa^f4p zxE;;U%e)@hgdx?ZTKx{&sSoor74xck<+vkQ>dj&Ak&6gsq)SQjT!(G8Z{vd)i^LmI zs55CD4Y>+6zS)FVDoA-{+_!b~6L`RH@x#T8wPQ=EJ~JM?vc(CRc2~~SzZcx z0K>%XzHee@S?;1jFdcO&p@OxA>{S%PcInQ?1uOvL;3y4ub!IkT?Pb~j^d)ex*pW{J zJ9ChwZ%F|2*T1lzz8+z|JYTQZRKS%hltaNb>pY0eNoF^_(v2Ld+p`pNsw7@tox(@v zgp8Q3(|p#;!WHGN)+z%tWvOSXHtLeX#fqq}crlMb5}v4yx6J`oL+NQ#-k;X+<=Cv^IZi2FB!y#g5lH$C2Ron$Uwi8cVr-D(KrH zRdk!L5>ubuQyN_I;VsLb2~E@}WVB}3Y>XFgE_NN;=u<)>x5CZW6ZG-Z`E(?v2Fdhn z%5LA^hTEY$0(7|DuolnYC<-zZF1;=JiaTgM$M47ah1Zg|mkTbDZ-=5G3^_3mi$2ut z9g0_U=4_RO7$KSuQsO%$npi$==C5fdTs2vgL}V7%D-mc}lG=G$7@hb! z1_dFfs{qQyR_d|4J0r)1Rju@u#lHSuQC`(rO^@b-MU|qpo{C^LQjdj0VJngDySXJy!mOjF~;Sj^n;#|?;G#TJZ*KZ z>RP_QSYej5LlBBfGxSHd9`5MFSxkyVtaXy68NPa=Xt#%@MVRFKctOTbl{4Imy}39_ zP!d8S((c8)f4`CMu>iQOoiZcP@Na$X$&i$3z(wfL&jHF-SpuzOhNI3qo2;RVR0yIc zn#RHXOs4U4t#aR^jzZ&kG%R>EXuS08JKZ8M^M6q=b~|-IS_`zx9-p$h zNNnrI%Z&7hMGdT23Qjz^c|2!)XeIo6>+O8g8Iw+u896CSAo#D&dVHMZ4!rX+{$=@@ zB%qm23Oydz>hFmwRIW{hcF|KI z6hq9T*VpMSbqtHwL*M{=dVkNlX}{A)ioFMsox7k~U+@FpzMj7pmf+knOnO!Ui-%{Y zC#Uq`aeN{vRlCq8>Je%zaNt6yxA0@J$K;^kf)n)D@QdA_P-M(6So9jDDIbii5~txQ`zy|z=wEGg@2WOa z_N$9s(=Yz@S2Rha`omGglSKoZBIZ#Gg4B-PK^6x&i-S>>6eGgs2e|ab;(B_XNuvlj z;ia{yoq4{XnTkW|`Fx7^UEhajfrTt2Eq|kkwDpoly{y~P0 zp8;l9o>GQhST{zoN4`uSYQiKe1cG#?A=G-1xqfZq-!+W9?K|v{!A~ADd_2)W@WAjr z+jj@xaxFB$V4WyBLb6!02Q|&Sm}7F5<#TgRck&mzYEAD%g${#HE`FM;*dYf5d?Fqf zYP^AMR9~dqYT5Q2D{Xs&+qtPgS+M!V;SaNFYMnk~$elyWiy(FIOYn;mO|H)xk|%S8 zpk=Ef`AMv{yF@vK)a#su6hEGW7rA0ZTwf4jaqU__2zbiRUP6v6-1`p# zEo+Qv8PO&et5>zi)Qb-_)!^ArH!mquAazE$G^+`{0c= zDo(XJu=QhQ@=XIn5rB@je>RycgzTN~-aQ@YFV-#iN1+A!>BfH?)5N(wzX;6_-1S>4 z3Z;!}G%)n|56ctBC7&}9ZtDC>6!)tkfy$xRr1@{@DMPk#Ae#n;HJ-&9V%fg&&ldG4 zjt(~E-0E2pd|^J*s7`gK^0U_Gv780sWsRa)!y*knf1zEG9PPGXi3ODByfULLj!e5fl^_>xG$qLk><0*bN;Zpc z!wr%d==1GIF*}YYaf@P$o_m|}a1)a%N5$A}QspjbiWHS@##l(t`70TMtunhavAebE z_vho_`Z$i@O_uo73$z-@9qX*W!A|tiGFgyn7qf)u~8&nbIB#HpF@k=s#t7Aob$PZg0;tVfv5i}8Rl??+Pt3g%CQ+WLv_MWk(_Z-ZU@{IIuxxCQ$$0lwE#X#3B;8)&y z+sK-V61d;=9fOcODgOsDv=1#yx=ZcsPN@f%zY!MG2PTo`?i;O*@&aaBPsFn>_Vd=* zUgT0`$?UKcfpiu)LGTdtI*5P}{c8-Y?K*$>rqRY)-66t5;?$sjeyaInYegK%r3tt& z(o4p08D3ZYivfoLPu6rz{(=W9QP^c^)MKBaJS7WSq99fU@X2X^Oaa+mfdkbL&x026 z*Vs|2l&G_4j87fbh)^1NaqV{5cNv_*41E*Wy#F5_NY!aZ|2f5dT$rIeI6of*C1tE6 zE&+kAMvV^z5ivH8?%Z+2hSQIgI3I>Ovbc@UX$@-EPN&Ju;T!>2SHb^WJLPln{A8D1 z=7^&#+j`P=ok3T2(_V+WuQx`S?3-w9UJp`9weryjr45Y?B7w3LL(cFtV1rO(DlChf ze!TYwL-TXx?roo1Po)LDUPz2o>fUDCVzX#7N7nis!`i$7yoyL4TDZuh=+z@y=++86 zP)rAL)-3GrOOllH<`5pIBbGdb;fJ6!&u$xw(S@Es98=!u?<*JQ28G#`7RhH(H`37U ztQh3l!e}|0iS*O`Sve-F%+oRTSW(>1k4r|HZQCv}rRroIzJ;}=SMOubUDIBYOUuJ?P*XuhfOK&*702XD>o=i0}+HHZr*9yD$!=fk8K{qqGgsWfA(Qu=;erE5e z7fVnZM!^JPnweZZ`)N6@vd!I6LF3?iIkk~w_=gi3Y;0H5L|YC!(B}>STdDp15TQ-v z@Mu@0LIMN63R%e0l{f2s89~{{Vkmwp6*pb)X4p9=0qklMcDqkhJjj6u1B@-b(8BK)`lUm0ZtgN1QceLwKw(a~lXy=Cx2lO34Fmoh z-CdgIPc53fW6j_}NawvbF@5Qi{7!14-b9165Urx&S=d@00muBEdMj7*c|?TY@!BO* zi^OUCuDYME)u)T(YFXyf({T*UmtR>`W$^7a76Xd*gu&J1^U=(neDByrJomDQOeo3I zW7@MQpA{vQ$ac3@qk&KtF%CV3$rMiLs?c*JVIx;mlwg4QeN_EJaMie~)a&PwKreL2 z@MBAPde`=1UoC5*?#DAUE4J%*VeNx1TJE*+k^u4>i9%aQ&&6I)GPZO@nnrOZH}}I( zJ84=3y~i^gs3sV_)G7ZHt5A88LSN-Kiu0E5Pni)zV9Qq5EbFq@Qc>tdlC@WbN+S1f z{_gsRX!i#y^N7LVNKSmZtu5pYFn^+8np5ihb`@tf9@%Jr&UgF5>Syjfyj_>xF#;MN zUPN5WbEbatUhZvxdkDHtO77C_h1XiEfWU1jP|uZ?H+8#sYiX!RbcIh!|(86Iz>TA8x#u+_-TOP&f)Ss&_~ETp?xxPqCS*YOU_Z>vzVwYMJY zwHr8;5%`Bt{rx8gA+5=X;-q57uS$LfBm?~Uz5drFvwkV`-I{Bh=iCSOT5 zJXfl-jWkVeULMosbdQGA*!76pOKs^YPDw4auNdlF(_G~qkRlCtJS+jX{Iyd7m%K>I z1U2F4CeLRH6+OlZdT9_}ZT*^Fa>+;?jUhzcATWE@lx~mvMd4nsCSr1iV>zLtp)s<9 z0SviYGC=xqL0h-3p2luxzc5ZVZEk+-^g{z9-I5G1-IQlKxndxzhugme|4O~sj3d$f z_FguG!F=%BIZ~-uAw08j9rQUzxM~Q66Tj0#@O55l)85gtjXstkOB=rW%w#DZpz|m-5JQ5sqxqF#6=Y(d4av;-o@8~C5!?}$HTPL26Eid8!CNFO1MCXZgo z%XiODTGMtD_8%V@bN4b-)uPM?z5xW3d^xm(@+AhEV&?|%i!S3Jw?;fL8)~e{YFvVU z4UtnXoxfpmX4oj7aIMep?X`&$cj{i51aPXx{%fnp`rleTE*_5mW8j>HnVILm>QGo& zI641cbtn{VXnMSDVh1Q&+uL|n*$zd{X+T&My&Al+@Yc_or6 zt3(mTJv6q2R5pgD79}y_V&|nPYiz9q#4=o3r~S}_#-z%FGu-BY(TpfQeoju#B{3D3ds$^HH5y&tB9aCC97Jc3 zzCfR5;O-Tex`-lB2-j4q5Zr2Wqy0<9R;1cEhggdl80FN|2qCtW1GYt?X{6P+xby$( zP_X~E4y7uvHx3HK4Eh^zU3-*-9`3Wn_N>164Lo+c?-|R0V=E zblyrfHjns@j(iWY&J#?Zga1v950Q{E2NIa`{!z)tC8xz_nU2?)1vt1t;_)x_;x)sG zOhI27heZf#&;P1X+L*zxd;4>~gCC$*2@Y`%d-vsPZx3DH@Vy(J59cSBC^#6Xrh6)LAmrKzQt-3VGZn7g7nOg9mlhvW;IEV9+dz<+ z-P~R+N~Mj4U{U<;ALpL~7oNjM4YOu+?Qg4B$K)dNC3xWE$S|_e!Tu2hNRXZ$$aiVj zPk^Ng^!k?uqkg4AsJBkV)#te-7%QcR14;p2{QK6 zaUS%8uZ2;U0_a@)(ZMjvE5%6&T;u-si*$Era5pfbbK^@dzRFd7OJ02wFp6_WspDOm zzJ3Y$pB~fYJO0YLf}S9U5V4g?;M*8X2yr^Vkp{dzROc&)k!z z5Z)J8jIM5#2oB1rPAspk6s1qi=NrVfjBexR@_-zrcgCL6F9Jfs06Uy(F{AH;ZT=Ob=@ACjnDR^)pI0_(Q+!zLXOA`+C=e z%-^}b)XN(SJ2!ii?m)P|HK=aOX9B9alq6eAt-nU6pn$py3pKhrp^+yUx}W}lt}dfGtToiD z4}_(AjdVZzwX_RdSXQN$YL!Hp4u)=aO3vMzn(FJ;-(%zQ%i1J#HEF2-`RKT#@mOQ9 z_8-(=dOj3#6UZ{rICQQ*{WB=66z!Vxreo3k2|ntWR95AJWM@hkdagWf_Dk_D1J@vS zYoyaI_89|-DEgL%%C8@iy@QzYl=>G#&iWFN;G8w z*Co`b)9lCZgX)v+z`eo(N&pB zZ*_jNI)uNEV){C}AEF;fONCHQ#yr9c5Nh*NJHA$-{mUM@h6 z0NO^Te3YYR)W!kHMe5xMCw)Z=egB44-_Yl5r}xBVMzSrj2GbS37HXQ=8l;s4)?Qz>tf=HG$t=kzi@xl+1f--3j#Fl3rBC#UNG-+5FwZtQoz>Bd&@g3G zNRL%fn-(S_XM@P#iK+jVqEPB53+Ao6$HYr6hyI;m(|#f?sK5=8E^#4k(`?R`AGRH?!tO_AFJ&NtfZ!BCi2enY(Q*0?EBWW({{MmqA3*N5Tv|+Ei zau|fl^h!5-Ky|nkz=dXF3tnRR6W<~WbF$BueT9#TJAD;js>C_X$9lnMKH-+0M=j;^ zSO zUz=vUJJ(^bG0@FW?*8RJ76mzz=%+ioXy__^OqHoVW`|l#_R)MHgcHd*T31&L_#RWo zy$#k3a(J9Ke*$!XDG+4A8SJms)T9kkYo=-41ijv;m50&$4q`|MZsr|KJbC@nje5&W zncq7R<-TISQ;x=oKQ;$bbjrFDSmtrxA@S8RV1l&V@4IeV=}{O-Nfs+Tut2@AKs~hdJj`@#=Z>0AD-^V);{&+%89LYido}#IqzQx13fW6V z_Z|~%if&DfSH^33)G-%{V%Z2$-3;gQSjCWC4AjjD51}L8b1}NeQJ`iX1&@4%uX_QQ zPnl`ww6?n^0AV-7Mao)yY}PVqJf59UW4;*?F~7P#dsDNP%ob4>xgz-n~t2%`w?(Sd&1h(P#J2 zS6sHfJc)D{m?kD7khb^dexRe@jCIaeDTI?1m2GE}kZ`o?kqE0BUhDqKFnoB{_<-TZ zQVXvL(X(<|#*zh{%ITjrLB2f+C326qQWZQ3CxcB*?BSLA2qnLZp%L50g9ivL1k5+> zj05DKscmcGFF1Lb$_qy5`l4zt+Ush{lgLPy%itpgpLydkR3l7w5pW&U=+1`l_4&KQ zCR6&$>~%kvH~TE@3&XW-dQ$VYozeLIy6XHr`egBIBIMIE-tU9CPX4>!OKfEH?G$LW z!TtvQ8!=D;d?zMzJooz7ZXi!ad`5)36<8RnQ{+Bi*_Trw%Z^`9&g^eGe;V<#{zec=)M!mL&#lEXHA2vJ8)m>>u4{;A*R264$M<=W0Y&akqFkUjy1SPlYeGHt5iFLE(2uR~B_PG2o3 zGdSIZ)7#wD94y=(U3hJ8gfTv$A?kw$6`A3OW89q(W2LcYB8fNEu@CKj*76@wJx>nH zq@9W*B>g1gSJ!QEB`z%$U+S*2!#B@bIOA#u+sxewIu36JHGYytm~w4Bh})#6O{NhB||7YJOF`s#?t^g8tj;Rg|TGvz1M2qV`^IL-n6qKSkp z%9nGS8Toi8=VFQ$C)d^DjL_iPtcO^WtEFzmPE{!T9jkmCJ^s8@l%P$fUA?)9SmXu8 zHNx9)8fFfTixpg%u(kjWW`3^B{%Hbu2GMwTM>3WHkWKPMZCNEpbpZGBi&(;oM z_?9}+2WgW3tjeT1&LXgGYRPjRY?w?_53w4}3EHCNV!4=DBA+Jg3z6vDEtX&8H@!U_@uD zC(L=KEJA(b=>QwcilSrJI9HMTY+fxPriN^uk72= z-*dJ^7166Mw3WD^YNU?K5pl(%_0GItM+I1o2F5M%ykdUY(#urO$em z22yrEcx;B*nb?7K0@h;;)TPx7kToeXD~p=7xpaK8)Al7x0zA%Ca=Ru7^FK%;lvJ+8 zD7!a=K5CXy`IoC3i*=9HFHd--v#*qKfq^|2BeJtqQpc?wT=0NZb% z6<_`|>DW3NmuAi@t74li)qgOohbuqhOsRDTqfF*AUSy>=S-S<4U~Zh9SpK=xjT+<} zE15cLUR}Eyl680z7dK35W~iLX6&;Sh8;;zb%Xnvz0$)eZqZAcf-pVLC?Z_~&pgWdA zF^7^=EWr(g+Gr6V5a?CsF{3-|$(V=?(D?&Kv`vW(To$(s2Yhrpq94|^@}I8ogQ>sk zUK#-jI;U+*rNtr2w6KhcNaJ|*g~3dKrOd=O4l4ks1$Y%GqK6jaC96LO%pXsBjjWp# zK)Qd^FtgI>Lc7o0Mw~es_)^r5`Q$Vc0fUv$4P;4blb?;D4V>^eGHW=Gd^@u;b zJRWCnJA5jCb0HRrcudYM;z#S8t#-yTRR6eixv+Noj~zv`Rn(J-#$N zgmT*ATDs(P0u^d&CO@Gyh%3s8Yh!@-6*0%z{tY)OFViAt`}r-F*=rrB{H`^ru#7XU z`7|sFt$wU10dP%PY)4n+#3wVw-+Jgik28-87BZhh=$1Hu8>Uikq=IveSyRNNePfgx zV&cLQjUF?<6K&;dRER(SY|9aT82Ta~e9Rj^L=(bN@HF=sl7Y;`66#;EWWX7~+{p|! zYrbs1GX;o{uvittOA^H8usja}qjPoDc^KzmB*ewf>6}8C_oA^{Zt9?cj3ne<^~R<=m(xKyN)MF5-9W$ZP4)6fmDXj<2` zR0qLawt{sG343DFWzE>N@GG(@ewoFeF4AMu-a(OWl3{)Q2`!>s)z8?~*^T)Hl@f-c zH3Jdzuf0{A&cEs9A0ZhOT+Sn1#`ztc&;Z*?fUQTuE{4+;A~|ouek8@Ek3;x<$LHwK zh@5ZCN}W@lLTfA!@PR8jcNTX`O&RZc#jQGTL&4&RwP|m7FA$dBT&(=dC|aOOBYVEh z)1<@%_XXOi|9XM;p0UwMFmCa|AfVwS{@=F>%Ro?gt{`|l?M!X5!YiIC7jb9pO>*mI zy|~kB0pkk;IJNZ@U1A>XaV%VA*3Ds2fegKm7RHX1JB!Z2K)?WqNpv;U4~n@6r?Mzz zGAp^Kf>!FGWZ&VZ{iV(N#o@h*L9Dcn>k6m*8xjdErwOP>cau7TLaN)1s_&kwucTos z*(NUl3)8=Qr{ZHq%RCh6YH91Aw7>E-4AaI6gv^iFzpydhNAGq;l?4b>)MoN5&BYpH@8H`%*?MS82>s^yoU6RgB7&4lpqZ)FZx zCa>RJK5-PqZb>(GENtxSJ3m%iLdSSsi*i%LaC$UzC0a}TsV=2b-6skDVrt+HcnXhr z9S^1IUGN|Eg5ywrO4($DTRk57!x zjR;l#L*|G5$U!ZueK&7aIWa9H6+Ni?N^Y4um!^-uyFQF1sA|jS5-GofS;n=^ux8fg z&;DT|KM+|y9oN?%2hd;O2o6fSx00{+jrV4)_{u4x_ZEJErtANJ){*}w+~9dM(B6L?Psy-qryIl7|`u6Cs#Q#%7-afKI| zVv+gEM(j6%Su;|q&3dpXX$Hb9Dk|HESn3-I{6#j?_e9)HgZT%ccHUb(#_=?^#fYTc z0Jfa8%Y%A{Zv9MKI~w%FoxZrc%p(fhJrYX7c#}%hir1rO#r2c=T{UFI7$l{QsouhB z7bTxRdZRsXPYOK5WJr06yRBIr?+)!4Q9E;*9N2FdF8bipRIogPGWAduJAz1EQgvr?VDF?uh9~qY7RAt} z<-d0+boTg;qIu(+!0VMjxdj^Bj!Q|V$2DdGy)1p{C%A%hjjQrhC&Nq}58FLx7BSu) zo^$Z5da2i)Z+hZy9SV^LSMC9l$H@2*?ptoM^u`eL+jjWfVo6;Nr-xfrG=)S*^d~K) zV|j)h#-ZeUFI}i;PKVBdL-ZWMxBdU+hlZ-A(SyJnil@Z;tD;Z}TRBVv6qpTezZZ0-w6?_=LY%V+^eq?jZz4>)v zM(^3I^y5tEH?pI|;52qmK5|1C7R`RAAuN=tjsn>6O&_T!sLR1+k&EG4yE$A+V=6u< zH&)S3kP7u8L7l3SvBosD9M9gSz|qLN z<%aYHmiWzwvydPW7TBXz%!CTqoBFbC1Hk+W6>@c0G&_b`{;C;jTuj+bS91Qtu z!}F$b!wxmxy#Y^Sp3ydmFZYV-Z^pEbZcK(DR1#&Wues3`Q97;Drqjwf`oWRnO+*CYTYx3fw4*B}iU@0YabF`6vzjnu===QJ4Ita7n z&3Q8J{Q7H@ zK^z@V+c^eei_jDO;!{#%i;t?j^cju7I#il{G#aVbk}@1sI^snat0s^^YT0lh~vDoe;G%d;vdELntHW9#A~9hG&_<&R=m(`8gq-HX!6Ximj+(p zNcp2!-Dzo%Q)8GbXlh$e+bFy&D)qI#PF<6($No&Zep)BKOOGR?7%Z|#n*zB&= ztLQd`%)lS1e?^GpUwDX&FK z&YOYS&OZb4j*mJDt7M<+2UCnLirTh_17%ybiEHj>)rm zk!CFJMMV_g3a4t{wze1XD^rC$duIO;Q1;O<{8VsO!;2dpv~ZUslVg-JsMXtW=V2Ia zdC0}65G^&*vwHIX+Lz5yE*5g@m%!CNl%w0JNMs6xY=3)Z)L@x3D}BLSo!GbSYrCfls?9l1tTht zsa1+QP7bN!v1f7-0t*hUB!q;yH!$^%nDLW9i@|68F6eP_B_`N0(^-dBH-JKl!09as z8!S(>Np9vAbXA`ZeDNAoT;^}e@hGTMM${skv$_gWnR)j6gAwE)*s09msj8~`LV%j+ zdln4(OD>|ZpcivvLby8FHH#G1!*e{a|H4PP2X91G5ldB|F*Q8MrLm|ZLnX3{Fp1nC zK0Nk`v+uJgmOl`o_3$bvarG9=1RokTxb*=_Cs?Y6z`M|@1UYJzct>wH_2V_6(ON0X zlQ*{Wq>Xtt91`h*jmqMO^<<)EwF0(>VdbQqRU~nLrJ}u(^S9R}`myFx_fx*-5Culf z>4k2aYmr0EG=CR)y^h^BF9bNZxP<`@OlAD>U<3PaI^C=NGt!O6?Ax@OT}HK5iVod^3o0uxBK`17N5x7%a&fwPVO(@> zw}(%_7Sfcaj!v@yx&7tziYeB8#m(l;Md5OrU2^sx(u3*+;k!0RMJB~X367-1(!IyC z;yXD@7Z<}bcfl9)(m!3#{#J{*b^%sS@@Ifskj2=b&vFjD>1JFh{#weZ@{1ioT0`2T zHH2W)MSGh?8b_8CP^aT z25ls+%VP^!9rXC4OD`>yK&ACleihl*N4Cmh<#7zjAO!3@Hk5&1->}>f&j>O71&Q3F z9F9x=7Tu)XDPjD@Pb3Q7{yh?y&); zgjiD(XSl+@Qig(QGfGSU#30>l%gZ)1j@gkvk?2U)oq~pw>J9|47>RZBV{;3*3UckK z()&vkeA;w0G~H4%>bCu18pYHo3KGcD&-<5YQBS&M#`W_sGrd2+hgdZftJTRk9_H{# za3f%n!uqNZ&NMsFn+L_d?&9mN?sJU_>U2G2_WN5;5zyWb09vtNZCrSN=mejIr=MSL zQUqMzd-cf-d*b*&CJ(w#o2|(n$)~bZMkHmb%P(cd{W}i79=JC+@!fAc?H+{Zv1(N%)dP#skb3~TW0JK4$wV*~*Uu$`5u?TT zy1EF`*Z8}QkBEpqDgFHlRa#&{@ud=~kf@|`PtbWf?4;f>2dd1A+A5YVjkDmExUoo4UpQliWxJg!ODBji(x6LMDix7i&4_s%e9x-Idgx*d{?z$$gi&=bLAf0Af zBIlK1bK{Wm?4k$BdbywaMrPywlo^8P6D9r2-VN7uN6RV79X!03-@VhCejxgxa*7xd z=@|sV_}g)>Mt!?09X7>#a|Q+BIs{+m?#)F2`txg9fFP(m5k(-f1FiD|Yv#q8b{h1$ zRTzV>;aE1PZwnsnRZLngl%M?@*gd^cHMt^aaYHRHkT>hMb6qE`nVkhR1Z65?HXHSe z&VsCrp3uLQWR=B)dvNaqP;6=y`Qnc;a4|6eIEhnF3EmUxhw{RWj4Z zK(OcO8rVyS+J#AN7!pui^XOl)xUL{x3H3~SBmTsR|1{BoM0en>?#yiO1Ddj5)1>ja ziUG3>6Nd6hC*u5azsxuFBCg;-kjGBMLEVk4kI1*NZQ6^t$CaIvTZ;jt=)9A95-@L@ z5AVAN%yQzSnB_aAMX7EgYp=`QS} zxK32uYb-YGEMe)&7kA@6S6k?5D3$t`5?J$n`(|Y+5Y-xsx}R%gY|5%}kH)AgnotrE zY`_)AtYx*%3b*eDE4a3YR6b8KNUv8_Lj8#>kTYEjiQR0GvPh*}KrFsK0oIrjxoZ-F_Mg6{$A@TdvAkBKT>VxbIr39NbbD%%^@WW5& zYVr#6VDSY-^kgrfOuw1Ce1qJMJbX|WGHnvkZ~pO(rctbPHh$8DfUDAHU9K>5l8xCn zl5NGl_>6h5*ESid9l=)|1lLtFsL;V-wIPq@Jc&n*2K>I$p~}8BhZmMcY6TA*X?Jf; zL6#FMs-ZwIjR>{l5V`T+4b&2hZxQ$)-v46c9F{}@6eBveZQHhO+qP}nwr$(C?U_5a z%_O@lUaGR|>L2LE={jG8C2qJmQ!tdFD0Nq3DDC>`8Jo9qr>aH<1vbi}r%LyN%K?kJ zZS&S`W$!7+L46+F1|;v~2f0uug`DiN89YVgV_sTa&U+1el-(sb0ck{{Org4P4Okwk zz~pl)iDQNgxMwrI!22Y;%rlFonyV7@tJLv1bS* zndtU0-y~<+0i;GFFTc0RcWXGzg zqEcShc53iq$MFfZcx0#y?CI?^Bup6BCdgzk7Hc_V&dXidGchlK% za|R==@Kcf^UGxzS$?TyjvdI{X@1Z`Q717EM&vMYVIae41oO5=*W7zON&lutesDDZZ z4+1(rFeqdpHGy;)Dysc>Oqs!5Vq0()`ZU*vNRn&)E zEj%d`Z2`jAr)xcn{0gIF7}wvIJso|t_eF796&ReS=4wLNTTn!g90>V2b&_*)_8XBz zrTE*~emq{)pN~^#C2=Io1vNGVh4X@Krz?VQJ@G%XS(EVB=8Adxs1DM@XDP|temZSc z?I`Spc$--SzdG0)62{k@zbxlbK5#MpQl%l2)jzFQ99 z*4b4gyskklQ&-YUO3;s8i0h0zMKBM}SH0ASr8A%RdIQJnD}(n|LUHOAZTHeUI>;&< zMf5%2ifb%Q^b&W^D0!6VirGqMouAl9u{({#TQa2l8uOW#e%<%!;*DUdpn`odN= zepXq}-z;)K?9D-+JZ=ve*KuqAtR?w;i&#o!{wPhGaeYlv>{XcNEtx$EA?F_$>5YY5 zMTcBhankN$e-S~`oE^^ak+a473J?MAV5l>S6SS|ht)@&y(Y3Z(5o7r%IjJCq=xMub zK3GCeAK>!)!Kf-6?U5%Iq*KvG`mUWfY;R(RhF#Z{&Nxen(igHCQy-an9~Vt?bRqB2;Vp`(kzLPE|!k9sCQQ{&GuNX^P{+SX}Qu^o>O01?8tglx=vr4JKu;Qx928S=q z>rZugZzIdhp65I>@94ZWZjqGjMGV?K0Os3FX|guMe1Jy#z20l+Dx9fSl+rNr;V+i` zIpDfIdjbshAs0%Q@Im}bO@CE{XN$$`r&hh3+B;8lA#f)3Xv4Wp)kQw|OLHLlHGp%Z20UCnfRad1kD)6R>-2qh)Zf zcHi+(-x<%oLkg7INud-Y^>zX!+O|4Gjd82!Ak{|nrtHTRLy|eOBR!!E_WS@1%8luL zNFzWPtSW?)mWtr8;QRJPWz_W+@Gd`5=ii-$A3;^^#+lnlo{7TZcG*-n?O69aUyX@E zIjP5j-3=-oPNKCP0;_#;i$H&~2(rDdPEaCBsJm)uy=nbKywIGNs?ajBAodWg7c@}< zia+e#F*y#zc4DC8f$S45*tN8{*%XORvDmsACrwUlYzoKM=_$+ZSO%HFG9Vd3tizKbVny?;qc`b-~ib?7dE&)?e+mL_qa{ zoYj@1IL0#}-~5(JY|q#)SeA^ao>R~c;}|MoJZlMmiau*FcsAD29ndIgQW-i&pfXjf z7}~QVJ+6pS7m#>pot%OwPHdPONqAx{s|tVuhD|snq)3XgoNc?#F)mc?609dNl^rwe zqu_)NY;vDMkkiEGj9ZIg4?>7!Qcy7uMEz*=q+aqsx)VE8kaMGe&anrHyX6@u}>6M1I zAuS7nD^!>rU90@-K>|^V+3GRtHg^m_IDpUCDrTE~zQBF0bZWb`=3e+|(Q_Y_VX-7F za+*N>NR90LfTOBa;dB~Vzcz(Nt`tow+AxGT*TU}~CNC%SNI<@jh5Lu;2tN(_MXjq- zOq+uGp?(*EXJ${Q@Mp%q6%l4(L9Q|Sbg(bJv}704SEi6PNnSC-2J3A8+tq+8N~%m} zf5zBY!LOlc`vIo#@69t?z3L%&vInmHIwz|+%>z#a>9gfNMXCdUHm(8iNgHcXB{9RN zm!PnB;Fc8)@SWZt=#m=4aR9zdnJH`(yt%#_5MOHTuFi42*<=G;)lP~%;8QQ<{X;z{hItwq- z9RURj7p#VB)EZNY2;JRewHb(MzIrmHOitDYp9iQmP1Anux(Tw!4t>#`%#E}9Sw)eL z+IZrPUweTAhklruZ}F*en8zcjjVu~;{@`otOn8v!Rrsb@S0Ucml^T({_>3hpJkpxo zvvfW1^;?CiNgTTadX>MwrJQJIj*c8V5T~I<7_6X3=)>*7EZ>@0&RB(ssDs_zVWnJa zXly?WV(l7U^j=gqs15&FG!z*V$N0|&**DXDS%@&z@qaxtm=x?|tVZrLl7eTo{C<@yq=0?b&kb2RT;q7%Uoe`#62)0k* z9^rxBCg73=r~VB$t1f&fP(4M~vt$zy?w;34HsPBDJWBuQL5MV$J2_9Pisb0!?P3Yz zy4&sqi?)8GsruaoONUXI^`bwS0*QV=ySsCvZ2z>RZ0nPD)0dCbs6z`;Vr#t9G;hq} zdqhJY(@=}z*6Lqq%+98EXPCw-GIZnHg)Qd?`&;7O z2QFTpPiw>VuWAZq$&b*VXXh(d4c-LMf$#|~AL6k)(lN2=U7-*O%1iT{EnCODUyTZj zKvR3K<&yRspUZjbMP@tn$TG|*^kT9lV6s0fuoN*UhSQsbr{-SzAQE_syB^H9cIq~a z$yw_45*8zsH>>loPH6oVM3St#;qaf3AB7fd70^aj@io-Q(Qpoo1`VaIGH4FpzAf#Es^(saH?mu0G9V`I1SF%o%6|X zrLBtZjNqc)e%mP{-eEys_)0Fq;~M3c*ah8|5BXc@&UOomuze#4ly zzz%iu9N0$10QP8qEuxj-S>NmG4};mQHx47dp6bfLf@*laRD>!9wCcB+$M#QA6qL-#3SoXVV zMdhP`86m~O{$)5!t9i>SG~kW+Us4%DhS=xs!%(3~7$R>YJTg$cH7{0-ZlPo9LxR$3 zD~M0{$v}GC&lNPT1nx}3&hxP!uY$CgjID4*ZO&B=my~Ds6$o6~B#mjhvaY={x$tdu>w_IkViP-4s2Os!$R6*iqv;8lt7svl$_2OXS z{C``$+`(0nwU_IFmJ;<;A&8c|?{F`4vswgzK!kuK0~SOsluCkCD1nwr-}FEQy^t+~ zp6eu1`Mr4lKIYu-wBG7!QhS)6X09`P^UdqoYi7K{c%TS6Xf;sbgdZU8AEkj(T3}m2 z-T}YAg?hffgFY%3(wd1GXG61cq^;5D7eFVW z#W^ON9DsQUDZ~Q+F(Ab}0t5r}iGf`L=nnXcACQ`WVsHp2`hmh6yi>>#0Kp7!6e4gj z!bVCuh6M~j0Ghc56m+2ruAstxK-WH@2VmcuIROCm0{>{=$iLJHA+Ft-7Utk?51~Rl z1r6W;ydh9v7F1UcMGF;o00>~7p&>$u{$=4B=j%J>>`bYli><0U(UPfi{403mAm_sf>3F zzWQ%wJc$b|1dxAYd_n@e`}_XBO(G35AmD(#e1iXeMSMKRJj<-gcK*hE_X~=Oa$p?{ z6H`GPAfo&Ohk$^90vut0_xr0W1`qkZIO2C&HGFjl7}38mdcC+yn2upr+9YH`Jo^9Lj>;{f70jO>?B6O@cli7EBn`fv!4!#5d*f3I43uF zQxcRz6`Uo%Pr37Otci_=LUzFm^%rK}R3&uRSchLg;_F6}g3VaG!1us%C3C(?^WLbha%)>pobYWovuNCd7~cJnPph*nh_U;RvT? z*FM}j`8+GW1b;Q{m%?>>)VO+kk7NuhE_$#S84ZnS4BARj(w;)8MiC?5QpGQTHwk|? zwy(U)BYlC1f}(kmOMTb}sVUdl+$wrl^+D?^{kkevP7z!-3t^Ds+NvCSOBBX?pzXZi z?52GTF3gEm3G=d=NZeZ73$tw1bhUWCz)Wk$RAdFoM!lhuTvF z$;bK+3>`RI631sZdE6{E@iCt?mu)ysG0k;wn(L#OjXS8?M#hG)^#N5&8buC12e()fAg?{?Hnn>AphL z(3KeZ_J@H5Qq=u!xeVZ{OL0Uch(9*8?>A{l9U`>R7Yxqzba45c$mT=o zY-&ZNNnofi$22Dt;k0WeO}^YTcXTA@1Z>R5H%ne~-4;tOrQ-BgOk=A*NbPPmADqX{kRLxYm4eys>0@ z$z1q!MV{}!cO|{KOcc5FA(q^}r-#X5N6Vge1u&zzzX4Y4jpG17UM)-kpT5Xy2=;S=i;7Sb;HQO5r-;$_z>;RxNDxEYlin zolgFOsYkVvC-T_(v#!=mx%LS4w2uwlia{jga)I)mrau9(2c@mZn(+y#b#`U(Je8aU zsu`)5X$EQD3r`f;7DbUq2!kftC6N7ITx|+ba1LMRlw2WtE%aZ<8|ue1M~g*$YCw8`6sYJ$?O50@|e}> zCx2FX43bcXGD5OA26Ms0NI%MIED!HB`=+72D*x4>HPa<+$`M%+SHQP*_B0j#eFU0_ ze^d3=KD3mv#3=hW+3D9_YFNxQ!Ezh!yUVr~0S@~uKMTq*k+^qZTYjVWRx%hLi=rAx~`rR$|!CNN`{Q$05 z&ud}~N@%^e9YuurQcw9Qv!#=fV(4<3iBUMNJ`DJ*WJ^e!4SF18gTi6cT4I{HrA&uy zY(9yVEpL-JNc*+psNMdwZ5OwmsHcUx*ruZ<5Cn>`0Xi%U1BAD}Rk>><7BbYOPNsH*AgHC-1R@qU?Ot#xXPr zgHq;i&_U3-z?WhPP!?`ncRaYQkEsglYEY}8g4S2el1Es#_6?U_7cDF}q&AV{YI)lU zFHesXqDQ3h>(W@xMTT%nnKPQ1FWc*4$4gYW?Mgq>p0?45CdZ4>%Arw;-UN+#dq#w3 zhzPLu*zqeWR3{v{z(Tk;tYa#GVpR8qoJTT0jk9}HHT2?QKJ#5YmY|9UIV$h(DW~PSdR&)rR?^dt>o+F2MtL7!S8!R1 zCbKW3Oi@ddO&(`7rkCc>InQ=W+6U}h5M7*X^Z9})JA6Fo^!*G<>&b#`4s9M16%g1G zxhS(2EVGe`r$1S&`-OJ!6^(kkC0)$KyA|3upNyxDD3d0a(A)H&=GhaLarya6Ng($n zK9H;M1!BHE?Wx8(Tt|?nw8&vTUMbNRv>io5Av@2@`_FhHWr7dcYoDBYeMW^|iVIOk z+mrEoW?3HsAb&bzoGT3zd&2N#5i=(%KDFefs+4(koP4_Zjboad^Y!Z=W^PRWo56Om zcM|avNg;xLl6&WNDiB!VHljBlQz}VUR38l2Cb+fizyjTolsk7C&-t~?G5!_D%jY^$ zjkt>bSQ8JuQhZP{sFlm4gELt&^H0Cb2U&y<|CC0$z0bteBs*DD%_BsT*&DnSV+hS& zL`81(0(sTNJFNvG?Qo+BkgY&S^xnbz7=i1hbB!M~dr|WM#wFsLfy};Z{Y-0Ng zGY4w2m(9%o8EgAK1~302LLNS9jz3I-t%Y^d;GxO{&*JyX`#xmkem$U(x*S0@WitpMS(B=`<%TZtZ0{-JU(AlAbjg6vRrgcEN%zU z87f=@CBNVf9}p_LSvy3rq`p_)2D)Dl9@&k!-;5B(V%CRsH23|634q^y4F^+i28xB2~zd7bOtr#{&g|uU|qh$@-VXcsjnY-TQ z-NAWc9p!KlIc_Nx-D@Y;RE`M1Cr<|eLj!fnJg@owf+WaM?+kB(5Nh9Qn0w__>h)TX zV2{{u3%a`va^RVK3&j_!rE632_P%mr94pJW3g%}~Xp&fwjO+=}mGT*-*SMW+E=aQR z+NZn$MQ_^2yj^6)LhD0a5nA%HZf3R2GhArUJ8QaWJL;fN;2O?`yNOvPd%K;3rgCbrFXgd2?ZSSWaS6(cFGwKurd(J&x~#Sr6nw7s0Nb0Y z`N-bQZp7wOU@muZXBhmF;v#8<$F#_Gi1W`J_tq8|3aq&_~Ke)KA zw5}m}40g_mYM~+6`)iQn_$uRTf2<`?TmArc#|o3Pbw;lefLsy z<5Bgk5KGePn(4SffHkQhu)UibgDD3hE$57$C;3jSo3BM$5;s2ST_aOWY}3pNizgfV ziuBAAdxkjoNZ-C#-F$jC@@42%yB_o9F3Bk7b^1c3{aWu^Gc*Wsr_(()zw5qIZqCvO zp4ZOrAGSjzyz<#C0BWN=JTaN#yQYA}xl3B}HzlqwqYyZIT!%Sbx%+hI*)l;I8e zU7J=|8FINt8Ju#veN7!^EAbQ+t>$+589i?&iF)$O9Qa3gC@vFAR6$2-4)S9`JTalO z%JtKz50-yka6_)^PxR5}V=*>vlC~jrnS#Hb$8u((p~I&q+S4U~{`koD{3}6B_ro${ zWfIne5bZLSl1(A=7Jhz$vitH{?b@~){-_ll2o9X8gNxL4d)~8yK)&aS)qli<-|?h# z`uGr+BEDZJ4vv|};w!~SQ;@=TN@YuctC~yR!I<`Uy$jT5ZF+mJu(uMMOw5X!n7k?~ zg=W@lyrx%3F0s1_K$V8k13qlE=c9C;q%X5*nemq~Y11OApSO08I1C~_JlE89EHhQ~ zWLBJOL%o}kgDP&n0qrG+%vN9(OlKVA6mc|E4+jK>Y|$PDR%-H1EBRCviEvf#uqB5K`1rGpEqrh3O`WW|H%r1}1Bos8Po-&IlIeeC%@Yd%VPVAT^pI*lRQy8m7VK znBPdg^B0w^ZcX`>wOG1I+akk|{a(rCyh#J!)SMn{)L|4s*ZL&<*0D!tDm4d{v^ zyaQK2e1^?e=g6;^FTEl}5&50sd_Ir-<=e{_-PnB7(PS*D&p{hvWEGD)oq0aV^R8xL z-I?)>%n<3*l--s(_VwtgLPY4;sMflqc{!O*d(PJmx&cB(XHJuyrhByWV=S}N^&uPl zt^{fCBGT&@&{=0{W>dK&z9rvMiAzo5&94@_FVU!I0@Fj0;uALRvMenlH1Z~PBdhFM zB)apEweH<)Ysj-J-CGVtPh`XsHUiPZYl`pexo&j}#vB!BEy3ic(cLSe+A4DJa9U?ucK^oLr$wadE2F3PpvT;xZOxf4x30|B$p6E8nsv_*PjdzX-O- zJLTTCA3jz>o0Z8VPHUl2`>K{Od6wN= zolnIF0fV8lT=jYArY}oc!dt6w<6hhmA4C3EBZys(cfC(bsyP)QWmhmQ5;yW0H|6%q z_EgFuRowIxc3Bn0FL}(NN>&l=%R@@2|CITVVzug4_U(X6)j6iyR-~JA#>~KA&Mze1 z=-M9_{BmBU9PXz{Ltj>2_3_Yqd!-IeNOGrQrL42v8&U5Z2`Xk2qA5y^V;&?Ea?9BL zTC?*suQOQ@fju>_xnHvMK4RI-AjO?7Qpnx)+F>-dv9aV$D@ySiAosRGTDlT8^`+4i zJ9oZ8L-L&`oVH3eBOoEu*GTVmZZjL5nZrY8zJ=$7<3j%@c1p_xqLdw&8`whfUX)7a zrD0=lNot`&Gn5juc)D?4XMzu587L$j9yxol%(q3p-Kb{LbR9hj<&!%%M4Y-LBD-gI z&@slu%@t!(X|~PbI5nn-*tm=4lV->nFxFY(JdY`-rSY}5e3RriA^iD@Tk_9G!quYD z#-`U`)?=t3aP!gjHW1}0Vh94*zi&F*a8swvWqA2E7lP}Lscg;OZ*SkEMsL;CHvs`B zF_mZDM)6XExyTELP*Pm(>H6{?SGR&TdORL!;l%`wY%zA}bqvj|5HmXMAl_AhDoFD$M-DstSaBQW zFE8pzg}(jutq&ovX=m3dDAOU8)A!XPL1Hk-QTqU)q87Tzs`C^DKIg45D9gK7ZGq=X zd&RiOpvbEK)=5VG&b#34&3x=!&NL$&=)7(vSi?4(1~YRVKSjj`h7VXh(Mo-p#3TJg zCpX2K!|wjwN@sHtAzB#j*Ix82>Jn+WlwCzg$8mESDFy?!{HKMUO6f6fd-P0|<{J0 zR+N^m585^Gc4q%iwO0CDF#Jx^ub-Pp`E{O4HPDXD+7=5;UL^VRNtxpy`a#z~H#U!E zuzhAd>UW|P4b6$x?tHSyxCl%@NtyK5mpvvgoL|iwjhvm|$jQK&^!VE?xdg^75$PJ4 z9ZZDxl(n6X8AbQ3e3I*%ol(t$ynuw~)ZcP8y_!|5Rc#BJG zQ9PtLsxa9M)_mT7k$pRgv5cw`dU%W8A6W$BWs@3Cln284O2O29c5Cr0R-IFcVy7MX zr6xL6<(xt*73zw{F(md~a8nwr(YNc%AWHJ)# zXH@YPP4-oLs`Oh2j)1g4rEWXTMntrx2$LwKY3X@oU}0?yK{P^b&K1Wk)KSq9<~7o@VO8AP(BS77 z=NcWXYg&euF;|vz?_)?ap{yS1=>mDGpjx1iB&sOqp{jzN$71Nog)+L95rmbE@x zwnA(($@%;?e5;+q+phjn+CEbVpP;R$D~uoR#w^r9)6a|BPyg#HMZD#2_k$y>hVE#o zI`L;IMTqThZSnWYeptfzMEM@W2oJI-yzpLxu!Z!7EXMa#TgLX53LhiPaOU&0{ju$q zbcSXtjbm)AyeYmQhW3#Yio&X8aTK?xu`&z1fDh<7`~3n*&i0yIOuORi>SD&nu1=#L z%Akunc1!VvMV9heW-;{n-F~R`wjb7S+d6y{C$(jA5LvgGwbFy(FBz1^97`SymUq2V zP*Xe46E8(wf(Br2uFMW3$Uy*@f=Cx+l(OH0db@X1C?vKC=vJ@ec9xt=;yBD=``0Zd-`m zS$B>*idxy>2nOO$jzhjM{S>~v45TJ@8NRct#+$r4YJv})HSOzY^4I$J5l3yl`V%&4 zB2AnKB+%Q+QWdq_>_+z9rVfbS*1U5zah8V_XBWSx6NNaTAl6<%L zed9b1@TObuT-X^>IR(sxHAlY3I<*ro&6Y6n)k?PgkIRR|i;%z|L0i`Js8t*~#Kers z8vUD?Bc3i((CV635I6VQ;9po&CQe?PSWKN(%|a>Q9fc!~A+;P?SIKojt+2S%tQC|X zF&Gxs#och^b5fRwsq&5lhbtW;x$3jFE!?iz-g3+jnzc?MDjj)T>(4F4edE`vgq8n_ zgQM*`Z#;+6cfOe`B=@T0=T^`Uzsye_Y=V!^1%+JI`a=TOnd$nb0JkGWq_G1@!nF?Q?A2Bo0(^JS9I zl)#b#=TAwY&Xs9r8Mm45jWXwI6)a^gUyVrLi^v`nyk)+Ru61vwdP_&duBe`g(Q{wa z1&dxR(VAnu6@j$?J3z$0Dm6Mhmk4R{Puh|9jG9^`4;tHI5FwA}{8~$Nmi3EVmMguF z4%N!D`GNZ!I=p^7)4dDKVxv#8DWP+sQ2=)D3qCF35B~9}a>{?C;cWkjhO;sJFB;Ct z$oZdTcnnM|EdO5`-U_Oca)ZVuBPCV7B08SsZ#yxa@S%^_r<5XcWRf;&J^PM!%eaWN@}2nIUAxedV40f5s3 zgyRDwKoEcoar~D~I3ox^0O%B$0f@2*;KV?&ftV|YV|I26(AePOCG}LV4p2;q91xtJ zAAdsc9#BEM0cZpj1cV|emu9eC9#cku9l$k%0_5iOSC0^5Pm`N-4YIMRtE;;y&=zOI z01iz^h893QGKo|G<_5yu5ikqTj}1lVJ{BeizR(cD@k^^7p2^)AgfkF89tb;v z0^{oQ=n$3_hzlS;33%BA1z^g}@N91!+7GJ%@HYcCFgW&b=kVw82L}@PdjiA68oId^ zSa6F_0Xl$V1Pn$YP1(TN$=L`XpwPS@Mu088k<32<3epIe$%FZ=$_YV0S_K8b%laIkEz20Mr&B zu=t1kBMHOyhs}@M2Rs5>0Pb^$2M+N5^>oJ`OzxFASipAs$M|>Uv)S6Z!ot#$@pt>a z&&9}?fjy9%nhrcLH3A58$&>OsUz&5zA~ zw{H|tj4}diQwHS7)cT#x=s(TsZ=VGV0;LwnDdeY53!nx^aPRLTe`RX*D(Ebkm2(UruKd=tq zmd$^lOtk$c5LYg;@IP?_|DG~&7e4`e;PxKeN9y<;{1~)>`v)-p)A<+v_`i!+^zVAg zEG}*i9$hkj#~;g)zajr!9fJn!4HREHEHf$$3suw&CsjhJ)2S}LsmY(7JCbi0RcqO# z6;tI^{qNZqN_D%D8jJB`v5^<1J-V~>aW89IqJq|cOJ6GxfJL2IXeQ3d`ZRar_j`NS zTvQy+b`$OGS6I%1vosNK*ehT<^CDy!5t`TW>*Y)BrTYu)DY|J|cts2=H;slwF@6yO zgq}ZH(8z=hsw};&GBrvrMoqG5YY!qOF8p~yR$pAL?itDEt#{&x&^#ujsW#?kow?td zpK%IPYUp>+o=1!WAiG-WB$Ku<#ebU81^C60N9wMISc5Q|t&PZ;$-yu;SUKYl=Ly!J zXgD!geqh4qF3oLCJc8isSw{9XMXn?NNDcd8+>2|o9#D{~9z)CIluIUp0#@D5E0YCz z554OIV}{G{43R`WYYd!HVOn@W6uK6KnyOXKZ{IWd357B(_oi~gDc>p%ojFNNQ6^Vb z;<96W&!x_}7)2)r_FMx9m5n}5vADra}FZ=;bm~9G<>d==G#Lk}}nrk2uQB zV|;f>Z+>T5ufz;R*!*#5U-@WdlZ(mh5`R2>`?AiT7^YCO=q0$3-g*^moGzYKM5Vzp z5nm#SJF6ag->@QTL@D$_XMqsXZgxe7Z^5jB*sym%M+(9>cxn4P^_ZUbB8}*Q--m;w z!PN3dcshim!p+3(Rs@cy5=*1-34d$~V}w6ODTho|BV$ty2a7xAqlI%(JZKpI*~H5+12>NEDo zFtZX$vgeI&@1{3*#Z+oGj7(-gmj_=5M^mx|Gz%8eBoa*pNBo01x zO-yU#8-~2(W9AXZ?`FV}S09oOnW!i|H%<&!{2$TG&tNIEYe zqG|msgVRM}&f#kW8jyUSa=KAbq`Du_7U|p4Pxsq%(ZlLPpWw}v857IpZ9o>|lO*j9 zk0!%nV8(PKg%Bn?=E8;{_lwzsU9+(93|4Z`yokRx0B5g5b#4pG7w(x;wJ}2Ex*g95 za=i|~1m4j8UfV;slYt@sEapGg>9P)*Sftk=k+xdeRfr|G-Qd zUy(Pt-L>7juEe{C+^sQV5o^MQ61<-{q zau|(A?(%&;SO_qb?2~r&${An?AF&ebgk3>D%tM2g{3l`q$8%+jEQmMO(iD)nw9}3GUl;vF=k1DNwI@9`Yjswz zcWdr< z;flf?n=qe~A+&97WwX;lLJZJ0OTv1Y#rpX`xW7ElX^`osxFnCx#aoGmVQJr&e*bsan*a7o_M}UyR zyib~3fa~7R_E%!yMg&X56XPvh`FBp_ixdk_V7=3m%pWTO#?hx)HW#CR3Lo`_Kgp*XlSFFOlw;D%_5Nw*_-PTylTadl% z14@6WHoGf5J<+jsCxYSowTGuG%sWX;572@~pIps6)V!KL`;`v^2!jd2)077DZxbz6 zgq*c=r>8$F&ju%LJ)4_8{aYRNCvuZB$O36_E~_yH5ypGPqTc1Lz|n!e-v%YSE@S-gg`;@zUU?=aU+^0VbG7vl=RTesaj7h!SF0{sc?K;5%-Vb(NN$LOQS@XA1UfZoRw3S zy97Xav{xJBQ;eb7~Ad42hU)1-EH9 zX;Ob1lAVGJIMVDIlH(B7U?S)>_s57^d?GG$_#0ryb-eH0YLC#K{qEs?m)=9#!&xE& zvvO2trf6Budi0MFes=V~Y4ZBSi{L{p5}fLxDefiDk%6ZGikG}sjn+>%b6dN0s$UmS zfcKz)lt1b^Zbux_P*XdqaQSt#Y93}@eZq?fMP0V_qot@_Lz_!+FAw6~T^@|lB-x^N z2KRD>;!LP=wAxgfjrRvbm0&-7yiqBl!=i7Ow1D8_i3D#gGnC3GU8YCso}yG&naz{c z&%Jbl%$XaZxH6!oanHUX2R^FOZc(LB54*9B+)RBCAsbDFp|M@O2Cedda!%S@sH&R- zz<8_O6(s~>O3Qmd9S}3hLcyBRtp465@D+8H_l^mVWP2$dG*`Fdwfy#`BhU=(gxTFM z;Cr}PzVa6CFVIA<9#K)+!PD>)^9yCto9U(tYZSNsRY8YpFIv{;Dq*XSLi4Gi!pEk` zTFNkl?Q&D_@*<&C^53sYWI2cdQ|goxD^Qa?VEJ$ZqJfWG5(W(@v2{sIVak9AulhTr zX5U9*zRkxrXutJ|2)fDMs59i0uOBZ>teCcIy~jg!341e-HlExX zCHd?Wxb#eFJYC;_N$=cxh8B@TI&2!nx`qSvnuh*BA<%MuP_CqJiDZHZiY(Brp$E;H z_L-xScanA8P>J^j8aXTDzXCBdV~@tW0ma03#pHY+xvVvhc2;lK0T0F9HIfmK6gd^f za8%s2{3lEHggC=QDiFDAU*0Y0vD)zOy2ZLFaBofW2G%2iRuVH)YiQ#*MjtcvunB^o zb{-K5s(f_*PDalqDHIEt#tel#!qkAN96+rtFLigS3phnI0Tws3!aEOHL(~uI5}#$H zFDJKDS9`Mi^71&~y6m$+)BQAJs7c3X z*|Mz<6uKPjA`n>=U?o;kI$DRR&`DiasZjZ)v&7?99NHY0 z)2F-zml<=l^Lehec-SFB_J9H(J*|f)xsy}N;l9L}?cA>pWlyd0g-*%)(ia92zE2{U z$lli`pLm5po@|+n=PHHW<}Ac^yG_|(i_GIKo$%;Sg2o$RYGn)qH;yMV@8ee|S_!eA zh(+a`)}`mG?t??sktysZkG@viz{8negs_~_)9)@xq0Tv-tr@$0AX@5wNbx*8F-*uq zqMKJQ@DbIYjLxz~E9cD-j)F9rrojJ&~G?$Ut*NA?QPmixvjMey$bLv& z@bfX_J+D|gcy!{7$M(rr*F+dL3B?XkRh7xzH}uwN3lffn=Fksn6NGXs?6bfwgP3b) z$D^gA>9HG^==z~B3Kolk?B6{&^hqqF*DF!-%okQ(Hrq}}ZlYOC(CiZ)klNwn^hOPj zEpZ|)D+%Tm0!lTT6?1e*bt1&Y-n$ndl=ud>E-P>#m^(hUE3}hQR3-#=po_zcB}J-H zwmJ`bvhs;FpP-mt9MytM-k-zX`E_NnbNJXdsEag`?p-u1GrDwxQ+^bM44p(yjG167W2%wl|`Wq&^*>EdLU9U)|p{5?<^TvI14TrTnuwyxGxfMNXTZCS)XdG}kdp&~y_YbP zN=tCmg;R>vu+fL`X9oNs>jWT`qp9%vVL8+6dW;B6|5#xA+bcwLBB`+NuGd8W=r(O4 z-Or%xTol(47fUkDf1Z*k|E;6_A}p)|Zo*c62OX2{J}9<}09pDqD{u_2QE+JPORnkx z7L7rK==*FkkBArEQr^Uk2btD~LyiJ3(Ql?JmQdnqip;&NGucUhbcFOikbFtZD1^j5 z)OU)`bUiXdlD;!XSe>Ir@2{)C>eP?mvev2bN(a}R()nE+^%ni8gs;bw`#v|@LQI=@ z_GGkb;MoxZPhaxl{B{-&p|$G+%48v_e+tcItvWKqh~J(W9@2>REQO-!>DK~PEn_D2 zaoIq0$9nm<=+ppTXrXGofRWqV3zePnAtq9!^>4z#u^jzs$1y&ux!-65i_F8B$!kb_ zo}YzG>Ax7eXDCq=APJyj+qP}nwr$TF+qP}nwr$(C?cH#LA8cNES8z{PdCZX}3(fFu z!Qn^e$nwhw#k5d}#q4$w%uea3y-ijebCX!6yKrOICr9C0lRGn)^~@x7w=>Hj-idk7 zzyad%sV+iEZk2cW{YBP72K!;enT2IihI%Ir=JI$;Iq=$VTrXcw7lks~I|KLL>F0h> z;yx1LfvvYu3p;gidkN8LQxjhlL_3(kHn%T-2wpXBp!`m|aQNK1wV#$8LmX}Pr}i%J z$k;=t+ni#JIs_;TQ0{Nv2lh z!=c~NDu?jrp?Jx{y-gc*C`GHwLL*3#v18L%t=BGbDLqHmZ?^)(!+=@gnzc4FE8o;o zyg=>^nBX#~k7q?Pn9CC=5RrF|#hZt^Ex?B-=j+;Y2@(m2NW`Z0ret!SZQ3)`1g-Id z1({sI=A_f(W9fRDM?(i^knY-f6{SeCb_MiiwvQL)BP6rOkP~0n7q{Tt(@CKNUw7-= ztX_}+^|nxNu5Bm|5pjND%0UA7`j5tQ8?Tpbd9ax6BgFA%@TZ{sXpatI3Bl3zhy(Cg z7m-X049(CmrLl|KSM=c;h|Sf$(&u;26byeRknRCipg$^7n27B4YneSjMbAsR2z~OgvPF7v{)6)7k%YnMPFHW$@W036LZhG@sK)|&_ z3xvaiSL66pw&iLP3v>61W~Pv{&b~Yf*)5+3eD&Yym`ar+}1*D}9!$fGB1?{yqC?yi^67O$l z++DkPwLUP!j7xNi&5>@S95AXQxy)m zBb`)LPYH_F z!W+_MmqOfi7L^>JV%(%&QKUgn94C3>{Mw}Kvd0V2&q-|B-QEa6PYDKst&T(}Hmogp z@bjnCZ}#lHcTCI~-xVvfNHo~q=Za_s9wHq^$aB&q@WAa9J;~EQCklP#LXAjg^>Voa z(N^77)GW{FHd7Gz6e*XLylVIj_NI3~iOas91E1i&+{*GCekg)E&a`w3UA=?#H@pih z8)TkXCht;F0V`2)hH(m)D4~)M)HSPtBCr`A@hmC;HR{47sO(9g^__ple%J(4(TW5z zaCC4`IS3kD$~-1p>wXhcMJ=a6BH3fIQ6KTc*T+vx3YWq(JtZ_!t7^1#ch^=aH8Cmkg_ zkWQ7R5q?SOxTJmO6c+kM2q@Ogu-5i+gcU5Tx$+R@>x_v`D*xe3AdQr)rJYeUUO zfq01X-?q5MK-Y2g8CMYNyf!VOvqY8jjvHt9zT}A-JU|~j)uqifSSgnFKb84|6#JyV zILK3X7B4+LE&MwEHXylYxx@;9LjG9RknFPdvL;r}KCT zqzeyeEmwps=x^&6W3j3p3XM12Gz+@ghOz>T=3$ZMj)69)8Zg|BcPBtwpy^kJ3-DPK z}9F8Fw)Z|z7<$-lW?6Hz0C4xA3B zr=W5=-TT(+>`cog;W=xmoMQsr0#hqAN6#zijbWVeAjHBHp<^fQ#Z8cJA5T3Gfp}bp zC*$#T#b_SH$4+ch*V&7Z^l8aOQ!(Rz68nD#vLxq`iMxJueY-U;dbtW&Z+ag-jAI)- z{vd#J($V`SP&e_5neY#IEKnKhZ zTYrSG&A!oQhqxa1BOx+wkOSQyg(q`5mV*f@$Pvd%V{a~l)fO7jNCH}+m`zt{dBGpn z*^t39P-J4KOq+Vgk8HzbH-Kwq21!Oz6ApjOKWA^qD+f!DoAbv zx5M=LF$=j8SAWw)p)cj8#LHlwY}Jwn#?g)h$T9|1YfwujettOT4~}M5opp`IGmGU5 z(jrlD7P8Axe%D|*MlxsoiwKfB2zvRN)uq0fdod!tDDyd+BP3lqct^U%lAO}R@AXa= zVZ4|^V>dyh`Fv;0I`9bl<0KmrNr_S#$>5QKOIT*U_hDs%m(|bMoBw*a)20WPg-4AqO&JILv6^YH zqSH{T@YV{i;mU3lJo`gWCaevKk#K5_RS*#)I};NgdV6@va#S~aRy5Qa527ye@`YGP3Zc}US*Pk{7ztPJV`un5}d6uHW@R# zE;|ttEG3J=(9~|_5+zeXzzy^Wh_^^UZ0FzX6wm?2-dWhBKnFh>%5~KnNx&*&X7EUI z>M7o_#+K=f;9XZiDz!i_85zeN4%s1X6=3!tpQ)~)pqx``PKoq&LvDIs>aay$*c45X zW^4ra+r*~q)Rf8Wg}G%2rSpj1kgIAmhX3$3R?_5<#VDr>mv&%MscT^BvHI;;-H1J! zh$9P-OdnE_=>N?Z{8)F=&%6>lr?wv=TU0oI*JOOfkdz??)Gn+cfoF|mt#h%>DLx)5 zY;k6bXF8ADY{r0>oq8*yGRmo68qcfPU#;UVA$VvKMv&;WLv>)QqAQ>EcEM{;%aa}X zpdo`+Us!txqLrPT-6{7a+3C8!whu&cpNKWlx}P?oxu zJQf7I0yf&3ZDHd>B!Rw~Y%|_ML8+$d5EXba(!{5ioTynOc_KXG7L+P;L&Sz#eqiW( zCQ~S$xk7ocLJIhmLS8XTg=jq@Nsx=WbUiwWqzK)q0Y}*gJY`Smxc+&2O>6F(-j~|- z!0N*iVN7K`36Eji61NCYSmf1;JcwbuXz6OJ?Y`+tYr`*0)$wnNYm-0w0aJtIF^NO#=8f)kjD+ z$oPEqpz86(zDR^$?ijjZrfs(Og`2z2aJH~Wdfk}oIxe`Sjp+vAIp!DxJe3TVITSzB zj>%l3{YoENI|R;Rq@wR^=q34CCWV@-_;?5bGPyJ2rx}g4AQt9-b83|zYcAefClg7V z(p70af}3kH6k{Xcn<{{*(Jnt_GbKJ9t;Zebz42|i*-06pxJp*eZ@;-d>6inONBby= z*s7m8&s0!r`4B^I;^SQei<ulqI_F? z?pU?DhOU1E*_?j)ZU~@XdCH>6c)BDKQG<2_EE#E+bRlEadKOQh49_q2zLL^{f4=ad z?4YAqf6r3 zep_~8AYOn*FP3c1gnQ;4R2wLJNzAO%%}L!$Z1;3AeJv5;+KI}82b;pdwg&Mp~+`}fXdKTiq+}hY$A;si9YjBt2MmW(dF6)sjt(N46Fo4NY>{A z^mF~k&0#H@9sY)hurR9*w+UFy0S9t1W_g$_^nz)ki(;D#`ObV6iPLxeR|@qXgOmcw zJL_bL=;fSQHEK@QP2H`|*~}ci>nyiC(_`)+t)s!-q8vVO3Qb1UV=k4ZAjHqZ8Mh^> zKeNMaSQ}9)4ruoKs_^SkvjNx{f(1kACoFP~ z4!3lsDJtjEVRB`;|EZ{et+z0ehhWqQz)U<*4wBx;Sp=uHm(ddqM;~L=_#FDG`wgf5 zt=HB{&ByZ^$R`g?KOKb%yvFk2b+od{3$`t4&(@tbjqN7nUH;^oH5nr$P&PMn8s!(= z2cik;D>eNBPue^=Evq&@wHigCGm_NlRqFWBX;m>qac7_78pQ%ghh&1j{GdU{2RI#d zzD;bLclc8YMD1l!*q-NldsqsuYJ}Tnr+Da1o2x@gH20<>+@Tvd6r`+lDNBMhgiVG* ztmD~1I;RJpJE9@!u1s*rw3aHN++vZ0{Kk9odnv4XVZ8=+>k{nymULhLfZEr*Q@wiU zh$oVC@i40ZXTes0@&_x@j6x@{$MrTdKW2&ZLhtEM_<)M7hv0hoKDtwMDCW4(g^j4_ z(?;MtDSyz`OdyxpyM^Q~%qrKVd}**&6!KT6jTQV*4kU{5%TR+yAB7NT%T`F!l4`&U%&GWRp!~-uf zGjWF+WWktF^~p)h!Fjtis(&8%KGwBXbw-;v@HFp9G=h4~jYwGgsPH;p zeutenVs-}DD#nnOM+}I^(LZS39 zXC*JTR3zDa~ zG@fvu76?hwf+Fu~*DCS|Aj(d@S4EjM7#-Eul7VzDx>{1#3^f<-1;f1l=+H2J$T6U( z&Be%oW+b}LGARqFbBM#;@!dt>p5s@EdjkHvk=Rw@(PnjOOW^_*(VLvpKky9e-O>NV zhaCTf5C6~e9VP-!W=^*M#)qt&oc~{Z*bb_)bcf9r>+dh&#vOl)5Knh;3pR$qfB~~{ zej9%)77qt_k*XG-iXzxaQcQZBSR%<;y2EBiRx_^3dGG=D97(|f2-)C<_23!w~1LTDClNOo{jS((jH0KXd zT*x&InSTfcdJhTk9xU<^Eb8F__}$%2kIK5~l? zVYZNm=l1q?TR@zHeR@PKF$Q@bPH-L2x!=A%3VR*vzRJWOY##C3+;V$e27s3v;u}7I zZW`^7O8xH+^#K@T8WRI;Lvf9FON50(zZ+$+_QUL80|OgH{6&fp~Qh35j@k0AD}= zFaK+IU(op{XRtrC-_QiscTeu2Y(Tbgrv6?8Y=%eREo?YvU|et!P6sP z4PgNYf7Erf!{DDdHxiiEkE7`9KIma!gUP63e0=}6wW~Y3Q;AT`q3rzJzw$pk#<oK0ZD||N44@{=*cuzc`$8hyc&uiU5;ZYM>6#ONL`Qj-R?p!uPlpRd8)#9o#?l%4h(hHo?&e zve`|&z#Fw2ptFMg)u!~mT5 zHV7QanLQ!~$FN^)^<7Airr#Eu??54d{3z&oWNc3BV@ZVlnXqjs>-sS}CSdpW90Q1A zNC3?u`hKv}z#r@SK_URW8U6V?AKeB0-ME1cY{A$M!#qrw1rN_iBH-KcHYgV2QTB!FawJ9e;v*Kkvr} zsBg$W!RkH-lS#F4!k|CUho}FI2DG;B`-uvH39W)S01FMsNksnYGAgaSuC2hje&i)4 z!~}2<-oZm8Ci+=F0lDXoJAA97LUIXZ>m2-*69TGJ03E{ojHw`O2$9#{+=W5nKn4F* zQ3K$anvrz5{Ch?P2~)j+Oz6?S}M2ytreDCpHE8W_X`Jhba4(Q3R(5y{xpumuiacPc>1eEvz{IzD4VqGfZ@RtiFHtGs0g3ef%e+QZ z{|&<@-)S}Avc^ccNw?QJ#yT(zWTX9cQg&8OB^k=dQaHd1sA10vY0J5%MNZ)LC(eJ* z_UB8%LL^Sw>InN>OwV-yWi+u7Ly$f;%Dk;CtLAr&`b0wVBHY50{TOnZVPP{qpGte% z0RFKA8UwBDSrJk-98M+BhTNc%wMQ}29B}GrOm+vGBpPElC2c+%lwMN? z1-69@0;+{=#c~XQXZ_)0>s%cGF(+~KN{zLoS!;Usz28@2As5?pSS@CpWKz{G%7!n& zSA!OK@?4lW2f%R2$-7%FGaxNNAGIonj#)_e9RNbhIzmaCGiE}5f1gn%$(IN3>rF*n z3|!=d-@>5V4le@;uSgGXNnT(%zfso-!*_Db>$we6!Qhp0q(J%nkRqbcBr>@Kpw~BdB>t9b=@&&@uIp94 z^K{fJ+MP0m2+yAyVx?CWvGarki-tO!Z zk!MP$B4SV@#p|y*jEze^*#C!lyP-^^v+pj9@E48D9+@Lc{K};wV#sr zlT!G!;6Xowez>ZIg_j~oKM$&<7(cjV%}yIIQpY2fDRjqcYM|Q!?(3y}8cJWD2U^i) zAcDcKNyiCkO-92IoAs+^jwFHnI0uUs(cEntT{DBYw*$DfwuaR#LT-vRDQi z>gC~UTK*8OMy|~F(eBP)MEVcv!t=MGdQ5^T5^Wx!s+^q@|;!&2`SqgC!6 zQVFS0tM(Iyxxz7FxRvEk7{en z{N`F_3!S3AKmkfr`hv&fGC)r&;K?CbAf_jZh1jz=VO8V=1 zHS>#Bv9gTrBSFK_wPyIfki>kvUrSQ73{!X{ZZuiu5i;lJ>mQdRrIYCS#^&60J>^4b zF)gPP_(W*a12<0ixKXuDzBbcjnig95i3{fKQ^xSf>Lh-0H;#rNI?w>#+JmzqK2YBU z+=Vse+J<{oY-&t@g^&ZBSaj9a)0;A)Sj>8+l9O@iZSY(RC=hG~I({6#Q*hX?PxqZg~QQg#$@E zQbn8M+j1!@jKE2SE)L#y;RKpeoc07YPYdTnF#0Ef2x8eimEzbH>%&CEOsv~`JLmy( z*NF1N3(#G3wCYCk!4BqLl^gI=hrKU0S_kr6$qLrg0K2kX@rjZTP@??TWdzYdY!3;$2bAY6HjE6~gfAl}iosH)P1jvGp5 z4ej_58e?Rq=h2A<&I4r&sclc>?hsCr;w*Asmz6-;Bt5$#dVp}Rz)mF(27bLZA++^^ zsd##{wj9SJtAIl62wGzIY(f%h!S{~-nG2`TT`VKnmI>YOD|w z!7J<(FzBnIpaI?fMwzE2+X_oLoaG;5Y7ssb%AHsNiUT?3!J4r~5MlE(;~a@@x_%&8 z>pZ=!k3&H>o)^)msCiMZHVwrb$xGL@{?09&oN(Y+azh%;Af>{xCXAIP_$F(A60b?0 z-fM>i;v9shNqS8+y+}tT7#TsneR|Q`=dG3sVj;#MQ7)~m=qy>z(t$Q!C41kGZ%(;T?E`PrNH^zCBS~-ZqKYBMuxbTSws)_59E`=j)vdZy1aB zVnY})rZ_Fkl#)`F=L%Jqhv#C(G}<4SCsDewYxDR9bE!WGZAHCRUK=Cnm$-$txsKv1 z29F3BhJO{Ba!q4aV{=moohA5C4>cvd%UB78$W36_;9;-292OXC7=2sn>BxJjFE!FT z4&`6lQ61GybDa#lW&`|VBK=WRPEKDljK#2|o+y6cmq!>dS)PA2*N`QzV$_zZ620bd z4f0Ccfm_6d!f<`>gFKzRCLdr5*JW)_77>@gUQIlzXU!&F;T;Oi*rfJm>)ok3Ke1V( z186~o@$_;J?AyT8-8xTTz22CQ?`{hdIDFcy)mOA&OM7WWDALpggMyW8%b_HC(sUlF zUr1|_FuOD-@}Wb~%YY-*x+p!AErwyqdOT4sF;99)Ry|~de6$I3w+pa52ZMhNgVcR@ z+Zdvzg`Q7C`pMqSDcFTF9FiQ_UTc{i^21lYTcLWv+ll=0_=9z< z1^m3%zL3$WKK!yIp8cjaS|6r-)4f`L`Y+k*LwtQHUx4JJ6QJF*w9$QUk%i}Ba#Q_SgC$`o;GO>Lw9ZdQx7mtKC_s!!@_(4gz zxYeoa)su5q=t{b9J1yiinfHVPu-okMdv0Y>#05`q_F3k}S5giza)qEs8gTJ~#3cMyz*_kBeY#fVP0kf}R?} zT$Vg;$QV$h}Hfp;EE}MscH! z#YHzl%24m2y7`XIy}k31;HuJUD|BUHyPJ46Q!<{>DKuBeb}kZ2m^ogId5{w5El8u< z()xWZ=|IcZXCGYq^lc;PDeM)C?G3nBRNqL%*^Iq-ud-w;K^LUPqS}nbhu-5-_h@B# zEc>Giq3NUX`S-nwOobmV1WzE(qW}5j&R=MK`DCiog^-p)>UZws`i)rHmG{IF^4~nZ zwJVr(PZhyYZsH!j&e&o4yfb!GBzrqlD;ts!W2z`;Wgw6UDz4ZH%lqCTe%Xt2vV+Z~ za*OE~&n~H{U3`3`W$3l%pTb?}!J3(lHE+|`k;a_wahLk{!Ar&oP29uWVUWQdC%4i& z<@!1j*ySlRhkXO^fYr zVy`22pnr242$&C?*yM!t~Eh$WKP7Yw%Jf5 zY@F-C!NA4An8~|VO)A6>u*rO?tAyM^zyX#2g%^E8{h~kxzF6jje3hoz^otT+;2p2#6iCE=4$q zGd6kg+NkSms!!VKWq_LRI&+RjD9ep?r~!do zSVlBHK2l~b%u&wjZ|dH%xJ$>yf8ewVnm zdqRPzfaoL=D6rQOnd~K+8mY|Iw5npp_C(#{9G$b763++*wP{(Zrd8Jzg@s>P(Qd%L z6tdTcX=h|tgV!CWwS%z8XQ|BSzuJCj*6BeW-@mCbW=+%TpyzCNtekv&(Jx)0%D&LI zVr;I64GnQnX_m)w3k-=Uai`W$o^2plU0%+xdNFE+ODEb9sUDrwd03b~vzvt*&XrI4 zXFC~fOc}A-=h0$REn+>Awp_ni%5uJQjmJ}NY{-)!88H^|6RBA6tX#R+^kEE4MR{f{ zGZ)c}hEzjJ1{%?tpF04!VITeU@#HPK!b#i3#UJCjWafNAIJFZFz*$Yq%T!k)nFX#5 zKU}7iRAdKq$Lkm9O*Qp2Q(oV^SL1BoT9%} zl8S*i=3rez5hZ!DaD^%c(2mbGFwt9(bCp%s2Pv1F&Jg+eanGh$|EsIwFYV7Iw5VLj z741ViF9lb#GzZiMydY0|q?843YT12FFW14@Veab5nD!PRMvLG=&!JBpp@|XDFV5?x z`i!L9;?ro)geXkDdOSH=uT1XRQl6E#d9=#t?d(K$rcS<6g0wxFRNhZeJf}NE-4PPt zIoVKN$%{HDJ;A`D%1GFN{*K$*)Qo=(sLZIJL0k5B?n_+!5wi7W`VYX!Ze2hAzP#VS zrM)_~D6zi0qVaMEw#Hs1hmVKiX+~zl|7>+sK_979Pp>(9Kc+D1rjkz#KVV;cwEe!> z!YnVdJ+zjBrZ1U3^5n`%-f79z;i-yod50}k^JW5(bijHFVEfJ(RBrFBXj^6V!dSpC$kDNk@C- z&3fRg#dyu!a6>Y|y_b9%u+2wawB62JYO}SveA`42=l4+;M~MR;B#Jn_fT&E2Rx9yH zuC)w48MRfU#1mn{=>enjGqWfkkjKlHQ+%ng4mEQyF#s-99ZnR~6t{dxY=vgP4&EYa zqVYk}YxqTach<_=w-T(W^$#X*1epLLZFs_6M)rl*1!%x1*+`aZkdR)-tGFRHAjJ^( zXSr+dn`5(qG>km^@u1%=aJzLwW0A<3ihVL|wA>J5wBn2(zhfvnLf(z(ocZ{x*)bTk z*Z{@Y=T+;SsA^0VMsufCvQd$9q3GvH3%sGss1a?`A=6Y6h`)2)6d!6Cw2P~yfRT-s z8h-y~_e9gm$ls6eFZhQd=V9mZi9nwYQOP#9w-EM)%U+;%S?6s(Y-b`f)XN!U?|OA% zx+tt8a1gF|ia!aD`NNosG_$~7 z%`^Hld$8&N?@Lsw;}EJc*!t?g=}r9gMhdSr9U?|KxRAq~?6)yXXWoznz3^JLahA^1*y3ib}PR+M-C`@!dqa{)mu!pW%9?a8sC5urn; z^0Ed8E6z(cmKf4(fMj;ugNw6>TY$iiO^E$9(j~7(z2m zJ`o4<$r8mYHg+%W$aTi~!=gZI&KaL2$j}!9L@dTgL1w9}F;26#AL1dJOQ_vLCaP1* znmoQE(_JgEd!@-tg38Z!q+z87(~J+i=JkD&=-9WL%;Z@uHvcMvDu;~B)U90>t*WNE zt3$RyaMV8TqPA~Ok0oy~4wnns8xyc?5~ z7urt>b@iSfZWYtYuXPa$@CQQ@GJWW-R6MaMKrnBjsCG*F|*UnH^!Yo}0+$D$X zBE}-G!Kqsvh|*#~k8lFSPSgw^*=D~_t1X^+&cIa3huaAZ$~SE7`o-hE$bfWg?+2E9 z!~`QWDw?FN^n~W;8f_KCO`A%YIBTauABC3VJCvDb#>ce-V%exi@mt+b59RC8%M*zLis}B;eZ(6J2 zRh7ZaR#QYU`E{n=TFrL;DYBP>V+iBZ%;VwqHipA?n{EWJVWEj}Wc26L)SGx$HdCOS z@Z=PfA|=m;5<7kolbZcZfc>o}Y;K<8l5M#=#N&u|iGrx09=tw7O$vlsCLQTh+_hA7 zj=(N#+H4&v6{)inAmkgcydrDmbZ0S5Zat2Popy?k1-937tf-31V@DUWg-jd=Ypm}> z(+TcuhZ2{|jH!zBbXs|Ra8h3LZpDuYbT5BOCyp#a{c+ucbl0FTYR(j-$7V;6Y|fJ! z!o-LT^2{Jor#hjcCK^=}PID(BQfZtJ@0A^B zYnAFcgK416#Gd2%3yyedA2&sZh$TFBq=SRjs8MVBg{F-w0yA zNQ9I)a%Bafig1JpAN<;iq$)=`1+gsc5xp|xD}1S@&Jb(?e#yw8p1G6Hl@S>c2`z(} zTn|4~S+{N#C&vg?sKRU`ToEm6ou)blinOj&#bXYI(E`u*J85%dqJ~}rmHLl6q?}*H-i2Z7 z(}cz4fs?FGXFE9~2_{*%Nq3hcKcULr!7W5DS)v1u11($VGS-N5qN?bB%!O>+tQZ?W zH0!Py7*fT14P)us6xmvwwwB(qFf6zItf%pzYDoHU9SmcY!dt=)Cd*^bDT4$BAu&ft?!x;h z#D3gt$2Z>`Z0C4O$nnvnO5glk|K756X?gg34$^Iuv++zOT@#h;KLu=2O@CoDj~})` zKQ8FkG2E-#_SHgoGi;csN*60?OYrbhtMjzler*8tg!LpOdap2e4^GkDT9|Y)*{MHb z&g-s~TvJL_%5s5Xjp()z^0Aq%Rabxp1nS4~4Y=e=y4P~?$611GJ{mGU4U|g%*1Vw$ z)2Ak>=j}GPnBz(r{C!tuPi7P^FYl)FFZTG3N}rkFp2{>isdf*!FBD>kx2 zS$!fdVK^a59np67w2Jnyy=k7k-Qjga7oV2aYG&I>s32C6Hcho7O^44WMC0pkmW+zw zbYnU4%|K1Kx80zn8to@AVz5#DhWBkuhB(UbzN8rS+)2Uwq4Zuj$sb9GG?ZstbT;-2 zRvi}lM9b*4QSh9mpzeGYtem**%RSoKoj_NWm=SW7j;S|N%DF&HuQlkkzNbu8YE-Es zCw1EaZ~CO%X`<4!O_JT4#;eV96r)xTZNS3Je}}nnyG`6Jawk1n%uumhZ7}Q%Kjl-VWY(Y^tmj*udXE8HXsiuO-kD^=;t?%=GTBq$i@{{;!r;b^1~0c!rgdbW z;{2;h%c282HSw?b@W{{FaPO^BZ$qm*?5L0$u2cc~W5706rb*Eh;&TrGK~v zy*<=Q#RB&w;Wzu_0pWFW4{!P|u|EIN0o2HI>|s)(CTa#A>^i3@`YaF6g+s|!Gj5V= z{@_QU7$`}`ybl`pvktPO+rk=PW?p$h4d=jxkPnB8a5EvxooVT_Cfpbj;Wd&9SbX4P z&ns{}{##gEta=ehz7rpzs9+lXoQM_{a=T~OyLyA!Q}S|EZll7pnB(deKd9?^lx14T zc6nYbGCJQ;m$HUcib*?zFw|GLcxEgEEAiAT*(}K7_^US@p0SNOJg$)#gsVHQ zwmIv42->RFwal-Ws!bcE&;tyV{HgA?`>ty9VUE+d6Ge*QB!RI!3oiw|M7x&EEU2?N zMk?7q6CQD6TI~JGhJm#bV(wdv|G}8U`1DXyc*=ZQ8e}sjldNmg>&tX1yGp}%2GQSK zNU=y3(}(R~gC%bfS=F&Y3BF}4D4aXB?XBz*b(;Ygc5dNC=gdt&`}8F7q|wA_w|1hU zyB&D~$`c!yBB!0ubIr?6dUw7G8*Efh%*CLMP%X?vCxcD}QY3uSF%=OCdG>nq{R>=n z->vwjiS@tkr+`e)+UB|CdzFz++7Eg)UCx!`cG9wT3v@6n= z`pyy3$$y-A=!$woCU}4NH@;{q{)Qm^CXnXFz?ilr+v=wO18(&pj%o*w$J~B}9Yeu= zmRPLl!S6WGgf-V}?~x!wl+#?5cTpKNW8?etY3d)rW!rnPiJl}<0fG3)w)rw>U}cF7 zqXNgHsL4y-__iF!%x<5@^kkw{2qCr?x`M{|s7O|2g(Y~Ai_)$Z1}zp!gGyIJ(i9y5 zt8Rzp#12qD^jYekDapoov($a>C?uR~f{Np(exSByO?y@rC17o^VS7mt9X``aNkgYO z8}trz1cYKrVkuCD<6KClK06nC{790*@i!E-(7$6s8phT}t$HY62l=mgg$wQaRFY*G z>0=_i^XuswEGlG#ZP$)dn2k6Vx)a>Yn&5?<246#y;^Y%;j}mn3wS-P&c$}ZX+(a?sC|#}f;ylS?$7md6 z-rfi!Xoz8GUWJo!xAf+Q_K7C*~pS(k=fOwM9-S8STmI*Ltu~7*Zq2X+R1eY zL|FMHn7VaR`%4u3Nf~rEwPmx$2@8~XtK!*4$ZibN*fCIl+i~cCm~L zed=iJz9DI~0s%eo57VY`I5$R1CG`C1S*2>AG(T&>sMLea@3iV=f-Pr%vWuCyX3SD& z`7a;e(6L{`lrlxAJHoxsd1F)c`W;HZwc#RoV`Gmc{!%;#_*FyBd%2`{380L{Vg<{h z(%*RdeXLK~kcTPnql$7Rao{h>cKRhYAE?nx3a<|zi@VyZ$dIjuB&=!BOQk|Yl8TO> zbe%i89-)DA=9D-QOA@O6YB)41WSwbUK?q)y`0IInSAg0e9wOk7EqTEv3<|!>R!#PD}zH2OU zjUzmfIBH3q8gKVP-3dr@qQ$BtRA?wq48Rw_8D-a4_uaOckWDDIkBs!1+O7^(G_O!i z=m}?z_M8x7^OYDn1gCrl!Q{yi7A8_c2_W-`(RYcTPq&qNmd**?np?ZEQZ)!&AYFv- zR1V#oz=UVrPMst+G9o~3J7AQWT)leNybCmwNf#mDew0 z=lzq{3Hl)1D#W<~-I+xi*&nOqxZ-xwfkGu$^X9y&zh9k2!s(kT)oGln9Jt(v(g`S* z?lTcMbavH^iX4l&J5cYS;g_hqBfp`>)!C`WD|^z}dN^OI4aawe>aFh{Z(w$39k&(y zyMzo5TKO8Tf}u`h;djMLzwttwejk;DDMn}I9gu$Lt#31XJtSimBv}~yDarR^9hV<_ zbha2StQUht@-!h_zOv=iv59+_l~)FLqleuuPDrx|kIH{vJ@jTr=u2-=I4w_)!^)m*%6jhZ?+qH~=$ zGcknA!p@8^eG3PG*k(<2%_H8Cm{wYLS7R@&7%w=nAfq zw9`r}aYxWI5k>$62GIktC|C>{xF}5mC?rV`yqmRHML+=*=>JS6Ri`8p=?r+GDnKbE z-qDclbnCZ^ebke{n0VcBs^wMFbM~ERy^T;F&xGTHUl1T>7$Oof;OJjTx2%E$5Hu1p zV8Hljy%rs>VLq&%{=LZyGKw1^Ht|CrNCgHsbjZMpfi|`zTIA1-H|t-{K|n=HPDKk2 z25e+df87^u5si}%_9QY0sB5?nhZSN^-d7sw`Z7RpgInKj`}=~r--ZexVrp6<*tY~| ziVgy12`voR$xsbE2-;ReJB{gIXqy00J;yI~KgDg(Q0quUq^GAh$bjx<$TIqy3IK60 zWP}SK9Lx=L6yXot2L|?RKo`!R1ev4{9E}ry>t|1~33mlLL;!%ZAKfgPzwLmDN5KIE zyw3)nuf^rxOAYiAR{azP0Q8#+OF%{QdFSZY=^2na9)`|if$rVX=*dPR2~>-@`F zI9|upuB;|Lpt?Xu2Z6Gh^R4b@B>^*t=620Ts?mG+qEG$v~r$5|R?YAgqA<7FYN;mEG}C<44e?qIqKHnf-1ks8Rugv#ddL+~)_B}E(Xs`(>34kIb zB&L9t8Vba_IVuFqM;r9LpHhp0tA)U;d{evfr}|K?-(B8c9Xp7>-|C|9kw_H4`(Kd* zbUn#B+8f#DU(NF#HF|E1Vz76L{a( zl;x}h3Q(Y{_^)U4eq;asU*y5H=~b|ocl(ja2X}CP$64P0Fmg{l!T?N~K-;!$zHQsK zZQHhO+qP}nwr#t6zsV$#wqLNDef~Rs)^;KD9y>PF`iky@Tq>lkJ9flLFl$2gf zvQHlQM2vIrqc!dyP7f6`QUcJ92@Jr+9QwW15(tQhPBJ<4gZ_!#FCjdP8Z?|UMt6+w zwc)8x&p!2&3R0WkSL64E9dIwYo+u}iZ(|qAr5eVDtM;$S3$g|dVUDgMAGns z%;_7)Sv=P~w!LD#e@cCqFVvgblb-uvj`sb`Ka8aGlv9N77N=+@ac+j6!{Es5%=)w#EQ&}?T)B*ZaI+kmvNMPXGr|&c0NvuVf>vqrBr1U;(FFcn1JnD?-mFLN*1$x1vioTiH%?CEQND$O<>8-I?-Xz^2N3o}N7KUg?PxKc zL!71;&k6$6Pyg>4Ufp^)EjF581m$U@zF?aWl~8;jvyHdG_q!Rx;;OOC9`ere?ZR!O zo>7Y8OvRADw|R;1%j{5c)YF6KKlA00NSQb#$0>6+!w@NNCnjMkT%uF;P!q9cWD9PU zx*esNRxkT{9+#euE#S*?PKBUI8y6EW2CtsOzXl;QDzBS{scTFP`3XUvYb+^TrcElB z$T$!GwOpY-ttu+1O<7BM?Qu%V!dV(ldWgqjnZ$$6C{9vSf47XT17WS6EO-*Bd)M~H zF;7ZgF@_uM!a2eUI()r|naj=io1&+O&@Q=XGC{t5p~#TBY0Yp5PXVV4@{~!zPVEqz z@aMEi3fx?qjJzN2%{JKCH`@-GMRe~B;;EE@Em7_<9X^JHIZLj`^1HDsJ2rA&EK7yj z35}9h!t2XD(PebiES}aKGA4UHUHZzd17_YIU``8F19=-ta~7#86Kn01Rvw+xY;$*+ z0hf=96VVGTW2WWSVdj+XtYFhHhmJj^Gh4R@U3LzHc+t#;V&?j>s)nT7<_qxoNvE71 z(Rxd&c)-lX*sF~Upn>--vzGkJo#pICpa3GKvBIQ*ttiBF1%XEU!Z)L`hoVGnK5ES^ zX>y<3YdI=8a?4sj6(szsTE_Tjv@qih1KZsk23GoLd{0DT>Y0oqIv%Vym$$9YmV8}` zq@e=)GmeofCw%6N;awAZPgiw5JH!ZAAJn}H!aq5g-Z~bzc+NHRDjad}`}y;ivT1%Y zw}7y_lP^7$S{7$d-lTQ*q&V{n50j7L_)ClPG@b!g_)bE@o6|5X7`_wysTU8WtOR$s zqP}IZUU`H-H{G_NzP`tJb7J{N1Z)p}y*Y8k%?xDU#s`@!`nRa7B$7yzBze=nO`_$C z0?xp9SN*AN_uGz(z=431T==RY$yY~~U=Tg% zI{3eI^eF56igk?kGW{dEqFsZBWY6|9-ep=b4|b@>>%x#CHPN>D(7=l6(N=zD?BOj< zLu>TM`02gg#qI!uIkm(qElm+8;F$ImYS4_l-CNt;t~^y=_%F;A!nTe{iS|9$9O}>$ zpJ@PG=VgW7*93^@f#>uk81gdE?j<}&_` z^nTB$cl2|_4lMDfA7wvG159B21w(>@PFZpp|GZjKmkq0zr)SWG8oQXM?_@;+{})1! zxlSb@;E%{H+7iWEFUnL-qfC!lmG3*8QJTk{VxdL{Q-Va4z16!wmq@6c-kS3cV~2Mv1Kj&D<@LtGyk(qzxi_#-Oi1c9)GZG$@^X26;fDSWlz_(+Sw}U-e zoabhDPQzgQUX&$iSGqb}njO~UWQaU1ui*v5zR;9<s-rn6YGn4&1>G!5a+8F$GZa!iw0X9?o5G3y#F&smuoA#S(k-ut%q*;F{- zj)&(N#`4+ML%BO}oUBL)koapXzVl3EU{H30%E(HguwfZ|B3NM9uyb`CrjxtaCNBlp zrj62z6fc1k zLOO$0P2rR417NthdrPHZ5Sv@khX@(S;^`58@F%gQ{eyE~1QCx4=axHCzyOgU0tDcX z!GI1h60)J%W$Gfj@DmJ@6RXcgN$z~PW(^_MEl|9mwDFYwri)YULM+%>)7Lq#Zjz}Z zdY7Xj*EaYiV-hZL=`cT2o;l~Mc2PF9WaAD+6TOq ziBtlUeAR`eJepM$|HyK&Iwh{jCfK&dme;-oJ21bV6^+`)IT=2WVHY8)llVmE<9CML zq74s|qZHK_RxGSdq@f^6L*n%Gj)48lBP_}@St>f9mZ|R!!ndo9ZK*h?-%}P2Fm#QE zdU6z2B04UL!3weVl{Z)$i~yMtqBu3P&3H11Vm!gZck zT=y;{aagM8yMFZIwQkpKx)-e2rkkJlr9%SD!$PVFm*xy8+ixBQ z&q9X$yb8N-_dfd}$2hG@LgC$0*9GYoANS&$_u`4BKbQu+E*l`7q*SNT4sEb3O5inl%E~QG>R+0&jUv$LDtA799XlXCK9OZD z%0?x=&N=b|e)9sZU*v2H9g&WN_;%{(`V7lhVA zkPBvPr_7(6hc5)3qF%7jPqN)$?=B6C>92rkW_Ge4)EH-6(_@}vN}g{+aOx3tdGL=r z-cX(LzW<7bzL6C2$7GUV0K(SHN1pQh|kuE8dUN?D+yWaCK#a1LdnwB8LcxG%`qsQAd zOY#Q360Hn!Bv^SVh?303&rXct4kK=#o@6HG60_5<%M7|_g7K<_O3nGpiH7m5DCbNz zUd3I^i~)J3bP7>9k57b=;5%v`v&T_-r{OO~9aXOxn>ZUUkaLefS!#?a)BMPCnnB_{ zL(d(Mlm( zwej&+PPfgSj?^xZkpt*Of85Yg+NI3^4ZjD4KRGGH^&tcjh5GIMpA^{zw71oqh<;Ftzav zn@6s7#4q`3`f=wYQlZ@LtM4K*Gll8}3t~56w(GDIy8;k)R(N4bTkn)H%1gD@P&*uk z7yMgKRv||(yZhWVT8M4md$wO28{G37ex0$E+QeKm<&6ahjlzvgx=!G*EvC*Yj=D1X z^oQ@c2zTjIzFV%2%jy7ird*H>J^YnDP>c9@akc=)&MQR9gEYoq3X7d9%m+r~>utPv zSUx7&Y^I)tg^?^0_*bHk*~3n=MM7iUpy2*MMPF^BBh`6e8hB8|5W0=rh0Lln6hiM| z;pMDiCjH>hSti(^ABR<1o5p=Y;!UC_ERjlULRd1SC&A=MJ%M`9ue^uMc5C&B+nYt| zD){n5)WEJQS}#Cd0>>pB4@4tk;H3FqV7HG~nBs$$MQP4Q1FBfcsdD-*H0A&1POVt1 zg<5~^yrNpa=OD2z#%bls-}M&HB-D5)U{Y~)ca#^~P69c3!WY#glD{kJhMcT=vPF)x z-y}scxjdgL9H0|zDP-*AQ5ot4{-)Q*l;(#z69#;3k3M{^YJvUr0x|<^SizhBB(>I) zrApWrZYG_-r4&~O7s!4~4UPIJAucp<+Ed6B%Ii#K6xg*lbIQL!w7&`J!#7L|Ou56K3`w8x;nq^NQg0}7T;EJ2e4wo^ho3G-#etXH%`>VEpIlf!t2~lSX z)IDGE^kF<0tBTk0irbt2743VUw$Jc!kw*3ns8HiDE#xq;FWdGUW1YXx-!Xd+J(f}! z9fC}GScl(q4q^zR!bqAq;!VnRRGFyH_kzbPmuZaG0tI&JlmQz^dY;2qK6Fcr(@gSn zXpOT+UZK85!ToJjy3@f_&pJ&@$ltj5GI8_&mNk{_NQ6rQkTDBTuY4^mAxPhdSw|#U z!9AsbJ)?gIqwYu1b))9_3TH>WM;MO{vlVt&R8>Si^M#-3-E;#}UadpEw=G!%%3}>a zT8dKkjM$ffvcMLA9xbEXNSv$4A%Gr{lny;5Y<}jpF}s0oxgb9}%N683mH5m#smKjF z+d?C2byL<%*`@Oms<_RA>2`mMbm39up{36pQ@TK&nZLH0g=?A&%#W-;#G757-As{Y z9Ohn$-YC|1AKUNIo&8$#!k&d3y8wuaB>u9HoJ`t4Xn&MzC}kP_*8cafDpkxrdShbp^0juGTI!r)Ygrcngl!iagfJ3GMhS`J42wRrM>fgb zqz0XpCkAVtkkoT{o#hFF=h&?1SW3a$D1%ON5KIjz8w#(xss*t`CT`KY*mqkTxNk7n?`NOJPDO~#kkJ2M9ni>K^()!mGMC~abBG} zEqdk1IbFo{Z0Bq9pQPO68af@lajc5YI*fnj-q5DqaX7)hToUUbUBIZby-$}xdHuLz zsC7-(^+0hFQ`&9@hc!u7c4YWF=HrP#0|D#cPBZ;n1)H{=35 zYc=!$G8CdQbs;4uUUv1aEYXW@nIW|-zf540K^4vZ`bO*!x5ziOPBAB7d4ru8wy%?`Nj&jVwWDWQ}y0b^74oO-JNP zli1KYK*jsW!J5Wh?#tmtXssD@Ih&1^{%=uXPA-46(YIi? z#T{e0c(UF7_TMBDVQYtBL?t>lU8*H&)S0PjWwIxTX>E3B=V04he8GE*wem|Fk!qUB zWWP&Zk44)h8NS+&q>2 z&1lgktMr;}{$Cmg?0B*>$Xx*+_53jkpi7yVZt)boG9^^ciN=%?|9>Vy`fj*v;of}@hZi4f>NVKEECBOn$g@#e`s-Mcm<_HW}-z9`mc zoItlukI%q8_X-`m&jkR!FxPcUYlz<`Y`4hcHea%#f|$WIvvBaBo)0_y29$gcLJmKAKwpcQ@FO|A0thw6w425-+-WRI!n_vS>oIc+e)b1gj#%dtHQ)^>2jkkDe<* z4_^e}&7MkuNx+N@o~smIdP%<+z#)NVU4I?@JOaE7!pFw4anlsEyu1s#P~G{6@Q zLh`OIc*M^E}RWuPTt!|hI#I`N+j4FyFE@(libY?fDa#9Q{$04>>21H`t& z17lpUNc{+Ry5G?ayRJa->l`1tFOvCu>!)hY9eErs2a-xH(zk8hczb8j38*YZ8ef+%FQB1RJ}&vjgEv%2D4RM=T1MZqCm5`2 z_AD2Ey$3td4@zBBw(M7v;_q!I3I-W$&v?Pg&=75n9-1hK^ZvvXAA|Q+isF`5PMK{2Ml`w5q5tol12Blf>NHy#a2vk;pYxUUPQa+BCc-Ag%V}W;sm(E0(vf8g>z@G*~$Q zLjWic3BW_f2bsY?KfnGzg#bxj28eS5@&%n_k2NT7YfmD0=!bZ$Ko5ZxM9DvokxPaI zDDUJ38E$Sd*|$5%>)A)CWrta1F?yB8ohxCXuzwipcla3AZ0%8jM&O*-*eF*c#lmP%q-rtXR`wG4$wpqwS zK!E`O%sx;+AB;^lglhmS1W2C+{H($Put`fljNdqlKgiR+*97Pf!v9D6#`dK~$bRU? z2m&R-*~O2bYafi;k9G?JY))kXkW< zkOqD2hVTqMD!qSp6zB}NZW!wSF<@hmj*o=THjaz|5aSs3@$r4v&!^#0Qn4F~eu20QR~TuEe|;Q!^W*1Z)m&k z=uZse5XSBCQ>s6F6*q>#pA5+ec-T)VM&D1m3bHAni|fx#85MQ_LjcVn43UXXT$o?} z7#_||DbQ=s+Iio+{0D#HdjS8ZF%uaFa7mzZz^_M(UL!E@4|V7r`ZC4X1XGH*j!B+Y zN+S&S+NTZ@Z|B9d47b0p`m&F(>iiJp6m8~I!?nt7LO04f=%x_GxRoZTaUS0c*!&U=NKk$7Ricisj+{x(Jq#28ifd~eJGj-9o ztaPPerGn|jZCGXe812L1{-e3$Ohe4M9@Uw{rLVIyucpqfTxk40<-2kO8t5|9;s;fu zIZW|OgJPL!#VWU?~36kakbf4L?sW#P0oMtj>jCM)8HveSXi8eUt5Zn!3Z^;nbJZB8rnux+o<| zxlmv?0y3P9g-@O^7+|lP?Z*!2QHI*Ee{9XobcJcGyv5}0%#s6PG`1J1L8ziKX3u6U zl@s1BB4-8-pn2DQwOLb4yd{N(@qIVW#@)Or1>Vg~0l3VRXOQ{QWX2l|lf!lGQ6=pL zt<@GT1%)0s9P@;lgWx5)t+@^p8q6)VO5freSn)A_^!gFm|5=cCux|YX(5kd6`bgN# zd`%#;zdj$-BQh>ZL{s^Y#4&#P!!bW~ITfY#edK04Md;AVCe03}uJ}c+Efsw-Wdk<9 zp4|bJf1di;1>@YtrV}QM5~bZPTOIvPk1mMvu%mFhvwC#WL^p01Dv*j%R$)Qrj+Z>_ z)ZA(H5RJ6T9{%PPJtRf`qHF*Bp%kfwDCe{=gc$JsUz{FMamrj7Pj**!ZY1BDkEX3M z3~zJ1>Hb{~yRk$ao$Uh(Dh5(!X~8&e>IDA4<{yP zbqG7Pf3;@Isi$0wb-wGcMx3)X*R+x9RY-@Uv^6ruG@8YRGyj zeVKX!i#xf)uPF!+1SDxZC$|4hV^KTf+C;L119_6zK+Ks}T8bM2bYUXLA^Io7Dfbxh z4i7uij>$%7$OEmfrCvEIylt&g6rWPI1IE1lm@-Pf2TEngz;SNtgg{}}@KpQc!965w zzeaxOs7jpJjYUE*uc=ekV!Ya#C!^{``_NEL&XgPf_U@0o5yr8A?o^QLtLdb|!@R$35;Z?+BsCoQL)`su02brp>8iP1c3i z?) z3*k(OgX3o#v=)%!8C_q7Z{q7#IkP~l5{{;lTY3%cV3zc5!}EC;lds?a`3{zWNXUv<1B3kY313Sg$pWNiL`kb_xtz0_6v4H z+onU>u?Wcr&zJ=419n05!ApAQo%Ad?QlBA)G%N+?ssTN$94vP0ieg{;E_8&Svgsxwr(J5rYlVhd zP69h?nA%Z?`-r6aj63g<&_Mg-vhF6n8dH;NaBokFq)HrYY9BjZu{@s)s^k>jly!FG zRV+Jo7qX94JWZF~)Kce_L%77e&>@=-2Sl;l>@d5|8`kml1iX>Eph`Nim@AzMX*Pmh z16bkqTC9;Zazp$3JqkQij5VdVS@a>y~v`T{K z+k~^U-UVIdXzZkD;vq}rJl?(VyZa8dPSvb_dRK#r8(&Q~LjI3}| z?X8y8NqH`RI>h7P)AM}JkRc%5t1YJWqXI?x^+c97`v$Ray1Zzy2e^$%Rq}a(!#Kf@ zW8fm5S%36Q&CGPBBI#>FSQNN)u&-}jp$_VWEgVBxvZn@muuydB`T_c`EzRX#=ZQZm zH|Mij40TU{v&MEk-b)%W>VLKgq3ViJb6-B8SeXM)*t$T>u0pw^(T}tCnWJW+rzB#E z6PKp+jG&deUs16iWMk>|LDocEloLd#4|~t%4i$HkgBCw=s(KPOPKSC5-#<}X1yO&5 zV6L?LetGj!i6{u_8vDYBolxI?9w8}e7`ef`rn8E=@f4Ht9b{?+ zPO<&^MN@+vmXFeFhR&njJpug@Y&V;pQB|X8y;}t`Qmey%J{G_Hw0@md@rA4b*>1vP zHbRHiS?@xYK2z9qW5F@#qj6CWoq1x|O{^;D_wU?pEv{`GVy3#tn-Pm(u?sYAArI*= zQc=FfdkkcHP4_I({z*X>(qDSQ9Vf8bD4>)_&!wG@oI_`-$)Kch?l`P*cB%-*+mQ;Q)oy1qk2mW7YqM-3Vz;S zcWTGCXwMWoiXE1#tYCF_ujxjg>L-f1)L9c^_i$kK8--54zrf?l4=*5rKFu+=DNjU} zs+B6SQ|)lsVOtsaXX_t`wRf3Rhc0H5FR^LPK>dm?SN79R31KLDwyGOFPSmc@j-5+R z`4FFC1nDJRYmatiD#vp=DQ4qN?L$VV`s*0PfcF@EMip?b9LCtDOpO}*1riv6ld_~O z$>tCnLE9OH9Q18+i(faQq#ln8?VN{M^2$$Hz090BDe(q<} zFPNrY;W&6wZz6rcz&p7*}qP!1?j!3LIR5pAq@qj;-TcOo`+er*L# z3%2(*RXoH@*IiL7Uqq(`neAD7M-pm{ACk)6HV!UnMNhIF3|FiGz10YZ-^Oa)w4!X3 z?x%|8Ph;pjtMEHXl7b2H7Dl1f@{h{MX~+meh$? zogFG0hKrC0p}{80FtFA`l{oJx8>Ls!%{eA`06AJ<-DCMf6|jo2aQaJj zWX-q(@8Y9un#)JReFIY0DxcoN&HO}d*GTN7H-cfpj|mE)yt&_v+rqhPGQVKryhzp& zK;M(Yk_Gt%m*5+yvSNlao$;i3@0@AzbLueE1tobvto`r?crOjqjAzv{W*JMH zy?2%P)u1iKtuMiJ9+We;wn1=uN%)q4W$AG@QPH@8ZrRs7xeS=*t2N7QT8oXQWp7%q z-aM|M2j$)7QbQZVp}{`B=g;S_5M38%x?{2O7QUR6ri18}KFgL@-=2-#obXp0J6c6> zSFi4$W?W=>`x^bZCw)EL$M?SmsHv-&^1uD(W`vc@SN{q{AGvP)i7*|~fXIJX=0QOb)!HX(APgm z&0}Tc<}n}D)b4%x+8jzSYZc<1H>9B39$qefa5 z3ZGghVE|J=tiO!j9|S&CoMMLM>PTQE-&v(!XqLJcTgswtEhc+7+e=rgiejkFqhKJNF-XRB8yJb*0$m}rcIrv+@dqY%bV0Y)@`xk+`Qa1D>koxUD{8i zg&2K{;(>UCX&APxglw!09)9x~#g#$dW?5EE2(M_1c9Ea@LXcVgcglX5kd9@G(JfR& zz~Z6n9fk8%t>d!aVz*{*PA=lJr(CXGcK4w${DrTx9x{P4H_jy+`7d{);5>;1gR z4P)dmFb7HciFvFSZFV_)BGQ}Qy)@}WF(ajjL;F#@u7}@#(taeqF-G_g>?h_mj14Ku~mHk08G``Rn=8e3L!#($?nLC@5+*<>dFQ z?$OQ-;cb%FI5-^q(&^le`l*ihpZVoJ$wowAQ(cSB@l`N}Cl6amLBaN*TUN2GW7)En zZPaTQH+$Y`si_Egx7jvrqC`xvmd&gHhSfjsBB3d9zKykou$j+k=H$G}op;TdzYf#4 z2d24yLLqr2+Uyr`I&k6|@4Z01yXV!K29EQmYd~qaM@HDHl$34Nrfg-+x4Nih!cSo3 zbHC@)arAc%+Q{YGT%>xYF8}Cj7s6^k3u|*THRNGRZvN&^A}L}F@&dZAS!l)5OmbN9 zGYS}&HJ$^ps)a90yPikIeID}~_CCicrCH?2`SB0i#2H8E*5=ST*Bjz}dosU^uz8&@ zhtcK_{wRwD6qT-R_MP49lpMcyhr_$;x3E&PhV%7e^_lqT@fWw_(XkV)HCGYmPYE>yI(+EEh(Npl=c_&^}m)%bSz zd){Ep^^}T^>UR&DUehUqn^gpUhle6JbAOL<>lZ~ZN`aJT5`A{YfcdQ5x}T1Ah!vr? z$4s=vLxpYy#12d?fX0KBl&F2Nwr<}PD$4a;x=j;B_IvM#I5*;YS=cANp}`y(v^G4i z!juS)2RD<%8mk=dvNy$iR(G;(mEiFMW`_Z)hGKt=q*Wv8TNHdNuRwO}YZOU$ru*fh z*E2{azAC~Ty6;nED{4Hm<2gtlbapSfGf8DBXBbV*vhP`&4hy)UaT+{R_7V11-RTCf z(bOF==}Yi3-=!T;LyTwxRg-fIRZuNa^6D4}+J3k{SOkNfrGGba6uw}P;5+Js*}8Vu zi;F~gzkKUnCPpt=-hpP9L}sB|u3N5+r_NFA*oTPtLQkim?%|`8eH-j6Z*6b5Xp!f- z&?fo|OF9y9REIVW)M)W;yw7EpJ(roec<%32nU!T6wKE zVTVf7p0=cF#yCFRBP)U(C81riM$DQvp3@C`alCI->J!-TrbsGOE;m&l@ctE0uImwD zP3nyC;R^nHv}cW-UFb?x2l|CJZ&W}>>Dg=MRHZI<| zoP&3{k;@4O?G`6_=bJBi=>8NiTisapt5e@8EKHZquCabk5}S(;n_ZQC8O2qwy$C(4 zn!v8w+dAUH(W_RaL2si?Nq=j|p$GS%<-8W24x7F$@pdalp8`pC7>4&sZO=_M^&Jn@ z6q#l1NRjZg_TDx*K*1c>*PBlwbqlWd8#JA&HmBJVvRCFB# z^us;8qppD4SMpCU_~b_^8?@D7GuQu3N*!Yd@@P_qex@l(jql%-Qt?ltruSs2gViis zNHO^8&~23SnHziB$s{YAC(Mv>IulPGNLU(a za$hTs89MG9Ssm42)mi4xL%8k=BMXOF4~i z*d`=~L)Oec8`bhi1FY5C<#Z{bD&4y9A`x9Z7V?x^jvm;`1$k<7ccPW7OhE6(!5^D3 z<4@ct-?B&Pmo^-@(((a;JB9%cyHdQis`z%tPuFylCWkbk#kp{3u;itxRP#6$oTA7c zy&u)3sYo4&Gd7v^dl{ZV6)E3Mg>U(&mLObp5NpYjlbOhz?acGjOyzMt9`)8O_U)TP zufKE%Df#@kiX{FpeTb`i(|zrC4N@n>yhz3h=*o++?ttOU854J%^?}>!58a z)wFdNfDf6M#p&4E$hmpk9}i_Gf7nt)5-_fo;7BTFs@wykBj&9#8zyPP=*9EAV`T5S zw-olSr42p^h-}{Z1Blr~zWslllbM5=>3=wW<0mvB+bJ)Lf!?<`JnK z1;QbPI9`4~6?K(a)`~I46RN5Od6hDV`P0lnij3N>D0XTzV(VfIBh$HDremgljaA^7Xp5ijmB`~^rx zNi$$$)Ee1Xb=^^wj$re!zec%^E~1OE?lNX1*m&t@CzyE|L?&4ArVDbd0ww*^xFN^= zA5k*(c~)O4A!!5PECbCvA(bJpx{1g_%Ne9#pckH~1tK8T5jzXiU`SaL(&Kb34dweQ z@3Jr?$h#FQBPbo^*d(-KaxKDr9O67vJ%h1cw37l|&l3)X%@&|#>cm0I(XkIr8Fh0SCUgmDYDbP>^=7p3D$ zY$hlHnwn`s!fl!b-a;Mny-F`s4ODS31QM%J26HVLw3GCuX=7BoTtVd}`)mX;1$%$eeN zuybR;zLMf*utk6ao(W%1**MMVOLm;Uj_-SJ7fw!s^Lr45z`WPvPh#nE_Sy0D+@k#;NP{$6yR+{fVGAdBUOT_Wo~+r8 z-Ya%HZaYc0`!T2B<-?Se$dtBL;?9I4XxUCRqJG+Nw=Hv ztPO%0xQ&7#XuVN5vR1^>aW)6oLi9Gw4*~W0`h_aS;ekpup^z)BA)s)&Eey8DAzy6z z2I7=oph&DO$M~!lr?jAk(o(ck=84c9azWhJ4noaVUM&fz z4q@u%J4BP0GT8Pq64VKUh56D_Xg>w&2?^qru*U6BNV&FjAv#&{_gun*W2=h%XF`_1 zhh}SIQD%ppb(K+ZVVMrGj{Z)dKs+K2xd=m6j7t5H!;KG~*NUXxQQ1}d`uZi+U(|41Ccz7lfhZ*PvC09% z21F5nALVwGQ`^BIdnE<=5Tir|toOUH&&$L=vZ>YTfBfngVr@Z+D(Domh0KfYiVEP> z&R5%_y4Q@$(|~nF{A>v%lKoO(%`O)O?ghv>p`ZSweYNpvmuM1|P7N=vz;uVSpoJ7H zB80^R0tvAqA7@VD*XgLS(#(+k@)SZB1nLC}SdYa;@MLkl(rA0n#&`R zGmXjRqJR6{5Gd2bk?0FazUyI&vXh|1I?`7O(4xtH8(HecfVRMN&$33;QoPaBWu(xT zN=1~U5ywk34Z|5S;>9|Sfsb;B`~!dz4SnEntk;a~ko%uFVd?o$d1HjPjQvD=6)=dk zo{cb}EI=h>QUzs;S%xz?P(d)kb}*oth;*dmaYfuV6#GOYT5&>sQcQ>v7@;t(&BYi2 zKz^Sxq1*jc?e!Oy*Uy2A6`8WInaaQ_c5;RSy5^cYcGJ;r7vhw;K!3O&jO#Wc77&u4 zl`O8nuc%1$JC}Ik!cAKs;YhKdNXY;e zz8|j~B-ge~1VL_CppcT0u%r`tB19EBqgyDeq(g|Py38q(sUO-u)*CkWRw>D@Uz#Oc z$Mj>~8(gW6nZ>?ii&r@C)oV%}fn;%g4`6C@o|2_l_$88?Y^jgFX<|W8PYJm= z;WJifRVE?CT9S~7XQ_^WP0U%P?m{^$)rTD=g-4<3zD9b;ifHDBIn4Y6qq!CoI63wU!+Q7Mdf z{k+8+i99My37S+s*>I^M8x@sllPzzje*Q3XQ3LRw0hK6{OFce8Sl38FqZu4^vYHC`aa49J zlYng7v}`aIzNsu$J$0^pA@;{lSxI@1@N9JR4CdzckxNXLgbxqHrd>^~@d~-4{cJoH zy8+>*%@BZGbgFRaX0+9LZHMZ9V|CuYX@k0W8jGb`;FN7<)Hb>{jq6wF${DLdObR?p z9uERF@l5VD=3545)uy1PBu%_R#{{d)Hg=aGnjWmFczJay9lfOfx^e8pZYUV9Z(eUxFgi+Ti z>&eEE{8Mhc{SSnvE`fAc*%=yjX<8ky$N4@<@wRW*hy(cOVz134=RCoY8S>`3+?nIy zGPxJGlnJKSCcRS=*|cdOG5|7=8QbL^w{h-4V0FnP{}1El5G0DCbL+Nk+qP}nwr$(C zZQHiJ+qP}@e`arT2XCCJWDQnOmHMzMOA8>oJCzJ^y|E+!y<}`8ndrb68^~EncVyCR z8)+1M=7GeqM_h3two*CvBBk0NUGbOU0rC7V!j~5qzY~Nx=l_Wq3rwlCPd`K<6J#Ot z&b9Q8{fxTCOT0{^2#7CC&632b(|Ke1Y@>1-ui#)eacj>rfRAQIU%~1ac+vLGbNrzS`V`|-3#10{xJGLdv=2=n{#|p zF|M=&pDFMOZECV_3OsUagNF=+O%ezjRH+t)d8EI_vN-?<1e?i=u-#J>2_6#BFf=g; zDn9@Uhfz41+Kl_ZxyV=VsDqoVMzGqvDh)xtz-H)`&o*46#aXRKOsvS0+@#}RoW=V` z3zahG>@~%zt7)Db60r|B{_#E&;Grvs^f&YM1bLk$BJ=^GIcdAs99b(SQpBonUAN3K z31Kqkl+ADIH$Q&Ki-9^B8PasB;$!F`Hcy(?I-a)kK7oyfi{m^4ulKg6`}+x>zKg(u z{VUA*{d{dw(waq#YrYYR8Mk$7XPD5N@jBrMd?&I?^>nOmXxi$=7LUFjcE!V$Pf=ak zuaUZA(pnV6aA!Mq&u1H^VK|1e|F3o1iEXY$%%@A`{I~)n4tkMn&x+|E1oH;s{#{?v zoGCR3i~~jBu$o>#Oo@5%zBB)UwL6-)41|S}LrCW)uH^$ei8I~;Co8*ERL{}G zR7B2uyGFEBABJuc*Y4FBVj|RCc73X+&xrVAAugq~B_lablAm2F)yYcIP-Fk3XQcYH zdL~yc60qPMT7lijxQqkN!LJ+dVQ5Km_BJQk9y&o zx^X2}Q57_4Ca*!R%3BBxTl23=+*z~p@nc!fQ0o4a zGvUa?mEbJ3^FnGNRos^{A9Qu8dVNy*$kJmZD@Y&7Qa^OJE5GL(PIL^d_|ei-ghIN@ z;eGtY(D*4tfCfLp>vN9DOGK8ayN7u_2lK_rEXChY(#TX)fc}{u5_Ph5bCj*s47fEd zEAe2EwkJM8MtD`f6zX4Ux-Ljk=U={8tUe3Lqc%g2WDH;OoE)5KzD<+5f07v7O#^ry z&}-QY2pyh7G_|F}(KHUWAiVF)DFQRy-@Da}TKDajaW(!x<*Xfxj4Kj*Wz#TBj48s| z1?jn|ocKZ90>BQGmXt`~6%?U)f}x!0vFBYVyeDY8Z?AZR0`%rJ)a`k2^&I`D8g1S* zS2}0*`ZJBiNiR|d%EiHz(SBATS6W*n#S_7nFui%K{sNHHMv^RJVVZg&(xCs#EH`ZSOG-b*K+iyOE@oTLft&NXhN+^Lvf=MO=Zh{v&$c!X zAPk=vyXyRNj>G(!CwRQ4569fRoJqCi9RCfu1PQnb*_gfWeGQmCd z3!mVsV|D8xmIW({kS*^ll(yB9lOSR09bpjS+MHb)boiiTr}mOcoIy+vTAEQ{Ns|&r zdb%spX=54`$Ob9H7buHma*bw;K+0~Tt#~wgCWS14$2fX6h0s8oguYpS2`YyxwRxe` zu`@MsM`+rWeHwo+q>d}hw|aY6<+4q+jh^MlXh-sp#j8yTO9K^8e&OTow!9A5N|b5D z&WDVhcU{Q}aUTC-C8uMzemsQ9_{mF`{e9~1QdW=p*hZ}R?!NF&Q+wE?b=3B!4%XVs zwf#za;QROe!~LfVS67y7*(2v+d@yttr>C#a{Z8>8r$^hL6m=E`{9HXYIrZxY@L;`< zMPCqVIYZ121Yc|q->7*|O4_w6cyRSySn<;v^S9==Ubr#CTk6Q_A08XX zuLWC=M}|RqJ|{HE_~U$!@d1Zv0Xp+IiCBTOZZOv_rdAg&cEC6Tg^A)H|4jU zuTJkpT-Wg(`km?967QAXTd}{Fur(55*-+wD#2d3uIu$QO!zTl+9OSj=x%mxX^BVH%6RlR*PVsQ?`Fy@Y$9Oon zHHB*3GBr_-nh|>S2-fH$RjgH*vZ7@xteM~@^mz%X9(gx_5`OJ-O!5-T{wg~ATU|qu zwKeC>>GOFxJfw0y&8rWX4nTjw@osqeXxX6DIaQp6RE5gBEn%p1b*7_R7yB*{%x(N! z{AcBR;mkb!xU~Fpm*ofJpO$W_dF!XD+$WxG&mHU2@y$~yNO02dEC03@ABR0n-OjeA zlz-mcS{_|?Wx>sh{|#bnIlbK|xi99Y;u&Uoee7kglNM^!KZN3u5C5E^OgDfH+){RuGJ9nt`cm;yNJ4ywIx>$bq2+{K_!E( zB3yUNP>#|f0Cx_H_Ehc9my;uWzrk3a##^7N(m!YFVD9W0u<`+)#A#gzx^(5P1AO*iM>)&{W`f3U`8an* zj@%{2HyL7#K&QKL_O+ZhMK#A)x0&kcV4MB&QIA|NotTiGR>=tqAGds!kfSqB}pl>y}^DXsxxcEMBL2D43 zDC3o}>tm&n7m!KP0n`eZyo@&O$6b>ugY5OC?JDlUwCz>E{#0czzwhsrWc87zmr8#I z=dum@$ITz!jyAHVH*L&ub#nT+c=?P9KUmo(2@$<*e@@v+8<++uFrKQUU8iLJx`nP( z)vu4ycyWFn;M#ij6%LNI6)aQpeu6ZLKWoEuZG5@kW!Sp9vgG)ptKbWlNAk|a_T&w3 zPXkbTM_^NzyYBX2NXsLo*(1-B^Ct^kL^Yf>FU}UeH<)!5{$5d}PJP^Ku=qK0Mu+vEYlS zls2YT{@?#>cn&FjVW({^LHNLrV;!zjv&Nl}2ybml;-V)jj!xa_ygs#?$HVFKAylD- ze(y~Ly<#Oo#F(jDvFmlzNcG+gk)u8S2fB=ALGnKVDwh8NP%$wxbN)Yoijk9pndyHK zR7{La49x$Z|Am+rP=#FWr2y!o+gqFMot+)spy9EdT#z>a!5}bqclV&~Zf^(jiIbVk z%$$$k{>xn{)D8Li*Ogv9C?r!>Fhpi>G;VL*NF7$0mmdCT3ub z%uLVU;`99(Xash*b|zp71|SpM>p(gO6eGAey*V|sv^spp-zQ`NTPa|JgM)*jzjRK4 z5x8?3Q!5*I1qQcPkWF6^7S=Y<3eF5IK;7QoaZuL&(&}nrdgSEraPZ>BTAy z-htt%6{G?v7eKCV;8_6wR2T)8HsIfG7BLQ}fUeTi={;~OxYIhku{nW(@PXAS6pOn@ z!$UKBFc&Z%5%7x13E&i*z*m3j>AzY5_;+__0Gb$@Ki4<=t9~5Kjlb#j_2uQkxyAL} z>8V{H13Ob_5DLjD<}R@zB4z z`O)Q#sl}Dh#nGwPdaTS}r>8|TNN;9FaBl8`-fHM={f|g(PGDL7&)d=GyXn?O_xpyo z5BN;&jqJ=n)v(NL;^eU2)MRW0vF|-De4-A1Cg=j{fZ)u`%%8fMR555cwlVsfWXvX%z>eyDY*U5_k*{7zm(Y8-`sJ28`JAqTmT;b@~_46 zzmn_UJIsRLx1a@p-?j9j;qhZ2z-50h-T2Jljprly>Ay3_zx2t!e#*b;>A$Ojzr7fe z9UI#}SGk||p}+ag#@y7}wSHDUvbEV4V`vl{KDWW&zbY%hKRsR0g3Q$Hoj=}GN7mnU zFhuX@_((GYLvs_eKW)1cBFhs<7Uec4*4A%ttKT&1?^gRZwO0XCaP;bay$pD2v(rE4 z*G7{vHvIaC>2ZtSD~R_-;XiEkq21-d>v~h;lYI~jZfXW$HOj^Q8I#vB{~vc!H7e`N9i$!B;+-B!uWG2|8wH5L;TN(x@Y~v--x>J zaK6LJ-@M560-fUQ55#PlT)= z!uY%lj%;kLfPOxPGrE5h8^6?l-h#j0_liju{*gz+nU%pe<0{|cLvz3BBXhU%SpKol z$s7Fl#3c9l?~CA1{O^tyzwqBz3zza7ZT$;B`K$Oy(f<45F4O#-J}h%f(~C$JfAF#J znJfJF#l~!Z1Myxqe}VX;n?J-|`gUaG{BIwLk;&P^<7@x$h0)UDgL41Nw9@$v%>VtX`0KHqp+lP+ z=)d{aSSwS9_g8aJjBEE_J-3cOVE&~0_wgMc4ewuI{%!9cVE)a6pYkDi`CWmte&L__ zX@kqt??w1Ef8GQ9`7!@@{XG+c1!N1T_MUx%DRdiM?CoC-+Mv6^2L8dRxc4WnM>H{O zd7!l)1yY5Wzl$(hdMn8i?^}XPOX!=F*7x2?G3@A#F@t~XtWoIJoqf1V+a~F|?ohJ( zAIqY|2d+}bJ563ZO0L>`gy+|DWM-{;KTfzMb*&Q*JkckhbQ`fS(()xF!6BSx4((rbH z@FfA!wxfx9c7svh_bi2*jQhnmNc48GI&w<%_hv^0R5!HL$wKG}A)$BrL8&>vs5;Z{ zz^ZfVeP$k9g91CGl+J9e0e&pFy^5MQW9Esbn<#E9R1=)5QF<-G4(tM~5G2%Kn_i2n z4!jOatzyLez3`e?c|jnYkC*I=l$r%*e1Y_5#~&Fe@v3lUQ|tC+Zd7Gyge@Rt$_ZJ{ z+kpBTWiPO*AUQ)XHH#((Aq{=HP`(E5rWBWm+r6!`xppQ`- z8liq|KOZv6ovT;7Mtncath;T+Zi;3`5zrDu;BlF)s$Hk^Vl7sJm$;ay;EiWf_+Cd~R5gmx6X)@PZRn^yYQuj%j@rn7O zM?QYm?u`l+)(Es()W|LX z2sD{4@bEwsJFSBF&|%t@Mb|PlR;({!DLV~pd3=1lAB(E=6>}&vw&?;6gas6hCGB49 zzskvQ7^jM-h287fwY>LKMp+PF5EwFh!_U^(jvEZ9(d%adPaOI^<2WJi5xlZ8WZv6C zjr}b4*0Fq;Ac^Fvp(S#ZI$$vn56A5Lc;q`bAhf8K{B}vAf#r9T$%TKE>9LuW!+1-H zr)z`it>JLGIO7T5s!cxLolfhhgEJqHlJWWGRjDo%sbINs*({AIQ4#II5NA!wjR+3d zf-`t@v>lynDQK5yHuzSi+#fsWce0|0S;ejjIo-M{c)uqCVw*2`BZ)Kr^j)*rRb-o6 zQu&AsLOiIEf0vNdZD6G?l0s(N589jtSoJvB$16OH~>wH1xHX#}SYWYKZ>-F=s z?jav{|0E&+Rlmn11rDD))YSK(iJkJ=RQutIH{5(2a)(qP4-P;-4CwjzuH}=$(8s~t$m7jdX`1mTBaIqL553gK?RKk z7n8VTtk-)fAbpI_6*;GbOCm|D2auQLdB!KL@NcHr>j(_`ZfQyue0%clcPs+fjJxNBRFT0n&8e$L{VERbnA}Hll%$IHTLhj7j0-A`I+J>X z0@r?9cwNVxhSm)K+ zn~t2ky5YNVR;+t5c=N~`7aH_hJfvWxvL4~^4FsecIIjGLh%cDV`;5Y=CuF$j;Dnf9 zc6%W{shKG;FOa}^I|Cf$XMcvUtU$38gHiKLuZ?aJ?l44-psjmA#@-&q!(7ehtJ0DD#^pKV5()1s^O+OL26B6vk6S&$x0JLIm_P`SucJ+gF(~%aURTDo6JFUt@`<%%Hr!s4 zsVzt5!;-m)XV3;{A<(IqG9x}-&^c;;0i{dFBa+XL(PHn%(^(=(uI_%MGtm~1nk^)4 zSao$9PG2@v2ahA$G0>FdzJ=ppz&Q^0IiJXNMiZ`-S-&aBg4L|gHo%c08@~z*?whCjugqNvIJ2RdK1e8+8T>b@(#nF#+ZACj+pc7UF zQ}r(GD1X}9On#fSE?lEPSA~=kX)R9)g{HU+l*8ypYd_JRtfV}TOJMi>ESTOfv{iXS zxZcq|{EVVqa@S?IyH$fOJoib<^BACQn+vi`ghoCPo%VG}EwRRTj^5T3OsZz1&suD! zKPt>uczoQ(GL`*aGdB+^Rvunh;WyDbBEq`(XiQ+QB&~ydel&b{q+ll{_O1@@ncwpM z`?3*7vNE@&D zXlZ3RND;p=*dpypIFE}~&q-Z*Vn%Q_hl7z7?Ni}LsU>`NhQ7s12p#r0DBdo$SES6ce=aj74}QIb@@c!-%+IsM!H*(RlrK(J)5bN16l~TOghI)kOH&> z``aU!7gm9YCT#Dd9{GrV!V3L`Wa~7Urvk0`{Q65fwrxLOaY7$>fwT3CZ5t{l*gSky zN33cdZModUCdS;3J-)Ze`H&Y{C0k!0?^JNnK>nfM%J>61G9yosU*yaz;_+zlgfEql z?5zWkOw(sZ{K_sD3L+wLTMNfj$!3^niswa+kL! z6vz|?ba&C;$O7(S%_jj!sg?wWE_*7QE!(V{&TJm8#tWA;wBp;`*()bj z08Q_;48oU%C^TjZ~H-#o} zZ{wb>m2V1PHj8Mh$C2y~zkpyqJ7j|@1K#(Ka~^k+n-;qfINTq3z4$Mfn08E7(6U~d zQv|`0Q>bEBx>v4V$0rU$u>rxWCIC2C;b)?b<`_I>c}{gx_N9?D$!KPfv{N-`6MRM+XVu92c-}8 z&SjpCPU)bs~!tic~BP3&YG zJ-T}XDA#mJ*foBAt`lMObSvliUK$%OI4_g&XeZnD*=STOvgTV5WD?UV00YrF5HQtN zF#h@HIzbF4#x;`qN)qxz8R9$EoIj{}n*@IQLE9%@BlnLkH8Ad^KHv&G{1ct0$jT zZu~}jDQm#Bo>fOP3oLehtlMzECR**FSS_;K;AwJOH}blEgH$XxV`OYo5|DIuizfAr zfR=x%0T}J~zkGxF?*3v|6cNq4dIk1vyU{{9=zvZx=2Hp8$!nbZq}+bJa0`SSzN<5L zTeEaiTE!={iR`luw_4&+oyb*B98N-cPpekcG zF~SF*w8U;s;vF6w3F_|jQef|+0aWT70$Z@Yavz6^Lvv36+48FkDdJy}wp-uxxX6~q z?!&sLB=VHBCJ|t&O-@`wnC4kUbP?zxYVY=uiKkbFUh6+IjGmU(F~V^wzE3BDH66T$1U*LK4&d z#H1CTw_#-->|zcGn{|#I`c0ie16Yd0Bq&#T)-f%QDr~14KClR_Jf0`yFeR*Mo@z`N z31{D#e(L(yv;G|G+0h3#6sC=FL0V*mL3rGkg*Qp5Y0~kwg_ZGcDKgF2U1U*-J>&XT zmrnhd(%xt6Ijx{bWGQIFRIG5PG32s>^DpaWlYGl{Dn=NFrv*awuU-qUh{UrU62AZD z#oy`Qq=U!#xpa4N{fgVYWdfWZc^~X%l-kJXg=Vmbb*D+c9DJ@$xo-GnKl1(J?04zl z$4a1Fo{^94e(NVT8w{%8 zXm$WA$Q{9w{MF!)d}8hSh}sOu^Xg37O)oJQ)IiI#A5$6GAzvpOa|9A5AmNwKAU;jx z^KYkX}T9zh2+W(+e*Gy94bGE55|mm+B zGh&a2amaA7)eq)u#PR^*uE^nk`A`8Mc!DdjJOb{ttqSm|fh1D1!w%LM{~iAJLFq6- zVOd<{VfQ)hCPOoFU#M)4fw4WbJ?ebxb=%)bBro?9o{ji^s#`hq?_s!KoLpK z=SjF_z1CAi8As{b(FABB;&=OG_ie-4$qZG(f@jw(i5{UH$jGGpQ*9WLZ%g(_a9W_)qxel_guU+1Pm6@DFnen(Ak^Fl#)4L3`FQPxUe*P6oU}K}X zaH4owpy*>tzi2+=O^scdyL)%V%YS1E0QTM-Tn3nD>_AEVB^!67WU8+=igJK&Y9FSSMJN zaVF$S9$ci${LKlvK+k47KksOQ0aYm~QvFQ_K$yN9iL1V9!S{1Yf{FHHIZXIxOLNSl z1J>|4hyM?EBie=?jd(uTChxA027>D($GUs=BMg?IpRL(lf1G! zvo|WSOAB*{IUR=`OHP%p0xx+9c}-j5GJ{9$+!?33G{ z*GA#PQaA=Jxf7eWhFV#4`Zt3B?SDiiT7LEq03Pbn=2H)aMw>yiFTXX3V@pghCr-9k zR=x_lW)R|F{(?xM{$~ntPq~(xk|0xr{E2nG{Z7Lmaek@==DASk_4KFa(%tQSOWRvf z+OY$tEo*{WmDm&<{lp5E;jVcS|-Viu}e200#?Sn zt(K+3^(;H9||(OL--cCZ_mdPe;mTriI$`Y1s>%;2TDKL z5?_W@yRzAp!$xj|)Cj9xXF=0cC+cS_G&Y}b#4}UxCrBIiOZKz#m%(MC<_wR!?&Z>8 zVko*a7DC?#nn~la;ut0QXlh8d-|V2actRHIFICsOueilx3kS82pC4uR_fl$uZgx4A z&qhYySUUC}NY<#>xo~-woChBDDl}g9Ye%&DejIL|KZdk_3e{p;;i?yK7UF%aLYxP` z!sl=t5UGr)Kuy26nY~(HkJTT@$J8@Xry*8YC9ck-j@C1t$#IX!QNX%+6#zRx#J@Kw z;K#n1@@O}7{QWt;WPZdHkchE4xudj-DiP=a7bw%PZ z82)ee_?bw(m6I_J@!dLXVJ!MEl-~?x6oaD>em$~UWqVvr7jcah)D96eh>zb;x{yJZ zc#g(uHg!{RdfXU>`SM2$^b0peEs5c7?bfX7y;@}QM5I!$utE2-AKvY@Q zk@R>qk)FRqX<(wHEhX-)9Z?_v402(xxvmBjM*H7%qOug>szCq>>i2I{ukK6({$2%6 z9n>W(knZ^&1^c~p+RkMI6R8pu%<4zthL3yi+y&DYhBhM>-I!N{P@pjPC)>?vKVZ0< z>eJk%_zqN+aQnp$vxTeh9;ZK_{ic^}QZi9k*jZ4ok~5;sQS8PXKrfBhSi9z6hs(=K zT!|s(YUzMuKCI_c=-sw2fVXkXiDQ0uz)(Ne_%4e>+(k~bz0W+u`Y+!z$?q5uSFA=G zZitn@YSr~KTP#ZIUgDJC#Kg_-50$f6A!Z9E(^&KRFhX^veHnYg9j^8VJ974rNw8ja zA)|U8VifgX`oEgo5AW_H88a_WVF5>bqk)*>f7;Thg|4-rB@h*ShyM*uiY-1(#t_U#P3$H~ zw{!@@Z}ceZ52D2WW|qA(Z3 zHMX=E&$&(H;} zSGny`o*`S3EJZXHVty)pP(1HIoHC%Nu5=_>;=DVD*=2(B|Ff{zq&2eSAruH%OWK<1 z&B?Xms~re|OB5bcFSIF&Da08j!1#-Z77IwQG!@PI$>U5o;7Gl+Nf{8~a-KCTBQN(~ zUV2X6Ye~v1KOp%kR64G-G)!yIv9E46wNS2Y7ek@;%mwAmbX{e->p2yY(bworK)~YF zCLeWQyx!X1ZMYOtXgXz^}i@1TE0*eJ6Np_Ox*-u$fe>jMeP%|8Eh(# zNiVSuaI032L~NI5aOX!S=Vftw{Cnp)06gC!%L=}DWBC*!@XOlksTw5jKL-vm#jNRY z!^ssH-iyl?C16s(ve6@s6=y&y;m!f`MtBybXY$Q3>sc? z*k%uxvImA3iFY39ScJHF?|tIa(!e-6N4oa7I1S?vm$vu%J*UgRD0w#8Tuf5_Bb6^pA!liwG_p8sYZ! zZC4Dx)CDhe(wp@4jP4$$ft}O=+ubwWKV)$JFjIUCEQgtj%QO9>vOH+0xK|~Kz@iHG z{sZfbZ@y}3u>*}l2OnjcJ>~Ku-cnQmEELJ``?rXV!r3tt(o&TF7YPX9<24r4@{nDg zkVEUnZ_bgKI?ct6UOUE@5EK|_WWn#GPz{qY4>qSudx3{{Lq>J@thL$)6=vcVT?2|Y z?zoCEhzyP%Efqh(&#JY5`H~TrzK^#!HVqHR%7v*H3ks6h;?{YM)1iWU^wvbjkcCqx zxfjYW6Gy+77&8t2;LXxr>``l6V2MU(yLJ3z5ynpmOzP#c+_x}s9Afm)cY_eCkb^X- zh|r5|AjXyzXQ$Yz=CX3@`3GlL3(BNtMw!78LBL+reonEbF9Tq(J(vd(I^O1fp!k!d z_|`SdXshpqSzP3#3OGo}HkacncVyV!@wyXLPc(wODDAr!32(UC{@G0fu+&e!E)i#@O$ z^~G-L=mdt9WPzd_d5&N=YajRdUzg2j7$F4tjNIzCyAsuyt7DPwUpT`N(^dWUYsv%LK9&^|rdJ#`*X{9jO-uXCvUez)f&ZPZX4^LGxNQRo zbDs$Qz$mm>YUpBH%s`!hIW6+qSGXi^rQZ=j5(Z;ZUrb9 zmiJ#M`vysTfince_Jo~my7vwY_)A_M7ltJdYt7Pn9jw91rg)X#y z(cIwya_QDPiFqj?ThS+oO7W2~u$RUgttavyr)A6Pjil{H2Ac=++TNO6f1k*AXroeH1+XTg_N>Q%Ulfm6>yFnpt~ zQ*EePL*>k}?K{vMxUgtGB!-C-6kvn(nYCK-v~GQ2eRMSOx!~LyL)v%>^hqhVG!@6s zq|=Oo@in8I>rQKl`Qb=4aM_hMEaa89tnfJm)b+)4y42X9nhL`xX1~E?F4E0wF`77t ze`{v3Di|*e7q2N;`)hn_$J8`C%l@`qYSrf~S$>d@Pp%DK@DH2ESj&Z-o)ERj0l7Zl z50duy_F`hd?DFodrSuW{%bz#sQnXOq8lxuR#ZkQ&H$0c`!CX00_Y`m0rlCZyB1+Hc zjV+6yUu2fo#Ni8LA*`g``8bVU{$zKL&2a8cW4`ZmtC0&d#NcpEv+l^NXcNJ7%zyv z-Kdf_u?4tg$wmVgx*_4gmNs+peQth-dz3|68#vnpWg+>*W8`0G2GLeSBXnxn4l7m_ zoq6-(0R9d5XVo28oN@c88|_53@|Nn2@;@6JAGQ6DbBl7+*DAqJna>YYJ<#5DAW<}P zaz)U!0H5eO)V=PtvfdvcXoR}u8YoN5F^+)Ded}w+F`Sw}&`K;t#Ym}2B`Vxm%@(c| z^(4*JS(oanG;J;@O+}nGF=WbU)+ z>;={S=MkS@|CDfMUVpCDOp#2hs{aw&xcIrtUKICiVN})eQK>|!x0R#r&E~49plIQV zcBO>KKPC^Wj=HMPS*X34ox}K{fxYFEvf@@~jn@)H{cIYGs7jyhBw0!@g)Q?W+D+th zF<>nDCNa1U6vEmz(61e&Y%It!Y%i%DvcvKjwk(dhM-0Ff$+B>078vdTVS4Mf0jGmFMhjuzGM~AZky>%76713 zY$p5Kj{SPX_)6iMSxw;a*#Iw`d?kXuIi7K%3J0RhZ6nlr|IE&&-?~R546^`9i#&xi zxfI8pcO5oI_fFpNdJ5}tnN^~(2W;PQH*No-@>9W%Wb1nx8CCTV)mTYGCw+gYzP5Nac;d9IKw6;nK?kcg; z-onaz3-?e9std$<>zx2z>rAT)-qYxECRm3q=2is}n_7r8mw#p{*X9TPHiV3coSl8Z zNQ5&Ho0=&P4IbIA`;sv8EBG)?b~j%=J~px<^szo)EX`BiU@m00!biaLyl&Ud%lU4? z1>0f<9fdit{iV5oI!^}6q?_4_Sa@J~lzPLwS>GlrP8;E~d7eG6KgvCBduKz9u+@WP z;tMu-Ha;qVQaKKZm?gFKp{%8;mv=|Kd?zAkIVz=kZ@Ja&X#Ym=3g(-eBy)Z8$e4uj zY&{Iy78Z&$Ut!GG79>`*S-olZX&=2s!ptVyG}cRt!R?3+ISP-{-jEFl2mcmfZR_C| zDx=r=ykh()Nv{PT!bsaaN}_BaVIDlC7@^xVH&3alIeVSSw<_Qhb*kHGghyEwiwnLZN^^kk?p_?|A^32waiG!E_1) zHCtw7#^Rp^d-{}(AneI!+QtUwd-76^?X>s5?I~e@spN(8r&g#R@(n`Ad`AGiB6 zQKF2zhPtJN-K!>wiXxcHYvuy+Z&U+bJV1cZyB?H4HgV})o1C1HM7Is~HiWZ`|Bhl` zS&!2jG167X8y2RL#S8duE$k|#jl3RlNB&*tK{c!=t4gNGY0Ae5^VmRV-=T9J2?us1 zwIx_Mb>uk<+(96t^lWgmWcvbtvk#h=D@ScvdqKEvwnj)+TO6HU<8;Kv$d|)%mE(B7 zxj&xrD;cSB;Q4nQh8c7(@V%=0)Q5--akno2wvj{xCrwQ3hBBFAb zuU)vyU-IeJ0%JQ3{&bPaW~g#2vm{%ea{dzJx_UYF$QC~<#XX)h3z6y2*Y85W%sL1J zS}k^aHtRF=5&GY`TfS5a6F2rSwQiSVhqHy4&*N;vO(9#vu`?bj?_NeLZ%>*iOhpUs z{ETEFIvu!$123~I%RuqmsNIZWGnC7t0s#nI1qH-%A^h3}HNGEUQ!18cRt9*cOFh5+ z7@XZDKW0uMU!$Y-;#EV{BxGg-B-0q1ll% zm3{Dpj(XTopInUsER7Ig1`TlEJt}0>r(oYOjhSb=ea@bnk7DFN1jobT&eAqS=^=!J z!0KzlGvvW||0I}u%W9_IoZ!)RX)1GYxlGGfQT#aLAW(QUQwWJMgH;HRA0nJ-e=3y~ zKyXJ)2Kq5E<^xJ}06^B0Ioe@*Y_3uE| z1vz^AkEssOnTI|K6&Qppr#f^V^k`N_9Go|d(?^bl5Jm4)Go81Nl=OQOF<}>`JG>Wq ziF%|vox!bGVdgqIb7bk5u%0Us$EFOh=}jHNLt*7ToMzX^$DADbV_r%$8&hi~Id02K zbYoXR(UN^dt9a@+pq$~g6G1q|mwM#;qToH(Flc_B?@YIcacJ*lgVcR9^lJXr%IP|>*t!u-kx1FML_#&p}G)(bARk!LbpWM@X zMat{zySp;PhCH%7T*(PZu39gS5-f&bsN+U|xW?nPNziBeWb_-~cb(d)9}cvAb03qs zDM*L&Fk#M>}Xa+*&ln_uE3bb8Pj}@_5o>yiP7R zMuo5tC-6yDtg{QzSce{p!XZhWAKW{+9~XC1kzshpH!cMK5T~0abjKV0`S@tkw682+ zScf@sM_zK&!jg1`_fR@^GgVD{ifZh9w?yK2vN{}&eBu*9XL;GkHZXK@EcA#Y1ssOH zl@Mf@hCvJ*7=lg?$X5lRZe6N3eYB7WsU=i74__mrtN5Y@_0x+!7a}@ow_Q1HKiq_W zFUi7*L8Tg3l3|JW;#&UpJRX+lI89**8vjZWzMfh1O0}#0X6b*!zO<%Q)ME_dn6FWT zrMnW*9(r-44{cCy_v=vP{NGVkA)hn88FJ^GL_{?;6<6$H>b=vq{L_)Em(&>$xqCBH zN%G&WwsK?9)#QqtU-W4D!CWbsRz}L?ft~zS*G;J(YIVrQmhsN-~Oq6d{8sYofk(^&yZ*>&v@_h z;1W(ZEE~{+9_uWZP-qXNdF(nImSIbq&sI{F9kc^Zj2m?M7i;0JV>G$`KAghT{1cB8 zdzu<{l$kqY-l3DH3?iMJ&lg|AHDQZ?CVpj`%U_cNVXE_DbutM8z>Pd_5t6^XzGr}t zK*b#Z!H}y)DT{Iq`1sL|=MVgfRf3+Z3#eF`+5&7RE9_S!P^<$X;yIqzK%82MBQ;lJ zj1I=F1i9r^=PB=?17xzf>)yH6c2c+QXWaGjUsZ7tkl@Lk(_#KmTC)i6nP$veCQtI) zmL^mI8u_s;!|6jU@Ptc8$s9T}xQ<6TdXA90wpvXjE{n)CShEVTC|E)Eq8o3_bkJRc zBCtP8*F8vb-$&NF`RU^3yy9w}1+%ONXzJ{!9@~bmSu|!xbY(94+%_v&XQx8T(*n6% zYX+nl8OF`v&A7`r5MVB&JAws_)DrSM*oy(7*Z8C`(W&LFkfTzW__t&~F3xh4x0xu6 zc94NlZ^Jv09$cx(@~-aRyTD{S&Q7zKO`?efmZSd!f(+|DeXGI{GLs`AkcB#RtEf1M z5CoK~O2e7YWy`%b1f-oRm}5e@*38o0ItTh1=<6Vvt$v+hg7b=ylYRmfO+rG|-^Cd} zYV86uePgx(N>gujxFij1Fi?XSBwO6?ycJ44F=gr9g$(GY3D59 zvEzvXqJc4y5b$*%n=vHQ5Yq7NC|^I2vJ{UBH*5f-%_w$tucuz_n76mm-aIrESVI4d zlPdLT88ldaCKf|~almYrDELi=>=8ZEP|&sX33?*ITZ4|1sdhwC24JuLnvsEd1xZ(M z4_6ptS^zudfNxpYagP3X$v&D8riZc*(2E4Bx!EC;4^^7F6l)3{8-5d>Jk+Mqm#f(a zR+~${iKHFB>IL`KCGtAI3ChN<*?b_F@239@8FO*>4}Pc0F_BS5`Iw>~U5zWxbH1DM zLVN2#TCys4AJoSO$C|@s(0 zL9pFziSfs2Qq|#;tyFlDmRfN|p-<;1ny<~-f?*`Kk<(5K)!@8f{^6jGn5~9J_uyez zsfrusl(v>FTXS)8Ycgd_uN9U~kY>;Ea6RvXL`S*h(1> z3`ZvGRZ4uF#qDR!9%tt?<$br=KV-4GnTxmX@qLw)O z$BfNMZ+sAjqf6?BN+B_$LOJq=vMQ07VhF^wGj3zp?co$Wh%4Fl{+|(&7TcxG7Th+b z+cp%Pj^s;J|CYxNCmYc(K7WJJk=N1yHAavXNBN4A8J*k1? zDUAFe7nx&D5x<`xkui_UxOLS?9rQ{@n&r?^<9boD;y8R6kE}7go<^@3lYW~D1p?-~ zdvEy}rNz2i+@1oD2}$`qVh^hQ-HE&Gtn&79%F>U!lc=J;1?C|cfll4X`4qqgAUr5L zF(N2PFg0#mXH+0wdmc`AE|q9et?rs;yP-C#z)LIY}_zL)Tz>--|c&zHfb{8EIYW#k{Uzi6~t> z6jOm2hS(>z(J7kQ1Cs^T5y*qq$!u}SW)%D>co@y{-bCTsskilPP02Cq(m%ApoqRNr zgNmX;?6M#AdZ+vJ5}3B8OT1jXycc_`BkCU2{rS)}(NFG-Ij!jydjR8WBgY0%$qDQo z*T8yS+FU45D?9^Br82Sx+Nze3g0a={_(kiW+*PPGodl_et55C$<+Tv<3}Sz%z-aK` z!)b7DHR=I2&hwg~oaf3LAR4pVkO(i-W!8h9ni~@=5T3!eUa7PKYwR!0iZ<_p+JiPy zFc^}DtdFP7p4R(oiS=@Vy|S=673we+6+$6=#-YKjB=XfXCB%&n50iQB+`i55Yb(th>~NuhgjEJZ zf^mzBRNT=TyC}-;W>WTwqo9N*+=6-IJqKhO%lgMYO;%>_AmoLx(moUlO*T0CGA8Sx zVFyo?NlOR+MEOE&AFMo9wbFlbZ=I4nG>s4Qj|BW2=W>>2S~+o?^5JO;j)w)#la4pk z+JQu^Ddb4xAbKKEd$>S80ypbKQKJK(BGuy!JxrrB2~zeMnM*+0S>2zPNRVvn7@TN& z_1wyfX~iphhBKENESoW>wix$feWaJsl8IWm;jvJW^v5A9@i=O5L+sKCWUTL^h=0JBtwFWE;?EO)NVoKJ zF4pUK6H=~I+p;)T0)+Mc84~vKSbYIIZzv5V)p|KLsqRz+(bc2&xa*c)w;&vHyg@8C z5fYVma=BtY<{pEd_=s}W$tszhl~*S@20>yA4!Uxt_ddVfk-Om_q~15Vx_VcsmV+|l ze`ufgtFUJez7QpeAZUgow{tZv)WGe%^jjSKl3{}+m`-t>Hn2EILql|$nDXZJ)Vi|Q zXvtYkc~y~8;W%Bd6Y$?##Ndc>a8`dIivlM*+*?<+oqf6KVP9d6&mG=(Um=0-!C0Se z5#XBt(}U-|j%iT<;@p{IP>Lfi4*fQ&6pj}nc3S5rkOIy`K}$`_oomao|De(-YhPKP z`zE;-q!>5Msfga`e0aV$6$uzkoUVZm#I5PC+lqzBH)qIN-(Yy zUOfCNu^pzYcQPHf+t+?-J~O8Ci5~fuMMfEJyBXG@y6Yjf`|V-OVz9_yDR^DG=f=y* zyojjy#=m(?B;Dr;z6~eEgNjhl;ei1hNa#DgA+g_123kDZ`#EW4;|)&~P*WM*@%If* zWI))ZA+9LzW~XmfMOIJo1ZgwR`vbsmHWu6!`;rlh^``ShRa{Cht{y^EanB2;$77Pf zlrR9E7mrua9IChn6lS-T+>h;HQ^xHvPgln?Vqaa^=Gk_7)=_7?^lSs0X&iwtCNKa| z*(>J635Kwbq}JguR?xv4?XH;Yoc>nok0hQGZ@`)t&v^x)?JWEWBEZNbC z{gEoCnx)8n)>4=doOy~<_Ru2ty=lh}zI_dz#fa-x1dpT33uF82?3{L`0ZY)L za3qgmFPCj!W@}rxU^XX!df+sYm|xQv zh`6Md%Sk-;$RQzEB^casyq3-IYUVjOi@>f<`IvdX{xB1{G0eWghYNGcG@ch#o(slP zEDuGIsS;w16J|Jff?{AklptULGO4*r61v3c@mBz83#FFE}|V+;IFrFI=&sNhY#2gYmEsRK9zahQs*u>w1pi2;5M`r%)r|_l_x;*LFmNRg@68R7r|xCD@K?&YnI@OH;i8v> zrJ>EL;9;8u(@~pBYQg+L0eoYDEo`IQ7cK;JCZ$i|vw7MYF+|(h8{H+A4X~f&kzu?2 zpjO%l#WXXZ0R*gC21eVbJ(^7mEgNi`Z=8`}$8hqgQ`mJIX2oZtWGp1$SWK~NTVE$m zV|!WK_OS%AE)@_g_BM?TYORsM(c#N5$Qzx9Tqy!n{9&nJO9|rRj-9a`>+)JJQ1sB1 z#P7@sSMp(RVB?@IO@gWS#H8-aln@2+LAI# z3RsO}(@<6>6U_d6PW4hDRbV^?Yz!YerXGq~fS*KCaQSIuV&q`r`La50hwVr%!L^+J zrtXCW-f;6YhTvgp`YN+t#toV>_QaxaGNzxQ<68#Id1ylH(_`3XXyE(*$~udv_PY7O z$YsPY`F}4=no-?HzPk*?sjN4Zvmel4VblLS7{7v>Eow&thgx>Zm znH|?H$NWK?tiM~JJ5rRUfjhNni#v0`WiEUOso3h$sNmzS9D~-=t@GqlAa-Oml8e?x zfi$Gw_9iEf$=x}Mb62KJFRZ;8<0eWD+*)ua;$oN14O7ZrcLC&FHcdn z>bG*vrbFT{(p&oXS{A^D^nqG==itGA6jSC7Md2Kp*}MqL^de_nR3$$9SE=r4pe!4$ zdjYzDZ(L2oBxM!OkTPKcw%o^X5&RRR3?52YCyfYW5;htC?FvgCvMyQK(ip`g+iuNa zvB@5U*32a|+jFC%SG0UOPbjwJ8?R|mG4UJcLf&?7R!T+w9)wtd+69AWgh_9=9GAR} zY(9$H&95D`id#Y^LZ`~yJYZ7d#F_{wE7a_nz7hvr7?-{0Ya+wr0W^@ua69%>utUn< z6no&yfo=x_Ohn$w{XwH=rHGH*CLmwRMPM+@Tk&q-=t!$7aH?F8RXr%4ccJ+yqr(c#aMnxV2hKb(*CPTgz#HTtXEKO7=u zMf{!X5WuAVzU1ghA*gj9C)}D}8X>|u^dOAynu#V&0jycgooq|0*KyrfVrZYtQt)t> z3MPG-(5pItkwn(nAG5KR~AvzfYQet>u0&zfAbu^2{f%btw9{Cmukts zp|S*Fi;7!-8Ue}aL+vnl(o#B>Ad@b$!)I%pjya_6x{;Xh|7u>re z^}MB?jlGP1Tmo`?uV3!fA(Dkb8cwD_XL8?dI09ts&=FsC%&3|3ujMDcsy&!FG>Ub5r9#qH}CB^JdLGXj=PnV3zvfupKbZO>0iCj*stfRNItR=K9&nfAi zD)n^EemSy^n9p6b`Wj8iLqveU^6xHYjOb}iA3VN!Py#2{EsnlBZ6o@NzeD;>wb-)J_-yQ7OlUG8*uv${ zfUWCx^a>-}WW2|qo^Nm^bx5}wR^ljNAF~CRNLEyuEYGQqCVOzZauqsKmaZ3lMv0wf z_AS%$b_cO~)_vTzeS_O*VG0khR7h&T&c5>zSqp&Oxq&qJ(mjjRScGj94_jT@PoTOn z9t|=Ie)52xGBj>kCXpQQDY!@jd*8Zsll%O3ZRA98U#dP&$RiGuoK)zb;uw-tdnaJ# zH7%Ygm99&aJ6|N`?o2&?JrVR)$&mZ!tAJG2H)hj&+m1{F4gx_2vibfnehN-d5B^H1 zL@t6}%&OF5@E}#0pe<5QFqSICJoJ&bh6xuoMGbzTaTFFI9#o`!I8k?gR$TcQt9$}) zJ{vbWO>+TgybT5u);eGBcHBo-dQ6ST&3^I5#S(AgM@MeVtt50YbXZW&-b`j>-U)}p z$O47roVH6i_Ck9M;=IRSy;kQ*6_yr7cR=Gek@|?=$EtlRP+rE<{83Zp)!tI|oVgMg!AGrp zfN3S893K5ShS9k4Nhiz5i+Lu#dB%?0~R!+!WJ(149aN=lttP}KeYULPXP z^7ceHq_!md7ZU0^jLVNsNuD)k)O1pOW-L^Zq8|9O zAdpmkr21!n()!E1h)3)Eb@?NUuN&f!ma&vavupMhNHQC&%C3CA)nnDa1k)r;q&Gn3 z*`CMVTrJW`_cZqmH)(2FN+iusmT1%Ua1m9hyMSNig1a-hF@h&hLWQj|K7|WHimM@| zRcyvY0LaH|6KBaNa0~T#Ph6PlAWh#NggG1vFFz9Ag!mH~9GUzFrNz5_P-oJ7BXSFc z$h{QVvJ+#15!c{My%P>gIwhU3>Mea`iUbQOj~bsr{*`yBa6;~={dm^md=T1c@GB%g z(moSGosmUZu5^V?Vf>qX11sQ8qGbWpgta|&##<0<+J3I80k*3ik07hcnjf(_kB2B<5$*&KJ8nk zp^*baa^F^ukEANCtPhvuN|%{|=r!jryKezR$0r2XZO7f9v?CyK{a_9bdHVPBB*7T9 zyv%42Cy)X+g%Rkq??Lb0Osf1OWsgk7VCzqR;mMT(j*+IL_d*HWn&kL*$xxSJFPR{S zL&23XOi*|&6$#+1pMOsnM3xL)I}XhO+y1SX-M^e8?y-8n_sPQ{hv!Jb#jVh4m6%v* z8fR^EJL43z8N{Ls7#o*ox}SHUt&0sRmev*9JFFQ0c!(*85RTA+F_8?M@{G4>mTzv< zts|O)v%#I?bvGY_QgY?EJPF22FMKddvosmpZY*CE1e{Qt;T;-Ovn#Q{eco3A`1Lii%#=X1=$9m5D{=-E}c=;{kE94-$}1t?JsQ*9aS&S#@y;j=ZU}Yhdzw`doaFuEts) zXrAkF_IXE#uFkUe{!xMvWo?-(kxl;5uxdE1SyAZ&S@PP<0tX>_BGh$J$Ba^o0>%b@ zac^gg_qQhrl7IvginQ7mD5=kRu6gY*;)e{xF5PV#!}fHJ|6)nZTefUE&Dq;|a*A{h zbt~$-Tn(dPoiQO?VZ!I1VYv@uG@3_|+%V4(@h1-%BYyb{kDN)EwI*#aRA(n8_nV~; zP#I~l;bX-x24z+1mXQXNmR5yKWLaG;xCE+j-4Kk$2J)soBXg~d&Z{ih)UR<52^(`J z%y^yhUQBg^zCIUkS4tAgPy7$ZITH7MQ|4TLfxZfDYyJ17vlZsKirI*|?X0Iw>c#2x zc1u7@TlR8^-X_&`475>}fcL}4&tL$FvWw~n?uiphu&;$(MF2a#cdH}F<^dKugf1ogL#k58 z=wOCnK!$7KGg>bz#i^D+0^v(Ss%*AKbpVf1&5b)!2HcokedpnkEF1}a5Py3lLmm>k z$KUgA;wN3m8jZ_fDI~3}0cN}r1IApS5K)30McYDWDpx9xB&1HmTrcldZM6RWL29jM z<T2GX^gezwk~I$(qT|nsm3Q8#$xlKJo7Uk7)_bm)NKz4GNyam+1~kj; zWFuDF>t2V*4z6>!;Yt~*1~f*nBAeuMpALU-)Gn?+Z3Y}<>*vq1m$03#d!ct$UA@3i zbz<>|vb&}4?*br5Nk)nl_d$5E?N;-$ECu#+JSp1}kFK3eu}y71HCm!jRi$XF2KN{< zMc9fUot``Q2^TFM>7mtnsPU|a59yzeG2+`xMU?Mm{T9&Jx!F%FRi>L+1*`w?!WFQJ z3aM!oSnI5KyzKr3$Q+J!AzS+T?HSse^B!J_`xFxjVTn6jBzAtnuexdPug^^~Da#Yl zQTbo!C6?q$u&LHXzq3MQrerh4D=dJ_~3Uu#^QlxDze@$dwC*AxSAb`Ss2`xFBjGfLVj` z{gH^uD7Ehgu7||cMyxuFYM0sZU=d0(itAy6^mFSv5aG2m!2{2igOWxySYP4;!azA* z;;&dsUY-c9%@>bk{OA6iOLV>)=YuAMCd`M5Nzo21DOiee?$`>;OQE^65|(H>S(7%R zk>WA(_T?q*Cnsl2BxM4juJZm!UHRzn_t89cC@`_@2_3W%uAR{l#;1P%L9Q6w7l;oO z#-ub*dc2#RV@aBqu zWEYIXZfbD_lJYmSt2zm2gvm&fmH$Tgy58}MsR@e zQ%?XS$PKrkfT4+K96IV$NBV{xQMkI&gdk_H%8s;cexr9v@*t?Wy3O`7Z3*HD6;HF@ zaP4yaIAq4jM}u~=qURwc+rNY;^%$)P?YodYkM@ENgW^IAan+n@DJ$?(AK0QXA8-J0 z{xcjbqh2St)rhikr>nvyRddoPvHc_*xJcEdIaFgHecEQR_@+mPANXZFFdvs7%-2W| z&${#=EY5U36Rdonag4D%cNFgdjSW^2`C=P{d3&oMU9Dwh|3q1~VdtJfK=~vUhyKj! zcE~j!(QujP&5Js)gkUo^g%A@!%j(kp*5B)ZWf3(h3E@BpC}UT2UCfDSiq;**N|dE2 zl2=|&ixZ(KREvUCOlE0ZkUnWsZaHTO7P%v?9nn|36P&4wWq>2Z{|=Chml?h74#xF- zO2<@b7<@4e+DryE140e-q_Avb39Gc#q_~dCKMqE}*Mu7`jnj!Z3=9H|x}&eu-!j@y zaOtV5CWsC9)4BY#9u>rgO!snZt;PplOmi@l-q`M_)5^`c^~#!$`(U=DYO7dsnbR3s zCFI5%5uzVEqW}4sH~s-mhv)XYZqbfuk!8-iZq(#`=1i-AvIx4*oV#3Qt3b^QO8Bps zCg*>_G?}@WnEw~1$->0R{ogQ6W>ywv=Kmw68T$sVl(W4Q01G-81Wx&WaC>`;5(NGR z41v9~iyze8Z9~Kb<{o}!a@TK21Y%h82w(*2@J-ypcixkV&;<(mf5?Y@Hks; zXW?O1X%GS~!Ld_4WtCFH>)s?R{-q9B3=QU3(yuXs63E=3Y-#K3&f8;4k-w@ z&??@^eY(i#*vjJQ0s)iM27egO$7;=36v*snR=Z#ppl!QCuy z15?wNduM;|uND;8&+_KN#>Uvz8qzfcNH(yXu!hh8rL?Dnq^6`-0P)5NqNpe>WHzV2 zsHJE^YT;t`V16Yo2${G5DBM2HSGNG9sdqwcF=#R9#8o^_=8x8yZ8rG;g$c*nnovVA z=n&yYWbk$1nEmJN*z?<=PLQqM0KqS@D$>Ttb3BV&4j^-7fQXl}Am)9a!=5C>-^@Zt z5(tu&m6ekg5y%P>s0$~X^&44ebr$2jJn0niK^NYDppByqXt|vwz<@~gU>hA>m^?D_G%{m36WFcjPfgvAXE3wTz@D8ygG9~D&o_o)4;9kLUoC3Ao4Kbq$F-~N4< zeV&;S2*BxU{71gz3>hgoWpSa@oBQOidQ{X_2M8b51{W|~4IUj328IO(5GEw+z+eA$ zWfrjST*xQEJ{BWYT%dWw%f6dse-*c%ZpeyX0wkP)-?~z(r?pNYpclc|y5X5Yz}M^8 z<8SBgugk~p`l)}-@*)W&sfF}BtQcIM`9QZOEwaGek;c|vjHhoSycy53E^ zXER7(pbQ5>fuLOmC!@vLZ?QKni|KT`?#N*+`>z`CzQu+Au}na$t=tb5hX#T|Kw)oh zAuh<1u4JQ=0}!9qUfB}Z#Xn0{&{7>i#KH=yK?{(JgTf82g^;}=FB+2MbLW}dR3 zU;n9RhVK{~um2MhKmUnC!}qq(xZk1nUWb2}eWi#0e{*UG-Z$#{iTZo~6`Ol~v(GRr z2>DXW{WI*p*CSLwyMSfvxHp!;xKPBsnF^=QPz-7=DkZM)>BTm&bG!#~N@dOP9a zZH3zyps?6D-%o|qw+pefa=5r*a6e~3D1E2y*W6Xl${$u{&toDHj+GQ2@I5)R5DkI> zU`|@qqtv2jX5J(ayLX@v2*y&T@bxI(;2NK9Tp7|7hlAOc9TDOaZ%^%-;X#+-eI1M> zvu1$JC|!RgyuZJvZvB?gFT`(67^{W;b~ib}75ntWOaD!bev6yY^w+iKuFz(hKAdSX9osKk~Z*F^|__InO( zS(4ocMdzMg+6KMLK+9ea!C0$-ur&4PW)wHb)tJw&L9E=Tv=7I3boU!(n;ZTi_p2I` z21Qn(hjyLZt<1%SI+-YP%o_B^?ibx!{hxPH6GK-B)vHAvq|(w ziVQ?o*+i7WwZF%4vNy=f{$5n+LHKN6(ok~2-a`f)?=beBPm2b1?NRxSRwg*R%;uRi zeC&yu)^(I9h7jSvJ=mp!*rGATR}DrW?RhqN&^H|1|HFstEFe*Wt-3~<0G=N^CEE31 zigV)MIVAGct?vTQK-~Tk8L4!!gicc@9+s{DZ{Y{1%F6tU#dlPlC1wy3gk=g=Qv6~R zuA|g=&!yegg-yCtDs^hI*76uP)IF+)ARE7+3&$!^(W@Ns8?_#Iu`&!^w^Xc~%%(l7 zRz{I#shGbR|3`LYyse^W!5h+9W%L>KBA^hT?ugN{^(_s4#Ed21KG?GfL;hZNzUfMz z6Cmpl&X#DVdyHzX>p&z}D2Smp$QJiiv?my*sXFk;o`P!FU`sa^p%R38PsXahBYK&% zuYB%5mP+2-StPv5(`9}gM5IakR4zfkNQDG7Xp?4gke|Bl48(*{CwB{Fo4i>Zw8M69 z(coV~ZR$|Jx74*oaKlcE8F9#quB;7!lCPT}%U|(B4QJ1ZF)f_1h%Y9?A-3lQ zrDZjrK`wqfq>EDXJ`VCzVoA}+s72C;)AI>scaFeRGs_pEM8RVW2%-#mQ9)bGeBd>VGZ(!Pn74ttv+ffxsBr8efz!Svcv z+=YTQoz`x0qeRt{kZ#WE7@_?ec3V^TL3!$Ja7R8Rd+k^fyPFrgkV5@|gy=^8{qz>jzg_Y0atMB3;C=OYBM}5G&Ipo8P0?7!>hPg72|eyz%T2PBvah zSbZx|&uYM-u|>mJj_}%wCvduaFu=$0V&!I3NP(?ItsGiD8+V zs3qw6^C+vp+?|#NPbPABaTl)=1r@8cw=-;Y9M!TZ${__^xn#nCAnc_-cZdu&_BZ}r z9YtdBe2{7Yw@vG&loo{$)#H6Ztr9}yA3f6J-_C%2q&3G@MMm#u0pP)#8{ATSWC6%Nrv^nJ8nzP@}a7&QF?dc-Fx4U8s-uER@WF zU^iuAeM|{e3ihco8kRwenR!d;9_R&668XD+tA|063er>Pxy)d@&>xa1^|QjtQ#7h~ z`F2C1MW4>aUtJ12cJ1@2q4thM=h38W;6O(^J=WEbLonRMgZrD^ZHgM>E=kEF!s1*6#km8`dO%D!TB;xuRlQzG? z)Z3y(VvXRp&#I$%-af%UY7x4CdlR_`Ki=%o&oeb@l)Stzy+=1}CnlyV~J#tTaizzd#E!P7(;Q{@ST0@cJ ztS-+u=4|DjXF(&ORkt1{;teDe#@1y@4+kI5PT5g5@sds4e%v&_sSQBnK;D4dQ$y-!??2wJKd7mrKrj*!+aKe-D*hya%RnH{3>a!57$TR z-v>IlC$vVP7F|Gqhe{418txa}({2D55m z+W5>)6lA!$@$BpKpVwhmB*#kI(dzl}gY#87au_n=#>quPEo!hkPYvHL@fCZ(ZgV|n zc*%mdnGbafTTHbXavbdqwEida9KkFbx^w=uJZ_Bsy<1N*?KY^`bjX3Yv#b#{HGVma zf6z-`x~1fXf*>BJ5n6<(k4IWbN+y1<6Fw=JK#6h=mT7EYL)$!ZThJ7DSpg^t!~30z zzu0LcbF{JAzDg;%{!lTbJ30CloOIg!`H)-Bbw~TF4I_2BJqcs;@C5`iv>G_raP0z~ z6xIoOTzriAjby6TRKmm_dRCC9px@+r?qAt9Jjm&U?}!E>emG2c4A)m@1HG$EfOAq$ zvdW36kYsKvPAcx3JeV@?jF#uAv@H8p!!_J=_Z@ zp7EJjkqk{y%UgIQ`WZXKur}#R|DJ|vl=5Mo(Um|gobsF=Zb?|U&x0hMQoLwS7r|g- zO+(oZ)c%t#%5EIm+yj2T6k&1BFAH+?_()tu<=Z855oGHV$RrTlsxnaryoN&3LVVLv z2#HuKU*|H|dT{nNHK5bNs2)YLXN#Jzdu6294Tb>qy7epr;c^<(d%_4UTC1Q@#4mtG_-+F|=#*n3Kpt zT$mRWBFd5L><{{uwI6gn?=b4~X}DD#P}!e#Jp>ynQFw3f5G85e|CBtbspLKpBrL2! zbm&6*MmD*|ah$8}>(`ydY-EL=aKGAC#6PS+>E*i-{kw%Yi-k*-&Yn{=n0Kq^&)85b zk8T#FS9P7*S^mkJK`7%`%@4WFZ?R-=7Z`S(vRP++Qp7G770Ygt@SmTKm4tQM^A18! zuHnxEtP0s#cMZ>q9{Z~3n$YS=IpNfR$kNQew0w&nKfAI7CV!U^1fV&_?4duvwtq{ z##74Tgrd3f2xB>viEPIqJyd*e)y5#Ht`?5G$DQj+NQjHWZ5FnJhfYgiDef?!QqPyf ze1~aiw;Ls<%j=4ulH3jNxRh`8kY9nByj?tC{M%(tK=S2B=}#|G3ic~IA>&E%x6I1q9>U{FHjK(Ulk?K03=V>;4L1P$IdI*ZbZ9L38xSjHpiF zT(9T~dgQ$xl2qoNDW_XXXVMA9h++k$9JeQB6$ClMwdW5x|jQ;#c=GcU-) zQo|p%n5DtVv#Ua&;AdnSY?BJyMOye1zpnJ6xig zJB$d+vzs^;n)RBDrvcX^lR!N)Z57_vmH%iI5#ZxRRfadfp}8A^reMc0FsRLzS&Ldu zT<;NYucp@%yU#6jYJN=-M0k_3+>R<5_r&|P?9VZQdnZg0Bp%DOwMgGZXX%Z~SN0~= zcZ(T0pIAs^)LanEEm?JD};z%}<3A;3zeW3RyEF%Y*0tg~X&S1E2u<~(1g#%t)|L=lav1-Gk<#=0Lj z7Q?RJ6zP1sKdM2np!!G~4U2csQkL$lw}Oj)qlmrcZvu)<#8HUhntDzU%getlt0*|X zE~SjlK5FPUS3u@n1;D&>y>dCouNwAIvwLr{b%l@L&j9E_e4fz2IMxK{W&rjk^Usqy&q~c$eExByG1OMq#W=VwK-C1wMoSeXd zv<%C%JtW0{JzbX(w;{*zi2bfGWcT=6?U^f3D_~&y@8bH5<1DFTg73kk3rDT6t?xR| zBbZdM{$SbzUc7VSq+>|(YGHsk!Cf-4WPInPFPM~WQyY?)7OsJSJ8BzKb3s8F1Ef0(oO8O zVA;nW`#rtEmJMa5|BWmY(7!Hm2U(KDODjtl2hCT6(ANMuVfW_Mtp+3$e=MN=1SyZk z4mTb6?&Fs?WjKgZ500~nYss~9x8W$}&r^F>E~(xP{=0o7*TZo&zJ!hPj*huW2bG{? zfV(J=*3H9Mn~$ulHg`>El-a&;m97C#@C+d=*nSopi`Ty%AA3Z1W?Ztce;_9bUo+XU z@c-58cqZG~((PkumZfX>DdJGsxK5CZHk+EHVuXxFERCMQAjT>&Dt7AzIbH||i=pkF z9bNr>9ESSy;sz4ich6&th?DeX_iB7PLl5QbH~fi&pLh$wy&>2^z$g&bH{e53*?U}_ z`?-^N435*{0XWJci#qK*p0-S(MX4bUnjbIHAgtI9+dOs$U-tl=`PRs#y&>qjGh6%j z&^=h2XP6&Bqq z)yb|(@|wD1k8)o&Gf_QV3I0nS;)wUBMZuSCM*5Mk6MFOIu2rWcBf^p#$m>%^Ik#v zB>4BLiCH}xrV$4L)TPFqinIDg5%b{IpfomFFM_Q!NCDL8 zOeXF2XbZ&nbph?@Cy4i%<#aB(yZa`G#^NT|sF?Y?Z z1vyhN!KYQ&@HKI5>$Q#5TWoAsKixm)U+R*otA!Y4X;#ll!AkrI{_M?=rI^L~h1!{7 zvJp&7s+Npl8CFFR^7oO%mL)&KDu0$7#9Xa zj#UWUb6N1bL7DD2gat|(L={#V+GnCGz_rB@+Zgwhxv@#Bi-m_(UQcy~8D2ar>1nri zp4k|q6Aaz);U-1H?v~oGSmGju;Zw!KxvNlJ7r*TFH_Yng%5m>4cV&zZvfnh=NK=awcK`fr3w@i zCDVajqPjhCRqv=S;#BemHa`mc)Jx0|HodKeO$2KW!SY*(+#LiRfio!6X)kSg_6oV) zyxvfM=X0xX16bynvp>^c7?;+u(zu(rkHlD_jl4)ArK*GIMnG$C-9LRRv&ozDXVc<= zvJ;C@Z;8TSrAJ0GRqjfFPI@&;HH%?SJx2qAK))W}^O|ML)CL+9SL78If{0>{2TU7r zBSvq(cA~;LcEGWrf^*QL|BPmzjMF97F?1^kJF-=u&77)wf52SPRSwfJb@PQ0C`$6$ z5XRx#79Q@XF>yoZOQyNF!JLyPl9(gIKnWy;X(^5G%GE*RoH9OzrO4cc$&lbGX9vE_ z>!W1jjc3s5j#{|iFW#8FFF$a^GhN;1fCJ5e^Ql=Xh6o2HIRH`OSseM@q8c83$dWH0Wd!Ogg>%@7I&g*StgQe5zrGb*pUtVIZG z*6%W>h|{Hx;1uqkaS-kV3F*={Pq@Bv1no?lFN0v`sddW!Iw8X2aURyH>4iM9b35{b znd1|npmDT-X-p1bbK}_B<7{f;=92 z(%RE(3Bx8V51)9-%mz!YLj()|lDxOvEyfIq#;+!T#FaFrcH7IGM!&lSp*@GD^-A4n z_Ua3pUm?jr%u;Yi3jWYcJE1=ypIjGuuKzsn@3RxLa8R0Fa``+*!avhCEj)e+AokNoW5Pm1AuUM)_ZGicL&rctLO$I0EWcx$5K2tlVQ&*OIbT_~pYLfOgby|Z_aY^TjqfhGgSY`zat;y%k(A&;n77D5=g}o zt$r!Vtc|US$vT@H2-~#0o6|WGC?dEiDP_T8(h z-%~Hdz`+7z8jo?^8@F=^ipD=)@-e+S8#d9ec%<~pn3s&Nx?U<7OvVhSfp;GIRh%U* zH)QiRXG5(xX06D_-+=7T7<<}~g~kO{n2u}y>Pv*rRL_#dJ7v@u`}_)h3%hyBQFEQZ zB3>k!?yHm;+F2)r#F_K7H~OD%s>htHNoy&%Lf}2~17&O58^{9hZ+=AYZww;(32;ek z9P=<6_N#HlPxHLIO@oWft(4KBkG)J!RxP@$$u3648VBml!o|BdDiV{USEpDS^RQhbIADAPeJP%kxK=}kA zD8O$$x3(3!A(?dvN5piBU%tIF=TF2EZnF6HE&fh)#GvyZx2dH_pTR(s;!b5<^gj^@ zp{L*$>H4;#i!v;ukZm6-{}8-i=F(wZ!sx?%GY^c6g`T7L$(QI@D4qEdWk~gD8qtnA z5Y%@4s(?c^tU~O5trgHd(4vC1?^@?IJ#^_baBf4I3Yj#_QQuSKKo8bWnDukrkLRU6 z_KR(|!(F7+R$C`BcrdC$mXo{R64h`N)6z>W+VWm=9Gtt=-~V`G=19a7;>5zmA2c0u z$pr9CZUj=H5Bl z4U|g>;aU@PRtg>Q7IU)OB=ORrd3Lhe5>ccK>J+f`6u8(WlklL%;HvQcKgQh|JQs%R zx{Yl+*};x&+qP}nwr$(CZ6`anZQt?#ywz#G;>>bfvsvAm#gIlgV^o72R~k;?t3Z(9iwnp>`s&mH09qUn9H==6{W zaYj`jTQY*L5^c?h7#LQWHkZ{F&Y0W<;nZh6m;{>+5$`VAy3ud}t|nm;lb^qKXh&B> z6=5L8;9Fwr+>lq09oUmJ4557OMfk7L)6`usQzETW_&hrsi2LGbc;K}-egIGS*5x#( z;ChQ8oEIeieIdAX$J9r`jy)YjH>Q-gbE|O(9$T9NWN^#)9Zy@-ph!_kA^%xs#&`xe z5;Ey+2Uar!7szHj(O8gN9S?lHou-q zBH0!~;|T@g8lyY3*Mja`U)Z&h+)U{a+ux40>u*)!MR?5Pf*SgMlZ}zf9)sF{Ln)S{ z3V>J*yfuL^#G7FeoIPwr@XJB_x;q%=hNTwi2yMs_Pwz*t*=R-v&p>WVq++-)@6Zu+ z6dOJm2fzXR*n86HI_{tCx$5#wX!W|S_x?9Ex3JZEpIJV-$ZFboV$nZ( z$yjp?iLt|;8+mKzO`CO z+7nZgwXK;WT&vY^@$g`^Zyg%`&`Ueq?vyTN7LwH>GWo;H9NBCm9{GSs2yU)$rPN?n zrF)(^-0O~H-x;pA>gMgoGUJ9etlZ;NDV8WCO`A=IBMfs)1jMW1jum0Ca>NTSMAgjM zwmzN!v;19_*il;%nvJ_GSdu35eg@OzAk?Bb+LF}*bmSk;T72dQ2(L47qzk2&s{)<= zi3NuNU(Qlt2F%)P&in}{klJA8M|KFwd5B-&39b&jx{8^j;dzhz#XWRe+ad>)ph zZxG(ntc-%!! zw-G7YP}<(Ihi@gz`6bj%FIlA7y3!3+-&X4Vtnm~#9cB_+&qCSfTD6yeD@s$mlWMd6 zBj&Q#xYlr8TB$N{Gs}*Dv0-vk>NpXBpkWv^O@>>E)cSo*My-;g5MrL=39a2_DI3$W z*9;mR&LD1t!l8GEp3X0-@&=!egH7Zn1s@Xtdjzx<#J4+bbxN{ae{ZnT?jJNigGB4q zPH)VA@-JBa@W3wdrg^=esr5$Esm(VnHtaz)c?QSQr`4z`x*yk$XF4HZ;G@5ZPZ|5< zh-&@!oe>-o;WTuiVvY7NvWa3FEQgdZFK^yI>heO8U7Z%6n5jFc3tIQ{vH((HPLdT1 zczX(6oms+oJ0Y|d1J^(OH#(ce+wyz#%go_yC!4#VCpeES&m{BH)QBW7!xvQ@LS>vx z1cs9+Dr`lD@ItFg?8*?Wd9WJ}{8IEs=p4T;QaU&p$oF(kv@04} zvryl(lwMBh%J{=iG+{uGGdEC+JUF|hnErtpw%2_4)aoawu9Y}qUy^H-_GlV;2-wk? zZA()@Z@Kro!CXPV&m6+E*qHm*Gr6kK8;`@2A_s~`SAeoSOU>2ynVOl#@_N5CEtAIL ze+<8>**1tJv3My}OD?|kuj<{ms;c^BU|hZfel2Lna!pPmX7)?zKC$tyt;;r%5uW~C4ev4wY}~wyl9=}dNUcte5Sz(180%izfjI2z#eN>&-C=71uH-8?P|L>kB*mMdW8xt83YWFpp3byd-5M{M&uA zP6Wc49QxUzb#vM0Lou@WvhjLW*nBfJCaZomt5L z3HK|nR0usZmOqiF>WV`Z&sg_<+3|(T*%4f_4spR5QuleR55VrSbJ1ea74>P9$5>A> z^`oMOv)Y!L1BOQzwPK>!=ZA4Y;461Zp#6B$lu21O`gk4IcDStv%8>^BF|*UH!bv`;J zW_$H5)~qMYzPh`G@Qoj7uaZ=?+ey=esm*8y(Kqc_>`5QtK=ZR#E}Vk6k*2BuQ8Hf% z2DFp}n2#&k7^|c5`x$BX+|e05YI(}}B4>NdygGB)Qdy(m5}2vbRT5S7&oJdz>$Mo` z`~`&p@THKI9DA?VX|4#eowXKJ!*WTL_6q@{tg#Rfnx*xdE1DH52d5dM3E?T#XfHIy?U$a zk2-BE>Bw?MKI8ZwR{%M^;n|01F>>G^60RsS!?t9Rc;UOgrZA%4M$YUI-C146Qc$@A zW9Y}j*fAd3HX`$`ZWYJ*#9~3aNA4kC4^U14GoJ*5>zT$o`_vk$Yz}8~{AqXRInXvu z>opN%oZc~YZ(!O;ifMsCkrFN^V}EBtcQ%;to-iiaV_vUy?>5WD-%i< z9~^l7y@51FV|ipRHeX;2mLrVgh-d=0Hp-EFN1=B9W2+h3s%)@w*bA;lIy4(LqDHHMD7v8A*jx}_CZw|TDEzv$dqNnlxg@>dm|ub+*}AIv z@2kGanqvkj`#tqihFm`#BS0>_IIxfb9&OfG1s%=PSD0`W&cn4zW!}!`F&zP7!rA(o z0sn5U4fuIS@AB%#LJU+N&8s1Pql~Z$Uq1{2CDhqp94s-CcC;n)Y#=QKfo?Vf6&CY+ ziGYBcB|g0z?KZ1$W9-n$WpInHP0iLbUbW7JWAV`d6r$ft+K6h#q z(w?+bS8_V>WWEs@mzA~b#-ehFobpE5Z+43+!Aefn*BwGD2)Yx?-H`EJ?Ofymh zXP#Gl|RHS&86MqT_ zU4y*D*?8Ts`|lNigyt+~WfCNYR8BhSa}r!L$4a%fUhgZ#_{3yZ=+}pX$KG6M31+}A zn_dVz8xA7&_ws-k5s|jOOhI|w!<2ig{bu_4Qr6F5c|Go)IBuWiuFpn-ywnE2t6y?w zx03#i@9Z9>vwHN{>zl$CB!tqY5Hd^1e5uqLTuNC?v)8=0G$RBN`=F4eFID3{(2)%? zI7=0&?lq}^yp6}e%9|B+2=xLBIIE$2JOr@+xy_g%~4JJU{ubbOq<~u0TWMNra zV9$ap783}=%Mj=+KRcPByEZ?2 z{@dv1vXM##^LiURu(prK%Ltu$zMff^AwwW75i=1w6^C<_fmCr&v|;kK1N(Z z9S)3idrB%2t!}h(j0md{EY>`Kq>fqUJd){vbTn(+Btfr^Z!~Wj$|6^h+?z*tjUij% zH=`ANkFXuEgR!5-^RtP)J281z2R_$*|Ll}AOzxgFD2Q%Di6|%fk}d1+Sg;iOFQ_#9 zT3S$uhDzpNOJM?GIl5Q=lvo1l1tu#$Xd?Tbf@E9VK0lT0@ny>D93vBWs=<8SU&G{B z$;@eh1U?pfn4%h&V3jCJ6jzgFoF#q-$A5eJ6IpNxxw-KBUY%-)2_62Me$WfObYvS5 zO0!(kfkq^zs?vD2u41)6eFfrc17G}%zF)RCAr;2ZRt+L0n#ZhJ2O88ITW7{M;hD@q zUptIh#VdTIZNINGfX6DQub#*-eZ^0jW%s; zL*4S9b=FBaN8PwqTTV`u-a2G8C7-ZIRXFxV}Na&7p*M|h)8KMVD~mWE##v##YfD?mk$ zhO6Z+0gq|wo0=For8S3wcj|76MX?=!)2`Fdl3gE@7;b6t6aSHe<@J>fjiOr-clFk>1_hDGGR3E~H-9*A=7BJ)TTPMGJ?u(Bu_Ry3WP_;t|Wc>0H;l$1{CHZ z_qa5@eyLi^vdGSeyMk8|vjcUu@4i$~C4;2(nO^cEjWAVyYnbHaH7Yi&YozKkyAebRn_64Iz&l`lQ)#w1UPdi%S;hL1I+A8 zuCMPLcczfZ?C9nk{5po z;*rNMY8l6*r(n;U$vvJT+lAev04Jq&jF@gIv8`S(!WF~327Lw2foV>~j54(U1+(=W zOvV!SV&{X<&L!j0H=aO0J>}w#GL>$z)6mpj=|6ulN8TSQYu4H&r%kWqEWv)*-rdwW zOWM2tv)?7lnBR*1yD<`btGu-JE4>J7fQp7Hww&UA}{yHSVgEx?$L}c&nTE zoVc(|4*fT%S!NQ`2&Mz+$syX!TCO&o7?i14%1x6+gcT(@KIq>*gSXUO^T13Io%>4- zdiSi?HSX08()l$N3ms*gd?fyzTLDrazV0GcH-Z3=T^j+LgbIOMCg(~NwS4;n2tyQZWkW{C{IRUMD*>slYPv@kFJfo;- z!1<%rjQR|TUmU!V44;MZ6>%)b(dXH@CfEfTz&#$_@LXzrkHE*=>$@`}qqIT!yMJSw zGHdJTV_ke^yc?XDa?b7-aQ_QLk3O*Re@a1||A!RB#KFYy|4Bhi49u)-|CbcR#K_6Y z^#4-|ifIQ|z+1OUx#>N6AWNQs? z%pgDF@e;LQj}NVG&WY~B!$M%8&LPrzfx80H(ed$toNnEK+~k0`|KKmynW5Urg9h+zfh7J-kWBxn!Lk8g}@El(igp7P^?xR||wxcT|%m-J787`+ps zTd@~_3!q$@K{kR(vVd=ZF5}EriP-Ocodn5EPEJor2c|ABE+#=*+)W)_8qvzkK|Hj& zHG!7}>WDBA6FB$Pr+~-`c+c~fHIOQSanx4p{0S?#wK_aObOnGK2Ie;5j`v~M9Nifp zJAnokfiIh2fK15~hWDmm{HYm$e<%?GaznrM-G4QHolI`-(-{|LW)^p$&-ZO_44@kt z+CYI*O3^g7d$u!x1R$UEqXf9s#T$4RKrL(`S-`2kMDd{#P?W)e2C;vV^Q$wXTa&Y? zqpMqY>hU!FJAubS=Lqu7ncU#_$P)o2e%K^ zaZYdLYO)Yk=WdBM*niIhL?il5(}>uC*l}@j3CSUWj^ThjGc_B3Uwe!~pOZ*DhNp1|ZoB;}zgCRZd1)ab z*>^mIhuzN30C&MT)Gk=~uj&fKcW*}&k+Ze?=Pz%nD`=3r;LPB^+dUzzZYfRgz!}w= zotc`Stl0k1tp3-TxlQ1b!*i?eD>Gn*=H|xld2El)8JofOaB2_IUoMb9_p^QyDXt98 ztY0lA1Umwtg;DVH*a?U{QUp7@0D5i@+*v_Ae|W}#)X~93cpDHP^E1Q-2N%Jw6(s^Y zpz3w{#k~l~0jeL|2M`T1KN3EmtL(i9!U3vx`a@ID2B~kcYaq3Yy$H|&swZ4W5Dim* z5;~yjZF{g&;$PgrPKqxv1G=bw#rN0&9Tk5P2F%tys2S03b`a-9 zz<-G4f2j6<=<|Q*e=V0^(Juo!uYTlp1OWXZ*cR|j?m+kRmC#pwN-iiK%(;~rd>%Bn z^FqH9U7Wtry@poz=8qeco8bp;psGxuSzDc(fO<7QUQjcOD{xlewE%{le?;dm@^5e9 zFPGu3whgS)C%v!Vod3zMF_7H)wf+danaQaIWZtQNr1N*u`qr=9*IGvNPa-2g%lJE& z!Xrvk@E1MsY^}4I8(;@$cQIhv&ujzT{5=5A=^eGFH-Lw=j_2S0;c|0ve;)>_F?#2Q z0&G97Ex~^%gU@`slXVWDH$S>HfpC5m0;g&I0uLVB{sioYZT$cbj=txDA$GvH^{aip zadxEmn}GEVYx;*DLVP*_=|lHN10me(+u46yF|~aKqX1kDUB6^;u9qGVsomThisbs^ z2N`n)^nvkysyB45eo8+L&4_v7@~>h%24ymgTF z4FDPh?fugjY*4P}FW{hZ$4}Iu%-7Eu%=;do^J8|eFCUQE4cnn==imEp|LuSuP*1N* zlz=$_=jb}QD8n#QMp^S&C>S%5>}mh;!t;{nhQZr>S2Yd!CawZ-`S|EbpiSp)d~O7O zp6^a_X)m%i;rEs2(%L8CR#xry1Mp&#SI^$K{;8WejGIZxiC{%cCPHGij=25&c6nWb zX$!p#xK1smb8$NpR;3*Cy2nd;wIZm~%8$|5RNkpSOBDC2`WV5h$2LdO=@Za3`EWzd z2bqh_JVpmSEydg%y4m3|8=Qg7)L|ki`nBXSudLr?6CZjL&5y-_M_A(np#tIcK{?l+ zndHRLe;9`2>@nSoYU-$6n|`->ra{t99yakq?L9UJ`G9H4@UU^CtN!|ztn#BfTQkTN zJOSnDefd`oPF9Y*Vj2*Gk(a0RZ*=NzIxOZ@oCNu{SO;qsofqXGwa53Zzh-FrGH-h- zKZkYp{4?fC`v1PH6|s+`7aI*IrF?a0&%L*xG!5mCk2WiD|0E9v*Oi7qCL(Jk`#yd9QSWW* z7QQ;lrU&B&wprZo<9Ct>9h{lf-Z@c07{9|h2lU^&AKppr-eBi}Eizn;qx_|ZP-|2s zlUiJa&j+z_M3THn(EWuZPq5uS&upxR@)X z_S#I|k*gTZ;~dE)$Dk&TBvPKDj;tP|eAGV*YJ#zb$TI5^QOVfmEpZE$GnmizQl1mz zyY1MkE1ju2ZuNh!{Kmfh*Kt$0)EcleHP=vu1pv4j9 zV=W!)(`B~tYZT$UKqfjXpyAabG|wo6X6Kug#ADwi@vklIl9>PGZ?Z>;DxO9K z->*YM?6FE>S4NsJ^vn8t^D3Oi;6m7^i2slvL{DXGrFgwXzp7>W)sBaB1St%o2`dS=3P%b z$Rj3)=LnX8H-#m7GJfygWrLyo?Jv@xL09$Ti<8~q?xXrtG&J;-+PPAB7M}QslF3Yo zTWx1@Y_tSfY5vgk)yGefo_;$$u`-IYD9$2a=f7YvFlkjOAa^&k{eutZe`rE?) zq*Gr5gm7W<63WI~$p(V1wBasCMeT(IAXE2d0gMmF5WR`%MMm|m4^bEwu0D3mj{&+< zbeMITBz#ciNZ?gQK+q9IaE8Ldc7BesOoqK#Teqnqm7uYTy>9crQrL~m^|3_xqVg5+ zeSReTX|n#2D#(?~cF%+1LqE{)(ddTC3|uEMRVtz-_C-`%wK9b^mPqcPxZuJU0=7RuoFt>z0Qx$M=~CCYgsJ>t{|7s@4HSBJZRv8YwC8 zu)r>stDGeaubMJmV4xlH?z9M{PM*hR@qN#x0FyqtL1m5fs+V4|{}HYW)lfC$?4(*L zw`5thUDEbyMlPP%k9=Fr*p_obt}OqUD|0+)w^q3C7%Ojy)N21xAS*lu){;934_Yf8 z#nY`MtA`*>Ww?K2@dhE&RvWVqk-*A=Vp@%FQY7f6zZJ#ECsV3JzKw{HWni~Wdjnw( zzC&vdWI-*FThyj-p*O5@)#QG?rz(zV^*Xnr(vU~RKV(Iul)aFH@)@>xFpUV?Ad#ab zKlz0D<~0e&Z0vb0!AI-hiS3*$c0-0y=f*dB49B>0oSuER?zogCmtn`jY&x;s2$Z6X zi|`)SPeeh%y=IJ@LC|au1nSg5WTP?r2Co#$i>}F?~b?EFdsvkLy4_;5OVdHyk=p)9~4{ zHs^L6$GcuUkho-SDJnx2r4wVOT|xVHZJdOqvrDvmwWK<4kNEG`J=Oi5n1{sjcvXOp zTd4sK`%%11iIDs8e(L0BRlcuS6dyugvPt2YH+kBb*-!@OjYp4H&TCa21{uXNAd${pfa%eDLxRnzp2W=Qn2eb+M5aU zH|xGFk|fZ19?e=}GChDqr9L-CpDmsVj%H2K*}!Osqr?_`CC zl^Z4p$0oQwtiU#WD9**BK7=isca|XCS)r_#Q|TK{zwx8!1-p|;dJ;b_@z)eSHX4&; zRU0SP&Tik+8n%=e%d~(#tZ3ET2XaNxbAV&EeoW>rb6vd0gkPmamtG>qrugi$jp81E zg4#HKLWT4~Aj1fU9RJZ2POqK*>yXDDn*Kwgct)vb6am7THBbMke7RAl7Ye+rFZlrI zAI^2hQj6-gl>dQ?zSO8{$X_)5g&CtN`NcuAc%cvp=LHfKs@-IuN}n2oog3rCSJ;;l zIjwbCxD}}qpu7vL{w2HUltj-s0Lw*E4h}wPJYO@!FgB*A-d9dLT?llo(Um-xMaic6 zi}_KM#tM~D3JJ!fAPySFh$9*Ualz+|d9G6yT5H~)$#`;!FmyZ^!Et0^Bp!wdbz-$= zAC_c}nnMAX=!NbW6{jA#wu)70_GdTI!UGKpd^U-E#A|I89u=%hM{`+HLN`AQMq1hK!b8PrlR2qOb+I_idrvvrd&&oYaur$nLdd!ym~}lyb0R!yx~mLMdLO6}n##tKF~^KZ&`2wSv0;q*AjS3N_~mH>I}*UV zOXcj~?=u-_YrC^BU>c->vE!`w0xL`c(JCXXS*ghtr%f-tlP#_6ovHG)UgI1L>C>IA z>M26=(5#TDQaUHcyV@?q5!5;)HqNIfW7Q{CscZ&L*U8-j?`o4oi`y<*jNN+MM2VG8 z`YT5hqJ$cDj_VE#_tCd}=+$qJj^LzXKM3XcbF-+vHY^H*=!M~W-T_zYJNFL;`cvcO z;_P-S7P~r#(&TteEp_>qI8?jKfXe>Vz^4z=eg;XJ$zV?w7Alk3+QRZmc$>S5fjvY8 z?W$5L?M&!=1OT-vX3O`g6|Zva#n`q=1`UB!KlC=;O(*fWW-0qUu))sSHop;E4_Bmv<4UWtqaqbs zTK^>>^`X`uw@>O;O8v@;IKBR)CBFBUJcqM12ZXy0D$QuR0t#c)q0aKX#EPHlK~z3G zRP*!NCs2Ai+>7*&bawzCdd9?V$Ll?FZSc}tJ&gP`qDYZUaO{3NnyGH<55FD%Q4C(Q z-kr;s+@=+>eaD@)AH2+`CR(>k+^zhyX*yqa8~I0X-#yJKrzR@3BQ@WcVLd1mnL2;-{tN%Qiwb7TC4xd&O2Htz#IvYYw& zD2Y9+cF%I1OzUdk8Bc7r(?u_{Y+M-v)ZkpPk8OTi0N8M zqC`-(lgc($kEZD>DkxIpkexE2H@{hWIq>~9y>!C0C*A7o$I}R0xy%U%eC}3oV63sjbY46tWsV8v;%wiqJwQ21N7;#I+e15A0 zthxf0=2h$~uLY*VnO|A7RJFz9#3?$F1gRw+@#f-SBer8Fa1l$$01(T#!0Y%(>N9KG z4}RqoS#0qC0Hj%hg;FV}7c92P(aBvFrNe%EG!ow0jIdiqDyK1jS%lpmXzv^IwYv~0 z;YPyka=>!fgoufm#Te#ITB=tI?#WtDEg4=N7M1kIY2!S=GfjZg9Tkw2OwqecC$nx= zRF?}_+}W7wC;vFvQaZ-f;^@T6UWA<2Q^V5N6F&NKL{*yq=+RsW$(km?>5Z;xo$G0X zI?=b1LYsb$kWSfZaZ-tzl8)OkJ;)8G4+hABT1KUKsrcB|D^wzNN`j@)(k?~rpImDm zG?B@cmm-_mK7D4h6rgP9F)&~)m1bLWx+pT~yc z7%wp13I~}p!czV zKN$v-lz!h86xLsZ(n{R{zO+SMe7n{iIQJcO&EP_$x_!&|mR6Yb^rz<}H(8)|)PP!p~lSf$QBdyB!&JsP3j(gY>NFsjQG;0U90ZG)KgbxlNCClM2ncms7*>kF7utVr8UJBIluCy_(VH_f3bQKfBJ z_M9#$ifZqFan!`q+ zNU=vyM#nT-My?|I&$reh<&FaS#q@$0UQ~j73J)qtoDl}lu|qHtArQCOOlv~FbAWtj zJ2^(LvnwPx<)`9irU08&muM@j=01vrn!aKYudQ?6F{T5K--eO+r^0o^YhCeuMQbm= ziIZE`PeA1*D8t!tJJnMq)@ttczLRSLAE6gav{uiNEK9{F2TV0<*W5Kq#d>iSw3p6N zA`ud+Pv(HwhbHJ6CPH;PIiZ6xu1>n!^i$*@N%%O6yOvI|9r49$KlzR05%D{e5LQg+ zc*gWv1%4aGGCK+jt7&Fvz9ZVQ=?v*Q@ifIV!|CgcWuqx_;UFb8Ak%ORQLYaFU2S-gI zHNnnbq;rjza1r-!FtVvOBDg?0p6+O_b_&DIyUc1>SojD)M3lrQ66~*qFPY=fg>y0e6atiqb*hd!E)Y;T6_l#v2 zXCL{aP77)0smz6b%$u~@mE&LJ!H$|T-x`sva20@9l#8t`Ic*VuRe zS+nxBMiSL|zvYbSD(MSWkDNu8VTr=eQ&8?Bhhs6zAwzuo^<5S*-Ahw7%)qKUh+%RL zmTVBb_c?ljq51ndDoPq&cG=$0%&>@5oH58TX(Uc}@*_cJ<(5#}w%}c3r zhDsPr@%&WKzaAAIim1zM;m7t5^#y-!H3@tv)xChweoxwpr5PPLEFHm6wRc~Vz8a^o z{Wg}yKYg1y%fIyLSBHi;@ygxt_wNnmK5KSGqIrpA1$9he<@&Y*5At7tv`6mY$RwKM z9ImGE=82y8vS_!el(@n$EYA%PYJ9X1ec2hO!*dZHu_efqitv=`W(SJM8`+Sui*f#H z@Nz&Mgw(!MSRiLatYI_tF0VnimlLc`@aFo>U(e&>KUx`rxuh+wYQ-c3)<;8$=38H-ts*u;@#78^O4h1eJbCR2 zkwi!4Cx1IBC0TrDA=nZ(-!F(0iR=+32)lk!^nfv&uPeC_S)VDM)jBnQm=Lhn%fsLw zhAmz&pS`O(%)t!Rr0^I2NI+`l-k~kM*)m-oYVY81(rM$Z#Yt;XVUFpv=j11T_BjItWR~yb1!$Qm9C9%ac>2i;Q!Nf43 z`$E2xDTi-0o<$V?eLrOzao-79ZK0IUYSmps=1~5ETnK&xg)>hX@tgBZO#hRwS)!}N z?>|NG0a)1QY>uS3& zM+NG1T-tU==KT0aG@k~pDNx^6Jt@#8HoA`OWZk(vB7eihZ@xmmZxbQOIPGC0aqaQ- z-?ry2!h!DaffoGU8O7;0QmwsAWD};J1^K^pr8CGj;Tv+DEMlJQoQHq zxx6%t&phH1b47++5~z|ht!KxL&Iq9SzRRPXtoavhBJMpcOJ4~4!>HxPTDYNnYqd|y zS%B%_6;m8kcmkcin_R45ulbN*sm#zIA4bZBFTq|}K%XS*vya-9oNrT;z{&?wzrkcf zh1Zne2E}k4&2zn#zv|K(SfV_>wM%Wm`PZz|{iX{IGF_S>{%;$MN3b$V^eMJQsQ<9s zwS#adV^V>t)ZdM!4J<;(bp&QY0fwzzNN-fOhPK=R<6XOufi<>xMFÍogRJG$}B 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%0AH7)i>Y(4l!B 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

      JYni7qKgAkpXZF=5rAoBK+#V-1wfBy4{8mN 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%0Ai93Sot*jmqr4%OaFRipOf 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

      )z!pAP0i&B6Y@gQ;wegP# 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%0AU-#c+`FF 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 atv!0{{@P#rYk&P6um1 Date: Sun, 1 Aug 2021 22:39:08 -0600 Subject: [PATCH 0201/1472] howHeatCap im mDecisions --- build/source/dshare/get_ixname.f90 | 1 + build/source/dshare/var_lookup.f90 | 3 ++- build/source/engine/mDecisions.f90 | 13 +++++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index b5b68151a..c5c76a4b4 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -95,6 +95,7 @@ function get_ixdecisions(varName) case('subRouting' ); get_ixdecisions=iLookDECISIONS%subRouting ! choice of method for sub-grid routing case('snowDenNew' ); get_ixdecisions=iLookDECISIONS%snowDenNew ! choice of method for new snow density case('snowUnload' ); get_ixdecisions=iLookDECISIONS%snowUnload ! choice of parameterization for snow unloading from canopy + case('howHeatCap' ); get_ixdecisions=iLookDECISIONS%howHeatCap ! how to compute heat capacity in energy equation ! get to here if cannot find the variable case default get_ixdecisions = integerMissing diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 712353a2d..3f0cb20a7 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -71,6 +71,7 @@ MODULE var_lookup integer(i4b) :: spatial_gw = integerMissing ! choice of method for spatial representation of groundwater integer(i4b) :: subRouting = integerMissing ! choice of method for sub-grid routing integer(i4b) :: snowDenNew = integerMissing ! choice of method for new snow density + integer(i4b) :: howHeatCap = integerMissing ! how to compute heat capacity in energy equation endtype iLook_decision ! *********************************************************************************************************** @@ -776,7 +777,7 @@ MODULE var_lookup type(iLook_decision),public,parameter :: iLookDECISIONS=iLook_decision( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,& - 31, 32, 33, 34, 35, 36, 37, 38) + 31, 32, 33, 34, 35, 36, 37, 38, 39) ! named variables: model time type(iLook_time), public,parameter :: iLookTIME =iLook_time ( 1, 2, 3, 4, 5, 6, 7) diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index bd5602293..b33c4b251 100644 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -147,6 +147,9 @@ module mDecisions_module ! look-up values for the choice of snow unloading from the canopy integer(i4b),parameter,public :: meltDripUnload = 321 ! Hedstrom and Pomeroy (1998), Storck et al 2002 (snowUnloadingCoeff & ratioDrip2Unloading) integer(i4b),parameter,public :: windUnload = 322 ! Roesch et al 2001, formulate unloading based on wind and temperature +! look-up values for the choice of energy equation +integer(i4b),parameter,public :: enthalpyFD = 323 ! enthalpyFD +integer(i4b),parameter,public :: closedForm = 324 ! closedForm ! ----------------------------------------------------------------------------------------------------------- contains @@ -404,6 +407,16 @@ subroutine mDecisions(err,message) err=10; message=trim(message)//"unknown numerical method [option="//trim(model_decisions(iLookDECISIONS%num_method)%cDecision)//"]"; return end select + ! how to compute heat capacity in energy equation + select case(trim(model_decisions(iLookDECISIONS%howHeatCap)%cDecision)) + case('enthalpyFD'); model_decisions(iLookDECISIONS%howHeatCap)%iDecision = enthalpyFD ! enthalpyFD + case('closedForm'); model_decisions(iLookDECISIONS%howHeatCap)%iDecision = closedForm ! closedForm + case default + ! TODO: after adding howHeatCap decision in corresponding file we should delete the next line + model_decisions(iLookDECISIONS%howHeatCap)%iDecision = enthalpyFD + ! err=10; message=trim(message)//"unknown Cp computation [option="//trim(model_decisions(iLookDECISIONS%howHeatCap)%cDecision)//"]"; return +end select + ! identify the method used to calculate flux derivatives select case(trim(model_decisions(iLookDECISIONS%fDerivMeth)%cDecision)) case('numericl'); model_decisions(iLookDECISIONS%fDerivMeth)%iDecision = numerical ! numerical From c43def69852bdaf7ba8f8c4c94758012fe66f363 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 1 Aug 2021 22:50:24 -0600 Subject: [PATCH 0202/1472] howHeatCap decision in eval8DAE --- build/source/engine/eval8DAE.f90 | 10 +++++----- build/source/engine/mDecisions.f90 | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index e5966776b..6a22e84f9 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -65,7 +65,8 @@ module eval8DAE_module ! look-up values for the choice of groundwater representation (local-column, or single-basin) USE mDecisions_module,only: & localColumn, & ! separate groundwater representation in each local soil column - singleBasin ! single groundwater store over the entire basin + singleBasin, & ! single groundwater store over the entire basin + enthalpyFD ! heat capacity using enthalpy ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: & @@ -266,8 +267,7 @@ subroutine eval8DAE(& real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step - logical(lgt),parameter :: enthalpyFD=.false. ! flag to indicate if we compute Cp using dH_T/dT - logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains + logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m @@ -491,7 +491,7 @@ subroutine eval8DAE(& if(updateCp)then ! *** compute volumetric heat capacity C_p - if(enthalpyFD)then + if(model_decisions(iLookDECISIONS%howHeatCap)%iDecision == enthalpyFD)then ! compute H_T call t2enthalpy_T(& ! input: data structures @@ -553,7 +553,7 @@ subroutine eval8DAE(& mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur end do endif ! if dt_cur is not too samll - else + else ! if using closed formula of heat capacity call computHeatCapAnalytic(& ! input: control variables computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index b33c4b251..fb6369d24 100644 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -413,7 +413,7 @@ subroutine mDecisions(err,message) case('closedForm'); model_decisions(iLookDECISIONS%howHeatCap)%iDecision = closedForm ! closedForm case default ! TODO: after adding howHeatCap decision in corresponding file we should delete the next line - model_decisions(iLookDECISIONS%howHeatCap)%iDecision = enthalpyFD + model_decisions(iLookDECISIONS%howHeatCap)%iDecision = closedForm ! err=10; message=trim(message)//"unknown Cp computation [option="//trim(model_decisions(iLookDECISIONS%howHeatCap)%cDecision)//"]"; return end select From 0631e78778c535aa206a990761f3256527a30cc3 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 1 Aug 2021 23:07:56 -0600 Subject: [PATCH 0203/1472] more in flags_params --- sundials/flags_params.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sundials/flags_params.txt b/sundials/flags_params.txt index f327227eb..19e575cdb 100644 --- a/sundials/flags_params.txt +++ b/sundials/flags_params.txt @@ -1,6 +1,10 @@ All SUMMA-SUNDIALS files are in build/source/engine To switch between SUMMA-BE and SUMMA-SUNDIALS, open opSplittin.f90 and set solver=BE or solver=IDA, respectively. +In energy equation, the heat capacity is computed using either the analytical (closed) formula or the finite differece formula (dH_T/dT). The +"howHeatCap" varialbe has been added to the var_lookup module to handle such decision. A user should add this variable to the model_decision +file with one of the values "closedForm" or "enthalpyFD". For now, this value is by default "colsedForm" in mDecisions.f90. + The other important files are as following: systemSolvSundials.f90: contains public subroutine systemSolvSundials which runs the coupled energy-mass model for one timestep From 973fb064177d3b0c2deb27f663d30fc7533f06ab Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 1 Aug 2021 23:43:23 -0600 Subject: [PATCH 0204/1472] diffEqSolv in var_lookup and mDecisions --- build/source/dshare/get_ixname.f90 | 1 + build/source/dshare/var_lookup.f90 | 3 ++- build/source/engine/mDecisions.f90 | 13 +++++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index c5c76a4b4..87162458d 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -96,6 +96,7 @@ function get_ixdecisions(varName) case('snowDenNew' ); get_ixdecisions=iLookDECISIONS%snowDenNew ! choice of method for new snow density case('snowUnload' ); get_ixdecisions=iLookDECISIONS%snowUnload ! choice of parameterization for snow unloading from canopy case('howHeatCap' ); get_ixdecisions=iLookDECISIONS%howHeatCap ! how to compute heat capacity in energy equation + case('diffEqSolv' ); get_ixdecisions=iLookDECISIONS%diffEqSolv ! how to solve the system of differential equations ! get to here if cannot find the variable case default get_ixdecisions = integerMissing diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 3f0cb20a7..bb423d090 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -72,6 +72,7 @@ MODULE var_lookup integer(i4b) :: subRouting = integerMissing ! choice of method for sub-grid routing integer(i4b) :: snowDenNew = integerMissing ! choice of method for new snow density integer(i4b) :: howHeatCap = integerMissing ! how to compute heat capacity in energy equation + integer(i4b) :: diffEqSolv = integerMissing ! how to solve the system of differential equations endtype iLook_decision ! *********************************************************************************************************** @@ -777,7 +778,7 @@ MODULE var_lookup type(iLook_decision),public,parameter :: iLookDECISIONS=iLook_decision( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,& - 31, 32, 33, 34, 35, 36, 37, 38, 39) + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40) ! named variables: model time type(iLook_time), public,parameter :: iLookTIME =iLook_time ( 1, 2, 3, 4, 5, 6, 7) diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index fb6369d24..dceb83362 100644 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -150,6 +150,9 @@ module mDecisions_module ! look-up values for the choice of energy equation integer(i4b),parameter,public :: enthalpyFD = 323 ! enthalpyFD integer(i4b),parameter,public :: closedForm = 324 ! closedForm +! look-up values for the choice of DAE solver +integer(i4b),parameter,public :: sundialIDA = 325 ! IDA solver form Sundials package +integer(i4b),parameter,public :: backwEuler = 326 ! backward Euler method implemented by Martyn ! ----------------------------------------------------------------------------------------------------------- contains @@ -417,6 +420,16 @@ subroutine mDecisions(err,message) ! err=10; message=trim(message)//"unknown Cp computation [option="//trim(model_decisions(iLookDECISIONS%howHeatCap)%cDecision)//"]"; return end select + ! how to solve the system of differential equations +select case(trim(model_decisions(iLookDECISIONS%diffEqSolv)%cDecision)) +case('sundialIDA'); model_decisions(iLookDECISIONS%diffEqSolv)%iDecision = sundialIDA ! enthalpyFD +case('backwEuler'); model_decisions(iLookDECISIONS%diffEqSolv)%iDecision = backwEuler ! closedForm +case default + ! TODO: after adding diffEqSolv decision in corresponding file we should delete the next line + model_decisions(iLookDECISIONS%diffEqSolv)%iDecision = sundialIDA + ! err=10; message=trim(message)//"unknown DAE solver [option="//trim(model_decisions(iLookDECISIONS%diffEqSolv)%cDecision)//"]"; return +end select + ! identify the method used to calculate flux derivatives select case(trim(model_decisions(iLookDECISIONS%fDerivMeth)%cDecision)) case('numericl'); model_decisions(iLookDECISIONS%fDerivMeth)%iDecision = numerical ! numerical From 88e5e065dc58a141baf733a7bfe6e002c9af5b87 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Mon, 2 Aug 2021 00:34:37 -0600 Subject: [PATCH 0205/1472] diffEqSolv in oppSplittin --- build/source/engine/opSplittin.f90 | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 62ed42d50..b0958e837 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -112,7 +112,9 @@ module opSplittin_module USE mDecisions_module,only: & qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization bigBucket, & ! a big bucket (lumped aquifer model) - noExplicit ! no explicit groundwater parameterization + noExplicit, & ! no explicit groundwater parameterization + sundialIDA, & ! IDA solver from Sundials package + backwEuler ! backward Euler method ! safety: set private unless specified otherwise implicit none @@ -294,9 +296,6 @@ subroutine opSplittin(& logical(lgt) :: doAdjustTemp ! flag to adjust temperature after the mass split logical(lgt) :: failedMinimumStep ! flag to denote failure of substepping for a given split integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - integer(i4b),parameter :: IDA=1 - integer(i4b),parameter :: BE=2 - integer(i4b) :: solver=IDA ! BE or IDA integer(i4b) :: nCoupling real(qp) :: dt_out ! ! --------------------------------------------------------------------------------------- @@ -365,10 +364,10 @@ subroutine opSplittin(& err=0; message="opSplittin/" ! we just solve the fully coupled problem by ida - select case(solver) - case(BE); nCoupling = 2 - case(IDA); nCoupling = 1 - case default; err=20; message=trim(message)//'expect case to be IDA or BE'; return + select case(model_decisions(iLookDECISIONS%diffEqSolv)%iDecision) + case(sundialIDA); nCoupling = 1 + case(backwEuler); nCoupling = 2 + case default; err=20; message=trim(message)//'expect case to be sundialIDA or backwEuler'; return end select ! ***** @@ -774,8 +773,8 @@ subroutine opSplittin(& if(ixSolution==scalar) numberScalarSolutions = numberScalarSolutions + 1 ! solve variable subset for one full time step - select case(solver) - case(IDA) + select case(model_decisions(iLookDECISIONS%diffEqSolv)%iDecision) + case(sundialIDA) call varSubstepSundials(& ! input: model control dt, & ! intent(inout) : time step (s) @@ -812,7 +811,7 @@ subroutine opSplittin(& tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt dt_out, & ! intent(out) err,cmessage) ! intent(out) : error code and error message - case(BE) + case(backwEuler) call varSubstep(& ! input: model control dt, & ! intent(inout) : time step (s) @@ -849,7 +848,7 @@ subroutine opSplittin(& tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt err,cmessage) ! intent(out) : error code and error message ! check - case default; err=20; message=trim(message)//'expect case to be ida or be'; return + case default; err=20; message=trim(message)//'expect case to backwEuler or sundialIDA'; return end select dt = dt_out From 5dc456f26fa6957521c8cb5b8ac1d5f549608633 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Mon, 2 Aug 2021 00:57:29 -0600 Subject: [PATCH 0206/1472] more in flags_params --- sundials/flags_params.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sundials/flags_params.txt b/sundials/flags_params.txt index 19e575cdb..6e061e522 100644 --- a/sundials/flags_params.txt +++ b/sundials/flags_params.txt @@ -1,11 +1,14 @@ -All SUMMA-SUNDIALS files are in build/source/engine -To switch between SUMMA-BE and SUMMA-SUNDIALS, open opSplittin.f90 and set solver=BE or solver=IDA, respectively. + +To switch between SUMMA-BE and SUMMA-SUNDIALS, the "diffEqSolv" varialbe has been added to the var_lookup module. +A user should add this variable to the model_decision file with one of the values "backwEuler" or "sundialIDA". + For now, this value is by default "sundialIDA" in mDecisions.f90. In energy equation, the heat capacity is computed using either the analytical (closed) formula or the finite differece formula (dH_T/dT). The "howHeatCap" varialbe has been added to the var_lookup module to handle such decision. A user should add this variable to the model_decision file with one of the values "closedForm" or "enthalpyFD". For now, this value is by default "colsedForm" in mDecisions.f90. -The other important files are as following: +All SUMMA-SUNDIALS files are in build/source/engine. The important ones are as following: + systemSolvSundials.f90: contains public subroutine systemSolvSundials which runs the coupled energy-mass model for one timestep solveByIDA.f90: contains public subroutine solveByIDA which solves the differential equation system F(y,y') = 0 by IDA (y is the state vector) From c6bfc184525ba2e46ab12efa01bfeef8191d9763 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sat, 4 Sep 2021 08:38:06 -0600 Subject: [PATCH 0207/1472] print statements in computResidDAE --- build/source/engine/computResidDAE.f90 | 2 ++ build/source/engine/eval8DAE.f90 | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/build/source/engine/computResidDAE.f90 b/build/source/engine/computResidDAE.f90 index 35585ead2..220edf615 100644 --- a/build/source/engine/computResidDAE.f90 +++ b/build/source/engine/computResidDAE.f90 @@ -240,6 +240,8 @@ subroutine computResidDAE(& !print*, 'PAUSE:'; read(*,*) endif + call printResidDAE(nSnow,nSoil,nLayers,indx_data,rAdd,rVec) + ! check if(any(isNan(rVec)))then call printResidDAE(nSnow,nSoil,nLayers,indx_data,rAdd,rVec) diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index 6a22e84f9..ec955493f 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -693,7 +693,9 @@ subroutine eval8DAE(& dCompress_dPsi, & ! intent(inout): derivative in compressibility w.r.t. matric head (m-1) err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - + + print *, 'dt = ', dt + print *, 'dt_cur = ', dt_cur ! compute the residual vector call computResidDAE(& @@ -730,6 +732,8 @@ subroutine eval8DAE(& resVec, & ! intent(out): residual vector err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + print *, '=====================================================================================' ! end association with the information in the data structures From 224bd022415236a1e843ba9e9cdc662e4d811f8a Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Fri, 10 Sep 2021 22:23:00 -0600 Subject: [PATCH 0208/1472] commented out print statements, fullMatrix is false --- build/source/driver/summa_driver.f90 | 2 +- build/source/engine/computResidDAE.f90 | 2 +- build/source/engine/eval8DAE.f90 | 6 +++--- build/source/engine/systemSolvSundials.f90 | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index d4bb7006a..d05088b60 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -90,7 +90,7 @@ program summa_driver call summa_readForcing(modelTimeStep, summa1_struc(n), err, message) call handle_err(err, message) - print *, 'step ---> ', iStep +! print *, 'step ---> ', iStep ! run the summa physics for one time step call summa_runPhysics(modelTimeStep, summa1_struc(n), err, message) call handle_err(err, message) diff --git a/build/source/engine/computResidDAE.f90 b/build/source/engine/computResidDAE.f90 index 220edf615..00a6ecd67 100644 --- a/build/source/engine/computResidDAE.f90 +++ b/build/source/engine/computResidDAE.f90 @@ -240,7 +240,7 @@ subroutine computResidDAE(& !print*, 'PAUSE:'; read(*,*) endif - call printResidDAE(nSnow,nSoil,nLayers,indx_data,rAdd,rVec) +! call printResidDAE(nSnow,nSoil,nLayers,indx_data,rAdd,rVec) ! check if(any(isNan(rVec)))then diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index ec955493f..0cab33c50 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -694,8 +694,8 @@ subroutine eval8DAE(& err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - print *, 'dt = ', dt - print *, 'dt_cur = ', dt_cur +! print *, 'dt = ', dt +! print *, 'dt_cur = ', dt_cur ! compute the residual vector call computResidDAE(& @@ -733,7 +733,7 @@ subroutine eval8DAE(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - print *, '=====================================================================================' +! print *, '=====================================================================================' ! end association with the information in the data structures diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 4359799f0..cc8d9e27c 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -190,7 +190,7 @@ subroutine systemSolvSundials(& ! ------------------------------------------------------------------------------------------------------ ! * model solver ! ------------------------------------------------------------------------------------------------------ - logical(lgt),parameter :: forceFullMatrix=.true. ! flag to force the use of the full Jacobian matrix + logical(lgt),parameter :: forceFullMatrix=.false. ! flag to force the use of the full Jacobian matrix integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) type(var_dlength) :: flux_init ! model fluxes at the start of the time step real(rkind),allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! NOTE: allocatable, since not always needed From 2cade32bd45fcb31003e344084b8203a83b06436 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 12 Sep 2021 22:26:52 -0600 Subject: [PATCH 0209/1472] full matrix, print statements --- build/source/driver/summa_driver.f90 | 2 +- build/source/engine/computJacDAE.f90 | 11 +++++++++++ build/source/engine/computResidDAE.f90 | 2 +- build/source/engine/eval8DAE.f90 | 6 +++--- build/source/engine/systemSolvSundials.f90 | 2 +- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index d05088b60..d4bb7006a 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -90,7 +90,7 @@ program summa_driver call summa_readForcing(modelTimeStep, summa1_struc(n), err, message) call handle_err(err, message) -! print *, 'step ---> ', iStep + print *, 'step ---> ', iStep ! run the summa physics for one time step call summa_runPhysics(modelTimeStep, summa1_struc(n), err, message) call handle_err(err, message) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index c32ad0fb6..45e4302b2 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -831,6 +831,15 @@ subroutine computJacDAE(& write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(min(iJac1,nState):min(iJac2,nState),iLayer) end do end if + + + print*, '** analytical Jacobian (full):' + print *, 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) + do iLayer=min(iJac1,nState),min(iJac2,nState) + print *, iLayer, aJac(min(iJac1,nState):min(iJac2,nState),iLayer) + end do + + print *, '--------------------------------------------------------------' ! *** ! check @@ -839,6 +848,8 @@ subroutine computJacDAE(& end select ! type of matrix if(any(isNan(aJac)))then + print *, '******************************* WE FIRST FOUND NAN IN JACOBIAN ************************************' + stop 1 message=trim(message)//'we found NaN' err=20; return endif diff --git a/build/source/engine/computResidDAE.f90 b/build/source/engine/computResidDAE.f90 index 00a6ecd67..220edf615 100644 --- a/build/source/engine/computResidDAE.f90 +++ b/build/source/engine/computResidDAE.f90 @@ -240,7 +240,7 @@ subroutine computResidDAE(& !print*, 'PAUSE:'; read(*,*) endif -! call printResidDAE(nSnow,nSoil,nLayers,indx_data,rAdd,rVec) + call printResidDAE(nSnow,nSoil,nLayers,indx_data,rAdd,rVec) ! check if(any(isNan(rVec)))then diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index 0cab33c50..ec955493f 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -694,8 +694,8 @@ subroutine eval8DAE(& err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) -! print *, 'dt = ', dt -! print *, 'dt_cur = ', dt_cur + print *, 'dt = ', dt + print *, 'dt_cur = ', dt_cur ! compute the residual vector call computResidDAE(& @@ -733,7 +733,7 @@ subroutine eval8DAE(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) -! print *, '=====================================================================================' + print *, '=====================================================================================' ! end association with the information in the data structures diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index cc8d9e27c..4359799f0 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -190,7 +190,7 @@ subroutine systemSolvSundials(& ! ------------------------------------------------------------------------------------------------------ ! * model solver ! ------------------------------------------------------------------------------------------------------ - logical(lgt),parameter :: forceFullMatrix=.false. ! flag to force the use of the full Jacobian matrix + logical(lgt),parameter :: forceFullMatrix=.true. ! flag to force the use of the full Jacobian matrix integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) type(var_dlength) :: flux_init ! model fluxes at the start of the time step real(rkind),allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! NOTE: allocatable, since not always needed From fe97519497079b4b786c5d3e807a1767c21f8526 Mon Sep 17 00:00:00 2001 From: Reza Zolfaghari Date: Sun, 17 Oct 2021 21:54:35 -0600 Subject: [PATCH 0210/1472] iden_ice instead of iden_water in soil energy of Jacobian, prints disabled --- build/source/engine/computJacDAE.f90 | 261 ++----------------------- build/source/engine/computResidDAE.f90 | 2 +- build/source/engine/eval8DAE.f90 | 6 +- 3 files changed, 16 insertions(+), 253 deletions(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 45e4302b2..bd9ffd985 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -307,9 +307,9 @@ subroutine computJacDAE(& + LH_fus*iden_ice * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & + LH_fus*iden_ice * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) case(iname_soil) - dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj & - + LH_fus*iden_water * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & - + LH_fus*iden_water * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) + dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_ice*mLayerdTheta_dTk(iLayer) ) * cj & + + LH_fus*iden_ice * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & + + LH_fus*iden_ice * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) end select end if end do @@ -337,245 +337,8 @@ subroutine computJacDAE(& ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* case(ixBandMatrix) ! ixBandMatrix ixFullMatrix - - ! check - if(size(aJac,1)/=nBands .or. size(aJac,2)/=size(dMat))then - message=trim(message)//'unexpected shape of the Jacobian matrix: expect aJac(nBands,nState)' - err=20; return - end if - - ! ----- - ! * energy and liquid fluxes over vegetation... - ! --------------------------------------------- - if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) - - ! * diagonal elements for the vegetation canopy (-) - if(ixCasNrg/=integerMissing) aJac(ixDiag,ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) * cj - if(ixVegNrg/=integerMissing) aJac(ixDiag,ixVegNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanopyTemp) + dMat(ixVegNrg) - if(ixVegHyd/=integerMissing) aJac(ixDiag,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanLiq - scalarCanopyLiqDeriv)*dt + 1._rkind * cj ! ixVegHyd: CORRECT - - ! * cross-derivative terms w.r.t. canopy water - if(ixVegHyd/=integerMissing)then - ! cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) - if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixCasNrg),ixCasNrg) = -dCanopyEvaporation_dTCanair*dt ! ixCasNrg: CORRECT - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixVegNrg),ixVegNrg) = -dCanopyEvaporation_dTCanopy*dt + dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy ! ixVegNrg: CORRECT - if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixTopNrg),ixTopNrg) = -dCanopyEvaporation_dTGround*dt ! ixTopNrg: CORRECT - ! cross-derivative terms w.r.t. canopy water (kg-1 m2) - if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water - ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) - ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (dt/canopyDepth) *(-dCanopyNetFlux_dCanLiq) + cj * (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth + LH_fus * iden_water * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy ! dF/dLiq - if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanLiq) - endif - - ! cross-derivative terms between surface hydrology and the temperature of the vegetation canopy (K-1) - if(ixVegNrg/=integerMissing)then - if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarCanopyLiqDeriv*dCanLiq_dTcanopy)/iden_water - endif - - ! cross-derivative terms w.r.t. the temperature of the canopy air space (J m-3 K-1) - if(ixCasNrg/=integerMissing)then - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixCasNrg,ixVegNrg),ixVegNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanopyTemp) - if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixCasNrg,ixTopNrg),ixTopNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dGroundTemp) - endif - - ! cross-derivative terms w.r.t. the temperature of the vegetation canopy (J m-3 K-1) - if(ixVegNrg/=integerMissing)then - if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixCasNrg),ixCasNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanairTemp) - if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixTopNrg),ixTopNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dGroundTemp) - endif - - ! cross-derivative terms w.r.t. the temperature of the surface (J m-3 K-1) - if(ixTopNrg/=integerMissing)then - if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanairTemp) - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanopyTemp) - endif - - endif ! if there is a need to compute energy fluxes within vegetation - - ! ----- - ! * energy fluxes for the snow+soil domain... - ! ------------------------------------------- - if(nSnowSoilNrg>0)then - do iLayer=1,nLayers ! loop through all layers in the snow+soil domain - - ! check if the state is in the subset - if(ixSnowSoilNrg(iLayer)==integerMissing) cycle - - ! - define index within the state subset and the full state vector - jState = ixSnowSoilNrg(iLayer) ! index within the state subset - - ! - diagonal elements - aJac(ixDiag,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) - - ! - lower-diagonal elements - if(iLayer > 1)then - if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowSoilNrg(iLayer-1),jState),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) - endif - - ! - upper diagonal elements - if(iLayer < nLayers)then - if(ixSnowSoilNrg(iLayer+1)/=integerMissing) aJac(ixOffDiag(ixSnowSoilNrg(iLayer+1),jState),jState) = (dt/mLayerDepth(iLayer+1))*(-dNrgFlux_dTempAbove(iLayer ) ) - endif - - end do ! (looping through energy states in the snow+soil domain) - endif ! (if the subset includes energy state variables in the snow+soil domain) - - ! ----- - ! * liquid water fluxes for the snow domain... - ! -------------------------------------------- - if(nSnowOnlyHyd>0)then - do iLayer=1,nSnow ! loop through layers in the snow domain - - ! - check that the snow layer is desired - if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle - - ! - define state indices for the current layer - watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - - ! compute factor to convert liquid water derivative to total water derivative - select case( ixHydType(iLayer) ) - case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) - case default; convLiq2tot = 1._rkind - end select - - ! - diagonal elements - aJac(ixDiag,watState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot + dMat(watState) * cj - - ! - lower-diagonal elements - if(iLayer > 1)then - if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyHyd(iLayer-1),watState),watState) = 0._rkind ! sub-diagonal: no dependence on other layers - endif - - ! - upper diagonal elements - if(iLayer < nSnow)then - if(ixSnowOnlyHyd(iLayer+1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyHyd(iLayer+1),watState),watState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot ! dVol(below)/dLiq(above) -- (-) - endif - - ! - compute cross-derivative terms for energy - ! NOTE: increase in volumetric liquid water content balanced by a decrease in volumetric ice content - if(nSnowOnlyNrg>0)then - - ! (define the energy state) - nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector - if(nrgstate/=integerMissing)then ! (energy state for the current layer is within the state subset) - - ! (cross-derivative terms for the current layer) - aJac(ixOffDiag(nrgState,watState),watState) = -(1._rkind - mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & - + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) - aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) - - ! (cross-derivative terms for the layer below) - if(iLayer < nSnow)then - aJac(ixOffDiag(ixSnowOnlyHyd(iLayer+1),nrgState),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 - endif ! (if there is a water state in the layer below the current layer in the given state subset) - - endif ! (if the energy state for the current layer is within the state subset) - endif ! (if state variables exist for energy in snow+soil layers) - - end do ! (looping through liquid water states in the snow domain) - endif ! (if the subset includes hydrology state variables in the snow domain) - - ! ----- - ! * liquid water fluxes for the soil domain... - ! -------------------------------------------- - if(nSoilOnlyHyd>0)then - do iLayer=1,nSoil - - ! - check that the soil layer is desired - if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle - - ! - define state indices - watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset - - ! - define indices of the soil layers - jLayer = iLayer+nSnow ! index of layer in the snow+soil vector - - ! - compute the diagonal elements - ! all terms *excluding* baseflow - aJac(ixDiag,watState) = (dt/mLayerDepth(jLayer))*(-dq_dHydStateBelow(iLayer-1) + dq_dHydStateAbove(iLayer)) + dMat(watState) - - ! - compute the lower-diagonal elements - if(iLayer > 1)then - if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),watState),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) - endif - - ! - compute the upper-diagonal elements - if(iLayer0 .and. nSoilOnlyNrg>0)then - do iLayer=1,nSoilOnlyHyd - - ! - check that the soil layer is desired - if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle - - ! - define index of hydrology state variable within the state subset - watState = ixSoilOnlyHyd(iLayer) - - ! - define indices of the soil layers - jLayer = iLayer+nSnow ! index of layer in the snow+soil vector - - ! - define the energy state variable - nrgState = ixNrgLayer(jLayer) ! index within the full state vector - - ! only compute derivatives if the energy state for the current layer is within the state subset - if(nrgstate/=integerMissing)then - - ! - compute the Jacobian for the layer itself - aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance - - ! - include derivatives w.r.t. ground evaporation - if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer - if(computeVegFlux)then - aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanLiq/iden_water) ! dVol/dLiq (kg m-2)-1 - aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) - aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) - endif - aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) - endif - - ! melt-freeze: compute derivative in energy with respect to mass - if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present - aJac(ixOffDiag(nrgState,watState),watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water*cj - LH_fus*iden_water * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content - else - aJac(ixOffDiag(nrgState,watState),watState) = 0._rkind - endif - - ! - compute lower diagonal elements - if(iLayer>1)then - if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),nrgState),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 - endif - - ! compute upper-diagonal elements - if(iLayer Date: Mon, 8 Nov 2021 13:55:43 +0900 Subject: [PATCH 0211/1472] new makefiles for mac --- build/build_summa | 3 + build/my_makefile | 411 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 414 insertions(+) create mode 100755 build/build_summa create mode 100644 build/my_makefile diff --git a/build/build_summa b/build/build_summa new file mode 100755 index 000000000..c0b0667a4 --- /dev/null +++ b/build/build_summa @@ -0,0 +1,3 @@ +module load gnu +module load openblas +make -f my_makefile diff --git a/build/my_makefile b/build/my_makefile new file mode 100644 index 000000000..b37eb4bc3 --- /dev/null +++ b/build/my_makefile @@ -0,0 +1,411 @@ +#======================================================================== +# Makefile to compile SUMMA +#======================================================================== +# +# Recommended use: Copy this file to Makefile.local, edit it to your +# heart's content, and then run `make -f build/Makefile.local` from +# your top level SUMMA directory. Don't include the Makefile.local in +# any pull requests you make. +# +# Note that Makefile configurations that we commonly use can be found on +# the SUMMA wiki at: +# https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration +# feel free to add yours to that page. +# +# To troubleshoot your paths and setup, type 'make check' +# +# At a minimum you will need to set the following: +# * F_MASTER - top level summa directory +# * FC - compiler suite +# * FC_EXE - compiler executable +# * INCLUDES - path to include files +# * LIBRARIES - path to and libraries to include +# +# Some further options can be specified for OpenMP, etc. See in Part 0 of +# the Makefile. You don't need to make any changes in PART 1 and +# following unless you are doing SUMMA development and changed what +# needs to be compiled + +#======================================================================== +# PART 0: User-configurable part +#======================================================================== + +# The variables can be specified in one of two ways: +# * delete the '##' in front of the variable, fill out the entry, +# save the file and run make +# * make no changes to this file, but specify the variables in your +# environment before you run make + +# Define core directory below which everything resides. This is the +# parent directory of the 'build' directory +F_MASTER = /Users/amedin/Research/SummaSundials/summa + +# Define the Fortran Compiler. If you are using gfortran, then this needs +# to be version 6 or higher. This variable is simply used to select the right +# compiler flags in the ifeq statements in this Makefile. The compiler +# executable is set separately as FC_EXE +# Currently this is either gfortran or ifort +FC = gfortran + +# Define the path for the compiler executable. This is the actual executable +# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin-gfortran-mp-6 +# FC and FC_EXE have to be consistent +FC_EXE =/opt/local/bin/gfortran + +# Define the NetCDF and LAPACK libraries and path to include files. +# INCLUDES needs to be of the form (no quotes around the string): +# INCLUDES = -I -I -I<...> -I +# LIBRARIES needs to be of the form ( no quotes around the string): +# LIBRARIES = '-L -lnetcdff -L -lblas -L -l' +# If none of this makes sense, please talk to your system +# administrator. +INCLUDES = -I/opt/include -I//opt/local/share/doc/liblapack3 -I/opt/local/lib/x86_64-linux-gnu -I/opt/local/include \ + -I/opt/local/lib -I/opt/local/fortran +LDFLAGS += -L/opt/local/lib +LIBRARIES = -L/opt/local/lib -llapack -lgfortran -lnetcdff -lnetcdf -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod \ + -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod + +# Eventually we plan move to a real configure script, but for now we like +# to keep track of successful compilations of SUMMA on different platforms +# and with different compilers. If you are successful compiling SUMMA, +# please add your configuration (operating system and compiler plus +# part 0 of the Makefile) to the SUMMA wiki on github. + +# Define compiler flags. If you use a different compiler, +# you will need to figure out what the equivalent flags are +# and may need to update this section + +# ------------ define compiler flags ---------------------------------------- + +# define open MP flags +isOpenMP = +FLAGS_OMP = +LIBOPENMP = + +# Define compiler flags. If you use a different compiler, +# you will need to figure out what the equivalent flags are +# and may need to update this section + +# gfortran compiler flags +ifeq "$(FC)" "gfortran" + + ifeq "$(isOpenMP)" "yes" + FLAGS_OMP = -fopenmp + endif + +# Production runs +FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) + +# Debug runs +#FLAGS_NOAH = -p -g -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument +#FLAGS_COMM = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds +#FLAGS_SUMMA = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds + +endif + +# ifort compiler flags +ifeq "$(FC)" "ifort" + + ifeq "$(isOpenMP)" "yes" + FLAGS_OMP = -qopenmp + endif + +# Production runs +FLAGS_NOAH = -O3 -noerror_limit -FR -auto -fltconsistency $(FLAGS_OMP) +FLAGS_COMM = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) +FLAGS_SUMMA = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) + +# Debug runs +#FLAGS_NOAH = -O0 -p -g -warn nounused -noerror_limit -FR -auto -WB -traceback -fltconsistency +#FLAGS_COMM = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 +#FLAGS_SUMMA = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 +endif + +#======================================================================== +# PART 1: Define directory paths +#======================================================================== + +# Core directory that contains source code +F_KORE_DIR = $(F_MASTER)/build/source + +# Location of the compiled modules +MOD_PATH = $(F_MASTER)/build + +# Define the directory for the executables +EXE_PATH = $(F_MASTER)/bin + +#======================================================================== +# PART 2: Assemble all of the SUMMA sub-routines +#======================================================================== + +# Define directories +DRIVER_DIR = $(F_KORE_DIR)/driver +HOOKUP_DIR = $(F_KORE_DIR)/hookup +NETCDF_DIR = $(F_KORE_DIR)/netcdf +DSHARE_DIR = $(F_KORE_DIR)/dshare +NUMREC_DIR = $(F_KORE_DIR)/numrec +NOAHMP_DIR = $(F_KORE_DIR)/noah-mp +ENGINE_DIR = $(F_KORE_DIR)/engine + +# utilities +SUMMA_NRUTIL= \ + nrtype.f90 \ + f2008funcs.f90 \ + nr_utility.f90 +NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) + +# +# Numerical recipes procedures +# NOTE: all numerical recipes procedures are now replaced with free versions +SUMMA_NRPROC= \ + expIntegral.f90 \ + spline_int.f90 +NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) + +# Hook-up modules (set files and directory paths) +SUMMA_HOOKUP= \ + ascii_util.f90 \ + summaFileManager.f90 + +HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) + +# Data modules +SUMMA_DATAMS= \ + multiconst.f90 \ + var_lookup.f90 \ + data_types.f90 \ + globalData.f90 \ + flxMapping.f90 \ + get_ixname.f90 \ + popMetadat.f90 \ + outpt_stat.f90 +DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) + +# utility modules +SUMMA_UTILMS= \ + time_utils.f90 \ + mDecisions.f90 \ + snow_utils.f90 \ + soil_utils.f90 \ + soil_utilsSundials.f90 \ + updatState.f90 \ + updatStateSundials.f90 \ + matrixOper.f90 +UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) + +# Model guts +SUMMA_MODGUT= \ + MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) + +# Solver +SUMMA_SOLVER= \ + vegPhenlgy.f90 \ + diagn_evar.f90 \ + stomResist.f90 \ + groundwatr.f90 \ + vegSWavRad.f90 \ + vegNrgFlux.f90 \ + ssdNrgFlux.f90 \ + vegLiqFlux.f90 \ + snowLiqFlx.f90 \ + soilLiqFlx.f90 \ + bigAquifer.f90 \ + computFlux.f90 \ + computResid.f90 \ + computJacob.f90 \ + eval8summa.f90 \ + summaSolve.f90 \ + systemSolv.f90 \ + type4IDA.f90 \ + tol4IDA.f90 \ + computEnthalpy.f90 \ + computHeatCap.f90 \ + computThermConduct.f90 \ + computResidDAE.f90 \ + eval8DAE.f90 \ + evalDAE4IDA.f90 \ + computJacDAE.f90 \ + eval8JacDAE.f90 \ + evalJac4IDA.f90 \ + computSnowDepth.f90 \ + solveByIDA.f90 \ + systemSolvSundials.f90 \ + varSubstep.f90 \ + varSubstepSundials.f90 \ + opSplittin.f90 \ + coupled_em.f90 \ + run_oneHRU.f90 \ + run_oneGRU.f90 +SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) + +# Define routines for SUMMA preliminaries +SUMMA_PRELIM= \ + conv_funcs.f90 \ + sunGeomtry.f90 \ + convE2Temp.f90 \ + allocspace.f90 \ + checkStruc.f90 \ + childStruc.f90 \ + ffile_info.f90 \ + read_attrb.f90 \ + read_pinit.f90 \ + pOverwrite.f90 \ + read_param.f90 \ + paramCheck.f90 \ + check_icond.f90 +PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) + +SUMMA_NOAHMP= \ + module_model_constants.F \ + module_sf_noahutl.F \ + module_sf_noahlsm.F \ + module_sf_noahmplsm.F +NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) + +# Define routines for the SUMMA model runs +SUMMA_MODRUN = \ + indexState.f90 \ + getVectorz.f90 \ + varExtrSundials.f90 \ + t2enthalpy.f90 \ + updateVars.f90 \ + updateVarsSundials.f90 \ + updateVars4JacDAE.f90 \ + var_derive.f90 \ + read_force.f90 \ + derivforce.f90 \ + snowAlbedo.f90 \ + canopySnow.f90 \ + tempAdjust.f90 \ + snwCompact.f90 \ + layerMerge.f90 \ + layerDivide.f90 \ + volicePack.f90 \ + qTimeDelay.f90 +MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) + +# Define routines for the solver +SUMMA_MSOLVE = \ + +# Define NetCDF routines +SUMMA_NETCDF = \ + netcdf_util.f90 \ + def_output.f90 \ + modelwrite.f90 \ + read_icond.f90 +NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) + +# ... stitch together common programs +COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) + +# ... stitch together SUMMA programs +SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) + +# Define the driver routine +SUMMA_DRIVER= \ + summa_type.f90 \ + summa_util.f90 \ + summa_alarms.f90 \ + summa_globalData.f90 \ + summa_defineOutput.f90 \ + summa_init.f90 \ + summa_setup.f90 \ + summa_restart.f90 \ + summa_forcing.f90 \ + summa_modelRun.f90 \ + summa_writeOutput.f90 \ + summa_driver.f90 +DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) + +# Define the executable +DRIVER__EX = summa.exe + +# Define version number +VERSIONFILE = $(DRIVER_DIR)/summaversion.inc +VERSION = $(shell git tag | tail -n 1) +BULTTIM = $(shell date) +GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') +GITHASH = $(shell git rev-parse HEAD) + +#======================================================================== +# PART 3: Checks +#====================================================================== +# make sure that the paths are defined. These are just some high level checks +ifndef F_MASTER + $(error F_MASTER is undefined) +endif +ifndef FC + $(error FC is undefined: Specify your compiler) +endif +ifndef FC_EXE + $(error FC_EXE is undefined: Specify your compiler executable) +endif +ifndef FLAGS_SUMMA + $(error Specify flags for your compiler: $(FC)) +endif +ifndef INCLUDES + $(error INCLUDES is undefined) +endif +ifndef LIBRARIES + $(error LIBRARIES is undefined) +endif + +#======================================================================== +# PART 4: compilation +#====================================================================== + +# Compile +all: compile_noah compile_comm compile_summa link clean install +part: compile_noah compile_comm compile_summa + +check: + $(info) + $(info Displaying make variables:) + $(info F_MASTER : $(F_MASTER)) + $(info EXE_PATH : $(EXE_PATH)) + $(info FC : $(FC)) + $(info INCLUDES : $(INCLUDES)) + $(info LIBRARIES : $(LIBRARIES)) + $(info FLAGS_NOAH : $(FLAGS_NOAH)) + $(info FLAGS_COMM : $(FLAGS_COMM)) + $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) + $(info) + +# update version information +update_version: + echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) + echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) + +# compile Noah-MP routines +compile_noah: + $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) + +# compile common routines + +compile_comm: + $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) + +# compile SUMMA routines +compile_summa: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ + $(INCLUDES) + +# link routines +link: + $(FC_EXE) -g *.o $(LIBRARIES) -o $(DRIVER__EX) + +# Remove object files +clean: + rm -f *.o + rm -f *.mod + rm -f soil_veg_gen_parm__genmod.f90 + +# Copy the executable to the bin directory +install: + @mkdir -p $(EXE_PATH) + @mv $(DRIVER__EX) $(EXE_PATH) + $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) From 104bd2137fa8666aead5e09848efdb23fb68da2e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 15 Nov 2021 12:40:21 +0900 Subject: [PATCH 0212/1472] Installation notes and makefiles for Mac --- build/my_makefile | 10 ++++------ docs/installation/SUMMA_on_OS_X.md | 17 +++++++++-------- sundials/flags_params.txt | 2 +- sundials/installation.txt | 16 ++++++++++------ 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/build/my_makefile b/build/my_makefile index b37eb4bc3..b642dfccf 100644 --- a/build/my_makefile +++ b/build/my_makefile @@ -48,7 +48,7 @@ F_MASTER = /Users/amedin/Research/SummaSundials/summa FC = gfortran # Define the path for the compiler executable. This is the actual executable -# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin-gfortran-mp-6 +# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin/gfortran-mp-11 # FC and FC_EXE have to be consistent FC_EXE =/opt/local/bin/gfortran @@ -59,11 +59,9 @@ FC_EXE =/opt/local/bin/gfortran # LIBRARIES = '-L -lnetcdff -L -lblas -L -l' # If none of this makes sense, please talk to your system # administrator. -INCLUDES = -I/opt/include -I//opt/local/share/doc/liblapack3 -I/opt/local/lib/x86_64-linux-gnu -I/opt/local/include \ - -I/opt/local/lib -I/opt/local/fortran -LDFLAGS += -L/opt/local/lib -LIBRARIES = -L/opt/local/lib -llapack -lgfortran -lnetcdff -lnetcdf -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod \ - -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod +INCLUDES = -I/opt/local/include -I/opt/local/lib -I/usr/local/include -I/usr/local/lib -I/Users/amedin/Research/SummaSundials/builddir/fortran_STATIC +LDFLAGS = -L/opt/local/lib -L/usr/local/lib +LIBRARIES = $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod # Eventually we plan move to a real configure script, but for now we like # to keep track of successful compilations of SUMMA on different platforms diff --git a/docs/installation/SUMMA_on_OS_X.md b/docs/installation/SUMMA_on_OS_X.md index 08f50e59e..c90c96148 100755 --- a/docs/installation/SUMMA_on_OS_X.md +++ b/docs/installation/SUMMA_on_OS_X.md @@ -29,11 +29,11 @@ In the following I will assume that you don't have a Fortran compiler or NetCDF `sudo port info netcdf-fortran` - The gcc compiler will be listed under `Build Dependencies`. For example, `gcc6`, which is what I'll assume for now. However, make sure to use the correct version of gcc (i.e. the one used to compile the netcdf-fortran library) when you compile SUMMA. + The gcc compiler will be listed under `Build Dependencies`. For example, `gcc11`, which is what I'll assume for now. However, make sure to use the correct version of gcc (i.e. the one used to compile the netcdf-fortran library) when you compile SUMMA. - 1. Install the correct version of gcc (here we'll assume gcc6) + 1. Install the correct version of gcc (here we'll assume gcc11) - `sudo port install gcc6` + `sudo port install gcc11` 1. Note that MacPorts typically installs everything in the `/opt/local` directory. Make sure you now have NetCDF and gfortran installed: @@ -43,16 +43,17 @@ In the following I will assume that you don't have a Fortran compiler or NetCDF `ls /opt/local/bin/*fortran*` - You should have one or more `gfortran-*` files. If you installed `gcc6`, the gfortran executable will be `/opt/local/bin/gfortran-mp-6`. Since MacPorts will have modified your path so that `/opt/local/bin` is part of that, you should be able to invoke the compiler by typing + You should have one or more `gfortran-*` files. If you installed `gcc11`, the gfortran executable will be `/opt/local/bin/gfortran-mp-11`. Since MacPorts will have modified your path so that `/opt/local/bin` is part of that, you should be able to invoke the compiler by typing - `gfortran-mp-6` + `gfortran-mp-11` and the results should be - `gfortran-mp-6: fatal error: no input files` + `gfortran-mp-11: fatal error: no input files` `compilation terminated.` - Note that you may also have a symbolic link named `/opt/local/bin/gfortran`. Make sure that that link points to the correct executable (e.g. `/opt/local/bin/gfortran-mp-6`). If so, then you should be able to invoke the correct version of the fortran compile by simply typing `gfortran`. + Note that you may also have a symbolic link named `/opt/local/bin/gfortran`. Make sure that that link points to the correct executable (e.g. command `readlink /opt/local/bin/gfortran` should result in `/opt/local/bin/gfortran-mp-11`). If so, then you should be able to invoke the correct version of the fortran compile by simply typing `gfortran`. Or, type `sudo ln -sf /opt/local/bin/gfortran-mp-11 /opt/local/bin/gfortran` to change it. + 1. While you are at it, there are a number of other packages that would be useful to install, in particular @@ -62,4 +63,4 @@ In the following I will assume that you don't have a Fortran compiler or NetCDF 1. Now obtain the SUMMA source code from the [SUMMA source code repository](https://github.com/NCAR/summa). You may just want to download the latest tagged release. Unless you are planning to contribute to the source code, there is no need to clone or fork the repository. - 1. Untar or unzip the archive, then go to the `summa/build` directory and follow the instructions in the [SUMMA installation](SUMMA_installation.md) page. If you are using MacPorts, the `FC_ENV` can be set to `gfortran-6-macports`. + 1. Untar or unzip the archive, then go to the `summa/build` directory and follow the instructions in the [SUMMA installation](SUMMA_installation.md) page. If you are using MacPorts, the `FC_ENV` can be set to `gfortran-11-macports`. diff --git a/sundials/flags_params.txt b/sundials/flags_params.txt index 6e061e522..50242d808 100644 --- a/sundials/flags_params.txt +++ b/sundials/flags_params.txt @@ -1,5 +1,5 @@ -To switch between SUMMA-BE and SUMMA-SUNDIALS, the "diffEqSolv" varialbe has been added to the var_lookup module. +To switch between SUMMA-BE and SUMMA-SUNDIALS, the "diffEqSolv" variabe has been added to the var_lookup module. A user should add this variable to the model_decision file with one of the values "backwEuler" or "sundialIDA". For now, this value is by default "sundialIDA" in mDecisions.f90. diff --git a/sundials/installation.txt b/sundials/installation.txt index a18da0c60..d99850c18 100644 --- a/sundials/installation.txt +++ b/sundials/installation.txt @@ -1,15 +1,19 @@ 1. Download the latest release of IDA solver from SUNDIALS package in https://computing.llnl.gov/projects/sundials/sundials-software 2. Extract the corresponding compressed file -3. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, edit CMakeLists.txt before running cmake. That is, the following -setting needs to be enabled: - # Fortran 2003 interface is disabled by default - set(DOCSTR "Enable Fortran 2003 interfaces") - option(F2003_INTERFACE_ENABLE "${DOCSTR}" ON) +3. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, edit CMakeLists.txt before running cmake. That is, the following setting needs to be enabled: +HOW DO, OR JUST CHANGE IN CACHE??? + +# Fortran 2003 interface is disabled by default +set(DOCSTR "Enable Fortran 2003 interfaces") +option(F2003_INTERFACE_ENABLE "${DOCSTR}" ON) + +set(DOCSTR "Enable Fortran 2003 modules") +option(BUILD_FORTRAN_MODULE_INTERFACE "${DOCSTR}" ON) 4. Read INSTALL_GUIDE.pdf and follow the installation instructions -5. The default compiler is gfortan. To change it, add the following optoin to camke +5. The default compiler is gfortan. To change it, add the following option to cmake -DCMAKE_Fortran_COMPILER=your_favorite_compiler From 3932c1e765706a77ef23a661ae91760fdbf972be Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 16 Nov 2021 09:47:04 +0900 Subject: [PATCH 0213/1472] Adding makefiles for different systems --- build/build_summa | 11 +- build/my_makefile_cop | 409 +++++++++++++++++++++++++ build/{my_makefile => my_makefile_mac} | 0 3 files changed, 418 insertions(+), 2 deletions(-) create mode 100644 build/my_makefile_cop rename build/{my_makefile => my_makefile_mac} (100%) diff --git a/build/build_summa b/build/build_summa index c0b0667a4..8750f83c0 100755 --- a/build/build_summa +++ b/build/build_summa @@ -1,3 +1,10 @@ -module load gnu +module load nixpkgs/16.09 +module load gcc/7.3.0 module load openblas -make -f my_makefile +module load netcdf-fortran/4.4.4 + +# Specify the necessary paths for the compiler +export INCLUDES="-I$EBROOTNETCDFMINFORTRAN/include" +export LIBRARIES="-L$EBROOTNETCDFMINFORTRAN/lib64 -L$EBROOTOPENBLAS/lib -lnetcdff -lopenblas" + +make -f my_makefile_cop diff --git a/build/my_makefile_cop b/build/my_makefile_cop new file mode 100644 index 000000000..481322783 --- /dev/null +++ b/build/my_makefile_cop @@ -0,0 +1,409 @@ +#======================================================================== +# Makefile to compile SUMMA +#======================================================================== +# +# Recommended use: Copy this file to Makefile.local, edit it to your +# heart's content, and then run `make -f build/Makefile.local` from +# your top level SUMMA directory. Don't include the Makefile.local in +# any pull requests you make. +# +# Note that Makefile configurations that we commonly use can be found on +# the SUMMA wiki at: +# https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration +# feel free to add yours to that page. +# +# To troubleshoot your paths and setup, type 'make check' +# +# At a minimum you will need to set the following: +# * F_MASTER - top level summa directory +# * FC - compiler suite +# * FC_EXE - compiler executable +# * INCLUDES - path to include files +# * LIBRARIES - path to and libraries to include +# +# Some further options can be specified for OpenMP, etc. See in Part 0 of +# the Makefile. You don't need to make any changes in PART 1 and +# following unless you are doing SUMMA development and changed what +# needs to be compiled + +#======================================================================== +# PART 0: User-configurable part +#======================================================================== + +# The variables can be specified in one of two ways: +# * delete the '##' in front of the variable, fill out the entry, +# save the file and run make +# * make no changes to this file, but specify the variables in your +# environment before you run make + +# Define core directory below which everything resides. This is the +# parent directory of the 'build' directory +F_MASTER =/globalhome/gwu479/HPC/SummaSundials/summa + +# Define the Fortran Compiler. If you are using gfortran, then this needs +# to be version 6 or higher. This variable is simply used to select the right +# compiler flags in the ifeq statements in this Makefile. The compiler +# executable is set separately as FC_EXE +# Currently this is either gfortran or ifort +FC = gfortran + +# Define the path for the compiler executable. This is the actual executable +# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin/gfortran-mp-11 +# FC and FC_EXE have to be consistent +FC_EXE = gfortran + +# Define the NetCDF and LAPACK libraries and path to include files. +# INCLUDES needs to be of the form (no quotes around the string): +# INCLUDES = -I -I -I<...> -I +# LIBRARIES needs to be of the form ( no quotes around the string): +# LIBRARIES = '-L -lnetcdff -L -lblas -L -l' +# If none of this makes sense, please talk to your system +# administrator. +INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include -I/globalhome/gwu479/HPC/SummaSundials/builddir/fortran_STATIC +LDFLAGS = -L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib +LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod + +# Eventually we plan move to a real configure script, but for now we like +# to keep track of successful compilations of SUMMA on different platforms +# and with different compilers. If you are successful compiling SUMMA, +# please add your configuration (operating system and compiler plus +# part 0 of the Makefile) to the SUMMA wiki on github. + +# Define compiler flags. If you use a different compiler, +# you will need to figure out what the equivalent flags are +# and may need to update this section + +# ------------ define compiler flags ---------------------------------------- + +# define open MP flags +isOpenMP = +FLAGS_OMP = +LIBOPENMP = + +# Define compiler flags. If you use a different compiler, +# you will need to figure out what the equivalent flags are +# and may need to update this section + +# gfortran compiler flags +ifeq "$(FC)" "gfortran" + + ifeq "$(isOpenMP)" "yes" + FLAGS_OMP = -fopenmp + endif + +# Production runs +FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) + +# Debug runs +#FLAGS_NOAH = -p -g -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument +#FLAGS_COMM = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds +#FLAGS_SUMMA = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds + +endif + +# ifort compiler flags +ifeq "$(FC)" "ifort" + + ifeq "$(isOpenMP)" "yes" + FLAGS_OMP = -qopenmp + endif + +# Production runs +FLAGS_NOAH = -O3 -noerror_limit -FR -auto -fltconsistency $(FLAGS_OMP) +FLAGS_COMM = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) +FLAGS_SUMMA = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) + +# Debug runs +#FLAGS_NOAH = -O0 -p -g -warn nounused -noerror_limit -FR -auto -WB -traceback -fltconsistency +#FLAGS_COMM = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 +#FLAGS_SUMMA = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 +endif + +#======================================================================== +# PART 1: Define directory paths +#======================================================================== + +# Core directory that contains source code +F_KORE_DIR = $(F_MASTER)/build/source + +# Location of the compiled modules +MOD_PATH = $(F_MASTER)/build + +# Define the directory for the executables +EXE_PATH = $(F_MASTER)/bin + +#======================================================================== +# PART 2: Assemble all of the SUMMA sub-routines +#======================================================================== + +# Define directories +DRIVER_DIR = $(F_KORE_DIR)/driver +HOOKUP_DIR = $(F_KORE_DIR)/hookup +NETCDF_DIR = $(F_KORE_DIR)/netcdf +DSHARE_DIR = $(F_KORE_DIR)/dshare +NUMREC_DIR = $(F_KORE_DIR)/numrec +NOAHMP_DIR = $(F_KORE_DIR)/noah-mp +ENGINE_DIR = $(F_KORE_DIR)/engine + +# utilities +SUMMA_NRUTIL= \ + nrtype.f90 \ + f2008funcs.f90 \ + nr_utility.f90 +NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) + +# +# Numerical recipes procedures +# NOTE: all numerical recipes procedures are now replaced with free versions +SUMMA_NRPROC= \ + expIntegral.f90 \ + spline_int.f90 +NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) + +# Hook-up modules (set files and directory paths) +SUMMA_HOOKUP= \ + ascii_util.f90 \ + summaFileManager.f90 + +HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) + +# Data modules +SUMMA_DATAMS= \ + multiconst.f90 \ + var_lookup.f90 \ + data_types.f90 \ + globalData.f90 \ + flxMapping.f90 \ + get_ixname.f90 \ + popMetadat.f90 \ + outpt_stat.f90 +DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) + +# utility modules +SUMMA_UTILMS= \ + time_utils.f90 \ + mDecisions.f90 \ + snow_utils.f90 \ + soil_utils.f90 \ + soil_utilsSundials.f90 \ + updatState.f90 \ + updatStateSundials.f90 \ + matrixOper.f90 +UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) + +# Model guts +SUMMA_MODGUT= \ + MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) + +# Solver +SUMMA_SOLVER= \ + vegPhenlgy.f90 \ + diagn_evar.f90 \ + stomResist.f90 \ + groundwatr.f90 \ + vegSWavRad.f90 \ + vegNrgFlux.f90 \ + ssdNrgFlux.f90 \ + vegLiqFlux.f90 \ + snowLiqFlx.f90 \ + soilLiqFlx.f90 \ + bigAquifer.f90 \ + computFlux.f90 \ + computResid.f90 \ + computJacob.f90 \ + eval8summa.f90 \ + summaSolve.f90 \ + systemSolv.f90 \ + type4IDA.f90 \ + tol4IDA.f90 \ + computEnthalpy.f90 \ + computHeatCap.f90 \ + computThermConduct.f90 \ + computResidDAE.f90 \ + eval8DAE.f90 \ + evalDAE4IDA.f90 \ + computJacDAE.f90 \ + eval8JacDAE.f90 \ + evalJac4IDA.f90 \ + computSnowDepth.f90 \ + solveByIDA.f90 \ + systemSolvSundials.f90 \ + varSubstep.f90 \ + varSubstepSundials.f90 \ + opSplittin.f90 \ + coupled_em.f90 \ + run_oneHRU.f90 \ + run_oneGRU.f90 +SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) + +# Define routines for SUMMA preliminaries +SUMMA_PRELIM= \ + conv_funcs.f90 \ + sunGeomtry.f90 \ + convE2Temp.f90 \ + allocspace.f90 \ + checkStruc.f90 \ + childStruc.f90 \ + ffile_info.f90 \ + read_attrb.f90 \ + read_pinit.f90 \ + pOverwrite.f90 \ + read_param.f90 \ + paramCheck.f90 \ + check_icond.f90 +PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) + +SUMMA_NOAHMP= \ + module_model_constants.F \ + module_sf_noahutl.F \ + module_sf_noahlsm.F \ + module_sf_noahmplsm.F +NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) + +# Define routines for the SUMMA model runs +SUMMA_MODRUN = \ + indexState.f90 \ + getVectorz.f90 \ + varExtrSundials.f90 \ + t2enthalpy.f90 \ + updateVars.f90 \ + updateVarsSundials.f90 \ + updateVars4JacDAE.f90 \ + var_derive.f90 \ + read_force.f90 \ + derivforce.f90 \ + snowAlbedo.f90 \ + canopySnow.f90 \ + tempAdjust.f90 \ + snwCompact.f90 \ + layerMerge.f90 \ + layerDivide.f90 \ + volicePack.f90 \ + qTimeDelay.f90 +MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) + +# Define routines for the solver +SUMMA_MSOLVE = \ + +# Define NetCDF routines +SUMMA_NETCDF = \ + netcdf_util.f90 \ + def_output.f90 \ + modelwrite.f90 \ + read_icond.f90 +NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) + +# ... stitch together common programs +COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) + +# ... stitch together SUMMA programs +SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) + +# Define the driver routine +SUMMA_DRIVER= \ + summa_type.f90 \ + summa_util.f90 \ + summa_alarms.f90 \ + summa_globalData.f90 \ + summa_defineOutput.f90 \ + summa_init.f90 \ + summa_setup.f90 \ + summa_restart.f90 \ + summa_forcing.f90 \ + summa_modelRun.f90 \ + summa_writeOutput.f90 \ + summa_driver.f90 +DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) + +# Define the executable +DRIVER__EX = summa.exe + +# Define version number +VERSIONFILE = $(DRIVER_DIR)/summaversion.inc +VERSION = $(shell git tag | tail -n 1) +BULTTIM = $(shell date) +GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') +GITHASH = $(shell git rev-parse HEAD) + +#======================================================================== +# PART 3: Checks +#====================================================================== +# make sure that the paths are defined. These are just some high level checks +ifndef F_MASTER + $(error F_MASTER is undefined) +endif +ifndef FC + $(error FC is undefined: Specify your compiler) +endif +ifndef FC_EXE + $(error FC_EXE is undefined: Specify your compiler executable) +endif +ifndef FLAGS_SUMMA + $(error Specify flags for your compiler: $(FC)) +endif +ifndef INCLUDES + $(error INCLUDES is undefined) +endif +ifndef LIBRARIES + $(error LIBRARIES is undefined) +endif + +#======================================================================== +# PART 4: compilation +#====================================================================== + +# Compile +all: compile_noah compile_comm compile_summa link clean install +part: compile_noah compile_comm compile_summa + +check: + $(info) + $(info Displaying make variables:) + $(info F_MASTER : $(F_MASTER)) + $(info EXE_PATH : $(EXE_PATH)) + $(info FC : $(FC)) + $(info INCLUDES : $(INCLUDES)) + $(info LIBRARIES : $(LIBRARIES)) + $(info FLAGS_NOAH : $(FLAGS_NOAH)) + $(info FLAGS_COMM : $(FLAGS_COMM)) + $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) + $(info) + +# update version information +update_version: + echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) + echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) + +# compile Noah-MP routines +compile_noah: + $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) + +# compile common routines + +compile_comm: + $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) + +# compile SUMMA routines +compile_summa: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ + $(INCLUDES) + +# link routines +link: + $(FC_EXE) -g *.o $(LIBRARIES) -o $(DRIVER__EX) + +# Remove object files +clean: + rm -f *.o + rm -f *.mod + rm -f soil_veg_gen_parm__genmod.f90 + +# Copy the executable to the bin directory +install: + @mkdir -p $(EXE_PATH) + @mv $(DRIVER__EX) $(EXE_PATH) + $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) diff --git a/build/my_makefile b/build/my_makefile_mac similarity index 100% rename from build/my_makefile rename to build/my_makefile_mac From 975f7751b597e705620979c7e7a82e2d6bdbc72b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 16 Nov 2021 11:21:04 +0900 Subject: [PATCH 0214/1472] updating makefiles and instructions --- build/build_summa | 4 ++-- build/my_makefile_cop | 2 +- sundials/installation.txt | 36 ++++++++++++++++++++++++------------ 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/build/build_summa b/build/build_summa index 8750f83c0..a94daa3ff 100755 --- a/build/build_summa +++ b/build/build_summa @@ -4,7 +4,7 @@ module load openblas module load netcdf-fortran/4.4.4 # Specify the necessary paths for the compiler -export INCLUDES="-I$EBROOTNETCDFMINFORTRAN/include" -export LIBRARIES="-L$EBROOTNETCDFMINFORTRAN/lib64 -L$EBROOTOPENBLAS/lib -lnetcdff -lopenblas" +#export INCLUDES="-I$EBROOTNETCDFMINFORTRAN/include" +#export LIBRARIES="-L$EBROOTNETCDFMINFORTRAN/lib64 -L$EBROOTOPENBLAS/lib -lnetcdff -lopenblas" make -f my_makefile_cop diff --git a/build/my_makefile_cop b/build/my_makefile_cop index 481322783..935e0d5ef 100644 --- a/build/my_makefile_cop +++ b/build/my_makefile_cop @@ -50,7 +50,7 @@ FC = gfortran # Define the path for the compiler executable. This is the actual executable # that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin/gfortran-mp-11 # FC and FC_EXE have to be consistent -FC_EXE = gfortran +FC_EXE = /cvmfs/soft.computecanada.ca/easybuild/software/2020/Core/gcccore/9.3.0/bin/gfortran # Define the NetCDF and LAPACK libraries and path to include files. # INCLUDES needs to be of the form (no quotes around the string): diff --git a/sundials/installation.txt b/sundials/installation.txt index d99850c18..e13c7db0a 100644 --- a/sundials/installation.txt +++ b/sundials/installation.txt @@ -1,21 +1,33 @@ 1. Download the latest release of IDA solver from SUNDIALS package in https://computing.llnl.gov/projects/sundials/sundials-software +% wget "https://github.com/LLNL/sundials/releases/download/v5.8.0/sundials-5.8.0.tar.gz" + 2. Extract the corresponding compressed file -3. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, edit CMakeLists.txt before running cmake. That is, the following setting needs to be enabled: -HOW DO, OR JUST CHANGE IN CACHE??? +% tar -xzf sundials-5.8.0.tar.gz -# Fortran 2003 interface is disabled by default -set(DOCSTR "Enable Fortran 2003 interfaces") -option(F2003_INTERFACE_ENABLE "${DOCSTR}" ON) +3. Read INSTALL_GUIDE.pdf and follow the installation instructions. Roughly, do the following: +Make a directory outside the sundials-5.8.0 folder and enter it +% mkdir builddir +% cd /builddir -set(DOCSTR "Enable Fortran 2003 modules") -option(BUILD_FORTRAN_MODULE_INTERFACE "${DOCSTR}" ON) +4. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, the following setting needs to be enabled: +% cmake ../sundials-5.8.0/ -DF2003_INTERFACE_ENABLE=ON -DBUILD_FORTRAN_MODULE_INTERFACE=ON -4. Read INSTALL_GUIDE.pdf and follow the installation instructions -5. The default compiler is gfortan. To change it, add the following option to cmake - -DCMAKE_Fortran_COMPILER=your_favorite_compiler - +5. The default compiler is gfortan. To change it, you could also add the following option to cmake + -DCMAKE_Fortran_COMPILER=gfortran +6. If the above went well, staying in the builddir directory run +% make + +7. Enter summa directory and build summa as in /docs/SUMMA_instatllation.md. You will have to edit the SUMMA Makefile as below additionally (as well as install required libraries and link them as directed in the Makefile). + 6. In SUMMA Makefile, define the SUNDIALS libraries (-lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod - -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) and path to include files. + -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) + +7. In SUMMA Makefile, add the INCLUDES for these modules from the path you installed them at, for example + INCLUDES = -I/my_SundialsInstation/builddir/fortran_STATIC + +8. Inside the /summa/build/ directory run +% make + From bf8e3efb1cd9a9858c3d7b1b683e9a9cfa4555e9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 16 Nov 2021 11:51:29 +0900 Subject: [PATCH 0215/1472] More compiling files --- build/build_cmakeSundials_cop | 3 +++ build/build_cmakeSundials_mac | 2 ++ build/build_summa | 2 +- build/my_makefile_cop | 2 +- sundials/installation.txt | 12 +++++++----- 5 files changed, 14 insertions(+), 7 deletions(-) create mode 100755 build/build_cmakeSundials_cop create mode 100755 build/build_cmakeSundials_mac diff --git a/build/build_cmakeSundials_cop b/build/build_cmakeSundials_cop new file mode 100755 index 000000000..b4cb2be2c --- /dev/null +++ b/build/build_cmakeSundials_cop @@ -0,0 +1,3 @@ +cmake ../../sundials-5.8.0/ -DF2003_INTERFACE_ENABLE=ON -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=/cvmfs/soft.computecanada.ca/nix/var/nix/profiles/gcc-7.3.0/bin/gfortran + + diff --git a/build/build_cmakeSundials_mac b/build/build_cmakeSundials_mac new file mode 100755 index 000000000..ebdfad13e --- /dev/null +++ b/build/build_cmakeSundials_mac @@ -0,0 +1,2 @@ +cmake ../../sundials-5.8.0/ -DF2003_INTERFACE_ENABLE=ON -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran + diff --git a/build/build_summa b/build/build_summa index a94daa3ff..ced2c45b7 100755 --- a/build/build_summa +++ b/build/build_summa @@ -3,7 +3,7 @@ module load gcc/7.3.0 module load openblas module load netcdf-fortran/4.4.4 -# Specify the necessary paths for the compiler +# Specify the necessary paths for the compiler I PUT THESE IN THE MAKEFILE #export INCLUDES="-I$EBROOTNETCDFMINFORTRAN/include" #export LIBRARIES="-L$EBROOTNETCDFMINFORTRAN/lib64 -L$EBROOTOPENBLAS/lib -lnetcdff -lopenblas" diff --git a/build/my_makefile_cop b/build/my_makefile_cop index 935e0d5ef..9a286b5ba 100644 --- a/build/my_makefile_cop +++ b/build/my_makefile_cop @@ -50,7 +50,7 @@ FC = gfortran # Define the path for the compiler executable. This is the actual executable # that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin/gfortran-mp-11 # FC and FC_EXE have to be consistent -FC_EXE = /cvmfs/soft.computecanada.ca/easybuild/software/2020/Core/gcccore/9.3.0/bin/gfortran +FC_EXE = /cvmfs/soft.computecanada.ca/nix/var/nix/profiles/gcc-7.3.0/bin/gfortran # Define the NetCDF and LAPACK libraries and path to include files. # INCLUDES needs to be of the form (no quotes around the string): diff --git a/sundials/installation.txt b/sundials/installation.txt index e13c7db0a..55120efbd 100644 --- a/sundials/installation.txt +++ b/sundials/installation.txt @@ -14,20 +14,22 @@ Make a directory outside the sundials-5.8.0 folder and enter it % cmake ../sundials-5.8.0/ -DF2003_INTERFACE_ENABLE=ON -DBUILD_FORTRAN_MODULE_INTERFACE=ON -5. The default compiler is gfortan. To change it, you could also add the following option to cmake - -DCMAKE_Fortran_COMPILER=gfortran +5. The default compiler is gfortan. To change it (it should be the same as the netcdf build and the later summa build), you could also add the following option to cmake + -DCMAKE_Fortran_COMPILER=g/cvmfs/soft.computecanada.ca/nix/var/nix/profiles/gcc-7.3.0/bin/gfortran 6. If the above went well, staying in the builddir directory run % make 7. Enter summa directory and build summa as in /docs/SUMMA_instatllation.md. You will have to edit the SUMMA Makefile as below additionally (as well as install required libraries and link them as directed in the Makefile). + +8. In SUMMA Makefile, define the FC_EXE to be the same compiler as you used for sundials and netcdf -6. In SUMMA Makefile, define the SUNDIALS libraries (-lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod +9. In SUMMA Makefile, define the SUNDIALS libraries (-lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) -7. In SUMMA Makefile, add the INCLUDES for these modules from the path you installed them at, for example +10. In SUMMA Makefile, add the INCLUDES for these modules from the path you installed them at, for example INCLUDES = -I/my_SundialsInstation/builddir/fortran_STATIC -8. Inside the /summa/build/ directory run +11. Inside the /summa/build/ directory run % make From b15fb5819aa919f6b3abbbbd72c7630a1a9ea66e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 16 Nov 2021 16:16:48 +0900 Subject: [PATCH 0216/1472] more makefile stuff --- build/{build_cmakeSundials_mac => build_cmakeSundials} | 3 ++- build/build_cmakeSundials_cop | 3 --- build/build_summa | 6 +++--- build/my_makefile_cop | 2 +- sundials/installation.txt | 4 ++-- 5 files changed, 8 insertions(+), 10 deletions(-) rename build/{build_cmakeSundials_mac => build_cmakeSundials} (51%) delete mode 100755 build/build_cmakeSundials_cop diff --git a/build/build_cmakeSundials_mac b/build/build_cmakeSundials similarity index 51% rename from build/build_cmakeSundials_mac rename to build/build_cmakeSundials index ebdfad13e..fc19788f7 100755 --- a/build/build_cmakeSundials_mac +++ b/build/build_cmakeSundials @@ -1,2 +1,3 @@ -cmake ../../sundials-5.8.0/ -DF2003_INTERFACE_ENABLE=ON -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran +cmake ../../sundials-5.8.0/ -DF2003_INTERFACE_ENABLE=ON -DBUILD_FORTRAN_MODULE_INTERFACE=ON + diff --git a/build/build_cmakeSundials_cop b/build/build_cmakeSundials_cop deleted file mode 100755 index b4cb2be2c..000000000 --- a/build/build_cmakeSundials_cop +++ /dev/null @@ -1,3 +0,0 @@ -cmake ../../sundials-5.8.0/ -DF2003_INTERFACE_ENABLE=ON -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=/cvmfs/soft.computecanada.ca/nix/var/nix/profiles/gcc-7.3.0/bin/gfortran - - diff --git a/build/build_summa b/build/build_summa index ced2c45b7..483460dfa 100755 --- a/build/build_summa +++ b/build/build_summa @@ -1,7 +1,7 @@ -module load nixpkgs/16.09 -module load gcc/7.3.0 +module load StdEnv/2020 +module load gcc/9.3.0 module load openblas -module load netcdf-fortran/4.4.4 +module load netcdf-fortran/4.5.2 # Specify the necessary paths for the compiler I PUT THESE IN THE MAKEFILE #export INCLUDES="-I$EBROOTNETCDFMINFORTRAN/include" diff --git a/build/my_makefile_cop b/build/my_makefile_cop index 9a286b5ba..481322783 100644 --- a/build/my_makefile_cop +++ b/build/my_makefile_cop @@ -50,7 +50,7 @@ FC = gfortran # Define the path for the compiler executable. This is the actual executable # that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin/gfortran-mp-11 # FC and FC_EXE have to be consistent -FC_EXE = /cvmfs/soft.computecanada.ca/nix/var/nix/profiles/gcc-7.3.0/bin/gfortran +FC_EXE = gfortran # Define the NetCDF and LAPACK libraries and path to include files. # INCLUDES needs to be of the form (no quotes around the string): diff --git a/sundials/installation.txt b/sundials/installation.txt index 55120efbd..4fa62d844 100644 --- a/sundials/installation.txt +++ b/sundials/installation.txt @@ -14,8 +14,8 @@ Make a directory outside the sundials-5.8.0 folder and enter it % cmake ../sundials-5.8.0/ -DF2003_INTERFACE_ENABLE=ON -DBUILD_FORTRAN_MODULE_INTERFACE=ON -5. The default compiler is gfortan. To change it (it should be the same as the netcdf build and the later summa build), you could also add the following option to cmake - -DCMAKE_Fortran_COMPILER=g/cvmfs/soft.computecanada.ca/nix/var/nix/profiles/gcc-7.3.0/bin/gfortran +5. The default compiler is gfortan. To change it (it should be the same as the netcdf build and the later summa build), you could also add the following option to cmake with your "my_gfortran" + -DCMAKE_Fortran_COMPILER=my_gfortran 6. If the above went well, staying in the builddir directory run % make From 5b97b974089de4241750fb0a461311b670102a2c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 16 Nov 2021 16:39:28 +0900 Subject: [PATCH 0217/1472] more --- build/build_cmakeSundials | 2 +- build/build_summa | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/build_cmakeSundials b/build/build_cmakeSundials index fc19788f7..d5cd32d38 100755 --- a/build/build_cmakeSundials +++ b/build/build_cmakeSundials @@ -1,3 +1,3 @@ -cmake ../../sundials-5.8.0/ -DF2003_INTERFACE_ENABLE=ON -DBUILD_FORTRAN_MODULE_INTERFACE=ON +cmake ../../sundials-5.8.0/ -DF2003_INTERFACE_ENABLE=ON -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran diff --git a/build/build_summa b/build/build_summa index 483460dfa..dbf8eeee3 100755 --- a/build/build_summa +++ b/build/build_summa @@ -1,6 +1,6 @@ module load StdEnv/2020 module load gcc/9.3.0 -module load openblas +module load openblas/0.3.17 module load netcdf-fortran/4.5.2 # Specify the necessary paths for the compiler I PUT THESE IN THE MAKEFILE From 3abdfafb28df643f5ed6f9fa86679cac077cf822 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 16 Nov 2021 16:51:09 +0900 Subject: [PATCH 0218/1472] more --- build/build_cmakeSundials | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/build_cmakeSundials b/build/build_cmakeSundials index d5cd32d38..222464182 100755 --- a/build/build_cmakeSundials +++ b/build/build_cmakeSundials @@ -1,3 +1,3 @@ -cmake ../../sundials-5.8.0/ -DF2003_INTERFACE_ENABLE=ON -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran +cmake ../sundials-5.8.0/ -DF2003_INTERFACE_ENABLE=ON -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran From a1ef9da805f200cdb181f641b0314db3af657fee Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 16 Nov 2021 16:53:59 +0900 Subject: [PATCH 0219/1472] more --- build/build_summa | 4 ---- sundials/installation.txt | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/build/build_summa b/build/build_summa index dbf8eeee3..ad69c96b7 100755 --- a/build/build_summa +++ b/build/build_summa @@ -3,8 +3,4 @@ module load gcc/9.3.0 module load openblas/0.3.17 module load netcdf-fortran/4.5.2 -# Specify the necessary paths for the compiler I PUT THESE IN THE MAKEFILE -#export INCLUDES="-I$EBROOTNETCDFMINFORTRAN/include" -#export LIBRARIES="-L$EBROOTNETCDFMINFORTRAN/lib64 -L$EBROOTOPENBLAS/lib -lnetcdff -lopenblas" - make -f my_makefile_cop diff --git a/sundials/installation.txt b/sundials/installation.txt index 4fa62d844..1f3f5ae16 100644 --- a/sundials/installation.txt +++ b/sundials/installation.txt @@ -10,7 +10,7 @@ Make a directory outside the sundials-5.8.0 folder and enter it % mkdir builddir % cd /builddir -4. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, the following setting needs to be enabled: +4. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, the following setting needs to be enabled while running the cmake inside the builddir: % cmake ../sundials-5.8.0/ -DF2003_INTERFACE_ENABLE=ON -DBUILD_FORTRAN_MODULE_INTERFACE=ON From ffaafe67d68197086ded0106cb1d7e72cc8ce5af Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 16 Nov 2021 18:27:44 +0900 Subject: [PATCH 0220/1472] sorting out the build --- build/Makefile | 27 +++++++++++++++----------- build/build_cmakeSundials | 3 --- build/build_cmakeSundials_cop | 5 +++++ build/build_cmakeSundials_mac | 5 +++++ build/{build_summa => build_summa_cop} | 0 build/build_summa_mac | 1 + build/my_makefile_cop | 19 ++++++++++++------ build/my_makefile_mac | 19 ++++++++++++------ 8 files changed, 53 insertions(+), 26 deletions(-) delete mode 100755 build/build_cmakeSundials create mode 100755 build/build_cmakeSundials_cop create mode 100755 build/build_cmakeSundials_mac rename build/{build_summa => build_summa_cop} (100%) create mode 100755 build/build_summa_mac diff --git a/build/Makefile b/build/Makefile index c4c027bdf..3fa063e95 100644 --- a/build/Makefile +++ b/build/Makefile @@ -1,5 +1,5 @@ #======================================================================== -# Makefile to compile SUMMA +# Makefile to compile SUMMA SUNDIALS #======================================================================== # # Recommended use: Copy this file to Makefile.local, edit it to your @@ -38,7 +38,7 @@ # Define core directory below which everything resides. This is the # parent directory of the 'build' directory -F_MASTER = /home/stiff/summa +F_MASTER = /home/summa # Define the Fortran Compiler. If you are using gfortran, then this needs # to be version 6 or higher. This variable is simply used to select the right @@ -48,9 +48,9 @@ F_MASTER = /home/stiff/summa FC = gfortran # Define the path for the compiler executable. This is the actual executable -# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin-gfortran-mp-6 +# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin/gfortran-mp-11 # FC and FC_EXE have to be consistent -FC_EXE = /usr/bin/gfortran-7 +FC_EXE =/opt/local/bin/gfortran # Define the NetCDF and LAPACK libraries and path to include files. # INCLUDES needs to be of the form (no quotes around the string): @@ -59,11 +59,13 @@ FC_EXE = /usr/bin/gfortran-7 # LIBRARIES = '-L -lnetcdff -L -lblas -L -l' # If none of this makes sense, please talk to your system # administrator. -INCLUDES = -I/usr/include -I/usr/share/doc/liblapack3 -I/usr/lib/x86_64-linux-gnu -I/usr/local/include \ - -I/usr/local/lib -I/usr/local/fortran -LDFLAGS += -L/usr/local/lib -LIBRARIES = -llapack -lgfortran -lnetcdff -lnetcdf -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod \ - -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod +INCLUDES = -I/opt/local/include -I/opt/local/lib +LDFLAGS = -L/opt/local/lib +LIBRARIES = $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf + +DIR_SUNDIALS=/home/sundials/instdir +INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran +LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod # Eventually we plan move to a real configure script, but for now we like # to keep track of successful compilations of SUMMA on different platforms @@ -371,6 +373,9 @@ check: $(info FLAGS_NOAH : $(FLAGS_NOAH)) $(info FLAGS_COMM : $(FLAGS_COMM)) $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) + $(info SUNDIALSDIR: $(DIR_SUNDIALS)) + $(info INC_SUNDIALS: $(INC_SUNDIALS)) + $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) $(info) # update version information @@ -392,11 +397,11 @@ compile_comm: # compile SUMMA routines compile_summa: update_version $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ - $(INCLUDES) + $(INCLUDES) $(INC_SUNDIALS) # link routines link: - $(FC_EXE) -g *.o $(LIBRARIES) -o $(DRIVER__EX) + $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) # Remove object files clean: diff --git a/build/build_cmakeSundials b/build/build_cmakeSundials deleted file mode 100755 index 222464182..000000000 --- a/build/build_cmakeSundials +++ /dev/null @@ -1,3 +0,0 @@ -cmake ../sundials-5.8.0/ -DF2003_INTERFACE_ENABLE=ON -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran - - diff --git a/build/build_cmakeSundials_cop b/build/build_cmakeSundials_cop new file mode 100755 index 000000000..bfe906aee --- /dev/null +++ b/build/build_cmakeSundials_cop @@ -0,0 +1,5 @@ +# run cp build_cmakeSundials_cop ../../sundials/builddir/build_cmake on this and run from other directory + +cmake ../../sundials-5.8.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir/examples + + diff --git a/build/build_cmakeSundials_mac b/build/build_cmakeSundials_mac new file mode 100755 index 000000000..e9fef9b8d --- /dev/null +++ b/build/build_cmakeSundials_mac @@ -0,0 +1,5 @@ +# run cp build_cmakeSundials_mac ../../sundials/builddir/build_cmake on this and run from other directory + +cmake ../../sundials-5.8.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/Users/amedin/Research/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/Users/amedin/Research/SummaSundials/sundials/instdir/examples + + diff --git a/build/build_summa b/build/build_summa_cop similarity index 100% rename from build/build_summa rename to build/build_summa_cop diff --git a/build/build_summa_mac b/build/build_summa_mac new file mode 100755 index 000000000..cebfebe94 --- /dev/null +++ b/build/build_summa_mac @@ -0,0 +1 @@ +make -f my_makefile_mac diff --git a/build/my_makefile_cop b/build/my_makefile_cop index 481322783..607b3ab34 100644 --- a/build/my_makefile_cop +++ b/build/my_makefile_cop @@ -1,5 +1,5 @@ #======================================================================== -# Makefile to compile SUMMA +# Makefile to compile SUMMA SUNDIALS #======================================================================== # # Recommended use: Copy this file to Makefile.local, edit it to your @@ -59,9 +59,13 @@ FC_EXE = gfortran # LIBRARIES = '-L -lnetcdff -L -lblas -L -l' # If none of this makes sense, please talk to your system # administrator. -INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include -I/globalhome/gwu479/HPC/SummaSundials/builddir/fortran_STATIC -LDFLAGS = -L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib -LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod +INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include +LDFLAGS = -L$(EBROOTOPENBLAS)/lib +LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas + +DIR_SUNDIALS=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir +INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran +LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod # Eventually we plan move to a real configure script, but for now we like # to keep track of successful compilations of SUMMA on different platforms @@ -369,6 +373,9 @@ check: $(info FLAGS_NOAH : $(FLAGS_NOAH)) $(info FLAGS_COMM : $(FLAGS_COMM)) $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) + $(info SUNDIALSDIR: $(DIR_SUNDIALS)) + $(info INC_SUNDIALS: $(INC_SUNDIALS)) + $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) $(info) # update version information @@ -390,11 +397,11 @@ compile_comm: # compile SUMMA routines compile_summa: update_version $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ - $(INCLUDES) + $(INCLUDES) $(INC_SUNDIALS) # link routines link: - $(FC_EXE) -g *.o $(LIBRARIES) -o $(DRIVER__EX) + $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) # Remove object files clean: diff --git a/build/my_makefile_mac b/build/my_makefile_mac index b642dfccf..f3132ac15 100644 --- a/build/my_makefile_mac +++ b/build/my_makefile_mac @@ -1,5 +1,5 @@ #======================================================================== -# Makefile to compile SUMMA +# Makefile to compile SUMMA SUNDIALS #======================================================================== # # Recommended use: Copy this file to Makefile.local, edit it to your @@ -59,9 +59,13 @@ FC_EXE =/opt/local/bin/gfortran # LIBRARIES = '-L -lnetcdff -L -lblas -L -l' # If none of this makes sense, please talk to your system # administrator. -INCLUDES = -I/opt/local/include -I/opt/local/lib -I/usr/local/include -I/usr/local/lib -I/Users/amedin/Research/SummaSundials/builddir/fortran_STATIC -LDFLAGS = -L/opt/local/lib -L/usr/local/lib -LIBRARIES = $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod +INCLUDES = -I/opt/local/include -I/opt/local/lib +LDFLAGS = -L/opt/local/lib +LIBRARIES = $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf + +DIR_SUNDIALS=/Users/amedin/Research/SummaSundials/sundials/instdir +INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran +LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod # Eventually we plan move to a real configure script, but for now we like # to keep track of successful compilations of SUMMA on different platforms @@ -369,6 +373,9 @@ check: $(info FLAGS_NOAH : $(FLAGS_NOAH)) $(info FLAGS_COMM : $(FLAGS_COMM)) $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) + $(info SUNDIALSDIR: $(DIR_SUNDIALS)) + $(info INC_SUNDIALS: $(INC_SUNDIALS)) + $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) $(info) # update version information @@ -390,11 +397,11 @@ compile_comm: # compile SUMMA routines compile_summa: update_version $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ - $(INCLUDES) + $(INCLUDES) $(INC_SUNDIALS) # link routines link: - $(FC_EXE) -g *.o $(LIBRARIES) -o $(DRIVER__EX) + $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) # Remove object files clean: From 809ec0213ac81a91582bd44920c46749f3f334a5 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 16 Nov 2021 18:28:03 +0900 Subject: [PATCH 0221/1472] oops --- sundials/installation.txt | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/sundials/installation.txt b/sundials/installation.txt index 1f3f5ae16..28a228f3e 100644 --- a/sundials/installation.txt +++ b/sundials/installation.txt @@ -6,29 +6,30 @@ % tar -xzf sundials-5.8.0.tar.gz 3. Read INSTALL_GUIDE.pdf and follow the installation instructions. Roughly, do the following: -Make a directory outside the sundials-5.8.0 folder and enter it +Make a directory outside the sundials-5.8.0 folder and enter it, and make the install and build dirs, enter the build dir. +% mkdir sundials +% cd sundials +% mkdir instdir % mkdir builddir % cd /builddir -4. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, the following setting needs to be enabled while running the cmake inside the builddir: -% cmake ../sundials-5.8.0/ -DF2003_INTERFACE_ENABLE=ON -DBUILD_FORTRAN_MODULE_INTERFACE=ON +4. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, the following setting needs to be enabled while running the cmake inside the builddir, using your home directory as $(YOUR_HOME) +% cmake ../sundials-5.8.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON DCMAKE_INSTALL_PREFIX=$(YOUR_HOME)/sundials/instdir -DEXAMPLES_INSTALL_PATH=$(YOUR_HOME)/sundials/instdir/examples -5. The default compiler is gfortan. To change it (it should be the same as the netcdf build and the later summa build), you could also add the following option to cmake with your "my_gfortran" - -DCMAKE_Fortran_COMPILER=my_gfortran +5. The default compiler is gfortan. To change it (it should be the same as the netcdf build and the later summa build), you could also add the following option to cmake with your $(YOUR_GFORTRAN) + -DCMAKE_Fortran_COMPILER=$(YOUR_GFORTRAN) 6. If the above went well, staying in the builddir directory run % make +% make install 7. Enter summa directory and build summa as in /docs/SUMMA_instatllation.md. You will have to edit the SUMMA Makefile as below additionally (as well as install required libraries and link them as directed in the Makefile). 8. In SUMMA Makefile, define the FC_EXE to be the same compiler as you used for sundials and netcdf -9. In SUMMA Makefile, define the SUNDIALS libraries (-lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod - -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) - -10. In SUMMA Makefile, add the INCLUDES for these modules from the path you installed them at, for example - INCLUDES = -I/my_SundialsInstation/builddir/fortran_STATIC +9. In SUMMA Makefile, define the SUNDIALS installation directory + DIR_SUNDIALS=$(YOUR_HOME)/sundials/instdir 11. Inside the /summa/build/ directory run % make From cce0d5f8ea755cc84e0e7708e25a57e22ca30d19 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 16 Nov 2021 19:04:34 +0900 Subject: [PATCH 0222/1472] more --- build/my_makefile_cop | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/my_makefile_cop b/build/my_makefile_cop index 607b3ab34..663f6cac1 100644 --- a/build/my_makefile_cop +++ b/build/my_makefile_cop @@ -60,12 +60,12 @@ FC_EXE = gfortran # If none of this makes sense, please talk to your system # administrator. INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include -LDFLAGS = -L$(EBROOTOPENBLAS)/lib +LDFLAGS = -L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas DIR_SUNDIALS=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran -LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod +LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod # Eventually we plan move to a real configure script, but for now we like # to keep track of successful compilations of SUMMA on different platforms From eddf26a25bf4b7597c86a6fd833fea6b30d2153f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 16 Nov 2021 21:11:40 +0900 Subject: [PATCH 0223/1472] more --- build/Makefile | 6 ++++-- build/my_makefile_cop | 8 +++++--- build/my_makefile_mac | 6 ++++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/build/Makefile b/build/Makefile index 3fa063e95..fa6a338b1 100644 --- a/build/Makefile +++ b/build/Makefile @@ -19,7 +19,9 @@ # * FC - compiler suite # * FC_EXE - compiler executable # * INCLUDES - path to include files -# * LIBRARIES - path to and libraries to include +# * ILDFLAGS - path to libraries to include +# * LIBRARIES - libraries to include +# * DIR_SUNDIALS - installation sundials directory # # Some further options can be specified for OpenMP, etc. See in Part 0 of # the Makefile. You don't need to make any changes in PART 1 and @@ -322,7 +324,7 @@ SUMMA_DRIVER= \ DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) # Define the executable -DRIVER__EX = summa.exe +DRIVER__EX = summa_sundials.exe # Define version number VERSIONFILE = $(DRIVER_DIR)/summaversion.inc diff --git a/build/my_makefile_cop b/build/my_makefile_cop index 663f6cac1..2f2c7a923 100644 --- a/build/my_makefile_cop +++ b/build/my_makefile_cop @@ -19,7 +19,9 @@ # * FC - compiler suite # * FC_EXE - compiler executable # * INCLUDES - path to include files -# * LIBRARIES - path to and libraries to include +# * ILDFLAGS - path to libraries to include +# * LIBRARIES - libraries to include +# * DIR_SUNDIALS - installation sundials directory # # Some further options can be specified for OpenMP, etc. See in Part 0 of # the Makefile. You don't need to make any changes in PART 1 and @@ -65,7 +67,7 @@ LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas DIR_SUNDIALS=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran -LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod +LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_nvecmanyvector -lsundials_fnvecmanyvector_mod -lsundials_ida -lsundials_fida_mod -lsundials_nvecserial -lsundials_fnvecserial_mod -lsundials_sunlinsoldense -lsundials_fsunlinsoldense_mod -lsundials_sunmatrixdense -lsundials_fsunmatrixdense_mod # Eventually we plan move to a real configure script, but for now we like # to keep track of successful compilations of SUMMA on different platforms @@ -322,7 +324,7 @@ SUMMA_DRIVER= \ DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) # Define the executable -DRIVER__EX = summa.exe +DRIVER__EX = summa_sundials.exe # Define version number VERSIONFILE = $(DRIVER_DIR)/summaversion.inc diff --git a/build/my_makefile_mac b/build/my_makefile_mac index f3132ac15..caaa10435 100644 --- a/build/my_makefile_mac +++ b/build/my_makefile_mac @@ -19,7 +19,9 @@ # * FC - compiler suite # * FC_EXE - compiler executable # * INCLUDES - path to include files -# * LIBRARIES - path to and libraries to include +# * ILDFLAGS - path to libraries to include +# * LIBRARIES - libraries to include +# * DIR_SUNDIALS - installation sundials directory # # Some further options can be specified for OpenMP, etc. See in Part 0 of # the Makefile. You don't need to make any changes in PART 1 and @@ -322,7 +324,7 @@ SUMMA_DRIVER= \ DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) # Define the executable -DRIVER__EX = summa.exe +DRIVER__EX = summa_sundials.exe # Define version number VERSIONFILE = $(DRIVER_DIR)/summaversion.inc From e5b8ebcbe03b4e2581af3196633475fe2e4ee544 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 16 Nov 2021 21:47:08 +0900 Subject: [PATCH 0224/1472] more --- build/my_makefile_cop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/my_makefile_cop b/build/my_makefile_cop index 2f2c7a923..27ae8119f 100644 --- a/build/my_makefile_cop +++ b/build/my_makefile_cop @@ -67,7 +67,7 @@ LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas DIR_SUNDIALS=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran -LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_nvecmanyvector -lsundials_fnvecmanyvector_mod -lsundials_ida -lsundials_fida_mod -lsundials_nvecserial -lsundials_fnvecserial_mod -lsundials_sunlinsoldense -lsundials_fsunlinsoldense_mod -lsundials_sunmatrixdense -lsundials_fsunmatrixdense_mod +LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod # Eventually we plan move to a real configure script, but for now we like # to keep track of successful compilations of SUMMA on different platforms From 759e33be59f7b165cac2f5ba03f9d7d4d3c2028e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 18 Nov 2021 23:33:10 +0900 Subject: [PATCH 0225/1472] end of installation --- sundials/installation.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sundials/installation.txt b/sundials/installation.txt index 28a228f3e..ad5c8f11c 100644 --- a/sundials/installation.txt +++ b/sundials/installation.txt @@ -34,3 +34,8 @@ Make a directory outside the sundials-5.8.0 folder and enter it, and make the in 11. Inside the /summa/build/ directory run % make +(12) On some installations you may need to add +LD_LIBRARY_PATH='$(YOUR_HOME)/sundials/instdir/lib64' +export LD_LIBRARY_PATH + + From 72f6d882665be8692fa4786007e7ec54f23fef60 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 23 Nov 2021 00:06:52 +0900 Subject: [PATCH 0226/1472] print statement for Jacobian in computeJacDAE --- build/source/dshare/globalData.f90 | 4 +- build/source/engine/computJacDAE.f90 | 54 ++-- build/source/engine/evalJac4IDA.f90 | 26 +- build/source/engine/solveByIDA.f90 | 274 ++++++++++----------- build/source/engine/systemSolvSundials.f90 | 61 ++--- 5 files changed, 210 insertions(+), 209 deletions(-) diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 index 04b7e4a1c..f1e714794 100644 --- a/build/source/dshare/globalData.f90 +++ b/build/source/dshare/globalData.f90 @@ -334,12 +334,12 @@ MODULE globalData ! output file information logical(lgt),dimension(maxvarFreq),save,public :: outFreq ! true if the output frequency is desired integer(i4b),dimension(maxvarFreq),save,public :: ncid ! netcdf output file id - + ! look-up values for the choice of the time zone information (formerly in modelDecisions module) integer(i4b),parameter,public :: ncTime=1 ! time zone information from NetCDF file (timeOffset = longitude/15. - ncTimeOffset) integer(i4b),parameter,public :: utcTime=2 ! all times in UTC (timeOffset = longitude/15. hours) integer(i4b),parameter,public :: localTime=3 ! all times local (timeOffset = 0) - + ! define fixed dimensions integer(i4b),parameter,public :: nBand=2 ! number of spectral bands integer(i4b),parameter,public :: nTimeDelay=2000 ! number of hours in the time delay histogram (default: ~1 season = 24*365/4) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index bd9ffd985..c914b382d 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -117,7 +117,7 @@ subroutine computJacDAE(& mLayerVolFracWatPrime, & ! intent(in) scalarCanopyTemp, & ! intent(in) scalarCanopyTempPrime, & ! intent(in) derivative value for temperature of the vegetation canopy (K) - scalarCanopyWatPrime, & ! intetn(in) + scalarCanopyWatPrime, & ! intetn(in) mLayerd2Theta_dTk2, & ! intetn(in) d2VolTot_d2Psi0, & ! intetn(in) dFracLiqSnow_dTk, & ! intent(in) @@ -150,14 +150,14 @@ subroutine computJacDAE(& real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! input: state variables real(rkind),intent(in) :: mLayerTemp(:) - real(rkind),intent(in) :: mLayerTempPrime(:) + real(rkind),intent(in) :: mLayerTempPrime(:) real(rkind),intent(in) :: mLayerMatricHeadPrime(:) real(rkind),intent(in) :: mLayerMatricHeadLiqPrime(:) - real(rkind),intent(in) :: mLayerd2Theta_dTk2(:) + real(rkind),intent(in) :: mLayerd2Theta_dTk2(:) real(rkind),intent(in) :: mLayerVolFracWatPrime(:) real(rkind),intent(in) :: scalarCanopyTemp real(rkind),intent(in) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) - real(rkind),intent(in) :: scalarCanopyWatPrime + real(rkind),intent(in) :: scalarCanopyWatPrime real(rkind),intent(in) :: d2VolTot_d2Psi0(:) real(rkind),intent(in) :: dFracLiqSnow_dTk(:) real(rkind),intent(in) :: d2Theta_dTkCanopy2 @@ -294,7 +294,7 @@ subroutine computJacDAE(& if(ixVegNrg/=integerMissing)then dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy ) * cj & + LH_fus*iden_water * scalarCanopyTempPrime * d2Theta_dTkCanopy2 & - + LH_fus * dFracLiqVeg_dTkCanopy * scalarCanopyWatPrime / canopyDepth ! volumetric heat capacity of the vegetation (J m-3 K-1) + + LH_fus * dFracLiqVeg_dTkCanopy * scalarCanopyWatPrime / canopyDepth ! volumetric heat capacity of the vegetation (J m-3 K-1) end if ! compute additional terms for the Jacobian for the snow-soil domain (excluding fluxes) @@ -305,11 +305,11 @@ subroutine computJacDAE(& case(iname_snow) dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_ice*mLayerdTheta_dTk(iLayer) ) * cj & + LH_fus*iden_ice * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & - + LH_fus*iden_ice * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) + + LH_fus*iden_ice * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) case(iname_soil) dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_ice*mLayerdTheta_dTk(iLayer) ) * cj & + LH_fus*iden_ice * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & - + LH_fus*iden_ice * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) + + LH_fus*iden_ice * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) end select end if end do @@ -318,15 +318,15 @@ subroutine computJacDAE(& do iLayer=1,nSoil if(ixSoilOnlyHyd(iLayer)/=integerMissing)then dMat(ixSoilOnlyHyd(iLayer)) = ( dVolTot_dPsi0(iLayer) + dCompress_dPsi(iLayer) ) * cj + d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) - + if(ixRichards==mixdform)then dMat(ixSoilOnlyHyd(iLayer)) = dMat(ixSoilOnlyHyd(iLayer)) + specificStorage * dVolTot_dPsi0(iLayer) * mLayerMatricHeadPrime(iLayer) / theta_sat(iLayer) end if - + end if end do - - + + ! define the form of the matrix select case(ixMatrix) @@ -339,7 +339,7 @@ subroutine computJacDAE(& case(ixBandMatrix) ! ixBandMatrix ixFullMatrix print *, 'banded jacobian matrix needs to be implemented' stop 1 - + ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* ! * PART 2: FULL MATRIX @@ -468,7 +468,7 @@ subroutine computJacDAE(& nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector if(nrgstate/=integerMissing)then ! (energy state for the current layer is within the state subset) - ! (cross-derivative terms for the current layer) + ! (cross-derivative terms for the current layer) aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_ice * cj & + LH_fus*iden_ice * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) @@ -518,7 +518,7 @@ subroutine computJacDAE(& if(computeBaseflow .and. nSoilOnlyHyd==nSoil)then do pLayer=1,nSoil qState = ixSoilOnlyHyd(pLayer) ! hydrology state index within the state subset - aJac(watState,qState) = aJac(watState,qState) + (dt/mLayerDepth(jLayer))*dBaseflow_dMatric(iLayer,pLayer) + aJac(watState,qState) = aJac(watState,qState) + (dt/mLayerDepth(jLayer))*dBaseflow_dMatric(iLayer,pLayer) end do endif @@ -528,7 +528,7 @@ subroutine computJacDAE(& ! ----- ! * liquid water fluxes for the aquifer... ! ---------------------------------------- - if(ixAqWat/=integerMissing) aJac(ixAqWat,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) * cj + if(ixAqWat/=integerMissing) aJac(ixAqWat,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) * cj ! ----- ! * derivative in liquid water fluxes w.r.t. temperature for the soil domain... @@ -594,33 +594,33 @@ subroutine computJacDAE(& write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(min(iJac1,nState):min(iJac2,nState),iLayer) end do end if - - - ! print*, '** analytical Jacobian (full):' - ! print *, 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) - ! do iLayer=min(iJac1,nState),min(iJac2,nState) - ! print *, iLayer, aJac(min(iJac1,nState):min(iJac2,nState),iLayer) - ! end do - - ! print *, '--------------------------------------------------------------' + + + print*, '** analytical Jacobian (full):' + write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=1,size(aJac,2)) + do iLayer=1,size(aJac,2) + write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(1:size(aJac,1),iLayer) + end do + + print *, '--------------------------------------------------------------' ! *** ! check case default; err=20; message=trim(message)//'unable to identify option for the type of matrix'; return end select ! type of matrix - + if(any(isNan(aJac)))then print *, '******************************* WE FOUND NAN IN JACOBIAN ************************************' stop 1 message=trim(message)//'we found NaN' - err=20; return + err=20; return endif ! end association to variables in the data structures end associate - + end subroutine computJacDAE diff --git a/build/source/engine/evalJac4IDA.f90 b/build/source/engine/evalJac4IDA.f90 index 5ebecd99b..f2c4ca256 100644 --- a/build/source/engine/evalJac4IDA.f90 +++ b/build/source/engine/evalJac4IDA.f90 @@ -16,7 +16,7 @@ module evalJac4IDA_module var_dlength, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions - + ! privacy implicit none @@ -37,7 +37,7 @@ module evalJac4IDA_module integer(c_int) function evalJac4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & sunmat_J, user_data, sunvec_temp1, sunvec_temp2, sunvec_temp3) & result(ierr) bind(C,name='evalJac4IDA') - + !======= Inclusions =========== use, intrinsic :: iso_c_binding use fsundials_nvector_mod @@ -57,26 +57,26 @@ integer(c_int) function evalJac4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & type(N_Vector) :: sunvec_yp ! derivative N_Vector type(N_Vector) :: sunvec_r ! residual N_Vector type(SUNMatrix) :: sunmat_J ! Jacobian SUNMatrix - type(c_ptr), value :: user_data ! user-defined data + type(c_ptr), value :: user_data ! user-defined data type(N_Vector) :: sunvec_temp1 ! temporary N_Vector type(N_Vector) :: sunvec_temp2 ! temporary N_Vector type(N_Vector) :: sunvec_temp3 ! temporary N_Vector - + ! pointers to data in SUNDIALS vectors real(rkind), pointer :: stateVec(:) ! state vector real(rkind), pointer :: stateVecPrime(:)! derivative of the state vector real(rkind), pointer :: rVec(:) ! residual vector real(rkind), pointer :: Jac(:,:) ! Jacobian matrix - type(eqnsData), pointer :: eqns_data ! equations data + type(eqnsData), pointer :: eqns_data ! equations data + - !======= Internals ============ - + ! get equations data from user-defined data call c_f_pointer(user_data, eqns_data) - - + + ! get data arrays from SUNDIALS vectors stateVec => FN_VGetArrayPointer(sunvec_y) stateVecPrime => FN_VGetArrayPointer(sunvec_yp) @@ -86,7 +86,7 @@ integer(c_int) function evalJac4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & ! compute Jacobian matrix call eval8JacDAE(& ! input: model control - cj, & ! intent(in): this scalar changes whenever the step size or method order changes + cj, & ! intent(in): this scalar changes whenever the step size or method order changes eqns_data%dt, & ! intent(in): data step eqns_data%nSnow, & ! intent(in): number of snow layers eqns_data%nSoil, & ! intent(in): number of soil layers @@ -110,15 +110,15 @@ integer(c_int) function evalJac4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & eqns_data%dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) ! output eqns_data%dMat, & ! intetn(inout): diagonal of the Jacobian matrix - Jac, & ! intent(out): Jacobain matrix + Jac, & ! intent(out): Jacobian matrix eqns_data%err,eqns_data%message) ! intent(out): error control - if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif + if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif ! return success ierr = 0 - return + return diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 06ef031bb..914c2e8c9 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -77,7 +77,7 @@ module solveByIDA_module ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: qbaseTopmodel ! TOPMODEL-ish baseflow parameterization - + ! privacy implicit none @@ -91,10 +91,10 @@ module solveByIDA_module !------------------- ! * public subroutine solveByIDA: solve F(y,y') = 0 by IDA (y is the state vector) ! ------------------ - subroutine solveByIDA( & + subroutine solveByIDA( & dt, & ! intent(in): data time step atol, & ! intent(in): absolute telerance - rtol, & ! intent(in): relative tolerance + rtol, & ! intent(in): relative tolerance nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers @@ -104,7 +104,7 @@ subroutine solveByIDA( & computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors - stateVecInit, & ! intent(in): initial state vector + stateVecInit, & ! intent(in): initial state vector sMul, & ! intent(inout): state vector multiplier (USEd in the residual calculations) dMat, & ! intent(inout): diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures @@ -122,13 +122,13 @@ subroutine solveByIDA( & flux_data, & ! intent(inout): model fluxes for a local HRU flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output + ! output ixSaturation, & ! intent(out) idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix - dt_out, & ! intent(out): time step + dt_out, & ! intent(out): time step stateVec, & ! intent(out): model state vector - stateVecPrime, & ! intent(out): derivative of model state vector + stateVecPrime, & ! intent(out): derivative of model state vector err,message & ! intent(out): error control ) @@ -145,7 +145,7 @@ subroutine solveByIDA( & USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver USE allocspace_module,only:allocLocal ! allocate local data structures - USE evalDAE4IDA_module,only:evalDAE4IDA ! DAE/ODE functions + USE evalDAE4IDA_module,only:evalDAE4IDA ! DAE/ODE functions USE evalJac4IDA_module,only:evalJac4IDA ! system Jacobian USE tol4IDA_module,only:computWeight4IDA ! weigth required for tolerances USE eval8DAE_module,only:eval8DAE ! residual of DAE @@ -158,7 +158,7 @@ subroutine solveByIDA( & !======= Declarations ========= implicit none - + ! -------------------------------------------------------------------------------------------------------------------------------- ! calling variables ! -------------------------------------------------------------------------------------------------------------------------------- @@ -191,11 +191,11 @@ subroutine solveByIDA( & type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: flux_temp ! model fluxes for a local HRU type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: flux_sum ! sum of fluxes + type(var_dlength),intent(inout) :: flux_sum ! sum of fluxes type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables real(rkind),intent(inout) :: mLayerCmpress_sum(:) ! sum of soil compress ! output: state vectors - integer(i4b),intent(out) :: ixSaturation ! index of the lowest saturated layer + integer(i4b),intent(out) :: ixSaturation ! index of the lowest saturated layer real(rkind),intent(inout) :: stateVec(:) ! model state vector (y) real(rkind),intent(inout) :: stateVecPrime(:) ! model state vector (y') logical(lgt),intent(out) :: idaSucceeds ! flag to indicate if IDA is successful @@ -203,7 +203,7 @@ subroutine solveByIDA( & ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message - + ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- @@ -215,12 +215,12 @@ subroutine solveByIDA( & type(SUNNonLinearSolver), pointer :: sunnonlin_NLS ! sundials nonlinear solver type(c_ptr) :: ida_mem ! IDA memory type(eqnsData), target :: eqns_data ! IDA type - integer(i4b) :: retval ! return value + integer(i4b) :: retval ! return value logical(lgt) :: feasible ! feasibility flag - real(qp) :: t0 ! staring time + real(qp) :: t0 ! staring time real(qp) :: dt_last(1) ! last time step integer(kind = 8) :: mu, lu ! in banded matrix mode - integer(i4b) :: iVar + integer(i4b) :: iVar logical(lgt) :: startQuadrature real(rkind) :: mLayerMatricHeadLiqPrev(nSoil) real(qp) :: h_init @@ -246,29 +246,29 @@ subroutine solveByIDA( & err=0; message="solveByIDA/" nState = nStat idaSucceeds = .true. - ! fill eqns_data which will be required later to call eval8DAE + ! fill eqns_data which will be required later to call eval8DAE eqns_data%dt = dt - eqns_data%nSnow = nSnow + eqns_data%nSnow = nSnow eqns_data%nSoil = nSoil eqns_data%nLayers = nLayers - eqns_data%nState = nState - eqns_data%ixMatrix = ixMatrix - eqns_data%firstSubStep = firstSubStep + eqns_data%nState = nState + eqns_data%ixMatrix = ixMatrix + eqns_data%firstSubStep = firstSubStep eqns_data%computeVegFlux = computeVegFlux eqns_data%scalarSolution = scalarSolution allocate( eqns_data%atol(nState) ) eqns_data%atol = atol - + allocate( eqns_data%rtol(nState) ) eqns_data%rtol = rtol - + allocate( eqns_data%sMul(nState) ) eqns_data%sMul = sMul - + allocate( eqns_data%dMat(nState) ) eqns_data%dMat = dMat - + ! allocate space for the temporary prognostic variable structure call allocLocal(prog_meta(:),eqns_data%prog_data,nSnow,nSoil,err,message) if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif @@ -288,48 +288,48 @@ subroutine solveByIDA( & call allocLocal(deriv_meta(:),eqns_data%deriv_data,nSnow,nSoil,err,message) if(err/=0)then; err=20; message=trim(message)//trim(message); return; end if eqns_data%deriv_data = deriv_data - + eqns_data%lookup_data = lookup_data eqns_data%type_data = type_data eqns_data%attr_data = attr_data - eqns_data%mpar_data = mpar_data - eqns_data%forc_data = forc_data + eqns_data%mpar_data = mpar_data + eqns_data%forc_data = forc_data eqns_data%bvar_data = bvar_data eqns_data%indx_data = indx_data - ! allocate space + ! allocate space if(model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel)then - allocate(eqns_data%dBaseflow_dMatric(nSoil,nSoil),stat=err) + allocate(eqns_data%dBaseflow_dMatric(nSoil,nSoil),stat=err) else - allocate(eqns_data%dBaseflow_dMatric(0,0),stat=err) + allocate(eqns_data%dBaseflow_dMatric(0,0),stat=err) end if - + allocate( eqns_data%mLayerMatricHeadLiqTrial(nSoil) ) allocate( eqns_data%mLayerMatricHeadTrial(nSoil) ) allocate( eqns_data%mLayerMatricHeadPrev(nSoil) ) - + allocate( eqns_data%mLayerVolFracWatTrial(nLayers) ) allocate( eqns_data%mLayerVolFracWatPrev(nLayers) ) - + allocate( eqns_data%mLayerTempTrial(nLayers) ) allocate( eqns_data%mLayerTempPrev(nLayers) ) - + allocate( eqns_data%mLayerVolFracIceTrial(nLayers) ) allocate( eqns_data%mLayerVolFracIcePrev(nLayers) ) - + allocate( eqns_data%mLayerVolFracLiqTrial(nLayers) ) allocate( eqns_data%mLayerVolFracLiqPrev(nLayers) ) - + allocate( eqns_data%mLayerEnthalpyTrial(nLayers) ) allocate( eqns_data%mLayerEnthalpyPrev(nLayers) ) - + allocate( eqns_data%fluxVec(nState) ) allocate( eqns_data%resSink(nState) ) - + startQuadrature = .true. - - + + ! create serial vectors sunvec_y => FN_VMake_Serial(nState, stateVec) @@ -337,21 +337,21 @@ subroutine solveByIDA( & sunvec_yp => FN_VMake_Serial(nState, stateVecPrime) if (.not. associated(sunvec_yp)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif - - ! Initialize solution vectors + + ! Initialize solution vectors call setInitialCondition(nState, stateVecInit, sunvec_y, sunvec_yp) ! Call FIDACreate and FIDAInit to initialize IDA memory ida_mem = FIDACreate() if (.not. c_associated(ida_mem)) then; err=20; message='solveByIDA: ida_mem = NULL'; return; endif - + eqns_data%ida_mem = ida_mem retval = FIDASetUserData(ida_mem, c_loc(eqns_data)) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetUserData'; return; endif - + t0 = 0._rkind retval = FIDAInit(ida_mem, c_funloc(evalDAE4IDA), t0, sunvec_y, sunvec_yp) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAInit'; return; endif @@ -359,7 +359,7 @@ subroutine solveByIDA( & ! set tolerances retval = FIDAWFtolerances(ida_mem, c_funloc(computWeight4IDA)) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAWFtolerances'; return; endif - + ! define the form of the matrix select case(ixMatrix) case(ixBandMatrix) @@ -371,7 +371,7 @@ subroutine solveByIDA( & ! Create banded SUNLinearSolver object sunlinsol_LS => FSUNLinSol_Band(sunvec_y, sunmat_A) if (.not. associated(sunlinsol_LS)) then; err=20; message='solveByIDA: sunlinsol = NULL'; return; endif - + case(ixFullMatrix) ! Create dense SUNMatrix for use in linear solves sunmat_A => FSUNDenseMatrix(nState, nState) @@ -380,36 +380,36 @@ subroutine solveByIDA( & ! Create dense SUNLinearSolver object sunlinsol_LS => FSUNDenseLinearSolver(sunvec_y, sunmat_A) if (.not. associated(sunlinsol_LS)) then; err=20; message='solveByIDA: sunlinsol = NULL'; return; endif - + ! check case default; err=20; message='solveByIDA: error in type of matrix'; return - + end select ! form of matrix ! Attach the matrix and linear solver retval = FIDASetLinearSolver(ida_mem, sunlinsol_LS, sunmat_A); if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetLinearSolver'; return; endif - + if(ixMatrix == ixFullMatrix)then - ! Set the user-supplied Jacobian routine + ! Set the user-supplied Jacobian routine retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetJacFn'; return; endif + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetJacFn'; return; endif endif ! Create Newton SUNNonlinearSolver object sunnonlin_NLS => FSUNNonlinSol_Newton(sunvec_y) - if (.not. associated(sunnonlin_NLS)) then; err=20; message='solveByIDA: sunnonlinsol = NULL'; return; endif - + if (.not. associated(sunnonlin_NLS)) then; err=20; message='solveByIDA: sunnonlinsol = NULL'; return; endif + ! Attach the nonlinear solver retval = FIDASetNonlinearSolver(ida_mem, sunnonlin_NLS) - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetNonlinearSolver'; return; endif - + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetNonlinearSolver'; return; endif + ! Enforce the solver to stop at the end of the data time step retval = FIDASetStopTime(ida_mem, dt) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetStopTime'; return; endif - + ! Set solver parameters such as maximum order, number of iterations, ... - call setSolverParams(dt, ida_mem, retval) + call setSolverParams(dt, ida_mem, retval) if (retval /= 0) then; err=20; message='solveByIDA: error in setSolverParams'; return; endif ! Disable error messages and warnings @@ -417,16 +417,16 @@ subroutine solveByIDA( & retval = FIDASetErrFile(ida_mem, c_null_ptr) retval = FIDASetNoInactiveRootWarn(ida_mem) endif - + ! need the following values for the first substep eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) eqns_data%scalarCanopyIcePrev = prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) - eqns_data%scalarCanopyLiqPrev = prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) + eqns_data%scalarCanopyLiqPrev = prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) - eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) - eqns_data%mLayerVolFracLiqPrev(:) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(:) - eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) + eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) + eqns_data%mLayerVolFracLiqPrev(:) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(:) + eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) eqns_data%scalarAquiferStoragePrev = prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) @@ -435,26 +435,26 @@ subroutine solveByIDA( & mLayerDepth = prog_data%var(iLookPROG%mLayerDepth)%dat scalarSnowDepth = prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) scalarSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) - + !********************************************************************************** !****************************** Main Solver *************************************** !************************* loop on one_step mode ********************************** - !********************************************************************************** - tret(1) = t0 - do while(tret(1) < dt) + !********************************************************************************** + tret(1) = t0 + do while(tret(1) < dt) eqns_data%firstFluxCall = .false. eqns_data%firstSplitOper = .true. ! call IDASolve - retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) + retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) if( retval < 0 )then idaSucceeds = .false. exit endif - - - ! get the last stepsize + + + ! get the last stepsize retval = FIDAGetLastStep(ida_mem, dt_last) - + ! compute the flux and the residual vector for a given state vector call eval8DAE(& ! input: model control @@ -494,7 +494,7 @@ subroutine solveByIDA( & eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) eqns_data%scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) eqns_data%scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - eqns_data%scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) + eqns_data%scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) eqns_data%scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): value for enthalpy of the vegetation canopy (J m-3) eqns_data%mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) @@ -507,9 +507,9 @@ subroutine solveByIDA( & eqns_data%mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) eqns_data%mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) eqns_data%mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - eqns_data%mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) - eqns_data%scalarAquiferStorageTrial, & ! intent(out): trial value of storage of water in the aquifer (m) - eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) + eqns_data%mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) + eqns_data%scalarAquiferStorageTrial, & ! intent(out): trial value of storage of water in the aquifer (m) + eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer @@ -518,23 +518,23 @@ subroutine solveByIDA( & eqns_data%fluxVec, & ! intent(out): flux vector eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation rVec, & ! intent(out): residual vector - eqns_data%err,eqns_data%message) ! intent(out): error control - - - ! sum of fluxes - do iVar=1,size(flux_meta) - flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * dt_last(1) + eqns_data%err,eqns_data%message) ! intent(out): error control + + + ! sum of fluxes + do iVar=1,size(flux_meta) + flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * dt_last(1) end do - - ! do iVar=1,size(flux_meta) + + ! do iVar=1,size(flux_meta) ! flux_data%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / tret(1) ! end do - + ! sum of mLayerCmpress mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) - - + + ! save required quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial @@ -548,16 +548,16 @@ subroutine solveByIDA( & eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial - - - if(checkSnow)then - mLayerDepth(:) = prog_data%var(iLookPROG%mLayerDepth)%dat(:) + + + if(checkSnow)then + mLayerDepth(:) = prog_data%var(iLookPROG%mLayerDepth)%dat(:) ! compute the melt in each snow and soil layer if(nSnow>0)then mLayerMeltFreeze(1:nSnow) = -( eqns_data%mLayerVolFracIceTrial(1:nSnow) - prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow) ) * iden_ice mLayerMeltFreeze(nSnow+1:nLayers) = -(eqns_data%mLayerVolFracIceTrial(nSnow+1:nLayers) - prog_data%var(iLookPROG%mLayerVolFracIce)%dat(nSnow+1:nLayers))*iden_water - endif - + endif + call computSnowDepth(& tret(1), & ! intent(in) eqns_data%nSnow, & ! intent(in) @@ -565,21 +565,21 @@ subroutine solveByIDA( & eqns_data%mLayerVolFracLiqTrial, & ! intent(inout) eqns_data%mLayerVolFracIceTrial, & ! intent(inout) eqns_data%mLayerTempTrial, & ! intent(in) - mLayerMeltFreeze, & ! intent(in) + mLayerMeltFreeze, & ! intent(in) eqns_data%mpar_data, & ! intent(in) ! output mLayerDepth, & ! intent(inout) ! error control err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if - - + + ! recompute snow depth and SWE if(eqns_data%nSnow > 0)then scalarSnowDepth = sum( mLayerDepth(1:nSnow) ) scalarSWE = sum( (eqns_data%mLayerVolFracLiqTrial(1:nSnow)*iden_water + eqns_data%mLayerVolFracIceTrial(1:nSnow)*iden_ice) * mLayerDepth(1:nSnow) ) end if - + ! check the need to merge snow layers tooMuchMelt = .false. if(eqns_data%nSnow>0)then @@ -588,19 +588,19 @@ subroutine solveByIDA( & ! compute the energy required to melt the top snow layer (J m-2) bulkDensity = eqns_data%mLayerVolFracIceTrial(1)*iden_ice + eqns_data%mLayerVolFracLiqTrial(1)*iden_water volEnthalpy = temp2ethpy(eqns_data%mLayerTempTrial(1),bulkDensity,eqns_data%mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1)) - if(-volEnthalpy < flux_data%var(iLookFLUX%mLayerNrgFlux)%dat(1)*tret(1)) tooMuchMelt = .true. + if(-volEnthalpy < flux_data%var(iLookFLUX%mLayerNrgFlux)%dat(1)*tret(1)) tooMuchMelt = .true. endif - - + + if(tooMuchMelt) exit - + divideLayer = .false. call needDivideLayer(& ! input/output: model data structures model_decisions, & ! intent(in): model decisions eqns_data%mpar_data, & ! intent(in): model parameters eqns_data%nSnow, & ! intent(in): number of snow layers - mLayerDepth, & ! intent(in): + mLayerDepth, & ! intent(in): scalarSnowDepth, & ! intent(in) ! output divideLayer, & ! intent(out): flag to denote that a layer was divided @@ -608,8 +608,8 @@ subroutine solveByIDA( & if(divideLayer .and. tret(1)>50) then exit endif - - + + mergedLayers = .false. call needMergeLayers(& ! input/output: model data structures @@ -621,39 +621,39 @@ subroutine solveByIDA( & ! output mergedLayers, & ! intent(out): flag to denote that layers were merged err,message) ! intent(out): error control - + if(mergedLayers .and. tret(1)>50) exit - + endif ! checkSnow - + end do ! while loop on one_step mode - + !****************************** End of Main Solver *************************************** - - + + err = eqns_data%err - message = eqns_data%message + message = eqns_data%message if( .not. feasible) idaSucceeds = .false. - + if(idaSucceeds)then - ! copy to output data - diag_data = eqns_data%diag_data - flux_data = eqns_data%flux_data + ! copy to output data + diag_data = eqns_data%diag_data + flux_data = eqns_data%flux_data deriv_data = eqns_data%deriv_data ixSaturation = eqns_data%ixSaturation dt_out = tret(1) - endif - + endif - ! free memory + + ! free memory deallocate(eqns_data%sMul) deallocate(eqns_data%dMat) deallocate(eqns_data%dBaseflow_dMatric) deallocate(eqns_data%mLayerMatricHeadLiqTrial) deallocate(eqns_data%mLayerMatricHeadTrial) deallocate(eqns_data%mLayerMatricHeadPrev) - deallocate( eqns_data%fluxVec ) + deallocate( eqns_data%fluxVec ) deallocate( eqns_data%resSink ) deallocate( eqns_data%mLayerVolFracWatTrial ) deallocate( eqns_data%mLayerVolFracWatPrev ) @@ -664,17 +664,17 @@ subroutine solveByIDA( & deallocate( eqns_data%mLayerVolFracLiqPrev ) deallocate( eqns_data%mLayerEnthalpyTrial ) deallocate( eqns_data%mLayerEnthalpyPrev ) - + call FIDAFree(ida_mem) retval = FSUNNonlinSolFree(sunnonlin_NLS) retval = FSUNLinSolFree(sunlinsol_LS) call FSUNMatDestroy(sunmat_A) call FN_VDestroy(sunvec_y) call FN_VDestroy(sunvec_yp) - + end subroutine solveByIDA - + ! ---------------------------------------------------------------- ! SetInitialCondition: routine to initialize u and up vectors. @@ -714,7 +714,7 @@ subroutine setInitialCondition(neq, y, sunvec_u, sunvec_up) end subroutine setInitialCondition ! ---------------------------------------------------------------- -! setSolverParams: private routine to set paprmeters in ida solver +! setSolverParams: private routine to set parameters in ida solver ! ---------------------------------------------------------------- subroutine setSolverParams(dt,ida_mem,retval) !======= Inclusions =========== @@ -723,56 +723,56 @@ subroutine setSolverParams(dt,ida_mem,retval) implicit none real(rkind),intent(in) :: dt ! time step - type(c_ptr),intent(inout) :: ida_mem ! IDA memory + type(c_ptr),intent(inout) :: ida_mem ! IDA memory integer(i4b),intent(out) :: retval ! return value real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 integer,parameter :: max_order = 1 ! maximum BDF order, default = 5 - integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 + integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 - integer(kind = 8),parameter :: max_step = 999999 ! maximum number of steps, dafault = 500 + integer(kind = 8),parameter :: max_step = 999999 ! maximum number of steps, dafault = 500 real(qp),parameter :: h_init = 0 ! initial stepsize real(qp) :: h_max ! maximum stepsize, dafault = infinity ! Set the maximum BDF order - retval = FIDASetMaxOrd(ida_mem, max_order) + retval = FIDASetMaxOrd(ida_mem, max_order) if (retval /= 0) return - + ! Set Coeff. in the nonlinear convergence test retval = FIDASetNonlinConvCoef(ida_mem, coef_nonlin) if (retval /= 0) return - + ! Set maximun number of nonliear iterations retval = FIDASetMaxNonlinIters(ida_mem, nonlin_iter) if (retval /= 0) return - + ! Set maximum number of convergence test failures retval = FIDASetMaxConvFails(ida_mem, convtest_fail) if (retval /= 0) return - + ! Set maximum number of error test failures retval = FIDASetMaxErrTestFails(ida_mem, acurtest_fail) if (retval /= 0) return - + ! Set maximum number of steps retval = FIDASetMaxNumSteps(ida_mem, max_step) if (retval /= 0) return - + ! Set maximum stepsize h_max = dt retval = FIDASetMaxStep(ida_mem, h_max) if (retval /= 0) return - + ! Set initial stepsize retval = FIDASetInitStep(ida_mem, h_init) if (retval /= 0) return - + ! scalling on 1 and off 0 ! retval = FIDASetLinearSolutionScaling(ida_mem, 0) ! if (retval /= 0) return - -end subroutine setSolverParams + +end subroutine setSolverParams ! ********************************************************************************************************* ! private subroutine implctMelt: compute melt of the "snow without a layer" @@ -835,7 +835,7 @@ subroutine implctMelt(& scalarSfcMeltPond = 0._rkind ! kg m-2 end if ! (if the "snow without a layer" exists) - end subroutine implctMelt + end subroutine implctMelt end module solveByIDA_module diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 4359799f0..61897191e 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -75,7 +75,7 @@ module systemSolvSundials_module qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization bigBucket, & ! a big bucket (lumped aquifer model) noExplicit ! no explicit groundwater parameterization - + ! safety: set private unless specified otherwise implicit none @@ -134,7 +134,7 @@ subroutine systemSolvSundials(& USE summaSolve_module,only:summaSolve ! calculate the iteration increment, evaluate the new state, and refine if necessary USE getVectorz_module,only:getScaling ! get the scaling vectors USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy - USE tol4IDA_module,only:popTol4IDA ! pop tolerances + USE tol4IDA_module,only:popTol4IDA ! pop tolerances USE solveByIDA_module,only:solveByIDA ! solve DAE by IDA USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy use, intrinsic :: iso_c_binding @@ -186,7 +186,7 @@ subroutine systemSolvSundials(& real(rkind),parameter :: tempAccelerate=0.00_rkind ! factor to force initial canopy temperatures to be close to air temperature real(rkind),parameter :: xMinCanopyWater=0.0001_rkind ! minimum value to initialize canopy water (kg m-2) real(rkind),parameter :: tinyStep=0.000001_rkind ! stupidly small time step (s) - + ! ------------------------------------------------------------------------------------------------------ ! * model solver ! ------------------------------------------------------------------------------------------------------ @@ -205,11 +205,11 @@ subroutine systemSolvSundials(& real(rkind) :: fOld ! function values (-); NOTE: dimensionless because scaled logical(lgt) :: feasible ! feasibility flag real(rkind) :: atol(nState) ! absolute telerance - real(rkind) :: rtol(nState) ! relative tolerance - type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a data step - integer(i4b) :: tol_iter ! iteration index + real(rkind) :: rtol(nState) ! relative tolerance + type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a data step + integer(i4b) :: tol_iter ! iteration index real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix - logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step + logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step ! --------------------------------------------------------------------------------------- @@ -319,8 +319,8 @@ subroutine systemSolvSundials(& if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) ! initialize the trial state vectors - stateVecTrial = stateVecInit - + stateVecTrial = stateVecInit + ! need to intialize canopy water at a positive value if(ixVegHyd/=integerMissing)then if(stateVecTrial(ixVegHyd) < xMinCanopyWater) stateVecTrial(ixVegHyd) = stateVecTrial(ixVegHyd) + xMinCanopyWater @@ -355,7 +355,7 @@ subroutine systemSolvSundials(& ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - + ! compute the flux and the residual vector for a given state vector ! NOTE 1: The derivatives computed in eval8summa are used to calculate the Jacobian matrix for the first iteration ! NOTE 2: The Jacobian matrix together with the residual vector is used to calculate the first iteration increment @@ -402,7 +402,7 @@ subroutine systemSolvSundials(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif - + ! copy over the initial flux structure since some model fluxes are not computed in the iterations do concurrent ( iVar=1:size(flux_meta) ) @@ -415,7 +415,7 @@ subroutine systemSolvSundials(& ! allocate space for mLayerCmpress_sum allocate( mLayerCmpress_sum(nSoil) ) - + ! check the need to merge snow layers @@ -430,7 +430,7 @@ subroutine systemSolvSundials(& err=-20; return ! negative error code to denote a warning endif endif - + ! get tolerance vectors call popTol4IDA(& ! input @@ -444,25 +444,25 @@ subroutine systemSolvSundials(& rtol, & ! intent(out): relative tolerances vector (mixed units) err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - + !------------------- ! * solving F(y,y') = 0 by IDA. Here, y is the state vector ! ------------------ - + do tol_iter=1,3 - + ! initialize flux_sum do concurrent ( iVar=1:size(flux_meta) ) flux_sum%var(iVar)%dat(:) = 0._rkind end do - + ! initialize sum of compression of the soil matrix mLayerCmpress_sum(:) = 0._rkind call solveByIDA(& dt, & ! intent (in) data time step atol, & ! intent (in) absolute telerance - rtol, & ! intent (in) relative tolerance + rtol, & ! intent (in) relative tolerance nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): number of snow+soil layers @@ -487,7 +487,7 @@ subroutine systemSolvSundials(& ! input-output: data structures diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) - flux_temp, & ! intent(inout): model fluxes for a local HRU + flux_temp, & ! intent(inout): model fluxes for a local HRU flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output @@ -498,19 +498,20 @@ subroutine systemSolvSundials(& stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) if (idaSucceeds)then exit else atol = atol * 0.1 rtol = rtol * 0.1 endif - + end do ! iteration over tolerances - - + + ! check if IDA is successful if( .not.idaSucceeds )then err = 20 @@ -518,25 +519,25 @@ subroutine systemSolvSundials(& ! reduceCoupledStep = .true. return endif - - ! compute average flux - do iVar=1,size(flux_meta) + + ! compute average flux + do iVar=1,size(flux_meta) flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / dt_out end do - + diag_data%var(iLookDIAG%mLayerCompress)%dat(:) = mLayerCmpress_sum(:) ! compute the total change in storage associated with compression of the soil matrix (kg m-2) diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sum(diag_data%var(iLookDIAG%mLayerCompress)%dat(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water - + ! save the computed solution stateVecTrial = stateVecNew - + ! free memory deallocate(mLayerCmpress_sum) - deallocate(dBaseflow_dMatric) + deallocate(dBaseflow_dMatric) ! end associate statements end associate globalVars From 975474f802449d6dbbc2875f005473bbbebbbbc7 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 23 Nov 2021 15:00:56 +0900 Subject: [PATCH 0227/1472] Don't print Jac in Summa code --- build/source/engine/computJacDAE.f90 | 12 ++++++------ sundials/installation.txt | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index c914b382d..eef3e7394 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -596,13 +596,13 @@ subroutine computJacDAE(& end if - print*, '** analytical Jacobian (full):' - write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=1,size(aJac,2)) - do iLayer=1,size(aJac,2) - write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(1:size(aJac,1),iLayer) - end do + !print*, '** analytical Jacobian (full):' + !write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=1,size(aJac,2)) + !do iLayer=1,size(aJac,2) + ! write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(1:size(aJac,1),iLayer) + !end do - print *, '--------------------------------------------------------------' + !print *, '--------------------------------------------------------------' ! *** ! check diff --git a/sundials/installation.txt b/sundials/installation.txt index ad5c8f11c..7a562bf32 100644 --- a/sundials/installation.txt +++ b/sundials/installation.txt @@ -34,7 +34,7 @@ Make a directory outside the sundials-5.8.0 folder and enter it, and make the in 11. Inside the /summa/build/ directory run % make -(12) On some installations you may need to add +(12) On some installations you may need to add to your .bashrc file (and run source .bashrc) LD_LIBRARY_PATH='$(YOUR_HOME)/sundials/instdir/lib64' export LD_LIBRARY_PATH From 64a56489776209d9d20b87e1d88ec0bd13e03411 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 23 Nov 2021 23:21:42 +0900 Subject: [PATCH 0228/1472] print Jac FD and Analytical by running one step --- build/source/engine/computJacDAE.f90 | 2 +- build/source/engine/solveByIDA.f90 | 21 +++++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index eef3e7394..7ad1fc1a6 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -596,7 +596,7 @@ subroutine computJacDAE(& end if - !print*, '** analytical Jacobian (full):' + print*, '** analytical Jacobian (full):' !write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=1,size(aJac,2)) !do iLayer=1,size(aJac,2) ! write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(1:size(aJac,1),iLayer) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 914c2e8c9..42e1da653 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -240,6 +240,7 @@ subroutine solveByIDA( & real(rkind) :: scalarSnowDepth real(rkind) :: scalarSWE real(rkind) :: mLayerMeltFreeze(nLayers) + integer(i4b) :: i ! ----------------------------------------------------------------------------------------------------- ! initialize error control @@ -329,7 +330,7 @@ subroutine solveByIDA( & startQuadrature = .true. - + do i = 1,2 ! create serial vectors sunvec_y => FN_VMake_Serial(nState, stateVec) @@ -392,7 +393,7 @@ subroutine solveByIDA( & if(ixMatrix == ixFullMatrix)then ! Set the user-supplied Jacobian routine - retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) + if (i==2) retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetJacFn'; return; endif endif @@ -436,6 +437,22 @@ subroutine solveByIDA( & scalarSnowDepth = prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) scalarSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) + tret(1) = t0 + eqns_data%firstFluxCall = .false. + eqns_data%firstSplitOper = .true. + retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) + if(i==1) then + print*, "Use Finite-Dif Jacobian" + call FIDAFree(ida_mem) + retval = FSUNNonlinSolFree(sunnonlin_NLS) + retval = FSUNLinSolFree(sunlinsol_LS) + call FSUNMatDestroy(sunmat_A) + call FN_VDestroy(sunvec_y) + call FN_VDestroy(sunvec_yp) + endif + if(i==2) print*, "Use Analytical Jacobian" + enddo + !********************************************************************************** !****************************** Main Solver *************************************** !************************* loop on one_step mode ********************************** From fd0aec574c89f802c9fd9164ed0f1e7462956704 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 27 Nov 2021 17:31:58 +0900 Subject: [PATCH 0229/1472] run as normal, no printing --- build/source/engine/computJacDAE.f90 | 2 +- build/source/engine/solveByIDA.f90 | 34 ++++++++++++++-------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 7ad1fc1a6..eef3e7394 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -596,7 +596,7 @@ subroutine computJacDAE(& end if - print*, '** analytical Jacobian (full):' + !print*, '** analytical Jacobian (full):' !write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=1,size(aJac,2)) !do iLayer=1,size(aJac,2) ! write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(1:size(aJac,1),iLayer) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 42e1da653..1dd9b249a 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -330,7 +330,7 @@ subroutine solveByIDA( & startQuadrature = .true. - do i = 1,2 + !do i = 1,2 ! create serial vectors sunvec_y => FN_VMake_Serial(nState, stateVec) @@ -393,7 +393,7 @@ subroutine solveByIDA( & if(ixMatrix == ixFullMatrix)then ! Set the user-supplied Jacobian routine - if (i==2) retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) + !if (i==2) retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetJacFn'; return; endif endif @@ -437,21 +437,21 @@ subroutine solveByIDA( & scalarSnowDepth = prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) scalarSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) - tret(1) = t0 - eqns_data%firstFluxCall = .false. - eqns_data%firstSplitOper = .true. - retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) - if(i==1) then - print*, "Use Finite-Dif Jacobian" - call FIDAFree(ida_mem) - retval = FSUNNonlinSolFree(sunnonlin_NLS) - retval = FSUNLinSolFree(sunlinsol_LS) - call FSUNMatDestroy(sunmat_A) - call FN_VDestroy(sunvec_y) - call FN_VDestroy(sunvec_yp) - endif - if(i==2) print*, "Use Analytical Jacobian" - enddo + !tret(1) = t0 + ! eqns_data%firstFluxCall = .false. + ! eqns_data%firstSplitOper = .true. + ! retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) + ! if(i==1) then + ! print*, "Use Finite-Dif Jacobian" + ! call FIDAFree(ida_mem) + ! retval = FSUNNonlinSolFree(sunnonlin_NLS) + ! retval = FSUNLinSolFree(sunlinsol_LS) + ! call FSUNMatDestroy(sunmat_A) + ! call FN_VDestroy(sunvec_y) + ! call FN_VDestroy(sunvec_yp) + ! endif + ! if(i==2) print*, "Use Analytical Jacobian" + !enddo !********************************************************************************** !****************************** Main Solver *************************************** From ebde5cc476c408dfada2a504815517f2e1e1b3dd Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 27 Nov 2021 17:55:10 +0900 Subject: [PATCH 0230/1472] oops last was FD --- build/source/engine/solveByIDA.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 1dd9b249a..8ba6cea1f 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -394,6 +394,7 @@ subroutine solveByIDA( & if(ixMatrix == ixFullMatrix)then ! Set the user-supplied Jacobian routine !if (i==2) retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) + retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetJacFn'; return; endif endif From af99dcbc1e5aaf8f9a42ba23f7d603925042c62c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 30 Nov 2021 11:27:08 +0900 Subject: [PATCH 0231/1472] printing indices --- build/source/engine/computJacDAE.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index eef3e7394..c63a44c41 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -352,7 +352,7 @@ subroutine computJacDAE(& message=trim(message)//'unexpected shape of the Jacobian matrix: expect aJac(nState,nState)' err=20; return end if - + print*, ixVegHyd,ixCasNrg,ixVegNrg,ixTopNrg, "indices" ! ----- ! * energy and liquid fluxes over vegetation... ! --------------------------------------------- From ba3d544b8258ad1c8ca60144e621db64c589e30b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 30 Nov 2021 11:42:16 +0900 Subject: [PATCH 0232/1472] more prints --- build/source/engine/computJacDAE.f90 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index c63a44c41..d06886f17 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -352,7 +352,7 @@ subroutine computJacDAE(& message=trim(message)//'unexpected shape of the Jacobian matrix: expect aJac(nState,nState)' err=20; return end if - print*, ixVegHyd,ixCasNrg,ixVegNrg,ixTopNrg, "indices" + ! ----- ! * energy and liquid fluxes over vegetation... ! --------------------------------------------- @@ -406,7 +406,7 @@ subroutine computJacDAE(& ! ------------------------------------------- if(nSnowSoilNrg>0)then do iLayer=1,nLayers ! loop through all layers in the snow+soil domain - + print*, ixSnowSoilNrg(iLayer),jState, "snowsoilindices" ! check if the state is in the subset if(ixSnowSoilNrg(iLayer)==integerMissing) cycle @@ -434,7 +434,7 @@ subroutine computJacDAE(& ! -------------------------------------------- if(nSnowOnlyHyd>0)then do iLayer=1,nSnow ! loop through layers in the snow domain - + print*, ixSnowOnlyHyd(iLayer),watState, "snowsoilindices" ! - check that the snow layer is desired if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle @@ -490,7 +490,7 @@ subroutine computJacDAE(& if(nSoilOnlyHyd>0)then do iLayer=1,nSoil - + print*, ixSoilOnlyHyd(iLayer),watState, "watsoilindices" ! - check that the soil layer is desired if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle @@ -534,8 +534,8 @@ subroutine computJacDAE(& ! * derivative in liquid water fluxes w.r.t. temperature for the soil domain... ! ----------------------------------------------------------------------------- if(nSoilOnlyHyd>0 .and. nSoilOnlyNrg>0)then + print*, ixVegHyd,ixCasNrg,ixVegNrg, ixTopNrg,watState,nrgState, "watvegindices" do iLayer=1,nSoilOnlyHyd - ! - check that the soil layer is desired if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle From 4e2db9b2a1def596aed527c4f5441ce91c86ebf8 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 30 Nov 2021 11:50:20 +0900 Subject: [PATCH 0233/1472] oops --- build/source/engine/computJacDAE.f90 | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index d06886f17..7bace4323 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -434,13 +434,13 @@ subroutine computJacDAE(& ! -------------------------------------------- if(nSnowOnlyHyd>0)then do iLayer=1,nSnow ! loop through layers in the snow domain - print*, ixSnowOnlyHyd(iLayer),watState, "snowsoilindices" + ! - check that the snow layer is desired if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle ! - define state indices for the current layer watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - + print*, ixSnowOnlyHyd(iLayer),watState, "snowsoilindices" ! compute factor to convert liquid water derivative to total water derivative select case( ixHydType(iLayer) ) case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) @@ -490,13 +490,13 @@ subroutine computJacDAE(& if(nSoilOnlyHyd>0)then do iLayer=1,nSoil - print*, ixSoilOnlyHyd(iLayer),watState, "watsoilindices" + ! - check that the soil layer is desired if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle ! - define state indices watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset - + print*, ixSoilOnlyHyd(iLayer),watState, "watsoilindices" ! - define indices of the soil layers jLayer = iLayer+nSnow ! index of layer in the snow+soil vector @@ -534,14 +534,13 @@ subroutine computJacDAE(& ! * derivative in liquid water fluxes w.r.t. temperature for the soil domain... ! ----------------------------------------------------------------------------- if(nSoilOnlyHyd>0 .and. nSoilOnlyNrg>0)then - print*, ixVegHyd,ixCasNrg,ixVegNrg, ixTopNrg,watState,nrgState, "watvegindices" do iLayer=1,nSoilOnlyHyd ! - check that the soil layer is desired if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle ! - define index of hydrology state variable within the state subset watState = ixSoilOnlyHyd(iLayer) - + print*, ixVegHyd,ixCasNrg,ixVegNrg, ixTopNrg,watState,nrgState, "watvegindices" ! - define indices of the soil layers jLayer = iLayer+nSnow ! index of layer in the snow+soil vector From ef4433b82eb6f60f1cee748db9b85ed9c20c2d4a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 30 Nov 2021 12:20:03 +0900 Subject: [PATCH 0234/1472] printing --- build/source/engine/computJacDAE.f90 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 7bace4323..066c66c50 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -406,13 +406,13 @@ subroutine computJacDAE(& ! ------------------------------------------- if(nSnowSoilNrg>0)then do iLayer=1,nLayers ! loop through all layers in the snow+soil domain - print*, ixSnowSoilNrg(iLayer),jState, "snowsoilindices" + ! check if the state is in the subset if(ixSnowSoilNrg(iLayer)==integerMissing) cycle ! - define index within the state subset and the full state vector jState = ixSnowSoilNrg(iLayer) ! index within the state subset - + print*, ixSnowSoilNrg(iLayer),jState, "snowsoilindices" ! - diagonal elements aJac(jState,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) @@ -440,7 +440,7 @@ subroutine computJacDAE(& ! - define state indices for the current layer watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - print*, ixSnowOnlyHyd(iLayer),watState, "snowsoilindices" + print*, ixSnowOnlyHyd(iLayer),watState, "snowwatindices" ! compute factor to convert liquid water derivative to total water derivative select case( ixHydType(iLayer) ) case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) @@ -467,7 +467,7 @@ subroutine computJacDAE(& ! (define the energy state) nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector if(nrgstate/=integerMissing)then ! (energy state for the current layer is within the state subset) - + print*, nrgState,watState, "snowwatenergyindices" ! (cross-derivative terms for the current layer) aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_ice * cj & + LH_fus*iden_ice * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) @@ -540,7 +540,7 @@ subroutine computJacDAE(& ! - define index of hydrology state variable within the state subset watState = ixSoilOnlyHyd(iLayer) - print*, ixVegHyd,ixCasNrg,ixVegNrg, ixTopNrg,watState,nrgState, "watvegindices" + ! - define indices of the soil layers jLayer = iLayer+nSnow ! index of layer in the snow+soil vector @@ -549,7 +549,7 @@ subroutine computJacDAE(& ! only compute derivatives if the energy state for the current layer is within the state subset if(nrgstate/=integerMissing)then - + print*, ixVegHyd,ixCasNrg,ixVegNrg, ixTopNrg,watState,nrgState, "watvegindices" ! - compute the Jacobian for the layer itself aJac(watState,nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance From b761882100256847acffad4f5a0c299cfa76ea61 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 30 Nov 2021 20:49:38 +0900 Subject: [PATCH 0235/1472] print more --- build/source/engine/computJacDAE.f90 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 066c66c50..b13a30d9e 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -475,7 +475,10 @@ subroutine computJacDAE(& ! (cross-derivative terms for the layer below) if(iLayer < nSnow)then + !aJac(ixSnowOnlyNrg(iLayer+1),watState) = (-1._rkind + mLayerFracLiqSnow(iLayer)+1)*LH_fus*iden_ice * cj & + ! + LH_fus*iden_ice * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! dF(below)/dLiq(above) -- K-1 aJac(ixSnowOnlyHyd(iLayer+1),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 + endif ! (if there is a water state in the layer below the current layer in the given state subset) endif ! (if the energy state for the current layer is within the state subset) From bd1d6e603de9e651c383346a0ff541a8d2868b85 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 30 Nov 2021 21:11:37 +0900 Subject: [PATCH 0236/1472] printing --- build/source/engine/computJacDAE.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index b13a30d9e..91b3b77a5 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -462,6 +462,7 @@ subroutine computJacDAE(& ! - compute cross-derivative terms for energy ! NOTE: increase in volumetric liquid water content balanced by a decrease in volumetric ice content + print*, nSnowOnlyNrg,ixSnowOnlyNrg(iLayer),watState, ixSnowOnlyHyd(iLayer+1),ixSnowOnlyNrg(iLayer+1), "maybeneedthesesnowwatind" if(nSnowOnlyNrg>0)then ! (define the energy state) @@ -565,7 +566,7 @@ subroutine computJacDAE(& endif aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) endif - + print*,mLayerdTheta_dTk(jLayer),"melt-freeze" ! melt-freeze: compute derivative in energy with respect to mass if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(nrgState,watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water*cj - LH_fus*iden_water * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content From ea20c7b87bdb8a6eedfc71401c1e19566c53a69a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 8 Dec 2021 08:59:16 +0900 Subject: [PATCH 0237/1472] Added timing --- build/source/dshare/get_ixname.f90 | 5 +++-- build/source/dshare/popMetadat.f90 | 3 ++- build/source/dshare/var_lookup.f90 | 7 ++++--- build/source/engine/coupled_em.f90 | 23 ++++++++++++++++++----- 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 87162458d..6d840ffd1 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -564,8 +564,9 @@ function get_ixdiag(varName) case('scalarVGn_m' ); get_ixdiag = iLookDIAG%scalarVGn_m ! van Genuchten "m" parameter (-) case('scalarKappa' ); get_ixdiag = iLookDIAG%scalarKappa ! constant in the freezing curve function (m K-1) case('scalarVolLatHt_fus' ); get_ixdiag = iLookDIAG%scalarVolLatHt_fus ! volumetric latent heat of fusion (J m-3) - ! number of function evaluations + ! timing information case('numFluxCalls' ); get_ixdiag = iLookDIAG%numFluxCalls ! number of flux calls (-) + case('wallClockTime' ); get_ixdiag = iLookDIAG%wallClockTime ! wall clock time (s) ! get to here if cannot find the variable case default get_ixdiag = integerMissing @@ -1011,7 +1012,7 @@ subroutine get_ixUnknown(varName,typeName,vDex,err,message) err=20;message=trim(message)//'variable '//trim(varName)//' is not found in any structure'; return end subroutine get_ixUnknown - + ! ******************************************************************************************************************* ! public function get_ixfreq: get the index of the named variables for the output frequencies ! ******************************************************************************************************************* diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index caeb95ce5..2c8f24f7b 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -426,8 +426,9 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%scalarVGn_m) = var_info('scalarVGn_m' , 'van Genuchten "m" parameter' , '-' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarKappa) = var_info('scalarKappa' , 'constant in the freezing curve function' , 'm K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarVolLatHt_fus) = var_info('scalarVolLatHt_fus' , 'volumetric latent heat of fusion' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! number of function evaluations + ! timing information diag_meta(iLookDIAG%numFluxCalls) = var_info('numFluxCalls' , 'number of flux calls' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%wallClockTime) = var_info('wallClockTime' , 'wall clock time' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! ----- ! * local model fluxes... diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index bb423d090..d2a89e8e6 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -441,8 +441,9 @@ MODULE var_lookup integer(i4b) :: scalarVGn_m = integerMissing ! van Genuchten "m" parameter (-) integer(i4b) :: scalarKappa = integerMissing ! constant in the freezing curve function (m K-1) integer(i4b) :: scalarVolLatHt_fus = integerMissing ! volumetric latent heat of fusion (J m-3) - ! number of function evaluations + ! timing information integer(i4b) :: numFluxCalls = integerMissing ! number of flux calls (-) + integer(i4b) :: wallClockTime = integerMissing ! wall clock time (s) endtype iLook_diag ! *********************************************************************************************************** @@ -769,7 +770,7 @@ MODULE var_lookup integer(i4b) :: enthalpy = integerMissing ! enthalpy (J m-3) integer(i4b) :: deriv2 = integerMissing ! second derivatives of the interpolating function endtype iLook_vLookup - + ! *********************************************************************************************************** ! (X) define data structures and maximum number of variables of each type ! *********************************************************************************************************** @@ -868,7 +869,7 @@ MODULE var_lookup ! number of possible output frequencies type(iLook_freq), public,parameter :: iLookFreq =ilook_freq ( 1, 2, 3, 4) - + ! named variables in the lookup table structure type(iLook_vLookup), public,parameter :: iLookLOOKUP =ilook_vLookup ( 1, 2, 3) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 2787c669b..9c3755620 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -134,6 +134,7 @@ subroutine coupled_em(& ! the model solver USE indexState_module,only:indexState ! define indices for all model state variables and layers USE opSplittin_module,only:opSplittin ! solve the system of thermodynamic and hydrology equations for a given substep + USE time_utils_module,only:elapsedSec ! calculate the elapsed time ! additional subroutines USE tempAdjust_module,only:tempAdjust ! adjust snow temperature associated with new snowfall USE snwDensify_module,only:snwDensify ! snow densification (compaction and cavitation) @@ -238,12 +239,18 @@ subroutine coupled_em(& logical(lgt), parameter :: printBalance=.false. ! flag to print the balance checks real(rkind), allocatable :: liqSnowInit(:) ! volumetric liquid water conetnt of snow at the start of the time step real(rkind), allocatable :: liqSoilInit(:) ! soil moisture at the start of the time step + ! timing information + real(rkind) :: startTime ! start time (used to compute wall clock time) + real(rkind) :: endTime ! end time (used to compute wall clock time) ! ---------------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="coupled_em/" ! This is the start of a data step for a local HRU + ! get the start time + call cpu_time(startTime) + ! check that the decision is supported if(model_decisions(iLookDECISIONS%groundwatr)%iDecision==bigBucket .and. & model_decisions(iLookDECISIONS%spatial_gw)%iDecision/=localColumn)then @@ -719,7 +726,7 @@ subroutine coupled_em(& ! save input step dtSave = dt_sub !write(*,'(a,1x,3(f12.5,1x))') trim(message)//'before opSplittin: dt_init, dt_sub, dt_solv = ', dt_init, dt_sub, dt_solv - + ! get the new solution call opSplittin(& @@ -749,7 +756,7 @@ subroutine coupled_em(& stepFailure, & ! intent(out): flag to denote that the coupled step failed ixSolution, & ! intent(out): solution method used in this iteration err,cmessage) ! intent(out): error code and error message - + ! check for all errors (error recovery within opSplittin) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if @@ -829,7 +836,7 @@ subroutine coupled_em(& endif end if ! (if computing the vegetation flux) - + call computSnowDepth(& dt_sub, & ! intent(in) nSnow, & ! intent(in) @@ -844,9 +851,9 @@ subroutine coupled_em(& ! error control err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if - + end associate sublime - + ! update coordinate variables call calcHeight(& ! input/output: data structures @@ -1170,6 +1177,12 @@ subroutine coupled_em(& err=20; return end if + ! get the end time + call cpu_time(endTime) + + ! get the elapsed time + diag_data%var(iLookDIAG%wallClockTime)%dat(1) = endTime - startTime + end subroutine coupled_em From fd8187a731d9d1cb1375e7b273cb4b361df5a7ec Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 8 Dec 2021 09:15:37 +0900 Subject: [PATCH 0238/1472] Space for wall clock from Kevin --- build/source/dshare/var_lookup.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index d2a89e8e6..b994f7aa4 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -827,7 +827,7 @@ MODULE var_lookup 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,& 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,& 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,& - 81, 82, 83, 84, 85, 86) + 81, 82, 83, 84, 85, 86, 87) ! named variables: model fluxes type(iLook_flux), public,parameter :: iLookFLUX =iLook_flux ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& From 617e4c514268207fff319805648b83c703b51832 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 8 Dec 2021 10:40:14 +0900 Subject: [PATCH 0239/1472] Turn off prints with flag --- build/source/engine/computJacDAE.f90 | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 91b3b77a5..73d4ce2e0 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -183,6 +183,7 @@ subroutine computJacDAE(& integer(i4b) :: pLayer ! indices of soil layers (used for the baseflow derivatives) ! conversion factors real(rkind) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative + integer(i4b) :: doprint ! -------------------------------------------------------------- ! associate variables from data structures associate(& @@ -275,7 +276,7 @@ subroutine computJacDAE(& ! -------------------------------------------------------------- ! initialize error control err=0; message='computJacDAE/' - + doprint=0 ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* ! * PART 0: PRELIMINARIES (INITIALIZE JACOBIAN AND COMPUTE TIME-VARIABLE DIAGONAL TERMS) @@ -412,7 +413,7 @@ subroutine computJacDAE(& ! - define index within the state subset and the full state vector jState = ixSnowSoilNrg(iLayer) ! index within the state subset - print*, ixSnowSoilNrg(iLayer),jState, "snowsoilindices" + if (doprint==1) print*, ixSnowSoilNrg(iLayer),jState, "snowsoilindices" ! - diagonal elements aJac(jState,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) @@ -440,7 +441,7 @@ subroutine computJacDAE(& ! - define state indices for the current layer watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - print*, ixSnowOnlyHyd(iLayer),watState, "snowwatindices" + if (doprint==1) print*, ixSnowOnlyHyd(iLayer),watState, "snowwatindices" ! compute factor to convert liquid water derivative to total water derivative select case( ixHydType(iLayer) ) case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) @@ -462,13 +463,13 @@ subroutine computJacDAE(& ! - compute cross-derivative terms for energy ! NOTE: increase in volumetric liquid water content balanced by a decrease in volumetric ice content - print*, nSnowOnlyNrg,ixSnowOnlyNrg(iLayer),watState, ixSnowOnlyHyd(iLayer+1),ixSnowOnlyNrg(iLayer+1), "maybeneedthesesnowwatind" + if (doprint==1) print*, nSnowOnlyNrg,ixSnowOnlyNrg(iLayer),watState, ixSnowOnlyHyd(iLayer+1),ixSnowOnlyNrg(iLayer+1), "maybeneedthesesnowwatind" if(nSnowOnlyNrg>0)then ! (define the energy state) nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector if(nrgstate/=integerMissing)then ! (energy state for the current layer is within the state subset) - print*, nrgState,watState, "snowwatenergyindices" + if (doprint==1) print*, nrgState,watState, "snowwatenergyindices" ! (cross-derivative terms for the current layer) aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_ice * cj & + LH_fus*iden_ice * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) @@ -500,7 +501,7 @@ subroutine computJacDAE(& ! - define state indices watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset - print*, ixSoilOnlyHyd(iLayer),watState, "watsoilindices" + if (doprint==1) print*, ixSoilOnlyHyd(iLayer),watState, "watsoilindices" ! - define indices of the soil layers jLayer = iLayer+nSnow ! index of layer in the snow+soil vector @@ -553,7 +554,7 @@ subroutine computJacDAE(& ! only compute derivatives if the energy state for the current layer is within the state subset if(nrgstate/=integerMissing)then - print*, ixVegHyd,ixCasNrg,ixVegNrg, ixTopNrg,watState,nrgState, "watvegindices" + if (doprint==1) print*, ixVegHyd,ixCasNrg,ixVegNrg, ixTopNrg,watState,nrgState, "watvegindices" ! - compute the Jacobian for the layer itself aJac(watState,nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance @@ -566,7 +567,7 @@ subroutine computJacDAE(& endif aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) endif - print*,mLayerdTheta_dTk(jLayer),"melt-freeze" + if (doprint==1) print*,mLayerdTheta_dTk(jLayer),"melt-freeze" ! melt-freeze: compute derivative in energy with respect to mass if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(nrgState,watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water*cj - LH_fus*iden_water * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content From 971a0db7601de50a2bacfd4f93da77cd2fbddc46 Mon Sep 17 00:00:00 2001 From: ashleymedin <34691332+ashleymedin@users.noreply.github.com> Date: Mon, 14 Feb 2022 13:54:02 +0900 Subject: [PATCH 0240/1472] Adding in heat capacity Jacobian terms, commit one, still has bugs --- build/source/dshare/get_ixname.f90 | 5 +- build/source/dshare/popMetadat.f90 | 3 + build/source/dshare/var_lookup.f90 | 6 +- build/source/engine/check_icond.f90 | 1 + build/source/engine/computFlux.f90 | 762 +++++++++++++++++++ build/source/engine/computJacDAE.f90 | 156 ++-- build/source/engine/eval8DAE.f90 | 3 +- build/source/engine/eval8JacDAE.f90 | 17 +- build/source/engine/solveByIDA.f90 | 4 +- build/source/engine/ssdNrgFlux.f90 | 878 +++++++++++++++++++++- build/source/engine/updateVars4JacDAE.f90 | 66 +- build/source/engine/vegNrgFlux.f90 | 28 +- 12 files changed, 1827 insertions(+), 102 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 6d840ffd1..5b5a88954 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -733,7 +733,10 @@ function get_ixderiv(varName) case('scalarCanopyLiqDrainageDeriv' ); get_ixderiv = iLookDERIV%scalarCanopyLiqDrainageDeriv ! derivative in canopy drainage w.r.t. canopy liquid water (s-1) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below case('dNrgFlux_dTempAbove' ); get_ixderiv = iLookDERIV%dNrgFlux_dTempAbove ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) - case('dNrgFlux_dTempBelow ' ); get_ixderiv = iLookDERIV%dNrgFlux_dTempBelow ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) + case('dNrgFlux_dTempBelow' ); get_ixderiv = iLookDERIV%dNrgFlux_dTempBelow ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) + ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below + case('dNrgFlux_dWatAbove' ); get_ixderiv = iLookDERIV%dNrgFlux_dWatAbove ! derivatives in the flux w.r.t. water state temperature in the layer above + case('dNrgFlux_dWatBelow' ); get_ixderiv = iLookDERIV%dNrgFlux_dWatBelow ! derivatives in the flux w.r.t. water state in the layer below ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above case('iLayerLiqFluxSnowDeriv' ); get_ixderiv = iLookDERIV%iLayerLiqFluxSnowDeriv ! derivative in vertical liquid water flux at layer interfaces (m s-1) ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 2c8f24f7b..3fc7c092a 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -568,6 +568,9 @@ subroutine popMetadat(err,message) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below deriv_meta(iLookDERIV%dNrgFlux_dTempAbove) = var_info('dNrgFlux_dTempAbove' , 'derivatives in the flux w.r.t. temperature in the layer above' , 'J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dNrgFlux_dTempBelow) = var_info('dNrgFlux_dTempBelow' , 'derivatives in the flux w.r.t. temperature in the layer below' , 'J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below + deriv_meta(iLookDERIV%dNrgFlux_dWatAbove) = var_info('dNrgFlux_dWatAbove' , 'derivatives in the flux w.r.t. water state in the layer above' , 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dNrgFlux_dWatBelow) = var_info('dNrgFlux_dWatBelow' , 'derivatives in the flux w.r.t. water state in the layer below' , 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above deriv_meta(iLookDERIV%iLayerLiqFluxSnowDeriv) = var_info('iLayerLiqFluxSnowDeriv' , 'derivative in vertical liquid water flux at layer interfaces' , 'm s-1' , get_ixVarType('ifcSnow'), iMissVec, iMissVec, .false.) ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index b994f7aa4..c144c7885 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -587,6 +587,9 @@ MODULE var_lookup ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below integer(i4b) :: dNrgFlux_dTempAbove = integerMissing ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) integer(i4b) :: dNrgFlux_dTempBelow = integerMissing ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) + ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below + integer(i4b) :: dNrgFlux_dWatAbove = integerMissing ! derivatives in the flux w.r.t. water state in the layer above + integer(i4b) :: dNrgFlux_dWatBelow = integerMissing ! derivatives in the flux w.r.t. water state in the layer below ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above integer(i4b) :: iLayerLiqFluxSnowDeriv = integerMissing ! derivative in vertical liquid water flux at layer interfaces (m s-1) ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables @@ -843,7 +846,8 @@ MODULE var_lookup type(iLook_deriv), public,parameter :: iLookDERIV =iLook_deriv ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,& - 31, 32, 33, 34, 35, 36, 37, 38, 39) + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,& + 41) ! named variables: model indices type(iLook_index), public,parameter :: iLookINDEX =ilook_index ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& diff --git a/build/source/engine/check_icond.f90 b/build/source/engine/check_icond.f90 index 3210932be..2a7503a2f 100644 --- a/build/source/engine/check_icond.f90 +++ b/build/source/engine/check_icond.f90 @@ -122,6 +122,7 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU ! ensure the initial conditions are consistent with the constitutive functions do iGRU = 1,nGRU do iHRU = 1,gru_struc(iGRU)%hruCount + !progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerTemp)%dat = 282.0 ! associate local variables with variables in the data structures associate(& diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 20c018613..85f986a57 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -96,6 +96,7 @@ module computFlux_module implicit none private public::computFlux +public::computFluxSundials public::soilCmpres public::soilCmpresSundials contains @@ -852,6 +853,767 @@ subroutine computFlux(& end subroutine computFlux + ! ********************************************************************************************************* + ! public subroutine computFluxSundials: compute model fluxes + ! ********************************************************************************************************* + subroutine computFluxSundials(& + ! input-output: model control + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to denote the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + drainageMeltPond, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) + ! input: state variables + scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) + scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) + mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) + mLayerMatricHeadLiqTrial, & ! intent(in): trial value for the liquid water matric potential in each soil layer (m) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + scalarAquiferStorageTrial,& ! intent(in): trial value of storage of water in the aquifer (m) + ! input: diagnostic variables defining the liquid water and ice content + scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) + mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liquid water content in each snow and soil layer (-) + mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) + ! input: data structures + model_decisions, & ! intent(in): model decisions + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): index data + ! input-output: data structures + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: flux vector and baseflow derivatives + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + fluxVec, & ! intent(out): flux vector (mixed units) + ! output: error control + err,message) ! intent(out): error code and error message + ! provide access to flux subroutines + USE vegNrgFlux_module,only:vegNrgFlux ! compute energy fluxes over vegetation + USE ssdNrgFlux_module,only:ssdNrgFluxSundials ! compute energy fluxes throughout the snow and soil subdomains + USE vegLiqFlux_module,only:vegLiqFlux ! compute liquid water fluxes through vegetation + USE snowLiqFlx_module,only:snowLiqflx ! compute liquid water fluxes through snow + USE soilLiqFlx_module,only:soilLiqflx ! compute liquid water fluxes through soil + USE groundwatr_module,only:groundwatr ! compute the baseflow flux + USE bigAquifer_module,only:bigAquifer ! compute fluxes for the big aquifer + implicit none + ! --------------------------------------------------------------------------------------- + ! * dummy variables + ! --------------------------------------------------------------------------------------- + ! input-output: control + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + real(rkind),intent(in) :: drainageMeltPond ! drainage from the surface melt pond (kg m-2 s-1) + ! input: state variables + real(rkind),intent(in) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind),intent(in) :: mLayerTempTrial(:) ! trial value for temperature of each snow/soil layer (K) + real(rkind),intent(in) :: mLayerMatricHeadLiqTrial(:) ! trial value for the liquid water matric potential (m) + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial value for the total water matric potential (m) + real(rkind),intent(in) :: scalarAquiferStorageTrial ! trial value of aquifer storage (m) + ! input: diagnostic variables + real(rkind),intent(in) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) + ! input: data structures + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(var_i), intent(in) :: type_data ! type of vegetation and soil + type(var_d), intent(in) :: attr_data ! spatial attributes + type(var_dlength), intent(in) :: mpar_data ! model parameters + type(var_d), intent(in) :: forc_data ! model forcing data + type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin + type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_ilength), intent(in) :: indx_data ! indices defining model states and layers + ! input-output: data structures + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + ! input-output: flux vector and baseflow derivatives + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + real(rkind),intent(out) :: fluxVec(:) ! model flux vector (mixed units) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! --------------------------------------------------------------------------------------- + ! * local variables + ! --------------------------------------------------------------------------------------- + integer(i4b) :: local_ixGroundwater ! local index for groundwater representation + integer(i4b) :: iLayer ! index of model layers + logical(lgt) :: doVegNrgFlux ! flag to compute the energy flux over vegetation + real(rkind),dimension(nSoil) :: dHydCond_dMatric ! derivative in hydraulic conductivity w.r.t matric head (s-1) + character(LEN=256) :: cmessage ! error message of downwind routine + ! -------------------------------------------------------------- + ! initialize error control + err=0; message='computFluxSundials/' + + ! ***** + ! (0) PRELIMINARIES... + ! ******************** + + ! get the necessary variables for the flux computations + associate(& + + ! model decisions + ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization + ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) + + ! domain boundary conditions + upperBoundTemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) + scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in): [dp] rainfall rate (kg m-2 s-1) + + ! canopy and layer depth + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + + ! indices of model state variables for the vegetation subdomain + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain + ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer + + ! indices of model state variables for the snow+soil domain + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] type of layer (iname_soil or iname_snow) + + ! number of state variables of a specific type + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain + nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + + ! derivatives + dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) + dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(in): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + + ! number of flux calls + numFluxCalls => diag_data%var(iLookDIAG%numFluxCalls)%dat(1) ,& ! intent(out): [dp] number of flux calls (-) + + ! net fluxes over the vegetation domain + scalarCanairNetNrgFlux => flux_data%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the canopy air space (W m-2) + scalarCanopyNetNrgFlux => flux_data%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the vegetation canopy (W m-2) + scalarGroundNetNrgFlux => flux_data%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the ground surface (W m-2) + scalarCanopyNetLiqFlux => flux_data%var(iLookFLUX%scalarCanopyNetLiqFlux)%dat(1) ,& ! intent(out): [dp] net liquid water flux for the vegetation canopy (kg m-2 s-1) + + ! net fluxes over the snow+soil domain + mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) + mLayerLiqFluxSnow => flux_data%var(iLookFLUX%mLayerLiqFluxSnow)%dat ,& ! intent(out): [dp] net liquid water flux for each snow layer (s-1) + mLayerLiqFluxSoil => flux_data%var(iLookFLUX%mLayerLiqFluxSoil)%dat ,& ! intent(out): [dp] net liquid water flux for each soil layer (s-1) + + ! evaporative fluxes + scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(out): [dp] canopy transpiration (kg m-2 s-1) + scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(out): [dp] canopy evaporation/condensation (kg m-2 s-1) + scalarGroundEvaporation => flux_data%var(iLookFLUX%scalarGroundEvaporation)%dat(1) ,& ! intent(out): [dp] ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(out): [dp(:)] transpiration loss from each soil layer (m s-1) + + ! fluxes for the snow+soil domain + iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(out): [dp(0:)] vertical energy flux at the interface of snow and soil layers + iLayerLiqFluxSnow => flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at snow layer interfaces (-) + iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at soil layer interfaces (-) + mLayerHydCond => flux_data%var(iLookFLUX%mLayerHydCond)%dat ,& ! intent(out): [dp(:)] hydraulic conductivity in each soil layer (m s-1) + mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(out): [dp(:)] baseflow from each soil layer (m s-1) + scalarSnowDrainage => flux_data%var(iLookFLUX%scalarSnowDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the snow profile (m s-1) + scalarSoilDrainage => flux_data%var(iLookFLUX%scalarSoilDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the soil profile (m s-1) + scalarSoilBaseflow => flux_data%var(iLookFLUX%scalarSoilBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the soil profile (m s-1) + + ! infiltration + scalarInfilArea => diag_data%var(iLookDIAG%scalarInfilArea )%dat(1) ,& ! intent(out): [dp] fraction of unfrozen area where water can infiltrate (-) + scalarFrozenArea => diag_data%var(iLookDIAG%scalarFrozenArea )%dat(1) ,& ! intent(out): [dp] fraction of area that is considered impermeable due to soil ice (-) + scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1) ,& ! intent(out): [dp] soil control on infiltration, zero or one + scalarMaxInfilRate => flux_data%var(iLookFLUX%scalarMaxInfilRate)%dat(1) ,& ! intent(out): [dp] maximum infiltration rate (m s-1) + scalarInfiltration => flux_data%var(iLookFLUX%scalarInfiltration)%dat(1) ,& ! intent(out): [dp] infiltration of water into the soil profile (m s-1) + + ! boundary fluxes in the soil domain + scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(out): [dp] rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(out): [dp] drainage of liquid water from the vegetation canopy (kg m-2 s-1) + scalarRainPlusMelt => flux_data%var(iLookFLUX%scalarRainPlusMelt)%dat(1) ,& ! intent(out): [dp] rain plus melt (m s-1) + scalarSurfaceRunoff => flux_data%var(iLookFLUX%scalarSurfaceRunoff)%dat(1) ,& ! intent(out): [dp] surface runoff (m s-1) + scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1) ,& ! intent(out): [dp] exfiltration from the soil profile (m s-1) + mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat ,& ! intent(out): [dp(:)] column outflow from each soil layer (m3 s-1) + + ! fluxes for the aquifer + scalarAquiferTranspire => flux_data%var(iLookFLUX%scalarAquiferTranspire)%dat(1) ,& ! intent(out): [dp] transpiration loss from the aquifer (m s-1 + scalarAquiferRecharge => flux_data%var(iLookFLUX%scalarAquiferRecharge)%dat(1) ,& ! intent(out): [dp] recharge to the aquifer (m s-1) + scalarAquiferBaseflow => flux_data%var(iLookFLUX%scalarAquiferBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the aquifer (m s-1) + + ! total runoff + scalarTotalRunoff => flux_data%var(iLookFLUX%scalarTotalRunoff)%dat(1) ,& ! intent(out): [dp] total runoff (m s-1) + + ! derivatives in net vegetation energy fluxes w.r.t. relevant state variables + dCanairNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy air temperature + dCanairNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy temperature + dCanairNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. ground temperature + dCanopyNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy air temperature + dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy temperature + dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. ground temperature + dCanopyNetFlux_dCanLiq => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in net canopy fluxes w.r.t. canopy liquid water content + dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy air temperature + dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy temperature + dGroundNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. ground temperature + dGroundNetFlux_dCanLiq => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in net ground fluxes w.r.t. canopy liquid water content + + ! derivatives in evaporative fluxes w.r.t. relevant state variables + dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy air temperature + dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy temperature + dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. ground temperature + dCanopyEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy liquid water content + dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy air temperature + dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy temperature + dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. ground temperature + dGroundEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy liquid water content + + ! derivatives in canopy water w.r.t canopy temperature + dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(out): [dp] derivative of canopy liquid storage w.r.t. temperature + + ! derivatives in canopy liquid fluxes w.r.t. canopy water + scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ,& ! intent(out): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water + scalarThroughfallRainDeriv => deriv_data%var(iLookDERIV%scalarThroughfallRainDeriv )%dat(1) ,& ! intent(out): [dp] derivative in throughfall w.r.t. canopy liquid water + scalarCanopyLiqDrainageDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDrainageDeriv)%dat(1) ,& ! intent(out): [dp] derivative in canopy drainage w.r.t. canopy liquid water + + ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below + dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above + dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below + + ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below + dNrgFlux_dWatAbove => deriv_data%var(iLookDERIV%dNrgFlux_dWatAbove )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. water state in the layer above + dNrgFlux_dWatBelow => deriv_data%var(iLookDERIV%dNrgFlux_dWatBelow )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. water state in the layer below + + ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above + iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(out): [dp(:)] derivative in vertical liquid water flux at layer interfaces + + ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential + dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above + dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + mLayerdTheta_dPsi => deriv_data%var(iLookDERIV%mLayerdTheta_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. psi + mLayerdPsi_dTheta => deriv_data%var(iLookDERIV%mLayerdPsi_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. theta + dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in compressibility w.r.t matric head + + ! derivative in baseflow flux w.r.t. aquifer storage + dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] erivative in baseflow flux w.r.t. aquifer storage (s-1) + + ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables + dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above + dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat & ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + ) ! association to data in structures + + ! ***** + ! * PRELIMINARIES... + ! ****************** + + !print*, '***** nSnowSoilNrg, nSnowOnlyNrg, nSoilOnlyNrg, nSnowSoilHyd, nSnowOnlyHyd, nSoilOnlyHyd = ', & + ! nSnowSoilNrg, nSnowOnlyNrg, nSoilOnlyNrg, nSnowSoilHyd, nSnowOnlyHyd, nSoilOnlyHyd + + ! increment the number of flux calls + numFluxCalls = numFluxCalls+1 + + ! modify the groundwater representation for this single-column implementation + select case(ixSpatialGroundwater) + case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale + case(localColumn); local_ixGroundwater = ixGroundwater ! go with the specified decision + case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return + end select ! (modify the groundwater representation for this single-column implementation) + + ! initialize liquid water fluxes throughout the snow and soil domains + ! NOTE: used in the energy routines, which is called before the hydrology routines + if(firstFluxCall)then + if(nSnow > 0) iLayerLiqFluxSnow(0:nSnow) = 0._rkind + end if + + ! ***** + ! * CALCULATE ENERGY FLUXES OVER VEGETATION... + ! ********************************************* + + ! identify the need to calculate the energy flux over vegetation + doVegNrgFlux = (ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing .or. ixTopNrg/=integerMissing) + + ! check if there is a need to calculate the energy fluxes over vegetation + if(doVegNrgFlux)then + + ! derivative in canopy liquid storage w.r.t. canopy temperature + dCanLiq_dTcanopy = dTheta_dTkCanopy*iden_water*canopyDepth ! kg m-2 K-1 + + ! calculate the energy fluxes over vegetation + call vegNrgFlux(& + ! input: model control + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + ! input: model state variables + upperBoundTemp, & ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature + scalarCanairTempTrial, & ! intent(in): trial value of the canopy air space temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + mLayerTempTrial(1), & ! intent(in): trial value of ground temperature (K) + scalarCanopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) + ! input: model derivatives + dCanLiq_dTcanopy, & ! intent(in): derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) + ! input/output: data structures + type_data, & ! intent(in): type of vegetation and soil + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + bvar_data, & ! intent(in): model variables for the local basin + model_decisions, & ! intent(in): model decisions + ! output: liquid water fluxes associated with evaporation/transpiration + scalarCanopyTranspiration, & ! intent(out): canopy transpiration (kg m-2 s-1) + scalarCanopyEvaporation, & ! intent(out): canopy evaporation/condensation (kg m-2 s-1) + scalarGroundEvaporation, & ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + ! output: fluxes + scalarCanairNetNrgFlux, & ! intent(out): net energy flux for the canopy air space (W m-2) + scalarCanopyNetNrgFlux, & ! intent(out): net energy flux for the vegetation canopy (W m-2) + scalarGroundNetNrgFlux, & ! intent(out): net energy flux for the ground surface (W m-2) + ! output: flux derivatives + dCanairNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) + dCanairNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) + dCanairNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) + dCanopyNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) + dCanopyNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) + dCanopyNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) + dGroundNetFlux_dCanairTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) + dGroundNetFlux_dCanopyTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) + dGroundNetFlux_dGroundTemp, & ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + ! output: liquid water flux derivatives (canopy evap) + dCanopyEvaporation_dCanLiq, & ! intent(out): derivative in canopy evaporation w.r.t. canopy liquid water content (s-1) + dCanopyEvaporation_dTCanair, & ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTCanopy, & ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTGround, & ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + ! output: liquid water flux derivatives (ground evap) + dGroundEvaporation_dCanLiq, & ! intent(out): derivative in ground evaporation w.r.t. canopy liquid water content (s-1) + dGroundEvaporation_dTCanair, & ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTCanopy, & ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTGround, & ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + ! output: cross derivative terms + dCanopyNetFlux_dCanLiq, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dGroundNetFlux_dCanLiq, & ! intent(out): derivative in net ground fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + + ! check fluxes + if(globalPrintFlag)then + print*, '**' + write(*,'(a,1x,10(f30.20))') 'canopyDepth = ', canopyDepth + write(*,'(a,1x,10(f30.20))') 'mLayerDepth(1:2) = ', mLayerDepth(1:2) + write(*,'(a,1x,10(f30.20))') 'scalarCanairTempTrial = ', scalarCanairTempTrial ! trial value of the canopy air space temperature (K) + write(*,'(a,1x,10(f30.20))') 'scalarCanopyTempTrial = ', scalarCanopyTempTrial ! trial value of canopy temperature (K) + write(*,'(a,1x,10(f30.20))') 'mLayerTempTrial(1:2) = ', mLayerTempTrial(1:2) ! trial value of ground temperature (K) + write(*,'(a,1x,10(f30.20))') 'scalarCanairNetNrgFlux = ', scalarCanairNetNrgFlux + write(*,'(a,1x,10(f30.20))') 'scalarCanopyNetNrgFlux = ', scalarCanopyNetNrgFlux + write(*,'(a,1x,10(f30.20))') 'scalarGroundNetNrgFlux = ', scalarGroundNetNrgFlux + write(*,'(a,1x,10(f30.20))') 'dGroundNetFlux_dGroundTemp = ', dGroundNetFlux_dGroundTemp + endif ! if checking fluxes + + endif ! if calculating the energy fluxes over vegetation + + ! ***** + ! * CALCULATE ENERGY FLUXES THROUGH THE SNOW-SOIL DOMAIN... + ! ********************************************************** + + ! check the need to compute energy fluxes throughout the snow+soil domain + if(nSnowSoilNrg>0)then + + ! calculate energy fluxes at layer interfaces through the snow and soil domain + call ssdNrgFluxSundials(& + ! input: model control + (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution + .true., & ! intent(in): flag indicating if derivatives are desired + ! input: fluxes and derivatives at the upper boundary + scalarGroundNetNrgFlux, & ! intent(in): total flux at the ground surface (W m-2) + dGroundNetFlux_dGroundTemp, & ! intent(in): derivative in total ground surface flux w.r.t. ground temperature (W m-2 K-1) + ! input: liquid water fluxes throughout the snow and soil domains + iLayerLiqFluxSnow, & ! intent(in): liquid flux at the interface of each snow layer (m s-1) + iLayerLiqFluxSoil, & ! intent(in): liquid flux at the interface of each soil layer (m s-1) + ! input: trial value of model state variabes + mLayerTempTrial, & ! intent(in): trial temperature at the current iteration (K) + mLayerMatricHeadTrial, & ! intent(in): trial value for the total water matric potential in each soil layer (m) + mLayerVolFracLiqTrial, & ! intent(in): trial volumetric fraction of liquid water at the current iteration(-) + ! input-output: data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model indices + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output: fluxes and derivatives at all layer interfaces + iLayerNrgFlux, & ! intent(out): energy flux at the layer interfaces (W m-2) + dNrgFlux_dTempAbove, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (W m-2 K-1) + dNrgFlux_dTempBelow, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (W m-2 K-1) + dNrgFlux_dWatAbove, & ! intent(out): derivatives in the flux w.r.t. water state in the layer above + dNrgFlux_dWatBelow, & ! intent(out): derivatives in the flux w.r.t. water state in the layer below + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! calculate net energy fluxes for each snow and soil layer (J m-3 s-1) + do iLayer=1,nLayers + mLayerNrgFlux(iLayer) = -(iLayerNrgFlux(iLayer) - iLayerNrgFlux(iLayer-1))/mLayerDepth(iLayer) + if(globalPrintFlag)then + if(iLayer < 10) write(*,'(a,1x,i4,1x,10(f25.15,1x))') 'iLayer, iLayerNrgFlux(iLayer-1:iLayer), mLayerNrgFlux(iLayer) = ', iLayer, iLayerNrgFlux(iLayer-1:iLayer), mLayerNrgFlux(iLayer) + endif + end do + + endif ! if computing energy fluxes throughout the snow+soil domain + + + ! ***** + ! * CALCULATE THE LIQUID FLUX THROUGH VEGETATION... + ! ************************************************** + + ! check the need to compute the liquid water fluxes through vegetation + if(ixVegHyd/=integerMissing)then + + ! calculate liquid water fluxes through vegetation + call vegLiqFlux(& + ! input + computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + scalarCanopyLiqTrial, & ! intent(in): trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) + scalarRainfall, & ! intent(in): rainfall rate (kg m-2 s-1) + ! input-output: data structures + mpar_data, & ! intent(in): model parameters + diag_data, & ! intent(in): local HRU diagnostic model variables + ! output + scalarThroughfallRain, & ! intent(out): rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + scalarCanopyLiqDrainage, & ! intent(out): drainage of liquid water from the vegetation canopy (kg m-2 s-1) + scalarThroughfallRainDeriv, & ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) + scalarCanopyLiqDrainageDeriv, & ! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! calculate the net liquid water flux for the vegetation canopy + scalarCanopyNetLiqFlux = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + + ! calculate the total derivative in the downward liquid flux + scalarCanopyLiqDeriv = scalarThroughfallRainDeriv + scalarCanopyLiqDrainageDeriv + + ! test + if(globalPrintFlag)then + print*, '**' + print*, 'scalarRainfall = ', scalarRainfall + print*, 'scalarThroughfallRain = ', scalarThroughfallRain + print*, 'scalarCanopyEvaporation = ', scalarCanopyEvaporation + print*, 'scalarCanopyLiqDrainage = ', scalarCanopyLiqDrainage + print*, 'scalarCanopyNetLiqFlux = ', scalarCanopyNetLiqFlux + print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial + endif + + endif ! computing the liquid water fluxes through vegetation + + ! ***** + ! * CALCULATE THE LIQUID FLUX THROUGH SNOW... + ! ******************************************** + + ! check the need to compute liquid water fluxes through snow + if(nSnowOnlyHyd>0)then + + ! compute liquid fluxes through snow + call snowLiqFlx(& + ! input: model control + nSnow, & ! intent(in): number of snow layers + firstFluxCall, & ! intent(in): the first flux call (compute variables that are constant over the iterations) + (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution + ! input: forcing for the snow domain + scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) + scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) + ! input: model state vector + mLayerVolFracLiqTrial(1:nSnow), & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) + ! input-output: data structures + indx_data, & ! intent(in): model indices + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + ! output: fluxes and derivatives + iLayerLiqFluxSnow(0:nSnow), & ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) + iLayerLiqFluxSnowDeriv(0:nSnow), & ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! define forcing for the soil domain + scalarRainPlusMelt = iLayerLiqFluxSnow(nSnow) ! drainage from the base of the snowpack + + ! calculate net liquid water fluxes for each soil layer (s-1) + do iLayer=1,nSnow + mLayerLiqFluxSnow(iLayer) = -(iLayerLiqFluxSnow(iLayer) - iLayerLiqFluxSnow(iLayer-1))/mLayerDepth(iLayer) + !write(*,'(a,1x,i4,1x,2(f16.10,1x))') 'iLayer, mLayerLiqFluxSnow(iLayer), iLayerLiqFluxSnow(iLayer-1) = ', & + ! iLayer, mLayerLiqFluxSnow(iLayer), iLayerLiqFluxSnow(iLayer-1) + end do + + ! compute drainage from the soil zone (needed for mass balance checks) + scalarSnowDrainage = iLayerLiqFluxSnow(nSnow) + + else + + ! define forcing for the soil domain for the case of no snow layers + ! NOTE: in case where nSnowOnlyHyd==0 AND snow layers exist, then scalarRainPlusMelt is taken from the previous flux evaluation + if(nSnow==0)then + scalarRainPlusMelt = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water & ! liquid flux from the canopy (m s-1) + + drainageMeltPond/iden_water ! melt of the snow without a layer (m s-1) + endif ! if no snow layers + + endif + + ! ***** + ! * CALCULATE THE LIQUID FLUX THROUGH SOIL... + ! ******************************************** + + ! check the need to calculate the liquid flux through soil + if(nSoilOnlyHyd>0)then + + ! calculate the liquid flux through soil + call soilLiqFlx(& + ! input: model control + nSoil, & ! intent(in): number of soil layers + firstSplitOper, & ! intent(in): flag indicating first flux call in a splitting operation + (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution + .true., & ! intent(in): flag indicating if derivatives are desired + ! input: trial state variables + mLayerTempTrial(nSnow+1:nLayers), & ! intent(in): trial temperature at the current iteration (K) + mLayerMatricHeadLiqTrial(1:nSoil), & ! intent(in): liquid water matric potential (m) + mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of ice (-) + ! input: pre-computed deriavatives + mLayerdTheta_dTk(nSnow+1:nLayers), & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + dPsiLiq_dTemp(1:nSoil), & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) + ! input: fluxes + scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) + scalarGroundEvaporation, & ! intent(in): ground evaporation (kg m-2 s-1) + scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) + ! input-output: data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model indices + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output: diagnostic variables for surface runoff + scalarMaxInfilRate, & ! intent(inout): maximum infiltration rate (m s-1) + scalarInfilArea, & ! intent(inout): fraction of unfrozen area where water can infiltrate (-) + scalarFrozenArea, & ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) + scalarSurfaceRunoff, & ! intent(inout): surface runoff (m s-1) + ! output: diagnostic variables for model layers + mLayerdTheta_dPsi, & ! intent(inout): derivative in the soil water characteristic w.r.t. psi (m-1) + mLayerdPsi_dTheta, & ! intent(inout): derivative in the soil water characteristic w.r.t. theta (m) + dHydCond_dMatric, & ! intent(inout): derivative in hydraulic conductivity w.r.t matric head (s-1) + ! output: fluxes + scalarInfiltration, & ! intent(inout): surface infiltration rate (m s-1) -- controls on infiltration only computed for iter==1 + iLayerLiqFluxSoil, & ! intent(inout): liquid fluxes at layer interfaces (m s-1) + mLayerTranspire, & ! intent(inout): transpiration loss from each soil layer (m s-1) + mLayerHydCond, & ! intent(inout): hydraulic conductivity in each layer (m s-1) + ! output: derivatives in fluxes w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below (m s-1 or s-1) + dq_dHydStateAbove, & ! intent(inout): derivatives in the flux w.r.t. matric head in the layer above (s-1) + dq_dHydStateBelow, & ! intent(inout): derivatives in the flux w.r.t. matric head in the layer below (s-1) + ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) + dq_dNrgStateAbove, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) + dq_dNrgStateBelow, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! calculate net liquid water fluxes for each soil layer (s-1) + do iLayer=1,nSoil + mLayerLiqFluxSoil(iLayer) = -(iLayerLiqFluxSoil(iLayer) - iLayerLiqFluxSoil(iLayer-1))/mLayerDepth(iLayer+nSnow) + !if(iLayer<8) write(*,'(a,1x,2(i4,1x),3(e20.10),f12.7)') 'iLayerLiqFluxSoil(iLayer-1), iLayerLiqFluxSoil(iLayer), mLayerLiqFluxSoil(iLayer) = ', iLayer-1, iLayer, & + ! iLayerLiqFluxSoil(iLayer-1), iLayerLiqFluxSoil(iLayer), mLayerLiqFluxSoil(iLayer), mLayerDepth(iLayer+nSnow) + end do + + ! calculate the soil control on infiltration + if(nSnow==0) then + ! * case of infiltration into soil + if(scalarMaxInfilRate > scalarRainPlusMelt)then ! infiltration is not rate-limited + scalarSoilControl = (1._rkind - scalarFrozenArea)*scalarInfilArea + else + scalarSoilControl = 0._rkind ! (scalarRainPlusMelt exceeds maximum infiltration rate + endif + else + ! * case of infiltration into snow + scalarSoilControl = 1._rkind + endif + + ! compute drainage from the soil zone (needed for mass balance checks) + scalarSoilDrainage = iLayerLiqFluxSoil(nSoil) + + ! expand derivatives to the total water matric potential + ! NOTE: arrays are offset because computing derivatives in interface fluxes, at the top and bottom of the layer respectively + if(globalPrintFlag) print*, 'dPsiLiq_dPsi0(1:nSoil) = ', dPsiLiq_dPsi0(1:nSoil) + dq_dHydStateAbove(1:nSoil) = dq_dHydStateAbove(1:nSoil) *dPsiLiq_dPsi0(1:nSoil) + dq_dHydStateBelow(0:nSoil-1) = dq_dHydStateBelow(0:nSoil-1)*dPsiLiq_dPsi0(1:nSoil) + + endif ! if calculating the liquid flux through soil + + ! ***** + ! * CALCULATE THE GROUNDWATER FLOW... + ! ************************************ + + ! check if computing soil hydrology + if(nSoilOnlyHyd>0)then + + ! set baseflow fluxes to zero if the topmodel baseflow routine is not used + if(local_ixGroundwater/=qbaseTopmodel)then + ! (diagnostic variables in the data structures) + scalarExfiltration = 0._rkind ! exfiltration from the soil profile (m s-1) + mLayerColumnOutflow(:) = 0._rkind ! column outflow from each soil layer (m3 s-1) + ! (variables needed for the numerical solution) + mLayerBaseflow(:) = 0._rkind ! baseflow from each soil layer (m s-1) + + ! topmodel-ish shallow groundwater + else ! local_ixGroundwater==qbaseTopmodel + + ! check the derivative matrix is sized appropriately + if(size(dBaseflow_dMatric,1)/=nSoil .or. size(dBaseflow_dMatric,2)/=nSoil)then + message=trim(message)//'expect dBaseflow_dMatric to be nSoil x nSoil' + err=20; return + endif + + ! compute the baseflow flux + call groundwatr(& + ! input: model control + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + firstFluxCall, & ! intent(in): logical flag to compute index of the lowest saturated layer + ! input: state and diagnostic variables + mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) + mLayerMatricHeadLiqTrial, & ! intent(in): liquid water matric potential (m) + mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of ice (-) + ! input: data structures + attr_data, & ! intent(in): model attributes + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output + ixSaturation, & ! intent(inout) index of lowest saturated layer (NOTE: only computed on the first iteration) + mLayerBaseflow, & ! intent(out): baseflow from each soil layer (m s-1) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + endif ! computing baseflow flux + + ! compute total baseflow from the soil zone (needed for mass balance checks) + scalarSoilBaseflow = sum(mLayerBaseflow) + + ! compute total runoff + ! (Note: scalarSoilBaseflow is zero if topmodel is not used) + ! (Note: scalarSoilBaseflow may need to re-envisioned in topmodel formulation if parts of it flow into neighboring soil rather than exfiltrate) + scalarTotalRunoff = scalarSurfaceRunoff + scalarSoilDrainage + scalarSoilBaseflow + + endif ! if computing soil hydrology + + + ! ***** + ! (7) CALCULATE FLUXES FOR THE DEEP AQUIFER... + ! ******************************************** + + ! check if computing aquifer fluxes + if(ixAqWat/=integerMissing)then + + ! identify modeling decision + if(local_ixGroundwater==bigBucket)then + + ! compute fluxes for the big bucket + call bigAquifer(& + ! input: state variables and fluxes + scalarAquiferStorageTrial, & ! intent(in): trial value of aquifer storage (m) + scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) + scalarSoilDrainage, & ! intent(in): soil drainage (m s-1) + ! input: diagnostic variables and parameters + mpar_data, & ! intent(in): model parameter structure + diag_data, & ! intent(in): diagnostic variable structure + ! output: fluxes + scalarAquiferTranspire, & ! intent(out): transpiration loss from the aquifer (m s-1) + scalarAquiferRecharge, & ! intent(out): recharge to the aquifer (m s-1) + scalarAquiferBaseflow, & ! intent(out): total baseflow from the aquifer (m s-1) + dBaseflow_dAquifer, & ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! compute total runoff (overwrite previously calculated value before considering aquifer). + ! (Note: SoilDrainage goes into aquifer, not runoff) + scalarTotalRunoff = scalarSurfaceRunoff + scalarAquiferBaseflow + + ! if no aquifer, then fluxes are zero + else + scalarAquiferTranspire = 0._rkind ! transpiration loss from the aquifer (m s-1) + scalarAquiferRecharge = 0._rkind ! recharge to the aquifer (m s-1) + scalarAquiferBaseflow = 0._rkind ! total baseflow from the aquifer (m s-1) + dBaseflow_dAquifer = 0._rkind ! change in baseflow flux w.r.t. aquifer storage (s-1) + end if ! no aquifer + + endif ! if computing aquifer fluxes + + ! ***** + ! (X) WRAP UP... + ! ************* + + ! define model flux vector for the vegetation sub-domain + if(ixCasNrg/=integerMissing) fluxVec(ixCasNrg) = scalarCanairNetNrgFlux/canopyDepth + if(ixVegNrg/=integerMissing) fluxVec(ixVegNrg) = scalarCanopyNetNrgFlux/canopyDepth + if(ixVegHyd/=integerMissing) fluxVec(ixVegHyd) = scalarCanopyNetLiqFlux ! NOTE: solid fluxes are handled separately + + ! populate the flux vector for energy + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + fluxVec( ixSnowSoilNrg(iLayer) ) = mLayerNrgFlux(iLayer) + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! populate the flux vector for hydrology + ! NOTE: ixVolFracWat and ixVolFracLiq can also include states in the soil domain, hence enable primary variable switching + if(nSnowSoilHyd>0)then ! check if any hydrology states exist + do iLayer=1,nLayers + if(ixSnowSoilHyd(iLayer)/=integerMissing)then ! check if a given hydrology state exists + select case( layerType(iLayer) ) + case(iname_snow); fluxVec( ixSnowSoilHyd(iLayer) ) = mLayerLiqFluxSnow(iLayer) + case(iname_soil); fluxVec( ixSnowSoilHyd(iLayer) ) = mLayerLiqFluxSoil(iLayer-nSnow) + case default; err=20; message=trim(message)//'expect layerType to be either iname_snow or iname_soil'; return + end select + endif ! if a given hydrology state exists + end do ! looping through non-missing energy state variables in the snow+soil domain + endif ! if any hydrology states exist + + ! compute the flux vector for the aquifer + if(ixAqWat/=integerMissing) fluxVec(ixAqWat) = scalarAquiferTranspire + scalarAquiferRecharge - scalarAquiferBaseflow + + ! set the first flux call to false + firstFluxCall=.false. + + ! end association to variables in the data structures + end associate + + end subroutine computFluxSundials + + ! ********************************************************************************************************** ! public subroutine soilCmpres: compute soil compressibility (-) and its derivative w.r.t matric head (m-1) ! ********************************************************************************************************** diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 73d4ce2e0..53c1a85f5 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -76,7 +76,10 @@ module computJacDAE_module USE multiconst,only:& LH_fus, & ! latent heat of fusion (J kg-1) iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water ! intrinsic density of liquid water (kg m-3) + iden_water, & ! intrinsic density of liquid water (kg m-3) + ! specific heat + Cp_ice, & ! specific heat of ice (J kg-1 K-1) + Cp_water ! specific heat of liquid water (J kg-1 K-1) implicit none ! define constants @@ -117,12 +120,18 @@ subroutine computJacDAE(& mLayerVolFracWatPrime, & ! intent(in) scalarCanopyTemp, & ! intent(in) scalarCanopyTempPrime, & ! intent(in) derivative value for temperature of the vegetation canopy (K) - scalarCanopyWatPrime, & ! intetn(in) - mLayerd2Theta_dTk2, & ! intetn(in) - d2VolTot_d2Psi0, & ! intetn(in) + scalarCanopyWatPrime, & ! intent(in) + mLayerd2Theta_dTk2, & ! intent(in) + d2VolTot_d2Psi0, & ! intent(in) dFracLiqSnow_dTk, & ! intent(in) d2Theta_dTkCanopy2, & ! intent(in) dFracLiqVeg_dTkCanopy, & ! intent(in) + dVolHtCapBulk_dPsi0, & ! intent(in): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(in): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dThetaCan, & ! intent(in): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(in): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(in): derivative in bulk heat capacity w.r.t. temperature + ! input-output: Jacobian and its diagonal dMat, & ! intent(inout): diagonal of the Jacobian matrix aJac, & ! intent(out): Jacobian matrix @@ -162,6 +171,13 @@ subroutine computJacDAE(& real(rkind),intent(in) :: dFracLiqSnow_dTk(:) real(rkind),intent(in) :: d2Theta_dTkCanopy2 real(rkind),intent(in) :: dFracLiqVeg_dTkCanopy + real(rkind),intent(out) :: dVolHtCapBulk_dPsi0(:) ! derivative in bulk heat capacity w.r.t. matric potential + real(rkind),intent(out) :: dVolHtCapBulk_dTheta(:) ! derivative in bulk heat capacity w.r.t. volumetric water content + real(rkind),intent(out) :: dVolHtCapBulk_dThetaCan ! derivative in bulk heat capacity w.r.t. volumetric water content + real(rkind),intent(out) :: dVolHtCapBulk_dTk(:) ! derivative in bulk heat capacity w.r.t. temperature + real(rkind),intent(out) :: dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature + + ! input-output: Jacobian and its diagonal real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix real(rkind),intent(out) :: aJac(:,:) ! Jacobian matrix @@ -183,6 +199,8 @@ subroutine computJacDAE(& integer(i4b) :: pLayer ! indices of soil layers (used for the baseflow derivatives) ! conversion factors real(rkind) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative + ! + real(rkind) :: dVolFracWat_dPsi0_iLayer integer(i4b) :: doprint ! -------------------------------------------------------------- ! associate variables from data structures @@ -242,13 +260,16 @@ subroutine computJacDAE(& dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. ground temperature dGroundEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy liquid water content ! derivatives in canopy water w.r.t canopy temperature - dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(in): [dp] derivative of canopy liquid storage w.r.t. temperature - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(in): [dp] derivative in canopy liquid storage w.r.t. temperature + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative in volumetric liquid water content w.r.t. temperature ! derivatives in canopy liquid fluxes w.r.t. canopy water scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ,& ! intent(in): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below + ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below + dNrgFlux_dWatAbove => deriv_data%var(iLookDERIV%dNrgFlux_dWatAbove )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. water state in the layer above + dNrgFlux_dWatBelow => deriv_data%var(iLookDERIV%dNrgFlux_dWatBelow )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. water state in the layer below ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(in): [dp(:)] derivative in vertical liquid water flux at layer interfaces ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables @@ -257,11 +278,11 @@ subroutine computJacDAE(& dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t matric head ! derivative in baseflow flux w.r.t. aquifer storage - dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] erivative in baseflow flux w.r.t. aquifer storage (s-1) + dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] derivative in baseflow flux w.r.t. aquifer storage (s-1) ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk )%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk )%dat ,& ! intent(in): [dp(:)] derivative in volumetric liquid water content w.r.t. temperature ! diagnostic variables scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) @@ -294,6 +315,7 @@ subroutine computJacDAE(& ! NOTE: energy for vegetation is computed *within* the iteration loop as it includes phase change if(ixVegNrg/=integerMissing)then dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy ) * cj & + + dVolHtCapBulk_dTkCanopy & + LH_fus*iden_water * scalarCanopyTempPrime * d2Theta_dTkCanopy2 & + LH_fus * dFracLiqVeg_dTkCanopy * scalarCanopyWatPrime / canopyDepth ! volumetric heat capacity of the vegetation (J m-3 K-1) end if @@ -302,16 +324,11 @@ subroutine computJacDAE(& ! NOTE: energy for snow+soil is computed *within* the iteration loop as it includes phase change do iLayer=1,nLayers if(ixSnowSoilNrg(iLayer)/=integerMissing) then - select case( layerType(iLayer) ) - case(iname_snow) - dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_ice*mLayerdTheta_dTk(iLayer) ) * cj & - + LH_fus*iden_ice * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & - + LH_fus*iden_ice * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) - case(iname_soil) - dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_ice*mLayerdTheta_dTk(iLayer) ) * cj & - + LH_fus*iden_ice * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & - + LH_fus*iden_ice * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) - end select + dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj & + + dVolHtCapBulk_dTk(iLayer) & + + LH_fus*iden_water * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & + + LH_fus*iden_water * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) + end if end do @@ -327,8 +344,6 @@ subroutine computJacDAE(& end if end do - - ! define the form of the matrix select case(ixMatrix) @@ -371,7 +386,10 @@ subroutine computJacDAE(& if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (dt/canopyDepth) *(-dCanopyNetFlux_dCanLiq) + cj * (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth ! dF/dLiq + if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanLiq) + cj * (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth & + + dVolHtCapBulk_dThetaCan * scalarCanopyTempPrime & + + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth ! dF/dLiq + if(ixCasNrg/=integerMissing) aJac(ixCasNrg,ixVegHyd) = (dt/canopyDepth)*(-dCanAirNetFlux_dCanLiq) !doesn't exist if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanLiq) endif @@ -461,33 +479,52 @@ subroutine computJacDAE(& if(ixSnowOnlyHyd(iLayer+1)/=integerMissing) aJac(ixSnowOnlyHyd(iLayer+1),watState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot ! dVol(below)/dLiq(above) -- (-) endif - ! - compute cross-derivative terms for energy - ! NOTE: increase in volumetric liquid water content balanced by a decrease in volumetric ice content - if (doprint==1) print*, nSnowOnlyNrg,ixSnowOnlyNrg(iLayer),watState, ixSnowOnlyHyd(iLayer+1),ixSnowOnlyNrg(iLayer+1), "maybeneedthesesnowwatind" - if(nSnowOnlyNrg>0)then + end do ! (looping through liquid water states in the snow domain) + endif ! (if the subset includes hydrology state variables in the snow domain) + + ! ----- + ! * cross derivatives in the snow domain... + ! ---------------------------------------- + if(nSnowOnlyHyd>0 .and. nSnowOnlyNrg>0)then + do iLayer=1,nSnow ! loop through layers in the snow domain - ! (define the energy state) - nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector - if(nrgstate/=integerMissing)then ! (energy state for the current layer is within the state subset) + ! - check that the snow layer is desired + if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle + + ! - define state indices for the current layer + watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset + + ! (define the energy state) + nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector + if(nrgstate/=integerMissing)then ! (energy state for the current layer is within the state subset) if (doprint==1) print*, nrgState,watState, "snowwatenergyindices" - ! (cross-derivative terms for the current layer) - aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_ice * cj & + ! - include derivatives of energy fluxes w.r.t water fluxes for current layer + aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_ice * cj & + + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) & + LH_fus*iden_ice * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) - aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) - ! (cross-derivative terms for the layer below) - if(iLayer < nSnow)then - !aJac(ixSnowOnlyNrg(iLayer+1),watState) = (-1._rkind + mLayerFracLiqSnow(iLayer)+1)*LH_fus*iden_ice * cj & - ! + LH_fus*iden_ice * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! dF(below)/dLiq(above) -- K-1 - aJac(ixSnowOnlyHyd(iLayer+1),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 + ! - include derivatives of water fluxes w.r.t energy fluxes for current layer + aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) - endif ! (if there is a water state in the layer below the current layer in the given state subset) + ! (cross-derivative terms for the layer below) + if(iLayer1)then + if(ixSnowOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSnowOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) + endif - end do ! (looping through liquid water states in the snow domain) - endif ! (if the subset includes hydrology state variables in the snow domain) + ! (cross-derivative terms for the layer below) + if(iLayer0 .and. nSoilOnlyNrg>0)then do iLayer=1,nSoilOnlyHyd ! - check that the soil layer is desired @@ -555,10 +591,21 @@ subroutine computJacDAE(& ! only compute derivatives if the energy state for the current layer is within the state subset if(nrgstate/=integerMissing)then if (doprint==1) print*, ixVegHyd,ixCasNrg,ixVegNrg, ixTopNrg,watState,nrgState, "watvegindices" - ! - compute the Jacobian for the layer itself + + ! - include derivates in liquid water fluxes w.r.t. temperature for current layer aJac(watState,nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance - ! - include derivatives w.r.t. ground evaporation + ! - compute lower diagonal elements + if(iLayer>1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 + endif + + ! compute upper-diagonal elements + if(iLayer verySmall)then ! ice is present - aJac(nrgState,watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water*cj - LH_fus*iden_water * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + aJac(nrgState,watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water*cj + dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(iLayer) & + - LH_fus*iden_water * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content else - aJac(nrgState,watState) = 0._rkind + aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(iLayer) endif - ! - compute lower diagonal elements + ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above if(iLayer>1)then - if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 + if(ixSoilOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSoilOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) endif - ! compute upper-diagonal elements + ! (cross-derivative terms for the layer below) if(iLayer model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): method used to calculate flux derivatives + ixDerivMethod => model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): method used to calculate flux derivatives ix_bcLowrTdyn => model_decisions(iLookDECISIONS%bcLowrTdyn)%iDecision, & ! intent(in): method used to calculate the lower boundary condition for thermodynamics ! input: model coordinates nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers @@ -258,7 +275,7 @@ subroutine ssdNrgFlux(& case(prescribedTemp) dz = mLayerDepth(iLayer)*0.5_rkind - if(ix_fDerivMeth==analytical)then ! ** analytical derivatives + if(ixDerivMethod==analytical)then ! ** analytical derivatives dFlux_dTempAbove(iLayer) = iLayerThermalC(iLayer)/dz else ! ** numerical derivatives flux0 = -iLayerThermalC(iLayer)*(lowerBoundTemp - (mLayerTempTrial(iLayer) ))/dz @@ -267,17 +284,17 @@ subroutine ssdNrgFlux(& end if ! * zero flux at the lower boundary - case(zeroFlux) - dFlux_dTempAbove(iLayer) = 0._rkind + case(zeroFlux) + dFlux_dTempAbove(iLayer) = 0._rkind - case default; err=20; message=trim(message)//'unable to identify lower boundary condition for thermodynamics'; return + case default; err=20; message=trim(message)//'unable to identify lower boundary condition for thermodynamics'; return end select ! (identifying the lower boundary condition for thermodynamics) ! ***** internal layers else dz = (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) - if(ix_fDerivMeth==analytical)then ! ** analytical derivatives + if(ixDerivMethod==analytical)then ! ** analytical derivatives dFlux_dTempAbove(iLayer) = iLayerThermalC(iLayer)/dz dFlux_dTempBelow(iLayer) = -iLayerThermalC(iLayer)/dz else ! ** numerical derivatives @@ -297,5 +314,846 @@ subroutine ssdNrgFlux(& end subroutine ssdNrgFlux + + + ! ************************************************************************************************ + ! public subroutine ssdNrgFluxSundials: compute energy fluxes and derivatives at layer interfaces + ! ************************************************************************************************ + subroutine ssdNrgFluxSundials(& + ! input: model control + scalarSolution, & ! intent(in): flag to indicate the scalar solution + deriv_desired, & ! intent(in): flag indicating if derivatives are desired + ! input: fluxes and derivatives at the upper boundary + groundNetFlux, & ! intent(in): total flux at the ground surface (W m-2) + dGroundNetFlux_dGroundTemp, & ! intent(in): derivative in total ground surface flux w.r.t. ground temperature (W m-2 K-1) + ! input: liquid water fluxes + iLayerLiqFluxSnow, & ! intent(in): liquid flux at the interface of each snow layer (m s-1) + iLayerLiqFluxSoil, & ! intent(in): liquid flux at the interface of each soil layer (m s-1) + ! input: trial value of model state variables + mLayerTempTrial, & ! intent(in): trial temperature at the current iteration (K) + mLayerMatricHeadTrial, & ! intent(in): trial matric head at the current iteration(m) + mLayerVolFracLiqTrial, & ! intent(in): trial volumetric fraction of liquid water at the current iteration(-) + ! input-output: data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model indices + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output: fluxes and derivatives at all layer interfaces + iLayerNrgFlux, & ! intent(out): energy flux at the layer interfaces (W m-2) + dFlux_dTempAbove, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (W m-2 K-1) + dFlux_dTempBelow, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (W m-2 K-1) + dFlux_dWatAbove, & ! intent(out): derivatives in the flux w.r.t. water state in the layer above (W m-2 K-1) + dFlux_dWatBelow, & ! intent(out): derivatives in the flux w.r.t. water state in the layer below (W m-2 K-1) + ! output: error control + err,message) ! intent(out): error control + ! utility modules + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water + USE soil_utils_module,only:dTheta_dPsi ! derivative in the soil water characteristic (soil) + USE soil_utils_module,only:dTheta_dTk ! differentiate the freezing curve w.r.t. temperature (soil) + ! constants + USE multiconst, only: gravity, & ! gravitational acceleration (m s-1) + Tfreeze, & ! freezing point of water (K) + iden_water,iden_ice,& ! intrinsic density of water and ice (kg m-3) + LH_fus ! latent heat of fusion (J kg-1) + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input: model control + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + logical(lgt),intent(in) :: deriv_desired ! flag indicating if derivatives are desired + ! input: fluxes and derivatives at the upper boundary + real(rkind),intent(in) :: groundNetFlux ! net energy flux for the ground surface (W m-2) + real(rkind),intent(in) :: dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + ! input: liquid water fluxes + real(rkind),intent(in) :: iLayerLiqFluxSnow(0:) ! intent(in): liquid flux at the interface of each snow layer (m s-1) + real(rkind),intent(in) :: iLayerLiqFluxSoil(0:) ! intent(in): liquid flux at the interface of each soil layer (m s-1) + ! input: trial model state variables + real(rkind),intent(in) :: mLayerTempTrial(:) ! temperature in each layer at the current iteration (m) + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! matric head in each layer at the current iteration (m) + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! volumetric fraction of liquid at the current iteration (-) + ! input-output: data structures + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! state vector geometry + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + ! output: fluxes and derivatives at all layer interfaces + real(rkind),intent(out) :: iLayerNrgFlux(0:) ! energy flux at the layer interfaces (W m-2) + real(rkind),intent(out) :: dFlux_dTempAbove(0:) ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) + real(rkind),intent(out) :: dFlux_dTempBelow(0:) ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) + real(rkind),intent(out) :: dFlux_dWatAbove(0:) ! derivatives in the flux w.r.t. water state in the layer above (J m-2 s-1 K-1) + real(rkind),intent(out) :: dFlux_dWatBelow(0:) ! derivatives in the flux w.r.t. water state in the layer below (J m-2 s-1 K-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------------------------------------------------------------ + ! local variables + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: i,iLayer ! index of model layers + integer(i4b) :: ibeg,iend ! start and end indices of the soil layers in concatanated snow-soil vector + integer(i4b) :: iSoil ! index of soil layer + integer(i4b) :: ixLayerDesired(1) ! layer desired (scalar solution) + integer(i4b) :: ixTop ! top layer in subroutine call + integer(i4b) :: ixBot ! bottom layer in subroutine call + real(rkind) :: qFlux ! liquid flux at layer interfaces (m s-1) + real(rkind) :: dz ! height difference (m) + ! additional variables to compute numerical derivatives + integer(i4b) :: nFlux ! number of flux calculations required (>1 = numerical derivatives with one-sided finite differences) + integer(i4b) :: itry ! index of different flux calculations + integer(i4b),parameter :: unperturbed=0 ! named variable to identify the case of unperturbed state variables + integer(i4b),parameter :: perturbState=1 ! named variable to identify the case where we perturb the state in the current layer + integer(i4b),parameter :: perturbStateTempAbove=2 ! named variable to identify the case where we perturb the state layer above + integer(i4b),parameter :: perturbStateTempBelow=3 ! named variable to identify the case where we perturb the state layer below + integer(i4b),parameter :: perturbStateWatAbove=4 ! named variable to identify the case where we perturb the state layer above + integer(i4b),parameter :: perturbStateWatBelow=5 ! named variable to identify the case where we perturb the state layer below + integer(i4b) :: ixPerturb ! index of element in 2-element vector to perturb + integer(i4b) :: ixOriginal ! index of perturbed element in the original vector + real(rkind) :: scalarMatricHeadTrial ! trial value of matric head (m) + real(rkind) :: scalarVolFracLiqTrial ! trial value of volumetric total water content (-) + real(rkind) :: scalarTempTrial ! trial value of temperature (K) + real(rkind) :: scalarThermCFlux ! thermal conductivity (W m-1 K-1) + real(rkind) :: scalarThermCFlux_dTempAbove ! thermal conductivity with perturbation to the temperature state above (W m-1 K-1) + real(rkind) :: scalarThermCFlux_dTempBelow ! thermal conductivity with perturbation to the temperature state below (W m-1 K-1) + real(rkind) :: scalarThermCFlux_dWatAbove ! thermal conductivity with perturbation to the water state above + real(rkind) :: scalarThermCFlux_dWatBelow ! thermal conductivity with perturbation to the water state below + real(rkind) :: flux0,flux1,flux2 ! fluxes used to calculate derivatives (W m-2) + ! compute fluxes and derivatives at layer interfaces + integer(rkind),dimension(2) :: mLayer_ind ! indices of above and below layers + integer(rkind),dimension(2) :: iLayer_ind ! indices of above and below interfaces + real(rkind),dimension(2) :: vectorMatricHeadTrial ! trial value of matric head (m) + real(rkind),dimension(2) :: vectorVolFracLiqTrial ! trial value of volumetric total water content (-) + real(rkind),dimension(2) :: vectorTempTrial ! trial value of temperature (K) + real(rkind),dimension(2) :: vectorvGn_alpha ! layer above and below van Genutchen "alpha" parameter (m-1) + real(rkind),dimension(2) :: vectorvGn_n ! layer above and below van Genutchen "n" parameter (-) + real(rkind),dimension(2) :: vectorvGn_m ! layer above and below van Genutchen "m" parameter (-) + real(rkind),dimension(2) :: vectortheta_sat ! layer above and below soil porosity (-) + real(rkind),dimension(2) :: vectortheta_res ! layer above and below soil residual volumetric water content (-) + real(rkind),dimension(2) :: vectoriden_soil ! layer above and below density of soil (kg m-3) + real(rkind),dimension(2) :: vectorthCond_soil ! layer above and below thermal conductivity of soil (W m-1 K-1) + real(rkind),dimension(2) :: vectorfrac_sand ! layer above and below fraction of sand (-) + real(rkind),dimension(2) :: vectorfrac_silt ! layer above and below fraction of silt (-) + real(rkind),dimension(2) :: vectorfrac_clay ! layer above and below fraction of clay (-) + ! recompute the perturbed version of iLayerThermalC, this could be the only version and remove the omputThermConduct_module + real(rkind) :: scalariLayerThermalC ! thermal conductivity at the interface of each layer (W m-1 K-1) + real(rkind) :: dThermalC_dHydStateAbove ! derivative in the thermal conductivity w.r.t. water state in the layer above + real(rkind) :: dThermalC_dHydStateBelow ! derivative in the thermal conductivity w.r.t. water state in the layer above + real(rkind) :: dThermalC_dNrgStateAbove ! derivative in the thermal conductivity w.r.t. energy state in the layer above + real(rkind) :: dThermalC_dNrgStateBelow ! derivative in the thermal conductivity w.r.t. energy state in the layer above + ! ------------------------------------------------------------------------------------------------------------------------------------------------------ + ! make association of local variables with information in the data structures + associate(& + ixDerivMethod => model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): method used to calculate flux derivatives + ix_bcLowrTdyn => model_decisions(iLookDECISIONS%bcLowrTdyn)%iDecision, & ! intent(in): method used to calculate the lower boundary condition for thermodynamics + ixThCondSnow => model_decisions(iLookDECISIONS%thCondSnow)%iDecision, & ! intent(in): choice of method for thermal conductivity of snow + ixThCondSoil => model_decisions(iLookDECISIONS%thCondSoil)%iDecision, & ! intent(in): choice of method for thermal conductivity of soil + ! input: model coordinates + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers + layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) + ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat, & ! intent(in): index in the state subset for energy state variables in the snow+soil domain + ! input: thermal properties + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): depth of each layer (m) + mLayerHeight => prog_data%var(iLookPROG%mLayerHeight)%dat, & ! intent(in): height at the mid-point of each layer (m) + lowerBoundTemp => mpar_data%var(iLookPARAM%lowerBoundTemp)%dat(1), & ! intent(in): temperature of the lower boundary (K) + iLayerHeight => prog_data%var(iLookPROG%iLayerHeight)%dat, & ! intent(in): height at the interface of each layer (m) + fixedThermalCond_snow => mpar_data%var(iLookPARAM%fixedThermalCond_snow)%dat(1), & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) + iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(in): thermal conductivity at the interface of each layer (W m-1 K-1) + ! input: depth varying soil parameters + iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) + thCond_soil => mpar_data%var(iLookPARAM%thCond_soil)%dat, & ! intent(in): thermal conductivity of soil (W m-1 K-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): soil porosity (-) + frac_sand => mpar_data%var(iLookPARAM%frac_sand)%dat, & ! intent(in): fraction of sand (-) + frac_silt => mpar_data%var(iLookPARAM%frac_silt)%dat, & ! intent(in): fraction of silt (-) + frac_clay => mpar_data%var(iLookPARAM%frac_clay)%dat, & ! intent(in): fraction of clay (-) + vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat, & ! intent(in): [dp(:)] van Genutchen "m" parameter (-) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat, & ! intent(in): [dp(:)] van Genutchen "n" parameter (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat, & ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat, & ! intent(in): [dp(:)] soil residual volumetric water content (-) + ! input: snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ! output: diagnostic fluxes + iLayerConductiveFlux => flux_data%var(iLookFLUX%iLayerConductiveFlux)%dat, & ! intent(out): conductive energy flux at layer interfaces at end of time step (W m-2) + iLayerAdvectiveFlux => flux_data%var(iLookFLUX%iLayerAdvectiveFlux)%dat & ! intent(out): advective energy flux at layer interfaces at end of time step (W m-2) + ) ! association of local variables with information in the data structures + ! ------------------------------------------------------------------------------------------------------------------------------------------------------ + ! initialize error control + err=0; message='ssdNrgFluxSundials/' + + ! initialize the soil layer + iSoil=integerMissing + + ! set conductive and advective fluxes to missing in the upper boundary + ! NOTE: advective flux at the upper boundary is included in the ground heat flux + iLayerConductiveFlux(0) = valueMissing + iLayerAdvectiveFlux(0) = valueMissing + + ! check the need to compute numerical derivatives + if(ixDerivMethod==numerical)then + nFlux=5 ! compute the derivatives and cross derivates using one-sided finite differences + else + nFlux=0 ! compute analytical derivatives + end if + + ! get the indices for the snow+soil layers + if(scalarSolution)then + ixLayerDesired = pack(ixLayerState, ixSnowSoilNrg/=integerMissing) + ixTop = ixLayerDesired(1) + ixBot = ixLayerDesired(1) + else + ixTop = 1 !This needs to be 0 eventually! + ixBot = nLayers + endif + + ! ------------------------------------------------------------------------------------------------------------------------- + ! ***** compute the conductive fluxes at layer interfaces ***** + ! ------------------------------------------------------------------------------------------------------------------------- + do iLayer=ixTop,ixBot ! (loop through model layers) + + if(iLayer>nSnow) iSoil = iLayer-nSnow + + ! compute fluxes at the lower boundary -- positive downwards + if(iLayer==nLayers)then + ! flux depends on the type of lower boundary condition + select case(ix_bcLowrTdyn) ! (identify the lower boundary condition for thermodynamics + case(prescribedTemp); iLayerConductiveFlux(nLayers) = -iLayerThermalC(iLayer)*(lowerBoundTemp - mLayerTempTrial(iLayer))/(mLayerDepth(iLayer)*0.5_rkind) + case(zeroFlux); iLayerConductiveFlux(nLayers) = 0._rkind + case default; err=20; message=trim(message)//'unable to identify lower boundary condition for thermodynamics'; return + end select ! (identifying the lower boundary condition for thermodynamics) + + ! compute fluxes within the domain -- positive downwards + else + iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) / & + (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) + + !write(*,'(a,i4,1x,2(f9.3,1x))') 'iLayer, iLayerConductiveFlux(iLayer), iLayerThermalC(iLayer) = ', iLayer, iLayerConductiveFlux(iLayer), iLayerThermalC(iLayer) + end if ! (the type of layer) + end do + + ! ------------------------------------------------------------------------------------------------------------------------- + ! ***** compute the advective fluxes at layer interfaces ***** + ! ------------------------------------------------------------------------------------------------------------------------- + do iLayer=ixTop,ixBot + ! get the liquid flux at layer interfaces + select case(layerType(iLayer)) + case(iname_snow); qFlux = iLayerLiqFluxSnow(iLayer) + case(iname_soil); qFlux = iLayerLiqFluxSoil(iLayer-nSnow) + case default; err=20; message=trim(message)//'unable to identify layer type'; return + end select + ! compute fluxes at the lower boundary -- positive downwards + if(iLayer==nLayers)then + iLayerAdvectiveFlux(iLayer) = -Cp_water*iden_water*qFlux*(lowerBoundTemp - mLayerTempTrial(iLayer)) + ! compute fluxes within the domain -- positive downwards + else + iLayerAdvectiveFlux(iLayer) = -Cp_water*iden_water*qFlux*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) + end if + end do ! looping through layers + + ! ------------------------------------------------------------------------------------------------------------------------- + ! ***** compute the total fluxes at layer interfaces ***** + ! ------------------------------------------------------------------------------------------------------------------------- + ! NOTE: ignore advective fluxes for now + iLayerNrgFlux(0) = groundNetFlux + iLayerNrgFlux(ixTop:ixBot) = iLayerConductiveFlux(ixTop:ixBot) + !print*, 'iLayerNrgFlux(0:4) = ', iLayerNrgFlux(0:4) + + ! ------------------------------------------------------------------------------------------------------------------------- + ! ***** compute the derivative in fluxes at layer interfaces w.r.t temperature in the layer above and the layer below ***** + ! ------------------------------------------------------------------------------------------------------------------------- + + !FIX, MOVE LAYER 0 TO vegNrgFlux?? Orrr? Right now not computing correctly + ! get the indices for the snow+soil layers + if(.not.scalarSolution) ixTop = 0 + + ! initialize un-used elements + ! ***** the upper boundary + dFlux_dTempAbove(0) = 0._rkind ! this will be in canopy + dFlux_dWatAbove(0) = 0._rkind ! this will be in canopy + + ! ***** the lower boundary + dFlux_dTempBelow(nLayers) = -huge(lowerBoundTemp) ! don't expect this to be used, so deliberately set to a ridiculous value to cause problems + dFlux_dWatBelow(nLayers) = -huge(lowerBoundTemp) ! don't expect this to be used, so deliberately set to a ridiculous value to cause problems + + ! loop through INTERFACES... + do iLayer=ixTop,ixBot + + ! either one or multiple flux calls, depending on if using analytical or numerical derivatives + do itry=nFlux,0,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) + + ! ===== + ! determine layer to perturb + ! ========================== + select case(itry) + ! skip undesired perturbations + case(perturbState); cycle ! perturbing the layers above and below the flux at the interface + ! identify the index for the perturbation + case(unperturbed); ixPerturb = 0 + case(perturbStateTempAbove) + if(iLayer==0) cycle ! cannot perturb state above (does not exist) -- so keep cycling + ixPerturb = 1 + case(perturbStateTempBelow) + if(iLayer==nLayers) cycle ! cannot perturb state below (does not exist) -- so keep cycling + ixPerturb = 2 + case(perturbStateWatAbove) + if(iLayer==0) cycle ! cannot perturb state above (does not exist) -- so keep cycling + ixPerturb = 3 + case(perturbStateWatBelow) + if(iLayer==nLayers) cycle ! cannot perturb state below (does not exist) -- so keep cycling + ixPerturb = 4 + case default; err=10; message=trim(message)//"unknown perturbation"; return + end select ! (identifying layer to of perturbation) + ! determine the index in the original vector + ixOriginal = iLayer + (ixPerturb-1) + + ! ===== + ! set indices and parameters needed for layer perturbation + ! ======================================================== + mLayer_ind(1) = iLayer + mLayer_ind(2) = iLayer+1 + if (iLayer==0 ) mLayer_ind(1) = 1 !not doing ground here, will not use + if (iLayer==nLayers ) mLayer_ind(2) = nLayers + ! indices of interface are different at top layer since interface 0 exists + iLayer_ind = mLayer_ind + if (iLayer==0 ) iLayer_ind(1) = 0 !not doing ground here, will not use + ! soil parameters if layer is soil + do i = 1,2 + if (mLayer_ind(i)>nSnow) then + vectorvGn_alpha(i) = vGn_alpha(mLayer_ind(i)-nSnow) + vectorvGn_n(i) = vGn_n(mLayer_ind(i)-nSnow) + vectorvGn_m(i) = vGn_m(mLayer_ind(i)-nSnow) + vectortheta_sat(i) = theta_sat(mLayer_ind(i)-nSnow) + vectortheta_res(i) = theta_res(mLayer_ind(i)-nSnow) + vectoriden_soil(i) = iden_soil(mLayer_ind(i)-nSnow) + vectorthCond_soil(i) = thCond_soil(mLayer_ind(i)-nSnow) + vectorfrac_sand(i) = frac_sand(mLayer_ind(i)-nSnow) + vectorfrac_silt(i) = frac_silt(mLayer_ind(i)-nSnow) + vectorfrac_clay(i) = frac_clay(mLayer_ind(i)-nSnow) + else + vectorvGn_alpha(i) = realMissing + vectorvGn_n(i) = realMissing + vectorvGn_m(i) = realMissing + vectortheta_sat(i) = realMissing + vectortheta_res(i) = realMissing + vectoriden_soil(i) = realMissing + vectorthCond_soil(i) = realMissing + vectorfrac_sand(i) = realMissing + vectorfrac_silt(i) = realMissing + vectorfrac_clay(iSoil) = realMissing + end if + end do + + ! ===== + ! get input state variables... + ! ============================ + ! start with the un-perturbed case + vectorVolFracLiqTrial(1:2) = mLayerVolFracLiqTrial(mLayer_ind) + vectorMatricHeadTrial(1:2) = mLayerMatricHeadTrial(mLayer_ind) + vectorTempTrial(1:2) = mLayerTempTrial(mLayer_ind) + ! make appropriate perturbations, + if(ixPerturb > 2)then + vectorMatricHeadTrial(ixPerturb-2) = vectorMatricHeadTrial(ixPerturb-2) + dx + vectorVolFracLiqTrial(ixPerturb-2) = vectorVolFracLiqTrial(ixPerturb-2) + dx + else if(ixPerturb > 0)then + vectorTempTrial(ixPerturb) = vectorTempTrial(ixPerturb) + dx + endif + + ! ===== + ! get thermal conductivity at layer interface and its derivative w.r.t. the state above and the state below... + ! ============================================================================================================ + call iLayerThermalConduct(& + ! input: model control + ixThCondSnow, & ! intent(in): choice of method for thermal conductivity of snow + ixThCondSoil, & ! intent(in): choice of method for thermal conductivity of soil + ! input: coordinate variables + nLayers, & ! intent(in): number of layers + iLayer, & ! intent(in): layer index for output + nSnow, & ! intent(in): number of snow layers + layerType(mLayer_ind), & ! intent(in): layer type (iname_soil or iname_snow) + ! input: state variables (adjacent layers) + vectorMatricHeadTrial, & ! intent(in): matric head at the nodes (m) + vectorVolFracLiqTrial, & ! intent(in): volumetric total liquid water at the nodes (m) + vectorTempTrial, & ! intent(in): temperature at the nodes (m) + ! input: model coordinate variables (adjacent layers) + mLayerHeight(mLayer_ind), & ! intent(in): height at the mid-point of the node (m) + iLayerHeight(iLayer_ind), & ! intent(in): height at the interface of the nodes (m) + ! input: soil parameters + vectorvGn_alpha, & ! intent(in): van Genutchen "alpha" parameter (m-1) + vectorvGn_n, & ! intent(in): van Genutchen "n" parameter (-) + vectorvGn_m, & ! intent(in): van Genutchen "m" parameter (-) + vectortheta_sat, & ! intent(in): soil porosity (-) + vectortheta_res, & ! intent(in): soil residual volumetric water content (-) + vectoriden_soil, & ! intent(in): intrinsic density of soil (kg m-3) + vectorthCond_soil, & ! intent(in): thermal conductivity of soil (W m-1 K-1) + vectorfrac_sand, & ! intent(in): fraction of sand (-) + vectorfrac_silt, & ! intent(in): fraction of silt (-) + vectorfrac_clay, & ! intent(in): fraction of clay (-) + ! input: snow parameters + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + fixedThermalCond_snow, & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) + ! output: conductivity at the layer interface (scalars) + scalariLayerThermalC, & ! intent(out): thermal conductivity at the interface of each layer (W m-1 K-1) + ! output: derivatives in thermal conductivity w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below + dThermalC_dHydStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dHydStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + ! output: derivatives in thermal conductivity w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (W m-1 K-2) + dThermalC_dNrgStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dNrgStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + ! output: error control + err,cmessage) ! intent(out): error control + + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + ! compute total vertical flux, to compute derivatives + if(deriv_desired .and. ixDerivMethod==numerical)then + select case(itry) + case(unperturbed); scalarThermCFlux = scalariLayerThermalC + case(perturbStateTempAbove); scalarThermCFlux_dTempAbove = scalariLayerThermalC + case(perturbStateTempBelow); scalarThermCFlux_dTempBelow = scalariLayerThermalC + case(perturbStateWatAbove); scalarThermCFlux_dWatAbove = scalariLayerThermalC + case(perturbStateWatBelow); scalarThermCFlux_dWatBelow = scalariLayerThermalC + case default; err=10; message=trim(message)//"unknown perturbation"; return + end select + end if + + end do ! (looping through different flux calculations -- one or multiple calls depending if desire for numerical or analytical derivatives) + + ! ***** the upper boundary + if(iLayer==0)then ! (upper boundary) + !dz = (mLayerHeight(1) - mLayerHeight(iLayer)) + !if(ixDerivMethod==analytical)then ! ** analytical derivatives + !dFlux_dWatBelow(iLayer) = -dThermalC_dHydStateBelow * ( mLayerTempTrial(1) - groundtemp )/dz + !dFlux_dTempBelow(iLayer) = -dThermalC_dNrgStateBelow * ( mLayerTempTrial(1) - groundtemp )/dz - scalariLayerThermalC/dz + !else ! ** numerical derivatives + !flux0 = -scalarThermCFlux *( mLayerTempTrial(1) - groundtemp ) / dz + !flux2 = -scalarThermCFlux_dWatBelow*( mLayerTempTrial(1) - groundtemp ) / dz + !dFlux_dWatBelow(iLayer) = (flux2 - flux0)/dx + !flux0 = -scalarThermCFlux *( mLayerTempTrial(1) - groundtemp ) / dz + !flux2 = -scalarThermCFlux_dTempBelow*((mLayerTempTrial(1)+dx) - groundtemp ) / dz + !dFlux_dTempBelow(iLayer) = (flux2 - flux0)/dx + !end if + dFlux_dWatBelow(iLayer) = 0._rkind + dFlux_dTempBelow(iLayer) = dGroundNetFlux_dGroundTemp + + ! ***** the lower boundary + else if(iLayer==nLayers)then ! (lower boundary) + + ! identify the lower boundary condition + select case(ix_bcLowrTdyn) + + ! * prescribed temperature at the lower boundary + case(prescribedTemp) + dz = mLayerDepth(iLayer)*0.5_rkind + if(ixDerivMethod==analytical)then ! ** analytical derivatives + dFlux_dWatAbove(iLayer) = -dThermalC_dHydStateAbove * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + dFlux_dTempAbove(iLayer) = -dThermalC_dNrgStateAbove * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + scalariLayerThermalC/dz + else ! ** numerical derivatives + flux0 = -scalarThermCFlux * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + flux1 = -scalarThermCFlux_dWatAbove * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + dFlux_dWatAbove(iLayer) = (flux1 - flux0)/dx + flux0 = -scalarThermCFlux * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + flux1 = -scalarThermCFlux_dTempAbove * ( lowerBoundTemp - (mLayerTempTrial(iLayer)+dx) )/dz + dFlux_dTempAbove(iLayer) = (flux1 - flux0)/dx + end if + + ! * zero flux at the lower boundary + case(zeroFlux) + dFlux_dWatAbove(iLayer) = 0._rkind + dFlux_dTempAbove(iLayer) = 0._rkind + + case default; err=20; message=trim(message)//'unable to identify lower boundary condition for thermodynamics'; return + + end select ! (identifying the lower boundary condition for thermodynamics) + + ! ***** internal layers + + else + dz = (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) + if(ixDerivMethod==analytical)then ! ** analytical derivatives + dFlux_dWatAbove(iLayer) = -dThermalC_dHydStateAbove * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz + dFlux_dWatBelow(iLayer) = -dThermalC_dHydStateBelow * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz + dFlux_dTempAbove(iLayer) = -dThermalC_dNrgStateAbove * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz + scalariLayerThermalC/dz + dFlux_dTempBelow(iLayer) = -dThermalC_dNrgStateBelow * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz - scalariLayerThermalC/dz + else ! ** numerical derivatives + flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz + flux1 = -scalarThermCFlux_dWatAbove*( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz + flux2 = -scalarThermCFlux_dWatBelow*( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz + dFlux_dWatAbove(iLayer) = (flux1 - flux0)/dx + dFlux_dWatBelow(iLayer) = (flux2 - flux0)/dx + flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz + flux1 = -scalarThermCFlux_dTempAbove*( mLayerTempTrial(iLayer+1) - (mLayerTempTrial(iLayer)+dx)) / dz + flux2 = -scalarThermCFlux_dTempBelow*((mLayerTempTrial(iLayer+1)+dx) - mLayerTempTrial(iLayer) ) / dz + dFlux_dTempAbove(iLayer) = (flux1 - flux0)/dx + dFlux_dTempBelow(iLayer) = (flux2 - flux0)/dx + end if + + end if ! type of layer (upper, internal, or lower) + + end do ! (looping through layers) + + ! end association of local variables with information in the data structures + end associate + + end subroutine ssdNrgFluxSundials + + + ! ********************************************************************************************************** + ! private subroutine iLayerThermalConduct: compute diagnostic energy variables (thermal conductivity and heat capacity) and derivatives + ! ********************************************************************************************************** + subroutine iLayerThermalConduct(& + ! input: model control + ixThCondSnow, & ! intent(in): choice of method for thermal conductivity of snow + ixThCondSoil, & ! intent(in): choice of method for thermal conductivity of soil + ! input: coordinate variables + nLayers, & ! intent(in): number of layers + ixLayerDesired, & ! intent(in): layer index for output + nSnow, & ! intent(in): number of snow layers + layerType, & ! intent(in): layer type (iname_soil or iname_snow) + ! input: state variables (adjacent layers) + nodeMatricHeadTrial, & ! intent(in): matric head at the nodes (m) + nodeVolFracLiqTrial0, & ! intent(inout): volumetric liquid water content at the nodes (m) + nodeTempTrial, & ! intent(in): temperature at the nodes (m) + ! input: model coordinate variables (adjacent layers) + nodeHeight, & ! intent(in): height at the mid-point of the node (m) + node_iHeight, & ! intent(in): height at the interface of the nodes (m) + ! input: soil parameters at nodes + vGn_alpha, & ! intent(in): van Genutchen "alpha" parameter (m-1) + vGn_n, & ! intent(in): van Genutchen "n" parameter (-) + VGn_m, & ! intent(in): van Genutchen "m" parameter (-) + theta_sat, & ! intent(in): soil porosity (-) + theta_res, & ! intent(in): soil residual volumetric water content (-) + iden_soil, & !intrinsic density of soil (kg m-3) + thCond_soil, & ! thermal conductivity of soil (W m-1 K-1) + frac_sand, & ! intent(in): fraction of sand (-) + frac_silt, & ! fraction of silt (-) + frac_clay, & ! fraction of clay (-) + ! input: snow parameters + snowfrz_scale, & ! scaling parameter for the snow freezing curve (K-1) + fixedThermalCond_snow, & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) + ! output: conductivity at the layer interface (scalars) + iLayerThermalC, & ! thermal conductivity at the interface of each layer (W m-1 K-1) + ! output: derivatives in thermal conductivity w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below + dThermalC_dHydStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dHydStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + ! output: derivatives in thermal conductivity w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (W m-1 K-2) + dThermalC_dNrgStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dNrgStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + ! output: error control + err,message) ! intent(out): error control + USE snow_utils_module,only:tcond_snow ! compute thermal conductivity of snow + USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water + USE soil_utils_module,only:dTheta_dPsi ! derivative in the soil water characteristic (soil) + USE soil_utils_module,only:dTheta_dTk ! differentiate the freezing curve w.r.t. temperature (soil) + ! constants + USE multiconst, only: gravity, & ! gravitational acceleration (m s-1) + Tfreeze, & ! freezing point of water (K) + iden_water,iden_ice,& ! intrinsic density of water and ice (kg m-3) + LH_fus ! latent heat of fusion (J kg-1) + implicit none + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! input: model control + integer(i4b),intent(in) :: ixThCondSnow ! intent(in): choice of method for thermal conductivity of snow + integer(i4b),intent(in) :: ixThCondSoil ! intent(in): choice of method for thermal conductivity of soil + ! input: coordinate variables + integer(i4b),intent(in) :: nLayers ! intent(in): number of layers + integer(i4b),intent(in) :: ixLayerDesired ! intent(in): layer index for output + integer(i4b),intent(in) :: nSnow ! intent(in): number of snow layers + integer(i4b),intent(in) :: layerType(:) ! intent(in): layer type (iname_soil or iname_snow) + ! input: state variables + real(rkind),intent(in) :: nodeMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind),intent(in) :: nodeVolFracLiqTrial0(:) ! trial vector of volumetric liquid water content, recomputed with perturbed water state(-) + real(rkind),intent(in) :: nodeTempTrial(:) ! trial vector of temperature (K) + ! input: model coordinate variables + real(rkind),intent(in) :: nodeHeight(:) ! height at the mid-point of the lower node (m) + real(rkind),intent(in) :: node_iHeight(:) ! height at the interface of each node (m) + ! input: soil parameters + real(rkind),intent(in) :: vGn_alpha(:) ! van Genutchen "alpha" parameter (m-1) + real(rkind),intent(in) :: vGn_n(:) ! van Genutchen "n" parameter (-) + real(rkind),intent(in) :: vGn_m(:) ! van Genutchen "m" parameter (-) + real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) + real(rkind),intent(in) :: theta_res(:) ! soil residual volumetric water content (-) + real(rkind),intent(in) :: iden_soil(:) ! intrinsic density of soil (kg m-3) + real(rkind),intent(in) :: thCond_soil(:) ! thermal conductivity of soil (W m-1 K-1) + real(rkind),intent(in) :: frac_sand(:) ! intent(in): fraction of sand (-) + real(rkind),intent(in) :: frac_silt(:) ! fraction of silt (-) + real(rkind),intent(in) :: frac_clay(:) ! fraction of clay (-) + ! input: snow parameters + real(rkind),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) + real(rkind),intent(in) :: fixedThermalCond_snow ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) + ! output: thermal conductivity at layer interfaces + real(rkind),intent(out) :: iLayerThermalC ! thermal conductivity at the interface of each layer (W m-1 K-1) + ! output: thermal conductivity derivatives at all layer interfaces + real(rkind),intent(out) :: dThermalC_dHydStateAbove ! derivatives in the thermal conductivity w.r.t. matric head or volumetric liquid water in the layer above + real(rkind),intent(out) :: dThermalC_dHydStateBelow ! derivatives in the thermal conductivity w.r.t. matric head or volumetric liquid water in the layer below + real(rkind),intent(out) :: dThermalC_dNrgStateAbove ! derivatives in the thermal conductivity w.r.t. temperature in the layer above (W m-1 K-2) + real(rkind),intent(out) :: dThermalC_dNrgStateBelow ! derivatives in the thermal conductivity w.r.t. temperature in the layer below (W m-1 K-2) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables (named variables to provide index of 2-element vectors) + integer(i4b),parameter :: ixUpper=1 ! index of upper node in the 2-element vectors + integer(i4b),parameter :: ixLower=2 ! index of lower node in the 2-element vectors + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: iSoil ! index of soil layer + real(rkind) :: matricHead ! matric head for frozen soil + real(rkind) :: TCn ! thermal conductivity below the layer interface (W m-1 K-1) + real(rkind) :: TCp ! thermal conductivity above the layer interface (W m-1 K-1) + real(rkind) :: zdn ! height difference between interface and lower value (m) + real(rkind) :: zdp ! height difference between interface and upper value (m) + real(rkind) :: bulkden_soil ! bulk density of soil (kg m-3) + real(rkind) :: lambda_drysoil ! thermal conductivity of dry soil (W m-1) + real(rkind) :: lambda_wetsoil ! thermal conductivity of wet soil (W m-1) + real(rkind) :: lambda_wet ! thermal conductivity of the wet material + real(rkind) :: relativeSat ! relative saturation (-) + real(rkind) :: kerstenNum ! the Kersten number (-), defining weight applied to conductivity of the wet medium + real(rkind) :: den ! denominator in the thermal conductivity calculations + real(rkind) :: mLayerdThermalC_dWat(2) ! derivative in thermal conductivity w.r.t. matric head or volumetric liquid water + real(rkind) :: mLayerdThermalC_dNrg(2) ! derivative in thermal conductivity w.r.t. temperature + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind) :: fLiq ! fraction of liquid water (-) + real(rkind) :: dlambda_wet_dPsi0 ! derivative in thermal conductivity of wet material w.r.t. matric head + real(rkind) :: dlambda_wet_dTk ! derivative in thermal conductivity of wet material w.r.t. temperature + real(rkind) :: dkerstenNum_dPsi0 ! derivative in Kersten number w.r.t. matric head + real(rkind) :: nodeVolTotWatTrial(2) ! trial vector of volumetric total water content (-) + real(rkind) :: nodeVolFracLiqTrial(2) ! trial vector of volumetric liquid water content, recomputed from input if perturbed water state (-) + real(rkind) :: nodeVolFracIceTrial(2) ! trial vector of volumetric ice water content (-) + real(rkind) :: nodeVolFracAirTrial(2) ! trial vector of volumetric air water content (-) + real(rkind) :: mLayerThermalC(2) ! thermal conductivity of each layer (W m-1 K-1) + real(rkind) :: mLayerdVolFracLiq_dPsi0 ! derivative in water retention curve above critical temp w.r.t. matric head + real(rkind) :: mLayerdVolFracLiq_dTk ! derivative in water retention curve below critical temp w.r.t. temperature + real(rkind) :: mLayerdVolFracIce_dTheta ! derivative in vol fraction of ice w.r.t. volumetric liquid water + real(rkind) :: mLayerdVolFracIce_dTk ! derivative in vol fraction of ice w.r.t. temperature +! local variables to reproduce the thermal conductivity of Hansson et al. VZJ 2005 + real(rkind),parameter :: c1=0.55_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c2=0.8_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c3=3.07_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: c4=0.13_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c5=4._rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: f1=13.05_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: f2=1.06_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind) :: fArg,xArg ! temporary variables (see Hansson et al. VZJ 2005 for details) + real(rkind) :: dxArg_dPsi0,dxArg_dTk ! derivates of the temporary variables with respect to matric head and temperature + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="iLayerThermalConduct/" + + ! initialize the soil layer + iSoil=integerMissing + + ! loop through layers + do iLayer=ixUpper,ixLower + + ! compute the thermal conductivity of dry and wet soils (W m-1) + ! NOTE: this is actually constant over the simulation, and included here for clarity + if(ixThCondSoil==funcSoilWet .and. layerType(iLayer)==iname_soil)then + bulkden_soil = iden_soil(iLayer)*( 1._rkind - theta_sat(iLayer) ) + lambda_drysoil = (0.135_rkind*bulkden_soil + 64.7_rkind) / (iden_soil(iLayer) - 0.947_rkind*bulkden_soil) + lambda_wetsoil = (8.80_rkind*frac_sand(iLayer) + 2.92_rkind*frac_clay(iLayer)) / (frac_sand(iLayer) + frac_clay(iLayer)) + end if + + ! ***** + ! * compute the volumetric fraction of liquid, ice, and air in each layer in response to perturbation ... + ! ******************************************************************************************************* + select case(layerType(iLayer)) + case(iname_soil) + Tcrit = crit_soilT(nodeMatricHeadTrial(iLayer) ) + ! do we also need to consider ixRichards form? FIX + if( nodeTempTrial(iLayer) < Tcrit) then !if do not perturb temperature, this should not change, but nodeMatricHeadTrial will have changed + matricHead = (LH_fus/gravity)*(nodeTempTrial(iLayer) - Tfreeze)/Tfreeze + nodeVolFracLiqTrial(iLayer) = volFracLiq(matricHead,theta_res(iLayer),theta_sat(iLayer),vGn_alpha(iSoil),vGn_n(iLayer),vGn_m(iLayer)) + else + nodeVolFracLiqTrial(iLayer) = volFracLiq(nodeMatricHeadTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) + endif + nodeVolFracAirTrial(iLayer) = theta_sat(iLayer) - volFracLiq(nodeMatricHeadTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) + nodeVolFracIceTrial(iLayer) = theta_sat(iLayer) - (nodeVolFracAirTrial(iLayer) + nodeVolFracLiqTrial(iLayer)) + case(iname_snow) + fliq = 1._rkind / ( 1._rkind + (snowfrz_scale*( Tfreeze - nodeTempTrial(iLayer) ))**2._rkind ) + nodeVolTotWatTrial(iLayer) = nodeVolFracLiqTrial0(iLayer) / fliq ! use potentially perturbed nodeVolTotWatTrial + nodeVolFracLiqTrial(iLayer) = nodeVolFracLiqTrial0(iLayer) + nodeVolFracIceTrial(iLayer) = (nodeVolTotWatTrial(iLayer) - nodeVolFracLiqTrial(iLayer))*(iden_water/iden_ice) + nodeVolFracAirTrial(iLayer) = 1._rkind - (nodeVolFracIceTrial(iLayer) + nodeVolFracLiqTrial(iLayer)) + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute volumetric fraction of air'; return + end select + + ! ***** + ! * compute the thermal conductivity of snow and soil and derivates at the mid-point of each layer... + ! *************************************************************************************************** + mLayerdThermalC_dWat(iLayer) = 0._rkind + mLayerdThermalC_dNrg(iLayer) = 0._rkind + + select case(layerType(iLayer)) + + ! ***** soil + case(iname_soil) + mLayerdVolFracLiq_dPsi0 = dTheta_dPsi(nodeMatricHeadTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) + mLayerdVolFracLiq_dTk = dTheta_dTk(nodeTempTrial(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_alpha(iSoil),vGn_n(iLayer),vGn_m(iLayer)) + + ! select option for thermal conductivity of soil + select case(ixThCondSoil) + + ! ** function of soil wetness + case(funcSoilWet) + + ! compute the thermal conductivity of the wet material (W m-1) + lambda_wet = lambda_wetsoil**( 1._rkind - theta_sat(iLayer) ) * lambda_water**theta_sat(iLayer) * lambda_ice**(theta_sat(iLayer) - nodeVolFracLiqTrial(iLayer)) + if( nodeTempTrial(iLayer) < Tcrit) then + dlambda_wet_dPsi0 = 0._rkind + dlambda_wet_dTk = lambda_wet * LOG(lambda_ice) * (-mLayerdVolFracLiq_dTk) + else + dlambda_wet_dPsi0 = lambda_wet * LOG(lambda_ice) * (-mLayerdVolFracLiq_dPsi0) + dlambda_wet_dTk = 0._rkind + endif + relativeSat = (nodeVolFracIceTrial(iLayer) + nodeVolFracLiqTrial(iLayer))/theta_sat(iLayer) ! relative saturation + ! compute the Kersten number (-) + if(relativeSat > 0.1_rkind)then ! log10(0.1) = -1 + kerstenNum = log10(relativeSat) + 1._rkind + ! these derivatives are the same frozen or unfrozen + dkerstenNum_dPsi0 = mLayerdVolFracLiq_dPsi0 + !dkerstenNum_dTk = 0._rkind because drelativeSat_dTk = 0 + else + kerstenNum = 0._rkind ! dry thermal conductivity + dkerstenNum_dPsi0 = 0._rkind + !dkerstenNum_dTk = 0._rkind + endif + ! ...and, compute the thermal conductivity + mLayerThermalC(iLayer) = kerstenNum*lambda_wet + (1._rkind - kerstenNum)*lambda_drysoil + + ! compute derivatives + mLayerdThermalC_dWat(iLayer) = dkerstenNum_dPsi0 * ( lambda_wet - lambda_drysoil ) + kerstenNum*dlambda_wet_dPsi0 + mLayerdThermalC_dNrg(iLayer) = kerstenNum*dlambda_wet_dTk + + ! ** mixture of constituents + case(mixConstit) + mLayerThermalC(iLayer) = thCond_soil(iSoil) * ( 1._rkind - theta_sat(iLayer) ) + & ! soil component + lambda_ice * nodeVolFracIceTrial(iLayer) + & ! ice component + lambda_water * nodeVolFracLiqTrial(iLayer) + & ! liquid water component + lambda_air * nodeVolFracAirTrial(iLayer) ! air component + + ! compute derivatives + if( nodeTempTrial(iLayer) < Tcrit) then + mLayerdThermalC_dWat(iLayer) = (lambda_ice - lambda_air) * mLayerdVolFracLiq_dPsi0 + mLayerdThermalC_dNrg(iLayer) = (-lambda_ice + lambda_water) * mLayerdVolFracLiq_dTk + else + mLayerdThermalC_dWat(iLayer) = (lambda_water - lambda_air) * mLayerdVolFracLiq_dPsi0 + mLayerdThermalC_dNrg(iLayer) = 0._rkind + endif + + ! ** test case for the mizoguchi lab experiment, Hansson et al. VZJ 2004 + case(hanssonVZJ) + fArg = 1._rkind + f1*nodeVolFracIceTrial(iLayer)**f2 + xArg = nodeVolFracLiqTrial(iLayer) + fArg*nodeVolFracIceTrial(iLayer) + if( nodeTempTrial(iLayer) < Tcrit) then + dxArg_dPsi0 = mLayerdVolFracLiq_dPsi0 * f1*(f2+1)*nodeVolFracIceTrial(iLayer)**f2 + dxArg_dTk = mLayerdVolFracLiq_dTk * (-f1)*(f2+1)*nodeVolFracIceTrial(iLayer)**f2 + else + dxArg_dPsi0 = mLayerdVolFracLiq_dPsi0 * (2._rkind + f1*(f2+1)*nodeVolFracIceTrial(iLayer)**f2) + dxArg_dTk = 0._rkind + endif + ! ...and, compute the thermal conductivity + mLayerThermalC(iLayer) = c1 + c2*xArg + (c1 - c4)*exp(-(c3*xArg)**c5) + + ! compute derivatives + mLayerdThermalC_dWat(iLayer) = ( c2 + -(c3*xArg)**(c5-1)*(c1 - c4)*exp(-(c3*xArg)**c5) ) * dxArg_dPsi0 + mLayerdThermalC_dNrg(iLayer) = ( c2 + -(c3*xArg)**(c5-1)*(c1 - c4)*exp(-(c3*xArg)**c5) ) * dxArg_dTk + + ! ** check + case default; err=20; message=trim(message)//'unable to identify option for thermal conductivity of soil'; return + + end select ! option for the thermal conductivity of soil + + ! ***** snow + case(iname_snow) + mLayerdVolFracIce_dTheta = ( 1._rkind - fLiq )*(iden_water/iden_ice) + mLayerdVolFracIce_dTk = -dFracLiq_dTk(nodeTempTrial(iLayer),snowfrz_scale) * nodeVolFracLiqTrial(iLayer)*(iden_water/iden_ice) + + ! temporally constant thermal conductivity + if(ixThCondSnow==Smirnova2000)then + mLayerThermalC(iLayer) = fixedThermalCond_snow + mLayerdThermalC_dWat(iLayer) = 0._rkind + mLayerdThermalC_dNrg(iLayer) = 0._rkind + ! thermal conductivity as a function of snow density + else + call tcond_snow(nodeVolFracIceTrial(iLayer)*iden_ice, & ! input: snow density (kg m-3) + mLayerThermalC(iLayer), & ! output: thermal conductivity (W m-1 K-1) + err,cmessage) ! output: error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + select case(ixThCondSnow) + case(Yen1965) + mLayerdThermalC_dWat(iLayer) = 2._rkind * 3.217d-6 * nodeVolFracIceTrial(iLayer) * iden_ice * mLayerdVolFracIce_dTheta + mLayerdThermalC_dNrg(iLayer) = 2._rkind * 3.217d-6 * nodeVolFracIceTrial(iLayer) * iden_ice * mLayerdVolFracIce_dTk + case(Mellor1977) + mLayerdThermalC_dWat(iLayer) = 2._rkind * 2.576d-6 * nodeVolFracIceTrial(iLayer) * iden_ice * mLayerdVolFracIce_dTheta + mLayerdThermalC_dNrg(iLayer) = 2._rkind * 2.576d-6 * nodeVolFracIceTrial(iLayer) * iden_ice * mLayerdVolFracIce_dTk + case(Jordan1991) + mLayerdThermalC_dWat(iLayer) = ( 7.75d-5 + 2._rkind * 1.105d-6 * nodeVolFracIceTrial(iLayer) * iden_ice ) * (lambda_ice-lambda_air) * mLayerdVolFracIce_dTheta + mLayerdThermalC_dNrg(iLayer) = ( 7.75d-5 + 2._rkind * 1.105d-6 * nodeVolFracIceTrial(iLayer) * iden_ice ) * (lambda_ice-lambda_air) * mLayerdVolFracIce_dTk + end select ! option for the thermal conductivity of snow + end if + + ! * error check + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute thermal conductivity'; return + + end select + !print*, 'iLayer, mLayerThermalC(iLayer) = ', iLayer, mLayerThermalC(iLayer) + + end do ! looping through layers + !pause + + ! ***** + ! * compute the thermal conductivity of snow at the interface of each layer... + ! **************************************************************************** + if (ixLayerDesired==0 ) then + ! special case of hansson + if(ixThCondSoil==hanssonVZJ)then + iLayerThermalC = 28._rkind*(0.5_rkind*(node_iHeight(ixLower) - node_iHeight(ixUpper))) ! these are indices 1,0 since was passed with 0:1 + dThermalC_dHydStateBelow = 0._rkind + dThermalC_dNrgStateBelow = 0._rkind + else + iLayerThermalC = mLayerThermalC(1) + dThermalC_dHydStateBelow = mLayerdThermalC_dWat(ixLower) !these are index 1 since was passed with 1:2 + dThermalC_dNrgStateBelow = mLayerdThermalC_dNrg(ixLower) !these are index 1 since was passed with 1:2 + end if + dThermalC_dHydStateAbove = 0._rkind !should be missing + dThermalC_dNrgStateAbove = 0._rkind !should be missing + else if (ixLayerDesired==nLayers ) then + ! assume the thermal conductivity at the domain boundaries is equal to the thermal conductivity of the layer + iLayerThermalC = mLayerThermalC(nLayers) + dThermalC_dHydStateAbove = mLayerdThermalC_dWat(ixLower) !these are index 2 since was passed with iLayers-1:iLayers + dThermalC_dNrgStateAbove = mLayerdThermalC_dNrg(ixLower) !these are index 2 since was passed with iLayers-1:iLayers + dThermalC_dHydStateBelow = 0._rkind !should be missing + dThermalC_dNrgStateBelow = 0._rkind !should be missing + else + ! get temporary variables + TCn = mLayerThermalC(ixUpper) ! thermal conductivity below the layer interface (W m-1 K-1) + TCp = mLayerThermalC(ixLower) ! thermal conductivity above the layer interface (W m-1 K-1) + zdn = node_iHeight(ixUpper) - nodeHeight(ixUpper) ! height difference between interface and lower value (m) + zdp = nodeHeight(ixLower) - node_iHeight(ixUpper) ! height difference between interface and upper value (m) + den = TCn*zdp + TCp*zdn ! denominator + ! compute thermal conductivity + if(TCn+TCp > epsilon(TCn))then + iLayerThermalC = (TCn*TCp*(zdn + zdp)) / den + dThermalC_dHydStateBelow = ( TCn*(zdn + zdp) + iLayerThermalC*zdn ) / den * mLayerdThermalC_dWat(ixLower) + dThermalC_dHydStateAbove = ( TCp*(zdn + zdp) + iLayerThermalC*zdp ) / den * mLayerdThermalC_dWat(ixUpper) + dThermalC_dNrgStateBelow = ( TCn*(zdn + zdp) + iLayerThermalC*zdn ) / den * mLayerdThermalC_dNrg(ixLower) + dThermalC_dNrgStateAbove = ( TCp*(zdn + zdp) + iLayerThermalC*zdp ) / den * mLayerdThermalC_dNrg(ixUpper) + else + iLayerThermalC = (TCn*zdn + TCp*zdp) / (zdn + zdp) + dThermalC_dHydStateBelow = zdp / (zdn + zdp) * mLayerdThermalC_dWat(ixLower) + dThermalC_dHydStateAbove = zdn / (zdn + zdp) * mLayerdThermalC_dWat(ixUpper) + dThermalC_dNrgStateBelow = zdp / (zdn + zdp) * mLayerdThermalC_dNrg(ixLower) + dThermalC_dNrgStateAbove = zdn / (zdn + zdp) * mLayerdThermalC_dNrg(ixUpper) + end if + !write(*,'(a,1x,i4,1x,10(f9.3,1x))') 'iLayer, TCn, TCp, zdn, zdp, iLayerThermalC(iLayer) = ', iLayer, TCn, TCp, zdn, zdp, iLayerThermalC(iLayer) + endif + + end subroutine iLayerThermalConduct + + + end module ssdNrgFlux_module diff --git a/build/source/engine/updateVars4JacDAE.f90 b/build/source/engine/updateVars4JacDAE.f90 index 1c91cdfe4..d6c75815f 100644 --- a/build/source/engine/updateVars4JacDAE.f90 +++ b/build/source/engine/updateVars4JacDAE.f90 @@ -56,6 +56,8 @@ module updateVars4JacDAE_module gravity, & ! acceleration of gravity (m s-2) Tfreeze, & ! temperature at freezing (K) Cp_air, & ! specific heat of air (J kg-1 K-1) + Cp_ice, & ! specific heat of ice (J kg-1 K-1) + Cp_water, & ! specific heat of liquid water (J kg-1 K-1) LH_fus, & ! latent heat of fusion (J kg-1) iden_air, & ! intrinsic density of air (kg m-3) iden_ice, & ! intrinsic density of ice (kg m-3) @@ -140,6 +142,11 @@ subroutine updateVars4JacDAE(& dFracLiqSnow_dTk, & d2Theta_dTkCanopy2, & dFracLiqVeg_dTkCanopy, & + dVolHtCapBulk_dPsi0, & ! derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dThetaCan, & ! derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! derivative in bulk heat capacity w.r.t. temperature ! output: error control err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- @@ -180,6 +187,11 @@ subroutine updateVars4JacDAE(& real(rkind),intent(out) :: dFracLiqSnow_dTk(:) ! reza real(rkind),intent(out) :: d2Theta_dTkCanopy2 real(rkind),intent(out) :: dFracLiqVeg_dTkCanopy + real(rkind),intent(out) :: dVolHtCapBulk_dPsi0(:) ! derivative in bulk heat capacity w.r.t. matric potential + real(rkind),intent(out) :: dVolHtCapBulk_dTheta(:) ! derivative in bulk heat capacity w.r.t. volumetric water content + real(rkind),intent(out) :: dVolHtCapBulk_dThetaCan ! derivative in bulk heat capacity w.r.t. volumetric water content + real(rkind),intent(out) :: dVolHtCapBulk_dTk(:) ! derivative in bulk heat capacity w.r.t. temperature + real(rkind),intent(out) :: dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -200,6 +212,7 @@ subroutine updateVars4JacDAE(& real(rkind) :: scalarVolFracIcePrime ! volumetric fraction of ice (-) real(rkind) :: Tcrit ! critical soil temperature below which ice exists (K) real(rkind) :: xTemp ! temporary temperature (K) + real(rkind) :: fLiq ! fraction of liquid water (-) real(rkind) :: effSat ! effective saturation (-) real(rkind) :: avPore ! available pore space (-) character(len=256) :: cMessage ! error message of downwind routine @@ -441,45 +454,62 @@ subroutine updateVars4JacDAE(& ! - compute derivatives... ! ------------------------ + ! compute the derivative in bulk heat capacity w.r.t. total water content or water matric potential (m-1) ! compute the derivative in total water content w.r.t. total water matric potential (m-1) ! NOTE 1: valid for frozen and unfrozen conditions ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi - if(ixDomainType==iname_soil)then + select case(ixDomainType) + case(iname_veg) + fliq = 1._rkind / ( 1._rkind + (snowfrz_scale*( Tfreeze - xTemp ))**2._rkind ) + dVolHtCapBulk_dThetaCan = scalarCanopyLiq/canopyDepth * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + case(iname_snow) + fliq = 1._rkind / ( 1._rkind + (snowfrz_scale*( Tfreeze - xTemp ))**2._rkind ) + dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fliq ) * Cp_air + case(iname_soil) select case( ixStateType(ixFullVector) ) - case(iname_lmpLayer) - dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore - d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + case(iname_lmpLayer) + dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + ! dVolHtCapBulk_dPsi0 uses the derivative in water retention curve above critical temp w.r.t. matric head + dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_ice * Cp_ice - iden_air * Cp_air) *& + dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) case default - dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),& - vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),& + vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + ! dVolHtCapBulk_dPsi0 uses the derivative in water retention curve above critical temp w.r.t. matric head, here is dVolTot_dPsi0 + dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_water * Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) end select - endif + end select ! compute the derivative in liquid water content w.r.t. temperature ! --> partially frozen: dependence of liquid water on temperature + ! compute the derivative in bulk heat capacity w.r.t. temperature if(xTemp unfrozen: no dependence of liquid water on temperature else select case(ixDomainType) - case(iname_veg); dTheta_dTkCanopy = 0._rkind; d2Theta_dTkCanopy2 = 0; dFracLiqVeg_dTkCanopy = 0 - case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind; mLayerd2Theta_dTk2(iLayer) = 0._rkind; dFracLiqSnow_dTk(iLayer) = 0 + case(iname_veg); dTheta_dTkCanopy = 0._rkind; d2Theta_dTkCanopy2 = 0._rkind; dFracLiqVeg_dTkCanopy = 0._rkind; dVolHtCapBulk_dTkCanopy = 0._rkind + case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind; mLayerd2Theta_dTk2(iLayer) = 0._rkind; dFracLiqSnow_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return end select ! domain type endif diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 1471d0d04..789f8bcc0 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -430,7 +430,7 @@ subroutine vegNrgFlux(& ! input: model decisions ix_bcUpprTdyn => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision, & ! intent(in): [i4b] choice of upper boundary condition for thermodynamics - ix_fDerivMeth => model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): [i4b] choice of method to compute derivatives + ixDerivMethod => model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): [i4b] choice of method to compute derivatives ix_veg_traits => model_decisions(iLookDECISIONS%veg_traits)%iDecision, & ! intent(in): [i4b] choice of parameterization for vegetation roughness length and displacement height ix_canopyEmis => model_decisions(iLookDECISIONS%canopyEmis)%iDecision, & ! intent(in): [i4b] choice of parameterization for canopy emissivity ix_windPrfile => model_decisions(iLookDECISIONS%windPrfile)%iDecision, & ! intent(in): [i4b] choice of canopy wind profile @@ -772,7 +772,7 @@ subroutine vegNrgFlux(& call wettedFrac(& ! input .true., & ! flag to denote if derivative is desired - (ix_fDerivMeth == numerical), & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) + (ixDerivMethod == numerical), & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) (scalarLatHeatSubVapCanopy > LH_vap+verySmall), & ! flag to denote if the canopy is frozen dCanLiq_dTcanopy, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) fracLiquidCanopy, & ! fraction of liquid water on the canopy (-) @@ -817,7 +817,7 @@ subroutine vegNrgFlux(& call aeroResist(& ! input: model control computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) - (ix_fDerivMeth == analytical), & ! intent(in): logical flag if would like to compute analytical derivaties + (ixDerivMethod == analytical), & ! intent(in): logical flag if would like to compute analytical derivaties ix_veg_traits, & ! intent(in): choice of parameterization for vegetation roughness length and displacement height ix_windPrfile, & ! intent(in): choice of canopy wind profile ix_astability, & ! intent(in): choice of stability function @@ -950,7 +950,7 @@ subroutine vegNrgFlux(& ! compute canopy longwave radiation balance call longwaveBal(& ! input: model control - ix_fDerivMeth, & ! intent(in): method used to calculate flux derivatives + ixDerivMethod, & ! intent(in): method used to calculate flux derivatives computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation ! input: canopy and ground temperature canopyTempTrial, & ! intent(in): temperature of the vegetation canopy (K) @@ -994,7 +994,7 @@ subroutine vegNrgFlux(& ! ******************************************************************************************************************************************************************* ! check the need to compute numerical derivatives - if(ix_fDerivMeth == numerical)then + if(ixDerivMethod == numerical)then nFlux=5 ! compute the derivatives using one-sided finite differences else nFlux=1 ! compute analytical derivatives @@ -1050,7 +1050,7 @@ subroutine vegNrgFlux(& call wettedFrac(& ! input .false., & ! flag to denote if derivative is desired - (ix_fDerivMeth == numerical), & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) + (ixDerivMethod == numerical), & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) (scalarLatHeatSubVapCanopy > LH_vap+verySmall), & ! flag to denote if the canopy is frozen dCanLiq_dTcanopy, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) fracLiquidCanopy, & ! fraction of liquid water on the canopy (-) @@ -1184,7 +1184,7 @@ subroutine vegNrgFlux(& call turbFluxes(& ! input: model control computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) - ix_fDerivMeth, & ! intent(in): method used to calculate flux derivatives + ixDerivMethod, & ! intent(in): method used to calculate flux derivatives ! input: above-canopy forcing data airtemp, & ! intent(in): air temperature at some height above the surface (K) airpres, & ! intent(in): air pressure of the air above the vegetation canopy (Pa) @@ -1292,7 +1292,7 @@ subroutine vegNrgFlux(& !trialCanopyResistance ! above canopy aerodynamic resistance (s m-1) ! save perturbed fluxes - if(ix_fDerivMeth == numerical)then + if(ixDerivMethod == numerical)then select case(itry) ! (select type of perturbation) case(unperturbed) try0 = turbFluxGround @@ -1325,13 +1325,13 @@ subroutine vegNrgFlux(& end do ! (looping through different flux perturbations) ! test derivative - !if(ix_fDerivMeth == numerical) print*, 'try0, try1 = ', try0, try1 - !if(ix_fDerivMeth == numerical) print*, 'derivative = ', (ix_fDerivMeth == numerical), (try1 - try0)/dx - !if(ix_fDerivMeth == analytical) print*, 'derivative = ', (ix_fDerivMeth == numerical), dTurbFluxGround_dTGround + !if(ixDerivMethod == numerical) print*, 'try0, try1 = ', try0, try1 + !if(ixDerivMethod == numerical) print*, 'derivative = ', (ixDerivMethod == numerical), (try1 - try0)/dx + !if(ixDerivMethod == analytical) print*, 'derivative = ', (ixDerivMethod == numerical), dTurbFluxGround_dTGround !pause ! compute numerical derivatives - if(ix_fDerivMeth == numerical)then + if(ixDerivMethod == numerical)then ! derivatives w.r.t. canopy air temperature dTurbFluxCanair_dTCanair = (turbFluxCanair_dStateCanair - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) dTurbFluxCanopy_dTCanair = (turbFluxCanopy_dStateCanair - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) @@ -1356,7 +1356,7 @@ subroutine vegNrgFlux(& !if(heightCanopyBottom < scalarSnowDepth+z0Ground) pause 'bottom of the canopy is covered' ! test - !print*, (ix_fDerivMeth == numerical) + !print*, (ixDerivMethod == numerical) !print*, 'dTurbFluxCanair_dTCanair = ', dTurbFluxCanair_dTCanair !print*, 'dTurbFluxCanair_dTCanopy = ', dTurbFluxCanair_dTCanopy !print*, 'dTurbFluxCanair_dTGround = ', dTurbFluxCanair_dTGround @@ -1488,7 +1488,7 @@ subroutine vegNrgFlux(& dCanopyNetFlux_dCanLiq = dTurbFluxCanopy_dCanLiq ! derivative in net canopy fluxes w.r.t. canopy liquid water content (J kg-1 s-1) dGroundNetFlux_dCanLiq = dTurbFluxGround_dCanLiq ! derivative in net ground fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - !print*, (ix_fDerivMeth == numerical) + !print*, (ixDerivMethod == numerical) !print*, 'dGroundNetFlux_dCanairTemp = ', dGroundNetFlux_dCanairTemp !print*, 'dCanopyNetFlux_dCanopyTemp = ', dCanopyNetFlux_dCanopyTemp !print*, 'dGroundNetFlux_dCanopyTemp = ', dGroundNetFlux_dCanopyTemp From cd73515848b6effb74cd9fb1af637cf528d6805b Mon Sep 17 00:00:00 2001 From: ashleymedin <34691332+ashleymedin@users.noreply.github.com> Date: Wed, 16 Feb 2022 13:23:21 +0900 Subject: [PATCH 0241/1472] Moving derivates out of updateVars4JacDAE call and putting them in derivative structure where they belong. Also fixed above and below layer derivates with bulk heat capacity for all configurations. --- build/source/dshare/get_ixname.f90 | 18 +++++-- build/source/dshare/popMetadat.f90 | 18 +++++-- build/source/dshare/var_lookup.f90 | 22 ++++++-- build/source/engine/computJacDAE.f90 | 58 +++++++++------------- build/source/engine/eval8DAE.f90 | 2 +- build/source/engine/eval8JacDAE.f90 | 35 +------------ build/source/engine/solveByIDA.f90 | 2 +- build/source/engine/ssdNrgFlux.f90 | 41 +++++++-------- build/source/engine/updateVars4JacDAE.f90 | 38 ++++++-------- build/source/engine/updateVarsSundials.f90 | 2 +- 10 files changed, 109 insertions(+), 127 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 5b5a88954..0a7535b48 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -726,7 +726,9 @@ function get_ixderiv(varName) case('dGroundEvaporation_dCanLiq' ); get_ixderiv = iLookDERIV%dGroundEvaporation_dCanLiq ! derivative in ground evaporation w.r.t. canopy liquid water content (s-1) ! derivatives in canopy water w.r.t canopy temperature case('dTheta_dTkCanopy' ); get_ixderiv = iLookDERIV%dTheta_dTkCanopy ! derivative of volumetric liquid water content w.r.t. temperature (K-1) + case('d2Theta_dTkCanopy2' ); get_ixderiv = iLookDERIV%d2Theta_dTkCanopy2 ! second derivative of volumetric liquid water content w.r.t. temperature case('dCanLiq_dTcanopy' ); get_ixderiv = iLookDERIV%dCanLiq_dTcanopy ! derivative of canopy liquid storage w.r.t. temperature (kg m-2 K-1) + case('dFracLiqVeg_dTkCanopy' ); get_ixderiv = iLookDERIV%dFracLiqVeg_dTkCanopy ! derivative in fraction of (throughfall + drainage) w.r.t. temperature ! derivatives in canopy liquid fluxes w.r.t. canopy water case('scalarCanopyLiqDeriv' ); get_ixderiv = iLookDERIV%scalarCanopyLiqDeriv ! derivative in (throughfall + canopy drainage) w.r.t. canopy liquid water (s-1) case('scalarThroughfallRainDeriv' ); get_ixderiv = iLookDERIV%scalarThroughfallRainDeriv ! derivative in throughfall w.r.t. canopy liquid water (s-1) @@ -735,12 +737,13 @@ function get_ixderiv(varName) case('dNrgFlux_dTempAbove' ); get_ixderiv = iLookDERIV%dNrgFlux_dTempAbove ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) case('dNrgFlux_dTempBelow' ); get_ixderiv = iLookDERIV%dNrgFlux_dTempBelow ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below - case('dNrgFlux_dWatAbove' ); get_ixderiv = iLookDERIV%dNrgFlux_dWatAbove ! derivatives in the flux w.r.t. water state temperature in the layer above - case('dNrgFlux_dWatBelow' ); get_ixderiv = iLookDERIV%dNrgFlux_dWatBelow ! derivatives in the flux w.r.t. water state in the layer below + case('dNrgFlux_dWatAbove' ); get_ixderiv = iLookDERIV%dNrgFlux_dWatAbove ! derivatives in the flux w.r.t. water state temperature in the layer above + case('dNrgFlux_dWatBelow' ); get_ixderiv = iLookDERIV%dNrgFlux_dWatBelow ! derivatives in the flux w.r.t. water state in the layer below ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above case('iLayerLiqFluxSnowDeriv' ); get_ixderiv = iLookDERIV%iLayerLiqFluxSnowDeriv ! derivative in vertical liquid water flux at layer interfaces (m s-1) ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables case('dVolTot_dPsi0' ); get_ixderiv = iLookDERIV%dVolTot_dPsi0 ! derivative in total water content w.r.t. total water matric potential (m-1) + case('d2VolTot_d2Psi0' ); get_ixderiv = iLookDERIV%d2VolTot_d2Psi0 ! second derivative in total water content w.r.t. total water matric potential case('dq_dHydStateAbove' ); get_ixderiv = iLookDERIV%dq_dHydStateAbove ! change in the flux in layer interfaces w.r.t. state variables in the layer above case('dq_dHydStateBelow' ); get_ixderiv = iLookDERIV%dq_dHydStateBelow ! change in the flux in layer interfaces w.r.t. state variables in the layer below case('mLayerdTheta_dPsi' ); get_ixderiv = iLookDERIV%mLayerdTheta_dPsi ! derivative in the soil water characteristic w.r.t. psi (m-1) @@ -751,9 +754,18 @@ function get_ixderiv(varName) ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables case('dq_dNrgStateAbove' ); get_ixderiv = iLookDERIV%dq_dNrgStateAbove ! change in the flux in layer interfaces w.r.t. state variables in the layer above case('dq_dNrgStateBelow' ); get_ixderiv = iLookDERIV%dq_dNrgStateBelow ! change in the flux in layer interfaces w.r.t. state variables in the layer below - case('mLayerdTheta_dTk' ); get_ixderiv = iLookDERIV%mLayerdTheta_dTk ! derivative of volumetric liquid water content w.r.t. temperature (K-1) case('dPsiLiq_dTemp' ); get_ixderiv = iLookDERIV%dPsiLiq_dTemp ! derivative in the liquid water matric potential w.r.t. temperature (m K-1) case('dPsiLiq_dPsi0' ); get_ixderiv = iLookDERIV%dPsiLiq_dPsi0 ! derivative in liquid matric potential w.r.t. total matric potential (-) + ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature + case('dFracLiqSnow_dTk' ); get_ixderiv = iLookDERIV%dFracLiqSnow_dTk ! derivative in fraction of liquid snow w.r.t. temperature + case('mLayerdTheta_dTk' ); get_ixderiv = iLookDERIV%mLayerdTheta_dTk ! derivative of volumetric liquid water content w.r.t. temperature (K-1) + case('mLayerd2Theta_dTk2' ); get_ixderiv = iLookDERIV%mLayerd2Theta_dTk2 ! second derivative of volumetric liquid water content w.r.t. temperature + ! derivate in bulk heat capacity w.r.t. relevant state variables + case('dVolHtCapBulk_dPsi0' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dPsi0 ! derivative in bulk heat capacity w.r.t. matric potential + case('dVolHtCapBulk_dTheta' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTheta ! derivative in bulk heat capacity w.r.t. volumetric water content + case('dVolHtCapBulk_dThetaCan' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dThetaCan ! derivative in bulk heat capacity w.r.t. volumetric water content + case('dVolHtCapBulk_dTk' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTk ! derivative in bulk heat capacity w.r.t. temperature + case('dVolHtCapBulk_dTkCanopy' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature case default get_ixderiv = integerMissing diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 3fc7c092a..3035b2f5e 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -560,7 +560,9 @@ subroutine popMetadat(err,message) deriv_meta(iLookDERIV%dGroundEvaporation_dCanLiq) = var_info('dGroundEvaporation_dCanLiq' , 'derivative in ground evaporation w.r.t. canopy liquid water content' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! derivatives in canopy water w.r.t canopy temperature deriv_meta(iLookDERIV%dTheta_dTkCanopy) = var_info('dTheta_dTkCanopy' , 'derivative of volumetric liquid water content w.r.t. temperature' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%d2Theta_dTkCanopy2) = var_info('d2Theta_dTkCanopy2' , 'second derivative of volumetric liquid water content w.r.t. temperature', 'K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dCanLiq_dTcanopy) = var_info('dCanLiq_dTcanopy' , 'derivative of canopy liquid storage w.r.t. temperature' , 'kg m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dFracLiqVeg_dTkCanopy) = var_info('dFracLiqVeg_dTkCanopy' , 'derivative in fraction of (throughfall + drainage) w.r.t. temperature', 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! derivatives in canopy liquid fluxes w.r.t. canopy water deriv_meta(iLookDERIV%scalarCanopyLiqDeriv) = var_info('scalarCanopyLiqDeriv' , 'derivative in (throughfall + drainage) w.r.t. canopy liquid water' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%scalarThroughfallRainDeriv) = var_info('scalarThroughfallRainDeriv' , 'derivative in throughfall w.r.t. canopy liquid water' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) @@ -569,12 +571,13 @@ subroutine popMetadat(err,message) deriv_meta(iLookDERIV%dNrgFlux_dTempAbove) = var_info('dNrgFlux_dTempAbove' , 'derivatives in the flux w.r.t. temperature in the layer above' , 'J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dNrgFlux_dTempBelow) = var_info('dNrgFlux_dTempBelow' , 'derivatives in the flux w.r.t. temperature in the layer below' , 'J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below - deriv_meta(iLookDERIV%dNrgFlux_dWatAbove) = var_info('dNrgFlux_dWatAbove' , 'derivatives in the flux w.r.t. water state in the layer above' , 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dNrgFlux_dWatBelow) = var_info('dNrgFlux_dWatBelow' , 'derivatives in the flux w.r.t. water state in the layer below' , 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dNrgFlux_dWatAbove) = var_info('dNrgFlux_dWatAbove' , 'derivatives in the flux w.r.t. water state in the layer above' , 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dNrgFlux_dWatBelow) = var_info('dNrgFlux_dWatBelow' , 'derivatives in the flux w.r.t. water state in the layer below' , 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above deriv_meta(iLookDERIV%iLayerLiqFluxSnowDeriv) = var_info('iLayerLiqFluxSnowDeriv' , 'derivative in vertical liquid water flux at layer interfaces' , 'm s-1' , get_ixVarType('ifcSnow'), iMissVec, iMissVec, .false.) ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables deriv_meta(iLookDERIV%dVolTot_dPsi0) = var_info('dVolTot_dPsi0' , 'derivative in total water content w.r.t. total water matric potential', 'm-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%d2VolTot_d2Psi0) = var_info('d2VolTot_d2Psi0' , 'second derivative in total water content w.r.t. total water matric potential', 'm-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dCompress_dPsi) = var_info('dCompress_dPsi' , 'derivative in compressibility w.r.t matric head' , 'm-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%mLayerdTheta_dPsi) = var_info('mLayerdTheta_dPsi' , 'derivative in the soil water characteristic w.r.t. psi' , 'm-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%mLayerdPsi_dTheta) = var_info('mLayerdPsi_dTheta' , 'derivative in the soil water characteristic w.r.t. theta' , 'm' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) @@ -585,9 +588,18 @@ subroutine popMetadat(err,message) ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables deriv_meta(iLookDERIV%dq_dNrgStateAbove) = var_info('dq_dNrgStateAbove' , 'change in flux at layer interfaces w.r.t. states in the layer above' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dq_dNrgStateBelow) = var_info('dq_dNrgStateBelow' , 'change in flux at layer interfaces w.r.t. states in the layer below' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%mLayerdTheta_dTk) = var_info('mLayerdTheta_dTk' , 'derivative of volumetric liquid water content w.r.t. temperature' , 'K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dPsiLiq_dTemp) = var_info('dPsiLiq_dTemp' , 'derivative in the liquid water matric potential w.r.t. temperature' , 'm K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dPsiLiq_dPsi0) = var_info('dPsiLiq_dPsi0' , 'derivative in liquid matric potential w.r.t. total matric potential' , '-' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature + deriv_meta(iLookDERIV%dFracLiqSnow_dTk) = var_info('dFracLiqSnow_dTk' , 'derivative in fraction of liquid snow w.r.t. temperature' , 'K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%mLayerdTheta_dTk) = var_info('mLayerdTheta_dTk' , 'derivative of volumetric liquid water content w.r.t. temperature' , 'K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%mLayerd2Theta_dTk2) = var_info('mLayerd2Theta_dTk2' , 'second derivative of volumetric liquid water content w.r.t. temperature', 'K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + ! derivate in bulk heat capacity w.r.t. relevant state variables + deriv_meta(iLookDERIV%dVolHtCapBulk_dPsi0) = var_info('dVolHtCapBulk_dPsi0' , 'derivative in bulk heat capacity w.r.t. matric potential' , 'J m-4 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dTheta) = var_info('dVolHtCapBulk_dTheta' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dThetaCan) = var_info('dVolHtCapBulk_dThetaCan' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dTk) = var_info('dVolHtCapBulk_dTk' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dTkCanopy) = var_info('dVolHtCapBulk_dTkCanopy' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! ----- ! * basin-wide runoff and aquifer fluxes... diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index c144c7885..6adcdd687 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -579,7 +579,9 @@ MODULE var_lookup integer(i4b) :: dGroundEvaporation_dCanLiq = integerMissing ! derivative in ground evaporation w.r.t. canopy liquid water content (s-1) ! derivatives in canopy water w.r.t canopy temperature integer(i4b) :: dTheta_dTkCanopy = integerMissing ! derivative of volumetric liquid water content w.r.t. temperature (K-1) + integer(i4b) :: d2Theta_dTkCanopy2 = integerMissing ! second derivative of volumetric liquid water content w.r.t. temperature integer(i4b) :: dCanLiq_dTcanopy = integerMissing ! derivative of canopy liquid storage w.r.t. temperature (kg m-2 K-1) + integer(i4b) :: dFracLiqVeg_dTkCanopy = integerMissing ! derivative in fraction of (throughfall + drainage) w.r.t. temperature ! derivatives in canopy liquid fluxes w.r.t. canopy water integer(i4b) :: scalarCanopyLiqDeriv = integerMissing ! derivative in (throughfall + canopy drainage) w.r.t. canopy liquid water (s-1) integer(i4b) :: scalarThroughfallRainDeriv = integerMissing ! derivative in throughfall w.r.t. canopy liquid water (s-1) @@ -588,12 +590,13 @@ MODULE var_lookup integer(i4b) :: dNrgFlux_dTempAbove = integerMissing ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) integer(i4b) :: dNrgFlux_dTempBelow = integerMissing ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below - integer(i4b) :: dNrgFlux_dWatAbove = integerMissing ! derivatives in the flux w.r.t. water state in the layer above - integer(i4b) :: dNrgFlux_dWatBelow = integerMissing ! derivatives in the flux w.r.t. water state in the layer below + integer(i4b) :: dNrgFlux_dWatAbove = integerMissing ! derivatives in the flux w.r.t. water state in the layer above + integer(i4b) :: dNrgFlux_dWatBelow = integerMissing ! derivatives in the flux w.r.t. water state in the layer below ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above integer(i4b) :: iLayerLiqFluxSnowDeriv = integerMissing ! derivative in vertical liquid water flux at layer interfaces (m s-1) ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables integer(i4b) :: dVolTot_dPsi0 = integerMissing ! derivative in total water content w.r.t. total water matric potential (m-1) + integer(i4b) :: d2VolTot_d2Psi0 = integerMissing ! second derivative in total water content w.r.t. total water matric potential integer(i4b) :: dq_dHydStateAbove = integerMissing ! change in the flux in layer interfaces w.r.t. state variables in the layer above integer(i4b) :: dq_dHydStateBelow = integerMissing ! change in the flux in layer interfaces w.r.t. state variables in the layer below integer(i4b) :: mLayerdTheta_dPsi = integerMissing ! derivative in the soil water characteristic w.r.t. psi (m-1) @@ -604,9 +607,19 @@ MODULE var_lookup ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables integer(i4b) :: dq_dNrgStateAbove = integerMissing ! change in the flux in layer interfaces w.r.t. state variables in the layer above integer(i4b) :: dq_dNrgStateBelow = integerMissing ! change in the flux in layer interfaces w.r.t. state variables in the layer below - integer(i4b) :: mLayerdTheta_dTk = integerMissing ! derivative of volumetric liquid water content w.r.t. temperature (K-1) integer(i4b) :: dPsiLiq_dTemp = integerMissing ! derivative in the liquid water matric potential w.r.t. temperature (m K-1) integer(i4b) :: dPsiLiq_dPsi0 = integerMissing ! derivative in liquid water matric potential w.r.t. the total water matric potential (-) + ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature + integer(i4b) :: dFracLiqSnow_dTk = integerMissing ! derivative in fraction of liquid snow w.r.t. temperature + integer(i4b) :: mLayerdTheta_dTk = integerMissing ! derivative of volumetric liquid water content w.r.t. temperature (K-1) + integer(i4b) :: mLayerd2Theta_dTk2 = integerMissing ! second derivative of volumetric liquid water content w.r.t. temperature + ! derivate in bulk heat capacity w.r.t. relevant state variables + integer(i4b) :: dVolHtCapBulk_dPsi0 = integerMissing ! derivative in bulk heat capacity w.r.t. matric potential + integer(i4b) :: dVolHtCapBulk_dTheta = integerMissing ! derivative in bulk heat capacity w.r.t. volumetric water content + integer(i4b) :: dVolHtCapBulk_dThetaCan = integerMissing ! derivative in bulk heat capacity w.r.t. volumetric water content + integer(i4b) :: dVolHtCapBulk_dTk = integerMissing ! derivative in bulk heat capacity w.r.t. temperature + integer(i4b) :: dVolHtCapBulk_dTkCanopy = integerMissing ! derivative in bulk heat capacity w.r.t. temperature + endtype iLook_deriv ! *********************************************************************************************************** @@ -847,7 +860,8 @@ MODULE var_lookup 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,& 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,& - 41) + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,& + 51) ! named variables: model indices type(iLook_index), public,parameter :: iLookINDEX =ilook_index ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 53c1a85f5..8159802bd 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -121,17 +121,6 @@ subroutine computJacDAE(& scalarCanopyTemp, & ! intent(in) scalarCanopyTempPrime, & ! intent(in) derivative value for temperature of the vegetation canopy (K) scalarCanopyWatPrime, & ! intent(in) - mLayerd2Theta_dTk2, & ! intent(in) - d2VolTot_d2Psi0, & ! intent(in) - dFracLiqSnow_dTk, & ! intent(in) - d2Theta_dTkCanopy2, & ! intent(in) - dFracLiqVeg_dTkCanopy, & ! intent(in) - dVolHtCapBulk_dPsi0, & ! intent(in): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(in): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dThetaCan, & ! intent(in): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(in): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(in): derivative in bulk heat capacity w.r.t. temperature - ! input-output: Jacobian and its diagonal dMat, & ! intent(inout): diagonal of the Jacobian matrix aJac, & ! intent(out): Jacobian matrix @@ -162,21 +151,10 @@ subroutine computJacDAE(& real(rkind),intent(in) :: mLayerTempPrime(:) real(rkind),intent(in) :: mLayerMatricHeadPrime(:) real(rkind),intent(in) :: mLayerMatricHeadLiqPrime(:) - real(rkind),intent(in) :: mLayerd2Theta_dTk2(:) real(rkind),intent(in) :: mLayerVolFracWatPrime(:) real(rkind),intent(in) :: scalarCanopyTemp real(rkind),intent(in) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) real(rkind),intent(in) :: scalarCanopyWatPrime - real(rkind),intent(in) :: d2VolTot_d2Psi0(:) - real(rkind),intent(in) :: dFracLiqSnow_dTk(:) - real(rkind),intent(in) :: d2Theta_dTkCanopy2 - real(rkind),intent(in) :: dFracLiqVeg_dTkCanopy - real(rkind),intent(out) :: dVolHtCapBulk_dPsi0(:) ! derivative in bulk heat capacity w.r.t. matric potential - real(rkind),intent(out) :: dVolHtCapBulk_dTheta(:) ! derivative in bulk heat capacity w.r.t. volumetric water content - real(rkind),intent(out) :: dVolHtCapBulk_dThetaCan ! derivative in bulk heat capacity w.r.t. volumetric water content - real(rkind),intent(out) :: dVolHtCapBulk_dTk(:) ! derivative in bulk heat capacity w.r.t. temperature - real(rkind),intent(out) :: dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature - ! input-output: Jacobian and its diagonal real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix @@ -262,6 +240,8 @@ subroutine computJacDAE(& ! derivatives in canopy water w.r.t canopy temperature dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(in): [dp] derivative in canopy liquid storage w.r.t. temperature dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative in volumetric liquid water content w.r.t. temperature + d2Theta_dTkCanopy2 => deriv_data%var(iLookDERIV%d2Theta_dTkCanopy2 )%dat(1) ,& ! intent(in): [dp] second derivative of volumetric liquid water content w.r.t. temperature + dFracLiqVeg_dTkCanopy => deriv_data%var(iLookDERIV%dFracLiqVeg_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative in fraction of (throughfall + drainage) w.r.t. temperature ! derivatives in canopy liquid fluxes w.r.t. canopy water scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ,& ! intent(in): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below @@ -274,25 +254,35 @@ subroutine computJacDAE(& iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(in): [dp(:)] derivative in vertical liquid water flux at layer interfaces ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + d2VolTot_d2Psi0 => deriv_data%var(iLookDERIV%d2VolTot_d2Psi0 )%dat ,& ! intent(in): [dp(:)] second derivative in total water content w.r.t. total water matric potential + dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t matric head dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below - dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t matric head ! derivative in baseflow flux w.r.t. aquifer storage dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] derivative in baseflow flux w.r.t. aquifer storage (s-1) ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature + dFracLiqSnow_dTk => deriv_data%var(iLookDERIV%dFracLiqSnow_dTk )%dat ,& ! intent(in): [dp(:)] derivative in fraction of liquid snow w.r.t. temperature mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk )%dat ,& ! intent(in): [dp(:)] derivative in volumetric liquid water content w.r.t. temperature + mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2 )%dat ,& ! intent(in): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature + ! derivate in bulk heat capacity w.r.t. relevant state variables + dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dThetaCan => deriv_data%var(iLookDERIV%dVolHtCapBulk_dThetaCan )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. temperature ! diagnostic variables - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) - scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) - mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] bulk volumetric heat capacity in each snow and soil layer (J m-3 K-1) - scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl)%dat(1) ,& ! intent(in): [dp] soil control on infiltration, zero or one + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg )%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg )%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow )%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk )%dat ,& ! intent(in): [dp(:)] bulk volumetric heat capacity in each snow and soil layer (J m-3 K-1) + scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1) ,& ! intent(in): [dp] soil control on infiltration, zero or one ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] named variables defining the type of layer in snow+soil domain + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth )%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth )%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + layerType => indx_data%var(iLookINDEX%layerType )%dat & ! intent(in): [i4b(:)] named variables defining the type of layer in snow+soil domain ) ! making association with data in structures ! -------------------------------------------------------------- ! initialize error control @@ -389,7 +379,7 @@ subroutine computJacDAE(& if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanLiq) + cj * (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth & + dVolHtCapBulk_dThetaCan * scalarCanopyTempPrime & + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth ! dF/dLiq - if(ixCasNrg/=integerMissing) aJac(ixCasNrg,ixVegHyd) = (dt/canopyDepth)*(-dCanAirNetFlux_dCanLiq) !doesn't exist + !if(ixCasNrg/=integerMissing) aJac(ixCasNrg,ixVegHyd) = (dt/canopyDepth)*(-dCanAirNetFlux_dCanLiq) !doesn't exist if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanLiq) endif @@ -513,7 +503,7 @@ subroutine computJacDAE(& ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above if(iLayer>1)then - if(ixSnowOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSnowOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) + if(ixSnowOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSnowOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) ) endif ! (cross-derivative terms for the layer below) @@ -625,7 +615,7 @@ subroutine computJacDAE(& ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above if(iLayer>1)then - if(ixSoilOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSoilOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) + if(ixSoilOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSoilOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) ) endif ! (cross-derivative terms for the layer below) diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index a39892614..82e48fd4c 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -157,7 +157,7 @@ subroutine eval8DAE(& USE updateVarsSundials_module, only:updateVarsSundials ! update variables USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy USE computFlux_module, only:soilCmpresSundials ! compute soil compression - USE computFlux_module, only:computFlux ! compute fluxes given a state vector + USE computFlux_module, only:computFluxSundials ! compute fluxes given a state vector USE computHeatCap_module,only:computHeatCap ! compute heat capacity USE computHeatCap_module,only:computHeatCapAnalytic ! compute heat capacity USE computHeatCap_module,only:computCm diff --git a/build/source/engine/eval8JacDAE.f90 b/build/source/engine/eval8JacDAE.f90 index cec65e8d8..cae27b10f 100644 --- a/build/source/engine/eval8JacDAE.f90 +++ b/build/source/engine/eval8JacDAE.f90 @@ -170,7 +170,6 @@ subroutine eval8JacDAE(& real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) - ! derivative of state variables real(rkind) :: scalarCanairTempPrime ! derivative value for temperature of the canopy air space (K) real(rkind) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) @@ -188,16 +187,6 @@ subroutine eval8JacDAE(& ! other local variables character(LEN=256) :: cmessage ! error message of downwind routine real(rkind) :: dt1 - real(rkind) :: mLayerd2Theta_dTk2(nLayers) - real(rkind) :: d2VolTot_d2Psi0(nLayers) - real(rkind) :: dFracLiqSnow_dTk(nLayers) - real(rkind) :: d2Theta_dTkCanopy2 - real(rkind) :: dFracLiqVeg_dTkCanopy - real(rkind) :: dVolHtCapBulk_dPsi0(nLayers) ! derivative in bulk heat capacity w.r.t. matric potential - real(rkind) :: dVolHtCapBulk_dTheta(nLayers) ! derivative in bulk heat capacity w.r.t. volumetric water content - real(rkind) :: dVolHtCapBulk_dThetaCan ! derivative in bulk heat capacity w.r.t. volumetric water content - real(rkind) :: dVolHtCapBulk_dTk(nLayers) ! derivative in bulk heat capacity w.r.t. temperature - real(rkind) :: dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature ! -------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures @@ -302,16 +291,6 @@ subroutine eval8JacDAE(& mLayerVolFracIcePrime, & ! mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) - mLayerd2Theta_dTk2, & ! intetn(out) - d2VolTot_d2Psi0, & ! intetn(out) - dFracLiqSnow_dTk, & ! intent(out) - d2Theta_dTkCanopy2, & ! intent(out) - dFracLiqVeg_dTkCanopy, & ! intent(out) - dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dThetaCan, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) @@ -323,7 +302,7 @@ subroutine eval8JacDAE(& ! -------------------------------- ! compute the analytical Jacobian matrix - ! NOTE: The derivatives were computed in the previous call to computFlux + ! NOTE: The derivatives were computed in the previous call to computFluxSundials ! This occurred either at the call to eval8DAE at the start of sysSolveSundials ! or in the call to eval8DAE in the previous iteration dt1 = 1._qp @@ -354,17 +333,7 @@ subroutine eval8JacDAE(& mLayerVolFracWatPrime, & ! intent(in) scalarCanopyTempTrial, & ! intent(in) scalarCanopyTempPrime, & ! intent(in) derivative value for temperature of the vegetation canopy (K) - scalarCanopyWatPrime, & ! intetn(in) - mLayerd2Theta_dTk2, & ! intent(in) - d2VolTot_d2Psi0, & ! intent(in) - dFracLiqSnow_dTk, & ! intent(in) - d2Theta_dTkCanopy2, & ! intent(in) - dFracLiqVeg_dTkCanopy, & ! intent(in) - dVolHtCapBulk_dPsi0, & ! intent(in): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(in): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dThetaCan, & ! intent(in): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(in): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(in): derivative in bulk heat capacity w.r.t. temperature + scalarCanopyWatPrime, & ! intetn(in) ! input-output: Jacobian and its diagonal dMat, & ! intent(inout): diagonal of the Jacobian matrix Jac, & ! intent(out): Jacobian matrix diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 8796ff206..fe48541e4 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -394,7 +394,7 @@ subroutine solveByIDA( & if(ixMatrix == ixFullMatrix)then ! Set the user-supplied Jacobian routine !if (i==2) retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) - !retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) !commment this line out to use FD Jacobian + retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) !commment this line out to use FD Jacobian if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetJacFn'; return; endif endif diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index 7c3be1982..786bb5f1f 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -638,7 +638,7 @@ subroutine ssdNrgFluxSundials(& vectorthCond_soil(i) = realMissing vectorfrac_sand(i) = realMissing vectorfrac_silt(i) = realMissing - vectorfrac_clay(iSoil) = realMissing + vectorfrac_clay(i) = realMissing end if end do @@ -897,7 +897,6 @@ subroutine iLayerThermalConduct(& integer(i4b),parameter :: ixLower=2 ! index of lower node in the 2-element vectors character(LEN=256) :: cmessage ! error message of downwind routine integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: iSoil ! index of soil layer real(rkind) :: matricHead ! matric head for frozen soil real(rkind) :: TCn ! thermal conductivity below the layer interface (W m-1 K-1) real(rkind) :: TCp ! thermal conductivity above the layer interface (W m-1 K-1) @@ -941,9 +940,6 @@ subroutine iLayerThermalConduct(& ! initialize error control err=0; message="iLayerThermalConduct/" - ! initialize the soil layer - iSoil=integerMissing - ! loop through layers do iLayer=ixUpper,ixLower @@ -964,7 +960,7 @@ subroutine iLayerThermalConduct(& ! do we also need to consider ixRichards form? FIX if( nodeTempTrial(iLayer) < Tcrit) then !if do not perturb temperature, this should not change, but nodeMatricHeadTrial will have changed matricHead = (LH_fus/gravity)*(nodeTempTrial(iLayer) - Tfreeze)/Tfreeze - nodeVolFracLiqTrial(iLayer) = volFracLiq(matricHead,theta_res(iLayer),theta_sat(iLayer),vGn_alpha(iSoil),vGn_n(iLayer),vGn_m(iLayer)) + nodeVolFracLiqTrial(iLayer) = volFracLiq(matricHead,theta_res(iLayer),theta_sat(iLayer),vGn_alpha(iLayer),vGn_n(iLayer),vGn_m(iLayer)) else nodeVolFracLiqTrial(iLayer) = volFracLiq(nodeMatricHeadTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) endif @@ -990,7 +986,7 @@ subroutine iLayerThermalConduct(& ! ***** soil case(iname_soil) mLayerdVolFracLiq_dPsi0 = dTheta_dPsi(nodeMatricHeadTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) - mLayerdVolFracLiq_dTk = dTheta_dTk(nodeTempTrial(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_alpha(iSoil),vGn_n(iLayer),vGn_m(iLayer)) + mLayerdVolFracLiq_dTk = dTheta_dTk(nodeTempTrial(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_alpha(iLayer),vGn_n(iLayer),vGn_m(iLayer)) ! select option for thermal conductivity of soil select case(ixThCondSoil) @@ -1002,22 +998,21 @@ subroutine iLayerThermalConduct(& lambda_wet = lambda_wetsoil**( 1._rkind - theta_sat(iLayer) ) * lambda_water**theta_sat(iLayer) * lambda_ice**(theta_sat(iLayer) - nodeVolFracLiqTrial(iLayer)) if( nodeTempTrial(iLayer) < Tcrit) then dlambda_wet_dPsi0 = 0._rkind - dlambda_wet_dTk = lambda_wet * LOG(lambda_ice) * (-mLayerdVolFracLiq_dTk) + dlambda_wet_dTk = lambda_wet * log(lambda_ice) * (-mLayerdVolFracLiq_dTk) else - dlambda_wet_dPsi0 = lambda_wet * LOG(lambda_ice) * (-mLayerdVolFracLiq_dPsi0) + dlambda_wet_dPsi0 = lambda_wet * log(lambda_ice) * (-mLayerdVolFracLiq_dPsi0) dlambda_wet_dTk = 0._rkind endif relativeSat = (nodeVolFracIceTrial(iLayer) + nodeVolFracLiqTrial(iLayer))/theta_sat(iLayer) ! relative saturation + ! drelativeSat_dPsi0 = mLayerdVolFracLiq_dPsi0, and drelativeSat_dTk = 0 (so dkerstenNum_dTk = 0) ! compute the Kersten number (-) if(relativeSat > 0.1_rkind)then ! log10(0.1) = -1 kerstenNum = log10(relativeSat) + 1._rkind ! these derivatives are the same frozen or unfrozen - dkerstenNum_dPsi0 = mLayerdVolFracLiq_dPsi0 - !dkerstenNum_dTk = 0._rkind because drelativeSat_dTk = 0 + dkerstenNum_dPsi0 = mLayerdVolFracLiq_dPsi0 / ( theta_sat(iLayer) * relativeSat * log(10._rkind) ) else kerstenNum = 0._rkind ! dry thermal conductivity dkerstenNum_dPsi0 = 0._rkind - !dkerstenNum_dTk = 0._rkind endif ! ...and, compute the thermal conductivity mLayerThermalC(iLayer) = kerstenNum*lambda_wet + (1._rkind - kerstenNum)*lambda_drysoil @@ -1028,7 +1023,7 @@ subroutine iLayerThermalConduct(& ! ** mixture of constituents case(mixConstit) - mLayerThermalC(iLayer) = thCond_soil(iSoil) * ( 1._rkind - theta_sat(iLayer) ) + & ! soil component + mLayerThermalC(iLayer) = thCond_soil(iLayer) * ( 1._rkind - theta_sat(iLayer) ) + & ! soil component lambda_ice * nodeVolFracIceTrial(iLayer) + & ! ice component lambda_water * nodeVolFracLiqTrial(iLayer) + & ! liquid water component lambda_air * nodeVolFracAirTrial(iLayer) ! air component @@ -1047,18 +1042,18 @@ subroutine iLayerThermalConduct(& fArg = 1._rkind + f1*nodeVolFracIceTrial(iLayer)**f2 xArg = nodeVolFracLiqTrial(iLayer) + fArg*nodeVolFracIceTrial(iLayer) if( nodeTempTrial(iLayer) < Tcrit) then - dxArg_dPsi0 = mLayerdVolFracLiq_dPsi0 * f1*(f2+1)*nodeVolFracIceTrial(iLayer)**f2 - dxArg_dTk = mLayerdVolFracLiq_dTk * (-f1)*(f2+1)*nodeVolFracIceTrial(iLayer)**f2 + dxArg_dPsi0 = mLayerdVolFracLiq_dPsi0 * (1._rkind + f1*(f2+1)*nodeVolFracIceTrial(iLayer)**f2) + dxArg_dTk = mLayerdVolFracLiq_dTk * (1._rkind - f1*(f2+1)*nodeVolFracIceTrial(iLayer)**f2) else - dxArg_dPsi0 = mLayerdVolFracLiq_dPsi0 * (2._rkind + f1*(f2+1)*nodeVolFracIceTrial(iLayer)**f2) + dxArg_dPsi0 = mLayerdVolFracLiq_dPsi0 dxArg_dTk = 0._rkind endif ! ...and, compute the thermal conductivity mLayerThermalC(iLayer) = c1 + c2*xArg + (c1 - c4)*exp(-(c3*xArg)**c5) ! compute derivatives - mLayerdThermalC_dWat(iLayer) = ( c2 + -(c3*xArg)**(c5-1)*(c1 - c4)*exp(-(c3*xArg)**c5) ) * dxArg_dPsi0 - mLayerdThermalC_dNrg(iLayer) = ( c2 + -(c3*xArg)**(c5-1)*(c1 - c4)*exp(-(c3*xArg)**c5) ) * dxArg_dTk + mLayerdThermalC_dWat(iLayer) = ( c2 - c5*c3*(c3*xArg)**(c5-1)*(c1 - c4)*exp(-(c3*xArg)**c5) ) * dxArg_dPsi0 + mLayerdThermalC_dNrg(iLayer) = ( c2 - c5*c3*(c3*xArg)**(c5-1)*(c1 - c4)*exp(-(c3*xArg)**c5) ) * dxArg_dTk ! ** check case default; err=20; message=trim(message)//'unable to identify option for thermal conductivity of soil'; return @@ -1107,7 +1102,7 @@ subroutine iLayerThermalConduct(& ! ***** ! * compute the thermal conductivity of snow at the interface of each layer... ! **************************************************************************** - if (ixLayerDesired==0 ) then + if (ixLayerDesired==0) then ! special case of hansson if(ixThCondSoil==hanssonVZJ)then iLayerThermalC = 28._rkind*(0.5_rkind*(node_iHeight(ixLower) - node_iHeight(ixUpper))) ! these are indices 1,0 since was passed with 0:1 @@ -1137,10 +1132,10 @@ subroutine iLayerThermalConduct(& ! compute thermal conductivity if(TCn+TCp > epsilon(TCn))then iLayerThermalC = (TCn*TCp*(zdn + zdp)) / den - dThermalC_dHydStateBelow = ( TCn*(zdn + zdp) + iLayerThermalC*zdn ) / den * mLayerdThermalC_dWat(ixLower) - dThermalC_dHydStateAbove = ( TCp*(zdn + zdp) + iLayerThermalC*zdp ) / den * mLayerdThermalC_dWat(ixUpper) - dThermalC_dNrgStateBelow = ( TCn*(zdn + zdp) + iLayerThermalC*zdn ) / den * mLayerdThermalC_dNrg(ixLower) - dThermalC_dNrgStateAbove = ( TCp*(zdn + zdp) + iLayerThermalC*zdp ) / den * mLayerdThermalC_dNrg(ixUpper) + dThermalC_dHydStateBelow = ( TCn*(zdn + zdp) - iLayerThermalC*zdn ) / den * mLayerdThermalC_dWat(ixLower) + dThermalC_dHydStateAbove = ( TCp*(zdn + zdp) - iLayerThermalC*zdp ) / den * mLayerdThermalC_dWat(ixUpper) + dThermalC_dNrgStateBelow = ( TCn*(zdn + zdp) - iLayerThermalC*zdn ) / den * mLayerdThermalC_dNrg(ixLower) + dThermalC_dNrgStateAbove = ( TCp*(zdn + zdp) - iLayerThermalC*zdp ) / den * mLayerdThermalC_dNrg(ixUpper) else iLayerThermalC = (TCn*zdn + TCp*zdp) / (zdn + zdp) dThermalC_dHydStateBelow = zdp / (zdn + zdp) * mLayerdThermalC_dWat(ixLower) diff --git a/build/source/engine/updateVars4JacDAE.f90 b/build/source/engine/updateVars4JacDAE.f90 index d6c75815f..296442094 100644 --- a/build/source/engine/updateVars4JacDAE.f90 +++ b/build/source/engine/updateVars4JacDAE.f90 @@ -137,16 +137,6 @@ subroutine updateVars4JacDAE(& mLayerVolFracIcePrime, & ! reza mLayerMatricHeadPrime, & ! reza mLayerMatricHeadLiqPrime, & ! reza - mLayerd2Theta_dTk2, & ! reza - d2VolTot_d2Psi0, & - dFracLiqSnow_dTk, & - d2Theta_dTkCanopy2, & - dFracLiqVeg_dTkCanopy, & - dVolHtCapBulk_dPsi0, & ! derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dThetaCan, & ! derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! derivative in bulk heat capacity w.r.t. temperature ! output: error control err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- @@ -182,16 +172,6 @@ subroutine updateVars4JacDAE(& real(rkind),intent(inout) :: mLayerVolFracIcePrime(:) ! reza real(rkind),intent(inout) :: mLayerMatricHeadPrime(:) ! reza real(rkind),intent(inout) :: mLayerMatricHeadLiqPrime(:) ! reza - real(rkind),intent(out) :: mLayerd2Theta_dTk2(:) ! reza - real(rkind),intent(out) :: d2VolTot_d2Psi0(:) ! reza - real(rkind),intent(out) :: dFracLiqSnow_dTk(:) ! reza - real(rkind),intent(out) :: d2Theta_dTkCanopy2 - real(rkind),intent(out) :: dFracLiqVeg_dTkCanopy - real(rkind),intent(out) :: dVolHtCapBulk_dPsi0(:) ! derivative in bulk heat capacity w.r.t. matric potential - real(rkind),intent(out) :: dVolHtCapBulk_dTheta(:) ! derivative in bulk heat capacity w.r.t. volumetric water content - real(rkind),intent(out) :: dVolHtCapBulk_dThetaCan ! derivative in bulk heat capacity w.r.t. volumetric water content - real(rkind),intent(out) :: dVolHtCapBulk_dTk(:) ! derivative in bulk heat capacity w.r.t. temperature - real(rkind),intent(out) :: dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -290,8 +270,18 @@ subroutine updateVars4JacDAE(& dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(out): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) & ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature - ) ! association with variables in the data structures + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature + d2VolTot_d2Psi0 => deriv_data%var(iLookDERIV%d2VolTot_d2Psi0 )%dat ,& ! intent(out): [dp(:)] second derivative in total water content w.r.t. total water matric potential + mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2 )%dat ,& ! intent(out): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature + d2Theta_dTkCanopy2 => deriv_data%var(iLookDERIV%d2Theta_dTkCanopy2 )%dat(1) ,& ! intent(out): [dp ] second derivative of volumetric liquid water content w.r.t. temperature + dFracLiqSnow_dTk => deriv_data%var(iLookDERIV%dFracLiqSnow_dTk )%dat ,& ! intent(out): [dp(:)] derivative in fraction of liquid snow w.r.t. temperature + dFracLiqVeg_dTkCanopy => deriv_data%var(iLookDERIV%dFracLiqVeg_dTkCanopy )%dat(1) ,& ! intent(out): [dp ] derivative in fraction of (throughfall + drainage) w.r.t. temperature + dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dThetaCan => deriv_data%var(iLookDERIV%dVolHtCapBulk_dThetaCan)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1) & ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. temperature +) ! association with variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- @@ -508,8 +498,8 @@ subroutine updateVars4JacDAE(& ! --> unfrozen: no dependence of liquid water on temperature else select case(ixDomainType) - case(iname_veg); dTheta_dTkCanopy = 0._rkind; d2Theta_dTkCanopy2 = 0._rkind; dFracLiqVeg_dTkCanopy = 0._rkind; dVolHtCapBulk_dTkCanopy = 0._rkind - case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind; mLayerd2Theta_dTk2(iLayer) = 0._rkind; dFracLiqSnow_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind + case(iname_veg); dTheta_dTkCanopy = 0._rkind; d2Theta_dTkCanopy2 = 0._rkind; dFracLiqVeg_dTkCanopy = 0._rkind; dVolHtCapBulk_dTkCanopy = 0._rkind + case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind; mLayerd2Theta_dTk2(iLayer) = 0._rkind; dFracLiqSnow_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return end select ! domain type endif diff --git a/build/source/engine/updateVarsSundials.f90 b/build/source/engine/updateVarsSundials.f90 index a7d02710e..697a41eac 100644 --- a/build/source/engine/updateVarsSundials.f90 +++ b/build/source/engine/updateVarsSundials.f90 @@ -445,7 +445,7 @@ subroutine updateVarsSundials(& if(ixDomainType==iname_soil)then select case( ixStateType(ixFullVector) ) case(iname_lmpLayer); dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore - case default; dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + case default; dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) end select endif From 33ce69a138b8df96728fad5e0e81fa2a65f92cbc Mon Sep 17 00:00:00 2001 From: ashleymedin <34691332+ashleymedin@users.noreply.github.com> Date: Thu, 17 Feb 2022 21:30:27 +0900 Subject: [PATCH 0242/1472] Added last term in soil/snow heat capacity derivatives. All work now. --- build/source/engine/computJacDAE.f90 | 21 +++++++++++---------- build/source/engine/updateVars4JacDAE.f90 | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 8159802bd..4e50155d9 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -489,9 +489,10 @@ subroutine computJacDAE(& if(nrgstate/=integerMissing)then ! (energy state for the current layer is within the state subset) if (doprint==1) print*, nrgState,watState, "snowwatenergyindices" ! - include derivatives of energy fluxes w.r.t water fluxes for current layer - aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_ice * cj & - + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) & - + LH_fus*iden_ice * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) + aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_ice * cj & + + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) & + + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) & + + LH_fus*iden_ice * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) ! - include derivatives of water fluxes w.r.t energy fluxes for current layer aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) @@ -606,21 +607,21 @@ subroutine computJacDAE(& endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present - aJac(nrgState,watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water*cj + dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(iLayer) & - - LH_fus*iden_water * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content - else - aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(iLayer) + aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) & + + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + if(mLayerdTheta_dTk(jLayer) > verySmall) then ! ice is present + aJac(nrgState,watState) = aJac(nrgState,watState) - dVolTot_dPsi0(iLayer)*LH_fus*iden_water*cj & + - LH_fus*iden_water * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content endif ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above if(iLayer>1)then - if(ixSoilOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSoilOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) ) + if(ixSoilOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSoilOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(jLayer-1))*( dNrgFlux_dWatBelow(jLayer-1) ) endif ! (cross-derivative terms for the layer below) if(iLayer Date: Mon, 28 Feb 2022 18:57:12 +0900 Subject: [PATCH 0243/1472] Added throughfall derivatives (advective heat flux). Resistance derivatives are started but commented out under turbulent flux. --- build/my_makefile_cop | 2 +- build/my_makefile_mac | 2 +- build/source/dshare/get_ixname.f90 | 13 +- build/source/dshare/popMetadat.f90 | 13 +- build/source/dshare/var_lookup.f90 | 15 +- build/source/engine/canopySnow.f90 | 96 +- build/source/engine/check_icond.f90 | 1 - build/source/engine/computFlux.f90 | 23 +- build/source/engine/computJacDAE.f90 | 30 +- build/source/engine/computSnowDepth.f90 | 18 +- build/source/engine/coupled_em.f90 | 17 +- build/source/engine/diagn_evar.f90 | 2 +- build/source/engine/ssdNrgFlux.f90 | 103 +- build/source/engine/updateVars4JacDAE.f90 | 48 +- build/source/engine/vegLiqFlux.f90 | 2 +- build/source/engine/vegNrgFlux.f90 | 1738 ++++++++++++++++++++- 16 files changed, 1923 insertions(+), 200 deletions(-) diff --git a/build/my_makefile_cop b/build/my_makefile_cop index 27ae8119f..0fc373346 100644 --- a/build/my_makefile_cop +++ b/build/my_makefile_cop @@ -210,9 +210,9 @@ SUMMA_SOLVER= \ stomResist.f90 \ groundwatr.f90 \ vegSWavRad.f90 \ + vegLiqFlux.f90 \ vegNrgFlux.f90 \ ssdNrgFlux.f90 \ - vegLiqFlux.f90 \ snowLiqFlx.f90 \ soilLiqFlx.f90 \ bigAquifer.f90 \ diff --git a/build/my_makefile_mac b/build/my_makefile_mac index caaa10435..4e268b271 100644 --- a/build/my_makefile_mac +++ b/build/my_makefile_mac @@ -210,9 +210,9 @@ SUMMA_SOLVER= \ stomResist.f90 \ groundwatr.f90 \ vegSWavRad.f90 \ + vegLiqFlux.f90 \ vegNrgFlux.f90 \ ssdNrgFlux.f90 \ - vegLiqFlux.f90 \ snowLiqFlx.f90 \ soilLiqFlx.f90 \ bigAquifer.f90 \ diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 0a7535b48..c4d5f48d6 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -710,20 +710,20 @@ function get_ixderiv(varName) case('dCanopyNetFlux_dCanairTemp' ); get_ixderiv = iLookDERIV%dCanopyNetFlux_dCanairTemp ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) case('dCanopyNetFlux_dCanopyTemp' ); get_ixderiv = iLookDERIV%dCanopyNetFlux_dCanopyTemp ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) case('dCanopyNetFlux_dGroundTemp' ); get_ixderiv = iLookDERIV%dCanopyNetFlux_dGroundTemp ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - case('dCanopyNetFlux_dCanLiq' ); get_ixderiv = iLookDERIV%dCanopyNetFlux_dCanLiq ! derivative in net canopy fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + case('dCanopyNetFlux_dCanLiq' ); get_ixderiv = iLookDERIV%dCanopyNetFlux_dCanLiq ! derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) case('dGroundNetFlux_dCanairTemp' ); get_ixderiv = iLookDERIV%dGroundNetFlux_dCanairTemp ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) case('dGroundNetFlux_dCanopyTemp' ); get_ixderiv = iLookDERIV%dGroundNetFlux_dCanopyTemp ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) case('dGroundNetFlux_dGroundTemp' ); get_ixderiv = iLookDERIV%dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - case('dGroundNetFlux_dCanLiq' ); get_ixderiv = iLookDERIV%dGroundNetFlux_dCanLiq ! derivative in net ground fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + case('dGroundNetFlux_dCanLiq' ); get_ixderiv = iLookDERIV%dGroundNetFlux_dCanLiq ! derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) ! derivatives in evaporative fluxes w.r.t. relevant state variables case('dCanopyEvaporation_dTCanair' ); get_ixderiv = iLookDERIV%dCanopyEvaporation_dTCanair ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) case('dCanopyEvaporation_dTCanopy' ); get_ixderiv = iLookDERIV%dCanopyEvaporation_dTCanopy ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) case('dCanopyEvaporation_dTGround' ); get_ixderiv = iLookDERIV%dCanopyEvaporation_dTGround ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - case('dCanopyEvaporation_dCanLiq' ); get_ixderiv = iLookDERIV%dCanopyEvaporation_dCanLiq ! derivative in canopy evaporation w.r.t. canopy liquid water content (s-1) + case('dCanopyEvaporation_dCanLiq' ); get_ixderiv = iLookDERIV%dCanopyEvaporation_dCanLiq ! derivative in canopy evaporation w.r.t. canopy total water content (s-1) case('dGroundEvaporation_dTCanair' ); get_ixderiv = iLookDERIV%dGroundEvaporation_dTCanair ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) case('dGroundEvaporation_dTCanopy' ); get_ixderiv = iLookDERIV%dGroundEvaporation_dTCanopy ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) case('dGroundEvaporation_dTGround' ); get_ixderiv = iLookDERIV%dGroundEvaporation_dTGround ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - case('dGroundEvaporation_dCanLiq' ); get_ixderiv = iLookDERIV%dGroundEvaporation_dCanLiq ! derivative in ground evaporation w.r.t. canopy liquid water content (s-1) + case('dGroundEvaporation_dCanLiq' ); get_ixderiv = iLookDERIV%dGroundEvaporation_dCanLiq ! derivative in ground evaporation w.r.t. canopy total water content (s-1) ! derivatives in canopy water w.r.t canopy temperature case('dTheta_dTkCanopy' ); get_ixderiv = iLookDERIV%dTheta_dTkCanopy ! derivative of volumetric liquid water content w.r.t. temperature (K-1) case('d2Theta_dTkCanopy2' ); get_ixderiv = iLookDERIV%d2Theta_dTkCanopy2 ! second derivative of volumetric liquid water content w.r.t. temperature @@ -733,6 +733,9 @@ function get_ixderiv(varName) case('scalarCanopyLiqDeriv' ); get_ixderiv = iLookDERIV%scalarCanopyLiqDeriv ! derivative in (throughfall + canopy drainage) w.r.t. canopy liquid water (s-1) case('scalarThroughfallRainDeriv' ); get_ixderiv = iLookDERIV%scalarThroughfallRainDeriv ! derivative in throughfall w.r.t. canopy liquid water (s-1) case('scalarCanopyLiqDrainageDeriv' ); get_ixderiv = iLookDERIV%scalarCanopyLiqDrainageDeriv ! derivative in canopy drainage w.r.t. canopy liquid water (s-1) + case('scalarThroughfallSnowDeriv' ); get_ixderiv = iLookDERIV%scalarThroughfallSnowDeriv ! derivative in snow throughfall w.r.t. canopy ice (s-1) + case('scalarCanopySnowUnloadingDeriv' ); get_ixderiv = iLookDERIV%scalarCanopySnowUnloadingDeriv ! derivative in unloading of snow w.r.t. canopy ice (s-1) + case('scalarCanopySnowUnload_TkDeriv' ); get_ixderiv = iLookDERIV%scalarCanopySnowUnload_TkDeriv ! derivative in unloading of snow w.r.t. canopy air temperature ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below case('dNrgFlux_dTempAbove' ); get_ixderiv = iLookDERIV%dNrgFlux_dTempAbove ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) case('dNrgFlux_dTempBelow' ); get_ixderiv = iLookDERIV%dNrgFlux_dTempBelow ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) @@ -763,7 +766,7 @@ function get_ixderiv(varName) ! derivate in bulk heat capacity w.r.t. relevant state variables case('dVolHtCapBulk_dPsi0' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dPsi0 ! derivative in bulk heat capacity w.r.t. matric potential case('dVolHtCapBulk_dTheta' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTheta ! derivative in bulk heat capacity w.r.t. volumetric water content - case('dVolHtCapBulk_dThetaCan' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dThetaCan ! derivative in bulk heat capacity w.r.t. volumetric water content + case('dVolHtCapBulk_dCanWat' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. volumetric water content case('dVolHtCapBulk_dTk' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTk ! derivative in bulk heat capacity w.r.t. temperature case('dVolHtCapBulk_dTkCanopy' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 3035b2f5e..7cbfdfef4 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -544,20 +544,20 @@ subroutine popMetadat(err,message) deriv_meta(iLookDERIV%dCanopyNetFlux_dCanairTemp) = var_info('dCanopyNetFlux_dCanairTemp' , 'derivative in net canopy flux w.r.t. canopy air temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dCanopyNetFlux_dCanopyTemp) = var_info('dCanopyNetFlux_dCanopyTemp' , 'derivative in net canopy flux w.r.t. canopy temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dCanopyNetFlux_dGroundTemp) = var_info('dCanopyNetFlux_dGroundTemp' , 'derivative in net canopy flux w.r.t. ground temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dCanopyNetFlux_dCanLiq) = var_info('dCanopyNetFlux_dCanLiq' , 'derivative in net canopy fluxes w.r.t. canopy liquid water content' , 'J kg-1 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCanopyNetFlux_dCanLiq) = var_info('dCanopyNetFlux_dCanLiq' , 'derivative in net canopy fluxes w.r.t. canopy total water content' , 'J kg-1 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dGroundNetFlux_dCanairTemp) = var_info('dGroundNetFlux_dCanairTemp' , 'derivative in net ground flux w.r.t. canopy air temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dGroundNetFlux_dCanopyTemp) = var_info('dGroundNetFlux_dCanopyTemp' , 'derivative in net ground flux w.r.t. canopy temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dGroundNetFlux_dGroundTemp) = var_info('dGroundNetFlux_dGroundTemp' , 'derivative in net ground flux w.r.t. ground temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dGroundNetFlux_dCanLiq) = var_info('dGroundNetFlux_dCanLiq' , 'derivative in net ground fluxes w.r.t. canopy liquid water content' , 'J kg-1 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dGroundNetFlux_dCanLiq) = var_info('dGroundNetFlux_dCanLiq' , 'derivative in net ground fluxes w.r.t. canopy total water content' , 'J kg-1 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! derivatives in evaporative fluxes w.r.t. relevant state variables deriv_meta(iLookDERIV%dCanopyEvaporation_dTCanair) = var_info('dCanopyEvaporation_dTCanair' , 'derivative in canopy evaporation w.r.t. canopy air temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dCanopyEvaporation_dTCanopy) = var_info('dCanopyEvaporation_dTCanopy' , 'derivative in canopy evaporation w.r.t. canopy temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dCanopyEvaporation_dTGround) = var_info('dCanopyEvaporation_dTGround' , 'derivative in canopy evaporation w.r.t. ground temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dCanopyEvaporation_dCanLiq) = var_info('dCanopyEvaporation_dCanLiq' , 'derivative in canopy evaporation w.r.t. canopy liquid water content' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCanopyEvaporation_dCanLiq) = var_info('dCanopyEvaporation_dCanLiq' , 'derivative in canopy evaporation w.r.t. canopy total water content' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dGroundEvaporation_dTCanair) = var_info('dGroundEvaporation_dTCanair' , 'derivative in ground evaporation w.r.t. canopy air temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dGroundEvaporation_dTCanopy) = var_info('dGroundEvaporation_dTCanopy' , 'derivative in ground evaporation w.r.t. canopy temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dGroundEvaporation_dTGround) = var_info('dGroundEvaporation_dTGround' , 'derivative in ground evaporation w.r.t. ground temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dGroundEvaporation_dCanLiq) = var_info('dGroundEvaporation_dCanLiq' , 'derivative in ground evaporation w.r.t. canopy liquid water content' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dGroundEvaporation_dCanLiq) = var_info('dGroundEvaporation_dCanLiq' , 'derivative in ground evaporation w.r.t. canopy total water content' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! derivatives in canopy water w.r.t canopy temperature deriv_meta(iLookDERIV%dTheta_dTkCanopy) = var_info('dTheta_dTkCanopy' , 'derivative of volumetric liquid water content w.r.t. temperature' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%d2Theta_dTkCanopy2) = var_info('d2Theta_dTkCanopy2' , 'second derivative of volumetric liquid water content w.r.t. temperature', 'K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) @@ -567,6 +567,9 @@ subroutine popMetadat(err,message) deriv_meta(iLookDERIV%scalarCanopyLiqDeriv) = var_info('scalarCanopyLiqDeriv' , 'derivative in (throughfall + drainage) w.r.t. canopy liquid water' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%scalarThroughfallRainDeriv) = var_info('scalarThroughfallRainDeriv' , 'derivative in throughfall w.r.t. canopy liquid water' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%scalarCanopyLiqDrainageDeriv) = var_info('scalarCanopyLiqDrainageDeriv' , 'derivative in canopy drainage w.r.t. canopy liquid water' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%scalarThroughfallSnowDeriv) = var_info('scalarThroughfallSnowDeriv' , 'derivative in snow throughfall w.r.t. canopy ice' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%scalarCanopySnowUnloadingDeriv)= var_info('scalarCanopySnowUnloadingDeriv', 'derivative in unloading of snow w.r.t. canopy ice' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%scalarCanopySnowUnload_TkDeriv)= var_info('scalarCanopySnowUnload_TkDeriv', 'derivative in unloading of snow w.r.t. canopy air temperature' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below deriv_meta(iLookDERIV%dNrgFlux_dTempAbove) = var_info('dNrgFlux_dTempAbove' , 'derivatives in the flux w.r.t. temperature in the layer above' , 'J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dNrgFlux_dTempBelow) = var_info('dNrgFlux_dTempBelow' , 'derivatives in the flux w.r.t. temperature in the layer below' , 'J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) @@ -597,7 +600,7 @@ subroutine popMetadat(err,message) ! derivate in bulk heat capacity w.r.t. relevant state variables deriv_meta(iLookDERIV%dVolHtCapBulk_dPsi0) = var_info('dVolHtCapBulk_dPsi0' , 'derivative in bulk heat capacity w.r.t. matric potential' , 'J m-4 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dVolHtCapBulk_dTheta) = var_info('dVolHtCapBulk_dTheta' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dThetaCan) = var_info('dVolHtCapBulk_dThetaCan' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dCanWat) = var_info('dVolHtCapBulk_dCanWat' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dVolHtCapBulk_dTk) = var_info('dVolHtCapBulk_dTk' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dVolHtCapBulk_dTkCanopy) = var_info('dVolHtCapBulk_dTkCanopy' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 6adcdd687..d102edf55 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -563,20 +563,20 @@ MODULE var_lookup integer(i4b) :: dCanopyNetFlux_dCanairTemp = integerMissing ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) integer(i4b) :: dCanopyNetFlux_dCanopyTemp = integerMissing ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) integer(i4b) :: dCanopyNetFlux_dGroundTemp = integerMissing ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - integer(i4b) :: dCanopyNetFlux_dCanLiq = integerMissing ! derivative in net canopy fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + integer(i4b) :: dCanopyNetFlux_dCanLiq = integerMissing ! derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) integer(i4b) :: dGroundNetFlux_dCanairTemp = integerMissing ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) integer(i4b) :: dGroundNetFlux_dCanopyTemp = integerMissing ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) integer(i4b) :: dGroundNetFlux_dGroundTemp = integerMissing ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - integer(i4b) :: dGroundNetFlux_dCanLiq = integerMissing ! derivative in net ground fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + integer(i4b) :: dGroundNetFlux_dCanLiq = integerMissing ! derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) ! derivatives in evaporative fluxes w.r.t. relevant state variables integer(i4b) :: dCanopyEvaporation_dTCanair = integerMissing ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) integer(i4b) :: dCanopyEvaporation_dTCanopy = integerMissing ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) integer(i4b) :: dCanopyEvaporation_dTGround = integerMissing ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - integer(i4b) :: dCanopyEvaporation_dCanLiq = integerMissing ! derivative in canopy evaporation w.r.t. canopy liquid water content (s-1) + integer(i4b) :: dCanopyEvaporation_dCanLiq = integerMissing ! derivative in canopy evaporation w.r.t. canopy total water content (s-1) integer(i4b) :: dGroundEvaporation_dTCanair = integerMissing ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) integer(i4b) :: dGroundEvaporation_dTCanopy = integerMissing ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) integer(i4b) :: dGroundEvaporation_dTGround = integerMissing ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - integer(i4b) :: dGroundEvaporation_dCanLiq = integerMissing ! derivative in ground evaporation w.r.t. canopy liquid water content (s-1) + integer(i4b) :: dGroundEvaporation_dCanLiq = integerMissing ! derivative in ground evaporation w.r.t. canopy total water content (s-1) ! derivatives in canopy water w.r.t canopy temperature integer(i4b) :: dTheta_dTkCanopy = integerMissing ! derivative of volumetric liquid water content w.r.t. temperature (K-1) integer(i4b) :: d2Theta_dTkCanopy2 = integerMissing ! second derivative of volumetric liquid water content w.r.t. temperature @@ -586,6 +586,9 @@ MODULE var_lookup integer(i4b) :: scalarCanopyLiqDeriv = integerMissing ! derivative in (throughfall + canopy drainage) w.r.t. canopy liquid water (s-1) integer(i4b) :: scalarThroughfallRainDeriv = integerMissing ! derivative in throughfall w.r.t. canopy liquid water (s-1) integer(i4b) :: scalarCanopyLiqDrainageDeriv = integerMissing ! derivative in canopy drainage w.r.t. canopy liquid water (s-1) + integer(i4b) :: scalarThroughfallSnowDeriv = integerMissing ! derivative in snow throughfall w.r.t. canopy ice (s-1) + integer(i4b) :: scalarCanopySnowUnloadingDeriv = integerMissing ! derivative in unloading of snow w.r.t. canopy ice (s-1) + integer(i4b) :: scalarCanopySnowUnload_TkDeriv = integerMissing ! derivative in unloading of snow w.r.t. canopy air temperature (K-1) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below integer(i4b) :: dNrgFlux_dTempAbove = integerMissing ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) integer(i4b) :: dNrgFlux_dTempBelow = integerMissing ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) @@ -616,7 +619,7 @@ MODULE var_lookup ! derivate in bulk heat capacity w.r.t. relevant state variables integer(i4b) :: dVolHtCapBulk_dPsi0 = integerMissing ! derivative in bulk heat capacity w.r.t. matric potential integer(i4b) :: dVolHtCapBulk_dTheta = integerMissing ! derivative in bulk heat capacity w.r.t. volumetric water content - integer(i4b) :: dVolHtCapBulk_dThetaCan = integerMissing ! derivative in bulk heat capacity w.r.t. volumetric water content + integer(i4b) :: dVolHtCapBulk_dCanWat = integerMissing ! derivative in bulk heat capacity w.r.t. volumetric water content integer(i4b) :: dVolHtCapBulk_dTk = integerMissing ! derivative in bulk heat capacity w.r.t. temperature integer(i4b) :: dVolHtCapBulk_dTkCanopy = integerMissing ! derivative in bulk heat capacity w.r.t. temperature @@ -861,7 +864,7 @@ MODULE var_lookup 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,& 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,& 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,& - 51) + 51, 52, 53, 54) ! named variables: model indices type(iLook_index), public,parameter :: iLookINDEX =ilook_index ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& diff --git a/build/source/engine/canopySnow.f90 b/build/source/engine/canopySnow.f90 index d3ea510fc..ba2b6ffdc 100644 --- a/build/source/engine/canopySnow.f90 +++ b/build/source/engine/canopySnow.f90 @@ -34,8 +34,8 @@ module canopySnow_module USE multiconst,only:Tfreeze ! freezing point of pure water (K) ! named variables defining elements in the data structures -USE var_lookup,only:iLookFORCE,iLookPARAM,iLookDIAG,iLookPROG,iLookFLUX ! named variables for structure elements -USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure +USE var_lookup,only:iLookFORCE,iLookPARAM,iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure ! model decisions USE mDecisions_module,only: & @@ -57,33 +57,49 @@ module canopySnow_module ! ************************************************************************************************ subroutine canopySnow(& ! input: model control - dt, & ! intent(in): time step (seconds) - exposedVAI, & ! intent(in): exposed vegetation area index (m2 m-2) - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + dt, & ! intent(in): time step (seconds) + exposedVAI, & ! intent(in): exposed vegetation area index (m2 m-2) + computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + scalarCanopyIceTrial, & ! intent(inout): trial mass of ice on the vegetation canopy at the current iteration (kg m-2) + scalarCanairTempTrial, & ! intent(in): temperature of the canopy air space (k) + scalarSnowfall, & ! intent(in): computed snowfall rate (kg m-2 s-1) + scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) + scalarWindspdCanopyTop, & ! intent(in): windspeed at the top of the canopy (m s-1) ! input/output: data structures - model_decisions, & ! intent(in): model decisions - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(inout): model prognostic variables for a local HRU - flux_data, & ! intent(inout): model flux variables - ! output: error control - err,message) ! intent(out): error control + model_decisions, & ! intent(in): model decisions + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + diag_data, & ! intent(in): model diagnostic variables for a local HRU + ! output + scalarThroughfallSnow, & ! intent(out): snow that reaches the ground without ever touching the canopy (kg m-2 s-1) + scalarCanopySnowUnloading, & ! intent(out): unloading of snow from the vegetion canopy (kg m-2 s-1) + scalarThroughfallSnowDeriv, & ! intent(out): derivative in throughfall w.r.t. canopy ice (s-1) + scalarCanopySnowUnloadingDeriv, & ! intent(out): derivative in unloading of snow w.r.t. canopy ice (s-1) + scalarCanopySnowUnload_TkDeriv, & ! intent(out): derivative in unloading of snow w.r.t. canopy air temperature (K-1) + err,message) ! intent(out): error control ! ------------------------------------------------------------------------------------------------ implicit none ! ------------------------------------------------------------------------------------------------ ! input: model control real(rkind),intent(in) :: dt ! time step (seconds) real(rkind),intent(in) :: exposedVAI ! exposed vegetation area index -- leaf + stem -- after burial by snow (m2 m-2) - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) + real(rkind),intent(inout) :: scalarCanopyIceTrial ! trial mass of ice on the vegetation canopy at the current iteration (kg m-2) + real(rkind),intent(in) :: scalarCanairTempTrial ! trial temperature of the canopy air space (k) + real(rkind),intent(in) :: scalarSnowfall ! computed snowfall rate (kg m-2 s-1) + real(rkind),intent(in) :: scalarCanopyLiqDrainage ! liquid drainage from the vegetation canopy (kg m-2 s-1) + real(rkind),intent(in) :: scalarWindspdCanopyTop ! windspeed at the top of the canopy (m s-1) ! input/output: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions type(var_d),intent(in) :: forc_data ! model forcing data type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_dlength),intent(in) :: diag_data ! model diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: prog_data ! model prognostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model flux variables - ! output: error control + ! output + real(rkind),intent(out) :: scalarThroughfallSnow ! snow that reaches the ground without ever touching the canopy (kg m-2 s-1) + real(rkind),intent(out) :: scalarCanopySnowUnloading ! unloading of snow from the vegetion canopy (kg m-2 s-1) + real(rkind),intent(out) :: scalarThroughfallSnowDeriv ! derivative in throughfall flux w.r.t. canopy storage (s-1) + real(rkind),intent(out) :: scalarCanopySnowUnloadingDeriv ! derivative in unloading flux w.r.t. canopy storage (s-1) + real(rkind),intent(out) :: scalarCanopySnowUnload_TkDeriv ! derivative in unloading flux w.r.t. canopy air temperature (K-1) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local variables @@ -95,8 +111,6 @@ subroutine canopySnow(& real(rkind) :: leafScaleFactor ! scaling factor for interception based on temperature (-) real(rkind) :: leafInterceptCapSnow ! storage capacity for snow per unit leaf area (kg m-2) real(rkind) :: canopyIceScaleFactor ! capacity scaling factor for throughfall (kg m-2) - real(rkind) :: throughfallDeriv ! derivative in throughfall flux w.r.t. canopy storage (s-1) - real(rkind) :: unloadingDeriv ! derivative in unloading flux w.r.t. canopy storage (s-1) real(rkind) :: scalarCanopyIceIter ! trial value for mass of ice on the vegetation canopy (kg m-2) (kg m-2) real(rkind) :: flux ! net flux (kg m-2 s-1) real(rkind) :: delS ! change in storage (kg m-2) @@ -128,19 +142,7 @@ subroutine canopySnow(& rateWindUnloading => mpar_data%var(iLookPARAM%rateWindUnloading)%dat(1), & ! constant describing how quickly snow will unload due to wind in windySnow parameterization (K s) ! model diagnostic variables - scalarNewSnowDensity => diag_data%var(iLookDIAG%scalarNewSnowDensity)%dat(1), & ! intent(in): [dp] density of new snow (kg m-3) - - ! model prognostic variables (input/output) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! intent(inout): [dp] mass of ice on the vegetation canopy (kg m-2) - - ! model fluxes (input) - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1), & ! intent(in): [dp] temperature of the canopy air space (k) - scalarSnowfall => flux_data%var(iLookFLUX%scalarSnowfall)%dat(1), & ! intent(in): [dp] computed snowfall rate (kg m-2 s-1) - scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1), & ! intent(in): [dp] liquid drainage from the vegetation canopy (kg m-2 s-1) - scalarWindspdCanopyTop => flux_data%var(iLookFLUX%scalarWindspdCanopyTop)%dat(1), & ! intent(in): [dp] windspeed at the top of the canopy (m s-1) - ! model variables (output) - scalarThroughfallSnow => flux_data%var(iLookFLUX%scalarThroughfallSnow)%dat(1), & ! intent(out): [dp] snow that reaches the ground without ever touching the canopy (kg m-2 s-1) - scalarCanopySnowUnloading => flux_data%var(iLookFLUX%scalarCanopySnowUnloading)%dat(1) & ! intent(out): [dp] unloading of snow from the vegetion canopy (kg m-2 s-1) + scalarNewSnowDensity => diag_data%var(iLookDIAG%scalarNewSnowDensity)%dat(1) & ! intent(in): [dp] density of new snow (kg m-3) ) ! associate variables in the data structures ! ----------------------------------------------------------------------------------------------------------------------------------------------------- @@ -149,31 +151,32 @@ subroutine canopySnow(& ! ************************************* if(computeVegFlux)then - unloading_melt = min(ratioDrip2Unloading*scalarCanopyLiqDrainage, scalarCanopyIce/dt) ! kg m-2 s-1 + unloading_melt = min(ratioDrip2Unloading*scalarCanopyLiqDrainage, scalarCanopyIceTrial/dt) ! kg m-2 s-1 else unloading_melt = 0._rkind end if - scalarCanopyIce = scalarCanopyIce - unloading_melt*dt + scalarCanopyIceTrial = scalarCanopyIceTrial - unloading_melt*dt ! ***** ! compute the ice balance due to snowfall and unloading... ! ******************************************************** ! check for early returns - if(.not.computeVegFlux .or. (scalarSnowfall= minWindUnloading) then windUnloadingFun = abs(scalarWindspdCanopyTop) / rateWindUnloading ! (s-1) else @@ -181,13 +184,18 @@ subroutine canopySnow(& end if ! implement the "windySnow" Roesch et al. 2001 parameterization, Eq. 13 in Roesch et al. 2001 scalarCanopySnowUnloading = scalarCanopyIceIter * (tempUnloadingFun + windUnloadingFun) - unloadingDeriv = tempUnloadingFun + windUnloadingFun + scalarCanopySnowUnloadingDeriv = tempUnloadingFun + windUnloadingFun + if (tempUnloadingFun > 0._rkind)then + scalarCanopySnowUnload_TkDeriv = scalarCanopyIceIter/ rateTempUnloading + else + scalarCanopySnowUnload_TkDeriv = 0._rkind + end if end if ! no snowfall if(scalarSnowfall deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy air temperature dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy temperature dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. ground temperature - dCanopyNetFlux_dCanLiq => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in net canopy fluxes w.r.t. canopy liquid water content + dCanopyNetFlux_dCanWat => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in net canopy fluxes w.r.t. canopy total water content dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy air temperature dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy temperature dGroundNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. ground temperature - dGroundNetFlux_dCanLiq => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in net ground fluxes w.r.t. canopy liquid water content + dGroundNetFlux_dCanWat => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in net ground fluxes w.r.t. canopy total water content ! derivatives in evaporative fluxes w.r.t. relevant state variables dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy air temperature dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy temperature dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. ground temperature - dCanopyEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy liquid water content + dCanopyEvaporation_dCanWat => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy total water content dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy air temperature dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy temperature dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. ground temperature - dGroundEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy liquid water content + dGroundEvaporation_dCanWat => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy total water content ! derivatives in canopy water w.r.t canopy temperature dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(out): [dp] derivative of canopy liquid storage w.r.t. temperature @@ -1161,7 +1161,7 @@ subroutine computFluxSundials(& dCanLiq_dTcanopy = dTheta_dTkCanopy*iden_water*canopyDepth ! kg m-2 K-1 ! calculate the energy fluxes over vegetation - call vegNrgFlux(& + call vegNrgFluxSundials(& ! input: model control firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call @@ -1183,6 +1183,7 @@ subroutine computFluxSundials(& prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables bvar_data, & ! intent(in): model variables for the local basin model_decisions, & ! intent(in): model decisions ! output: liquid water fluxes associated with evaporation/transpiration @@ -1204,18 +1205,18 @@ subroutine computFluxSundials(& dGroundNetFlux_dCanopyTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) dGroundNetFlux_dGroundTemp, & ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) ! output: liquid water flux derivatives (canopy evap) - dCanopyEvaporation_dCanLiq, & ! intent(out): derivative in canopy evaporation w.r.t. canopy liquid water content (s-1) + dCanopyEvaporation_dCanWat, & ! intent(out): derivative in canopy evaporation w.r.t. canopy total water content (s-1) dCanopyEvaporation_dTCanair, & ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) dCanopyEvaporation_dTCanopy, & ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) dCanopyEvaporation_dTGround, & ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) ! output: liquid water flux derivatives (ground evap) - dGroundEvaporation_dCanLiq, & ! intent(out): derivative in ground evaporation w.r.t. canopy liquid water content (s-1) + dGroundEvaporation_dCanWat, & ! intent(out): derivative in ground evaporation w.r.t. canopy total water content (s-1) dGroundEvaporation_dTCanair, & ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) dGroundEvaporation_dTCanopy, & ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) dGroundEvaporation_dTGround, & ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) ! output: cross derivative terms - dCanopyNetFlux_dCanLiq, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dGroundNetFlux_dCanLiq, & ! intent(out): derivative in net ground fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dCanopyNetFlux_dCanWat, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) + dGroundNetFlux_dCanWat, & ! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 4e50155d9..8315cb01a 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -224,19 +224,19 @@ subroutine computJacDAE(& dCanopyNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. canopy air temperature dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. canopy temperature dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. ground temperature - dCanopyNetFlux_dCanLiq => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in net canopy fluxes w.r.t. canopy liquid water content + dCanopyNetFlux_dCanWat => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in net canopy fluxes w.r.t. canopy total water content dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net ground flux w.r.t. canopy air temperature dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net ground flux w.r.t. canopy temperature - dGroundNetFlux_dCanLiq => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in net ground fluxes w.r.t. canopy liquid water content + dGroundNetFlux_dCanWat => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in net ground fluxes w.r.t. canopy total water content ! derivatives in evaporative fluxes w.r.t. relevant state variables dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy air temperature dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy temperature dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. ground temperature - dCanopyEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy liquid water content + dCanopyEvaporation_dCanWat => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy total water content dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy air temperature dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy temperature dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. ground temperature - dGroundEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy liquid water content + dGroundEvaporation_dCanWat => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy total water content ! derivatives in canopy water w.r.t canopy temperature dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(in): [dp] derivative in canopy liquid storage w.r.t. temperature dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative in volumetric liquid water content w.r.t. temperature @@ -270,7 +270,7 @@ subroutine computJacDAE(& ! derivate in bulk heat capacity w.r.t. relevant state variables dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dThetaCan => deriv_data%var(iLookDERIV%dVolHtCapBulk_dThetaCan )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. volumetric water content dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. temperature ! diagnostic variables @@ -304,8 +304,7 @@ subroutine computJacDAE(& ! compute terms in the Jacobian for vegetation (excluding fluxes) ! NOTE: energy for vegetation is computed *within* the iteration loop as it includes phase change if(ixVegNrg/=integerMissing)then - dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy ) * cj & - + dVolHtCapBulk_dTkCanopy & + dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy ) * cj + dVolHtCapBulk_dTkCanopy & + LH_fus*iden_water * scalarCanopyTempPrime * d2Theta_dTkCanopy2 & + LH_fus * dFracLiqVeg_dTkCanopy * scalarCanopyWatPrime / canopyDepth ! volumetric heat capacity of the vegetation (J m-3 K-1) end if @@ -314,11 +313,9 @@ subroutine computJacDAE(& ! NOTE: energy for snow+soil is computed *within* the iteration loop as it includes phase change do iLayer=1,nLayers if(ixSnowSoilNrg(iLayer)/=integerMissing) then - dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj & - + dVolHtCapBulk_dTk(iLayer) & + dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj + dVolHtCapBulk_dTk(iLayer) & + LH_fus*iden_water * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & + LH_fus*iden_water * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) - end if end do @@ -363,9 +360,10 @@ subroutine computJacDAE(& ! * energy and liquid fluxes over vegetation... ! --------------------------------------------- if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) + if (doprint==1) print*, ixCasNrg,ixVegNrg,ixVegHyd, ixTopNrg,ixTopHyd, "watvegindices" ! * liquid water fluxes for vegetation canopy (-) - if(ixVegHyd/=integerMissing) aJac(ixVegHyd,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanLiq - scalarCanopyLiqDeriv)*dt + 1._rkind * cj + if(ixVegHyd/=integerMissing) aJac(ixVegHyd,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanWat - scalarCanopyLiqDeriv)*dt + 1._rkind * cj ! * cross-derivative terms for canopy water if(ixVegHyd/=integerMissing)then ! cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) @@ -376,11 +374,10 @@ subroutine computJacDAE(& if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanLiq) + cj * (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth & - + dVolHtCapBulk_dThetaCan * scalarCanopyTempPrime & + if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth * cj & + + dVolHtCapBulk_dCanWat * scalarCanopyTempPrime - (dt/canopyDepth) * dCanopyNetFlux_dCanWat & + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth ! dF/dLiq - !if(ixCasNrg/=integerMissing) aJac(ixCasNrg,ixVegHyd) = (dt/canopyDepth)*(-dCanAirNetFlux_dCanLiq) !doesn't exist - if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanLiq) + if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) endif ! cross-derivative terms w.r.t. canopy temperature (K-1) @@ -581,7 +578,6 @@ subroutine computJacDAE(& ! only compute derivatives if the energy state for the current layer is within the state subset if(nrgstate/=integerMissing)then - if (doprint==1) print*, ixVegHyd,ixCasNrg,ixVegNrg, ixTopNrg,watState,nrgState, "watvegindices" ! - include derivates in liquid water fluxes w.r.t. temperature for current layer aJac(watState,nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance @@ -599,7 +595,7 @@ subroutine computJacDAE(& ! - include derivatives of energy w.r.t. ground evaporation if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer if(computeVegFlux)then - aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanLiq/iden_water) ! dVol/dLiq (kg m-2)-1 + aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) endif diff --git a/build/source/engine/computSnowDepth.f90 b/build/source/engine/computSnowDepth.f90 index 32750a03a..faa39b06f 100644 --- a/build/source/engine/computSnowDepth.f90 +++ b/build/source/engine/computSnowDepth.f90 @@ -40,7 +40,7 @@ module computSnowDepth_module contains ! ************************************************************************************************ - ! public subroutine coupled_em: run the coupled energy-mass model for one timestep + ! public subroutine computSnowDepth: compute snow depth for one sub timestep ! ************************************************************************************************ subroutine computSnowDepth(& dt_sub, & @@ -55,7 +55,7 @@ subroutine computSnowDepth(& mLayerDepth, & ! intent(inout) ! error control err,message) ! intent(out): error control - + USE snwDensify_module,only:snwDensify ! snow densification (compaction and cavitation) implicit none @@ -70,7 +70,7 @@ subroutine computSnowDepth(& real(rkind),intent(inout) :: mLayerDepth(:) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message - + ! local variables character(len=256) :: cmessage ! error message integer(i4b) :: iSnow ! index of snow layers @@ -91,7 +91,7 @@ subroutine computSnowDepth(& ! NOTE: assume constant density mLayerDepth(iSnow) = mLayerDepth(iSnow) + dt_sub*scalarSnowSublimation/(mLayerVolFracIce(iSnow)*iden_ice) - ! update the volumetric fraction of liquid water + ! update the volumetric fraction of liquid water mLayerVolFracLiq(iSnow) = massLiquid / (mLayerDepth(iSnow)*iden_water) ! no snow @@ -104,7 +104,7 @@ subroutine computSnowDepth(& end if end if ! (if snow layers exist) - + ! *** account for compaction and cavitation in the snowpack... ! ------------------------------------------------------------ @@ -130,10 +130,10 @@ subroutine computSnowDepth(& err,cmessage) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if end if ! if snow layers exist - - + + end subroutine computSnowDepth - - + + end module computSnowDepth_module diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 9c3755620..8f4eb7e0a 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -193,6 +193,9 @@ subroutine coupled_em(& real(rkind) :: exposedVAI ! exposed vegetation area index real(rkind) :: dCanopyWetFraction_dWat ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) real(rkind) :: dCanopyWetFraction_dT ! derivative in wetted fraction w.r.t. canopy temperature (K-1) + real(rkind) :: throughfallDeriv ! derivative in snow throughfall flux w.r.t. canopy storage (s-1) + real(rkind) :: unloadingDeriv ! derivative in snow unloading flux w.r.t. canopy storage (s-1) + real(rkind) :: unload_TkDeriv ! derivative in unloading of snow w.r.t. canopy air temperature (K-1) real(rkind),parameter :: varNotUsed1=-9999._rkind ! variables used to calculate derivatives (not needed here) real(rkind),parameter :: varNotUsed2=-9999._rkind ! variables used to calculate derivatives (not needed here) integer(i4b) :: iSnow ! index of snow layers @@ -514,14 +517,22 @@ subroutine coupled_em(& data_step, & ! intent(in): time step (seconds) exposedVAI, & ! intent(in): exposed vegetation area index (m2 m-2) computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! intent(inout): trial mass of ice on the vegetation canopy at the current iteration (kg m-2) + prog_data%var(iLookPROG%scalarCanairTemp)%dat(1), & ! intent(in): temperature of the canopy air space (k) + flux_data%var(iLookFLUX%scalarSnowfall)%dat(1), & ! intent(in): [dp] computed snowfall rate (kg m-2 s-1) + flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1), & ! intent(in): [dp] liquid drainage from the vegetation canopy (kg m-2 s-1) + flux_data%var(iLookFLUX%scalarWindspdCanopyTop)%dat(1), & ! intent(in): [dp] windspeed at the top of the canopy (m s-1) ! input/output: data structures model_decisions, & ! intent(in): model decisions forc_data, & ! intent(in): model forcing data mpar_data, & ! intent(in): model parameters diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(inout): model prognostic variables for a local HRU - flux_data, & ! intent(inout): model flux variables - ! output: error control + ! output + flux_data%var(iLookFLUX%scalarThroughfallSnow)%dat(1), & ! intent(out): snow that reaches the ground without ever touching the canopy (kg m-2 s-1) + flux_data%var(iLookFLUX%scalarCanopySnowUnloading)%dat(1), & ! intent(out): unloading of snow from the vegetion canopy (kg m-2 s-1) + throughfallDeriv, & ! intent(out): derivative in throughfall w.r.t. canopy ice (s-1) + unloadingDeriv, & ! intent(out): derivative in unloading of snow w.r.t. canopy ice (s-1) + unload_TkDeriv, & ! intent(out): derivative in unloading of snow w.r.t. canopy air temperature (K-1) err,cmessage) ! intent(out): error control if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if diff --git a/build/source/engine/diagn_evar.f90 b/build/source/engine/diagn_evar.f90 index df0d06467..697f08e50 100644 --- a/build/source/engine/diagn_evar.f90 +++ b/build/source/engine/diagn_evar.f90 @@ -217,7 +217,7 @@ subroutine diagn_evar(& ! * soil case(iname_soil) mLayerVolHtCapBulk(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component - iden_ice * Cp_Ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component iden_air * Cp_air * mLayerVolFracAir(iLayer) ! air component ! * snow diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index 786bb5f1f..c12eae6a2 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -128,7 +128,7 @@ subroutine ssdNrgFlux(& dFlux_dTempAbove, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (W m-2 K-1) dFlux_dTempBelow, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (W m-2 K-1) ! output: error control - err,message) ! intent(out): error control + err,message) ! intent(out): error control ! ------------------------------------------------------------------------------------------------------------------------------------------------- implicit none ! input: model control @@ -363,7 +363,7 @@ subroutine ssdNrgFluxSundials(& logical(lgt),intent(in) :: deriv_desired ! flag indicating if derivatives are desired ! input: fluxes and derivatives at the upper boundary real(rkind),intent(in) :: groundNetFlux ! net energy flux for the ground surface (W m-2) - real(rkind),intent(in) :: dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + real(rkind),intent(inout) :: dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) ! input: liquid water fluxes real(rkind),intent(in) :: iLayerLiqFluxSnow(0:) ! intent(in): liquid flux at the interface of each snow layer (m s-1) real(rkind),intent(in) :: iLayerLiqFluxSoil(0:) ! intent(in): liquid flux at the interface of each soil layer (m s-1) @@ -443,6 +443,7 @@ subroutine ssdNrgFluxSundials(& ! make association of local variables with information in the data structures associate(& ixDerivMethod => model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): method used to calculate flux derivatives + ix_bcUpprTdyn => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision, & ! intent(in): method used to calculate the upper boundary condition for thermodynamics ix_bcLowrTdyn => model_decisions(iLookDECISIONS%bcLowrTdyn)%iDecision, & ! intent(in): method used to calculate the lower boundary condition for thermodynamics ixThCondSnow => model_decisions(iLookDECISIONS%thCondSnow)%iDecision, & ! intent(in): choice of method for thermal conductivity of snow ixThCondSoil => model_decisions(iLookDECISIONS%thCondSoil)%iDecision, & ! intent(in): choice of method for thermal conductivity of soil @@ -455,6 +456,7 @@ subroutine ssdNrgFluxSundials(& ! input: thermal properties mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): depth of each layer (m) mLayerHeight => prog_data%var(iLookPROG%mLayerHeight)%dat, & ! intent(in): height at the mid-point of each layer (m) + upperBoundTemp => mpar_data%var(iLookPARAM%upperBoundTemp)%dat(1), & ! intent(in): temperature of the upper boundary (K) lowerBoundTemp => mpar_data%var(iLookPARAM%lowerBoundTemp)%dat(1), & ! intent(in): temperature of the lower boundary (K) iLayerHeight => prog_data%var(iLookPROG%iLayerHeight)%dat, & ! intent(in): height at the interface of each layer (m) fixedThermalCond_snow => mpar_data%var(iLookPARAM%fixedThermalCond_snow)%dat(1), & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) @@ -561,7 +563,6 @@ subroutine ssdNrgFluxSundials(& ! ***** compute the derivative in fluxes at layer interfaces w.r.t temperature in the layer above and the layer below ***** ! ------------------------------------------------------------------------------------------------------------------------- - !FIX, MOVE LAYER 0 TO vegNrgFlux?? Orrr? Right now not computing correctly ! get the indices for the snow+soil layers if(.not.scalarSolution) ixTop = 0 @@ -629,16 +630,16 @@ subroutine ssdNrgFluxSundials(& vectorfrac_silt(i) = frac_silt(mLayer_ind(i)-nSnow) vectorfrac_clay(i) = frac_clay(mLayer_ind(i)-nSnow) else - vectorvGn_alpha(i) = realMissing - vectorvGn_n(i) = realMissing - vectorvGn_m(i) = realMissing - vectortheta_sat(i) = realMissing - vectortheta_res(i) = realMissing - vectoriden_soil(i) = realMissing - vectorthCond_soil(i) = realMissing - vectorfrac_sand(i) = realMissing - vectorfrac_silt(i) = realMissing - vectorfrac_clay(i) = realMissing + vectorvGn_alpha(i) = valueMissing + vectorvGn_n(i) = valueMissing + vectorvGn_m(i) = valueMissing + vectortheta_sat(i) = valueMissing + vectortheta_res(i) = valueMissing + vectoriden_soil(i) = valueMissing + vectorthCond_soil(i) = valueMissing + vectorfrac_sand(i) = valueMissing + vectorfrac_silt(i) = valueMissing + vectorfrac_clay(i) = valueMissing end if end do @@ -662,6 +663,7 @@ subroutine ssdNrgFluxSundials(& ! ============================================================================================================ call iLayerThermalConduct(& ! input: model control + valueMissing, & ! intent(in): missing value ixThCondSnow, & ! intent(in): choice of method for thermal conductivity of snow ixThCondSoil, & ! intent(in): choice of method for thermal conductivity of soil ! input: coordinate variables @@ -706,11 +708,11 @@ subroutine ssdNrgFluxSundials(& ! compute total vertical flux, to compute derivatives if(deriv_desired .and. ixDerivMethod==numerical)then select case(itry) - case(unperturbed); scalarThermCFlux = scalariLayerThermalC + case(unperturbed); scalarThermCFlux = scalariLayerThermalC case(perturbStateTempAbove); scalarThermCFlux_dTempAbove = scalariLayerThermalC case(perturbStateTempBelow); scalarThermCFlux_dTempBelow = scalariLayerThermalC - case(perturbStateWatAbove); scalarThermCFlux_dWatAbove = scalariLayerThermalC - case(perturbStateWatBelow); scalarThermCFlux_dWatBelow = scalariLayerThermalC + case(perturbStateWatAbove); scalarThermCFlux_dWatAbove = scalariLayerThermalC + case(perturbStateWatBelow); scalarThermCFlux_dWatBelow = scalariLayerThermalC case default; err=10; message=trim(message)//"unknown perturbation"; return end select end if @@ -719,20 +721,40 @@ subroutine ssdNrgFluxSundials(& ! ***** the upper boundary if(iLayer==0)then ! (upper boundary) - !dz = (mLayerHeight(1) - mLayerHeight(iLayer)) - !if(ixDerivMethod==analytical)then ! ** analytical derivatives - !dFlux_dWatBelow(iLayer) = -dThermalC_dHydStateBelow * ( mLayerTempTrial(1) - groundtemp )/dz - !dFlux_dTempBelow(iLayer) = -dThermalC_dNrgStateBelow * ( mLayerTempTrial(1) - groundtemp )/dz - scalariLayerThermalC/dz - !else ! ** numerical derivatives - !flux0 = -scalarThermCFlux *( mLayerTempTrial(1) - groundtemp ) / dz - !flux2 = -scalarThermCFlux_dWatBelow*( mLayerTempTrial(1) - groundtemp ) / dz - !dFlux_dWatBelow(iLayer) = (flux2 - flux0)/dx - !flux0 = -scalarThermCFlux *( mLayerTempTrial(1) - groundtemp ) / dz - !flux2 = -scalarThermCFlux_dTempBelow*((mLayerTempTrial(1)+dx) - groundtemp ) / dz - !dFlux_dTempBelow(iLayer) = (flux2 - flux0)/dx - !end if - dFlux_dWatBelow(iLayer) = 0._rkind - dFlux_dTempBelow(iLayer) = dGroundNetFlux_dGroundTemp + + select case(ix_bcUpprTdyn) + + ! * prescribed temperature at the upper boundary + case(prescribedTemp) + dz = (mLayerHeight(1) - mLayerHeight(iLayer)) + if(ixDerivMethod==analytical)then ! ** analytical derivatives + dFlux_dWatBelow(iLayer) = -dThermalC_dHydStateBelow * ( mLayerTempTrial(1) - upperBoundTemp )/dz + dFlux_dTempBelow(iLayer) = -dThermalC_dNrgStateBelow * ( mLayerTempTrial(1) - upperBoundTemp )/dz - scalariLayerThermalC/dz + else ! ** numerical derivatives + flux0 = -scalarThermCFlux *( mLayerTempTrial(1) - upperBoundTemp ) / dz + flux2 = -scalarThermCFlux_dWatBelow*( mLayerTempTrial(1) - upperBoundTemp ) / dz + dFlux_dWatBelow(iLayer) = (flux2 - flux0)/dx + flux0 = -scalarThermCFlux *( mLayerTempTrial(1) - upperBoundTemp ) / dz + flux2 = -scalarThermCFlux_dTempBelow*((mLayerTempTrial(1)+dx) - upperBoundTemp ) / dz + dFlux_dTempBelow(iLayer) = (flux2 - flux0)/dx + end if + dGroundNetFlux_dGroundTemp = dFlux_dTempBelow(iLayer) + + ! * zero flux at the upper boundary + case(zeroFlux) + dFlux_dWatAbove(iLayer) = 0._rkind + dFlux_dTempAbove(iLayer) = 0._rkind + dGroundNetFlux_dGroundTemp = dFlux_dTempBelow(iLayer) + + ! * compute flux inside vegetation energy flux routine, use here + case(energyFlux) + dFlux_dTempBelow(iLayer) = dGroundNetFlux_dGroundTemp !FIX might need to do the same for dGroundNetFlux_dGroundWat + + case default; err=20; message=trim(message)//'unable to identify upper boundary condition for thermodynamics'; return + + dGroundNetFlux_dGroundTemp = dFlux_dTempBelow(iLayer) + !dGroundNetFlux_dGroundWat = dFlux_dWatBelow(iLayer) + end select ! (identifying the upper boundary condition for thermodynamics) ! ***** the lower boundary else if(iLayer==nLayers)then ! (lower boundary) @@ -796,11 +818,12 @@ subroutine ssdNrgFluxSundials(& end subroutine ssdNrgFluxSundials - ! ********************************************************************************************************** + ! ************************************************************************************************************************************* ! private subroutine iLayerThermalConduct: compute diagnostic energy variables (thermal conductivity and heat capacity) and derivatives - ! ********************************************************************************************************** + ! ************************************************************************************************************************************* subroutine iLayerThermalConduct(& ! input: model control + valueMissing, & ! intent(in): missing value ixThCondSnow, & ! intent(in): choice of method for thermal conductivity of snow ixThCondSoil, & ! intent(in): choice of method for thermal conductivity of soil ! input: coordinate variables @@ -841,6 +864,7 @@ subroutine iLayerThermalConduct(& err,message) ! intent(out): error control USE snow_utils_module,only:tcond_snow ! compute thermal conductivity of snow USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) + USE snow_utils_module,only:fracliquid ! compute fraction of liquid water at a given temperature USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water USE soil_utils_module,only:dTheta_dPsi ! derivative in the soil water characteristic (soil) @@ -853,6 +877,7 @@ subroutine iLayerThermalConduct(& implicit none ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: model control + real(rkind),intent(in) :: valueMissing ! intent(in): missing value integer(i4b),intent(in) :: ixThCondSnow ! intent(in): choice of method for thermal conductivity of snow integer(i4b),intent(in) :: ixThCondSoil ! intent(in): choice of method for thermal conductivity of soil ! input: coordinate variables @@ -967,8 +992,8 @@ subroutine iLayerThermalConduct(& nodeVolFracAirTrial(iLayer) = theta_sat(iLayer) - volFracLiq(nodeMatricHeadTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) nodeVolFracIceTrial(iLayer) = theta_sat(iLayer) - (nodeVolFracAirTrial(iLayer) + nodeVolFracLiqTrial(iLayer)) case(iname_snow) - fliq = 1._rkind / ( 1._rkind + (snowfrz_scale*( Tfreeze - nodeTempTrial(iLayer) ))**2._rkind ) - nodeVolTotWatTrial(iLayer) = nodeVolFracLiqTrial0(iLayer) / fliq ! use potentially perturbed nodeVolTotWatTrial + fLiq = fracliquid(nodeTempTrial(iLayer),snowfrz_scale) ! fraction of liquid water + nodeVolTotWatTrial(iLayer) = nodeVolFracLiqTrial0(iLayer) / fLiq ! use potentially perturbed nodeVolTotWatTrial nodeVolFracLiqTrial(iLayer) = nodeVolFracLiqTrial0(iLayer) nodeVolFracIceTrial(iLayer) = (nodeVolTotWatTrial(iLayer) - nodeVolFracLiqTrial(iLayer))*(iden_water/iden_ice) nodeVolFracAirTrial(iLayer) = 1._rkind - (nodeVolFracIceTrial(iLayer) + nodeVolFracLiqTrial(iLayer)) @@ -1063,7 +1088,7 @@ subroutine iLayerThermalConduct(& ! ***** snow case(iname_snow) mLayerdVolFracIce_dTheta = ( 1._rkind - fLiq )*(iden_water/iden_ice) - mLayerdVolFracIce_dTk = -dFracLiq_dTk(nodeTempTrial(iLayer),snowfrz_scale) * nodeVolFracLiqTrial(iLayer)*(iden_water/iden_ice) + mLayerdVolFracIce_dTk = -dFracLiq_dTk(nodeTempTrial(iLayer),snowfrz_scale) * nodeVolTotWatTrial(iLayer)*(iden_water/iden_ice) ! temporally constant thermal conductivity if(ixThCondSnow==Smirnova2000)then @@ -1113,15 +1138,15 @@ subroutine iLayerThermalConduct(& dThermalC_dHydStateBelow = mLayerdThermalC_dWat(ixLower) !these are index 1 since was passed with 1:2 dThermalC_dNrgStateBelow = mLayerdThermalC_dNrg(ixLower) !these are index 1 since was passed with 1:2 end if - dThermalC_dHydStateAbove = 0._rkind !should be missing - dThermalC_dNrgStateAbove = 0._rkind !should be missing + dThermalC_dHydStateAbove = valueMissing + dThermalC_dNrgStateAbove = valueMissing else if (ixLayerDesired==nLayers ) then ! assume the thermal conductivity at the domain boundaries is equal to the thermal conductivity of the layer iLayerThermalC = mLayerThermalC(nLayers) dThermalC_dHydStateAbove = mLayerdThermalC_dWat(ixLower) !these are index 2 since was passed with iLayers-1:iLayers dThermalC_dNrgStateAbove = mLayerdThermalC_dNrg(ixLower) !these are index 2 since was passed with iLayers-1:iLayers - dThermalC_dHydStateBelow = 0._rkind !should be missing - dThermalC_dNrgStateBelow = 0._rkind !should be missing + dThermalC_dHydStateBelow = valueMissing + dThermalC_dNrgStateBelow = valueMissing else ! get temporary variables TCn = mLayerThermalC(ixUpper) ! thermal conductivity below the layer interface (W m-1 K-1) diff --git a/build/source/engine/updateVars4JacDAE.f90 b/build/source/engine/updateVars4JacDAE.f90 index 7d89afa78..516f95e1f 100644 --- a/build/source/engine/updateVars4JacDAE.f90 +++ b/build/source/engine/updateVars4JacDAE.f90 @@ -165,7 +165,7 @@ subroutine updateVars4JacDAE(& real(rkind),intent(inout) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric ice water content (-) real(rkind),intent(inout) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) real(rkind),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) - + real(rkind),intent(inout) :: mLayerTempPrime(:) real(rkind),intent(inout) :: mLayerVolFracWatPrime(:) ! reza real(rkind),intent(inout) :: mLayerVolFracLiqPrime(:) ! reza @@ -278,14 +278,14 @@ subroutine updateVars4JacDAE(& dFracLiqVeg_dTkCanopy => deriv_data%var(iLookDERIV%dFracLiqVeg_dTkCanopy )%dat(1) ,& ! intent(out): [dp ] derivative in fraction of (throughfall + drainage) w.r.t. temperature dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dThetaCan => deriv_data%var(iLookDERIV%dVolHtCapBulk_dThetaCan)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1) & ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. temperature ) ! association with variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- - + ! initialize error control err=0; message='updateVars4JacDAE/' @@ -347,7 +347,7 @@ subroutine updateVars4JacDAE(& print*, 'isCoupled = ', isCoupled print*, 'isNrgState = ', isNrgState endif - + ! ======================================================================================================================================= @@ -381,7 +381,7 @@ subroutine updateVars4JacDAE(& select case( ixStateType(ixFullVector) ) ! --> update the total water from the liquid water matric potential case(iname_lmpLayer) - + effSat = volFracLiq(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex)) ! effective saturation avPore = theta_sat(ixControlIndex) - mLayerVolFracIceTrial(iLayer) - theta_res(ixControlIndex) ! available pore space mLayerVolFracLiqTrial(iLayer) = effSat*avPore + theta_res(ixControlIndex) @@ -392,12 +392,12 @@ subroutine updateVars4JacDAE(& !write(*,'(a,1x,i4,1x,3(f20.10,1x))') 'mLayerVolFracLiqTrial(iLayer) 1 = ', iLayer, mLayerVolFracLiqTrial(iLayer), mLayerVolFracIceTrial(iLayer), mLayerVolFracWatTrial(iLayer) ! --> update the total water from the total water matric potential case(iname_matLayer) - + mLayerVolFracWatTrial(iLayer) = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) mLayerVolFracWatPrime(iLayer) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) *mLayerMatricHeadPrime(ixControlIndex) ! --> update the total water matric potential (assume already have mLayerVolFracWatTrial given block above) case(iname_liqLayer, iname_watLayer) - + mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) mLayerMatricHeadPrime(ixControlIndex) = dPsi_dTheta(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) * mLayerVolFracWatPrime(iLayer) case default; err=20; message=trim(message)//'expect iname_lmpLayer, iname_matLayer, iname_liqLayer, or iname_watLayer'; return @@ -410,7 +410,7 @@ subroutine updateVars4JacDAE(& ! compute the critical soil temperature below which ice exists select case(ixDomainType) case(iname_veg, iname_snow); Tcrit = Tfreeze - case(iname_soil); Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) + case(iname_soil); Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return end select @@ -428,7 +428,7 @@ subroutine updateVars4JacDAE(& ! get iterations (set to maximum iterations if adjusting the temperature) niter = merge(maxiter, 1, do_adjustTemp) - + ! iterate iterations: do iter=1,niter @@ -450,11 +450,11 @@ subroutine updateVars4JacDAE(& ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi select case(ixDomainType) case(iname_veg) - fliq = 1._rkind / ( 1._rkind + (snowfrz_scale*( Tfreeze - xTemp ))**2._rkind ) - dVolHtCapBulk_dThetaCan = scalarCanopyLiq/canopyDepth * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + fLiq = fracLiquid(xTemp,snowfrz_scale) + dVolHtCapBulk_dCanWat = scalarCanopyLiq/canopyDepth * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) case(iname_snow) - fliq = 1._rkind / ( 1._rkind + (snowfrz_scale*( Tfreeze - xTemp ))**2._rkind ) - dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fliq ) * Cp_air + fLiq = fracLiquid(xTemp,snowfrz_scale) + dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air case(iname_soil) select case( ixStateType(ixFullVector) ) case(iname_lmpLayer) @@ -478,14 +478,16 @@ subroutine updateVars4JacDAE(& if(xTemp NOTE: use air temperature + canairTempTrial, & ! intent(in): trial value of the canopy air space temperature (K) + canopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + groundTempTrial, & ! intent(in): trial value of ground temperature (K) + canopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) + canopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) + + ! input: model derivatives + dCanLiq_dTcanopy, & ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) + + ! input/output: data structures + type_data, & ! intent(in): type of vegetation and soil + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): state vector geometry + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + bvar_data, & ! intent(in): model variables for the local basin + model_decisions, & ! intent(in): model decisions + + ! output: liquid water fluxes associated with evaporation/transpiration (needed for coupling) + returnCanopyTranspiration, & ! intent(out): canopy transpiration (kg m-2 s-1) + returnCanopyEvaporation, & ! intent(out): canopy evaporation/condensation (kg m-2 s-1) + returnGroundEvaporation, & ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + + ! output: fluxes + canairNetFlux, & ! intent(out): net energy flux for the canopy air space (W m-2) + canopyNetFlux, & ! intent(out): net energy flux for the vegetation canopy (W m-2) + groundNetFlux, & ! intent(out): net energy flux for the ground surface (W m-2) + + ! output: energy flux derivatives + dCanairNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) + dCanairNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) + dCanairNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) + dCanopyNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) + dCanopyNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) + dCanopyNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) + dGroundNetFlux_dCanairTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) + dGroundNetFlux_dCanopyTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) + dGroundNetFlux_dGroundTemp, & ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + + ! output liquid water flux derivarives (canopy evap) + dCanopyEvaporation_dCanWat, & ! intent(out): derivative in canopy evaporation w.r.t. canopy total water content (s-1) + dCanopyEvaporation_dTCanair, & ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTCanopy, & ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTGround, & ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + + ! output: liquid water flux derivarives (ground evap) + dGroundEvaporation_dCanWat, & ! intent(out): derivative in ground evaporation w.r.t. canopy total water content (s-1) + dGroundEvaporation_dTCanair, & ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTCanopy, & ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTGround, & ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + + ! output: cross derivative terms + dCanopyNetFlux_dCanWat, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) + dGroundNetFlux_dCanWat, & ! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) + + ! output: error control + err,message) ! intent(out): error control + + ! utilities + USE expIntegral_module,only:expInt ! function to calculate the exponential integral + ! conversion functions + USE conv_funcs_module,only:satVapPress ! function to compute the saturated vapor pressure (Pa) + USE conv_funcs_module,only:getLatentHeatValue ! function to identify latent heat of vaporization/sublimation (J kg-1) + ! stomatal resistance + USE stomResist_module,only:stomResist ! subroutine to calculate stomatal resistance + ! throughfall of rain and snow + USE canopySnow_module,only:canopySnow ! compute interception and unloading of snow from the vegetation canopy + USE vegLiqFlux_module,only:vegLiqFlux ! compute liquid water fluxes through vegetation + USE snow_utils_module,only:fracliquid ! compute fraction of liquid water at a given temperature + + ! compute energy and mass fluxes for vegetation + implicit none + + ! --------------------------------------------------------------------------------------- + ! * dummy variables + ! --------------------------------------------------------------------------------------- + ! input: model control + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(in) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + + ! input: model state variables + real(rkind),intent(in) :: upperBoundTemp ! temperature of the upper boundary (K) --> NOTE: use air temperature + real(rkind),intent(in) :: canairTempTrial ! trial value of canopy air space temperature (K) + real(rkind),intent(in) :: canopyTempTrial ! trial value of canopy temperature (K) + real(rkind),intent(in) :: groundTempTrial ! trial value of ground temperature (K) + real(rkind),intent(in) :: canopyIceTrial ! trial value of mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: canopyLiqTrial ! trial value of mass of liquid water on the vegetation canopy (kg m-2) + + ! input: model derivatives + real(rkind),intent(in) :: dCanLiq_dTcanopy ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) + + ! input/output: data structures + type(var_i),intent(in) :: type_data ! type of vegetation and soil + type(var_d),intent(in) :: forc_data ! model forcing data + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! state vector geometry + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin + type(model_options),intent(in) :: model_decisions(:) ! model decisions + + ! output: liquid water fluxes associated with evaporation/transpiration (needed for coupling) + real(rkind),intent(out) :: returnCanopyTranspiration ! canopy transpiration (kg m-2 s-1) + real(rkind),intent(out) :: returnCanopyEvaporation ! canopy evaporation/condensation (kg m-2 s-1) + real(rkind),intent(out) :: returnGroundEvaporation ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + + ! output: fluxes + real(rkind),intent(out) :: canairNetFlux ! net energy flux for the canopy air space (W m-2) + real(rkind),intent(out) :: canopyNetFlux ! net energy flux for the vegetation canopy (W m-2) + real(rkind),intent(out) :: groundNetFlux ! net energy flux for the ground surface (W m-2) + + ! output: energy flux derivatives + real(rkind),intent(out) :: dCanairNetFlux_dCanairTemp ! derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) + real(rkind),intent(out) :: dCanairNetFlux_dCanopyTemp ! derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) + real(rkind),intent(out) :: dCanairNetFlux_dGroundTemp ! derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) + real(rkind),intent(out) :: dCanopyNetFlux_dCanairTemp ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) + real(rkind),intent(out) :: dCanopyNetFlux_dCanopyTemp ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) + real(rkind),intent(out) :: dCanopyNetFlux_dGroundTemp ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) + real(rkind),intent(out) :: dGroundNetFlux_dCanairTemp ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) + real(rkind),intent(out) :: dGroundNetFlux_dCanopyTemp ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) + real(rkind),intent(out) :: dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + + ! output: liquid flux derivatives (canopy evap) + real(rkind),intent(out) :: dCanopyEvaporation_dCanWat ! derivative in canopy evaporation w.r.t. canopy total water content (s-1) + real(rkind),intent(out) :: dCanopyEvaporation_dTCanair ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(rkind),intent(out) :: dCanopyEvaporation_dTCanopy ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(rkind),intent(out) :: dCanopyEvaporation_dTGround ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + + ! output: liquid flux derivatives (ground evap) + real(rkind),intent(out) :: dGroundEvaporation_dCanWat ! derivative in ground evaporation w.r.t. canopy total water content (s-1) + real(rkind),intent(out) :: dGroundEvaporation_dTCanair ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(rkind),intent(out) :: dGroundEvaporation_dTCanopy ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(rkind),intent(out) :: dGroundEvaporation_dTGround ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + + ! output: cross derivative terms + real(rkind),intent(out) :: dCanopyNetFlux_dCanWat ! derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) + real(rkind),intent(out) :: dGroundNetFlux_dCanWat ! derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) + + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + + ! --------------------------------------------------------------------------------------- + ! * local variables + ! --------------------------------------------------------------------------------------- + ! local (general) + character(LEN=256) :: cmessage ! error message of downwind routine + real(rkind) :: VAI ! vegetation area index (m2 m-2) + real(rkind) :: exposedVAI ! exposed vegetation area index (m2 m-2) + real(rkind) :: totalCanopyWater ! total water on the vegetation canopy (kg m-2) + real(rkind) :: scalarAquiferStorage ! aquifer storage (m) + + ! local (compute numerical derivatives) + integer(i4b),parameter :: unperturbed=1 ! named variable to identify the case of unperturbed state variables + integer(i4b),parameter :: perturbStateGround=2 ! named variable to identify the case where we perturb the ground temperature + integer(i4b),parameter :: perturbStateCanopy=3 ! named variable to identify the case where we perturb the canopy temperature + integer(i4b),parameter :: perturbStateCanair=4 ! named variable to identify the case where we perturb the canopy air temperature + integer(i4b),parameter :: perturbStateCanWat=5 ! named variable to identify the case where we perturb the canopy total water content + integer(i4b) :: itry ! index of flux evaluation + integer(i4b) :: nFlux ! number of flux evaluations + real(rkind) :: groundTemp ! value of ground temperature used in flux calculations (may be perturbed) + real(rkind) :: canopyTemp ! value of canopy temperature used in flux calculations (may be perturbed) + real(rkind) :: canairTemp ! value of canopy air temperature used in flux calculations (may be perturbed) + real(rkind) :: canopyWat ! value of canopy total water used in flux calculations (may be perturbed) + real(rkind) :: canopyLiq ! value of canopy liquid water used in flux calculations (may be perturbed) + real(rkind) :: canopyIce ! value of canopy ice used in flux calculations (may be perturbed) + real(rkind) :: try0,try1 ! trial values to evaluate specific derivatives (testing only) + + ! local (saturation vapor pressure of veg) + real(rkind) :: TV_celcius ! vegetaion temperature (C) + real(rkind) :: TG_celcius ! ground temperature (C) + real(rkind) :: dSVPCanopy_dCanopyTemp ! derivative in canopy saturated vapor pressure w.r.t. vegetation temperature (Pa/K) + real(rkind) :: dSVPGround_dGroundTemp ! derivative in ground saturated vapor pressure w.r.t. ground temperature (Pa/K) + + ! local (wetted canopy area) + real(rkind) :: fracLiquidCanopy ! fraction of liquid water in the canopy (-) + real(rkind) :: canopyWetFraction ! trial value of the canopy wetted fraction (-) + real(rkind) :: dCanopyWetFraction_dWat ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) + real(rkind) :: dCanopyWetFraction_dT ! derivative in wetted fraction w.r.t. canopy temperature (K-1) + + ! local (longwave radiation) + real(rkind) :: expi ! exponential integral + real(rkind) :: scaleLAI ! scaled LAI (computing diffuse transmissivity) + real(rkind) :: diffuseTrans ! diffuse transmissivity (-) + real(rkind) :: groundEmissivity ! emissivity of the ground surface (-) + real(rkind),parameter :: vegEmissivity=0.98_rkind ! emissivity of vegetation (0.9665 in JULES) (-) + real(rkind),parameter :: soilEmissivity=0.98_rkind ! emmisivity of the soil (0.9665 in JULES) (-) + real(rkind),parameter :: snowEmissivity=0.99_rkind ! emissivity of snow (-) + real(rkind) :: dLWNetCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dLWNetGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dLWNetCanopy_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dLWNetGround_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) + + ! local (aerodynamic resistance) + real(rkind) :: scalarCanopyStabilityCorrection_old ! stability correction for the canopy (-) + real(rkind) :: scalarGroundStabilityCorrection_old ! stability correction for the ground surface (-) + +! local (advective heat transfer) + real(rkind) :: throughfallRain ! rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + real(rkind) :: canopyLiqDrainage ! drainage of liquid water from the vegetation canopy (kg m-2 s-1) + real(rkind) :: throughfallRainDeriv ! derivative in throughfall w.r.t. canopy liquid water (s-1) + real(rkind) :: canopyLiqDrainageDeriv ! derivative in canopy drainage w.r.t. canopy liquid water (s-1) + real(rkind) :: throughfallSnow ! snow that reaches the ground without ever touching the canopy (kg m-2 s-1) + real(rkind) :: canopySnowUnloading ! unloading of snow from the vegetion canopy (kg m-2 s-1) + real(rkind) :: throughfallSnowDeriv ! derivative in snow throughfall w.r.t. canopy ice (s-1) + real(rkind) :: canopySnowUnloadingDeriv ! derivative in unloading of snow w.r.t. canopy ice (s-1) + real(rkind) :: canopySnowUnload_TkDeriv ! derivative in unloading of snow w.r.t. canopy air temperature + real(rkind) :: dThroughfallRain_dTCanopy ! derivative in rain throughfall w.r.t. canopy temperature + real(rkind) :: dCanopyLiqDrainage_dTCanopy ! derivative in canopy drainage w.r.t. canopy temperature + real(rkind) :: dThroughfallSnow_dTCanopy ! derivative in snow throughfall w.r.t. canopy temperature + real(rkind) :: dCanopySnowUnloading_dTCanopy ! derivative in unloading of snow w.r.t. canopy temperature + real(rkind) :: dThroughfallRain_dCanWat ! derivative in rain throughfall w.r.t. canopy total water + real(rkind) :: dCanopyLiqDrainage_dCanWat ! derivative in canopy drainage w.r.t. canopy total water + real(rkind) :: dThroughfallSnow_dCanWat ! derivative in snow throughfall w.r.t. canopy total water + real(rkind) :: dCanopySnowUnloading_dCanWat ! derivative in unloading of snow w.r.t. canopy total water + + ! local (turbulent heat transfer) + real(rkind) :: z0Ground ! roughness length of the ground (ground below the canopy or non-vegetated surface) (m) + real(rkind) :: soilEvapFactor ! soil water control on evaporation from non-vegetated surfaces + real(rkind) :: soilRelHumidity_noSnow ! relative humidity in the soil pores [0-1] + real(rkind) :: scalarLeafConductance ! leaf conductance (m s-1) + real(rkind) :: scalarCanopyConductance ! canopy conductance (m s-1) + real(rkind) :: scalarGroundConductanceSH ! ground conductance for sensible heat (m s-1) + real(rkind) :: scalarGroundConductanceLH ! ground conductance for latent heat -- includes soil resistance (m s-1) + real(rkind) :: scalarEvapConductance ! conductance for evaporation (m s-1) + real(rkind) :: scalarTransConductance ! conductance for transpiration (m s-1) + real(rkind) :: scalarTotalConductanceSH ! total conductance for sensible heat (m s-1) + real(rkind) :: scalarTotalConductanceLH ! total conductance for latent heat (m s-1) + real(rkind) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + real(rkind) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + real(rkind) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + real(rkind) :: turbFluxCanair ! total turbulent heat fluxes exchanged at the canopy air space (W m-2) + real(rkind) :: turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) + real(rkind) :: turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) + + ! local (turbulent heat transfer -- compute numerical derivatives) + ! (temporary scalar resistances when states are perturbed) + real(rkind) :: trialLeafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) + real(rkind) :: trialGroundResistance ! below canopy aerodynamic resistance (s m-1) + real(rkind) :: trialCanopyResistance ! above canopy aerodynamic resistance (s m-1) + real(rkind) :: notUsed_RiBulkCanopy ! bulk Richardson number for the canopy (-) + real(rkind) :: notUsed_RiBulkGround ! bulk Richardson number for the ground surface (-) + real(rkind) :: notUsed_z0Canopy ! roughness length of the vegetation canopy (m) + real(rkind) :: notUsed_WindReductionFactor ! canopy wind reduction factor (-) + real(rkind) :: notUsed_ZeroPlaneDisplacement ! zero plane displacement (m) + real(rkind) :: notUsed_scalarCanopyStabilityCorrection ! stability correction for the canopy (-) + real(rkind) :: notUsed_scalarGroundStabilityCorrection ! stability correction for the ground surface (-) + real(rkind) :: notUsed_EddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) + real(rkind) :: notUsed_FrictionVelocity ! friction velocity (m s-1) + real(rkind) :: notUsed_WindspdCanopyTop ! windspeed at the top of the canopy (m s-1) + real(rkind) :: notUsed_WindspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) + real(rkind) :: notUsed_dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + real(rkind) :: notUsed_dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind) :: notUsed_dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + real(rkind) :: notUsed_dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind) :: notUsed_dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + + ! (fluxes after perturbations in model states -- canopy air space) + real(rkind) :: turbFluxCanair_dStateCanair ! turbulent exchange from the canopy air space to the atmosphere, after canopy air temperature is perturbed (W m-2) + real(rkind) :: turbFluxCanair_dStateCanopy ! turbulent exchange from the canopy air space to the atmosphere, after canopy temperature is perturbed (W m-2) + real(rkind) :: turbFluxCanair_dStateGround ! turbulent exchange from the canopy air space to the atmosphere, after ground temperature is perturbed (W m-2) + real(rkind) :: turbFluxCanair_dStateCanWat ! turbulent exchange from the canopy air space to the atmosphere, after canopy total water content is perturbed (W m-2) + + ! (fluxes after perturbations in model states -- vegetation canopy) + real(rkind) :: turbFluxCanopy_dStateCanair ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy air temperature is perturbed (W m-2) + real(rkind) :: turbFluxCanopy_dStateCanopy ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy temperature is perturbed (W m-2) + real(rkind) :: turbFluxCanopy_dStateGround ! total turbulent heat fluxes from the canopy to the canopy air space, after ground temperature is perturbed (W m-2) + real(rkind) :: turbFluxCanopy_dStateCanWat ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy total water content is perturbed (W m-2) + real(rkind) :: advectFluxCanopy_dStateCanopy ! total advective heat fluxes from the canopy to the canopy air space, after canopy temperature is perturbed (W m-2) + real(rkind) :: advectFluxCanopy_dStateCanWat ! total advective heat fluxes from the canopy to the canopy air space, after canopy total water content is perturbed (W m-2) + + ! (fluxes after perturbations in model states -- ground surface) + real(rkind) :: turbFluxGround_dStateCanair ! total turbulent heat fluxes from the ground to the canopy air space, after canopy air temperature is perturbed (W m-2) + real(rkind) :: turbFluxGround_dStateCanopy ! total turbulent heat fluxes from the ground to the canopy air space, after canopy temperature is perturbed (W m-2) + real(rkind) :: turbFluxGround_dStateGround ! total turbulent heat fluxes from the ground to the canopy air space, after ground temperature is perturbed (W m-2) + real(rkind) :: turbFluxGround_dStateCanWat ! total turbulent heat fluxes from the ground to the canopy air space, after canopy total water content is perturbed (W m-2) + real(rkind) :: advectFluxGround_dStateCanair ! total advective heat fluxes from the ground to the canopy air space, after canopy air temperature is perturbed (W m-2) + real(rkind) :: advectFluxGround_dStateCanopy ! total advective heat fluxes from the ground to the canopy air space, after canopy temperature is perturbed (W m-2) + real(rkind) :: advectFluxGround_dStateGround ! total advective heat fluxes from the ground to the canopy air space, after ground temperature is perturbed (W m-2) + real(rkind) :: advectFluxGround_dStateCanWat ! total advective heat fluxes from the ground to the canopy air space, after canopy total water content is perturbed (W m-2) + + ! (fluxes after perturbations in model states -- canopy evaporation) + real(rkind) :: latHeatCanEvap_dStateCanair ! canopy evaporation after canopy air temperature is perturbed (W m-2) + real(rkind) :: latHeatCanEvap_dStateCanopy ! canopy evaporation after canopy temperature is perturbed (W m-2) + real(rkind) :: latHeatCanEvap_dStateGround ! canopy evaporation after ground temperature is perturbed (W m-2) + real(rkind) :: latHeatCanEvap_dStateCanWat ! canopy evaporation after canopy total water content is perturbed (W m-2) + + ! (flux derivatives -- canopy air space) + real(rkind) :: dTurbFluxCanair_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanair_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanair_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanair_dCanWat ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) + + ! (flux derivatives -- vegetation canopy) + real(rkind) :: dTurbFluxCanopy_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanopy_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanopy_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanopy_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + real(rkind) :: dAdvecFluxCanopy_dTGround ! derivative in net canopy advective fluxes w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dAdvecFluxCanopy_dTCanopy ! derivative in net canopy advective fluxes w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dAdvecFluxCanopy_dCanWat ! derivative in net canopy advective fluxes w.r.t. canopy total water content (J kg-1 s-1) + + ! (flux derivatives -- ground surface) + real(rkind) :: dTurbFluxGround_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dTurbFluxGround_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dTurbFluxGround_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dTurbFluxGround_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + real(rkind) :: dAdvecFluxGround_dTCanair ! derivative in net ground advective fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dAdvecFluxGround_dTCanopy ! derivative in net ground advective fluxes w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dAdvecFluxGround_dTGround ! derivative in net ground advective fluxes w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dAdvecFluxGround_dCanWat ! derivative in net ground advective fluxes w.r.t. canopy total water content (J kg-1 s-1) + + ! (liquid water flux derivatives -- canopy evap) + real(rkind) :: dLatHeatCanopyEvap_dCanWat ! derivative in latent heat of canopy evaporation w.r.t. canopy total water content (W kg-1) + real(rkind) :: dLatHeatCanopyEvap_dTCanair ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dLatHeatCanopyEvap_dTCanopy ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dLatHeatCanopyEvap_dTGround ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) + + ! (liquid water flux derivatives -- ground evap) + real(rkind) :: dLatHeatGroundEvap_dCanWat ! derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) + real(rkind) :: dLatHeatGroundEvap_dTCanair ! derivative in latent heat of ground evaporation w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) + + ! --------------------------------------------------------------------------------------- + ! point to variables in the data structure + ! --------------------------------------------------------------------------------------- + associate(& + + ! input: model decisions + ix_bcUpprTdyn => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision, & ! intent(in): [i4b] choice of upper boundary condition for thermodynamics + ixDerivMethod => model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): [i4b] choice of method to compute derivatives + ix_veg_traits => model_decisions(iLookDECISIONS%veg_traits)%iDecision, & ! intent(in): [i4b] choice of parameterization for vegetation roughness length and displacement height + ix_canopyEmis => model_decisions(iLookDECISIONS%canopyEmis)%iDecision, & ! intent(in): [i4b] choice of parameterization for canopy emissivity + ix_windPrfile => model_decisions(iLookDECISIONS%windPrfile)%iDecision, & ! intent(in): [i4b] choice of canopy wind profile + ix_astability => model_decisions(iLookDECISIONS%astability)%iDecision, & ! intent(in): [i4b] choice of stability function + ix_soilStress => model_decisions(iLookDECISIONS%soilStress)%iDecision, & ! intent(in): [i4b] choice of function for the soil moisture control on stomatal resistance + ix_groundwatr => model_decisions(iLookDECISIONS%groundwatr)%iDecision, & ! intent(in): [i4b] choice of groundwater parameterization + ix_stomResist => model_decisions(iLookDECISIONS%stomResist)%iDecision, & ! intent(in): [i4b] choice of function for stomatal resistance + ix_spatial_gw => model_decisions(iLookDECISIONS%spatial_gw)%iDecision, & ! intent(in): [i4b] choice of groundwater representation (local, basin) + + ! input: layer geometry + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1), & ! intent(in): [i4b] number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): [i4b] total number of layers + + ! input: physical attributes + vegTypeIndex => type_data%var(iLookTYPE%vegTypeIndex), & ! intent(in): [i4b] vegetation type index + soilTypeIndex => type_data%var(iLookTYPE%soilTypeIndex), & ! intent(in): [i4b] soil type index + + ! input: vegetation parameters + heightCanopyTop => mpar_data%var(iLookPARAM%heightCanopyTop)%dat(1), & ! intent(in): [dp] height at the top of the vegetation canopy (m) + heightCanopyBottom => mpar_data%var(iLookPARAM%heightCanopyBottom)%dat(1), & ! intent(in): [dp] height at the bottom of the vegetation canopy (m) + canopyWettingFactor => mpar_data%var(iLookPARAM%canopyWettingFactor)%dat(1), & ! intent(in): [dp] maximum wetted fraction of the canopy (-) + canopyWettingExp => mpar_data%var(iLookPARAM%canopyWettingExp)%dat(1), & ! intent(in): [dp] exponent in canopy wetting function (-) + scalarCanopyIceMax => diag_data%var(iLookDIAG%scalarCanopyIceMax)%dat(1), & ! intent(in): [dp] maximum interception storage capacity for ice (kg m-2) + scalarCanopyLiqMax => diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1), & ! intent(in): [dp] maximum interception storage capacity for liquid water (kg m-2) + + ! input: vegetation phenology + scalarLAI => diag_data%var(iLookDIAG%scalarLAI)%dat(1), & ! intent(in): [dp] one-sided leaf area index (m2 m-2) + scalarSAI => diag_data%var(iLookDIAG%scalarSAI)%dat(1), & ! intent(in): [dp] one-sided stem area index (m2 m-2) + scalarExposedLAI => diag_data%var(iLookDIAG%scalarExposedLAI)%dat(1), & ! intent(in): [dp] exposed leaf area index after burial by snow (m2 m-2) + scalarExposedSAI => diag_data%var(iLookDIAG%scalarExposedSAI)%dat(1), & ! intent(in): [dp] exposed stem area index after burial by snow (m2 m-2) + scalarGrowingSeasonIndex => diag_data%var(iLookDIAG%scalarGrowingSeasonIndex)%dat(1), & ! intent(in): [dp] growing season index (0=off, 1=on) + scalarFoliageNitrogenFactor => diag_data%var(iLookDIAG%scalarFoliageNitrogenFactor)%dat(1), & ! intent(in): [dp] foliage nitrogen concentration (1.0 = saturated) + + ! input: aerodynamic resistance parameters + z0Snow => mpar_data%var(iLookPARAM%z0Snow)%dat(1), & ! intent(in): [dp] roughness length of snow (m) + z0Soil => mpar_data%var(iLookPARAM%z0Soil)%dat(1), & ! intent(in): [dp] roughness length of soil (m) + z0CanopyParam => mpar_data%var(iLookPARAM%z0Canopy)%dat(1), & ! intent(in): [dp] roughness length of the canopy (m) + zpdFraction => mpar_data%var(iLookPARAM%zpdFraction)%dat(1), & ! intent(in): [dp] zero plane displacement / canopy height (-) + critRichNumber => mpar_data%var(iLookPARAM%critRichNumber)%dat(1), & ! intent(in): [dp] critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam => mpar_data%var(iLookPARAM%Louis79_bparam)%dat(1), & ! intent(in): [dp] parameter in Louis (1979) stability function + Louis79_cStar => mpar_data%var(iLookPARAM%Louis79_cStar)%dat(1), & ! intent(in): [dp] parameter in Louis (1979) stability function + Mahrt87_eScale => mpar_data%var(iLookPARAM%Mahrt87_eScale)%dat(1), & ! intent(in): [dp] exponential scaling factor in the Mahrt (1987) stability function + windReductionParam => mpar_data%var(iLookPARAM%windReductionParam)%dat(1), & ! intent(in): [dp] canopy wind reduction parameter (-) + leafExchangeCoeff => mpar_data%var(iLookPARAM%leafExchangeCoeff)%dat(1), & ! intent(in): [dp] turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) + leafDimension => mpar_data%var(iLookPARAM%leafDimension)%dat(1), & ! intent(in): [dp] characteristic leaf dimension (m) + + ! input: soil stress parameters + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(1), & ! intent(in): [dp] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(1), & ! intent(in): [dp] residual volumetric liquid water content (-) + plantWiltPsi => mpar_data%var(iLookPARAM%plantWiltPsi)%dat(1), & ! intent(in): [dp] matric head at wilting point (m) + soilStressParam => mpar_data%var(iLookPARAM%soilStressParam)%dat(1), & ! intent(in): [dp] parameter in the exponential soil stress function (-) + critSoilWilting => mpar_data%var(iLookPARAM%critSoilWilting)%dat(1), & ! intent(in): [dp] critical vol. liq. water content when plants are wilting (-) + critSoilTranspire => mpar_data%var(iLookPARAM%critSoilTranspire)%dat(1), & ! intent(in): [dp] critical vol. liq. water content when transpiration is limited (-) + critAquiferTranspire => mpar_data%var(iLookPARAM%critAquiferTranspire)%dat(1), & ! intent(in): [dp] critical aquifer storage value when transpiration is limited (m) + minStomatalResistance => mpar_data%var(iLookPARAM%minStomatalResistance)%dat(1), & ! intent(in): [dp] mimimum stomatal resistance (s m-1) + + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + + ! input: forcing at the upper boundary + mHeight => diag_data%var(iLookDIAG%scalarAdjMeasHeight)%dat(1), & ! intent(in): [dp] measurement height (m) + airtemp => forc_data%var(iLookFORCE%airtemp), & ! intent(in): [dp] air temperature at some height above the surface (K) + windspd => forc_data%var(iLookFORCE%windspd), & ! intent(in): [dp] wind speed at some height above the surface (m s-1) + airpres => forc_data%var(iLookFORCE%airpres), & ! intent(in): [dp] air pressure at some height above the surface (Pa) + LWRadAtm => forc_data%var(iLookFORCE%LWRadAtm), & ! intent(in): [dp] downwelling longwave radiation at the upper boundary (W m-2) + scalarVPair => diag_data%var(iLookDIAG%scalarVPair)%dat(1), & ! intent(in): [dp] vapor pressure at some height above the surface (Pa) + scalarO2air => diag_data%var(iLookDIAG%scalarO2air)%dat(1), & ! intent(in): [dp] atmospheric o2 concentration (Pa) + scalarCO2air => diag_data%var(iLookDIAG%scalarCO2air)%dat(1), & ! intent(in): [dp] atmospheric co2 concentration (Pa) + scalarTwetbulb => diag_data%var(iLookDIAG%scalarTwetbulb)%dat(1), & ! intent(in): [dp] wetbulb temperature (K) + scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1), & ! intent(in): [dp] computed rainfall rate (kg m-2 s-1) + scalarSnowfall => flux_data%var(iLookFLUX%scalarSnowfall)%dat(1), & ! intent(in): [dp] computed snowfall rate (kg m-2 s-1) + scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1), & ! intent(in): [dp] rainfall through the vegetation canopy (kg m-2 s-1) + scalarThroughfallSnow => flux_data%var(iLookFLUX%scalarThroughfallSnow)%dat(1), & ! intent(in): [dp] snowfall through the vegetation canopy (kg m-2 s-1) + scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1), & ! intent(in): [dp] liquid drainage from the vegetation canopy (kg m-2 s-1) + scalarCanopySnowUnloading => flux_data%var(iLookFLUX%scalarCanopySnowUnloading)%dat(1), & ! intent(in): [dp] unloading of snow from the vegetion canopy (kg m-2 s-1) + + ! input/output: derivatives in canopy liquid fluxes w.r.t. canopy water + scalarThroughfallRainDeriv => deriv_data%var(iLookDERIV%scalarThroughfallRainDeriv )%dat(1) ,& ! intent(in): [dp] derivative in throughfall w.r.t. canopy liquid water + scalarCanopyLiqDrainageDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDrainageDeriv)%dat(1) ,& ! intent(in): [dp] derivative in canopy drainage w.r.t. canopy liquid water + scalarThroughfallSnowDeriv => deriv_data%var(iLookDERIV%scalarThroughfallSnowDeriv )%dat(1) ,& ! intent(inout): [dp] derivative in snow throughfall w.r.t. canopy ice + scalarCanopySnowUnloadingDeriv => deriv_data%var(iLookDERIV%scalarCanopySnowUnloadingDeriv)%dat(1),& ! intent(inout): [dp] derivative in unloading of snow w.r.t. canopy ice + scalarCanopySnowUnload_TkDeriv => deriv_data%var(iLookDERIV%scalarCanopySnowUnload_TkDeriv)%dat(1),& ! intent(inout): [dp] derivative in unloading of snow w.r.t. canopy air temperature + ! input: water storage + ! NOTE: soil stress only computed at the start of the substep (firstFluxCall=.true.) + scalarSWE => prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(in): [dp] snow water equivalent on the ground (kg m-2) + scalarSnowDepth => prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(in): [dp] snow depth on the ground surface (m) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat, & ! intent(in): [dp(:)] volumetric fraction of liquid water in each layer (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat, & ! intent(in): [dp(:)] matric head in each soil layer (m) + localAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1), & ! intent(in): [dp] aquifer storage for the local column (m) + basinAquiferStorage => bvar_data%var(iLookBVAR%basin__AquiferStorage)%dat(1), & ! intent(in): [dp] aquifer storage for the single basin (m) + + ! input: shortwave radiation fluxes + scalarCanopySunlitLAI => diag_data%var(iLookDIAG%scalarCanopySunlitLAI)%dat(1), & ! intent(in): [dp] sunlit leaf area (-) + scalarCanopyShadedLAI => diag_data%var(iLookDIAG%scalarCanopyShadedLAI)%dat(1), & ! intent(in): [dp] shaded leaf area (-) + scalarCanopySunlitPAR => flux_data%var(iLookFLUX%scalarCanopySunlitPAR)%dat(1), & ! intent(in): [dp] average absorbed par for sunlit leaves (w m-2) + scalarCanopyShadedPAR => flux_data%var(iLookFLUX%scalarCanopyShadedPAR)%dat(1), & ! intent(in): [dp] average absorbed par for shaded leaves (w m-2) + scalarCanopyAbsorbedSolar => flux_data%var(iLookFLUX%scalarCanopyAbsorbedSolar)%dat(1), & ! intent(in): [dp] solar radiation absorbed by canopy (W m-2) + scalarGroundAbsorbedSolar => flux_data%var(iLookFLUX%scalarGroundAbsorbedSolar)%dat(1), & ! intent(in): [dp] solar radiation absorbed by ground (W m-2) + + ! output: fraction of wetted canopy area and fraction of snow on the ground + scalarCanopyWetFraction => diag_data%var(iLookDIAG%scalarCanopyWetFraction)%dat(1), & ! intent(out): [dp] fraction of canopy that is wet + scalarGroundSnowFraction => diag_data%var(iLookDIAG%scalarGroundSnowFraction)%dat(1), & ! intent(out): [dp] fraction of ground covered with snow (-) + + ! output: longwave radiation fluxes + scalarCanopyEmissivity => diag_data%var(iLookDIAG%scalarCanopyEmissivity)%dat(1), & ! intent(out): [dp] effective emissivity of the canopy (-) + scalarLWRadCanopy => flux_data%var(iLookFLUX%scalarLWRadCanopy)%dat(1), & ! intent(out): [dp] longwave radiation emitted from the canopy (W m-2) + scalarLWRadGround => flux_data%var(iLookFLUX%scalarLWRadGround)%dat(1), & ! intent(out): [dp] longwave radiation emitted at the ground surface (W m-2) + scalarLWRadUbound2Canopy => flux_data%var(iLookFLUX%scalarLWRadUbound2Canopy)%dat(1), & ! intent(out): [dp] downward atmospheric longwave radiation absorbed by the canopy (W m-2) + scalarLWRadUbound2Ground => flux_data%var(iLookFLUX%scalarLWRadUbound2Ground)%dat(1), & ! intent(out): [dp] downward atmospheric longwave radiation absorbed by the ground (W m-2) + scalarLWRadUbound2Ubound => flux_data%var(iLookFLUX%scalarLWRadUbound2Ubound)%dat(1), & ! intent(out): [dp] atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) + scalarLWRadCanopy2Ubound => flux_data%var(iLookFLUX%scalarLWRadCanopy2Ubound)%dat(1), & ! intent(out): [dp] longwave radiation emitted from canopy lost thru upper boundary (W m-2) + scalarLWRadCanopy2Ground => flux_data%var(iLookFLUX%scalarLWRadCanopy2Ground)%dat(1), & ! intent(out): [dp] longwave radiation emitted from canopy absorbed by the ground (W m-2) + scalarLWRadCanopy2Canopy => flux_data%var(iLookFLUX%scalarLWRadCanopy2Canopy)%dat(1), & ! intent(out): [dp] canopy longwave reflected from ground and absorbed by the canopy (W m-2) + scalarLWRadGround2Ubound => flux_data%var(iLookFLUX%scalarLWRadGround2Ubound)%dat(1), & ! intent(out): [dp] longwave radiation emitted from ground lost thru upper boundary (W m-2) + scalarLWRadGround2Canopy => flux_data%var(iLookFLUX%scalarLWRadGround2Canopy)%dat(1), & ! intent(out): [dp] longwave radiation emitted from ground and absorbed by the canopy (W m-2) + scalarLWNetCanopy => flux_data%var(iLookFLUX%scalarLWNetCanopy)%dat(1), & ! intent(out): [dp] net longwave radiation at the canopy (W m-2) + scalarLWNetGround => flux_data%var(iLookFLUX%scalarLWNetGround)%dat(1), & ! intent(out): [dp] net longwave radiation at the ground surface (W m-2) + scalarLWNetUbound => flux_data%var(iLookFLUX%scalarLWNetUbound)%dat(1), & ! intent(out): [dp] net longwave radiation at the upper boundary (W m-2) + + ! output: aerodynamic resistance + scalarZ0Canopy => diag_data%var(iLookDIAG%scalarZ0Canopy)%dat(1), & ! intent(out): [dp] roughness length of the canopy (m) + scalarWindReductionFactor => diag_data%var(iLookDIAG%scalarWindReductionFactor)%dat(1), & ! intent(out): [dp] canopy wind reduction factor (-) + scalarZeroPlaneDisplacement => diag_data%var(iLookDIAG%scalarZeroPlaneDisplacement)%dat(1), & ! intent(out): [dp] zero plane displacement (m) + scalarRiBulkCanopy => diag_data%var(iLookDIAG%scalarRiBulkCanopy)%dat(1), & ! intent(out): [dp] bulk Richardson number for the canopy (-) + scalarRiBulkGround => diag_data%var(iLookDIAG%scalarRiBulkGround)%dat(1), & ! intent(out): [dp] bulk Richardson number for the ground surface (-) + scalarEddyDiffusCanopyTop => flux_data%var(iLookFLUX%scalarEddyDiffusCanopyTop)%dat(1), & ! intent(out): [dp] eddy diffusivity for heat at the top of the canopy (m2 s-1) + scalarFrictionVelocity => flux_data%var(iLookFLUX%scalarFrictionVelocity)%dat(1), & ! intent(out): [dp] friction velocity (m s-1) + scalarWindspdCanopyTop => flux_data%var(iLookFLUX%scalarWindspdCanopyTop)%dat(1), & ! intent(out): [dp] windspeed at the top of the canopy (m s-1) + scalarWindspdCanopyBottom => flux_data%var(iLookFLUX%scalarWindspdCanopyBottom)%dat(1), & ! intent(out): [dp] windspeed at the height of the bottom of the canopy (m s-1) + scalarLeafResistance => flux_data%var(iLookFLUX%scalarLeafResistance)%dat(1), & ! intent(out): [dp] mean leaf boundary layer resistance per unit leaf area (s m-1) + scalarGroundResistance => flux_data%var(iLookFLUX%scalarGroundResistance)%dat(1), & ! intent(out): [dp] below canopy aerodynamic resistance (s m-1) + scalarCanopyResistance => flux_data%var(iLookFLUX%scalarCanopyResistance)%dat(1), & ! intent(out): [dp] above canopy aerodynamic resistance (s m-1) + + ! input/output: soil resistance -- intent(in) and intent(inout) because only called at the first flux call + mLayerRootDensity => diag_data%var(iLookDIAG%mLayerRootDensity)%dat, & ! intent(in): [dp] root density in each layer (-) + scalarAquiferRootFrac => diag_data%var(iLookDIAG%scalarAquiferRootFrac)%dat(1), & ! intent(in): [dp] fraction of roots below the lowest soil layer (-) + scalarTranspireLim => diag_data%var(iLookDIAG%scalarTranspireLim)%dat(1), & ! intent(inout): [dp] weighted average of the transpiration limiting factor (-) + mLayerTranspireLim => diag_data%var(iLookDIAG%mLayerTranspireLim)%dat, & ! intent(inout): [dp] transpiration limiting factor in each layer (-) + scalarTranspireLimAqfr => diag_data%var(iLookDIAG%scalarTranspireLimAqfr)%dat(1), & ! intent(inout): [dp] transpiration limiting factor for the aquifer (-) + scalarSoilRelHumidity => diag_data%var(iLookDIAG%scalarSoilRelHumidity)%dat(1), & ! intent(inout): [dp] relative humidity in the soil pores [0-1] + scalarSoilResistance => flux_data%var(iLookFLUX%scalarSoilResistance)%dat(1), & ! intent(inout): [dp] resistance from the soil (s m-1) + + ! input/output: stomatal resistance -- intent(inout) because only called at the first flux call + scalarStomResistSunlit => flux_data%var(iLookFLUX%scalarStomResistSunlit)%dat(1), & ! intent(inout): [dp] stomatal resistance for sunlit leaves (s m-1) + scalarStomResistShaded => flux_data%var(iLookFLUX%scalarStomResistShaded)%dat(1), & ! intent(inout): [dp] stomatal resistance for shaded leaves (s m-1) + scalarPhotosynthesisSunlit => flux_data%var(iLookFLUX%scalarPhotosynthesisSunlit)%dat(1), & ! intent(inout): [dp] sunlit photosynthesis (umolco2 m-2 s-1) + scalarPhotosynthesisShaded => flux_data%var(iLookFLUX%scalarPhotosynthesisShaded)%dat(1), & ! intent(inout): [dp] shaded photosynthesis (umolco2 m-2 s-1) + + ! output: turbulent heat fluxes + scalarLatHeatSubVapCanopy => diag_data%var(iLookDIAG%scalarLatHeatSubVapCanopy)%dat(1), & ! intent(inout): [dp] latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) + scalarLatHeatSubVapGround => diag_data%var(iLookDIAG%scalarLatHeatSubVapGround)%dat(1), & ! intent(inout): [dp] latent heat of sublimation/vaporization for the ground surface (J kg-1) + scalarSatVP_canopyTemp => diag_data%var(iLookDIAG%scalarSatVP_CanopyTemp)%dat(1), & ! intent(out): [dp] saturation vapor pressure at the temperature of the vegetation canopy (Pa) + scalarSatVP_groundTemp => diag_data%var(iLookDIAG%scalarSatVP_GroundTemp)%dat(1), & ! intent(out): [dp] saturation vapor pressure at the temperature of the ground surface (Pa) + scalarSenHeatTotal => flux_data%var(iLookFLUX%scalarSenHeatTotal)%dat(1), & ! intent(out): [dp] sensible heat from the canopy air space to the atmosphere (W m-2) + scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1), & ! intent(out): [dp] sensible heat flux from the canopy to the canopy air space (W m-2) + scalarSenHeatGround => flux_data%var(iLookFLUX%scalarSenHeatGround)%dat(1), & ! intent(out): [dp] sensible heat flux from ground surface below vegetation (W m-2) + scalarLatHeatTotal => flux_data%var(iLookFLUX%scalarLatHeatTotal)%dat(1), & ! intent(out): [dp] latent heat from the canopy air space to the atmosphere (W m-2) + scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1), & ! intent(out): [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + scalarLatHeatCanopyTrans => flux_data%var(iLookFLUX%scalarLatHeatCanopyTrans)%dat(1), & ! intent(out): [dp] latent heat flux for transpiration from the canopy to the canopy air space (W m-2) + scalarLatHeatGround => flux_data%var(iLookFLUX%scalarLatHeatGround)%dat(1), & ! intent(out): [dp] latent heat flux from ground surface below vegetation (W m-2) + + ! output: advective heat fluxes + scalarCanopyAdvectiveHeatFlux => flux_data%var(iLookFLUX%scalarCanopyAdvectiveHeatFlux)%dat(1), & ! intent(out): [dp] heat advected to the canopy surface with rain + snow (W m-2) + scalarGroundAdvectiveHeatFlux => flux_data%var(iLookFLUX%scalarGroundAdvectiveHeatFlux)%dat(1), & ! intent(out): [dp] heat advected to the ground surface with throughfall (W m-2) + + ! output: mass fluxes + scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1), & ! intent(out): [dp] canopy sublimation/frost (kg m-2 s-1) + scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! intent(out): [dp] snow sublimation/frost -- below canopy or non-vegetated (kg m-2 s-1) + + ! input/output: canopy air space variables + scalarVP_CanopyAir => diag_data%var(iLookDIAG%scalarVP_CanopyAir)%dat(1), & ! intent(inout): [dp] vapor pressure of the canopy air space (Pa) + scalarCanopyStabilityCorrection => diag_data%var(iLookDIAG%scalarCanopyStabilityCorrection)%dat(1),& ! intent(inout): [dp] stability correction for the canopy (-) + scalarGroundStabilityCorrection => diag_data%var(iLookDIAG%scalarGroundStabilityCorrection)%dat(1),& ! intent(inout): [dp] stability correction for the ground surface (-) + + ! output: liquid water fluxes + scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1), & ! intent(out): [dp] canopy transpiration (kg m-2 s-1) + scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1), & ! intent(out): [dp] canopy evaporation/condensation (kg m-2 s-1) + scalarGroundEvaporation => flux_data%var(iLookFLUX%scalarGroundEvaporation)%dat(1), & ! intent(out): [dp] ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + + ! output: derived fluxes + scalarTotalET => flux_data%var(iLookFLUX%scalarTotalET)%dat(1), & ! intent(out): [dp] total ET (kg m-2 s-1) + scalarNetRadiation => flux_data%var(iLookFLUX%scalarNetRadiation)%dat(1) & ! intent(out): [dp] net radiation (W m-2) + ) + ! --------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="vegNrgFluxSundials/" + + ! initialize printflag + printflag = .false. + + ! identify the type of boundary condition for thermodynamics + select case(ix_bcUpprTdyn) + + ! ***** + ! (1) DIRICHLET OR ZERO FLUX BOUNDARY CONDITION... + ! ************************************************ + + ! NOTE: Vegetation fluxes are not computed in this case + + ! ** prescribed temperature or zero flux at the upper boundary of the snow-soil system + case(prescribedTemp,zeroFlux) + + ! derived fluxes + scalarTotalET = 0._rkind ! total ET (kg m-2 s-1) + scalarNetRadiation = 0._rkind ! net radiation (W m-2) + ! liquid water fluxes associated with evaporation/transpiration + scalarCanopyTranspiration = 0._rkind ! canopy transpiration (kg m-2 s-1) + scalarCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) + scalarGroundEvaporation = 0._rkind ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + ! solid water fluxes associated with sublimation/frost + scalarCanopySublimation = 0._rkind ! sublimation from the vegetation canopy ((kg m-2 s-1) + scalarSnowSublimation = 0._rkind ! sublimation from the snow surface ((kg m-2 s-1) + ! set canopy fluxes to zero (no canopy) + canairNetFlux = 0._rkind ! net energy flux for the canopy air space (W m-2) + canopyNetFlux = 0._rkind ! net energy flux for the vegetation canopy (W m-2) + ! set canopy derivatives to zero + dCanairNetFlux_dCanairTemp = 0._rkind ! derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) + dCanairNetFlux_dCanopyTemp = 0._rkind ! derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) + dCanairNetFlux_dGroundTemp = 0._rkind ! derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) + dCanopyNetFlux_dCanairTemp = 0._rkind ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) + dCanopyNetFlux_dCanopyTemp = 0._rkind ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) + dCanopyNetFlux_dGroundTemp = 0._rkind ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) + dGroundNetFlux_dCanairTemp = 0._rkind ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) + dGroundNetFlux_dCanopyTemp = 0._rkind ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) + ! set liquid flux derivatives to zero (canopy evap) + dCanopyEvaporation_dCanWat = 0._rkind ! derivative in canopy evaporation w.r.t. canopy total water content (s-1) + dCanopyEvaporation_dTCanair= 0._rkind ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTCanopy= 0._rkind ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTGround= 0._rkind ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + ! set liquid flux derivatives to zero (ground evap) + dGroundEvaporation_dCanWat = 0._rkind ! derivative in ground evaporation w.r.t. canopy total water content (s-1) + dGroundEvaporation_dTCanair= 0._rkind ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTCanopy= 0._rkind ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTGround= 0._rkind ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + + ! compute fluxes and derivatives -- separate approach for prescribed temperature and zero flux, + if(ix_bcUpprTdyn == prescribedTemp)then + ! compute ground net flux (W m-2) + groundNetFlux = -diag_data%var(iLookDIAG%iLayerThermalC)%dat(0)*(groundTempTrial - upperBoundTemp)/(prog_data%var(iLookPROG%mLayerDepth)%dat(1)*0.5_rkind) + ! compute derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) inside soil and snow (ssd) energy flux routine + ! dGroundNetFlux_dGroundTemp = missingValue + elseif(ix_bcUpprTdyn == zeroFlux)then + groundNetFlux = 0._rkind + ! dGroundNetFlux_dGroundTemp = missingValue + else + err=20; message=trim(message)//'unable to identify upper boundary condition for thermodynamics: expect the case to be prescribedTemp or zeroFlux'; return + end if + + ! ***** + ! (2) NEUMANN BOUNDARY CONDITION... + ! ********************************* + + ! NOTE 1: This is the main routine for calculating vegetation fluxes + ! NOTE 2: This routine also calculates surface fluxes for the case where vegetation is buried with snow (or bare soil) + + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + ! ***** PRELIMINARIES ********************************************************************************************************************************************** + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + + ! * flux boundary condition + case(energyFlux) + + ! identify the appropriate groundwater variable + select case(ix_spatial_gw) + case(singleBasin); scalarAquiferStorage = basinAquiferStorage + case(localColumn); scalarAquiferStorage = localAquiferStorage + case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return + end select ! (modify the groundwater representation for this single-column implementation) + + ! set canopy stability corrections to the previous values + scalarCanopyStabilityCorrection_old = scalarCanopyStabilityCorrection ! stability correction for the canopy (-) + scalarGroundStabilityCorrection_old = scalarGroundStabilityCorrection ! stability correction for the ground surface (-) + + ! initialize variables to compute stomatal resistance + if(firstFluxCall .and. firstSubStep)then + ! vapor pressure in the canopy air space initialized as vapor pressure of air above the vegetation canopy + ! NOTE: this is needed for the stomatal resistance calculations + if(scalarVP_CanopyAir < 0._rkind)then + scalarVP_CanopyAir = scalarVPair - 1._rkind ! "small" offset used to assist in checking initial derivative calculations + end if + end if + + ! set latent heat of sublimation/vaporization for canopy and ground surface (Pa/K) + ! NOTE: variables are constant over the substep, to simplify relating energy and mass fluxes + if(firstFluxCall)then + scalarLatHeatSubVapCanopy = getLatentHeatValue(canopyTempTrial) + ! case when there is snow on the ground (EXCLUDE "snow without a layer" -- in this case, evaporate from the soil) + if(nSnow > 0)then + if(groundTempTrial > Tfreeze)then; err=20; message=trim(message)//'do not expect ground temperature > 0 when snow is on the ground'; return; end if + scalarLatHeatSubVapGround = LH_sub ! sublimation from snow + scalarGroundSnowFraction = 1._rkind + ! case when the ground is snow-free + else + scalarLatHeatSubVapGround = LH_vap ! evaporation of water in the soil pores: this occurs even if frozen because of super-cooled water + scalarGroundSnowFraction = 0._rkind + end if ! (if there is snow on the ground) + end if ! (if the first flux call) + !write(*,'(a,1x,10(f30.10,1x))') 'groundTempTrial, scalarLatHeatSubVapGround = ', groundTempTrial, scalarLatHeatSubVapGround + + ! compute the roughness length of the ground (ground below the canopy or non-vegetated surface) + z0Ground = z0soil*(1._rkind - scalarGroundSnowFraction) + z0Snow*scalarGroundSnowFraction ! roughness length (m) + + ! compute the total vegetation area index (leaf plus stem) + VAI = scalarLAI + scalarSAI ! vegetation area index + exposedVAI = scalarExposedLAI + scalarExposedSAI ! exposed vegetation area index + + ! compute emissivity of the canopy (-) + if(computeVegFlux)then + select case(ix_canopyEmis) + ! *** simple exponential function + case(simplExp) + scalarCanopyEmissivity = 1._rkind - exp(-exposedVAI) ! effective emissivity of the canopy (-) + ! *** canopy emissivity parameterized as a function of diffuse transmissivity + case(difTrans) + ! compute the exponential integral + scaleLAI = 0.5_rkind*exposedVAI + expi = expInt(scaleLAI) + ! compute diffuse transmissivity (-) + diffuseTrans = (1._rkind - scaleLAI)*exp(-scaleLAI) + (scaleLAI**2._rkind)*expi + ! compute the canopy emissivity + scalarCanopyEmissivity = (1._rkind - diffuseTrans)*vegEmissivity + ! *** check we found the correct option + case default + err=20; message=trim(message)//'unable to identify option for canopy emissivity'; return + end select + end if + + ! ensure canopy longwave fluxes are zero when not computing canopy fluxes + if(.not.computeVegFlux) scalarCanopyEmissivity=0._rkind + + ! compute emissivity of the ground surface (-) + groundEmissivity = scalarGroundSnowFraction*snowEmissivity + (1._rkind - scalarGroundSnowFraction)*soilEmissivity ! emissivity of the ground surface (-) + + ! compute the fraction of canopy that is wet + ! NOTE: we either sublimate or evaporate over the entire substep + if(computeVegFlux)then + + ! compute the fraction of liquid water in the canopy (-) + totalCanopyWater = canopyLiqTrial + canopyIceTrial + if(totalCanopyWater > tiny(1.0_rkind))then + fracLiquidCanopy = canopyLiqTrial / (canopyLiqTrial + canopyIceTrial) + else + fracLiquidCanopy = 0._rkind + end if + + ! get wetted fraction and derivatives + call wettedFrac(& + ! input + .true., & ! flag to denote if derivative is desired + (ixDerivMethod == numerical), & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) + (scalarLatHeatSubVapCanopy > LH_vap+verySmall), & ! flag to denote if the canopy is frozen + dCanLiq_dTcanopy, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) + fracLiquidCanopy, & ! fraction of liquid water on the canopy (-) + canopyLiqTrial, & ! canopy liquid water (kg m-2) + canopyIceTrial, & ! canopy ice (kg m-2) + scalarCanopyLiqMax, & ! maximum canopy liquid water (kg m-2) + scalarCanopyIceMax, & ! maximum canopy ice content (kg m-2) + canopyWettingFactor, & ! maximum wetted fraction of the canopy (-) + canopyWettingExp, & ! exponent in canopy wetting function (-) + ! output + scalarCanopyWetFraction, & ! canopy wetted fraction (-) + dCanopyWetFraction_dWat, & ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) + dCanopyWetFraction_dT, & ! derivative in wetted fraction w.r.t. canopy temperature (K-1) + err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + else + scalarCanopyWetFraction = 0._rkind ! canopy wetted fraction (-) + dCanopyWetFraction_dWat = 0._rkind ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) + dCanopyWetFraction_dT = 0._rkind ! derivative in wetted fraction w.r.t. canopy temperature (K-1) + end if + !write(*,'(a,1x,L1,1x,f25.15,1x))') 'computeVegFlux, scalarCanopyWetFraction = ', computeVegFlux, scalarCanopyWetFraction + !print*, 'dCanopyWetFraction_dWat = ', dCanopyWetFraction_dWat + !print*, 'dCanopyWetFraction_dT = ', dCanopyWetFraction_dT + !print*, 'canopyLiqTrial = ', canopyLiqTrial + !print*, 'canopyIceTrial = ', canopyIceTrial + !print*, 'scalarCanopyLiqMax = ', scalarCanopyLiqMax + !print*, 'scalarCanopyIceMax = ', scalarCanopyIceMax + + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + ! ***** AERODYNAMIC RESISTANCE ***************************************************************************************************************************************** + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + + ! NOTE: compute for all iterations + + ! compute aerodynamic resistances + ! Refs: Choudhury and Monteith (4-layer model for heat budget of homogenous surfaces; QJRMS, 1988) + ! Niu and Yang (Canopy effects on snow processes; JGR, 2004) + ! Mahat et al. (Below-canopy turbulence in a snowmelt model, WRR, 2012) + call aeroResist(& + ! input: model control + computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) + (ixDerivMethod == analytical), & ! intent(in): logical flag if would like to compute analytical derivaties + ix_veg_traits, & ! intent(in): choice of parameterization for vegetation roughness length and displacement height + ix_windPrfile, & ! intent(in): choice of canopy wind profile + ix_astability, & ! intent(in): choice of stability function + ! input: above-canopy forcing data + mHeight, & ! intent(in): measurement height (m) + airtemp, & ! intent(in): air temperature at some height above the surface (K) + windspd, & ! intent(in): wind speed at some height above the surface (m s-1) + ! input: canopy and ground temperature + canairTempTrial, & ! intent(in): temperature of the canopy air space (K) + groundTempTrial, & ! intent(in): temperature of the ground surface (K) + ! input: diagnostic variables + exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) + scalarSnowDepth, & ! intent(in): snow depth (m) + ! input: parameters + z0Ground, & ! intent(in): roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) + z0CanopyParam, & ! intent(in): roughness length of the canopy (m) + zpdFraction, & ! intent(in): zero plane displacement / canopy height (-) + critRichNumber, & ! intent(in): critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam, & ! intent(in): parameter in Louis (1979) stability function + Mahrt87_eScale, & ! intent(in): exponential scaling factor in the Mahrt (1987) stability function + windReductionParam, & ! intent(in): canopy wind reduction parameter (-) + leafExchangeCoeff, & ! intent(in): turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) + leafDimension, & ! intent(in): characteristic leaf dimension (m) + heightCanopyTop, & ! intent(in): height at the top of the vegetation canopy (m) + heightCanopyBottom, & ! intent(in): height at the bottom of the vegetation canopy (m) + ! output: stability corrections + scalarRiBulkCanopy, & ! intent(out): bulk Richardson number for the canopy (-) + scalarRiBulkGround, & ! intent(out): bulk Richardson number for the ground surface (-) + scalarCanopyStabilityCorrection, & ! intent(out): stability correction for the canopy (-) + scalarGroundStabilityCorrection, & ! intent(out): stability correction for the ground surface (-) + ! output: scalar resistances + scalarZ0Canopy, & ! intent(out): roughness length of the canopy (m) + scalarWindReductionFactor, & ! intent(out): canopy wind reduction factor (-) + scalarZeroPlaneDisplacement, & ! intent(out): zero plane displacement (m) + scalarEddyDiffusCanopyTop, & ! intent(out): eddy diffusivity for heat at the top of the canopy (m2 s-1) + scalarFrictionVelocity, & ! intent(out): friction velocity (m s-1) + scalarWindspdCanopyTop, & ! intent(out): windspeed at the top of the canopy (m s-1) + scalarWindspdCanopyBottom, & ! intent(out): windspeed at the height of the bottom of the canopy (m s-1) + scalarLeafResistance, & ! intent(out): mean leaf boundary layer resistance per unit leaf area (s m-1) + scalarGroundResistance, & ! intent(out): below canopy aerodynamic resistance (s m-1) + scalarCanopyResistance, & ! intent(out): above canopy aerodynamic resistance (s m-1) + ! output: derivatives in scalar resistances + dGroundResistance_dTGround, & ! intent(out): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + dGroundResistance_dTCanopy, & ! intent(out): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + dGroundResistance_dTCanair, & ! intent(out): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + dCanopyResistance_dTCanopy, & ! intent(out): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + dCanopyResistance_dTCanair, & ! intent(out): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + ! output: error control + err,cmessage ) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + !print*, scalarLeafResistance, & ! mean leaf boundary layer resistance per unit leaf area (s m-1) + ! scalarGroundResistance, & ! below canopy aerodynamic resistance (s m-1) + ! scalarCanopyResistance, & ! above canopy aerodynamic resistance (s m-1) + ! '(leaf, ground, canopy)' + + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + ! ***** STOMATAL RESISTANCE ***************************************************************************************************************************************** + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + + ! stomatal resistance is constant over the SUBSTEP + ! NOTE: This is a simplification, as stomatal resistance does depend on canopy temperature + ! This "short-cut" made because: + ! (1) computations are expensive; + ! (2) derivative calculations are rather complex (iterations within the Ball-Berry routine); and + ! (3) stomatal resistance does not change rapidly + if(firstFluxCall)then + + ! compute the saturation vapor pressure for vegetation temperature + TV_celcius = canopyTempTrial - Tfreeze + call satVapPress(TV_celcius, scalarSatVP_CanopyTemp, dSVPCanopy_dCanopyTemp) + + ! compute soil moisture factor controlling stomatal resistance + call soilResist(& + ! input (model decisions) + ix_soilStress, & ! intent(in): choice of function for the soil moisture control on stomatal resistance + ix_groundwatr, & ! intent(in): groundwater parameterization + ! input (state variables) + mLayerMatricHead(1:nSoil), & ! intent(in): matric head in each soil layer (m) + mLayerVolFracLiq(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water in each soil layer (-) + scalarAquiferStorage, & ! intent(in): aquifer storage (m) + ! input (diagnostic variables) + mLayerRootDensity(1:nSoil), & ! intent(in): root density in each layer (-) + scalarAquiferRootFrac, & ! intent(in): fraction of roots below the lowest soil layer (-) + ! input (parameters) + plantWiltPsi, & ! intent(in): matric head at wilting point (m) + soilStressParam, & ! intent(in): parameter in the exponential soil stress function (-) + critSoilWilting, & ! intent(in): critical vol. liq. water content when plants are wilting (-) + critSoilTranspire, & ! intent(in): critical vol. liq. water content when transpiration is limited (-) + critAquiferTranspire, & ! intent(in): critical aquifer storage value when transpiration is limited (m) + ! output + scalarTranspireLim, & ! intent(out): weighted average of the transpiration limiting factor (-) + mLayerTranspireLim(1:nSoil), & ! intent(out): transpiration limiting factor in each layer (-) + scalarTranspireLimAqfr, & ! intent(out): transpiration limiting factor for the aquifer (-) + err,cmessage ) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + !print*, 'weighted average of the soil moiture factor controlling stomatal resistance (-) = ', scalarTranspireLim + + !write(*,'(a,1x,10(f20.10,1x))') 'canopyTempTrial, scalarSatVP_CanopyTemp, scalarVP_CanopyAir = ', & + ! canopyTempTrial, scalarSatVP_CanopyTemp, scalarVP_CanopyAir + + ! compute stomatal resistance + call stomResist(& + ! input (state and diagnostic variables) + canopyTempTrial, & ! intent(in): temperature of the vegetation canopy (K) + scalarSatVP_CanopyTemp, & ! intent(in): saturation vapor pressure at the temperature of the veg canopy (Pa) + scalarVP_CanopyAir, & ! intent(in): canopy air vapor pressure (Pa) + ! input: data structures + type_data, & ! intent(in): type of vegetation and soil + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + model_decisions, & ! intent(in): model decisions + ! input-output: data structures + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output: error control + err,cmessage ) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + end if ! (if the first flux call in a given sub-step) + + + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + ! ***** LONGWAVE RADIATION ***************************************************************************************************************************************** + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + + ! compute canopy longwave radiation balance + call longwaveBal(& + ! input: model control + ixDerivMethod, & ! intent(in): method used to calculate flux derivatives + computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation + ! input: canopy and ground temperature + canopyTempTrial, & ! intent(in): temperature of the vegetation canopy (K) + groundTempTrial, & ! intent(in): temperature of the ground surface (K) + ! input: canopy and ground emissivity + scalarCanopyEmissivity, & ! intent(in): canopy emissivity (-) + groundEmissivity, & ! intent(in): ground emissivity (-) + ! input: forcing + LWRadAtm, & ! intent(in): downwelling longwave radiation at the upper boundary (W m-2) + ! output: emitted radiation from the canopy and ground + scalarLWRadCanopy, & ! intent(out): longwave radiation emitted from the canopy (W m-2) + scalarLWRadGround, & ! intent(out): longwave radiation emitted at the ground surface (W m-2) + ! output: individual fluxes + scalarLWRadUbound2Canopy, & ! intent(out): downward atmospheric longwave radiation absorbed by the canopy (W m-2) + scalarLWRadUbound2Ground, & ! intent(out): downward atmospheric longwave radiation absorbed by the ground (W m-2) + scalarLWRadUbound2Ubound, & ! intent(out): atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) + scalarLWRadCanopy2Ubound, & ! intent(out): longwave radiation emitted from canopy lost thru upper boundary (W m-2) + scalarLWRadCanopy2Ground, & ! intent(out): longwave radiation emitted from canopy absorbed by the ground (W m-2) + scalarLWRadCanopy2Canopy, & ! intent(out): canopy longwave reflected from ground and absorbed by the canopy (W m-2) + scalarLWRadGround2Ubound, & ! intent(out): longwave radiation emitted from ground lost thru upper boundary (W m-2) + scalarLWRadGround2Canopy, & ! intent(out): longwave radiation emitted from ground and absorbed by the canopy (W m-2) + ! output: net fluxes + scalarLWNetCanopy, & ! intent(out): net longwave radiation at the canopy (W m-2) + scalarLWNetGround, & ! intent(out): net longwave radiation at the ground surface (W m-2) + scalarLWNetUbound, & ! intent(out): net longwave radiation at the upper boundary (W m-2) + ! output: flux derivatives + dLWNetCanopy_dTCanopy, & ! intent(out): derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) + dLWNetGround_dTGround, & ! intent(out): derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) + dLWNetCanopy_dTGround, & ! intent(out): derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) + dLWNetGround_dTCanopy, & ! intent(out): derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) + ! output: error control + err,cmessage ) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + !print*, 'dLWNetCanopy_dTGround = ', dLWNetCanopy_dTGround + + + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + ! ***** TURBULENT HEAT FLUXES ************************************************************************************************************************************** + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + + ! check the need to compute numerical derivatives + if(ixDerivMethod == numerical)then + nFlux=5 ! compute the derivatives using one-sided finite differences + else + nFlux=1 ! compute analytical derivatives + end if + + ! either one or multiple flux calls, depending on if using analytical or numerical derivatives + do itry=nFlux,1,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) + + ! ------------------------------------------------------------------------------------- + ! state perturbations for numerical deriavtives with one-sided finite differences + ! note: no perturbations performed using analytical derivatives (nFlux=1) + ! ------------------------------------------------------------------------------------- + + ! identify the type of perturbation + select case(itry) + + ! un-perturbed case + case(unperturbed) + groundTemp = groundTempTrial + canopyTemp = canopyTempTrial + canairTemp = canairTempTrial + canopyWat = totalCanopyWater + + ! perturb ground temperature + case(perturbStateGround) + groundTemp = groundTempTrial + dx + canopyTemp = canopyTempTrial + canairTemp = canairTempTrial + canopyWat = totalCanopyWater + + ! perturb canopy temperature + case(perturbStateCanopy) + groundTemp = groundTempTrial + canopyTemp = canopyTempTrial + dx + canairTemp = canairTempTrial + canopyWat = totalCanopyWater + + ! perturb canopy air temperature + case(perturbStateCanair) + groundTemp = groundTempTrial + canopyTemp = canopyTempTrial + canairTemp = canairTempTrial + dx + canopyWat = totalCanopyWater + + ! perturb canopy total water content + case(perturbStateCanWat) + groundTemp = groundTempTrial + canopyTemp = canopyTempTrial + canairTemp = canairTempTrial + canopyWat = totalCanopyWater + dx + + ! check for an unknown perturbation + case default; err=10; message=trim(message)//"unknown perturbation"; return + + end select ! (type of perturbation) + + ! recalculate liquid and ice from total water + canopyWetFraction = scalarCanopyWetFraction + fracLiquidCanopy = fracliquid(canopyTemp,snowfrz_scale) + canopyLiq = fracLiquidCanopy*canopyWat + canopyIce = (1._rkind - fracLiquidCanopy)*canopyWat + + ! perturbations in canopy total water content affect canopy wetted fraction + if(itry == perturbStateCanopy .OR. itry == perturbStateCanWat)then + if(computeVegFlux)then + call wettedFrac(& + ! input + .false., & ! flag to denote if derivative is desired + (ixDerivMethod == numerical), & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) + (scalarLatHeatSubVapCanopy > LH_vap+verySmall), & ! flag to denote if the canopy is frozen + dCanLiq_dTcanopy, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) + fracLiquidCanopy, & ! fraction of liquid water on the canopy (-) + canopyLiq, & ! canopy liquid water (kg m-2) + canopyIce, & ! canopy ice (kg m-2) + scalarCanopyLiqMax, & ! maximum canopy liquid water (kg m-2) + scalarCanopyIceMax, & ! maximum canopy ice content (kg m-2) + canopyWettingFactor, & ! maximum wetted fraction of the canopy (-) + canopyWettingExp, & ! exponent in canopy wetting function (-) + ! output + canopyWetFraction, & ! canopy wetted fraction (-) + dCanopyWetFraction_dWat, & ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) + dCanopyWetFraction_dT, & ! derivative in wetted fraction w.r.t. canopy temperature (K-1) + err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + else + canopyWetFraction = 0._rkind + end if ! (desired computing vegetation flux) + + end if ! (re-computing wetted fraction for perturbed cases) + !print*, 'wetted fraction derivative = ', (canopyWetFraction - scalarCanopyWetFraction)/dx + !pause + + ! compute the saturation vapor pressure for vegetation temperature + ! NOTE: saturated vapor pressure derivatives don't seem that accurate.... + TV_celcius = canopyTemp - Tfreeze + call satVapPress(TV_celcius, scalarSatVP_CanopyTemp, dSVPCanopy_dCanopyTemp) + + ! compute the saturation vapor pressure for ground temperature + ! NOTE: saturated vapor pressure derivatives don't seem that accurate.... + TG_celcius = groundTemp - Tfreeze + call satVapPress(TG_celcius, scalarSatVP_GroundTemp, dSVPGround_dGroundTemp) + + ! ------------------------------------------------------------------------------------- + ! calculation block (unperturbed fluxes returned [computed last]) + ! ------------------------------------------------------------------------------------- + + ! re-compute aerodynamic resistances for perturbed cases + ! NOTE: unperturbed fluxes computed earlier, and not over-written + if(itry /= unperturbed)then + call aeroResist(& + ! input: model control + computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) + .false., & ! intent(in): logical flag if would like to compute analytical derivaties + ix_veg_traits, & ! intent(in): choice of parameterization for vegetation roughness length and displacement height + ix_windPrfile, & ! intent(in): choice of canopy wind profile + ix_astability, & ! intent(in): choice of stability function + ! input: above-canopy forcing data + mHeight, & ! intent(in): measurement height (m) + airtemp, & ! intent(in): air temperature at some height above the surface (K) + windspd, & ! intent(in): wind speed at some height above the surface (m s-1) + ! input: temperature (canopy, ground, canopy air space) + canairTemp, & ! intent(in): temperature of the canopy air space (K) + groundTemp, & ! intent(in): ground temperature (K) + ! input: diagnostic variables + exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) + scalarSnowDepth, & ! intent(in): snow depth (m) + ! input: parameters + z0Ground, & ! intent(in): roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) + z0CanopyParam, & ! intent(in): roughness length of the canopy (m) + zpdFraction, & ! intent(in): zero plane displacement / canopy height (-) + critRichNumber, & ! intent(in): critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam, & ! intent(in): parameter in Louis (1979) stability function + Mahrt87_eScale, & ! intent(in): exponential scaling factor in the Mahrt (1987) stability function + windReductionParam, & ! intent(in): canopy wind reduction parameter (-) + leafExchangeCoeff, & ! intent(in): turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) + leafDimension, & ! intent(in): characteristic leaf dimension (m) + heightCanopyTop, & ! intent(in): height at the top of the vegetation canopy (m) + heightCanopyBottom, & ! intent(in): height at the bottom of the vegetation canopy (m) + ! output: stability corrections + notUsed_RiBulkCanopy, & ! intent(out): bulk Richardson number for the canopy (-) + notUsed_RiBulkGround, & ! intent(out): bulk Richardson number for the ground surface (-) + notUsed_scalarCanopyStabilityCorrection, & ! intent(out): stability correction for the canopy (-) + notUsed_scalarGroundStabilityCorrection, & ! intent(out): stability correction for the ground surface (-) + ! output: scalar resistances + notUsed_z0Canopy, & ! intent(out): roughness length of the canopy (m) + notUsed_WindReductionFactor, & ! intent(out): canopy wind reduction factor (-) + notUsed_ZeroPlaneDisplacement, & ! intent(out): zero plane displacement (m) + notUsed_EddyDiffusCanopyTop, & ! intent(out): eddy diffusivity for heat at the top of the canopy (m2 s-1) + notUsed_FrictionVelocity, & ! intent(out): friction velocity (m s-1) + notUsed_WindspdCanopyTop, & ! intent(out): windspeed at the top of the canopy (m s-1) + notUsed_WindspdCanopyBottom, & ! intent(out): windspeed at the height of the bottom of the canopy (m s-1) + trialLeafResistance, & ! intent(out): mean leaf boundary layer resistance per unit leaf area (s m-1) + trialGroundResistance, & ! intent(out): below canopy aerodynamic resistance (s m-1) + trialCanopyResistance, & ! intent(out): above canopy aerodynamic resistance (s m-1) + ! output: derivatives in scalar resistances + notUsed_dGroundResistance_dTGround, & ! intent(out): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + notUsed_dGroundResistance_dTCanopy, & ! intent(out): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + notUsed_dGroundResistance_dTCanair, & ! intent(out): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + notUsed_dCanopyResistance_dTCanopy, & ! intent(out): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + notUsed_dCanopyResistance_dTCanair, & ! intent(out): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + ! output: error control + err,cmessage ) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + + ! assign scalar resistances for un-perturbed cases + else + trialLeafResistance = scalarLeafResistance + trialGroundResistance = scalarGroundResistance + trialCanopyResistance = scalarCanopyResistance + + end if ! (re-computing resistances for perturbed cases) + !print*, 'trialLeafResistance = ', trialLeafResistance + !print*, 'trialGroundResistance = ', trialGroundResistance + !print*, 'trialCanopyResistance = ', trialCanopyResistance + + ! compute the relative humidity in the top soil layer and the resistance at the ground surface + ! NOTE: computations are based on start-of-step values, so only compute for the first flux call + if(firstFluxCall)then + ! (soil water derivatives with ground water and temp) + dGroundVolFracLiq_dPsi0 = dTheta_dPsi(mLayerMatricHead(1),vGn_alpha(1),theta_res,theta_sat,vGn_n(1),vGn_m(1)) + dGroundVolFracLiq_dTk = dTheta_dTk(groundTemp,theta_res,theta_sat,vGn_alpha(1),vGn_n(1),vGn_m(1)) + ! (soil water evaporation factor [0-1]) + soilEvapFactor = mLayerVolFracLiq(nSnow+1)/(theta_sat - theta_res) + ! (resistance from the soil [s m-1]) + scalarSoilResistance = scalarGroundSnowFraction*1._rkind + (1._rkind - scalarGroundSnowFraction)*EXP(8.25_rkind - 4.225_rkind*soilEvapFactor) ! Sellers (1992) + !scalarSoilResistance = scalarGroundSnowFraction*0._rkind + (1._rkind - scalarGroundSnowFraction)*exp(8.25_rkind - 6.0_rkind*soilEvapFactor) ! Niu adjustment to decrease resistance for wet soil + ! (soil resistence derivatives with ground water and temp) + !dSoilResistance_dWat = -scalarGroundSnowFraction*EXP(8.25_rkind - 4.225_rkind*soilEvapFactor) * (-4.225_rkind*dGroundVolFracLiq_dPsi0/(theta_sat - theta_res)) ! Sellers (1992) + !dSoilResistance_dT = -scalarGroundSnowFraction*EXP(8.25_rkind - 4.225_rkind*soilEvapFactor) * (-4.225_rkind*dGroundVolFracLiq_dTk/(theta_sat - theta_res)) ! Sellers (1992) + !dSoilResistance_dWat = -scalarGroundSnowFraction*EXP(8.25_rkind - 6.0_rkind*soilEvapFactor) * (-6.0_rkind*dGroundVolFracLiq_dPsi0/(theta_sat - theta_res)) ! Niu adjustment + !dSoilResistance_dT = -scalarGroundSnowFraction*EXP(8.25_rkind - 6.0_rkind*soilEvapFactor) * (-6.0_rkind*dGroundVolFracLiq_dTk/(theta_sat - theta_res)) ! Niu adjustment + ! (relative humidity in the soil pores [0-1]) + if(mLayerMatricHead(1) > -1.e+6_rkind)then ! avoid problems with numerical precision when soil is very dry + soilRelHumidity_noSnow = exp( (mLayerMatricHead(1)*gravity) / (groundTemp*R_wv) ) + else + soilRelHumidity_noSnow = 0._rkind + end if ! (if matric head is very low) + scalarSoilRelHumidity = scalarGroundSnowFraction*1._rkind + (1._rkind - scalarGroundSnowFraction)*soilRelHumidity_noSnow + ! (relative humidity derivatives with ground water and tems) + ! using dSoilRelH_noSnow_dWat = soilRelHumidity_noSnow* gravity / (groundTemp*R_wv) and dSoilRelH_noSnow_dT = soilRelHumidity_noSnow* (-mLayerMatricHead(1)*gravity) / ( (groundTemp**2._rkind) *R_wv ) + !dSoilRelHumidity_dWat = (1._rkind - scalarGroundSnowFraction)*soilRelHumidity_noSnow * gravity / (groundTemp*R_wv) + !dSoilRelHumidity_dT = (1._rkind - scalarGroundSnowFraction)*soilRelHumidity_noSnow * (-mLayerMatricHead(1)*gravity) / ( (groundTemp**2._rkind) *R_wv ) + !print*, 'mLayerMatricHead(1), scalarSoilRelHumidity = ', mLayerMatricHead(1), scalarSoilRelHumidity + end if ! (if the first flux call) + + ! compute turbulent heat fluxes + call turbFluxes(& + ! input: model control + computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) + ixDerivMethod, & ! intent(in): method used to calculate flux derivatives + ! input: above-canopy forcing data + airtemp, & ! intent(in): air temperature at some height above the surface (K) + airpres, & ! intent(in): air pressure of the air above the vegetation canopy (Pa) + scalarVPair, & ! intent(in): vapor pressure of the air above the vegetation canopy (Pa) + ! input: latent heat of sublimation/vaporization + scalarLatHeatSubVapCanopy, & ! intent(in): latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) + scalarLatHeatSubVapGround, & ! intent(in): latent heat of sublimation/vaporization for the ground surface (J kg-1) + ! input: canopy/ground temperature and saturated vapor pressure + canairTemp, & ! intent(in): temperature of the canopy air space (K) + canopyTemp, & ! intent(in): canopy temperature (K) + groundTemp, & ! intent(in): ground temperature (K) + scalarSatVP_CanopyTemp, & ! intent(in): saturation vapor pressure at the temperature of the veg canopy (Pa) + scalarSatVP_GroundTemp, & ! intent(in): saturation vapor pressure at the temperature of the ground (Pa) + dSVPCanopy_dCanopyTemp, & ! intent(in): derivative in canopy saturation vapor pressure w.r.t. canopy temperature (Pa K-1) + dSVPGround_dGroundTemp, & ! intent(in): derivative in ground saturation vapor pressure w.r.t. ground temperature (Pa K-1) + ! input: diagnostic variables + exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) + canopyWetFraction, & ! intent(in): trial value for the fraction of canopy that is wet [0-1] + dCanopyWetFraction_dWat, & ! intent(in): derivative in the canopy wetted fraction w.r.t. total water content (kg-1 m-2) + dCanopyWetFraction_dT, & ! intent(in): derivative in wetted fraction w.r.t. canopy temperature (K-1) + scalarCanopySunlitLAI, & ! intent(in): sunlit leaf area (-) + scalarCanopyShadedLAI, & ! intent(in): shaded leaf area (-) + scalarSoilRelHumidity, & ! intent(in): relative humidity in the soil pores [0-1] + scalarSoilResistance, & ! intent(in): resistance from the soil (s m-1) + trialLeafResistance, & ! intent(in): mean leaf boundary layer resistance per unit leaf area (s m-1) + trialGroundResistance, & ! intent(in): below canopy aerodynamic resistance (s m-1) + trialCanopyResistance, & ! intent(in): above canopy aerodynamic resistance (s m-1) + scalarStomResistSunlit, & ! intent(in): stomatal resistance for sunlit leaves (s m-1) + scalarStomResistShaded, & ! intent(in): stomatal resistance for shaded leaves (s m-1) + ! input: derivatives in scalar resistances + dGroundResistance_dTGround, & ! intent(in): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + dGroundResistance_dTCanopy, & ! intent(in): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + dGroundResistance_dTCanair, & ! intent(in): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + dCanopyResistance_dTCanopy, & ! intent(in): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + dCanopyResistance_dTCanair, & ! intent(in): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + ! output: conductances (used to check derivative calculations) + scalarLeafConductance, & ! intent(out): leaf conductance (m s-1) + scalarCanopyConductance, & ! intent(out): canopy conductance (m s-1) + scalarGroundConductanceSH, & ! intent(out): ground conductance for sensible heat (m s-1) + scalarGroundConductanceLH, & ! intent(out): ground conductance for latent heat -- includes soil resistance (m s-1) + scalarEvapConductance, & ! intent(out): conductance for evaporation (m s-1) + scalarTransConductance, & ! intent(out): conductance for transpiration (m s-1) + scalarTotalConductanceSH, & ! intent(out): total conductance for sensible heat (m s-1) + scalarTotalConductanceLH, & ! intent(out): total conductance for latent heat (m s-1) + ! output: canopy air space variables + scalarVP_CanopyAir, & ! intent(out): vapor pressure of the canopy air space (Pa) + ! output: fluxes from the vegetation canopy + scalarSenHeatCanopy, & ! intent(out): sensible heat flux from the canopy to the canopy air space (W m-2) + scalarLatHeatCanopyEvap, & ! intent(out): latent heat flux associated with evaporation from the canopy to the canopy air space (W m-2) + scalarLatHeatCanopyTrans, & ! intent(out): latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) + ! output: fluxes from non-vegetated surfaces (ground surface below vegetation, bare ground, or snow covered vegetation) + scalarSenHeatGround, & ! intent(out): sensible heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) + scalarLatHeatGround, & ! intent(out): latent heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) + ! output: total heat fluxes to the atmosphere + scalarSenHeatTotal, & ! intent(out): total sensible heat flux to the atmosphere (W m-2) + scalarLatHeatTotal, & ! intent(out): total latent heat flux to the atmosphere (W m-2) + ! output: net fluxes + turbFluxCanair, & ! intent(out): net turbulent heat fluxes at the canopy air space (W m-2) + turbFluxCanopy, & ! intent(out): net turbulent heat fluxes at the canopy (W m-2) + turbFluxGround, & ! intent(out): net turbulent heat fluxes at the ground surface (W m-2) + ! output: energy flux derivatives + dTurbFluxCanair_dTCanair, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxCanair_dTCanopy, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxCanair_dTGround, & ! intent(out): derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanair, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanopy, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxCanopy_dTGround, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxGround_dTCanair, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxGround_dTCanopy, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxGround_dTGround, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + ! output: liquid flux derivatives (canopy evap) + dLatHeatCanopyEvap_dCanWat, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy total water content (W kg-1) + dLatHeatCanopyEvap_dTCanair, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) + dLatHeatCanopyEvap_dTCanopy, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) + dLatHeatCanopyEvap_dTGround, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) + ! output: liquid flux derivatives (ground evap) + dLatHeatGroundEvap_dCanWat, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) + dLatHeatGroundEvap_dTCanair, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy air temperature + dLatHeatGroundEvap_dTCanopy, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy temperature + dLatHeatGroundEvap_dTGround, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. ground temperature + ! output: cross derivatives + dTurbFluxCanair_dCanWat, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxCanopy_dCanWat, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxGround_dCanWat, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + ! output: error control + err,cmessage ) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + !write(*,'(a,f25.15)') 'scalarSenHeatTotal = ', scalarSenHeatTotal + !write(*,'(a,f25.15)') 'scalarSenHeatCanopy = ', scalarSenHeatCanopy + !write(*,'(a,f25.15)') 'scalarLatHeatCanopyEvap = ', scalarLatHeatCanopyEvap + !write(*,'(a,f25.15)') 'scalarLatHeatCanopyTrans = ', scalarLatHeatCanopyTrans + + !print*, 'scalarSenHeatGround = ', scalarSenHeatGround + !print*, 'scalarLatHeatGround = ', scalarLatHeatGround + + !notUsed_scalarCanopyStabilityCorrection ! stability correction for the canopy (-) + !notUsed_scalarGroundStabilityCorrection ! stability correction for the ground surface (-) + !notUsed_EddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) + !notUsed_FrictionVelocity ! friction velocity (m s-1) + !notUsed_WindspdCanopyTop ! windspeed at the top of the canopy (m s-1) + !notUsed_WindspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) + !trialLeafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) + !trialGroundResistance ! below canopy aerodynamic resistance (s m-1) + !trialCanopyResistance ! above canopy aerodynamic resistance (s m-1) + + ! save perturbed fluxes + if(ixDerivMethod == numerical)then + select case(itry) ! (select type of perturbation) + case(unperturbed) + try0 = turbFluxGround + exit + case(perturbStateCanair) + turbFluxCanair_dStateCanair = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) + turbFluxCanopy_dStateCanair = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) + turbFluxGround_dStateCanair = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) + latHeatCanEvap_dStateCanair = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) + case(perturbStateCanopy) + turbFluxCanair_dStateCanopy = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) + turbFluxCanopy_dStateCanopy = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) + turbFluxGround_dStateCanopy = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) + latHeatCanEvap_dStateCanopy = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) + case(perturbStateGround) + try1 = turbFluxGround + turbFluxCanair_dStateGround = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) + turbFluxCanopy_dStateGround = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) + turbFluxGround_dStateGround = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) + latHeatCanEvap_dStateGround = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) + case(perturbStateCanWat) + turbFluxCanair_dStateCanWat = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) + turbFluxCanopy_dStateCanWat = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) + turbFluxGround_dStateCanWat = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) + latHeatCanEvap_dStateCanWat = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) + case default; err=10; message=trim(message)//"unknown perturbation"; return + end select ! (type of perturbation) + end if ! (if numerical) + + end do ! (looping through different flux perturbations) + + ! test derivative + !if(ixDerivMethod == numerical) print*, 'try0, try1 = ', try0, try1 + !if(ixDerivMethod == numerical) print*, 'derivative = ', (ixDerivMethod == numerical), (try1 - try0)/dx + !if(ixDerivMethod == analytical) print*, 'derivative = ', (ixDerivMethod == numerical), dTurbFluxGround_dTGround + !pause + + ! compute numerical derivatives + if(ixDerivMethod == numerical)then + ! derivatives w.r.t. canopy air temperature + dTurbFluxCanair_dTCanair = (turbFluxCanair_dStateCanair - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanair = (turbFluxCanopy_dStateCanair - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxGround_dTCanair = (turbFluxGround_dStateCanair - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dLatHeatCanopyEvap_dTCanair = (latHeatCanEvap_dStateCanair - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) + ! derivatives w.r.t. canopy temperature + dTurbFluxCanair_dTCanopy = (turbFluxCanair_dStateCanopy - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanopy = (turbFluxCanopy_dStateCanopy - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxGround_dTCanopy = (turbFluxGround_dStateCanopy - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dLatHeatCanopyEvap_dTCanopy = (latHeatCanEvap_dStateCanopy - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) + ! derivatives w.r.t. ground temperature + dTurbFluxCanair_dTGround = (turbFluxCanair_dStateGround - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxCanopy_dTGround = (turbFluxCanopy_dStateGround - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxGround_dTGround = (turbFluxGround_dStateGround - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + dLatHeatCanopyEvap_dTGround = (latHeatCanEvap_dStateGround - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) + ! derivatives w.r.t. canopy total water content + dTurbFluxCanair_dCanWat = (turbFluxCanair_dStateCanWat - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxCanopy_dCanWat = (turbFluxCanopy_dStateCanWat - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxGround_dCanWat = (turbFluxGround_dStateCanWat - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + dLatHeatCanopyEvap_dCanWat = (latHeatCanEvap_dStateCanWat - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy total water content (J kg-1 s-1) + end if + !if(heightCanopyBottom < scalarSnowDepth+z0Ground) pause 'bottom of the canopy is covered' + + ! test + !print*, (ixDerivMethod == numerical) + !print*, 'dTurbFluxCanair_dTCanair = ', dTurbFluxCanair_dTCanair + !print*, 'dTurbFluxCanair_dTCanopy = ', dTurbFluxCanair_dTCanopy + !print*, 'dTurbFluxCanair_dTGround = ', dTurbFluxCanair_dTGround + !print*, 'dTurbFluxCanopy_dTCanair = ', dTurbFluxCanopy_dTCanair + !print*, 'dTurbFluxCanopy_dTCanopy = ', dTurbFluxCanopy_dTCanopy + !print*, 'dTurbFluxCanopy_dTGround = ', dTurbFluxCanopy_dTGround + !print*, 'dTurbFluxGround_dTCanair = ', dTurbFluxGround_dTCanair + !print*, 'dTurbFluxGround_dTCanopy = ', dTurbFluxGround_dTCanopy + !print*, 'dTurbFluxGround_dTGround = ', dTurbFluxGround_dTGround + !print*, 'dLatHeatCanopyEvap_dCanWat = ', dLatHeatCanopyEvap_dCanWat + !print*, 'dLatHeatCanopyEvap_dTCanair = ', dLatHeatCanopyEvap_dTCanair + !print*, 'dLatHeatCanopyEvap_dTCanopy = ', dLatHeatCanopyEvap_dTCanopy + !print*, 'dLatHeatCanopyEvap_dTGround = ', dLatHeatCanopyEvap_dTGround + !print*, 'dTurbFluxCanair_dCanWat = ', dTurbFluxCanair_dCanWat + !print*, 'dTurbFluxCanopy_dCanWat = ', dTurbFluxCanopy_dCanWat + !print*, 'dTurbFluxGround_dCanWat = ', dTurbFluxGround_dCanWat + !print*, '*****' + !pause + + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + ! ***** ADVECTIVE HEAT FLUXES ************************************************************************************************************************************** + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + + !print*, 'scalarRainfall, scalarThroughfallRain, scalarSnowfall, scalarThroughfallSnow, canopyTempTrial, scalarTwetbulb = ', & + ! scalarRainfall, scalarThroughfallRain, scalarSnowfall, scalarThroughfallSnow, canopyTempTrial, scalarTwetbulb + + ! check the need to compute numerical derivatives + if(ixDerivMethod == numerical)then + nFlux=5 ! compute the derivatives using one-sided finite differences + else + nFlux=1 ! compute analytical derivatives + end if + ! set unperturbed case throughfall rain and derivatives + throughfallRain = scalarThroughfallRain + canopyLiqDrainage = scalarCanopyLiqDrainage + throughfallRainDeriv = scalarThroughfallRainDeriv + canopyLiqDrainageDeriv = scalarCanopyLiqDrainageDeriv + + ! set unperturbed case throughfall snow, derivatives will need to call + throughfallSnow = scalarThroughfallSnow + canopySnowUnloading = scalarCanopySnowUnloading + + ! set unperturbed case total water + totalCanopyWater = canopyLiqTrial + canopyIceTrial + + ! either one or multiple flux calls, depending on if using analytical or numerical derivatives + do itry=nFlux,1,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) + + ! ------------------------------------------------------------------------------------- + ! state perturbations for numerical deriavtives with one-sided finite differences + ! note: no perturbations performed using analytical derivatives (nFlux=1) + ! ------------------------------------------------------------------------------------- + + ! identify the type of perturbation + select case(itry) + + ! un-perturbed case + case(unperturbed) + groundTemp = groundTempTrial + canopyTemp = canopyTempTrial + canairTemp = canairTempTrial + canopyWat = totalCanopyWater + + ! perturb ground temperature + case(perturbStateGround) + groundTemp = groundTempTrial + dx + canopyTemp = canopyTempTrial + canairTemp = canairTempTrial + canopyWat = totalCanopyWater + + ! perturb canopy temperature + case(perturbStateCanopy) + groundTemp = groundTempTrial + canopyTemp = canopyTempTrial + dx + canairTemp = canairTempTrial + canopyWat = totalCanopyWater + + ! perturb canopy air temperature + case(perturbStateCanair) + groundTemp = groundTempTrial + canopyTemp = canopyTempTrial + canairTemp = canairTempTrial + dx + canopyWat = totalCanopyWater + + ! perturb canopy water content + case(perturbStateCanWat) + groundTemp = groundTempTrial + canopyTemp = canopyTempTrial + canairTemp = canairTempTrial + canopyWat = totalCanopyWater + dx + + ! check for an unknown perturbation + case default; err=10; message=trim(message)//"unknown perturbation"; return + + end select ! (type of perturbation) + + ! ------------------------------------------------------------------------------------- + ! calculation block (unperturbed fluxes returned [computed last]) + ! ------------------------------------------------------------------------------------- + + ! recalculate liquid and ice from total water + fracLiquidCanopy = fracliquid(canopyTemp,snowfrz_scale) + canopyLiq = fracLiquidCanopy*canopyWat + canopyIce = (1._rkind - fracLiquidCanopy)*canopyWat + + if(itry == perturbStateCanopy .OR. itry == perturbStateCanWat)then + ! perturbations in canopy total water content through canopy temperature or total water perturbations will affect canopy rain throughfall + call vegLiqFlux(& + ! input + computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + canopyLiq, & ! intent(in): trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) + scalarRainfall, & ! intent(in): rainfall rate (kg m-2 s-1) + ! input-output: data structures + mpar_data, & ! intent(in): model parameters + diag_data, & ! intent(in): local HRU diagnostic model variables + ! output + throughfallRain, & ! intent(out): rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + canopyLiqDrainage, & ! intent(out): drainage of liquid water from the vegetation canopy (kg m-2 s-1) + throughfallRainDeriv, & ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) + canopyLiqDrainageDeriv, & ! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + end if ! (re-computing rain throughfall for perturbed cases) + + if(itry /= perturbStateGround)then !if(itry /= unperturbed .AND. itry /= perturbStateGround)then ! ideally since already called this in coupled_em_module would have saved the derivatives, but we did not + call canopySnow(& + ! input: model control + data_step, & ! intent(in): time step (seconds) + exposedVAI, & ! intent(in): exposed vegetation area index (m2 m-2) + computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + canopyIce, & ! intent(inout): trial mass of ice on the vegetation canopy at the current iteration (kg m-2) + canairTemp, & ! intent(in): temperature of the canopy air space (k) + scalarSnowfall, & ! intent(in): computed snowfall rate (kg m-2 s-1) + canopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) + scalarWindspdCanopyTop, & ! intent(in): windspeed at the top of the canopy (m s-1) + ! input/output: data structures + model_decisions, & ! intent(in): model decisions + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + diag_data, & ! intent(in): model diagnostic variables for a local HRU + ! output + throughfallSnow, & ! intent(out): snow that reaches the ground without ever touching the canopy (kg m-2 s-1) + canopySnowUnloading, & ! intent(out): unloading of snow from the vegetion canopy (kg m-2 s-1) + throughfallSnowDeriv, & ! intent(out): derivative in throughfall w.r.t. canopy ice (s-1) + canopySnowUnloadingDeriv, & ! intent(out): derivative in unloading of snow w.r.t. canopy ice (s-1) + canopySnowUnload_TkDeriv, & ! intent(out): derivative in unloading of snow w.r.t. canopy air temperature (K-1) + err,cmessage) ! intent(out): error control + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + end if ! (re-computing snow throughfall for perturbed cases) + + ! save derivatives, since we did not save them earlier + scalarThroughfallSnowDeriv = throughfallSnowDeriv + scalarCanopySnowUnloadingDeriv = canopySnowUnloadingDeriv + scalarCanopySnowUnload_TkDeriv = canopySnowUnload_TkDeriv + + ! compute the heat advected with precipitation (W m-2) + ! NOTE: fluxes are in kg m-2 s-1, so no need to use density of water/ice here + scalarCanopyAdvectiveHeatFlux = ( -Cp_water*( scalarRainfall - throughfallRain ) - Cp_ice*( scalarSnowfall - throughfallSnow ) ) * ( canopyTemp - scalarTwetbulb ) + scalarGroundAdvectiveHeatFlux = ( -Cp_water*throughfallRain - Cp_ice*throughfallSnow ) * ( groundTemp - scalarTwetbulb ) !+ & + ! ( -Cp_water*canopyLiqDrainage - Cp_ice*canopySnowUnloading ) * ( groundTemp - canopyTemp ) + + ! save perturbed fluxes + if(ixDerivMethod == numerical)then + select case(itry) ! (select type of perturbation) + case(perturbStateGround) + ! only possible for ground advective flux to be changed with ground temperature perturbation + advectFluxGround_dStateGround = scalarGroundAdvectiveHeatFlux + case(perturbStateCanopy) + advectFluxCanopy_dStateCanopy = scalarCanopyAdvectiveHeatFlux + advectFluxGround_dStateCanopy = scalarGroundAdvectiveHeatFlux + case(perturbStateCanair) + ! only possible for ground advective flux to be changed with canopy air temperature perturbation, and currently commented out + advectFluxGround_dStateCanair = scalarGroundAdvectiveHeatFlux + case(perturbStateCanWat) + advectFluxCanopy_dStateCanWat = scalarCanopyAdvectiveHeatFlux + advectFluxGround_dStateCanWat = scalarGroundAdvectiveHeatFlux + case default; err=10; message=trim(message)//"unknown perturbation"; return + end select ! (type of perturbation) + end if ! (if numerical) + + end do ! (looping through different flux perturbations) + + ! ***** + ! compute all derivatives with respect to state variables ... + ! *********************************************************** + + ! analytical throughfall derivatives w.r.t. canopy temperature + dThroughfallRain_dTCanopy = throughfallRainDeriv * dCanLiq_dTcanopy + dCanopyLiqDrainage_dTCanopy = canopyLiqDrainageDeriv * dCanLiq_dTcanopy + dThroughfallSnow_dTCanopy = throughfallSnowDeriv * (-dCanLiq_dTcanopy) ! dCanIce_dTcanopy = -dCanLiq_dTcanopy + dCanopySnowUnloading_dTCanopy = canopySnowUnloadingDeriv * (-dCanLiq_dTcanopy) ! dCanIce_dTcanopy = -dCanLiq_dTcanopy + ! analytical throughfall derivatives w.r.t. canopy total water content + dThroughfallRain_dCanWat = throughfallRainDeriv * fracLiquidCanopy ! dCanLiq_dWat = fracLiquidCanopy + dCanopyLiqDrainage_dCanWat = canopyLiqDrainageDeriv * fracLiquidCanopy ! dCanLiq_dWat = fracLiquidCanopy + dThroughfallSnow_dCanWat = throughfallSnowDeriv * (1._rkind - fracLiquidCanopy) ! dCanIce_dWat = 1._rkind - fracLiquidCanopy + dCanopySnowUnloading_dCanWat = canopySnowUnloadingDeriv * (1._rkind - fracLiquidCanopy) ! dCanIce_dWat = 1._rkind - fracLiquidCanopy + + if(ixDerivMethod==analytical)then ! ** analytical derivatives + ! advection heat derivatives w.r.t. ground temperature + dAdvecFluxGround_dTGround = -Cp_water*throughfallRain - Cp_ice*throughfallSnow !- Cp_water*canopyLiqDrainage - Cp_ice*canopySnowUnloading + ! advection heat derivatives w.r.t. canopy temperature + dAdvecFluxCanopy_dTCanopy = -Cp_water*( scalarRainfall - throughfallRain - dThroughfallRain_dTCanopy*( canopyTemp - scalarTwetbulb ) ) + & + -Cp_ice *( scalarSnowfall - throughfallSnow - dThroughfallSnow_dTCanopy*( canopyTemp - scalarTwetbulb ) ) + dAdvecFluxGround_dTCanopy = (-Cp_water*dThroughfallRain_dTCanopy - Cp_ice*dThroughfallSnow_dTCanopy) *( groundTemp - scalarTwetbulb ) !+ & + ! -Cp_water*( -canopyLiqDrainage + dCanopyLiqDrainage_dTCanopy *( groundTemp - canopyTemp ) ) + & + ! -Cp_ice *( -canopySnowUnloading + dCanopySnowUnloading_dTCanopy*( groundTemp - canopyTemp ) ) + ! advection heat derivatives w.r.t. canopy air temperature + dAdvecFluxGround_dTCanair = 0._rkind ! -Cp_ice*canopySnowUnload_TkDeriv * ( groundTemp - canopyTemp ) + ! advection heat derivatives w.r.t. canopy total water content + dAdvecFluxCanopy_dCanWat = (Cp_water*dThroughfallRain_dCanWat + Cp_ice*dThroughfallSnow_dCanWat) * ( canopyTemp - scalarTwetbulb ) + dAdvecFluxGround_dCanWat = -(Cp_water*dThroughfallRain_dCanWat + Cp_ice*dThroughfallSnow_dCanWat) * ( groundTemp - scalarTwetbulb ) !+ + ! -(Cp_water*dCanopyLiqDrainage_dCanWat + Cp_ice*dCanopySnowUnloading_dCanWat) * ( groundTemp - canopyTemp ) + else ! ** numerical derivatives + ! advection heat derivatives w.r.t. ground temperature + dAdvecFluxGround_dTGround = ( advectFluxGround_dStateGround - scalarGroundAdvectiveHeatFlux ) / dx + ! advection derivatives w.r.t. canopy temperature + dAdvecFluxCanopy_dTCanopy = ( advectFluxCanopy_dStateCanopy - scalarCanopyAdvectiveHeatFlux ) / dx + dAdvecFluxGround_dTCanopy = ( advectFluxGround_dStateCanopy - scalarGroundAdvectiveHeatFlux ) / dx + ! advection derivatives w.r.t. canopy air temperature + dAdvecFluxGround_dTCanair = ( advectFluxGround_dStateCanair - scalarGroundAdvectiveHeatFlux ) / dx + ! advection derivatives w.r.t. canopy total water content + dAdvecFluxCanopy_dCanWat = ( advectFluxCanopy_dStateCanWat - scalarCanopyAdvectiveHeatFlux ) / dx + dAdvecFluxGround_dCanWat = ( advectFluxGround_dStateCanWat - scalarGroundAdvectiveHeatFlux ) / dx + end if ! (type of derivative) + + !print*, 'scalarRainfall, scalarThroughfallRain, scalarSnowfall, scalarThroughfallSnow = ', scalarRainfall, scalarThroughfallRain, scalarSnowfall, scalarThroughfallSnow + !print*, 'scalarCanopyAdvectiveHeatFlux, scalarGroundAdvectiveHeatFlux = ', scalarCanopyAdvectiveHeatFlux, scalarGroundAdvectiveHeatFlux + + ! compute the mass flux associated with transpiration and evaporation/sublimation (J m-2 s-1 --> kg m-2 s-1) + ! NOTE: remove water from the snow on the ground in preference to removing water from the water in soil pores + !print*, 'scalarLatHeatCanopyTrans = ', scalarLatHeatCanopyTrans + !print*, 'scalarLatHeatGround = ', scalarLatHeatGround + ! (canopy transpiration/sublimation) + if(scalarLatHeatSubVapCanopy > LH_vap+verySmall)then ! sublimation + scalarCanopyEvaporation = 0._rkind + scalarCanopySublimation = scalarLatHeatCanopyEvap/LH_sub + if(scalarLatHeatCanopyTrans > 0._rkind)then ! flux directed towards the veg + scalarCanopySublimation = scalarCanopySublimation + scalarLatHeatCanopyTrans/LH_sub ! frost + scalarCanopyTranspiration = 0._rkind + else + scalarCanopyTranspiration = scalarLatHeatCanopyTrans/LH_vap ! transpiration is always vapor + end if + ! (canopy transpiration/evaporation) + else ! evaporation + scalarCanopyEvaporation = scalarLatHeatCanopyEvap/LH_vap + scalarCanopySublimation = 0._rkind + if(scalarLatHeatCanopyTrans > 0._rkind)then ! flux directed towards the veg + scalarCanopyEvaporation = scalarCanopyEvaporation + scalarLatHeatCanopyTrans/LH_vap + scalarCanopyTranspiration = 0._rkind + else + scalarCanopyTranspiration = scalarLatHeatCanopyTrans/LH_vap + end if + end if + ! (ground evaporation/sublimation) + if(scalarLatHeatSubVapGround > LH_vap+verySmall)then ! sublimation + ! NOTE: this should only occur when we have formed snow layers, so check + if(nSnow == 0)then; err=20; message=trim(message)//'only expect snow sublimation when we have formed some snow layers'; return; end if + scalarGroundEvaporation = 0._rkind ! ground evaporation is zero once the snowpack has formed + scalarSnowSublimation = scalarLatHeatGround/LH_sub + else + ! NOTE: this should only occur when we have no snow layers, so check + if(nSnow > 0)then; err=20; message=trim(message)//'only expect ground evaporation when there are no snow layers'; return; end if + scalarGroundEvaporation = scalarLatHeatGround/LH_vap + scalarSnowSublimation = 0._rkind ! no sublimation from snow if no snow layers have formed + end if + !print*, 'scalarSnowSublimation, scalarLatHeatGround = ', scalarSnowSublimation, scalarLatHeatGround + + !print*, 'canopyWetFraction, scalarCanopyEvaporation = ', canopyWetFraction, scalarCanopyEvaporation + + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + ! ***** AND STITCH EVERYTHING TOGETHER ***************************************************************************************************************************** + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + + ! compute derived fluxes + scalarTotalET = scalarGroundEvaporation + scalarCanopyEvaporation + scalarCanopyTranspiration + scalarNetRadiation = scalarCanopyAbsorbedSolar + scalarLWNetCanopy + scalarGroundAbsorbedSolar + scalarLWNetGround + + ! compute net fluxes at the canopy and ground surface + canairNetFlux = turbFluxCanair + canopyNetFlux = scalarCanopyAbsorbedSolar + scalarLWNetCanopy + turbFluxCanopy + scalarCanopyAdvectiveHeatFlux + groundNetFlux = scalarGroundAbsorbedSolar + scalarLWNetGround + turbFluxGround + scalarGroundAdvectiveHeatFlux + !write(*,'(a,1x,10(e17.10,1x))') 'canopyNetFlux, groundNetFlux, scalarLWNetCanopy, turbFluxCanopy, turbFluxGround, scalarLWNetGround, scalarCanopyAdvectiveHeatFlux = ', & + ! canopyNetFlux, groundNetFlux, scalarLWNetCanopy, turbFluxCanopy, turbFluxGround, scalarLWNetGround, scalarCanopyAdvectiveHeatFlux + !write(*,'(a,1x,10(e20.14,1x))') 'groundNetFlux, scalarGroundAbsorbedSolar, scalarLWNetGround, turbFluxGround, scalarGroundAdvectiveHeatFlux = ', & + ! groundNetFlux, scalarGroundAbsorbedSolar, scalarLWNetGround, turbFluxGround, scalarGroundAdvectiveHeatFlux + + ! compute the energy derivatives + dCanairNetFlux_dCanairTemp = dTurbFluxCanair_dTCanair + dCanairNetFlux_dCanopyTemp = dTurbFluxCanair_dTCanopy + dCanairNetFlux_dGroundTemp = dTurbFluxCanair_dTGround + dCanopyNetFlux_dCanairTemp = dTurbFluxCanopy_dTCanair + dCanopyNetFlux_dCanopyTemp = dLWNetCanopy_dTCanopy + dTurbFluxCanopy_dTCanopy + dAdvecFluxCanopy_dTCanopy + dCanopyNetFlux_dGroundTemp = dLWNetCanopy_dTGround + dTurbFluxCanopy_dTGround + dGroundNetFlux_dCanairTemp = dTurbFluxGround_dTCanair + dAdvecFluxGround_dTCanair + dGroundNetFlux_dCanopyTemp = dLWNetGround_dTCanopy + dTurbFluxGround_dTCanopy + dAdvecFluxGround_dTCanopy + dGroundNetFlux_dGroundTemp = dLWNetGround_dTGround + dTurbFluxGround_dTGround + dAdvecFluxGround_dTGround + + ! check if evaporation or sublimation + if(scalarLatHeatSubVapCanopy < LH_vap+verySmall)then ! evaporation + + ! compute the liquid water derivarives + dCanopyEvaporation_dCanWat = dLatHeatCanopyEvap_dCanWat/LH_vap ! (s-1) + dCanopyEvaporation_dTCanair = dLatHeatCanopyEvap_dTCanair/LH_vap ! (kg m-2 s-1 K-1) + dCanopyEvaporation_dTCanopy = dLatHeatCanopyEvap_dTCanopy/LH_vap ! (kg m-2 s-1 K-1) + dCanopyEvaporation_dTGround = dLatHeatCanopyEvap_dTGround/LH_vap ! (kg m-2 s-1 K-1) + + ! sublimation + else + dCanopyEvaporation_dCanWat = 0._rkind ! (s-1) + dCanopyEvaporation_dTCanair = 0._rkind ! (kg m-2 s-1 K-1) + dCanopyEvaporation_dTCanopy = 0._rkind ! (kg m-2 s-1 K-1) + dCanopyEvaporation_dTGround = 0._rkind ! (kg m-2 s-1 K-1) + end if + + ! compute the liquid water derivarives (ground evap) + dGroundEvaporation_dCanWat = dLatHeatGroundEvap_dCanWat/LH_vap ! (s-1) + dGroundEvaporation_dTCanair = dLatHeatGroundEvap_dTCanair/LH_vap ! (kg m-2 s-1 K-1) + dGroundEvaporation_dTCanopy = dLatHeatGroundEvap_dTCanopy/LH_vap ! (kg m-2 s-1 K-1) + dGroundEvaporation_dTGround = dLatHeatGroundEvap_dTGround/LH_vap ! (kg m-2 s-1 K-1) + + ! compute the cross derivative terms (turbulent fluxes and advective fluxes) + dCanopyNetFlux_dCanWat = dTurbFluxCanopy_dCanWat + dAdvecFluxCanopy_dCanWat ! (J kg-1 s-1) + dGroundNetFlux_dCanWat = dTurbFluxGround_dCanWat + dAdvecFluxGround_dCanWat ! (J kg-1 s-1) + + !print*, (ixDerivMethod == numerical) + !print*, 'dGroundNetFlux_dCanairTemp = ', dGroundNetFlux_dCanairTemp + !print*, 'dCanopyNetFlux_dCanopyTemp = ', dCanopyNetFlux_dCanopyTemp + !print*, 'dGroundNetFlux_dCanopyTemp = ', dGroundNetFlux_dCanopyTemp + !print*, 'dCanopyNetFlux_dGroundTemp = ', dCanopyNetFlux_dGroundTemp + !print*, 'dLWNetCanopy_dTGround = ', dLWNetCanopy_dTGround + !print*, 'dTurbFluxCanopy_dTGround = ', dTurbFluxCanopy_dTGround + !pause + + ! * check + case default; err=10; message=trim(message)//'unable to identify upper boundary condition for thermodynamics'; return + + ! end case statement + end select ! upper boundary condition for thermodynamics + + ! return liquid fluxes (needed for coupling) + returnCanopyTranspiration = scalarCanopyTranspiration ! canopy transpiration (kg m-2 s-1) + returnCanopyEvaporation = scalarCanopyEvaporation ! canopy evaporation/condensation (kg m-2 s-1) + returnGroundEvaporation = scalarGroundEvaporation ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + + ! end associations + end associate + + + end subroutine vegNrgFluxSundials + + ! ******************************************************************************************************* ! public subroutine wettedFrac: compute wetted fraction of the canopy ! ******************************************************************************************************* @@ -2606,19 +4278,19 @@ subroutine turbFluxes(& dTurbFluxGround_dTCanopy, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) dTurbFluxGround_dTGround, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) ! output: liquid flux derivatives (canopy evap) - dLatHeatCanopyEvap_dCanLiq, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (J kg-1 s-1) + dLatHeatCanopyEvap_dCanWat, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy total water content (J kg-1 s-1) dLatHeatCanopyEvap_dTCanair, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) dLatHeatCanopyEvap_dTCanopy, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) dLatHeatCanopyEvap_dTGround, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) ! output: liquid flux derivatives (ground evap) - dLatHeatGroundEvap_dCanLiq, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy liquid water content (J kg-1 s-1) + dLatHeatGroundEvap_dCanWat, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) dLatHeatGroundEvap_dTCanair, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy air temperature dLatHeatGroundEvap_dTCanopy, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy temperature dLatHeatGroundEvap_dTGround, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. ground temperature ! output: cross derivatives - dTurbFluxCanair_dCanLiq, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dTurbFluxCanopy_dCanLiq, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dTurbFluxGround_dCanLiq, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dTurbFluxCanair_dCanWat, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxCanopy_dCanWat, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxGround_dCanWat, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) ! output: error control err,message ) ! intent(out): error control ! ----------------------------------------------------------------------------------------------------------------------------------------- @@ -2698,19 +4370,19 @@ subroutine turbFluxes(& real(rkind),intent(out) :: dTurbFluxGround_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) real(rkind),intent(out) :: dTurbFluxGround_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) ! output: liquid flux derivatives (canopy evap) - real(rkind),intent(out) :: dLatHeatCanopyEvap_dCanLiq ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (W kg-1) + real(rkind),intent(out) :: dLatHeatCanopyEvap_dCanWat ! derivative in latent heat of canopy evaporation w.r.t. canopy total water content (W kg-1) real(rkind),intent(out) :: dLatHeatCanopyEvap_dTCanair ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) real(rkind),intent(out) :: dLatHeatCanopyEvap_dTCanopy ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) real(rkind),intent(out) :: dLatHeatCanopyEvap_dTGround ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) ! output: liquid flux derivatives (ground evap) - real(rkind),intent(out) :: dLatHeatGroundEvap_dCanLiq ! derivative in latent heat of ground evaporation w.r.t. canopy liquid water content (J kg-1 s-1) + real(rkind),intent(out) :: dLatHeatGroundEvap_dCanWat ! derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) real(rkind),intent(out) :: dLatHeatGroundEvap_dTCanair ! derivative in latent heat of ground evaporation w.r.t. canopy air temperature (W m-2 K-1) real(rkind),intent(out) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) real(rkind),intent(out) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) ! output: cross derivatives - real(rkind),intent(out) :: dTurbFluxCanair_dCanLiq ! derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - real(rkind),intent(out) :: dTurbFluxCanopy_dCanLiq ! derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - real(rkind),intent(out) :: dTurbFluxGround_dCanLiq ! derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + real(rkind),intent(out) :: dTurbFluxCanair_dCanWat ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) + real(rkind),intent(out) :: dTurbFluxCanopy_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + real(rkind),intent(out) :: dTurbFluxGround_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -2740,7 +4412,7 @@ subroutine turbFluxes(& real(rkind) :: dVPCanopyAir_dTCanopy ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the canopy real(rkind) :: dVPCanopyAir_dTGround ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the ground real(rkind) :: dVPCanopyAir_dWetFrac ! derivative of vapor pressure in the canopy air space w.r.t. wetted fraction of the canopy - real(rkind) :: dVPCanopyAir_dCanLiq ! derivative of vapor pressure in the canopy air space w.r.t. canopy liquid water content + real(rkind) :: dVPCanopyAir_dCanWat ! derivative of vapor pressure in the canopy air space w.r.t. canopy total water content ! local variables -- sensible heat flux derivatives real(rkind) :: dSenHeatTotal_dTCanair ! derivative in the total sensible heat flux w.r.t. canopy air temperature real(rkind) :: dSenHeatTotal_dTCanopy ! derivative in the total sensible heat flux w.r.t. canopy air temperature @@ -2758,7 +4430,7 @@ subroutine turbFluxes(& ! local variables -- wetted fraction derivatives real(rkind) :: dLatHeatCanopyEvap_dWetFrac ! derivative in the latent heat of canopy evaporation w.r.t. canopy wet fraction (W m-2) real(rkind) :: dLatHeatCanopyTrans_dWetFrac ! derivative in the latent heat of canopy transpiration w.r.t. canopy wet fraction (W m-2) - real(rkind) :: dLatHeatCanopyTrans_dCanLiq ! derivative in the latent heat of canopy transpiration w.r.t. canopy liquid water (J kg-1 s-1) + real(rkind) :: dLatHeatCanopyTrans_dCanWat ! derivative in the latent heat of canopy transpiration w.r.t. canopy liquid water (J kg-1 s-1) ! ----------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='turbFluxes/' @@ -2929,9 +4601,9 @@ subroutine turbFluxes(& dPart1 = (leafConductance - leafConductanceTr)*satVP_CanopyTemp dPart2 = -(leafConductance - leafConductanceTr)/(totalConductanceLH**2._rkind) dVPCanopyAir_dWetFrac = dPart1/totalConductanceLH + fPart_VP*dPart2 - dVPCanopyAir_dCanLiq = dVPCanopyAir_dWetFrac*dCanopyWetFraction_dWat - !write(*,'(a,5(f20.8,1x))') 'dVPCanopyAir_dTCanair, dVPCanopyAir_dTCanopy, dVPCanopyAir_dTGround, dVPCanopyAir_dWetFrac, dVPCanopyAir_dCanLiq = ', & - ! dVPCanopyAir_dTCanair, dVPCanopyAir_dTCanopy, dVPCanopyAir_dTGround, dVPCanopyAir_dWetFrac, dVPCanopyAir_dCanLiq + dVPCanopyAir_dCanWat = dVPCanopyAir_dWetFrac*dCanopyWetFraction_dWat + !write(*,'(a,5(f20.8,1x))') 'dVPCanopyAir_dTCanair, dVPCanopyAir_dTCanopy, dVPCanopyAir_dTGround, dVPCanopyAir_dWetFrac, dVPCanopyAir_dCanWat = ', & + ! dVPCanopyAir_dTCanair, dVPCanopyAir_dTCanopy, dVPCanopyAir_dTGround, dVPCanopyAir_dWetFrac, dVPCanopyAir_dCanWat ! sensible heat from the canopy to the atmosphere dSenHeatTotal_dTCanair = -volHeatCapacityAir*canopyConductance - volHeatCapacityAir*dCanopyCond_dCanairTemp*(canairTemp - airtemp) @@ -2962,7 +4634,7 @@ subroutine turbFluxes(& dPart2 = dSVPCanopy_dCanopyTemp - dVPCanopyAir_dTCanopy ! (derivatives) dLatHeatCanopyEvap_dTCanair = fPart1*(-dVPCanopyAir_dTCanair) - dLatHeatCanopyEvap_dTCanopy = fPart1*dpart2 + fPart2*dPart1 + dLatHeatCanopyEvap_dTCanopy = fPart1*dPart2 + fPart2*dPart1 dLatHeatCanopyEvap_dTGround = fPart1*(-dVPCanopyAir_dTGround) !write(*,'(a,3(f20.8,1x))') 'dLatHeatCanopyEvap_dTCanair, dLatHeatCanopyEvap_dTCanopy, dLatHeatCanopyEvap_dTGround = ', & ! dLatHeatCanopyEvap_dTCanair, dLatHeatCanopyEvap_dTCanopy, dLatHeatCanopyEvap_dTGround @@ -2999,8 +4671,8 @@ subroutine turbFluxes(& !print*, 'dLatHeatCanopyTrans_dWetFrac = ', dLatHeatCanopyTrans_dWetFrac ! latent heat associated with canopy transpiration w.r.t. canopy liquid water - dLatHeatCanopyTrans_dCanLiq = dLatHeatCanopyTrans_dWetFrac*dCanopyWetFraction_dWat ! (J s-1 kg-1) - !print*, 'dLatHeatCanopyTrans_dCanLiq = ', dLatHeatCanopyTrans_dCanLiq + dLatHeatCanopyTrans_dCanWat = dLatHeatCanopyTrans_dWetFrac*dCanopyWetFraction_dWat ! (J s-1 kg-1) + !print*, 'dLatHeatCanopyTrans_dCanWat = ', dLatHeatCanopyTrans_dCanWat else ! canopy is undefined @@ -3020,9 +4692,9 @@ subroutine turbFluxes(& ! set derivatives for wetted area and canopy transpiration to zero (no canopy, so fluxes are undefined) dLatHeatCanopyEvap_dWetFrac = 0._rkind - dLatHeatCanopyEvap_dCanLiq = 0._rkind - dLatHeatCanopyTrans_dCanLiq = 0._rkind - dVPCanopyAir_dCanLiq = 0._rkind + dLatHeatCanopyEvap_dCanWat = 0._rkind + dLatHeatCanopyTrans_dCanWat = 0._rkind + dVPCanopyAir_dCanWat = 0._rkind ! set derivatives for ground fluxes w.r.t canopy temperature to zero (no canopy, so fluxes are undefined) dSenHeatGround_dTCanair = 0._rkind @@ -3066,12 +4738,12 @@ subroutine turbFluxes(& dTurbFluxGround_dTCanopy = dSenHeatGround_dTCanopy + dLatHeatGroundEvap_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) dTurbFluxGround_dTGround = dSenHeatGround_dTGround + dLatHeatGroundEvap_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) ! (liquid water derivatives) - dLatHeatCanopyEvap_dCanLiq = dLatHeatCanopyEvap_dWetFrac*dCanopyWetFraction_dWat ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water (W kg-1) - dLatHeatGroundEvap_dCanLiq = latHeatSubVapGround*latentHeatConstant*groundConductanceLH*dVPCanopyAir_dCanLiq ! derivative in latent heat of ground evaporation w.r.t. canopy liquid water (J kg-1 s-1) + dLatHeatCanopyEvap_dCanWat = dLatHeatCanopyEvap_dWetFrac*dCanopyWetFraction_dWat ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water (W kg-1) + dLatHeatGroundEvap_dCanWat = latHeatSubVapGround*latentHeatConstant*groundConductanceLH*dVPCanopyAir_dCanWat ! derivative in latent heat of ground evaporation w.r.t. canopy liquid water (J kg-1 s-1) ! (cross deriavtives) - dTurbFluxCanair_dCanLiq = 0._rkind ! derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dTurbFluxCanopy_dCanLiq = dLatHeatCanopyEvap_dCanLiq + dLatHeatCanopyTrans_dCanLiq ! derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dTurbFluxGround_dCanLiq = dLatHeatGroundEvap_dCanLiq ! derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dTurbFluxCanair_dCanWat = 0._rkind ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxCanopy_dCanWat = dLatHeatCanopyEvap_dCanWat + dLatHeatCanopyTrans_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxGround_dCanWat = dLatHeatGroundEvap_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) else ! (just make sure we return something) ! (energy derivatives) dTurbFluxCanair_dTCanair = 0._rkind @@ -3084,12 +4756,12 @@ subroutine turbFluxes(& dTurbFluxGround_dTCanopy = 0._rkind dTurbFluxGround_dTGround = 0._rkind ! (liquid water derivatives) - dLatHeatCanopyEvap_dCanLiq = 0._rkind - dLatHeatGroundEvap_dCanLiq = 0._rkind + dLatHeatCanopyEvap_dCanWat = 0._rkind + dLatHeatGroundEvap_dCanWat = 0._rkind ! (cross deriavtives) - dTurbFluxCanair_dCanLiq = 0._rkind - dTurbFluxCanopy_dCanLiq = 0._rkind - dTurbFluxGround_dCanLiq = 0._rkind + dTurbFluxCanair_dCanWat = 0._rkind + dTurbFluxCanopy_dCanWat = 0._rkind + dTurbFluxGround_dCanWat = 0._rkind end if end subroutine turbFluxes From 99591ea24c35182964977e8b2456139f9142d017 Mon Sep 17 00:00:00 2001 From: ashleymedin <34691332+ashleymedin@users.noreply.github.com> Date: Thu, 3 Mar 2022 09:49:45 +0900 Subject: [PATCH 0244/1472] minor changes, in process of rolling back the vegNrgFlux changes I made and should have not --- build/source/engine/eval8summa.f90 | 468 ++++++++++++++++++++- build/source/engine/soilLiqFlx.f90 | 12 +- build/source/engine/soil_utilsSundials.f90 | 2 +- build/source/engine/ssdNrgFlux.f90 | 121 +++--- build/source/engine/systemSolvSundials.f90 | 4 +- build/source/engine/updateVars4JacDAE.f90 | 10 +- 6 files changed, 552 insertions(+), 65 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index cc3927b93..59eea70e0 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -98,6 +98,7 @@ module eval8summa_module implicit none private public::eval8summa +public::eval8summaSundials contains @@ -394,7 +395,7 @@ subroutine eval8summa(& ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - + ! compute enthalpy (J m-3) if(needEnthalpy)then call t2enthalpy(& @@ -561,4 +562,469 @@ subroutine eval8summa(& end associate end subroutine eval8summa + + + ! ********************************************************************************************************** + ! public subroutine eval8summaSundials: compute the residual vector and the Jacobian matrix + ! ********************************************************************************************************** + subroutine eval8summaSundials(& + ! input: model control + dt, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + nState, & ! intent(in): total number of state variables + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVecTrial, & ! intent(in): model state vector + fScale, & ! intent(in): function scaling vector + sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + indx_data, & ! intent(inout): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + ! output: flux and residual vectors + feasible, & ! intent(out): flag to denote the feasibility of the solution + fluxVec, & ! intent(out): flux vector + resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + resVec, & ! intent(out): residual vector + fEval, & ! intent(out): function evaluation + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- + ! provide access to subroutines + USE getVectorz_module, only:varExtract ! extract variables from the state vector + USE updateVars_module, only:updateVars ! update prognostic variables + USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy + USE computFlux_module, only:soilCmpres ! compute soil compression, use non-sundials version because sundials version needs mLayerMatricHeadPrime + USE computFlux_module, only:computFluxSundials ! compute fluxes given a state vector + USE computResid_module,only:computResid ! compute residuals given a state vector + implicit none + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + ! input: model control + real(rkind),intent(in) :: dt ! length of the time step (seconds) + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + integer(i4b),intent(in) :: nState ! total number of state variables + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + ! input: state vectors + real(rkind),intent(in) :: stateVecTrial(:) ! model state vector + real(rkind),intent(in) :: fScale(:) ! function scaling vector + real(rkind),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + ! input: data structures + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup), intent(in) :: lookup_data ! lookup tables + type(var_i), intent(in) :: type_data ! type of vegetation and soil + type(var_d), intent(in) :: attr_data ! spatial attributes + type(var_dlength), intent(in) :: mpar_data ! model parameters + type(var_d), intent(in) :: forc_data ! model forcing data + type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin + type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU + ! output: data structures + type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + ! output: flux and residual vectors + logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution + real(rkind),intent(out) :: fluxVec(:) ! flux vector + real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation + real(rkind),intent(out) :: resVec(:) ! NOTE: qp ! residual vector + real(rkind),intent(out) :: fEval ! function evaluation + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + ! -------------------------------------------------------------------------------------------------------------------------------- + ! state variables + real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial value for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial value for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial value for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) + real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + ! diagnostic variables + logical(lgt),parameter :: needEnthalpy=.true. ! flag to compute enthalpy + real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) + ! other local variables + integer(i4b) :: iLayer ! index of model layer in the snow+soil domain + integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain + integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine + integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) + real(rkind) :: xMin,xMax ! minimum and maximum values for water content + real(rkind) :: scalarCanopyHydTrial ! trial value for mass of water on the vegetation canopy (kg m-2) + real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) + real(rkind),dimension(nLayers) :: mLayerVolFracHydTrial ! trial value for volumetric fraction of water (-), general vector merged from Wat and Liq + real(rkind),dimension(nState) :: rVecScaled ! scaled residual vector + character(LEN=256) :: cmessage ! error message of downwind routine + ! -------------------------------------------------------------------------------------------------------------------------------- + ! association to variables in the data structures + ! -------------------------------------------------------------------------------------------------------------------------------- + associate(& + ! model decisions + ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ! soil parameters + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) + ! canopy and layer depth + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! model state variables + scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) + ! model diagnostic variables + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) +! enthalpy + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) + ! soil compression + scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in storage associated with compression of the soil matrix (-) + ! derivatives + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) + ! mapping + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) + ! indices + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable (nrg) + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable (nrg) + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain + layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) + ) ! association to variables in the data structures + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="eval8summaSundials/" + + ! check the feasibility of the solution + feasible=.true. + + ! check that the canopy air space temperature is reasonable + if(ixCasNrg/=integerMissing)then + if(stateVecTrial(ixCasNrg) > canopyTempMax) feasible=.false. + endif + + ! check that the canopy air space temperature is reasonable + if(ixVegNrg/=integerMissing)then + if(stateVecTrial(ixVegNrg) > canopyTempMax) feasible=.false. + endif + + ! check canopy liquid water is not negative + if(ixVegHyd/=integerMissing)then + if(stateVecTrial(ixVegHyd) < 0._rkind) feasible=.false. + end if + + ! check snow temperature is below freezing + if(count(ixSnowOnlyNrg/=integerMissing)>0)then + if(any(stateVecTrial( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. + endif + + ! loop through non-missing hydrology state variables in the snow+soil domain + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) + + ! check the minimum and maximum water constraints + if(ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_liqLayer)then + + ! --> minimum + if (layerType(iLayer) == iname_soil) then + xMin = theta_sat(iLayer-nSnow) + else + xMin = 0._rkind + endif + + ! --> maximum + select case( layerType(iLayer) ) + case(iname_snow); xMax = merge(iden_ice, 1._rkind - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) + case(iname_soil); xMax = merge(theta_sat(iLayer-nSnow), theta_sat(iLayer-nSnow) - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) + end select + + ! --> check + if(stateVecTrial( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVecTrial( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. + !if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax + + endif ! if water states + + end do ! loop through non-missing hydrology state variables in the snow+soil domain + + ! early return for non-feasible solutions + if(.not.feasible)then + fluxVec(:) = realMissing + resVec(:) = quadMissing + fEval = realMissing + return + end if + + ! get the start and end indices for the soil compression calculations + if(scalarSolution)then + jState = pack(ixControlVolume, ixMapFull2Subset/=integerMissing) + ixBeg = jState(1) + ixEnd = jState(1) + else + ixBeg = 1 + ixEnd = nSoil + endif + + ! extract variables from the model state vector + call varExtract(& + ! input + stateVecTrial, & ! intent(in): model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(out): trial value of canopy ice content (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) + ! output: variables for the aquifer + scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! update diagnostic variables + call updateVars(& + ! input + .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + lookup_data, & ! intent(in): lookup tables for a local HRU + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output: variables for the vegetation canopy + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! compute enthalpy (J m-3) + if(needEnthalpy)then + call t2enthalpy(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) + ! output: enthalpy + scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + endif ! if computing enthalpy + + ! print the states in the canopy domain + !print*, 'dt = ', dt + !write(*,'(a,1x,10(f20.10,1x))') 'scalarCanopyTempTrial = ', scalarCanopyTempTrial + !write(*,'(a,1x,10(f20.10,1x))') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + !write(*,'(a,1x,10(f20.10,1x))') 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial + !write(*,'(a,1x,10(f20.10,1x))') 'scalarCanopyIceTrial = ', scalarCanopyIceTrial + + ! print the states in the snow+soil domain + !write(*,'(a,1x,10(f20.10,1x))') 'mLayerTempTrial = ', mLayerTempTrial(iJac1:min(nLayers,iJac2)) + !write(*,'(a,1x,10(f20.10,1x))') 'mLayerVolFracWatTrial = ', mLayerVolFracWatTrial(iJac1:min(nLayers,iJac2)) + !write(*,'(a,1x,10(f20.10,1x))') 'mLayerVolFracLiqTrial = ', mLayerVolFracLiqTrial(iJac1:min(nLayers,iJac2)) + !write(*,'(a,1x,10(f20.10,1x))') 'mLayerVolFracIceTrial = ', mLayerVolFracIceTrial(iJac1:min(nLayers,iJac2)) + !write(*,'(a,1x,10(f20.10,1x))') 'mLayerMatricHeadTrial = ', mLayerMatricHeadTrial(iJac1:min(nSoil,iJac2)) + !write(*,'(a,1x,10(f20.10,1x))') 'mLayerMatricHeadLiqTrial = ', mLayerMatricHeadLiqTrial(iJac1:min(nSoil,iJac2)) + + ! print the water content + if(globalPrintFlag)then + if(iJac1 model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): method used to calculate flux derivatives ix_bcUpprTdyn => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision, & ! intent(in): method used to calculate the upper boundary condition for thermodynamics ix_bcLowrTdyn => model_decisions(iLookDECISIONS%bcLowrTdyn)%iDecision, & ! intent(in): method used to calculate the lower boundary condition for thermodynamics + ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision, & ! intent(in): index of the form of Richards' equation ixThCondSnow => model_decisions(iLookDECISIONS%thCondSnow)%iDecision, & ! intent(in): choice of method for thermal conductivity of snow ixThCondSoil => model_decisions(iLookDECISIONS%thCondSoil)%iDecision, & ! intent(in): choice of method for thermal conductivity of soil ! input: model coordinates @@ -664,6 +665,7 @@ subroutine ssdNrgFluxSundials(& call iLayerThermalConduct(& ! input: model control valueMissing, & ! intent(in): missing value + ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) ixThCondSnow, & ! intent(in): choice of method for thermal conductivity of snow ixThCondSoil, & ! intent(in): choice of method for thermal conductivity of soil ! input: coordinate variables @@ -824,6 +826,7 @@ end subroutine ssdNrgFluxSundials subroutine iLayerThermalConduct(& ! input: model control valueMissing, & ! intent(in): missing value + ixRichards, & ! intent(in): choice of option for Richards' equation ixThCondSnow, & ! intent(in): choice of method for thermal conductivity of snow ixThCondSoil, & ! intent(in): choice of method for thermal conductivity of soil ! input: coordinate variables @@ -832,7 +835,7 @@ subroutine iLayerThermalConduct(& nSnow, & ! intent(in): number of snow layers layerType, & ! intent(in): layer type (iname_soil or iname_snow) ! input: state variables (adjacent layers) - nodeMatricHeadTrial, & ! intent(in): matric head at the nodes (m) + nodeMatricHeadTrial0, & ! intent(in): matric head at the nodes (m) nodeVolFracLiqTrial0, & ! intent(inout): volumetric liquid water content at the nodes (m) nodeTempTrial, & ! intent(in): temperature at the nodes (m) ! input: model coordinate variables (adjacent layers) @@ -841,7 +844,7 @@ subroutine iLayerThermalConduct(& ! input: soil parameters at nodes vGn_alpha, & ! intent(in): van Genutchen "alpha" parameter (m-1) vGn_n, & ! intent(in): van Genutchen "n" parameter (-) - VGn_m, & ! intent(in): van Genutchen "m" parameter (-) + vGn_m, & ! intent(in): van Genutchen "m" parameter (-) theta_sat, & ! intent(in): soil porosity (-) theta_res, & ! intent(in): soil residual volumetric water content (-) iden_soil, & !intrinsic density of soil (kg m-3) @@ -869,15 +872,18 @@ subroutine iLayerThermalConduct(& USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water USE soil_utils_module,only:dTheta_dPsi ! derivative in the soil water characteristic (soil) USE soil_utils_module,only:dTheta_dTk ! differentiate the freezing curve w.r.t. temperature (soil) + USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content ! constants USE multiconst, only: gravity, & ! gravitational acceleration (m s-1) Tfreeze, & ! freezing point of water (K) iden_water,iden_ice,& ! intrinsic density of water and ice (kg m-3) LH_fus ! latent heat of fusion (J kg-1) + implicit none ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: model control real(rkind),intent(in) :: valueMissing ! intent(in): missing value + integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation integer(i4b),intent(in) :: ixThCondSnow ! intent(in): choice of method for thermal conductivity of snow integer(i4b),intent(in) :: ixThCondSoil ! intent(in): choice of method for thermal conductivity of soil ! input: coordinate variables @@ -886,7 +892,7 @@ subroutine iLayerThermalConduct(& integer(i4b),intent(in) :: nSnow ! intent(in): number of snow layers integer(i4b),intent(in) :: layerType(:) ! intent(in): layer type (iname_soil or iname_snow) ! input: state variables - real(rkind),intent(in) :: nodeMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind),intent(in) :: nodeMatricHeadTrial0(:) ! trial vector of total water matric potential (m) real(rkind),intent(in) :: nodeVolFracLiqTrial0(:) ! trial vector of volumetric liquid water content, recomputed with perturbed water state(-) real(rkind),intent(in) :: nodeTempTrial(:) ! trial vector of temperature (K) ! input: model coordinate variables @@ -922,7 +928,7 @@ subroutine iLayerThermalConduct(& integer(i4b),parameter :: ixLower=2 ! index of lower node in the 2-element vectors character(LEN=256) :: cmessage ! error message of downwind routine integer(i4b) :: iLayer ! index of model layer - real(rkind) :: matricHead ! matric head for frozen soil + real(rkind) :: matricFHead ! matric head for frozen soil real(rkind) :: TCn ! thermal conductivity below the layer interface (W m-1 K-1) real(rkind) :: TCp ! thermal conductivity above the layer interface (W m-1 K-1) real(rkind) :: zdn ! height difference between interface and lower value (m) @@ -938,17 +944,18 @@ subroutine iLayerThermalConduct(& real(rkind) :: mLayerdThermalC_dNrg(2) ! derivative in thermal conductivity w.r.t. temperature real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) real(rkind) :: fLiq ! fraction of liquid water (-) - real(rkind) :: dlambda_wet_dPsi0 ! derivative in thermal conductivity of wet material w.r.t. matric head + real(rkind) :: dlambda_wet_dWat ! derivative in thermal conductivity of wet material w.r.t.soil water state variable real(rkind) :: dlambda_wet_dTk ! derivative in thermal conductivity of wet material w.r.t. temperature - real(rkind) :: dkerstenNum_dPsi0 ! derivative in Kersten number w.r.t. matric head + real(rkind) :: dkerstenNum_dWat ! derivative in Kersten number w.r.t. soil water state variable + real(rkind) :: nodeMatricHeadTrial(2) ! trial vector of matric head, recomputed from input if perturbed water state (-) real(rkind) :: nodeVolTotWatTrial(2) ! trial vector of volumetric total water content (-) real(rkind) :: nodeVolFracLiqTrial(2) ! trial vector of volumetric liquid water content, recomputed from input if perturbed water state (-) real(rkind) :: nodeVolFracIceTrial(2) ! trial vector of volumetric ice water content (-) real(rkind) :: nodeVolFracAirTrial(2) ! trial vector of volumetric air water content (-) real(rkind) :: mLayerThermalC(2) ! thermal conductivity of each layer (W m-1 K-1) - real(rkind) :: mLayerdVolFracLiq_dPsi0 ! derivative in water retention curve above critical temp w.r.t. matric head - real(rkind) :: mLayerdVolFracLiq_dTk ! derivative in water retention curve below critical temp w.r.t. temperature - real(rkind) :: mLayerdVolFracIce_dTheta ! derivative in vol fraction of ice w.r.t. volumetric liquid water + real(rkind) :: mLayerdVolFracLiq_dWat ! derivative in vol fraction of liquid w.r.t. water state variable + real(rkind) :: mLayerdVolFracIce_dWat ! derivative in vol fraction of ice w.r.t. water state variable + real(rkind) :: mLayerdVolFracLiq_dTk ! derivative in vol fraction of liquid w.r.t. temperature real(rkind) :: mLayerdVolFracIce_dTk ! derivative in vol fraction of ice w.r.t. temperature ! local variables to reproduce the thermal conductivity of Hansson et al. VZJ 2005 real(rkind),parameter :: c1=0.55_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) @@ -959,7 +966,7 @@ subroutine iLayerThermalConduct(& real(rkind),parameter :: f1=13.05_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) real(rkind),parameter :: f2=1.06_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) real(rkind) :: fArg,xArg ! temporary variables (see Hansson et al. VZJ 2005 for details) - real(rkind) :: dxArg_dPsi0,dxArg_dTk ! derivates of the temporary variables with respect to matric head and temperature + real(rkind) :: dxArg_dWat,dxArg_dTk ! derivates of the temporary variables with respect to soil water state variable and temperature ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -982,13 +989,28 @@ subroutine iLayerThermalConduct(& select case(layerType(iLayer)) case(iname_soil) Tcrit = crit_soilT(nodeMatricHeadTrial(iLayer) ) - ! do we also need to consider ixRichards form? FIX - if( nodeTempTrial(iLayer) < Tcrit) then !if do not perturb temperature, this should not change, but nodeMatricHeadTrial will have changed - matricHead = (LH_fus/gravity)*(nodeTempTrial(iLayer) - Tfreeze)/Tfreeze - nodeVolFracLiqTrial(iLayer) = volFracLiq(matricHead,theta_res(iLayer),theta_sat(iLayer),vGn_alpha(iLayer),vGn_n(iLayer),vGn_m(iLayer)) - else - nodeVolFracLiqTrial(iLayer) = volFracLiq(nodeMatricHeadTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) - endif + ! is ixRichards form right? FIX + select case(ixRichards) ! (form of Richards' equation) + case(moisture) + nodeVolFracLiqTrial(iLayer) = nodeVolFracLiqTrial0(iLayer) + if( nodeTempTrial(iLayer) < Tcrit) then !if do not perturb temperature, this should not change, but nodeMatricHeadTrial will have changed + matricFHead = (LH_fus/gravity)*(nodeTempTrial(iLayer) - Tfreeze)/Tfreeze + ! if you change the temp below the critical temp, then liquid changes, shouldn't happen? + nodeVolFracLiqTrial(iLayer) = volFracLiq(matricFHead,theta_res(iLayer),theta_sat(iLayer),vGn_alpha(iLayer),vGn_n(iLayer),vGn_m(iLayer)) + ! if you change the liquid amounts and are below the critical temp, then temp changes, howww, shouldn't happen + nodeMatricHeadTrial(iLayer) = matricHead(nodeVolFracLiqTrial(iLayer),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + else + nodeMatricHeadTrial(iLayer) = matricHead(nodeVolFracLiqTrial0(iLayer),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + endif + case(mixdform) + nodeMatricHeadTrial(iLayer) = nodeMatricHeadTrial0(iLayer) + if( nodeTempTrial(iLayer) < Tcrit) then !if do not perturb temperature, this should not change, but nodeMatricHeadTrial will have changed + matricFHead = (LH_fus/gravity)*(nodeTempTrial(iLayer) - Tfreeze)/Tfreeze + nodeVolFracLiqTrial(iLayer) = volFracLiq(matricFHead,theta_res(iLayer),theta_sat(iLayer),vGn_alpha(iLayer),vGn_n(iLayer),vGn_m(iLayer)) + else + nodeVolFracLiqTrial(iLayer) = volFracLiq(nodeMatricHeadTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) + endif + end select nodeVolFracAirTrial(iLayer) = theta_sat(iLayer) - volFracLiq(nodeMatricHeadTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) nodeVolFracIceTrial(iLayer) = theta_sat(iLayer) - (nodeVolFracAirTrial(iLayer) + nodeVolFracLiqTrial(iLayer)) case(iname_snow) @@ -1010,8 +1032,22 @@ subroutine iLayerThermalConduct(& ! ***** soil case(iname_soil) - mLayerdVolFracLiq_dPsi0 = dTheta_dPsi(nodeMatricHeadTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) - mLayerdVolFracLiq_dTk = dTheta_dTk(nodeTempTrial(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_alpha(iLayer),vGn_n(iLayer),vGn_m(iLayer)) + select case(ixRichards) ! (form of Richards' equation) + case(moisture) + mLayerdVolFracLiq_dWat = 1.rkind + mLayerdVolFracIce_dWat = dPsi_dTheta(nodeVolFracLiqTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) - 1.rkind + case(mixdform) + if(nodeTempTrial(iLayer) < Tcrit) then + mLayerdVolFracLiq_dWat = 0.0 + mLayerdVolFracIce_dWat = dTheta_dPsi(nodeMatricHeadTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) + else + mLayerdVolFracLiq_dWat = dTheta_dPsi(nodeMatricHeadTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) + mLayerdVolFracIce_dWat = 0.rkind + endif + end select + if(nodeTempTrial(iLayer) < Tcrit) mLayerdVolFracLiq_dTk = dTheta_dTk(nodeTempTrial(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_alpha(iLayer),vGn_n(iLayer),vGn_m(iLayer)) + if(nodeTempTrial(iLayer) >=Tcrit) mLayerdVolFracLiq_dTk = 0._rkind + mLayerdVolFracIce_dTk = -mLayerdVolFracLiq_dTk !often can and will simplify one of these terms out ! select option for thermal conductivity of soil select case(ixThCondSoil) @@ -1021,29 +1057,24 @@ subroutine iLayerThermalConduct(& ! compute the thermal conductivity of the wet material (W m-1) lambda_wet = lambda_wetsoil**( 1._rkind - theta_sat(iLayer) ) * lambda_water**theta_sat(iLayer) * lambda_ice**(theta_sat(iLayer) - nodeVolFracLiqTrial(iLayer)) - if( nodeTempTrial(iLayer) < Tcrit) then - dlambda_wet_dPsi0 = 0._rkind - dlambda_wet_dTk = lambda_wet * log(lambda_ice) * (-mLayerdVolFracLiq_dTk) - else - dlambda_wet_dPsi0 = lambda_wet * log(lambda_ice) * (-mLayerdVolFracLiq_dPsi0) - dlambda_wet_dTk = 0._rkind - endif + dlambda_wet_dWat = -lambda_wet * log(lambda_ice) * mLayerdVolFracLiq_dWat + dlambda_wet_dTk = -lambda_wet * log(lambda_ice) * mLayerdVolFracLiq_dTk + relativeSat = (nodeVolFracIceTrial(iLayer) + nodeVolFracLiqTrial(iLayer))/theta_sat(iLayer) ! relative saturation - ! drelativeSat_dPsi0 = mLayerdVolFracLiq_dPsi0, and drelativeSat_dTk = 0 (so dkerstenNum_dTk = 0) + ! drelativeSat_dWat = dPsi0_dWat, and drelativeSat_dTk = 0 (so dkerstenNum_dTk = 0) ! compute the Kersten number (-) if(relativeSat > 0.1_rkind)then ! log10(0.1) = -1 kerstenNum = log10(relativeSat) + 1._rkind - ! these derivatives are the same frozen or unfrozen - dkerstenNum_dPsi0 = mLayerdVolFracLiq_dPsi0 / ( theta_sat(iLayer) * relativeSat * log(10._rkind) ) + dkerstenNum_dWat = (mLayerdVolFracIce_dWat + mLayerdVolFracLiq_dWat) / ( theta_sat(iLayer) * relativeSat * log(10._rkind) ) else kerstenNum = 0._rkind ! dry thermal conductivity - dkerstenNum_dPsi0 = 0._rkind + dkerstenNum_dWat = 0._rkind endif ! ...and, compute the thermal conductivity mLayerThermalC(iLayer) = kerstenNum*lambda_wet + (1._rkind - kerstenNum)*lambda_drysoil ! compute derivatives - mLayerdThermalC_dWat(iLayer) = dkerstenNum_dPsi0 * ( lambda_wet - lambda_drysoil ) + kerstenNum*dlambda_wet_dPsi0 + mLayerdThermalC_dWat(iLayer) = dkerstenNum_dWat * ( lambda_wet - lambda_drysoil ) + kerstenNum*dlambda_wet_dWat mLayerdThermalC_dNrg(iLayer) = kerstenNum*dlambda_wet_dTk ! ** mixture of constituents @@ -1054,30 +1085,20 @@ subroutine iLayerThermalConduct(& lambda_air * nodeVolFracAirTrial(iLayer) ! air component ! compute derivatives - if( nodeTempTrial(iLayer) < Tcrit) then - mLayerdThermalC_dWat(iLayer) = (lambda_ice - lambda_air) * mLayerdVolFracLiq_dPsi0 - mLayerdThermalC_dNrg(iLayer) = (-lambda_ice + lambda_water) * mLayerdVolFracLiq_dTk - else - mLayerdThermalC_dWat(iLayer) = (lambda_water - lambda_air) * mLayerdVolFracLiq_dPsi0 - mLayerdThermalC_dNrg(iLayer) = 0._rkind - endif + mLayerdThermalC_dWat(iLayer) = lambda_ice*mLayerdVolFracLiq_dIce + lambda_water*mLayerdVolFracLiq_dWat + lambda_air*(mLayerdVolFracIce_dWat + mLayerdVolFracLiq_dWat) + mLayerdThermalC_dNrg(iLayer) = (lambda_ice - lambda_water) * mLayerdVolFracIce_dTk ! ** test case for the mizoguchi lab experiment, Hansson et al. VZJ 2004 case(hanssonVZJ) fArg = 1._rkind + f1*nodeVolFracIceTrial(iLayer)**f2 xArg = nodeVolFracLiqTrial(iLayer) + fArg*nodeVolFracIceTrial(iLayer) - if( nodeTempTrial(iLayer) < Tcrit) then - dxArg_dPsi0 = mLayerdVolFracLiq_dPsi0 * (1._rkind + f1*(f2+1)*nodeVolFracIceTrial(iLayer)**f2) - dxArg_dTk = mLayerdVolFracLiq_dTk * (1._rkind - f1*(f2+1)*nodeVolFracIceTrial(iLayer)**f2) - else - dxArg_dPsi0 = mLayerdVolFracLiq_dPsi0 - dxArg_dTk = 0._rkind - endif + dxArg_dWat = mLayerdVolFracLiq_dWat + mLayerdVolFracIce_dWat * (1._rkind + f1*(f2+1)*nodeVolFracIceTrial(iLayer)**f2) + dxArg_dTk = mLayerdVolFracIce_dTk * f1*(f2+1)*nodeVolFracIceTrial(iLayer)**f2 ! ...and, compute the thermal conductivity mLayerThermalC(iLayer) = c1 + c2*xArg + (c1 - c4)*exp(-(c3*xArg)**c5) ! compute derivatives - mLayerdThermalC_dWat(iLayer) = ( c2 - c5*c3*(c3*xArg)**(c5-1)*(c1 - c4)*exp(-(c3*xArg)**c5) ) * dxArg_dPsi0 + mLayerdThermalC_dWat(iLayer) = ( c2 - c5*c3*(c3*xArg)**(c5-1)*(c1 - c4)*exp(-(c3*xArg)**c5) ) * dxArg_dWat mLayerdThermalC_dNrg(iLayer) = ( c2 - c5*c3*(c3*xArg)**(c5-1)*(c1 - c4)*exp(-(c3*xArg)**c5) ) * dxArg_dTk ! ** check @@ -1087,7 +1108,7 @@ subroutine iLayerThermalConduct(& ! ***** snow case(iname_snow) - mLayerdVolFracIce_dTheta = ( 1._rkind - fLiq )*(iden_water/iden_ice) + mLayerdVolFracIce_dWat = ( 1._rkind - fLiq )*(iden_water/iden_ice) mLayerdVolFracIce_dTk = -dFracLiq_dTk(nodeTempTrial(iLayer),snowfrz_scale) * nodeVolTotWatTrial(iLayer)*(iden_water/iden_ice) ! temporally constant thermal conductivity @@ -1104,13 +1125,13 @@ subroutine iLayerThermalConduct(& select case(ixThCondSnow) case(Yen1965) - mLayerdThermalC_dWat(iLayer) = 2._rkind * 3.217d-6 * nodeVolFracIceTrial(iLayer) * iden_ice * mLayerdVolFracIce_dTheta + mLayerdThermalC_dWat(iLayer) = 2._rkind * 3.217d-6 * nodeVolFracIceTrial(iLayer) * iden_ice * mLayerdVolFracIce_dTWat mLayerdThermalC_dNrg(iLayer) = 2._rkind * 3.217d-6 * nodeVolFracIceTrial(iLayer) * iden_ice * mLayerdVolFracIce_dTk case(Mellor1977) - mLayerdThermalC_dWat(iLayer) = 2._rkind * 2.576d-6 * nodeVolFracIceTrial(iLayer) * iden_ice * mLayerdVolFracIce_dTheta + mLayerdThermalC_dWat(iLayer) = 2._rkind * 2.576d-6 * nodeVolFracIceTrial(iLayer) * iden_ice * mLayerdVolFracIce_dWat mLayerdThermalC_dNrg(iLayer) = 2._rkind * 2.576d-6 * nodeVolFracIceTrial(iLayer) * iden_ice * mLayerdVolFracIce_dTk case(Jordan1991) - mLayerdThermalC_dWat(iLayer) = ( 7.75d-5 + 2._rkind * 1.105d-6 * nodeVolFracIceTrial(iLayer) * iden_ice ) * (lambda_ice-lambda_air) * mLayerdVolFracIce_dTheta + mLayerdThermalC_dWat(iLayer) = ( 7.75d-5 + 2._rkind * 1.105d-6 * nodeVolFracIceTrial(iLayer) * iden_ice ) * (lambda_ice-lambda_air) * mLayerdVolFracIce_dWat mLayerdThermalC_dNrg(iLayer) = ( 7.75d-5 + 2._rkind * 1.105d-6 * nodeVolFracIceTrial(iLayer) * iden_ice ) * (lambda_ice-lambda_air) * mLayerdVolFracIce_dTk end select ! option for the thermal conductivity of snow end if diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 61897191e..6f3e99c30 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -129,7 +129,7 @@ subroutine systemSolvSundials(& ! structure allocations USE allocspace_module,only:allocLocal ! allocate local data structures ! simulation of fluxes and residuals given a trial state vector - USE eval8summa_module,only:eval8summa ! simulation of fluxes and residuals given a trial state vector + USE eval8summa_module,only:eval8summaSundials ! simulation of fluxes and residuals given a trial state vector USE eval8DAE_module,only:eval8DAE USE summaSolve_module,only:summaSolve ! calculate the iteration increment, evaluate the new state, and refine if necessary USE getVectorz_module,only:getScaling ! get the scaling vectors @@ -360,7 +360,7 @@ subroutine systemSolvSundials(& ! NOTE 1: The derivatives computed in eval8summa are used to calculate the Jacobian matrix for the first iteration ! NOTE 2: The Jacobian matrix together with the residual vector is used to calculate the first iteration increment - call eval8summa(& + call eval8summaSundials(& ! input: model control dt, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers diff --git a/build/source/engine/updateVars4JacDAE.f90 b/build/source/engine/updateVars4JacDAE.f90 index 516f95e1f..cc18b17a8 100644 --- a/build/source/engine/updateVars4JacDAE.f90 +++ b/build/source/engine/updateVars4JacDAE.f90 @@ -460,16 +460,16 @@ subroutine updateVars4JacDAE(& case(iname_lmpLayer) dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore - ! dVolHtCapBulk_dPsi0 uses the derivative in water retention curve above critical temp w.r.t. matric head - dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_ice * Cp_ice - iden_air * Cp_air) *& - dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) case default dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),& vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - ! dVolHtCapBulk_dPsi0 uses the derivative in water retention curve above critical temp w.r.t. matric head, here it is dVolTot_dPsi0 - dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_water * Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) end select + + ! dVolHtCapBulk_dPsi0 uses the derivative in water retention curve above critical temp w.r.t.state variable dVolTot_dPsi0 + if(xTemp< Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) + if(xTemp>=Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_water * Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) + end select ! compute the derivative in liquid water content w.r.t. temperature From 49d5086bb21d632088bae010973a2b77a841d609 Mon Sep 17 00:00:00 2001 From: ashleymedin <34691332+ashleymedin@users.noreply.github.com> Date: Thu, 3 Mar 2022 10:02:42 +0900 Subject: [PATCH 0245/1472] Undoing vegNrgFlux changes --- build/my_makefile_cop | 2 +- build/my_makefile_mac | 2 +- build/source/dshare/get_ixname.f90 | 3 - build/source/dshare/popMetadat.f90 | 3 - build/source/dshare/var_lookup.f90 | 5 +- build/source/engine/canopySnow.f90 | 96 +- build/source/engine/computFlux.f90 | 5 +- build/source/engine/coupled_em.f90 | 17 +- build/source/engine/vegNrgFlux.f90 | 1738 +--------------------------- 9 files changed, 85 insertions(+), 1786 deletions(-) diff --git a/build/my_makefile_cop b/build/my_makefile_cop index 0fc373346..27ae8119f 100644 --- a/build/my_makefile_cop +++ b/build/my_makefile_cop @@ -210,9 +210,9 @@ SUMMA_SOLVER= \ stomResist.f90 \ groundwatr.f90 \ vegSWavRad.f90 \ - vegLiqFlux.f90 \ vegNrgFlux.f90 \ ssdNrgFlux.f90 \ + vegLiqFlux.f90 \ snowLiqFlx.f90 \ soilLiqFlx.f90 \ bigAquifer.f90 \ diff --git a/build/my_makefile_mac b/build/my_makefile_mac index 4e268b271..caaa10435 100644 --- a/build/my_makefile_mac +++ b/build/my_makefile_mac @@ -210,9 +210,9 @@ SUMMA_SOLVER= \ stomResist.f90 \ groundwatr.f90 \ vegSWavRad.f90 \ - vegLiqFlux.f90 \ vegNrgFlux.f90 \ ssdNrgFlux.f90 \ + vegLiqFlux.f90 \ snowLiqFlx.f90 \ soilLiqFlx.f90 \ bigAquifer.f90 \ diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index c4d5f48d6..a9471379b 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -733,9 +733,6 @@ function get_ixderiv(varName) case('scalarCanopyLiqDeriv' ); get_ixderiv = iLookDERIV%scalarCanopyLiqDeriv ! derivative in (throughfall + canopy drainage) w.r.t. canopy liquid water (s-1) case('scalarThroughfallRainDeriv' ); get_ixderiv = iLookDERIV%scalarThroughfallRainDeriv ! derivative in throughfall w.r.t. canopy liquid water (s-1) case('scalarCanopyLiqDrainageDeriv' ); get_ixderiv = iLookDERIV%scalarCanopyLiqDrainageDeriv ! derivative in canopy drainage w.r.t. canopy liquid water (s-1) - case('scalarThroughfallSnowDeriv' ); get_ixderiv = iLookDERIV%scalarThroughfallSnowDeriv ! derivative in snow throughfall w.r.t. canopy ice (s-1) - case('scalarCanopySnowUnloadingDeriv' ); get_ixderiv = iLookDERIV%scalarCanopySnowUnloadingDeriv ! derivative in unloading of snow w.r.t. canopy ice (s-1) - case('scalarCanopySnowUnload_TkDeriv' ); get_ixderiv = iLookDERIV%scalarCanopySnowUnload_TkDeriv ! derivative in unloading of snow w.r.t. canopy air temperature ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below case('dNrgFlux_dTempAbove' ); get_ixderiv = iLookDERIV%dNrgFlux_dTempAbove ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) case('dNrgFlux_dTempBelow' ); get_ixderiv = iLookDERIV%dNrgFlux_dTempBelow ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 7cbfdfef4..9079c07d0 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -567,9 +567,6 @@ subroutine popMetadat(err,message) deriv_meta(iLookDERIV%scalarCanopyLiqDeriv) = var_info('scalarCanopyLiqDeriv' , 'derivative in (throughfall + drainage) w.r.t. canopy liquid water' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%scalarThroughfallRainDeriv) = var_info('scalarThroughfallRainDeriv' , 'derivative in throughfall w.r.t. canopy liquid water' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%scalarCanopyLiqDrainageDeriv) = var_info('scalarCanopyLiqDrainageDeriv' , 'derivative in canopy drainage w.r.t. canopy liquid water' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%scalarThroughfallSnowDeriv) = var_info('scalarThroughfallSnowDeriv' , 'derivative in snow throughfall w.r.t. canopy ice' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%scalarCanopySnowUnloadingDeriv)= var_info('scalarCanopySnowUnloadingDeriv', 'derivative in unloading of snow w.r.t. canopy ice' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%scalarCanopySnowUnload_TkDeriv)= var_info('scalarCanopySnowUnload_TkDeriv', 'derivative in unloading of snow w.r.t. canopy air temperature' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below deriv_meta(iLookDERIV%dNrgFlux_dTempAbove) = var_info('dNrgFlux_dTempAbove' , 'derivatives in the flux w.r.t. temperature in the layer above' , 'J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dNrgFlux_dTempBelow) = var_info('dNrgFlux_dTempBelow' , 'derivatives in the flux w.r.t. temperature in the layer below' , 'J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index d102edf55..6d6e10702 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -586,9 +586,6 @@ MODULE var_lookup integer(i4b) :: scalarCanopyLiqDeriv = integerMissing ! derivative in (throughfall + canopy drainage) w.r.t. canopy liquid water (s-1) integer(i4b) :: scalarThroughfallRainDeriv = integerMissing ! derivative in throughfall w.r.t. canopy liquid water (s-1) integer(i4b) :: scalarCanopyLiqDrainageDeriv = integerMissing ! derivative in canopy drainage w.r.t. canopy liquid water (s-1) - integer(i4b) :: scalarThroughfallSnowDeriv = integerMissing ! derivative in snow throughfall w.r.t. canopy ice (s-1) - integer(i4b) :: scalarCanopySnowUnloadingDeriv = integerMissing ! derivative in unloading of snow w.r.t. canopy ice (s-1) - integer(i4b) :: scalarCanopySnowUnload_TkDeriv = integerMissing ! derivative in unloading of snow w.r.t. canopy air temperature (K-1) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below integer(i4b) :: dNrgFlux_dTempAbove = integerMissing ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) integer(i4b) :: dNrgFlux_dTempBelow = integerMissing ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) @@ -864,7 +861,7 @@ MODULE var_lookup 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,& 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,& 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,& - 51, 52, 53, 54) + 51) ! named variables: model indices type(iLook_index), public,parameter :: iLookINDEX =ilook_index ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& diff --git a/build/source/engine/canopySnow.f90 b/build/source/engine/canopySnow.f90 index ba2b6ffdc..d3ea510fc 100644 --- a/build/source/engine/canopySnow.f90 +++ b/build/source/engine/canopySnow.f90 @@ -34,8 +34,8 @@ module canopySnow_module USE multiconst,only:Tfreeze ! freezing point of pure water (K) ! named variables defining elements in the data structures -USE var_lookup,only:iLookFORCE,iLookPARAM,iLookDIAG ! named variables for structure elements -USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure +USE var_lookup,only:iLookFORCE,iLookPARAM,iLookDIAG,iLookPROG,iLookFLUX ! named variables for structure elements +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure ! model decisions USE mDecisions_module,only: & @@ -57,49 +57,33 @@ module canopySnow_module ! ************************************************************************************************ subroutine canopySnow(& ! input: model control - dt, & ! intent(in): time step (seconds) - exposedVAI, & ! intent(in): exposed vegetation area index (m2 m-2) - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation - scalarCanopyIceTrial, & ! intent(inout): trial mass of ice on the vegetation canopy at the current iteration (kg m-2) - scalarCanairTempTrial, & ! intent(in): temperature of the canopy air space (k) - scalarSnowfall, & ! intent(in): computed snowfall rate (kg m-2 s-1) - scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) - scalarWindspdCanopyTop, & ! intent(in): windspeed at the top of the canopy (m s-1) + dt, & ! intent(in): time step (seconds) + exposedVAI, & ! intent(in): exposed vegetation area index (m2 m-2) + computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation ! input/output: data structures - model_decisions, & ! intent(in): model decisions - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - diag_data, & ! intent(in): model diagnostic variables for a local HRU - ! output - scalarThroughfallSnow, & ! intent(out): snow that reaches the ground without ever touching the canopy (kg m-2 s-1) - scalarCanopySnowUnloading, & ! intent(out): unloading of snow from the vegetion canopy (kg m-2 s-1) - scalarThroughfallSnowDeriv, & ! intent(out): derivative in throughfall w.r.t. canopy ice (s-1) - scalarCanopySnowUnloadingDeriv, & ! intent(out): derivative in unloading of snow w.r.t. canopy ice (s-1) - scalarCanopySnowUnload_TkDeriv, & ! intent(out): derivative in unloading of snow w.r.t. canopy air temperature (K-1) - err,message) ! intent(out): error control + model_decisions, & ! intent(in): model decisions + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(inout): model prognostic variables for a local HRU + flux_data, & ! intent(inout): model flux variables + ! output: error control + err,message) ! intent(out): error control ! ------------------------------------------------------------------------------------------------ implicit none ! ------------------------------------------------------------------------------------------------ ! input: model control real(rkind),intent(in) :: dt ! time step (seconds) real(rkind),intent(in) :: exposedVAI ! exposed vegetation area index -- leaf + stem -- after burial by snow (m2 m-2) - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) - real(rkind),intent(inout) :: scalarCanopyIceTrial ! trial mass of ice on the vegetation canopy at the current iteration (kg m-2) - real(rkind),intent(in) :: scalarCanairTempTrial ! trial temperature of the canopy air space (k) - real(rkind),intent(in) :: scalarSnowfall ! computed snowfall rate (kg m-2 s-1) - real(rkind),intent(in) :: scalarCanopyLiqDrainage ! liquid drainage from the vegetation canopy (kg m-2 s-1) - real(rkind),intent(in) :: scalarWindspdCanopyTop ! windspeed at the top of the canopy (m s-1) + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) ! input/output: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions type(var_d),intent(in) :: forc_data ! model forcing data type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_dlength),intent(in) :: diag_data ! model diagnostic variables for a local HRU - ! output - real(rkind),intent(out) :: scalarThroughfallSnow ! snow that reaches the ground without ever touching the canopy (kg m-2 s-1) - real(rkind),intent(out) :: scalarCanopySnowUnloading ! unloading of snow from the vegetion canopy (kg m-2 s-1) - real(rkind),intent(out) :: scalarThroughfallSnowDeriv ! derivative in throughfall flux w.r.t. canopy storage (s-1) - real(rkind),intent(out) :: scalarCanopySnowUnloadingDeriv ! derivative in unloading flux w.r.t. canopy storage (s-1) - real(rkind),intent(out) :: scalarCanopySnowUnload_TkDeriv ! derivative in unloading flux w.r.t. canopy air temperature (K-1) + type(var_dlength),intent(inout) :: prog_data ! model prognostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model flux variables + ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local variables @@ -111,6 +95,8 @@ subroutine canopySnow(& real(rkind) :: leafScaleFactor ! scaling factor for interception based on temperature (-) real(rkind) :: leafInterceptCapSnow ! storage capacity for snow per unit leaf area (kg m-2) real(rkind) :: canopyIceScaleFactor ! capacity scaling factor for throughfall (kg m-2) + real(rkind) :: throughfallDeriv ! derivative in throughfall flux w.r.t. canopy storage (s-1) + real(rkind) :: unloadingDeriv ! derivative in unloading flux w.r.t. canopy storage (s-1) real(rkind) :: scalarCanopyIceIter ! trial value for mass of ice on the vegetation canopy (kg m-2) (kg m-2) real(rkind) :: flux ! net flux (kg m-2 s-1) real(rkind) :: delS ! change in storage (kg m-2) @@ -142,7 +128,19 @@ subroutine canopySnow(& rateWindUnloading => mpar_data%var(iLookPARAM%rateWindUnloading)%dat(1), & ! constant describing how quickly snow will unload due to wind in windySnow parameterization (K s) ! model diagnostic variables - scalarNewSnowDensity => diag_data%var(iLookDIAG%scalarNewSnowDensity)%dat(1) & ! intent(in): [dp] density of new snow (kg m-3) + scalarNewSnowDensity => diag_data%var(iLookDIAG%scalarNewSnowDensity)%dat(1), & ! intent(in): [dp] density of new snow (kg m-3) + + ! model prognostic variables (input/output) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! intent(inout): [dp] mass of ice on the vegetation canopy (kg m-2) + + ! model fluxes (input) + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1), & ! intent(in): [dp] temperature of the canopy air space (k) + scalarSnowfall => flux_data%var(iLookFLUX%scalarSnowfall)%dat(1), & ! intent(in): [dp] computed snowfall rate (kg m-2 s-1) + scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1), & ! intent(in): [dp] liquid drainage from the vegetation canopy (kg m-2 s-1) + scalarWindspdCanopyTop => flux_data%var(iLookFLUX%scalarWindspdCanopyTop)%dat(1), & ! intent(in): [dp] windspeed at the top of the canopy (m s-1) + ! model variables (output) + scalarThroughfallSnow => flux_data%var(iLookFLUX%scalarThroughfallSnow)%dat(1), & ! intent(out): [dp] snow that reaches the ground without ever touching the canopy (kg m-2 s-1) + scalarCanopySnowUnloading => flux_data%var(iLookFLUX%scalarCanopySnowUnloading)%dat(1) & ! intent(out): [dp] unloading of snow from the vegetion canopy (kg m-2 s-1) ) ! associate variables in the data structures ! ----------------------------------------------------------------------------------------------------------------------------------------------------- @@ -151,32 +149,31 @@ subroutine canopySnow(& ! ************************************* if(computeVegFlux)then - unloading_melt = min(ratioDrip2Unloading*scalarCanopyLiqDrainage, scalarCanopyIceTrial/dt) ! kg m-2 s-1 + unloading_melt = min(ratioDrip2Unloading*scalarCanopyLiqDrainage, scalarCanopyIce/dt) ! kg m-2 s-1 else unloading_melt = 0._rkind end if - scalarCanopyIceTrial = scalarCanopyIceTrial - unloading_melt*dt + scalarCanopyIce = scalarCanopyIce - unloading_melt*dt ! ***** ! compute the ice balance due to snowfall and unloading... ! ******************************************************** ! check for early returns - if(.not.computeVegFlux .or. (scalarSnowfall= minWindUnloading) then windUnloadingFun = abs(scalarWindspdCanopyTop) / rateWindUnloading ! (s-1) else @@ -184,18 +181,13 @@ subroutine canopySnow(& end if ! implement the "windySnow" Roesch et al. 2001 parameterization, Eq. 13 in Roesch et al. 2001 scalarCanopySnowUnloading = scalarCanopyIceIter * (tempUnloadingFun + windUnloadingFun) - scalarCanopySnowUnloadingDeriv = tempUnloadingFun + windUnloadingFun - if (tempUnloadingFun > 0._rkind)then - scalarCanopySnowUnload_TkDeriv = scalarCanopyIceIter/ rateTempUnloading - else - scalarCanopySnowUnload_TkDeriv = 0._rkind - end if + unloadingDeriv = tempUnloadingFun + windUnloadingFun end if ! no snowfall if(scalarSnowfall NOTE: use air temperature - canairTempTrial, & ! intent(in): trial value of the canopy air space temperature (K) - canopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - groundTempTrial, & ! intent(in): trial value of ground temperature (K) - canopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) - canopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) - - ! input: model derivatives - dCanLiq_dTcanopy, & ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) - - ! input/output: data structures - type_data, & ! intent(in): type of vegetation and soil - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): state vector geometry - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - bvar_data, & ! intent(in): model variables for the local basin - model_decisions, & ! intent(in): model decisions - - ! output: liquid water fluxes associated with evaporation/transpiration (needed for coupling) - returnCanopyTranspiration, & ! intent(out): canopy transpiration (kg m-2 s-1) - returnCanopyEvaporation, & ! intent(out): canopy evaporation/condensation (kg m-2 s-1) - returnGroundEvaporation, & ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - - ! output: fluxes - canairNetFlux, & ! intent(out): net energy flux for the canopy air space (W m-2) - canopyNetFlux, & ! intent(out): net energy flux for the vegetation canopy (W m-2) - groundNetFlux, & ! intent(out): net energy flux for the ground surface (W m-2) - - ! output: energy flux derivatives - dCanairNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) - dCanairNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) - dCanairNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) - dCanopyNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) - dCanopyNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) - dCanopyNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - dGroundNetFlux_dCanairTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) - dGroundNetFlux_dCanopyTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) - dGroundNetFlux_dGroundTemp, & ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - - ! output liquid water flux derivarives (canopy evap) - dCanopyEvaporation_dCanWat, & ! intent(out): derivative in canopy evaporation w.r.t. canopy total water content (s-1) - dCanopyEvaporation_dTCanair, & ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTCanopy, & ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTGround, & ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - - ! output: liquid water flux derivarives (ground evap) - dGroundEvaporation_dCanWat, & ! intent(out): derivative in ground evaporation w.r.t. canopy total water content (s-1) - dGroundEvaporation_dTCanair, & ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTCanopy, & ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTGround, & ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - - ! output: cross derivative terms - dCanopyNetFlux_dCanWat, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) - dGroundNetFlux_dCanWat, & ! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) - - ! output: error control - err,message) ! intent(out): error control - - ! utilities - USE expIntegral_module,only:expInt ! function to calculate the exponential integral - ! conversion functions - USE conv_funcs_module,only:satVapPress ! function to compute the saturated vapor pressure (Pa) - USE conv_funcs_module,only:getLatentHeatValue ! function to identify latent heat of vaporization/sublimation (J kg-1) - ! stomatal resistance - USE stomResist_module,only:stomResist ! subroutine to calculate stomatal resistance - ! throughfall of rain and snow - USE canopySnow_module,only:canopySnow ! compute interception and unloading of snow from the vegetation canopy - USE vegLiqFlux_module,only:vegLiqFlux ! compute liquid water fluxes through vegetation - USE snow_utils_module,only:fracliquid ! compute fraction of liquid water at a given temperature - - ! compute energy and mass fluxes for vegetation - implicit none - - ! --------------------------------------------------------------------------------------- - ! * dummy variables - ! --------------------------------------------------------------------------------------- - ! input: model control - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(in) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - - ! input: model state variables - real(rkind),intent(in) :: upperBoundTemp ! temperature of the upper boundary (K) --> NOTE: use air temperature - real(rkind),intent(in) :: canairTempTrial ! trial value of canopy air space temperature (K) - real(rkind),intent(in) :: canopyTempTrial ! trial value of canopy temperature (K) - real(rkind),intent(in) :: groundTempTrial ! trial value of ground temperature (K) - real(rkind),intent(in) :: canopyIceTrial ! trial value of mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: canopyLiqTrial ! trial value of mass of liquid water on the vegetation canopy (kg m-2) - - ! input: model derivatives - real(rkind),intent(in) :: dCanLiq_dTcanopy ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) - - ! input/output: data structures - type(var_i),intent(in) :: type_data ! type of vegetation and soil - type(var_d),intent(in) :: forc_data ! model forcing data - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! state vector geometry - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin - type(model_options),intent(in) :: model_decisions(:) ! model decisions - - ! output: liquid water fluxes associated with evaporation/transpiration (needed for coupling) - real(rkind),intent(out) :: returnCanopyTranspiration ! canopy transpiration (kg m-2 s-1) - real(rkind),intent(out) :: returnCanopyEvaporation ! canopy evaporation/condensation (kg m-2 s-1) - real(rkind),intent(out) :: returnGroundEvaporation ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - - ! output: fluxes - real(rkind),intent(out) :: canairNetFlux ! net energy flux for the canopy air space (W m-2) - real(rkind),intent(out) :: canopyNetFlux ! net energy flux for the vegetation canopy (W m-2) - real(rkind),intent(out) :: groundNetFlux ! net energy flux for the ground surface (W m-2) - - ! output: energy flux derivatives - real(rkind),intent(out) :: dCanairNetFlux_dCanairTemp ! derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) - real(rkind),intent(out) :: dCanairNetFlux_dCanopyTemp ! derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dCanairNetFlux_dGroundTemp ! derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) - real(rkind),intent(out) :: dCanopyNetFlux_dCanairTemp ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) - real(rkind),intent(out) :: dCanopyNetFlux_dCanopyTemp ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dCanopyNetFlux_dGroundTemp ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - real(rkind),intent(out) :: dGroundNetFlux_dCanairTemp ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) - real(rkind),intent(out) :: dGroundNetFlux_dCanopyTemp ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - - ! output: liquid flux derivatives (canopy evap) - real(rkind),intent(out) :: dCanopyEvaporation_dCanWat ! derivative in canopy evaporation w.r.t. canopy total water content (s-1) - real(rkind),intent(out) :: dCanopyEvaporation_dTCanair ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - real(rkind),intent(out) :: dCanopyEvaporation_dTCanopy ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - real(rkind),intent(out) :: dCanopyEvaporation_dTGround ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - - ! output: liquid flux derivatives (ground evap) - real(rkind),intent(out) :: dGroundEvaporation_dCanWat ! derivative in ground evaporation w.r.t. canopy total water content (s-1) - real(rkind),intent(out) :: dGroundEvaporation_dTCanair ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - real(rkind),intent(out) :: dGroundEvaporation_dTCanopy ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - real(rkind),intent(out) :: dGroundEvaporation_dTGround ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - - ! output: cross derivative terms - real(rkind),intent(out) :: dCanopyNetFlux_dCanWat ! derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) - real(rkind),intent(out) :: dGroundNetFlux_dCanWat ! derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) - - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - - ! --------------------------------------------------------------------------------------- - ! * local variables - ! --------------------------------------------------------------------------------------- - ! local (general) - character(LEN=256) :: cmessage ! error message of downwind routine - real(rkind) :: VAI ! vegetation area index (m2 m-2) - real(rkind) :: exposedVAI ! exposed vegetation area index (m2 m-2) - real(rkind) :: totalCanopyWater ! total water on the vegetation canopy (kg m-2) - real(rkind) :: scalarAquiferStorage ! aquifer storage (m) - - ! local (compute numerical derivatives) - integer(i4b),parameter :: unperturbed=1 ! named variable to identify the case of unperturbed state variables - integer(i4b),parameter :: perturbStateGround=2 ! named variable to identify the case where we perturb the ground temperature - integer(i4b),parameter :: perturbStateCanopy=3 ! named variable to identify the case where we perturb the canopy temperature - integer(i4b),parameter :: perturbStateCanair=4 ! named variable to identify the case where we perturb the canopy air temperature - integer(i4b),parameter :: perturbStateCanWat=5 ! named variable to identify the case where we perturb the canopy total water content - integer(i4b) :: itry ! index of flux evaluation - integer(i4b) :: nFlux ! number of flux evaluations - real(rkind) :: groundTemp ! value of ground temperature used in flux calculations (may be perturbed) - real(rkind) :: canopyTemp ! value of canopy temperature used in flux calculations (may be perturbed) - real(rkind) :: canairTemp ! value of canopy air temperature used in flux calculations (may be perturbed) - real(rkind) :: canopyWat ! value of canopy total water used in flux calculations (may be perturbed) - real(rkind) :: canopyLiq ! value of canopy liquid water used in flux calculations (may be perturbed) - real(rkind) :: canopyIce ! value of canopy ice used in flux calculations (may be perturbed) - real(rkind) :: try0,try1 ! trial values to evaluate specific derivatives (testing only) - - ! local (saturation vapor pressure of veg) - real(rkind) :: TV_celcius ! vegetaion temperature (C) - real(rkind) :: TG_celcius ! ground temperature (C) - real(rkind) :: dSVPCanopy_dCanopyTemp ! derivative in canopy saturated vapor pressure w.r.t. vegetation temperature (Pa/K) - real(rkind) :: dSVPGround_dGroundTemp ! derivative in ground saturated vapor pressure w.r.t. ground temperature (Pa/K) - - ! local (wetted canopy area) - real(rkind) :: fracLiquidCanopy ! fraction of liquid water in the canopy (-) - real(rkind) :: canopyWetFraction ! trial value of the canopy wetted fraction (-) - real(rkind) :: dCanopyWetFraction_dWat ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) - real(rkind) :: dCanopyWetFraction_dT ! derivative in wetted fraction w.r.t. canopy temperature (K-1) - - ! local (longwave radiation) - real(rkind) :: expi ! exponential integral - real(rkind) :: scaleLAI ! scaled LAI (computing diffuse transmissivity) - real(rkind) :: diffuseTrans ! diffuse transmissivity (-) - real(rkind) :: groundEmissivity ! emissivity of the ground surface (-) - real(rkind),parameter :: vegEmissivity=0.98_rkind ! emissivity of vegetation (0.9665 in JULES) (-) - real(rkind),parameter :: soilEmissivity=0.98_rkind ! emmisivity of the soil (0.9665 in JULES) (-) - real(rkind),parameter :: snowEmissivity=0.99_rkind ! emissivity of snow (-) - real(rkind) :: dLWNetCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dLWNetGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dLWNetCanopy_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dLWNetGround_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) - - ! local (aerodynamic resistance) - real(rkind) :: scalarCanopyStabilityCorrection_old ! stability correction for the canopy (-) - real(rkind) :: scalarGroundStabilityCorrection_old ! stability correction for the ground surface (-) - -! local (advective heat transfer) - real(rkind) :: throughfallRain ! rain that reaches the ground without ever touching the canopy (kg m-2 s-1) - real(rkind) :: canopyLiqDrainage ! drainage of liquid water from the vegetation canopy (kg m-2 s-1) - real(rkind) :: throughfallRainDeriv ! derivative in throughfall w.r.t. canopy liquid water (s-1) - real(rkind) :: canopyLiqDrainageDeriv ! derivative in canopy drainage w.r.t. canopy liquid water (s-1) - real(rkind) :: throughfallSnow ! snow that reaches the ground without ever touching the canopy (kg m-2 s-1) - real(rkind) :: canopySnowUnloading ! unloading of snow from the vegetion canopy (kg m-2 s-1) - real(rkind) :: throughfallSnowDeriv ! derivative in snow throughfall w.r.t. canopy ice (s-1) - real(rkind) :: canopySnowUnloadingDeriv ! derivative in unloading of snow w.r.t. canopy ice (s-1) - real(rkind) :: canopySnowUnload_TkDeriv ! derivative in unloading of snow w.r.t. canopy air temperature - real(rkind) :: dThroughfallRain_dTCanopy ! derivative in rain throughfall w.r.t. canopy temperature - real(rkind) :: dCanopyLiqDrainage_dTCanopy ! derivative in canopy drainage w.r.t. canopy temperature - real(rkind) :: dThroughfallSnow_dTCanopy ! derivative in snow throughfall w.r.t. canopy temperature - real(rkind) :: dCanopySnowUnloading_dTCanopy ! derivative in unloading of snow w.r.t. canopy temperature - real(rkind) :: dThroughfallRain_dCanWat ! derivative in rain throughfall w.r.t. canopy total water - real(rkind) :: dCanopyLiqDrainage_dCanWat ! derivative in canopy drainage w.r.t. canopy total water - real(rkind) :: dThroughfallSnow_dCanWat ! derivative in snow throughfall w.r.t. canopy total water - real(rkind) :: dCanopySnowUnloading_dCanWat ! derivative in unloading of snow w.r.t. canopy total water - - ! local (turbulent heat transfer) - real(rkind) :: z0Ground ! roughness length of the ground (ground below the canopy or non-vegetated surface) (m) - real(rkind) :: soilEvapFactor ! soil water control on evaporation from non-vegetated surfaces - real(rkind) :: soilRelHumidity_noSnow ! relative humidity in the soil pores [0-1] - real(rkind) :: scalarLeafConductance ! leaf conductance (m s-1) - real(rkind) :: scalarCanopyConductance ! canopy conductance (m s-1) - real(rkind) :: scalarGroundConductanceSH ! ground conductance for sensible heat (m s-1) - real(rkind) :: scalarGroundConductanceLH ! ground conductance for latent heat -- includes soil resistance (m s-1) - real(rkind) :: scalarEvapConductance ! conductance for evaporation (m s-1) - real(rkind) :: scalarTransConductance ! conductance for transpiration (m s-1) - real(rkind) :: scalarTotalConductanceSH ! total conductance for sensible heat (m s-1) - real(rkind) :: scalarTotalConductanceLH ! total conductance for latent heat (m s-1) - real(rkind) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - real(rkind) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - real(rkind) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - real(rkind) :: turbFluxCanair ! total turbulent heat fluxes exchanged at the canopy air space (W m-2) - real(rkind) :: turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) - real(rkind) :: turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) - - ! local (turbulent heat transfer -- compute numerical derivatives) - ! (temporary scalar resistances when states are perturbed) - real(rkind) :: trialLeafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) - real(rkind) :: trialGroundResistance ! below canopy aerodynamic resistance (s m-1) - real(rkind) :: trialCanopyResistance ! above canopy aerodynamic resistance (s m-1) - real(rkind) :: notUsed_RiBulkCanopy ! bulk Richardson number for the canopy (-) - real(rkind) :: notUsed_RiBulkGround ! bulk Richardson number for the ground surface (-) - real(rkind) :: notUsed_z0Canopy ! roughness length of the vegetation canopy (m) - real(rkind) :: notUsed_WindReductionFactor ! canopy wind reduction factor (-) - real(rkind) :: notUsed_ZeroPlaneDisplacement ! zero plane displacement (m) - real(rkind) :: notUsed_scalarCanopyStabilityCorrection ! stability correction for the canopy (-) - real(rkind) :: notUsed_scalarGroundStabilityCorrection ! stability correction for the ground surface (-) - real(rkind) :: notUsed_EddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) - real(rkind) :: notUsed_FrictionVelocity ! friction velocity (m s-1) - real(rkind) :: notUsed_WindspdCanopyTop ! windspeed at the top of the canopy (m s-1) - real(rkind) :: notUsed_WindspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) - real(rkind) :: notUsed_dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - real(rkind) :: notUsed_dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind) :: notUsed_dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - real(rkind) :: notUsed_dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind) :: notUsed_dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - - ! (fluxes after perturbations in model states -- canopy air space) - real(rkind) :: turbFluxCanair_dStateCanair ! turbulent exchange from the canopy air space to the atmosphere, after canopy air temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanair_dStateCanopy ! turbulent exchange from the canopy air space to the atmosphere, after canopy temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanair_dStateGround ! turbulent exchange from the canopy air space to the atmosphere, after ground temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanair_dStateCanWat ! turbulent exchange from the canopy air space to the atmosphere, after canopy total water content is perturbed (W m-2) - - ! (fluxes after perturbations in model states -- vegetation canopy) - real(rkind) :: turbFluxCanopy_dStateCanair ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy air temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanopy_dStateCanopy ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanopy_dStateGround ! total turbulent heat fluxes from the canopy to the canopy air space, after ground temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanopy_dStateCanWat ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy total water content is perturbed (W m-2) - real(rkind) :: advectFluxCanopy_dStateCanopy ! total advective heat fluxes from the canopy to the canopy air space, after canopy temperature is perturbed (W m-2) - real(rkind) :: advectFluxCanopy_dStateCanWat ! total advective heat fluxes from the canopy to the canopy air space, after canopy total water content is perturbed (W m-2) - - ! (fluxes after perturbations in model states -- ground surface) - real(rkind) :: turbFluxGround_dStateCanair ! total turbulent heat fluxes from the ground to the canopy air space, after canopy air temperature is perturbed (W m-2) - real(rkind) :: turbFluxGround_dStateCanopy ! total turbulent heat fluxes from the ground to the canopy air space, after canopy temperature is perturbed (W m-2) - real(rkind) :: turbFluxGround_dStateGround ! total turbulent heat fluxes from the ground to the canopy air space, after ground temperature is perturbed (W m-2) - real(rkind) :: turbFluxGround_dStateCanWat ! total turbulent heat fluxes from the ground to the canopy air space, after canopy total water content is perturbed (W m-2) - real(rkind) :: advectFluxGround_dStateCanair ! total advective heat fluxes from the ground to the canopy air space, after canopy air temperature is perturbed (W m-2) - real(rkind) :: advectFluxGround_dStateCanopy ! total advective heat fluxes from the ground to the canopy air space, after canopy temperature is perturbed (W m-2) - real(rkind) :: advectFluxGround_dStateGround ! total advective heat fluxes from the ground to the canopy air space, after ground temperature is perturbed (W m-2) - real(rkind) :: advectFluxGround_dStateCanWat ! total advective heat fluxes from the ground to the canopy air space, after canopy total water content is perturbed (W m-2) - - ! (fluxes after perturbations in model states -- canopy evaporation) - real(rkind) :: latHeatCanEvap_dStateCanair ! canopy evaporation after canopy air temperature is perturbed (W m-2) - real(rkind) :: latHeatCanEvap_dStateCanopy ! canopy evaporation after canopy temperature is perturbed (W m-2) - real(rkind) :: latHeatCanEvap_dStateGround ! canopy evaporation after ground temperature is perturbed (W m-2) - real(rkind) :: latHeatCanEvap_dStateCanWat ! canopy evaporation after canopy total water content is perturbed (W m-2) - - ! (flux derivatives -- canopy air space) - real(rkind) :: dTurbFluxCanair_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanair_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanair_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanair_dCanWat ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) - - ! (flux derivatives -- vegetation canopy) - real(rkind) :: dTurbFluxCanopy_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanopy_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanopy_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanopy_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - real(rkind) :: dAdvecFluxCanopy_dTGround ! derivative in net canopy advective fluxes w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dAdvecFluxCanopy_dTCanopy ! derivative in net canopy advective fluxes w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dAdvecFluxCanopy_dCanWat ! derivative in net canopy advective fluxes w.r.t. canopy total water content (J kg-1 s-1) - - ! (flux derivatives -- ground surface) - real(rkind) :: dTurbFluxGround_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dTurbFluxGround_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dTurbFluxGround_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dTurbFluxGround_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - real(rkind) :: dAdvecFluxGround_dTCanair ! derivative in net ground advective fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dAdvecFluxGround_dTCanopy ! derivative in net ground advective fluxes w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dAdvecFluxGround_dTGround ! derivative in net ground advective fluxes w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dAdvecFluxGround_dCanWat ! derivative in net ground advective fluxes w.r.t. canopy total water content (J kg-1 s-1) - - ! (liquid water flux derivatives -- canopy evap) - real(rkind) :: dLatHeatCanopyEvap_dCanWat ! derivative in latent heat of canopy evaporation w.r.t. canopy total water content (W kg-1) - real(rkind) :: dLatHeatCanopyEvap_dTCanair ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dLatHeatCanopyEvap_dTCanopy ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dLatHeatCanopyEvap_dTGround ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) - - ! (liquid water flux derivatives -- ground evap) - real(rkind) :: dLatHeatGroundEvap_dCanWat ! derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) - real(rkind) :: dLatHeatGroundEvap_dTCanair ! derivative in latent heat of ground evaporation w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) - - ! --------------------------------------------------------------------------------------- - ! point to variables in the data structure - ! --------------------------------------------------------------------------------------- - associate(& - - ! input: model decisions - ix_bcUpprTdyn => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision, & ! intent(in): [i4b] choice of upper boundary condition for thermodynamics - ixDerivMethod => model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): [i4b] choice of method to compute derivatives - ix_veg_traits => model_decisions(iLookDECISIONS%veg_traits)%iDecision, & ! intent(in): [i4b] choice of parameterization for vegetation roughness length and displacement height - ix_canopyEmis => model_decisions(iLookDECISIONS%canopyEmis)%iDecision, & ! intent(in): [i4b] choice of parameterization for canopy emissivity - ix_windPrfile => model_decisions(iLookDECISIONS%windPrfile)%iDecision, & ! intent(in): [i4b] choice of canopy wind profile - ix_astability => model_decisions(iLookDECISIONS%astability)%iDecision, & ! intent(in): [i4b] choice of stability function - ix_soilStress => model_decisions(iLookDECISIONS%soilStress)%iDecision, & ! intent(in): [i4b] choice of function for the soil moisture control on stomatal resistance - ix_groundwatr => model_decisions(iLookDECISIONS%groundwatr)%iDecision, & ! intent(in): [i4b] choice of groundwater parameterization - ix_stomResist => model_decisions(iLookDECISIONS%stomResist)%iDecision, & ! intent(in): [i4b] choice of function for stomatal resistance - ix_spatial_gw => model_decisions(iLookDECISIONS%spatial_gw)%iDecision, & ! intent(in): [i4b] choice of groundwater representation (local, basin) - - ! input: layer geometry - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1), & ! intent(in): [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): [i4b] total number of layers - - ! input: physical attributes - vegTypeIndex => type_data%var(iLookTYPE%vegTypeIndex), & ! intent(in): [i4b] vegetation type index - soilTypeIndex => type_data%var(iLookTYPE%soilTypeIndex), & ! intent(in): [i4b] soil type index - - ! input: vegetation parameters - heightCanopyTop => mpar_data%var(iLookPARAM%heightCanopyTop)%dat(1), & ! intent(in): [dp] height at the top of the vegetation canopy (m) - heightCanopyBottom => mpar_data%var(iLookPARAM%heightCanopyBottom)%dat(1), & ! intent(in): [dp] height at the bottom of the vegetation canopy (m) - canopyWettingFactor => mpar_data%var(iLookPARAM%canopyWettingFactor)%dat(1), & ! intent(in): [dp] maximum wetted fraction of the canopy (-) - canopyWettingExp => mpar_data%var(iLookPARAM%canopyWettingExp)%dat(1), & ! intent(in): [dp] exponent in canopy wetting function (-) - scalarCanopyIceMax => diag_data%var(iLookDIAG%scalarCanopyIceMax)%dat(1), & ! intent(in): [dp] maximum interception storage capacity for ice (kg m-2) - scalarCanopyLiqMax => diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1), & ! intent(in): [dp] maximum interception storage capacity for liquid water (kg m-2) - - ! input: vegetation phenology - scalarLAI => diag_data%var(iLookDIAG%scalarLAI)%dat(1), & ! intent(in): [dp] one-sided leaf area index (m2 m-2) - scalarSAI => diag_data%var(iLookDIAG%scalarSAI)%dat(1), & ! intent(in): [dp] one-sided stem area index (m2 m-2) - scalarExposedLAI => diag_data%var(iLookDIAG%scalarExposedLAI)%dat(1), & ! intent(in): [dp] exposed leaf area index after burial by snow (m2 m-2) - scalarExposedSAI => diag_data%var(iLookDIAG%scalarExposedSAI)%dat(1), & ! intent(in): [dp] exposed stem area index after burial by snow (m2 m-2) - scalarGrowingSeasonIndex => diag_data%var(iLookDIAG%scalarGrowingSeasonIndex)%dat(1), & ! intent(in): [dp] growing season index (0=off, 1=on) - scalarFoliageNitrogenFactor => diag_data%var(iLookDIAG%scalarFoliageNitrogenFactor)%dat(1), & ! intent(in): [dp] foliage nitrogen concentration (1.0 = saturated) - - ! input: aerodynamic resistance parameters - z0Snow => mpar_data%var(iLookPARAM%z0Snow)%dat(1), & ! intent(in): [dp] roughness length of snow (m) - z0Soil => mpar_data%var(iLookPARAM%z0Soil)%dat(1), & ! intent(in): [dp] roughness length of soil (m) - z0CanopyParam => mpar_data%var(iLookPARAM%z0Canopy)%dat(1), & ! intent(in): [dp] roughness length of the canopy (m) - zpdFraction => mpar_data%var(iLookPARAM%zpdFraction)%dat(1), & ! intent(in): [dp] zero plane displacement / canopy height (-) - critRichNumber => mpar_data%var(iLookPARAM%critRichNumber)%dat(1), & ! intent(in): [dp] critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam => mpar_data%var(iLookPARAM%Louis79_bparam)%dat(1), & ! intent(in): [dp] parameter in Louis (1979) stability function - Louis79_cStar => mpar_data%var(iLookPARAM%Louis79_cStar)%dat(1), & ! intent(in): [dp] parameter in Louis (1979) stability function - Mahrt87_eScale => mpar_data%var(iLookPARAM%Mahrt87_eScale)%dat(1), & ! intent(in): [dp] exponential scaling factor in the Mahrt (1987) stability function - windReductionParam => mpar_data%var(iLookPARAM%windReductionParam)%dat(1), & ! intent(in): [dp] canopy wind reduction parameter (-) - leafExchangeCoeff => mpar_data%var(iLookPARAM%leafExchangeCoeff)%dat(1), & ! intent(in): [dp] turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) - leafDimension => mpar_data%var(iLookPARAM%leafDimension)%dat(1), & ! intent(in): [dp] characteristic leaf dimension (m) - - ! input: soil stress parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(1), & ! intent(in): [dp] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(1), & ! intent(in): [dp] residual volumetric liquid water content (-) - plantWiltPsi => mpar_data%var(iLookPARAM%plantWiltPsi)%dat(1), & ! intent(in): [dp] matric head at wilting point (m) - soilStressParam => mpar_data%var(iLookPARAM%soilStressParam)%dat(1), & ! intent(in): [dp] parameter in the exponential soil stress function (-) - critSoilWilting => mpar_data%var(iLookPARAM%critSoilWilting)%dat(1), & ! intent(in): [dp] critical vol. liq. water content when plants are wilting (-) - critSoilTranspire => mpar_data%var(iLookPARAM%critSoilTranspire)%dat(1), & ! intent(in): [dp] critical vol. liq. water content when transpiration is limited (-) - critAquiferTranspire => mpar_data%var(iLookPARAM%critAquiferTranspire)%dat(1), & ! intent(in): [dp] critical aquifer storage value when transpiration is limited (m) - minStomatalResistance => mpar_data%var(iLookPARAM%minStomatalResistance)%dat(1), & ! intent(in): [dp] mimimum stomatal resistance (s m-1) - - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - - ! input: forcing at the upper boundary - mHeight => diag_data%var(iLookDIAG%scalarAdjMeasHeight)%dat(1), & ! intent(in): [dp] measurement height (m) - airtemp => forc_data%var(iLookFORCE%airtemp), & ! intent(in): [dp] air temperature at some height above the surface (K) - windspd => forc_data%var(iLookFORCE%windspd), & ! intent(in): [dp] wind speed at some height above the surface (m s-1) - airpres => forc_data%var(iLookFORCE%airpres), & ! intent(in): [dp] air pressure at some height above the surface (Pa) - LWRadAtm => forc_data%var(iLookFORCE%LWRadAtm), & ! intent(in): [dp] downwelling longwave radiation at the upper boundary (W m-2) - scalarVPair => diag_data%var(iLookDIAG%scalarVPair)%dat(1), & ! intent(in): [dp] vapor pressure at some height above the surface (Pa) - scalarO2air => diag_data%var(iLookDIAG%scalarO2air)%dat(1), & ! intent(in): [dp] atmospheric o2 concentration (Pa) - scalarCO2air => diag_data%var(iLookDIAG%scalarCO2air)%dat(1), & ! intent(in): [dp] atmospheric co2 concentration (Pa) - scalarTwetbulb => diag_data%var(iLookDIAG%scalarTwetbulb)%dat(1), & ! intent(in): [dp] wetbulb temperature (K) - scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1), & ! intent(in): [dp] computed rainfall rate (kg m-2 s-1) - scalarSnowfall => flux_data%var(iLookFLUX%scalarSnowfall)%dat(1), & ! intent(in): [dp] computed snowfall rate (kg m-2 s-1) - scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1), & ! intent(in): [dp] rainfall through the vegetation canopy (kg m-2 s-1) - scalarThroughfallSnow => flux_data%var(iLookFLUX%scalarThroughfallSnow)%dat(1), & ! intent(in): [dp] snowfall through the vegetation canopy (kg m-2 s-1) - scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1), & ! intent(in): [dp] liquid drainage from the vegetation canopy (kg m-2 s-1) - scalarCanopySnowUnloading => flux_data%var(iLookFLUX%scalarCanopySnowUnloading)%dat(1), & ! intent(in): [dp] unloading of snow from the vegetion canopy (kg m-2 s-1) - - ! input/output: derivatives in canopy liquid fluxes w.r.t. canopy water - scalarThroughfallRainDeriv => deriv_data%var(iLookDERIV%scalarThroughfallRainDeriv )%dat(1) ,& ! intent(in): [dp] derivative in throughfall w.r.t. canopy liquid water - scalarCanopyLiqDrainageDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDrainageDeriv)%dat(1) ,& ! intent(in): [dp] derivative in canopy drainage w.r.t. canopy liquid water - scalarThroughfallSnowDeriv => deriv_data%var(iLookDERIV%scalarThroughfallSnowDeriv )%dat(1) ,& ! intent(inout): [dp] derivative in snow throughfall w.r.t. canopy ice - scalarCanopySnowUnloadingDeriv => deriv_data%var(iLookDERIV%scalarCanopySnowUnloadingDeriv)%dat(1),& ! intent(inout): [dp] derivative in unloading of snow w.r.t. canopy ice - scalarCanopySnowUnload_TkDeriv => deriv_data%var(iLookDERIV%scalarCanopySnowUnload_TkDeriv)%dat(1),& ! intent(inout): [dp] derivative in unloading of snow w.r.t. canopy air temperature - ! input: water storage - ! NOTE: soil stress only computed at the start of the substep (firstFluxCall=.true.) - scalarSWE => prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(in): [dp] snow water equivalent on the ground (kg m-2) - scalarSnowDepth => prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(in): [dp] snow depth on the ground surface (m) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat, & ! intent(in): [dp(:)] volumetric fraction of liquid water in each layer (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat, & ! intent(in): [dp(:)] matric head in each soil layer (m) - localAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1), & ! intent(in): [dp] aquifer storage for the local column (m) - basinAquiferStorage => bvar_data%var(iLookBVAR%basin__AquiferStorage)%dat(1), & ! intent(in): [dp] aquifer storage for the single basin (m) - - ! input: shortwave radiation fluxes - scalarCanopySunlitLAI => diag_data%var(iLookDIAG%scalarCanopySunlitLAI)%dat(1), & ! intent(in): [dp] sunlit leaf area (-) - scalarCanopyShadedLAI => diag_data%var(iLookDIAG%scalarCanopyShadedLAI)%dat(1), & ! intent(in): [dp] shaded leaf area (-) - scalarCanopySunlitPAR => flux_data%var(iLookFLUX%scalarCanopySunlitPAR)%dat(1), & ! intent(in): [dp] average absorbed par for sunlit leaves (w m-2) - scalarCanopyShadedPAR => flux_data%var(iLookFLUX%scalarCanopyShadedPAR)%dat(1), & ! intent(in): [dp] average absorbed par for shaded leaves (w m-2) - scalarCanopyAbsorbedSolar => flux_data%var(iLookFLUX%scalarCanopyAbsorbedSolar)%dat(1), & ! intent(in): [dp] solar radiation absorbed by canopy (W m-2) - scalarGroundAbsorbedSolar => flux_data%var(iLookFLUX%scalarGroundAbsorbedSolar)%dat(1), & ! intent(in): [dp] solar radiation absorbed by ground (W m-2) - - ! output: fraction of wetted canopy area and fraction of snow on the ground - scalarCanopyWetFraction => diag_data%var(iLookDIAG%scalarCanopyWetFraction)%dat(1), & ! intent(out): [dp] fraction of canopy that is wet - scalarGroundSnowFraction => diag_data%var(iLookDIAG%scalarGroundSnowFraction)%dat(1), & ! intent(out): [dp] fraction of ground covered with snow (-) - - ! output: longwave radiation fluxes - scalarCanopyEmissivity => diag_data%var(iLookDIAG%scalarCanopyEmissivity)%dat(1), & ! intent(out): [dp] effective emissivity of the canopy (-) - scalarLWRadCanopy => flux_data%var(iLookFLUX%scalarLWRadCanopy)%dat(1), & ! intent(out): [dp] longwave radiation emitted from the canopy (W m-2) - scalarLWRadGround => flux_data%var(iLookFLUX%scalarLWRadGround)%dat(1), & ! intent(out): [dp] longwave radiation emitted at the ground surface (W m-2) - scalarLWRadUbound2Canopy => flux_data%var(iLookFLUX%scalarLWRadUbound2Canopy)%dat(1), & ! intent(out): [dp] downward atmospheric longwave radiation absorbed by the canopy (W m-2) - scalarLWRadUbound2Ground => flux_data%var(iLookFLUX%scalarLWRadUbound2Ground)%dat(1), & ! intent(out): [dp] downward atmospheric longwave radiation absorbed by the ground (W m-2) - scalarLWRadUbound2Ubound => flux_data%var(iLookFLUX%scalarLWRadUbound2Ubound)%dat(1), & ! intent(out): [dp] atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) - scalarLWRadCanopy2Ubound => flux_data%var(iLookFLUX%scalarLWRadCanopy2Ubound)%dat(1), & ! intent(out): [dp] longwave radiation emitted from canopy lost thru upper boundary (W m-2) - scalarLWRadCanopy2Ground => flux_data%var(iLookFLUX%scalarLWRadCanopy2Ground)%dat(1), & ! intent(out): [dp] longwave radiation emitted from canopy absorbed by the ground (W m-2) - scalarLWRadCanopy2Canopy => flux_data%var(iLookFLUX%scalarLWRadCanopy2Canopy)%dat(1), & ! intent(out): [dp] canopy longwave reflected from ground and absorbed by the canopy (W m-2) - scalarLWRadGround2Ubound => flux_data%var(iLookFLUX%scalarLWRadGround2Ubound)%dat(1), & ! intent(out): [dp] longwave radiation emitted from ground lost thru upper boundary (W m-2) - scalarLWRadGround2Canopy => flux_data%var(iLookFLUX%scalarLWRadGround2Canopy)%dat(1), & ! intent(out): [dp] longwave radiation emitted from ground and absorbed by the canopy (W m-2) - scalarLWNetCanopy => flux_data%var(iLookFLUX%scalarLWNetCanopy)%dat(1), & ! intent(out): [dp] net longwave radiation at the canopy (W m-2) - scalarLWNetGround => flux_data%var(iLookFLUX%scalarLWNetGround)%dat(1), & ! intent(out): [dp] net longwave radiation at the ground surface (W m-2) - scalarLWNetUbound => flux_data%var(iLookFLUX%scalarLWNetUbound)%dat(1), & ! intent(out): [dp] net longwave radiation at the upper boundary (W m-2) - - ! output: aerodynamic resistance - scalarZ0Canopy => diag_data%var(iLookDIAG%scalarZ0Canopy)%dat(1), & ! intent(out): [dp] roughness length of the canopy (m) - scalarWindReductionFactor => diag_data%var(iLookDIAG%scalarWindReductionFactor)%dat(1), & ! intent(out): [dp] canopy wind reduction factor (-) - scalarZeroPlaneDisplacement => diag_data%var(iLookDIAG%scalarZeroPlaneDisplacement)%dat(1), & ! intent(out): [dp] zero plane displacement (m) - scalarRiBulkCanopy => diag_data%var(iLookDIAG%scalarRiBulkCanopy)%dat(1), & ! intent(out): [dp] bulk Richardson number for the canopy (-) - scalarRiBulkGround => diag_data%var(iLookDIAG%scalarRiBulkGround)%dat(1), & ! intent(out): [dp] bulk Richardson number for the ground surface (-) - scalarEddyDiffusCanopyTop => flux_data%var(iLookFLUX%scalarEddyDiffusCanopyTop)%dat(1), & ! intent(out): [dp] eddy diffusivity for heat at the top of the canopy (m2 s-1) - scalarFrictionVelocity => flux_data%var(iLookFLUX%scalarFrictionVelocity)%dat(1), & ! intent(out): [dp] friction velocity (m s-1) - scalarWindspdCanopyTop => flux_data%var(iLookFLUX%scalarWindspdCanopyTop)%dat(1), & ! intent(out): [dp] windspeed at the top of the canopy (m s-1) - scalarWindspdCanopyBottom => flux_data%var(iLookFLUX%scalarWindspdCanopyBottom)%dat(1), & ! intent(out): [dp] windspeed at the height of the bottom of the canopy (m s-1) - scalarLeafResistance => flux_data%var(iLookFLUX%scalarLeafResistance)%dat(1), & ! intent(out): [dp] mean leaf boundary layer resistance per unit leaf area (s m-1) - scalarGroundResistance => flux_data%var(iLookFLUX%scalarGroundResistance)%dat(1), & ! intent(out): [dp] below canopy aerodynamic resistance (s m-1) - scalarCanopyResistance => flux_data%var(iLookFLUX%scalarCanopyResistance)%dat(1), & ! intent(out): [dp] above canopy aerodynamic resistance (s m-1) - - ! input/output: soil resistance -- intent(in) and intent(inout) because only called at the first flux call - mLayerRootDensity => diag_data%var(iLookDIAG%mLayerRootDensity)%dat, & ! intent(in): [dp] root density in each layer (-) - scalarAquiferRootFrac => diag_data%var(iLookDIAG%scalarAquiferRootFrac)%dat(1), & ! intent(in): [dp] fraction of roots below the lowest soil layer (-) - scalarTranspireLim => diag_data%var(iLookDIAG%scalarTranspireLim)%dat(1), & ! intent(inout): [dp] weighted average of the transpiration limiting factor (-) - mLayerTranspireLim => diag_data%var(iLookDIAG%mLayerTranspireLim)%dat, & ! intent(inout): [dp] transpiration limiting factor in each layer (-) - scalarTranspireLimAqfr => diag_data%var(iLookDIAG%scalarTranspireLimAqfr)%dat(1), & ! intent(inout): [dp] transpiration limiting factor for the aquifer (-) - scalarSoilRelHumidity => diag_data%var(iLookDIAG%scalarSoilRelHumidity)%dat(1), & ! intent(inout): [dp] relative humidity in the soil pores [0-1] - scalarSoilResistance => flux_data%var(iLookFLUX%scalarSoilResistance)%dat(1), & ! intent(inout): [dp] resistance from the soil (s m-1) - - ! input/output: stomatal resistance -- intent(inout) because only called at the first flux call - scalarStomResistSunlit => flux_data%var(iLookFLUX%scalarStomResistSunlit)%dat(1), & ! intent(inout): [dp] stomatal resistance for sunlit leaves (s m-1) - scalarStomResistShaded => flux_data%var(iLookFLUX%scalarStomResistShaded)%dat(1), & ! intent(inout): [dp] stomatal resistance for shaded leaves (s m-1) - scalarPhotosynthesisSunlit => flux_data%var(iLookFLUX%scalarPhotosynthesisSunlit)%dat(1), & ! intent(inout): [dp] sunlit photosynthesis (umolco2 m-2 s-1) - scalarPhotosynthesisShaded => flux_data%var(iLookFLUX%scalarPhotosynthesisShaded)%dat(1), & ! intent(inout): [dp] shaded photosynthesis (umolco2 m-2 s-1) - - ! output: turbulent heat fluxes - scalarLatHeatSubVapCanopy => diag_data%var(iLookDIAG%scalarLatHeatSubVapCanopy)%dat(1), & ! intent(inout): [dp] latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) - scalarLatHeatSubVapGround => diag_data%var(iLookDIAG%scalarLatHeatSubVapGround)%dat(1), & ! intent(inout): [dp] latent heat of sublimation/vaporization for the ground surface (J kg-1) - scalarSatVP_canopyTemp => diag_data%var(iLookDIAG%scalarSatVP_CanopyTemp)%dat(1), & ! intent(out): [dp] saturation vapor pressure at the temperature of the vegetation canopy (Pa) - scalarSatVP_groundTemp => diag_data%var(iLookDIAG%scalarSatVP_GroundTemp)%dat(1), & ! intent(out): [dp] saturation vapor pressure at the temperature of the ground surface (Pa) - scalarSenHeatTotal => flux_data%var(iLookFLUX%scalarSenHeatTotal)%dat(1), & ! intent(out): [dp] sensible heat from the canopy air space to the atmosphere (W m-2) - scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1), & ! intent(out): [dp] sensible heat flux from the canopy to the canopy air space (W m-2) - scalarSenHeatGround => flux_data%var(iLookFLUX%scalarSenHeatGround)%dat(1), & ! intent(out): [dp] sensible heat flux from ground surface below vegetation (W m-2) - scalarLatHeatTotal => flux_data%var(iLookFLUX%scalarLatHeatTotal)%dat(1), & ! intent(out): [dp] latent heat from the canopy air space to the atmosphere (W m-2) - scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1), & ! intent(out): [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - scalarLatHeatCanopyTrans => flux_data%var(iLookFLUX%scalarLatHeatCanopyTrans)%dat(1), & ! intent(out): [dp] latent heat flux for transpiration from the canopy to the canopy air space (W m-2) - scalarLatHeatGround => flux_data%var(iLookFLUX%scalarLatHeatGround)%dat(1), & ! intent(out): [dp] latent heat flux from ground surface below vegetation (W m-2) - - ! output: advective heat fluxes - scalarCanopyAdvectiveHeatFlux => flux_data%var(iLookFLUX%scalarCanopyAdvectiveHeatFlux)%dat(1), & ! intent(out): [dp] heat advected to the canopy surface with rain + snow (W m-2) - scalarGroundAdvectiveHeatFlux => flux_data%var(iLookFLUX%scalarGroundAdvectiveHeatFlux)%dat(1), & ! intent(out): [dp] heat advected to the ground surface with throughfall (W m-2) - - ! output: mass fluxes - scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1), & ! intent(out): [dp] canopy sublimation/frost (kg m-2 s-1) - scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! intent(out): [dp] snow sublimation/frost -- below canopy or non-vegetated (kg m-2 s-1) - - ! input/output: canopy air space variables - scalarVP_CanopyAir => diag_data%var(iLookDIAG%scalarVP_CanopyAir)%dat(1), & ! intent(inout): [dp] vapor pressure of the canopy air space (Pa) - scalarCanopyStabilityCorrection => diag_data%var(iLookDIAG%scalarCanopyStabilityCorrection)%dat(1),& ! intent(inout): [dp] stability correction for the canopy (-) - scalarGroundStabilityCorrection => diag_data%var(iLookDIAG%scalarGroundStabilityCorrection)%dat(1),& ! intent(inout): [dp] stability correction for the ground surface (-) - - ! output: liquid water fluxes - scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1), & ! intent(out): [dp] canopy transpiration (kg m-2 s-1) - scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1), & ! intent(out): [dp] canopy evaporation/condensation (kg m-2 s-1) - scalarGroundEvaporation => flux_data%var(iLookFLUX%scalarGroundEvaporation)%dat(1), & ! intent(out): [dp] ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - - ! output: derived fluxes - scalarTotalET => flux_data%var(iLookFLUX%scalarTotalET)%dat(1), & ! intent(out): [dp] total ET (kg m-2 s-1) - scalarNetRadiation => flux_data%var(iLookFLUX%scalarNetRadiation)%dat(1) & ! intent(out): [dp] net radiation (W m-2) - ) - ! --------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="vegNrgFluxSundials/" - - ! initialize printflag - printflag = .false. - - ! identify the type of boundary condition for thermodynamics - select case(ix_bcUpprTdyn) - - ! ***** - ! (1) DIRICHLET OR ZERO FLUX BOUNDARY CONDITION... - ! ************************************************ - - ! NOTE: Vegetation fluxes are not computed in this case - - ! ** prescribed temperature or zero flux at the upper boundary of the snow-soil system - case(prescribedTemp,zeroFlux) - - ! derived fluxes - scalarTotalET = 0._rkind ! total ET (kg m-2 s-1) - scalarNetRadiation = 0._rkind ! net radiation (W m-2) - ! liquid water fluxes associated with evaporation/transpiration - scalarCanopyTranspiration = 0._rkind ! canopy transpiration (kg m-2 s-1) - scalarCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) - scalarGroundEvaporation = 0._rkind ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - ! solid water fluxes associated with sublimation/frost - scalarCanopySublimation = 0._rkind ! sublimation from the vegetation canopy ((kg m-2 s-1) - scalarSnowSublimation = 0._rkind ! sublimation from the snow surface ((kg m-2 s-1) - ! set canopy fluxes to zero (no canopy) - canairNetFlux = 0._rkind ! net energy flux for the canopy air space (W m-2) - canopyNetFlux = 0._rkind ! net energy flux for the vegetation canopy (W m-2) - ! set canopy derivatives to zero - dCanairNetFlux_dCanairTemp = 0._rkind ! derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) - dCanairNetFlux_dCanopyTemp = 0._rkind ! derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) - dCanairNetFlux_dGroundTemp = 0._rkind ! derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) - dCanopyNetFlux_dCanairTemp = 0._rkind ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) - dCanopyNetFlux_dCanopyTemp = 0._rkind ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) - dCanopyNetFlux_dGroundTemp = 0._rkind ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - dGroundNetFlux_dCanairTemp = 0._rkind ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) - dGroundNetFlux_dCanopyTemp = 0._rkind ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) - ! set liquid flux derivatives to zero (canopy evap) - dCanopyEvaporation_dCanWat = 0._rkind ! derivative in canopy evaporation w.r.t. canopy total water content (s-1) - dCanopyEvaporation_dTCanair= 0._rkind ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTCanopy= 0._rkind ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTGround= 0._rkind ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - ! set liquid flux derivatives to zero (ground evap) - dGroundEvaporation_dCanWat = 0._rkind ! derivative in ground evaporation w.r.t. canopy total water content (s-1) - dGroundEvaporation_dTCanair= 0._rkind ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTCanopy= 0._rkind ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTGround= 0._rkind ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - - ! compute fluxes and derivatives -- separate approach for prescribed temperature and zero flux, - if(ix_bcUpprTdyn == prescribedTemp)then - ! compute ground net flux (W m-2) - groundNetFlux = -diag_data%var(iLookDIAG%iLayerThermalC)%dat(0)*(groundTempTrial - upperBoundTemp)/(prog_data%var(iLookPROG%mLayerDepth)%dat(1)*0.5_rkind) - ! compute derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) inside soil and snow (ssd) energy flux routine - ! dGroundNetFlux_dGroundTemp = missingValue - elseif(ix_bcUpprTdyn == zeroFlux)then - groundNetFlux = 0._rkind - ! dGroundNetFlux_dGroundTemp = missingValue - else - err=20; message=trim(message)//'unable to identify upper boundary condition for thermodynamics: expect the case to be prescribedTemp or zeroFlux'; return - end if - - ! ***** - ! (2) NEUMANN BOUNDARY CONDITION... - ! ********************************* - - ! NOTE 1: This is the main routine for calculating vegetation fluxes - ! NOTE 2: This routine also calculates surface fluxes for the case where vegetation is buried with snow (or bare soil) - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! ***** PRELIMINARIES ********************************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - - ! * flux boundary condition - case(energyFlux) - - ! identify the appropriate groundwater variable - select case(ix_spatial_gw) - case(singleBasin); scalarAquiferStorage = basinAquiferStorage - case(localColumn); scalarAquiferStorage = localAquiferStorage - case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return - end select ! (modify the groundwater representation for this single-column implementation) - - ! set canopy stability corrections to the previous values - scalarCanopyStabilityCorrection_old = scalarCanopyStabilityCorrection ! stability correction for the canopy (-) - scalarGroundStabilityCorrection_old = scalarGroundStabilityCorrection ! stability correction for the ground surface (-) - - ! initialize variables to compute stomatal resistance - if(firstFluxCall .and. firstSubStep)then - ! vapor pressure in the canopy air space initialized as vapor pressure of air above the vegetation canopy - ! NOTE: this is needed for the stomatal resistance calculations - if(scalarVP_CanopyAir < 0._rkind)then - scalarVP_CanopyAir = scalarVPair - 1._rkind ! "small" offset used to assist in checking initial derivative calculations - end if - end if - - ! set latent heat of sublimation/vaporization for canopy and ground surface (Pa/K) - ! NOTE: variables are constant over the substep, to simplify relating energy and mass fluxes - if(firstFluxCall)then - scalarLatHeatSubVapCanopy = getLatentHeatValue(canopyTempTrial) - ! case when there is snow on the ground (EXCLUDE "snow without a layer" -- in this case, evaporate from the soil) - if(nSnow > 0)then - if(groundTempTrial > Tfreeze)then; err=20; message=trim(message)//'do not expect ground temperature > 0 when snow is on the ground'; return; end if - scalarLatHeatSubVapGround = LH_sub ! sublimation from snow - scalarGroundSnowFraction = 1._rkind - ! case when the ground is snow-free - else - scalarLatHeatSubVapGround = LH_vap ! evaporation of water in the soil pores: this occurs even if frozen because of super-cooled water - scalarGroundSnowFraction = 0._rkind - end if ! (if there is snow on the ground) - end if ! (if the first flux call) - !write(*,'(a,1x,10(f30.10,1x))') 'groundTempTrial, scalarLatHeatSubVapGround = ', groundTempTrial, scalarLatHeatSubVapGround - - ! compute the roughness length of the ground (ground below the canopy or non-vegetated surface) - z0Ground = z0soil*(1._rkind - scalarGroundSnowFraction) + z0Snow*scalarGroundSnowFraction ! roughness length (m) - - ! compute the total vegetation area index (leaf plus stem) - VAI = scalarLAI + scalarSAI ! vegetation area index - exposedVAI = scalarExposedLAI + scalarExposedSAI ! exposed vegetation area index - - ! compute emissivity of the canopy (-) - if(computeVegFlux)then - select case(ix_canopyEmis) - ! *** simple exponential function - case(simplExp) - scalarCanopyEmissivity = 1._rkind - exp(-exposedVAI) ! effective emissivity of the canopy (-) - ! *** canopy emissivity parameterized as a function of diffuse transmissivity - case(difTrans) - ! compute the exponential integral - scaleLAI = 0.5_rkind*exposedVAI - expi = expInt(scaleLAI) - ! compute diffuse transmissivity (-) - diffuseTrans = (1._rkind - scaleLAI)*exp(-scaleLAI) + (scaleLAI**2._rkind)*expi - ! compute the canopy emissivity - scalarCanopyEmissivity = (1._rkind - diffuseTrans)*vegEmissivity - ! *** check we found the correct option - case default - err=20; message=trim(message)//'unable to identify option for canopy emissivity'; return - end select - end if - - ! ensure canopy longwave fluxes are zero when not computing canopy fluxes - if(.not.computeVegFlux) scalarCanopyEmissivity=0._rkind - - ! compute emissivity of the ground surface (-) - groundEmissivity = scalarGroundSnowFraction*snowEmissivity + (1._rkind - scalarGroundSnowFraction)*soilEmissivity ! emissivity of the ground surface (-) - - ! compute the fraction of canopy that is wet - ! NOTE: we either sublimate or evaporate over the entire substep - if(computeVegFlux)then - - ! compute the fraction of liquid water in the canopy (-) - totalCanopyWater = canopyLiqTrial + canopyIceTrial - if(totalCanopyWater > tiny(1.0_rkind))then - fracLiquidCanopy = canopyLiqTrial / (canopyLiqTrial + canopyIceTrial) - else - fracLiquidCanopy = 0._rkind - end if - - ! get wetted fraction and derivatives - call wettedFrac(& - ! input - .true., & ! flag to denote if derivative is desired - (ixDerivMethod == numerical), & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) - (scalarLatHeatSubVapCanopy > LH_vap+verySmall), & ! flag to denote if the canopy is frozen - dCanLiq_dTcanopy, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) - fracLiquidCanopy, & ! fraction of liquid water on the canopy (-) - canopyLiqTrial, & ! canopy liquid water (kg m-2) - canopyIceTrial, & ! canopy ice (kg m-2) - scalarCanopyLiqMax, & ! maximum canopy liquid water (kg m-2) - scalarCanopyIceMax, & ! maximum canopy ice content (kg m-2) - canopyWettingFactor, & ! maximum wetted fraction of the canopy (-) - canopyWettingExp, & ! exponent in canopy wetting function (-) - ! output - scalarCanopyWetFraction, & ! canopy wetted fraction (-) - dCanopyWetFraction_dWat, & ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) - dCanopyWetFraction_dT, & ! derivative in wetted fraction w.r.t. canopy temperature (K-1) - err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - else - scalarCanopyWetFraction = 0._rkind ! canopy wetted fraction (-) - dCanopyWetFraction_dWat = 0._rkind ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) - dCanopyWetFraction_dT = 0._rkind ! derivative in wetted fraction w.r.t. canopy temperature (K-1) - end if - !write(*,'(a,1x,L1,1x,f25.15,1x))') 'computeVegFlux, scalarCanopyWetFraction = ', computeVegFlux, scalarCanopyWetFraction - !print*, 'dCanopyWetFraction_dWat = ', dCanopyWetFraction_dWat - !print*, 'dCanopyWetFraction_dT = ', dCanopyWetFraction_dT - !print*, 'canopyLiqTrial = ', canopyLiqTrial - !print*, 'canopyIceTrial = ', canopyIceTrial - !print*, 'scalarCanopyLiqMax = ', scalarCanopyLiqMax - !print*, 'scalarCanopyIceMax = ', scalarCanopyIceMax - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! ***** AERODYNAMIC RESISTANCE ***************************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - - ! NOTE: compute for all iterations - - ! compute aerodynamic resistances - ! Refs: Choudhury and Monteith (4-layer model for heat budget of homogenous surfaces; QJRMS, 1988) - ! Niu and Yang (Canopy effects on snow processes; JGR, 2004) - ! Mahat et al. (Below-canopy turbulence in a snowmelt model, WRR, 2012) - call aeroResist(& - ! input: model control - computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) - (ixDerivMethod == analytical), & ! intent(in): logical flag if would like to compute analytical derivaties - ix_veg_traits, & ! intent(in): choice of parameterization for vegetation roughness length and displacement height - ix_windPrfile, & ! intent(in): choice of canopy wind profile - ix_astability, & ! intent(in): choice of stability function - ! input: above-canopy forcing data - mHeight, & ! intent(in): measurement height (m) - airtemp, & ! intent(in): air temperature at some height above the surface (K) - windspd, & ! intent(in): wind speed at some height above the surface (m s-1) - ! input: canopy and ground temperature - canairTempTrial, & ! intent(in): temperature of the canopy air space (K) - groundTempTrial, & ! intent(in): temperature of the ground surface (K) - ! input: diagnostic variables - exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) - scalarSnowDepth, & ! intent(in): snow depth (m) - ! input: parameters - z0Ground, & ! intent(in): roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) - z0CanopyParam, & ! intent(in): roughness length of the canopy (m) - zpdFraction, & ! intent(in): zero plane displacement / canopy height (-) - critRichNumber, & ! intent(in): critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam, & ! intent(in): parameter in Louis (1979) stability function - Mahrt87_eScale, & ! intent(in): exponential scaling factor in the Mahrt (1987) stability function - windReductionParam, & ! intent(in): canopy wind reduction parameter (-) - leafExchangeCoeff, & ! intent(in): turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) - leafDimension, & ! intent(in): characteristic leaf dimension (m) - heightCanopyTop, & ! intent(in): height at the top of the vegetation canopy (m) - heightCanopyBottom, & ! intent(in): height at the bottom of the vegetation canopy (m) - ! output: stability corrections - scalarRiBulkCanopy, & ! intent(out): bulk Richardson number for the canopy (-) - scalarRiBulkGround, & ! intent(out): bulk Richardson number for the ground surface (-) - scalarCanopyStabilityCorrection, & ! intent(out): stability correction for the canopy (-) - scalarGroundStabilityCorrection, & ! intent(out): stability correction for the ground surface (-) - ! output: scalar resistances - scalarZ0Canopy, & ! intent(out): roughness length of the canopy (m) - scalarWindReductionFactor, & ! intent(out): canopy wind reduction factor (-) - scalarZeroPlaneDisplacement, & ! intent(out): zero plane displacement (m) - scalarEddyDiffusCanopyTop, & ! intent(out): eddy diffusivity for heat at the top of the canopy (m2 s-1) - scalarFrictionVelocity, & ! intent(out): friction velocity (m s-1) - scalarWindspdCanopyTop, & ! intent(out): windspeed at the top of the canopy (m s-1) - scalarWindspdCanopyBottom, & ! intent(out): windspeed at the height of the bottom of the canopy (m s-1) - scalarLeafResistance, & ! intent(out): mean leaf boundary layer resistance per unit leaf area (s m-1) - scalarGroundResistance, & ! intent(out): below canopy aerodynamic resistance (s m-1) - scalarCanopyResistance, & ! intent(out): above canopy aerodynamic resistance (s m-1) - ! output: derivatives in scalar resistances - dGroundResistance_dTGround, & ! intent(out): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - dGroundResistance_dTCanopy, & ! intent(out): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - dGroundResistance_dTCanair, & ! intent(out): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - dCanopyResistance_dTCanopy, & ! intent(out): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - dCanopyResistance_dTCanair, & ! intent(out): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - ! output: error control - err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - !print*, scalarLeafResistance, & ! mean leaf boundary layer resistance per unit leaf area (s m-1) - ! scalarGroundResistance, & ! below canopy aerodynamic resistance (s m-1) - ! scalarCanopyResistance, & ! above canopy aerodynamic resistance (s m-1) - ! '(leaf, ground, canopy)' - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! ***** STOMATAL RESISTANCE ***************************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - - ! stomatal resistance is constant over the SUBSTEP - ! NOTE: This is a simplification, as stomatal resistance does depend on canopy temperature - ! This "short-cut" made because: - ! (1) computations are expensive; - ! (2) derivative calculations are rather complex (iterations within the Ball-Berry routine); and - ! (3) stomatal resistance does not change rapidly - if(firstFluxCall)then - - ! compute the saturation vapor pressure for vegetation temperature - TV_celcius = canopyTempTrial - Tfreeze - call satVapPress(TV_celcius, scalarSatVP_CanopyTemp, dSVPCanopy_dCanopyTemp) - - ! compute soil moisture factor controlling stomatal resistance - call soilResist(& - ! input (model decisions) - ix_soilStress, & ! intent(in): choice of function for the soil moisture control on stomatal resistance - ix_groundwatr, & ! intent(in): groundwater parameterization - ! input (state variables) - mLayerMatricHead(1:nSoil), & ! intent(in): matric head in each soil layer (m) - mLayerVolFracLiq(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water in each soil layer (-) - scalarAquiferStorage, & ! intent(in): aquifer storage (m) - ! input (diagnostic variables) - mLayerRootDensity(1:nSoil), & ! intent(in): root density in each layer (-) - scalarAquiferRootFrac, & ! intent(in): fraction of roots below the lowest soil layer (-) - ! input (parameters) - plantWiltPsi, & ! intent(in): matric head at wilting point (m) - soilStressParam, & ! intent(in): parameter in the exponential soil stress function (-) - critSoilWilting, & ! intent(in): critical vol. liq. water content when plants are wilting (-) - critSoilTranspire, & ! intent(in): critical vol. liq. water content when transpiration is limited (-) - critAquiferTranspire, & ! intent(in): critical aquifer storage value when transpiration is limited (m) - ! output - scalarTranspireLim, & ! intent(out): weighted average of the transpiration limiting factor (-) - mLayerTranspireLim(1:nSoil), & ! intent(out): transpiration limiting factor in each layer (-) - scalarTranspireLimAqfr, & ! intent(out): transpiration limiting factor for the aquifer (-) - err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - !print*, 'weighted average of the soil moiture factor controlling stomatal resistance (-) = ', scalarTranspireLim - - !write(*,'(a,1x,10(f20.10,1x))') 'canopyTempTrial, scalarSatVP_CanopyTemp, scalarVP_CanopyAir = ', & - ! canopyTempTrial, scalarSatVP_CanopyTemp, scalarVP_CanopyAir - - ! compute stomatal resistance - call stomResist(& - ! input (state and diagnostic variables) - canopyTempTrial, & ! intent(in): temperature of the vegetation canopy (K) - scalarSatVP_CanopyTemp, & ! intent(in): saturation vapor pressure at the temperature of the veg canopy (Pa) - scalarVP_CanopyAir, & ! intent(in): canopy air vapor pressure (Pa) - ! input: data structures - type_data, & ! intent(in): type of vegetation and soil - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - model_decisions, & ! intent(in): model decisions - ! input-output: data structures - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output: error control - err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - end if ! (if the first flux call in a given sub-step) - - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! ***** LONGWAVE RADIATION ***************************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - - ! compute canopy longwave radiation balance - call longwaveBal(& - ! input: model control - ixDerivMethod, & ! intent(in): method used to calculate flux derivatives - computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation - ! input: canopy and ground temperature - canopyTempTrial, & ! intent(in): temperature of the vegetation canopy (K) - groundTempTrial, & ! intent(in): temperature of the ground surface (K) - ! input: canopy and ground emissivity - scalarCanopyEmissivity, & ! intent(in): canopy emissivity (-) - groundEmissivity, & ! intent(in): ground emissivity (-) - ! input: forcing - LWRadAtm, & ! intent(in): downwelling longwave radiation at the upper boundary (W m-2) - ! output: emitted radiation from the canopy and ground - scalarLWRadCanopy, & ! intent(out): longwave radiation emitted from the canopy (W m-2) - scalarLWRadGround, & ! intent(out): longwave radiation emitted at the ground surface (W m-2) - ! output: individual fluxes - scalarLWRadUbound2Canopy, & ! intent(out): downward atmospheric longwave radiation absorbed by the canopy (W m-2) - scalarLWRadUbound2Ground, & ! intent(out): downward atmospheric longwave radiation absorbed by the ground (W m-2) - scalarLWRadUbound2Ubound, & ! intent(out): atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) - scalarLWRadCanopy2Ubound, & ! intent(out): longwave radiation emitted from canopy lost thru upper boundary (W m-2) - scalarLWRadCanopy2Ground, & ! intent(out): longwave radiation emitted from canopy absorbed by the ground (W m-2) - scalarLWRadCanopy2Canopy, & ! intent(out): canopy longwave reflected from ground and absorbed by the canopy (W m-2) - scalarLWRadGround2Ubound, & ! intent(out): longwave radiation emitted from ground lost thru upper boundary (W m-2) - scalarLWRadGround2Canopy, & ! intent(out): longwave radiation emitted from ground and absorbed by the canopy (W m-2) - ! output: net fluxes - scalarLWNetCanopy, & ! intent(out): net longwave radiation at the canopy (W m-2) - scalarLWNetGround, & ! intent(out): net longwave radiation at the ground surface (W m-2) - scalarLWNetUbound, & ! intent(out): net longwave radiation at the upper boundary (W m-2) - ! output: flux derivatives - dLWNetCanopy_dTCanopy, & ! intent(out): derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) - dLWNetGround_dTGround, & ! intent(out): derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetCanopy_dTGround, & ! intent(out): derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetGround_dTCanopy, & ! intent(out): derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) - ! output: error control - err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - !print*, 'dLWNetCanopy_dTGround = ', dLWNetCanopy_dTGround - - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! ***** TURBULENT HEAT FLUXES ************************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - - ! check the need to compute numerical derivatives - if(ixDerivMethod == numerical)then - nFlux=5 ! compute the derivatives using one-sided finite differences - else - nFlux=1 ! compute analytical derivatives - end if - - ! either one or multiple flux calls, depending on if using analytical or numerical derivatives - do itry=nFlux,1,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) - - ! ------------------------------------------------------------------------------------- - ! state perturbations for numerical deriavtives with one-sided finite differences - ! note: no perturbations performed using analytical derivatives (nFlux=1) - ! ------------------------------------------------------------------------------------- - - ! identify the type of perturbation - select case(itry) - - ! un-perturbed case - case(unperturbed) - groundTemp = groundTempTrial - canopyTemp = canopyTempTrial - canairTemp = canairTempTrial - canopyWat = totalCanopyWater - - ! perturb ground temperature - case(perturbStateGround) - groundTemp = groundTempTrial + dx - canopyTemp = canopyTempTrial - canairTemp = canairTempTrial - canopyWat = totalCanopyWater - - ! perturb canopy temperature - case(perturbStateCanopy) - groundTemp = groundTempTrial - canopyTemp = canopyTempTrial + dx - canairTemp = canairTempTrial - canopyWat = totalCanopyWater - - ! perturb canopy air temperature - case(perturbStateCanair) - groundTemp = groundTempTrial - canopyTemp = canopyTempTrial - canairTemp = canairTempTrial + dx - canopyWat = totalCanopyWater - - ! perturb canopy total water content - case(perturbStateCanWat) - groundTemp = groundTempTrial - canopyTemp = canopyTempTrial - canairTemp = canairTempTrial - canopyWat = totalCanopyWater + dx - - ! check for an unknown perturbation - case default; err=10; message=trim(message)//"unknown perturbation"; return - - end select ! (type of perturbation) - - ! recalculate liquid and ice from total water - canopyWetFraction = scalarCanopyWetFraction - fracLiquidCanopy = fracliquid(canopyTemp,snowfrz_scale) - canopyLiq = fracLiquidCanopy*canopyWat - canopyIce = (1._rkind - fracLiquidCanopy)*canopyWat - - ! perturbations in canopy total water content affect canopy wetted fraction - if(itry == perturbStateCanopy .OR. itry == perturbStateCanWat)then - if(computeVegFlux)then - call wettedFrac(& - ! input - .false., & ! flag to denote if derivative is desired - (ixDerivMethod == numerical), & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) - (scalarLatHeatSubVapCanopy > LH_vap+verySmall), & ! flag to denote if the canopy is frozen - dCanLiq_dTcanopy, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) - fracLiquidCanopy, & ! fraction of liquid water on the canopy (-) - canopyLiq, & ! canopy liquid water (kg m-2) - canopyIce, & ! canopy ice (kg m-2) - scalarCanopyLiqMax, & ! maximum canopy liquid water (kg m-2) - scalarCanopyIceMax, & ! maximum canopy ice content (kg m-2) - canopyWettingFactor, & ! maximum wetted fraction of the canopy (-) - canopyWettingExp, & ! exponent in canopy wetting function (-) - ! output - canopyWetFraction, & ! canopy wetted fraction (-) - dCanopyWetFraction_dWat, & ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) - dCanopyWetFraction_dT, & ! derivative in wetted fraction w.r.t. canopy temperature (K-1) - err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - else - canopyWetFraction = 0._rkind - end if ! (desired computing vegetation flux) - - end if ! (re-computing wetted fraction for perturbed cases) - !print*, 'wetted fraction derivative = ', (canopyWetFraction - scalarCanopyWetFraction)/dx - !pause - - ! compute the saturation vapor pressure for vegetation temperature - ! NOTE: saturated vapor pressure derivatives don't seem that accurate.... - TV_celcius = canopyTemp - Tfreeze - call satVapPress(TV_celcius, scalarSatVP_CanopyTemp, dSVPCanopy_dCanopyTemp) - - ! compute the saturation vapor pressure for ground temperature - ! NOTE: saturated vapor pressure derivatives don't seem that accurate.... - TG_celcius = groundTemp - Tfreeze - call satVapPress(TG_celcius, scalarSatVP_GroundTemp, dSVPGround_dGroundTemp) - - ! ------------------------------------------------------------------------------------- - ! calculation block (unperturbed fluxes returned [computed last]) - ! ------------------------------------------------------------------------------------- - - ! re-compute aerodynamic resistances for perturbed cases - ! NOTE: unperturbed fluxes computed earlier, and not over-written - if(itry /= unperturbed)then - call aeroResist(& - ! input: model control - computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) - .false., & ! intent(in): logical flag if would like to compute analytical derivaties - ix_veg_traits, & ! intent(in): choice of parameterization for vegetation roughness length and displacement height - ix_windPrfile, & ! intent(in): choice of canopy wind profile - ix_astability, & ! intent(in): choice of stability function - ! input: above-canopy forcing data - mHeight, & ! intent(in): measurement height (m) - airtemp, & ! intent(in): air temperature at some height above the surface (K) - windspd, & ! intent(in): wind speed at some height above the surface (m s-1) - ! input: temperature (canopy, ground, canopy air space) - canairTemp, & ! intent(in): temperature of the canopy air space (K) - groundTemp, & ! intent(in): ground temperature (K) - ! input: diagnostic variables - exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) - scalarSnowDepth, & ! intent(in): snow depth (m) - ! input: parameters - z0Ground, & ! intent(in): roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) - z0CanopyParam, & ! intent(in): roughness length of the canopy (m) - zpdFraction, & ! intent(in): zero plane displacement / canopy height (-) - critRichNumber, & ! intent(in): critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam, & ! intent(in): parameter in Louis (1979) stability function - Mahrt87_eScale, & ! intent(in): exponential scaling factor in the Mahrt (1987) stability function - windReductionParam, & ! intent(in): canopy wind reduction parameter (-) - leafExchangeCoeff, & ! intent(in): turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) - leafDimension, & ! intent(in): characteristic leaf dimension (m) - heightCanopyTop, & ! intent(in): height at the top of the vegetation canopy (m) - heightCanopyBottom, & ! intent(in): height at the bottom of the vegetation canopy (m) - ! output: stability corrections - notUsed_RiBulkCanopy, & ! intent(out): bulk Richardson number for the canopy (-) - notUsed_RiBulkGround, & ! intent(out): bulk Richardson number for the ground surface (-) - notUsed_scalarCanopyStabilityCorrection, & ! intent(out): stability correction for the canopy (-) - notUsed_scalarGroundStabilityCorrection, & ! intent(out): stability correction for the ground surface (-) - ! output: scalar resistances - notUsed_z0Canopy, & ! intent(out): roughness length of the canopy (m) - notUsed_WindReductionFactor, & ! intent(out): canopy wind reduction factor (-) - notUsed_ZeroPlaneDisplacement, & ! intent(out): zero plane displacement (m) - notUsed_EddyDiffusCanopyTop, & ! intent(out): eddy diffusivity for heat at the top of the canopy (m2 s-1) - notUsed_FrictionVelocity, & ! intent(out): friction velocity (m s-1) - notUsed_WindspdCanopyTop, & ! intent(out): windspeed at the top of the canopy (m s-1) - notUsed_WindspdCanopyBottom, & ! intent(out): windspeed at the height of the bottom of the canopy (m s-1) - trialLeafResistance, & ! intent(out): mean leaf boundary layer resistance per unit leaf area (s m-1) - trialGroundResistance, & ! intent(out): below canopy aerodynamic resistance (s m-1) - trialCanopyResistance, & ! intent(out): above canopy aerodynamic resistance (s m-1) - ! output: derivatives in scalar resistances - notUsed_dGroundResistance_dTGround, & ! intent(out): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - notUsed_dGroundResistance_dTCanopy, & ! intent(out): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - notUsed_dGroundResistance_dTCanair, & ! intent(out): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - notUsed_dCanopyResistance_dTCanopy, & ! intent(out): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - notUsed_dCanopyResistance_dTCanair, & ! intent(out): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - ! output: error control - err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - - ! assign scalar resistances for un-perturbed cases - else - trialLeafResistance = scalarLeafResistance - trialGroundResistance = scalarGroundResistance - trialCanopyResistance = scalarCanopyResistance - - end if ! (re-computing resistances for perturbed cases) - !print*, 'trialLeafResistance = ', trialLeafResistance - !print*, 'trialGroundResistance = ', trialGroundResistance - !print*, 'trialCanopyResistance = ', trialCanopyResistance - - ! compute the relative humidity in the top soil layer and the resistance at the ground surface - ! NOTE: computations are based on start-of-step values, so only compute for the first flux call - if(firstFluxCall)then - ! (soil water derivatives with ground water and temp) - dGroundVolFracLiq_dPsi0 = dTheta_dPsi(mLayerMatricHead(1),vGn_alpha(1),theta_res,theta_sat,vGn_n(1),vGn_m(1)) - dGroundVolFracLiq_dTk = dTheta_dTk(groundTemp,theta_res,theta_sat,vGn_alpha(1),vGn_n(1),vGn_m(1)) - ! (soil water evaporation factor [0-1]) - soilEvapFactor = mLayerVolFracLiq(nSnow+1)/(theta_sat - theta_res) - ! (resistance from the soil [s m-1]) - scalarSoilResistance = scalarGroundSnowFraction*1._rkind + (1._rkind - scalarGroundSnowFraction)*EXP(8.25_rkind - 4.225_rkind*soilEvapFactor) ! Sellers (1992) - !scalarSoilResistance = scalarGroundSnowFraction*0._rkind + (1._rkind - scalarGroundSnowFraction)*exp(8.25_rkind - 6.0_rkind*soilEvapFactor) ! Niu adjustment to decrease resistance for wet soil - ! (soil resistence derivatives with ground water and temp) - !dSoilResistance_dWat = -scalarGroundSnowFraction*EXP(8.25_rkind - 4.225_rkind*soilEvapFactor) * (-4.225_rkind*dGroundVolFracLiq_dPsi0/(theta_sat - theta_res)) ! Sellers (1992) - !dSoilResistance_dT = -scalarGroundSnowFraction*EXP(8.25_rkind - 4.225_rkind*soilEvapFactor) * (-4.225_rkind*dGroundVolFracLiq_dTk/(theta_sat - theta_res)) ! Sellers (1992) - !dSoilResistance_dWat = -scalarGroundSnowFraction*EXP(8.25_rkind - 6.0_rkind*soilEvapFactor) * (-6.0_rkind*dGroundVolFracLiq_dPsi0/(theta_sat - theta_res)) ! Niu adjustment - !dSoilResistance_dT = -scalarGroundSnowFraction*EXP(8.25_rkind - 6.0_rkind*soilEvapFactor) * (-6.0_rkind*dGroundVolFracLiq_dTk/(theta_sat - theta_res)) ! Niu adjustment - ! (relative humidity in the soil pores [0-1]) - if(mLayerMatricHead(1) > -1.e+6_rkind)then ! avoid problems with numerical precision when soil is very dry - soilRelHumidity_noSnow = exp( (mLayerMatricHead(1)*gravity) / (groundTemp*R_wv) ) - else - soilRelHumidity_noSnow = 0._rkind - end if ! (if matric head is very low) - scalarSoilRelHumidity = scalarGroundSnowFraction*1._rkind + (1._rkind - scalarGroundSnowFraction)*soilRelHumidity_noSnow - ! (relative humidity derivatives with ground water and tems) - ! using dSoilRelH_noSnow_dWat = soilRelHumidity_noSnow* gravity / (groundTemp*R_wv) and dSoilRelH_noSnow_dT = soilRelHumidity_noSnow* (-mLayerMatricHead(1)*gravity) / ( (groundTemp**2._rkind) *R_wv ) - !dSoilRelHumidity_dWat = (1._rkind - scalarGroundSnowFraction)*soilRelHumidity_noSnow * gravity / (groundTemp*R_wv) - !dSoilRelHumidity_dT = (1._rkind - scalarGroundSnowFraction)*soilRelHumidity_noSnow * (-mLayerMatricHead(1)*gravity) / ( (groundTemp**2._rkind) *R_wv ) - !print*, 'mLayerMatricHead(1), scalarSoilRelHumidity = ', mLayerMatricHead(1), scalarSoilRelHumidity - end if ! (if the first flux call) - - ! compute turbulent heat fluxes - call turbFluxes(& - ! input: model control - computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) - ixDerivMethod, & ! intent(in): method used to calculate flux derivatives - ! input: above-canopy forcing data - airtemp, & ! intent(in): air temperature at some height above the surface (K) - airpres, & ! intent(in): air pressure of the air above the vegetation canopy (Pa) - scalarVPair, & ! intent(in): vapor pressure of the air above the vegetation canopy (Pa) - ! input: latent heat of sublimation/vaporization - scalarLatHeatSubVapCanopy, & ! intent(in): latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) - scalarLatHeatSubVapGround, & ! intent(in): latent heat of sublimation/vaporization for the ground surface (J kg-1) - ! input: canopy/ground temperature and saturated vapor pressure - canairTemp, & ! intent(in): temperature of the canopy air space (K) - canopyTemp, & ! intent(in): canopy temperature (K) - groundTemp, & ! intent(in): ground temperature (K) - scalarSatVP_CanopyTemp, & ! intent(in): saturation vapor pressure at the temperature of the veg canopy (Pa) - scalarSatVP_GroundTemp, & ! intent(in): saturation vapor pressure at the temperature of the ground (Pa) - dSVPCanopy_dCanopyTemp, & ! intent(in): derivative in canopy saturation vapor pressure w.r.t. canopy temperature (Pa K-1) - dSVPGround_dGroundTemp, & ! intent(in): derivative in ground saturation vapor pressure w.r.t. ground temperature (Pa K-1) - ! input: diagnostic variables - exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) - canopyWetFraction, & ! intent(in): trial value for the fraction of canopy that is wet [0-1] - dCanopyWetFraction_dWat, & ! intent(in): derivative in the canopy wetted fraction w.r.t. total water content (kg-1 m-2) - dCanopyWetFraction_dT, & ! intent(in): derivative in wetted fraction w.r.t. canopy temperature (K-1) - scalarCanopySunlitLAI, & ! intent(in): sunlit leaf area (-) - scalarCanopyShadedLAI, & ! intent(in): shaded leaf area (-) - scalarSoilRelHumidity, & ! intent(in): relative humidity in the soil pores [0-1] - scalarSoilResistance, & ! intent(in): resistance from the soil (s m-1) - trialLeafResistance, & ! intent(in): mean leaf boundary layer resistance per unit leaf area (s m-1) - trialGroundResistance, & ! intent(in): below canopy aerodynamic resistance (s m-1) - trialCanopyResistance, & ! intent(in): above canopy aerodynamic resistance (s m-1) - scalarStomResistSunlit, & ! intent(in): stomatal resistance for sunlit leaves (s m-1) - scalarStomResistShaded, & ! intent(in): stomatal resistance for shaded leaves (s m-1) - ! input: derivatives in scalar resistances - dGroundResistance_dTGround, & ! intent(in): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - dGroundResistance_dTCanopy, & ! intent(in): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - dGroundResistance_dTCanair, & ! intent(in): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - dCanopyResistance_dTCanopy, & ! intent(in): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - dCanopyResistance_dTCanair, & ! intent(in): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - ! output: conductances (used to check derivative calculations) - scalarLeafConductance, & ! intent(out): leaf conductance (m s-1) - scalarCanopyConductance, & ! intent(out): canopy conductance (m s-1) - scalarGroundConductanceSH, & ! intent(out): ground conductance for sensible heat (m s-1) - scalarGroundConductanceLH, & ! intent(out): ground conductance for latent heat -- includes soil resistance (m s-1) - scalarEvapConductance, & ! intent(out): conductance for evaporation (m s-1) - scalarTransConductance, & ! intent(out): conductance for transpiration (m s-1) - scalarTotalConductanceSH, & ! intent(out): total conductance for sensible heat (m s-1) - scalarTotalConductanceLH, & ! intent(out): total conductance for latent heat (m s-1) - ! output: canopy air space variables - scalarVP_CanopyAir, & ! intent(out): vapor pressure of the canopy air space (Pa) - ! output: fluxes from the vegetation canopy - scalarSenHeatCanopy, & ! intent(out): sensible heat flux from the canopy to the canopy air space (W m-2) - scalarLatHeatCanopyEvap, & ! intent(out): latent heat flux associated with evaporation from the canopy to the canopy air space (W m-2) - scalarLatHeatCanopyTrans, & ! intent(out): latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) - ! output: fluxes from non-vegetated surfaces (ground surface below vegetation, bare ground, or snow covered vegetation) - scalarSenHeatGround, & ! intent(out): sensible heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) - scalarLatHeatGround, & ! intent(out): latent heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) - ! output: total heat fluxes to the atmosphere - scalarSenHeatTotal, & ! intent(out): total sensible heat flux to the atmosphere (W m-2) - scalarLatHeatTotal, & ! intent(out): total latent heat flux to the atmosphere (W m-2) - ! output: net fluxes - turbFluxCanair, & ! intent(out): net turbulent heat fluxes at the canopy air space (W m-2) - turbFluxCanopy, & ! intent(out): net turbulent heat fluxes at the canopy (W m-2) - turbFluxGround, & ! intent(out): net turbulent heat fluxes at the ground surface (W m-2) - ! output: energy flux derivatives - dTurbFluxCanair_dTCanair, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxCanair_dTCanopy, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxCanair_dTGround, & ! intent(out): derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanair, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanopy, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxCanopy_dTGround, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxGround_dTCanair, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxGround_dTCanopy, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxGround_dTGround, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - ! output: liquid flux derivatives (canopy evap) - dLatHeatCanopyEvap_dCanWat, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy total water content (W kg-1) - dLatHeatCanopyEvap_dTCanair, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTCanopy, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTGround, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) - ! output: liquid flux derivatives (ground evap) - dLatHeatGroundEvap_dCanWat, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) - dLatHeatGroundEvap_dTCanair, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy air temperature - dLatHeatGroundEvap_dTCanopy, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy temperature - dLatHeatGroundEvap_dTGround, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. ground temperature - ! output: cross derivatives - dTurbFluxCanair_dCanWat, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) - dTurbFluxCanopy_dCanWat, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - dTurbFluxGround_dCanWat, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - ! output: error control - err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - !write(*,'(a,f25.15)') 'scalarSenHeatTotal = ', scalarSenHeatTotal - !write(*,'(a,f25.15)') 'scalarSenHeatCanopy = ', scalarSenHeatCanopy - !write(*,'(a,f25.15)') 'scalarLatHeatCanopyEvap = ', scalarLatHeatCanopyEvap - !write(*,'(a,f25.15)') 'scalarLatHeatCanopyTrans = ', scalarLatHeatCanopyTrans - - !print*, 'scalarSenHeatGround = ', scalarSenHeatGround - !print*, 'scalarLatHeatGround = ', scalarLatHeatGround - - !notUsed_scalarCanopyStabilityCorrection ! stability correction for the canopy (-) - !notUsed_scalarGroundStabilityCorrection ! stability correction for the ground surface (-) - !notUsed_EddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) - !notUsed_FrictionVelocity ! friction velocity (m s-1) - !notUsed_WindspdCanopyTop ! windspeed at the top of the canopy (m s-1) - !notUsed_WindspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) - !trialLeafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) - !trialGroundResistance ! below canopy aerodynamic resistance (s m-1) - !trialCanopyResistance ! above canopy aerodynamic resistance (s m-1) - - ! save perturbed fluxes - if(ixDerivMethod == numerical)then - select case(itry) ! (select type of perturbation) - case(unperturbed) - try0 = turbFluxGround - exit - case(perturbStateCanair) - turbFluxCanair_dStateCanair = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) - turbFluxCanopy_dStateCanair = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) - turbFluxGround_dStateCanair = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) - latHeatCanEvap_dStateCanair = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) - case(perturbStateCanopy) - turbFluxCanair_dStateCanopy = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) - turbFluxCanopy_dStateCanopy = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) - turbFluxGround_dStateCanopy = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) - latHeatCanEvap_dStateCanopy = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) - case(perturbStateGround) - try1 = turbFluxGround - turbFluxCanair_dStateGround = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) - turbFluxCanopy_dStateGround = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) - turbFluxGround_dStateGround = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) - latHeatCanEvap_dStateGround = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) - case(perturbStateCanWat) - turbFluxCanair_dStateCanWat = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) - turbFluxCanopy_dStateCanWat = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) - turbFluxGround_dStateCanWat = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) - latHeatCanEvap_dStateCanWat = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) - case default; err=10; message=trim(message)//"unknown perturbation"; return - end select ! (type of perturbation) - end if ! (if numerical) - - end do ! (looping through different flux perturbations) - - ! test derivative - !if(ixDerivMethod == numerical) print*, 'try0, try1 = ', try0, try1 - !if(ixDerivMethod == numerical) print*, 'derivative = ', (ixDerivMethod == numerical), (try1 - try0)/dx - !if(ixDerivMethod == analytical) print*, 'derivative = ', (ixDerivMethod == numerical), dTurbFluxGround_dTGround - !pause - - ! compute numerical derivatives - if(ixDerivMethod == numerical)then - ! derivatives w.r.t. canopy air temperature - dTurbFluxCanair_dTCanair = (turbFluxCanair_dStateCanair - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanair = (turbFluxCanopy_dStateCanair - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxGround_dTCanair = (turbFluxGround_dStateCanair - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTCanair = (latHeatCanEvap_dStateCanair - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) - ! derivatives w.r.t. canopy temperature - dTurbFluxCanair_dTCanopy = (turbFluxCanair_dStateCanopy - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanopy = (turbFluxCanopy_dStateCanopy - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxGround_dTCanopy = (turbFluxGround_dStateCanopy - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTCanopy = (latHeatCanEvap_dStateCanopy - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) - ! derivatives w.r.t. ground temperature - dTurbFluxCanair_dTGround = (turbFluxCanair_dStateGround - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxCanopy_dTGround = (turbFluxCanopy_dStateGround - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxGround_dTGround = (turbFluxGround_dStateGround - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTGround = (latHeatCanEvap_dStateGround - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) - ! derivatives w.r.t. canopy total water content - dTurbFluxCanair_dCanWat = (turbFluxCanair_dStateCanWat - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) - dTurbFluxCanopy_dCanWat = (turbFluxCanopy_dStateCanWat - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - dTurbFluxGround_dCanWat = (turbFluxGround_dStateCanWat - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - dLatHeatCanopyEvap_dCanWat = (latHeatCanEvap_dStateCanWat - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy total water content (J kg-1 s-1) - end if - !if(heightCanopyBottom < scalarSnowDepth+z0Ground) pause 'bottom of the canopy is covered' - - ! test - !print*, (ixDerivMethod == numerical) - !print*, 'dTurbFluxCanair_dTCanair = ', dTurbFluxCanair_dTCanair - !print*, 'dTurbFluxCanair_dTCanopy = ', dTurbFluxCanair_dTCanopy - !print*, 'dTurbFluxCanair_dTGround = ', dTurbFluxCanair_dTGround - !print*, 'dTurbFluxCanopy_dTCanair = ', dTurbFluxCanopy_dTCanair - !print*, 'dTurbFluxCanopy_dTCanopy = ', dTurbFluxCanopy_dTCanopy - !print*, 'dTurbFluxCanopy_dTGround = ', dTurbFluxCanopy_dTGround - !print*, 'dTurbFluxGround_dTCanair = ', dTurbFluxGround_dTCanair - !print*, 'dTurbFluxGround_dTCanopy = ', dTurbFluxGround_dTCanopy - !print*, 'dTurbFluxGround_dTGround = ', dTurbFluxGround_dTGround - !print*, 'dLatHeatCanopyEvap_dCanWat = ', dLatHeatCanopyEvap_dCanWat - !print*, 'dLatHeatCanopyEvap_dTCanair = ', dLatHeatCanopyEvap_dTCanair - !print*, 'dLatHeatCanopyEvap_dTCanopy = ', dLatHeatCanopyEvap_dTCanopy - !print*, 'dLatHeatCanopyEvap_dTGround = ', dLatHeatCanopyEvap_dTGround - !print*, 'dTurbFluxCanair_dCanWat = ', dTurbFluxCanair_dCanWat - !print*, 'dTurbFluxCanopy_dCanWat = ', dTurbFluxCanopy_dCanWat - !print*, 'dTurbFluxGround_dCanWat = ', dTurbFluxGround_dCanWat - !print*, '*****' - !pause - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! ***** ADVECTIVE HEAT FLUXES ************************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - - !print*, 'scalarRainfall, scalarThroughfallRain, scalarSnowfall, scalarThroughfallSnow, canopyTempTrial, scalarTwetbulb = ', & - ! scalarRainfall, scalarThroughfallRain, scalarSnowfall, scalarThroughfallSnow, canopyTempTrial, scalarTwetbulb - - ! check the need to compute numerical derivatives - if(ixDerivMethod == numerical)then - nFlux=5 ! compute the derivatives using one-sided finite differences - else - nFlux=1 ! compute analytical derivatives - end if - ! set unperturbed case throughfall rain and derivatives - throughfallRain = scalarThroughfallRain - canopyLiqDrainage = scalarCanopyLiqDrainage - throughfallRainDeriv = scalarThroughfallRainDeriv - canopyLiqDrainageDeriv = scalarCanopyLiqDrainageDeriv - - ! set unperturbed case throughfall snow, derivatives will need to call - throughfallSnow = scalarThroughfallSnow - canopySnowUnloading = scalarCanopySnowUnloading - - ! set unperturbed case total water - totalCanopyWater = canopyLiqTrial + canopyIceTrial - - ! either one or multiple flux calls, depending on if using analytical or numerical derivatives - do itry=nFlux,1,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) - - ! ------------------------------------------------------------------------------------- - ! state perturbations for numerical deriavtives with one-sided finite differences - ! note: no perturbations performed using analytical derivatives (nFlux=1) - ! ------------------------------------------------------------------------------------- - - ! identify the type of perturbation - select case(itry) - - ! un-perturbed case - case(unperturbed) - groundTemp = groundTempTrial - canopyTemp = canopyTempTrial - canairTemp = canairTempTrial - canopyWat = totalCanopyWater - - ! perturb ground temperature - case(perturbStateGround) - groundTemp = groundTempTrial + dx - canopyTemp = canopyTempTrial - canairTemp = canairTempTrial - canopyWat = totalCanopyWater - - ! perturb canopy temperature - case(perturbStateCanopy) - groundTemp = groundTempTrial - canopyTemp = canopyTempTrial + dx - canairTemp = canairTempTrial - canopyWat = totalCanopyWater - - ! perturb canopy air temperature - case(perturbStateCanair) - groundTemp = groundTempTrial - canopyTemp = canopyTempTrial - canairTemp = canairTempTrial + dx - canopyWat = totalCanopyWater - - ! perturb canopy water content - case(perturbStateCanWat) - groundTemp = groundTempTrial - canopyTemp = canopyTempTrial - canairTemp = canairTempTrial - canopyWat = totalCanopyWater + dx - - ! check for an unknown perturbation - case default; err=10; message=trim(message)//"unknown perturbation"; return - - end select ! (type of perturbation) - - ! ------------------------------------------------------------------------------------- - ! calculation block (unperturbed fluxes returned [computed last]) - ! ------------------------------------------------------------------------------------- - - ! recalculate liquid and ice from total water - fracLiquidCanopy = fracliquid(canopyTemp,snowfrz_scale) - canopyLiq = fracLiquidCanopy*canopyWat - canopyIce = (1._rkind - fracLiquidCanopy)*canopyWat - - if(itry == perturbStateCanopy .OR. itry == perturbStateCanWat)then - ! perturbations in canopy total water content through canopy temperature or total water perturbations will affect canopy rain throughfall - call vegLiqFlux(& - ! input - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation - canopyLiq, & ! intent(in): trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) - scalarRainfall, & ! intent(in): rainfall rate (kg m-2 s-1) - ! input-output: data structures - mpar_data, & ! intent(in): model parameters - diag_data, & ! intent(in): local HRU diagnostic model variables - ! output - throughfallRain, & ! intent(out): rain that reaches the ground without ever touching the canopy (kg m-2 s-1) - canopyLiqDrainage, & ! intent(out): drainage of liquid water from the vegetation canopy (kg m-2 s-1) - throughfallRainDeriv, & ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) - canopyLiqDrainageDeriv, & ! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - end if ! (re-computing rain throughfall for perturbed cases) - - if(itry /= perturbStateGround)then !if(itry /= unperturbed .AND. itry /= perturbStateGround)then ! ideally since already called this in coupled_em_module would have saved the derivatives, but we did not - call canopySnow(& - ! input: model control - data_step, & ! intent(in): time step (seconds) - exposedVAI, & ! intent(in): exposed vegetation area index (m2 m-2) - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation - canopyIce, & ! intent(inout): trial mass of ice on the vegetation canopy at the current iteration (kg m-2) - canairTemp, & ! intent(in): temperature of the canopy air space (k) - scalarSnowfall, & ! intent(in): computed snowfall rate (kg m-2 s-1) - canopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) - scalarWindspdCanopyTop, & ! intent(in): windspeed at the top of the canopy (m s-1) - ! input/output: data structures - model_decisions, & ! intent(in): model decisions - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - diag_data, & ! intent(in): model diagnostic variables for a local HRU - ! output - throughfallSnow, & ! intent(out): snow that reaches the ground without ever touching the canopy (kg m-2 s-1) - canopySnowUnloading, & ! intent(out): unloading of snow from the vegetion canopy (kg m-2 s-1) - throughfallSnowDeriv, & ! intent(out): derivative in throughfall w.r.t. canopy ice (s-1) - canopySnowUnloadingDeriv, & ! intent(out): derivative in unloading of snow w.r.t. canopy ice (s-1) - canopySnowUnload_TkDeriv, & ! intent(out): derivative in unloading of snow w.r.t. canopy air temperature (K-1) - err,cmessage) ! intent(out): error control - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - - end if ! (re-computing snow throughfall for perturbed cases) - - ! save derivatives, since we did not save them earlier - scalarThroughfallSnowDeriv = throughfallSnowDeriv - scalarCanopySnowUnloadingDeriv = canopySnowUnloadingDeriv - scalarCanopySnowUnload_TkDeriv = canopySnowUnload_TkDeriv - - ! compute the heat advected with precipitation (W m-2) - ! NOTE: fluxes are in kg m-2 s-1, so no need to use density of water/ice here - scalarCanopyAdvectiveHeatFlux = ( -Cp_water*( scalarRainfall - throughfallRain ) - Cp_ice*( scalarSnowfall - throughfallSnow ) ) * ( canopyTemp - scalarTwetbulb ) - scalarGroundAdvectiveHeatFlux = ( -Cp_water*throughfallRain - Cp_ice*throughfallSnow ) * ( groundTemp - scalarTwetbulb ) !+ & - ! ( -Cp_water*canopyLiqDrainage - Cp_ice*canopySnowUnloading ) * ( groundTemp - canopyTemp ) - - ! save perturbed fluxes - if(ixDerivMethod == numerical)then - select case(itry) ! (select type of perturbation) - case(perturbStateGround) - ! only possible for ground advective flux to be changed with ground temperature perturbation - advectFluxGround_dStateGround = scalarGroundAdvectiveHeatFlux - case(perturbStateCanopy) - advectFluxCanopy_dStateCanopy = scalarCanopyAdvectiveHeatFlux - advectFluxGround_dStateCanopy = scalarGroundAdvectiveHeatFlux - case(perturbStateCanair) - ! only possible for ground advective flux to be changed with canopy air temperature perturbation, and currently commented out - advectFluxGround_dStateCanair = scalarGroundAdvectiveHeatFlux - case(perturbStateCanWat) - advectFluxCanopy_dStateCanWat = scalarCanopyAdvectiveHeatFlux - advectFluxGround_dStateCanWat = scalarGroundAdvectiveHeatFlux - case default; err=10; message=trim(message)//"unknown perturbation"; return - end select ! (type of perturbation) - end if ! (if numerical) - - end do ! (looping through different flux perturbations) - - ! ***** - ! compute all derivatives with respect to state variables ... - ! *********************************************************** - - ! analytical throughfall derivatives w.r.t. canopy temperature - dThroughfallRain_dTCanopy = throughfallRainDeriv * dCanLiq_dTcanopy - dCanopyLiqDrainage_dTCanopy = canopyLiqDrainageDeriv * dCanLiq_dTcanopy - dThroughfallSnow_dTCanopy = throughfallSnowDeriv * (-dCanLiq_dTcanopy) ! dCanIce_dTcanopy = -dCanLiq_dTcanopy - dCanopySnowUnloading_dTCanopy = canopySnowUnloadingDeriv * (-dCanLiq_dTcanopy) ! dCanIce_dTcanopy = -dCanLiq_dTcanopy - ! analytical throughfall derivatives w.r.t. canopy total water content - dThroughfallRain_dCanWat = throughfallRainDeriv * fracLiquidCanopy ! dCanLiq_dWat = fracLiquidCanopy - dCanopyLiqDrainage_dCanWat = canopyLiqDrainageDeriv * fracLiquidCanopy ! dCanLiq_dWat = fracLiquidCanopy - dThroughfallSnow_dCanWat = throughfallSnowDeriv * (1._rkind - fracLiquidCanopy) ! dCanIce_dWat = 1._rkind - fracLiquidCanopy - dCanopySnowUnloading_dCanWat = canopySnowUnloadingDeriv * (1._rkind - fracLiquidCanopy) ! dCanIce_dWat = 1._rkind - fracLiquidCanopy - - if(ixDerivMethod==analytical)then ! ** analytical derivatives - ! advection heat derivatives w.r.t. ground temperature - dAdvecFluxGround_dTGround = -Cp_water*throughfallRain - Cp_ice*throughfallSnow !- Cp_water*canopyLiqDrainage - Cp_ice*canopySnowUnloading - ! advection heat derivatives w.r.t. canopy temperature - dAdvecFluxCanopy_dTCanopy = -Cp_water*( scalarRainfall - throughfallRain - dThroughfallRain_dTCanopy*( canopyTemp - scalarTwetbulb ) ) + & - -Cp_ice *( scalarSnowfall - throughfallSnow - dThroughfallSnow_dTCanopy*( canopyTemp - scalarTwetbulb ) ) - dAdvecFluxGround_dTCanopy = (-Cp_water*dThroughfallRain_dTCanopy - Cp_ice*dThroughfallSnow_dTCanopy) *( groundTemp - scalarTwetbulb ) !+ & - ! -Cp_water*( -canopyLiqDrainage + dCanopyLiqDrainage_dTCanopy *( groundTemp - canopyTemp ) ) + & - ! -Cp_ice *( -canopySnowUnloading + dCanopySnowUnloading_dTCanopy*( groundTemp - canopyTemp ) ) - ! advection heat derivatives w.r.t. canopy air temperature - dAdvecFluxGround_dTCanair = 0._rkind ! -Cp_ice*canopySnowUnload_TkDeriv * ( groundTemp - canopyTemp ) - ! advection heat derivatives w.r.t. canopy total water content - dAdvecFluxCanopy_dCanWat = (Cp_water*dThroughfallRain_dCanWat + Cp_ice*dThroughfallSnow_dCanWat) * ( canopyTemp - scalarTwetbulb ) - dAdvecFluxGround_dCanWat = -(Cp_water*dThroughfallRain_dCanWat + Cp_ice*dThroughfallSnow_dCanWat) * ( groundTemp - scalarTwetbulb ) !+ - ! -(Cp_water*dCanopyLiqDrainage_dCanWat + Cp_ice*dCanopySnowUnloading_dCanWat) * ( groundTemp - canopyTemp ) - else ! ** numerical derivatives - ! advection heat derivatives w.r.t. ground temperature - dAdvecFluxGround_dTGround = ( advectFluxGround_dStateGround - scalarGroundAdvectiveHeatFlux ) / dx - ! advection derivatives w.r.t. canopy temperature - dAdvecFluxCanopy_dTCanopy = ( advectFluxCanopy_dStateCanopy - scalarCanopyAdvectiveHeatFlux ) / dx - dAdvecFluxGround_dTCanopy = ( advectFluxGround_dStateCanopy - scalarGroundAdvectiveHeatFlux ) / dx - ! advection derivatives w.r.t. canopy air temperature - dAdvecFluxGround_dTCanair = ( advectFluxGround_dStateCanair - scalarGroundAdvectiveHeatFlux ) / dx - ! advection derivatives w.r.t. canopy total water content - dAdvecFluxCanopy_dCanWat = ( advectFluxCanopy_dStateCanWat - scalarCanopyAdvectiveHeatFlux ) / dx - dAdvecFluxGround_dCanWat = ( advectFluxGround_dStateCanWat - scalarGroundAdvectiveHeatFlux ) / dx - end if ! (type of derivative) - - !print*, 'scalarRainfall, scalarThroughfallRain, scalarSnowfall, scalarThroughfallSnow = ', scalarRainfall, scalarThroughfallRain, scalarSnowfall, scalarThroughfallSnow - !print*, 'scalarCanopyAdvectiveHeatFlux, scalarGroundAdvectiveHeatFlux = ', scalarCanopyAdvectiveHeatFlux, scalarGroundAdvectiveHeatFlux - - ! compute the mass flux associated with transpiration and evaporation/sublimation (J m-2 s-1 --> kg m-2 s-1) - ! NOTE: remove water from the snow on the ground in preference to removing water from the water in soil pores - !print*, 'scalarLatHeatCanopyTrans = ', scalarLatHeatCanopyTrans - !print*, 'scalarLatHeatGround = ', scalarLatHeatGround - ! (canopy transpiration/sublimation) - if(scalarLatHeatSubVapCanopy > LH_vap+verySmall)then ! sublimation - scalarCanopyEvaporation = 0._rkind - scalarCanopySublimation = scalarLatHeatCanopyEvap/LH_sub - if(scalarLatHeatCanopyTrans > 0._rkind)then ! flux directed towards the veg - scalarCanopySublimation = scalarCanopySublimation + scalarLatHeatCanopyTrans/LH_sub ! frost - scalarCanopyTranspiration = 0._rkind - else - scalarCanopyTranspiration = scalarLatHeatCanopyTrans/LH_vap ! transpiration is always vapor - end if - ! (canopy transpiration/evaporation) - else ! evaporation - scalarCanopyEvaporation = scalarLatHeatCanopyEvap/LH_vap - scalarCanopySublimation = 0._rkind - if(scalarLatHeatCanopyTrans > 0._rkind)then ! flux directed towards the veg - scalarCanopyEvaporation = scalarCanopyEvaporation + scalarLatHeatCanopyTrans/LH_vap - scalarCanopyTranspiration = 0._rkind - else - scalarCanopyTranspiration = scalarLatHeatCanopyTrans/LH_vap - end if - end if - ! (ground evaporation/sublimation) - if(scalarLatHeatSubVapGround > LH_vap+verySmall)then ! sublimation - ! NOTE: this should only occur when we have formed snow layers, so check - if(nSnow == 0)then; err=20; message=trim(message)//'only expect snow sublimation when we have formed some snow layers'; return; end if - scalarGroundEvaporation = 0._rkind ! ground evaporation is zero once the snowpack has formed - scalarSnowSublimation = scalarLatHeatGround/LH_sub - else - ! NOTE: this should only occur when we have no snow layers, so check - if(nSnow > 0)then; err=20; message=trim(message)//'only expect ground evaporation when there are no snow layers'; return; end if - scalarGroundEvaporation = scalarLatHeatGround/LH_vap - scalarSnowSublimation = 0._rkind ! no sublimation from snow if no snow layers have formed - end if - !print*, 'scalarSnowSublimation, scalarLatHeatGround = ', scalarSnowSublimation, scalarLatHeatGround - - !print*, 'canopyWetFraction, scalarCanopyEvaporation = ', canopyWetFraction, scalarCanopyEvaporation - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! ***** AND STITCH EVERYTHING TOGETHER ***************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - - ! compute derived fluxes - scalarTotalET = scalarGroundEvaporation + scalarCanopyEvaporation + scalarCanopyTranspiration - scalarNetRadiation = scalarCanopyAbsorbedSolar + scalarLWNetCanopy + scalarGroundAbsorbedSolar + scalarLWNetGround - - ! compute net fluxes at the canopy and ground surface - canairNetFlux = turbFluxCanair - canopyNetFlux = scalarCanopyAbsorbedSolar + scalarLWNetCanopy + turbFluxCanopy + scalarCanopyAdvectiveHeatFlux - groundNetFlux = scalarGroundAbsorbedSolar + scalarLWNetGround + turbFluxGround + scalarGroundAdvectiveHeatFlux - !write(*,'(a,1x,10(e17.10,1x))') 'canopyNetFlux, groundNetFlux, scalarLWNetCanopy, turbFluxCanopy, turbFluxGround, scalarLWNetGround, scalarCanopyAdvectiveHeatFlux = ', & - ! canopyNetFlux, groundNetFlux, scalarLWNetCanopy, turbFluxCanopy, turbFluxGround, scalarLWNetGround, scalarCanopyAdvectiveHeatFlux - !write(*,'(a,1x,10(e20.14,1x))') 'groundNetFlux, scalarGroundAbsorbedSolar, scalarLWNetGround, turbFluxGround, scalarGroundAdvectiveHeatFlux = ', & - ! groundNetFlux, scalarGroundAbsorbedSolar, scalarLWNetGround, turbFluxGround, scalarGroundAdvectiveHeatFlux - - ! compute the energy derivatives - dCanairNetFlux_dCanairTemp = dTurbFluxCanair_dTCanair - dCanairNetFlux_dCanopyTemp = dTurbFluxCanair_dTCanopy - dCanairNetFlux_dGroundTemp = dTurbFluxCanair_dTGround - dCanopyNetFlux_dCanairTemp = dTurbFluxCanopy_dTCanair - dCanopyNetFlux_dCanopyTemp = dLWNetCanopy_dTCanopy + dTurbFluxCanopy_dTCanopy + dAdvecFluxCanopy_dTCanopy - dCanopyNetFlux_dGroundTemp = dLWNetCanopy_dTGround + dTurbFluxCanopy_dTGround - dGroundNetFlux_dCanairTemp = dTurbFluxGround_dTCanair + dAdvecFluxGround_dTCanair - dGroundNetFlux_dCanopyTemp = dLWNetGround_dTCanopy + dTurbFluxGround_dTCanopy + dAdvecFluxGround_dTCanopy - dGroundNetFlux_dGroundTemp = dLWNetGround_dTGround + dTurbFluxGround_dTGround + dAdvecFluxGround_dTGround - - ! check if evaporation or sublimation - if(scalarLatHeatSubVapCanopy < LH_vap+verySmall)then ! evaporation - - ! compute the liquid water derivarives - dCanopyEvaporation_dCanWat = dLatHeatCanopyEvap_dCanWat/LH_vap ! (s-1) - dCanopyEvaporation_dTCanair = dLatHeatCanopyEvap_dTCanair/LH_vap ! (kg m-2 s-1 K-1) - dCanopyEvaporation_dTCanopy = dLatHeatCanopyEvap_dTCanopy/LH_vap ! (kg m-2 s-1 K-1) - dCanopyEvaporation_dTGround = dLatHeatCanopyEvap_dTGround/LH_vap ! (kg m-2 s-1 K-1) - - ! sublimation - else - dCanopyEvaporation_dCanWat = 0._rkind ! (s-1) - dCanopyEvaporation_dTCanair = 0._rkind ! (kg m-2 s-1 K-1) - dCanopyEvaporation_dTCanopy = 0._rkind ! (kg m-2 s-1 K-1) - dCanopyEvaporation_dTGround = 0._rkind ! (kg m-2 s-1 K-1) - end if - - ! compute the liquid water derivarives (ground evap) - dGroundEvaporation_dCanWat = dLatHeatGroundEvap_dCanWat/LH_vap ! (s-1) - dGroundEvaporation_dTCanair = dLatHeatGroundEvap_dTCanair/LH_vap ! (kg m-2 s-1 K-1) - dGroundEvaporation_dTCanopy = dLatHeatGroundEvap_dTCanopy/LH_vap ! (kg m-2 s-1 K-1) - dGroundEvaporation_dTGround = dLatHeatGroundEvap_dTGround/LH_vap ! (kg m-2 s-1 K-1) - - ! compute the cross derivative terms (turbulent fluxes and advective fluxes) - dCanopyNetFlux_dCanWat = dTurbFluxCanopy_dCanWat + dAdvecFluxCanopy_dCanWat ! (J kg-1 s-1) - dGroundNetFlux_dCanWat = dTurbFluxGround_dCanWat + dAdvecFluxGround_dCanWat ! (J kg-1 s-1) - - !print*, (ixDerivMethod == numerical) - !print*, 'dGroundNetFlux_dCanairTemp = ', dGroundNetFlux_dCanairTemp - !print*, 'dCanopyNetFlux_dCanopyTemp = ', dCanopyNetFlux_dCanopyTemp - !print*, 'dGroundNetFlux_dCanopyTemp = ', dGroundNetFlux_dCanopyTemp - !print*, 'dCanopyNetFlux_dGroundTemp = ', dCanopyNetFlux_dGroundTemp - !print*, 'dLWNetCanopy_dTGround = ', dLWNetCanopy_dTGround - !print*, 'dTurbFluxCanopy_dTGround = ', dTurbFluxCanopy_dTGround - !pause - - ! * check - case default; err=10; message=trim(message)//'unable to identify upper boundary condition for thermodynamics'; return - - ! end case statement - end select ! upper boundary condition for thermodynamics - - ! return liquid fluxes (needed for coupling) - returnCanopyTranspiration = scalarCanopyTranspiration ! canopy transpiration (kg m-2 s-1) - returnCanopyEvaporation = scalarCanopyEvaporation ! canopy evaporation/condensation (kg m-2 s-1) - returnGroundEvaporation = scalarGroundEvaporation ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - - ! end associations - end associate - - - end subroutine vegNrgFluxSundials - - ! ******************************************************************************************************* ! public subroutine wettedFrac: compute wetted fraction of the canopy ! ******************************************************************************************************* @@ -4278,19 +2606,19 @@ subroutine turbFluxes(& dTurbFluxGround_dTCanopy, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) dTurbFluxGround_dTGround, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) ! output: liquid flux derivatives (canopy evap) - dLatHeatCanopyEvap_dCanWat, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy total water content (J kg-1 s-1) + dLatHeatCanopyEvap_dCanLiq, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (J kg-1 s-1) dLatHeatCanopyEvap_dTCanair, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) dLatHeatCanopyEvap_dTCanopy, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) dLatHeatCanopyEvap_dTGround, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) ! output: liquid flux derivatives (ground evap) - dLatHeatGroundEvap_dCanWat, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) + dLatHeatGroundEvap_dCanLiq, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy liquid water content (J kg-1 s-1) dLatHeatGroundEvap_dTCanair, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy air temperature dLatHeatGroundEvap_dTCanopy, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy temperature dLatHeatGroundEvap_dTGround, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. ground temperature ! output: cross derivatives - dTurbFluxCanair_dCanWat, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) - dTurbFluxCanopy_dCanWat, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - dTurbFluxGround_dCanWat, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxCanair_dCanLiq, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dTurbFluxCanopy_dCanLiq, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dTurbFluxGround_dCanLiq, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) ! output: error control err,message ) ! intent(out): error control ! ----------------------------------------------------------------------------------------------------------------------------------------- @@ -4370,19 +2698,19 @@ subroutine turbFluxes(& real(rkind),intent(out) :: dTurbFluxGround_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) real(rkind),intent(out) :: dTurbFluxGround_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) ! output: liquid flux derivatives (canopy evap) - real(rkind),intent(out) :: dLatHeatCanopyEvap_dCanWat ! derivative in latent heat of canopy evaporation w.r.t. canopy total water content (W kg-1) + real(rkind),intent(out) :: dLatHeatCanopyEvap_dCanLiq ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (W kg-1) real(rkind),intent(out) :: dLatHeatCanopyEvap_dTCanair ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) real(rkind),intent(out) :: dLatHeatCanopyEvap_dTCanopy ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) real(rkind),intent(out) :: dLatHeatCanopyEvap_dTGround ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) ! output: liquid flux derivatives (ground evap) - real(rkind),intent(out) :: dLatHeatGroundEvap_dCanWat ! derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) + real(rkind),intent(out) :: dLatHeatGroundEvap_dCanLiq ! derivative in latent heat of ground evaporation w.r.t. canopy liquid water content (J kg-1 s-1) real(rkind),intent(out) :: dLatHeatGroundEvap_dTCanair ! derivative in latent heat of ground evaporation w.r.t. canopy air temperature (W m-2 K-1) real(rkind),intent(out) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) real(rkind),intent(out) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) ! output: cross derivatives - real(rkind),intent(out) :: dTurbFluxCanair_dCanWat ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) - real(rkind),intent(out) :: dTurbFluxCanopy_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - real(rkind),intent(out) :: dTurbFluxGround_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + real(rkind),intent(out) :: dTurbFluxCanair_dCanLiq ! derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + real(rkind),intent(out) :: dTurbFluxCanopy_dCanLiq ! derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + real(rkind),intent(out) :: dTurbFluxGround_dCanLiq ! derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -4412,7 +2740,7 @@ subroutine turbFluxes(& real(rkind) :: dVPCanopyAir_dTCanopy ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the canopy real(rkind) :: dVPCanopyAir_dTGround ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the ground real(rkind) :: dVPCanopyAir_dWetFrac ! derivative of vapor pressure in the canopy air space w.r.t. wetted fraction of the canopy - real(rkind) :: dVPCanopyAir_dCanWat ! derivative of vapor pressure in the canopy air space w.r.t. canopy total water content + real(rkind) :: dVPCanopyAir_dCanLiq ! derivative of vapor pressure in the canopy air space w.r.t. canopy liquid water content ! local variables -- sensible heat flux derivatives real(rkind) :: dSenHeatTotal_dTCanair ! derivative in the total sensible heat flux w.r.t. canopy air temperature real(rkind) :: dSenHeatTotal_dTCanopy ! derivative in the total sensible heat flux w.r.t. canopy air temperature @@ -4430,7 +2758,7 @@ subroutine turbFluxes(& ! local variables -- wetted fraction derivatives real(rkind) :: dLatHeatCanopyEvap_dWetFrac ! derivative in the latent heat of canopy evaporation w.r.t. canopy wet fraction (W m-2) real(rkind) :: dLatHeatCanopyTrans_dWetFrac ! derivative in the latent heat of canopy transpiration w.r.t. canopy wet fraction (W m-2) - real(rkind) :: dLatHeatCanopyTrans_dCanWat ! derivative in the latent heat of canopy transpiration w.r.t. canopy liquid water (J kg-1 s-1) + real(rkind) :: dLatHeatCanopyTrans_dCanLiq ! derivative in the latent heat of canopy transpiration w.r.t. canopy liquid water (J kg-1 s-1) ! ----------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='turbFluxes/' @@ -4601,9 +2929,9 @@ subroutine turbFluxes(& dPart1 = (leafConductance - leafConductanceTr)*satVP_CanopyTemp dPart2 = -(leafConductance - leafConductanceTr)/(totalConductanceLH**2._rkind) dVPCanopyAir_dWetFrac = dPart1/totalConductanceLH + fPart_VP*dPart2 - dVPCanopyAir_dCanWat = dVPCanopyAir_dWetFrac*dCanopyWetFraction_dWat - !write(*,'(a,5(f20.8,1x))') 'dVPCanopyAir_dTCanair, dVPCanopyAir_dTCanopy, dVPCanopyAir_dTGround, dVPCanopyAir_dWetFrac, dVPCanopyAir_dCanWat = ', & - ! dVPCanopyAir_dTCanair, dVPCanopyAir_dTCanopy, dVPCanopyAir_dTGround, dVPCanopyAir_dWetFrac, dVPCanopyAir_dCanWat + dVPCanopyAir_dCanLiq = dVPCanopyAir_dWetFrac*dCanopyWetFraction_dWat + !write(*,'(a,5(f20.8,1x))') 'dVPCanopyAir_dTCanair, dVPCanopyAir_dTCanopy, dVPCanopyAir_dTGround, dVPCanopyAir_dWetFrac, dVPCanopyAir_dCanLiq = ', & + ! dVPCanopyAir_dTCanair, dVPCanopyAir_dTCanopy, dVPCanopyAir_dTGround, dVPCanopyAir_dWetFrac, dVPCanopyAir_dCanLiq ! sensible heat from the canopy to the atmosphere dSenHeatTotal_dTCanair = -volHeatCapacityAir*canopyConductance - volHeatCapacityAir*dCanopyCond_dCanairTemp*(canairTemp - airtemp) @@ -4634,7 +2962,7 @@ subroutine turbFluxes(& dPart2 = dSVPCanopy_dCanopyTemp - dVPCanopyAir_dTCanopy ! (derivatives) dLatHeatCanopyEvap_dTCanair = fPart1*(-dVPCanopyAir_dTCanair) - dLatHeatCanopyEvap_dTCanopy = fPart1*dPart2 + fPart2*dPart1 + dLatHeatCanopyEvap_dTCanopy = fPart1*dpart2 + fPart2*dPart1 dLatHeatCanopyEvap_dTGround = fPart1*(-dVPCanopyAir_dTGround) !write(*,'(a,3(f20.8,1x))') 'dLatHeatCanopyEvap_dTCanair, dLatHeatCanopyEvap_dTCanopy, dLatHeatCanopyEvap_dTGround = ', & ! dLatHeatCanopyEvap_dTCanair, dLatHeatCanopyEvap_dTCanopy, dLatHeatCanopyEvap_dTGround @@ -4671,8 +2999,8 @@ subroutine turbFluxes(& !print*, 'dLatHeatCanopyTrans_dWetFrac = ', dLatHeatCanopyTrans_dWetFrac ! latent heat associated with canopy transpiration w.r.t. canopy liquid water - dLatHeatCanopyTrans_dCanWat = dLatHeatCanopyTrans_dWetFrac*dCanopyWetFraction_dWat ! (J s-1 kg-1) - !print*, 'dLatHeatCanopyTrans_dCanWat = ', dLatHeatCanopyTrans_dCanWat + dLatHeatCanopyTrans_dCanLiq = dLatHeatCanopyTrans_dWetFrac*dCanopyWetFraction_dWat ! (J s-1 kg-1) + !print*, 'dLatHeatCanopyTrans_dCanLiq = ', dLatHeatCanopyTrans_dCanLiq else ! canopy is undefined @@ -4692,9 +3020,9 @@ subroutine turbFluxes(& ! set derivatives for wetted area and canopy transpiration to zero (no canopy, so fluxes are undefined) dLatHeatCanopyEvap_dWetFrac = 0._rkind - dLatHeatCanopyEvap_dCanWat = 0._rkind - dLatHeatCanopyTrans_dCanWat = 0._rkind - dVPCanopyAir_dCanWat = 0._rkind + dLatHeatCanopyEvap_dCanLiq = 0._rkind + dLatHeatCanopyTrans_dCanLiq = 0._rkind + dVPCanopyAir_dCanLiq = 0._rkind ! set derivatives for ground fluxes w.r.t canopy temperature to zero (no canopy, so fluxes are undefined) dSenHeatGround_dTCanair = 0._rkind @@ -4738,12 +3066,12 @@ subroutine turbFluxes(& dTurbFluxGround_dTCanopy = dSenHeatGround_dTCanopy + dLatHeatGroundEvap_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) dTurbFluxGround_dTGround = dSenHeatGround_dTGround + dLatHeatGroundEvap_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) ! (liquid water derivatives) - dLatHeatCanopyEvap_dCanWat = dLatHeatCanopyEvap_dWetFrac*dCanopyWetFraction_dWat ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water (W kg-1) - dLatHeatGroundEvap_dCanWat = latHeatSubVapGround*latentHeatConstant*groundConductanceLH*dVPCanopyAir_dCanWat ! derivative in latent heat of ground evaporation w.r.t. canopy liquid water (J kg-1 s-1) + dLatHeatCanopyEvap_dCanLiq = dLatHeatCanopyEvap_dWetFrac*dCanopyWetFraction_dWat ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water (W kg-1) + dLatHeatGroundEvap_dCanLiq = latHeatSubVapGround*latentHeatConstant*groundConductanceLH*dVPCanopyAir_dCanLiq ! derivative in latent heat of ground evaporation w.r.t. canopy liquid water (J kg-1 s-1) ! (cross deriavtives) - dTurbFluxCanair_dCanWat = 0._rkind ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) - dTurbFluxCanopy_dCanWat = dLatHeatCanopyEvap_dCanWat + dLatHeatCanopyTrans_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - dTurbFluxGround_dCanWat = dLatHeatGroundEvap_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxCanair_dCanLiq = 0._rkind ! derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dTurbFluxCanopy_dCanLiq = dLatHeatCanopyEvap_dCanLiq + dLatHeatCanopyTrans_dCanLiq ! derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dTurbFluxGround_dCanLiq = dLatHeatGroundEvap_dCanLiq ! derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) else ! (just make sure we return something) ! (energy derivatives) dTurbFluxCanair_dTCanair = 0._rkind @@ -4756,12 +3084,12 @@ subroutine turbFluxes(& dTurbFluxGround_dTCanopy = 0._rkind dTurbFluxGround_dTGround = 0._rkind ! (liquid water derivatives) - dLatHeatCanopyEvap_dCanWat = 0._rkind - dLatHeatGroundEvap_dCanWat = 0._rkind + dLatHeatCanopyEvap_dCanLiq = 0._rkind + dLatHeatGroundEvap_dCanLiq = 0._rkind ! (cross deriavtives) - dTurbFluxCanair_dCanWat = 0._rkind - dTurbFluxCanopy_dCanWat = 0._rkind - dTurbFluxGround_dCanWat = 0._rkind + dTurbFluxCanair_dCanLiq = 0._rkind + dTurbFluxCanopy_dCanLiq = 0._rkind + dTurbFluxGround_dCanLiq = 0._rkind end if end subroutine turbFluxes From 97273e2dfc1bcbceb95395e432b42078c16ff364 Mon Sep 17 00:00:00 2001 From: ashleymedin <34691332+ashleymedin@users.noreply.github.com> Date: Mon, 7 Mar 2022 16:59:16 +0900 Subject: [PATCH 0246/1472] vegNrgFlux changes are undone. --- build/source/dshare/get_ixname.f90 | 8 +- build/source/dshare/popMetadat.f90 | 8 +- build/source/dshare/var_lookup.f90 | 8 +- build/source/engine/computFlux.f90 | 772 +-------------------- build/source/engine/computJacDAE.f90 | 32 +- build/source/engine/computJacob.f90 | 24 +- build/source/engine/eval8DAE.f90 | 80 +-- build/source/engine/eval8JacDAE.f90 | 36 +- build/source/engine/eval8summa.f90 | 468 +------------ build/source/engine/ssdNrgFlux.f90 | 284 +------- build/source/engine/systemSolvSundials.f90 | 4 +- build/source/engine/updateVars4JacDAE.f90 | 5 +- build/source/engine/vegNrgFlux.f90 | 249 ++++--- 13 files changed, 286 insertions(+), 1692 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index a9471379b..289582ba1 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -710,20 +710,20 @@ function get_ixderiv(varName) case('dCanopyNetFlux_dCanairTemp' ); get_ixderiv = iLookDERIV%dCanopyNetFlux_dCanairTemp ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) case('dCanopyNetFlux_dCanopyTemp' ); get_ixderiv = iLookDERIV%dCanopyNetFlux_dCanopyTemp ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) case('dCanopyNetFlux_dGroundTemp' ); get_ixderiv = iLookDERIV%dCanopyNetFlux_dGroundTemp ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - case('dCanopyNetFlux_dCanLiq' ); get_ixderiv = iLookDERIV%dCanopyNetFlux_dCanLiq ! derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) + case('dCanopyNetFlux_dCanWat' ); get_ixderiv = iLookDERIV%dCanopyNetFlux_dCanWat ! derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) case('dGroundNetFlux_dCanairTemp' ); get_ixderiv = iLookDERIV%dGroundNetFlux_dCanairTemp ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) case('dGroundNetFlux_dCanopyTemp' ); get_ixderiv = iLookDERIV%dGroundNetFlux_dCanopyTemp ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) case('dGroundNetFlux_dGroundTemp' ); get_ixderiv = iLookDERIV%dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - case('dGroundNetFlux_dCanLiq' ); get_ixderiv = iLookDERIV%dGroundNetFlux_dCanLiq ! derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) + case('dGroundNetFlux_dCanWat' ); get_ixderiv = iLookDERIV%dGroundNetFlux_dCanWat ! derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) ! derivatives in evaporative fluxes w.r.t. relevant state variables case('dCanopyEvaporation_dTCanair' ); get_ixderiv = iLookDERIV%dCanopyEvaporation_dTCanair ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) case('dCanopyEvaporation_dTCanopy' ); get_ixderiv = iLookDERIV%dCanopyEvaporation_dTCanopy ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) case('dCanopyEvaporation_dTGround' ); get_ixderiv = iLookDERIV%dCanopyEvaporation_dTGround ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - case('dCanopyEvaporation_dCanLiq' ); get_ixderiv = iLookDERIV%dCanopyEvaporation_dCanLiq ! derivative in canopy evaporation w.r.t. canopy total water content (s-1) + case('dCanopyEvaporation_dCanWat' ); get_ixderiv = iLookDERIV%dCanopyEvaporation_dCanWat ! derivative in canopy evaporation w.r.t. canopy total water content (s-1) case('dGroundEvaporation_dTCanair' ); get_ixderiv = iLookDERIV%dGroundEvaporation_dTCanair ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) case('dGroundEvaporation_dTCanopy' ); get_ixderiv = iLookDERIV%dGroundEvaporation_dTCanopy ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) case('dGroundEvaporation_dTGround' ); get_ixderiv = iLookDERIV%dGroundEvaporation_dTGround ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - case('dGroundEvaporation_dCanLiq' ); get_ixderiv = iLookDERIV%dGroundEvaporation_dCanLiq ! derivative in ground evaporation w.r.t. canopy total water content (s-1) + case('dGroundEvaporation_dCanWat' ); get_ixderiv = iLookDERIV%dGroundEvaporation_dCanWat ! derivative in ground evaporation w.r.t. canopy total water content (s-1) ! derivatives in canopy water w.r.t canopy temperature case('dTheta_dTkCanopy' ); get_ixderiv = iLookDERIV%dTheta_dTkCanopy ! derivative of volumetric liquid water content w.r.t. temperature (K-1) case('d2Theta_dTkCanopy2' ); get_ixderiv = iLookDERIV%d2Theta_dTkCanopy2 ! second derivative of volumetric liquid water content w.r.t. temperature diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 9079c07d0..001c5d077 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -544,20 +544,20 @@ subroutine popMetadat(err,message) deriv_meta(iLookDERIV%dCanopyNetFlux_dCanairTemp) = var_info('dCanopyNetFlux_dCanairTemp' , 'derivative in net canopy flux w.r.t. canopy air temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dCanopyNetFlux_dCanopyTemp) = var_info('dCanopyNetFlux_dCanopyTemp' , 'derivative in net canopy flux w.r.t. canopy temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dCanopyNetFlux_dGroundTemp) = var_info('dCanopyNetFlux_dGroundTemp' , 'derivative in net canopy flux w.r.t. ground temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dCanopyNetFlux_dCanLiq) = var_info('dCanopyNetFlux_dCanLiq' , 'derivative in net canopy fluxes w.r.t. canopy total water content' , 'J kg-1 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCanopyNetFlux_dCanWat) = var_info('dCanopyNetFlux_dCanWat' , 'derivative in net canopy fluxes w.r.t. canopy total water content' , 'J kg-1 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dGroundNetFlux_dCanairTemp) = var_info('dGroundNetFlux_dCanairTemp' , 'derivative in net ground flux w.r.t. canopy air temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dGroundNetFlux_dCanopyTemp) = var_info('dGroundNetFlux_dCanopyTemp' , 'derivative in net ground flux w.r.t. canopy temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dGroundNetFlux_dGroundTemp) = var_info('dGroundNetFlux_dGroundTemp' , 'derivative in net ground flux w.r.t. ground temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dGroundNetFlux_dCanLiq) = var_info('dGroundNetFlux_dCanLiq' , 'derivative in net ground fluxes w.r.t. canopy total water content' , 'J kg-1 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dGroundNetFlux_dCanWat) = var_info('dGroundNetFlux_dCanWat' , 'derivative in net ground fluxes w.r.t. canopy total water content' , 'J kg-1 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! derivatives in evaporative fluxes w.r.t. relevant state variables deriv_meta(iLookDERIV%dCanopyEvaporation_dTCanair) = var_info('dCanopyEvaporation_dTCanair' , 'derivative in canopy evaporation w.r.t. canopy air temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dCanopyEvaporation_dTCanopy) = var_info('dCanopyEvaporation_dTCanopy' , 'derivative in canopy evaporation w.r.t. canopy temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dCanopyEvaporation_dTGround) = var_info('dCanopyEvaporation_dTGround' , 'derivative in canopy evaporation w.r.t. ground temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dCanopyEvaporation_dCanLiq) = var_info('dCanopyEvaporation_dCanLiq' , 'derivative in canopy evaporation w.r.t. canopy total water content' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCanopyEvaporation_dCanWat) = var_info('dCanopyEvaporation_dCanWat' , 'derivative in canopy evaporation w.r.t. canopy total water content' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dGroundEvaporation_dTCanair) = var_info('dGroundEvaporation_dTCanair' , 'derivative in ground evaporation w.r.t. canopy air temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dGroundEvaporation_dTCanopy) = var_info('dGroundEvaporation_dTCanopy' , 'derivative in ground evaporation w.r.t. canopy temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dGroundEvaporation_dTGround) = var_info('dGroundEvaporation_dTGround' , 'derivative in ground evaporation w.r.t. ground temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dGroundEvaporation_dCanLiq) = var_info('dGroundEvaporation_dCanLiq' , 'derivative in ground evaporation w.r.t. canopy total water content' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dGroundEvaporation_dCanWat) = var_info('dGroundEvaporation_dCanWat' , 'derivative in ground evaporation w.r.t. canopy total water content' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! derivatives in canopy water w.r.t canopy temperature deriv_meta(iLookDERIV%dTheta_dTkCanopy) = var_info('dTheta_dTkCanopy' , 'derivative of volumetric liquid water content w.r.t. temperature' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%d2Theta_dTkCanopy2) = var_info('d2Theta_dTkCanopy2' , 'second derivative of volumetric liquid water content w.r.t. temperature', 'K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 6d6e10702..0ef591f2e 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -563,20 +563,20 @@ MODULE var_lookup integer(i4b) :: dCanopyNetFlux_dCanairTemp = integerMissing ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) integer(i4b) :: dCanopyNetFlux_dCanopyTemp = integerMissing ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) integer(i4b) :: dCanopyNetFlux_dGroundTemp = integerMissing ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - integer(i4b) :: dCanopyNetFlux_dCanLiq = integerMissing ! derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) + integer(i4b) :: dCanopyNetFlux_dCanWat = integerMissing ! derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) integer(i4b) :: dGroundNetFlux_dCanairTemp = integerMissing ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) integer(i4b) :: dGroundNetFlux_dCanopyTemp = integerMissing ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) integer(i4b) :: dGroundNetFlux_dGroundTemp = integerMissing ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - integer(i4b) :: dGroundNetFlux_dCanLiq = integerMissing ! derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) + integer(i4b) :: dGroundNetFlux_dCanWat = integerMissing ! derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) ! derivatives in evaporative fluxes w.r.t. relevant state variables integer(i4b) :: dCanopyEvaporation_dTCanair = integerMissing ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) integer(i4b) :: dCanopyEvaporation_dTCanopy = integerMissing ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) integer(i4b) :: dCanopyEvaporation_dTGround = integerMissing ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - integer(i4b) :: dCanopyEvaporation_dCanLiq = integerMissing ! derivative in canopy evaporation w.r.t. canopy total water content (s-1) + integer(i4b) :: dCanopyEvaporation_dCanWat = integerMissing ! derivative in canopy evaporation w.r.t. canopy total water content (s-1) integer(i4b) :: dGroundEvaporation_dTCanair = integerMissing ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) integer(i4b) :: dGroundEvaporation_dTCanopy = integerMissing ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) integer(i4b) :: dGroundEvaporation_dTGround = integerMissing ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - integer(i4b) :: dGroundEvaporation_dCanLiq = integerMissing ! derivative in ground evaporation w.r.t. canopy total water content (s-1) + integer(i4b) :: dGroundEvaporation_dCanWat = integerMissing ! derivative in ground evaporation w.r.t. canopy total water content (s-1) ! derivatives in canopy water w.r.t canopy temperature integer(i4b) :: dTheta_dTkCanopy = integerMissing ! derivative of volumetric liquid water content w.r.t. temperature (K-1) integer(i4b) :: d2Theta_dTkCanopy2 = integerMissing ! second derivative of volumetric liquid water content w.r.t. temperature diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index c677b6b0a..ab4632cfd 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -96,767 +96,15 @@ module computFlux_module implicit none private public::computFlux -public::computFluxSundials public::soilCmpres public::soilCmpresSundials contains - ! ********************************************************************************************************* - ! public subroutine computFlux: compute model fluxes - ! ********************************************************************************************************* - subroutine computFlux(& - ! input-output: model control - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to denote the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - drainageMeltPond, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) - ! input: state variables - scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) - scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) - mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) - mLayerMatricHeadLiqTrial, & ! intent(in): trial value for the liquid water matric potential in each soil layer (m) - scalarAquiferStorageTrial,& ! intent(in): trial value of storage of water in the aquifer (m) - ! input: diagnostic variables defining the liquid water and ice content - scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) - mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liquid water content in each snow and soil layer (-) - mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) - ! input: data structures - model_decisions, & ! intent(in): model decisions - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): index data - ! input-output: data structures - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: flux vector and baseflow derivatives - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - fluxVec, & ! intent(out): flux vector (mixed units) - ! output: error control - err,message) ! intent(out): error code and error message - ! provide access to flux subroutines - USE vegNrgFlux_module,only:vegNrgFlux ! compute energy fluxes over vegetation - USE ssdNrgFlux_module,only:ssdNrgFlux ! compute energy fluxes throughout the snow and soil subdomains - USE vegLiqFlux_module,only:vegLiqFlux ! compute liquid water fluxes through vegetation - USE snowLiqFlx_module,only:snowLiqflx ! compute liquid water fluxes through snow - USE soilLiqFlx_module,only:soilLiqflx ! compute liquid water fluxes through soil - USE groundwatr_module,only:groundwatr ! compute the baseflow flux - USE bigAquifer_module,only:bigAquifer ! compute fluxes for the big aquifer - implicit none - ! --------------------------------------------------------------------------------------- - ! * dummy variables - ! --------------------------------------------------------------------------------------- - ! input-output: control - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - real(rkind),intent(in) :: drainageMeltPond ! drainage from the surface melt pond (kg m-2 s-1) - ! input: state variables - real(rkind),intent(in) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind),intent(in) :: mLayerTempTrial(:) ! trial value for temperature of each snow/soil layer (K) - real(rkind),intent(in) :: mLayerMatricHeadLiqTrial(:) ! trial value for the liquid water matric potential (m) - real(rkind),intent(in) :: scalarAquiferStorageTrial ! trial value of aquifer storage (m) - ! input: diagnostic variables - real(rkind),intent(in) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) - real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) - ! input: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(var_i), intent(in) :: type_data ! type of vegetation and soil - type(var_d), intent(in) :: attr_data ! spatial attributes - type(var_dlength), intent(in) :: mpar_data ! model parameters - type(var_d), intent(in) :: forc_data ! model forcing data - type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin - type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_ilength), intent(in) :: indx_data ! indices defining model states and layers - ! input-output: data structures - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! input-output: flux vector and baseflow derivatives - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - real(rkind),intent(out) :: fluxVec(:) ! model flux vector (mixed units) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! --------------------------------------------------------------------------------------- - ! * local variables - ! --------------------------------------------------------------------------------------- - integer(i4b) :: local_ixGroundwater ! local index for groundwater representation - integer(i4b) :: iLayer ! index of model layers - logical(lgt) :: doVegNrgFlux ! flag to compute the energy flux over vegetation - real(rkind),dimension(nSoil) :: dHydCond_dMatric ! derivative in hydraulic conductivity w.r.t matric head (s-1) - character(LEN=256) :: cmessage ! error message of downwind routine - ! -------------------------------------------------------------- - ! initialize error control - err=0; message='computFlux/' - - ! ***** - ! (0) PRELIMINARIES... - ! ******************** - - ! get the necessary variables for the flux computations - associate(& - - ! model decisions - ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization - ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) - - ! domain boundary conditions - upperBoundTemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) - scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in): [dp] rainfall rate (kg m-2 s-1) - - ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - - ! indices of model state variables for the vegetation subdomain - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain - ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer - - ! indices of model state variables for the snow+soil domain - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] type of layer (iname_soil or iname_snow) - - ! number of state variables of a specific type - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain - nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain - nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain - - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - - ! derivatives - dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) - dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(in): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature - - ! number of flux calls - numFluxCalls => diag_data%var(iLookDIAG%numFluxCalls)%dat(1) ,& ! intent(out): [dp] number of flux calls (-) - - ! net fluxes over the vegetation domain - scalarCanairNetNrgFlux => flux_data%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the canopy air space (W m-2) - scalarCanopyNetNrgFlux => flux_data%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the vegetation canopy (W m-2) - scalarGroundNetNrgFlux => flux_data%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the ground surface (W m-2) - scalarCanopyNetLiqFlux => flux_data%var(iLookFLUX%scalarCanopyNetLiqFlux)%dat(1) ,& ! intent(out): [dp] net liquid water flux for the vegetation canopy (kg m-2 s-1) - - ! net fluxes over the snow+soil domain - mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) - mLayerLiqFluxSnow => flux_data%var(iLookFLUX%mLayerLiqFluxSnow)%dat ,& ! intent(out): [dp] net liquid water flux for each snow layer (s-1) - mLayerLiqFluxSoil => flux_data%var(iLookFLUX%mLayerLiqFluxSoil)%dat ,& ! intent(out): [dp] net liquid water flux for each soil layer (s-1) - - ! evaporative fluxes - scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(out): [dp] canopy transpiration (kg m-2 s-1) - scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(out): [dp] canopy evaporation/condensation (kg m-2 s-1) - scalarGroundEvaporation => flux_data%var(iLookFLUX%scalarGroundEvaporation)%dat(1) ,& ! intent(out): [dp] ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(out): [dp(:)] transpiration loss from each soil layer (m s-1) - - ! fluxes for the snow+soil domain - iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(out): [dp(0:)] vertical energy flux at the interface of snow and soil layers - iLayerLiqFluxSnow => flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at snow layer interfaces (-) - iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at soil layer interfaces (-) - mLayerHydCond => flux_data%var(iLookFLUX%mLayerHydCond)%dat ,& ! intent(out): [dp(:)] hydraulic conductivity in each soil layer (m s-1) - mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(out): [dp(:)] baseflow from each soil layer (m s-1) - scalarSnowDrainage => flux_data%var(iLookFLUX%scalarSnowDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the snow profile (m s-1) - scalarSoilDrainage => flux_data%var(iLookFLUX%scalarSoilDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the soil profile (m s-1) - scalarSoilBaseflow => flux_data%var(iLookFLUX%scalarSoilBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the soil profile (m s-1) - - ! infiltration - scalarInfilArea => diag_data%var(iLookDIAG%scalarInfilArea )%dat(1) ,& ! intent(out): [dp] fraction of unfrozen area where water can infiltrate (-) - scalarFrozenArea => diag_data%var(iLookDIAG%scalarFrozenArea )%dat(1) ,& ! intent(out): [dp] fraction of area that is considered impermeable due to soil ice (-) - scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1) ,& ! intent(out): [dp] soil control on infiltration, zero or one - scalarMaxInfilRate => flux_data%var(iLookFLUX%scalarMaxInfilRate)%dat(1) ,& ! intent(out): [dp] maximum infiltration rate (m s-1) - scalarInfiltration => flux_data%var(iLookFLUX%scalarInfiltration)%dat(1) ,& ! intent(out): [dp] infiltration of water into the soil profile (m s-1) - - ! boundary fluxes in the soil domain - scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(out): [dp] rain that reaches the ground without ever touching the canopy (kg m-2 s-1) - scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(out): [dp] drainage of liquid water from the vegetation canopy (kg m-2 s-1) - scalarRainPlusMelt => flux_data%var(iLookFLUX%scalarRainPlusMelt)%dat(1) ,& ! intent(out): [dp] rain plus melt (m s-1) - scalarSurfaceRunoff => flux_data%var(iLookFLUX%scalarSurfaceRunoff)%dat(1) ,& ! intent(out): [dp] surface runoff (m s-1) - scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1) ,& ! intent(out): [dp] exfiltration from the soil profile (m s-1) - mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat ,& ! intent(out): [dp(:)] column outflow from each soil layer (m3 s-1) - - ! fluxes for the aquifer - scalarAquiferTranspire => flux_data%var(iLookFLUX%scalarAquiferTranspire)%dat(1) ,& ! intent(out): [dp] transpiration loss from the aquifer (m s-1 - scalarAquiferRecharge => flux_data%var(iLookFLUX%scalarAquiferRecharge)%dat(1) ,& ! intent(out): [dp] recharge to the aquifer (m s-1) - scalarAquiferBaseflow => flux_data%var(iLookFLUX%scalarAquiferBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the aquifer (m s-1) - - ! total runoff - scalarTotalRunoff => flux_data%var(iLookFLUX%scalarTotalRunoff)%dat(1) ,& ! intent(out): [dp] total runoff (m s-1) - - ! derivatives in net vegetation energy fluxes w.r.t. relevant state variables - dCanairNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy air temperature - dCanairNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy temperature - dCanairNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. ground temperature - dCanopyNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy air temperature - dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy temperature - dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. ground temperature - dCanopyNetFlux_dCanLiq => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in net canopy fluxes w.r.t. canopy liquid water content - dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy air temperature - dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy temperature - dGroundNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. ground temperature - dGroundNetFlux_dCanLiq => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in net ground fluxes w.r.t. canopy liquid water content - - ! derivatives in evaporative fluxes w.r.t. relevant state variables - dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy air temperature - dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy temperature - dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. ground temperature - dCanopyEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy liquid water content - dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy air temperature - dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy temperature - dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. ground temperature - dGroundEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy liquid water content - - ! derivatives in canopy water w.r.t canopy temperature - dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(out): [dp] derivative of canopy liquid storage w.r.t. temperature - - ! derivatives in canopy liquid fluxes w.r.t. canopy water - scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ,& ! intent(out): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water - scalarThroughfallRainDeriv => deriv_data%var(iLookDERIV%scalarThroughfallRainDeriv )%dat(1) ,& ! intent(out): [dp] derivative in throughfall w.r.t. canopy liquid water - scalarCanopyLiqDrainageDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDrainageDeriv)%dat(1) ,& ! intent(out): [dp] derivative in canopy drainage w.r.t. canopy liquid water - - ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below - dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above - dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below - - ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above - iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(out): [dp(:)] derivative in vertical liquid water flux at layer interfaces - - ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential - dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above - dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below - mLayerdTheta_dPsi => deriv_data%var(iLookDERIV%mLayerdTheta_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. psi - mLayerdPsi_dTheta => deriv_data%var(iLookDERIV%mLayerdPsi_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. theta - dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in compressibility w.r.t matric head - - ! derivative in baseflow flux w.r.t. aquifer storage - dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] erivative in baseflow flux w.r.t. aquifer storage (s-1) - - ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables - dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above - dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat & ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below - - ) ! association to data in structures - - ! ***** - ! * PRELIMINARIES... - ! ****************** - - !print*, '***** nSnowSoilNrg, nSnowOnlyNrg, nSoilOnlyNrg, nSnowSoilHyd, nSnowOnlyHyd, nSoilOnlyHyd = ', & - ! nSnowSoilNrg, nSnowOnlyNrg, nSoilOnlyNrg, nSnowSoilHyd, nSnowOnlyHyd, nSoilOnlyHyd - - ! increment the number of flux calls - numFluxCalls = numFluxCalls+1 - - ! modify the groundwater representation for this single-column implementation - select case(ixSpatialGroundwater) - case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale - case(localColumn); local_ixGroundwater = ixGroundwater ! go with the specified decision - case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return - end select ! (modify the groundwater representation for this single-column implementation) - - ! initialize liquid water fluxes throughout the snow and soil domains - ! NOTE: used in the energy routines, which is called before the hydrology routines - if(firstFluxCall)then - if(nSnow > 0) iLayerLiqFluxSnow(0:nSnow) = 0._rkind - iLayerLiqFluxSoil(0:nSoil) = 0._rkind - end if - - ! ***** - ! * CALCULATE ENERGY FLUXES OVER VEGETATION... - ! ********************************************* - - ! identify the need to calculate the energy flux over vegetation - doVegNrgFlux = (ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing .or. ixTopNrg/=integerMissing) - - ! check if there is a need to calculate the energy fluxes over vegetation - if(doVegNrgFlux)then - - ! derivative in canopy liquid storage w.r.t. canopy temperature - dCanLiq_dTcanopy = dTheta_dTkCanopy*iden_water*canopyDepth ! kg m-2 K-1 - - ! calculate the energy fluxes over vegetation - call vegNrgFlux(& - ! input: model control - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - ! input: model state variables - upperBoundTemp, & ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature - scalarCanairTempTrial, & ! intent(in): trial value of the canopy air space temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - mLayerTempTrial(1), & ! intent(in): trial value of ground temperature (K) - scalarCanopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) - ! input: model derivatives - dCanLiq_dTcanopy, & ! intent(in): derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) - ! input/output: data structures - type_data, & ! intent(in): type of vegetation and soil - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): index data - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - bvar_data, & ! intent(in): model variables for the local basin - model_decisions, & ! intent(in): model decisions - ! output: liquid water fluxes associated with evaporation/transpiration - scalarCanopyTranspiration, & ! intent(out): canopy transpiration (kg m-2 s-1) - scalarCanopyEvaporation, & ! intent(out): canopy evaporation/condensation (kg m-2 s-1) - scalarGroundEvaporation, & ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - ! output: fluxes - scalarCanairNetNrgFlux, & ! intent(out): net energy flux for the canopy air space (W m-2) - scalarCanopyNetNrgFlux, & ! intent(out): net energy flux for the vegetation canopy (W m-2) - scalarGroundNetNrgFlux, & ! intent(out): net energy flux for the ground surface (W m-2) - ! output: flux derivatives - dCanairNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) - dCanairNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) - dCanairNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) - dCanopyNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) - dCanopyNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) - dCanopyNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - dGroundNetFlux_dCanairTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) - dGroundNetFlux_dCanopyTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) - dGroundNetFlux_dGroundTemp, & ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - ! output: liquid water flux derivatives (canopy evap) - dCanopyEvaporation_dCanLiq, & ! intent(out): derivative in canopy evaporation w.r.t. canopy liquid water content (s-1) - dCanopyEvaporation_dTCanair, & ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTCanopy, & ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTGround, & ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - ! output: liquid water flux derivatives (ground evap) - dGroundEvaporation_dCanLiq, & ! intent(out): derivative in ground evaporation w.r.t. canopy liquid water content (s-1) - dGroundEvaporation_dTCanair, & ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTCanopy, & ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTGround, & ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - ! output: cross derivative terms - dCanopyNetFlux_dCanLiq, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dGroundNetFlux_dCanLiq, & ! intent(out): derivative in net ground fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - ! check fluxes - if(globalPrintFlag)then - print*, '**' - write(*,'(a,1x,10(f30.20))') 'canopyDepth = ', canopyDepth - write(*,'(a,1x,10(f30.20))') 'mLayerDepth(1:2) = ', mLayerDepth(1:2) - write(*,'(a,1x,10(f30.20))') 'scalarCanairTempTrial = ', scalarCanairTempTrial ! trial value of the canopy air space temperature (K) - write(*,'(a,1x,10(f30.20))') 'scalarCanopyTempTrial = ', scalarCanopyTempTrial ! trial value of canopy temperature (K) - write(*,'(a,1x,10(f30.20))') 'mLayerTempTrial(1:2) = ', mLayerTempTrial(1:2) ! trial value of ground temperature (K) - write(*,'(a,1x,10(f30.20))') 'scalarCanairNetNrgFlux = ', scalarCanairNetNrgFlux - write(*,'(a,1x,10(f30.20))') 'scalarCanopyNetNrgFlux = ', scalarCanopyNetNrgFlux - write(*,'(a,1x,10(f30.20))') 'scalarGroundNetNrgFlux = ', scalarGroundNetNrgFlux - write(*,'(a,1x,10(f30.20))') 'dGroundNetFlux_dGroundTemp = ', dGroundNetFlux_dGroundTemp - endif ! if checking fluxes - - endif ! if calculating the energy fluxes over vegetation - - ! ***** - ! * CALCULATE ENERGY FLUXES THROUGH THE SNOW-SOIL DOMAIN... - ! ********************************************************** - - ! check the need to compute energy fluxes throughout the snow+soil domain - if(nSnowSoilNrg>0)then - - ! calculate energy fluxes at layer interfaces through the snow and soil domain - call ssdNrgFlux(& - ! input: model control - (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution - ! input: fluxes and derivatives at the upper boundary - scalarGroundNetNrgFlux, & ! intent(in): total flux at the ground surface (W m-2) - dGroundNetFlux_dGroundTemp, & ! intent(in): derivative in total ground surface flux w.r.t. ground temperature (W m-2 K-1) - ! input: liquid water fluxes throughout the snow and soil domains - iLayerLiqFluxSnow, & ! intent(in): liquid flux at the interface of each snow layer (m s-1) - iLayerLiqFluxSoil, & ! intent(in): liquid flux at the interface of each soil layer (m s-1) - ! input: trial value of model state variabes - mLayerTempTrial, & ! intent(in): trial temperature at the current iteration (K) - ! input-output: data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model indices - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output: fluxes and derivatives at all layer interfaces - iLayerNrgFlux, & ! intent(out): energy flux at the layer interfaces (W m-2) - dNrgFlux_dTempAbove, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (W m-2 K-1) - dNrgFlux_dTempBelow, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (W m-2 K-1) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! calculate net energy fluxes for each snow and soil layer (J m-3 s-1) - do iLayer=1,nLayers - mLayerNrgFlux(iLayer) = -(iLayerNrgFlux(iLayer) - iLayerNrgFlux(iLayer-1))/mLayerDepth(iLayer) - if(globalPrintFlag)then - if(iLayer < 10) write(*,'(a,1x,i4,1x,10(f25.15,1x))') 'iLayer, iLayerNrgFlux(iLayer-1:iLayer), mLayerNrgFlux(iLayer) = ', iLayer, iLayerNrgFlux(iLayer-1:iLayer), mLayerNrgFlux(iLayer) - endif - end do - - endif ! if computing energy fluxes throughout the snow+soil domain - - - ! ***** - ! * CALCULATE THE LIQUID FLUX THROUGH VEGETATION... - ! ************************************************** - - ! check the need to compute the liquid water fluxes through vegetation - if(ixVegHyd/=integerMissing)then - - ! calculate liquid water fluxes through vegetation - call vegLiqFlux(& - ! input - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation - scalarCanopyLiqTrial, & ! intent(in): trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) - scalarRainfall, & ! intent(in): rainfall rate (kg m-2 s-1) - ! input-output: data structures - mpar_data, & ! intent(in): model parameters - diag_data, & ! intent(in): local HRU diagnostic model variables - ! output - scalarThroughfallRain, & ! intent(out): rain that reaches the ground without ever touching the canopy (kg m-2 s-1) - scalarCanopyLiqDrainage, & ! intent(out): drainage of liquid water from the vegetation canopy (kg m-2 s-1) - scalarThroughfallRainDeriv, & ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) - scalarCanopyLiqDrainageDeriv, & ! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! calculate the net liquid water flux for the vegetation canopy - scalarCanopyNetLiqFlux = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage - - ! calculate the total derivative in the downward liquid flux - scalarCanopyLiqDeriv = scalarThroughfallRainDeriv + scalarCanopyLiqDrainageDeriv - - ! test - if(globalPrintFlag)then - print*, '**' - print*, 'scalarRainfall = ', scalarRainfall - print*, 'scalarThroughfallRain = ', scalarThroughfallRain - print*, 'scalarCanopyEvaporation = ', scalarCanopyEvaporation - print*, 'scalarCanopyLiqDrainage = ', scalarCanopyLiqDrainage - print*, 'scalarCanopyNetLiqFlux = ', scalarCanopyNetLiqFlux - print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial - endif - - endif ! computing the liquid water fluxes through vegetation - - ! ***** - ! * CALCULATE THE LIQUID FLUX THROUGH SNOW... - ! ******************************************** - - ! check the need to compute liquid water fluxes through snow - if(nSnowOnlyHyd>0)then - - ! compute liquid fluxes through snow - call snowLiqFlx(& - ! input: model control - nSnow, & ! intent(in): number of snow layers - firstFluxCall, & ! intent(in): the first flux call (compute variables that are constant over the iterations) - (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution - ! input: forcing for the snow domain - scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) - scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) - ! input: model state vector - mLayerVolFracLiqTrial(1:nSnow), & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) - ! input-output: data structures - indx_data, & ! intent(in): model indices - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output: fluxes and derivatives - iLayerLiqFluxSnow(0:nSnow), & ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) - iLayerLiqFluxSnowDeriv(0:nSnow), & ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! define forcing for the soil domain - scalarRainPlusMelt = iLayerLiqFluxSnow(nSnow) ! drainage from the base of the snowpack - - ! calculate net liquid water fluxes for each soil layer (s-1) - do iLayer=1,nSnow - mLayerLiqFluxSnow(iLayer) = -(iLayerLiqFluxSnow(iLayer) - iLayerLiqFluxSnow(iLayer-1))/mLayerDepth(iLayer) - !write(*,'(a,1x,i4,1x,2(f16.10,1x))') 'iLayer, mLayerLiqFluxSnow(iLayer), iLayerLiqFluxSnow(iLayer-1) = ', & - ! iLayer, mLayerLiqFluxSnow(iLayer), iLayerLiqFluxSnow(iLayer-1) - end do - - ! compute drainage from the soil zone (needed for mass balance checks) - scalarSnowDrainage = iLayerLiqFluxSnow(nSnow) - - else - - ! define forcing for the soil domain for the case of no snow layers - ! NOTE: in case where nSnowOnlyHyd==0 AND snow layers exist, then scalarRainPlusMelt is taken from the previous flux evaluation - if(nSnow==0)then - scalarRainPlusMelt = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water & ! liquid flux from the canopy (m s-1) - + drainageMeltPond/iden_water ! melt of the snow without a layer (m s-1) - endif ! if no snow layers - - endif - - ! ***** - ! * CALCULATE THE LIQUID FLUX THROUGH SOIL... - ! ******************************************** - - ! check the need to calculate the liquid flux through soil - if(nSoilOnlyHyd>0)then - - ! calculate the liquid flux through soil - call soilLiqFlx(& - ! input: model control - nSoil, & ! intent(in): number of soil layers - firstSplitOper, & ! intent(in): flag indicating first flux call in a splitting operation - (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution - .true., & ! intent(in): flag indicating if derivatives are desired - ! input: trial state variables - mLayerTempTrial(nSnow+1:nLayers), & ! intent(in): trial temperature at the current iteration (K) - mLayerMatricHeadLiqTrial(1:nSoil), & ! intent(in): liquid water matric potential (m) - mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of ice (-) - ! input: pre-computed deriavatives - mLayerdTheta_dTk(nSnow+1:nLayers), & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - dPsiLiq_dTemp(1:nSoil), & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) - ! input: fluxes - scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) - scalarGroundEvaporation, & ! intent(in): ground evaporation (kg m-2 s-1) - scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) - ! input-output: data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model indices - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output: diagnostic variables for surface runoff - scalarMaxInfilRate, & ! intent(inout): maximum infiltration rate (m s-1) - scalarInfilArea, & ! intent(inout): fraction of unfrozen area where water can infiltrate (-) - scalarFrozenArea, & ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) - scalarSurfaceRunoff, & ! intent(inout): surface runoff (m s-1) - ! output: diagnostic variables for model layers - mLayerdTheta_dPsi, & ! intent(inout): derivative in the soil water characteristic w.r.t. psi (m-1) - mLayerdPsi_dTheta, & ! intent(inout): derivative in the soil water characteristic w.r.t. theta (m) - dHydCond_dMatric, & ! intent(inout): derivative in hydraulic conductivity w.r.t matric head (s-1) - ! output: fluxes - scalarInfiltration, & ! intent(inout): surface infiltration rate (m s-1) -- controls on infiltration only computed for iter==1 - iLayerLiqFluxSoil, & ! intent(inout): liquid fluxes at layer interfaces (m s-1) - mLayerTranspire, & ! intent(inout): transpiration loss from each soil layer (m s-1) - mLayerHydCond, & ! intent(inout): hydraulic conductivity in each layer (m s-1) - ! output: derivatives in fluxes w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below (m s-1 or s-1) - dq_dHydStateAbove, & ! intent(inout): derivatives in the flux w.r.t. matric head in the layer above (s-1) - dq_dHydStateBelow, & ! intent(inout): derivatives in the flux w.r.t. matric head in the layer below (s-1) - ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) - dq_dNrgStateAbove, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) - dq_dNrgStateBelow, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! calculate net liquid water fluxes for each soil layer (s-1) - do iLayer=1,nSoil - mLayerLiqFluxSoil(iLayer) = -(iLayerLiqFluxSoil(iLayer) - iLayerLiqFluxSoil(iLayer-1))/mLayerDepth(iLayer+nSnow) - !if(iLayer<8) write(*,'(a,1x,2(i4,1x),3(e20.10),f12.7)') 'iLayerLiqFluxSoil(iLayer-1), iLayerLiqFluxSoil(iLayer), mLayerLiqFluxSoil(iLayer) = ', iLayer-1, iLayer, & - ! iLayerLiqFluxSoil(iLayer-1), iLayerLiqFluxSoil(iLayer), mLayerLiqFluxSoil(iLayer), mLayerDepth(iLayer+nSnow) - end do - - ! calculate the soil control on infiltration - if(nSnow==0) then - ! * case of infiltration into soil - if(scalarMaxInfilRate > scalarRainPlusMelt)then ! infiltration is not rate-limited - scalarSoilControl = (1._rkind - scalarFrozenArea)*scalarInfilArea - else - scalarSoilControl = 0._rkind ! (scalarRainPlusMelt exceeds maximum infiltration rate - endif - else - ! * case of infiltration into snow - scalarSoilControl = 1._rkind - endif - - ! compute drainage from the soil zone (needed for mass balance checks) - scalarSoilDrainage = iLayerLiqFluxSoil(nSoil) - - ! expand derivatives to the total water matric potential - ! NOTE: arrays are offset because computing derivatives in interface fluxes, at the top and bottom of the layer respectively - if(globalPrintFlag) print*, 'dPsiLiq_dPsi0(1:nSoil) = ', dPsiLiq_dPsi0(1:nSoil) - dq_dHydStateAbove(1:nSoil) = dq_dHydStateAbove(1:nSoil) *dPsiLiq_dPsi0(1:nSoil) - dq_dHydStateBelow(0:nSoil-1) = dq_dHydStateBelow(0:nSoil-1)*dPsiLiq_dPsi0(1:nSoil) - - endif ! if calculating the liquid flux through soil - - ! ***** - ! * CALCULATE THE GROUNDWATER FLOW... - ! ************************************ - - ! check if computing soil hydrology - if(nSoilOnlyHyd>0)then - - ! set baseflow fluxes to zero if the topmodel baseflow routine is not used - if(local_ixGroundwater/=qbaseTopmodel)then - ! (diagnostic variables in the data structures) - scalarExfiltration = 0._rkind ! exfiltration from the soil profile (m s-1) - mLayerColumnOutflow(:) = 0._rkind ! column outflow from each soil layer (m3 s-1) - ! (variables needed for the numerical solution) - mLayerBaseflow(:) = 0._rkind ! baseflow from each soil layer (m s-1) - - ! topmodel-ish shallow groundwater - else ! local_ixGroundwater==qbaseTopmodel - - ! check the derivative matrix is sized appropriately - if(size(dBaseflow_dMatric,1)/=nSoil .or. size(dBaseflow_dMatric,2)/=nSoil)then - message=trim(message)//'expect dBaseflow_dMatric to be nSoil x nSoil' - err=20; return - endif - - ! compute the baseflow flux - call groundwatr(& - ! input: model control - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - firstFluxCall, & ! intent(in): logical flag to compute index of the lowest saturated layer - ! input: state and diagnostic variables - mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) - mLayerMatricHeadLiqTrial, & ! intent(in): liquid water matric potential (m) - mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of ice (-) - ! input: data structures - attr_data, & ! intent(in): model attributes - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output - ixSaturation, & ! intent(inout) index of lowest saturated layer (NOTE: only computed on the first iteration) - mLayerBaseflow, & ! intent(out): baseflow from each soil layer (m s-1) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - endif ! computing baseflow flux - - ! compute total baseflow from the soil zone (needed for mass balance checks) - scalarSoilBaseflow = sum(mLayerBaseflow) - - ! compute total runoff - ! (Note: scalarSoilBaseflow is zero if topmodel is not used) - ! (Note: scalarSoilBaseflow may need to re-envisioned in topmodel formulation if parts of it flow into neighboring soil rather than exfiltrate) - scalarTotalRunoff = scalarSurfaceRunoff + scalarSoilDrainage + scalarSoilBaseflow - - endif ! if computing soil hydrology - - - ! ***** - ! (7) CALCULATE FLUXES FOR THE DEEP AQUIFER... - ! ******************************************** - - ! check if computing aquifer fluxes - if(ixAqWat/=integerMissing)then - - ! identify modeling decision - if(local_ixGroundwater==bigBucket)then - - ! compute fluxes for the big bucket - call bigAquifer(& - ! input: state variables and fluxes - scalarAquiferStorageTrial, & ! intent(in): trial value of aquifer storage (m) - scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) - scalarSoilDrainage, & ! intent(in): soil drainage (m s-1) - ! input: diagnostic variables and parameters - mpar_data, & ! intent(in): model parameter structure - diag_data, & ! intent(in): diagnostic variable structure - ! output: fluxes - scalarAquiferTranspire, & ! intent(out): transpiration loss from the aquifer (m s-1) - scalarAquiferRecharge, & ! intent(out): recharge to the aquifer (m s-1) - scalarAquiferBaseflow, & ! intent(out): total baseflow from the aquifer (m s-1) - dBaseflow_dAquifer, & ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! compute total runoff (overwrite previously calculated value before considering aquifer). - ! (Note: SoilDrainage goes into aquifer, not runoff) - scalarTotalRunoff = scalarSurfaceRunoff + scalarAquiferBaseflow - - ! if no aquifer, then fluxes are zero - else - scalarAquiferTranspire = 0._rkind ! transpiration loss from the aquifer (m s-1) - scalarAquiferRecharge = 0._rkind ! recharge to the aquifer (m s-1) - scalarAquiferBaseflow = 0._rkind ! total baseflow from the aquifer (m s-1) - dBaseflow_dAquifer = 0._rkind ! change in baseflow flux w.r.t. aquifer storage (s-1) - end if ! no aquifer - - endif ! if computing aquifer fluxes - - ! ***** - ! (X) WRAP UP... - ! ************* - - ! define model flux vector for the vegetation sub-domain - if(ixCasNrg/=integerMissing) fluxVec(ixCasNrg) = scalarCanairNetNrgFlux/canopyDepth - if(ixVegNrg/=integerMissing) fluxVec(ixVegNrg) = scalarCanopyNetNrgFlux/canopyDepth - if(ixVegHyd/=integerMissing) fluxVec(ixVegHyd) = scalarCanopyNetLiqFlux ! NOTE: solid fluxes are handled separately - - ! populate the flux vector for energy - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - fluxVec( ixSnowSoilNrg(iLayer) ) = mLayerNrgFlux(iLayer) - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! populate the flux vector for hydrology - ! NOTE: ixVolFracWat and ixVolFracLiq can also include states in the soil domain, hence enable primary variable switching - if(nSnowSoilHyd>0)then ! check if any hydrology states exist - do iLayer=1,nLayers - if(ixSnowSoilHyd(iLayer)/=integerMissing)then ! check if a given hydrology state exists - select case( layerType(iLayer) ) - case(iname_snow); fluxVec( ixSnowSoilHyd(iLayer) ) = mLayerLiqFluxSnow(iLayer) - case(iname_soil); fluxVec( ixSnowSoilHyd(iLayer) ) = mLayerLiqFluxSoil(iLayer-nSnow) - case default; err=20; message=trim(message)//'expect layerType to be either iname_snow or iname_soil'; return - end select - endif ! if a given hydrology state exists - end do ! looping through non-missing energy state variables in the snow+soil domain - endif ! if any hydrology states exist - - ! compute the flux vector for the aquifer - if(ixAqWat/=integerMissing) fluxVec(ixAqWat) = scalarAquiferTranspire + scalarAquiferRecharge - scalarAquiferBaseflow - - ! set the first flux call to false - firstFluxCall=.false. - - ! end association to variables in the data structures - end associate - - end subroutine computFlux - ! ********************************************************************************************************* - ! public subroutine computFluxSundials: compute model fluxes + ! public subroutine computFlux: compute model fluxes ! ********************************************************************************************************* - subroutine computFluxSundials(& + subroutine computFlux(& ! input-output: model control nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -900,7 +148,7 @@ subroutine computFluxSundials(& err,message) ! intent(out): error code and error message ! provide access to flux subroutines USE vegNrgFlux_module,only:vegNrgFlux ! compute energy fluxes over vegetation - USE ssdNrgFlux_module,only:ssdNrgFluxSundials ! compute energy fluxes throughout the snow and soil subdomains + USE ssdNrgFlux_module,only:ssdNrgFlux ! compute energy fluxes throughout the snow and soil subdomains USE vegLiqFlux_module,only:vegLiqFlux ! compute liquid water fluxes through vegetation USE snowLiqFlx_module,only:snowLiqflx ! compute liquid water fluxes through snow USE soilLiqFlx_module,only:soilLiqflx ! compute liquid water fluxes through soil @@ -962,7 +210,7 @@ subroutine computFluxSundials(& character(LEN=256) :: cmessage ! error message of downwind routine ! -------------------------------------------------------------- ! initialize error control - err=0; message='computFluxSundials/' + err=0; message='computFlux/' ! ***** ! (0) PRELIMINARIES... @@ -1073,21 +321,21 @@ subroutine computFluxSundials(& dCanopyNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy air temperature dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy temperature dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. ground temperature - dCanopyNetFlux_dCanWat => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in net canopy fluxes w.r.t. canopy total water content + dCanopyNetFlux_dCanWat => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in net canopy fluxes w.r.t. canopy total water content dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy air temperature dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy temperature dGroundNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. ground temperature - dGroundNetFlux_dCanWat => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in net ground fluxes w.r.t. canopy total water content + dGroundNetFlux_dCanWat => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in net ground fluxes w.r.t. canopy total water content ! derivatives in evaporative fluxes w.r.t. relevant state variables dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy air temperature dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy temperature dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. ground temperature - dCanopyEvaporation_dCanWat => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy total water content + dCanopyEvaporation_dCanWat => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy total water content dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy air temperature dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy temperature dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. ground temperature - dGroundEvaporation_dCanWat => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanLiq )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy total water content + dGroundEvaporation_dCanWat => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy total water content ! derivatives in canopy water w.r.t canopy temperature dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(out): [dp] derivative of canopy liquid storage w.r.t. temperature @@ -1244,7 +492,7 @@ subroutine computFluxSundials(& if(nSnowSoilNrg>0)then ! calculate energy fluxes at layer interfaces through the snow and soil domain - call ssdNrgFluxSundials(& + call ssdNrgFlux(& ! input: model control (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution .true., & ! intent(in): flag indicating if derivatives are desired @@ -1611,7 +859,7 @@ subroutine computFluxSundials(& ! end association to variables in the data structures end associate - end subroutine computFluxSundials + end subroutine computFlux ! ********************************************************************************************************** diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 8315cb01a..e61a49e8e 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -39,7 +39,6 @@ module computJacDAE_module moisture, & ! moisture-based form of Richards' equation mixdform ! mixed form of Richards' equation - ! access the global print flag USE globalData,only:globalPrintFlag @@ -224,19 +223,19 @@ subroutine computJacDAE(& dCanopyNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. canopy air temperature dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. canopy temperature dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. ground temperature - dCanopyNetFlux_dCanWat => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in net canopy fluxes w.r.t. canopy total water content + dCanopyNetFlux_dCanWat => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in net canopy fluxes w.r.t. canopy total water content dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net ground flux w.r.t. canopy air temperature dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net ground flux w.r.t. canopy temperature - dGroundNetFlux_dCanWat => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in net ground fluxes w.r.t. canopy total water content + dGroundNetFlux_dCanWat => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in net ground fluxes w.r.t. canopy total water content ! derivatives in evaporative fluxes w.r.t. relevant state variables dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy air temperature dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy temperature dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. ground temperature - dCanopyEvaporation_dCanWat => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy total water content + dCanopyEvaporation_dCanWat => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy total water content dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy air temperature dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy temperature dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. ground temperature - dGroundEvaporation_dCanWat => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy total water content + dGroundEvaporation_dCanWat => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy total water content ! derivatives in canopy water w.r.t canopy temperature dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(in): [dp] derivative in canopy liquid storage w.r.t. temperature dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative in volumetric liquid water content w.r.t. temperature @@ -362,16 +361,21 @@ subroutine computJacDAE(& if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) if (doprint==1) print*, ixCasNrg,ixVegNrg,ixVegHyd, ixTopNrg,ixTopHyd, "watvegindices" - ! * liquid water fluxes for vegetation canopy (-) + ! * liquid water fluxes for vegetation canopy (-), dt*scalarFracLiqVeg*scalarCanopyLiqDeriv is the derivative in throughfall and canopy drainage with canopy water if(ixVegHyd/=integerMissing) aJac(ixVegHyd,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanWat - scalarCanopyLiqDeriv)*dt + 1._rkind * cj + ! * cross-derivative terms for canopy water if(ixVegHyd/=integerMissing)then + ! cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) if(ixCasNrg/=integerMissing) aJac(ixVegHyd,ixCasNrg) = -dCanopyEvaporation_dTCanair*dt + ! dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy is the derivative in throughfall and canopy drainage with canopy temperature if(ixVegNrg/=integerMissing) aJac(ixVegHyd,ixVegNrg) = -dCanopyEvaporation_dTCanopy*dt + dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy if(ixTopNrg/=integerMissing) aJac(ixVegHyd,ixTopNrg) = -dCanopyEvaporation_dTGround*dt + ! cross-derivative terms w.r.t. canopy water (kg-1 m2) if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water + ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth * cj & @@ -608,7 +612,7 @@ subroutine computJacDAE(& if(mLayerdTheta_dTk(jLayer) > verySmall) then ! ice is present aJac(nrgState,watState) = aJac(nrgState,watState) - dVolTot_dPsi0(iLayer)*LH_fus*iden_water*cj & - LH_fus*iden_water * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content - endif + endif ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above if(iLayer>1)then @@ -634,14 +638,12 @@ subroutine computJacDAE(& end do end if - - !print*, '** analytical Jacobian (full):' - !write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=1,size(aJac,2)) - !do iLayer=1,size(aJac,2) - ! write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(1:size(aJac,1),iLayer) - !end do - - !print *, '--------------------------------------------------------------' + !print*, '** analytical Jacobian (full):' + !write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=1,size(aJac,2)) + !do iLayer=1,size(aJac,2) + ! write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(1:size(aJac,1),iLayer) + !end do + !print *, '--------------------------------------------------------------' ! *** ! check diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index b02443771..ff90f9dc6 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -185,19 +185,19 @@ subroutine computJacob(& dCanopyNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. canopy air temperature dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. canopy temperature dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. ground temperature - dCanopyNetFlux_dCanLiq => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in net canopy fluxes w.r.t. canopy liquid water content + dCanopyNetFlux_dCanWat => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in net canopy fluxes w.r.t. canopy total water content dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net ground flux w.r.t. canopy air temperature dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net ground flux w.r.t. canopy temperature - dGroundNetFlux_dCanLiq => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in net ground fluxes w.r.t. canopy liquid water content + dGroundNetFlux_dCanWat => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in net ground fluxes w.r.t. canopy total water content ! derivatives in evaporative fluxes w.r.t. relevant state variables dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy air temperature dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy temperature dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. ground temperature - dCanopyEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy liquid water content + dCanopyEvaporation_dCanWat => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy total water content dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy air temperature dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy temperature dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. ground temperature - dGroundEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy liquid water content + dGroundEvaporation_dCanWat => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy total water content ! derivatives in canopy water w.r.t canopy temperature dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(in): [dp] derivative of canopy liquid storage w.r.t. temperature dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature @@ -285,7 +285,7 @@ subroutine computJacob(& ! * diagonal elements for the vegetation canopy (-) if(ixCasNrg/=integerMissing) aJac(ixDiag,ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) if(ixVegNrg/=integerMissing) aJac(ixDiag,ixVegNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanopyTemp) + dMat(ixVegNrg) - if(ixVegHyd/=integerMissing) aJac(ixDiag,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanLiq - scalarCanopyLiqDeriv)*dt + 1._rkind ! ixVegHyd: CORRECT + if(ixVegHyd/=integerMissing) aJac(ixDiag,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanWat - scalarCanopyLiqDeriv)*dt + 1._rkind ! ixVegHyd: CORRECT ! * cross-derivative terms w.r.t. canopy water if(ixVegHyd/=integerMissing)then @@ -297,8 +297,8 @@ subroutine computJacob(& if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (dt/canopyDepth) *(-dCanopyNetFlux_dCanLiq) - (1._rkind - scalarFracLiqVeg)*LH_fus/canopyDepth ! dF/dLiq - if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanLiq) + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (dt/canopyDepth) *(-dCanopyNetFlux_dCanWat) - (1._rkind - scalarFracLiqVeg)*LH_fus/canopyDepth ! dF/dLiq + if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) endif ! cross-derivative terms between surface hydrology and the temperature of the vegetation canopy (K-1) @@ -472,7 +472,7 @@ subroutine computJacob(& ! - include derivatives w.r.t. ground evaporation if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer if(computeVegFlux)then - aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanLiq/iden_water) ! dVol/dLiq (kg m-2)-1 + aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) endif @@ -529,7 +529,7 @@ subroutine computJacob(& if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) ! * liquid water fluxes for vegetation canopy (-) - if(ixVegHyd/=integerMissing) aJac(ixVegHyd,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanLiq - scalarCanopyLiqDeriv)*dt + 1._rkind + if(ixVegHyd/=integerMissing) aJac(ixVegHyd,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanWat - scalarCanopyLiqDeriv)*dt + 1._rkind ! * cross-derivative terms for canopy water if(ixVegHyd/=integerMissing)then @@ -541,8 +541,8 @@ subroutine computJacob(& if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (dt/canopyDepth) *(-dCanopyNetFlux_dCanLiq) - (1._rkind - scalarFracLiqVeg)*LH_fus/canopyDepth ! dF/dLiq - if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanLiq) + if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (dt/canopyDepth) *(-dCanopyNetFlux_dCanWat) - (1._rkind - scalarFracLiqVeg)*LH_fus/canopyDepth ! dF/dLiq + if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) endif ! cross-derivative terms w.r.t. canopy temperature (K-1) @@ -727,7 +727,7 @@ subroutine computJacob(& ! - include derivatives w.r.t. ground evaporation if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer if(computeVegFlux)then - aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanLiq/iden_water) ! dVol/dLiq (kg m-2)-1 + aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) endif diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index 82e48fd4c..6e6ff6c50 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -103,7 +103,7 @@ subroutine eval8DAE(& scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVec, & ! intent(in): model state vector - stateVecPrime, & ! intent(in): derivative of model state vector + stateVecPrime, & ! intent(in): derivative of model state vector sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures model_decisions, & ! intent(in): model decisions @@ -140,8 +140,8 @@ subroutine eval8DAE(& mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) - scalarAquiferStorageTrial, & ! intent(out): trial value of storage of water in the aquifer (m) - scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) + scalarAquiferStorageTrial, & ! intent(out): trial value of storage of water in the aquifer (m) + scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) ixSaturation, & ! intent(inout): index of the lowest saturated layer @@ -157,7 +157,7 @@ subroutine eval8DAE(& USE updateVarsSundials_module, only:updateVarsSundials ! update variables USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy USE computFlux_module, only:soilCmpresSundials ! compute soil compression - USE computFlux_module, only:computFluxSundials ! compute fluxes given a state vector + USE computFlux_module, only:computFlux ! compute fluxes given a state vector USE computHeatCap_module,only:computHeatCap ! compute heat capacity USE computHeatCap_module,only:computHeatCapAnalytic ! compute heat capacity USE computHeatCap_module,only:computCm @@ -221,7 +221,7 @@ subroutine eval8DAE(& real(rkind),intent(in) :: mLayerVolFracIcePrev(:) ! vector of volumetric ice water content (-) real(rkind),intent(out) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) real(rkind),intent(in) :: mLayerVolFracLiqPrev(:) ! vector of volumetric liquid water content (-) - real(rkind),intent(out) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + real(rkind),intent(out) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) real(rkind),intent(in) :: scalarAquiferStoragePrev ! value of storage of water in the aquifer (m) real(rkind),intent(in) :: mLayerEnthalpyPrev(:) ! vector of enthalpy for snow+soil layers (J m-3) real(rkind),intent(out) :: mLayerEnthalpyTrial(:) ! trial vector of enthalpy for snow+soil layers (J m-3) @@ -268,7 +268,7 @@ subroutine eval8DAE(& real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m - + ! -------------------------------------------------------------------------------------------------------------------------------- @@ -307,7 +307,7 @@ subroutine eval8DAE(& ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) - heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(out): volumetric heat capacity of vegetation canopy + heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(out): volumetric heat capacity of vegetation canopy mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & ! intent(out): heat capacity for snow and soil ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- @@ -336,7 +336,7 @@ subroutine eval8DAE(& if(count(ixSnowOnlyNrg/=integerMissing)>0)then if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. endif - + ! loop through non-missing hydrology state variables in the snow+soil domain do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) @@ -381,7 +381,7 @@ subroutine eval8DAE(& ixBeg = 1 ixEnd = nSoil endif - + ! initialize to state variable from the last update scalarCanopyTempTrial = scalarCanopyTempPrev scalarCanopyLiqTrial = scalarCanopyLiqPrev @@ -390,7 +390,7 @@ subroutine eval8DAE(& mLayerVolFracWatTrial = mLayerVolFracWatPrev mLayerVolFracLiqTrial = mLayerVolFracLiqPrev mLayerVolFracIceTrial = mLayerVolFracIcePrev - mLayerMatricHeadTrial = mLayerMatricHeadPrev + mLayerMatricHeadTrial = mLayerMatricHeadPrev scalarAquiferStorageTrial = scalarAquiferStoragePrev ! extract variables from the model state vector @@ -416,10 +416,10 @@ subroutine eval8DAE(& ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - - - call varExtractSundials(& + + + + call varExtractSundials(& ! input stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -441,12 +441,12 @@ subroutine eval8DAE(& ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - - call updateVarsSundials(& + + + call updateVarsSundials(& ! input dt_cur, & - .false., & ! intent(in): logical flag to adjust temperature to account for the energy + .false., & ! intent(in): logical flag to adjust temperature to account for the energy mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU @@ -470,16 +470,16 @@ subroutine eval8DAE(& mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! + mLayerTempPrime, & ! mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) - mLayerVolFracIcePrime, & ! + mLayerVolFracIcePrime, & ! mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - + ! print the water content if(globalPrintFlag)then @@ -487,8 +487,8 @@ subroutine eval8DAE(& if(iJac1 1e-14_rkind) then - scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur + scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur do concurrent (iLayer=1:nLayers) mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur end do endif ! if dt_cur is not too samll - else ! if using closed formula of heat capacity + else ! if using closed formula of heat capacity call computHeatCapAnalytic(& ! input: control variables computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux @@ -567,12 +567,12 @@ subroutine eval8DAE(& mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices ! output - heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy + heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow ! output: error control err,cmessage) ! intent(out): error control endif - + ! compute multiplier of state vector call computStatMult(& ! input @@ -584,7 +584,7 @@ subroutine eval8DAE(& sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - + ! update thermal conductivity call computThermConduct(& ! input: control variables @@ -604,9 +604,9 @@ subroutine eval8DAE(& if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if end if ! updateCp - - - if(needCm)then + + + if(needCm)then ! compute C_m call computCm(& ! input: control variables @@ -626,12 +626,12 @@ subroutine eval8DAE(& scalarCanopyCmTrial = 0._qp mLayerCmTrial = 0._qp end if ! needCm - + ! save the number of flux calls per time step indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) + 1 ! compute the fluxes for a given state vector - call computFluxSundials(& + call computFlux(& ! input-output: model control nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -674,9 +674,9 @@ subroutine eval8DAE(& ! output: error control err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - + firstSplitOper = .true. - + ! compute soil compressibility (-) and its derivative w.r.t. matric head (m) ! NOTE: we already extracted trial matrix head and volumetric liquid water as part of the flux calculations @@ -694,9 +694,9 @@ subroutine eval8DAE(& dCompress_dPsi, & ! intent(inout): derivative in compressibility w.r.t. matric head (m-1) err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - + ! print *, 'dt = ', dt -! print *, 'dt_cur = ', dt_cur +! print *, 'dt_cur = ', dt_cur ! compute the residual vector call computResidDAE(& @@ -708,7 +708,7 @@ subroutine eval8DAE(& sMul, & ! intent(in): state vector multiplier (used in the residual calculations) fluxVec, & ! intent(in): flux vector ! input: state variables (already disaggregated into scalars and vectors) - scalarCanopyTempTrial, & ! intent(in): + scalarCanopyTempTrial, & ! intent(in): mLayerTempTrial, & ! intent(in) scalarCanairTempPrime, & ! intent(in): Prime value for the temperature of the canopy air space (K) scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K) @@ -733,13 +733,13 @@ subroutine eval8DAE(& resVec, & ! intent(out): residual vector err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - + !print *, '=====================================================================================' ! end association with the information in the data structures end associate - + end subroutine eval8DAE end module eval8DAE_module diff --git a/build/source/engine/eval8JacDAE.f90 b/build/source/engine/eval8JacDAE.f90 index cae27b10f..225d7adee 100644 --- a/build/source/engine/eval8JacDAE.f90 +++ b/build/source/engine/eval8JacDAE.f90 @@ -88,7 +88,7 @@ module eval8JacDAE_module ! public subroutine eval8JacDAE: compute the Jacobian matrix ! ********************************************************************************************************** subroutine eval8JacDAE(& - ! input: model control + ! input: model control cj, & ! intent(in): this scalar changes whenever the step size or method order changes dt, & ! intent(in): time step nSnow, & ! intent(in): number of snow layers @@ -99,7 +99,7 @@ subroutine eval8JacDAE(& scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVec, & ! intent(in): model state vector - stateVecPrime, & ! intent(in): derivative of model state vector + stateVecPrime, & ! intent(in): derivative of model state vector sMul, & ! intent(in): state vector multiplier (used in the residual calculations) ! input: data structures model_decisions, & ! intent(in): model decisions @@ -206,7 +206,7 @@ subroutine eval8JacDAE(& ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="eval8JacDAE/" - + ! extract variables from the model state vector call varExtract(& @@ -231,11 +231,11 @@ subroutine eval8JacDAE(& ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - - + + + ! extract derivative of variables from derivative of the model state vector - call varExtractSundials(& + call varExtractSundials(& ! input stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -257,11 +257,11 @@ subroutine eval8JacDAE(& ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - - - call updateVars4JacDAE(& + + + + call updateVars4JacDAE(& ! input .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU @@ -285,24 +285,24 @@ subroutine eval8JacDAE(& mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! + mLayerTempPrime, & ! mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) - mLayerVolFracIcePrime, & ! + mLayerVolFracIcePrime, & ! mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - + + ! ----- ! * compute the Jacobian matrix... ! -------------------------------- ! compute the analytical Jacobian matrix - ! NOTE: The derivatives were computed in the previous call to computFluxSundials + ! NOTE: The derivatives were computed in the previous call to computFlux ! This occurred either at the call to eval8DAE at the start of sysSolveSundials ! or in the call to eval8DAE in the previous iteration dt1 = 1._qp @@ -341,11 +341,11 @@ subroutine eval8JacDAE(& err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - + ! end association with the information in the data structures end associate - + end subroutine eval8JacDAE end module eval8JacDAE_module diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 59eea70e0..ac8721ac4 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -98,10 +98,10 @@ module eval8summa_module implicit none private public::eval8summa -public::eval8summaSundials contains + ! ********************************************************************************************************** ! public subroutine eval8summa: compute the residual vector and the Jacobian matrix ! ********************************************************************************************************** @@ -150,7 +150,7 @@ subroutine eval8summa(& USE getVectorz_module, only:varExtract ! extract variables from the state vector USE updateVars_module, only:updateVars ! update prognostic variables USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy - USE computFlux_module, only:soilCmpres ! compute soil compression + USE computFlux_module, only:soilCmpres ! compute soil compression, use non-sundials version because sundials version needs mLayerMatricHeadPrime USE computFlux_module, only:computFlux ! compute fluxes given a state vector USE computResid_module,only:computResid ! compute residuals given a state vector implicit none @@ -450,468 +450,6 @@ subroutine eval8summa(& ! compute the fluxes for a given state vector call computFlux(& - ! input-output: model control - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to denote the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - scalarSfcMeltPond/dt, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) - ! input: state variables - scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) - scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) - mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) - mLayerMatricHeadLiqTrial, & ! intent(in): trial value for the liquid water matric potential in each soil layer (m) - scalarAquiferStorageTrial, & ! intent(in): trial value of storage of water in the aquifer (m) - ! input: diagnostic variables defining the liquid water and ice content - scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) - mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liquid water content in each snow and soil layer (-) - mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) - ! input: data structures - model_decisions, & ! intent(in): model decisions - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): index data - ! input-output: data structures - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - deriv_data, & ! intent(out): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: flux vector and baseflow derivatives - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - fluxVec, & ! intent(out): flux vector (mixed units) - ! output: error control - err,cmessage) ! intent(out): error code and error message - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! compute soil compressibility (-) and its derivative w.r.t. matric head (m) - ! NOTE: we already extracted trial matrix head and volumetric liquid water as part of the flux calculations - call soilCmpres(& - ! input: - ixRichards, & ! intent(in): choice of option for Richards' equation - ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers - mLayerMatricHeadLiq(1:nSoil), & ! intent(in): matric head at the start of the time step (m) - mLayerMatricHeadLiqTrial(1:nSoil), & ! intent(in): trial value of matric head (m) - mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) - mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric ice content in each soil layer (-) - specificStorage, & ! intent(in): specific storage coefficient (m-1) - theta_sat, & ! intent(in): soil porosity (-) - ! output: - mLayerCompress, & ! intent(inout): compressibility of the soil matrix (-) - dCompress_dPsi, & ! intent(inout): derivative in compressibility w.r.t. matric head (m-1) - err,cmessage) ! intent(out): error code and error message - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! compute the total change in storage associated with compression of the soil matrix (kg m-2) - scalarSoilCompress = sum(mLayerCompress(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water - - ! vegetation domain: get the correct water states (total water, or liquid water, depending on the state type) - if(computeVegFlux)then - scalarCanopyHydTrial = merge(scalarCanopyWatTrial, scalarCanopyLiqTrial, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) - else - scalarCanopyHydTrial = realMissing - endif - - ! snow+soil domain: get the correct water states (total water, or liquid water, depending on the state type) - mLayerVolFracHydTrial = merge(mLayerVolFracWatTrial, mLayerVolFracLiqTrial, (ixHydType==iname_watLayer .or. ixHydType==iname_matLayer) ) - - ! compute the residual vector - call computResid(& - ! input: model control - dt, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - ! input: flux vectors - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) - fluxVec, & ! intent(in): flux vector - ! input: state variables (already disaggregated into scalars and vectors) - scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) - scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) - scalarCanopyHydTrial, & ! intent(in): trial value of canopy hydrology state variable (kg m-2) - mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) - mLayerVolFracHydTrial, & ! intent(in): trial vector of volumetric water content (-) - scalarAquiferStorageTrial, & ! intent(in): trial value of storage of water in the aquifer (m) - ! input: diagnostic variables defining the liquid water and ice content (function of state variables) - scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) - mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) - ! input: data structures - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - flux_data, & ! intent(in): model fluxes for a local HRU - indx_data, & ! intent(in): index data - ! output - resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - resVec, & ! intent(out): residual vector - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! compute the function evaluation - rVecScaled = fScale(:)*real(resVec(:), rkind) ! scale the residual vector (NOTE: residual vector is in quadruple precision) - fEval = 0.5_rkind*dot_product(rVecScaled,rVecScaled) - - ! end association with the information in the data structures - end associate - - end subroutine eval8summa - - - ! ********************************************************************************************************** - ! public subroutine eval8summaSundials: compute the residual vector and the Jacobian matrix - ! ********************************************************************************************************** - subroutine eval8summaSundials(& - ! input: model control - dt, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - nState, & ! intent(in): total number of state variables - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVecTrial, & ! intent(in): model state vector - fScale, & ! intent(in): function scaling vector - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - ! output: flux and residual vectors - feasible, & ! intent(out): flag to denote the feasibility of the solution - fluxVec, & ! intent(out): flux vector - resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - resVec, & ! intent(out): residual vector - fEval, & ! intent(out): function evaluation - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - ! provide access to subroutines - USE getVectorz_module, only:varExtract ! extract variables from the state vector - USE updateVars_module, only:updateVars ! update prognostic variables - USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy - USE computFlux_module, only:soilCmpres ! compute soil compression, use non-sundials version because sundials version needs mLayerMatricHeadPrime - USE computFlux_module, only:computFluxSundials ! compute fluxes given a state vector - USE computResid_module,only:computResid ! compute residuals given a state vector - implicit none - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - ! input: model control - real(rkind),intent(in) :: dt ! length of the time step (seconds) - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - integer(i4b),intent(in) :: nState ! total number of state variables - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - ! input: state vectors - real(rkind),intent(in) :: stateVecTrial(:) ! model state vector - real(rkind),intent(in) :: fScale(:) ! function scaling vector - real(rkind),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) - ! input: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(zLookup), intent(in) :: lookup_data ! lookup tables - type(var_i), intent(in) :: type_data ! type of vegetation and soil - type(var_d), intent(in) :: attr_data ! spatial attributes - type(var_dlength), intent(in) :: mpar_data ! model parameters - type(var_d), intent(in) :: forc_data ! model forcing data - type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin - type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU - ! output: data structures - type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - ! output: flux and residual vectors - logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution - real(rkind),intent(out) :: fluxVec(:) ! flux vector - real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation - real(rkind),intent(out) :: resVec(:) ! NOTE: qp ! residual vector - real(rkind),intent(out) :: fEval ! function evaluation - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - ! -------------------------------------------------------------------------------------------------------------------------------- - ! state variables - real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial value for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial value for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial value for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) - real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) - ! diagnostic variables - logical(lgt),parameter :: needEnthalpy=.true. ! flag to compute enthalpy - real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) - ! other local variables - integer(i4b) :: iLayer ! index of model layer in the snow+soil domain - integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain - integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine - integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) - real(rkind) :: xMin,xMax ! minimum and maximum values for water content - real(rkind) :: scalarCanopyHydTrial ! trial value for mass of water on the vegetation canopy (kg m-2) - real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) - real(rkind),dimension(nLayers) :: mLayerVolFracHydTrial ! trial value for volumetric fraction of water (-), general vector merged from Wat and Liq - real(rkind),dimension(nState) :: rVecScaled ! scaled residual vector - character(LEN=256) :: cmessage ! error message of downwind routine - ! -------------------------------------------------------------------------------------------------------------------------------- - ! association to variables in the data structures - ! -------------------------------------------------------------------------------------------------------------------------------- - associate(& - ! model decisions - ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ! soil parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) - ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! model state variables - scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) - ! model diagnostic variables - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) -! enthalpy - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) - ! soil compression - scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in storage associated with compression of the soil matrix (-) - ! derivatives - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential - dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) - ! mapping - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) - ! indices - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable (nrg) - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable (nrg) - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain - layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) - ) ! association to variables in the data structures - ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="eval8summaSundials/" - - ! check the feasibility of the solution - feasible=.true. - - ! check that the canopy air space temperature is reasonable - if(ixCasNrg/=integerMissing)then - if(stateVecTrial(ixCasNrg) > canopyTempMax) feasible=.false. - endif - - ! check that the canopy air space temperature is reasonable - if(ixVegNrg/=integerMissing)then - if(stateVecTrial(ixVegNrg) > canopyTempMax) feasible=.false. - endif - - ! check canopy liquid water is not negative - if(ixVegHyd/=integerMissing)then - if(stateVecTrial(ixVegHyd) < 0._rkind) feasible=.false. - end if - - ! check snow temperature is below freezing - if(count(ixSnowOnlyNrg/=integerMissing)>0)then - if(any(stateVecTrial( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. - endif - - ! loop through non-missing hydrology state variables in the snow+soil domain - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) - - ! check the minimum and maximum water constraints - if(ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_liqLayer)then - - ! --> minimum - if (layerType(iLayer) == iname_soil) then - xMin = theta_sat(iLayer-nSnow) - else - xMin = 0._rkind - endif - - ! --> maximum - select case( layerType(iLayer) ) - case(iname_snow); xMax = merge(iden_ice, 1._rkind - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) - case(iname_soil); xMax = merge(theta_sat(iLayer-nSnow), theta_sat(iLayer-nSnow) - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) - end select - - ! --> check - if(stateVecTrial( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVecTrial( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. - !if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax - - endif ! if water states - - end do ! loop through non-missing hydrology state variables in the snow+soil domain - - ! early return for non-feasible solutions - if(.not.feasible)then - fluxVec(:) = realMissing - resVec(:) = quadMissing - fEval = realMissing - return - end if - - ! get the start and end indices for the soil compression calculations - if(scalarSolution)then - jState = pack(ixControlVolume, ixMapFull2Subset/=integerMissing) - ixBeg = jState(1) - ixEnd = jState(1) - else - ixBeg = 1 - ixEnd = nSoil - endif - - ! extract variables from the model state vector - call varExtract(& - ! input - stateVecTrial, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(out): trial value of canopy ice content (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! update diagnostic variables - call updateVars(& - ! input - .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - lookup_data, & ! intent(in): lookup tables for a local HRU - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! compute enthalpy (J m-3) - if(needEnthalpy)then - call t2enthalpy(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) - ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy, & ! intent(out): enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - endif ! if computing enthalpy - - ! print the states in the canopy domain - !print*, 'dt = ', dt - !write(*,'(a,1x,10(f20.10,1x))') 'scalarCanopyTempTrial = ', scalarCanopyTempTrial - !write(*,'(a,1x,10(f20.10,1x))') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - !write(*,'(a,1x,10(f20.10,1x))') 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial - !write(*,'(a,1x,10(f20.10,1x))') 'scalarCanopyIceTrial = ', scalarCanopyIceTrial - - ! print the states in the snow+soil domain - !write(*,'(a,1x,10(f20.10,1x))') 'mLayerTempTrial = ', mLayerTempTrial(iJac1:min(nLayers,iJac2)) - !write(*,'(a,1x,10(f20.10,1x))') 'mLayerVolFracWatTrial = ', mLayerVolFracWatTrial(iJac1:min(nLayers,iJac2)) - !write(*,'(a,1x,10(f20.10,1x))') 'mLayerVolFracLiqTrial = ', mLayerVolFracLiqTrial(iJac1:min(nLayers,iJac2)) - !write(*,'(a,1x,10(f20.10,1x))') 'mLayerVolFracIceTrial = ', mLayerVolFracIceTrial(iJac1:min(nLayers,iJac2)) - !write(*,'(a,1x,10(f20.10,1x))') 'mLayerMatricHeadTrial = ', mLayerMatricHeadTrial(iJac1:min(nSoil,iJac2)) - !write(*,'(a,1x,10(f20.10,1x))') 'mLayerMatricHeadLiqTrial = ', mLayerMatricHeadLiqTrial(iJac1:min(nSoil,iJac2)) - - ! print the water content - if(globalPrintFlag)then - if(iJac1 model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): method used to calculate flux derivatives - ix_bcLowrTdyn => model_decisions(iLookDECISIONS%bcLowrTdyn)%iDecision, & ! intent(in): method used to calculate the lower boundary condition for thermodynamics - ! input: model coordinates - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers - layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) - ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat, & ! intent(in): index in the state subset for energy state variables in the snow+soil domain - ! input: thermal properties - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): depth of each layer (m) - mLayerHeight => prog_data%var(iLookPROG%mLayerHeight)%dat, & ! intent(in): height at the mid-point of each layer (m) - iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(in): thermal conductivity at the interface of each layer (W m-1 K-1) - lowerBoundTemp => mpar_data%var(iLookPARAM%lowerBoundTemp)%dat(1), & ! intent(in): temperature of the lower boundary (K) - ! output: diagnostic fluxes - iLayerConductiveFlux => flux_data%var(iLookFLUX%iLayerConductiveFlux)%dat, & ! intent(out): conductive energy flux at layer interfaces at end of time step (W m-2) - iLayerAdvectiveFlux => flux_data%var(iLookFLUX%iLayerAdvectiveFlux)%dat & ! intent(out): advective energy flux at layer interfaces at end of time step (W m-2) - ) ! association of local variables with information in the data structures - ! ------------------------------------------------------------------------------------------------------------------------------------------------------ - ! initialize error control - err=0; message='ssdNrgFlux/' - - ! set conductive and advective fluxes to missing in the upper boundary - ! NOTE: advective flux at the upper boundary is included in the ground heat flux - iLayerConductiveFlux(0) = valueMissing - iLayerAdvectiveFlux(0) = valueMissing - - ! get the indices for the snow+soil layers - if(scalarSolution)then - ixLayerDesired = pack(ixLayerState, ixSnowSoilNrg/=integerMissing) - ixTop = ixLayerDesired(1) - ixBot = ixLayerDesired(1) - else - ixTop = 1 - ixBot = nLayers - endif - - ! ------------------------------------------------------------------------------------------------------------------------- - ! ***** compute the conductive fluxes at layer interfaces ***** - ! ------------------------------------------------------------------------------------------------------------------------- - do iLayer=ixTop,ixBot ! (loop through model layers) - - ! compute fluxes at the lower boundary -- positive downwards - if(iLayer==nLayers)then - ! flux depends on the type of lower boundary condition - select case(ix_bcLowrTdyn) ! (identify the lower boundary condition for thermodynamics - case(prescribedTemp); iLayerConductiveFlux(nLayers) = -iLayerThermalC(iLayer)*(lowerBoundTemp - mLayerTempTrial(iLayer))/(mLayerDepth(iLayer)*0.5_rkind) - case(zeroFlux); iLayerConductiveFlux(nLayers) = 0._rkind - case default; err=20; message=trim(message)//'unable to identify lower boundary condition for thermodynamics'; return - end select ! (identifying the lower boundary condition for thermodynamics) - - ! compute fluxes within the domain -- positive downwards - else - iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) / & - (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) - - !write(*,'(a,i4,1x,2(f9.3,1x))') 'iLayer, iLayerConductiveFlux(iLayer), iLayerThermalC(iLayer) = ', iLayer, iLayerConductiveFlux(iLayer), iLayerThermalC(iLayer) - end if ! (the type of layer) - end do - - ! ------------------------------------------------------------------------------------------------------------------------- - ! ***** compute the advective fluxes at layer interfaces ***** - ! ------------------------------------------------------------------------------------------------------------------------- - do iLayer=ixTop,ixBot - ! get the liquid flux at layer interfaces - select case(layerType(iLayer)) - case(iname_snow); qFlux = iLayerLiqFluxSnow(iLayer) - case(iname_soil); qFlux = iLayerLiqFluxSoil(iLayer-nSnow) - case default; err=20; message=trim(message)//'unable to identify layer type'; return - end select - ! compute fluxes at the lower boundary -- positive downwards - if(iLayer==nLayers)then - iLayerAdvectiveFlux(iLayer) = -Cp_water*iden_water*qFlux*(lowerBoundTemp - mLayerTempTrial(iLayer)) - ! compute fluxes within the domain -- positive downwards - else - iLayerAdvectiveFlux(iLayer) = -Cp_water*iden_water*qFlux*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) - end if - end do ! looping through layers - - ! ------------------------------------------------------------------------------------------------------------------------- - ! ***** compute the total fluxes at layer interfaces ***** - ! ------------------------------------------------------------------------------------------------------------------------- - ! NOTE: ignore advective fluxes for now - iLayerNrgFlux(0) = groundNetFlux - iLayerNrgFlux(ixTop:ixBot) = iLayerConductiveFlux(ixTop:ixBot) - !print*, 'iLayerNrgFlux(0:4) = ', iLayerNrgFlux(0:4) - - ! ------------------------------------------------------------------------------------------------------------------------- - ! ***** compute the derivative in fluxes at layer interfaces w.r.t temperature in the layer above and the layer below ***** - ! ------------------------------------------------------------------------------------------------------------------------- - - ! initialize un-used elements - dFlux_dTempBelow(nLayers) = -huge(lowerBoundTemp) ! don't expect this to be used, so deliberately set to a ridiculous value to cause problems - - ! ***** the upper boundary - dFlux_dTempBelow(0) = dGroundNetFlux_dGroundTemp - - ! loop through INTERFACES... - do iLayer=ixTop,ixBot - - ! ***** the lower boundary - if(iLayer==nLayers)then ! (lower boundary) - - ! identify the lower boundary condition - select case(ix_bcLowrTdyn) - - ! * prescribed temperature at the lower boundary - case(prescribedTemp) - - dz = mLayerDepth(iLayer)*0.5_rkind - if(ixDerivMethod==analytical)then ! ** analytical derivatives - dFlux_dTempAbove(iLayer) = iLayerThermalC(iLayer)/dz - else ! ** numerical derivatives - flux0 = -iLayerThermalC(iLayer)*(lowerBoundTemp - (mLayerTempTrial(iLayer) ))/dz - flux1 = -iLayerThermalC(iLayer)*(lowerBoundTemp - (mLayerTempTrial(iLayer)+dx))/dz - dFlux_dTempAbove(iLayer) = (flux1 - flux0)/dx - end if - - ! * zero flux at the lower boundary - case(zeroFlux) - dFlux_dTempAbove(iLayer) = 0._rkind - - case default; err=20; message=trim(message)//'unable to identify lower boundary condition for thermodynamics'; return - - end select ! (identifying the lower boundary condition for thermodynamics) - - ! ***** internal layers - else - dz = (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) - if(ixDerivMethod==analytical)then ! ** analytical derivatives - dFlux_dTempAbove(iLayer) = iLayerThermalC(iLayer)/dz - dFlux_dTempBelow(iLayer) = -iLayerThermalC(iLayer)/dz - else ! ** numerical derivatives - flux0 = -iLayerThermalC(iLayer)*( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz - flux1 = -iLayerThermalC(iLayer)*( mLayerTempTrial(iLayer+1) - (mLayerTempTrial(iLayer)+dx)) / dz - flux2 = -iLayerThermalC(iLayer)*((mLayerTempTrial(iLayer+1)+dx) - mLayerTempTrial(iLayer) ) / dz - dFlux_dTempAbove(iLayer) = (flux1 - flux0)/dx - dFlux_dTempBelow(iLayer) = (flux2 - flux0)/dx - end if - - end if ! type of layer (upper, internal, or lower) - - end do ! (looping through layers) - - ! end association of local variables with information in the data structures - end associate - - end subroutine ssdNrgFlux - - - - ! ************************************************************************************************ - ! public subroutine ssdNrgFluxSundials: compute energy fluxes and derivatives at layer interfaces - ! ************************************************************************************************ - subroutine ssdNrgFluxSundials(& ! input: model control scalarSolution, & ! intent(in): flag to indicate the scalar solution deriv_desired, & ! intent(in): flag indicating if derivatives are desired @@ -481,7 +271,7 @@ subroutine ssdNrgFluxSundials(& ) ! association of local variables with information in the data structures ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! initialize error control - err=0; message='ssdNrgFluxSundials/' + err=0; message='ssdNrgFlux/' ! initialize the soil layer iSoil=integerMissing @@ -504,7 +294,7 @@ subroutine ssdNrgFluxSundials(& ixTop = ixLayerDesired(1) ixBot = ixLayerDesired(1) else - ixTop = 1 !This needs to be 0 eventually! + ixTop = 1 ixBot = nLayers endif @@ -564,7 +354,7 @@ subroutine ssdNrgFluxSundials(& ! ***** compute the derivative in fluxes at layer interfaces w.r.t temperature in the layer above and the layer below ***** ! ------------------------------------------------------------------------------------------------------------------------- - ! get the indices for the snow+soil layers + ! get the indices for the snow+soil layers, include layer 0 now if(.not.scalarSolution) ixTop = 0 ! initialize un-used elements @@ -612,11 +402,11 @@ subroutine ssdNrgFluxSundials(& ! ======================================================== mLayer_ind(1) = iLayer mLayer_ind(2) = iLayer+1 - if (iLayer==0 ) mLayer_ind(1) = 1 !not doing ground here, will not use + if (iLayer==0 ) mLayer_ind(1) = 1 if (iLayer==nLayers ) mLayer_ind(2) = nLayers ! indices of interface are different at top layer since interface 0 exists iLayer_ind = mLayer_ind - if (iLayer==0 ) iLayer_ind(1) = 0 !not doing ground here, will not use + if (iLayer==0 ) iLayer_ind(1) = 0 ! soil parameters if layer is soil do i = 1,2 if (mLayer_ind(i)>nSnow) then @@ -724,6 +514,7 @@ subroutine ssdNrgFluxSundials(& ! ***** the upper boundary if(iLayer==0)then ! (upper boundary) + ! identify the upper boundary condition select case(ix_bcUpprTdyn) ! * prescribed temperature at the upper boundary @@ -740,23 +531,22 @@ subroutine ssdNrgFluxSundials(& flux2 = -scalarThermCFlux_dTempBelow*((mLayerTempTrial(1)+dx) - upperBoundTemp ) / dz dFlux_dTempBelow(iLayer) = (flux2 - flux0)/dx end if - dGroundNetFlux_dGroundTemp = dFlux_dTempBelow(iLayer) ! * zero flux at the upper boundary case(zeroFlux) - dFlux_dWatAbove(iLayer) = 0._rkind - dFlux_dTempAbove(iLayer) = 0._rkind - dGroundNetFlux_dGroundTemp = dFlux_dTempBelow(iLayer) + dFlux_dWatBelow(iLayer) = 0._rkind + dFlux_dTempBelow(iLayer) = 0._rkind ! * compute flux inside vegetation energy flux routine, use here case(energyFlux) - dFlux_dTempBelow(iLayer) = dGroundNetFlux_dGroundTemp !FIX might need to do the same for dGroundNetFlux_dGroundWat + dFlux_dWatBelow(iLayer) = 0._rkind !dGroundNetFlux_dGroundWat, does not exist in vegNrgFlux + dFlux_dTempBelow(iLayer) = dGroundNetFlux_dGroundTemp case default; err=20; message=trim(message)//'unable to identify upper boundary condition for thermodynamics'; return - dGroundNetFlux_dGroundTemp = dFlux_dTempBelow(iLayer) - !dGroundNetFlux_dGroundWat = dFlux_dWatBelow(iLayer) end select ! (identifying the upper boundary condition for thermodynamics) + !dGroundNetFlux_dGroundWat = dFlux_dWatBelow(iLayer) ! this is true, but since not used in vegNrgFlux do not define + dGroundNetFlux_dGroundTemp = dFlux_dTempBelow(iLayer) ! need this in vegNrgFlux ! ***** the lower boundary else if(iLayer==nLayers)then ! (lower boundary) @@ -817,7 +607,7 @@ subroutine ssdNrgFluxSundials(& ! end association of local variables with information in the data structures end associate - end subroutine ssdNrgFluxSundials + end subroutine ssdNrgFlux ! ************************************************************************************************************************************* @@ -871,6 +661,7 @@ subroutine iLayerThermalConduct(& USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water USE soil_utils_module,only:dTheta_dPsi ! derivative in the soil water characteristic (soil) + USE soil_utils_module,only:dPsi_dTheta ! derivative in the soil water characteristic (soil) USE soil_utils_module,only:dTheta_dTk ! differentiate the freezing curve w.r.t. temperature (soil) USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content ! constants @@ -988,25 +779,23 @@ subroutine iLayerThermalConduct(& ! ******************************************************************************************************* select case(layerType(iLayer)) case(iname_soil) - Tcrit = crit_soilT(nodeMatricHeadTrial(iLayer) ) - ! is ixRichards form right? FIX select case(ixRichards) ! (form of Richards' equation) - case(moisture) + case(moisture) ! nodeVolFracLiqTrial(iLayer) = nodeVolFracLiqTrial0(iLayer) - if( nodeTempTrial(iLayer) < Tcrit) then !if do not perturb temperature, this should not change, but nodeMatricHeadTrial will have changed + nodeMatricHeadTrial(iLayer) = matricHead(nodeVolFracLiqTrial0(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) + Tcrit = crit_soilT(nodeMatricHeadTrial(iLayer)) + !if change temp and below critical, it changes the state variable, seems like a problem FIX + if( nodeTempTrial(iLayer) < Tcrit) then !if do not perturb temperature, this should not change + err=20; message=trim(message)//'temperature derivatives of moisture-based form of Richards eqn have problems'; return matricFHead = (LH_fus/gravity)*(nodeTempTrial(iLayer) - Tfreeze)/Tfreeze - ! if you change the temp below the critical temp, then liquid changes, shouldn't happen? - nodeVolFracLiqTrial(iLayer) = volFracLiq(matricFHead,theta_res(iLayer),theta_sat(iLayer),vGn_alpha(iLayer),vGn_n(iLayer),vGn_m(iLayer)) - ! if you change the liquid amounts and are below the critical temp, then temp changes, howww, shouldn't happen - nodeMatricHeadTrial(iLayer) = matricHead(nodeVolFracLiqTrial(iLayer),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - else - nodeMatricHeadTrial(iLayer) = matricHead(nodeVolFracLiqTrial0(iLayer),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + nodeVolFracLiqTrial(iLayer) = volFracLiq(matricFHead,vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) endif case(mixdform) nodeMatricHeadTrial(iLayer) = nodeMatricHeadTrial0(iLayer) + Tcrit = crit_soilT(nodeMatricHeadTrial(iLayer)) if( nodeTempTrial(iLayer) < Tcrit) then !if do not perturb temperature, this should not change, but nodeMatricHeadTrial will have changed matricFHead = (LH_fus/gravity)*(nodeTempTrial(iLayer) - Tfreeze)/Tfreeze - nodeVolFracLiqTrial(iLayer) = volFracLiq(matricFHead,theta_res(iLayer),theta_sat(iLayer),vGn_alpha(iLayer),vGn_n(iLayer),vGn_m(iLayer)) + nodeVolFracLiqTrial(iLayer) = volFracLiq(matricFHead,vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) else nodeVolFracLiqTrial(iLayer) = volFracLiq(nodeMatricHeadTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) endif @@ -1034,15 +823,15 @@ subroutine iLayerThermalConduct(& case(iname_soil) select case(ixRichards) ! (form of Richards' equation) case(moisture) - mLayerdVolFracLiq_dWat = 1.rkind - mLayerdVolFracIce_dWat = dPsi_dTheta(nodeVolFracLiqTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) - 1.rkind + mLayerdVolFracLiq_dWat = 1._rkind + mLayerdVolFracIce_dWat = dPsi_dTheta(nodeVolFracLiqTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) - 1._rkind case(mixdform) if(nodeTempTrial(iLayer) < Tcrit) then - mLayerdVolFracLiq_dWat = 0.0 + mLayerdVolFracLiq_dWat = 0._rkind mLayerdVolFracIce_dWat = dTheta_dPsi(nodeMatricHeadTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) else mLayerdVolFracLiq_dWat = dTheta_dPsi(nodeMatricHeadTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) - mLayerdVolFracIce_dWat = 0.rkind + mLayerdVolFracIce_dWat = 0._rkind endif end select if(nodeTempTrial(iLayer) < Tcrit) mLayerdVolFracLiq_dTk = dTheta_dTk(nodeTempTrial(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_alpha(iLayer),vGn_n(iLayer),vGn_m(iLayer)) @@ -1083,9 +872,8 @@ subroutine iLayerThermalConduct(& lambda_ice * nodeVolFracIceTrial(iLayer) + & ! ice component lambda_water * nodeVolFracLiqTrial(iLayer) + & ! liquid water component lambda_air * nodeVolFracAirTrial(iLayer) ! air component - ! compute derivatives - mLayerdThermalC_dWat(iLayer) = lambda_ice*mLayerdVolFracLiq_dIce + lambda_water*mLayerdVolFracLiq_dWat + lambda_air*(mLayerdVolFracIce_dWat + mLayerdVolFracLiq_dWat) + mLayerdThermalC_dWat(iLayer) = lambda_ice*mLayerdVolFracIce_dWat + lambda_water*mLayerdVolFracLiq_dWat + lambda_air*(-mLayerdVolFracIce_dWat - mLayerdVolFracLiq_dWat) mLayerdThermalC_dNrg(iLayer) = (lambda_ice - lambda_water) * mLayerdVolFracIce_dTk ! ** test case for the mizoguchi lab experiment, Hansson et al. VZJ 2004 @@ -1125,7 +913,7 @@ subroutine iLayerThermalConduct(& select case(ixThCondSnow) case(Yen1965) - mLayerdThermalC_dWat(iLayer) = 2._rkind * 3.217d-6 * nodeVolFracIceTrial(iLayer) * iden_ice * mLayerdVolFracIce_dTWat + mLayerdThermalC_dWat(iLayer) = 2._rkind * 3.217d-6 * nodeVolFracIceTrial(iLayer) * iden_ice * mLayerdVolFracIce_dWat mLayerdThermalC_dNrg(iLayer) = 2._rkind * 3.217d-6 * nodeVolFracIceTrial(iLayer) * iden_ice * mLayerdVolFracIce_dTk case(Mellor1977) mLayerdThermalC_dWat(iLayer) = 2._rkind * 2.576d-6 * nodeVolFracIceTrial(iLayer) * iden_ice * mLayerdVolFracIce_dWat diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 6f3e99c30..2dd14905b 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -129,7 +129,7 @@ subroutine systemSolvSundials(& ! structure allocations USE allocspace_module,only:allocLocal ! allocate local data structures ! simulation of fluxes and residuals given a trial state vector - USE eval8summa_module,only:eval8summaSundials ! simulation of fluxes and residuals given a trial state vector + USE eval8summa_module,only:eval8summa ! simulation of fluxes and residuals given a trial state vector USE eval8DAE_module,only:eval8DAE USE summaSolve_module,only:summaSolve ! calculate the iteration increment, evaluate the new state, and refine if necessary USE getVectorz_module,only:getScaling ! get the scaling vectors @@ -360,7 +360,7 @@ subroutine systemSolvSundials(& ! NOTE 1: The derivatives computed in eval8summa are used to calculate the Jacobian matrix for the first iteration ! NOTE 2: The Jacobian matrix together with the residual vector is used to calculate the first iteration increment - call eval8summaSundials(& + call eval8summa(& ! input: model control dt, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers diff --git a/build/source/engine/updateVars4JacDAE.f90 b/build/source/engine/updateVars4JacDAE.f90 index cc18b17a8..219e1f33c 100644 --- a/build/source/engine/updateVars4JacDAE.f90 +++ b/build/source/engine/updateVars4JacDAE.f90 @@ -399,7 +399,7 @@ subroutine updateVars4JacDAE(& case(iname_liqLayer, iname_watLayer) mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - mLayerMatricHeadPrime(ixControlIndex) = dPsi_dTheta(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) * mLayerVolFracWatPrime(iLayer) + mLayerMatricHeadPrime(ixControlIndex) = dPsi_dTheta(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) * mLayerVolFracWatPrime(iLayer) case default; err=20; message=trim(message)//'expect iname_lmpLayer, iname_matLayer, iname_liqLayer, or iname_watLayer'; return end select endif ! if in the soil domain @@ -447,7 +447,7 @@ subroutine updateVars4JacDAE(& ! compute the derivative in bulk heat capacity w.r.t. total water content or water matric potential (m-1) ! compute the derivative in total water content w.r.t. total water matric potential (m-1) ! NOTE 1: valid for frozen and unfrozen conditions - ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi + ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi, dVolHtCapBulk_dPsi0 may be wrong select case(ixDomainType) case(iname_veg) fLiq = fracLiquid(xTemp,snowfrz_scale) @@ -465,7 +465,6 @@ subroutine updateVars4JacDAE(& d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),& vGn_n(ixControlIndex),vGn_m(ixControlIndex)) end select - ! dVolHtCapBulk_dPsi0 uses the derivative in water retention curve above critical temp w.r.t.state variable dVolTot_dPsi0 if(xTemp< Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) if(xTemp>=Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_water * Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 789f8bcc0..65907474e 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -176,20 +176,20 @@ subroutine vegNrgFlux(& dGroundNetFlux_dGroundTemp, & ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) ! output liquid water flux derivarives (canopy evap) - dCanopyEvaporation_dCanLiq, & ! intent(out): derivative in canopy evaporation w.r.t. canopy liquid water content (s-1) + dCanopyEvaporation_dCanWat, & ! intent(out): derivative in canopy evaporation w.r.t. canopy total water content (s-1) dCanopyEvaporation_dTCanair, & ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) dCanopyEvaporation_dTCanopy, & ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) dCanopyEvaporation_dTGround, & ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) ! output: liquid water flux derivarives (ground evap) - dGroundEvaporation_dCanLiq, & ! intent(out): derivative in ground evaporation w.r.t. canopy liquid water content (s-1) + dGroundEvaporation_dCanWat, & ! intent(out): derivative in ground evaporation w.r.t. canopy total water content (s-1) dGroundEvaporation_dTCanair, & ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) dGroundEvaporation_dTCanopy, & ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) dGroundEvaporation_dTGround, & ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) ! output: cross derivative terms - dCanopyNetFlux_dCanLiq, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dGroundNetFlux_dCanLiq, & ! intent(out): derivative in net ground fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dCanopyNetFlux_dCanWat, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) + dGroundNetFlux_dCanWat, & ! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) ! output: error control err,message) ! intent(out): error control @@ -201,6 +201,9 @@ subroutine vegNrgFlux(& USE conv_funcs_module,only:getLatentHeatValue ! function to identify latent heat of vaporization/sublimation (J kg-1) ! stomatal resistance USE stomResist_module,only:stomResist ! subroutine to calculate stomatal resistance + ! phase changes + USE snow_utils_module,only:fracliquid ! compute fraction of liquid water at a given temperature + ! compute energy and mass fluxes for vegetation implicit none @@ -256,20 +259,20 @@ subroutine vegNrgFlux(& real(rkind),intent(out) :: dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) ! output: liquid flux derivatives (canopy evap) - real(rkind),intent(out) :: dCanopyEvaporation_dCanLiq ! derivative in canopy evaporation w.r.t. canopy liquid water content (s-1) + real(rkind),intent(out) :: dCanopyEvaporation_dCanWat ! derivative in canopy evaporation w.r.t. canopy total water content (s-1) real(rkind),intent(out) :: dCanopyEvaporation_dTCanair ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) real(rkind),intent(out) :: dCanopyEvaporation_dTCanopy ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) real(rkind),intent(out) :: dCanopyEvaporation_dTGround ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) ! output: liquid flux derivatives (ground evap) - real(rkind),intent(out) :: dGroundEvaporation_dCanLiq ! derivative in ground evaporation w.r.t. canopy liquid water content (s-1) + real(rkind),intent(out) :: dGroundEvaporation_dCanWat ! derivative in ground evaporation w.r.t. canopy total water content (s-1) real(rkind),intent(out) :: dGroundEvaporation_dTCanair ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) real(rkind),intent(out) :: dGroundEvaporation_dTCanopy ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) real(rkind),intent(out) :: dGroundEvaporation_dTGround ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) ! output: cross derivative terms - real(rkind),intent(out) :: dCanopyNetFlux_dCanLiq ! derivative in net canopy fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - real(rkind),intent(out) :: dGroundNetFlux_dCanLiq ! derivative in net ground fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + real(rkind),intent(out) :: dCanopyNetFlux_dCanWat ! derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) + real(rkind),intent(out) :: dGroundNetFlux_dCanWat ! derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) ! output: error control integer(i4b),intent(out) :: err ! error code @@ -290,12 +293,15 @@ subroutine vegNrgFlux(& integer(i4b),parameter :: perturbStateGround=2 ! named variable to identify the case where we perturb the ground temperature integer(i4b),parameter :: perturbStateCanopy=3 ! named variable to identify the case where we perturb the canopy temperature integer(i4b),parameter :: perturbStateCanair=4 ! named variable to identify the case where we perturb the canopy air temperature - integer(i4b),parameter :: perturbStateCanLiq=5 ! named variable to identify the case where we perturb the canopy liquid water content + integer(i4b),parameter :: perturbStateCanWat=5 ! named variable to identify the case where we perturb the canopy total water content integer(i4b) :: itry ! index of flux evaluation integer(i4b) :: nFlux ! number of flux evaluations real(rkind) :: groundTemp ! value of ground temperature used in flux calculations (may be perturbed) real(rkind) :: canopyTemp ! value of canopy temperature used in flux calculations (may be perturbed) real(rkind) :: canairTemp ! value of canopy air temperature used in flux calculations (may be perturbed) + real(rkind) :: canopyWat ! value of canopy total water used in flux calculations (may be perturbed) + real(rkind) :: canopyLiq ! value of canopy liquid water used in flux calculations (may be perturbed) + real(rkind) :: canopyIce ! value of canopy ice used in flux calculations (may be perturbed) real(rkind) :: try0,try1 ! trial values to evaluate specific derivatives (testing only) ! local (saturation vapor pressure of veg) @@ -374,51 +380,51 @@ subroutine vegNrgFlux(& real(rkind) :: turbFluxCanair_dStateCanair ! turbulent exchange from the canopy air space to the atmosphere, after canopy air temperature is perturbed (W m-2) real(rkind) :: turbFluxCanair_dStateCanopy ! turbulent exchange from the canopy air space to the atmosphere, after canopy temperature is perturbed (W m-2) real(rkind) :: turbFluxCanair_dStateGround ! turbulent exchange from the canopy air space to the atmosphere, after ground temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanair_dStateCanliq ! turbulent exchange from the canopy air space to the atmosphere, after canopy liquid water content is perturbed (W m-2) + real(rkind) :: turbFluxCanair_dStateCanWat ! turbulent exchange from the canopy air space to the atmosphere, after canopy total water content is perturbed (W m-2) ! (fluxes after perturbations in model states -- vegetation canopy) real(rkind) :: turbFluxCanopy_dStateCanair ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy air temperature is perturbed (W m-2) real(rkind) :: turbFluxCanopy_dStateCanopy ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy temperature is perturbed (W m-2) real(rkind) :: turbFluxCanopy_dStateGround ! total turbulent heat fluxes from the canopy to the canopy air space, after ground temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanopy_dStateCanLiq ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy liquid water content is perturbed (W m-2) + real(rkind) :: turbFluxCanopy_dStateCanWat ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy total water content is perturbed (W m-2) ! (fluxes after perturbations in model states -- ground surface) real(rkind) :: turbFluxGround_dStateCanair ! total turbulent heat fluxes from the ground to the canopy air space, after canopy air temperature is perturbed (W m-2) real(rkind) :: turbFluxGround_dStateCanopy ! total turbulent heat fluxes from the ground to the canopy air space, after canopy temperature is perturbed (W m-2) real(rkind) :: turbFluxGround_dStateGround ! total turbulent heat fluxes from the ground to the canopy air space, after ground temperature is perturbed (W m-2) - real(rkind) :: turbFluxGround_dStateCanLiq ! total turbulent heat fluxes from the ground to the canopy air space, after canopy liquid water content is perturbed (W m-2) + real(rkind) :: turbFluxGround_dStateCanWat ! total turbulent heat fluxes from the ground to the canopy air space, after canopy total water content is perturbed (W m-2) ! (fluxes after perturbations in model states -- canopy evaporation) real(rkind) :: latHeatCanEvap_dStateCanair ! canopy evaporation after canopy air temperature is perturbed (W m-2) real(rkind) :: latHeatCanEvap_dStateCanopy ! canopy evaporation after canopy temperature is perturbed (W m-2) real(rkind) :: latHeatCanEvap_dStateGround ! canopy evaporation after ground temperature is perturbed (W m-2) - real(rkind) :: latHeatCanEvap_dStateCanLiq ! canopy evaporation after canopy liquid water content is perturbed (W m-2) + real(rkind) :: latHeatCanEvap_dStateCanWat ! canopy evaporation after canopy total water content is perturbed (W m-2) ! (flux derivatives -- canopy air space) real(rkind) :: dTurbFluxCanair_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) real(rkind) :: dTurbFluxCanair_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) real(rkind) :: dTurbFluxCanair_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanair_dCanLiq ! derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + real(rkind) :: dTurbFluxCanair_dCanWat ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) ! (flux derivatives -- vegetation canopy) real(rkind) :: dTurbFluxCanopy_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) real(rkind) :: dTurbFluxCanopy_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) real(rkind) :: dTurbFluxCanopy_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanopy_dCanLiq ! derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + real(rkind) :: dTurbFluxCanopy_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) ! (flux derivatives -- ground surface) real(rkind) :: dTurbFluxGround_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) real(rkind) :: dTurbFluxGround_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) real(rkind) :: dTurbFluxGround_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dTurbFluxGround_dCanLiq ! derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + real(rkind) :: dTurbFluxGround_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) ! (liquid water flux derivatives -- canopy evap) - real(rkind) :: dLatHeatCanopyEvap_dCanLiq ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (W kg-1) + real(rkind) :: dLatHeatCanopyEvap_dCanWat ! derivative in latent heat of canopy evaporation w.r.t. canopy total water content (W kg-1) real(rkind) :: dLatHeatCanopyEvap_dTCanair ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) real(rkind) :: dLatHeatCanopyEvap_dTCanopy ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) real(rkind) :: dLatHeatCanopyEvap_dTGround ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) ! (liquid water flux derivatives -- ground evap) - real(rkind) :: dLatHeatGroundEvap_dCanLiq ! derivative in latent heat of ground evaporation w.r.t. canopy liquid water content (J kg-1 s-1) + real(rkind) :: dLatHeatGroundEvap_dCanWat ! derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) real(rkind) :: dLatHeatGroundEvap_dTCanair ! derivative in latent heat of ground evaporation w.r.t. canopy air temperature (W m-2 K-1) real(rkind) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) real(rkind) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) @@ -488,6 +494,9 @@ subroutine vegNrgFlux(& critAquiferTranspire => mpar_data%var(iLookPARAM%critAquiferTranspire)%dat(1), & ! intent(in): [dp] critical aquifer storage value when transpiration is limited (m) minStomatalResistance => mpar_data%var(iLookPARAM%minStomatalResistance)%dat(1), & ! intent(in): [dp] mimimum stomatal resistance (s m-1) + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ! input: forcing at the upper boundary mHeight => diag_data%var(iLookDIAG%scalarAdjMeasHeight)%dat(1), & ! intent(in): [dp] measurement height (m) airtemp => forc_data%var(iLookFORCE%airtemp), & ! intent(in): [dp] air temperature at some height above the surface (K) @@ -646,25 +655,25 @@ subroutine vegNrgFlux(& dGroundNetFlux_dCanairTemp = 0._rkind ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) dGroundNetFlux_dCanopyTemp = 0._rkind ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) ! set liquid flux derivatives to zero (canopy evap) - dCanopyEvaporation_dCanLiq = 0._rkind ! derivative in canopy evaporation w.r.t. canopy liquid water content (s-1) + dCanopyEvaporation_dCanWat = 0._rkind ! derivative in canopy evaporation w.r.t. canopy total water content (s-1) dCanopyEvaporation_dTCanair= 0._rkind ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) dCanopyEvaporation_dTCanopy= 0._rkind ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) dCanopyEvaporation_dTGround= 0._rkind ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) ! set liquid flux derivatives to zero (ground evap) - dGroundEvaporation_dCanLiq = 0._rkind ! derivative in ground evaporation w.r.t. canopy liquid water content (s-1) + dGroundEvaporation_dCanWat = 0._rkind ! derivative in ground evaporation w.r.t. canopy total water content (s-1) dGroundEvaporation_dTCanair= 0._rkind ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) dGroundEvaporation_dTCanopy= 0._rkind ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) dGroundEvaporation_dTGround= 0._rkind ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - ! compute fluxes and derivatives -- separate approach for prescribed temperature and zero flux + ! compute fluxes and derivatives -- separate approach for prescribed temperature and zero flux, if(ix_bcUpprTdyn == prescribedTemp)then ! compute ground net flux (W m-2) groundNetFlux = -diag_data%var(iLookDIAG%iLayerThermalC)%dat(0)*(groundTempTrial - upperBoundTemp)/(prog_data%var(iLookPROG%mLayerDepth)%dat(1)*0.5_rkind) - ! compute derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - dGroundNetFlux_dGroundTemp = -diag_data%var(iLookDIAG%iLayerThermalC)%dat(0)/(prog_data%var(iLookPROG%mLayerDepth)%dat(1)*0.5_rkind) - elseif(model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision == zeroFlux)then + ! compute derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) inside soil and snow (ssd) energy flux routine + ! dGroundNetFlux_dGroundTemp = missingValue + elseif(ix_bcUpprTdyn == zeroFlux)then groundNetFlux = 0._rkind - dGroundNetFlux_dGroundTemp = 0._rkind + ! dGroundNetFlux_dGroundTemp = missingValue else err=20; message=trim(message)//'unable to identify upper boundary condition for thermodynamics: expect the case to be prescribedTemp or zeroFlux'; return end if @@ -791,7 +800,7 @@ subroutine vegNrgFlux(& else scalarCanopyWetFraction = 0._rkind ! canopy wetted fraction (-) - dCanopyWetFraction_dWat = 0._rkind ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) + dCanopyWetFraction_dWat = 0._rkind ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) dCanopyWetFraction_dT = 0._rkind ! derivative in wetted fraction w.r.t. canopy temperature (K-1) end if !write(*,'(a,1x,L1,1x,f25.15,1x))') 'computeVegFlux, scalarCanopyWetFraction = ', computeVegFlux, scalarCanopyWetFraction @@ -1016,68 +1025,78 @@ subroutine vegNrgFlux(& groundTemp = groundTempTrial canopyTemp = canopyTempTrial canairTemp = canairTempTrial - canopyWetFraction = scalarCanopyWetFraction + canopyWat = totalCanopyWater ! perturb ground temperature case(perturbStateGround) groundTemp = groundTempTrial + dx canopyTemp = canopyTempTrial canairTemp = canairTempTrial - canopyWetFraction = scalarCanopyWetFraction + canopyWat = totalCanopyWater ! perturb canopy temperature case(perturbStateCanopy) groundTemp = groundTempTrial canopyTemp = canopyTempTrial + dx canairTemp = canairTempTrial - canopyWetFraction = scalarCanopyWetFraction + canopyWat = totalCanopyWater ! perturb canopy air temperature case(perturbStateCanair) groundTemp = groundTempTrial canopyTemp = canopyTempTrial canairTemp = canairTempTrial + dx - canopyWetFraction = scalarCanopyWetFraction + canopyWat = totalCanopyWater - ! perturb canopy liquid water content - case(perturbStateCanLiq) + ! perturb canopy total water content + case(perturbStateCanWat) groundTemp = groundTempTrial canopyTemp = canopyTempTrial canairTemp = canairTempTrial - - ! perturbations in canopy liquid water content affect canopy wetted fraction - if(computeVegFlux)then - call wettedFrac(& - ! input - .false., & ! flag to denote if derivative is desired - (ixDerivMethod == numerical), & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) - (scalarLatHeatSubVapCanopy > LH_vap+verySmall), & ! flag to denote if the canopy is frozen - dCanLiq_dTcanopy, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) - fracLiquidCanopy, & ! fraction of liquid water on the canopy (-) - canopyLiqTrial+dx, & ! canopy liquid water (kg m-2) - canopyIceTrial, & ! canopy ice (kg m-2) - scalarCanopyLiqMax, & ! maximum canopy liquid water (kg m-2) - scalarCanopyIceMax, & ! maximum canopy ice content (kg m-2) - canopyWettingFactor, & ! maximum wetted fraction of the canopy (-) - canopyWettingExp, & ! exponent in canopy wetting function (-) - ! output - canopyWetFraction, & ! canopy wetted fraction (-) - dCanopyWetFraction_dWat, & ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) - dCanopyWetFraction_dT, & ! derivative in wetted fraction w.r.t. canopy temperature (K-1) - err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - else - canopyWetFraction = 0._rkind - end if - !print*, 'wetted fraction derivative = ', (canopyWetFraction - scalarCanopyWetFraction)/dx - !pause + canopyWat = totalCanopyWater + dx ! check for an unknown perturbation case default; err=10; message=trim(message)//"unknown perturbation"; return end select ! (type of perturbation) + ! recalculate liquid and ice from total water + canopyWetFraction = scalarCanopyWetFraction + fracLiquidCanopy = fracliquid(canopyTemp,snowfrz_scale) + canopyLiq = fracLiquidCanopy*canopyWat + canopyIce = (1._rkind - fracLiquidCanopy)*canopyWat + + ! perturbations in canopy total water content affect canopy wetted fraction + if(itry == perturbStateCanopy .OR. itry == perturbStateCanWat)then + if(computeVegFlux)then + call wettedFrac(& + ! input + .false., & ! flag to denote if derivative is desired + (ixDerivMethod == numerical), & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) + (scalarLatHeatSubVapCanopy > LH_vap+verySmall), & ! flag to denote if the canopy is frozen + dCanLiq_dTcanopy, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) + fracLiquidCanopy, & ! fraction of liquid water on the canopy (-) + canopyLiq, & ! canopy liquid water (kg m-2) + canopyIce, & ! canopy ice (kg m-2) + scalarCanopyLiqMax, & ! maximum canopy liquid water (kg m-2) + scalarCanopyIceMax, & ! maximum canopy ice content (kg m-2) + canopyWettingFactor, & ! maximum wetted fraction of the canopy (-) + canopyWettingExp, & ! exponent in canopy wetting function (-) + ! output + canopyWetFraction, & ! canopy wetted fraction (-) + dCanopyWetFraction_dWat, & ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) + dCanopyWetFraction_dT, & ! derivative in wetted fraction w.r.t. canopy temperature (K-1) + err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + else + canopyWetFraction = 0._rkind + end if ! (desired computing vegetation flux) + + end if ! (re-computing wetted fraction for perturbed cases) + !print*, 'wetted fraction derivative = ', (canopyWetFraction - scalarCanopyWetFraction)/dx + !pause + ! compute the saturation vapor pressure for vegetation temperature ! NOTE: saturated vapor pressure derivatives don't seem that accurate.... TV_celcius = canopyTemp - Tfreeze @@ -1256,19 +1275,19 @@ subroutine vegNrgFlux(& dTurbFluxGround_dTCanopy, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) dTurbFluxGround_dTGround, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) ! output: liquid flux derivatives (canopy evap) - dLatHeatCanopyEvap_dCanLiq, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (W kg-1) + dLatHeatCanopyEvap_dCanWat, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy total water content (W kg-1) dLatHeatCanopyEvap_dTCanair, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) dLatHeatCanopyEvap_dTCanopy, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) dLatHeatCanopyEvap_dTGround, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) ! output: liquid flux derivatives (ground evap) - dLatHeatGroundEvap_dCanLiq, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy liquid water content (J kg-1 s-1) + dLatHeatGroundEvap_dCanWat, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) dLatHeatGroundEvap_dTCanair, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy air temperature dLatHeatGroundEvap_dTCanopy, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy temperature dLatHeatGroundEvap_dTGround, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. ground temperature ! output: cross derivatives - dTurbFluxCanair_dCanLiq, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dTurbFluxCanopy_dCanLiq, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dTurbFluxGround_dCanLiq, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dTurbFluxCanair_dCanWat, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxCanopy_dCanWat, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxGround_dCanWat, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) ! output: error control err,cmessage ) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if @@ -1313,11 +1332,11 @@ subroutine vegNrgFlux(& turbFluxCanopy_dStateGround = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) turbFluxGround_dStateGround = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) latHeatCanEvap_dStateGround = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) - case(perturbStateCanLiq) - turbFluxCanair_dStateCanliq = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) - turbFluxCanopy_dStateCanLiq = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) - turbFluxGround_dStateCanLiq = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) - latHeatCanEvap_dStateCanliq = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) + case(perturbStateCanWat) + turbFluxCanair_dStateCanWat = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) + turbFluxCanopy_dStateCanWat = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) + turbFluxGround_dStateCanWat = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) + latHeatCanEvap_dStateCanWat = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) case default; err=10; message=trim(message)//"unknown perturbation"; return end select ! (type of perturbation) end if ! (if numerical) @@ -1347,11 +1366,11 @@ subroutine vegNrgFlux(& dTurbFluxCanopy_dTGround = (turbFluxCanopy_dStateGround - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) dTurbFluxGround_dTGround = (turbFluxGround_dStateGround - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) dLatHeatCanopyEvap_dTGround = (latHeatCanEvap_dStateGround - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) - ! derivatives w.r.t. canopy liquid water content - dTurbFluxCanair_dCanLiq = (turbFluxCanair_dStateCanliq - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dTurbFluxCanopy_dCanLiq = (turbFluxCanopy_dStateCanLiq - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dTurbFluxGround_dCanLiq = (turbFluxGround_dStateCanLiq - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dLatHeatCanopyEvap_dCanLiq = (latHeatCanEvap_dStateCanliq - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (J kg-1 s-1) + ! derivatives w.r.t. canopy total water content + dTurbFluxCanair_dCanWat = (turbFluxCanair_dStateCanWat - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxCanopy_dCanWat = (turbFluxCanopy_dStateCanWat - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxGround_dCanWat = (turbFluxGround_dStateCanWat - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + dLatHeatCanopyEvap_dCanWat = (latHeatCanEvap_dStateCanWat - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy total water content (J kg-1 s-1) end if !if(heightCanopyBottom < scalarSnowDepth+z0Ground) pause 'bottom of the canopy is covered' @@ -1366,13 +1385,13 @@ subroutine vegNrgFlux(& !print*, 'dTurbFluxGround_dTCanair = ', dTurbFluxGround_dTCanair !print*, 'dTurbFluxGround_dTCanopy = ', dTurbFluxGround_dTCanopy !print*, 'dTurbFluxGround_dTGround = ', dTurbFluxGround_dTGround - !print*, 'dLatHeatCanopyEvap_dCanLiq = ', dLatHeatCanopyEvap_dCanLiq + !print*, 'dLatHeatCanopyEvap_dCanWat = ', dLatHeatCanopyEvap_dCanWat !print*, 'dLatHeatCanopyEvap_dTCanair = ', dLatHeatCanopyEvap_dTCanair !print*, 'dLatHeatCanopyEvap_dTCanopy = ', dLatHeatCanopyEvap_dTCanopy !print*, 'dLatHeatCanopyEvap_dTGround = ', dLatHeatCanopyEvap_dTGround - !print*, 'dTurbFluxCanair_dCanLiq = ', dTurbFluxCanair_dCanLiq - !print*, 'dTurbFluxCanopy_dCanLiq = ', dTurbFluxCanopy_dCanLiq - !print*, 'dTurbFluxGround_dCanLiq = ', dTurbFluxGround_dCanLiq + !print*, 'dTurbFluxCanair_dCanWat = ', dTurbFluxCanair_dCanWat + !print*, 'dTurbFluxCanopy_dCanWat = ', dTurbFluxCanopy_dCanWat + !print*, 'dTurbFluxGround_dCanWat = ', dTurbFluxGround_dCanWat !print*, '*****' !pause @@ -1465,28 +1484,28 @@ subroutine vegNrgFlux(& if(scalarLatHeatSubVapCanopy < LH_vap+verySmall)then ! evaporation ! compute the liquid water derivarives - dCanopyEvaporation_dCanLiq = dLatHeatCanopyEvap_dCanLiq/LH_vap ! (s-1) + dCanopyEvaporation_dCanWat = dLatHeatCanopyEvap_dCanWat/LH_vap ! (s-1) dCanopyEvaporation_dTCanair = dLatHeatCanopyEvap_dTCanair/LH_vap ! (kg m-2 s-1 K-1) dCanopyEvaporation_dTCanopy = dLatHeatCanopyEvap_dTCanopy/LH_vap ! (kg m-2 s-1 K-1) dCanopyEvaporation_dTGround = dLatHeatCanopyEvap_dTGround/LH_vap ! (kg m-2 s-1 K-1) ! sublimation else - dCanopyEvaporation_dCanLiq = 0._rkind ! (s-1) + dCanopyEvaporation_dCanWat = 0._rkind ! (s-1) dCanopyEvaporation_dTCanair = 0._rkind ! (kg m-2 s-1 K-1) dCanopyEvaporation_dTCanopy = 0._rkind ! (kg m-2 s-1 K-1) dCanopyEvaporation_dTGround = 0._rkind ! (kg m-2 s-1 K-1) end if ! compute the liquid water derivarives (ground evap) - dGroundEvaporation_dCanLiq = dLatHeatGroundEvap_dCanLiq/LH_vap ! (s-1) + dGroundEvaporation_dCanWat = dLatHeatGroundEvap_dCanWat/LH_vap ! (s-1) dGroundEvaporation_dTCanair = dLatHeatGroundEvap_dTCanair/LH_vap ! (kg m-2 s-1 K-1) dGroundEvaporation_dTCanopy = dLatHeatGroundEvap_dTCanopy/LH_vap ! (kg m-2 s-1 K-1) dGroundEvaporation_dTGround = dLatHeatGroundEvap_dTGround/LH_vap ! (kg m-2 s-1 K-1) ! compute the cross derivative terms (only related to turbulent fluxes; specifically canopy evaporation and transpiration) - dCanopyNetFlux_dCanLiq = dTurbFluxCanopy_dCanLiq ! derivative in net canopy fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dGroundNetFlux_dCanLiq = dTurbFluxGround_dCanLiq ! derivative in net ground fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dCanopyNetFlux_dCanWat = dTurbFluxCanopy_dCanWat ! derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) + dGroundNetFlux_dCanWat = dTurbFluxGround_dCanWat ! derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) !print*, (ixDerivMethod == numerical) !print*, 'dGroundNetFlux_dCanairTemp = ', dGroundNetFlux_dCanairTemp @@ -2606,19 +2625,19 @@ subroutine turbFluxes(& dTurbFluxGround_dTCanopy, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) dTurbFluxGround_dTGround, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) ! output: liquid flux derivatives (canopy evap) - dLatHeatCanopyEvap_dCanLiq, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (J kg-1 s-1) + dLatHeatCanopyEvap_dCanWat, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy total water content (J kg-1 s-1) dLatHeatCanopyEvap_dTCanair, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) dLatHeatCanopyEvap_dTCanopy, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) dLatHeatCanopyEvap_dTGround, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) ! output: liquid flux derivatives (ground evap) - dLatHeatGroundEvap_dCanLiq, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy liquid water content (J kg-1 s-1) + dLatHeatGroundEvap_dCanWat, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) dLatHeatGroundEvap_dTCanair, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy air temperature dLatHeatGroundEvap_dTCanopy, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy temperature dLatHeatGroundEvap_dTGround, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. ground temperature ! output: cross derivatives - dTurbFluxCanair_dCanLiq, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dTurbFluxCanopy_dCanLiq, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dTurbFluxGround_dCanLiq, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dTurbFluxCanair_dCanWat, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxCanopy_dCanWat, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxGround_dCanWat, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) ! output: error control err,message ) ! intent(out): error control ! ----------------------------------------------------------------------------------------------------------------------------------------- @@ -2698,19 +2717,19 @@ subroutine turbFluxes(& real(rkind),intent(out) :: dTurbFluxGround_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) real(rkind),intent(out) :: dTurbFluxGround_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) ! output: liquid flux derivatives (canopy evap) - real(rkind),intent(out) :: dLatHeatCanopyEvap_dCanLiq ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water content (W kg-1) + real(rkind),intent(out) :: dLatHeatCanopyEvap_dCanWat ! derivative in latent heat of canopy evaporation w.r.t. canopy total water content (W kg-1) real(rkind),intent(out) :: dLatHeatCanopyEvap_dTCanair ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) real(rkind),intent(out) :: dLatHeatCanopyEvap_dTCanopy ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) real(rkind),intent(out) :: dLatHeatCanopyEvap_dTGround ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) ! output: liquid flux derivatives (ground evap) - real(rkind),intent(out) :: dLatHeatGroundEvap_dCanLiq ! derivative in latent heat of ground evaporation w.r.t. canopy liquid water content (J kg-1 s-1) + real(rkind),intent(out) :: dLatHeatGroundEvap_dCanWat ! derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) real(rkind),intent(out) :: dLatHeatGroundEvap_dTCanair ! derivative in latent heat of ground evaporation w.r.t. canopy air temperature (W m-2 K-1) real(rkind),intent(out) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) real(rkind),intent(out) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) ! output: cross derivatives - real(rkind),intent(out) :: dTurbFluxCanair_dCanLiq ! derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - real(rkind),intent(out) :: dTurbFluxCanopy_dCanLiq ! derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - real(rkind),intent(out) :: dTurbFluxGround_dCanLiq ! derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + real(rkind),intent(out) :: dTurbFluxCanair_dCanWat ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) + real(rkind),intent(out) :: dTurbFluxCanopy_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + real(rkind),intent(out) :: dTurbFluxGround_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -2740,7 +2759,7 @@ subroutine turbFluxes(& real(rkind) :: dVPCanopyAir_dTCanopy ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the canopy real(rkind) :: dVPCanopyAir_dTGround ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the ground real(rkind) :: dVPCanopyAir_dWetFrac ! derivative of vapor pressure in the canopy air space w.r.t. wetted fraction of the canopy - real(rkind) :: dVPCanopyAir_dCanLiq ! derivative of vapor pressure in the canopy air space w.r.t. canopy liquid water content + real(rkind) :: dVPCanopyAir_dCanWat ! derivative of vapor pressure in the canopy air space w.r.t. canopy total water content ! local variables -- sensible heat flux derivatives real(rkind) :: dSenHeatTotal_dTCanair ! derivative in the total sensible heat flux w.r.t. canopy air temperature real(rkind) :: dSenHeatTotal_dTCanopy ! derivative in the total sensible heat flux w.r.t. canopy air temperature @@ -2758,7 +2777,7 @@ subroutine turbFluxes(& ! local variables -- wetted fraction derivatives real(rkind) :: dLatHeatCanopyEvap_dWetFrac ! derivative in the latent heat of canopy evaporation w.r.t. canopy wet fraction (W m-2) real(rkind) :: dLatHeatCanopyTrans_dWetFrac ! derivative in the latent heat of canopy transpiration w.r.t. canopy wet fraction (W m-2) - real(rkind) :: dLatHeatCanopyTrans_dCanLiq ! derivative in the latent heat of canopy transpiration w.r.t. canopy liquid water (J kg-1 s-1) + real(rkind) :: dLatHeatCanopyTrans_dCanWat ! derivative in the latent heat of canopy transpiration w.r.t. canopy total water (J kg-1 s-1) ! ----------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='turbFluxes/' @@ -2929,9 +2948,9 @@ subroutine turbFluxes(& dPart1 = (leafConductance - leafConductanceTr)*satVP_CanopyTemp dPart2 = -(leafConductance - leafConductanceTr)/(totalConductanceLH**2._rkind) dVPCanopyAir_dWetFrac = dPart1/totalConductanceLH + fPart_VP*dPart2 - dVPCanopyAir_dCanLiq = dVPCanopyAir_dWetFrac*dCanopyWetFraction_dWat - !write(*,'(a,5(f20.8,1x))') 'dVPCanopyAir_dTCanair, dVPCanopyAir_dTCanopy, dVPCanopyAir_dTGround, dVPCanopyAir_dWetFrac, dVPCanopyAir_dCanLiq = ', & - ! dVPCanopyAir_dTCanair, dVPCanopyAir_dTCanopy, dVPCanopyAir_dTGround, dVPCanopyAir_dWetFrac, dVPCanopyAir_dCanLiq + dVPCanopyAir_dCanWat = dVPCanopyAir_dWetFrac*dCanopyWetFraction_dWat + !write(*,'(a,5(f20.8,1x))') 'dVPCanopyAir_dTCanair, dVPCanopyAir_dTCanopy, dVPCanopyAir_dTGround, dVPCanopyAir_dWetFrac, dVPCanopyAir_dCanWat = ', & + ! dVPCanopyAir_dTCanair, dVPCanopyAir_dTCanopy, dVPCanopyAir_dTGround, dVPCanopyAir_dWetFrac, dVPCanopyAir_dCanWat ! sensible heat from the canopy to the atmosphere dSenHeatTotal_dTCanair = -volHeatCapacityAir*canopyConductance - volHeatCapacityAir*dCanopyCond_dCanairTemp*(canairTemp - airtemp) @@ -2998,9 +3017,9 @@ subroutine turbFluxes(& dLatHeatCanopyTrans_dWetFrac = dPart1*(satVP_CanopyTemp - VP_CanopyAir) + fPart1*(-dVPCanopyAir_dWetFrac) !print*, 'dLatHeatCanopyTrans_dWetFrac = ', dLatHeatCanopyTrans_dWetFrac - ! latent heat associated with canopy transpiration w.r.t. canopy liquid water - dLatHeatCanopyTrans_dCanLiq = dLatHeatCanopyTrans_dWetFrac*dCanopyWetFraction_dWat ! (J s-1 kg-1) - !print*, 'dLatHeatCanopyTrans_dCanLiq = ', dLatHeatCanopyTrans_dCanLiq + ! latent heat associated with canopy transpiration w.r.t. canopy total water + dLatHeatCanopyTrans_dCanWat = dLatHeatCanopyTrans_dWetFrac*dCanopyWetFraction_dWat ! (J s-1 kg-1) + !print*, 'dLatHeatCanopyTrans_dCanWat = ', dLatHeatCanopyTrans_dCanWat else ! canopy is undefined @@ -3020,9 +3039,9 @@ subroutine turbFluxes(& ! set derivatives for wetted area and canopy transpiration to zero (no canopy, so fluxes are undefined) dLatHeatCanopyEvap_dWetFrac = 0._rkind - dLatHeatCanopyEvap_dCanLiq = 0._rkind - dLatHeatCanopyTrans_dCanLiq = 0._rkind - dVPCanopyAir_dCanLiq = 0._rkind + dLatHeatCanopyEvap_dCanWat = 0._rkind + dLatHeatCanopyTrans_dCanWat = 0._rkind + dVPCanopyAir_dCanWat = 0._rkind ! set derivatives for ground fluxes w.r.t canopy temperature to zero (no canopy, so fluxes are undefined) dSenHeatGround_dTCanair = 0._rkind @@ -3066,12 +3085,12 @@ subroutine turbFluxes(& dTurbFluxGround_dTCanopy = dSenHeatGround_dTCanopy + dLatHeatGroundEvap_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) dTurbFluxGround_dTGround = dSenHeatGround_dTGround + dLatHeatGroundEvap_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) ! (liquid water derivatives) - dLatHeatCanopyEvap_dCanLiq = dLatHeatCanopyEvap_dWetFrac*dCanopyWetFraction_dWat ! derivative in latent heat of canopy evaporation w.r.t. canopy liquid water (W kg-1) - dLatHeatGroundEvap_dCanLiq = latHeatSubVapGround*latentHeatConstant*groundConductanceLH*dVPCanopyAir_dCanLiq ! derivative in latent heat of ground evaporation w.r.t. canopy liquid water (J kg-1 s-1) + dLatHeatCanopyEvap_dCanWat = dLatHeatCanopyEvap_dWetFrac*dCanopyWetFraction_dWat ! derivative in latent heat of canopy evaporation w.r.t. canopy total water (W kg-1) + dLatHeatGroundEvap_dCanWat = latHeatSubVapGround*latentHeatConstant*groundConductanceLH*dVPCanopyAir_dCanWat ! derivative in latent heat of ground evaporation w.r.t. canopy total water (J kg-1 s-1) ! (cross deriavtives) - dTurbFluxCanair_dCanLiq = 0._rkind ! derivative in net canopy air space fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dTurbFluxCanopy_dCanLiq = dLatHeatCanopyEvap_dCanLiq + dLatHeatCanopyTrans_dCanLiq ! derivative in net canopy turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) - dTurbFluxGround_dCanLiq = dLatHeatGroundEvap_dCanLiq ! derivative in net ground turbulent fluxes w.r.t. canopy liquid water content (J kg-1 s-1) + dTurbFluxCanair_dCanWat = 0._rkind ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxCanopy_dCanWat = dLatHeatCanopyEvap_dCanWat + dLatHeatCanopyTrans_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxGround_dCanWat = dLatHeatGroundEvap_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) else ! (just make sure we return something) ! (energy derivatives) dTurbFluxCanair_dTCanair = 0._rkind @@ -3084,12 +3103,12 @@ subroutine turbFluxes(& dTurbFluxGround_dTCanopy = 0._rkind dTurbFluxGround_dTGround = 0._rkind ! (liquid water derivatives) - dLatHeatCanopyEvap_dCanLiq = 0._rkind - dLatHeatGroundEvap_dCanLiq = 0._rkind + dLatHeatCanopyEvap_dCanWat = 0._rkind + dLatHeatGroundEvap_dCanWat = 0._rkind ! (cross deriavtives) - dTurbFluxCanair_dCanLiq = 0._rkind - dTurbFluxCanopy_dCanLiq = 0._rkind - dTurbFluxGround_dCanLiq = 0._rkind + dTurbFluxCanair_dCanWat = 0._rkind + dTurbFluxCanopy_dCanWat = 0._rkind + dTurbFluxGround_dCanWat = 0._rkind end if end subroutine turbFluxes From 7c2c708377ece2c4958ee3b635467bdbd2ead3cb Mon Sep 17 00:00:00 2001 From: ashleymedin <34691332+ashleymedin@users.noreply.github.com> Date: Tue, 8 Mar 2022 23:22:52 +0900 Subject: [PATCH 0247/1472] Added derivatives of soil and aquifer transpiration with w.r.t canopy transpiration, and aquifer recharge w.r.t lowest soil flux --- build/source/dshare/get_ixname.f90 | 15 ++++++++ build/source/dshare/popMetadat.f90 | 19 +++++++++- build/source/dshare/var_lookup.f90 | 20 +++++++++- build/source/engine/bigAquifer.f90 | 25 ++++++++++++ build/source/engine/computFlux.f90 | 47 ++++++++++++++++++++++- build/source/engine/computJacDAE.f90 | 32 +++++++++++++++- build/source/engine/soilLiqFlx.f90 | 36 ++++++++++++++++-- build/source/engine/vegNrgFlux.f90 | 57 +++++++++++++++++++++++++--- 8 files changed, 235 insertions(+), 16 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 289582ba1..7f26f8b03 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -724,6 +724,11 @@ function get_ixderiv(varName) case('dGroundEvaporation_dTCanopy' ); get_ixderiv = iLookDERIV%dGroundEvaporation_dTCanopy ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) case('dGroundEvaporation_dTGround' ); get_ixderiv = iLookDERIV%dGroundEvaporation_dTGround ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) case('dGroundEvaporation_dCanWat' ); get_ixderiv = iLookDERIV%dGroundEvaporation_dCanWat ! derivative in ground evaporation w.r.t. canopy total water content (s-1) + ! derivatives in transpiration + case('dCanopyTrans_dTCanair' ); get_ixderiv = iLookDERIV%dCanopyTrans_dTCanair ! derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + case('dCanopyTrans_dTCanopy' ); get_ixderiv = iLookDERIV%dCanopyTrans_dTCanopy ! derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + case('dCanopyTrans_dTGround' ); get_ixderiv = iLookDERIV%dCanopyTrans_dTGround ! derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + case('dCanopyTrans_dCanWat' ); get_ixderiv = iLookDERIV%dCanopyTrans_dCanWat ! derivative in canopy transpiration w.r.t. canopy total water content (s-1) ! derivatives in canopy water w.r.t canopy temperature case('dTheta_dTkCanopy' ); get_ixderiv = iLookDERIV%dTheta_dTkCanopy ! derivative of volumetric liquid water content w.r.t. temperature (K-1) case('d2Theta_dTkCanopy2' ); get_ixderiv = iLookDERIV%d2Theta_dTkCanopy2 ! second derivative of volumetric liquid water content w.r.t. temperature @@ -756,6 +761,16 @@ function get_ixderiv(varName) case('dq_dNrgStateBelow' ); get_ixderiv = iLookDERIV%dq_dNrgStateBelow ! change in the flux in layer interfaces w.r.t. state variables in the layer below case('dPsiLiq_dTemp' ); get_ixderiv = iLookDERIV%dPsiLiq_dTemp ! derivative in the liquid water matric potential w.r.t. temperature (m K-1) case('dPsiLiq_dPsi0' ); get_ixderiv = iLookDERIV%dPsiLiq_dPsi0 ! derivative in liquid matric potential w.r.t. total matric potential (-) + ! derivatives in soil transpiration w.r.t. canopy state variables + case('mLayerdTrans_dTCanair' ); get_ixderiv = iLookDERIV%mLayerdTrans_dTCanair ! derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + case('mLayerdTrans_dTCanopy' ); get_ixderiv = iLookDERIV%mLayerdTrans_dTCanopy ! derivatives in the soil layer transpiration flux w.r.t. canopy temperature + case('mLayerdTrans_dTGround' ); get_ixderiv = iLookDERIV%mLayerdTrans_dTGround ! derivatives in the soil layer transpiration flux w.r.t. ground temperature + case('mLayerdTrans_dCanWat' ); get_ixderiv = iLookDERIV%mLayerdTrans_dCanWat ! derivatives in the soil layer transpiration flux w.r.t. canopy total water + ! derivatives in aquifer transpiration w.r.t. canopy state variables + case('dAquiferTrans_dTCanair' ); get_ixderiv = iLookDERIV%dAquiferTrans_dTCanair ! derivative in the aquifer transpiration flux w.r.t. canopy air temperature + case('dAquiferTrans_dTCanopy' ); get_ixderiv = iLookDERIV%dAquiferTrans_dTCanopy ! derivative in the aquifer transpiration flux w.r.t. canopy temperature + case('dAquiferTrans_dTGround' ); get_ixderiv = iLookDERIV%dAquiferTrans_dTGround ! derivative in the aquifer transpiration flux w.r.t. ground temperature + case('dAquiferTrans_dCanWat' ); get_ixderiv = iLookDERIV%dAquiferTrans_dCanWat ! derivative in the aquifer transpiration flux w.r.t. canopy total water ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature case('dFracLiqSnow_dTk' ); get_ixderiv = iLookDERIV%dFracLiqSnow_dTk ! derivative in fraction of liquid snow w.r.t. temperature case('mLayerdTheta_dTk' ); get_ixderiv = iLookDERIV%mLayerdTheta_dTk ! derivative of volumetric liquid water content w.r.t. temperature (K-1) diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 001c5d077..9bace74bc 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -558,11 +558,16 @@ subroutine popMetadat(err,message) deriv_meta(iLookDERIV%dGroundEvaporation_dTCanopy) = var_info('dGroundEvaporation_dTCanopy' , 'derivative in ground evaporation w.r.t. canopy temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dGroundEvaporation_dTGround) = var_info('dGroundEvaporation_dTGround' , 'derivative in ground evaporation w.r.t. ground temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dGroundEvaporation_dCanWat) = var_info('dGroundEvaporation_dCanWat' , 'derivative in ground evaporation w.r.t. canopy total water content' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! derivatives in transpiration + deriv_meta(iLookDERIV%dCanopyTrans_dTCanair) = var_info('dCanopyTrans_dTCanair' , 'derivative in canopy transpiration w.r.t. canopy air temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCanopyTrans_dTCanopy) = var_info('dCanopyTrans_dTCanopy' , 'derivative in canopy transpiration w.r.t. canopy temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCanopyTrans_dTGround) = var_info('dCanopyTrans_dTGround' , 'derivative in canopy transpiration w.r.t. ground temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCanopyTrans_dCanWat) = var_info('dCanopyTrans_dCanWat' , 'derivative in canopy transpiration w.r.t. canopy total water content' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! derivatives in canopy water w.r.t canopy temperature deriv_meta(iLookDERIV%dTheta_dTkCanopy) = var_info('dTheta_dTkCanopy' , 'derivative of volumetric liquid water content w.r.t. temperature' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%d2Theta_dTkCanopy2) = var_info('d2Theta_dTkCanopy2' , 'second derivative of volumetric liquid water content w.r.t. temperature', 'K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dCanLiq_dTcanopy) = var_info('dCanLiq_dTcanopy' , 'derivative of canopy liquid storage w.r.t. temperature' , 'kg m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dFracLiqVeg_dTkCanopy) = var_info('dFracLiqVeg_dTkCanopy' , 'derivative in fraction of (throughfall + drainage) w.r.t. temperature', 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dFracLiqVeg_dTkCanopy) = var_info('dFracLiqVeg_dTkCanopy' , 'derivative in fraction of (throughfall + drainage) w.r.t. temperature', 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! derivatives in canopy liquid fluxes w.r.t. canopy water deriv_meta(iLookDERIV%scalarCanopyLiqDeriv) = var_info('scalarCanopyLiqDeriv' , 'derivative in (throughfall + drainage) w.r.t. canopy liquid water' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%scalarThroughfallRainDeriv) = var_info('scalarThroughfallRainDeriv' , 'derivative in throughfall w.r.t. canopy liquid water' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) @@ -590,6 +595,16 @@ subroutine popMetadat(err,message) deriv_meta(iLookDERIV%dq_dNrgStateBelow) = var_info('dq_dNrgStateBelow' , 'change in flux at layer interfaces w.r.t. states in the layer below' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dPsiLiq_dTemp) = var_info('dPsiLiq_dTemp' , 'derivative in the liquid water matric potential w.r.t. temperature' , 'm K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dPsiLiq_dPsi0) = var_info('dPsiLiq_dPsi0' , 'derivative in liquid matric potential w.r.t. total matric potential' , '-' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + ! derivatives in soil transpiration w.r.t. canopy state variables + deriv_meta(iLookDERIV%mLayerdTrans_dTCanair) = var_info('mLayerdTrans_dTCanair' , 'derivatives in the soil layer transpiration flux w.r.t. canopy air temperature', 'm s-1 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%mLayerdTrans_dTCanopy) = var_info('mLayerdTrans_dTCanopy' , 'derivatives in the soil layer transpiration flux w.r.t. canopy temperature', 'm s-1 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%mLayerdTrans_dTGround) = var_info('mLayerdTrans_dTGround' , 'derivatives in the soil layer transpiration flux w.r.t. ground temperature', 'm s-1 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%mLayerdTrans_dCanWat) = var_info('mLayerdTrans_dCanWat' , 'derivatives in the soil layer transpiration flux w.r.t. canopy total water', 'm-1 s-1 kg-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + ! derivatives in aquifer transpiration w.r.t. canopy state variables + deriv_meta(iLookDERIV%dAquiferTrans_dTCanair) = var_info('dAquiferTrans_dTCanair' , 'derivative in the aquifer transpiration flux w.r.t. canopy air temperature', 'm s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dAquiferTrans_dTCanopy) = var_info('dAquiferTrans_dTCanopy' , 'derivative in the aquifer transpiration flux w.r.t. canopy temperature', 'm s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dAquiferTrans_dTGround) = var_info('dAquiferTrans_dTGround' , 'derivative in the aquifer transpiration flux w.r.t. ground temperature', 'm s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dAquiferTrans_dCanWat) = var_info('dAquiferTrans_dCanWat' , 'derivative in the aquifer transpiration flux w.r.t. canopy total water', 'm-1 s-1 kg-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature deriv_meta(iLookDERIV%dFracLiqSnow_dTk) = var_info('dFracLiqSnow_dTk' , 'derivative in fraction of liquid snow w.r.t. temperature' , 'K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%mLayerdTheta_dTk) = var_info('mLayerdTheta_dTk' , 'derivative of volumetric liquid water content w.r.t. temperature' , 'K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) @@ -597,7 +612,7 @@ subroutine popMetadat(err,message) ! derivate in bulk heat capacity w.r.t. relevant state variables deriv_meta(iLookDERIV%dVolHtCapBulk_dPsi0) = var_info('dVolHtCapBulk_dPsi0' , 'derivative in bulk heat capacity w.r.t. matric potential' , 'J m-4 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dVolHtCapBulk_dTheta) = var_info('dVolHtCapBulk_dTheta' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dCanWat) = var_info('dVolHtCapBulk_dCanWat' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dCanWat) = var_info('dVolHtCapBulk_dCanWat' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dVolHtCapBulk_dTk) = var_info('dVolHtCapBulk_dTk' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dVolHtCapBulk_dTkCanopy) = var_info('dVolHtCapBulk_dTkCanopy' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 0ef591f2e..1818387cf 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -577,7 +577,12 @@ MODULE var_lookup integer(i4b) :: dGroundEvaporation_dTCanopy = integerMissing ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) integer(i4b) :: dGroundEvaporation_dTGround = integerMissing ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) integer(i4b) :: dGroundEvaporation_dCanWat = integerMissing ! derivative in ground evaporation w.r.t. canopy total water content (s-1) - ! derivatives in canopy water w.r.t canopy temperature + ! derivatives in transpiration + integer(i4b) :: dCanopyTrans_dTCanair = integerMissing ! derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + integer(i4b) :: dCanopyTrans_dTCanopy = integerMissing ! derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + integer(i4b) :: dCanopyTrans_dTGround = integerMissing ! derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + integer(i4b) :: dCanopyTrans_dCanWat = integerMissing ! derivative in canopy transpiration w.r.t. canopy total water content (s-1) + ! derivatives in canopy water w.r.t canopy temperature integer(i4b) :: dTheta_dTkCanopy = integerMissing ! derivative of volumetric liquid water content w.r.t. temperature (K-1) integer(i4b) :: d2Theta_dTkCanopy2 = integerMissing ! second derivative of volumetric liquid water content w.r.t. temperature integer(i4b) :: dCanLiq_dTcanopy = integerMissing ! derivative of canopy liquid storage w.r.t. temperature (kg m-2 K-1) @@ -609,6 +614,16 @@ MODULE var_lookup integer(i4b) :: dq_dNrgStateBelow = integerMissing ! change in the flux in layer interfaces w.r.t. state variables in the layer below integer(i4b) :: dPsiLiq_dTemp = integerMissing ! derivative in the liquid water matric potential w.r.t. temperature (m K-1) integer(i4b) :: dPsiLiq_dPsi0 = integerMissing ! derivative in liquid water matric potential w.r.t. the total water matric potential (-) + ! derivatives in soil transpiration w.r.t. canopy state variables + integer(i4b) :: mLayerdTrans_dTCanair = integerMissing ! derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + integer(i4b) :: mLayerdTrans_dTCanopy = integerMissing ! derivatives in the soil layer transpiration flux w.r.t. canopy temperature + integer(i4b) :: mLayerdTrans_dTGround = integerMissing ! derivatives in the soil layer transpiration flux w.r.t. ground temperature + integer(i4b) :: mLayerdTrans_dCanWat = integerMissing ! derivatives in the soil layer transpiration flux w.r.t. canopy total water + ! derivatives in aquifer transpiration w.r.t. canopy state variables + integer(i4b) :: dAquiferTrans_dTCanair = integerMissing ! derivative in the aquifer transpiration flux w.r.t. canopy air temperature + integer(i4b) :: dAquiferTrans_dTCanopy = integerMissing ! derivative in the aquifer transpiration flux w.r.t. canopy temperature + integer(i4b) :: dAquiferTrans_dTGround = integerMissing ! derivative in the aquifer transpiration flux w.r.t. ground temperature + integer(i4b) :: dAquiferTrans_dCanWat = integerMissing ! derivative in the aquifer transpiration flux w.r.t. canopy total water ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature integer(i4b) :: dFracLiqSnow_dTk = integerMissing ! derivative in fraction of liquid snow w.r.t. temperature integer(i4b) :: mLayerdTheta_dTk = integerMissing ! derivative of volumetric liquid water content w.r.t. temperature (K-1) @@ -861,7 +876,8 @@ MODULE var_lookup 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,& 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,& 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,& - 51) + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,& + 61, 62, 63) ! named variables: model indices type(iLook_index), public,parameter :: iLookINDEX =ilook_index ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& diff --git a/build/source/engine/bigAquifer.f90 b/build/source/engine/bigAquifer.f90 index 4a6e322c4..6ee449d6b 100644 --- a/build/source/engine/bigAquifer.f90 +++ b/build/source/engine/bigAquifer.f90 @@ -48,6 +48,11 @@ subroutine bigAquifer(& scalarAquiferStorageTrial, & ! intent(in): trial value of aquifer storage (m) scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) scalarSoilDrainage, & ! intent(in): soil drainage (m s-1) + ! input: pre-computed derivatives + dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) ! input: diagnostic variables and parameters mpar_data, & ! intent(in): model parameter structure diag_data, & ! intent(in): diagnostic variable structure @@ -56,6 +61,11 @@ subroutine bigAquifer(& scalarAquiferRecharge, & ! intent(out): recharge to the aquifer (m s-1) scalarAquiferBaseflow, & ! intent(out): total baseflow from the aquifer (m s-1) dBaseflow_dAquifer, & ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) + ! output: derivatives in transpiration w.r.t. canopy state variables + dAquiferTrans_dTCanair, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + dAquiferTrans_dTCanopy, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy temperature + dAquiferTrans_dTGround, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. ground temperature + dAquiferTrans_dCanWat, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy total water ! output: error control err,message) ! intent(out): error control ! named variables @@ -69,6 +79,11 @@ subroutine bigAquifer(& real(rkind),intent(in) :: scalarAquiferStorageTrial ! trial value of aquifer storage (m) real(rkind),intent(in) :: scalarCanopyTranspiration ! canopy transpiration (kg m-2 s-1) real(rkind),intent(in) :: scalarSoilDrainage ! soil drainage (m s-1) + ! input: pre-computed derivatves + real(rkind),intent(in) :: dCanopyTrans_dCanWat ! derivative in canopy transpiration w.r.t. canopy total water content (s-1) + real(rkind),intent(in) :: dCanopyTrans_dTCanair ! derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(rkind),intent(in) :: dCanopyTrans_dTCanopy ! derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(rkind),intent(in) :: dCanopyTrans_dTGround ! derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) ! input: diagnostic variables and parameters type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU @@ -77,6 +92,11 @@ subroutine bigAquifer(& real(rkind),intent(out) :: scalarAquiferRecharge ! recharge to the aquifer (m s-1) real(rkind),intent(out) :: scalarAquiferBaseflow ! total baseflow from the aquifer (m s-1) real(rkind),intent(out) :: dBaseflow_dAquifer ! change in baseflow flux w.r.t. aquifer storage (s-1) + ! output: derivatives in transpiration w.r.t. canopy state variables + real(rkind),intent(inout) :: dAquiferTrans_dTCanair ! derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + real(rkind),intent(inout) :: dAquiferTrans_dTCanopy ! derivatives in the aquifer transpiration flux w.r.t. canopy temperature + real(rkind),intent(inout) :: dAquiferTrans_dTGround ! derivatives in the aquifer transpiration flux w.r.t. ground temperature + real(rkind),intent(inout) :: dAquiferTrans_dCanWat ! derivatives in the aquifer transpiration flux w.r.t. canopy total water ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -103,6 +123,11 @@ subroutine bigAquifer(& ! compute aquifer transpiration (m s-1) aquiferTranspireFrac = scalarAquiferRootFrac*scalarTranspireLimAqfr/scalarTranspireLim ! fraction of total transpiration that comes from the aquifer (-) scalarAquiferTranspire = aquiferTranspireFrac*scalarCanopyTranspiration/iden_water ! aquifer transpiration (kg m-2 s-1 --> m s-1) + ! derivatives in transpiration w.r.t. canopy state variables + dAquiferTrans_dCanWat = aquiferTranspireFrac*dCanopyTrans_dCanWat /iden_water + dAquiferTrans_dTCanair = aquiferTranspireFrac*dCanopyTrans_dTCanair/iden_water + dAquiferTrans_dTCanopy = aquiferTranspireFrac*dCanopyTrans_dTCanopy/iden_water + dAquiferTrans_dTGround = aquiferTranspireFrac*dCanopyTrans_dTGround/iden_water ! compute aquifer recharge (transfer variables -- included for generality for basin-wide aquifer) scalarAquiferRecharge = scalarSoilDrainage ! m s-1 diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index ab4632cfd..65b766f97 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -337,6 +337,12 @@ subroutine computFlux(& dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. ground temperature dGroundEvaporation_dCanWat => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy total water content + ! derivatives in transpiration + dCanopyTrans_dTCanair => deriv_data%var(iLookDERIV%dCanopyTrans_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy => deriv_data%var(iLookDERIV%dCanopyTrans_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround => deriv_data%var(iLookDERIV%dCanopyTrans_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + dCanopyTrans_dCanWat => deriv_data%var(iLookDERIV%dCanopyTrans_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy total water content (s-1) + ! derivatives in canopy water w.r.t canopy temperature dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(out): [dp] derivative of canopy liquid storage w.r.t. temperature @@ -369,7 +375,20 @@ subroutine computFlux(& ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above - dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat & ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + + ! derivatives in soil transpiration w.r.t. canopy state variables + mLayerdTrans_dTCanair => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanair )%dat ,& !intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + mLayerdTrans_dTCanopy => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanopy )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy temperature + mLayerdTrans_dTGround => deriv_data%var(iLookDERIV%mLayerdTrans_dTGround )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. ground temperature + mLayerdTrans_dCanWat => deriv_data%var(iLookDERIV%mLayerdTrans_dCanWat )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy total water + + ! derivatives in aquifer transpiration w.r.t. canopy state variables + dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair )%dat(1) ,& !intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + dAquiferTrans_dTCanopy => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanopy )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy temperature + dAquiferTrans_dTGround => deriv_data%var(iLookDERIV%dAquiferTrans_dTGround )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. ground temperature + dAquiferTrans_dCanWat => deriv_data%var(iLookDERIV%dAquiferTrans_dCanWat )%dat(1) & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy total water + ) ! association to data in structures ! ***** @@ -461,6 +480,11 @@ subroutine computFlux(& dGroundEvaporation_dTCanair, & ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) dGroundEvaporation_dTCanopy, & ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) dGroundEvaporation_dTGround, & ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + ! output: transpiration derivatives + dCanopyTrans_dCanWat, & ! intent(out): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair, & ! intent(out): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy, & ! intent(out): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround, & ! intent(out): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) ! output: cross derivative terms dCanopyNetFlux_dCanWat, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) dGroundNetFlux_dCanWat, & ! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) @@ -652,6 +676,10 @@ subroutine computFlux(& ! input: pre-computed deriavatives mLayerdTheta_dTk(nSnow+1:nLayers), & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) dPsiLiq_dTemp(1:nSoil), & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) + dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) ! input: fluxes scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) scalarGroundEvaporation, & ! intent(in): ground evaporation (kg m-2 s-1) @@ -682,6 +710,11 @@ subroutine computFlux(& ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) dq_dNrgStateAbove, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) dq_dNrgStateBelow, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) + ! output: derivatives in transpiration w.r.t. canopy state variables + mLayerdTrans_dTCanair, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + mLayerdTrans_dTCanopy, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy temperature + mLayerdTrans_dTGround, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. ground temperature + mLayerdTrans_dCanWat, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy total water ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -706,7 +739,7 @@ subroutine computFlux(& scalarSoilControl = 1._rkind endif - ! compute drainage from the soil zone (needed for mass balance checks) + ! compute drainage from the soil zone (needed for mass balance checks and in aquifer recharge) scalarSoilDrainage = iLayerLiqFluxSoil(nSoil) ! expand derivatives to the total water matric potential @@ -794,6 +827,11 @@ subroutine computFlux(& scalarAquiferStorageTrial, & ! intent(in): trial value of aquifer storage (m) scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) scalarSoilDrainage, & ! intent(in): soil drainage (m s-1) + ! input: pre-computed derivatives + dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) ! input: diagnostic variables and parameters mpar_data, & ! intent(in): model parameter structure diag_data, & ! intent(in): diagnostic variable structure @@ -802,6 +840,11 @@ subroutine computFlux(& scalarAquiferRecharge, & ! intent(out): recharge to the aquifer (m s-1) scalarAquiferBaseflow, & ! intent(out): total baseflow from the aquifer (m s-1) dBaseflow_dAquifer, & ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) + ! output: derivatives in transpiration w.r.t. canopy state variables + dAquiferTrans_dTCanair, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + dAquiferTrans_dTCanopy, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy temperature + dAquiferTrans_dTGround, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. ground temperature + dAquiferTrans_dCanWat, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy total water ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index e61a49e8e..30d5f9991 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -249,6 +249,16 @@ subroutine computJacDAE(& ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below dNrgFlux_dWatAbove => deriv_data%var(iLookDERIV%dNrgFlux_dWatAbove )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. water state in the layer above dNrgFlux_dWatBelow => deriv_data%var(iLookDERIV%dNrgFlux_dWatBelow )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. water state in the layer below + ! derivatives in soil transpiration w.r.t. canopy state variables + mLayerdTrans_dTCanair => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanair )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + mLayerdTrans_dTCanopy => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanopy )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. canopy temperature + mLayerdTrans_dTGround => deriv_data%var(iLookDERIV%mLayerdTrans_dTGround )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. ground temperature + mLayerdTrans_dCanWat => deriv_data%var(iLookDERIV%mLayerdTrans_dCanWat )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. canopy total water + ! derivatives in aquifer transpiration w.r.t. canopy state variables + dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair )%dat(1) ,& !intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + dAquiferTrans_dTCanopy => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanopy )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy temperature + dAquiferTrans_dTGround => deriv_data%var(iLookDERIV%dAquiferTrans_dTGround )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. ground temperature + dAquiferTrans_dCanWat => deriv_data%var(iLookDERIV%dAquiferTrans_dCanWat )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy total water ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(in): [dp(:)] derivative in vertical liquid water flux at layer interfaces ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables @@ -551,7 +561,7 @@ subroutine computJacDAE(& if(computeBaseflow .and. nSoilOnlyHyd==nSoil)then do pLayer=1,nSoil qState = ixSoilOnlyHyd(pLayer) ! hydrology state index within the state subset - aJac(watState,qState) = aJac(watState,qState) + (dt/mLayerDepth(jLayer))*dBaseflow_dMatric(iLayer,pLayer) + aJac(watState,qState) = (dt/mLayerDepth(jLayer))*dBaseflow_dMatric(iLayer,pLayer) + aJac(watState,qState) end do endif @@ -561,7 +571,17 @@ subroutine computJacDAE(& ! ----- ! * liquid water fluxes for the aquifer... ! ---------------------------------------- - if(ixAqWat/=integerMissing) aJac(ixAqWat,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) * cj + if(ixAqWat/=integerMissing) then + aJac(ixAqWat,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) * cj + aJac(ixAqWat,ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! scalarAquiferRecharge = iLayerLiqFluxSoil(nSoil) + ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) + if(computeVegFlux)then + aJac(ixAqWat,ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 + aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) + aJac(ixAqWat,ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) + aJac(ixAqWat,ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) + endif + endif ! ----- ! * cross derivatives in the soil domain... @@ -606,6 +626,14 @@ subroutine computJacDAE(& aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) endif + ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) + if(computeVegFlux)then + aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dCanWat(iLayer)) + aJac(watState,ixVegHyd) ! dVol/dLiq (kg m-2)-1 + aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanair(iLayer)) + aJac(watState,ixCasNrg) ! dVol/dT (K-1) + aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanopy(iLayer)) + aJac(watState,ixVegNrg) ! dVol/dT (K-1) + aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTGround(iLayer)) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) + endif + ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) & + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index 0b86e93d8..2b4eceba2 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -102,6 +102,10 @@ subroutine soilLiqFlx(& ! input: pre-computed derivatives mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) dPsiLiq_dTemp, & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) + dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) ! input: fluxes scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) scalarGroundEvaporation, & ! intent(in): ground evaporation (kg m-2 s-1) @@ -132,6 +136,11 @@ subroutine soilLiqFlx(& ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) dq_dNrgStateAbove, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) dq_dNrgStateBelow, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) + ! output: derivatives in transpiration w.r.t. canopy state variables + mLayerdTrans_dTCanair, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + mLayerdTrans_dTCanopy, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy temperature + mLayerdTrans_dTGround, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. ground temperature + mLayerdTrans_dCanWat, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy total water ! output: error control err,message) ! intent(out): error control ! utility modules @@ -157,6 +166,10 @@ subroutine soilLiqFlx(& ! input: pre-computed derivatves real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative in volumetric liquid water content w.r.t. temperature (K-1) real(rkind),intent(in) :: dPsiLiq_dTemp(:) ! derivative in liquid water matric potential w.r.t. temperature (m K-1) + real(rkind),intent(in) :: dCanopyTrans_dCanWat ! derivative in canopy transpiration w.r.t. canopy total water content (s-1) + real(rkind),intent(in) :: dCanopyTrans_dTCanair ! derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(rkind),intent(in) :: dCanopyTrans_dTCanopy ! derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(rkind),intent(in) :: dCanopyTrans_dTGround ! derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) ! input: model fluxes real(rkind),intent(in) :: scalarCanopyTranspiration ! canopy transpiration (kg m-2 s-1) real(rkind),intent(in) :: scalarGroundEvaporation ! ground evaporation (kg m-2 s-1) @@ -187,7 +200,12 @@ subroutine soilLiqFlx(& ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) real(rkind),intent(inout) :: dq_dNrgStateAbove(0:) ! derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) real(rkind),intent(inout) :: dq_dNrgStateBelow(0:) ! derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) - ! output: error control + ! output: derivatives in transpiration w.r.t. canopy state variables + real(rkind),intent(inout) :: mLayerdTrans_dTCanair(:) ! derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + real(rkind),intent(inout) :: mLayerdTrans_dTCanopy(:) ! derivatives in the soil layer transpiration flux w.r.t. canopy temperature + real(rkind),intent(inout) :: mLayerdTrans_dTGround(:) ! derivatives in the soil layer transpiration flux w.r.t. ground temperature + real(rkind),intent(inout) :: mLayerdTrans_dCanWat(:) ! derivatives in the soil layer transpiration flux w.r.t. canopy total water +! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! ----------------------------------------------------------------------------------------------------------------------------------------------------- @@ -370,10 +388,22 @@ subroutine soilLiqFlx(& endif ! compute transpiration loss from each soil layer (kg m-2 s-1 --> m s-1) - mLayerTranspire = mLayerTranspireFrac(:)*scalarCanopyTranspiration/iden_water + mLayerTranspire(:) = mLayerTranspireFrac(:)*scalarCanopyTranspiration/iden_water + ! derivatives in transpiration w.r.t. canopy state variables + mLayerdTrans_dCanWat(:) = mLayerTranspireFrac(:)*dCanopyTrans_dCanWat /iden_water + mLayerdTrans_dTCanair(:) = mLayerTranspireFrac(:)*dCanopyTrans_dTCanair/iden_water + mLayerdTrans_dTCanopy(:) = mLayerTranspireFrac(:)*dCanopyTrans_dTCanopy/iden_water + mLayerdTrans_dTGround(:) = mLayerTranspireFrac(:)*dCanopyTrans_dTGround/iden_water ! special case of prescribed head -- no transpiration - if(ixBcUpperSoilHydrology==prescribedHead) mLayerTranspire(:) = 0._rkind + if(ixBcUpperSoilHydrology==prescribedHead) then + mLayerTranspire(:) = 0._rkind + ! derivatives in transpiration w.r.t. canopy state variables + mLayerdTrans_dCanWat(:) = 0._rkind + mLayerdTrans_dTCanair(:)= 0._rkind + mLayerdTrans_dTCanopy(:)= 0._rkind + mLayerdTrans_dTGround(:)= 0._rkind + endif endif ! if need to compute transpiration diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 65907474e..bca3c5926 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -187,6 +187,12 @@ subroutine vegNrgFlux(& dGroundEvaporation_dTCanopy, & ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) dGroundEvaporation_dTGround, & ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + ! output: transpiration derivatives + dCanopyTrans_dCanWat, & ! intent(out): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair, & ! intent(out): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy, & ! intent(out): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround, & ! intent(out): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + ! output: cross derivative terms dCanopyNetFlux_dCanWat, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) dGroundNetFlux_dCanWat, & ! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) @@ -270,6 +276,12 @@ subroutine vegNrgFlux(& real(rkind),intent(out) :: dGroundEvaporation_dTCanopy ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) real(rkind),intent(out) :: dGroundEvaporation_dTGround ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) +! output: transpiration derivatives + real(rkind),intent(out) :: dCanopyTrans_dCanWat ! intent(out): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + real(rkind),intent(out) :: dCanopyTrans_dTCanair ! intent(out): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(rkind),intent(out) :: dCanopyTrans_dTCanopy ! intent(out): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(rkind),intent(out) :: dCanopyTrans_dTGround ! intent(out): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + ! output: cross derivative terms real(rkind),intent(out) :: dCanopyNetFlux_dCanWat ! derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) real(rkind),intent(out) :: dGroundNetFlux_dCanWat ! derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) @@ -429,6 +441,12 @@ subroutine vegNrgFlux(& real(rkind) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) real(rkind) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) + ! output: latent heat flux derivatives (canopy trans) + real(rkind) :: dLatHeatCanopyTrans_dCanWat ! derivative in the latent heat of canopy transpiration w.r.t. canopy total water (J kg-1 s-1) + real(rkind) :: dLatHeatCanopyTrans_dTCanair ! derivative in the latent heat of canopy transpiration w.r.t. canopy air temperature + real(rkind) :: dLatHeatCanopyTrans_dTCanopy ! derivative in the latent heat of canopy transpiration w.r.t. canopy temperature + real(rkind) :: dLatHeatCanopyTrans_dTGround ! derivative in the latent heat of canopy transpiration w.r.t. ground temperature + ! --------------------------------------------------------------------------------------- ! point to variables in the data structure ! --------------------------------------------------------------------------------------- @@ -664,6 +682,11 @@ subroutine vegNrgFlux(& dGroundEvaporation_dTCanair= 0._rkind ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) dGroundEvaporation_dTCanopy= 0._rkind ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) dGroundEvaporation_dTGround= 0._rkind ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + ! set transpiration derivatives to zero + dCanopyTrans_dCanWat = 0._rkind ! derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair= 0._rkind ! derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy= 0._rkind ! derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround= 0._rkind ! derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) ! compute fluxes and derivatives -- separate approach for prescribed temperature and zero flux, if(ix_bcUpprTdyn == prescribedTemp)then @@ -1284,6 +1307,11 @@ subroutine vegNrgFlux(& dLatHeatGroundEvap_dTCanair, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy air temperature dLatHeatGroundEvap_dTCanopy, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy temperature dLatHeatGroundEvap_dTGround, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. ground temperature + ! output: latent heat flux derivatives (canopy trans) + dLatHeatCanopyTrans_dCanWat, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy total water (J kg-1 s-1) + dLatHeatCanopyTrans_dTCanair, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy air temperature + dLatHeatCanopyTrans_dTCanopy, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy temperature + dLatHeatCanopyTrans_dTGround, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. ground temperature ! output: cross derivatives dTurbFluxCanair_dCanWat, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) dTurbFluxCanopy_dCanWat, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) @@ -1497,6 +1525,20 @@ subroutine vegNrgFlux(& dCanopyEvaporation_dTGround = 0._rkind ! (kg m-2 s-1 K-1) end if + ! transpiration + if(scalarLatHeatCanopyTrans > 0._rkind)then ! flux directed towards the veg + dCanopyTrans_dCanWat = 0._rkind + dCanopyTrans_dTCanair= 0._rkind + dCanopyTrans_dTCanopy= 0._rkind + dCanopyTrans_dTGround= 0._rkind + else + dCanopyTrans_dCanWat= dLatHeatCanopyTrans_dCanWat/LH_vap ! transpiration is always vapor + dCanopyTrans_dTCanair= dLatHeatCanopyTrans_dTCanair/LH_vap + dCanopyTrans_dTCanopy= dLatHeatCanopyTrans_dTCanopy/LH_vap + dCanopyTrans_dTGround= dLatHeatCanopyTrans_dTGround/LH_vap + end if + + ! compute the liquid water derivarives (ground evap) dGroundEvaporation_dCanWat = dLatHeatGroundEvap_dCanWat/LH_vap ! (s-1) dGroundEvaporation_dTCanair = dLatHeatGroundEvap_dTCanair/LH_vap ! (kg m-2 s-1 K-1) @@ -2634,6 +2676,11 @@ subroutine turbFluxes(& dLatHeatGroundEvap_dTCanair, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy air temperature dLatHeatGroundEvap_dTCanopy, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy temperature dLatHeatGroundEvap_dTGround, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. ground temperature + ! output: latent heat flux derivatives (canopy trans) + dLatHeatCanopyTrans_dCanWat, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy total water (J kg-1 s-1) + dLatHeatCanopyTrans_dTCanair, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy air temperature + dLatHeatCanopyTrans_dTCanopy, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy temperature + dLatHeatCanopyTrans_dTGround, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. ground temperature ! output: cross derivatives dTurbFluxCanair_dCanWat, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) dTurbFluxCanopy_dCanWat, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) @@ -2726,6 +2773,11 @@ subroutine turbFluxes(& real(rkind),intent(out) :: dLatHeatGroundEvap_dTCanair ! derivative in latent heat of ground evaporation w.r.t. canopy air temperature (W m-2 K-1) real(rkind),intent(out) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) real(rkind),intent(out) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) + ! output: latent heat flux derivatives (canopy trans) + real(rkind) :: dLatHeatCanopyTrans_dCanWat ! derivative in the latent heat of canopy transpiration w.r.t. canopy total water (J kg-1 s-1) + real(rkind) :: dLatHeatCanopyTrans_dTCanair ! derivative in the latent heat of canopy transpiration w.r.t. canopy air temperature + real(rkind) :: dLatHeatCanopyTrans_dTCanopy ! derivative in the latent heat of canopy transpiration w.r.t. canopy temperature + real(rkind) :: dLatHeatCanopyTrans_dTGround ! derivative in the latent heat of canopy transpiration flux w.r.t. ground temperature ! output: cross derivatives real(rkind),intent(out) :: dTurbFluxCanair_dCanWat ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) real(rkind),intent(out) :: dTurbFluxCanopy_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) @@ -2770,14 +2822,9 @@ subroutine turbFluxes(& real(rkind) :: dSenHeatGround_dTCanair ! derivative in the ground sensible heat flux w.r.t. canopy air temperature real(rkind) :: dSenHeatGround_dTCanopy ! derivative in the ground sensible heat flux w.r.t. canopy temperature real(rkind) :: dSenHeatGround_dTGround ! derivative in the ground sensible heat flux w.r.t. ground temperature - ! local variables -- latent heat flux derivatives - real(rkind) :: dLatHeatCanopyTrans_dTCanair ! derivative in the canopy transpiration flux w.r.t. canopy air temperature - real(rkind) :: dLatHeatCanopyTrans_dTCanopy ! derivative in the canopy transpiration flux w.r.t. canopy temperature - real(rkind) :: dLatHeatCanopyTrans_dTGround ! derivative in the canopy transpiration flux w.r.t. ground temperature ! local variables -- wetted fraction derivatives real(rkind) :: dLatHeatCanopyEvap_dWetFrac ! derivative in the latent heat of canopy evaporation w.r.t. canopy wet fraction (W m-2) real(rkind) :: dLatHeatCanopyTrans_dWetFrac ! derivative in the latent heat of canopy transpiration w.r.t. canopy wet fraction (W m-2) - real(rkind) :: dLatHeatCanopyTrans_dCanWat ! derivative in the latent heat of canopy transpiration w.r.t. canopy total water (J kg-1 s-1) ! ----------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='turbFluxes/' From 2e365c87238fd7a5f8d7b54aa731c71c65a98366 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 14 Mar 2022 20:46:54 +0900 Subject: [PATCH 0248/1472] Surface infiltration derivatives are added and checked, and a few bugs in thermal conductivity derivatives fixed. Perturbation in infiltration and thermal conductivity need to be fixed still. --- build/source/dshare/get_ixname.f90 | 2 + build/source/dshare/popMetadat.f90 | 2 + build/source/dshare/var_lookup.f90 | 4 +- build/source/engine/bigAquifer.f90 | 2 +- build/source/engine/computFlux.f90 | 89 +++++++--- build/source/engine/computJacDAE.f90 | 68 +++++--- build/source/engine/soilLiqFlx.f90 | 245 ++++++++++++++++++++++----- build/source/engine/ssdNrgFlux.f90 | 24 +-- 8 files changed, 323 insertions(+), 113 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 7f26f8b03..fca7e975a 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -751,6 +751,7 @@ function get_ixderiv(varName) case('d2VolTot_d2Psi0' ); get_ixderiv = iLookDERIV%d2VolTot_d2Psi0 ! second derivative in total water content w.r.t. total water matric potential case('dq_dHydStateAbove' ); get_ixderiv = iLookDERIV%dq_dHydStateAbove ! change in the flux in layer interfaces w.r.t. state variables in the layer above case('dq_dHydStateBelow' ); get_ixderiv = iLookDERIV%dq_dHydStateBelow ! change in the flux in layer interfaces w.r.t. state variables in the layer below + case('dq_dHydStateLayerSurfVec' ); get_ixderiv = iLookDERIV%dq_dHydStateLayerSurfVec ! change in the flux in soil surface interface w.r.t. state variables in layer above and below case('mLayerdTheta_dPsi' ); get_ixderiv = iLookDERIV%mLayerdTheta_dPsi ! derivative in the soil water characteristic w.r.t. psi (m-1) case('mLayerdPsi_dTheta' ); get_ixderiv = iLookDERIV%mLayerdPsi_dTheta ! derivative in the soil water characteristic w.r.t. theta (m) case('dCompress_dPsi' ); get_ixderiv = iLookDERIV%dCompress_dPsi ! derivative in compressibility w.r.t matric head (m-1) @@ -759,6 +760,7 @@ function get_ixderiv(varName) ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables case('dq_dNrgStateAbove' ); get_ixderiv = iLookDERIV%dq_dNrgStateAbove ! change in the flux in layer interfaces w.r.t. state variables in the layer above case('dq_dNrgStateBelow' ); get_ixderiv = iLookDERIV%dq_dNrgStateBelow ! change in the flux in layer interfaces w.r.t. state variables in the layer below + case('dq_dNrgStateLayerSurfVec' ); get_ixderiv = iLookDERIV%dq_dNrgStateLayerSurfVec ! change in the flux in soil surface interface w.r.t. state variables in layer above and below case('dPsiLiq_dTemp' ); get_ixderiv = iLookDERIV%dPsiLiq_dTemp ! derivative in the liquid water matric potential w.r.t. temperature (m K-1) case('dPsiLiq_dPsi0' ); get_ixderiv = iLookDERIV%dPsiLiq_dPsi0 ! derivative in liquid matric potential w.r.t. total matric potential (-) ! derivatives in soil transpiration w.r.t. canopy state variables diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 9bace74bc..6ab16a1fe 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -588,11 +588,13 @@ subroutine popMetadat(err,message) deriv_meta(iLookDERIV%mLayerdPsi_dTheta) = var_info('mLayerdPsi_dTheta' , 'derivative in the soil water characteristic w.r.t. theta' , 'm' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dq_dHydStateAbove) = var_info('dq_dHydStateAbove' , 'change in flux at layer interfaces w.r.t. states in the layer above' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dq_dHydStateBelow) = var_info('dq_dHydStateBelow' , 'change in flux at layer interfaces w.r.t. states in the layer below' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dq_dHydStateLayerSurfVec) = var_info('dq_dHydStateLayerSurfVec' , 'change in the flux in soil surface interface w.r.t. state variables in layers' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) ! derivative in baseflow flux w.r.t. aquifer storage deriv_meta(iLookDERIV%dBaseflow_dAquifer) = var_info('dBaseflow_dAquifer' , 'derivative in baseflow flux w.r.t. aquifer storage' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables deriv_meta(iLookDERIV%dq_dNrgStateAbove) = var_info('dq_dNrgStateAbove' , 'change in flux at layer interfaces w.r.t. states in the layer above' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dq_dNrgStateBelow) = var_info('dq_dNrgStateBelow' , 'change in flux at layer interfaces w.r.t. states in the layer below' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dq_dNrgStateLayerSurfVec) = var_info('dq_dNrgStateLayerSurfVec' , 'change in the flux in soil surface interface w.r.t. state variables in layers' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dPsiLiq_dTemp) = var_info('dPsiLiq_dTemp' , 'derivative in the liquid water matric potential w.r.t. temperature' , 'm K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dPsiLiq_dPsi0) = var_info('dPsiLiq_dPsi0' , 'derivative in liquid matric potential w.r.t. total matric potential' , '-' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) ! derivatives in soil transpiration w.r.t. canopy state variables diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 1818387cf..482433254 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -604,6 +604,7 @@ MODULE var_lookup integer(i4b) :: d2VolTot_d2Psi0 = integerMissing ! second derivative in total water content w.r.t. total water matric potential integer(i4b) :: dq_dHydStateAbove = integerMissing ! change in the flux in layer interfaces w.r.t. state variables in the layer above integer(i4b) :: dq_dHydStateBelow = integerMissing ! change in the flux in layer interfaces w.r.t. state variables in the layer below + integer(i4b) :: dq_dHydStateLayerSurfVec = integerMissing ! change in the flux in soil surface interface w.r.t. state variables in layer above and below integer(i4b) :: mLayerdTheta_dPsi = integerMissing ! derivative in the soil water characteristic w.r.t. psi (m-1) integer(i4b) :: mLayerdPsi_dTheta = integerMissing ! derivative in the soil water characteristic w.r.t. theta (m) integer(i4b) :: dCompress_dPsi = integerMissing ! derivative in compressibility w.r.t matric head (m-1) @@ -612,6 +613,7 @@ MODULE var_lookup ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables integer(i4b) :: dq_dNrgStateAbove = integerMissing ! change in the flux in layer interfaces w.r.t. state variables in the layer above integer(i4b) :: dq_dNrgStateBelow = integerMissing ! change in the flux in layer interfaces w.r.t. state variables in the layer below + integer(i4b) :: dq_dNrgStateLayerSurfVec = integerMissing ! change in the flux in soil surface interface w.r.t. state variables in layer above and below integer(i4b) :: dPsiLiq_dTemp = integerMissing ! derivative in the liquid water matric potential w.r.t. temperature (m K-1) integer(i4b) :: dPsiLiq_dPsi0 = integerMissing ! derivative in liquid water matric potential w.r.t. the total water matric potential (-) ! derivatives in soil transpiration w.r.t. canopy state variables @@ -877,7 +879,7 @@ MODULE var_lookup 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,& 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,& 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,& - 61, 62, 63) + 61, 62, 63, 64, 65) ! named variables: model indices type(iLook_index), public,parameter :: iLookINDEX =ilook_index ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& diff --git a/build/source/engine/bigAquifer.f90 b/build/source/engine/bigAquifer.f90 index 6ee449d6b..729c5ed64 100644 --- a/build/source/engine/bigAquifer.f90 +++ b/build/source/engine/bigAquifer.f90 @@ -41,7 +41,7 @@ module bigAquifer_module ! *************************************************************************************************************** - ! public subroutine soilLiqFlx: compute liquid water fluxes and their derivatives + ! public subroutine bigAquifer: compute aquifer water fluxes and their derivatives ! *************************************************************************************************************** subroutine bigAquifer(& ! input: state variables and fluxes diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 65b766f97..e8c117b74 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -159,14 +159,14 @@ subroutine computFlux(& ! * dummy variables ! --------------------------------------------------------------------------------------- ! input-output: control - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution real(rkind),intent(in) :: drainageMeltPond ! drainage from the surface melt pond (kg m-2 s-1) ! input: state variables real(rkind),intent(in) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) @@ -181,33 +181,37 @@ subroutine computFlux(& real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) ! input: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(var_i), intent(in) :: type_data ! type of vegetation and soil - type(var_d), intent(in) :: attr_data ! spatial attributes - type(var_dlength), intent(in) :: mpar_data ! model parameters - type(var_d), intent(in) :: forc_data ! model forcing data - type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin - type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_ilength), intent(in) :: indx_data ! indices defining model states and layers + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(var_i), intent(in) :: type_data ! type of vegetation and soil + type(var_d), intent(in) :: attr_data ! spatial attributes + type(var_dlength), intent(in) :: mpar_data ! model parameters + type(var_d), intent(in) :: forc_data ! model forcing data + type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin + type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_ilength), intent(in) :: indx_data ! indices defining model states and layers ! input-output: data structures - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! input-output: flux vector and baseflow derivatives - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) real(rkind),intent(out) :: fluxVec(:) ! model flux vector (mixed units) ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! --------------------------------------------------------------------------------------- ! * local variables ! --------------------------------------------------------------------------------------- integer(i4b) :: local_ixGroundwater ! local index for groundwater representation integer(i4b) :: iLayer ! index of model layers logical(lgt) :: doVegNrgFlux ! flag to compute the energy flux over vegetation - real(rkind),dimension(nSoil) :: dHydCond_dMatric ! derivative in hydraulic conductivity w.r.t matric head (s-1) + real(rkind),dimension(nSoil) :: dHydCond_dMatric ! derivative in hydraulic conductivity w.r.t matric head (s-1) character(LEN=256) :: cmessage ! error message of downwind routine + real(rkind) :: above_soilLiqFluxDeriv ! derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + real(rkind) :: above_soildLiq_dTk ! derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + real(rkind) :: above_soilFracLiq ! fraction of liquid water layer above soil (canopy or snow) (-) + ! -------------------------------------------------------------- ! initialize error control err=0; message='computFlux/' @@ -297,6 +301,8 @@ subroutine computFlux(& scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1) ,& ! intent(out): [dp] soil control on infiltration, zero or one scalarMaxInfilRate => flux_data%var(iLookFLUX%scalarMaxInfilRate)%dat(1) ,& ! intent(out): [dp] maximum infiltration rate (m s-1) scalarInfiltration => flux_data%var(iLookFLUX%scalarInfiltration)%dat(1) ,& ! intent(out): [dp] infiltration of water into the soil profile (m s-1) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(inout): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(inout): [dp(:)] fraction of liquid water in each snow layer (-) ! boundary fluxes in the soil domain scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(out): [dp] rain that reaches the ground without ever touching the canopy (kg m-2 s-1) @@ -356,8 +362,8 @@ subroutine computFlux(& dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below - dNrgFlux_dWatAbove => deriv_data%var(iLookDERIV%dNrgFlux_dWatAbove )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. water state in the layer above - dNrgFlux_dWatBelow => deriv_data%var(iLookDERIV%dNrgFlux_dWatBelow )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. water state in the layer below + dNrgFlux_dWatAbove => deriv_data%var(iLookDERIV%dNrgFlux_dWatAbove )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. water state in the layer above + dNrgFlux_dWatBelow => deriv_data%var(iLookDERIV%dNrgFlux_dWatBelow )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. water state in the layer below ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(out): [dp(:)] derivative in vertical liquid water flux at layer interfaces @@ -366,6 +372,7 @@ subroutine computFlux(& dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + dq_dHydStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dHydStateLayerSurfVec )%dat ,& ! intent(out): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers mLayerdTheta_dPsi => deriv_data%var(iLookDERIV%mLayerdTheta_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. psi mLayerdPsi_dTheta => deriv_data%var(iLookDERIV%mLayerdPsi_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. theta dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in compressibility w.r.t matric head @@ -376,6 +383,7 @@ subroutine computFlux(& ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + dq_dNrgStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dNrgStateLayerSurfVec )%dat ,& ! intent(out): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers ! derivatives in soil transpiration w.r.t. canopy state variables mLayerdTrans_dTCanair => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanair )%dat ,& !intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature @@ -643,16 +651,36 @@ subroutine computFlux(& ! compute drainage from the soil zone (needed for mass balance checks) scalarSnowDrainage = iLayerLiqFluxSnow(nSnow) + ! save bottom layer of snow derivatives + above_soilLiqFluxDeriv = iLayerLiqFluxSnowDeriv(nSnow) ! derivative in vertical liquid water flux at bottom snow layer interface + above_soildLiq_dTk = mLayerdTheta_dTk(nSnow) ! derivative in volumetric liquid water content in bottom snow layer w.r.t. temperature + above_soilFracLiq = mLayerFracLiqSnow(nSnow) ! fraction of liquid water in bottom snow layer (-) + else ! define forcing for the soil domain for the case of no snow layers ! NOTE: in case where nSnowOnlyHyd==0 AND snow layers exist, then scalarRainPlusMelt is taken from the previous flux evaluation - if(nSnow==0)then + if(nSnow==0)then !no snow layers scalarRainPlusMelt = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water & ! liquid flux from the canopy (m s-1) + drainageMeltPond/iden_water ! melt of the snow without a layer (m s-1) - endif ! if no snow layers - endif + if(ixVegHyd/=integerMissing)then + ! save canopy derivatives + above_soilLiqFluxDeriv = scalarcanopyLiqDeriv/iden_water ! derivative in (throughfall + drainage) w.r.t. canopy liquid water + above_soildLiq_dTk = dCanLiq_dTcanopy ! derivative of canopy liquid storage w.r.t. temperature + above_soilFracLiq = scalarFracLiqVeg ! fraction of liquid water in canopy (-) + else + above_soilLiqFluxDeriv = 0._rkind + above_soildLiq_dTk = 0._rkind + above_soilFracLiq = 0._rkind + endif + else ! snow layers, take from previous flux calculation + above_soilLiqFluxDeriv = iLayerLiqFluxSnowDeriv(nSnow) ! derivative in vertical liquid water flux at bottom snow layer interface + above_soildLiq_dTk = mLayerdTheta_dTk(nSnow) ! derivative in volumetric liquid water content in bottom snow layer w.r.t. temperature + above_soilFracLiq = mLayerFracLiqSnow(nSnow) ! fraction of liquid water in bottom snow layer (-) + endif ! snow layers or not + + endif ! if calculating the liquid flux through snow ! ***** ! * CALCULATE THE LIQUID FLUX THROUGH SOIL... @@ -680,6 +708,9 @@ subroutine computFlux(& dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) ! input: fluxes scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) scalarGroundEvaporation, & ! intent(in): ground evaporation (kg m-2 s-1) @@ -707,9 +738,11 @@ subroutine computFlux(& ! output: derivatives in fluxes w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below (m s-1 or s-1) dq_dHydStateAbove, & ! intent(inout): derivatives in the flux w.r.t. matric head in the layer above (s-1) dq_dHydStateBelow, & ! intent(inout): derivatives in the flux w.r.t. matric head in the layer below (s-1) + dq_dHydStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) dq_dNrgStateAbove, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) dq_dNrgStateBelow, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) + dq_dNrgStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) ! output: derivatives in transpiration w.r.t. canopy state variables mLayerdTrans_dTCanair, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature mLayerdTrans_dTCanopy, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy temperature diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 30d5f9991..c511fa614 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -267,11 +267,13 @@ subroutine computJacDAE(& dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t matric head dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + dq_dHydStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dHydStateLayerSurfVec )%dat ,& ! intent(in): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers ! derivative in baseflow flux w.r.t. aquifer storage dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] derivative in baseflow flux w.r.t. aquifer storage (s-1) ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + dq_dNrgStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dNrgStateLayerSurfVec )%dat ,& ! intent(in): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature dFracLiqSnow_dTk => deriv_data%var(iLookDERIV%dFracLiqSnow_dTk )%dat ,& ! intent(in): [dp(:)] derivative in fraction of liquid snow w.r.t. temperature mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk )%dat ,& ! intent(in): [dp(:)] derivative in volumetric liquid water content w.r.t. temperature @@ -369,7 +371,6 @@ subroutine computJacDAE(& ! * energy and liquid fluxes over vegetation... ! --------------------------------------------- if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) - if (doprint==1) print*, ixCasNrg,ixVegNrg,ixVegHyd, ixTopNrg,ixTopHyd, "watvegindices" ! * liquid water fluxes for vegetation canopy (-), dt*scalarFracLiqVeg*scalarCanopyLiqDeriv is the derivative in throughfall and canopy drainage with canopy water if(ixVegHyd/=integerMissing) aJac(ixVegHyd,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanWat - scalarCanopyLiqDeriv)*dt + 1._rkind * cj @@ -432,7 +433,7 @@ subroutine computJacDAE(& ! - define index within the state subset and the full state vector jState = ixSnowSoilNrg(iLayer) ! index within the state subset - if (doprint==1) print*, ixSnowSoilNrg(iLayer),jState, "snowsoilindices" + ! - diagonal elements aJac(jState,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) @@ -460,7 +461,7 @@ subroutine computJacDAE(& ! - define state indices for the current layer watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - if (doprint==1) print*, ixSnowOnlyHyd(iLayer),watState, "snowwatindices" + ! compute factor to convert liquid water derivative to total water derivative select case( ixHydType(iLayer) ) case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) @@ -490,15 +491,16 @@ subroutine computJacDAE(& do iLayer=1,nSnow ! loop through layers in the snow domain ! - check that the snow layer is desired - if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle + if(ixSnowOnlyNrg(iLayer)==integerMissing) cycle + + ! (define the energy state) + nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector ! - define state indices for the current layer watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - ! (define the energy state) - nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector - if(nrgstate/=integerMissing)then ! (energy state for the current layer is within the state subset) - if (doprint==1) print*, nrgState,watState, "snowwatenergyindices" + if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) + if (doprint==1) print*, nrgState,watState, "snow nrg wat indices" ! - include derivatives of energy fluxes w.r.t water fluxes for current layer aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_ice * cj & + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) & @@ -525,7 +527,8 @@ subroutine computJacDAE(& endif ! (if the energy state for the current layer is within the state subset) - end do ! (looping through soil layers) + end do ! (looping through snow layers) + endif ! (if there are state variables for both water and energy in the snow domain) ! ----- @@ -540,7 +543,7 @@ subroutine computJacDAE(& ! - define state indices watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset - if (doprint==1) print*, ixSoilOnlyHyd(iLayer),watState, "watsoilindices" + ! - define indices of the soil layers jLayer = iLayer+nSnow ! index of layer in the snow+soil vector @@ -557,6 +560,7 @@ subroutine computJacDAE(& if(iLayer0 .and. ixSnowOnlyHyd(nSnow)/=integerMissing)then + if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)) = (dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + elseif(computeVegFlux .and. ixVegHyd/=integerMissing)then !ixTopHyd = ixSoilOnlyHyd(1) + if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = (dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegNrg) + endif + endif ! (if the subset includes hydrology state variables in the soil domain) ! ----- @@ -587,12 +602,9 @@ subroutine computJacDAE(& ! * cross derivatives in the soil domain... ! ---------------------------------------- if(nSoilOnlyHyd>0 .and. nSoilOnlyNrg>0)then - do iLayer=1,nSoilOnlyHyd + do iLayer=1,nSoilOnlyNrg ! - check that the soil layer is desired - if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle - - ! - define index of hydrology state variable within the state subset - watState = ixSoilOnlyHyd(iLayer) + if(ixSoilOnlyNrg(iLayer)==integerMissing) cycle ! - define indices of the soil layers jLayer = iLayer+nSnow ! index of layer in the snow+soil vector @@ -600,8 +612,11 @@ subroutine computJacDAE(& ! - define the energy state variable nrgState = ixNrgLayer(jLayer) ! index within the full state vector + ! - define index of hydrology state variable within the state subset + watState = ixSoilOnlyHyd(iLayer) + if (doprint==1) print*, nrgState,watState, "soil nrg wat indices" ! only compute derivatives if the energy state for the current layer is within the state subset - if(nrgstate/=integerMissing)then + if(watstate/=integerMissing)then ! - include derivates in liquid water fluxes w.r.t. temperature for current layer aJac(watState,nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance @@ -638,9 +653,9 @@ subroutine computJacDAE(& aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) & + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) if(mLayerdTheta_dTk(jLayer) > verySmall) then ! ice is present - aJac(nrgState,watState) = aJac(nrgState,watState) - dVolTot_dPsi0(iLayer)*LH_fus*iden_water*cj & - - LH_fus*iden_water * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content - endif + aJac(nrgState,watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water*cj & + - LH_fus*iden_water * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + endif ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above if(iLayer>1)then @@ -652,11 +667,24 @@ subroutine computJacDAE(& if(ixSoilOnlyHyd(iLayer+1)/=integerMissing) aJac(ixSoilOnlyNrg(iLayer+1),watState) = (dt/mLayerDepth(jLayer+1))*(-dNrgFlux_dWatAbove(jLayer ) ) endif - endif ! (if the energy state for the current layer is within the state subset) + endif ! (if the water state for the current layer is within the state subset) + + ! - include terms for surface infiltration below surface + if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),nrgState) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(iLayer) + aJac(ixSoilOnlyHyd(1),nrgState) end do ! (looping through soil layers) + + ! - include terms for surface infiltration above surface + if(nSnowOnlyHyd>0 .and. ixSnowOnlyNrg(nSnow)/=integerMissing)then + if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + elseif(computeVegFlux .and. ixVegNrg/=integerMissing) then !ixTopHyd = ixSoilOnlyHyd(1) + if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegNrg) + endif + endif ! (if there are state variables for both water and energy in the soil domain) + + ! print the Jacobian if(globalPrintFlag)then print*, '** analytical Jacobian (full):' diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index 2b4eceba2..ad59929cc 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -106,6 +106,9 @@ subroutine soilLiqFlx(& dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) ! input: fluxes scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) scalarGroundEvaporation, & ! intent(in): ground evaporation (kg m-2 s-1) @@ -133,9 +136,11 @@ subroutine soilLiqFlx(& ! output: derivatives in fluxes w.r.t. hydrology state variables -- matric head or volumetric lquid water -- in the layer above and layer below (m s-1 or s-1) dq_dHydStateAbove, & ! intent(out): derivatives in the flux w.r.t. volumetric liquid water content in the layer above (m s-1) dq_dHydStateBelow, & ! intent(out): derivatives in the flux w.r.t. volumetric liquid water content in the layer below (m s-1) + dq_dHydStateLayerSurfVec, & ! intent(out): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) dq_dNrgStateAbove, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) dq_dNrgStateBelow, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) + dq_dNrgStateLayerSurfVec, & ! intent(out): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) ! output: derivatives in transpiration w.r.t. canopy state variables mLayerdTrans_dTCanair, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature mLayerdTrans_dTCanopy, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy temperature @@ -170,16 +175,19 @@ subroutine soilLiqFlx(& real(rkind),intent(in) :: dCanopyTrans_dTCanair ! derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) real(rkind),intent(in) :: dCanopyTrans_dTCanopy ! derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) real(rkind),intent(in) :: dCanopyTrans_dTGround ! derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + real(rkind),intent(in) :: above_soilLiqFluxDeriv ! derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + real(rkind),intent(in) :: above_soildLiq_dTk ! derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + real(rkind),intent(in) :: above_soilFracLiq ! fraction of liquid water layer above soil (canopy or snow) (-) ! input: model fluxes real(rkind),intent(in) :: scalarCanopyTranspiration ! canopy transpiration (kg m-2 s-1) real(rkind),intent(in) :: scalarGroundEvaporation ! ground evaporation (kg m-2 s-1) real(rkind),intent(in) :: scalarRainPlusMelt ! rain plus melt (m s-1) ! input-output: data structures - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! state vector geometry - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! state vector geometry + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU ! output: diagnostic variables for surface runoff real(rkind),intent(inout) :: xMaxInfilRate ! maximum infiltration rate (m s-1) real(rkind),intent(inout) :: scalarInfilArea ! fraction of unfrozen area where water can infiltrate (-) @@ -197,15 +205,17 @@ subroutine soilLiqFlx(& ! output: derivatives in fluxes w.r.t. state variables in the layer above and layer below (m s-1) real(rkind),intent(inout) :: dq_dHydStateAbove(0:) ! derivative in the flux in layer interfaces w.r.t. state variables in the layer above real(rkind),intent(inout) :: dq_dHydStateBelow(0:) ! derivative in the flux in layer interfaces w.r.t. state variables in the layer below + real(rkind),intent(inout) :: dq_dHydStateLayerSurfVec(0:) ! derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) real(rkind),intent(inout) :: dq_dNrgStateAbove(0:) ! derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) real(rkind),intent(inout) :: dq_dNrgStateBelow(0:) ! derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) + real(rkind),intent(inout) :: dq_dNrgStateLayerSurfVec(0:) ! derivative in surface infiltration w.r.t. temperature in above soil snow or canopy and every soil layer (m s-1 or s-1) ! output: derivatives in transpiration w.r.t. canopy state variables real(rkind),intent(inout) :: mLayerdTrans_dTCanair(:) ! derivatives in the soil layer transpiration flux w.r.t. canopy air temperature real(rkind),intent(inout) :: mLayerdTrans_dTCanopy(:) ! derivatives in the soil layer transpiration flux w.r.t. canopy temperature real(rkind),intent(inout) :: mLayerdTrans_dTGround(:) ! derivatives in the soil layer transpiration flux w.r.t. ground temperature real(rkind),intent(inout) :: mLayerdTrans_dCanWat(:) ! derivatives in the soil layer transpiration flux w.r.t. canopy total water -! output: error control + ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! ----------------------------------------------------------------------------------------------------------------------------------------------------- @@ -475,6 +485,7 @@ subroutine soilLiqFlx(& ! get input state variables... ! ============================ ! identify the type of perturbation + ! NOTE, WE ALSO NEED TO PERTURB EACH LAYER BELOW IN TURN AND TEMPERATURE select case(itry) ! skip undesired perturbations @@ -515,11 +526,21 @@ subroutine soilLiqFlx(& ixBcUpperSoilHydrology, & ! intent(in): index defining the type of boundary conditions (neumann or diriclet) nRoots, & ! intent(in): number of layers that contain roots ixIce, & ! intent(in): index of lowest ice layer + nSoil, & ! intent(in): number of soil layers ! input: state variables + mLayerTempTrial, & ! intent(in): temperature (K) scalarMatricHeadTrial, & ! intent(in): matric head in the upper-most soil layer (m) + mLayerMatricHeadTrial, & ! intent(in): matric head in each soil layer (m) scalarVolFracLiqTrial, & ! intent(in): volumetric liquid water content the upper-most soil layer (-) mLayerVolFracLiqTrial, & ! intent(in): volumetric liquid water content in each soil layer (-) mLayerVolFracIceTrial, & ! intent(in): volumetric ice content in each soil layer (-) + ! input: pre-computed deriavatives + mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) + mLayerdPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) + above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) ! input: depth of upper-most soil layer (m) mLayerDepth, & ! intent(in): depth of each soil layer (m) iLayerHeight, & ! intent(in): height at the interface of each layer (m) @@ -554,12 +575,15 @@ subroutine soilLiqFlx(& scalarSurfaceRunoff, & ! intent(out): surface runoff (m s-1) scalarSurfaceInfiltration, & ! intent(out): surface infiltration (m s-1) ! input-output: deriavtives in surface infiltration w.r.t. volumetric liquid water (m s-1) and matric head (s-1) in the upper-most soil layer - dq_dHydStateBelow(0), & ! intent(inout): derivative in surface infiltration w.r.t. hydrology state variable in the upper-most soil layer (m s-1 or s-1) - dq_dNrgStateBelow(0), & ! intent(out): derivative in surface infiltration w.r.t. energy state variable in the upper-most soil layer (m s-1 K-1) + dq_dHydStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) + dq_dNrgStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + dq_dHydStateBelow(0) = 0._rkind + dq_dNrgStateBelow(0) = 0._rkind + ! include base soil evaporation as the upper boundary flux iLayerLiqFluxSoil(0) = scalarGroundEvaporation/iden_water + scalarSurfaceInfiltration @@ -1121,11 +1145,21 @@ subroutine surfaceFlx(& bc_upper, & ! intent(in): index defining the type of boundary conditions (neumann or diriclet) nRoots, & ! intent(in): number of layers that contain roots ixIce, & ! intent(in): index of lowest ice layer + nSoil, & ! intent(in): number of soil layers ! input: state variables + mLayerTemp, & ! intent(in): temperature (K) scalarMatricHead, & ! intent(in): matric head in the upper-most soil layer (m) + mLayerMatricHead, & ! intent(in): matric head in each soil layer (m) scalarVolFracLiq, & ! intent(in): volumetric liquid water content in the upper-most soil layer (-) mLayerVolFracLiq, & ! intent(in): volumetric liquid water content in each soil layer (-) mLayerVolFracIce, & ! intent(in): volumetric ice content in each soil layer (-) + ! input: pre-computed derivatives + dTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + dTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) + mLayerdPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) + above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) ! input: depth of upper-most soil layer (m) mLayerDepth, & ! intent(in): depth of each soil layer (m) iLayerHeight, & ! intent(in): height at the interface of each layer (m) @@ -1160,30 +1194,41 @@ subroutine surfaceFlx(& scalarSurfaceRunoff, & ! intent(out): surface runoff (m s-1) scalarSurfaceInfiltration, & ! intent(out): surface infiltration (m s-1) ! input-output: deriavtives in surface infiltration w.r.t. volumetric liquid water (m s-1) and matric head (s-1) in the upper-most soil layer - dq_dHydState, & ! intent(inout): derivative in surface infiltration w.r.t. state variable in the upper-most soil layer (m s-1 or s-1) - dq_dNrgState, & ! intent(out): derivative in surface infiltration w.r.t. energy state variable in the upper-most soil layer (m s-1 K-1) + dq_dHydStateVec, & ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) + dq_dNrgStateVec, & ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) ! output: error control err,message) ! intent(out): error control USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water as a function of matric head (-) USE soil_utils_module,only:hydCond_psi ! compute hydraulic conductivity as a function of matric head (m s-1) USE soil_utils_module,only:hydCond_liq ! compute hydraulic conductivity as a function of volumetric liquid water content (m s-1) USE soil_utils_module,only:dPsi_dTheta ! compute derivative of the soil moisture characteristic w.r.t. theta (m) + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists USE soil_utils_module,only:gammp ! compute the cumulative probabilty based on the Gamma distribution ! compute infiltraton at the surface and its derivative w.r.t. mass in the upper soil layer implicit none ! ----------------------------------------------------------------------------------------------------------------------------- ! input: model control - logical(lgt),intent(in) :: doInfiltration ! flag indicating if desire to compute infiltration - logical(lgt),intent(in) :: deriv_desired ! flag to indicate if derivatives are desired - integer(i4b),intent(in) :: bc_upper ! index defining the type of boundary conditions - integer(i4b),intent(in) :: ixRichards ! index defining the option for Richards' equation (moisture or mixdform) - integer(i4b),intent(in) :: nRoots ! number of layers that contain roots - integer(i4b),intent(in) :: ixIce ! index of lowest ice layer + logical(lgt),intent(in) :: doInfiltration ! flag indicating if desire to compute infiltration + logical(lgt),intent(in) :: deriv_desired ! flag to indicate if derivatives are desired + integer(i4b),intent(in) :: bc_upper ! index defining the type of boundary conditions + integer(i4b),intent(in) :: ixRichards ! index defining the option for Richards' equation (moisture or mixdform) + integer(i4b),intent(in) :: nRoots ! number of layers that contain roots + integer(i4b),intent(in) :: ixIce ! index of lowest ice layer + integer(i4b),intent(in) :: nSoil ! number of soil layers ! input: state and diagnostic variables + real(rkind),intent(in) :: mLayerTemp(:) ! temperature (K) real(rkind),intent(in) :: scalarMatricHead ! matric head in the upper-most soil layer (m) + real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head in each soil layer (m) real(rkind),intent(in) :: scalarVolFracLiq ! volumetric liquid water content in the upper-most soil layer (-) real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric liquid water content in each soil layer (-) real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric ice content in each soil layer (-) + ! input: pre-computed derivatives, note all of these would need to be recomputed if wanted a numerical derivative + real(rkind),intent(in) :: dTheta_dTk(:) ! derivative in volumetric liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: dTheta_dPsi(:) ! derivative in the soil water characteristic w.r.t. psi (m-1) + real(rkind),intent(in) :: mLayerdPsi_dTheta(:) ! derivative in the soil water characteristic w.r.t. theta (m) + real(rkind),intent(in) :: above_soilLiqFluxDeriv ! derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + real(rkind),intent(in) :: above_soildLiq_dTk ! derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + real(rkind),intent(in) :: above_soilFracLiq ! fraction of liquid water layer above soil (canopy or snow) (-) ! input: depth of upper-most soil layer (m) real(rkind),intent(in) :: mLayerDepth(:) ! depth of upper-most soil layer (m) real(rkind),intent(in) :: iLayerHeight(0:) ! height at the interface of each layer (m) @@ -1219,17 +1264,21 @@ subroutine surfaceFlx(& real(rkind),intent(inout) :: scalarFrozenArea ! fraction of area that is considered impermeable due to soil ice (-) real(rkind),intent(out) :: scalarSurfaceRunoff ! surface runoff (m s-1) real(rkind),intent(out) :: scalarSurfaceInfiltration ! surface infiltration (m s-1) - ! output: deriavtives in surface infiltration w.r.t. volumetric liquid water (m s-1) and matric head (s-1) in the upper-most soil layer - real(rkind),intent(out) :: dq_dHydState ! derivative in surface infiltration w.r.t. state variable in the upper-most soil layer (m s-1 or s-1) - real(rkind),intent(out) :: dq_dNrgState ! derivative in surface infiltration w.r.t. energy state variable in the upper-most soil layer (m s-1 K-1) + ! output: derivatives in surface infiltration w.r.t. states in above soil snow or canopy and every soil layer + real(rkind),intent(out) :: dq_dHydStateVec(0:) ! derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) + real(rkind),intent(out) :: dq_dNrgStateVec(0:) ! derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! ----------------------------------------------------------------------------------------------------------------------------- ! local variables ! (general) - integer(i4b) :: iLayer ! index of soil layer - ! (head boundary condition) + integer(i4b) :: iLayer ! index of soil layer + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind) :: fpart1,fpart2 ! different parts of a function + real(rkind) :: dpart1(1:nSoil),dpart2(1:nSoil) ! derivatives for different parts of a function + real(rkind) :: dfracCap(1:nSoil),dfInfRaw(1:nSoil) ! derivatives for different parts of a function +! (head boundary condition) real(rkind) :: cFlux ! capillary flux (m s-1) real(rkind) :: dNum ! numerical derivative ! (simplified Green-Ampt infiltration) @@ -1247,12 +1296,32 @@ subroutine surfaceFlx(& ! (fraction of impermeable area associated with frozen ground) real(rkind) :: alpha ! shape parameter in the Gamma distribution real(rkind) :: xLimg ! upper limit of the integral + ! (derivatives) + real(rkind) :: dVolFracLiq_dWat(1:nSoil) ! derivative in vol fraction of liquid w.r.t. water state variable in root layers + real(rkind) :: dVolFracIce_dWat(1:nSoil) ! derivative in vol fraction of ice w.r.t. water state variable in root layers + real(rkind) :: dVolFracLiq_dTk(1:nSoil) ! derivative in vol fraction of liquid w.r.t. temperature in root layers + real(rkind) :: dVolFracIce_dTk(1:nSoil) ! derivative in vol fraction of ice w.r.t. temperature in root layers + real(rkind) :: dRootZoneLiq_dWat(1:nSoil) ! derivative in vol fraction of scalar root zone liquid w.r.t. water state variable in root layers + real(rkind) :: dRootZoneIce_dWat(1:nSoil) ! derivative in vol fraction of scalar root zone ice w.r.t. water state variable in root layers + real(rkind) :: dRootZoneLiq_dTk(1:nSoil) ! derivative in vol fraction of scalar root zone liquid w.r.t. temperature in root layers + real(rkind) :: dRootZoneIce_dTk(1:nSoil) ! derivative in vol fraction of scalar root zone ice w.r.t. temperature in root layers + real(rkind) :: dDepthWettingFront_dWat(1:nSoil) ! derivative in scalar depth of wetting front w.r.t. water state variable in root layers + real(rkind) :: dDepthWettingFront_dTk(1:nSoil) ! derivative in scalar depth of wetting front w.r.t. temperature in root layers + real(rkind) :: dXMaxInfilRate_dWat(1:nSoil) ! derivative in scalar max infiltration rate w.r.t. water state variable in root layers + real(rkind) :: dXMaxInfilRate_dTk(1:nSoil) ! derivative in scalar max infiltration rate w.r.t. temperature in root layers + real(rkind) :: dInfilArea_dWat(0:nSoil) ! derivative in scalar infiltration rate w.r.t. water state variable in canopy or snow and root layers + real(rkind) :: dInfilArea_dTk(0:nSoil) ! derivative in scalar infiltration rate w.r.t. temperature in canopy or snow and root layers + real(rkind) :: dFrozenArea_dWat(0:nSoil) ! derivative in scalar frozen area w.r.t. water state variable in canopy or snow and root layers + real(rkind) :: dFrozenArea_dTk(0:nSoil) ! derivative in scalar frozen area w.r.t. temperature in canopy or snow and root layers + real(rkind) :: dInfilRate_dWat(0:nSoil) ! derivative in scalar infiltration rate w.r.t. water state variable in canopy or snow and root layers + real(rkind) :: dInfilRate_dTk(0:nSoil) ! derivative in scalar infiltration rate w.r.t. temperature in canopy or snow and root layers + ! initialize error control err=0; message="surfaceFlx/" - ! compute derivative in the energy state - ! NOTE: revisit the need to do this - dq_dNrgState = 0._rkind + ! initialize derivatives + dq_dHydStateVec(:) = 0._rkind + dq_dNrgStateVec(:) = 0._rkind ! ***** ! compute the surface flux and its derivative @@ -1285,20 +1354,19 @@ subroutine surfaceFlx(& scalarSurfaceInfiltration = cflux + surfaceHydCond ! compute the derivative if(deriv_desired)then - ! compute the hydrology derivative + ! compute the hydrology derivative at the surface select case(ixRichards) ! (form of Richards' equation) - case(moisture); dq_dHydState = -surfaceDiffuse/(mLayerDepth(1)/2._rkind) - case(mixdform); dq_dHydState = -surfaceHydCond/(mLayerDepth(1)/2._rkind) + case(moisture); dq_dHydStateVec(1) = -surfaceDiffuse/(mLayerDepth(1)/2._rkind) + case(mixdform); dq_dHydStateVec(1) = -surfaceHydCond/(mLayerDepth(1)/2._rkind) case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return end select - ! compute the energy derivative - dq_dNrgState = -(dHydCond_dTemp/2._rkind)*(scalarMatricHead - upperBoundHead)/(mLayerDepth(1)*0.5_rkind) + dHydCond_dTemp/2._rkind + ! compute the energy derivative at the surface + dq_dNrgStateVec(1) = -(dHydCond_dTemp/2._rkind)*(scalarMatricHead - upperBoundHead)/(mLayerDepth(1)*0.5_rkind) + dHydCond_dTemp/2._rkind ! compute the numerical derivative !cflux = -surfaceHydCond*((scalarMatricHead+dx) - upperBoundHead) / (mLayerDepth(1)*0.5_rkind) !surfaceInfiltration1 = cflux + surfaceHydCond !dNum = (surfaceInfiltration1 - scalarSurfaceInfiltration)/dx else - dq_dHydState = 0._rkind dNum = 0._rkind end if !write(*,'(a,1x,10(e30.20,1x))') 'scalarMatricHead, scalarSurfaceInfiltration, dq_dHydState, dNum = ', & @@ -1311,19 +1379,64 @@ subroutine surfaceFlx(& ! force infiltration to be constant over the iterations if(doInfiltration)then - ! define the storage in the root zone (m) + ! (process root layers only liquid and ice derivatives) + dVolFracLiq_dWat(:) = 0._rkind + dVolFracIce_dWat(:) = 0._rkind + dVolFracLiq_dTk(:) = 0._rkind + dVolFracIce_dTk(:) = 0._rkind + if(nRoots > 0)then + select case(ixRichards) ! (form of Richards' equation) + case(moisture) + dVolFracLiq_dWat(:) = 1._rkind + dVolFracIce_dWat(:) = mLayerdPsi_dTheta(:) - 1._rkind + do iLayer=1,nRoots + Tcrit = crit_soilT( mLayerMatricHead(iLayer) ) + if(mLayerTemp(iLayer) < Tcrit) dVolFracLiq_dTk(iLayer) = dTheta_dTk(iLayer) !dTheta_dTk(mLayerTempTrial(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_alpha(iLayer),vGn_n(iLayer),vGn_m(iLayer)) + if(mLayerTemp(iLayer) >=Tcrit) dVolFracLiq_dTk(iLayer) = 0._rkind + enddo + case(mixdform) + do iLayer=1,nRoots + Tcrit = crit_soilT( mLayerMatricHead(iLayer) ) + if(mLayerTemp(iLayer) < Tcrit)then + dVolFracLiq_dWat(iLayer) = 0._rkind + dVolFracIce_dWat(iLayer) = dTheta_dPsi(iLayer) + else + dVolFracLiq_dWat(iLayer) = dTheta_dPsi(iLayer) + dVolFracIce_dWat(iLayer) = 0._rkind + endif + if(mLayerTemp(iLayer) < Tcrit) dVolFracLiq_dTk(iLayer) = dTheta_dTk(iLayer) + if(mLayerTemp(iLayer) >=Tcrit) dVolFracLiq_dTk(iLayer) = 0._rkind + enddo + end select + dVolFracIce_dTk(:) = -dVolFracLiq_dTk(:) !often can and will simplify one of these terms out + endif + + ! define the storage in the root zone (m) and derivatives rootZoneLiq = 0._rkind rootZoneIce = 0._rkind + dRootZoneLiq_dWat(:) = 0._rkind + dRootZoneIce_dWat(:) = 0._rkind + dRootZoneLiq_dTk(:) = 0._rkind + dRootZoneIce_dTk(:) = 0._rkind + ! (process layers where the roots extend to the bottom of the layer) if(nRoots > 1)then do iLayer=1,nRoots-1 rootZoneLiq = rootZoneLiq + mLayerVolFracLiq(iLayer)*mLayerDepth(iLayer) rootZoneIce = rootZoneIce + mLayerVolFracIce(iLayer)*mLayerDepth(iLayer) + dRootZoneLiq_dWat(iLayer) = dVolFracLiq_dWat(iLayer)*mLayerDepth(iLayer) + dRootZoneIce_dWat(iLayer) = dVolFracIce_dWat(iLayer)*mLayerDepth(iLayer) + dRootZoneLiq_dTk(iLayer) = dVolFracLiq_dTk(iLayer) *mLayerDepth(iLayer) + dRootZoneIce_dTk(iLayer) = dVolFracIce_dTk(iLayer) *mLayerDepth(iLayer) end do end if ! (process layers where the roots end in the current layer) rootZoneLiq = rootZoneLiq + mLayerVolFracLiq(nRoots)*(rootingDepth - iLayerHeight(nRoots-1)) rootZoneIce = rootZoneIce + mLayerVolFracIce(nRoots)*(rootingDepth - iLayerHeight(nRoots-1)) + dRootZoneLiq_dWat(nRoots) = dVolFracLiq_dWat(nRoots)*(rootingDepth - iLayerHeight(nRoots-1)) + dRootZoneIce_dWat(nRoots) = dVolFracIce_dWat(nRoots)*(rootingDepth - iLayerHeight(nRoots-1)) + dRootZoneLiq_dTk(nRoots) = dVolFracLiq_dTk(nRoots)* (rootingDepth - iLayerHeight(nRoots-1)) + dRootZoneIce_dTk(nRoots) = dVolFracIce_dTk(nRoots)* (rootingDepth - iLayerHeight(nRoots-1)) ! define available capacity to hold water (m) availCapacity = theta_sat*rootingDepth - rootZoneIce @@ -1332,24 +1445,49 @@ subroutine surfaceFlx(& err=20; return end if - ! define the depth to the wetting front (m) + ! define the depth to the wetting front (m) and derivatives depthWettingFront = (rootZoneLiq/availCapacity)*rootingDepth + dDepthWettingFront_dWat(:)=( dRootZoneLiq_dWat(:)*rootingDepth + dRootZoneIce_dWat(:)*depthWettingFront )/availCapacity + dDepthWettingFront_dTk(:) =( dRootZoneLiq_dTk(:)*rootingDepth + dRootZoneIce_dTk(:)*depthWettingFront )/availCapacity ! define the hydraulic conductivity at depth=depthWettingFront (m s-1) hydCondWettingFront = surfaceSatHydCond * ( (1._rkind - depthWettingFront/sum(mLayerDepth))**(zScale_TOPMODEL - 1._rkind) ) - ! define the maximum infiltration rate (m s-1) + ! define the maximum infiltration rate (m s-1) and derivatives xMaxInfilRate = hydCondWettingFront*( (wettingFrontSuction + depthWettingFront)/depthWettingFront ) ! maximum infiltration rate (m s-1) !write(*,'(a,1x,f9.3,1x,10(e20.10,1x))') 'depthWettingFront, surfaceSatHydCond, hydCondWettingFront, xMaxInfilRate = ', depthWettingFront, surfaceSatHydCond, hydCondWettingFront, xMaxInfilRate - - ! define the infiltrating area for the non-frozen part of the cell/basin + fPart1 = hydCondWettingFront + fPart2 = ( (wettingFrontSuction + depthWettingFront)/depthWettingFront ) + dPart1(:) = surfaceSatHydCond*(zScale_TOPMODEL - 1._rkind) * ( (1._rkind - depthWettingFront/sum(mLayerDepth))**(zScale_TOPMODEL - 2._rkind) ) * (-dDepthWettingFront_dWat(:))/sum(mLayerDepth) + dPart2(:) = -wettingFrontSuction / (dDepthWettingFront_dWat(:)**2._rkind) + dXMaxInfilRate_dWat(:) = fPart1*dpart2(:) + fPart2*dPart1(:) + dPart1(:) = surfaceSatHydCond*(zScale_TOPMODEL - 1._rkind) * ( (1._rkind - depthWettingFront/sum(mLayerDepth))**(zScale_TOPMODEL - 2._rkind) ) * (-dDepthWettingFront_dTk(:))/sum(mLayerDepth) + dPart2(:) = -wettingFrontSuction / (dDepthWettingFront_dTk(:)**2._rkind) + dXMaxInfilRate_dTk(:) = fPart1*dpart2(:) + fPart2*dPart1(:) + + ! define the infiltrating area and derivatives for the non-frozen part of the cell/basin if(qSurfScale < qSurfScaleMax)then fracCap = rootZoneLiq/(maxFracCap*availCapacity) ! fraction of available root zone filled with water fInfRaw = 1._rkind - exp(-qSurfScale*(1._rkind - fracCap)) ! infiltrating area -- allowed to violate solution constraints scalarInfilArea = min(0.5_rkind*(fInfRaw + sqrt(fInfRaw**2._rkind + scaleFactor)), 1._rkind) ! infiltrating area -- constrained + if (0.5_rkind*(fInfRaw + sqrt(fInfRaw**2._rkind + scaleFactor))< 1._rkind) then + dfracCap(:) = ( dRootZoneLiq_dWat(:)/maxFracCap + dRootZoneIce_dWat(:)*fracCap )/availCapacity + dfInfRaw(:) = qSurfScale*dfracCap(:) * exp(-qSurfScale*(1._rkind - fracCap)) + dInfilArea_dWat(1:nSoil) = 0.5_rkind*dfInfRaw(:) * (1._rkind + fInfRaw/sqrt(fInfRaw**2._rkind + scaleFactor)) + dfracCap(:) = ( dRootZoneLiq_dTk(:)/maxFracCap + dRootZoneIce_dTk(:)*fracCap )/availCapacity + dfInfRaw(:) = qSurfScale*dfracCap(:) * exp(-qSurfScale*(1._rkind - fracCap)) + dInfilArea_dTk(1:nSoil) = 0.5_rkind*dfInfRaw(:) * (1._rkind + fInfRaw/sqrt(fInfRaw**2._rkind + scaleFactor)) + else ! scalarInfilArea = 1._rkind + dInfilArea_dWat(:) = 0._rkind + dInfilArea_dTk(:) = 0._rkind + endif else scalarInfilArea = 1._rkind + dInfilArea_dWat(:) = 0._rkind + dInfilArea_dTk(:) = 0._rkind endif + dInfilArea_dWat(0) = 0._rkind + dInfilArea_dTk(0) = 0._rkind ! check to ensure we are not infiltrating into a fully saturated column if(ixIce mpar_data%var(iLookPARAM%thCond_soil)%dat, & ! intent(in): thermal conductivity of soil (W m-1 K-1) theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): soil porosity (-) frac_sand => mpar_data%var(iLookPARAM%frac_sand)%dat, & ! intent(in): fraction of sand (-) - frac_silt => mpar_data%var(iLookPARAM%frac_silt)%dat, & ! intent(in): fraction of silt (-) frac_clay => mpar_data%var(iLookPARAM%frac_clay)%dat, & ! intent(in): fraction of clay (-) vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat, & ! intent(in): [dp(:)] van Genutchen "m" parameter (-) vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat, & ! intent(in): [dp(:)] van Genutchen "n" parameter (-) @@ -418,7 +412,6 @@ subroutine ssdNrgFlux(& vectoriden_soil(i) = iden_soil(mLayer_ind(i)-nSnow) vectorthCond_soil(i) = thCond_soil(mLayer_ind(i)-nSnow) vectorfrac_sand(i) = frac_sand(mLayer_ind(i)-nSnow) - vectorfrac_silt(i) = frac_silt(mLayer_ind(i)-nSnow) vectorfrac_clay(i) = frac_clay(mLayer_ind(i)-nSnow) else vectorvGn_alpha(i) = valueMissing @@ -429,7 +422,6 @@ subroutine ssdNrgFlux(& vectoriden_soil(i) = valueMissing vectorthCond_soil(i) = valueMissing vectorfrac_sand(i) = valueMissing - vectorfrac_silt(i) = valueMissing vectorfrac_clay(i) = valueMissing end if end do @@ -461,7 +453,6 @@ subroutine ssdNrgFlux(& ! input: coordinate variables nLayers, & ! intent(in): number of layers iLayer, & ! intent(in): layer index for output - nSnow, & ! intent(in): number of snow layers layerType(mLayer_ind), & ! intent(in): layer type (iname_soil or iname_snow) ! input: state variables (adjacent layers) vectorMatricHeadTrial, & ! intent(in): matric head at the nodes (m) @@ -479,7 +470,6 @@ subroutine ssdNrgFlux(& vectoriden_soil, & ! intent(in): intrinsic density of soil (kg m-3) vectorthCond_soil, & ! intent(in): thermal conductivity of soil (W m-1 K-1) vectorfrac_sand, & ! intent(in): fraction of sand (-) - vectorfrac_silt, & ! intent(in): fraction of silt (-) vectorfrac_clay, & ! intent(in): fraction of clay (-) ! input: snow parameters snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) @@ -622,7 +612,6 @@ subroutine iLayerThermalConduct(& ! input: coordinate variables nLayers, & ! intent(in): number of layers ixLayerDesired, & ! intent(in): layer index for output - nSnow, & ! intent(in): number of snow layers layerType, & ! intent(in): layer type (iname_soil or iname_snow) ! input: state variables (adjacent layers) nodeMatricHeadTrial0, & ! intent(in): matric head at the nodes (m) @@ -640,7 +629,6 @@ subroutine iLayerThermalConduct(& iden_soil, & !intrinsic density of soil (kg m-3) thCond_soil, & ! thermal conductivity of soil (W m-1 K-1) frac_sand, & ! intent(in): fraction of sand (-) - frac_silt, & ! fraction of silt (-) frac_clay, & ! fraction of clay (-) ! input: snow parameters snowfrz_scale, & ! scaling parameter for the snow freezing curve (K-1) @@ -680,7 +668,6 @@ subroutine iLayerThermalConduct(& ! input: coordinate variables integer(i4b),intent(in) :: nLayers ! intent(in): number of layers integer(i4b),intent(in) :: ixLayerDesired ! intent(in): layer index for output - integer(i4b),intent(in) :: nSnow ! intent(in): number of snow layers integer(i4b),intent(in) :: layerType(:) ! intent(in): layer type (iname_soil or iname_snow) ! input: state variables real(rkind),intent(in) :: nodeMatricHeadTrial0(:) ! trial vector of total water matric potential (m) @@ -698,7 +685,6 @@ subroutine iLayerThermalConduct(& real(rkind),intent(in) :: iden_soil(:) ! intrinsic density of soil (kg m-3) real(rkind),intent(in) :: thCond_soil(:) ! thermal conductivity of soil (W m-1 K-1) real(rkind),intent(in) :: frac_sand(:) ! intent(in): fraction of sand (-) - real(rkind),intent(in) :: frac_silt(:) ! fraction of silt (-) real(rkind),intent(in) :: frac_clay(:) ! fraction of clay (-) ! input: snow parameters real(rkind),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) @@ -944,16 +930,16 @@ subroutine iLayerThermalConduct(& dThermalC_dNrgStateBelow = 0._rkind else iLayerThermalC = mLayerThermalC(1) - dThermalC_dHydStateBelow = mLayerdThermalC_dWat(ixLower) !these are index 1 since was passed with 1:2 - dThermalC_dNrgStateBelow = mLayerdThermalC_dNrg(ixLower) !these are index 1 since was passed with 1:2 + dThermalC_dHydStateBelow = mLayerdThermalC_dWat(ixLower) !these are index 1 since was passed with 1:1 + dThermalC_dNrgStateBelow = mLayerdThermalC_dNrg(ixLower) !these are index 1 since was passed with 1:1 end if dThermalC_dHydStateAbove = valueMissing dThermalC_dNrgStateAbove = valueMissing else if (ixLayerDesired==nLayers ) then ! assume the thermal conductivity at the domain boundaries is equal to the thermal conductivity of the layer - iLayerThermalC = mLayerThermalC(nLayers) - dThermalC_dHydStateAbove = mLayerdThermalC_dWat(ixLower) !these are index 2 since was passed with iLayers-1:iLayers - dThermalC_dNrgStateAbove = mLayerdThermalC_dNrg(ixLower) !these are index 2 since was passed with iLayers-1:iLayers + iLayerThermalC = mLayerThermalC(1) + dThermalC_dHydStateAbove = mLayerdThermalC_dWat(ixLower) !these are index 2 since was passed with iLayers:iLayers + dThermalC_dNrgStateAbove = mLayerdThermalC_dNrg(ixLower) !these are index 2 since was passed with iLayers:iLayers dThermalC_dHydStateBelow = valueMissing dThermalC_dNrgStateBelow = valueMissing else From 2d0cd188759fa3ca3c266e77546938af4252948a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 16 Mar 2022 17:26:16 +0900 Subject: [PATCH 0249/1472] Fix snow energy bulk heat capacity derivatives. --- build/source/dshare/popMetadat.f90 | 2 +- build/source/engine/computFlux.f90 | 7 +- build/source/engine/snowLiqFlx.f90 | 6 +- build/source/engine/soilLiqFlx.f90 | 16 +- build/source/engine/solveByIDA.f90 | 21 -- build/source/engine/ssdNrgFlux.f90 | 527 +++++++++++++++-------------- 6 files changed, 283 insertions(+), 296 deletions(-) diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 6ab16a1fe..4f41dc04a 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -324,7 +324,7 @@ subroutine popMetadat(err,message) prog_meta(iLookPROG%scalarSurfaceTemp) = var_info('scalarSurfaceTemp' , 'surface temperature (just a copy of the upper-layer temperature)' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! define coordinate variables prog_meta(iLookPROG%mLayerDepth) = var_info('mLayerDepth' , 'depth of each layer' , 'm' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - prog_meta(iLookPROG%mLayerHeight) = var_info('mLayerHeight' , 'height of the layer mid-point (top of soil = 0)' , 'm' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + prog_meta(iLookPROG%mLayerHeight) = var_info('mLayerHeight' , 'height of the layer mid-point (top of soil = 0)' , 'm' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) prog_meta(iLookPROG%iLayerHeight) = var_info('iLayerHeight' , 'height of the layer interface (top of soil = 0)' , 'm' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) ! ----- diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index e8c117b74..be4fb2550 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -538,6 +538,11 @@ subroutine computFlux(& mLayerTempTrial, & ! intent(in): trial temperature at the current iteration (K) mLayerMatricHeadTrial, & ! intent(in): trial value for the total water matric potential in each soil layer (m) mLayerVolFracLiqTrial, & ! intent(in): trial volumetric fraction of liquid water at the current iteration(-) + ! input: pre-computed derivatives + mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) + mLayerdPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) ! input-output: data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model indices @@ -641,7 +646,7 @@ subroutine computFlux(& ! define forcing for the soil domain scalarRainPlusMelt = iLayerLiqFluxSnow(nSnow) ! drainage from the base of the snowpack - ! calculate net liquid water fluxes for each soil layer (s-1) + ! calculate net liquid water fluxes for each snow layer (s-1) do iLayer=1,nSnow mLayerLiqFluxSnow(iLayer) = -(iLayerLiqFluxSnow(iLayer) - iLayerLiqFluxSnow(iLayer-1))/mLayerDepth(iLayer) !write(*,'(a,1x,i4,1x,2(f16.10,1x))') 'iLayer, mLayerLiqFluxSnow(iLayer), iLayerLiqFluxSnow(iLayer-1) = ', & diff --git a/build/source/engine/snowLiqFlx.f90 b/build/source/engine/snowLiqFlx.f90 index 415aba608..6778561f8 100644 --- a/build/source/engine/snowLiqFlx.f90 +++ b/build/source/engine/snowLiqFlx.f90 @@ -160,7 +160,7 @@ subroutine snowLiqFlx(& ! define the liquid flux at the upper boundary (m s-1) iLayerLiqFluxSnow(0) = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water - iLayerLiqFluxSnowDeriv(0) = 0._rkind + iLayerLiqFluxSnowDeriv(0) = 0._rkind !computed inside computJacDAE_module ! compute properties fixed over the time step if(firstFluxCall)then @@ -198,8 +198,8 @@ subroutine snowLiqFlx(& end associate end subroutine snowLiqFlx - - + + ! ************************************************************************************************ ! public subroutine snowLiqFlxSundials: compute liquid water flux through the snowpack diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index ad59929cc..bb3c6c67a 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -485,7 +485,7 @@ subroutine soilLiqFlx(& ! get input state variables... ! ============================ ! identify the type of perturbation - ! NOTE, WE ALSO NEED TO PERTURB EACH LAYER BELOW IN TURN AND TEMPERATURE + ! Currently we are ignoring the perturbations in the non-surface layers and with temperature select case(itry) ! skip undesired perturbations @@ -1384,16 +1384,11 @@ subroutine surfaceFlx(& dVolFracIce_dWat(:) = 0._rkind dVolFracLiq_dTk(:) = 0._rkind dVolFracIce_dTk(:) = 0._rkind - if(nRoots > 0)then + if(deriv_desired .and. nRoots > 0)then select case(ixRichards) ! (form of Richards' equation) case(moisture) dVolFracLiq_dWat(:) = 1._rkind dVolFracIce_dWat(:) = mLayerdPsi_dTheta(:) - 1._rkind - do iLayer=1,nRoots - Tcrit = crit_soilT( mLayerMatricHead(iLayer) ) - if(mLayerTemp(iLayer) < Tcrit) dVolFracLiq_dTk(iLayer) = dTheta_dTk(iLayer) !dTheta_dTk(mLayerTempTrial(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_alpha(iLayer),vGn_n(iLayer),vGn_m(iLayer)) - if(mLayerTemp(iLayer) >=Tcrit) dVolFracLiq_dTk(iLayer) = 0._rkind - enddo case(mixdform) do iLayer=1,nRoots Tcrit = crit_soilT( mLayerMatricHead(iLayer) ) @@ -1404,12 +1399,11 @@ subroutine surfaceFlx(& dVolFracLiq_dWat(iLayer) = dTheta_dPsi(iLayer) dVolFracIce_dWat(iLayer) = 0._rkind endif - if(mLayerTemp(iLayer) < Tcrit) dVolFracLiq_dTk(iLayer) = dTheta_dTk(iLayer) - if(mLayerTemp(iLayer) >=Tcrit) dVolFracLiq_dTk(iLayer) = 0._rkind enddo - end select + end select ! (form of Richards' equation) + dVolFracLiq_dTk(:) = dTheta_dTk(:) !already zeroed out if not below critical temperature dVolFracIce_dTk(:) = -dVolFracLiq_dTk(:) !often can and will simplify one of these terms out - endif + endif ! define the storage in the root zone (m) and derivatives rootZoneLiq = 0._rkind diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index fe48541e4..dc8c0a6e2 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -330,8 +330,6 @@ subroutine solveByIDA( & startQuadrature = .true. - !do i = 1,2 - ! create serial vectors sunvec_y => FN_VMake_Serial(nState, stateVec) if (.not. associated(sunvec_y)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif @@ -342,7 +340,6 @@ subroutine solveByIDA( & ! Initialize solution vectors call setInitialCondition(nState, stateVecInit, sunvec_y, sunvec_yp) - ! Call FIDACreate and FIDAInit to initialize IDA memory ida_mem = FIDACreate() if (.not. c_associated(ida_mem)) then; err=20; message='solveByIDA: ida_mem = NULL'; return; endif @@ -393,7 +390,6 @@ subroutine solveByIDA( & if(ixMatrix == ixFullMatrix)then ! Set the user-supplied Jacobian routine - !if (i==2) retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) !commment this line out to use FD Jacobian if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetJacFn'; return; endif endif @@ -438,22 +434,6 @@ subroutine solveByIDA( & scalarSnowDepth = prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) scalarSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) - !tret(1) = t0 - ! eqns_data%firstFluxCall = .false. - ! eqns_data%firstSplitOper = .true. - ! retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) - ! if(i==1) then - ! print*, "Use Finite-Dif Jacobian" - ! call FIDAFree(ida_mem) - ! retval = FSUNNonlinSolFree(sunnonlin_NLS) - ! retval = FSUNLinSolFree(sunlinsol_LS) - ! call FSUNMatDestroy(sunmat_A) - ! call FN_VDestroy(sunvec_y) - ! call FN_VDestroy(sunvec_yp) - ! endif - ! if(i==2) print*, "Use Analytical Jacobian" - !enddo - !********************************************************************************** !****************************** Main Solver *************************************** !************************* loop on one_step mode ********************************** @@ -724,7 +704,6 @@ subroutine setInitialCondition(neq, y, sunvec_u, sunvec_up) uu(1:neq) => FN_VGetArrayPointer(sunvec_u) up(1:neq) => FN_VGetArrayPointer(sunvec_up) - uu = y up = 0._rkind diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index fa0de9754..be6552fd1 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -102,7 +102,6 @@ module ssdNrgFlux_module public::ssdNrgFlux ! global parameters real(rkind),parameter :: dx=1.e-10_rkind ! finite difference increment (K) -real(rkind),parameter :: valueMissing=-9999._rkind ! missing value parameter contains @@ -121,8 +120,13 @@ subroutine ssdNrgFlux(& iLayerLiqFluxSoil, & ! intent(in): liquid flux at the interface of each soil layer (m s-1) ! input: trial value of model state variables mLayerTempTrial, & ! intent(in): trial temperature at the current iteration (K) - mLayerMatricHeadTrial, & ! intent(in): trial matric head at the current iteration(m) - mLayerVolFracLiqTrial, & ! intent(in): trial volumetric fraction of liquid water at the current iteration(-) + mLayerMatricHeadTrial, & ! intent(in): trial matric head at the current iteration(m) + mLayerVolFracLiqTrial, & ! intent(in): trial volumetric fraction of liquid water at the current iteration(-) + ! input: pre-computed derivatives + mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) + mLayerdPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) ! input-output: data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model indices @@ -139,8 +143,10 @@ subroutine ssdNrgFlux(& err,message) ! intent(out): error control ! utility modules USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water - USE soil_utils_module,only:dTheta_dPsi ! derivative in the soil water characteristic (soil) - USE soil_utils_module,only:dTheta_dTk ! differentiate the freezing curve w.r.t. temperature (soil) + USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + USE snow_utils_module,only:fracliquid ! compute fraction of liquid water at a given temperature + ! constants USE multiconst, only: gravity, & ! gravitational acceleration (m s-1) Tfreeze, & ! freezing point of water (K) @@ -149,37 +155,42 @@ subroutine ssdNrgFlux(& ! ------------------------------------------------------------------------------------------------------------------------------------------------- implicit none ! input: model control - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - logical(lgt),intent(in) :: deriv_desired ! flag indicating if derivatives are desired + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + logical(lgt),intent(in) :: deriv_desired ! flag indicating if derivatives are desired ! input: fluxes and derivatives at the upper boundary - real(rkind),intent(in) :: groundNetFlux ! net energy flux for the ground surface (W m-2) - real(rkind),intent(inout) :: dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + real(rkind),intent(in) :: groundNetFlux ! net energy flux for the ground surface (W m-2) + real(rkind),intent(inout) :: dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) ! input: liquid water fluxes - real(rkind),intent(in) :: iLayerLiqFluxSnow(0:) ! intent(in): liquid flux at the interface of each snow layer (m s-1) - real(rkind),intent(in) :: iLayerLiqFluxSoil(0:) ! intent(in): liquid flux at the interface of each soil layer (m s-1) + real(rkind),intent(in) :: iLayerLiqFluxSnow(0:) ! liquid flux at the interface of each snow layer (m s-1) + real(rkind),intent(in) :: iLayerLiqFluxSoil(0:) ! liquid flux at the interface of each soil layer (m s-1) ! input: trial model state variables - real(rkind),intent(in) :: mLayerTempTrial(:) ! temperature in each layer at the current iteration (m) - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! matric head in each layer at the current iteration (m) - real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! volumetric fraction of liquid at the current iteration (-) + real(rkind),intent(in) :: mLayerTempTrial(:) ! temperature in each layer at the current iteration (m) + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! matric head in each layer at the current iteration (m) + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! volumetric fraction of liquid at the current iteration (-) +! input: pre-computed derivatives + real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative in volumetric liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) + real(rkind),intent(in) :: mLayerdTheta_dPsi(:) ! derivative in the soil water characteristic w.r.t. psi (m-1) + real(rkind),intent(in) :: mLayerdPsi_dTheta(:) ! derivative in the soil water characteristic w.r.t. theta (m) ! input-output: data structures - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! state vector geometry - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! state vector geometry + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU ! output: fluxes and derivatives at all layer interfaces - real(rkind),intent(out) :: iLayerNrgFlux(0:) ! energy flux at the layer interfaces (W m-2) - real(rkind),intent(out) :: dFlux_dTempAbove(0:) ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) - real(rkind),intent(out) :: dFlux_dTempBelow(0:) ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) - real(rkind),intent(out) :: dFlux_dWatAbove(0:) ! derivatives in the flux w.r.t. water state in the layer above (J m-2 s-1 K-1) - real(rkind),intent(out) :: dFlux_dWatBelow(0:) ! derivatives in the flux w.r.t. water state in the layer below (J m-2 s-1 K-1) + real(rkind),intent(out) :: iLayerNrgFlux(0:) ! energy flux at the layer interfaces (W m-2) + real(rkind),intent(out) :: dFlux_dTempAbove(0:) ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) + real(rkind),intent(out) :: dFlux_dTempBelow(0:) ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) + real(rkind),intent(out) :: dFlux_dWatAbove(0:) ! derivatives in the flux w.r.t. water state in the layer above (J m-2 s-1 K-1) + real(rkind),intent(out) :: dFlux_dWatBelow(0:) ! derivatives in the flux w.r.t. water state in the layer below (J m-2 s-1 K-1) ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! local variables character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b) :: i,iLayer ! index of model layers + integer(i4b) :: i,j,iLayer ! index of model layers integer(i4b) :: iSoil ! index of soil layer integer(i4b) :: ixLayerDesired(1) ! layer desired (scalar solution) integer(i4b) :: ixTop ! top layer in subroutine call @@ -204,16 +215,19 @@ subroutine ssdNrgFlux(& real(rkind) :: scalarThermCFlux_dWatBelow ! thermal conductivity with perturbation to the water state below real(rkind) :: flux0,flux1,flux2 ! fluxes used to calculate derivatives (W m-2) ! compute fluxes and derivatives at layer interfaces - integer(rkind),dimension(2) :: mLayer_ind ! indices of above and below layers - integer(rkind),dimension(2) :: iLayer_ind ! indices of above and below interfaces + integer(i4b),dimension(2) :: mLayer_ind ! indices of above and below layers + integer(i4b),dimension(2) :: iLayer_ind ! indices of above and below interfaces + real(rkind) :: matricFHead ! matric head for frozen soil + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind) :: fLiq ! fraction of liquid water (-) real(rkind),dimension(2) :: vectorMatricHeadTrial ! trial value of matric head (m) - real(rkind),dimension(2) :: vectorVolFracLiqTrial ! trial value of volumetric total water content (-) + real(rkind),dimension(2) :: vectorVolFracLiqTrial ! trial value of volumetric liquid content (-) + real(rkind),dimension(2) :: vectorVolFracIceTrial ! trial value of volumetric ice content (-) real(rkind),dimension(2) :: vectorTempTrial ! trial value of temperature (K) - real(rkind),dimension(2) :: vectorvGn_alpha ! layer above and below van Genutchen "alpha" parameter (m-1) - real(rkind),dimension(2) :: vectorvGn_n ! layer above and below van Genutchen "n" parameter (-) - real(rkind),dimension(2) :: vectorvGn_m ! layer above and below van Genutchen "m" parameter (-) + real(rkind),dimension(2) :: vectordTheta_dPsi ! derivative in the soil water characteristic w.r.t. psi (m-1) + real(rkind),dimension(2) :: vectordPsi_dTheta ! derivative in the soil water characteristic w.r.t. theta (m) + real(rkind),dimension(2) :: vectorFracLiqSnow ! fraction of liquid water (-) real(rkind),dimension(2) :: vectortheta_sat ! layer above and below soil porosity (-) - real(rkind),dimension(2) :: vectortheta_res ! layer above and below soil residual volumetric water content (-) real(rkind),dimension(2) :: vectoriden_soil ! layer above and below density of soil (kg m-3) real(rkind),dimension(2) :: vectorthCond_soil ! layer above and below thermal conductivity of soil (W m-1 K-1) real(rkind),dimension(2) :: vectorfrac_sand ! layer above and below fraction of sand (-) @@ -272,8 +286,8 @@ subroutine ssdNrgFlux(& ! set conductive and advective fluxes to missing in the upper boundary ! NOTE: advective flux at the upper boundary is included in the ground heat flux - iLayerConductiveFlux(0) = valueMissing - iLayerAdvectiveFlux(0) = valueMissing + iLayerConductiveFlux(0) = realMissing + iLayerAdvectiveFlux(0) = realMissing ! check the need to compute numerical derivatives if(ixDerivMethod==numerical)then @@ -401,30 +415,6 @@ subroutine ssdNrgFlux(& ! indices of interface are different at top layer since interface 0 exists iLayer_ind = mLayer_ind if (iLayer==0 ) iLayer_ind(1) = 0 - ! soil parameters if layer is soil - do i = 1,2 - if (mLayer_ind(i)>nSnow) then - vectorvGn_alpha(i) = vGn_alpha(mLayer_ind(i)-nSnow) - vectorvGn_n(i) = vGn_n(mLayer_ind(i)-nSnow) - vectorvGn_m(i) = vGn_m(mLayer_ind(i)-nSnow) - vectortheta_sat(i) = theta_sat(mLayer_ind(i)-nSnow) - vectortheta_res(i) = theta_res(mLayer_ind(i)-nSnow) - vectoriden_soil(i) = iden_soil(mLayer_ind(i)-nSnow) - vectorthCond_soil(i) = thCond_soil(mLayer_ind(i)-nSnow) - vectorfrac_sand(i) = frac_sand(mLayer_ind(i)-nSnow) - vectorfrac_clay(i) = frac_clay(mLayer_ind(i)-nSnow) - else - vectorvGn_alpha(i) = valueMissing - vectorvGn_n(i) = valueMissing - vectorvGn_m(i) = valueMissing - vectortheta_sat(i) = valueMissing - vectortheta_res(i) = valueMissing - vectoriden_soil(i) = valueMissing - vectorthCond_soil(i) = valueMissing - vectorfrac_sand(i) = valueMissing - vectorfrac_clay(i) = valueMissing - end if - end do ! ===== ! get input state variables... @@ -441,12 +431,70 @@ subroutine ssdNrgFlux(& vectorTempTrial(ixPerturb) = vectorTempTrial(ixPerturb) + dx endif + ! ***** + ! * compute the volumetric fraction of liquid, ice, and air in each layer in response to perturbation ... + ! ******************************************************************************************************* + + do i = 1,2 !(layer above and below) + select case(layerType(mLayer_ind(i))) !(snow or soil) + + case(iname_soil) + j = mLayer_ind(i)-nSnow !soil layer + select case(ixRichards) ! (form of Richards' equation) + case(moisture) ! + vectorMatricHeadTrial(i) = matricHead(vectorVolFracLiqTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) + Tcrit = crit_soilT(vectorMatricHeadTrial(i)) + !if change temp and below critical, it changes the state variable, seems like a problem FIX + if(vectorTempTrial(i) < Tcrit) then !if do not perturb temperature, this should not change + matricFHead = (LH_fus/gravity)*(vectorTempTrial(i) - Tfreeze)/Tfreeze + vectorVolFracLiqTrial(i) = volFracLiq(matricFHead,vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) + endif + case(mixdform) + Tcrit = crit_soilT(vectorMatricHeadTrial(i)) + if(vectorTempTrial(i) < Tcrit) then !if do not perturb temperature, this should not change, but matricHeadTrial will have changed + matricFHead = (LH_fus/gravity)*(vectorTempTrial(i) - Tfreeze)/Tfreeze + vectorVolFracLiqTrial(i) = volFracLiq(matricFHead,vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) + else + vectorVolFracLiqTrial(i) = volFracLiq(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) + endif + end select ! (form of Richards' equation) + vectorVolFracIceTrial(i) = volFracLiq(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - vectorVolFracLiqTrial(i) + ! derivatives + vectordPsi_dTheta(i) = mLayerdPsi_dTheta(j) + vectordTheta_dPsi(i) = mLayerdTheta_dPsi(j) + vectorFracLiqSnow(i) = realMissing + ! soil parameters + vectortheta_sat(i) = theta_sat(j) + vectoriden_soil(i) = iden_soil(j) + vectorthCond_soil(i) = thCond_soil(j) + vectorfrac_sand(i) = frac_sand(j) + vectorfrac_clay(i) = frac_clay(j) + + case(iname_snow) + fLiq = fracliquid(vectorTempTrial(i),snowfrz_scale) ! fraction of liquid water + vectorVolFracIceTrial(i) = ( vectorVolFracLiqTrial(i) / fLiq - vectorVolFracLiqTrial(i) )*(iden_water/iden_ice) ! use potentially perturbed nodeVolTotWatTrial + ! derivatives + vectordPsi_dTheta(i) = realMissing + vectordTheta_dPsi(i) = realMissing + vectorFracLiqSnow(i) = mLayerFracLiqSnow(mLayer_ind(i)) + ! soil parameters do not exist + vectortheta_sat(i) = realMissing + vectoriden_soil(i) = realMissing + vectorthCond_soil(i) = realMissing + vectorfrac_sand(i) = realMissing + vectorfrac_clay(i) = realMissing + + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute volumetric fraction of air'; return + + end select !(snow or soil) + enddo !(layer above and below) + ! ===== ! get thermal conductivity at layer interface and its derivative w.r.t. the state above and the state below... ! ============================================================================================================ call iLayerThermalConduct(& ! input: model control - valueMissing, & ! intent(in): missing value + deriv_desired, & ! intent(in): flag indicating if derivatives are desired ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) ixThCondSnow, & ! intent(in): choice of method for thermal conductivity of snow ixThCondSoil, & ! intent(in): choice of method for thermal conductivity of soil @@ -456,23 +504,24 @@ subroutine ssdNrgFlux(& layerType(mLayer_ind), & ! intent(in): layer type (iname_soil or iname_snow) ! input: state variables (adjacent layers) vectorMatricHeadTrial, & ! intent(in): matric head at the nodes (m) - vectorVolFracLiqTrial, & ! intent(in): volumetric total liquid water at the nodes (m) + vectorVolFracLiqTrial, & ! intent(in): volumetric liquid water at the nodes (m) + vectorVolFracIceTrial, & ! intent(in): volumetric ice at the nodes (m) vectorTempTrial, & ! intent(in): temperature at the nodes (m) + ! input: pre-computed derivatives + mLayerdTheta_dTk(mLayer_ind), & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + vectorFracLiqSnow, & ! intent(in): fraction of liquid water (-) + vectordTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) + vectordPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) ! input: model coordinate variables (adjacent layers) mLayerHeight(mLayer_ind), & ! intent(in): height at the mid-point of the node (m) iLayerHeight(iLayer_ind), & ! intent(in): height at the interface of the nodes (m) ! input: soil parameters - vectorvGn_alpha, & ! intent(in): van Genutchen "alpha" parameter (m-1) - vectorvGn_n, & ! intent(in): van Genutchen "n" parameter (-) - vectorvGn_m, & ! intent(in): van Genutchen "m" parameter (-) vectortheta_sat, & ! intent(in): soil porosity (-) - vectortheta_res, & ! intent(in): soil residual volumetric water content (-) vectoriden_soil, & ! intent(in): intrinsic density of soil (kg m-3) vectorthCond_soil, & ! intent(in): thermal conductivity of soil (W m-1 K-1) vectorfrac_sand, & ! intent(in): fraction of sand (-) vectorfrac_clay, & ! intent(in): fraction of clay (-) ! input: snow parameters - snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) fixedThermalCond_snow, & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) ! output: conductivity at the layer interface (scalars) scalariLayerThermalC, & ! intent(out): thermal conductivity at the interface of each layer (W m-1 K-1) @@ -605,7 +654,7 @@ end subroutine ssdNrgFlux ! ************************************************************************************************************************************* subroutine iLayerThermalConduct(& ! input: model control - valueMissing, & ! intent(in): missing value + deriv_desired, & ! intent(in): flag indicating if derivatives are desired ixRichards, & ! intent(in): choice of option for Richards' equation ixThCondSnow, & ! intent(in): choice of method for thermal conductivity of snow ixThCondSoil, & ! intent(in): choice of method for thermal conductivity of soil @@ -614,83 +663,79 @@ subroutine iLayerThermalConduct(& ixLayerDesired, & ! intent(in): layer index for output layerType, & ! intent(in): layer type (iname_soil or iname_snow) ! input: state variables (adjacent layers) - nodeMatricHeadTrial0, & ! intent(in): matric head at the nodes (m) - nodeVolFracLiqTrial0, & ! intent(inout): volumetric liquid water content at the nodes (m) - nodeTempTrial, & ! intent(in): temperature at the nodes (m) + nodeMatricHead, & ! intent(in): matric head at the nodes (m) + nodeVolFracLiq, & ! intent(in): volumetric liquid water content at the nodes (m) + nodeVolFracIce, & ! intent(in): volumetric ice at the nodes (m) + nodeTemp, & ! intent(in): temperature at the nodes (m) + ! input: pre-computed derivatives + dTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + fracLiqSnow, & ! intent(in) : fraction of liquid water (-) + dTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) + dPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) ! input: model coordinate variables (adjacent layers) nodeHeight, & ! intent(in): height at the mid-point of the node (m) - node_iHeight, & ! intent(in): height at the interface of the nodes (m) + node_iHeight, & ! intent(in): height at the interface of the nodes (m) ! input: soil parameters at nodes - vGn_alpha, & ! intent(in): van Genutchen "alpha" parameter (m-1) - vGn_n, & ! intent(in): van Genutchen "n" parameter (-) - vGn_m, & ! intent(in): van Genutchen "m" parameter (-) theta_sat, & ! intent(in): soil porosity (-) - theta_res, & ! intent(in): soil residual volumetric water content (-) iden_soil, & !intrinsic density of soil (kg m-3) thCond_soil, & ! thermal conductivity of soil (W m-1 K-1) frac_sand, & ! intent(in): fraction of sand (-) frac_clay, & ! fraction of clay (-) ! input: snow parameters - snowfrz_scale, & ! scaling parameter for the snow freezing curve (K-1) fixedThermalCond_snow, & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) ! output: conductivity at the layer interface (scalars) iLayerThermalC, & ! thermal conductivity at the interface of each layer (W m-1 K-1) ! output: derivatives in thermal conductivity w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below - dThermalC_dHydStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dHydStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dHydStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dHydStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above ! output: derivatives in thermal conductivity w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (W m-1 K-2) - dThermalC_dNrgStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dNrgStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dNrgStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dNrgStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above ! output: error control - err,message) ! intent(out): error control + err,message) ! intent(out): error control + USE snow_utils_module,only:tcond_snow ! compute thermal conductivity of snow - USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) - USE snow_utils_module,only:fracliquid ! compute fraction of liquid water at a given temperature USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water - USE soil_utils_module,only:dTheta_dPsi ! derivative in the soil water characteristic (soil) - USE soil_utils_module,only:dPsi_dTheta ! derivative in the soil water characteristic (soil) - USE soil_utils_module,only:dTheta_dTk ! differentiate the freezing curve w.r.t. temperature (soil) - USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content ! constants USE multiconst, only: gravity, & ! gravitational acceleration (m s-1) - Tfreeze, & ! freezing point of water (K) - iden_water,iden_ice,& ! intrinsic density of water and ice (kg m-3) - LH_fus ! latent heat of fusion (J kg-1) + Tfreeze, & ! freezing point of water (K) + iden_water,iden_ice,& ! intrinsic density of water and ice (kg m-3) + LH_fus ! latent heat of fusion (J kg-1) implicit none ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: model control - real(rkind),intent(in) :: valueMissing ! intent(in): missing value - integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation - integer(i4b),intent(in) :: ixThCondSnow ! intent(in): choice of method for thermal conductivity of snow - integer(i4b),intent(in) :: ixThCondSoil ! intent(in): choice of method for thermal conductivity of soil + logical(lgt),intent(in) :: deriv_desired ! flag to indicate if derivatives are desired + integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation + integer(i4b),intent(in) :: ixThCondSnow ! choice of method for thermal conductivity of snow + integer(i4b),intent(in) :: ixThCondSoil ! choice of method for thermal conductivity of soil ! input: coordinate variables - integer(i4b),intent(in) :: nLayers ! intent(in): number of layers - integer(i4b),intent(in) :: ixLayerDesired ! intent(in): layer index for output - integer(i4b),intent(in) :: layerType(:) ! intent(in): layer type (iname_soil or iname_snow) + integer(i4b),intent(in) :: nLayers ! number of layers + integer(i4b),intent(in) :: ixLayerDesired ! layer index for output + integer(i4b),intent(in) :: layerType(:) ! layer type (iname_soil or iname_snow) ! input: state variables - real(rkind),intent(in) :: nodeMatricHeadTrial0(:) ! trial vector of total water matric potential (m) - real(rkind),intent(in) :: nodeVolFracLiqTrial0(:) ! trial vector of volumetric liquid water content, recomputed with perturbed water state(-) - real(rkind),intent(in) :: nodeTempTrial(:) ! trial vector of temperature (K) + real(rkind),intent(in) :: nodeMatricHead(:) ! trial vector of total water matric potential (m) + real(rkind),intent(in) :: nodeVolFracLiq(:) ! trial vector of volumetric liquid water content, recomputed with perturbed water state(-) + real(rkind),intent(in) :: nodeVolFracIce(:) ! trial vector of ice content, recomputed with perturbed water state(-) + real(rkind),intent(in) :: nodeTemp(:) ! trial vector of temperature (K) + ! input: pre-computed derivatives + real(rkind),intent(in) :: dTheta_dTk(:) ! derivative in volumetric liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: fracLiqSnow(:) ! fraction of liquid water (-) + real(rkind),intent(in) :: dTheta_dPsi(:) ! derivative in the soil water characteristic w.r.t. psi (m-1) + real(rkind),intent(in) :: dPsi_dTheta(:) ! derivative in the soil water characteristic w.r.t. theta (m) ! input: model coordinate variables real(rkind),intent(in) :: nodeHeight(:) ! height at the mid-point of the lower node (m) - real(rkind),intent(in) :: node_iHeight(:) ! height at the interface of each node (m) + real(rkind),intent(in) :: node_iHeight(:) ! height at the interface of each node (m) ! input: soil parameters - real(rkind),intent(in) :: vGn_alpha(:) ! van Genutchen "alpha" parameter (m-1) - real(rkind),intent(in) :: vGn_n(:) ! van Genutchen "n" parameter (-) - real(rkind),intent(in) :: vGn_m(:) ! van Genutchen "m" parameter (-) - real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) - real(rkind),intent(in) :: theta_res(:) ! soil residual volumetric water content (-) - real(rkind),intent(in) :: iden_soil(:) ! intrinsic density of soil (kg m-3) - real(rkind),intent(in) :: thCond_soil(:) ! thermal conductivity of soil (W m-1 K-1) - real(rkind),intent(in) :: frac_sand(:) ! intent(in): fraction of sand (-) - real(rkind),intent(in) :: frac_clay(:) ! fraction of clay (-) + real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) + real(rkind),intent(in) :: iden_soil(:) ! intrinsic density of soil (kg m-3) + real(rkind),intent(in) :: thCond_soil(:) ! thermal conductivity of soil (W m-1 K-1) + real(rkind),intent(in) :: frac_sand(:) ! intent(in): fraction of sand (-) + real(rkind),intent(in) :: frac_clay(:) ! fraction of clay (-) ! input: snow parameters - real(rkind),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) - real(rkind),intent(in) :: fixedThermalCond_snow ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) + real(rkind),intent(in) :: fixedThermalCond_snow ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) ! output: thermal conductivity at layer interfaces - real(rkind),intent(out) :: iLayerThermalC ! thermal conductivity at the interface of each layer (W m-1 K-1) + real(rkind),intent(out) :: iLayerThermalC ! thermal conductivity at the interface of each layer (W m-1 K-1) ! output: thermal conductivity derivatives at all layer interfaces real(rkind),intent(out) :: dThermalC_dHydStateAbove ! derivatives in the thermal conductivity w.r.t. matric head or volumetric liquid water in the layer above real(rkind),intent(out) :: dThermalC_dHydStateBelow ! derivatives in the thermal conductivity w.r.t. matric head or volumetric liquid water in the layer below @@ -701,49 +746,42 @@ subroutine iLayerThermalConduct(& character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables (named variables to provide index of 2-element vectors) - integer(i4b),parameter :: ixUpper=1 ! index of upper node in the 2-element vectors - integer(i4b),parameter :: ixLower=2 ! index of lower node in the 2-element vectors - character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b) :: iLayer ! index of model layer - real(rkind) :: matricFHead ! matric head for frozen soil - real(rkind) :: TCn ! thermal conductivity below the layer interface (W m-1 K-1) - real(rkind) :: TCp ! thermal conductivity above the layer interface (W m-1 K-1) - real(rkind) :: zdn ! height difference between interface and lower value (m) - real(rkind) :: zdp ! height difference between interface and upper value (m) - real(rkind) :: bulkden_soil ! bulk density of soil (kg m-3) - real(rkind) :: lambda_drysoil ! thermal conductivity of dry soil (W m-1) - real(rkind) :: lambda_wetsoil ! thermal conductivity of wet soil (W m-1) - real(rkind) :: lambda_wet ! thermal conductivity of the wet material - real(rkind) :: relativeSat ! relative saturation (-) - real(rkind) :: kerstenNum ! the Kersten number (-), defining weight applied to conductivity of the wet medium - real(rkind) :: den ! denominator in the thermal conductivity calculations - real(rkind) :: mLayerdThermalC_dWat(2) ! derivative in thermal conductivity w.r.t. matric head or volumetric liquid water - real(rkind) :: mLayerdThermalC_dNrg(2) ! derivative in thermal conductivity w.r.t. temperature - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - real(rkind) :: fLiq ! fraction of liquid water (-) - real(rkind) :: dlambda_wet_dWat ! derivative in thermal conductivity of wet material w.r.t.soil water state variable - real(rkind) :: dlambda_wet_dTk ! derivative in thermal conductivity of wet material w.r.t. temperature - real(rkind) :: dkerstenNum_dWat ! derivative in Kersten number w.r.t. soil water state variable - real(rkind) :: nodeMatricHeadTrial(2) ! trial vector of matric head, recomputed from input if perturbed water state (-) - real(rkind) :: nodeVolTotWatTrial(2) ! trial vector of volumetric total water content (-) - real(rkind) :: nodeVolFracLiqTrial(2) ! trial vector of volumetric liquid water content, recomputed from input if perturbed water state (-) - real(rkind) :: nodeVolFracIceTrial(2) ! trial vector of volumetric ice water content (-) - real(rkind) :: nodeVolFracAirTrial(2) ! trial vector of volumetric air water content (-) - real(rkind) :: mLayerThermalC(2) ! thermal conductivity of each layer (W m-1 K-1) - real(rkind) :: mLayerdVolFracLiq_dWat ! derivative in vol fraction of liquid w.r.t. water state variable - real(rkind) :: mLayerdVolFracIce_dWat ! derivative in vol fraction of ice w.r.t. water state variable - real(rkind) :: mLayerdVolFracLiq_dTk ! derivative in vol fraction of liquid w.r.t. temperature - real(rkind) :: mLayerdVolFracIce_dTk ! derivative in vol fraction of ice w.r.t. temperature + integer(i4b),parameter :: ixUpper=1 ! index of upper node in the 2-element vectors + integer(i4b),parameter :: ixLower=2 ! index of lower node in the 2-element vectors + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iLayer ! index of model layer + real(rkind) :: TCn ! thermal conductivity below the layer interface (W m-1 K-1) + real(rkind) :: TCp ! thermal conductivity above the layer interface (W m-1 K-1) + real(rkind) :: zdn ! height difference between interface and lower value (m) + real(rkind) :: zdp ! height difference between interface and upper value (m) + real(rkind) :: bulkden_soil ! bulk density of soil (kg m-3) + real(rkind) :: lambda_drysoil ! thermal conductivity of dry soil (W m-1) + real(rkind) :: lambda_wetsoil ! thermal conductivity of wet soil (W m-1) + real(rkind) :: lambda_wet ! thermal conductivity of the wet material + real(rkind) :: relativeSat ! relative saturation (-) + real(rkind) :: kerstenNum ! the Kersten number (-), defining weight applied to conductivity of the wet medium + real(rkind) :: den ! denominator in the thermal conductivity calculations + real(rkind) :: dThermalC_dWat(2) ! derivative in thermal conductivity w.r.t. matric head or volumetric liquid water + real(rkind) :: dThermalC_dNrg(2) ! derivative in thermal conductivity w.r.t. temperature + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind) :: dlambda_wet_dWat ! derivative in thermal conductivity of wet material w.r.t.soil water state variable + real(rkind) :: dlambda_wet_dTk ! derivative in thermal conductivity of wet material w.r.t. temperature + real(rkind) :: dkerstenNum_dWat ! derivative in Kersten number w.r.t. soil water state variable + real(rkind) :: mLayerThermalC(2) ! thermal conductivity of each layer (W m-1 K-1) + real(rkind) :: dVolFracLiq_dWat ! derivative in vol fraction of liquid w.r.t. water state variable + real(rkind) :: dVolFracIce_dWat ! derivative in vol fraction of ice w.r.t. water state variable + real(rkind) :: dVolFracLiq_dTk ! derivative in vol fraction of liquid w.r.t. temperature + real(rkind) :: dVolFracIce_dTk ! derivative in vol fraction of ice w.r.t. temperature ! local variables to reproduce the thermal conductivity of Hansson et al. VZJ 2005 - real(rkind),parameter :: c1=0.55_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c2=0.8_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c3=3.07_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: c4=0.13_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c5=4._rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: f1=13.05_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: f2=1.06_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind) :: fArg,xArg ! temporary variables (see Hansson et al. VZJ 2005 for details) - real(rkind) :: dxArg_dWat,dxArg_dTk ! derivates of the temporary variables with respect to soil water state variable and temperature + real(rkind),parameter :: c1=0.55_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c2=0.8_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c3=3.07_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: c4=0.13_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c5=4._rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: f1=13.05_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: f2=1.06_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind) :: fArg,xArg ! temporary variables (see Hansson et al. VZJ 2005 for details) + real(rkind) :: dxArg_dWat,dxArg_dTk ! derivates of the temporary variables with respect to soil water state variable and temperature ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -760,69 +798,40 @@ subroutine iLayerThermalConduct(& lambda_wetsoil = (8.80_rkind*frac_sand(iLayer) + 2.92_rkind*frac_clay(iLayer)) / (frac_sand(iLayer) + frac_clay(iLayer)) end if - ! ***** - ! * compute the volumetric fraction of liquid, ice, and air in each layer in response to perturbation ... - ! ******************************************************************************************************* - select case(layerType(iLayer)) - case(iname_soil) - select case(ixRichards) ! (form of Richards' equation) - case(moisture) ! - nodeVolFracLiqTrial(iLayer) = nodeVolFracLiqTrial0(iLayer) - nodeMatricHeadTrial(iLayer) = matricHead(nodeVolFracLiqTrial0(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) - Tcrit = crit_soilT(nodeMatricHeadTrial(iLayer)) - !if change temp and below critical, it changes the state variable, seems like a problem FIX - if( nodeTempTrial(iLayer) < Tcrit) then !if do not perturb temperature, this should not change - err=20; message=trim(message)//'temperature derivatives of moisture-based form of Richards eqn have problems'; return - matricFHead = (LH_fus/gravity)*(nodeTempTrial(iLayer) - Tfreeze)/Tfreeze - nodeVolFracLiqTrial(iLayer) = volFracLiq(matricFHead,vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) - endif - case(mixdform) - nodeMatricHeadTrial(iLayer) = nodeMatricHeadTrial0(iLayer) - Tcrit = crit_soilT(nodeMatricHeadTrial(iLayer)) - if( nodeTempTrial(iLayer) < Tcrit) then !if do not perturb temperature, this should not change, but nodeMatricHeadTrial will have changed - matricFHead = (LH_fus/gravity)*(nodeTempTrial(iLayer) - Tfreeze)/Tfreeze - nodeVolFracLiqTrial(iLayer) = volFracLiq(matricFHead,vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) - else - nodeVolFracLiqTrial(iLayer) = volFracLiq(nodeMatricHeadTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) - endif - end select - nodeVolFracAirTrial(iLayer) = theta_sat(iLayer) - volFracLiq(nodeMatricHeadTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) - nodeVolFracIceTrial(iLayer) = theta_sat(iLayer) - (nodeVolFracAirTrial(iLayer) + nodeVolFracLiqTrial(iLayer)) - case(iname_snow) - fLiq = fracliquid(nodeTempTrial(iLayer),snowfrz_scale) ! fraction of liquid water - nodeVolTotWatTrial(iLayer) = nodeVolFracLiqTrial0(iLayer) / fLiq ! use potentially perturbed nodeVolTotWatTrial - nodeVolFracLiqTrial(iLayer) = nodeVolFracLiqTrial0(iLayer) - nodeVolFracIceTrial(iLayer) = (nodeVolTotWatTrial(iLayer) - nodeVolFracLiqTrial(iLayer))*(iden_water/iden_ice) - nodeVolFracAirTrial(iLayer) = 1._rkind - (nodeVolFracIceTrial(iLayer) + nodeVolFracLiqTrial(iLayer)) - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute volumetric fraction of air'; return - end select - ! ***** ! * compute the thermal conductivity of snow and soil and derivates at the mid-point of each layer... ! *************************************************************************************************** - mLayerdThermalC_dWat(iLayer) = 0._rkind - mLayerdThermalC_dNrg(iLayer) = 0._rkind + dThermalC_dWat(iLayer) = 0._rkind + dThermalC_dNrg(iLayer) = 0._rkind select case(layerType(iLayer)) ! ***** soil case(iname_soil) - select case(ixRichards) ! (form of Richards' equation) - case(moisture) - mLayerdVolFracLiq_dWat = 1._rkind - mLayerdVolFracIce_dWat = dPsi_dTheta(nodeVolFracLiqTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) - 1._rkind - case(mixdform) - if(nodeTempTrial(iLayer) < Tcrit) then - mLayerdVolFracLiq_dWat = 0._rkind - mLayerdVolFracIce_dWat = dTheta_dPsi(nodeMatricHeadTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) - else - mLayerdVolFracLiq_dWat = dTheta_dPsi(nodeMatricHeadTrial(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) - mLayerdVolFracIce_dWat = 0._rkind - endif - end select - if(nodeTempTrial(iLayer) < Tcrit) mLayerdVolFracLiq_dTk = dTheta_dTk(nodeTempTrial(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_alpha(iLayer),vGn_n(iLayer),vGn_m(iLayer)) - if(nodeTempTrial(iLayer) >=Tcrit) mLayerdVolFracLiq_dTk = 0._rkind - mLayerdVolFracIce_dTk = -mLayerdVolFracLiq_dTk !often can and will simplify one of these terms out + + ! (process derivatives) + dVolFracLiq_dWat = 0._rkind + dVolFracIce_dWat = 0._rkind + dVolFracLiq_dTk = 0._rkind + dVolFracIce_dTk = 0._rkind + if(deriv_desired)then + select case(ixRichards) ! (form of Richards' equation) + case(moisture) + dVolFracLiq_dWat = 1._rkind + dVolFracIce_dWat = dPsi_dTheta(iLayer) - 1._rkind + case(mixdform) + Tcrit = crit_soilT(nodeMatricHead(iLayer) ) + if(nodeTemp(iLayer) < Tcrit) then + dVolFracLiq_dWat = 0._rkind + dVolFracIce_dWat = dTheta_dPsi(iLayer) + else + dVolFracLiq_dWat = dTheta_dPsi(iLayer) + dVolFracIce_dWat = 0._rkind + endif + end select + dVolFracLiq_dTk = dTheta_dTk(iLayer) !already zeroed out if not below critical temperature + dVolFracIce_dTk = -dVolFracLiq_dTk !often can and will simplify one of these terms out + endif ! select option for thermal conductivity of soil select case(ixThCondSoil) @@ -831,16 +840,16 @@ subroutine iLayerThermalConduct(& case(funcSoilWet) ! compute the thermal conductivity of the wet material (W m-1) - lambda_wet = lambda_wetsoil**( 1._rkind - theta_sat(iLayer) ) * lambda_water**theta_sat(iLayer) * lambda_ice**(theta_sat(iLayer) - nodeVolFracLiqTrial(iLayer)) - dlambda_wet_dWat = -lambda_wet * log(lambda_ice) * mLayerdVolFracLiq_dWat - dlambda_wet_dTk = -lambda_wet * log(lambda_ice) * mLayerdVolFracLiq_dTk + lambda_wet = lambda_wetsoil**( 1._rkind - theta_sat(iLayer) ) * lambda_water**theta_sat(iLayer) * lambda_ice**(theta_sat(iLayer) - nodeVolFracLiq(iLayer)) + dlambda_wet_dWat = -lambda_wet * log(lambda_ice) * dVolFracLiq_dWat + dlambda_wet_dTk = -lambda_wet * log(lambda_ice) * dVolFracLiq_dTk - relativeSat = (nodeVolFracIceTrial(iLayer) + nodeVolFracLiqTrial(iLayer))/theta_sat(iLayer) ! relative saturation + relativeSat = (nodeVolFracIce(iLayer) + nodeVolFracLiq(iLayer))/theta_sat(iLayer) ! relative saturation ! drelativeSat_dWat = dPsi0_dWat, and drelativeSat_dTk = 0 (so dkerstenNum_dTk = 0) ! compute the Kersten number (-) if(relativeSat > 0.1_rkind)then ! log10(0.1) = -1 kerstenNum = log10(relativeSat) + 1._rkind - dkerstenNum_dWat = (mLayerdVolFracIce_dWat + mLayerdVolFracLiq_dWat) / ( theta_sat(iLayer) * relativeSat * log(10._rkind) ) + dkerstenNum_dWat = (dVolFracIce_dWat + dVolFracLiq_dWat) / ( theta_sat(iLayer) * relativeSat * log(10._rkind) ) else kerstenNum = 0._rkind ! dry thermal conductivity dkerstenNum_dWat = 0._rkind @@ -849,31 +858,31 @@ subroutine iLayerThermalConduct(& mLayerThermalC(iLayer) = kerstenNum*lambda_wet + (1._rkind - kerstenNum)*lambda_drysoil ! compute derivatives - mLayerdThermalC_dWat(iLayer) = dkerstenNum_dWat * ( lambda_wet - lambda_drysoil ) + kerstenNum*dlambda_wet_dWat - mLayerdThermalC_dNrg(iLayer) = kerstenNum*dlambda_wet_dTk + dThermalC_dWat(iLayer) = dkerstenNum_dWat * ( lambda_wet - lambda_drysoil ) + kerstenNum*dlambda_wet_dWat + dThermalC_dNrg(iLayer) = kerstenNum*dlambda_wet_dTk ! ** mixture of constituents case(mixConstit) mLayerThermalC(iLayer) = thCond_soil(iLayer) * ( 1._rkind - theta_sat(iLayer) ) + & ! soil component - lambda_ice * nodeVolFracIceTrial(iLayer) + & ! ice component - lambda_water * nodeVolFracLiqTrial(iLayer) + & ! liquid water component - lambda_air * nodeVolFracAirTrial(iLayer) ! air component + lambda_ice * nodeVolFracIce(iLayer) + & ! ice component + lambda_water * nodeVolFracLiq(iLayer) + & ! liquid water component + lambda_air * ( theta_sat(iLayer) - (nodeVolFracIce(iLayer) + nodeVolFracLiq(iLayer)) ) ! air component ! compute derivatives - mLayerdThermalC_dWat(iLayer) = lambda_ice*mLayerdVolFracIce_dWat + lambda_water*mLayerdVolFracLiq_dWat + lambda_air*(-mLayerdVolFracIce_dWat - mLayerdVolFracLiq_dWat) - mLayerdThermalC_dNrg(iLayer) = (lambda_ice - lambda_water) * mLayerdVolFracIce_dTk + dThermalC_dWat(iLayer) = lambda_ice*dVolFracIce_dWat + lambda_water*dVolFracLiq_dWat + lambda_air*(-dVolFracIce_dWat - dVolFracLiq_dWat) + dThermalC_dNrg(iLayer) = (lambda_ice - lambda_water) * dVolFracIce_dTk ! ** test case for the mizoguchi lab experiment, Hansson et al. VZJ 2004 case(hanssonVZJ) - fArg = 1._rkind + f1*nodeVolFracIceTrial(iLayer)**f2 - xArg = nodeVolFracLiqTrial(iLayer) + fArg*nodeVolFracIceTrial(iLayer) - dxArg_dWat = mLayerdVolFracLiq_dWat + mLayerdVolFracIce_dWat * (1._rkind + f1*(f2+1)*nodeVolFracIceTrial(iLayer)**f2) - dxArg_dTk = mLayerdVolFracIce_dTk * f1*(f2+1)*nodeVolFracIceTrial(iLayer)**f2 + fArg = 1._rkind + f1*nodeVolFracIce(iLayer)**f2 + xArg = nodeVolFracLiq(iLayer) + fArg*nodeVolFracIce(iLayer) + dxArg_dWat = dVolFracLiq_dWat + dVolFracIce_dWat * (1._rkind + f1*(f2+1)*nodeVolFracIce(iLayer)**f2) + dxArg_dTk = dVolFracIce_dTk * f1*(f2+1)*nodeVolFracIce(iLayer)**f2 ! ...and, compute the thermal conductivity mLayerThermalC(iLayer) = c1 + c2*xArg + (c1 - c4)*exp(-(c3*xArg)**c5) ! compute derivatives - mLayerdThermalC_dWat(iLayer) = ( c2 - c5*c3*(c3*xArg)**(c5-1)*(c1 - c4)*exp(-(c3*xArg)**c5) ) * dxArg_dWat - mLayerdThermalC_dNrg(iLayer) = ( c2 - c5*c3*(c3*xArg)**(c5-1)*(c1 - c4)*exp(-(c3*xArg)**c5) ) * dxArg_dTk + dThermalC_dWat(iLayer) = ( c2 - c5*c3*(c3*xArg)**(c5-1)*(c1 - c4)*exp(-(c3*xArg)**c5) ) * dxArg_dWat + dThermalC_dNrg(iLayer) = ( c2 - c5*c3*(c3*xArg)**(c5-1)*(c1 - c4)*exp(-(c3*xArg)**c5) ) * dxArg_dTk ! ** check case default; err=20; message=trim(message)//'unable to identify option for thermal conductivity of soil'; return @@ -882,31 +891,31 @@ subroutine iLayerThermalConduct(& ! ***** snow case(iname_snow) - mLayerdVolFracIce_dWat = ( 1._rkind - fLiq )*(iden_water/iden_ice) - mLayerdVolFracIce_dTk = -dFracLiq_dTk(nodeTempTrial(iLayer),snowfrz_scale) * nodeVolTotWatTrial(iLayer)*(iden_water/iden_ice) + dVolFracIce_dWat = ( 1._rkind - fracLiqSnow(iLayer) )*(iden_water/iden_ice) + dVolFracIce_dTk = -dTheta_dTk(iLayer)*(iden_water/iden_ice) ! temporally constant thermal conductivity if(ixThCondSnow==Smirnova2000)then mLayerThermalC(iLayer) = fixedThermalCond_snow - mLayerdThermalC_dWat(iLayer) = 0._rkind - mLayerdThermalC_dNrg(iLayer) = 0._rkind + dThermalC_dWat(iLayer) = 0._rkind + dThermalC_dNrg(iLayer) = 0._rkind ! thermal conductivity as a function of snow density else - call tcond_snow(nodeVolFracIceTrial(iLayer)*iden_ice, & ! input: snow density (kg m-3) + call tcond_snow(nodeVolFracIce(iLayer)*iden_ice, & ! input: snow density (kg m-3) mLayerThermalC(iLayer), & ! output: thermal conductivity (W m-1 K-1) err,cmessage) ! output: error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if select case(ixThCondSnow) case(Yen1965) - mLayerdThermalC_dWat(iLayer) = 2._rkind * 3.217d-6 * nodeVolFracIceTrial(iLayer) * iden_ice * mLayerdVolFracIce_dWat - mLayerdThermalC_dNrg(iLayer) = 2._rkind * 3.217d-6 * nodeVolFracIceTrial(iLayer) * iden_ice * mLayerdVolFracIce_dTk + dThermalC_dWat(iLayer) = 2._rkind * 3.217d-6 * nodeVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dWat + dThermalC_dNrg(iLayer) = 2._rkind * 3.217d-6 * nodeVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dTk case(Mellor1977) - mLayerdThermalC_dWat(iLayer) = 2._rkind * 2.576d-6 * nodeVolFracIceTrial(iLayer) * iden_ice * mLayerdVolFracIce_dWat - mLayerdThermalC_dNrg(iLayer) = 2._rkind * 2.576d-6 * nodeVolFracIceTrial(iLayer) * iden_ice * mLayerdVolFracIce_dTk + dThermalC_dWat(iLayer) = 2._rkind * 2.576d-6 * nodeVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dWat + dThermalC_dNrg(iLayer) = 2._rkind * 2.576d-6 * nodeVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dTk case(Jordan1991) - mLayerdThermalC_dWat(iLayer) = ( 7.75d-5 + 2._rkind * 1.105d-6 * nodeVolFracIceTrial(iLayer) * iden_ice ) * (lambda_ice-lambda_air) * mLayerdVolFracIce_dWat - mLayerdThermalC_dNrg(iLayer) = ( 7.75d-5 + 2._rkind * 1.105d-6 * nodeVolFracIceTrial(iLayer) * iden_ice ) * (lambda_ice-lambda_air) * mLayerdVolFracIce_dTk + dThermalC_dWat(iLayer) = ( 7.75d-5 + 2._rkind * 1.105d-6 * nodeVolFracIce(iLayer) * iden_ice ) * (lambda_ice-lambda_air) * iden_ice * dVolFracIce_dWat + dThermalC_dNrg(iLayer) = ( 7.75d-5 + 2._rkind * 1.105d-6 * nodeVolFracIce(iLayer) * iden_ice ) * (lambda_ice-lambda_air) * iden_ice * dVolFracIce_dTk end select ! option for the thermal conductivity of snow end if @@ -929,19 +938,19 @@ subroutine iLayerThermalConduct(& dThermalC_dHydStateBelow = 0._rkind dThermalC_dNrgStateBelow = 0._rkind else - iLayerThermalC = mLayerThermalC(1) - dThermalC_dHydStateBelow = mLayerdThermalC_dWat(ixLower) !these are index 1 since was passed with 1:1 - dThermalC_dNrgStateBelow = mLayerdThermalC_dNrg(ixLower) !these are index 1 since was passed with 1:1 + iLayerThermalC = mLayerThermalC(ixUpper) ! index was passed with 1:1 + dThermalC_dHydStateBelow = dThermalC_dWat(ixUpper) + dThermalC_dNrgStateBelow = dThermalC_dNrg(ixUpper) end if - dThermalC_dHydStateAbove = valueMissing - dThermalC_dNrgStateAbove = valueMissing + dThermalC_dHydStateAbove = realMissing + dThermalC_dNrgStateAbove = realMissing else if (ixLayerDesired==nLayers ) then ! assume the thermal conductivity at the domain boundaries is equal to the thermal conductivity of the layer - iLayerThermalC = mLayerThermalC(1) - dThermalC_dHydStateAbove = mLayerdThermalC_dWat(ixLower) !these are index 2 since was passed with iLayers:iLayers - dThermalC_dNrgStateAbove = mLayerdThermalC_dNrg(ixLower) !these are index 2 since was passed with iLayers:iLayers - dThermalC_dHydStateBelow = valueMissing - dThermalC_dNrgStateBelow = valueMissing + iLayerThermalC = mLayerThermalC(ixLower) ! index was passed with iLayers:iLayers + dThermalC_dHydStateAbove = dThermalC_dWat(ixLower) + dThermalC_dNrgStateAbove = dThermalC_dNrg(ixLower) + dThermalC_dHydStateBelow = realMissing + dThermalC_dNrgStateBelow = realMissing else ! get temporary variables TCn = mLayerThermalC(ixUpper) ! thermal conductivity below the layer interface (W m-1 K-1) @@ -952,16 +961,16 @@ subroutine iLayerThermalConduct(& ! compute thermal conductivity if(TCn+TCp > epsilon(TCn))then iLayerThermalC = (TCn*TCp*(zdn + zdp)) / den - dThermalC_dHydStateBelow = ( TCn*(zdn + zdp) - iLayerThermalC*zdn ) / den * mLayerdThermalC_dWat(ixLower) - dThermalC_dHydStateAbove = ( TCp*(zdn + zdp) - iLayerThermalC*zdp ) / den * mLayerdThermalC_dWat(ixUpper) - dThermalC_dNrgStateBelow = ( TCn*(zdn + zdp) - iLayerThermalC*zdn ) / den * mLayerdThermalC_dNrg(ixLower) - dThermalC_dNrgStateAbove = ( TCp*(zdn + zdp) - iLayerThermalC*zdp ) / den * mLayerdThermalC_dNrg(ixUpper) + dThermalC_dHydStateBelow = ( TCn*(zdn + zdp) - iLayerThermalC*zdn ) / den * dThermalC_dWat(ixLower) + dThermalC_dHydStateAbove = ( TCp*(zdn + zdp) - iLayerThermalC*zdp ) / den * dThermalC_dWat(ixUpper) + dThermalC_dNrgStateBelow = ( TCn*(zdn + zdp) - iLayerThermalC*zdn ) / den * dThermalC_dNrg(ixLower) + dThermalC_dNrgStateAbove = ( TCp*(zdn + zdp) - iLayerThermalC*zdp ) / den * dThermalC_dNrg(ixUpper) else iLayerThermalC = (TCn*zdn + TCp*zdp) / (zdn + zdp) - dThermalC_dHydStateBelow = zdp / (zdn + zdp) * mLayerdThermalC_dWat(ixLower) - dThermalC_dHydStateAbove = zdn / (zdn + zdp) * mLayerdThermalC_dWat(ixUpper) - dThermalC_dNrgStateBelow = zdp / (zdn + zdp) * mLayerdThermalC_dNrg(ixLower) - dThermalC_dNrgStateAbove = zdn / (zdn + zdp) * mLayerdThermalC_dNrg(ixUpper) + dThermalC_dHydStateBelow = zdp / (zdn + zdp) * dThermalC_dWat(ixLower) + dThermalC_dHydStateAbove = zdn / (zdn + zdp) * dThermalC_dWat(ixUpper) + dThermalC_dNrgStateBelow = zdp / (zdn + zdp) * dThermalC_dNrg(ixLower) + dThermalC_dNrgStateAbove = zdn / (zdn + zdp) * dThermalC_dNrg(ixUpper) end if !write(*,'(a,1x,i4,1x,10(f9.3,1x))') 'iLayer, TCn, TCp, zdn, zdp, iLayerThermalC(iLayer) = ', iLayer, TCn, TCp, zdn, zdp, iLayerThermalC(iLayer) endif From 5067c54a5e10ecbf92a9684d3eb88f503331b2ee Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 16 Mar 2022 17:29:10 +0900 Subject: [PATCH 0250/1472] Get rid of print statements --- build/source/engine/computJacDAE.f90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index c511fa614..6e108fd79 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -178,7 +178,7 @@ subroutine computJacDAE(& real(rkind) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative ! real(rkind) :: dVolFracWat_dPsi0_iLayer - integer(i4b) :: doprint + ! -------------------------------------------------------------- ! associate variables from data structures associate(& @@ -298,7 +298,7 @@ subroutine computJacDAE(& ! -------------------------------------------------------------- ! initialize error control err=0; message='computJacDAE/' - doprint=0 + ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* ! * PART 0: PRELIMINARIES (INITIALIZE JACOBIAN AND COMPUTE TIME-VARIABLE DIAGONAL TERMS) @@ -500,7 +500,7 @@ subroutine computJacDAE(& watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) - if (doprint==1) print*, nrgState,watState, "snow nrg wat indices" + ! - include derivatives of energy fluxes w.r.t water fluxes for current layer aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_ice * cj & + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) & @@ -614,7 +614,7 @@ subroutine computJacDAE(& ! - define index of hydrology state variable within the state subset watState = ixSoilOnlyHyd(iLayer) - if (doprint==1) print*, nrgState,watState, "soil nrg wat indices" + ! only compute derivatives if the energy state for the current layer is within the state subset if(watstate/=integerMissing)then From 5e2462ee7bc134519057591d936af10f99570ec6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 22 Mar 2022 12:12:12 +0900 Subject: [PATCH 0251/1472] Added error message for feasibility. --- build/source/engine/eval8DAE.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index 6e6ff6c50..4aa0f6f99 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -369,7 +369,8 @@ subroutine eval8DAE(& if(.not.feasible)then fluxVec(:) = realMissing resVec(:) = quadMissing - return + message=trim(message)//'solution is non-feasible' + err=20; return end if ! get the start and end indices for the soil compression calculations From c95d7fdd8c9cc345484d349ec66c4104839a53a8 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 22 Mar 2022 13:49:47 +0900 Subject: [PATCH 0252/1472] Added more error messaging --- build/source/engine/systemSolvSundials.f90 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 2dd14905b..38a3277a5 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -506,6 +506,8 @@ subroutine systemSolvSundials(& atol = atol * 0.1 rtol = rtol * 0.1 endif + if( .not.idaSucceeds ) imessage=trim(message)//'IDA did not succeed after reducing tolerance magnitudes '//(tol_iter-1)//' times' + end do ! iteration over tolerances From fca8182a0c60d9994a4b9f0d0c791c6e4e739234 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 22 Mar 2022 13:50:37 +0900 Subject: [PATCH 0253/1472] error messaging --- build/source/engine/systemSolvSundials.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 38a3277a5..54a9db117 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -508,7 +508,6 @@ subroutine systemSolvSundials(& endif if( .not.idaSucceeds ) imessage=trim(message)//'IDA did not succeed after reducing tolerance magnitudes '//(tol_iter-1)//' times' - end do ! iteration over tolerances From d716148f1de375c1903e0d2e375aca8f2f0c58db Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 22 Mar 2022 13:53:05 +0900 Subject: [PATCH 0254/1472] oops --- build/source/engine/systemSolvSundials.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 54a9db117..ae67ab7a2 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -506,7 +506,7 @@ subroutine systemSolvSundials(& atol = atol * 0.1 rtol = rtol * 0.1 endif - if( .not.idaSucceeds ) imessage=trim(message)//'IDA did not succeed after reducing tolerance magnitudes '//(tol_iter-1)//' times' + if( .not.idaSucceeds ) message=trim(message)//'IDA did not succeed after reducing tolerance magnitudes '//(tol_iter-1)//' times' end do ! iteration over tolerances From d31d4eadecc67a96283761ff83cb7073dba279ad Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 22 Mar 2022 13:59:40 +0900 Subject: [PATCH 0255/1472] make error a string --- build/source/engine/systemSolvSundials.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index ae67ab7a2..450944a54 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -506,7 +506,7 @@ subroutine systemSolvSundials(& atol = atol * 0.1 rtol = rtol * 0.1 endif - if( .not.idaSucceeds ) message=trim(message)//'IDA did not succeed after reducing tolerance magnitudes '//(tol_iter-1)//' times' + if( .not.idaSucceeds ) message=trim(message)//'IDA did not succeed after reducing tolerance magnitudes tol_iter times' end do ! iteration over tolerances From 26ec856541b835046bc1e5d4401a8cbe2f320bb6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 22 Mar 2022 14:10:15 +0900 Subject: [PATCH 0256/1472] last time on that error I think --- build/source/engine/systemSolvSundials.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 450944a54..e0f077b4a 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -506,7 +506,7 @@ subroutine systemSolvSundials(& atol = atol * 0.1 rtol = rtol * 0.1 endif - if( .not.idaSucceeds ) message=trim(message)//'IDA did not succeed after reducing tolerance magnitudes tol_iter times' + if( .not.idaSucceeds .and. tol_iter==3) message=trim(message)//'IDA did not succeed after reducing tolerance magnitudes 3 times' end do ! iteration over tolerances From 9b22f44de695a68203130526967585ba58d3fe8d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 4 Apr 2022 14:06:45 +0900 Subject: [PATCH 0257/1472] change some instructions, error message, and temporarily move over subsetting python code --- build/build_cmakeSundials_mac | 4 +++- build/source/engine/systemSolvSundials.f90 | 2 +- subsetGRU.py | 24 ++++++++++++++++++++++ sundials/installation.txt | 4 ++++ 4 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 subsetGRU.py diff --git a/build/build_cmakeSundials_mac b/build/build_cmakeSundials_mac index e9fef9b8d..7fa01c9a6 100755 --- a/build/build_cmakeSundials_mac +++ b/build/build_cmakeSundials_mac @@ -1,4 +1,6 @@ -# run cp build_cmakeSundials_mac ../../sundials/builddir/build_cmake on this and run from other directory +# from ../../sundials/builddir, run +# cp ../../sundials/builddir/build_cmakeSundials_mac build_cmake +# run script from the builddir directory with ./build_cmake cmake ../../sundials-5.8.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/Users/amedin/Research/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/Users/amedin/Research/SummaSundials/sundials/instdir/examples diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index e0f077b4a..f8f589ec6 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -506,7 +506,7 @@ subroutine systemSolvSundials(& atol = atol * 0.1 rtol = rtol * 0.1 endif - if( .not.idaSucceeds .and. tol_iter==3) message=trim(message)//'IDA did not succeed after reducing tolerance magnitudes 3 times' + if( .not.idaSucceeds .and. tol_iter==3) message=trim(message)//'IDA did not succeed after reducing tolerance by 2 magnitudes' end do ! iteration over tolerances diff --git a/subsetGRU.py b/subsetGRU.py new file mode 100644 index 000000000..ff9640232 --- /dev/null +++ b/subsetGRU.py @@ -0,0 +1,24 @@ +# Subset out a NA HRU forcing, parameter, and attribute files where GRU matches HRU +# +import os +os.system('ls') + +nbasin_slurm = 1 # first basin in the slurm batch run +nscript_slurm = 20 # 20th script +GRU_id =154 + nbasin_slurm + 207*nscript_slurm +HRU_id =154 + nbasin_slurm + 207*nscript_slurm + +settingsPath = '/globalhome/gwu479/HPC/TestScripts/settings/' +forcingPath = '/project/gwf/gwf_cmt/rez299/summaWorkflow_data/domain_NorthAmerica/forcing/4_SUMMA_input/' +attributeFile_in = settingsPath + 'attributes.nc' # Relative to settingsPath +trialParamFile_in = settingsPath + 'trialParams.nc' # Relative to settingsPath +forcingFile_in = forcingPath + 'forcing.nc' # Relative to forcingPath WHAT IS THIS + +desPath = '/globalhome/gwu479/HPC/TestScripts/subset_'+str(GRU_id)+'/' # Will need to change this in fileManger for settingsPath and forcingPath +attributeFile_out = desPath + 'attributes.nc' # Relative to settingsPath +trialParamFile_out = desPath + 'trialParams.nc' # Relative to settingsPath +forcingFile_out = desPath + 'forcing.nc' # Relative to forcingPath WHAT IS THIS + +os.system('ncks -d gru GRU_id,GRU_id -d hru HRU_id,HRU_id forcingFile_in forcingFile_out') +os.system('ncks -d gru GRU_id,GRU_id -d hru HRU_id,HRU_id attributeFile_in attributeFile_out') +os.system('ncks -d gru GRU_id,GRU_id -d hru HRU_id,HRU_id trialParamFile_in trialParamFile_out') diff --git a/sundials/installation.txt b/sundials/installation.txt index 7a562bf32..672782e0e 100644 --- a/sundials/installation.txt +++ b/sundials/installation.txt @@ -12,9 +12,13 @@ Make a directory outside the sundials-5.8.0 folder and enter it, and make the in % mkdir instdir % mkdir builddir % cd /builddir +NOTE if you need to recompile after a system upgrade, delete the contents of these directories before running cmake in the next step! 4. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, the following setting needs to be enabled while running the cmake inside the builddir, using your home directory as $(YOUR_HOME) % cmake ../sundials-5.8.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON DCMAKE_INSTALL_PREFIX=$(YOUR_HOME)/sundials/instdir -DEXAMPLES_INSTALL_PATH=$(YOUR_HOME)/sundials/instdir/examples +You can do this by run +cp ../../sundials/builddir/build_cmakeSundials_mac build_cmake +./build_cmake 5. The default compiler is gfortan. To change it (it should be the same as the netcdf build and the later summa build), you could also add the following option to cmake with your $(YOUR_GFORTRAN) From 2f823ee634768db4cf44543a5c1efedd5f24205c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 4 Apr 2022 17:58:56 +0900 Subject: [PATCH 0258/1472] Fixed subset file --- subsetGRU.py | 53 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/subsetGRU.py b/subsetGRU.py index ff9640232..91648bc76 100644 --- a/subsetGRU.py +++ b/subsetGRU.py @@ -1,24 +1,55 @@ # Subset out a NA HRU forcing, parameter, and attribute files where GRU matches HRU # -import os -os.system('ls') +# Inside forcingFile_out need to change forcingPath to = desForcingPath +# Inside forcingFile_out need to change initConditionFile, attributeFile, trialParamFile to = *out versions +import glob +import subprocess + +# GRU want to subset, change to do another GRU nbasin_slurm = 1 # first basin in the slurm batch run nscript_slurm = 20 # 20th script GRU_id =154 + nbasin_slurm + 207*nscript_slurm +GRU_bounds = str(GRU_id) + ',' + str(GRU_id) HRU_id =154 + nbasin_slurm + 207*nscript_slurm +HRU_bounds = str(HRU_id) + ',' + str(HRU_id) -settingsPath = '/globalhome/gwu479/HPC/TestScripts/settings/' +# top paths, could change +homePath = '/globalhome/gwu479/HPC/TestScripts/' +summa_exe= '/globalhome//gwu479/HPC/SummaSundials/summa/bin/summa_sundials.exe' forcingPath = '/project/gwf/gwf_cmt/rez299/summaWorkflow_data/domain_NorthAmerica/forcing/4_SUMMA_input/' + +# in paths, probably won't change +fileManager_in = homePath + 'fileManager.txt' +settingsPath = homePath + 'settings/' +initConditionFile_in = settingsPath + 'coldState.nc' # Relative to settingsPath attributeFile_in = settingsPath + 'attributes.nc' # Relative to settingsPath trialParamFile_in = settingsPath + 'trialParams.nc' # Relative to settingsPath -forcingFile_in = forcingPath + 'forcing.nc' # Relative to forcingPath WHAT IS THIS -desPath = '/globalhome/gwu479/HPC/TestScripts/subset_'+str(GRU_id)+'/' # Will need to change this in fileManger for settingsPath and forcingPath -attributeFile_out = desPath + 'attributes.nc' # Relative to settingsPath -trialParamFile_out = desPath + 'trialParams.nc' # Relative to settingsPath -forcingFile_out = desPath + 'forcing.nc' # Relative to forcingPath WHAT IS THIS +# out paths, probably won't change +fileManager_out = homePath + 'fileManager_' + str(GRU_id) + '.txt' +initConditionFile_out = settingsPath + 'coldState_' + str(GRU_id) + '.nc' # Relative to settingsPath +attributeFile_out = settingsPath + 'attributes_' + str(GRU_id) + '.nc' # Relative to settingsPath +trialParamFile_out = settingsPath + 'trialParams_' + str(GRU_id) + '.nc' # Relative to settingsPath +desForcingPath = homePath + 'forcing_' + str(GRU_id)+'/' + +# set up directory and new file Manager (will have to change things in it manually as above) +subprocess.call(['mkdir', desForcingPath]) +subprocess.call(['cp', fileManager_in, fileManager_out]) + +# do the subset +subprocess.call(['ncks', '-d', 'gru', GRU_bounds, '-d', 'hru', HRU_bounds, initConditionFile_in initConditionFile_out]) +subprocess.call(['ncks', '-d', 'gru', GRU_bounds, '-d', 'hru', HRU_bounds, attributeFile_in attributeFile_out]) +subprocess.call(['ncks', '-d', 'gru', GRU_bounds, '-d', 'hru', HRU_bounds, trialParamFile_in trialParamFile_out]) + +# forcing subset has multiple files +for fn in glob.glob(forcingPath +'*.nc'): + output_fn = desForcingPath + fn + output = subprocess.call(['ncks', '-d', 'gru', GRU_bounds, '-d', 'hru', HRU_bounds, fn, output_fn]) -os.system('ncks -d gru GRU_id,GRU_id -d hru HRU_id,HRU_id forcingFile_in forcingFile_out') -os.system('ncks -d gru GRU_id,GRU_id -d hru HRU_id,HRU_id attributeFile_in attributeFile_out') -os.system('ncks -d gru GRU_id,GRU_id -d hru HRU_id,HRU_id trialParamFile_in trialParamFile_out') +# write summa command call file +runFile = homePath + 'run_' + str(GRU_id) + '.sh' +summaCommand = summa_exe + '-p never -s _testSumma -m' + fileManger_out + '-r e' +runFile = open(homePath + 'run_' + str(GRU_id) + '.sh', "w") +a = runFile.write(summaCommand) +runFile.close() From c410ef6f4b84e961a25bd0fcb2dfba0a192df668 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 4 Apr 2022 19:06:15 +0900 Subject: [PATCH 0259/1472] Change it subset into a bash file from python file --- subsetGRU.py | 55 ---------------------------------------------------- subsetGRU.sh | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 55 deletions(-) delete mode 100644 subsetGRU.py create mode 100644 subsetGRU.sh diff --git a/subsetGRU.py b/subsetGRU.py deleted file mode 100644 index 91648bc76..000000000 --- a/subsetGRU.py +++ /dev/null @@ -1,55 +0,0 @@ -# Subset out a NA HRU forcing, parameter, and attribute files where GRU matches HRU -# -# Inside forcingFile_out need to change forcingPath to = desForcingPath -# Inside forcingFile_out need to change initConditionFile, attributeFile, trialParamFile to = *out versions - -import glob -import subprocess - -# GRU want to subset, change to do another GRU -nbasin_slurm = 1 # first basin in the slurm batch run -nscript_slurm = 20 # 20th script -GRU_id =154 + nbasin_slurm + 207*nscript_slurm -GRU_bounds = str(GRU_id) + ',' + str(GRU_id) -HRU_id =154 + nbasin_slurm + 207*nscript_slurm -HRU_bounds = str(HRU_id) + ',' + str(HRU_id) - -# top paths, could change -homePath = '/globalhome/gwu479/HPC/TestScripts/' -summa_exe= '/globalhome//gwu479/HPC/SummaSundials/summa/bin/summa_sundials.exe' -forcingPath = '/project/gwf/gwf_cmt/rez299/summaWorkflow_data/domain_NorthAmerica/forcing/4_SUMMA_input/' - -# in paths, probably won't change -fileManager_in = homePath + 'fileManager.txt' -settingsPath = homePath + 'settings/' -initConditionFile_in = settingsPath + 'coldState.nc' # Relative to settingsPath -attributeFile_in = settingsPath + 'attributes.nc' # Relative to settingsPath -trialParamFile_in = settingsPath + 'trialParams.nc' # Relative to settingsPath - -# out paths, probably won't change -fileManager_out = homePath + 'fileManager_' + str(GRU_id) + '.txt' -initConditionFile_out = settingsPath + 'coldState_' + str(GRU_id) + '.nc' # Relative to settingsPath -attributeFile_out = settingsPath + 'attributes_' + str(GRU_id) + '.nc' # Relative to settingsPath -trialParamFile_out = settingsPath + 'trialParams_' + str(GRU_id) + '.nc' # Relative to settingsPath -desForcingPath = homePath + 'forcing_' + str(GRU_id)+'/' - -# set up directory and new file Manager (will have to change things in it manually as above) -subprocess.call(['mkdir', desForcingPath]) -subprocess.call(['cp', fileManager_in, fileManager_out]) - -# do the subset -subprocess.call(['ncks', '-d', 'gru', GRU_bounds, '-d', 'hru', HRU_bounds, initConditionFile_in initConditionFile_out]) -subprocess.call(['ncks', '-d', 'gru', GRU_bounds, '-d', 'hru', HRU_bounds, attributeFile_in attributeFile_out]) -subprocess.call(['ncks', '-d', 'gru', GRU_bounds, '-d', 'hru', HRU_bounds, trialParamFile_in trialParamFile_out]) - -# forcing subset has multiple files -for fn in glob.glob(forcingPath +'*.nc'): - output_fn = desForcingPath + fn - output = subprocess.call(['ncks', '-d', 'gru', GRU_bounds, '-d', 'hru', HRU_bounds, fn, output_fn]) - -# write summa command call file -runFile = homePath + 'run_' + str(GRU_id) + '.sh' -summaCommand = summa_exe + '-p never -s _testSumma -m' + fileManger_out + '-r e' -runFile = open(homePath + 'run_' + str(GRU_id) + '.sh', "w") -a = runFile.write(summaCommand) -runFile.close() diff --git a/subsetGRU.sh b/subsetGRU.sh new file mode 100644 index 000000000..68d90c2a1 --- /dev/null +++ b/subsetGRU.sh @@ -0,0 +1,54 @@ +#!/bin/bash +# Subset out a NA HRU forcing, parameter, and attribute files where GRU matches HRU +# +# Inside forcingFile_out need to change forcingPath to = desForcingPath +# Inside forcingFile_out need to change initConditionFile, attributeFile, trialParamFile to = *out versions + +module load StdEnv/2020 +module load gcc/9.3.0 +module load nco/5.0.6 + +# GRU want to subset, change to do another GRU +# first basin in the slurm batch run, 20th script +nbasin_slurm = 1 +nscript_slurm = 20 +GRU_id =$((154 + nbasin_slurm + 207*nscript_slurm)) +HRU_id =$((154 + nbasin_slurm + 207*nscript_slurm)) + +# top paths, could change +homePath = /globalhome/gwu479/HPC/TestScripts/ +summa_exe= /globalhome//gwu479/HPC/SummaSundials/summa/bin/summa_sundials.exe +forcingPath = /project/gwf/gwf_cmt/rez299/summaWorkflow_data/domain_NorthAmerica/forcing/4_SUMMA_input/ + +# in paths, probably won't change +fileManager_in = ${homePath}fileManager.txt +settingsPath = ${homePath}settings/ +initConditionFile_in = ${settingsPath}coldState.nc +attributeFile_in = ${settingsPath}attributes.nc +trialParamFile_in = ${settingsPath}trialParams.nc + +# out paths, probably won't change +fileManager_out = ${homePath}fileManager_${GRU_id}.txt +initConditionFile_out = ${settingsPath}coldState_${GRU_id}.nc +attributeFile_out = ${settingsPath}attributes_${GRU_id}.nc +trialParamFile_out = ${settingsPath}trialParams_${GRU_id}.nc +desForcingPath = ${homePath}forcing_${GRU_id})/ + +# set up directory and new file Manager (will have to change things in it manually as above) +mkdir $desForcingPath +cp $fileManager_in $fileManager_out + +# do the subset +ncks -d gru $GRU_id,$GRU_id -d hru $HRU_id,$HRU_id $initConditionFile_in $initConditionFile_out +ncks -d gru $GRU_id,$GRU_id -d hru $HRU_id,$HRU_id $attributeFile_in $attributeFile_out +ncks -d gru $GRU_id,$GRU_id -d hru $HRU_id,$HRU_id $trialParamFile_in $trialParamFile_out + +# forcing subset has multiple files +for fn in ${forcingPath}*.nc; do + output_fn = ${desForcingPath}${fn} + output = ncks -d gru $GRU_id,$GRU_id -d hru $HRU_id,$HRU_id $fn $output_fn + +# write summa command call file +runFile = ${homePath}run_${GRU_id}.sh +summaCommand = $summa_exe -p never -s _testSumma -m $fileManger_out -r e +$summaCommand > runFile From c7384999c9155e2148624fbeefa14d056ad3728d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 5 Apr 2022 10:47:24 +0900 Subject: [PATCH 0260/1472] the HRU id was wrong --- subsetGRU.sh | 64 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/subsetGRU.sh b/subsetGRU.sh index 68d90c2a1..32c4fc264 100644 --- a/subsetGRU.sh +++ b/subsetGRU.sh @@ -10,45 +10,55 @@ module load nco/5.0.6 # GRU want to subset, change to do another GRU # first basin in the slurm batch run, 20th script -nbasin_slurm = 1 -nscript_slurm = 20 -GRU_id =$((154 + nbasin_slurm + 207*nscript_slurm)) -HRU_id =$((154 + nbasin_slurm + 207*nscript_slurm)) +# could also just set this as id from G* on output nc files (here 4295) +# However, this is not the actual GRUid, it is one off from the nc files, so subtract one before ncks +nbasin_slurm=1 +nscript_slurm=20 +GRU_file=$((154 + nbasin_slurm + 207*nscript_slurm)) +GRU_id=$((GRU_file - 1)) +HRU_id=$GRU_id +echo "file name id is # top paths, could change -homePath = /globalhome/gwu479/HPC/TestScripts/ -summa_exe= /globalhome//gwu479/HPC/SummaSundials/summa/bin/summa_sundials.exe -forcingPath = /project/gwf/gwf_cmt/rez299/summaWorkflow_data/domain_NorthAmerica/forcing/4_SUMMA_input/ +homePath=/globalhome/gwu479/HPC/TestScripts/ +summa_exe=/globalhome/gwu479/HPC/SummaSundials/summa/bin/summa_sundials.exe +forcingPath=/project/gwf/gwf_cmt/rez299/summaWorkflow_data/domain_NorthAmerica/forcing/4_SUMMA_input/ # in paths, probably won't change -fileManager_in = ${homePath}fileManager.txt -settingsPath = ${homePath}settings/ -initConditionFile_in = ${settingsPath}coldState.nc -attributeFile_in = ${settingsPath}attributes.nc -trialParamFile_in = ${settingsPath}trialParams.nc +fileManager_in=${homePath}fileManager.txt +settingsPath=${homePath}settings/ +initConditionFile_in=${settingsPath}coldState.nc +attributeFile_in=${settingsPath}attributes.nc +trialParamFile_in=${settingsPath}trialParams.nc # out paths, probably won't change -fileManager_out = ${homePath}fileManager_${GRU_id}.txt -initConditionFile_out = ${settingsPath}coldState_${GRU_id}.nc -attributeFile_out = ${settingsPath}attributes_${GRU_id}.nc -trialParamFile_out = ${settingsPath}trialParams_${GRU_id}.nc -desForcingPath = ${homePath}forcing_${GRU_id})/ +fileManager_out=${homePath}fileManager_${GRU_file}.txt +initConditionFile_out=${settingsPath}coldState_${GRU_file}.nc +attributeFile_out=${settingsPath}attributes_${GRU_file}.nc +trialParamFile_out=${settingsPath}trialParams_${GRU_file}.nc +desForcingPath=/project/gwf/gwf_cmt/gwu479/summaWorkflow_data/domain_NorthAmerica/forcing/4_SUMMA_input_${GRU_file}/ # set up directory and new file Manager (will have to change things in it manually as above) -mkdir $desForcingPath +mkdir -p "$desForcingPath" cp $fileManager_in $fileManager_out # do the subset -ncks -d gru $GRU_id,$GRU_id -d hru $HRU_id,$HRU_id $initConditionFile_in $initConditionFile_out -ncks -d gru $GRU_id,$GRU_id -d hru $HRU_id,$HRU_id $attributeFile_in $attributeFile_out -ncks -d gru $GRU_id,$GRU_id -d hru $HRU_id,$HRU_id $trialParamFile_in $trialParamFile_out +ncks -d hru,$HRU_id,$HRU_id $initConditionFile_in $initConditionFile_out +echo "coldState.nc HRU ${HRU_id} subsetted" +ncks -d gru,$GRU_id,$GRU_id -d hru,$HRU_id,$HRU_id $attributeFile_in $attributeFile_out +echo "attributes.nc GRU ${GRU_id} HRU ${HRU_id} subsetted" +ncks -d hru,$HRU_id,$HRU_id $trialParamFile_in $trialParamFile_out +echo "trialParams.nc HRU ${HRU_id} subsetted" # forcing subset has multiple files -for fn in ${forcingPath}*.nc; do - output_fn = ${desForcingPath}${fn} - output = ncks -d gru $GRU_id,$GRU_id -d hru $HRU_id,$HRU_id $fn $output_fn +cd $forcingPath +for fn in *.nc; do + output_fn=${desForcingPath}${fn} + ncks -d hru,$HRU_id,$HRU_id $fn $output_fn + echo "${fn} HRU ${HRU_id} subsetted" +done +cd $homePath # write summa command call file -runFile = ${homePath}run_${GRU_id}.sh -summaCommand = $summa_exe -p never -s _testSumma -m $fileManger_out -r e -$summaCommand > runFile +runFile=${homePath}run_${GRU_file}.sh +echo "${summa_exe} -p never -s _testSumma -m ${fileManager_out} -r e" > $runFile From a9fff54a73bbf908b8c29020e146f1385180ab5f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 5 Apr 2022 14:04:50 +0900 Subject: [PATCH 0261/1472] Fixed soil snow interface derivative --- build/source/engine/computJacDAE.f90 | 4 ++++ build/source/engine/solveByIDA.f90 | 2 +- subsetGRU.sh | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 6e108fd79..fb5c08c0e 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -523,6 +523,8 @@ subroutine computJacDAE(& ! (cross-derivative terms for the layer below) if(iLayer0)then !bottom snow layer and there is soil below + if(ixSoilOnlyNrg(1)/=integerMissing) aJac(ixSoilOnlyNrg(1),watState) = (dt/mLayerDepth(nSnow+1))*(-dNrgFlux_dWatAbove(nSnow) ) endif endif ! (if the energy state for the current layer is within the state subset) @@ -660,6 +662,8 @@ subroutine computJacDAE(& ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above if(iLayer>1)then if(ixSoilOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSoilOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(jLayer-1))*( dNrgFlux_dWatBelow(jLayer-1) ) + elseif(iLayer==1 .and. nSnowOnlyNrg>0)then !top soil layer and there is snow above + if(ixSnowOnlyNrg(nSnow)/=integerMissing) aJac(ixSnowOnlyNrg(nSnow),watState) = (dt/mLayerDepth(nSnow))*( dNrgFlux_dWatBelow(nSnow) ) endif ! (cross-derivative terms for the layer below) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index dc8c0a6e2..306e98754 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -390,7 +390,7 @@ subroutine solveByIDA( & if(ixMatrix == ixFullMatrix)then ! Set the user-supplied Jacobian routine - retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) !commment this line out to use FD Jacobian + retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) !comment this line out to use FD Jacobian if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetJacFn'; return; endif endif diff --git a/subsetGRU.sh b/subsetGRU.sh index 32c4fc264..ae0f1ccdf 100644 --- a/subsetGRU.sh +++ b/subsetGRU.sh @@ -17,7 +17,7 @@ nscript_slurm=20 GRU_file=$((154 + nbasin_slurm + 207*nscript_slurm)) GRU_id=$((GRU_file - 1)) HRU_id=$GRU_id -echo "file name id is +echo "file name id is ${GRU_file}, actual GRU id is ${GRU_id}" # top paths, could change homePath=/globalhome/gwu479/HPC/TestScripts/ From cc137ff1002c9a056ed5a0df21d603a8dace3cf9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 5 Apr 2022 22:20:03 +0900 Subject: [PATCH 0262/1472] Only recompute liquid and ice if perturb --- build/source/engine/computFlux.f90 | 1 + build/source/engine/computJacDAE.f90 | 2 +- build/source/engine/soilLiqFlx.f90 | 4 +- build/source/engine/ssdNrgFlux.f90 | 203 ++++++++++++++------------- build/source/engine/vegNrgFlux.f90 | 15 +- 5 files changed, 116 insertions(+), 109 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index be4fb2550..ed1e5e257 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -538,6 +538,7 @@ subroutine computFlux(& mLayerTempTrial, & ! intent(in): trial temperature at the current iteration (K) mLayerMatricHeadTrial, & ! intent(in): trial value for the total water matric potential in each soil layer (m) mLayerVolFracLiqTrial, & ! intent(in): trial volumetric fraction of liquid water at the current iteration(-) + mLayerVolFracIceTrial, & ! intent(in): trial volumetric fraction of ice water at the current iteration(-) ! input: pre-computed derivatives mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index fb5c08c0e..4e17b630f 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -620,7 +620,7 @@ subroutine computJacDAE(& ! only compute derivatives if the energy state for the current layer is within the state subset if(watstate/=integerMissing)then - ! - include derivates in liquid water fluxes w.r.t. temperature for current layer + ! - include derivatives in liquid water fluxes w.r.t. temperature for current layer aJac(watState,nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance ! - compute lower diagonal elements diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index bb3c6c67a..ed911e3e6 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -660,7 +660,7 @@ subroutine soilLiqFlx(& vectorHydCondTrial(1:2) = mLayerHydCond(iLayer:iLayer+1) vectorDiffuseTrial(1:2) = mLayerDiffuse(iLayer:iLayer+1) ! make appropriate perturbations - if(ixPerturb > 0)then + if(ixPerturb > 0)then ! only recompute these if perturbed select case(ixRichards) case(moisture) scalardPsi_dTheta = dPsi_dTheta(vectorVolFracLiqTrial(ixPerturb),vGn_alpha(ixPerturb),theta_res(ixPerturb),theta_sat(ixPerturb),vGn_n(ixPerturb),vGn_m(ixPerturb)) @@ -673,7 +673,7 @@ subroutine soilLiqFlx(& vectorHydCondTrial(ixPerturb) = scalarHydCondMicro + scalarHydCondMacro case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return end select ! (form of Richards' equation) - end if + endif ! (recompute if perturbed) ! ===== ! compute vertical flux at layer interface and its derivative w.r.t. the state above and the state below... diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index be6552fd1..40897a904 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -122,6 +122,7 @@ subroutine ssdNrgFlux(& mLayerTempTrial, & ! intent(in): trial temperature at the current iteration (K) mLayerMatricHeadTrial, & ! intent(in): trial matric head at the current iteration(m) mLayerVolFracLiqTrial, & ! intent(in): trial volumetric fraction of liquid water at the current iteration(-) + mLayerVolFracIceTrial, & ! intent(in): trial volumetric fraction of ice water at the current iteration(-) ! input: pre-computed derivatives mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) @@ -131,7 +132,7 @@ subroutine ssdNrgFlux(& mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model indices prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU ! output: fluxes and derivatives at all layer interfaces iLayerNrgFlux, & ! intent(out): energy flux at the layer interfaces (W m-2) @@ -167,6 +168,7 @@ subroutine ssdNrgFlux(& real(rkind),intent(in) :: mLayerTempTrial(:) ! temperature in each layer at the current iteration (m) real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! matric head in each layer at the current iteration (m) real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! volumetric fraction of liquid at the current iteration (-) + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! volumetric fraction of ice at the current iteration (-) ! input: pre-computed derivatives real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative in volumetric liquid water content w.r.t. temperature (K-1) real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) @@ -176,7 +178,7 @@ subroutine ssdNrgFlux(& type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! state vector geometry type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU ! output: fluxes and derivatives at all layer interfaces real(rkind),intent(out) :: iLayerNrgFlux(0:) ! energy flux at the layer interfaces (W m-2) @@ -191,7 +193,6 @@ subroutine ssdNrgFlux(& ! local variables character(LEN=256) :: cmessage ! error message of downwind routine integer(i4b) :: i,j,iLayer ! index of model layers - integer(i4b) :: iSoil ! index of soil layer integer(i4b) :: ixLayerDesired(1) ! layer desired (scalar solution) integer(i4b) :: ixTop ! top layer in subroutine call integer(i4b) :: ixBot ! bottom layer in subroutine call @@ -233,7 +234,6 @@ subroutine ssdNrgFlux(& real(rkind),dimension(2) :: vectorfrac_sand ! layer above and below fraction of sand (-) real(rkind),dimension(2) :: vectorfrac_clay ! layer above and below fraction of clay (-) ! recompute the perturbed version of iLayerThermalC, this could be the only version and remove the omputThermConduct_module - real(rkind) :: scalariLayerThermalC ! thermal conductivity at the interface of each layer (W m-1 K-1) real(rkind) :: dThermalC_dHydStateAbove ! derivative in the thermal conductivity w.r.t. water state in the layer above real(rkind) :: dThermalC_dHydStateBelow ! derivative in the thermal conductivity w.r.t. water state in the layer above real(rkind) :: dThermalC_dNrgStateAbove ! derivative in the thermal conductivity w.r.t. energy state in the layer above @@ -260,7 +260,7 @@ subroutine ssdNrgFlux(& lowerBoundTemp => mpar_data%var(iLookPARAM%lowerBoundTemp)%dat(1), & ! intent(in): temperature of the lower boundary (K) iLayerHeight => prog_data%var(iLookPROG%iLayerHeight)%dat, & ! intent(in): height at the interface of each layer (m) fixedThermalCond_snow => mpar_data%var(iLookPARAM%fixedThermalCond_snow)%dat(1), & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) - iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(in): thermal conductivity at the interface of each layer (W m-1 K-1) + iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(inout): thermal conductivity at the interface of each layer (W m-1 K-1) ! input: depth varying soil parameters iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) thCond_soil => mpar_data%var(iLookPARAM%thCond_soil)%dat, & ! intent(in): thermal conductivity of soil (W m-1 K-1) @@ -281,9 +281,6 @@ subroutine ssdNrgFlux(& ! initialize error control err=0; message='ssdNrgFlux/' - ! initialize the soil layer - iSoil=integerMissing - ! set conductive and advective fluxes to missing in the upper boundary ! NOTE: advective flux at the upper boundary is included in the ground heat flux iLayerConductiveFlux(0) = realMissing @@ -302,68 +299,13 @@ subroutine ssdNrgFlux(& ixTop = ixLayerDesired(1) ixBot = ixLayerDesired(1) else - ixTop = 1 + ixTop = 0 !include layer 0 in layer interface derivatives ixBot = nLayers endif - ! ------------------------------------------------------------------------------------------------------------------------- - ! ***** compute the conductive fluxes at layer interfaces ***** - ! ------------------------------------------------------------------------------------------------------------------------- - do iLayer=ixTop,ixBot ! (loop through model layers) - - if(iLayer>nSnow) iSoil = iLayer-nSnow - - ! compute fluxes at the lower boundary -- positive downwards - if(iLayer==nLayers)then - ! flux depends on the type of lower boundary condition - select case(ix_bcLowrTdyn) ! (identify the lower boundary condition for thermodynamics - case(prescribedTemp); iLayerConductiveFlux(nLayers) = -iLayerThermalC(iLayer)*(lowerBoundTemp - mLayerTempTrial(iLayer))/(mLayerDepth(iLayer)*0.5_rkind) - case(zeroFlux); iLayerConductiveFlux(nLayers) = 0._rkind - case default; err=20; message=trim(message)//'unable to identify lower boundary condition for thermodynamics'; return - end select ! (identifying the lower boundary condition for thermodynamics) - - ! compute fluxes within the domain -- positive downwards - else - iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) / & - (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) - - !write(*,'(a,i4,1x,2(f9.3,1x))') 'iLayer, iLayerConductiveFlux(iLayer), iLayerThermalC(iLayer) = ', iLayer, iLayerConductiveFlux(iLayer), iLayerThermalC(iLayer) - end if ! (the type of layer) - end do - - ! ------------------------------------------------------------------------------------------------------------------------- - ! ***** compute the advective fluxes at layer interfaces ***** - ! ------------------------------------------------------------------------------------------------------------------------- - do iLayer=ixTop,ixBot - ! get the liquid flux at layer interfaces - select case(layerType(iLayer)) - case(iname_snow); qFlux = iLayerLiqFluxSnow(iLayer) - case(iname_soil); qFlux = iLayerLiqFluxSoil(iLayer-nSnow) - case default; err=20; message=trim(message)//'unable to identify layer type'; return - end select - ! compute fluxes at the lower boundary -- positive downwards - if(iLayer==nLayers)then - iLayerAdvectiveFlux(iLayer) = -Cp_water*iden_water*qFlux*(lowerBoundTemp - mLayerTempTrial(iLayer)) - ! compute fluxes within the domain -- positive downwards - else - iLayerAdvectiveFlux(iLayer) = -Cp_water*iden_water*qFlux*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) - end if - end do ! looping through layers - - ! ------------------------------------------------------------------------------------------------------------------------- - ! ***** compute the total fluxes at layer interfaces ***** - ! ------------------------------------------------------------------------------------------------------------------------- - ! NOTE: ignore advective fluxes for now - iLayerNrgFlux(0) = groundNetFlux - iLayerNrgFlux(ixTop:ixBot) = iLayerConductiveFlux(ixTop:ixBot) - !print*, 'iLayerNrgFlux(0:4) = ', iLayerNrgFlux(0:4) - - ! ------------------------------------------------------------------------------------------------------------------------- - ! ***** compute the derivative in fluxes at layer interfaces w.r.t temperature in the layer above and the layer below ***** - ! ------------------------------------------------------------------------------------------------------------------------- - - ! get the indices for the snow+soil layers, include layer 0 now - if(.not.scalarSolution) ixTop = 0 + ! ------------------------------------------------------------------------------------------------------------------- + ! ***** compute the derivative in fluxes at layer interfaces w.r.t state in the layer above and the layer below ***** + ! ------------------------------------------------------------------------------------------------------------------- ! initialize un-used elements ! ***** the upper boundary @@ -423,6 +365,7 @@ subroutine ssdNrgFlux(& vectorVolFracLiqTrial(1:2) = mLayerVolFracLiqTrial(mLayer_ind) vectorMatricHeadTrial(1:2) = mLayerMatricHeadTrial(mLayer_ind) vectorTempTrial(1:2) = mLayerTempTrial(mLayer_ind) + vectorVolFracIceTrial(1:2) = mLayerVolFracIceTrial(mLayer_ind) ! make appropriate perturbations, if(ixPerturb > 2)then vectorMatricHeadTrial(ixPerturb-2) = vectorMatricHeadTrial(ixPerturb-2) + dx @@ -440,25 +383,27 @@ subroutine ssdNrgFlux(& case(iname_soil) j = mLayer_ind(i)-nSnow !soil layer - select case(ixRichards) ! (form of Richards' equation) - case(moisture) ! - vectorMatricHeadTrial(i) = matricHead(vectorVolFracLiqTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - Tcrit = crit_soilT(vectorMatricHeadTrial(i)) - !if change temp and below critical, it changes the state variable, seems like a problem FIX - if(vectorTempTrial(i) < Tcrit) then !if do not perturb temperature, this should not change - matricFHead = (LH_fus/gravity)*(vectorTempTrial(i) - Tfreeze)/Tfreeze - vectorVolFracLiqTrial(i) = volFracLiq(matricFHead,vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - endif - case(mixdform) - Tcrit = crit_soilT(vectorMatricHeadTrial(i)) - if(vectorTempTrial(i) < Tcrit) then !if do not perturb temperature, this should not change, but matricHeadTrial will have changed - matricFHead = (LH_fus/gravity)*(vectorTempTrial(i) - Tfreeze)/Tfreeze - vectorVolFracLiqTrial(i) = volFracLiq(matricFHead,vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - else - vectorVolFracLiqTrial(i) = volFracLiq(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - endif - end select ! (form of Richards' equation) - vectorVolFracIceTrial(i) = volFracLiq(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - vectorVolFracLiqTrial(i) + if(ixPerturb > 0)then ! only recompute these if perturbed + select case(ixRichards) ! (form of Richards' equation) + case(moisture) ! + vectorMatricHeadTrial(i) = matricHead(vectorVolFracLiqTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) + Tcrit = crit_soilT(vectorMatricHeadTrial(i)) + !if change temp and below critical, it changes the state variable, seems like a problem FIX + if(vectorTempTrial(i) < Tcrit) then !if do not perturb temperature, this should not change + matricFHead = (LH_fus/gravity)*(vectorTempTrial(i) - Tfreeze)/Tfreeze + vectorVolFracLiqTrial(i) = volFracLiq(matricFHead,vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) + endif + case(mixdform) + Tcrit = crit_soilT(vectorMatricHeadTrial(i)) + if(vectorTempTrial(i) < Tcrit) then !if do not perturb temperature, this should not change, but matricHeadTrial will have changed + matricFHead = (LH_fus/gravity)*(vectorTempTrial(i) - Tfreeze)/Tfreeze + vectorVolFracLiqTrial(i) = volFracLiq(matricFHead,vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) + else + vectorVolFracLiqTrial(i) = volFracLiq(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) + endif + end select ! (form of Richards' equation) + vectorVolFracIceTrial(i) = volFracLiq(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - vectorVolFracLiqTrial(i) + endif ! (recompute if perturbed) ! derivatives vectordPsi_dTheta(i) = mLayerdPsi_dTheta(j) vectordTheta_dPsi(i) = mLayerdTheta_dPsi(j) @@ -472,7 +417,7 @@ subroutine ssdNrgFlux(& case(iname_snow) fLiq = fracliquid(vectorTempTrial(i),snowfrz_scale) ! fraction of liquid water - vectorVolFracIceTrial(i) = ( vectorVolFracLiqTrial(i) / fLiq - vectorVolFracLiqTrial(i) )*(iden_water/iden_ice) ! use potentially perturbed nodeVolTotWatTrial + if(ixPerturb > 0) vectorVolFracIceTrial(i) = ( vectorVolFracLiqTrial(i) / fLiq - vectorVolFracLiqTrial(i) )*(iden_water/iden_ice) ! use perturbed nodeVolTotWatTrial ! derivatives vectordPsi_dTheta(i) = realMissing vectordTheta_dPsi(i) = realMissing @@ -524,7 +469,7 @@ subroutine ssdNrgFlux(& ! input: snow parameters fixedThermalCond_snow, & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) ! output: conductivity at the layer interface (scalars) - scalariLayerThermalC, & ! intent(out): thermal conductivity at the interface of each layer (W m-1 K-1) + iLayerThermalC(iLayer), & ! intent(inout): thermal conductivity at the interface of each layer (W m-1 K-1) ! output: derivatives in thermal conductivity w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below dThermalC_dHydStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above dThermalC_dHydStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above @@ -539,17 +484,18 @@ subroutine ssdNrgFlux(& ! compute total vertical flux, to compute derivatives if(deriv_desired .and. ixDerivMethod==numerical)then select case(itry) - case(unperturbed); scalarThermCFlux = scalariLayerThermalC - case(perturbStateTempAbove); scalarThermCFlux_dTempAbove = scalariLayerThermalC - case(perturbStateTempBelow); scalarThermCFlux_dTempBelow = scalariLayerThermalC - case(perturbStateWatAbove); scalarThermCFlux_dWatAbove = scalariLayerThermalC - case(perturbStateWatBelow); scalarThermCFlux_dWatBelow = scalariLayerThermalC + case(unperturbed); scalarThermCFlux = iLayerThermalC(iLayer) + case(perturbStateTempAbove); scalarThermCFlux_dTempAbove = iLayerThermalC(iLayer) + case(perturbStateTempBelow); scalarThermCFlux_dTempBelow = iLayerThermalC(iLayer) + case(perturbStateWatAbove); scalarThermCFlux_dWatAbove = iLayerThermalC(iLayer) + case(perturbStateWatBelow); scalarThermCFlux_dWatBelow = iLayerThermalC(iLayer) case default; err=10; message=trim(message)//"unknown perturbation"; return end select end if end do ! (looping through different flux calculations -- one or multiple calls depending if desire for numerical or analytical derivatives) + ! ***** the upper boundary if(iLayer==0)then ! (upper boundary) @@ -561,7 +507,7 @@ subroutine ssdNrgFlux(& dz = (mLayerHeight(1) - mLayerHeight(iLayer)) if(ixDerivMethod==analytical)then ! ** analytical derivatives dFlux_dWatBelow(iLayer) = -dThermalC_dHydStateBelow * ( mLayerTempTrial(1) - upperBoundTemp )/dz - dFlux_dTempBelow(iLayer) = -dThermalC_dNrgStateBelow * ( mLayerTempTrial(1) - upperBoundTemp )/dz - scalariLayerThermalC/dz + dFlux_dTempBelow(iLayer) = -dThermalC_dNrgStateBelow * ( mLayerTempTrial(1) - upperBoundTemp )/dz - iLayerThermalC(iLayer)/dz else ! ** numerical derivatives flux0 = -scalarThermCFlux *( mLayerTempTrial(1) - upperBoundTemp ) / dz flux2 = -scalarThermCFlux_dWatBelow*( mLayerTempTrial(1) - upperBoundTemp ) / dz @@ -598,7 +544,7 @@ subroutine ssdNrgFlux(& dz = mLayerDepth(iLayer)*0.5_rkind if(ixDerivMethod==analytical)then ! ** analytical derivatives dFlux_dWatAbove(iLayer) = -dThermalC_dHydStateAbove * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz - dFlux_dTempAbove(iLayer) = -dThermalC_dNrgStateAbove * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + scalariLayerThermalC/dz + dFlux_dTempAbove(iLayer) = -dThermalC_dNrgStateAbove * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + iLayerThermalC(iLayer)/dz else ! ** numerical derivatives flux0 = -scalarThermCFlux * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz flux1 = -scalarThermCFlux_dWatAbove * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz @@ -624,8 +570,8 @@ subroutine ssdNrgFlux(& if(ixDerivMethod==analytical)then ! ** analytical derivatives dFlux_dWatAbove(iLayer) = -dThermalC_dHydStateAbove * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz dFlux_dWatBelow(iLayer) = -dThermalC_dHydStateBelow * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz - dFlux_dTempAbove(iLayer) = -dThermalC_dNrgStateAbove * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz + scalariLayerThermalC/dz - dFlux_dTempBelow(iLayer) = -dThermalC_dNrgStateBelow * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz - scalariLayerThermalC/dz + dFlux_dTempAbove(iLayer) = -dThermalC_dNrgStateAbove * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz + iLayerThermalC(iLayer)/dz + dFlux_dTempBelow(iLayer) = -dThermalC_dNrgStateBelow * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz - iLayerThermalC(iLayer)/dz else ! ** numerical derivatives flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz flux1 = -scalarThermCFlux_dWatAbove*( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz @@ -643,6 +589,63 @@ subroutine ssdNrgFlux(& end do ! (looping through layers) + ! ------------------------------------------------------------------------------------------------------------------------- + ! Compute flux after the derivatives, because need iLayerThermal as calculated above + ! ------------------------------------------------------------------------------------------------------------------------- + + ! get the indices for the snow+soil layers, remove layer 0 now + if(.not.scalarSolution) ixTop = 1 + + ! ------------------------------------------------------------------------------------------------------------------------- + ! ***** compute the conductive fluxes at layer interfaces ***** + ! ------------------------------------------------------------------------------------------------------------------------- + do iLayer=ixTop,ixBot ! (loop through model layers) + + ! compute fluxes at the lower boundary -- positive downwards + if(iLayer==nLayers)then + ! flux depends on the type of lower boundary condition + select case(ix_bcLowrTdyn) ! (identify the lower boundary condition for thermodynamics + case(prescribedTemp); iLayerConductiveFlux(nLayers) = -iLayerThermalC(iLayer)*(lowerBoundTemp - mLayerTempTrial(iLayer))/(mLayerDepth(iLayer)*0.5_rkind) + case(zeroFlux); iLayerConductiveFlux(nLayers) = 0._rkind + case default; err=20; message=trim(message)//'unable to identify lower boundary condition for thermodynamics'; return + end select ! (identifying the lower boundary condition for thermodynamics) + + ! compute fluxes within the domain -- positive downwards + else + iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) / & + (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) + + !write(*,'(a,i4,1x,2(f9.3,1x))') 'iLayer, iLayerConductiveFlux(iLayer), iLayerThermalC(iLayer) = ', iLayer, iLayerConductiveFlux(iLayer), iLayerThermalC(iLayer) + end if ! (the type of layer) + end do + + ! ------------------------------------------------------------------------------------------------------------------------- + ! ***** compute the advective fluxes at layer interfaces ***** + ! ------------------------------------------------------------------------------------------------------------------------- + do iLayer=ixTop,ixBot + ! get the liquid flux at layer interfaces + select case(layerType(iLayer)) + case(iname_snow); qFlux = iLayerLiqFluxSnow(iLayer) + case(iname_soil); qFlux = iLayerLiqFluxSoil(iLayer-nSnow) + case default; err=20; message=trim(message)//'unable to identify layer type'; return + end select + ! compute fluxes at the lower boundary -- positive downwards + if(iLayer==nLayers)then + iLayerAdvectiveFlux(iLayer) = -Cp_water*iden_water*qFlux*(lowerBoundTemp - mLayerTempTrial(iLayer)) + ! compute fluxes within the domain -- positive downwards + else + iLayerAdvectiveFlux(iLayer) = -Cp_water*iden_water*qFlux*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) + end if + end do ! looping through layers + + ! ------------------------------------------------------------------------------------------------------------------------- + ! ***** compute the total fluxes at layer interfaces ***** + ! ------------------------------------------------------------------------------------------------------------------------- + ! NOTE: ignore advective fluxes for now + iLayerNrgFlux(0) = groundNetFlux + iLayerNrgFlux(ixTop:ixBot) = iLayerConductiveFlux(ixTop:ixBot) + !print*, 'iLayerNrgFlux(0:4) = ', iLayerNrgFlux(0:4) + ! end association of local variables with information in the data structures end associate @@ -684,7 +687,7 @@ subroutine iLayerThermalConduct(& ! input: snow parameters fixedThermalCond_snow, & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) ! output: conductivity at the layer interface (scalars) - iLayerThermalC, & ! thermal conductivity at the interface of each layer (W m-1 K-1) + iLayerThermalC, & ! intent(inout) thermal conductivity at the interface of each layer (W m-1 K-1) ! output: derivatives in thermal conductivity w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below dThermalC_dHydStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above dThermalC_dHydStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above @@ -735,7 +738,7 @@ subroutine iLayerThermalConduct(& ! input: snow parameters real(rkind),intent(in) :: fixedThermalCond_snow ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) ! output: thermal conductivity at layer interfaces - real(rkind),intent(out) :: iLayerThermalC ! thermal conductivity at the interface of each layer (W m-1 K-1) + real(rkind),intent(inout) :: iLayerThermalC ! thermal conductivity at the interface of each layer (W m-1 K-1) ! output: thermal conductivity derivatives at all layer interfaces real(rkind),intent(out) :: dThermalC_dHydStateAbove ! derivatives in the thermal conductivity w.r.t. matric head or volumetric liquid water in the layer above real(rkind),intent(out) :: dThermalC_dHydStateBelow ! derivatives in the thermal conductivity w.r.t. matric head or volumetric liquid water in the layer below @@ -845,7 +848,7 @@ subroutine iLayerThermalConduct(& dlambda_wet_dTk = -lambda_wet * log(lambda_ice) * dVolFracLiq_dTk relativeSat = (nodeVolFracIce(iLayer) + nodeVolFracLiq(iLayer))/theta_sat(iLayer) ! relative saturation - ! drelativeSat_dWat = dPsi0_dWat, and drelativeSat_dTk = 0 (so dkerstenNum_dTk = 0) + ! drelativeSat_dWat = dPsi0_dWat/theta_sat(iLayer), and drelativeSat_dTk = 0 (so dkerstenNum_dTk = 0) ! compute the Kersten number (-) if(relativeSat > 0.1_rkind)then ! log10(0.1) = -1 kerstenNum = log10(relativeSat) + 1._rkind diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index bca3c5926..66999559f 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -1040,6 +1040,9 @@ subroutine vegNrgFlux(& ! note: no perturbations performed using analytical derivatives (nFlux=1) ! ------------------------------------------------------------------------------------- + ! initialize for unperturbed state + canopyWetFraction = scalarCanopyWetFraction + ! identify the type of perturbation select case(itry) @@ -1083,14 +1086,14 @@ subroutine vegNrgFlux(& end select ! (type of perturbation) - ! recalculate liquid and ice from total water - canopyWetFraction = scalarCanopyWetFraction - fracLiquidCanopy = fracliquid(canopyTemp,snowfrz_scale) - canopyLiq = fracLiquidCanopy*canopyWat - canopyIce = (1._rkind - fracLiquidCanopy)*canopyWat - ! perturbations in canopy total water content affect canopy wetted fraction if(itry == perturbStateCanopy .OR. itry == perturbStateCanWat)then + + ! recalculate liquid and ice from total water + fracLiquidCanopy = fracliquid(canopyTemp,snowfrz_scale) + canopyLiq = fracLiquidCanopy*canopyWat + canopyIce = (1._rkind - fracLiquidCanopy)*canopyWat + if(computeVegFlux)then call wettedFrac(& ! input From 86ade3038a04a8de264765df0e540af8f4a8d06c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 8 Apr 2022 15:16:43 +0900 Subject: [PATCH 0263/1472] Fix (nrg might be still slightly small?) infiltration derivatives --- build/source/engine/computJacDAE.f90 | 22 ++++++++++++---------- build/source/engine/soilLiqFlx.f90 | 12 ++++++------ build/source/engine/soil_utilsSundials.f90 | 16 ++++++++-------- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 4e17b630f..839f4fc10 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -315,18 +315,20 @@ subroutine computJacDAE(& ! compute terms in the Jacobian for vegetation (excluding fluxes) ! NOTE: energy for vegetation is computed *within* the iteration loop as it includes phase change if(ixVegNrg/=integerMissing)then - dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy ) * cj + dVolHtCapBulk_dTkCanopy & - + LH_fus*iden_water * scalarCanopyTempPrime * d2Theta_dTkCanopy2 & - + LH_fus * dFracLiqVeg_dTkCanopy * scalarCanopyWatPrime / canopyDepth ! volumetric heat capacity of the vegetation (J m-3 K-1) + dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy ) * cj & + + dVolHtCapBulk_dTkCanopy * scalarCanopyTempPrime & + + LH_fus*iden_water * scalarCanopyTempPrime * d2Theta_dTkCanopy2 & + + LH_fus * dFracLiqVeg_dTkCanopy * scalarCanopyWatPrime / canopyDepth ! volumetric heat capacity of the vegetation (J m-3 K-1) end if ! compute additional terms for the Jacobian for the snow-soil domain (excluding fluxes) ! NOTE: energy for snow+soil is computed *within* the iteration loop as it includes phase change do iLayer=1,nLayers if(ixSnowSoilNrg(iLayer)/=integerMissing) then - dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj + dVolHtCapBulk_dTk(iLayer) & - + LH_fus*iden_water * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & - + LH_fus*iden_water * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) + dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj & + + dVolHtCapBulk_dTk(iLayer) * mLayerTempPrime(iLayer) & + + LH_fus*iden_water * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & + + LH_fus*iden_water * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) end if end do @@ -572,15 +574,15 @@ subroutine computJacDAE(& endif ! - include terms for surface infiltration below surface - if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),watState) = (dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(iLayer) + aJac(ixSoilOnlyHyd(1),watState) + if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),watState) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(iLayer) + aJac(ixSoilOnlyHyd(1),watState) end do ! (looping through hydrology states in the soil domain) ! - include terms for surface infiltration above surface if(nSnowOnlyHyd>0 .and. ixSnowOnlyHyd(nSnow)/=integerMissing)then - if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)) = (dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) elseif(computeVegFlux .and. ixVegHyd/=integerMissing)then !ixTopHyd = ixSoilOnlyHyd(1) - if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = (dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegNrg) + if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegNrg) endif endif ! (if the subset includes hydrology state variables in the soil domain) @@ -653,7 +655,7 @@ subroutine computJacDAE(& ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) & - + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) if(mLayerdTheta_dTk(jLayer) > verySmall) then ! ice is present aJac(nrgState,watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water*cj & - LH_fus*iden_water * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index ed911e3e6..953c7bdff 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -1453,11 +1453,11 @@ subroutine surfaceFlx(& fPart1 = hydCondWettingFront fPart2 = ( (wettingFrontSuction + depthWettingFront)/depthWettingFront ) dPart1(:) = surfaceSatHydCond*(zScale_TOPMODEL - 1._rkind) * ( (1._rkind - depthWettingFront/sum(mLayerDepth))**(zScale_TOPMODEL - 2._rkind) ) * (-dDepthWettingFront_dWat(:))/sum(mLayerDepth) - dPart2(:) = -wettingFrontSuction / (dDepthWettingFront_dWat(:)**2._rkind) - dXMaxInfilRate_dWat(:) = fPart1*dpart2(:) + fPart2*dPart1(:) + dPart2(:) = -dDepthWettingFront_dWat(:)*wettingFrontSuction / (depthWettingFront**2._rkind) + dXMaxInfilRate_dWat(:) = fPart1*dpart2(:) + fPart2*dPart1(:) dPart1(:) = surfaceSatHydCond*(zScale_TOPMODEL - 1._rkind) * ( (1._rkind - depthWettingFront/sum(mLayerDepth))**(zScale_TOPMODEL - 2._rkind) ) * (-dDepthWettingFront_dTk(:))/sum(mLayerDepth) - dPart2(:) = -wettingFrontSuction / (dDepthWettingFront_dTk(:)**2._rkind) - dXMaxInfilRate_dTk(:) = fPart1*dpart2(:) + fPart2*dPart1(:) + dPart2(:) = -dDepthWettingFront_dTk(:)*wettingFrontSuction / (depthWettingFront**2._rkind) + dXMaxInfilRate_dTk(:) = fPart1*dpart2(:) + fPart2*dPart1(:) ! define the infiltrating area and derivatives for the non-frozen part of the cell/basin if(qSurfScale < qSurfScaleMax)then @@ -1466,10 +1466,10 @@ subroutine surfaceFlx(& scalarInfilArea = min(0.5_rkind*(fInfRaw + sqrt(fInfRaw**2._rkind + scaleFactor)), 1._rkind) ! infiltrating area -- constrained if (0.5_rkind*(fInfRaw + sqrt(fInfRaw**2._rkind + scaleFactor))< 1._rkind) then dfracCap(:) = ( dRootZoneLiq_dWat(:)/maxFracCap + dRootZoneIce_dWat(:)*fracCap )/availCapacity - dfInfRaw(:) = qSurfScale*dfracCap(:) * exp(-qSurfScale*(1._rkind - fracCap)) + dfInfRaw(:) = -qSurfScale*dfracCap(:) * exp(-qSurfScale*(1._rkind - fracCap)) dInfilArea_dWat(1:nSoil) = 0.5_rkind*dfInfRaw(:) * (1._rkind + fInfRaw/sqrt(fInfRaw**2._rkind + scaleFactor)) dfracCap(:) = ( dRootZoneLiq_dTk(:)/maxFracCap + dRootZoneIce_dTk(:)*fracCap )/availCapacity - dfInfRaw(:) = qSurfScale*dfracCap(:) * exp(-qSurfScale*(1._rkind - fracCap)) + dfInfRaw(:) = -qSurfScale*dfracCap(:) * exp(-qSurfScale*(1._rkind - fracCap)) dInfilArea_dTk(1:nSoil) = 0.5_rkind*dfInfRaw(:) * (1._rkind + fInfRaw/sqrt(fInfRaw**2._rkind + scaleFactor)) else ! scalarInfilArea = 1._rkind dInfilArea_dWat(:) = 0._rkind diff --git a/build/source/engine/soil_utilsSundials.f90 b/build/source/engine/soil_utilsSundials.f90 index a255d11ce..b7c049020 100644 --- a/build/source/engine/soil_utilsSundials.f90 +++ b/build/source/engine/soil_utilsSundials.f90 @@ -83,7 +83,7 @@ subroutine liquidHeadSundials(& real(rkind),intent(in) :: volFracIcePrime ! output real(rkind),intent(out) :: matricHeadLiq ! liquid water matric potential (m) - real(rkind),intent(out) :: matricHeadLiqPrime + real(rkind),intent(out) :: matricHeadLiqPrime real(rkind),intent(out) ,optional :: dPsiLiq_dPsi0 ! derivative in the liquid water matric potential w.r.t. the total water matric potential (-) real(rkind),intent(out) ,optional :: dPsiLiq_dTemp ! derivative in the liquid water matric potential w.r.t. temperature (m K-1) ! output: error control @@ -102,7 +102,7 @@ subroutine liquidHeadSundials(& ! ** partially frozen soil if(volFracIce > verySmall .and. matricHeadTotal < 0._rkind)then ! check that ice exists and that the soil is unsaturated - + ! ----- ! - compute liquid water matric potential... @@ -123,7 +123,7 @@ subroutine liquidHeadSundials(& else matricHeadLiqPrime = 0._rkind endif - + ! compute derivative in liquid water matric potential w.r.t. effective saturation (m) if(present(dPsiLiq_dPsi0).or.present(dPsiLiq_dTemp))then @@ -165,7 +165,7 @@ subroutine liquidHeadSundials(& dPsiLiq_dTemp = dPsiLiq_dEffSat*dEffSat_dTemp ! matricHeadLiqPrime = dPsiLiq_dTemp * tempPrime - + endif ! if dPsiLiq_dTemp is desired @@ -178,10 +178,10 @@ subroutine liquidHeadSundials(& end if ! (if ice exists) end subroutine liquidHeadSundials - - + + ! ****************************************************************************************************************************** - ! public function dTheta_dPsi: compute the derivative of the soil water characteristic (m-1) + ! public function d2Theta_dPsi2: compute the second derivative of the soil water characteristic (m-1) ! ****************************************************************************************************************************** function d2Theta_dPsi2(psi,alpha,theta_res,theta_sat,n,m) implicit none @@ -204,7 +204,7 @@ function d2Theta_dPsi2(psi,alpha,theta_res,theta_sat,n,m) d2Theta_dPsi2 = 0._rkind end if end function d2Theta_dPsi2 - + ! ****************************************************************************************************************************** ! public function d2Theta_dTk2: differentiate the freezing curve w.r.t. temperature From ff545453ecf6b8dae51fc0390242698da01717cf Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 8 Apr 2022 23:16:28 +0900 Subject: [PATCH 0264/1472] Fix the ssdNrgFlx to compute dTheta_dPsi and dPsi_dTheta since they are computed in soilLiqFlx which is called after --- build/source/engine/computFlux.f90 | 6 +-- build/source/engine/soilLiqFlx.f90 | 4 +- build/source/engine/ssdNrgFlux.f90 | 12 +++--- subsetGRU.sh | 64 ------------------------------ 4 files changed, 9 insertions(+), 77 deletions(-) delete mode 100644 subsetGRU.sh diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index ed1e5e257..3da3d334e 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -534,7 +534,7 @@ subroutine computFlux(& ! input: liquid water fluxes throughout the snow and soil domains iLayerLiqFluxSnow, & ! intent(in): liquid flux at the interface of each snow layer (m s-1) iLayerLiqFluxSoil, & ! intent(in): liquid flux at the interface of each soil layer (m s-1) - ! input: trial value of model state variabes + ! input: trial value of model state variables mLayerTempTrial, & ! intent(in): trial temperature at the current iteration (K) mLayerMatricHeadTrial, & ! intent(in): trial value for the total water matric potential in each soil layer (m) mLayerVolFracLiqTrial, & ! intent(in): trial volumetric fraction of liquid water at the current iteration(-) @@ -542,8 +542,6 @@ subroutine computFlux(& ! input: pre-computed derivatives mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) - mLayerdPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) ! input-output: data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model indices @@ -672,7 +670,7 @@ subroutine computFlux(& if(ixVegHyd/=integerMissing)then ! save canopy derivatives - above_soilLiqFluxDeriv = scalarcanopyLiqDeriv/iden_water ! derivative in (throughfall + drainage) w.r.t. canopy liquid water + above_soilLiqFluxDeriv = scalarCanopyLiqDeriv/iden_water ! derivative in (throughfall + drainage) w.r.t. canopy liquid water above_soildLiq_dTk = dCanLiq_dTcanopy ! derivative of canopy liquid storage w.r.t. temperature above_soilFracLiq = scalarFracLiqVeg ! fraction of liquid water in canopy (-) else diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index 953c7bdff..efc31fb51 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -1018,11 +1018,11 @@ subroutine diagv_node(& ! compute the derivative in the soil water characteristic select case(ixRichards) case(moisture) - scalardPsi_dTheta = dPsi_dTheta(scalarvolFracLiqTrial,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + scalardPsi_dTheta = dPsi_dTheta(scalarVolFracLiqTrial,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) scalardTheta_dPsi = realMissing ! (deliberately cause problems if this is ever used) case(mixdform) scalardTheta_dPsi = dTheta_dPsi(scalarMatricHeadTrial,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - scalardPsi_dTheta = dPsi_dTheta(scalarvolFracLiqTrial,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + scalardPsi_dTheta = dPsi_dTheta(scalarVolFracLiqTrial,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) if(testDeriv)then volFracLiq1 = volFracLiq(scalarMatricHeadTrial, vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) volFracLiq2 = volFracLiq(scalarMatricHeadTrial+dx,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index 40897a904..5dc093b8c 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -126,8 +126,6 @@ subroutine ssdNrgFlux(& ! input: pre-computed derivatives mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) - mLayerdPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) ! input-output: data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model indices @@ -147,6 +145,8 @@ subroutine ssdNrgFlux(& USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists USE snow_utils_module,only:fracliquid ! compute fraction of liquid water at a given temperature + USE soil_utils_module,only:dTheta_dPsi ! compute derivative of the soil moisture characteristic w.r.t. psi (m-1) + USE soil_utils_module,only:dPsi_dTheta ! compute derivative of the soil moisture characteristic w.r.t. theta (m) ! constants USE multiconst, only: gravity, & ! gravitational acceleration (m s-1) @@ -172,8 +172,6 @@ subroutine ssdNrgFlux(& ! input: pre-computed derivatives real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative in volumetric liquid water content w.r.t. temperature (K-1) real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) - real(rkind),intent(in) :: mLayerdTheta_dPsi(:) ! derivative in the soil water characteristic w.r.t. psi (m-1) - real(rkind),intent(in) :: mLayerdPsi_dTheta(:) ! derivative in the soil water characteristic w.r.t. theta (m) ! input-output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! state vector geometry @@ -404,9 +402,9 @@ subroutine ssdNrgFlux(& end select ! (form of Richards' equation) vectorVolFracIceTrial(i) = volFracLiq(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - vectorVolFracLiqTrial(i) endif ! (recompute if perturbed) - ! derivatives - vectordPsi_dTheta(i) = mLayerdPsi_dTheta(j) - vectordTheta_dPsi(i) = mLayerdTheta_dPsi(j) + ! derivatives, these need to be computed because they are computed only in soilLiqFlx which is called after this + vectordPsi_dTheta(i) = dPsi_dTheta(vectorVolFracLiqTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) + vectordTheta_dPsi(i) = dTheta_dPsi(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) vectorFracLiqSnow(i) = realMissing ! soil parameters vectortheta_sat(i) = theta_sat(j) diff --git a/subsetGRU.sh b/subsetGRU.sh deleted file mode 100644 index ae0f1ccdf..000000000 --- a/subsetGRU.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/bash -# Subset out a NA HRU forcing, parameter, and attribute files where GRU matches HRU -# -# Inside forcingFile_out need to change forcingPath to = desForcingPath -# Inside forcingFile_out need to change initConditionFile, attributeFile, trialParamFile to = *out versions - -module load StdEnv/2020 -module load gcc/9.3.0 -module load nco/5.0.6 - -# GRU want to subset, change to do another GRU -# first basin in the slurm batch run, 20th script -# could also just set this as id from G* on output nc files (here 4295) -# However, this is not the actual GRUid, it is one off from the nc files, so subtract one before ncks -nbasin_slurm=1 -nscript_slurm=20 -GRU_file=$((154 + nbasin_slurm + 207*nscript_slurm)) -GRU_id=$((GRU_file - 1)) -HRU_id=$GRU_id -echo "file name id is ${GRU_file}, actual GRU id is ${GRU_id}" - -# top paths, could change -homePath=/globalhome/gwu479/HPC/TestScripts/ -summa_exe=/globalhome/gwu479/HPC/SummaSundials/summa/bin/summa_sundials.exe -forcingPath=/project/gwf/gwf_cmt/rez299/summaWorkflow_data/domain_NorthAmerica/forcing/4_SUMMA_input/ - -# in paths, probably won't change -fileManager_in=${homePath}fileManager.txt -settingsPath=${homePath}settings/ -initConditionFile_in=${settingsPath}coldState.nc -attributeFile_in=${settingsPath}attributes.nc -trialParamFile_in=${settingsPath}trialParams.nc - -# out paths, probably won't change -fileManager_out=${homePath}fileManager_${GRU_file}.txt -initConditionFile_out=${settingsPath}coldState_${GRU_file}.nc -attributeFile_out=${settingsPath}attributes_${GRU_file}.nc -trialParamFile_out=${settingsPath}trialParams_${GRU_file}.nc -desForcingPath=/project/gwf/gwf_cmt/gwu479/summaWorkflow_data/domain_NorthAmerica/forcing/4_SUMMA_input_${GRU_file}/ - -# set up directory and new file Manager (will have to change things in it manually as above) -mkdir -p "$desForcingPath" -cp $fileManager_in $fileManager_out - -# do the subset -ncks -d hru,$HRU_id,$HRU_id $initConditionFile_in $initConditionFile_out -echo "coldState.nc HRU ${HRU_id} subsetted" -ncks -d gru,$GRU_id,$GRU_id -d hru,$HRU_id,$HRU_id $attributeFile_in $attributeFile_out -echo "attributes.nc GRU ${GRU_id} HRU ${HRU_id} subsetted" -ncks -d hru,$HRU_id,$HRU_id $trialParamFile_in $trialParamFile_out -echo "trialParams.nc HRU ${HRU_id} subsetted" - -# forcing subset has multiple files -cd $forcingPath -for fn in *.nc; do - output_fn=${desForcingPath}${fn} - ncks -d hru,$HRU_id,$HRU_id $fn $output_fn - echo "${fn} HRU ${HRU_id} subsetted" -done -cd $homePath - -# write summa command call file -runFile=${homePath}run_${GRU_file}.sh -echo "${summa_exe} -p never -s _testSumma -m ${fileManager_out} -r e" > $runFile From cfbdc7d8dff81cfeae60a28e7255ad3453bb79cb Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 11 Apr 2022 18:37:06 +0900 Subject: [PATCH 0265/1472] Fixing surface infiltration derivatives --- build/source/engine/computFlux.f90 | 1 + build/source/engine/soilLiqFlx.f90 | 17 +++++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 3da3d334e..e9d0d8b3a 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -784,6 +784,7 @@ subroutine computFlux(& if(globalPrintFlag) print*, 'dPsiLiq_dPsi0(1:nSoil) = ', dPsiLiq_dPsi0(1:nSoil) dq_dHydStateAbove(1:nSoil) = dq_dHydStateAbove(1:nSoil) *dPsiLiq_dPsi0(1:nSoil) dq_dHydStateBelow(0:nSoil-1) = dq_dHydStateBelow(0:nSoil-1)*dPsiLiq_dPsi0(1:nSoil) + dq_dHydStateLayerSurfVec(0:nSoil) = dq_dHydStateLayerSurfVec(0:nSoil)*dPsiLiq_dPsi0(0:nSoil) endif ! if calculating the liquid flux through soil diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index efc31fb51..96af858d1 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -581,9 +581,6 @@ subroutine soilLiqFlx(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - dq_dHydStateBelow(0) = 0._rkind - dq_dNrgStateBelow(0) = 0._rkind - ! include base soil evaporation as the upper boundary flux iLayerLiqFluxSoil(0) = scalarGroundEvaporation/iden_water + scalarSurfaceInfiltration @@ -600,9 +597,13 @@ subroutine soilLiqFlx(& end do ! (looping through different flux calculations -- one or multiple calls depending if desire for numerical or analytical derivatives) + dq_dHydStateBelow(0) = 0._rkind ! contribution will be in dq_dHydStateLayerSurfVec(1) + dq_dNrgStateBelow(0) = 0._rkind ! contribution will be in dq_dNrgStateLayerSurfVec(1) + ! compute numerical derivatives if(deriv_desired .and. ixDerivMethod==numerical)then - dq_dHydStateBelow(0) = (scalarFlux_dStateBelow - scalarFlux)/dx ! change in surface flux w.r.t. change in the soil moisture in the top soil layer (m s-1) + dq_dHydStateLayerSurfVec(1) = (scalarFlux_dStateBelow - scalarFlux)/dx ! change in surface flux w.r.t. change in the soil moisture in the top soil layer (m s-1) + dq_dHydStateLayerSurfVec(1) = 0._rkind ! Did not yet compute this perturbation end if !print*, 'scalarSurfaceInfiltration, iLayerLiqFluxSoil(0) = ', scalarSurfaceInfiltration, iLayerLiqFluxSoil(0) !print*, '(ixDerivMethod==numerical), dq_dHydStateBelow(0) = ', (ixDerivMethod==numerical), dq_dHydStateBelow(0) @@ -1451,7 +1452,7 @@ subroutine surfaceFlx(& xMaxInfilRate = hydCondWettingFront*( (wettingFrontSuction + depthWettingFront)/depthWettingFront ) ! maximum infiltration rate (m s-1) !write(*,'(a,1x,f9.3,1x,10(e20.10,1x))') 'depthWettingFront, surfaceSatHydCond, hydCondWettingFront, xMaxInfilRate = ', depthWettingFront, surfaceSatHydCond, hydCondWettingFront, xMaxInfilRate fPart1 = hydCondWettingFront - fPart2 = ( (wettingFrontSuction + depthWettingFront)/depthWettingFront ) + fPart2 = (wettingFrontSuction + depthWettingFront)/depthWettingFront dPart1(:) = surfaceSatHydCond*(zScale_TOPMODEL - 1._rkind) * ( (1._rkind - depthWettingFront/sum(mLayerDepth))**(zScale_TOPMODEL - 2._rkind) ) * (-dDepthWettingFront_dWat(:))/sum(mLayerDepth) dPart2(:) = -dDepthWettingFront_dWat(:)*wettingFrontSuction / (depthWettingFront**2._rkind) dXMaxInfilRate_dWat(:) = fPart1*dpart2(:) + fPart2*dPart1(:) @@ -1728,7 +1729,7 @@ subroutine qDrainFlux(& lowerBoundHead, & ! intent(in): lower boundary condition (m) lowerBoundTheta, & ! intent(in): lower boundary condition (-) ! input: derivative in soil water characteristix - node__dPsi_dTheta, & ! intent(in): derivative of the soil moisture characteristic w.r.t. theta (m) + node_dPsi_dTheta, & ! intent(in): derivative of the soil moisture characteristic w.r.t. theta (m) ! input: transmittance surfaceSatHydCond, & ! intent(in): saturated hydraulic conductivity at the surface (m s-1) bottomSatHydCond, & ! intent(in): saturated hydraulic conductivity at the bottom of the unsaturated zone (m s-1) @@ -1778,7 +1779,7 @@ subroutine qDrainFlux(& real(rkind),intent(in) :: lowerBoundHead ! lower boundary condition for matric head (m) real(rkind),intent(in) :: lowerBoundTheta ! lower boundary condition for volumetric liquid water content (-) ! input: derivative in soil water characteristix - real(rkind),intent(in) :: node__dPsi_dTheta ! derivative of the soil moisture characteristic w.r.t. theta (m) + real(rkind),intent(in) :: node_dPsi_dTheta ! derivative of the soil moisture characteristic w.r.t. theta (m) ! input: transmittance real(rkind),intent(in) :: surfaceSatHydCond ! saturated hydraulic conductivity at the surface (m s-1) real(rkind),intent(in) :: bottomSatHydCond ! saturated hydraulic conductivity at the bottom of the unsaturated zone (m s-1) @@ -1875,7 +1876,7 @@ subroutine qDrainFlux(& if(deriv_desired)then ! hydrology derivatives select case(ixRichards) ! (form of Richards' equation) - case(moisture); dq_dHydStateUnsat = kAnisotropic*surfaceSatHydCond * node__dPsi_dTheta*exp(-zWater/zScale_TOPMODEL)/zScale_TOPMODEL + case(moisture); dq_dHydStateUnsat = kAnisotropic*surfaceSatHydCond * node_dPsi_dTheta*exp(-zWater/zScale_TOPMODEL)/zScale_TOPMODEL case(mixdform); dq_dHydStateUnsat = kAnisotropic*surfaceSatHydCond * exp(-zWater/zScale_TOPMODEL)/zScale_TOPMODEL case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return end select From e6b575ef78d5e23c03a4a79e3ad42e61b4f4ab35 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 11 Apr 2022 20:52:49 +0900 Subject: [PATCH 0266/1472] debugging, will undo --- build/source/engine/soilLiqFlx.f90 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index 96af858d1..4246643d5 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -1533,6 +1533,10 @@ subroutine surfaceFlx(& dq_dNrgStateVec(:) = (1._rkind - scalarFrozenArea) * ( dInfilArea_dTk(:) *min(scalarRainPlusMelt,xMaxInfilRate) + scalarInfilArea*dInfilRate_dTk(:) ) +& (-dFrozenArea_dTk(:)) *scalarInfilArea*min(scalarRainPlusMelt,xMaxInfilRate) + scalarSurfaceInfiltration = rootZoneIce + dq_dHydStateVec(1:nSoil) = dRootZoneIce_dWat + dq_dNrgStateVec(1:nSoil) = dRootZoneIce_dTk + ! compute surface runoff (m s-1) scalarSurfaceRunoff = scalarRainPlusMelt - scalarSurfaceInfiltration !print*, 'scalarRainPlusMelt, xMaxInfilRate = ', scalarRainPlusMelt, xMaxInfilRate From b5c66ecfc80279846d1066185fec152439159d68 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 11 Apr 2022 23:18:11 +0900 Subject: [PATCH 0267/1472] Fix aquifer and infiltration, infiltration still looks off --- build/source/engine/computFlux.f90 | 2 +- build/source/engine/computJacDAE.f90 | 3 ++- build/source/engine/soilLiqFlx.f90 | 4 ---- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index e9d0d8b3a..9355d1fa9 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -784,7 +784,7 @@ subroutine computFlux(& if(globalPrintFlag) print*, 'dPsiLiq_dPsi0(1:nSoil) = ', dPsiLiq_dPsi0(1:nSoil) dq_dHydStateAbove(1:nSoil) = dq_dHydStateAbove(1:nSoil) *dPsiLiq_dPsi0(1:nSoil) dq_dHydStateBelow(0:nSoil-1) = dq_dHydStateBelow(0:nSoil-1)*dPsiLiq_dPsi0(1:nSoil) - dq_dHydStateLayerSurfVec(0:nSoil) = dq_dHydStateLayerSurfVec(0:nSoil)*dPsiLiq_dPsi0(0:nSoil) + dq_dHydStateLayerSurfVec(1:nSoil) = dq_dHydStateLayerSurfVec(1:nSoil)*dPsiLiq_dPsi0(1:nSoil) endif ! if calculating the liquid flux through soil diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 839f4fc10..490315690 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -592,7 +592,8 @@ subroutine computJacDAE(& ! ---------------------------------------- if(ixAqWat/=integerMissing) then aJac(ixAqWat,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) * cj - aJac(ixAqWat,ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! scalarAquiferRecharge = iLayerLiqFluxSoil(nSoil) + aJac(ixAqWat,ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat + aJac(ixAqWat,ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) if(computeVegFlux)then aJac(ixAqWat,ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index 4246643d5..96af858d1 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -1533,10 +1533,6 @@ subroutine surfaceFlx(& dq_dNrgStateVec(:) = (1._rkind - scalarFrozenArea) * ( dInfilArea_dTk(:) *min(scalarRainPlusMelt,xMaxInfilRate) + scalarInfilArea*dInfilRate_dTk(:) ) +& (-dFrozenArea_dTk(:)) *scalarInfilArea*min(scalarRainPlusMelt,xMaxInfilRate) - scalarSurfaceInfiltration = rootZoneIce - dq_dHydStateVec(1:nSoil) = dRootZoneIce_dWat - dq_dNrgStateVec(1:nSoil) = dRootZoneIce_dTk - ! compute surface runoff (m s-1) scalarSurfaceRunoff = scalarRainPlusMelt - scalarSurfaceInfiltration !print*, 'scalarRainPlusMelt, xMaxInfilRate = ', scalarRainPlusMelt, xMaxInfilRate From 26063fea7294051f223e37e739aacb9cd43046d2 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 12 Apr 2022 12:01:42 +0900 Subject: [PATCH 0268/1472] update Jac printing to match sundials --- build/source/engine/computJacDAE.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 490315690..4199b3046 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -703,8 +703,8 @@ subroutine computJacDAE(& !print*, '** analytical Jacobian (full):' !write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=1,size(aJac,2)) - !do iLayer=1,size(aJac,2) - ! write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(1:size(aJac,1),iLayer) + !do iLayer=1,size(aJac,1) + ! write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(iLayer,1:size(aJac,2)) !end do !print *, '--------------------------------------------------------------' From 85030f65c77f4739d680b45e26ddeb2ba50f085c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 12 Apr 2022 13:49:24 +0900 Subject: [PATCH 0269/1472] Fixed vectormatrichead wrong indices passed --- build/source/engine/ssdNrgFlux.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index 5dc093b8c..1efa96f0b 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -361,7 +361,7 @@ subroutine ssdNrgFlux(& ! ============================ ! start with the un-perturbed case vectorVolFracLiqTrial(1:2) = mLayerVolFracLiqTrial(mLayer_ind) - vectorMatricHeadTrial(1:2) = mLayerMatricHeadTrial(mLayer_ind) + vectorMatricHeadTrial(1:2) = mLayerMatricHeadTrial(mLayer_ind-nSnow) vectorTempTrial(1:2) = mLayerTempTrial(mLayer_ind) vectorVolFracIceTrial(1:2) = mLayerVolFracIceTrial(mLayer_ind) ! make appropriate perturbations, From c19473df3a364084827a1e0b78e7c24f9b43cee6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 12 Apr 2022 14:21:51 +0900 Subject: [PATCH 0270/1472] Bug that Martyn found. --- build/source/engine/eval8DAE.f90 | 3 ++- build/source/engine/eval8summa.f90 | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index 4aa0f6f99..6fd8df3b0 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -282,6 +282,7 @@ subroutine eval8DAE(& ! soil parameters theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) ! canopy and layer depth canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) @@ -346,7 +347,7 @@ subroutine eval8DAE(& ! --> minimum if (layerType(iLayer) == iname_soil) then - xMin = theta_sat(iLayer-nSnow) + xMin = theta_res(iLayer-nSnow) else xMin = 0._rkind endif diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index ac8721ac4..2feba21f7 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -236,6 +236,7 @@ subroutine eval8summa(& snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) ! soil parameters theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) ! canopy and layer depth canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) @@ -307,7 +308,7 @@ subroutine eval8summa(& ! --> minimum if (layerType(iLayer) == iname_soil) then - xMin = theta_sat(iLayer-nSnow) + xMin = theta_res(iLayer-nSnow) else xMin = 0._rkind endif From af2c25415fb91305b69040d1527d86f404d0fc62 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 13 Apr 2022 16:53:11 +0900 Subject: [PATCH 0271/1472] fix sundials insturctions --- build/{build_cmakeSundials_mac => build_cmakeSundials} | 0 build/build_cmakeSundials_cop | 5 ----- sundials/installation.txt | 4 ++-- 3 files changed, 2 insertions(+), 7 deletions(-) rename build/{build_cmakeSundials_mac => build_cmakeSundials} (100%) delete mode 100755 build/build_cmakeSundials_cop diff --git a/build/build_cmakeSundials_mac b/build/build_cmakeSundials similarity index 100% rename from build/build_cmakeSundials_mac rename to build/build_cmakeSundials diff --git a/build/build_cmakeSundials_cop b/build/build_cmakeSundials_cop deleted file mode 100755 index bfe906aee..000000000 --- a/build/build_cmakeSundials_cop +++ /dev/null @@ -1,5 +0,0 @@ -# run cp build_cmakeSundials_cop ../../sundials/builddir/build_cmake on this and run from other directory - -cmake ../../sundials-5.8.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir/examples - - diff --git a/sundials/installation.txt b/sundials/installation.txt index 672782e0e..b6e425e27 100644 --- a/sundials/installation.txt +++ b/sundials/installation.txt @@ -16,8 +16,8 @@ NOTE if you need to recompile after a system upgrade, delete the contents of the 4. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, the following setting needs to be enabled while running the cmake inside the builddir, using your home directory as $(YOUR_HOME) % cmake ../sundials-5.8.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON DCMAKE_INSTALL_PREFIX=$(YOUR_HOME)/sundials/instdir -DEXAMPLES_INSTALL_PATH=$(YOUR_HOME)/sundials/instdir/examples -You can do this by run -cp ../../sundials/builddir/build_cmakeSundials_mac build_cmake +You can do this by running +cp ../../sundials/builddir/build_cmakeSundials build_cmake ./build_cmake From 72f90feec92335598352b4f21b5ef0782123d95b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 13 Apr 2022 16:57:34 +0900 Subject: [PATCH 0272/1472] update sundials instructions --- build/build_cmakeSundials | 2 +- sundials/installation.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/build_cmakeSundials b/build/build_cmakeSundials index 7fa01c9a6..0b4fba23c 100755 --- a/build/build_cmakeSundials +++ b/build/build_cmakeSundials @@ -1,5 +1,5 @@ # from ../../sundials/builddir, run -# cp ../../sundials/builddir/build_cmakeSundials_mac build_cmake +# cp ../../summa/build/build_cmakeSundials build_cmake # run script from the builddir directory with ./build_cmake cmake ../../sundials-5.8.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/Users/amedin/Research/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/Users/amedin/Research/SummaSundials/sundials/instdir/examples diff --git a/sundials/installation.txt b/sundials/installation.txt index b6e425e27..79cfbf2c0 100644 --- a/sundials/installation.txt +++ b/sundials/installation.txt @@ -16,8 +16,8 @@ NOTE if you need to recompile after a system upgrade, delete the contents of the 4. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, the following setting needs to be enabled while running the cmake inside the builddir, using your home directory as $(YOUR_HOME) % cmake ../sundials-5.8.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON DCMAKE_INSTALL_PREFIX=$(YOUR_HOME)/sundials/instdir -DEXAMPLES_INSTALL_PATH=$(YOUR_HOME)/sundials/instdir/examples -You can do this by running -cp ../../sundials/builddir/build_cmakeSundials build_cmake +You can do this by running from inside buildir: +cp ../../summa/build/build_cmakeSundials build_cmake ./build_cmake From 0e8f2162af0d52aab13a8a665c4e9e1af8b67c6e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 13 Apr 2022 17:30:41 +0900 Subject: [PATCH 0273/1472] update sundials instructions --- build/build_cmakeSundials_cop | 7 +++++++ build/build_cmakeSundials_gra | 7 +++++++ build/{build_cmakeSundials => build_cmakeSundials_mac} | 2 +- sundials/installation.txt | 4 ++-- 4 files changed, 17 insertions(+), 3 deletions(-) create mode 100755 build/build_cmakeSundials_cop create mode 100755 build/build_cmakeSundials_gra rename build/{build_cmakeSundials => build_cmakeSundials_mac} (85%) diff --git a/build/build_cmakeSundials_cop b/build/build_cmakeSundials_cop new file mode 100755 index 000000000..6d0e22052 --- /dev/null +++ b/build/build_cmakeSundials_cop @@ -0,0 +1,7 @@ +# from ../../sundials/builddir, run +# cp ../../summa/build/build_cmakeSundials_cop build_cmake +# run script from the builddir directory with ./build_cmake + +cmake ../../sundials-5.8.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir/examples + + diff --git a/build/build_cmakeSundials_gra b/build/build_cmakeSundials_gra new file mode 100755 index 000000000..be3d6e209 --- /dev/null +++ b/build/build_cmakeSundials_gra @@ -0,0 +1,7 @@ +# from ../../sundials/builddir, run +# cp ../../summa/build/build_cmakeSundials_gra build_cmake +# run script from the builddir directory with ./build_cmake + +cmake ../../sundials-5.8.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/home/avanb/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/home/avanb/SummaSundials/sundials/instdir/examples + + diff --git a/build/build_cmakeSundials b/build/build_cmakeSundials_mac similarity index 85% rename from build/build_cmakeSundials rename to build/build_cmakeSundials_mac index 0b4fba23c..6df9ed588 100755 --- a/build/build_cmakeSundials +++ b/build/build_cmakeSundials_mac @@ -1,5 +1,5 @@ # from ../../sundials/builddir, run -# cp ../../summa/build/build_cmakeSundials build_cmake +# cp ../../summa/build/build_cmakeSundialsmac_ build_cmake # run script from the builddir directory with ./build_cmake cmake ../../sundials-5.8.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/Users/amedin/Research/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/Users/amedin/Research/SummaSundials/sundials/instdir/examples diff --git a/sundials/installation.txt b/sundials/installation.txt index 79cfbf2c0..dd52ac130 100644 --- a/sundials/installation.txt +++ b/sundials/installation.txt @@ -16,8 +16,8 @@ NOTE if you need to recompile after a system upgrade, delete the contents of the 4. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, the following setting needs to be enabled while running the cmake inside the builddir, using your home directory as $(YOUR_HOME) % cmake ../sundials-5.8.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON DCMAKE_INSTALL_PREFIX=$(YOUR_HOME)/sundials/instdir -DEXAMPLES_INSTALL_PATH=$(YOUR_HOME)/sundials/instdir/examples -You can do this by running from inside buildir: -cp ../../summa/build/build_cmakeSundials build_cmake +You can do this by running from inside buildir, where the star is the system specific file with hardcoded paths: +cp ../../summa/build/build_cmakeSundials_* build_cmake ./build_cmake From d9c3b1f1976ad73d69c9aca41cb566fee06dcbd4 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 13 Apr 2022 17:54:31 +0900 Subject: [PATCH 0274/1472] fixing subset and _gra installation --- build/build_summa_gra | 6 + build/my_makefile_gra | 418 ++++++++++++++++++++++++++++++++++++++++++ utils/subsetGRU.sh | 67 +++++++ 3 files changed, 491 insertions(+) create mode 100755 build/build_summa_gra create mode 100644 build/my_makefile_gra create mode 100644 utils/subsetGRU.sh diff --git a/build/build_summa_gra b/build/build_summa_gra new file mode 100755 index 000000000..c428dfdd5 --- /dev/null +++ b/build/build_summa_gra @@ -0,0 +1,6 @@ +module load StdEnv/2020 +module load gcc/9.3.0 +module load openblas/0.3.17 +module load netcdf-fortran/4.5.2 + +make -f my_makefile_gra diff --git a/build/my_makefile_gra b/build/my_makefile_gra new file mode 100644 index 000000000..ca010b077 --- /dev/null +++ b/build/my_makefile_gra @@ -0,0 +1,418 @@ +#======================================================================== +# Makefile to compile SUMMA SUNDIALS +#======================================================================== +# +# Recommended use: Copy this file to Makefile.local, edit it to your +# heart's content, and then run `make -f build/Makefile.local` from +# your top level SUMMA directory. Don't include the Makefile.local in +# any pull requests you make. +# +# Note that Makefile configurations that we commonly use can be found on +# the SUMMA wiki at: +# https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration +# feel free to add yours to that page. +# +# To troubleshoot your paths and setup, type 'make check' +# +# At a minimum you will need to set the following: +# * F_MASTER - top level summa directory +# * FC - compiler suite +# * FC_EXE - compiler executable +# * INCLUDES - path to include files +# * ILDFLAGS - path to libraries to include +# * LIBRARIES - libraries to include +# * DIR_SUNDIALS - installation sundials directory +# +# Some further options can be specified for OpenMP, etc. See in Part 0 of +# the Makefile. You don't need to make any changes in PART 1 and +# following unless you are doing SUMMA development and changed what +# needs to be compiled + +#======================================================================== +# PART 0: User-configurable part +#======================================================================== + +# The variables can be specified in one of two ways: +# * delete the '##' in front of the variable, fill out the entry, +# save the file and run make +# * make no changes to this file, but specify the variables in your +# environment before you run make + +# Define core directory below which everything resides. This is the +# parent directory of the 'build' directory +F_MASTER =/home/avanb/SummaSundials/summa + +# Define the Fortran Compiler. If you are using gfortran, then this needs +# to be version 6 or higher. This variable is simply used to select the right +# compiler flags in the ifeq statements in this Makefile. The compiler +# executable is set separately as FC_EXE +# Currently this is either gfortran or ifort +FC = gfortran + +# Define the path for the compiler executable. This is the actual executable +# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin/gfortran-mp-11 +# FC and FC_EXE have to be consistent +FC_EXE = gfortran + +# Define the NetCDF and LAPACK libraries and path to include files. +# INCLUDES needs to be of the form (no quotes around the string): +# INCLUDES = -I -I -I<...> -I +# LIBRARIES needs to be of the form ( no quotes around the string): +# LIBRARIES = '-L -lnetcdff -L -lblas -L -l' +# If none of this makes sense, please talk to your system +# administrator. +INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include +LDFLAGS = -L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib +LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas + +DIR_SUNDIALS=/home/avanb/SummaSundials/sundials/instdir +INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran +LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod + +# Eventually we plan move to a real configure script, but for now we like +# to keep track of successful compilations of SUMMA on different platforms +# and with different compilers. If you are successful compiling SUMMA, +# please add your configuration (operating system and compiler plus +# part 0 of the Makefile) to the SUMMA wiki on github. + +# Define compiler flags. If you use a different compiler, +# you will need to figure out what the equivalent flags are +# and may need to update this section + +# ------------ define compiler flags ---------------------------------------- + +# define open MP flags +isOpenMP = +FLAGS_OMP = +LIBOPENMP = + +# Define compiler flags. If you use a different compiler, +# you will need to figure out what the equivalent flags are +# and may need to update this section + +# gfortran compiler flags +ifeq "$(FC)" "gfortran" + + ifeq "$(isOpenMP)" "yes" + FLAGS_OMP = -fopenmp + endif + +# Production runs +FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) + +# Debug runs +#FLAGS_NOAH = -p -g -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument +#FLAGS_COMM = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds +#FLAGS_SUMMA = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds + +endif + +# ifort compiler flags +ifeq "$(FC)" "ifort" + + ifeq "$(isOpenMP)" "yes" + FLAGS_OMP = -qopenmp + endif + +# Production runs +FLAGS_NOAH = -O3 -noerror_limit -FR -auto -fltconsistency $(FLAGS_OMP) +FLAGS_COMM = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) +FLAGS_SUMMA = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) + +# Debug runs +#FLAGS_NOAH = -O0 -p -g -warn nounused -noerror_limit -FR -auto -WB -traceback -fltconsistency +#FLAGS_COMM = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 +#FLAGS_SUMMA = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 +endif + +#======================================================================== +# PART 1: Define directory paths +#======================================================================== + +# Core directory that contains source code +F_KORE_DIR = $(F_MASTER)/build/source + +# Location of the compiled modules +MOD_PATH = $(F_MASTER)/build + +# Define the directory for the executables +EXE_PATH = $(F_MASTER)/bin + +#======================================================================== +# PART 2: Assemble all of the SUMMA sub-routines +#======================================================================== + +# Define directories +DRIVER_DIR = $(F_KORE_DIR)/driver +HOOKUP_DIR = $(F_KORE_DIR)/hookup +NETCDF_DIR = $(F_KORE_DIR)/netcdf +DSHARE_DIR = $(F_KORE_DIR)/dshare +NUMREC_DIR = $(F_KORE_DIR)/numrec +NOAHMP_DIR = $(F_KORE_DIR)/noah-mp +ENGINE_DIR = $(F_KORE_DIR)/engine + +# utilities +SUMMA_NRUTIL= \ + nrtype.f90 \ + f2008funcs.f90 \ + nr_utility.f90 +NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) + +# +# Numerical recipes procedures +# NOTE: all numerical recipes procedures are now replaced with free versions +SUMMA_NRPROC= \ + expIntegral.f90 \ + spline_int.f90 +NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) + +# Hook-up modules (set files and directory paths) +SUMMA_HOOKUP= \ + ascii_util.f90 \ + summaFileManager.f90 + +HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) + +# Data modules +SUMMA_DATAMS= \ + multiconst.f90 \ + var_lookup.f90 \ + data_types.f90 \ + globalData.f90 \ + flxMapping.f90 \ + get_ixname.f90 \ + popMetadat.f90 \ + outpt_stat.f90 +DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) + +# utility modules +SUMMA_UTILMS= \ + time_utils.f90 \ + mDecisions.f90 \ + snow_utils.f90 \ + soil_utils.f90 \ + soil_utilsSundials.f90 \ + updatState.f90 \ + updatStateSundials.f90 \ + matrixOper.f90 +UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) + +# Model guts +SUMMA_MODGUT= \ + MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) + +# Solver +SUMMA_SOLVER= \ + vegPhenlgy.f90 \ + diagn_evar.f90 \ + stomResist.f90 \ + groundwatr.f90 \ + vegSWavRad.f90 \ + vegNrgFlux.f90 \ + ssdNrgFlux.f90 \ + vegLiqFlux.f90 \ + snowLiqFlx.f90 \ + soilLiqFlx.f90 \ + bigAquifer.f90 \ + computFlux.f90 \ + computResid.f90 \ + computJacob.f90 \ + eval8summa.f90 \ + summaSolve.f90 \ + systemSolv.f90 \ + type4IDA.f90 \ + tol4IDA.f90 \ + computEnthalpy.f90 \ + computHeatCap.f90 \ + computThermConduct.f90 \ + computResidDAE.f90 \ + eval8DAE.f90 \ + evalDAE4IDA.f90 \ + computJacDAE.f90 \ + eval8JacDAE.f90 \ + evalJac4IDA.f90 \ + computSnowDepth.f90 \ + solveByIDA.f90 \ + systemSolvSundials.f90 \ + varSubstep.f90 \ + varSubstepSundials.f90 \ + opSplittin.f90 \ + coupled_em.f90 \ + run_oneHRU.f90 \ + run_oneGRU.f90 +SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) + +# Define routines for SUMMA preliminaries +SUMMA_PRELIM= \ + conv_funcs.f90 \ + sunGeomtry.f90 \ + convE2Temp.f90 \ + allocspace.f90 \ + checkStruc.f90 \ + childStruc.f90 \ + ffile_info.f90 \ + read_attrb.f90 \ + read_pinit.f90 \ + pOverwrite.f90 \ + read_param.f90 \ + paramCheck.f90 \ + check_icond.f90 +PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) + +SUMMA_NOAHMP= \ + module_model_constants.F \ + module_sf_noahutl.F \ + module_sf_noahlsm.F \ + module_sf_noahmplsm.F +NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) + +# Define routines for the SUMMA model runs +SUMMA_MODRUN = \ + indexState.f90 \ + getVectorz.f90 \ + varExtrSundials.f90 \ + t2enthalpy.f90 \ + updateVars.f90 \ + updateVarsSundials.f90 \ + updateVars4JacDAE.f90 \ + var_derive.f90 \ + read_force.f90 \ + derivforce.f90 \ + snowAlbedo.f90 \ + canopySnow.f90 \ + tempAdjust.f90 \ + snwCompact.f90 \ + layerMerge.f90 \ + layerDivide.f90 \ + volicePack.f90 \ + qTimeDelay.f90 +MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) + +# Define routines for the solver +SUMMA_MSOLVE = \ + +# Define NetCDF routines +SUMMA_NETCDF = \ + netcdf_util.f90 \ + def_output.f90 \ + modelwrite.f90 \ + read_icond.f90 +NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) + +# ... stitch together common programs +COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) + +# ... stitch together SUMMA programs +SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) + +# Define the driver routine +SUMMA_DRIVER= \ + summa_type.f90 \ + summa_util.f90 \ + summa_alarms.f90 \ + summa_globalData.f90 \ + summa_defineOutput.f90 \ + summa_init.f90 \ + summa_setup.f90 \ + summa_restart.f90 \ + summa_forcing.f90 \ + summa_modelRun.f90 \ + summa_writeOutput.f90 \ + summa_driver.f90 +DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) + +# Define the executable +DRIVER__EX = summa_sundials.exe + +# Define version number +VERSIONFILE = $(DRIVER_DIR)/summaversion.inc +VERSION = $(shell git tag | tail -n 1) +BULTTIM = $(shell date) +GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') +GITHASH = $(shell git rev-parse HEAD) + +#======================================================================== +# PART 3: Checks +#====================================================================== +# make sure that the paths are defined. These are just some high level checks +ifndef F_MASTER + $(error F_MASTER is undefined) +endif +ifndef FC + $(error FC is undefined: Specify your compiler) +endif +ifndef FC_EXE + $(error FC_EXE is undefined: Specify your compiler executable) +endif +ifndef FLAGS_SUMMA + $(error Specify flags for your compiler: $(FC)) +endif +ifndef INCLUDES + $(error INCLUDES is undefined) +endif +ifndef LIBRARIES + $(error LIBRARIES is undefined) +endif + +#======================================================================== +# PART 4: compilation +#====================================================================== + +# Compile +all: compile_noah compile_comm compile_summa link clean install +part: compile_noah compile_comm compile_summa + +check: + $(info) + $(info Displaying make variables:) + $(info F_MASTER : $(F_MASTER)) + $(info EXE_PATH : $(EXE_PATH)) + $(info FC : $(FC)) + $(info INCLUDES : $(INCLUDES)) + $(info LIBRARIES : $(LIBRARIES)) + $(info FLAGS_NOAH : $(FLAGS_NOAH)) + $(info FLAGS_COMM : $(FLAGS_COMM)) + $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) + $(info SUNDIALSDIR: $(DIR_SUNDIALS)) + $(info INC_SUNDIALS: $(INC_SUNDIALS)) + $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) + $(info) + +# update version information +update_version: + echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) + echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) + +# compile Noah-MP routines +compile_noah: + $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) + +# compile common routines + +compile_comm: + $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) + +# compile SUMMA routines +compile_summa: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ + $(INCLUDES) $(INC_SUNDIALS) + +# link routines +link: + $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) + +# Remove object files +clean: + rm -f *.o + rm -f *.mod + rm -f soil_veg_gen_parm__genmod.f90 + +# Copy the executable to the bin directory +install: + @mkdir -p $(EXE_PATH) + @mv $(DRIVER__EX) $(EXE_PATH) + $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) diff --git a/utils/subsetGRU.sh b/utils/subsetGRU.sh new file mode 100644 index 000000000..fed60af8d --- /dev/null +++ b/utils/subsetGRU.sh @@ -0,0 +1,67 @@ +#!/bin/bash +# Subset out a NA HRU forcing, parameter, and attribute files where GRU matches HRU +# +# Inside forcingFile_out need to change forcingPath to = desForcingPath +# Inside forcingFile_out need to change initConditionFile, attributeFile, trialParamFile to = _${GRU_file} versions + +module load StdEnv/2020 +module load gcc/9.3.0 +module load nco/5.0.6 + +# GRU want to subset, change to do another GRU +# first basin in the slurm batch run, 20th script +# could also just set this as GRU_file from G* on output nc files (here the equation output is 4295) +# GRU_file is not the actual GRUid, it is one off from the nc files, so subtract one before ncks +nbasin_slurm=1 +nscript_slurm=20 +GRU_file=$((154 + nbasin_slurm + 207*nscript_slurm)) +GRU_id=$((GRU_file - 1)) +HRU_id=$GRU_id +echo "file name id is ${GRU_file}, actual GRU id is ${GRU_id}" + +# top paths, change these to yours +homeDir=/globalhome/gwu479/HPC/ +desforceDir=/project/gwf/gwf_cmt/gwu479/ +inpforceDir=/project/gwf/gwf_cmt/rez299/ +homePath=${homeDir}TestScripts/ +summa_exe=${homeDir}SummaSundials/summa/bin/summa_sundials.exe +forcingPath=${inpforceDir}summaWorkflow_data/domain_NorthAmerica/forcing/4_SUMMA_input/ + +# in paths, probably won't change +fileManager_in=${homePath}fileManager.txt +settingsPath=${homePath}settings/ +initConditionFile_in=${settingsPath}coldState.nc +attributeFile_in=${settingsPath}attributes.nc +trialParamFile_in=${settingsPath}trialParams.nc + +# out paths, probably won't change +fileManager_out=${homePath}fileManager_${GRU_file}.txt +initConditionFile_out=${settingsPath}coldState_${GRU_file}.nc +attributeFile_out=${settingsPath}attributes_${GRU_file}.nc +trialParamFile_out=${settingsPath}trialParams_${GRU_file}.nc +desForcingPath=${desforceDir}summaWorkflow_data/domain_NorthAmerica/forcing/4_SUMMA_input_${GRU_file}/ + +# set up directory and new file Manager (will have to change things in it manually as above) +mkdir -p "$desForcingPath" +cp $fileManager_in $fileManager_out + +# do the subset +ncks -d hru,$HRU_id,$HRU_id $initConditionFile_in $initConditionFile_out +echo "coldState.nc HRU ${HRU_id} subsetted" +ncks -d gru,$GRU_id,$GRU_id -d hru,$HRU_id,$HRU_id $attributeFile_in $attributeFile_out +echo "attributes.nc GRU ${GRU_id} HRU ${HRU_id} subsetted" +ncks -d hru,$HRU_id,$HRU_id $trialParamFile_in $trialParamFile_out +echo "trialParams.nc HRU ${HRU_id} subsetted" + +# forcing subset has multiple files +cd $forcingPath +for fn in *.nc; do + output_fn=${desForcingPath}${fn} + ncks -d hru,$HRU_id,$HRU_id $fn $output_fn + echo "${fn} HRU ${HRU_id} subsetted" +done +cd $homePath + +# write summa command call file +runFile=${homePath}run_${GRU_file}.sh +echo "${summa_exe} -p never -s _testSumma -m ${fileManager_out} -r e" > $runFile From 38ecbebafce511df2ed736c683f5321bffb0b4f7 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 15 Apr 2022 15:11:17 +0900 Subject: [PATCH 0275/1472] add error counter to utilities --- utils/SUMMA_summarize_logs.py | 194 ++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 utils/SUMMA_summarize_logs.py diff --git a/utils/SUMMA_summarize_logs.py b/utils/SUMMA_summarize_logs.py new file mode 100644 index 000000000..df1952be2 --- /dev/null +++ b/utils/SUMMA_summarize_logs.py @@ -0,0 +1,194 @@ +'''Summarize all SUMMA logs in a folder. Assumes all .txt files in folder are SUMMA logs. +Summary file is placed inside the log folder. Specifying a summary file name is optional. +Usage: python summarize_logs.py [log_folder] [name_of_summary_file.txt] [log file extension]''' + +# Modules +import os +import re +import sys +import statistics as sts + +# ---------------------- +# Set defaults +summaryFile = '_log_summary.txt' # default, placed at the top of the log folder +ext = '.txt' + +# Handle input arguments +if len(sys.argv) == 1: # sys.argv only contains the script name + sys.exit('Error: no input folder specified') + +else: # at least 2 elements in sys.argv; len(sys.argv) cannot be zero or we wouldn't be in this script + + # The first input argument specifies the folder where the log files are + folder = sys.argv[1] # sys.argv values are strings by default so this is fine + + # Check if there are more arguments + if len(sys.argv) == 3: + + # Assume the second argument is the name for the log file + summaryFile = sys.argv[2] # string + + # No extra argument so no summary file name is specified + elif len(sys.argv) == 4: + + # Assume the second argument is the name for the log file and the third is the file extension + summaryFile = sys.argv[2] # string + ext = sys.argv[3] # string + +# End of input arguments +# ---------------------- + +# ------------- +# Sub functions + +# Define a function to grab the last line in a file +# See: https://stackoverflow.com/questions/136168/get-last-n-lines-of-a-file-similar-to-tail +def tail(folder, file, lines=1, _buffer=4098): + + """Tail a file and get X lines from the end""" + + # open the file + with open(folder + '/' + file,'r') as f: + + # place holder for the lines found + lines_found = [] + + # block counter will be multiplied by buffer to get the block size from the end + block_counter = -1 + + # loop until we find X lines + while len(lines_found) < lines: + try: + f.seek(block_counter * _buffer, os.SEEK_END) + except IOError: # either file is too small, or too many lines requested + f.seek(0) + lines_found = f.readlines() + break + + lines_found = f.readlines() + + # decrement the block counter to get the next X bytes + block_counter -= 1 + + return lines_found[-lines:] + +# Define a function to handle the three types of log results: success, SUMMA error, non-SUMMA error +def determine_output(folder,file,nLines=2): + + # initialize outputs + success, summa, other = [0,0,0] # error flags + time = -1 # times are only extracted for successful simulations - a default negative time allows filtering outside this function + + # get the file contents + log_txt = tail(folder,file,nLines) + + # loop over the lines in the log text to find what happened + msg = 'no line read' # initialize output + for line in log_txt: + + # determine if the log contains a SUMMA statement + if 'successfully' in line: + + # determine time taken + more_lines = tail(folder,file,6) # extracting bottom 6 lines includes time in [h] + time = float(re.sub("[^\d\.]", "", more_lines[0])) # hours should be in top entry; store as float + + # process output flags + success = 1 + msg = 'success after ' + '{:.2f}'.format(time) + ' h \n' # message string + return success, summa, other, msg, time # we know what happened, stop function call + + elif 'FATAL ERROR' in line: + summa = 1 + msg = line + return success, summa, other, msg, time # we know what happened, stop function call + + # if we reach this, no SUMMA termination statement was found + other = 1 + msg = 'check SLURM logs - simulation terminated early at: ' + line + + return success, summa, other, msg, time + +# End of sub functions +# -------------------- + +# ------------------- +# Start of processing + +# Remove the summar file if it exists +try: + os.remove(folder + '/' + summaryFile) +except OSError: + pass + +# Find the .txt files in the folder +files = [] +for file in os.listdir(folder): + #if file.endswith(".txt"): + if file.endswith(ext): + files.append(file) + +# Sort the list +files.sort() + +# Initialize case counters +total_success, total_summa, total_other = [0,0,0] + +# Initialize time list +computation_time = [] + +# Open the summary file +with open(folder + '/' + summaryFile, 'w') as sf: + + # Add a header + sf.write('Summarizing log files in ' + folder + '\n \n') + sf.write('Log files' + '\n') + + # Loop over the log files + for file in files: + + # Find the result contained in each log file + success, summa, other, msg, time = determine_output(folder,file) # default of using last 2 lines should suffice to catch all success/error cases + + # Increment the counters + total_success += success + total_summa += summa + total_other += other + + # Save the computation time (time == -1 if error encountered) + computation_time.append(time) + + # Add the file name and error message to the summary file + sf.write(file + '\t' + msg) + + # Calculate percentages + total = total_success + total_summa + total_other + pct_success = total_success / total * 100 + pct_summa = total_summa / total * 100 + pct_other = total_other / total * 100 + + # Calculate computation time stats + computation_time = [time for time in computation_time if time >= 0] # remove negative times that are associated with simulations that return an error + st_min = min(computation_time) + st_max = max(computation_time) + st_mean = sts.mean(computation_time) + st_median = sts.median(computation_time) + + # add a statistical summary + sf.write('\nSuccess stats' + '\n') + sf.write('Success' + '\t \t \t \t {:.2f}% \n'.format(pct_success)) + sf.write('SUMMA error' + '\t \t \t {:.2f}% \n'.format(pct_summa)) + sf.write('Early termination' + '\t {:.2f}% \n'.format(pct_other)) + sf.write('\nTime needed for successful computations' + '\n') + sf.write('Min time ' + '\t \t \t {:.2f} h \n'.format(st_min)) + sf.write('Median time ' + '\t \t {:.2f} h \n'.format(st_median)) + sf.write('Mean time ' + '\t \t \t {:.2f} h \n'.format(st_mean)) + sf.write('Max time ' + '\t \t \t {:.2f} h \n'.format(st_max)) + + + #sf.write('Min time ' + '\t \t \t \t' + str(min(computation_time)) +'h \n') + #sf.write('Median time ' + '\t \t \t' + str(sts.median(computation_time)) +'h \n') + #sf.write('Mean time ' + '\t \t \t \t' + str(sts.mean(computation_time)) +'h \n') + #sf.write('Max time ' + '\t \t \t \t' + str(max(computation_time)) +'h \n') +# done, summary file is closed +# ---------------------------- \ No newline at end of file From 77074d6d409b766246a09ac80c1476f2b6393db0 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 18 Apr 2022 14:32:35 +0900 Subject: [PATCH 0276/1472] Fix iden_ice error in Jacobian in snow layers--- corrected to iden_water --- build/source/engine/computJacDAE.f90 | 5 ++--- utils/subsetGRU.sh | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 4199b3046..2946040b4 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -74,7 +74,6 @@ module computJacDAE_module ! constants USE multiconst,only:& LH_fus, & ! latent heat of fusion (J kg-1) - iden_ice, & ! intrinsic density of ice (kg m-3) iden_water, & ! intrinsic density of liquid water (kg m-3) ! specific heat Cp_ice, & ! specific heat of ice (J kg-1 K-1) @@ -504,10 +503,10 @@ subroutine computJacDAE(& if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) ! - include derivatives of energy fluxes w.r.t water fluxes for current layer - aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_ice * cj & + aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) & + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) & - + LH_fus*iden_ice * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) + + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) ! - include derivatives of water fluxes w.r.t energy fluxes for current layer aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) diff --git a/utils/subsetGRU.sh b/utils/subsetGRU.sh index fed60af8d..ff2e90c11 100644 --- a/utils/subsetGRU.sh +++ b/utils/subsetGRU.sh @@ -55,7 +55,7 @@ echo "trialParams.nc HRU ${HRU_id} subsetted" # forcing subset has multiple files cd $forcingPath -for fn in *.nc; do +for fn in NorthAmerica_remapped_*00-00-00.nc; do output_fn=${desForcingPath}${fn} ncks -d hru,$HRU_id,$HRU_id $fn $output_fn echo "${fn} HRU ${HRU_id} subsetted" From 4b8ea5f29195f8680397421fc05e4928ba191b96 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 18 Apr 2022 22:34:27 +0900 Subject: [PATCH 0277/1472] Fixing veg heat capacity derivative --- build/source/engine/computHeatCap.f90 | 2 +- build/source/engine/computJacDAE.f90 | 3 ++- build/source/engine/updateVars4JacDAE.f90 | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index 04bf46225..615e1d350 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -379,7 +379,7 @@ subroutine computHeatCapAnalytic(& ! * soil case(iname_soil) mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component - iden_ice * Cp_Ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component case(iname_snow) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 2946040b4..4079d3904 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -74,6 +74,7 @@ module computJacDAE_module ! constants USE multiconst,only:& LH_fus, & ! latent heat of fusion (J kg-1) + iden_ice, & ! intrinsic density of ice (kg m-3) iden_water, & ! intrinsic density of liquid water (kg m-3) ! specific heat Cp_ice, & ! specific heat of ice (J kg-1 K-1) @@ -327,7 +328,7 @@ subroutine computJacDAE(& dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj & + dVolHtCapBulk_dTk(iLayer) * mLayerTempPrime(iLayer) & + LH_fus*iden_water * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & - + LH_fus*iden_water * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) + + LH_fus*iden_water * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) end if end do diff --git a/build/source/engine/updateVars4JacDAE.f90 b/build/source/engine/updateVars4JacDAE.f90 index 219e1f33c..570fe9c74 100644 --- a/build/source/engine/updateVars4JacDAE.f90 +++ b/build/source/engine/updateVars4JacDAE.f90 @@ -451,7 +451,7 @@ subroutine updateVars4JacDAE(& select case(ixDomainType) case(iname_veg) fLiq = fracLiquid(xTemp,snowfrz_scale) - dVolHtCapBulk_dCanWat = scalarCanopyLiq/canopyDepth * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + dVolHtCapBulk_dCanWat = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) case(iname_snow) fLiq = fracLiquid(xTemp,snowfrz_scale) dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air @@ -481,7 +481,7 @@ subroutine updateVars4JacDAE(& dFracLiqVeg_dTkCanopy = dFracLiq_dTk(xTemp,snowfrz_scale) dTheta_dTkCanopy = dFracLiqVeg_dTkCanopy * scalarCanopyWatTrial/(iden_water*canopyDepth) d2Theta_dTkCanopy2 = 2._rkind * snowfrz_scale**2._rkind * ( (Tfreeze - xTemp) * 2._rkind * fLiq * dFracLiqVeg_dTkCanopy - fLiq**2._rkind ) * scalarCanopyWatTrial/(iden_water*canopyDepth) - dVolHtCapBulk_dTkCanopy = scalarCanopyLiq/canopyDepth * (-Cp_ice + Cp_water) * dTheta_dTkCanopy !same as snow but there is no derivative in air + dVolHtCapBulk_dTkCanopy = iden_water * (-Cp_ice + Cp_water) * dTheta_dTkCanopy !same as snow but there is no derivative in air case(iname_snow) fLiq = fracLiquid(xTemp,snowfrz_scale) dFracLiqSnow_dTk(iLayer) = dFracLiq_dTk(xTemp,snowfrz_scale) From f2392b566db5f751f26d78b8eec2a2c3ae0dc709 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 19 Apr 2022 11:13:06 +0900 Subject: [PATCH 0278/1472] just prettify --- build/source/engine/vegNrgFlux.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 66999559f..6de454a9c 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -1435,8 +1435,8 @@ subroutine vegNrgFlux(& (-Cp_ice)*(scalarSnowfall - scalarThroughfallSnow)*(canopyTempTrial - scalarTwetbulb) scalarGroundAdvectiveHeatFlux = -Cp_water*scalarThroughfallRain*(groundTempTrial - scalarTwetbulb) + & (-Cp_ice)*scalarThroughfallSnow*(groundTempTrial - scalarTwetbulb) !+ & - ! -Cp_water*scalarCanopyLiqDrainage *(groundTempTrial - canopyTempTrial) + & - ! -Cp_ice *scalarCanopySnowUnloading*(groundTempTrial - canopyTempTrial) + ! (-Cp_water)*scalarCanopyLiqDrainage *(groundTempTrial - canopyTempTrial) + & + ! (-Cp_ice) *scalarCanopySnowUnloading*(groundTempTrial - canopyTempTrial) !print*, 'scalarRainfall, scalarThroughfallRain, scalarSnowfall, scalarThroughfallSnow = ', scalarRainfall, scalarThroughfallRain, scalarSnowfall, scalarThroughfallSnow !print*, 'scalarCanopyAdvectiveHeatFlux, scalarGroundAdvectiveHeatFlux = ', scalarCanopyAdvectiveHeatFlux, scalarGroundAdvectiveHeatFlux From 0c176e9768c48a34d47b4c4e02fc585cfd2ea8ed Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 19 Apr 2022 12:12:51 +0900 Subject: [PATCH 0279/1472] finishing merge --- build/Makefile | 418 ----------------------------- build/source/engine/coupled_em.f90 | 13 +- 2 files changed, 1 insertion(+), 430 deletions(-) delete mode 100644 build/Makefile diff --git a/build/Makefile b/build/Makefile deleted file mode 100644 index 5f73aa387..000000000 --- a/build/Makefile +++ /dev/null @@ -1,418 +0,0 @@ -#======================================================================== -# Makefile to compile SUMMA SUNDIALS -#======================================================================== -# -# Recommended use: Copy this file to Makefile.local, edit it to your -# heart's content, and then run `make -f build/Makefile.local` from -# your top level SUMMA directory. Don't include the Makefile.local in -# any pull requests you make. -# -# Note that Makefile configurations that we commonly use can be found on -# the SUMMA wiki at: -# https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration -# feel free to add yours to that page. -# -# To troubleshoot your paths and setup, type 'make check' -# -# At a minimum you will need to set the following: -# * F_MASTER - top level summa directory -# * FC - compiler suite -# * FC_EXE - compiler executable -# * INCLUDES - path to include files -# * ILDFLAGS - path to libraries to include -# * LIBRARIES - libraries to include -# * DIR_SUNDIALS - installation sundials directory -# -# Some further options can be specified for OpenMP, etc. See in Part 0 of -# the Makefile. You don't need to make any changes in PART 1 and -# following unless you are doing SUMMA development and changed what -# needs to be compiled - -#======================================================================== -# PART 0: User-configurable part -#======================================================================== - -# The variables can be specified in one of two ways: -# * delete the '##' in front of the variable, fill out the entry, -# save the file and run make -# * make no changes to this file, but specify the variables in your -# environment before you run make - -# Define core directory below which everything resides. This is the -# parent directory of the 'build' directory -F_MASTER = /home/summa - -# Define the Fortran Compiler. If you are using gfortran, then this needs -# to be version 6 or higher. This variable is simply used to select the right -# compiler flags in the ifeq statements in this Makefile. The compiler -# executable is set separately as FC_EXE -# Currently this is either gfortran or ifort -FC = gfortran - -# Define the path for the compiler executable. This is the actual executable -# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin/gfortran-mp-11 -# FC and FC_EXE have to be consistent -FC_EXE =/opt/local/bin/gfortran - -# Define the NetCDF and LAPACK libraries and path to include files. -# INCLUDES needs to be of the form (no quotes around the string): -# INCLUDES = -I -I -I<...> -I -# LIBRARIES needs to be of the form ( no quotes around the string): -# LIBRARIES = '-L -lnetcdff -L -lblas -L -l' -# If none of this makes sense, please talk to your system -# administrator. -INCLUDES = -I/opt/local/include -I/opt/local/lib -LDFLAGS = -L/opt/local/lib -LIBRARIES = $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf - -DIR_SUNDIALS=/home/sundials/instdir -INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran -LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod - -# Eventually we plan move to a real configure script, but for now we like -# to keep track of successful compilations of SUMMA on different platforms -# and with different compilers. If you are successful compiling SUMMA, -# please add your configuration (operating system and compiler plus -# part 0 of the Makefile) to the SUMMA wiki on github. - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# ------------ define compiler flags ---------------------------------------- - -# define open MP flags -isOpenMP = -FLAGS_OMP = -LIBOPENMP = - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# gfortran compiler flags -ifeq "$(FC)" "gfortran" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -fopenmp - endif - -# Production runs -FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) - -# Debug runs -#FLAGS_NOAH = -p -g -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -#FLAGS_COMM = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -#FLAGS_SUMMA = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds - -endif - -# ifort compiler flags -ifeq "$(FC)" "ifort" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -qopenmp - endif - -# Production runs -FLAGS_NOAH = -O3 -noerror_limit -FR -auto -fltconsistency $(FLAGS_OMP) -FLAGS_COMM = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) - -# Debug runs -#FLAGS_NOAH = -O0 -p -g -warn nounused -noerror_limit -FR -auto -WB -traceback -fltconsistency -#FLAGS_COMM = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -#FLAGS_SUMMA = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -endif - -#======================================================================== -# PART 1: Define directory paths -#======================================================================== - -# Core directory that contains source code -F_KORE_DIR = $(F_MASTER)/build/source - -# Location of the compiled modules -MOD_PATH = $(F_MASTER)/build - -# Define the directory for the executables -EXE_PATH = $(F_MASTER)/bin - -#======================================================================== -# PART 2: Assemble all of the SUMMA sub-routines -#======================================================================== - -# Define directories -DRIVER_DIR = $(F_KORE_DIR)/driver -HOOKUP_DIR = $(F_KORE_DIR)/hookup -NETCDF_DIR = $(F_KORE_DIR)/netcdf -DSHARE_DIR = $(F_KORE_DIR)/dshare -NUMREC_DIR = $(F_KORE_DIR)/numrec -NOAHMP_DIR = $(F_KORE_DIR)/noah-mp -ENGINE_DIR = $(F_KORE_DIR)/engine - -# utilities -SUMMA_NRUTIL= \ - nrtype.f90 \ - f2008funcs.f90 \ - nr_utility.f90 -NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) - -# -# Numerical recipes procedures -# NOTE: all numerical recipes procedures are now replaced with free versions -SUMMA_NRPROC= \ - expIntegral.f90 \ - spline_int.f90 -NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) - -# Hook-up modules (set files and directory paths) -SUMMA_HOOKUP= \ - ascii_util.f90 \ - summaFileManager.f90 - -HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) - -# Data modules -SUMMA_DATAMS= \ - multiconst.f90 \ - var_lookup.f90 \ - data_types.f90 \ - globalData.f90 \ - flxMapping.f90 \ - get_ixname.f90 \ - popMetadat.f90 \ - outpt_stat.f90 -DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) - -# utility modules -SUMMA_UTILMS= \ - time_utils.f90 \ - mDecisions.f90 \ - snow_utils.f90 \ - soil_utils.f90 \ - soil_utilsSundials.f90 \ - updatState.f90 \ - updatStateSundials.f90 \ - matrixOper.f90 -UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) - -# Model guts -SUMMA_MODGUT= \ - MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) - -# Solver -SUMMA_SOLVER= \ - vegPhenlgy.f90 \ - diagn_evar.f90 \ - stomResist.f90 \ - groundwatr.f90 \ - vegSWavRad.f90 \ - vegNrgFlux.f90 \ - ssdNrgFlux.f90 \ - vegLiqFlux.f90 \ - snowLiqFlx.f90 \ - soilLiqFlx.f90 \ - bigAquifer.f90 \ - computFlux.f90 \ - computResid.f90 \ - computJacob.f90 \ - eval8summa.f90 \ - summaSolve.f90 \ - systemSolv.f90 \ - type4IDA.f90 \ - tol4IDA.f90 \ - computEnthalpy.f90 \ - computHeatCap.f90 \ - computThermConduct.f90 \ - computResidDAE.f90 \ - eval8DAE.f90 \ - evalDAE4IDA.f90 \ - computJacDAE.f90 \ - eval8JacDAE.f90 \ - evalJac4IDA.f90 \ - computSnowDepth.f90 \ - solveByIDA.f90 \ - systemSolvSundials.f90 \ - varSubstep.f90 \ - varSubstepSundials.f90 \ - opSplittin.f90 \ - coupled_em.f90 \ - run_oneHRU.f90 \ - run_oneGRU.f90 -SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) - -# Define routines for SUMMA preliminaries -SUMMA_PRELIM= \ - conv_funcs.f90 \ - sunGeomtry.f90 \ - convE2Temp.f90 \ - allocspace.f90 \ - checkStruc.f90 \ - childStruc.f90 \ - ffile_info.f90 \ - read_attrb.f90 \ - read_pinit.f90 \ - pOverwrite.f90 \ - read_param.f90 \ - paramCheck.f90 \ - check_icond.f90 -PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) - -SUMMA_NOAHMP= \ - module_model_constants.F \ - module_sf_noahutl.F \ - module_sf_noahlsm.F \ - module_sf_noahmplsm.F -NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) - -# Define routines for the SUMMA model runs -SUMMA_MODRUN = \ - indexState.f90 \ - getVectorz.f90 \ - varExtrSundials.f90 \ - t2enthalpy.f90 \ - updateVars.f90 \ - updateVarsSundials.f90 \ - updateVars4JacDAE.f90 \ - var_derive.f90 \ - read_force.f90 \ - derivforce.f90 \ - snowAlbedo.f90 \ - canopySnow.f90 \ - tempAdjust.f90 \ - snwCompact.f90 \ - layerMerge.f90 \ - layerDivide.f90 \ - volicePack.f90 \ - qTimeDelay.f90 -MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) - -# Define routines for the solver -SUMMA_MSOLVE = \ - -# Define NetCDF routines -SUMMA_NETCDF = \ - netcdf_util.f90 \ - def_output.f90 \ - modelwrite.f90 \ - read_icond.f90 -NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) - -# ... stitch together common programs -COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) - -# ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) - -# Define the driver routine -SUMMA_DRIVER= \ - summa_type.f90 \ - summa_util.f90 \ - summa_alarms.f90 \ - summa_globalData.f90 \ - summa_defineOutput.f90 \ - summa_init.f90 \ - summa_setup.f90 \ - summa_restart.f90 \ - summa_forcing.f90 \ - summa_modelRun.f90 \ - summa_writeOutput.f90 \ - summa_driver.f90 -DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) - -# Define the executable -DRIVER__EX = summa_sundials.exe - -# Define version number -VERSIONFILE = $(DRIVER_DIR)/summaversion.inc -BULTTIM = $(shell date) -GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') -GITHASH = $(shell git rev-parse HEAD) -VERSION = $(shell git show-ref --tags | grep $GITHASH | sed 's/.*tags\///' | grep . || echo "undefined") - -#======================================================================== -# PART 3: Checks -#====================================================================== -# make sure that the paths are defined. These are just some high level checks -ifndef F_MASTER - $(error F_MASTER is undefined) -endif -ifndef FC - $(error FC is undefined: Specify your compiler) -endif -ifndef FC_EXE - $(error FC_EXE is undefined: Specify your compiler executable) -endif -ifndef FLAGS_SUMMA - $(error Specify flags for your compiler: $(FC)) -endif -ifndef INCLUDES - $(error INCLUDES is undefined) -endif -ifndef LIBRARIES - $(error LIBRARIES is undefined) -endif - -#======================================================================== -# PART 4: compilation -#====================================================================== - -# Compile -all: compile_noah compile_comm compile_summa link clean install -part: compile_noah compile_comm compile_summa - -check: - $(info) - $(info Displaying make variables:) - $(info F_MASTER : $(F_MASTER)) - $(info EXE_PATH : $(EXE_PATH)) - $(info FC : $(FC)) - $(info INCLUDES : $(INCLUDES)) - $(info LIBRARIES : $(LIBRARIES)) - $(info FLAGS_NOAH : $(FLAGS_NOAH)) - $(info FLAGS_COMM : $(FLAGS_COMM)) - $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) - $(info SUNDIALSDIR: $(DIR_SUNDIALS)) - $(info INC_SUNDIALS: $(INC_SUNDIALS)) - $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) - $(info) - -# update version information -update_version: - echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) - echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) - -# compile Noah-MP routines -compile_noah: - $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) - -# compile common routines - -compile_comm: - $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) - -# compile SUMMA routines -compile_summa: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ - $(INCLUDES) $(INC_SUNDIALS) - -# link routines -link: - $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) - -# Remove object files -clean: - rm -f *.o - rm -f *.mod - rm -f soil_veg_gen_parm__genmod.f90 - -# Copy the executable to the bin directory -install: - @mkdir -p $(EXE_PATH) - @mv $(DRIVER__EX) $(EXE_PATH) - $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index c713b5345..742eaeb8c 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -138,19 +138,8 @@ subroutine coupled_em(& USE tempAdjust_module,only:tempAdjust ! adjust snow temperature associated with new snowfall USE snwDensify_module,only:snwDensify ! snow densification (compaction and cavitation) USE var_derive_module,only:calcHeight ! module to calculate height at layer interfaces and layer mid-point -<<<<<<< HEAD USE computSnowDepth_module,only:computSnowDepth - ! look-up values for the numerical method - USE mDecisions_module,only: & - iterative, & ! iterative - nonIterative, & ! non-iterative - iterSurfEnergyBal ! iterate only on the surface energy balance -======= ->>>>>>> d5038836bf993ea010c858721b63b2923999d3a7 - ! look-up values for the maximum interception capacity - USE mDecisions_module,only: & - stickySnow, & ! maximum interception capacity an increasing function of temerature - lightSnow ! maximum interception capacity an inverse function of new snow density + implicit none ! model control integer(8),intent(in) :: hruId ! hruId From b2dd12df814c6af83e853bb0267de8af16c5ec04 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 19 Apr 2022 12:20:56 +0900 Subject: [PATCH 0280/1472] finish merge, compiles and runs --- build/source/engine/coupled_em.f90 | 9 --------- 1 file changed, 9 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 742eaeb8c..ca31032ac 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -242,15 +242,6 @@ subroutine coupled_em(& ! get the start time call cpu_time(startTime) -<<<<<<< HEAD -======= - ! check the sundials decision - if(model_decisions(iLookDECISIONS%num_method)%iDecision==sundials)then - message=trim(message)//'still need to implement the sundials solver' - err=20; return - endif - ->>>>>>> d5038836bf993ea010c858721b63b2923999d3a7 ! check that the decision is supported if(model_decisions(iLookDECISIONS%groundwatr)%iDecision==bigBucket .and. & model_decisions(iLookDECISIONS%spatial_gw)%iDecision/=localColumn)then From caab1d2b326c581407dec669c1be4036ae3edd35 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 21 Apr 2022 09:07:55 +0900 Subject: [PATCH 0281/1472] cleaning up JacDAE, subsetGRU utility --- build/source/engine/computJacDAE.f90 | 3 +-- utils/subsetGRU.sh | 21 +++++++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 4079d3904..df36c9586 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -74,7 +74,6 @@ module computJacDAE_module ! constants USE multiconst,only:& LH_fus, & ! latent heat of fusion (J kg-1) - iden_ice, & ! intrinsic density of ice (kg m-3) iden_water, & ! intrinsic density of liquid water (kg m-3) ! specific heat Cp_ice, & ! specific heat of ice (J kg-1 K-1) @@ -318,7 +317,7 @@ subroutine computJacDAE(& dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy ) * cj & + dVolHtCapBulk_dTkCanopy * scalarCanopyTempPrime & + LH_fus*iden_water * scalarCanopyTempPrime * d2Theta_dTkCanopy2 & - + LH_fus * dFracLiqVeg_dTkCanopy * scalarCanopyWatPrime / canopyDepth ! volumetric heat capacity of the vegetation (J m-3 K-1) + + LH_fus * dFracLiqVeg_dTkCanopy * scalarCanopyWatPrime / canopyDepth end if ! compute additional terms for the Jacobian for the snow-soil domain (excluding fluxes) diff --git a/utils/subsetGRU.sh b/utils/subsetGRU.sh index ff2e90c11..793f4daad 100644 --- a/utils/subsetGRU.sh +++ b/utils/subsetGRU.sh @@ -9,13 +9,22 @@ module load gcc/9.3.0 module load nco/5.0.6 # GRU want to subset, change to do another GRU -# first basin in the slurm batch run, 20th script -# could also just set this as GRU_file from G* on output nc files (here the equation output is 4295) +# 155th basin in the slurm batch run, offset-th script (offset is SLURM_ARRAY_TASK_ID +# could also just set this as GRU_file from G* on output nc files (here the equation output is 487981). # GRU_file is not the actual GRUid, it is one off from the nc files, so subtract one before ncks -nbasin_slurm=1 -nscript_slurm=20 -GRU_file=$((154 + nbasin_slurm + 207*nscript_slurm)) -GRU_id=$((GRU_file - 1)) + +offset=942 +gruCount=518 + +# If know basin number in batch, add here, or comment out and use next if know GRU_id from the failure message +#nbasin_slurm=25 +#GRU_file=$((nbasin_slurm + gruCount*offset)) +#GRU_id=$((GRU_file - 1)) + +GRU_id=487980 +GRU_file=$((GRU_id+1)) +nbasin_slurm=$((GRU_file+1-gruCount*offset)) + HRU_id=$GRU_id echo "file name id is ${GRU_file}, actual GRU id is ${GRU_id}" From 65b14ee62883bcec7abf1baf757d3d33cf9fa1b8 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 22 Apr 2022 08:40:02 +0900 Subject: [PATCH 0282/1472] Fix heat capacity with canopy water deriv --- build/source/engine/updateVars4JacDAE.f90 | 2 +- utils/subsetGRU.sh | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/build/source/engine/updateVars4JacDAE.f90 b/build/source/engine/updateVars4JacDAE.f90 index 570fe9c74..c8cc1204c 100644 --- a/build/source/engine/updateVars4JacDAE.f90 +++ b/build/source/engine/updateVars4JacDAE.f90 @@ -451,7 +451,7 @@ subroutine updateVars4JacDAE(& select case(ixDomainType) case(iname_veg) fLiq = fracLiquid(xTemp,snowfrz_scale) - dVolHtCapBulk_dCanWat = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) case(iname_snow) fLiq = fracLiquid(xTemp,snowfrz_scale) dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air diff --git a/utils/subsetGRU.sh b/utils/subsetGRU.sh index 793f4daad..16b72598e 100644 --- a/utils/subsetGRU.sh +++ b/utils/subsetGRU.sh @@ -16,15 +16,14 @@ module load nco/5.0.6 offset=942 gruCount=518 -# If know basin number in batch, add here, or comment out and use next if know GRU_id from the failure message +# If know basin number in batch, add here, or comment out and use next if know GRU_file from the failure message id #nbasin_slurm=25 #GRU_file=$((nbasin_slurm + gruCount*offset)) -#GRU_id=$((GRU_file - 1)) -GRU_id=487980 -GRU_file=$((GRU_id+1)) -nbasin_slurm=$((GRU_file+1-gruCount*offset)) +GRU_file=487980 +nbasin_slurm=$((GRU_file-gruCount*offset)) +GRU_id=$((GRU_file - 1)) HRU_id=$GRU_id echo "file name id is ${GRU_file}, actual GRU id is ${GRU_id}" From f26d7a42564a764a46102bab72af86c98e4e9d08 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 25 Apr 2022 13:10:59 +0900 Subject: [PATCH 0283/1472] Fixing ground net flux for prescribed temp --- build/source/engine/ssdNrgFlux.f90 | 42 +++++++++++++++--------------- utils/subsetGRU.sh | 2 +- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index 946079fd7..e8f4ee7ab 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -498,16 +498,16 @@ subroutine ssdNrgFlux(& ! * prescribed temperature at the upper boundary case(prescribedTemp) - dz = (mLayerHeight(1) - mLayerHeight(iLayer)) + dz = (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) if(ixDerivMethod==analytical)then ! ** analytical derivatives - dFlux_dWatBelow(iLayer) = -dThermalC_dHydStateBelow * ( mLayerTempTrial(1) - upperBoundTemp )/dz - dFlux_dTempBelow(iLayer) = -dThermalC_dNrgStateBelow * ( mLayerTempTrial(1) - upperBoundTemp )/dz - iLayerThermalC(iLayer)/dz + dFlux_dWatBelow(iLayer) = -dThermalC_dHydStateBelow * ( mLayerTempTrial(iLayer+1) - upperBoundTemp )/dz + dFlux_dTempBelow(iLayer) = -dThermalC_dNrgStateBelow * ( mLayerTempTrial(iLayer+1) - upperBoundTemp )/dz - iLayerThermalC(iLayer)/dz else ! ** numerical derivatives - flux0 = -scalarThermCFlux *( mLayerTempTrial(1) - upperBoundTemp ) / dz - flux2 = -scalarThermCFlux_dWatBelow*( mLayerTempTrial(1) - upperBoundTemp ) / dz + flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - upperBoundTemp ) / dz + flux2 = -scalarThermCFlux_dWatBelow*( mLayerTempTrial(iLayer+1) - upperBoundTemp ) / dz dFlux_dWatBelow(iLayer) = (flux2 - flux0)/dx - flux0 = -scalarThermCFlux *( mLayerTempTrial(1) - upperBoundTemp ) / dz - flux2 = -scalarThermCFlux_dTempBelow*((mLayerTempTrial(1)+dx) - upperBoundTemp ) / dz + flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - upperBoundTemp ) / dz + flux2 = -scalarThermCFlux_dTempBelow*((mLayerTempTrial(iLayer+1)+dx) - upperBoundTemp ) / dz dFlux_dTempBelow(iLayer) = (flux2 - flux0)/dx end if @@ -584,28 +584,29 @@ subroutine ssdNrgFlux(& end do ! (looping through layers) ! ------------------------------------------------------------------------------------------------------------------------- + ! ***** compute the conductive fluxes at layer interfaces ***** ! Compute flux after the derivatives, because need iLayerThermal as calculated above ! ------------------------------------------------------------------------------------------------------------------------- - ! get the indices for the snow+soil layers, remove layer 0 now - if(.not.scalarSolution) ixTop = 1 - - ! ------------------------------------------------------------------------------------------------------------------------- - ! ***** compute the conductive fluxes at layer interfaces ***** - ! ------------------------------------------------------------------------------------------------------------------------- do iLayer=ixTop,ixBot ! (loop through model layers) - ! compute fluxes at the lower boundary -- positive downwards - if(iLayer==nLayers)then + if(iLayer==0)then ! (upper boundary fluxes -- positive downwards) + ! flux depends on the type of upper boundary condition + select case(ix_bcUpprTdyn) ! (identify the upper boundary condition for thermodynamics + case(prescribedTemp); iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*( mLayerTempTrial(iLayer+1) - upperBoundTemp )/ & + (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) + case(zeroFlux); iLayerConductiveFlux(iLayer) = 0._rkind + case(energyFlux); iLayerConductiveFlux(iLayer) = groundNetFlux !from vegNrgFlux module + end select ! (identifying the lower boundary condition for thermodynamics) + + else if(iLayer==nLayers)then ! (lower boundary fluxes -- positive downwards) ! flux depends on the type of lower boundary condition select case(ix_bcLowrTdyn) ! (identify the lower boundary condition for thermodynamics - case(prescribedTemp); iLayerConductiveFlux(nLayers) = -iLayerThermalC(iLayer)*(lowerBoundTemp - mLayerTempTrial(iLayer))/(mLayerDepth(iLayer)*0.5_rkind) - case(zeroFlux); iLayerConductiveFlux(nLayers) = 0._rkind - case default; err=20; message=trim(message)//'unable to identify lower boundary condition for thermodynamics'; return + case(prescribedTemp); iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*(lowerBoundTemp - mLayerTempTrial(iLayer))/(mLayerDepth(iLayer)*0.5_rkind) + case(zeroFlux); iLayerConductiveFlux(iLayer) = 0._rkind end select ! (identifying the lower boundary condition for thermodynamics) - ! compute fluxes within the domain -- positive downwards - else + else ! (domain boundary fluxes -- positive downwards) iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) / & (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) @@ -636,7 +637,6 @@ subroutine ssdNrgFlux(& ! ***** compute the total fluxes at layer interfaces ***** ! ------------------------------------------------------------------------------------------------------------------------- ! NOTE: ignore advective fluxes for now - iLayerNrgFlux(0) = groundNetFlux iLayerNrgFlux(ixTop:ixBot) = iLayerConductiveFlux(ixTop:ixBot) !print*, 'iLayerNrgFlux(0:4) = ', iLayerNrgFlux(0:4) diff --git a/utils/subsetGRU.sh b/utils/subsetGRU.sh index 16b72598e..f499d44bb 100644 --- a/utils/subsetGRU.sh +++ b/utils/subsetGRU.sh @@ -25,7 +25,7 @@ nbasin_slurm=$((GRU_file-gruCount*offset)) GRU_id=$((GRU_file - 1)) HRU_id=$GRU_id -echo "file name id is ${GRU_file}, actual GRU id is ${GRU_id}" +echo "file name id is ${GRU_file}, actual GRU id is ${GRU_id}, number in batch is ${nbasin_slurm}" # top paths, change these to yours homeDir=/globalhome/gwu479/HPC/ From afd3edb3faaf87667f19715f29373ebd4e29de6e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 25 Apr 2022 13:22:41 +0900 Subject: [PATCH 0284/1472] fix surface layer no advective flux in ssdNrgFlux --- build/source/engine/ssdNrgFlux.f90 | 35 ++++++++++++++++-------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index e8f4ee7ab..1dcc8f2e4 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -587,7 +587,6 @@ subroutine ssdNrgFlux(& ! ***** compute the conductive fluxes at layer interfaces ***** ! Compute flux after the derivatives, because need iLayerThermal as calculated above ! ------------------------------------------------------------------------------------------------------------------------- - do iLayer=ixTop,ixBot ! (loop through model layers) if(iLayer==0)then ! (upper boundary fluxes -- positive downwards) @@ -612,25 +611,29 @@ subroutine ssdNrgFlux(& !write(*,'(a,i4,1x,2(f9.3,1x))') 'iLayer, iLayerConductiveFlux(iLayer), iLayerThermalC(iLayer) = ', iLayer, iLayerConductiveFlux(iLayer), iLayerThermalC(iLayer) end if ! (the type of layer) - end do + end do ! looping through layers ! ------------------------------------------------------------------------------------------------------------------------- ! ***** compute the advective fluxes at layer interfaces ***** ! ------------------------------------------------------------------------------------------------------------------------- - do iLayer=ixTop,ixBot - ! get the liquid flux at layer interfaces - select case(layerType(iLayer)) - case(iname_snow); qFlux = iLayerLiqFluxSnow(iLayer) - case(iname_soil); qFlux = iLayerLiqFluxSoil(iLayer-nSnow) - case default; err=20; message=trim(message)//'unable to identify layer type'; return - end select - ! compute fluxes at the lower boundary -- positive downwards - if(iLayer==nLayers)then - iLayerAdvectiveFlux(iLayer) = -Cp_water*iden_water*qFlux*(lowerBoundTemp - mLayerTempTrial(iLayer)) - ! compute fluxes within the domain -- positive downwards - else - iLayerAdvectiveFlux(iLayer) = -Cp_water*iden_water*qFlux*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) - end if + do iLayer=ixTop,ixBot !(loop through model layers) + + if (iLayer==0) then + iLayerAdvectiveFlux(iLayer) = realMissing !advective flux at the upper boundary is included in the ground heat flux + else ! get the liquid flux at layer interfaces + select case(layerType(iLayer)) + case(iname_snow); qFlux = iLayerLiqFluxSnow(iLayer) + case(iname_soil); qFlux = iLayerLiqFluxSoil(iLayer-nSnow) + case default; err=20; message=trim(message)//'unable to identify layer type'; return + end select + ! compute fluxes at the lower boundary -- positive downwards + if(iLayer==nLayers)then + iLayerAdvectiveFlux(iLayer) = -Cp_water*iden_water*qFlux*(lowerBoundTemp - mLayerTempTrial(iLayer)) + ! compute fluxes within the domain -- positive downwards + else + iLayerAdvectiveFlux(iLayer) = -Cp_water*iden_water*qFlux*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) + end if + end if ! (all layers except surface) end do ! looping through layers ! ------------------------------------------------------------------------------------------------------------------------- From d0b64165071b74840db1b33562d12e91b87e5526 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 25 Apr 2022 19:56:32 +0900 Subject: [PATCH 0285/1472] Not sure where cj should go on bulk heat capacity derivs --- build/source/engine/computJacDAE.f90 | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index df36c9586..1411fbd50 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -314,7 +314,7 @@ subroutine computJacDAE(& ! compute terms in the Jacobian for vegetation (excluding fluxes) ! NOTE: energy for vegetation is computed *within* the iteration loop as it includes phase change if(ixVegNrg/=integerMissing)then - dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy ) * cj & + dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + dVolHtCapBulk_dTkCanopy * scalarCanopyTempPrime + LH_fus*iden_water*dTheta_dTkCanopy ) * cj & + dVolHtCapBulk_dTkCanopy * scalarCanopyTempPrime & + LH_fus*iden_water * scalarCanopyTempPrime * d2Theta_dTkCanopy2 & + LH_fus * dFracLiqVeg_dTkCanopy * scalarCanopyWatPrime / canopyDepth @@ -324,7 +324,7 @@ subroutine computJacDAE(& ! NOTE: energy for snow+soil is computed *within* the iteration loop as it includes phase change do iLayer=1,nLayers if(ixSnowSoilNrg(iLayer)/=integerMissing) then - dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj & + dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + dVolHtCapBulk_dTk(iLayer) * mLayerTempPrime(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj & + dVolHtCapBulk_dTk(iLayer) * mLayerTempPrime(iLayer) & + LH_fus*iden_water * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & + LH_fus*iden_water * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) @@ -390,7 +390,7 @@ subroutine computJacDAE(& ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth * cj & + if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = ( dVolHtCapBulk_dCanWat * scalarCanopyTempPrime + (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth ) * cj & + dVolHtCapBulk_dCanWat * scalarCanopyTempPrime - (dt/canopyDepth) * dCanopyNetFlux_dCanWat & + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth ! dF/dLiq if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) @@ -403,7 +403,7 @@ subroutine computJacDAE(& ! energy fluxes with the canopy air space (J m-3 K-1) if(ixCasNrg/=integerMissing)then - aJac(ixCasNrg,ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) * cj + aJac(ixCasNrg,ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) * cj ! dMat(ixCasNrg) initialized as heatCap, Cp_air*iden_air (cj not included) if(ixVegNrg/=integerMissing) aJac(ixCasNrg,ixVegNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanopyTemp) if(ixTopNrg/=integerMissing) aJac(ixCasNrg,ixTopNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dGroundTemp) endif @@ -503,7 +503,7 @@ subroutine computJacDAE(& if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) ! - include derivatives of energy fluxes w.r.t water fluxes for current layer - aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & + aJac(nrgState,watState) = ( dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) + (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water ) * cj & + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) & + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) & + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) @@ -654,10 +654,11 @@ subroutine computJacDAE(& endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) & + aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer)*mLayerTempPrime(jLayer)*cj & + + dVolHtCapBulk_dPsi0(iLayer)*mLayerTempPrime(jLayer)*cj + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) if(mLayerdTheta_dTk(jLayer) > verySmall) then ! ice is present - aJac(nrgState,watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water*cj & + aJac(nrgState,watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer)*cj & - LH_fus*iden_water * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content endif From 100b6bc05631688fd774dbd310ff2e7de38f5461 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 25 Apr 2022 21:49:15 +0900 Subject: [PATCH 0286/1472] Revert "Not sure where cj should go on bulk heat capacity derivs" This reverts commit d0b64165071b74840db1b33562d12e91b87e5526. --- build/source/engine/computJacDAE.f90 | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 1411fbd50..df36c9586 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -314,7 +314,7 @@ subroutine computJacDAE(& ! compute terms in the Jacobian for vegetation (excluding fluxes) ! NOTE: energy for vegetation is computed *within* the iteration loop as it includes phase change if(ixVegNrg/=integerMissing)then - dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + dVolHtCapBulk_dTkCanopy * scalarCanopyTempPrime + LH_fus*iden_water*dTheta_dTkCanopy ) * cj & + dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy ) * cj & + dVolHtCapBulk_dTkCanopy * scalarCanopyTempPrime & + LH_fus*iden_water * scalarCanopyTempPrime * d2Theta_dTkCanopy2 & + LH_fus * dFracLiqVeg_dTkCanopy * scalarCanopyWatPrime / canopyDepth @@ -324,7 +324,7 @@ subroutine computJacDAE(& ! NOTE: energy for snow+soil is computed *within* the iteration loop as it includes phase change do iLayer=1,nLayers if(ixSnowSoilNrg(iLayer)/=integerMissing) then - dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + dVolHtCapBulk_dTk(iLayer) * mLayerTempPrime(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj & + dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj & + dVolHtCapBulk_dTk(iLayer) * mLayerTempPrime(iLayer) & + LH_fus*iden_water * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & + LH_fus*iden_water * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) @@ -390,7 +390,7 @@ subroutine computJacDAE(& ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = ( dVolHtCapBulk_dCanWat * scalarCanopyTempPrime + (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth ) * cj & + if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth * cj & + dVolHtCapBulk_dCanWat * scalarCanopyTempPrime - (dt/canopyDepth) * dCanopyNetFlux_dCanWat & + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth ! dF/dLiq if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) @@ -403,7 +403,7 @@ subroutine computJacDAE(& ! energy fluxes with the canopy air space (J m-3 K-1) if(ixCasNrg/=integerMissing)then - aJac(ixCasNrg,ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) * cj ! dMat(ixCasNrg) initialized as heatCap, Cp_air*iden_air (cj not included) + aJac(ixCasNrg,ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) * cj if(ixVegNrg/=integerMissing) aJac(ixCasNrg,ixVegNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanopyTemp) if(ixTopNrg/=integerMissing) aJac(ixCasNrg,ixTopNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dGroundTemp) endif @@ -503,7 +503,7 @@ subroutine computJacDAE(& if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) ! - include derivatives of energy fluxes w.r.t water fluxes for current layer - aJac(nrgState,watState) = ( dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) + (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water ) * cj & + aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) & + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) & + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) @@ -654,11 +654,10 @@ subroutine computJacDAE(& endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer)*mLayerTempPrime(jLayer)*cj & - + dVolHtCapBulk_dPsi0(iLayer)*mLayerTempPrime(jLayer)*cj + aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) & + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) if(mLayerdTheta_dTk(jLayer) > verySmall) then ! ice is present - aJac(nrgState,watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer)*cj & + aJac(nrgState,watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water*cj & - LH_fus*iden_water * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content endif From 50d337974e27efeb16430d7046b650b27d2be1b7 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 25 Apr 2022 21:59:38 +0900 Subject: [PATCH 0287/1472] screwed up git, no real changes --- build/source/engine/computResidDAE.f90 | 71 +++++++++++++------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/build/source/engine/computResidDAE.f90 b/build/source/engine/computResidDAE.f90 index 6cd71ec37..c4edc36d9 100644 --- a/build/source/engine/computResidDAE.f90 +++ b/build/source/engine/computResidDAE.f90 @@ -100,7 +100,7 @@ subroutine computResidDAE(& real(rkind),intent(in) :: fVec(:) ! flux vector ! input: state variables (already disaggregated into scalars and vectors) real(rkind),intent(in) :: scalarCanopyTempTrial - real(rkind),intent(in) :: mLayerTempTrial(:) + real(rkind),intent(in) :: mLayerTempTrial(:) real(rkind),intent(in) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) real(rkind),intent(in) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) real(rkind),intent(in) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) @@ -108,7 +108,7 @@ subroutine computResidDAE(& real(rkind),intent(in) :: scalarAquiferStoragePrime ! trial value of aquifer storage (m) ! input: diagnostic variables defining the liquid water and ice content (function of state variables) real(rkind),intent(in) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: scalarCanopyLiqPrime + real(rkind),intent(in) :: scalarCanopyLiqPrime real(rkind),intent(in) :: mLayerVolFracIcePrime(:) ! trial value for volumetric fraction of ice (-) real(rkind),intent(in) :: mLayerVolFracLiqPrime(:) real(rkind),intent(in) :: mLayerVolFracWatPrime(:) @@ -162,7 +162,7 @@ subroutine computResidDAE(& ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="computResidDAE/" - + ! --- ! * compute sink terms... @@ -178,20 +178,19 @@ subroutine computResidDAE(& if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) select case( layerType(iLayer) ) - case(iname_snow) - rAdd( ixSnowSoilNrg(iLayer) ) = rAdd( ixSnowSoilNrg(iLayer) ) + LH_fus*iden_ice * mLayerVolFracIcePrime(iLayer) - case(iname_soil); rAdd( ixSnowSoilNrg(iLayer) ) = rAdd( ixSnowSoilNrg(iLayer) ) + LH_fus*iden_water * mLayerVolFracIcePrime(iLayer) + case(iname_snow); rAdd( ixSnowSoilNrg(iLayer) ) = rAdd( ixSnowSoilNrg(iLayer) ) + LH_fus*iden_ice * mLayerVolFracIcePrime(iLayer) + case(iname_soil); rAdd( ixSnowSoilNrg(iLayer) ) = rAdd( ixSnowSoilNrg(iLayer) ) + LH_fus*iden_water * mLayerVolFracIcePrime(iLayer) end select end do ! looping through non-missing energy state variables in the snow+soil domain endif - - + + ! sink terms soil hydrology (-) ! NOTE 1: state variable is volumetric water content, so melt-freeze is not included ! NOTE 2: ground evaporation was already included in the flux at the upper boundary ! NOTE 3: rAdd(ixSnowOnlyWat)=0, and is defined in the initialization above ! NOTE 4: same sink terms for matric head and liquid matric potential - if(nSoilOnlyHyd>0)then + if(nSoilOnlyHyd>0)then do concurrent (iLayer=1:nSoil,ixSoilOnlyHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) rAdd( ixSoilOnlyHyd(iLayer) ) = rAdd( ixSoilOnlyHyd(iLayer) ) + (mLayerTranspire(iLayer) - mLayerBaseflow(iLayer) )/mLayerDepth(iLayer+nSnow) - mLayerCompress(iLayer) end do ! looping through non-missing energy state variables in the snow+soil domain @@ -207,13 +206,13 @@ subroutine computResidDAE(& if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*scalarCanairTempPrime - ( fVec(ixCasNrg) + rAdd(ixCasNrg) ) if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime + scalarCanopyCmTrial * scalarCanopyWatPrime/canopyDepth - ( fVec(ixVegNrg) + rAdd(ixVegNrg) ) ! --> mass balance - if(ixVegHyd/=integerMissing)then - scalarCanopyHydPrime = merge(scalarCanopyWatPrime, scalarCanopyLiqPrime, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) + if(ixVegHyd/=integerMissing)then + scalarCanopyHydPrime = merge(scalarCanopyWatPrime, scalarCanopyLiqPrime, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) rVec(ixVegHyd) = sMul(ixVegHyd)*scalarCanopyHydPrime - ( fVec(ixVegHyd) + rAdd(ixVegHyd) ) endif ! compute the residual vector for the snow and soil sub-domains for energy - if(nSnowSoilNrg>0)then + if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) ) * mLayerTempPrime(iLayer) + mLayerCmTrial(iLayer) * mLayerVolFracWatPrime(iLayer) - ( fVec( ixSnowSoilNrg(iLayer) ) + rAdd( ixSnowSoilNrg(iLayer) ) ) end do ! looping through non-missing energy state variables in the snow+soil domain @@ -223,13 +222,13 @@ subroutine computResidDAE(& ! NOTE: residual depends on choice of state variable if(nSnowSoilHyd>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) - ! (get the correct state variable) + ! (get the correct state variable) mLayerVolFracHydPrime(iLayer) = merge(mLayerVolFracWatPrime(iLayer), mLayerVolFracLiqPrime(iLayer), (ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_matLayer) ) ! (compute the residual) rVec( ixSnowSoilHyd(iLayer) ) = mLayerVolFracHydPrime(iLayer) - ( fVec( ixSnowSoilHyd(iLayer) ) + rAdd( ixSnowSoilHyd(iLayer) ) ) end do ! looping through non-missing energy state variables in the snow+soil domain endif - + ! compute the residual vector for the aquifer if(ixAqWat/=integerMissing) rVec(ixAqWat) = sMul(ixAqWat)*scalarAquiferStoragePrime - ( fVec(ixAqWat) + rAdd(ixAqWat) ) @@ -239,9 +238,9 @@ subroutine computResidDAE(& write(*,'(a,1x,100(e12.5,1x))') 'fVec = ', fVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) !print*, 'PAUSE:'; read(*,*) endif - + !call printResidDAE(nSnow,nSoil,nLayers,indx_data,rAdd,rVec) - + ! check if(any(isNan(rVec)))then call printResidDAE(nSnow,nSoil,nLayers,indx_data,rAdd,rVec) @@ -306,40 +305,40 @@ subroutine printResidDAE( & ! -------------------------------------------------------------------------------------------------------------------------------- -if(ixVegNrg/=integerMissing) print *, 'rAdd(ixVegNrg) = ', rAdd(ixVegNrg) +if(ixVegNrg/=integerMissing) print *, 'rAdd(ixVegNrg) = ', rAdd(ixVegNrg) if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) select case( layerType(iLayer) ) case(iname_snow) - print *, 'rAdd( ixSnowSoilNrg(iLayer) ) = ', rAdd( ixSnowSoilNrg(iLayer) ) - case(iname_soil); print *, 'rAdd( ixSnowSoilNrg(iLayer) ) = ', rAdd( ixSnowSoilNrg(iLayer) ) + print *, 'rAdd( ixSnowSoilNrg(iLayer) ) = ', rAdd( ixSnowSoilNrg(iLayer) ) + case(iname_soil); print *, 'rAdd( ixSnowSoilNrg(iLayer) ) = ', rAdd( ixSnowSoilNrg(iLayer) ) end select - end do + end do endif -if(nSoilOnlyHyd>0)then - do concurrent (iLayer=1:nSoil,ixSoilOnlyHyd(iLayer)/=integerMissing) - print *, 'rAdd( ixSoilOnlyHyd(iLayer) ) = ', rAdd( ixSoilOnlyHyd(iLayer) ) - end do +if(nSoilOnlyHyd>0)then + do concurrent (iLayer=1:nSoil,ixSoilOnlyHyd(iLayer)/=integerMissing) + print *, 'rAdd( ixSoilOnlyHyd(iLayer) ) = ', rAdd( ixSoilOnlyHyd(iLayer) ) + end do endif -if(ixCasNrg/=integerMissing) print *, 'rVec(ixCasNrg) = ', rVec(ixCasNrg) -if(ixVegNrg/=integerMissing) print *, 'rVec(ixVegNrg) = ', rVec(ixVegNrg) -if(ixVegHyd/=integerMissing)then - print *, 'rVec(ixVegHyd) = ', rVec(ixVegHyd) +if(ixCasNrg/=integerMissing) print *, 'rVec(ixCasNrg) = ', rVec(ixCasNrg) +if(ixVegNrg/=integerMissing) print *, 'rVec(ixVegNrg) = ', rVec(ixVegNrg) +if(ixVegHyd/=integerMissing)then + print *, 'rVec(ixVegHyd) = ', rVec(ixVegHyd) endif -if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) - print *, 'rVec( ixSnowSoilNrg(iLayer) ) = ', rVec( ixSnowSoilNrg(iLayer) ) - end do +if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) + print *, 'rVec( ixSnowSoilNrg(iLayer) ) = ', rVec( ixSnowSoilNrg(iLayer) ) + end do endif if(nSnowSoilHyd>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) - print *, 'rVec( ixSnowSoilHyd(iLayer) ) = ', rVec( ixSnowSoilHyd(iLayer) ) - end do + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) + print *, 'rVec( ixSnowSoilHyd(iLayer) ) = ', rVec( ixSnowSoilHyd(iLayer) ) + end do endif if(ixAqWat/=integerMissing) print *, ' rVec(ixAqWat) = ', rVec(ixAqWat) From 561b09ba6ab318332d2f73561ac4d7d0eaffa080 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 28 Apr 2022 14:25:53 +0900 Subject: [PATCH 0288/1472] Changing updateStateSundials for the case when the soil crosses over the freezing point. These equations should be correct. It seems the analytical Jacobian should also change, but that is not changed yet in this commit. --- build/source/engine/computResidDAE.f90 | 4 - build/source/engine/eval8JacDAE.f90 | 3 +- build/source/engine/updatStateSundials.f90 | 171 +++++++++++++-------- build/source/engine/updateVars4JacDAE.f90 | 55 +++---- build/source/engine/updateVarsSundials.f90 | 38 ++--- 5 files changed, 153 insertions(+), 118 deletions(-) diff --git a/build/source/engine/computResidDAE.f90 b/build/source/engine/computResidDAE.f90 index c4edc36d9..a87b2e719 100644 --- a/build/source/engine/computResidDAE.f90 +++ b/build/source/engine/computResidDAE.f90 @@ -163,7 +163,6 @@ subroutine computResidDAE(& ! initialize error control err=0; message="computResidDAE/" - ! --- ! * compute sink terms... ! ----------------------- @@ -184,7 +183,6 @@ subroutine computResidDAE(& end do ! looping through non-missing energy state variables in the snow+soil domain endif - ! sink terms soil hydrology (-) ! NOTE 1: state variable is volumetric water content, so melt-freeze is not included ! NOTE 2: ground evaporation was already included in the flux at the upper boundary @@ -253,7 +251,6 @@ subroutine computResidDAE(& end subroutine computResidDAE - ! ********************************************************************************************************** ! private subroutine printResidDAE: print the residual vector mainly for debugging ! ********************************************************************************************************** @@ -304,7 +301,6 @@ subroutine printResidDAE( & ) ! association to necessary variables for the residual computations ! -------------------------------------------------------------------------------------------------------------------------------- - if(ixVegNrg/=integerMissing) print *, 'rAdd(ixVegNrg) = ', rAdd(ixVegNrg) if(nSnowSoilNrg>0)then diff --git a/build/source/engine/eval8JacDAE.f90 b/build/source/engine/eval8JacDAE.f90 index 225d7adee..8bb13e444 100644 --- a/build/source/engine/eval8JacDAE.f90 +++ b/build/source/engine/eval8JacDAE.f90 @@ -125,7 +125,7 @@ subroutine eval8JacDAE(& ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control - real(rkind),intent(in) :: cj ! this scalar changes whenever the step size or method order changes + real(rkind),intent(in) :: cj ! this scalar changes whenever the step size or method order changes real(rkind),intent(in) :: dt ! time step integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers @@ -263,6 +263,7 @@ subroutine eval8JacDAE(& call updateVars4JacDAE(& ! input + dt, & ! intent(in): time step .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers diff --git a/build/source/engine/updatStateSundials.f90 b/build/source/engine/updatStateSundials.f90 index 1622dfe6d..23de661e0 100644 --- a/build/source/engine/updatStateSundials.f90 +++ b/build/source/engine/updatStateSundials.f90 @@ -19,14 +19,14 @@ module updatStateSundials_module real(rkind),parameter :: verySmall=1e-14_rkind ! a very small number (used to avoid divide by zero) contains - - + + ! ************************************************************************************************************* ! public subroutine updateVegSundials: compute phase change impacts on volumetric liquid water and ice ! Input: Theta * canopyDepth * iden_water ! Outputs: VolFracLiq * canopyDepth * iden_water and VolFracIce * canopyDepth * iden_ice ! ************************************************************************************************************* - subroutine updateVegSundials(& + subroutine updateVegSundials(& ! input Temp ,& ! intent(in): temperature (K) Theta ,& ! intent(in): volume fraction of total water (-) @@ -68,15 +68,15 @@ subroutine updateVegSundials(& VolFracIce = (1._rkind - fLiq)*Theta VolFracLiqPrime = fLiq * ThetaPrime + dFracLiq_dTk(Temp,snowfrz_scale) * Theta * TempPrime VolFracIcePrime = ( ThetaPrime - VolFracLiqPrime ) - + end subroutine updateVegSundials - - + + ! ************************************************************************************************************* ! public subroutine updateSnowSundials: compute phase change impacts on volumetric liquid water and ice ! ************************************************************************************************************* - subroutine updateSnowSundials(& + subroutine updateSnowSundials(& ! input mLayerTemp ,& ! intent(in): temperature (K) mLayerTheta ,& ! intent(in): volume fraction of total water (-) @@ -118,22 +118,23 @@ subroutine updateSnowSundials(& mLayerVolFracIce = (1._rkind - fLiq)*mLayerTheta*(iden_water/iden_ice) mLayerVolFracLiqPrime = fLiq * mLayerThetaPrime + dFracLiq_dTk(mLayerTemp,snowfrz_scale) * mLayerTheta * mLayerTempPrime mLayerVolFracIcePrime = ( mLayerThetaPrime - mLayerVolFracLiqPrime ) * (iden_water/iden_ice) - + end subroutine updateSnowSundials ! ************************************************************************************************************* ! public subroutine updateSoilSundials: compute phase change impacts on matric head and volumetric liquid water and ice + ! uses mLayerMatricHeadPrev and mLayerVolFracWatPrev ! ************************************************************************************************************* subroutine updateSoilSundials(& ! input dt_cur, & - mLayerTemp ,& ! intent(in): temperature vector (K) - mLayerMatricHead ,& ! intent(in): matric head (m) - mLayerMatricHeadPrev ,& - mLayerVolFracWatPrev ,& - mLayerTempPrime ,& ! intent(in) - mLayerMatricHeadPrime, & ! intent(in) + mLayerTemp ,& ! intent(in): temperature (K) + mLayerMatricHead ,& ! intent(in): total water matric potential (m) + mLayerMatricHeadPrev ,& ! intent(in): total water matric potential previous time step (m) + mLayerVolFracWatPrev ,& ! intent(in): volumetric fraction of total water previous time step (-) + mLayerTempPrime ,& ! intent(in): temperature time derivative (K/s) + mLayerMatricHeadPrime, & ! intent(in): total water matric potential time derivative (m/s) vGn_alpha ,& ! intent(in): van Genutchen "alpha" parameter vGn_n ,& ! intent(in): van Genutchen "n" parameter theta_sat ,& ! intent(in): soil porosity (-) @@ -143,23 +144,23 @@ subroutine updateSoilSundials(& mLayerVolFracWat ,& ! intent(out): volumetric fraction of total water (-) mLayerVolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) mLayerVolFracIce ,& ! intent(out): volumetric fraction of ice (-) - mLayerVolFracWatPrime ,& ! intent(out): volumetric fraction of total water (-) - mLayerVolFracLiqPrime ,& ! intent(out): volumetric fraction of liquid water (-) - mLayerVolFracIcePrime ,& ! intent(out): volumetric fraction of ice (-) + mLayerVolFracWatPrime ,& ! intent(out): volumetric fraction of total water time derivative (-) + mLayerVolFracLiqPrime ,& ! intent(out): volumetric fraction of liquid water time derivative (-) + mLayerVolFracIcePrime ,& ! intent(out): volumetric fraction of ice time derivative (-) err,message) ! intent(out): error control ! utility routines USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water based on matric head USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric liquid water content - USE soil_utils_module,only:dTheta_dPsi + USE soil_utils_module,only:dTheta_dPsi implicit none ! input variables real(rkind),intent(in) :: dt_cur real(rkind),intent(in) :: mLayerTemp ! estimate of temperature (K) real(rkind),intent(in) :: mLayerMatricHead ! matric head (m) - real(rkind),intent(in) :: mLayerMatricHeadPrev ! matric head (m) - real(rkind),intent(in) :: mLayerVolFracWatPrev - real(rkind),intent(in) :: mLayerTempPrime - real(rkind),intent(in) :: mLayerMatricHeadPrime ! matric head (m) + real(rkind),intent(in) :: mLayerMatricHeadPrev ! matric head previous time step (m) + real(rkind),intent(in) :: mLayerVolFracWatPrev ! volumetric fraction of total waterprevious time step (m) + real(rkind),intent(in) :: mLayerTempPrime ! temperature time derivative (K/s) + real(rkind),intent(in) :: mLayerMatricHeadPrime ! matric head time derivative (m/s) real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter real(rkind),intent(in) :: theta_sat ! soil porosity (-) @@ -178,24 +179,25 @@ subroutine updateSoilSundials(& real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) + real(rkind) :: dt_inv ! inverse of timestep + real(rkind) :: mLayerTempPrev ! estimate of previous timestep temperature (K) + real(rkind) :: mLayerVolFracLiqPrev ! previous timestep volumetric fraction of liquid water (-) ! initialize error control err=0; message="updateSoilSundials/" - + ! compute fractional **volume** of total water (liquid plus ice) - mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) ! mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime - if( abs(mLayerMatricHead - mLayerMatricHeadPrev) < verySmall )then - mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev) / dt_cur - else - mLayerVolFracWatPrime = mLayerMatricHeadPrime * (mLayerVolFracWat - mLayerVolFracWatPrev) / (mLayerMatricHead - mLayerMatricHeadPrev) - endif - - + if( abs(mLayerMatricHead - mLayerMatricHeadPrev) < verySmall )then + dt_inv = 1._rkind/ dt_cur !WHY NOT JUST USE THIS + else + dt_inv = mLayerMatricHeadPrime / (mLayerMatricHead - mLayerMatricHeadPrev) + endif + mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev)*dt_inv - if(mLayerVolFracWat > theta_sat)then; err=20; message=trim(message)//'volume of liquid and ice exceeds porosity'; return; end if - + ! compute the critical soil temperature where all water is unfrozen (K) ! (eq 17 in Dall'Amico 2011) TcSoil = Tfreeze + min(mLayerMatricHead,0._rkind)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) @@ -208,41 +210,56 @@ subroutine updateSoilSundials(& xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if(mLayerPsiLiq<0._rkind)then - mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime - else - mLayerVolFracLiqPrime = 0._rkind + if( mLayerTemp - mLayerTempPrime*dt_cur < TcSoil )then + if(mLayerPsiLiq<0._rkind)then + mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime + else + mLayerVolFracLiqPrime = 0._rkind + endif + else ! was unfrozen on previous time step + mLayerVolFracLiqPrev = mLayerVolFracWatPrev + mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv endif ! - volumetric ice content (-) mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime - + ! *** compute volumetric fraction of liquid water and ice for unfrozen soil - else - + else !( mLayerTemp >= TcSoil) + ! all water is unfrozen mLayerVolFracLiq = mLayerVolFracWat mLayerVolFracIce = 0._rkind - - mLayerVolFracLiqPrime = mLayerVolFracWatPrime - mLayerVolFracIcePrime = 0._rkind - end if ! (check if soil is partially frozen) + if ( mLayerTemp - mLayerTempPrime*dt_cur >= TcSoil )then + mLayerVolFracLiqPrime = mLayerVolFracWatPrime + mLayerVolFracIcePrime = 0._rkind + else ! was partially frozen on previous time step + mLayerTempPrev = mLayerTemp-mLayerTempPrime + xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) + mLayerPsiLiq = xConst*(mLayerTempPrev - Tfreeze) ! liquid water matric potential from the Clapeyron eqution + mLayerVolFracLiqPrev = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv + mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime + endif + end if ! (check if soil is partially frozen) end subroutine updateSoilSundials ! ************************************************************************************************************* ! public subroutine updateSoilSundials: compute phase change impacts on matric head and volumetric liquid water and ice + ! uses mLayerMatricHeadPrime ! ************************************************************************************************************* subroutine updateSoilSundials2(& ! input + dt_cur ,& ! intent(in): time step mLayerTemp ,& ! intent(in): temperature vector (K) - mLayerMatricHead ,& ! intent(in): matric head (m) - mLayerTempPrime ,& ! intent(in) - mLayerMatricHeadPrime, & ! intent(in) + mLayerMatricHead ,& ! intent(in): total water matric potential (m) + mLayerTempPrime ,& ! intent(in): temperature time derivative (K/s) + mLayerMatricHeadPrime, & ! intent(in): total water matric potential time derivative (m/s) vGn_alpha ,& ! intent(in): van Genutchen "alpha" parameter vGn_n ,& ! intent(in): van Genutchen "n" parameter theta_sat ,& ! intent(in): soil porosity (-) @@ -259,13 +276,14 @@ subroutine updateSoilSundials2(& ! utility routines USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water based on matric head USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric liquid water content - USE soil_utils_module,only:dTheta_dPsi + USE soil_utils_module,only:dTheta_dPsi implicit none ! input variables + real(rkind),intent(in) :: dt_cur real(rkind),intent(in) :: mLayerTemp ! estimate of temperature (K) real(rkind),intent(in) :: mLayerMatricHead ! matric head (m) - real(rkind),intent(in) :: mLayerTempPrime - real(rkind),intent(in) :: mLayerMatricHeadPrime ! matric head (m) + real(rkind),intent(in) :: mLayerTempPrime ! temperature time derivative (K/s) + real(rkind),intent(in) :: mLayerMatricHeadPrime ! matric head time derivative (m/s) real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter real(rkind),intent(in) :: theta_sat ! soil porosity (-) @@ -275,60 +293,77 @@ subroutine updateSoilSundials2(& real(rkind),intent(out) :: mLayerVolFracWat ! fractional volume of total water (-) real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) - real(rkind),intent(out) :: mLayerVolFracWatPrime ! fractional volume of total water (-) - real(rkind),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice (-) + real(rkind),intent(out) :: mLayerVolFracWatPrime ! fractional volume of total water time derivative (-) + real(rkind),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water time derivative (-) + real(rkind),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice time derivative (-) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! define local variables real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) + real(rkind) :: dt_inv ! inverse of timestep + real(rkind) :: mLayerTempPrev ! estimate of previous timestep temperature (K) + real(rkind) :: mLayerVolFracLiqPrev ! previous timestep volumetric fraction of liquid water (-) ! initialize error control err=0; message="updateSoilSundials2/" - + ! compute fractional **volume** of total water (liquid plus ice) mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime - + + dt_inv = 1._rkind/ dt_cur !WHY NOT JUST USE THIS + + if(mLayerVolFracWat > theta_sat)then; err=20; message=trim(message)//'volume of liquid and ice exceeds porosity'; return; end if - ! compute the critical soil temperature where all water is unfrozen (K) ! (eq 17 in Dall'Amico 2011) TcSoil = Tfreeze + min(mLayerMatricHead,0._rkind)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) ! *** compute volumetric fraction of liquid water and ice for partially frozen soil - if(mLayerTemp < TcSoil)then ! (check if soil temperature is less than the critical temperature) + if( mLayerTemp < TcSoil )then ! (check if soil temperature is less than the critical temperature) ! - volumetric liquid water content (-) ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if(mLayerPsiLiq<0._rkind)then - mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime - else - mLayerVolFracLiqPrime = 0._rkind + if( mLayerTemp - mLayerTempPrime*dt_cur < TcSoil )then + if(mLayerPsiLiq<0._rkind)then + mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime + else + mLayerVolFracLiqPrime = 0._rkind + endif + else ! was unfrozen on previous time step + mLayerVolFracLiqPrev = mLayerVolFracWat - mLayerVolFracWatPrime*dt_cur + mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv endif ! - volumetric ice content (-) mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime - ! *** compute volumetric fraction of liquid water and ice for unfrozen soil - else - + else !( mLayerTemp >= TcSoil) + ! all water is unfrozen mLayerVolFracLiq = mLayerVolFracWat mLayerVolFracIce = 0._rkind - - mLayerVolFracLiqPrime = mLayerVolFracWatPrime - mLayerVolFracIcePrime = 0._rkind - end if ! (check if soil is partially frozen) + if ( mLayerTemp - mLayerTempPrime*dt_cur >= TcSoil )then + mLayerVolFracLiqPrime = mLayerVolFracWatPrime + mLayerVolFracIcePrime = 0._rkind + else ! was partially frozen on previous time step + mLayerTempPrev = mLayerTemp - mLayerTempPrime*dt_cur + xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) + mLayerPsiLiq = xConst*(mLayerTempPrev - Tfreeze) ! liquid water matric potential from the Clapeyron eqution + mLayerVolFracLiqPrev = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv + mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime + endif + end if ! (check if soil is partially frozen) end subroutine updateSoilSundials2 end module updatStateSundials_module diff --git a/build/source/engine/updateVars4JacDAE.f90 b/build/source/engine/updateVars4JacDAE.f90 index c8cc1204c..0c791842a 100644 --- a/build/source/engine/updateVars4JacDAE.f90 +++ b/build/source/engine/updateVars4JacDAE.f90 @@ -109,6 +109,7 @@ module updateVars4JacDAE_module ! ********************************************************************************************************** subroutine updateVars4JacDAE(& ! input + dt, & ! intent(in): time step do_adjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers @@ -143,6 +144,7 @@ subroutine updateVars4JacDAE(& ! -------------------------------------------------------------------------------------------------------------------------------- implicit none ! input + real(rkind) ,intent(in) :: dt ! time step logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers @@ -540,14 +542,14 @@ subroutine updateVars4JacDAE(& ! compute volumetric fraction of liquid water and ice call updateVegSundials(& xTemp, & ! intent(in) : temperature (K) - scalarCanopyWatTrial, & ! intent(in) : volumetric fraction of total water (-) + scalarCanopyWatTrial, & ! intent(in) : canopy total water (kg m-2) snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) - scalarCanopyTempPrime, & ! intent(in) - scalarCanopyWatPrime, & ! intent(in) : volumetric fraction of total water (-) - scalarVolFracLiq, & ! intent(out) : trial volumetric fraction of liquid water (-) - scalarVolFracIce, & ! intent(out) : trial volumetric fraction if ice (-) - scalarVolFracLiqPrime, & ! intent(out) : trial volumetric fraction of liquid water (-) - scalarVolFracIcePrime, & ! intent(out) : trial volumetric fraction if ice (-) + scalarCanopyTempPrime, & ! intent(in) : canopy temperature time derivative (K/s) + scalarCanopyWatPrime, & ! intent(in) : canopy total water time derivative (kg m-2 /s) + scalarVolFracLiq, & ! intent(out) : trial canopy liquid water (-) + scalarVolFracIce, & ! intent(out) : trial volumetric canopy ice (-) + scalarVolFracLiqPrime, & ! intent(out) : trial volumetric canopy liquid water (-) + scalarVolFracIcePrime, & ! intent(out) : trial volumetric canopy ice (-) scalarFracLiqVeg, & ! intent(out) : fraction of liquid water (-) err,cmessage) ! intent(out) : error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -559,12 +561,12 @@ subroutine updateVars4JacDAE(& xTemp, & ! intent(in) : temperature (K) mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) - mLayerTempPrime(iLayer), & ! - mLayerVolFracWatPrime(iLayer), & ! intent(in) + mLayerTempPrime(iLayer), & ! intent(in) : temperature time derivative (K/s) + mLayerVolFracWatPrime(iLayer), & ! intent(in) : volumetric fraction of total water time derivative (-) mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) - mLayerVolFracLiqPrime(iLayer), & ! intent(out) - mLayerVolFracIcePrime(iLayer), & ! intent(out) + mLayerVolFracLiqPrime(iLayer), & ! intent(out) : volumetric fraction of liquid water time derivative (-) + mLayerVolFracIcePrime(iLayer), & ! intent(out) : volumetric fraction of ice time derivative (-) mLayerFracLiqSnow(iLayer), & ! intent(out) : fraction of liquid water (-) err,cmessage) ! intent(out) : error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -574,22 +576,23 @@ subroutine updateVars4JacDAE(& ! compute volumetric fraction of liquid water and ice call updateSoilSundials2(& - xTemp, & ! intent(in) : temperature (K) - mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) - mLayerTempPrime(iLayer), & - mLayerMatricHeadPrime(ixControlIndex), & + dt, & ! intent(in) : time step + xTemp, & ! intent(in) : temperature (K) + mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) + mLayerTempPrime(iLayer), & ! intent(in) : temperature time derivative (K/s) + mLayerMatricHeadPrime(ixControlIndex), & ! intent(in) : total water matric potential time derivative (m/s) ! intent(in) : soil parameters - vGn_alpha(ixControlIndex), & - vGn_n(ixControlIndex), & - theta_sat(ixControlIndex), & - theta_res(ixControlIndex), & - vGn_m(ixControlIndex), & - mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) - mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) - mLayerVolFracWatPrime(iLayer), & - mLayerVolFracLiqPrime(iLayer), & - mLayerVolFracIcePrime(iLayer), & + vGn_alpha(ixControlIndex), & ! intent(in) : van Genutchen "alpha" parameter + vGn_n(ixControlIndex), & ! intent(in) : van Genutchen "n" parameter + theta_sat(ixControlIndex), & ! intent(in) : soil porosity (-) + theta_res(ixControlIndex), & ! intent(in) : soil residual volumetric water content (-) + vGn_m(ixControlIndex), & ! intent(in) : van Genutchen "m" parameter (-) + mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) + mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) + mLayerVolFracWatPrime(iLayer), & ! intent(out) : volumetric fraction of total water time derivative (-) + mLayerVolFracLiqPrime(iLayer), & ! intent(out) : volumetric fraction of liquid water time derivative (-) + mLayerVolFracIcePrime(iLayer), & ! intent(out) : volumetric fraction of ice time derivative (-) err,cmessage) ! intent(out) : error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif diff --git a/build/source/engine/updateVarsSundials.f90 b/build/source/engine/updateVarsSundials.f90 index 697a41eac..5b945a833 100644 --- a/build/source/engine/updateVarsSundials.f90 +++ b/build/source/engine/updateVarsSundials.f90 @@ -156,7 +156,7 @@ subroutine updateVarsSundials(& real(rkind),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) real(rkind),intent(inout) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) real(rkind),intent(inout) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - + real(rkind),intent(inout) :: scalarCanopyTempPrime ! trial value of canopy temperature (K) real(rkind),intent(inout) :: scalarCanopyWatPrime ! trial value of canopy total water (kg m-2) real(rkind),intent(inout) :: scalarCanopyLiqPrime ! trial value of canopy liquid water (kg m-2) @@ -168,14 +168,14 @@ subroutine updateVarsSundials(& real(rkind),intent(inout) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric ice water content (-) real(rkind),intent(inout) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) real(rkind),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) - + real(rkind),intent(inout) :: mLayerTempPrime(:) real(rkind),intent(inout) :: mLayerVolFracWatPrime(:) ! reza real(rkind),intent(inout) :: mLayerVolFracLiqPrime(:) ! reza real(rkind),intent(inout) :: mLayerVolFracIcePrime(:) ! reza real(rkind),intent(inout) :: mLayerMatricHeadPrime(:) ! reza real(rkind),intent(inout) :: mLayerMatricHeadLiqPrime(:) ! reza - + ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -278,7 +278,7 @@ subroutine updateVarsSundials(& ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- - + ! initialize error control err=0; message='updateVarsSundials/' @@ -340,7 +340,7 @@ subroutine updateVarsSundials(& print*, 'isCoupled = ', isCoupled print*, 'isNrgState = ', isNrgState endif - + ! ======================================================================================================================================= @@ -352,7 +352,7 @@ subroutine updateVarsSundials(& ! update hydrology state variables for the uncoupled solution if(.not.isNrgState .and. .not.isCoupled)then - + stop 1 ! update the total water from volumetric liquid water @@ -376,7 +376,7 @@ subroutine updateVarsSundials(& select case( ixStateType(ixFullVector) ) ! --> update the total water from the liquid water matric potential case(iname_lmpLayer) - + effSat = volFracLiq(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex)) ! effective saturation avPore = theta_sat(ixControlIndex) - mLayerVolFracIceTrial(iLayer) - theta_res(ixControlIndex) ! available pore space mLayerVolFracLiqTrial(iLayer) = effSat*avPore + theta_res(ixControlIndex) @@ -387,14 +387,14 @@ subroutine updateVarsSundials(& !write(*,'(a,1x,i4,1x,3(f20.10,1x))') 'mLayerVolFracLiqTrial(iLayer) 1 = ', iLayer, mLayerVolFracLiqTrial(iLayer), mLayerVolFracIceTrial(iLayer), mLayerVolFracWatTrial(iLayer) ! --> update the total water from the total water matric potential case(iname_matLayer) - + mLayerVolFracWatTrial(iLayer) = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) mLayerVolFracWatPrime(iLayer) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) *mLayerMatricHeadPrime(ixControlIndex) ! --> update the total water matric potential (assume already have mLayerVolFracWatTrial given block above) case(iname_liqLayer, iname_watLayer) - + mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - mLayerMatricHeadPrime(ixControlIndex) = dPsi_dTheta(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) * mLayerVolFracWatPrime(iLayer) + mLayerMatricHeadPrime(ixControlIndex) = dPsi_dTheta(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) * mLayerVolFracWatPrime(iLayer) case default; err=20; message=trim(message)//'expect iname_lmpLayer, iname_matLayer, iname_liqLayer, or iname_watLayer'; return end select endif ! if in the soil domain @@ -405,7 +405,7 @@ subroutine updateVarsSundials(& ! compute the critical soil temperature below which ice exists select case(ixDomainType) case(iname_veg, iname_snow); Tcrit = Tfreeze - case(iname_soil); Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) + case(iname_soil); Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return end select @@ -463,13 +463,13 @@ subroutine updateVarsSundials(& else select case(ixDomainType) case(iname_veg); dTheta_dTkCanopy = 0._rkind - case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind + case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return end select ! domain type endif - - - + + + ! ----- ! - update volumetric fraction of liquid water and ice... @@ -498,7 +498,7 @@ subroutine updateVarsSundials(& select case(ixDomainType) ! *** vegetation canopy - case(iname_veg) + case(iname_veg) ! compute mass of liquid water and ice call updateVegSundials(& xTemp, & ! intent(in) : temperature (K) @@ -516,13 +516,13 @@ subroutine updateVarsSundials(& ! *** snow layers case(iname_snow) - + call updateSnowSundials(& xTemp, & ! intent(in) : temperature (K) mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) mLayerTempPrime(iLayer), & ! - mLayerVolFracWatPrime(iLayer), & ! intent(in) + mLayerVolFracWatPrime(iLayer), & ! intent(in) mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) mLayerVolFracLiqPrime(iLayer), & ! intent(out) @@ -548,7 +548,7 @@ subroutine updateVarsSundials(& vGn_n(ixControlIndex), & theta_sat(ixControlIndex), & theta_res(ixControlIndex), & - vGn_m(ixControlIndex), & + vGn_m(ixControlIndex), & mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) From 364a099ba1b81a19cbf5d7a35766f5b1c0fdb3a2 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 29 Apr 2022 08:21:07 +0900 Subject: [PATCH 0289/1472] First cut at derivatives for new equation formulation --- build/source/dshare/get_ixname.f90 | 1 + build/source/dshare/popMetadat.f90 | 5 +- build/source/dshare/var_lookup.f90 | 3 +- build/source/engine/computJacDAE.f90 | 52 +++++++++++++++--- build/source/engine/updatStateSundials.f90 | 35 ++++++------ build/source/engine/updateVars4JacDAE.f90 | 64 +++++++++++++++------- 6 files changed, 110 insertions(+), 50 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index fca7e975a..3405c4fe1 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -776,6 +776,7 @@ function get_ixderiv(varName) ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature case('dFracLiqSnow_dTk' ); get_ixderiv = iLookDERIV%dFracLiqSnow_dTk ! derivative in fraction of liquid snow w.r.t. temperature case('mLayerdTheta_dTk' ); get_ixderiv = iLookDERIV%mLayerdTheta_dTk ! derivative of volumetric liquid water content w.r.t. temperature (K-1) + case('mLayerdTheta_dTkPrev' ); get_ixderiv = iLookDERIV%mLayerdTheta_dTkPrev ! previous timestep derivative of volumetric liquid water content w.r.t. temperature (K-1) case('mLayerd2Theta_dTk2' ); get_ixderiv = iLookDERIV%mLayerd2Theta_dTk2 ! second derivative of volumetric liquid water content w.r.t. temperature ! derivate in bulk heat capacity w.r.t. relevant state variables case('dVolHtCapBulk_dPsi0' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dPsi0 ! derivative in bulk heat capacity w.r.t. matric potential diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 2e7483f68..ccbbcdfea 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -610,6 +610,7 @@ subroutine popMetadat(err,message) ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature deriv_meta(iLookDERIV%dFracLiqSnow_dTk) = var_info('dFracLiqSnow_dTk' , 'derivative in fraction of liquid snow w.r.t. temperature' , 'K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%mLayerdTheta_dTk) = var_info('mLayerdTheta_dTk' , 'derivative of volumetric liquid water content w.r.t. temperature' , 'K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%mLayerdTheta_dTkPrev) = var_info('mLayerdTheta_dTkPrev' , 'previous timestep derivative of volumetric liquid water content w.r.t. temperature','K-1', get_ixVarType('midToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%mLayerd2Theta_dTk2) = var_info('mLayerd2Theta_dTk2' , 'second derivative of volumetric liquid water content w.r.t. temperature', 'K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) ! derivate in bulk heat capacity w.r.t. relevant state variables deriv_meta(iLookDERIV%dVolHtCapBulk_dPsi0) = var_info('dVolHtCapBulk_dPsi0' , 'derivative in bulk heat capacity w.r.t. matric potential' , 'J m-4 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) @@ -756,7 +757,7 @@ subroutine read_output_file(err,message) USE globalData, only: flux_meta ! data structure for local flux variables USE globalData, only: deriv_meta ! data structure for local flux derivatives USE globalData, only: outputPrecision ! data structure for output precision - USE globalData, only: outputCompressionLevel ! data structure for output netcdf deflate level + USE globalData, only: outputCompressionLevel ! data structure for output netcdf deflate level ! structures of named variables USE var_lookup, only: iLookTYPE ! named variables for categorical data @@ -863,7 +864,7 @@ subroutine read_output_file(err,message) end if cycle end if - + ! set output netcdf file compression level if given. default is level 4. if (trim(varName)=='outputCompressionLevel') then statName = trim(lineWords(nWords)) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 482433254..b3d3ffa3b 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -629,6 +629,7 @@ MODULE var_lookup ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature integer(i4b) :: dFracLiqSnow_dTk = integerMissing ! derivative in fraction of liquid snow w.r.t. temperature integer(i4b) :: mLayerdTheta_dTk = integerMissing ! derivative of volumetric liquid water content w.r.t. temperature (K-1) + integer(i4b) :: mLayerdTheta_dTkPrev = integerMissing ! previous timestep derivative of volumetric liquid water content w.r.t. temperature (K-1) integer(i4b) :: mLayerd2Theta_dTk2 = integerMissing ! second derivative of volumetric liquid water content w.r.t. temperature ! derivate in bulk heat capacity w.r.t. relevant state variables integer(i4b) :: dVolHtCapBulk_dPsi0 = integerMissing ! derivative in bulk heat capacity w.r.t. matric potential @@ -879,7 +880,7 @@ MODULE var_lookup 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,& 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,& 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,& - 61, 62, 63, 64, 65) + 61, 62, 63, 64, 65, 66) ! named variables: model indices type(iLook_index), public,parameter :: iLookINDEX =ilook_index ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index df36c9586..de1e678b3 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -276,6 +276,7 @@ subroutine computJacDAE(& ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature dFracLiqSnow_dTk => deriv_data%var(iLookDERIV%dFracLiqSnow_dTk )%dat ,& ! intent(in): [dp(:)] derivative in fraction of liquid snow w.r.t. temperature mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk )%dat ,& ! intent(in): [dp(:)] derivative in volumetric liquid water content w.r.t. temperature + mLayerdTheta_dTkPrev => deriv_data%var(iLookDERIV%mLayerdTheta_dTkPrev )%dat ,& ! intent(in): [dp(:)] previous timestep derivative in volumetric liquid water content w.r.t. temperature mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2 )%dat ,& ! intent(in): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature ! derivate in bulk heat capacity w.r.t. relevant state variables dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential @@ -451,6 +452,36 @@ subroutine computJacDAE(& end do ! (looping through energy states in the snow+soil domain) endif ! (if the subset includes energy state variables in the snow+soil domain) + + ! ----- + ! * energy fluxes in the soil domain if changed frozen status... + ! ---------------------------------------- + if(nSoilOnlyNrg>0)then + do iLayer=1,nSoilOnlyNrg + + ! - check that the soil layer is desired + if(ixSoilOnlyNrg(iLayer)==integerMissing) cycle + + ! - define indices of the soil layers + jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + + ! - define the energy state variable + nrgState = ixNrgLayer(jLayer) ! index within the full state vector + + ! - consider if switched frozen status from previous state + if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present + if(mLayerdTheta_dTkPrev(jLayer) <= verySmall)then ! no ice on previous time step + aJac(jState,jState) = LH_fus*iden_water * ( -mLayerdTheta_dTk(jLayer)*cj - mLayerTempPrime(jLayer)*mLayerd2Theta_dTk2(jLayer) + mLayerdTheta_dTk(jLayer)/dt ) + aJac(jState,jState) + endif + else ! no ice currently + if(mLayerdTheta_dTkPrev(jLayer) > verySmall)then ! ice was present on previous time step + aJac(jState,jState) = LH_fus*iden_water * ( mLayerdTheta_dTkPrev(jLayer)*cj - mLayerTempPrime(jLayer)*mLayerdTheta_dTkPrev(jLayer)/dt ) + aJac(jState,jState) + endif + endif + + end do ! (looping through energy states in the soil domain) + endif ! (if the subset includes energy state variables in the soil domain) + ! ----- ! * liquid water fluxes for the snow domain... ! -------------------------------------------- @@ -538,7 +569,6 @@ subroutine computJacDAE(& ! * liquid water fluxes for the soil domain... ! -------------------------------------------- if(nSoilOnlyHyd>0)then - do iLayer=1,nSoil ! - check that the soil layer is desired @@ -607,6 +637,7 @@ subroutine computJacDAE(& ! ---------------------------------------- if(nSoilOnlyHyd>0 .and. nSoilOnlyNrg>0)then do iLayer=1,nSoilOnlyNrg + ! - check that the soil layer is desired if(ixSoilOnlyNrg(iLayer)==integerMissing) cycle @@ -653,12 +684,19 @@ subroutine computJacDAE(& aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTGround(iLayer)) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) endif - ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer + ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer, need to consider if switched frozen status from previous state aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) & + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) - if(mLayerdTheta_dTk(jLayer) > verySmall) then ! ice is present - aJac(nrgState,watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water*cj & - - LH_fus*iden_water * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present + aJac(nrgState,watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) * cj & + - LH_fus*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_d2Psi0(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + if(mLayerdTheta_dTkPrev(jLayer) <= verySmall)then ! no ice on previous time step + aJac(nrgState,watState) = LH_fus*iden_water * ( dVolTot_dPsi0(iLayer)*cj + mLayerMatricHeadPrime(iLayer)*d2VolTot_d2Psi0(iLayer) - dVolTot_dPsi0(iLayer)/dt ) + aJac(nrgState,watState) + endif + else ! no ice currently + if(mLayerdTheta_dTkPrev(jLayer) > verySmall)then ! ice was present on previous time step + aJac(nrgState,watState) = LH_fus*iden_water * ( -dVolTot_dPsi0(iLayer)*cj - mLayerMatricHeadPrime(iLayer)*d2VolTot_d2Psi0(iLayer) + dVolTot_dPsi0(iLayer)/dt ) + aJac(nrgState,watState) + endif endif ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above @@ -689,8 +727,6 @@ subroutine computJacDAE(& endif ! (if there are state variables for both water and energy in the soil domain) - - ! print the Jacobian if(globalPrintFlag)then print*, '** analytical Jacobian (full):' @@ -720,11 +756,9 @@ subroutine computJacDAE(& err=20; return endif - ! end association to variables in the data structures end associate - end subroutine computJacDAE diff --git a/build/source/engine/updatStateSundials.f90 b/build/source/engine/updatStateSundials.f90 index 23de661e0..fcef7a64a 100644 --- a/build/source/engine/updatStateSundials.f90 +++ b/build/source/engine/updatStateSundials.f90 @@ -177,9 +177,10 @@ subroutine updateSoilSundials(& character(*),intent(out) :: message ! error message ! define local variables real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) + real(rkind) :: TcSoilPrev ! previous timestep critical soil temperature when all water is unfrozen (K) real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) - real(rkind) :: dt_inv ! inverse of timestep + real(rkind) :: dt_inv ! inverse of timestep real(rkind) :: mLayerTempPrev ! estimate of previous timestep temperature (K) real(rkind) :: mLayerVolFracLiqPrev ! previous timestep volumetric fraction of liquid water (-) ! initialize error control @@ -195,12 +196,12 @@ subroutine updateSoilSundials(& endif mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev)*dt_inv - if(mLayerVolFracWat > theta_sat)then; err=20; message=trim(message)//'volume of liquid and ice exceeds porosity'; return; end if ! compute the critical soil temperature where all water is unfrozen (K) ! (eq 17 in Dall'Amico 2011) TcSoil = Tfreeze + min(mLayerMatricHead,0._rkind)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) + TcSoilPrev = Tfreeze + min(mLayerMatricHead - mLayerMatricHeadPrime*dt_cur,0._rkind)*gravity*Tfreeze/LH_fus ! *** compute volumetric fraction of liquid water and ice for partially frozen soil if(mLayerTemp < TcSoil)then ! (check if soil temperature is less than the critical temperature) @@ -210,7 +211,7 @@ subroutine updateSoilSundials(& xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if( mLayerTemp - mLayerTempPrime*dt_cur < TcSoil )then + if( mLayerTemp - mLayerTempPrime*dt_cur < TcSoilPrev )then if(mLayerPsiLiq<0._rkind)then mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime else @@ -218,14 +219,14 @@ subroutine updateSoilSundials(& endif else ! was unfrozen on previous time step mLayerVolFracLiqPrev = mLayerVolFracWatPrev - mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv + ! using mLayerVolFracLiqPrev = mLayerVolFracWatPrev + mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracWatPrev)*dt_inv ! = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv endif ! - volumetric ice content (-) mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime - ! *** compute volumetric fraction of liquid water and ice for unfrozen soil else !( mLayerTemp >= TcSoil) @@ -233,16 +234,16 @@ subroutine updateSoilSundials(& mLayerVolFracLiq = mLayerVolFracWat mLayerVolFracIce = 0._rkind - if ( mLayerTemp - mLayerTempPrime*dt_cur >= TcSoil )then + if ( mLayerTemp - mLayerTempPrime*dt_cur >= TcSoilPrev )then mLayerVolFracLiqPrime = mLayerVolFracWatPrime mLayerVolFracIcePrime = 0._rkind else ! was partially frozen on previous time step - mLayerTempPrev = mLayerTemp-mLayerTempPrime + mLayerTempPrev = mLayerTemp - mLayerTempPrime*dt_cur xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*(mLayerTempPrev - Tfreeze) ! liquid water matric potential from the Clapeyron eqution mLayerVolFracLiqPrev = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv - mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime + mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime ! = mLayerVolFracIcePrev*dt_inv endif end if ! (check if soil is partially frozen) @@ -300,9 +301,10 @@ subroutine updateSoilSundials2(& character(*),intent(out) :: message ! error message ! define local variables real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) + real(rkind) :: TcSoilPrev ! previous timestep critical soil temperature when all water is unfrozen (K) real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) - real(rkind) :: dt_inv ! inverse of timestep + real(rkind) :: dt_inv ! inverse of timestep real(rkind) :: mLayerTempPrev ! estimate of previous timestep temperature (K) real(rkind) :: mLayerVolFracLiqPrev ! previous timestep volumetric fraction of liquid water (-) ! initialize error control @@ -310,16 +312,15 @@ subroutine updateSoilSundials2(& ! compute fractional **volume** of total water (liquid plus ice) mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + dt_inv = 1._rkind/ dt_cur mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime - dt_inv = 1._rkind/ dt_cur !WHY NOT JUST USE THIS - - if(mLayerVolFracWat > theta_sat)then; err=20; message=trim(message)//'volume of liquid and ice exceeds porosity'; return; end if ! compute the critical soil temperature where all water is unfrozen (K) ! (eq 17 in Dall'Amico 2011) TcSoil = Tfreeze + min(mLayerMatricHead,0._rkind)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) + TcSoilPrev = Tfreeze + min(mLayerMatricHead - mLayerMatricHeadPrime*dt_cur,0._rkind)*gravity*Tfreeze/LH_fus ! *** compute volumetric fraction of liquid water and ice for partially frozen soil if( mLayerTemp < TcSoil )then ! (check if soil temperature is less than the critical temperature) @@ -329,15 +330,15 @@ subroutine updateSoilSundials2(& xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if( mLayerTemp - mLayerTempPrime*dt_cur < TcSoil )then + if( mLayerTemp - mLayerTempPrime*dt_cur < TcSoilPrev )then if(mLayerPsiLiq<0._rkind)then mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime else mLayerVolFracLiqPrime = 0._rkind endif else ! was unfrozen on previous time step - mLayerVolFracLiqPrev = mLayerVolFracWat - mLayerVolFracWatPrime*dt_cur - mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv + ! using mLayerVolFracLiqPrev = mLayerVolFracWat - mLayerVolFracWatPrime*dt_cur + mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracWat)*dt_inv + mLayerVolFracWatPrime ! = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv endif ! - volumetric ice content (-) @@ -351,7 +352,7 @@ subroutine updateSoilSundials2(& mLayerVolFracLiq = mLayerVolFracWat mLayerVolFracIce = 0._rkind - if ( mLayerTemp - mLayerTempPrime*dt_cur >= TcSoil )then + if( mLayerTemp - mLayerTempPrime*dt_cur >= TcSoilPrev )then mLayerVolFracLiqPrime = mLayerVolFracWatPrime mLayerVolFracIcePrime = 0._rkind else ! was partially frozen on previous time step @@ -360,7 +361,7 @@ subroutine updateSoilSundials2(& mLayerPsiLiq = xConst*(mLayerTempPrev - Tfreeze) ! liquid water matric potential from the Clapeyron eqution mLayerVolFracLiqPrev = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv - mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime + mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime ! = mLayerVolFracIcePrev*dt_inv endif end if ! (check if soil is partially frozen) diff --git a/build/source/engine/updateVars4JacDAE.f90 b/build/source/engine/updateVars4JacDAE.f90 index 0c791842a..e56692004 100644 --- a/build/source/engine/updateVars4JacDAE.f90 +++ b/build/source/engine/updateVars4JacDAE.f90 @@ -193,7 +193,9 @@ subroutine updateVars4JacDAE(& real(rkind) :: scalarVolFracLiqPrime ! volumetric fraction of liquid water (-) real(rkind) :: scalarVolFracIcePrime ! volumetric fraction of ice (-) real(rkind) :: Tcrit ! critical soil temperature below which ice exists (K) + real(rkind) :: TcritPrev ! previous timestep critical soil temperature below which ice exists (K) real(rkind) :: xTemp ! temporary temperature (K) + real(rkind) :: xTempPrev ! previous timestep temperature for layer (K) real(rkind) :: fLiq ! fraction of liquid water (-) real(rkind) :: effSat ! effective saturation (-) real(rkind) :: avPore ! available pore space (-) @@ -272,6 +274,7 @@ subroutine updateVars4JacDAE(& dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(out): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + mLayerdTheta_dTkPrev => deriv_data%var(iLookDERIV%mLayerdTheta_dTkPrev)%dat ,& ! intent(out): [dp(:)] previous timestep derivative of volumetric liquid water content w.r.t. temperature dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature d2VolTot_d2Psi0 => deriv_data%var(iLookDERIV%d2VolTot_d2Psi0 )%dat ,& ! intent(out): [dp(:)] second derivative in total water content w.r.t. total water matric potential mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2 )%dat ,& ! intent(out): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature @@ -408,11 +411,10 @@ subroutine updateVars4JacDAE(& endif ! if hydrology state variable or uncoupled solution - ! compute the critical soil temperature below which ice exists select case(ixDomainType) - case(iname_veg, iname_snow); Tcrit = Tfreeze - case(iname_soil); Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) + case(iname_veg, iname_snow); Tcrit = Tfreeze; + case(iname_soil); Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ); case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return end select @@ -423,6 +425,15 @@ subroutine updateVars4JacDAE(& case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return end select + ! compute the previous timestep temperature + select case(ixDomainType) + case(iname_veg, iname_snow); TcritPrev = Tfreeze; xTempPrev = realmissing ! will not use + case(iname_soil) + TcritPrev = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) - mLayerMatricHeadPrime(ixControlIndex)*dt) + xTempPrev = xTemp - mLayerTempPrime(iLayer)*dt + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select + ! define brackets for the root ! NOTE: start with an enormous range; updated quickly in the iterations tempMin = xTemp - 10._rkind @@ -458,19 +469,19 @@ subroutine updateVars4JacDAE(& fLiq = fracLiquid(xTemp,snowfrz_scale) dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air case(iname_soil) - select case( ixStateType(ixFullVector) ) - case(iname_lmpLayer) - dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore - d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore - case default - dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),& - vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - end select - ! dVolHtCapBulk_dPsi0 uses the derivative in water retention curve above critical temp w.r.t.state variable dVolTot_dPsi0 - if(xTemp< Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) - if(xTemp>=Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_water * Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) - + select case( ixStateType(ixFullVector) ) + case(iname_lmpLayer) + dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + case default + dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),& + vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + end select + ! dVolHtCapBulk_dPsi0 uses the derivative in water retention curve above critical temp w.r.t.state variable dVolTot_dPsi0 + dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use + if(xTemp< Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) + if(xTemp>=Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_water * Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) end select ! compute the derivative in liquid water content w.r.t. temperature @@ -490,26 +501,37 @@ subroutine updateVars4JacDAE(& mLayerdTheta_dTk(iLayer) = dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatTrial(iLayer) mLayerd2Theta_dTk2(iLayer) = 2._rkind * snowfrz_scale**2._rkind * ( (Tfreeze - xTemp) * 2._rkind * fLiq * dFracLiqSnow_dTk(iLayer) - fLiq**2._rkind ) * mLayerVolFracWatTrial(iLayer) dVolHtCapBulk_dTk(iLayer) = ( iden_water * (-Cp_ice + Cp_water) + iden_air * (iden_water/iden_ice - 1._rkind) * Cp_air ) * mLayerdTheta_dTk(iLayer) + mLayerdTheta_dTkPrev(iLayer) = realMissing ! do not use case(iname_soil) dFracLiqSnow_dTk(iLayer) = 0._rkind !dTheta_dTk(xTemp,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex))/ mLayerVolFracWatTrial(iLayer) mLayerdTheta_dTk(iLayer) = dTheta_dTk(xTemp,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) mLayerd2Theta_dTk2(iLayer) = d2Theta_dTk2(xTemp,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) dVolHtCapBulk_dTk(iLayer) = (-iden_ice * Cp_ice + iden_water * Cp_water) * mLayerdTheta_dTk(iLayer) + if( xTempPrev < TcritPrev ) then ! was frozen on previous time step + mLayerdTheta_dTkPrev(iLayer) = dTheta_dTk(xTempPrev,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + else + mLayerdTheta_dTkPrev(iLayer) = 0._rkind + endif case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return end select ! domain type ! --> unfrozen: no dependence of liquid water on temperature else select case(ixDomainType) - case(iname_veg); dTheta_dTkCanopy = 0._rkind; d2Theta_dTkCanopy2 = 0._rkind; dFracLiqVeg_dTkCanopy = 0._rkind; dVolHtCapBulk_dTkCanopy = 0._rkind - case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind; mLayerd2Theta_dTk2(iLayer) = 0._rkind; dFracLiqSnow_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind + case(iname_veg); dTheta_dTkCanopy = 0._rkind; d2Theta_dTkCanopy2 = 0._rkind; dFracLiqVeg_dTkCanopy = 0._rkind; dVolHtCapBulk_dTkCanopy = 0._rkind + case(iname_snow); mLayerdTheta_dTk(iLayer) = 0._rkind; mLayerd2Theta_dTk2(iLayer) = 0._rkind; dFracLiqSnow_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind; & + mLayerdTheta_dTkPrev(iLayer) = realMissing ! do not use + case(iname_soil) + mLayerdTheta_dTk(iLayer) = 0._rkind; mLayerd2Theta_dTk2(iLayer) = 0._rkind; dFracLiqSnow_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind + if( xTempPrev < TcritPrev ) then ! was frozen on previous time step + mLayerdTheta_dTkPrev(iLayer) = dTheta_dTk(xTempPrev,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + else + mLayerdTheta_dTkPrev(iLayer) = 0._rkind + endif case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return end select ! domain type endif - - - ! ----- ! - update volumetric fraction of liquid water and ice... ! => case of hydrology state uncoupled with energy (and when not adjusting the temperature)... From 8c8cc74a90070e74881f4c871d089771f085f64b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 29 Apr 2022 21:43:23 +0900 Subject: [PATCH 0290/1472] Derivative index was off --- build/source/engine/computJacDAE.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index de1e678b3..925e79f05 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -466,7 +466,7 @@ subroutine computJacDAE(& jLayer = iLayer+nSnow ! index of layer in the snow+soil vector ! - define the energy state variable - nrgState = ixNrgLayer(jLayer) ! index within the full state vector + jState = ixSoilOnlyNrg(iLayer) ! index within the full state vector ! - consider if switched frozen status from previous state if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present From 2e62cc8038894121204198f9d29526ed5ea759d6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 2 May 2022 16:27:31 +0900 Subject: [PATCH 0291/1472] Switch everything to updateSoilSundials2, which does not use the previous values but instead uses the prime values. This seems more correct and the Jacobians line up better --- build/source/engine/updateVarsSundials.f90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/source/engine/updateVarsSundials.f90 b/build/source/engine/updateVarsSundials.f90 index 5b945a833..f4b86958e 100644 --- a/build/source/engine/updateVarsSundials.f90 +++ b/build/source/engine/updateVarsSundials.f90 @@ -78,7 +78,7 @@ module updateVarsSundials_module ! provide access to routines to update states USE updatStateSundials_module,only:updateVegSundials ! update snow states USE updatStateSundials_module,only:updateSnowSundials ! update snow states -USE updatStateSundials_module,only:updateSoilSundials ! update soil states +USE updatStateSundials_module,only:updateSoilSundials2 ! update soil states ! provide access to functions for the constitutive functions and derivatives USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) @@ -535,12 +535,12 @@ subroutine updateVarsSundials(& case(iname_soil) ! compute volumetric fraction of liquid water and ice - call updateSoilSundials(& + call updateSoilSundials2(& dt_cur, & xTemp, & ! intent(in) : temperature (K) mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) - mLayerMatricHeadPrev(ixControlIndex), & ! intent(in) - mLayerVolFracWatPrev(iLayer), & ! intent(in) + !mLayerMatricHeadPrev(ixControlIndex), & ! intent(in) + !mLayerVolFracWatPrev(iLayer), & ! intent(in) mLayerTempPrime(iLayer), & mLayerMatricHeadPrime(ixControlIndex), & ! intent(in) : soil parameters From edbc40e2634e9583f20477e535cd0414b440a767 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 11 May 2022 10:21:07 +0900 Subject: [PATCH 0292/1472] updating SUMMA_summarize_logs.py and a few comments --- build/source/engine/updatStateSundials.f90 | 8 +++----- utils/SUMMA_summarize_logs.py | 7 +++++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/build/source/engine/updatStateSundials.f90 b/build/source/engine/updatStateSundials.f90 index fcef7a64a..b8c4b1473 100644 --- a/build/source/engine/updatStateSundials.f90 +++ b/build/source/engine/updatStateSundials.f90 @@ -330,7 +330,7 @@ subroutine updateSoilSundials2(& xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if( mLayerTemp - mLayerTempPrime*dt_cur < TcSoilPrev )then + if( mLayerTemp - mLayerTempPrime*dt_cur < TcSoilPrev )then ! was partially frozen on previous time step if(mLayerPsiLiq<0._rkind)then mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime else @@ -346,13 +346,11 @@ subroutine updateSoilSundials2(& mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime ! *** compute volumetric fraction of liquid water and ice for unfrozen soil - else !( mLayerTemp >= TcSoil) - - ! all water is unfrozen + else !( mLayerTemp >= TcSoil, all water is unfrozen) mLayerVolFracLiq = mLayerVolFracWat mLayerVolFracIce = 0._rkind - if( mLayerTemp - mLayerTempPrime*dt_cur >= TcSoilPrev )then + if( mLayerTemp - mLayerTempPrime*dt_cur >= TcSoilPrev )then ! was unfrozen on previous time step mLayerVolFracLiqPrime = mLayerVolFracWatPrime mLayerVolFracIcePrime = 0._rkind else ! was partially frozen on previous time step diff --git a/utils/SUMMA_summarize_logs.py b/utils/SUMMA_summarize_logs.py index df1952be2..6efb355cd 100644 --- a/utils/SUMMA_summarize_logs.py +++ b/utils/SUMMA_summarize_logs.py @@ -1,6 +1,6 @@ '''Summarize all SUMMA logs in a folder. Assumes all .txt files in folder are SUMMA logs. Summary file is placed inside the log folder. Specifying a summary file name is optional. -Usage: python summarize_logs.py [log_folder] [name_of_summary_file.txt] [log file extension]''' +Usage: python SUMMA_summarize_logs.py [log_folder] [name_of_summary_file.txt] [log file extension]''' # Modules import os @@ -147,6 +147,9 @@ def determine_output(folder,file,nLines=2): # Loop over the log files for file in files: + size = os.path.getsize(folder + '/' + file) + if (size==0): continue + # Find the result contained in each log file success, summa, other, msg, time = determine_output(folder,file) # default of using last 2 lines should suffice to catch all success/error cases @@ -191,4 +194,4 @@ def determine_output(folder,file,nLines=2): #sf.write('Mean time ' + '\t \t \t \t' + str(sts.mean(computation_time)) +'h \n') #sf.write('Max time ' + '\t \t \t \t' + str(max(computation_time)) +'h \n') # done, summary file is closed -# ---------------------------- \ No newline at end of file +# ---------------------------- From 98f2d90b2a2ced4950fa45fff2a75e87cb0404d0 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 12 May 2022 20:55:11 +0900 Subject: [PATCH 0293/1472] print out infeasibilities, leave mlayerVolFracIcePrime change in the updateVarsState but not updateVars4Jac, because there it seems like it wants the instantaneous derivatives. Left the derivative changes in the computeJacDAE. This is probably wrong. Run this on problem HRUs with Finite difference Jac, so only using updateVarsState, and see if they go infeasible and why. Then run without the change. --- build/source/engine/eval8DAE.f90 | 28 ++++--- build/source/engine/updatStateSundials.f90 | 91 +++++++++++----------- build/source/engine/updateVars4JacDAE.f90 | 2 +- build/source/engine/updateVarsSundials.f90 | 25 +++--- build/source/engine/varSubstepSundials.f90 | 45 +++++------ 5 files changed, 102 insertions(+), 89 deletions(-) diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index 6fd8df3b0..b917b2802 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -120,31 +120,31 @@ subroutine eval8DAE(& flux_data, & ! intent(inout): model fluxes for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! input-output: - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) scalarCanopyTempPrev, & ! intent(in): value of canopy temperature (K) - scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) scalarCanopyEnthalpyPrev,& ! intent(in): value for enthalpy of the vegetation canopy (J m-3) mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) mLayerTempPrev, & ! intent(in): vector of layer temperature (K) mLayerMatricHeadLiqTrial,& ! intent(out): trial value for liquid water matric potential (m) mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) - mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) + mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) mLayerVolFracWatPrev, & ! intent(in): vector of volumetric total water content (-) mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) - scalarAquiferStorageTrial, & ! intent(out): trial value of storage of water in the aquifer (m) - scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) + scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) + scalarAquiferStoragePrev,& ! intent(in): value of storage of water in the aquifer (m) mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) - ixSaturation, & ! intent(inout): index of the lowest saturated layer + ixSaturation, & ! intent(inout): index of the lowest saturated layer feasible, & ! intent(out): flag to denote the feasibility of the solution fluxVec, & ! intent(out): flux vector resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation @@ -321,24 +321,29 @@ subroutine eval8DAE(& ! check that the canopy air space temperature is reasonable if(ixCasNrg/=integerMissing)then if(stateVec(ixCasNrg) > canopyTempMax) feasible=.false. + if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( stateVec(ixCasNrg) )', feasible, canopyTempMax, stateVec(ixCasNrg) endif ! check that the canopy air space temperature is reasonable if(ixVegNrg/=integerMissing)then if(stateVec(ixVegNrg) > canopyTempMax) feasible=.false. + if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( stateVec(ixVegNrg) )', feasible, canopyTempMax, stateVec(ixVegNrg) endif ! check canopy liquid water is not negative if(ixVegHyd/=integerMissing)then if(stateVec(ixVegHyd) < 0._rkind) feasible=.false. + if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVec( stateVec(ixVegHyd) )', feasible, 0._rkind, stateVec(ixVegHyd) end if ! check snow temperature is below freezing if(count(ixSnowOnlyNrg/=integerMissing)>0)then if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. + do iLayer=1,nSnow + if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVec( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVec( ixSnowOnlyNrg(iLayer) ) + enddo endif - ! loop through non-missing hydrology state variables in the snow+soil domain do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) @@ -360,7 +365,7 @@ subroutine eval8DAE(& ! --> check if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. - ! if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax + if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax endif ! if water states @@ -452,6 +457,7 @@ subroutine eval8DAE(& mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU + mLayerTempPrev, & ! intent(in) mLayerVolFracWatPrev, & ! intent(in) mLayerMatricHeadPrev, & ! intent(in) diag_data, & ! intent(inout): model diagnostic variables for a local HRU diff --git a/build/source/engine/updatStateSundials.f90 b/build/source/engine/updatStateSundials.f90 index b8c4b1473..27c66b6dd 100644 --- a/build/source/engine/updatStateSundials.f90 +++ b/build/source/engine/updatStateSundials.f90 @@ -124,12 +124,13 @@ end subroutine updateSnowSundials ! ************************************************************************************************************* ! public subroutine updateSoilSundials: compute phase change impacts on matric head and volumetric liquid water and ice - ! uses mLayerMatricHeadPrev and mLayerVolFracWatPrev + ! uses mLayerMatricHeadPrev and mLayerVolFracWatPrev to get dt_cur, or use dt_cur as it can change here ! ************************************************************************************************************* subroutine updateSoilSundials(& ! input dt_cur, & mLayerTemp ,& ! intent(in): temperature (K) + mLayerTempPrev ,& ! intent(in): temperature previous time step (K) mLayerMatricHead ,& ! intent(in): total water matric potential (m) mLayerMatricHeadPrev ,& ! intent(in): total water matric potential previous time step (m) mLayerVolFracWatPrev ,& ! intent(in): volumetric fraction of total water previous time step (-) @@ -156,6 +157,7 @@ subroutine updateSoilSundials(& ! input variables real(rkind),intent(in) :: dt_cur real(rkind),intent(in) :: mLayerTemp ! estimate of temperature (K) + real(rkind),intent(in) :: mLayerTempPrev ! temperature previous time step (K) real(rkind),intent(in) :: mLayerMatricHead ! matric head (m) real(rkind),intent(in) :: mLayerMatricHeadPrev ! matric head previous time step (m) real(rkind),intent(in) :: mLayerVolFracWatPrev ! volumetric fraction of total waterprevious time step (m) @@ -181,8 +183,9 @@ subroutine updateSoilSundials(& real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) real(rkind) :: dt_inv ! inverse of timestep - real(rkind) :: mLayerTempPrev ! estimate of previous timestep temperature (K) + !real(rkind) :: mLayerTempPrev ! estimate of previous timestep temperature (K) real(rkind) :: mLayerVolFracLiqPrev ! previous timestep volumetric fraction of liquid water (-) + real(rkind) :: dt ! initialize error control err=0; message="updateSoilSundials/" @@ -191,8 +194,10 @@ subroutine updateSoilSundials(& ! mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime if( abs(mLayerMatricHead - mLayerMatricHeadPrev) < verySmall )then dt_inv = 1._rkind/ dt_cur !WHY NOT JUST USE THIS + dt = dt_cur else dt_inv = mLayerMatricHeadPrime / (mLayerMatricHead - mLayerMatricHeadPrev) + dt = 1._rkind/ dt_inv endif mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev)*dt_inv @@ -201,17 +206,18 @@ subroutine updateSoilSundials(& ! compute the critical soil temperature where all water is unfrozen (K) ! (eq 17 in Dall'Amico 2011) TcSoil = Tfreeze + min(mLayerMatricHead,0._rkind)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) - TcSoilPrev = Tfreeze + min(mLayerMatricHead - mLayerMatricHeadPrime*dt_cur,0._rkind)*gravity*Tfreeze/LH_fus + !TcSoilPrev = Tfreeze + min(mLayerMatricHead - mLayerMatricHeadPrime*dt,0._rkind)*gravity*Tfreeze/LH_fus + TcSoilPrev = Tfreeze + min(mLayerMatricHeadPrev,0._rkind)*gravity*Tfreeze/LH_fus - ! *** compute volumetric fraction of liquid water and ice for partially frozen soil + ! *** compute volumetric fraction of liquid water for partially frozen soil if(mLayerTemp < TcSoil)then ! (check if soil temperature is less than the critical temperature) - ! - volumetric liquid water content (-) ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if( mLayerTemp - mLayerTempPrime*dt_cur < TcSoilPrev )then + !if( mLayerTemp - mLayerTempPrime*dt < TcSoilPrev )then + if( mLayerTempPrev < TcSoilPrev )then if(mLayerPsiLiq<0._rkind)then mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime else @@ -222,37 +228,37 @@ subroutine updateSoilSundials(& ! using mLayerVolFracLiqPrev = mLayerVolFracWatPrev mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracWatPrev)*dt_inv ! = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv endif + if ( mLayerTemp - mLayerTempPrime*dt >= TcSoilPrev )print*,"froze" + if ( mLayerTempPrev >= TcSoilPrev )print*,"FROZE2" - ! - volumetric ice content (-) - mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq - mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime - - ! *** compute volumetric fraction of liquid water and ice for unfrozen soil - else !( mLayerTemp >= TcSoil) - - ! all water is unfrozen + ! *** compute volumetric fraction of liquid water for unfrozen soil + else !( mLayerTemp >= TcSoil, all water is unfrozen ) mLayerVolFracLiq = mLayerVolFracWat - mLayerVolFracIce = 0._rkind - - if ( mLayerTemp - mLayerTempPrime*dt_cur >= TcSoilPrev )then + !if ( mLayerTemp - mLayerTempPrime*dt >= TcSoilPrev )then + if ( mLayerTempPrev >= TcSoilPrev )then mLayerVolFracLiqPrime = mLayerVolFracWatPrime mLayerVolFracIcePrime = 0._rkind else ! was partially frozen on previous time step - mLayerTempPrev = mLayerTemp - mLayerTempPrime*dt_cur + !mLayerTempPrev = mLayerTemp - mLayerTempPrime*dt xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*(mLayerTempPrev - Tfreeze) ! liquid water matric potential from the Clapeyron eqution mLayerVolFracLiqPrev = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv - mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime ! = mLayerVolFracIcePrev*dt_inv endif + if ( mLayerTemp - mLayerTempPrime*dt < TcSoilPrev )print*,"thawed" + if ( mLayerTempPrev < TcSoilPrev )print*,"THAWED2" end if ! (check if soil is partially frozen) + ! - volumetric ice content (-) + mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq + mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime + end subroutine updateSoilSundials ! ************************************************************************************************************* ! public subroutine updateSoilSundials: compute phase change impacts on matric head and volumetric liquid water and ice - ! uses mLayerMatricHeadPrime + ! uses mLayerMatricHeadPrime, and wants instantaneous dt, dt_cur will always be a full step size as input here ! ************************************************************************************************************* subroutine updateSoilSundials2(& ! input @@ -322,47 +328,44 @@ subroutine updateSoilSundials2(& TcSoil = Tfreeze + min(mLayerMatricHead,0._rkind)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) TcSoilPrev = Tfreeze + min(mLayerMatricHead - mLayerMatricHeadPrime*dt_cur,0._rkind)*gravity*Tfreeze/LH_fus - ! *** compute volumetric fraction of liquid water and ice for partially frozen soil + ! *** compute volumetric fraction of liquid water for partially frozen soil if( mLayerTemp < TcSoil )then ! (check if soil temperature is less than the critical temperature) - ! - volumetric liquid water content (-) ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if( mLayerTemp - mLayerTempPrime*dt_cur < TcSoilPrev )then ! was partially frozen on previous time step + !if( mLayerTemp - mLayerTempPrime*dt_cur < TcSoilPrev )then ! was partially frozen on previous time step if(mLayerPsiLiq<0._rkind)then mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime else mLayerVolFracLiqPrime = 0._rkind endif - else ! was unfrozen on previous time step + !else ! was unfrozen on previous time step ! using mLayerVolFracLiqPrev = mLayerVolFracWat - mLayerVolFracWatPrime*dt_cur - mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracWat)*dt_inv + mLayerVolFracWatPrime ! = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv - endif - - ! - volumetric ice content (-) - mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq - mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime + !mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracWat)*dt_inv + mLayerVolFracWatPrime ! = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv + !print*,"froze", mLayerVolFracLiqPrime,mLayerTemp,mLayerTempPrime,dt_cur,mLayerTemp - mLayerTempPrime*dt_cur-TcSoilPrev + !endif - ! *** compute volumetric fraction of liquid water and ice for unfrozen soil - else !( mLayerTemp >= TcSoil, all water is unfrozen) + ! *** compute volumetric fraction of liquid water for unfrozen soil + else !( mLayerTemp >= TcSoil, all water is unfrozen ) mLayerVolFracLiq = mLayerVolFracWat - mLayerVolFracIce = 0._rkind - - if( mLayerTemp - mLayerTempPrime*dt_cur >= TcSoilPrev )then ! was unfrozen on previous time step + !if( mLayerTemp - mLayerTempPrime*dt_cur >= TcSoilPrev )then ! was unfrozen on previous time step mLayerVolFracLiqPrime = mLayerVolFracWatPrime - mLayerVolFracIcePrime = 0._rkind - else ! was partially frozen on previous time step - mLayerTempPrev = mLayerTemp - mLayerTempPrime*dt_cur - xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) - mLayerPsiLiq = xConst*(mLayerTempPrev - Tfreeze) ! liquid water matric potential from the Clapeyron eqution - mLayerVolFracLiqPrev = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv - mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime ! = mLayerVolFracIcePrev*dt_inv - endif + !else ! was partially frozen on previous time step + !mLayerTempPrev = mLayerTemp - mLayerTempPrime*dt_cur + !xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) + !mLayerPsiLiq = xConst*(mLayerTempPrev - Tfreeze) ! liquid water matric potential from the Clapeyron eqution + !mLayerVolFracLiqPrev = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + !mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv + !print*,"thawed", mLayerVolFracLiqPrime,mLayerTemp,mLayerTempPrime,dt_cur,mLayerTemp - mLayerTempPrime*dt_cur-TcSoilPrev + !endif end if ! (check if soil is partially frozen) + ! - volumetric ice content (-) + mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq + mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime + end subroutine updateSoilSundials2 end module updatStateSundials_module diff --git a/build/source/engine/updateVars4JacDAE.f90 b/build/source/engine/updateVars4JacDAE.f90 index e56692004..14fd1f6bf 100644 --- a/build/source/engine/updateVars4JacDAE.f90 +++ b/build/source/engine/updateVars4JacDAE.f90 @@ -615,7 +615,7 @@ subroutine updateVars4JacDAE(& mLayerVolFracWatPrime(iLayer), & ! intent(out) : volumetric fraction of total water time derivative (-) mLayerVolFracLiqPrime(iLayer), & ! intent(out) : volumetric fraction of liquid water time derivative (-) mLayerVolFracIcePrime(iLayer), & ! intent(out) : volumetric fraction of ice time derivative (-) - err,cmessage) ! intent(out) : error control + err,cmessage) ! intent(out) : error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! check diff --git a/build/source/engine/updateVarsSundials.f90 b/build/source/engine/updateVarsSundials.f90 index f4b86958e..e6285c2c4 100644 --- a/build/source/engine/updateVarsSundials.f90 +++ b/build/source/engine/updateVarsSundials.f90 @@ -78,7 +78,7 @@ module updateVarsSundials_module ! provide access to routines to update states USE updatStateSundials_module,only:updateVegSundials ! update snow states USE updatStateSundials_module,only:updateSnowSundials ! update snow states -USE updatStateSundials_module,only:updateSoilSundials2 ! update soil states +USE updatStateSundials_module,only:updateSoilSundials ! update soil states ! provide access to functions for the constitutive functions and derivatives USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) @@ -110,6 +110,7 @@ subroutine updateVarsSundials(& mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU + mLayerTempPrev, & ! intent(in) mLayerVolFracWatPrev, & ! intent(in) mLayerMatricHeadPrev, & ! intent(in) diag_data, & ! intent(inout): model diagnostic variables for a local HRU @@ -147,8 +148,9 @@ subroutine updateVarsSundials(& type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - real(rkind),intent(in) :: mLayerMatricHeadPrev(:) + real(rkind),intent(in) :: mLayerTempPrev(:) real(rkind),intent(in) :: mLayerVolFracWatPrev(:) + real(rkind),intent(in) :: mLayerMatricHeadPrev(:) type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy @@ -213,8 +215,8 @@ subroutine updateVarsSundials(& real(rkind) :: critDiff ! temperature difference from critical (K) real(rkind) :: tempMin ! minimum bracket for temperature (K) real(rkind) :: tempMax ! maximum bracket for temperature (K) - logical(lgt) :: bFlag ! flag to denote that iteration increment was constrained using bi-section - real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical temperature (K) + logical(lgt) :: bFlag ! flag to denote that iteration increment was constrained using bi-section + real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical temperature (K) ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures associate(& @@ -534,13 +536,14 @@ subroutine updateVarsSundials(& ! *** soil layers case(iname_soil) - ! compute volumetric fraction of liquid water and ice - call updateSoilSundials2(& + ! compute volumetric fraction of liquid water and ice, step size dt_cur changes here + call updateSoilSundials(& dt_cur, & xTemp, & ! intent(in) : temperature (K) + mLayerTempPrev(iLayer), & ! intent(in) : temperature previous time step (K) mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) - !mLayerMatricHeadPrev(ixControlIndex), & ! intent(in) - !mLayerVolFracWatPrev(iLayer), & ! intent(in) + mLayerMatricHeadPrev(ixControlIndex), & ! intent(in) + mLayerVolFracWatPrev(iLayer), & ! intent(in) mLayerTempPrime(iLayer), & mLayerMatricHeadPrime(ixControlIndex), & ! intent(in) : soil parameters @@ -555,7 +558,7 @@ subroutine updateVarsSundials(& mLayerVolFracWatPrime(iLayer), & mLayerVolFracLiqPrime(iLayer), & mLayerVolFracIcePrime(iLayer), & - err,cmessage) ! intent(out) : error control + err,cmessage) ! intent(out) : error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! check @@ -625,9 +628,9 @@ subroutine updateVarsSundials(& vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in) : soil parameters dVolTot_dPsi0(ixControlIndex) ,& ! intent(in) : derivative in the soil water characteristic (m-1) mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) - mLayerTempPrime(ixControlIndex) ,& + mLayerTempPrime(ixControlIndex) ,& mLayerVolFracLiqPrime(iLayer) ,& - mLayerVolFracIcePrime(iLayer) ,& + mLayerVolFracIcePrime(iLayer) ,& ! output mLayerMatricHeadLiqTrial(ixControlIndex) ,& ! intent(out): liquid water matric potential (m) mLayerMatricHeadLiqPrime(ixControlIndex) ,& ! diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index bd2f662fc..b1892b8aa 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -307,7 +307,7 @@ subroutine varSubstepSundials(& stateVecInit, & ! intent(out): initial model state vector (mixed units) err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - + ! ----- ! * iterative solution... ! ----------------------- @@ -343,12 +343,12 @@ subroutine varSubstepSundials(& tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt dt_out, & ! intent(out): time step (s) err,cmessage) ! intent(out): error code and error message - + if(err/=0)then message=trim(message)//trim(cmessage) if(err>0) return endif - + ! set untapped melt energy to zero untappedMelt(:) = 0._rkind @@ -396,7 +396,7 @@ subroutine varSubstepSundials(& endif ! identify the need to check the mass balance - checkMassBalance = .true. ! (.not.scalarSolution) + checkMassBalance = .true. ! (.not.scalarSolution) checkNrgBalance = .true. ! update prognostic variables @@ -532,7 +532,7 @@ subroutine varSubstepSundials(& err=-20 ! negative = recoverable error message=trim(message)//'failed minimum step' endif - + end subroutine varSubstepSundials @@ -560,7 +560,7 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux real(rkind) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) real(rkind) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance - logical(lgt) ,intent(in) :: checkNrgBalance ! flag to check the energy balance + logical(lgt) ,intent(in) :: checkNrgBalance ! flag to check the energy balance ! data structures type(zLookup),intent(in) :: lookup_data ! lookup tables type(var_dlength),intent(in) :: mpar_data ! model parameters @@ -643,7 +643,7 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(in) : [dp] canopy transpiration (kg m-2 s-1) scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(in) : [dp] drainage liquid water from vegetation canopy (kg m-2 s-1) iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(in) : [dp(0:)] vertical liquid water flux at soil layer interfaces (-) - iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(in) : + iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(in) : mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(in) : [dp(:)] transpiration loss from each soil layer (m s-1) mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(in) : [dp(:)] baseflow from each soil layer (m s-1) @@ -717,8 +717,8 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - call varExtractSundials(& + + call varExtractSundials(& ! input stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -740,18 +740,19 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - + ! update diagnostic variables - call updateVarsSundials(& + call updateVarsSundials(& ! input dt, & doAdjustTemp, & ! intent(in): logical flag to adjust temperature to accou melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU - mLayerVolFracWatTrial, & - mLayerMatricHeadTrial, & + mLayerTempTrial, & + mLayerVolFracWatTrial, & + mLayerMatricHeadTrial, & diag_data, & ! intent(inout): model diagnostic variables for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy @@ -770,21 +771,21 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! + mLayerTempPrime, & ! mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) - mLayerVolFracIcePrime, & ! + mLayerVolFracIcePrime, & ! mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - + ! ---- ! * check energy balance !------------------------ ! NOTE: for now, we just compute enthalpy - if(checkNrgBalance)then + if(checkNrgBalance)then ! compute enthalpy at t_{n+1} call t2enthalpy(& ! input: data structures @@ -809,7 +810,7 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - + endif ! ----- @@ -888,7 +889,7 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) - ! write(1,*) liqError + ! write(1,*) liqError if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues write(*,'(a,1x,f20.10)') 'dt = ', dt write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 @@ -1011,7 +1012,7 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux if(scalarCanopyLiqTrial > -verySmall)then scalarCanopyIceTrial = scalarCanopyIceTrial - scalarCanopyLiqTrial scalarCanopyLiqTrial = 0._rkind - + ! encountered an inconsistency: spit the dummy else @@ -1053,7 +1054,7 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux endif ! (if we removed too much water) endif ! (if energy state variables exist) - + ! ----- ! * update enthalpy as a diagnostic variable... ! -------------------------------- @@ -1081,7 +1082,7 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux ! update state variables for the aquifer scalarAquiferStorage = scalarAquiferStorageTrial - + ! end associations to info in the data structures end associate From 320997942503228a0e76a375d9f1523e0acf7d2f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 16 May 2022 16:48:21 +0900 Subject: [PATCH 0294/1472] more specific error messages --- build/source/engine/eval8DAE.f90 | 7 ++++++- build/source/engine/updatStateSundials.f90 | 1 - 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index b917b2802..0a9dbad7e 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -321,24 +321,28 @@ subroutine eval8DAE(& ! check that the canopy air space temperature is reasonable if(ixCasNrg/=integerMissing)then if(stateVec(ixCasNrg) > canopyTempMax) feasible=.false. + if(stateVec(ixCasNrg) > canopyTempMax) message=trim(message)//'canopy air space temperature too high ' if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( stateVec(ixCasNrg) )', feasible, canopyTempMax, stateVec(ixCasNrg) endif - ! check that the canopy air space temperature is reasonable + ! check that the canopy temperature is reasonable if(ixVegNrg/=integerMissing)then if(stateVec(ixVegNrg) > canopyTempMax) feasible=.false. + if(stateVec(ixVegNrg) > canopyTempMax) message=trim(message)//'canopy temperature too high ' if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( stateVec(ixVegNrg) )', feasible, canopyTempMax, stateVec(ixVegNrg) endif ! check canopy liquid water is not negative if(ixVegHyd/=integerMissing)then if(stateVec(ixVegHyd) < 0._rkind) feasible=.false. + if(stateVec(ixVegHyd) < 0._rkind) message=trim(message)//'canopy water is negative ' if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVec( stateVec(ixVegHyd) )', feasible, 0._rkind, stateVec(ixVegHyd) end if ! check snow temperature is below freezing if(count(ixSnowOnlyNrg/=integerMissing)>0)then if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. + if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) message=trim(message)//'snow temperature is above freezing ' do iLayer=1,nSnow if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVec( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVec( ixSnowOnlyNrg(iLayer) ) enddo @@ -365,6 +369,7 @@ subroutine eval8DAE(& ! --> check if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. + if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) message=trim(message)//'layer water is outside of bounds ' if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax endif ! if water states diff --git a/build/source/engine/updatStateSundials.f90 b/build/source/engine/updatStateSundials.f90 index 27c66b6dd..baa9126a9 100644 --- a/build/source/engine/updatStateSundials.f90 +++ b/build/source/engine/updatStateSundials.f90 @@ -206,7 +206,6 @@ subroutine updateSoilSundials(& ! compute the critical soil temperature where all water is unfrozen (K) ! (eq 17 in Dall'Amico 2011) TcSoil = Tfreeze + min(mLayerMatricHead,0._rkind)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) - !TcSoilPrev = Tfreeze + min(mLayerMatricHead - mLayerMatricHeadPrime*dt,0._rkind)*gravity*Tfreeze/LH_fus TcSoilPrev = Tfreeze + min(mLayerMatricHeadPrev,0._rkind)*gravity*Tfreeze/LH_fus ! *** compute volumetric fraction of liquid water for partially frozen soil From 315fd6e3df44bdfa51fecaec54c1d35ab615b974 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 17 May 2022 23:00:21 +0900 Subject: [PATCH 0295/1472] Update error message and only have change of ice prime in outer loop that might go over freeze point, inside sundials is instantaneous. --- build/source/engine/computJacDAE.f90 | 32 +++++++++++----------- build/source/engine/eval8DAE.f90 | 12 ++++---- build/source/engine/updatStateSundials.f90 | 8 +++--- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 925e79f05..3bb59ff30 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -469,15 +469,15 @@ subroutine computJacDAE(& jState = ixSoilOnlyNrg(iLayer) ! index within the full state vector ! - consider if switched frozen status from previous state - if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present - if(mLayerdTheta_dTkPrev(jLayer) <= verySmall)then ! no ice on previous time step - aJac(jState,jState) = LH_fus*iden_water * ( -mLayerdTheta_dTk(jLayer)*cj - mLayerTempPrime(jLayer)*mLayerd2Theta_dTk2(jLayer) + mLayerdTheta_dTk(jLayer)/dt ) + aJac(jState,jState) - endif - else ! no ice currently - if(mLayerdTheta_dTkPrev(jLayer) > verySmall)then ! ice was present on previous time step - aJac(jState,jState) = LH_fus*iden_water * ( mLayerdTheta_dTkPrev(jLayer)*cj - mLayerTempPrime(jLayer)*mLayerdTheta_dTkPrev(jLayer)/dt ) + aJac(jState,jState) - endif - endif +! if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present +! if(mLayerdTheta_dTkPrev(jLayer) <= verySmall)then ! no ice on previous time step +! aJac(jState,jState) = LH_fus*iden_water * ( -mLayerdTheta_dTk(jLayer)*cj - mLayerTempPrime(jLayer)*mLayerd2Theta_dTk2(jLayer) + mLayerdTheta_dTk(jLayer)/dt ) + aJac(jState,jState) +! endif +! else ! no ice currently +! if(mLayerdTheta_dTkPrev(jLayer) > verySmall)then ! ice was present on previous time step +! aJac(jState,jState) = LH_fus*iden_water * ( mLayerdTheta_dTkPrev(jLayer)*cj - mLayerTempPrime(jLayer)*mLayerdTheta_dTkPrev(jLayer)/dt ) + aJac(jState,jState) +! endif +! endif end do ! (looping through energy states in the soil domain) endif ! (if the subset includes energy state variables in the soil domain) @@ -690,13 +690,13 @@ subroutine computJacDAE(& if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(nrgState,watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) * cj & - LH_fus*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_d2Psi0(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content - if(mLayerdTheta_dTkPrev(jLayer) <= verySmall)then ! no ice on previous time step - aJac(nrgState,watState) = LH_fus*iden_water * ( dVolTot_dPsi0(iLayer)*cj + mLayerMatricHeadPrime(iLayer)*d2VolTot_d2Psi0(iLayer) - dVolTot_dPsi0(iLayer)/dt ) + aJac(nrgState,watState) - endif - else ! no ice currently - if(mLayerdTheta_dTkPrev(jLayer) > verySmall)then ! ice was present on previous time step - aJac(nrgState,watState) = LH_fus*iden_water * ( -dVolTot_dPsi0(iLayer)*cj - mLayerMatricHeadPrime(iLayer)*d2VolTot_d2Psi0(iLayer) + dVolTot_dPsi0(iLayer)/dt ) + aJac(nrgState,watState) - endif +! if(mLayerdTheta_dTkPrev(jLayer) <= verySmall)then ! no ice on previous time step +! aJac(nrgState,watState) = LH_fus*iden_water * ( dVolTot_dPsi0(iLayer)*cj + mLayerMatricHeadPrime(iLayer)*d2VolTot_d2Psi0(iLayer) - dVolTot_dPsi0(iLayer)/dt ) + aJac(nrgState,watState) +! endif +! else ! no ice currently +! if(mLayerdTheta_dTkPrev(jLayer) > verySmall)then ! ice was present on previous time step +! aJac(nrgState,watState) = LH_fus*iden_water * ( -dVolTot_dPsi0(iLayer)*cj - mLayerMatricHeadPrime(iLayer)*d2VolTot_d2Psi0(iLayer) + dVolTot_dPsi0(iLayer)/dt ) + aJac(nrgState,watState) +! endif endif ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index 0a9dbad7e..e4cff2596 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -321,28 +321,28 @@ subroutine eval8DAE(& ! check that the canopy air space temperature is reasonable if(ixCasNrg/=integerMissing)then if(stateVec(ixCasNrg) > canopyTempMax) feasible=.false. - if(stateVec(ixCasNrg) > canopyTempMax) message=trim(message)//'canopy air space temperature too high ' + if(stateVec(ixCasNrg) > canopyTempMax) message=trim(message)//'canopy air space temp high,' if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( stateVec(ixCasNrg) )', feasible, canopyTempMax, stateVec(ixCasNrg) endif ! check that the canopy temperature is reasonable if(ixVegNrg/=integerMissing)then if(stateVec(ixVegNrg) > canopyTempMax) feasible=.false. - if(stateVec(ixVegNrg) > canopyTempMax) message=trim(message)//'canopy temperature too high ' + if(stateVec(ixVegNrg) > canopyTempMax) message=trim(message)//'canopy temp high,' if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( stateVec(ixVegNrg) )', feasible, canopyTempMax, stateVec(ixVegNrg) endif ! check canopy liquid water is not negative if(ixVegHyd/=integerMissing)then if(stateVec(ixVegHyd) < 0._rkind) feasible=.false. - if(stateVec(ixVegHyd) < 0._rkind) message=trim(message)//'canopy water is negative ' + if(stateVec(ixVegHyd) < 0._rkind) message=trim(message)//'canopy water negative,' if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVec( stateVec(ixVegHyd) )', feasible, 0._rkind, stateVec(ixVegHyd) end if ! check snow temperature is below freezing if(count(ixSnowOnlyNrg/=integerMissing)>0)then if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. - if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) message=trim(message)//'snow temperature is above freezing ' + if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) message=trim(message)//'snow temp above freezing,' do iLayer=1,nSnow if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVec( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVec( ixSnowOnlyNrg(iLayer) ) enddo @@ -369,7 +369,7 @@ subroutine eval8DAE(& ! --> check if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. - if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) message=trim(message)//'layer water is outside of bounds ' + if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) message=trim(message)//'layer water outside bounds,' if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax endif ! if water states @@ -380,7 +380,7 @@ subroutine eval8DAE(& if(.not.feasible)then fluxVec(:) = realMissing resVec(:) = quadMissing - message=trim(message)//'solution is non-feasible' + message=trim(message)//'non-feasible' err=20; return end if diff --git a/build/source/engine/updatStateSundials.f90 b/build/source/engine/updatStateSundials.f90 index baa9126a9..8aa6c8091 100644 --- a/build/source/engine/updatStateSundials.f90 +++ b/build/source/engine/updatStateSundials.f90 @@ -227,8 +227,8 @@ subroutine updateSoilSundials(& ! using mLayerVolFracLiqPrev = mLayerVolFracWatPrev mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracWatPrev)*dt_inv ! = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv endif - if ( mLayerTemp - mLayerTempPrime*dt >= TcSoilPrev )print*,"froze" - if ( mLayerTempPrev >= TcSoilPrev )print*,"FROZE2" + !if ( mLayerTemp - mLayerTempPrime*dt >= TcSoilPrev )print*,"froze" + !if ( mLayerTempPrev >= TcSoilPrev )print*,"FROZE2" ! *** compute volumetric fraction of liquid water for unfrozen soil else !( mLayerTemp >= TcSoil, all water is unfrozen ) @@ -244,8 +244,8 @@ subroutine updateSoilSundials(& mLayerVolFracLiqPrev = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv endif - if ( mLayerTemp - mLayerTempPrime*dt < TcSoilPrev )print*,"thawed" - if ( mLayerTempPrev < TcSoilPrev )print*,"THAWED2" + !if ( mLayerTemp - mLayerTempPrime*dt < TcSoilPrev )print*,"thawed" + !if ( mLayerTempPrev < TcSoilPrev )print*,"THAWED2" end if ! (check if soil is partially frozen) From a2aa3e0932e1b3bc5543ca4c39774f0c24ce0197 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 25 May 2022 17:44:28 +0900 Subject: [PATCH 0296/1472] Removed crossing over the soil frozen threshold and the checkSnow and checkFeas inside the sundials loop. This should only happen outside the loop I think. Need to use bounds to keep feasible but might need to alter sundials code to do for nonzero inequalities. Also should remove the checkSnow loop completely if it is actually wrong there. --- build/source/dshare/get_ixname.f90 | 1 - build/source/dshare/popMetadat.f90 | 1 - build/source/dshare/var_lookup.f90 | 3 +- build/source/engine/computJacDAE.f90 | 40 +--- build/source/engine/eval8DAE.f90 | 130 ++++++------ build/source/engine/evalDAE4IDA.f90 | 54 +++-- build/source/engine/evalJac4IDA.f90 | 6 +- build/source/engine/solveByIDA.f90 | 223 ++++++++++----------- build/source/engine/systemSolvSundials.f90 | 75 ++++--- build/source/engine/updatStateSundials.f90 | 34 +--- build/source/engine/updateVars4JacDAE.f90 | 30 +-- 11 files changed, 260 insertions(+), 337 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 3405c4fe1..fca7e975a 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -776,7 +776,6 @@ function get_ixderiv(varName) ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature case('dFracLiqSnow_dTk' ); get_ixderiv = iLookDERIV%dFracLiqSnow_dTk ! derivative in fraction of liquid snow w.r.t. temperature case('mLayerdTheta_dTk' ); get_ixderiv = iLookDERIV%mLayerdTheta_dTk ! derivative of volumetric liquid water content w.r.t. temperature (K-1) - case('mLayerdTheta_dTkPrev' ); get_ixderiv = iLookDERIV%mLayerdTheta_dTkPrev ! previous timestep derivative of volumetric liquid water content w.r.t. temperature (K-1) case('mLayerd2Theta_dTk2' ); get_ixderiv = iLookDERIV%mLayerd2Theta_dTk2 ! second derivative of volumetric liquid water content w.r.t. temperature ! derivate in bulk heat capacity w.r.t. relevant state variables case('dVolHtCapBulk_dPsi0' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dPsi0 ! derivative in bulk heat capacity w.r.t. matric potential diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index ccbbcdfea..0b9562d06 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -610,7 +610,6 @@ subroutine popMetadat(err,message) ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature deriv_meta(iLookDERIV%dFracLiqSnow_dTk) = var_info('dFracLiqSnow_dTk' , 'derivative in fraction of liquid snow w.r.t. temperature' , 'K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%mLayerdTheta_dTk) = var_info('mLayerdTheta_dTk' , 'derivative of volumetric liquid water content w.r.t. temperature' , 'K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%mLayerdTheta_dTkPrev) = var_info('mLayerdTheta_dTkPrev' , 'previous timestep derivative of volumetric liquid water content w.r.t. temperature','K-1', get_ixVarType('midToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%mLayerd2Theta_dTk2) = var_info('mLayerd2Theta_dTk2' , 'second derivative of volumetric liquid water content w.r.t. temperature', 'K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) ! derivate in bulk heat capacity w.r.t. relevant state variables deriv_meta(iLookDERIV%dVolHtCapBulk_dPsi0) = var_info('dVolHtCapBulk_dPsi0' , 'derivative in bulk heat capacity w.r.t. matric potential' , 'J m-4 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index b3d3ffa3b..482433254 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -629,7 +629,6 @@ MODULE var_lookup ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature integer(i4b) :: dFracLiqSnow_dTk = integerMissing ! derivative in fraction of liquid snow w.r.t. temperature integer(i4b) :: mLayerdTheta_dTk = integerMissing ! derivative of volumetric liquid water content w.r.t. temperature (K-1) - integer(i4b) :: mLayerdTheta_dTkPrev = integerMissing ! previous timestep derivative of volumetric liquid water content w.r.t. temperature (K-1) integer(i4b) :: mLayerd2Theta_dTk2 = integerMissing ! second derivative of volumetric liquid water content w.r.t. temperature ! derivate in bulk heat capacity w.r.t. relevant state variables integer(i4b) :: dVolHtCapBulk_dPsi0 = integerMissing ! derivative in bulk heat capacity w.r.t. matric potential @@ -880,7 +879,7 @@ MODULE var_lookup 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,& 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,& 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,& - 61, 62, 63, 64, 65, 66) + 61, 62, 63, 64, 65) ! named variables: model indices type(iLook_index), public,parameter :: iLookINDEX =ilook_index ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 3bb59ff30..49292031c 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -276,7 +276,6 @@ subroutine computJacDAE(& ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature dFracLiqSnow_dTk => deriv_data%var(iLookDERIV%dFracLiqSnow_dTk )%dat ,& ! intent(in): [dp(:)] derivative in fraction of liquid snow w.r.t. temperature mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk )%dat ,& ! intent(in): [dp(:)] derivative in volumetric liquid water content w.r.t. temperature - mLayerdTheta_dTkPrev => deriv_data%var(iLookDERIV%mLayerdTheta_dTkPrev )%dat ,& ! intent(in): [dp(:)] previous timestep derivative in volumetric liquid water content w.r.t. temperature mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2 )%dat ,& ! intent(in): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature ! derivate in bulk heat capacity w.r.t. relevant state variables dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential @@ -452,36 +451,6 @@ subroutine computJacDAE(& end do ! (looping through energy states in the snow+soil domain) endif ! (if the subset includes energy state variables in the snow+soil domain) - - ! ----- - ! * energy fluxes in the soil domain if changed frozen status... - ! ---------------------------------------- - if(nSoilOnlyNrg>0)then - do iLayer=1,nSoilOnlyNrg - - ! - check that the soil layer is desired - if(ixSoilOnlyNrg(iLayer)==integerMissing) cycle - - ! - define indices of the soil layers - jLayer = iLayer+nSnow ! index of layer in the snow+soil vector - - ! - define the energy state variable - jState = ixSoilOnlyNrg(iLayer) ! index within the full state vector - - ! - consider if switched frozen status from previous state -! if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present -! if(mLayerdTheta_dTkPrev(jLayer) <= verySmall)then ! no ice on previous time step -! aJac(jState,jState) = LH_fus*iden_water * ( -mLayerdTheta_dTk(jLayer)*cj - mLayerTempPrime(jLayer)*mLayerd2Theta_dTk2(jLayer) + mLayerdTheta_dTk(jLayer)/dt ) + aJac(jState,jState) -! endif -! else ! no ice currently -! if(mLayerdTheta_dTkPrev(jLayer) > verySmall)then ! ice was present on previous time step -! aJac(jState,jState) = LH_fus*iden_water * ( mLayerdTheta_dTkPrev(jLayer)*cj - mLayerTempPrime(jLayer)*mLayerdTheta_dTkPrev(jLayer)/dt ) + aJac(jState,jState) -! endif -! endif - - end do ! (looping through energy states in the soil domain) - endif ! (if the subset includes energy state variables in the soil domain) - ! ----- ! * liquid water fluxes for the snow domain... ! -------------------------------------------- @@ -684,19 +653,12 @@ subroutine computJacDAE(& aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTGround(iLayer)) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) endif - ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer, need to consider if switched frozen status from previous state + ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) & + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(nrgState,watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) * cj & - LH_fus*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_d2Psi0(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content -! if(mLayerdTheta_dTkPrev(jLayer) <= verySmall)then ! no ice on previous time step -! aJac(nrgState,watState) = LH_fus*iden_water * ( dVolTot_dPsi0(iLayer)*cj + mLayerMatricHeadPrime(iLayer)*d2VolTot_d2Psi0(iLayer) - dVolTot_dPsi0(iLayer)/dt ) + aJac(nrgState,watState) -! endif -! else ! no ice currently -! if(mLayerdTheta_dTkPrev(jLayer) > verySmall)then ! ice was present on previous time step -! aJac(nrgState,watState) = LH_fus*iden_water * ( -dVolTot_dPsi0(iLayer)*cj - mLayerMatricHeadPrime(iLayer)*d2VolTot_d2Psi0(iLayer) + dVolTot_dPsi0(iLayer)/dt ) + aJac(nrgState,watState) -! endif endif ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index e4cff2596..754e89b6d 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -96,6 +96,7 @@ subroutine eval8DAE(& nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers nState, & ! intent(in): total number of state variables + checkFeas, & ! intent(in): flag to indicate if we are checking for feasibility firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(inout) flag to indicate if we are processing the first flux call firstSplitOper, & ! intent(inout) flag to indicate if we are processing the first flux call in a splitting operation @@ -176,6 +177,7 @@ subroutine eval8DAE(& integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers integer,intent(in) :: nState ! total number of state variables + logical(lgt),intent(in) :: checkFeas ! flag to indicate if we are checking for feasibility logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(inout) :: firstFluxCall logical(lgt),intent(inout) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation @@ -314,75 +316,77 @@ subroutine eval8DAE(& ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="eval8DAE/" - - ! check the feasibility of the solution feasible=.true. - ! check that the canopy air space temperature is reasonable - if(ixCasNrg/=integerMissing)then - if(stateVec(ixCasNrg) > canopyTempMax) feasible=.false. - if(stateVec(ixCasNrg) > canopyTempMax) message=trim(message)//'canopy air space temp high,' - if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( stateVec(ixCasNrg) )', feasible, canopyTempMax, stateVec(ixCasNrg) - endif + ! check the feasibility of the solution + if (checkFeas) then + ! check that the canopy air space temperature is reasonable + if(ixCasNrg/=integerMissing)then + if(stateVec(ixCasNrg) > canopyTempMax) feasible=.false. + if(stateVec(ixCasNrg) > canopyTempMax) message=trim(message)//'canopy air space temp high,' + if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixCasNrg )', feasible, canopyTempMax, stateVec(ixCasNrg) + endif - ! check that the canopy temperature is reasonable - if(ixVegNrg/=integerMissing)then - if(stateVec(ixVegNrg) > canopyTempMax) feasible=.false. - if(stateVec(ixVegNrg) > canopyTempMax) message=trim(message)//'canopy temp high,' - if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( stateVec(ixVegNrg) )', feasible, canopyTempMax, stateVec(ixVegNrg) - endif + ! check that the canopy temperature is reasonable + if(ixVegNrg/=integerMissing)then + if(stateVec(ixVegNrg) > canopyTempMax) feasible=.false. + if(stateVec(ixVegNrg) > canopyTempMax) message=trim(message)//'canopy temp high,' + if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixVegNrg )', feasible, canopyTempMax, stateVec(ixVegNrg) + endif - ! check canopy liquid water is not negative - if(ixVegHyd/=integerMissing)then - if(stateVec(ixVegHyd) < 0._rkind) feasible=.false. - if(stateVec(ixVegHyd) < 0._rkind) message=trim(message)//'canopy water negative,' - if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVec( stateVec(ixVegHyd) )', feasible, 0._rkind, stateVec(ixVegHyd) - end if - - ! check snow temperature is below freezing - if(count(ixSnowOnlyNrg/=integerMissing)>0)then - if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. - if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) message=trim(message)//'snow temp above freezing,' - do iLayer=1,nSnow - if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVec( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVec( ixSnowOnlyNrg(iLayer) ) - enddo - endif + ! check canopy liquid water is not negative + if(ixVegHyd/=integerMissing)then + if(stateVec(ixVegHyd) < 0._rkind) feasible=.false. + if(stateVec(ixVegHyd) < 0._rkind) message=trim(message)//'canopy water negative,' + if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVec( ixVegHyd )', feasible, 0._rkind, stateVec(ixVegHyd) + end if + + ! check snow temperature is below freezing + if(count(ixSnowOnlyNrg/=integerMissing)>0)then + if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. + if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) message=trim(message)//'snow temp above freezing,' + do iLayer=1,nSnow + if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVec( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVec( ixSnowOnlyNrg(iLayer) ) + enddo + endif ! loop through non-missing hydrology state variables in the snow+soil domain - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) - - ! check the minimum and maximum water constraints - if(ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_liqLayer)then - - ! --> minimum - if (layerType(iLayer) == iname_soil) then - xMin = theta_res(iLayer-nSnow) - else - xMin = 0._rkind - endif - - ! --> maximum - select case( layerType(iLayer) ) - case(iname_snow); xMax = merge(iden_ice, 1._rkind - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) - case(iname_soil); xMax = merge(theta_sat(iLayer-nSnow), theta_sat(iLayer-nSnow) - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) - end select - - ! --> check - if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. - if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) message=trim(message)//'layer water outside bounds,' - if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax - - endif ! if water states - - end do ! loop through non-missing hydrology state variables in the snow+soil domain - - ! early return for non-feasible solutions - if(.not.feasible)then - fluxVec(:) = realMissing - resVec(:) = quadMissing - message=trim(message)//'non-feasible' - err=20; return - end if + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) + + ! check the minimum and maximum water constraints + if(ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_liqLayer)then + + ! --> minimum + if (layerType(iLayer) == iname_soil) then + xMin = theta_res(iLayer-nSnow) + else + xMin = 0._rkind + endif + + ! --> maximum + select case( layerType(iLayer) ) + case(iname_snow); xMax = merge(iden_ice, 1._rkind - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) + case(iname_soil); xMax = merge(theta_sat(iLayer-nSnow), theta_sat(iLayer-nSnow) - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) + end select + + ! --> check + if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. + if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) message=trim(message)//'layer water outside bounds,' + if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax + + endif ! if water states + + end do ! loop through non-missing hydrology state variables in the snow+soil domain + + ! early return for non-feasible solutions + if(.not.feasible)then + fluxVec(:) = realMissing + resVec(:) = quadMissing + message=trim(message)//'non-feasible' + err=20; return + end if + + end if ! ( feasibility check ) ! get the start and end indices for the soil compression calculations if(scalarSolution)then diff --git a/build/source/engine/evalDAE4IDA.f90 b/build/source/engine/evalDAE4IDA.f90 index 35ffc0a62..e02b57f32 100644 --- a/build/source/engine/evalDAE4IDA.f90 +++ b/build/source/engine/evalDAE4IDA.f90 @@ -17,11 +17,11 @@ module evalDAE4IDA_module var_dlength, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions USE multiconst,only:iden_water ! intrinsic density of liquid water (kg m-3) - USE var_lookup,only:iLookDIAG - USE var_lookup,only:iLookPROG + USE var_lookup,only:iLookDIAG + USE var_lookup,only:iLookPROG USE var_lookup,only:iLookINDEX ! named variables for structure elements USE var_lookup,only:iLookDERIV ! named variables for structure elements - + ! privacy implicit none @@ -44,7 +44,7 @@ integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_da !======= Inclusions =========== use, intrinsic :: iso_c_binding - use fida_mod + use fida_mod use fsundials_nvector_mod use fnvector_serial_mod use nrtype @@ -59,7 +59,7 @@ integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_da type(N_Vector) :: sunvec_y ! solution N_Vector y type(N_Vector) :: sunvec_yp ! derivative N_Vector y' type(N_Vector) :: sunvec_r ! residual N_Vector F(t,y,y') - type(c_ptr), value :: user_data ! user-defined data + type(c_ptr), value :: user_data ! user-defined data ! pointers to data in SUNDIALS vectors @@ -67,33 +67,24 @@ integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_da real(rkind), pointer :: stateVec(:) real(rkind), pointer :: stateVecPrime(:) real(rkind), pointer :: rVec(:) - logical(lgt) :: feasible - integer(i4b) :: retval + logical(lgt) :: feasible + integer(i4b) :: retval real(c_double) :: stepsize_next(1) - - - - - - !======= Internals ============ - + ! get equations data from user-defined data call c_f_pointer(user_data, eqns_data) - - ! get data arrays from SUNDIALS vectors - stateVec => FN_VGetArrayPointer(sunvec_y) - stateVecPrime => FN_VGetArrayPointer(sunvec_yp) - rVec => FN_VGetArrayPointer(sunvec_r) - + stateVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_y) + stateVecPrime(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_yp) + rVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_r) + retval = FIDAGetCurrentStep(eqns_data%ida_mem, stepsize_next) if (retval /= 0) then print *, 'Error in FIDAGetCurrentStep, retval = ', retval, '; halting' stop 1 - end if - + end if ! compute the flux and the residual vector for a given state vector call eval8DAE(& @@ -104,6 +95,7 @@ integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_da eqns_data%nSoil, & ! intent(in): number of soil layers eqns_data%nLayers, & ! intent(in): number of layers eqns_data%nState, & ! intent(in): number of state variables in the current subset + .false., & ! intent(in): do not check for feasibility inside Sundials loop eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation @@ -134,7 +126,7 @@ integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_da eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) eqns_data%scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) eqns_data%scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - eqns_data%scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) + eqns_data%scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) eqns_data%scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): value for enthalpy of the vegetation canopy (J m-3) eqns_data%mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) @@ -149,21 +141,21 @@ integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_da eqns_data%mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) eqns_data%mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) eqns_data%scalarAquiferStorageTrial, & ! intent(out): trial value of storage of water in the aquifer (m) - eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) + eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) - eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) - eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer + eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) + eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer ! output feasible, & ! intent(out): flag to denote the feasibility of the solution eqns_data%fluxVec, & ! intent(out): flux vector eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation rVec, & ! intent(out): residual vector eqns_data%err,eqns_data%message) ! intent(out): error control - - if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif - if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif - if(.not.feasible)then; eqns_data%message=trim(eqns_data%message)//'state vector not feasible'; ierr = 1; return; endif - + + if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif + if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif + if(.not.feasible)then; eqns_data%message=trim(eqns_data%message)//'state vector not feasible'; ierr = 1; return; endif + ! return success ierr = 0 return diff --git a/build/source/engine/evalJac4IDA.f90 b/build/source/engine/evalJac4IDA.f90 index f2c4ca256..ca8703109 100644 --- a/build/source/engine/evalJac4IDA.f90 +++ b/build/source/engine/evalJac4IDA.f90 @@ -78,9 +78,9 @@ integer(c_int) function evalJac4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & ! get data arrays from SUNDIALS vectors - stateVec => FN_VGetArrayPointer(sunvec_y) - stateVecPrime => FN_VGetArrayPointer(sunvec_yp) - rVec => FN_VGetArrayPointer(sunvec_r) + stateVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_y) + stateVecPrime(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_yp) + rVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_r) Jac(1:eqns_data%nState, 1:eqns_data%nState) => FSUNDenseMatrix_Data(sunmat_J) ! compute Jacobian matrix diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 306e98754..4dccef274 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -105,6 +105,7 @@ subroutine solveByIDA( & scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVecInit, & ! intent(in): initial state vector + stateVecConstraints, & ! intent(inout): model state vector constraints sMul, & ! intent(inout): state vector multiplier (USEd in the residual calculations) dMat, & ! intent(inout): diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures @@ -123,10 +124,10 @@ subroutine solveByIDA( & flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output - ixSaturation, & ! intent(out) - idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step + ixSaturation, & ! intent(out) + idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix - dt_out, & ! intent(out): time step + dt_out, & ! intent(out): time step stateVec, & ! intent(out): model state vector stateVecPrime, & ! intent(out): derivative of model state vector err,message & ! intent(out): error control @@ -176,7 +177,8 @@ subroutine solveByIDA( & logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors real(rkind),intent(in) :: stateVecInit(:) ! model state vector - real(qp),intent(in) :: sMul(:) ! state vector multiplier (used in the residual calculations) + real(rkind),intent(inout) :: stateVecConstraints(:) ! model state vector constraints + real(qp),intent(in) :: sMul(:) ! state vector multiplier (used in the residual calculations) real(rkind), intent(inout) :: dMat(:) ! input: data structures type(zLookup),intent(in) :: lookup_data ! lookup tables @@ -195,11 +197,11 @@ subroutine solveByIDA( & type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables real(rkind),intent(inout) :: mLayerCmpress_sum(:) ! sum of soil compress ! output: state vectors - integer(i4b),intent(out) :: ixSaturation ! index of the lowest saturated layer + integer(i4b),intent(out) :: ixSaturation ! index of the lowest saturated layer real(rkind),intent(inout) :: stateVec(:) ! model state vector (y) real(rkind),intent(inout) :: stateVecPrime(:) ! model state vector (y') - logical(lgt),intent(out) :: idaSucceeds ! flag to indicate if IDA is successful - real(qp),intent(out) :: dt_out ! time step + logical(lgt),intent(out) :: idaSucceeds ! flag to indicate if IDA is successful + real(qp),intent(out) :: dt_out ! time step ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -209,6 +211,7 @@ subroutine solveByIDA( & ! -------------------------------------------------------------------------------------------------------------------------------- type(N_Vector), pointer :: sunvec_y ! sundials solution vector type(N_Vector), pointer :: sunvec_yp ! sundials derivative vector + type(N_Vector), pointer :: sunvec_c ! sundials constraints vector type(N_Vector), pointer :: sunvec_av ! sundials tolerance vector type(SUNMatrix), pointer :: sunmat_A ! sundials matrix type(SUNLinearSolver), pointer :: sunlinsol_LS ! sundials linear solver @@ -219,28 +222,28 @@ subroutine solveByIDA( & logical(lgt) :: feasible ! feasibility flag real(qp) :: t0 ! staring time real(qp) :: dt_last(1) ! last time step - integer(kind = 8) :: mu, lu ! in banded matrix mode - integer(i4b) :: iVar - logical(lgt) :: startQuadrature - real(rkind) :: mLayerMatricHeadLiqPrev(nSoil) + integer(kind = 8) :: mu, lu ! in banded matrix mode + integer(i4b) :: iVar + logical(lgt) :: startQuadrature + real(rkind) :: mLayerMatricHeadLiqPrev(nSoil) real(qp) :: h_init integer(c_long) :: nState ! total number of state variables real(rkind) :: rVec(nStat) real(qp) :: tret(1) real(rkind) :: bulkDensity ! bulk density of a given layer (kg m-3) real(rkind) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) - logical(lgt) :: tooMuchMelt - logical(lgt) :: divideLayer - logical(lgt) :: mergedLayers - logical(lgt),parameter :: checkSnow = .true. + logical(lgt) :: tooMuchMelt + logical(lgt) :: divideLayer + logical(lgt) :: mergedLayers + logical(lgt),parameter :: checkSnow = .false. !.true. logical(lgt),parameter :: offErrWarnMessage = .false. real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) - real(rkind) :: mLayerDepth(nLayers) - real(rkind) :: scalarSnowDepth - real(rkind) :: scalarSWE - real(rkind) :: mLayerMeltFreeze(nLayers) - integer(i4b) :: i + real(rkind) :: mLayerDepth(nLayers) + real(rkind) :: scalarSnowDepth + real(rkind) :: scalarSWE + real(rkind) :: mLayerMeltFreeze(nLayers) + integer(i4b) :: i ! ----------------------------------------------------------------------------------------------------- ! initialize error control @@ -337,19 +340,26 @@ subroutine solveByIDA( & sunvec_yp => FN_VMake_Serial(nState, stateVecPrime) if (.not. associated(sunvec_yp)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif + sunvec_c => FN_VMake_Serial(nState, stateVecConstraints) + if (.not. associated(sunvec_c)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif + ! Initialize solution vectors call setInitialCondition(nState, stateVecInit, sunvec_y, sunvec_yp) - ! Call FIDACreate and FIDAInit to initialize IDA memory + ! Create memory ida_mem = FIDACreate() if (.not. c_associated(ida_mem)) then; err=20; message='solveByIDA: ida_mem = NULL'; return; endif - eqns_data%ida_mem = ida_mem - + ! Set constraints + retval = FIDASetConstraints(ida_mem, sunvec_c) + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetConstraints'; return; endif + ! Attach user data to memory + eqns_data%ida_mem = ida_mem retval = FIDASetUserData(ida_mem, c_loc(eqns_data)) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetUserData'; return; endif + ! Initialize memory t0 = 0._rkind retval = FIDAInit(ida_mem, c_funloc(evalDAE4IDA), t0, sunvec_y, sunvec_yp) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAInit'; return; endif @@ -358,19 +368,19 @@ subroutine solveByIDA( & retval = FIDAWFtolerances(ida_mem, c_funloc(computWeight4IDA)) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAWFtolerances'; return; endif - ! define the form of the matrix - select case(ixMatrix) - case(ixBandMatrix) - mu = ku; lu = kl; - ! Create banded SUNMatrix for use in linear solves - sunmat_A => FSUNBandMatrix(nState, mu, lu) - if (.not. associated(sunmat_A)) then; err=20; message='solveByIDA: sunmat = NULL'; return; endif + ! define the form of the matrix + select case(ixMatrix) + case(ixBandMatrix) + mu = ku; lu = kl; + ! Create banded SUNMatrix for use in linear solves + sunmat_A => FSUNBandMatrix(nState, mu, lu) + if (.not. associated(sunmat_A)) then; err=20; message='solveByIDA: sunmat = NULL'; return; endif ! Create banded SUNLinearSolver object sunlinsol_LS => FSUNLinSol_Band(sunvec_y, sunmat_A) if (.not. associated(sunlinsol_LS)) then; err=20; message='solveByIDA: sunlinsol = NULL'; return; endif - case(ixFullMatrix) + case(ixFullMatrix) ! Create dense SUNMatrix for use in linear solves sunmat_A => FSUNDenseMatrix(nState, nState) if (.not. associated(sunmat_A)) then; err=20; message='solveByIDA: sunmat = NULL'; return; endif @@ -380,9 +390,9 @@ subroutine solveByIDA( & if (.not. associated(sunlinsol_LS)) then; err=20; message='solveByIDA: sunlinsol = NULL'; return; endif ! check - case default; err=20; message='solveByIDA: error in type of matrix'; return + case default; err=20; message='solveByIDA: error in type of matrix'; return - end select ! form of matrix + end select ! form of matrix ! Attach the matrix and linear solver retval = FIDASetLinearSolver(ida_mem, sunlinsol_LS, sunmat_A); @@ -417,7 +427,7 @@ subroutine solveByIDA( & endif ! need the following values for the first substep - eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) + eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) eqns_data%scalarCanopyIcePrev = prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) eqns_data%scalarCanopyLiqPrev = prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) @@ -449,7 +459,6 @@ subroutine solveByIDA( & exit endif - ! get the last stepsize retval = FIDAGetLastStep(ida_mem, dt_last) @@ -462,6 +471,7 @@ subroutine solveByIDA( & eqns_data%nSoil, & ! intent(in): number of soil layers eqns_data%nLayers, & ! intent(in): number of layers eqns_data%nState, & ! intent(in): number of state variables in the current subset + .true., & ! intent(in): check for feasibility once outside Sundials loop eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation @@ -528,103 +538,94 @@ subroutine solveByIDA( & ! flux_data%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / tret(1) ! end do - ! sum of mLayerCmpress - mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & + ! sum of mLayerCmpress + mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) - - ! save required quantities for next step - eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial - eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial - eqns_data%scalarCanopyLiqPrev = eqns_data%scalarCanopyLiqTrial - eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) - mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) - eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) - eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) - eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) - eqns_data%mLayerVolFracLiqPrev(:) = eqns_data%mLayerVolFracLiqTrial(:) - eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial - eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) - eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial - - - if(checkSnow)then - mLayerDepth(:) = prog_data%var(iLookPROG%mLayerDepth)%dat(:) - ! compute the melt in each snow and soil layer - if(nSnow>0)then - mLayerMeltFreeze(1:nSnow) = -( eqns_data%mLayerVolFracIceTrial(1:nSnow) - prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow) ) * iden_ice - mLayerMeltFreeze(nSnow+1:nLayers) = -(eqns_data%mLayerVolFracIceTrial(nSnow+1:nLayers) - prog_data%var(iLookPROG%mLayerVolFracIce)%dat(nSnow+1:nLayers))*iden_water - endif - - call computSnowDepth(& - tret(1), & ! intent(in) - eqns_data%nSnow, & ! intent(in) + ! save required quantities for next step + eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial + eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial + eqns_data%scalarCanopyLiqPrev = eqns_data%scalarCanopyLiqTrial + eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) + mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) + eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) + eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) + eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) + eqns_data%mLayerVolFracLiqPrev(:) = eqns_data%mLayerVolFracLiqTrial(:) + eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial + eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) + eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial + + if(checkSnow)then + mLayerDepth(:) = prog_data%var(iLookPROG%mLayerDepth)%dat(:) + ! compute the melt in each snow and soil layer + if(nSnow>0)then + mLayerMeltFreeze(1:nSnow) = -( eqns_data%mLayerVolFracIceTrial(1:nSnow) - prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow) ) * iden_ice + mLayerMeltFreeze(nSnow+1:nLayers) = -(eqns_data%mLayerVolFracIceTrial(nSnow+1:nLayers) - prog_data%var(iLookPROG%mLayerVolFracIce)%dat(nSnow+1:nLayers))*iden_water + endif + + call computSnowDepth(& + tret(1), & ! intent(in) + eqns_data%nSnow, & ! intent(in) flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! intent(in) - eqns_data%mLayerVolFracLiqTrial, & ! intent(inout) - eqns_data%mLayerVolFracIceTrial, & ! intent(inout) - eqns_data%mLayerTempTrial, & ! intent(in) - mLayerMeltFreeze, & ! intent(in) - eqns_data%mpar_data, & ! intent(in) - ! output - mLayerDepth, & ! intent(inout) - ! error control - err,message) ! intent(out): error control + eqns_data%mLayerVolFracLiqTrial, & ! intent(inout) + eqns_data%mLayerVolFracIceTrial, & ! intent(inout) + eqns_data%mLayerTempTrial, & ! intent(in) + mLayerMeltFreeze, & ! intent(in) + eqns_data%mpar_data, & ! intent(in) + ! output + mLayerDepth, & ! intent(inout) + ! error control + err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if + ! recompute snow depth and SWE + if(eqns_data%nSnow > 0)then + scalarSnowDepth = sum( mLayerDepth(1:nSnow) ) + scalarSWE = sum( (eqns_data%mLayerVolFracLiqTrial(1:nSnow)*iden_water + eqns_data%mLayerVolFracIceTrial(1:nSnow)*iden_ice) * mLayerDepth(1:nSnow) ) + end if - ! recompute snow depth and SWE - if(eqns_data%nSnow > 0)then - scalarSnowDepth = sum( mLayerDepth(1:nSnow) ) - scalarSWE = sum( (eqns_data%mLayerVolFracLiqTrial(1:nSnow)*iden_water + eqns_data%mLayerVolFracIceTrial(1:nSnow)*iden_ice) * mLayerDepth(1:nSnow) ) - end if - - ! check the need to merge snow layers - tooMuchMelt = .false. - if(eqns_data%nSnow>0)then + ! check the need to merge snow layers + tooMuchMelt = .false. + if(eqns_data%nSnow>0)then ! check that we did not remove the entire layer if(mLayerDepth(1) < 1.e-6_rkind) exit ! compute the energy required to melt the top snow layer (J m-2) bulkDensity = eqns_data%mLayerVolFracIceTrial(1)*iden_ice + eqns_data%mLayerVolFracLiqTrial(1)*iden_water volEnthalpy = temp2ethpy(eqns_data%mLayerTempTrial(1),bulkDensity,eqns_data%mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1)) if(-volEnthalpy < flux_data%var(iLookFLUX%mLayerNrgFlux)%dat(1)*tret(1)) tooMuchMelt = .true. - endif - + endif - if(tooMuchMelt) exit + if(tooMuchMelt) exit - divideLayer = .false. - call needDivideLayer(& + divideLayer = .false. + call needDivideLayer(& ! input/output: model data structures - model_decisions, & ! intent(in): model decisions + model_decisions, & ! intent(in): model decisions eqns_data%mpar_data, & ! intent(in): model parameters eqns_data%nSnow, & ! intent(in): number of snow layers - mLayerDepth, & ! intent(in): - scalarSnowDepth, & ! intent(in) + mLayerDepth, & ! intent(in): + scalarSnowDepth, & ! intent(in) ! output - divideLayer, & ! intent(out): flag to denote that a layer was divided - err,message) ! intent(out): error control - if(divideLayer .and. tret(1)>50) then - exit - endif - + divideLayer, & ! intent(out): flag to denote that a layer was divided + err,message) ! intent(out): error control + if(divideLayer .and. tret(1)>50) exit mergedLayers = .false. call needMergeLayers(& ! input/output: model data structures - tooMuchMelt, & ! intent(in): flag to force merge of snow layers - model_decisions, & ! intent(in): model decisions + tooMuchMelt, & ! intent(in): flag to force merge of snow layers + model_decisions, & ! intent(in): model decisions eqns_data%mpar_data, & ! intent(in): model parameters eqns_data%nSnow, & ! intent(in): - mLayerDepth, & ! intent(inout): model prognostic variables for a local HRU + mLayerDepth, & ! intent(inout): model prognostic variables for a local HRU ! output - mergedLayers, & ! intent(out): flag to denote that layers were merged - err,message) ! intent(out): error control - + mergedLayers, & ! intent(out): flag to denote that layers were merged + err,message) ! intent(out): error control if(mergedLayers .and. tret(1)>50) exit endif ! checkSnow - end do ! while loop on one_step mode !****************************** End of Main Solver *************************************** @@ -645,12 +646,12 @@ subroutine solveByIDA( & ! free memory - deallocate(eqns_data%sMul) - deallocate(eqns_data%dMat) - deallocate(eqns_data%dBaseflow_dMatric) - deallocate(eqns_data%mLayerMatricHeadLiqTrial) - deallocate(eqns_data%mLayerMatricHeadTrial) - deallocate(eqns_data%mLayerMatricHeadPrev) + deallocate( eqns_data%sMul ) + deallocate( eqns_data%dMat ) + deallocate( eqns_data%dBaseflow_dMatric ) + deallocate( eqns_data%mLayerMatricHeadLiqTrial ) + deallocate( eqns_data%mLayerMatricHeadTrial ) + deallocate( eqns_data%mLayerMatricHeadPrev ) deallocate( eqns_data%fluxVec ) deallocate( eqns_data%resSink ) deallocate( eqns_data%mLayerVolFracWatTrial ) @@ -669,11 +670,10 @@ subroutine solveByIDA( & call FSUNMatDestroy(sunmat_A) call FN_VDestroy(sunvec_y) call FN_VDestroy(sunvec_yp) - + call FN_VDestroy(sunvec_c) end subroutine solveByIDA - ! ---------------------------------------------------------------- ! SetInitialCondition: routine to initialize u and up vectors. ! ---------------------------------------------------------------- @@ -707,7 +707,6 @@ subroutine setInitialCondition(neq, y, sunvec_u, sunvec_up) uu = y up = 0._rkind - end subroutine setInitialCondition ! ---------------------------------------------------------------- diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index f8f589ec6..c65a8d1d4 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -202,14 +202,15 @@ subroutine systemSolvSundials(& real(qp) :: sMul(nState) ! NOTE: qp ! multiplier for state vector for the residual calculations real(qp) :: rVec(nState) ! NOTE: qp ! residual vector real(rkind) :: rAdd(nState) ! additional terms in the residual vector + real(rkind) :: stateVecConstraints(nState) ! model state vector constraints real(rkind) :: fOld ! function values (-); NOTE: dimensionless because scaled logical(lgt) :: feasible ! feasibility flag - real(rkind) :: atol(nState) ! absolute telerance - real(rkind) :: rtol(nState) ! relative tolerance - type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a data step - integer(i4b) :: tol_iter ! iteration index - real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix - logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step + real(rkind) :: atol(nState) ! absolute telerance + real(rkind) :: rtol(nState) ! relative tolerance + type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a data step + integer(i4b) :: tol_iter ! iteration index + real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix + logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step ! --------------------------------------------------------------------------------------- @@ -354,7 +355,7 @@ subroutine systemSolvSundials(& mLayerEnthalpy, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control err,cmessage) ! intent(out): error control -if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! compute the flux and the residual vector for a given state vector ! NOTE 1: The derivatives computed in eval8summa are used to calculate the Jacobian matrix for the first iteration @@ -413,10 +414,8 @@ subroutine systemSolvSundials(& call allocLocal(flux_meta(:),flux_sum,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - ! allocate space for mLayerCmpress_sum - allocate( mLayerCmpress_sum(nSoil) ) - - + ! allocate space for mLayerCmpress_sum + allocate( mLayerCmpress_sum(nSoil) ) ! check the need to merge snow layers if(nSnow>0)then @@ -431,19 +430,41 @@ subroutine systemSolvSundials(& endif endif - ! get tolerance vectors - call popTol4IDA(& - ! input - nState, & ! intent(in): number of desired state variables - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - mpar_data, & ! intent(in): model parameters - ! output - atol, & ! intent(out): absolute tolerances vector (mixed units) - rtol, & ! intent(out): relative tolerances vector (mixed units) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + ! get tolerance vectors + call popTol4IDA(& + ! input + nState, & ! intent(in): number of desired state variables + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + mpar_data, & ! intent(in): model parameters + ! output + atol, & ! intent(out): absolute tolerances vector (mixed units) + rtol, & ! intent(out): relative tolerances vector (mixed units) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + + ! Set constraints for state vector + ! 0 then no constraint is imposed on y. + ! 1 then yi will be constrained to be yi>=0 + !-1 then yi will be constrained to be yi<=0 + stateVecConstraints = 0.d0 + !stateVec(ixCasNrg) - canopyTempMax = -1.d0 !canopy air space temperature cannot be too high + !stateVec(ixVegNrg) - canopyTempMax = -1.d0 !canopy temperature cannot be too high + stateVecConstraints(ixVegHyd) = 1.d0 !canopy liquid water cannot be negative + ! loop through non-missing energy state variables in the snow domain + !do concurrent (iLayer=1:nLayers,ixSnowOnlyNrg(iLayer)/=integerMissing) + ! stateVec(ixSnowOnlyNrg(iLayer)) - Tfreeze = 1.d0 !snow temp cannot be less than Tfreeze + !end do + ! loop through non-missing hydrology state variables in the snow+soil domain + !do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) + ! check the minimum and maximum water constraints + ! stateVec(ixSnowSoilHyd(iLayer)) - xMin = 1.d0 !water cannot be less than xMin + ! stateVec(ixSnowSoilHyd(iLayer)) - xMax = -1.d0 !water cannot be more than xMax + !enddo + + + !------------------- ! * solving F(y,y') = 0 by IDA. Here, y is the state vector @@ -473,6 +494,7 @@ subroutine systemSolvSundials(& scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vector stateVecTrial, & ! intent(in): model state vector at the beginning of the data time step + stateVecConstraints, & ! intent(inout): model state vector constraints sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout) diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures @@ -510,9 +532,6 @@ subroutine systemSolvSundials(& end do ! iteration over tolerances - - - ! check if IDA is successful if( .not.idaSucceeds )then err = 20 @@ -521,7 +540,6 @@ subroutine systemSolvSundials(& return endif - ! compute average flux do iVar=1,size(flux_meta) flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / dt_out @@ -532,7 +550,6 @@ subroutine systemSolvSundials(& ! compute the total change in storage associated with compression of the soil matrix (kg m-2) diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sum(diag_data%var(iLookDIAG%mLayerCompress)%dat(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water - ! save the computed solution stateVecTrial = stateVecNew diff --git a/build/source/engine/updatStateSundials.f90 b/build/source/engine/updatStateSundials.f90 index 8aa6c8091..a8681a854 100644 --- a/build/source/engine/updatStateSundials.f90 +++ b/build/source/engine/updatStateSundials.f90 @@ -227,8 +227,6 @@ subroutine updateSoilSundials(& ! using mLayerVolFracLiqPrev = mLayerVolFracWatPrev mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracWatPrev)*dt_inv ! = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv endif - !if ( mLayerTemp - mLayerTempPrime*dt >= TcSoilPrev )print*,"froze" - !if ( mLayerTempPrev >= TcSoilPrev )print*,"FROZE2" ! *** compute volumetric fraction of liquid water for unfrozen soil else !( mLayerTemp >= TcSoil, all water is unfrozen ) @@ -244,8 +242,6 @@ subroutine updateSoilSundials(& mLayerVolFracLiqPrev = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv endif - !if ( mLayerTemp - mLayerTempPrime*dt < TcSoilPrev )print*,"thawed" - !if ( mLayerTempPrev < TcSoilPrev )print*,"THAWED2" end if ! (check if soil is partially frozen) @@ -306,12 +302,10 @@ subroutine updateSoilSundials2(& character(*),intent(out) :: message ! error message ! define local variables real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) - real(rkind) :: TcSoilPrev ! previous timestep critical soil temperature when all water is unfrozen (K) real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) real(rkind) :: dt_inv ! inverse of timestep real(rkind) :: mLayerTempPrev ! estimate of previous timestep temperature (K) - real(rkind) :: mLayerVolFracLiqPrev ! previous timestep volumetric fraction of liquid water (-) ! initialize error control err=0; message="updateSoilSundials2/" @@ -325,7 +319,6 @@ subroutine updateSoilSundials2(& ! compute the critical soil temperature where all water is unfrozen (K) ! (eq 17 in Dall'Amico 2011) TcSoil = Tfreeze + min(mLayerMatricHead,0._rkind)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) - TcSoilPrev = Tfreeze + min(mLayerMatricHead - mLayerMatricHeadPrime*dt_cur,0._rkind)*gravity*Tfreeze/LH_fus ! *** compute volumetric fraction of liquid water for partially frozen soil if( mLayerTemp < TcSoil )then ! (check if soil temperature is less than the critical temperature) @@ -334,31 +327,16 @@ subroutine updateSoilSundials2(& xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - !if( mLayerTemp - mLayerTempPrime*dt_cur < TcSoilPrev )then ! was partially frozen on previous time step - if(mLayerPsiLiq<0._rkind)then - mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime - else - mLayerVolFracLiqPrime = 0._rkind - endif - !else ! was unfrozen on previous time step - ! using mLayerVolFracLiqPrev = mLayerVolFracWat - mLayerVolFracWatPrime*dt_cur - !mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracWat)*dt_inv + mLayerVolFracWatPrime ! = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv - !print*,"froze", mLayerVolFracLiqPrime,mLayerTemp,mLayerTempPrime,dt_cur,mLayerTemp - mLayerTempPrime*dt_cur-TcSoilPrev - !endif + if(mLayerPsiLiq<0._rkind)then + mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime + else + mLayerVolFracLiqPrime = 0._rkind + endif ! *** compute volumetric fraction of liquid water for unfrozen soil else !( mLayerTemp >= TcSoil, all water is unfrozen ) mLayerVolFracLiq = mLayerVolFracWat - !if( mLayerTemp - mLayerTempPrime*dt_cur >= TcSoilPrev )then ! was unfrozen on previous time step - mLayerVolFracLiqPrime = mLayerVolFracWatPrime - !else ! was partially frozen on previous time step - !mLayerTempPrev = mLayerTemp - mLayerTempPrime*dt_cur - !xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) - !mLayerPsiLiq = xConst*(mLayerTempPrev - Tfreeze) ! liquid water matric potential from the Clapeyron eqution - !mLayerVolFracLiqPrev = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - !mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv - !print*,"thawed", mLayerVolFracLiqPrime,mLayerTemp,mLayerTempPrime,dt_cur,mLayerTemp - mLayerTempPrime*dt_cur-TcSoilPrev - !endif + mLayerVolFracLiqPrime = mLayerVolFracWatPrime end if ! (check if soil is partially frozen) diff --git a/build/source/engine/updateVars4JacDAE.f90 b/build/source/engine/updateVars4JacDAE.f90 index 14fd1f6bf..734483cb4 100644 --- a/build/source/engine/updateVars4JacDAE.f90 +++ b/build/source/engine/updateVars4JacDAE.f90 @@ -193,9 +193,7 @@ subroutine updateVars4JacDAE(& real(rkind) :: scalarVolFracLiqPrime ! volumetric fraction of liquid water (-) real(rkind) :: scalarVolFracIcePrime ! volumetric fraction of ice (-) real(rkind) :: Tcrit ! critical soil temperature below which ice exists (K) - real(rkind) :: TcritPrev ! previous timestep critical soil temperature below which ice exists (K) real(rkind) :: xTemp ! temporary temperature (K) - real(rkind) :: xTempPrev ! previous timestep temperature for layer (K) real(rkind) :: fLiq ! fraction of liquid water (-) real(rkind) :: effSat ! effective saturation (-) real(rkind) :: avPore ! available pore space (-) @@ -274,7 +272,6 @@ subroutine updateVars4JacDAE(& dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(out): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - mLayerdTheta_dTkPrev => deriv_data%var(iLookDERIV%mLayerdTheta_dTkPrev)%dat ,& ! intent(out): [dp(:)] previous timestep derivative of volumetric liquid water content w.r.t. temperature dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature d2VolTot_d2Psi0 => deriv_data%var(iLookDERIV%d2VolTot_d2Psi0 )%dat ,& ! intent(out): [dp(:)] second derivative in total water content w.r.t. total water matric potential mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2 )%dat ,& ! intent(out): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature @@ -425,15 +422,6 @@ subroutine updateVars4JacDAE(& case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return end select - ! compute the previous timestep temperature - select case(ixDomainType) - case(iname_veg, iname_snow); TcritPrev = Tfreeze; xTempPrev = realmissing ! will not use - case(iname_soil) - TcritPrev = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) - mLayerMatricHeadPrime(ixControlIndex)*dt) - xTempPrev = xTemp - mLayerTempPrime(iLayer)*dt - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - end select - ! define brackets for the root ! NOTE: start with an enormous range; updated quickly in the iterations tempMin = xTemp - 10._rkind @@ -501,17 +489,11 @@ subroutine updateVars4JacDAE(& mLayerdTheta_dTk(iLayer) = dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatTrial(iLayer) mLayerd2Theta_dTk2(iLayer) = 2._rkind * snowfrz_scale**2._rkind * ( (Tfreeze - xTemp) * 2._rkind * fLiq * dFracLiqSnow_dTk(iLayer) - fLiq**2._rkind ) * mLayerVolFracWatTrial(iLayer) dVolHtCapBulk_dTk(iLayer) = ( iden_water * (-Cp_ice + Cp_water) + iden_air * (iden_water/iden_ice - 1._rkind) * Cp_air ) * mLayerdTheta_dTk(iLayer) - mLayerdTheta_dTkPrev(iLayer) = realMissing ! do not use case(iname_soil) dFracLiqSnow_dTk(iLayer) = 0._rkind !dTheta_dTk(xTemp,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex))/ mLayerVolFracWatTrial(iLayer) mLayerdTheta_dTk(iLayer) = dTheta_dTk(xTemp,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) mLayerd2Theta_dTk2(iLayer) = d2Theta_dTk2(xTemp,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) dVolHtCapBulk_dTk(iLayer) = (-iden_ice * Cp_ice + iden_water * Cp_water) * mLayerdTheta_dTk(iLayer) - if( xTempPrev < TcritPrev ) then ! was frozen on previous time step - mLayerdTheta_dTkPrev(iLayer) = dTheta_dTk(xTempPrev,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - else - mLayerdTheta_dTkPrev(iLayer) = 0._rkind - endif case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return end select ! domain type @@ -519,16 +501,8 @@ subroutine updateVars4JacDAE(& else select case(ixDomainType) case(iname_veg); dTheta_dTkCanopy = 0._rkind; d2Theta_dTkCanopy2 = 0._rkind; dFracLiqVeg_dTkCanopy = 0._rkind; dVolHtCapBulk_dTkCanopy = 0._rkind - case(iname_snow); mLayerdTheta_dTk(iLayer) = 0._rkind; mLayerd2Theta_dTk2(iLayer) = 0._rkind; dFracLiqSnow_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind; & - mLayerdTheta_dTkPrev(iLayer) = realMissing ! do not use - case(iname_soil) - mLayerdTheta_dTk(iLayer) = 0._rkind; mLayerd2Theta_dTk2(iLayer) = 0._rkind; dFracLiqSnow_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind - if( xTempPrev < TcritPrev ) then ! was frozen on previous time step - mLayerdTheta_dTkPrev(iLayer) = dTheta_dTk(xTempPrev,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - else - mLayerdTheta_dTkPrev(iLayer) = 0._rkind - endif - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + case(iname_snow); mLayerdTheta_dTk(iLayer) = 0._rkind; mLayerd2Theta_dTk2(iLayer) = 0._rkind; dFracLiqSnow_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind + case(iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind; mLayerd2Theta_dTk2(iLayer) = 0._rkind; dFracLiqSnow_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind end select ! domain type endif From a96ff416bae47dc52684c778eb46462bf9e4b365 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 27 May 2022 20:52:58 +0900 Subject: [PATCH 0297/1472] Added failure message in for low canopy temperature --- build/source/engine/conv_funcs.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/source/engine/conv_funcs.f90 b/build/source/engine/conv_funcs.f90 index 71b8d73ce..9c7891cf1 100644 --- a/build/source/engine/conv_funcs.f90 +++ b/build/source/engine/conv_funcs.f90 @@ -88,6 +88,7 @@ subroutine satVapPress(TC, SVP, dSVP_dT) SVP = SATVPFRZ * EXP( (X1*TC)/(X2 + TC) ) ! Saturated Vapour Press (Pa) dSVP_dT = SVP * (X1/(X2 + TC) - X1*TC/(X2 + TC)**2._rkind) +if(X2 + TC < 0) print*, "error, canopy temperature is very low, satVapPress=Inf" !will fail as SVP=inf if(testDeriv) print*, 'dSVP_dT check... ', SVP, dSVP_dT, (SATVPRESS(TC+dx) - SVP)/dx END SUBROUTINE satVapPress From 846e3379a7b9f005b4d1b594004d7c005c212436 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 7 Jun 2022 10:18:10 +0900 Subject: [PATCH 0298/1472] Make nonzero constraint values for Sundials. Note, currently turned off any constraints, and need to install the modified (by me) version of Sundials in order to use the nonzero constraints. --- build/build_cmakeSundials_cop | 3 +- build/build_cmakeSundials_gra | 3 +- build/build_cmakeSundials_mac | 3 +- build/source/engine/solveByIDA.f90 | 23 +++++--- build/source/engine/systemSolvSundials.f90 | 69 +++++++++++++++++----- 5 files changed, 75 insertions(+), 26 deletions(-) diff --git a/build/build_cmakeSundials_cop b/build/build_cmakeSundials_cop index 6d0e22052..220b75d84 100755 --- a/build/build_cmakeSundials_cop +++ b/build/build_cmakeSundials_cop @@ -1,7 +1,8 @@ # from ../../sundials/builddir, run # cp ../../summa/build/build_cmakeSundials_cop build_cmake # run script from the builddir directory with ./build_cmake +# Note, using -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF because modified constraint code and did not modify examples -cmake ../../sundials-5.8.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir/examples +cmake ../../sundials-5.8.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir/examples diff --git a/build/build_cmakeSundials_gra b/build/build_cmakeSundials_gra index be3d6e209..f43375c6a 100755 --- a/build/build_cmakeSundials_gra +++ b/build/build_cmakeSundials_gra @@ -1,7 +1,8 @@ # from ../../sundials/builddir, run # cp ../../summa/build/build_cmakeSundials_gra build_cmake # run script from the builddir directory with ./build_cmake +# Note, using -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF because modified constraint code and did not modify examples -cmake ../../sundials-5.8.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/home/avanb/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/home/avanb/SummaSundials/sundials/instdir/examples +cmake ../../sundials-5.8.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/home/avanb/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/home/avanb/SummaSundials/sundials/instdir/examples diff --git a/build/build_cmakeSundials_mac b/build/build_cmakeSundials_mac index 6df9ed588..9a80200f0 100755 --- a/build/build_cmakeSundials_mac +++ b/build/build_cmakeSundials_mac @@ -1,7 +1,8 @@ # from ../../sundials/builddir, run # cp ../../summa/build/build_cmakeSundialsmac_ build_cmake # run script from the builddir directory with ./build_cmake +# Note, using -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF because modified constraint code and did not modify examples -cmake ../../sundials-5.8.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/Users/amedin/Research/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/Users/amedin/Research/SummaSundials/sundials/instdir/examples +cmake ../../sundials-5.8.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/Users/amedin/Research/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/Users/amedin/Research/SummaSundials/sundials/instdir/examples diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 4dccef274..eaec0c3ce 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -105,7 +105,8 @@ subroutine solveByIDA( & scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVecInit, & ! intent(in): initial state vector - stateVecConstraints, & ! intent(inout): model state vector constraints + stateVecConstraints, & ! intent(inout): model state vector constraints + stateVecConstValues, & ! intent(inout): model state vector constraint values sMul, & ! intent(inout): state vector multiplier (USEd in the residual calculations) dMat, & ! intent(inout): diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures @@ -178,6 +179,7 @@ subroutine solveByIDA( & ! input: state vectors real(rkind),intent(in) :: stateVecInit(:) ! model state vector real(rkind),intent(inout) :: stateVecConstraints(:) ! model state vector constraints + real(rkind),intent(inout) :: stateVecConstValues(:) ! model state vector constraint values real(qp),intent(in) :: sMul(:) ! state vector multiplier (used in the residual calculations) real(rkind), intent(inout) :: dMat(:) ! input: data structures @@ -212,6 +214,7 @@ subroutine solveByIDA( & type(N_Vector), pointer :: sunvec_y ! sundials solution vector type(N_Vector), pointer :: sunvec_yp ! sundials derivative vector type(N_Vector), pointer :: sunvec_c ! sundials constraints vector + type(N_Vector), pointer :: sunvec_cv ! sundials constraints vector of values type(N_Vector), pointer :: sunvec_av ! sundials tolerance vector type(SUNMatrix), pointer :: sunmat_A ! sundials matrix type(SUNLinearSolver), pointer :: sunlinsol_LS ! sundials linear solver @@ -248,6 +251,7 @@ subroutine solveByIDA( & ! initialize error control err=0; message="solveByIDA/" + nState = nStat idaSucceeds = .true. ! fill eqns_data which will be required later to call eval8DAE @@ -331,7 +335,6 @@ subroutine solveByIDA( & allocate( eqns_data%fluxVec(nState) ) allocate( eqns_data%resSink(nState) ) - startQuadrature = .true. ! create serial vectors sunvec_y => FN_VMake_Serial(nState, stateVec) @@ -343,6 +346,9 @@ subroutine solveByIDA( & sunvec_c => FN_VMake_Serial(nState, stateVecConstraints) if (.not. associated(sunvec_c)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif + sunvec_cv => FN_VMake_Serial(nState, stateVecConstValues) + if (.not. associated(sunvec_cv)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif + ! Initialize solution vectors call setInitialCondition(nState, stateVecInit, sunvec_y, sunvec_yp) @@ -351,7 +357,7 @@ subroutine solveByIDA( & if (.not. c_associated(ida_mem)) then; err=20; message='solveByIDA: ida_mem = NULL'; return; endif ! Set constraints - retval = FIDASetConstraints(ida_mem, sunvec_c) + !retval = FIDASetConstraints(ida_mem, sunvec_c, sunvec_cv) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetConstraints'; return; endif ! Attach user data to memory @@ -431,18 +437,18 @@ subroutine solveByIDA( & eqns_data%scalarCanopyIcePrev = prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) eqns_data%scalarCanopyLiqPrev = prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) - eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) + eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) eqns_data%mLayerVolFracLiqPrev(:) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(:) eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) eqns_data%scalarAquiferStoragePrev = prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) - mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) + mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) eqns_data%ixSaturation = ixSaturation - mLayerDepth = prog_data%var(iLookPROG%mLayerDepth)%dat - scalarSnowDepth = prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) - scalarSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) + mLayerDepth = prog_data%var(iLookPROG%mLayerDepth)%dat + scalarSnowDepth = prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) + scalarSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) !********************************************************************************** !****************************** Main Solver *************************************** @@ -671,6 +677,7 @@ subroutine solveByIDA( & call FN_VDestroy(sunvec_y) call FN_VDestroy(sunvec_yp) call FN_VDestroy(sunvec_c) + call FN_VDestroy(sunvec_cv) end subroutine solveByIDA diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index c65a8d1d4..a9c2aca36 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -203,10 +203,14 @@ subroutine systemSolvSundials(& real(qp) :: rVec(nState) ! NOTE: qp ! residual vector real(rkind) :: rAdd(nState) ! additional terms in the residual vector real(rkind) :: stateVecConstraints(nState) ! model state vector constraints + real(rkind) :: stateVecConstValues(nState) ! model state vector constraint values real(rkind) :: fOld ! function values (-); NOTE: dimensionless because scaled logical(lgt) :: feasible ! feasibility flag real(rkind) :: atol(nState) ! absolute telerance real(rkind) :: rtol(nState) ! relative tolerance + integer(i4b) :: iLayer ! index of model layer in the snow+soil domain + real(rkind) :: xMin,xMax ! minimum and maximum values for water content + real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a data step integer(i4b) :: tol_iter ! iteration index real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix @@ -224,6 +228,9 @@ subroutine systemSolvSundials(& scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) + ! soil parameters + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) ! model state variables scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) @@ -240,6 +247,10 @@ subroutine systemSolvSundials(& airtemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers @@ -331,7 +342,6 @@ subroutine systemSolvSundials(& if(ixCasNrg/=integerMissing) stateVecTrial(ixCasNrg) = stateVecInit(ixCasNrg) + (airtemp - stateVecInit(ixCasNrg))*tempAccelerate if(ixVegNrg/=integerMissing) stateVecTrial(ixVegNrg) = stateVecInit(ixVegNrg) + (airtemp - stateVecInit(ixVegNrg))*tempAccelerate - ! compute H_T at the beginning of the data step call t2enthalpy_T(& ! input: data structures @@ -404,7 +414,6 @@ subroutine systemSolvSundials(& if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif - ! copy over the initial flux structure since some model fluxes are not computed in the iterations do concurrent ( iVar=1:size(flux_meta) ) flux_temp%var(iVar)%dat(:) = flux_init%var(iVar)%dat(:) @@ -448,23 +457,52 @@ subroutine systemSolvSundials(& ! 0 then no constraint is imposed on y. ! 1 then yi will be constrained to be yi>=0 !-1 then yi will be constrained to be yi<=0 - stateVecConstraints = 0.d0 - !stateVec(ixCasNrg) - canopyTempMax = -1.d0 !canopy air space temperature cannot be too high - !stateVec(ixVegNrg) - canopyTempMax = -1.d0 !canopy temperature cannot be too high - stateVecConstraints(ixVegHyd) = 1.d0 !canopy liquid water cannot be negative + stateVecConstraints = 0._rkind + stateVecConstValues = 0._rkind + stateVecConstraints(ixCasNrg) = -1._rkind !canopy air space temperature cannot be above canopyTempMax + stateVecConstraints(ixVegNrg) = -1._rkind !canopy temperature cannot be above canopyTempMax + stateVecConstraints(ixVegHyd) = 1._rkind !canopy liquid water cannot be below bound of 0 + stateVecConstValues(ixCasNrg) = canopyTempMax !canopy air space temperature cannot be above canopyTempMax + stateVecConstValues(ixVegNrg) = canopyTempMax !canopy temperature cannot be above canopyTempMax + stateVecConstValues(ixVegHyd) = 0._rkind !canopy liquid water cannot be below 0 + ! loop through non-missing energy state variables in the snow domain - !do concurrent (iLayer=1:nLayers,ixSnowOnlyNrg(iLayer)/=integerMissing) - ! stateVec(ixSnowOnlyNrg(iLayer)) - Tfreeze = 1.d0 !snow temp cannot be less than Tfreeze - !end do - ! loop through non-missing hydrology state variables in the snow+soil domain - !do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) - ! check the minimum and maximum water constraints - ! stateVec(ixSnowSoilHyd(iLayer)) - xMin = 1.d0 !water cannot be less than xMin - ! stateVec(ixSnowSoilHyd(iLayer)) - xMax = -1.d0 !water cannot be more than xMax - !enddo + do concurrent (iLayer=1:nSnow,ixSnowOnlyNrg(iLayer)/=integerMissing) + stateVecConstraints(ixSnowOnlyNrg(iLayer)) = 1._rkind !snow temp cannot be below Tfreeze + stateVecConstValues(ixSnowOnlyNrg(iLayer)) = Tfreeze !snow temp cannot be below Tfreeze + end do + ! loop through non-missing hydrology state variables in the snow+soil domain + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) + ! check the minimum and maximum water constraints + if(ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_liqLayer)then + ! --> minimum water constraints + if (layerType(iLayer) == iname_soil) then + xMin = theta_res(iLayer-nSnow) + else + xMin = 0._rkind + endif + stateVecConstraints(ixSnowSoilHyd(iLayer)) = 1._rkind !water cannot be below xMin + stateVecConstValues(ixSnowSoilHyd(iLayer)) = xMin !water cannot be below xMin + ! --> maximum water constraints, SUNDIALS CAN ONLY DO ONE INEQUALITY PER STATE VARIABLE + ! mLayerVolFracIce => eqns_data%mLayerVolFracIceTrial ,& ! intent(in): [dp(:)] trial vector of volumetric ice water content (-) + ! select case( layerType(iLayer) ) + ! case(iname_snow); xMax = merge(iden_ice, 1._rkind - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) + ! case(iname_soil); xMax = merge(theta_sat(iLayer-nSnow), theta_sat(iLayer-nSnow) - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) + ! end select + ! stateVecConstraints(ixSnowSoilHyd(iLayer)) = -1._rkind !water cannot be above xMax + ! stateVecConstValues(ixSnowSoilHyd(iLayer)) = xMax !water cannot be above xMax + + endif ! if water states + end do ! loop through non-missing hydrology state variables in the snow+soil domain + ! loop through non-missing hydrology state variables in the snow+soil domain + !do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) + ! minimum temperature constraints + ! xMin = 100._rkind !degrees C + ! stateVecConstraints(ixSnowSoilNrg(iLayer)) - xMin = 1._rkind !water cannot be less than xMin + !enddo !------------------- ! * solving F(y,y') = 0 by IDA. Here, y is the state vector @@ -495,6 +533,7 @@ subroutine systemSolvSundials(& ! input: state vector stateVecTrial, & ! intent(in): model state vector at the beginning of the data time step stateVecConstraints, & ! intent(inout): model state vector constraints + stateVecConstValues, & ! intent(inout): model state vector constraint values sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout) diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures From 7d7587605025ab64b64e0836e051e8bb22a8740a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 7 Jun 2022 16:45:08 +0900 Subject: [PATCH 0299/1472] had some signs backwards --- build/source/engine/solveByIDA.f90 | 2 +- build/source/engine/systemSolvSundials.f90 | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index eaec0c3ce..336c78562 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -357,7 +357,7 @@ subroutine solveByIDA( & if (.not. c_associated(ida_mem)) then; err=20; message='solveByIDA: ida_mem = NULL'; return; endif ! Set constraints - !retval = FIDASetConstraints(ida_mem, sunvec_c, sunvec_cv) + !retval = FIDASetConstraints(ida_mem, sunvec_c, sunvec_cv) !uncomment this line to use feasibility constraints if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetConstraints'; return; endif ! Attach user data to memory diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index a9c2aca36..5d795fe4a 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -468,8 +468,8 @@ subroutine systemSolvSundials(& ! loop through non-missing energy state variables in the snow domain do concurrent (iLayer=1:nSnow,ixSnowOnlyNrg(iLayer)/=integerMissing) - stateVecConstraints(ixSnowOnlyNrg(iLayer)) = 1._rkind !snow temp cannot be below Tfreeze - stateVecConstValues(ixSnowOnlyNrg(iLayer)) = Tfreeze !snow temp cannot be below Tfreeze + stateVecConstraints(ixSnowOnlyNrg(iLayer)) = -1._rkind !snow temp cannot be above Tfreeze + stateVecConstValues(ixSnowOnlyNrg(iLayer)) = Tfreeze !snow temp cannot be above Tfreeze end do ! loop through non-missing hydrology state variables in the snow+soil domain From 55f6d45a0ecb8d205d52849868c11824577d430a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 7 Jun 2022 20:38:29 +0900 Subject: [PATCH 0300/1472] Trying to break out if snow layer too hot, merge layers and quit loop. --- build/source/engine/layerDivide.f90 | 129 ---------------- build/source/engine/layerMerge.f90 | 108 +------------- build/source/engine/solveByIDA.f90 | 166 +++++---------------- build/source/engine/systemSolvSundials.f90 | 2 +- 4 files changed, 43 insertions(+), 362 deletions(-) diff --git a/build/source/engine/layerDivide.f90 b/build/source/engine/layerDivide.f90 index ec9b2407f..266e86c7f 100644 --- a/build/source/engine/layerDivide.f90 +++ b/build/source/engine/layerDivide.f90 @@ -73,139 +73,10 @@ module layerDivide_module implicit none private public::layerDivide -public::needDivideLayer contains - ! *********************************************************************************************************** - ! public subroutine needDivideLayer: check to see if we need to add new snowfall to the system - ! *********************************************************************************************************** - subroutine needDivideLayer(& - ! input/output: model data structures - model_decisions, & ! intent(in): model decisions - mpar_data, & ! intent(in): model parameters - nSnow, & ! intent(in): number of snow layers - mLayerDepth, & ! intent(in): - scalarSnowDepth, & ! intent(in) - ! output - divideLayer, & ! intent(out): flag to denote that a layer was divided - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------- - ! computational modules - USE globalData,only:maxSnowLayers, & ! maximum number of snow layers - veryBig - implicit none - ! -------------------------------------------------------------------------------------------------------- - ! input/output: model data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(var_dlength),intent(in) :: mpar_data ! model parameters - integer(i4b),intent(in) :: nSnow ! number of snow layers - real(rkind),intent(in) :: mLayerDepth(:) - real(rkind),intent(in) :: scalarSnowDepth - ! output - logical(lgt),intent(out) :: divideLayer ! flag to denote that a layer was divided - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------- - ! define local variables - integer(i4b) :: iLayer ! layer index - integer(i4b) :: jLayer ! layer index - real(rkind),dimension(4) :: zmax_lower ! lower value of maximum layer depth - real(rkind),dimension(4) :: zmax_upper ! upper value of maximum layer depth - real(rkind) :: zmaxCheck ! value of zmax for a given snow layer - integer(i4b) :: nCheck ! number of layers to check to divide - logical(lgt) :: createLayer ! flag to indicate we are creating a new snow layer - ! -------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="needDivideLayer/" - ! -------------------------------------------------------------------------------------------------------- - ! associate variables in the data structures - associate(& - ! model decisions - ix_snowLayers => model_decisions(iLookDECISIONS%snowLayers)%iDecision, & ! decision for snow combination - ! model parameters (control the depth of snow layers) - zmax => mpar_data%var(iLookPARAM%zmax)%dat(1), & ! maximum layer depth (m) - zmaxLayer1_lower => mpar_data%var(iLookPARAM%zmaxLayer1_lower)%dat(1), & ! maximum layer depth for the 1st (top) layer when only 1 layer (m) - zmaxLayer2_lower => mpar_data%var(iLookPARAM%zmaxLayer2_lower)%dat(1), & ! maximum layer depth for the 2nd layer when only 2 layers (m) - zmaxLayer3_lower => mpar_data%var(iLookPARAM%zmaxLayer3_lower)%dat(1), & ! maximum layer depth for the 3rd layer when only 3 layers (m) - zmaxLayer4_lower => mpar_data%var(iLookPARAM%zmaxLayer4_lower)%dat(1), & ! maximum layer depth for the 4th layer when only 4 layers (m) - zmaxLayer1_upper => mpar_data%var(iLookPARAM%zmaxLayer1_upper)%dat(1), & ! maximum layer depth for the 1st (top) layer when > 1 layer (m) - zmaxLayer2_upper => mpar_data%var(iLookPARAM%zmaxLayer2_upper)%dat(1), & ! maximum layer depth for the 2nd layer when > 2 layers (m) - zmaxLayer3_upper => mpar_data%var(iLookPARAM%zmaxLayer3_upper)%dat(1), & ! maximum layer depth for the 3rd layer when > 3 layers (m) - zmaxLayer4_upper => mpar_data%var(iLookPARAM%zmaxLayer4_upper)%dat(1) & ! maximum layer depth for the 4th layer when > 4 layers (m) - ) ! end associate statement - - ! --------------------------------------------------------------------------------------------------- - - ! initialize flag to denote that a layer was divided - divideLayer=.false. - - ! identify algorithmic control parameters to syb-divide and combine snow layers - zmax_lower = (/zmaxLayer1_lower, zmaxLayer2_lower, zmaxLayer3_lower, zmaxLayer4_lower/) - zmax_upper = (/zmaxLayer1_upper, zmaxLayer2_upper, zmaxLayer3_upper, zmaxLayer4_upper/) - - ! ***** special case of no snow layers - if(nSnow==0)then - - ! check if create the first snow layer - select case(ix_snowLayers) - case(sameRulesAllLayers); createLayer = (scalarSnowDepth > zmax) - case(rulesDependLayerIndex); createLayer = (scalarSnowDepth > zmaxLayer1_lower) - case default; err=20; message=trim(message)//'unable to identify option to combine/sub-divide snow layers'; return - end select ! (option to combine/sub-divide snow layers) - - ! ** create a new snow layer - if(createLayer)then - ! flag that the layers have changed - divideLayer=.true. - return - end if - - ! ***** sub-divide snow layers, if necessary - else ! if nSnow>0 - - ! identify the number of layers to check for need for sub-division - nCheck = min(nSnow, maxSnowLayers-1) ! the depth of the last layer, if it exists, does not have a maximum value - ! loop through all layers, and sub-divide a given layer, if necessary - do iLayer=1,nCheck - divideLayer=.false. - - ! identify the maximum depth of the layer - select case(ix_snowLayers) - case(sameRulesAllLayers) - if (nCheck >= maxSnowLayers-1) then - ! make sure we don't divide so make very big - zmaxCheck = veryBig - else - zmaxCheck = zmax - end if - case(rulesDependLayerIndex) - if(iLayer == nSnow)then - zmaxCheck = zmax_lower(iLayer) - else - zmaxCheck = zmax_upper(iLayer) - end if - case default; err=20; message=trim(message)//'unable to identify option to combine/sub-divide snow layers'; return - end select ! (option to combine/sub-divide snow layers) - - ! check the need to sub-divide - if(mLayerDepth(iLayer) > zmaxCheck)then - ! flag that layers were divided - divideLayer=.true. - return - end if ! (if sub-dividing layer) - - end do ! (looping through layers) - - end if ! if nSnow==0 - - ! end associate variables in data structure - end associate - end subroutine needDivideLayer - - ! *********************************************************************************************************** ! public subroutine layerDivide: add new snowfall to the system, and increase number of snow layers if needed ! *********************************************************************************************************** diff --git a/build/source/engine/layerMerge.f90 b/build/source/engine/layerMerge.f90 index b5b5276ea..a423811af 100644 --- a/build/source/engine/layerMerge.f90 +++ b/build/source/engine/layerMerge.f90 @@ -62,116 +62,10 @@ module layerMerge_module implicit none private public::layerMerge -public::needMergeLayers contains - ! ***************************************************************************************************************** - ! public subroutine needMergeLayers: Should we merge layers? (if the thickness is less than zmin) - ! ***************************************************************************************************************** - subroutine needMergeLayers(& - ! input/output: model data structures - tooMuchMelt, & ! intent(in): flag to force merge of snow layers - model_decisions, & ! intent(in): model decisions - mpar_data, & ! intent(in): model parameters - nSnow, & ! intent(in): - mLayerDepth, & ! intent(inout): model prognostic variables for a local HRU - ! output - mergedLayers, & ! intent(out): flag to denote that layers were merged - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------- - implicit none - ! -------------------------------------------------------------------------------------------------------- - ! input/output: model data structures - logical(lgt),intent(in) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(var_dlength),intent(in) :: mpar_data ! model parameters - integer(i4b),intent(in) :: nSnow - real(rkind),intent(in) :: mLayerDepth(:) ! model prognostic variables for a local HRU - ! output - logical(lgt),intent(out) :: mergedLayers ! flag to denote that layers were merged - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------- - ! define local variables - real(rkind),dimension(5) :: zminLayer ! minimum layer depth in each layer (m) - logical(lgt) :: removeLayer ! flag to indicate need to remove a layer - integer(i4b) :: nCheck ! number of layers to check for combination - integer(i4b) :: iSnow ! index of snow layers (looping) - integer(i4b) :: jSnow ! index of snow layer identified for combination with iSnow - integer(i4b) :: kSnow ! index of the upper layer of the two layers identified for combination - ! initialize error control - err=0; message="needMergeLayers/" - ! -------------------------------------------------------------------------------------------------------- - ! associate variables to the data structures - associate(& - ! model decisions - ix_snowLayers => model_decisions(iLookDECISIONS%snowLayers)%iDecision, & ! decision for snow combination - ! model parameters (control the depth of snow layers) - zmin => mpar_data%var(iLookPARAM%zmin)%dat(1), & ! minimum layer depth (m) - zminLayer1 => mpar_data%var(iLookPARAM%zminLayer1)%dat(1), & ! minimum layer depth for the 1st (top) layer (m) - zminLayer2 => mpar_data%var(iLookPARAM%zminLayer2)%dat(1), & ! minimum layer depth for the 2nd layer (m) - zminLayer3 => mpar_data%var(iLookPARAM%zminLayer3)%dat(1), & ! minimum layer depth for the 3rd layer (m) - zminLayer4 => mpar_data%var(iLookPARAM%zminLayer4)%dat(1), & ! minimum layer depth for the 4th layer (m) - zminLayer5 => mpar_data%var(iLookPARAM%zminLayer5)%dat(1) & ! minimum layer depth for the 5th (bottom) layer (m) - ) ! end associate statement - ! -------------------------------------------------------------------------------------------------------- - - ! identify algorithmic control parameters to syb-divide and combine snow layers - zminLayer = (/zminLayer1, zminLayer2, zminLayer3, zminLayer4, zminLayer5/) - - ! intialize the modified layers flag - mergedLayers=.false. - - kSnow=0 ! initialize first layer to test (top layer) - do ! attempt to remove multiple layers in a single time step (continuous do loop with exit clause) - - ! special case of >5 layers: add an offset to use maximum threshold from layer above - if(ix_snowLayers == rulesDependLayerIndex .and. nSnow > 5)then - nCheck=5 - else - nCheck=nSnow - end if - - ! loop through snow layers - do iSnow=kSnow+1,nCheck - ! check if the layer depth is less than the depth threshold - select case(ix_snowLayers) - case(sameRulesAllLayers); removeLayer = (mLayerDepth(iSnow) < zmin) - case(rulesDependLayerIndex); removeLayer = (mLayerDepth(iSnow) < zminLayer(iSnow)) - case default; err=20; message=trim(message)//'unable to identify option to combine/sub-divide snow layers'; return - end select ! (option to combine/sub-divide snow layers) - - ! check if we have too much melt - ! NOTE: assume that this is the top snow layer; need more trickery to relax this assumption - if(tooMuchMelt .and. iSnow==1) removeLayer=.true. - - ! check if need to remove a layer - if(removeLayer)then - ! flag that we modified a layer - mergedLayers=.true. - return - end if ! (if layer is below the mass threshold) - - kSnow=iSnow ! ksnow is used for completion test, so include here - - end do ! (looping through snow layers) - - !print*, 'ksnow = ', ksnow - - ! exit if finished - if(kSnow==nCheck)exit - - end do ! continuous do - - ! end association to variables in the data structure - end associate - - end subroutine needMergeLayers - - ! ***************************************************************************************************************** ! public subroutine layerMerge: merge layers if the thickness is less than zmin ! ***************************************************************************************************************** @@ -239,7 +133,7 @@ subroutine layerMerge(& ) ! end associate statement ! -------------------------------------------------------------------------------------------------------- - ! identify algorithmic control parameters to syb-divide and combine snow layers + ! identify algorithmic control parameters to sub-divide and combine snow layers zminLayer = (/zminLayer1, zminLayer2, zminLayer3, zminLayer4, zminLayer5/) ! intialize the modified layers flag diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 336c78562..21230bb07 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -64,6 +64,7 @@ module solveByIDA_module USE var_lookup,only:iLookDERIV ! named variables for structure elements USE var_lookup,only:iLookFLUX ! named variables for structure elements USE var_lookup,only:iLookPARAM ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements ! provide access to the derived types to define the data structures USE data_types,only:& @@ -135,28 +136,24 @@ subroutine solveByIDA( & ) !======= Inclusions =========== - USE fida_mod ! Fortran interface to IDA - USE fnvector_serial_mod ! Fortran interface to serial N_Vector - USE fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix - USE fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver - USE fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix - USE fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver - USE fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver - USE fsundials_matrix_mod ! Fortran interface to generic SUNMatrix - USE fsundials_nvector_mod ! Fortran interface to generic N_Vector - USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver - USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver + USE fida_mod ! Fortran interface to IDA + USE fnvector_serial_mod ! Fortran interface to serial N_Vector + USE fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix + USE fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver + USE fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix + USE fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver + USE fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver + USE fsundials_matrix_mod ! Fortran interface to generic SUNMatrix + USE fsundials_nvector_mod ! Fortran interface to generic N_Vector + USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver + USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver USE allocspace_module,only:allocLocal ! allocate local data structures USE evalDAE4IDA_module,only:evalDAE4IDA ! DAE/ODE functions USE evalJac4IDA_module,only:evalJac4IDA ! system Jacobian USE tol4IDA_module,only:computWeight4IDA ! weigth required for tolerances USE eval8DAE_module,only:eval8DAE ! residual of DAE - USE computEnthalpy_module,only:computEnthalpy ! enthalpy - USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy - USE computSnowDepth_module,only:computSnowDepth ! snow depth USE var_derive_module,only:calcHeight ! height at layer interfaces and layer mid-point - USE layerDivide_module,only:needDivideLayer ! do we need to divide snow layers - Use layerMerge_module,only:needMergeLayers ! do we need to merge snow layers + USE layerMerge_module,only:layerMerge ! merge snow layers if go above freezing (too thin) !======= Declarations ========= implicit none @@ -233,19 +230,11 @@ subroutine solveByIDA( & integer(c_long) :: nState ! total number of state variables real(rkind) :: rVec(nStat) real(qp) :: tret(1) - real(rkind) :: bulkDensity ! bulk density of a given layer (kg m-3) - real(rkind) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) logical(lgt) :: tooMuchMelt - logical(lgt) :: divideLayer logical(lgt) :: mergedLayers - logical(lgt),parameter :: checkSnow = .false. !.true. logical(lgt),parameter :: offErrWarnMessage = .false. real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) - real(rkind) :: mLayerDepth(nLayers) - real(rkind) :: scalarSnowDepth - real(rkind) :: scalarSWE - real(rkind) :: mLayerMeltFreeze(nLayers) integer(i4b) :: i ! ----------------------------------------------------------------------------------------------------- @@ -446,9 +435,6 @@ subroutine solveByIDA( & eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) eqns_data%ixSaturation = ixSaturation - mLayerDepth = prog_data%var(iLookPROG%mLayerDepth)%dat - scalarSnowDepth = prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) - scalarSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) !********************************************************************************** !****************************** Main Solver *************************************** @@ -465,6 +451,14 @@ subroutine solveByIDA( & exit endif + tooMuchMelt = .false. + ! loop through non-missing energy state variables in the snow domain to see if need to merge + ! CURRENTLY WILL ONLY MERGE TOP LAYER + do concurrent (i=1:nSnow,indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing) + if (stateVec(indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)) > Tfreeze) tooMuchMelt = .true. !need to merge + end do + if(tooMuchMelt)exit + ! get the last stepsize retval = FIDAGetLastStep(ida_mem, dt_last) @@ -497,7 +491,7 @@ subroutine solveByIDA( & eqns_data%bvar_data, & ! intent(in): average model variables for the entire basin eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU ! input-output: data structures - eqns_data%indx_data, & ! intent(inou): index data + eqns_data%indx_data, & ! intent(inout): index data eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables @@ -562,81 +556,10 @@ subroutine solveByIDA( & eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial - if(checkSnow)then - mLayerDepth(:) = prog_data%var(iLookPROG%mLayerDepth)%dat(:) - ! compute the melt in each snow and soil layer - if(nSnow>0)then - mLayerMeltFreeze(1:nSnow) = -( eqns_data%mLayerVolFracIceTrial(1:nSnow) - prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow) ) * iden_ice - mLayerMeltFreeze(nSnow+1:nLayers) = -(eqns_data%mLayerVolFracIceTrial(nSnow+1:nLayers) - prog_data%var(iLookPROG%mLayerVolFracIce)%dat(nSnow+1:nLayers))*iden_water - endif - - call computSnowDepth(& - tret(1), & ! intent(in) - eqns_data%nSnow, & ! intent(in) - flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! intent(in) - eqns_data%mLayerVolFracLiqTrial, & ! intent(inout) - eqns_data%mLayerVolFracIceTrial, & ! intent(inout) - eqns_data%mLayerTempTrial, & ! intent(in) - mLayerMeltFreeze, & ! intent(in) - eqns_data%mpar_data, & ! intent(in) - ! output - mLayerDepth, & ! intent(inout) - ! error control - err,message) ! intent(out): error control - if(err/=0)then; err=55; return; end if - - ! recompute snow depth and SWE - if(eqns_data%nSnow > 0)then - scalarSnowDepth = sum( mLayerDepth(1:nSnow) ) - scalarSWE = sum( (eqns_data%mLayerVolFracLiqTrial(1:nSnow)*iden_water + eqns_data%mLayerVolFracIceTrial(1:nSnow)*iden_ice) * mLayerDepth(1:nSnow) ) - end if - - ! check the need to merge snow layers - tooMuchMelt = .false. - if(eqns_data%nSnow>0)then - ! check that we did not remove the entire layer - if(mLayerDepth(1) < 1.e-6_rkind) exit - ! compute the energy required to melt the top snow layer (J m-2) - bulkDensity = eqns_data%mLayerVolFracIceTrial(1)*iden_ice + eqns_data%mLayerVolFracLiqTrial(1)*iden_water - volEnthalpy = temp2ethpy(eqns_data%mLayerTempTrial(1),bulkDensity,eqns_data%mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1)) - if(-volEnthalpy < flux_data%var(iLookFLUX%mLayerNrgFlux)%dat(1)*tret(1)) tooMuchMelt = .true. - endif - - if(tooMuchMelt) exit - - divideLayer = .false. - call needDivideLayer(& - ! input/output: model data structures - model_decisions, & ! intent(in): model decisions - eqns_data%mpar_data, & ! intent(in): model parameters - eqns_data%nSnow, & ! intent(in): number of snow layers - mLayerDepth, & ! intent(in): - scalarSnowDepth, & ! intent(in) - ! output - divideLayer, & ! intent(out): flag to denote that a layer was divided - err,message) ! intent(out): error control - if(divideLayer .and. tret(1)>50) exit - - mergedLayers = .false. - call needMergeLayers(& - ! input/output: model data structures - tooMuchMelt, & ! intent(in): flag to force merge of snow layers - model_decisions, & ! intent(in): model decisions - eqns_data%mpar_data, & ! intent(in): model parameters - eqns_data%nSnow, & ! intent(in): - mLayerDepth, & ! intent(inout): model prognostic variables for a local HRU - ! output - mergedLayers, & ! intent(out): flag to denote that layers were merged - err,message) ! intent(out): error control - if(mergedLayers .and. tret(1)>50) exit - - endif ! checkSnow - end do ! while loop on one_step mode !****************************** End of Main Solver *************************************** - err = eqns_data%err message = eqns_data%message if( .not. feasible) idaSucceeds = .false. @@ -650,7 +573,6 @@ subroutine solveByIDA( & dt_out = tret(1) endif - ! free memory deallocate( eqns_data%sMul ) deallocate( eqns_data%dMat ) @@ -704,9 +626,6 @@ subroutine setInitialCondition(neq, y, sunvec_u, sunvec_up) real(c_double), pointer :: uu(:) real(c_double), pointer :: up(:) - - !======= Internals ============ - ! get data arrays from SUNDIALS vectors uu(1:neq) => FN_VGetArrayPointer(sunvec_u) up(1:neq) => FN_VGetArrayPointer(sunvec_up) @@ -722,21 +641,25 @@ end subroutine setInitialCondition subroutine setSolverParams(dt,ida_mem,retval) !======= Inclusions =========== USE, intrinsic :: iso_c_binding - USE fida_mod ! Fortran interface to IDA -implicit none - - real(rkind),intent(in) :: dt ! time step - type(c_ptr),intent(inout) :: ida_mem ! IDA memory - integer(i4b),intent(out) :: retval ! return value - - real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 - integer,parameter :: max_order = 1 ! maximum BDF order, default = 5 - integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 - integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 - integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 - integer(kind = 8),parameter :: max_step = 999999 ! maximum number of steps, dafault = 500 - real(qp),parameter :: h_init = 0 ! initial stepsize - real(qp) :: h_max ! maximum stepsize, dafault = infinity + USE fida_mod ! Fortran interface to IDA + + !======= Declarations ========= + implicit none + + ! calling variables + real(rkind),intent(in) :: dt ! time step + type(c_ptr),intent(inout) :: ida_mem ! IDA memory + integer(i4b),intent(out) :: retval ! return value + + !======= Internals ============ + real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 + integer,parameter :: max_order = 1 ! maximum BDF order, default = 5 + integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 + integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 + integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 + integer(kind = 8),parameter :: max_step = 999999 ! maximum number of steps, dafault = 500 + real(qp),parameter :: h_init = 0 ! initial stepsize + real(qp) :: h_max ! maximum stepsize, dafault = infinity ! Set the maximum BDF order retval = FIDASetMaxOrd(ida_mem, max_order) @@ -841,10 +764,3 @@ subroutine implctMelt(& end subroutine implctMelt end module solveByIDA_module - - - - - - - diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 5d795fe4a..c52fe7e87 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -54,7 +54,7 @@ module systemSolvSundials_module USE var_lookup,only:iLookPARAM ! named variables for structure elements USE var_lookup,only:iLookINDEX ! named variables for structure elements USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure - USE var_lookup,only:iLookDERIV ! named variables for structure elements +USE var_lookup,only:iLookDERIV ! named variables for structure elements ! provide access to the derived types to define the data structures USE data_types,only:& From 27e0ad01d6367de62ec3c3ea9c9698e33e90a622 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 9 Jun 2022 14:01:50 +0900 Subject: [PATCH 0301/1472] exit ida step if need to merge --- build/source/engine/solveByIDA.f90 | 19 +++++++++++-------- build/source/engine/systemSolvSundials.f90 | 7 +++++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 21230bb07..de8d36634 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -126,8 +126,9 @@ subroutine solveByIDA( & flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output - ixSaturation, & ! intent(out) + ixSaturation, & ! intent(inout) index of the lowest saturated layer (NOTE: only computed on the first iteration) idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step + tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix dt_out, & ! intent(out): time step stateVec, & ! intent(out): model state vector @@ -196,10 +197,11 @@ subroutine solveByIDA( & type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables real(rkind),intent(inout) :: mLayerCmpress_sum(:) ! sum of soil compress ! output: state vectors - integer(i4b),intent(out) :: ixSaturation ! index of the lowest saturated layer + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer real(rkind),intent(inout) :: stateVec(:) ! model state vector (y) real(rkind),intent(inout) :: stateVecPrime(:) ! model state vector (y') logical(lgt),intent(out) :: idaSucceeds ! flag to indicate if IDA is successful + logical(lgt),intent(inout) :: tooMuchMelt ! flag to denote that there was too much melt real(qp),intent(out) :: dt_out ! time step ! output: error control integer(i4b),intent(out) :: err ! error code @@ -230,7 +232,6 @@ subroutine solveByIDA( & integer(c_long) :: nState ! total number of state variables real(rkind) :: rVec(nStat) real(qp) :: tret(1) - logical(lgt) :: tooMuchMelt logical(lgt) :: mergedLayers logical(lgt),parameter :: offErrWarnMessage = .false. real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) @@ -451,11 +452,13 @@ subroutine solveByIDA( & exit endif - tooMuchMelt = .false. - ! loop through non-missing energy state variables in the snow domain to see if need to merge - ! CURRENTLY WILL ONLY MERGE TOP LAYER + tooMuchMelt = .false. + feasible = .true. + ! loop through non-missing energy state variables in the snow domain to see if need to merge + ! CURRENTLY WILL ONLY MERGE TOP LAYER do concurrent (i=1:nSnow,indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing) if (stateVec(indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)) > Tfreeze) tooMuchMelt = .true. !need to merge + if (stateVec(indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)) > Tfreeze) print*,i,tret(1),nSnow,idaSucceeds,"merge" end do if(tooMuchMelt)exit @@ -520,12 +523,12 @@ subroutine solveByIDA( & eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) - eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer + eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer ! output feasible, & ! intent(out): flag to denote the feasibility of the solution eqns_data%fluxVec, & ! intent(out): flux vector eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector + rVec, & ! intent(out): residual vector eqns_data%err,eqns_data%message) ! intent(out): error control diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index c52fe7e87..b5411af7c 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -170,7 +170,7 @@ subroutine systemSolvSundials(& real(rkind),intent(out) :: stateVecPrime(:) ! trial state vector (mixed units) logical(lgt),intent(out) :: reduceCoupledStep ! flag to reduce the length of the coupled step logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that there was too much melt - real(qp),intent(out) :: dt_out ! time step + real(qp),intent(out) :: dt_out ! time step integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! ********************************************************************************************************************************************************* @@ -553,7 +553,8 @@ subroutine systemSolvSundials(& deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step + idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step + tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix dt_out, & ! intent(out): time step stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step @@ -577,6 +578,8 @@ subroutine systemSolvSundials(& message=trim(message)//trim(cmessage) ! reduceCoupledStep = .true. return + else + if (tooMuchMelt) return !exit to start same step over after merge endif ! compute average flux From 73a98c968792ecf01ad81742291493bb0c0c26f9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 16 Jun 2022 14:52:24 +0900 Subject: [PATCH 0302/1472] removing constraints for now --- build/source/engine/coupled_em.f90 | 1 - build/source/engine/opSplittin.f90 | 28 +++++++++++++--------------- build/source/engine/solveByIDA.f90 | 14 +++++++------- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index ca31032ac..e01b6493d 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -635,7 +635,6 @@ subroutine coupled_em(& nSoil = indx_data%var(iLookINDEX%nSoil)%dat(1) nLayers = indx_data%var(iLookINDEX%nLayers)%dat(1) - ! compute the indices for the model state variables if(firstSubStep .or. modifiedVegState .or. modifiedLayers)then call indexState(computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index b0958e837..be480b67d 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -297,7 +297,7 @@ subroutine opSplittin(& logical(lgt) :: failedMinimumStep ! flag to denote failure of substepping for a given split integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) integer(i4b) :: nCoupling - real(qp) :: dt_out ! + real(qp) :: dt_out ! ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- @@ -362,13 +362,13 @@ subroutine opSplittin(& ! --------------------------------------------------------------------------------------- ! initialize error control err=0; message="opSplittin/" - + ! we just solve the fully coupled problem by ida select case(model_decisions(iLookDECISIONS%diffEqSolv)%iDecision) case(sundialIDA); nCoupling = 1 case(backwEuler); nCoupling = 2 - case default; err=20; message=trim(message)//'expect case to be sundialIDA or backwEuler'; return - end select + case default; err=20; message=trim(message)//'expect case to be sundialIDA or backwEuler'; return + end select ! ***** ! (0) PRELIMINARIES... @@ -771,7 +771,7 @@ subroutine opSplittin(& ! keep track of the number of scalar solutions if(ixSolution==scalar) numberScalarSolutions = numberScalarSolutions + 1 - + ! solve variable subset for one full time step select case(model_decisions(iLookDECISIONS%diffEqSolv)%iDecision) case(sundialIDA) @@ -811,7 +811,7 @@ subroutine opSplittin(& tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt dt_out, & ! intent(out) err,cmessage) ! intent(out) : error code and error message - case(backwEuler) + case(backwEuler) call varSubstep(& ! input: model control dt, & ! intent(inout) : time step (s) @@ -846,15 +846,13 @@ subroutine opSplittin(& failedMinimumStep, & ! intent(out) : flag for failed substeps reduceCoupledStep, & ! intent(out) : flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt - err,cmessage) ! intent(out) : error code and error message + err,cmessage) ! intent(out) : error code and error message ! check - case default; err=20; message=trim(message)//'expect case to backwEuler or sundialIDA'; return - end select - - dt = dt_out - - - + case default; err=20; message=trim(message)//'expect case to backwEuler or sundialIDA'; return + end select + + dt = dt_out + if(err/=0)then message=trim(message)//trim(cmessage) if(err>0) return @@ -1035,7 +1033,7 @@ subroutine opSplittin(& diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(1:nSnow) = -( mLayerVolFracIce(1:nSnow) - mLayerVolFracIceInit(1:nSnow) ) * iden_ice diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(nSnow+1:nLayers) = -(mLayerVolFracIce(nSnow+1:nLayers) - mLayerVolFracIceInit(nSnow+1:nLayers))*iden_water endif - + ! end associate statements end associate globalVars diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index de8d36634..e01b22786 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -154,7 +154,6 @@ subroutine solveByIDA( & USE tol4IDA_module,only:computWeight4IDA ! weigth required for tolerances USE eval8DAE_module,only:eval8DAE ! residual of DAE USE var_derive_module,only:calcHeight ! height at layer interfaces and layer mid-point - USE layerMerge_module,only:layerMerge ! merge snow layers if go above freezing (too thin) !======= Declarations ========= implicit none @@ -325,6 +324,7 @@ subroutine solveByIDA( & allocate( eqns_data%fluxVec(nState) ) allocate( eqns_data%resSink(nState) ) + startQuadrature = .true. ! create serial vectors sunvec_y => FN_VMake_Serial(nState, stateVec) @@ -333,11 +333,11 @@ subroutine solveByIDA( & sunvec_yp => FN_VMake_Serial(nState, stateVecPrime) if (.not. associated(sunvec_yp)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif - sunvec_c => FN_VMake_Serial(nState, stateVecConstraints) - if (.not. associated(sunvec_c)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif + !sunvec_c => FN_VMake_Serial(nState, stateVecConstraints) + !if (.not. associated(sunvec_c)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif - sunvec_cv => FN_VMake_Serial(nState, stateVecConstValues) - if (.not. associated(sunvec_cv)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif + !sunvec_cv => FN_VMake_Serial(nState, stateVecConstValues) + !if (.not. associated(sunvec_cv)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif ! Initialize solution vectors call setInitialCondition(nState, stateVecInit, sunvec_y, sunvec_yp) @@ -601,8 +601,8 @@ subroutine solveByIDA( & call FSUNMatDestroy(sunmat_A) call FN_VDestroy(sunvec_y) call FN_VDestroy(sunvec_yp) - call FN_VDestroy(sunvec_c) - call FN_VDestroy(sunvec_cv) + !call FN_VDestroy(sunvec_c) + !call FN_VDestroy(sunvec_cv) end subroutine solveByIDA From 213648b853b6531f227f49721e83b5cd9204a623 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 17 Jun 2022 11:28:02 +0900 Subject: [PATCH 0303/1472] need /=integerMissing statements in indexing or segfaults --- build/source/engine/systemSolvSundials.f90 | 18 ++++++--- build/source/engine/tol4IDA.f90 | 44 +++++++++++----------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index b5411af7c..5fd506c3b 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -459,12 +459,18 @@ subroutine systemSolvSundials(& !-1 then yi will be constrained to be yi<=0 stateVecConstraints = 0._rkind stateVecConstValues = 0._rkind - stateVecConstraints(ixCasNrg) = -1._rkind !canopy air space temperature cannot be above canopyTempMax - stateVecConstraints(ixVegNrg) = -1._rkind !canopy temperature cannot be above canopyTempMax - stateVecConstraints(ixVegHyd) = 1._rkind !canopy liquid water cannot be below bound of 0 - stateVecConstValues(ixCasNrg) = canopyTempMax !canopy air space temperature cannot be above canopyTempMax - stateVecConstValues(ixVegNrg) = canopyTempMax !canopy temperature cannot be above canopyTempMax - stateVecConstValues(ixVegHyd) = 0._rkind !canopy liquid water cannot be below 0 + if(ixCasNrg/=integerMissing)then !canopy air space temperature cannot be above canopyTempMax + stateVecConstraints(ixCasNrg) = -1._rkind + stateVecConstValues(ixCasNrg) = canopyTempMax + endif + if(ixVegNrg/=integerMissing)then !canopy temperature cannot be above canopyTempMax + stateVecConstraints(ixVegNrg) = -1._rkind + stateVecConstValues(ixVegNrg) = canopyTempMax + endif + if(ixVegHyd/=integerMissing)then !canopy liquid water cannot be below 0 + stateVecConstraints(ixVegHyd) = 1._rkind + stateVecConstValues(ixVegHyd) = 0._rkind + endif ! loop through non-missing energy state variables in the snow domain do concurrent (iLayer=1:nSnow,ixSnowOnlyNrg(iLayer)/=integerMissing) diff --git a/build/source/engine/tol4IDA.f90 b/build/source/engine/tol4IDA.f90 index a5f78c92a..4674c31ef 100644 --- a/build/source/engine/tol4IDA.f90 +++ b/build/source/engine/tol4IDA.f90 @@ -8,7 +8,7 @@ module tol4IDA_module use, intrinsic :: iso_c_binding use nrtype use type4IDA - + ! missing values USE globalData,only:integerMissing ! missing integer USE globalData,only:realMissing ! missing real number @@ -60,7 +60,7 @@ module tol4IDA_module USE var_lookup,only:iLookDERIV ! named variables for structure elements USE var_lookup,only:iLookPARAM ! named variables for structure elements USE var_lookup,only:iLookINDEX ! named variables for structure elements - + ! privacy implicit none @@ -72,7 +72,7 @@ module tol4IDA_module contains ! ********************************************************************************************************** - ! public function computWeight4IDA: compute w_i = 1 / ( rtol_i * y_i + atol_i ) + ! public function computWeight4IDA: compute w_i = 1 / ( rtol_i * y_i + atol_i ) ! ********************************************************************************************************** ! Return values: ! 0 = success, @@ -91,40 +91,40 @@ integer(c_int) function computWeight4IDA(sunvec_y, sunvec_ewt, user_data) & !======= Declarations ========= implicit none - ! calling variables + ! calling variables type(N_Vector) :: sunvec_y ! solution N_Vector y type(N_Vector) :: sunvec_ewt ! derivative N_Vector W - type(c_ptr), value :: user_data ! user-defined data + type(c_ptr), value :: user_data ! user-defined data ! pointers to data in SUNDIALS vectors type(eqnsData), pointer :: tol_data ! equations data real(rkind), pointer :: stateVec(:) - real(rkind), pointer :: weightVec(:) + real(rkind), pointer :: weightVec(:) integer(c_int) :: iState !======= Internals ============ - + ! get equations data from user-defined data call c_f_pointer(user_data, tol_data) - - + + ! get data arrays from SUNDIALS vectors - stateVec => FN_VGetArrayPointer(sunvec_y) - weightVec => FN_VGetArrayPointer(sunvec_ewt) - - + stateVec(1:tol_data%nState) => FN_VGetArrayPointer(sunvec_y) + weightVec(1:tol_data%nState) => FN_VGetArrayPointer(sunvec_ewt) + + do iState = 1,tol_data%nState weightVec(iState) = tol_data%rtol(iState) * abs( stateVec(iState) ) + tol_data%atol(iState) - weightVec(iState) = 1._rkind / weightVec(iState) - end do - + weightVec(iState) = 1._rkind / weightVec(iState) + end do + ierr = 0 return end function computWeight4IDA - - + + ! ********************************************************************************************************** ! public subroutine popTol4IDA: populate tolerances for state vectors ! ********************************************************************************************************** @@ -238,7 +238,7 @@ subroutine popTol4IDA(& do concurrent (iState=1:size(ixVegHyd),ixVegHyd(iState)/=integerMissing) tolFlag( ixVegHyd(iState) ) = .true. ! flag to denote that tolerances are populated select case(ixStateType_subset( ixVegHyd(iState) )) - case(iname_watCanopy); absTol( ixVegHyd(iState) ) = absTolWatVeg ; relTol( ixVegHyd(iState) ) = relTolWatVeg + case(iname_watCanopy); absTol( ixVegHyd(iState) ) = absTolWatVeg ; relTol( ixVegHyd(iState) ) = relTolWatVeg case(iname_liqCanopy); absTol( ixVegHyd(iState) ) = absTolWatVeg ; relTol( ixVegHyd(iState) ) = relTolWatVeg ! transfer liquid canopy water to the state vector case default; tolFlag( ixVegHyd(iState) ) = .false. ! flag to denote that tolerances are populated end select @@ -260,9 +260,9 @@ subroutine popTol4IDA(& ixStateSubset = ixSnowSoilHyd(iLayer) ! index within the state vector tolFlag(ixStateSubset) = .true. ! flag to denote that tolerances are populated select case( ixHydType(iLayer) ) - case(iname_watLayer); absTol(ixStateSubset) = absTolWatSnow ; relTol(ixStateSubset) = relTolWatSnow - case(iname_liqLayer); absTol(ixStateSubset) = absTolWatSnow ; relTol(ixStateSubset) = relTolWatSnow - case(iname_matLayer); absTol(ixStateSubset) = absTolMatric ; relTol(ixStateSubset) = relTolMatric + case(iname_watLayer); absTol(ixStateSubset) = absTolWatSnow ; relTol(ixStateSubset) = relTolWatSnow + case(iname_liqLayer); absTol(ixStateSubset) = absTolWatSnow ; relTol(ixStateSubset) = relTolWatSnow + case(iname_matLayer); absTol(ixStateSubset) = absTolMatric ; relTol(ixStateSubset) = relTolMatric case(iname_lmpLayer); absTol(ixStateSubset) = absTolMatric ; relTol(ixStateSubset) = relTolMatric case default; tolFlag(ixStateSubset) = .false. ! flag to denote that tolerances are populated end select From eba47a264e32ec4178c3054541df445ac8691a4c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 5 Jul 2022 23:36:55 +0900 Subject: [PATCH 0304/1472] If go over freezing soil threshold in dt, will not do a fancy derivative for icePrime that considers it being 0 on one side and positive on the other. This was always true in the Sundials steps, but now true outside too. It seems to cause problems if you do the fancy derivative, I am not yet sure why. --- build/source/engine/eval8DAE.f90 | 1 - build/source/engine/opSplittin.f90 | 3 +- build/source/engine/solveByIDA.f90 | 13 +++---- build/source/engine/systemSolvSundials.f90 | 4 ++- build/source/engine/updatStateSundials.f90 | 40 ++++------------------ build/source/engine/updateVarsSundials.f90 | 3 -- build/source/engine/varSubstepSundials.f90 | 1 - 7 files changed, 18 insertions(+), 47 deletions(-) diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index 754e89b6d..9b8f6676f 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -466,7 +466,6 @@ subroutine eval8DAE(& mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU - mLayerTempPrev, & ! intent(in) mLayerVolFracWatPrev, & ! intent(in) mLayerMatricHeadPrev, & ! intent(in) diag_data, & ! intent(inout): model diagnostic variables for a local HRU diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index be480b67d..a6bcf9f31 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -297,7 +297,7 @@ subroutine opSplittin(& logical(lgt) :: failedMinimumStep ! flag to denote failure of substepping for a given split integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) integer(i4b) :: nCoupling - real(qp) :: dt_out ! + real(qp) :: dt_out ! ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- @@ -642,6 +642,7 @@ subroutine opSplittin(& ! domain splitting else + print*,"split domain" ! initialize to .false. fluxMask%var(iVar)%dat = .false. diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index e01b22786..0ec635633 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -346,10 +346,6 @@ subroutine solveByIDA( & ida_mem = FIDACreate() if (.not. c_associated(ida_mem)) then; err=20; message='solveByIDA: ida_mem = NULL'; return; endif - ! Set constraints - !retval = FIDASetConstraints(ida_mem, sunvec_c, sunvec_cv) !uncomment this line to use feasibility constraints - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetConstraints'; return; endif - ! Attach user data to memory eqns_data%ida_mem = ida_mem retval = FIDASetUserData(ida_mem, c_loc(eqns_data)) @@ -394,9 +390,14 @@ subroutine solveByIDA( & retval = FIDASetLinearSolver(ida_mem, sunlinsol_LS, sunmat_A); if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetLinearSolver'; return; endif + ! Set constraints + !retval = FIDASetConstraints(ida_mem, sunvec_c, sunvec_cv) !uncomment this line to use feasibility constraints + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetConstraints'; return; endif + if(ixMatrix == ixFullMatrix)then - ! Set the user-supplied Jacobian routine - retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) !comment this line out to use FD Jacobian + ! Set the user-supplied Jacobian routine + !comment this line out to use FD Jacobian, currently cannot do with constraint version of IDA, memory size error + retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetJacFn'; return; endif endif diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 5fd506c3b..75e3ea72b 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -249,6 +249,7 @@ subroutine systemSolvSundials(& ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) @@ -507,7 +508,8 @@ subroutine systemSolvSundials(& !do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! minimum temperature constraints ! xMin = 100._rkind !degrees C - ! stateVecConstraints(ixSnowSoilNrg(iLayer)) - xMin = 1._rkind !water cannot be less than xMin + ! stateVecConstraints(ixSnowSoilNrg(iLayer)) = 1._rkind !temp cannot be less than xMin + ! stateVecConstraints(ixSnowSoilNrg(iLayer)) = xMin !temp cannot be less than xMin !enddo !------------------- diff --git a/build/source/engine/updatStateSundials.f90 b/build/source/engine/updatStateSundials.f90 index a8681a854..51a3ead2a 100644 --- a/build/source/engine/updatStateSundials.f90 +++ b/build/source/engine/updatStateSundials.f90 @@ -130,7 +130,6 @@ subroutine updateSoilSundials(& ! input dt_cur, & mLayerTemp ,& ! intent(in): temperature (K) - mLayerTempPrev ,& ! intent(in): temperature previous time step (K) mLayerMatricHead ,& ! intent(in): total water matric potential (m) mLayerMatricHeadPrev ,& ! intent(in): total water matric potential previous time step (m) mLayerVolFracWatPrev ,& ! intent(in): volumetric fraction of total water previous time step (-) @@ -157,7 +156,6 @@ subroutine updateSoilSundials(& ! input variables real(rkind),intent(in) :: dt_cur real(rkind),intent(in) :: mLayerTemp ! estimate of temperature (K) - real(rkind),intent(in) :: mLayerTempPrev ! temperature previous time step (K) real(rkind),intent(in) :: mLayerMatricHead ! matric head (m) real(rkind),intent(in) :: mLayerMatricHeadPrev ! matric head previous time step (m) real(rkind),intent(in) :: mLayerVolFracWatPrev ! volumetric fraction of total waterprevious time step (m) @@ -179,13 +177,9 @@ subroutine updateSoilSundials(& character(*),intent(out) :: message ! error message ! define local variables real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) - real(rkind) :: TcSoilPrev ! previous timestep critical soil temperature when all water is unfrozen (K) real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) real(rkind) :: dt_inv ! inverse of timestep - !real(rkind) :: mLayerTempPrev ! estimate of previous timestep temperature (K) - real(rkind) :: mLayerVolFracLiqPrev ! previous timestep volumetric fraction of liquid water (-) - real(rkind) :: dt ! initialize error control err=0; message="updateSoilSundials/" @@ -194,10 +188,8 @@ subroutine updateSoilSundials(& ! mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime if( abs(mLayerMatricHead - mLayerMatricHeadPrev) < verySmall )then dt_inv = 1._rkind/ dt_cur !WHY NOT JUST USE THIS - dt = dt_cur else dt_inv = mLayerMatricHeadPrime / (mLayerMatricHead - mLayerMatricHeadPrev) - dt = 1._rkind/ dt_inv endif mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev)*dt_inv @@ -206,7 +198,6 @@ subroutine updateSoilSundials(& ! compute the critical soil temperature where all water is unfrozen (K) ! (eq 17 in Dall'Amico 2011) TcSoil = Tfreeze + min(mLayerMatricHead,0._rkind)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) - TcSoilPrev = Tfreeze + min(mLayerMatricHeadPrev,0._rkind)*gravity*Tfreeze/LH_fus ! *** compute volumetric fraction of liquid water for partially frozen soil if(mLayerTemp < TcSoil)then ! (check if soil temperature is less than the critical temperature) @@ -215,33 +206,17 @@ subroutine updateSoilSundials(& xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - !if( mLayerTemp - mLayerTempPrime*dt < TcSoilPrev )then - if( mLayerTempPrev < TcSoilPrev )then - if(mLayerPsiLiq<0._rkind)then - mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime - else - mLayerVolFracLiqPrime = 0._rkind - endif - else ! was unfrozen on previous time step - mLayerVolFracLiqPrev = mLayerVolFracWatPrev - ! using mLayerVolFracLiqPrev = mLayerVolFracWatPrev - mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracWatPrev)*dt_inv ! = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv + if(mLayerPsiLiq<0._rkind)then + mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime + else + mLayerVolFracLiqPrime = 0._rkind endif ! *** compute volumetric fraction of liquid water for unfrozen soil else !( mLayerTemp >= TcSoil, all water is unfrozen ) mLayerVolFracLiq = mLayerVolFracWat - !if ( mLayerTemp - mLayerTempPrime*dt >= TcSoilPrev )then - if ( mLayerTempPrev >= TcSoilPrev )then - mLayerVolFracLiqPrime = mLayerVolFracWatPrime - mLayerVolFracIcePrime = 0._rkind - else ! was partially frozen on previous time step - !mLayerTempPrev = mLayerTemp - mLayerTempPrime*dt - xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) - mLayerPsiLiq = xConst*(mLayerTempPrev - Tfreeze) ! liquid water matric potential from the Clapeyron eqution - mLayerVolFracLiqPrev = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - mLayerVolFracLiqPrime = (mLayerVolFracLiq - mLayerVolFracLiqPrev)*dt_inv - endif + mLayerVolFracLiqPrime = mLayerVolFracWatPrime + mLayerVolFracIcePrime = 0._rkind end if ! (check if soil is partially frozen) @@ -304,14 +279,11 @@ subroutine updateSoilSundials2(& real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) - real(rkind) :: dt_inv ! inverse of timestep - real(rkind) :: mLayerTempPrev ! estimate of previous timestep temperature (K) ! initialize error control err=0; message="updateSoilSundials2/" ! compute fractional **volume** of total water (liquid plus ice) mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - dt_inv = 1._rkind/ dt_cur mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime if(mLayerVolFracWat > theta_sat)then; err=20; message=trim(message)//'volume of liquid and ice exceeds porosity'; return; end if diff --git a/build/source/engine/updateVarsSundials.f90 b/build/source/engine/updateVarsSundials.f90 index e6285c2c4..35a7d4cc7 100644 --- a/build/source/engine/updateVarsSundials.f90 +++ b/build/source/engine/updateVarsSundials.f90 @@ -110,7 +110,6 @@ subroutine updateVarsSundials(& mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU - mLayerTempPrev, & ! intent(in) mLayerVolFracWatPrev, & ! intent(in) mLayerMatricHeadPrev, & ! intent(in) diag_data, & ! intent(inout): model diagnostic variables for a local HRU @@ -148,7 +147,6 @@ subroutine updateVarsSundials(& type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - real(rkind),intent(in) :: mLayerTempPrev(:) real(rkind),intent(in) :: mLayerVolFracWatPrev(:) real(rkind),intent(in) :: mLayerMatricHeadPrev(:) type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU @@ -540,7 +538,6 @@ subroutine updateVarsSundials(& call updateSoilSundials(& dt_cur, & xTemp, & ! intent(in) : temperature (K) - mLayerTempPrev(iLayer), & ! intent(in) : temperature previous time step (K) mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) mLayerMatricHeadPrev(ixControlIndex), & ! intent(in) mLayerVolFracWatPrev(iLayer), & ! intent(in) diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index b1892b8aa..9bb02588e 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -750,7 +750,6 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU - mLayerTempTrial, & mLayerVolFracWatTrial, & mLayerMatricHeadTrial, & diag_data, & ! intent(inout): model diagnostic variables for a local HRU From 6211201c5f9a6f3ca2b9a5c34122baa2849a8a9d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 12 Jul 2022 21:40:15 +0900 Subject: [PATCH 0305/1472] Added event detection and restart if any soil layer crosses freezing. Not sure if this is correct yet, and if I need to stop for every layer (maybe just the top layer). --- build/source/engine/solveByIDA.f90 | 902 ++++++++++++++++------------- sundials/installation.txt | 2 +- 2 files changed, 495 insertions(+), 409 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 0ec635633..8d6734db6 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -74,19 +74,15 @@ module solveByIDA_module var_dlength, & ! data vector with variable length dimension (rkind) zLookup ! data vector - ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: qbaseTopmodel ! TOPMODEL-ish baseflow parameterization - - ! privacy implicit none private::setInitialCondition private::setSolverParams public::solveByIDA - contains !------------------- @@ -119,44 +115,44 @@ subroutine solveByIDA( & bvar_data, & ! intent(in): average model variables for the entire basin prog_data, & ! intent(in): model prognostic variables for a local HRU ! input-output: data structures - indx_data, & ! intent(in): index data + indx_data, & ! intent(in): index data diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_temp, & ! intent(inout): model fluxes for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output - ixSaturation, & ! intent(inout) index of the lowest saturated layer (NOTE: only computed on the first iteration) - idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step - tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt + ixSaturation, & ! intent(inout) index of the lowest saturated layer (NOTE: only computed on the first iteration) + idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step + tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix - dt_out, & ! intent(out): time step + dt_out, & ! intent(out): time step stateVec, & ! intent(out): model state vector stateVecPrime, & ! intent(out): derivative of model state vector err,message & ! intent(out): error control ) - !======= Inclusions =========== - USE fida_mod ! Fortran interface to IDA - USE fnvector_serial_mod ! Fortran interface to serial N_Vector - USE fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix - USE fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver - USE fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix - USE fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver - USE fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver - USE fsundials_matrix_mod ! Fortran interface to generic SUNMatrix - USE fsundials_nvector_mod ! Fortran interface to generic N_Vector - USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver - USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver - USE allocspace_module,only:allocLocal ! allocate local data structures - USE evalDAE4IDA_module,only:evalDAE4IDA ! DAE/ODE functions - USE evalJac4IDA_module,only:evalJac4IDA ! system Jacobian - USE tol4IDA_module,only:computWeight4IDA ! weigth required for tolerances - USE eval8DAE_module,only:eval8DAE ! residual of DAE - USE var_derive_module,only:calcHeight ! height at layer interfaces and layer mid-point - - !======= Declarations ========= - implicit none + !======= Inclusions =========== + USE fida_mod ! Fortran interface to IDA + USE fnvector_serial_mod ! Fortran interface to serial N_Vector + USE fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix + USE fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver + USE fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix + USE fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver + USE fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver + USE fsundials_matrix_mod ! Fortran interface to generic SUNMatrix + USE fsundials_nvector_mod ! Fortran interface to generic N_Vector + USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver + USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver + USE allocspace_module,only:allocLocal ! allocate local data structures + USE evalDAE4IDA_module,only:evalDAE4IDA ! DAE/ODE functions + USE evalJac4IDA_module,only:evalJac4IDA ! system Jacobian + USE tol4IDA_module,only:computWeight4IDA ! weigth required for tolerances + USE eval8DAE_module,only:eval8DAE ! residual of DAE + USE var_derive_module,only:calcHeight ! height at layer interfaces and layer mid-point + + !======= Declarations ========= + implicit none ! -------------------------------------------------------------------------------------------------------------------------------- ! calling variables @@ -168,7 +164,7 @@ subroutine solveByIDA( & integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers - integer(i4b),intent(in) :: nStat ! total number of state variables + integer(i4b),intent(in) :: nStat ! total number of state variables integer(i4b),intent(in) :: ixMatrix ! form of matrix (dense or banded) logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation @@ -177,7 +173,7 @@ subroutine solveByIDA( & real(rkind),intent(in) :: stateVecInit(:) ! model state vector real(rkind),intent(inout) :: stateVecConstraints(:) ! model state vector constraints real(rkind),intent(inout) :: stateVecConstValues(:) ! model state vector constraint values - real(qp),intent(in) :: sMul(:) ! state vector multiplier (used in the residual calculations) + real(qp),intent(in) :: sMul(:) ! state vector multiplier (used in the residual calculations) real(rkind), intent(inout) :: dMat(:) ! input: data structures type(zLookup),intent(in) :: lookup_data ! lookup tables @@ -187,7 +183,7 @@ subroutine solveByIDA( & type(var_d), intent(in) :: forc_data ! model forcing data type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_ilength), intent(in) :: indx_data ! indices defining model states and layers + type(var_ilength), intent(in) :: indx_data ! indices defining model states and layers ! input-output: data structures type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: flux_temp ! model fluxes for a local HRU @@ -196,12 +192,12 @@ subroutine solveByIDA( & type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables real(rkind),intent(inout) :: mLayerCmpress_sum(:) ! sum of soil compress ! output: state vectors - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer real(rkind),intent(inout) :: stateVec(:) ! model state vector (y) real(rkind),intent(inout) :: stateVecPrime(:) ! model state vector (y') - logical(lgt),intent(out) :: idaSucceeds ! flag to indicate if IDA is successful + logical(lgt),intent(out) :: idaSucceeds ! flag to indicate if IDA is successful logical(lgt),intent(inout) :: tooMuchMelt ! flag to denote that there was too much melt - real(qp),intent(out) :: dt_out ! time step + real(qp),intent(out) :: dt_out ! time step ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -209,62 +205,64 @@ subroutine solveByIDA( & ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- - type(N_Vector), pointer :: sunvec_y ! sundials solution vector - type(N_Vector), pointer :: sunvec_yp ! sundials derivative vector - type(N_Vector), pointer :: sunvec_c ! sundials constraints vector - type(N_Vector), pointer :: sunvec_cv ! sundials constraints vector of values - type(N_Vector), pointer :: sunvec_av ! sundials tolerance vector - type(SUNMatrix), pointer :: sunmat_A ! sundials matrix - type(SUNLinearSolver), pointer :: sunlinsol_LS ! sundials linear solver - type(SUNNonLinearSolver), pointer :: sunnonlin_NLS ! sundials nonlinear solver - type(c_ptr) :: ida_mem ! IDA memory - type(eqnsData), target :: eqns_data ! IDA type - integer(i4b) :: retval ! return value - logical(lgt) :: feasible ! feasibility flag - real(qp) :: t0 ! staring time - real(qp) :: dt_last(1) ! last time step - integer(kind = 8) :: mu, lu ! in banded matrix mode - integer(i4b) :: iVar - logical(lgt) :: startQuadrature - real(rkind) :: mLayerMatricHeadLiqPrev(nSoil) - real(qp) :: h_init - integer(c_long) :: nState ! total number of state variables - real(rkind) :: rVec(nStat) - real(qp) :: tret(1) - logical(lgt) :: mergedLayers - logical(lgt),parameter :: offErrWarnMessage = .false. - real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) - real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) - integer(i4b) :: i - ! ----------------------------------------------------------------------------------------------------- - - ! initialize error control - err=0; message="solveByIDA/" - - nState = nStat - idaSucceeds = .true. - ! fill eqns_data which will be required later to call eval8DAE - eqns_data%dt = dt - eqns_data%nSnow = nSnow - eqns_data%nSoil = nSoil - eqns_data%nLayers = nLayers - eqns_data%nState = nState - eqns_data%ixMatrix = ixMatrix - eqns_data%firstSubStep = firstSubStep - eqns_data%computeVegFlux = computeVegFlux - eqns_data%scalarSolution = scalarSolution - - allocate( eqns_data%atol(nState) ) - eqns_data%atol = atol - - allocate( eqns_data%rtol(nState) ) - eqns_data%rtol = rtol - - allocate( eqns_data%sMul(nState) ) - eqns_data%sMul = sMul - - allocate( eqns_data%dMat(nState) ) - eqns_data%dMat = dMat + type(N_Vector), pointer :: sunvec_y ! sundials solution vector + type(N_Vector), pointer :: sunvec_yp ! sundials derivative vector + type(N_Vector), pointer :: sunvec_c ! sundials constraints vector + type(N_Vector), pointer :: sunvec_cv ! sundials constraints vector of values + type(N_Vector), pointer :: sunvec_av ! sundials tolerance vector + type(SUNMatrix), pointer :: sunmat_A ! sundials matrix + type(SUNLinearSolver), pointer :: sunlinsol_LS ! sundials linear solver + type(SUNNonLinearSolver), pointer :: sunnonlin_NLS ! sundials nonlinear solver + type(c_ptr) :: ida_mem ! IDA memory + type(eqnsData), target :: eqns_data ! IDA type + integer(i4b) :: retval, retvalr ! return value + integer(i4b) :: rootsfound(nSoil) ! point where mLayerTemp = freezing in each soil layer + logical(lgt) :: feasible ! feasibility flag + real(qp) :: t0 ! staring time + real(qp) :: dt_last(1) ! last time step + integer(kind = 8) :: mu, lu ! in banded matrix mode + integer(i4b) :: iVar + logical(lgt) :: startQuadrature + real(rkind) :: mLayerMatricHeadLiqPrev(nSoil) + real(qp) :: h_init + integer(c_long) :: nState ! total number of state variables + real(rkind) :: rVec(nStat) + real(qp) :: tret(1) + logical(lgt) :: mergedLayers + logical(lgt),parameter :: offErrWarnMessage = .false. + real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) + real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) + integer(i4b) :: i + + ! ----------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message="solveByIDA/" + + nState = nStat + idaSucceeds = .true. + ! fill eqns_data which will be required later to call eval8DAE + eqns_data%dt = dt + eqns_data%nSnow = nSnow + eqns_data%nSoil = nSoil + eqns_data%nLayers = nLayers + eqns_data%nState = nState + eqns_data%ixMatrix = ixMatrix + eqns_data%firstSubStep = firstSubStep + eqns_data%computeVegFlux = computeVegFlux + eqns_data%scalarSolution = scalarSolution + + allocate( eqns_data%atol(nState) ) + eqns_data%atol = atol + + allocate( eqns_data%rtol(nState) ) + eqns_data%rtol = rtol + + allocate( eqns_data%sMul(nState) ) + eqns_data%sMul = sMul + + allocate( eqns_data%dMat(nState) ) + eqns_data%dMat = dMat ! allocate space for the temporary prognostic variable structure call allocLocal(prog_meta(:),eqns_data%prog_data,nSnow,nSoil,err,message) @@ -286,13 +284,13 @@ subroutine solveByIDA( & if(err/=0)then; err=20; message=trim(message)//trim(message); return; end if eqns_data%deriv_data = deriv_data - eqns_data%lookup_data = lookup_data - eqns_data%type_data = type_data - eqns_data%attr_data = attr_data - eqns_data%mpar_data = mpar_data - eqns_data%forc_data = forc_data - eqns_data%bvar_data = bvar_data - eqns_data%indx_data = indx_data + eqns_data%lookup_data = lookup_data + eqns_data%type_data = type_data + eqns_data%attr_data = attr_data + eqns_data%mpar_data = mpar_data + eqns_data%forc_data = forc_data + eqns_data%bvar_data = bvar_data + eqns_data%indx_data = indx_data ! allocate space if(model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel)then @@ -300,153 +298,152 @@ subroutine solveByIDA( & else allocate(eqns_data%dBaseflow_dMatric(0,0),stat=err) end if - - allocate( eqns_data%mLayerMatricHeadLiqTrial(nSoil) ) - - allocate( eqns_data%mLayerMatricHeadTrial(nSoil) ) - allocate( eqns_data%mLayerMatricHeadPrev(nSoil) ) - - allocate( eqns_data%mLayerVolFracWatTrial(nLayers) ) - allocate( eqns_data%mLayerVolFracWatPrev(nLayers) ) - - allocate( eqns_data%mLayerTempTrial(nLayers) ) - allocate( eqns_data%mLayerTempPrev(nLayers) ) - - allocate( eqns_data%mLayerVolFracIceTrial(nLayers) ) - allocate( eqns_data%mLayerVolFracIcePrev(nLayers) ) - - allocate( eqns_data%mLayerVolFracLiqTrial(nLayers) ) - allocate( eqns_data%mLayerVolFracLiqPrev(nLayers) ) - - allocate( eqns_data%mLayerEnthalpyTrial(nLayers) ) - allocate( eqns_data%mLayerEnthalpyPrev(nLayers) ) - - allocate( eqns_data%fluxVec(nState) ) - allocate( eqns_data%resSink(nState) ) - - startQuadrature = .true. - - ! create serial vectors - sunvec_y => FN_VMake_Serial(nState, stateVec) - if (.not. associated(sunvec_y)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif - - sunvec_yp => FN_VMake_Serial(nState, stateVecPrime) - if (.not. associated(sunvec_yp)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif - - !sunvec_c => FN_VMake_Serial(nState, stateVecConstraints) - !if (.not. associated(sunvec_c)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif - - !sunvec_cv => FN_VMake_Serial(nState, stateVecConstValues) - !if (.not. associated(sunvec_cv)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif - - ! Initialize solution vectors - call setInitialCondition(nState, stateVecInit, sunvec_y, sunvec_yp) - - ! Create memory - ida_mem = FIDACreate() - if (.not. c_associated(ida_mem)) then; err=20; message='solveByIDA: ida_mem = NULL'; return; endif - - ! Attach user data to memory - eqns_data%ida_mem = ida_mem - retval = FIDASetUserData(ida_mem, c_loc(eqns_data)) - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetUserData'; return; endif - - ! Initialize memory - t0 = 0._rkind - retval = FIDAInit(ida_mem, c_funloc(evalDAE4IDA), t0, sunvec_y, sunvec_yp) - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAInit'; return; endif - - ! set tolerances - retval = FIDAWFtolerances(ida_mem, c_funloc(computWeight4IDA)) - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAWFtolerances'; return; endif - - ! define the form of the matrix - select case(ixMatrix) - case(ixBandMatrix) - mu = ku; lu = kl; - ! Create banded SUNMatrix for use in linear solves - sunmat_A => FSUNBandMatrix(nState, mu, lu) - if (.not. associated(sunmat_A)) then; err=20; message='solveByIDA: sunmat = NULL'; return; endif - - ! Create banded SUNLinearSolver object - sunlinsol_LS => FSUNLinSol_Band(sunvec_y, sunmat_A) - if (.not. associated(sunlinsol_LS)) then; err=20; message='solveByIDA: sunlinsol = NULL'; return; endif - - case(ixFullMatrix) - ! Create dense SUNMatrix for use in linear solves - sunmat_A => FSUNDenseMatrix(nState, nState) - if (.not. associated(sunmat_A)) then; err=20; message='solveByIDA: sunmat = NULL'; return; endif - - ! Create dense SUNLinearSolver object - sunlinsol_LS => FSUNDenseLinearSolver(sunvec_y, sunmat_A) - if (.not. associated(sunlinsol_LS)) then; err=20; message='solveByIDA: sunlinsol = NULL'; return; endif - - ! check - case default; err=20; message='solveByIDA: error in type of matrix'; return - - end select ! form of matrix - - ! Attach the matrix and linear solver - retval = FIDASetLinearSolver(ida_mem, sunlinsol_LS, sunmat_A); - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetLinearSolver'; return; endif - - ! Set constraints - !retval = FIDASetConstraints(ida_mem, sunvec_c, sunvec_cv) !uncomment this line to use feasibility constraints - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetConstraints'; return; endif - - if(ixMatrix == ixFullMatrix)then - ! Set the user-supplied Jacobian routine - !comment this line out to use FD Jacobian, currently cannot do with constraint version of IDA, memory size error - retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetJacFn'; return; endif - endif - - ! Create Newton SUNNonlinearSolver object - sunnonlin_NLS => FSUNNonlinSol_Newton(sunvec_y) - if (.not. associated(sunnonlin_NLS)) then; err=20; message='solveByIDA: sunnonlinsol = NULL'; return; endif - - ! Attach the nonlinear solver - retval = FIDASetNonlinearSolver(ida_mem, sunnonlin_NLS) - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetNonlinearSolver'; return; endif - - ! Enforce the solver to stop at the end of the data time step - retval = FIDASetStopTime(ida_mem, dt) - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetStopTime'; return; endif - - ! Set solver parameters such as maximum order, number of iterations, ... - call setSolverParams(dt, ida_mem, retval) - if (retval /= 0) then; err=20; message='solveByIDA: error in setSolverParams'; return; endif - - ! Disable error messages and warnings - if(offErrWarnMessage) then - retval = FIDASetErrFile(ida_mem, c_null_ptr) - retval = FIDASetNoInactiveRootWarn(ida_mem) - endif - - ! need the following values for the first substep - eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) + allocate( eqns_data%mLayerMatricHeadLiqTrial(nSoil) ) + allocate( eqns_data%mLayerMatricHeadTrial(nSoil) ) + allocate( eqns_data%mLayerMatricHeadPrev(nSoil) ) + allocate( eqns_data%mLayerVolFracWatTrial(nLayers) ) + allocate( eqns_data%mLayerVolFracWatPrev(nLayers) ) + allocate( eqns_data%mLayerTempTrial(nLayers) ) + allocate( eqns_data%mLayerTempPrev(nLayers) ) + allocate( eqns_data%mLayerVolFracIceTrial(nLayers) ) + allocate( eqns_data%mLayerVolFracIcePrev(nLayers) ) + allocate( eqns_data%mLayerVolFracLiqTrial(nLayers) ) + allocate( eqns_data%mLayerVolFracLiqPrev(nLayers) ) + allocate( eqns_data%mLayerEnthalpyTrial(nLayers) ) + allocate( eqns_data%mLayerEnthalpyPrev(nLayers) ) + allocate( eqns_data%fluxVec(nState) ) + allocate( eqns_data%resSink(nState) ) + + startQuadrature = .true. + + ! create serial vectors + sunvec_y => FN_VMake_Serial(nState, stateVec) + if (.not. associated(sunvec_y)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif + + sunvec_yp => FN_VMake_Serial(nState, stateVecPrime) + if (.not. associated(sunvec_yp)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif + + !sunvec_c => FN_VMake_Serial(nState, stateVecConstraints) + !if (.not. associated(sunvec_c)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif + + !sunvec_cv => FN_VMake_Serial(nState, stateVecConstValues) + !if (.not. associated(sunvec_cv)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif + + ! Initialize solution vectors + call setInitialCondition(nState, stateVecInit, sunvec_y, sunvec_yp) + + ! Create memory + ida_mem = FIDACreate() + if (.not. c_associated(ida_mem)) then; err=20; message='solveByIDA: ida_mem = NULL'; return; endif + + ! Attach user data to memory + eqns_data%ida_mem = ida_mem + retval = FIDASetUserData(ida_mem, c_loc(eqns_data)) + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetUserData'; return; endif + + ! Initialize memory + t0 = 0._rkind + retval = FIDAInit(ida_mem, c_funloc(evalDAE4IDA), t0, sunvec_y, sunvec_yp) + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAInit'; return; endif + + ! set tolerances + retval = FIDAWFtolerances(ida_mem, c_funloc(computWeight4IDA)) + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAWFtolerances'; return; endif + + ! initialize rootfinding problem with nSoil components + if(nSoil>0)then + retval = FIDARootInit(ida_mem, nSoil, c_funloc(freezePoint4IDA)) + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif + endif + + ! define the form of the matrix + select case(ixMatrix) + case(ixBandMatrix) + mu = ku; lu = kl; + ! Create banded SUNMatrix for use in linear solves + sunmat_A => FSUNBandMatrix(nState, mu, lu) + if (.not. associated(sunmat_A)) then; err=20; message='solveByIDA: sunmat = NULL'; return; endif + + ! Create banded SUNLinearSolver object + sunlinsol_LS => FSUNLinSol_Band(sunvec_y, sunmat_A) + if (.not. associated(sunlinsol_LS)) then; err=20; message='solveByIDA: sunlinsol = NULL'; return; endif + + case(ixFullMatrix) + ! Create dense SUNMatrix for use in linear solves + sunmat_A => FSUNDenseMatrix(nState, nState) + if (.not. associated(sunmat_A)) then; err=20; message='solveByIDA: sunmat = NULL'; return; endif + + ! Create dense SUNLinearSolver object + sunlinsol_LS => FSUNDenseLinearSolver(sunvec_y, sunmat_A) + if (.not. associated(sunlinsol_LS)) then; err=20; message='solveByIDA: sunlinsol = NULL'; return; endif + + ! check + case default; err=20; message='solveByIDA: error in type of matrix'; return + + end select ! form of matrix + + ! Attach the matrix and linear solver + retval = FIDASetLinearSolver(ida_mem, sunlinsol_LS, sunmat_A); + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetLinearSolver'; return; endif + + ! Set constraints + !retval = FIDASetConstraints(ida_mem, sunvec_c, sunvec_cv) !uncomment this line to use feasibility constraints + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetConstraints'; return; endif + + if(ixMatrix == ixFullMatrix)then + ! Set the user-supplied Jacobian routine + !comment this line out to use FD Jacobian, currently cannot do with constraint version of IDA, memory size error + retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetJacFn'; return; endif + endif + + ! Create Newton SUNNonlinearSolver object + sunnonlin_NLS => FSUNNonlinSol_Newton(sunvec_y) + if (.not. associated(sunnonlin_NLS)) then; err=20; message='solveByIDA: sunnonlinsol = NULL'; return; endif + + ! Attach the nonlinear solver + retval = FIDASetNonlinearSolver(ida_mem, sunnonlin_NLS) + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetNonlinearSolver'; return; endif + + ! Enforce the solver to stop at end of the time step + retval = FIDASetStopTime(ida_mem, dt) + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetStopTime'; return; endif + + ! Set solver parameters such as maximum order, number of iterations, ... + call setSolverParams(dt, ida_mem, retval) + if (retval /= 0) then; err=20; message='solveByIDA: error in setSolverParams'; return; endif + + ! Disable error messages and warnings + if(offErrWarnMessage) then + retval = FIDASetErrFile(ida_mem, c_null_ptr) + retval = FIDASetNoInactiveRootWarn(ida_mem) + endif + + ! need the following values for the first substep + eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) eqns_data%scalarCanopyIcePrev = prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) eqns_data%scalarCanopyLiqPrev = prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) - eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) - eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) - eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) - eqns_data%mLayerVolFracLiqPrev(:) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(:) - eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) + eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) + eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) + eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) + eqns_data%mLayerVolFracLiqPrev(:) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(:) + eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) eqns_data%scalarAquiferStoragePrev = prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) - eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) + eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) - mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) + mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) eqns_data%ixSaturation = ixSaturation !********************************************************************************** !****************************** Main Solver *************************************** !************************* loop on one_step mode ********************************** !********************************************************************************** + tret(1) = t0 do while(tret(1) < dt) eqns_data%firstFluxCall = .false. eqns_data%firstSplitOper = .true. - ! call IDASolve + ! call IDASolve, advance solver just one internal step retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) if( retval < 0 )then idaSucceeds = .false. @@ -456,11 +453,11 @@ subroutine solveByIDA( & tooMuchMelt = .false. feasible = .true. ! loop through non-missing energy state variables in the snow domain to see if need to merge - ! CURRENTLY WILL ONLY MERGE TOP LAYER + ! CURRENTLY WILL ONLY MERGE TOP LAYER, FIX? do concurrent (i=1:nSnow,indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing) if (stateVec(indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)) > Tfreeze) tooMuchMelt = .true. !need to merge if (stateVec(indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)) > Tfreeze) print*,i,tret(1),nSnow,idaSucceeds,"merge" - end do + enddo if(tooMuchMelt)exit ! get the last stepsize @@ -469,44 +466,44 @@ subroutine solveByIDA( & ! compute the flux and the residual vector for a given state vector call eval8DAE(& ! input: model control - dt_last(1), & ! intent(in): current stepsize - eqns_data%dt, & ! intent(in): data step - eqns_data%nSnow, & ! intent(in): number of snow layers - eqns_data%nSoil, & ! intent(in): number of soil layers - eqns_data%nLayers, & ! intent(in): number of layers - eqns_data%nState, & ! intent(in): number of state variables in the current subset - .true., & ! intent(in): check for feasibility once outside Sundials loop - eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation - eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution + dt_last(1), & ! intent(in): current stepsize + eqns_data%dt, & ! intent(in): total data step + eqns_data%nSnow, & ! intent(in): number of snow layers + eqns_data%nSoil, & ! intent(in): number of soil layers + eqns_data%nLayers, & ! intent(in): number of layers + eqns_data%nState, & ! intent(in): number of state variables in the current subset + .true., & ! intent(in): check for feasibility once outside Sundials loop + eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation + eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors - stateVec, & ! intent(in): model state vector - stateVecPrime, & ! intent(in): model state vector - eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + stateVec, & ! intent(in): model state vector + stateVecPrime, & ! intent(in): model state vector + eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures - model_decisions, & ! intent(in): model decisions - eqns_data%lookup_data, & ! intent(in): lookup data - eqns_data%type_data, & ! intent(in): type of vegetation and soil - eqns_data%attr_data, & ! intent(in): spatial attributes - eqns_data%mpar_data, & ! intent(in): model parameters - eqns_data%forc_data, & ! intent(in): model forcing data - eqns_data%bvar_data, & ! intent(in): average model variables for the entire basin - eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU + model_decisions, & ! intent(in): model decisions + eqns_data%lookup_data, & ! intent(in): lookup data + eqns_data%type_data, & ! intent(in): type of vegetation and soil + eqns_data%attr_data, & ! intent(in): spatial attributes + eqns_data%mpar_data, & ! intent(in): model parameters + eqns_data%forc_data, & ! intent(in): model forcing data + eqns_data%bvar_data, & ! intent(in): average model variables for the entire basin + eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU ! input-output: data structures - eqns_data%indx_data, & ! intent(inout): index data - eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU - eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) - eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + eqns_data%indx_data, & ! intent(inout): index data + eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU + eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) + eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! input-output - eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian - eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) - eqns_data%scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) - eqns_data%scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - eqns_data%scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) + eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian + eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) + eqns_data%scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) + eqns_data%scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + eqns_data%scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) eqns_data%scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): value for enthalpy of the vegetation canopy (J m-3) eqns_data%mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) @@ -520,189 +517,278 @@ subroutine solveByIDA( & eqns_data%mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) eqns_data%mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) eqns_data%mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) - eqns_data%scalarAquiferStorageTrial, & ! intent(out): trial value of storage of water in the aquifer (m) + eqns_data%scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) - eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer + eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer ! output feasible, & ! intent(out): flag to denote the feasibility of the solution eqns_data%fluxVec, & ! intent(out): flux vector eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector + rVec, & ! intent(out): residual vector eqns_data%err,eqns_data%message) ! intent(out): error control - ! sum of fluxes - do iVar=1,size(flux_meta) - flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * dt_last(1) + do iVar=1,size(flux_meta) + flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * dt_last(1) end do - ! do iVar=1,size(flux_meta) - ! flux_data%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / tret(1) - ! end do + ! do iVar=1,size(flux_meta) + ! flux_data%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / tret(1) + ! end do ! sum of mLayerCmpress mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & - * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) + * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) ! save required quantities for next step - eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial - eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial - eqns_data%scalarCanopyLiqPrev = eqns_data%scalarCanopyLiqTrial - eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) - mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) - eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) - eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) - eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) - eqns_data%mLayerVolFracLiqPrev(:) = eqns_data%mLayerVolFracLiqTrial(:) - eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial - eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) + eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial + eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial + eqns_data%scalarCanopyLiqPrev = eqns_data%scalarCanopyLiqTrial + eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) + mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) + eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) + eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) + eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) + eqns_data%mLayerVolFracLiqPrev(:) = eqns_data%mLayerVolFracLiqTrial(:) + eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial + eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial - end do ! while loop on one_step mode + if(nSoil>0)then + if (retval .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) + ! To find which layer of 1:nSoil has a root, call and print the following, where + ! rootsfound[i]= +1 indicates that gi is increasing, -1 g[i] decreasing, 0 no root + ! MAYBE ONLY CARE ABOUT TOP LAYER + retvalr = FIDAGetRootInfo(ida_mem, rootsfound) + if (retvalr < 0) then; err=20; message='solveByIDA: error in FIDAGetRootInfo'; return; endif + print '(a,100(i2,2x))', " rootsfound[] = ", rootsfound + + ! Reininitialize solver for running after discontinuity and restart at root + retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAReInit'; return; endif + endif + endif + + enddo ! while loop on one_step mode until time dt !****************************** End of Main Solver *************************************** - err = eqns_data%err - message = eqns_data%message - if( .not. feasible) idaSucceeds = .false. - - if(idaSucceeds)then - ! copy to output data - diag_data = eqns_data%diag_data - flux_data = eqns_data%flux_data - deriv_data = eqns_data%deriv_data - ixSaturation = eqns_data%ixSaturation - dt_out = tret(1) - endif + err = eqns_data%err + message = eqns_data%message + if( .not. feasible) idaSucceeds = .false. + + if(idaSucceeds)then + ! copy to output data + diag_data = eqns_data%diag_data + flux_data = eqns_data%flux_data + deriv_data = eqns_data%deriv_data + ixSaturation = eqns_data%ixSaturation + dt_out = tret(1) + endif + + ! free memory + deallocate( eqns_data%sMul ) + deallocate( eqns_data%dMat ) + deallocate( eqns_data%dBaseflow_dMatric ) + deallocate( eqns_data%mLayerMatricHeadLiqTrial ) + deallocate( eqns_data%mLayerMatricHeadTrial ) + deallocate( eqns_data%mLayerMatricHeadPrev ) + deallocate( eqns_data%fluxVec ) + deallocate( eqns_data%resSink ) + deallocate( eqns_data%mLayerVolFracWatTrial ) + deallocate( eqns_data%mLayerVolFracWatPrev ) + deallocate( eqns_data%mLayerVolFracIceTrial ) + deallocate( eqns_data%mLayerTempPrev ) + deallocate( eqns_data%mLayerTempTrial ) + deallocate( eqns_data%mLayerVolFracIcePrev ) + deallocate( eqns_data%mLayerVolFracLiqPrev ) + deallocate( eqns_data%mLayerEnthalpyTrial ) + deallocate( eqns_data%mLayerEnthalpyPrev ) + + call FIDAFree(ida_mem) + retval = FSUNNonlinSolFree(sunnonlin_NLS) + retval = FSUNLinSolFree(sunlinsol_LS) + call FSUNMatDestroy(sunmat_A) + call FN_VDestroy(sunvec_y) + call FN_VDestroy(sunvec_yp) + !call FN_VDestroy(sunvec_c) + !call FN_VDestroy(sunvec_cv) - ! free memory - deallocate( eqns_data%sMul ) - deallocate( eqns_data%dMat ) - deallocate( eqns_data%dBaseflow_dMatric ) - deallocate( eqns_data%mLayerMatricHeadLiqTrial ) - deallocate( eqns_data%mLayerMatricHeadTrial ) - deallocate( eqns_data%mLayerMatricHeadPrev ) - deallocate( eqns_data%fluxVec ) - deallocate( eqns_data%resSink ) - deallocate( eqns_data%mLayerVolFracWatTrial ) - deallocate( eqns_data%mLayerVolFracWatPrev ) - deallocate( eqns_data%mLayerVolFracIceTrial ) - deallocate( eqns_data%mLayerTempPrev ) - deallocate( eqns_data%mLayerTempTrial ) - deallocate( eqns_data%mLayerVolFracIcePrev ) - deallocate( eqns_data%mLayerVolFracLiqPrev ) - deallocate( eqns_data%mLayerEnthalpyTrial ) - deallocate( eqns_data%mLayerEnthalpyPrev ) - - call FIDAFree(ida_mem) - retval = FSUNNonlinSolFree(sunnonlin_NLS) - retval = FSUNLinSolFree(sunlinsol_LS) - call FSUNMatDestroy(sunmat_A) - call FN_VDestroy(sunvec_y) - call FN_VDestroy(sunvec_yp) - !call FN_VDestroy(sunvec_c) - !call FN_VDestroy(sunvec_cv) end subroutine solveByIDA ! ---------------------------------------------------------------- ! SetInitialCondition: routine to initialize u and up vectors. ! ---------------------------------------------------------------- -subroutine setInitialCondition(neq, y, sunvec_u, sunvec_up) + subroutine setInitialCondition(neq, y, sunvec_u, sunvec_up) - !======= Inclusions =========== - USE, intrinsic :: iso_c_binding - USE fsundials_nvector_mod - USE fnvector_serial_mod + !======= Inclusions =========== + USE, intrinsic :: iso_c_binding + USE fsundials_nvector_mod + USE fnvector_serial_mod - !======= Declarations ========= - implicit none + !======= Declarations ========= + implicit none - ! calling variables - type(N_Vector) :: sunvec_u ! solution N_Vector - type(N_Vector) :: sunvec_up ! derivative N_Vector - integer(c_long) :: neq - real(rkind) :: y(neq) + ! calling variables + type(N_Vector) :: sunvec_u ! solution N_Vector + type(N_Vector) :: sunvec_up ! derivative N_Vector + integer(c_long) :: neq + real(rkind) :: y(neq) - ! pointers to data in SUNDIALS vectors - real(c_double), pointer :: uu(:) - real(c_double), pointer :: up(:) + ! pointers to data in SUNDIALS vectors + real(c_double), pointer :: uu(:) + real(c_double), pointer :: up(:) - ! get data arrays from SUNDIALS vectors - uu(1:neq) => FN_VGetArrayPointer(sunvec_u) - up(1:neq) => FN_VGetArrayPointer(sunvec_up) + ! get data arrays from SUNDIALS vectors + uu(1:neq) => FN_VGetArrayPointer(sunvec_u) + up(1:neq) => FN_VGetArrayPointer(sunvec_up) - uu = y - up = 0._rkind + uu = y + up = 0._rkind -end subroutine setInitialCondition + end subroutine setInitialCondition ! ---------------------------------------------------------------- ! setSolverParams: private routine to set parameters in ida solver ! ---------------------------------------------------------------- -subroutine setSolverParams(dt,ida_mem,retval) - !======= Inclusions =========== - USE, intrinsic :: iso_c_binding - USE fida_mod ! Fortran interface to IDA - - !======= Declarations ========= - implicit none - - ! calling variables - real(rkind),intent(in) :: dt ! time step - type(c_ptr),intent(inout) :: ida_mem ! IDA memory - integer(i4b),intent(out) :: retval ! return value - - !======= Internals ============ - real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 - integer,parameter :: max_order = 1 ! maximum BDF order, default = 5 - integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 - integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 - integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 - integer(kind = 8),parameter :: max_step = 999999 ! maximum number of steps, dafault = 500 - real(qp),parameter :: h_init = 0 ! initial stepsize - real(qp) :: h_max ! maximum stepsize, dafault = infinity - - ! Set the maximum BDF order - retval = FIDASetMaxOrd(ida_mem, max_order) - if (retval /= 0) return - - ! Set Coeff. in the nonlinear convergence test - retval = FIDASetNonlinConvCoef(ida_mem, coef_nonlin) - if (retval /= 0) return - - ! Set maximun number of nonliear iterations - retval = FIDASetMaxNonlinIters(ida_mem, nonlin_iter) - if (retval /= 0) return - - ! Set maximum number of convergence test failures - retval = FIDASetMaxConvFails(ida_mem, convtest_fail) - if (retval /= 0) return - - ! Set maximum number of error test failures - retval = FIDASetMaxErrTestFails(ida_mem, acurtest_fail) - if (retval /= 0) return - - ! Set maximum number of steps - retval = FIDASetMaxNumSteps(ida_mem, max_step) - if (retval /= 0) return - - ! Set maximum stepsize - h_max = dt - retval = FIDASetMaxStep(ida_mem, h_max) - if (retval /= 0) return - - ! Set initial stepsize - retval = FIDASetInitStep(ida_mem, h_init) - if (retval /= 0) return - - ! scalling on 1 and off 0 + subroutine setSolverParams(dt,ida_mem,retval) + + !======= Inclusions =========== + USE, intrinsic :: iso_c_binding + USE fida_mod ! Fortran interface to IDA + + !======= Declarations ========= + implicit none + + ! calling variables + real(rkind),intent(in) :: dt ! time step + type(c_ptr),intent(inout) :: ida_mem ! IDA memory + integer(i4b),intent(out) :: retval ! return value + + !======= Internals ============ + real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 + integer,parameter :: max_order = 1 ! maximum BDF order, default = 5 + integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 + integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 + integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 + integer(kind = 8),parameter :: max_step = 999999 ! maximum number of steps, dafault = 500 + real(qp),parameter :: h_init = 0 ! initial stepsize + real(qp) :: h_max ! maximum stepsize, dafault = infinity + + ! Set the maximum BDF order + retval = FIDASetMaxOrd(ida_mem, max_order) + if (retval /= 0) return + + ! Set Coeff. in the nonlinear convergence test + retval = FIDASetNonlinConvCoef(ida_mem, coef_nonlin) + if (retval /= 0) return + + ! Set maximun number of nonliear iterations + retval = FIDASetMaxNonlinIters(ida_mem, nonlin_iter) + if (retval /= 0) return + + ! Set maximum number of convergence test failures + retval = FIDASetMaxConvFails(ida_mem, convtest_fail) + if (retval /= 0) return + + ! Set maximum number of error test failures + retval = FIDASetMaxErrTestFails(ida_mem, acurtest_fail) + if (retval /= 0) return + + ! Set maximum number of steps + retval = FIDASetMaxNumSteps(ida_mem, max_step) + if (retval /= 0) return + + ! Set maximum stepsize + h_max = dt + retval = FIDASetMaxStep(ida_mem, h_max) + if (retval /= 0) return + + ! Set initial stepsize + retval = FIDASetInitStep(ida_mem, h_init) + if (retval /= 0) return + + ! scaling on 1 and off 0 ! retval = FIDASetLinearSolutionScaling(ida_mem, 0) ! if (retval /= 0) return -end subroutine setSolverParams + end subroutine setSolverParams + +! ---------------------------------------------------------------- +! freezePoint4IDA: The root function routine to find soil freeze +! ---------------------------------------------------------------- +! Return values: +! 0 = success, +! 1 = recoverable error, +! -1 = non-recoverable error +! ---------------------------------------------------------------- + integer(c_int) function freezePoint4IDA(t, sunvec_u, sunvec_up, gout, user_data) & + result(ierr) bind(C,name='freezePoint4IDA') + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use fsundials_nvector_mod + use fnvector_serial_mod + use soil_utils_module,only:crit_soilT ! compute the critical temperature below which ice exists + use globalData,only:integerMissing ! missing integer + use var_lookup,only:iLookINDEX ! named variables for structure elements + + !======= Declarations ========= + implicit none + + ! calling variables + real(c_double), value :: t ! current time + type(N_Vector) :: sunvec_u ! solution N_Vector + type(N_Vector) :: sunvec_up ! derivative N_Vector + real(c_double) :: gout(8) ! root function values + type(c_ptr), value :: user_data ! user-defined data + + ! local variables + integer(i4b) :: i ! index of model layer + integer(i4b) :: nState ! number of states + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers + real(rkind) :: xPsi ! matric head at layer (m) + real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) + + ! pointers to data in SUNDIALS vectors + real(c_double), pointer :: uu(:) + type(eqnsData), pointer :: eqns_data ! equations data + + !======= Internals ============ + ! get equations data from user-defined data + call c_f_pointer(user_data, eqns_data) + nState = eqns_data%nState + nSnow = eqns_data%nSnow + nSoil = eqns_data%nSoil + gout(1:nSoil) = 0._rkind + + ! get data array from SUNDIALS vector + uu(1:nState) => FN_VGetArrayPointer(sunvec_u) + + ! fill root vector, will not call this function unless nSoil>0 + do i=1,nSoil + if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)==integerMissing) cycle + ! get the matric potential of total water + if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing)then + xPsi = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)) + else + xPsi = eqns_data%prog_data%var(iLookPROG%mLayerMatricHead)%dat(i) + endif + + ! identify the critical point when soil begins to freeze (TcSoil) + TcSoil = crit_soilT(xPsi) + gout(i) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) - TcSoil + enddo + + ! return success + ierr = 0 + return + + end function freezePoint4IDA ! ********************************************************************************************************* ! private subroutine implctMelt: compute melt of the "snow without a layer" diff --git a/sundials/installation.txt b/sundials/installation.txt index dd52ac130..15fbb8e0b 100644 --- a/sundials/installation.txt +++ b/sundials/installation.txt @@ -28,7 +28,7 @@ cp ../../summa/build/build_cmakeSundials_* build_cmake % make % make install -7. Enter summa directory and build summa as in /docs/SUMMA_instatllation.md. You will have to edit the SUMMA Makefile as below additionally (as well as install required libraries and link them as directed in the Makefile). +7. Enter summa directory and build summa as in /docs/SUMMA_installation.md. You will have to edit the SUMMA Makefile as below additionally (as well as install required libraries and link them as directed in the Makefile). 8. In SUMMA Makefile, define the FC_EXE to be the same compiler as you used for sundials and netcdf From 283ed6fb2e89702e6a6f78b3876799261ee37b5a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 12 Jul 2022 22:39:22 +0900 Subject: [PATCH 0306/1472] Fixed retvalr to be only for IDAsolve --- build/source/engine/solveByIDA.f90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 8d6734db6..e7a13dad0 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -444,7 +444,7 @@ subroutine solveByIDA( & eqns_data%firstFluxCall = .false. eqns_data%firstSplitOper = .true. ! call IDASolve, advance solver just one internal step - retval = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) + retvalr = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) if( retval < 0 )then idaSucceeds = .false. exit @@ -557,12 +557,12 @@ subroutine solveByIDA( & eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial if(nSoil>0)then - if (retval .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) + if (retvalr .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) ! To find which layer of 1:nSoil has a root, call and print the following, where ! rootsfound[i]= +1 indicates that gi is increasing, -1 g[i] decreasing, 0 no root ! MAYBE ONLY CARE ABOUT TOP LAYER - retvalr = FIDAGetRootInfo(ida_mem, rootsfound) - if (retvalr < 0) then; err=20; message='solveByIDA: error in FIDAGetRootInfo'; return; endif + retval = FIDAGetRootInfo(ida_mem, rootsfound) + if (retval < 0) then; err=20; message='solveByIDA: error in FIDAGetRootInfo'; return; endif print '(a,100(i2,2x))', " rootsfound[] = ", rootsfound ! Reininitialize solver for running after discontinuity and restart at root From 71f0c5dbcf250726aa6e86a92fd64917f5e6ae14 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 13 Jul 2022 10:44:19 +0900 Subject: [PATCH 0307/1472] bumping temp over freezing point by 1e-7 --- build/source/engine/solveByIDA.f90 | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index e7a13dad0..e2b2d9101 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -233,6 +233,8 @@ subroutine solveByIDA( & real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) integer(i4b) :: i + real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical (K) + ! ----------------------------------------------------------------------------------------------------- @@ -563,8 +565,12 @@ subroutine solveByIDA( & ! MAYBE ONLY CARE ABOUT TOP LAYER retval = FIDAGetRootInfo(ida_mem, rootsfound) if (retval < 0) then; err=20; message='solveByIDA: error in FIDAGetRootInfo'; return; endif - print '(a,100(i2,2x))', " rootsfound[] = ", rootsfound - + print '(a,f7.3,2x,100(i2,2x))', "time, rootsfound[] = ", rootsfound + do concurrent (i=1:nSoil,indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing) + if (rootsfound(i)==-1) stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) = stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) - epsT !freezing, so move a bit colder than freeze point + if (rootsfound(i)== 1) stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) = stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) + epsT !thawing, so move a bit warmer than freeze point + enddo + sunvec_y => FN_VMake_Serial(nState, stateVec) ! Reininitialize solver for running after discontinuity and restart at root retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAReInit'; return; endif From a3da9c4c4669b2ff1f549a5195680b26682b30d3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 13 Jul 2022 14:35:38 +0900 Subject: [PATCH 0308/1472] temp bump seems to need to be at least 1e-6 --- build/source/engine/solveByIDA.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index e2b2d9101..eb0b4bbd6 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -233,7 +233,7 @@ subroutine solveByIDA( & real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) integer(i4b) :: i - real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical (K) + real(rkind),parameter :: epsT=1.e-6_rkind ! small interval above/below critical (K) ! ----------------------------------------------------------------------------------------------------- @@ -565,7 +565,7 @@ subroutine solveByIDA( & ! MAYBE ONLY CARE ABOUT TOP LAYER retval = FIDAGetRootInfo(ida_mem, rootsfound) if (retval < 0) then; err=20; message='solveByIDA: error in FIDAGetRootInfo'; return; endif - print '(a,f7.3,2x,100(i2,2x))', "time, rootsfound[] = ", rootsfound + print '(a,f15.3,2x,100(i2,2x))', "time, rootsfound[] = ", tret(1), rootsfound do concurrent (i=1:nSoil,indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing) if (rootsfound(i)==-1) stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) = stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) - epsT !freezing, so move a bit colder than freeze point if (rootsfound(i)== 1) stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) = stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) + epsT !thawing, so move a bit warmer than freeze point From bbc10c5e7dc276b49060d820ff9c6b8717ccf4ed Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 14 Jul 2022 10:07:08 +0900 Subject: [PATCH 0309/1472] Added a flag for tooMuchSublim and a return out of computSnowDepth. The function computeSnowDepth is part of coupled_em in the BE version, and has this exit. --- build/source/engine/computSnowDepth.f90 | 35 ++++++++++++++++--------- build/source/engine/coupled_em.f90 | 34 +++++++++++++++++++----- 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/build/source/engine/computSnowDepth.f90 b/build/source/engine/computSnowDepth.f90 index faa39b06f..9332798a6 100644 --- a/build/source/engine/computSnowDepth.f90 +++ b/build/source/engine/computSnowDepth.f90 @@ -52,6 +52,7 @@ subroutine computSnowDepth(& mLayerMeltFreeze, & ! intent(in) mpar_data, & ! intent(in) ! output + tooMuchSublim, & ! intent(out): flag to denote that there was too much sublimation in a given time step mLayerDepth, & ! intent(inout) ! error control err,message) ! intent(out): error control @@ -59,25 +60,29 @@ subroutine computSnowDepth(& USE snwDensify_module,only:snwDensify ! snow densification (compaction and cavitation) implicit none - real(qp),intent(in) :: dt_sub - integer(i4b),intent(in) :: nSnow ! number of snow layers - real(rkind),intent(in) :: scalarSnowSublimation - real(rkind),intent(inout) :: mLayerVolFracLiq(:) - real(rkind),intent(inout) :: mLayerVolFracIce(:) - real(rkind),intent(in) :: mLayerTemp(:) - real(rkind),intent(in) :: mLayerMeltFreeze(:) - type(var_dlength),intent(in) :: mpar_data ! model parameters - real(rkind),intent(inout) :: mLayerDepth(:) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + real(qp),intent(in) :: dt_sub + integer(i4b),intent(in) :: nSnow ! number of snow layers + real(rkind),intent(in) :: scalarSnowSublimation + real(rkind),intent(inout) :: mLayerVolFracLiq(:) + real(rkind),intent(inout) :: mLayerVolFracIce(:) + real(rkind),intent(in) :: mLayerTemp(:) + real(rkind),intent(in) :: mLayerMeltFreeze(:) + type(var_dlength),intent(in) :: mpar_data ! model parameters + logical(lgt) :: tooMuchSublim ! flag to denote that there was too much sublimation in a given time step + real(rkind),intent(inout) :: mLayerDepth(:) + + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! local variables character(len=256) :: cmessage ! error message integer(i4b) :: iSnow ! index of snow layers - real(rkind) :: massLiquid ! mass liquid water (kg m-2) + real(rkind) :: massLiquid ! mass liquid water (kg m-2) ! * compute change in ice content of the top snow layer due to sublimation... ! --------------------------------------------------------------------------- + ! initialize the flags + tooMuchSublim=.false. ! too much sublimination (merge snow layers) ! NOTE: this is done BEFORE densification if(nSnow > 0)then ! snow layers exist @@ -91,6 +96,12 @@ subroutine computSnowDepth(& ! NOTE: assume constant density mLayerDepth(iSnow) = mLayerDepth(iSnow) + dt_sub*scalarSnowSublimation/(mLayerVolFracIce(iSnow)*iden_ice) + ! check that we did not remove the entire layer + if(mLayerDepth(iSnow) < verySmall)then + tooMuchSublim=.true. + return + endif + ! update the volumetric fraction of liquid water mLayerVolFracLiq(iSnow) = massLiquid / (mLayerDepth(iSnow)*iden_water) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index e01b6493d..178fe8d74 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -195,6 +195,7 @@ subroutine coupled_em(& logical(lgt) :: firstSubStep ! flag to denote if the first time step logical(lgt) :: stepFailure ! flag to denote the need to reduce length of the coupled step and try again logical(lgt) :: tooMuchMelt ! flag to denote that there was too much melt in a given time step + logical(lgt) :: tooMuchSublim ! flag to denote that there was too much sublimation in a given time step logical(lgt) :: doLayerMerge ! flag to denote the need to merge snow layers logical(lgt) :: pauseFlag ! flag to pause execution logical(lgt),parameter :: backwardsCompatibility=.true. ! flag to denote a desire to ensure backwards compatibility with previous branches. @@ -764,10 +765,8 @@ subroutine coupled_em(& ! handle special case of the step failure ! NOTE: need to revert back to the previous state vector that we were happy with and reduce the time step if(stepFailure)then - ! halve step dt_sub = dtSave/2._rkind - ! check that the step is not tiny if(dt_sub < minstep)then print*,ixSolution @@ -775,10 +774,8 @@ subroutine coupled_em(& message=trim(message)//'length of the coupled step is below the minimum step length' err=20; return endif - ! try again cycle substeps - endif ! update first step @@ -837,10 +834,35 @@ subroutine coupled_em(& diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat, & ! intent(in) mpar_data, & ! intent(in) ! output + tooMuchSublim, & ! intent(out): flag to denote that there was too much sublimation in a given time step mLayerDepth, & ! intent(inout) ! error control - err,message) ! intent(out): error control - if(err/=0)then; err=55; return; end if + err,message) ! intent(out): error control + if(err/=0)then; err=55; return; end if + + ! process the flag for too much sublimation + if(tooMuchSublim)then + stepFailure = .true. + doLayerMerge = .true. + else + doLayerMerge = .false. + endif + + ! handle special case of the step failure + ! NOTE: need to revert back to the previous state vector that we were happy with and reduce the time step + if(stepFailure)then + ! halve step + dt_sub = dtSave/2._rkind + ! check that the step is not tiny + if(dt_sub < minstep)then + print*,ixSolution + print*, 'dtSave, dt_sub', dtSave, dt_sub + message=trim(message)//'length of the coupled step is below the minimum step length' + err=20; return + endif + ! try again + cycle substeps + endif end associate sublime From 21a3546e637485ca7a9158e5945275a7ded67639 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 14 Jul 2022 11:33:21 +0900 Subject: [PATCH 0310/1472] moving epsT, how much move off freeze point in soil. BE had 1e-7. 1e-6 does not seem to be big enough, trying 1e-4. --- build/source/engine/solveByIDA.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index eb0b4bbd6..8707a7e9f 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -233,7 +233,7 @@ subroutine solveByIDA( & real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) integer(i4b) :: i - real(rkind),parameter :: epsT=1.e-6_rkind ! small interval above/below critical (K) + real(rkind),parameter :: epsT=1.e-4_rkind ! small interval above/below critical (K) ! ----------------------------------------------------------------------------------------------------- From 08f88a69e2907e30d7ea9dd2091bd22d75bdcf7d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 14 Jul 2022 22:13:15 +0900 Subject: [PATCH 0311/1472] Trying to increase epsT somewhat and only stop with top layer, still, seems like root might be able to find backwards in time root so need to not start there and fix that. --- build/source/engine/solveByIDA.f90 | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 8707a7e9f..29bb3af50 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -216,7 +216,8 @@ subroutine solveByIDA( & type(c_ptr) :: ida_mem ! IDA memory type(eqnsData), target :: eqns_data ! IDA type integer(i4b) :: retval, retvalr ! return value - integer(i4b) :: rootsfound(nSoil) ! point where mLayerTemp = freezing in each soil layer + !integer(i4b) :: rootsfound(nSoil) ! point where mLayerTemp = freezing in each soil layer + integer(i4b) :: rootsfound(1) ! point where mLayerTemp = freezing in top soil layer logical(lgt) :: feasible ! feasibility flag real(qp) :: t0 ! staring time real(qp) :: dt_last(1) ! last time step @@ -233,8 +234,7 @@ subroutine solveByIDA( & real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) integer(i4b) :: i - real(rkind),parameter :: epsT=1.e-4_rkind ! small interval above/below critical (K) - + real(rkind),parameter :: epsT=1.e-5_rkind ! small interval above/below critical (K) ! ----------------------------------------------------------------------------------------------------- @@ -354,7 +354,8 @@ subroutine solveByIDA( & ! initialize rootfinding problem with nSoil components if(nSoil>0)then - retval = FIDARootInit(ida_mem, nSoil, c_funloc(freezePoint4IDA)) + !retval = FIDARootInit(ida_mem, nSoil, c_funloc(freezePoint4IDA)) ! all layers + retval = FIDARootInit(ida_mem, 1, c_funloc(freezePoint4IDA)) ! try only top layer if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif endif @@ -566,10 +567,12 @@ subroutine solveByIDA( & retval = FIDAGetRootInfo(ida_mem, rootsfound) if (retval < 0) then; err=20; message='solveByIDA: error in FIDAGetRootInfo'; return; endif print '(a,f15.3,2x,100(i2,2x))', "time, rootsfound[] = ", tret(1), rootsfound - do concurrent (i=1:nSoil,indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing) + !do concurrent (i=1:nSoil,indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing) + do concurrent (i=1:1,indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing) if (rootsfound(i)==-1) stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) = stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) - epsT !freezing, so move a bit colder than freeze point if (rootsfound(i)== 1) stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) = stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) + epsT !thawing, so move a bit warmer than freeze point enddo + sunvec_y => FN_VMake_Serial(nState, stateVec) ! Reininitialize solver for running after discontinuity and restart at root retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) @@ -749,7 +752,7 @@ integer(c_int) function freezePoint4IDA(t, sunvec_u, sunvec_up, gout, user_data) real(c_double), value :: t ! current time type(N_Vector) :: sunvec_u ! solution N_Vector type(N_Vector) :: sunvec_up ! derivative N_Vector - real(c_double) :: gout(8) ! root function values + real(c_double) :: gout(100) ! root function values HAVE TO DECLARE THIS A SIZE type(c_ptr), value :: user_data ! user-defined data ! local variables @@ -770,13 +773,17 @@ integer(c_int) function freezePoint4IDA(t, sunvec_u, sunvec_up, gout, user_data) nState = eqns_data%nState nSnow = eqns_data%nSnow nSoil = eqns_data%nSoil - gout(1:nSoil) = 0._rkind + !if (nSoil> 100) then + ! print*, 'error, gout size in freezePoint4IDA function must be increased over 100' + !endif + !gout(1:nSoil) = 0._rkind + gout(1:1) = 0._rkind ! get data array from SUNDIALS vector uu(1:nState) => FN_VGetArrayPointer(sunvec_u) ! fill root vector, will not call this function unless nSoil>0 - do i=1,nSoil + do i=1,1 !nSoil if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)==integerMissing) cycle ! get the matric potential of total water if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing)then From da6c08db86f48b81d58ef1d197a6d2746640d370 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 15 Jul 2022 21:59:51 +0900 Subject: [PATCH 0312/1472] Making epsT big (1e-3) and removing a comment about merging only the top layer which is not true, all layers can merge. --- build/source/engine/solveByIDA.f90 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 29bb3af50..d63cf2b78 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -234,7 +234,7 @@ subroutine solveByIDA( & real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) integer(i4b) :: i - real(rkind),parameter :: epsT=1.e-5_rkind ! small interval above/below critical (K) + real(rkind),parameter :: epsT=1.e-3_rkind ! small interval above/below critical (K) ! ----------------------------------------------------------------------------------------------------- @@ -456,7 +456,6 @@ subroutine solveByIDA( & tooMuchMelt = .false. feasible = .true. ! loop through non-missing energy state variables in the snow domain to see if need to merge - ! CURRENTLY WILL ONLY MERGE TOP LAYER, FIX? do concurrent (i=1:nSnow,indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing) if (stateVec(indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)) > Tfreeze) tooMuchMelt = .true. !need to merge if (stateVec(indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)) > Tfreeze) print*,i,tret(1),nSnow,idaSucceeds,"merge" @@ -559,6 +558,7 @@ subroutine solveByIDA( & eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial + ! Look for where soil layer crosses the freezing point and makes a discontinuity (the root is the freezing point) if(nSoil>0)then if (retvalr .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) ! To find which layer of 1:nSoil has a root, call and print the following, where @@ -567,6 +567,7 @@ subroutine solveByIDA( & retval = FIDAGetRootInfo(ida_mem, rootsfound) if (retval < 0) then; err=20; message='solveByIDA: error in FIDAGetRootInfo'; return; endif print '(a,f15.3,2x,100(i2,2x))', "time, rootsfound[] = ", tret(1), rootsfound + ! Need to move off of the freezing point, okay to adjust since in the middle of a step !do concurrent (i=1:nSoil,indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing) do concurrent (i=1:1,indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing) if (rootsfound(i)==-1) stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) = stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) - epsT !freezing, so move a bit colder than freeze point @@ -574,7 +575,7 @@ subroutine solveByIDA( & enddo sunvec_y => FN_VMake_Serial(nState, stateVec) - ! Reininitialize solver for running after discontinuity and restart at root + ! Reininitialize solver for running after discontinuity and restart retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAReInit'; return; endif endif From ae9ba5861e67ce51111e2dc271fbc33ca11f1aae Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 8 Aug 2022 12:17:17 +0900 Subject: [PATCH 0313/1472] Trying to fix oscillatory root behavior such that starts at epsT=1e-5 (BE has 1e-7 and maybe should start there??), and then move up to 1 if still oscillatory around root. --- build/source/engine/solveByIDA.f90 | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index d63cf2b78..59002fc21 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -216,11 +216,14 @@ subroutine solveByIDA( & type(c_ptr) :: ida_mem ! IDA memory type(eqnsData), target :: eqns_data ! IDA type integer(i4b) :: retval, retvalr ! return value - !integer(i4b) :: rootsfound(nSoil) ! point where mLayerTemp = freezing in each soil layer - integer(i4b) :: rootsfound(1) ! point where mLayerTemp = freezing in top soil layer + !integer(i4b) :: rootsfound(nSoil) ! crossing direction where mLayerTemp = freezing in each soil layer + !integer(i4b) :: prev_rootsfound(nSoil) ! previous crossing direction where mLayerTemp = freezing in each soil layer + integer(i4b) :: rootsfound(1) ! crossing direction where mLayerTemp = freezing in top soil layer + integer(i4b) :: prev_rootsfound(1) ! previous crossing direction where mLayerTemp = freezing in top soil layer logical(lgt) :: feasible ! feasibility flag real(qp) :: t0 ! staring time real(qp) :: dt_last(1) ! last time step + real(qp) :: prev_root_time ! last time found root integer(kind = 8) :: mu, lu ! in banded matrix mode integer(i4b) :: iVar logical(lgt) :: startQuadrature @@ -234,7 +237,8 @@ subroutine solveByIDA( & real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) integer(i4b) :: i - real(rkind),parameter :: epsT=1.e-3_rkind ! small interval above/below critical (K) + real(rkind),parameter :: epsT0=1.e-5_rkind ! small interval above/below critical (K) + real(rkind) :: epsT ! interval above/below critical (K), change from epsT0 so does not oscillate around root ! ----------------------------------------------------------------------------------------------------- @@ -442,7 +446,8 @@ subroutine solveByIDA( & !************************* loop on one_step mode ********************************** !********************************************************************************** - tret(1) = t0 + tret(1) = t0 ! intial time + prev_rootsfound = 0 ! reset everything at beginning of step do while(tret(1) < dt) eqns_data%firstFluxCall = .false. eqns_data%firstSplitOper = .true. @@ -563,16 +568,23 @@ subroutine solveByIDA( & if (retvalr .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) ! To find which layer of 1:nSoil has a root, call and print the following, where ! rootsfound[i]= +1 indicates that gi is increasing, -1 g[i] decreasing, 0 no root - ! MAYBE ONLY CARE ABOUT TOP LAYER + ! Here, ONLY CARE ABOUT TOP LAYER, if decide wrong, change to 1:nSoil indices BUT THEN CAN BOUNCE TIME BACK AND FORTH BETWEEN LAYERS retval = FIDAGetRootInfo(ida_mem, rootsfound) if (retval < 0) then; err=20; message='solveByIDA: error in FIDAGetRootInfo'; return; endif - print '(a,f15.3,2x,100(i2,2x))', "time, rootsfound[] = ", tret(1), rootsfound ! Need to move off of the freezing point, okay to adjust since in the middle of a step !do concurrent (i=1:nSoil,indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing) do concurrent (i=1:1,indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing) - if (rootsfound(i)==-1) stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) = stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) - epsT !freezing, so move a bit colder than freeze point - if (rootsfound(i)== 1) stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) = stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) + epsT !thawing, so move a bit warmer than freeze point + if ( rootsfound(i)== -prev_rootsfound(i) .AND. prev_root_time+dt_last(1)==tret(1) .AND. dt_last(1)<10.*epsT)then + epsT = 10.*epsT !then oscillating root, from previous time step, and that time step is small or negative (can happen if looking at more than top layer) + if (epsT>1.0) epsT=1.0 ! shouldn't need larger than this THROW ERROR? + else + epsT = epsT0 !not oscillating on small time step, set or reset to original small adjustment + endif + stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) = stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) + real(rootsfound(i))*epsT enddo + print '(a,3(f15.3,2x),100(i2,2x))', "time, epsT, dt_last(1), rootsfound[] = ", tret(1), epsT, dt_last(1), rootsfound + prev_rootsfound = rootsfound + prev_root_time = tret(1) sunvec_y => FN_VMake_Serial(nState, stateVec) ! Reininitialize solver for running after discontinuity and restart From 2beb96c95958c49a5f69b20db57bcb2f6af40e72 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 8 Aug 2022 12:42:53 +0900 Subject: [PATCH 0314/1472] didn't properly initialize, also logic is cleaned up for top layer rootfinding only, will have to rethink if want other layers --- build/source/engine/solveByIDA.f90 | 31 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 59002fc21..16e881f4b 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -446,8 +446,9 @@ subroutine solveByIDA( & !************************* loop on one_step mode ********************************** !********************************************************************************** + prev_rootsfound = 100 !set everything at beginning of step to values that won't trigger root statements + prev_root_time = -10000._rkind tret(1) = t0 ! intial time - prev_rootsfound = 0 ! reset everything at beginning of step do while(tret(1) < dt) eqns_data%firstFluxCall = .false. eqns_data%firstSplitOper = .true. @@ -563,29 +564,25 @@ subroutine solveByIDA( & eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial - ! Look for where soil layer crosses the freezing point and makes a discontinuity (the root is the freezing point) + ! Look for where top soil layer crosses the freezing point and makes a discontinuity (the root is the freezing point) if(nSoil>0)then if (retvalr .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) - ! To find which layer of 1:nSoil has a root, call and print the following, where - ! rootsfound[i]= +1 indicates that gi is increasing, -1 g[i] decreasing, 0 no root - ! Here, ONLY CARE ABOUT TOP LAYER, if decide wrong, change to 1:nSoil indices BUT THEN CAN BOUNCE TIME BACK AND FORTH BETWEEN LAYERS + ! rootsfound[i]= +1 indicates that gi is increasing, -1 g[i] decreasing, 0 no root retval = FIDAGetRootInfo(ida_mem, rootsfound) if (retval < 0) then; err=20; message='solveByIDA: error in FIDAGetRootInfo'; return; endif ! Need to move off of the freezing point, okay to adjust since in the middle of a step - !do concurrent (i=1:nSoil,indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing) - do concurrent (i=1:1,indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing) - if ( rootsfound(i)== -prev_rootsfound(i) .AND. prev_root_time+dt_last(1)==tret(1) .AND. dt_last(1)<10.*epsT)then - epsT = 10.*epsT !then oscillating root, from previous time step, and that time step is small or negative (can happen if looking at more than top layer) - if (epsT>1.0) epsT=1.0 ! shouldn't need larger than this THROW ERROR? - else - epsT = epsT0 !not oscillating on small time step, set or reset to original small adjustment - endif - stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) = stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) + real(rootsfound(i))*epsT - enddo - print '(a,3(f15.3,2x),100(i2,2x))', "time, epsT, dt_last(1), rootsfound[] = ", tret(1), epsT, dt_last(1), rootsfound + if ( rootsfound(1)== -prev_rootsfound(1) .AND. prev_root_time+dt_last(1)==tret(1) .AND. dt_last(1)<10.*epsT)then + epsT = 10.*epsT !then oscillating root , from previous time step, and that time step is small + if (epsT>1.0) epsT=1.0 ! shouldn't need larger than this THROW ERROR? + else + epsT = epsT0 !not oscillating on small time step, set or reset to original small adjustment + endif + if (indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(1)/=integerMissing) then + stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(1)) = stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(1)) + real(rootsfound(1))*epsT + endif prev_rootsfound = rootsfound prev_root_time = tret(1) - + print '(a,3(f15.3,2x),100(i2,2x))', "time, epsT, dt_last(1), rootsfound[] = ", tret(1), epsT, dt_last(1), rootsfound sunvec_y => FN_VMake_Serial(nState, stateVec) ! Reininitialize solver for running after discontinuity and restart retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) From f5d8b7160cd08143814ec40bc9013d728f3769df Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 8 Aug 2022 14:57:48 +0900 Subject: [PATCH 0315/1472] make it look at previous 2 steps --- build/source/engine/solveByIDA.f90 | 36 +++++++++++++++++++----------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 16e881f4b..e19c4f446 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -216,14 +216,14 @@ subroutine solveByIDA( & type(c_ptr) :: ida_mem ! IDA memory type(eqnsData), target :: eqns_data ! IDA type integer(i4b) :: retval, retvalr ! return value - !integer(i4b) :: rootsfound(nSoil) ! crossing direction where mLayerTemp = freezing in each soil layer - !integer(i4b) :: prev_rootsfound(nSoil) ! previous crossing direction where mLayerTemp = freezing in each soil layer integer(i4b) :: rootsfound(1) ! crossing direction where mLayerTemp = freezing in top soil layer - integer(i4b) :: prev_rootsfound(1) ! previous crossing direction where mLayerTemp = freezing in top soil layer + integer(i4b) :: prev1_rootsfound(1) ! previous crossing direction where mLayerTemp = freezing in top soil layer + integer(i4b) :: prev2_rootsfound(1) ! previous previous crossing direction where mLayerTemp = freezing in top soil layer logical(lgt) :: feasible ! feasibility flag real(qp) :: t0 ! staring time real(qp) :: dt_last(1) ! last time step - real(qp) :: prev_root_time ! last time found root + real(qp) :: prev1_root_dt ! previous time step when found root + real(qp) :: prev2_root_dt ! previous previous time step when found root integer(kind = 8) :: mu, lu ! in banded matrix mode integer(i4b) :: iVar logical(lgt) :: startQuadrature @@ -446,9 +446,13 @@ subroutine solveByIDA( & !************************* loop on one_step mode ********************************** !********************************************************************************** - prev_rootsfound = 100 !set everything at beginning of step to values that won't trigger root statements - prev_root_time = -10000._rkind + prev1_rootsfound = 100 !set everything at beginning of step to values that won't trigger root statements + prev2_rootsfound = 100 + prev1_root_dt = -10000._rkind + prev2_root_dt = -10000._rkind tret(1) = t0 ! intial time + epsT = epsT0 !set to original small adjustment for temp + do while(tret(1) < dt) eqns_data%firstFluxCall = .false. eqns_data%firstSplitOper = .true. @@ -563,7 +567,7 @@ subroutine solveByIDA( & eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial - + print*,tret(1) ! Look for where top soil layer crosses the freezing point and makes a discontinuity (the root is the freezing point) if(nSoil>0)then if (retvalr .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) @@ -571,22 +575,28 @@ subroutine solveByIDA( & retval = FIDAGetRootInfo(ida_mem, rootsfound) if (retval < 0) then; err=20; message='solveByIDA: error in FIDAGetRootInfo'; return; endif ! Need to move off of the freezing point, okay to adjust since in the middle of a step - if ( rootsfound(1)== -prev_rootsfound(1) .AND. prev_root_time+dt_last(1)==tret(1) .AND. dt_last(1)<10.*epsT)then - epsT = 10.*epsT !then oscillating root , from previous time step, and that time step is small + if ( rootsfound(1)== prev2_rootsfound(1) .AND. dt_last(1)==prev2_root_dt )then + epsT = 10.*epsT !then oscillating root , same as 2 states ago if (epsT>1.0) epsT=1.0 ! shouldn't need larger than this THROW ERROR? else - epsT = epsT0 !not oscillating on small time step, set or reset to original small adjustment + epsT = epsT0 !not oscillating, set or reset to original small adjustment endif if (indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(1)/=integerMissing) then stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(1)) = stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(1)) + real(rootsfound(1))*epsT endif - prev_rootsfound = rootsfound - prev_root_time = tret(1) - print '(a,3(f15.3,2x),100(i2,2x))', "time, epsT, dt_last(1), rootsfound[] = ", tret(1), epsT, dt_last(1), rootsfound + !print '(a,3(f15.5,2x),100(i2,2x))', "time, epsT, dt_last(1), rootsfound[] = ", tret(1), epsT, dt_last(1), rootsfound + print '(a,5(f15.5,2x),100(i2,2x))', "time, epsT, dt_last(1), rootsfound[] = ", tret(1), epsT, dt_last(1), prev1_root_dt, prev2_root_dt, rootsfound,prev1_rootsfound(1) ,prev2_rootsfound(1) + + prev2_rootsfound = prev1_rootsfound + prev2_root_dt = prev1_root_dt + prev1_rootsfound = rootsfound + prev1_root_dt = dt_last(1) sunvec_y => FN_VMake_Serial(nState, stateVec) ! Reininitialize solver for running after discontinuity and restart retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAReInit'; return; endif + else + epsT = epsT0 !not oscillating, set or reset to original small adjustment endif endif From 83369adf2dfba7f948d588d220d6e5c870234868 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 8 Aug 2022 16:24:07 +0900 Subject: [PATCH 0316/1472] works better if do off previous time step time, will have to use previous 2 timesteps if looking at more than one layer but just looking at top layer now --- build/source/engine/solveByIDA.f90 | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index e19c4f446..e4223ae84 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -217,13 +217,11 @@ subroutine solveByIDA( & type(eqnsData), target :: eqns_data ! IDA type integer(i4b) :: retval, retvalr ! return value integer(i4b) :: rootsfound(1) ! crossing direction where mLayerTemp = freezing in top soil layer - integer(i4b) :: prev1_rootsfound(1) ! previous crossing direction where mLayerTemp = freezing in top soil layer - integer(i4b) :: prev2_rootsfound(1) ! previous previous crossing direction where mLayerTemp = freezing in top soil layer + integer(i4b) :: prev_rootsfound(1) ! previous crossing direction where mLayerTemp = freezing in top soil layer logical(lgt) :: feasible ! feasibility flag real(qp) :: t0 ! staring time real(qp) :: dt_last(1) ! last time step - real(qp) :: prev1_root_dt ! previous time step when found root - real(qp) :: prev2_root_dt ! previous previous time step when found root + real(qp) :: prev_root_tret ! previous time when found root integer(kind = 8) :: mu, lu ! in banded matrix mode integer(i4b) :: iVar logical(lgt) :: startQuadrature @@ -446,10 +444,8 @@ subroutine solveByIDA( & !************************* loop on one_step mode ********************************** !********************************************************************************** - prev1_rootsfound = 100 !set everything at beginning of step to values that won't trigger root statements - prev2_rootsfound = 100 - prev1_root_dt = -10000._rkind - prev2_root_dt = -10000._rkind + prev_rootsfound = 10 !set everything at beginning of step to values that won't trigger root statements + prev_root_tret = -10000._rkind tret(1) = t0 ! intial time epsT = epsT0 !set to original small adjustment for temp @@ -567,7 +563,6 @@ subroutine solveByIDA( & eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial - print*,tret(1) ! Look for where top soil layer crosses the freezing point and makes a discontinuity (the root is the freezing point) if(nSoil>0)then if (retvalr .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) @@ -575,8 +570,8 @@ subroutine solveByIDA( & retval = FIDAGetRootInfo(ida_mem, rootsfound) if (retval < 0) then; err=20; message='solveByIDA: error in FIDAGetRootInfo'; return; endif ! Need to move off of the freezing point, okay to adjust since in the middle of a step - if ( rootsfound(1)== prev2_rootsfound(1) .AND. dt_last(1)==prev2_root_dt )then - epsT = 10.*epsT !then oscillating root , same as 2 states ago + if ( rootsfound(1)== -prev_rootsfound(1) .AND. tret(1)-prev_root_tret<1.e-2 )then + epsT = 10.*epsT !then oscillating root, found other direction root very recently if (epsT>1.0) epsT=1.0 ! shouldn't need larger than this THROW ERROR? else epsT = epsT0 !not oscillating, set or reset to original small adjustment @@ -584,19 +579,14 @@ subroutine solveByIDA( & if (indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(1)/=integerMissing) then stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(1)) = stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(1)) + real(rootsfound(1))*epsT endif - !print '(a,3(f15.5,2x),100(i2,2x))', "time, epsT, dt_last(1), rootsfound[] = ", tret(1), epsT, dt_last(1), rootsfound - print '(a,5(f15.5,2x),100(i2,2x))', "time, epsT, dt_last(1), rootsfound[] = ", tret(1), epsT, dt_last(1), prev1_root_dt, prev2_root_dt, rootsfound,prev1_rootsfound(1) ,prev2_rootsfound(1) + print '(a,4(f15.7,2x),100(i2,2x))', "time, epsT, dt_last(1), rootsfound[] = ", tret(1), epsT, dt_last(1), prev_root_tret, rootsfound,prev_rootsfound(1) - prev2_rootsfound = prev1_rootsfound - prev2_root_dt = prev1_root_dt - prev1_rootsfound = rootsfound - prev1_root_dt = dt_last(1) + prev_rootsfound = rootsfound + prev_root_tret = tret(1) sunvec_y => FN_VMake_Serial(nState, stateVec) ! Reininitialize solver for running after discontinuity and restart retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAReInit'; return; endif - else - epsT = epsT0 !not oscillating, set or reset to original small adjustment endif endif From 9d95cee1f6719147cdd78040b98b7d4a58e14131 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 9 Aug 2022 13:24:40 +0900 Subject: [PATCH 0317/1472] moving rootfinding from temperature to matric head, since that is the point that effects the freezing point equation that effects the discontinuity --- build/source/engine/solveByIDA.f90 | 61 +++++++----------------------- 1 file changed, 13 insertions(+), 48 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index e4223ae84..c0ef922f9 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -216,12 +216,10 @@ subroutine solveByIDA( & type(c_ptr) :: ida_mem ! IDA memory type(eqnsData), target :: eqns_data ! IDA type integer(i4b) :: retval, retvalr ! return value - integer(i4b) :: rootsfound(1) ! crossing direction where mLayerTemp = freezing in top soil layer - integer(i4b) :: prev_rootsfound(1) ! previous crossing direction where mLayerTemp = freezing in top soil layer + integer(i4b) :: rootsfound(1) ! crossing direction where mLayerMatricHead = 0 in top soil layer logical(lgt) :: feasible ! feasibility flag real(qp) :: t0 ! staring time real(qp) :: dt_last(1) ! last time step - real(qp) :: prev_root_tret ! previous time when found root integer(kind = 8) :: mu, lu ! in banded matrix mode integer(i4b) :: iVar logical(lgt) :: startQuadrature @@ -235,8 +233,6 @@ subroutine solveByIDA( & real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) integer(i4b) :: i - real(rkind),parameter :: epsT0=1.e-5_rkind ! small interval above/below critical (K) - real(rkind) :: epsT ! interval above/below critical (K), change from epsT0 so does not oscillate around root ! ----------------------------------------------------------------------------------------------------- @@ -356,8 +352,8 @@ subroutine solveByIDA( & ! initialize rootfinding problem with nSoil components if(nSoil>0)then - !retval = FIDARootInit(ida_mem, nSoil, c_funloc(freezePoint4IDA)) ! all layers - retval = FIDARootInit(ida_mem, 1, c_funloc(freezePoint4IDA)) ! try only top layer + !retval = FIDARootInit(ida_mem, nSoil, c_funloc(matZeroPoint4IDA)) ! all layers + retval = FIDARootInit(ida_mem, 1, c_funloc(matZeroPoint4IDA)) ! try only top layer if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif endif @@ -444,11 +440,7 @@ subroutine solveByIDA( & !************************* loop on one_step mode ********************************** !********************************************************************************** - prev_rootsfound = 10 !set everything at beginning of step to values that won't trigger root statements - prev_root_tret = -10000._rkind tret(1) = t0 ! intial time - epsT = epsT0 !set to original small adjustment for temp - do while(tret(1) < dt) eqns_data%firstFluxCall = .false. eqns_data%firstSplitOper = .true. @@ -563,27 +555,13 @@ subroutine solveByIDA( & eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial - ! Look for where top soil layer crosses the freezing point and makes a discontinuity (the root is the freezing point) + ! Look for where top soil layer crosses the matric head 0 point and changes equation of freezing temperature if(nSoil>0)then if (retvalr .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) ! rootsfound[i]= +1 indicates that gi is increasing, -1 g[i] decreasing, 0 no root retval = FIDAGetRootInfo(ida_mem, rootsfound) if (retval < 0) then; err=20; message='solveByIDA: error in FIDAGetRootInfo'; return; endif - ! Need to move off of the freezing point, okay to adjust since in the middle of a step - if ( rootsfound(1)== -prev_rootsfound(1) .AND. tret(1)-prev_root_tret<1.e-2 )then - epsT = 10.*epsT !then oscillating root, found other direction root very recently - if (epsT>1.0) epsT=1.0 ! shouldn't need larger than this THROW ERROR? - else - epsT = epsT0 !not oscillating, set or reset to original small adjustment - endif - if (indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(1)/=integerMissing) then - stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(1)) = stateVec(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(1)) + real(rootsfound(1))*epsT - endif - print '(a,4(f15.7,2x),100(i2,2x))', "time, epsT, dt_last(1), rootsfound[] = ", tret(1), epsT, dt_last(1), prev_root_tret, rootsfound,prev_rootsfound(1) - - prev_rootsfound = rootsfound - prev_root_tret = tret(1) - sunvec_y => FN_VMake_Serial(nState, stateVec) + print '(a,f15.7,2x,100(i2,2x))', "time, rootsfound[] = ", tret(1), rootsfound ! Reininitialize solver for running after discontinuity and restart retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAReInit'; return; endif @@ -737,21 +715,20 @@ subroutine setSolverParams(dt,ida_mem,retval) end subroutine setSolverParams ! ---------------------------------------------------------------- -! freezePoint4IDA: The root function routine to find soil freeze +! matZeroPoint4IDA: The root function routine to find soil freeze ! ---------------------------------------------------------------- ! Return values: ! 0 = success, ! 1 = recoverable error, ! -1 = non-recoverable error ! ---------------------------------------------------------------- - integer(c_int) function freezePoint4IDA(t, sunvec_u, sunvec_up, gout, user_data) & - result(ierr) bind(C,name='freezePoint4IDA') + integer(c_int) function matZeroPoint4IDA(t, sunvec_u, sunvec_up, gout, user_data) & + result(ierr) bind(C,name='matZeroPoint4IDA') !======= Inclusions =========== use, intrinsic :: iso_c_binding use fsundials_nvector_mod use fnvector_serial_mod - use soil_utils_module,only:crit_soilT ! compute the critical temperature below which ice exists use globalData,only:integerMissing ! missing integer use var_lookup,only:iLookINDEX ! named variables for structure elements @@ -768,10 +745,7 @@ integer(c_int) function freezePoint4IDA(t, sunvec_u, sunvec_up, gout, user_data) ! local variables integer(i4b) :: i ! index of model layer integer(i4b) :: nState ! number of states - integer(i4b) :: nSnow ! number of snow layers integer(i4b) :: nSoil ! number of soil layers - real(rkind) :: xPsi ! matric head at layer (m) - real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) ! pointers to data in SUNDIALS vectors real(c_double), pointer :: uu(:) @@ -781,10 +755,9 @@ integer(c_int) function freezePoint4IDA(t, sunvec_u, sunvec_up, gout, user_data) ! get equations data from user-defined data call c_f_pointer(user_data, eqns_data) nState = eqns_data%nState - nSnow = eqns_data%nSnow nSoil = eqns_data%nSoil !if (nSoil> 100) then - ! print*, 'error, gout size in freezePoint4IDA function must be increased over 100' + ! print*, 'error, gout size in matZeroPoint4IDA function must be increased over 100' !endif !gout(1:nSoil) = 0._rkind gout(1:1) = 0._rkind @@ -794,24 +767,16 @@ integer(c_int) function freezePoint4IDA(t, sunvec_u, sunvec_up, gout, user_data) ! fill root vector, will not call this function unless nSoil>0 do i=1,1 !nSoil - if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)==integerMissing) cycle - ! get the matric potential of total water - if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing)then - xPsi = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)) - else - xPsi = eqns_data%prog_data%var(iLookPROG%mLayerMatricHead)%dat(i) - endif - - ! identify the critical point when soil begins to freeze (TcSoil) - TcSoil = crit_soilT(xPsi) - gout(i) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) - TcSoil + if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)==integerMissing) cycle + ! identify the critical point when soil has no matrix potential and Tfreeze depends only on temp (0) + gout(i) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)) enddo ! return success ierr = 0 return - end function freezePoint4IDA + end function matZeroPoint4IDA ! ********************************************************************************************************* ! private subroutine implctMelt: compute melt of the "snow without a layer" From b32b88b0322a82c76eea69494e02bb93c5722cb3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 9 Aug 2022 15:09:11 +0900 Subject: [PATCH 0318/1472] Run without the rootfinding, and had a retval where should be retvalr --- build/source/engine/solveByIDA.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index c0ef922f9..c7b13308e 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -353,7 +353,7 @@ subroutine solveByIDA( & ! initialize rootfinding problem with nSoil components if(nSoil>0)then !retval = FIDARootInit(ida_mem, nSoil, c_funloc(matZeroPoint4IDA)) ! all layers - retval = FIDARootInit(ida_mem, 1, c_funloc(matZeroPoint4IDA)) ! try only top layer + !retval = FIDARootInit(ida_mem, 1, c_funloc(matZeroPoint4IDA)) ! try only top layer, uncomment this line to stop at soil discontinuity if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif endif @@ -446,7 +446,7 @@ subroutine solveByIDA( & eqns_data%firstSplitOper = .true. ! call IDASolve, advance solver just one internal step retvalr = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) - if( retval < 0 )then + if( retvalr < 0 )then idaSucceeds = .false. exit endif From 44abf9c84f99b54df6ddb61b9fc63cd95f8b6e46 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 9 Aug 2022 21:00:04 +0900 Subject: [PATCH 0319/1472] Only run rootfinding on top layer of soil and only when there is no snow --- build/source/engine/solveByIDA.f90 | 37 +++++++++++------------------- 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index c7b13308e..609def86d 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -350,10 +350,9 @@ subroutine solveByIDA( & retval = FIDAWFtolerances(ida_mem, c_funloc(computWeight4IDA)) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAWFtolerances'; return; endif - ! initialize rootfinding problem with nSoil components - if(nSoil>0)then - !retval = FIDARootInit(ida_mem, nSoil, c_funloc(matZeroPoint4IDA)) ! all layers - !retval = FIDARootInit(ida_mem, 1, c_funloc(matZeroPoint4IDA)) ! try only top layer, uncomment this line to stop at soil discontinuity + ! initialize rootfinding problem if we have a snow-free ground interfacing with vegetation + if(nSoil>0 .and. nSnow==0 .and. eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(1)==integerMissing)then + retval = FIDARootInit(ida_mem, 1, c_funloc(matZeroPoint4IDA)) !comment this line to not restart at soil surface discontinuity if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif endif @@ -556,12 +555,12 @@ subroutine solveByIDA( & eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial ! Look for where top soil layer crosses the matric head 0 point and changes equation of freezing temperature - if(nSoil>0)then + if(nSoil>0 .and. nSnow==0 .and. eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(1)==integerMissing)then if (retvalr .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) ! rootsfound[i]= +1 indicates that gi is increasing, -1 g[i] decreasing, 0 no root retval = FIDAGetRootInfo(ida_mem, rootsfound) if (retval < 0) then; err=20; message='solveByIDA: error in FIDAGetRootInfo'; return; endif - print '(a,f15.7,2x,100(i2,2x))', "time, rootsfound[] = ", tret(1), rootsfound + print '(a,f15.7,2x,1(i2,2x))', "time, rootsfound[] = ", tret(1), rootsfound ! Reininitialize solver for running after discontinuity and restart retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAReInit'; return; endif @@ -714,14 +713,15 @@ subroutine setSolverParams(dt,ida_mem,retval) end subroutine setSolverParams -! ---------------------------------------------------------------- -! matZeroPoint4IDA: The root function routine to find soil freeze -! ---------------------------------------------------------------- +! ---------------------------------------------------------------------------------------- +! matZeroPoint4IDA: The root function routine to find soil surface matrix potential = 0 +! sundials will not call matZeroPoint4IDA unless this soil index exists and there is no snow +! ---------------------------------------------------------------------------------------- ! Return values: ! 0 = success, ! 1 = recoverable error, ! -1 = non-recoverable error -! ---------------------------------------------------------------- +! ---------------------------------------------------------------------------------------- integer(c_int) function matZeroPoint4IDA(t, sunvec_u, sunvec_up, gout, user_data) & result(ierr) bind(C,name='matZeroPoint4IDA') @@ -739,13 +739,12 @@ integer(c_int) function matZeroPoint4IDA(t, sunvec_u, sunvec_up, gout, user_data real(c_double), value :: t ! current time type(N_Vector) :: sunvec_u ! solution N_Vector type(N_Vector) :: sunvec_up ! derivative N_Vector - real(c_double) :: gout(100) ! root function values HAVE TO DECLARE THIS A SIZE + real(c_double) :: gout(1) ! root function values type(c_ptr), value :: user_data ! user-defined data ! local variables integer(i4b) :: i ! index of model layer integer(i4b) :: nState ! number of states - integer(i4b) :: nSoil ! number of soil layers ! pointers to data in SUNDIALS vectors real(c_double), pointer :: uu(:) @@ -755,22 +754,12 @@ integer(c_int) function matZeroPoint4IDA(t, sunvec_u, sunvec_up, gout, user_data ! get equations data from user-defined data call c_f_pointer(user_data, eqns_data) nState = eqns_data%nState - nSoil = eqns_data%nSoil - !if (nSoil> 100) then - ! print*, 'error, gout size in matZeroPoint4IDA function must be increased over 100' - !endif - !gout(1:nSoil) = 0._rkind - gout(1:1) = 0._rkind ! get data array from SUNDIALS vector uu(1:nState) => FN_VGetArrayPointer(sunvec_u) - ! fill root vector, will not call this function unless nSoil>0 - do i=1,1 !nSoil - if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)==integerMissing) cycle - ! identify the critical point when soil has no matrix potential and Tfreeze depends only on temp (0) - gout(i) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)) - enddo + ! identify the critical point when soil surface matrix potential goes below 0 and Tfreeze depends only on temp + gout(1) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(1)) ! return success ierr = 0 From fc2589eab710ba18658f8fdee99a62a0a0bd1872 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 11 Aug 2022 14:39:04 +0900 Subject: [PATCH 0320/1472] had == where need /= in integerMissing statement so never looked for root. Fixed now, also turned off printing of root so did not fill disk looking for oscillations. --- build/source/engine/solveByIDA.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 609def86d..c698c3f9d 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -351,7 +351,7 @@ subroutine solveByIDA( & if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAWFtolerances'; return; endif ! initialize rootfinding problem if we have a snow-free ground interfacing with vegetation - if(nSoil>0 .and. nSnow==0 .and. eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(1)==integerMissing)then + if(nSoil>0 .and. nSnow==0 .and. eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(1)/=integerMissing)then retval = FIDARootInit(ida_mem, 1, c_funloc(matZeroPoint4IDA)) !comment this line to not restart at soil surface discontinuity if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif endif @@ -555,12 +555,12 @@ subroutine solveByIDA( & eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial ! Look for where top soil layer crosses the matric head 0 point and changes equation of freezing temperature - if(nSoil>0 .and. nSnow==0 .and. eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(1)==integerMissing)then + if(nSoil>0 .and. nSnow==0 .and. eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(1)/=integerMissing)then if (retvalr .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) ! rootsfound[i]= +1 indicates that gi is increasing, -1 g[i] decreasing, 0 no root retval = FIDAGetRootInfo(ida_mem, rootsfound) if (retval < 0) then; err=20; message='solveByIDA: error in FIDAGetRootInfo'; return; endif - print '(a,f15.7,2x,1(i2,2x))', "time, rootsfound[] = ", tret(1), rootsfound + !print '(a,f15.7,2x,1(i2,2x))', "time, rootsfound[] = ", tret(1), rootsfound ! Reininitialize solver for running after discontinuity and restart retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAReInit'; return; endif From 315c15d66a4e5ffa521b775ec5a88beb0a3db017 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 12 Aug 2022 10:44:22 +0900 Subject: [PATCH 0321/1472] putting both water and temp root in --- build/source/engine/solveByIDA.f90 | 44 +++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index c698c3f9d..1b6277d94 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -216,7 +216,7 @@ subroutine solveByIDA( & type(c_ptr) :: ida_mem ! IDA memory type(eqnsData), target :: eqns_data ! IDA type integer(i4b) :: retval, retvalr ! return value - integer(i4b) :: rootsfound(1) ! crossing direction where mLayerMatricHead = 0 in top soil layer + integer(i4b) :: rootsfound(2) ! crossing direction of soil discontinuities logical(lgt) :: feasible ! feasibility flag real(qp) :: t0 ! staring time real(qp) :: dt_last(1) ! last time step @@ -351,8 +351,8 @@ subroutine solveByIDA( & if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAWFtolerances'; return; endif ! initialize rootfinding problem if we have a snow-free ground interfacing with vegetation - if(nSoil>0 .and. nSnow==0 .and. eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(1)/=integerMissing)then - retval = FIDARootInit(ida_mem, 1, c_funloc(matZeroPoint4IDA)) !comment this line to not restart at soil surface discontinuity + if(nSoil>0 .and. nSnow==0)then + retval = FIDARootInit(ida_mem, 2, c_funloc(soilDiscont4IDA)) !comment this line to not restart at soil surface discontinuity if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif endif @@ -555,12 +555,12 @@ subroutine solveByIDA( & eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial ! Look for where top soil layer crosses the matric head 0 point and changes equation of freezing temperature - if(nSoil>0 .and. nSnow==0 .and. eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(1)/=integerMissing)then + if(nSoil>0 .and. nSnow==0)then if (retvalr .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) ! rootsfound[i]= +1 indicates that gi is increasing, -1 g[i] decreasing, 0 no root retval = FIDAGetRootInfo(ida_mem, rootsfound) if (retval < 0) then; err=20; message='solveByIDA: error in FIDAGetRootInfo'; return; endif - !print '(a,f15.7,2x,1(i2,2x))', "time, rootsfound[] = ", tret(1), rootsfound + !print '(a,f15.7,2x,2(i2,2x))', "time, rootsfound[] = ", tret(1), rootsfound ! Reininitialize solver for running after discontinuity and restart retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAReInit'; return; endif @@ -714,21 +714,23 @@ subroutine setSolverParams(dt,ida_mem,retval) end subroutine setSolverParams ! ---------------------------------------------------------------------------------------- -! matZeroPoint4IDA: The root function routine to find soil surface matrix potential = 0 -! sundials will not call matZeroPoint4IDA unless this soil index exists and there is no snow +! soilDiscont4IDA: The root function routine to find soil surface matrix potential = 0 and +! T = Tfreeze +! sundials will not call soilDiscont4IDA unless this soil index exists and there is no snow ! ---------------------------------------------------------------------------------------- ! Return values: ! 0 = success, ! 1 = recoverable error, ! -1 = non-recoverable error ! ---------------------------------------------------------------------------------------- - integer(c_int) function matZeroPoint4IDA(t, sunvec_u, sunvec_up, gout, user_data) & - result(ierr) bind(C,name='matZeroPoint4IDA') + integer(c_int) function soilDiscont4IDA(t, sunvec_u, sunvec_up, gout, user_data) & + result(ierr) bind(C,name='soilDiscont4IDA') !======= Inclusions =========== use, intrinsic :: iso_c_binding use fsundials_nvector_mod use fnvector_serial_mod + use soil_utils_module,only:crit_soilT ! compute the critical temperature below which ice exists use globalData,only:integerMissing ! missing integer use var_lookup,only:iLookINDEX ! named variables for structure elements @@ -739,12 +741,14 @@ integer(c_int) function matZeroPoint4IDA(t, sunvec_u, sunvec_up, gout, user_data real(c_double), value :: t ! current time type(N_Vector) :: sunvec_u ! solution N_Vector type(N_Vector) :: sunvec_up ! derivative N_Vector - real(c_double) :: gout(1) ! root function values + real(c_double) :: gout(2) ! root function values type(c_ptr), value :: user_data ! user-defined data ! local variables - integer(i4b) :: i ! index of model layer integer(i4b) :: nState ! number of states + real(rkind) :: xPsi ! matric head at layer (m) + real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) + ! pointers to data in SUNDIALS vectors real(c_double), pointer :: uu(:) @@ -759,13 +763,27 @@ integer(c_int) function matZeroPoint4IDA(t, sunvec_u, sunvec_up, gout, user_data uu(1:nState) => FN_VGetArrayPointer(sunvec_u) ! identify the critical point when soil surface matrix potential goes below 0 and Tfreeze depends only on temp - gout(1) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(1)) + if (eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(1)/=integerMissing)then + xPsi = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(1)) + gout(1) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(1)) + else + xPsi = eqns_data%prog_data%var(iLookPROG%mLayerMatricHead)%dat(1) + gout(1) = 0._rkind + endif + + ! identify the critical point when soil begins to freeze (TcSoil) + if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(1)/=integerMissing) then + TcSoil = crit_soilT(xPsi) + gout(2) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(1)) - TcSoil + else + gout(2) = 0._rkind + endif ! return success ierr = 0 return - end function matZeroPoint4IDA + end function soilDiscont4IDA ! ********************************************************************************************************* ! private subroutine implctMelt: compute melt of the "snow without a layer" From 19c9bc71445e1dffb4bd8d73b376096e2d3a7112 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 18 Aug 2022 23:43:02 +0900 Subject: [PATCH 0322/1472] Turn off rootfinding and move longwave balance check to only happen outside Sundials loop. Also set satVapPress to be 0 if temp is very negative so does not go to infinity. --- build/source/engine/computFlux.f90 | 3 ++ build/source/engine/conv_funcs.f90 | 11 +++-- build/source/engine/eval8DAE.f90 | 11 +++-- build/source/engine/eval8JacDAE.f90 | 4 +- build/source/engine/eval8summa.f90 | 1 + build/source/engine/evalDAE4IDA.f90 | 3 +- build/source/engine/solveByIDA.f90 | 37 ++++++++------- build/source/engine/varExtrSundials.f90 | 27 +++++------ build/source/engine/varSubstepSundials.f90 | 41 +++++++++-------- build/source/engine/vegNrgFlux.f90 | 52 ++++++++++++---------- 10 files changed, 107 insertions(+), 83 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 9355d1fa9..61d53a3e8 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -114,6 +114,7 @@ subroutine computFlux(& firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution + requireLWBal, & ! intent(in): flag to indicate if we need longwave to be balanced drainageMeltPond, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) ! input: state variables scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) @@ -167,6 +168,7 @@ subroutine computFlux(& logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + logical(lgt),intent(in) :: requireLWBal ! flag to indicate if we need longwave to be balanced real(rkind),intent(in) :: drainageMeltPond ! drainage from the surface melt pond (kg m-2 s-1) ! input: state variables real(rkind),intent(in) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) @@ -441,6 +443,7 @@ subroutine computFlux(& firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + requireLWBal, & ! intent(in): flag to indicate if we need longwave to be balanced ! input: model state variables upperBoundTemp, & ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature scalarCanairTempTrial, & ! intent(in): trial value of the canopy air space temperature (K) diff --git a/build/source/engine/conv_funcs.f90 b/build/source/engine/conv_funcs.f90 index 9c7891cf1..10ac07f69 100644 --- a/build/source/engine/conv_funcs.f90 +++ b/build/source/engine/conv_funcs.f90 @@ -86,9 +86,14 @@ subroutine satVapPress(TC, SVP, dSVP_dT) ! Units note : Pa = N m-2 = kg m-1 s-2 ! SATVPFRZ= 610.8 ! Saturation water vapour pressure at 273.16K (Pa) -SVP = SATVPFRZ * EXP( (X1*TC)/(X2 + TC) ) ! Saturated Vapour Press (Pa) -dSVP_dT = SVP * (X1/(X2 + TC) - X1*TC/(X2 + TC)**2._rkind) -if(X2 + TC < 0) print*, "error, canopy temperature is very low, satVapPress=Inf" !will fail as SVP=inf +if(X2 + TC < 0)then + !print*, "error, canopy temperature is very low, satVapPress=Inf" !will fail as SVP=inf + SVP = 0._rkind + dSVP_dT = 0._rkind +else + SVP = SATVPFRZ * EXP( (X1*TC)/(X2 + TC) ) ! Saturated Vapour Press (Pa) + dSVP_dT = SVP * (X1/(X2 + TC) - X1*TC/(X2 + TC)**2._rkind) +end if if(testDeriv) print*, 'dSVP_dT check... ', SVP, dSVP_dT, (SATVPRESS(TC+dx) - SVP)/dx END SUBROUTINE satVapPress diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index 9b8f6676f..c5e5d0ce7 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -98,10 +98,11 @@ subroutine eval8DAE(& nState, & ! intent(in): total number of state variables checkFeas, & ! intent(in): flag to indicate if we are checking for feasibility firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout) flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(inout) flag to indicate if we are processing the first flux call in a splitting operation + firstFluxCall, & ! intent(inout) flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(inout) flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution + requireLWBal, & ! intent(in): flag to indicate if we need longwave to be balanced ! input: state vectors stateVec, & ! intent(in): model state vector stateVecPrime, & ! intent(in): derivative of model state vector @@ -153,7 +154,7 @@ subroutine eval8DAE(& err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- ! provide access to subroutines - USE varExtrSundials_module, only:varExtract ! extract variables from the state vector + USE varExtrSundials_module, only:varExtract2 ! extract variables from the state vector USE varExtrSundials_module, only:varExtractSundials USE updateVarsSundials_module, only:updateVarsSundials ! update variables USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy @@ -183,6 +184,7 @@ subroutine eval8DAE(& logical(lgt),intent(inout) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + logical(lgt),intent(in) :: requireLWBal ! flag to indicate if we need longwave to be balanced ! input: state vectors real(rkind),intent(in) :: stateVec(:) ! model state vector real(rkind),intent(in) :: stateVecPrime(:) ! model state vector @@ -410,7 +412,7 @@ subroutine eval8DAE(& scalarAquiferStorageTrial = scalarAquiferStoragePrev ! extract variables from the model state vector - call varExtract(& + call varExtract2(& ! input stateVec, & ! intent(in): model state vector (mixed units) diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -657,6 +659,7 @@ subroutine eval8DAE(& firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution + requireLWBal, & ! intent(in): flag to indicate if we need longwave to be balanced scalarSfcMeltPond/dt, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) ! input: state variables scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) diff --git a/build/source/engine/eval8JacDAE.f90 b/build/source/engine/eval8JacDAE.f90 index 8bb13e444..f407581ee 100644 --- a/build/source/engine/eval8JacDAE.f90 +++ b/build/source/engine/eval8JacDAE.f90 @@ -117,7 +117,7 @@ subroutine eval8JacDAE(& err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- ! provide access to subroutines - USE varExtrSundials_module, only:varExtract ! extract variables from the state vector + USE varExtrSundials_module, only:varExtract2 ! extract variables from the state vector USE varExtrSundials_module, only:varExtractSundials USE updateVars4JacDAE_module, only:updateVars4JacDAE ! update prognostic variables USE computJacDAE_module,only:computJacDAE @@ -209,7 +209,7 @@ subroutine eval8JacDAE(& ! extract variables from the model state vector - call varExtract(& + call varExtract2(& ! input stateVec, & ! intent(in): model state vector (mixed units) diag_data, & ! intent(in): model diagnostic variables for a local HRU diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 2feba21f7..35f608d11 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -460,6 +460,7 @@ subroutine eval8summa(& firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution + .true., & ! intent(in): balance longwave scalarSfcMeltPond/dt, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) ! input: state variables scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) diff --git a/build/source/engine/evalDAE4IDA.f90 b/build/source/engine/evalDAE4IDA.f90 index e02b57f32..349d364d9 100644 --- a/build/source/engine/evalDAE4IDA.f90 +++ b/build/source/engine/evalDAE4IDA.f90 @@ -98,9 +98,10 @@ integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_da .false., & ! intent(in): do not check for feasibility inside Sundials loop eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation + eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution + .false., & ! intent(in): do not require that longwave is balanced inside Sundials loop ! input: state vectors stateVec, & ! intent(in): model state vector stateVecPrime, & ! intent(in): model state vector diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 1b6277d94..75d3a649f 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -216,7 +216,7 @@ subroutine solveByIDA( & type(c_ptr) :: ida_mem ! IDA memory type(eqnsData), target :: eqns_data ! IDA type integer(i4b) :: retval, retvalr ! return value - integer(i4b) :: rootsfound(2) ! crossing direction of soil discontinuities + integer(i4b) :: rootsfound(3) ! crossing direction of discontinuities logical(lgt) :: feasible ! feasibility flag real(qp) :: t0 ! staring time real(qp) :: dt_last(1) ! last time step @@ -351,8 +351,8 @@ subroutine solveByIDA( & if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAWFtolerances'; return; endif ! initialize rootfinding problem if we have a snow-free ground interfacing with vegetation - if(nSoil>0 .and. nSnow==0)then - retval = FIDARootInit(ida_mem, 2, c_funloc(soilDiscont4IDA)) !comment this line to not restart at soil surface discontinuity + if(nSoil>0 .and. nSnow==0 .and. indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then + !retval = FIDARootInit(ida_mem, 3, c_funloc(vegsoilDisC4IDA)) !uncomment this line to restart at discontinuities if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif endif @@ -477,6 +477,7 @@ subroutine solveByIDA( & eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution + .true., & ! intent(in): require that longwave is balanced once outside Sundials loop ! input: state vectors stateVec, & ! intent(in): model state vector stateVecPrime, & ! intent(in): model state vector @@ -555,7 +556,7 @@ subroutine solveByIDA( & eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial ! Look for where top soil layer crosses the matric head 0 point and changes equation of freezing temperature - if(nSoil>0 .and. nSnow==0)then + if(nSoil>0 .and. nSnow==0 .and. indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then if (retvalr .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) ! rootsfound[i]= +1 indicates that gi is increasing, -1 g[i] decreasing, 0 no root retval = FIDAGetRootInfo(ida_mem, rootsfound) @@ -714,17 +715,17 @@ subroutine setSolverParams(dt,ida_mem,retval) end subroutine setSolverParams ! ---------------------------------------------------------------------------------------- -! soilDiscont4IDA: The root function routine to find soil surface matrix potential = 0 and -! T = Tfreeze -! sundials will not call soilDiscont4IDA unless this soil index exists and there is no snow +! vegsoilDisC4IDA: The root function routine to find soil surface matrix potential = 0, +! soil surface temp = critical frozen point, and veg temp = Tfreeze +! sundials will not call vegsoilDisC4IDA unless this soil index exists and there is no snow ! ---------------------------------------------------------------------------------------- ! Return values: ! 0 = success, ! 1 = recoverable error, ! -1 = non-recoverable error ! ---------------------------------------------------------------------------------------- - integer(c_int) function soilDiscont4IDA(t, sunvec_u, sunvec_up, gout, user_data) & - result(ierr) bind(C,name='soilDiscont4IDA') + integer(c_int) function vegsoilDisC4IDA(t, sunvec_u, sunvec_up, gout, user_data) & + result(ierr) bind(C,name='vegsoilDisC4IDA') !======= Inclusions =========== use, intrinsic :: iso_c_binding @@ -733,6 +734,7 @@ integer(c_int) function soilDiscont4IDA(t, sunvec_u, sunvec_up, gout, user_data) use soil_utils_module,only:crit_soilT ! compute the critical temperature below which ice exists use globalData,only:integerMissing ! missing integer use var_lookup,only:iLookINDEX ! named variables for structure elements + use multiconst,only:Tfreeze ! freezing point of pure water (K) !======= Declarations ========= implicit none @@ -741,7 +743,7 @@ integer(c_int) function soilDiscont4IDA(t, sunvec_u, sunvec_up, gout, user_data) real(c_double), value :: t ! current time type(N_Vector) :: sunvec_u ! solution N_Vector type(N_Vector) :: sunvec_up ! derivative N_Vector - real(c_double) :: gout(2) ! root function values + real(c_double) :: gout(3) ! root function values type(c_ptr), value :: user_data ! user-defined data ! local variables @@ -761,6 +763,7 @@ integer(c_int) function soilDiscont4IDA(t, sunvec_u, sunvec_up, gout, user_data) ! get data array from SUNDIALS vector uu(1:nState) => FN_VGetArrayPointer(sunvec_u) + gout = 0._rkind ! identify the critical point when soil surface matrix potential goes below 0 and Tfreeze depends only on temp if (eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(1)/=integerMissing)then @@ -768,22 +771,24 @@ integer(c_int) function soilDiscont4IDA(t, sunvec_u, sunvec_up, gout, user_data) gout(1) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(1)) else xPsi = eqns_data%prog_data%var(iLookPROG%mLayerMatricHead)%dat(1) - gout(1) = 0._rkind endif - ! identify the critical point when soil begins to freeze (TcSoil) - if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(1)/=integerMissing) then + ! identify the critical point when soil surface begins to freeze (TcSoil) + if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(1)/=integerMissing)then TcSoil = crit_soilT(xPsi) gout(2) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(1)) - TcSoil - else - gout(2) = 0._rkind + endif + + ! identify the critical point when vegetation begins to freeze + if(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then + gout(3) = uu(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)) - Tfreeze endif ! return success ierr = 0 return - end function soilDiscont4IDA + end function vegsoilDisC4IDA ! ********************************************************************************************************* ! private subroutine implctMelt: compute melt of the "snow without a layer" diff --git a/build/source/engine/varExtrSundials.f90 b/build/source/engine/varExtrSundials.f90 index 747c7ad07..0760b5125 100644 --- a/build/source/engine/varExtrSundials.f90 +++ b/build/source/engine/varExtrSundials.f90 @@ -74,7 +74,7 @@ module varExtrSundials_module implicit none private -public::varExtract +public::varExtract2 public::varExtractSundials public::residDiscontinuity public::countDiscontinuity @@ -84,9 +84,10 @@ module varExtrSundials_module ! ********************************************************************************************************** - ! public subroutine varExtract: extract variables from the state vector and compute diagnostic variables + ! public subroutine varExtract2: extract variables from the state vector and compute diagnostic variables + ! This routine does not initialize any of the variables and is to be used inside the Sundials iteration, vs varExtract ! ********************************************************************************************************** - subroutine varExtract(& + subroutine varExtract2(& ! input stateVec, & ! intent(in): model state vector (mixed units) diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -159,7 +160,7 @@ subroutine varExtract(& ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message='varExtract/' + err=0; message='varExtract2/' ! *** extract state variables for the vegetation canopy @@ -212,10 +213,10 @@ subroutine varExtract(& end associate - end subroutine varExtract - + end subroutine varExtract2 + ! ********************************************************************************************************** - ! public subroutine varExtractSundials: extract variables from the state vector and compute diagnostic variables + ! public subroutine varExtractSundials: extract prime variables from the state vector and compute diagnostic variables ! ********************************************************************************************************** subroutine varExtractSundials(& ! input @@ -343,7 +344,7 @@ end subroutine varExtractSundials ! ********************************************************************************************************** - ! public subroutine residDiscontinuity: + ! public subroutine residDiscontinuity: ! ********************************************************************************************************** subroutine residDiscontinuity(& ! input @@ -352,7 +353,7 @@ subroutine residDiscontinuity(& prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output - resid, & ! intent(out) + resid, & ! intent(out) err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- @@ -426,12 +427,12 @@ subroutine residDiscontinuity(& endif end associate - + end subroutine residDiscontinuity - + ! ********************************************************************************************************** - ! public subroutine countDiscontinuity: + ! public subroutine countDiscontinuity: ! ********************************************************************************************************** subroutine countDiscontinuity(& ! input @@ -440,7 +441,7 @@ subroutine countDiscontinuity(& prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output - countD, & ! intent(out) + countD, & ! intent(out) err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index 9bb02588e..6ad53569a 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -865,17 +865,17 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial if(abs(liqError) > absConvTol_liquid*10._rkind*iden_water)then ! *10 because of precision issues - write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 - write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 - write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt - write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt - write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt - write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - write(*,'(a,1x,f20.10)') 'liqError = ', liqError - waterBalanceError = .true. - return + !write(*,'(a,1x,f20.10)') 'dt = ', dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + !write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 + !write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 + !write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt + !write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt + !write(*,'(a,1x,f20.10)') 'liqError = ', liqError + waterBalanceError = .true. + return endif ! if there is a water balance error endif ! if veg canopy @@ -888,17 +888,16 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) - ! write(1,*) liqError if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues - write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 - write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 - write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux - write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink - write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink - write(*,'(a,1x,f20.10)') 'compSink = ', compSink - write(*,'(a,1x,f20.10)') 'liqError = ', liqError - write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid + !write(*,'(a,1x,f20.10)') 'dt = ', dt + !write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 + !write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 + !write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux + !write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink + !write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink + !write(*,'(a,1x,f20.10)') 'compSink = ', compSink + !write(*,'(a,1x,f20.10)') 'liqError = ', liqError + !write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid waterBalanceError = .true. return endif ! if there is a water balance error diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 6de454a9c..2ee7c93b1 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -131,6 +131,7 @@ subroutine vegNrgFlux(& firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + requireLWBal, & ! intent(in): flag to indicate if we need longwave to be balanced ! input: model state variables upperBoundTemp, & ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature @@ -220,7 +221,7 @@ subroutine vegNrgFlux(& logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(in) :: firstFluxCall ! flag to indicate if we are processing the first flux call logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - + logical(lgt),intent(in) :: requireLWBal ! flag to indicate if we need longwave to be balanced ! input: model state variables real(rkind),intent(in) :: upperBoundTemp ! temperature of the upper boundary (K) --> NOTE: use air temperature real(rkind),intent(in) :: canairTempTrial ! trial value of canopy air space temperature (K) @@ -984,6 +985,7 @@ subroutine vegNrgFlux(& ! input: model control ixDerivMethod, & ! intent(in): method used to calculate flux derivatives computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation + requireLWBal, & ! intent(in): flag to indicate if we need longwave to be balanced ! input: canopy and ground temperature canopyTempTrial, & ! intent(in): temperature of the vegetation canopy (K) groundTempTrial, & ! intent(in): temperature of the ground surface (K) @@ -1778,7 +1780,8 @@ end subroutine logisticSmoother subroutine longwaveBal(& ! input: model control ixDerivMethod, & ! intent(in): choice of method used to compute derivative (analytical or numerical) - computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation + computeVegFlux, & ! intent(in): flag to compute fluxes over vegetati + requireLWBal, & ! intent(in): flag to indicate if we need longwave to be balanced ! input: canopy and ground temperature canopyTemp, & ! intent(in): canopy temperature (K) groundTemp, & ! intent(in): ground temperature (K) @@ -1815,6 +1818,7 @@ subroutine longwaveBal(& ! input: model control integer(i4b),intent(in) :: ixDerivMethod ! choice of method used to compute derivative (analytical or numerical) logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: requireLWBal ! flag to indicate if we need longwave to be balanced ! input: canopy and ground temperature real(rkind),intent(in) :: canopyTemp ! canopy temperature (K) real(rkind),intent(in) :: groundTemp ! ground temperature (K) @@ -1948,27 +1952,29 @@ subroutine longwaveBal(& !print*, 'LWNetUbound = ', LWNetUbound ! check the flux balance - fluxBalance = LWNetUbound - (LWNetCanopy + LWNetGround) - if(abs(fluxBalance) > fluxTolerance)then - print*, 'fluxBalance = ', fluxBalance - print*, 'emg, emc = ', emg, emc - print*, 'TCan, TGnd = ', TCan, TGnd - print*, 'LWRadUbound = ', LWRadUbound - print*, 'LWRadCanopy = ', LWRadCanopy - print*, 'LWRadGround = ', LWRadGround - print*, 'LWRadUbound2Canopy = ', LWRadUbound2Canopy - print*, 'LWRadUbound2Ground = ', LWRadUbound2Ground - print*, 'LWRadUbound2Ubound = ', LWRadUbound2Ubound - print*, 'LWRadCanopy2Ubound = ', LWRadCanopy2Ubound - print*, 'LWRadCanopy2Ground = ', LWRadCanopy2Ground - print*, 'LWRadCanopy2Canopy = ', LWRadCanopy2Canopy - print*, 'LWRadGround2Ubound = ', LWRadGround2Ubound - print*, 'LWRadGround2Canopy = ', LWRadGround2Canopy - print*, 'LWNetCanopy = ', LWNetCanopy - print*, 'LWNetGround = ', LWNetGround - print*, 'LWNetUbound = ', LWNetUbound - message=trim(message)//'flux imbalance' - err=20; return + if(requireLWBal)then + fluxBalance = LWNetUbound - (LWNetCanopy + LWNetGround) + if(abs(fluxBalance) > fluxTolerance)then + print*, 'fluxBalance = ', fluxBalance + print*, 'emg, emc = ', emg, emc + print*, 'TCan, TGnd = ', TCan, TGnd + print*, 'LWRadUbound = ', LWRadUbound + print*, 'LWRadCanopy = ', LWRadCanopy + print*, 'LWRadGround = ', LWRadGround + print*, 'LWRadUbound2Canopy = ', LWRadUbound2Canopy + print*, 'LWRadUbound2Ground = ', LWRadUbound2Ground + print*, 'LWRadUbound2Ubound = ', LWRadUbound2Ubound + print*, 'LWRadCanopy2Ubound = ', LWRadCanopy2Ubound + print*, 'LWRadCanopy2Ground = ', LWRadCanopy2Ground + print*, 'LWRadCanopy2Canopy = ', LWRadCanopy2Canopy + print*, 'LWRadGround2Ubound = ', LWRadGround2Ubound + print*, 'LWRadGround2Canopy = ', LWRadGround2Canopy + print*, 'LWNetCanopy = ', LWNetCanopy + print*, 'LWNetGround = ', LWNetGround + print*, 'LWNetUbound = ', LWNetUbound + message=trim(message)//'flux imbalance' + err=20; return + end if end if ! -------------------------------------------------------------------------------------- From ce60d55d039132ef6db21db9813268cc27fad2f3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 25 Aug 2022 10:13:52 +0900 Subject: [PATCH 0323/1472] Fixing soilCmprs, should use matric potential not liquid matric potential. Also changed variable name inside soilLiqFlux for consistency --- build/source/engine/eval8DAE.f90 | 2 +- build/source/engine/eval8summa.f90 | 6 +-- build/source/engine/soilLiqFlx.f90 | 70 +++++++++++++++--------------- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index c5e5d0ce7..a83f8acda 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -703,7 +703,7 @@ subroutine eval8DAE(& ! input: ixRichards, & ! intent(in): choice of option for Richards' equation ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers - mLayerMatricHeadLiqPrime(1:nSoil), & ! intent(in): matric head at the start of the time step (m) + mLayerMatricHeadPrime(1:nSoil), & ! intent(in): matric head at the start of the time step (m) mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric ice content in each soil layer (-) specificStorage, & ! intent(in): specific storage coefficient (m-1) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 35f608d11..da802fc61 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -245,7 +245,7 @@ subroutine eval8summa(& scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] matric potential (m) ! model diagnostic variables scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) @@ -502,8 +502,8 @@ subroutine eval8summa(& ! input: ixRichards, & ! intent(in): choice of option for Richards' equation ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers - mLayerMatricHeadLiq(1:nSoil), & ! intent(in): matric head at the start of the time step (m) - mLayerMatricHeadLiqTrial(1:nSoil), & ! intent(in): trial value of matric head (m) + mLayerMatricHead(1:nSoil), & ! intent(in): matric head at the start of the time step (m) + mLayerMatricHeadTrial(1:nSoil), & ! intent(in): trial value of matric head (m) mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric ice content in each soil layer (-) specificStorage, & ! intent(in): specific storage coefficient (m-1) diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index 96af858d1..1d3c59687 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -96,7 +96,7 @@ subroutine soilLiqFlx(& deriv_desired, & ! intent(in): flag indicating if derivatives are desired ! input: trial state variables mLayerTempTrial, & ! intent(in): temperature (K) - mLayerMatricHeadTrial, & ! intent(in): matric head (m) + mLayerMatricHeadLiqTrial, & ! intent(in): liquid matric head (m) mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water (-) mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice (-) ! input: pre-computed derivatives @@ -165,7 +165,7 @@ subroutine soilLiqFlx(& logical(lgt),intent(in) :: deriv_desired ! flag indicating if derivatives are desired ! input: trial model state variables real(rkind),intent(in) :: mLayerTempTrial(:) ! temperature in each layer at the current iteration (m) - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! matric head in each layer at the current iteration (m) + real(rkind),intent(in) :: mLayerMatricHeadLiqTrial(:) ! liquid matric head in each layer at the current iteration (m) real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! volumetric fraction of liquid water at the current iteration (-) real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! volumetric fraction of ice at the current iteration (-) ! input: pre-computed derivatves @@ -237,7 +237,7 @@ subroutine soilLiqFlx(& integer(i4b) :: ixPerturb ! index of element in 2-element vector to perturb integer(i4b) :: ixOriginal ! index of perturbed element in the original vector real(rkind) :: scalarVolFracLiqTrial ! trial value of volumetric liquid water content (-) - real(rkind) :: scalarMatricHeadTrial ! trial value of matric head (m) + real(rkind) :: scalarMatricHeadLiqTrial ! trial value of liquid matric head (m) real(rkind) :: scalarHydCondTrial ! trial value of hydraulic conductivity (m s-1) real(rkind) :: scalarHydCondMicro ! trial value of hydraulic conductivity of micropores (m s-1) real(rkind) :: scalarHydCondMacro ! trial value of hydraulic conductivity of macropores (m s-1) @@ -260,7 +260,7 @@ subroutine soilLiqFlx(& real(rkind),dimension(0:nSoil) :: iLayerHeight ! height of the layer interfaces (m) ! compute fluxes and derivatives at layer interfaces real(rkind),dimension(2) :: vectorVolFracLiqTrial ! trial value of volumetric liquid water content (-) - real(rkind),dimension(2) :: vectorMatricHeadTrial ! trial value of matric head (m) + real(rkind),dimension(2) :: vectorMatricHeadLiqTrial ! trial value of liquid matric head (m) real(rkind),dimension(2) :: vectorHydCondTrial ! trial value of hydraulic conductivity (m s-1) real(rkind),dimension(2) :: vectorDiffuseTrial ! trial value of hydraulic diffusivity (m2 s-1) real(rkind) :: scalardPsi_dTheta ! derivative in soil water characteristix, used for perturbations when computing numerical derivatives @@ -431,7 +431,7 @@ subroutine soilLiqFlx(& ixRichards, & ! intent(in): index defining the option for Richards' equation (moisture or mixdform) ! input: state variables mLayerTempTrial(iSoil), & ! intent(in): temperature (K) - mLayerMatricHeadTrial(iSoil), & ! intent(in): matric head in each layer (m) + mLayerMatricHeadLiqTrial(iSoil), & ! intent(in): liquid matric head in each layer (m) mLayerVolFracLiqTrial(iSoil), & ! intent(in): volumetric liquid water content in each soil layer (-) mLayerVolFracIceTrial(iSoil), & ! intent(in): volumetric ice content in each soil layer (-) ! input: pre-computed deriavatives @@ -495,7 +495,7 @@ subroutine soilLiqFlx(& ! un-perturbed case case(unperturbed) scalarVolFracLiqTrial = mLayerVolFracLiqTrial(1) - scalarMatricHeadTrial = mLayerMatricHeadTrial(1) + scalarMatricHeadLiqTrial = mLayerMatricHeadLiqTrial(1) ! perturb soil state (one-sided finite differences) case(perturbStateBelow) @@ -503,10 +503,10 @@ subroutine soilLiqFlx(& select case(ixRichards) case(moisture) scalarVolFracLiqTrial = mLayerVolFracLiqTrial(1) + dx - scalarMatricHeadTrial = mLayerMatricHeadTrial(1) + scalarMatricHeadLiqTrial = mLayerMatricHeadLiqTrial(1) case(mixdform) scalarVolFracLiqTrial = mLayerVolFracLiqTrial(1) - scalarMatricHeadTrial = mLayerMatricHeadTrial(1) + dx + scalarMatricHeadLiqTrial = mLayerMatricHeadLiqTrial(1) + dx case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return end select ! (form of Richards' equation ! check for an unknown perturbation @@ -529,8 +529,8 @@ subroutine soilLiqFlx(& nSoil, & ! intent(in): number of soil layers ! input: state variables mLayerTempTrial, & ! intent(in): temperature (K) - scalarMatricHeadTrial, & ! intent(in): matric head in the upper-most soil layer (m) - mLayerMatricHeadTrial, & ! intent(in): matric head in each soil layer (m) + scalarMatricHeadLiqTrial, & ! intent(in): liquid matric head in the upper-most soil layer (m) + mLayerMatricHeadLiqTrial, & ! intent(in): liquid matric head in each soil layer (m) scalarVolFracLiqTrial, & ! intent(in): volumetric liquid water content the upper-most soil layer (-) mLayerVolFracLiqTrial, & ! intent(in): volumetric liquid water content in each soil layer (-) mLayerVolFracIceTrial, & ! intent(in): volumetric ice content in each soil layer (-) @@ -644,12 +644,12 @@ subroutine soilLiqFlx(& ! ============================ ! start with the un-perturbed case vectorVolFracLiqTrial(1:2) = mLayerVolFracLiqTrial(iLayer:iLayer+1) - vectorMatricHeadTrial(1:2) = mLayerMatricHeadTrial(iLayer:iLayer+1) + vectorMatricHeadLiqTrial(1:2) = mLayerMatricHeadLiqTrial(iLayer:iLayer+1) ! make appropriate perturbations if(ixPerturb > 0)then select case(ixRichards) case(moisture); vectorVolFracLiqTrial(ixPerturb) = vectorVolFracLiqTrial(ixPerturb) + dx - case(mixdform); vectorMatricHeadTrial(ixPerturb) = vectorMatricHeadTrial(ixPerturb) + dx + case(mixdform); vectorMatricHeadLiqTrial(ixPerturb) = vectorMatricHeadLiqTrial(ixPerturb) + dx case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return end select ! (form of Richards' equation) end if @@ -668,8 +668,8 @@ subroutine soilLiqFlx(& vectorHydCondTrial(ixPerturb) = hydCond_liq(vectorVolFracLiqTrial(ixPerturb),mLayerSatHydCond(ixOriginal),theta_res(ixPerturb),theta_sat(ixPerturb),vGn_m(ixPerturb)) * iceImpedeFac(ixOriginal) vectorDiffuseTrial(ixPerturb) = scalardPsi_dTheta * vectorHydCondTrial(ixPerturb) case(mixdform) - scalarVolFracLiqTrial = volFracLiq(vectorMatricHeadTrial(ixPerturb),vGn_alpha(ixPerturb),theta_res(ixPerturb),theta_sat(ixPerturb),vGn_n(ixPerturb),vGn_m(ixPerturb)) - scalarHydCondMicro = hydCond_psi(vectorMatricHeadTrial(ixPerturb),mLayerSatHydCond(ixOriginal),vGn_alpha(ixPerturb),vGn_n(ixPerturb),vGn_m(ixPerturb)) * iceImpedeFac(ixOriginal) + scalarVolFracLiqTrial = volFracLiq(vectorMatricHeadLiqTrial(ixPerturb),vGn_alpha(ixPerturb),theta_res(ixPerturb),theta_sat(ixPerturb),vGn_n(ixPerturb),vGn_m(ixPerturb)) + scalarHydCondMicro = hydCond_psi(vectorMatricHeadLiqTrial(ixPerturb),mLayerSatHydCond(ixOriginal),vGn_alpha(ixPerturb),vGn_n(ixPerturb),vGn_m(ixPerturb)) * iceImpedeFac(ixOriginal) scalarHydCondMacro = hydCondMP_liq(scalarVolFracLiqTrial,theta_sat(ixPerturb),theta_mp,mpExp,mLayerSatHydCondMP(ixOriginal),mLayerSatHydCond(ixOriginal)) vectorHydCondTrial(ixPerturb) = scalarHydCondMicro + scalarHydCondMacro case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return @@ -687,7 +687,7 @@ subroutine soilLiqFlx(& desireAnal, & ! intent(in): flag indicating if derivatives are desired ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) ! input: state variables (adjacent layers) - vectorMatricHeadTrial, & ! intent(in): matric head at the soil nodes (m) + vectorMatricHeadLiqTrial, & ! intent(in): liquid matric head at the soil nodes (m) vectorVolFracLiqTrial, & ! intent(in): volumetric liquid water content at the soil nodes (-) ! input: model coordinate variables (adjacent layers) mLayerHeight(iLayer:iLayer+1), & ! intent(in): height of the soil nodes (m) @@ -735,8 +735,8 @@ subroutine soilLiqFlx(& end if ! check - !if(iLayer==6) write(*,'(a,i4,1x,10(e25.15,1x))') 'iLayer, vectorMatricHeadTrial, iLayerHydCond(iLayer), iLayerLiqFluxSoil(iLayer) = ',& - ! iLayer, vectorMatricHeadTrial, iLayerHydCond(iLayer), iLayerLiqFluxSoil(iLayer) + !if(iLayer==6) write(*,'(a,i4,1x,10(e25.15,1x))') 'iLayer, vectorMatricHeadLiqTrial, iLayerHydCond(iLayer), iLayerLiqFluxSoil(iLayer) = ',& + ! iLayer, vectorMatricHeadLiqTrial, iLayerHydCond(iLayer), iLayerLiqFluxSoil(iLayer) !if(iLayer==1) write(*,'(a,i4,1x,L1,1x,2(e15.5,1x))') 'iLayer, (ixDerivMethod==numerical), dq_dHydStateBelow(iLayer-1), dq_dHydStateAbove(iLayer) = ', & ! iLayer, (ixDerivMethod==numerical), dq_dHydStateBelow(iLayer-1), dq_dHydStateAbove(iLayer) !pause @@ -773,17 +773,17 @@ subroutine soilLiqFlx(& ! un-perturbed case case(unperturbed) scalarVolFracLiqTrial = mLayerVolFracLiqTrial(nSoil) - scalarMatricHeadTrial = mLayerMatricHeadTrial(nSoil) + scalarMatricHeadLiqTrial = mLayerMatricHeadLiqTrial(nSoil) ! perturb soil state (one-sided finite differences) case(perturbStateAbove) select case(ixRichards) ! (perturbation depends on the form of Richards' equation) case(moisture) scalarVolFracLiqTrial = mLayerVolFracLiqTrial(nSoil) + dx - scalarMatricHeadTrial = mLayerMatricHeadTrial(nSoil) + scalarMatricHeadLiqTrial = mLayerMatricHeadLiqTrial(nSoil) case(mixdform) scalarVolFracLiqTrial = mLayerVolFracLiqTrial(nSoil) - scalarMatricHeadTrial = mLayerMatricHeadTrial(nSoil) + dx + scalarMatricHeadLiqTrial = mLayerMatricHeadLiqTrial(nSoil) + dx case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return end select ! (form of Richards' equation) @@ -798,7 +798,7 @@ subroutine soilLiqFlx(& case(perturbStateAbove) select case(ixRichards) case(moisture); scalarHydCondTrial = hydCond_liq(scalarVolFracLiqTrial,mLayerSatHydCond(nSoil),theta_res(nSoil),theta_sat(nSoil),vGn_m(nSoil)) * iceImpedeFac(nSoil) - case(mixdform); scalarHydCondTrial = hydCond_psi(scalarMatricHeadTrial,mLayerSatHydCond(nSoil),vGn_alpha(nSoil),vGn_n(nSoil),vGn_m(nSoil)) * iceImpedeFac(nSoil) + case(mixdform); scalarHydCondTrial = hydCond_psi(scalarMatricHeadLiqTrial,mLayerSatHydCond(nSoil),vGn_alpha(nSoil),vGn_n(nSoil),vGn_m(nSoil)) * iceImpedeFac(nSoil) end select ! (use un-perturbed value) @@ -817,7 +817,7 @@ subroutine soilLiqFlx(& ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) ixBcLowerSoilHydrology, & ! intent(in): index defining the type of boundary conditions ! input: state variables - scalarMatricHeadTrial, & ! intent(in): matric head in the lowest unsaturated node (m) + scalarMatricHeadLiqTrial, & ! intent(in): liquid matric head in the lowest unsaturated node (m) scalarVolFracLiqTrial, & ! intent(in): volumetric liquid water content the lowest unsaturated node (-) ! input: model coordinate variables mLayerDepth(nSoil), & ! intent(in): depth of the lowest unsaturated soil layer (m) @@ -902,7 +902,7 @@ subroutine diagv_node(& ixRichards, & ! intent(in): index defining the option for Richards' equation (moisture or mixdform) ! input: state variables scalarTempTrial, & ! intent(in): temperature (K) - scalarMatricHeadTrial, & ! intent(in): matric head in a given layer (m) + scalarMatricHeadLiqTrial, & ! intent(in): liquid matric head in a given layer (m) scalarVolFracLiqTrial, & ! intent(in): volumetric liquid water content in a given soil layer (-) scalarVolFracIceTrial, & ! intent(in): volumetric ice content in a given soil layer (-) ! input: pre-computed deriavatives @@ -953,7 +953,7 @@ subroutine diagv_node(& integer(i4b),intent(in) :: ixRichards ! index defining the option for Richards' equation (moisture or mixdform) ! input: state and diagnostic variables real(rkind),intent(in) :: scalarTempTrial ! temperature in each layer (K) - real(rkind),intent(in) :: scalarMatricHeadTrial ! matric head in each layer (m) + real(rkind),intent(in) :: scalarMatricHeadLiqTrial ! liquid matric head in each layer (m) real(rkind),intent(in) :: scalarVolFracLiqTrial ! volumetric fraction of liquid water in a given layer (-) real(rkind),intent(in) :: scalarVolFracIceTrial ! volumetric fraction of ice in a given layer (-) ! input: pre-computed deriavatives @@ -1022,11 +1022,11 @@ subroutine diagv_node(& scalardPsi_dTheta = dPsi_dTheta(scalarVolFracLiqTrial,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) scalardTheta_dPsi = realMissing ! (deliberately cause problems if this is ever used) case(mixdform) - scalardTheta_dPsi = dTheta_dPsi(scalarMatricHeadTrial,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + scalardTheta_dPsi = dTheta_dPsi(scalarMatricHeadLiqTrial,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) scalardPsi_dTheta = dPsi_dTheta(scalarVolFracLiqTrial,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) if(testDeriv)then - volFracLiq1 = volFracLiq(scalarMatricHeadTrial, vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - volFracLiq2 = volFracLiq(scalarMatricHeadTrial+dx,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + volFracLiq1 = volFracLiq(scalarMatricHeadLiqTrial, vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + volFracLiq2 = volFracLiq(scalarMatricHeadLiqTrial+dx,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) end if ! (testing the derivative) case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return end select @@ -1063,10 +1063,10 @@ subroutine diagv_node(& ! ***** mixed form of Richards' equation -- just compute hydraulic condictivity case(mixdform) ! compute the hydraulic conductivity (m s-1) and diffusivity (m2 s-1) for a given layer - hydCond_noIce = hydCond_psi(scalarMatricHeadTrial,scalarSatHydCond,vGn_alpha,vGn_n,vGn_m) + hydCond_noIce = hydCond_psi(scalarMatricHeadLiqTrial,scalarSatHydCond,vGn_alpha,vGn_n,vGn_m) scalarDiffuse = realMissing ! not used, so cause problems ! compute the hydraulic conductivity of macropores (m s-1) - localVolFracLiq = volFracLiq(scalarMatricHeadTrial,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + localVolFracLiq = volFracLiq(scalarMatricHeadLiqTrial,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) scalarHydCondMP = hydCondMP_liq(localVolFracLiq,theta_sat,theta_mp,mpExp,scalarSatHydCondMP,scalarSatHydCond) scalarHydCond = hydCond_noIce*iceImpedeFac + scalarHydCondMP @@ -1083,12 +1083,12 @@ subroutine diagv_node(& end if ! (compute derivatives for micropores) if(scalarVolFracIceTrial > verySmall)then - dK_dPsi__noIce = dHydCond_dPsi(scalarMatricHeadTrial,scalarSatHydCond,vGn_alpha,vGn_n,vGn_m,.true.) ! analytical + dK_dPsi__noIce = dHydCond_dPsi(scalarMatricHeadLiqTrial,scalarSatHydCond,vGn_alpha,vGn_n,vGn_m,.true.) ! analytical dHydCondMicro_dTemp = dPsiLiq_dTemp*dK_dPsi__noIce ! m s-1 K-1 dHydCondMicro_dMatric = hydCond_noIce*dIceImpede_dLiq*scalardTheta_dPsi + dK_dPsi__noIce*iceImpedeFac else dHydCondMicro_dTemp = 0._rkind - dHydCondMicro_dMatric = dHydCond_dPsi(scalarMatricHeadTrial,scalarSatHydCond,vGn_alpha,vGn_n,vGn_m,.true.) + dHydCondMicro_dMatric = dHydCond_dPsi(scalarMatricHeadLiqTrial,scalarSatHydCond,vGn_alpha,vGn_n,vGn_m,.true.) end if ! (combine derivatives) dHydCond_dMatric = dHydCondMicro_dMatric + dHydCondMacro_dMatric @@ -1111,7 +1111,7 @@ subroutine diagv_node(& hydCon = hydCond_psi(psiLiq,scalarSatHydCond,vGn_alpha,vGn_n,vGn_m) call iceImpede(volIce,f_impede,iceImpedeFac,dIceImpede_dLiq) hydIce = hydCon*iceImpedeFac - print*, 'test derivative: ', (psiLiq - scalarMatricHeadTrial)/dx, dPsiLiq_dTemp + print*, 'test derivative: ', (psiLiq - scalarMatricHeadLiqTrial)/dx, dPsiLiq_dTemp print*, 'test derivative: ', (hydCon - hydCond_noIce)/dx, dHydCondMicro_dTemp print*, 'test derivative: ', (hydIce - scalarHydCond)/dx, dHydCond_dTemp print*, 'press any key to continue'; read(*,*) ! (alternative to the deprecated 'pause' statement) @@ -1559,7 +1559,7 @@ subroutine iLayerFlux(& deriv_desired, & ! intent(in): flag indicating if derivatives are desired ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) ! input: state variables (adjacent layers) - nodeMatricHeadTrial, & ! intent(in): matric head at the soil nodes (m) + nodeMatricHeadLiqTrial, & ! intent(in): liquid matric head at the soil nodes (m) nodeVolFracLiqTrial, & ! intent(in): volumetric liquid water content at the soil nodes (-) ! input: model coordinate variables (adjacent layers) nodeHeight, & ! intent(in): height of the soil nodes (m) @@ -1591,7 +1591,7 @@ subroutine iLayerFlux(& logical(lgt),intent(in) :: deriv_desired ! flag indicating if derivatives are desired integer(i4b),intent(in) :: ixRichards ! index defining the option for Richards' equation (moisture or mixdform) ! input: state variables - real(rkind),intent(in) :: nodeMatricHeadTrial(:) ! matric head at the soil nodes (m) + real(rkind),intent(in) :: nodeMatricHeadLiqTrial(:) ! liquid matric head at the soil nodes (m) real(rkind),intent(in) :: nodeVolFracLiqTrial(:) ! volumetric fraction of liquid water at the soil nodes (-) ! input: model coordinate variables real(rkind),intent(in) :: nodeHeight(:) ! height at the mid-point of the lower layer (m) @@ -1659,7 +1659,7 @@ subroutine iLayerFlux(& cflux = -iLayerDiffuse * dLiq/dz case(mixdform) iLayerDiffuse = realMissing - dPsi = nodeMatricHeadTrial(ixLower) - nodeMatricHeadTrial(ixUpper) + dPsi = nodeMatricHeadLiqTrial(ixLower) - nodeMatricHeadLiqTrial(ixUpper) cflux = -iLayerHydCond * dPsi/dz case default; err=10; message=trim(message)//"unable to identify option for Richards' equation"; return end select From 0e627b9c5273f8bef57cf1837576073acb9afb17 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 25 Aug 2022 20:11:03 +0900 Subject: [PATCH 0324/1472] remove constraints --- build/source/engine/solveByIDA.f90 | 20 ------- build/source/engine/systemSolvSundials.f90 | 62 ---------------------- 2 files changed, 82 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 75d3a649f..93a33de3b 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -102,8 +102,6 @@ subroutine solveByIDA( & scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVecInit, & ! intent(in): initial state vector - stateVecConstraints, & ! intent(inout): model state vector constraints - stateVecConstValues, & ! intent(inout): model state vector constraint values sMul, & ! intent(inout): state vector multiplier (USEd in the residual calculations) dMat, & ! intent(inout): diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures @@ -171,8 +169,6 @@ subroutine solveByIDA( & logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors real(rkind),intent(in) :: stateVecInit(:) ! model state vector - real(rkind),intent(inout) :: stateVecConstraints(:) ! model state vector constraints - real(rkind),intent(inout) :: stateVecConstValues(:) ! model state vector constraint values real(qp),intent(in) :: sMul(:) ! state vector multiplier (used in the residual calculations) real(rkind), intent(inout) :: dMat(:) ! input: data structures @@ -207,8 +203,6 @@ subroutine solveByIDA( & ! -------------------------------------------------------------------------------------------------------------------------------- type(N_Vector), pointer :: sunvec_y ! sundials solution vector type(N_Vector), pointer :: sunvec_yp ! sundials derivative vector - type(N_Vector), pointer :: sunvec_c ! sundials constraints vector - type(N_Vector), pointer :: sunvec_cv ! sundials constraints vector of values type(N_Vector), pointer :: sunvec_av ! sundials tolerance vector type(SUNMatrix), pointer :: sunmat_A ! sundials matrix type(SUNLinearSolver), pointer :: sunlinsol_LS ! sundials linear solver @@ -323,12 +317,6 @@ subroutine solveByIDA( & sunvec_yp => FN_VMake_Serial(nState, stateVecPrime) if (.not. associated(sunvec_yp)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif - !sunvec_c => FN_VMake_Serial(nState, stateVecConstraints) - !if (.not. associated(sunvec_c)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif - - !sunvec_cv => FN_VMake_Serial(nState, stateVecConstValues) - !if (.not. associated(sunvec_cv)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif - ! Initialize solution vectors call setInitialCondition(nState, stateVecInit, sunvec_y, sunvec_yp) @@ -386,10 +374,6 @@ subroutine solveByIDA( & retval = FIDASetLinearSolver(ida_mem, sunlinsol_LS, sunmat_A); if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetLinearSolver'; return; endif - ! Set constraints - !retval = FIDASetConstraints(ida_mem, sunvec_c, sunvec_cv) !uncomment this line to use feasibility constraints - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetConstraints'; return; endif - if(ixMatrix == ixFullMatrix)then ! Set the user-supplied Jacobian routine !comment this line out to use FD Jacobian, currently cannot do with constraint version of IDA, memory size error @@ -455,7 +439,6 @@ subroutine solveByIDA( & ! loop through non-missing energy state variables in the snow domain to see if need to merge do concurrent (i=1:nSnow,indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing) if (stateVec(indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)) > Tfreeze) tooMuchMelt = .true. !need to merge - if (stateVec(indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)) > Tfreeze) print*,i,tret(1),nSnow,idaSucceeds,"merge" enddo if(tooMuchMelt)exit @@ -610,9 +593,6 @@ subroutine solveByIDA( & call FSUNMatDestroy(sunmat_A) call FN_VDestroy(sunvec_y) call FN_VDestroy(sunvec_yp) - !call FN_VDestroy(sunvec_c) - !call FN_VDestroy(sunvec_cv) - end subroutine solveByIDA diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 75e3ea72b..dfb623051 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -202,8 +202,6 @@ subroutine systemSolvSundials(& real(qp) :: sMul(nState) ! NOTE: qp ! multiplier for state vector for the residual calculations real(qp) :: rVec(nState) ! NOTE: qp ! residual vector real(rkind) :: rAdd(nState) ! additional terms in the residual vector - real(rkind) :: stateVecConstraints(nState) ! model state vector constraints - real(rkind) :: stateVecConstValues(nState) ! model state vector constraint values real(rkind) :: fOld ! function values (-); NOTE: dimensionless because scaled logical(lgt) :: feasible ! feasibility flag real(rkind) :: atol(nState) ! absolute telerance @@ -454,64 +452,6 @@ subroutine systemSolvSundials(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - ! Set constraints for state vector - ! 0 then no constraint is imposed on y. - ! 1 then yi will be constrained to be yi>=0 - !-1 then yi will be constrained to be yi<=0 - stateVecConstraints = 0._rkind - stateVecConstValues = 0._rkind - if(ixCasNrg/=integerMissing)then !canopy air space temperature cannot be above canopyTempMax - stateVecConstraints(ixCasNrg) = -1._rkind - stateVecConstValues(ixCasNrg) = canopyTempMax - endif - if(ixVegNrg/=integerMissing)then !canopy temperature cannot be above canopyTempMax - stateVecConstraints(ixVegNrg) = -1._rkind - stateVecConstValues(ixVegNrg) = canopyTempMax - endif - if(ixVegHyd/=integerMissing)then !canopy liquid water cannot be below 0 - stateVecConstraints(ixVegHyd) = 1._rkind - stateVecConstValues(ixVegHyd) = 0._rkind - endif - - ! loop through non-missing energy state variables in the snow domain - do concurrent (iLayer=1:nSnow,ixSnowOnlyNrg(iLayer)/=integerMissing) - stateVecConstraints(ixSnowOnlyNrg(iLayer)) = -1._rkind !snow temp cannot be above Tfreeze - stateVecConstValues(ixSnowOnlyNrg(iLayer)) = Tfreeze !snow temp cannot be above Tfreeze - end do - - ! loop through non-missing hydrology state variables in the snow+soil domain - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) - ! check the minimum and maximum water constraints - if(ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_liqLayer)then - - ! --> minimum water constraints - if (layerType(iLayer) == iname_soil) then - xMin = theta_res(iLayer-nSnow) - else - xMin = 0._rkind - endif - stateVecConstraints(ixSnowSoilHyd(iLayer)) = 1._rkind !water cannot be below xMin - stateVecConstValues(ixSnowSoilHyd(iLayer)) = xMin !water cannot be below xMin - ! --> maximum water constraints, SUNDIALS CAN ONLY DO ONE INEQUALITY PER STATE VARIABLE - ! mLayerVolFracIce => eqns_data%mLayerVolFracIceTrial ,& ! intent(in): [dp(:)] trial vector of volumetric ice water content (-) - ! select case( layerType(iLayer) ) - ! case(iname_snow); xMax = merge(iden_ice, 1._rkind - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) - ! case(iname_soil); xMax = merge(theta_sat(iLayer-nSnow), theta_sat(iLayer-nSnow) - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) - ! end select - ! stateVecConstraints(ixSnowSoilHyd(iLayer)) = -1._rkind !water cannot be above xMax - ! stateVecConstValues(ixSnowSoilHyd(iLayer)) = xMax !water cannot be above xMax - - endif ! if water states - end do ! loop through non-missing hydrology state variables in the snow+soil domain - - ! loop through non-missing hydrology state variables in the snow+soil domain - !do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) - ! minimum temperature constraints - ! xMin = 100._rkind !degrees C - ! stateVecConstraints(ixSnowSoilNrg(iLayer)) = 1._rkind !temp cannot be less than xMin - ! stateVecConstraints(ixSnowSoilNrg(iLayer)) = xMin !temp cannot be less than xMin - !enddo - !------------------- ! * solving F(y,y') = 0 by IDA. Here, y is the state vector ! ------------------ @@ -540,8 +480,6 @@ subroutine systemSolvSundials(& scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vector stateVecTrial, & ! intent(in): model state vector at the beginning of the data time step - stateVecConstraints, & ! intent(inout): model state vector constraints - stateVecConstValues, & ! intent(inout): model state vector constraint values sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout) diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures From 404ef27c337e65cc7ffda48ba07cb5a89d4bb2b3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 25 Aug 2022 20:20:13 +0900 Subject: [PATCH 0325/1472] Remove rootfinding --- build/source/engine/solveByIDA.f90 | 96 +----------------------------- 1 file changed, 1 insertion(+), 95 deletions(-) diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/solveByIDA.f90 index 93a33de3b..0385f19a9 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/solveByIDA.f90 @@ -338,12 +338,6 @@ subroutine solveByIDA( & retval = FIDAWFtolerances(ida_mem, c_funloc(computWeight4IDA)) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAWFtolerances'; return; endif - ! initialize rootfinding problem if we have a snow-free ground interfacing with vegetation - if(nSoil>0 .and. nSnow==0 .and. indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then - !retval = FIDARootInit(ida_mem, 3, c_funloc(vegsoilDisC4IDA)) !uncomment this line to restart at discontinuities - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif - endif - ! define the form of the matrix select case(ixMatrix) case(ixBandMatrix) @@ -376,7 +370,7 @@ subroutine solveByIDA( & if(ixMatrix == ixFullMatrix)then ! Set the user-supplied Jacobian routine - !comment this line out to use FD Jacobian, currently cannot do with constraint version of IDA, memory size error + !comment this line out to use FD Jacobian retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetJacFn'; return; endif endif @@ -538,18 +532,6 @@ subroutine solveByIDA( & eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial - ! Look for where top soil layer crosses the matric head 0 point and changes equation of freezing temperature - if(nSoil>0 .and. nSnow==0 .and. indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then - if (retvalr .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) - ! rootsfound[i]= +1 indicates that gi is increasing, -1 g[i] decreasing, 0 no root - retval = FIDAGetRootInfo(ida_mem, rootsfound) - if (retval < 0) then; err=20; message='solveByIDA: error in FIDAGetRootInfo'; return; endif - !print '(a,f15.7,2x,2(i2,2x))', "time, rootsfound[] = ", tret(1), rootsfound - ! Reininitialize solver for running after discontinuity and restart - retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAReInit'; return; endif - endif - endif enddo ! while loop on one_step mode until time dt @@ -694,82 +676,6 @@ subroutine setSolverParams(dt,ida_mem,retval) end subroutine setSolverParams -! ---------------------------------------------------------------------------------------- -! vegsoilDisC4IDA: The root function routine to find soil surface matrix potential = 0, -! soil surface temp = critical frozen point, and veg temp = Tfreeze -! sundials will not call vegsoilDisC4IDA unless this soil index exists and there is no snow -! ---------------------------------------------------------------------------------------- -! Return values: -! 0 = success, -! 1 = recoverable error, -! -1 = non-recoverable error -! ---------------------------------------------------------------------------------------- - integer(c_int) function vegsoilDisC4IDA(t, sunvec_u, sunvec_up, gout, user_data) & - result(ierr) bind(C,name='vegsoilDisC4IDA') - - !======= Inclusions =========== - use, intrinsic :: iso_c_binding - use fsundials_nvector_mod - use fnvector_serial_mod - use soil_utils_module,only:crit_soilT ! compute the critical temperature below which ice exists - use globalData,only:integerMissing ! missing integer - use var_lookup,only:iLookINDEX ! named variables for structure elements - use multiconst,only:Tfreeze ! freezing point of pure water (K) - - !======= Declarations ========= - implicit none - - ! calling variables - real(c_double), value :: t ! current time - type(N_Vector) :: sunvec_u ! solution N_Vector - type(N_Vector) :: sunvec_up ! derivative N_Vector - real(c_double) :: gout(3) ! root function values - type(c_ptr), value :: user_data ! user-defined data - - ! local variables - integer(i4b) :: nState ! number of states - real(rkind) :: xPsi ! matric head at layer (m) - real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) - - - ! pointers to data in SUNDIALS vectors - real(c_double), pointer :: uu(:) - type(eqnsData), pointer :: eqns_data ! equations data - - !======= Internals ============ - ! get equations data from user-defined data - call c_f_pointer(user_data, eqns_data) - nState = eqns_data%nState - - ! get data array from SUNDIALS vector - uu(1:nState) => FN_VGetArrayPointer(sunvec_u) - gout = 0._rkind - - ! identify the critical point when soil surface matrix potential goes below 0 and Tfreeze depends only on temp - if (eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(1)/=integerMissing)then - xPsi = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(1)) - gout(1) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(1)) - else - xPsi = eqns_data%prog_data%var(iLookPROG%mLayerMatricHead)%dat(1) - endif - - ! identify the critical point when soil surface begins to freeze (TcSoil) - if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(1)/=integerMissing)then - TcSoil = crit_soilT(xPsi) - gout(2) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(1)) - TcSoil - endif - - ! identify the critical point when vegetation begins to freeze - if(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then - gout(3) = uu(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)) - Tfreeze - endif - - ! return success - ierr = 0 - return - - end function vegsoilDisC4IDA - ! ********************************************************************************************************* ! private subroutine implctMelt: compute melt of the "snow without a layer" ! ********************************************************************************************************* From de6ac4222f96809389531a78cfda71aea2eb2f3c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 31 Aug 2022 17:18:50 +0900 Subject: [PATCH 0326/1472] build for richardson --- build/build_cmakeSundials_ric | 8 + build/build_summa_ric | 6 + build/my_makefile_ric | 418 ++++++++++++++++++++++++++++++++++ sundials/installation.txt | 4 +- 4 files changed, 434 insertions(+), 2 deletions(-) create mode 100755 build/build_cmakeSundials_ric create mode 100755 build/build_summa_ric create mode 100644 build/my_makefile_ric diff --git a/build/build_cmakeSundials_ric b/build/build_cmakeSundials_ric new file mode 100755 index 000000000..39dd5068c --- /dev/null +++ b/build/build_cmakeSundials_ric @@ -0,0 +1,8 @@ +# from ../../sundials/builddir, run +# cp ../../summa/build/build_cmakeSundials_ric build_cmake +# run script from the builddir directory with ./build_cmake +# Note, using -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF because modified constraint code and did not modify examples + +cmake ../../sundials-5.8.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/staff/gwu479/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/staff/gwu479/SummaSundials/sundials/instdir/examples + + diff --git a/build/build_summa_ric b/build/build_summa_ric new file mode 100755 index 000000000..02d2eebb2 --- /dev/null +++ b/build/build_summa_ric @@ -0,0 +1,6 @@ +module load StdEnv/2020 +module load gcc/9.3.0 +module load openblas/0.3.17 +module load netcdf-fortran/4.5.2 + +make -f my_makefile_ric diff --git a/build/my_makefile_ric b/build/my_makefile_ric new file mode 100644 index 000000000..2fb99b632 --- /dev/null +++ b/build/my_makefile_ric @@ -0,0 +1,418 @@ +#======================================================================== +# Makefile to compile SUMMA SUNDIALS +#======================================================================== +# +# Recommended use: Copy this file to Makefile.local, edit it to your +# heart's content, and then run `make -f build/Makefile.local` from +# your top level SUMMA directory. Don't include the Makefile.local in +# any pull requests you make. +# +# Note that Makefile configurations that we commonly use can be found on +# the SUMMA wiki at: +# https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration +# feel free to add yours to that page. +# +# To troubleshoot your paths and setup, type 'make check' +# +# At a minimum you will need to set the following: +# * F_MASTER - top level summa directory +# * FC - compiler suite +# * FC_EXE - compiler executable +# * INCLUDES - path to include files +# * ILDFLAGS - path to libraries to include +# * LIBRARIES - libraries to include +# * DIR_SUNDIALS - installation sundials directory +# +# Some further options can be specified for OpenMP, etc. See in Part 0 of +# the Makefile. You don't need to make any changes in PART 1 and +# following unless you are doing SUMMA development and changed what +# needs to be compiled + +#======================================================================== +# PART 0: User-configurable part +#======================================================================== + +# The variables can be specified in one of two ways: +# * delete the '##' in front of the variable, fill out the entry, +# save the file and run make +# * make no changes to this file, but specify the variables in your +# environment before you run make + +# Define core directory below which everything resides. This is the +# parent directory of the 'build' directory +F_MASTER =/staff/gwu479/SummaSundials/summa + +# Define the Fortran Compiler. If you are using gfortran, then this needs +# to be version 6 or higher. This variable is simply used to select the right +# compiler flags in the ifeq statements in this Makefile. The compiler +# executable is set separately as FC_EXE +# Currently this is either gfortran or ifort +FC = gfortran + +# Define the path for the compiler executable. This is the actual executable +# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin/gfortran-mp-11 +# FC and FC_EXE have to be consistent +FC_EXE = gfortran + +# Define the NetCDF and LAPACK libraries and path to include files. +# INCLUDES needs to be of the form (no quotes around the string): +# INCLUDES = -I -I -I<...> -I +# LIBRARIES needs to be of the form ( no quotes around the string): +# LIBRARIES = '-L -lnetcdff -L -lblas -L -l' +# If none of this makes sense, please talk to your system +# administrator. +INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include +LDFLAGS = -L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib +LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas + +DIR_SUNDIALS=/staff/gwu479/SummaSundials/sundials/instdir +INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran +LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod + +# Eventually we plan move to a real configure script, but for now we like +# to keep track of successful compilations of SUMMA on different platforms +# and with different compilers. If you are successful compiling SUMMA, +# please add your configuration (operating system and compiler plus +# part 0 of the Makefile) to the SUMMA wiki on github. + +# Define compiler flags. If you use a different compiler, +# you will need to figure out what the equivalent flags are +# and may need to update this section + +# ------------ define compiler flags ---------------------------------------- + +# define open MP flags +isOpenMP = +FLAGS_OMP = +LIBOPENMP = + +# Define compiler flags. If you use a different compiler, +# you will need to figure out what the equivalent flags are +# and may need to update this section + +# gfortran compiler flags +ifeq "$(FC)" "gfortran" + + ifeq "$(isOpenMP)" "yes" + FLAGS_OMP = -fopenmp + endif + +# Production runs +FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) + +# Debug runs +#FLAGS_NOAH = -p -g -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument +#FLAGS_COMM = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds +#FLAGS_SUMMA = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds + +endif + +# ifort compiler flags +ifeq "$(FC)" "ifort" + + ifeq "$(isOpenMP)" "yes" + FLAGS_OMP = -qopenmp + endif + +# Production runs +FLAGS_NOAH = -O3 -noerror_limit -FR -auto -fltconsistency $(FLAGS_OMP) +FLAGS_COMM = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) +FLAGS_SUMMA = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) + +# Debug runs +#FLAGS_NOAH = -O0 -p -g -warn nounused -noerror_limit -FR -auto -WB -traceback -fltconsistency +#FLAGS_COMM = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 +#FLAGS_SUMMA = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 +endif + +#======================================================================== +# PART 1: Define directory paths +#======================================================================== + +# Core directory that contains source code +F_KORE_DIR = $(F_MASTER)/build/source + +# Location of the compiled modules +MOD_PATH = $(F_MASTER)/build + +# Define the directory for the executables +EXE_PATH = $(F_MASTER)/bin + +#======================================================================== +# PART 2: Assemble all of the SUMMA sub-routines +#======================================================================== + +# Define directories +DRIVER_DIR = $(F_KORE_DIR)/driver +HOOKUP_DIR = $(F_KORE_DIR)/hookup +NETCDF_DIR = $(F_KORE_DIR)/netcdf +DSHARE_DIR = $(F_KORE_DIR)/dshare +NUMREC_DIR = $(F_KORE_DIR)/numrec +NOAHMP_DIR = $(F_KORE_DIR)/noah-mp +ENGINE_DIR = $(F_KORE_DIR)/engine + +# utilities +SUMMA_NRUTIL= \ + nrtype.f90 \ + f2008funcs.f90 \ + nr_utility.f90 +NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) + +# +# Numerical recipes procedures +# NOTE: all numerical recipes procedures are now replaced with free versions +SUMMA_NRPROC= \ + expIntegral.f90 \ + spline_int.f90 +NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) + +# Hook-up modules (set files and directory paths) +SUMMA_HOOKUP= \ + ascii_util.f90 \ + summaFileManager.f90 + +HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) + +# Data modules +SUMMA_DATAMS= \ + multiconst.f90 \ + var_lookup.f90 \ + data_types.f90 \ + globalData.f90 \ + flxMapping.f90 \ + get_ixname.f90 \ + popMetadat.f90 \ + outpt_stat.f90 +DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) + +# utility modules +SUMMA_UTILMS= \ + time_utils.f90 \ + mDecisions.f90 \ + snow_utils.f90 \ + soil_utils.f90 \ + soil_utilsSundials.f90 \ + updatState.f90 \ + updatStateSundials.f90 \ + matrixOper.f90 +UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) + +# Model guts +SUMMA_MODGUT= \ + MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) + +# Solver +SUMMA_SOLVER= \ + vegPhenlgy.f90 \ + diagn_evar.f90 \ + stomResist.f90 \ + groundwatr.f90 \ + vegSWavRad.f90 \ + vegNrgFlux.f90 \ + ssdNrgFlux.f90 \ + vegLiqFlux.f90 \ + snowLiqFlx.f90 \ + soilLiqFlx.f90 \ + bigAquifer.f90 \ + computFlux.f90 \ + computResid.f90 \ + computJacob.f90 \ + eval8summa.f90 \ + summaSolve.f90 \ + systemSolv.f90 \ + type4IDA.f90 \ + tol4IDA.f90 \ + computEnthalpy.f90 \ + computHeatCap.f90 \ + computThermConduct.f90 \ + computResidDAE.f90 \ + eval8DAE.f90 \ + evalDAE4IDA.f90 \ + computJacDAE.f90 \ + eval8JacDAE.f90 \ + evalJac4IDA.f90 \ + computSnowDepth.f90 \ + solveByIDA.f90 \ + systemSolvSundials.f90 \ + varSubstep.f90 \ + varSubstepSundials.f90 \ + opSplittin.f90 \ + coupled_em.f90 \ + run_oneHRU.f90 \ + run_oneGRU.f90 +SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) + +# Define routines for SUMMA preliminaries +SUMMA_PRELIM= \ + conv_funcs.f90 \ + sunGeomtry.f90 \ + convE2Temp.f90 \ + allocspace.f90 \ + checkStruc.f90 \ + childStruc.f90 \ + ffile_info.f90 \ + read_attrb.f90 \ + read_pinit.f90 \ + pOverwrite.f90 \ + read_param.f90 \ + paramCheck.f90 \ + check_icond.f90 +PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) + +SUMMA_NOAHMP= \ + module_model_constants.F \ + module_sf_noahutl.F \ + module_sf_noahlsm.F \ + module_sf_noahmplsm.F +NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) + +# Define routines for the SUMMA model runs +SUMMA_MODRUN = \ + indexState.f90 \ + getVectorz.f90 \ + varExtrSundials.f90 \ + t2enthalpy.f90 \ + updateVars.f90 \ + updateVarsSundials.f90 \ + updateVars4JacDAE.f90 \ + var_derive.f90 \ + read_force.f90 \ + derivforce.f90 \ + snowAlbedo.f90 \ + canopySnow.f90 \ + tempAdjust.f90 \ + snwCompact.f90 \ + layerMerge.f90 \ + layerDivide.f90 \ + volicePack.f90 \ + qTimeDelay.f90 +MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) + +# Define routines for the solver +SUMMA_MSOLVE = \ + +# Define NetCDF routines +SUMMA_NETCDF = \ + netcdf_util.f90 \ + def_output.f90 \ + modelwrite.f90 \ + read_icond.f90 +NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) + +# ... stitch together common programs +COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) + +# ... stitch together SUMMA programs +SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) + +# Define the driver routine +SUMMA_DRIVER= \ + summa_type.f90 \ + summa_util.f90 \ + summa_alarms.f90 \ + summa_globalData.f90 \ + summa_defineOutput.f90 \ + summa_init.f90 \ + summa_setup.f90 \ + summa_restart.f90 \ + summa_forcing.f90 \ + summa_modelRun.f90 \ + summa_writeOutput.f90 \ + summa_driver.f90 +DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) + +# Define the executable +DRIVER__EX = summa_sundials.exe + +# Define version number +VERSIONFILE = $(DRIVER_DIR)/summaversion.inc +VERSION = $(shell git tag | tail -n 1) +BULTTIM = $(shell date) +GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') +GITHASH = $(shell git rev-parse HEAD) + +#======================================================================== +# PART 3: Checks +#====================================================================== +# make sure that the paths are defined. These are just some high level checks +ifndef F_MASTER + $(error F_MASTER is undefined) +endif +ifndef FC + $(error FC is undefined: Specify your compiler) +endif +ifndef FC_EXE + $(error FC_EXE is undefined: Specify your compiler executable) +endif +ifndef FLAGS_SUMMA + $(error Specify flags for your compiler: $(FC)) +endif +ifndef INCLUDES + $(error INCLUDES is undefined) +endif +ifndef LIBRARIES + $(error LIBRARIES is undefined) +endif + +#======================================================================== +# PART 4: compilation +#====================================================================== + +# Compile +all: compile_noah compile_comm compile_summa link clean install +part: compile_noah compile_comm compile_summa + +check: + $(info) + $(info Displaying make variables:) + $(info F_MASTER : $(F_MASTER)) + $(info EXE_PATH : $(EXE_PATH)) + $(info FC : $(FC)) + $(info INCLUDES : $(INCLUDES)) + $(info LIBRARIES : $(LIBRARIES)) + $(info FLAGS_NOAH : $(FLAGS_NOAH)) + $(info FLAGS_COMM : $(FLAGS_COMM)) + $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) + $(info SUNDIALSDIR: $(DIR_SUNDIALS)) + $(info INC_SUNDIALS: $(INC_SUNDIALS)) + $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) + $(info) + +# update version information +update_version: + echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) + echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) + +# compile Noah-MP routines +compile_noah: + $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) + +# compile common routines + +compile_comm: + $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) + +# compile SUMMA routines +compile_summa: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ + $(INCLUDES) $(INC_SUNDIALS) + +# link routines +link: + $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) + +# Remove object files +clean: + rm -f *.o + rm -f *.mod + rm -f soil_veg_gen_parm__genmod.f90 + +# Copy the executable to the bin directory +install: + @mkdir -p $(EXE_PATH) + @mv $(DRIVER__EX) $(EXE_PATH) + $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) diff --git a/sundials/installation.txt b/sundials/installation.txt index 15fbb8e0b..a610f89d7 100644 --- a/sundials/installation.txt +++ b/sundials/installation.txt @@ -10,8 +10,8 @@ Make a directory outside the sundials-5.8.0 folder and enter it, and make the in % mkdir sundials % cd sundials % mkdir instdir -% mkdir builddir -% cd /builddir +% mkdir buildir +% cd /buildir NOTE if you need to recompile after a system upgrade, delete the contents of these directories before running cmake in the next step! 4. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, the following setting needs to be enabled while running the cmake inside the builddir, using your home directory as $(YOUR_HOME) From f5fe05033511585d913c81e80028f1abf7902729 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 5 Sep 2022 15:57:05 +0900 Subject: [PATCH 0327/1472] richardson makefiles --- build/build_cmakeSundials_ric | 2 +- build/build_summa_ric | 5 ----- build/my_makefile_ric | 10 +++++----- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/build/build_cmakeSundials_ric b/build/build_cmakeSundials_ric index 39dd5068c..eabdaeeb5 100755 --- a/build/build_cmakeSundials_ric +++ b/build/build_cmakeSundials_ric @@ -3,6 +3,6 @@ # run script from the builddir directory with ./build_cmake # Note, using -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF because modified constraint code and did not modify examples -cmake ../../sundials-5.8.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/staff/gwu479/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/staff/gwu479/SummaSundials/sundials/instdir/examples +cmake ../../sundials-5.8.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/u1/gwu479/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/u1/gwu479/SummaSundials/sundials/instdir/examples diff --git a/build/build_summa_ric b/build/build_summa_ric index 02d2eebb2..b37eca78e 100755 --- a/build/build_summa_ric +++ b/build/build_summa_ric @@ -1,6 +1 @@ -module load StdEnv/2020 -module load gcc/9.3.0 -module load openblas/0.3.17 -module load netcdf-fortran/4.5.2 - make -f my_makefile_ric diff --git a/build/my_makefile_ric b/build/my_makefile_ric index 2fb99b632..8a2571953 100644 --- a/build/my_makefile_ric +++ b/build/my_makefile_ric @@ -40,7 +40,7 @@ # Define core directory below which everything resides. This is the # parent directory of the 'build' directory -F_MASTER =/staff/gwu479/SummaSundials/summa +F_MASTER =/u1/gwu479/SummaSundials/summa # Define the Fortran Compiler. If you are using gfortran, then this needs # to be version 6 or higher. This variable is simply used to select the right @@ -61,11 +61,11 @@ FC_EXE = gfortran # LIBRARIES = '-L -lnetcdff -L -lblas -L -l' # If none of this makes sense, please talk to your system # administrator. -INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include -LDFLAGS = -L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib -LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas +INCLUDES = -I/usr/include -I/usr/local/include +LDFLAGS = -L/usr/local/lib +LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas -lnetcdf -DIR_SUNDIALS=/staff/gwu479/SummaSundials/sundials/instdir +DIR_SUNDIALS=/u1/gwu479/SummaSundials/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod From 78d2443ccf13ebf09fb6eb405c33affb95e316a0 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 6 Sep 2022 11:54:14 +0900 Subject: [PATCH 0328/1472] Get rid of varExtract2 in Sundials (simplify code) and add in Jacobian changes to BE code --- build/source/dshare/get_ixname.f90 | 3 + build/source/dshare/popMetadat.f90 | 3 + build/source/dshare/var_lookup.f90 | 5 +- build/source/engine/computJacDAE.f90 | 111 +++-- build/source/engine/computJacob.f90 | 461 +++++++++++++++------ build/source/engine/computResidDAE.f90 | 6 +- build/source/engine/eval8DAE.f90 | 8 +- build/source/engine/eval8JacDAE.f90 | 14 +- build/source/engine/eval8summa.f90 | 54 ++- build/source/engine/evalDAE4IDA.f90 | 14 +- build/source/engine/getVectorz.f90 | 40 +- build/source/engine/systemSolvSundials.f90 | 23 +- build/source/engine/updateVars.f90 | 62 ++- build/source/engine/varExtrSundials.f90 | 133 ------ build/source/engine/varSubstep.f90 | 16 +- build/source/engine/varSubstepSundials.f90 | 17 +- 16 files changed, 553 insertions(+), 417 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index fca7e975a..7fab5482f 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -783,6 +783,9 @@ function get_ixderiv(varName) case('dVolHtCapBulk_dCanWat' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. volumetric water content case('dVolHtCapBulk_dTk' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTk ! derivative in bulk heat capacity w.r.t. temperature case('dVolHtCapBulk_dTkCanopy' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature + ! derivatives in time + case( 'mLayerdTemp_dt' ); get_ixderiv = iLookDERIV%mLayerdTemp_dt ! timestep change in layer temperature + case( 'scalarCanopydTemp_dt' ); get_ixderiv = iLookDERIV%scalarCanopydTemp_dt ! timestep change in canopy temperature case default get_ixderiv = integerMissing diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 0b9562d06..2acc7f59b 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -617,6 +617,9 @@ subroutine popMetadat(err,message) deriv_meta(iLookDERIV%dVolHtCapBulk_dCanWat) = var_info('dVolHtCapBulk_dCanWat' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dVolHtCapBulk_dTk) = var_info('dVolHtCapBulk_dTk' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dVolHtCapBulk_dTkCanopy) = var_info('dVolHtCapBulk_dTkCanopy' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! derivatives in time + deriv_meta(iLookDERIV%mLayerdTemp_dt) = var_info('mLayerdTemp_dt' , 'timestep change in layer temperature' , 'K' ,get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%scalarCanopydTemp_dt) = var_info('scalarCanopydTemp_dt' , 'timestep change in canopy temperature' , 'K' ,get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! ----- ! * basin-wide runoff and aquifer fluxes... diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 482433254..71197db27 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -636,6 +636,9 @@ MODULE var_lookup integer(i4b) :: dVolHtCapBulk_dCanWat = integerMissing ! derivative in bulk heat capacity w.r.t. volumetric water content integer(i4b) :: dVolHtCapBulk_dTk = integerMissing ! derivative in bulk heat capacity w.r.t. temperature integer(i4b) :: dVolHtCapBulk_dTkCanopy = integerMissing ! derivative in bulk heat capacity w.r.t. temperature + ! derivatives in time + integer(i4b) :: mLayerdTemp_dt = integerMissing ! timestep change in layer temperature + integer(i4b) :: scalarCanopydTemp_dt = integerMissing ! timestep change in canopy temperature endtype iLook_deriv @@ -879,7 +882,7 @@ MODULE var_lookup 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,& 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,& 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,& - 61, 62, 63, 64, 65) + 61, 62, 63, 64, 65, 66, 67) ! named variables: model indices type(iLook_index), public,parameter :: iLookINDEX =ilook_index ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacDAE.f90 index 49292031c..8c105ce95 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacDAE.f90 @@ -74,10 +74,7 @@ module computJacDAE_module ! constants USE multiconst,only:& LH_fus, & ! latent heat of fusion (J kg-1) - iden_water, & ! intrinsic density of liquid water (kg m-3) - ! specific heat - Cp_ice, & ! specific heat of ice (J kg-1 K-1) - Cp_water ! specific heat of liquid water (J kg-1 K-1) + iden_water ! intrinsic density of liquid water (kg m-3) implicit none ! define constants @@ -112,12 +109,12 @@ subroutine computJacDAE(& dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) ! input: state variables mLayerTemp, & ! intent(in): vector of layer temperature (K) - mLayerTempPrime, & ! intent(in) + mLayerTempPrime, & ! intent(in): vector of derivative value for layer temperature (K) mLayerMatricHeadPrime, & ! intent(in) mLayerMatricHeadLiqPrime, & ! intent(in) mLayerVolFracWatPrime, & ! intent(in) - scalarCanopyTemp, & ! intent(in) - scalarCanopyTempPrime, & ! intent(in) derivative value for temperature of the vegetation canopy (K) + scalarCanopyTemp, & ! intent(in): temperature of the vegetation canopy (K) + scalarCanopyTempPrime, & ! intent(in): derivative value for temperature of the vegetation canopy (K) scalarCanopyWatPrime, & ! intent(in) ! input-output: Jacobian and its diagonal dMat, & ! intent(inout): diagonal of the Jacobian matrix @@ -153,7 +150,6 @@ subroutine computJacDAE(& real(rkind),intent(in) :: scalarCanopyTemp real(rkind),intent(in) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) real(rkind),intent(in) :: scalarCanopyWatPrime - ! input-output: Jacobian and its diagonal real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix real(rkind),intent(out) :: aJac(:,:) ! Jacobian matrix @@ -175,9 +171,6 @@ subroutine computJacDAE(& integer(i4b) :: pLayer ! indices of soil layers (used for the baseflow derivatives) ! conversion factors real(rkind) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative - ! - real(rkind) :: dVolFracWat_dPsi0_iLayer - ! -------------------------------------------------------------- ! associate variables from data structures associate(& @@ -254,10 +247,10 @@ subroutine computJacDAE(& mLayerdTrans_dTGround => deriv_data%var(iLookDERIV%mLayerdTrans_dTGround )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. ground temperature mLayerdTrans_dCanWat => deriv_data%var(iLookDERIV%mLayerdTrans_dCanWat )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. canopy total water ! derivatives in aquifer transpiration w.r.t. canopy state variables - dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair )%dat(1) ,& !intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature - dAquiferTrans_dTCanopy => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanopy )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy temperature - dAquiferTrans_dTGround => deriv_data%var(iLookDERIV%dAquiferTrans_dTGround )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. ground temperature - dAquiferTrans_dCanWat => deriv_data%var(iLookDERIV%dAquiferTrans_dCanWat )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy total water + dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + dAquiferTrans_dTCanopy => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanopy )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. canopy temperature + dAquiferTrans_dTGround => deriv_data%var(iLookDERIV%dAquiferTrans_dTGround )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. ground temperature + dAquiferTrans_dCanWat => deriv_data%var(iLookDERIV%dAquiferTrans_dCanWat )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. canopy total water ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(in): [dp(:)] derivative in vertical liquid water flux at layer interfaces ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables @@ -268,7 +261,7 @@ subroutine computJacDAE(& dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below dq_dHydStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dHydStateLayerSurfVec )%dat ,& ! intent(in): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers ! derivative in baseflow flux w.r.t. aquifer storage - dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] derivative in baseflow flux w.r.t. aquifer storage (s-1) + dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(in): [dp(:)] derivative in baseflow flux w.r.t. aquifer storage (s-1) ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below @@ -314,21 +307,21 @@ subroutine computJacDAE(& ! compute terms in the Jacobian for vegetation (excluding fluxes) ! NOTE: energy for vegetation is computed *within* the iteration loop as it includes phase change if(ixVegNrg/=integerMissing)then - dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy ) * cj & - + dVolHtCapBulk_dTkCanopy * scalarCanopyTempPrime & - + LH_fus*iden_water * scalarCanopyTempPrime * d2Theta_dTkCanopy2 & - + LH_fus * dFracLiqVeg_dTkCanopy * scalarCanopyWatPrime / canopyDepth - end if + dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy ) * cj & + + dVolHtCapBulk_dTkCanopy * scalarCanopyTempPrime & + + LH_fus*iden_water * scalarCanopyTempPrime * d2Theta_dTkCanopy2 & + + LH_fus * dFracLiqVeg_dTkCanopy * scalarCanopyWatPrime / canopyDepth + endif ! compute additional terms for the Jacobian for the snow-soil domain (excluding fluxes) ! NOTE: energy for snow+soil is computed *within* the iteration loop as it includes phase change do iLayer=1,nLayers - if(ixSnowSoilNrg(iLayer)/=integerMissing) then - dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj & - + dVolHtCapBulk_dTk(iLayer) * mLayerTempPrime(iLayer) & - + LH_fus*iden_water * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & - + LH_fus*iden_water * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) - end if + if(ixSnowSoilNrg(iLayer)/=integerMissing)then + dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj & + + dVolHtCapBulk_dTk(iLayer) * mLayerTempPrime(iLayer) & + + LH_fus*iden_water * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & + + LH_fus*iden_water * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) + endif end do ! compute additional terms for the Jacobian for the soil domain (excluding fluxes) @@ -338,9 +331,9 @@ subroutine computJacDAE(& if(ixRichards==mixdform)then dMat(ixSoilOnlyHyd(iLayer)) = dMat(ixSoilOnlyHyd(iLayer)) + specificStorage * dVolTot_dPsi0(iLayer) * mLayerMatricHeadPrime(iLayer) / theta_sat(iLayer) - end if + endif - end if + endif end do ! define the form of the matrix @@ -366,29 +359,28 @@ subroutine computJacDAE(& if(size(aJac,1)/=size(dMat) .or. size(aJac,2)/=size(dMat))then message=trim(message)//'unexpected shape of the Jacobian matrix: expect aJac(nState,nState)' err=20; return - end if + endif ! ----- ! * energy and liquid fluxes over vegetation... ! --------------------------------------------- if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) - ! * liquid water fluxes for vegetation canopy (-), dt*scalarFracLiqVeg*scalarCanopyLiqDeriv is the derivative in throughfall and canopy drainage with canopy water - if(ixVegHyd/=integerMissing) aJac(ixVegHyd,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanWat - scalarCanopyLiqDeriv)*dt + 1._rkind * cj - - ! * cross-derivative terms for canopy water + ! * energy fluxes with the canopy water if(ixVegHyd/=integerMissing)then - ! cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) + ! * cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) if(ixCasNrg/=integerMissing) aJac(ixVegHyd,ixCasNrg) = -dCanopyEvaporation_dTCanair*dt ! dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy is the derivative in throughfall and canopy drainage with canopy temperature if(ixVegNrg/=integerMissing) aJac(ixVegHyd,ixVegNrg) = -dCanopyEvaporation_dTCanopy*dt + dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy + ! * liquid water fluxes for vegetation canopy (-), dt*scalarFracLiqVeg*scalarCanopyLiqDeriv is the derivative in throughfall and canopy drainage with canopy water + aJac(ixVegHyd,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanWat - scalarCanopyLiqDeriv)*dt + 1._rkind * cj if(ixTopNrg/=integerMissing) aJac(ixVegHyd,ixTopNrg) = -dCanopyEvaporation_dTGround*dt - ! cross-derivative terms w.r.t. canopy water (kg-1 m2) + ! * cross-derivative terms w.r.t. canopy water (kg-1 m2) if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water - ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) + ! * cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth * cj & + dVolHtCapBulk_dCanWat * scalarCanopyTempPrime - (dt/canopyDepth) * dCanopyNetFlux_dCanWat & @@ -396,26 +388,26 @@ subroutine computJacDAE(& if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) endif - ! cross-derivative terms w.r.t. canopy temperature (K-1) + ! * -derivative terms w.r.t. canopy temperature (K-1) if(ixVegNrg/=integerMissing)then if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegNrg) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarCanopyLiqDeriv*dCanLiq_dTcanopy)/iden_water endif - ! energy fluxes with the canopy air space (J m-3 K-1) + ! * energy fluxes with the canopy air space (J m-3 K-1) if(ixCasNrg/=integerMissing)then aJac(ixCasNrg,ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) * cj if(ixVegNrg/=integerMissing) aJac(ixCasNrg,ixVegNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanopyTemp) if(ixTopNrg/=integerMissing) aJac(ixCasNrg,ixTopNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dGroundTemp) endif - ! energy fluxes with the vegetation canopy (J m-3 K-1) + ! * energy fluxes with the vegetation canopy (J m-3 K-1) if(ixVegNrg/=integerMissing)then if(ixCasNrg/=integerMissing) aJac(ixVegNrg,ixCasNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanairTemp) aJac(ixVegNrg,ixVegNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanopyTemp) + dMat(ixVegNrg) if(ixTopNrg/=integerMissing) aJac(ixVegNrg,ixTopNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dGroundTemp) endif - ! energy fluxes with the surface (J m-3 K-1) + ! * energy fluxes with the surface (J m-3 K-1) if(ixTopNrg/=integerMissing)then if(ixCasNrg/=integerMissing) aJac(ixTopNrg,ixCasNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanairTemp) if(ixVegNrg/=integerMissing) aJac(ixTopNrg,ixVegNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanopyTemp) @@ -439,12 +431,12 @@ subroutine computJacDAE(& aJac(jState,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) ! - lower-diagonal elements - if(iLayer > 1)then + if(iLayer>1)then if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixSnowSoilNrg(iLayer-1),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) endif ! - upper diagonal elements - if(iLayer < nLayers)then + if(iLayer 1)then + if(iLayer>1)then if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSnowOnlyHyd(iLayer-1),watState) = 0._rkind ! sub-diagonal: no dependence on other layers endif ! - upper diagonal elements - if(iLayer < nSnow)then + if(iLayer 1)then + if(iLayer>1)then if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) endif @@ -580,7 +571,7 @@ subroutine computJacDAE(& if(nSnowOnlyHyd>0 .and. ixSnowOnlyHyd(nSnow)/=integerMissing)then if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) elseif(computeVegFlux .and. ixVegHyd/=integerMissing)then !ixTopHyd = ixSoilOnlyHyd(1) - if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegNrg) + if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegHyd) endif endif ! (if the subset includes hydrology state variables in the soil domain) @@ -590,13 +581,13 @@ subroutine computJacDAE(& ! ---------------------------------------- if(ixAqWat/=integerMissing) then aJac(ixAqWat,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) * cj - aJac(ixAqWat,ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat aJac(ixAqWat,ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk + aJac(ixAqWat,ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) if(computeVegFlux)then - aJac(ixAqWat,ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) aJac(ixAqWat,ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) + aJac(ixAqWat,ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 aJac(ixAqWat,ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) endif endif @@ -619,7 +610,7 @@ subroutine computJacDAE(& ! - define index of hydrology state variable within the state subset watState = ixSoilOnlyHyd(iLayer) - ! only compute derivatives if the energy state for the current layer is within the state subset + ! only compute derivatives if the water state for the current layer is within the state subset if(watstate/=integerMissing)then ! - include derivatives in liquid water fluxes w.r.t. temperature for current layer @@ -647,9 +638,9 @@ subroutine computJacDAE(& ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) if(computeVegFlux)then - aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dCanWat(iLayer)) + aJac(watState,ixVegHyd) ! dVol/dLiq (kg m-2)-1 aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanair(iLayer)) + aJac(watState,ixCasNrg) ! dVol/dT (K-1) aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanopy(iLayer)) + aJac(watState,ixVegNrg) ! dVol/dT (K-1) + aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dCanWat(iLayer)) + aJac(watState,ixVegHyd) ! dVol/dLiq (kg m-2)-1 aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTGround(iLayer)) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) endif @@ -683,7 +674,7 @@ subroutine computJacDAE(& ! - include terms for surface infiltration above surface if(nSnowOnlyHyd>0 .and. ixSnowOnlyNrg(nSnow)/=integerMissing)then if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) - elseif(computeVegFlux .and. ixVegNrg/=integerMissing) then !ixTopHyd = ixSoilOnlyHyd(1) + elseif(computeVegFlux .and. ixVegNrg/=integerMissing)then !ixTopHyd = ixSoilOnlyHyd(1) if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegNrg) endif @@ -696,7 +687,7 @@ subroutine computJacDAE(& do iLayer=min(iJac1,nState),min(iJac2,nState) write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(min(iJac1,nState):min(iJac2,nState),iLayer) end do - end if + endif !print*, '** analytical Jacobian (full):' !write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=1,size(aJac,2)) @@ -711,12 +702,12 @@ subroutine computJacDAE(& end select ! type of matrix - if(any(isNan(aJac)))then - print *, '******************************* WE FOUND NAN IN JACOBIAN ************************************' - stop 1 - message=trim(message)//'we found NaN' - err=20; return - endif + if(any(isNan(aJac)))then + print *, '******************************* WE FOUND NAN IN JACOBIAN ************************************' + stop 1 + message=trim(message)//'we found NaN' + err=20; return + endif ! end association to variables in the data structures end associate diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index ff90f9dc6..0d11c6da7 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -69,7 +69,6 @@ module computJacob_module ! constants USE multiconst,only:& LH_fus, & ! latent heat of fusion (J kg-1) - iden_ice, & ! intrinsic density of ice (kg m-3) iden_water ! intrinsic density of liquid water (kg m-3) implicit none @@ -107,7 +106,7 @@ subroutine computJacob(& ! ----------------------------------------------------------------------------------------------------------------- implicit none ! input: model control - real(rkind),intent(in) :: dt ! length of the time step (seconds) + real(rkind),intent(in) :: dt ! length of the time step (seconds) integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain @@ -140,7 +139,7 @@ subroutine computJacob(& integer(i4b) :: jLayer ! index of model layer within the full state vector (hydrology) integer(i4b) :: pLayer ! indices of soil layers (used for the baseflow derivatives) ! conversion factors - real(rkind) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative + real(rkind) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative ! -------------------------------------------------------------- ! associate variables from data structures associate(& @@ -206,19 +205,44 @@ subroutine computJacob(& ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below + ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below + dNrgFlux_dWatAbove => deriv_data%var(iLookDERIV%dNrgFlux_dWatAbove )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. water state in the layer above + dNrgFlux_dWatBelow => deriv_data%var(iLookDERIV%dNrgFlux_dWatBelow )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. water state in the layer below + ! derivatives in soil transpiration w.r.t. canopy state variables + mLayerdTrans_dTCanair => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanair )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + mLayerdTrans_dTCanopy => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanopy )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. canopy temperature + mLayerdTrans_dTGround => deriv_data%var(iLookDERIV%mLayerdTrans_dTGround )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. ground temperature + mLayerdTrans_dCanWat => deriv_data%var(iLookDERIV%mLayerdTrans_dCanWat )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. canopy total water + ! derivatives in aquifer transpiration w.r.t. canopy state variables + dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + dAquiferTrans_dTCanopy => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanopy )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. canopy temperature + dAquiferTrans_dTGround => deriv_data%var(iLookDERIV%dAquiferTrans_dTGround )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. ground temperature + dAquiferTrans_dCanWat => deriv_data%var(iLookDERIV%dAquiferTrans_dCanWat )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. canopy total water ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(in): [dp(:)] derivative in vertical liquid water flux at layer interfaces ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t matric head dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below - dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t matric head + dq_dHydStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dHydStateLayerSurfVec )%dat ,& ! intent(in): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers ! derivative in baseflow flux w.r.t. aquifer storage - dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] erivative in baseflow flux w.r.t. aquifer storage (s-1) + dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(in): [dp(:)] erivative in baseflow flux w.r.t. aquifer storage (s-1) ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + dq_dNrgStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dNrgStateLayerSurfVec )%dat ,& ! intent(in): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers + ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk )%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + ! derivative in bulk heat capacity w.r.t. relevant state variables + dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. temperature + ! derivatives in time + mLayerdTemp_dt => deriv_data%var(iLookDERIV%mLayerdTemp_dt )%dat ,& ! intent(in): [dp(:)] timestep change in layer temperature + scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt )%dat(1) ,& ! intent(in): [dp ] timestep change in canopy temperature ! diagnostic variables scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) @@ -248,17 +272,25 @@ subroutine computJacob(& ! compute terms in the Jacobian for vegetation (excluding fluxes) ! NOTE: energy for vegetation is computed *within* the iteration loop as it includes phase change - if(ixVegNrg/=integerMissing) dMat(ixVegNrg) = scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy ! volumetric heat capacity of the vegetation (J m-3 K-1) + if(ixVegNrg/=integerMissing)then + dMat(ixVegNrg) = scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy & + + dVolHtCapBulk_dTkCanopy * scalarCanopydTemp_dt + endif ! compute additional terms for the Jacobian for the snow-soil domain (excluding fluxes) ! NOTE: energy for snow+soil is computed *within* the iteration loop as it includes phase change do iLayer=1,nLayers - if(ixSnowSoilNrg(iLayer)/=integerMissing) dMat(ixSnowSoilNrg(iLayer)) = mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) + if(ixSnowSoilNrg(iLayer)/=integerMissing)then + dMat(ixSnowSoilNrg(iLayer)) = mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) & + + dVolHtCapBulk_dTk(iLayer) * mLayerdTemp_dt(iLayer) + endif end do ! compute additional terms for the Jacobian for the soil domain (excluding fluxes) do iLayer=1,nSoil - if(ixSoilOnlyHyd(iLayer)/=integerMissing) dMat(ixSoilOnlyHyd(iLayer)) = dVolTot_dPsi0(iLayer) + dCompress_dPsi(iLayer) + if(ixSoilOnlyHyd(iLayer)/=integerMissing)then + dMat(ixSoilOnlyHyd(iLayer)) = dVolTot_dPsi0(iLayer) + dCompress_dPsi(iLayer) + endif end do ! define the form of the matrix @@ -275,50 +307,54 @@ subroutine computJacob(& if(size(aJac,1)/=nBands .or. size(aJac,2)/=size(dMat))then message=trim(message)//'unexpected shape of the Jacobian matrix: expect aJac(nBands,nState)' err=20; return - end if + endif ! ----- ! * energy and liquid fluxes over vegetation... ! --------------------------------------------- if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) - ! * diagonal elements for the vegetation canopy (-) - if(ixCasNrg/=integerMissing) aJac(ixDiag,ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) - if(ixVegNrg/=integerMissing) aJac(ixDiag,ixVegNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanopyTemp) + dMat(ixVegNrg) - if(ixVegHyd/=integerMissing) aJac(ixDiag,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanWat - scalarCanopyLiqDeriv)*dt + 1._rkind ! ixVegHyd: CORRECT - - ! * cross-derivative terms w.r.t. canopy water + ! * energy fluxes with the canopy water if(ixVegHyd/=integerMissing)then - ! cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) - if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixCasNrg),ixCasNrg) = -dCanopyEvaporation_dTCanair*dt ! ixCasNrg: CORRECT - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixVegNrg),ixVegNrg) = -dCanopyEvaporation_dTCanopy*dt + dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy ! ixVegNrg: CORRECT - if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixTopNrg),ixTopNrg) = -dCanopyEvaporation_dTGround*dt ! ixTopNrg: CORRECT - ! cross-derivative terms w.r.t. canopy water (kg-1 m2) + + ! * cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) + if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixCasNrg),ixCasNrg) = -dCanopyEvaporation_dTCanair*dt + ! dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy is the derivative in throughfall and canopy drainage with canopy temperature + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixVegNrg),ixVegNrg) = -dCanopyEvaporation_dTCanopy*dt + dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy + ! * liquid water fluxes for vegetation canopy (-), dt*scalarFracLiqVeg*scalarCanopyLiqDeriv is the derivative in throughfall and canopy drainage with canopy water + aJac(ixDiag, ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanWat - scalarCanopyLiqDeriv)*dt + 1._rkind + if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixTopNrg),ixTopNrg) = -dCanopyEvaporation_dTGround*dt + + ! * cross-derivative terms w.r.t. canopy water (kg-1 m2) if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water - ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) + + ! * cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (dt/canopyDepth) *(-dCanopyNetFlux_dCanWat) - (1._rkind - scalarFracLiqVeg)*LH_fus/canopyDepth ! dF/dLiq + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth & + + dVolHtCapBulk_dCanWat * scalarCanopydTemp_dt - (dt/canopyDepth) * dCanopyNetFlux_dCanWat ! dF/dLiq if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) endif - ! cross-derivative terms between surface hydrology and the temperature of the vegetation canopy (K-1) + ! * cross-derivative terms w.r.t. canopy temperature (K-1) if(ixVegNrg/=integerMissing)then if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarCanopyLiqDeriv*dCanLiq_dTcanopy)/iden_water endif - ! cross-derivative terms w.r.t. the temperature of the canopy air space (J m-3 K-1) + ! * energy fluxes with the canopy air space (J m-3 K-1) if(ixCasNrg/=integerMissing)then + aJac(ixDiag, ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixCasNrg,ixVegNrg),ixVegNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanopyTemp) if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixCasNrg,ixTopNrg),ixTopNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dGroundTemp) endif - ! cross-derivative terms w.r.t. the temperature of the vegetation canopy (J m-3 K-1) + ! * energy fluxes with the vegetation canopy (J m-3 K-1) if(ixVegNrg/=integerMissing)then if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixCasNrg),ixCasNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanairTemp) + aJac(ixDiag, ixVegNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanopyTemp) + dMat(ixVegNrg) if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixTopNrg),ixTopNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dGroundTemp) endif - ! cross-derivative terms w.r.t. the temperature of the surface (J m-3 K-1) + ! * energy fluxes with the surface (J m-3 K-1) if(ixTopNrg/=integerMissing)then if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanairTemp) if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanopyTemp) @@ -342,12 +378,12 @@ subroutine computJacob(& aJac(ixDiag,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) ! - lower-diagonal elements - if(iLayer > 1)then + if(iLayer>1)then if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowSoilNrg(iLayer-1),jState),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) endif ! - upper diagonal elements - if(iLayer < nLayers)then + if(iLayer 1)then + if(iLayer>1)then if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyHyd(iLayer-1),watState),watState) = 0._rkind ! sub-diagonal: no dependence on other layers endif ! - upper diagonal elements - if(iLayer < nSnow)then + if(iLayer0)then + end do ! (looping through liquid water states in the snow domain) + endif ! (if the subset includes hydrology state variables in the snow domain) - ! (define the energy state) - nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector - if(nrgstate/=integerMissing)then ! (energy state for the current layer is within the state subset) + ! ----- + ! * cross derivatives in the snow domain... + ! ---------------------------------------- + if(nSnowOnlyHyd>0 .and. nSnowOnlyNrg>0)then + do iLayer=1,nSnow ! loop through layers in the snow domain - ! (cross-derivative terms for the current layer) - aJac(ixOffDiag(nrgState,watState),watState) = -(1._rkind - mLayerFracLiqSnow(iLayer))*LH_fus*iden_water ! (dF/dLiq) - aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) + ! - check that the snow layer is desired + if(ixSnowOnlyNrg(iLayer)==integerMissing) cycle - ! (cross-derivative terms for the layer below) - if(iLayer < nSnow)then - aJac(ixOffDiag(ixSnowOnlyHyd(iLayer+1),nrgState),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 - endif ! (if there is a water state in the layer below the current layer in the given state subset) + ! (define the energy state) + nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector - endif ! (if the energy state for the current layer is within the state subset) - endif ! (if state variables exist for energy in snow+soil layers) + ! - define state indices for the current layer + watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - end do ! (looping through liquid water states in the snow domain) - endif ! (if the subset includes hydrology state variables in the snow domain) + if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) + + ! - include derivatives of energy fluxes w.r.t water fluxes for current layer + aJac(ixOffDiag(nrgState,watState),watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water & + + dVolHtCapBulk_dTheta(iLayer) * mLayerdTemp_dt(iLayer) & + + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) ! (dF/dLiq) + + ! - include derivatives of water fluxes w.r.t energy fluxes for current layer + aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) + + ! (cross-derivative terms for the layer below) + if(iLayer < nSnow)then + aJac(ixOffDiag(ixSnowOnlyHyd(iLayer+1),nrgState),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 + endif ! (if there is a water state in the layer below the current layer in the given state subset) + + ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above + if(iLayer>1)then + if(ixSnowOnlyNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyNrg(iLayer-1),watState),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) ) + endif + + ! (cross-derivative terms for the layer below) + if(iLayer0)then !bottom snow layer and there is soil below + if(ixSoilOnlyNrg(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyNrg(1),watState),watState) = (dt/mLayerDepth(nSnow+1))*(-dNrgFlux_dWatAbove(nSnow) ) + endif + + endif ! (if the energy state for the current layer is within the state subset) + + end do ! (looping through snow layers) + endif ! (if there are state variables for both water and energy in the snow domain) ! ----- ! * liquid water fluxes for the soil domain... @@ -428,7 +491,7 @@ subroutine computJacob(& aJac(ixDiag,watState) = (dt/mLayerDepth(jLayer))*(-dq_dHydStateBelow(iLayer-1) + dq_dHydStateAbove(iLayer)) + dMat(watState) ! - compute the lower-diagonal elements - if(iLayer > 1)then + if(iLayer>1)then if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),watState),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) endif @@ -437,25 +500,52 @@ subroutine computJacob(& if(ixSoilOnlyHyd(iLayer+1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer+1),watState),watState) = (dt/mLayerDepth(jLayer+1))*(-dq_dHydStateAbove(iLayer)) endif + ! - include terms for baseflow WHY NOT USE + !if(computeBaseflow .and. nSoilOnlyHyd==nSoil)then + ! do pLayer=1,nSoil + ! qState = ixSoilOnlyHyd(pLayer) ! hydrology state index within the state subset + ! aJac(ixOffDiag(watState,qState),qState) = (dt/mLayerDepth(jLayer))*dBaseflow_dMatric(iLayer,pLayer) + aJac(ixOffDiag(watState,qState),qState) + ! end do + !endif + + ! - include terms for surface infiltration below surface + if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),watState),watState) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(iLayer) + aJac(ixOffDiag(ixSoilOnlyHyd(1),watState),watState) + end do ! (looping through hydrology states in the soil domain) + + ! - include terms for surface infiltration above surface + if(nSnowOnlyHyd>0 .and. ixSnowOnlyHyd(nSnow)/=integerMissing)then + if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + elseif(computeVegFlux .and. ixVegHyd/=integerMissing)then !ixTopHyd = ixSoilOnlyHyd(1) + if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) + endif + endif ! (if the subset includes hydrology state variables in the soil domain) ! ----- ! * liquid water fluxes for the aquifer... ! ---------------------------------------- - if(ixAqWat/=integerMissing) aJac(ixDiag,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) + if(ixAqWat/=integerMissing) then + aJac(ixDiag,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) + aJac(ixOffDiag(ixAqWat,ixSoilOnlyNrg(nSoil)),ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk + aJac(ixOffDiag(ixAqWat,ixSoilOnlyHyd(nSoil)),ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat + ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) + if(computeVegFlux)then + aJac(ixOffDiag(ixAqWat,ixCasNrg),ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) + aJac(ixOffDiag(ixAqWat,ixVegNrg),ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) + aJac(ixOffDiag(ixAqWat,ixVegHyd),ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 + aJac(ixOffDiag(ixAqWat,ixTopNrg),ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) + endif + endif ! ----- - ! * derivative in liquid water fluxes w.r.t. temperature for the soil domain... + ! * cross derivatives in the soil domain... ! ----------------------------------------------------------------------------- if(nSoilOnlyHyd>0 .and. nSoilOnlyNrg>0)then - do iLayer=1,nSoilOnlyHyd + do iLayer=1,nSoilOnlyNrg ! - check that the soil layer is desired - if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle - - ! - define index of hydrology state variable within the state subset - watState = ixSoilOnlyHyd(iLayer) + if(ixSoilOnlyNrg(iLayer)==integerMissing) cycle ! - define indices of the soil layers jLayer = iLayer+nSnow ! index of layer in the snow+soil vector @@ -463,42 +553,76 @@ subroutine computJacob(& ! - define the energy state variable nrgState = ixNrgLayer(jLayer) ! index within the full state vector - ! only compute derivatives if the energy state for the current layer is within the state subset - if(nrgstate/=integerMissing)then + ! - define index of hydrology state variable within the state subset + watState = ixSoilOnlyHyd(iLayer) - ! - compute the Jacobian for the layer itself + ! only compute derivatives if the water state for the current layer is within the state subset + if(watstate/=integerMissing)then + + ! - include derivatives in liquid water fluxes w.r.t. temperature for current layer aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance + ! - compute lower diagonal elements + if(iLayer>1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),nrgState),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 + endif + + ! compute upper-diagonal elements + if(iLayer verySmall)then ! ice is present - aJac(ixOffDiag(nrgState,watState),watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content - else - aJac(ixOffDiag(nrgState,watState),watState) = 0._rkind + aJac(ixOffDiag(nrgState,watState),watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) + aJac(ixOffDiag(nrgState,watState),watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content endif - ! - compute lower diagonal elements + ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above if(iLayer>1)then - if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),nrgState),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 + if(ixSoilOnlyNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyNrg(iLayer-1),watState),watState) = (dt/mLayerDepth(jLayer-1))*( dNrgFlux_dWatBelow(jLayer-1) ) + elseif(iLayer==1 .and. nSnowOnlyNrg>0)then !top soil layer and there is snow above + if(ixSnowOnlyNrg(nSnow)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyNrg(nSnow),watState),watState) = (dt/mLayerDepth(nSnow))*( dNrgFlux_dWatBelow(nSnow) ) endif - ! compute upper-diagonal elements + ! (cross-derivative terms for the layer below) if(iLayer0 .and. ixSnowOnlyNrg(nSnow)/=integerMissing)then + if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + elseif(computeVegFlux .and. ixVegNrg/=integerMissing) then !ixTopHyd = ixSoilOnlyHyd(1) + if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) + endif + endif ! (if there are state variables for both water and energy in the soil domain) if(globalPrintFlag)then @@ -507,7 +631,7 @@ subroutine computJacob(& do iLayer=kl+1,nBands write(*,'(i4,1x,100(e17.10,1x))') iLayer, (aJac(iLayer,jLayer),jLayer=min(iJac1,nState),min(iJac2,nState)) end do - end if + endif !print*, 'PAUSE: banded analytical Jacobian'; read(*,*) ! ********************************************************************************************************************************************************* @@ -521,27 +645,31 @@ subroutine computJacob(& if(size(aJac,1)/=size(dMat) .or. size(aJac,2)/=size(dMat))then message=trim(message)//'unexpected shape of the Jacobian matrix: expect aJac(nState,nState)' err=20; return - end if + endif ! ----- ! * energy and liquid fluxes over vegetation... ! --------------------------------------------- if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) - ! * liquid water fluxes for vegetation canopy (-) - if(ixVegHyd/=integerMissing) aJac(ixVegHyd,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanWat - scalarCanopyLiqDeriv)*dt + 1._rkind - - ! * cross-derivative terms for canopy water + ! * energy fluxes with the canopy water if(ixVegHyd/=integerMissing)then + ! cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) if(ixCasNrg/=integerMissing) aJac(ixVegHyd,ixCasNrg) = -dCanopyEvaporation_dTCanair*dt + ! dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy is the derivative in throughfall and canopy drainage with canopy temperature if(ixVegNrg/=integerMissing) aJac(ixVegHyd,ixVegNrg) = -dCanopyEvaporation_dTCanopy*dt + dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy + ! * liquid water fluxes for vegetation canopy (-) + aJac(ixVegHyd,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanWat - scalarCanopyLiqDeriv)*dt + 1._rkind if(ixTopNrg/=integerMissing) aJac(ixVegHyd,ixTopNrg) = -dCanopyEvaporation_dTGround*dt + ! cross-derivative terms w.r.t. canopy water (kg-1 m2) if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water + ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (dt/canopyDepth) *(-dCanopyNetFlux_dCanWat) - (1._rkind - scalarFracLiqVeg)*LH_fus/canopyDepth ! dF/dLiq + if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth & + + dVolHtCapBulk_dCanWat * scalarCanopydTemp_dt - (dt/canopyDepth) * dCanopyNetFlux_dCanWat ! dF/dLiq if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) endif @@ -588,12 +716,12 @@ subroutine computJacob(& aJac(jState,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) ! - lower-diagonal elements - if(iLayer > 1)then + if(iLayer>1)then if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixSnowSoilNrg(iLayer-1),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) endif ! - upper diagonal elements - if(iLayer < nLayers)then + if(iLayer 1)then + if(iLayer>1)then if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSnowOnlyHyd(iLayer-1),watState) = 0._rkind ! sub-diagonal: no dependence on other layers endif ! - upper diagonal elements - if(iLayer < nSnow)then + if(iLayer0)then + end do ! (looping through liquid water states in the snow domain) + endif ! (if the subset includes hydrology state variables in the snow domain) - ! (define the energy state) - nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector - if(nrgstate/=integerMissing)then ! (energy state for the current layer is within the state subset) + ! ----- + ! * cross derivatives in the snow domain... + ! ---------------------------------------- + if(nSnowOnlyHyd>0 .and. nSnowOnlyNrg>0)then + do iLayer=1,nSnow ! loop through layers in the snow domain + + ! - check that the snow layer is desired + if(ixSnowOnlyNrg(iLayer)==integerMissing) cycle - ! (cross-derivative terms for the current layer) - aJac(nrgState,watState) = -(1._rkind - mLayerFracLiqSnow(iLayer))*LH_fus*iden_water ! (dF/dLiq) - aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) + ! (define the energy state) + nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector - ! (cross-derivative terms for the layer below) - if(iLayer < nSnow)then - aJac(ixSnowOnlyHyd(iLayer+1),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 - endif ! (if there is a water state in the layer below the current layer in the given state subset) + ! - define state indices for the current layer + watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - endif ! (if the energy state for the current layer is within the state subset) - endif ! (if state variables exist for energy in snow+soil layers) + if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) - end do ! (looping through liquid water states in the snow domain) - endif ! (if the subset includes hydrology state variables in the snow domain) + ! - include derivatives of energy fluxes w.r.t water fluxes for current layer + aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water & + + dVolHtCapBulk_dTheta(iLayer) * mLayerdTemp_dt(iLayer) & + + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) ! (dF/dLiq) + + ! - include derivatives of water fluxes w.r.t energy fluxes for current layer + aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) + + ! (cross-derivative terms for the layer below) + if(iLayer < nSnow)then + aJac(ixSnowOnlyHyd(iLayer+1),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 + endif ! (if there is a water state in the layer below the current layer in the given state subset) + + ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above + if(iLayer>1)then + if(ixSnowOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSnowOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) ) + endif + + ! (cross-derivative terms for the layer below) + if(iLayer0)then !bottom snow layer and there is soil below + if(ixSoilOnlyNrg(1)/=integerMissing) aJac(ixSoilOnlyNrg(1),watState) = (dt/mLayerDepth(nSnow+1))*(-dNrgFlux_dWatAbove(nSnow) ) + endif + + endif ! (if the energy state for the current layer is within the state subset) + + end do ! (looping through snow layers) + endif ! (if there are state variables for both water and energy in the snow domain) ! ----- ! * liquid water fluxes for the soil domain... ! -------------------------------------------- if(nSoilOnlyHyd>0)then - do iLayer=1,nSoil ! - check that the soil layer is desired @@ -675,7 +829,7 @@ subroutine computJacob(& aJac(watState,watState) = (dt/mLayerDepth(jLayer))*(-dq_dHydStateBelow(iLayer-1) + dq_dHydStateAbove(iLayer)) + dMat(watState) ! - compute the lower-diagonal elements - if(iLayer > 1)then + if(iLayer>1)then if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) endif @@ -688,29 +842,48 @@ subroutine computJacob(& if(computeBaseflow .and. nSoilOnlyHyd==nSoil)then do pLayer=1,nSoil qState = ixSoilOnlyHyd(pLayer) ! hydrology state index within the state subset - aJac(watState,qState) = aJac(watState,qState) + (dt/mLayerDepth(jLayer))*dBaseflow_dMatric(iLayer,pLayer) + aJac(watState,qState) = (dt/mLayerDepth(jLayer))*dBaseflow_dMatric(iLayer,pLayer) + aJac(watState,qState) end do endif + ! - include terms for surface infiltration below surface + if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),watState) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(iLayer) + aJac(ixSoilOnlyHyd(1),watState) + end do ! (looping through hydrology states in the soil domain) + + ! - include terms for surface infiltration above surface + if(nSnowOnlyHyd>0 .and. ixSnowOnlyHyd(nSnow)/=integerMissing)then + if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + elseif(computeVegFlux .and. ixVegHyd/=integerMissing)then !ixTopHyd = ixSoilOnlyHyd(1) + if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegHyd) + endif + endif ! (if the subset includes hydrology state variables in the soil domain) ! ----- ! * liquid water fluxes for the aquifer... ! ---------------------------------------- - if(ixAqWat/=integerMissing) aJac(ixAqWat,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) + if(ixAqWat/=integerMissing) then + aJac(ixAqWat,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) + aJac(ixAqWat,ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk + aJac(ixAqWat,ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat + ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) + if(computeVegFlux)then + aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) + aJac(ixAqWat,ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) + aJac(ixAqWat,ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 + aJac(ixAqWat,ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) + endif + endif ! ----- - ! * derivative in liquid water fluxes w.r.t. temperature for the soil domain... + ! * cross derivatives in the soil domain... ! ----------------------------------------------------------------------------- if(nSoilOnlyHyd>0 .and. nSoilOnlyNrg>0)then - do iLayer=1,nSoilOnlyHyd + do iLayer=1,nSoilOnlyNrg ! - check that the soil layer is desired - if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle - - ! - define index of hydrology state variable within the state subset - watState = ixSoilOnlyHyd(iLayer) + if(ixSoilOnlyNrg(iLayer)==integerMissing) cycle ! - define indices of the soil layers jLayer = iLayer+nSnow ! index of layer in the snow+soil vector @@ -718,42 +891,76 @@ subroutine computJacob(& ! - define the energy state variable nrgState = ixNrgLayer(jLayer) ! index within the full state vector - ! only compute derivatives if the energy state for the current layer is within the state subset - if(nrgstate/=integerMissing)then + ! - define index of hydrology state variable within the state subset + watState = ixSoilOnlyHyd(iLayer) - ! - compute the Jacobian for the layer itself + ! only compute derivatives if the water state for the current layer is within the state subset + if(watstate/=integerMissing)then + + ! - include derivatives in liquid water fluxes w.r.t. temperature for current layer aJac(watState,nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance + ! - compute lower diagonal elements + if(iLayer>1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 + endif + + ! compute upper-diagonal elements + if(iLayer verySmall)then ! ice is present - aJac(nrgState,watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content - else - aJac(nrgState,watState) = 0._rkind + aJac(nrgState,watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content endif - ! - compute lower diagonal elements + ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above if(iLayer>1)then - if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 + if(ixSoilOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSoilOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(jLayer-1))*( dNrgFlux_dWatBelow(jLayer-1) ) + elseif(iLayer==1 .and. nSnowOnlyNrg>0)then !top soil layer and there is snow above + if(ixSnowOnlyNrg(nSnow)/=integerMissing) aJac(ixSnowOnlyNrg(nSnow),watState) = (dt/mLayerDepth(nSnow))*( dNrgFlux_dWatBelow(nSnow) ) endif - ! compute upper-diagonal elements + ! (cross-derivative terms for the layer below) if(iLayer0 .and. ixSnowOnlyNrg(nSnow)/=integerMissing)then + if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + elseif(computeVegFlux .and. ixVegNrg/=integerMissing) then !ixTopHyd = ixSoilOnlyHyd(1) + if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegNrg) + endif + endif ! (if there are state variables for both water and energy in the soil domain) ! print the Jacobian @@ -763,7 +970,14 @@ subroutine computJacob(& do iLayer=min(iJac1,nState),min(iJac2,nState) write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(min(iJac1,nState):min(iJac2,nState),iLayer) end do - end if + endif + + !print*, '** analytical Jacobian (full):' + !write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=1,size(aJac,2)) + !do iLayer=1,size(aJac,1) + ! write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(iLayer,1:size(aJac,2)) + !end do + !print *, '--------------------------------------------------------------' ! *** ! check @@ -771,6 +985,13 @@ subroutine computJacob(& end select ! type of matrix + if(any(isNan(aJac)))then + print *, '******************************* WE FOUND NAN IN JACOBIAN ************************************' + stop 1 + message=trim(message)//'we found NaN' + err=20; return + endif + ! end association to variables in the data structures end associate diff --git a/build/source/engine/computResidDAE.f90 b/build/source/engine/computResidDAE.f90 index a87b2e719..76920b6af 100644 --- a/build/source/engine/computResidDAE.f90 +++ b/build/source/engine/computResidDAE.f90 @@ -99,8 +99,8 @@ subroutine computResidDAE(& real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(rkind),intent(in) :: fVec(:) ! flux vector ! input: state variables (already disaggregated into scalars and vectors) - real(rkind),intent(in) :: scalarCanopyTempTrial - real(rkind),intent(in) :: mLayerTempTrial(:) + real(rkind),intent(in) :: scalarCanopyTempTrial + real(rkind),intent(in) :: mLayerTempTrial(:) real(rkind),intent(in) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) real(rkind),intent(in) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) real(rkind),intent(in) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) @@ -120,7 +120,7 @@ subroutine computResidDAE(& type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers ! output - real(rkind),intent(out) :: rAdd(:) ! additional (sink) terms on the RHS of the state equation + real(rkind),intent(out) :: rAdd(:) ! additional (sink) terms on the RHS of the state equation real(qp),intent(out) :: rVec(:) ! NOTE: qp ! residual vector integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index a83f8acda..f17b635ed 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -154,7 +154,7 @@ subroutine eval8DAE(& err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- ! provide access to subroutines - USE varExtrSundials_module, only:varExtract2 ! extract variables from the state vector + USE getVectorz_module, only:varExtract ! extract variables from the state vector USE varExtrSundials_module, only:varExtractSundials USE updateVarsSundials_module, only:updateVarsSundials ! update variables USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy @@ -400,7 +400,7 @@ subroutine eval8DAE(& ixEnd = nSoil endif - ! initialize to state variable from the last update + ! initialize to state variable from the last update scalarCanopyTempTrial = scalarCanopyTempPrev scalarCanopyLiqTrial = scalarCanopyLiqPrev scalarCanopyIceTrial = scalarCanopyIcePrev @@ -412,7 +412,7 @@ subroutine eval8DAE(& scalarAquiferStorageTrial = scalarAquiferStoragePrev ! extract variables from the model state vector - call varExtract2(& + call varExtract(& ! input stateVec, & ! intent(in): model state vector (mixed units) diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -498,7 +498,6 @@ subroutine eval8DAE(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! print the water content if(globalPrintFlag)then if(iJac1 canopyTempMax) feasible=.false. + if(stateVecTrial(ixVegNrg) > canopyTempMax) message=trim(message)//'canopy temp high,' + if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVecTrial( ixVegNrg )', feasible, canopyTempMax, stateVecTrial(ixVegNrg) endif ! check canopy liquid water is not negative if(ixVegHyd/=integerMissing)then if(stateVecTrial(ixVegHyd) < 0._rkind) feasible=.false. + if(stateVecTrial(ixVegHyd) < 0._rkind) message=trim(message)//'canopy water negative,' + if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVecTrial( ixVegHyd )', feasible, 0._rkind, stateVecTrial(ixVegHyd) + end if ! check snow temperature is below freezing if(count(ixSnowOnlyNrg/=integerMissing)>0)then if(any(stateVecTrial( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. + if(any(stateVecTrial( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) message=trim(message)//'snow temp above freezing,' + do iLayer=1,nSnow + if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVecTrial( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVecTrial( ixSnowOnlyNrg(iLayer) ) + enddo endif ! loop through non-missing hydrology state variables in the snow+soil domain @@ -321,7 +341,8 @@ subroutine eval8summa(& ! --> check if(stateVecTrial( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVecTrial( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. - !if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax + if(stateVecTrial( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVecTrial( ixSnowSoilHyd(iLayer) ) > xMax) message=trim(message)//'layer water outside bounds,' + if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax endif ! if water states @@ -332,8 +353,9 @@ subroutine eval8summa(& fluxVec(:) = realMissing resVec(:) = quadMissing fEval = realMissing - return - end if + message=trim(message)//'non-feasible' + err=20; return + endif ! get the start and end indices for the soil compression calculations if(scalarSolution)then @@ -345,6 +367,20 @@ subroutine eval8summa(& ixEnd = nSoil endif + ! initialize to state variable from the last update + scalarCanairTempTrial = scalarCanairTemp + scalarCanopyTempTrial = scalarCanopyTemp + scalarCanopyWatTrial = scalarCanopyWat + scalarCanopyLiqTrial = scalarCanopyLiq + scalarCanopyIceTrial = scalarCanopyIce + mLayerTempTrial = mLayerTemp + mLayerVolFracWatTrial = mLayerVolFracWat + mLayerVolFracLiqTrial = mLayerVolFracLiq + mLayerVolFracIceTrial = mLayerVolFracIce + mLayerMatricHeadTrial = mLayerMatricHead ! total water matric potential + mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq ! liquid water matric potential + scalarAquiferStorageTrial = scalarAquiferStorage + ! extract variables from the model state vector call varExtract(& ! input @@ -357,12 +393,10 @@ subroutine eval8summa(& scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(out): trial value of canopy ice content (kg m-2) ! output: variables for the snow-soil domain mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) ! output: variables for the aquifer @@ -371,7 +405,7 @@ subroutine eval8summa(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! update diagnostic variables + ! update diagnostic variables and derivatives call updateVars(& ! input .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze diff --git a/build/source/engine/evalDAE4IDA.f90 b/build/source/engine/evalDAE4IDA.f90 index 349d364d9..ed8fad968 100644 --- a/build/source/engine/evalDAE4IDA.f90 +++ b/build/source/engine/evalDAE4IDA.f90 @@ -124,17 +124,17 @@ integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_da eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) - eqns_data%scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) - eqns_data%scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - eqns_data%scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) + eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) + eqns_data%scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) + eqns_data%scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + eqns_data%scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) eqns_data%scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): value for enthalpy of the vegetation canopy (J m-3) eqns_data%mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) eqns_data%mLayerTempPrev, & ! intent(in): vector of layer temperature (K) eqns_data%mLayerMatricHeadLiqTrial,& ! intent(out): trial value for liquid water matric potential (m) eqns_data%mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) - eqns_data%mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) + eqns_data%mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) eqns_data%mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) eqns_data%mLayerVolFracWatPrev, & ! intent(in): vector of volumetric total water content (-) eqns_data%mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) @@ -142,10 +142,10 @@ integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_da eqns_data%mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) eqns_data%mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) eqns_data%scalarAquiferStorageTrial, & ! intent(out): trial value of storage of water in the aquifer (m) - eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) + eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) - eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer + eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer ! output feasible, & ! intent(out): flag to denote the feasibility of the solution eqns_data%fluxVec, & ! intent(out): flux vector diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index e04f36fca..c2359aece 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -400,9 +400,9 @@ subroutine getScaling(& end subroutine getScaling - ! ********************************************************************************************************** ! public subroutine varExtract: extract variables from the state vector and compute diagnostic variables + ! This routine does not initialize any of the variables ! ********************************************************************************************************** subroutine varExtract(& ! input @@ -415,12 +415,10 @@ subroutine varExtract(& scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(out): trial value of canopy ice content (kg m-2) ! output: variables for the snow-soil domain mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) ! output: variables for the aquifer @@ -440,12 +438,10 @@ subroutine varExtract(& real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) real(rkind),intent(out) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) real(rkind),intent(out) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) - real(rkind),intent(out) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) ! output: variables for the snow-soil domain real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) real(rkind),intent(out) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) real(rkind),intent(out) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(out) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric ice water content (-) real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) real(rkind),intent(out) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) ! output: variables for the aquifer @@ -474,24 +470,8 @@ subroutine varExtract(& nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain ! indices defining type of model state variables ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] type of desired model state variables - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain - ! model states for the vegetation canopy - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variable vectors for the snow-soil layers - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] total water matric potential (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) - ! model state variables for the aquifer - scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in): [dp] storage of water in the aquifer (m) - ! model diagnostic variables from a previous solution - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat & ! intent(in): [dp(:)] volumetric fraction of ice (-) - ) ! association with variables in the data structures + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat & ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain +)! association with variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- @@ -501,12 +481,6 @@ subroutine varExtract(& ! *** extract state variables for the vegetation canopy - ! initialize to state variable from the last update - scalarCanairTempTrial = scalarCanairTemp - scalarCanopyTempTrial = scalarCanopyTemp - scalarCanopyWatTrial = scalarCanopyWat - scalarCanopyLiqTrial = scalarCanopyLiq - scalarCanopyIceTrial = scalarCanopyIce ! check if computing the vegetation flux if(ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing .or. ixVegHyd/=integerMissing)then @@ -530,14 +504,6 @@ subroutine varExtract(& ! *** extract state variables from the snow+soil sub-domain - ! initialize to the state variable from the last update - mLayerTempTrial = mLayerTemp - mLayerVolFracWatTrial = mLayerVolFracWat - mLayerVolFracLiqTrial = mLayerVolFracLiq - mLayerVolFracIceTrial = mLayerVolFracIce - mLayerMatricHeadTrial = mLayerMatricHead ! total water matric potential - mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq ! liquid water matric potential - scalarAquiferStorageTrial = scalarAquiferStorage ! overwrite with the energy values from the state vector if(nSnowSoilNrg>0)then diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index dfb623051..1698889b2 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -131,7 +131,6 @@ subroutine systemSolvSundials(& ! simulation of fluxes and residuals given a trial state vector USE eval8summa_module,only:eval8summa ! simulation of fluxes and residuals given a trial state vector USE eval8DAE_module,only:eval8DAE - USE summaSolve_module,only:summaSolve ! calculate the iteration increment, evaluate the new state, and refine if necessary USE getVectorz_module,only:getScaling ! get the scaling vectors USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy USE tol4IDA_module,only:popTol4IDA ! pop tolerances @@ -456,7 +455,7 @@ subroutine systemSolvSundials(& ! * solving F(y,y') = 0 by IDA. Here, y is the state vector ! ------------------ - do tol_iter=1,3 + !do tol_iter=1,3 ! initialize flux_sum do concurrent ( iVar=1:size(flux_meta) ) @@ -507,16 +506,16 @@ subroutine systemSolvSundials(& stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - if (idaSucceeds)then - exit - else - atol = atol * 0.1 - rtol = rtol * 0.1 - endif - if( .not.idaSucceeds .and. tol_iter==3) message=trim(message)//'IDA did not succeed after reducing tolerance by 2 magnitudes' - - end do ! iteration over tolerances + !if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + ! if (idaSucceeds)then + ! exit + ! else + ! atol = atol * 0.1 + ! rtol = rtol * 0.1 + ! endif + ! if( .not.idaSucceeds .and. tol_iter==3) message=trim(message)//'IDA did not succeed after reducing tolerance by 2 magnitudes' + + !end do ! iteration over tolerances ! check if IDA is successful if( .not.idaSucceeds )then diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 index a29350f08..c01cac663 100644 --- a/build/source/engine/updateVars.f90 +++ b/build/source/engine/updateVars.f90 @@ -56,6 +56,8 @@ module updateVars_module gravity, & ! acceleration of gravity (m s-2) Tfreeze, & ! temperature at freezing (K) Cp_air, & ! specific heat of air (J kg-1 K-1) + Cp_ice, & ! specific heat of ice (J kg-1 K-1) + Cp_water, & ! specific heat of liquid water (J kg-1 K-1) LH_fus, & ! latent heat of fusion (J kg-1) iden_air, & ! intrinsic density of air (kg m-3) iden_ice, & ! intrinsic density of ice (kg m-3) @@ -104,7 +106,7 @@ module updateVars_module contains ! ********************************************************************************************************** - ! public subroutine updateVars: compute diagnostic variables + ! public subroutine updateVars: compute diagnostic variables and derivatives ! ********************************************************************************************************** subroutine updateVars(& ! input @@ -170,6 +172,7 @@ subroutine updateVars(& real(rkind) :: scalarVolFracIce ! volumetric fraction of ice (-) real(rkind) :: Tcrit ! critical soil temperature below which ice exists (K) real(rkind) :: xTemp ! temporary temperature (K) + real(rkind) :: fLiq ! fraction of liquid water (-) real(rkind) :: effSat ! effective saturation (-) real(rkind) :: avPore ! available pore space (-) character(len=256) :: cMessage ! error message of downwind routine @@ -247,8 +250,16 @@ subroutine updateVars(& dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(out): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) & ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature - ) ! association with variables in the data structures + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature + ! derivatives inside solver for Jacobian only + dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. temperature + mLayerdTemp_dt => deriv_data%var(iLookDERIV%mLayerdTemp_dt )%dat ,& ! intent(out): [dp(:)] timestep change in layer temperature + scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt )%dat(1) & ! intent(out): [dp ] timestep change in canopy temperature +) ! association with variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- @@ -395,31 +406,54 @@ subroutine updateVars(& ! - compute derivatives... ! ------------------------ + ! compute temperature time derivatives + select case(ixDomainType) + case(iname_veg); scalarCanopydTemp_dt = xTemp - scalarCanopyTemp + case(iname_snow, iname_soil); mLayerdTemp_dt(iLayer) = xTemp - mLayerTemp(iLayer) + end select + ! compute the derivative in total water content w.r.t. total water matric potential (m-1) ! NOTE 1: valid for frozen and unfrozen conditions ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi - if(ixDomainType==iname_soil)then - select case( ixStateType(ixFullVector) ) - case(iname_lmpLayer); dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore - case default; dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - end select - endif + select case(ixDomainType) + case(iname_veg) + fLiq = fracLiquid(xTemp,snowfrz_scale) + dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) + case(iname_snow) + fLiq = fracLiquid(xTemp,snowfrz_scale) + dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air + case(iname_soil) + select case( ixStateType(ixFullVector) ) + case(iname_lmpLayer); dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + case default; dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + end select + ! dVolHtCapBulk_dPsi0 uses the derivative in water retention curve above critical temp w.r.t.state variable dVolTot_dPsi0 + dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use + if(xTemp< Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) + if(xTemp>=Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_water * Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) + end select ! compute the derivative in liquid water content w.r.t. temperature ! --> partially frozen: dependence of liquid water on temperature if(xTemp unfrozen: no dependence of liquid water on temperature else select case(ixDomainType) - case(iname_veg); dTheta_dTkCanopy = 0._rkind - case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind + case(iname_veg); dTheta_dTkCanopy = 0._rkind; dVolHtCapBulk_dTkCanopy = 0._rkind + case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return end select ! domain type endif diff --git a/build/source/engine/varExtrSundials.f90 b/build/source/engine/varExtrSundials.f90 index 0760b5125..4a6396308 100644 --- a/build/source/engine/varExtrSundials.f90 +++ b/build/source/engine/varExtrSundials.f90 @@ -74,7 +74,6 @@ module varExtrSundials_module implicit none private -public::varExtract2 public::varExtractSundials public::residDiscontinuity public::countDiscontinuity @@ -83,138 +82,6 @@ module varExtrSundials_module contains - ! ********************************************************************************************************** - ! public subroutine varExtract2: extract variables from the state vector and compute diagnostic variables - ! This routine does not initialize any of the variables and is to be used inside the Sundials iteration, vs varExtract - ! ********************************************************************************************************** - subroutine varExtract2(& - ! input - stateVec, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStorageTrial, & ! intent(out): trial value of storage of water in the aquifer (m) - ! output: error control - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input - real(rkind),intent(in) :: stateVec(:) ! model state vector (mixed units) - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - ! output: variables for the vegetation canopy - real(rkind),intent(out) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) - real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind),intent(out) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - real(rkind),intent(out) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(out) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(rkind),intent(out) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind),intent(out) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) - ! output: variables for the aquifer - real(rkind),intent(out) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - associate(& - ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers - ! indices defining model states and layers - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of the squifer storage state variable - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for energy states in the snow+soil subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for hydrology states in the snow+soil subdomain - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain - ! indices defining type of model state variables - ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] type of desired model state variables - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat & ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain -)! association with variables in the data structures - - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - - ! initialize error control - err=0; message='varExtract2/' - - ! *** extract state variables for the vegetation canopy - - - ! check if computing the vegetation flux - if(ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing .or. ixVegHyd/=integerMissing)then - - ! extract temperature of the canopy air space - if(ixCasNrg/=integerMissing) scalarCanairTempTrial = stateVec(ixCasNrg) - - ! extract canopy temperature - if(ixVegNrg/=integerMissing) scalarCanopyTempTrial = stateVec(ixVegNrg) - - ! extract intercepted water - if(ixVegHyd/=integerMissing)then - select case( ixStateType_subset(ixVegHyd) ) - case(iname_liqCanopy); scalarCanopyLiqTrial = stateVec(ixVegHyd) - case(iname_watCanopy); scalarCanopyWatTrial = stateVec(ixVegHyd) - case default; err=20; message=trim(message)//'case not found: expect iname_liqCanopy or iname_watCanopy'; return - end select - endif - - endif ! not computing the vegetation flux - - ! *** extract state variables from the snow+soil sub-domain - - - ! overwrite with the energy values from the state vector - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - mLayerTempTrial(iLayer) = stateVec( ixSnowSoilNrg(iLayer) ) - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! overwrite with the hydrology values from the state vector - if(nSnowSoilHyd>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) - select case( ixHydType(iLayer) ) - case(iname_watLayer); mLayerVolFracWatTrial(iLayer) = stateVec( ixSnowSoilHyd(iLayer) ) ! total water state variable for snow+soil layers - case(iname_liqLayer); mLayerVolFracLiqTrial(iLayer) = stateVec( ixSnowSoilHyd(iLayer) ) ! liquid water state variable for snow+soil layers - case(iname_matLayer); mLayerMatricHeadTrial(iLayer-nSnow) = stateVec( ixSnowSoilHyd(iLayer) ) ! total water matric potential variable for soil layers - case(iname_lmpLayer); mLayerMatricHeadLiqTrial(iLayer-nSnow) = stateVec( ixSnowSoilHyd(iLayer) ) ! liquid matric potential state variable for soil layers - case default ! do nothing - end select - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! extract temperature of the canopy air space - if(ixAqWat/=integerMissing) scalarAquiferStorageTrial = stateVec(ixAqWat) - - end associate - - end subroutine varExtract2 - ! ********************************************************************************************************** ! public subroutine varExtractSundials: extract prime variables from the state vector and compute diagnostic variables ! ********************************************************************************************************** diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 392dba234..98a87ac87 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -665,6 +665,20 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! * update states... ! ------------------ + ! initialize to state variable from the last update + scalarCanairTempTrial = scalarCanairTemp + scalarCanopyTempTrial = scalarCanopyTemp + scalarCanopyWatTrial = scalarCanopyWat + scalarCanopyLiqTrial = scalarCanopyLiq + scalarCanopyIceTrial = scalarCanopyIce + mLayerTempTrial = mLayerTemp + mLayerVolFracWatTrial = mLayerVolFracWat + mLayerVolFracLiqTrial = mLayerVolFracLiq + mLayerVolFracIceTrial = mLayerVolFracIce + mLayerMatricHeadTrial = mLayerMatricHead ! total water matric potential + mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq ! liquid water matric potential + scalarAquiferStorageTrial = scalarAquiferStorage + ! extract states from the state vector call varExtract(& ! input @@ -677,12 +691,10 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(out): trial value of canopy ice content (kg m-2) ! output: variables for the snow-soil domain mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) ! output: variables for the aquifer diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index 6ad53569a..b0fc27037 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -692,6 +692,21 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux ! ----- ! * update states... ! ------------------ + + ! initialize to state variable from the last update + scalarCanairTempTrial = scalarCanairTemp + scalarCanopyTempTrial = scalarCanopyTemp + scalarCanopyWatTrial = scalarCanopyWat + scalarCanopyLiqTrial = scalarCanopyLiq + scalarCanopyIceTrial = scalarCanopyIce + mLayerTempTrial = mLayerTemp + mLayerVolFracWatTrial = mLayerVolFracWat + mLayerVolFracLiqTrial = mLayerVolFracLiq + mLayerVolFracIceTrial = mLayerVolFracIce + mLayerMatricHeadTrial = mLayerMatricHead ! total water matric potential + mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq ! liquid water matric potential + scalarAquiferStorageTrial = scalarAquiferStorage + ! extract states from the state vector call varExtract(& ! input @@ -704,12 +719,10 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(out): trial value of canopy ice content (kg m-2) ! output: variables for the snow-soil domain mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) ! output: variables for the aquifer From 7c429016a4328c5e02170564abfa8a7f58d7191e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 7 Sep 2022 13:00:16 +0900 Subject: [PATCH 0329/1472] Remove repeat code (and module) of updateVars4JacDAE. Everything now done in with flags based on inside or outside Sundials solver (IDA). --- build/my_makefile_cop | 1 - build/my_makefile_gra | 1 - build/my_makefile_mac | 1 - build/my_makefile_ric | 3 +- build/source/engine/eval8DAE.f90 | 5 +- build/source/engine/eval8JacDAE.f90 | 10 +- build/source/engine/updatState.f90 | 3 +- build/source/engine/updatStateSundials.f90 | 173 +---- build/source/engine/updateVars.f90 | 4 +- build/source/engine/updateVars4JacDAE.f90 | 820 --------------------- build/source/engine/updateVarsSundials.f90 | 384 +++++++--- build/source/engine/varSubstepSundials.f90 | 19 +- 12 files changed, 326 insertions(+), 1098 deletions(-) delete mode 100644 build/source/engine/updateVars4JacDAE.f90 diff --git a/build/my_makefile_cop b/build/my_makefile_cop index 27ae8119f..941ed87b7 100644 --- a/build/my_makefile_cop +++ b/build/my_makefile_cop @@ -276,7 +276,6 @@ SUMMA_MODRUN = \ t2enthalpy.f90 \ updateVars.f90 \ updateVarsSundials.f90 \ - updateVars4JacDAE.f90 \ var_derive.f90 \ read_force.f90 \ derivforce.f90 \ diff --git a/build/my_makefile_gra b/build/my_makefile_gra index ca010b077..1f7cbb073 100644 --- a/build/my_makefile_gra +++ b/build/my_makefile_gra @@ -276,7 +276,6 @@ SUMMA_MODRUN = \ t2enthalpy.f90 \ updateVars.f90 \ updateVarsSundials.f90 \ - updateVars4JacDAE.f90 \ var_derive.f90 \ read_force.f90 \ derivforce.f90 \ diff --git a/build/my_makefile_mac b/build/my_makefile_mac index caaa10435..cc89a17f6 100644 --- a/build/my_makefile_mac +++ b/build/my_makefile_mac @@ -276,7 +276,6 @@ SUMMA_MODRUN = \ t2enthalpy.f90 \ updateVars.f90 \ updateVarsSundials.f90 \ - updateVars4JacDAE.f90 \ var_derive.f90 \ read_force.f90 \ derivforce.f90 \ diff --git a/build/my_makefile_ric b/build/my_makefile_ric index 8a2571953..8fafae33e 100644 --- a/build/my_makefile_ric +++ b/build/my_makefile_ric @@ -67,7 +67,7 @@ LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas -lnetcdf DIR_SUNDIALS=/u1/gwu479/SummaSundials/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran -LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod +LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod # Eventually we plan move to a real configure script, but for now we like # to keep track of successful compilations of SUMMA on different platforms @@ -276,7 +276,6 @@ SUMMA_MODRUN = \ t2enthalpy.f90 \ updateVars.f90 \ updateVarsSundials.f90 \ - updateVars4JacDAE.f90 \ var_derive.f90 \ read_force.f90 \ derivforce.f90 \ diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8DAE.f90 index f17b635ed..89a6dd7fa 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8DAE.f90 @@ -464,12 +464,13 @@ subroutine eval8DAE(& call updateVarsSundials(& ! input dt_cur, & + .false., & ! intent(in): logical flag if inside Sundials solver .false., & ! intent(in): logical flag to adjust temperature to account for the energy mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU - mLayerVolFracWatPrev, & ! intent(in) - mLayerMatricHeadPrev, & ! intent(in) + mLayerVolFracWatPrev, & ! intent(in): previous vector of total water matric potential (m) + mLayerMatricHeadPrev, & ! intent(in): previous vector of volumetric total water content (-) diag_data, & ! intent(inout): model diagnostic variables for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy diff --git a/build/source/engine/eval8JacDAE.f90 b/build/source/engine/eval8JacDAE.f90 index 4640bf870..068ede391 100644 --- a/build/source/engine/eval8JacDAE.f90 +++ b/build/source/engine/eval8JacDAE.f90 @@ -119,7 +119,7 @@ subroutine eval8JacDAE(& ! provide access to subroutines USE getVectorz_module, only:varExtract ! extract variables from the state vector USE varExtrSundials_module, only:varExtractSundials - USE updateVars4JacDAE_module, only:updateVars4JacDAE ! update prognostic variables + USE updateVarsSundials_module, only:updateVarsSundials ! update prognostic variables USE computJacDAE_module,only:computJacDAE implicit none ! -------------------------------------------------------------------------------------------------------------------------------- @@ -255,16 +255,16 @@ subroutine eval8JacDAE(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - - - call updateVars4JacDAE(& + call updateVarsSundials(& ! input dt, & ! intent(in): time step + .true., & ! intent(in): logical flag if inside Sundials solver .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU + mLayerVolFracWatTrial, & ! intent(in): use current vector for prev vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): use current vector for prev vector of total water matric potential (m) diag_data, & ! intent(inout): model diagnostic variables for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy diff --git a/build/source/engine/updatState.f90 b/build/source/engine/updatState.f90 index 88bd826df..f1a210b89 100644 --- a/build/source/engine/updatState.f90 +++ b/build/source/engine/updatState.f90 @@ -36,7 +36,7 @@ module updatState_module ! ************************************************************************************************************* - ! public subroutine updateSnow: compute phase change impacts on volumetric liquid water and ice + ! public subroutine updateSnow: compute phase change impacts on volumetric liquid water and ice (veg or soil) ! ************************************************************************************************************* subroutine updateSnow(& ! input @@ -164,5 +164,4 @@ subroutine updateSoil(& end subroutine updateSoil - end module updatState_module diff --git a/build/source/engine/updatStateSundials.f90 b/build/source/engine/updatStateSundials.f90 index 51a3ead2a..71e8c74ef 100644 --- a/build/source/engine/updatStateSundials.f90 +++ b/build/source/engine/updatStateSundials.f90 @@ -13,67 +13,13 @@ module updatStateSundials_module private public::updateSnowSundials public::updateSoilSundials -public::updateSoilSundials2 -public::updateVegSundials real(rkind),parameter :: verySmall=1e-14_rkind ! a very small number (used to avoid divide by zero) contains - ! ************************************************************************************************************* - ! public subroutine updateVegSundials: compute phase change impacts on volumetric liquid water and ice - ! Input: Theta * canopyDepth * iden_water - ! Outputs: VolFracLiq * canopyDepth * iden_water and VolFracIce * canopyDepth * iden_ice ! ************************************************************************************************************* - subroutine updateVegSundials(& - ! input - Temp ,& ! intent(in): temperature (K) - Theta ,& ! intent(in): volume fraction of total water (-) - snowfrz_scale ,& ! intent(in): scaling parameter for the snow freezing curve (K-1) - TempPrime ,& ! intent(in): temperature (K) - ThetaPrime ,& ! intent(in): volume fraction of total water (-) - ! output - VolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) - VolFracIce ,& ! intent(out): volumetric fraction of ice (-) - VolFracLiqPrime ,& ! intent(out): volumetric fraction of liquid water (-) - VolFracIcePrime ,& ! intent(out): volumetric fraction of ice (-) - fLiq ,& ! intent(out): fraction of liquid water (-) - err,message) ! intent(out): error control - ! utility routines - USE snow_utils_module,only:fracliquid ! compute volumetric fraction of liquid water - USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) - implicit none - ! input variables - real(rkind),intent(in) :: Temp ! temperature (K) - real(rkind),intent(in) :: Theta ! volume fraction of total water (-) - real(rkind),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) - real(rkind),intent(in) :: TempPrime ! temperature (K) - real(rkind),intent(in) :: ThetaPrime ! volume fraction of total water (-) - ! output variables - real(rkind),intent(out) :: VolFracLiq ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: VolFracIce ! volumetric fraction of ice (-) - real(rkind),intent(out) :: VolFracLiqPrime ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: VolFracIcePrime ! volumetric fraction of ice (-) - real(rkind),intent(out) :: fLiq ! fraction of liquid water (-) - ! error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! initialize error control - err=0; message="updateVegSundials/" - - ! compute the volumetric fraction of liquid water and ice (-) - fLiq = fracliquid(Temp,snowfrz_scale) - VolFracLiq = fLiq*Theta - VolFracIce = (1._rkind - fLiq)*Theta - VolFracLiqPrime = fLiq * ThetaPrime + dFracLiq_dTk(Temp,snowfrz_scale) * Theta * TempPrime - VolFracIcePrime = ( ThetaPrime - VolFracLiqPrime ) - - - end subroutine updateVegSundials - - - ! ************************************************************************************************************* ! public subroutine updateSnowSundials: compute phase change impacts on volumetric liquid water and ice ! ************************************************************************************************************* subroutine updateSnowSundials(& @@ -122,13 +68,13 @@ subroutine updateSnowSundials(& end subroutine updateSnowSundials - ! ************************************************************************************************************* - ! public subroutine updateSoilSundials: compute phase change impacts on matric head and volumetric liquid water and ice - ! uses mLayerMatricHeadPrev and mLayerVolFracWatPrev to get dt_cur, or use dt_cur as it can change here - ! ************************************************************************************************************* + ! *********************************************************************************************************************************** + ! public subroutine updateSoilSundials: compute phase change impacts on matric head and volumetric liquid water and ice (veg or soil) + ! *********************************************************************************************************************************** subroutine updateSoilSundials(& ! input - dt_cur, & + dt ,& ! intent(in): time step + insideIDA ,& ! intent(in): flag if inside Sundials solver mLayerTemp ,& ! intent(in): temperature (K) mLayerMatricHead ,& ! intent(in): total water matric potential (m) mLayerMatricHeadPrev ,& ! intent(in): total water matric potential previous time step (m) @@ -154,7 +100,8 @@ subroutine updateSoilSundials(& USE soil_utils_module,only:dTheta_dPsi implicit none ! input variables - real(rkind),intent(in) :: dt_cur + real(rkind),intent(in) :: dt + logical(lgt),intent(in) :: insideIDA ! flag if inside Sundials solver real(rkind),intent(in) :: mLayerTemp ! estimate of temperature (K) real(rkind),intent(in) :: mLayerMatricHead ! matric head (m) real(rkind),intent(in) :: mLayerMatricHeadPrev ! matric head previous time step (m) @@ -185,13 +132,16 @@ subroutine updateSoilSundials(& ! compute fractional **volume** of total water (liquid plus ice) mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - ! mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime - if( abs(mLayerMatricHead - mLayerMatricHeadPrev) < verySmall )then - dt_inv = 1._rkind/ dt_cur !WHY NOT JUST USE THIS - else - dt_inv = mLayerMatricHeadPrime / (mLayerMatricHead - mLayerMatricHeadPrev) + if (.not.insideIDA)then ! calculate dt current, or use dt current as it can change here + if( abs(mLayerMatricHead - mLayerMatricHeadPrev) < verySmall )then !this difference is set as 0 inside varSubstep + dt_inv = 1._rkind/dt + else + dt_inv = mLayerMatricHeadPrime / (mLayerMatricHead - mLayerMatricHeadPrev) ! + endif + mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev)*dt_inv + else ! inside Sundials: instantaneous derivative will always be a full step size as input here + mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime endif - mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev)*dt_inv if(mLayerVolFracWat > theta_sat)then; err=20; message=trim(message)//'volume of liquid and ice exceeds porosity'; return; end if @@ -226,95 +176,4 @@ subroutine updateSoilSundials(& end subroutine updateSoilSundials - ! ************************************************************************************************************* - ! public subroutine updateSoilSundials: compute phase change impacts on matric head and volumetric liquid water and ice - ! uses mLayerMatricHeadPrime, and wants instantaneous dt, dt_cur will always be a full step size as input here - ! ************************************************************************************************************* - subroutine updateSoilSundials2(& - ! input - dt_cur ,& ! intent(in): time step - mLayerTemp ,& ! intent(in): temperature vector (K) - mLayerMatricHead ,& ! intent(in): total water matric potential (m) - mLayerTempPrime ,& ! intent(in): temperature time derivative (K/s) - mLayerMatricHeadPrime, & ! intent(in): total water matric potential time derivative (m/s) - vGn_alpha ,& ! intent(in): van Genutchen "alpha" parameter - vGn_n ,& ! intent(in): van Genutchen "n" parameter - theta_sat ,& ! intent(in): soil porosity (-) - theta_res ,& ! intent(in): soil residual volumetric water content (-) - vGn_m ,& ! intent(in): van Genutchen "m" parameter (-) - ! output - mLayerVolFracWat ,& ! intent(out): volumetric fraction of total water (-) - mLayerVolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) - mLayerVolFracIce ,& ! intent(out): volumetric fraction of ice (-) - mLayerVolFracWatPrime ,& ! intent(out): volumetric fraction of total water (-) - mLayerVolFracLiqPrime ,& ! intent(out): volumetric fraction of liquid water (-) - mLayerVolFracIcePrime ,& ! intent(out): volumetric fraction of ice (-) - err,message) ! intent(out): error control - ! utility routines - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water based on matric head - USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric liquid water content - USE soil_utils_module,only:dTheta_dPsi - implicit none - ! input variables - real(rkind),intent(in) :: dt_cur - real(rkind),intent(in) :: mLayerTemp ! estimate of temperature (K) - real(rkind),intent(in) :: mLayerMatricHead ! matric head (m) - real(rkind),intent(in) :: mLayerTempPrime ! temperature time derivative (K/s) - real(rkind),intent(in) :: mLayerMatricHeadPrime ! matric head time derivative (m/s) - real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter - real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter - real(rkind),intent(in) :: theta_sat ! soil porosity (-) - real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) - real(rkind),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) - ! output variables - real(rkind),intent(out) :: mLayerVolFracWat ! fractional volume of total water (-) - real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) - real(rkind),intent(out) :: mLayerVolFracWatPrime ! fractional volume of total water time derivative (-) - real(rkind),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water time derivative (-) - real(rkind),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice time derivative (-) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! define local variables - real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) - real(rkind) :: xConst ! constant in the freezing curve function (m K-1) - real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) - ! initialize error control - err=0; message="updateSoilSundials2/" - - ! compute fractional **volume** of total water (liquid plus ice) - mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime - - if(mLayerVolFracWat > theta_sat)then; err=20; message=trim(message)//'volume of liquid and ice exceeds porosity'; return; end if - - ! compute the critical soil temperature where all water is unfrozen (K) - ! (eq 17 in Dall'Amico 2011) - TcSoil = Tfreeze + min(mLayerMatricHead,0._rkind)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) - - ! *** compute volumetric fraction of liquid water for partially frozen soil - if( mLayerTemp < TcSoil )then ! (check if soil temperature is less than the critical temperature) - ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice - ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations - xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) - mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution - mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if(mLayerPsiLiq<0._rkind)then - mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime - else - mLayerVolFracLiqPrime = 0._rkind - endif - - ! *** compute volumetric fraction of liquid water for unfrozen soil - else !( mLayerTemp >= TcSoil, all water is unfrozen ) - mLayerVolFracLiq = mLayerVolFracWat - mLayerVolFracLiqPrime = mLayerVolFracWatPrime - - end if ! (check if soil is partially frozen) - - ! - volumetric ice content (-) - mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq - mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime - - end subroutine updateSoilSundials2 end module updatStateSundials_module diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 index c01cac663..d9a74d5f5 100644 --- a/build/source/engine/updateVars.f90 +++ b/build/source/engine/updateVars.f90 @@ -500,8 +500,8 @@ subroutine updateVars(& ! compute mass of water on the canopy ! NOTE: possibilities for speed-up here - scalarCanopyLiqTrial = scalarFracLiqVeg *scalarCanopyWatTrial - scalarCanopyIceTrial = (1._rkind - scalarFracLiqVeg)*scalarCanopyWatTrial + scalarCanopyLiqTrial = scalarFracLiqVeg *scalarCanopyWatTrial !(kg m-2), scalarVolFracLiq*iden_water*canopyDepth + scalarCanopyIceTrial = (1._rkind - scalarFracLiqVeg)*scalarCanopyWatTrial !(kg m-2), scalarVolFracIce* iden_ice *canopyDepth ! *** snow layers case(iname_snow) diff --git a/build/source/engine/updateVars4JacDAE.f90 b/build/source/engine/updateVars4JacDAE.f90 deleted file mode 100644 index 734483cb4..000000000 --- a/build/source/engine/updateVars4JacDAE.f90 +++ /dev/null @@ -1,820 +0,0 @@ -! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2015 NCAR/RAL -! -! This file is part of SUMMA -! -! For more information see: http://www.ral.ucar.edu/projects/summa -! -! This program is free software: you can redistribute it and/or modify -! it under the terms of the GNU General Public License as published by -! the Free Software Foundation, either version 3 of the License, or -! (at your option) any later version. -! -! This program is distributed in the hope that it will be useful, -! but WITHOUT ANY WARRANTY; without even the implied warranty of -! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -! GNU General Public License for more details. -! -! You should have received a copy of the GNU General Public License -! along with this program. If not, see . - -module updateVars4JacDAE_module - -! data types -USE nrtype - -! missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing real number - -! access the global print flag -USE globalData,only:globalPrintFlag - -! domain types -USE globalData,only:iname_cas ! named variables for canopy air space -USE globalData,only:iname_veg ! named variables for vegetation canopy -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil -USE globalData,only:iname_aquifer ! named variables for the aquifer - -! named variables to describe the state variable type -USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space -USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy -USE globalData,only:iname_watCanopy ! named variable defining the mass of total water on the vegetation canopy -USE globalData,only:iname_liqCanopy ! named variable defining the mass of liquid water on the vegetation canopy -USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers -USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers -USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers -USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers -USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers - -! metadata for information in the data structures -USE globalData,only:indx_meta ! metadata for the variables in the index structure - -! constants -USE multiconst,only:& - gravity, & ! acceleration of gravity (m s-2) - Tfreeze, & ! temperature at freezing (K) - Cp_air, & ! specific heat of air (J kg-1 K-1) - Cp_ice, & ! specific heat of ice (J kg-1 K-1) - Cp_water, & ! specific heat of liquid water (J kg-1 K-1) - LH_fus, & ! latent heat of fusion (J kg-1) - iden_air, & ! intrinsic density of air (kg m-3) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water ! intrinsic density of liquid water (kg m-3) - -! provide access to the derived types to define the data structures -USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (rkind) - -! provide access to indices that define elements of the data structures -USE var_lookup,only:iLookDIAG ! named variables for structure elements -USE var_lookup,only:iLookPROG ! named variables for structure elements -USE var_lookup,only:iLookDERIV ! named variables for structure elements -USE var_lookup,only:iLookPARAM ! named variables for structure elements -USE var_lookup,only:iLookINDEX ! named variables for structure elements - -! provide access to routines to update states -USE updatStateSundials_module,only:updateVegSundials ! update snow states -USE updatStateSundials_module,only:updateSnowSundials ! update snow states -USE updatStateSundials_module,only:updateSoilSundials2 ! update soil states - -! provide access to functions for the constitutive functions and derivatives -USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) -USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) -USE soil_utils_module,only:dTheta_dTk ! differentiate the freezing curve w.r.t. temperature (soil) -USE soil_utils_module,only:dTheta_dPsi ! derivative in the soil water characteristic (soil) -USE soil_utils_module,only:dPsi_dTheta ! derivative in the soil water characteristic (soil) -USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content -USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water -USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists -USE soil_utilsSundials_module,only:liquidHeadSundials ! compute the liquid water matric potential -USE soil_utilsSundials_module,only:d2Theta_dPsi2 -USE soil_utilsSundials_module,only:d2Theta_dTk2 - -! IEEE checks -USE, intrinsic :: ieee_arithmetic ! check values (NaN, etc.) - -implicit none -private -public::updateVars4JacDAE - -contains - - ! ********************************************************************************************************** - ! public subroutine updateVars4JacDAE: compute diagnostic variables - ! ********************************************************************************************************** - subroutine updateVars4JacDAE(& - ! input - dt, & ! intent(in): time step - do_adjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! reza - mLayerVolFracWatPrime, & ! reza - mLayerVolFracLiqPrime, & ! reza - mLayerVolFracIcePrime, & ! reza - mLayerMatricHeadPrime, & ! reza - mLayerMatricHeadLiqPrime, & ! reza - ! output: error control - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input - real(rkind) ,intent(in) :: dt ! time step - logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze - type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! output: variables for the vegetation canopy - real(rkind),intent(inout) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - real(rkind),intent(inout) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) - real(rkind),intent(inout) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - real(rkind),intent(inout) :: scalarCanopyTempPrime ! trial value of canopy temperature (K) - real(rkind),intent(inout) :: scalarCanopyWatPrime ! trial value of canopy total water (kg m-2) - real(rkind),intent(inout) :: scalarCanopyLiqPrime ! trial value of canopy liquid water (kg m-2) - real(rkind),intent(inout) :: scalarCanopyIcePrime ! trial value of canopy ice content (kg m-2) - ! output: variables for the snow-soil domain - real(rkind),intent(inout) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(inout) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(rkind),intent(inout) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(inout) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric ice water content (-) - real(rkind),intent(inout) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) - - real(rkind),intent(inout) :: mLayerTempPrime(:) - real(rkind),intent(inout) :: mLayerVolFracWatPrime(:) ! reza - real(rkind),intent(inout) :: mLayerVolFracLiqPrime(:) ! reza - real(rkind),intent(inout) :: mLayerVolFracIcePrime(:) ! reza - real(rkind),intent(inout) :: mLayerMatricHeadPrime(:) ! reza - real(rkind),intent(inout) :: mLayerMatricHeadLiqPrime(:) ! reza - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! general local variables - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixDomainType ! name of a given model domain - integer(i4b) :: ixControlIndex ! index within a given model domain - integer(i4b) :: ixOther,ixOtherLocal ! index of the coupled state variable within the (full, local) vector - logical(lgt) :: isCoupled ! .true. if a given variable shared another state variable in the same control volume - logical(lgt) :: isNrgState ! .true. if a given variable is an energy state - logical(lgt),allocatable :: computedCoupling(:) ! .true. if computed the coupling for a given state variable - real(rkind) :: scalarVolFracLiq ! volumetric fraction of liquid water (-) - real(rkind) :: scalarVolFracIce ! volumetric fraction of ice (-) - real(rkind) :: scalarVolFracLiqPrime ! volumetric fraction of liquid water (-) - real(rkind) :: scalarVolFracIcePrime ! volumetric fraction of ice (-) - real(rkind) :: Tcrit ! critical soil temperature below which ice exists (K) - real(rkind) :: xTemp ! temporary temperature (K) - real(rkind) :: fLiq ! fraction of liquid water (-) - real(rkind) :: effSat ! effective saturation (-) - real(rkind) :: avPore ! available pore space (-) - character(len=256) :: cMessage ! error message of downwind routine - logical(lgt),parameter :: printFlag=.false. ! flag to turn on printing - ! iterative solution for temperature - real(rkind) :: meltNrg ! energy for melt+freeze (J m-3) - real(rkind) :: residual ! residual in the energy equation (J m-3) - real(rkind) :: derivative ! derivative in the energy equation (J m-3 K-1) - real(rkind) :: tempInc ! iteration increment (K) - integer(i4b) :: iter ! iteration index - integer(i4b) :: niter ! number of iterations - integer(i4b),parameter :: maxiter=100 ! maximum number of iterations - real(rkind),parameter :: nrgConvTol=1.e-4_rkind ! convergence tolerance for energy (J m-3) - real(rkind),parameter :: tempConvTol=1.e-6_rkind ! convergence tolerance for temperature (K) - real(rkind) :: critDiff ! temperature difference from critical (K) - real(rkind) :: tempMin ! minimum bracket for temperature (K) - real(rkind) :: tempMax ! maximum bracket for temperature (K) - logical(lgt) :: bFlag ! flag to denote that iteration increment was constrained using bi-section - real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical temperature (K) - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - associate(& - ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! indices defining model states and layers - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ! indices in the full vector for specific domains - ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain - ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain - ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain - ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain - ! mapping between the full state vector and the state subset - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset - ! type of domain, type of state variable, and index of control volume within domain - ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ! depth-varying model parameters - vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) - ! model diagnostic variables (heat capacity) - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(in): [dp ] volumetric heat capacity of the vegetation (J m-3 K-1) - mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] volumetric heat capacity in each layer (J m-3 K-1) - ! model diagnostic variables (fraction of liquid water) - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) - ! model states for the vegetation canopy - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variable vectors for the snow-soil layers - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] total water matric potential (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) - ! model diagnostic variables from a previous solution - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - ! derivatives - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential - dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) - dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(out): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature - d2VolTot_d2Psi0 => deriv_data%var(iLookDERIV%d2VolTot_d2Psi0 )%dat ,& ! intent(out): [dp(:)] second derivative in total water content w.r.t. total water matric potential - mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2 )%dat ,& ! intent(out): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature - d2Theta_dTkCanopy2 => deriv_data%var(iLookDERIV%d2Theta_dTkCanopy2 )%dat(1) ,& ! intent(out): [dp ] second derivative of volumetric liquid water content w.r.t. temperature - dFracLiqSnow_dTk => deriv_data%var(iLookDERIV%dFracLiqSnow_dTk )%dat ,& ! intent(out): [dp(:)] derivative in fraction of liquid snow w.r.t. temperature - dFracLiqVeg_dTkCanopy => deriv_data%var(iLookDERIV%dFracLiqVeg_dTkCanopy )%dat(1) ,& ! intent(out): [dp ] derivative in fraction of (throughfall + drainage) w.r.t. temperature - dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1) & ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. temperature -) ! association with variables in the data structures - - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - - ! initialize error control - err=0; message='updateVars4JacDAE/' - - ! allocate space and assign values to the flag vector - allocate(computedCoupling(size(ixMapSubset2Full)),stat=err) ! .true. if computed the coupling for a given state variable - if(err/=0)then; message=trim(message)//'problem allocating computedCoupling'; return; endif - computedCoupling(:)=.false. - - ! loop through model state variables - do iState=1,size(ixMapSubset2Full) - - ! check the need for the computations - if(computedCoupling(iState)) cycle - - ! ----- - ! - compute indices... - ! -------------------- - - ! get domain type, and index of the control volume within the domain - ixFullVector = ixMapSubset2Full(iState) ! index within full state vector - ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) - ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain - - ! get the layer index - select case(ixDomainType) - case(iname_cas); cycle ! canopy air space: do nothing - case(iname_veg); iLayer = 0 - case(iname_snow); iLayer = ixControlIndex - case(iname_soil); iLayer = ixControlIndex + nSnow - case(iname_aquifer); cycle ! aquifer: do nothing - case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return - end select - - ! get the index of the other (energy or mass) state variable within the full state vector - select case(ixDomainType) - case(iname_veg) ; ixOther = merge(ixHydCanopy(1), ixNrgCanopy(1), ixStateType(ixFullVector)==iname_nrgCanopy) - case(iname_snow, iname_soil); ixOther = merge(ixHydLayer(iLayer),ixNrgLayer(iLayer),ixStateType(ixFullVector)==iname_nrgLayer) - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - end select - - ! get the index in the local state vector - ixOtherLocal = ixMapFull2Subset(ixOther) ! ixOtherLocal could equal integerMissing - if(ixOtherLocal/=integerMissing) computedCoupling(ixOtherLocal)=.true. - - ! check if we have a coupled solution - isCoupled = (ixOtherLocal/=integerMissing) - - ! check if we are an energy state - isNrgState = (ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer) - - if(printFlag)then - print*, 'iState = ', iState, size(ixMapSubset2Full) - print*, 'ixFullVector = ', ixFullVector - print*, 'ixDomainType = ', ixDomainType - print*, 'ixControlIndex = ', ixControlIndex - print*, 'ixOther = ', ixOther - print*, 'ixOtherLocal = ', ixOtherLocal - print*, 'do_adjustTemp = ', do_adjustTemp - print*, 'isCoupled = ', isCoupled - print*, 'isNrgState = ', isNrgState - endif - - - - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - - ! update hydrology state variables for the uncoupled solution - if(.not.isNrgState .and. .not.isCoupled)then - - ! update the total water from volumetric liquid water - if(ixStateType(ixFullVector)==iname_liqCanopy .or. ixStateType(ixFullVector)==iname_liqLayer)then - select case(ixDomainType) - case(iname_veg) - scalarCanopyWatTrial = scalarCanopyLiqTrial + scalarCanopyIceTrial - scalarCanopyWatPrime = scalarCanopyLiqPrime + scalarCanopyIcePrime - case(iname_snow) - mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer)*iden_ice/iden_water - mLayerVolFracWatPrime(iLayer) = mLayerVolFracLiqPrime(iLayer) + mLayerVolFracIcePrime(iLayer)*iden_ice/iden_water - case(iname_soil) - mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer) ! no volume expansion - mLayerVolFracWatPrime(iLayer) = mLayerVolFracLiqPrime(iLayer) + mLayerVolFracIcePrime(iLayer) - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, or iname_soil'; return - end select - endif - - ! update the total water and the total water matric potential - if(ixDomainType==iname_soil)then - select case( ixStateType(ixFullVector) ) - ! --> update the total water from the liquid water matric potential - case(iname_lmpLayer) - - effSat = volFracLiq(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex)) ! effective saturation - avPore = theta_sat(ixControlIndex) - mLayerVolFracIceTrial(iLayer) - theta_res(ixControlIndex) ! available pore space - mLayerVolFracLiqTrial(iLayer) = effSat*avPore + theta_res(ixControlIndex) - mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer) ! no volume expansion - mLayerVolFracWatPrime(iLayer) = mLayerVolFracLiqPrime(iLayer) + mLayerVolFracIcePrime(iLayer) - mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - mLayerMatricHeadPrime(ixControlIndex) = dPsi_dTheta(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) * mLayerVolFracWatPrime(iLayer) - !write(*,'(a,1x,i4,1x,3(f20.10,1x))') 'mLayerVolFracLiqTrial(iLayer) 1 = ', iLayer, mLayerVolFracLiqTrial(iLayer), mLayerVolFracIceTrial(iLayer), mLayerVolFracWatTrial(iLayer) - ! --> update the total water from the total water matric potential - case(iname_matLayer) - - mLayerVolFracWatTrial(iLayer) = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - mLayerVolFracWatPrime(iLayer) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) *mLayerMatricHeadPrime(ixControlIndex) - ! --> update the total water matric potential (assume already have mLayerVolFracWatTrial given block above) - case(iname_liqLayer, iname_watLayer) - - mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - mLayerMatricHeadPrime(ixControlIndex) = dPsi_dTheta(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) * mLayerVolFracWatPrime(iLayer) - case default; err=20; message=trim(message)//'expect iname_lmpLayer, iname_matLayer, iname_liqLayer, or iname_watLayer'; return - end select - endif ! if in the soil domain - - endif ! if hydrology state variable or uncoupled solution - - ! compute the critical soil temperature below which ice exists - select case(ixDomainType) - case(iname_veg, iname_snow); Tcrit = Tfreeze; - case(iname_soil); Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ); - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - end select - - ! initialize temperature - select case(ixDomainType) - case(iname_veg); xTemp = scalarCanopyTempTrial - case(iname_snow, iname_soil); xTemp = mLayerTempTrial(iLayer) - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - end select - - ! define brackets for the root - ! NOTE: start with an enormous range; updated quickly in the iterations - tempMin = xTemp - 10._rkind - tempMax = xTemp + 10._rkind - - ! get iterations (set to maximum iterations if adjusting the temperature) - niter = merge(maxiter, 1, do_adjustTemp) - - ! iterate - iterations: do iter=1,niter - - ! restrict temperature - if(xTemp <= tempMin .or. xTemp >= tempMax)then - xTemp = 0.5_rkind*(tempMin + tempMax) ! new value - bFlag = .true. - else - bFlag = .false. - endif - - ! ----- - ! - compute derivatives... - ! ------------------------ - - ! compute the derivative in bulk heat capacity w.r.t. total water content or water matric potential (m-1) - ! compute the derivative in total water content w.r.t. total water matric potential (m-1) - ! NOTE 1: valid for frozen and unfrozen conditions - ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi, dVolHtCapBulk_dPsi0 may be wrong - select case(ixDomainType) - case(iname_veg) - fLiq = fracLiquid(xTemp,snowfrz_scale) - dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) - case(iname_snow) - fLiq = fracLiquid(xTemp,snowfrz_scale) - dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air - case(iname_soil) - select case( ixStateType(ixFullVector) ) - case(iname_lmpLayer) - dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore - d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore - case default - dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),& - vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - end select - ! dVolHtCapBulk_dPsi0 uses the derivative in water retention curve above critical temp w.r.t.state variable dVolTot_dPsi0 - dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use - if(xTemp< Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) - if(xTemp>=Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_water * Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) - end select - - ! compute the derivative in liquid water content w.r.t. temperature - ! --> partially frozen: dependence of liquid water on temperature - ! compute the derivative in bulk heat capacity w.r.t. temperature - if(xTemp unfrozen: no dependence of liquid water on temperature - else - select case(ixDomainType) - case(iname_veg); dTheta_dTkCanopy = 0._rkind; d2Theta_dTkCanopy2 = 0._rkind; dFracLiqVeg_dTkCanopy = 0._rkind; dVolHtCapBulk_dTkCanopy = 0._rkind - case(iname_snow); mLayerdTheta_dTk(iLayer) = 0._rkind; mLayerd2Theta_dTk2(iLayer) = 0._rkind; dFracLiqSnow_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind - case(iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind; mLayerd2Theta_dTk2(iLayer) = 0._rkind; dFracLiqSnow_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind - end select ! domain type - endif - - ! ----- - ! - update volumetric fraction of liquid water and ice... - ! => case of hydrology state uncoupled with energy (and when not adjusting the temperature)... - ! ----------------------------------------------------------------------------------------------- - - ! case of hydrology state uncoupled with energy (and when not adjusting the temperature) - if(.not.do_adjustTemp .and. .not.isNrgState .and. .not.isCoupled)then - - ! compute the fraction of snow - select case(ixDomainType) - case(iname_veg); scalarFracLiqVeg = fracliquid(xTemp,snowfrz_scale) - case(iname_snow); mLayerFracLiqSnow(iLayer) = fracliquid(xTemp,snowfrz_scale) - case(iname_soil) ! do nothing - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - end select ! domain type - - ! ----- - ! - update volumetric fraction of liquid water and ice... - ! => case of energy state or coupled solution (or adjusting the temperature)... - ! -------------------------------------------------------------------------------- - - ! case of energy state OR coupled solution (or adjusting the temperature) - elseif(do_adjustTemp .or. ( (isNrgState .or. isCoupled) ) )then - - ! identify domain type - select case(ixDomainType) - - ! *** vegetation canopy - case(iname_veg) - ! compute volumetric fraction of liquid water and ice - call updateVegSundials(& - xTemp, & ! intent(in) : temperature (K) - scalarCanopyWatTrial, & ! intent(in) : canopy total water (kg m-2) - snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) - scalarCanopyTempPrime, & ! intent(in) : canopy temperature time derivative (K/s) - scalarCanopyWatPrime, & ! intent(in) : canopy total water time derivative (kg m-2 /s) - scalarVolFracLiq, & ! intent(out) : trial canopy liquid water (-) - scalarVolFracIce, & ! intent(out) : trial volumetric canopy ice (-) - scalarVolFracLiqPrime, & ! intent(out) : trial volumetric canopy liquid water (-) - scalarVolFracIcePrime, & ! intent(out) : trial volumetric canopy ice (-) - scalarFracLiqVeg, & ! intent(out) : fraction of liquid water (-) - err,cmessage) ! intent(out) : error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! *** snow layers - case(iname_snow) - - call updateSnowSundials(& - xTemp, & ! intent(in) : temperature (K) - mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) - snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) - mLayerTempPrime(iLayer), & ! intent(in) : temperature time derivative (K/s) - mLayerVolFracWatPrime(iLayer), & ! intent(in) : volumetric fraction of total water time derivative (-) - mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) - mLayerVolFracLiqPrime(iLayer), & ! intent(out) : volumetric fraction of liquid water time derivative (-) - mLayerVolFracIcePrime(iLayer), & ! intent(out) : volumetric fraction of ice time derivative (-) - mLayerFracLiqSnow(iLayer), & ! intent(out) : fraction of liquid water (-) - err,cmessage) ! intent(out) : error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! *** soil layers - case(iname_soil) - - ! compute volumetric fraction of liquid water and ice - call updateSoilSundials2(& - dt, & ! intent(in) : time step - xTemp, & ! intent(in) : temperature (K) - mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) - mLayerTempPrime(iLayer), & ! intent(in) : temperature time derivative (K/s) - mLayerMatricHeadPrime(ixControlIndex), & ! intent(in) : total water matric potential time derivative (m/s) - ! intent(in) : soil parameters - vGn_alpha(ixControlIndex), & ! intent(in) : van Genutchen "alpha" parameter - vGn_n(ixControlIndex), & ! intent(in) : van Genutchen "n" parameter - theta_sat(ixControlIndex), & ! intent(in) : soil porosity (-) - theta_res(ixControlIndex), & ! intent(in) : soil residual volumetric water content (-) - vGn_m(ixControlIndex), & ! intent(in) : van Genutchen "m" parameter (-) - mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) - mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) - mLayerVolFracWatPrime(iLayer), & ! intent(out) : volumetric fraction of total water time derivative (-) - mLayerVolFracLiqPrime(iLayer), & ! intent(out) : volumetric fraction of liquid water time derivative (-) - mLayerVolFracIcePrime(iLayer), & ! intent(out) : volumetric fraction of ice time derivative (-) - err,cmessage) ! intent(out) : error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! check - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - - end select ! domain type - - ! final check - else - - ! do nothing (input = output) -- and check that we got here correctly - if( (isNrgState .or. isCoupled) )then - scalarVolFracLiq = realMissing - scalarVolFracIce = realMissing - else - message=trim(message)//'unexpected else branch' - err=20; return - endif - - endif ! if energy state or solution is coupled - - ! ----- - ! - update temperatures... - ! ------------------------ - - ! check the need to adjust temperature - if(do_adjustTemp)then - - ! get the melt energy - meltNrg = merge(LH_fus*iden_ice, LH_fus*iden_water, ixDomainType==iname_snow) - - ! compute the residual and the derivative - select case(ixDomainType) - - ! * vegetation - case(iname_veg) - call xTempSolve(& - ! constant over iterations - meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) - heatCap = scalarBulkVolHeatCapVeg ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) - tempInit = scalarCanopyTemp ,& ! intent(in) : initial temperature (K) - volFracIceInit = scalarCanopyIce/(iden_water*canopyDepth),& ! intent(in) : initial volumetric fraction of ice (-) - ! trial values - xTemp = xTemp ,& ! intent(inout) : trial value of temperature - dLiq_dT = dTheta_dTkCanopy ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) - volFracIceTrial = scalarVolFracIce ,& ! intent(in) : trial value for volumetric fraction of ice - ! residual and derivative - residual = residual ,& ! intent(out) : residual (J m-3) - derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) - - ! * snow and soil - case(iname_snow, iname_soil) - call xTempSolve(& - ! constant over iterations - meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) - heatCap = mLayerVolHtCapBulk(iLayer) ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) - tempInit = mLayerTemp(iLayer) ,& ! intent(in) : initial temperature (K) - volFracIceInit = mLayerVolFracIce(iLayer) ,& ! intent(in) : initial volumetric fraction of ice (-) - ! trial values - xTemp = xTemp ,& ! intent(inout) : trial value of temperature - dLiq_dT = mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) - volFracIceTrial = mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : trial value for volumetric fraction of ice - ! residual and derivative - residual = residual ,& ! intent(out) : residual (J m-3) - derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) - - ! * check - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - - end select ! domain type - - ! check validity of residual - if( ieee_is_nan(residual) )then - message=trim(message)//'residual is not valid' - err=20; return - endif - - ! update bracket - if(residual < 0._rkind)then - tempMax = min(xTemp,tempMax) - else - tempMin = max(tempMin,xTemp) - end if - - ! compute iteration increment - tempInc = residual/derivative ! K - - ! check - if(globalPrintFlag)& - write(*,'(i4,1x,e20.10,1x,5(f20.10,1x),L1)') iter, residual, xTemp-Tcrit, tempInc, Tcrit, tempMin, tempMax, bFlag - - ! check convergence - if(abs(residual) < nrgConvTol .or. abs(tempInc) < tempConvTol) exit iterations - - ! add constraints for snow temperature - if(ixDomainType==iname_veg .or. ixDomainType==iname_snow)then - if(tempInc > Tcrit - xTemp) tempInc=(Tcrit - xTemp)*0.5_rkind ! simple bi-section method - endif ! if the domain is vegetation or snow - - ! deal with the discontinuity between partially frozen and unfrozen soil - if(ixDomainType==iname_soil)then - ! difference from the temperature below which ice exists - critDiff = Tcrit - xTemp - ! --> initially frozen (T < Tcrit) - if(critDiff > 0._rkind)then - if(tempInc > critDiff) tempInc = critDiff + epsT ! set iteration increment to slightly above critical temperature - ! --> initially unfrozen (T > Tcrit) - else - if(tempInc < critDiff) tempInc = critDiff - epsT ! set iteration increment to slightly below critical temperature - endif - endif ! if the domain is soil - - ! update the temperature trial - xTemp = xTemp + tempInc - - ! check failed convergence - if(iter==maxiter)then - message=trim(message)//'failed to converge' - err=-20; return ! negative error code = try to recover - endif - - endif ! if adjusting the temperature - - end do iterations ! iterating - - ! save temperature - select case(ixDomainType) - case(iname_veg); scalarCanopyTempTrial = xTemp - case(iname_snow, iname_soil); mLayerTempTrial(iLayer) = xTemp - end select - - ! ======================================================================================================================================= - ! ======================================================================================================================================= - - ! ----- - ! - compute the liquid water matric potential (and necessay derivatives)... - ! ------------------------------------------------------------------------- - - ! only for soil - if(ixDomainType==iname_soil)then - - ! check liquid water - if(mLayerVolFracLiqTrial(iLayer) > theta_sat(ixControlIndex) )then - message=trim(message)//'liquid water greater than porosity' - err=20; return - endif - - ! case of hydrology state uncoupled with energy - if(.not.isNrgState .and. .not.isCoupled)then - - ! derivatives relating liquid water matric potential to total water matric potential and temperature - dPsiLiq_dPsi0(ixControlIndex) = 1._rkind ! exact correspondence (psiLiq=psi0) - dPsiLiq_dTemp(ixControlIndex) = 0._rkind ! no relationship between liquid water matric potential and temperature - - ! case of energy state or coupled solution - else - ! compute the liquid matric potential (and the derivatives w.r.t. total matric potential and temperature) - call liquidHeadSundials(& - ! input - mLayerMatricHeadTrial(ixControlIndex) ,& ! intent(in) : total water matric potential (m) - mLayerMatricHeadPrime(ixControlIndex) ,& ! - mLayerVolFracLiqTrial(iLayer) ,& ! intent(in) : volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : volumetric fraction of ice (-) - vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in) : soil parameters - dVolTot_dPsi0(ixControlIndex) ,& ! intent(in) : derivative in the soil water characteristic (m-1) - mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) - mLayerTempPrime(ixControlIndex) ,& - mLayerVolFracLiqPrime(iLayer) ,& - mLayerVolFracIcePrime(iLayer) ,& - ! output - mLayerMatricHeadLiqTrial(ixControlIndex) ,& ! intent(out): liquid water matric potential (m) - mLayerMatricHeadLiqPrime(ixControlIndex) ,& ! - dPsiLiq_dPsi0(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. the total water matric potential (-) - dPsiLiq_dTemp(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. temperature (m K-1) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - endif ! switch between hydrology and energy state - - endif ! if domain is soil - - end do ! looping through state variables - - ! deallocate space - deallocate(computedCoupling,stat=err) ! .true. if computed the coupling for a given state variable - if(err/=0)then; message=trim(message)//'problem deallocating computedCoupling'; return; endif - - ! end association to the variables in the data structures - end associate - - end subroutine updateVars4JacDAE - - - ! ********************************************************************************************************** - ! private subroutine xTempSolve: compute residual and derivative for temperature - ! ********************************************************************************************************** - subroutine xTempSolve(& - ! input: constant over iterations - meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) - heatCap ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) - tempInit ,& ! intent(in) : initial temperature (K) - volFracIceInit ,& ! intent(in) : initial volumetric fraction of ice (-) - ! input-output: trial values - xTemp ,& ! intent(inout) : trial value of temperature - dLiq_dT ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) - volFracIceTrial ,& ! intent(in) : trial value for volumetric fraction of ice - ! output: residual and derivative - residual ,& ! intent(out) : residual (J m-3) - derivative ) ! intent(out) : derivative (J m-3 K-1) - implicit none - ! input: constant over iterations - real(rkind),intent(in) :: meltNrg ! energy for melt+freeze (J m-3) - real(rkind),intent(in) :: heatCap ! volumetric heat capacity (J m-3 K-1) - real(rkind),intent(in) :: tempInit ! initial temperature (K) - real(rkind),intent(in) :: volFracIceInit ! initial volumetric fraction of ice (-) - ! input-output: trial values - real(rkind),intent(inout) :: xTemp ! trial value for temperature - real(rkind),intent(in) :: dLiq_dT ! derivative in liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: volFracIceTrial ! trial value for the volumetric fraction of ice (-) - ! output: residual and derivative - real(rkind),intent(out) :: residual ! residual (J m-3) - real(rkind),intent(out) :: derivative ! derivative (J m-3 K-1) - ! subroutine starts here - residual = -heatCap*(xTemp - tempInit) + meltNrg*(volFracIceTrial - volFracIceInit) ! J m-3 - derivative = heatCap + LH_fus*iden_water*dLiq_dT ! J m-3 K-1 - end subroutine xTempSolve - -end module updateVars4JacDAE_module diff --git a/build/source/engine/updateVarsSundials.f90 b/build/source/engine/updateVarsSundials.f90 index 35a7d4cc7..ce17d861d 100644 --- a/build/source/engine/updateVarsSundials.f90 +++ b/build/source/engine/updateVarsSundials.f90 @@ -56,6 +56,8 @@ module updateVarsSundials_module gravity, & ! acceleration of gravity (m s-2) Tfreeze, & ! temperature at freezing (K) Cp_air, & ! specific heat of air (J kg-1 K-1) + Cp_ice, & ! specific heat of ice (J kg-1 K-1) + Cp_water, & ! specific heat of liquid water (J kg-1 K-1) LH_fus, & ! latent heat of fusion (J kg-1) iden_air, & ! intrinsic density of air (kg m-3) iden_ice, & ! intrinsic density of ice (kg m-3) @@ -76,7 +78,6 @@ module updateVarsSundials_module USE var_lookup,only:iLookINDEX ! named variables for structure elements ! provide access to routines to update states -USE updatStateSundials_module,only:updateVegSundials ! update snow states USE updatStateSundials_module,only:updateSnowSundials ! update snow states USE updatStateSundials_module,only:updateSoilSundials ! update soil states @@ -90,6 +91,8 @@ module updateVarsSundials_module USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists USE soil_utilsSundials_module,only:liquidHeadSundials ! compute the liquid water matric potential +USE soil_utilsSundials_module,only:d2Theta_dPsi2 +USE soil_utilsSundials_module,only:d2Theta_dTk2 ! IEEE checks USE, intrinsic :: ieee_arithmetic ! check values (NaN, etc.) @@ -101,17 +104,18 @@ module updateVarsSundials_module contains ! ********************************************************************************************************** - ! public subroutine updateVarsSundials: compute diagnostic variables + ! public subroutine updateVarsSundials: compute diagnostic variables and derivatives for Sundials Jacobian ! ********************************************************************************************************** subroutine updateVarsSundials(& ! input - dt_cur, & + dt, & ! intent(in): time step + insideIDA, & ! intent(in): logical flag if inside Sundials solver do_adjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU - mLayerVolFracWatPrev, & ! intent(in) - mLayerMatricHeadPrev, & ! intent(in) + mLayerVolFracWatPrev, & ! intent(in): previous vector of total water matric potential (m) + mLayerMatricHeadPrev, & ! intent(in): previous vector of volumetric total water content (-) diag_data, & ! intent(inout): model diagnostic variables for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy @@ -119,10 +123,10 @@ subroutine updateVarsSundials(& scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2) + scalarCanopyTempPrime, & ! intent(inout): trial value of time derivative canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): trial value of time derivative canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): trial value of time derivative canopy liquid water (kg m-2) + scalarCanopyIcePrime, & ! intent(inout): trial value of time derivative canopy ice content (kg m-2) ! output: variables for the snow-soil domain mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) @@ -130,25 +134,26 @@ subroutine updateVarsSundials(& mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! reza - mLayerVolFracWatPrime, & ! reza - mLayerVolFracLiqPrime, & ! reza - mLayerVolFracIcePrime, & ! reza - mLayerMatricHeadPrime, & ! reza - mLayerMatricHeadLiqPrime, & ! reza + mLayerTempPrime, & ! intent(inout): trial value of time derivative layer temperature (K) + mLayerVolFracWatPrime, & ! intent(inout): trial value of time derivative volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): trial value of time derivative volumetric liquid water content (-) + mLayerVolFracIcePrime, & ! intent(inout): trial value of time derivative volumetric ice water content (-) + mLayerMatricHeadPrime, & ! intent(inout): trial value of time derivative total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): trial value of time derivative liquid water matric potential (m) ! output: error control err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- implicit none ! input - real(rkind),intent(in) :: dt_cur + real(rkind) ,intent(in) :: dt ! time step + logical(lgt) ,intent(in) :: insideIDA ! flag if inside Sundials solver logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - real(rkind),intent(in) :: mLayerVolFracWatPrev(:) - real(rkind),intent(in) :: mLayerMatricHeadPrev(:) + real(rkind),intent(in) :: mLayerVolFracWatPrev(:) ! previous vector of total water matric potential (m) + real(rkind),intent(in) :: mLayerMatricHeadPrev(:) ! previous vector of volumetric total water content (-) type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy @@ -156,11 +161,10 @@ subroutine updateVarsSundials(& real(rkind),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) real(rkind),intent(inout) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) real(rkind),intent(inout) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - - real(rkind),intent(inout) :: scalarCanopyTempPrime ! trial value of canopy temperature (K) - real(rkind),intent(inout) :: scalarCanopyWatPrime ! trial value of canopy total water (kg m-2) - real(rkind),intent(inout) :: scalarCanopyLiqPrime ! trial value of canopy liquid water (kg m-2) - real(rkind),intent(inout) :: scalarCanopyIcePrime ! trial value of canopy ice content (kg m-2) + real(rkind),intent(inout) :: scalarCanopyTempPrime ! trial value of time derivative canopy temperature (K) + real(rkind),intent(inout) :: scalarCanopyWatPrime ! trial value of time derivative canopy total water (kg m-2) + real(rkind),intent(inout) :: scalarCanopyLiqPrime ! trial value of time derivative canopy liquid water (kg m-2) + real(rkind),intent(inout) :: scalarCanopyIcePrime ! trial value of time derivative canopy ice content (kg m-2) ! output: variables for the snow-soil domain real(rkind),intent(inout) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) real(rkind),intent(inout) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) @@ -168,14 +172,12 @@ subroutine updateVarsSundials(& real(rkind),intent(inout) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric ice water content (-) real(rkind),intent(inout) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) real(rkind),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) - - real(rkind),intent(inout) :: mLayerTempPrime(:) - real(rkind),intent(inout) :: mLayerVolFracWatPrime(:) ! reza - real(rkind),intent(inout) :: mLayerVolFracLiqPrime(:) ! reza - real(rkind),intent(inout) :: mLayerVolFracIcePrime(:) ! reza - real(rkind),intent(inout) :: mLayerMatricHeadPrime(:) ! reza - real(rkind),intent(inout) :: mLayerMatricHeadLiqPrime(:) ! reza - + real(rkind),intent(inout) :: mLayerTempPrime(:) ! trial value of time derivative layer temperature (K) + real(rkind),intent(inout) :: mLayerVolFracWatPrime(:) ! trial value of time derivative volumetric total water content (-) + real(rkind),intent(inout) :: mLayerVolFracLiqPrime(:) ! trial value of time derivative volumetric liquid water content (-) + real(rkind),intent(inout) :: mLayerVolFracIcePrime(:) ! trial value of time derivative volumetric ice water content (-) + real(rkind),intent(inout) :: mLayerMatricHeadPrime(:) ! trial value of time derivative total water matric potential (m) + real(rkind),intent(inout) :: mLayerMatricHeadLiqPrime(:) ! trial value of time derivative liquid water matric potential (m) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -192,10 +194,11 @@ subroutine updateVarsSundials(& logical(lgt),allocatable :: computedCoupling(:) ! .true. if computed the coupling for a given state variable real(rkind) :: scalarVolFracLiq ! volumetric fraction of liquid water (-) real(rkind) :: scalarVolFracIce ! volumetric fraction of ice (-) - real(rkind) :: scalarVolFracLiqPrime ! volumetric fraction of liquid water (-) - real(rkind) :: scalarVolFracIcePrime ! volumetric fraction of ice (-) + real(rkind) :: scalarVolFracLiqPrime ! time derivative volumetric fraction of liquid water (-) + real(rkind) :: scalarVolFracIcePrime ! time derivative volumetric fraction of ice (-) real(rkind) :: Tcrit ! critical soil temperature below which ice exists (K) real(rkind) :: xTemp ! temporary temperature (K) + real(rkind) :: fLiq ! fraction of liquid water (-) real(rkind) :: effSat ! effective saturation (-) real(rkind) :: avPore ! available pore space (-) character(len=256) :: cMessage ! error message of downwind routine @@ -213,8 +216,8 @@ subroutine updateVarsSundials(& real(rkind) :: critDiff ! temperature difference from critical (K) real(rkind) :: tempMin ! minimum bracket for temperature (K) real(rkind) :: tempMax ! maximum bracket for temperature (K) - logical(lgt) :: bFlag ! flag to denote that iteration increment was constrained using bi-section - real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical temperature (K) + logical(lgt) :: bFlag ! flag to denote that iteration increment was constrained using bi-section + real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical temperature (K) ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures associate(& @@ -273,8 +276,19 @@ subroutine updateVarsSundials(& dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(out): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) & ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature - ) ! association with variables in the data structures + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature + dFracLiqSnow_dTk => deriv_data%var(iLookDERIV%dFracLiqSnow_dTk )%dat ,& ! intent(out): [dp(:)] derivative in fraction of liquid snow w.r.t. temperature + dFracLiqVeg_dTkCanopy => deriv_data%var(iLookDERIV%dFracLiqVeg_dTkCanopy )%dat(1) ,& ! intent(out): [dp ] derivative in fraction of (throughfall + drainage) w.r.t. temperature + ! derivatives inside solver for Jacobian only + dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. temperature + d2VolTot_d2Psi0 => deriv_data%var(iLookDERIV%d2VolTot_d2Psi0 )%dat ,& ! intent(out): [dp(:)] second derivative in total water content w.r.t. total water matric potential + mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2 )%dat ,& ! intent(out): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature + d2Theta_dTkCanopy2 => deriv_data%var(iLookDERIV%d2Theta_dTkCanopy2 )%dat(1) & ! intent(out): [dp ] second derivative of volumetric liquid water content w.r.t. temperature +) ! association with variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- @@ -341,8 +355,6 @@ subroutine updateVarsSundials(& print*, 'isNrgState = ', isNrgState endif - - ! ======================================================================================================================================= ! ======================================================================================================================================= ! ======================================================================================================================================= @@ -353,7 +365,7 @@ subroutine updateVarsSundials(& ! update hydrology state variables for the uncoupled solution if(.not.isNrgState .and. .not.isCoupled)then - stop 1 + if(.not.insideIDA) stop 1 ! this does not work yet? FIX ! update the total water from volumetric liquid water if(ixStateType(ixFullVector)==iname_liqCanopy .or. ixStateType(ixFullVector)==iname_liqLayer)then @@ -401,11 +413,10 @@ subroutine updateVarsSundials(& endif ! if hydrology state variable or uncoupled solution - ! compute the critical soil temperature below which ice exists select case(ixDomainType) - case(iname_veg, iname_snow); Tcrit = Tfreeze - case(iname_soil); Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) + case(iname_veg, iname_snow); Tcrit = Tfreeze + case(iname_soil); Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return end select @@ -439,38 +450,78 @@ subroutine updateVarsSundials(& ! - compute derivatives... ! ------------------------ + ! compute the derivative in bulk heat capacity w.r.t. total water content or water matric potential (m-1) ! compute the derivative in total water content w.r.t. total water matric potential (m-1) ! NOTE 1: valid for frozen and unfrozen conditions - ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi - if(ixDomainType==iname_soil)then - select case( ixStateType(ixFullVector) ) - case(iname_lmpLayer); dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore - case default; dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - end select - endif + ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi, dVolHtCapBulk_dPsi0 may be wrong + select case(ixDomainType) + case(iname_veg) + if(insideIDA)then + fLiq = fracLiquid(xTemp,snowfrz_scale) + dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) + endif + case(iname_snow) + if(insideIDA)then + fLiq = fracLiquid(xTemp,snowfrz_scale) + dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air + endif + case(iname_soil) + select case( ixStateType(ixFullVector) ) + case(iname_lmpLayer) + dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + if(insideIDA) d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + case default + dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + if(insideIDA) d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),& + vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + end select + ! dVolHtCapBulk_dPsi0 uses the derivative in water retention curve above critical temp w.r.t.state variable dVolTot_dPsi0 + if(insideIDA)then + dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use + if(xTemp< Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) + if(xTemp>=Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_water * Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) + endif + end select ! compute the derivative in liquid water content w.r.t. temperature ! --> partially frozen: dependence of liquid water on temperature + ! compute the derivative in bulk heat capacity w.r.t. temperature if(xTemp unfrozen: no dependence of liquid water on temperature else select case(ixDomainType) - case(iname_veg); dTheta_dTkCanopy = 0._rkind - case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + case(iname_veg); dTheta_dTkCanopy = 0._rkind; d2Theta_dTkCanopy2 = 0._rkind; dFracLiqVeg_dTkCanopy = 0._rkind; dVolHtCapBulk_dTkCanopy = 0._rkind + case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind; mLayerd2Theta_dTk2(iLayer) = 0._rkind; dFracLiqSnow_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind end select ! domain type endif - - - ! ----- ! - update volumetric fraction of liquid water and ice... ! => case of hydrology state uncoupled with energy (and when not adjusting the temperature)... @@ -478,9 +529,10 @@ subroutine updateVarsSundials(& ! case of hydrology state uncoupled with energy (and when not adjusting the temperature) if(.not.do_adjustTemp .and. .not.isNrgState .and. .not.isCoupled)then + ! compute the fraction of snow select case(ixDomainType) - case(iname_veg); scalarFracLiqVeg = fracliquid(xTemp,snowfrz_scale) + case(iname_veg); scalarFracLiqVeg = fracliquid(xTemp,snowfrz_scale) case(iname_snow); mLayerFracLiqSnow(iLayer) = fracliquid(xTemp,snowfrz_scale) case(iname_soil) ! do nothing case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return @@ -499,34 +551,43 @@ subroutine updateVarsSundials(& ! *** vegetation canopy case(iname_veg) - ! compute mass of liquid water and ice - call updateVegSundials(& + + ! compute volumetric fraction of liquid water and ice + call updateSnowSundials(& xTemp, & ! intent(in) : temperature (K) - scalarCanopyWatTrial, & ! intent(in) : mass of total water (-) + scalarCanopyWatTrial/(iden_water*canopyDepth),& ! intent(in) : volumetric fraction of total water (-) snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) - scalarCanopyTempPrime, & ! intent(in) - scalarCanopyWatPrime, & ! intent(in) : mass of total water (-) - scalarCanopyLiqTrial, & ! intent(out) : trial mass of liquid water (-) - scalarCanopyIceTrial, & ! intent(out) : trial mass of ice (-) - scalarCanopyLiqPrime, & ! intent(out) : trial mass of liquid water (-) - scalarCanopyIcePrime, & ! intent(out) : trial mass of ice (-) + scalarCanopyTempPrime, & ! intent(in) : canopy temperature time derivative (K/s) + scalarCanopyWatPrime/(iden_water*canopyDepth),& ! intent(in) : volumetric fraction of total water time derivative (-) + scalarVolFracLiq, & ! intent(out) : trial canopy liquid water (-) + scalarVolFracIce, & ! intent(out) : trial volumetric canopy ice (-) + scalarVolFracLiqPrime, & ! intent(out) : trial volumetric canopy liquid water (-) + scalarVolFracIcePrime, & ! intent(out) : trial volumetric canopy ice (-) scalarFracLiqVeg, & ! intent(out) : fraction of liquid water (-) err,cmessage) ! intent(out) : error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + ! compute mass of water on the canopy + ! NOTE: possibilities for speed-up here + scalarCanopyLiqTrial = scalarFracLiqVeg *scalarCanopyWatTrial !(kg m-2), scalarVolFracLiq*iden_water*canopyDepth + scalarCanopyLiqPrime = scalarVolFracLiqPrime*iden_water*canopyDepth + scalarCanopyIceTrial = (1._rkind - scalarFracLiqVeg)*scalarCanopyWatTrial !(kg m-2), scalarVolFracIce* iden_ice *canopyDepth + scalarCanopyIcePrime = scalarVolFracIcePrime* iden_ice *canopyDepth + ! *** snow layers case(iname_snow) + ! compute volumetric fraction of liquid water and ice call updateSnowSundials(& xTemp, & ! intent(in) : temperature (K) mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) - mLayerTempPrime(iLayer), & ! - mLayerVolFracWatPrime(iLayer), & ! intent(in) + mLayerTempPrime(iLayer), & ! intent(in) : temperature time derivative (K/s) + mLayerVolFracWatPrime(iLayer), & ! intent(in) : volumetric fraction of total water time derivative (-) mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) - mLayerVolFracLiqPrime(iLayer), & ! intent(out) - mLayerVolFracIcePrime(iLayer), & ! intent(out) + mLayerVolFracLiqPrime(iLayer), & ! intent(out) : volumetric fraction of liquid water time derivative (-) + mLayerVolFracIcePrime(iLayer), & ! intent(out) : volumetric fraction of ice time derivative (-) mLayerFracLiqSnow(iLayer), & ! intent(out) : fraction of liquid water (-) err,cmessage) ! intent(out) : error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -534,27 +595,27 @@ subroutine updateVarsSundials(& ! *** soil layers case(iname_soil) - ! compute volumetric fraction of liquid water and ice, step size dt_cur changes here + ! compute volumetric fraction of liquid water and ice call updateSoilSundials(& - dt_cur, & - xTemp, & ! intent(in) : temperature (K) - mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) - mLayerMatricHeadPrev(ixControlIndex), & ! intent(in) - mLayerVolFracWatPrev(iLayer), & ! intent(in) - mLayerTempPrime(iLayer), & - mLayerMatricHeadPrime(ixControlIndex), & - ! intent(in) : soil parameters - vGn_alpha(ixControlIndex), & - vGn_n(ixControlIndex), & - theta_sat(ixControlIndex), & - theta_res(ixControlIndex), & - vGn_m(ixControlIndex), & - mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) - mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) - mLayerVolFracWatPrime(iLayer), & - mLayerVolFracLiqPrime(iLayer), & - mLayerVolFracIcePrime(iLayer), & + dt, & ! intent(in) : time step + insideIDA, & ! intent(in) : logical flag if inside Sundials solver + xTemp, & ! intent(in) : temperature (K) + mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) + mLayerMatricHeadPrev(ixControlIndex), & ! intent(in) : previous values, will be same as current if insideIDA + mLayerVolFracWatPrev(iLayer), & ! intent(in) : previous values, will be same as current if insideIDA + mLayerTempPrime(iLayer), & ! intent(in) : temperature time derivative (K/s) + mLayerMatricHeadPrime(ixControlIndex), & ! intent(in) : total water matric potential time derivative (m/s) + vGn_alpha(ixControlIndex), & ! intent(in) : van Genutchen "alpha" parameter + vGn_n(ixControlIndex), & ! intent(in) : van Genutchen "n" parameter + theta_sat(ixControlIndex), & ! intent(in) : soil porosity (-) + theta_res(ixControlIndex), & ! intent(in) : soil residual volumetric water content (-) + vGn_m(ixControlIndex), & ! intent(in) : van Genutchen "m" parameter (-) + mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) + mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) + mLayerVolFracWatPrime(iLayer), & ! intent(out) : volumetric fraction of total water time derivative (-) + mLayerVolFracLiqPrime(iLayer), & ! intent(out) : volumetric fraction of liquid water time derivative (-) + mLayerVolFracIcePrime(iLayer), & ! intent(out) : volumetric fraction of ice time derivative (-) err,cmessage) ! intent(out) : error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -578,9 +639,106 @@ subroutine updateVarsSundials(& endif ! if energy state or solution is coupled ! ----- - ! - update temperatures... ! ------------------------ + ! check the need to adjust temperature (will always be false if inside solver) + ! can be true if inside varSubstepSundials, outside solver, but currently will not work so turn off + if(do_adjustTemp .and. insideIDA)then + + ! get the melt energy + meltNrg = merge(LH_fus*iden_ice, LH_fus*iden_water, ixDomainType==iname_snow) + + ! compute the residual and the derivative + select case(ixDomainType) + + ! * vegetation + case(iname_veg) + call xTempSolve(& + ! constant over iterations + meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) + heatCap = scalarBulkVolHeatCapVeg ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) + tempInit = scalarCanopyTemp ,& ! intent(in) : initial temperature (K) + volFracIceInit = scalarCanopyIce/(iden_water*canopyDepth),& ! intent(in) : initial volumetric fraction of ice (-) + ! trial values + xTemp = xTemp ,& ! intent(inout) : trial value of temperature + dLiq_dT = dTheta_dTkCanopy ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) + volFracIceTrial = scalarVolFracIce ,& ! intent(in) : trial value for volumetric fraction of ice + ! residual and derivative + residual = residual ,& ! intent(out) : residual (J m-3) + derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) + + ! * snow and soil + case(iname_snow, iname_soil) + call xTempSolve(& + ! constant over iterations + meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) + heatCap = mLayerVolHtCapBulk(iLayer) ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) + tempInit = mLayerTemp(iLayer) ,& ! intent(in) : initial temperature (K) + volFracIceInit = mLayerVolFracIce(iLayer) ,& ! intent(in) : initial volumetric fraction of ice (-) + ! trial values + xTemp = xTemp ,& ! intent(inout) : trial value of temperature + dLiq_dT = mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) + volFracIceTrial = mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : trial value for volumetric fraction of ice + ! residual and derivative + residual = residual ,& ! intent(out) : residual (J m-3) + derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) + + ! * check + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + + end select ! domain type + + ! check validity of residual + if( ieee_is_nan(residual) )then + message=trim(message)//'residual is not valid' + err=20; return + endif + + ! update bracket + if(residual < 0._rkind)then + tempMax = min(xTemp,tempMax) + else + tempMin = max(tempMin,xTemp) + end if + + ! compute iteration increment + tempInc = residual/derivative ! K + + ! check + if(globalPrintFlag)& + write(*,'(i4,1x,e20.10,1x,5(f20.10,1x),L1)') iter, residual, xTemp-Tcrit, tempInc, Tcrit, tempMin, tempMax, bFlag + + ! check convergence + if(abs(residual) < nrgConvTol .or. abs(tempInc) < tempConvTol) exit iterations + + ! add constraints for snow temperature + if(ixDomainType==iname_veg .or. ixDomainType==iname_snow)then + if(tempInc > Tcrit - xTemp) tempInc=(Tcrit - xTemp)*0.5_rkind ! simple bi-section method + endif ! if the domain is vegetation or snow + + ! deal with the discontinuity between partially frozen and unfrozen soil + if(ixDomainType==iname_soil)then + ! difference from the temperature below which ice exists + critDiff = Tcrit - xTemp + ! --> initially frozen (T < Tcrit) + if(critDiff > 0._rkind)then + if(tempInc > critDiff) tempInc = critDiff + epsT ! set iteration increment to slightly above critical temperature + ! --> initially unfrozen (T > Tcrit) + else + if(tempInc < critDiff) tempInc = critDiff - epsT ! set iteration increment to slightly below critical temperature + endif + endif ! if the domain is soil + + ! update the temperature trial + xTemp = xTemp + tempInc + + ! check failed convergence + if(iter==maxiter)then + message=trim(message)//'failed to converge' + err=-20; return ! negative error code = try to recover + endif + + endif ! if adjusting the temperature end do iterations ! iterating @@ -625,9 +783,9 @@ subroutine updateVarsSundials(& vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in) : soil parameters dVolTot_dPsi0(ixControlIndex) ,& ! intent(in) : derivative in the soil water characteristic (m-1) mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) - mLayerTempPrime(ixControlIndex) ,& + mLayerTempPrime(ixControlIndex) ,& mLayerVolFracLiqPrime(iLayer) ,& - mLayerVolFracIcePrime(iLayer) ,& + mLayerVolFracIcePrime(iLayer) ,& ! output mLayerMatricHeadLiqTrial(ixControlIndex) ,& ! intent(out): liquid water matric potential (m) mLayerMatricHeadLiqPrime(ixControlIndex) ,& ! @@ -652,4 +810,38 @@ subroutine updateVarsSundials(& end subroutine updateVarsSundials + ! ********************************************************************************************************** + ! private subroutine xTempSolve: compute residual and derivative for temperature + ! ********************************************************************************************************** + subroutine xTempSolve(& + ! input: constant over iterations + meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) + heatCap ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) + tempInit ,& ! intent(in) : initial temperature (K) + volFracIceInit ,& ! intent(in) : initial volumetric fraction of ice (-) + ! input-output: trial values + xTemp ,& ! intent(inout) : trial value of temperature + dLiq_dT ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) + volFracIceTrial ,& ! intent(in) : trial value for volumetric fraction of ice + ! output: residual and derivative + residual ,& ! intent(out) : residual (J m-3) + derivative ) ! intent(out) : derivative (J m-3 K-1) + implicit none + ! input: constant over iterations + real(rkind),intent(in) :: meltNrg ! energy for melt+freeze (J m-3) + real(rkind),intent(in) :: heatCap ! volumetric heat capacity (J m-3 K-1) + real(rkind),intent(in) :: tempInit ! initial temperature (K) + real(rkind),intent(in) :: volFracIceInit ! initial volumetric fraction of ice (-) + ! input-output: trial values + real(rkind),intent(inout) :: xTemp ! trial value for temperature + real(rkind),intent(in) :: dLiq_dT ! derivative in liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: volFracIceTrial ! trial value for the volumetric fraction of ice (-) + ! output: residual and derivative + real(rkind),intent(out) :: residual ! residual (J m-3) + real(rkind),intent(out) :: derivative ! derivative (J m-3 K-1) + ! subroutine starts here + residual = -heatCap*(xTemp - tempInit) + meltNrg*(volFracIceTrial - volFracIceInit) ! J m-3 + derivative = heatCap + LH_fus*iden_water*dLiq_dT ! J m-3 K-1 + end subroutine xTempSolve + end module updateVarsSundials_module diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index b0fc27037..42f690ac9 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -127,11 +127,11 @@ subroutine varSubstepSundials(& ! structure allocations USE allocspace_module,only:allocLocal ! allocate local data structures ! simulation of fluxes and residuals given a trial state vector - USE systemSolv_module,only:systemSolv ! solve the system of equations for one time step - USE getVectorz_module,only:popStateVec ! populate the state vector - USE getVectorz_module,only:varExtract ! extract variables from the state vector - USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables - USE varExtrSundials_module, only:varExtractSundials + USE systemSolv_module,only:systemSolv ! solve the system of equations for one time step + USE getVectorz_module,only:popStateVec ! populate the state vector + USE getVectorz_module,only:varExtract ! extract variables from the state vector + USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables + USE varExtrSundials_module,only:varExtractSundials ! identify name of variable type (for error message) USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages USE systemSolvSundials_module,only:systemSolvSundials @@ -544,7 +544,7 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures waterBalanceError,nrgFluxModified,err,message) ! output: flags and error control USE getVectorz_module,only:varExtract ! extract variables from the state vector - USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables + USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables USE varExtrSundials_module, only:varExtractSundials USE computEnthalpy_module,only:computEnthalpy USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy @@ -759,12 +759,13 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux call updateVarsSundials(& ! input dt, & - doAdjustTemp, & ! intent(in): logical flag to adjust temperature to accou melt+freeze + .false., & ! intent(in): logical flag if inside Sundials solver + doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU - mLayerVolFracWatTrial, & - mLayerMatricHeadTrial, & + mLayerVolFracWatTrial, & ! intent(in): use current vector for prev vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): use current vector for prev vector of total water matric potential (m) diag_data, & ! intent(inout): model diagnostic variables for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy From 8019b7dce96bd2be843a5d7cdcf9ab21e08ece86 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 7 Sep 2022 14:55:50 +0900 Subject: [PATCH 0330/1472] Change sundials names so match BE modules: Does not need non Sundials version: eval8DAE.f90 to eval8summaSundials.f90 (uses Sundials one in systemSolvSundials, then also calls just this in other routines, so not an add on) computResidDAE.f90 to computResidSundials.f90 (doesn't use nonSundials one) computJacDAE.f90 to computJacobSundials.f90 (doesn't use nonSundials one) solveByIDA.f90 to summaSolveSundialsIDA.f90? (doesn't use nonSundials one) These are add ons, needs nonSundials one too: soil_utilsSundials.f90 to soil_utilsAddSundials.f90 varExtrSundials.f90 to getVectorzAddSundials.f90 --- build/my_makefile_cop | 36 ++++++------- build/my_makefile_gra | 36 ++++++------- build/my_makefile_mac | 34 ++++++------ build/my_makefile_ric | 32 +++++------ ...mputJacDAE.f90 => computJacobSundials.f90} | 14 ++--- ...utResidDAE.f90 => computResidSundials.f90} | 14 ++--- build/source/engine/eval8JacDAE.f90 | 10 ++-- .../{eval8DAE.f90 => eval8summaSundials.f90} | 20 +++---- build/source/engine/evalDAE4IDA.f90 | 4 +- ...Sundials.f90 => getVectorzAddSundials.f90} | 4 +- build/source/engine/snowLiqFlx.f90 | 2 +- ...Sundials.f90 => soil_utilsAddSundials.f90} | 4 +- ...lveByIDA.f90 => summaSolveSundialsIDA.f90} | 54 +++++++++---------- build/source/engine/systemSolvSundials.f90 | 6 +-- build/source/engine/updateVarsSundials.f90 | 6 +-- build/source/engine/varSubstepSundials.f90 | 4 +- 16 files changed, 140 insertions(+), 140 deletions(-) rename build/source/engine/{computJacDAE.f90 => computJacobSundials.f90} (99%) rename build/source/engine/{computResidDAE.f90 => computResidSundials.f90} (98%) rename build/source/engine/{eval8DAE.f90 => eval8summaSundials.f90} (99%) rename build/source/engine/{varExtrSundials.f90 => getVectorzAddSundials.f90} (99%) rename build/source/engine/{soil_utilsSundials.f90 => soil_utilsAddSundials.f90} (99%) rename build/source/engine/{solveByIDA.f90 => summaSolveSundialsIDA.f90} (94%) diff --git a/build/my_makefile_cop b/build/my_makefile_cop index 941ed87b7..f91efa347 100644 --- a/build/my_makefile_cop +++ b/build/my_makefile_cop @@ -2,13 +2,13 @@ # Makefile to compile SUMMA SUNDIALS #======================================================================== # -# Recommended use: Copy this file to Makefile.local, edit it to your +# Recommended use: Copy this file to Makefile.local, edit it to your # heart's content, and then run `make -f build/Makefile.local` from # your top level SUMMA directory. Don't include the Makefile.local in # any pull requests you make. # # Note that Makefile configurations that we commonly use can be found on -# the SUMMA wiki at: +# the SUMMA wiki at: # https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration # feel free to add yours to that page. # @@ -22,10 +22,10 @@ # * ILDFLAGS - path to libraries to include # * LIBRARIES - libraries to include # * DIR_SUNDIALS - installation sundials directory -# +# # Some further options can be specified for OpenMP, etc. See in Part 0 of -# the Makefile. You don't need to make any changes in PART 1 and -# following unless you are doing SUMMA development and changed what +# the Makefile. You don't need to make any changes in PART 1 and +# following unless you are doing SUMMA development and changed what # needs to be compiled #======================================================================== @@ -33,7 +33,7 @@ #======================================================================== # The variables can be specified in one of two ways: -# * delete the '##' in front of the variable, fill out the entry, +# * delete the '##' in front of the variable, fill out the entry, # save the file and run make # * make no changes to this file, but specify the variables in your # environment before you run make @@ -54,21 +54,21 @@ FC = gfortran # FC and FC_EXE have to be consistent FC_EXE = gfortran -# Define the NetCDF and LAPACK libraries and path to include files. +# Define the NetCDF and LAPACK libraries and path to include files. # INCLUDES needs to be of the form (no quotes around the string): # INCLUDES = -I -I -I<...> -I # LIBRARIES needs to be of the form ( no quotes around the string): # LIBRARIES = '-L -lnetcdff -L -lblas -L -l' # If none of this makes sense, please talk to your system # administrator. -INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include +INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include LDFLAGS = -L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib -LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas +LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas DIR_SUNDIALS=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran -LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod - +LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod + # Eventually we plan move to a real configure script, but for now we like # to keep track of successful compilations of SUMMA on different platforms # and with different compilers. If you are successful compiling SUMMA, @@ -193,7 +193,7 @@ SUMMA_UTILMS= \ mDecisions.f90 \ snow_utils.f90 \ soil_utils.f90 \ - soil_utilsSundials.f90 \ + soil_utilsAddSundials.f90 \ updatState.f90 \ updatStateSundials.f90 \ matrixOper.f90 @@ -227,14 +227,14 @@ SUMMA_SOLVER= \ computEnthalpy.f90 \ computHeatCap.f90 \ computThermConduct.f90 \ - computResidDAE.f90 \ - eval8DAE.f90 \ + computResidSundials.f90 \ + eval8summaSundials.f90 \ evalDAE4IDA.f90 \ - computJacDAE.f90 \ + computJacobSundials.f90 \ eval8JacDAE.f90 \ evalJac4IDA.f90 \ computSnowDepth.f90 \ - solveByIDA.f90 \ + summaSolveSundialsIDA.f90 \ systemSolvSundials.f90 \ varSubstep.f90 \ varSubstepSundials.f90 \ @@ -272,7 +272,7 @@ NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) SUMMA_MODRUN = \ indexState.f90 \ getVectorz.f90 \ - varExtrSundials.f90 \ + getVectorzAddSundials.f90 \ t2enthalpy.f90 \ updateVars.f90 \ updateVarsSundials.f90 \ @@ -393,7 +393,7 @@ compile_noah: # compile common routines compile_comm: - $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) + $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) # compile SUMMA routines compile_summa: update_version diff --git a/build/my_makefile_gra b/build/my_makefile_gra index 1f7cbb073..be98032b9 100644 --- a/build/my_makefile_gra +++ b/build/my_makefile_gra @@ -2,13 +2,13 @@ # Makefile to compile SUMMA SUNDIALS #======================================================================== # -# Recommended use: Copy this file to Makefile.local, edit it to your +# Recommended use: Copy this file to Makefile.local, edit it to your # heart's content, and then run `make -f build/Makefile.local` from # your top level SUMMA directory. Don't include the Makefile.local in # any pull requests you make. # # Note that Makefile configurations that we commonly use can be found on -# the SUMMA wiki at: +# the SUMMA wiki at: # https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration # feel free to add yours to that page. # @@ -22,10 +22,10 @@ # * ILDFLAGS - path to libraries to include # * LIBRARIES - libraries to include # * DIR_SUNDIALS - installation sundials directory -# +# # Some further options can be specified for OpenMP, etc. See in Part 0 of -# the Makefile. You don't need to make any changes in PART 1 and -# following unless you are doing SUMMA development and changed what +# the Makefile. You don't need to make any changes in PART 1 and +# following unless you are doing SUMMA development and changed what # needs to be compiled #======================================================================== @@ -33,7 +33,7 @@ #======================================================================== # The variables can be specified in one of two ways: -# * delete the '##' in front of the variable, fill out the entry, +# * delete the '##' in front of the variable, fill out the entry, # save the file and run make # * make no changes to this file, but specify the variables in your # environment before you run make @@ -54,21 +54,21 @@ FC = gfortran # FC and FC_EXE have to be consistent FC_EXE = gfortran -# Define the NetCDF and LAPACK libraries and path to include files. +# Define the NetCDF and LAPACK libraries and path to include files. # INCLUDES needs to be of the form (no quotes around the string): # INCLUDES = -I -I -I<...> -I # LIBRARIES needs to be of the form ( no quotes around the string): # LIBRARIES = '-L -lnetcdff -L -lblas -L -l' # If none of this makes sense, please talk to your system # administrator. -INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include +INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include LDFLAGS = -L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib -LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas +LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas DIR_SUNDIALS=/home/avanb/SummaSundials/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran -LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod - +LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod + # Eventually we plan move to a real configure script, but for now we like # to keep track of successful compilations of SUMMA on different platforms # and with different compilers. If you are successful compiling SUMMA, @@ -193,7 +193,7 @@ SUMMA_UTILMS= \ mDecisions.f90 \ snow_utils.f90 \ soil_utils.f90 \ - soil_utilsSundials.f90 \ + soil_utilsAddSundials.f90 \ updatState.f90 \ updatStateSundials.f90 \ matrixOper.f90 @@ -227,14 +227,14 @@ SUMMA_SOLVER= \ computEnthalpy.f90 \ computHeatCap.f90 \ computThermConduct.f90 \ - computResidDAE.f90 \ - eval8DAE.f90 \ + computResidSundials.f90 \ + eval8summaSundials.f90 \ evalDAE4IDA.f90 \ - computJacDAE.f90 \ + computJacobSundials.f90 \ eval8JacDAE.f90 \ evalJac4IDA.f90 \ computSnowDepth.f90 \ - solveByIDA.f90 \ + summaSolveSundialsIDA.f90 \ systemSolvSundials.f90 \ varSubstep.f90 \ varSubstepSundials.f90 \ @@ -272,7 +272,7 @@ NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) SUMMA_MODRUN = \ indexState.f90 \ getVectorz.f90 \ - varExtrSundials.f90 \ + getVectorzAddSundials.f90 \ t2enthalpy.f90 \ updateVars.f90 \ updateVarsSundials.f90 \ @@ -393,7 +393,7 @@ compile_noah: # compile common routines compile_comm: - $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) + $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) # compile SUMMA routines compile_summa: update_version diff --git a/build/my_makefile_mac b/build/my_makefile_mac index cc89a17f6..ce0ee1083 100644 --- a/build/my_makefile_mac +++ b/build/my_makefile_mac @@ -2,13 +2,13 @@ # Makefile to compile SUMMA SUNDIALS #======================================================================== # -# Recommended use: Copy this file to Makefile.local, edit it to your +# Recommended use: Copy this file to Makefile.local, edit it to your # heart's content, and then run `make -f build/Makefile.local` from # your top level SUMMA directory. Don't include the Makefile.local in # any pull requests you make. # # Note that Makefile configurations that we commonly use can be found on -# the SUMMA wiki at: +# the SUMMA wiki at: # https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration # feel free to add yours to that page. # @@ -22,10 +22,10 @@ # * ILDFLAGS - path to libraries to include # * LIBRARIES - libraries to include # * DIR_SUNDIALS - installation sundials directory -# +# # Some further options can be specified for OpenMP, etc. See in Part 0 of -# the Makefile. You don't need to make any changes in PART 1 and -# following unless you are doing SUMMA development and changed what +# the Makefile. You don't need to make any changes in PART 1 and +# following unless you are doing SUMMA development and changed what # needs to be compiled #======================================================================== @@ -33,7 +33,7 @@ #======================================================================== # The variables can be specified in one of two ways: -# * delete the '##' in front of the variable, fill out the entry, +# * delete the '##' in front of the variable, fill out the entry, # save the file and run make # * make no changes to this file, but specify the variables in your # environment before you run make @@ -54,7 +54,7 @@ FC = gfortran # FC and FC_EXE have to be consistent FC_EXE =/opt/local/bin/gfortran -# Define the NetCDF and LAPACK libraries and path to include files. +# Define the NetCDF and LAPACK libraries and path to include files. # INCLUDES needs to be of the form (no quotes around the string): # INCLUDES = -I -I -I<...> -I # LIBRARIES needs to be of the form ( no quotes around the string): @@ -63,12 +63,12 @@ FC_EXE =/opt/local/bin/gfortran # administrator. INCLUDES = -I/opt/local/include -I/opt/local/lib LDFLAGS = -L/opt/local/lib -LIBRARIES = $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf +LIBRARIES = $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf DIR_SUNDIALS=/Users/amedin/Research/SummaSundials/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran -LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod - +LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod + # Eventually we plan move to a real configure script, but for now we like # to keep track of successful compilations of SUMMA on different platforms # and with different compilers. If you are successful compiling SUMMA, @@ -193,7 +193,7 @@ SUMMA_UTILMS= \ mDecisions.f90 \ snow_utils.f90 \ soil_utils.f90 \ - soil_utilsSundials.f90 \ + soil_utilsAddSundials.f90 \ updatState.f90 \ updatStateSundials.f90 \ matrixOper.f90 @@ -227,14 +227,14 @@ SUMMA_SOLVER= \ computEnthalpy.f90 \ computHeatCap.f90 \ computThermConduct.f90 \ - computResidDAE.f90 \ - eval8DAE.f90 \ + computResidSundials.f90 \ + eval8summaSundials.f90 \ evalDAE4IDA.f90 \ - computJacDAE.f90 \ + computJacobSundials.f90 \ eval8JacDAE.f90 \ evalJac4IDA.f90 \ computSnowDepth.f90 \ - solveByIDA.f90 \ + summaSolveSundialsIDA.f90 \ systemSolvSundials.f90 \ varSubstep.f90 \ varSubstepSundials.f90 \ @@ -272,7 +272,7 @@ NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) SUMMA_MODRUN = \ indexState.f90 \ getVectorz.f90 \ - varExtrSundials.f90 \ + getVectorzAddSundials.f90 \ t2enthalpy.f90 \ updateVars.f90 \ updateVarsSundials.f90 \ @@ -393,7 +393,7 @@ compile_noah: # compile common routines compile_comm: - $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) + $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) # compile SUMMA routines compile_summa: update_version diff --git a/build/my_makefile_ric b/build/my_makefile_ric index 8fafae33e..037e81841 100644 --- a/build/my_makefile_ric +++ b/build/my_makefile_ric @@ -2,13 +2,13 @@ # Makefile to compile SUMMA SUNDIALS #======================================================================== # -# Recommended use: Copy this file to Makefile.local, edit it to your +# Recommended use: Copy this file to Makefile.local, edit it to your # heart's content, and then run `make -f build/Makefile.local` from # your top level SUMMA directory. Don't include the Makefile.local in # any pull requests you make. # # Note that Makefile configurations that we commonly use can be found on -# the SUMMA wiki at: +# the SUMMA wiki at: # https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration # feel free to add yours to that page. # @@ -22,10 +22,10 @@ # * ILDFLAGS - path to libraries to include # * LIBRARIES - libraries to include # * DIR_SUNDIALS - installation sundials directory -# +# # Some further options can be specified for OpenMP, etc. See in Part 0 of -# the Makefile. You don't need to make any changes in PART 1 and -# following unless you are doing SUMMA development and changed what +# the Makefile. You don't need to make any changes in PART 1 and +# following unless you are doing SUMMA development and changed what # needs to be compiled #======================================================================== @@ -33,7 +33,7 @@ #======================================================================== # The variables can be specified in one of two ways: -# * delete the '##' in front of the variable, fill out the entry, +# * delete the '##' in front of the variable, fill out the entry, # save the file and run make # * make no changes to this file, but specify the variables in your # environment before you run make @@ -54,7 +54,7 @@ FC = gfortran # FC and FC_EXE have to be consistent FC_EXE = gfortran -# Define the NetCDF and LAPACK libraries and path to include files. +# Define the NetCDF and LAPACK libraries and path to include files. # INCLUDES needs to be of the form (no quotes around the string): # INCLUDES = -I -I -I<...> -I # LIBRARIES needs to be of the form ( no quotes around the string): @@ -67,8 +67,8 @@ LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas -lnetcdf DIR_SUNDIALS=/u1/gwu479/SummaSundials/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran -LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod - +LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod + # Eventually we plan move to a real configure script, but for now we like # to keep track of successful compilations of SUMMA on different platforms # and with different compilers. If you are successful compiling SUMMA, @@ -193,7 +193,7 @@ SUMMA_UTILMS= \ mDecisions.f90 \ snow_utils.f90 \ soil_utils.f90 \ - soil_utilsSundials.f90 \ + soil_utilsAddSundials.f90 \ updatState.f90 \ updatStateSundials.f90 \ matrixOper.f90 @@ -227,14 +227,14 @@ SUMMA_SOLVER= \ computEnthalpy.f90 \ computHeatCap.f90 \ computThermConduct.f90 \ - computResidDAE.f90 \ - eval8DAE.f90 \ + computResidSundials.f90 \ + eval8summaSundials.f90 \ evalDAE4IDA.f90 \ - computJacDAE.f90 \ + computJacobSundials.f90 \ eval8JacDAE.f90 \ evalJac4IDA.f90 \ computSnowDepth.f90 \ - solveByIDA.f90 \ + summaSolveSundialsIDA.f90 \ systemSolvSundials.f90 \ varSubstep.f90 \ varSubstepSundials.f90 \ @@ -272,7 +272,7 @@ NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) SUMMA_MODRUN = \ indexState.f90 \ getVectorz.f90 \ - varExtrSundials.f90 \ + getVectorzAddSundials.f90 \ t2enthalpy.f90 \ updateVars.f90 \ updateVarsSundials.f90 \ @@ -393,7 +393,7 @@ compile_noah: # compile common routines compile_comm: - $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) + $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) # compile SUMMA routines compile_summa: update_version diff --git a/build/source/engine/computJacDAE.f90 b/build/source/engine/computJacobSundials.f90 similarity index 99% rename from build/source/engine/computJacDAE.f90 rename to build/source/engine/computJacobSundials.f90 index 8c105ce95..394346ea1 100644 --- a/build/source/engine/computJacDAE.f90 +++ b/build/source/engine/computJacobSundials.f90 @@ -18,7 +18,7 @@ ! You should have received a copy of the GNU General Public License ! along with this program. If not, see . -module computJacDAE_module +module computJacobSundials_module ! data types USE nrtype @@ -82,13 +82,13 @@ module computJacDAE_module integer(i4b),parameter :: ixBandOffset=kl+ku+1 ! offset in the band Jacobian matrix private -public::computJacDAE +public::computJacobSundials contains ! ********************************************************************************************************** - ! public subroutine computJacDAE: compute the Jacobian matrix + ! public subroutine computJacobSundials: compute the Jacobian matrix ! ********************************************************************************************************** - subroutine computJacDAE(& + subroutine computJacobSundials(& ! input: model control cj, & ! intent(in): this scalar changes whenever the step size or method order changes dt, & ! intent(in): length of the time step (seconds) @@ -289,7 +289,7 @@ subroutine computJacDAE(& ) ! making association with data in structures ! -------------------------------------------------------------- ! initialize error control - err=0; message='computJacDAE/' + err=0; message='computJacobSundials/' ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* @@ -712,7 +712,7 @@ subroutine computJacDAE(& ! end association to variables in the data structures end associate - end subroutine computJacDAE + end subroutine computJacobSundials ! ********************************************************************************************************** @@ -726,4 +726,4 @@ function ixOffDiag(jState,iState) ixOffDiag = ixBandOffset + jState - iState end function ixOffDiag -end module computJacDAE_module +end module computJacobSundials_module diff --git a/build/source/engine/computResidDAE.f90 b/build/source/engine/computResidSundials.f90 similarity index 98% rename from build/source/engine/computResidDAE.f90 rename to build/source/engine/computResidSundials.f90 index 76920b6af..61b9d24ff 100644 --- a/build/source/engine/computResidDAE.f90 +++ b/build/source/engine/computResidSundials.f90 @@ -1,6 +1,6 @@ -module computResidDAE_module +module computResidSundials_module ! data types USE nrtype @@ -50,13 +50,13 @@ module computResidDAE_module ! privacy implicit none private::printResidDAE -public::computResidDAE +public::computResidSundials contains ! ********************************************************************************************************** - ! public subroutine computResidDAE: compute the residual vector + ! public subroutine computResidSundials: compute the residual vector ! ********************************************************************************************************** - subroutine computResidDAE(& + subroutine computResidSundials(& ! input: model control nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -161,7 +161,7 @@ subroutine computResidDAE(& ) ! association to necessary variables for the residual computations ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="computResidDAE/" + err=0; message="computResidSundials/" ! --- ! * compute sink terms... @@ -249,7 +249,7 @@ subroutine computResidDAE(& ! end association with the necessary variabiles for the residual calculations end associate - end subroutine computResidDAE + end subroutine computResidSundials ! ********************************************************************************************************** ! private subroutine printResidDAE: print the residual vector mainly for debugging @@ -343,4 +343,4 @@ subroutine printResidDAE( & end subroutine printResidDAE -end module computResidDAE_module +end module computResidSundials_module diff --git a/build/source/engine/eval8JacDAE.f90 b/build/source/engine/eval8JacDAE.f90 index 068ede391..235567f7a 100644 --- a/build/source/engine/eval8JacDAE.f90 +++ b/build/source/engine/eval8JacDAE.f90 @@ -118,9 +118,9 @@ subroutine eval8JacDAE(& ! -------------------------------------------------------------------------------------------------------------------------------- ! provide access to subroutines USE getVectorz_module, only:varExtract ! extract variables from the state vector - USE varExtrSundials_module, only:varExtractSundials + USE getVectorzAddSundials_module, only:varExtractSundials USE updateVarsSundials_module, only:updateVarsSundials ! update prognostic variables - USE computJacDAE_module,only:computJacDAE + USE computJacobSundials_module,only:computJacobSundials implicit none ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- @@ -299,10 +299,10 @@ subroutine eval8JacDAE(& ! compute the analytical Jacobian matrix ! NOTE: The derivatives were computed in the previous call to computFlux - ! This occurred either at the call to eval8DAE at the start of sysSolveSundials - ! or in the call to eval8DAE in the previous iteration + ! This occurred either at the call to eval8summaSundials at the start of sysSolveSundials + ! or in the call to eval8summaSundials in the previous iteration dt1 = 1._qp - call computJacDAE(& + call computJacobSundials(& ! input: model control cj, & ! intent(in): this scalar changes whenever the step size or method order changes dt1, & ! intent(in): length of the time step (seconds) diff --git a/build/source/engine/eval8DAE.f90 b/build/source/engine/eval8summaSundials.f90 similarity index 99% rename from build/source/engine/eval8DAE.f90 rename to build/source/engine/eval8summaSundials.f90 index 89a6dd7fa..ab2211537 100644 --- a/build/source/engine/eval8DAE.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -1,5 +1,5 @@ -module eval8DAE_module +module eval8summaSundials_module ! data types USE nrtype @@ -81,14 +81,14 @@ module eval8DAE_module implicit none private -public::eval8DAE +public::eval8summaSundials contains ! ********************************************************************************************************** - ! public subroutine eval8DAE: compute the residual vector + ! public subroutine eval8summaSundials: compute the residual vector ! ********************************************************************************************************** - subroutine eval8DAE(& + subroutine eval8summaSundials(& ! input: model control dt_cur, & ! intent(in): current stepsize dt, & ! intent(in): entire time step @@ -155,7 +155,7 @@ subroutine eval8DAE(& ! -------------------------------------------------------------------------------------------------------------------------------- ! provide access to subroutines USE getVectorz_module, only:varExtract ! extract variables from the state vector - USE varExtrSundials_module, only:varExtractSundials + USE getVectorzAddSundials_module, only:varExtractSundials USE updateVarsSundials_module, only:updateVarsSundials ! update variables USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy USE computFlux_module, only:soilCmpresSundials ! compute soil compression @@ -164,7 +164,7 @@ subroutine eval8DAE(& USE computHeatCap_module,only:computHeatCapAnalytic ! compute heat capacity USE computHeatCap_module,only:computCm USE computHeatCap_module, only:computStatMult - USE computResidDAE_module,only:computResidDAE ! compute residuals given a state vector + USE computResidSundials_module,only:computResidSundials ! compute residuals given a state vector USE computThermConduct_module,only:computThermConduct USE computEnthalpy_module,only:computEnthalpy USE computEnthalpy_module,only:computEnthalpyPrime @@ -317,7 +317,7 @@ subroutine eval8DAE(& ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="eval8DAE/" + err=0; message="eval8summaSundials/" feasible=.true. ! check the feasibility of the solution @@ -717,7 +717,7 @@ subroutine eval8DAE(& ! print *, 'dt_cur = ', dt_cur ! compute the residual vector - call computResidDAE(& + call computResidSundials(& ! input: model control nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -759,5 +759,5 @@ subroutine eval8DAE(& end associate - end subroutine eval8DAE -end module eval8DAE_module + end subroutine eval8summaSundials +end module eval8summaSundials_module diff --git a/build/source/engine/evalDAE4IDA.f90 b/build/source/engine/evalDAE4IDA.f90 index ed8fad968..72d1a3164 100644 --- a/build/source/engine/evalDAE4IDA.f90 +++ b/build/source/engine/evalDAE4IDA.f90 @@ -49,7 +49,7 @@ integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_da use fnvector_serial_mod use nrtype use type4IDA - use eval8DAE_module,only:eval8DAE + use eval8summaSundials_module,only:eval8summaSundials !======= Declarations ========= implicit none @@ -87,7 +87,7 @@ integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_da end if ! compute the flux and the residual vector for a given state vector - call eval8DAE(& + call eval8summaSundials(& ! input: model control stepsize_next(1), & ! intent(in): current stepsize eqns_data%dt, & ! intent(in): data step diff --git a/build/source/engine/varExtrSundials.f90 b/build/source/engine/getVectorzAddSundials.f90 similarity index 99% rename from build/source/engine/varExtrSundials.f90 rename to build/source/engine/getVectorzAddSundials.f90 index 4a6396308..6db88e775 100644 --- a/build/source/engine/varExtrSundials.f90 +++ b/build/source/engine/getVectorzAddSundials.f90 @@ -1,6 +1,6 @@ -module varExtrSundials_module +module getVectorzAddSundials_module ! data types USE nrtype @@ -379,4 +379,4 @@ end subroutine countDiscontinuity -end module varExtrSundials_module +end module getVectorzAddSundials_module diff --git a/build/source/engine/snowLiqFlx.f90 b/build/source/engine/snowLiqFlx.f90 index 6778561f8..091b629a1 100644 --- a/build/source/engine/snowLiqFlx.f90 +++ b/build/source/engine/snowLiqFlx.f90 @@ -160,7 +160,7 @@ subroutine snowLiqFlx(& ! define the liquid flux at the upper boundary (m s-1) iLayerLiqFluxSnow(0) = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water - iLayerLiqFluxSnowDeriv(0) = 0._rkind !computed inside computJacDAE_module + iLayerLiqFluxSnowDeriv(0) = 0._rkind !computed inside computJacobSundials_module ! compute properties fixed over the time step if(firstFluxCall)then diff --git a/build/source/engine/soil_utilsSundials.f90 b/build/source/engine/soil_utilsAddSundials.f90 similarity index 99% rename from build/source/engine/soil_utilsSundials.f90 rename to build/source/engine/soil_utilsAddSundials.f90 index b7c049020..04af5faaf 100644 --- a/build/source/engine/soil_utilsSundials.f90 +++ b/build/source/engine/soil_utilsAddSundials.f90 @@ -18,7 +18,7 @@ ! You should have received a copy of the GNU General Public License ! along with this program. If not, see . -module soil_utilsSundials_module +module soil_utilsAddSundials_module ! data types USE nrtype @@ -231,4 +231,4 @@ function d2Theta_dTk2(Tk,theta_res,theta_sat,alpha,n,m) end function d2Theta_dTk2 -end module soil_utilsSundials_module +end module soil_utilsAddSundials_module diff --git a/build/source/engine/solveByIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 similarity index 94% rename from build/source/engine/solveByIDA.f90 rename to build/source/engine/summaSolveSundialsIDA.f90 index 0385f19a9..f41edf4e8 100644 --- a/build/source/engine/solveByIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -1,7 +1,7 @@ -module solveByIDA_module +module summaSolveSundialsIDA_module !======= Inclusions =========== @@ -81,14 +81,14 @@ module solveByIDA_module implicit none private::setInitialCondition private::setSolverParams - public::solveByIDA + public::summaSolveSundialsIDA contains !------------------- - ! * public subroutine solveByIDA: solve F(y,y') = 0 by IDA (y is the state vector) + ! * public subroutine summaSolveSundialsIDA: solve F(y,y') = 0 by IDA (y is the state vector) ! ------------------ - subroutine solveByIDA( & + subroutine summaSolveSundialsIDA( & dt, & ! intent(in): data time step atol, & ! intent(in): absolute telerance rtol, & ! intent(in): relative tolerance @@ -146,7 +146,7 @@ subroutine solveByIDA( & USE evalDAE4IDA_module,only:evalDAE4IDA ! DAE/ODE functions USE evalJac4IDA_module,only:evalJac4IDA ! system Jacobian USE tol4IDA_module,only:computWeight4IDA ! weigth required for tolerances - USE eval8DAE_module,only:eval8DAE ! residual of DAE + USE eval8summaSundials_module,only:eval8summaSundials ! residual of DAE USE var_derive_module,only:calcHeight ! height at layer interfaces and layer mid-point !======= Declarations ========= @@ -231,11 +231,11 @@ subroutine solveByIDA( & ! ----------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="solveByIDA/" + err=0; message="summaSolveSundialsIDA/" nState = nStat idaSucceeds = .true. - ! fill eqns_data which will be required later to call eval8DAE + ! fill eqns_data which will be required later to call eval8summaSundials eqns_data%dt = dt eqns_data%nSnow = nSnow eqns_data%nSoil = nSoil @@ -312,31 +312,31 @@ subroutine solveByIDA( & ! create serial vectors sunvec_y => FN_VMake_Serial(nState, stateVec) - if (.not. associated(sunvec_y)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif + if (.not. associated(sunvec_y)) then; err=20; message='summaSolveSundialsIDA: sunvec = NULL'; return; endif sunvec_yp => FN_VMake_Serial(nState, stateVecPrime) - if (.not. associated(sunvec_yp)) then; err=20; message='solveByIDA: sunvec = NULL'; return; endif + if (.not. associated(sunvec_yp)) then; err=20; message='summaSolveSundialsIDA: sunvec = NULL'; return; endif ! Initialize solution vectors call setInitialCondition(nState, stateVecInit, sunvec_y, sunvec_yp) ! Create memory ida_mem = FIDACreate() - if (.not. c_associated(ida_mem)) then; err=20; message='solveByIDA: ida_mem = NULL'; return; endif + if (.not. c_associated(ida_mem)) then; err=20; message='summaSolveSundialsIDA: ida_mem = NULL'; return; endif ! Attach user data to memory eqns_data%ida_mem = ida_mem retval = FIDASetUserData(ida_mem, c_loc(eqns_data)) - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetUserData'; return; endif + if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetUserData'; return; endif ! Initialize memory t0 = 0._rkind retval = FIDAInit(ida_mem, c_funloc(evalDAE4IDA), t0, sunvec_y, sunvec_yp) - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAInit'; return; endif + if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDAInit'; return; endif ! set tolerances retval = FIDAWFtolerances(ida_mem, c_funloc(computWeight4IDA)) - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAWFtolerances'; return; endif + if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDAWFtolerances'; return; endif ! define the form of the matrix select case(ixMatrix) @@ -344,52 +344,52 @@ subroutine solveByIDA( & mu = ku; lu = kl; ! Create banded SUNMatrix for use in linear solves sunmat_A => FSUNBandMatrix(nState, mu, lu) - if (.not. associated(sunmat_A)) then; err=20; message='solveByIDA: sunmat = NULL'; return; endif + if (.not. associated(sunmat_A)) then; err=20; message='summaSolveSundialsIDA: sunmat = NULL'; return; endif ! Create banded SUNLinearSolver object sunlinsol_LS => FSUNLinSol_Band(sunvec_y, sunmat_A) - if (.not. associated(sunlinsol_LS)) then; err=20; message='solveByIDA: sunlinsol = NULL'; return; endif + if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolveSundialsIDA: sunlinsol = NULL'; return; endif case(ixFullMatrix) ! Create dense SUNMatrix for use in linear solves sunmat_A => FSUNDenseMatrix(nState, nState) - if (.not. associated(sunmat_A)) then; err=20; message='solveByIDA: sunmat = NULL'; return; endif + if (.not. associated(sunmat_A)) then; err=20; message='summaSolveSundialsIDA: sunmat = NULL'; return; endif ! Create dense SUNLinearSolver object sunlinsol_LS => FSUNDenseLinearSolver(sunvec_y, sunmat_A) - if (.not. associated(sunlinsol_LS)) then; err=20; message='solveByIDA: sunlinsol = NULL'; return; endif + if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolveSundialsIDA: sunlinsol = NULL'; return; endif ! check - case default; err=20; message='solveByIDA: error in type of matrix'; return + case default; err=20; message='summaSolveSundialsIDA: error in type of matrix'; return end select ! form of matrix ! Attach the matrix and linear solver retval = FIDASetLinearSolver(ida_mem, sunlinsol_LS, sunmat_A); - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetLinearSolver'; return; endif + if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetLinearSolver'; return; endif if(ixMatrix == ixFullMatrix)then ! Set the user-supplied Jacobian routine !comment this line out to use FD Jacobian retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetJacFn'; return; endif + if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetJacFn'; return; endif endif ! Create Newton SUNNonlinearSolver object sunnonlin_NLS => FSUNNonlinSol_Newton(sunvec_y) - if (.not. associated(sunnonlin_NLS)) then; err=20; message='solveByIDA: sunnonlinsol = NULL'; return; endif + if (.not. associated(sunnonlin_NLS)) then; err=20; message='summaSolveSundialsIDA: sunnonlinsol = NULL'; return; endif ! Attach the nonlinear solver retval = FIDASetNonlinearSolver(ida_mem, sunnonlin_NLS) - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetNonlinearSolver'; return; endif + if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetNonlinearSolver'; return; endif ! Enforce the solver to stop at end of the time step retval = FIDASetStopTime(ida_mem, dt) - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetStopTime'; return; endif + if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetStopTime'; return; endif ! Set solver parameters such as maximum order, number of iterations, ... call setSolverParams(dt, ida_mem, retval) - if (retval /= 0) then; err=20; message='solveByIDA: error in setSolverParams'; return; endif + if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in setSolverParams'; return; endif ! Disable error messages and warnings if(offErrWarnMessage) then @@ -440,7 +440,7 @@ subroutine solveByIDA( & retval = FIDAGetLastStep(ida_mem, dt_last) ! compute the flux and the residual vector for a given state vector - call eval8DAE(& + call eval8summaSundials(& ! input: model control dt_last(1), & ! intent(in): current stepsize eqns_data%dt, & ! intent(in): total data step @@ -576,7 +576,7 @@ subroutine solveByIDA( & call FN_VDestroy(sunvec_y) call FN_VDestroy(sunvec_yp) - end subroutine solveByIDA + end subroutine summaSolveSundialsIDA ! ---------------------------------------------------------------- ! SetInitialCondition: routine to initialize u and up vectors. @@ -739,4 +739,4 @@ subroutine implctMelt(& end subroutine implctMelt -end module solveByIDA_module +end module summaSolveSundialsIDA_module diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 1698889b2..854461994 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -130,11 +130,11 @@ subroutine systemSolvSundials(& USE allocspace_module,only:allocLocal ! allocate local data structures ! simulation of fluxes and residuals given a trial state vector USE eval8summa_module,only:eval8summa ! simulation of fluxes and residuals given a trial state vector - USE eval8DAE_module,only:eval8DAE + USE eval8summaSundials_module,only:eval8summaSundials USE getVectorz_module,only:getScaling ! get the scaling vectors USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy USE tol4IDA_module,only:popTol4IDA ! pop tolerances - USE solveByIDA_module,only:solveByIDA ! solve DAE by IDA + USE summaSolveSundialsIDA_module,only:summaSolveSundialsIDA ! solve DAE by IDA USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy use, intrinsic :: iso_c_binding implicit none @@ -465,7 +465,7 @@ subroutine systemSolvSundials(& ! initialize sum of compression of the soil matrix mLayerCmpress_sum(:) = 0._rkind - call solveByIDA(& + call summaSolveSundialsIDA(& dt, & ! intent (in) data time step atol, & ! intent (in) absolute telerance rtol, & ! intent (in) relative tolerance diff --git a/build/source/engine/updateVarsSundials.f90 b/build/source/engine/updateVarsSundials.f90 index ce17d861d..c45d639bd 100644 --- a/build/source/engine/updateVarsSundials.f90 +++ b/build/source/engine/updateVarsSundials.f90 @@ -90,9 +90,9 @@ module updateVarsSundials_module USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists -USE soil_utilsSundials_module,only:liquidHeadSundials ! compute the liquid water matric potential -USE soil_utilsSundials_module,only:d2Theta_dPsi2 -USE soil_utilsSundials_module,only:d2Theta_dTk2 +USE soil_utilsAddSundials_module,only:liquidHeadSundials ! compute the liquid water matric potential +USE soil_utilsAddSundials_module,only:d2Theta_dPsi2 +USE soil_utilsAddSundials_module,only:d2Theta_dTk2 ! IEEE checks USE, intrinsic :: ieee_arithmetic ! check values (NaN, etc.) diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index 42f690ac9..f5be83696 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -131,7 +131,7 @@ subroutine varSubstepSundials(& USE getVectorz_module,only:popStateVec ! populate the state vector USE getVectorz_module,only:varExtract ! extract variables from the state vector USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables - USE varExtrSundials_module,only:varExtractSundials + USE getVectorzAddSundials_module,only:varExtractSundials ! identify name of variable type (for error message) USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages USE systemSolvSundials_module,only:systemSolvSundials @@ -545,7 +545,7 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux waterBalanceError,nrgFluxModified,err,message) ! output: flags and error control USE getVectorz_module,only:varExtract ! extract variables from the state vector USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables - USE varExtrSundials_module, only:varExtractSundials + USE getVectorzAddSundials_module, only:varExtractSundials USE computEnthalpy_module,only:computEnthalpy USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy implicit none From 49a1e7d98e8dc4ec34e1aa8861d9ea08ec756220 Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 15 Sep 2022 21:47:23 +0000 Subject: [PATCH 0331/1472] added makefile to work with a container --- build/makefile | 416 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 416 insertions(+) create mode 100644 build/makefile diff --git a/build/makefile b/build/makefile new file mode 100644 index 000000000..d10a6cbfa --- /dev/null +++ b/build/makefile @@ -0,0 +1,416 @@ +#======================================================================== +# Makefile to compile SUMMA SUNDIALS +#======================================================================== +# +# Recommended use: Copy this file to Makefile.local, edit it to your +# heart's content, and then run `make -f build/Makefile.local` from +# your top level SUMMA directory. Don't include the Makefile.local in +# any pull requests you make. +# +# Note that Makefile configurations that we commonly use can be found on +# the SUMMA wiki at: +# https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration +# feel free to add yours to that page. +# +# To troubleshoot your paths and setup, type 'make check' +# +# At a minimum you will need to set the following: +# * F_MASTER - top level summa directory +# * FC - compiler suite +# * FC_EXE - compiler executable +# * INCLUDES - path to include files +# * ILDFLAGS - path to libraries to include +# * LIBRARIES - libraries to include +# * DIR_SUNDIALS - installation sundials directory +# +# Some further options can be specified for OpenMP, etc. See in Part 0 of +# the Makefile. You don't need to make any changes in PART 1 and +# following unless you are doing SUMMA development and changed what +# needs to be compiled + +#======================================================================== +# PART 0: User-configurable part +#======================================================================== + +# The variables can be specified in one of two ways: +# * delete the '##' in front of the variable, fill out the entry, +# save the file and run make +# * make no changes to this file, but specify the variables in your +# environment before you run make + +# Define core directory below which everything resides. This is the +# parent directory of the 'build' directory +F_MASTER =/SUMMA + +# Define the Fortran Compiler. If you are using gfortran, then this needs +# to be version 6 or higher. This variable is simply used to select the right +# compiler flags in the ifeq statements in this Makefile. The compiler +# executable is set separately as FC_EXE +# Currently this is either gfortran or ifort +FC = gfortran + +# Define the path for the compiler executable. This is the actual executable +# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin/gfortran-mp-11 +# FC and FC_EXE have to be consistent +FC_EXE = gfortran + +# Define the NetCDF and LAPACK libraries and path to include files. +# INCLUDES needs to be of the form (no quotes around the string): +# INCLUDES = -I -I -I<...> -I +# LIBRARIES needs to be of the form ( no quotes around the string): +# LIBRARIES = '-L -lnetcdff -L -lblas -L -l' +# If none of this makes sense, please talk to your system +# administrator. +INCLUDES = -I/usr/include -I/usr/local/include +LIBRARIES = -L/usr/lib -L/usr/local/lib -lnetcdff -lopenblas + +DIR_SUNDIALS=/code/sundials/instdir +INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran +LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod + +# Eventually we plan move to a real configure script, but for now we like +# to keep track of successful compilations of SUMMA on different platforms +# and with different compilers. If you are successful compiling SUMMA, +# please add your configuration (operating system and compiler plus +# part 0 of the Makefile) to the SUMMA wiki on github. + +# Define compiler flags. If you use a different compiler, +# you will need to figure out what the equivalent flags are +# and may need to update this section + +# ------------ define compiler flags ---------------------------------------- + +# define open MP flags +isOpenMP = +FLAGS_OMP = +LIBOPENMP = + +# Define compiler flags. If you use a different compiler, +# you will need to figure out what the equivalent flags are +# and may need to update this section + +# gfortran compiler flags +ifeq "$(FC)" "gfortran" + + ifeq "$(isOpenMP)" "yes" + FLAGS_OMP = -fopenmp + endif + +# Production runs +FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_COMM = -g -O0 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_SUMMA = -g -O0 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) + +# Debug runs +# FLAGS_NOAH = -p -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -Wall +# FLAGS_COMM = -p -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds +# FLAGS_SUMMA = -p -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds + +endif + +# ifort compiler flags +ifeq "$(FC)" "ifort" + + ifeq "$(isOpenMP)" "yes" + FLAGS_OMP = -qopenmp + endif + +# Production runs +FLAGS_NOAH = -O3 -noerror_limit -FR -auto -fltconsistency $(FLAGS_OMP) +FLAGS_COMM = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) +FLAGS_SUMMA = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) + +# Debug runs +#FLAGS_NOAH = -O0 -p -g -warn nounused -noerror_limit -FR -auto -WB -traceback -fltconsistency +#FLAGS_COMM = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 +#FLAGS_SUMMA = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 +endif + +#======================================================================== +# PART 1: Define directory paths +#======================================================================== + +# Core directory that contains source code +F_KORE_DIR = $(F_MASTER)/build/source + +# Location of the compiled modules +MOD_PATH = $(F_MASTER)/build + +# Define the directory for the executables +EXE_PATH = $(F_MASTER)/bin + +#======================================================================== +# PART 2: Assemble all of the SUMMA sub-routines +#======================================================================== + +# Define directories +DRIVER_DIR = $(F_KORE_DIR)/driver +HOOKUP_DIR = $(F_KORE_DIR)/hookup +NETCDF_DIR = $(F_KORE_DIR)/netcdf +DSHARE_DIR = $(F_KORE_DIR)/dshare +NUMREC_DIR = $(F_KORE_DIR)/numrec +NOAHMP_DIR = $(F_KORE_DIR)/noah-mp +ENGINE_DIR = $(F_KORE_DIR)/engine + +# utilities +SUMMA_NRUTIL= \ + nrtype.f90 \ + f2008funcs.f90 \ + nr_utility.f90 +NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) + +# +# Numerical recipes procedures +# NOTE: all numerical recipes procedures are now replaced with free versions +SUMMA_NRPROC= \ + expIntegral.f90 \ + spline_int.f90 +NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) + +# Hook-up modules (set files and directory paths) +SUMMA_HOOKUP= \ + ascii_util.f90 \ + summaFileManager.f90 + +HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) + +# Data modules +SUMMA_DATAMS= \ + multiconst.f90 \ + var_lookup.f90 \ + data_types.f90 \ + globalData.f90 \ + flxMapping.f90 \ + get_ixname.f90 \ + popMetadat.f90 \ + outpt_stat.f90 +DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) + +# utility modules +SUMMA_UTILMS= \ + time_utils.f90 \ + mDecisions.f90 \ + snow_utils.f90 \ + soil_utils.f90 \ + soil_utilsAddSundials.f90 \ + updatState.f90 \ + updatStateSundials.f90 \ + matrixOper.f90 +UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) + +# Model guts +SUMMA_MODGUT= \ + MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) + +# Solver +SUMMA_SOLVER= \ + vegPhenlgy.f90 \ + diagn_evar.f90 \ + stomResist.f90 \ + groundwatr.f90 \ + vegSWavRad.f90 \ + vegNrgFlux.f90 \ + ssdNrgFlux.f90 \ + vegLiqFlux.f90 \ + snowLiqFlx.f90 \ + soilLiqFlx.f90 \ + bigAquifer.f90 \ + computFlux.f90 \ + computResid.f90 \ + computJacob.f90 \ + eval8summa.f90 \ + summaSolve.f90 \ + systemSolv.f90 \ + type4IDA.f90 \ + tol4IDA.f90 \ + computEnthalpy.f90 \ + computHeatCap.f90 \ + computThermConduct.f90 \ + computResidSundials.f90 \ + eval8summaSundials.f90 \ + evalDAE4IDA.f90 \ + computJacobSundials.f90 \ + eval8JacDAE.f90 \ + evalJac4IDA.f90 \ + computSnowDepth.f90 \ + summaSolveSundialsIDA.f90 \ + systemSolvSundials.f90 \ + varSubstep.f90 \ + varSubstepSundials.f90 \ + opSplittin.f90 \ + coupled_em.f90 \ + run_oneHRU.f90 \ + run_oneGRU.f90 +SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) + +# Define routines for SUMMA preliminaries +SUMMA_PRELIM= \ + conv_funcs.f90 \ + sunGeomtry.f90 \ + convE2Temp.f90 \ + allocspace.f90 \ + checkStruc.f90 \ + childStruc.f90 \ + ffile_info.f90 \ + read_attrb.f90 \ + read_pinit.f90 \ + pOverwrite.f90 \ + read_param.f90 \ + paramCheck.f90 \ + check_icond.f90 +PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) + +SUMMA_NOAHMP= \ + module_model_constants.F \ + module_sf_noahutl.F \ + module_sf_noahlsm.F \ + module_sf_noahmplsm.F +NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) + +# Define routines for the SUMMA model runs +SUMMA_MODRUN = \ + indexState.f90 \ + getVectorz.f90 \ + getVectorzAddSundials.f90 \ + t2enthalpy.f90 \ + updateVars.f90 \ + updateVarsSundials.f90 \ + var_derive.f90 \ + read_force.f90 \ + derivforce.f90 \ + snowAlbedo.f90 \ + canopySnow.f90 \ + tempAdjust.f90 \ + snwCompact.f90 \ + layerMerge.f90 \ + layerDivide.f90 \ + volicePack.f90 \ + qTimeDelay.f90 +MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) + +# Define routines for the solver +SUMMA_MSOLVE = \ + +# Define NetCDF routines +SUMMA_NETCDF = \ + netcdf_util.f90 \ + def_output.f90 \ + modelwrite.f90 \ + read_icond.f90 +NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) + +# ... stitch together common programs +COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) + +# ... stitch together SUMMA programs +SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) + +# Define the driver routine +SUMMA_DRIVER= \ + summa_type.f90 \ + summa_util.f90 \ + summa_alarms.f90 \ + summa_globalData.f90 \ + summa_defineOutput.f90 \ + summa_init.f90 \ + summa_setup.f90 \ + summa_restart.f90 \ + summa_forcing.f90 \ + summa_modelRun.f90 \ + summa_writeOutput.f90 \ + summa_driver.f90 +DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) + +# Define the executable +DRIVER__EX = summa_sundials.exe + +# Define version number +VERSIONFILE = $(DRIVER_DIR)/summaversion.inc +VERSION = $(shell git tag | tail -n 1) +BULTTIM = $(shell date) +GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') +GITHASH = $(shell git rev-parse HEAD) + +#======================================================================== +# PART 3: Checks +#====================================================================== +# make sure that the paths are defined. These are just some high level checks +ifndef F_MASTER + $(error F_MASTER is undefined) +endif +ifndef FC + $(error FC is undefined: Specify your compiler) +endif +ifndef FC_EXE + $(error FC_EXE is undefined: Specify your compiler executable) +endif +ifndef FLAGS_SUMMA + $(error Specify flags for your compiler: $(FC)) +endif +ifndef INCLUDES + $(error INCLUDES is undefined) +endif +ifndef LIBRARIES + $(error LIBRARIES is undefined) +endif + +#======================================================================== +# PART 4: compilation +#====================================================================== + +# Compile +all: compile_noah compile_comm compile_summa link clean install +part: compile_noah compile_comm compile_summa + +check: + $(info) + $(info Displaying make variables:) + $(info F_MASTER : $(F_MASTER)) + $(info EXE_PATH : $(EXE_PATH)) + $(info FC : $(FC)) + $(info INCLUDES : $(INCLUDES)) + $(info LIBRARIES : $(LIBRARIES)) + $(info FLAGS_NOAH : $(FLAGS_NOAH)) + $(info FLAGS_COMM : $(FLAGS_COMM)) + $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) + $(info SUNDIALSDIR: $(DIR_SUNDIALS)) + $(info INC_SUNDIALS: $(INC_SUNDIALS)) + $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) + $(info) + +# update version information +update_version: + echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) + echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) + +# compile Noah-MP routines +compile_noah: + $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) + +# compile common routines + +compile_comm: + $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) + +# compile SUMMA routines +compile_summa: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ + $(INCLUDES) $(INC_SUNDIALS) + +# link routines +link: + $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) + +# Remove object files +clean: + rm -f *.o + rm -f *.mod + rm -f soil_veg_gen_parm__genmod.f90 + +# Copy the executable to the bin directory +install: + @mkdir -p $(EXE_PATH) + @mv $(DRIVER__EX) $(EXE_PATH) + $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) From 68c800d2fb5697b631710759511567f35f334dc9 Mon Sep 17 00:00:00 2001 From: Kyle Date: Fri, 16 Sep 2022 16:15:26 +0000 Subject: [PATCH 0332/1472] fixed Celia --- build/source/engine/computJacob.f90 | 25 +++++++++++++++------ build/source/engine/computJacobSundials.f90 | 16 ++++++------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 0d11c6da7..7aa89161c 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -514,12 +514,15 @@ subroutine computJacob(& end do ! (looping through hydrology states in the soil domain) ! - include terms for surface infiltration above surface - if(nSnowOnlyHyd>0 .and. ixSnowOnlyHyd(nSnow)/=integerMissing)then - if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) - elseif(computeVegFlux .and. ixVegHyd/=integerMissing)then !ixTopHyd = ixSoilOnlyHyd(1) - if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) + if(nSnowOnlyHyd>0)then !have snow above first soil layer + if(ixSnowOnlyHyd(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing)then + aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + endif + elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) + if(ixVegHyd/=integerMissing .and. ixTopHyd/=integerMissing)then + aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) + endif endif - endif ! (if the subset includes hydrology state variables in the soil domain) ! ----- @@ -622,8 +625,16 @@ subroutine computJacob(& elseif(computeVegFlux .and. ixVegNrg/=integerMissing) then !ixTopHyd = ixSoilOnlyHyd(1) if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) endif - - endif ! (if there are state variables for both water and energy in the soil domain) + if(nSnowOnlyHyd>0)then !have snow above first soil layer + if(ixSnowOnlyNrg(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing)then + aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + endif + elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) + if(ixVegNrg/=integerMissing .and. ixTopHyd/=integerMissing)then + aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) + endif + endif + endif ! (if there are state variables for both water and energy in the soil domain) if(globalPrintFlag)then print*, '** banded analytical Jacobian:' diff --git a/build/source/engine/computJacobSundials.f90 b/build/source/engine/computJacobSundials.f90 index 394346ea1..b825d8e72 100644 --- a/build/source/engine/computJacobSundials.f90 +++ b/build/source/engine/computJacobSundials.f90 @@ -568,10 +568,10 @@ subroutine computJacobSundials(& end do ! (looping through hydrology states in the soil domain) ! - include terms for surface infiltration above surface - if(nSnowOnlyHyd>0 .and. ixSnowOnlyHyd(nSnow)/=integerMissing)then - if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) - elseif(computeVegFlux .and. ixVegHyd/=integerMissing)then !ixTopHyd = ixSoilOnlyHyd(1) - if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegHyd) + if(nSnowOnlyHyd>0)then !have snow above first soil layer + if(ixSnowOnlyHyd(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) + if(ixVegHyd/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegHyd) endif endif ! (if the subset includes hydrology state variables in the soil domain) @@ -672,10 +672,10 @@ subroutine computJacobSundials(& end do ! (looping through soil layers) ! - include terms for surface infiltration above surface - if(nSnowOnlyHyd>0 .and. ixSnowOnlyNrg(nSnow)/=integerMissing)then - if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) - elseif(computeVegFlux .and. ixVegNrg/=integerMissing)then !ixTopHyd = ixSoilOnlyHyd(1) - if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegNrg) + if(nSnowOnlyHyd>0)then !have snow above first soil layer + if(ixSnowOnlyNrg(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) + if(ixVegNrg/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegNrg) endif endif ! (if there are state variables for both water and energy in the soil domain) From 22afc458ebb052985a7c51d56102bd1ce51f432b Mon Sep 17 00:00:00 2001 From: Kyle Date: Fri, 16 Sep 2022 22:44:11 +0000 Subject: [PATCH 0333/1472] fixed uninitalized value in ssdNrgFlux Error was caused by index into vector that was negative. Fixed by Checking index before accessing variable --- build/source/engine/ssdNrgFlux.f90 | 950 +++++++++++++++-------------- 1 file changed, 477 insertions(+), 473 deletions(-) diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index 1dcc8f2e4..da3e00b22 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -101,487 +101,491 @@ module ssdNrgFlux_module contains - ! ********************************************************************************************************** - ! public subroutine ssdNrgFlux: compute energy fluxes and derivatives at layer interfaces - ! ********************************************************************************************************** - subroutine ssdNrgFlux(& - ! input: model control - scalarSolution, & ! intent(in): flag to indicate the scalar solution - deriv_desired, & ! intent(in): flag indicating if derivatives are desired - ! input: fluxes and derivatives at the upper boundary - groundNetFlux, & ! intent(in): total flux at the ground surface (W m-2) - dGroundNetFlux_dGroundTemp, & ! intent(in): derivative in total ground surface flux w.r.t. ground temperature (W m-2 K-1) - ! input: liquid water fluxes - iLayerLiqFluxSnow, & ! intent(in): liquid flux at the interface of each snow layer (m s-1) - iLayerLiqFluxSoil, & ! intent(in): liquid flux at the interface of each soil layer (m s-1) - ! input: trial value of model state variables - mLayerTempTrial, & ! intent(in): trial temperature at the current iteration (K) - mLayerMatricHeadTrial, & ! intent(in): trial matric head at the current iteration(m) - mLayerVolFracLiqTrial, & ! intent(in): trial volumetric fraction of liquid water at the current iteration(-) - mLayerVolFracIceTrial, & ! intent(in): trial volumetric fraction of ice water at the current iteration(-) - ! input: pre-computed derivatives - mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - ! input-output: data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model indices - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output: fluxes and derivatives at all layer interfaces - iLayerNrgFlux, & ! intent(out): energy flux at the layer interfaces (W m-2) - dFlux_dTempAbove, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (W m-2 K-1) - dFlux_dTempBelow, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (W m-2 K-1) - dFlux_dWatAbove, & ! intent(out): derivatives in the flux w.r.t. water state in the layer above (W m-2 K-1) - dFlux_dWatBelow, & ! intent(out): derivatives in the flux w.r.t. water state in the layer below (W m-2 K-1) - ! output: error control - err,message) ! intent(out): error control - ! utility modules - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water - USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content - USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists - USE snow_utils_module,only:fracliquid ! compute fraction of liquid water at a given temperature - USE soil_utils_module,only:dTheta_dPsi ! compute derivative of the soil moisture characteristic w.r.t. psi (m-1) - USE soil_utils_module,only:dPsi_dTheta ! compute derivative of the soil moisture characteristic w.r.t. theta (m) - - ! constants - USE multiconst, only: gravity, & ! gravitational acceleration (m s-1) - Tfreeze, & ! freezing point of water (K) - iden_water,iden_ice,& ! intrinsic density of water and ice (kg m-3) - LH_fus ! latent heat of fusion (J kg-1) - ! ------------------------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input: model control - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - logical(lgt),intent(in) :: deriv_desired ! flag indicating if derivatives are desired - ! input: fluxes and derivatives at the upper boundary - real(rkind),intent(in) :: groundNetFlux ! net energy flux for the ground surface (W m-2) - real(rkind),intent(inout) :: dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - ! input: liquid water fluxes - real(rkind),intent(in) :: iLayerLiqFluxSnow(0:) ! liquid flux at the interface of each snow layer (m s-1) - real(rkind),intent(in) :: iLayerLiqFluxSoil(0:) ! liquid flux at the interface of each soil layer (m s-1) - ! input: trial model state variables - real(rkind),intent(in) :: mLayerTempTrial(:) ! temperature in each layer at the current iteration (m) - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! matric head in each layer at the current iteration (m) - real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! volumetric fraction of liquid at the current iteration (-) - real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! volumetric fraction of ice at the current iteration (-) -! input: pre-computed derivatives - real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative in volumetric liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) - ! input-output: data structures - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! state vector geometry - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - ! output: fluxes and derivatives at all layer interfaces - real(rkind),intent(out) :: iLayerNrgFlux(0:) ! energy flux at the layer interfaces (W m-2) - real(rkind),intent(out) :: dFlux_dTempAbove(0:) ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) - real(rkind),intent(out) :: dFlux_dTempBelow(0:) ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) - real(rkind),intent(out) :: dFlux_dWatAbove(0:) ! derivatives in the flux w.r.t. water state in the layer above (J m-2 s-1 K-1) - real(rkind),intent(out) :: dFlux_dWatBelow(0:) ! derivatives in the flux w.r.t. water state in the layer below (J m-2 s-1 K-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ------------------------------------------------------------------------------------------------------------------------------------------------------ - ! local variables - character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b) :: i,j,iLayer ! index of model layers - integer(i4b) :: ixLayerDesired(1) ! layer desired (scalar solution) - integer(i4b) :: ixTop ! top layer in subroutine call - integer(i4b) :: ixBot ! bottom layer in subroutine call - real(rkind) :: qFlux ! liquid flux at layer interfaces (m s-1) - real(rkind) :: dz ! height difference (m) - ! additional variables to compute numerical derivatives - integer(i4b) :: nFlux ! number of flux calculations required (>1 = numerical derivatives with one-sided finite differences) - integer(i4b) :: itry ! index of different flux calculations - integer(i4b),parameter :: unperturbed=0 ! named variable to identify the case of unperturbed state variables - integer(i4b),parameter :: perturbState=1 ! named variable to identify the case where we perturb the state in the current layer - integer(i4b),parameter :: perturbStateTempAbove=2 ! named variable to identify the case where we perturb the state layer above - integer(i4b),parameter :: perturbStateTempBelow=3 ! named variable to identify the case where we perturb the state layer below - integer(i4b),parameter :: perturbStateWatAbove=4 ! named variable to identify the case where we perturb the state layer above - integer(i4b),parameter :: perturbStateWatBelow=5 ! named variable to identify the case where we perturb the state layer below - integer(i4b) :: ixPerturb ! index of element in 2-element vector to perturb - integer(i4b) :: ixOriginal ! index of perturbed element in the original vector - real(rkind) :: scalarThermCFlux ! thermal conductivity (W m-1 K-1) - real(rkind) :: scalarThermCFlux_dTempAbove ! thermal conductivity with perturbation to the temperature state above (W m-1 K-1) - real(rkind) :: scalarThermCFlux_dTempBelow ! thermal conductivity with perturbation to the temperature state below (W m-1 K-1) - real(rkind) :: scalarThermCFlux_dWatAbove ! thermal conductivity with perturbation to the water state above - real(rkind) :: scalarThermCFlux_dWatBelow ! thermal conductivity with perturbation to the water state below - real(rkind) :: flux0,flux1,flux2 ! fluxes used to calculate derivatives (W m-2) - ! compute fluxes and derivatives at layer interfaces - integer(i4b),dimension(2) :: mLayer_ind ! indices of above and below layers - integer(i4b),dimension(2) :: iLayer_ind ! indices of above and below interfaces - real(rkind) :: matricFHead ! matric head for frozen soil - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - real(rkind) :: fLiq ! fraction of liquid water (-) - real(rkind),dimension(2) :: vectorMatricHeadTrial ! trial value of matric head (m) - real(rkind),dimension(2) :: vectorVolFracLiqTrial ! trial value of volumetric liquid content (-) - real(rkind),dimension(2) :: vectorVolFracIceTrial ! trial value of volumetric ice content (-) - real(rkind),dimension(2) :: vectorTempTrial ! trial value of temperature (K) - real(rkind),dimension(2) :: vectordTheta_dPsi ! derivative in the soil water characteristic w.r.t. psi (m-1) - real(rkind),dimension(2) :: vectordPsi_dTheta ! derivative in the soil water characteristic w.r.t. theta (m) - real(rkind),dimension(2) :: vectorFracLiqSnow ! fraction of liquid water (-) - real(rkind),dimension(2) :: vectortheta_sat ! layer above and below soil porosity (-) - real(rkind),dimension(2) :: vectoriden_soil ! layer above and below density of soil (kg m-3) - real(rkind),dimension(2) :: vectorthCond_soil ! layer above and below thermal conductivity of soil (W m-1 K-1) - real(rkind),dimension(2) :: vectorfrac_sand ! layer above and below fraction of sand (-) - real(rkind),dimension(2) :: vectorfrac_clay ! layer above and below fraction of clay (-) - ! recompute the perturbed version of iLayerThermalC, this could be the only version and remove the omputThermConduct_module - real(rkind) :: dThermalC_dHydStateAbove ! derivative in the thermal conductivity w.r.t. water state in the layer above - real(rkind) :: dThermalC_dHydStateBelow ! derivative in the thermal conductivity w.r.t. water state in the layer above - real(rkind) :: dThermalC_dNrgStateAbove ! derivative in the thermal conductivity w.r.t. energy state in the layer above - real(rkind) :: dThermalC_dNrgStateBelow ! derivative in the thermal conductivity w.r.t. energy state in the layer above - ! ------------------------------------------------------------------------------------------------------------------------------------------------------ - ! make association of local variables with information in the data structures - associate(& - ixDerivMethod => model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): method used to calculate flux derivatives - ix_bcUpprTdyn => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision, & ! intent(in): method used to calculate the upper boundary condition for thermodynamics - ix_bcLowrTdyn => model_decisions(iLookDECISIONS%bcLowrTdyn)%iDecision, & ! intent(in): method used to calculate the lower boundary condition for thermodynamics - ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision, & ! intent(in): index of the form of Richards' equation - ixThCondSnow => model_decisions(iLookDECISIONS%thCondSnow)%iDecision, & ! intent(in): choice of method for thermal conductivity of snow - ixThCondSoil => model_decisions(iLookDECISIONS%thCondSoil)%iDecision, & ! intent(in): choice of method for thermal conductivity of soil - ! input: model coordinates - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers - layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) - ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat, & ! intent(in): index in the state subset for energy state variables in the snow+soil domain - ! input: thermal properties - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): depth of each layer (m) - mLayerHeight => prog_data%var(iLookPROG%mLayerHeight)%dat, & ! intent(in): height at the mid-point of each layer (m) - upperBoundTemp => mpar_data%var(iLookPARAM%upperBoundTemp)%dat(1), & ! intent(in): temperature of the upper boundary (K) - lowerBoundTemp => mpar_data%var(iLookPARAM%lowerBoundTemp)%dat(1), & ! intent(in): temperature of the lower boundary (K) - iLayerHeight => prog_data%var(iLookPROG%iLayerHeight)%dat, & ! intent(in): height at the interface of each layer (m) - fixedThermalCond_snow => mpar_data%var(iLookPARAM%fixedThermalCond_snow)%dat(1), & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) - iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(inout): thermal conductivity at the interface of each layer (W m-1 K-1) - ! input: depth varying soil parameters - iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) - thCond_soil => mpar_data%var(iLookPARAM%thCond_soil)%dat, & ! intent(in): thermal conductivity of soil (W m-1 K-1) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): soil porosity (-) - frac_sand => mpar_data%var(iLookPARAM%frac_sand)%dat, & ! intent(in): fraction of sand (-) - frac_clay => mpar_data%var(iLookPARAM%frac_clay)%dat, & ! intent(in): fraction of clay (-) - vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat, & ! intent(in): [dp(:)] van Genutchen "m" parameter (-) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat, & ! intent(in): [dp(:)] van Genutchen "n" parameter (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat, & ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat, & ! intent(in): [dp(:)] soil residual volumetric water content (-) - ! input: snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ! output: diagnostic fluxes - iLayerConductiveFlux => flux_data%var(iLookFLUX%iLayerConductiveFlux)%dat, & ! intent(out): conductive energy flux at layer interfaces at end of time step (W m-2) - iLayerAdvectiveFlux => flux_data%var(iLookFLUX%iLayerAdvectiveFlux)%dat & ! intent(out): advective energy flux at layer interfaces at end of time step (W m-2) - ) ! association of local variables with information in the data structures - ! ------------------------------------------------------------------------------------------------------------------------------------------------------ - ! initialize error control - err=0; message='ssdNrgFlux/' - - ! set conductive and advective fluxes to missing in the upper boundary - ! NOTE: advective flux at the upper boundary is included in the ground heat flux - iLayerConductiveFlux(0) = realMissing - iLayerAdvectiveFlux(0) = realMissing - - ! check the need to compute numerical derivatives - if(ixDerivMethod==numerical)then - nFlux=5 ! compute the derivatives and cross derivates using one-sided finite differences - else - nFlux=0 ! compute analytical derivatives - end if - - ! get the indices for the snow+soil layers - if(scalarSolution)then - ixLayerDesired = pack(ixLayerState, ixSnowSoilNrg/=integerMissing) - ixTop = ixLayerDesired(1) - ixBot = ixLayerDesired(1) - else - ixTop = 0 !include layer 0 in layer interface derivatives - ixBot = nLayers - endif - - ! ------------------------------------------------------------------------------------------------------------------- - ! ***** compute the derivative in fluxes at layer interfaces w.r.t state in the layer above and the layer below ***** - ! ------------------------------------------------------------------------------------------------------------------- - - ! initialize un-used elements - ! ***** the upper boundary - dFlux_dTempAbove(0) = 0._rkind ! this will be in canopy - dFlux_dWatAbove(0) = 0._rkind ! this will be in canopy - - ! ***** the lower boundary - dFlux_dTempBelow(nLayers) = -huge(lowerBoundTemp) ! don't expect this to be used, so deliberately set to a ridiculous value to cause problems - dFlux_dWatBelow(nLayers) = -huge(lowerBoundTemp) ! don't expect this to be used, so deliberately set to a ridiculous value to cause problems - - ! loop through INTERFACES... - do iLayer=ixTop,ixBot - - ! either one or multiple flux calls, depending on if using analytical or numerical derivatives - do itry=nFlux,0,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) - - ! ===== - ! determine layer to perturb - ! ========================== - select case(itry) - ! skip undesired perturbations - case(perturbState); cycle ! perturbing the layers above and below the flux at the interface - ! identify the index for the perturbation - case(unperturbed); ixPerturb = 0 - case(perturbStateTempAbove) - if(iLayer==0) cycle ! cannot perturb state above (does not exist) -- so keep cycling - ixPerturb = 1 - case(perturbStateTempBelow) - if(iLayer==nLayers) cycle ! cannot perturb state below (does not exist) -- so keep cycling - ixPerturb = 2 - case(perturbStateWatAbove) - if(iLayer==0) cycle ! cannot perturb state above (does not exist) -- so keep cycling - ixPerturb = 3 - case(perturbStateWatBelow) - if(iLayer==nLayers) cycle ! cannot perturb state below (does not exist) -- so keep cycling - ixPerturb = 4 - case default; err=10; message=trim(message)//"unknown perturbation"; return - end select ! (identifying layer to of perturbation) - ! determine the index in the original vector - ixOriginal = iLayer + (ixPerturb-1) - - ! ===== - ! set indices and parameters needed for layer perturbation - ! ======================================================== - mLayer_ind(1) = iLayer - mLayer_ind(2) = iLayer+1 - if (iLayer==0 ) mLayer_ind(1) = 1 - if (iLayer==nLayers ) mLayer_ind(2) = nLayers - ! indices of interface are different at top layer since interface 0 exists - iLayer_ind = mLayer_ind - if (iLayer==0 ) iLayer_ind(1) = 0 - - ! ===== - ! get input state variables... - ! ============================ - ! start with the un-perturbed case - vectorVolFracLiqTrial(1:2) = mLayerVolFracLiqTrial(mLayer_ind) - vectorMatricHeadTrial(1:2) = mLayerMatricHeadTrial(mLayer_ind-nSnow) - vectorTempTrial(1:2) = mLayerTempTrial(mLayer_ind) - vectorVolFracIceTrial(1:2) = mLayerVolFracIceTrial(mLayer_ind) - ! make appropriate perturbations, - if(ixPerturb > 2)then - vectorMatricHeadTrial(ixPerturb-2) = vectorMatricHeadTrial(ixPerturb-2) + dx - vectorVolFracLiqTrial(ixPerturb-2) = vectorVolFracLiqTrial(ixPerturb-2) + dx - else if(ixPerturb > 0)then - vectorTempTrial(ixPerturb) = vectorTempTrial(ixPerturb) + dx - endif - - ! ***** - ! * compute the volumetric fraction of liquid, ice, and air in each layer in response to perturbation ... - ! ******************************************************************************************************* - - do i = 1,2 !(layer above and below) - select case(layerType(mLayer_ind(i))) !(snow or soil) - - case(iname_soil) - j = mLayer_ind(i)-nSnow !soil layer - if(ixPerturb > 0)then ! only recompute these if perturbed - select case(ixRichards) ! (form of Richards' equation) - case(moisture) ! - vectorMatricHeadTrial(i) = matricHead(vectorVolFracLiqTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - Tcrit = crit_soilT(vectorMatricHeadTrial(i)) - !if change temp and below critical, it changes the state variable, seems like a problem FIX - if(vectorTempTrial(i) < Tcrit) then !if do not perturb temperature, this should not change - matricFHead = (LH_fus/gravity)*(vectorTempTrial(i) - Tfreeze)/Tfreeze - vectorVolFracLiqTrial(i) = volFracLiq(matricFHead,vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - endif - case(mixdform) - Tcrit = crit_soilT(vectorMatricHeadTrial(i)) - if(vectorTempTrial(i) < Tcrit) then !if do not perturb temperature, this should not change, but matricHeadTrial will have changed - matricFHead = (LH_fus/gravity)*(vectorTempTrial(i) - Tfreeze)/Tfreeze - vectorVolFracLiqTrial(i) = volFracLiq(matricFHead,vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - else - vectorVolFracLiqTrial(i) = volFracLiq(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - endif - end select ! (form of Richards' equation) - vectorVolFracIceTrial(i) = volFracLiq(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - vectorVolFracLiqTrial(i) - endif ! (recompute if perturbed) - ! derivatives, these need to be computed because they are computed only in soilLiqFlx which is called after this - vectordPsi_dTheta(i) = dPsi_dTheta(vectorVolFracLiqTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - vectordTheta_dPsi(i) = dTheta_dPsi(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - vectorFracLiqSnow(i) = realMissing - ! soil parameters - vectortheta_sat(i) = theta_sat(j) - vectoriden_soil(i) = iden_soil(j) - vectorthCond_soil(i) = thCond_soil(j) - vectorfrac_sand(i) = frac_sand(j) - vectorfrac_clay(i) = frac_clay(j) - - case(iname_snow) - fLiq = fracliquid(vectorTempTrial(i),snowfrz_scale) ! fraction of liquid water - if(ixPerturb > 0) vectorVolFracIceTrial(i) = ( vectorVolFracLiqTrial(i) / fLiq - vectorVolFracLiqTrial(i) )*(iden_water/iden_ice) ! use perturbed nodeVolTotWatTrial - ! derivatives - vectordPsi_dTheta(i) = realMissing - vectordTheta_dPsi(i) = realMissing - vectorFracLiqSnow(i) = mLayerFracLiqSnow(mLayer_ind(i)) - ! soil parameters do not exist - vectortheta_sat(i) = realMissing - vectoriden_soil(i) = realMissing - vectorthCond_soil(i) = realMissing - vectorfrac_sand(i) = realMissing - vectorfrac_clay(i) = realMissing - - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute volumetric fraction of air'; return - - end select !(snow or soil) - enddo !(layer above and below) - - ! ===== - ! get thermal conductivity at layer interface and its derivative w.r.t. the state above and the state below... - ! ============================================================================================================ - call iLayerThermalConduct(& - ! input: model control - deriv_desired, & ! intent(in): flag indicating if derivatives are desired - ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) - ixThCondSnow, & ! intent(in): choice of method for thermal conductivity of snow - ixThCondSoil, & ! intent(in): choice of method for thermal conductivity of soil - ! input: coordinate variables - nLayers, & ! intent(in): number of layers - iLayer, & ! intent(in): layer index for output - layerType(mLayer_ind), & ! intent(in): layer type (iname_soil or iname_snow) - ! input: state variables (adjacent layers) - vectorMatricHeadTrial, & ! intent(in): matric head at the nodes (m) - vectorVolFracLiqTrial, & ! intent(in): volumetric liquid water at the nodes (m) - vectorVolFracIceTrial, & ! intent(in): volumetric ice at the nodes (m) - vectorTempTrial, & ! intent(in): temperature at the nodes (m) - ! input: pre-computed derivatives - mLayerdTheta_dTk(mLayer_ind), & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - vectorFracLiqSnow, & ! intent(in): fraction of liquid water (-) - vectordTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) - vectordPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) - ! input: model coordinate variables (adjacent layers) - mLayerHeight(mLayer_ind), & ! intent(in): height at the mid-point of the node (m) - iLayerHeight(iLayer_ind), & ! intent(in): height at the interface of the nodes (m) - ! input: soil parameters - vectortheta_sat, & ! intent(in): soil porosity (-) - vectoriden_soil, & ! intent(in): intrinsic density of soil (kg m-3) - vectorthCond_soil, & ! intent(in): thermal conductivity of soil (W m-1 K-1) - vectorfrac_sand, & ! intent(in): fraction of sand (-) - vectorfrac_clay, & ! intent(in): fraction of clay (-) - ! input: snow parameters - fixedThermalCond_snow, & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) - ! output: conductivity at the layer interface (scalars) - iLayerThermalC(iLayer), & ! intent(inout): thermal conductivity at the interface of each layer (W m-1 K-1) - ! output: derivatives in thermal conductivity w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below - dThermalC_dHydStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dHydStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above - ! output: derivatives in thermal conductivity w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (W m-1 K-2) - dThermalC_dNrgStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dNrgStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above - ! output: error control - err,cmessage) ! intent(out): error control - - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - ! compute total vertical flux, to compute derivatives - if(deriv_desired .and. ixDerivMethod==numerical)then - select case(itry) - case(unperturbed); scalarThermCFlux = iLayerThermalC(iLayer) - case(perturbStateTempAbove); scalarThermCFlux_dTempAbove = iLayerThermalC(iLayer) - case(perturbStateTempBelow); scalarThermCFlux_dTempBelow = iLayerThermalC(iLayer) - case(perturbStateWatAbove); scalarThermCFlux_dWatAbove = iLayerThermalC(iLayer) - case(perturbStateWatBelow); scalarThermCFlux_dWatBelow = iLayerThermalC(iLayer) - case default; err=10; message=trim(message)//"unknown perturbation"; return - end select - end if +! ********************************************************************************************************** +! public subroutine ssdNrgFlux: compute energy fluxes and derivatives at layer interfaces +! ********************************************************************************************************** +subroutine ssdNrgFlux(& + ! input: model control + scalarSolution, & ! intent(in): flag to indicate the scalar solution + deriv_desired, & ! intent(in): flag indicating if derivatives are desired + ! input: fluxes and derivatives at the upper boundary + groundNetFlux, & ! intent(in): total flux at the ground surface (W m-2) + dGroundNetFlux_dGroundTemp, & ! intent(in): derivative in total ground surface flux w.r.t. ground temperature (W m-2 K-1) + ! input: liquid water fluxes + iLayerLiqFluxSnow, & ! intent(in): liquid flux at the interface of each snow layer (m s-1) + iLayerLiqFluxSoil, & ! intent(in): liquid flux at the interface of each soil layer (m s-1) + ! input: trial value of model state variables + mLayerTempTrial, & ! intent(in): trial temperature at the current iteration (K) + mLayerMatricHeadTrial, & ! intent(in): trial matric head at the current iteration(m) + mLayerVolFracLiqTrial, & ! intent(in): trial volumetric fraction of liquid water at the current iteration(-) + mLayerVolFracIceTrial, & ! intent(in): trial volumetric fraction of ice water at the current iteration(-) + ! input: pre-computed derivatives + mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + ! input-output: data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model indices + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output: fluxes and derivatives at all layer interfaces + iLayerNrgFlux, & ! intent(out): energy flux at the layer interfaces (W m-2) + dFlux_dTempAbove, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (W m-2 K-1) + dFlux_dTempBelow, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (W m-2 K-1) + dFlux_dWatAbove, & ! intent(out): derivatives in the flux w.r.t. water state in the layer above (W m-2 K-1) + dFlux_dWatBelow, & ! intent(out): derivatives in the flux w.r.t. water state in the layer below (W m-2 K-1) + ! output: error control + err,message) ! intent(out): error control + ! utility modules + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water + USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + USE snow_utils_module,only:fracliquid ! compute fraction of liquid water at a given temperature + USE soil_utils_module,only:dTheta_dPsi ! compute derivative of the soil moisture characteristic w.r.t. psi (m-1) + USE soil_utils_module,only:dPsi_dTheta ! compute derivative of the soil moisture characteristic w.r.t. theta (m) + + ! constants + USE multiconst, only: gravity, & ! gravitational acceleration (m s-1) + Tfreeze, & ! freezing point of water (K) + iden_water,iden_ice,& ! intrinsic density of water and ice (kg m-3) + LH_fus ! latent heat of fusion (J kg-1) + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input: model control + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + logical(lgt),intent(in) :: deriv_desired ! flag indicating if derivatives are desired + ! input: fluxes and derivatives at the upper boundary + real(rkind),intent(in) :: groundNetFlux ! net energy flux for the ground surface (W m-2) + real(rkind),intent(inout) :: dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + ! input: liquid water fluxes + real(rkind),intent(in) :: iLayerLiqFluxSnow(0:) ! liquid flux at the interface of each snow layer (m s-1) + real(rkind),intent(in) :: iLayerLiqFluxSoil(0:) ! liquid flux at the interface of each soil layer (m s-1) + ! input: trial model state variables + real(rkind),intent(in) :: mLayerTempTrial(:) ! temperature in each layer at the current iteration (m) + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! matric head in each layer at the current iteration (m) + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! volumetric fraction of liquid at the current iteration (-) + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! volumetric fraction of ice at the current iteration (-) + ! input: pre-computed derivatives + real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative in volumetric liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) + ! input-output: data structures + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! state vector geometry + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + ! output: fluxes and derivatives at all layer interfaces + real(rkind),intent(out) :: iLayerNrgFlux(0:) ! energy flux at the layer interfaces (W m-2) + real(rkind),intent(out) :: dFlux_dTempAbove(0:) ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) + real(rkind),intent(out) :: dFlux_dTempBelow(0:) ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) + real(rkind),intent(out) :: dFlux_dWatAbove(0:) ! derivatives in the flux w.r.t. water state in the layer above (J m-2 s-1 K-1) + real(rkind),intent(out) :: dFlux_dWatBelow(0:) ! derivatives in the flux w.r.t. water state in the layer below (J m-2 s-1 K-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------------------------------------------------------------ + ! local variables + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: i,j,iLayer ! index of model layers + integer(i4b) :: ixLayerDesired(1) ! layer desired (scalar solution) + integer(i4b) :: ixTop ! top layer in subroutine call + integer(i4b) :: ixBot ! bottom layer in subroutine call + real(rkind) :: qFlux ! liquid flux at layer interfaces (m s-1) + real(rkind) :: dz ! height difference (m) + ! additional variables to compute numerical derivatives + integer(i4b) :: nFlux ! number of flux calculations required (>1 = numerical derivatives with one-sided finite differences) + integer(i4b) :: itry ! index of different flux calculations + integer(i4b),parameter :: unperturbed=0 ! named variable to identify the case of unperturbed state variables + integer(i4b),parameter :: perturbState=1 ! named variable to identify the case where we perturb the state in the current layer + integer(i4b),parameter :: perturbStateTempAbove=2 ! named variable to identify the case where we perturb the state layer above + integer(i4b),parameter :: perturbStateTempBelow=3 ! named variable to identify the case where we perturb the state layer below + integer(i4b),parameter :: perturbStateWatAbove=4 ! named variable to identify the case where we perturb the state layer above + integer(i4b),parameter :: perturbStateWatBelow=5 ! named variable to identify the case where we perturb the state layer below + integer(i4b) :: ixPerturb ! index of element in 2-element vector to perturb + integer(i4b) :: ixOriginal ! index of perturbed element in the original vector + real(rkind) :: scalarThermCFlux ! thermal conductivity (W m-1 K-1) + real(rkind) :: scalarThermCFlux_dTempAbove ! thermal conductivity with perturbation to the temperature state above (W m-1 K-1) + real(rkind) :: scalarThermCFlux_dTempBelow ! thermal conductivity with perturbation to the temperature state below (W m-1 K-1) + real(rkind) :: scalarThermCFlux_dWatAbove ! thermal conductivity with perturbation to the water state above + real(rkind) :: scalarThermCFlux_dWatBelow ! thermal conductivity with perturbation to the water state below + real(rkind) :: flux0,flux1,flux2 ! fluxes used to calculate derivatives (W m-2) + ! compute fluxes and derivatives at layer interfaces + integer(i4b),dimension(2) :: mLayer_ind ! indices of above and below layers + integer(i4b),dimension(2) :: iLayer_ind ! indices of above and below interfaces + real(rkind) :: matricFHead ! matric head for frozen soil + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind) :: fLiq ! fraction of liquid water (-) + real(rkind),dimension(2) :: vectorMatricHeadTrial ! trial value of matric head (m) + real(rkind),dimension(2) :: vectorVolFracLiqTrial ! trial value of volumetric liquid content (-) + real(rkind),dimension(2) :: vectorVolFracIceTrial ! trial value of volumetric ice content (-) + real(rkind),dimension(2) :: vectorTempTrial ! trial value of temperature (K) + real(rkind),dimension(2) :: vectordTheta_dPsi ! derivative in the soil water characteristic w.r.t. psi (m-1) + real(rkind),dimension(2) :: vectordPsi_dTheta ! derivative in the soil water characteristic w.r.t. theta (m) + real(rkind),dimension(2) :: vectorFracLiqSnow ! fraction of liquid water (-) + real(rkind),dimension(2) :: vectortheta_sat ! layer above and below soil porosity (-) + real(rkind),dimension(2) :: vectoriden_soil ! layer above and below density of soil (kg m-3) + real(rkind),dimension(2) :: vectorthCond_soil ! layer above and below thermal conductivity of soil (W m-1 K-1) + real(rkind),dimension(2) :: vectorfrac_sand ! layer above and below fraction of sand (-) + real(rkind),dimension(2) :: vectorfrac_clay ! layer above and below fraction of clay (-) + ! recompute the perturbed version of iLayerThermalC, this could be the only version and remove the omputThermConduct_module + real(rkind) :: dThermalC_dHydStateAbove ! derivative in the thermal conductivity w.r.t. water state in the layer above + real(rkind) :: dThermalC_dHydStateBelow ! derivative in the thermal conductivity w.r.t. water state in the layer above + real(rkind) :: dThermalC_dNrgStateAbove ! derivative in the thermal conductivity w.r.t. energy state in the layer above + real(rkind) :: dThermalC_dNrgStateBelow ! derivative in the thermal conductivity w.r.t. energy state in the layer above + ! ------------------------------------------------------------------------------------------------------------------------------------------------------ + ! make association of local variables with information in the data structures + associate(& + ixDerivMethod => model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): method used to calculate flux derivatives + ix_bcUpprTdyn => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision, & ! intent(in): method used to calculate the upper boundary condition for thermodynamics + ix_bcLowrTdyn => model_decisions(iLookDECISIONS%bcLowrTdyn)%iDecision, & ! intent(in): method used to calculate the lower boundary condition for thermodynamics + ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision, & ! intent(in): index of the form of Richards' equation + ixThCondSnow => model_decisions(iLookDECISIONS%thCondSnow)%iDecision, & ! intent(in): choice of method for thermal conductivity of snow + ixThCondSoil => model_decisions(iLookDECISIONS%thCondSoil)%iDecision, & ! intent(in): choice of method for thermal conductivity of soil + ! input: model coordinates + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers + layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) + ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat, & ! intent(in): index in the state subset for energy state variables in the snow+soil domain + ! input: thermal properties + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): depth of each layer (m) + mLayerHeight => prog_data%var(iLookPROG%mLayerHeight)%dat, & ! intent(in): height at the mid-point of each layer (m) + upperBoundTemp => mpar_data%var(iLookPARAM%upperBoundTemp)%dat(1), & ! intent(in): temperature of the upper boundary (K) + lowerBoundTemp => mpar_data%var(iLookPARAM%lowerBoundTemp)%dat(1), & ! intent(in): temperature of the lower boundary (K) + iLayerHeight => prog_data%var(iLookPROG%iLayerHeight)%dat, & ! intent(in): height at the interface of each layer (m) + fixedThermalCond_snow => mpar_data%var(iLookPARAM%fixedThermalCond_snow)%dat(1), & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) + iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(inout): thermal conductivity at the interface of each layer (W m-1 K-1) + ! input: depth varying soil parameters + iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) + thCond_soil => mpar_data%var(iLookPARAM%thCond_soil)%dat, & ! intent(in): thermal conductivity of soil (W m-1 K-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): soil porosity (-) + frac_sand => mpar_data%var(iLookPARAM%frac_sand)%dat, & ! intent(in): fraction of sand (-) + frac_clay => mpar_data%var(iLookPARAM%frac_clay)%dat, & ! intent(in): fraction of clay (-) + vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat, & ! intent(in): [dp(:)] van Genutchen "m" parameter (-) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat, & ! intent(in): [dp(:)] van Genutchen "n" parameter (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat, & ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat, & ! intent(in): [dp(:)] soil residual volumetric water content (-) + ! input: snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ! output: diagnostic fluxes + iLayerConductiveFlux => flux_data%var(iLookFLUX%iLayerConductiveFlux)%dat, & ! intent(out): conductive energy flux at layer interfaces at end of time step (W m-2) + iLayerAdvectiveFlux => flux_data%var(iLookFLUX%iLayerAdvectiveFlux)%dat & ! intent(out): advective energy flux at layer interfaces at end of time step (W m-2) + ) ! association of local variables with information in the data structures + ! ------------------------------------------------------------------------------------------------------------------------------------------------------ + ! initialize error control + err=0; message='ssdNrgFlux/' + + ! set conductive and advective fluxes to missing in the upper boundary + ! NOTE: advective flux at the upper boundary is included in the ground heat flux + iLayerConductiveFlux(0) = realMissing + iLayerAdvectiveFlux(0) = realMissing + + ! check the need to compute numerical derivatives + if(ixDerivMethod==numerical)then + nFlux=5 ! compute the derivatives and cross derivates using one-sided finite differences + else + nFlux=0 ! compute analytical derivatives + end if - end do ! (looping through different flux calculations -- one or multiple calls depending if desire for numerical or analytical derivatives) + ! get the indices for the snow+soil layers + if(scalarSolution)then + ixLayerDesired = pack(ixLayerState, ixSnowSoilNrg/=integerMissing) + ixTop = ixLayerDesired(1) + ixBot = ixLayerDesired(1) + else + ixTop = 0 !include layer 0 in layer interface derivatives + ixBot = nLayers + endif + ! ------------------------------------------------------------------------------------------------------------------- + ! ***** compute the derivative in fluxes at layer interfaces w.r.t state in the layer above and the layer below ***** + ! ------------------------------------------------------------------------------------------------------------------- + ! initialize un-used elements ! ***** the upper boundary - if(iLayer==0)then ! (upper boundary) - - ! identify the upper boundary condition - select case(ix_bcUpprTdyn) - - ! * prescribed temperature at the upper boundary - case(prescribedTemp) - dz = (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) - if(ixDerivMethod==analytical)then ! ** analytical derivatives - dFlux_dWatBelow(iLayer) = -dThermalC_dHydStateBelow * ( mLayerTempTrial(iLayer+1) - upperBoundTemp )/dz - dFlux_dTempBelow(iLayer) = -dThermalC_dNrgStateBelow * ( mLayerTempTrial(iLayer+1) - upperBoundTemp )/dz - iLayerThermalC(iLayer)/dz - else ! ** numerical derivatives - flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - upperBoundTemp ) / dz - flux2 = -scalarThermCFlux_dWatBelow*( mLayerTempTrial(iLayer+1) - upperBoundTemp ) / dz - dFlux_dWatBelow(iLayer) = (flux2 - flux0)/dx - flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - upperBoundTemp ) / dz - flux2 = -scalarThermCFlux_dTempBelow*((mLayerTempTrial(iLayer+1)+dx) - upperBoundTemp ) / dz - dFlux_dTempBelow(iLayer) = (flux2 - flux0)/dx - end if - - ! * zero flux at the upper boundary - case(zeroFlux) - dFlux_dWatBelow(iLayer) = 0._rkind - dFlux_dTempBelow(iLayer) = 0._rkind - - ! * compute flux inside vegetation energy flux routine, use here - case(energyFlux) - dFlux_dWatBelow(iLayer) = 0._rkind !dGroundNetFlux_dGroundWat, does not exist in vegNrgFlux - dFlux_dTempBelow(iLayer) = dGroundNetFlux_dGroundTemp - - case default; err=20; message=trim(message)//'unable to identify upper boundary condition for thermodynamics'; return - - end select ! (identifying the upper boundary condition for thermodynamics) - !dGroundNetFlux_dGroundWat = dFlux_dWatBelow(iLayer) ! this is true, but since not used in vegNrgFlux do not define - dGroundNetFlux_dGroundTemp = dFlux_dTempBelow(iLayer) ! need this in vegNrgFlux + dFlux_dTempAbove(0) = 0._rkind ! this will be in canopy + dFlux_dWatAbove(0) = 0._rkind ! this will be in canopy ! ***** the lower boundary - else if(iLayer==nLayers)then ! (lower boundary) - - ! identify the lower boundary condition - select case(ix_bcLowrTdyn) - - ! * prescribed temperature at the lower boundary - case(prescribedTemp) - dz = mLayerDepth(iLayer)*0.5_rkind - if(ixDerivMethod==analytical)then ! ** analytical derivatives - dFlux_dWatAbove(iLayer) = -dThermalC_dHydStateAbove * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz - dFlux_dTempAbove(iLayer) = -dThermalC_dNrgStateAbove * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + iLayerThermalC(iLayer)/dz - else ! ** numerical derivatives - flux0 = -scalarThermCFlux * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz - flux1 = -scalarThermCFlux_dWatAbove * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz - dFlux_dWatAbove(iLayer) = (flux1 - flux0)/dx - flux0 = -scalarThermCFlux * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz - flux1 = -scalarThermCFlux_dTempAbove * ( lowerBoundTemp - (mLayerTempTrial(iLayer)+dx) )/dz - dFlux_dTempAbove(iLayer) = (flux1 - flux0)/dx - end if - - ! * zero flux at the lower boundary - case(zeroFlux) - dFlux_dWatAbove(iLayer) = 0._rkind - dFlux_dTempAbove(iLayer) = 0._rkind - - case default; err=20; message=trim(message)//'unable to identify lower boundary condition for thermodynamics'; return - - end select ! (identifying the lower boundary condition for thermodynamics) - - ! ***** internal layers - - else - dz = (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) - if(ixDerivMethod==analytical)then ! ** analytical derivatives - dFlux_dWatAbove(iLayer) = -dThermalC_dHydStateAbove * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz - dFlux_dWatBelow(iLayer) = -dThermalC_dHydStateBelow * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz - dFlux_dTempAbove(iLayer) = -dThermalC_dNrgStateAbove * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz + iLayerThermalC(iLayer)/dz - dFlux_dTempBelow(iLayer) = -dThermalC_dNrgStateBelow * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz - iLayerThermalC(iLayer)/dz - else ! ** numerical derivatives - flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz - flux1 = -scalarThermCFlux_dWatAbove*( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz - flux2 = -scalarThermCFlux_dWatBelow*( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz - dFlux_dWatAbove(iLayer) = (flux1 - flux0)/dx - dFlux_dWatBelow(iLayer) = (flux2 - flux0)/dx - flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz - flux1 = -scalarThermCFlux_dTempAbove*( mLayerTempTrial(iLayer+1) - (mLayerTempTrial(iLayer)+dx)) / dz - flux2 = -scalarThermCFlux_dTempBelow*((mLayerTempTrial(iLayer+1)+dx) - mLayerTempTrial(iLayer) ) / dz - dFlux_dTempAbove(iLayer) = (flux1 - flux0)/dx - dFlux_dTempBelow(iLayer) = (flux2 - flux0)/dx - end if + dFlux_dTempBelow(nLayers) = -huge(lowerBoundTemp) ! don't expect this to be used, so deliberately set to a ridiculous value to cause problems + dFlux_dWatBelow(nLayers) = -huge(lowerBoundTemp) ! don't expect this to be used, so deliberately set to a ridiculous value to cause problems + + ! loop through INTERFACES... + do iLayer=ixTop,ixBot + + ! either one or multiple flux calls, depending on if using analytical or numerical derivatives + do itry=nFlux,0,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) + + ! ===== + ! determine layer to perturb + ! ========================== + select case(itry) + ! skip undesired perturbations + case(perturbState); cycle ! perturbing the layers above and below the flux at the interface + ! identify the index for the perturbation + case(unperturbed); ixPerturb = 0 + case(perturbStateTempAbove) + if(iLayer==0) cycle ! cannot perturb state above (does not exist) -- so keep cycling + ixPerturb = 1 + case(perturbStateTempBelow) + if(iLayer==nLayers) cycle ! cannot perturb state below (does not exist) -- so keep cycling + ixPerturb = 2 + case(perturbStateWatAbove) + if(iLayer==0) cycle ! cannot perturb state above (does not exist) -- so keep cycling + ixPerturb = 3 + case(perturbStateWatBelow) + if(iLayer==nLayers) cycle ! cannot perturb state below (does not exist) -- so keep cycling + ixPerturb = 4 + case default; err=10; message=trim(message)//"unknown perturbation"; return + end select ! (identifying layer to of perturbation) + ! determine the index in the original vector + ixOriginal = iLayer + (ixPerturb-1) + + ! ===== + ! set indices and parameters needed for layer perturbation + ! ======================================================== + mLayer_ind(1) = iLayer + mLayer_ind(2) = iLayer+1 + if (iLayer==0 ) mLayer_ind(1) = 1 + if (iLayer==nLayers ) mLayer_ind(2) = nLayers + ! indices of interface are different at top layer since interface 0 exists + iLayer_ind = mLayer_ind + if (iLayer==0 ) iLayer_ind(1) = 0 + + ! ===== + ! get input state variables... + ! ============================ + ! start with the un-perturbed case + vectorVolFracLiqTrial(1:2) = mLayerVolFracLiqTrial(mLayer_ind) + ! need to protect against negative indexes + if ( mLayer_ind(1) .ge. nSnow .and. mLayer_ind(2) .ge. nSnow)then + if (iLayer==nSnow ) mLayer_ind(1) = nsnow+1 + vectorMatricHeadTrial(1:2) = mLayerMatricHeadTrial(mLayer_ind-nSnow) + end if + vectorTempTrial(1:2) = mLayerTempTrial(mLayer_ind) + vectorVolFracIceTrial(1:2) = mLayerVolFracIceTrial(mLayer_ind) + ! make appropriate perturbations, + if(ixPerturb > 2)then + vectorMatricHeadTrial(ixPerturb-2) = vectorMatricHeadTrial(ixPerturb-2) + dx + vectorVolFracLiqTrial(ixPerturb-2) = vectorVolFracLiqTrial(ixPerturb-2) + dx + else if(ixPerturb > 0)then + vectorTempTrial(ixPerturb) = vectorTempTrial(ixPerturb) + dx + endif - end if ! type of layer (upper, internal, or lower) + ! ***** + ! * compute the volumetric fraction of liquid, ice, and air in each layer in response to perturbation ... + ! ******************************************************************************************************* + + do i = 1,2 !(layer above and below) + select case(layerType(mLayer_ind(i))) !(snow or soil) + + case(iname_soil) + j = mLayer_ind(i)-nSnow !soil layer + if(ixPerturb > 0)then ! only recompute these if perturbed + select case(ixRichards) ! (form of Richards' equation) + case(moisture) ! + vectorMatricHeadTrial(i) = matricHead(vectorVolFracLiqTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) + Tcrit = crit_soilT(vectorMatricHeadTrial(i)) + !if change temp and below critical, it changes the state variable, seems like a problem FIX + if(vectorTempTrial(i) < Tcrit) then !if do not perturb temperature, this should not change + matricFHead = (LH_fus/gravity)*(vectorTempTrial(i) - Tfreeze)/Tfreeze + vectorVolFracLiqTrial(i) = volFracLiq(matricFHead,vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) + endif + case(mixdform) + Tcrit = crit_soilT(vectorMatricHeadTrial(i)) + if(vectorTempTrial(i) < Tcrit) then !if do not perturb temperature, this should not change, but matricHeadTrial will have changed + matricFHead = (LH_fus/gravity)*(vectorTempTrial(i) - Tfreeze)/Tfreeze + vectorVolFracLiqTrial(i) = volFracLiq(matricFHead,vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) + else + vectorVolFracLiqTrial(i) = volFracLiq(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) + endif + end select ! (form of Richards' equation) + vectorVolFracIceTrial(i) = volFracLiq(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - vectorVolFracLiqTrial(i) + endif ! (recompute if perturbed) + ! derivatives, these need to be computed because they are computed only in soilLiqFlx which is called after this + vectordPsi_dTheta(i) = dPsi_dTheta(vectorVolFracLiqTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) + vectordTheta_dPsi(i) = dTheta_dPsi(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) + vectorFracLiqSnow(i) = realMissing + ! soil parameters + vectortheta_sat(i) = theta_sat(j) + vectoriden_soil(i) = iden_soil(j) + vectorthCond_soil(i) = thCond_soil(j) + vectorfrac_sand(i) = frac_sand(j) + vectorfrac_clay(i) = frac_clay(j) + + case(iname_snow) + fLiq = fracliquid(vectorTempTrial(i),snowfrz_scale) ! fraction of liquid water + if(ixPerturb > 0) vectorVolFracIceTrial(i) = ( vectorVolFracLiqTrial(i) / fLiq - vectorVolFracLiqTrial(i) )*(iden_water/iden_ice) ! use perturbed nodeVolTotWatTrial + ! derivatives + vectordPsi_dTheta(i) = realMissing + vectordTheta_dPsi(i) = realMissing + vectorFracLiqSnow(i) = mLayerFracLiqSnow(mLayer_ind(i)) + ! soil parameters do not exist + vectortheta_sat(i) = realMissing + vectoriden_soil(i) = realMissing + vectorthCond_soil(i) = realMissing + vectorfrac_sand(i) = realMissing + vectorfrac_clay(i) = realMissing + + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute volumetric fraction of air'; return + + end select !(snow or soil) + enddo !(layer above and below) + + ! ===== + ! get thermal conductivity at layer interface and its derivative w.r.t. the state above and the state below... + ! ============================================================================================================ + call iLayerThermalConduct(& + ! input: model control + deriv_desired, & ! intent(in): flag indicating if derivatives are desired + ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) + ixThCondSnow, & ! intent(in): choice of method for thermal conductivity of snow + ixThCondSoil, & ! intent(in): choice of method for thermal conductivity of soil + ! input: coordinate variables + nLayers, & ! intent(in): number of layers + iLayer, & ! intent(in): layer index for output + layerType(mLayer_ind), & ! intent(in): layer type (iname_soil or iname_snow) + ! input: state variables (adjacent layers) + vectorMatricHeadTrial, & ! intent(in): matric head at the nodes (m) + vectorVolFracLiqTrial, & ! intent(in): volumetric liquid water at the nodes (m) + vectorVolFracIceTrial, & ! intent(in): volumetric ice at the nodes (m) + vectorTempTrial, & ! intent(in): temperature at the nodes (m) + ! input: pre-computed derivatives + mLayerdTheta_dTk(mLayer_ind), & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + vectorFracLiqSnow, & ! intent(in): fraction of liquid water (-) + vectordTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) + vectordPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) + ! input: model coordinate variables (adjacent layers) + mLayerHeight(mLayer_ind), & ! intent(in): height at the mid-point of the node (m) + iLayerHeight(iLayer_ind), & ! intent(in): height at the interface of the nodes (m) + ! input: soil parameters + vectortheta_sat, & ! intent(in): soil porosity (-) + vectoriden_soil, & ! intent(in): intrinsic density of soil (kg m-3) + vectorthCond_soil, & ! intent(in): thermal conductivity of soil (W m-1 K-1) + vectorfrac_sand, & ! intent(in): fraction of sand (-) + vectorfrac_clay, & ! intent(in): fraction of clay (-) + ! input: snow parameters + fixedThermalCond_snow, & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) + ! output: conductivity at the layer interface (scalars) + iLayerThermalC(iLayer), & ! intent(inout): thermal conductivity at the interface of each layer (W m-1 K-1) + ! output: derivatives in thermal conductivity w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below + dThermalC_dHydStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dHydStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + ! output: derivatives in thermal conductivity w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (W m-1 K-2) + dThermalC_dNrgStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dNrgStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + ! output: error control + err,cmessage) ! intent(out): error control + + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + ! compute total vertical flux, to compute derivatives + if(deriv_desired .and. ixDerivMethod==numerical)then + select case(itry) + case(unperturbed); scalarThermCFlux = iLayerThermalC(iLayer) + case(perturbStateTempAbove); scalarThermCFlux_dTempAbove = iLayerThermalC(iLayer) + case(perturbStateTempBelow); scalarThermCFlux_dTempBelow = iLayerThermalC(iLayer) + case(perturbStateWatAbove); scalarThermCFlux_dWatAbove = iLayerThermalC(iLayer) + case(perturbStateWatBelow); scalarThermCFlux_dWatBelow = iLayerThermalC(iLayer) + case default; err=10; message=trim(message)//"unknown perturbation"; return + end select + end if + + end do ! (looping through different flux calculations -- one or multiple calls depending if desire for numerical or analytical derivatives) + + + ! ***** the upper boundary + if(iLayer==0)then ! (upper boundary) + + ! identify the upper boundary condition + select case(ix_bcUpprTdyn) + + ! * prescribed temperature at the upper boundary + case(prescribedTemp) + dz = (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) + if(ixDerivMethod==analytical)then ! ** analytical derivatives + dFlux_dWatBelow(iLayer) = -dThermalC_dHydStateBelow * ( mLayerTempTrial(iLayer+1) - upperBoundTemp )/dz + dFlux_dTempBelow(iLayer) = -dThermalC_dNrgStateBelow * ( mLayerTempTrial(iLayer+1) - upperBoundTemp )/dz - iLayerThermalC(iLayer)/dz + else ! ** numerical derivatives + flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - upperBoundTemp ) / dz + flux2 = -scalarThermCFlux_dWatBelow*( mLayerTempTrial(iLayer+1) - upperBoundTemp ) / dz + dFlux_dWatBelow(iLayer) = (flux2 - flux0)/dx + flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - upperBoundTemp ) / dz + flux2 = -scalarThermCFlux_dTempBelow*((mLayerTempTrial(iLayer+1)+dx) - upperBoundTemp ) / dz + dFlux_dTempBelow(iLayer) = (flux2 - flux0)/dx + end if + + ! * zero flux at the upper boundary + case(zeroFlux) + dFlux_dWatBelow(iLayer) = 0._rkind + dFlux_dTempBelow(iLayer) = 0._rkind + + ! * compute flux inside vegetation energy flux routine, use here + case(energyFlux) + dFlux_dWatBelow(iLayer) = 0._rkind !dGroundNetFlux_dGroundWat, does not exist in vegNrgFlux + dFlux_dTempBelow(iLayer) = dGroundNetFlux_dGroundTemp + + case default; err=20; message=trim(message)//'unable to identify upper boundary condition for thermodynamics'; return + + end select ! (identifying the upper boundary condition for thermodynamics) + !dGroundNetFlux_dGroundWat = dFlux_dWatBelow(iLayer) ! this is true, but since not used in vegNrgFlux do not define + dGroundNetFlux_dGroundTemp = dFlux_dTempBelow(iLayer) ! need this in vegNrgFlux + + ! ***** the lower boundary + else if(iLayer==nLayers)then ! (lower boundary) + + ! identify the lower boundary condition + select case(ix_bcLowrTdyn) + + ! * prescribed temperature at the lower boundary + case(prescribedTemp) + dz = mLayerDepth(iLayer)*0.5_rkind + if(ixDerivMethod==analytical)then ! ** analytical derivatives + dFlux_dWatAbove(iLayer) = -dThermalC_dHydStateAbove * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + dFlux_dTempAbove(iLayer) = -dThermalC_dNrgStateAbove * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + iLayerThermalC(iLayer)/dz + else ! ** numerical derivatives + flux0 = -scalarThermCFlux * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + flux1 = -scalarThermCFlux_dWatAbove * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + dFlux_dWatAbove(iLayer) = (flux1 - flux0)/dx + flux0 = -scalarThermCFlux * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + flux1 = -scalarThermCFlux_dTempAbove * ( lowerBoundTemp - (mLayerTempTrial(iLayer)+dx) )/dz + dFlux_dTempAbove(iLayer) = (flux1 - flux0)/dx + end if + + ! * zero flux at the lower boundary + case(zeroFlux) + dFlux_dWatAbove(iLayer) = 0._rkind + dFlux_dTempAbove(iLayer) = 0._rkind + + case default; err=20; message=trim(message)//'unable to identify lower boundary condition for thermodynamics'; return + + end select ! (identifying the lower boundary condition for thermodynamics) + + ! ***** internal layers - end do ! (looping through layers) + else + dz = (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) + if(ixDerivMethod==analytical)then ! ** analytical derivatives + dFlux_dWatAbove(iLayer) = -dThermalC_dHydStateAbove * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz + dFlux_dWatBelow(iLayer) = -dThermalC_dHydStateBelow * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz + dFlux_dTempAbove(iLayer) = -dThermalC_dNrgStateAbove * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz + iLayerThermalC(iLayer)/dz + dFlux_dTempBelow(iLayer) = -dThermalC_dNrgStateBelow * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz - iLayerThermalC(iLayer)/dz + else ! ** numerical derivatives + flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz + flux1 = -scalarThermCFlux_dWatAbove*( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz + flux2 = -scalarThermCFlux_dWatBelow*( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz + dFlux_dWatAbove(iLayer) = (flux1 - flux0)/dx + dFlux_dWatBelow(iLayer) = (flux2 - flux0)/dx + flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz + flux1 = -scalarThermCFlux_dTempAbove*( mLayerTempTrial(iLayer+1) - (mLayerTempTrial(iLayer)+dx)) / dz + flux2 = -scalarThermCFlux_dTempBelow*((mLayerTempTrial(iLayer+1)+dx) - mLayerTempTrial(iLayer) ) / dz + dFlux_dTempAbove(iLayer) = (flux1 - flux0)/dx + dFlux_dTempBelow(iLayer) = (flux2 - flux0)/dx + end if + + end if ! type of layer (upper, internal, or lower) + + end do ! (looping through layers) ! ------------------------------------------------------------------------------------------------------------------------- ! ***** compute the conductive fluxes at layer interfaces ***** From 4c57782c84049ca9dad2d9feac6dff3e4b92d8df Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 19 Sep 2022 18:17:55 +0000 Subject: [PATCH 0334/1472] fixed uninitialzed values in the colbeck problem --- build/source/engine/soilLiqFlx.f90 | 5 +- build/source/engine/varSubstepSundials.f90 | 702 +++++++++++---------- 2 files changed, 356 insertions(+), 351 deletions(-) diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index 1d3c59687..389e138a5 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -1319,7 +1319,6 @@ subroutine surfaceFlx(& ! initialize error control err=0; message="surfaceFlx/" - ! initialize derivatives dq_dHydStateVec(:) = 0._rkind dq_dNrgStateVec(:) = 0._rkind @@ -1516,7 +1515,11 @@ subroutine surfaceFlx(& scalarSurfaceInfiltration = (1._rkind - scalarFrozenArea)*scalarInfilArea*min(scalarRainPlusMelt,xMaxInfilRate) ! compute infiltration derivative for layers not at surface + ! TODO: VERIFY the first part of the if statement + ! I added the 0._rkind to the (0) index in the if condition to get rid of an uninitalized value error if (xMaxInfilRate < scalarRainPlusMelt) then ! = dXMaxInfilRate_d + dInfilRate_dWat(:) = 0._rkind ! set to 0 to get rid of any unitialized values that may exit after going from 1:nsoil + dInfilRate_dTk(:) = 0._rkind ! set to 0 to get rid of any unitialized values that may exit after going from 1:nsoil dInfilRate_dWat(1:nSoil) = dXMaxInfilRate_dWat(:) dInfilRate_dTk(1:nSoil) = dXMaxInfilRate_dTk(:) else ! = dRainPlusMelt_d only dependent on canopy diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index f5be83696..8e46abbbf 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -258,6 +258,9 @@ subroutine varSubstepSundials(& ! initialize the length of the substep dtSubstep = dtInit + ! initalize flag for checking if energy fluxes had been modified + nrgFluxModified = .false. + ! allocate space for the temporary model flux structure call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif @@ -537,199 +540,199 @@ subroutine varSubstepSundials(& end subroutine varSubstepSundials - ! ********************************************************************************************************** - ! private subroutine updateProgSundials: update prognostic variables - ! ********************************************************************************************************** - subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control +! ********************************************************************************************************** +! private subroutine updateProgSundials: update prognostic variables +! ********************************************************************************************************** +subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures waterBalanceError,nrgFluxModified,err,message) ! output: flags and error control - USE getVectorz_module,only:varExtract ! extract variables from the state vector - USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables - USE getVectorzAddSundials_module, only:varExtractSundials - USE computEnthalpy_module,only:computEnthalpy - USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy - implicit none - ! model control - real(rkind) ,intent(in) :: dt ! time step (s) - integer(i4b) ,intent(in) :: nSnow ! number of snow layers - integer(i4b) ,intent(in) :: nSoil ! number of soil layers - integer(i4b) ,intent(in) :: nLayers ! total number of layers - logical(lgt) ,intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature - logical(lgt) ,intent(in) :: computeVegFlux ! flag to compute the vegetation flux - real(rkind) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) - real(rkind) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) - real(rkind) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) - logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance - logical(lgt) ,intent(in) :: checkNrgBalance ! flag to check the energy balance - ! data structures - type(zLookup),intent(in) :: lookup_data ! lookup tables - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! indices for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! flags and error control - logical(lgt) ,intent(out) :: waterBalanceError ! flag to denote that there is a water balance error - logical(lgt) ,intent(out) :: nrgFluxModified ! flag to denote that the energy fluxes were modified - integer(i4b) ,intent(out) :: err ! error code - character(*) ,intent(out) :: message ! error message - ! ================================================================================================================== - ! general - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: ixSubset ! index within the state subset - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixControlIndex ! index within a given domain - real(rkind) :: volMelt ! volumetric melt (kg m-3) - real(rkind),parameter :: verySmall=epsilon(1._rkind)*2._rkind ! a very small number (deal with precision issues) - ! mass balance - real(rkind) :: canopyBalance0,canopyBalance1 ! canopy storage at start/end of time step - real(rkind) :: soilBalance0,soilBalance1 ! soil storage at start/end of time step - real(rkind) :: vertFlux ! change in storage due to vertical fluxes - real(rkind) :: tranSink,baseSink,compSink ! change in storage due to sink terms - real(rkind) :: liqError ! water balance error - real(rkind) :: fluxNet ! net water fluxes (kg m-2 s-1) - real(rkind) :: superflousWat ! superflous water used for evaporation (kg m-2 s-1) - real(rkind) :: superflousNrg ! superflous energy that cannot be used for evaporation (W m-2 [J m-2 s-1]) - character(LEN=256) :: cmessage ! error message of downwind routine - ! trial state variables - real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial vector for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector for liquid water matric potential (m) - real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) - ! diagnostic variables - real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric fraction of ice (-) - ! derivative of state variables - real(rkind) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatPrime ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempPrime ! trial vector for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! trial vector for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! trial vector for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! trial vector for liquid water matric potential (m) - real(rkind) :: scalarAquiferStoragePrime ! trial value for storage of water in the aquifer (m) - ! diagnostic variables - real(rkind) :: scalarCanopyLiqPrime ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector for volumetric fraction of ice (-) - real(rkind) :: scalarCanairEnthalpyTrial ! enthalpy of the canopy air space (J m-3) - real(rkind) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! enthalpy of snow + soil (J m-3) - ! ------------------------------------------------------------------------------------------------------------------- - - ! ------------------------------------------------------------------------------------------------------------------- - ! point to flux variables in the data structure - associate(& - ! get indices for mass balance - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in) : [i4b] index of canopy hydrology state variable (mass) - ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the soil domain - ! get indices for the un-tapped melt - ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in) : [i4b(:)] list of indices for all energy states - ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in) : [i4b(:)] indices defining the domain of the state (iname_veg, iname_snow, iname_soil) - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in) : [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in) : [i4b(:)] [state subset] list of indices of the full state vector in the state subset - ! water fluxes - scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in) : [dp] rainfall rate (kg m-2 s-1) - scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(in) : [dp] rain reaches ground without touching the canopy (kg m-2 s-1) - scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(in) : [dp] canopy evaporation/condensation (kg m-2 s-1) - scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(in) : [dp] canopy transpiration (kg m-2 s-1) - scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(in) : [dp] drainage liquid water from vegetation canopy (kg m-2 s-1) - iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(in) : [dp(0:)] vertical liquid water flux at soil layer interfaces (-) - iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(in) : - mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) - mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(in) : [dp(:)] transpiration loss from each soil layer (m s-1) - mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(in) : [dp(:)] baseflow from each soil layer (m s-1) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in) : [dp(:)] change in storage associated with compression of the soil matrix (-) - scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the vegetation canopy (kg m-2 s-1) - scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the snow surface (kg m-2 s-1) - ! energy fluxes - scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ,& ! intent(in) : [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ,& ! intent(in) : [dp] sensible heat flux from the canopy to the canopy air space (W m-2) - ! domain depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in) : [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in) : [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! model state variables (vegetation canopy) - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout) : [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout) : [dp] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout) : [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout) : [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout) : [dp(:)] matric head (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(inout) : [dp(:)] matric potential of liquid water (m) -! enthalpy - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(inout): [dp(:)] enthalpy of the snow+soil layers (J m-3) - ! model state variables (aquifer) - scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(inout) : [dp(:)] storage of water in the aquifer (m) - ! error tolerance - absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1) & ! intent(in) : [dp] absolute convergence tolerance for vol frac liq water (-) - ) ! associating flux variables in the data structure - ! ------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='updateProgSundials/' + USE getVectorz_module,only:varExtract ! extract variables from the state vector + USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables + USE getVectorzAddSundials_module, only:varExtractSundials + USE computEnthalpy_module,only:computEnthalpy + USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy + implicit none + ! model control + real(rkind) ,intent(in) :: dt ! time step (s) + integer(i4b) ,intent(in) :: nSnow ! number of snow layers + integer(i4b) ,intent(in) :: nSoil ! number of soil layers + integer(i4b) ,intent(in) :: nLayers ! total number of layers + logical(lgt) ,intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature + logical(lgt) ,intent(in) :: computeVegFlux ! flag to compute the vegetation flux + real(rkind) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) + real(rkind) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) + real(rkind) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) + logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance + logical(lgt) ,intent(in) :: checkNrgBalance ! flag to check the energy balance + ! data structures + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! indices for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + ! flags and error control + logical(lgt) ,intent(out) :: waterBalanceError ! flag to denote that there is a water balance error + logical(lgt) ,intent(out) :: nrgFluxModified ! flag to denote that the energy fluxes were modified + integer(i4b) ,intent(out) :: err ! error code + character(*) ,intent(out) :: message ! error message + ! ================================================================================================================== + ! general + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: ixSubset ! index within the state subset + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixControlIndex ! index within a given domain + real(rkind) :: volMelt ! volumetric melt (kg m-3) + real(rkind),parameter :: verySmall=epsilon(1._rkind)*2._rkind ! a very small number (deal with precision issues) + ! mass balance + real(rkind) :: canopyBalance0,canopyBalance1 ! canopy storage at start/end of time step + real(rkind) :: soilBalance0,soilBalance1 ! soil storage at start/end of time step + real(rkind) :: vertFlux ! change in storage due to vertical fluxes + real(rkind) :: tranSink,baseSink,compSink ! change in storage due to sink terms + real(rkind) :: liqError ! water balance error + real(rkind) :: fluxNet ! net water fluxes (kg m-2 s-1) + real(rkind) :: superflousWat ! superflous water used for evaporation (kg m-2 s-1) + real(rkind) :: superflousNrg ! superflous energy that cannot be used for evaporation (W m-2 [J m-2 s-1]) + character(LEN=256) :: cmessage ! error message of downwind routine + ! trial state variables + real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial vector for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector for liquid water matric potential (m) + real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) + ! diagnostic variables + real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric fraction of ice (-) + ! derivative of state variables + real(rkind) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatPrime ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempPrime ! trial vector for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! trial vector for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! trial vector for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! trial vector for liquid water matric potential (m) + real(rkind) :: scalarAquiferStoragePrime ! trial value for storage of water in the aquifer (m) + ! diagnostic variables + real(rkind) :: scalarCanopyLiqPrime ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector for volumetric fraction of ice (-) + real(rkind) :: scalarCanairEnthalpyTrial ! enthalpy of the canopy air space (J m-3) + real(rkind) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! enthalpy of snow + soil (J m-3) + ! ------------------------------------------------------------------------------------------------------------------- + + ! ------------------------------------------------------------------------------------------------------------------- + ! point to flux variables in the data structure + associate(& + ! get indices for mass balance + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in) : [i4b] index of canopy hydrology state variable (mass) + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the soil domain + ! get indices for the un-tapped melt + ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in) : [i4b(:)] list of indices for all energy states + ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in) : [i4b(:)] indices defining the domain of the state (iname_veg, iname_snow, iname_soil) + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in) : [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in) : [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ! water fluxes + scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in) : [dp] rainfall rate (kg m-2 s-1) + scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(in) : [dp] rain reaches ground without touching the canopy (kg m-2 s-1) + scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(in) : [dp] canopy evaporation/condensation (kg m-2 s-1) + scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(in) : [dp] canopy transpiration (kg m-2 s-1) + scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(in) : [dp] drainage liquid water from vegetation canopy (kg m-2 s-1) + iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(in) : [dp(0:)] vertical liquid water flux at soil layer interfaces (-) + iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(in) : + mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) + mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(in) : [dp(:)] transpiration loss from each soil layer (m s-1) + mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(in) : [dp(:)] baseflow from each soil layer (m s-1) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in) : [dp(:)] change in storage associated with compression of the soil matrix (-) + scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the vegetation canopy (kg m-2 s-1) + scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the snow surface (kg m-2 s-1) + ! energy fluxes + scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ,& ! intent(in) : [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ,& ! intent(in) : [dp] sensible heat flux from the canopy to the canopy air space (W m-2) + ! domain depth + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in) : [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in) : [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! model state variables (vegetation canopy) + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the vegetation canopy (K) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout) : [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout) : [dp] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout) : [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variables (snow and soil domains) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout) : [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout) : [dp(:)] matric head (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(inout) : [dp(:)] matric potential of liquid water (m) + ! enthalpy + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(inout): [dp(:)] enthalpy of the snow+soil layers (J m-3) + ! model state variables (aquifer) + scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(inout) : [dp(:)] storage of water in the aquifer (m) + ! error tolerance + absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1) & ! intent(in) : [dp] absolute convergence tolerance for vol frac liq water (-) + ) ! associating flux variables in the data structure + ! ------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='updateProgSundials/' - ! initialize water balancmLayerVolFracWatTriale error - waterBalanceError=.false. + ! initialize water balancmLayerVolFracWatTriale error + waterBalanceError=.false. - ! get storage at the start of the step - canopyBalance0 = merge(scalarCanopyWat, realMissing, computeVegFlux) - soilBalance0 = sum( (mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) + ! get storage at the start of the step + canopyBalance0 = merge(scalarCanopyWat, realMissing, computeVegFlux) + soilBalance0 = sum( (mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) - ! ----- - ! * update states... - ! ------------------ - - ! initialize to state variable from the last update - scalarCanairTempTrial = scalarCanairTemp - scalarCanopyTempTrial = scalarCanopyTemp - scalarCanopyWatTrial = scalarCanopyWat - scalarCanopyLiqTrial = scalarCanopyLiq - scalarCanopyIceTrial = scalarCanopyIce - mLayerTempTrial = mLayerTemp - mLayerVolFracWatTrial = mLayerVolFracWat - mLayerVolFracLiqTrial = mLayerVolFracLiq - mLayerVolFracIceTrial = mLayerVolFracIce - mLayerMatricHeadTrial = mLayerMatricHead ! total water matric potential - mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq ! liquid water matric potential - scalarAquiferStorageTrial = scalarAquiferStorage - - ! extract states from the state vector - call varExtract(& - ! input - stateVecTrial, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + ! ----- + ! * update states... + ! ------------------ + + ! initialize to state variable from the last update + scalarCanairTempTrial = scalarCanairTemp + scalarCanopyTempTrial = scalarCanopyTemp + scalarCanopyWatTrial = scalarCanopyWat + scalarCanopyLiqTrial = scalarCanopyLiq + scalarCanopyIceTrial = scalarCanopyIce + mLayerTempTrial = mLayerTemp + mLayerVolFracWatTrial = mLayerVolFracWat + mLayerVolFracLiqTrial = mLayerVolFracLiq + mLayerVolFracIceTrial = mLayerVolFracIce + mLayerMatricHeadTrial = mLayerMatricHead ! total water matric potential + mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq ! liquid water matric potential + scalarAquiferStorageTrial = scalarAquiferStorage + + ! extract states from the state vector + call varExtract(& + ! input + stateVecTrial, & ! intent(in): model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) + mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) + ! output: variables for the aquifer + scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) call varExtractSundials(& ! input @@ -752,172 +755,171 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux scalarAquiferStoragePrime,& ! intent(out): derivative of storage of water in the aquifer (m) ! output: error control err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - - ! update diagnostic variables - call updateVarsSundials(& - ! input - dt, & - .false., & ! intent(in): logical flag if inside Sundials solver - doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - mLayerVolFracWatTrial, & ! intent(in): use current vector for prev vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): use current vector for prev vector of total water matric potential (m) - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! - mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) - mLayerVolFracIcePrime, & ! - mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! ---- - ! * check energy balance - !------------------------ - ! NOTE: for now, we just compute enthalpy - if(checkNrgBalance)then - ! compute enthalpy at t_{n+1} - call t2enthalpy(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) - ! output: enthalpy - scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + + ! update diagnostic variables + call updateVarsSundials(& + ! input + dt, & + .false., & ! intent(in): logical flag if inside Sundials solver + doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + mLayerVolFracWatTrial, & ! intent(in): use current vector for prev vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): use current vector for prev vector of total water matric potential (m) + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output: variables for the vegetation canopy + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempPrime, & ! + mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) + mLayerVolFracIcePrime, & ! + mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! ---- + ! * check energy balance + !------------------------ + ! NOTE: for now, we just compute enthalpy + if(checkNrgBalance)then + ! compute enthalpy at t_{n+1} + call t2enthalpy(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) + ! output: enthalpy + scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - endif + endif - ! ----- - ! * check mass balance... - ! ----------------------- - - ! NOTE: should not need to do this, since mass balance is checked in the solver - if(checkMassBalance)then - - ! check mass balance for the canopy - if(ixVegHyd/=integerMissing)then - - ! handle cases where fluxes empty the canopy - fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage - if(-fluxNet*dt > canopyBalance0)then - - ! --> first add water - canopyBalance1 = canopyBalance0 + (scalarRainfall - scalarThroughfallRain)*dt - - ! --> next, remove canopy evaporation -- put the unsatisfied evap into sensible heat - canopyBalance1 = canopyBalance1 + scalarCanopyEvaporation*dt - if(canopyBalance1 < 0._rkind)then - ! * get superfluous water and energy - superflousWat = -canopyBalance1/dt ! kg m-2 s-1 - superflousNrg = superflousWat*LH_vap ! W m-2 (J m-2 s-1) - ! * update fluxes and states - canopyBalance1 = 0._rkind - scalarCanopyEvaporation = scalarCanopyEvaporation + superflousWat - scalarLatHeatCanopyEvap = scalarLatHeatCanopyEvap + superflousNrg - scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg - endif - - ! --> next, remove canopy drainage - canopyBalance1 = canopyBalance1 - scalarCanopyLiqDrainage*dt - if(canopyBalance1 < 0._rkind)then - superflousWat = -canopyBalance1/dt ! kg m-2 s-1 - canopyBalance1 = 0._rkind - scalarCanopyLiqDrainage = scalarCanopyLiqDrainage + superflousWat - endif - - ! update the trial state - scalarCanopyWatTrial = canopyBalance1 - - ! set the modification flag - nrgFluxModified = .true. + ! ----- + ! * check mass balance... + ! ----------------------- - else - canopyBalance1 = canopyBalance0 + fluxNet*dt - nrgFluxModified = .false. - endif ! cases where fluxes empty the canopy - - ! check the mass balance - fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage - liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial - if(abs(liqError) > absConvTol_liquid*10._rkind*iden_water)then ! *10 because of precision issues - !write(*,'(a,1x,f20.10)') 'dt = ', dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - !write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 - !write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 - !write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt - !write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - !write(*,'(a,1x,f20.10)') 'liqError = ', liqError - waterBalanceError = .true. - return - endif ! if there is a water balance error - endif ! if veg canopy - - ! check mass balance for soil - ! NOTE: fatal errors, though possible to recover using negative error codes - if(count(ixSoilOnlyHyd/=integerMissing)==nSoil)then - soilBalance1 = sum( (mLayerVolFracLiqTrial(nSnow+1:nLayers) + mLayerVolFracIceTrial(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) - vertFlux = -(iLayerLiqFluxSoil(nSoil) - iLayerLiqFluxSoil(0))*dt ! m s-1 --> m - tranSink = sum(mLayerTranspire)*dt ! m s-1 --> m - baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m - compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m - liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) - if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues - !write(*,'(a,1x,f20.10)') 'dt = ', dt - !write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 - !write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 - !write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux - !write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink - !write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink - !write(*,'(a,1x,f20.10)') 'compSink = ', compSink - !write(*,'(a,1x,f20.10)') 'liqError = ', liqError - !write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid - waterBalanceError = .true. - return - endif ! if there is a water balance error - endif ! if hydrology states exist in the soil domain - -endif ! if checking the mass balance + ! NOTE: should not need to do this, since mass balance is checked in the solver + if(checkMassBalance)then + + ! check mass balance for the canopy + if(ixVegHyd/=integerMissing)then + + ! handle cases where fluxes empty the canopy + fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + if(-fluxNet*dt > canopyBalance0)then + + ! --> first add water + canopyBalance1 = canopyBalance0 + (scalarRainfall - scalarThroughfallRain)*dt + + ! --> next, remove canopy evaporation -- put the unsatisfied evap into sensible heat + canopyBalance1 = canopyBalance1 + scalarCanopyEvaporation*dt + if(canopyBalance1 < 0._rkind)then + ! * get superfluous water and energy + superflousWat = -canopyBalance1/dt ! kg m-2 s-1 + superflousNrg = superflousWat*LH_vap ! W m-2 (J m-2 s-1) + ! * update fluxes and states + canopyBalance1 = 0._rkind + scalarCanopyEvaporation = scalarCanopyEvaporation + superflousWat + scalarLatHeatCanopyEvap = scalarLatHeatCanopyEvap + superflousNrg + scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg + endif + + ! --> next, remove canopy drainage + canopyBalance1 = canopyBalance1 - scalarCanopyLiqDrainage*dt + if(canopyBalance1 < 0._rkind)then + superflousWat = -canopyBalance1/dt ! kg m-2 s-1 + canopyBalance1 = 0._rkind + scalarCanopyLiqDrainage = scalarCanopyLiqDrainage + superflousWat + endif + + ! update the trial state + scalarCanopyWatTrial = canopyBalance1 + + ! set the modification flag + nrgFluxModified = .true. + + else + canopyBalance1 = canopyBalance0 + fluxNet*dt + nrgFluxModified = .false. + endif ! cases where fluxes empty the canopy + + ! check the mass balance + fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial + if(abs(liqError) > absConvTol_liquid*10._rkind*iden_water)then ! *10 because of precision issues + !write(*,'(a,1x,f20.10)') 'dt = ', dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + !write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 + !write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 + !write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt + !write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt + !write(*,'(a,1x,f20.10)') 'liqError = ', liqError + waterBalanceError = .true. + return + endif ! if there is a water balance error + endif ! if veg canopy + + ! check mass balance for soil + ! NOTE: fatal errors, though possible to recover using negative error codes + if(count(ixSoilOnlyHyd/=integerMissing)==nSoil)then + soilBalance1 = sum( (mLayerVolFracLiqTrial(nSnow+1:nLayers) + mLayerVolFracIceTrial(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) + vertFlux = -(iLayerLiqFluxSoil(nSoil) - iLayerLiqFluxSoil(0))*dt ! m s-1 --> m + tranSink = sum(mLayerTranspire)*dt ! m s-1 --> m + baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m + compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m + liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) + if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues + !write(*,'(a,1x,f20.10)') 'dt = ', dt + !write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 + !write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 + !write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux + !write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink + !write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink + !write(*,'(a,1x,f20.10)') 'compSink = ', compSink + !write(*,'(a,1x,f20.10)') 'liqError = ', liqError + !write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid + waterBalanceError = .true. + return + endif ! if there is a water balance error + endif ! if hydrology states exist in the soil domain + endif ! if checking the mass balance ! ----- ! * remove untapped melt energy... From 5ffdc88ea2e1e03498d1fae634076361913cce89 Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 20 Sep 2022 21:58:05 +0000 Subject: [PATCH 0335/1472] applied change from ashely to soilLiqFlx.f90 --- Laugh-Tests | 1 + build/source/engine/soilLiqFlx.f90 | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) create mode 160000 Laugh-Tests diff --git a/Laugh-Tests b/Laugh-Tests new file mode 160000 index 000000000..e372b9a31 --- /dev/null +++ b/Laugh-Tests @@ -0,0 +1 @@ +Subproject commit e372b9a31e9eb555e8764616bc9491370dc49c34 diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index 389e138a5..57446cf74 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -1523,11 +1523,11 @@ subroutine surfaceFlx(& dInfilRate_dWat(1:nSoil) = dXMaxInfilRate_dWat(:) dInfilRate_dTk(1:nSoil) = dXMaxInfilRate_dTk(:) else ! = dRainPlusMelt_d only dependent on canopy - dInfilRate_dWat(:) = 0._rkind !only calculate for layers that are not the surface - dInfilRate_dTk(:) = 0._rkind !only calculate for layers that are not the surface - ! dependent on above layer (canopy or snow) water and temp dInfilRate_dWat(0) = above_soilLiqFluxDeriv*above_soilFracLiq dInfilRate_dTk(0) = above_soilLiqFluxDeriv*above_soildLiq_dTk + dInfilRate_dWat(1:nSoil) = 0._rkind !only calculate for layers that are not the surface + dInfilRate_dTk(1:nSoil) = 0._rkind !only calculate for layers that are not the surface + ! dependent on above layer (canopy or snow) water and temp endif ! dq w.r.t. infiltration only, scalarRainPlusMelt accounted for in computeJacDAE_module From dd39130bee0d7f2997bf1d661b5ae16eed637d4f Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 20 Sep 2022 22:06:24 +0000 Subject: [PATCH 0336/1472] Remove accidental folder upload --- Laugh-Tests | 1 - 1 file changed, 1 deletion(-) delete mode 160000 Laugh-Tests diff --git a/Laugh-Tests b/Laugh-Tests deleted file mode 160000 index e372b9a31..000000000 --- a/Laugh-Tests +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e372b9a31e9eb555e8764616bc9491370dc49c34 From 05b582c58aba50a9a3ff42b16fbd096fb36b504b Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 20 Sep 2022 22:07:17 +0000 Subject: [PATCH 0337/1472] added unwanted folder to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a93e28036..2d50f40a9 100755 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,4 @@ Makefile-* *.backup # site directory from mkdocs site/ +Laugh-Tests From 8638618b1b9689bed07fda41233a3f341723f61d Mon Sep 17 00:00:00 2001 From: Kyle Date: Wed, 21 Sep 2022 20:18:26 +0000 Subject: [PATCH 0338/1472] changed make file to allow for specifying debug or production run --- .gitignore | 2 +- build/makefile | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 2d50f40a9..8d478f773 100755 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,4 @@ Makefile-* *.backup # site directory from mkdocs site/ -Laugh-Tests +Laugh-Tests/ diff --git a/build/makefile b/build/makefile index d10a6cbfa..c98766375 100644 --- a/build/makefile +++ b/build/makefile @@ -97,9 +97,9 @@ ifeq "$(FC)" "gfortran" endif # Production runs -FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_COMM = -g -O0 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_SUMMA = -g -O0 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_NOAH = -ffree-form -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_COMM = -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_SUMMA = -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) # Debug runs # FLAGS_NOAH = -p -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -Wall @@ -359,7 +359,20 @@ endif #====================================================================== # Compile + +production: FLAGS_NOAH += -g -O3 +production: FLAGS_COMM += -g -O3 +production: FLAGS_SUMMA += -g -O3 + + + +debug: FLAGS_NOAH += -g -O0 +debug: FLAGS_COMM += -g -O0 +debug: FLAGS_SUMMA += -g -O0 +debug: all + all: compile_noah compile_comm compile_summa link clean install + part: compile_noah compile_comm compile_summa check: From 819cf8eaae7e83175854cd8f08177c4f1e7c04a2 Mon Sep 17 00:00:00 2001 From: Kyle Date: Wed, 21 Sep 2022 20:57:37 +0000 Subject: [PATCH 0339/1472] Changed call from varExtractSundials to varExtract --- build/source/engine/eval8summaSundials.f90 | 5 ++--- build/source/engine/getVectorz.f90 | 22 +++++++++---------- build/source/engine/getVectorzAddSundials.f90 | 6 ++--- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index ab2211537..869a00263 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -435,9 +435,8 @@ subroutine eval8summaSundials(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - - call varExtractSundials(& + ! Extract the variables for stateVecPrime + call varExtract(& ! input stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) diag_data, & ! intent(in): model diagnostic variables for a local HRU diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index c2359aece..cccf133b3 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -397,14 +397,14 @@ subroutine getScaling(& ! ------------------------------------------------------------------------------------------ end associate fixedLength ! end association to variables in the data structure where vector length does not change - end subroutine getScaling +end subroutine getScaling - ! ********************************************************************************************************** - ! public subroutine varExtract: extract variables from the state vector and compute diagnostic variables - ! This routine does not initialize any of the variables - ! ********************************************************************************************************** - subroutine varExtract(& +! ********************************************************************************************************** +! public subroutine varExtract: extract variables from the state vector and compute diagnostic variables +! This routine does not initialize any of the variables +! ********************************************************************************************************** +subroutine varExtract(& ! input stateVec, & ! intent(in): model state vector (mixed units) diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -430,9 +430,9 @@ subroutine varExtract(& implicit none ! input real(rkind),intent(in) :: stateVec(:) ! model state vector (mixed units) - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers ! output: variables for the vegetation canopy real(rkind),intent(out) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) @@ -447,8 +447,8 @@ subroutine varExtract(& ! output: variables for the aquifer real(rkind),intent(out) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables integer(i4b) :: iLayer ! index of layer within the snow+soil domain diff --git a/build/source/engine/getVectorzAddSundials.f90 b/build/source/engine/getVectorzAddSundials.f90 index 6db88e775..c77edda5f 100644 --- a/build/source/engine/getVectorzAddSundials.f90 +++ b/build/source/engine/getVectorzAddSundials.f90 @@ -111,9 +111,9 @@ subroutine varExtractSundials(& implicit none ! input real(rkind),intent(in) :: stateVecPrime(:) ! model state vector (mixed units) - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers ! output: variables for the vegetation canopy real(rkind),intent(out) :: scalarCanairTempPrime ! trial value of canopy air temperature (K) real(rkind),intent(out) :: scalarCanopyTempPrime ! trial value of canopy temperature (K) From 4750a4af78cf5f65d9f2bcaf93c8322a9d8cb7e8 Mon Sep 17 00:00:00 2001 From: Kyle Date: Wed, 21 Sep 2022 21:34:24 +0000 Subject: [PATCH 0340/1472] deleted varExtractSundials varExtract is the same thing we should reuse it and just pass it the correct parameters --- build/makefile | 1 + build/source/engine/eval8JacDAE.f90 | 3 +- build/source/engine/eval8summaSundials.f90 | 1 - build/source/engine/getVectorzAddSundials.f90 | 130 ------------------ build/source/engine/varSubstepSundials.f90 | 4 +- 5 files changed, 3 insertions(+), 136 deletions(-) diff --git a/build/makefile b/build/makefile index c98766375..046e5d806 100644 --- a/build/makefile +++ b/build/makefile @@ -363,6 +363,7 @@ endif production: FLAGS_NOAH += -g -O3 production: FLAGS_COMM += -g -O3 production: FLAGS_SUMMA += -g -O3 +production: all diff --git a/build/source/engine/eval8JacDAE.f90 b/build/source/engine/eval8JacDAE.f90 index 235567f7a..114e13381 100644 --- a/build/source/engine/eval8JacDAE.f90 +++ b/build/source/engine/eval8JacDAE.f90 @@ -118,7 +118,6 @@ subroutine eval8JacDAE(& ! -------------------------------------------------------------------------------------------------------------------------------- ! provide access to subroutines USE getVectorz_module, only:varExtract ! extract variables from the state vector - USE getVectorzAddSundials_module, only:varExtractSundials USE updateVarsSundials_module, only:updateVarsSundials ! update prognostic variables USE computJacobSundials_module,only:computJacobSundials implicit none @@ -232,7 +231,7 @@ subroutine eval8JacDAE(& if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! extract derivative of variables from derivative of the model state vector - call varExtractSundials(& + call varExtract(& ! input stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) diag_data, & ! intent(in): model diagnostic variables for a local HRU diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index 869a00263..e1836e9f4 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -155,7 +155,6 @@ subroutine eval8summaSundials(& ! -------------------------------------------------------------------------------------------------------------------------------- ! provide access to subroutines USE getVectorz_module, only:varExtract ! extract variables from the state vector - USE getVectorzAddSundials_module, only:varExtractSundials USE updateVarsSundials_module, only:updateVarsSundials ! update variables USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy USE computFlux_module, only:soilCmpresSundials ! compute soil compression diff --git a/build/source/engine/getVectorzAddSundials.f90 b/build/source/engine/getVectorzAddSundials.f90 index c77edda5f..3113f43d3 100644 --- a/build/source/engine/getVectorzAddSundials.f90 +++ b/build/source/engine/getVectorzAddSundials.f90 @@ -74,141 +74,11 @@ module getVectorzAddSundials_module implicit none private -public::varExtractSundials public::residDiscontinuity public::countDiscontinuity - - contains - ! ********************************************************************************************************** - ! public subroutine varExtractSundials: extract prime variables from the state vector and compute diagnostic variables - ! ********************************************************************************************************** - subroutine varExtractSundials(& - ! input - stateVecPrime, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy - scalarCanairTempPrime, & ! intent(out): trial value of canopy air temperature (K) - scalarCanopyTempPrime, & ! intent(out): trial value of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(out): trial value of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(out): trial value of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempPrime, & ! intent(out): trial vector of layer temperature (K) - mLayerVolFracWatPrime, & ! intent(out): trial vector of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerMatricHeadPrime, & ! intent(out): trial vector of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(out): trial vector of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStoragePrime, & ! intent(out): trial value of storage of water in the aquifer (m) - ! output: error control - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input - real(rkind),intent(in) :: stateVecPrime(:) ! model state vector (mixed units) - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - ! output: variables for the vegetation canopy - real(rkind),intent(out) :: scalarCanairTempPrime ! trial value of canopy air temperature (K) - real(rkind),intent(out) :: scalarCanopyTempPrime ! trial value of canopy temperature (K) - real(rkind),intent(out) :: scalarCanopyWatPrime ! trial value of canopy total water (kg m-2) - real(rkind),intent(out) :: scalarCanopyLiqPrime ! trial value of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - real(rkind),intent(out) :: mLayerTempPrime(:) ! trial vector of layer temperature (K) - real(rkind),intent(out) :: mLayerVolFracWatPrime(:) ! trial vector of volumetric total water content (-) - real(rkind),intent(out) :: mLayerVolFracLiqPrime(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(out) :: mLayerMatricHeadPrime(:) ! trial vector of total water matric potential (m) - real(rkind),intent(out) :: mLayerMatricHeadLiqPrime(:) ! trial vector of liquid water matric potential (m) - ! output: variables for the aquifer - real(rkind),intent(out) :: scalarAquiferStoragePrime ! trial value of storage of water in the aquifer (m) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - associate(& - ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers - ! indices defining model states and layers - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of the squifer storage state variable - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for energy states in the snow+soil subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for hydrology states in the snow+soil subdomain - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain - ! indices defining type of model state variables - ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] type of desired model state variables - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat & ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain -) ! association with variables in the data structures - - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - - ! initialize error control - err=0; message='varExtractSundials/' - - - ! check if computing the vegetation flux - if(ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing .or. ixVegHyd/=integerMissing)then - - ! extract temperature of the canopy air space - if(ixCasNrg/=integerMissing) scalarCanairTempPrime = stateVecPrime(ixCasNrg) - - ! extract canopy temperature - if(ixVegNrg/=integerMissing) scalarCanopyTempPrime = stateVecPrime(ixVegNrg) - - ! extract intercepted water - if(ixVegHyd/=integerMissing)then - select case( ixStateType_subset(ixVegHyd) ) - case(iname_liqCanopy); scalarCanopyLiqPrime = stateVecPrime(ixVegHyd) - case(iname_watCanopy); scalarCanopyWatPrime = stateVecPrime(ixVegHyd) - case default; err=20; message=trim(message)//'case not found: expect iname_liqCanopy or iname_watCanopy'; return - end select - endif - - endif ! not computing the vegetation flux - - - ! overwrite with the energy values from the state vector - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - mLayerTempPrime(iLayer) = stateVecPrime( ixSnowSoilNrg(iLayer) ) - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! overwrite with the hydrology values from the state vector - if(nSnowSoilHyd>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) - select case( ixHydType(iLayer) ) - case(iname_watLayer); mLayerVolFracWatPrime(iLayer) = stateVecPrime( ixSnowSoilHyd(iLayer) ) ! total water state variable for snow+soil layers - case(iname_liqLayer); mLayerVolFracLiqPrime(iLayer) = stateVecPrime( ixSnowSoilHyd(iLayer) ) ! liquid water state variable for snow+soil layers - case(iname_matLayer); mLayerMatricHeadPrime(iLayer-nSnow) = stateVecPrime( ixSnowSoilHyd(iLayer) ) ! total water matric potential variable for soil layers - case(iname_lmpLayer); mLayerMatricHeadLiqPrime(iLayer-nSnow) = stateVecPrime( ixSnowSoilHyd(iLayer) ) ! liquid matric potential state variable for soil layers - case default ! do nothing - end select - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! extract temperature of the canopy air space - if(ixAqWat/=integerMissing) scalarAquiferStoragePrime = stateVecPrime(ixAqWat) - - end associate - - end subroutine varExtractSundials - ! ********************************************************************************************************** ! public subroutine residDiscontinuity: diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index 8e46abbbf..e0158f7e4 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -131,7 +131,6 @@ subroutine varSubstepSundials(& USE getVectorz_module,only:popStateVec ! populate the state vector USE getVectorz_module,only:varExtract ! extract variables from the state vector USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables - USE getVectorzAddSundials_module,only:varExtractSundials ! identify name of variable type (for error message) USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages USE systemSolvSundials_module,only:systemSolvSundials @@ -548,7 +547,6 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux waterBalanceError,nrgFluxModified,err,message) ! output: flags and error control USE getVectorz_module,only:varExtract ! extract variables from the state vector USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables - USE getVectorzAddSundials_module, only:varExtractSundials USE computEnthalpy_module,only:computEnthalpy USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy implicit none @@ -734,7 +732,7 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - call varExtractSundials(& + call varExtract(& ! input stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) diag_data, & ! intent(in): model diagnostic variables for a local HRU From eff9d4bb983d05a655d9685fb24dc548b03e3f8c Mon Sep 17 00:00:00 2001 From: Kyle Date: Wed, 21 Sep 2022 22:35:17 +0000 Subject: [PATCH 0341/1472] Combined lwbal and feasiblity flags tested output and results did not change from the changes made in the code --- build/source/engine/computFlux.f90 | 6 +-- build/source/engine/eval8JacDAE.f90 | 2 +- build/source/engine/eval8summaSundials.f90 | 16 +++---- build/source/engine/evalDAE4IDA.f90 | 5 +- build/source/engine/summaSolveSundialsIDA.f90 | 3 +- build/source/engine/updateVarsSundials.f90 | 48 +++++++++---------- build/source/engine/varSubstepSundials.f90 | 2 +- build/source/engine/vegNrgFlux.f90 | 18 +++---- 8 files changed, 48 insertions(+), 52 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 61d53a3e8..c25266062 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -114,7 +114,7 @@ subroutine computFlux(& firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution - requireLWBal, & ! intent(in): flag to indicate if we need longwave to be balanced + insideIDA, & ! intent(in): flag to indicate inside Sundials Solver (do not require longwave to be balanced) drainageMeltPond, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) ! input: state variables scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) @@ -168,7 +168,7 @@ subroutine computFlux(& logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - logical(lgt),intent(in) :: requireLWBal ! flag to indicate if we need longwave to be balanced + logical(lgt),intent(in) :: insideIDA ! flag if inside Sundials solver real(rkind),intent(in) :: drainageMeltPond ! drainage from the surface melt pond (kg m-2 s-1) ! input: state variables real(rkind),intent(in) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) @@ -443,7 +443,7 @@ subroutine computFlux(& firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - requireLWBal, & ! intent(in): flag to indicate if we need longwave to be balanced + insideIDA, & ! intent(in): flag to indicate inside Sundials Solver (do not require longwave to be balanced) ! input: model state variables upperBoundTemp, & ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature scalarCanairTempTrial, & ! intent(in): trial value of the canopy air space temperature (K) diff --git a/build/source/engine/eval8JacDAE.f90 b/build/source/engine/eval8JacDAE.f90 index 114e13381..97915d669 100644 --- a/build/source/engine/eval8JacDAE.f90 +++ b/build/source/engine/eval8JacDAE.f90 @@ -257,7 +257,7 @@ subroutine eval8JacDAE(& call updateVarsSundials(& ! input dt, & ! intent(in): time step - .true., & ! intent(in): logical flag if inside Sundials solver + .true., & ! intent(in): logical flag if computing Jacobian for sundials solver .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index e1836e9f4..d31b5a017 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -96,13 +96,12 @@ subroutine eval8summaSundials(& nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers nState, & ! intent(in): total number of state variables - checkFeas, & ! intent(in): flag to indicate if we are checking for feasibility + insideIDA, & ! intent(in): flag to indicate if we are inside Sundials solver firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout) flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(inout) flag to indicate if we are processing the first flux call in a splitting operation + firstFluxCall, & ! intent(inout) flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(inout) flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution - requireLWBal, & ! intent(in): flag to indicate if we need longwave to be balanced ! input: state vectors stateVec, & ! intent(in): model state vector stateVecPrime, & ! intent(in): derivative of model state vector @@ -177,13 +176,12 @@ subroutine eval8summaSundials(& integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers integer,intent(in) :: nState ! total number of state variables - logical(lgt),intent(in) :: checkFeas ! flag to indicate if we are checking for feasibility + logical(lgt),intent(in) :: insideIDA ! flag to indicate if we are inside Sundials solver logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(inout) :: firstFluxCall logical(lgt),intent(inout) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - logical(lgt),intent(in) :: requireLWBal ! flag to indicate if we need longwave to be balanced ! input: state vectors real(rkind),intent(in) :: stateVec(:) ! model state vector real(rkind),intent(in) :: stateVecPrime(:) ! model state vector @@ -320,7 +318,7 @@ subroutine eval8summaSundials(& feasible=.true. ! check the feasibility of the solution - if (checkFeas) then + if (.not.insideIDA) then ! check that the canopy air space temperature is reasonable if(ixCasNrg/=integerMissing)then if(stateVec(ixCasNrg) > canopyTempMax) feasible=.false. @@ -462,7 +460,7 @@ subroutine eval8summaSundials(& call updateVarsSundials(& ! input dt_cur, & - .false., & ! intent(in): logical flag if inside Sundials solver + .false., & ! intent(in): logical flag if computing Jacobian for Sundials solver .false., & ! intent(in): logical flag to adjust temperature to account for the energy mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers @@ -656,7 +654,7 @@ subroutine eval8summaSundials(& firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution - requireLWBal, & ! intent(in): flag to indicate if we need longwave to be balanced + insideIDA, & ! intent(in): logical flag if inside Sundials solver scalarSfcMeltPond/dt, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) ! input: state variables scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) diff --git a/build/source/engine/evalDAE4IDA.f90 b/build/source/engine/evalDAE4IDA.f90 index 72d1a3164..8565fe6c8 100644 --- a/build/source/engine/evalDAE4IDA.f90 +++ b/build/source/engine/evalDAE4IDA.f90 @@ -95,13 +95,12 @@ integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_da eqns_data%nSoil, & ! intent(in): number of soil layers eqns_data%nLayers, & ! intent(in): number of layers eqns_data%nState, & ! intent(in): number of state variables in the current subset - .false., & ! intent(in): do not check for feasibility inside Sundials loop + .true., & ! intent(in): inside Sundials solver eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation + eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution - .false., & ! intent(in): do not require that longwave is balanced inside Sundials loop ! input: state vectors stateVec, & ! intent(in): model state vector stateVecPrime, & ! intent(in): model state vector diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index f41edf4e8..fb1113539 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -448,13 +448,12 @@ subroutine summaSolveSundialsIDA( & eqns_data%nSoil, & ! intent(in): number of soil layers eqns_data%nLayers, & ! intent(in): number of layers eqns_data%nState, & ! intent(in): number of state variables in the current subset - .true., & ! intent(in): check for feasibility once outside Sundials loop + .false., & ! intent(in): check for feasibility once outside Sundials loop eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution - .true., & ! intent(in): require that longwave is balanced once outside Sundials loop ! input: state vectors stateVec, & ! intent(in): model state vector stateVecPrime, & ! intent(in): model state vector diff --git a/build/source/engine/updateVarsSundials.f90 b/build/source/engine/updateVarsSundials.f90 index c45d639bd..3e4d5cd0d 100644 --- a/build/source/engine/updateVarsSundials.f90 +++ b/build/source/engine/updateVarsSundials.f90 @@ -109,7 +109,7 @@ module updateVarsSundials_module subroutine updateVarsSundials(& ! input dt, & ! intent(in): time step - insideIDA, & ! intent(in): logical flag if inside Sundials solver + computJac, & ! intent(in): logical flag if computing Jacobian for Sundials solve do_adjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers @@ -146,16 +146,16 @@ subroutine updateVarsSundials(& ! -------------------------------------------------------------------------------------------------------------------------------- implicit none ! input - real(rkind) ,intent(in) :: dt ! time step - logical(lgt) ,intent(in) :: insideIDA ! flag if inside Sundials solver - logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze - type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - real(rkind),intent(in) :: mLayerVolFracWatPrev(:) ! previous vector of total water matric potential (m) - real(rkind),intent(in) :: mLayerMatricHeadPrev(:) ! previous vector of volumetric total water content (-) - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + real(rkind) ,intent(in) :: dt ! time step + logical(lgt) ,intent(in) :: computJac ! flag if computing Jacobian for Sundials solver + logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze + type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + real(rkind),intent(in) :: mLayerVolFracWatPrev(:) ! previous vector of total water matric potential (m) + real(rkind),intent(in) :: mLayerMatricHeadPrev(:) ! previous vector of volumetric total water content (-) + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy real(rkind),intent(inout) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) real(rkind),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) @@ -365,7 +365,7 @@ subroutine updateVarsSundials(& ! update hydrology state variables for the uncoupled solution if(.not.isNrgState .and. .not.isCoupled)then - if(.not.insideIDA) stop 1 ! this does not work yet? FIX + if(.not.computJac) stop 1 ! this does not work yet? FIX ! update the total water from volumetric liquid water if(ixStateType(ixFullVector)==iname_liqCanopy .or. ixStateType(ixFullVector)==iname_liqLayer)then @@ -456,12 +456,12 @@ subroutine updateVarsSundials(& ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi, dVolHtCapBulk_dPsi0 may be wrong select case(ixDomainType) case(iname_veg) - if(insideIDA)then + if(computJac)then fLiq = fracLiquid(xTemp,snowfrz_scale) dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) endif case(iname_snow) - if(insideIDA)then + if(computJac)then fLiq = fracLiquid(xTemp,snowfrz_scale) dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air endif @@ -469,14 +469,14 @@ subroutine updateVarsSundials(& select case( ixStateType(ixFullVector) ) case(iname_lmpLayer) dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore - if(insideIDA) d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + if(computJac) d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore case default dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - if(insideIDA) d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),& + if(computJac) d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),& vGn_n(ixControlIndex),vGn_m(ixControlIndex)) end select ! dVolHtCapBulk_dPsi0 uses the derivative in water retention curve above critical temp w.r.t.state variable dVolTot_dPsi0 - if(insideIDA)then + if(computJac)then dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use if(xTemp< Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) if(xTemp>=Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_water * Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) @@ -491,7 +491,7 @@ subroutine updateVarsSundials(& case(iname_veg) dFracLiqVeg_dTkCanopy = dFracLiq_dTk(xTemp,snowfrz_scale) dTheta_dTkCanopy = dFracLiqVeg_dTkCanopy * scalarCanopyWatTrial/(iden_water*canopyDepth) - if(insideIDA)then + if(computJac)then fLiq = fracLiquid(xTemp,snowfrz_scale) d2Theta_dTkCanopy2 = 2._rkind * snowfrz_scale**2._rkind * ( (Tfreeze - xTemp) * 2._rkind * fLiq * dFracLiqVeg_dTkCanopy - fLiq**2._rkind ) * scalarCanopyWatTrial/(iden_water*canopyDepth) dVolHtCapBulk_dTkCanopy = iden_water * (-Cp_ice + Cp_water) * dTheta_dTkCanopy !same as snow but there is no derivative in air @@ -499,7 +499,7 @@ subroutine updateVarsSundials(& case(iname_snow) dFracLiqSnow_dTk(iLayer) = dFracLiq_dTk(xTemp,snowfrz_scale) mLayerdTheta_dTk(iLayer) = dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatTrial(iLayer) - if(insideIDA)then + if(computJac)then fLiq = fracLiquid(xTemp,snowfrz_scale) mLayerd2Theta_dTk2(iLayer) = 2._rkind * snowfrz_scale**2._rkind * ( (Tfreeze - xTemp) * 2._rkind * fLiq * dFracLiqSnow_dTk(iLayer) - fLiq**2._rkind ) * mLayerVolFracWatTrial(iLayer) dVolHtCapBulk_dTk(iLayer) = ( iden_water * (-Cp_ice + Cp_water) + iden_air * (iden_water/iden_ice - 1._rkind) * Cp_air ) * mLayerdTheta_dTk(iLayer) @@ -507,7 +507,7 @@ subroutine updateVarsSundials(& case(iname_soil) dFracLiqSnow_dTk(iLayer) = 0._rkind !dTheta_dTk(xTemp,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex))/ mLayerVolFracWatTrial(iLayer) mLayerdTheta_dTk(iLayer) = dTheta_dTk(xTemp,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - if(insideIDA)then + if(computJac)then mLayerd2Theta_dTk2(iLayer) = d2Theta_dTk2(xTemp,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) dVolHtCapBulk_dTk(iLayer) = (-iden_ice * Cp_ice + iden_water * Cp_water) * mLayerdTheta_dTk(iLayer) endif @@ -598,11 +598,11 @@ subroutine updateVarsSundials(& ! compute volumetric fraction of liquid water and ice call updateSoilSundials(& dt, & ! intent(in) : time step - insideIDA, & ! intent(in) : logical flag if inside Sundials solver + computJac, & ! intent(in) : logical flag if inside Sundials solver xTemp, & ! intent(in) : temperature (K) mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) - mLayerMatricHeadPrev(ixControlIndex), & ! intent(in) : previous values, will be same as current if insideIDA - mLayerVolFracWatPrev(iLayer), & ! intent(in) : previous values, will be same as current if insideIDA + mLayerMatricHeadPrev(ixControlIndex), & ! intent(in) : previous values, will be same as current if computJac + mLayerVolFracWatPrev(iLayer), & ! intent(in) : previous values, will be same as current if computJac mLayerTempPrime(iLayer), & ! intent(in) : temperature time derivative (K/s) mLayerMatricHeadPrime(ixControlIndex), & ! intent(in) : total water matric potential time derivative (m/s) vGn_alpha(ixControlIndex), & ! intent(in) : van Genutchen "alpha" parameter @@ -643,7 +643,7 @@ subroutine updateVarsSundials(& ! check the need to adjust temperature (will always be false if inside solver) ! can be true if inside varSubstepSundials, outside solver, but currently will not work so turn off - if(do_adjustTemp .and. insideIDA)then + if(do_adjustTemp .and. computJac)then ! get the melt energy meltNrg = merge(LH_fus*iden_ice, LH_fus*iden_water, ixDomainType==iname_snow) diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index e0158f7e4..9a60934a2 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -760,7 +760,7 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux call updateVarsSundials(& ! input dt, & - .false., & ! intent(in): logical flag if inside Sundials solver + .false., & ! intent(in): logical flag if computing Jacobian for Sundials solver doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 2ee7c93b1..6c4ba0600 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -131,7 +131,7 @@ subroutine vegNrgFlux(& firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - requireLWBal, & ! intent(in): flag to indicate if we need longwave to be balanced + insideIDA, & ! intent(in): flag to indicate inside Sundials Solver (do not require longwave to be balanced) ! input: model state variables upperBoundTemp, & ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature @@ -218,10 +218,10 @@ subroutine vegNrgFlux(& ! * dummy variables ! --------------------------------------------------------------------------------------- ! input: model control - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(in) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: requireLWBal ! flag to indicate if we need longwave to be balanced + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(in) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: insideIDA ! flag if inside Sundials solver ! input: model state variables real(rkind),intent(in) :: upperBoundTemp ! temperature of the upper boundary (K) --> NOTE: use air temperature real(rkind),intent(in) :: canairTempTrial ! trial value of canopy air space temperature (K) @@ -985,7 +985,7 @@ subroutine vegNrgFlux(& ! input: model control ixDerivMethod, & ! intent(in): method used to calculate flux derivatives computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation - requireLWBal, & ! intent(in): flag to indicate if we need longwave to be balanced + insideIDA, & ! intent(in): flag to indicate inside Sundials Solver (do not require longwave to be balanced) ! input: canopy and ground temperature canopyTempTrial, & ! intent(in): temperature of the vegetation canopy (K) groundTempTrial, & ! intent(in): temperature of the ground surface (K) @@ -1781,7 +1781,7 @@ subroutine longwaveBal(& ! input: model control ixDerivMethod, & ! intent(in): choice of method used to compute derivative (analytical or numerical) computeVegFlux, & ! intent(in): flag to compute fluxes over vegetati - requireLWBal, & ! intent(in): flag to indicate if we need longwave to be balanced + insideIDA, & ! intent(in): flag to indicate inside Sundials Solver (do not require longwave to be balanced) ! input: canopy and ground temperature canopyTemp, & ! intent(in): canopy temperature (K) groundTemp, & ! intent(in): ground temperature (K) @@ -1818,7 +1818,7 @@ subroutine longwaveBal(& ! input: model control integer(i4b),intent(in) :: ixDerivMethod ! choice of method used to compute derivative (analytical or numerical) logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: requireLWBal ! flag to indicate if we need longwave to be balanced + logical(lgt),intent(in) :: insideIDA ! flag if inside Sundials solver ! input: canopy and ground temperature real(rkind),intent(in) :: canopyTemp ! canopy temperature (K) real(rkind),intent(in) :: groundTemp ! ground temperature (K) @@ -1952,7 +1952,7 @@ subroutine longwaveBal(& !print*, 'LWNetUbound = ', LWNetUbound ! check the flux balance - if(requireLWBal)then + if(.not.insideIDA)then fluxBalance = LWNetUbound - (LWNetCanopy + LWNetGround) if(abs(fluxBalance) > fluxTolerance)then print*, 'fluxBalance = ', fluxBalance From cd3e9e9123a4a9b8a8861ec549ad812d72157c75 Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 22 Sep 2022 22:51:58 +0000 Subject: [PATCH 0342/1472] added BE Flag --- build/source/engine/mDecisions.f90 | 13 ----- build/source/engine/opSplittin.f90 | 34 ++++++----- build/source/engine/soilLiqFlx.f90 | 93 +++++++++++++++--------------- 3 files changed, 65 insertions(+), 75 deletions(-) diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index 05df4d241..c9b5f1e8c 100644 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -149,9 +149,6 @@ module mDecisions_module ! look-up values for the choice of energy equation integer(i4b),parameter,public :: enthalpyFD = 323 ! enthalpyFD integer(i4b),parameter,public :: closedForm = 324 ! closedForm -! look-up values for the choice of DAE solver -integer(i4b),parameter,public :: sundialIDA = 325 ! IDA solver form Sundials package -integer(i4b),parameter,public :: backwEuler = 326 ! backward Euler method implemented by Martyn ! ----------------------------------------------------------------------------------------------------------- contains @@ -419,16 +416,6 @@ subroutine mDecisions(err,message) ! err=10; message=trim(message)//"unknown Cp computation [option="//trim(model_decisions(iLookDECISIONS%howHeatCap)%cDecision)//"]"; return end select - ! how to solve the system of differential equations -select case(trim(model_decisions(iLookDECISIONS%diffEqSolv)%cDecision)) -case('sundialIDA'); model_decisions(iLookDECISIONS%diffEqSolv)%iDecision = sundialIDA ! enthalpyFD -case('backwEuler'); model_decisions(iLookDECISIONS%diffEqSolv)%iDecision = backwEuler ! closedForm -case default - ! TODO: after adding diffEqSolv decision in corresponding file we should delete the next line - model_decisions(iLookDECISIONS%diffEqSolv)%iDecision = sundialIDA - ! err=10; message=trim(message)//"unknown DAE solver [option="//trim(model_decisions(iLookDECISIONS%diffEqSolv)%cDecision)//"]"; return -end select - ! identify the method used to calculate flux derivatives select case(trim(model_decisions(iLookDECISIONS%fDerivMeth)%cDecision)) case('numericl'); model_decisions(iLookDECISIONS%fDerivMeth)%iDecision = numerical ! numerical diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index a6bcf9f31..f9ee91b87 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -113,8 +113,8 @@ module opSplittin_module qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization bigBucket, & ! a big bucket (lumped aquifer model) noExplicit, & ! no explicit groundwater parameterization - sundialIDA, & ! IDA solver from Sundials package - backwEuler ! backward Euler method + sundials, & ! SUNDIALS/IDA solution + bEuler ! home-grown backward Euler solution with long time step ! safety: set private unless specified otherwise implicit none @@ -305,6 +305,7 @@ subroutine opSplittin(& ! model decisions ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical method, backward Euler or SUNDIALS/IDA ! domain boundary conditions airtemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) ! vector of energy and hydrology indices for the snow and soil domains @@ -363,11 +364,11 @@ subroutine opSplittin(& ! initialize error control err=0; message="opSplittin/" - ! we just solve the fully coupled problem by ida - select case(model_decisions(iLookDECISIONS%diffEqSolv)%iDecision) - case(sundialIDA); nCoupling = 1 - case(backwEuler); nCoupling = 2 - case default; err=20; message=trim(message)//'expect case to be sundialIDA or backwEuler'; return + ! we just solve the fully coupled problem by with Sundials for now + select case(ixNumericalMethod) + case(sundials); nCoupling = 1 + case(bEuler); nCoupling = 2 + case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return end select ! ***** @@ -774,11 +775,11 @@ subroutine opSplittin(& if(ixSolution==scalar) numberScalarSolutions = numberScalarSolutions + 1 ! solve variable subset for one full time step - select case(model_decisions(iLookDECISIONS%diffEqSolv)%iDecision) - case(sundialIDA) + select case(ixNumericalMethod) + case(sundials) call varSubstepSundials(& ! input: model control - dt, & ! intent(inout) : time step (s) + dt, & ! intent(in) : time step (s) dtInit, & ! intent(in) : initial time step (seconds) dt_min, & ! intent(in) : minimum time step (seconds) nSubset, & ! intent(in) : total number of variables in the state subset @@ -812,10 +813,13 @@ subroutine opSplittin(& tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt dt_out, & ! intent(out) err,cmessage) ! intent(out) : error code and error message - case(backwEuler) + + dt = dt_out + + case(bEuler) call varSubstep(& ! input: model control - dt, & ! intent(inout) : time step (s) + dt, & ! intent(in) : time step (s) dtInit, & ! intent(in) : initial time step (seconds) dt_min, & ! intent(in) : minimum time step (seconds) nSubset, & ! intent(in) : total number of variables in the state subset @@ -849,11 +853,9 @@ subroutine opSplittin(& tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt err,cmessage) ! intent(out) : error code and error message ! check - case default; err=20; message=trim(message)//'expect case to backwEuler or sundialIDA'; return + case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return end select - - dt = dt_out - + if(err/=0)then message=trim(message)//trim(cmessage) if(err>0) return diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index 57446cf74..7de450187 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -1319,6 +1319,7 @@ subroutine surfaceFlx(& ! initialize error control err=0; message="surfaceFlx/" + ! initialize derivatives dq_dHydStateVec(:) = 0._rkind dq_dNrgStateVec(:) = 0._rkind @@ -1472,13 +1473,13 @@ subroutine surfaceFlx(& dfInfRaw(:) = -qSurfScale*dfracCap(:) * exp(-qSurfScale*(1._rkind - fracCap)) dInfilArea_dTk(1:nSoil) = 0.5_rkind*dfInfRaw(:) * (1._rkind + fInfRaw/sqrt(fInfRaw**2._rkind + scaleFactor)) else ! scalarInfilArea = 1._rkind - dInfilArea_dWat(:) = 0._rkind - dInfilArea_dTk(:) = 0._rkind + dInfilArea_dWat(1:nSoil) = 0._rkind + dInfilArea_dTk(1:nSoil) = 0._rkind endif else scalarInfilArea = 1._rkind - dInfilArea_dWat(:) = 0._rkind - dInfilArea_dTk(:) = 0._rkind + dInfilArea_dWat(1:nSoil) = 0._rkind + dInfilArea_dTk(1:nSoil) = 0._rkind endif dInfilArea_dWat(0) = 0._rkind dInfilArea_dTk(0) = 0._rkind @@ -1498,56 +1499,56 @@ subroutine surfaceFlx(& !scalarFrozenArea = 1._rkind - gammp(alpha,xLimg) ! fraction of frozen area !if we use this, we will have a derivative of scalarFrozenArea w.r.t. water and temperature in each layer (through mLayerVolFracIce) scalarFrozenArea = 0._rkind - dFrozenArea_dWat(:) = 0._rkind - dFrozenArea_dTk(:) = 0._rkind + dFrozenArea_dWat(1:nSoil) = 0._rkind + dFrozenArea_dTk(1:nSoil) = 0._rkind else scalarFrozenArea = 0._rkind - dFrozenArea_dWat(:) = 0._rkind - dFrozenArea_dTk(:) = 0._rkind + dFrozenArea_dWat(1:nSoil) = 0._rkind + dFrozenArea_dTk(1:nSoil) = 0._rkind end if dFrozenArea_dWat(0) = 0._rkind dFrozenArea_dTk(0) = 0._rkind !print*, 'scalarFrozenArea, rootZoneIce = ', scalarFrozenArea, rootZoneIce - end if ! (if desire to compute infiltration) - - ! compute infiltration (m s-1) - scalarSurfaceInfiltration = (1._rkind - scalarFrozenArea)*scalarInfilArea*min(scalarRainPlusMelt,xMaxInfilRate) - ! compute infiltration derivative for layers not at surface - ! TODO: VERIFY the first part of the if statement - ! I added the 0._rkind to the (0) index in the if condition to get rid of an uninitalized value error - if (xMaxInfilRate < scalarRainPlusMelt) then ! = dXMaxInfilRate_d - dInfilRate_dWat(:) = 0._rkind ! set to 0 to get rid of any unitialized values that may exit after going from 1:nsoil - dInfilRate_dTk(:) = 0._rkind ! set to 0 to get rid of any unitialized values that may exit after going from 1:nsoil - dInfilRate_dWat(1:nSoil) = dXMaxInfilRate_dWat(:) - dInfilRate_dTk(1:nSoil) = dXMaxInfilRate_dTk(:) - else ! = dRainPlusMelt_d only dependent on canopy - dInfilRate_dWat(0) = above_soilLiqFluxDeriv*above_soilFracLiq - dInfilRate_dTk(0) = above_soilLiqFluxDeriv*above_soildLiq_dTk - dInfilRate_dWat(1:nSoil) = 0._rkind !only calculate for layers that are not the surface - dInfilRate_dTk(1:nSoil) = 0._rkind !only calculate for layers that are not the surface - ! dependent on above layer (canopy or snow) water and temp - endif - - ! dq w.r.t. infiltration only, scalarRainPlusMelt accounted for in computeJacDAE_module - dq_dHydStateVec(:) = (1._rkind - scalarFrozenArea) * ( dInfilArea_dWat(:)*min(scalarRainPlusMelt,xMaxInfilRate) + scalarInfilArea*dInfilRate_dWat(:) ) +& - (-dFrozenArea_dWat(:))*scalarInfilArea*min(scalarRainPlusMelt,xMaxInfilRate) - dq_dNrgStateVec(:) = (1._rkind - scalarFrozenArea) * ( dInfilArea_dTk(:) *min(scalarRainPlusMelt,xMaxInfilRate) + scalarInfilArea*dInfilRate_dTk(:) ) +& - (-dFrozenArea_dTk(:)) *scalarInfilArea*min(scalarRainPlusMelt,xMaxInfilRate) - - ! compute surface runoff (m s-1) - scalarSurfaceRunoff = scalarRainPlusMelt - scalarSurfaceInfiltration - !print*, 'scalarRainPlusMelt, xMaxInfilRate = ', scalarRainPlusMelt, xMaxInfilRate - !print*, 'scalarSurfaceInfiltration, scalarSurfaceRunoff = ', scalarSurfaceInfiltration, scalarSurfaceRunoff - !print*, '(1._rkind - scalarFrozenArea), (1._rkind - scalarFrozenArea)*scalarInfilArea = ', (1._rkind - scalarFrozenArea), (1._rkind - scalarFrozenArea)*scalarInfilArea - - ! set surface hydraulic conductivity and diffusivity to missing (not used for flux condition) - surfaceHydCond = realMissing - surfaceDiffuse = realMissing - - ! ***** error check - case default; err=20; message=trim(message)//'unknown upper boundary condition for soil hydrology'; return + if (xMaxInfilRate < scalarRainPlusMelt) then ! = dXMaxInfilRate_d + dInfilRate_dWat(1:nSoil) = dXMaxInfilRate_dWat(:) + dInfilRate_dTk(1:nSoil) = dXMaxInfilRate_dTk(:) + else ! = dRainPlusMelt_d only dependent on canopy + dInfilRate_dWat(1:nSoil) = 0._rkind !only calculate for layers that are not the surface + dInfilRate_dTk(1:nSoil) = 0._rkind !only calculate for layers that are not the surface + endif + ! dependent on above layer (canopy or snow) water and temp + dInfilRate_dWat(0) = above_soilLiqFluxDeriv*above_soilFracLiq + dInfilRate_dTk(0) = above_soilLiqFluxDeriv*above_soildLiq_dTk + + ! dq w.r.t. infiltration only, scalarRainPlusMelt accounted for in computeJacob module + dq_dHydStateVec(:) = (1._rkind - scalarFrozenArea) * ( dInfilArea_dWat(:)*min(scalarRainPlusMelt,xMaxInfilRate) + scalarInfilArea*dInfilRate_dWat(:) ) +& + (-dFrozenArea_dWat(:))*scalarInfilArea*min(scalarRainPlusMelt,xMaxInfilRate) + dq_dNrgStateVec(:) = (1._rkind - scalarFrozenArea) * ( dInfilArea_dTk(:) *min(scalarRainPlusMelt,xMaxInfilRate) + scalarInfilArea*dInfilRate_dTk(:) ) +& + (-dFrozenArea_dTk(:)) *scalarInfilArea*min(scalarRainPlusMelt,xMaxInfilRate) + + else ! do not compute infiltration after first flux call in a splitting operation + dq_dHydStateVec(:) = 0._rkind + dq_dNrgStateVec(:) = 0._rkind + + end if ! (if desire to compute infiltration) + + ! compute infiltration (m s-1), if after first flux call in a splitting operation does not change + scalarSurfaceInfiltration = (1._rkind - scalarFrozenArea)*scalarInfilArea*min(scalarRainPlusMelt,xMaxInfilRate) + + ! compute surface runoff (m s-1) + scalarSurfaceRunoff = scalarRainPlusMelt - scalarSurfaceInfiltration + !print*, 'scalarRainPlusMelt, xMaxInfilRate = ', scalarRainPlusMelt, xMaxInfilRate + !print*, 'scalarSurfaceInfiltration, scalarSurfaceRunoff = ', scalarSurfaceInfiltration, scalarSurfaceRunoff + !print*, '(1._rkind - scalarFrozenArea), (1._rkind - scalarFrozenArea)*scalarInfilArea = ', (1._rkind - scalarFrozenArea), (1._rkind - scalarFrozenArea)*scalarInfilArea + + ! set surface hydraulic conductivity and diffusivity to missing (not used for flux condition) + surfaceHydCond = realMissing + surfaceDiffuse = realMissing + + ! ***** error check + case default; err=20; message=trim(message)//'unknown upper boundary condition for soil hydrology'; return end select ! (type of upper boundary condition) From d05f700bfe41ae1af75f4478329f7b950e3fe624 Mon Sep 17 00:00:00 2001 From: Kyle Date: Fri, 23 Sep 2022 14:09:49 +0000 Subject: [PATCH 0343/1472] removed diffEqSolv decision from code it is now a numerics decision to use sundials --- build/source/dshare/get_ixname.f90 | 1 - build/source/dshare/var_lookup.f90 | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 7fab5482f..e4fe3c7aa 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -96,7 +96,6 @@ function get_ixdecisions(varName) case('snowDenNew' ); get_ixdecisions=iLookDECISIONS%snowDenNew ! choice of method for new snow density case('snowUnload' ); get_ixdecisions=iLookDECISIONS%snowUnload ! choice of parameterization for snow unloading from canopy case('howHeatCap' ); get_ixdecisions=iLookDECISIONS%howHeatCap ! how to compute heat capacity in energy equation - case('diffEqSolv' ); get_ixdecisions=iLookDECISIONS%diffEqSolv ! how to solve the system of differential equations ! get to here if cannot find the variable case default get_ixdecisions = integerMissing diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 71197db27..be96c7212 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -72,7 +72,6 @@ MODULE var_lookup integer(i4b) :: subRouting = integerMissing ! choice of method for sub-grid routing integer(i4b) :: snowDenNew = integerMissing ! choice of method for new snow density integer(i4b) :: howHeatCap = integerMissing ! how to compute heat capacity in energy equation - integer(i4b) :: diffEqSolv = integerMissing ! how to solve the system of differential equations endtype iLook_decision ! *********************************************************************************************************** @@ -815,7 +814,7 @@ MODULE var_lookup type(iLook_decision),public,parameter :: iLookDECISIONS=iLook_decision( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,& - 31, 32, 33, 34, 35, 36, 37, 38, 39, 40) + 31, 32, 33, 34, 35, 36, 37, 38, 39) ! named variables: model time type(iLook_time), public,parameter :: iLookTIME =iLook_time ( 1, 2, 3, 4, 5, 6, 7) From 8927fc4923546e29090fb909a5ef725cf6037366 Mon Sep 17 00:00:00 2001 From: Kyle Date: Fri, 23 Sep 2022 14:42:09 +0000 Subject: [PATCH 0344/1472] adjusted computJacob to include the unintalized value bug fix --- build/source/engine/computJacob.f90 | 1968 +++++++++---------- build/source/engine/computJacobSundials.f90 | 4 +- 2 files changed, 985 insertions(+), 987 deletions(-) diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 7aa89161c..2681a077e 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -20,1004 +20,1002 @@ module computJacob_module -! data types -USE nrtype - -! derived types to define the data structures -USE data_types,only:& - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (rkind) - -! named variables for structure elements -USE var_lookup,only:iLookPROG ! named variables for structure elements -USE var_lookup,only:iLookDIAG ! named variables for structure elements -USE var_lookup,only:iLookINDEX ! named variables for structure elements -USE var_lookup,only:iLookDERIV ! named variables for structure elements - -! access the global print flag -USE globalData,only:globalPrintFlag - -! access missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing real number - -! domain types -USE globalData,only:iname_veg ! named variables for vegetation -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil - -! named variables to describe the state variable type -USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space -USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy -USE globalData,only:iname_watCanopy ! named variable defining the mass of water on the vegetation canopy -USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers -USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers -USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers -USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers -USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers - -! access named variables to describe the form and structure of the matrices used in the numerical solver -USE globalData,only: ku ! number of super-diagonal bands -USE globalData,only: kl ! number of sub-diagonal bands -USE globalData,only: ixDiag ! index for the diagonal band -USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix -USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix -USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix -USE globalData,only: iJac1 ! first layer of the Jacobian to print -USE globalData,only: iJac2 ! last layer of the Jacobian to print - -! constants -USE multiconst,only:& - LH_fus, & ! latent heat of fusion (J kg-1) - iden_water ! intrinsic density of liquid water (kg m-3) - -implicit none -! define constants -real(rkind),parameter :: verySmall=tiny(1.0_rkind) ! a very small number -integer(i4b),parameter :: ixBandOffset=kl+ku+1 ! offset in the band Jacobian matrix - -private -public::computJacob -contains - - ! ********************************************************************************************************** - ! public subroutine computJacob: compute the Jacobian matrix - ! ********************************************************************************************************** - subroutine computJacob(& - ! input: model control - dt, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - computeBaseflow, & ! intent(in): flag to indicate if we need to compute baseflow - ixMatrix, & ! intent(in): form of the Jacobian matrix - ! input: data structures - indx_data, & ! intent(in): index data - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! input-output: Jacobian and its diagonal - dMat, & ! intent(inout): diagonal of the Jacobian matrix - aJac, & ! intent(out): Jacobian matrix - ! output: error control - err,message) ! intent(out): error code and error message - ! ----------------------------------------------------------------------------------------------------------------- - implicit none - ! input: model control - real(rkind),intent(in) :: dt ! length of the time step (seconds) - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: computeBaseflow ! flag to indicate if computing baseflow - integer(i4b),intent(in) :: ixMatrix ! form of the Jacobian matrix - ! input: data structures - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - ! input-output: Jacobian and its diagonal - real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix - real(rkind),intent(out) :: aJac(:,:) ! Jacobian matrix - ! output variables - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------- - ! * local variables - ! -------------------------------------------------------------- - ! indices of model state variables - integer(i4b) :: jState ! index of state within the state subset - integer(i4b) :: qState ! index of cross-derivative state variable for baseflow - integer(i4b) :: nrgState ! energy state variable - integer(i4b) :: watState ! hydrology state variable - integer(i4b) :: nState ! number of state variables - ! indices of model layers - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: jLayer ! index of model layer within the full state vector (hydrology) - integer(i4b) :: pLayer ! indices of soil layers (used for the baseflow derivatives) - ! conversion factors - real(rkind) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative - ! -------------------------------------------------------------- - ! associate variables from data structures - associate(& - ! indices of model state variables - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain - ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer - ! vectors of indices for specfic state types within specific sub-domains IN THE FULL STATE VECTOR - ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain - ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain - ! vector of energy indices for the snow and soil domains - ! NOTE: states not in the subset are equal to integerMissing - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain - ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow domain - ixSoilOnlyNrg => indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the soil domain - ! vector of hydrology indices for the snow and soil domains - ! NOTE: states not in the subset are equal to integerMissing - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow domain - ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the soil domain - ! number of state variables of a specific type - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain - nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain - nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain - ! type and index of model control volume - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain - ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the domain (iname_veg, iname_snow, iname_soil) - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for specific model domains - ! mapping between states and model layers - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] list of indices in the full state vector that are in the state subset - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset in each element of the full state vector - ! derivatives in net vegetation energy fluxes w.r.t. relevant state variables - dCanairNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. canopy air temperature - dCanairNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. canopy temperature - dCanairNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dGroundTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. ground temperature - dCanopyNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. canopy air temperature - dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. canopy temperature - dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. ground temperature - dCanopyNetFlux_dCanWat => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in net canopy fluxes w.r.t. canopy total water content - dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net ground flux w.r.t. canopy air temperature - dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net ground flux w.r.t. canopy temperature - dGroundNetFlux_dCanWat => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in net ground fluxes w.r.t. canopy total water content - ! derivatives in evaporative fluxes w.r.t. relevant state variables - dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy air temperature - dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy temperature - dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. ground temperature - dCanopyEvaporation_dCanWat => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy total water content - dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy air temperature - dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy temperature - dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. ground temperature - dGroundEvaporation_dCanWat => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy total water content - ! derivatives in canopy water w.r.t canopy temperature - dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(in): [dp] derivative of canopy liquid storage w.r.t. temperature - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature - ! derivatives in canopy liquid fluxes w.r.t. canopy water - scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ,& ! intent(in): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water - ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below - dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above - dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below - ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below - dNrgFlux_dWatAbove => deriv_data%var(iLookDERIV%dNrgFlux_dWatAbove )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. water state in the layer above - dNrgFlux_dWatBelow => deriv_data%var(iLookDERIV%dNrgFlux_dWatBelow )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. water state in the layer below - ! derivatives in soil transpiration w.r.t. canopy state variables - mLayerdTrans_dTCanair => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanair )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature - mLayerdTrans_dTCanopy => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanopy )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. canopy temperature - mLayerdTrans_dTGround => deriv_data%var(iLookDERIV%mLayerdTrans_dTGround )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. ground temperature - mLayerdTrans_dCanWat => deriv_data%var(iLookDERIV%mLayerdTrans_dCanWat )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. canopy total water - ! derivatives in aquifer transpiration w.r.t. canopy state variables - dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature - dAquiferTrans_dTCanopy => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanopy )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. canopy temperature - dAquiferTrans_dTGround => deriv_data%var(iLookDERIV%dAquiferTrans_dTGround )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. ground temperature - dAquiferTrans_dCanWat => deriv_data%var(iLookDERIV%dAquiferTrans_dCanWat )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. canopy total water - ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above - iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(in): [dp(:)] derivative in vertical liquid water flux at layer interfaces - ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential - dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t matric head - dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above - dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below - dq_dHydStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dHydStateLayerSurfVec )%dat ,& ! intent(in): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers - ! derivative in baseflow flux w.r.t. aquifer storage - dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(in): [dp(:)] erivative in baseflow flux w.r.t. aquifer storage (s-1) - ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables - dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above - dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below - dq_dNrgStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dNrgStateLayerSurfVec )%dat ,& ! intent(in): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers - ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk )%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - ! derivative in bulk heat capacity w.r.t. relevant state variables - dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. temperature - ! derivatives in time - mLayerdTemp_dt => deriv_data%var(iLookDERIV%mLayerdTemp_dt )%dat ,& ! intent(in): [dp(:)] timestep change in layer temperature - scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt )%dat(1) ,& ! intent(in): [dp ] timestep change in canopy temperature - ! diagnostic variables - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) - scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) - mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] bulk volumetric heat capacity in each snow and soil layer (J m-3 K-1) - scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl)%dat(1) ,& ! intent(in): [dp] soil control on infiltration, zero or one - ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat & ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ) ! making association with data in structures - ! -------------------------------------------------------------- - ! initialize error control - err=0; message='computJacob/' - - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - ! * PART 0: PRELIMINARIES (INITIALIZE JACOBIAN AND COMPUTE TIME-VARIABLE DIAGONAL TERMS) - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - - ! get the number of state variables - nState = size(dMat) - - ! initialize the Jacobian - ! NOTE: this needs to be done every time, since Jacobian matrix is modified in the solver - aJac(:,:) = 0._rkind ! analytical Jacobian matrix - - ! compute terms in the Jacobian for vegetation (excluding fluxes) - ! NOTE: energy for vegetation is computed *within* the iteration loop as it includes phase change - if(ixVegNrg/=integerMissing)then - dMat(ixVegNrg) = scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy & - + dVolHtCapBulk_dTkCanopy * scalarCanopydTemp_dt - endif - - ! compute additional terms for the Jacobian for the snow-soil domain (excluding fluxes) - ! NOTE: energy for snow+soil is computed *within* the iteration loop as it includes phase change - do iLayer=1,nLayers - if(ixSnowSoilNrg(iLayer)/=integerMissing)then - dMat(ixSnowSoilNrg(iLayer)) = mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) & - + dVolHtCapBulk_dTk(iLayer) * mLayerdTemp_dt(iLayer) - endif - end do - - ! compute additional terms for the Jacobian for the soil domain (excluding fluxes) - do iLayer=1,nSoil - if(ixSoilOnlyHyd(iLayer)/=integerMissing)then - dMat(ixSoilOnlyHyd(iLayer)) = dVolTot_dPsi0(iLayer) + dCompress_dPsi(iLayer) - endif - end do - - ! define the form of the matrix - select case(ixMatrix) - - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - ! * PART 1: BAND MATRIX - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - case(ixBandMatrix) - - ! check - if(size(aJac,1)/=nBands .or. size(aJac,2)/=size(dMat))then - message=trim(message)//'unexpected shape of the Jacobian matrix: expect aJac(nBands,nState)' - err=20; return - endif - - ! ----- - ! * energy and liquid fluxes over vegetation... - ! --------------------------------------------- - if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) - - ! * energy fluxes with the canopy water - if(ixVegHyd/=integerMissing)then - - ! * cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) - if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixCasNrg),ixCasNrg) = -dCanopyEvaporation_dTCanair*dt - ! dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy is the derivative in throughfall and canopy drainage with canopy temperature - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixVegNrg),ixVegNrg) = -dCanopyEvaporation_dTCanopy*dt + dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy - ! * liquid water fluxes for vegetation canopy (-), dt*scalarFracLiqVeg*scalarCanopyLiqDeriv is the derivative in throughfall and canopy drainage with canopy water - aJac(ixDiag, ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanWat - scalarCanopyLiqDeriv)*dt + 1._rkind - if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixTopNrg),ixTopNrg) = -dCanopyEvaporation_dTGround*dt - - ! * cross-derivative terms w.r.t. canopy water (kg-1 m2) - if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water - - ! * cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) - ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth & - + dVolHtCapBulk_dCanWat * scalarCanopydTemp_dt - (dt/canopyDepth) * dCanopyNetFlux_dCanWat ! dF/dLiq - if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) - endif - - ! * cross-derivative terms w.r.t. canopy temperature (K-1) + ! data types + USE nrtype + + ! derived types to define the data structures + USE data_types,only:& + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength ! data vector with variable length dimension (rkind) + + ! named variables for structure elements + USE var_lookup,only:iLookPROG ! named variables for structure elements + USE var_lookup,only:iLookDIAG ! named variables for structure elements + USE var_lookup,only:iLookINDEX ! named variables for structure elements + USE var_lookup,only:iLookDERIV ! named variables for structure elements + + ! access the global print flag + USE globalData,only:globalPrintFlag + + ! access missing values + USE globalData,only:integerMissing ! missing integer + USE globalData,only:realMissing ! missing real number + + ! domain types + USE globalData,only:iname_veg ! named variables for vegetation + USE globalData,only:iname_snow ! named variables for snow + USE globalData,only:iname_soil ! named variables for soil + + ! named variables to describe the state variable type + USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space + USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy + USE globalData,only:iname_watCanopy ! named variable defining the mass of water on the vegetation canopy + USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers + USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers + USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers + USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers + USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers + + ! access named variables to describe the form and structure of the matrices used in the numerical solver + USE globalData,only: ku ! number of super-diagonal bands + USE globalData,only: kl ! number of sub-diagonal bands + USE globalData,only: ixDiag ! index for the diagonal band + USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix + USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix + USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix + USE globalData,only: iJac1 ! first layer of the Jacobian to print + USE globalData,only: iJac2 ! last layer of the Jacobian to print + + ! constants + USE multiconst,only:& + LH_fus, & ! latent heat of fusion (J kg-1) + iden_water ! intrinsic density of liquid water (kg m-3) + + implicit none + ! define constants + real(rkind),parameter :: verySmall=tiny(1.0_rkind) ! a very small number + integer(i4b),parameter :: ixBandOffset=kl+ku+1 ! offset in the band Jacobian matrix + + private + public::computJacob + contains + + ! ********************************************************************************************************** + ! public subroutine computJacob: compute the Jacobian matrix + ! ********************************************************************************************************** + subroutine computJacob(& + ! input: model control + dt, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + computeBaseflow, & ! intent(in): flag to indicate if we need to compute baseflow + ixMatrix, & ! intent(in): form of the Jacobian matrix + ! input: data structures + indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables + dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! input-output: Jacobian and its diagonal + dMat, & ! intent(inout): diagonal of the Jacobian matrix + aJac, & ! intent(out): Jacobian matrix + ! output: error control + err,message) ! intent(out): error code and error message + ! ----------------------------------------------------------------------------------------------------------------- + implicit none + ! input: model control + real(rkind),intent(in) :: dt ! length of the time step (seconds) + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: computeBaseflow ! flag to indicate if computing baseflow + integer(i4b),intent(in) :: ixMatrix ! form of the Jacobian matrix + ! input: data structures + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + ! input-output: Jacobian and its diagonal + real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix + real(rkind),intent(out) :: aJac(:,:) ! Jacobian matrix + ! output variables + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------- + ! * local variables + ! -------------------------------------------------------------- + ! indices of model state variables + integer(i4b) :: jState ! index of state within the state subset + integer(i4b) :: qState ! index of cross-derivative state variable for baseflow + integer(i4b) :: nrgState ! energy state variable + integer(i4b) :: watState ! hydrology state variable + integer(i4b) :: nState ! number of state variables + ! indices of model layers + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: jLayer ! index of model layer within the full state vector (hydrology) + integer(i4b) :: pLayer ! indices of soil layers (used for the baseflow derivatives) + ! conversion factors + real(rkind) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative + ! -------------------------------------------------------------- + ! associate variables from data structures + associate(& + ! indices of model state variables + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain + ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer + ! vectors of indices for specfic state types within specific sub-domains IN THE FULL STATE VECTOR + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + ! vector of energy indices for the snow and soil domains + ! NOTE: states not in the subset are equal to integerMissing + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow domain + ixSoilOnlyNrg => indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the soil domain + ! vector of hydrology indices for the snow and soil domains + ! NOTE: states not in the subset are equal to integerMissing + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow domain + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the soil domain + ! number of state variables of a specific type + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain + nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + ! type and index of model control volume + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain + ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the domain (iname_veg, iname_snow, iname_soil) + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for specific model domains + ! mapping between states and model layers + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] list of indices in the full state vector that are in the state subset + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset in each element of the full state vector + ! derivatives in net vegetation energy fluxes w.r.t. relevant state variables + dCanairNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. canopy air temperature + dCanairNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. canopy temperature + dCanairNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dGroundTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. ground temperature + dCanopyNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. canopy air temperature + dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. canopy temperature + dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. ground temperature + dCanopyNetFlux_dCanWat => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in net canopy fluxes w.r.t. canopy total water content + dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net ground flux w.r.t. canopy air temperature + dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net ground flux w.r.t. canopy temperature + dGroundNetFlux_dCanWat => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in net ground fluxes w.r.t. canopy total water content + ! derivatives in evaporative fluxes w.r.t. relevant state variables + dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy air temperature + dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy temperature + dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. ground temperature + dCanopyEvaporation_dCanWat => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy total water content + dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy air temperature + dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy temperature + dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. ground temperature + dGroundEvaporation_dCanWat => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy total water content + ! derivatives in canopy water w.r.t canopy temperature + dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(in): [dp] derivative of canopy liquid storage w.r.t. temperature + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + ! derivatives in canopy liquid fluxes w.r.t. canopy water + scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ,& ! intent(in): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water + ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below + dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above + dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below + ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below + dNrgFlux_dWatAbove => deriv_data%var(iLookDERIV%dNrgFlux_dWatAbove )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. water state in the layer above + dNrgFlux_dWatBelow => deriv_data%var(iLookDERIV%dNrgFlux_dWatBelow )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. water state in the layer below + ! derivatives in soil transpiration w.r.t. canopy state variables + mLayerdTrans_dTCanair => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanair )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + mLayerdTrans_dTCanopy => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanopy )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. canopy temperature + mLayerdTrans_dTGround => deriv_data%var(iLookDERIV%mLayerdTrans_dTGround )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. ground temperature + mLayerdTrans_dCanWat => deriv_data%var(iLookDERIV%mLayerdTrans_dCanWat )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. canopy total water + ! derivatives in aquifer transpiration w.r.t. canopy state variables + dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + dAquiferTrans_dTCanopy => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanopy )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. canopy temperature + dAquiferTrans_dTGround => deriv_data%var(iLookDERIV%dAquiferTrans_dTGround )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. ground temperature + dAquiferTrans_dCanWat => deriv_data%var(iLookDERIV%dAquiferTrans_dCanWat )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. canopy total water + ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above + iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(in): [dp(:)] derivative in vertical liquid water flux at layer interfaces + ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t matric head + dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above + dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + dq_dHydStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dHydStateLayerSurfVec )%dat ,& ! intent(in): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers + ! derivative in baseflow flux w.r.t. aquifer storage + dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(in): [dp(:)] erivative in baseflow flux w.r.t. aquifer storage (s-1) + ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables + dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above + dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + dq_dNrgStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dNrgStateLayerSurfVec )%dat ,& ! intent(in): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers + ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk )%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + ! derivative in bulk heat capacity w.r.t. relevant state variables + dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. temperature + ! derivatives in time + mLayerdTemp_dt => deriv_data%var(iLookDERIV%mLayerdTemp_dt )%dat ,& ! intent(in): [dp(:)] timestep change in layer temperature + scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt )%dat(1) ,& ! intent(in): [dp ] timestep change in canopy temperature + ! diagnostic variables + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] bulk volumetric heat capacity in each snow and soil layer (J m-3 K-1) + scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl)%dat(1) ,& ! intent(in): [dp] soil control on infiltration, zero or one + ! canopy and layer depth + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat & ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ) ! making association with data in structures + ! -------------------------------------------------------------- + ! initialize error control + err=0; message='computJacob/' + + ! ********************************************************************************************************************************************************* + ! ********************************************************************************************************************************************************* + ! * PART 0: PRELIMINARIES (INITIALIZE JACOBIAN AND COMPUTE TIME-VARIABLE DIAGONAL TERMS) + ! ********************************************************************************************************************************************************* + ! ********************************************************************************************************************************************************* + + ! get the number of state variables + nState = size(dMat) + + ! initialize the Jacobian + ! NOTE: this needs to be done every time, since Jacobian matrix is modified in the solver + aJac(:,:) = 0._rkind ! analytical Jacobian matrix + + ! compute terms in the Jacobian for vegetation (excluding fluxes) + ! NOTE: energy for vegetation is computed *within* the iteration loop as it includes phase change if(ixVegNrg/=integerMissing)then - if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarCanopyLiqDeriv*dCanLiq_dTcanopy)/iden_water + dMat(ixVegNrg) = scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy & + + dVolHtCapBulk_dTkCanopy * scalarCanopydTemp_dt endif - - ! * energy fluxes with the canopy air space (J m-3 K-1) - if(ixCasNrg/=integerMissing)then - aJac(ixDiag, ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixCasNrg,ixVegNrg),ixVegNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanopyTemp) - if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixCasNrg,ixTopNrg),ixTopNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dGroundTemp) - endif - - ! * energy fluxes with the vegetation canopy (J m-3 K-1) - if(ixVegNrg/=integerMissing)then - if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixCasNrg),ixCasNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanairTemp) - aJac(ixDiag, ixVegNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanopyTemp) + dMat(ixVegNrg) - if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixTopNrg),ixTopNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dGroundTemp) - endif - - ! * energy fluxes with the surface (J m-3 K-1) - if(ixTopNrg/=integerMissing)then - if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanairTemp) - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanopyTemp) - endif - - endif ! if there is a need to compute energy fluxes within vegetation - - ! ----- - ! * energy fluxes for the snow+soil domain... - ! ------------------------------------------- - if(nSnowSoilNrg>0)then - do iLayer=1,nLayers ! loop through all layers in the snow+soil domain - - ! check if the state is in the subset - if(ixSnowSoilNrg(iLayer)==integerMissing) cycle - - ! - define index within the state subset and the full state vector - jState = ixSnowSoilNrg(iLayer) ! index within the state subset - - ! - diagonal elements - aJac(ixDiag,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) - - ! - lower-diagonal elements - if(iLayer>1)then - if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowSoilNrg(iLayer-1),jState),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) - endif - - ! - upper diagonal elements - if(iLayer0)then - do iLayer=1,nSnow ! loop through layers in the snow domain - - ! - check that the snow layer is desired - if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle - - ! - define state indices for the current layer - watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - - ! compute factor to convert liquid water derivative to total water derivative - select case( ixHydType(iLayer) ) - case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) - case default; convLiq2tot = 1._rkind - end select - - ! - diagonal elements - aJac(ixDiag,watState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot + dMat(watState) - - ! - lower-diagonal elements - if(iLayer>1)then - if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyHyd(iLayer-1),watState),watState) = 0._rkind ! sub-diagonal: no dependence on other layers - endif - - ! - upper diagonal elements - if(iLayer0 .and. nSnowOnlyNrg>0)then - do iLayer=1,nSnow ! loop through layers in the snow domain - - ! - check that the snow layer is desired - if(ixSnowOnlyNrg(iLayer)==integerMissing) cycle - - ! (define the energy state) - nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector - - ! - define state indices for the current layer - watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - - if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) - - ! - include derivatives of energy fluxes w.r.t water fluxes for current layer - aJac(ixOffDiag(nrgState,watState),watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water & - + dVolHtCapBulk_dTheta(iLayer) * mLayerdTemp_dt(iLayer) & - + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) ! (dF/dLiq) - - ! - include derivatives of water fluxes w.r.t energy fluxes for current layer - aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) - - ! (cross-derivative terms for the layer below) - if(iLayer < nSnow)then - aJac(ixOffDiag(ixSnowOnlyHyd(iLayer+1),nrgState),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 - endif ! (if there is a water state in the layer below the current layer in the given state subset) - - ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above - if(iLayer>1)then - if(ixSnowOnlyNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyNrg(iLayer-1),watState),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) ) - endif - - ! (cross-derivative terms for the layer below) - if(iLayer0)then !bottom snow layer and there is soil below - if(ixSoilOnlyNrg(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyNrg(1),watState),watState) = (dt/mLayerDepth(nSnow+1))*(-dNrgFlux_dWatAbove(nSnow) ) - endif - - endif ! (if the energy state for the current layer is within the state subset) - - end do ! (looping through snow layers) - endif ! (if there are state variables for both water and energy in the snow domain) - - ! ----- - ! * liquid water fluxes for the soil domain... - ! -------------------------------------------- - if(nSoilOnlyHyd>0)then + end do + + ! compute additional terms for the Jacobian for the soil domain (excluding fluxes) do iLayer=1,nSoil - - ! - check that the soil layer is desired - if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle - - ! - define state indices - watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset - - ! - define indices of the soil layers - jLayer = iLayer+nSnow ! index of layer in the snow+soil vector - - ! - compute the diagonal elements - ! all terms *excluding* baseflow - aJac(ixDiag,watState) = (dt/mLayerDepth(jLayer))*(-dq_dHydStateBelow(iLayer-1) + dq_dHydStateAbove(iLayer)) + dMat(watState) - - ! - compute the lower-diagonal elements - if(iLayer>1)then - if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),watState),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) + if(ixSoilOnlyHyd(iLayer)/=integerMissing)then + dMat(ixSoilOnlyHyd(iLayer)) = dVolTot_dPsi0(iLayer) + dCompress_dPsi(iLayer) endif - - ! - compute the upper-diagonal elements - if(iLayer0)then !have snow above first soil layer - if(ixSnowOnlyHyd(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing)then - aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) - endif - elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) - if(ixVegHyd/=integerMissing .and. ixTopHyd/=integerMissing)then - aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) - endif - endif - endif ! (if the subset includes hydrology state variables in the soil domain) - - ! ----- - ! * liquid water fluxes for the aquifer... - ! ---------------------------------------- - if(ixAqWat/=integerMissing) then - aJac(ixDiag,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) - aJac(ixOffDiag(ixAqWat,ixSoilOnlyNrg(nSoil)),ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk - aJac(ixOffDiag(ixAqWat,ixSoilOnlyHyd(nSoil)),ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat - ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) - if(computeVegFlux)then - aJac(ixOffDiag(ixAqWat,ixCasNrg),ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) - aJac(ixOffDiag(ixAqWat,ixVegNrg),ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) - aJac(ixOffDiag(ixAqWat,ixVegHyd),ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 - aJac(ixOffDiag(ixAqWat,ixTopNrg),ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) - endif - endif - - ! ----- - ! * cross derivatives in the soil domain... - ! ----------------------------------------------------------------------------- - if(nSoilOnlyHyd>0 .and. nSoilOnlyNrg>0)then - do iLayer=1,nSoilOnlyNrg - - ! - check that the soil layer is desired - if(ixSoilOnlyNrg(iLayer)==integerMissing) cycle - - ! - define indices of the soil layers - jLayer = iLayer+nSnow ! index of layer in the snow+soil vector - - ! - define the energy state variable - nrgState = ixNrgLayer(jLayer) ! index within the full state vector - - ! - define index of hydrology state variable within the state subset - watState = ixSoilOnlyHyd(iLayer) - - ! only compute derivatives if the water state for the current layer is within the state subset - if(watstate/=integerMissing)then - - ! - include derivatives in liquid water fluxes w.r.t. temperature for current layer - aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance - - ! - compute lower diagonal elements - if(iLayer>1)then - if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),nrgState),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 - endif - - ! compute upper-diagonal elements - if(iLayer0)then + do iLayer=1,nLayers ! loop through all layers in the snow+soil domain + + ! check if the state is in the subset + if(ixSnowSoilNrg(iLayer)==integerMissing) cycle + + ! - define index within the state subset and the full state vector + jState = ixSnowSoilNrg(iLayer) ! index within the state subset + + ! - diagonal elements + aJac(ixDiag,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) + + ! - lower-diagonal elements + if(iLayer>1)then + if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowSoilNrg(iLayer-1),jState),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) + endif + + ! - upper diagonal elements + if(iLayer0)then + do iLayer=1,nSnow ! loop through layers in the snow domain + + ! - check that the snow layer is desired + if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle + + ! - define state indices for the current layer + watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset + + ! compute factor to convert liquid water derivative to total water derivative + select case( ixHydType(iLayer) ) + case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) + case default; convLiq2tot = 1._rkind + end select + + ! - diagonal elements + aJac(ixDiag,watState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot + dMat(watState) + + ! - lower-diagonal elements + if(iLayer>1)then + if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyHyd(iLayer-1),watState),watState) = 0._rkind ! sub-diagonal: no dependence on other layers + endif + + ! - upper diagonal elements + if(iLayer0 .and. nSnowOnlyNrg>0)then + do iLayer=1,nSnow ! loop through layers in the snow domain + + ! - check that the snow layer is desired + if(ixSnowOnlyNrg(iLayer)==integerMissing) cycle + + ! (define the energy state) + nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector + + ! - define state indices for the current layer + watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset + + if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) + + ! - include derivatives of energy fluxes w.r.t water fluxes for current layer + aJac(ixOffDiag(nrgState,watState),watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water & + + dVolHtCapBulk_dTheta(iLayer) * mLayerdTemp_dt(iLayer) & + + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) ! (dF/dLiq) + + ! - include derivatives of water fluxes w.r.t energy fluxes for current layer + aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) + + ! (cross-derivative terms for the layer below) + if(iLayer < nSnow)then + aJac(ixOffDiag(ixSnowOnlyHyd(iLayer+1),nrgState),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 + endif ! (if there is a water state in the layer below the current layer in the given state subset) + + ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above + if(iLayer>1)then + if(ixSnowOnlyNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyNrg(iLayer-1),watState),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) ) + endif + + ! (cross-derivative terms for the layer below) + if(iLayer0)then !bottom snow layer and there is soil below + if(ixSoilOnlyNrg(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyNrg(1),watState),watState) = (dt/mLayerDepth(nSnow+1))*(-dNrgFlux_dWatAbove(nSnow) ) + endif + + endif ! (if the energy state for the current layer is within the state subset) + + end do ! (looping through snow layers) + endif ! (if there are state variables for both water and energy in the snow domain) + + ! ----- + ! * liquid water fluxes for the soil domain... + ! -------------------------------------------- + if(nSoilOnlyHyd>0)then + do iLayer=1,nSoil + + ! - check that the soil layer is desired + if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle + + ! - define state indices + watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset + + ! - define indices of the soil layers + jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + + ! - compute the diagonal elements + ! all terms *excluding* baseflow + aJac(ixDiag,watState) = (dt/mLayerDepth(jLayer))*(-dq_dHydStateBelow(iLayer-1) + dq_dHydStateAbove(iLayer)) + dMat(watState) + + ! - compute the lower-diagonal elements + if(iLayer>1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),watState),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) + endif + + ! - compute the upper-diagonal elements + if(iLayer0)then !have snow above first soil layer + if(ixSnowOnlyHyd(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) + if(ixVegHyd/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) + endif + + endif ! (if the subset includes hydrology state variables in the soil domain) + + ! ----- + ! * liquid water fluxes for the aquifer... + ! ---------------------------------------- + if(ixAqWat/=integerMissing) then + aJac(ixDiag,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) + aJac(ixOffDiag(ixAqWat,ixSoilOnlyNrg(nSoil)),ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk + aJac(ixOffDiag(ixAqWat,ixSoilOnlyHyd(nSoil)),ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat + ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) if(computeVegFlux)then - aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) - aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) - aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 + aJac(ixOffDiag(ixAqWat,ixCasNrg),ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) + aJac(ixOffDiag(ixAqWat,ixVegNrg),ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) + aJac(ixOffDiag(ixAqWat,ixVegHyd),ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 + aJac(ixOffDiag(ixAqWat,ixTopNrg),ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) endif - aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) endif - - ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) - if(computeVegFlux)then - aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanair(iLayer)) + aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) ! dVol/dT (K-1) - aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanopy(iLayer)) + aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) ! dVol/dT (K-1) - aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dCanWat(iLayer)) + aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) ! dVol/dLiq (kg m-2)-1 - aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTGround(iLayer)) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) - endif - - ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerdTemp_dt(jLayer) & - + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) - if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present - aJac(ixOffDiag(nrgState,watState),watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) + aJac(ixOffDiag(nrgState,watState),watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content - endif - - ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above - if(iLayer>1)then - if(ixSoilOnlyNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyNrg(iLayer-1),watState),watState) = (dt/mLayerDepth(jLayer-1))*( dNrgFlux_dWatBelow(jLayer-1) ) - elseif(iLayer==1 .and. nSnowOnlyNrg>0)then !top soil layer and there is snow above - if(ixSnowOnlyNrg(nSnow)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyNrg(nSnow),watState),watState) = (dt/mLayerDepth(nSnow))*( dNrgFlux_dWatBelow(nSnow) ) - endif - - ! (cross-derivative terms for the layer below) - if(iLayer0 .and. ixSnowOnlyNrg(nSnow)/=integerMissing)then - if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) - elseif(computeVegFlux .and. ixVegNrg/=integerMissing) then !ixTopHyd = ixSoilOnlyHyd(1) - if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) - endif - if(nSnowOnlyHyd>0)then !have snow above first soil layer - if(ixSnowOnlyNrg(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing)then - aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) - endif - elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) - if(ixVegNrg/=integerMissing .and. ixTopHyd/=integerMissing)then - aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) - endif - endif - endif ! (if there are state variables for both water and energy in the soil domain) - - if(globalPrintFlag)then - print*, '** banded analytical Jacobian:' - write(*,'(a4,1x,100(i17,1x))') 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) - do iLayer=kl+1,nBands - write(*,'(i4,1x,100(e17.10,1x))') iLayer, (aJac(iLayer,jLayer),jLayer=min(iJac1,nState),min(iJac2,nState)) - end do - endif - !print*, 'PAUSE: banded analytical Jacobian'; read(*,*) - - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - ! * PART 2: FULL MATRIX - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - case(ixFullMatrix) - - ! check - if(size(aJac,1)/=size(dMat) .or. size(aJac,2)/=size(dMat))then - message=trim(message)//'unexpected shape of the Jacobian matrix: expect aJac(nState,nState)' - err=20; return - endif - - ! ----- - ! * energy and liquid fluxes over vegetation... - ! --------------------------------------------- - if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) - - ! * energy fluxes with the canopy water - if(ixVegHyd/=integerMissing)then - - ! cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) - if(ixCasNrg/=integerMissing) aJac(ixVegHyd,ixCasNrg) = -dCanopyEvaporation_dTCanair*dt - ! dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy is the derivative in throughfall and canopy drainage with canopy temperature - if(ixVegNrg/=integerMissing) aJac(ixVegHyd,ixVegNrg) = -dCanopyEvaporation_dTCanopy*dt + dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy - ! * liquid water fluxes for vegetation canopy (-) - aJac(ixVegHyd,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanWat - scalarCanopyLiqDeriv)*dt + 1._rkind - if(ixTopNrg/=integerMissing) aJac(ixVegHyd,ixTopNrg) = -dCanopyEvaporation_dTGround*dt - - ! cross-derivative terms w.r.t. canopy water (kg-1 m2) - if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water - - ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) - ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth & - + dVolHtCapBulk_dCanWat * scalarCanopydTemp_dt - (dt/canopyDepth) * dCanopyNetFlux_dCanWat ! dF/dLiq - if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) - endif - - ! cross-derivative terms w.r.t. canopy temperature (K-1) - if(ixVegNrg/=integerMissing)then - if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegNrg) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarCanopyLiqDeriv*dCanLiq_dTcanopy)/iden_water - endif - - ! energy fluxes with the canopy air space (J m-3 K-1) - if(ixCasNrg/=integerMissing)then - aJac(ixCasNrg,ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) - if(ixVegNrg/=integerMissing) aJac(ixCasNrg,ixVegNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanopyTemp) - if(ixTopNrg/=integerMissing) aJac(ixCasNrg,ixTopNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dGroundTemp) - endif - - ! energy fluxes with the vegetation canopy (J m-3 K-1) - if(ixVegNrg/=integerMissing)then - if(ixCasNrg/=integerMissing) aJac(ixVegNrg,ixCasNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanairTemp) - aJac(ixVegNrg,ixVegNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanopyTemp) + dMat(ixVegNrg) - if(ixTopNrg/=integerMissing) aJac(ixVegNrg,ixTopNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dGroundTemp) - endif - - ! energy fluxes with the surface (J m-3 K-1) - if(ixTopNrg/=integerMissing)then - if(ixCasNrg/=integerMissing) aJac(ixTopNrg,ixCasNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanairTemp) - if(ixVegNrg/=integerMissing) aJac(ixTopNrg,ixVegNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanopyTemp) - endif - - endif ! if there is a need to compute energy fluxes within vegetation - - ! ----- - ! * energy fluxes for the snow+soil domain... - ! ------------------------------------------- - if(nSnowSoilNrg>0)then - do iLayer=1,nLayers ! loop through all layers in the snow+soil domain - - ! check if the state is in the subset - if(ixSnowSoilNrg(iLayer)==integerMissing) cycle - - ! - define index within the state subset and the full state vector - jState = ixSnowSoilNrg(iLayer) ! index within the state subset - - ! - diagonal elements - aJac(jState,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) - - ! - lower-diagonal elements - if(iLayer>1)then - if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixSnowSoilNrg(iLayer-1),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) - endif - - ! - upper diagonal elements - if(iLayer0)then - do iLayer=1,nSnow ! loop through layers in the snow domain - - ! - check that the snow layer is desired - if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle - - ! - define state indices for the current layer - watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - - ! compute factor to convert liquid water derivative to total water derivative - select case( ixHydType(iLayer) ) - case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) - case default; convLiq2tot = 1._rkind - end select - - ! - diagonal elements - aJac(watState,watState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot + dMat(watState) - - ! - lower-diagonal elements - if(iLayer>1)then - if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSnowOnlyHyd(iLayer-1),watState) = 0._rkind ! sub-diagonal: no dependence on other layers - endif - - ! - upper diagonal elements - if(iLayer0 .and. nSnowOnlyNrg>0)then - do iLayer=1,nSnow ! loop through layers in the snow domain - - ! - check that the snow layer is desired - if(ixSnowOnlyNrg(iLayer)==integerMissing) cycle - - ! (define the energy state) - nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector - - ! - define state indices for the current layer - watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - - if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) - - ! - include derivatives of energy fluxes w.r.t water fluxes for current layer - aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water & - + dVolHtCapBulk_dTheta(iLayer) * mLayerdTemp_dt(iLayer) & - + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) ! (dF/dLiq) - - ! - include derivatives of water fluxes w.r.t energy fluxes for current layer - aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) - - ! (cross-derivative terms for the layer below) - if(iLayer < nSnow)then - aJac(ixSnowOnlyHyd(iLayer+1),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 - endif ! (if there is a water state in the layer below the current layer in the given state subset) - - ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above - if(iLayer>1)then - if(ixSnowOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSnowOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) ) - endif - - ! (cross-derivative terms for the layer below) - if(iLayer0)then !bottom snow layer and there is soil below - if(ixSoilOnlyNrg(1)/=integerMissing) aJac(ixSoilOnlyNrg(1),watState) = (dt/mLayerDepth(nSnow+1))*(-dNrgFlux_dWatAbove(nSnow) ) - endif - - endif ! (if the energy state for the current layer is within the state subset) - - end do ! (looping through snow layers) - endif ! (if there are state variables for both water and energy in the snow domain) - - ! ----- - ! * liquid water fluxes for the soil domain... - ! -------------------------------------------- - if(nSoilOnlyHyd>0)then - do iLayer=1,nSoil - - ! - check that the soil layer is desired - if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle - - ! - define state indices - watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset - - ! - define indices of the soil layers - jLayer = iLayer+nSnow ! index of layer in the snow+soil vector - - ! - compute the diagonal elements - ! all terms *excluding* baseflow - aJac(watState,watState) = (dt/mLayerDepth(jLayer))*(-dq_dHydStateBelow(iLayer-1) + dq_dHydStateAbove(iLayer)) + dMat(watState) - - ! - compute the lower-diagonal elements - if(iLayer>1)then - if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) - endif - - ! - compute the upper-diagonal elements - if(iLayer0 .and. ixSnowOnlyHyd(nSnow)/=integerMissing)then - if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) - elseif(computeVegFlux .and. ixVegHyd/=integerMissing)then !ixTopHyd = ixSoilOnlyHyd(1) - if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegHyd) - endif - - endif ! (if the subset includes hydrology state variables in the soil domain) - - ! ----- - ! * liquid water fluxes for the aquifer... - ! ---------------------------------------- - if(ixAqWat/=integerMissing) then - aJac(ixAqWat,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) - aJac(ixAqWat,ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk - aJac(ixAqWat,ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat - ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) - if(computeVegFlux)then - aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) - aJac(ixAqWat,ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) - aJac(ixAqWat,ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 - aJac(ixAqWat,ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) - endif - endif - - ! ----- - ! * cross derivatives in the soil domain... - ! ----------------------------------------------------------------------------- - if(nSoilOnlyHyd>0 .and. nSoilOnlyNrg>0)then - do iLayer=1,nSoilOnlyNrg - - ! - check that the soil layer is desired - if(ixSoilOnlyNrg(iLayer)==integerMissing) cycle - - ! - define indices of the soil layers - jLayer = iLayer+nSnow ! index of layer in the snow+soil vector - - ! - define the energy state variable - nrgState = ixNrgLayer(jLayer) ! index within the full state vector - - ! - define index of hydrology state variable within the state subset - watState = ixSoilOnlyHyd(iLayer) - - ! only compute derivatives if the water state for the current layer is within the state subset - if(watstate/=integerMissing)then - - ! - include derivatives in liquid water fluxes w.r.t. temperature for current layer - aJac(watState,nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance - - ! - compute lower diagonal elements - if(iLayer>1)then - if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 + + ! ----- + ! * cross derivatives in the soil domain... + ! ----------------------------------------------------------------------------- + if(nSoilOnlyHyd>0 .and. nSoilOnlyNrg>0)then + do iLayer=1,nSoilOnlyNrg + + ! - check that the soil layer is desired + if(ixSoilOnlyNrg(iLayer)==integerMissing) cycle + + ! - define indices of the soil layers + jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + + ! - define the energy state variable + nrgState = ixNrgLayer(jLayer) ! index within the full state vector + + ! - define index of hydrology state variable within the state subset + watState = ixSoilOnlyHyd(iLayer) + + ! only compute derivatives if the water state for the current layer is within the state subset + if(watstate/=integerMissing)then + + ! - include derivatives in liquid water fluxes w.r.t. temperature for current layer + aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance + + ! - compute lower diagonal elements + if(iLayer>1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),nrgState),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 + endif + + ! compute upper-diagonal elements + if(iLayer verySmall)then ! ice is present + aJac(ixOffDiag(nrgState,watState),watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) + aJac(ixOffDiag(nrgState,watState),watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + endif + + ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above + if(iLayer>1)then + if(ixSoilOnlyNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyNrg(iLayer-1),watState),watState) = (dt/mLayerDepth(jLayer-1))*( dNrgFlux_dWatBelow(jLayer-1) ) + elseif(iLayer==1 .and. nSnowOnlyNrg>0)then !top soil layer and there is snow above + if(ixSnowOnlyNrg(nSnow)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyNrg(nSnow),watState),watState) = (dt/mLayerDepth(nSnow))*( dNrgFlux_dWatBelow(nSnow) ) + endif + + ! (cross-derivative terms for the layer below) + if(iLayer0)then !have snow above first soil layer + if(ixSnowOnlyNrg(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) + if(ixVegNrg/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) + endif + + endif ! (if there are state variables for both water and energy in the soil domain) + + if(globalPrintFlag)then + print*, '** banded analytical Jacobian:' + write(*,'(a4,1x,100(i17,1x))') 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) + do iLayer=kl+1,nBands + write(*,'(i4,1x,100(e17.10,1x))') iLayer, (aJac(iLayer,jLayer),jLayer=min(iJac1,nState),min(iJac2,nState)) + end do endif - - ! compute upper-diagonal elements - if(iLayer0)then + do iLayer=1,nLayers ! loop through all layers in the snow+soil domain + + ! check if the state is in the subset + if(ixSnowSoilNrg(iLayer)==integerMissing) cycle + + ! - define index within the state subset and the full state vector + jState = ixSnowSoilNrg(iLayer) ! index within the state subset + + ! - diagonal elements + aJac(jState,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) + + ! - lower-diagonal elements + if(iLayer>1)then + if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixSnowSoilNrg(iLayer-1),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) + endif + + ! - upper diagonal elements + if(iLayer0)then + do iLayer=1,nSnow ! loop through layers in the snow domain + + ! - check that the snow layer is desired + if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle + + ! - define state indices for the current layer + watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset + + ! compute factor to convert liquid water derivative to total water derivative + select case( ixHydType(iLayer) ) + case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) + case default; convLiq2tot = 1._rkind + end select + + ! - diagonal elements + aJac(watState,watState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot + dMat(watState) + + ! - lower-diagonal elements + if(iLayer>1)then + if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSnowOnlyHyd(iLayer-1),watState) = 0._rkind ! sub-diagonal: no dependence on other layers + endif + + ! - upper diagonal elements + if(iLayer0 .and. nSnowOnlyNrg>0)then + do iLayer=1,nSnow ! loop through layers in the snow domain + + ! - check that the snow layer is desired + if(ixSnowOnlyNrg(iLayer)==integerMissing) cycle + + ! (define the energy state) + nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector + + ! - define state indices for the current layer + watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset + + if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) + + ! - include derivatives of energy fluxes w.r.t water fluxes for current layer + aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water & + + dVolHtCapBulk_dTheta(iLayer) * mLayerdTemp_dt(iLayer) & + + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) ! (dF/dLiq) + + ! - include derivatives of water fluxes w.r.t energy fluxes for current layer + aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) + + ! (cross-derivative terms for the layer below) + if(iLayer < nSnow)then + aJac(ixSnowOnlyHyd(iLayer+1),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 + endif ! (if there is a water state in the layer below the current layer in the given state subset) + + ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above + if(iLayer>1)then + if(ixSnowOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSnowOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) ) + endif + + ! (cross-derivative terms for the layer below) + if(iLayer0)then !bottom snow layer and there is soil below + if(ixSoilOnlyNrg(1)/=integerMissing) aJac(ixSoilOnlyNrg(1),watState) = (dt/mLayerDepth(nSnow+1))*(-dNrgFlux_dWatAbove(nSnow) ) + endif + + endif ! (if the energy state for the current layer is within the state subset) + + end do ! (looping through snow layers) + endif ! (if there are state variables for both water and energy in the snow domain) + + ! ----- + ! * liquid water fluxes for the soil domain... + ! -------------------------------------------- + if(nSoilOnlyHyd>0)then + do iLayer=1,nSoil + + ! - check that the soil layer is desired + if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle + + ! - define state indices + watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset + + ! - define indices of the soil layers + jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + + ! - compute the diagonal elements + ! all terms *excluding* baseflow + aJac(watState,watState) = (dt/mLayerDepth(jLayer))*(-dq_dHydStateBelow(iLayer-1) + dq_dHydStateAbove(iLayer)) + dMat(watState) + + ! - compute the lower-diagonal elements + if(iLayer>1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) + endif + + ! - compute the upper-diagonal elements + if(iLayer0)then !have snow above first soil layer + if(ixSnowOnlyHyd(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) + if(ixVegHyd/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegHyd) + endif + + + endif ! (if the subset includes hydrology state variables in the soil domain) + + ! ----- + ! * liquid water fluxes for the aquifer... + ! ---------------------------------------- + if(ixAqWat/=integerMissing) then + aJac(ixAqWat,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) + aJac(ixAqWat,ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk + aJac(ixAqWat,ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat + ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) if(computeVegFlux)then - aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) - aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) - aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 + aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) + aJac(ixAqWat,ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) + aJac(ixAqWat,ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 + aJac(ixAqWat,ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) endif - aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) - endif - - ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) - if(computeVegFlux)then - aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanair(iLayer)) + aJac(watState,ixCasNrg) ! dVol/dT (K-1) - aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanopy(iLayer)) + aJac(watState,ixVegNrg) ! dVol/dT (K-1) - aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dCanWat(iLayer)) + aJac(watState,ixVegHyd) ! dVol/dLiq (kg m-2)-1 - aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTGround(iLayer)) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) endif - - ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerdTemp_dt(jLayer) & - + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) - if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present - aJac(nrgState,watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content - endif - - ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above - if(iLayer>1)then - if(ixSoilOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSoilOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(jLayer-1))*( dNrgFlux_dWatBelow(jLayer-1) ) - elseif(iLayer==1 .and. nSnowOnlyNrg>0)then !top soil layer and there is snow above - if(ixSnowOnlyNrg(nSnow)/=integerMissing) aJac(ixSnowOnlyNrg(nSnow),watState) = (dt/mLayerDepth(nSnow))*( dNrgFlux_dWatBelow(nSnow) ) - endif - - ! (cross-derivative terms for the layer below) - if(iLayer0 .and. nSoilOnlyNrg>0)then + do iLayer=1,nSoilOnlyNrg + + ! - check that the soil layer is desired + if(ixSoilOnlyNrg(iLayer)==integerMissing) cycle + + ! - define indices of the soil layers + jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + + ! - define the energy state variable + nrgState = ixNrgLayer(jLayer) ! index within the full state vector + + ! - define index of hydrology state variable within the state subset + watState = ixSoilOnlyHyd(iLayer) + + ! only compute derivatives if the water state for the current layer is within the state subset + if(watstate/=integerMissing)then + + ! - include derivatives in liquid water fluxes w.r.t. temperature for current layer + aJac(watState,nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance + + ! - compute lower diagonal elements + if(iLayer>1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 + endif + + ! compute upper-diagonal elements + if(iLayer verySmall)then ! ice is present + aJac(nrgState,watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + endif + + ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above + if(iLayer>1)then + if(ixSoilOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSoilOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(jLayer-1))*( dNrgFlux_dWatBelow(jLayer-1) ) + elseif(iLayer==1 .and. nSnowOnlyNrg>0)then !top soil layer and there is snow above + if(ixSnowOnlyNrg(nSnow)/=integerMissing) aJac(ixSnowOnlyNrg(nSnow),watState) = (dt/mLayerDepth(nSnow))*( dNrgFlux_dWatBelow(nSnow) ) + endif + + ! (cross-derivative terms for the layer below) + if(iLayer0)then !have snow above first soil layer + if(ixSnowOnlyNrg(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) + if(ixVegNrg/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegNrg) + endif + + endif ! (if there are state variables for both water and energy in the soil domain) + + ! print the Jacobian + if(globalPrintFlag)then + print*, '** analytical Jacobian (full):' + write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) + do iLayer=min(iJac1,nState),min(iJac2,nState) + write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(min(iJac1,nState):min(iJac2,nState),iLayer) + end do endif - - endif ! (if the water state for the current layer is within the state subset) - - ! - include terms for surface infiltration below surface - if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),nrgState) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(iLayer) + aJac(ixSoilOnlyHyd(1),nrgState) - - end do ! (looping through soil layers) - - ! - include terms for surface infiltration above surface - if(nSnowOnlyHyd>0 .and. ixSnowOnlyNrg(nSnow)/=integerMissing)then - if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) - elseif(computeVegFlux .and. ixVegNrg/=integerMissing) then !ixTopHyd = ixSoilOnlyHyd(1) - if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegNrg) + + !print*, '** analytical Jacobian (full):' + !write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=1,size(aJac,2)) + !do iLayer=1,size(aJac,1) + ! write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(iLayer,1:size(aJac,2)) + !end do + !print *, '--------------------------------------------------------------' + + ! *** + ! check + case default; err=20; message=trim(message)//'unable to identify option for the type of matrix'; return + + end select ! type of matrix + + if(any(isNan(aJac)))then + print *, '******************************* WE FOUND NAN IN JACOBIAN ************************************' + stop 1 + message=trim(message)//'we found NaN' + err=20; return endif - - endif ! (if there are state variables for both water and energy in the soil domain) - - ! print the Jacobian - if(globalPrintFlag)then - print*, '** analytical Jacobian (full):' - write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) - do iLayer=min(iJac1,nState),min(iJac2,nState) - write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(min(iJac1,nState):min(iJac2,nState),iLayer) - end do - endif - - !print*, '** analytical Jacobian (full):' - !write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=1,size(aJac,2)) - !do iLayer=1,size(aJac,1) - ! write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(iLayer,1:size(aJac,2)) - !end do - !print *, '--------------------------------------------------------------' - - ! *** - ! check - case default; err=20; message=trim(message)//'unable to identify option for the type of matrix'; return - - end select ! type of matrix - - if(any(isNan(aJac)))then - print *, '******************************* WE FOUND NAN IN JACOBIAN ************************************' - stop 1 - message=trim(message)//'we found NaN' - err=20; return - endif - - ! end association to variables in the data structures - end associate - - end subroutine computJacob - - - ! ********************************************************************************************************** - ! private function: get the off-diagonal index in the band-diagonal matrix - ! ********************************************************************************************************** - function ixOffDiag(jState,iState) - implicit none - integer(i4b),intent(in) :: jState ! off-diagonal state - integer(i4b),intent(in) :: iState ! diagonal state - integer(i4b) :: ixOffDiag ! off-diagonal index in gthe band-diagonal matrix - ixOffDiag = ixBandOffset + jState - iState - end function ixOffDiag - -end module computJacob_module + + ! end association to variables in the data structures + end associate + + end subroutine computJacob + + + ! ********************************************************************************************************** + ! private function: get the off-diagonal index in the band-diagonal matrix + ! ********************************************************************************************************** + function ixOffDiag(jState,iState) + implicit none + integer(i4b),intent(in) :: jState ! off-diagonal state + integer(i4b),intent(in) :: iState ! diagonal state + integer(i4b) :: ixOffDiag ! off-diagonal index in gthe band-diagonal matrix + ixOffDiag = ixBandOffset + jState - iState + end function ixOffDiag + + end module computJacob_module \ No newline at end of file diff --git a/build/source/engine/computJacobSundials.f90 b/build/source/engine/computJacobSundials.f90 index b825d8e72..ef0a227e2 100644 --- a/build/source/engine/computJacobSundials.f90 +++ b/build/source/engine/computJacobSundials.f90 @@ -568,7 +568,7 @@ subroutine computJacobSundials(& end do ! (looping through hydrology states in the soil domain) ! - include terms for surface infiltration above surface - if(nSnowOnlyHyd>0)then !have snow above first soil layer + if(nSnowOnlyNrg>0)then !have snow above first soil layer if(ixSnowOnlyHyd(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) if(ixVegHyd/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegHyd) @@ -672,7 +672,7 @@ subroutine computJacobSundials(& end do ! (looping through soil layers) ! - include terms for surface infiltration above surface - if(nSnowOnlyHyd>0)then !have snow above first soil layer + if(nSnowOnlyNrg>0)then !have snow above first soil layer if(ixSnowOnlyNrg(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) if(ixVegNrg/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegNrg) From d12d880d497e95e64bff563e693437fefc7b84be Mon Sep 17 00:00:00 2001 From: Kyle Date: Fri, 23 Sep 2022 15:30:03 +0000 Subject: [PATCH 0345/1472] added changes for banded matrix This was tested using the dense matrix the code for the banded matrix has not been tested --- build/source/engine/computJacob.f90 | 62 +-- build/source/engine/computJacobSundials.f90 | 369 +++++++++++++++++- build/source/engine/evalJac4IDA.f90 | 7 +- build/source/engine/summaSolveSundialsIDA.f90 | 2 - 4 files changed, 391 insertions(+), 49 deletions(-) diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 2681a077e..2d92d0100 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -500,7 +500,7 @@ subroutine computJacob(& if(ixSoilOnlyHyd(iLayer+1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer+1),watState),watState) = (dt/mLayerDepth(jLayer+1))*(-dq_dHydStateAbove(iLayer)) endif - ! - include terms for baseflow WHY NOT USE + ! - do not include terms for baseflow in banded structure !if(computeBaseflow .and. nSoilOnlyHyd==nSoil)then ! do pLayer=1,nSoil ! qState = ixSoilOnlyHyd(pLayer) ! hydrology state index within the state subset @@ -508,17 +508,17 @@ subroutine computJacob(& ! end do !endif - ! - include terms for surface infiltration below surface - if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),watState),watState) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(iLayer) + aJac(ixOffDiag(ixSoilOnlyHyd(1),watState),watState) + ! - do not include terms for surface infiltration below surface in banded structure + ! if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),watState),watState) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(iLayer) + aJac(ixOffDiag(ixSoilOnlyHyd(1),watState),watState) end do ! (looping through hydrology states in the soil domain) - ! - include terms for surface infiltration above surface - if(nSnowOnlyHyd>0)then !have snow above first soil layer - if(ixSnowOnlyHyd(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) - elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) - if(ixVegHyd/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) - endif + ! - do not include terms for surface infiltration above surface in banded structure + ! if(nSnowOnlyHyd>0)then !have snow above first soil layer + ! if(ixSnowOnlyHyd(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + ! elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) + ! if(ixVegHyd/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) + ! endif endif ! (if the subset includes hydrology state variables in the soil domain) @@ -529,13 +529,13 @@ subroutine computJacob(& aJac(ixDiag,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) aJac(ixOffDiag(ixAqWat,ixSoilOnlyNrg(nSoil)),ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk aJac(ixOffDiag(ixAqWat,ixSoilOnlyHyd(nSoil)),ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat - ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) - if(computeVegFlux)then - aJac(ixOffDiag(ixAqWat,ixCasNrg),ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) - aJac(ixOffDiag(ixAqWat,ixVegNrg),ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) - aJac(ixOffDiag(ixAqWat,ixVegHyd),ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 - aJac(ixOffDiag(ixAqWat,ixTopNrg),ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) - endif + ! - do not include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) in banded structure + ! if(computeVegFlux)then + ! aJac(ixOffDiag(ixAqWat,ixCasNrg),ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) + ! aJac(ixOffDiag(ixAqWat,ixVegNrg),ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) + ! aJac(ixOffDiag(ixAqWat,ixVegHyd),ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 + ! aJac(ixOffDiag(ixAqWat,ixTopNrg),ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) + ! endif endif ! ----- @@ -582,13 +582,13 @@ subroutine computJacob(& aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) endif - ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) - if(computeVegFlux)then - aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanair(iLayer)) + aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) ! dVol/dT (K-1) - aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanopy(iLayer)) + aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) ! dVol/dT (K-1) - aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dCanWat(iLayer)) + aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) ! dVol/dLiq (kg m-2)-1 - aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTGround(iLayer)) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) - endif + ! - do not include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) in banded structure + ! if(computeVegFlux)then + ! aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanair(iLayer)) + aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) ! dVol/dT (K-1) + ! aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanopy(iLayer)) + aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) ! dVol/dT (K-1) + ! aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dCanWat(iLayer)) + aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) ! dVol/dLiq (kg m-2)-1 + ! aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTGround(iLayer)) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) + ! endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerdTemp_dt(jLayer) & @@ -611,17 +611,17 @@ subroutine computJacob(& endif ! (if the water state for the current layer is within the state subset) - ! - include terms for surface infiltration below surface - if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),nrgState),nrgState) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(iLayer) + aJac(ixOffDiag(ixSoilOnlyHyd(1),nrgState),nrgState) + ! - do not include terms for surface infiltration below surface in banded structure + ! if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),nrgState),nrgState) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(iLayer) + aJac(ixOffDiag(ixSoilOnlyHyd(1),nrgState),nrgState) end do ! (looping through soil layers) - ! - include terms for surface infiltration above surface - if(nSnowOnlyNrg>0)then !have snow above first soil layer - if(ixSnowOnlyNrg(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) - elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) - if(ixVegNrg/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) - endif + ! - do not include terms for surface infiltration above surface in banded structure + !if(nSnowOnlyNrg>0)then !have snow above first soil layer + ! if(ixSnowOnlyNrg(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + !elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) + ! if(ixVegNrg/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) + !endif endif ! (if there are state variables for both water and energy in the soil domain) diff --git a/build/source/engine/computJacobSundials.f90 b/build/source/engine/computJacobSundials.f90 index ef0a227e2..e7abbc8b1 100644 --- a/build/source/engine/computJacobSundials.f90 +++ b/build/source/engine/computJacobSundials.f90 @@ -344,16 +344,355 @@ subroutine computJacobSundials(& ! * PART 1: BAND MATRIX ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* - case(ixBandMatrix) ! ixBandMatrix ixFullMatrix - print *, 'banded jacobian matrix needs to be implemented' - stop 1 + case(ixBandMatrix) + ! check + if(size(aJac,1)/=nBands .or. size(aJac,2)/=size(dMat))then + message=trim(message)//'unexpected shape of the Jacobian matrix: expect aJac(nBands,nState)' + err=20; return + endif + + ! ----- + ! * energy and liquid fluxes over vegetation... + ! --------------------------------------------- + if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) + + ! * energy fluxes with the canopy water + if(ixVegHyd/=integerMissing)then + + ! * cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) + if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixCasNrg),ixCasNrg) = -dCanopyEvaporation_dTCanair*dt + ! dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy is the derivative in throughfall and canopy drainage with canopy temperature + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixVegNrg),ixVegNrg) = -dCanopyEvaporation_dTCanopy*dt + dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy + ! * liquid water fluxes for vegetation canopy (-), dt*scalarFracLiqVeg*scalarCanopyLiqDeriv is the derivative in throughfall and canopy drainage with canopy water + aJac(ixDiag, ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanWat - scalarCanopyLiqDeriv)*dt + 1._rkind * cj + if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixTopNrg),ixTopNrg) = -dCanopyEvaporation_dTGround*dt + + ! * cross-derivative terms w.r.t. canopy water (kg-1 m2) + if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water + + ! * cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) + ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth * cj & + + dVolHtCapBulk_dCanWat * scalarCanopyTempPrime - (dt/canopyDepth) * dCanopyNetFlux_dCanWat & + + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth ! dF/dLiq + if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) + endif + + ! * -derivative terms w.r.t. canopy temperature (K-1) + if(ixVegNrg/=integerMissing)then + if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarCanopyLiqDeriv*dCanLiq_dTcanopy)/iden_water + endif + + ! * energy fluxes with the canopy air space (J m-3 K-1) + if(ixCasNrg/=integerMissing)then + aJac(ixDiag, ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) * cj + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixCasNrg,ixVegNrg),ixVegNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanopyTemp) + if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixCasNrg,ixTopNrg),ixTopNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dGroundTemp) + endif + + ! * energy fluxes with the vegetation canopy (J m-3 K-1) + if(ixVegNrg/=integerMissing)then + if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixCasNrg),ixCasNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanairTemp) + aJac(ixDiag, ixVegNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanopyTemp) + dMat(ixVegNrg) + if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixTopNrg),ixTopNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dGroundTemp) + endif + + ! * energy fluxes with the surface (J m-3 K-1) + if(ixTopNrg/=integerMissing)then + if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanairTemp) + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanopyTemp) + endif + + endif ! if there is a need to compute energy fluxes within vegetation + + ! ----- + ! * energy fluxes for the snow+soil domain... + ! ------------------------------------------- + if(nSnowSoilNrg>0)then + do iLayer=1,nLayers ! loop through all layers in the snow+soil domain + + ! check if the state is in the subset + if(ixSnowSoilNrg(iLayer)==integerMissing) cycle + + ! - define index within the state subset and the full state vector + jState = ixSnowSoilNrg(iLayer) ! index within the state subset + + ! - diagonal elements + aJac(ixDiag,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) + + ! - lower-diagonal elements + if(iLayer>1)then + if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowSoilNrg(iLayer-1),jState),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) + endif + + ! - upper diagonal elements + if(iLayer0)then + do iLayer=1,nSnow ! loop through layers in the snow domain + + ! - check that the snow layer is desired + if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle + + ! - define state indices for the current layer + watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset + + ! compute factor to convert liquid water derivative to total water derivative + select case( ixHydType(iLayer) ) + case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) + case default; convLiq2tot = 1._rkind + end select + + ! - diagonal elements + aJac(ixDiag,watState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot + dMat(watState) * cj + + ! - lower-diagonal elements + if(iLayer>1)then + if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyHyd(iLayer-1),watState),watState) = 0._rkind ! sub-diagonal: no dependence on other layers + endif + + ! - upper diagonal elements + if(iLayer0 .and. nSnowOnlyNrg>0)then + do iLayer=1,nSnow ! loop through layers in the snow domain + + ! - check that the snow layer is desired + if(ixSnowOnlyNrg(iLayer)==integerMissing) cycle + + ! (define the energy state) + nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector + + ! - define state indices for the current layer + watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset + + if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) + + ! - include derivatives of energy fluxes w.r.t water fluxes for current layer + aJac(ixOffDiag(nrgState,watState),watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & + + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) & + + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) & + + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) + + ! - include derivatives of water fluxes w.r.t energy fluxes for current layer + aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) + + ! (cross-derivative terms for the layer below) + if(iLayer1)then + if(ixSnowOnlyNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyNrg(iLayer-1),watState),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) ) + endif + + ! (cross-derivative terms for the layer below) + if(iLayer0)then !bottom snow layer and there is soil below + if(ixSoilOnlyNrg(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyNrg(1),watState),watState) = (dt/mLayerDepth(nSnow+1))*(-dNrgFlux_dWatAbove(nSnow) ) + endif + + endif ! (if the energy state for the current layer is within the state subset) + + end do ! (looping through snow layers) + endif ! (if there are state variables for both water and energy in the snow domain) + + ! ----- + ! * liquid water fluxes for the soil domain... + ! -------------------------------------------- + if(nSoilOnlyHyd>0)then + do iLayer=1,nSoil + + ! - check that the soil layer is desired + if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle + + ! - define state indices + watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset + + ! - define indices of the soil layers + jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + + ! - compute the diagonal elements + ! all terms *excluding* baseflow + aJac(ixDiag,watState) = (dt/mLayerDepth(jLayer))*(-dq_dHydStateBelow(iLayer-1) + dq_dHydStateAbove(iLayer)) + dMat(watState) + + ! - compute the lower-diagonal elements + if(iLayer>1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),watState),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) + endif + + ! - compute the upper-diagonal elements + if(iLayer0)then !have snow above first soil layer + ! if(ixSnowOnlyHyd(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + !elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) + ! if(ixVegHyd/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) + !endif + + endif ! (if the subset includes hydrology state variables in the soil domain) + + ! ----- + ! * liquid water fluxes for the aquifer... + ! ---------------------------------------- + if(ixAqWat/=integerMissing) then + aJac(ixDiag,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) * cj + aJac(ixOffDiag(ixAqWat,ixSoilOnlyNrg(nSoil)),ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk + aJac(ixOffDiag(ixAqWat,ixSoilOnlyHyd(nSoil)),ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat + ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) + !if(computeVegFlux)then + ! aJac(ixOffDiag(ixAqWat,ixCasNrg),ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) + ! aJac(ixOffDiag(ixAqWat,ixVegNrg),ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) + ! aJac(ixOffDiag(ixAqWat,ixVegHyd),ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 + ! aJac(ixOffDiag(ixAqWat,ixTopNrg),ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) + !endif + endif + + ! ----- + ! * cross derivatives in the soil domain... + ! ---------------------------------------- + if(nSoilOnlyHyd>0 .and. nSoilOnlyNrg>0)then + do iLayer=1,nSoilOnlyNrg + + ! - check that the soil layer is desired + if(ixSoilOnlyNrg(iLayer)==integerMissing) cycle + + ! - define indices of the soil layers + jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + + ! - define the energy state variable + nrgState = ixNrgLayer(jLayer) ! index within the full state vector + + ! - define index of hydrology state variable within the state subset + watState = ixSoilOnlyHyd(iLayer) + + ! only compute derivatives if the water state for the current layer is within the state subset + if(watstate/=integerMissing)then + + ! - include derivatives in liquid water fluxes w.r.t. temperature for current layer + aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance + + ! - compute lower diagonal elements + if(iLayer>1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),nrgState),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 + endif + + ! compute upper-diagonal elements + if(iLayer verySmall)then ! ice is present + aJac(ixOffDiag(nrgState,watState),watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) * cj & + - LH_fus*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_d2Psi0(iLayer) + aJac(ixOffDiag(nrgState,watState),watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + endif + + ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above + if(iLayer>1)then + if(ixSoilOnlyNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyNrg(iLayer-1),watState),watState) = (dt/mLayerDepth(jLayer-1))*( dNrgFlux_dWatBelow(jLayer-1) ) + elseif(iLayer==1 .and. nSnowOnlyNrg>0)then !top soil layer and there is snow above + if(ixSnowOnlyNrg(nSnow)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyNrg(nSnow),watState),watState) = (dt/mLayerDepth(nSnow))*( dNrgFlux_dWatBelow(nSnow) ) + endif + + ! (cross-derivative terms for the layer below) + if(iLayer0)then !have snow above first soil layer + ! if(ixSnowOnlyNrg(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + !elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) + ! if(ixVegNrg/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) + !endif + + endif ! (if there are state variables for both water and energy in the soil domain) + + if(globalPrintFlag)then + print*, '** banded analytical Jacobian:' + write(*,'(a4,1x,100(i17,1x))') 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) + do iLayer=kl+1,nBands + write(*,'(i4,1x,100(e17.10,1x))') iLayer, (aJac(iLayer,jLayer),jLayer=min(iJac1,nState),min(iJac2,nState)) + end do + endif + + !print*, '** banded analytical Jacobian:' + !write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=1,size(aJac,2)) + !do iLayer=1,size(aJac,1) + ! write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(iLayer,1:size(aJac,2)) + !end do + !print *, '--------------------------------------------------------------' + + !print*, 'PAUSE: banded analytical Jacobian'; read(*,*) ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* ! * PART 2: FULL MATRIX ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* - case(ixFullMatrix) ! ixFullMatrix ixBandMatrix + case(ixFullMatrix) ! check if(size(aJac,1)/=size(dMat) .or. size(aJac,2)/=size(dMat))then @@ -629,9 +968,9 @@ subroutine computJacobSundials(& ! - include derivatives of energy w.r.t. ground evaporation if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer if(computeVegFlux)then - aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) + aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 endif aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) endif @@ -715,15 +1054,15 @@ subroutine computJacobSundials(& end subroutine computJacobSundials - ! ********************************************************************************************************** - ! private function: get the off-diagonal index in the band-diagonal matrix - ! ********************************************************************************************************** - function ixOffDiag(jState,iState) - implicit none - integer(i4b),intent(in) :: jState ! off-diagonal state - integer(i4b),intent(in) :: iState ! diagonal state - integer(i4b) :: ixOffDiag ! off-diagonal index in gthe band-diagonal matrix - ixOffDiag = ixBandOffset + jState - iState - end function ixOffDiag +! ********************************************************************************************************** +! private function: get the off-diagonal index in the band-diagonal matrix +! ********************************************************************************************************** +function ixOffDiag(jState,iState) + implicit none + integer(i4b),intent(in) :: jState ! off-diagonal state + integer(i4b),intent(in) :: iState ! diagonal state + integer(i4b) :: ixOffDiag ! off-diagonal index in gthe band-diagonal matrix + ixOffDiag = ixBandOffset + jState - iState +end function ixOffDiag end module computJacobSundials_module diff --git a/build/source/engine/evalJac4IDA.f90 b/build/source/engine/evalJac4IDA.f90 index ca8703109..873db431c 100644 --- a/build/source/engine/evalJac4IDA.f90 +++ b/build/source/engine/evalJac4IDA.f90 @@ -43,10 +43,14 @@ integer(c_int) function evalJac4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & use fsundials_nvector_mod use fsundials_matrix_mod use fnvector_serial_mod + use fsunmatrix_band_mod use fsunmatrix_dense_mod use nrtype use type4IDA use eval8JacDAE_module,only:eval8JacDAE ! compute Jacobian matrix + USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix + USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix + USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix !======= Declarations ========= implicit none @@ -81,7 +85,8 @@ integer(c_int) function evalJac4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & stateVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_y) stateVecPrime(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_yp) rVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_r) - Jac(1:eqns_data%nState, 1:eqns_data%nState) => FSUNDenseMatrix_Data(sunmat_J) + if (eqns_data%ixMatrix==ixBandMatrix) Jac(1:nBands, 1:eqns_data%nState) => FSUNBandMatrix_Data(sunmat_J) + if (eqns_data%ixMatrix==ixFullMatrix) Jac(1:eqns_data%nState, 1:eqns_data%nState) => FSUNDenseMatrix_Data(sunmat_J) ! compute Jacobian matrix call eval8JacDAE(& diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index fb1113539..e08fd853a 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -368,12 +368,10 @@ subroutine summaSolveSundialsIDA( & retval = FIDASetLinearSolver(ida_mem, sunlinsol_LS, sunmat_A); if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetLinearSolver'; return; endif - if(ixMatrix == ixFullMatrix)then ! Set the user-supplied Jacobian routine !comment this line out to use FD Jacobian retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetJacFn'; return; endif - endif ! Create Newton SUNNonlinearSolver object sunnonlin_NLS => FSUNNonlinSol_Newton(sunvec_y) From c4beeb5706e43910943f52b9aedfe8000a8d09b5 Mon Sep 17 00:00:00 2001 From: Kyle Date: Fri, 23 Sep 2022 16:32:09 +0000 Subject: [PATCH 0346/1472] added final adjustment to soilLiqFlx --- build/source/engine/soilLiqFlx.f90 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index 7de450187..a3cb0b475 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -1510,17 +1510,17 @@ subroutine surfaceFlx(& dFrozenArea_dTk(0) = 0._rkind !print*, 'scalarFrozenArea, rootZoneIce = ', scalarFrozenArea, rootZoneIce - ! compute infiltration derivative for layers not at surface - if (xMaxInfilRate < scalarRainPlusMelt) then ! = dXMaxInfilRate_d + if (xMaxInfilRate < scalarRainPlusMelt) then ! = dXMaxInfilRate_d, dependent on layers not at surface + dInfilRate_dWat(0) = 0._rkind + dInfilRate_dTk(0) = 0._rkind dInfilRate_dWat(1:nSoil) = dXMaxInfilRate_dWat(:) dInfilRate_dTk(1:nSoil) = dXMaxInfilRate_dTk(:) - else ! = dRainPlusMelt_d only dependent on canopy - dInfilRate_dWat(1:nSoil) = 0._rkind !only calculate for layers that are not the surface - dInfilRate_dTk(1:nSoil) = 0._rkind !only calculate for layers that are not the surface + else ! = dRainPlusMelt_d, dependent on above layer (canopy or snow) water and temp + dInfilRate_dWat(0) = above_soilLiqFluxDeriv*above_soilFracLiq + dInfilRate_dTk(0) = above_soilLiqFluxDeriv*above_soildLiq_dTk + dInfilRate_dWat(1:nSoil) = 0._rkind + dInfilRate_dTk(1:nSoil) = 0._rkind endif - ! dependent on above layer (canopy or snow) water and temp - dInfilRate_dWat(0) = above_soilLiqFluxDeriv*above_soilFracLiq - dInfilRate_dTk(0) = above_soilLiqFluxDeriv*above_soildLiq_dTk ! dq w.r.t. infiltration only, scalarRainPlusMelt accounted for in computeJacob module dq_dHydStateVec(:) = (1._rkind - scalarFrozenArea) * ( dInfilArea_dWat(:)*min(scalarRainPlusMelt,xMaxInfilRate) + scalarInfilArea*dInfilRate_dWat(:) ) +& From 8c3a1bc8f8c2dd0f40f0ddc3674fed33a41b95aa Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 26 Sep 2022 16:29:45 +0000 Subject: [PATCH 0347/1472] fixed style changes for coupled em --- build/source/engine/coupled_em.f90 | 2292 ++++++++++++++-------------- 1 file changed, 1136 insertions(+), 1156 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 178fe8d74..129653a79 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -96,1169 +96,1149 @@ module coupled_em_module contains - ! ************************************************************************************************ - ! public subroutine coupled_em: run the coupled energy-mass model for one timestep - ! ************************************************************************************************ - subroutine coupled_em(& - ! model control - hruId, & ! intent(in): hruId - dt_init, & ! intent(inout): used to initialize the size of the sub-step - computeVegFlux, & ! intent(inout): flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) - ! data structures (input) - type_data, & ! intent(in): local classification of soil veg etc. for each HRU - attr_data, & ! intent(in): local attributes for each HRU - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - bvar_data, & ! intent(in): basin-average variables - lookup_data, & ! intent(in): lookup tables - ! data structures (input-output) - indx_data, & ! intent(inout): model indices - prog_data, & ! intent(inout): prognostic variables for a local HRU - diag_data, & ! intent(inout): diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! error control - err,message) ! intent(out): error control - ! structure allocations - USE allocspace_module,only:allocLocal ! allocate local data structures - USE allocspace_module,only:resizeData ! clone a data structure - ! preliminary subroutines - USE vegPhenlgy_module,only:vegPhenlgy ! compute vegetation phenology - USE vegNrgFlux_module,only:wettedFrac ! compute wetted fraction of the canopy (used in sw radiation fluxes) - USE snowAlbedo_module,only:snowAlbedo ! compute snow albedo - USE vegSWavRad_module,only:vegSWavRad ! compute canopy sw radiation fluxes - USE canopySnow_module,only:canopySnow ! compute interception and unloading of snow from the vegetation canopy - USE volicePack_module,only:newsnwfall ! compute change in the top snow layer due to throughfall and unloading - USE volicePack_module,only:volicePack ! merge and sub-divide snow layers, if necessary - USE diagn_evar_module,only:diagn_evar ! compute diagnostic energy variables -- thermal conductivity and heat capacity - ! the model solver - USE indexState_module,only:indexState ! define indices for all model state variables and layers - USE opSplittin_module,only:opSplittin ! solve the system of thermodynamic and hydrology equations for a given substep - USE time_utils_module,only:elapsedSec ! calculate the elapsed time - ! additional subroutines - USE tempAdjust_module,only:tempAdjust ! adjust snow temperature associated with new snowfall - USE snwDensify_module,only:snwDensify ! snow densification (compaction and cavitation) - USE var_derive_module,only:calcHeight ! module to calculate height at layer interfaces and layer mid-point - USE computSnowDepth_module,only:computSnowDepth - - implicit none - ! model control - integer(8),intent(in) :: hruId ! hruId - real(rkind),intent(inout) :: dt_init ! used to initialize the size of the sub-step - logical(lgt),intent(inout) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) - ! data structures (input) - type(var_i),intent(in) :: type_data ! type of vegetation and soil - type(var_d),intent(in) :: attr_data ! spatial attributes - type(var_d),intent(in) :: forc_data ! model forcing data - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(in) :: bvar_data ! basin-average model variables - type(zLookup),intent(in) :: lookup_data ! lookup tables - ! data structures (input-output) - type(var_ilength),intent(inout) :: indx_data ! state vector geometry - type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - ! error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ===================================================================================================================================================== - ! ===================================================================================================================================================== - ! local variables - character(len=256) :: cmessage ! error message - integer(i4b) :: nSnow ! number of snow layers - integer(i4b) :: nSoil ! number of soil layers - integer(i4b) :: nLayers ! total number of layers - integer(i4b) :: nState ! total number of state variables - real(rkind) :: dtSave ! length of last input model sub-step (seconds) - real(rkind) :: dt_sub ! length of model sub-step (seconds) - real(rkind) :: dt_wght ! weight applied to model sub-step (dt_sub/data_step) - real(rkind) :: dt_solv ! seconds in the data step that have been completed - real(rkind) :: dtMultiplier ! time step multiplier (-) based on what happenned in "opSplittin" - real(rkind) :: minstep,maxstep ! minimum and maximum time step length (seconds) - integer(i4b) :: nsub ! number of substeps - logical(lgt) :: computeVegFluxOld ! flag to indicate if we are computing fluxes over vegetation on the previous sub step - logical(lgt) :: includeAquifer ! flag to denote that an aquifer is included - logical(lgt) :: modifiedLayers ! flag to denote that snow layers were modified - logical(lgt) :: modifiedVegState ! flag to denote that vegetation states were modified - type(var_dlength) :: flux_mean ! timestep-average model fluxes for a local HRU - integer(i4b) :: nLayersRoots ! number of soil layers that contain roots - real(rkind) :: exposedVAI ! exposed vegetation area index - real(rkind) :: dCanopyWetFraction_dWat ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) - real(rkind) :: dCanopyWetFraction_dT ! derivative in wetted fraction w.r.t. canopy temperature (K-1) - real(rkind),parameter :: varNotUsed1=-9999._rkind ! variables used to calculate derivatives (not needed here) - real(rkind),parameter :: varNotUsed2=-9999._rkind ! variables used to calculate derivatives (not needed here) - integer(i4b) :: iSnow ! index of snow layers - integer(i4b) :: iLayer ! index of model layers - real(rkind) :: massLiquid ! mass liquid water (kg m-2) - real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) - real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) - integer(i4b) :: ixSolution ! solution method used by opSplitting - logical(lgt) :: firstSubStep ! flag to denote if the first time step - logical(lgt) :: stepFailure ! flag to denote the need to reduce length of the coupled step and try again - logical(lgt) :: tooMuchMelt ! flag to denote that there was too much melt in a given time step - logical(lgt) :: tooMuchSublim ! flag to denote that there was too much sublimation in a given time step - logical(lgt) :: doLayerMerge ! flag to denote the need to merge snow layers - logical(lgt) :: pauseFlag ! flag to pause execution - logical(lgt),parameter :: backwardsCompatibility=.true. ! flag to denote a desire to ensure backwards compatibility with previous branches. - type(var_ilength) :: indx_temp ! temporary model index variables - type(var_dlength) :: prog_temp ! temporary model prognostic variables - type(var_dlength) :: diag_temp ! temporary model diagnostic variables - ! check SWE - real(rkind) :: oldSWE ! SWE at the start of the substep - real(rkind) :: newSWE ! SWE at the end of the substep - real(rkind) :: delSWE ! change in SWE over the subtep - real(rkind) :: effRainfall ! effective rainfall (kg m-2 s-1) - real(rkind) :: effSnowfall ! effective snowfall (kg m-2 s-1) - real(rkind) :: sfcMeltPond ! surface melt pond (kg m-2) - real(rkind) :: massBalance ! mass balance error (kg m-2) - ! balance checks - integer(i4b) :: iVar ! loop through model variables - real(rkind) :: totalSoilCompress ! total soil compression (kg m-2) - real(rkind) :: scalarCanopyWatBalError ! water balance error for the vegetation canopy (kg m-2) - real(rkind) :: scalarSoilWatBalError ! water balance error (kg m-2) - real(rkind) :: scalarInitCanopyLiq ! initial liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarInitCanopyIce ! initial ice on the vegetation canopy (kg m-2) - real(rkind) :: balanceCanopyWater0 ! total water stored in the vegetation canopy at the start of the step (kg m-2) - real(rkind) :: balanceCanopyWater1 ! total water stored in the vegetation canopy at the end of the step (kg m-2) - real(rkind) :: balanceSoilWater0 ! total soil storage at the start of the step (kg m-2) - real(rkind) :: balanceSoilWater1 ! total soil storage at the end of the step (kg m-2) - real(rkind) :: balanceSoilInflux ! input to the soil zone - real(rkind) :: balanceSoilBaseflow ! output from the soil zone - real(rkind) :: balanceSoilDrainage ! output from the soil zone - real(rkind) :: balanceSoilET ! output from the soil zone - real(rkind) :: balanceAquifer0 ! total aquifer storage at the start of the step (kg m-2) - real(rkind) :: balanceAquifer1 ! total aquifer storage at the end of the step (kg m-2) - ! test balance checks - logical(lgt), parameter :: printBalance=.false. ! flag to print the balance checks - real(rkind), allocatable :: liqSnowInit(:) ! volumetric liquid water conetnt of snow at the start of the time step - real(rkind), allocatable :: liqSoilInit(:) ! soil moisture at the start of the time step - ! timing information - real(rkind) :: startTime ! start time (used to compute wall clock time) - real(rkind) :: endTime ! end time (used to compute wall clock time) - ! ---------------------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="coupled_em/" - - ! This is the start of a data step for a local HRU - - ! get the start time - call cpu_time(startTime) - - ! check that the decision is supported - if(model_decisions(iLookDECISIONS%groundwatr)%iDecision==bigBucket .and. & - model_decisions(iLookDECISIONS%spatial_gw)%iDecision/=localColumn)then - message=trim(message)//'expect "spatial_gw" decision to equal localColumn when "groundwatr" decision is bigBucket' - err=20; return - endif - - ! check if the aquifer is included - includeAquifer = (model_decisions(iLookDECISIONS%groundwatr)%iDecision==bigBucket) - - ! initialize the numerix tracking variables - indx_data%var(iLookINDEX%numberFluxCalc )%dat(1) = 0 ! number of flux calculations (-) - indx_data%var(iLookINDEX%numberStateSplit )%dat(1) = 0 ! number of state splitting solutions (-) - indx_data%var(iLookINDEX%numberDomainSplitNrg )%dat(1) = 0 ! number of domain splitting solutions for energy (-) - indx_data%var(iLookINDEX%numberDomainSplitMass)%dat(1) = 0 ! number of domain splitting solutions for mass (-) - indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1) = 0 ! number of scalar solutions (-) - - ! link canopy depth to the information in the data structure - canopy: associate(canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ) ! intent(out): [dp] canopy depth (m) - - ! start by NOT pausing - pauseFlag=.false. - - ! start by assuming that the step is successful - stepFailure = .false. - doLayerMerge = .false. - - ! initialize flags to modify the veg layers or modify snow layers - modifiedLayers = .false. ! flag to denote that snow layers were modified - modifiedVegState = .false. ! flag to denote that vegetation states were modified - - ! define the first step - firstSubStep = .true. - - ! count the number of snow and soil layers - ! NOTE: need to re-compute the number of snow and soil layers at the start of each sub-step because the number of layers may change - ! (nSnow and nSoil are shared in the data structure) - nSnow = count(indx_data%var(iLookINDEX%layerType)%dat==iname_snow) - nSoil = count(indx_data%var(iLookINDEX%layerType)%dat==iname_soil) - - ! compute the total number of snow and soil layers - nLayers = nSnow + nSoil - - ! create temporary data structures for prognostic variables - call resizeData(prog_meta(:),prog_data,prog_temp,err=err,message=cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! create temporary data structures for diagnostic variables - call resizeData(diag_meta(:),diag_data,diag_temp,err=err,message=cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! create temporary data structures for index variables - call resizeData(indx_meta(:),indx_data,indx_temp,err=err,message=cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! allocate space for the local fluxes - call allocLocal(averageFlux_meta(:)%var_info,flux_mean,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - - ! initialize compression and surface melt pond - sfcMeltPond = 0._rkind ! change in storage associated with the surface melt pond (kg m-2) - totalSoilCompress = 0._rkind ! change in soil storage associated with compression of the matrix (kg m-2) - - ! initialize mean fluxes - do iVar=1,size(averageFlux_meta) - flux_mean%var(iVar)%dat(:) = 0._rkind - end do - - ! associate local variables with information in the data structures - associate(& - ! state variables in the vegetation canopy - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! canopy liquid water (kg m-2) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! canopy ice content (kg m-2) - ! state variables in the soil domain - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers) ,& ! depth of each soil layer (m) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat(nSnow+1:nLayers) ,& ! volumetric ice content in each soil layer (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(nSnow+1:nLayers) ,& ! volumetric liquid water content in each soil layer (-) - scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! aquifer storage (m) - scalarTotalSoilIce => diag_data%var(iLookDIAG%scalarTotalSoilIce)%dat(1) ,& ! total ice in the soil column (kg m-2) - scalarTotalSoilLiq => diag_data%var(iLookDIAG%scalarTotalSoilLiq)%dat(1) ,& ! total liquid water in the soil column (kg m-2) - scalarTotalSoilWat => diag_data%var(iLookDIAG%scalarTotalSoilWat)%dat(1) & ! total water in the soil column (kg m-2) - ) ! (association of local variables with information in the data structures - - ! save the liquid water and ice on the vegetation canopy - scalarInitCanopyLiq = scalarCanopyLiq ! initial liquid water on the vegetation canopy (kg m-2) - scalarInitCanopyIce = scalarCanopyIce ! initial ice on the vegetation canopy (kg m-2) - - ! compute total soil moisture and ice at the *START* of the step (kg m-2) - scalarTotalSoilLiq = sum(iden_water*mLayerVolFracLiq(1:nSoil)*mLayerDepth(1:nSoil)) - scalarTotalSoilIce = sum(iden_water*mLayerVolFracIce(1:nSoil)*mLayerDepth(1:nSoil)) ! NOTE: no expansion and hence use iden_water - - ! compute storage of water in the canopy and the soil - balanceCanopyWater0 = scalarCanopyLiq + scalarCanopyIce - balanceSoilWater0 = scalarTotalSoilLiq + scalarTotalSoilIce - - ! get the total aquifer storage at the start of the time step (kg m-2) - balanceAquifer0 = scalarAquiferStorage*iden_water - - ! save liquid water content - if(printBalance)then - allocate(liqSnowInit(nSnow), liqSoilInit(nSoil), stat=err) - if(err/=0)then - message=trim(message)//'unable to allocate space for the initial vectors' - err=20; return - endif - if(nSnow>0) liqSnowInit = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow) - liqSoilInit = mLayerVolFracLiq - endif - - ! end association of local variables with information in the data structures - end associate - - ! short-cut to the algorithmic control parameters - ! NOTE - temporary assignment of minstep to foce something reasonable - minstep = 10._rkind ! mpar_data%var(iLookPARAM%minstep)%dat(1) ! minimum time step (s) - maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) - !print*, 'minstep, maxstep = ', minstep, maxstep - - ! compute the number of layers with roots - nLayersRoots = count(prog_data%var(iLookPROG%iLayerHeight)%dat(nSnow:nLayers-1) < mpar_data%var(iLookPARAM%rootingDepth)%dat(1)-verySmall) - if(nLayersRoots == 0)then - message=trim(message)//'no roots within the soil profile' - err=20; return - end if - - ! define the foliage nitrogen factor - diag_data%var(iLookDIAG%scalarFoliageNitrogenFactor)%dat(1) = 1._rkind ! foliage nitrogen concentration (1.0 = saturated) - - ! save SWE - oldSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) - !print*, 'nSnow = ', nSnow - !print*, 'oldSWE = ', oldSWE - - ! *** compute phenology... - ! ------------------------ - - ! compute the temperature of the root zone: used in vegetation phenology - diag_data%var(iLookDIAG%scalarRootZoneTemp)%dat(1) = sum(prog_data%var(iLookPROG%mLayerTemp)%dat(nSnow+1:nSnow+nLayersRoots)) / real(nLayersRoots, kind(rkind)) - - ! remember if we compute the vegetation flux on the previous sub-step - computeVegFluxOld = computeVegFlux - - ! compute the exposed LAI and SAI and whether veg is buried by snow - call vegPhenlgy(& - ! input/output: data structures - model_decisions, & ! intent(in): model decisions - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output - computeVegFlux, & ! intent(out): flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) - canopyDepth, & ! intent(out): canopy depth (m) - exposedVAI, & ! intent(out): exposed vegetation area index (m2 m-2) - err,cmessage) ! intent(out): error control - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - - ! check - if(computeVegFlux)then - if(canopyDepth < epsilon(canopyDepth))then - message=trim(message)//'canopy depth is zero when computeVegFlux flag is .true.' - err=20; return - endif - endif - - ! flag the case where number of vegetation states has changed - modifiedVegState = (computeVegFlux.neqv.computeVegFluxOld) - - ! *** compute wetted canopy area... - ! --------------------------------- - - ! compute maximum canopy liquid water (kg m-2) - diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1) = mpar_data%var(iLookPARAM%refInterceptCapRain)%dat(1)*exposedVAI - - ! compute maximum canopy ice content (kg m-2) - ! NOTE 1: this is used to compute the snow fraction on the canopy, as used in *BOTH* the radiation AND canopy sublimation routines - ! NOTE 2: this is a different variable than the max ice used in the throughfall (snow interception) calculations - ! NOTE 3: use maximum per unit leaf area storage capacity for snow (kg m-2) - select case(model_decisions(iLookDECISIONS%snowIncept)%iDecision) - case(lightSnow); diag_data%var(iLookDIAG%scalarCanopyIceMax)%dat(1) = exposedVAI*mpar_data%var(iLookPARAM%refInterceptCapSnow)%dat(1) - case(stickySnow); diag_data%var(iLookDIAG%scalarCanopyIceMax)%dat(1) = exposedVAI*mpar_data%var(iLookPARAM%refInterceptCapSnow)%dat(1)*4._rkind - case default; message=trim(message)//'unable to identify option for maximum branch interception capacity'; err=20; return - end select ! identifying option for maximum branch interception capacity - !print*, 'diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1) = ', diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1) - !print*, 'diag_data%var(iLookDIAG%scalarCanopyIceMax)%dat(1) = ', diag_data%var(iLookDIAG%scalarCanopyIceMax)%dat(1) - - ! compute wetted fraction of the canopy - ! NOTE: assume that the wetted fraction is constant over the substep for the radiation calculations - if(computeVegFlux)then - - ! compute wetted fraction of the canopy - call wettedFrac(& - ! input - .false., & ! flag to denote if derivatives are required - .false., & ! flag to denote if derivatives are calculated numerically - (prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) < Tfreeze), & ! flag to denote if the canopy is frozen - varNotUsed1, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) - varNotUsed2, & ! fraction of liquid water on the canopy - prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1), & ! canopy liquid water (kg m-2) - prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! canopy ice (kg m-2) - diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1), & ! maximum canopy liquid water (kg m-2) - diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1), & ! maximum canopy ice content (kg m-2) - mpar_data%var(iLookPARAM%canopyWettingFactor)%dat(1), & ! maximum wetted fraction of the canopy (-) - mpar_data%var(iLookPARAM%canopyWettingExp)%dat(1), & ! exponent in canopy wetting function (-) - ! output - diag_data%var(iLookDIAG%scalarCanopyWetFraction)%dat(1), & ! canopy wetted fraction (-) - dCanopyWetFraction_dWat, & ! derivative in wetted fraction w.r.t. canopy liquid water content (kg-1 m2) - dCanopyWetFraction_dT, & ! derivative in wetted fraction w.r.t. canopy liquid water content (kg-1 m2) - err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - ! vegetation is completely buried by snow (or no veg exists at all) - else - diag_data%var(iLookDIAG%scalarCanopyWetFraction)%dat(1) = 0._rkind - dCanopyWetFraction_dWat = 0._rkind - dCanopyWetFraction_dT = 0._rkind - end if - - ! *** compute snow albedo... - ! -------------------------- - ! NOTE: this should be done before the radiation calculations - ! NOTE: uses snowfall; should really use canopy throughfall + canopy unloading - call snowAlbedo(& - ! input: model control - data_step, & ! intent(in): model time step (s) - (nSnow > 0), & ! intent(in): logical flag to denote if snow is present - ! input/output: data structures - model_decisions, & ! intent(in): model decisions - mpar_data, & ! intent(in): model parameters - flux_data, & ! intent(in): model flux variables - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - prog_data, & ! intent(inout): model prognostic variables for a local HRU - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - - - ! *** compute canopy sw radiation fluxes... - ! ----------------------------------------- - call vegSWavRad(& - data_step, & ! intent(in): time step (s) -- only used in Noah-MP radiation, to compute albedo - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) - type_data, & ! intent(in): type of vegetation and soil - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model flux variables - err,cmessage) ! intent(out): error control - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - - - ! *** compute canopy throughfall and unloading... - ! ----------------------------------------------- - ! NOTE 1: this needs to be done before solving the energy and liquid water equations, to account for the heat advected with precipitation (and throughfall/unloading) - ! NOTE 2: the unloading flux is computed using canopy drip (scalarCanopyLiqDrainage) from the previous time step - call canopySnow(& - ! input: model control - data_step, & ! intent(in): time step (seconds) - exposedVAI, & ! intent(in): exposed vegetation area index (m2 m-2) - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation - ! input/output: data structures - model_decisions, & ! intent(in): model decisions - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(inout): model prognostic variables for a local HRU - flux_data, & ! intent(inout): model flux variables - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - - ! adjust canopy temperature to account for new snow - if(computeVegFlux)then ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) - call tempAdjust(& - ! input: derived parameters - canopyDepth, & ! intent(in): canopy depth (m) - ! input/output: data structures - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(out): model diagnostic variables for a local HRU - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - endif ! if computing fluxes over vegetation - - ! initialize drainage and throughfall - ! NOTE 1: this needs to be done before solving the energy and liquid water equations, to account for the heat advected with precipitation - ! NOTE 2: this initialization needs to be done AFTER the call to canopySnow, since canopySnow uses canopy drip drom the previous time step - if(.not.computeVegFlux)then - flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) = flux_data%var(iLookFLUX%scalarRainfall)%dat(1) - flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) = 0._rkind - else - flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) = 0._rkind - flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) = 0._rkind - end if - - ! **************************************************************************************************** - ! *** MAIN SOLVER ************************************************************************************ - ! **************************************************************************************************** - - ! initialize the length of the sub-step - dt_solv = 0._rkind ! length of time step that has been completed (s) - dt_init = min(data_step,maxstep) ! initial substep length (s) - dt_sub = dt_init ! length of substep - dtSave = dt_init ! length of substep - - ! initialize the number of sub-steps - nsub=0 - - ! loop through sub-steps - substeps: do ! continuous do statement with exit clause (alternative to "while") - - ! print progress - !print*, '*** new substep' - !write(*,'(a,3(f11.4,1x))') 'dt_sub, dt_init = ', dt_sub, dt_init - - ! print progress - if(globalPrintFlag)then - write(*,'(a,1x,4(f13.5,1x))') ' start of step: dt_init, dt_sub, dt_solv, data_step: ', dt_init, dt_sub, dt_solv, data_step - print*, 'stepFailure = ', stepFailure - print*, 'before resizeData: nSnow, nSoil = ', nSnow, nSoil - endif - - ! increment the number of sub-steps - nsub = nsub+1 - - ! resize the "indx_data" structure - ! NOTE: this is necessary because the length of index variables depends on a given split - ! --> the resize here is overwritten later (in indexSplit) - ! --> admittedly ugly, and retained for now - if(stepFailure)then - call resizeData(indx_meta(:),indx_temp,indx_data,err=err,message=cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - else - call resizeData(indx_meta(:),indx_data,indx_temp,err=err,message=cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - endif - - ! save/recover copies of index variables - do iVar=1,size(indx_data%var) - !print*, 'indx_meta(iVar)%varname = ', trim(indx_meta(iVar)%varname) - select case(stepFailure) - case(.false.); indx_temp%var(iVar)%dat(:) = indx_data%var(iVar)%dat(:) - case(.true.); indx_data%var(iVar)%dat(:) = indx_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - ! save/recover copies of prognostic variables - do iVar=1,size(prog_data%var) - !print*, 'prog_meta(iVar)%varname = ', trim(prog_meta(iVar)%varname) - select case(stepFailure) - case(.false.); prog_temp%var(iVar)%dat(:) = prog_data%var(iVar)%dat(:) - case(.true.); prog_data%var(iVar)%dat(:) = prog_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - ! save/recover copies of diagnostic variables - do iVar=1,size(diag_data%var) - select case(stepFailure) - case(.false.); diag_temp%var(iVar)%dat(:) = diag_data%var(iVar)%dat(:) - case(.true.); diag_data%var(iVar)%dat(:) = diag_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - ! re-assign dimension lengths - nSnow = count(indx_data%var(iLookINDEX%layerType)%dat==iname_snow) - nSoil = count(indx_data%var(iLookINDEX%layerType)%dat==iname_soil) - nLayers = nSnow+nSoil - - ! *** merge/sub-divide snow layers... - ! ----------------------------------- - call volicePack(& - ! input/output: model data structures - doLayerMerge, & ! intent(in): flag to force merge of snow layers - model_decisions, & ! intent(in): model decisions - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): type of each layer - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output - modifiedLayers, & ! intent(out): flag to denote that layers were modified - err,cmessage) ! intent(out): error control - if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if - - ! save the number of snow and soil layers - nSnow = indx_data%var(iLookINDEX%nSnow)%dat(1) - nSoil = indx_data%var(iLookINDEX%nSoil)%dat(1) - nLayers = indx_data%var(iLookINDEX%nLayers)%dat(1) - - ! compute the indices for the model state variables - if(firstSubStep .or. modifiedVegState .or. modifiedLayers)then - call indexState(computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - includeAquifer, & ! intent(in): flag to denote if included the aquifer - nSnow,nSoil,nLayers, & ! intent(in): number of snow and soil layers, and total number of layers - indx_data, & ! intent(inout): indices defining model states and layers - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - end if - - ! recreate the temporary data structures - ! NOTE: resizeData(meta, old, new, ..) - if(modifiedVegState .or. modifiedLayers)then - - ! create temporary data structures for prognostic variables - call resizeData(prog_meta(:),prog_data,prog_temp,copy=.true.,err=err,message=cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! create temporary data structures for diagnostic variables - call resizeData(diag_meta(:),diag_data,diag_temp,copy=.true.,err=err,message=cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! create temporary data structures for index variables - call resizeData(indx_meta(:),indx_data,indx_temp,copy=.true.,err=err,message=cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - do iVar=1,size(indx_data%var) - !print*, 'indx_meta(iVar)%varname = ', trim(indx_meta(iVar)%varname) - select case(stepFailure) - case(.false.); indx_temp%var(iVar)%dat(:) = indx_data%var(iVar)%dat(:) - case(.true.); indx_data%var(iVar)%dat(:) = indx_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - endif ! if modified the states - - ! define the number of state variables - nState = indx_data%var(iLookINDEX%nState)%dat(1) - - ! *** compute diagnostic variables for each layer... - ! -------------------------------------------------- - ! NOTE: this needs to be done AFTER volicePack, since layers may have been sub-divided and/or merged - call diagn_evar(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): canopy depth (m) - ! input/output: data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if - - - ! *** compute melt of the "snow without a layer"... - ! ------------------------------------------------- - ! NOTE: forms a surface melt pond, which drains into the upper-most soil layer through the time step - ! (check for the special case of "snow without a layer") - if(nSnow==0)then - call implctMelt(& - ! input/output: integrated snowpack properties - prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(inout): snow water equivalent (kg m-2) - prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(inout): snow depth (m) - prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1), & ! intent(inout): surface melt pond (kg m-2) - ! input/output: properties of the upper-most soil layer - prog_data%var(iLookPROG%mLayerTemp)%dat(nSnow+1), & ! intent(inout): surface layer temperature (K) - prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1), & ! intent(inout): surface layer depth (m) - diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat(nSnow+1),& ! intent(inout): surface layer volumetric heat capacity (J m-3 K-1) - ! output: error control - err,cmessage ) ! intent(out): error control - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - end if - - ! *** solve model equations... - ! ---------------------------- - - ! save input step - dtSave = dt_sub - !write(*,'(a,1x,3(f12.5,1x))') trim(message)//'before opSplittin: dt_init, dt_sub, dt_solv = ', dt_init, dt_sub, dt_solv - - - ! get the new solution - call opSplittin(& - ! input: model control - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - nState, & ! intent(in): total number of layers - dt_sub, & ! intent(inout): length of the model sub-step - (nsub==1), & ! intent(in): logical flag to denote the first substep - computeVegFlux, & ! intent(in): logical flag to compute fluxes within the vegetation canopy - ! input/output: data structures - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): index data - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - bvar_data, & ! intent(in): model variables for the local basin - lookup_data, & ! intent(in): lookup tables - model_decisions, & ! intent(in): model decisions - ! output: model control - dtMultiplier, & ! intent(out): substep multiplier (-) - tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt - stepFailure, & ! intent(out): flag to denote that the coupled step failed - ixSolution, & ! intent(out): solution method used in this iteration - err,cmessage) ! intent(out): error code and error message - - - ! check for all errors (error recovery within opSplittin) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - !print*, 'completed step' - !print*, 'PAUSE: '; read(*,*) - - ! process the flag for too much melt - if(tooMuchMelt)then - stepFailure = .true. - doLayerMerge = .true. - else - doLayerMerge = .false. - endif - - ! handle special case of the step failure - ! NOTE: need to revert back to the previous state vector that we were happy with and reduce the time step - if(stepFailure)then - ! halve step - dt_sub = dtSave/2._rkind - ! check that the step is not tiny - if(dt_sub < minstep)then - print*,ixSolution - print*, 'dtSave, dt_sub', dtSave, dt_sub - message=trim(message)//'length of the coupled step is below the minimum step length' - err=20; return - endif - ! try again - cycle substeps - endif - - ! update first step - firstSubStep=.false. - - ! *** remove ice due to sublimation... - ! -------------------------------------------------------------- - sublime: associate(& - scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1), & ! sublimation from the vegetation canopy (kg m-2 s-1) - scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! sublimation from the snow surface (kg m-2 s-1) - scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1), & ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1), & ! sensible heat flux from the canopy to the canopy air space (W m-2) - scalarLatHeatGround => flux_data%var(iLookFLUX%scalarLatHeatGround)%dat(1), & ! latent heat flux from ground surface below vegetation (W m-2) - scalarSenHeatGround => flux_data%var(iLookFLUX%scalarSenHeatGround)%dat(1), & ! sensible heat flux from ground surface below vegetation (W m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1), & ! liquid water stored on the vegetation canopy (kg m-2) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! ice stored on the vegetation canopy (kg m-2) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat, & ! volumetric fraction of ice in the snow+soil domain (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat, & ! volumetric fraction of liquid water in the snow+soil domain (-) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat & ! depth of each snow+soil layer (m) - ) ! associations to variables in data structures - - ! * compute change in canopy ice content due to sublimation... - ! ------------------------------------------------------------ - if(computeVegFlux)then - - ! remove mass of ice on the canopy - scalarCanopyIce = scalarCanopyIce + scalarCanopySublimation*dt_sub - - ! if removed all ice, take the remaining sublimation from water - if(scalarCanopyIce < 0._rkind)then - scalarCanopyLiq = scalarCanopyLiq + scalarCanopyIce - scalarCanopyIce = 0._rkind - endif - - ! modify fluxes if there is insufficient canopy water to support the converged sublimation rate over the time step dt_sub - if(scalarCanopyLiq < 0._rkind)then - ! --> superfluous sublimation flux - superflousSub = -scalarCanopyLiq/dt_sub ! kg m-2 s-1 - superflousNrg = superflousSub*LH_sub ! W m-2 (J m-2 s-1) - ! --> update fluxes and states - scalarCanopySublimation = scalarCanopySublimation + superflousSub - scalarLatHeatCanopyEvap = scalarLatHeatCanopyEvap + superflousNrg - scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg - scalarCanopyLiq = 0._rkind - endif - - end if ! (if computing the vegetation flux) - - call computSnowDepth(& - dt_sub, & ! intent(in) - nSnow, & ! intent(in) - scalarSnowSublimation, & ! intent(in) - mLayerVolFracLiq, & ! intent(inout) - mLayerVolFracIce, & ! intent(inout) - prog_data%var(iLookPROG%mLayerTemp)%dat, & ! intent(in) - diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat, & ! intent(in) - mpar_data, & ! intent(in) - ! output - tooMuchSublim, & ! intent(out): flag to denote that there was too much sublimation in a given time step - mLayerDepth, & ! intent(inout) - ! error control - err,message) ! intent(out): error control - if(err/=0)then; err=55; return; end if - - ! process the flag for too much sublimation - if(tooMuchSublim)then - stepFailure = .true. - doLayerMerge = .true. - else - doLayerMerge = .false. - endif - - ! handle special case of the step failure - ! NOTE: need to revert back to the previous state vector that we were happy with and reduce the time step - if(stepFailure)then - ! halve step - dt_sub = dtSave/2._rkind - ! check that the step is not tiny - if(dt_sub < minstep)then - print*,ixSolution - print*, 'dtSave, dt_sub', dtSave, dt_sub - message=trim(message)//'length of the coupled step is below the minimum step length' +! ************************************************************************************************ +! public subroutine coupled_em: run the coupled energy-mass model for one timestep +! ************************************************************************************************ +subroutine coupled_em(& + ! model control + hruId, & ! intent(in): hruId + dt_init, & ! intent(inout): used to initialize the size of the sub-step + computeVegFlux, & ! intent(inout): flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) + ! data structures (input) + type_data, & ! intent(in): local classification of soil veg etc. for each HRU + attr_data, & ! intent(in): local attributes for each HRU + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + bvar_data, & ! intent(in): basin-average variables + lookup_data, & ! intent(in): lookup tables + ! data structures (input-output) + indx_data, & ! intent(inout): model indices + prog_data, & ! intent(inout): prognostic variables for a local HRU + diag_data, & ! intent(inout): diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! error control + err,message) ! intent(out): error control + ! structure allocations + USE allocspace_module,only:allocLocal ! allocate local data structures + USE allocspace_module,only:resizeData ! clone a data structure + ! preliminary subroutines + USE vegPhenlgy_module,only:vegPhenlgy ! compute vegetation phenology + USE vegNrgFlux_module,only:wettedFrac ! compute wetted fraction of the canopy (used in sw radiation fluxes) + USE snowAlbedo_module,only:snowAlbedo ! compute snow albedo + USE vegSWavRad_module,only:vegSWavRad ! compute canopy sw radiation fluxes + USE canopySnow_module,only:canopySnow ! compute interception and unloading of snow from the vegetation canopy + USE volicePack_module,only:newsnwfall ! compute change in the top snow layer due to throughfall and unloading + USE volicePack_module,only:volicePack ! merge and sub-divide snow layers, if necessary + USE diagn_evar_module,only:diagn_evar ! compute diagnostic energy variables -- thermal conductivity and heat capacity + ! the model solver + USE indexState_module,only:indexState ! define indices for all model state variables and layers + USE opSplittin_module,only:opSplittin ! solve the system of thermodynamic and hydrology equations for a given substep + USE time_utils_module,only:elapsedSec ! calculate the elapsed time + ! additional subroutines + USE tempAdjust_module,only:tempAdjust ! adjust snow temperature associated with new snowfall + USE snwDensify_module,only:snwDensify ! snow densification (compaction and cavitation) + USE var_derive_module,only:calcHeight ! module to calculate height at layer interfaces and layer mid-point + USE computSnowDepth_module,only:computSnowDepth + + implicit none + ! model control + integer(8),intent(in) :: hruId ! hruId + real(rkind),intent(inout) :: dt_init ! used to initialize the size of the sub-step + logical(lgt),intent(inout) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) + ! data structures (input) + type(var_i),intent(in) :: type_data ! type of vegetation and soil + type(var_d),intent(in) :: attr_data ! spatial attributes + type(var_d),intent(in) :: forc_data ! model forcing data + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(in) :: bvar_data ! basin-average model variables + type(zLookup),intent(in) :: lookup_data ! lookup tables + ! data structures (input-output) + type(var_ilength),intent(inout) :: indx_data ! state vector geometry + type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + ! error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ===================================================================================================================================================== + ! ===================================================================================================================================================== + ! local variables + character(len=256) :: cmessage ! error message + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers + integer(i4b) :: nLayers ! total number of layers + integer(i4b) :: nState ! total number of state variables + real(rkind) :: dtSave ! length of last input model sub-step (seconds) + real(rkind) :: dt_sub ! length of model sub-step (seconds) + real(rkind) :: dt_wght ! weight applied to model sub-step (dt_sub/data_step) + real(rkind) :: dt_solv ! seconds in the data step that have been completed + real(rkind) :: dtMultiplier ! time step multiplier (-) based on what happenned in "opSplittin" + real(rkind) :: minstep,maxstep ! minimum and maximum time step length (seconds) + integer(i4b) :: nsub ! number of substeps + logical(lgt) :: computeVegFluxOld ! flag to indicate if we are computing fluxes over vegetation on the previous sub step + logical(lgt) :: includeAquifer ! flag to denote that an aquifer is included + logical(lgt) :: modifiedLayers ! flag to denote that snow layers were modified + logical(lgt) :: modifiedVegState ! flag to denote that vegetation states were modified + type(var_dlength) :: flux_mean ! timestep-average model fluxes for a local HRU + integer(i4b) :: nLayersRoots ! number of soil layers that contain roots + real(rkind) :: exposedVAI ! exposed vegetation area index + real(rkind) :: dCanopyWetFraction_dWat ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) + real(rkind) :: dCanopyWetFraction_dT ! derivative in wetted fraction w.r.t. canopy temperature (K-1) + real(rkind),parameter :: varNotUsed1=-9999._rkind ! variables used to calculate derivatives (not needed here) + real(rkind),parameter :: varNotUsed2=-9999._rkind ! variables used to calculate derivatives (not needed here) + integer(i4b) :: iSnow ! index of snow layers + integer(i4b) :: iLayer ! index of model layers + real(rkind) :: massLiquid ! mass liquid water (kg m-2) + real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) + real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) + integer(i4b) :: ixSolution ! solution method used by opSplitting + logical(lgt) :: firstSubStep ! flag to denote if the first time step + logical(lgt) :: stepFailure ! flag to denote the need to reduce length of the coupled step and try again + logical(lgt) :: tooMuchMelt ! flag to denote that there was too much melt in a given time step + logical(lgt) :: tooMuchSublim ! flag to denote that there was too much sublimation in a given time step + logical(lgt) :: doLayerMerge ! flag to denote the need to merge snow layers + logical(lgt) :: pauseFlag ! flag to pause execution + logical(lgt),parameter :: backwardsCompatibility=.true. ! flag to denote a desire to ensure backwards compatibility with previous branches. + type(var_ilength) :: indx_temp ! temporary model index variables + type(var_dlength) :: prog_temp ! temporary model prognostic variables + type(var_dlength) :: diag_temp ! temporary model diagnostic variables + ! check SWE + real(rkind) :: oldSWE ! SWE at the start of the substep + real(rkind) :: newSWE ! SWE at the end of the substep + real(rkind) :: delSWE ! change in SWE over the subtep + real(rkind) :: effRainfall ! effective rainfall (kg m-2 s-1) + real(rkind) :: effSnowfall ! effective snowfall (kg m-2 s-1) + real(rkind) :: sfcMeltPond ! surface melt pond (kg m-2) + real(rkind) :: massBalance ! mass balance error (kg m-2) + ! balance checks + integer(i4b) :: iVar ! loop through model variables + real(rkind) :: totalSoilCompress ! total soil compression (kg m-2) + real(rkind) :: scalarCanopyWatBalError! water balance error for the vegetation canopy (kg m-2) + real(rkind) :: scalarSoilWatBalError ! water balance error (kg m-2) + real(rkind) :: scalarInitCanopyLiq ! initial liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarInitCanopyIce ! initial ice on the vegetation canopy (kg m-2) + real(rkind) :: balanceCanopyWater0 ! total water stored in the vegetation canopy at the start of the step (kg m-2) + real(rkind) :: balanceCanopyWater1 ! total water stored in the vegetation canopy at the end of the step (kg m-2) + real(rkind) :: balanceSoilWater0 ! total soil storage at the start of the step (kg m-2) + real(rkind) :: balanceSoilWater1 ! total soil storage at the end of the step (kg m-2) + real(rkind) :: balanceSoilInflux ! input to the soil zone + real(rkind) :: balanceSoilBaseflow ! output from the soil zone + real(rkind) :: balanceSoilDrainage ! output from the soil zone + real(rkind) :: balanceSoilET ! output from the soil zone + real(rkind) :: balanceAquifer0 ! total aquifer storage at the start of the step (kg m-2) + real(rkind) :: balanceAquifer1 ! total aquifer storage at the end of the step (kg m-2) + ! test balance checks + logical(lgt), parameter :: printBalance=.false. ! flag to print the balance checks + real(rkind), allocatable :: liqSnowInit(:) ! volumetric liquid water conetnt of snow at the start of the time step + real(rkind), allocatable :: liqSoilInit(:) ! soil moisture at the start of the time step + ! timing information + real(rkind) :: startTime ! start time (used to compute wall clock time) + real(rkind) :: endTime ! end time (used to compute wall clock time) + ! ---------------------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="coupled_em/" + + ! This is the start of a data step for a local HRU + + ! get the start time + call cpu_time(startTime) + + ! check that the decision is supported + if(model_decisions(iLookDECISIONS%groundwatr)%iDecision==bigBucket .and. & + model_decisions(iLookDECISIONS%spatial_gw)%iDecision/=localColumn)then + message=trim(message)//'expect "spatial_gw" decision to equal localColumn when "groundwatr" decision is bigBucket' err=20; return - endif - ! try again - cycle substeps endif - end associate sublime - - ! update coordinate variables - call calcHeight(& - ! input/output: data structures - indx_data, & ! intent(in): layer type - prog_data, & ! intent(inout): model variables for a local HRU + ! check if the aquifer is included + includeAquifer = (model_decisions(iLookDECISIONS%groundwatr)%iDecision==bigBucket) + + ! initialize the numerix tracking variables + indx_data%var(iLookINDEX%numberFluxCalc )%dat(1) = 0 ! number of flux calculations (-) + indx_data%var(iLookINDEX%numberStateSplit )%dat(1) = 0 ! number of state splitting solutions (-) + indx_data%var(iLookINDEX%numberDomainSplitNrg )%dat(1) = 0 ! number of domain splitting solutions for energy (-) + indx_data%var(iLookINDEX%numberDomainSplitMass)%dat(1) = 0 ! number of domain splitting solutions for mass (-) + indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1) = 0 ! number of scalar solutions (-) + + ! link canopy depth to the information in the data structure + canopy: associate(canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ) ! intent(out): [dp] canopy depth (m) + + ! start by NOT pausing + pauseFlag=.false. + + ! start by assuming that the step is successful + stepFailure = .false. + doLayerMerge = .false. + + ! initialize flags to modify the veg layers or modify snow layers + modifiedLayers = .false. ! flag to denote that snow layers were modified + modifiedVegState = .false. ! flag to denote that vegetation states were modified + + ! define the first step + firstSubStep = .true. + + ! count the number of snow and soil layers + ! NOTE: need to re-compute the number of snow and soil layers at the start of each sub-step because the number of layers may change + ! (nSnow and nSoil are shared in the data structure) + nSnow = count(indx_data%var(iLookINDEX%layerType)%dat==iname_snow) + nSoil = count(indx_data%var(iLookINDEX%layerType)%dat==iname_soil) + + ! compute the total number of snow and soil layers + nLayers = nSnow + nSoil + + ! create temporary data structures for prognostic variables + call resizeData(prog_meta(:),prog_data,prog_temp,err=err,message=cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! create temporary data structures for diagnostic variables + call resizeData(diag_meta(:),diag_data,diag_temp,err=err,message=cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! create temporary data structures for index variables + call resizeData(indx_meta(:),indx_data,indx_temp,err=err,message=cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! allocate space for the local fluxes + call allocLocal(averageFlux_meta(:)%var_info,flux_mean,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + ! initialize compression and surface melt pond + sfcMeltPond = 0._rkind ! change in storage associated with the surface melt pond (kg m-2) + totalSoilCompress = 0._rkind ! change in soil storage associated with compression of the matrix (kg m-2) + + ! initialize mean fluxes + do iVar=1,size(averageFlux_meta) + flux_mean%var(iVar)%dat(:) = 0._rkind + end do + + ! associate local variables with information in the data structures + associate(& + ! state variables in the vegetation canopy + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! canopy liquid water (kg m-2) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! canopy ice content (kg m-2) + ! state variables in the soil domain + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers) ,& ! depth of each soil layer (m) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat(nSnow+1:nLayers) ,& ! volumetric ice content in each soil layer (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(nSnow+1:nLayers) ,& ! volumetric liquid water content in each soil layer (-) + scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! aquifer storage (m) + scalarTotalSoilIce => diag_data%var(iLookDIAG%scalarTotalSoilIce)%dat(1) ,& ! total ice in the soil column (kg m-2) + scalarTotalSoilLiq => diag_data%var(iLookDIAG%scalarTotalSoilLiq)%dat(1) ,& ! total liquid water in the soil column (kg m-2) + scalarTotalSoilWat => diag_data%var(iLookDIAG%scalarTotalSoilWat)%dat(1) & ! total water in the soil column (kg m-2) + ) ! (association of local variables with information in the data structures + + ! save the liquid water and ice on the vegetation canopy + scalarInitCanopyLiq = scalarCanopyLiq ! initial liquid water on the vegetation canopy (kg m-2) + scalarInitCanopyIce = scalarCanopyIce ! initial ice on the vegetation canopy (kg m-2) + + ! compute total soil moisture and ice at the *START* of the step (kg m-2) + scalarTotalSoilLiq = sum(iden_water*mLayerVolFracLiq(1:nSoil)*mLayerDepth(1:nSoil)) + scalarTotalSoilIce = sum(iden_water*mLayerVolFracIce(1:nSoil)*mLayerDepth(1:nSoil)) ! NOTE: no expansion and hence use iden_water + + ! compute storage of water in the canopy and the soil + balanceCanopyWater0 = scalarCanopyLiq + scalarCanopyIce + balanceSoilWater0 = scalarTotalSoilLiq + scalarTotalSoilIce + + ! get the total aquifer storage at the start of the time step (kg m-2) + balanceAquifer0 = scalarAquiferStorage*iden_water + + ! save liquid water content + if(printBalance)then + allocate(liqSnowInit(nSnow), liqSoilInit(nSoil), stat=err) + if(err/=0)then + message=trim(message)//'unable to allocate space for the initial vectors' + err=20; return + endif + if(nSnow>0) liqSnowInit = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow) + liqSoilInit = mLayerVolFracLiq + endif + + ! end association of local variables with information in the data structures + end associate + + ! short-cut to the algorithmic control parameters + ! NOTE - temporary assignment of minstep to foce something reasonable + minstep = 10._rkind ! mpar_data%var(iLookPARAM%minstep)%dat(1) ! minimum time step (s) + maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) + + ! compute the number of layers with roots + nLayersRoots = count(prog_data%var(iLookPROG%iLayerHeight)%dat(nSnow:nLayers-1) < mpar_data%var(iLookPARAM%rootingDepth)%dat(1)-verySmall) + if(nLayersRoots == 0)then + message=trim(message)//'no roots within the soil profile' + err=20; return + end if + + ! define the foliage nitrogen factor + diag_data%var(iLookDIAG%scalarFoliageNitrogenFactor)%dat(1) = 1._rkind ! foliage nitrogen concentration (1.0 = saturated) + + ! save SWE + oldSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) + + + ! *** compute phenology... + ! ------------------------ + + ! compute the temperature of the root zone: used in vegetation phenology + diag_data%var(iLookDIAG%scalarRootZoneTemp)%dat(1) = sum(prog_data%var(iLookPROG%mLayerTemp)%dat(nSnow+1:nSnow+nLayersRoots)) / real(nLayersRoots, kind(rkind)) + + ! remember if we compute the vegetation flux on the previous sub-step + computeVegFluxOld = computeVegFlux + + ! compute the exposed LAI and SAI and whether veg is buried by snow + call vegPhenlgy(& + ! input/output: data structures + model_decisions, & ! intent(in): model decisions + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + ! output + computeVegFlux, & ! intent(out): flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) + canopyDepth, & ! intent(out): canopy depth (m) + exposedVAI, & ! intent(out): exposed vegetation area index (m2 m-2) + err,cmessage) ! intent(out): error control + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + ! check + if(computeVegFlux)then + if(canopyDepth < epsilon(canopyDepth))then + message=trim(message)//'canopy depth is zero when computeVegFlux flag is .true.' + err=20; return + endif + endif + + ! flag the case where number of vegetation states has changed + modifiedVegState = (computeVegFlux.neqv.computeVegFluxOld) + + ! *** compute wetted canopy area... + ! --------------------------------- + + ! compute maximum canopy liquid water (kg m-2) + diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1) = mpar_data%var(iLookPARAM%refInterceptCapRain)%dat(1)*exposedVAI + + ! compute maximum canopy ice content (kg m-2) + ! NOTE 1: this is used to compute the snow fraction on the canopy, as used in *BOTH* the radiation AND canopy sublimation routines + ! NOTE 2: this is a different variable than the max ice used in the throughfall (snow interception) calculations + ! NOTE 3: use maximum per unit leaf area storage capacity for snow (kg m-2) + select case(model_decisions(iLookDECISIONS%snowIncept)%iDecision) + case(lightSnow); diag_data%var(iLookDIAG%scalarCanopyIceMax)%dat(1) = exposedVAI*mpar_data%var(iLookPARAM%refInterceptCapSnow)%dat(1) + case(stickySnow); diag_data%var(iLookDIAG%scalarCanopyIceMax)%dat(1) = exposedVAI*mpar_data%var(iLookPARAM%refInterceptCapSnow)%dat(1)*4._rkind + case default; message=trim(message)//'unable to identify option for maximum branch interception capacity'; err=20; return + end select ! identifying option for maximum branch interception capacity + + ! compute wetted fraction of the canopy + ! NOTE: assume that the wetted fraction is constant over the substep for the radiation calculations + if(computeVegFlux)then + + ! compute wetted fraction of the canopy + call wettedFrac(& + ! input + .false., & ! flag to denote if derivatives are required + .false., & ! flag to denote if derivatives are calculated numerically + (prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) < Tfreeze), & ! flag to denote if the canopy is frozen + varNotUsed1, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) + varNotUsed2, & ! fraction of liquid water on the canopy + prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1), & ! canopy liquid water (kg m-2) + prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! canopy ice (kg m-2) + diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1), & ! maximum canopy liquid water (kg m-2) + diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1), & ! maximum canopy ice content (kg m-2) + mpar_data%var(iLookPARAM%canopyWettingFactor)%dat(1), & ! maximum wetted fraction of the canopy (-) + mpar_data%var(iLookPARAM%canopyWettingExp)%dat(1), & ! exponent in canopy wetting function (-) + ! output + diag_data%var(iLookDIAG%scalarCanopyWetFraction)%dat(1), & ! canopy wetted fraction (-) + dCanopyWetFraction_dWat, & ! derivative in wetted fraction w.r.t. canopy liquid water content (kg-1 m2) + dCanopyWetFraction_dT, & ! derivative in wetted fraction w.r.t. canopy liquid water content (kg-1 m2) + err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + ! vegetation is completely buried by snow (or no veg exists at all) + else + diag_data%var(iLookDIAG%scalarCanopyWetFraction)%dat(1) = 0._rkind + dCanopyWetFraction_dWat = 0._rkind + dCanopyWetFraction_dT = 0._rkind + end if + + ! *** compute snow albedo... + ! -------------------------- + ! NOTE: this should be done before the radiation calculations + ! NOTE: uses snowfall; should really use canopy throughfall + canopy unloading + call snowAlbedo(& + ! input: model control + data_step, & ! intent(in): model time step (s) + (nSnow > 0), & ! intent(in): logical flag to denote if snow is present + ! input/output: data structures + model_decisions, & ! intent(in): model decisions + mpar_data, & ! intent(in): model parameters + flux_data, & ! intent(in): model flux variables + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + prog_data, & ! intent(inout): model prognostic variables for a local HRU + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + + ! *** compute canopy sw radiation fluxes... + ! ----------------------------------------- + call vegSWavRad(& + data_step, & ! intent(in): time step (s) -- only used in Noah-MP radiation, to compute albedo + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) + type_data, & ! intent(in): type of vegetation and soil + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model flux variables + err,cmessage) ! intent(out): error control + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + + ! *** compute canopy throughfall and unloading... + ! ----------------------------------------------- + ! NOTE 1: this needs to be done before solving the energy and liquid water equations, to account for the heat advected with precipitation (and throughfall/unloading) + ! NOTE 2: the unloading flux is computed using canopy drip (scalarCanopyLiqDrainage) from the previous time step + call canopySnow(& + ! input: model control + data_step, & ! intent(in): time step (seconds) + exposedVAI, & ! intent(in): exposed vegetation area index (m2 m-2) + computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + ! input/output: data structures + model_decisions, & ! intent(in): model decisions + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(inout): model prognostic variables for a local HRU + flux_data, & ! intent(inout): model flux variables + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + ! adjust canopy temperature to account for new snow + if(computeVegFlux)then ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) + call tempAdjust(& + ! input: derived parameters + canopyDepth, & ! intent(in): canopy depth (m) + ! input/output: data structures + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(out): model diagnostic variables for a local HRU + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + endif ! if computing fluxes over vegetation + + ! initialize drainage and throughfall + ! NOTE 1: this needs to be done before solving the energy and liquid water equations, to account for the heat advected with precipitation + ! NOTE 2: this initialization needs to be done AFTER the call to canopySnow, since canopySnow uses canopy drip drom the previous time step + if(.not.computeVegFlux)then + flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) = flux_data%var(iLookFLUX%scalarRainfall)%dat(1) + flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) = 0._rkind + else + flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) = 0._rkind + flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) = 0._rkind + end if + + ! **************************************************************************************************** + ! *** MAIN SOLVER ************************************************************************************ + ! **************************************************************************************************** + + ! initialize the length of the sub-step + dt_solv = 0._rkind ! length of time step that has been completed (s) + dt_init = min(data_step,maxstep) ! initial substep length (s) + dt_sub = dt_init ! length of substep + dtSave = dt_init ! length of substep + + ! initialize the number of sub-steps + nsub=0 + + ! loop through sub-steps + substeps: do ! continuous do statement with exit clause (alternative to "while") + + ! print progress + if(globalPrintFlag)then + write(*,'(a,1x,4(f13.5,1x))') ' start of step: dt_init, dt_sub, dt_solv, data_step: ', dt_init, dt_sub, dt_solv, data_step + print*, 'stepFailure = ', stepFailure + print*, 'before resizeData: nSnow, nSoil = ', nSnow, nSoil + endif + + ! increment the number of sub-steps + nsub = nsub+1 + + ! resize the "indx_data" structure + ! NOTE: this is necessary because the length of index variables depends on a given split + ! --> the resize here is overwritten later (in indexSplit) + ! --> admittedly ugly, and retained for now + if(stepFailure)then + call resizeData(indx_meta(:),indx_temp,indx_data,err=err,message=cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + else + call resizeData(indx_meta(:),indx_data,indx_temp,err=err,message=cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + endif + + ! save/recover copies of index variables + do iVar=1,size(indx_data%var) + select case(stepFailure) + case(.false.); indx_temp%var(iVar)%dat(:) = indx_data%var(iVar)%dat(:) + case(.true.); indx_data%var(iVar)%dat(:) = indx_temp%var(iVar)%dat(:) + end select + end do ! looping through variables + + ! save/recover copies of prognostic variables + do iVar=1,size(prog_data%var) + select case(stepFailure) + case(.false.); prog_temp%var(iVar)%dat(:) = prog_data%var(iVar)%dat(:) + case(.true.); prog_data%var(iVar)%dat(:) = prog_temp%var(iVar)%dat(:) + end select + end do ! looping through variables + + ! save/recover copies of diagnostic variables + do iVar=1,size(diag_data%var) + select case(stepFailure) + case(.false.); diag_temp%var(iVar)%dat(:) = diag_data%var(iVar)%dat(:) + case(.true.); diag_data%var(iVar)%dat(:) = diag_temp%var(iVar)%dat(:) + end select + end do ! looping through variables + + ! re-assign dimension lengths + nSnow = count(indx_data%var(iLookINDEX%layerType)%dat==iname_snow) + nSoil = count(indx_data%var(iLookINDEX%layerType)%dat==iname_soil) + nLayers = nSnow+nSoil + + ! *** merge/sub-divide snow layers... + ! ----------------------------------- + call volicePack(& + ! input/output: model data structures + doLayerMerge, & ! intent(in): flag to force merge of snow layers + model_decisions, & ! intent(in): model decisions + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): type of each layer + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output + modifiedLayers, & ! intent(out): flag to denote that layers were modified + err,cmessage) ! intent(out): error control + if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if + + ! save the number of snow and soil layers + nSnow = indx_data%var(iLookINDEX%nSnow)%dat(1) + nSoil = indx_data%var(iLookINDEX%nSoil)%dat(1) + nLayers = indx_data%var(iLookINDEX%nLayers)%dat(1) + + ! compute the indices for the model state variables + if(firstSubStep .or. modifiedVegState .or. modifiedLayers)then + call indexState(computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + includeAquifer, & ! intent(in): flag to denote if included the aquifer + nSnow,nSoil,nLayers, & ! intent(in): number of snow and soil layers, and total number of layers + indx_data, & ! intent(inout): indices defining model states and layers + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + end if + + ! recreate the temporary data structures + ! NOTE: resizeData(meta, old, new, ..) + if(modifiedVegState .or. modifiedLayers)then + + ! create temporary data structures for prognostic variables + call resizeData(prog_meta(:),prog_data,prog_temp,copy=.true.,err=err,message=cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! create temporary data structures for diagnostic variables + call resizeData(diag_meta(:),diag_data,diag_temp,copy=.true.,err=err,message=cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! create temporary data structures for index variables + call resizeData(indx_meta(:),indx_data,indx_temp,copy=.true.,err=err,message=cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + do iVar=1,size(indx_data%var) + select case(stepFailure) + case(.false.); indx_temp%var(iVar)%dat(:) = indx_data%var(iVar)%dat(:) + case(.true.); indx_data%var(iVar)%dat(:) = indx_temp%var(iVar)%dat(:) + end select + end do ! looping through variables + + endif ! if modified the states + + ! define the number of state variables + nState = indx_data%var(iLookINDEX%nState)%dat(1) + + ! *** compute diagnostic variables for each layer... + ! -------------------------------------------------- + ! NOTE: this needs to be done AFTER volicePack, since layers may have been sub-divided and/or merged + call diagn_evar(& + ! input: control variables + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): canopy depth (m) + ! input/output: data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if + + + ! *** compute melt of the "snow without a layer"... + ! ------------------------------------------------- + ! NOTE: forms a surface melt pond, which drains into the upper-most soil layer through the time step + ! (check for the special case of "snow without a layer") + if(nSnow==0)then + call implctMelt(& + ! input/output: integrated snowpack properties + prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(inout): snow water equivalent (kg m-2) + prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(inout): snow depth (m) + prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1), & ! intent(inout): surface melt pond (kg m-2) + ! input/output: properties of the upper-most soil layer + prog_data%var(iLookPROG%mLayerTemp)%dat(nSnow+1), & ! intent(inout): surface layer temperature (K) + prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1), & ! intent(inout): surface layer depth (m) + diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat(nSnow+1),& ! intent(inout): surface layer volumetric heat capacity (J m-3 K-1) + ! output: error control + err,cmessage ) ! intent(out): error control + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + end if + + ! *** solve model equations... + ! ---------------------------- + ! save input step + dtSave = dt_sub + + + ! get the new solution + call opSplittin(& + ! input: model control + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + nState, & ! intent(in): total number of layers + dt_sub, & ! intent(inout): length of the model sub-step + (nsub==1), & ! intent(in): logical flag to denote the first substep + computeVegFlux, & ! intent(in): logical flag to compute fluxes within the vegetation canopy + ! input/output: data structures + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): index data + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + bvar_data, & ! intent(in): model variables for the local basin + lookup_data, & ! intent(in): lookup tables + model_decisions, & ! intent(in): model decisions + ! output: model control + dtMultiplier, & ! intent(out): substep multiplier (-) + tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt + stepFailure, & ! intent(out): flag to denote that the coupled step failed + ixSolution, & ! intent(out): solution method used in this iteration + err,cmessage) ! intent(out): error code and error message + + + ! check for all errors (error recovery within opSplittin) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + + ! process the flag for too much melt + if(tooMuchMelt)then + stepFailure = .true. + doLayerMerge = .true. + else + doLayerMerge = .false. + endif + + ! handle special case of the step failure + ! NOTE: need to revert back to the previous state vector that we were happy with and reduce the time step + if(stepFailure)then + ! halve step + dt_sub = dtSave/2._rkind + ! check that the step is not tiny + if(dt_sub < minstep)then + print*,ixSolution + print*, 'dtSave, dt_sub', dtSave, dt_sub + message=trim(message)//'length of the coupled step is below the minimum step length' + err=20; return + endif + ! try again + cycle substeps + endif + + ! update first step + firstSubStep=.false. + + ! *** remove ice due to sublimation... + ! -------------------------------------------------------------- + sublime: associate(& + scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1), & ! sublimation from the vegetation canopy (kg m-2 s-1) + scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! sublimation from the snow surface (kg m-2 s-1) + scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1), & ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1), & ! sensible heat flux from the canopy to the canopy air space (W m-2) + scalarLatHeatGround => flux_data%var(iLookFLUX%scalarLatHeatGround)%dat(1), & ! latent heat flux from ground surface below vegetation (W m-2) + scalarSenHeatGround => flux_data%var(iLookFLUX%scalarSenHeatGround)%dat(1), & ! sensible heat flux from ground surface below vegetation (W m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1), & ! liquid water stored on the vegetation canopy (kg m-2) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! ice stored on the vegetation canopy (kg m-2) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat, & ! volumetric fraction of ice in the snow+soil domain (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat, & ! volumetric fraction of liquid water in the snow+soil domain (-) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat & ! depth of each snow+soil layer (m) + ) ! associations to variables in data structures + + ! * compute change in canopy ice content due to sublimation... + ! ------------------------------------------------------------ + if(computeVegFlux)then + + ! remove mass of ice on the canopy + scalarCanopyIce = scalarCanopyIce + scalarCanopySublimation*dt_sub + + ! if removed all ice, take the remaining sublimation from water + if(scalarCanopyIce < 0._rkind)then + scalarCanopyLiq = scalarCanopyLiq + scalarCanopyIce + scalarCanopyIce = 0._rkind + endif + + ! modify fluxes if there is insufficient canopy water to support the converged sublimation rate over the time step dt_sub + if(scalarCanopyLiq < 0._rkind)then + ! --> superfluous sublimation flux + superflousSub = -scalarCanopyLiq/dt_sub ! kg m-2 s-1 + superflousNrg = superflousSub*LH_sub ! W m-2 (J m-2 s-1) + ! --> update fluxes and states + scalarCanopySublimation = scalarCanopySublimation + superflousSub + scalarLatHeatCanopyEvap = scalarLatHeatCanopyEvap + superflousNrg + scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg + scalarCanopyLiq = 0._rkind + endif + + end if ! (if computing the vegetation flux) + + call computSnowDepth(& + dt_sub, & ! intent(in) + nSnow, & ! intent(in) + scalarSnowSublimation, & ! intent(in) + mLayerVolFracLiq, & ! intent(inout) + mLayerVolFracIce, & ! intent(inout) + prog_data%var(iLookPROG%mLayerTemp)%dat, & ! intent(in) + diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat, & ! intent(in) + mpar_data, & ! intent(in) + ! output + tooMuchSublim, & ! intent(out): flag to denote that there was too much sublimation in a given time step + mLayerDepth, & ! intent(inout) + ! error control + err,message) ! intent(out): error control + if(err/=0)then; err=55; return; end if + + ! process the flag for too much sublimation + if(tooMuchSublim)then + stepFailure = .true. + doLayerMerge = .true. + else + doLayerMerge = .false. + endif + + ! handle special case of the step failure + ! NOTE: need to revert back to the previous state vector that we were happy with and reduce the time step + if(stepFailure)then + ! halve step + dt_sub = dtSave/2._rkind + ! check that the step is not tiny + if(dt_sub < minstep)then + print*,ixSolution + print*, 'dtSave, dt_sub', dtSave, dt_sub + message=trim(message)//'length of the coupled step is below the minimum step length' + err=20; return + endif + ! try again + cycle substeps + endif + + end associate sublime + + ! update coordinate variables + call calcHeight(& + ! input/output: data structures + indx_data, & ! intent(in): layer type + prog_data, & ! intent(inout): model variables for a local HRU + ! output: error control + err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + ! recompute snow depth and SWE + if(nSnow > 0)then + prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow)) + prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow)*iden_water + & + prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice) & + * prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) + end if + + ! increment fluxes + dt_wght = dt_sub/data_step ! define weight applied to each sub-step + do iVar=1,size(averageFlux_meta) + flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:)*dt_wght + end do + + ! increment change in storage associated with the surface melt pond (kg m-2) + if(nSnow==0) sfcMeltPond = sfcMeltPond + prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) + + ! increment soil compression (kg m-2) + totalSoilCompress = totalSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression over whole layer (kg m-2) + + ! **************************************************************************************************** + ! *** END MAIN SOLVER ******************************************************************************** + ! **************************************************************************************************** + + ! increment sub-step + dt_solv = dt_solv + dt_sub + + ! save the time step to initialize the subsequent step + if(dt_solv= data_step-verySmall) then + exit substeps + endif + + ! adjust length of the sub-step (make sure that we don't exceed the step) + dt_sub = data_step - dt_solv !min(data_step - dt_solv, dt_sub) + + end do substeps ! (sub-step loop) + + ! *** add snowfall to the snowpack... + ! ----------------------------------- + ! add new snowfall to the snowpack + ! NOTE: This needs to be done AFTER the call to canopySnow, since throughfall and unloading are computed in canopySnow + call newsnwfall(& + ! input: model control + data_step, & ! time step (seconds) + (nSnow > 0), & ! logical flag if snow layers exist + mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! freeezing curve parameter for snow (K-1) + ! input: diagnostic scalar variables + diag_data%var(iLookDIAG%scalarSnowfallTemp)%dat(1), & ! computed temperature of fresh snow (K) + diag_data%var(iLookDIAG%scalarNewSnowDensity)%dat(1), & ! computed density of new snow (kg m-3) + flux_data%var(iLookFLUX%scalarThroughfallSnow)%dat(1), & ! throughfall of snow through the canopy (kg m-2 s-1) + flux_data%var(iLookFLUX%scalarCanopySnowUnloading)%dat(1), & ! unloading of snow from the canopy (kg m-2 s-1) + ! input/output: state variables + prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! SWE (kg m-2) + prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! total snow depth (m) + prog_data%var(iLookPROG%mLayerTemp)%dat(1), & ! temperature of the top layer (K) + prog_data%var(iLookPROG%mLayerDepth)%dat(1), & ! depth of the top layer (m) + prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1), & ! volumetric fraction of ice of the top layer (-) + prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1), & ! volumetric fraction of liquid water of the top layer (-) ! output: error control - err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - - ! recompute snow depth and SWE - if(nSnow > 0)then - prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow)) - prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow)*iden_water + & - prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice) & - * prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) + err,cmessage) ! error control + if(err/=0)then; err=30; message=trim(message)//trim(cmessage); return; end if + + ! re-compute snow depth and SWE + if(nSnow > 0)then + prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow)) + prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow)*iden_water + & + prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice) & + * prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) + end if + + ! re-assign dimension lengths + nSnow = count(indx_data%var(iLookINDEX%layerType)%dat==iname_snow) + nSoil = count(indx_data%var(iLookINDEX%layerType)%dat==iname_soil) + nLayers = nSnow+nSoil + + ! update coordinate variables + call calcHeight(& + ! input/output: data structures + indx_data, & ! intent(in): layer type + prog_data, & ! intent(inout): model variables for a local HRU + ! output: error control + err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + ! overwrite flux_data with flux_mean (returns timestep-average fluxes for scalar variables) + do iVar=1,size(averageFlux_meta) + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:) = flux_mean%var(iVar)%dat(:) + end do + + ! *********************************************************************************************************************************** + ! *********************************************************************************************************************************** + ! *********************************************************************************************************************************** + ! *********************************************************************************************************************************** + + ! --- + ! *** balance checks... + ! --------------------- + + ! save the average compression and melt pond storage in the data structures + prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) = sfcMeltPond + + ! associate local variables with information in the data structures + associate(& + ! model forcing + scalarSnowfall => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSnowfall) )%dat(1) ,& ! computed snowfall rate (kg m-2 s-1) + scalarRainfall => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarRainfall) )%dat(1) ,& ! computed rainfall rate (kg m-2 s-1) + ! canopy fluxes + averageThroughfallSnow => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarThroughfallSnow) )%dat(1) ,& ! snow that reaches the ground without ever touching the canopy (kg m-2 s-1) + averageThroughfallRain => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarThroughfallRain) )%dat(1) ,& ! rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + averageCanopySnowUnloading => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySnowUnloading))%dat(1) ,& ! unloading of snow from the vegetion canopy (kg m-2 s-1) + averageCanopyLiqDrainage => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopyLiqDrainage) )%dat(1) ,& ! drainage of liquid water from the vegetation canopy (kg m-2 s-1) + averageCanopySublimation => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySublimation) )%dat(1) ,& ! canopy sublimation/frost (kg m-2 s-1) + averageCanopyEvaporation => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopyEvaporation) )%dat(1) ,& ! canopy evaporation/condensation (kg m-2 s-1) + ! snow fluxes + averageSnowSublimation => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSnowSublimation) )%dat(1) ,& ! sublimation from the snow surface (kg m-2 s-1) + averageSnowDrainage => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSnowDrainage) )%dat(1) ,& ! drainage from the bottom of the snowpack (m s-1) + ! soil fluxes + averageSoilInflux => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarInfiltration) )%dat(1) ,& ! influx of water at the top of the soil profile (m s-1) + averageSoilDrainage => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSoilDrainage) )%dat(1) ,& ! drainage from the bottom of the soil profile (m s-1) + averageSoilBaseflow => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSoilBaseflow) )%dat(1) ,& ! total baseflow from throughout the soil profile (m s-1) + averageGroundEvaporation => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarGroundEvaporation) )%dat(1) ,& ! soil evaporation (kg m-2 s-1) + averageCanopyTranspiration => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopyTranspiration))%dat(1) ,& ! canopy transpiration (kg m-2 s-1) + ! state variables in the vegetation canopy + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! canopy liquid water (kg m-2) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! canopy ice content (kg m-2) + ! state variables in the soil domain + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers) ,& ! depth of each soil layer (m) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat(nSnow+1:nLayers) ,& ! volumetric ice content in each soil layer (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(nSnow+1:nLayers) ,& ! volumetric liquid water content in each soil layer (-) + scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! aquifer storage (m) + ! error tolerance + absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1) ,& ! absolute convergence tolerance for vol frac liq water (-) + scalarTotalSoilIce => diag_data%var(iLookDIAG%scalarTotalSoilIce)%dat(1) ,& ! total ice in the soil column (kg m-2) + scalarTotalSoilLiq => diag_data%var(iLookDIAG%scalarTotalSoilLiq)%dat(1) & ! total liquid water in the soil column (kg m-2) + ) ! (association of local variables with information in the data structures + + ! ----- + ! * balance checks for the canopy... + ! ---------------------------------- + + ! if computing the vegetation flux + if(computeVegFlux)then + + ! canopy water balance + balanceCanopyWater1 = scalarCanopyLiq + scalarCanopyIce + + ! balance checks for the canopy + ! NOTE: need to put the balance checks in the sub-step loop so that we can re-compute if necessary + scalarCanopyWatBalError = balanceCanopyWater1 - (balanceCanopyWater0 + (scalarSnowfall - averageThroughfallSnow)*data_step + (scalarRainfall - averageThroughfallRain)*data_step & + - averageCanopySnowUnloading*data_step - averageCanopyLiqDrainage*data_step + averageCanopySublimation*data_step + averageCanopyEvaporation*data_step) + if(abs(scalarCanopyWatBalError) > absConvTol_liquid*iden_water*10._rkind)then + print*, '** canopy water balance error:' + write(*,'(a,1x,f20.10)') 'data_step = ', data_step + write(*,'(a,1x,f20.10)') 'balanceCanopyWater0 = ', balanceCanopyWater0 + write(*,'(a,1x,f20.10)') 'balanceCanopyWater1 = ', balanceCanopyWater1 + write(*,'(a,1x,f20.10)') 'scalarSnowfall = ', scalarSnowfall + write(*,'(a,1x,f20.10)') 'scalarRainfall = ', scalarRainfall + write(*,'(a,1x,f20.10)') '(scalarSnowfall - averageThroughfallSnow) = ', (scalarSnowfall - averageThroughfallSnow)!*data_step + write(*,'(a,1x,f20.10)') '(scalarRainfall - averageThroughfallRain) = ', (scalarRainfall - averageThroughfallRain)!*data_step + write(*,'(a,1x,f20.10)') 'averageCanopySnowUnloading = ', averageCanopySnowUnloading!*data_step + write(*,'(a,1x,f20.10)') 'averageCanopyLiqDrainage = ', averageCanopyLiqDrainage!*data_step + write(*,'(a,1x,f20.10)') 'averageCanopySublimation = ', averageCanopySublimation!*data_step + write(*,'(a,1x,f20.10)') 'averageCanopyEvaporation = ', averageCanopyEvaporation!*data_step + write(*,'(a,1x,f20.10)') 'scalarCanopyWatBalError = ', scalarCanopyWatBalError + message=trim(message)//'canopy hydrology does not balance' + err=20; return + end if + + endif ! if computing the vegetation flux + + ! ----- + ! * balance checks for SWE... + ! --------------------------- + + ! recompute snow depth (m) and SWE (kg m-2) + if(nSnow > 0)then + prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow)) + prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow)*iden_water + & + prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice) & + * prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) + end if + + ! check the individual layers + if(printBalance .and. nSnow>0)then + write(*,'(a,1x,10(f12.8,1x))') 'liqSnowInit = ', liqSnowInit + write(*,'(a,1x,10(f12.8,1x))') 'volFracLiq = ', prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow) + write(*,'(a,1x,10(f12.8,1x))') 'iLayerLiqFluxSnow = ', flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat*iden_water*data_step + write(*,'(a,1x,10(f12.8,1x))') 'mLayerLiqFluxSnow = ', flux_data%var(iLookFLUX%mLayerLiqFluxSnow)%dat*data_step + write(*,'(a,1x,10(f12.8,1x))') 'change volFracLiq = ', prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow) - liqSnowInit + deallocate(liqSnowInit, stat=err) + if(err/=0)then + message=trim(message)//'unable to deallocate space for the initial volumetric liquid water content of snow' + err=20; return + endif + endif + + ! check SWE + if(nSnow>0)then + effSnowfall = averageThroughfallSnow + averageCanopySnowUnloading + effRainfall = averageThroughfallRain + averageCanopyLiqDrainage + newSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) + delSWE = newSWE - (oldSWE - sfcMeltPond) + massBalance = delSWE - (effSnowfall + effRainfall + averageSnowSublimation - averageSnowDrainage*iden_water)*data_step + if(abs(massBalance) > absConvTol_liquid*iden_water*10._rkind)then + print*, 'nSnow = ', nSnow + print*, 'nSub = ', nSub + write(*,'(a,1x,f20.10)') 'data_step = ', data_step + write(*,'(a,1x,f20.10)') 'oldSWE = ', oldSWE + write(*,'(a,1x,f20.10)') 'newSWE = ', newSWE + write(*,'(a,1x,f20.10)') 'delSWE = ', delSWE + write(*,'(a,1x,f20.10)') 'effRainfall = ', effRainfall*data_step + write(*,'(a,1x,f20.10)') 'effSnowfall = ', effSnowfall*data_step + write(*,'(a,1x,f20.10)') 'sublimation = ', averageSnowSublimation*data_step + write(*,'(a,1x,f20.10)') 'snwDrainage = ', averageSnowDrainage*iden_water*data_step + write(*,'(a,1x,f20.10)') 'sfcMeltPond = ', sfcMeltPond + write(*,'(a,1x,f20.10)') 'massBalance = ', massBalance + message=trim(message)//'SWE does not balance' + err=20; return + endif ! if failed mass balance check + endif ! if snow layers exist + + ! ----- + ! * balance checks for soil... + ! ---------------------------- + + ! compute the liquid water and ice content at the end of the time step + scalarTotalSoilLiq = sum(iden_water*mLayerVolFracLiq(1:nSoil)*mLayerDepth(1:nSoil)) + scalarTotalSoilIce = sum(iden_water*mLayerVolFracIce(1:nSoil)*mLayerDepth(1:nSoil)) ! NOTE: no expansion of soil, hence use iden_water + + ! get the total water in the soil (liquid plus ice) at the end of the time step (kg m-2) + balanceSoilWater1 = scalarTotalSoilLiq + scalarTotalSoilIce + + ! get the total aquifer storage at the start of the time step (kg m-2) + balanceAquifer1 = scalarAquiferStorage*iden_water + + ! get the input and output to/from the soil zone (kg m-2) + balanceSoilInflux = averageSoilInflux*iden_water*data_step + balanceSoilBaseflow = averageSoilBaseflow*iden_water*data_step + balanceSoilDrainage = averageSoilDrainage*iden_water*data_step + balanceSoilET = (averageCanopyTranspiration + averageGroundEvaporation)*data_step + + ! check the individual layers + if(printBalance)then + write(*,'(a,1x,10(f12.8,1x))') 'liqSoilInit = ', liqSoilInit + write(*,'(a,1x,10(f12.8,1x))') 'volFracLiq = ', mLayerVolFracLiq + write(*,'(a,1x,10(f12.8,1x))') 'iLayerLiqFluxSoil = ', flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat*iden_water*data_step + write(*,'(a,1x,10(f12.8,1x))') 'mLayerLiqFluxSoil = ', flux_data%var(iLookFLUX%mLayerLiqFluxSoil)%dat*data_step + write(*,'(a,1x,10(f12.8,1x))') 'change volFracLiq = ', mLayerVolFracLiq - liqSoilInit + deallocate(liqSoilInit, stat=err) + if(err/=0)then + message=trim(message)//'unable to deallocate space for the initial soil moisture' + err=20; return + endif + endif + + ! check the soil water balance + scalarSoilWatBalError = balanceSoilWater1 - (balanceSoilWater0 + (balanceSoilInflux + balanceSoilET - balanceSoilBaseflow - balanceSoilDrainage - totalSoilCompress) ) + if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues + write(*,*) 'solution method = ', ixSolution + write(*,'(a,1x,f20.10)') 'data_step = ', data_step + write(*,'(a,1x,f20.10)') 'totalSoilCompress = ', totalSoilCompress + write(*,'(a,1x,f20.10)') 'scalarTotalSoilLiq = ', scalarTotalSoilLiq + write(*,'(a,1x,f20.10)') 'scalarTotalSoilIce = ', scalarTotalSoilIce + write(*,'(a,1x,f20.10)') 'balanceSoilWater0 = ', balanceSoilWater0 + write(*,'(a,1x,f20.10)') 'balanceSoilWater1 = ', balanceSoilWater1 + write(*,'(a,1x,f20.10)') 'balanceSoilInflux = ', balanceSoilInflux + write(*,'(a,1x,f20.10)') 'balanceSoilBaseflow = ', balanceSoilBaseflow + write(*,'(a,1x,f20.10)') 'balanceSoilDrainage = ', balanceSoilDrainage + write(*,'(a,1x,f20.10)') 'balanceSoilET = ', balanceSoilET + write(*,'(a,1x,f20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError + write(*,'(a,1x,f20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError/iden_water + write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid + ! error control + message=trim(message)//'soil hydrology does not balance' + err=20; return + end if + + ! end association of local variables with information in the data structures + end associate + + ! end association to canopy depth + end associate canopy + + ! Save the total soil water (Liquid+Ice) + diag_data%var(iLookDIAG%scalarTotalSoilWat)%dat(1) = balanceSoilWater1 + ! save the surface temperature (just to make things easier to visualize) + prog_data%var(iLookPROG%scalarSurfaceTemp)%dat(1) = prog_data%var(iLookPROG%mLayerTemp)%dat(1) + + ! overwrite flux data with the timestep-average value + if(.not.backwardsCompatibility)then + do iVar=1,size(flux_mean%var) + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat = flux_mean%var(iVar)%dat + end do end if - ! increment fluxes - dt_wght = dt_sub/data_step ! define weight applied to each sub-step - do iVar=1,size(averageFlux_meta) - flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:)*dt_wght - end do - - ! increment change in storage associated with the surface melt pond (kg m-2) - if(nSnow==0) sfcMeltPond = sfcMeltPond + prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) - - ! increment soil compression (kg m-2) - totalSoilCompress = totalSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression over whole layer (kg m-2) - - ! **************************************************************************************************** - ! *** END MAIN SOLVER ******************************************************************************** - ! **************************************************************************************************** - - ! increment sub-step - dt_solv = dt_solv + dt_sub - - ! save the time step to initialize the subsequent step - if(dt_solv= data_step-verySmall) then - exit substeps - endif - - ! adjust length of the sub-step (make sure that we don't exceed the step) - dt_sub = data_step - dt_solv !min(data_step - dt_solv, dt_sub) - !print*, 'dt_sub = ', dt_sub - - end do substeps ! (sub-step loop) - !print*, 'PAUSE: completed time step'; read(*,*) - - ! *** add snowfall to the snowpack... - ! ----------------------------------- - - ! add new snowfall to the snowpack - ! NOTE: This needs to be done AFTER the call to canopySnow, since throughfall and unloading are computed in canopySnow - call newsnwfall(& - ! input: model control - data_step, & ! time step (seconds) - (nSnow > 0), & ! logical flag if snow layers exist - mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! freeezing curve parameter for snow (K-1) - ! input: diagnostic scalar variables - diag_data%var(iLookDIAG%scalarSnowfallTemp)%dat(1), & ! computed temperature of fresh snow (K) - diag_data%var(iLookDIAG%scalarNewSnowDensity)%dat(1), & ! computed density of new snow (kg m-3) - flux_data%var(iLookFLUX%scalarThroughfallSnow)%dat(1), & ! throughfall of snow through the canopy (kg m-2 s-1) - flux_data%var(iLookFLUX%scalarCanopySnowUnloading)%dat(1), & ! unloading of snow from the canopy (kg m-2 s-1) - ! input/output: state variables - prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! SWE (kg m-2) - prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! total snow depth (m) - prog_data%var(iLookPROG%mLayerTemp)%dat(1), & ! temperature of the top layer (K) - prog_data%var(iLookPROG%mLayerDepth)%dat(1), & ! depth of the top layer (m) - prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1), & ! volumetric fraction of ice of the top layer (-) - prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1), & ! volumetric fraction of liquid water of the top layer (-) - ! output: error control - err,cmessage) ! error control - if(err/=0)then; err=30; message=trim(message)//trim(cmessage); return; end if - - ! re-compute snow depth and SWE - if(nSnow > 0)then - prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow)) - prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow)*iden_water + & - prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice) & - * prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) - end if - !print*, 'SWE after snowfall = ', prog_data%var(iLookPROG%scalarSWE)%dat(1) - - ! re-assign dimension lengths - nSnow = count(indx_data%var(iLookINDEX%layerType)%dat==iname_snow) - nSoil = count(indx_data%var(iLookINDEX%layerType)%dat==iname_soil) - nLayers = nSnow+nSoil - - ! update coordinate variables - call calcHeight(& - ! input/output: data structures - indx_data, & ! intent(in): layer type - prog_data, & ! intent(inout): model variables for a local HRU - ! output: error control - err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - - ! overwrite flux_data with flux_mean (returns timestep-average fluxes for scalar variables) - do iVar=1,size(averageFlux_meta) - flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:) = flux_mean%var(iVar)%dat(:) - end do - - ! *********************************************************************************************************************************** - ! *********************************************************************************************************************************** - ! *********************************************************************************************************************************** - ! *********************************************************************************************************************************** - - ! --- - ! *** balance checks... - ! --------------------- - - ! save the average compression and melt pond storage in the data structures - prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) = sfcMeltPond - - ! associate local variables with information in the data structures - associate(& - ! model forcing - scalarSnowfall => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSnowfall) )%dat(1) ,& ! computed snowfall rate (kg m-2 s-1) - scalarRainfall => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarRainfall) )%dat(1) ,& ! computed rainfall rate (kg m-2 s-1) - ! canopy fluxes - averageThroughfallSnow => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarThroughfallSnow) )%dat(1) ,& ! snow that reaches the ground without ever touching the canopy (kg m-2 s-1) - averageThroughfallRain => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarThroughfallRain) )%dat(1) ,& ! rain that reaches the ground without ever touching the canopy (kg m-2 s-1) - averageCanopySnowUnloading => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySnowUnloading))%dat(1) ,& ! unloading of snow from the vegetion canopy (kg m-2 s-1) - averageCanopyLiqDrainage => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopyLiqDrainage) )%dat(1) ,& ! drainage of liquid water from the vegetation canopy (kg m-2 s-1) - averageCanopySublimation => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySublimation) )%dat(1) ,& ! canopy sublimation/frost (kg m-2 s-1) - averageCanopyEvaporation => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopyEvaporation) )%dat(1) ,& ! canopy evaporation/condensation (kg m-2 s-1) - ! snow fluxes - averageSnowSublimation => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSnowSublimation) )%dat(1) ,& ! sublimation from the snow surface (kg m-2 s-1) - averageSnowDrainage => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSnowDrainage) )%dat(1) ,& ! drainage from the bottom of the snowpack (m s-1) - ! soil fluxes - averageSoilInflux => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarInfiltration) )%dat(1) ,& ! influx of water at the top of the soil profile (m s-1) - averageSoilDrainage => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSoilDrainage) )%dat(1) ,& ! drainage from the bottom of the soil profile (m s-1) - averageSoilBaseflow => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSoilBaseflow) )%dat(1) ,& ! total baseflow from throughout the soil profile (m s-1) - averageGroundEvaporation => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarGroundEvaporation) )%dat(1) ,& ! soil evaporation (kg m-2 s-1) - averageCanopyTranspiration => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopyTranspiration))%dat(1) ,& ! canopy transpiration (kg m-2 s-1) - ! state variables in the vegetation canopy - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! canopy liquid water (kg m-2) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! canopy ice content (kg m-2) - ! state variables in the soil domain - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers) ,& ! depth of each soil layer (m) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat(nSnow+1:nLayers) ,& ! volumetric ice content in each soil layer (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(nSnow+1:nLayers) ,& ! volumetric liquid water content in each soil layer (-) - scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! aquifer storage (m) - ! error tolerance - absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1) ,& ! absolute convergence tolerance for vol frac liq water (-) - scalarTotalSoilIce => diag_data%var(iLookDIAG%scalarTotalSoilIce)%dat(1) ,& ! total ice in the soil column (kg m-2) - scalarTotalSoilLiq => diag_data%var(iLookDIAG%scalarTotalSoilLiq)%dat(1) & ! total liquid water in the soil column (kg m-2) - ) ! (association of local variables with information in the data structures - - ! ----- - ! * balance checks for the canopy... - ! ---------------------------------- - - ! if computing the vegetation flux - if(computeVegFlux)then - - ! canopy water balance - balanceCanopyWater1 = scalarCanopyLiq + scalarCanopyIce - - ! balance checks for the canopy - ! NOTE: need to put the balance checks in the sub-step loop so that we can re-compute if necessary - scalarCanopyWatBalError = balanceCanopyWater1 - (balanceCanopyWater0 + (scalarSnowfall - averageThroughfallSnow)*data_step + (scalarRainfall - averageThroughfallRain)*data_step & - - averageCanopySnowUnloading*data_step - averageCanopyLiqDrainage*data_step + averageCanopySublimation*data_step + averageCanopyEvaporation*data_step) - if(abs(scalarCanopyWatBalError) > absConvTol_liquid*iden_water*10._rkind)then - print*, '** canopy water balance error:' - write(*,'(a,1x,f20.10)') 'data_step = ', data_step - write(*,'(a,1x,f20.10)') 'balanceCanopyWater0 = ', balanceCanopyWater0 - write(*,'(a,1x,f20.10)') 'balanceCanopyWater1 = ', balanceCanopyWater1 - write(*,'(a,1x,f20.10)') 'scalarSnowfall = ', scalarSnowfall - write(*,'(a,1x,f20.10)') 'scalarRainfall = ', scalarRainfall - write(*,'(a,1x,f20.10)') '(scalarSnowfall - averageThroughfallSnow) = ', (scalarSnowfall - averageThroughfallSnow)!*data_step - write(*,'(a,1x,f20.10)') '(scalarRainfall - averageThroughfallRain) = ', (scalarRainfall - averageThroughfallRain)!*data_step - write(*,'(a,1x,f20.10)') 'averageCanopySnowUnloading = ', averageCanopySnowUnloading!*data_step - write(*,'(a,1x,f20.10)') 'averageCanopyLiqDrainage = ', averageCanopyLiqDrainage!*data_step - write(*,'(a,1x,f20.10)') 'averageCanopySublimation = ', averageCanopySublimation!*data_step - write(*,'(a,1x,f20.10)') 'averageCanopyEvaporation = ', averageCanopyEvaporation!*data_step - write(*,'(a,1x,f20.10)') 'scalarCanopyWatBalError = ', scalarCanopyWatBalError - message=trim(message)//'canopy hydrology does not balance' - err=20; return + iLayer = nSnow+1 + if(nsub>50000)then + write(message,'(a,i0)') trim(cmessage)//'number of sub-steps > 50000 for HRU ', hruID + err=20; return end if - endif ! if computing the vegetation flux - - ! ----- - ! * balance checks for SWE... - ! --------------------------- - - ! recompute snow depth (m) and SWE (kg m-2) - if(nSnow > 0)then - prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow)) - prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow)*iden_water + & - prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice) & - * prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) - end if - - ! check the individual layers - if(printBalance .and. nSnow>0)then - write(*,'(a,1x,10(f12.8,1x))') 'liqSnowInit = ', liqSnowInit - write(*,'(a,1x,10(f12.8,1x))') 'volFracLiq = ', prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow) - write(*,'(a,1x,10(f12.8,1x))') 'iLayerLiqFluxSnow = ', flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat*iden_water*data_step - write(*,'(a,1x,10(f12.8,1x))') 'mLayerLiqFluxSnow = ', flux_data%var(iLookFLUX%mLayerLiqFluxSnow)%dat*data_step - write(*,'(a,1x,10(f12.8,1x))') 'change volFracLiq = ', prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow) - liqSnowInit - deallocate(liqSnowInit, stat=err) - if(err/=0)then - message=trim(message)//'unable to deallocate space for the initial volumetric liquid water content of snow' - err=20; return - endif - endif - - ! check SWE - if(nSnow>0)then - effSnowfall = averageThroughfallSnow + averageCanopySnowUnloading - effRainfall = averageThroughfallRain + averageCanopyLiqDrainage - newSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) - delSWE = newSWE - (oldSWE - sfcMeltPond) - massBalance = delSWE - (effSnowfall + effRainfall + averageSnowSublimation - averageSnowDrainage*iden_water)*data_step - if(abs(massBalance) > absConvTol_liquid*iden_water*10._rkind)then - print*, 'nSnow = ', nSnow - print*, 'nSub = ', nSub - write(*,'(a,1x,f20.10)') 'data_step = ', data_step - write(*,'(a,1x,f20.10)') 'oldSWE = ', oldSWE - write(*,'(a,1x,f20.10)') 'newSWE = ', newSWE - write(*,'(a,1x,f20.10)') 'delSWE = ', delSWE - write(*,'(a,1x,f20.10)') 'effRainfall = ', effRainfall*data_step - write(*,'(a,1x,f20.10)') 'effSnowfall = ', effSnowfall*data_step - write(*,'(a,1x,f20.10)') 'sublimation = ', averageSnowSublimation*data_step - write(*,'(a,1x,f20.10)') 'snwDrainage = ', averageSnowDrainage*iden_water*data_step - write(*,'(a,1x,f20.10)') 'sfcMeltPond = ', sfcMeltPond - write(*,'(a,1x,f20.10)') 'massBalance = ', massBalance - message=trim(message)//'SWE does not balance' - err=20; return - endif ! if failed mass balance check - endif ! if snow layers exist - - ! ----- - ! * balance checks for soil... - ! ---------------------------- - - ! compute the liquid water and ice content at the end of the time step - scalarTotalSoilLiq = sum(iden_water*mLayerVolFracLiq(1:nSoil)*mLayerDepth(1:nSoil)) - scalarTotalSoilIce = sum(iden_water*mLayerVolFracIce(1:nSoil)*mLayerDepth(1:nSoil)) ! NOTE: no expansion of soil, hence use iden_water - - ! get the total water in the soil (liquid plus ice) at the end of the time step (kg m-2) - balanceSoilWater1 = scalarTotalSoilLiq + scalarTotalSoilIce - - ! get the total aquifer storage at the start of the time step (kg m-2) - balanceAquifer1 = scalarAquiferStorage*iden_water - - ! get the input and output to/from the soil zone (kg m-2) - balanceSoilInflux = averageSoilInflux*iden_water*data_step - balanceSoilBaseflow = averageSoilBaseflow*iden_water*data_step - balanceSoilDrainage = averageSoilDrainage*iden_water*data_step - balanceSoilET = (averageCanopyTranspiration + averageGroundEvaporation)*data_step - - ! check the individual layers - if(printBalance)then - write(*,'(a,1x,10(f12.8,1x))') 'liqSoilInit = ', liqSoilInit - write(*,'(a,1x,10(f12.8,1x))') 'volFracLiq = ', mLayerVolFracLiq - write(*,'(a,1x,10(f12.8,1x))') 'iLayerLiqFluxSoil = ', flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat*iden_water*data_step - write(*,'(a,1x,10(f12.8,1x))') 'mLayerLiqFluxSoil = ', flux_data%var(iLookFLUX%mLayerLiqFluxSoil)%dat*data_step - write(*,'(a,1x,10(f12.8,1x))') 'change volFracLiq = ', mLayerVolFracLiq - liqSoilInit - deallocate(liqSoilInit, stat=err) - if(err/=0)then - message=trim(message)//'unable to deallocate space for the initial soil moisture' - err=20; return - endif - endif - - ! check the soil water balance - scalarSoilWatBalError = balanceSoilWater1 - (balanceSoilWater0 + (balanceSoilInflux + balanceSoilET - balanceSoilBaseflow - balanceSoilDrainage - totalSoilCompress) ) - if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues - write(*,*) 'solution method = ', ixSolution - write(*,'(a,1x,f20.10)') 'data_step = ', data_step - write(*,'(a,1x,f20.10)') 'totalSoilCompress = ', totalSoilCompress - write(*,'(a,1x,f20.10)') 'scalarTotalSoilLiq = ', scalarTotalSoilLiq - write(*,'(a,1x,f20.10)') 'scalarTotalSoilIce = ', scalarTotalSoilIce - write(*,'(a,1x,f20.10)') 'balanceSoilWater0 = ', balanceSoilWater0 - write(*,'(a,1x,f20.10)') 'balanceSoilWater1 = ', balanceSoilWater1 - write(*,'(a,1x,f20.10)') 'balanceSoilInflux = ', balanceSoilInflux - write(*,'(a,1x,f20.10)') 'balanceSoilBaseflow = ', balanceSoilBaseflow - write(*,'(a,1x,f20.10)') 'balanceSoilDrainage = ', balanceSoilDrainage - write(*,'(a,1x,f20.10)') 'balanceSoilET = ', balanceSoilET - write(*,'(a,1x,f20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError - write(*,'(a,1x,f20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError/iden_water - write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid - ! error control - message=trim(message)//'soil hydrology does not balance' - err=20; return - end if - - ! end association of local variables with information in the data structures - end associate - - ! end association to canopy depth - end associate canopy - - ! Save the total soil water (Liquid+Ice) - diag_data%var(iLookDIAG%scalarTotalSoilWat)%dat(1) = balanceSoilWater1 - ! save the surface temperature (just to make things easier to visualize) - prog_data%var(iLookPROG%scalarSurfaceTemp)%dat(1) = prog_data%var(iLookPROG%mLayerTemp)%dat(1) - - ! overwrite flux data with the timestep-average value - if(.not.backwardsCompatibility)then - do iVar=1,size(flux_mean%var) - flux_data%var(averageFlux_meta(iVar)%ixParent)%dat = flux_mean%var(iVar)%dat - end do - end if - - iLayer = nSnow+1 - !print*, 'nsub, mLayerTemp(iLayer), mLayerVolFracIce(iLayer) = ', nsub, mLayerTemp(iLayer), mLayerVolFracIce(iLayer) - !print*, 'nsub = ', nsub - if(nsub>50000)then - write(message,'(a,i0)') trim(cmessage)//'number of sub-steps > 50000 for HRU ', hruID - err=20; return - end if - - ! get the end time - call cpu_time(endTime) - - ! get the elapsed time - diag_data%var(iLookDIAG%wallClockTime)%dat(1) = endTime - startTime - - end subroutine coupled_em - - - ! ********************************************************************************************************* - ! private subroutine implctMelt: compute melt of the "snow without a layer" - ! ********************************************************************************************************* - subroutine implctMelt(& - ! input/output: integrated snowpack properties - scalarSWE, & ! intent(inout): snow water equivalent (kg m-2) - scalarSnowDepth, & ! intent(inout): snow depth (m) - scalarSfcMeltPond, & ! intent(inout): surface melt pond (kg m-2) - ! input/output: properties of the upper-most soil layer - soilTemp, & ! intent(inout): surface layer temperature (K) - soilDepth, & ! intent(inout): surface layer depth (m) - soilHeatcap, & ! intent(inout): surface layer volumetric heat capacity (J m-3 K-1) - ! output: error control - err,message ) ! intent(out): error control - implicit none - ! input/output: integrated snowpack properties - real(rkind),intent(inout) :: scalarSWE ! snow water equivalent (kg m-2) - real(rkind),intent(inout) :: scalarSnowDepth ! snow depth (m) - real(rkind),intent(inout) :: scalarSfcMeltPond ! surface melt pond (kg m-2) - ! input/output: properties of the upper-most soil layer - real(rkind),intent(inout) :: soilTemp ! surface layer temperature (K) - real(rkind),intent(inout) :: soilDepth ! surface layer depth (m) - real(rkind),intent(inout) :: soilHeatcap ! surface layer volumetric heat capacity (J m-3 K-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! local variables - real(rkind) :: nrgRequired ! energy required to melt all the snow (J m-2) - real(rkind) :: nrgAvailable ! energy available to melt the snow (J m-2) - real(rkind) :: snwDensity ! snow density (kg m-3) - ! initialize error control - err=0; message='implctMelt/' - - if(scalarSWE > 0._rkind)then - ! only melt if temperature of the top soil layer is greater than Tfreeze - if(soilTemp > Tfreeze)then - ! compute the energy required to melt all the snow (J m-2) - nrgRequired = scalarSWE*LH_fus - ! compute the energy available to melt the snow (J m-2) - nrgAvailable = soilHeatcap*(soilTemp - Tfreeze)*soilDepth - ! compute the snow density (not saved) - snwDensity = scalarSWE/scalarSnowDepth - ! compute the amount of melt, and update SWE (kg m-2) - if(nrgAvailable > nrgRequired)then - scalarSfcMeltPond = scalarSWE - scalarSWE = 0._rkind - else - scalarSfcMeltPond = nrgAvailable/LH_fus - scalarSWE = scalarSWE - scalarSfcMeltPond - end if - ! update depth - scalarSnowDepth = scalarSWE/snwDensity - ! update temperature of the top soil layer (K) - soilTemp = soilTemp - (LH_fus*scalarSfcMeltPond/soilDepth)/soilHeatcap - else ! melt is zero if the temperature of the top soil layer is less than Tfreeze - scalarSfcMeltPond = 0._rkind ! kg m-2 - end if ! (if the temperature of the top soil layer is greater than Tfreeze) - else ! melt is zero if the "snow without a layer" does not exist - scalarSfcMeltPond = 0._rkind ! kg m-2 - end if ! (if the "snow without a layer" exists) - - end subroutine implctMelt + ! get the end time + call cpu_time(endTime) + + ! get the elapsed time + diag_data%var(iLookDIAG%wallClockTime)%dat(1) = endTime - startTime + +end subroutine coupled_em + + +! ********************************************************************************************************* +! private subroutine implctMelt: compute melt of the "snow without a layer" +! ********************************************************************************************************* +subroutine implctMelt(& + ! input/output: integrated snowpack properties + scalarSWE, & ! intent(inout): snow water equivalent (kg m-2) + scalarSnowDepth, & ! intent(inout): snow depth (m) + scalarSfcMeltPond, & ! intent(inout): surface melt pond (kg m-2) + ! input/output: properties of the upper-most soil layer + soilTemp, & ! intent(inout): surface layer temperature (K) + soilDepth, & ! intent(inout): surface layer depth (m) + soilHeatcap, & ! intent(inout): surface layer volumetric heat capacity (J m-3 K-1) + ! output: error control + err,message ) ! intent(out): error control + implicit none + ! input/output: integrated snowpack properties + real(rkind),intent(inout) :: scalarSWE ! snow water equivalent (kg m-2) + real(rkind),intent(inout) :: scalarSnowDepth ! snow depth (m) + real(rkind),intent(inout) :: scalarSfcMeltPond ! surface melt pond (kg m-2) + ! input/output: properties of the upper-most soil layer + real(rkind),intent(inout) :: soilTemp ! surface layer temperature (K) + real(rkind),intent(inout) :: soilDepth ! surface layer depth (m) + real(rkind),intent(inout) :: soilHeatcap ! surface layer volumetric heat capacity (J m-3 K-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local variables + real(rkind) :: nrgRequired ! energy required to melt all the snow (J m-2) + real(rkind) :: nrgAvailable ! energy available to melt the snow (J m-2) + real(rkind) :: snwDensity ! snow density (kg m-3) + ! initialize error control + err=0; message='implctMelt/' + + if(scalarSWE > 0._rkind)then + ! only melt if temperature of the top soil layer is greater than Tfreeze + if(soilTemp > Tfreeze)then + ! compute the energy required to melt all the snow (J m-2) + nrgRequired = scalarSWE*LH_fus + ! compute the energy available to melt the snow (J m-2) + nrgAvailable = soilHeatcap*(soilTemp - Tfreeze)*soilDepth + ! compute the snow density (not saved) + snwDensity = scalarSWE/scalarSnowDepth + ! compute the amount of melt, and update SWE (kg m-2) + if(nrgAvailable > nrgRequired)then + scalarSfcMeltPond = scalarSWE + scalarSWE = 0._rkind + else + scalarSfcMeltPond = nrgAvailable/LH_fus + scalarSWE = scalarSWE - scalarSfcMeltPond + end if + ! update depth + scalarSnowDepth = scalarSWE/snwDensity + ! update temperature of the top soil layer (K) + soilTemp = soilTemp - (LH_fus*scalarSfcMeltPond/soilDepth)/soilHeatcap + else ! melt is zero if the temperature of the top soil layer is less than Tfreeze + scalarSfcMeltPond = 0._rkind ! kg m-2 + end if ! (if the temperature of the top soil layer is greater than Tfreeze) + else ! melt is zero if the "snow without a layer" does not exist + scalarSfcMeltPond = 0._rkind ! kg m-2 + end if ! (if the "snow without a layer" exists) + +end subroutine implctMelt end module coupled_em_module From 54055c49e6c35fc13a29fe1d08a287bea3569910 Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 26 Sep 2022 17:22:19 +0000 Subject: [PATCH 0348/1472] cleaned up opsplittin --- build/source/engine/opSplittin.f90 | 1918 +++++++++++++--------------- 1 file changed, 920 insertions(+), 998 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index f9ee91b87..9fbaa4255 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -157,1034 +157,956 @@ module opSplittin_module contains - ! ********************************************************************************************************** - ! public subroutine opSplittin: run the coupled energy-mass model for one timestep - ! - ! The logic of the solver is as follows: - ! (1) Attempt different solutions in the following order: (a) fully coupled; (b) split by state type and by - ! domain type for a given energy and mass split (vegetation, snow, and soil); and (c) scalar solution - ! for a given state type and domain subset. - ! (2) For a given split, compute a variable number of substeps (in varSubstepSundials). - ! ********************************************************************************************************** - subroutine opSplittin(& - ! input: model control - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - nState, & ! intent(in): total number of state variables - dt, & ! intent(inout): time step (s) - firstSubStep, & ! intent(in): flag to denote first sub-step - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation - ! input/output: data structures - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): index data - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - bvar_data, & ! intent(in): model variables for the local basin - lookup_data, & ! intent(in): lookup tables - model_decisions,& ! intent(in): model decisions - ! output: model control - dtMultiplier, & ! intent(out): substep multiplier (-) - tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt - stepFailure, & ! intent(out): flag to denote step failure - ixCoupling, & ! intent(out): coupling method used in this iteration - err,message) ! intent(out): error code and error message - ! --------------------------------------------------------------------------------------- - ! structure allocations - USE allocspace_module,only:allocLocal ! allocate local data structures - ! simulation of fluxes and residuals given a trial state vector - USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content - USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential - ! population/extraction of state vectors - USE indexState_module,only:indexSplit ! get state indices - USE varSubstep_module,only:varSubstep ! complete substeps for a given split - USE varSubstepSundials_module,only:varSubstepSundials ! complete substeps for a given split - ! identify name of variable type (for error message) - USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages - implicit none - ! --------------------------------------------------------------------------------------- - ! * dummy variables - ! --------------------------------------------------------------------------------------- - ! input: model control - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - integer(i4b),intent(in) :: nState ! total number of state variables - real(rkind),intent(inout) :: dt ! time step (seconds) - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) - ! input/output: data structures - type(var_i),intent(in) :: type_data ! type of vegetation and soil - type(var_d),intent(in) :: attr_data ! spatial attributes - type(var_d),intent(in) :: forc_data ! model forcing data - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU - type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin - type(zLookup), intent(in) :: lookup_data ! lookup tables - type(model_options),intent(in) :: model_decisions(:) ! model decisions - ! output: model control - real(rkind),intent(out) :: dtMultiplier ! substep multiplier (-) - logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt - logical(lgt),intent(out) :: stepFailure ! flag to denote step failure - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - ! --------------------------------------------------------------------------------------- - ! * general local variables - ! --------------------------------------------------------------------------------------- - character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b) :: minLayer ! the minimum layer used in assigning flags for flux aggregations - integer(i4b) :: iOffset ! offset to account for different indices in the soil domain - integer(i4b) :: iMin(1),iMax(1) ! bounds of a given vector - integer(i4b) :: iLayer,jLayer ! index of model layer - integer(i4b) :: iSoil ! index of soil layer - integer(i4b) :: iVar ! index of variables in data structures - logical(lgt) :: firstSuccess ! flag to define the first success - logical(lgt) :: firstFluxCall ! flag to define the first flux call - logical(lgt) :: reduceCoupledStep ! flag to define the need to reduce the length of the coupled step - type(var_dlength) :: prog_temp ! temporary model prognostic variables - type(var_dlength) :: diag_temp ! temporary model diagnostic variables - type(var_dlength) :: flux_temp ! temporary model fluxes - type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(rkind),dimension(nLayers) :: mLayerVolFracIceInit ! initial vector for volumetric fraction of ice (-) - ! ------------------------------------------------------------------------------------------------------ - ! * operator splitting - ! ------------------------------------------------------------------------------------------------------ - ! minimum timestep - real(rkind),parameter :: dtmin_coupled=1800._rkind ! minimum time step for the fully coupled solution (seconds) - real(rkind),parameter :: dtmin_split=60._rkind ! minimum time step for the fully split solution (seconds) - real(rkind),parameter :: dtmin_scalar=10._rkind ! minimum time step for the scalar solution (seconds) - real(rkind) :: dt_min ! minimum time step (seconds) - real(rkind) :: dtInit ! initial time step (seconds) - ! explicit error tolerance (depends on state type split, so defined here) - real(rkind),parameter :: errorTolLiqFlux=0.01_rkind ! error tolerance in the explicit solution (liquid flux) - real(rkind),parameter :: errorTolNrgFlux=10._rkind ! error tolerance in the explicit solution (energy flux) - ! number of substeps taken for a given split - integer(i4b) :: nSubsteps ! number of substeps taken for a given split - ! named variables defining the coupling and solution method - integer(i4b) :: ixCoupling ! index of coupling method (1,2) - integer(i4b) :: ixSolution ! index of solution method (1,2) - integer(i4b) :: ixStateThenDomain ! switch between the state and domain (1,2) - integer(i4b) :: tryDomainSplit ! (0,1) - flag to try the domain split - ! actual number of splits - integer(i4b) :: nStateTypeSplit ! number of splits for the state type - integer(i4b) :: nDomainSplit ! number of splits for the domain - integer(i4b) :: nStateSplit ! number of splits for the states within a given domain - ! indices for the state type and the domain split - integer(i4b) :: iStateTypeSplit ! index of the state type split - integer(i4b) :: iDomainSplit ! index of the domain split - integer(i4b) :: iStateSplit ! index of the state split - ! flux masks - logical(lgt) :: neededFlux(nFlux) ! .true. if flux is needed at all - logical(lgt) :: desiredFlux ! .true. if flux is desired for a given split - type(var_ilength) :: fluxCount ! number of times each flux is updated (should equal nSubsteps) - type(var_flagVec) :: fluxMask ! mask defining model fluxes - ! state masks - integer(i4b),dimension(nState) :: stateCheck ! number of times each state variable is updated (should equal 1) - logical(lgt),dimension(nState) :: stateMask ! mask defining desired state variables - integer(i4b) :: nSubset ! number of selected state variables for a given split - ! flags - logical(lgt) :: failure ! flag to denote failure of substepping - logical(lgt) :: doAdjustTemp ! flag to adjust temperature after the mass split - logical(lgt) :: failedMinimumStep ! flag to denote failure of substepping for a given split - integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - integer(i4b) :: nCoupling - real(qp) :: dt_out ! - ! --------------------------------------------------------------------------------------- - ! point to variables in the data structures - ! --------------------------------------------------------------------------------------- - globalVars: associate(& - ! model decisions - ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization - ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) - ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical method, backward Euler or SUNDIALS/IDA - ! domain boundary conditions - airtemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) - ! vector of energy and hydrology indices for the snow and soil domains - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology state variables in the snow+soil domain - ! indices of model state variables - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset (missing for values not in the subset) - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) - ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain - ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain - ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain - ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ! numerix tracking - numberStateSplit => indx_data%var(iLookINDEX%numberStateSplit )%dat(1) ,& ! intent(inout): [i4b] number of state splitting solutions (-) - numberDomainSplitNrg => indx_data%var(iLookINDEX%numberDomainSplitNrg )%dat(1) ,& ! intent(inout): [i4b] number of domain splitting solutions for energy (-) - numberDomainSplitMass => indx_data%var(iLookINDEX%numberDomainSplitMass)%dat(1) ,& ! intent(inout): [i4b] number of domain splitting solutions for mass (-) - numberScalarSolutions => indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1) ,& ! intent(inout): [i4b] number of scalar solutions (-) - ! domain configuration - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ! depth-varying soil parameters - vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) - ! soil parameters - specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) - ! model diagnostic variables (fraction of liquid water) - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) - mLayerMeltFreeze => diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat ,& ! intent(out): [dp(:)] melt-freeze in each snow and soil layer (kg m-3) - ! model state variables (vegetation canopy) - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(out): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(out): [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(out): [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(out): [dp] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(out): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(out): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(out): [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(out): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(out): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(out): [dp(:)] matric head (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat & ! intent(out): [dp(:)] matric potential of liquid water (m) - ) - ! --------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="opSplittin/" - - ! we just solve the fully coupled problem by with Sundials for now - select case(ixNumericalMethod) - case(sundials); nCoupling = 1 - case(bEuler); nCoupling = 2 - case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return - end select - - ! ***** - ! (0) PRELIMINARIES... - ! ******************** - - ! ----- - ! * initialize... - ! --------------- - - ! set the global print flag - globalPrintFlag=.false. - - if(globalPrintFlag)& - print*, trim(message), dt - - ! initialize the first success call - firstSuccess=.false. - - ! initialize the flags - tooMuchMelt=.false. ! too much melt (merge snow layers) - stepFailure=.false. ! step failure - - ! initialize flag for the success of the substepping - failure=.false. - - ! initialize the flux check - neededFlux(:) = .false. - - ! initialize the state check - stateCheck(:) = 0 - - ! compute the total water content in the vegetation canopy - scalarCanopyWat = scalarCanopyLiq + scalarCanopyIce ! kg m-2 - - ! save volumetric ice content at the start of the step - ! NOTE: used for volumetric loss due to melt-freeze - mLayerVolFracIceInit(:) = mLayerVolFracIce(:) - - ! compute the total water content in snow and soil - ! NOTE: no ice expansion allowed for soil - if(nSnow>0)& - mLayerVolFracWat( 1:nSnow ) = mLayerVolFracLiq( 1:nSnow ) + mLayerVolFracIce( 1:nSnow )*(iden_ice/iden_water) - mLayerVolFracWat(nSnow+1:nLayers) = mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) - - ! compute the liquid water matric potential (m) - ! NOTE: include ice content as part of the solid porosity - major effect of ice is to reduce the pore size; ensure that effSat=1 at saturation - ! (from Zhao et al., J. Hydrol., 1997: Numerical analysis of simultaneous heat and mass transfer...) - do iSoil=1,nSoil - call liquidHead(mLayerMatricHead(iSoil),mLayerVolFracLiq(nSnow+iSoil),mLayerVolFracIce(nSnow+iSoil), & ! input: state variables - vGn_alpha(iSoil),vGn_n(iSoil),theta_sat(iSoil),theta_res(iSoil),vGn_m(iSoil), & ! input: parameters - matricHeadLiq=mLayerMatricHeadLiq(iSoil), & ! output: liquid water matric potential (m) - err=err,message=cmessage) ! output: error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - end do ! looping through soil layers (computing liquid water matric potential) - - ! allocate space for the flux mask (used to define when fluxes are updated) - call allocLocal(flux_meta(:),fluxMask,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! allocate space for the flux count (used to check that fluxes are only updated once) - call allocLocal(flux_meta(:),fluxCount,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! allocate space for the temporary prognostic variable structure - call allocLocal(prog_meta(:),prog_temp,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! allocate space for the temporary diagnostic variable structure - call allocLocal(diag_meta(:),diag_temp,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! allocate space for the temporary flux variable structure - call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! allocate space for the derivative structure - call allocLocal(deriv_meta(:),deriv_data,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - - ! intialize the flux conter - do iVar=1,size(flux_meta) ! loop through fluxes - fluxCount%var(iVar)%dat(:) = 0 - end do - - ! initialize the model fluxes - do iVar=1,size(flux_meta) ! loop through fluxes - if(flux2state_orig(iVar)%state1==integerMissing .and. flux2state_orig(iVar)%state2==integerMissing) cycle ! flux does not depend on state (e.g., input) - if(flux2state_orig(iVar)%state1==iname_watCanopy .and. .not.computeVegFlux) cycle ! use input fluxes in cases where there is no canopy - flux_data%var(iVar)%dat(:) = 0._rkind - end do - - ! initialize derivatives - do iVar=1,size(deriv_meta) - deriv_data%var(iVar)%dat(:) = 0._rkind - end do - - ! ========================================================================================================================================== - ! ========================================================================================================================================== - ! ========================================================================================================================================== - ! ========================================================================================================================================== - - ! loop through different coupling strategies - coupling: do ixCoupling=1,nCoupling - - ! initialize the time step - dtInit = min( merge(dt, dtmin_coupled, ixCoupling==fullyCoupled), dt) ! initial time step - dt_min = min( merge(dtmin_coupled, dtmin_split, ixCoupling==fullyCoupled), dt) ! minimum time step - - ! keep track of the number of state splits - if(ixCoupling/=fullyCoupled) numberStateSplit = numberStateSplit + 1 - - ! define the number of operator splits for the state type - select case(ixCoupling) - case(fullyCoupled); nStateTypeSplit=1 - case(stateTypeSplit); nStateTypeSplit=nStateTypes - case default; err=20; message=trim(message)//'coupling case not found'; return - end select ! operator splitting option - - ! define if we wish to try the domain split - select case(ixCoupling) - case(fullyCoupled); tryDomainSplit=0 - case(stateTypeSplit); tryDomainSplit=1 - case default; err=20; message=trim(message)//'coupling case not found'; return - end select ! operator splitting option - - ! state splitting loop - stateTypeSplit: do iStateTypeSplit=1,nStateTypeSplit - - !print*, 'iStateTypeSplit, nStateTypeSplit = ', iStateTypeSplit, nStateTypeSplit - - ! ----- - ! * identify state-specific variables for a given state split... - ! -------------------------------------------------------------- - - ! flag to adjust the temperature - doAdjustTemp = (ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit) - - ! modify the state type names associated with the state vector - if(ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit)then - if(computeVegFlux)then - where(ixStateType(ixHydCanopy)==iname_watCanopy) ixStateType(ixHydCanopy)=iname_liqCanopy - endif - where(ixStateType(ixHydLayer) ==iname_watLayer) ixStateType(ixHydLayer) =iname_liqLayer - where(ixStateType(ixHydLayer) ==iname_matLayer) ixStateType(ixHydLayer) =iname_lmpLayer - endif ! if modifying state variables for the mass split - - ! first try the state type split, then try the domain split within a given state type - stateThenDomain: do ixStateThenDomain=1,1+tryDomainSplit ! 1=state type split; 2=domain split within a given state type - - !print*, 'start of stateThenDomain loop' - - ! keep track of the number of domain splits - if(iStateTypeSplit==nrgSplit .and. ixStateThenDomain==subDomain) numberDomainSplitNrg = numberDomainSplitNrg + 1 - if(iStateTypeSplit==massSplit .and. ixStateThenDomain==subDomain) numberDomainSplitMass = numberDomainSplitMass + 1 - - ! define the number of domain splits for the state type - select case(ixStateThenDomain) - case(fullDomain); nDomainSplit=1 - case(subDomain); nDomainSplit=nDomains - case default; err=20; message=trim(message)//'coupling case not found'; return +! ********************************************************************************************************** +! public subroutine opSplittin: run the coupled energy-mass model for one timestep +! +! The logic of the solver is as follows: +! (1) Attempt different solutions in the following order: (a) fully coupled; (b) split by state type and by +! domain type for a given energy and mass split (vegetation, snow, and soil); and (c) scalar solution +! for a given state type and domain subset. +! (2) For a given split, compute a variable number of substeps (in varSubstepSundials). +! ********************************************************************************************************** +subroutine opSplittin(& + ! input: model control + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + nState, & ! intent(in): total number of state variables + dt, & ! intent(inout): time step (s) + firstSubStep, & ! intent(in): flag to denote first sub-step + computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + ! input/output: data structures + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): index data + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + bvar_data, & ! intent(in): model variables for the local basin + lookup_data, & ! intent(in): lookup tables + model_decisions,& ! intent(in): model decisions + ! output: model control + dtMultiplier, & ! intent(out): substep multiplier (-) + tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt + stepFailure, & ! intent(out): flag to denote step failure + ixCoupling, & ! intent(out): coupling method used in this iteration + err,message) ! intent(out): error code and error message + ! --------------------------------------------------------------------------------------- + ! structure allocations + USE allocspace_module,only:allocLocal ! allocate local data structures + ! simulation of fluxes and residuals given a trial state vector + USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content + USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential + ! population/extraction of state vectors + USE indexState_module,only:indexSplit ! get state indices + USE varSubstep_module,only:varSubstep ! complete substeps for a given split + USE varSubstepSundials_module,only:varSubstepSundials ! complete substeps for a given split + ! identify name of variable type (for error message) + USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages + implicit none + ! --------------------------------------------------------------------------------------- + ! * dummy variables + ! --------------------------------------------------------------------------------------- + ! input: model control + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + integer(i4b),intent(in) :: nState ! total number of state variables + real(rkind),intent(inout) :: dt ! time step (seconds) + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) + ! input/output: data structures + type(var_i),intent(in) :: type_data ! type of vegetation and soil + type(var_d),intent(in) :: attr_data ! spatial attributes + type(var_d),intent(in) :: forc_data ! model forcing data + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU + type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin + type(zLookup), intent(in) :: lookup_data ! lookup tables + type(model_options),intent(in) :: model_decisions(:) ! model decisions + ! output: model control + real(rkind),intent(out) :: dtMultiplier ! substep multiplier (-) + logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt + logical(lgt),intent(out) :: stepFailure ! flag to denote step failure + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! --------------------------------------------------------------------------------------- + ! * general local variables + ! --------------------------------------------------------------------------------------- + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: minLayer ! the minimum layer used in assigning flags for flux aggregations + integer(i4b) :: iOffset ! offset to account for different indices in the soil domain + integer(i4b) :: iMin(1),iMax(1) ! bounds of a given vector + integer(i4b) :: iLayer,jLayer ! index of model layer + integer(i4b) :: iSoil ! index of soil layer + integer(i4b) :: iVar ! index of variables in data structures + logical(lgt) :: firstSuccess ! flag to define the first success + logical(lgt) :: firstFluxCall ! flag to define the first flux call + logical(lgt) :: reduceCoupledStep ! flag to define the need to reduce the length of the coupled step + type(var_dlength) :: prog_temp ! temporary model prognostic variables + type(var_dlength) :: diag_temp ! temporary model diagnostic variables + type(var_dlength) :: flux_temp ! temporary model fluxes + type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + real(rkind),dimension(nLayers) :: mLayerVolFracIceInit ! initial vector for volumetric fraction of ice (-) + ! ------------------------------------------------------------------------------------------------------ + ! * operator splitting + ! ------------------------------------------------------------------------------------------------------ + ! minimum timestep + real(rkind),parameter :: dtmin_coupled=1800._rkind ! minimum time step for the fully coupled solution (seconds) + real(rkind),parameter :: dtmin_split=60._rkind ! minimum time step for the fully split solution (seconds) + real(rkind),parameter :: dtmin_scalar=10._rkind ! minimum time step for the scalar solution (seconds) + real(rkind) :: dt_min ! minimum time step (seconds) + real(rkind) :: dtInit ! initial time step (seconds) + ! explicit error tolerance (depends on state type split, so defined here) + real(rkind),parameter :: errorTolLiqFlux=0.01_rkind ! error tolerance in the explicit solution (liquid flux) + real(rkind),parameter :: errorTolNrgFlux=10._rkind ! error tolerance in the explicit solution (energy flux) + ! number of substeps taken for a given split + integer(i4b) :: nSubsteps ! number of substeps taken for a given split + ! named variables defining the coupling and solution method + integer(i4b) :: ixCoupling ! index of coupling method (1,2) + integer(i4b) :: ixSolution ! index of solution method (1,2) + integer(i4b) :: ixStateThenDomain ! switch between the state and domain (1,2) + integer(i4b) :: tryDomainSplit ! (0,1) - flag to try the domain split + ! actual number of splits + integer(i4b) :: nStateTypeSplit ! number of splits for the state type + integer(i4b) :: nDomainSplit ! number of splits for the domain + integer(i4b) :: nStateSplit ! number of splits for the states within a given domain + ! indices for the state type and the domain split + integer(i4b) :: iStateTypeSplit ! index of the state type split + integer(i4b) :: iDomainSplit ! index of the domain split + integer(i4b) :: iStateSplit ! index of the state split + ! flux masks + logical(lgt) :: neededFlux(nFlux) ! .true. if flux is needed at all + logical(lgt) :: desiredFlux ! .true. if flux is desired for a given split + type(var_ilength) :: fluxCount ! number of times each flux is updated (should equal nSubsteps) + type(var_flagVec) :: fluxMask ! mask defining model fluxes + ! state masks + integer(i4b),dimension(nState) :: stateCheck ! number of times each state variable is updated (should equal 1) + logical(lgt),dimension(nState) :: stateMask ! mask defining desired state variables + integer(i4b) :: nSubset ! number of selected state variables for a given split + ! flags + logical(lgt) :: failure ! flag to denote failure of substepping + logical(lgt) :: doAdjustTemp ! flag to adjust temperature after the mass split + logical(lgt) :: failedMinimumStep ! flag to denote failure of substepping for a given split + integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + integer(i4b) :: nCoupling + real(qp) :: dt_out ! + ! --------------------------------------------------------------------------------------- + ! point to variables in the data structures + ! --------------------------------------------------------------------------------------- + + globalVars: associate(& + ! model decisions + ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization + ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical method, backward Euler or SUNDIALS/IDA + ! domain boundary conditions + airtemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) + ! vector of energy and hydrology indices for the snow and soil domains + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology state variables in the snow+soil domain + ! indices of model state variables + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset (missing for values not in the subset) + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) + ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain + ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ! numerix tracking + numberStateSplit => indx_data%var(iLookINDEX%numberStateSplit )%dat(1) ,& ! intent(inout): [i4b] number of state splitting solutions (-) + numberDomainSplitNrg => indx_data%var(iLookINDEX%numberDomainSplitNrg )%dat(1) ,& ! intent(inout): [i4b] number of domain splitting solutions for energy (-) + numberDomainSplitMass => indx_data%var(iLookINDEX%numberDomainSplitMass)%dat(1) ,& ! intent(inout): [i4b] number of domain splitting solutions for mass (-) + numberScalarSolutions => indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1) ,& ! intent(inout): [i4b] number of scalar solutions (-) + ! domain configuration + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ! depth-varying soil parameters + vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) + ! soil parameters + specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) + ! model diagnostic variables (fraction of liquid water) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) + mLayerMeltFreeze => diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat ,& ! intent(out): [dp(:)] melt-freeze in each snow and soil layer (kg m-3) + ! model state variables (vegetation canopy) + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(out): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(out): [dp] temperature of the vegetation canopy (K) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(out): [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(out): [dp] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(out): [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variables (snow and soil domains) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(out): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(out): [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(out): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(out): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(out): [dp(:)] matric head (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat & ! intent(out): [dp(:)] matric potential of liquid water (m) + ) + ! --------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="opSplittin/" + + ! we just solve the fully coupled problem by with Sundials for now + select case(ixNumericalMethod) + case(sundials); nCoupling = 1 + case(bEuler); nCoupling = 2 + case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return end select - ! check that we haven't split the domain when we are fully coupled - if(ixCoupling==fullyCoupled .and. nDomainSplit==nDomains)then - message=trim(message)//'cannot split domains when fully coupled' - err=20; return - endif - - ! domain splitting loop - domainSplit: do iDomainSplit=1,nDomainSplit - - ! trial with the vector then scalar solution - solution: do ixSolution=1,nSolutions - - ! initialize error control - err=0; message="opSplittin/" - - ! refine the time step - if(ixSolution==scalar)then - dtInit = min(dtmin_split, dt) ! initial time step - dt_min = min(dtmin_scalar, dt) ! minimum time step - endif - - ! initialize the first flux call - firstFluxCall=.true. - - ! get the number of split layers - select case(ixSolution) - case(vector); nStateSplit=1 - case(scalar); nStateSplit=count(stateMask) - case default; err=20; message=trim(message)//'unknown solution method'; return - end select - - !print*, '*****' - !print*, 'computeVegFlux = ', computeVegFlux - !print*, '(ixSolution==scalar) = ', (ixSolution==scalar) - !print*, 'ixCoupling, iStateTypeSplit, ixStateThenDomain, iDomainSplit, nDomainSplit: ', ixCoupling, iStateTypeSplit, ixStateThenDomain, iDomainSplit, nDomainSplit - !print*, 'ixSoilOnlyHyd = ', indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat - - ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) - stateSplit: do iStateSplit=1,nStateSplit - - ! ----- - ! * define state subsets for a given split... - ! ------------------------------------------- - - ! get the mask for the state subset - call stateFilter(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit,& - indx_data,stateMask,nSubset,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - ! check that state variables exist - if(nSubset==0) cycle domainSplit - - ! avoid redundant case where vector solution is of length 1 - if(ixSolution==vector .and. count(stateMask)==1) cycle solution - - ! check - !print*, 'after stateFilter: stateMask = ', stateMask - !print*, 'count(stateMask) = ', count(stateMask) - - !if(ixSolution==scalar)then - ! print*, 'iStateSplit, nStateSplit = ', iStateSplit, nStateSplit - ! print*, 'start of scalar solution' - ! !print*, 'PAUSE'; read(*,*) - !endif - - ! ----- - ! * assemble vectors for a given split... - ! --------------------------------------- - - ! get indices for a given split - call indexSplit(stateMask, & ! intent(in) : logical vector (.true. if state is in the subset) - nSnow,nSoil,nLayers,nSubset, & ! intent(in) : number of snow and soil layers, and total number of layers - indx_data, & ! intent(inout) : index data structure - err,cmessage) ! intent(out) : error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! ----- - ! * define the mask of the fluxes used... - ! --------------------------------------- - - ! identify the type of state for the states in the subset - stateSubset: associate(ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] indices of state types - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) - ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat ,& ! intent(in): [i4b(:)] list of indices for all active layers (inactive=integerM - ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ) ! intent(in): [i4b(:)] indices defining the type of the domain (iname_veg, iname_snow, iname_soil) - - ! loop through flux variables - do iVar=1,size(flux_meta) - - ! * identify flux mask for the fully coupled solution - if(ixCoupling==fullyCoupled)then - desiredFlux = any(ixStateType_subset==flux2state_orig(iVar)%state1) .or. any(ixStateType_subset==flux2state_orig(iVar)%state2) - fluxMask%var(iVar)%dat = desiredFlux - - ! * identify flux mask for the split solution - else - - ! identify the flux mask for a given state split - select case(iStateTypeSplit) - case(nrgSplit); desiredFlux = any(ixStateType_subset==flux2state_orig(iVar)%state1) .or. any(ixStateType_subset==flux2state_orig(iVar)%state2) - case(massSplit); desiredFlux = any(ixStateType_subset==flux2state_liq(iVar)%state1) .or. any(ixStateType_subset==flux2state_liq(iVar)%state2) - case default; err=20; message=trim(message)//'unable to identify split based on state type'; return - end select - - ! no domain splitting - if(nDomains==1)then - fluxMask%var(iVar)%dat = desiredFlux - - ! domain splitting - else - print*,"split domain" - - ! initialize to .false. - fluxMask%var(iVar)%dat = .false. - - ! only need to proceed if the flux is desired - if(desiredFlux)then - - ! different domain splitting operations - select case(iDomainSplit) - - ! canopy fluxes -- (:1) gets the upper boundary(0) if it exists - case(vegSplit) - - ! vector solution (should only be present for energy) - if(ixSolution==vector)then - fluxMask%var(iVar)%dat(:1) = desiredFlux - if(ixStateThenDomain>1 .and. iStateTypeSplit/=nrgSplit)then - message=trim(message)//'only expect a vector solution for the vegetation domain for energy' - err=20; return + ! ----- + ! * initialize... + ! --------------- + + ! set the global print flag + globalPrintFlag=.false. + + if(globalPrintFlag)& + print*, trim(message), dt + + ! initialize the first success call + firstSuccess=.false. + + ! initialize the flags + tooMuchMelt=.false. ! too much melt (merge snow layers) + stepFailure=.false. ! step failure + + ! initialize flag for the success of the substepping + failure=.false. + + ! initialize the flux check + neededFlux(:) = .false. + + ! initialize the state check + stateCheck(:) = 0 + + ! compute the total water content in the vegetation canopy + scalarCanopyWat = scalarCanopyLiq + scalarCanopyIce ! kg m-2 + + ! save volumetric ice content at the start of the step + ! NOTE: used for volumetric loss due to melt-freeze + mLayerVolFracIceInit(:) = mLayerVolFracIce(:) + + ! compute the total water content in snow and soil + ! NOTE: no ice expansion allowed for soil + if(nSnow>0)& + mLayerVolFracWat( 1:nSnow ) = mLayerVolFracLiq( 1:nSnow ) + mLayerVolFracIce( 1:nSnow )*(iden_ice/iden_water) + mLayerVolFracWat(nSnow+1:nLayers) = mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) + + ! compute the liquid water matric potential (m) + ! NOTE: include ice content as part of the solid porosity - major effect of ice is to reduce the pore size; ensure that effSat=1 at saturation + ! (from Zhao et al., J. Hydrol., 1997: Numerical analysis of simultaneous heat and mass transfer...) + do iSoil=1,nSoil + call liquidHead(mLayerMatricHead(iSoil),mLayerVolFracLiq(nSnow+iSoil),mLayerVolFracIce(nSnow+iSoil), & ! input: state variables + vGn_alpha(iSoil),vGn_n(iSoil),theta_sat(iSoil),theta_res(iSoil),vGn_m(iSoil), & ! input: parameters + matricHeadLiq=mLayerMatricHeadLiq(iSoil), & ! output: liquid water matric potential (m) + err=err,message=cmessage) ! output: error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + end do ! looping through soil layers (computing liquid water matric potential) + + ! allocate space for the flux mask (used to define when fluxes are updated) + call allocLocal(flux_meta(:),fluxMask,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! allocate space for the flux count (used to check that fluxes are only updated once) + call allocLocal(flux_meta(:),fluxCount,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! allocate space for the temporary prognostic variable structure + call allocLocal(prog_meta(:),prog_temp,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! allocate space for the temporary diagnostic variable structure + call allocLocal(diag_meta(:),diag_temp,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! allocate space for the temporary flux variable structure + call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! allocate space for the derivative structure + call allocLocal(deriv_meta(:),deriv_data,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + ! intialize the flux conter + do iVar=1,size(flux_meta) ! loop through fluxes + fluxCount%var(iVar)%dat(:) = 0 + end do + + ! initialize the model fluxes + do iVar=1,size(flux_meta) ! loop through fluxes + if(flux2state_orig(iVar)%state1==integerMissing .and. flux2state_orig(iVar)%state2==integerMissing) cycle ! flux does not depend on state (e.g., input) + if(flux2state_orig(iVar)%state1==iname_watCanopy .and. .not.computeVegFlux) cycle ! use input fluxes in cases where there is no canopy + flux_data%var(iVar)%dat(:) = 0._rkind + end do + + ! initialize derivatives + do iVar=1,size(deriv_meta) + deriv_data%var(iVar)%dat(:) = 0._rkind + end do + + ! ========================================================================================================================================== + ! ========================================================================================================================================== + ! ========================================================================================================================================== + ! ========================================================================================================================================== + + ! loop through different coupling strategies + coupling: do ixCoupling=1,nCoupling + + ! initialize the time step + dtInit = min( merge(dt, dtmin_coupled, ixCoupling==fullyCoupled), dt) ! initial time step + dt_min = min( merge(dtmin_coupled, dtmin_split, ixCoupling==fullyCoupled), dt) ! minimum time step + + ! keep track of the number of state splits + if(ixCoupling/=fullyCoupled) numberStateSplit = numberStateSplit + 1 + + ! define the number of operator splits for the state type + select case(ixCoupling) + case(fullyCoupled); nStateTypeSplit=1 + case(stateTypeSplit); nStateTypeSplit=nStateTypes + case default; err=20; message=trim(message)//'coupling case not found'; return + end select ! operator splitting option + + ! define if we wish to try the domain split + select case(ixCoupling) + case(fullyCoupled); tryDomainSplit=0 + case(stateTypeSplit); tryDomainSplit=1 + case default; err=20; message=trim(message)//'coupling case not found'; return + end select ! operator splitting option + + ! state splitting loop + stateTypeSplit: do iStateTypeSplit=1,nStateTypeSplit + + ! ----- + ! * identify state-specific variables for a given state split... + ! -------------------------------------------------------------- + + ! flag to adjust the temperature + doAdjustTemp = (ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit) + + ! modify the state type names associated with the state vector + if(ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit)then + if(computeVegFlux)then + where(ixStateType(ixHydCanopy)==iname_watCanopy) ixStateType(ixHydCanopy)=iname_liqCanopy + endif + where(ixStateType(ixHydLayer) ==iname_watLayer) ixStateType(ixHydLayer) =iname_liqLayer + where(ixStateType(ixHydLayer) ==iname_matLayer) ixStateType(ixHydLayer) =iname_lmpLayer + endif ! if modifying state variables for the mass split + + ! first try the state type split, then try the domain split within a given state type + stateThenDomain: do ixStateThenDomain=1,1+tryDomainSplit ! 1=state type split; 2=domain split within a given state type + + ! keep track of the number of domain splits + if(iStateTypeSplit==nrgSplit .and. ixStateThenDomain==subDomain) numberDomainSplitNrg = numberDomainSplitNrg + 1 + if(iStateTypeSplit==massSplit .and. ixStateThenDomain==subDomain) numberDomainSplitMass = numberDomainSplitMass + 1 + + ! define the number of domain splits for the state type + select case(ixStateThenDomain) + case(fullDomain); nDomainSplit=1 + case(subDomain); nDomainSplit=nDomains + case default; err=20; message=trim(message)//'coupling case not found'; return + end select + + ! check that we haven't split the domain when we are fully coupled + if(ixCoupling==fullyCoupled .and. nDomainSplit==nDomains)then + message=trim(message)//'cannot split domains when fully coupled' + err=20; return + endif + + ! domain splitting loop + domainSplit: do iDomainSplit=1,nDomainSplit + + ! trial with the vector then scalar solution + solution: do ixSolution=1,nSolutions + + ! initialize error control + err=0; message="opSplittin/" + + ! refine the time step + if(ixSolution==scalar)then + dtInit = min(dtmin_split, dt) ! initial time step + dt_min = min(dtmin_scalar, dt) ! minimum time step endif - ! scalar solution - else - fluxMask%var(iVar)%dat(:1) = desiredFlux - endif - - ! fluxes through snow and soil - case(snowSplit,soilSplit) - - ! loop through layers - do iLayer=1,nLayers - if(ixlayerActive(iLayer)/=integerMissing)then - - ! get the offset (ixLayerActive=1,2,3,...nLayers, and soil vectors nSnow+1, nSnow+2, ..., nLayers) - iOffset = merge(nSnow, 0, flux_meta(iVar)%vartype==iLookVarType%midSoil .or. flux_meta(iVar)%vartype==iLookVarType%ifcSoil) - jLayer = iLayer-iOffset - - ! identify the minimum layer - select case(flux_meta(iVar)%vartype) - case(iLookVarType%ifcToto, iLookVarType%ifcSnow, iLookVarType%ifcSoil); minLayer=merge(jLayer-1, jLayer, jLayer==1) - case(iLookVarType%midToto, iLookVarType%midSnow, iLookVarType%midSoil); minLayer=jLayer - case default; minLayer=integerMissing - end select - - ! set desired layers - select case(flux_meta(iVar)%vartype) - case(iLookVarType%midToto,iLookVarType%ifcToto); fluxMask%var(iVar)%dat(minLayer:jLayer) = desiredFlux - case(iLookVarType%midSnow,iLookVarType%ifcSnow); if(iLayer<=nSnow) fluxMask%var(iVar)%dat(minLayer:jLayer) = desiredFlux - case(iLookVarType%midSoil,iLookVarType%ifcSoil); if(iLayer> nSnow) fluxMask%var(iVar)%dat(minLayer:jLayer) = desiredFlux - end select - - ! add hydrology states for scalar variables - if(iStateTypeSplit==massSplit .and. flux_meta(iVar)%vartype==iLookVarType%scalarv)then - select case(iDomainSplit) - case(snowSplit); if(iLayer==nSnow) fluxMask%var(iVar)%dat = desiredFlux - case(soilSplit); if(iLayer==nSnow+1) fluxMask%var(iVar)%dat = desiredFlux - end select - endif ! if hydrology split and scalar + ! initialize the first flux call + firstFluxCall=.true. - endif ! if the layer is active - end do ! looping through layers + ! get the number of split layers + select case(ixSolution) + case(vector); nStateSplit=1 + case(scalar); nStateSplit=count(stateMask) + case default; err=20; message=trim(message)//'unknown solution method'; return + end select - ! check - case default; err=20; message=trim(message)//'unable to identify split based on domain type'; return - end select ! domain split - endif ! if flux is desired + ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) + stateSplit: do iStateSplit=1,nStateSplit - endif ! domain splitting - endif ! not fully coupled + ! ----- + ! * define state subsets for a given split... + ! ------------------------------------------- - ! define if the flux is desired - if(desiredFlux) neededFlux(iVar)=.true. - !if(desiredFlux) print*, flux_meta(iVar)%varname, fluxMask%var(iVar)%dat + ! get the mask for the state subset + call stateFilter(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit,& + indx_data,stateMask,nSubset,err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + + ! check that state variables exist + if(nSubset==0) cycle domainSplit + + ! avoid redundant case where vector solution is of length 1 + if(ixSolution==vector .and. count(stateMask)==1) cycle solution + + + + ! ----- + ! * assemble vectors for a given split... + ! --------------------------------------- + ! get indices for a given split + call indexSplit(stateMask, & ! intent(in) : logical vector (.true. if state is in the subset) + nSnow,nSoil,nLayers,nSubset, & ! intent(in) : number of snow and soil layers, and total number of layers + indx_data, & ! intent(inout) : index data structure + err,cmessage) ! intent(out) : error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! ----- + ! * define the mask of the fluxes used... + ! --------------------------------------- + + ! identify the type of state for the states in the subset + stateSubset: associate(ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] indices of state types + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) + ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat ,& ! intent(in): [i4b(:)] list of indices for all active layers (inactive=integerM + ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ) ! intent(in): [i4b(:)] indices defining the type of the domain (iname_veg, iname_snow, iname_soil) + + ! loop through flux variables + do iVar=1,size(flux_meta) + + ! * identify flux mask for the fully coupled solution + if(ixCoupling==fullyCoupled)then + desiredFlux = any(ixStateType_subset==flux2state_orig(iVar)%state1) .or. any(ixStateType_subset==flux2state_orig(iVar)%state2) + fluxMask%var(iVar)%dat = desiredFlux - ! * check - if( globalPrintFlag .and. count(fluxMask%var(iVar)%dat)>0 )& - print*, trim(flux_meta(iVar)%varname) + ! * identify flux mask for the split solution + else + + ! identify the flux mask for a given state split + select case(iStateTypeSplit) + case(nrgSplit); desiredFlux = any(ixStateType_subset==flux2state_orig(iVar)%state1) .or. any(ixStateType_subset==flux2state_orig(iVar)%state2) + case(massSplit); desiredFlux = any(ixStateType_subset==flux2state_liq(iVar)%state1) .or. any(ixStateType_subset==flux2state_liq(iVar)%state2) + case default; err=20; message=trim(message)//'unable to identify split based on state type'; return + end select + + ! no domain splitting + if(nDomains==1)then + fluxMask%var(iVar)%dat = desiredFlux - end do ! (loop through fluxes) + ! domain splitting + else + print*,"split domain" + + ! initialize to .false. + fluxMask%var(iVar)%dat = .false. + + ! only need to proceed if the flux is desired + if(desiredFlux)then + + ! different domain splitting operations + select case(iDomainSplit) + + ! canopy fluxes -- (:1) gets the upper boundary(0) if it exists + case(vegSplit) + + ! vector solution (should only be present for energy) + if(ixSolution==vector)then + fluxMask%var(iVar)%dat(:1) = desiredFlux + if(ixStateThenDomain>1 .and. iStateTypeSplit/=nrgSplit)then + message=trim(message)//'only expect a vector solution for the vegetation domain for energy' + err=20; return + endif + + ! scalar solution + else + fluxMask%var(iVar)%dat(:1) = desiredFlux + endif + + ! fluxes through snow and soil + case(snowSplit,soilSplit) + + ! loop through layers + do iLayer=1,nLayers + if(ixlayerActive(iLayer)/=integerMissing)then + + ! get the offset (ixLayerActive=1,2,3,...nLayers, and soil vectors nSnow+1, nSnow+2, ..., nLayers) + iOffset = merge(nSnow, 0, flux_meta(iVar)%vartype==iLookVarType%midSoil .or. flux_meta(iVar)%vartype==iLookVarType%ifcSoil) + jLayer = iLayer-iOffset + + ! identify the minimum layer + select case(flux_meta(iVar)%vartype) + case(iLookVarType%ifcToto, iLookVarType%ifcSnow, iLookVarType%ifcSoil); minLayer=merge(jLayer-1, jLayer, jLayer==1) + case(iLookVarType%midToto, iLookVarType%midSnow, iLookVarType%midSoil); minLayer=jLayer + case default; minLayer=integerMissing + end select + + ! set desired layers + select case(flux_meta(iVar)%vartype) + case(iLookVarType%midToto,iLookVarType%ifcToto); fluxMask%var(iVar)%dat(minLayer:jLayer) = desiredFlux + case(iLookVarType%midSnow,iLookVarType%ifcSnow); if(iLayer<=nSnow) fluxMask%var(iVar)%dat(minLayer:jLayer) = desiredFlux + case(iLookVarType%midSoil,iLookVarType%ifcSoil); if(iLayer> nSnow) fluxMask%var(iVar)%dat(minLayer:jLayer) = desiredFlux + end select + + ! add hydrology states for scalar variables + if(iStateTypeSplit==massSplit .and. flux_meta(iVar)%vartype==iLookVarType%scalarv)then + select case(iDomainSplit) + case(snowSplit); if(iLayer==nSnow) fluxMask%var(iVar)%dat = desiredFlux + case(soilSplit); if(iLayer==nSnow+1) fluxMask%var(iVar)%dat = desiredFlux + end select + endif ! if hydrology split and scalar + + endif ! if the layer is active + end do ! looping through layers + + ! check + case default; err=20; message=trim(message)//'unable to identify split based on domain type'; return + end select ! domain split + + endif ! if flux is desired + + endif ! domain splitting + endif ! not fully coupled + + ! define if the flux is desired + if(desiredFlux) neededFlux(iVar)=.true. + !if(desiredFlux) print*, flux_meta(iVar)%varname, fluxMask%var(iVar)%dat + + ! * check + if( globalPrintFlag .and. count(fluxMask%var(iVar)%dat)>0 )& + print*, trim(flux_meta(iVar)%varname) + + end do ! (loop through fluxes) + + end associate stateSubset + + ! ******************************************************************************************************************************* + ! ******************************************************************************************************************************* + ! ******************************************************************************************************************************* + ! ***** trial with a given solution method... + + ! check that we do not attempt the scalar solution for the fully coupled case + if(ixCoupling==fullyCoupled .and. ixSolution==scalar)then + message=trim(message)//'only apply the scalar solution to the fully split coupling strategy' + err=20; return + endif + + ! reset the flag for the first flux call + if(.not.firstSuccess) firstFluxCall=.true. + + ! save/recover copies of prognostic variables + do iVar=1,size(prog_data%var) + select case(failure) + case(.false.); prog_temp%var(iVar)%dat(:) = prog_data%var(iVar)%dat(:) + case(.true.); prog_data%var(iVar)%dat(:) = prog_temp%var(iVar)%dat(:) + end select + end do ! looping through variables + + ! save/recover copies of diagnostic variables + do iVar=1,size(diag_data%var) + select case(failure) + case(.false.); diag_temp%var(iVar)%dat(:) = diag_data%var(iVar)%dat(:) + case(.true.); diag_data%var(iVar)%dat(:) = diag_temp%var(iVar)%dat(:) + end select + end do ! looping through variables + + ! save/recover copies of model fluxes + do iVar=1,size(flux_data%var) + select case(failure) + case(.false.); flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + case(.true.); flux_data%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) + end select + end do ! looping through variables + + ! ----- + ! * solve variable subset for one time step... + ! -------------------------------------------- + + ! keep track of the number of scalar solutions + if(ixSolution==scalar) numberScalarSolutions = numberScalarSolutions + 1 + + ! solve variable subset for one full time step + select case(ixNumericalMethod) + case(sundials) + call varSubstepSundials(& + ! input: model control + dt, & ! intent(in) : time step (s) + dtInit, & ! intent(in) : initial time step (seconds) + dt_min, & ! intent(in) : minimum time step (seconds) + nSubset, & ! intent(in) : total number of variables in the state subset + doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature + firstSubStep, & ! intent(in) : flag to denote first sub-step + firstFluxCall, & ! intent(inout) : flag to indicate if we are processing the first flux call + computeVegFlux, & ! intent(in) : flag to denote if computing energy flux over vegetation + (ixSolution==scalar), & ! intent(in) : flag to denote computing the scalar solution + iStateSplit, & ! intent(in) : index of the layer in the splitting operation + fluxMask, & ! intent(in) : mask for the fluxes used in this given state subset + fluxCount, & ! intent(inout) : number of times fluxes are updated (should equal nsubstep) + ! input/output: data structures + model_decisions, & ! intent(in) : model decisions + lookup_data, & ! intent(in) : lookup tables + type_data, & ! intent(in) : type of vegetation and soil + attr_data, & ! intent(in) : spatial attributes + forc_data, & ! intent(in) : model forcing data + mpar_data, & ! intent(in) : model parameters + indx_data, & ! intent(inout) : index data + prog_data, & ! intent(inout) : model prognostic variables for a local HRU + diag_data, & ! intent(inout) : model diagnostic variables for a local HRU + flux_data, & ! intent(inout) : model fluxes for a local HRU + deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables + bvar_data, & ! intent(in) : model variables for the local basin + ! output: control + ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) + dtMultiplier, & ! intent(out) : substep multiplier (-) + nSubsteps, & ! intent(out) : number of substeps taken for a given split + failedMinimumStep, & ! intent(out) : flag for failed substeps + reduceCoupledStep, & ! intent(out) : flag to reduce the length of the coupled step + tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt + dt_out, & ! intent(out) + err,cmessage) ! intent(out) : error code and error message + + dt = dt_out + + case(bEuler) + call varSubstep(& + ! input: model control + dt, & ! intent(in) : time step (s) + dtInit, & ! intent(in) : initial time step (seconds) + dt_min, & ! intent(in) : minimum time step (seconds) + nSubset, & ! intent(in) : total number of variables in the state subset + doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature + firstSubStep, & ! intent(in) : flag to denote first sub-step + firstFluxCall, & ! intent(inout) : flag to indicate if we are processing the first flux call + computeVegFlux, & ! intent(in) : flag to denote if computing energy flux over vegetation + (ixSolution==scalar), & ! intent(in) : flag to denote computing the scalar solution + iStateSplit, & ! intent(in) : index of the layer in the splitting operation + fluxMask, & ! intent(in) : mask for the fluxes used in this given state subset + fluxCount, & ! intent(inout) : number of times fluxes are updated (should equal nsubstep) + ! input/output: data structures + model_decisions, & ! intent(in) : model decisions + lookup_data, & ! intent(in) : lookup tables + type_data, & ! intent(in) : type of vegetation and soil + attr_data, & ! intent(in) : spatial attributes + forc_data, & ! intent(in) : model forcing data + mpar_data, & ! intent(in) : model parameters + indx_data, & ! intent(inout) : index data + prog_data, & ! intent(inout) : model prognostic variables for a local HRU + diag_data, & ! intent(inout) : model diagnostic variables for a local HRU + flux_data, & ! intent(inout) : model fluxes for a local HRU + deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables + bvar_data, & ! intent(in) : model variables for the local basin + ! output: control + ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) + dtMultiplier, & ! intent(out) : substep multiplier (-) + nSubsteps, & ! intent(out) : number of substeps taken for a given split + failedMinimumStep, & ! intent(out) : flag for failed substeps + reduceCoupledStep, & ! intent(out) : flag to reduce the length of the coupled step + tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt + err,cmessage) ! intent(out) : error code and error message + ! check + case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return + end select + + if(err/=0)then + message=trim(message)//trim(cmessage) + if(err>0) return + endif ! (check for errors) + + ! reduce coupled step if failed the minimum step for the scalar solution + if(failedMinimumStep .and. ixSolution==scalar) reduceCoupledStep=.true. + + ! if too much melt (or some other need to reduce the coupled step) then return + ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry + if(tooMuchMelt .or. reduceCoupledStep)then + stepFailure=.true. + err=0 ! recovering + return + endif + + ! define failure + failure = (failedMinimumStep .or. err<0) + if(.not.failure) firstSuccess=.true. + + ! if failed, need to reset the flux counter + if(failure)then + do iVar=1,size(flux_meta) + iMin=lbound(flux_data%var(iVar)%dat) + iMax=ubound(flux_data%var(iVar)%dat) + do iLayer=iMin(1),iMax(1) + if(fluxMask%var(iVar)%dat(iLayer)) fluxCount%var(iVar)%dat(iLayer) = fluxCount%var(iVar)%dat(iLayer) - nSubsteps + end do + end do + endif + + ! try the fully split solution if failed to converge with a minimum time step in the coupled solution + if(ixCoupling==fullyCoupled .and. failure) cycle coupling + + ! try the scalar solution if failed to converge with a minimum time step in the split solution + if(ixCoupling/=fullyCoupled)then + select case(ixStateThenDomain) + case(fullDomain); if(failure) cycle stateThenDomain + case(subDomain); if(failure) cycle solution + case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' + end select + endif + + ! check that state variables updated + where(stateMask) stateCheck = stateCheck+1 + if(any(stateCheck>1))then + message=trim(message)//'state variable updated more than once!' + err=20; return + endif + + ! success = exit solution + if(.not.failure)then + select case(ixStateThenDomain) + case(fullDomain); if(iStateSplit==nStateSplit) exit stateThenDomain + case(subDomain); if(iStateSplit==nStateSplit) exit solution + case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' + end select + else + + ! check that we did not fail for the scalar solution (last resort) + if(ixSolution==scalar)then + message=trim(message)//'failed the minimum step for the scalar solution' + err=20; return + + ! check for an unexpected failure + else + message=trim(message)//'unexpected failure' + err=20; return + endif + + endif ! success check + + end do stateSplit ! solution with split layers + + end do solution ! trial with the full layer solution then the split layer solution + + ! ***** trial with a given solution method... + ! ******************************************************************************************************************************* + ! ******************************************************************************************************************************* + ! ******************************************************************************************************************************* + + end do domainSplit ! domain type splitting loop + + + end do stateThenDomain ! switch between the state and the domain + + + ! ----- + ! * reset state variables for the mass split... + ! --------------------------------------------- + ! modify the state type names associated with the state vector + if(ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit)then + if(computeVegFlux)then + where(ixStateType(ixHydCanopy)==iname_liqCanopy) ixStateType(ixHydCanopy)=iname_watCanopy + endif + where(ixStateType(ixHydLayer) ==iname_liqLayer) ixStateType(ixHydLayer) =iname_watLayer + where(ixStateType(ixHydLayer) ==iname_lmpLayer) ixStateType(ixHydLayer) =iname_matLayer + endif ! if modifying state variables for the mass split + + end do stateTypeSplit ! state type splitting loop + + ! success = exit the coupling loop + if(ixCoupling==fullyCoupled .and. .not.failure) exit coupling + + end do coupling ! coupling method + + ! check that all state variables were updated + if(any(stateCheck==0))then + message=trim(message)//'some state variables were not updated!' + err=20; return + endif - end associate stateSubset + ! check that the desired fluxes were computed + do iVar=1,size(flux_meta) + if(neededFlux(iVar) .and. any(fluxCount%var(iVar)%dat==0))then + print*, 'fluxCount%var(iVar)%dat = ', fluxCount%var(iVar)%dat + message=trim(message)//'flux '//trim(flux_meta(iVar)%varname)//' was not computed' + err=20; return + endif + end do - ! ******************************************************************************************************************************* - ! ******************************************************************************************************************************* - ! ******************************************************************************************************************************* - ! ***** trial with a given solution method... + ! use step halving if unable to complete the fully coupled solution in one substep + if(ixCoupling/=fullyCoupled .or. nSubsteps>1) dtMultiplier=0.5_rkind - ! check that we do not attempt the scalar solution for the fully coupled case - if(ixCoupling==fullyCoupled .and. ixSolution==scalar)then - message=trim(message)//'only apply the scalar solution to the fully split coupling strategy' - err=20; return - endif - - ! reset the flag for the first flux call - if(.not.firstSuccess) firstFluxCall=.true. - - ! save/recover copies of prognostic variables - do iVar=1,size(prog_data%var) - select case(failure) - case(.false.); prog_temp%var(iVar)%dat(:) = prog_data%var(iVar)%dat(:) - case(.true.); prog_data%var(iVar)%dat(:) = prog_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - ! save/recover copies of diagnostic variables - do iVar=1,size(diag_data%var) - select case(failure) - case(.false.); diag_temp%var(iVar)%dat(:) = diag_data%var(iVar)%dat(:) - case(.true.); diag_data%var(iVar)%dat(:) = diag_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - ! save/recover copies of model fluxes - do iVar=1,size(flux_data%var) - select case(failure) - case(.false.); flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) - case(.true.); flux_data%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - ! ----- - ! * solve variable subset for one time step... - ! -------------------------------------------- - - !print*, trim(message)//'before varSubstepSundials: nSubset = ', nSubset - - ! keep track of the number of scalar solutions - if(ixSolution==scalar) numberScalarSolutions = numberScalarSolutions + 1 - - ! solve variable subset for one full time step - select case(ixNumericalMethod) - case(sundials) - call varSubstepSundials(& - ! input: model control - dt, & ! intent(in) : time step (s) - dtInit, & ! intent(in) : initial time step (seconds) - dt_min, & ! intent(in) : minimum time step (seconds) - nSubset, & ! intent(in) : total number of variables in the state subset - doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature - firstSubStep, & ! intent(in) : flag to denote first sub-step - firstFluxCall, & ! intent(inout) : flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in) : flag to denote if computing energy flux over vegetation - (ixSolution==scalar), & ! intent(in) : flag to denote computing the scalar solution - iStateSplit, & ! intent(in) : index of the layer in the splitting operation - fluxMask, & ! intent(in) : mask for the fluxes used in this given state subset - fluxCount, & ! intent(inout) : number of times fluxes are updated (should equal nsubstep) - ! input/output: data structures - model_decisions, & ! intent(in) : model decisions - lookup_data, & ! intent(in) : lookup tables - type_data, & ! intent(in) : type of vegetation and soil - attr_data, & ! intent(in) : spatial attributes - forc_data, & ! intent(in) : model forcing data - mpar_data, & ! intent(in) : model parameters - indx_data, & ! intent(inout) : index data - prog_data, & ! intent(inout) : model prognostic variables for a local HRU - diag_data, & ! intent(inout) : model diagnostic variables for a local HRU - flux_data, & ! intent(inout) : model fluxes for a local HRU - deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables - bvar_data, & ! intent(in) : model variables for the local basin - ! output: control - ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) - dtMultiplier, & ! intent(out) : substep multiplier (-) - nSubsteps, & ! intent(out) : number of substeps taken for a given split - failedMinimumStep, & ! intent(out) : flag for failed substeps - reduceCoupledStep, & ! intent(out) : flag to reduce the length of the coupled step - tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt - dt_out, & ! intent(out) - err,cmessage) ! intent(out) : error code and error message - - dt = dt_out - - case(bEuler) - call varSubstep(& - ! input: model control - dt, & ! intent(in) : time step (s) - dtInit, & ! intent(in) : initial time step (seconds) - dt_min, & ! intent(in) : minimum time step (seconds) - nSubset, & ! intent(in) : total number of variables in the state subset - doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature - firstSubStep, & ! intent(in) : flag to denote first sub-step - firstFluxCall, & ! intent(inout) : flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in) : flag to denote if computing energy flux over vegetation - (ixSolution==scalar), & ! intent(in) : flag to denote computing the scalar solution - iStateSplit, & ! intent(in) : index of the layer in the splitting operation - fluxMask, & ! intent(in) : mask for the fluxes used in this given state subset - fluxCount, & ! intent(inout) : number of times fluxes are updated (should equal nsubstep) - ! input/output: data structures - model_decisions, & ! intent(in) : model decisions - lookup_data, & ! intent(in) : lookup tables - type_data, & ! intent(in) : type of vegetation and soil - attr_data, & ! intent(in) : spatial attributes - forc_data, & ! intent(in) : model forcing data - mpar_data, & ! intent(in) : model parameters - indx_data, & ! intent(inout) : index data - prog_data, & ! intent(inout) : model prognostic variables for a local HRU - diag_data, & ! intent(inout) : model diagnostic variables for a local HRU - flux_data, & ! intent(inout) : model fluxes for a local HRU - deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables - bvar_data, & ! intent(in) : model variables for the local basin - ! output: control - ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) - dtMultiplier, & ! intent(out) : substep multiplier (-) - nSubsteps, & ! intent(out) : number of substeps taken for a given split - failedMinimumStep, & ! intent(out) : flag for failed substeps - reduceCoupledStep, & ! intent(out) : flag to reduce the length of the coupled step - tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt - err,cmessage) ! intent(out) : error code and error message - ! check - case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return - end select - - if(err/=0)then - message=trim(message)//trim(cmessage) - if(err>0) return - endif ! (check for errors) - - !print*, trim(message)//'after varSubstepSundials: scalarSnowDrainage = ', flux_data%var(iLookFLUX%scalarSnowDrainage)%dat - !print*, trim(message)//'after varSubstepSundials: iLayerLiqFluxSnow = ', flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat - !print*, trim(message)//'after varSubstepSundials: iLayerLiqFluxSoil = ', flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat - - ! check - !if(ixSolution==scalar)then - ! print*, 'PAUSE: check scalar'; read(*,*) - !endif - - ! reduce coupled step if failed the minimum step for the scalar solution - if(failedMinimumStep .and. ixSolution==scalar) reduceCoupledStep=.true. - - ! check - !if(ixCoupling/=fullyCoupled)then - ! print*, 'dt = ', dt - ! print*, 'after varSubstepSundials: err = ', err - ! print*, 'after varSubstepSundials: cmessage = ', trim(cmessage) - ! print*, 'after varSubstepSundials: computeVegFlux = ', computeVegFlux - ! print*, 'after varSubstepSundials: stateMask = ', stateMask - ! print*, 'after varSubstepSundials: coupling = ', (ixCoupling==fullyCoupled) - ! print*, 'after varSubstepSundials: scalar solve = ', (ixSolution==scalar) - ! print*, 'iStateTypeSplit, nStateTypeSplit = ', iStateTypeSplit, nStateTypeSplit - ! print*, 'iDomainSplit, nDomainSplit = ', iDomainSplit, nDomainSplit - ! print*, 'nSubset = ', nSubset - ! print*, 'tooMuchMelt = ', tooMuchMelt - ! print*, 'reduceCoupledStep = ', reduceCoupledStep - ! print*, 'failedMinimumStep = ', failedMinimumStep, merge('coupled','opSplit',ixCoupling==fullyCoupled) - ! if(ixSolution==scalar)then; print*, 'PAUSE'; read(*,*); endif - !endif - - !if(ixSolution==scalar)then - ! !print*, trim(message)//'stop: checking scalar solution'; stop - ! print*, trim(message)//'pause: checking scalar solution'; read(*,*) - !endif - - !print*, 'tooMuchMelt, reduceCoupledStep = ', tooMuchMelt, reduceCoupledStep - - ! if too much melt (or some other need to reduce the coupled step) then return - ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry - if(tooMuchMelt .or. reduceCoupledStep)then - stepFailure=.true. - err=0 ! recovering - return - endif - - ! define failure - failure = (failedMinimumStep .or. err<0) - if(.not.failure) firstSuccess=.true. - - ! if failed, need to reset the flux counter - if(failure)then - !print*, 'failure!' - do iVar=1,size(flux_meta) - iMin=lbound(flux_data%var(iVar)%dat) - iMax=ubound(flux_data%var(iVar)%dat) - do iLayer=iMin(1),iMax(1) - if(fluxMask%var(iVar)%dat(iLayer)) fluxCount%var(iVar)%dat(iLayer) = fluxCount%var(iVar)%dat(iLayer) - nSubsteps - end do - !if(iVar==iLookFLUX%mLayerTranspire) print*, flux_meta(iVar)%varname, fluxCount%var(iVar)%dat - end do - endif - - ! try the fully split solution if failed to converge with a minimum time step in the coupled solution - if(ixCoupling==fullyCoupled .and. failure) cycle coupling - - ! try the scalar solution if failed to converge with a minimum time step in the split solution - if(ixCoupling/=fullyCoupled)then - select case(ixStateThenDomain) - case(fullDomain); if(failure) cycle stateThenDomain - case(subDomain); if(failure) cycle solution - case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' - end select - endif - - ! check that state variables updated - where(stateMask) stateCheck = stateCheck+1 - if(any(stateCheck>1))then - message=trim(message)//'state variable updated more than once!' - err=20; return - endif + ! compute the melt in each snow and soil layer + if(nSnow>0)then + diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(1:nSnow) = -( mLayerVolFracIce(1:nSnow) - mLayerVolFracIceInit(1:nSnow) ) * iden_ice + diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(nSnow+1:nLayers) = -(mLayerVolFracIce(nSnow+1:nLayers) - mLayerVolFracIceInit(nSnow+1:nLayers))*iden_water + endif - ! success = exit solution - if(.not.failure)then + ! end associate statements + end associate globalVars + +end subroutine opSplittin + + +! ********************************************************************************************************** +! private subroutine stateFilter: get a mask for the desired state variables +! ********************************************************************************************************** +subroutine stateFilter(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit,& + indx_data,stateMask,nSubset,err,message) + + USE indexState_module,only:indxSubset ! get state indices + implicit none + ! input + integer(i4b),intent(in) :: ixCoupling ! index of coupling method (1,2) + integer(i4b),intent(in) :: ixSolution ! index of solution method (1,2) + integer(i4b),intent(in) :: ixStateThenDomain ! switch between full domain and sub domains + integer(i4b),intent(in) :: iStateTypeSplit ! index of the state type split + integer(i4b),intent(in) :: iDomainSplit ! index of the domain split + integer(i4b),intent(in) :: iStateSplit ! index of the layer split + type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU + ! output + logical(lgt),intent(out) :: stateMask(:) ! mask defining desired state variables + integer(i4b),intent(out) :: nSubset ! number of selected state variables for a given split + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local + integer(i4b),allocatable :: ixSubset(:) ! list of indices in the state subset + character(len=256) :: cmessage ! error message + ! -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + ! data structures + associate(& + ! indices of model state variables + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) + ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain + ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + ixWatAquifer => indx_data%var(iLookINDEX%ixWatAquifer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for water storage in the aquifer + ixAllState => indx_data%var(iLookINDEX%ixAllState)%dat ,& ! intent(in): [i4b(:)] list of indices for all model state variables (1,2,3,...nState) + ! number of layers + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in): [i4b] total number of layers + ) ! data structures + ! -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='stateFilter/' + + ! identify splitting option + select case(ixCoupling) + + ! ----- + ! - fully coupled... + ! ------------------ + + ! use all state variables + case(fullyCoupled); stateMask(:) = .true. + + ! ----- + ! - splitting by state type... + ! ---------------------------- + + ! initial split by state type + case(stateTypeSplit) + + ! switch between full domain and sub domains select case(ixStateThenDomain) - case(fullDomain); if(iStateSplit==nStateSplit) exit stateThenDomain - case(subDomain); if(iStateSplit==nStateSplit) exit solution - case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' - end select - else - ! check that we did not fail for the scalar solution (last resort) - if(ixSolution==scalar)then - message=trim(message)//'failed the minimum step for the scalar solution' - err=20; return + ! split into energy and mass + case(fullDomain) + select case(iStateTypeSplit) + case(nrgSplit); stateMask = (ixStateType==iname_nrgCanair .or. ixStateType==iname_nrgCanopy .or. ixStateType==iname_nrgLayer) + case(massSplit); stateMask = (ixStateType==iname_liqCanopy .or. ixStateType==iname_liqLayer .or. ixStateType==iname_lmpLayer .or. ixStateType==iname_watAquifer) + case default; err=20; message=trim(message)//'unable to identify split based on state type'; return + end select - ! check for an unexpected failure - else - message=trim(message)//'unexpected failure' - err=20; return - endif + ! split into vegetation, snow, and soil + case(subDomain) - endif ! success check + ! define state mask + stateMask=.false. ! (initialize state mask) + select case(iStateTypeSplit) - end do stateSplit ! solution with split layers - !print*, 'after stateSplit' + ! define mask for energy + case(nrgSplit) + select case(iDomainSplit) + case(vegSplit) + if(ixNrgCanair(1)/=integerMissing) stateMask(ixNrgCanair) = .true. ! energy of the canopy air space + if(ixNrgCanopy(1)/=integerMissing) stateMask(ixNrgCanopy) = .true. ! energy of the vegetation canopy + stateMask(ixNrgLayer(1)) = .true. ! energy of the upper-most layer in the snow+soil domain + case(snowSplit); if(nSnow>1) stateMask(ixNrgLayer(2:nSnow)) = .true. ! NOTE: (2:) because the top layer in the snow+soil domain included in vegSplit + case(soilSplit); stateMask(ixNrgLayer(max(2,nSnow+1):nLayers)) = .true. ! NOTE: max(2,nSnow+1) gives second layer unless more than 2 snow layers + case(aquiferSplit) ! do nothing: no energy state variable for the aquifer domain + case default; err=20; message=trim(message)//'unable to identify model sub-domain'; return + end select - end do solution ! trial with the full layer solution then the split layer solution + ! define mask for water + case(massSplit) + select case(iDomainSplit) + case(vegSplit); if(ixHydCanopy(1)/=integerMissing) stateMask(ixHydCanopy) = .true. ! hydrology of the vegetation canopy + case(snowSplit); stateMask(ixHydLayer(1:nSnow)) = .true. ! snow hydrology + case(soilSplit); stateMask(ixHydLayer(nSnow+1:nLayers)) = .true. ! soil hydrology + case(aquiferSplit); if(ixWatAquifer(1)/=integerMissing) stateMask(ixWatAquifer) = .true. ! aquifer storage + case default; err=20; message=trim(message)//'unable to identify model sub-domain'; return + end select - !print*, 'after solution loop' + ! check + case default; err=20; message=trim(message)//'unable to identify the state type'; return + end select ! (split based on state type) - ! ***** trial with a given solution method... - ! ******************************************************************************************************************************* - ! ******************************************************************************************************************************* - ! ******************************************************************************************************************************* + ! check + case default; err=20; message=trim(message)//'unable to identify the switch between full domains and sub domains'; return + end select ! (switch between full domains and sub domains) - end do domainSplit ! domain type splitting loop + ! check + case default; err=20; message=trim(message)//'unable to identify coupling method'; return + end select ! (selecting solution method) - !print*, 'ixStateThenDomain = ', ixStateThenDomain - !print*, 'after domain split loop' + ! identify scalar solutions + if(ixSolution==scalar)then - end do stateThenDomain ! switch between the state and the domain + ! get the subset of indices + call indxSubset(ixSubset, ixAllState, stateMask, err, cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - !print*, 'after stateThenDomain switch' + ! get the mask + stateMask(:) = .false. + stateMask( ixSubset(iStateSplit) ) = .true. - ! ----- - ! * reset state variables for the mass split... - ! --------------------------------------------- + ! check + if(count(stateMask)/=1)then + message=trim(message)//'expect size=1 (scalar)' + err=20; return + endif - ! modify the state type names associated with the state vector - if(ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit)then - if(computeVegFlux)then - where(ixStateType(ixHydCanopy)==iname_liqCanopy) ixStateType(ixHydCanopy)=iname_watCanopy endif - where(ixStateType(ixHydLayer) ==iname_liqLayer) ixStateType(ixHydLayer) =iname_watLayer - where(ixStateType(ixHydLayer) ==iname_lmpLayer) ixStateType(ixHydLayer) =iname_matLayer - endif ! if modifying state variables for the mass split - - end do stateTypeSplit ! state type splitting loop - - ! check - !if(ixCoupling/=fullyCoupled)then - ! print*, 'PAUSE: end of splitting loop'; read(*,*) - !endif - - ! ========================================================================================================================================== - ! ========================================================================================================================================== - - ! success = exit the coupling loop - if(ixCoupling==fullyCoupled .and. .not.failure) exit coupling - - end do coupling ! coupling method - - ! check that all state variables were updated - if(any(stateCheck==0))then - message=trim(message)//'some state variables were not updated!' - err=20; return - endif - - ! check that the desired fluxes were computed - do iVar=1,size(flux_meta) - if(neededFlux(iVar) .and. any(fluxCount%var(iVar)%dat==0))then - print*, 'fluxCount%var(iVar)%dat = ', fluxCount%var(iVar)%dat - message=trim(message)//'flux '//trim(flux_meta(iVar)%varname)//' was not computed' - err=20; return - endif - end do - - ! use step halving if unable to complete the fully coupled solution in one substep - if(ixCoupling/=fullyCoupled .or. nSubsteps>1) dtMultiplier=0.5_rkind - - ! compute the melt in each snow and soil layer - if(nSnow>0)then - diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(1:nSnow) = -( mLayerVolFracIce(1:nSnow) - mLayerVolFracIceInit(1:nSnow) ) * iden_ice - diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(nSnow+1:nLayers) = -(mLayerVolFracIce(nSnow+1:nLayers) - mLayerVolFracIceInit(nSnow+1:nLayers))*iden_water - endif - - ! end associate statements - end associate globalVars - - end subroutine opSplittin - - - ! ********************************************************************************************************** - ! private subroutine stateFilter: get a mask for the desired state variables - ! ********************************************************************************************************** - subroutine stateFilter(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit,& - indx_data,stateMask,nSubset,err,message) - - USE indexState_module,only:indxSubset ! get state indices - implicit none - ! input - integer(i4b),intent(in) :: ixCoupling ! index of coupling method (1,2) - integer(i4b),intent(in) :: ixSolution ! index of solution method (1,2) - integer(i4b),intent(in) :: ixStateThenDomain ! switch between full domain and sub domains - integer(i4b),intent(in) :: iStateTypeSplit ! index of the state type split - integer(i4b),intent(in) :: iDomainSplit ! index of the domain split - integer(i4b),intent(in) :: iStateSplit ! index of the layer split - type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU - ! output - logical(lgt),intent(out) :: stateMask(:) ! mask defining desired state variables - integer(i4b),intent(out) :: nSubset ! number of selected state variables for a given split - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! local - integer(i4b),allocatable :: ixSubset(:) ! list of indices in the state subset - character(len=256) :: cmessage ! error message - ! -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ! data structures - associate(& - ! indices of model state variables - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) - ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain - ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain - ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain - ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain - ixWatAquifer => indx_data%var(iLookINDEX%ixWatAquifer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for water storage in the aquifer - ixAllState => indx_data%var(iLookINDEX%ixAllState)%dat ,& ! intent(in): [i4b(:)] list of indices for all model state variables (1,2,3,...nState) - ! number of layers - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in): [i4b] total number of layers - ) ! data structures - ! -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='stateFilter/' - - ! identify splitting option - select case(ixCoupling) - - ! ----- - ! - fully coupled... - ! ------------------ - - ! use all state variables - case(fullyCoupled); stateMask(:) = .true. - - ! ----- - ! - splitting by state type... - ! ---------------------------- - - ! initial split by state type - case(stateTypeSplit) - - ! switch between full domain and sub domains - select case(ixStateThenDomain) - - ! split into energy and mass - case(fullDomain) - select case(iStateTypeSplit) - case(nrgSplit); stateMask = (ixStateType==iname_nrgCanair .or. ixStateType==iname_nrgCanopy .or. ixStateType==iname_nrgLayer) - case(massSplit); stateMask = (ixStateType==iname_liqCanopy .or. ixStateType==iname_liqLayer .or. ixStateType==iname_lmpLayer .or. ixStateType==iname_watAquifer) - case default; err=20; message=trim(message)//'unable to identify split based on state type'; return - end select - ! split into vegetation, snow, and soil - case(subDomain) - - ! define state mask - stateMask=.false. ! (initialize state mask) - select case(iStateTypeSplit) - - ! define mask for energy - case(nrgSplit) - select case(iDomainSplit) - case(vegSplit) - if(ixNrgCanair(1)/=integerMissing) stateMask(ixNrgCanair) = .true. ! energy of the canopy air space - if(ixNrgCanopy(1)/=integerMissing) stateMask(ixNrgCanopy) = .true. ! energy of the vegetation canopy - stateMask(ixNrgLayer(1)) = .true. ! energy of the upper-most layer in the snow+soil domain - case(snowSplit); if(nSnow>1) stateMask(ixNrgLayer(2:nSnow)) = .true. ! NOTE: (2:) because the top layer in the snow+soil domain included in vegSplit - case(soilSplit); stateMask(ixNrgLayer(max(2,nSnow+1):nLayers)) = .true. ! NOTE: max(2,nSnow+1) gives second layer unless more than 2 snow layers - case(aquiferSplit) ! do nothing: no energy state variable for the aquifer domain - case default; err=20; message=trim(message)//'unable to identify model sub-domain'; return - end select - - ! define mask for water - case(massSplit) - select case(iDomainSplit) - case(vegSplit); if(ixHydCanopy(1)/=integerMissing) stateMask(ixHydCanopy) = .true. ! hydrology of the vegetation canopy - case(snowSplit); stateMask(ixHydLayer(1:nSnow)) = .true. ! snow hydrology - case(soilSplit); stateMask(ixHydLayer(nSnow+1:nLayers)) = .true. ! soil hydrology - case(aquiferSplit); if(ixWatAquifer(1)/=integerMissing) stateMask(ixWatAquifer) = .true. ! aquifer storage - case default; err=20; message=trim(message)//'unable to identify model sub-domain'; return - end select - - ! check - case default; err=20; message=trim(message)//'unable to identify the state type'; return - end select ! (split based on state type) - - ! check - case default; err=20; message=trim(message)//'unable to identify the switch between full domains and sub domains'; return - end select ! (switch between full domains and sub domains) - - ! check - case default; err=20; message=trim(message)//'unable to identify coupling method'; return - end select ! (selecting solution method) - - !print*, 'stateMask = ', stateMask - - ! identify scalar solutions - if(ixSolution==scalar)then - - ! get the subset of indices - call indxSubset(ixSubset, ixAllState, stateMask, err, cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! get the mask - stateMask(:) = .false. - stateMask( ixSubset(iStateSplit) ) = .true. - - ! check - if(count(stateMask)/=1)then - message=trim(message)//'expect size=1 (scalar)' - err=20; return - endif - - endif - - ! get the number of selected state variables - nSubset = count(stateMask) - - ! end associations - end associate - - end subroutine stateFilter + ! get the number of selected state variables + nSubset = count(stateMask) + + ! end associations + end associate + +end subroutine stateFilter end module opSplittin_module From 4371189d3183326c31d4d5bd4d0f2319062c9781 Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 26 Sep 2022 20:14:17 +0000 Subject: [PATCH 0349/1472] cleaned up opSplittin and varSubstepSundials --- build/source/engine/opSplittin.f90 | 8 +- build/source/engine/varSubstepSundials.f90 | 1819 ++++++++++---------- 2 files changed, 907 insertions(+), 920 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 9fbaa4255..38c6b72d8 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -149,10 +149,10 @@ module opSplittin_module integer(i4b),parameter :: nDomains=4 ! number of domains (vegetation, snow, soil, and aquifer) ! control parameters -real(rkind),parameter :: valueMissing=-9999._rkind ! missing value -real(rkind),parameter :: verySmall=1.e-12_rkind ! a very small number (used to check consistency) -real(rkind),parameter :: veryBig=1.e+20_rkind ! a very big number -real(rkind),parameter :: dx = 1.e-8_rkind ! finite difference increment +real(rkind),parameter :: valueMissing=-9999._rkind ! missing value +real(rkind),parameter :: verySmall=1.e-12_rkind ! a very small number (used to check consistency) +real(rkind),parameter :: veryBig=1.e+20_rkind ! a very big number +real(rkind),parameter :: dx = 1.e-8_rkind ! finite difference increment contains diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index 9a60934a2..1d073403c 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -84,459 +84,447 @@ module varSubstepSundials_module contains - ! ********************************************************************************************************** - ! public subroutine varSubstepSundials: run the model for a collection of substeps for a given state subset - ! ********************************************************************************************************** - subroutine varSubstepSundials(& - ! input: model control - dt, & ! intent(in) : time step (s) - dtInit, & ! intent(in) : initial time step (seconds) - dt_min, & ! intent(in) : minimum time step (seconds) - nState, & ! intent(in) : total number of state variables - doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature - firstSubStep, & ! intent(in) : flag to denote first sub-step - firstFluxCall, & ! intent(inout) : flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in) : flag to denote if computing energy flux over vegetation - scalarSolution, & ! intent(in) : flag to denote implementing the scalar solution - iStateSplit, & ! intent(in) : index of the state in the splitting operation - fluxMask, & ! intent(in) : mask for the fluxes used in this given state subset - fluxCount, & ! intent(inout) : number of times that fluxes are updated (should equal nSubsteps) - ! input/output: data structures - model_decisions, & ! intent(in) : model decisions - lookup_data, & ! intent(in) : lookup tables - type_data, & ! intent(in) : type of vegetation and soil - attr_data, & ! intent(in) : spatial attributes - forc_data, & ! intent(in) : model forcing data - mpar_data, & ! intent(in) : model parameters - indx_data, & ! intent(inout) : index data - prog_data, & ! intent(inout) : model prognostic variables for a local HRU - diag_data, & ! intent(inout) : model diagnostic variables for a local HRU - flux_data, & ! intent(inout) : model fluxes for a local HRU - deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables - bvar_data, & ! intent(in) : model variables for the local basin - ! output: model control - ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) - dtMultiplier, & ! intent(out) : substep multiplier (-) - nSubsteps, & ! intent(out) : number of substeps taken for a given split - failedMinimumStep, & ! intent(out) : flag to denote success of substepping for a given split - reduceCoupledStep, & ! intent(out) : flag to denote need to reduce the length of the coupled step - tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt - dt_out, & ! intent(out) - err,message) ! intent(out) : error code and error message - ! --------------------------------------------------------------------------------------- - ! structure allocations - USE allocspace_module,only:allocLocal ! allocate local data structures - ! simulation of fluxes and residuals given a trial state vector - USE systemSolv_module,only:systemSolv ! solve the system of equations for one time step - USE getVectorz_module,only:popStateVec ! populate the state vector - USE getVectorz_module,only:varExtract ! extract variables from the state vector - USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables - ! identify name of variable type (for error message) - USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages - USE systemSolvSundials_module,only:systemSolvSundials - implicit none - ! --------------------------------------------------------------------------------------- - ! * dummy variables - ! --------------------------------------------------------------------------------------- - ! input: model control - real(rkind),intent(in) :: dt ! time step (seconds) - real(rkind),intent(in) :: dtInit ! initial time step (seconds) - real(rkind),intent(in) :: dt_min ! minimum time step (seconds) - integer(i4b),intent(in) :: nState ! total number of state variables - logical(lgt),intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) - logical(lgt),intent(in) :: scalarSolution ! flag to denote implementing the scalar solution - integer(i4b),intent(in) :: iStateSplit ! index of the state in the splitting operation - type(var_flagVec),intent(in) :: fluxMask ! flags to denote if the flux is calculated in the given state subset - type(var_ilength),intent(inout) :: fluxCount ! number of times that the flux is updated (should equal nSubsteps) - ! input/output: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(zLookup),intent(in) :: lookup_data ! lookup tables - type(var_i),intent(in) :: type_data ! type of vegetation and soil - type(var_d),intent(in) :: attr_data ! spatial attributes - type(var_d),intent(in) :: forc_data ! model forcing data - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU - type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin - ! output: model control - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: dtMultiplier ! substep multiplier (-) - integer(i4b),intent(out) :: nSubsteps ! number of substeps taken for a given split - logical(lgt),intent(out) :: failedMinimumStep ! flag to denote success of substepping for a given split - logical(lgt),intent(out) :: reduceCoupledStep ! flag to denote need to reduce the length of the coupled step - logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt - real(qp),intent(out) :: dt_out - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! --------------------------------------------------------------------------------------- - ! * general local variables - ! --------------------------------------------------------------------------------------- - ! error control - character(LEN=256) :: cmessage ! error message of downwind routine - ! general local variables - integer(i4b) :: iVar ! index of variables in data structures - integer(i4b) :: iSoil ! index of soil layers - integer(i4b) :: ixLayer ! index in a given domain - integer(i4b), dimension(1) :: ixMin,ixMax ! bounds of a given flux vector - ! time stepping - real(rkind) :: dtSum ! sum of time from successful steps (seconds) - real(rkind) :: dt_wght ! weight given to a given flux calculation - real(rkind) :: dtSubstep ! length of a substep (s) - ! adaptive sub-stepping for the explicit solution - logical(lgt) :: failedSubstep ! flag to denote success of substepping for a given split - real(rkind),parameter :: safety=0.85_rkind ! safety factor in adaptive sub-stepping - real(rkind),parameter :: reduceMin=0.1_rkind ! mimimum factor that time step is reduced - real(rkind),parameter :: increaseMax=4.0_rkind ! maximum factor that time step is increased - ! adaptive sub-stepping for the implicit solution - integer(i4b),parameter :: n_inc=5 ! minimum number of iterations to increase time step - integer(i4b),parameter :: n_dec=15 ! maximum number of iterations to decrease time step - real(rkind),parameter :: F_inc = 1.25_rkind ! factor used to increase time step - real(rkind),parameter :: F_dec = 0.90_rkind ! factor used to decrease time step - ! state and flux vectors - real(rkind) :: untappedMelt(nState) ! un-tapped melt energy (J m-3 s-1) - real(rkind) :: stateVecInit(nState) ! initial state vector (mixed units) - real(rkind) :: stateVecTrial(nState) ! trial state vector (mixed units) - real(rkind) :: stateVecPrime(nState) ! trial state vector (mixed units) - type(var_dlength) :: flux_temp ! temporary model fluxes - ! flags - logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt) :: checkMassBalance ! flag to check the mass balance - logical(lgt) :: checkNrgBalance - logical(lgt) :: waterBalanceError ! flag to denote that there is a water balance error - logical(lgt) :: nrgFluxModified ! flag to denote that the energy fluxes were modified - ! energy fluxes - real(rkind) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) - real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) - real(rkind) :: sumSoilCompress - real(rkind),allocatable :: sumLayerCompress(:) - ! --------------------------------------------------------------------------------------- - ! point to variables in the data structures - ! --------------------------------------------------------------------------------------- - globalVars: associate(& - ! number of layers - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! mapping between state vectors and control volumes - ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat ,& ! intent(in): [i4b(:)] list of indices for all active layers (inactive=integerMissing) - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) - ! model state variables (vegetation canopy) - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout): [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout): [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout): [dp] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat & ! intent(inout): [dp(:)] matric potential of liquid water (m) - ) ! end association with variables in the data structures - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - ! Procedure starts here - - ! initialize error control - err=0; message='varSubstepSundials/' - - ! initialize flag for the success of the substepping - failedMinimumStep=.false. - - ! initialize the length of the substep - dtSubstep = dtInit - - ! initalize flag for checking if energy fluxes had been modified - nrgFluxModified = .false. - - ! allocate space for the temporary model flux structure - call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! initialize the model fluxes (some model fluxes are not computed in the iterations) - do iVar=1,size(flux_data%var) - flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) - end do - - ! initialize the total energy fluxes (modified in updateProgSundials) - sumCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap = 0._rkind ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy = 0._rkind ! sensible heat flux from the canopy to the canopy air space (W m-2) - sumSoilCompress = 0._rkind ! total soil compression - allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._rkind ! soil compression by layer - - ! define the first flux call in a splitting operation - firstSplitOper = (.not.scalarSolution .or. iStateSplit==1) - - ! initialize subStep - dtSum = 0._rkind ! keep track of the portion of the time step that is completed - nSubsteps = 0 - - ! loop through substeps - ! NOTE: continuous do statement with exit clause - substeps: do - - ! initialize error control - err=0; message='varSubstepSundials/' - - !write(*,'(a,1x,3(f13.2,1x))') '***** new subStep: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt - !print*, 'scalarCanopyIce = ', prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) - !print*, 'scalarCanopyTemp = ', prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) - - ! ----- - ! * populate state vectors... - ! --------------------------- - - ! initialize state vectors - call popStateVec(& - ! input - nState, & ! intent(in): number of desired state variables - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output - stateVecInit, & ! intent(out): initial model state vector (mixed units) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - ! ----- - ! * iterative solution... - ! ----------------------- - ! solve the system of equations for a given state subset - call systemSolvSundials(& - ! input: model control - dtSubstep, & ! intent(in): time step (s) - nState, & ! intent(in): total number of state variables - firstSubStep, & ! intent(in): flag to denote first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation - scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution - ! input/output: data structures - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): index data - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_temp, & ! intent(inout): model fluxes for a local HRU - bvar_data, & ! intent(in): model variables for the local basin - model_decisions, & ! intent(in): model decisions - stateVecInit, & ! intent(in): initial state vector - ! output: model control - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - stateVecTrial, & ! intent(out): updated state vector - stateVecPrime, & ! intent(out): updated state vector - reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step - tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt - dt_out, & ! intent(out): time step (s) - err,cmessage) ! intent(out): error code and error message - - if(err/=0)then - message=trim(message)//trim(cmessage) - if(err>0) return - endif - - ! set untapped melt energy to zero - untappedMelt(:) = 0._rkind - - ! if too much melt or need to reduce length of the coupled step then return - ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry - if(tooMuchMelt .or. reduceCoupledStep) return +! ********************************************************************************************************** +! public subroutine varSubstepSundials: run the model for a collection of substeps for a given state subset +! ********************************************************************************************************** +subroutine varSubstepSundials(& + ! input: model control + dt, & ! intent(in) : time step (s) + dtInit, & ! intent(in) : initial time step (seconds) + dt_min, & ! intent(in) : minimum time step (seconds) + nState, & ! intent(in) : total number of state variables + doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature + firstSubStep, & ! intent(in) : flag to denote first sub-step + firstFluxCall, & ! intent(inout) : flag to indicate if we are processing the first flux call + computeVegFlux, & ! intent(in) : flag to denote if computing energy flux over vegetation + scalarSolution, & ! intent(in) : flag to denote implementing the scalar solution + iStateSplit, & ! intent(in) : index of the state in the splitting operation + fluxMask, & ! intent(in) : mask for the fluxes used in this given state subset + fluxCount, & ! intent(inout) : number of times that fluxes are updated (should equal nSubsteps) + ! input/output: data structures + model_decisions, & ! intent(in) : model decisions + lookup_data, & ! intent(in) : lookup tables + type_data, & ! intent(in) : type of vegetation and soil + attr_data, & ! intent(in) : spatial attributes + forc_data, & ! intent(in) : model forcing data + mpar_data, & ! intent(in) : model parameters + indx_data, & ! intent(inout) : index data + prog_data, & ! intent(inout) : model prognostic variables for a local HRU + diag_data, & ! intent(inout) : model diagnostic variables for a local HRU + flux_data, & ! intent(inout) : model fluxes for a local HRU + deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables + bvar_data, & ! intent(in) : model variables for the local basin + ! output: model control + ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) + dtMultiplier, & ! intent(out) : substep multiplier (-) + nSubsteps, & ! intent(out) : number of substeps taken for a given split + failedMinimumStep, & ! intent(out) : flag to denote success of substepping for a given split + reduceCoupledStep, & ! intent(out) : flag to denote need to reduce the length of the coupled step + tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt + dt_out, & ! intent(out) + err,message) ! intent(out) : error code and error message + ! --------------------------------------------------------------------------------------- + ! structure allocations + USE allocspace_module,only:allocLocal ! allocate local data structures + ! simulation of fluxes and residuals given a trial state vector + USE systemSolv_module,only:systemSolv ! solve the system of equations for one time step + USE getVectorz_module,only:popStateVec ! populate the state vector + USE getVectorz_module,only:varExtract ! extract variables from the state vector + USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables + ! identify name of variable type (for error message) + USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages + USE systemSolvSundials_module,only:systemSolvSundials + implicit none + ! --------------------------------------------------------------------------------------- + ! * dummy variables + ! --------------------------------------------------------------------------------------- + ! input: model control + real(rkind),intent(in) :: dt ! time step (seconds) + real(rkind),intent(in) :: dtInit ! initial time step (seconds) + real(rkind),intent(in) :: dt_min ! minimum time step (seconds) + integer(i4b),intent(in) :: nState ! total number of state variables + logical(lgt),intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) + logical(lgt),intent(in) :: scalarSolution ! flag to denote implementing the scalar solution + integer(i4b),intent(in) :: iStateSplit ! index of the state in the splitting operation + type(var_flagVec),intent(in) :: fluxMask ! flags to denote if the flux is calculated in the given state subset + type(var_ilength),intent(inout) :: fluxCount ! number of times that the flux is updated (should equal nSubsteps) + ! input/output: data structures + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_i),intent(in) :: type_data ! type of vegetation and soil + type(var_d),intent(in) :: attr_data ! spatial attributes + type(var_d),intent(in) :: forc_data ! model forcing data + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU + type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin + ! output: model control + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + real(rkind),intent(out) :: dtMultiplier ! substep multiplier (-) + integer(i4b),intent(out) :: nSubsteps ! number of substeps taken for a given split + logical(lgt),intent(out) :: failedMinimumStep ! flag to denote success of substepping for a given split + logical(lgt),intent(out) :: reduceCoupledStep ! flag to denote need to reduce the length of the coupled step + logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt + real(qp),intent(out) :: dt_out + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! --------------------------------------------------------------------------------------- + ! * general local variables + ! --------------------------------------------------------------------------------------- + ! error control + character(LEN=256) :: cmessage ! error message of downwind routine + ! general local variables + integer(i4b) :: iVar ! index of variables in data structures + integer(i4b) :: iSoil ! index of soil layers + integer(i4b) :: ixLayer ! index in a given domain + integer(i4b), dimension(1) :: ixMin,ixMax ! bounds of a given flux vector + ! time stepping + real(rkind) :: dtSum ! sum of time from successful steps (seconds) + real(rkind) :: dt_wght ! weight given to a given flux calculation + real(rkind) :: dtSubstep ! length of a substep (s) + ! adaptive sub-stepping for the explicit solution + logical(lgt) :: failedSubstep ! flag to denote success of substepping for a given split + real(rkind),parameter :: safety=0.85_rkind ! safety factor in adaptive sub-stepping + real(rkind),parameter :: reduceMin=0.1_rkind ! mimimum factor that time step is reduced + real(rkind),parameter :: increaseMax=4.0_rkind ! maximum factor that time step is increased + ! adaptive sub-stepping for the implicit solution + integer(i4b),parameter :: n_inc=5 ! minimum number of iterations to increase time step + integer(i4b),parameter :: n_dec=15 ! maximum number of iterations to decrease time step + real(rkind),parameter :: F_inc = 1.25_rkind ! factor used to increase time step + real(rkind),parameter :: F_dec = 0.90_rkind ! factor used to decrease time step + ! state and flux vectors + real(rkind) :: untappedMelt(nState) ! un-tapped melt energy (J m-3 s-1) + real(rkind) :: stateVecInit(nState) ! initial state vector (mixed units) + real(rkind) :: stateVecTrial(nState) ! trial state vector (mixed units) + real(rkind) :: stateVecPrime(nState) ! trial state vector (mixed units) + type(var_dlength) :: flux_temp ! temporary model fluxes + ! flags + logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt) :: checkMassBalance ! flag to check the mass balance + logical(lgt) :: checkNrgBalance + logical(lgt) :: waterBalanceError ! flag to denote that there is a water balance error + logical(lgt) :: nrgFluxModified ! flag to denote that the energy fluxes were modified + ! energy fluxes + real(rkind) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) + real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) + real(rkind) :: sumSoilCompress + real(rkind),allocatable :: sumLayerCompress(:) + ! --------------------------------------------------------------------------------------- + ! point to variables in the data structures + ! --------------------------------------------------------------------------------------- + globalVars: associate(& + ! number of layers + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! mapping between state vectors and control volumes + ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat ,& ! intent(in): [i4b(:)] list of indices for all active layers (inactive=integerMissing) + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) + ! model state variables (vegetation canopy) + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout): [dp] temperature of the vegetation canopy (K) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout): [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout): [dp] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout): [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variables (snow and soil domains) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat & ! intent(inout): [dp(:)] matric potential of liquid water (m) + ) ! end association with variables in the data structures + ! ********************************************************************************************************************************************************* + ! ********************************************************************************************************************************************************* + ! Procedure starts here + + ! initialize error control + err=0; message='varSubstepSundials/' + + ! initialize flag for the success of the substepping + failedMinimumStep=.false. + + ! initialize the length of the substep + dtSubstep = dtInit + + ! initalize flag for checking if energy fluxes had been modified + nrgFluxModified = .false. + + ! allocate space for the temporary model flux structure + call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! initialize the model fluxes (some model fluxes are not computed in the iterations) + do iVar=1,size(flux_data%var) + flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + end do + + ! initialize the total energy fluxes (modified in updateProgSundials) + sumCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = 0._rkind ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = 0._rkind ! sensible heat flux from the canopy to the canopy air space (W m-2) + sumSoilCompress = 0._rkind ! total soil compression + allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._rkind ! soil compression by layer + + ! define the first flux call in a splitting operation + firstSplitOper = (.not.scalarSolution .or. iStateSplit==1) + + ! initialize subStep + dtSum = 0._rkind ! keep track of the portion of the time step that is completed + nSubsteps = 0 + + ! loop through substeps + ! NOTE: continuous do statement with exit clause + substeps: do + + ! initialize error control + err=0; message='varSubstepSundials/' + + ! ----- + ! * populate state vectors... + ! --------------------------- + + ! initialize state vectors + call popStateVec(& + ! input + nState, & ! intent(in): number of desired state variables + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output + stateVecInit, & ! intent(out): initial model state vector (mixed units) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + + ! ----- + ! * iterative solution... + ! ----------------------- + ! solve the system of equations for a given state subset + call systemSolvSundials(& + ! input: model control + dtSubstep, & ! intent(in): time step (s) + nState, & ! intent(in): total number of state variables + firstSubStep, & ! intent(in): flag to denote first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution + ! input/output: data structures + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): index data + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_temp, & ! intent(inout): model fluxes for a local HRU + bvar_data, & ! intent(in): model variables for the local basin + model_decisions, & ! intent(in): model decisions + stateVecInit, & ! intent(in): initial state vector + ! output: model control + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + stateVecTrial, & ! intent(out): updated state vector + stateVecPrime, & ! intent(out): updated state vector + reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step + tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt + dt_out, & ! intent(out): time step (s) + err,cmessage) ! intent(out): error code and error message + + if(err/=0)then + message=trim(message)//trim(cmessage) + if(err>0) return + endif - ! identify failure - failedSubstep = (err<0) + ! set untapped melt energy to zero + untappedMelt(:) = 0._rkind - ! reduce step based on failure - if(failedSubstep)then - err=0; message='varSubstepSundials/' ! recover from failed convergence - dtMultiplier = 0.5_rkind ! system failure: step halving - else + ! if too much melt or need to reduce length of the coupled step then return + ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry + if(tooMuchMelt .or. reduceCoupledStep) return - endif ! switch between failure and success + ! identify failure + failedSubstep = (err<0) - ! check if we failed the substep - if(failedSubstep)then + ! reduce step based on failure + if(failedSubstep)then + err=0; message='varSubstepSundials/' ! recover from failed convergence + dtMultiplier = 0.5_rkind ! system failure: step halving + else - ! check that the substep is greater than the minimum step - if(dtSubstep*dtMultiplier exit, and either (1) try another solution method; or (2) reduce coupled step - failedMinimumStep=.true. - exit subSteps + endif ! switch between failure and success - else ! step is still OK - dtSubstep = dtSubstep*dtMultiplier - cycle subSteps - endif ! if step is less than the minimum + ! check if we failed the substep + if(failedSubstep)then - endif ! if failed the substep + ! check that the substep is greater than the minimum step + if(dtSubstep*dtMultiplier exit, and either (1) try another solution method; or (2) reduce coupled step + failedMinimumStep=.true. + exit subSteps - ! ----- - ! * update model fluxes... - ! ------------------------ + else ! step is still OK + dtSubstep = dtSubstep*dtMultiplier + cycle subSteps + endif ! if step is less than the minimum - ! NOTE: if we get to here then we are accepting the step + endif ! if failed the substep - ! NOTE: we get to here if iterations are successful - if(err/=0)then - message=trim(message)//'expect err=0 if updating fluxes' - return - endif + ! ----- + ! * update model fluxes... + ! ------------------------ - ! identify the need to check the mass balance - checkMassBalance = .true. ! (.not.scalarSolution) - checkNrgBalance = .true. - - ! update prognostic variables - call updateProgSundials(dt_out,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control - lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures - waterBalanceError,nrgFluxModified,err,cmessage) ! output: flags and error control - if(err/=0)then - message=trim(message)//trim(cmessage) - if(err>0) return - endif + ! NOTE: if we get to here then we are accepting the step - ! if water balance error then reduce the length of the coupled step - if(waterBalanceError)then - message=trim(message)//'water balance error' - reduceCoupledStep=.true. - err=-20; return - endif + ! NOTE: we get to here if iterations are successful + if(err/=0)then + message=trim(message)//'expect err=0 if updating fluxes' + return + endif - if(globalPrintFlag)& - print*, trim(cmessage)//': dt = ', dtSubstep - - ! recover from errors in prognostic update - if(err<0)then - - ! modify step - err=0 ! error recovery - dtSubstep = dtSubstep/2._rkind - - ! check minimum: fail minimum step if there is an error in the update - if(dtSubstep0) then - ! scalar compression - if(.not.scalarSolution .or. iStateSplit==nSoil)& - sumSoilCompress = sumSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression - ! vector compression - do iSoil=1,nSoil - if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& - sumLayerCompress(iSoil) = sumLayerCompress(iSoil) + diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) ! soil compression in layers - end do - endif + ! identify the need to check the mass balance + checkMassBalance = .true. ! (.not.scalarSolution) + checkNrgBalance = .true. + + ! update prognostic variables + call updateProgSundials(dt_out,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control + lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures + waterBalanceError,nrgFluxModified,err,cmessage) ! output: flags and error control + if(err/=0)then + message=trim(message)//trim(cmessage) + if(err>0) return + endif - ! print progress - if(globalPrintFlag)& - write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt - - ! increment fluxes - dt_wght = 1._qp !dt_out/dt ! (define weight applied to each splitting operation) - do iVar=1,size(flux_meta) - if(count(fluxMask%var(iVar)%dat)>0) then - - !print*, flux_meta(iVar)%varname, fluxMask%var(iVar)%dat - - ! ** no domain splitting - if(count(ixLayerActive/=integerMissing)==nLayers)then - flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght - fluxCount%var(iVar)%dat(:) = fluxCount%var(iVar)%dat(:) + 1 - - ! ** domain splitting - else - ixMin=lbound(flux_data%var(iVar)%dat) - ixMax=ubound(flux_data%var(iVar)%dat) - do ixLayer=ixMin(1),ixMax(1) - if(fluxMask%var(iVar)%dat(ixLayer)) then - flux_data%var(iVar)%dat(ixLayer) = flux_data%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght - fluxCount%var(iVar)%dat(ixLayer) = fluxCount%var(iVar)%dat(ixLayer) + 1 + ! if water balance error then reduce the length of the coupled step + if(waterBalanceError)then + message=trim(message)//'water balance error' + reduceCoupledStep=.true. + err=-20; return endif - end do - endif ! (domain splitting) - endif ! (if the flux is desired) - end do ! (loop through fluxes) + if(globalPrintFlag)& + print*, trim(cmessage)//': dt = ', dtSubstep - ! ------------------------------------------------------ - ! ------------------------------------------------------ + ! recover from errors in prognostic update + if(err<0)then - ! increment the number of substeps - nSubsteps = nSubsteps+1 + ! modify step + err=0 ! error recovery + dtSubstep = dtSubstep/2._rkind - ! increment the sub-step legth - dtSum = dtSum + dtSubstep - !print*, 'dtSum, dtSubstep, dt, nSubsteps = ', dtSum, dtSubstep, dt, nSubsteps + ! check minimum: fail minimum step if there is an error in the update + if(dtSubstep= dt-verySmall)then - failedMinimumStep=.false. - exit subSteps - endif + endif ! if errors in prognostic update - ! adjust length of the sub-step (make sure that we don't exceed the step) - dtSubstep = min(dt - dtSum, max(dtSubstep*dtMultiplier, dt_min) ) + ! get the total energy fluxes (modified in updateProgSundials) + if(nrgFluxModified .or. indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then + sumCanopyEvaporation = sumCanopyEvaporation + dt_out*flux_temp%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dt_out*flux_temp%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = sumSenHeatCanopy + dt_out*flux_temp%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) + else + sumCanopyEvaporation = sumCanopyEvaporation + dt_out*flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dt_out*flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = sumSenHeatCanopy + dt_out*flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) + endif ! if energy fluxes were modified + + ! get the total soil compression + if (count(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat/=integerMissing)>0) then + ! scalar compression + if(.not.scalarSolution .or. iStateSplit==nSoil)& + sumSoilCompress = sumSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression + ! vector compression + do iSoil=1,nSoil + if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& + sumLayerCompress(iSoil) = sumLayerCompress(iSoil) + diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) ! soil compression in layers + end do + endif - end do substeps ! time steps for variable-dependent sub-stepping + ! print progress + if(globalPrintFlag)& + write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt + + ! increment fluxes + dt_wght = 1._qp !dt_out/dt ! (define weight applied to each splitting operation) + do iVar=1,size(flux_meta) + if(count(fluxMask%var(iVar)%dat)>0) then + + ! ** no domain splitting + if(count(ixLayerActive/=integerMissing)==nLayers)then + flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght + fluxCount%var(iVar)%dat(:) = fluxCount%var(iVar)%dat(:) + 1 + + ! ** domain splitting + else + ixMin=lbound(flux_data%var(iVar)%dat) + ixMax=ubound(flux_data%var(iVar)%dat) + do ixLayer=ixMin(1),ixMax(1) + if(fluxMask%var(iVar)%dat(ixLayer)) then + flux_data%var(iVar)%dat(ixLayer) = flux_data%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght + fluxCount%var(iVar)%dat(ixLayer) = fluxCount%var(iVar)%dat(ixLayer) + 1 + endif + end do + endif ! (domain splitting) + + endif ! (if the flux is desired) + end do ! (loop through fluxes) + + ! increment the number of substeps + nSubsteps = nSubsteps+1 + + ! increment the sub-step legth + dtSum = dtSum + dtSubstep + + ! check that we have completed the sub-step + if(dtSum >= dt-verySmall)then + failedMinimumStep=.false. + exit subSteps + endif - ! save the energy fluxes - flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt_out ! canopy evaporation/condensation (kg m-2 s-1) - flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt_out ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt_out ! sensible heat flux from the canopy to the canopy air space (W m-2) + ! adjust length of the sub-step (make sure that we don't exceed the step) + dtSubstep = min(dt - dtSum, max(dtSubstep*dtMultiplier, dt_min) ) - ! save the soil compression diagnostics - diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress - do iSoil=1,nSoil - if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& - diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) = sumLayerCompress(iSoil) - end do - deallocate(sumLayerCompress) + end do substeps ! time steps for variable-dependent sub-stepping - ! end associate statements - end associate globalVars + ! save the energy fluxes + flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt_out ! canopy evaporation/condensation (kg m-2 s-1) + flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt_out ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt_out ! sensible heat flux from the canopy to the canopy air space (W m-2) - ! update error codes - if(failedMinimumStep)then - err=-20 ! negative = recoverable error - message=trim(message)//'failed minimum step' - endif + ! save the soil compression diagnostics + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress + do iSoil=1,nSoil + if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& + diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) = sumLayerCompress(iSoil) + end do + deallocate(sumLayerCompress) + ! end associate statements + end associate globalVars - end subroutine varSubstepSundials + ! update error codes + if(failedMinimumStep)then + err=-20 ! negative = recoverable error + message=trim(message)//'failed minimum step' + endif +end subroutine varSubstepSundials ! ********************************************************************************************************** @@ -581,523 +569,522 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux integer(i4b) :: ixSubset ! index within the state subset integer(i4b) :: ixFullVector ! index within full state vector integer(i4b) :: ixControlIndex ! index within a given domain - real(rkind) :: volMelt ! volumetric melt (kg m-3) - real(rkind),parameter :: verySmall=epsilon(1._rkind)*2._rkind ! a very small number (deal with precision issues) + real(rkind) :: volMelt ! volumetric melt (kg m-3) + real(rkind),parameter :: verySmall=epsilon(1._rkind)*2._rkind ! a very small number (deal with precision issues) ! mass balance - real(rkind) :: canopyBalance0,canopyBalance1 ! canopy storage at start/end of time step - real(rkind) :: soilBalance0,soilBalance1 ! soil storage at start/end of time step - real(rkind) :: vertFlux ! change in storage due to vertical fluxes - real(rkind) :: tranSink,baseSink,compSink ! change in storage due to sink terms - real(rkind) :: liqError ! water balance error - real(rkind) :: fluxNet ! net water fluxes (kg m-2 s-1) - real(rkind) :: superflousWat ! superflous water used for evaporation (kg m-2 s-1) - real(rkind) :: superflousNrg ! superflous energy that cannot be used for evaporation (W m-2 [J m-2 s-1]) + real(rkind) :: canopyBalance0,canopyBalance1 ! canopy storage at start/end of time step + real(rkind) :: soilBalance0,soilBalance1 ! soil storage at start/end of time step + real(rkind) :: vertFlux ! change in storage due to vertical fluxes + real(rkind) :: tranSink,baseSink,compSink ! change in storage due to sink terms + real(rkind) :: liqError ! water balance error + real(rkind) :: fluxNet ! net water fluxes (kg m-2 s-1) + real(rkind) :: superflousWat ! superflous water used for evaporation (kg m-2 s-1) + real(rkind) :: superflousNrg ! superflous energy that cannot be used for evaporation (W m-2 [J m-2 s-1]) character(LEN=256) :: cmessage ! error message of downwind routine ! trial state variables - real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial vector for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector for liquid water matric potential (m) - real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) + real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial vector for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector for liquid water matric potential (m) + real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) ! diagnostic variables - real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric fraction of ice (-) + real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric fraction of ice (-) ! derivative of state variables - real(rkind) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatPrime ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempPrime ! trial vector for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! trial vector for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! trial vector for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! trial vector for liquid water matric potential (m) - real(rkind) :: scalarAquiferStoragePrime ! trial value for storage of water in the aquifer (m) + real(rkind) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatPrime ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempPrime ! trial vector for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! trial vector for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! trial vector for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! trial vector for liquid water matric potential (m) + real(rkind) :: scalarAquiferStoragePrime ! trial value for storage of water in the aquifer (m) ! diagnostic variables - real(rkind) :: scalarCanopyLiqPrime ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector for volumetric fraction of ice (-) - real(rkind) :: scalarCanairEnthalpyTrial ! enthalpy of the canopy air space (J m-3) - real(rkind) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! enthalpy of snow + soil (J m-3) + real(rkind) :: scalarCanopyLiqPrime ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector for volumetric fraction of ice (-) + real(rkind) :: scalarCanairEnthalpyTrial ! enthalpy of the canopy air space (J m-3) + real(rkind) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! enthalpy of snow + soil (J m-3) ! ------------------------------------------------------------------------------------------------------------------- ! ------------------------------------------------------------------------------------------------------------------- ! point to flux variables in the data structure associate(& - ! get indices for mass balance - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in) : [i4b] index of canopy hydrology state variable (mass) - ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the soil domain - ! get indices for the un-tapped melt - ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in) : [i4b(:)] list of indices for all energy states - ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in) : [i4b(:)] indices defining the domain of the state (iname_veg, iname_snow, iname_soil) - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in) : [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in) : [i4b(:)] [state subset] list of indices of the full state vector in the state subset - ! water fluxes - scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in) : [dp] rainfall rate (kg m-2 s-1) - scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(in) : [dp] rain reaches ground without touching the canopy (kg m-2 s-1) - scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(in) : [dp] canopy evaporation/condensation (kg m-2 s-1) - scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(in) : [dp] canopy transpiration (kg m-2 s-1) - scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(in) : [dp] drainage liquid water from vegetation canopy (kg m-2 s-1) - iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(in) : [dp(0:)] vertical liquid water flux at soil layer interfaces (-) - iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(in) : - mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) - mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(in) : [dp(:)] transpiration loss from each soil layer (m s-1) - mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(in) : [dp(:)] baseflow from each soil layer (m s-1) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in) : [dp(:)] change in storage associated with compression of the soil matrix (-) - scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the vegetation canopy (kg m-2 s-1) - scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the snow surface (kg m-2 s-1) - ! energy fluxes - scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ,& ! intent(in) : [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ,& ! intent(in) : [dp] sensible heat flux from the canopy to the canopy air space (W m-2) - ! domain depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in) : [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in) : [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! model state variables (vegetation canopy) - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout) : [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout) : [dp] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout) : [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout) : [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout) : [dp(:)] matric head (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(inout) : [dp(:)] matric potential of liquid water (m) - ! enthalpy - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(inout): [dp(:)] enthalpy of the snow+soil layers (J m-3) - ! model state variables (aquifer) - scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(inout) : [dp(:)] storage of water in the aquifer (m) - ! error tolerance - absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1) & ! intent(in) : [dp] absolute convergence tolerance for vol frac liq water (-) - ) ! associating flux variables in the data structure - ! ------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='updateProgSundials/' - - ! initialize water balancmLayerVolFracWatTriale error - waterBalanceError=.false. - - ! get storage at the start of the step - canopyBalance0 = merge(scalarCanopyWat, realMissing, computeVegFlux) - soilBalance0 = sum( (mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) - - ! ----- - ! * update states... - ! ------------------ - - ! initialize to state variable from the last update - scalarCanairTempTrial = scalarCanairTemp - scalarCanopyTempTrial = scalarCanopyTemp - scalarCanopyWatTrial = scalarCanopyWat - scalarCanopyLiqTrial = scalarCanopyLiq - scalarCanopyIceTrial = scalarCanopyIce - mLayerTempTrial = mLayerTemp - mLayerVolFracWatTrial = mLayerVolFracWat - mLayerVolFracLiqTrial = mLayerVolFracLiq - mLayerVolFracIceTrial = mLayerVolFracIce - mLayerMatricHeadTrial = mLayerMatricHead ! total water matric potential - mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq ! liquid water matric potential - scalarAquiferStorageTrial = scalarAquiferStorage - - ! extract states from the state vector - call varExtract(& + ! get indices for mass balance + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in) : [i4b] index of canopy hydrology state variable (mass) + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the soil domain + ! get indices for the un-tapped melt + ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in) : [i4b(:)] list of indices for all energy states + ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in) : [i4b(:)] indices defining the domain of the state (iname_veg, iname_snow, iname_soil) + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in) : [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in) : [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ! water fluxes + scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in) : [dp] rainfall rate (kg m-2 s-1) + scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(in) : [dp] rain reaches ground without touching the canopy (kg m-2 s-1) + scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(in) : [dp] canopy evaporation/condensation (kg m-2 s-1) + scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(in) : [dp] canopy transpiration (kg m-2 s-1) + scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(in) : [dp] drainage liquid water from vegetation canopy (kg m-2 s-1) + iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(in) : [dp(0:)] vertical liquid water flux at soil layer interfaces (-) + iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(in) : + mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) + mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(in) : [dp(:)] transpiration loss from each soil layer (m s-1) + mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(in) : [dp(:)] baseflow from each soil layer (m s-1) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in) : [dp(:)] change in storage associated with compression of the soil matrix (-) + scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the vegetation canopy (kg m-2 s-1) + scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the snow surface (kg m-2 s-1) + ! energy fluxes + scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ,& ! intent(in) : [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ,& ! intent(in) : [dp] sensible heat flux from the canopy to the canopy air space (W m-2) + ! domain depth + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in) : [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in) : [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! model state variables (vegetation canopy) + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the vegetation canopy (K) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout) : [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout) : [dp] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout) : [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variables (snow and soil domains) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout) : [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout) : [dp(:)] matric head (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(inout) : [dp(:)] matric potential of liquid water (m) + ! enthalpy + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(inout): [dp(:)] enthalpy of the snow+soil layers (J m-3) + ! model state variables (aquifer) + scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(inout) : [dp(:)] storage of water in the aquifer (m) + ! error tolerance + absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1) & ! intent(in) : [dp] absolute convergence tolerance for vol frac liq water (-) + ) ! associating flux variables in the data structure + ! ------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='updateProgSundials/' + + ! initialize water balancmLayerVolFracWatTriale error + waterBalanceError=.false. + + ! get storage at the start of the step + canopyBalance0 = merge(scalarCanopyWat, realMissing, computeVegFlux) + soilBalance0 = sum( (mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) + + ! ----- + ! * update states... + ! ------------------ + + ! initialize to state variable from the last update + scalarCanairTempTrial = scalarCanairTemp + scalarCanopyTempTrial = scalarCanopyTemp + scalarCanopyWatTrial = scalarCanopyWat + scalarCanopyLiqTrial = scalarCanopyLiq + scalarCanopyIceTrial = scalarCanopyIce + mLayerTempTrial = mLayerTemp + mLayerVolFracWatTrial = mLayerVolFracWat + mLayerVolFracLiqTrial = mLayerVolFracLiq + mLayerVolFracIceTrial = mLayerVolFracIce + mLayerMatricHeadTrial = mLayerMatricHead ! total water matric potential + mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq ! liquid water matric potential + scalarAquiferStorageTrial = scalarAquiferStorage + + ! extract states from the state vector + call varExtract(& + ! input + stateVecTrial, & ! intent(in): model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) + mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) + ! output: variables for the aquifer + scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + call varExtract(& ! input - stateVecTrial, & ! intent(in): model state vector (mixed units) + stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) diag_data, & ! intent(in): model diagnostic variables for a local HRU prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + scalarCanairTempPrime, & ! intent(out): derivative of canopy air temperature (K) + scalarCanopyTempPrime, & ! intent(out): derivative of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(out): derivative of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(out): derivative of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) + mLayerTempPrime, & ! intent(out): derivative of layer temperature (K) + mLayerVolFracWatPrime, & ! intent(out): derivative of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(out): derivative of volumetric liquid water content (-) + mLayerMatricHeadPrime, & ! intent(out): derivative of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(out): derivative of liquid water matric potential (m) ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) + scalarAquiferStoragePrime,& ! intent(out): derivative of storage of water in the aquifer (m) ! output: error control err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - call varExtract(& - ! input - stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy - scalarCanairTempPrime, & ! intent(out): derivative of canopy air temperature (K) - scalarCanopyTempPrime, & ! intent(out): derivative of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(out): derivative of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(out): derivative of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempPrime, & ! intent(out): derivative of layer temperature (K) - mLayerVolFracWatPrime, & ! intent(out): derivative of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(out): derivative of volumetric liquid water content (-) - mLayerMatricHeadPrime, & ! intent(out): derivative of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(out): derivative of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStoragePrime,& ! intent(out): derivative of storage of water in the aquifer (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! update diagnostic variables - call updateVarsSundials(& - ! input - dt, & - .false., & ! intent(in): logical flag if computing Jacobian for Sundials solver - doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - mLayerVolFracWatTrial, & ! intent(in): use current vector for prev vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): use current vector for prev vector of total water matric potential (m) - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! - mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) - mLayerVolFracIcePrime, & ! - mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) + call updateVarsSundials(& + ! input + dt, & + .false., & ! intent(in): logical flag if computing Jacobian for Sundials solver + doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + mLayerVolFracWatTrial, & ! intent(in): use current vector for prev vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): use current vector for prev vector of total water matric potential (m) + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output: variables for the vegetation canopy + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempPrime, & ! + mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) + mLayerVolFracIcePrime, & ! + mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! ---- + ! * check energy balance + !------------------------ + ! NOTE: for now, we just compute enthalpy + if(checkNrgBalance)then + ! compute enthalpy at t_{n+1} + call t2enthalpy(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) + ! output: enthalpy + scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! ---- - ! * check energy balance - !------------------------ - ! NOTE: for now, we just compute enthalpy - if(checkNrgBalance)then - ! compute enthalpy at t_{n+1} - call t2enthalpy(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) - ! output: enthalpy - scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - endif - - ! ----- - ! * check mass balance... - ! ----------------------- - - ! NOTE: should not need to do this, since mass balance is checked in the solver - if(checkMassBalance)then - - ! check mass balance for the canopy - if(ixVegHyd/=integerMissing)then - - ! handle cases where fluxes empty the canopy - fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage - if(-fluxNet*dt > canopyBalance0)then - - ! --> first add water - canopyBalance1 = canopyBalance0 + (scalarRainfall - scalarThroughfallRain)*dt - - ! --> next, remove canopy evaporation -- put the unsatisfied evap into sensible heat - canopyBalance1 = canopyBalance1 + scalarCanopyEvaporation*dt - if(canopyBalance1 < 0._rkind)then - ! * get superfluous water and energy - superflousWat = -canopyBalance1/dt ! kg m-2 s-1 - superflousNrg = superflousWat*LH_vap ! W m-2 (J m-2 s-1) - ! * update fluxes and states - canopyBalance1 = 0._rkind - scalarCanopyEvaporation = scalarCanopyEvaporation + superflousWat - scalarLatHeatCanopyEvap = scalarLatHeatCanopyEvap + superflousNrg - scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg - endif - - ! --> next, remove canopy drainage - canopyBalance1 = canopyBalance1 - scalarCanopyLiqDrainage*dt - if(canopyBalance1 < 0._rkind)then - superflousWat = -canopyBalance1/dt ! kg m-2 s-1 - canopyBalance1 = 0._rkind - scalarCanopyLiqDrainage = scalarCanopyLiqDrainage + superflousWat - endif - - ! update the trial state - scalarCanopyWatTrial = canopyBalance1 - - ! set the modification flag - nrgFluxModified = .true. - - else - canopyBalance1 = canopyBalance0 + fluxNet*dt - nrgFluxModified = .false. - endif ! cases where fluxes empty the canopy - - ! check the mass balance - fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage - liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial - if(abs(liqError) > absConvTol_liquid*10._rkind*iden_water)then ! *10 because of precision issues - !write(*,'(a,1x,f20.10)') 'dt = ', dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - !write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 - !write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 - !write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt - !write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - !write(*,'(a,1x,f20.10)') 'liqError = ', liqError - waterBalanceError = .true. - return - endif ! if there is a water balance error - endif ! if veg canopy + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + endif + + ! ----- + ! * check mass balance... + ! ----------------------- + + ! NOTE: should not need to do this, since mass balance is checked in the solver + if(checkMassBalance)then + + ! check mass balance for the canopy + if(ixVegHyd/=integerMissing)then + + ! handle cases where fluxes empty the canopy + fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + if(-fluxNet*dt > canopyBalance0)then + + ! --> first add water + canopyBalance1 = canopyBalance0 + (scalarRainfall - scalarThroughfallRain)*dt + + ! --> next, remove canopy evaporation -- put the unsatisfied evap into sensible heat + canopyBalance1 = canopyBalance1 + scalarCanopyEvaporation*dt + if(canopyBalance1 < 0._rkind)then + ! * get superfluous water and energy + superflousWat = -canopyBalance1/dt ! kg m-2 s-1 + superflousNrg = superflousWat*LH_vap ! W m-2 (J m-2 s-1) + ! * update fluxes and states + canopyBalance1 = 0._rkind + scalarCanopyEvaporation = scalarCanopyEvaporation + superflousWat + scalarLatHeatCanopyEvap = scalarLatHeatCanopyEvap + superflousNrg + scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg + endif + + ! --> next, remove canopy drainage + canopyBalance1 = canopyBalance1 - scalarCanopyLiqDrainage*dt + if(canopyBalance1 < 0._rkind)then + superflousWat = -canopyBalance1/dt ! kg m-2 s-1 + canopyBalance1 = 0._rkind + scalarCanopyLiqDrainage = scalarCanopyLiqDrainage + superflousWat + endif + + ! update the trial state + scalarCanopyWatTrial = canopyBalance1 + + ! set the modification flag + nrgFluxModified = .true. + + else + canopyBalance1 = canopyBalance0 + fluxNet*dt + nrgFluxModified = .false. + endif ! cases where fluxes empty the canopy + + ! check the mass balance + fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial + if(abs(liqError) > absConvTol_liquid*10._rkind*iden_water)then ! *10 because of precision issues + write(*,'(a,1x,f20.10)') 'dt = ', dt + write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 + write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 + write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt + write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt + write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt + write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt + write(*,'(a,1x,f20.10)') 'liqError = ', liqError + waterBalanceError = .true. + return + endif ! if there is a water balance error + endif ! if veg canopy ! check mass balance for soil ! NOTE: fatal errors, though possible to recover using negative error codes - if(count(ixSoilOnlyHyd/=integerMissing)==nSoil)then - soilBalance1 = sum( (mLayerVolFracLiqTrial(nSnow+1:nLayers) + mLayerVolFracIceTrial(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) - vertFlux = -(iLayerLiqFluxSoil(nSoil) - iLayerLiqFluxSoil(0))*dt ! m s-1 --> m - tranSink = sum(mLayerTranspire)*dt ! m s-1 --> m - baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m - compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m - liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) - if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues - !write(*,'(a,1x,f20.10)') 'dt = ', dt - !write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 - !write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 - !write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux - !write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink - !write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink - !write(*,'(a,1x,f20.10)') 'compSink = ', compSink - !write(*,'(a,1x,f20.10)') 'liqError = ', liqError - !write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid - waterBalanceError = .true. - return - endif ! if there is a water balance error - endif ! if hydrology states exist in the soil domain - endif ! if checking the mass balance - - ! ----- - ! * remove untapped melt energy... - ! -------------------------------- - - ! only work with energy state variables - if(size(ixNrgOnly)>0)then ! energy state variables exist - - ! loop through energy state variables - do iState=1,size(ixNrgOnly) - - ! get index of the control volume within the domain - ixSubset = ixNrgOnly(iState) ! index within the state subset - ixFullVector = ixMapSubset2Full(ixSubset) ! index within full state vector - ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain - - ! compute volumetric melt (kg m-3) - volMelt = dt*untappedMelt(ixSubset)/LH_fus ! (kg m-3) - - ! update ice content - select case( ixDomainType(ixFullVector) ) - case(iname_cas); cycle ! do nothing, since there is no snow stored in the canopy air space - case(iname_veg); scalarCanopyIceTrial = scalarCanopyIceTrial - volMelt*canopyDepth ! (kg m-2) - case(iname_snow); mLayerVolFracIceTrial(ixControlIndex) = mLayerVolFracIceTrial(ixControlIndex) - volMelt/iden_ice ! (-) - case(iname_soil); mLayerVolFracIceTrial(ixControlIndex+nSnow) = mLayerVolFracIceTrial(ixControlIndex+nSnow) - volMelt/iden_water ! (-) - case default; err=20; message=trim(message)//'unable to identify domain type [remove untapped melt energy]'; return - end select - - ! update liquid water content - select case( ixDomainType(ixFullVector) ) - case(iname_cas); cycle ! do nothing, since there is no snow stored in the canopy air space - case(iname_veg); scalarCanopyLiqTrial = scalarCanopyLiqTrial + volMelt*canopyDepth ! (kg m-2) - case(iname_snow); mLayerVolFracLiqTrial(ixControlIndex) = mLayerVolFracLiqTrial(ixControlIndex) + volMelt/iden_water ! (-) - case(iname_soil); mLayerVolFracLiqTrial(ixControlIndex+nSnow) = mLayerVolFracLiqTrial(ixControlIndex+nSnow) + volMelt/iden_water ! (-) - case default; err=20; message=trim(message)//'unable to identify domain type [remove untapped melt energy]'; return - end select - - end do ! looping through energy variables - - ! ======================================================================================================== - - ! *** ice - - ! --> check if we removed too much water - if(scalarCanopyIceTrial < 0._rkind .or. any(mLayerVolFracIceTrial < 0._rkind) )then - - ! ** - ! canopy within numerical precision - if(scalarCanopyIceTrial < 0._rkind)then - - if(scalarCanopyIceTrial > -verySmall)then - scalarCanopyLiqTrial = scalarCanopyLiqTrial - scalarCanopyIceTrial - scalarCanopyIceTrial = 0._rkind - - ! encountered an inconsistency: spit the dummy - else - print*, 'dt = ', dt - print*, 'untappedMelt = ', untappedMelt - print*, 'untappedMelt*dt = ', untappedMelt*dt - print*, 'scalarCanopyiceTrial = ', scalarCanopyIceTrial - message=trim(message)//'melted more than the available water' - err=20; return - endif ! (inconsistency) - - endif ! if checking the canopy - ! ** - ! snow+soil within numerical precision - do iState=1,size(mLayerVolFracIceTrial) - - ! snow layer within numerical precision - if(mLayerVolFracIceTrial(iState) < 0._rkind)then - - if(mLayerVolFracIceTrial(iState) > -verySmall)then - mLayerVolFracLiqTrial(iState) = mLayerVolFracLiqTrial(iState) - mLayerVolFracIceTrial(iState) - mLayerVolFracIceTrial(iState) = 0._rkind - - ! encountered an inconsistency: spit the dummy - else - print*, 'dt = ', dt - print*, 'untappedMelt = ', untappedMelt - print*, 'untappedMelt*dt = ', untappedMelt*dt - print*, 'mLayerVolFracIceTrial = ', mLayerVolFracIceTrial - message=trim(message)//'melted more than the available water' - err=20; return - endif ! (inconsistency) - - endif ! if checking a snow layer - - end do ! (looping through state variables) - - endif ! (if we removed too much water) - - ! ======================================================================================================== - - ! *** liquid water - - ! --> check if we removed too much water - if(scalarCanopyLiqTrial < 0._rkind .or. any(mLayerVolFracLiqTrial < 0._rkind) )then - - ! ** - ! canopy within numerical precision - if(scalarCanopyLiqTrial < 0._rkind)then - - if(scalarCanopyLiqTrial > -verySmall)then - scalarCanopyIceTrial = scalarCanopyIceTrial - scalarCanopyLiqTrial - scalarCanopyLiqTrial = 0._rkind - - - ! encountered an inconsistency: spit the dummy - else - print*, 'dt = ', dt - print*, 'untappedMelt = ', untappedMelt - print*, 'untappedMelt*dt = ', untappedMelt*dt - print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial - message=trim(message)//'frozen more than the available water' - err=20; return - endif ! (inconsistency) - - endif ! checking the canopy - - ! ** - ! snow+soil within numerical precision - do iState=1,size(mLayerVolFracLiqTrial) - - ! snow layer within numerical precision - if(mLayerVolFracLiqTrial(iState) < 0._rkind)then - - if(mLayerVolFracLiqTrial(iState) > -verySmall)then - mLayerVolFracIceTrial(iState) = mLayerVolFracIceTrial(iState) - mLayerVolFracLiqTrial(iState) - mLayerVolFracLiqTrial(iState) = 0._rkind - - ! encountered an inconsistency: spit the dummy - else - print*, 'dt = ', dt - print*, 'untappedMelt = ', untappedMelt - print*, 'untappedMelt*dt = ', untappedMelt*dt - print*, 'mLayerVolFracLiqTrial = ', mLayerVolFracLiqTrial - message=trim(message)//'frozen more than the available water' - err=20; return - endif ! (inconsistency) - - endif ! checking a snow layer - - end do ! (looping through state variables) - - endif ! (if we removed too much water) - - endif ! (if energy state variables exist) - - ! ----- - ! * update enthalpy as a diagnostic variable... - ! -------------------------------- - scalarCanairEnthalpy = scalarCanairEnthalpyTrial - scalarCanopyEnthalpy = scalarCanopyEnthalpyTrial - mLayerEnthalpy = mLayerEnthalpyTrial - - ! ----- - ! * update prognostic variables... - ! -------------------------------- - ! update state variables for the vegetation canopy - scalarCanairTemp = scalarCanairTempTrial ! trial value of canopy air temperature (K) - scalarCanopyTemp = scalarCanopyTempTrial ! trial value of canopy temperature (K) - scalarCanopyWat = scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - scalarCanopyLiq = scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) - scalarCanopyIce = scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - - ! update state variables for the snow+soil domain - mLayerTemp = mLayerTempTrial ! trial vector of layer temperature (K) - mLayerVolFracWat = mLayerVolFracWatTrial ! trial vector of volumetric total water content (-) - mLayerVolFracLiq = mLayerVolFracLiqTrial ! trial vector of volumetric liquid water content (-) - mLayerVolFracIce = mLayerVolFracIceTrial ! trial vector of volumetric ice water content (-) - mLayerMatricHead = mLayerMatricHeadTrial ! trial vector of matric head (m) - mLayerMatricHeadLiq = mLayerMatricHeadLiqTrial ! trial vector of matric head (m) - - ! update state variables for the aquifer - scalarAquiferStorage = scalarAquiferStorageTrial - - ! end associations to info in the data structures - end associate - - end subroutine updateProgSundials + if(count(ixSoilOnlyHyd/=integerMissing)==nSoil)then + soilBalance1 = sum( (mLayerVolFracLiqTrial(nSnow+1:nLayers) + mLayerVolFracIceTrial(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) + vertFlux = -(iLayerLiqFluxSoil(nSoil) - iLayerLiqFluxSoil(0))*dt ! m s-1 --> m + tranSink = sum(mLayerTranspire)*dt ! m s-1 --> m + baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m + compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m + liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) + if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues + write(*,'(a,1x,f20.10)') 'dt = ', dt + write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 + write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 + write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux + write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink + write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink + write(*,'(a,1x,f20.10)') 'compSink = ', compSink + write(*,'(a,1x,f20.10)') 'liqError = ', liqError + write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid + waterBalanceError = .true. + return + endif ! if there is a water balance error + endif ! if hydrology states exist in the soil domain + endif ! if checking the mass balance + + ! ----- + ! * remove untapped melt energy... + ! -------------------------------- + + ! only work with energy state variables + if(size(ixNrgOnly)>0)then ! energy state variables exist + + ! loop through energy state variables + do iState=1,size(ixNrgOnly) + + ! get index of the control volume within the domain + ixSubset = ixNrgOnly(iState) ! index within the state subset + ixFullVector = ixMapSubset2Full(ixSubset) ! index within full state vector + ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain + + ! compute volumetric melt (kg m-3) + volMelt = dt*untappedMelt(ixSubset)/LH_fus ! (kg m-3) + + ! update ice content + select case( ixDomainType(ixFullVector) ) + case(iname_cas); cycle ! do nothing, since there is no snow stored in the canopy air space + case(iname_veg); scalarCanopyIceTrial = scalarCanopyIceTrial - volMelt*canopyDepth ! (kg m-2) + case(iname_snow); mLayerVolFracIceTrial(ixControlIndex) = mLayerVolFracIceTrial(ixControlIndex) - volMelt/iden_ice ! (-) + case(iname_soil); mLayerVolFracIceTrial(ixControlIndex+nSnow) = mLayerVolFracIceTrial(ixControlIndex+nSnow) - volMelt/iden_water ! (-) + case default; err=20; message=trim(message)//'unable to identify domain type [remove untapped melt energy]'; return + end select + + ! update liquid water content + select case( ixDomainType(ixFullVector) ) + case(iname_cas); cycle ! do nothing, since there is no snow stored in the canopy air space + case(iname_veg); scalarCanopyLiqTrial = scalarCanopyLiqTrial + volMelt*canopyDepth ! (kg m-2) + case(iname_snow); mLayerVolFracLiqTrial(ixControlIndex) = mLayerVolFracLiqTrial(ixControlIndex) + volMelt/iden_water ! (-) + case(iname_soil); mLayerVolFracLiqTrial(ixControlIndex+nSnow) = mLayerVolFracLiqTrial(ixControlIndex+nSnow) + volMelt/iden_water ! (-) + case default; err=20; message=trim(message)//'unable to identify domain type [remove untapped melt energy]'; return + end select + + end do ! looping through energy variables + + ! ======================================================================================================== + + ! *** ice + + ! --> check if we removed too much water + if(scalarCanopyIceTrial < 0._rkind .or. any(mLayerVolFracIceTrial < 0._rkind) )then + + ! ** + ! canopy within numerical precision + if(scalarCanopyIceTrial < 0._rkind)then + + if(scalarCanopyIceTrial > -verySmall)then + scalarCanopyLiqTrial = scalarCanopyLiqTrial - scalarCanopyIceTrial + scalarCanopyIceTrial = 0._rkind + + ! encountered an inconsistency: spit the dummy + else + print*, 'dt = ', dt + print*, 'untappedMelt = ', untappedMelt + print*, 'untappedMelt*dt = ', untappedMelt*dt + print*, 'scalarCanopyiceTrial = ', scalarCanopyIceTrial + message=trim(message)//'melted more than the available water' + err=20; return + endif ! (inconsistency) + + endif ! if checking the canopy + ! ** + ! snow+soil within numerical precision + do iState=1,size(mLayerVolFracIceTrial) + + ! snow layer within numerical precision + if(mLayerVolFracIceTrial(iState) < 0._rkind)then + + if(mLayerVolFracIceTrial(iState) > -verySmall)then + mLayerVolFracLiqTrial(iState) = mLayerVolFracLiqTrial(iState) - mLayerVolFracIceTrial(iState) + mLayerVolFracIceTrial(iState) = 0._rkind + + ! encountered an inconsistency: spit the dummy + else + print*, 'dt = ', dt + print*, 'untappedMelt = ', untappedMelt + print*, 'untappedMelt*dt = ', untappedMelt*dt + print*, 'mLayerVolFracIceTrial = ', mLayerVolFracIceTrial + message=trim(message)//'melted more than the available water' + err=20; return + endif ! (inconsistency) + + endif ! if checking a snow layer + + end do ! (looping through state variables) + + endif ! (if we removed too much water) + + ! ======================================================================================================== + + ! *** liquid water + + ! --> check if we removed too much water + if(scalarCanopyLiqTrial < 0._rkind .or. any(mLayerVolFracLiqTrial < 0._rkind) )then + + ! ** + ! canopy within numerical precision + if(scalarCanopyLiqTrial < 0._rkind)then + + if(scalarCanopyLiqTrial > -verySmall)then + scalarCanopyIceTrial = scalarCanopyIceTrial - scalarCanopyLiqTrial + scalarCanopyLiqTrial = 0._rkind + + + ! encountered an inconsistency: spit the dummy + else + print*, 'dt = ', dt + print*, 'untappedMelt = ', untappedMelt + print*, 'untappedMelt*dt = ', untappedMelt*dt + print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial + message=trim(message)//'frozen more than the available water' + err=20; return + endif ! (inconsistency) + endif ! checking the canopy + + ! ** + ! snow+soil within numerical precision + do iState=1,size(mLayerVolFracLiqTrial) + + ! snow layer within numerical precision + if(mLayerVolFracLiqTrial(iState) < 0._rkind)then + + if(mLayerVolFracLiqTrial(iState) > -verySmall)then + mLayerVolFracIceTrial(iState) = mLayerVolFracIceTrial(iState) - mLayerVolFracLiqTrial(iState) + mLayerVolFracLiqTrial(iState) = 0._rkind + + ! encountered an inconsistency: spit the dummy + else + print*, 'dt = ', dt + print*, 'untappedMelt = ', untappedMelt + print*, 'untappedMelt*dt = ', untappedMelt*dt + print*, 'mLayerVolFracLiqTrial = ', mLayerVolFracLiqTrial + message=trim(message)//'frozen more than the available water' + err=20; return + endif ! (inconsistency) + + endif ! checking a snow layer + + end do ! (looping through state variables) + + endif ! (if we removed too much water) + + endif ! (if energy state variables exist) + + ! ----- + ! * update enthalpy as a diagnostic variable... + ! -------------------------------- + scalarCanairEnthalpy = scalarCanairEnthalpyTrial + scalarCanopyEnthalpy = scalarCanopyEnthalpyTrial + mLayerEnthalpy = mLayerEnthalpyTrial + + ! ----- + ! * update prognostic variables... + ! -------------------------------- + ! update state variables for the vegetation canopy + scalarCanairTemp = scalarCanairTempTrial ! trial value of canopy air temperature (K) + scalarCanopyTemp = scalarCanopyTempTrial ! trial value of canopy temperature (K) + scalarCanopyWat = scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + scalarCanopyLiq = scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) + scalarCanopyIce = scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + + ! update state variables for the snow+soil domain + mLayerTemp = mLayerTempTrial ! trial vector of layer temperature (K) + mLayerVolFracWat = mLayerVolFracWatTrial ! trial vector of volumetric total water content (-) + mLayerVolFracLiq = mLayerVolFracLiqTrial ! trial vector of volumetric liquid water content (-) + mLayerVolFracIce = mLayerVolFracIceTrial ! trial vector of volumetric ice water content (-) + mLayerMatricHead = mLayerMatricHeadTrial ! trial vector of matric head (m) + mLayerMatricHeadLiq = mLayerMatricHeadLiqTrial ! trial vector of matric head (m) + + ! update state variables for the aquifer + scalarAquiferStorage = scalarAquiferStorageTrial + + ! end associations to info in the data structures + end associate + +end subroutine updateProgSundials end module varSubstepSundials_module From a427937b883c998881045966b33dc70b8b0bbebe Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 26 Sep 2022 20:27:49 +0000 Subject: [PATCH 0350/1472] cleaned up indentation for getVectorz routine --- build/source/engine/getVectorz.f90 | 784 +++++++++--------- build/source/engine/getVectorzAddSundials.f90 | 333 ++++---- 2 files changed, 553 insertions(+), 564 deletions(-) diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index cccf133b3..7fa99b73f 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -101,10 +101,10 @@ module getVectorz_module contains - ! ********************************************************************************************************** - ! public subroutine popStateVec: populate model state vectors - ! ********************************************************************************************************** - subroutine popStateVec(& +! ********************************************************************************************************** +! public subroutine popStateVec: populate model state vectors +! ********************************************************************************************************** +subroutine popStateVec(& ! input: data structures nState, & ! intent(in): number of desired state variables prog_data, & ! intent(in): model prognostic variables for a local HRU @@ -113,290 +113,287 @@ subroutine popStateVec(& ! output stateVec, & ! intent(out): model state vector err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - ! input: data structures - integer(i4b),intent(in) :: nState ! number of desired state variables - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - ! output - real(rkind),intent(out) :: stateVec(:) ! model state vector (mixed units) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - ! -------------------------------------------------------------------------------------------------------------------------------- - ! state subsets - integer(i4b) :: iState ! index of state within the snow+soil domain - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - integer(i4b) :: ixStateSubset ! index within the state subset - logical(lgt),dimension(nState) :: stateFlag ! flag to denote that the state is populated - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - fixedLength: associate(& - ! model states for the vegetation canopy - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in) : [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in) : [dp] temperature of the vegetation canopy (K) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in) : [dp] mass of total water on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in) : [dp] mass of liquid water on the vegetation canopy (kg m-2) - ! model state variable vectors for the snow-soil layers - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in) : [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in) : [dp(:)] volumetric fraction of total water (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in) : [dp(:)] volumetric fraction of liquid water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in) : [dp(:)] matric head (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in) : [dp(:)] matric potential of liquid water (m) - ! model state variables for the aquifer - scalarAquiferStorage=> prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in) : [dp] storage of water in the aquifer (m) - ! indices defining specific model states - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy hydrology state variable (mass) - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of aquifer storage state variable - ! vector of energy and hydrology indices for the snow and soil domains - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for energy state variables in the snow+soil domain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in) : [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in) : [i4b] number of hydrology state variables in the snow+soil domain - ! type of model state variabless - ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in) : [i4b(:)] [state subset] type of desired model state variables - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in) : [i4b(:)] index of the type of hydrology states in snow+soil domain - ! number of layers - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in) : [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in) : [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in) : [i4b] total number of layers - ) ! end association with variables in the data structures - ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='popStateVec/' - - ! ----- - ! * initialize state vectors... - ! ----------------------------- - - ! initialize flags - stateFlag(:) = .false. - - ! build the state vector for the temperature of the canopy air space - ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer canopy - do concurrent (iState=1:size(ixCasNrg),ixCasNrg(iState)/=integerMissing) - stateVec( ixCasNrg(iState) ) = scalarCanairTemp ! transfer canopy air temperature to the state vector - stateFlag( ixCasNrg(iState) ) = .true. ! flag to denote that the state is populated - end do - - ! build the state vector for the temperature of the vegetation canopy - ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer canopy - do concurrent (iState=1:size(ixVegNrg),ixVegNrg(iState)/=integerMissing) - stateVec( ixVegNrg(iState) ) = scalarCanopyTemp ! transfer vegetation temperature to the state vector - stateFlag( ixVegNrg(iState) ) = .true. ! flag to denote that the state is populated - end do - - ! build the state vector for the water in the vegetation canopy - ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer canopy - do concurrent (iState=1:size(ixVegHyd),ixVegHyd(iState)/=integerMissing) - stateFlag( ixVegHyd(iState) ) = .true. ! flag to denote that the state is populated - select case(ixStateType_subset( ixVegHyd(iState) )) - case(iname_watCanopy); stateVec( ixVegHyd(iState) ) = scalarCanopyWat ! transfer total canopy water to the state vector - case(iname_liqCanopy); stateVec( ixVegHyd(iState) ) = scalarCanopyLiq ! transfer liquid canopy water to the state vector - case default; stateFlag( ixVegHyd(iState) ) = .false. ! flag to denote that the state is populated - end select - end do - - ! build the energy state vector for the snow and soil domain - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - ixStateSubset = ixSnowSoilNrg(iLayer) ! index within the state vector - stateVec(ixStateSubset) = mLayerTemp(iLayer) ! transfer temperature from a layer to the state vector - stateFlag(ixStateSubset) = .true. ! flag to denote that the state is populated - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! build the hydrology state vector for the snow+soil domains - ! NOTE: ixVolFracWat and ixVolFracLiq can also include states in the soil domain, hence enable primary variable switching - if(nSnowSoilHyd>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) - ixStateSubset = ixSnowSoilHyd(iLayer) ! index within the state vector - stateFlag(ixStateSubset) = .true. ! flag to denote that the state is populated - select case( ixHydType(iLayer) ) - case(iname_watLayer); stateVec(ixStateSubset) = mLayerVolFracWat(iLayer) ! total water state variable for snow+soil layers - case(iname_liqLayer); stateVec(ixStateSubset) = mLayerVolFracLiq(iLayer) ! liquid water state variable for snow+soil layers - case(iname_matLayer); stateVec(ixStateSubset) = mLayerMatricHead(iLayer-nSnow) ! total water matric potential variable for soil layers - case(iname_lmpLayer); stateVec(ixStateSubset) = mLayerMatricHeadLiq(iLayer-nSnow) ! liquid matric potential state variable for soil layers - case default; stateFlag(ixStateSubset) = .false. ! flag to denote that the state is populated - end select - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! build the state vector for the aquifer storage - ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer aquifer - do concurrent (iState=1:size(ixAqWat),ixAqWat(iState)/=integerMissing) - stateVec( ixAqWat(iState) ) = scalarAquiferStorage ! transfer aquifer storage to the state vector - stateFlag( ixAqWat(iState) ) = .true. ! flag to denote that the state is populated - end do - - ! check that we populated all state variables - if(count(stateFlag)/=nState)then - print*, 'stateFlag = ', stateFlag - message=trim(message)//'some state variables unpopulated' - err=20; return - endif - - end associate fixedLength ! end association to variables in the data structure where vector length does not change - end subroutine popStateVec - - - ! ********************************************************************************************************** - ! public subroutine getScaling: get scale factors - ! ********************************************************************************************************** - subroutine getScaling(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output - fScale, & ! intent(out): function scaling vector (mixed units) - xScale, & ! intent(out): variable scaling vector (mixed units) - sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) - dMat, & ! intent(out): diagonal of the Jacobian matrix (excludes fluxes) - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - USE nr_utility_module,only:arth ! get a sequence of numbers arth(start, incr, count) - USE f2008funcs_module,only:findIndex ! finds the index of the first value within a vector - ! -------------------------------------------------------------------------------------------------------------------------------- - ! input: data structures - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - ! output: state vectors - real(rkind),intent(out) :: fScale(:) ! function scaling vector (mixed units) - real(rkind),intent(out) :: xScale(:) ! variable scaling vector (mixed units) - real(rkind),intent(out) :: sMul(:) ! NOTE: qp ! multiplier for state vector (used in the residual calculations) - real(rkind),intent(out) :: dMat(:) ! diagonal of the Jacobian matrix (excludes fluxes) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - ! -------------------------------------------------------------------------------------------------------------------------------- - ! scaling parameters - real(rkind),parameter :: fScaleLiq=0.01_rkind ! func eval: characteristic scale for volumetric liquid water content (-) - real(rkind),parameter :: fScaleMat=10._rkind ! func eval: characteristic scale for matric head (m) - real(rkind),parameter :: fScaleNrg=1000000._rkind ! func eval: characteristic scale for energy (J m-3) - real(rkind),parameter :: xScaleLiq=0.1_rkind ! state var: characteristic scale for volumetric liquid water content (-) - real(rkind),parameter :: xScaleMat=10._rkind ! state var: characteristic scale for matric head (m) - real(rkind),parameter :: xScaleTemp=1._rkind ! state var: characteristic scale for temperature (K) - ! state subsets - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - integer(i4b) :: ixStateSubset ! index within the state subset - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - fixedLength: associate(& - ! model diagnostic variables - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp] canopy depth (m) - volHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(in) : [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) - mLayerVolHeatCap => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in) : [dp(:)] bulk volumetric heat capacity in each snow and soil layer (J m-3 K-1) - ! indices defining specific model states - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy hydrology state variable (mass) - ! vector of energy and hydrology indices for the snow and soil domains - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for energy state variables in the snow+soil domain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in) : [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in) : [i4b] number of hydrology state variables in the snow+soil domain - ! type of model state variabless - ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in) : [i4b(:)] [state subset] type of desired model state variables - ! number of layers - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in) : [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in) : [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in) : [i4b] total number of layers - ) ! end association with variables in the data structures - ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='getScaling/' - - ! ----- - ! * define scaling vectors... - ! --------------------------- - - ! define the function and variable scaling factors for energy - where(ixStateType_subset==iname_nrgCanair .or. ixStateType_subset==iname_nrgCanopy .or. ixStateType_subset==iname_nrgLayer) - fScale = 1._rkind / fScaleNrg ! 1/(J m-3) - xScale = 1._rkind ! K - endwhere - - ! define the function and variable scaling factors for water on the vegetation canopy - where(ixStateType_subset==iname_watCanopy .or. ixStateType_subset==iname_liqCanopy) - fScale = 1._rkind / (fScaleLiq*canopyDepth*iden_water) ! 1/(kg m-2) - xScale = 1._rkind ! (kg m-2) - endwhere - - ! define the function and variable scaling factors for water in the snow+soil domain - where(ixStateType_subset==iname_watLayer .or. ixStateType_subset==iname_liqLayer) - fScale = 1._rkind / fScaleLiq ! (-) - xScale = 1._rkind ! (-) - end where - - ! define the function and variable scaling factors for water in the snow+soil domain - where(ixStateType_subset==iname_matLayer .or. ixStateType_subset==iname_lmpLayer) - fScale = 1._rkind / fScaleLiq ! (-) - xScale = 1._rkind ! (m) - end where - - ! define the function and variable scaling factors for water storage in the aquifer - where(ixStateType_subset==iname_watAquifer) - fScale = 1._rkind - xScale = 1._rkind - endwhere - - ! ----- - ! * define components of derivative matrices that are constant over a time step (substep)... - ! ------------------------------------------------------------------------------------------ - - ! define the multiplier for the state vector for residual calculations (vegetation canopy) - ! NOTE: Use the "where" statement to generalize to multiple canopy layers (currently one canopy layer) - - where(ixStateType_subset==iname_nrgCanair) sMul = Cp_air*iden_air ! volumetric heat capacity of air (J m-3 K-1) - where(ixStateType_subset==iname_nrgCanopy) sMul = volHeatCapVeg ! volumetric heat capacity of the vegetation (J m-3 K-1) - where(ixStateType_subset==iname_watCanopy) sMul = 1._rkind ! nothing else on the left hand side - where(ixStateType_subset==iname_liqCanopy) sMul = 1._rkind ! nothing else on the left hand side - - ! compute terms in the Jacobian for vegetation (excluding fluxes) - ! NOTE: This is computed outside the iteration loop because it does not depend on state variables - ! NOTE: Energy for vegetation is computed *within* the iteration loop as it includes phase change - ! NOTE: Use the "where" statement to generalize to multiple canopy layers (currently one canopy layer) - where(ixStateType_subset==iname_nrgCanair) dMat = Cp_air*iden_air ! volumetric heat capacity of air (J m-3 K-1) - where(ixStateType_subset==iname_nrgCanopy) dMat = realMissing ! populated within the iteration loop - where(ixStateType_subset==iname_watCanopy) dMat = 1._rkind ! nothing else on the left hand side - where(ixStateType_subset==iname_liqCanopy) dMat = 1._rkind ! nothing else on the left hand side - - ! define the energy multiplier and diagonal elements for the state vector for residual calculations (snow-soil domain) - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - ixStateSubset = ixSnowSoilNrg(iLayer) ! index within the state vector - sMul(ixStateSubset) = mLayerVolHeatCap(iLayer) ! transfer volumetric heat capacity to the state multiplier - dMat(ixStateSubset) = realMissing ! diagonal element populated within the iteration loop - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! define the hydrology multiplier and diagonal elements for the state vector for residual calculations (snow-soil domain) - if(nSnowSoilHyd>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - ixStateSubset = ixSnowSoilHyd(iLayer) ! index within the state vector - sMul(ixStateSubset) = 1._rkind ! state multiplier = 1 (nothing else on the left-hand-side) - dMat(ixStateSubset) = 1._rkind ! diagonal element = 1 (nothing else on the left-hand-side) - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! define the scaling factor and diagonal elements for the aquifer - where(ixStateType_subset==iname_watAquifer) - sMul = 1._rkind - dMat = 1._rkind - endwhere - - ! ------------------------------------------------------------------------------------------ - ! ------------------------------------------------------------------------------------------ - - end associate fixedLength ! end association to variables in the data structure where vector length does not change + ! -------------------------------------------------------------------------------------------------------------------------------- + ! input: data structures + integer(i4b),intent(in) :: nState ! number of desired state variables + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + ! output + real(rkind),intent(out) :: stateVec(:) ! model state vector (mixed units) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + ! -------------------------------------------------------------------------------------------------------------------------------- + ! state subsets + integer(i4b) :: iState ! index of state within the snow+soil domain + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + integer(i4b) :: ixStateSubset ! index within the state subset + logical(lgt),dimension(nState) :: stateFlag ! flag to denote that the state is populated + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + fixedLength: associate(& + ! model states for the vegetation canopy + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in) : [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in) : [dp] temperature of the vegetation canopy (K) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in) : [dp] mass of total water on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in) : [dp] mass of liquid water on the vegetation canopy (kg m-2) + ! model state variable vectors for the snow-soil layers + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in) : [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in) : [dp(:)] volumetric fraction of total water (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in) : [dp(:)] volumetric fraction of liquid water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in) : [dp(:)] matric head (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in) : [dp(:)] matric potential of liquid water (m) + ! model state variables for the aquifer + scalarAquiferStorage=> prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in) : [dp] storage of water in the aquifer (m) + ! indices defining specific model states + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy hydrology state variable (mass) + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of aquifer storage state variable + ! vector of energy and hydrology indices for the snow and soil domains + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in) : [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in) : [i4b] number of hydrology state variables in the snow+soil domain + ! type of model state variabless + ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in) : [i4b(:)] [state subset] type of desired model state variables + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in) : [i4b(:)] index of the type of hydrology states in snow+soil domain + ! number of layers + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in) : [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in) : [i4b] number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in) : [i4b] total number of layers + ) ! end association with variables in the data structures + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='popStateVec/' + + ! ----- + ! * initialize state vectors... + ! ----------------------------- + + ! initialize flags + stateFlag(:) = .false. + + ! build the state vector for the temperature of the canopy air space + ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer canopy + do concurrent (iState=1:size(ixCasNrg),ixCasNrg(iState)/=integerMissing) + stateVec( ixCasNrg(iState) ) = scalarCanairTemp ! transfer canopy air temperature to the state vector + stateFlag( ixCasNrg(iState) ) = .true. ! flag to denote that the state is populated + end do + + ! build the state vector for the temperature of the vegetation canopy + ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer canopy + do concurrent (iState=1:size(ixVegNrg),ixVegNrg(iState)/=integerMissing) + stateVec( ixVegNrg(iState) ) = scalarCanopyTemp ! transfer vegetation temperature to the state vector + stateFlag( ixVegNrg(iState) ) = .true. ! flag to denote that the state is populated + end do + + ! build the state vector for the water in the vegetation canopy + ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer canopy + do concurrent (iState=1:size(ixVegHyd),ixVegHyd(iState)/=integerMissing) + stateFlag( ixVegHyd(iState) ) = .true. ! flag to denote that the state is populated + select case(ixStateType_subset( ixVegHyd(iState) )) + case(iname_watCanopy); stateVec( ixVegHyd(iState) ) = scalarCanopyWat ! transfer total canopy water to the state vector + case(iname_liqCanopy); stateVec( ixVegHyd(iState) ) = scalarCanopyLiq ! transfer liquid canopy water to the state vector + case default; stateFlag( ixVegHyd(iState) ) = .false. ! flag to denote that the state is populated + end select + end do + + ! build the energy state vector for the snow and soil domain + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + ixStateSubset = ixSnowSoilNrg(iLayer) ! index within the state vector + stateVec(ixStateSubset) = mLayerTemp(iLayer) ! transfer temperature from a layer to the state vector + stateFlag(ixStateSubset) = .true. ! flag to denote that the state is populated + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! build the hydrology state vector for the snow+soil domains + ! NOTE: ixVolFracWat and ixVolFracLiq can also include states in the soil domain, hence enable primary variable switching + if(nSnowSoilHyd>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) + ixStateSubset = ixSnowSoilHyd(iLayer) ! index within the state vector + stateFlag(ixStateSubset) = .true. ! flag to denote that the state is populated + select case( ixHydType(iLayer) ) + case(iname_watLayer); stateVec(ixStateSubset) = mLayerVolFracWat(iLayer) ! total water state variable for snow+soil layers + case(iname_liqLayer); stateVec(ixStateSubset) = mLayerVolFracLiq(iLayer) ! liquid water state variable for snow+soil layers + case(iname_matLayer); stateVec(ixStateSubset) = mLayerMatricHead(iLayer-nSnow) ! total water matric potential variable for soil layers + case(iname_lmpLayer); stateVec(ixStateSubset) = mLayerMatricHeadLiq(iLayer-nSnow) ! liquid matric potential state variable for soil layers + case default; stateFlag(ixStateSubset) = .false. ! flag to denote that the state is populated + end select + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! build the state vector for the aquifer storage + ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer aquifer + do concurrent (iState=1:size(ixAqWat),ixAqWat(iState)/=integerMissing) + stateVec( ixAqWat(iState) ) = scalarAquiferStorage ! transfer aquifer storage to the state vector + stateFlag( ixAqWat(iState) ) = .true. ! flag to denote that the state is populated + end do + + ! check that we populated all state variables + if(count(stateFlag)/=nState)then + print*, 'stateFlag = ', stateFlag + message=trim(message)//'some state variables unpopulated' + err=20; return + endif + + end associate fixedLength ! end association to variables in the data structure where vector length does not change +end subroutine popStateVec + + +! ********************************************************************************************************** +! public subroutine getScaling: get scale factors +! ********************************************************************************************************** +subroutine getScaling(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output + fScale, & ! intent(out): function scaling vector (mixed units) + xScale, & ! intent(out): variable scaling vector (mixed units) + sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) + dMat, & ! intent(out): diagonal of the Jacobian matrix (excludes fluxes) + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- + USE nr_utility_module,only:arth ! get a sequence of numbers arth(start, incr, count) + USE f2008funcs_module,only:findIndex ! finds the index of the first value within a vector + ! -------------------------------------------------------------------------------------------------------------------------------- + ! input: data structures + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + ! output: state vectors + real(rkind),intent(out) :: fScale(:) ! function scaling vector (mixed units) + real(rkind),intent(out) :: xScale(:) ! variable scaling vector (mixed units) + real(rkind),intent(out) :: sMul(:) ! NOTE: qp ! multiplier for state vector (used in the residual calculations) + real(rkind),intent(out) :: dMat(:) ! diagonal of the Jacobian matrix (excludes fluxes) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + ! -------------------------------------------------------------------------------------------------------------------------------- + ! scaling parameters + real(rkind),parameter :: fScaleLiq=0.01_rkind ! func eval: characteristic scale for volumetric liquid water content (-) + real(rkind),parameter :: fScaleMat=10._rkind ! func eval: characteristic scale for matric head (m) + real(rkind),parameter :: fScaleNrg=1000000._rkind ! func eval: characteristic scale for energy (J m-3) + real(rkind),parameter :: xScaleLiq=0.1_rkind ! state var: characteristic scale for volumetric liquid water content (-) + real(rkind),parameter :: xScaleMat=10._rkind ! state var: characteristic scale for matric head (m) + real(rkind),parameter :: xScaleTemp=1._rkind ! state var: characteristic scale for temperature (K) + ! state subsets + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + integer(i4b) :: ixStateSubset ! index within the state subset + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + fixedLength: associate(& + ! model diagnostic variables + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp] canopy depth (m) + volHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(in) : [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) + mLayerVolHeatCap => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in) : [dp(:)] bulk volumetric heat capacity in each snow and soil layer (J m-3 K-1) + ! indices defining specific model states + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy hydrology state variable (mass) + ! vector of energy and hydrology indices for the snow and soil domains + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in) : [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in) : [i4b] number of hydrology state variables in the snow+soil domain + ! type of model state variabless + ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in) : [i4b(:)] [state subset] type of desired model state variables + ! number of layers + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in) : [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in) : [i4b] number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in) : [i4b] total number of layers + ) ! end association with variables in the data structures + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='getScaling/' + + ! ----- + ! * define scaling vectors... + ! --------------------------- + + ! define the function and variable scaling factors for energy + where(ixStateType_subset==iname_nrgCanair .or. ixStateType_subset==iname_nrgCanopy .or. ixStateType_subset==iname_nrgLayer) + fScale = 1._rkind / fScaleNrg ! 1/(J m-3) + xScale = 1._rkind ! K + endwhere + + ! define the function and variable scaling factors for water on the vegetation canopy + where(ixStateType_subset==iname_watCanopy .or. ixStateType_subset==iname_liqCanopy) + fScale = 1._rkind / (fScaleLiq*canopyDepth*iden_water) ! 1/(kg m-2) + xScale = 1._rkind ! (kg m-2) + endwhere + + ! define the function and variable scaling factors for water in the snow+soil domain + where(ixStateType_subset==iname_watLayer .or. ixStateType_subset==iname_liqLayer) + fScale = 1._rkind / fScaleLiq ! (-) + xScale = 1._rkind ! (-) + end where + + ! define the function and variable scaling factors for water in the snow+soil domain + where(ixStateType_subset==iname_matLayer .or. ixStateType_subset==iname_lmpLayer) + fScale = 1._rkind / fScaleLiq ! (-) + xScale = 1._rkind ! (m) + end where + + ! define the function and variable scaling factors for water storage in the aquifer + where(ixStateType_subset==iname_watAquifer) + fScale = 1._rkind + xScale = 1._rkind + endwhere + + ! ----- + ! * define components of derivative matrices that are constant over a time step (substep)... + ! ------------------------------------------------------------------------------------------ + + ! define the multiplier for the state vector for residual calculations (vegetation canopy) + ! NOTE: Use the "where" statement to generalize to multiple canopy layers (currently one canopy layer) + + where(ixStateType_subset==iname_nrgCanair) sMul = Cp_air*iden_air ! volumetric heat capacity of air (J m-3 K-1) + where(ixStateType_subset==iname_nrgCanopy) sMul = volHeatCapVeg ! volumetric heat capacity of the vegetation (J m-3 K-1) + where(ixStateType_subset==iname_watCanopy) sMul = 1._rkind ! nothing else on the left hand side + where(ixStateType_subset==iname_liqCanopy) sMul = 1._rkind ! nothing else on the left hand side + + ! compute terms in the Jacobian for vegetation (excluding fluxes) + ! NOTE: This is computed outside the iteration loop because it does not depend on state variables + ! NOTE: Energy for vegetation is computed *within* the iteration loop as it includes phase change + ! NOTE: Use the "where" statement to generalize to multiple canopy layers (currently one canopy layer) + where(ixStateType_subset==iname_nrgCanair) dMat = Cp_air*iden_air ! volumetric heat capacity of air (J m-3 K-1) + where(ixStateType_subset==iname_nrgCanopy) dMat = realMissing ! populated within the iteration loop + where(ixStateType_subset==iname_watCanopy) dMat = 1._rkind ! nothing else on the left hand side + where(ixStateType_subset==iname_liqCanopy) dMat = 1._rkind ! nothing else on the left hand side + + ! define the energy multiplier and diagonal elements for the state vector for residual calculations (snow-soil domain) + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + ixStateSubset = ixSnowSoilNrg(iLayer) ! index within the state vector + sMul(ixStateSubset) = mLayerVolHeatCap(iLayer) ! transfer volumetric heat capacity to the state multiplier + dMat(ixStateSubset) = realMissing ! diagonal element populated within the iteration loop + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! define the hydrology multiplier and diagonal elements for the state vector for residual calculations (snow-soil domain) + if(nSnowSoilHyd>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + ixStateSubset = ixSnowSoilHyd(iLayer) ! index within the state vector + sMul(ixStateSubset) = 1._rkind ! state multiplier = 1 (nothing else on the left-hand-side) + dMat(ixStateSubset) = 1._rkind ! diagonal element = 1 (nothing else on the left-hand-side) + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! define the scaling factor and diagonal elements for the aquifer + where(ixStateType_subset==iname_watAquifer) + sMul = 1._rkind + dMat = 1._rkind + endwhere + + end associate fixedLength ! end association to variables in the data structure where vector length does not change end subroutine getScaling @@ -425,111 +422,110 @@ subroutine varExtract(& scalarAquiferStorageTrial, & ! intent(out): trial value of storage of water in the aquifer (m) ! output: error control err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input - real(rkind),intent(in) :: stateVec(:) ! model state vector (mixed units) - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - ! output: variables for the vegetation canopy - real(rkind),intent(out) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) - real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind),intent(out) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - real(rkind),intent(out) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(out) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(rkind),intent(out) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind),intent(out) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) - ! output: variables for the aquifer - real(rkind),intent(out) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - associate(& - ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers - ! indices defining model states and layers - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of the squifer storage state variable - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for energy states in the snow+soil subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for hydrology states in the snow+soil subdomain - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain - ! indices defining type of model state variables - ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] type of desired model state variables - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat & ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain -)! association with variables in the data structures - - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - - ! initialize error control - err=0; message='varExtract/' - - ! *** extract state variables for the vegetation canopy - - - ! check if computing the vegetation flux - if(ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing .or. ixVegHyd/=integerMissing)then - - ! extract temperature of the canopy air space - if(ixCasNrg/=integerMissing) scalarCanairTempTrial = stateVec(ixCasNrg) - - ! extract canopy temperature - if(ixVegNrg/=integerMissing) scalarCanopyTempTrial = stateVec(ixVegNrg) - - ! extract intercepted water - if(ixVegHyd/=integerMissing)then - select case( ixStateType_subset(ixVegHyd) ) - case(iname_liqCanopy); scalarCanopyLiqTrial = stateVec(ixVegHyd) - case(iname_watCanopy); scalarCanopyWatTrial = stateVec(ixVegHyd) - case default; err=20; message=trim(message)//'case not found: expect iname_liqCanopy or iname_watCanopy'; return - end select - endif - - endif ! not computing the vegetation flux - - ! *** extract state variables from the snow+soil sub-domain - - - ! overwrite with the energy values from the state vector - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - mLayerTempTrial(iLayer) = stateVec( ixSnowSoilNrg(iLayer) ) - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! overwrite with the hydrology values from the state vector - if(nSnowSoilHyd>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) - select case( ixHydType(iLayer) ) - case(iname_watLayer); mLayerVolFracWatTrial(iLayer) = stateVec( ixSnowSoilHyd(iLayer) ) ! total water state variable for snow+soil layers - case(iname_liqLayer); mLayerVolFracLiqTrial(iLayer) = stateVec( ixSnowSoilHyd(iLayer) ) ! liquid water state variable for snow+soil layers - case(iname_matLayer); mLayerMatricHeadTrial(iLayer-nSnow) = stateVec( ixSnowSoilHyd(iLayer) ) ! total water matric potential variable for soil layers - case(iname_lmpLayer); mLayerMatricHeadLiqTrial(iLayer-nSnow) = stateVec( ixSnowSoilHyd(iLayer) ) ! liquid matric potential state variable for soil layers - case default ! do nothing - end select - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! extract temperature of the canopy air space - if(ixAqWat/=integerMissing) scalarAquiferStorageTrial = stateVec(ixAqWat) - - end associate - - end subroutine varExtract + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input + real(rkind),intent(in) :: stateVec(:) ! model state vector (mixed units) + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + ! output: variables for the vegetation canopy + real(rkind),intent(out) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) + real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind),intent(out) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + real(rkind),intent(out) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) + ! output: variables for the snow-soil domain + real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(out) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(rkind),intent(out) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind),intent(out) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) + ! output: variables for the aquifer + real(rkind),intent(out) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + associate(& + ! number of model layers, and layer type + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + ! indices defining model states and layers + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of the squifer storage state variable + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for energy states in the snow+soil subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for hydrology states in the snow+soil subdomain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + ! indices defining type of model state variables + ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] type of desired model state variables + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat & ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain + )! association with variables in the data structures + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message='varExtract/' + + ! *** extract state variables for the vegetation canopy + + ! check if computing the vegetation flux + if(ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing .or. ixVegHyd/=integerMissing)then + + ! extract temperature of the canopy air space + if(ixCasNrg/=integerMissing) scalarCanairTempTrial = stateVec(ixCasNrg) + + ! extract canopy temperature + if(ixVegNrg/=integerMissing) scalarCanopyTempTrial = stateVec(ixVegNrg) + + ! extract intercepted water + if(ixVegHyd/=integerMissing)then + select case( ixStateType_subset(ixVegHyd) ) + case(iname_liqCanopy); scalarCanopyLiqTrial = stateVec(ixVegHyd) + case(iname_watCanopy); scalarCanopyWatTrial = stateVec(ixVegHyd) + case default; err=20; message=trim(message)//'case not found: expect iname_liqCanopy or iname_watCanopy'; return + end select + endif + + endif ! not computing the vegetation flux + + ! *** extract state variables from the snow+soil sub-domain + + + ! overwrite with the energy values from the state vector + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + mLayerTempTrial(iLayer) = stateVec( ixSnowSoilNrg(iLayer) ) + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! overwrite with the hydrology values from the state vector + if(nSnowSoilHyd>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) + select case( ixHydType(iLayer) ) + case(iname_watLayer); mLayerVolFracWatTrial(iLayer) = stateVec( ixSnowSoilHyd(iLayer) ) ! total water state variable for snow+soil layers + case(iname_liqLayer); mLayerVolFracLiqTrial(iLayer) = stateVec( ixSnowSoilHyd(iLayer) ) ! liquid water state variable for snow+soil layers + case(iname_matLayer); mLayerMatricHeadTrial(iLayer-nSnow) = stateVec( ixSnowSoilHyd(iLayer) ) ! total water matric potential variable for soil layers + case(iname_lmpLayer); mLayerMatricHeadLiqTrial(iLayer-nSnow) = stateVec( ixSnowSoilHyd(iLayer) ) ! liquid matric potential state variable for soil layers + case default ! do nothing + end select + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! extract temperature of the canopy air space + if(ixAqWat/=integerMissing) scalarAquiferStorageTrial = stateVec(ixAqWat) + + end associate + +end subroutine varExtract end module getVectorz_module diff --git a/build/source/engine/getVectorzAddSundials.f90 b/build/source/engine/getVectorzAddSundials.f90 index 3113f43d3..83545e2c9 100644 --- a/build/source/engine/getVectorzAddSundials.f90 +++ b/build/source/engine/getVectorzAddSundials.f90 @@ -1,5 +1,3 @@ - - module getVectorzAddSundials_module ! data types @@ -80,173 +78,168 @@ module getVectorzAddSundials_module - ! ********************************************************************************************************** - ! public subroutine residDiscontinuity: - ! ********************************************************************************************************** - subroutine residDiscontinuity(& - ! input - stateVec, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output - resid, & ! intent(out) - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input - real(rkind),intent(in) :: stateVec(:) ! model state vector (mixed units) - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - real(qp),intent(out) :: resid(:) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - integer(i4b) :: iCount - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - associate(& - ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers - ! indices defining model states and layers - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of the squifer storage state variable - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for energy states in the snow+soil subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for hydrology states in the snow+soil subdomain - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain - ! indices defining type of model state variables - ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] type of desired model state variables - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat & ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain -)! association with variables in the data structures - - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - - ! initialize error control - err=0; message='residDiscontinuity/' - - iCount = 1 - - - ! check if computing the vegetation flux - if(ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing)then - - ! temperature of the canopy air space - if(ixCasNrg/=integerMissing)then - resid(iCount) = stateVec(ixCasNrg) - Tfreeze ! scalarCanairTempTrial - Tfreeze - iCount = iCount + 1 - endif - - ! canopy temperature - if(ixVegNrg/=integerMissing)then - resid(iCount) = stateVec(ixVegNrg) - Tfreeze ! scalarCanopyTempTrial - Tfreeze - iCount = iCount + 1 - endif - - - endif ! not computing the vegetation flux - - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - resid( iCount ) = stateVec( ixSnowSoilNrg(iLayer) ) - Tfreeze ! mLayerTempTrial(iLayer) - Tfreeze - iCount = iCount + 1 - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - end associate - - - end subroutine residDiscontinuity - - ! ********************************************************************************************************** - ! public subroutine countDiscontinuity: - ! ********************************************************************************************************** - subroutine countDiscontinuity(& - ! input - stateVec, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output - countD, & ! intent(out) - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input - real(rkind),intent(in) :: stateVec(:) ! model state vector (mixed units) - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - integer(i4b),intent(out) :: countD - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - associate(& - ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers - ! indices defining model states and layers - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of the squifer storage state variable - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for energy states in the snow+soil subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for hydrology states in the snow+soil subdomain - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain - ! indices defining type of model state variables - ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] type of desired model state variables - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat & ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain -)! association with variables in the data structures - - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - - ! initialize error control - err=0; message='countDiscontinuity/' - - ! *** extract state variables for the vegetation canopy - - countD = 0 - ! check if computing the vegetation flux - if(ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing)then - - ! temperature of the canopy air space - if(ixCasNrg/=integerMissing) countD = countD + 1 - - ! canopy temperature - if(ixVegNrg/=integerMissing) countD = countD + 1 - - - endif ! not computing the vegetation flux - - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - countD = countD + 1 - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - end associate - - end subroutine countDiscontinuity - - +! ********************************************************************************************************** +! public subroutine residDiscontinuity: +! ********************************************************************************************************** +subroutine residDiscontinuity(& + ! input + stateVec, & ! intent(in): model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output + resid, & ! intent(out) + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input + real(rkind),intent(in) :: stateVec(:) ! model state vector (mixed units) + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + real(qp),intent(out) :: resid(:) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + integer(i4b) :: iCount + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + associate(& + ! number of model layers, and layer type + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + ! indices defining model states and layers + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of the squifer storage state variable + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for energy states in the snow+soil subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for hydrology states in the snow+soil subdomain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + ! indices defining type of model state variables + ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] type of desired model state variables + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat & ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain + )! association with variables in the data structures + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message='residDiscontinuity/' + + iCount = 1 + + + ! check if computing the vegetation flux + if(ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing)then + + ! temperature of the canopy air space + if(ixCasNrg/=integerMissing)then + resid(iCount) = stateVec(ixCasNrg) - Tfreeze ! scalarCanairTempTrial - Tfreeze + iCount = iCount + 1 + endif + + ! canopy temperature + if(ixVegNrg/=integerMissing)then + resid(iCount) = stateVec(ixVegNrg) - Tfreeze ! scalarCanopyTempTrial - Tfreeze + iCount = iCount + 1 + endif + + endif ! not computing the vegetation flux + + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + resid( iCount ) = stateVec( ixSnowSoilNrg(iLayer) ) - Tfreeze ! mLayerTempTrial(iLayer) - Tfreeze + iCount = iCount + 1 + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + end associate +end subroutine residDiscontinuity + +! ********************************************************************************************************** +! public subroutine countDiscontinuity: +! ********************************************************************************************************** +subroutine countDiscontinuity(& + ! input + stateVec, & ! intent(in): model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output + countD, & ! intent(out) + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input + real(rkind),intent(in) :: stateVec(:) ! model state vector (mixed units) + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + integer(i4b),intent(out) :: countD + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + associate(& + ! number of model layers, and layer type + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + ! indices defining model states and layers + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of the squifer storage state variable + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for energy states in the snow+soil subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for hydrology states in the snow+soil subdomain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + ! indices defining type of model state variables + ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] type of desired model state variables + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat & ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain + )! association with variables in the data structures + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message='countDiscontinuity/' + + ! *** extract state variables for the vegetation canopy + + countD = 0 + ! check if computing the vegetation flux + if(ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing)then + + ! temperature of the canopy air space + if(ixCasNrg/=integerMissing) countD = countD + 1 + + ! canopy temperature + if(ixVegNrg/=integerMissing) countD = countD + 1 + + + endif ! not computing the vegetation flux + + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + countD = countD + 1 + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + end associate + +end subroutine countDiscontinuity end module getVectorzAddSundials_module From 937776400317eb04ef3f259c639cf282c2e8ee38 Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 26 Sep 2022 21:13:38 +0000 Subject: [PATCH 0351/1472] cleaned up indentation for eval8summa --- build/source/engine/eval8summa.f90 | 980 ++++++++++++++--------------- 1 file changed, 482 insertions(+), 498 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 99232deed..01563b05f 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -102,503 +102,487 @@ module eval8summa_module contains - ! ********************************************************************************************************** - ! public subroutine eval8summa: compute the residual vector and the Jacobian matrix - ! ********************************************************************************************************** - subroutine eval8summa(& - ! input: model control - dt, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - nState, & ! intent(in): total number of state variables - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVecTrial, & ! intent(in): model state vector - fScale, & ! intent(in): function scaling vector - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - ! output: flux and residual vectors - feasible, & ! intent(out): flag to denote the feasibility of the solution - fluxVec, & ! intent(out): flux vector - resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - resVec, & ! intent(out): residual vector - fEval, & ! intent(out): function evaluation - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - ! provide access to subroutines - USE getVectorz_module, only:varExtract ! extract variables from the state vector - USE updateVars_module, only:updateVars ! update prognostic variables - USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy - USE computFlux_module, only:soilCmpres ! compute soil compression, use non-sundials version because sundials version needs mLayerMatricHeadPrime - USE computFlux_module, only:computFlux ! compute fluxes given a state vector - USE computResid_module,only:computResid ! compute residuals given a state vector - implicit none - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - ! input: model control - real(rkind),intent(in) :: dt ! length of the time step (seconds) - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - integer(i4b),intent(in) :: nState ! total number of state variables - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - ! input: state vectors - real(rkind),intent(in) :: stateVecTrial(:) ! model state vector - real(rkind),intent(in) :: fScale(:) ! function scaling vector - real(rkind),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) - ! input: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(zLookup), intent(in) :: lookup_data ! lookup tables - type(var_i), intent(in) :: type_data ! type of vegetation and soil - type(var_d), intent(in) :: attr_data ! spatial attributes - type(var_dlength), intent(in) :: mpar_data ! model parameters - type(var_d), intent(in) :: forc_data ! model forcing data - type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin - type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU - ! output: data structures - type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - ! output: flux and residual vectors - logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution - real(rkind),intent(out) :: fluxVec(:) ! flux vector - real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation - real(rkind),intent(out) :: resVec(:) ! NOTE: qp ! residual vector - real(rkind),intent(out) :: fEval ! function evaluation - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - ! -------------------------------------------------------------------------------------------------------------------------------- - ! state variables - real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial value for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial value for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial value for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) - real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) - ! diagnostic variables - logical(lgt),parameter :: needEnthalpy=.true. ! flag to compute enthalpy - real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) - ! other local variables - integer(i4b) :: iLayer ! index of model layer in the snow+soil domain - integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain - integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine - integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) - real(rkind) :: xMin,xMax ! minimum and maximum values for water content - real(rkind) :: scalarCanopyHydTrial ! trial value for mass of water on the vegetation canopy (kg m-2) - real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) - real(rkind),dimension(nLayers) :: mLayerVolFracHydTrial ! trial value for volumetric fraction of water (-), general vector merged from Wat and Liq - real(rkind),dimension(nState) :: rVecScaled ! scaled residual vector - character(LEN=256) :: cmessage ! error message of downwind routine - ! -------------------------------------------------------------------------------------------------------------------------------- - ! association to variables in the data structures - ! -------------------------------------------------------------------------------------------------------------------------------- - associate(& - ! model decisions - ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ! soil parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) - specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) - ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! model state variables - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] total water matric potential (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) - scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in): [dp] storage of water in the aquifer (m) - ! model diagnostic variables from a previous solution - scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) -! enthalpy - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) - ! soil compression - scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in storage associated with compression of the soil matrix (-) - ! derivatives - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential - dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) - ! mapping - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) - ! indices - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable (nrg) - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable (nrg) - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain - layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) - ) ! association to variables in the data structures - ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="eval8summa/" - - ! check the feasibility of the solution - feasible=.true. - - ! check that the canopy air space temperature is reasonable - if(ixCasNrg/=integerMissing)then - if(stateVecTrial(ixCasNrg) > canopyTempMax) feasible=.false. - if(stateVecTrial(ixCasNrg) > canopyTempMax) message=trim(message)//'canopy air space temp high,' - if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVecTrial( ixCasNrg )', feasible, canopyTempMax, stateVecTrial(ixCasNrg) - endif - - ! check that the canopy air space temperature is reasonable - if(ixVegNrg/=integerMissing)then - if(stateVecTrial(ixVegNrg) > canopyTempMax) feasible=.false. - if(stateVecTrial(ixVegNrg) > canopyTempMax) message=trim(message)//'canopy temp high,' - if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVecTrial( ixVegNrg )', feasible, canopyTempMax, stateVecTrial(ixVegNrg) - endif - - ! check canopy liquid water is not negative - if(ixVegHyd/=integerMissing)then - if(stateVecTrial(ixVegHyd) < 0._rkind) feasible=.false. - if(stateVecTrial(ixVegHyd) < 0._rkind) message=trim(message)//'canopy water negative,' - if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVecTrial( ixVegHyd )', feasible, 0._rkind, stateVecTrial(ixVegHyd) - - end if - - ! check snow temperature is below freezing - if(count(ixSnowOnlyNrg/=integerMissing)>0)then - if(any(stateVecTrial( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. - if(any(stateVecTrial( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) message=trim(message)//'snow temp above freezing,' - do iLayer=1,nSnow - if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVecTrial( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVecTrial( ixSnowOnlyNrg(iLayer) ) - enddo - endif - - ! loop through non-missing hydrology state variables in the snow+soil domain - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) - - ! check the minimum and maximum water constraints - if(ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_liqLayer)then - - ! --> minimum - if (layerType(iLayer) == iname_soil) then - xMin = theta_res(iLayer-nSnow) - else - xMin = 0._rkind - endif - - ! --> maximum - select case( layerType(iLayer) ) - case(iname_snow); xMax = merge(iden_ice, 1._rkind - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) - case(iname_soil); xMax = merge(theta_sat(iLayer-nSnow), theta_sat(iLayer-nSnow) - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) - end select - - ! --> check - if(stateVecTrial( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVecTrial( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. - if(stateVecTrial( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVecTrial( ixSnowSoilHyd(iLayer) ) > xMax) message=trim(message)//'layer water outside bounds,' - if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax - - endif ! if water states - - end do ! loop through non-missing hydrology state variables in the snow+soil domain - - ! early return for non-feasible solutions - if(.not.feasible)then - fluxVec(:) = realMissing - resVec(:) = quadMissing - fEval = realMissing - message=trim(message)//'non-feasible' - err=20; return - endif - - ! get the start and end indices for the soil compression calculations - if(scalarSolution)then - jState = pack(ixControlVolume, ixMapFull2Subset/=integerMissing) - ixBeg = jState(1) - ixEnd = jState(1) - else - ixBeg = 1 - ixEnd = nSoil - endif - - ! initialize to state variable from the last update - scalarCanairTempTrial = scalarCanairTemp - scalarCanopyTempTrial = scalarCanopyTemp - scalarCanopyWatTrial = scalarCanopyWat - scalarCanopyLiqTrial = scalarCanopyLiq - scalarCanopyIceTrial = scalarCanopyIce - mLayerTempTrial = mLayerTemp - mLayerVolFracWatTrial = mLayerVolFracWat - mLayerVolFracLiqTrial = mLayerVolFracLiq - mLayerVolFracIceTrial = mLayerVolFracIce - mLayerMatricHeadTrial = mLayerMatricHead ! total water matric potential - mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq ! liquid water matric potential - scalarAquiferStorageTrial = scalarAquiferStorage - - ! extract variables from the model state vector - call varExtract(& - ! input - stateVecTrial, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! update diagnostic variables and derivatives - call updateVars(& - ! input - .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - lookup_data, & ! intent(in): lookup tables for a local HRU - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! compute enthalpy (J m-3) - if(needEnthalpy)then - call t2enthalpy(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) - ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy, & ! intent(out): enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - endif ! if computing enthalpy - - ! print the states in the canopy domain - !print*, 'dt = ', dt - !write(*,'(a,1x,10(f20.10,1x))') 'scalarCanopyTempTrial = ', scalarCanopyTempTrial - !write(*,'(a,1x,10(f20.10,1x))') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - !write(*,'(a,1x,10(f20.10,1x))') 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial - !write(*,'(a,1x,10(f20.10,1x))') 'scalarCanopyIceTrial = ', scalarCanopyIceTrial - - ! print the states in the snow+soil domain - !write(*,'(a,1x,10(f20.10,1x))') 'mLayerTempTrial = ', mLayerTempTrial(iJac1:min(nLayers,iJac2)) - !write(*,'(a,1x,10(f20.10,1x))') 'mLayerVolFracWatTrial = ', mLayerVolFracWatTrial(iJac1:min(nLayers,iJac2)) - !write(*,'(a,1x,10(f20.10,1x))') 'mLayerVolFracLiqTrial = ', mLayerVolFracLiqTrial(iJac1:min(nLayers,iJac2)) - !write(*,'(a,1x,10(f20.10,1x))') 'mLayerVolFracIceTrial = ', mLayerVolFracIceTrial(iJac1:min(nLayers,iJac2)) - !write(*,'(a,1x,10(f20.10,1x))') 'mLayerMatricHeadTrial = ', mLayerMatricHeadTrial(iJac1:min(nSoil,iJac2)) - !write(*,'(a,1x,10(f20.10,1x))') 'mLayerMatricHeadLiqTrial = ', mLayerMatricHeadLiqTrial(iJac1:min(nSoil,iJac2)) - - ! print the water content - if(globalPrintFlag)then - if(iJac1 model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ! soil parameters + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) + specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) + ! canopy and layer depth + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! model state variables + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] total water matric potential (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) + scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in): [dp] storage of water in the aquifer (m) + ! model diagnostic variables from a previous solution + scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + ! enthalpy + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) + ! soil compression + scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in storage associated with compression of the soil matrix (-) + ! derivatives + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) + ! mapping + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) + ! indices + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable (nrg) + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable (nrg) + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain + layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) + ) ! association to variables in the data structures + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="eval8summa/" + + ! check the feasibility of the solution + feasible=.true. + + ! check that the canopy air space temperature is reasonable + if(ixCasNrg/=integerMissing)then + if(stateVecTrial(ixCasNrg) > canopyTempMax) feasible=.false. + if(stateVecTrial(ixCasNrg) > canopyTempMax) message=trim(message)//'canopy air space temp high,' + if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVecTrial( ixCasNrg )', feasible, canopyTempMax, stateVecTrial(ixCasNrg) + endif + + ! check that the canopy air space temperature is reasonable + if(ixVegNrg/=integerMissing)then + if(stateVecTrial(ixVegNrg) > canopyTempMax) feasible=.false. + if(stateVecTrial(ixVegNrg) > canopyTempMax) message=trim(message)//'canopy temp high,' + if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVecTrial( ixVegNrg )', feasible, canopyTempMax, stateVecTrial(ixVegNrg) + endif + + ! check canopy liquid water is not negative + if(ixVegHyd/=integerMissing)then + if(stateVecTrial(ixVegHyd) < 0._rkind) feasible=.false. + if(stateVecTrial(ixVegHyd) < 0._rkind) message=trim(message)//'canopy water negative,' + if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVecTrial( ixVegHyd )', feasible, 0._rkind, stateVecTrial(ixVegHyd) + end if + + ! check snow temperature is below freezing + if(count(ixSnowOnlyNrg/=integerMissing)>0)then + if(any(stateVecTrial( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. + if(any(stateVecTrial( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) message=trim(message)//'snow temp above freezing,' + do iLayer=1,nSnow + if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVecTrial( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVecTrial( ixSnowOnlyNrg(iLayer) ) + enddo + endif + + ! loop through non-missing hydrology state variables in the snow+soil domain + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) + + ! check the minimum and maximum water constraints + if(ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_liqLayer)then + + ! --> minimum + if (layerType(iLayer) == iname_soil) then + xMin = theta_res(iLayer-nSnow) + else + xMin = 0._rkind + endif + + ! --> maximum + select case( layerType(iLayer) ) + case(iname_snow); xMax = merge(iden_ice, 1._rkind - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) + case(iname_soil); xMax = merge(theta_sat(iLayer-nSnow), theta_sat(iLayer-nSnow) - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) + end select + + ! --> check + if(stateVecTrial( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVecTrial( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. + if(stateVecTrial( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVecTrial( ixSnowSoilHyd(iLayer) ) > xMax) message=trim(message)//'layer water outside bounds,' + if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax + + endif ! if water states + + end do ! loop through non-missing hydrology state variables in the snow+soil domain + + ! early return for non-feasible solutions + if(.not.feasible)then + fluxVec(:) = realMissing + resVec(:) = quadMissing + fEval = realMissing + message=trim(message)//'non-feasible' + err=20; return + endif + + ! get the start and end indices for the soil compression calculations + if(scalarSolution)then + jState = pack(ixControlVolume, ixMapFull2Subset/=integerMissing) + ixBeg = jState(1) + ixEnd = jState(1) + else + ixBeg = 1 + ixEnd = nSoil + endif + + ! initialize to state variable from the last update + scalarCanairTempTrial = scalarCanairTemp + scalarCanopyTempTrial = scalarCanopyTemp + scalarCanopyWatTrial = scalarCanopyWat + scalarCanopyLiqTrial = scalarCanopyLiq + scalarCanopyIceTrial = scalarCanopyIce + mLayerTempTrial = mLayerTemp + mLayerVolFracWatTrial = mLayerVolFracWat + mLayerVolFracLiqTrial = mLayerVolFracLiq + mLayerVolFracIceTrial = mLayerVolFracIce + mLayerMatricHeadTrial = mLayerMatricHead ! total water matric potential + mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq ! liquid water matric potential + scalarAquiferStorageTrial = scalarAquiferStorage + + ! extract variables from the model state vector + call varExtract(& + ! input + stateVecTrial, & ! intent(in): model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) + mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) + ! output: variables for the aquifer + scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! update diagnostic variables and derivatives + call updateVars(& + ! input + .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + lookup_data, & ! intent(in): lookup tables for a local HRU + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output: variables for the vegetation canopy + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! compute enthalpy (J m-3) + if(needEnthalpy)then + call t2enthalpy(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) + ! output: enthalpy + scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + endif ! if computing enthalpy + + ! print the water content + if(globalPrintFlag)then + if(iJac1 Date: Mon, 26 Sep 2022 21:22:25 +0000 Subject: [PATCH 0352/1472] adjusted indentation for eval8summaSundials --- build/source/engine/eval8summaSundials.f90 | 1328 ++++++++++---------- 1 file changed, 661 insertions(+), 667 deletions(-) diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index d31b5a017..0b9ae15fa 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -85,675 +85,669 @@ module eval8summaSundials_module contains - ! ********************************************************************************************************** - ! public subroutine eval8summaSundials: compute the residual vector - ! ********************************************************************************************************** - subroutine eval8summaSundials(& - ! input: model control - dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): entire time step - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - nState, & ! intent(in): total number of state variables - insideIDA, & ! intent(in): flag to indicate if we are inside Sundials solver - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout) flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(inout) flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVec, & ! intent(in): model state vector - stateVecPrime, & ! intent(in): derivative of model state vector - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup data - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) - scalarCanopyTempPrev, & ! intent(in): value of canopy temperature (K) - scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) - scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) - scalarCanopyEnthalpyPrev,& ! intent(in): value for enthalpy of the vegetation canopy (J m-3) - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - mLayerTempPrev, & ! intent(in): vector of layer temperature (K) - mLayerMatricHeadLiqTrial,& ! intent(out): trial value for liquid water matric potential (m) - mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) - mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) - mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - mLayerVolFracWatPrev, & ! intent(in): vector of volumetric total water content (-) - mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) - mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) - mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) - scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) - scalarAquiferStoragePrev,& ! intent(in): value of storage of water in the aquifer (m) - mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) - mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) - ixSaturation, & ! intent(inout): index of the lowest saturated layer - feasible, & ! intent(out): flag to denote the feasibility of the solution - fluxVec, & ! intent(out): flux vector - resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - resVec, & ! intent(out): residual vector - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - ! provide access to subroutines - USE getVectorz_module, only:varExtract ! extract variables from the state vector - USE updateVarsSundials_module, only:updateVarsSundials ! update variables - USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy - USE computFlux_module, only:soilCmpresSundials ! compute soil compression - USE computFlux_module, only:computFlux ! compute fluxes given a state vector - USE computHeatCap_module,only:computHeatCap ! compute heat capacity - USE computHeatCap_module,only:computHeatCapAnalytic ! compute heat capacity - USE computHeatCap_module,only:computCm - USE computHeatCap_module, only:computStatMult - USE computResidSundials_module,only:computResidSundials ! compute residuals given a state vector - USE computThermConduct_module,only:computThermConduct - USE computEnthalpy_module,only:computEnthalpy - USE computEnthalpy_module,only:computEnthalpyPrime - implicit none - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - ! input: model control - real(rkind),intent(in) :: dt_cur - real(rkind),intent(in) :: dt ! time step - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - integer,intent(in) :: nState ! total number of state variables - logical(lgt),intent(in) :: insideIDA ! flag to indicate if we are inside Sundials solver - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall - logical(lgt),intent(inout) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - ! input: state vectors - real(rkind),intent(in) :: stateVec(:) ! model state vector - real(rkind),intent(in) :: stateVecPrime(:) ! model state vector - real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) - ! input: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(zLookup),intent(in) :: lookup_data ! lookup tables - type(var_i), intent(in) :: type_data ! type of vegetation and soil - type(var_d), intent(in) :: attr_data ! spatial attributes - type(var_dlength), intent(in) :: mpar_data ! model parameters - type(var_d), intent(in) :: forc_data ! model forcing data - type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin - type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU - ! output: data structures - type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - ! output: flux and residual vectors - real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind),intent(in) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) - real(rkind),intent(out) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: scalarCanopyIcePrev ! previous value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(out) :: scalarCanopyLiqTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: scalarCanopyLiqPrev ! previous value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(out) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(in) :: scalarCanopyEnthalpyPrev ! previous value of enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(in) :: mLayerTempPrev(:) - real(rkind),intent(out) :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) - real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial value for total water matric potential (m) - real(rkind),intent(in) :: mLayerMatricHeadPrev(:) ! value for total water matric potential (m) - real(rkind),intent(out) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(rkind),intent(in) :: mLayerVolFracWatPrev(:) ! vector of volumetric total water content (-) - real(rkind),intent(out) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric ice water content (-) - real(rkind),intent(in) :: mLayerVolFracIcePrev(:) ! vector of volumetric ice water content (-) - real(rkind),intent(out) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(in) :: mLayerVolFracLiqPrev(:) ! vector of volumetric liquid water content (-) - real(rkind),intent(out) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) - real(rkind),intent(in) :: scalarAquiferStoragePrev ! value of storage of water in the aquifer (m) - real(rkind),intent(in) :: mLayerEnthalpyPrev(:) ! vector of enthalpy for snow+soil layers (J m-3) - real(rkind),intent(out) :: mLayerEnthalpyTrial(:) ! trial vector of enthalpy for snow+soil layers (J m-3) - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer - logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution - real(rkind),intent(out) :: fluxVec(:) ! flux vector - real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation - real(qp),intent(out) :: resVec(:) ! NOTE: qp ! residual vector - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - ! -------------------------------------------------------------------------------------------------------------------------------- - ! state variables - real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - ! derivative of state variables - real(rkind) :: scalarCanairTempPrime ! derivative value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempPrime ! derivative value for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! derivative value for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! derivative value for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! derivative value for liquid water matric potential (m) - real(rkind) :: scalarAquiferStoragePrime ! derivative value of storage of water in the aquifer (m) - ! derivative of diagnostic variables - real(rkind) :: scalarCanopyLiqPrime ! derivative value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIcePrime ! derivative value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! derivative value for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! derivative value for volumetric fraction of ice (-) - ! enthalpy - real(rkind) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthalpyPrime ! enthalpy of each snow+soil layer (J m-3) - ! other local variables - integer(i4b) :: iLayer ! index of model layer in the snow+soil domain - integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain - integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine - integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) - real(rkind) :: xMin,xMax ! minimum and maximum values for water content - real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) - character(LEN=256) :: cmessage ! error message of downwind routine - real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy - real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil - logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step - logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m - - - - ! -------------------------------------------------------------------------------------------------------------------------------- - ! association to variables in the data structures - ! -------------------------------------------------------------------------------------------------------------------------------- - associate(& - ! model decisions - ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ! soil parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) - ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! model state variables - scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - ! soil compression - scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in storage associated with compression of the soil matrix (-) - ! derivatives - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential - dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) - ! mapping - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) - ! indices - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable (nrg) - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable (nrg) - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) - heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & ! intent(out): heat capacity for snow and soil - ) ! association to variables in the data structures - ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="eval8summaSundials/" - feasible=.true. - - ! check the feasibility of the solution - if (.not.insideIDA) then - ! check that the canopy air space temperature is reasonable - if(ixCasNrg/=integerMissing)then - if(stateVec(ixCasNrg) > canopyTempMax) feasible=.false. - if(stateVec(ixCasNrg) > canopyTempMax) message=trim(message)//'canopy air space temp high,' - if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixCasNrg )', feasible, canopyTempMax, stateVec(ixCasNrg) - endif - - ! check that the canopy temperature is reasonable - if(ixVegNrg/=integerMissing)then - if(stateVec(ixVegNrg) > canopyTempMax) feasible=.false. - if(stateVec(ixVegNrg) > canopyTempMax) message=trim(message)//'canopy temp high,' - if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixVegNrg )', feasible, canopyTempMax, stateVec(ixVegNrg) - endif - - ! check canopy liquid water is not negative - if(ixVegHyd/=integerMissing)then - if(stateVec(ixVegHyd) < 0._rkind) feasible=.false. - if(stateVec(ixVegHyd) < 0._rkind) message=trim(message)//'canopy water negative,' - if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVec( ixVegHyd )', feasible, 0._rkind, stateVec(ixVegHyd) - end if - - ! check snow temperature is below freezing - if(count(ixSnowOnlyNrg/=integerMissing)>0)then - if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. - if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) message=trim(message)//'snow temp above freezing,' - do iLayer=1,nSnow - if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVec( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVec( ixSnowOnlyNrg(iLayer) ) - enddo - endif - - ! loop through non-missing hydrology state variables in the snow+soil domain - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) - - ! check the minimum and maximum water constraints - if(ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_liqLayer)then - - ! --> minimum - if (layerType(iLayer) == iname_soil) then - xMin = theta_res(iLayer-nSnow) +! ********************************************************************************************************** +! public subroutine eval8summaSundials: compute the residual vector +! ********************************************************************************************************** +subroutine eval8summaSundials(& + ! input: model control + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): entire time step + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + nState, & ! intent(in): total number of state variables + insideIDA, & ! intent(in): flag to indicate if we are inside Sundials solver + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout) flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(inout) flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVec, & ! intent(in): model state vector + stateVecPrime, & ! intent(in): derivative of model state vector + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup data + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + indx_data, & ! intent(inout): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) + scalarCanopyTempPrev, & ! intent(in): value of canopy temperature (K) + scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) + scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpyPrev,& ! intent(in): value for enthalpy of the vegetation canopy (J m-3) + mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + mLayerTempPrev, & ! intent(in): vector of layer temperature (K) + mLayerMatricHeadLiqTrial,& ! intent(out): trial value for liquid water matric potential (m) + mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) + mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) + mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) + mLayerVolFracWatPrev, & ! intent(in): vector of volumetric total water content (-) + mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) + mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) + mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) + mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) + scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) + scalarAquiferStoragePrev,& ! intent(in): value of storage of water in the aquifer (m) + mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) + mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) + ixSaturation, & ! intent(inout): index of the lowest saturated layer + feasible, & ! intent(out): flag to denote the feasibility of the solution + fluxVec, & ! intent(out): flux vector + resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + resVec, & ! intent(out): residual vector + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- + ! provide access to subroutines + USE getVectorz_module, only:varExtract ! extract variables from the state vector + USE updateVarsSundials_module, only:updateVarsSundials ! update variables + USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy + USE computFlux_module, only:soilCmpresSundials ! compute soil compression + USE computFlux_module, only:computFlux ! compute fluxes given a state vector + USE computHeatCap_module,only:computHeatCap ! compute heat capacity + USE computHeatCap_module,only:computHeatCapAnalytic ! compute heat capacity + USE computHeatCap_module,only:computCm + USE computHeatCap_module, only:computStatMult + USE computResidSundials_module,only:computResidSundials ! compute residuals given a state vector + USE computThermConduct_module,only:computThermConduct + USE computEnthalpy_module,only:computEnthalpy + USE computEnthalpy_module,only:computEnthalpyPrime + implicit none + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + ! input: model control + real(rkind),intent(in) :: dt_cur + real(rkind),intent(in) :: dt ! time step + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + integer,intent(in) :: nState ! total number of state variables + logical(lgt),intent(in) :: insideIDA ! flag to indicate if we are inside Sundials solver + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(inout) :: firstFluxCall + logical(lgt),intent(inout) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + ! input: state vectors + real(rkind),intent(in) :: stateVec(:) ! model state vector + real(rkind),intent(in) :: stateVecPrime(:) ! model state vector + real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + ! input: data structures + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_i), intent(in) :: type_data ! type of vegetation and soil + type(var_d), intent(in) :: attr_data ! spatial attributes + type(var_dlength), intent(in) :: mpar_data ! model parameters + type(var_d), intent(in) :: forc_data ! model forcing data + type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin + type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU + ! output: data structures + type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + ! output: flux and residual vectors + real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind),intent(in) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) + real(rkind),intent(out) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: scalarCanopyIcePrev ! previous value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(out) :: scalarCanopyLiqTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: scalarCanopyLiqPrev ! previous value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(out) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(in) :: scalarCanopyEnthalpyPrev ! previous value of enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(in) :: mLayerTempPrev(:) + real(rkind),intent(out) :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) + real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial value for total water matric potential (m) + real(rkind),intent(in) :: mLayerMatricHeadPrev(:) ! value for total water matric potential (m) + real(rkind),intent(out) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(rkind),intent(in) :: mLayerVolFracWatPrev(:) ! vector of volumetric total water content (-) + real(rkind),intent(out) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric ice water content (-) + real(rkind),intent(in) :: mLayerVolFracIcePrev(:) ! vector of volumetric ice water content (-) + real(rkind),intent(out) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(in) :: mLayerVolFracLiqPrev(:) ! vector of volumetric liquid water content (-) + real(rkind),intent(out) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + real(rkind),intent(in) :: scalarAquiferStoragePrev ! value of storage of water in the aquifer (m) + real(rkind),intent(in) :: mLayerEnthalpyPrev(:) ! vector of enthalpy for snow+soil layers (J m-3) + real(rkind),intent(out) :: mLayerEnthalpyTrial(:) ! trial vector of enthalpy for snow+soil layers (J m-3) + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer + logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution + real(rkind),intent(out) :: fluxVec(:) ! flux vector + real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation + real(qp),intent(out) :: resVec(:) ! NOTE: qp ! residual vector + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + ! -------------------------------------------------------------------------------------------------------------------------------- + ! state variables + real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + ! derivative of state variables + real(rkind) :: scalarCanairTempPrime ! derivative value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempPrime ! derivative value for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! derivative value for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! derivative value for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! derivative value for liquid water matric potential (m) + real(rkind) :: scalarAquiferStoragePrime ! derivative value of storage of water in the aquifer (m) + ! derivative of diagnostic variables + real(rkind) :: scalarCanopyLiqPrime ! derivative value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIcePrime ! derivative value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! derivative value for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! derivative value for volumetric fraction of ice (-) + ! enthalpy + real(rkind) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpyPrime ! enthalpy of each snow+soil layer (J m-3) + ! other local variables + integer(i4b) :: iLayer ! index of model layer in the snow+soil domain + integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain + integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine + integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) + real(rkind) :: xMin,xMax ! minimum and maximum values for water content + real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) + character(LEN=256) :: cmessage ! error message of downwind routine + real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy + real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil + logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step + logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! association to variables in the data structures + ! -------------------------------------------------------------------------------------------------------------------------------- + associate(& + ! model decisions + ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ! soil parameters + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) + ! canopy and layer depth + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! model state variables + scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + ! soil compression + scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in storage associated with compression of the soil matrix (-) + ! derivatives + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) + ! mapping + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) + ! indices + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable (nrg) + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable (nrg) + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) + heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(out): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & ! intent(out): heat capacity for snow and soil + ) ! association to variables in the data structures + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="eval8summaSundials/" + feasible=.true. + + ! check the feasibility of the solution + if (.not.insideIDA) then + ! check that the canopy air space temperature is reasonable + if(ixCasNrg/=integerMissing)then + if(stateVec(ixCasNrg) > canopyTempMax) feasible=.false. + if(stateVec(ixCasNrg) > canopyTempMax) message=trim(message)//'canopy air space temp high,' + if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixCasNrg )', feasible, canopyTempMax, stateVec(ixCasNrg) + endif + + ! check that the canopy temperature is reasonable + if(ixVegNrg/=integerMissing)then + if(stateVec(ixVegNrg) > canopyTempMax) feasible=.false. + if(stateVec(ixVegNrg) > canopyTempMax) message=trim(message)//'canopy temp high,' + if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixVegNrg )', feasible, canopyTempMax, stateVec(ixVegNrg) + endif + + ! check canopy liquid water is not negative + if(ixVegHyd/=integerMissing)then + if(stateVec(ixVegHyd) < 0._rkind) feasible=.false. + if(stateVec(ixVegHyd) < 0._rkind) message=trim(message)//'canopy water negative,' + if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVec( ixVegHyd )', feasible, 0._rkind, stateVec(ixVegHyd) + end if + + ! check snow temperature is below freezing + if(count(ixSnowOnlyNrg/=integerMissing)>0)then + if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. + if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) message=trim(message)//'snow temp above freezing,' + do iLayer=1,nSnow + if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVec( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVec( ixSnowOnlyNrg(iLayer) ) + enddo + endif + + ! loop through non-missing hydrology state variables in the snow+soil domain + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) + + ! check the minimum and maximum water constraints + if(ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_liqLayer)then + + ! --> minimum + if (layerType(iLayer) == iname_soil) then + xMin = theta_res(iLayer-nSnow) + else + xMin = 0._rkind + endif + + ! --> maximum + select case( layerType(iLayer) ) + case(iname_snow); xMax = merge(iden_ice, 1._rkind - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) + case(iname_soil); xMax = merge(theta_sat(iLayer-nSnow), theta_sat(iLayer-nSnow) - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) + end select + + ! --> check + if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. + if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) message=trim(message)//'layer water outside bounds,' + if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax + + endif ! if water states + + end do ! loop through non-missing hydrology state variables in the snow+soil domain + + ! early return for non-feasible solutions + if(.not.feasible)then + fluxVec(:) = realMissing + resVec(:) = quadMissing + message=trim(message)//'non-feasible' + err=20; return + end if + + end if ! ( feasibility check ) + + ! get the start and end indices for the soil compression calculations + if(scalarSolution)then + jState = pack(ixControlVolume, ixMapFull2Subset/=integerMissing) + ixBeg = jState(1) + ixEnd = jState(1) else - xMin = 0._rkind + ixBeg = 1 + ixEnd = nSoil endif - ! --> maximum - select case( layerType(iLayer) ) - case(iname_snow); xMax = merge(iden_ice, 1._rkind - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) - case(iname_soil); xMax = merge(theta_sat(iLayer-nSnow), theta_sat(iLayer-nSnow) - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) - end select - - ! --> check - if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. - if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) message=trim(message)//'layer water outside bounds,' - if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax - - endif ! if water states - - end do ! loop through non-missing hydrology state variables in the snow+soil domain - - ! early return for non-feasible solutions - if(.not.feasible)then - fluxVec(:) = realMissing - resVec(:) = quadMissing - message=trim(message)//'non-feasible' - err=20; return - end if - - end if ! ( feasibility check ) - - ! get the start and end indices for the soil compression calculations - if(scalarSolution)then - jState = pack(ixControlVolume, ixMapFull2Subset/=integerMissing) - ixBeg = jState(1) - ixEnd = jState(1) - else - ixBeg = 1 - ixEnd = nSoil - endif - - ! initialize to state variable from the last update - scalarCanopyTempTrial = scalarCanopyTempPrev - scalarCanopyLiqTrial = scalarCanopyLiqPrev - scalarCanopyIceTrial = scalarCanopyIcePrev - mLayerTempTrial = mLayerTempPrev - mLayerVolFracWatTrial = mLayerVolFracWatPrev - mLayerVolFracLiqTrial = mLayerVolFracLiqPrev - mLayerVolFracIceTrial = mLayerVolFracIcePrev - mLayerMatricHeadTrial = mLayerMatricHeadPrev - scalarAquiferStorageTrial = scalarAquiferStoragePrev - - ! extract variables from the model state vector - call varExtract(& - ! input - stateVec, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! Extract the variables for stateVecPrime - call varExtract(& - ! input - stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy - scalarCanairTempPrime, & ! intent(out): derivative of canopy air temperature (K) - scalarCanopyTempPrime, & ! intent(out): derivative of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(out): derivative of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(out): derivative of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempPrime, & ! intent(out): derivative of layer temperature (K) - mLayerVolFracWatPrime, & ! intent(out): derivative of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(out): derivative of volumetric liquid water content (-) - mLayerMatricHeadPrime, & ! intent(out): derivative of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(out): derivative of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStoragePrime,& ! intent(out): derivative of storage of water in the aquifer (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - - call updateVarsSundials(& - ! input - dt_cur, & - .false., & ! intent(in): logical flag if computing Jacobian for Sundials solver - .false., & ! intent(in): logical flag to adjust temperature to account for the energy - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - mLayerVolFracWatPrev, & ! intent(in): previous vector of total water matric potential (m) - mLayerMatricHeadPrev, & ! intent(in): previous vector of volumetric total water content (-) - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! - mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) - mLayerVolFracIcePrime, & ! - mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! print the water content - if(globalPrintFlag)then - if(iJac1 1e-14_rkind) then - scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur - do concurrent (iLayer=1:nLayers) - mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur - end do - endif ! if dt_cur is not too samll - else ! if using closed formula of heat capacity - call computHeatCapAnalytic(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) - ! input: state variables - scalarCanopyIceTrial, & ! intent(in) - scalarCanopyLiqTrial, & ! intent(in) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): fraction of liquid water at the start of the sub-step (-) - ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - ! output - heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow - ! output: error control - err,cmessage) ! intent(out): error control - endif - - ! compute multiplier of state vector - call computStatMult(& - ! input - heatCapVegTrial, & ! intent(in): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(in): volumetric heat capacity of soil and snow - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output - sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - ! update thermal conductivity - call computThermConduct(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) - ! input: state variables - scalarCanopyIceTrial, & ! intent(in) - scalarCanopyLiqTrial, & ! intent(in) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - ! input/output: data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - err,cmessage) ! intent(out): error control - if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if - - end if ! updateCp - - - if(needCm)then - ! compute C_m - call computCm(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - ! input: state variables - scalarCanopyTempTrial, & ! intent(in) - mLayerTempTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - mLayerMatricHeadTrial, & ! intent(in) - ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - ! output - scalarCanopyCmTrial, & ! intent(out): Cm for vegetation - mLayerCmTrial, & ! intent(out): Cm for soil and snow - err,cmessage) ! intent(out): error control - else - scalarCanopyCmTrial = 0._qp - mLayerCmTrial = 0._qp - end if ! needCm - - - ! save the number of flux calls per time step - indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) + 1 - ! compute the fluxes for a given state vector - call computFlux(& - ! input-output: model control - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to denote the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - insideIDA, & ! intent(in): logical flag if inside Sundials solver - scalarSfcMeltPond/dt, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) - ! input: state variables - scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) - scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) - mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) - mLayerMatricHeadLiqTrial, & ! intent(in): trial value for the liquid water matric potential in each soil layer (m) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - scalarAquiferStorageTrial, & ! intent(in): trial value of storage of water in the aquifer (m) - ! input: diagnostic variables defining the liquid water and ice content - scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) - mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liquid water content in each snow and soil layer (-) - mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) - ! input: data structures - model_decisions, & ! intent(in): model decisions - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): index data - ! input-output: data structures - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - deriv_data, & ! intent(out): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: flux vector and baseflow derivatives - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later in computeJacobSundials - fluxVec, & ! intent(out): flux vector (mixed units) - ! output: error control - err,cmessage) ! intent(out): error code and error message - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - firstSplitOper = .true. - - - ! compute soil compressibility (-) and its derivative w.r.t. matric head (m) - ! NOTE: we already extracted trial matrix head and volumetric liquid water as part of the flux calculations - call soilCmpresSundials(& - ! input: - ixRichards, & ! intent(in): choice of option for Richards' equation - ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers - mLayerMatricHeadPrime(1:nSoil), & ! intent(in): matric head at the start of the time step (m) - mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) - mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric ice content in each soil layer (-) - specificStorage, & ! intent(in): specific storage coefficient (m-1) - theta_sat, & ! intent(in): soil porosity (-) - ! output: - mLayerCompress, & ! intent(inout): compressibility of the soil matrix (-) - dCompress_dPsi, & ! intent(inout): derivative in compressibility w.r.t. matric head (m-1) - err,cmessage) ! intent(out): error code and error message - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - -! print *, 'dt = ', dt -! print *, 'dt_cur = ', dt_cur - - ! compute the residual vector - call computResidSundials(& - ! input: model control - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - ! input: flux vectors - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) - fluxVec, & ! intent(in): flux vector - ! input: state variables (already disaggregated into scalars and vectors) - scalarCanopyTempTrial, & ! intent(in): - mLayerTempTrial, & ! intent(in) - scalarCanairTempPrime, & ! intent(in): Prime value for the temperature of the canopy air space (K) - scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K) - scalarCanopyWatPrime, & - mLayerTempPrime, & ! intent(in): Prime value for the temperature of each snow and soil layer (K) - scalarAquiferStoragePrime, & ! intent(in): Prime value of storage of water in the aquifer (m) - ! input: diagnostic variables defining the liquid water and ice content (function of state variables) - scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2) - scalarCanopyLiqPrime, & ! intent(in): - mLayerVolFracIcePrime, & ! intent(in): Prime value for the volumetric ice in each snow and soil layer (-) - mLayerVolFracWatPrime, & - mLayerVolFracLiqPrime, & - scalarCanopyCmTrial, & ! intent(in) Cm of vegetation canopy - mLayerCmTrial, & ! intent(in) Cm of soil and snow - ! input: data structures - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - flux_data, & ! intent(in): model fluxes for a local HRU - indx_data, & ! intent(in): index data - ! output - resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - resVec, & ! intent(out): residual vector - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - !print *, '=====================================================================================' - - - ! end association with the information in the data structures - end associate - - - end subroutine eval8summaSundials + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! *** compute volumetric heat capacity C_p = dH_T/dT + call computHeatCap(& + ! input: control variables + nLayers, & ! intent(in): number of layers (soil+snow) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) + ! input data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + diag_data, & ! intent(in): model diagnostic variables for a local HRU + ! input: state variables + scalarCanopyIceTrial, & ! intent(in): trial value for canopy ice content (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial temperature + mLayerTempPrev, & ! intent(in): previous temperature + mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil + mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil + ! output + heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + ! to conserve energy compute finite difference approximation of (theta_ice)' + if(dt_cur > 1e-14_rkind) then + scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur + do concurrent (iLayer=1:nLayers) + mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur + end do + endif ! if dt_cur is not too samll + else ! if using closed formula of heat capacity + call computHeatCapAnalytic(& + ! input: control variables + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) + ! input: state variables + scalarCanopyIceTrial, & ! intent(in) + scalarCanopyLiqTrial, & ! intent(in) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): fraction of liquid water at the start of the sub-step (-) + ! input data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + ! output + heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow + ! output: error control + err,cmessage) ! intent(out): error control + endif + + ! compute multiplier of state vector + call computStatMult(& + ! input + heatCapVegTrial, & ! intent(in): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(in): volumetric heat capacity of soil and snow + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output + sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + + ! update thermal conductivity + call computThermConduct(& + ! input: control variables + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) + ! input: state variables + scalarCanopyIceTrial, & ! intent(in) + scalarCanopyLiqTrial, & ! intent(in) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + ! input/output: data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + err,cmessage) ! intent(out): error control + if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if + + end if ! updateCp + + + if(needCm)then + ! compute C_m + call computCm(& + ! input: control variables + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + ! input: state variables + scalarCanopyTempTrial, & ! intent(in) + mLayerTempTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerMatricHeadTrial, & ! intent(in) + ! input data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + ! output + scalarCanopyCmTrial, & ! intent(out): Cm for vegetation + mLayerCmTrial, & ! intent(out): Cm for soil and snow + err,cmessage) ! intent(out): error control + else + scalarCanopyCmTrial = 0._qp + mLayerCmTrial = 0._qp + end if ! needCm + + + ! save the number of flux calls per time step + indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) + 1 + ! compute the fluxes for a given state vector + call computFlux(& + ! input-output: model control + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to denote the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + insideIDA, & ! intent(in): logical flag if inside Sundials solver + scalarSfcMeltPond/dt, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) + ! input: state variables + scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) + scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) + mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) + mLayerMatricHeadLiqTrial, & ! intent(in): trial value for the liquid water matric potential in each soil layer (m) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + scalarAquiferStorageTrial, & ! intent(in): trial value of storage of water in the aquifer (m) + ! input: diagnostic variables defining the liquid water and ice content + scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) + mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liquid water content in each snow and soil layer (-) + mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) + ! input: data structures + model_decisions, & ! intent(in): model decisions + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): index data + ! input-output: data structures + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + deriv_data, & ! intent(out): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: flux vector and baseflow derivatives + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later in computeJacobSundials + fluxVec, & ! intent(out): flux vector (mixed units) + ! output: error control + err,cmessage) ! intent(out): error code and error message + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + firstSplitOper = .true. + + + ! compute soil compressibility (-) and its derivative w.r.t. matric head (m) + ! NOTE: we already extracted trial matrix head and volumetric liquid water as part of the flux calculations + call soilCmpresSundials(& + ! input: + ixRichards, & ! intent(in): choice of option for Richards' equation + ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers + mLayerMatricHeadPrime(1:nSoil), & ! intent(in): matric head at the start of the time step (m) + mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) + mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric ice content in each soil layer (-) + specificStorage, & ! intent(in): specific storage coefficient (m-1) + theta_sat, & ! intent(in): soil porosity (-) + ! output: + mLayerCompress, & ! intent(inout): compressibility of the soil matrix (-) + dCompress_dPsi, & ! intent(inout): derivative in compressibility w.r.t. matric head (m-1) + err,cmessage) ! intent(out): error code and error message + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + + ! compute the residual vector + call computResidSundials(& + ! input: model control + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + ! input: flux vectors + sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + fluxVec, & ! intent(in): flux vector + ! input: state variables (already disaggregated into scalars and vectors) + scalarCanopyTempTrial, & ! intent(in): + mLayerTempTrial, & ! intent(in) + scalarCanairTempPrime, & ! intent(in): Prime value for the temperature of the canopy air space (K) + scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K) + scalarCanopyWatPrime, & + mLayerTempPrime, & ! intent(in): Prime value for the temperature of each snow and soil layer (K) + scalarAquiferStoragePrime, & ! intent(in): Prime value of storage of water in the aquifer (m) + ! input: diagnostic variables defining the liquid water and ice content (function of state variables) + scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2) + scalarCanopyLiqPrime, & ! intent(in): + mLayerVolFracIcePrime, & ! intent(in): Prime value for the volumetric ice in each snow and soil layer (-) + mLayerVolFracWatPrime, & + mLayerVolFracLiqPrime, & + scalarCanopyCmTrial, & ! intent(in) Cm of vegetation canopy + mLayerCmTrial, & ! intent(in) Cm of soil and snow + ! input: data structures + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + flux_data, & ! intent(in): model fluxes for a local HRU + indx_data, & ! intent(in): index data + ! output + resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + resVec, & ! intent(out): residual vector + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + + + ! end association with the information in the data structures + end associate + +end subroutine eval8summaSundials end module eval8summaSundials_module From 2de3b02ad36c219c6d244f1d09a5ba48aedd82a3 Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 26 Sep 2022 21:42:04 +0000 Subject: [PATCH 0353/1472] adjusted computJacobSundials indentation --- build/source/engine/computJacobSundials.f90 | 1810 +++++++++---------- 1 file changed, 875 insertions(+), 935 deletions(-) diff --git a/build/source/engine/computJacobSundials.f90 b/build/source/engine/computJacobSundials.f90 index e7abbc8b1..09a36cea9 100644 --- a/build/source/engine/computJacobSundials.f90 +++ b/build/source/engine/computJacobSundials.f90 @@ -85,984 +85,924 @@ module computJacobSundials_module public::computJacobSundials contains - ! ********************************************************************************************************** - ! public subroutine computJacobSundials: compute the Jacobian matrix - ! ********************************************************************************************************** - subroutine computJacobSundials(& - ! input: model control - cj, & ! intent(in): this scalar changes whenever the step size or method order changes - dt, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - computeBaseflow, & ! intent(in): flag to indicate if we need to compute baseflow - ixMatrix, & ! intent(in): form of the Jacobian matrix - specificStorage, & ! intent(in): specific storage coefficient (m-1) - theta_sat, & ! intent(in): soil porosity (-) - ixRichards, & ! intent(in): choice of option for Richards' equation - ! input: data structures - indx_data, & ! intent(in): index data - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! input: state variables - mLayerTemp, & ! intent(in): vector of layer temperature (K) - mLayerTempPrime, & ! intent(in): vector of derivative value for layer temperature (K) - mLayerMatricHeadPrime, & ! intent(in) - mLayerMatricHeadLiqPrime, & ! intent(in) - mLayerVolFracWatPrime, & ! intent(in) - scalarCanopyTemp, & ! intent(in): temperature of the vegetation canopy (K) - scalarCanopyTempPrime, & ! intent(in): derivative value for temperature of the vegetation canopy (K) - scalarCanopyWatPrime, & ! intent(in) - ! input-output: Jacobian and its diagonal - dMat, & ! intent(inout): diagonal of the Jacobian matrix - aJac, & ! intent(out): Jacobian matrix - ! output: error control - err,message) ! intent(out): error code and error message - ! ----------------------------------------------------------------------------------------------------------------- - implicit none - ! input: model control - real(rkind),intent(in) :: cj - real(rkind),intent(in) :: dt ! length of the time step (seconds) - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: computeBaseflow ! flag to indicate if computing baseflow - integer(i4b),intent(in) :: ixMatrix ! form of the Jacobian matrix - real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) - real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) - integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation - ! input: data structures - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - ! input: state variables - real(rkind),intent(in) :: mLayerTemp(:) - real(rkind),intent(in) :: mLayerTempPrime(:) - real(rkind),intent(in) :: mLayerMatricHeadPrime(:) - real(rkind),intent(in) :: mLayerMatricHeadLiqPrime(:) - real(rkind),intent(in) :: mLayerVolFracWatPrime(:) - real(rkind),intent(in) :: scalarCanopyTemp - real(rkind),intent(in) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) - real(rkind),intent(in) :: scalarCanopyWatPrime - ! input-output: Jacobian and its diagonal - real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix - real(rkind),intent(out) :: aJac(:,:) ! Jacobian matrix - ! output variables - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------- - ! * local variables - ! -------------------------------------------------------------- - ! indices of model state variables - integer(i4b) :: jState ! index of state within the state subset - integer(i4b) :: qState ! index of cross-derivative state variable for baseflow - integer(i4b) :: nrgState ! energy state variable - integer(i4b) :: watState ! hydrology state variable - integer(i4b) :: nState ! number of state variables - ! indices of model layers - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: jLayer ! index of model layer within the full state vector (hydrology) - integer(i4b) :: pLayer ! indices of soil layers (used for the baseflow derivatives) - ! conversion factors - real(rkind) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative - ! -------------------------------------------------------------- - ! associate variables from data structures - associate(& - ! indices of model state variables - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain - ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer - ! vectors of indices for specfic state types within specific sub-domains IN THE FULL STATE VECTOR - ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain - ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain - ! vector of energy indices for the snow and soil domains - ! NOTE: states not in the subset are equal to integerMissing - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain - ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow domain - ixSoilOnlyNrg => indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the soil domain - ! vector of hydrology indices for the snow and soil domains - ! NOTE: states not in the subset are equal to integerMissing - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow domain - ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the soil domain - ! number of state variables of a specific type - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain - nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain - nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain - ! type and index of model control volume - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain - ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the domain (iname_veg, iname_snow, iname_soil) - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for specific model domains - ! mapping between states and model layers - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] list of indices in the full state vector that are in the state subset - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset in each element of the full state vector - ! derivatives in net vegetation energy fluxes w.r.t. relevant state variables - dCanairNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. canopy air temperature - dCanairNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. canopy temperature - dCanairNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dGroundTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. ground temperature - dCanopyNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. canopy air temperature - dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. canopy temperature - dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. ground temperature - dCanopyNetFlux_dCanWat => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in net canopy fluxes w.r.t. canopy total water content - dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net ground flux w.r.t. canopy air temperature - dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net ground flux w.r.t. canopy temperature - dGroundNetFlux_dCanWat => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in net ground fluxes w.r.t. canopy total water content - ! derivatives in evaporative fluxes w.r.t. relevant state variables - dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy air temperature - dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy temperature - dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. ground temperature - dCanopyEvaporation_dCanWat => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy total water content - dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy air temperature - dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy temperature - dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. ground temperature - dGroundEvaporation_dCanWat => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy total water content - ! derivatives in canopy water w.r.t canopy temperature - dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(in): [dp] derivative in canopy liquid storage w.r.t. temperature - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative in volumetric liquid water content w.r.t. temperature - d2Theta_dTkCanopy2 => deriv_data%var(iLookDERIV%d2Theta_dTkCanopy2 )%dat(1) ,& ! intent(in): [dp] second derivative of volumetric liquid water content w.r.t. temperature - dFracLiqVeg_dTkCanopy => deriv_data%var(iLookDERIV%dFracLiqVeg_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative in fraction of (throughfall + drainage) w.r.t. temperature - ! derivatives in canopy liquid fluxes w.r.t. canopy water - scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ,& ! intent(in): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water - ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below - dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above - dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below - ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below - dNrgFlux_dWatAbove => deriv_data%var(iLookDERIV%dNrgFlux_dWatAbove )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. water state in the layer above - dNrgFlux_dWatBelow => deriv_data%var(iLookDERIV%dNrgFlux_dWatBelow )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. water state in the layer below - ! derivatives in soil transpiration w.r.t. canopy state variables - mLayerdTrans_dTCanair => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanair )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature - mLayerdTrans_dTCanopy => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanopy )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. canopy temperature - mLayerdTrans_dTGround => deriv_data%var(iLookDERIV%mLayerdTrans_dTGround )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. ground temperature - mLayerdTrans_dCanWat => deriv_data%var(iLookDERIV%mLayerdTrans_dCanWat )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. canopy total water - ! derivatives in aquifer transpiration w.r.t. canopy state variables - dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature - dAquiferTrans_dTCanopy => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanopy )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. canopy temperature - dAquiferTrans_dTGround => deriv_data%var(iLookDERIV%dAquiferTrans_dTGround )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. ground temperature - dAquiferTrans_dCanWat => deriv_data%var(iLookDERIV%dAquiferTrans_dCanWat )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. canopy total water - ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above - iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(in): [dp(:)] derivative in vertical liquid water flux at layer interfaces - ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential - d2VolTot_d2Psi0 => deriv_data%var(iLookDERIV%d2VolTot_d2Psi0 )%dat ,& ! intent(in): [dp(:)] second derivative in total water content w.r.t. total water matric potential - dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t matric head - dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above - dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below - dq_dHydStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dHydStateLayerSurfVec )%dat ,& ! intent(in): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers - ! derivative in baseflow flux w.r.t. aquifer storage - dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(in): [dp(:)] derivative in baseflow flux w.r.t. aquifer storage (s-1) - ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables - dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above - dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below - dq_dNrgStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dNrgStateLayerSurfVec )%dat ,& ! intent(in): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers - ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature - dFracLiqSnow_dTk => deriv_data%var(iLookDERIV%dFracLiqSnow_dTk )%dat ,& ! intent(in): [dp(:)] derivative in fraction of liquid snow w.r.t. temperature - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk )%dat ,& ! intent(in): [dp(:)] derivative in volumetric liquid water content w.r.t. temperature - mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2 )%dat ,& ! intent(in): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature - ! derivate in bulk heat capacity w.r.t. relevant state variables - dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. temperature - ! diagnostic variables - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg )%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) - scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg )%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow )%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) - mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk )%dat ,& ! intent(in): [dp(:)] bulk volumetric heat capacity in each snow and soil layer (J m-3 K-1) - scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1) ,& ! intent(in): [dp] soil control on infiltration, zero or one - ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth )%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth )%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - layerType => indx_data%var(iLookINDEX%layerType )%dat & ! intent(in): [i4b(:)] named variables defining the type of layer in snow+soil domain - ) ! making association with data in structures - ! -------------------------------------------------------------- - ! initialize error control - err=0; message='computJacobSundials/' - - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - ! * PART 0: PRELIMINARIES (INITIALIZE JACOBIAN AND COMPUTE TIME-VARIABLE DIAGONAL TERMS) - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - - ! get the number of state variables - nState = size(dMat) - - ! initialize the Jacobian - ! NOTE: this needs to be done every time, since Jacobian matrix is modified in the solver - aJac(:,:) = 0._rkind ! analytical Jacobian matrix - - ! compute terms in the Jacobian for vegetation (excluding fluxes) - ! NOTE: energy for vegetation is computed *within* the iteration loop as it includes phase change - if(ixVegNrg/=integerMissing)then - dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy ) * cj & - + dVolHtCapBulk_dTkCanopy * scalarCanopyTempPrime & - + LH_fus*iden_water * scalarCanopyTempPrime * d2Theta_dTkCanopy2 & - + LH_fus * dFracLiqVeg_dTkCanopy * scalarCanopyWatPrime / canopyDepth - endif - - ! compute additional terms for the Jacobian for the snow-soil domain (excluding fluxes) - ! NOTE: energy for snow+soil is computed *within* the iteration loop as it includes phase change - do iLayer=1,nLayers - if(ixSnowSoilNrg(iLayer)/=integerMissing)then - dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj & - + dVolHtCapBulk_dTk(iLayer) * mLayerTempPrime(iLayer) & - + LH_fus*iden_water * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & - + LH_fus*iden_water * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) - endif - end do - - ! compute additional terms for the Jacobian for the soil domain (excluding fluxes) - do iLayer=1,nSoil - if(ixSoilOnlyHyd(iLayer)/=integerMissing)then - dMat(ixSoilOnlyHyd(iLayer)) = ( dVolTot_dPsi0(iLayer) + dCompress_dPsi(iLayer) ) * cj + d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) - - if(ixRichards==mixdform)then - dMat(ixSoilOnlyHyd(iLayer)) = dMat(ixSoilOnlyHyd(iLayer)) + specificStorage * dVolTot_dPsi0(iLayer) * mLayerMatricHeadPrime(iLayer) / theta_sat(iLayer) - endif - - endif - end do - - ! define the form of the matrix - select case(ixMatrix) - - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - ! * PART 1: BAND MATRIX - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - case(ixBandMatrix) - ! check - if(size(aJac,1)/=nBands .or. size(aJac,2)/=size(dMat))then - message=trim(message)//'unexpected shape of the Jacobian matrix: expect aJac(nBands,nState)' - err=20; return - endif - - ! ----- - ! * energy and liquid fluxes over vegetation... - ! --------------------------------------------- - if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) - - ! * energy fluxes with the canopy water - if(ixVegHyd/=integerMissing)then - - ! * cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) - if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixCasNrg),ixCasNrg) = -dCanopyEvaporation_dTCanair*dt - ! dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy is the derivative in throughfall and canopy drainage with canopy temperature - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixVegNrg),ixVegNrg) = -dCanopyEvaporation_dTCanopy*dt + dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy - ! * liquid water fluxes for vegetation canopy (-), dt*scalarFracLiqVeg*scalarCanopyLiqDeriv is the derivative in throughfall and canopy drainage with canopy water - aJac(ixDiag, ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanWat - scalarCanopyLiqDeriv)*dt + 1._rkind * cj - if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixTopNrg),ixTopNrg) = -dCanopyEvaporation_dTGround*dt - - ! * cross-derivative terms w.r.t. canopy water (kg-1 m2) - if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water - - ! * cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) - ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth * cj & - + dVolHtCapBulk_dCanWat * scalarCanopyTempPrime - (dt/canopyDepth) * dCanopyNetFlux_dCanWat & - + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth ! dF/dLiq - if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) - endif - - ! * -derivative terms w.r.t. canopy temperature (K-1) - if(ixVegNrg/=integerMissing)then - if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarCanopyLiqDeriv*dCanLiq_dTcanopy)/iden_water - endif - - ! * energy fluxes with the canopy air space (J m-3 K-1) - if(ixCasNrg/=integerMissing)then - aJac(ixDiag, ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) * cj - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixCasNrg,ixVegNrg),ixVegNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanopyTemp) - if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixCasNrg,ixTopNrg),ixTopNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dGroundTemp) - endif - - ! * energy fluxes with the vegetation canopy (J m-3 K-1) - if(ixVegNrg/=integerMissing)then - if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixCasNrg),ixCasNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanairTemp) - aJac(ixDiag, ixVegNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanopyTemp) + dMat(ixVegNrg) - if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixTopNrg),ixTopNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dGroundTemp) - endif - - ! * energy fluxes with the surface (J m-3 K-1) - if(ixTopNrg/=integerMissing)then - if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanairTemp) - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanopyTemp) - endif - - endif ! if there is a need to compute energy fluxes within vegetation - - ! ----- - ! * energy fluxes for the snow+soil domain... - ! ------------------------------------------- - if(nSnowSoilNrg>0)then - do iLayer=1,nLayers ! loop through all layers in the snow+soil domain - - ! check if the state is in the subset - if(ixSnowSoilNrg(iLayer)==integerMissing) cycle - - ! - define index within the state subset and the full state vector - jState = ixSnowSoilNrg(iLayer) ! index within the state subset - - ! - diagonal elements - aJac(ixDiag,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) - - ! - lower-diagonal elements - if(iLayer>1)then - if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowSoilNrg(iLayer-1),jState),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) - endif - - ! - upper diagonal elements - if(iLayer0)then - do iLayer=1,nSnow ! loop through layers in the snow domain - - ! - check that the snow layer is desired - if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle - - ! - define state indices for the current layer - watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - - ! compute factor to convert liquid water derivative to total water derivative - select case( ixHydType(iLayer) ) - case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) - case default; convLiq2tot = 1._rkind - end select - - ! - diagonal elements - aJac(ixDiag,watState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot + dMat(watState) * cj - - ! - lower-diagonal elements - if(iLayer>1)then - if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyHyd(iLayer-1),watState),watState) = 0._rkind ! sub-diagonal: no dependence on other layers - endif - - ! - upper diagonal elements - if(iLayer0 .and. nSnowOnlyNrg>0)then - do iLayer=1,nSnow ! loop through layers in the snow domain - - ! - check that the snow layer is desired - if(ixSnowOnlyNrg(iLayer)==integerMissing) cycle - - ! (define the energy state) - nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector - - ! - define state indices for the current layer - watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - - if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) - - ! - include derivatives of energy fluxes w.r.t water fluxes for current layer - aJac(ixOffDiag(nrgState,watState),watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & - + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) & - + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) & - + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) - - ! - include derivatives of water fluxes w.r.t energy fluxes for current layer - aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) - - ! (cross-derivative terms for the layer below) - if(iLayer1)then - if(ixSnowOnlyNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyNrg(iLayer-1),watState),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) ) - endif - - ! (cross-derivative terms for the layer below) - if(iLayer0)then !bottom snow layer and there is soil below - if(ixSoilOnlyNrg(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyNrg(1),watState),watState) = (dt/mLayerDepth(nSnow+1))*(-dNrgFlux_dWatAbove(nSnow) ) - endif - - endif ! (if the energy state for the current layer is within the state subset) - - end do ! (looping through snow layers) - endif ! (if there are state variables for both water and energy in the snow domain) - - ! ----- - ! * liquid water fluxes for the soil domain... - ! -------------------------------------------- - if(nSoilOnlyHyd>0)then - do iLayer=1,nSoil - - ! - check that the soil layer is desired - if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle - - ! - define state indices - watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset - - ! - define indices of the soil layers - jLayer = iLayer+nSnow ! index of layer in the snow+soil vector - - ! - compute the diagonal elements - ! all terms *excluding* baseflow - aJac(ixDiag,watState) = (dt/mLayerDepth(jLayer))*(-dq_dHydStateBelow(iLayer-1) + dq_dHydStateAbove(iLayer)) + dMat(watState) - - ! - compute the lower-diagonal elements - if(iLayer>1)then - if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),watState),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) - endif - - ! - compute the upper-diagonal elements - if(iLayer0)then !have snow above first soil layer - ! if(ixSnowOnlyHyd(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) - !elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) - ! if(ixVegHyd/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) - !endif - - endif ! (if the subset includes hydrology state variables in the soil domain) - - ! ----- - ! * liquid water fluxes for the aquifer... - ! ---------------------------------------- - if(ixAqWat/=integerMissing) then - aJac(ixDiag,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) * cj - aJac(ixOffDiag(ixAqWat,ixSoilOnlyNrg(nSoil)),ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk - aJac(ixOffDiag(ixAqWat,ixSoilOnlyHyd(nSoil)),ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat - ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) - !if(computeVegFlux)then - ! aJac(ixOffDiag(ixAqWat,ixCasNrg),ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) - ! aJac(ixOffDiag(ixAqWat,ixVegNrg),ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) - ! aJac(ixOffDiag(ixAqWat,ixVegHyd),ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 - ! aJac(ixOffDiag(ixAqWat,ixTopNrg),ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) - !endif - endif - - ! ----- - ! * cross derivatives in the soil domain... - ! ---------------------------------------- - if(nSoilOnlyHyd>0 .and. nSoilOnlyNrg>0)then - do iLayer=1,nSoilOnlyNrg - - ! - check that the soil layer is desired - if(ixSoilOnlyNrg(iLayer)==integerMissing) cycle - - ! - define indices of the soil layers - jLayer = iLayer+nSnow ! index of layer in the snow+soil vector - - ! - define the energy state variable - nrgState = ixNrgLayer(jLayer) ! index within the full state vector - - ! - define index of hydrology state variable within the state subset - watState = ixSoilOnlyHyd(iLayer) - - ! only compute derivatives if the water state for the current layer is within the state subset - if(watstate/=integerMissing)then - - ! - include derivatives in liquid water fluxes w.r.t. temperature for current layer - aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance - - ! - compute lower diagonal elements - if(iLayer>1)then - if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),nrgState),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 - endif - - ! compute upper-diagonal elements - if(iLayer verySmall)then ! ice is present - aJac(ixOffDiag(nrgState,watState),watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) * cj & - - LH_fus*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_d2Psi0(iLayer) + aJac(ixOffDiag(nrgState,watState),watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content - endif - - ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above - if(iLayer>1)then - if(ixSoilOnlyNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyNrg(iLayer-1),watState),watState) = (dt/mLayerDepth(jLayer-1))*( dNrgFlux_dWatBelow(jLayer-1) ) - elseif(iLayer==1 .and. nSnowOnlyNrg>0)then !top soil layer and there is snow above - if(ixSnowOnlyNrg(nSnow)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyNrg(nSnow),watState),watState) = (dt/mLayerDepth(nSnow))*( dNrgFlux_dWatBelow(nSnow) ) - endif - - ! (cross-derivative terms for the layer below) - if(iLayer0)then !have snow above first soil layer - ! if(ixSnowOnlyNrg(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) - !elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) - ! if(ixVegNrg/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) - !endif - - endif ! (if there are state variables for both water and energy in the soil domain) - - if(globalPrintFlag)then - print*, '** banded analytical Jacobian:' - write(*,'(a4,1x,100(i17,1x))') 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) - do iLayer=kl+1,nBands - write(*,'(i4,1x,100(e17.10,1x))') iLayer, (aJac(iLayer,jLayer),jLayer=min(iJac1,nState),min(iJac2,nState)) - end do - endif - - !print*, '** banded analytical Jacobian:' - !write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=1,size(aJac,2)) - !do iLayer=1,size(aJac,1) - ! write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(iLayer,1:size(aJac,2)) - !end do - !print *, '--------------------------------------------------------------' - - !print*, 'PAUSE: banded analytical Jacobian'; read(*,*) - - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - ! * PART 2: FULL MATRIX - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - case(ixFullMatrix) - - ! check - if(size(aJac,1)/=size(dMat) .or. size(aJac,2)/=size(dMat))then - message=trim(message)//'unexpected shape of the Jacobian matrix: expect aJac(nState,nState)' - err=20; return - endif - - ! ----- - ! * energy and liquid fluxes over vegetation... - ! --------------------------------------------- - if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) - - ! * energy fluxes with the canopy water - if(ixVegHyd/=integerMissing)then - - ! * cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) - if(ixCasNrg/=integerMissing) aJac(ixVegHyd,ixCasNrg) = -dCanopyEvaporation_dTCanair*dt - ! dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy is the derivative in throughfall and canopy drainage with canopy temperature - if(ixVegNrg/=integerMissing) aJac(ixVegHyd,ixVegNrg) = -dCanopyEvaporation_dTCanopy*dt + dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy - ! * liquid water fluxes for vegetation canopy (-), dt*scalarFracLiqVeg*scalarCanopyLiqDeriv is the derivative in throughfall and canopy drainage with canopy water - aJac(ixVegHyd,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanWat - scalarCanopyLiqDeriv)*dt + 1._rkind * cj - if(ixTopNrg/=integerMissing) aJac(ixVegHyd,ixTopNrg) = -dCanopyEvaporation_dTGround*dt - - ! * cross-derivative terms w.r.t. canopy water (kg-1 m2) - if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water - - ! * cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) - ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth * cj & - + dVolHtCapBulk_dCanWat * scalarCanopyTempPrime - (dt/canopyDepth) * dCanopyNetFlux_dCanWat & - + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth ! dF/dLiq - if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) - endif - - ! * -derivative terms w.r.t. canopy temperature (K-1) - if(ixVegNrg/=integerMissing)then - if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegNrg) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarCanopyLiqDeriv*dCanLiq_dTcanopy)/iden_water - endif - - ! * energy fluxes with the canopy air space (J m-3 K-1) - if(ixCasNrg/=integerMissing)then - aJac(ixCasNrg,ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) * cj - if(ixVegNrg/=integerMissing) aJac(ixCasNrg,ixVegNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanopyTemp) - if(ixTopNrg/=integerMissing) aJac(ixCasNrg,ixTopNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dGroundTemp) - endif - - ! * energy fluxes with the vegetation canopy (J m-3 K-1) +! ********************************************************************************************************** +! public subroutine computJacobSundials: compute the Jacobian matrix +! ********************************************************************************************************** +subroutine computJacobSundials(& + ! input: model control + cj, & ! intent(in): this scalar changes whenever the step size or method order changes + dt, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + computeBaseflow, & ! intent(in): flag to indicate if we need to compute baseflow + ixMatrix, & ! intent(in): form of the Jacobian matrix + specificStorage, & ! intent(in): specific storage coefficient (m-1) + theta_sat, & ! intent(in): soil porosity (-) + ixRichards, & ! intent(in): choice of option for Richards' equation + ! input: data structures + indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables + dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! input: state variables + mLayerTemp, & ! intent(in): vector of layer temperature (K) + mLayerTempPrime, & ! intent(in): vector of derivative value for layer temperature (K) + mLayerMatricHeadPrime, & ! intent(in) + mLayerMatricHeadLiqPrime, & ! intent(in) + mLayerVolFracWatPrime, & ! intent(in) + scalarCanopyTemp, & ! intent(in): temperature of the vegetation canopy (K) + scalarCanopyTempPrime, & ! intent(in): derivative value for temperature of the vegetation canopy (K) + scalarCanopyWatPrime, & ! intent(in) + ! input-output: Jacobian and its diagonal + dMat, & ! intent(inout): diagonal of the Jacobian matrix + aJac, & ! intent(out): Jacobian matrix + ! output: error control + err,message) ! intent(out): error code and error message + ! ----------------------------------------------------------------------------------------------------------------- + implicit none + ! input: model control + real(rkind),intent(in) :: cj + real(rkind),intent(in) :: dt ! length of the time step (seconds) + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: computeBaseflow ! flag to indicate if computing baseflow + integer(i4b),intent(in) :: ixMatrix ! form of the Jacobian matrix + real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) + real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) + integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation + ! input: data structures + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + ! input: state variables + real(rkind),intent(in) :: mLayerTemp(:) + real(rkind),intent(in) :: mLayerTempPrime(:) + real(rkind),intent(in) :: mLayerMatricHeadPrime(:) + real(rkind),intent(in) :: mLayerMatricHeadLiqPrime(:) + real(rkind),intent(in) :: mLayerVolFracWatPrime(:) + real(rkind),intent(in) :: scalarCanopyTemp + real(rkind),intent(in) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) + real(rkind),intent(in) :: scalarCanopyWatPrime + ! input-output: Jacobian and its diagonal + real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix + real(rkind),intent(out) :: aJac(:,:) ! Jacobian matrix + ! output variables + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------- + ! * local variables + ! -------------------------------------------------------------- + ! indices of model state variables + integer(i4b) :: jState ! index of state within the state subset + integer(i4b) :: qState ! index of cross-derivative state variable for baseflow + integer(i4b) :: nrgState ! energy state variable + integer(i4b) :: watState ! hydrology state variable + integer(i4b) :: nState ! number of state variables + ! indices of model layers + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: jLayer ! index of model layer within the full state vector (hydrology) + integer(i4b) :: pLayer ! indices of soil layers (used for the baseflow derivatives) + ! conversion factors + real(rkind) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative + ! -------------------------------------------------------------- + ! associate variables from data structures + associate(& + ! indices of model state variables + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain + ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer + ! vectors of indices for specfic state types within specific sub-domains IN THE FULL STATE VECTOR + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + ! vector of energy indices for the snow and soil domains + ! NOTE: states not in the subset are equal to integerMissing + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow domain + ixSoilOnlyNrg => indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the soil domain + ! vector of hydrology indices for the snow and soil domains + ! NOTE: states not in the subset are equal to integerMissing + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow domain + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the soil domain + ! number of state variables of a specific type + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain + nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + ! type and index of model control volume + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain + ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the domain (iname_veg, iname_snow, iname_soil) + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for specific model domains + ! mapping between states and model layers + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] list of indices in the full state vector that are in the state subset + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset in each element of the full state vector + ! derivatives in net vegetation energy fluxes w.r.t. relevant state variables + dCanairNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. canopy air temperature + dCanairNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. canopy temperature + dCanairNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dGroundTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. ground temperature + dCanopyNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. canopy air temperature + dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. canopy temperature + dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. ground temperature + dCanopyNetFlux_dCanWat => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in net canopy fluxes w.r.t. canopy total water content + dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net ground flux w.r.t. canopy air temperature + dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net ground flux w.r.t. canopy temperature + dGroundNetFlux_dCanWat => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in net ground fluxes w.r.t. canopy total water content + ! derivatives in evaporative fluxes w.r.t. relevant state variables + dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy air temperature + dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy temperature + dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. ground temperature + dCanopyEvaporation_dCanWat => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy total water content + dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy air temperature + dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy temperature + dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. ground temperature + dGroundEvaporation_dCanWat => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy total water content + ! derivatives in canopy water w.r.t canopy temperature + dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(in): [dp] derivative in canopy liquid storage w.r.t. temperature + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative in volumetric liquid water content w.r.t. temperature + d2Theta_dTkCanopy2 => deriv_data%var(iLookDERIV%d2Theta_dTkCanopy2 )%dat(1) ,& ! intent(in): [dp] second derivative of volumetric liquid water content w.r.t. temperature + dFracLiqVeg_dTkCanopy => deriv_data%var(iLookDERIV%dFracLiqVeg_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative in fraction of (throughfall + drainage) w.r.t. temperature + ! derivatives in canopy liquid fluxes w.r.t. canopy water + scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ,& ! intent(in): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water + ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below + dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above + dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below + ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below + dNrgFlux_dWatAbove => deriv_data%var(iLookDERIV%dNrgFlux_dWatAbove )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. water state in the layer above + dNrgFlux_dWatBelow => deriv_data%var(iLookDERIV%dNrgFlux_dWatBelow )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. water state in the layer below + ! derivatives in soil transpiration w.r.t. canopy state variables + mLayerdTrans_dTCanair => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanair )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + mLayerdTrans_dTCanopy => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanopy )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. canopy temperature + mLayerdTrans_dTGround => deriv_data%var(iLookDERIV%mLayerdTrans_dTGround )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. ground temperature + mLayerdTrans_dCanWat => deriv_data%var(iLookDERIV%mLayerdTrans_dCanWat )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. canopy total water + ! derivatives in aquifer transpiration w.r.t. canopy state variables + dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + dAquiferTrans_dTCanopy => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanopy )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. canopy temperature + dAquiferTrans_dTGround => deriv_data%var(iLookDERIV%dAquiferTrans_dTGround )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. ground temperature + dAquiferTrans_dCanWat => deriv_data%var(iLookDERIV%dAquiferTrans_dCanWat )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. canopy total water + ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above + iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(in): [dp(:)] derivative in vertical liquid water flux at layer interfaces + ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + d2VolTot_d2Psi0 => deriv_data%var(iLookDERIV%d2VolTot_d2Psi0 )%dat ,& ! intent(in): [dp(:)] second derivative in total water content w.r.t. total water matric potential + dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t matric head + dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above + dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + dq_dHydStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dHydStateLayerSurfVec )%dat ,& ! intent(in): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers + ! derivative in baseflow flux w.r.t. aquifer storage + dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(in): [dp(:)] derivative in baseflow flux w.r.t. aquifer storage (s-1) + ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables + dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above + dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + dq_dNrgStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dNrgStateLayerSurfVec )%dat ,& ! intent(in): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers + ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature + dFracLiqSnow_dTk => deriv_data%var(iLookDERIV%dFracLiqSnow_dTk )%dat ,& ! intent(in): [dp(:)] derivative in fraction of liquid snow w.r.t. temperature + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk )%dat ,& ! intent(in): [dp(:)] derivative in volumetric liquid water content w.r.t. temperature + mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2 )%dat ,& ! intent(in): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature + ! derivate in bulk heat capacity w.r.t. relevant state variables + dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. temperature + ! diagnostic variables + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg )%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg )%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow )%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk )%dat ,& ! intent(in): [dp(:)] bulk volumetric heat capacity in each snow and soil layer (J m-3 K-1) + scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1) ,& ! intent(in): [dp] soil control on infiltration, zero or one + ! canopy and layer depth + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth )%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth )%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + layerType => indx_data%var(iLookINDEX%layerType )%dat & ! intent(in): [i4b(:)] named variables defining the type of layer in snow+soil domain + ) ! making association with data in structures + ! -------------------------------------------------------------- + ! initialize error control + err=0; message='computJacobSundials/' + + ! ********************************************************************************************************************************************************* + ! ********************************************************************************************************************************************************* + ! * PART 0: PRELIMINARIES (INITIALIZE JACOBIAN AND COMPUTE TIME-VARIABLE DIAGONAL TERMS) + ! ********************************************************************************************************************************************************* + ! ********************************************************************************************************************************************************* + + ! get the number of state variables + nState = size(dMat) + + ! initialize the Jacobian + ! NOTE: this needs to be done every time, since Jacobian matrix is modified in the solver + aJac(:,:) = 0._rkind ! analytical Jacobian matrix + + ! compute terms in the Jacobian for vegetation (excluding fluxes) + ! NOTE: energy for vegetation is computed *within* the iteration loop as it includes phase change if(ixVegNrg/=integerMissing)then - if(ixCasNrg/=integerMissing) aJac(ixVegNrg,ixCasNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanairTemp) - aJac(ixVegNrg,ixVegNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanopyTemp) + dMat(ixVegNrg) - if(ixTopNrg/=integerMissing) aJac(ixVegNrg,ixTopNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dGroundTemp) + dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy ) * cj & + + dVolHtCapBulk_dTkCanopy * scalarCanopyTempPrime & + + LH_fus*iden_water * scalarCanopyTempPrime * d2Theta_dTkCanopy2 & + + LH_fus * dFracLiqVeg_dTkCanopy * scalarCanopyWatPrime / canopyDepth endif - ! * energy fluxes with the surface (J m-3 K-1) - if(ixTopNrg/=integerMissing)then - if(ixCasNrg/=integerMissing) aJac(ixTopNrg,ixCasNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanairTemp) - if(ixVegNrg/=integerMissing) aJac(ixTopNrg,ixVegNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanopyTemp) - endif - - endif ! if there is a need to compute energy fluxes within vegetation - - ! ----- - ! * energy fluxes for the snow+soil domain... - ! ------------------------------------------- - if(nSnowSoilNrg>0)then - do iLayer=1,nLayers ! loop through all layers in the snow+soil domain - - ! check if the state is in the subset - if(ixSnowSoilNrg(iLayer)==integerMissing) cycle - - ! - define index within the state subset and the full state vector - jState = ixSnowSoilNrg(iLayer) ! index within the state subset - - ! - diagonal elements - aJac(jState,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) - - ! - lower-diagonal elements - if(iLayer>1)then - if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixSnowSoilNrg(iLayer-1),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) - endif - - ! - upper diagonal elements - if(iLayer0)then - do iLayer=1,nSnow ! loop through layers in the snow domain - - ! - check that the snow layer is desired - if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle - - ! - define state indices for the current layer - watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - - ! compute factor to convert liquid water derivative to total water derivative - select case( ixHydType(iLayer) ) - case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) - case default; convLiq2tot = 1._rkind - end select - - ! - diagonal elements - aJac(watState,watState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot + dMat(watState) * cj - - ! - lower-diagonal elements - if(iLayer>1)then - if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSnowOnlyHyd(iLayer-1),watState) = 0._rkind ! sub-diagonal: no dependence on other layers - endif - - ! - upper diagonal elements - if(iLayer0 .and. nSnowOnlyNrg>0)then - do iLayer=1,nSnow ! loop through layers in the snow domain - - ! - check that the snow layer is desired - if(ixSnowOnlyNrg(iLayer)==integerMissing) cycle - - ! (define the energy state) - nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector - - ! - define state indices for the current layer - watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - - if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) - - ! - include derivatives of energy fluxes w.r.t water fluxes for current layer - aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & - + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) & - + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) & - + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) - - ! - include derivatives of water fluxes w.r.t energy fluxes for current layer - aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) - - ! (cross-derivative terms for the layer below) - if(iLayer1)then - if(ixSnowOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSnowOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) ) + ! compute additional terms for the Jacobian for the snow-soil domain (excluding fluxes) + ! NOTE: energy for snow+soil is computed *within* the iteration loop as it includes phase change + do iLayer=1,nLayers + if(ixSnowSoilNrg(iLayer)/=integerMissing)then + dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj & + + dVolHtCapBulk_dTk(iLayer) * mLayerTempPrime(iLayer) & + + LH_fus*iden_water * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & + + LH_fus*iden_water * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) endif + end do - ! (cross-derivative terms for the layer below) - if(iLayer0)then !bottom snow layer and there is soil below - if(ixSoilOnlyNrg(1)/=integerMissing) aJac(ixSoilOnlyNrg(1),watState) = (dt/mLayerDepth(nSnow+1))*(-dNrgFlux_dWatAbove(nSnow) ) - endif - - endif ! (if the energy state for the current layer is within the state subset) - - end do ! (looping through snow layers) - endif ! (if there are state variables for both water and energy in the snow domain) - - ! ----- - ! * liquid water fluxes for the soil domain... - ! -------------------------------------------- - if(nSoilOnlyHyd>0)then + ! compute additional terms for the Jacobian for the soil domain (excluding fluxes) do iLayer=1,nSoil + if(ixSoilOnlyHyd(iLayer)/=integerMissing)then + dMat(ixSoilOnlyHyd(iLayer)) = ( dVolTot_dPsi0(iLayer) + dCompress_dPsi(iLayer) ) * cj + d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) - ! - check that the soil layer is desired - if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle + if(ixRichards==mixdform)then + dMat(ixSoilOnlyHyd(iLayer)) = dMat(ixSoilOnlyHyd(iLayer)) + specificStorage * dVolTot_dPsi0(iLayer) * mLayerMatricHeadPrime(iLayer) / theta_sat(iLayer) + endif - ! - define state indices - watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset + endif + end do - ! - define indices of the soil layers - jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + ! define the form of the matrix + select case(ixMatrix) + + ! ********************************************************************************************************************************************************* + ! ********************************************************************************************************************************************************* + ! * PART 1: BAND MATRIX + ! ********************************************************************************************************************************************************* + ! ********************************************************************************************************************************************************* + case(ixBandMatrix) + ! check + if(size(aJac,1)/=nBands .or. size(aJac,2)/=size(dMat))then + message=trim(message)//'unexpected shape of the Jacobian matrix: expect aJac(nBands,nState)' + err=20; return + endif + + ! ----- + ! * energy and liquid fluxes over vegetation... + ! --------------------------------------------- + if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) + + ! * energy fluxes with the canopy water + if(ixVegHyd/=integerMissing)then + + ! * cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) + if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixCasNrg),ixCasNrg) = -dCanopyEvaporation_dTCanair*dt + ! dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy is the derivative in throughfall and canopy drainage with canopy temperature + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixVegNrg),ixVegNrg) = -dCanopyEvaporation_dTCanopy*dt + dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy + ! * liquid water fluxes for vegetation canopy (-), dt*scalarFracLiqVeg*scalarCanopyLiqDeriv is the derivative in throughfall and canopy drainage with canopy water + aJac(ixDiag, ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanWat - scalarCanopyLiqDeriv)*dt + 1._rkind * cj + if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixTopNrg),ixTopNrg) = -dCanopyEvaporation_dTGround*dt + + ! * cross-derivative terms w.r.t. canopy water (kg-1 m2) + if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water + + ! * cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) + ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth * cj & + + dVolHtCapBulk_dCanWat * scalarCanopyTempPrime - (dt/canopyDepth) * dCanopyNetFlux_dCanWat & + + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth ! dF/dLiq + if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) + endif + + ! * -derivative terms w.r.t. canopy temperature (K-1) + if(ixVegNrg/=integerMissing)then + if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarCanopyLiqDeriv*dCanLiq_dTcanopy)/iden_water + endif + + ! * energy fluxes with the canopy air space (J m-3 K-1) + if(ixCasNrg/=integerMissing)then + aJac(ixDiag, ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) * cj + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixCasNrg,ixVegNrg),ixVegNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanopyTemp) + if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixCasNrg,ixTopNrg),ixTopNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dGroundTemp) + endif + + ! * energy fluxes with the vegetation canopy (J m-3 K-1) + if(ixVegNrg/=integerMissing)then + if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixCasNrg),ixCasNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanairTemp) + aJac(ixDiag, ixVegNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanopyTemp) + dMat(ixVegNrg) + if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixTopNrg),ixTopNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dGroundTemp) + endif + + ! * energy fluxes with the surface (J m-3 K-1) + if(ixTopNrg/=integerMissing)then + if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanairTemp) + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanopyTemp) + endif + + endif ! if there is a need to compute energy fluxes within vegetation + + ! ----- + ! * energy fluxes for the snow+soil domain... + ! ------------------------------------------- + if(nSnowSoilNrg>0)then + do iLayer=1,nLayers ! loop through all layers in the snow+soil domain + + ! check if the state is in the subset + if(ixSnowSoilNrg(iLayer)==integerMissing) cycle + + ! - define index within the state subset and the full state vector + jState = ixSnowSoilNrg(iLayer) ! index within the state subset + + ! - diagonal elements + aJac(ixDiag,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) + + ! - lower-diagonal elements + if(iLayer>1)then + if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowSoilNrg(iLayer-1),jState),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) + endif + + ! - upper diagonal elements + if(iLayer0)then + do iLayer=1,nSnow ! loop through layers in the snow domain + + ! - check that the snow layer is desired + if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle + + ! - define state indices for the current layer + watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset + + ! compute factor to convert liquid water derivative to total water derivative + select case( ixHydType(iLayer) ) + case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) + case default; convLiq2tot = 1._rkind + end select + + ! - diagonal elements + aJac(ixDiag,watState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot + dMat(watState) * cj + + ! - lower-diagonal elements + if(iLayer>1)then + if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyHyd(iLayer-1),watState),watState) = 0._rkind ! sub-diagonal: no dependence on other layers + endif + + ! - upper diagonal elements + if(iLayer0 .and. nSnowOnlyNrg>0)then + do iLayer=1,nSnow ! loop through layers in the snow domain + + ! - check that the snow layer is desired + if(ixSnowOnlyNrg(iLayer)==integerMissing) cycle + + ! (define the energy state) + nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector + + ! - define state indices for the current layer + watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset + + if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) + + ! - include derivatives of energy fluxes w.r.t water fluxes for current layer + aJac(ixOffDiag(nrgState,watState),watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & + + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) & + + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) & + + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) + + ! - include derivatives of water fluxes w.r.t energy fluxes for current layer + aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) + + ! (cross-derivative terms for the layer below) + if(iLayer1)then + if(ixSnowOnlyNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyNrg(iLayer-1),watState),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) ) + endif + + ! (cross-derivative terms for the layer below) + if(iLayer0)then !bottom snow layer and there is soil below + if(ixSoilOnlyNrg(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyNrg(1),watState),watState) = (dt/mLayerDepth(nSnow+1))*(-dNrgFlux_dWatAbove(nSnow) ) + endif + + endif ! (if the energy state for the current layer is within the state subset) + + end do ! (looping through snow layers) + endif ! (if there are state variables for both water and energy in the snow domain) + + ! ----- + ! * liquid water fluxes for the soil domain... + ! -------------------------------------------- + if(nSoilOnlyHyd>0)then + do iLayer=1,nSoil + + ! - check that the soil layer is desired + if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle + + ! - define state indices + watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset + + ! - define indices of the soil layers + jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + + ! - compute the diagonal elements + ! all terms *excluding* baseflow + aJac(ixDiag,watState) = (dt/mLayerDepth(jLayer))*(-dq_dHydStateBelow(iLayer-1) + dq_dHydStateAbove(iLayer)) + dMat(watState) + + ! - compute the lower-diagonal elements + if(iLayer>1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),watState),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) + endif + + ! - compute the upper-diagonal elements + if(iLayer0 .and. nSoilOnlyNrg>0)then + do iLayer=1,nSoilOnlyNrg + + ! - check that the soil layer is desired + if(ixSoilOnlyNrg(iLayer)==integerMissing) cycle + + ! - define indices of the soil layers + jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + + ! - define the energy state variable + nrgState = ixNrgLayer(jLayer) ! index within the full state vector + + ! - define index of hydrology state variable within the state subset + watState = ixSoilOnlyHyd(iLayer) + + ! only compute derivatives if the water state for the current layer is within the state subset + if(watstate/=integerMissing)then + + ! - include derivatives in liquid water fluxes w.r.t. temperature for current layer + aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance + + ! - compute lower diagonal elements + if(iLayer>1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),nrgState),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 + endif + + ! compute upper-diagonal elements + if(iLayer verySmall)then ! ice is present + aJac(ixOffDiag(nrgState,watState),watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) * cj & + - LH_fus*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_d2Psi0(iLayer) + aJac(ixOffDiag(nrgState,watState),watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + endif + + ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above + if(iLayer>1)then + if(ixSoilOnlyNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyNrg(iLayer-1),watState),watState) = (dt/mLayerDepth(jLayer-1))*( dNrgFlux_dWatBelow(jLayer-1) ) + elseif(iLayer==1 .and. nSnowOnlyNrg>0)then !top soil layer and there is snow above + if(ixSnowOnlyNrg(nSnow)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyNrg(nSnow),watState),watState) = (dt/mLayerDepth(nSnow))*( dNrgFlux_dWatBelow(nSnow) ) + endif + + ! (cross-derivative terms for the layer below) + if(iLayer1)then - if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) - endif + ! * -derivative terms w.r.t. canopy temperature (K-1) + if(ixVegNrg/=integerMissing)then + if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegNrg) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarCanopyLiqDeriv*dCanLiq_dTcanopy)/iden_water + endif - ! - compute the upper-diagonal elements - if(iLayer0)then !have snow above first soil layer - if(ixSnowOnlyHyd(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) - elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) - if(ixVegHyd/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegHyd) - endif + ! ----- + ! * energy fluxes for the snow+soil domain... + ! ------------------------------------------- + if(nSnowSoilNrg>0)then + do iLayer=1,nLayers ! loop through all layers in the snow+soil domain - endif ! (if the subset includes hydrology state variables in the soil domain) - - ! ----- - ! * liquid water fluxes for the aquifer... - ! ---------------------------------------- - if(ixAqWat/=integerMissing) then - aJac(ixAqWat,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) * cj - aJac(ixAqWat,ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk - aJac(ixAqWat,ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat - ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) - if(computeVegFlux)then - aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) - aJac(ixAqWat,ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) - aJac(ixAqWat,ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 - aJac(ixAqWat,ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) - endif - endif + ! check if the state is in the subset + if(ixSnowSoilNrg(iLayer)==integerMissing) cycle - ! ----- - ! * cross derivatives in the soil domain... - ! ---------------------------------------- - if(nSoilOnlyHyd>0 .and. nSoilOnlyNrg>0)then - do iLayer=1,nSoilOnlyNrg + ! - define index within the state subset and the full state vector + jState = ixSnowSoilNrg(iLayer) ! index within the state subset - ! - check that the soil layer is desired - if(ixSoilOnlyNrg(iLayer)==integerMissing) cycle + ! - diagonal elements + aJac(jState,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) - ! - define indices of the soil layers - jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + ! - lower-diagonal elements + if(iLayer>1)then + if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixSnowSoilNrg(iLayer-1),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) + endif - ! - define the energy state variable - nrgState = ixNrgLayer(jLayer) ! index within the full state vector + ! - upper diagonal elements + if(iLayer0)then + do iLayer=1,nSnow ! loop through layers in the snow domain - ! - include derivatives in liquid water fluxes w.r.t. temperature for current layer - aJac(watState,nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance + ! - check that the snow layer is desired + if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle - ! - compute lower diagonal elements - if(iLayer>1)then - if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 - endif + ! - define state indices for the current layer + watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - ! compute upper-diagonal elements - if(iLayer1)then + if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSnowOnlyHyd(iLayer-1),watState) = 0._rkind ! sub-diagonal: no dependence on other layers + endif - ! - include derivatives of energy w.r.t. ground evaporation - if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer - if(computeVegFlux)then - aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) - aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) - aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 - endif - aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) - endif - - ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) - if(computeVegFlux)then - aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanair(iLayer)) + aJac(watState,ixCasNrg) ! dVol/dT (K-1) - aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanopy(iLayer)) + aJac(watState,ixVegNrg) ! dVol/dT (K-1) - aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dCanWat(iLayer)) + aJac(watState,ixVegHyd) ! dVol/dLiq (kg m-2)-1 - aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTGround(iLayer)) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) - endif + ! - upper diagonal elements + if(iLayer0 .and. nSnowOnlyNrg>0)then + do iLayer=1,nSnow ! loop through layers in the snow domain + + ! - check that the snow layer is desired + if(ixSnowOnlyNrg(iLayer)==integerMissing) cycle + + ! (define the energy state) + nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector + + ! - define state indices for the current layer + watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset + + if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) + + ! - include derivatives of energy fluxes w.r.t water fluxes for current layer + aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & + + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) & + + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) & + + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) + + ! - include derivatives of water fluxes w.r.t energy fluxes for current layer + aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) + + ! (cross-derivative terms for the layer below) + if(iLayer1)then + if(ixSnowOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSnowOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) ) + endif + + ! (cross-derivative terms for the layer below) + if(iLayer0)then !bottom snow layer and there is soil below + if(ixSoilOnlyNrg(1)/=integerMissing) aJac(ixSoilOnlyNrg(1),watState) = (dt/mLayerDepth(nSnow+1))*(-dNrgFlux_dWatAbove(nSnow) ) + endif + + endif ! (if the energy state for the current layer is within the state subset) + + end do ! (looping through snow layers) + endif ! (if there are state variables for both water and energy in the snow domain) + + ! ----- + ! * liquid water fluxes for the soil domain... + ! -------------------------------------------- + if(nSoilOnlyHyd>0)then + do iLayer=1,nSoil + + ! - check that the soil layer is desired + if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle + + ! - define state indices + watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset + + ! - define indices of the soil layers + jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + + ! - compute the diagonal elements + ! all terms *excluding* baseflow + aJac(watState,watState) = (dt/mLayerDepth(jLayer))*(-dq_dHydStateBelow(iLayer-1) + dq_dHydStateAbove(iLayer)) + dMat(watState) + + ! - compute the lower-diagonal elements + if(iLayer>1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) + endif + + ! - compute the upper-diagonal elements + if(iLayer0)then !have snow above first soil layer + if(ixSnowOnlyHyd(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) + if(ixVegHyd/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegHyd) + endif - ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) & - + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) - if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present - aJac(nrgState,watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) * cj & - - LH_fus*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_d2Psi0(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content - endif + endif ! (if the subset includes hydrology state variables in the soil domain) + + ! ----- + ! * liquid water fluxes for the aquifer... + ! ---------------------------------------- + if(ixAqWat/=integerMissing) then + aJac(ixAqWat,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) * cj + aJac(ixAqWat,ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk + aJac(ixAqWat,ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat + ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) + if(computeVegFlux)then + aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) + aJac(ixAqWat,ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) + aJac(ixAqWat,ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 + aJac(ixAqWat,ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) + endif + endif - ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above - if(iLayer>1)then - if(ixSoilOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSoilOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(jLayer-1))*( dNrgFlux_dWatBelow(jLayer-1) ) - elseif(iLayer==1 .and. nSnowOnlyNrg>0)then !top soil layer and there is snow above - if(ixSnowOnlyNrg(nSnow)/=integerMissing) aJac(ixSnowOnlyNrg(nSnow),watState) = (dt/mLayerDepth(nSnow))*( dNrgFlux_dWatBelow(nSnow) ) - endif + ! ----- + ! * cross derivatives in the soil domain... + ! ---------------------------------------- + if(nSoilOnlyHyd>0 .and. nSoilOnlyNrg>0)then + do iLayer=1,nSoilOnlyNrg + + ! - check that the soil layer is desired + if(ixSoilOnlyNrg(iLayer)==integerMissing) cycle + + ! - define indices of the soil layers + jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + + ! - define the energy state variable + nrgState = ixNrgLayer(jLayer) ! index within the full state vector + + ! - define index of hydrology state variable within the state subset + watState = ixSoilOnlyHyd(iLayer) + + ! only compute derivatives if the water state for the current layer is within the state subset + if(watstate/=integerMissing)then + + ! - include derivatives in liquid water fluxes w.r.t. temperature for current layer + aJac(watState,nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance + + ! - compute lower diagonal elements + if(iLayer>1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 + endif + + ! compute upper-diagonal elements + if(iLayer verySmall)then ! ice is present + aJac(nrgState,watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) * cj & + - LH_fus*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_d2Psi0(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + endif + + ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above + if(iLayer>1)then + if(ixSoilOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSoilOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(jLayer-1))*( dNrgFlux_dWatBelow(jLayer-1) ) + elseif(iLayer==1 .and. nSnowOnlyNrg>0)then !top soil layer and there is snow above + if(ixSnowOnlyNrg(nSnow)/=integerMissing) aJac(ixSnowOnlyNrg(nSnow),watState) = (dt/mLayerDepth(nSnow))*( dNrgFlux_dWatBelow(nSnow) ) + endif + + ! (cross-derivative terms for the layer below) + if(iLayer0)then !have snow above first soil layer + if(ixSnowOnlyNrg(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) + if(ixVegNrg/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegNrg) + endif - ! (cross-derivative terms for the layer below) - if(iLayer0)then !have snow above first soil layer - if(ixSnowOnlyNrg(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) - elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) - if(ixVegNrg/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegNrg) + if(any(isNan(aJac)))then + print *, '******************************* WE FOUND NAN IN JACOBIAN ************************************' + stop 1 + message=trim(message)//'we found NaN' + err=20; return endif - endif ! (if there are state variables for both water and energy in the soil domain) - - ! print the Jacobian - if(globalPrintFlag)then - print*, '** analytical Jacobian (full):' - write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) - do iLayer=min(iJac1,nState),min(iJac2,nState) - write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(min(iJac1,nState):min(iJac2,nState),iLayer) - end do - endif - - !print*, '** analytical Jacobian (full):' - !write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=1,size(aJac,2)) - !do iLayer=1,size(aJac,1) - ! write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(iLayer,1:size(aJac,2)) - !end do - !print *, '--------------------------------------------------------------' - - ! *** - ! check - case default; err=20; message=trim(message)//'unable to identify option for the type of matrix'; return - - end select ! type of matrix - - if(any(isNan(aJac)))then - print *, '******************************* WE FOUND NAN IN JACOBIAN ************************************' - stop 1 - message=trim(message)//'we found NaN' - err=20; return - endif - - ! end association to variables in the data structures - end associate + ! end association to variables in the data structures + end associate - end subroutine computJacobSundials +end subroutine computJacobSundials ! ********************************************************************************************************** ! private function: get the off-diagonal index in the band-diagonal matrix ! ********************************************************************************************************** function ixOffDiag(jState,iState) - implicit none - integer(i4b),intent(in) :: jState ! off-diagonal state - integer(i4b),intent(in) :: iState ! diagonal state - integer(i4b) :: ixOffDiag ! off-diagonal index in gthe band-diagonal matrix - ixOffDiag = ixBandOffset + jState - iState + implicit none + integer(i4b),intent(in) :: jState ! off-diagonal state + integer(i4b),intent(in) :: iState ! diagonal state + integer(i4b) :: ixOffDiag ! off-diagonal index in gthe band-diagonal matrix + ixOffDiag = ixBandOffset + jState - iState end function ixOffDiag end module computJacobSundials_module From acd38a7fa99122d2b73dc958f475b7b154540fcf Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 26 Sep 2022 21:48:15 +0000 Subject: [PATCH 0354/1472] adjusted summaSolveSundialsIDA --- build/source/engine/summaSolveSundialsIDA.f90 | 1246 ++++++++--------- 1 file changed, 619 insertions(+), 627 deletions(-) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index e08fd853a..5530f62aa 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -85,655 +85,647 @@ module summaSolveSundialsIDA_module contains - !------------------- - ! * public subroutine summaSolveSundialsIDA: solve F(y,y') = 0 by IDA (y is the state vector) - ! ------------------ - subroutine summaSolveSundialsIDA( & - dt, & ! intent(in): data time step - atol, & ! intent(in): absolute telerance - rtol, & ! intent(in): relative tolerance - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - nStat, & ! intent(in): total number of state variables - ixMatrix, & ! intent(in): type of matrix (dense or banded) - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVecInit, & ! intent(in): initial state vector - sMul, & ! intent(inout): state vector multiplier (USEd in the residual calculations) - dMat, & ! intent(inout): diagonal of the Jacobian matrix (excludes fluxes) - ! input: data structures - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(in): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_temp, & ! intent(inout): model fluxes for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output - ixSaturation, & ! intent(inout) index of the lowest saturated layer (NOTE: only computed on the first iteration) - idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step - tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt - mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix - dt_out, & ! intent(out): time step - stateVec, & ! intent(out): model state vector - stateVecPrime, & ! intent(out): derivative of model state vector - err,message & ! intent(out): error control - ) - - !======= Inclusions =========== - USE fida_mod ! Fortran interface to IDA - USE fnvector_serial_mod ! Fortran interface to serial N_Vector - USE fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix - USE fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver - USE fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix - USE fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver - USE fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver - USE fsundials_matrix_mod ! Fortran interface to generic SUNMatrix - USE fsundials_nvector_mod ! Fortran interface to generic N_Vector - USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver - USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver - USE allocspace_module,only:allocLocal ! allocate local data structures - USE evalDAE4IDA_module,only:evalDAE4IDA ! DAE/ODE functions - USE evalJac4IDA_module,only:evalJac4IDA ! system Jacobian - USE tol4IDA_module,only:computWeight4IDA ! weigth required for tolerances - USE eval8summaSundials_module,only:eval8summaSundials ! residual of DAE - USE var_derive_module,only:calcHeight ! height at layer interfaces and layer mid-point - - !======= Declarations ========= - implicit none - - ! -------------------------------------------------------------------------------------------------------------------------------- - ! calling variables - ! -------------------------------------------------------------------------------------------------------------------------------- - ! input: model control - real(qp),intent(in) :: dt ! data time step - real(qp),intent(inout) :: atol(:) ! vector of absolute tolerances - real(qp),intent(inout) :: rtol(:) ! vector of relative tolerances - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - integer(i4b),intent(in) :: nStat ! total number of state variables - integer(i4b),intent(in) :: ixMatrix ! form of matrix (dense or banded) - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - ! input: state vectors - real(rkind),intent(in) :: stateVecInit(:) ! model state vector - real(qp),intent(in) :: sMul(:) ! state vector multiplier (used in the residual calculations) - real(rkind), intent(inout) :: dMat(:) - ! input: data structures - type(zLookup),intent(in) :: lookup_data ! lookup tables - type(var_i), intent(in) :: type_data ! type of vegetation and soil - type(var_d), intent(in) :: attr_data ! spatial attributes - type(var_dlength), intent(in) :: mpar_data ! model parameters - type(var_d), intent(in) :: forc_data ! model forcing data - type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin - type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_ilength), intent(in) :: indx_data ! indices defining model states and layers - ! input-output: data structures - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_temp ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: flux_sum ! sum of fluxes - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(rkind),intent(inout) :: mLayerCmpress_sum(:) ! sum of soil compress - ! output: state vectors - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer - real(rkind),intent(inout) :: stateVec(:) ! model state vector (y) - real(rkind),intent(inout) :: stateVecPrime(:) ! model state vector (y') - logical(lgt),intent(out) :: idaSucceeds ! flag to indicate if IDA is successful - logical(lgt),intent(inout) :: tooMuchMelt ! flag to denote that there was too much melt - real(qp),intent(out) :: dt_out ! time step - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - ! -------------------------------------------------------------------------------------------------------------------------------- - type(N_Vector), pointer :: sunvec_y ! sundials solution vector - type(N_Vector), pointer :: sunvec_yp ! sundials derivative vector - type(N_Vector), pointer :: sunvec_av ! sundials tolerance vector - type(SUNMatrix), pointer :: sunmat_A ! sundials matrix - type(SUNLinearSolver), pointer :: sunlinsol_LS ! sundials linear solver - type(SUNNonLinearSolver), pointer :: sunnonlin_NLS ! sundials nonlinear solver - type(c_ptr) :: ida_mem ! IDA memory - type(eqnsData), target :: eqns_data ! IDA type - integer(i4b) :: retval, retvalr ! return value - integer(i4b) :: rootsfound(3) ! crossing direction of discontinuities - logical(lgt) :: feasible ! feasibility flag - real(qp) :: t0 ! staring time - real(qp) :: dt_last(1) ! last time step - integer(kind = 8) :: mu, lu ! in banded matrix mode - integer(i4b) :: iVar - logical(lgt) :: startQuadrature - real(rkind) :: mLayerMatricHeadLiqPrev(nSoil) - real(qp) :: h_init - integer(c_long) :: nState ! total number of state variables - real(rkind) :: rVec(nStat) - real(qp) :: tret(1) - logical(lgt) :: mergedLayers - logical(lgt),parameter :: offErrWarnMessage = .false. - real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) - real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) - integer(i4b) :: i - - ! ----------------------------------------------------------------------------------------------------- - - ! initialize error control - err=0; message="summaSolveSundialsIDA/" - - nState = nStat - idaSucceeds = .true. - ! fill eqns_data which will be required later to call eval8summaSundials - eqns_data%dt = dt - eqns_data%nSnow = nSnow - eqns_data%nSoil = nSoil - eqns_data%nLayers = nLayers - eqns_data%nState = nState - eqns_data%ixMatrix = ixMatrix - eqns_data%firstSubStep = firstSubStep - eqns_data%computeVegFlux = computeVegFlux - eqns_data%scalarSolution = scalarSolution - - allocate( eqns_data%atol(nState) ) - eqns_data%atol = atol - - allocate( eqns_data%rtol(nState) ) - eqns_data%rtol = rtol - - allocate( eqns_data%sMul(nState) ) - eqns_data%sMul = sMul - - allocate( eqns_data%dMat(nState) ) - eqns_data%dMat = dMat - - ! allocate space for the temporary prognostic variable structure - call allocLocal(prog_meta(:),eqns_data%prog_data,nSnow,nSoil,err,message) - if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif - eqns_data%prog_data = prog_data - - ! allocate space for the temporary diagnostic variable structure - call allocLocal(diag_meta(:),eqns_data%diag_data,nSnow,nSoil,err,message) - if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif - eqns_data%diag_data = diag_data - - ! allocate space for the temporary flux variable structure - call allocLocal(flux_meta(:),eqns_data%flux_data,nSnow,nSoil,err,message) - if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif - eqns_data%flux_data = flux_data - - ! allocate space for the derivative structure - call allocLocal(deriv_meta(:),eqns_data%deriv_data,nSnow,nSoil,err,message) - if(err/=0)then; err=20; message=trim(message)//trim(message); return; end if - eqns_data%deriv_data = deriv_data - - eqns_data%lookup_data = lookup_data - eqns_data%type_data = type_data - eqns_data%attr_data = attr_data - eqns_data%mpar_data = mpar_data - eqns_data%forc_data = forc_data - eqns_data%bvar_data = bvar_data - eqns_data%indx_data = indx_data - - ! allocate space - if(model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel)then - allocate(eqns_data%dBaseflow_dMatric(nSoil,nSoil),stat=err) - else - allocate(eqns_data%dBaseflow_dMatric(0,0),stat=err) - end if - allocate( eqns_data%mLayerMatricHeadLiqTrial(nSoil) ) - allocate( eqns_data%mLayerMatricHeadTrial(nSoil) ) - allocate( eqns_data%mLayerMatricHeadPrev(nSoil) ) - allocate( eqns_data%mLayerVolFracWatTrial(nLayers) ) - allocate( eqns_data%mLayerVolFracWatPrev(nLayers) ) - allocate( eqns_data%mLayerTempTrial(nLayers) ) - allocate( eqns_data%mLayerTempPrev(nLayers) ) - allocate( eqns_data%mLayerVolFracIceTrial(nLayers) ) - allocate( eqns_data%mLayerVolFracIcePrev(nLayers) ) - allocate( eqns_data%mLayerVolFracLiqTrial(nLayers) ) - allocate( eqns_data%mLayerVolFracLiqPrev(nLayers) ) - allocate( eqns_data%mLayerEnthalpyTrial(nLayers) ) - allocate( eqns_data%mLayerEnthalpyPrev(nLayers) ) - allocate( eqns_data%fluxVec(nState) ) - allocate( eqns_data%resSink(nState) ) - - startQuadrature = .true. - - ! create serial vectors - sunvec_y => FN_VMake_Serial(nState, stateVec) - if (.not. associated(sunvec_y)) then; err=20; message='summaSolveSundialsIDA: sunvec = NULL'; return; endif - - sunvec_yp => FN_VMake_Serial(nState, stateVecPrime) - if (.not. associated(sunvec_yp)) then; err=20; message='summaSolveSundialsIDA: sunvec = NULL'; return; endif - - ! Initialize solution vectors - call setInitialCondition(nState, stateVecInit, sunvec_y, sunvec_yp) - - ! Create memory - ida_mem = FIDACreate() - if (.not. c_associated(ida_mem)) then; err=20; message='summaSolveSundialsIDA: ida_mem = NULL'; return; endif - - ! Attach user data to memory - eqns_data%ida_mem = ida_mem - retval = FIDASetUserData(ida_mem, c_loc(eqns_data)) - if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetUserData'; return; endif - - ! Initialize memory - t0 = 0._rkind - retval = FIDAInit(ida_mem, c_funloc(evalDAE4IDA), t0, sunvec_y, sunvec_yp) - if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDAInit'; return; endif - - ! set tolerances - retval = FIDAWFtolerances(ida_mem, c_funloc(computWeight4IDA)) - if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDAWFtolerances'; return; endif - - ! define the form of the matrix - select case(ixMatrix) - case(ixBandMatrix) - mu = ku; lu = kl; - ! Create banded SUNMatrix for use in linear solves - sunmat_A => FSUNBandMatrix(nState, mu, lu) - if (.not. associated(sunmat_A)) then; err=20; message='summaSolveSundialsIDA: sunmat = NULL'; return; endif - - ! Create banded SUNLinearSolver object - sunlinsol_LS => FSUNLinSol_Band(sunvec_y, sunmat_A) - if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolveSundialsIDA: sunlinsol = NULL'; return; endif - - case(ixFullMatrix) - ! Create dense SUNMatrix for use in linear solves - sunmat_A => FSUNDenseMatrix(nState, nState) - if (.not. associated(sunmat_A)) then; err=20; message='summaSolveSundialsIDA: sunmat = NULL'; return; endif - - ! Create dense SUNLinearSolver object - sunlinsol_LS => FSUNDenseLinearSolver(sunvec_y, sunmat_A) - if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolveSundialsIDA: sunlinsol = NULL'; return; endif - - ! check - case default; err=20; message='summaSolveSundialsIDA: error in type of matrix'; return - - end select ! form of matrix - - ! Attach the matrix and linear solver - retval = FIDASetLinearSolver(ida_mem, sunlinsol_LS, sunmat_A); - if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetLinearSolver'; return; endif +!------------------- +! * public subroutine summaSolveSundialsIDA: solve F(y,y') = 0 by IDA (y is the state vector) +! ------------------ +subroutine summaSolveSundialsIDA( & + dt, & ! intent(in): data time step + atol, & ! intent(in): absolute telerance + rtol, & ! intent(in): relative tolerance + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + nStat, & ! intent(in): total number of state variables + ixMatrix, & ! intent(in): type of matrix (dense or banded) + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVecInit, & ! intent(in): initial state vector + sMul, & ! intent(inout): state vector multiplier (USEd in the residual calculations) + dMat, & ! intent(inout): diagonal of the Jacobian matrix (excludes fluxes) + ! input: data structures + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + indx_data, & ! intent(in): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_temp, & ! intent(inout): model fluxes for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output + ixSaturation, & ! intent(inout) index of the lowest saturated layer (NOTE: only computed on the first iteration) + idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step + tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt + mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix + dt_out, & ! intent(out): time step + stateVec, & ! intent(out): model state vector + stateVecPrime, & ! intent(out): derivative of model state vector + err,message & ! intent(out): error control + ) + + !======= Inclusions =========== + USE fida_mod ! Fortran interface to IDA + USE fnvector_serial_mod ! Fortran interface to serial N_Vector + USE fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix + USE fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver + USE fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix + USE fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver + USE fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver + USE fsundials_matrix_mod ! Fortran interface to generic SUNMatrix + USE fsundials_nvector_mod ! Fortran interface to generic N_Vector + USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver + USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver + USE allocspace_module,only:allocLocal ! allocate local data structures + USE evalDAE4IDA_module,only:evalDAE4IDA ! DAE/ODE functions + USE evalJac4IDA_module,only:evalJac4IDA ! system Jacobian + USE tol4IDA_module,only:computWeight4IDA ! weigth required for tolerances + USE eval8summaSundials_module,only:eval8summaSundials ! residual of DAE + USE var_derive_module,only:calcHeight ! height at layer interfaces and layer mid-point + + !======= Declarations ========= + implicit none + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! calling variables + ! -------------------------------------------------------------------------------------------------------------------------------- + ! input: model control + real(qp),intent(in) :: dt ! data time step + real(qp),intent(inout) :: atol(:) ! vector of absolute tolerances + real(qp),intent(inout) :: rtol(:) ! vector of relative tolerances + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + integer(i4b),intent(in) :: nStat ! total number of state variables + integer(i4b),intent(in) :: ixMatrix ! form of matrix (dense or banded) + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + ! input: state vectors + real(rkind),intent(in) :: stateVecInit(:) ! model state vector + real(qp),intent(in) :: sMul(:) ! state vector multiplier (used in the residual calculations) + real(rkind), intent(inout) :: dMat(:) + ! input: data structures + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_i), intent(in) :: type_data ! type of vegetation and soil + type(var_d), intent(in) :: attr_data ! spatial attributes + type(var_dlength), intent(in) :: mpar_data ! model parameters + type(var_d), intent(in) :: forc_data ! model forcing data + type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin + type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_ilength), intent(in) :: indx_data ! indices defining model states and layers + ! input-output: data structures + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_temp ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: flux_sum ! sum of fluxes + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + real(rkind),intent(inout) :: mLayerCmpress_sum(:) ! sum of soil compress + ! output: state vectors + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer + real(rkind),intent(inout) :: stateVec(:) ! model state vector (y) + real(rkind),intent(inout) :: stateVecPrime(:) ! model state vector (y') + logical(lgt),intent(out) :: idaSucceeds ! flag to indicate if IDA is successful + logical(lgt),intent(inout) :: tooMuchMelt ! flag to denote that there was too much melt + real(qp),intent(out) :: dt_out ! time step + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + ! -------------------------------------------------------------------------------------------------------------------------------- + type(N_Vector), pointer :: sunvec_y ! sundials solution vector + type(N_Vector), pointer :: sunvec_yp ! sundials derivative vector + type(N_Vector), pointer :: sunvec_av ! sundials tolerance vector + type(SUNMatrix), pointer :: sunmat_A ! sundials matrix + type(SUNLinearSolver), pointer :: sunlinsol_LS ! sundials linear solver + type(SUNNonLinearSolver), pointer :: sunnonlin_NLS ! sundials nonlinear solver + type(c_ptr) :: ida_mem ! IDA memory + type(eqnsData), target :: eqns_data ! IDA type + integer(i4b) :: retval, retvalr ! return value + integer(i4b) :: rootsfound(3) ! crossing direction of discontinuities + logical(lgt) :: feasible ! feasibility flag + real(qp) :: t0 ! staring time + real(qp) :: dt_last(1) ! last time step + integer(kind = 8) :: mu, lu ! in banded matrix mode + integer(i4b) :: iVar + logical(lgt) :: startQuadrature + real(rkind) :: mLayerMatricHeadLiqPrev(nSoil) + real(qp) :: h_init + integer(c_long) :: nState ! total number of state variables + real(rkind) :: rVec(nStat) + real(qp) :: tret(1) + logical(lgt) :: mergedLayers + logical(lgt),parameter :: offErrWarnMessage = .false. + real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) + real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) + integer(i4b) :: i + + ! ----------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message="summaSolveSundialsIDA/" + + nState = nStat + idaSucceeds = .true. + ! fill eqns_data which will be required later to call eval8summaSundials + eqns_data%dt = dt + eqns_data%nSnow = nSnow + eqns_data%nSoil = nSoil + eqns_data%nLayers = nLayers + eqns_data%nState = nState + eqns_data%ixMatrix = ixMatrix + eqns_data%firstSubStep = firstSubStep + eqns_data%computeVegFlux = computeVegFlux + eqns_data%scalarSolution = scalarSolution + + allocate( eqns_data%atol(nState) ) + eqns_data%atol = atol + + allocate( eqns_data%rtol(nState) ) + eqns_data%rtol = rtol + + allocate( eqns_data%sMul(nState) ) + eqns_data%sMul = sMul + + allocate( eqns_data%dMat(nState) ) + eqns_data%dMat = dMat + + ! allocate space for the temporary prognostic variable structure + call allocLocal(prog_meta(:),eqns_data%prog_data,nSnow,nSoil,err,message) + if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif + eqns_data%prog_data = prog_data + + ! allocate space for the temporary diagnostic variable structure + call allocLocal(diag_meta(:),eqns_data%diag_data,nSnow,nSoil,err,message) + if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif + eqns_data%diag_data = diag_data + + ! allocate space for the temporary flux variable structure + call allocLocal(flux_meta(:),eqns_data%flux_data,nSnow,nSoil,err,message) + if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif + eqns_data%flux_data = flux_data + + ! allocate space for the derivative structure + call allocLocal(deriv_meta(:),eqns_data%deriv_data,nSnow,nSoil,err,message) + if(err/=0)then; err=20; message=trim(message)//trim(message); return; end if + eqns_data%deriv_data = deriv_data + + eqns_data%lookup_data = lookup_data + eqns_data%type_data = type_data + eqns_data%attr_data = attr_data + eqns_data%mpar_data = mpar_data + eqns_data%forc_data = forc_data + eqns_data%bvar_data = bvar_data + eqns_data%indx_data = indx_data + + ! allocate space + if(model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel)then + allocate(eqns_data%dBaseflow_dMatric(nSoil,nSoil),stat=err) + else + allocate(eqns_data%dBaseflow_dMatric(0,0),stat=err) + end if + allocate( eqns_data%mLayerMatricHeadLiqTrial(nSoil) ) + allocate( eqns_data%mLayerMatricHeadTrial(nSoil) ) + allocate( eqns_data%mLayerMatricHeadPrev(nSoil) ) + allocate( eqns_data%mLayerVolFracWatTrial(nLayers) ) + allocate( eqns_data%mLayerVolFracWatPrev(nLayers) ) + allocate( eqns_data%mLayerTempTrial(nLayers) ) + allocate( eqns_data%mLayerTempPrev(nLayers) ) + allocate( eqns_data%mLayerVolFracIceTrial(nLayers) ) + allocate( eqns_data%mLayerVolFracIcePrev(nLayers) ) + allocate( eqns_data%mLayerVolFracLiqTrial(nLayers) ) + allocate( eqns_data%mLayerVolFracLiqPrev(nLayers) ) + allocate( eqns_data%mLayerEnthalpyTrial(nLayers) ) + allocate( eqns_data%mLayerEnthalpyPrev(nLayers) ) + allocate( eqns_data%fluxVec(nState) ) + allocate( eqns_data%resSink(nState) ) + + startQuadrature = .true. + + ! create serial vectors + sunvec_y => FN_VMake_Serial(nState, stateVec) + if (.not. associated(sunvec_y)) then; err=20; message='summaSolveSundialsIDA: sunvec = NULL'; return; endif + + sunvec_yp => FN_VMake_Serial(nState, stateVecPrime) + if (.not. associated(sunvec_yp)) then; err=20; message='summaSolveSundialsIDA: sunvec = NULL'; return; endif + + ! Initialize solution vectors + call setInitialCondition(nState, stateVecInit, sunvec_y, sunvec_yp) + + ! Create memory + ida_mem = FIDACreate() + if (.not. c_associated(ida_mem)) then; err=20; message='summaSolveSundialsIDA: ida_mem = NULL'; return; endif + + ! Attach user data to memory + eqns_data%ida_mem = ida_mem + retval = FIDASetUserData(ida_mem, c_loc(eqns_data)) + if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetUserData'; return; endif + + ! Initialize memory + t0 = 0._rkind + retval = FIDAInit(ida_mem, c_funloc(evalDAE4IDA), t0, sunvec_y, sunvec_yp) + if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDAInit'; return; endif + + ! set tolerances + retval = FIDAWFtolerances(ida_mem, c_funloc(computWeight4IDA)) + if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDAWFtolerances'; return; endif + + ! define the form of the matrix + select case(ixMatrix) + case(ixBandMatrix) + mu = ku; lu = kl; + ! Create banded SUNMatrix for use in linear solves + sunmat_A => FSUNBandMatrix(nState, mu, lu) + if (.not. associated(sunmat_A)) then; err=20; message='summaSolveSundialsIDA: sunmat = NULL'; return; endif + + ! Create banded SUNLinearSolver object + sunlinsol_LS => FSUNLinSol_Band(sunvec_y, sunmat_A) + if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolveSundialsIDA: sunlinsol = NULL'; return; endif + + case(ixFullMatrix) + ! Create dense SUNMatrix for use in linear solves + sunmat_A => FSUNDenseMatrix(nState, nState) + if (.not. associated(sunmat_A)) then; err=20; message='summaSolveSundialsIDA: sunmat = NULL'; return; endif + + ! Create dense SUNLinearSolver object + sunlinsol_LS => FSUNDenseLinearSolver(sunvec_y, sunmat_A) + if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolveSundialsIDA: sunlinsol = NULL'; return; endif + + ! check + case default; err=20; message='summaSolveSundialsIDA: error in type of matrix'; return + + end select ! form of matrix + + ! Attach the matrix and linear solver + retval = FIDASetLinearSolver(ida_mem, sunlinsol_LS, sunmat_A); + if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetLinearSolver'; return; endif ! Set the user-supplied Jacobian routine !comment this line out to use FD Jacobian retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetJacFn'; return; endif - ! Create Newton SUNNonlinearSolver object - sunnonlin_NLS => FSUNNonlinSol_Newton(sunvec_y) - if (.not. associated(sunnonlin_NLS)) then; err=20; message='summaSolveSundialsIDA: sunnonlinsol = NULL'; return; endif - - ! Attach the nonlinear solver - retval = FIDASetNonlinearSolver(ida_mem, sunnonlin_NLS) - if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetNonlinearSolver'; return; endif - - ! Enforce the solver to stop at end of the time step - retval = FIDASetStopTime(ida_mem, dt) - if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetStopTime'; return; endif - - ! Set solver parameters such as maximum order, number of iterations, ... - call setSolverParams(dt, ida_mem, retval) - if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in setSolverParams'; return; endif - - ! Disable error messages and warnings - if(offErrWarnMessage) then - retval = FIDASetErrFile(ida_mem, c_null_ptr) - retval = FIDASetNoInactiveRootWarn(ida_mem) - endif - - ! need the following values for the first substep - eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) - eqns_data%scalarCanopyIcePrev = prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) - eqns_data%scalarCanopyLiqPrev = prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) - eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) - eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) - eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) - eqns_data%mLayerVolFracLiqPrev(:) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(:) - eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) - eqns_data%scalarAquiferStoragePrev = prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) - eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) - eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) - mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) - eqns_data%ixSaturation = ixSaturation - - !********************************************************************************** - !****************************** Main Solver *************************************** - !************************* loop on one_step mode ********************************** - !********************************************************************************** - - tret(1) = t0 ! intial time - do while(tret(1) < dt) - eqns_data%firstFluxCall = .false. - eqns_data%firstSplitOper = .true. - ! call IDASolve, advance solver just one internal step - retvalr = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) - if( retvalr < 0 )then - idaSucceeds = .false. - exit + ! Create Newton SUNNonlinearSolver object + sunnonlin_NLS => FSUNNonlinSol_Newton(sunvec_y) + if (.not. associated(sunnonlin_NLS)) then; err=20; message='summaSolveSundialsIDA: sunnonlinsol = NULL'; return; endif + + ! Attach the nonlinear solver + retval = FIDASetNonlinearSolver(ida_mem, sunnonlin_NLS) + if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetNonlinearSolver'; return; endif + + ! Enforce the solver to stop at end of the time step + retval = FIDASetStopTime(ida_mem, dt) + if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetStopTime'; return; endif + + ! Set solver parameters such as maximum order, number of iterations, ... + call setSolverParams(dt, ida_mem, retval) + if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in setSolverParams'; return; endif + + ! Disable error messages and warnings + if(offErrWarnMessage) then + retval = FIDASetErrFile(ida_mem, c_null_ptr) + retval = FIDASetNoInactiveRootWarn(ida_mem) + endif + + ! need the following values for the first substep + eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) + eqns_data%scalarCanopyIcePrev = prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) + eqns_data%scalarCanopyLiqPrev = prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) + eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) + eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) + eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) + eqns_data%mLayerVolFracLiqPrev(:) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(:) + eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) + eqns_data%scalarAquiferStoragePrev = prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) + eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) + eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) + mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) + eqns_data%ixSaturation = ixSaturation + + !********************************************************************************** + !****************************** Main Solver *************************************** + !************************* loop on one_step mode ********************************** + !********************************************************************************** + + tret(1) = t0 ! intial time + do while(tret(1) < dt) + eqns_data%firstFluxCall = .false. + eqns_data%firstSplitOper = .true. + ! call IDASolve, advance solver just one internal step + retvalr = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) + if( retvalr < 0 )then + idaSucceeds = .false. + exit + endif + + tooMuchMelt = .false. + feasible = .true. + ! loop through non-missing energy state variables in the snow domain to see if need to merge + do concurrent (i=1:nSnow,indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing) + if (stateVec(indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)) > Tfreeze) tooMuchMelt = .true. !need to merge + enddo + if(tooMuchMelt)exit + + ! get the last stepsize + retval = FIDAGetLastStep(ida_mem, dt_last) + + ! compute the flux and the residual vector for a given state vector + call eval8summaSundials(& + ! input: model control + dt_last(1), & ! intent(in): current stepsize + eqns_data%dt, & ! intent(in): total data step + eqns_data%nSnow, & ! intent(in): number of snow layers + eqns_data%nSoil, & ! intent(in): number of soil layers + eqns_data%nLayers, & ! intent(in): number of layers + eqns_data%nState, & ! intent(in): number of state variables in the current subset + .false., & ! intent(in): check for feasibility once outside Sundials loop + eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation + eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVec, & ! intent(in): model state vector + stateVecPrime, & ! intent(in): model state vector + eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + eqns_data%lookup_data, & ! intent(in): lookup data + eqns_data%type_data, & ! intent(in): type of vegetation and soil + eqns_data%attr_data, & ! intent(in): spatial attributes + eqns_data%mpar_data, & ! intent(in): model parameters + eqns_data%forc_data, & ! intent(in): model forcing data + eqns_data%bvar_data, & ! intent(in): average model variables for the entire basin + eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + eqns_data%indx_data, & ! intent(inout): index data + eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU + eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) + eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output + eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian + eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) + eqns_data%scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) + eqns_data%scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + eqns_data%scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) + eqns_data%scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) + eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): value for enthalpy of the vegetation canopy (J m-3) + eqns_data%mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + eqns_data%mLayerTempPrev, & ! intent(in): vector of layer temperature (K) + eqns_data%mLayerMatricHeadLiqTrial, & ! intent(out): trial value for liquid water matric potential (m) + eqns_data%mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) + eqns_data%mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) + eqns_data%mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) + eqns_data%mLayerVolFracWatPrev, & ! intent(in): vector of volumetric total water content (-) + eqns_data%mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) + eqns_data%mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) + eqns_data%mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) + eqns_data%mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) + eqns_data%scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) + eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) + eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) + eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) + eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer + ! output + feasible, & ! intent(out): flag to denote the feasibility of the solution + eqns_data%fluxVec, & ! intent(out): flux vector + eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec, & ! intent(out): residual vector + eqns_data%err,eqns_data%message) ! intent(out): error control + + ! sum of fluxes + do iVar=1,size(flux_meta) + flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * dt_last(1) + end do + + ! sum of mLayerCmpress + mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & + * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) + + ! save required quantities for next step + eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial + eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial + eqns_data%scalarCanopyLiqPrev = eqns_data%scalarCanopyLiqTrial + eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) + mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) + eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) + eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) + eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) + eqns_data%mLayerVolFracLiqPrev(:) = eqns_data%mLayerVolFracLiqTrial(:) + eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial + eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) + eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial + + enddo ! while loop on one_step mode until time dt + + !****************************** End of Main Solver *************************************** + + err = eqns_data%err + message = eqns_data%message + if( .not. feasible) idaSucceeds = .false. + + if(idaSucceeds)then + ! copy to output data + diag_data = eqns_data%diag_data + flux_data = eqns_data%flux_data + deriv_data = eqns_data%deriv_data + ixSaturation = eqns_data%ixSaturation + dt_out = tret(1) endif - tooMuchMelt = .false. - feasible = .true. - ! loop through non-missing energy state variables in the snow domain to see if need to merge - do concurrent (i=1:nSnow,indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing) - if (stateVec(indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)) > Tfreeze) tooMuchMelt = .true. !need to merge - enddo - if(tooMuchMelt)exit - - ! get the last stepsize - retval = FIDAGetLastStep(ida_mem, dt_last) - - ! compute the flux and the residual vector for a given state vector - call eval8summaSundials(& - ! input: model control - dt_last(1), & ! intent(in): current stepsize - eqns_data%dt, & ! intent(in): total data step - eqns_data%nSnow, & ! intent(in): number of snow layers - eqns_data%nSoil, & ! intent(in): number of soil layers - eqns_data%nLayers, & ! intent(in): number of layers - eqns_data%nState, & ! intent(in): number of state variables in the current subset - .false., & ! intent(in): check for feasibility once outside Sundials loop - eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation - eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVec, & ! intent(in): model state vector - stateVecPrime, & ! intent(in): model state vector - eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - eqns_data%lookup_data, & ! intent(in): lookup data - eqns_data%type_data, & ! intent(in): type of vegetation and soil - eqns_data%attr_data, & ! intent(in): spatial attributes - eqns_data%mpar_data, & ! intent(in): model parameters - eqns_data%forc_data, & ! intent(in): model forcing data - eqns_data%bvar_data, & ! intent(in): average model variables for the entire basin - eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - eqns_data%indx_data, & ! intent(inout): index data - eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU - eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) - eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output - eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian - eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) - eqns_data%scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) - eqns_data%scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - eqns_data%scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) - eqns_data%scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) - eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): value for enthalpy of the vegetation canopy (J m-3) - eqns_data%mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - eqns_data%mLayerTempPrev, & ! intent(in): vector of layer temperature (K) - eqns_data%mLayerMatricHeadLiqTrial, & ! intent(out): trial value for liquid water matric potential (m) - eqns_data%mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) - eqns_data%mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) - eqns_data%mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - eqns_data%mLayerVolFracWatPrev, & ! intent(in): vector of volumetric total water content (-) - eqns_data%mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) - eqns_data%mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) - eqns_data%mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - eqns_data%mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) - eqns_data%scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) - eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) - eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) - eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) - eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer - ! output - feasible, & ! intent(out): flag to denote the feasibility of the solution - eqns_data%fluxVec, & ! intent(out): flux vector - eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector - eqns_data%err,eqns_data%message) ! intent(out): error control - - ! sum of fluxes - do iVar=1,size(flux_meta) - flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * dt_last(1) - end do - - ! do iVar=1,size(flux_meta) - ! flux_data%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / tret(1) - ! end do - - ! sum of mLayerCmpress - mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & - * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) - - ! save required quantities for next step - eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial - eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial - eqns_data%scalarCanopyLiqPrev = eqns_data%scalarCanopyLiqTrial - eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) - mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) - eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) - eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) - eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) - eqns_data%mLayerVolFracLiqPrev(:) = eqns_data%mLayerVolFracLiqTrial(:) - eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial - eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) - eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial - - enddo ! while loop on one_step mode until time dt - - !****************************** End of Main Solver *************************************** - - err = eqns_data%err - message = eqns_data%message - if( .not. feasible) idaSucceeds = .false. - - if(idaSucceeds)then - ! copy to output data - diag_data = eqns_data%diag_data - flux_data = eqns_data%flux_data - deriv_data = eqns_data%deriv_data - ixSaturation = eqns_data%ixSaturation - dt_out = tret(1) - endif - - ! free memory - deallocate( eqns_data%sMul ) - deallocate( eqns_data%dMat ) - deallocate( eqns_data%dBaseflow_dMatric ) - deallocate( eqns_data%mLayerMatricHeadLiqTrial ) - deallocate( eqns_data%mLayerMatricHeadTrial ) - deallocate( eqns_data%mLayerMatricHeadPrev ) - deallocate( eqns_data%fluxVec ) - deallocate( eqns_data%resSink ) - deallocate( eqns_data%mLayerVolFracWatTrial ) - deallocate( eqns_data%mLayerVolFracWatPrev ) - deallocate( eqns_data%mLayerVolFracIceTrial ) - deallocate( eqns_data%mLayerTempPrev ) - deallocate( eqns_data%mLayerTempTrial ) - deallocate( eqns_data%mLayerVolFracIcePrev ) - deallocate( eqns_data%mLayerVolFracLiqPrev ) - deallocate( eqns_data%mLayerEnthalpyTrial ) - deallocate( eqns_data%mLayerEnthalpyPrev ) - - call FIDAFree(ida_mem) - retval = FSUNNonlinSolFree(sunnonlin_NLS) - retval = FSUNLinSolFree(sunlinsol_LS) - call FSUNMatDestroy(sunmat_A) - call FN_VDestroy(sunvec_y) - call FN_VDestroy(sunvec_yp) - - end subroutine summaSolveSundialsIDA + ! free memory + deallocate( eqns_data%sMul ) + deallocate( eqns_data%dMat ) + deallocate( eqns_data%dBaseflow_dMatric ) + deallocate( eqns_data%mLayerMatricHeadLiqTrial ) + deallocate( eqns_data%mLayerMatricHeadTrial ) + deallocate( eqns_data%mLayerMatricHeadPrev ) + deallocate( eqns_data%fluxVec ) + deallocate( eqns_data%resSink ) + deallocate( eqns_data%mLayerVolFracWatTrial ) + deallocate( eqns_data%mLayerVolFracWatPrev ) + deallocate( eqns_data%mLayerVolFracIceTrial ) + deallocate( eqns_data%mLayerTempPrev ) + deallocate( eqns_data%mLayerTempTrial ) + deallocate( eqns_data%mLayerVolFracIcePrev ) + deallocate( eqns_data%mLayerVolFracLiqPrev ) + deallocate( eqns_data%mLayerEnthalpyTrial ) + deallocate( eqns_data%mLayerEnthalpyPrev ) + + call FIDAFree(ida_mem) + retval = FSUNNonlinSolFree(sunnonlin_NLS) + retval = FSUNLinSolFree(sunlinsol_LS) + call FSUNMatDestroy(sunmat_A) + call FN_VDestroy(sunvec_y) + call FN_VDestroy(sunvec_yp) + +end subroutine summaSolveSundialsIDA ! ---------------------------------------------------------------- ! SetInitialCondition: routine to initialize u and up vectors. ! ---------------------------------------------------------------- - subroutine setInitialCondition(neq, y, sunvec_u, sunvec_up) +subroutine setInitialCondition(neq, y, sunvec_u, sunvec_up) - !======= Inclusions =========== - USE, intrinsic :: iso_c_binding - USE fsundials_nvector_mod - USE fnvector_serial_mod + !======= Inclusions =========== + USE, intrinsic :: iso_c_binding + USE fsundials_nvector_mod + USE fnvector_serial_mod - !======= Declarations ========= - implicit none + !======= Declarations ========= + implicit none - ! calling variables - type(N_Vector) :: sunvec_u ! solution N_Vector - type(N_Vector) :: sunvec_up ! derivative N_Vector - integer(c_long) :: neq - real(rkind) :: y(neq) + ! calling variables + type(N_Vector) :: sunvec_u ! solution N_Vector + type(N_Vector) :: sunvec_up ! derivative N_Vector + integer(c_long) :: neq + real(rkind) :: y(neq) - ! pointers to data in SUNDIALS vectors - real(c_double), pointer :: uu(:) - real(c_double), pointer :: up(:) + ! pointers to data in SUNDIALS vectors + real(c_double), pointer :: uu(:) + real(c_double), pointer :: up(:) - ! get data arrays from SUNDIALS vectors - uu(1:neq) => FN_VGetArrayPointer(sunvec_u) - up(1:neq) => FN_VGetArrayPointer(sunvec_up) + ! get data arrays from SUNDIALS vectors + uu(1:neq) => FN_VGetArrayPointer(sunvec_u) + up(1:neq) => FN_VGetArrayPointer(sunvec_up) - uu = y - up = 0._rkind + uu = y + up = 0._rkind - end subroutine setInitialCondition +end subroutine setInitialCondition ! ---------------------------------------------------------------- ! setSolverParams: private routine to set parameters in ida solver ! ---------------------------------------------------------------- - subroutine setSolverParams(dt,ida_mem,retval) - - !======= Inclusions =========== - USE, intrinsic :: iso_c_binding - USE fida_mod ! Fortran interface to IDA - - !======= Declarations ========= - implicit none - - ! calling variables - real(rkind),intent(in) :: dt ! time step - type(c_ptr),intent(inout) :: ida_mem ! IDA memory - integer(i4b),intent(out) :: retval ! return value - - !======= Internals ============ - real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 - integer,parameter :: max_order = 1 ! maximum BDF order, default = 5 - integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 - integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 - integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 - integer(kind = 8),parameter :: max_step = 999999 ! maximum number of steps, dafault = 500 - real(qp),parameter :: h_init = 0 ! initial stepsize - real(qp) :: h_max ! maximum stepsize, dafault = infinity - - ! Set the maximum BDF order - retval = FIDASetMaxOrd(ida_mem, max_order) - if (retval /= 0) return - - ! Set Coeff. in the nonlinear convergence test - retval = FIDASetNonlinConvCoef(ida_mem, coef_nonlin) - if (retval /= 0) return - - ! Set maximun number of nonliear iterations - retval = FIDASetMaxNonlinIters(ida_mem, nonlin_iter) - if (retval /= 0) return - - ! Set maximum number of convergence test failures - retval = FIDASetMaxConvFails(ida_mem, convtest_fail) - if (retval /= 0) return - - ! Set maximum number of error test failures - retval = FIDASetMaxErrTestFails(ida_mem, acurtest_fail) - if (retval /= 0) return - - ! Set maximum number of steps - retval = FIDASetMaxNumSteps(ida_mem, max_step) - if (retval /= 0) return - - ! Set maximum stepsize - h_max = dt - retval = FIDASetMaxStep(ida_mem, h_max) - if (retval /= 0) return - - ! Set initial stepsize - retval = FIDASetInitStep(ida_mem, h_init) - if (retval /= 0) return - - ! scaling on 1 and off 0 - ! retval = FIDASetLinearSolutionScaling(ida_mem, 0) - ! if (retval /= 0) return - - end subroutine setSolverParams - - ! ********************************************************************************************************* - ! private subroutine implctMelt: compute melt of the "snow without a layer" - ! ********************************************************************************************************* - subroutine implctMelt(& - ! input/output: integrated snowpack properties - scalarSWE, & ! intent(inout): snow water equivalent (kg m-2) - scalarSnowDepth, & ! intent(inout): snow depth (m) - scalarSfcMeltPond, & ! intent(inout): surface melt pond (kg m-2) - ! input/output: properties of the upper-most soil layer - soilTemp, & ! intent(inout): surface layer temperature (K) - soilDepth, & ! intent(inout): surface layer depth (m) - soilHeatcap, & ! intent(inout): surface layer volumetric heat capacity (J m-3 K-1) - ! output: error control - err,message ) ! intent(out): error control - implicit none - ! input/output: integrated snowpack properties - real(rkind),intent(inout) :: scalarSWE ! snow water equivalent (kg m-2) - real(rkind),intent(inout) :: scalarSnowDepth ! snow depth (m) - real(rkind),intent(inout) :: scalarSfcMeltPond ! surface melt pond (kg m-2) - ! input/output: properties of the upper-most soil layer - real(rkind),intent(inout) :: soilTemp ! surface layer temperature (K) - real(rkind),intent(inout) :: soilDepth ! surface layer depth (m) - real(rkind),intent(inout) :: soilHeatcap ! surface layer volumetric heat capacity (J m-3 K-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! local variables - real(rkind) :: nrgRequired ! energy required to melt all the snow (J m-2) - real(rkind) :: nrgAvailable ! energy available to melt the snow (J m-2) - real(rkind) :: snwDensity ! snow density (kg m-3) - ! initialize error control - err=0; message='implctMelt/' - - if(scalarSWE > 0._rkind)then - ! only melt if temperature of the top soil layer is greater than Tfreeze - if(soilTemp > Tfreeze)then - ! compute the energy required to melt all the snow (J m-2) - nrgRequired = scalarSWE*LH_fus - ! compute the energy available to melt the snow (J m-2) - nrgAvailable = soilHeatcap*(soilTemp - Tfreeze)*soilDepth - ! compute the snow density (not saved) - snwDensity = scalarSWE/scalarSnowDepth - ! compute the amount of melt, and update SWE (kg m-2) - if(nrgAvailable > nrgRequired)then - scalarSfcMeltPond = scalarSWE - scalarSWE = 0._rkind - else - scalarSfcMeltPond = nrgAvailable/LH_fus - scalarSWE = scalarSWE - scalarSfcMeltPond - end if - ! update depth - scalarSnowDepth = scalarSWE/snwDensity - ! update temperature of the top soil layer (K) - soilTemp = soilTemp - (LH_fus*scalarSfcMeltPond/soilDepth)/soilHeatcap - else ! melt is zero if the temperature of the top soil layer is less than Tfreeze - scalarSfcMeltPond = 0._rkind ! kg m-2 - end if ! (if the temperature of the top soil layer is greater than Tfreeze) - else ! melt is zero if the "snow without a layer" does not exist - scalarSfcMeltPond = 0._rkind ! kg m-2 - end if ! (if the "snow without a layer" exists) - - end subroutine implctMelt +subroutine setSolverParams(dt,ida_mem,retval) + + !======= Inclusions =========== + USE, intrinsic :: iso_c_binding + USE fida_mod ! Fortran interface to IDA + + !======= Declarations ========= + implicit none + + ! calling variables + real(rkind),intent(in) :: dt ! time step + type(c_ptr),intent(inout) :: ida_mem ! IDA memory + integer(i4b),intent(out) :: retval ! return value + + !======= Internals ============ + real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 + integer,parameter :: max_order = 1 ! maximum BDF order, default = 5 + integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 + integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 + integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 + integer(kind = 8),parameter :: max_step = 999999 ! maximum number of steps, dafault = 500 + real(qp),parameter :: h_init = 0 ! initial stepsize + real(qp) :: h_max ! maximum stepsize, dafault = infinity + + ! Set the maximum BDF order + retval = FIDASetMaxOrd(ida_mem, max_order) + if (retval /= 0) return + + ! Set Coeff. in the nonlinear convergence test + retval = FIDASetNonlinConvCoef(ida_mem, coef_nonlin) + if (retval /= 0) return + + ! Set maximun number of nonliear iterations + retval = FIDASetMaxNonlinIters(ida_mem, nonlin_iter) + if (retval /= 0) return + + ! Set maximum number of convergence test failures + retval = FIDASetMaxConvFails(ida_mem, convtest_fail) + if (retval /= 0) return + + ! Set maximum number of error test failures + retval = FIDASetMaxErrTestFails(ida_mem, acurtest_fail) + if (retval /= 0) return + + ! Set maximum number of steps + retval = FIDASetMaxNumSteps(ida_mem, max_step) + if (retval /= 0) return + + ! Set maximum stepsize + h_max = dt + retval = FIDASetMaxStep(ida_mem, h_max) + if (retval /= 0) return + + ! Set initial stepsize + retval = FIDASetInitStep(ida_mem, h_init) + if (retval /= 0) return + +end subroutine setSolverParams + +! ********************************************************************************************************* +! private subroutine implctMelt: compute melt of the "snow without a layer" +! ********************************************************************************************************* +subroutine implctMelt(& + ! input/output: integrated snowpack properties + scalarSWE, & ! intent(inout): snow water equivalent (kg m-2) + scalarSnowDepth, & ! intent(inout): snow depth (m) + scalarSfcMeltPond, & ! intent(inout): surface melt pond (kg m-2) + ! input/output: properties of the upper-most soil layer + soilTemp, & ! intent(inout): surface layer temperature (K) + soilDepth, & ! intent(inout): surface layer depth (m) + soilHeatcap, & ! intent(inout): surface layer volumetric heat capacity (J m-3 K-1) + ! output: error control + err,message ) ! intent(out): error control + implicit none + ! input/output: integrated snowpack properties + real(rkind),intent(inout) :: scalarSWE ! snow water equivalent (kg m-2) + real(rkind),intent(inout) :: scalarSnowDepth ! snow depth (m) + real(rkind),intent(inout) :: scalarSfcMeltPond ! surface melt pond (kg m-2) + ! input/output: properties of the upper-most soil layer + real(rkind),intent(inout) :: soilTemp ! surface layer temperature (K) + real(rkind),intent(inout) :: soilDepth ! surface layer depth (m) + real(rkind),intent(inout) :: soilHeatcap ! surface layer volumetric heat capacity (J m-3 K-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local variables + real(rkind) :: nrgRequired ! energy required to melt all the snow (J m-2) + real(rkind) :: nrgAvailable ! energy available to melt the snow (J m-2) + real(rkind) :: snwDensity ! snow density (kg m-3) + ! initialize error control + err=0; message='implctMelt/' + + if(scalarSWE > 0._rkind)then + ! only melt if temperature of the top soil layer is greater than Tfreeze + if(soilTemp > Tfreeze)then + ! compute the energy required to melt all the snow (J m-2) + nrgRequired = scalarSWE*LH_fus + ! compute the energy available to melt the snow (J m-2) + nrgAvailable = soilHeatcap*(soilTemp - Tfreeze)*soilDepth + ! compute the snow density (not saved) + snwDensity = scalarSWE/scalarSnowDepth + ! compute the amount of melt, and update SWE (kg m-2) + if(nrgAvailable > nrgRequired)then + scalarSfcMeltPond = scalarSWE + scalarSWE = 0._rkind + else + scalarSfcMeltPond = nrgAvailable/LH_fus + scalarSWE = scalarSWE - scalarSfcMeltPond + end if + ! update depth + scalarSnowDepth = scalarSWE/snwDensity + ! update temperature of the top soil layer (K) + soilTemp = soilTemp - (LH_fus*scalarSfcMeltPond/soilDepth)/soilHeatcap + else ! melt is zero if the temperature of the top soil layer is less than Tfreeze + scalarSfcMeltPond = 0._rkind ! kg m-2 + end if ! (if the temperature of the top soil layer is greater than Tfreeze) + else ! melt is zero if the "snow without a layer" does not exist + scalarSfcMeltPond = 0._rkind ! kg m-2 + end if ! (if the "snow without a layer" exists) + +end subroutine implctMelt end module summaSolveSundialsIDA_module From 5d760b2589e76ebe7a170d5207e7abb1d46c0579 Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 26 Sep 2022 21:55:09 +0000 Subject: [PATCH 0355/1472] adjusted indentation --- build/source/engine/evalDAE4IDA.f90 | 304 ++++++++++++++-------------- build/source/engine/evalJac4IDA.f90 | 238 +++++++++++----------- 2 files changed, 269 insertions(+), 273 deletions(-) diff --git a/build/source/engine/evalDAE4IDA.f90 b/build/source/engine/evalDAE4IDA.f90 index 8565fe6c8..22cdbc02d 100644 --- a/build/source/engine/evalDAE4IDA.f90 +++ b/build/source/engine/evalDAE4IDA.f90 @@ -1,166 +1,164 @@ +module evalDAE4IDA_module -module evalDAE4IDA_module +!======= Inclusions =========== +use, intrinsic :: iso_c_binding +use nrtype +use type4IDA +USE globalData,only:model_decisions ! model decision structure +USE globalData,only:flux_meta ! metadata on the model fluxes +! provide access to the derived types to define the data structures +USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (rkind) + model_options ! defines the model decisions +USE multiconst,only:iden_water ! intrinsic density of liquid water (kg m-3) +USE var_lookup,only:iLookDIAG +USE var_lookup,only:iLookPROG +USE var_lookup,only:iLookINDEX ! named variables for structure elements +USE var_lookup,only:iLookDERIV ! named variables for structure elements + + +! privacy +implicit none +private +public::evalDAE4IDA + + +contains +! ********************************************************************************************************** +! public function evalDAE4IDA: compute the residual vector F(t,y,y') required for IDA solver +! ********************************************************************************************************** +! Return values: +! 0 = success, +! 1 = recoverable error, +! -1 = non-recoverable error +! ---------------------------------------------------------------- +integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_data) & + result(ierr) bind(C,name='evalDAE4IDA') !======= Inclusions =========== use, intrinsic :: iso_c_binding + use fida_mod + use fsundials_nvector_mod + use fnvector_serial_mod use nrtype use type4IDA - USE globalData,only:model_decisions ! model decision structure - USE globalData,only:flux_meta ! metadata on the model fluxes - ! provide access to the derived types to define the data structures - USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - model_options ! defines the model decisions - USE multiconst,only:iden_water ! intrinsic density of liquid water (kg m-3) - USE var_lookup,only:iLookDIAG - USE var_lookup,only:iLookPROG - USE var_lookup,only:iLookINDEX ! named variables for structure elements - USE var_lookup,only:iLookDERIV ! named variables for structure elements - - - ! privacy - implicit none - private - public::evalDAE4IDA - + use eval8summaSundials_module,only:eval8summaSundials -contains + !======= Declarations ========= + implicit none - ! ********************************************************************************************************** - ! public function evalDAE4IDA: compute the residual vector F(t,y,y') required for IDA solver - ! ********************************************************************************************************** - ! Return values: - ! 0 = success, - ! 1 = recoverable error, - ! -1 = non-recoverable error - ! ---------------------------------------------------------------- - integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_data) & - result(ierr) bind(C,name='evalDAE4IDA') - - !======= Inclusions =========== - use, intrinsic :: iso_c_binding - use fida_mod - use fsundials_nvector_mod - use fnvector_serial_mod - use nrtype - use type4IDA - use eval8summaSundials_module,only:eval8summaSundials - - !======= Declarations ========= - implicit none - - ! calling variables - real(rkind), value :: tres ! current time t - type(N_Vector) :: sunvec_y ! solution N_Vector y - type(N_Vector) :: sunvec_yp ! derivative N_Vector y' - type(N_Vector) :: sunvec_r ! residual N_Vector F(t,y,y') - type(c_ptr), value :: user_data ! user-defined data - - - ! pointers to data in SUNDIALS vectors - type(eqnsData), pointer :: eqns_data ! equations data - real(rkind), pointer :: stateVec(:) - real(rkind), pointer :: stateVecPrime(:) - real(rkind), pointer :: rVec(:) - logical(lgt) :: feasible - integer(i4b) :: retval - real(c_double) :: stepsize_next(1) - !======= Internals ============ - - ! get equations data from user-defined data - call c_f_pointer(user_data, eqns_data) - - ! get data arrays from SUNDIALS vectors - stateVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_y) - stateVecPrime(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_yp) - rVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_r) - - retval = FIDAGetCurrentStep(eqns_data%ida_mem, stepsize_next) - if (retval /= 0) then - print *, 'Error in FIDAGetCurrentStep, retval = ', retval, '; halting' - stop 1 - end if - - ! compute the flux and the residual vector for a given state vector - call eval8summaSundials(& - ! input: model control - stepsize_next(1), & ! intent(in): current stepsize - eqns_data%dt, & ! intent(in): data step - eqns_data%nSnow, & ! intent(in): number of snow layers - eqns_data%nSoil, & ! intent(in): number of soil layers - eqns_data%nLayers, & ! intent(in): number of layers - eqns_data%nState, & ! intent(in): number of state variables in the current subset - .true., & ! intent(in): inside Sundials solver - eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation - eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVec, & ! intent(in): model state vector - stateVecPrime, & ! intent(in): model state vector - eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - eqns_data%lookup_data, & ! intent(in): lookup data - eqns_data%type_data, & ! intent(in): type of vegetation and soil - eqns_data%attr_data, & ! intent(in): spatial attributes - eqns_data%mpar_data, & ! intent(in): model parameters - eqns_data%forc_data, & ! intent(in): model forcing data - eqns_data%bvar_data, & ! intent(in): average model variables for the entire basin - eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - eqns_data%indx_data, & ! intent(inou): index data - eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU - eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) - eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian - eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) - eqns_data%scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) - eqns_data%scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - eqns_data%scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) - eqns_data%scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) - eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): value for enthalpy of the vegetation canopy (J m-3) - eqns_data%mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - eqns_data%mLayerTempPrev, & ! intent(in): vector of layer temperature (K) - eqns_data%mLayerMatricHeadLiqTrial,& ! intent(out): trial value for liquid water matric potential (m) - eqns_data%mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) - eqns_data%mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) - eqns_data%mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - eqns_data%mLayerVolFracWatPrev, & ! intent(in): vector of volumetric total water content (-) - eqns_data%mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) - eqns_data%mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) - eqns_data%mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - eqns_data%mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) - eqns_data%scalarAquiferStorageTrial, & ! intent(out): trial value of storage of water in the aquifer (m) - eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) - eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) - eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) - eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer - ! output - feasible, & ! intent(out): flag to denote the feasibility of the solution - eqns_data%fluxVec, & ! intent(out): flux vector - eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector - eqns_data%err,eqns_data%message) ! intent(out): error control - - if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif - if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif - if(.not.feasible)then; eqns_data%message=trim(eqns_data%message)//'state vector not feasible'; ierr = 1; return; endif - - ! return success - ierr = 0 - return - - end function evalDAE4IDA + ! calling variables + real(rkind), value :: tres ! current time t + type(N_Vector) :: sunvec_y ! solution N_Vector y + type(N_Vector) :: sunvec_yp ! derivative N_Vector y' + type(N_Vector) :: sunvec_r ! residual N_Vector F(t,y,y') + type(c_ptr), value :: user_data ! user-defined data + + + ! pointers to data in SUNDIALS vectors + type(eqnsData), pointer :: eqns_data ! equations data + real(rkind), pointer :: stateVec(:) + real(rkind), pointer :: stateVecPrime(:) + real(rkind), pointer :: rVec(:) + logical(lgt) :: feasible + integer(i4b) :: retval + real(c_double) :: stepsize_next(1) + !======= Internals ============ + + ! get equations data from user-defined data + call c_f_pointer(user_data, eqns_data) + + ! get data arrays from SUNDIALS vectors + stateVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_y) + stateVecPrime(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_yp) + rVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_r) + + retval = FIDAGetCurrentStep(eqns_data%ida_mem, stepsize_next) + if (retval /= 0) then + print *, 'Error in FIDAGetCurrentStep, retval = ', retval, '; halting' + stop 1 + end if + + ! compute the flux and the residual vector for a given state vector + call eval8summaSundials(& + ! input: model control + stepsize_next(1), & ! intent(in): current stepsize + eqns_data%dt, & ! intent(in): data step + eqns_data%nSnow, & ! intent(in): number of snow layers + eqns_data%nSoil, & ! intent(in): number of soil layers + eqns_data%nLayers, & ! intent(in): number of layers + eqns_data%nState, & ! intent(in): number of state variables in the current subset + .true., & ! intent(in): inside Sundials solver + eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation + eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVec, & ! intent(in): model state vector + stateVecPrime, & ! intent(in): model state vector + eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + eqns_data%lookup_data, & ! intent(in): lookup data + eqns_data%type_data, & ! intent(in): type of vegetation and soil + eqns_data%attr_data, & ! intent(in): spatial attributes + eqns_data%mpar_data, & ! intent(in): model parameters + eqns_data%forc_data, & ! intent(in): model forcing data + eqns_data%bvar_data, & ! intent(in): average model variables for the entire basin + eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + eqns_data%indx_data, & ! intent(inou): index data + eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU + eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) + eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian + eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) + eqns_data%scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) + eqns_data%scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + eqns_data%scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) + eqns_data%scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) + eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): value for enthalpy of the vegetation canopy (J m-3) + eqns_data%mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + eqns_data%mLayerTempPrev, & ! intent(in): vector of layer temperature (K) + eqns_data%mLayerMatricHeadLiqTrial,& ! intent(out): trial value for liquid water matric potential (m) + eqns_data%mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) + eqns_data%mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) + eqns_data%mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) + eqns_data%mLayerVolFracWatPrev, & ! intent(in): vector of volumetric total water content (-) + eqns_data%mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) + eqns_data%mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) + eqns_data%mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) + eqns_data%mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) + eqns_data%scalarAquiferStorageTrial, & ! intent(out): trial value of storage of water in the aquifer (m) + eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) + eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) + eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) + eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer + ! output + feasible, & ! intent(out): flag to denote the feasibility of the solution + eqns_data%fluxVec, & ! intent(out): flux vector + eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec, & ! intent(out): residual vector + eqns_data%err,eqns_data%message) ! intent(out): error control + + if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif + if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif + if(.not.feasible)then; eqns_data%message=trim(eqns_data%message)//'state vector not feasible'; ierr = 1; return; endif + + ! return success + ierr = 0 + return + +end function evalDAE4IDA end module evalDAE4IDA_module diff --git a/build/source/engine/evalJac4IDA.f90 b/build/source/engine/evalJac4IDA.f90 index 873db431c..0995a9d5f 100644 --- a/build/source/engine/evalJac4IDA.f90 +++ b/build/source/engine/evalJac4IDA.f90 @@ -1,133 +1,131 @@ - - module evalJac4IDA_module - !======= Inclusions =========== - use, intrinsic :: iso_c_binding - use nrtype - use type4IDA - USE globalData,only:model_decisions ! model decision structure - ! provide access to the derived types to define the data structures - USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - model_options ! defines the model decisions +!======= Inclusions =========== +use, intrinsic :: iso_c_binding +use nrtype +use type4IDA +USE globalData,only:model_decisions ! model decision structure +! provide access to the derived types to define the data structures +USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (rkind) + model_options ! defines the model decisions - ! privacy - implicit none - private - public::evalJac4IDA +! privacy +implicit none +private +public::evalJac4IDA contains - ! ********************************************************************************************************** - ! public function evalJac4IDA: the interface to compute the Jacobian matrix dF/dy + c dF/dy' for IDA solver - ! ********************************************************************************************************** - ! Return values: - ! 0 = success, - ! 1 = recoverable error, - ! -1 = non-recoverable error - ! ---------------------------------------------------------------- - integer(c_int) function evalJac4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & - sunmat_J, user_data, sunvec_temp1, sunvec_temp2, sunvec_temp3) & - result(ierr) bind(C,name='evalJac4IDA') - - !======= Inclusions =========== - use, intrinsic :: iso_c_binding - use fsundials_nvector_mod - use fsundials_matrix_mod - use fnvector_serial_mod - use fsunmatrix_band_mod - use fsunmatrix_dense_mod - use nrtype - use type4IDA - use eval8JacDAE_module,only:eval8JacDAE ! compute Jacobian matrix - USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix - USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix - USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix - !======= Declarations ========= - implicit none - - ! calling variables - real(rkind), value :: t ! current time - real(rkind), value :: cj ! step size scaling factor - type(N_Vector) :: sunvec_y ! solution N_Vector - type(N_Vector) :: sunvec_yp ! derivative N_Vector - type(N_Vector) :: sunvec_r ! residual N_Vector - type(SUNMatrix) :: sunmat_J ! Jacobian SUNMatrix - type(c_ptr), value :: user_data ! user-defined data - type(N_Vector) :: sunvec_temp1 ! temporary N_Vector - type(N_Vector) :: sunvec_temp2 ! temporary N_Vector - type(N_Vector) :: sunvec_temp3 ! temporary N_Vector - - ! pointers to data in SUNDIALS vectors - real(rkind), pointer :: stateVec(:) ! state vector - real(rkind), pointer :: stateVecPrime(:)! derivative of the state vector - real(rkind), pointer :: rVec(:) ! residual vector - real(rkind), pointer :: Jac(:,:) ! Jacobian matrix - type(eqnsData), pointer :: eqns_data ! equations data - - - - !======= Internals ============ - - ! get equations data from user-defined data - call c_f_pointer(user_data, eqns_data) - - - ! get data arrays from SUNDIALS vectors - stateVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_y) - stateVecPrime(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_yp) - rVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_r) - if (eqns_data%ixMatrix==ixBandMatrix) Jac(1:nBands, 1:eqns_data%nState) => FSUNBandMatrix_Data(sunmat_J) - if (eqns_data%ixMatrix==ixFullMatrix) Jac(1:eqns_data%nState, 1:eqns_data%nState) => FSUNDenseMatrix_Data(sunmat_J) - - ! compute Jacobian matrix - call eval8JacDAE(& - ! input: model control - cj, & ! intent(in): this scalar changes whenever the step size or method order changes - eqns_data%dt, & ! intent(in): data step - eqns_data%nSnow, & ! intent(in): number of snow layers - eqns_data%nSoil, & ! intent(in): number of soil layers - eqns_data%nLayers, & ! intent(in): number of layers - eqns_data%ixMatrix, & ! intent(in): type of matrix (dense or banded) - eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVec, & ! intent(in): model state vector - stateVecPrime, & ! intent(in): model state vector - eqns_data%sMul, & ! intent(in): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - eqns_data%mpar_data, & ! intent(in): model parameters - eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - eqns_data%indx_data, & ! intent(inou): index data - eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU - eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input: baseflow - eqns_data%dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! output - eqns_data%dMat, & ! intetn(inout): diagonal of the Jacobian matrix - Jac, & ! intent(out): Jacobian matrix - eqns_data%err,eqns_data%message) ! intent(out): error control - - if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif - if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif - - ! return success - ierr = 0 - return - - - - end function evalJac4IDA +! ********************************************************************************************************** +! public function evalJac4IDA: the interface to compute the Jacobian matrix dF/dy + c dF/dy' for IDA solver +! ********************************************************************************************************** +! Return values: +! 0 = success, +! 1 = recoverable error, +! -1 = non-recoverable error +! ---------------------------------------------------------------- +integer(c_int) function evalJac4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & + sunmat_J, user_data, sunvec_temp1, sunvec_temp2, sunvec_temp3) & + result(ierr) bind(C,name='evalJac4IDA') + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use fsundials_nvector_mod + use fsundials_matrix_mod + use fnvector_serial_mod + use fsunmatrix_band_mod + use fsunmatrix_dense_mod + use nrtype + use type4IDA + use eval8JacDAE_module,only:eval8JacDAE ! compute Jacobian matrix + USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix + USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix + USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix + !======= Declarations ========= + implicit none + + ! calling variables + real(rkind), value :: t ! current time + real(rkind), value :: cj ! step size scaling factor + type(N_Vector) :: sunvec_y ! solution N_Vector + type(N_Vector) :: sunvec_yp ! derivative N_Vector + type(N_Vector) :: sunvec_r ! residual N_Vector + type(SUNMatrix) :: sunmat_J ! Jacobian SUNMatrix + type(c_ptr), value :: user_data ! user-defined data + type(N_Vector) :: sunvec_temp1 ! temporary N_Vector + type(N_Vector) :: sunvec_temp2 ! temporary N_Vector + type(N_Vector) :: sunvec_temp3 ! temporary N_Vector + + ! pointers to data in SUNDIALS vectors + real(rkind), pointer :: stateVec(:) ! state vector + real(rkind), pointer :: stateVecPrime(:)! derivative of the state vector + real(rkind), pointer :: rVec(:) ! residual vector + real(rkind), pointer :: Jac(:,:) ! Jacobian matrix + type(eqnsData), pointer :: eqns_data ! equations data + + + + !======= Internals ============ + + ! get equations data from user-defined data + call c_f_pointer(user_data, eqns_data) + + + ! get data arrays from SUNDIALS vectors + stateVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_y) + stateVecPrime(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_yp) + rVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_r) + if (eqns_data%ixMatrix==ixBandMatrix) Jac(1:nBands, 1:eqns_data%nState) => FSUNBandMatrix_Data(sunmat_J) + if (eqns_data%ixMatrix==ixFullMatrix) Jac(1:eqns_data%nState, 1:eqns_data%nState) => FSUNDenseMatrix_Data(sunmat_J) + + ! compute Jacobian matrix + call eval8JacDAE(& + ! input: model control + cj, & ! intent(in): this scalar changes whenever the step size or method order changes + eqns_data%dt, & ! intent(in): data step + eqns_data%nSnow, & ! intent(in): number of snow layers + eqns_data%nSoil, & ! intent(in): number of soil layers + eqns_data%nLayers, & ! intent(in): number of layers + eqns_data%ixMatrix, & ! intent(in): type of matrix (dense or banded) + eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVec, & ! intent(in): model state vector + stateVecPrime, & ! intent(in): model state vector + eqns_data%sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + eqns_data%mpar_data, & ! intent(in): model parameters + eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + eqns_data%indx_data, & ! intent(inou): index data + eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU + eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input: baseflow + eqns_data%dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! output + eqns_data%dMat, & ! intetn(inout): diagonal of the Jacobian matrix + Jac, & ! intent(out): Jacobian matrix + eqns_data%err,eqns_data%message) ! intent(out): error control + + if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif + if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif + + ! return success + ierr = 0 + return + + + +end function evalJac4IDA end module evalJac4IDA_module From 45556ae00ac1df7ea9d0f862ae6fecf4cbb04dff Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 26 Sep 2022 22:03:16 +0000 Subject: [PATCH 0356/1472] indentation changes --- build/source/engine/eval8JacDAE.f90 | 496 ++++++++++++++-------------- 1 file changed, 248 insertions(+), 248 deletions(-) diff --git a/build/source/engine/eval8JacDAE.f90 b/build/source/engine/eval8JacDAE.f90 index 97915d669..139d9c9ec 100644 --- a/build/source/engine/eval8JacDAE.f90 +++ b/build/source/engine/eval8JacDAE.f90 @@ -84,260 +84,260 @@ module eval8JacDAE_module contains - ! ********************************************************************************************************** - ! public subroutine eval8JacDAE: compute the Jacobian matrix - ! ********************************************************************************************************** - subroutine eval8JacDAE(& - ! input: model control - cj, & ! intent(in): this scalar changes whenever the step size or method order changes - dt, & ! intent(in): time step - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - ixMatrix, & ! intent(in): form of the Jacobian matrix - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVec, & ! intent(in): model state vector - stateVecPrime, & ! intent(in): derivative of model state vector - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input: baseflow - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! output: flux and residual vectors - dMat, & ! intent(inout): diagonal of Jacobian Matrix - Jac, & ! intent(out): jacobian matrix - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - ! provide access to subroutines - USE getVectorz_module, only:varExtract ! extract variables from the state vector - USE updateVarsSundials_module, only:updateVarsSundials ! update prognostic variables - USE computJacobSundials_module,only:computJacobSundials - implicit none - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - ! input: model control - real(rkind),intent(in) :: cj ! this scalar changes whenever the step size or method order changes - real(rkind),intent(in) :: dt ! time step - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - ! input: state vectors - real(rkind),intent(in) :: stateVec(:) ! model state vector - real(rkind),intent(in) :: stateVecPrime(:) ! model state vector - real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) - ! input: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(var_dlength), intent(in) :: mpar_data ! model parameters - type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU - ! output: data structures - type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - ! output: Jacobian - real(rkind), intent(inout) :: dMat(:) - real(rkind), intent(out) :: Jac(:,:) ! jacobian matrix - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - ! -------------------------------------------------------------------------------------------------------------------------------- - ! state variables - real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial value for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial value for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial value for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) - real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) - ! diagnostic variables - real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) - ! derivative of state variables - real(rkind) :: scalarCanairTempPrime ! derivative value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempPrime ! derivative value for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! derivative value for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! derivative value for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! derivative value for liquid water matric potential (m) - real(rkind) :: scalarAquiferStoragePrime ! derivative value of storage of water in the aquifer (m) - ! derivative of diagnostic variables - real(rkind) :: scalarCanopyLiqPrime ! derivative value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIcePrime ! derivative value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! derivative value for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! derivative value for volumetric fraction of ice (-) - ! other local variables - character(LEN=256) :: cmessage ! error message of downwind routine - real(rkind) :: dt1 +! ********************************************************************************************************** +! public subroutine eval8JacDAE: compute the Jacobian matrix +! ********************************************************************************************************** +subroutine eval8JacDAE(& + ! input: model control + cj, & ! intent(in): this scalar changes whenever the step size or method order changes + dt, & ! intent(in): time step + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + ixMatrix, & ! intent(in): form of the Jacobian matrix + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVec, & ! intent(in): model state vector + stateVecPrime, & ! intent(in): derivative of model state vector + sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + indx_data, & ! intent(inout): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input: baseflow + dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! output: flux and residual vectors + dMat, & ! intent(inout): diagonal of Jacobian Matrix + Jac, & ! intent(out): jacobian matrix + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- + ! provide access to subroutines + USE getVectorz_module, only:varExtract ! extract variables from the state vector + USE updateVarsSundials_module, only:updateVarsSundials ! update prognostic variables + USE computJacobSundials_module,only:computJacobSundials + implicit none + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + ! input: model control + real(rkind),intent(in) :: cj ! this scalar changes whenever the step size or method order changes + real(rkind),intent(in) :: dt ! time step + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + ! input: state vectors + real(rkind),intent(in) :: stateVec(:) ! model state vector + real(rkind),intent(in) :: stateVecPrime(:) ! model state vector + real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + ! input: data structures + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(var_dlength), intent(in) :: mpar_data ! model parameters + type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU + ! output: data structures + type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + ! output: Jacobian + real(rkind), intent(inout) :: dMat(:) + real(rkind), intent(out) :: Jac(:,:) ! jacobian matrix + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + ! -------------------------------------------------------------------------------------------------------------------------------- + ! state variables + real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial value for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial value for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial value for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) + real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + ! diagnostic variables + real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) + ! derivative of state variables + real(rkind) :: scalarCanairTempPrime ! derivative value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempPrime ! derivative value for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! derivative value for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! derivative value for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! derivative value for liquid water matric potential (m) + real(rkind) :: scalarAquiferStoragePrime ! derivative value of storage of water in the aquifer (m) + ! derivative of diagnostic variables + real(rkind) :: scalarCanopyLiqPrime ! derivative value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIcePrime ! derivative value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! derivative value for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! derivative value for volumetric fraction of ice (-) + ! other local variables + character(LEN=256) :: cmessage ! error message of downwind routine + real(rkind) :: dt1 - ! -------------------------------------------------------------------------------------------------------------------------------- - ! association to variables in the data structures - ! -------------------------------------------------------------------------------------------------------------------------------- - associate(& - ! model decisions - ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation - ! soil parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) - ! model state variables - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) - ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision & - ) ! association to variables in the data structures - ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="eval8JacDAE/" + ! -------------------------------------------------------------------------------------------------------------------------------- + ! association to variables in the data structures + ! -------------------------------------------------------------------------------------------------------------------------------- + associate(& + ! model decisions + ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation + ! soil parameters + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) + ! model state variables + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) + ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision & + ) ! association to variables in the data structures + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="eval8JacDAE/" - ! extract variables from the model state vector - call varExtract(& - ! input - stateVec, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + ! extract variables from the model state vector + call varExtract(& + ! input + stateVec, & ! intent(in): model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) + mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) + ! output: variables for the aquifer + scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! extract derivative of variables from derivative of the model state vector - call varExtract(& - ! input - stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy - scalarCanairTempPrime, & ! intent(out): derivative of canopy air temperature (K) - scalarCanopyTempPrime, & ! intent(out): derivative of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(out): derivative of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(out): derivative of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempPrime, & ! intent(out): derivative of layer temperature (K) - mLayerVolFracWatPrime, & ! intent(out): derivative of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(out): derivative of volumetric liquid water content (-) - mLayerMatricHeadPrime, & ! intent(out): derivative of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(out): derivative of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStoragePrime,& ! intent(out): derivative of storage of water in the aquifer (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + ! extract derivative of variables from derivative of the model state vector + call varExtract(& + ! input + stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy + scalarCanairTempPrime, & ! intent(out): derivative of canopy air temperature (K) + scalarCanopyTempPrime, & ! intent(out): derivative of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(out): derivative of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(out): derivative of canopy liquid water (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempPrime, & ! intent(out): derivative of layer temperature (K) + mLayerVolFracWatPrime, & ! intent(out): derivative of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(out): derivative of volumetric liquid water content (-) + mLayerMatricHeadPrime, & ! intent(out): derivative of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(out): derivative of liquid water matric potential (m) + ! output: variables for the aquifer + scalarAquiferStoragePrime,& ! intent(out): derivative of storage of water in the aquifer (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - call updateVarsSundials(& - ! input - dt, & ! intent(in): time step - .true., & ! intent(in): logical flag if computing Jacobian for sundials solver - .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - mLayerVolFracWatTrial, & ! intent(in): use current vector for prev vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): use current vector for prev vector of total water matric potential (m) - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2 - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! - mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) - mLayerVolFracIcePrime, & ! - mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + call updateVarsSundials(& + ! input + dt, & ! intent(in): time step + .true., & ! intent(in): logical flag if computing Jacobian for sundials solver + .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + mLayerVolFracWatTrial, & ! intent(in): use current vector for prev vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): use current vector for prev vector of total water matric potential (m) + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output: variables for the vegetation canopy + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2 + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempPrime, & ! + mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) + mLayerVolFracIcePrime, & ! + mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! ----- - ! * compute the Jacobian matrix... - ! -------------------------------- + ! ----- + ! * compute the Jacobian matrix... + ! -------------------------------- - ! compute the analytical Jacobian matrix - ! NOTE: The derivatives were computed in the previous call to computFlux - ! This occurred either at the call to eval8summaSundials at the start of sysSolveSundials - ! or in the call to eval8summaSundials in the previous iteration - dt1 = 1._qp - call computJacobSundials(& - ! input: model control - cj, & ! intent(in): this scalar changes whenever the step size or method order changes - dt1, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - (ixGroundwater==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow - ixMatrix, & ! intent(in): form of the Jacobian matrix - specificStorage, & ! intent(in): specific storage coefficient (m-1) - theta_sat, & ! intent(in): soil porosity (-) - ixRichards, & ! intent(in): choice of option for Richards' equation - ! input: data structures - indx_data, & ! intent(in): index data - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! input: state variables - mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) - mLayerTempPrime, & ! intent(in) - mLayerMatricHeadPrime, & ! intent(in) - mLayerMatricHeadLiqPrime, & ! intent(in) - mLayerVolFracWatPrime, & ! intent(in) - scalarCanopyTempTrial, & ! intent(in) - scalarCanopyTempPrime, & ! intent(in) derivative value for temperature of the vegetation canopy (K) - scalarCanopyWatPrime, & ! intetn(in) - ! input-output: Jacobian and its diagonal - dMat, & ! intent(inout): diagonal of the Jacobian matrix - Jac, & ! intent(out): Jacobian matrix - ! output: error control - err,cmessage) ! intent(out): error code and error message - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + ! compute the analytical Jacobian matrix + ! NOTE: The derivatives were computed in the previous call to computFlux + ! This occurred either at the call to eval8summaSundials at the start of sysSolveSundials + ! or in the call to eval8summaSundials in the previous iteration + dt1 = 1._qp + call computJacobSundials(& + ! input: model control + cj, & ! intent(in): this scalar changes whenever the step size or method order changes + dt1, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + (ixGroundwater==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow + ixMatrix, & ! intent(in): form of the Jacobian matrix + specificStorage, & ! intent(in): specific storage coefficient (m-1) + theta_sat, & ! intent(in): soil porosity (-) + ixRichards, & ! intent(in): choice of option for Richards' equation + ! input: data structures + indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables + dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! input: state variables + mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) + mLayerTempPrime, & ! intent(in) + mLayerMatricHeadPrime, & ! intent(in) + mLayerMatricHeadLiqPrime, & ! intent(in) + mLayerVolFracWatPrime, & ! intent(in) + scalarCanopyTempTrial, & ! intent(in) + scalarCanopyTempPrime, & ! intent(in) derivative value for temperature of the vegetation canopy (K) + scalarCanopyWatPrime, & ! intetn(in) + ! input-output: Jacobian and its diagonal + dMat, & ! intent(inout): diagonal of the Jacobian matrix + Jac, & ! intent(out): Jacobian matrix + ! output: error control + err,cmessage) ! intent(out): error code and error message + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! end association with the information in the data structures - end associate + ! end association with the information in the data structures + end associate - end subroutine eval8JacDAE +end subroutine eval8JacDAE end module eval8JacDAE_module From af4345bdfe45c3f75e633e8a2745dd55c036407c Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 26 Sep 2022 22:24:38 +0000 Subject: [PATCH 0357/1472] removed some unnecssary lines --- build/source/engine/systemSolvSundials.f90 | 879 ++++++++++----------- 1 file changed, 432 insertions(+), 447 deletions(-) diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 854461994..bac6ba478 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -91,372 +91,368 @@ module systemSolvSundials_module contains - ! ********************************************************************************************************** - ! public subroutine systemSolvSundials: run the coupled energy-mass model for one timestep - ! ********************************************************************************************************** - subroutine systemSolvSundials(& - ! input: model control - dt, & ! intent(in): time step (s) - nState, & ! intent(in): total number of state variables - firstSubStep, & ! intent(in): flag to denote first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation - scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution - ! input/output: data structures - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): index data - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_temp, & ! intent(inout): model fluxes for a local HRU - bvar_data, & ! intent(in): model variables for the local basin - model_decisions, & ! intent(in): model decisions - stateVecInit, & ! intent(in): initial state vector - ! output - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - stateVecTrial, & ! intent(out): updated state vector - stateVecPrime, & ! intent(out): updated state vector - reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step - tooMuchMelt, & ! intent(out): flag to denote that there was too much melt - dt_out, & ! intent(out) - err,message) ! intent(out): error code and error message - ! --------------------------------------------------------------------------------------- - ! structure allocations - USE allocspace_module,only:allocLocal ! allocate local data structures - ! simulation of fluxes and residuals given a trial state vector - USE eval8summa_module,only:eval8summa ! simulation of fluxes and residuals given a trial state vector - USE eval8summaSundials_module,only:eval8summaSundials - USE getVectorz_module,only:getScaling ! get the scaling vectors - USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy - USE tol4IDA_module,only:popTol4IDA ! pop tolerances - USE summaSolveSundialsIDA_module,only:summaSolveSundialsIDA ! solve DAE by IDA - USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy - use, intrinsic :: iso_c_binding - implicit none - ! --------------------------------------------------------------------------------------- - ! * dummy variables - ! --------------------------------------------------------------------------------------- - ! input: model control - real(rkind),intent(in) :: dt ! time step (seconds) - integer(i4b),intent(in) :: nState ! total number of state variables - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call - logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - ! input/output: data structures - type(zLookup),intent(in) :: lookup_data ! lookup tables - type(var_i),intent(in) :: type_data ! type of vegetation and soil - type(var_d),intent(in) :: attr_data ! spatial attributes - type(var_d),intent(in) :: forc_data ! model forcing data - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU - type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_temp ! model fluxes for a local HRU - type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin - type(model_options),intent(in) :: model_decisions(:) ! model decisions - real(rkind),intent(in) :: stateVecInit(:) ! initial state vector (mixed units) - ! output: model control - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: stateVecTrial(:) ! trial state vector (mixed units) - real(rkind),intent(out) :: stateVecPrime(:) ! trial state vector (mixed units) - logical(lgt),intent(out) :: reduceCoupledStep ! flag to reduce the length of the coupled step - logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that there was too much melt - real(qp),intent(out) :: dt_out ! time step - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - ! --------------------------------------------------------------------------------------- - ! * general local variables - ! --------------------------------------------------------------------------------------- - character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b) :: iVar ! index of variable - integer(i4b) :: local_ixGroundwater ! local index for groundwater representation - real(rkind) :: bulkDensity ! bulk density of a given layer (kg m-3) - real(rkind) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) - real(rkind),parameter :: tempAccelerate=0.00_rkind ! factor to force initial canopy temperatures to be close to air temperature - real(rkind),parameter :: xMinCanopyWater=0.0001_rkind ! minimum value to initialize canopy water (kg m-2) - real(rkind),parameter :: tinyStep=0.000001_rkind ! stupidly small time step (s) - - ! ------------------------------------------------------------------------------------------------------ - ! * model solver - ! ------------------------------------------------------------------------------------------------------ - logical(lgt),parameter :: forceFullMatrix=.true. ! flag to force the use of the full Jacobian matrix - integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) - type(var_dlength) :: flux_init ! model fluxes at the start of the time step - real(rkind),allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! NOTE: allocatable, since not always needed - real(rkind) :: stateVecNew(nState) ! new state vector (mixed units) - real(rkind) :: fluxVec0(nState) ! flux vector (mixed units) - real(rkind) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) - real(rkind) :: xScale(nState) ! characteristic scale of the state vector (mixed units) - real(rkind) :: dMat(nState) ! diagonal matrix (excludes flux derivatives) - real(qp) :: sMul(nState) ! NOTE: qp ! multiplier for state vector for the residual calculations - real(qp) :: rVec(nState) ! NOTE: qp ! residual vector - real(rkind) :: rAdd(nState) ! additional terms in the residual vector - real(rkind) :: fOld ! function values (-); NOTE: dimensionless because scaled - logical(lgt) :: feasible ! feasibility flag - real(rkind) :: atol(nState) ! absolute telerance - real(rkind) :: rtol(nState) ! relative tolerance - integer(i4b) :: iLayer ! index of model layer in the snow+soil domain - real(rkind) :: xMin,xMax ! minimum and maximum values for water content - real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) - type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a data step - integer(i4b) :: tol_iter ! iteration index - real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix - logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step - - - ! --------------------------------------------------------------------------------------- - ! point to variables in the data structures - ! --------------------------------------------------------------------------------------- - globalVars: associate(& - ! model decisions - ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization - ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) - ! enthalpy - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) - ! soil parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) - ! model state variables - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - airtemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in): [i4b] total number of layers - ) - ! --------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="systemSolvSundials/" - - ! ***** - ! (0) PRELIMINARIES... - ! ******************** - - ! ----- - ! * initialize... - ! --------------- - - ! check - if(dt < tinyStep)then - message=trim(message)//'dt is tiny' - err=20; return - endif - - ! initialize the flags - tooMuchMelt = .false. ! too much melt - reduceCoupledStep = .false. ! need to reduce the length of the coupled step - - - ! modify the groundwater representation for this single-column implementation - select case(ixSpatialGroundwater) - case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale - case(localColumn); local_ixGroundwater = ixGroundwater ! go with the specified decision - case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return - end select ! (modify the groundwater representation for this single-column implementation) - - ! allocate space for the model fluxes at the start of the time step - call allocLocal(flux_meta(:),flux_init,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! allocate space for the baseflow derivatives - ! NOTE: needs allocation because only used when baseflow sinks are active - if(ixGroundwater==qbaseTopmodel)then - allocate(dBaseflow_dMatric(nSoil,nSoil),stat=err) ! baseflow depends on total storage in the soil column, hence on matric head in every soil layer - else - allocate(dBaseflow_dMatric(0,0),stat=err) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix - end if - if(err/=0)then; err=20; message=trim(message)//'unable to allocate space for the baseflow derivatives'; return; end if - - - ! identify the matrix solution method - ! (the type of matrix used to solve the linear system A.X=B) - if(local_ixGroundwater==qbaseTopmodel .or. scalarSolution .or. forceFullMatrix)then - ixMatrix=ixFullMatrix ! named variable to denote the full Jacobian matrix - else - ixMatrix=ixBandMatrix ! named variable to denote the band-diagonal matrix - endif - - ! initialize the model fluxes (some model fluxes are not computed in the iterations) - do iVar=1,size(flux_temp%var) - flux_init%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) - end do - - ! ----- - ! * get scaling vectors... - ! ------------------------ - - ! initialize state vectors - call getScaling(& - ! input - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output - fScale, & ! intent(out): function scaling vector (mixed units) - xScale, & ! intent(out): variable scaling vector (mixed units) - sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) - dMat, & ! intent(out): diagonal of the Jacobian matrix (excludes fluxes) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - ! initialize the trial state vectors - stateVecTrial = stateVecInit - - ! need to intialize canopy water at a positive value - if(ixVegHyd/=integerMissing)then - if(stateVecTrial(ixVegHyd) < xMinCanopyWater) stateVecTrial(ixVegHyd) = stateVecTrial(ixVegHyd) + xMinCanopyWater - endif - - ! try to accelerate solution for energy - if(ixCasNrg/=integerMissing) stateVecTrial(ixCasNrg) = stateVecInit(ixCasNrg) + (airtemp - stateVecInit(ixCasNrg))*tempAccelerate - if(ixVegNrg/=integerMissing) stateVecTrial(ixVegNrg) = stateVecInit(ixVegNrg) + (airtemp - stateVecInit(ixVegNrg))*tempAccelerate - - ! compute H_T at the beginning of the data step - call t2enthalpy_T(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTemp, & ! intent(in): value of canopy air temperature (K) - scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) - scalarCanopyWat, & ! intent(in): value of canopy total water (kg m-2) - scalarCanopyIce, & ! intent(in): value for canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - mLayerTemp, & ! intent(in): vector of layer temperature (K) - mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) - mLayerMatricHead, & ! intent(in): vector of total water matric potential (m) - mLayerVolFracIce, & ! intent(in): vector of volumetric fraction of ice (-) - ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! compute the flux and the residual vector for a given state vector - ! NOTE 1: The derivatives computed in eval8summa are used to calculate the Jacobian matrix for the first iteration - ! NOTE 2: The Jacobian matrix together with the residual vector is used to calculate the first iteration increment - - call eval8summa(& - ! input: model control - dt, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): number of layers - nState, & ! intent(in): number of state variables in the current subset - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVecTrial, & ! intent(in): model state vector - fScale, & ! intent(in): function scaling vector - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): index data - ! input-output: data structures - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - ! output - feasible, & ! intent(out): flag to denote the feasibility of the solution - fluxVec0, & ! intent(out): flux vector - rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector - fOld, & ! intent(out): function evaluation - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif - - ! copy over the initial flux structure since some model fluxes are not computed in the iterations - do concurrent ( iVar=1:size(flux_meta) ) - flux_temp%var(iVar)%dat(:) = flux_init%var(iVar)%dat(:) - end do - - ! allocate space for the temporary flux_sum structure - call allocLocal(flux_meta(:),flux_sum,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! allocate space for mLayerCmpress_sum - allocate( mLayerCmpress_sum(nSoil) ) - - ! check the need to merge snow layers - if(nSnow>0)then - ! compute the energy required to melt the top snow layer (J m-2) - bulkDensity = mLayerVolFracIce(1)*iden_ice + mLayerVolFracLiq(1)*iden_water - volEnthalpy = temp2ethpy(mLayerTemp(1),bulkDensity,snowfrz_scale) - ! set flag and error codes for too much melt - if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt)then - tooMuchMelt=.true. - message=trim(message)//'net flux in the top snow layer can melt all the snow in the top layer' - err=-20; return ! negative error code to denote a warning - endif - endif - - ! get tolerance vectors - call popTol4IDA(& - ! input - nState, & ! intent(in): number of desired state variables - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - mpar_data, & ! intent(in): model parameters - ! output - atol, & ! intent(out): absolute tolerances vector (mixed units) - rtol, & ! intent(out): relative tolerances vector (mixed units) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - !------------------- - ! * solving F(y,y') = 0 by IDA. Here, y is the state vector - ! ------------------ +! ********************************************************************************************************** +! public subroutine systemSolvSundials: run the coupled energy-mass model for one timestep +! ********************************************************************************************************** +subroutine systemSolvSundials(& + ! input: model control + dt, & ! intent(in): time step (s) + nState, & ! intent(in): total number of state variables + firstSubStep, & ! intent(in): flag to denote first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution + ! input/output: data structures + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): index data + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_temp, & ! intent(inout): model fluxes for a local HRU + bvar_data, & ! intent(in): model variables for the local basin + model_decisions, & ! intent(in): model decisions + stateVecInit, & ! intent(in): initial state vector + ! output + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + stateVecTrial, & ! intent(out): updated state vector + stateVecPrime, & ! intent(out): updated state vector + reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step + tooMuchMelt, & ! intent(out): flag to denote that there was too much melt + dt_out, & ! intent(out) + err,message) ! intent(out): error code and error message + ! --------------------------------------------------------------------------------------- + ! structure allocations + USE allocspace_module,only:allocLocal ! allocate local data structures + ! simulation of fluxes and residuals given a trial state vector + USE eval8summa_module,only:eval8summa ! simulation of fluxes and residuals given a trial state vector + USE eval8summaSundials_module,only:eval8summaSundials + USE getVectorz_module,only:getScaling ! get the scaling vectors + USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy + USE tol4IDA_module,only:popTol4IDA ! pop tolerances + USE summaSolveSundialsIDA_module,only:summaSolveSundialsIDA ! solve DAE by IDA + USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy + use, intrinsic :: iso_c_binding + implicit none + ! --------------------------------------------------------------------------------------- + ! * dummy variables + ! --------------------------------------------------------------------------------------- + ! input: model control + real(rkind),intent(in) :: dt ! time step (seconds) + integer(i4b),intent(in) :: nState ! total number of state variables + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call + logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + ! input/output: data structures + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_i),intent(in) :: type_data ! type of vegetation and soil + type(var_d),intent(in) :: attr_data ! spatial attributes + type(var_d),intent(in) :: forc_data ! model forcing data + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU + type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_temp ! model fluxes for a local HRU + type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin + type(model_options),intent(in) :: model_decisions(:) ! model decisions + real(rkind),intent(in) :: stateVecInit(:) ! initial state vector (mixed units) + ! output: model control + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + real(rkind),intent(out) :: stateVecTrial(:) ! trial state vector (mixed units) + real(rkind),intent(out) :: stateVecPrime(:) ! trial state vector (mixed units) + logical(lgt),intent(out) :: reduceCoupledStep ! flag to reduce the length of the coupled step + logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that there was too much melt + real(qp),intent(out) :: dt_out ! time step + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + + ! --------------------------------------------------------------------------------------- + ! * general local variables + ! --------------------------------------------------------------------------------------- + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iVar ! index of variable + integer(i4b) :: local_ixGroundwater ! local index for groundwater representation + real(rkind) :: bulkDensity ! bulk density of a given layer (kg m-3) + real(rkind) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) + real(rkind),parameter :: tempAccelerate=0.00_rkind ! factor to force initial canopy temperatures to be close to air temperature + real(rkind),parameter :: xMinCanopyWater=0.0001_rkind ! minimum value to initialize canopy water (kg m-2) + real(rkind),parameter :: tinyStep=0.000001_rkind ! stupidly small time step (s) + + ! ------------------------------------------------------------------------------------------------------ + ! * model solver + ! ------------------------------------------------------------------------------------------------------ + logical(lgt),parameter :: forceFullMatrix=.true. ! flag to force the use of the full Jacobian matrix + integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) + type(var_dlength) :: flux_init ! model fluxes at the start of the time step + real(rkind),allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! NOTE: allocatable, since not always needed + real(rkind) :: stateVecNew(nState) ! new state vector (mixed units) + real(rkind) :: fluxVec0(nState) ! flux vector (mixed units) + real(rkind) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) + real(rkind) :: xScale(nState) ! characteristic scale of the state vector (mixed units) + real(rkind) :: dMat(nState) ! diagonal matrix (excludes flux derivatives) + real(qp) :: sMul(nState) ! NOTE: qp ! multiplier for state vector for the residual calculations + real(qp) :: rVec(nState) ! NOTE: qp ! residual vector + real(rkind) :: rAdd(nState) ! additional terms in the residual vector + real(rkind) :: fOld ! function values (-); NOTE: dimensionless because scaled + logical(lgt) :: feasible ! feasibility flag + real(rkind) :: atol(nState) ! absolute telerance + real(rkind) :: rtol(nState) ! relative tolerance + integer(i4b) :: iLayer ! index of model layer in the snow+soil domain + real(rkind) :: xMin,xMax ! minimum and maximum values for water content + real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) + type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a data step + integer(i4b) :: tol_iter ! iteration index + real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix + logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step + + + ! --------------------------------------------------------------------------------------- + ! point to variables in the data structures + ! --------------------------------------------------------------------------------------- + globalVars: associate(& + ! model decisions + ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization + ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) + ! enthalpy + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) + ! soil parameters + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) + ! model state variables + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variables (snow and soil domains) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + airtemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in): [i4b] total number of layers + ) + ! --------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="systemSolvSundials/" + + ! ***** + ! (0) PRELIMINARIES... + ! ******************** + + ! ----- + ! * initialize... + ! --------------- + + ! check + if(dt < tinyStep)then + message=trim(message)//'dt is tiny' + err=20; return + endif + + ! initialize the flags + tooMuchMelt = .false. ! too much melt + reduceCoupledStep = .false. ! need to reduce the length of the coupled step + + + ! modify the groundwater representation for this single-column implementation + select case(ixSpatialGroundwater) + case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale + case(localColumn); local_ixGroundwater = ixGroundwater ! go with the specified decision + case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return + end select ! (modify the groundwater representation for this single-column implementation) + + ! allocate space for the model fluxes at the start of the time step + call allocLocal(flux_meta(:),flux_init,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! allocate space for the baseflow derivatives + ! NOTE: needs allocation because only used when baseflow sinks are active + if(ixGroundwater==qbaseTopmodel)then + allocate(dBaseflow_dMatric(nSoil,nSoil),stat=err) ! baseflow depends on total storage in the soil column, hence on matric head in every soil layer + else + allocate(dBaseflow_dMatric(0,0),stat=err) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix + end if + if(err/=0)then; err=20; message=trim(message)//'unable to allocate space for the baseflow derivatives'; return; end if + + + ! identify the matrix solution method + ! (the type of matrix used to solve the linear system A.X=B) + if(local_ixGroundwater==qbaseTopmodel .or. scalarSolution .or. forceFullMatrix)then + ixMatrix=ixFullMatrix ! named variable to denote the full Jacobian matrix + else + ixMatrix=ixBandMatrix ! named variable to denote the band-diagonal matrix + endif + + ! initialize the model fluxes (some model fluxes are not computed in the iterations) + do iVar=1,size(flux_temp%var) + flux_init%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) + end do - !do tol_iter=1,3 + ! ----- + ! * get scaling vectors... + ! ------------------------ + + ! initialize state vectors + call getScaling(& + ! input + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output + fScale, & ! intent(out): function scaling vector (mixed units) + xScale, & ! intent(out): variable scaling vector (mixed units) + sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) + dMat, & ! intent(out): diagonal of the Jacobian matrix (excludes fluxes) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + + ! initialize the trial state vectors + stateVecTrial = stateVecInit + + ! need to intialize canopy water at a positive value + if(ixVegHyd/=integerMissing)then + if(stateVecTrial(ixVegHyd) < xMinCanopyWater) stateVecTrial(ixVegHyd) = stateVecTrial(ixVegHyd) + xMinCanopyWater + endif + + ! try to accelerate solution for energy + if(ixCasNrg/=integerMissing) stateVecTrial(ixCasNrg) = stateVecInit(ixCasNrg) + (airtemp - stateVecInit(ixCasNrg))*tempAccelerate + if(ixVegNrg/=integerMissing) stateVecTrial(ixVegNrg) = stateVecInit(ixVegNrg) + (airtemp - stateVecInit(ixVegNrg))*tempAccelerate + + ! compute H_T at the beginning of the data step + call t2enthalpy_T(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTemp, & ! intent(in): value of canopy air temperature (K) + scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) + scalarCanopyWat, & ! intent(in): value of canopy total water (kg m-2) + scalarCanopyIce, & ! intent(in): value for canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTemp, & ! intent(in): vector of layer temperature (K) + mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) + mLayerMatricHead, & ! intent(in): vector of total water matric potential (m) + mLayerVolFracIce, & ! intent(in): vector of volumetric fraction of ice (-) + ! output: enthalpy + scalarCanairEnthalpy, & ! intent(out): temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! compute the flux and the residual vector for a given state vector + ! NOTE 1: The derivatives computed in eval8summa are used to calculate the Jacobian matrix for the first iteration + ! NOTE 2: The Jacobian matrix together with the residual vector is used to calculate the first iteration increment + + call eval8summa(& + ! input: model control + dt, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): number of layers + nState, & ! intent(in): number of state variables in the current subset + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVecTrial, & ! intent(in): model state vector + fScale, & ! intent(in): function scaling vector + sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): index data + ! input-output: data structures + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + ! output + feasible, & ! intent(out): flag to denote the feasibility of the solution + fluxVec0, & ! intent(out): flux vector + rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec, & ! intent(out): residual vector + fOld, & ! intent(out): function evaluation + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif + + ! copy over the initial flux structure since some model fluxes are not computed in the iterations + do concurrent ( iVar=1:size(flux_meta) ) + flux_temp%var(iVar)%dat(:) = flux_init%var(iVar)%dat(:) + end do + ! allocate space for the temporary flux_sum structure + call allocLocal(flux_meta(:),flux_sum,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! allocate space for mLayerCmpress_sum + allocate( mLayerCmpress_sum(nSoil) ) + + ! check the need to merge snow layers + if(nSnow>0)then + ! compute the energy required to melt the top snow layer (J m-2) + bulkDensity = mLayerVolFracIce(1)*iden_ice + mLayerVolFracLiq(1)*iden_water + volEnthalpy = temp2ethpy(mLayerTemp(1),bulkDensity,snowfrz_scale) + ! set flag and error codes for too much melt + if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt)then + tooMuchMelt=.true. + message=trim(message)//'net flux in the top snow layer can melt all the snow in the top layer' + err=-20; return ! negative error code to denote a warning + endif + endif + + ! get tolerance vectors + call popTol4IDA(& + ! input + nState, & ! intent(in): number of desired state variables + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + mpar_data, & ! intent(in): model parameters + ! output + atol, & ! intent(out): absolute tolerances vector (mixed units) + rtol, & ! intent(out): relative tolerances vector (mixed units) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + + !------------------- + ! * solving F(y,y') = 0 by IDA. Here, y is the state vector + ! ------------------ ! initialize flux_sum do concurrent ( iVar=1:size(flux_meta) ) flux_sum%var(iVar)%dat(:) = 0._rkind @@ -465,88 +461,77 @@ subroutine systemSolvSundials(& ! initialize sum of compression of the soil matrix mLayerCmpress_sum(:) = 0._rkind - call summaSolveSundialsIDA(& - dt, & ! intent (in) data time step - atol, & ! intent (in) absolute telerance - rtol, & ! intent (in) relative tolerance - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): number of snow+soil layers - nState, & ! intent(in): number of state variables in the current subset - ixMatrix, & ! intent(in): type of matrix (dense or banded) - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vector - stateVecTrial, & ! intent(in): model state vector at the beginning of the data time step - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - dMat, & ! intent(inout) diagonal of the Jacobian matrix (excludes fluxes) - ! input: data structures - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): index data - ! input-output: data structures - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) - flux_temp, & ! intent(inout): model fluxes for a local HRU - flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step - tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt - mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix - dt_out, & ! intent(out): time step - stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step - stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step - err,cmessage) ! intent(out): error control - - !if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - ! if (idaSucceeds)then - ! exit - ! else - ! atol = atol * 0.1 - ! rtol = rtol * 0.1 - ! endif - ! if( .not.idaSucceeds .and. tol_iter==3) message=trim(message)//'IDA did not succeed after reducing tolerance by 2 magnitudes' - - !end do ! iteration over tolerances - - ! check if IDA is successful - if( .not.idaSucceeds )then - err = 20 - message=trim(message)//trim(cmessage) -! reduceCoupledStep = .true. - return - else - if (tooMuchMelt) return !exit to start same step over after merge - endif - - ! compute average flux - do iVar=1,size(flux_meta) - flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / dt_out - end do - - diag_data%var(iLookDIAG%mLayerCompress)%dat(:) = mLayerCmpress_sum(:) - - ! compute the total change in storage associated with compression of the soil matrix (kg m-2) - diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sum(diag_data%var(iLookDIAG%mLayerCompress)%dat(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water - - ! save the computed solution - stateVecTrial = stateVecNew - - ! free memory - deallocate(mLayerCmpress_sum) - deallocate(dBaseflow_dMatric) - - ! end associate statements - end associate globalVars - - end subroutine systemSolvSundials + call summaSolveSundialsIDA(& + dt, & ! intent (in) data time step + atol, & ! intent (in) absolute telerance + rtol, & ! intent (in) relative tolerance + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): number of snow+soil layers + nState, & ! intent(in): number of state variables in the current subset + ixMatrix, & ! intent(in): type of matrix (dense or banded) + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vector + stateVecTrial, & ! intent(in): model state vector at the beginning of the data time step + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + dMat, & ! intent(inout) diagonal of the Jacobian matrix (excludes fluxes) + ! input: data structures + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): index data + ! input-output: data structures + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) + flux_temp, & ! intent(inout): model fluxes for a local HRU + flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step + tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt + mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix + dt_out, & ! intent(out): time step + stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step + stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step + err,cmessage) ! intent(out): error control + + ! check if IDA is successful + if( .not.idaSucceeds )then + err = 20 + message=trim(message)//trim(cmessage) + ! reduceCoupledStep = .true. + return + else + if (tooMuchMelt) return !exit to start same step over after merge + endif + + ! compute average flux + do iVar=1,size(flux_meta) + flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / dt_out + end do + + diag_data%var(iLookDIAG%mLayerCompress)%dat(:) = mLayerCmpress_sum(:) + + ! compute the total change in storage associated with compression of the soil matrix (kg m-2) + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sum(diag_data%var(iLookDIAG%mLayerCompress)%dat(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water + + ! save the computed solution + stateVecTrial = stateVecNew + + ! free memory + deallocate(mLayerCmpress_sum) + deallocate(dBaseflow_dMatric) + + ! end associate statements + end associate globalVars + +end subroutine systemSolvSundials end module systemSolvSundials_module From 6ca51c42ae2fa646a9d1bdf2cdbe6297c8e47a9b Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 27 Sep 2022 18:23:46 +0000 Subject: [PATCH 0358/1472] adjusted computEnthalpy --- build/source/engine/computEnthalpy.f90 | 201 ++++++++++++------------- 1 file changed, 100 insertions(+), 101 deletions(-) diff --git a/build/source/engine/computEnthalpy.f90 b/build/source/engine/computEnthalpy.f90 index ba216ad2e..3d736d6cc 100644 --- a/build/source/engine/computEnthalpy.f90 +++ b/build/source/engine/computEnthalpy.f90 @@ -1,4 +1,3 @@ - module computEnthalpy_module ! data types @@ -45,112 +44,112 @@ module computEnthalpy_module public::computEnthalpyPrime contains - ! ********************************************************************************************************** - ! public subroutine computEnthalpy - ! ********************************************************************************************************** - subroutine computEnthalpy(& - ! input - indx_data, & - nLayers, & - mLayerTemp, & - mLayerVolFracIce, & - mLayerHeatCap, & - ! output - mLayerEnthalpy & - ) - ! -------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input: model control - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - integer(i4b),intent(in) :: nLayers ! number of snow layers - real(rkind),intent(in) :: mLayerTemp(:) ! temperature of each snow/soil layer (K) - real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice (-) - real(rkind),intent(in) :: mLayerHeatCap(:) - real(rkind),intent(out) :: mLayerEnthalpy(:) - - ! local variables - integer(i4b) :: iLayer +! ********************************************************************************************************** +! public subroutine computEnthalpy +! ********************************************************************************************************** +subroutine computEnthalpy(& + ! input + indx_data, & + nLayers, & + mLayerTemp, & + mLayerVolFracIce, & + mLayerHeatCap, & + ! output + mLayerEnthalpy & + ) + ! -------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input: model control + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + integer(i4b),intent(in) :: nLayers ! number of snow layers + real(rkind),intent(in) :: mLayerTemp(:) ! temperature of each snow/soil layer (K) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice (-) + real(rkind),intent(in) :: mLayerHeatCap(:) + real(rkind),intent(out) :: mLayerEnthalpy(:) + + ! local variables + integer(i4b) :: iLayer - ! -------------------------------------------------------------------------------------------------------------------------------- - - associate(& - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of layer - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat & ! intent(in): [i4b(:)] indices for energy states - ) - ! (loop through non-missing energy state variables in the snow+soil domain) - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) - select case( layerType(iLayer) ) - case(iname_snow) - mLayerEnthalpy(iLayer) = mLayerHeatCap(iLayer)*mLayerTemp(iLayer) - LH_fus*iden_ice * mLayerVolFracIce(iLayer) - case(iname_soil) - mLayerEnthalpy(iLayer) = mLayerHeatCap(iLayer)*mLayerTemp(iLayer) - LH_fus*iden_water * mLayerVolFracIce(iLayer) - end select - end do ! looping through non-missing energy state variables in the snow+soil domain + ! -------------------------------------------------------------------------------------------------------------------------------- - end associate + associate(& + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of layer + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat & ! intent(in): [i4b(:)] indices for energy states + ) + ! (loop through non-missing energy state variables in the snow+soil domain) + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) + select case( layerType(iLayer) ) + case(iname_snow) + mLayerEnthalpy(iLayer) = mLayerHeatCap(iLayer)*mLayerTemp(iLayer) - LH_fus*iden_ice * mLayerVolFracIce(iLayer) + case(iname_soil) + mLayerEnthalpy(iLayer) = mLayerHeatCap(iLayer)*mLayerTemp(iLayer) - LH_fus*iden_water * mLayerVolFracIce(iLayer) + end select + end do ! looping through non-missing energy state variables in the snow+soil domain + + end associate - end subroutine computEnthalpy - - ! ********************************************************************************************************** - ! public subroutine computEnthalpyPrime - ! ********************************************************************************************************** - subroutine computEnthalpyPrime(& - ! input - computeVegFlux, & - indx_data, & - nLayers, & - canopyDepth, & ! intent(in): canopy depth (m) - scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K) - scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2) - mLayerTempPrime, & - mLayerVolFracIcePrime, & - heatCapVeg, & - mLayerHeatCap, & - ! output - scalarCanopyEnthalpyPrime, & - mLayerEnthalpyPrime & - ) - ! -------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - integer(i4b),intent(in) :: nLayers ! number of snow layers - real(rkind),intent(in) :: canopyDepth ! canopy depth (m) - real(rkind),intent(in) :: scalarCanopyTempPrime ! Prime value for the temperature of the vegetation canopy (K) - real(rkind),intent(in) :: scalarCanopyIcePrime ! Prime value for the ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: heatCapVeg - real(rkind),intent(in) :: mLayerTempPrime(:) ! temperature of each snow/soil layer (K) - real(rkind),intent(in) :: mLayerVolFracIcePrime(:) ! volumetric fraction of ice (-) - real(rkind),intent(in) :: mLayerHeatCap(:) - real(rkind),intent(out) :: scalarCanopyEnthalpyPrime - real(rkind),intent(out) :: mLayerEnthalpyPrime(:) +end subroutine computEnthalpy - ! local variables - integer(i4b) :: iLayer +! ********************************************************************************************************** +! public subroutine computEnthalpyPrime +! ********************************************************************************************************** +subroutine computEnthalpyPrime(& + ! input + computeVegFlux, & + indx_data, & + nLayers, & + canopyDepth, & ! intent(in): canopy depth (m) + scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K) + scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2) + mLayerTempPrime, & + mLayerVolFracIcePrime, & + heatCapVeg, & + mLayerHeatCap, & + ! output + scalarCanopyEnthalpyPrime, & + mLayerEnthalpyPrime & + ) + ! -------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input: model control + logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + integer(i4b),intent(in) :: nLayers ! number of snow layers + real(rkind),intent(in) :: canopyDepth ! canopy depth (m) + real(rkind),intent(in) :: scalarCanopyTempPrime ! Prime value for the temperature of the vegetation canopy (K) + real(rkind),intent(in) :: scalarCanopyIcePrime ! Prime value for the ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: heatCapVeg + real(rkind),intent(in) :: mLayerTempPrime(:) ! temperature of each snow/soil layer (K) + real(rkind),intent(in) :: mLayerVolFracIcePrime(:) ! volumetric fraction of ice (-) + real(rkind),intent(in) :: mLayerHeatCap(:) + real(rkind),intent(out) :: scalarCanopyEnthalpyPrime + real(rkind),intent(out) :: mLayerEnthalpyPrime(:) + + ! local variables + integer(i4b) :: iLayer - ! -------------------------------------------------------------------------------------------------------------------------------- - - associate(& - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of layer - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat & ! intent(in): [i4b(:)] indices for energy states - ) - - if(computeVegFlux)then - scalarCanopyEnthalpyPrime = heatCapVeg * scalarCanopyTempPrime - LH_fus*scalarCanopyIcePrime/canopyDepth - end if - ! (loop through non-missing energy state variables in the snow+soil domain) - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) - select case( layerType(iLayer) ) - case(iname_snow) - mLayerEnthalpyPrime(iLayer) = mLayerHeatCap(iLayer)*mLayerTempPrime(iLayer) - LH_fus*iden_ice * mLayerVolFracIcePrime(iLayer) - case(iname_soil) - mLayerEnthalpyPrime(iLayer) = mLayerHeatCap(iLayer)*mLayerTempPrime(iLayer) - LH_fus*iden_water * mLayerVolFracIcePrime(iLayer) - end select - end do ! looping through non-missing energy state variables in the snow+soil domain + ! -------------------------------------------------------------------------------------------------------------------------------- + + associate(& + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of layer + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat & ! intent(in): [i4b(:)] indices for energy states + ) - end associate + if(computeVegFlux)then + scalarCanopyEnthalpyPrime = heatCapVeg * scalarCanopyTempPrime - LH_fus*scalarCanopyIcePrime/canopyDepth + end if + ! (loop through non-missing energy state variables in the snow+soil domain) + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) + select case( layerType(iLayer) ) + case(iname_snow) + mLayerEnthalpyPrime(iLayer) = mLayerHeatCap(iLayer)*mLayerTempPrime(iLayer) - LH_fus*iden_ice * mLayerVolFracIcePrime(iLayer) + case(iname_soil) + mLayerEnthalpyPrime(iLayer) = mLayerHeatCap(iLayer)*mLayerTempPrime(iLayer) - LH_fus*iden_water * mLayerVolFracIcePrime(iLayer) + end select + end do ! looping through non-missing energy state variables in the snow+soil domain + + end associate - end subroutine computEnthalpyPrime +end subroutine computEnthalpyPrime end module computEnthalpy_module From 549c1aab589eb82e418215bbb9fa9eee9e8ab30b Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 27 Sep 2022 18:30:42 +0000 Subject: [PATCH 0359/1472] adjusted indentation --- build/source/engine/computHeatCap.f90 | 833 +++++++++++++------------- 1 file changed, 416 insertions(+), 417 deletions(-) diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index 615e1d350..f6d9f8ad5 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -74,437 +74,436 @@ module computHeatCap_module contains - ! ********************************************************************************************************** - ! public subroutine computHeatCap: compute diagnostic energy variables (heat capacity) - ! ********************************************************************************************************** - subroutine computHeatCap(& - ! input: control variables - nLayers, & ! intent(in): number of layers (soil+snow) - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) - ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - diag_data, & ! intent(in): model diagnostic variables for a local HRU - ! input: state variables - scalarCanopyIce, & ! intent(in) - scalarCanopyLiquid, & ! intent(in) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) - scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial temperature - mLayerTempPrev, & ! intent(in): previous temperature - mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil - mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil - ! output - heatCapVeg, & - mLayerHeatCap, & ! intent(out): heat capacity for snow and soil - ! output: error control - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------------- - ! input: control variables - logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) - ! input/output: data structures - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model layer indices - ! input: - integer(i4b),intent(in) :: nLayers - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) - real(rkind),intent(in) :: scalarCanopyLiquid - real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature - real(rkind),intent(in) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(in) :: scalarCanopyEnthalpyPrev ! intent(in): previous enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(in) :: scalarCanopyTempPrev ! Previous value of canopy temperature - real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) - real(rkind),intent(in) :: mLayerTempTrial(:) - real(rkind),intent(in) :: mLayerTempPrev(:) - real(rkind),intent(in) :: mLayerEnthalpyTrial(:) - real(rkind),intent(in) :: mLayerEnthalpyPrev(:) - ! output: - real(qp),intent(out) :: heatCapVeg - real(qp),intent(out) :: mLayerHeatCap(:) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - integer(i4b) :: iLayer ! index of model layer - real(rkind) :: delT - real(rkind) :: delEnt - integer(i4b) :: iSoil ! index of soil layer - ! -------------------------------------------------------------------------------------------------------------------------------- - ! associate variables in data structure - associate(& - ! input: coordinate variables - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) - ! input: heat capacity and thermal conductivity - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) - ! input: depth varying soil parameters - iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) - ) ! end associate statemen - ! initialize error control - err=0; message="computHeatCap/" - - ! initialize the soil layer - iSoil=integerMissing - - ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) - if(computeVegFlux)then - delT = scalarCanopyTempTrial - scalarCanopyTempPrev - if(abs(delT) <= 1e-14_rkind)then - heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component - Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component - Cp_ice*scalarCanopyIce/canopyDepth ! ice component - else - delEnt = scalarCanopyEnthalpyTrial - scalarCanopyEnthalpyPrev - heatCapVeg = delEnt / delT - end if - end if - - ! loop through layers - do iLayer=1,nLayers - delT = mLayerTempTrial(iLayer) - mLayerTempPrev(iLayer) - if(abs(delT) <= 1e-14_rkind)then - ! get the soil layer - if(iLayer>nSnow) iSoil = iLayer-nSnow - select case(layerType(iLayer)) +! ********************************************************************************************************** +! public subroutine computHeatCap: compute diagnostic energy variables (heat capacity) +! ********************************************************************************************************** +subroutine computHeatCap(& + ! input: control variables + nLayers, & ! intent(in): number of layers (soil+snow) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) + ! input data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + diag_data, & ! intent(in): model diagnostic variables for a local HRU + ! input: state variables + scalarCanopyIce, & ! intent(in) + scalarCanopyLiquid, & ! intent(in) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial temperature + mLayerTempPrev, & ! intent(in): previous temperature + mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil + mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil + ! output + heatCapVeg, & + mLayerHeatCap, & ! intent(out): heat capacity for snow and soil + ! output: error control + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! input: control variables + logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) + ! input/output: data structures + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model layer indices + ! input: + integer(i4b),intent(in) :: nLayers + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) + real(rkind),intent(in) :: scalarCanopyLiquid + real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature + real(rkind),intent(in) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(in) :: scalarCanopyEnthalpyPrev ! intent(in): previous enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(in) :: scalarCanopyTempPrev ! Previous value of canopy temperature + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) + real(rkind),intent(in) :: mLayerTempTrial(:) + real(rkind),intent(in) :: mLayerTempPrev(:) + real(rkind),intent(in) :: mLayerEnthalpyTrial(:) + real(rkind),intent(in) :: mLayerEnthalpyPrev(:) + ! output: + real(qp),intent(out) :: heatCapVeg + real(qp),intent(out) :: mLayerHeatCap(:) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + integer(i4b) :: iLayer ! index of model layer + real(rkind) :: delT + real(rkind) :: delEnt + integer(i4b) :: iSoil ! index of soil layer + ! -------------------------------------------------------------------------------------------------------------------------------- + ! associate variables in data structure + associate(& + ! input: coordinate variables + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers + layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) + ! input: heat capacity and thermal conductivity + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) + ! input: depth varying soil parameters + iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) + ) ! end associate statemen + ! initialize error control + err=0; message="computHeatCap/" + + ! initialize the soil layer + iSoil=integerMissing + + ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) + if(computeVegFlux)then + delT = scalarCanopyTempTrial - scalarCanopyTempPrev + if(abs(delT) <= 1e-14_rkind)then + heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component + Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component + Cp_ice*scalarCanopyIce/canopyDepth ! ice component + else + delEnt = scalarCanopyEnthalpyTrial - scalarCanopyEnthalpyPrev + heatCapVeg = delEnt / delT + end if + end if + + ! loop through layers + do iLayer=1,nLayers + delT = mLayerTempTrial(iLayer) - mLayerTempPrev(iLayer) + if(abs(delT) <= 1e-14_rkind)then + ! get the soil layer + if(iLayer>nSnow) iSoil = iLayer-nSnow + select case(layerType(iLayer)) ! * soil case(iname_soil) mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component - iden_ice * Cp_Ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component + iden_ice * Cp_Ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component case(iname_snow) mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return - end select - else - delEnt = mLayerEnthalpyTrial(iLayer) - mLayerEnthalpyPrev(iLayer) - mLayerHeatCap(iLayer) = delEnt / delT - endif - end do ! looping through layers - - end associate + end select + else + delEnt = mLayerEnthalpyTrial(iLayer) - mLayerEnthalpyPrev(iLayer) + mLayerHeatCap(iLayer) = delEnt / delT + endif + end do ! looping through layers + + end associate - end subroutine computHeatCap +end subroutine computHeatCap - ! ********************************************************************************************************** - ! public subroutine computStatMult: get scale factors - ! ********************************************************************************************************** - subroutine computStatMult(& - heatCapVeg, & - mLayerHeatCap, & - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output - sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - USE nr_utility_module,only:arth ! get a sequence of numbers arth(start, incr, count) - USE f2008funcs_module,only:findIndex ! finds the index of the first value within a vector - ! -------------------------------------------------------------------------------------------------------------------------------- - ! input: data structures - real(qp),intent(out) :: heatCapVeg - real(qp),intent(out) :: mLayerHeatCap(:) - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - ! output: state vectors - real(qp),intent(out) :: sMul(:) ! NOTE: qp ! multiplier for state vector (used in the residual calculations) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - ! -------------------------------------------------------------------------------------------------------------------------------- - ! state subsets - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - integer(i4b) :: ixStateSubset ! index within the state subset - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - associate(& - ! model diagnostic variables - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp] canopy depth (m) - volHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(in) : [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) - ! indices defining specific model states - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy hydrology state variable (mass) - ! vector of energy and hydrology indices for the snow and soil domains - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for energy state variables in the snow+soil domain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in) : [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in) : [i4b] number of hydrology state variables in the snow+soil domain - ! type of model state variabless - ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in) : [i4b(:)] [state subset] type of desired model state variables - ! number of layers - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in) : [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in) : [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in) : [i4b] total number of layers - ) ! end association with variables in the data structures - ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='computStatMult/' - - ! ----- - ! * define components of derivative matrices that are constant over a time step (substep)... - ! ------------------------------------------------------------------------------------------ - - ! define the multiplier for the state vector for residual calculations (vegetation canopy) - ! NOTE: Use the "where" statement to generalize to multiple canopy layers (currently one canopy layer) - - where(ixStateType_subset==iname_nrgCanair) sMul = Cp_air*iden_air ! volumetric heat capacity of air (J m-3 K-1) - where(ixStateType_subset==iname_nrgCanopy) sMul = heatCapVeg ! volumetric heat capacity of the vegetation (J m-3 K-1) - where(ixStateType_subset==iname_watCanopy) sMul = 1._rkind ! nothing else on the left hand side - where(ixStateType_subset==iname_liqCanopy) sMul = 1._rkind ! nothing else on the left hand side - - - ! define the energy multiplier for the state vector for residual calculations (snow-soil domain) - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - ixStateSubset = ixSnowSoilNrg(iLayer) ! index within the state vector - sMul(ixStateSubset) = mLayerHeatCap(iLayer) ! transfer volumetric heat capacity to the state multiplier - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! define the hydrology multiplier and diagonal elements for the state vector for residual calculations (snow-soil domain) - if(nSnowSoilHyd>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - ixStateSubset = ixSnowSoilHyd(iLayer) ! index within the state vector - sMul(ixStateSubset) = 1._rkind ! state multiplier = 1 (nothing else on the left-hand-side) - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! define the scaling factor and diagonal elements for the aquifer - where(ixStateType_subset==iname_watAquifer) sMul = 1._rkind - - ! ------------------------------------------------------------------------------------------ - ! ------------------------------------------------------------------------------------------ - - end associate - ! end association to variables in the data structure where vector length does not change - end subroutine computStatMult +! ********************************************************************************************************** +! public subroutine computStatMult: get scale factors +! ********************************************************************************************************** +subroutine computStatMult(& + heatCapVeg, & + mLayerHeatCap, & + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output + sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) + err,message) ! intent(out): error control +! -------------------------------------------------------------------------------------------------------------------------------- +USE nr_utility_module,only:arth ! get a sequence of numbers arth(start, incr, count) +USE f2008funcs_module,only:findIndex ! finds the index of the first value within a vector + ! -------------------------------------------------------------------------------------------------------------------------------- + ! input: data structures + real(qp),intent(out) :: heatCapVeg + real(qp),intent(out) :: mLayerHeatCap(:) + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + ! output: state vectors + real(qp),intent(out) :: sMul(:) ! NOTE: qp ! multiplier for state vector (used in the residual calculations) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + ! -------------------------------------------------------------------------------------------------------------------------------- + ! state subsets + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + integer(i4b) :: ixStateSubset ! index within the state subset + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + associate(& + ! model diagnostic variables + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp] canopy depth (m) + volHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(in) : [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) + ! indices defining specific model states + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy hydrology state variable (mass) + ! vector of energy and hydrology indices for the snow and soil domains + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in) : [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in) : [i4b] number of hydrology state variables in the snow+soil domain + ! type of model state variabless + ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in) : [i4b(:)] [state subset] type of desired model state variables + ! number of layers + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in) : [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in) : [i4b] number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in) : [i4b] total number of layers + ) ! end association with variables in the data structures + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='computStatMult/' + + ! ----- + ! * define components of derivative matrices that are constant over a time step (substep)... + ! ------------------------------------------------------------------------------------------ + + ! define the multiplier for the state vector for residual calculations (vegetation canopy) + ! NOTE: Use the "where" statement to generalize to multiple canopy layers (currently one canopy layer) + + where(ixStateType_subset==iname_nrgCanair) sMul = Cp_air*iden_air ! volumetric heat capacity of air (J m-3 K-1) + where(ixStateType_subset==iname_nrgCanopy) sMul = heatCapVeg ! volumetric heat capacity of the vegetation (J m-3 K-1) + where(ixStateType_subset==iname_watCanopy) sMul = 1._rkind ! nothing else on the left hand side + where(ixStateType_subset==iname_liqCanopy) sMul = 1._rkind ! nothing else on the left hand side + + + ! define the energy multiplier for the state vector for residual calculations (snow-soil domain) + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + ixStateSubset = ixSnowSoilNrg(iLayer) ! index within the state vector + sMul(ixStateSubset) = mLayerHeatCap(iLayer) ! transfer volumetric heat capacity to the state multiplier + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! define the hydrology multiplier and diagonal elements for the state vector for residual calculations (snow-soil domain) + if(nSnowSoilHyd>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + ixStateSubset = ixSnowSoilHyd(iLayer) ! index within the state vector + sMul(ixStateSubset) = 1._rkind ! state multiplier = 1 (nothing else on the left-hand-side) + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! define the scaling factor and diagonal elements for the aquifer + where(ixStateType_subset==iname_watAquifer) sMul = 1._rkind + + ! ------------------------------------------------------------------------------------------ + ! ------------------------------------------------------------------------------------------ + + end associate +! end association to variables in the data structure where vector length does not change +end subroutine computStatMult - ! ********************************************************************************************************** - ! public subroutine computHeatCapAnalytic: compute diagnostic energy variables (heat capacity) - ! ********************************************************************************************************** - subroutine computHeatCapAnalytic(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) - ! input: state variables - scalarCanopyIce, & ! intent(in) - scalarCanopyLiquid, & ! intent(in) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - ! output - heatCapVeg, & - mLayerHeatCap, & - ! output: error control - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------------- - ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) - real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) - real(rkind),intent(in) :: scalarCanopyLiquid - real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) - ! input/output: data structures - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model layer indices - ! output: error control - real(qp),intent(out) :: heatCapVeg - real(qp),intent(out) :: mLayerHeatCap(:) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: iSoil ! index of soil layer - ! -------------------------------------------------------------------------------------------------------------------------------- - ! associate variables in data structure - associate(& - ! input: coordinate variables - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers - layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) - ! input: heat capacity and thermal conductivity - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) - ! input: depth varying soil parameters - iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) - ) ! end associate statement - ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="computHeatCapAnalytic/" - - ! initialize the soil layer - iSoil=integerMissing - - ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) - if(computeVegFlux)then - heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component - Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component - Cp_ice*scalarCanopyIce/canopyDepth ! ice component - end if - - ! loop through layers - do iLayer=1,nLayers - - ! get the soil layer - if(iLayer>nSnow) iSoil = iLayer-nSnow - - ! ***** - ! * compute the volumetric heat capacity of each layer (J m-3 K-1)... - ! ******************************************************************* - select case(layerType(iLayer)) - ! * soil - case(iname_soil) - mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component - iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component - case(iname_snow) - mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return - end select - - end do ! looping through layers - !pause - - ! end association to variables in the data structure - end associate - - end subroutine computHeatCapAnalytic +! ********************************************************************************************************** +! public subroutine computHeatCapAnalytic: compute diagnostic energy variables (heat capacity) +! ********************************************************************************************************** +subroutine computHeatCapAnalytic(& + ! input: control variables + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) + ! input: state variables + scalarCanopyIce, & ! intent(in) + scalarCanopyLiquid, & ! intent(in) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + ! input data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + ! output + heatCapVeg, & + mLayerHeatCap, & + ! output: error control + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! input: model control + logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) + real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) + real(rkind),intent(in) :: scalarCanopyLiquid + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) + ! input/output: data structures + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model layer indices + ! output: error control + real(qp),intent(out) :: heatCapVeg + real(qp),intent(out) :: mLayerHeatCap(:) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: iSoil ! index of soil layer + ! -------------------------------------------------------------------------------------------------------------------------------- + ! associate variables in data structure + associate(& + ! input: coordinate variables + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers + layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) + ! input: heat capacity and thermal conductivity + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) + ! input: depth varying soil parameters + iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) + ) ! end associate statement + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="computHeatCapAnalytic/" + + ! initialize the soil layer + iSoil=integerMissing + + ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) + if(computeVegFlux)then + heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component + Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component + Cp_ice*scalarCanopyIce/canopyDepth ! ice component + end if + + ! loop through layers + do iLayer=1,nLayers + + ! get the soil layer + if(iLayer>nSnow) iSoil = iLayer-nSnow + + ! ***** + ! * compute the volumetric heat capacity of each layer (J m-3 K-1)... + ! ******************************************************************* + select case(layerType(iLayer)) + ! * soil + case(iname_soil) + mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component + iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component + case(iname_snow) + mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return + end select + + end do ! looping through layers + !pause + + ! end association to variables in the data structure + end associate +end subroutine computHeatCapAnalytic - ! ********************************************************************************************************** - ! public subroutine computCm - ! ********************************************************************************************************** - subroutine computCm(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - ! input: state variables - scalarCanopyTemp, & ! intent(in) - mLayerTemp, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - mLayerMatricHead, & ! intent(in) - ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - ! output - scalarCanopyCm, & ! intent(out): Cm for vegetation - mLayerCm, & ! intent(out): Cm for soil and snow - ! output: error control - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------------- - ! provide access to external subroutines - USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists - ! -------------------------------------------------------------------------------------------------------------------------------------- - ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - real(rkind),intent(in) :: scalarCanopyTemp ! value of canopy ice content (kg m-2) - real(rkind),intent(in) :: mLayerTemp(:) ! vector of volumetric liquid water content (-) - real(rkind),intent(in) :: mLayerMatricHead(:) ! vector of total water matric potential (m) - ! input/output: data structures - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model layer indices - ! output: error control - real(qp),intent(out) :: scalarCanopyCm - real(qp),intent(out) :: mLayerCm(:) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: iSoil ! index of soil layer - real(rkind) :: g1 - real(rkind) :: g2 - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) +! ********************************************************************************************************** +! public subroutine computCm +! ********************************************************************************************************** +subroutine computCm(& + ! input: control variables + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + ! input: state variables + scalarCanopyTemp, & ! intent(in) + mLayerTemp, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerMatricHead, & ! intent(in) + ! input data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + ! output + scalarCanopyCm, & ! intent(out): Cm for vegetation + mLayerCm, & ! intent(out): Cm for soil and snow + ! output: error control + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! provide access to external subroutines + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! input: model control + logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + real(rkind),intent(in) :: scalarCanopyTemp ! value of canopy ice content (kg m-2) + real(rkind),intent(in) :: mLayerTemp(:) ! vector of volumetric liquid water content (-) + real(rkind),intent(in) :: mLayerMatricHead(:) ! vector of total water matric potential (m) + ! input/output: data structures + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model layer indices + ! output: error control + real(qp),intent(out) :: scalarCanopyCm + real(qp),intent(out) :: mLayerCm(:) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: iSoil ! index of soil layer + real(rkind) :: g1 + real(rkind) :: g2 + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - ! -------------------------------------------------------------------------------------------------------------------------------- - ! associate variables in data structure - associate(& - ! input: coordinate variables - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers - layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) -snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) ! end associate statement - ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="computCm/" - - ! initialize the soil layer - iSoil=integerMissing - - ! compute Cm of vegetation - ! Note that scalarCanopyCm/iden_water is computed - if(computeVegFlux)then - g2 = scalarCanopyTemp - Tfreeze - g1 = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * g2) - if(scalarCanopyTemp < Tfreeze)then - scalarCanopyCm = Cp_water * g1 + Cp_ice * (g2 - g1) - else - scalarCanopyCm = Cp_water * g2 - end if - end if - - ! loop through layers - do iLayer=1,nLayers - - ! get the soil layer - if(iLayer>nSnow) iSoil = iLayer-nSnow - - ! ***** - ! * compute Cm of of each layer - ! ******************************************************************* - select case(layerType(iLayer)) - ! * soil - case(iname_soil) - g2 = mLayerTemp(iLayer) - Tfreeze - Tcrit = crit_soilT( mLayerMatricHead(iSoil) ) - if( mLayerTemp(iLayer) < Tcrit)then - mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air) * g2 - else - mLayerCm(iLayer) = (iden_water * Cp_water - iden_air * Cp_air) * g2 + ! -------------------------------------------------------------------------------------------------------------------------------- + ! associate variables in data structure + associate(& + ! input: coordinate variables + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers + layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) ! end associate statement + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="computCm/" + + ! initialize the soil layer + iSoil=integerMissing + + ! compute Cm of vegetation + ! Note that scalarCanopyCm/iden_water is computed + if(computeVegFlux)then + g2 = scalarCanopyTemp - Tfreeze + g1 = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * g2) + if(scalarCanopyTemp < Tfreeze)then + scalarCanopyCm = Cp_water * g1 + Cp_ice * (g2 - g1) + else + scalarCanopyCm = Cp_water * g2 + end if end if - - case(iname_snow) - g2 = mLayerTemp(iLayer) - Tfreeze - g1 = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * g2) - mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( g2 - g1 ) & - + (iden_water * Cp_water - iden_air * Cp_air) * g1 - - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute Cm'; return - end select - - end do ! looping through layers - !pause - - ! end association to variables in the data structure - end associate - - end subroutine computCm + + ! loop through layers + do iLayer=1,nLayers + + ! get the soil layer + if(iLayer>nSnow) iSoil = iLayer-nSnow + + ! ***** + ! * compute Cm of of each layer + ! ******************************************************************* + select case(layerType(iLayer)) + ! * soil + case(iname_soil) + g2 = mLayerTemp(iLayer) - Tfreeze + Tcrit = crit_soilT( mLayerMatricHead(iSoil) ) + if( mLayerTemp(iLayer) < Tcrit)then + mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air) * g2 + else + mLayerCm(iLayer) = (iden_water * Cp_water - iden_air * Cp_air) * g2 + end if + + case(iname_snow) + g2 = mLayerTemp(iLayer) - Tfreeze + g1 = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * g2) + mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( g2 - g1 ) & + + (iden_water * Cp_water - iden_air * Cp_air) * g1 + + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute Cm'; return + end select + + end do ! looping through layers + !pause + + ! end association to variables in the data structure + end associate + +end subroutine computCm end module computHeatCap_module From 797a37fce17cacc29711a87de0346ed554cac800 Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 27 Sep 2022 18:34:30 +0000 Subject: [PATCH 0360/1472] Indentation Changes --- build/source/engine/computResidSundials.f90 | 554 ++++++++++---------- 1 file changed, 275 insertions(+), 279 deletions(-) diff --git a/build/source/engine/computResidSundials.f90 b/build/source/engine/computResidSundials.f90 index 61b9d24ff..ac620aa8f 100644 --- a/build/source/engine/computResidSundials.f90 +++ b/build/source/engine/computResidSundials.f90 @@ -53,293 +53,289 @@ module computResidSundials_module public::computResidSundials contains - ! ********************************************************************************************************** - ! public subroutine computResidSundials: compute the residual vector - ! ********************************************************************************************************** - subroutine computResidSundials(& +! ********************************************************************************************************** +! public subroutine computResidSundials: compute the residual vector +! ********************************************************************************************************** +subroutine computResidSundials(& + ! input: model control + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + ! input: flux vectors + sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + fVec, & ! intent(in): flux vector + ! input: state variables (already disaggregated into scalars and vectors) + scalarCanopyTempTrial, & ! intent(in): + mLayerTempTrial, & ! intent(in) + scalarCanairTempPrime, & ! intent(in): trial value for the temperature of the canopy air space (K) + scalarCanopyTempPrime, & ! intent(in): trial value for the temperature of the vegetation canopy (K) + scalarCanopyWatPrime, & ! + mLayerTempPrime, & ! intent(in): trial value for the temperature of each snow and soil layer (K) water content + scalarAquiferStoragePrime, & ! intent(in): trial value of storage of water in the aquifer (m) + ! input: diagnostic variables defining the liquid water and ice content (function of state variables) + scalarCanopyIcePrime, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) + scalarCanopyLiqPrime, & ! intent(in): + mLayerVolFracIcePrime, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) + mLayerVolFracWatPrime, & + mLayerVolFracLiqPrime, & + scalarCanopyCmTrial, & + mLayerCmTrial, & ! intent(in) + ! input: data structures + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + flux_data, & ! intent(in): model fluxes for a local HRU + indx_data, & ! intent(in): index data + ! output + rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec, & ! intent(out): residual vector + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input: model control + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain + ! input: flux vectors + real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + real(rkind),intent(in) :: fVec(:) ! flux vector + ! input: state variables (already disaggregated into scalars and vectors) + real(rkind),intent(in) :: scalarCanopyTempTrial + real(rkind),intent(in) :: mLayerTempTrial(:) + real(rkind),intent(in) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) + real(rkind),intent(in) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) + real(rkind),intent(in) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) + real(rkind),intent(in) :: mLayerTempPrime(:) ! trial value for temperature of each snow/soil layer (K) content + real(rkind),intent(in) :: scalarAquiferStoragePrime ! trial value of aquifer storage (m) + ! input: diagnostic variables defining the liquid water and ice content (function of state variables) + real(rkind),intent(in) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: scalarCanopyLiqPrime + real(rkind),intent(in) :: mLayerVolFracIcePrime(:) ! trial value for volumetric fraction of ice (-) + real(rkind),intent(in) :: mLayerVolFracLiqPrime(:) + real(rkind),intent(in) :: mLayerVolFracWatPrime(:) + real(qp),intent(in) :: scalarCanopyCmTrial + real(qp),intent(in) :: mLayerCmTrial(:) + ! input: data structures + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + ! output + real(rkind),intent(out) :: rAdd(:) ! additional (sink) terms on the RHS of the state equation + real(qp),intent(out) :: rVec(:) ! NOTE: qp ! residual vector + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + ! -------------------------------------------------------------------------------------------------------------------------------- + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) + real(rkind),dimension(nLayers) :: mLayerVolFracHydPrime ! vector of volumetric water content (-), either liquid water content or total water content + real(rkind) :: scalarCanopyHydPrime ! trial value for canopy water (kg m-2), either liquid water content or total water content + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + ! link to the necessary variables for the residual computations + associate(& + ! canopy and layer depth + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! model fluxes (sink terms in the soil domain) + mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(in): [dp] transpiration loss from each soil layer (m s-1) + mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(in): [dp(:)] baseflow from each soil layer (m s-1) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in storage associated with compression of the soil matrix (-) + ! number of state variables of a specific type + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + ! model indices + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the soil subdomain + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of hydrology states in snow+soil domain + layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] named variables defining the type of layer in snow+soil domain + ) ! association to necessary variables for the residual computations + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="computResidSundials/" + + ! --- + ! * compute sink terms... + ! ----------------------- + + ! intialize additional terms on the RHS as zero + rAdd(:) = 0._rkind + + ! compute energy associated with melt freeze for the vegetation canopy + if(ixVegNrg/=integerMissing) rAdd(ixVegNrg) = rAdd(ixVegNrg) + LH_fus*scalarCanopyIcePrime/canopyDepth ! energy associated with melt/freeze (J m-3) + ! compute energy associated with melt/freeze for snow + ! NOTE: allow expansion of ice during melt-freeze for snow; deny expansion of ice during melt-freeze for soil + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + select case( layerType(iLayer) ) + case(iname_snow); rAdd( ixSnowSoilNrg(iLayer) ) = rAdd( ixSnowSoilNrg(iLayer) ) + LH_fus*iden_ice * mLayerVolFracIcePrime(iLayer) + case(iname_soil); rAdd( ixSnowSoilNrg(iLayer) ) = rAdd( ixSnowSoilNrg(iLayer) ) + LH_fus*iden_water * mLayerVolFracIcePrime(iLayer) + end select + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! sink terms soil hydrology (-) + ! NOTE 1: state variable is volumetric water content, so melt-freeze is not included + ! NOTE 2: ground evaporation was already included in the flux at the upper boundary + ! NOTE 3: rAdd(ixSnowOnlyWat)=0, and is defined in the initialization above + ! NOTE 4: same sink terms for matric head and liquid matric potential + if(nSoilOnlyHyd>0)then + do concurrent (iLayer=1:nSoil,ixSoilOnlyHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) + rAdd( ixSoilOnlyHyd(iLayer) ) = rAdd( ixSoilOnlyHyd(iLayer) ) + (mLayerTranspire(iLayer) - mLayerBaseflow(iLayer) )/mLayerDepth(iLayer+nSnow) - mLayerCompress(iLayer) + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! --- + ! * compute the residual vector... + ! -------------------------------- + + ! compute the residual vector for the vegetation canopy + ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision + ! --> energy balance + if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*scalarCanairTempPrime - ( fVec(ixCasNrg) + rAdd(ixCasNrg) ) + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime + scalarCanopyCmTrial * scalarCanopyWatPrime/canopyDepth - ( fVec(ixVegNrg) + rAdd(ixVegNrg) ) + ! --> mass balance + if(ixVegHyd/=integerMissing)then + scalarCanopyHydPrime = merge(scalarCanopyWatPrime, scalarCanopyLiqPrime, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) + rVec(ixVegHyd) = sMul(ixVegHyd)*scalarCanopyHydPrime - ( fVec(ixVegHyd) + rAdd(ixVegHyd) ) + endif + + ! compute the residual vector for the snow and soil sub-domains for energy + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) ) * mLayerTempPrime(iLayer) + mLayerCmTrial(iLayer) * mLayerVolFracWatPrime(iLayer) - ( fVec( ixSnowSoilNrg(iLayer) ) + rAdd( ixSnowSoilNrg(iLayer) ) ) + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! compute the residual vector for the snow and soil sub-domains for hydrology + ! NOTE: residual depends on choice of state variable + if(nSnowSoilHyd>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) + ! (get the correct state variable) + mLayerVolFracHydPrime(iLayer) = merge(mLayerVolFracWatPrime(iLayer), mLayerVolFracLiqPrime(iLayer), (ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_matLayer) ) + ! (compute the residual) + rVec( ixSnowSoilHyd(iLayer) ) = mLayerVolFracHydPrime(iLayer) - ( fVec( ixSnowSoilHyd(iLayer) ) + rAdd( ixSnowSoilHyd(iLayer) ) ) + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! compute the residual vector for the aquifer + if(ixAqWat/=integerMissing) rVec(ixAqWat) = sMul(ixAqWat)*scalarAquiferStoragePrime - ( fVec(ixAqWat) + rAdd(ixAqWat) ) + + ! print result + if(globalPrintFlag)then + write(*,'(a,1x,100(e12.5,1x))') 'rVec = ', rVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) + write(*,'(a,1x,100(e12.5,1x))') 'fVec = ', fVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) + endif + + ! check + if(any(isNan(rVec)))then + call printResidDAE(nSnow,nSoil,nLayers,indx_data,rAdd,rVec) + message=trim(message)//'we found NaN' + err=20; return + endif + + ! end association with the necessary variabiles for the residual calculations + end associate + +end subroutine computResidSundials + +! ********************************************************************************************************** +! private subroutine printResidDAE: print the residual vector mainly for debugging +! ********************************************************************************************************** +subroutine printResidDAE( & ! input: model control nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers - ! input: flux vectors - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) - fVec, & ! intent(in): flux vector - ! input: state variables (already disaggregated into scalars and vectors) - scalarCanopyTempTrial, & ! intent(in): - mLayerTempTrial, & ! intent(in) - scalarCanairTempPrime, & ! intent(in): trial value for the temperature of the canopy air space (K) - scalarCanopyTempPrime, & ! intent(in): trial value for the temperature of the vegetation canopy (K) - scalarCanopyWatPrime, & ! - mLayerTempPrime, & ! intent(in): trial value for the temperature of each snow and soil layer (K) water content - scalarAquiferStoragePrime, & ! intent(in): trial value of storage of water in the aquifer (m) - ! input: diagnostic variables defining the liquid water and ice content (function of state variables) - scalarCanopyIcePrime, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) - scalarCanopyLiqPrime, & ! intent(in): - mLayerVolFracIcePrime, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) - mLayerVolFracWatPrime, & - mLayerVolFracLiqPrime, & - scalarCanopyCmTrial, & - mLayerCmTrial, & ! intent(in) ! input: data structures - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - flux_data, & ! intent(in): model fluxes for a local HRU indx_data, & ! intent(in): index data ! output rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input: model control - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain - ! input: flux vectors - real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) - real(rkind),intent(in) :: fVec(:) ! flux vector - ! input: state variables (already disaggregated into scalars and vectors) - real(rkind),intent(in) :: scalarCanopyTempTrial - real(rkind),intent(in) :: mLayerTempTrial(:) - real(rkind),intent(in) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) - real(rkind),intent(in) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) - real(rkind),intent(in) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) - real(rkind),intent(in) :: mLayerTempPrime(:) ! trial value for temperature of each snow/soil layer (K) content - real(rkind),intent(in) :: scalarAquiferStoragePrime ! trial value of aquifer storage (m) - ! input: diagnostic variables defining the liquid water and ice content (function of state variables) - real(rkind),intent(in) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: scalarCanopyLiqPrime - real(rkind),intent(in) :: mLayerVolFracIcePrime(:) ! trial value for volumetric fraction of ice (-) - real(rkind),intent(in) :: mLayerVolFracLiqPrime(:) - real(rkind),intent(in) :: mLayerVolFracWatPrime(:) - real(qp),intent(in) :: scalarCanopyCmTrial - real(qp),intent(in) :: mLayerCmTrial(:) - ! input: data structures - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - ! output - real(rkind),intent(out) :: rAdd(:) ! additional (sink) terms on the RHS of the state equation - real(qp),intent(out) :: rVec(:) ! NOTE: qp ! residual vector - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - ! -------------------------------------------------------------------------------------------------------------------------------- - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) - real(rkind),dimension(nLayers) :: mLayerVolFracHydPrime ! vector of volumetric water content (-), either liquid water content or total water content - real(rkind) :: scalarCanopyHydPrime ! trial value for canopy water (kg m-2), either liquid water content or total water content - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - ! link to the necessary variables for the residual computations - associate(& - ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! model fluxes (sink terms in the soil domain) - mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(in): [dp] transpiration loss from each soil layer (m s-1) - mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(in): [dp(:)] baseflow from each soil layer (m s-1) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in storage associated with compression of the soil matrix (-) - ! number of state variables of a specific type - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain - ! model indices - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain - ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the soil subdomain - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of hydrology states in snow+soil domain - layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] named variables defining the type of layer in snow+soil domain - ) ! association to necessary variables for the residual computations - ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="computResidSundials/" - - ! --- - ! * compute sink terms... - ! ----------------------- - - ! intialize additional terms on the RHS as zero - rAdd(:) = 0._rkind - - ! compute energy associated with melt freeze for the vegetation canopy - if(ixVegNrg/=integerMissing) rAdd(ixVegNrg) = rAdd(ixVegNrg) + LH_fus*scalarCanopyIcePrime/canopyDepth ! energy associated with melt/freeze (J m-3) - ! compute energy associated with melt/freeze for snow - ! NOTE: allow expansion of ice during melt-freeze for snow; deny expansion of ice during melt-freeze for soil - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - select case( layerType(iLayer) ) - case(iname_snow); rAdd( ixSnowSoilNrg(iLayer) ) = rAdd( ixSnowSoilNrg(iLayer) ) + LH_fus*iden_ice * mLayerVolFracIcePrime(iLayer) - case(iname_soil); rAdd( ixSnowSoilNrg(iLayer) ) = rAdd( ixSnowSoilNrg(iLayer) ) + LH_fus*iden_water * mLayerVolFracIcePrime(iLayer) - end select - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! sink terms soil hydrology (-) - ! NOTE 1: state variable is volumetric water content, so melt-freeze is not included - ! NOTE 2: ground evaporation was already included in the flux at the upper boundary - ! NOTE 3: rAdd(ixSnowOnlyWat)=0, and is defined in the initialization above - ! NOTE 4: same sink terms for matric head and liquid matric potential - if(nSoilOnlyHyd>0)then - do concurrent (iLayer=1:nSoil,ixSoilOnlyHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) - rAdd( ixSoilOnlyHyd(iLayer) ) = rAdd( ixSoilOnlyHyd(iLayer) ) + (mLayerTranspire(iLayer) - mLayerBaseflow(iLayer) )/mLayerDepth(iLayer+nSnow) - mLayerCompress(iLayer) - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! --- - ! * compute the residual vector... - ! -------------------------------- - - ! compute the residual vector for the vegetation canopy - ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision - ! --> energy balance - if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*scalarCanairTempPrime - ( fVec(ixCasNrg) + rAdd(ixCasNrg) ) - if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime + scalarCanopyCmTrial * scalarCanopyWatPrime/canopyDepth - ( fVec(ixVegNrg) + rAdd(ixVegNrg) ) - ! --> mass balance - if(ixVegHyd/=integerMissing)then - scalarCanopyHydPrime = merge(scalarCanopyWatPrime, scalarCanopyLiqPrime, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) - rVec(ixVegHyd) = sMul(ixVegHyd)*scalarCanopyHydPrime - ( fVec(ixVegHyd) + rAdd(ixVegHyd) ) - endif - - ! compute the residual vector for the snow and soil sub-domains for energy - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) ) * mLayerTempPrime(iLayer) + mLayerCmTrial(iLayer) * mLayerVolFracWatPrime(iLayer) - ( fVec( ixSnowSoilNrg(iLayer) ) + rAdd( ixSnowSoilNrg(iLayer) ) ) - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! compute the residual vector for the snow and soil sub-domains for hydrology - ! NOTE: residual depends on choice of state variable - if(nSnowSoilHyd>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) - ! (get the correct state variable) - mLayerVolFracHydPrime(iLayer) = merge(mLayerVolFracWatPrime(iLayer), mLayerVolFracLiqPrime(iLayer), (ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_matLayer) ) - ! (compute the residual) - rVec( ixSnowSoilHyd(iLayer) ) = mLayerVolFracHydPrime(iLayer) - ( fVec( ixSnowSoilHyd(iLayer) ) + rAdd( ixSnowSoilHyd(iLayer) ) ) - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! compute the residual vector for the aquifer - if(ixAqWat/=integerMissing) rVec(ixAqWat) = sMul(ixAqWat)*scalarAquiferStoragePrime - ( fVec(ixAqWat) + rAdd(ixAqWat) ) - - ! print result - if(globalPrintFlag)then - write(*,'(a,1x,100(e12.5,1x))') 'rVec = ', rVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) - write(*,'(a,1x,100(e12.5,1x))') 'fVec = ', fVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) - !print*, 'PAUSE:'; read(*,*) - endif - - !call printResidDAE(nSnow,nSoil,nLayers,indx_data,rAdd,rVec) - - ! check - if(any(isNan(rVec)))then - call printResidDAE(nSnow,nSoil,nLayers,indx_data,rAdd,rVec) - message=trim(message)//'we found NaN' - err=20; return - endif - - ! end association with the necessary variabiles for the residual calculations - end associate - - end subroutine computResidSundials - - ! ********************************************************************************************************** - ! private subroutine printResidDAE: print the residual vector mainly for debugging - ! ********************************************************************************************************** - subroutine printResidDAE( & - ! input: model control - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - ! input: data structures - indx_data, & ! intent(in): index data - ! output - rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec) ! intent(out): residual vector - -! -------------------------------------------------------------------------------------------------------------------------------- -implicit none -! input: model control -integer(i4b),intent(in) :: nSnow ! number of snow layers -integer(i4b),intent(in) :: nSoil ! number of soil layers -integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain -type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers -! output -real(rkind),intent(in) :: rAdd(:) ! additional (sink) terms on the RHS of the state equation -real(qp),intent(in) :: rVec(:) ! NOTE: qp ! residual vector -! -------------------------------------------------------------------------------------------------------------------------------- -! local variables -! -------------------------------------------------------------------------------------------------------------------------------- -integer(i4b) :: iLayer ! index of layer within the snow+soil domain -! -------------------------------------------------------------------------------------------------------------------------------- -! link to the necessary variables for the residual computations -associate(& -! number of state variables of a specific type -nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain -nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain -nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain -! model indices -ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable -ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable -ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) -ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer -ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain -ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain -ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the soil subdomain -ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) -ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain -ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of hydrology states in snow+soil domain -layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] named variables defining the type of layer in snow+soil domain -) ! association to necessary variables for the residual computations -! -------------------------------------------------------------------------------------------------------------------------------- - -if(ixVegNrg/=integerMissing) print *, 'rAdd(ixVegNrg) = ', rAdd(ixVegNrg) - -if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) - select case( layerType(iLayer) ) - case(iname_snow) - print *, 'rAdd( ixSnowSoilNrg(iLayer) ) = ', rAdd( ixSnowSoilNrg(iLayer) ) - case(iname_soil); print *, 'rAdd( ixSnowSoilNrg(iLayer) ) = ', rAdd( ixSnowSoilNrg(iLayer) ) - end select - end do -endif - -if(nSoilOnlyHyd>0)then - do concurrent (iLayer=1:nSoil,ixSoilOnlyHyd(iLayer)/=integerMissing) - print *, 'rAdd( ixSoilOnlyHyd(iLayer) ) = ', rAdd( ixSoilOnlyHyd(iLayer) ) - end do -endif - -if(ixCasNrg/=integerMissing) print *, 'rVec(ixCasNrg) = ', rVec(ixCasNrg) -if(ixVegNrg/=integerMissing) print *, 'rVec(ixVegNrg) = ', rVec(ixVegNrg) -if(ixVegHyd/=integerMissing)then - print *, 'rVec(ixVegHyd) = ', rVec(ixVegHyd) -endif - -if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) - print *, 'rVec( ixSnowSoilNrg(iLayer) ) = ', rVec( ixSnowSoilNrg(iLayer) ) - end do -endif - -if(nSnowSoilHyd>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) - print *, 'rVec( ixSnowSoilHyd(iLayer) ) = ', rVec( ixSnowSoilHyd(iLayer) ) - end do -endif - -if(ixAqWat/=integerMissing) print *, ' rVec(ixAqWat) = ', rVec(ixAqWat) - -end associate + rVec) ! intent(out): residual vector + + ! -------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input: model control + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + ! output + real(rkind),intent(in) :: rAdd(:) ! additional (sink) terms on the RHS of the state equation + real(qp),intent(in) :: rVec(:) ! NOTE: qp ! residual vector + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + ! -------------------------------------------------------------------------------------------------------------------------------- + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + ! -------------------------------------------------------------------------------------------------------------------------------- + ! link to the necessary variables for the residual computations + associate(& + ! number of state variables of a specific type + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + ! model indices + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the soil subdomain + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of hydrology states in snow+soil domain + layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] named variables defining the type of layer in snow+soil domain + ) ! association to necessary variables for the residual computations + ! -------------------------------------------------------------------------------------------------------------------------------- + + if(ixVegNrg/=integerMissing) print *, 'rAdd(ixVegNrg) = ', rAdd(ixVegNrg) + + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) + select case( layerType(iLayer) ) + case(iname_snow); print *, 'rAdd( ixSnowSoilNrg(iLayer) ) = ', rAdd( ixSnowSoilNrg(iLayer) ) + case(iname_soil); print *, 'rAdd( ixSnowSoilNrg(iLayer) ) = ', rAdd( ixSnowSoilNrg(iLayer) ) + end select + end do + endif + + if(nSoilOnlyHyd>0)then + do concurrent (iLayer=1:nSoil,ixSoilOnlyHyd(iLayer)/=integerMissing) + print *, 'rAdd( ixSoilOnlyHyd(iLayer) ) = ', rAdd( ixSoilOnlyHyd(iLayer) ) + end do + endif + + if(ixCasNrg/=integerMissing) print *, 'rVec(ixCasNrg) = ', rVec(ixCasNrg) + if(ixVegNrg/=integerMissing) print *, 'rVec(ixVegNrg) = ', rVec(ixVegNrg) + if(ixVegHyd/=integerMissing)then + print *, 'rVec(ixVegHyd) = ', rVec(ixVegHyd) + endif + + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) + print *, 'rVec( ixSnowSoilNrg(iLayer) ) = ', rVec( ixSnowSoilNrg(iLayer) ) + end do + endif + + if(nSnowSoilHyd>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) + print *, 'rVec( ixSnowSoilHyd(iLayer) ) = ', rVec( ixSnowSoilHyd(iLayer) ) + end do + endif + + if(ixAqWat/=integerMissing) print *, ' rVec(ixAqWat) = ', rVec(ixAqWat) + + end associate end subroutine printResidDAE From b65baf04c0e58d41534025970dc17067d0107d33 Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 27 Sep 2022 18:36:39 +0000 Subject: [PATCH 0361/1472] indentation changes --- build/source/engine/computSnowDepth.f90 | 159 ++++++++++++------------ 1 file changed, 79 insertions(+), 80 deletions(-) diff --git a/build/source/engine/computSnowDepth.f90 b/build/source/engine/computSnowDepth.f90 index 9332798a6..552020eb0 100644 --- a/build/source/engine/computSnowDepth.f90 +++ b/build/source/engine/computSnowDepth.f90 @@ -39,45 +39,45 @@ module computSnowDepth_module contains - ! ************************************************************************************************ - ! public subroutine computSnowDepth: compute snow depth for one sub timestep - ! ************************************************************************************************ - subroutine computSnowDepth(& - dt_sub, & - nSnow, & ! intent(in) - scalarSnowSublimation, & ! intent(in) - mLayerVolFracLiq, & ! intent(inout) - mLayerVolFracIce, & ! intent(inout) - mLayerTemp, & ! intent(in) - mLayerMeltFreeze, & ! intent(in) - mpar_data, & ! intent(in) - ! output - tooMuchSublim, & ! intent(out): flag to denote that there was too much sublimation in a given time step - mLayerDepth, & ! intent(inout) - ! error control - err,message) ! intent(out): error control - - USE snwDensify_module,only:snwDensify ! snow densification (compaction and cavitation) - - implicit none - real(qp),intent(in) :: dt_sub - integer(i4b),intent(in) :: nSnow ! number of snow layers - real(rkind),intent(in) :: scalarSnowSublimation - real(rkind),intent(inout) :: mLayerVolFracLiq(:) - real(rkind),intent(inout) :: mLayerVolFracIce(:) - real(rkind),intent(in) :: mLayerTemp(:) - real(rkind),intent(in) :: mLayerMeltFreeze(:) - type(var_dlength),intent(in) :: mpar_data ! model parameters - logical(lgt) :: tooMuchSublim ! flag to denote that there was too much sublimation in a given time step - real(rkind),intent(inout) :: mLayerDepth(:) - - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - - ! local variables - character(len=256) :: cmessage ! error message - integer(i4b) :: iSnow ! index of snow layers - real(rkind) :: massLiquid ! mass liquid water (kg m-2) +! ************************************************************************************************ +! public subroutine computSnowDepth: compute snow depth for one sub timestep +! ************************************************************************************************ +subroutine computSnowDepth(& + dt_sub, & + nSnow, & ! intent(in) + scalarSnowSublimation, & ! intent(in) + mLayerVolFracLiq, & ! intent(inout) + mLayerVolFracIce, & ! intent(inout) + mLayerTemp, & ! intent(in) + mLayerMeltFreeze, & ! intent(in) + mpar_data, & ! intent(in) + ! output + tooMuchSublim, & ! intent(out): flag to denote that there was too much sublimation in a given time step + mLayerDepth, & ! intent(inout) + ! error control + err,message) ! intent(out): error control + + USE snwDensify_module,only:snwDensify ! snow densification (compaction and cavitation) + + implicit none + real(qp),intent(in) :: dt_sub + integer(i4b),intent(in) :: nSnow ! number of snow layers + real(rkind),intent(in) :: scalarSnowSublimation + real(rkind),intent(inout) :: mLayerVolFracLiq(:) + real(rkind),intent(inout) :: mLayerVolFracIce(:) + real(rkind),intent(in) :: mLayerTemp(:) + real(rkind),intent(in) :: mLayerMeltFreeze(:) + type(var_dlength),intent(in) :: mpar_data ! model parameters + logical(lgt) :: tooMuchSublim ! flag to denote that there was too much sublimation in a given time step + real(rkind),intent(inout) :: mLayerDepth(:) + + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + + ! local variables + character(len=256) :: cmessage ! error message + integer(i4b) :: iSnow ! index of snow layers + real(rkind) :: massLiquid ! mass liquid water (kg m-2) ! * compute change in ice content of the top snow layer due to sublimation... ! --------------------------------------------------------------------------- @@ -86,33 +86,33 @@ subroutine computSnowDepth(& ! NOTE: this is done BEFORE densification if(nSnow > 0)then ! snow layers exist - ! try to remove ice from the top layer - iSnow=1 + ! try to remove ice from the top layer + iSnow=1 - ! save the mass of liquid water (kg m-2) - massLiquid = mLayerDepth(iSnow)*mLayerVolFracLiq(iSnow)*iden_water + ! save the mass of liquid water (kg m-2) + massLiquid = mLayerDepth(iSnow)*mLayerVolFracLiq(iSnow)*iden_water - ! add/remove the depth of snow gained/lost by frost/sublimation (m) - ! NOTE: assume constant density - mLayerDepth(iSnow) = mLayerDepth(iSnow) + dt_sub*scalarSnowSublimation/(mLayerVolFracIce(iSnow)*iden_ice) + ! add/remove the depth of snow gained/lost by frost/sublimation (m) + ! NOTE: assume constant density + mLayerDepth(iSnow) = mLayerDepth(iSnow) + dt_sub*scalarSnowSublimation/(mLayerVolFracIce(iSnow)*iden_ice) - ! check that we did not remove the entire layer - if(mLayerDepth(iSnow) < verySmall)then - tooMuchSublim=.true. - return - endif + ! check that we did not remove the entire layer + if(mLayerDepth(iSnow) < verySmall)then + tooMuchSublim=.true. + return + endif - ! update the volumetric fraction of liquid water - mLayerVolFracLiq(iSnow) = massLiquid / (mLayerDepth(iSnow)*iden_water) + ! update the volumetric fraction of liquid water + mLayerVolFracLiq(iSnow) = massLiquid / (mLayerDepth(iSnow)*iden_water) ! no snow else - ! no snow: check that sublimation is zero - if(abs(scalarSnowSublimation) > verySmall)then - message=trim(message)//'sublimation of snow has been computed when no snow exists' - err=20; return - end if + ! no snow: check that sublimation is zero + if(abs(scalarSnowSublimation) > verySmall)then + message=trim(message)//'sublimation of snow has been computed when no snow exists' + err=20; return + end if end if ! (if snow layers exist) @@ -120,30 +120,29 @@ subroutine computSnowDepth(& ! *** account for compaction and cavitation in the snowpack... ! ------------------------------------------------------------ if(nSnow>0)then - call snwDensify(& - ! intent(in): variables - dt_sub, & ! intent(in): time step (s) - nSnow, & ! intent(in): number of snow layers - mLayerTemp(1:nSnow), & ! intent(in): temperature of each layer (K) - mLayerMeltFreeze(1:nSnow), & ! intent(in): volumetric melt in each layer (kg m-3) - ! intent(in): parameters - mpar_data%var(iLookPARAM%densScalGrowth)%dat(1), & ! intent(in): density scaling factor for grain growth (kg-1 m3) - mpar_data%var(iLookPARAM%tempScalGrowth)%dat(1), & ! intent(in): temperature scaling factor for grain growth (K-1) - mpar_data%var(iLookPARAM%grainGrowthRate)%dat(1), & ! intent(in): rate of grain growth (s-1) - mpar_data%var(iLookPARAM%densScalOvrbdn)%dat(1), & ! intent(in): density scaling factor for overburden pressure (kg-1 m3) - mpar_data%var(iLookPARAM%tempScalOvrbdn)%dat(1), & ! intent(in): temperature scaling factor for overburden pressure (K-1) - mpar_data%var(iLookPARAM%baseViscosity)%dat(1), & ! intent(in): viscosity coefficient at T=T_frz and snow density=0 (kg m-2 s) - ! intent(inout): state variables - mLayerDepth(1:nSnow), & ! intent(inout): depth of each layer (m) - mLayerVolFracLiq(1:nSnow), & ! intent(inout): volumetric fraction of liquid water after itertations (-) - mLayerVolFracIce(1:nSnow), & ! intent(inout): volumetric fraction of ice after itertations (-) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if + call snwDensify(& + ! intent(in): variables + dt_sub, & ! intent(in): time step (s) + nSnow, & ! intent(in): number of snow layers + mLayerTemp(1:nSnow), & ! intent(in): temperature of each layer (K) + mLayerMeltFreeze(1:nSnow), & ! intent(in): volumetric melt in each layer (kg m-3) + ! intent(in): parameters + mpar_data%var(iLookPARAM%densScalGrowth)%dat(1), & ! intent(in): density scaling factor for grain growth (kg-1 m3) + mpar_data%var(iLookPARAM%tempScalGrowth)%dat(1), & ! intent(in): temperature scaling factor for grain growth (K-1) + mpar_data%var(iLookPARAM%grainGrowthRate)%dat(1), & ! intent(in): rate of grain growth (s-1) + mpar_data%var(iLookPARAM%densScalOvrbdn)%dat(1), & ! intent(in): density scaling factor for overburden pressure (kg-1 m3) + mpar_data%var(iLookPARAM%tempScalOvrbdn)%dat(1), & ! intent(in): temperature scaling factor for overburden pressure (K-1) + mpar_data%var(iLookPARAM%baseViscosity)%dat(1), & ! intent(in): viscosity coefficient at T=T_frz and snow density=0 (kg m-2 s) + ! intent(inout): state variables + mLayerDepth(1:nSnow), & ! intent(inout): depth of each layer (m) + mLayerVolFracLiq(1:nSnow), & ! intent(inout): volumetric fraction of liquid water after itertations (-) + mLayerVolFracIce(1:nSnow), & ! intent(inout): volumetric fraction of ice after itertations (-) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if end if ! if snow layers exist - - end subroutine computSnowDepth +end subroutine computSnowDepth end module computSnowDepth_module From 0af18e79ee2ec34042b87dc5828907d40d6cb490 Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 27 Sep 2022 18:40:30 +0000 Subject: [PATCH 0362/1472] adjusted indentation --- build/source/engine/computThermConduct.f90 | 426 ++++++++++----------- 1 file changed, 212 insertions(+), 214 deletions(-) diff --git a/build/source/engine/computThermConduct.f90 b/build/source/engine/computThermConduct.f90 index 843806ea5..7d0bb9107 100644 --- a/build/source/engine/computThermConduct.f90 +++ b/build/source/engine/computThermConduct.f90 @@ -61,227 +61,225 @@ module computThermConduct_module contains - ! ********************************************************************************************************** - ! public subroutine computThermConduct: compute diagnostic energy variables (thermal conductivity and heat capacity) - ! ********************************************************************************************************** - subroutine computThermConduct(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) - ! input: state variables - scalarCanopyIce, & ! intent(in): canopy ice content (kg m-2) - scalarCanopyLiquid, & ! intent(in): canopy liquid water content (kg m-2) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - ! input/output: data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output: error control - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------------- - ! provide access to external subroutines - USE snow_utils_module,only:tcond_snow ! compute thermal conductivity of snow - ! -------------------------------------------------------------------------------------------------------------------------------------- - ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) - real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) - real(rkind),intent(in) :: scalarCanopyLiquid - real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) - ! input/output: data structures - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model layer indices - type(var_dlength),intent(in) :: prog_data ! model prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for a local HRU - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: iSoil ! index of soil layer - real(rkind) :: TCn ! thermal conductivity below the layer interface (W m-1 K-1) - real(rkind) :: TCp ! thermal conductivity above the layer interface (W m-1 K-1) - real(rkind) :: zdn ! height difference between interface and lower value (m) - real(rkind) :: zdp ! height difference between interface and upper value (m) - real(rkind) :: bulkden_soil ! bulk density of soil (kg m-3) - real(rkind) :: lambda_drysoil ! thermal conductivity of dry soil (W m-1) - real(rkind) :: lambda_wetsoil ! thermal conductivity of wet soil (W m-1) - real(rkind) :: lambda_wet ! thermal conductivity of the wet material - real(rkind) :: relativeSat ! relative saturation (-) - real(rkind) :: kerstenNum ! the Kersten number (-), defining weight applied to conductivity of the wet medium - real(rkind) :: den ! denominator in the thermal conductivity calculations - ! local variables to reproduce the thermal conductivity of Hansson et al. VZJ 2005 - real(rkind),parameter :: c1=0.55_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c2=0.8_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c3=3.07_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: c4=0.13_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c5=4._rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: f1=13.05_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: f2=1.06_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind) :: fArg,xArg ! temporary variables (see Hansson et al. VZJ 2005 for details) - ! -------------------------------------------------------------------------------------------------------------------------------- - ! associate variables in data structure - associate(& - ! input: model decisions - ixThCondSnow => model_decisions(iLookDECISIONS%thCondSnow)%iDecision, & ! intent(in): choice of method for thermal conductivity of snow - ixThCondSoil => model_decisions(iLookDECISIONS%thCondSoil)%iDecision, & ! intent(in): choice of method for thermal conductivity of soil - ! input: coordinate variables - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1), & ! intent(in): number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers - layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) - mLayerHeight => prog_data%var(iLookPROG%mLayerHeight)%dat, & ! intent(in): height at the mid-point of each layer (m) - iLayerHeight => prog_data%var(iLookPROG%iLayerHeight)%dat, & ! intent(in): height at the interface of each layer (m) - ! input: heat capacity and thermal conductivity - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) - fixedThermalCond_snow => mpar_data%var(iLookPARAM%fixedThermalCond_snow)%dat(1), & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) - ! input: depth varying soil parameters - iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) - thCond_soil => mpar_data%var(iLookPARAM%thCond_soil)%dat, & ! intent(in): thermal conductivity of soil (W m-1 K-1) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): soil porosity (-) - frac_sand => mpar_data%var(iLookPARAM%frac_sand)%dat, & ! intent(in): fraction of sand (-) - frac_silt => mpar_data%var(iLookPARAM%frac_silt)%dat, & ! intent(in): fraction of silt (-) - frac_clay => mpar_data%var(iLookPARAM%frac_clay)%dat, & ! intent(in): fraction of clay (-) - ! output: diagnostic variables - mLayerThermalC => diag_data%var(iLookDIAG%mLayerThermalC)%dat, & ! intent(out): thermal conductivity at the mid-point of each layer (W m-1 K-1) - iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(out): thermal conductivity at the interface of each layer (W m-1 K-1) - mLayerVolFracAir => diag_data%var(iLookDIAG%mLayerVolFracAir)%dat & ! intent(out): volumetric fraction of air in each layer (-) - ) ! end associate statement - ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="computThermConduct/" - - ! initialize the soil layer - iSoil=integerMissing - - ! loop through layers - do iLayer=1,nLayers - - ! get the soil layer - if(iLayer>nSnow) iSoil = iLayer-nSnow - - ! compute the thermal conductivity of dry and wet soils (W m-1) - ! NOTE: this is actually constant over the simulation, and included here for clarity - if(ixThCondSoil == funcSoilWet .and. layerType(iLayer)==iname_soil)then - bulkden_soil = iden_soil(iSoil)*( 1._rkind - theta_sat(iSoil) ) - lambda_drysoil = (0.135_rkind*bulkden_soil + 64.7_rkind) / (iden_soil(iSoil) - 0.947_rkind*bulkden_soil) - lambda_wetsoil = (8.80_rkind*frac_sand(iSoil) + 2.92_rkind*frac_clay(iSoil)) / (frac_sand(iSoil) + frac_clay(iSoil)) - end if - - ! ***** - ! * compute the volumetric fraction of air in each layer... - ! ********************************************************* - select case(layerType(iLayer)) - case(iname_soil); mLayerVolFracAir(iLayer) = theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) - case(iname_snow); mLayerVolFracAir(iLayer) = 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute volumetric fraction of air'; return - end select - - ! ***** - ! * compute the thermal conductivity of snow and soil at the mid-point of each layer... - ! ************************************************************************************* - select case(layerType(iLayer)) - - ! ***** soil - case(iname_soil) - - ! select option for thermal conductivity of soil - select case(ixThCondSoil) - - ! ** function of soil wetness - case(funcSoilWet) - - ! compute the thermal conductivity of the wet material (W m-1) - lambda_wet = lambda_wetsoil**( 1._rkind - theta_sat(iSoil) ) * lambda_water**theta_sat(iSoil) * lambda_ice**(theta_sat(iSoil) - mLayerVolFracLiq(iLayer)) - relativeSat = (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer))/theta_sat(iSoil) ! relative saturation - ! compute the Kersten number (-) - if(relativeSat > 0.1_rkind)then ! log10(0.1) = -1 - kerstenNum = log10(relativeSat) + 1._rkind - else - kerstenNum = 0._rkind ! dry thermal conductivity - endif - ! ...and, compute the thermal conductivity - mLayerThermalC(iLayer) = kerstenNum*lambda_wet + (1._rkind - kerstenNum)*lambda_drysoil - - ! ** mixture of constituents - case(mixConstit) - mLayerThermalC(iLayer) = thCond_soil(iSoil) * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component +! ********************************************************************************************************** +! public subroutine computThermConduct: compute diagnostic energy variables (thermal conductivity and heat capacity) +! ********************************************************************************************************** +subroutine computThermConduct(& + ! input: control variables + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) + ! input: state variables + scalarCanopyIce, & ! intent(in): canopy ice content (kg m-2) + scalarCanopyLiquid, & ! intent(in): canopy liquid water content (kg m-2) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + ! input/output: data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + ! output: error control + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! provide access to external subroutines + USE snow_utils_module,only:tcond_snow ! compute thermal conductivity of snow + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! input: model control + logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) + real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) + real(rkind),intent(in) :: scalarCanopyLiquid + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) + ! input/output: data structures + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model layer indices + type(var_dlength),intent(in) :: prog_data ! model prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for a local HRU + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: iSoil ! index of soil layer + real(rkind) :: TCn ! thermal conductivity below the layer interface (W m-1 K-1) + real(rkind) :: TCp ! thermal conductivity above the layer interface (W m-1 K-1) + real(rkind) :: zdn ! height difference between interface and lower value (m) + real(rkind) :: zdp ! height difference between interface and upper value (m) + real(rkind) :: bulkden_soil ! bulk density of soil (kg m-3) + real(rkind) :: lambda_drysoil ! thermal conductivity of dry soil (W m-1) + real(rkind) :: lambda_wetsoil ! thermal conductivity of wet soil (W m-1) + real(rkind) :: lambda_wet ! thermal conductivity of the wet material + real(rkind) :: relativeSat ! relative saturation (-) + real(rkind) :: kerstenNum ! the Kersten number (-), defining weight applied to conductivity of the wet medium + real(rkind) :: den ! denominator in the thermal conductivity calculations + ! local variables to reproduce the thermal conductivity of Hansson et al. VZJ 2005 + real(rkind),parameter :: c1=0.55_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c2=0.8_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c3=3.07_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: c4=0.13_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c5=4._rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: f1=13.05_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: f2=1.06_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind) :: fArg,xArg ! temporary variables (see Hansson et al. VZJ 2005 for details) + ! -------------------------------------------------------------------------------------------------------------------------------- + ! associate variables in data structure + associate(& + ! input: model decisions + ixThCondSnow => model_decisions(iLookDECISIONS%thCondSnow)%iDecision, & ! intent(in): choice of method for thermal conductivity of snow + ixThCondSoil => model_decisions(iLookDECISIONS%thCondSoil)%iDecision, & ! intent(in): choice of method for thermal conductivity of soil + ! input: coordinate variables + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1), & ! intent(in): number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers + layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) + mLayerHeight => prog_data%var(iLookPROG%mLayerHeight)%dat, & ! intent(in): height at the mid-point of each layer (m) + iLayerHeight => prog_data%var(iLookPROG%iLayerHeight)%dat, & ! intent(in): height at the interface of each layer (m) + ! input: heat capacity and thermal conductivity + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) + fixedThermalCond_snow => mpar_data%var(iLookPARAM%fixedThermalCond_snow)%dat(1), & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) + ! input: depth varying soil parameters + iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) + thCond_soil => mpar_data%var(iLookPARAM%thCond_soil)%dat, & ! intent(in): thermal conductivity of soil (W m-1 K-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): soil porosity (-) + frac_sand => mpar_data%var(iLookPARAM%frac_sand)%dat, & ! intent(in): fraction of sand (-) + frac_silt => mpar_data%var(iLookPARAM%frac_silt)%dat, & ! intent(in): fraction of silt (-) + frac_clay => mpar_data%var(iLookPARAM%frac_clay)%dat, & ! intent(in): fraction of clay (-) + ! output: diagnostic variables + mLayerThermalC => diag_data%var(iLookDIAG%mLayerThermalC)%dat, & ! intent(out): thermal conductivity at the mid-point of each layer (W m-1 K-1) + iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(out): thermal conductivity at the interface of each layer (W m-1 K-1) + mLayerVolFracAir => diag_data%var(iLookDIAG%mLayerVolFracAir)%dat & ! intent(out): volumetric fraction of air in each layer (-) + ) ! end associate statement + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="computThermConduct/" + + ! initialize the soil layer + iSoil=integerMissing + + ! loop through layers + do iLayer=1,nLayers + + ! get the soil layer + if(iLayer>nSnow) iSoil = iLayer-nSnow + + ! compute the thermal conductivity of dry and wet soils (W m-1) + ! NOTE: this is actually constant over the simulation, and included here for clarity + if(ixThCondSoil == funcSoilWet .and. layerType(iLayer)==iname_soil)then + bulkden_soil = iden_soil(iSoil)*( 1._rkind - theta_sat(iSoil) ) + lambda_drysoil = (0.135_rkind*bulkden_soil + 64.7_rkind) / (iden_soil(iSoil) - 0.947_rkind*bulkden_soil) + lambda_wetsoil = (8.80_rkind*frac_sand(iSoil) + 2.92_rkind*frac_clay(iSoil)) / (frac_sand(iSoil) + frac_clay(iSoil)) + end if + + ! ***** + ! * compute the volumetric fraction of air in each layer... + ! ********************************************************* + select case(layerType(iLayer)) + case(iname_soil); mLayerVolFracAir(iLayer) = theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) + case(iname_snow); mLayerVolFracAir(iLayer) = 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute volumetric fraction of air'; return + end select + + ! ***** + ! * compute the thermal conductivity of snow and soil at the mid-point of each layer... + ! ************************************************************************************* + select case(layerType(iLayer)) + + ! ***** soil + case(iname_soil) + + ! select option for thermal conductivity of soil + select case(ixThCondSoil) + + ! ** function of soil wetness + case(funcSoilWet) + + ! compute the thermal conductivity of the wet material (W m-1) + lambda_wet = lambda_wetsoil**( 1._rkind - theta_sat(iSoil) ) * lambda_water**theta_sat(iSoil) * lambda_ice**(theta_sat(iSoil) - mLayerVolFracLiq(iLayer)) + relativeSat = (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer))/theta_sat(iSoil) ! relative saturation + ! compute the Kersten number (-) + if(relativeSat > 0.1_rkind)then ! log10(0.1) = -1 + kerstenNum = log10(relativeSat) + 1._rkind + else + kerstenNum = 0._rkind ! dry thermal conductivity + endif + ! ...and, compute the thermal conductivity + mLayerThermalC(iLayer) = kerstenNum*lambda_wet + (1._rkind - kerstenNum)*lambda_drysoil + + ! ** mixture of constituents + case(mixConstit) + mLayerThermalC(iLayer) = thCond_soil(iSoil) * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component lambda_ice * mLayerVolFracIce(iLayer) + & ! ice component lambda_water * mLayerVolFracLiq(iLayer) + & ! liquid water component lambda_air * mLayerVolFracAir(iLayer) ! air component - ! ** test case for the mizoguchi lab experiment, Hansson et al. VZJ 2004 - case(hanssonVZJ) - fArg = 1._rkind + f1*mLayerVolFracIce(iLayer)**f2 - xArg = mLayerVolFracLiq(iLayer) + fArg*mLayerVolFracIce(iLayer) - mLayerThermalC(iLayer) = c1 + c2*xArg + (c1 - c4)*exp(-(c3*xArg)**c5) + ! ** test case for the mizoguchi lab experiment, Hansson et al. VZJ 2004 + case(hanssonVZJ) + fArg = 1._rkind + f1*mLayerVolFracIce(iLayer)**f2 + xArg = mLayerVolFracLiq(iLayer) + fArg*mLayerVolFracIce(iLayer) + mLayerThermalC(iLayer) = c1 + c2*xArg + (c1 - c4)*exp(-(c3*xArg)**c5) + + ! ** check + case default; err=20; message=trim(message)//'unable to identify option for thermal conductivity of soil'; return + + end select ! option for the thermal conductivity of soil + + ! ***** snow + case(iname_snow) + ! temporally constant thermal conductivity + if(ixThCondSnow==Smirnova2000)then + mLayerThermalC(iLayer) = fixedThermalCond_snow + ! thermal conductivity as a function of snow density + else + call tcond_snow(mLayerVolFracIce(iLayer)*iden_ice, & ! input: snow density (kg m-3) + mLayerThermalC(iLayer), & ! output: thermal conductivity (W m-1 K-1) + err,cmessage) ! output: error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + endif + + ! * error check + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute thermal conductivity'; return + + end select + + end do ! looping through layers + !pause + + ! ***** + ! * compute the thermal conductivity of snow at the interface of each layer... + ! **************************************************************************** + do iLayer=1,nLayers-1 ! (loop through layers) + ! get temporary variables + TCn = mLayerThermalC(iLayer) ! thermal conductivity below the layer interface (W m-1 K-1) + TCp = mLayerThermalC(iLayer+1) ! thermal conductivity above the layer interface (W m-1 K-1) + zdn = iLayerHeight(iLayer) - mLayerHeight(iLayer) ! height difference between interface and lower value (m) + zdp = mLayerHeight(iLayer+1) - iLayerHeight(iLayer) ! height difference between interface and upper value (m) + den = TCn*zdp + TCp*zdn ! denominator + ! compute thermal conductivity + if(TCn+TCp > epsilon(TCn))then + iLayerThermalC(iLayer) = (TCn*TCp*(zdn + zdp)) / den + else + iLayerThermalC(iLayer) = (TCn*zdn + TCp*zdp) / (zdn + zdp) + endif + end do ! looping through layers + + ! special case of hansson + if(ixThCondSoil==hanssonVZJ)then + iLayerThermalC(0) = 28._rkind*(0.5_rkind*(iLayerHeight(1) - iLayerHeight(0))) + else + iLayerThermalC(0) = mLayerThermalC(1) + end if - ! ** check - case default; err=20; message=trim(message)//'unable to identify option for thermal conductivity of soil'; return + ! assume the thermal conductivity at the domain boundaries is equal to the thermal conductivity of the layer + iLayerThermalC(nLayers) = mLayerThermalC(nLayers) - end select ! option for the thermal conductivity of soil + ! end association to variables in the data structure + end associate - ! ***** snow - case(iname_snow) - ! temporally constant thermal conductivity - if(ixThCondSnow==Smirnova2000)then - mLayerThermalC(iLayer) = fixedThermalCond_snow - ! thermal conductivity as a function of snow density - else - call tcond_snow(mLayerVolFracIce(iLayer)*iden_ice, & ! input: snow density (kg m-3) - mLayerThermalC(iLayer), & ! output: thermal conductivity (W m-1 K-1) - err,cmessage) ! output: error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - endif - - ! * error check - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute thermal conductivity'; return - - end select - !print*, 'iLayer, mLayerThermalC(iLayer) = ', iLayer, mLayerThermalC(iLayer) - - end do ! looping through layers - !pause - - ! ***** - ! * compute the thermal conductivity of snow at the interface of each layer... - ! **************************************************************************** - do iLayer=1,nLayers-1 ! (loop through layers) - ! get temporary variables - TCn = mLayerThermalC(iLayer) ! thermal conductivity below the layer interface (W m-1 K-1) - TCp = mLayerThermalC(iLayer+1) ! thermal conductivity above the layer interface (W m-1 K-1) - zdn = iLayerHeight(iLayer) - mLayerHeight(iLayer) ! height difference between interface and lower value (m) - zdp = mLayerHeight(iLayer+1) - iLayerHeight(iLayer) ! height difference between interface and upper value (m) - den = TCn*zdp + TCp*zdn ! denominator - ! compute thermal conductivity - if(TCn+TCp > epsilon(TCn))then - iLayerThermalC(iLayer) = (TCn*TCp*(zdn + zdp)) / den - else - iLayerThermalC(iLayer) = (TCn*zdn + TCp*zdp) / (zdn + zdp) - endif - !write(*,'(a,1x,i4,1x,10(f9.3,1x))') 'iLayer, TCn, TCp, zdn, zdp, iLayerThermalC(iLayer) = ', iLayer, TCn, TCp, zdn, zdp, iLayerThermalC(iLayer) - end do ! looping through layers - - ! special case of hansson - if(ixThCondSoil==hanssonVZJ)then - iLayerThermalC(0) = 28._rkind*(0.5_rkind*(iLayerHeight(1) - iLayerHeight(0))) - else - iLayerThermalC(0) = mLayerThermalC(1) - end if - - ! assume the thermal conductivity at the domain boundaries is equal to the thermal conductivity of the layer - iLayerThermalC(nLayers) = mLayerThermalC(nLayers) - - ! end association to variables in the data structure - end associate - - end subroutine computThermConduct +end subroutine computThermConduct end module computThermConduct_module From 9a012c6de7f3f3fb07a5fec830a3b5d383dc1d9b Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 27 Sep 2022 18:44:19 +0000 Subject: [PATCH 0363/1472] fixed indentation --- build/source/engine/soil_utilsAddSundials.f90 | 354 +++++++++--------- 1 file changed, 173 insertions(+), 181 deletions(-) diff --git a/build/source/engine/soil_utilsAddSundials.f90 b/build/source/engine/soil_utilsAddSundials.f90 index 04af5faaf..af0f4f191 100644 --- a/build/source/engine/soil_utilsAddSundials.f90 +++ b/build/source/engine/soil_utilsAddSundials.f90 @@ -47,188 +47,180 @@ module soil_utilsAddSundials_module contains - ! ****************************************************************************************************************************** - ! public subroutine: compute the liquid water matric potential (and the derivatives w.r.t. total matric potential and temperature) - ! ****************************************************************************************************************************** - subroutine liquidHeadSundials(& - ! input - matricHeadTotal ,& ! intent(in) : total water matric potential (m) - matricHeadTotalPrime ,& ! intent(in) - volFracLiq ,& ! intent(in) : volumetric fraction of liquid water (-) - volFracIce ,& ! intent(in) : volumetric fraction of ice (-) - vGn_alpha,vGn_n,theta_sat,theta_res,vGn_m,& ! intent(in) : soil parameters - dVolTot_dPsi0 ,& ! intent(in) : derivative in the soil water characteristic (m-1) - dTheta_dT ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) - tempPrime ,& ! intent(in) - volFracLiqPrime ,& ! intent(in) - volFracIcePrime ,& ! intent(in) - ! output - matricHeadLiq ,& ! intent(out) : liquid water matric potential (m) - matricHeadLiqPrime ,& ! intent(out) - dPsiLiq_dPsi0 ,& ! intent(out) : derivative in the liquid water matric potential w.r.t. the total water matric potential (-) - dPsiLiq_dTemp ,& ! intent(out) : derivative in the liquid water matric potential w.r.t. temperature (m K-1) - err,message) ! intent(out) : error control - ! computes the liquid water matric potential (and the derivatives w.r.t. total matric potential and temperature) +! ****************************************************************************************************************************** +! public subroutine: compute the liquid water matric potential (and the derivatives w.r.t. total matric potential and temperature) +! ****************************************************************************************************************************** +subroutine liquidHeadSundials(& + ! input + matricHeadTotal ,& ! intent(in) : total water matric potential (m) + matricHeadTotalPrime ,& ! intent(in) + volFracLiq ,& ! intent(in) : volumetric fraction of liquid water (-) + volFracIce ,& ! intent(in) : volumetric fraction of ice (-) + vGn_alpha,vGn_n,theta_sat,theta_res,vGn_m,& ! intent(in) : soil parameters + dVolTot_dPsi0 ,& ! intent(in) : derivative in the soil water characteristic (m-1) + dTheta_dT ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) + tempPrime ,& ! intent(in) + volFracLiqPrime ,& ! intent(in) + volFracIcePrime ,& ! intent(in) + ! output + matricHeadLiq ,& ! intent(out) : liquid water matric potential (m) + matricHeadLiqPrime ,& ! intent(out) + dPsiLiq_dPsi0 ,& ! intent(out) : derivative in the liquid water matric potential w.r.t. the total water matric potential (-) + dPsiLiq_dTemp ,& ! intent(out) : derivative in the liquid water matric potential w.r.t. temperature (m K-1) + err,message) ! intent(out) : error control + ! computes the liquid water matric potential (and the derivatives w.r.t. total matric potential and temperature) implicit none - ! input - real(rkind),intent(in) :: matricHeadTotal ! total water matric potential (m) - real(rkind),intent(in) :: matricHeadTotalPrime - real(rkind),intent(in) :: volFracLiq ! volumetric fraction of liquid water (-) - real(rkind),intent(in) :: volFracIce ! volumetric fraction of ice (-) - real(rkind),intent(in) :: vGn_alpha,vGn_n,theta_sat,theta_res,vGn_m ! soil parameters - real(rkind),intent(in) ,optional :: dVolTot_dPsi0 ! derivative in the soil water characteristic (m-1) - real(rkind),intent(in) ,optional :: dTheta_dT ! derivative in volumetric total water w.r.t. temperature (K-1) - real(rkind),intent(in) :: TempPrime - real(rkind),intent(in) :: volFracLiqPrime - real(rkind),intent(in) :: volFracIcePrime - ! output - real(rkind),intent(out) :: matricHeadLiq ! liquid water matric potential (m) - real(rkind),intent(out) :: matricHeadLiqPrime - real(rkind),intent(out) ,optional :: dPsiLiq_dPsi0 ! derivative in the liquid water matric potential w.r.t. the total water matric potential (-) - real(rkind),intent(out) ,optional :: dPsiLiq_dTemp ! derivative in the liquid water matric potential w.r.t. temperature (m K-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! local - real(rkind) :: xNum,xDen ! temporary variables (numeratir, denominator) - real(rkind) :: effSat ! effective saturation (-) - real(rkind) :: dPsiLiq_dEffSat ! derivative in liquid water matric potential w.r.t. effective saturation (m) - real(rkind) :: dEffSat_dTemp ! derivative in effective saturation w.r.t. temperature (K-1) - real(rkind) :: dEffSat_dFracLiq - real(rkind) :: effSatPrime - ! ------------------------------------------------------------------------------------------------------------------------------ - ! initialize error control - err=0; message='liquidHeadSundials/' - - ! ** partially frozen soil - if(volFracIce > verySmall .and. matricHeadTotal < 0._rkind)then ! check that ice exists and that the soil is unsaturated - - - ! ----- - ! - compute liquid water matric potential... - ! ------------------------------------------ - - ! - compute effective saturation - ! NOTE: include ice content as part of the solid porosity - major effect of ice is to reduce the pore size; ensure that effSat=1 at saturation - ! (from Zhao et al., J. Hydrol., 1997: Numerical analysis of simultaneous heat and mass transfer...) - xNum = volFracLiq - theta_res - xDen = theta_sat - volFracIce - theta_res - effSat = xNum/xDen ! effective saturation - - ! - matric head associated with liquid water - matricHeadLiq = matricHead(effSat,vGn_alpha,0._rkind,1._rkind,vGn_n,vGn_m) ! argument is effective saturation, so theta_res=0 and theta_sat=1 - if (effSat < 1._rkind .and. effSat > 0._rkind)then - effSatPrime = (volFracLiqPrime * xDen + volFracIcePrime * xNum) / xDen**2._rkind - matricHeadLiqPrime = -( 1._rkind/(vGn_alpha*vGn_n*vGn_m) ) * effSat**(-1._rkind-1._rkind/vGn_m) * ( effSat**(-1._rkind/vGn_m) - 1._rkind )**(-1._rkind+1._rkind/vGn_n) * effSatPrime + ! input + real(rkind),intent(in) :: matricHeadTotal ! total water matric potential (m) + real(rkind),intent(in) :: matricHeadTotalPrime + real(rkind),intent(in) :: volFracLiq ! volumetric fraction of liquid water (-) + real(rkind),intent(in) :: volFracIce ! volumetric fraction of ice (-) + real(rkind),intent(in) :: vGn_alpha,vGn_n,theta_sat,theta_res,vGn_m ! soil parameters + real(rkind),intent(in) ,optional :: dVolTot_dPsi0 ! derivative in the soil water characteristic (m-1) + real(rkind),intent(in) ,optional :: dTheta_dT ! derivative in volumetric total water w.r.t. temperature (K-1) + real(rkind),intent(in) :: TempPrime + real(rkind),intent(in) :: volFracLiqPrime + real(rkind),intent(in) :: volFracIcePrime + ! output + real(rkind),intent(out) :: matricHeadLiq ! liquid water matric potential (m) + real(rkind),intent(out) :: matricHeadLiqPrime + real(rkind),intent(out) ,optional :: dPsiLiq_dPsi0 ! derivative in the liquid water matric potential w.r.t. the total water matric potential (-) + real(rkind),intent(out) ,optional :: dPsiLiq_dTemp ! derivative in the liquid water matric potential w.r.t. temperature (m K-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local + real(rkind) :: xNum,xDen ! temporary variables (numeratir, denominator) + real(rkind) :: effSat ! effective saturation (-) + real(rkind) :: dPsiLiq_dEffSat ! derivative in liquid water matric potential w.r.t. effective saturation (m) + real(rkind) :: dEffSat_dTemp ! derivative in effective saturation w.r.t. temperature (K-1) + real(rkind) :: dEffSat_dFracLiq + real(rkind) :: effSatPrime + ! ------------------------------------------------------------------------------------------------------------------------------ + ! initialize error control + err=0; message='liquidHeadSundials/' + + ! ** partially frozen soil + if(volFracIce > verySmall .and. matricHeadTotal < 0._rkind)then ! check that ice exists and that the soil is unsaturated + + + ! ----- + ! - compute liquid water matric potential... + ! ------------------------------------------ + + ! - compute effective saturation + ! NOTE: include ice content as part of the solid porosity - major effect of ice is to reduce the pore size; ensure that effSat=1 at saturation + ! (from Zhao et al., J. Hydrol., 1997: Numerical analysis of simultaneous heat and mass transfer...) + xNum = volFracLiq - theta_res + xDen = theta_sat - volFracIce - theta_res + effSat = xNum/xDen ! effective saturation + + ! - matric head associated with liquid water + matricHeadLiq = matricHead(effSat,vGn_alpha,0._rkind,1._rkind,vGn_n,vGn_m) ! argument is effective saturation, so theta_res=0 and theta_sat=1 + if (effSat < 1._rkind .and. effSat > 0._rkind)then + effSatPrime = (volFracLiqPrime * xDen + volFracIcePrime * xNum) / xDen**2._rkind + matricHeadLiqPrime = -( 1._rkind/(vGn_alpha*vGn_n*vGn_m) ) * effSat**(-1._rkind-1._rkind/vGn_m) * ( effSat**(-1._rkind/vGn_m) - 1._rkind )**(-1._rkind+1._rkind/vGn_n) * effSatPrime + else + matricHeadLiqPrime = 0._rkind + endif + + + ! compute derivative in liquid water matric potential w.r.t. effective saturation (m) + if(present(dPsiLiq_dPsi0).or.present(dPsiLiq_dTemp))then + dPsiLiq_dEffSat = dPsi_dTheta(effSat,vGn_alpha,0._rkind,1._rkind,vGn_n,vGn_m) + endif + + ! ----- + ! - compute derivative in the liquid water matric potential w.r.t. the total water matric potential... + ! ---------------------------------------------------------------------------------------------------- + + ! check if the derivative is desired + if(present(dPsiLiq_dTemp))then + ! (check required input derivative is present) + if(.not.present(dVolTot_dPsi0))then + message=trim(message)//'dVolTot_dPsi0 argument is missing' + err=20; return + endif + + ! (compute derivative in the liquid water matric potential w.r.t. the total water matric potential) + dPsiLiq_dPsi0 = dVolTot_dPsi0*dPsiLiq_dEffSat*xNum/(xDen**2._rkind) + + endif ! if dPsiLiq_dTemp is desired + + ! ----- + ! - compute the derivative in the liquid water matric potential w.r.t. temperature... + ! ----------------------------------------------------------------------------------- + + ! check if the derivative is desired + if(present(dPsiLiq_dTemp))then + + ! (check required input derivative is present) + if(.not.present(dTheta_dT))then + message=trim(message)//'dTheta_dT argument is missing' + err=20; return + endif + ! (compute the derivative in the liquid water matric potential w.r.t. temperature) + dEffSat_dTemp = -dTheta_dT*xNum/(xDen**2._rkind) + dTheta_dT/xDen + dPsiLiq_dTemp = dPsiLiq_dEffSat*dEffSat_dTemp + endif ! if dPsiLiq_dTemp is desired + + ! ** unfrozen soil + else ! (no ice) + matricHeadLiq = matricHeadTotal + matricHeadLiqPrime = matricHeadTotalPrime + if(present(dPsiLiq_dTemp)) dPsiLiq_dPsi0 = 1._rkind ! derivative=1 because values are identical + if(present(dPsiLiq_dTemp)) dPsiLiq_dTemp = 0._rkind ! derivative=0 because no impact of temperature for unfrozen conditions + end if ! (if ice exists) + +end subroutine liquidHeadSundials + +! ****************************************************************************************************************************** +! public function d2Theta_dPsi2: compute the second derivative of the soil water characteristic (m-1) +! ****************************************************************************************************************************** +function d2Theta_dPsi2(psi,alpha,theta_res,theta_sat,n,m) + implicit none + real(rkind),intent(in) :: psi ! soil water suction (m) + real(rkind),intent(in) :: alpha ! scaling parameter (m-1) + real(rkind),intent(in) :: theta_res ! residual volumetric water content (-) + real(rkind),intent(in) :: theta_sat ! porosity (-) + real(rkind),intent(in) :: n ! vGn "n" parameter (-) + real(rkind),intent(in) :: m ! vGn "m" parameter (-) + real(rkind) :: d2Theta_dPsi2 ! derivative of the soil water characteristic (m-1) + real(rkind) :: mult_fcn + real(rkind) :: mult_fcnp + if(psi<0._rkind)then + mult_fcn = (-m*n*alpha*(alpha*psi)**(n-1._rkind)) * ( 1._rkind + (psi*alpha)**n )**(-1._rkind) + mult_fcnp = -m*n*alpha*(n-1._rkind)*alpha*(alpha*psi)**(n-2._rkind)*( 1._rkind + (psi*alpha)**n )**(-1._rkind) - & + ( n*alpha*(alpha*psi)**(n-1._rkind)*(1._rkind + (psi*alpha)**n)**(-2._rkind) ) * ( -m*n*alpha*(alpha*psi)**(n-1._rkind) ) + d2Theta_dPsi2 = mult_fcn * dTheta_dPsi(psi,alpha,theta_res,theta_sat,n,m) + & + mult_fcnp * ( volFracLiq(psi,alpha,theta_res,theta_sat,n,m) - theta_res ) else - matricHeadLiqPrime = 0._rkind - endif - - - ! compute derivative in liquid water matric potential w.r.t. effective saturation (m) - if(present(dPsiLiq_dPsi0).or.present(dPsiLiq_dTemp))then - dPsiLiq_dEffSat = dPsi_dTheta(effSat,vGn_alpha,0._rkind,1._rkind,vGn_n,vGn_m) - endif - - ! ----- - ! - compute derivative in the liquid water matric potential w.r.t. the total water matric potential... - ! ---------------------------------------------------------------------------------------------------- - - ! check if the derivative is desired - if(present(dPsiLiq_dTemp))then - ! (check required input derivative is present) - if(.not.present(dVolTot_dPsi0))then - message=trim(message)//'dVolTot_dPsi0 argument is missing' - err=20; return - endif - - ! (compute derivative in the liquid water matric potential w.r.t. the total water matric potential) - dPsiLiq_dPsi0 = dVolTot_dPsi0*dPsiLiq_dEffSat*xNum/(xDen**2._rkind) - ! matricHeadLiqPrime = dPsiLiq_dPsi0 * matricHeadTotalPrime - - endif ! if dPsiLiq_dTemp is desired - - ! ----- - ! - compute the derivative in the liquid water matric potential w.r.t. temperature... - ! ----------------------------------------------------------------------------------- - - ! check if the derivative is desired - if(present(dPsiLiq_dTemp))then - - ! (check required input derivative is present) - if(.not.present(dTheta_dT))then - message=trim(message)//'dTheta_dT argument is missing' - err=20; return - endif - ! (compute the derivative in the liquid water matric potential w.r.t. temperature) - dEffSat_dTemp = -dTheta_dT*xNum/(xDen**2._rkind) + dTheta_dT/xDen - dPsiLiq_dTemp = dPsiLiq_dEffSat*dEffSat_dTemp - ! matricHeadLiqPrime = dPsiLiq_dTemp * tempPrime - - - - endif ! if dPsiLiq_dTemp is desired - - ! ** unfrozen soil - else ! (no ice) - matricHeadLiq = matricHeadTotal - matricHeadLiqPrime = matricHeadTotalPrime - if(present(dPsiLiq_dTemp)) dPsiLiq_dPsi0 = 1._rkind ! derivative=1 because values are identical - if(present(dPsiLiq_dTemp)) dPsiLiq_dTemp = 0._rkind ! derivative=0 because no impact of temperature for unfrozen conditions - end if ! (if ice exists) - - end subroutine liquidHeadSundials - - - ! ****************************************************************************************************************************** - ! public function d2Theta_dPsi2: compute the second derivative of the soil water characteristic (m-1) - ! ****************************************************************************************************************************** - function d2Theta_dPsi2(psi,alpha,theta_res,theta_sat,n,m) - implicit none - real(rkind),intent(in) :: psi ! soil water suction (m) - real(rkind),intent(in) :: alpha ! scaling parameter (m-1) - real(rkind),intent(in) :: theta_res ! residual volumetric water content (-) - real(rkind),intent(in) :: theta_sat ! porosity (-) - real(rkind),intent(in) :: n ! vGn "n" parameter (-) - real(rkind),intent(in) :: m ! vGn "m" parameter (-) - real(rkind) :: d2Theta_dPsi2 ! derivative of the soil water characteristic (m-1) - real(rkind) :: mult_fcn - real(rkind) :: mult_fcnp - if(psi<0._rkind)then - mult_fcn = (-m*n*alpha*(alpha*psi)**(n-1._rkind)) * ( 1._rkind + (psi*alpha)**n )**(-1._rkind) - mult_fcnp = -m*n*alpha*(n-1._rkind)*alpha*(alpha*psi)**(n-2._rkind)*( 1._rkind + (psi*alpha)**n )**(-1._rkind) - & - ( n*alpha*(alpha*psi)**(n-1._rkind)*(1._rkind + (psi*alpha)**n)**(-2._rkind) ) * ( -m*n*alpha*(alpha*psi)**(n-1._rkind) ) - d2Theta_dPsi2 = mult_fcn * dTheta_dPsi(psi,alpha,theta_res,theta_sat,n,m) + & - mult_fcnp * ( volFracLiq(psi,alpha,theta_res,theta_sat,n,m) - theta_res ) - else - d2Theta_dPsi2 = 0._rkind - end if - end function d2Theta_dPsi2 - - - ! ****************************************************************************************************************************** - ! public function d2Theta_dTk2: differentiate the freezing curve w.r.t. temperature - ! ****************************************************************************************************************************** - function d2Theta_dTk2(Tk,theta_res,theta_sat,alpha,n,m) - implicit none - real(rkind),intent(in) :: Tk ! temperature (K) - real(rkind),intent(in) :: theta_res ! residual liquid water content (-) - real(rkind),intent(in) :: theta_sat ! porosity (-) - real(rkind),intent(in) :: alpha ! vGn scaling parameter (m-1) - real(rkind),intent(in) :: n ! vGn "n" parameter (-) - real(rkind),intent(in) :: m ! vGn "m" parameter (-) - real(rkind) :: d2Theta_dTk2 ! derivative of the freezing curve w.r.t. temperature (K-1) - ! local variables - real(rkind) :: kappa ! constant (m K-1) - real(rkind) :: xtemp ! alpha*kappa*(Tk-Tfreeze) -- dimensionless variable (used more than once) - ! compute kappa (m K-1) - kappa = LH_fus/(gravity*Tfreeze) ! NOTE: J = kg m2 s-2 - ! define a tempory variable that is used more than once (-) - xtemp = alpha*kappa*(Tk-Tfreeze) - ! differentiate the freezing curve w.r.t. temperature -- making use of the chain rule - d2Theta_dTk2 = (-alpha*kappa*m*n*alpha*kappa)* (theta_sat - theta_res) * ( (n-1)*xtemp**(n - 2._rkind) * (1._rkind + xtemp**n)**(-m - 1._rkind) & - + n*(-m-1)*xtemp**(2*n - 2._rkind) * (1._rkind + xtemp**n)**(-m - 2._rkind) ) - end function d2Theta_dTk2 - + d2Theta_dPsi2 = 0._rkind + end if +end function d2Theta_dPsi2 + +! ****************************************************************************************************************************** +! public function d2Theta_dTk2: differentiate the freezing curve w.r.t. temperature +! ****************************************************************************************************************************** +function d2Theta_dTk2(Tk,theta_res,theta_sat,alpha,n,m) + implicit none + real(rkind),intent(in) :: Tk ! temperature (K) + real(rkind),intent(in) :: theta_res ! residual liquid water content (-) + real(rkind),intent(in) :: theta_sat ! porosity (-) + real(rkind),intent(in) :: alpha ! vGn scaling parameter (m-1) + real(rkind),intent(in) :: n ! vGn "n" parameter (-) + real(rkind),intent(in) :: m ! vGn "m" parameter (-) + real(rkind) :: d2Theta_dTk2 ! derivative of the freezing curve w.r.t. temperature (K-1) + ! local variables + real(rkind) :: kappa ! constant (m K-1) + real(rkind) :: xtemp ! alpha*kappa*(Tk-Tfreeze) -- dimensionless variable (used more than once) + ! compute kappa (m K-1) + kappa = LH_fus/(gravity*Tfreeze) ! NOTE: J = kg m2 s-2 + ! define a tempory variable that is used more than once (-) + xtemp = alpha*kappa*(Tk-Tfreeze) + ! differentiate the freezing curve w.r.t. temperature -- making use of the chain rule + d2Theta_dTk2 = (-alpha*kappa*m*n*alpha*kappa)* (theta_sat - theta_res) * ( (n-1)*xtemp**(n - 2._rkind) * (1._rkind + xtemp**n)**(-m - 1._rkind) & + + n*(-m-1)*xtemp**(2*n - 2._rkind) * (1._rkind + xtemp**n)**(-m - 2._rkind) ) +end function d2Theta_dTk2 end module soil_utilsAddSundials_module From c9b83cebd2dde03fbe2e55b708373a3ba022c537 Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 27 Sep 2022 18:47:32 +0000 Subject: [PATCH 0364/1472] adjusted indentation --- build/source/engine/tol4IDA.f90 | 452 ++++++++++++++++---------------- 1 file changed, 224 insertions(+), 228 deletions(-) diff --git a/build/source/engine/tol4IDA.f90 b/build/source/engine/tol4IDA.f90 index 4674c31ef..392557256 100644 --- a/build/source/engine/tol4IDA.f90 +++ b/build/source/engine/tol4IDA.f90 @@ -1,13 +1,9 @@ - - - module tol4IDA_module - - !======= Inclusions =========== - use, intrinsic :: iso_c_binding - use nrtype - use type4IDA +!======= Inclusions =========== +use, intrinsic :: iso_c_binding +use nrtype +use type4IDA ! missing values USE globalData,only:integerMissing ! missing integer @@ -62,230 +58,230 @@ module tol4IDA_module USE var_lookup,only:iLookINDEX ! named variables for structure elements - ! privacy - implicit none - private - public::computWeight4IDA - public::popTol4IDA +! privacy +implicit none +private +public::computWeight4IDA +public::popTol4IDA contains - ! ********************************************************************************************************** - ! public function computWeight4IDA: compute w_i = 1 / ( rtol_i * y_i + atol_i ) - ! ********************************************************************************************************** - ! Return values: - ! 0 = success, - ! -1 = non-recoverable error, NaN or negative values - ! ---------------------------------------------------------------- - integer(c_int) function computWeight4IDA(sunvec_y, sunvec_ewt, user_data) & - result(ierr) bind(C,name='computWeight4IDA') - - !======= Inclusions =========== - use, intrinsic :: iso_c_binding - use fsundials_nvector_mod - use fnvector_serial_mod - use nrtype - use type4IDA - - !======= Declarations ========= - implicit none - - ! calling variables - type(N_Vector) :: sunvec_y ! solution N_Vector y - type(N_Vector) :: sunvec_ewt ! derivative N_Vector W - type(c_ptr), value :: user_data ! user-defined data - - - ! pointers to data in SUNDIALS vectors - type(eqnsData), pointer :: tol_data ! equations data - real(rkind), pointer :: stateVec(:) - real(rkind), pointer :: weightVec(:) - integer(c_int) :: iState - - !======= Internals ============ - - ! get equations data from user-defined data - call c_f_pointer(user_data, tol_data) - - - ! get data arrays from SUNDIALS vectors - stateVec(1:tol_data%nState) => FN_VGetArrayPointer(sunvec_y) - weightVec(1:tol_data%nState) => FN_VGetArrayPointer(sunvec_ewt) - - - do iState = 1,tol_data%nState - weightVec(iState) = tol_data%rtol(iState) * abs( stateVec(iState) ) + tol_data%atol(iState) - weightVec(iState) = 1._rkind / weightVec(iState) - end do - - ierr = 0 - return - - end function computWeight4IDA - - - ! ********************************************************************************************************** - ! public subroutine popTol4IDA: populate tolerances for state vectors - ! ********************************************************************************************************** - subroutine popTol4IDA(& - ! input: data structures - nState, & ! intent(in): number of desired state variables - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - mpar_data, & ! intent(in) - ! output - absTol, & ! intent(out): model state vector - relTol, & - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - ! input: data structures - integer(i4b),intent(in) :: nState ! number of desired state variables - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(in) :: mpar_data ! model parameters - ! output - real(rkind),intent(out) :: absTol(:) ! model state vector (mixed units) - real(rkind),intent(out) :: relTol(:) ! model state vector (mixed units) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - ! -------------------------------------------------------------------------------------------------------------------------------- - ! state subsets - integer(i4b) :: iState ! index of state within the snow+soil domain - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - integer(i4b) :: ixStateSubset ! index within the state subset - logical(lgt),dimension(nState) :: tolFlag ! flag to denote that the state is populated - real(rkind) :: absTolTempCas = 1e-6 - real(rkind) :: relTolTempCas = 1e-6 - real(rkind) :: absTolTempVeg = 1e-6 - real(rkind) :: relTolTempVeg = 1e-6 - real(rkind) :: absTolWatVeg = 1e-6 - real(rkind) :: relTolWatVeg = 1e-6 - real(rkind) :: absTolTempSoilSnow = 1e-6 - real(rkind) :: relTolTempSoilSnow = 1e-6 - real(rkind) :: absTolWatSnow = 1e-6 - real(rkind) :: relTolWatSnow = 1e-6 - real(rkind) :: absTolMatric = 1e-6 - real(rkind) :: relTolMatric = 1e-6 - real(rkind) :: absTolAquifr = 1e-6 - real(rkind) :: relTolAquifr = 1e-6 - - - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - fixedLength: associate(& - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in) : [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in) : [dp] temperature of the vegetation canopy (K) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in) : [dp] mass of total water on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in) : [dp] mass of liquid water on the vegetation canopy (kg m-2) - ! model state variable vectors for the snow-soil layers - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in) : [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in) : [dp(:)] volumetric fraction of total water (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in) : [dp(:)] volumetric fraction of liquid water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in) : [dp(:)] matric head (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in) : [dp(:)] matric potential of liquid water (m) - ! model state variables for the aquifer - scalarAquiferStorage=> prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in) : [dp] storage of water in the aquifer (m) - ! indices defining specific model states - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy hydrology state variable (mass) - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of aquifer storage state variable - ! vector of energy and hydrology indices for the snow and soil domains - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for energy state variables in the snow+soil domain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in) : [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in) : [i4b] number of hydrology state variables in the snow+soil domain - ! type of model state variabless - ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in) : [i4b(:)] [state subset] type of desired model state variables - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in) : [i4b(:)] index of the type of hydrology states in snow+soil domain - ! number of layers - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in) : [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in) : [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in) : [i4b] total number of layers - ) ! end association with variables in the data structures - ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='popTol4IDA/' - - ! ----- - ! * initialize state vectors... - ! ----------------------------- - - ! initialize flags - tolFlag(:) = .false. - - ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer canopy - do concurrent (iState=1:size(ixCasNrg),ixCasNrg(iState)/=integerMissing) - absTol( ixCasNrg(iState) ) = absTolTempCas ! transfer canopy air temperature to the state vector - relTol( ixCasNrg(iState) ) = relTolTempCas - tolFlag( ixCasNrg(iState) ) = .true. ! flag to denote that tolerances are populated - end do - - ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer canopy - do concurrent (iState=1:size(ixVegNrg),ixVegNrg(iState)/=integerMissing) - absTol( ixVegNrg(iState) ) = absTolTempVeg ! transfer vegetation temperature to the state vector - relTol( ixVegNrg(iState) ) = relTolTempVeg ! transfer vegetation temperature to the state vector - tolFlag( ixVegNrg(iState) ) = .true. ! flag to denote that tolerances are populated - end do - - ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer canopy - do concurrent (iState=1:size(ixVegHyd),ixVegHyd(iState)/=integerMissing) - tolFlag( ixVegHyd(iState) ) = .true. ! flag to denote that tolerances are populated - select case(ixStateType_subset( ixVegHyd(iState) )) - case(iname_watCanopy); absTol( ixVegHyd(iState) ) = absTolWatVeg ; relTol( ixVegHyd(iState) ) = relTolWatVeg - case(iname_liqCanopy); absTol( ixVegHyd(iState) ) = absTolWatVeg ; relTol( ixVegHyd(iState) ) = relTolWatVeg ! transfer liquid canopy water to the state vector - case default; tolFlag( ixVegHyd(iState) ) = .false. ! flag to denote that tolerances are populated - end select - end do - - ! tolerance for tempreture of the snow and soil domain - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - ixStateSubset = ixSnowSoilNrg(iLayer) ! index within the state vector - absTol(ixStateSubset) = absTolTempSoilSnow ! transfer temperature from a layer to the state vector - relTol(ixStateSubset) = relTolTempSoilSnow - tolFlag(ixStateSubset) = .true. ! flag to denote that tolerances are populated - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! NOTE: ixVolFracWat and ixVolFracLiq can also include states in the soil domain, hence enable primary variable switching - if(nSnowSoilHyd>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) - ixStateSubset = ixSnowSoilHyd(iLayer) ! index within the state vector - tolFlag(ixStateSubset) = .true. ! flag to denote that tolerances are populated - select case( ixHydType(iLayer) ) - case(iname_watLayer); absTol(ixStateSubset) = absTolWatSnow ; relTol(ixStateSubset) = relTolWatSnow - case(iname_liqLayer); absTol(ixStateSubset) = absTolWatSnow ; relTol(ixStateSubset) = relTolWatSnow - case(iname_matLayer); absTol(ixStateSubset) = absTolMatric ; relTol(ixStateSubset) = relTolMatric - case(iname_lmpLayer); absTol(ixStateSubset) = absTolMatric ; relTol(ixStateSubset) = relTolMatric - case default; tolFlag(ixStateSubset) = .false. ! flag to denote that tolerances are populated - end select - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! build the state vector for the aquifer storage - ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer aquifer - do concurrent (iState=1:size(ixAqWat),ixAqWat(iState)/=integerMissing) - absTol( ixAqWat(iState) ) = absTolAquifr ! transfer aquifer storage to the state vector - relTol( ixAqWat(iState) ) = relTolAquifr - tolFlag( ixAqWat(iState) ) = .true. ! flag to denote that tolerances are populated - end do - - ! check that we specified tolerances for all state variables - if(count(tolFlag)/=nState)then - print*, 'tolFlag = ', tolFlag - message=trim(message)//'tolerances not specified for some state variables' - err=20; return - endif - - end associate fixedLength ! end association to variables in the data structure where vector length does not change - end subroutine popTol4IDA +! ********************************************************************************************************** +! public function computWeight4IDA: compute w_i = 1 / ( rtol_i * y_i + atol_i ) +! ********************************************************************************************************** +! Return values: +! 0 = success, +! -1 = non-recoverable error, NaN or negative values +! ---------------------------------------------------------------- +integer(c_int) function computWeight4IDA(sunvec_y, sunvec_ewt, user_data) & + result(ierr) bind(C,name='computWeight4IDA') + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use fsundials_nvector_mod + use fnvector_serial_mod + use nrtype + use type4IDA + + !======= Declarations ========= + implicit none + + ! calling variables + type(N_Vector) :: sunvec_y ! solution N_Vector y + type(N_Vector) :: sunvec_ewt ! derivative N_Vector W + type(c_ptr), value :: user_data ! user-defined data + + + ! pointers to data in SUNDIALS vectors + type(eqnsData), pointer :: tol_data ! equations data + real(rkind), pointer :: stateVec(:) + real(rkind), pointer :: weightVec(:) + integer(c_int) :: iState + + !======= Internals ============ + + ! get equations data from user-defined data + call c_f_pointer(user_data, tol_data) + + + ! get data arrays from SUNDIALS vectors + stateVec(1:tol_data%nState) => FN_VGetArrayPointer(sunvec_y) + weightVec(1:tol_data%nState) => FN_VGetArrayPointer(sunvec_ewt) + + + do iState = 1,tol_data%nState + weightVec(iState) = tol_data%rtol(iState) * abs( stateVec(iState) ) + tol_data%atol(iState) + weightVec(iState) = 1._rkind / weightVec(iState) + end do + + ierr = 0 + return + +end function computWeight4IDA + + +! ********************************************************************************************************** +! public subroutine popTol4IDA: populate tolerances for state vectors +! ********************************************************************************************************** +subroutine popTol4IDA(& + ! input: data structures + nState, & ! intent(in): number of desired state variables + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + mpar_data, & ! intent(in) + ! output + absTol, & ! intent(out): model state vector + relTol, & + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- + ! input: data structures + integer(i4b),intent(in) :: nState ! number of desired state variables + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(in) :: mpar_data ! model parameters + ! output + real(rkind),intent(out) :: absTol(:) ! model state vector (mixed units) + real(rkind),intent(out) :: relTol(:) ! model state vector (mixed units) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + ! -------------------------------------------------------------------------------------------------------------------------------- + ! state subsets + integer(i4b) :: iState ! index of state within the snow+soil domain + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + integer(i4b) :: ixStateSubset ! index within the state subset + logical(lgt),dimension(nState) :: tolFlag ! flag to denote that the state is populated + real(rkind) :: absTolTempCas = 1e-6 + real(rkind) :: relTolTempCas = 1e-6 + real(rkind) :: absTolTempVeg = 1e-6 + real(rkind) :: relTolTempVeg = 1e-6 + real(rkind) :: absTolWatVeg = 1e-6 + real(rkind) :: relTolWatVeg = 1e-6 + real(rkind) :: absTolTempSoilSnow = 1e-6 + real(rkind) :: relTolTempSoilSnow = 1e-6 + real(rkind) :: absTolWatSnow = 1e-6 + real(rkind) :: relTolWatSnow = 1e-6 + real(rkind) :: absTolMatric = 1e-6 + real(rkind) :: relTolMatric = 1e-6 + real(rkind) :: absTolAquifr = 1e-6 + real(rkind) :: relTolAquifr = 1e-6 + + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + fixedLength: associate(& + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in) : [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in) : [dp] temperature of the vegetation canopy (K) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in) : [dp] mass of total water on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in) : [dp] mass of liquid water on the vegetation canopy (kg m-2) + ! model state variable vectors for the snow-soil layers + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in) : [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in) : [dp(:)] volumetric fraction of total water (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in) : [dp(:)] volumetric fraction of liquid water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in) : [dp(:)] matric head (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in) : [dp(:)] matric potential of liquid water (m) + ! model state variables for the aquifer + scalarAquiferStorage=> prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in) : [dp] storage of water in the aquifer (m) + ! indices defining specific model states + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy hydrology state variable (mass) + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of aquifer storage state variable + ! vector of energy and hydrology indices for the snow and soil domains + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in) : [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in) : [i4b] number of hydrology state variables in the snow+soil domain + ! type of model state variabless + ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in) : [i4b(:)] [state subset] type of desired model state variables + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in) : [i4b(:)] index of the type of hydrology states in snow+soil domain + ! number of layers + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in) : [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in) : [i4b] number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in) : [i4b] total number of layers + ) ! end association with variables in the data structures + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='popTol4IDA/' + + ! ----- + ! * initialize state vectors... + ! ----------------------------- + + ! initialize flags + tolFlag(:) = .false. + + ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer canopy + do concurrent (iState=1:size(ixCasNrg),ixCasNrg(iState)/=integerMissing) + absTol( ixCasNrg(iState) ) = absTolTempCas ! transfer canopy air temperature to the state vector + relTol( ixCasNrg(iState) ) = relTolTempCas + tolFlag( ixCasNrg(iState) ) = .true. ! flag to denote that tolerances are populated + end do + + ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer canopy + do concurrent (iState=1:size(ixVegNrg),ixVegNrg(iState)/=integerMissing) + absTol( ixVegNrg(iState) ) = absTolTempVeg ! transfer vegetation temperature to the state vector + relTol( ixVegNrg(iState) ) = relTolTempVeg ! transfer vegetation temperature to the state vector + tolFlag( ixVegNrg(iState) ) = .true. ! flag to denote that tolerances are populated + end do + + ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer canopy + do concurrent (iState=1:size(ixVegHyd),ixVegHyd(iState)/=integerMissing) + tolFlag( ixVegHyd(iState) ) = .true. ! flag to denote that tolerances are populated + select case(ixStateType_subset( ixVegHyd(iState) )) + case(iname_watCanopy); absTol( ixVegHyd(iState) ) = absTolWatVeg ; relTol( ixVegHyd(iState) ) = relTolWatVeg + case(iname_liqCanopy); absTol( ixVegHyd(iState) ) = absTolWatVeg ; relTol( ixVegHyd(iState) ) = relTolWatVeg ! transfer liquid canopy water to the state vector + case default; tolFlag( ixVegHyd(iState) ) = .false. ! flag to denote that tolerances are populated + end select + end do + + ! tolerance for tempreture of the snow and soil domain + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + ixStateSubset = ixSnowSoilNrg(iLayer) ! index within the state vector + absTol(ixStateSubset) = absTolTempSoilSnow ! transfer temperature from a layer to the state vector + relTol(ixStateSubset) = relTolTempSoilSnow + tolFlag(ixStateSubset) = .true. ! flag to denote that tolerances are populated + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! NOTE: ixVolFracWat and ixVolFracLiq can also include states in the soil domain, hence enable primary variable switching + if(nSnowSoilHyd>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) + ixStateSubset = ixSnowSoilHyd(iLayer) ! index within the state vector + tolFlag(ixStateSubset) = .true. ! flag to denote that tolerances are populated + select case( ixHydType(iLayer) ) + case(iname_watLayer); absTol(ixStateSubset) = absTolWatSnow ; relTol(ixStateSubset) = relTolWatSnow + case(iname_liqLayer); absTol(ixStateSubset) = absTolWatSnow ; relTol(ixStateSubset) = relTolWatSnow + case(iname_matLayer); absTol(ixStateSubset) = absTolMatric ; relTol(ixStateSubset) = relTolMatric + case(iname_lmpLayer); absTol(ixStateSubset) = absTolMatric ; relTol(ixStateSubset) = relTolMatric + case default; tolFlag(ixStateSubset) = .false. ! flag to denote that tolerances are populated + end select + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! build the state vector for the aquifer storage + ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer aquifer + do concurrent (iState=1:size(ixAqWat),ixAqWat(iState)/=integerMissing) + absTol( ixAqWat(iState) ) = absTolAquifr ! transfer aquifer storage to the state vector + relTol( ixAqWat(iState) ) = relTolAquifr + tolFlag( ixAqWat(iState) ) = .true. ! flag to denote that tolerances are populated + end do + + ! check that we specified tolerances for all state variables + if(count(tolFlag)/=nState)then + print*, 'tolFlag = ', tolFlag + message=trim(message)//'tolerances not specified for some state variables' + err=20; return + endif + + end associate fixedLength ! end association to variables in the data structure where vector length does not change +end subroutine popTol4IDA end module tol4IDA_module From 0d8fd87a2aeccf2aff96554cc20bd478e1a528fb Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 27 Sep 2022 18:50:01 +0000 Subject: [PATCH 0365/1472] fixed indentation --- build/source/engine/type4IDA.f90 | 118 +++++++++++++++---------------- 1 file changed, 58 insertions(+), 60 deletions(-) diff --git a/build/source/engine/type4IDA.f90 b/build/source/engine/type4IDA.f90 index 7251a2912..7b0af42f6 100644 --- a/build/source/engine/type4IDA.f90 +++ b/build/source/engine/type4IDA.f90 @@ -1,5 +1,3 @@ - - module type4IDA ! data types @@ -16,64 +14,64 @@ module type4IDA implicit none - type eqnsData - type(c_ptr) :: ida_mem ! IDA memory - real(rkind) :: dt ! time step - integer(i4b) :: nSnow ! number of snow layers - integer(i4b) :: nSoil ! number of soil layers - integer(i4b) :: nLayers ! total number of layers - integer :: nState ! total number of state variables - integer(i4b) :: ixMatrix ! form of matrix (dense or banded) - logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt) :: firstFluxCall - logical(lgt) :: firstSplitOper - logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution - type(zLookup) :: lookup_data ! lookup tables - type(var_i) :: type_data ! type of vegetation and soil - type(var_d) :: attr_data ! spatial attributes - type(var_dlength) :: mpar_data ! model parameters - type(var_d) :: forc_data ! model forcing data - type(var_dlength) :: bvar_data ! model variables for the local basin - type(var_dlength) :: prog_data ! prognostic variables for a local HRU - type(var_ilength) :: indx_data ! indices defining model states and layers - type(var_dlength) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength) :: flux_data ! model fluxes for a local HRU - type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(rkind) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind) :: scalarCanopyTempPrev ! previous value of canopy temperature (K) - real(rkind) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - real(rkind) :: scalarCanopyIcePrev ! value of canopy ice content (kg m-2) at previous step - real(rkind) :: scalarCanopyLiqTrial ! trial value of canopy ice content (kg m-2) - real(rkind) :: scalarCanopyLiqPrev ! value of canopy ice content (kg m-2) at previous step - real(rkind) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) - real(rkind) :: scalarCanopyEnthalpyPrev ! previous enthalpy of the vegetation canopy (J m-3) - real(qp), allocatable :: sMul(:) ! state vector multiplier (used in the residual calculations) - real(rkind), allocatable :: dMat(:) ! diagonal of the Jacobian matrix - real(rkind), allocatable :: fluxVec(:) ! flux vector - real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation - real(rkind), allocatable :: atol(:) ! vector of absolute tolerances - real(rkind), allocatable :: rtol(:) ! vector of relative tolerances - real(rkind), allocatable :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) - real(rkind), allocatable :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind), allocatable :: mLayerMatricHeadPrev(:) ! vector of total water matric potential (m) at previous step - real(rkind), allocatable :: mLayerVolFracWatTrial(:) ! trial value for volumetric fraction of total water (-) - real(rkind), allocatable :: mLayerVolFracWatPrev(:) ! value for volumetric fraction of total water (-) at previous step - real(rkind), allocatable :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) - real(rkind), allocatable :: mLayerVolFracIcePrev(:) ! value for volumetric fraction of ice (-) at previous step - real(rkind), allocatable :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) - real(rkind), allocatable :: mLayerVolFracLiqPrev(:) ! value for volumetric fraction of liquid water (-) at previous step - real(rkind) :: scalarAquiferStoragePrev - real(rkind) :: scalarAquiferStorageTrial - real(rkind), allocatable :: mLayerEnthalpyTrial(:) ! trial enthalpy of snow and soil (J m-3) - real(rkind), allocatable :: mLayerEnthalpyPrev(:) ! enthalpy of snow and soil (J m-3) at previous step - real(rkind), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind), allocatable :: mLayerTempPrev(:) ! vector of layer temperature (K) at previous step - real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - integer :: ixSaturation - integer(i4b) :: err ! error code - character(len = 50) :: message ! error message - end type eqnsData +type eqnsData + type(c_ptr) :: ida_mem ! IDA memory + real(rkind) :: dt ! time step + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers + integer(i4b) :: nLayers ! total number of layers + integer :: nState ! total number of state variables + integer(i4b) :: ixMatrix ! form of matrix (dense or banded) + logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt) :: firstFluxCall + logical(lgt) :: firstSplitOper + logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution + type(zLookup) :: lookup_data ! lookup tables + type(var_i) :: type_data ! type of vegetation and soil + type(var_d) :: attr_data ! spatial attributes + type(var_dlength) :: mpar_data ! model parameters + type(var_d) :: forc_data ! model forcing data + type(var_dlength) :: bvar_data ! model variables for the local basin + type(var_dlength) :: prog_data ! prognostic variables for a local HRU + type(var_ilength) :: indx_data ! indices defining model states and layers + type(var_dlength) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength) :: flux_data ! model fluxes for a local HRU + type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + real(rkind) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind) :: scalarCanopyTempPrev ! previous value of canopy temperature (K) + real(rkind) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + real(rkind) :: scalarCanopyIcePrev ! value of canopy ice content (kg m-2) at previous step + real(rkind) :: scalarCanopyLiqTrial ! trial value of canopy ice content (kg m-2) + real(rkind) :: scalarCanopyLiqPrev ! value of canopy ice content (kg m-2) at previous step + real(rkind) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) + real(rkind) :: scalarCanopyEnthalpyPrev ! previous enthalpy of the vegetation canopy (J m-3) + real(qp), allocatable :: sMul(:) ! state vector multiplier (used in the residual calculations) + real(rkind), allocatable :: dMat(:) ! diagonal of the Jacobian matrix + real(rkind), allocatable :: fluxVec(:) ! flux vector + real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation + real(rkind), allocatable :: atol(:) ! vector of absolute tolerances + real(rkind), allocatable :: rtol(:) ! vector of relative tolerances + real(rkind), allocatable :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) + real(rkind), allocatable :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind), allocatable :: mLayerMatricHeadPrev(:) ! vector of total water matric potential (m) at previous step + real(rkind), allocatable :: mLayerVolFracWatTrial(:) ! trial value for volumetric fraction of total water (-) + real(rkind), allocatable :: mLayerVolFracWatPrev(:) ! value for volumetric fraction of total water (-) at previous step + real(rkind), allocatable :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) + real(rkind), allocatable :: mLayerVolFracIcePrev(:) ! value for volumetric fraction of ice (-) at previous step + real(rkind), allocatable :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) + real(rkind), allocatable :: mLayerVolFracLiqPrev(:) ! value for volumetric fraction of liquid water (-) at previous step + real(rkind) :: scalarAquiferStoragePrev + real(rkind) :: scalarAquiferStorageTrial + real(rkind), allocatable :: mLayerEnthalpyTrial(:) ! trial enthalpy of snow and soil (J m-3) + real(rkind), allocatable :: mLayerEnthalpyPrev(:) ! enthalpy of snow and soil (J m-3) at previous step + real(rkind), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind), allocatable :: mLayerTempPrev(:) ! vector of layer temperature (K) at previous step + real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + integer :: ixSaturation + integer(i4b) :: err ! error code + character(len = 50) :: message ! error message +end type eqnsData end module type4IDA From 35ccd31317681b337b060ac55829e0d799eb64a5 Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 27 Sep 2022 19:01:45 +0000 Subject: [PATCH 0366/1472] fixed intentation --- build/source/engine/updateVarsSundials.f90 | 1479 ++++++++++---------- 1 file changed, 740 insertions(+), 739 deletions(-) diff --git a/build/source/engine/updateVarsSundials.f90 b/build/source/engine/updateVarsSundials.f90 index 3e4d5cd0d..29d496ae0 100644 --- a/build/source/engine/updateVarsSundials.f90 +++ b/build/source/engine/updateVarsSundials.f90 @@ -103,745 +103,746 @@ module updateVarsSundials_module contains - ! ********************************************************************************************************** - ! public subroutine updateVarsSundials: compute diagnostic variables and derivatives for Sundials Jacobian - ! ********************************************************************************************************** - subroutine updateVarsSundials(& - ! input - dt, & ! intent(in): time step - computJac, & ! intent(in): logical flag if computing Jacobian for Sundials solve - do_adjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - mLayerVolFracWatPrev, & ! intent(in): previous vector of total water matric potential (m) - mLayerMatricHeadPrev, & ! intent(in): previous vector of volumetric total water content (-) - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - scalarCanopyTempPrime, & ! intent(inout): trial value of time derivative canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): trial value of time derivative canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): trial value of time derivative canopy liquid water (kg m-2) - scalarCanopyIcePrime, & ! intent(inout): trial value of time derivative canopy ice content (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! intent(inout): trial value of time derivative layer temperature (K) - mLayerVolFracWatPrime, & ! intent(inout): trial value of time derivative volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): trial value of time derivative volumetric liquid water content (-) - mLayerVolFracIcePrime, & ! intent(inout): trial value of time derivative volumetric ice water content (-) - mLayerMatricHeadPrime, & ! intent(inout): trial value of time derivative total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): trial value of time derivative liquid water matric potential (m) - ! output: error control - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input - real(rkind) ,intent(in) :: dt ! time step - logical(lgt) ,intent(in) :: computJac ! flag if computing Jacobian for Sundials solver - logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze - type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - real(rkind),intent(in) :: mLayerVolFracWatPrev(:) ! previous vector of total water matric potential (m) - real(rkind),intent(in) :: mLayerMatricHeadPrev(:) ! previous vector of volumetric total water content (-) - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! output: variables for the vegetation canopy - real(rkind),intent(inout) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - real(rkind),intent(inout) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) - real(rkind),intent(inout) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - real(rkind),intent(inout) :: scalarCanopyTempPrime ! trial value of time derivative canopy temperature (K) - real(rkind),intent(inout) :: scalarCanopyWatPrime ! trial value of time derivative canopy total water (kg m-2) - real(rkind),intent(inout) :: scalarCanopyLiqPrime ! trial value of time derivative canopy liquid water (kg m-2) - real(rkind),intent(inout) :: scalarCanopyIcePrime ! trial value of time derivative canopy ice content (kg m-2) - ! output: variables for the snow-soil domain - real(rkind),intent(inout) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(inout) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(rkind),intent(inout) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(inout) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric ice water content (-) - real(rkind),intent(inout) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) - real(rkind),intent(inout) :: mLayerTempPrime(:) ! trial value of time derivative layer temperature (K) - real(rkind),intent(inout) :: mLayerVolFracWatPrime(:) ! trial value of time derivative volumetric total water content (-) - real(rkind),intent(inout) :: mLayerVolFracLiqPrime(:) ! trial value of time derivative volumetric liquid water content (-) - real(rkind),intent(inout) :: mLayerVolFracIcePrime(:) ! trial value of time derivative volumetric ice water content (-) - real(rkind),intent(inout) :: mLayerMatricHeadPrime(:) ! trial value of time derivative total water matric potential (m) - real(rkind),intent(inout) :: mLayerMatricHeadLiqPrime(:) ! trial value of time derivative liquid water matric potential (m) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! general local variables - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixDomainType ! name of a given model domain - integer(i4b) :: ixControlIndex ! index within a given model domain - integer(i4b) :: ixOther,ixOtherLocal ! index of the coupled state variable within the (full, local) vector - logical(lgt) :: isCoupled ! .true. if a given variable shared another state variable in the same control volume - logical(lgt) :: isNrgState ! .true. if a given variable is an energy state - logical(lgt),allocatable :: computedCoupling(:) ! .true. if computed the coupling for a given state variable - real(rkind) :: scalarVolFracLiq ! volumetric fraction of liquid water (-) - real(rkind) :: scalarVolFracIce ! volumetric fraction of ice (-) - real(rkind) :: scalarVolFracLiqPrime ! time derivative volumetric fraction of liquid water (-) - real(rkind) :: scalarVolFracIcePrime ! time derivative volumetric fraction of ice (-) - real(rkind) :: Tcrit ! critical soil temperature below which ice exists (K) - real(rkind) :: xTemp ! temporary temperature (K) - real(rkind) :: fLiq ! fraction of liquid water (-) - real(rkind) :: effSat ! effective saturation (-) - real(rkind) :: avPore ! available pore space (-) - character(len=256) :: cMessage ! error message of downwind routine - logical(lgt),parameter :: printFlag=.false. ! flag to turn on printing - ! iterative solution for temperature - real(rkind) :: meltNrg ! energy for melt+freeze (J m-3) - real(rkind) :: residual ! residual in the energy equation (J m-3) - real(rkind) :: derivative ! derivative in the energy equation (J m-3 K-1) - real(rkind) :: tempInc ! iteration increment (K) - integer(i4b) :: iter ! iteration index - integer(i4b) :: niter ! number of iterations - integer(i4b),parameter :: maxiter=100 ! maximum number of iterations - real(rkind),parameter :: nrgConvTol=1.e-4_rkind ! convergence tolerance for energy (J m-3) - real(rkind),parameter :: tempConvTol=1.e-6_rkind ! convergence tolerance for temperature (K) - real(rkind) :: critDiff ! temperature difference from critical (K) - real(rkind) :: tempMin ! minimum bracket for temperature (K) - real(rkind) :: tempMax ! maximum bracket for temperature (K) - logical(lgt) :: bFlag ! flag to denote that iteration increment was constrained using bi-section - real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical temperature (K) - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - associate(& - ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! indices defining model states and layers - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ! indices in the full vector for specific domains - ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain - ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain - ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain - ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain - ! mapping between the full state vector and the state subset - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset - ! type of domain, type of state variable, and index of control volume within domain - ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ! depth-varying model parameters - vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) - ! model diagnostic variables (heat capacity) - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(in): [dp ] volumetric heat capacity of the vegetation (J m-3 K-1) - mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] volumetric heat capacity in each layer (J m-3 K-1) - ! model diagnostic variables (fraction of liquid water) - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) - ! model states for the vegetation canopy - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variable vectors for the snow-soil layers - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] total water matric potential (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) - ! model diagnostic variables from a previous solution - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - ! derivatives - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential - dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) - dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(out): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature - dFracLiqSnow_dTk => deriv_data%var(iLookDERIV%dFracLiqSnow_dTk )%dat ,& ! intent(out): [dp(:)] derivative in fraction of liquid snow w.r.t. temperature - dFracLiqVeg_dTkCanopy => deriv_data%var(iLookDERIV%dFracLiqVeg_dTkCanopy )%dat(1) ,& ! intent(out): [dp ] derivative in fraction of (throughfall + drainage) w.r.t. temperature - ! derivatives inside solver for Jacobian only - dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. temperature - d2VolTot_d2Psi0 => deriv_data%var(iLookDERIV%d2VolTot_d2Psi0 )%dat ,& ! intent(out): [dp(:)] second derivative in total water content w.r.t. total water matric potential - mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2 )%dat ,& ! intent(out): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature - d2Theta_dTkCanopy2 => deriv_data%var(iLookDERIV%d2Theta_dTkCanopy2 )%dat(1) & ! intent(out): [dp ] second derivative of volumetric liquid water content w.r.t. temperature -) ! association with variables in the data structures - - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - - ! initialize error control - err=0; message='updateVarsSundials/' - - ! allocate space and assign values to the flag vector - allocate(computedCoupling(size(ixMapSubset2Full)),stat=err) ! .true. if computed the coupling for a given state variable - if(err/=0)then; message=trim(message)//'problem allocating computedCoupling'; return; endif - computedCoupling(:)=.false. - - ! loop through model state variables - do iState=1,size(ixMapSubset2Full) - - ! check the need for the computations - if(computedCoupling(iState)) cycle - - ! ----- - ! - compute indices... - ! -------------------- - - ! get domain type, and index of the control volume within the domain - ixFullVector = ixMapSubset2Full(iState) ! index within full state vector - ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) - ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain - - ! get the layer index - select case(ixDomainType) - case(iname_cas); cycle ! canopy air space: do nothing - case(iname_veg); iLayer = 0 - case(iname_snow); iLayer = ixControlIndex - case(iname_soil); iLayer = ixControlIndex + nSnow - case(iname_aquifer); cycle ! aquifer: do nothing - case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return - end select - - ! get the index of the other (energy or mass) state variable within the full state vector - select case(ixDomainType) - case(iname_veg) ; ixOther = merge(ixHydCanopy(1), ixNrgCanopy(1), ixStateType(ixFullVector)==iname_nrgCanopy) - case(iname_snow, iname_soil); ixOther = merge(ixHydLayer(iLayer),ixNrgLayer(iLayer),ixStateType(ixFullVector)==iname_nrgLayer) - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - end select - - ! get the index in the local state vector - ixOtherLocal = ixMapFull2Subset(ixOther) ! ixOtherLocal could equal integerMissing - if(ixOtherLocal/=integerMissing) computedCoupling(ixOtherLocal)=.true. - - ! check if we have a coupled solution - isCoupled = (ixOtherLocal/=integerMissing) - - ! check if we are an energy state - isNrgState = (ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer) - - if(printFlag)then - print*, 'iState = ', iState, size(ixMapSubset2Full) - print*, 'ixFullVector = ', ixFullVector - print*, 'ixDomainType = ', ixDomainType - print*, 'ixControlIndex = ', ixControlIndex - print*, 'ixOther = ', ixOther - print*, 'ixOtherLocal = ', ixOtherLocal - print*, 'do_adjustTemp = ', do_adjustTemp - print*, 'isCoupled = ', isCoupled - print*, 'isNrgState = ', isNrgState - endif - - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - - ! update hydrology state variables for the uncoupled solution - if(.not.isNrgState .and. .not.isCoupled)then - - if(.not.computJac) stop 1 ! this does not work yet? FIX - - ! update the total water from volumetric liquid water - if(ixStateType(ixFullVector)==iname_liqCanopy .or. ixStateType(ixFullVector)==iname_liqLayer)then - select case(ixDomainType) - case(iname_veg) - scalarCanopyWatTrial = scalarCanopyLiqTrial + scalarCanopyIceTrial - scalarCanopyWatPrime = scalarCanopyLiqPrime + scalarCanopyIcePrime - case(iname_snow) - mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer)*iden_ice/iden_water - mLayerVolFracWatPrime(iLayer) = mLayerVolFracLiqPrime(iLayer) + mLayerVolFracIcePrime(iLayer)*iden_ice/iden_water - case(iname_soil) - mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer) ! no volume expansion - mLayerVolFracWatPrime(iLayer) = mLayerVolFracLiqPrime(iLayer) + mLayerVolFracIcePrime(iLayer) - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, or iname_soil'; return - end select - endif - - ! update the total water and the total water matric potential - if(ixDomainType==iname_soil)then - select case( ixStateType(ixFullVector) ) - ! --> update the total water from the liquid water matric potential - case(iname_lmpLayer) - - effSat = volFracLiq(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex)) ! effective saturation - avPore = theta_sat(ixControlIndex) - mLayerVolFracIceTrial(iLayer) - theta_res(ixControlIndex) ! available pore space - mLayerVolFracLiqTrial(iLayer) = effSat*avPore + theta_res(ixControlIndex) - mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer) ! no volume expansion - mLayerVolFracWatPrime(iLayer) = mLayerVolFracLiqPrime(iLayer) + mLayerVolFracIcePrime(iLayer) - mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - mLayerMatricHeadPrime(ixControlIndex) = dPsi_dTheta(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) * mLayerVolFracWatPrime(iLayer) - !write(*,'(a,1x,i4,1x,3(f20.10,1x))') 'mLayerVolFracLiqTrial(iLayer) 1 = ', iLayer, mLayerVolFracLiqTrial(iLayer), mLayerVolFracIceTrial(iLayer), mLayerVolFracWatTrial(iLayer) - ! --> update the total water from the total water matric potential - case(iname_matLayer) - - mLayerVolFracWatTrial(iLayer) = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - mLayerVolFracWatPrime(iLayer) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) *mLayerMatricHeadPrime(ixControlIndex) - ! --> update the total water matric potential (assume already have mLayerVolFracWatTrial given block above) - case(iname_liqLayer, iname_watLayer) - - mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - mLayerMatricHeadPrime(ixControlIndex) = dPsi_dTheta(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) * mLayerVolFracWatPrime(iLayer) - case default; err=20; message=trim(message)//'expect iname_lmpLayer, iname_matLayer, iname_liqLayer, or iname_watLayer'; return - end select - endif ! if in the soil domain - - endif ! if hydrology state variable or uncoupled solution - - ! compute the critical soil temperature below which ice exists - select case(ixDomainType) - case(iname_veg, iname_snow); Tcrit = Tfreeze - case(iname_soil); Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - end select - - ! initialize temperature - select case(ixDomainType) - case(iname_veg); xTemp = scalarCanopyTempTrial - case(iname_snow, iname_soil); xTemp = mLayerTempTrial(iLayer) - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - end select - - ! define brackets for the root - ! NOTE: start with an enormous range; updated quickly in the iterations - tempMin = xTemp - 10._rkind - tempMax = xTemp + 10._rkind - - ! get iterations (set to maximum iterations if adjusting the temperature) - niter = merge(maxiter, 1, do_adjustTemp) - - ! iterate - iterations: do iter=1,niter - - ! restrict temperature - if(xTemp <= tempMin .or. xTemp >= tempMax)then - xTemp = 0.5_rkind*(tempMin + tempMax) ! new value - bFlag = .true. - else - bFlag = .false. - endif - - ! ----- - ! - compute derivatives... - ! ------------------------ - - ! compute the derivative in bulk heat capacity w.r.t. total water content or water matric potential (m-1) - ! compute the derivative in total water content w.r.t. total water matric potential (m-1) - ! NOTE 1: valid for frozen and unfrozen conditions - ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi, dVolHtCapBulk_dPsi0 may be wrong - select case(ixDomainType) - case(iname_veg) - if(computJac)then - fLiq = fracLiquid(xTemp,snowfrz_scale) - dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) - endif - case(iname_snow) - if(computJac)then - fLiq = fracLiquid(xTemp,snowfrz_scale) - dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air - endif - case(iname_soil) - select case( ixStateType(ixFullVector) ) - case(iname_lmpLayer) - dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore - if(computJac) d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore - case default - dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - if(computJac) d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),& - vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - end select - ! dVolHtCapBulk_dPsi0 uses the derivative in water retention curve above critical temp w.r.t.state variable dVolTot_dPsi0 - if(computJac)then - dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use - if(xTemp< Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) - if(xTemp>=Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_water * Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) - endif - end select - - ! compute the derivative in liquid water content w.r.t. temperature - ! --> partially frozen: dependence of liquid water on temperature - ! compute the derivative in bulk heat capacity w.r.t. temperature - if(xTemp indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! indices defining model states and layers + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ! indices in the full vector for specific domains + ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain + ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + ! mapping between the full state vector and the state subset + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ! type of domain, type of state variable, and index of control volume within domain + ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ! depth-varying model parameters + vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) + ! model diagnostic variables (heat capacity) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(in): [dp ] volumetric heat capacity of the vegetation (J m-3 K-1) + mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] volumetric heat capacity in each layer (J m-3 K-1) + ! model diagnostic variables (fraction of liquid water) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) + ! model states for the vegetation canopy + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variable vectors for the snow-soil layers + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] total water matric potential (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) + ! model diagnostic variables from a previous solution + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + ! derivatives + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential + dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) + dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(out): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature + dFracLiqSnow_dTk => deriv_data%var(iLookDERIV%dFracLiqSnow_dTk )%dat ,& ! intent(out): [dp(:)] derivative in fraction of liquid snow w.r.t. temperature + dFracLiqVeg_dTkCanopy => deriv_data%var(iLookDERIV%dFracLiqVeg_dTkCanopy )%dat(1) ,& ! intent(out): [dp ] derivative in fraction of (throughfall + drainage) w.r.t. temperature + ! derivatives inside solver for Jacobian only + dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. temperature + d2VolTot_d2Psi0 => deriv_data%var(iLookDERIV%d2VolTot_d2Psi0 )%dat ,& ! intent(out): [dp(:)] second derivative in total water content w.r.t. total water matric potential + mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2 )%dat ,& ! intent(out): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature + d2Theta_dTkCanopy2 => deriv_data%var(iLookDERIV%d2Theta_dTkCanopy2 )%dat(1) & ! intent(out): [dp ] second derivative of volumetric liquid water content w.r.t. temperature + ) ! association with variables in the data structures + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message='updateVarsSundials/' + + ! allocate space and assign values to the flag vector + allocate(computedCoupling(size(ixMapSubset2Full)),stat=err) ! .true. if computed the coupling for a given state variable + if(err/=0)then; message=trim(message)//'problem allocating computedCoupling'; return; endif + computedCoupling(:)=.false. + + ! loop through model state variables + do iState=1,size(ixMapSubset2Full) + + ! check the need for the computations + if(computedCoupling(iState)) cycle + + ! ----- + ! - compute indices... + ! -------------------- + + ! get domain type, and index of the control volume within the domain + ixFullVector = ixMapSubset2Full(iState) ! index within full state vector + ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) + ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain + + ! get the layer index + select case(ixDomainType) + case(iname_cas); cycle ! canopy air space: do nothing + case(iname_veg); iLayer = 0 + case(iname_snow); iLayer = ixControlIndex + case(iname_soil); iLayer = ixControlIndex + nSnow + case(iname_aquifer); cycle ! aquifer: do nothing + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + + ! get the index of the other (energy or mass) state variable within the full state vector + select case(ixDomainType) + case(iname_veg) ; ixOther = merge(ixHydCanopy(1), ixNrgCanopy(1), ixStateType(ixFullVector)==iname_nrgCanopy) + case(iname_snow, iname_soil); ixOther = merge(ixHydLayer(iLayer),ixNrgLayer(iLayer),ixStateType(ixFullVector)==iname_nrgLayer) + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select + + ! get the index in the local state vector + ixOtherLocal = ixMapFull2Subset(ixOther) ! ixOtherLocal could equal integerMissing + if(ixOtherLocal/=integerMissing) computedCoupling(ixOtherLocal)=.true. + + ! check if we have a coupled solution + isCoupled = (ixOtherLocal/=integerMissing) + + ! check if we are an energy state + isNrgState = (ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer) + + if(printFlag)then + print*, 'iState = ', iState, size(ixMapSubset2Full) + print*, 'ixFullVector = ', ixFullVector + print*, 'ixDomainType = ', ixDomainType + print*, 'ixControlIndex = ', ixControlIndex + print*, 'ixOther = ', ixOther + print*, 'ixOtherLocal = ', ixOtherLocal + print*, 'do_adjustTemp = ', do_adjustTemp + print*, 'isCoupled = ', isCoupled + print*, 'isNrgState = ', isNrgState endif - case(iname_snow) - dFracLiqSnow_dTk(iLayer) = dFracLiq_dTk(xTemp,snowfrz_scale) - mLayerdTheta_dTk(iLayer) = dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatTrial(iLayer) - if(computJac)then - fLiq = fracLiquid(xTemp,snowfrz_scale) - mLayerd2Theta_dTk2(iLayer) = 2._rkind * snowfrz_scale**2._rkind * ( (Tfreeze - xTemp) * 2._rkind * fLiq * dFracLiqSnow_dTk(iLayer) - fLiq**2._rkind ) * mLayerVolFracWatTrial(iLayer) - dVolHtCapBulk_dTk(iLayer) = ( iden_water * (-Cp_ice + Cp_water) + iden_air * (iden_water/iden_ice - 1._rkind) * Cp_air ) * mLayerdTheta_dTk(iLayer) - endif - case(iname_soil) - dFracLiqSnow_dTk(iLayer) = 0._rkind !dTheta_dTk(xTemp,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex))/ mLayerVolFracWatTrial(iLayer) - mLayerdTheta_dTk(iLayer) = dTheta_dTk(xTemp,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - if(computJac)then - mLayerd2Theta_dTk2(iLayer) = d2Theta_dTk2(xTemp,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - dVolHtCapBulk_dTk(iLayer) = (-iden_ice * Cp_ice + iden_water * Cp_water) * mLayerdTheta_dTk(iLayer) - endif - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - end select ! domain type - - ! --> unfrozen: no dependence of liquid water on temperature - else - select case(ixDomainType) - case(iname_veg); dTheta_dTkCanopy = 0._rkind; d2Theta_dTkCanopy2 = 0._rkind; dFracLiqVeg_dTkCanopy = 0._rkind; dVolHtCapBulk_dTkCanopy = 0._rkind - case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind; mLayerd2Theta_dTk2(iLayer) = 0._rkind; dFracLiqSnow_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind - end select ! domain type - endif - - ! ----- - ! - update volumetric fraction of liquid water and ice... - ! => case of hydrology state uncoupled with energy (and when not adjusting the temperature)... - ! ----------------------------------------------------------------------------------------------- - - ! case of hydrology state uncoupled with energy (and when not adjusting the temperature) - if(.not.do_adjustTemp .and. .not.isNrgState .and. .not.isCoupled)then - - ! compute the fraction of snow - select case(ixDomainType) - case(iname_veg); scalarFracLiqVeg = fracliquid(xTemp,snowfrz_scale) - case(iname_snow); mLayerFracLiqSnow(iLayer) = fracliquid(xTemp,snowfrz_scale) - case(iname_soil) ! do nothing - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - end select ! domain type - - ! ----- - ! - update volumetric fraction of liquid water and ice... - ! => case of energy state or coupled solution (or adjusting the temperature)... - ! -------------------------------------------------------------------------------- - - ! case of energy state OR coupled solution (or adjusting the temperature) - elseif(do_adjustTemp .or. ( (isNrgState .or. isCoupled) ) )then - - ! identify domain type - select case(ixDomainType) - - ! *** vegetation canopy - case(iname_veg) - - ! compute volumetric fraction of liquid water and ice - call updateSnowSundials(& - xTemp, & ! intent(in) : temperature (K) - scalarCanopyWatTrial/(iden_water*canopyDepth),& ! intent(in) : volumetric fraction of total water (-) - snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) - scalarCanopyTempPrime, & ! intent(in) : canopy temperature time derivative (K/s) - scalarCanopyWatPrime/(iden_water*canopyDepth),& ! intent(in) : volumetric fraction of total water time derivative (-) - scalarVolFracLiq, & ! intent(out) : trial canopy liquid water (-) - scalarVolFracIce, & ! intent(out) : trial volumetric canopy ice (-) - scalarVolFracLiqPrime, & ! intent(out) : trial volumetric canopy liquid water (-) - scalarVolFracIcePrime, & ! intent(out) : trial volumetric canopy ice (-) - scalarFracLiqVeg, & ! intent(out) : fraction of liquid water (-) - err,cmessage) ! intent(out) : error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! compute mass of water on the canopy - ! NOTE: possibilities for speed-up here - scalarCanopyLiqTrial = scalarFracLiqVeg *scalarCanopyWatTrial !(kg m-2), scalarVolFracLiq*iden_water*canopyDepth - scalarCanopyLiqPrime = scalarVolFracLiqPrime*iden_water*canopyDepth - scalarCanopyIceTrial = (1._rkind - scalarFracLiqVeg)*scalarCanopyWatTrial !(kg m-2), scalarVolFracIce* iden_ice *canopyDepth - scalarCanopyIcePrime = scalarVolFracIcePrime* iden_ice *canopyDepth - - ! *** snow layers - case(iname_snow) - - ! compute volumetric fraction of liquid water and ice - call updateSnowSundials(& - xTemp, & ! intent(in) : temperature (K) - mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) - snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) - mLayerTempPrime(iLayer), & ! intent(in) : temperature time derivative (K/s) - mLayerVolFracWatPrime(iLayer), & ! intent(in) : volumetric fraction of total water time derivative (-) - mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) - mLayerVolFracLiqPrime(iLayer), & ! intent(out) : volumetric fraction of liquid water time derivative (-) - mLayerVolFracIcePrime(iLayer), & ! intent(out) : volumetric fraction of ice time derivative (-) - mLayerFracLiqSnow(iLayer), & ! intent(out) : fraction of liquid water (-) - err,cmessage) ! intent(out) : error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! *** soil layers - case(iname_soil) - - ! compute volumetric fraction of liquid water and ice - call updateSoilSundials(& - dt, & ! intent(in) : time step - computJac, & ! intent(in) : logical flag if inside Sundials solver - xTemp, & ! intent(in) : temperature (K) - mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) - mLayerMatricHeadPrev(ixControlIndex), & ! intent(in) : previous values, will be same as current if computJac - mLayerVolFracWatPrev(iLayer), & ! intent(in) : previous values, will be same as current if computJac - mLayerTempPrime(iLayer), & ! intent(in) : temperature time derivative (K/s) - mLayerMatricHeadPrime(ixControlIndex), & ! intent(in) : total water matric potential time derivative (m/s) - vGn_alpha(ixControlIndex), & ! intent(in) : van Genutchen "alpha" parameter - vGn_n(ixControlIndex), & ! intent(in) : van Genutchen "n" parameter - theta_sat(ixControlIndex), & ! intent(in) : soil porosity (-) - theta_res(ixControlIndex), & ! intent(in) : soil residual volumetric water content (-) - vGn_m(ixControlIndex), & ! intent(in) : van Genutchen "m" parameter (-) - mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) - mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) - mLayerVolFracWatPrime(iLayer), & ! intent(out) : volumetric fraction of total water time derivative (-) - mLayerVolFracLiqPrime(iLayer), & ! intent(out) : volumetric fraction of liquid water time derivative (-) - mLayerVolFracIcePrime(iLayer), & ! intent(out) : volumetric fraction of ice time derivative (-) - err,cmessage) ! intent(out) : error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! check - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - - end select ! domain type - - ! final check - else - - ! do nothing (input = output) -- and check that we got here correctly - if( (isNrgState .or. isCoupled) )then - scalarVolFracLiq = realMissing - scalarVolFracIce = realMissing - else - message=trim(message)//'unexpected else branch' - err=20; return - endif - - endif ! if energy state or solution is coupled - - ! ----- - ! ------------------------ - - ! check the need to adjust temperature (will always be false if inside solver) - ! can be true if inside varSubstepSundials, outside solver, but currently will not work so turn off - if(do_adjustTemp .and. computJac)then - - ! get the melt energy - meltNrg = merge(LH_fus*iden_ice, LH_fus*iden_water, ixDomainType==iname_snow) - - ! compute the residual and the derivative - select case(ixDomainType) - - ! * vegetation - case(iname_veg) - call xTempSolve(& - ! constant over iterations - meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) - heatCap = scalarBulkVolHeatCapVeg ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) - tempInit = scalarCanopyTemp ,& ! intent(in) : initial temperature (K) - volFracIceInit = scalarCanopyIce/(iden_water*canopyDepth),& ! intent(in) : initial volumetric fraction of ice (-) - ! trial values - xTemp = xTemp ,& ! intent(inout) : trial value of temperature - dLiq_dT = dTheta_dTkCanopy ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) - volFracIceTrial = scalarVolFracIce ,& ! intent(in) : trial value for volumetric fraction of ice - ! residual and derivative - residual = residual ,& ! intent(out) : residual (J m-3) - derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) - - ! * snow and soil - case(iname_snow, iname_soil) - call xTempSolve(& - ! constant over iterations - meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) - heatCap = mLayerVolHtCapBulk(iLayer) ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) - tempInit = mLayerTemp(iLayer) ,& ! intent(in) : initial temperature (K) - volFracIceInit = mLayerVolFracIce(iLayer) ,& ! intent(in) : initial volumetric fraction of ice (-) - ! trial values - xTemp = xTemp ,& ! intent(inout) : trial value of temperature - dLiq_dT = mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) - volFracIceTrial = mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : trial value for volumetric fraction of ice - ! residual and derivative - residual = residual ,& ! intent(out) : residual (J m-3) - derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) - - ! * check - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - - end select ! domain type - - ! check validity of residual - if( ieee_is_nan(residual) )then - message=trim(message)//'residual is not valid' - err=20; return - endif - - ! update bracket - if(residual < 0._rkind)then - tempMax = min(xTemp,tempMax) - else - tempMin = max(tempMin,xTemp) - end if - - ! compute iteration increment - tempInc = residual/derivative ! K - - ! check - if(globalPrintFlag)& - write(*,'(i4,1x,e20.10,1x,5(f20.10,1x),L1)') iter, residual, xTemp-Tcrit, tempInc, Tcrit, tempMin, tempMax, bFlag - - ! check convergence - if(abs(residual) < nrgConvTol .or. abs(tempInc) < tempConvTol) exit iterations - - ! add constraints for snow temperature - if(ixDomainType==iname_veg .or. ixDomainType==iname_snow)then - if(tempInc > Tcrit - xTemp) tempInc=(Tcrit - xTemp)*0.5_rkind ! simple bi-section method - endif ! if the domain is vegetation or snow - - ! deal with the discontinuity between partially frozen and unfrozen soil - if(ixDomainType==iname_soil)then - ! difference from the temperature below which ice exists - critDiff = Tcrit - xTemp - ! --> initially frozen (T < Tcrit) - if(critDiff > 0._rkind)then - if(tempInc > critDiff) tempInc = critDiff + epsT ! set iteration increment to slightly above critical temperature - ! --> initially unfrozen (T > Tcrit) - else - if(tempInc < critDiff) tempInc = critDiff - epsT ! set iteration increment to slightly below critical temperature - endif - endif ! if the domain is soil - - ! update the temperature trial - xTemp = xTemp + tempInc - - ! check failed convergence - if(iter==maxiter)then - message=trim(message)//'failed to converge' - err=-20; return ! negative error code = try to recover - endif - - endif ! if adjusting the temperature - - end do iterations ! iterating - - ! save temperature - select case(ixDomainType) - case(iname_veg); scalarCanopyTempTrial = xTemp - case(iname_snow, iname_soil); mLayerTempTrial(iLayer) = xTemp - end select - - ! ======================================================================================================================================= - ! ======================================================================================================================================= - - ! ----- - ! - compute the liquid water matric potential (and necessay derivatives)... - ! ------------------------------------------------------------------------- - - ! only for soil - if(ixDomainType==iname_soil)then - - ! check liquid water - if(mLayerVolFracLiqTrial(iLayer) > theta_sat(ixControlIndex) )then - message=trim(message)//'liquid water greater than porosity' - err=20; return - endif - - ! case of hydrology state uncoupled with energy - if(.not.isNrgState .and. .not.isCoupled)then - - ! derivatives relating liquid water matric potential to total water matric potential and temperature - dPsiLiq_dPsi0(ixControlIndex) = 1._rkind ! exact correspondence (psiLiq=psi0) - dPsiLiq_dTemp(ixControlIndex) = 0._rkind ! no relationship between liquid water matric potential and temperature - - ! case of energy state or coupled solution - else - ! compute the liquid matric potential (and the derivatives w.r.t. total matric potential and temperature) - call liquidHeadSundials(& - ! input - mLayerMatricHeadTrial(ixControlIndex) ,& ! intent(in) : total water matric potential (m) - mLayerMatricHeadPrime(ixControlIndex) ,& ! - mLayerVolFracLiqTrial(iLayer) ,& ! intent(in) : volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : volumetric fraction of ice (-) - vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in) : soil parameters - dVolTot_dPsi0(ixControlIndex) ,& ! intent(in) : derivative in the soil water characteristic (m-1) - mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) - mLayerTempPrime(ixControlIndex) ,& - mLayerVolFracLiqPrime(iLayer) ,& - mLayerVolFracIcePrime(iLayer) ,& - ! output - mLayerMatricHeadLiqTrial(ixControlIndex) ,& ! intent(out): liquid water matric potential (m) - mLayerMatricHeadLiqPrime(ixControlIndex) ,& ! - dPsiLiq_dPsi0(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. the total water matric potential (-) - dPsiLiq_dTemp(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. temperature (m K-1) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - endif ! switch between hydrology and energy state - - endif ! if domain is soil - - end do ! looping through state variables - - ! deallocate space - deallocate(computedCoupling,stat=err) ! .true. if computed the coupling for a given state variable - if(err/=0)then; message=trim(message)//'problem deallocating computedCoupling'; return; endif - - ! end association to the variables in the data structures - end associate - - end subroutine updateVarsSundials - - - ! ********************************************************************************************************** - ! private subroutine xTempSolve: compute residual and derivative for temperature - ! ********************************************************************************************************** - subroutine xTempSolve(& - ! input: constant over iterations - meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) - heatCap ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) - tempInit ,& ! intent(in) : initial temperature (K) - volFracIceInit ,& ! intent(in) : initial volumetric fraction of ice (-) - ! input-output: trial values - xTemp ,& ! intent(inout) : trial value of temperature - dLiq_dT ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) - volFracIceTrial ,& ! intent(in) : trial value for volumetric fraction of ice - ! output: residual and derivative - residual ,& ! intent(out) : residual (J m-3) - derivative ) ! intent(out) : derivative (J m-3 K-1) - implicit none - ! input: constant over iterations - real(rkind),intent(in) :: meltNrg ! energy for melt+freeze (J m-3) - real(rkind),intent(in) :: heatCap ! volumetric heat capacity (J m-3 K-1) - real(rkind),intent(in) :: tempInit ! initial temperature (K) - real(rkind),intent(in) :: volFracIceInit ! initial volumetric fraction of ice (-) - ! input-output: trial values - real(rkind),intent(inout) :: xTemp ! trial value for temperature - real(rkind),intent(in) :: dLiq_dT ! derivative in liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: volFracIceTrial ! trial value for the volumetric fraction of ice (-) - ! output: residual and derivative - real(rkind),intent(out) :: residual ! residual (J m-3) - real(rkind),intent(out) :: derivative ! derivative (J m-3 K-1) - ! subroutine starts here - residual = -heatCap*(xTemp - tempInit) + meltNrg*(volFracIceTrial - volFracIceInit) ! J m-3 - derivative = heatCap + LH_fus*iden_water*dLiq_dT ! J m-3 K-1 - end subroutine xTempSolve + + ! ======================================================================================================================================= + ! ======================================================================================================================================= + ! ======================================================================================================================================= + ! ======================================================================================================================================= + ! ======================================================================================================================================= + ! ======================================================================================================================================= + + ! update hydrology state variables for the uncoupled solution + if(.not.isNrgState .and. .not.isCoupled)then + + if(.not.computJac) stop 1 ! this does not work yet? FIX + + ! update the total water from volumetric liquid water + if(ixStateType(ixFullVector)==iname_liqCanopy .or. ixStateType(ixFullVector)==iname_liqLayer)then + select case(ixDomainType) + case(iname_veg) + scalarCanopyWatTrial = scalarCanopyLiqTrial + scalarCanopyIceTrial + scalarCanopyWatPrime = scalarCanopyLiqPrime + scalarCanopyIcePrime + case(iname_snow) + mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer)*iden_ice/iden_water + mLayerVolFracWatPrime(iLayer) = mLayerVolFracLiqPrime(iLayer) + mLayerVolFracIcePrime(iLayer)*iden_ice/iden_water + case(iname_soil) + mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer) ! no volume expansion + mLayerVolFracWatPrime(iLayer) = mLayerVolFracLiqPrime(iLayer) + mLayerVolFracIcePrime(iLayer) + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, or iname_soil'; return + end select + endif + + ! update the total water and the total water matric potential + if(ixDomainType==iname_soil)then + select case( ixStateType(ixFullVector) ) + ! --> update the total water from the liquid water matric potential + case(iname_lmpLayer) + + effSat = volFracLiq(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex)) ! effective saturation + avPore = theta_sat(ixControlIndex) - mLayerVolFracIceTrial(iLayer) - theta_res(ixControlIndex) ! available pore space + mLayerVolFracLiqTrial(iLayer) = effSat*avPore + theta_res(ixControlIndex) + mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer) ! no volume expansion + mLayerVolFracWatPrime(iLayer) = mLayerVolFracLiqPrime(iLayer) + mLayerVolFracIcePrime(iLayer) + mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + mLayerMatricHeadPrime(ixControlIndex) = dPsi_dTheta(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) * mLayerVolFracWatPrime(iLayer) + ! --> update the total water from the total water matric potential + case(iname_matLayer) + + mLayerVolFracWatTrial(iLayer) = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + mLayerVolFracWatPrime(iLayer) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) *mLayerMatricHeadPrime(ixControlIndex) + ! --> update the total water matric potential (assume already have mLayerVolFracWatTrial given block above) + case(iname_liqLayer, iname_watLayer) + + mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + mLayerMatricHeadPrime(ixControlIndex) = dPsi_dTheta(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) * mLayerVolFracWatPrime(iLayer) + case default; err=20; message=trim(message)//'expect iname_lmpLayer, iname_matLayer, iname_liqLayer, or iname_watLayer'; return + end select + endif ! if in the soil domain + + endif ! if hydrology state variable or uncoupled solution + + ! compute the critical soil temperature below which ice exists + select case(ixDomainType) + case(iname_veg, iname_snow); Tcrit = Tfreeze + case(iname_soil); Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select + + ! initialize temperature + select case(ixDomainType) + case(iname_veg); xTemp = scalarCanopyTempTrial + case(iname_snow, iname_soil); xTemp = mLayerTempTrial(iLayer) + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select + + ! define brackets for the root + ! NOTE: start with an enormous range; updated quickly in the iterations + tempMin = xTemp - 10._rkind + tempMax = xTemp + 10._rkind + + ! get iterations (set to maximum iterations if adjusting the temperature) + niter = merge(maxiter, 1, do_adjustTemp) + + ! iterate + iterations: do iter=1,niter + + ! restrict temperature + if(xTemp <= tempMin .or. xTemp >= tempMax)then + xTemp = 0.5_rkind*(tempMin + tempMax) ! new value + bFlag = .true. + else + bFlag = .false. + endif + + ! ----- + ! - compute derivatives... + ! ------------------------ + + ! compute the derivative in bulk heat capacity w.r.t. total water content or water matric potential (m-1) + ! compute the derivative in total water content w.r.t. total water matric potential (m-1) + ! NOTE 1: valid for frozen and unfrozen conditions + ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi, dVolHtCapBulk_dPsi0 may be wrong + select case(ixDomainType) + case(iname_veg) + if(computJac)then + fLiq = fracLiquid(xTemp,snowfrz_scale) + dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) + endif + + case(iname_snow) + + if(computJac)then + fLiq = fracLiquid(xTemp,snowfrz_scale) + dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air + endif + case(iname_soil) + select case( ixStateType(ixFullVector) ) + case(iname_lmpLayer) + dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + if(computJac) d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + case default + dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + if(computJac) d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),& + vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + end select + ! dVolHtCapBulk_dPsi0 uses the derivative in water retention curve above critical temp w.r.t.state variable dVolTot_dPsi0 + if(computJac)then + dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use + if(xTemp< Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) + if(xTemp>=Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_water * Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) + endif + end select + + ! compute the derivative in liquid water content w.r.t. temperature + ! --> partially frozen: dependence of liquid water on temperature + ! compute the derivative in bulk heat capacity w.r.t. temperature + if(xTemp unfrozen: no dependence of liquid water on temperature + else + select case(ixDomainType) + case(iname_veg); dTheta_dTkCanopy = 0._rkind; d2Theta_dTkCanopy2 = 0._rkind; dFracLiqVeg_dTkCanopy = 0._rkind; dVolHtCapBulk_dTkCanopy = 0._rkind + case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind; mLayerd2Theta_dTk2(iLayer) = 0._rkind; dFracLiqSnow_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind + end select ! domain type + endif + + ! ----- + ! - update volumetric fraction of liquid water and ice... + ! => case of hydrology state uncoupled with energy (and when not adjusting the temperature)... + ! ----------------------------------------------------------------------------------------------- + + ! case of hydrology state uncoupled with energy (and when not adjusting the temperature) + if(.not.do_adjustTemp .and. .not.isNrgState .and. .not.isCoupled)then + + ! compute the fraction of snow + select case(ixDomainType) + case(iname_veg); scalarFracLiqVeg = fracliquid(xTemp,snowfrz_scale) + case(iname_snow); mLayerFracLiqSnow(iLayer) = fracliquid(xTemp,snowfrz_scale) + case(iname_soil) ! do nothing + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select ! domain type + + ! ----- + ! - update volumetric fraction of liquid water and ice... + ! => case of energy state or coupled solution (or adjusting the temperature)... + ! -------------------------------------------------------------------------------- + + ! case of energy state OR coupled solution (or adjusting the temperature) + elseif(do_adjustTemp .or. ( (isNrgState .or. isCoupled) ) )then + + ! identify domain type + select case(ixDomainType) + + ! *** vegetation canopy + case(iname_veg) + + ! compute volumetric fraction of liquid water and ice + call updateSnowSundials(& + xTemp, & ! intent(in) : temperature (K) + scalarCanopyWatTrial/(iden_water*canopyDepth),& ! intent(in) : volumetric fraction of total water (-) + snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) + scalarCanopyTempPrime, & ! intent(in) : canopy temperature time derivative (K/s) + scalarCanopyWatPrime/(iden_water*canopyDepth),& ! intent(in) : volumetric fraction of total water time derivative (-) + scalarVolFracLiq, & ! intent(out) : trial canopy liquid water (-) + scalarVolFracIce, & ! intent(out) : trial volumetric canopy ice (-) + scalarVolFracLiqPrime, & ! intent(out) : trial volumetric canopy liquid water (-) + scalarVolFracIcePrime, & ! intent(out) : trial volumetric canopy ice (-) + scalarFracLiqVeg, & ! intent(out) : fraction of liquid water (-) + err,cmessage) ! intent(out) : error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! compute mass of water on the canopy + ! NOTE: possibilities for speed-up here + scalarCanopyLiqTrial = scalarFracLiqVeg *scalarCanopyWatTrial !(kg m-2), scalarVolFracLiq*iden_water*canopyDepth + scalarCanopyLiqPrime = scalarVolFracLiqPrime*iden_water*canopyDepth + scalarCanopyIceTrial = (1._rkind - scalarFracLiqVeg)*scalarCanopyWatTrial !(kg m-2), scalarVolFracIce* iden_ice *canopyDepth + scalarCanopyIcePrime = scalarVolFracIcePrime* iden_ice *canopyDepth + + ! *** snow layers + case(iname_snow) + + ! compute volumetric fraction of liquid water and ice + call updateSnowSundials(& + xTemp, & ! intent(in) : temperature (K) + mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) + snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) + mLayerTempPrime(iLayer), & ! intent(in) : temperature time derivative (K/s) + mLayerVolFracWatPrime(iLayer), & ! intent(in) : volumetric fraction of total water time derivative (-) + mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) + mLayerVolFracLiqPrime(iLayer), & ! intent(out) : volumetric fraction of liquid water time derivative (-) + mLayerVolFracIcePrime(iLayer), & ! intent(out) : volumetric fraction of ice time derivative (-) + mLayerFracLiqSnow(iLayer), & ! intent(out) : fraction of liquid water (-) + err,cmessage) ! intent(out) : error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! *** soil layers + case(iname_soil) + + ! compute volumetric fraction of liquid water and ice + call updateSoilSundials(& + dt, & ! intent(in) : time step + computJac, & ! intent(in) : logical flag if inside Sundials solver + xTemp, & ! intent(in) : temperature (K) + mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) + mLayerMatricHeadPrev(ixControlIndex), & ! intent(in) : previous values, will be same as current if computJac + mLayerVolFracWatPrev(iLayer), & ! intent(in) : previous values, will be same as current if computJac + mLayerTempPrime(iLayer), & ! intent(in) : temperature time derivative (K/s) + mLayerMatricHeadPrime(ixControlIndex), & ! intent(in) : total water matric potential time derivative (m/s) + vGn_alpha(ixControlIndex), & ! intent(in) : van Genutchen "alpha" parameter + vGn_n(ixControlIndex), & ! intent(in) : van Genutchen "n" parameter + theta_sat(ixControlIndex), & ! intent(in) : soil porosity (-) + theta_res(ixControlIndex), & ! intent(in) : soil residual volumetric water content (-) + vGn_m(ixControlIndex), & ! intent(in) : van Genutchen "m" parameter (-) + mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) + mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) + mLayerVolFracWatPrime(iLayer), & ! intent(out) : volumetric fraction of total water time derivative (-) + mLayerVolFracLiqPrime(iLayer), & ! intent(out) : volumetric fraction of liquid water time derivative (-) + mLayerVolFracIcePrime(iLayer), & ! intent(out) : volumetric fraction of ice time derivative (-) + err,cmessage) ! intent(out) : error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! check + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + + end select ! domain type + + ! final check + else + + ! do nothing (input = output) -- and check that we got here correctly + if( (isNrgState .or. isCoupled) )then + scalarVolFracLiq = realMissing + scalarVolFracIce = realMissing + else + message=trim(message)//'unexpected else branch' + err=20; return + endif + + endif ! if energy state or solution is coupled + + ! ----- + ! ------------------------ + + ! check the need to adjust temperature (will always be false if inside solver) + ! can be true if inside varSubstepSundials, outside solver, but currently will not work so turn off + if(do_adjustTemp .and. computJac)then + + ! get the melt energy + meltNrg = merge(LH_fus*iden_ice, LH_fus*iden_water, ixDomainType==iname_snow) + + ! compute the residual and the derivative + select case(ixDomainType) + + ! * vegetation + case(iname_veg) + call xTempSolve(& + ! constant over iterations + meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) + heatCap = scalarBulkVolHeatCapVeg ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) + tempInit = scalarCanopyTemp ,& ! intent(in) : initial temperature (K) + volFracIceInit = scalarCanopyIce/(iden_water*canopyDepth),& ! intent(in) : initial volumetric fraction of ice (-) + ! trial values + xTemp = xTemp ,& ! intent(inout) : trial value of temperature + dLiq_dT = dTheta_dTkCanopy ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) + volFracIceTrial = scalarVolFracIce ,& ! intent(in) : trial value for volumetric fraction of ice + ! residual and derivative + residual = residual ,& ! intent(out) : residual (J m-3) + derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) + + ! * snow and soil + case(iname_snow, iname_soil) + call xTempSolve(& + ! constant over iterations + meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) + heatCap = mLayerVolHtCapBulk(iLayer) ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) + tempInit = mLayerTemp(iLayer) ,& ! intent(in) : initial temperature (K) + volFracIceInit = mLayerVolFracIce(iLayer) ,& ! intent(in) : initial volumetric fraction of ice (-) + ! trial values + xTemp = xTemp ,& ! intent(inout) : trial value of temperature + dLiq_dT = mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) + volFracIceTrial = mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : trial value for volumetric fraction of ice + ! residual and derivative + residual = residual ,& ! intent(out) : residual (J m-3) + derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) + + ! * check + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + + end select ! domain type + + ! check validity of residual + if( ieee_is_nan(residual) )then + message=trim(message)//'residual is not valid' + err=20; return + endif + + ! update bracket + if(residual < 0._rkind)then + tempMax = min(xTemp,tempMax) + else + tempMin = max(tempMin,xTemp) + end if + + ! compute iteration increment + tempInc = residual/derivative ! K + + ! check + if(globalPrintFlag)& + write(*,'(i4,1x,e20.10,1x,5(f20.10,1x),L1)') iter, residual, xTemp-Tcrit, tempInc, Tcrit, tempMin, tempMax, bFlag + + ! check convergence + if(abs(residual) < nrgConvTol .or. abs(tempInc) < tempConvTol) exit iterations + + ! add constraints for snow temperature + if(ixDomainType==iname_veg .or. ixDomainType==iname_snow)then + if(tempInc > Tcrit - xTemp) tempInc=(Tcrit - xTemp)*0.5_rkind ! simple bi-section method + endif ! if the domain is vegetation or snow + + ! deal with the discontinuity between partially frozen and unfrozen soil + if(ixDomainType==iname_soil)then + ! difference from the temperature below which ice exists + critDiff = Tcrit - xTemp + ! --> initially frozen (T < Tcrit) + if(critDiff > 0._rkind)then + if(tempInc > critDiff) tempInc = critDiff + epsT ! set iteration increment to slightly above critical temperature + ! --> initially unfrozen (T > Tcrit) + else + if(tempInc < critDiff) tempInc = critDiff - epsT ! set iteration increment to slightly below critical temperature + endif + endif ! if the domain is soil + + ! update the temperature trial + xTemp = xTemp + tempInc + + ! check failed convergence + if(iter==maxiter)then + message=trim(message)//'failed to converge' + err=-20; return ! negative error code = try to recover + endif + + endif ! if adjusting the temperature + + end do iterations ! iterating + + ! save temperature + select case(ixDomainType) + case(iname_veg); scalarCanopyTempTrial = xTemp + case(iname_snow, iname_soil); mLayerTempTrial(iLayer) = xTemp + end select + + ! ======================================================================================================================================= + ! ======================================================================================================================================= + + ! ----- + ! - compute the liquid water matric potential (and necessay derivatives)... + ! ------------------------------------------------------------------------- + + ! only for soil + if(ixDomainType==iname_soil)then + + ! check liquid water + if(mLayerVolFracLiqTrial(iLayer) > theta_sat(ixControlIndex) )then + message=trim(message)//'liquid water greater than porosity' + err=20; return + endif + + ! case of hydrology state uncoupled with energy + if(.not.isNrgState .and. .not.isCoupled)then + + ! derivatives relating liquid water matric potential to total water matric potential and temperature + dPsiLiq_dPsi0(ixControlIndex) = 1._rkind ! exact correspondence (psiLiq=psi0) + dPsiLiq_dTemp(ixControlIndex) = 0._rkind ! no relationship between liquid water matric potential and temperature + + ! case of energy state or coupled solution + else + ! compute the liquid matric potential (and the derivatives w.r.t. total matric potential and temperature) + call liquidHeadSundials(& + ! input + mLayerMatricHeadTrial(ixControlIndex) ,& ! intent(in) : total water matric potential (m) + mLayerMatricHeadPrime(ixControlIndex) ,& ! + mLayerVolFracLiqTrial(iLayer) ,& ! intent(in) : volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : volumetric fraction of ice (-) + vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in) : soil parameters + dVolTot_dPsi0(ixControlIndex) ,& ! intent(in) : derivative in the soil water characteristic (m-1) + mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) + mLayerTempPrime(ixControlIndex) ,& + mLayerVolFracLiqPrime(iLayer) ,& + mLayerVolFracIcePrime(iLayer) ,& + ! output + mLayerMatricHeadLiqTrial(ixControlIndex) ,& ! intent(out): liquid water matric potential (m) + mLayerMatricHeadLiqPrime(ixControlIndex) ,& ! + dPsiLiq_dPsi0(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. the total water matric potential (-) + dPsiLiq_dTemp(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. temperature (m K-1) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + endif ! switch between hydrology and energy state + + endif ! if domain is soil + + end do ! looping through state variables + + ! deallocate space + deallocate(computedCoupling,stat=err) ! .true. if computed the coupling for a given state variable + if(err/=0)then; message=trim(message)//'problem deallocating computedCoupling'; return; endif + + ! end association to the variables in the data structures +end associate + +end subroutine updateVarsSundials + + +! ********************************************************************************************************** +! private subroutine xTempSolve: compute residual and derivative for temperature +! ********************************************************************************************************** +subroutine xTempSolve(& + ! input: constant over iterations + meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) + heatCap ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) + tempInit ,& ! intent(in) : initial temperature (K) + volFracIceInit ,& ! intent(in) : initial volumetric fraction of ice (-) + ! input-output: trial values + xTemp ,& ! intent(inout) : trial value of temperature + dLiq_dT ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) + volFracIceTrial ,& ! intent(in) : trial value for volumetric fraction of ice + ! output: residual and derivative + residual ,& ! intent(out) : residual (J m-3) + derivative ) ! intent(out) : derivative (J m-3 K-1) + implicit none + ! input: constant over iterations + real(rkind),intent(in) :: meltNrg ! energy for melt+freeze (J m-3) + real(rkind),intent(in) :: heatCap ! volumetric heat capacity (J m-3 K-1) + real(rkind),intent(in) :: tempInit ! initial temperature (K) + real(rkind),intent(in) :: volFracIceInit ! initial volumetric fraction of ice (-) + ! input-output: trial values + real(rkind),intent(inout) :: xTemp ! trial value for temperature + real(rkind),intent(in) :: dLiq_dT ! derivative in liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: volFracIceTrial ! trial value for the volumetric fraction of ice (-) + ! output: residual and derivative + real(rkind),intent(out) :: residual ! residual (J m-3) + real(rkind),intent(out) :: derivative ! derivative (J m-3 K-1) + ! subroutine starts here + residual = -heatCap*(xTemp - tempInit) + meltNrg*(volFracIceTrial - volFracIceInit) ! J m-3 + derivative = heatCap + LH_fus*iden_water*dLiq_dT ! J m-3 K-1 +end subroutine xTempSolve end module updateVarsSundials_module From 6caa4ae9c1830d74206697db29771719140a89d8 Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 27 Sep 2022 19:03:48 +0000 Subject: [PATCH 0367/1472] fixed indentatation --- build/source/engine/updatStateSundials.f90 | 308 ++++++++++----------- 1 file changed, 153 insertions(+), 155 deletions(-) diff --git a/build/source/engine/updatStateSundials.f90 b/build/source/engine/updatStateSundials.f90 index 71e8c74ef..4f3738c78 100644 --- a/build/source/engine/updatStateSundials.f90 +++ b/build/source/engine/updatStateSundials.f90 @@ -1,4 +1,3 @@ - module updatStateSundials_module USE nrtype ! physical constants @@ -19,161 +18,160 @@ module updatStateSundials_module contains - ! ************************************************************************************************************* - ! public subroutine updateSnowSundials: compute phase change impacts on volumetric liquid water and ice - ! ************************************************************************************************************* - subroutine updateSnowSundials(& - ! input - mLayerTemp ,& ! intent(in): temperature (K) - mLayerTheta ,& ! intent(in): volume fraction of total water (-) - snowfrz_scale ,& ! intent(in): scaling parameter for the snow freezing curve (K-1) - mLayerTempPrime ,& ! intent(in): temperature (K) - mLayerThetaPrime ,& ! intent(in): volume fraction of total water (-) - ! output - mLayerVolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) - mLayerVolFracIce ,& ! intent(out): volumetric fraction of ice (-) - mLayerVolFracLiqPrime ,& ! intent(out): volumetric fraction of liquid water (-) - mLayerVolFracIcePrime ,& ! intent(out): volumetric fraction of ice (-) - fLiq ,& ! intent(out): fraction of liquid water (-) - err,message) ! intent(out): error control - ! utility routines - USE snow_utils_module,only:fracliquid ! compute volumetric fraction of liquid water - USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) - implicit none - ! input variables - real(rkind),intent(in) :: mLayerTemp ! temperature (K) - real(rkind),intent(in) :: mLayerTheta ! volume fraction of total water (-) - real(rkind),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) - real(rkind),intent(in) :: mLayerTempPrime ! temperature (K) - real(rkind),intent(in) :: mLayerThetaPrime ! volume fraction of total water (-) - ! output variables - real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) - real(rkind),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice (-) - real(rkind),intent(out) :: fLiq ! fraction of liquid water (-) - ! error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! initialize error control - err=0; message="updateSnowSundials/" - - ! compute the volumetric fraction of liquid water and ice (-) - fLiq = fracliquid(mLayerTemp,snowfrz_scale) - mLayerVolFracLiq = fLiq*mLayerTheta - mLayerVolFracIce = (1._rkind - fLiq)*mLayerTheta*(iden_water/iden_ice) - mLayerVolFracLiqPrime = fLiq * mLayerThetaPrime + dFracLiq_dTk(mLayerTemp,snowfrz_scale) * mLayerTheta * mLayerTempPrime - mLayerVolFracIcePrime = ( mLayerThetaPrime - mLayerVolFracLiqPrime ) * (iden_water/iden_ice) - - - end subroutine updateSnowSundials - - ! *********************************************************************************************************************************** - ! public subroutine updateSoilSundials: compute phase change impacts on matric head and volumetric liquid water and ice (veg or soil) - ! *********************************************************************************************************************************** - subroutine updateSoilSundials(& - ! input - dt ,& ! intent(in): time step - insideIDA ,& ! intent(in): flag if inside Sundials solver - mLayerTemp ,& ! intent(in): temperature (K) - mLayerMatricHead ,& ! intent(in): total water matric potential (m) - mLayerMatricHeadPrev ,& ! intent(in): total water matric potential previous time step (m) - mLayerVolFracWatPrev ,& ! intent(in): volumetric fraction of total water previous time step (-) - mLayerTempPrime ,& ! intent(in): temperature time derivative (K/s) - mLayerMatricHeadPrime, & ! intent(in): total water matric potential time derivative (m/s) - vGn_alpha ,& ! intent(in): van Genutchen "alpha" parameter - vGn_n ,& ! intent(in): van Genutchen "n" parameter - theta_sat ,& ! intent(in): soil porosity (-) - theta_res ,& ! intent(in): soil residual volumetric water content (-) - vGn_m ,& ! intent(in): van Genutchen "m" parameter (-) - ! output - mLayerVolFracWat ,& ! intent(out): volumetric fraction of total water (-) - mLayerVolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) - mLayerVolFracIce ,& ! intent(out): volumetric fraction of ice (-) - mLayerVolFracWatPrime ,& ! intent(out): volumetric fraction of total water time derivative (-) - mLayerVolFracLiqPrime ,& ! intent(out): volumetric fraction of liquid water time derivative (-) - mLayerVolFracIcePrime ,& ! intent(out): volumetric fraction of ice time derivative (-) - err,message) ! intent(out): error control - ! utility routines - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water based on matric head - USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric liquid water content - USE soil_utils_module,only:dTheta_dPsi - implicit none - ! input variables - real(rkind),intent(in) :: dt - logical(lgt),intent(in) :: insideIDA ! flag if inside Sundials solver - real(rkind),intent(in) :: mLayerTemp ! estimate of temperature (K) - real(rkind),intent(in) :: mLayerMatricHead ! matric head (m) - real(rkind),intent(in) :: mLayerMatricHeadPrev ! matric head previous time step (m) - real(rkind),intent(in) :: mLayerVolFracWatPrev ! volumetric fraction of total waterprevious time step (m) - real(rkind),intent(in) :: mLayerTempPrime ! temperature time derivative (K/s) - real(rkind),intent(in) :: mLayerMatricHeadPrime ! matric head time derivative (m/s) - real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter - real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter - real(rkind),intent(in) :: theta_sat ! soil porosity (-) - real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) - real(rkind),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) - ! output variables - real(rkind),intent(out) :: mLayerVolFracWat ! fractional volume of total water (-) - real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) - real(rkind),intent(out) :: mLayerVolFracWatPrime ! fractional volume of total water (-) - real(rkind),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice (-) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! define local variables - real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) - real(rkind) :: xConst ! constant in the freezing curve function (m K-1) - real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) - real(rkind) :: dt_inv ! inverse of timestep - ! initialize error control - err=0; message="updateSoilSundials/" - - ! compute fractional **volume** of total water (liquid plus ice) - mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if (.not.insideIDA)then ! calculate dt current, or use dt current as it can change here - if( abs(mLayerMatricHead - mLayerMatricHeadPrev) < verySmall )then !this difference is set as 0 inside varSubstep - dt_inv = 1._rkind/dt - else - dt_inv = mLayerMatricHeadPrime / (mLayerMatricHead - mLayerMatricHeadPrev) ! - endif - mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev)*dt_inv - else ! inside Sundials: instantaneous derivative will always be a full step size as input here - mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime - endif - - if(mLayerVolFracWat > theta_sat)then; err=20; message=trim(message)//'volume of liquid and ice exceeds porosity'; return; end if - - ! compute the critical soil temperature where all water is unfrozen (K) - ! (eq 17 in Dall'Amico 2011) - TcSoil = Tfreeze + min(mLayerMatricHead,0._rkind)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) - - ! *** compute volumetric fraction of liquid water for partially frozen soil - if(mLayerTemp < TcSoil)then ! (check if soil temperature is less than the critical temperature) - ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice - ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations - xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) - mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution - mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if(mLayerPsiLiq<0._rkind)then - mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime - else - mLayerVolFracLiqPrime = 0._rkind +! ************************************************************************************************************* +! public subroutine updateSnowSundials: compute phase change impacts on volumetric liquid water and ice +! ************************************************************************************************************* +subroutine updateSnowSundials(& + ! input + mLayerTemp ,& ! intent(in): temperature (K) + mLayerTheta ,& ! intent(in): volume fraction of total water (-) + snowfrz_scale ,& ! intent(in): scaling parameter for the snow freezing curve (K-1) + mLayerTempPrime ,& ! intent(in): temperature (K) + mLayerThetaPrime ,& ! intent(in): volume fraction of total water (-) + ! output + mLayerVolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) + mLayerVolFracIce ,& ! intent(out): volumetric fraction of ice (-) + mLayerVolFracLiqPrime ,& ! intent(out): volumetric fraction of liquid water (-) + mLayerVolFracIcePrime ,& ! intent(out): volumetric fraction of ice (-) + fLiq ,& ! intent(out): fraction of liquid water (-) + err,message) ! intent(out): error control + ! utility routines + USE snow_utils_module,only:fracliquid ! compute volumetric fraction of liquid water + USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) + implicit none + ! input variables + real(rkind),intent(in) :: mLayerTemp ! temperature (K) + real(rkind),intent(in) :: mLayerTheta ! volume fraction of total water (-) + real(rkind),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) + real(rkind),intent(in) :: mLayerTempPrime ! temperature (K) + real(rkind),intent(in) :: mLayerThetaPrime ! volume fraction of total water (-) + ! output variables + real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) + real(rkind),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice (-) + real(rkind),intent(out) :: fLiq ! fraction of liquid water (-) + ! error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! initialize error control + err=0; message="updateSnowSundials/" + + ! compute the volumetric fraction of liquid water and ice (-) + fLiq = fracliquid(mLayerTemp,snowfrz_scale) + mLayerVolFracLiq = fLiq*mLayerTheta + mLayerVolFracIce = (1._rkind - fLiq)*mLayerTheta*(iden_water/iden_ice) + mLayerVolFracLiqPrime = fLiq * mLayerThetaPrime + dFracLiq_dTk(mLayerTemp,snowfrz_scale) * mLayerTheta * mLayerTempPrime + mLayerVolFracIcePrime = ( mLayerThetaPrime - mLayerVolFracLiqPrime ) * (iden_water/iden_ice) + +end subroutine updateSnowSundials + +! *********************************************************************************************************************************** +! public subroutine updateSoilSundials: compute phase change impacts on matric head and volumetric liquid water and ice (veg or soil) +! *********************************************************************************************************************************** +subroutine updateSoilSundials(& + ! input + dt ,& ! intent(in): time step + insideIDA ,& ! intent(in): flag if inside Sundials solver + mLayerTemp ,& ! intent(in): temperature (K) + mLayerMatricHead ,& ! intent(in): total water matric potential (m) + mLayerMatricHeadPrev ,& ! intent(in): total water matric potential previous time step (m) + mLayerVolFracWatPrev ,& ! intent(in): volumetric fraction of total water previous time step (-) + mLayerTempPrime ,& ! intent(in): temperature time derivative (K/s) + mLayerMatricHeadPrime, & ! intent(in): total water matric potential time derivative (m/s) + vGn_alpha ,& ! intent(in): van Genutchen "alpha" parameter + vGn_n ,& ! intent(in): van Genutchen "n" parameter + theta_sat ,& ! intent(in): soil porosity (-) + theta_res ,& ! intent(in): soil residual volumetric water content (-) + vGn_m ,& ! intent(in): van Genutchen "m" parameter (-) + ! output + mLayerVolFracWat ,& ! intent(out): volumetric fraction of total water (-) + mLayerVolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) + mLayerVolFracIce ,& ! intent(out): volumetric fraction of ice (-) + mLayerVolFracWatPrime ,& ! intent(out): volumetric fraction of total water time derivative (-) + mLayerVolFracLiqPrime ,& ! intent(out): volumetric fraction of liquid water time derivative (-) + mLayerVolFracIcePrime ,& ! intent(out): volumetric fraction of ice time derivative (-) + err,message) ! intent(out): error control + ! utility routines + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water based on matric head + USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric liquid water content + USE soil_utils_module,only:dTheta_dPsi + implicit none + ! input variables + real(rkind),intent(in) :: dt + logical(lgt),intent(in) :: insideIDA ! flag if inside Sundials solver + real(rkind),intent(in) :: mLayerTemp ! estimate of temperature (K) + real(rkind),intent(in) :: mLayerMatricHead ! matric head (m) + real(rkind),intent(in) :: mLayerMatricHeadPrev ! matric head previous time step (m) + real(rkind),intent(in) :: mLayerVolFracWatPrev ! volumetric fraction of total waterprevious time step (m) + real(rkind),intent(in) :: mLayerTempPrime ! temperature time derivative (K/s) + real(rkind),intent(in) :: mLayerMatricHeadPrime ! matric head time derivative (m/s) + real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter + real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter + real(rkind),intent(in) :: theta_sat ! soil porosity (-) + real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) + real(rkind),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) + ! output variables + real(rkind),intent(out) :: mLayerVolFracWat ! fractional volume of total water (-) + real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) + real(rkind),intent(out) :: mLayerVolFracWatPrime ! fractional volume of total water (-) + real(rkind),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice (-) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! define local variables + real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) + real(rkind) :: xConst ! constant in the freezing curve function (m K-1) + real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) + real(rkind) :: dt_inv ! inverse of timestep + ! initialize error control + err=0; message="updateSoilSundials/" + + ! compute fractional **volume** of total water (liquid plus ice) + mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + if (.not.insideIDA)then ! calculate dt current, or use dt current as it can change here + if( abs(mLayerMatricHead - mLayerMatricHeadPrev) < verySmall )then !this difference is set as 0 inside varSubstep + dt_inv = 1._rkind/dt + else + dt_inv = mLayerMatricHeadPrime / (mLayerMatricHead - mLayerMatricHeadPrev) ! + endif + mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev)*dt_inv + else ! inside Sundials: instantaneous derivative will always be a full step size as input here + mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime endif - ! *** compute volumetric fraction of liquid water for unfrozen soil - else !( mLayerTemp >= TcSoil, all water is unfrozen ) - mLayerVolFracLiq = mLayerVolFracWat - mLayerVolFracLiqPrime = mLayerVolFracWatPrime - mLayerVolFracIcePrime = 0._rkind - - end if ! (check if soil is partially frozen) - - ! - volumetric ice content (-) - mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq - mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime - - end subroutine updateSoilSundials + if(mLayerVolFracWat > theta_sat)then; err=20; message=trim(message)//'volume of liquid and ice exceeds porosity'; return; end if + + ! compute the critical soil temperature where all water is unfrozen (K) + ! (eq 17 in Dall'Amico 2011) + TcSoil = Tfreeze + min(mLayerMatricHead,0._rkind)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) + + ! *** compute volumetric fraction of liquid water for partially frozen soil + if(mLayerTemp < TcSoil)then ! (check if soil temperature is less than the critical temperature) + ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice + ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations + xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) + mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution + mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + if(mLayerPsiLiq<0._rkind)then + mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime + else + mLayerVolFracLiqPrime = 0._rkind + endif + + ! *** compute volumetric fraction of liquid water for unfrozen soil + else !( mLayerTemp >= TcSoil, all water is unfrozen ) + mLayerVolFracLiq = mLayerVolFracWat + mLayerVolFracLiqPrime = mLayerVolFracWatPrime + mLayerVolFracIcePrime = 0._rkind + + end if ! (check if soil is partially frozen) + + ! - volumetric ice content (-) + mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq + mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime + +end subroutine updateSoilSundials end module updatStateSundials_module From 02b8ef6fe086c76f8b4ddb11fbbb1415942c4179 Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 27 Sep 2022 19:35:14 +0000 Subject: [PATCH 0368/1472] fixed indentation --- build/source/engine/soilLiqFlx.f90 | 3601 ++++++++++++++-------------- 1 file changed, 1763 insertions(+), 1838 deletions(-) diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index a3cb0b475..0120c5c96 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -55,25 +55,25 @@ module soilLiqFlx_module ! provide access to look-up values for model decisions USE mDecisions_module,only: & - ! look-up values for method used to compute derivative - numerical, & ! numerical solution - analytical, & ! analytical solution - ! look-up values for the form of Richards' equation - moisture, & ! moisture-based form of Richards' equation - mixdform, & ! mixed form of Richards' equation - ! look-up values for the type of hydraulic conductivity profile - constant, & ! constant hydraulic conductivity with depth - powerLaw_profile, & ! power-law profile - ! look-up values for the choice of groundwater parameterization - qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization - bigBucket, & ! a big bucket (lumped aquifer model) - noExplicit, & ! no explicit groundwater parameterization - ! look-up values for the choice of boundary conditions for hydrology - prescribedHead, & ! prescribed head (volumetric liquid water content for mixed form of Richards' eqn) - funcBottomHead, & ! function of matric head in the lower-most layer - freeDrainage, & ! free drainage - liquidFlux, & ! liquid water flux - zeroFlux ! zero flux + ! look-up values for method used to compute derivative + numerical, & ! numerical solution + analytical, & ! analytical solution + ! look-up values for the form of Richards' equation + moisture, & ! moisture-based form of Richards' equation + mixdform, & ! mixed form of Richards' equation + ! look-up values for the type of hydraulic conductivity profile + constant, & ! constant hydraulic conductivity with depth + powerLaw_profile, & ! power-law profile + ! look-up values for the choice of groundwater parameterization + qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization + bigBucket, & ! a big bucket (lumped aquifer model) + noExplicit, & ! no explicit groundwater parameterization + ! look-up values for the choice of boundary conditions for hydrology + prescribedHead, & ! prescribed head (volumetric liquid water content for mixed form of Richards' eqn) + funcBottomHead, & ! function of matric head in the lower-most layer + freeDrainage, & ! free drainage + liquidFlux, & ! liquid water flux + zeroFlux ! zero flux ! ----------------------------------------------------------------------------------------------------------- implicit none @@ -85,1861 +85,1786 @@ module soilLiqFlx_module contains - ! *************************************************************************************************************** - ! public subroutine soilLiqFlx: compute liquid water fluxes and their derivatives - ! *************************************************************************************************************** - subroutine soilLiqFlx(& - ! input: model control - nSoil, & ! intent(in): number of soil layers - doInfiltrate, & ! intent(in): flag to compute infiltration - scalarSolution, & ! intent(in): flag to indicate the scalar solution - deriv_desired, & ! intent(in): flag indicating if derivatives are desired - ! input: trial state variables - mLayerTempTrial, & ! intent(in): temperature (K) - mLayerMatricHeadLiqTrial, & ! intent(in): liquid matric head (m) - mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water (-) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice (-) - ! input: pre-computed derivatives - mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - dPsiLiq_dTemp, & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) - dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water - above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature - above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) - ! input: fluxes - scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) - scalarGroundEvaporation, & ! intent(in): ground evaporation (kg m-2 s-1) - scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) - ! input-output: data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model indices - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output: diagnostic variables for surface runoff - xMaxInfilRate, & ! intent(inout): maximum infiltration rate (m s-1) - scalarInfilArea, & ! intent(inout): fraction of unfrozen area where water can infiltrate (-) - scalarFrozenArea, & ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) - scalarSurfaceRunoff, & ! intent(out): surface runoff (m s-1) - ! output: diagnostic variables for model layers - mLayerdTheta_dPsi, & ! intent(out): derivative in the soil water characteristic w.r.t. psi (m-1) - mLayerdPsi_dTheta, & ! intent(out): derivative in the soil water characteristic w.r.t. theta (m) - dHydCond_dMatric, & ! intent(out): derivative in hydraulic conductivity w.r.t matric head (s-1) - ! output: fluxes - scalarSurfaceInfiltration, & ! intent(out): surface infiltration rate (m s-1) - iLayerLiqFluxSoil, & ! intent(out): liquid fluxes at layer interfaces (m s-1) - mLayerTranspire, & ! intent(out): transpiration loss from each soil layer (m s-1) - mLayerHydCond, & ! intent(out): hydraulic conductivity in each soil layer (m s-1) - ! output: derivatives in fluxes w.r.t. hydrology state variables -- matric head or volumetric lquid water -- in the layer above and layer below (m s-1 or s-1) - dq_dHydStateAbove, & ! intent(out): derivatives in the flux w.r.t. volumetric liquid water content in the layer above (m s-1) - dq_dHydStateBelow, & ! intent(out): derivatives in the flux w.r.t. volumetric liquid water content in the layer below (m s-1) - dq_dHydStateLayerSurfVec, & ! intent(out): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) - ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) - dq_dNrgStateAbove, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) - dq_dNrgStateBelow, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) - dq_dNrgStateLayerSurfVec, & ! intent(out): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) - ! output: derivatives in transpiration w.r.t. canopy state variables - mLayerdTrans_dTCanair, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature - mLayerdTrans_dTCanopy, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy temperature - mLayerdTrans_dTGround, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. ground temperature - mLayerdTrans_dCanWat, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy total water - ! output: error control - err,message) ! intent(out): error control - ! utility modules - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water - USE soil_utils_module,only:matricHead ! compute matric head (m) - USE soil_utils_module,only:dTheta_dPsi ! compute derivative of the soil moisture characteristic w.r.t. psi (m-1) - USE soil_utils_module,only:dPsi_dTheta ! compute derivative of the soil moisture characteristic w.r.t. theta (m) - USE soil_utils_module,only:hydCond_psi ! compute hydraulic conductivity as a function of matric head - USE soil_utils_module,only:hydCond_liq ! compute hydraulic conductivity as a function of volumetric liquid water content - USE soil_utils_module,only:hydCondMP_liq ! compute hydraulic conductivity of macropores as a function of volumetric liquid water content - ! ------------------------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input: model control - integer(i4b),intent(in) :: nSoil ! number of soil layers - logical(lgt),intent(in) :: doInfiltrate ! flag to compute infiltration - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - logical(lgt),intent(in) :: deriv_desired ! flag indicating if derivatives are desired - ! input: trial model state variables - real(rkind),intent(in) :: mLayerTempTrial(:) ! temperature in each layer at the current iteration (m) - real(rkind),intent(in) :: mLayerMatricHeadLiqTrial(:) ! liquid matric head in each layer at the current iteration (m) - real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! volumetric fraction of liquid water at the current iteration (-) - real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! volumetric fraction of ice at the current iteration (-) - ! input: pre-computed derivatves - real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative in volumetric liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: dPsiLiq_dTemp(:) ! derivative in liquid water matric potential w.r.t. temperature (m K-1) - real(rkind),intent(in) :: dCanopyTrans_dCanWat ! derivative in canopy transpiration w.r.t. canopy total water content (s-1) - real(rkind),intent(in) :: dCanopyTrans_dTCanair ! derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - real(rkind),intent(in) :: dCanopyTrans_dTCanopy ! derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - real(rkind),intent(in) :: dCanopyTrans_dTGround ! derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - real(rkind),intent(in) :: above_soilLiqFluxDeriv ! derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water - real(rkind),intent(in) :: above_soildLiq_dTk ! derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature - real(rkind),intent(in) :: above_soilFracLiq ! fraction of liquid water layer above soil (canopy or snow) (-) - ! input: model fluxes - real(rkind),intent(in) :: scalarCanopyTranspiration ! canopy transpiration (kg m-2 s-1) - real(rkind),intent(in) :: scalarGroundEvaporation ! ground evaporation (kg m-2 s-1) - real(rkind),intent(in) :: scalarRainPlusMelt ! rain plus melt (m s-1) - ! input-output: data structures - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! state vector geometry - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - ! output: diagnostic variables for surface runoff - real(rkind),intent(inout) :: xMaxInfilRate ! maximum infiltration rate (m s-1) - real(rkind),intent(inout) :: scalarInfilArea ! fraction of unfrozen area where water can infiltrate (-) - real(rkind),intent(inout) :: scalarFrozenArea ! fraction of area that is considered impermeable due to soil ice (-) - real(rkind),intent(inout) :: scalarSurfaceRunoff ! surface runoff (m s-1) - ! output: diagnostic variables for each layer - real(rkind),intent(inout) :: mLayerdTheta_dPsi(:) ! derivative in the soil water characteristic w.r.t. psi (m-1) - real(rkind),intent(inout) :: mLayerdPsi_dTheta(:) ! derivative in the soil water characteristic w.r.t. theta (m) - real(rkind),intent(inout) :: dHydCond_dMatric(:) ! derivative in hydraulic conductivity w.r.t matric head (s-1) - ! output: liquid fluxes - real(rkind),intent(inout) :: scalarSurfaceInfiltration ! surface infiltration rate (m s-1) - real(rkind),intent(inout) :: iLayerLiqFluxSoil(0:) ! liquid flux at soil layer interfaces (m s-1) - real(rkind),intent(inout) :: mLayerTranspire(:) ! transpiration loss from each soil layer (m s-1) - real(rkind),intent(inout) :: mLayerHydCond(:) ! hydraulic conductivity in each soil layer (m s-1) - ! output: derivatives in fluxes w.r.t. state variables in the layer above and layer below (m s-1) - real(rkind),intent(inout) :: dq_dHydStateAbove(0:) ! derivative in the flux in layer interfaces w.r.t. state variables in the layer above - real(rkind),intent(inout) :: dq_dHydStateBelow(0:) ! derivative in the flux in layer interfaces w.r.t. state variables in the layer below - real(rkind),intent(inout) :: dq_dHydStateLayerSurfVec(0:) ! derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) - ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) - real(rkind),intent(inout) :: dq_dNrgStateAbove(0:) ! derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) - real(rkind),intent(inout) :: dq_dNrgStateBelow(0:) ! derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) - real(rkind),intent(inout) :: dq_dNrgStateLayerSurfVec(0:) ! derivative in surface infiltration w.r.t. temperature in above soil snow or canopy and every soil layer (m s-1 or s-1) - ! output: derivatives in transpiration w.r.t. canopy state variables - real(rkind),intent(inout) :: mLayerdTrans_dTCanair(:) ! derivatives in the soil layer transpiration flux w.r.t. canopy air temperature - real(rkind),intent(inout) :: mLayerdTrans_dTCanopy(:) ! derivatives in the soil layer transpiration flux w.r.t. canopy temperature - real(rkind),intent(inout) :: mLayerdTrans_dTGround(:) ! derivatives in the soil layer transpiration flux w.r.t. ground temperature - real(rkind),intent(inout) :: mLayerdTrans_dCanWat(:) ! derivatives in the soil layer transpiration flux w.r.t. canopy total water - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ----------------------------------------------------------------------------------------------------------------------------------------------------- - ! local variables: general - character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b) :: ibeg,iend ! start and end indices of the soil layers in concatanated snow-soil vector - logical(lgt) :: desireAnal ! flag to identify if analytical derivatives are desired - integer(i4b) :: iLayer,iSoil ! index of soil layer - integer(i4b) :: ixLayerDesired(1) ! layer desired (scalar solution) - integer(i4b) :: ixTop ! top layer in subroutine call - integer(i4b) :: ixBot ! bottom layer in subroutine call - ! additional variables to compute numerical derivatives - integer(i4b) :: nFlux ! number of flux calculations required (>1 = numerical derivatives with one-sided finite differences) - integer(i4b) :: itry ! index of different flux calculations - integer(i4b),parameter :: unperturbed=0 ! named variable to identify the case of unperturbed state variables - integer(i4b),parameter :: perturbState=1 ! named variable to identify the case where we perturb the state in the current layer - integer(i4b),parameter :: perturbStateAbove=2 ! named variable to identify the case where we perturb the state layer above - integer(i4b),parameter :: perturbStateBelow=3 ! named variable to identify the case where we perturb the state layer below - integer(i4b) :: ixPerturb ! index of element in 2-element vector to perturb - integer(i4b) :: ixOriginal ! index of perturbed element in the original vector - real(rkind) :: scalarVolFracLiqTrial ! trial value of volumetric liquid water content (-) - real(rkind) :: scalarMatricHeadLiqTrial ! trial value of liquid matric head (m) - real(rkind) :: scalarHydCondTrial ! trial value of hydraulic conductivity (m s-1) - real(rkind) :: scalarHydCondMicro ! trial value of hydraulic conductivity of micropores (m s-1) - real(rkind) :: scalarHydCondMacro ! trial value of hydraulic conductivity of macropores (m s-1) - real(rkind) :: scalarFlux ! vertical flux (m s-1) - real(rkind) :: scalarFlux_dStateAbove ! vertical flux with perturbation to the state above (m s-1) - real(rkind) :: scalarFlux_dStateBelow ! vertical flux with perturbation to the state below (m s-1) - ! transpiration sink term - real(rkind),dimension(nSoil) :: mLayerTranspireFrac ! fraction of transpiration allocated to each soil layer (-) - ! diagnostic variables - real(rkind),dimension(nSoil) :: iceImpedeFac ! ice impedence factor at layer mid-points (-) - real(rkind),dimension(nSoil) :: mLayerDiffuse ! diffusivity at layer mid-point (m2 s-1) - real(rkind),dimension(nSoil) :: dHydCond_dVolLiq ! derivative in hydraulic conductivity w.r.t volumetric liquid water content (m s-1) - real(rkind),dimension(nSoil) :: dDiffuse_dVolLiq ! derivative in hydraulic diffusivity w.r.t volumetric liquid water content (m2 s-1) - real(rkind),dimension(nSoil) :: dHydCond_dTemp ! derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) - real(rkind),dimension(0:nSoil) :: iLayerHydCond ! hydraulic conductivity at layer interface (m s-1) - real(rkind),dimension(0:nSoil) :: iLayerDiffuse ! diffusivity at layer interface (m2 s-1) - ! compute surface flux - integer(i4b) :: nRoots ! number of soil layers with roots - integer(i4b) :: ixIce ! index of the lowest soil layer that contains ice - real(rkind),dimension(0:nSoil) :: iLayerHeight ! height of the layer interfaces (m) - ! compute fluxes and derivatives at layer interfaces - real(rkind),dimension(2) :: vectorVolFracLiqTrial ! trial value of volumetric liquid water content (-) - real(rkind),dimension(2) :: vectorMatricHeadLiqTrial ! trial value of liquid matric head (m) - real(rkind),dimension(2) :: vectorHydCondTrial ! trial value of hydraulic conductivity (m s-1) - real(rkind),dimension(2) :: vectorDiffuseTrial ! trial value of hydraulic diffusivity (m2 s-1) - real(rkind) :: scalardPsi_dTheta ! derivative in soil water characteristix, used for perturbations when computing numerical derivatives - ! ------------------------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='soilLiqFlx/' - - ! get indices for the data structures - ibeg = indx_data%var(iLookINDEX%nSnow)%dat(1) + 1 - iend = indx_data%var(iLookINDEX%nSnow)%dat(1) + indx_data%var(iLookINDEX%nSoil)%dat(1) - - ! get a copy of iLayerHeight - ! NOTE: performance hit, though cannot define the shape (0:) with the associate construct - iLayerHeight(0:nSoil) = prog_data%var(iLookPROG%iLayerHeight)%dat(ibeg-1:iend) ! height of the layer interfaces (m) - - ! make association between local variables and the information in the data structures - associate(& +! *************************************************************************************************************** +! public subroutine soilLiqFlx: compute liquid water fluxes and their derivatives +! *************************************************************************************************************** +subroutine soilLiqFlx(& + ! input: model control + nSoil, & ! intent(in): number of soil layers + doInfiltrate, & ! intent(in): flag to compute infiltration + scalarSolution, & ! intent(in): flag to indicate the scalar solution + deriv_desired, & ! intent(in): flag indicating if derivatives are desired + ! input: trial state variables + mLayerTempTrial, & ! intent(in): temperature (K) + mLayerMatricHeadLiqTrial, & ! intent(in): liquid matric head (m) + mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water (-) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice (-) + ! input: pre-computed derivatives + mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + dPsiLiq_dTemp, & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) + dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) + ! input: fluxes + scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) + scalarGroundEvaporation, & ! intent(in): ground evaporation (kg m-2 s-1) + scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) + ! input-output: data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model indices + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output: diagnostic variables for surface runoff + xMaxInfilRate, & ! intent(inout): maximum infiltration rate (m s-1) + scalarInfilArea, & ! intent(inout): fraction of unfrozen area where water can infiltrate (-) + scalarFrozenArea, & ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) + scalarSurfaceRunoff, & ! intent(out): surface runoff (m s-1) + ! output: diagnostic variables for model layers + mLayerdTheta_dPsi, & ! intent(out): derivative in the soil water characteristic w.r.t. psi (m-1) + mLayerdPsi_dTheta, & ! intent(out): derivative in the soil water characteristic w.r.t. theta (m) + dHydCond_dMatric, & ! intent(out): derivative in hydraulic conductivity w.r.t matric head (s-1) + ! output: fluxes + scalarSurfaceInfiltration, & ! intent(out): surface infiltration rate (m s-1) + iLayerLiqFluxSoil, & ! intent(out): liquid fluxes at layer interfaces (m s-1) + mLayerTranspire, & ! intent(out): transpiration loss from each soil layer (m s-1) + mLayerHydCond, & ! intent(out): hydraulic conductivity in each soil layer (m s-1) + ! output: derivatives in fluxes w.r.t. hydrology state variables -- matric head or volumetric lquid water -- in the layer above and layer below (m s-1 or s-1) + dq_dHydStateAbove, & ! intent(out): derivatives in the flux w.r.t. volumetric liquid water content in the layer above (m s-1) + dq_dHydStateBelow, & ! intent(out): derivatives in the flux w.r.t. volumetric liquid water content in the layer below (m s-1) + dq_dHydStateLayerSurfVec, & ! intent(out): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) + ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) + dq_dNrgStateAbove, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) + dq_dNrgStateBelow, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) + dq_dNrgStateLayerSurfVec, & ! intent(out): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) + ! output: derivatives in transpiration w.r.t. canopy state variables + mLayerdTrans_dTCanair, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + mLayerdTrans_dTCanopy, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy temperature + mLayerdTrans_dTGround, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. ground temperature + mLayerdTrans_dCanWat, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy total water + ! output: error control + err,message) ! intent(out): error control + ! utility modules + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water + USE soil_utils_module,only:matricHead ! compute matric head (m) + USE soil_utils_module,only:dTheta_dPsi ! compute derivative of the soil moisture characteristic w.r.t. psi (m-1) + USE soil_utils_module,only:dPsi_dTheta ! compute derivative of the soil moisture characteristic w.r.t. theta (m) + USE soil_utils_module,only:hydCond_psi ! compute hydraulic conductivity as a function of matric head + USE soil_utils_module,only:hydCond_liq ! compute hydraulic conductivity as a function of volumetric liquid water content + USE soil_utils_module,only:hydCondMP_liq ! compute hydraulic conductivity of macropores as a function of volumetric liquid water content + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + implicit none ! input: model control - ixDerivMethod => model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): index of the method used to calculate flux derivatives - ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision, & ! intent(in): index of the form of Richards' equation - ixBcUpperSoilHydrology => model_decisions(iLookDECISIONS%bcUpprSoiH)%iDecision, & ! intent(in): index of the upper boundary conditions for soil hydrology - ixBcLowerSoilHydrology => model_decisions(iLookDECISIONS%bcLowrSoiH)%iDecision, & ! intent(in): index of the lower boundary conditions for soil hydrology - ! input: model indices - ixMatricHead => indx_data%var(iLookINDEX%ixMatricHead)%dat, & ! intent(in): indices of soil layers where matric head is the state variable - ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat, & ! intent(in): index in the state subset for hydrology state variables in the soil domain - ! input: model coordinate variables -- NOTE: use of ibeg and iend - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat(ibeg:iend), & ! intent(in): depth of the layer (m) - mLayerHeight => prog_data%var(iLookPROG%mLayerHeight)%dat(ibeg:iend), & ! intent(in): height of the layer mid-point (m) - ! input: upper boundary conditions - upperBoundHead => mpar_data%var(iLookPARAM%upperBoundHead)%dat(1), & ! intent(in): upper boundary condition for matric head (m) - upperBoundTheta => mpar_data%var(iLookPARAM%upperBoundTheta)%dat(1), & ! intent(in): upper boundary condition for volumetric liquid water content (-) - ! input: lower boundary conditions - lowerBoundHead => mpar_data%var(iLookPARAM%lowerBoundHead)%dat(1), & ! intent(in): lower boundary condition for matric head (m) - lowerBoundTheta => mpar_data%var(iLookPARAM%lowerBoundTheta)%dat(1), & ! intent(in): lower boundary condition for volumetric liquid water content (-) - ! input: vertically variable soil parameters - vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat, & ! intent(in): van Genutchen "m" parameter (-) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat, & ! intent(in): van Genutchen "n" parameter (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat, & ! intent(in): van Genutchen "alpha" parameter (m-1) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat, & ! intent(in): soil residual volumetric water content (-) - ! input: vertically constant soil parameters - wettingFrontSuction => mpar_data%var(iLookPARAM%wettingFrontSuction)%dat(1), & ! intent(in): Green-Ampt wetting front suction (m) - rootingDepth => mpar_data%var(iLookPARAM%rootingDepth)%dat(1), & ! intent(in): rooting depth (m) - kAnisotropic => mpar_data%var(iLookPARAM%kAnisotropic)%dat(1), & ! intent(in): anisotropy factor for lateral hydraulic conductivity (-) - zScale_TOPMODEL => mpar_data%var(iLookPARAM%zScale_TOPMODEL)%dat(1), & ! intent(in): TOPMODEL scaling factor (m) - qSurfScale => mpar_data%var(iLookPARAM%qSurfScale)%dat(1), & ! intent(in): scaling factor in the surface runoff parameterization (-) - f_impede => mpar_data%var(iLookPARAM%f_impede)%dat(1), & ! intent(in): ice impedence factor (-) - soilIceScale => mpar_data%var(iLookPARAM%soilIceScale)%dat(1), & ! intent(in): scaling factor for depth of soil ice, used to get frozen fraction (m) - soilIceCV => mpar_data%var(iLookPARAM%soilIceCV)%dat(1), & ! intent(in): CV of depth of soil ice, used to get frozen fraction (-) - theta_mp => mpar_data%var(iLookPARAM%theta_mp)%dat(1), & ! intent(in): volumetric liquid water content when macropore flow begins (-) - mpExp => mpar_data%var(iLookPARAM%mpExp)%dat(1), & ! intent(in): empirical exponent in macropore flow equation (-) - ! input: saturated hydraulic conductivity - mLayerSatHydCondMP => flux_data%var(iLookFLUX%mLayerSatHydCondMP)%dat, & ! intent(in): saturated hydraulic conductivity of macropores at the mid-point of each layer (m s-1) - mLayerSatHydCond => flux_data%var(iLookFLUX%mLayerSatHydCond)%dat, & ! intent(in): saturated hydraulic conductivity at the mid-point of each layer (m s-1) - iLayerSatHydCond => flux_data%var(iLookFLUX%iLayerSatHydCond)%dat, & ! intent(in): saturated hydraulic conductivity at the interface of each layer (m s-1) - ! input: factors limiting transpiration (from vegFlux routine) - mLayerRootDensity => diag_data%var(iLookDIAG%mLayerRootDensity)%dat, & ! intent(in): root density in each layer (-) - scalarTranspireLim => diag_data%var(iLookDIAG%scalarTranspireLim)%dat(1), & ! intent(in): weighted average of the transpiration limiting factor (-) - mLayerTranspireLim => diag_data%var(iLookDIAG%mLayerTranspireLim)%dat & ! intent(in): transpiration limiting factor in each layer (-) - ) ! associating local variables with the information in the data structures - - ! ------------------------------------------------------------------------------------------------------------------------------------------------- - ! preliminaries - ! ------------------------------------------------------------------------------------------------------------------------------------------------- - - ! define the pethod to compute derivatives - !print*, 'numerical derivatives = ', (ixDerivMethod==numerical) - - ! numerical derivatives are not implemented yet - if(ixDerivMethod==numerical)then - message=trim(message)//'numerical derivates do not account for the cross derivatives between hydrology and thermodynamics' - err=20; return - end if - - ! check the need to compute analytical derivatives - if(deriv_desired .and. ixDerivMethod==analytical)then - desireAnal = .true. - else - desireAnal = .false. - end if - - ! check the need to compute numerical derivatives - if(deriv_desired .and. ixDerivMethod==numerical)then - nFlux=3 ! compute the derivatives using one-sided finite differences - else - nFlux=0 ! compute analytical derivatives - end if - - ! get the indices for the soil layers - if(scalarSolution)then - ixLayerDesired = pack(ixMatricHead, ixSoilOnlyHyd/=integerMissing) - ixTop = ixLayerDesired(1) - ixBot = ixLayerDesired(1) - else - ixTop = 1 - ixBot = nSoil - endif - - ! identify the number of layers that contain roots - nRoots = count(iLayerHeight(0:nSoil-1) < rootingDepth-verySmall) - if(nRoots==0)then - message=trim(message)//'no layers with roots' - err=20; return - endif - - ! identify lowest soil layer with ice - ! NOTE: cannot use count because there may be an unfrozen wedge - ixIce = 0 ! initialize the index of the ice layer (0 means no ice in the soil profile) - do iLayer=1,nSoil ! (loop through soil layers) - if(mLayerVolFracIceTrial(iLayer) > verySmall) ixIce = iLayer - end do - !if(ixIce==nSoil)then; err=20; message=trim(message)//'ice extends to the bottom of the soil profile'; return; end if - - ! ************************************************************************************************************************************************* - ! ************************************************************************************************************************************************* - - ! ------------------------------------------------------------------------------------------------------------------------------------------------- - ! compute the transpiration sink term - ! ------------------------------------------------------------------------------------------------------------------------------------------------- - - ! check the need to compute transpiration (NOTE: intent=inout) - if( .not. (scalarSolution .and. ixTop>1) )then - - ! compute the fraction of transpiration loss from each soil layer - if(scalarTranspireLim > tiny(scalarTranspireLim))then ! (transpiration may be non-zero even if the soil moisture limiting factor is zero) - mLayerTranspireFrac(:) = mLayerRootDensity(:)*mLayerTranspireLim(:)/scalarTranspireLim - else ! (possible for there to be non-zero conductance and therefore transpiration in this case) - mLayerTranspireFrac(:) = mLayerRootDensity(:) / sum(mLayerRootDensity) - end if - - ! check fractions sum to one - if(abs(sum(mLayerTranspireFrac) - 1._rkind) > verySmall)then - message=trim(message)//'fraction transpiration in soil layers does not sum to one' - err=20; return - endif - - ! compute transpiration loss from each soil layer (kg m-2 s-1 --> m s-1) - mLayerTranspire(:) = mLayerTranspireFrac(:)*scalarCanopyTranspiration/iden_water - ! derivatives in transpiration w.r.t. canopy state variables - mLayerdTrans_dCanWat(:) = mLayerTranspireFrac(:)*dCanopyTrans_dCanWat /iden_water - mLayerdTrans_dTCanair(:) = mLayerTranspireFrac(:)*dCanopyTrans_dTCanair/iden_water - mLayerdTrans_dTCanopy(:) = mLayerTranspireFrac(:)*dCanopyTrans_dTCanopy/iden_water - mLayerdTrans_dTGround(:) = mLayerTranspireFrac(:)*dCanopyTrans_dTGround/iden_water - - ! special case of prescribed head -- no transpiration - if(ixBcUpperSoilHydrology==prescribedHead) then - mLayerTranspire(:) = 0._rkind - ! derivatives in transpiration w.r.t. canopy state variables - mLayerdTrans_dCanWat(:) = 0._rkind - mLayerdTrans_dTCanair(:)= 0._rkind - mLayerdTrans_dTCanopy(:)= 0._rkind - mLayerdTrans_dTGround(:)= 0._rkind - endif - - endif ! if need to compute transpiration - - ! ************************************************************************************************************************************************* - ! ************************************************************************************************************************************************* - - ! ------------------------------------------------------------------------------------------------------------------------------------------------- - ! compute diagnostic variables at the nodes throughout the soil profile - ! ------------------------------------------------------------------------------------------------------------------------------------------------- - do iSoil=ixTop,min(ixBot+1,nSoil) ! (loop through soil layers) - - call diagv_node(& - ! input: model control - desireAnal, & ! intent(in): flag indicating if derivatives are desired - ixRichards, & ! intent(in): index defining the option for Richards' equation (moisture or mixdform) - ! input: state variables - mLayerTempTrial(iSoil), & ! intent(in): temperature (K) - mLayerMatricHeadLiqTrial(iSoil), & ! intent(in): liquid matric head in each layer (m) - mLayerVolFracLiqTrial(iSoil), & ! intent(in): volumetric liquid water content in each soil layer (-) - mLayerVolFracIceTrial(iSoil), & ! intent(in): volumetric ice content in each soil layer (-) - ! input: pre-computed deriavatives - mLayerdTheta_dTk(iSoil), & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - dPsiLiq_dTemp(iSoil), & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) - ! input: soil parameters - vGn_alpha(iSoil), & ! intent(in): van Genutchen "alpha" parameter (m-1) - vGn_n(iSoil), & ! intent(in): van Genutchen "n" parameter (-) - vGn_m(iSoil), & ! intent(in): van Genutchen "m" parameter (-) - mpExp, & ! intent(in): empirical exponent in macropore flow equation (-) - theta_sat(iSoil), & ! intent(in): soil porosity (-) - theta_res(iSoil), & ! intent(in): soil residual volumetric water content (-) - theta_mp, & ! intent(in): volumetric liquid water content when macropore flow begins (-) - f_impede, & ! intent(in): ice impedence factor (-) - ! input: saturated hydraulic conductivity - mLayerSatHydCond(iSoil), & ! intent(in): saturated hydraulic conductivity at the mid-point of each layer (m s-1) - mLayerSatHydCondMP(iSoil), & ! intent(in): saturated hydraulic conductivity of macropores at the mid-point of each layer (m s-1) - ! output: derivative in the soil water characteristic - mLayerdPsi_dTheta(iSoil), & ! intent(out): derivative in the soil water characteristic - mLayerdTheta_dPsi(iSoil), & ! intent(out): derivative in the soil water characteristic - ! output: transmittance - mLayerHydCond(iSoil), & ! intent(out): hydraulic conductivity at layer mid-points (m s-1) - mLayerDiffuse(iSoil), & ! intent(out): diffusivity at layer mid-points (m2 s-1) - iceImpedeFac(iSoil), & ! intent(out): ice impedence factor in each layer (-) - ! output: transmittance derivatives - dHydCond_dVolLiq(iSoil), & ! intent(out): derivative in hydraulic conductivity w.r.t volumetric liquid water content (m s-1) - dDiffuse_dVolLiq(iSoil), & ! intent(out): derivative in hydraulic diffusivity w.r.t volumetric liquid water content (m2 s-1) - dHydCond_dMatric(iSoil), & ! intent(out): derivative in hydraulic conductivity w.r.t matric head (m s-1) - dHydCond_dTemp(iSoil), & ! intent(out): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - end do ! (looping through soil layers) - - ! ************************************************************************************************************************************************* - ! ************************************************************************************************************************************************* - - ! ------------------------------------------------------------------------------------------------------------------------------------------------- - ! compute infiltraton at the surface and its derivative w.r.t. mass in the upper soil layer - ! ------------------------------------------------------------------------------------------------------------------------------------------------- - - ! set derivative w.r.t. state above to zero (does not exist) - dq_dHydStateAbove(0) = 0._rkind - dq_dNrgStateAbove(0) = 0._rkind - - ! either one or multiple flux calls, depending on if using analytical or numerical derivatives - do itry=nFlux,0,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) - - ! ===== - ! get input state variables... - ! ============================ - ! identify the type of perturbation - ! Currently we are ignoring the perturbations in the non-surface layers and with temperature - select case(itry) - - ! skip undesired perturbations - case(perturbStateAbove); cycle ! cannot perturb state above (does not exist) -- so keep cycling - case(perturbState); cycle ! perturbing the layer below the flux at the top interface - - ! un-perturbed case - case(unperturbed) - scalarVolFracLiqTrial = mLayerVolFracLiqTrial(1) - scalarMatricHeadLiqTrial = mLayerMatricHeadLiqTrial(1) - - ! perturb soil state (one-sided finite differences) - case(perturbStateBelow) - ! (perturbation depends on the form of Richards' equation) - select case(ixRichards) - case(moisture) - scalarVolFracLiqTrial = mLayerVolFracLiqTrial(1) + dx - scalarMatricHeadLiqTrial = mLayerMatricHeadLiqTrial(1) - case(mixdform) - scalarVolFracLiqTrial = mLayerVolFracLiqTrial(1) - scalarMatricHeadLiqTrial = mLayerMatricHeadLiqTrial(1) + dx - case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return - end select ! (form of Richards' equation - ! check for an unknown perturbation - case default; err=10; message=trim(message)//"unknown perturbation"; return - - end select ! (type of perturbation) - - ! ===== - ! compute surface flux and its derivative... - ! ========================================== - - call surfaceFlx(& - ! input: model control - doInfiltrate, & ! intent(in): flag indicating if desire to compute infiltration - desireAnal, & ! intent(in): flag indicating if derivatives are desired - ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) - ixBcUpperSoilHydrology, & ! intent(in): index defining the type of boundary conditions (neumann or diriclet) - nRoots, & ! intent(in): number of layers that contain roots - ixIce, & ! intent(in): index of lowest ice layer - nSoil, & ! intent(in): number of soil layers - ! input: state variables - mLayerTempTrial, & ! intent(in): temperature (K) - scalarMatricHeadLiqTrial, & ! intent(in): liquid matric head in the upper-most soil layer (m) - mLayerMatricHeadLiqTrial, & ! intent(in): liquid matric head in each soil layer (m) - scalarVolFracLiqTrial, & ! intent(in): volumetric liquid water content the upper-most soil layer (-) - mLayerVolFracLiqTrial, & ! intent(in): volumetric liquid water content in each soil layer (-) - mLayerVolFracIceTrial, & ! intent(in): volumetric ice content in each soil layer (-) - ! input: pre-computed deriavatives - mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) - mLayerdPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) - above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water - above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature - above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) - ! input: depth of upper-most soil layer (m) - mLayerDepth, & ! intent(in): depth of each soil layer (m) - iLayerHeight, & ! intent(in): height at the interface of each layer (m) - ! input: boundary conditions - upperBoundHead, & ! intent(in): upper boundary condition (m) - upperBoundTheta, & ! intent(in): upper boundary condition (-) - ! input: flux at the upper boundary - scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) - ! input: transmittance - iLayerSatHydCond(0), & ! intent(in): saturated hydraulic conductivity at the surface (m s-1) - dHydCond_dTemp(1), & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) - iceImpedeFac(1), & ! intent(in): ice impedence factor in the upper-most soil layer (-) - ! input: soil parameters - vGn_alpha(1), & ! intent(in): van Genutchen "alpha" parameter (m-1) - vGn_n(1), & ! intent(in): van Genutchen "n" parameter (-) - vGn_m(1), & ! intent(in): van Genutchen "m" parameter (-) - theta_sat(1), & ! intent(in): soil porosity (-) - theta_res(1), & ! intent(in): soil residual volumetric water content (-) - qSurfScale, & ! intent(in): scaling factor in the surface runoff parameterization (-) - zScale_TOPMODEL, & ! intent(in): scaling factor used to describe decrease in hydraulic conductivity with depth (m) - rootingDepth, & ! intent(in): rooting depth (m) - wettingFrontSuction, & ! intent(in): Green-Ampt wetting front suction (m) - soilIceScale, & ! intent(in): soil ice scaling factor in Gamma distribution used to define frozen area (m) - soilIceCV, & ! intent(in): soil ice CV in Gamma distribution used to define frozen area (-) - ! input-output: hydraulic conductivity and diffusivity at the surface - iLayerHydCond(0), & ! intent(inout): hydraulic conductivity at the surface (m s-1) - iLayerDiffuse(0), & ! intent(inout): hydraulic diffusivity at the surface (m2 s-1) - ! input-output: fluxes at layer interfaces and surface runoff - xMaxInfilRate, & ! intent(inout): maximum infiltration rate (m s-1) - scalarInfilArea, & ! intent(inout): fraction of unfrozen area where water can infiltrate (-) - scalarFrozenArea, & ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) - scalarSurfaceRunoff, & ! intent(out): surface runoff (m s-1) - scalarSurfaceInfiltration, & ! intent(out): surface infiltration (m s-1) - ! input-output: deriavtives in surface infiltration w.r.t. volumetric liquid water (m s-1) and matric head (s-1) in the upper-most soil layer - dq_dHydStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) - dq_dNrgStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - ! include base soil evaporation as the upper boundary flux - iLayerLiqFluxSoil(0) = scalarGroundEvaporation/iden_water + scalarSurfaceInfiltration - - ! get copies of surface flux to compute numerical derivatives - if(deriv_desired .and. ixDerivMethod==numerical)then - select case(itry) - case(unperturbed); scalarFlux = iLayerLiqFluxSoil(0) - case(perturbStateBelow); scalarFlux_dStateBelow = iLayerLiqFluxSoil(0) - case default; err=10; message=trim(message)//"unknown perturbation"; return - end select - end if - - !write(*,'(a,1x,10(f30.15))') 'scalarRainPlusMelt, scalarSurfaceInfiltration = ', scalarRainPlusMelt, scalarSurfaceInfiltration - - end do ! (looping through different flux calculations -- one or multiple calls depending if desire for numerical or analytical derivatives) - - dq_dHydStateBelow(0) = 0._rkind ! contribution will be in dq_dHydStateLayerSurfVec(1) - dq_dNrgStateBelow(0) = 0._rkind ! contribution will be in dq_dNrgStateLayerSurfVec(1) - - ! compute numerical derivatives - if(deriv_desired .and. ixDerivMethod==numerical)then - dq_dHydStateLayerSurfVec(1) = (scalarFlux_dStateBelow - scalarFlux)/dx ! change in surface flux w.r.t. change in the soil moisture in the top soil layer (m s-1) - dq_dHydStateLayerSurfVec(1) = 0._rkind ! Did not yet compute this perturbation - end if - !print*, 'scalarSurfaceInfiltration, iLayerLiqFluxSoil(0) = ', scalarSurfaceInfiltration, iLayerLiqFluxSoil(0) - !print*, '(ixDerivMethod==numerical), dq_dHydStateBelow(0) = ', (ixDerivMethod==numerical), dq_dHydStateBelow(0) - !pause - - ! ************************************************************************************************************************************************* - ! ************************************************************************************************************************************************* - - ! ------------------------------------------------------------------------------------------------------------------------------------------------- - ! * compute fluxes and derivatives at layer interfaces... - ! ------------------------------------------------------------------------------------------------------------------------------------------------- - - ! NOTE: computing flux at the bottom of the layer - - ! loop through soil layers - do iLayer=ixTop,min(ixBot,nSoil-1) - - ! either one or multiple flux calls, depending on if using analytical or numerical derivatives - do itry=nFlux,0,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) - - ! ===== - ! determine layer to perturb - ! ============================ - select case(itry) - ! skip undesired perturbations - case(perturbState); cycle ! perturbing the layers above and below the flux at the interface - ! identify the index for the perturbation - case(unperturbed); ixPerturb = 0 - case(perturbStateAbove); ixPerturb = 1 - case(perturbStateBelow); ixPerturb = 2 - case default; err=10; message=trim(message)//"unknown perturbation"; return - end select ! (identifying layer to of perturbation) - ! determine the index in the original vector - ixOriginal = iLayer + (ixPerturb-1) - - ! ===== - ! get input state variables... - ! ============================ - ! start with the un-perturbed case - vectorVolFracLiqTrial(1:2) = mLayerVolFracLiqTrial(iLayer:iLayer+1) - vectorMatricHeadLiqTrial(1:2) = mLayerMatricHeadLiqTrial(iLayer:iLayer+1) - ! make appropriate perturbations - if(ixPerturb > 0)then - select case(ixRichards) - case(moisture); vectorVolFracLiqTrial(ixPerturb) = vectorVolFracLiqTrial(ixPerturb) + dx - case(mixdform); vectorMatricHeadLiqTrial(ixPerturb) = vectorMatricHeadLiqTrial(ixPerturb) + dx - case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return - end select ! (form of Richards' equation) - end if - - ! ===== - ! get hydraulic conductivty... - ! ============================ - ! start with the un-perturbed case - vectorHydCondTrial(1:2) = mLayerHydCond(iLayer:iLayer+1) - vectorDiffuseTrial(1:2) = mLayerDiffuse(iLayer:iLayer+1) - ! make appropriate perturbations - if(ixPerturb > 0)then ! only recompute these if perturbed - select case(ixRichards) - case(moisture) - scalardPsi_dTheta = dPsi_dTheta(vectorVolFracLiqTrial(ixPerturb),vGn_alpha(ixPerturb),theta_res(ixPerturb),theta_sat(ixPerturb),vGn_n(ixPerturb),vGn_m(ixPerturb)) - vectorHydCondTrial(ixPerturb) = hydCond_liq(vectorVolFracLiqTrial(ixPerturb),mLayerSatHydCond(ixOriginal),theta_res(ixPerturb),theta_sat(ixPerturb),vGn_m(ixPerturb)) * iceImpedeFac(ixOriginal) - vectorDiffuseTrial(ixPerturb) = scalardPsi_dTheta * vectorHydCondTrial(ixPerturb) - case(mixdform) - scalarVolFracLiqTrial = volFracLiq(vectorMatricHeadLiqTrial(ixPerturb),vGn_alpha(ixPerturb),theta_res(ixPerturb),theta_sat(ixPerturb),vGn_n(ixPerturb),vGn_m(ixPerturb)) - scalarHydCondMicro = hydCond_psi(vectorMatricHeadLiqTrial(ixPerturb),mLayerSatHydCond(ixOriginal),vGn_alpha(ixPerturb),vGn_n(ixPerturb),vGn_m(ixPerturb)) * iceImpedeFac(ixOriginal) - scalarHydCondMacro = hydCondMP_liq(scalarVolFracLiqTrial,theta_sat(ixPerturb),theta_mp,mpExp,mLayerSatHydCondMP(ixOriginal),mLayerSatHydCond(ixOriginal)) - vectorHydCondTrial(ixPerturb) = scalarHydCondMicro + scalarHydCondMacro - case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return - end select ! (form of Richards' equation) - endif ! (recompute if perturbed) - - ! ===== - ! compute vertical flux at layer interface and its derivative w.r.t. the state above and the state below... - ! ========================================================================================================= - - ! NOTE: computing flux at the bottom of the layer - - call iLayerFlux(& - ! input: model control - desireAnal, & ! intent(in): flag indicating if derivatives are desired - ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) - ! input: state variables (adjacent layers) - vectorMatricHeadLiqTrial, & ! intent(in): liquid matric head at the soil nodes (m) - vectorVolFracLiqTrial, & ! intent(in): volumetric liquid water content at the soil nodes (-) - ! input: model coordinate variables (adjacent layers) - mLayerHeight(iLayer:iLayer+1), & ! intent(in): height of the soil nodes (m) - ! input: temperature derivatives - dPsiLiq_dTemp(iLayer:iLayer+1), & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) - dHydCond_dTemp(iLayer:iLayer+1), & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) - ! input: transmittance (adjacent layers) - vectorHydCondTrial, & ! intent(in): hydraulic conductivity at the soil nodes (m s-1) - vectorDiffuseTrial, & ! intent(in): hydraulic diffusivity at the soil nodes (m2 s-1) - ! input: transmittance derivatives (adjacent layers) - dHydCond_dVolLiq(iLayer:iLayer+1), & ! intent(in): change in hydraulic conductivity w.r.t. change in volumetric liquid water content (m s-1) - dDiffuse_dVolLiq(iLayer:iLayer+1), & ! intent(in): change in hydraulic diffusivity w.r.t. change in volumetric liquid water content (m2 s-1) - dHydCond_dMatric(iLayer:iLayer+1), & ! intent(in): change in hydraulic conductivity w.r.t. change in matric head (s-1) - ! output: tranmsmittance at the layer interface (scalars) - iLayerHydCond(iLayer), & ! intent(out): hydraulic conductivity at the interface between layers (m s-1) - iLayerDiffuse(iLayer), & ! intent(out): hydraulic diffusivity at the interface between layers (m2 s-1) - ! output: vertical flux at the layer interface (scalars) - iLayerLiqFluxSoil(iLayer), & ! intent(out): vertical flux of liquid water at the layer interface (m s-1) - ! output: derivatives in fluxes w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below (m s-1 or s-1) - dq_dHydStateAbove(iLayer), & ! intent(out): derivatives in the flux w.r.t. matric head or volumetric lquid water in the layer above (m s-1 or s-1) - dq_dHydStateBelow(iLayer), & ! intent(out): derivatives in the flux w.r.t. matric head or volumetric lquid water in the layer below (m s-1 or s-1) - ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) - dq_dNrgStateAbove(iLayer), & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) - dq_dNrgStateBelow(iLayer), & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - ! compute total vertical flux, to compute derivatives - if(deriv_desired .and. ixDerivMethod==numerical)then - select case(itry) - case(unperturbed); scalarFlux = iLayerLiqFluxSoil(iLayer) - case(perturbStateAbove); scalarFlux_dStateAbove = iLayerLiqFluxSoil(iLayer) - case(perturbStateBelow); scalarFlux_dStateBelow = iLayerLiqFluxSoil(iLayer) - case default; err=10; message=trim(message)//"unknown perturbation"; return - end select - end if - - end do ! (looping through different flux calculations -- one or multiple calls depending if desire for numerical or analytical derivatives) - - ! compute numerical derivatives - if(deriv_desired .and. ixDerivMethod==numerical)then - dq_dHydStateAbove(iLayer) = (scalarFlux_dStateAbove - scalarFlux)/dx ! change in drainage flux w.r.t. change in the state in the layer below (m s-1 or s-1) - dq_dHydStateBelow(iLayer) = (scalarFlux_dStateBelow - scalarFlux)/dx ! change in drainage flux w.r.t. change in the state in the layer below (m s-1 or s-1) - end if - - ! check - !if(iLayer==6) write(*,'(a,i4,1x,10(e25.15,1x))') 'iLayer, vectorMatricHeadLiqTrial, iLayerHydCond(iLayer), iLayerLiqFluxSoil(iLayer) = ',& - ! iLayer, vectorMatricHeadLiqTrial, iLayerHydCond(iLayer), iLayerLiqFluxSoil(iLayer) - !if(iLayer==1) write(*,'(a,i4,1x,L1,1x,2(e15.5,1x))') 'iLayer, (ixDerivMethod==numerical), dq_dHydStateBelow(iLayer-1), dq_dHydStateAbove(iLayer) = ', & - ! iLayer, (ixDerivMethod==numerical), dq_dHydStateBelow(iLayer-1), dq_dHydStateAbove(iLayer) - !pause - - end do ! (looping through soil layers) - - ! add infiltration to the upper-most unfrozen layer - ! NOTE: this is done here rather than in surface runoff - !iLayerLiqFluxSoil(ixIce) = iLayerLiqFluxSoil(ixIce) + scalarSurfaceInfiltration - - ! ************************************************************************************************************************************************* - ! ************************************************************************************************************************************************* - - ! ------------------------------------------------------------------------------------------------------------------------------------------------- - ! * compute drainage flux from the bottom of the soil profile, and its derivative - ! ------------------------------------------------------------------------------------------------------------------------------------------------- - - ! define the need to compute drainage - if( .not. (scalarSolution .and. ixTop1 = numerical derivatives with one-sided finite differences) + integer(i4b) :: itry ! index of different flux calculations + integer(i4b),parameter :: unperturbed=0 ! named variable to identify the case of unperturbed state variables + integer(i4b),parameter :: perturbState=1 ! named variable to identify the case where we perturb the state in the current layer + integer(i4b),parameter :: perturbStateAbove=2 ! named variable to identify the case where we perturb the state layer above + integer(i4b),parameter :: perturbStateBelow=3 ! named variable to identify the case where we perturb the state layer below + integer(i4b) :: ixPerturb ! index of element in 2-element vector to perturb + integer(i4b) :: ixOriginal ! index of perturbed element in the original vector + real(rkind) :: scalarVolFracLiqTrial ! trial value of volumetric liquid water content (-) + real(rkind) :: scalarMatricHeadLiqTrial ! trial value of liquid matric head (m) + real(rkind) :: scalarHydCondTrial ! trial value of hydraulic conductivity (m s-1) + real(rkind) :: scalarHydCondMicro ! trial value of hydraulic conductivity of micropores (m s-1) + real(rkind) :: scalarHydCondMacro ! trial value of hydraulic conductivity of macropores (m s-1) + real(rkind) :: scalarFlux ! vertical flux (m s-1) + real(rkind) :: scalarFlux_dStateAbove ! vertical flux with perturbation to the state above (m s-1) + real(rkind) :: scalarFlux_dStateBelow ! vertical flux with perturbation to the state below (m s-1) + ! transpiration sink term + real(rkind),dimension(nSoil) :: mLayerTranspireFrac ! fraction of transpiration allocated to each soil layer (-) + ! diagnostic variables + real(rkind),dimension(nSoil) :: iceImpedeFac ! ice impedence factor at layer mid-points (-) + real(rkind),dimension(nSoil) :: mLayerDiffuse ! diffusivity at layer mid-point (m2 s-1) + real(rkind),dimension(nSoil) :: dHydCond_dVolLiq ! derivative in hydraulic conductivity w.r.t volumetric liquid water content (m s-1) + real(rkind),dimension(nSoil) :: dDiffuse_dVolLiq ! derivative in hydraulic diffusivity w.r.t volumetric liquid water content (m2 s-1) + real(rkind),dimension(nSoil) :: dHydCond_dTemp ! derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) + real(rkind),dimension(0:nSoil) :: iLayerHydCond ! hydraulic conductivity at layer interface (m s-1) + real(rkind),dimension(0:nSoil) :: iLayerDiffuse ! diffusivity at layer interface (m2 s-1) + ! compute surface flux + integer(i4b) :: nRoots ! number of soil layers with roots + integer(i4b) :: ixIce ! index of the lowest soil layer that contains ice + real(rkind),dimension(0:nSoil) :: iLayerHeight ! height of the layer interfaces (m) + ! compute fluxes and derivatives at layer interfaces + real(rkind),dimension(2) :: vectorVolFracLiqTrial ! trial value of volumetric liquid water content (-) + real(rkind),dimension(2) :: vectorMatricHeadLiqTrial ! trial value of liquid matric head (m) + real(rkind),dimension(2) :: vectorHydCondTrial ! trial value of hydraulic conductivity (m s-1) + real(rkind),dimension(2) :: vectorDiffuseTrial ! trial value of hydraulic diffusivity (m2 s-1) + real(rkind) :: scalardPsi_dTheta ! derivative in soil water characteristix, used for perturbations when computing numerical derivatives + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='soilLiqFlx/' + + ! get indices for the data structures + ibeg = indx_data%var(iLookINDEX%nSnow)%dat(1) + 1 + iend = indx_data%var(iLookINDEX%nSnow)%dat(1) + indx_data%var(iLookINDEX%nSoil)%dat(1) + + ! get a copy of iLayerHeight + ! NOTE: performance hit, though cannot define the shape (0:) with the associate construct + iLayerHeight(0:nSoil) = prog_data%var(iLookPROG%iLayerHeight)%dat(ibeg-1:iend) ! height of the layer interfaces (m) + + ! make association between local variables and the information in the data structures + associate(& + ! input: model control + ixDerivMethod => model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): index of the method used to calculate flux derivatives + ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision, & ! intent(in): index of the form of Richards' equation + ixBcUpperSoilHydrology => model_decisions(iLookDECISIONS%bcUpprSoiH)%iDecision, & ! intent(in): index of the upper boundary conditions for soil hydrology + ixBcLowerSoilHydrology => model_decisions(iLookDECISIONS%bcLowrSoiH)%iDecision, & ! intent(in): index of the lower boundary conditions for soil hydrology + ! input: model indices + ixMatricHead => indx_data%var(iLookINDEX%ixMatricHead)%dat, & ! intent(in): indices of soil layers where matric head is the state variable + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat, & ! intent(in): index in the state subset for hydrology state variables in the soil domain + ! input: model coordinate variables -- NOTE: use of ibeg and iend + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat(ibeg:iend), & ! intent(in): depth of the layer (m) + mLayerHeight => prog_data%var(iLookPROG%mLayerHeight)%dat(ibeg:iend), & ! intent(in): height of the layer mid-point (m) + ! input: upper boundary conditions + upperBoundHead => mpar_data%var(iLookPARAM%upperBoundHead)%dat(1), & ! intent(in): upper boundary condition for matric head (m) + upperBoundTheta => mpar_data%var(iLookPARAM%upperBoundTheta)%dat(1), & ! intent(in): upper boundary condition for volumetric liquid water content (-) + ! input: lower boundary conditions + lowerBoundHead => mpar_data%var(iLookPARAM%lowerBoundHead)%dat(1), & ! intent(in): lower boundary condition for matric head (m) + lowerBoundTheta => mpar_data%var(iLookPARAM%lowerBoundTheta)%dat(1), & ! intent(in): lower boundary condition for volumetric liquid water content (-) + ! input: vertically variable soil parameters + vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat, & ! intent(in): van Genutchen "m" parameter (-) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat, & ! intent(in): van Genutchen "n" parameter (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat, & ! intent(in): van Genutchen "alpha" parameter (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat, & ! intent(in): soil residual volumetric water content (-) + ! input: vertically constant soil parameters + wettingFrontSuction => mpar_data%var(iLookPARAM%wettingFrontSuction)%dat(1), & ! intent(in): Green-Ampt wetting front suction (m) + rootingDepth => mpar_data%var(iLookPARAM%rootingDepth)%dat(1), & ! intent(in): rooting depth (m) + kAnisotropic => mpar_data%var(iLookPARAM%kAnisotropic)%dat(1), & ! intent(in): anisotropy factor for lateral hydraulic conductivity (-) + zScale_TOPMODEL => mpar_data%var(iLookPARAM%zScale_TOPMODEL)%dat(1), & ! intent(in): TOPMODEL scaling factor (m) + qSurfScale => mpar_data%var(iLookPARAM%qSurfScale)%dat(1), & ! intent(in): scaling factor in the surface runoff parameterization (-) + f_impede => mpar_data%var(iLookPARAM%f_impede)%dat(1), & ! intent(in): ice impedence factor (-) + soilIceScale => mpar_data%var(iLookPARAM%soilIceScale)%dat(1), & ! intent(in): scaling factor for depth of soil ice, used to get frozen fraction (m) + soilIceCV => mpar_data%var(iLookPARAM%soilIceCV)%dat(1), & ! intent(in): CV of depth of soil ice, used to get frozen fraction (-) + theta_mp => mpar_data%var(iLookPARAM%theta_mp)%dat(1), & ! intent(in): volumetric liquid water content when macropore flow begins (-) + mpExp => mpar_data%var(iLookPARAM%mpExp)%dat(1), & ! intent(in): empirical exponent in macropore flow equation (-) + ! input: saturated hydraulic conductivity + mLayerSatHydCondMP => flux_data%var(iLookFLUX%mLayerSatHydCondMP)%dat, & ! intent(in): saturated hydraulic conductivity of macropores at the mid-point of each layer (m s-1) + mLayerSatHydCond => flux_data%var(iLookFLUX%mLayerSatHydCond)%dat, & ! intent(in): saturated hydraulic conductivity at the mid-point of each layer (m s-1) + iLayerSatHydCond => flux_data%var(iLookFLUX%iLayerSatHydCond)%dat, & ! intent(in): saturated hydraulic conductivity at the interface of each layer (m s-1) + ! input: factors limiting transpiration (from vegFlux routine) + mLayerRootDensity => diag_data%var(iLookDIAG%mLayerRootDensity)%dat, & ! intent(in): root density in each layer (-) + scalarTranspireLim => diag_data%var(iLookDIAG%scalarTranspireLim)%dat(1), & ! intent(in): weighted average of the transpiration limiting factor (-) + mLayerTranspireLim => diag_data%var(iLookDIAG%mLayerTranspireLim)%dat & ! intent(in): transpiration limiting factor in each layer (-) + ) ! associating local variables with the information in the data structures + + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + ! preliminaries + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + + ! numerical derivatives are not implemented yet + if(ixDerivMethod==numerical)then + message=trim(message)//'numerical derivates do not account for the cross derivatives between hydrology and thermodynamics' + err=20; return + end if - ! no dependence on the aquifer for drainage - dq_dHydStateBelow(nSoil) = 0._rkind ! keep this here in case we want to couple some day.... - dq_dNrgStateBelow(nSoil) = 0._rkind ! keep this here in case we want to couple some day.... - - ! print drainage - !print*, 'iLayerLiqFluxSoil(nSoil) = ', iLayerLiqFluxSoil(nSoil) - - endif ! if computing drainage - ! end of drainage section - - ! ***************************************************************************************************************************************************************** - ! ***************************************************************************************************************************************************************** - - ! end association between local variables and the information in the data structures - end associate - - end subroutine soilLiqFlx - - ! *************************************************************************************************************** - ! private subroutine diagv_node: compute transmittance and derivatives for model nodes - ! *************************************************************************************************************** - subroutine diagv_node(& - ! input: model control - deriv_desired, & ! intent(in): flag indicating if derivatives are desired - ixRichards, & ! intent(in): index defining the option for Richards' equation (moisture or mixdform) - ! input: state variables - scalarTempTrial, & ! intent(in): temperature (K) - scalarMatricHeadLiqTrial, & ! intent(in): liquid matric head in a given layer (m) - scalarVolFracLiqTrial, & ! intent(in): volumetric liquid water content in a given soil layer (-) - scalarVolFracIceTrial, & ! intent(in): volumetric ice content in a given soil layer (-) - ! input: pre-computed deriavatives - dTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - dPsiLiq_dTemp, & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) - ! input: soil parameters - vGn_alpha, & ! intent(in): van Genutchen "alpha" parameter (m-1) - vGn_n, & ! intent(in): van Genutchen "n" parameter (-) - vGn_m, & ! intent(in): van Genutchen "m" parameter (-) - mpExp, & ! intent(in): empirical exponent in macropore flow equation (-) - theta_sat, & ! intent(in): soil porosity (-) - theta_res, & ! intent(in): soil residual volumetric water content (-) - theta_mp, & ! intent(in): volumetric liquid water content when macropore flow begins (-) - f_impede, & ! intent(in): ice impedence factor (-) - ! input: saturated hydraulic conductivity - scalarSatHydCond, & ! intent(in): saturated hydraulic conductivity at the mid-point of a given layer (m s-1) - scalarSatHydCondMP, & ! intent(in): saturated hydraulic conductivity of macropores at the mid-point of a given layer (m s-1) - ! output: derivative in the soil water characteristic - scalardPsi_dTheta, & ! derivative in the soil water characteristic - scalardTheta_dPsi, & ! derivative in the soil water characteristic - ! output: transmittance - scalarHydCond, & ! intent(out): hydraulic conductivity at layer mid-points (m s-1) - scalarDiffuse, & ! intent(out): diffusivity at layer mid-points (m2 s-1) - iceImpedeFac, & ! intent(out): ice impedence factor in each layer (-) - ! output: transmittance derivatives - dHydCond_dVolLiq, & ! intent(out): derivative in hydraulic conductivity w.r.t volumetric liquid water content (m s-1) - dDiffuse_dVolLiq, & ! intent(out): derivative in hydraulic diffusivity w.r.t volumetric liquid water content (m2 s-1) - dHydCond_dMatric, & ! intent(out): derivative in hydraulic conductivity w.r.t matric head (m s-1) - dHydCond_dTemp, & ! intent(out): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) - ! output: error control - err,message) ! intent(out): error control - USE soil_utils_module,only:iceImpede ! compute the ice impedence factor - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water as a function of matric head - USE soil_utils_module,only:matricHead ! compute matric head (m) - USE soil_utils_module,only:hydCond_psi ! compute hydraulic conductivity as a function of matric head - USE soil_utils_module,only:hydCond_liq ! compute hydraulic conductivity as a function of volumetric liquid water content - USE soil_utils_module,only:hydCondMP_liq ! compute hydraulic conductivity of macropores as a function of volumetric liquid water content - USE soil_utils_module,only:dTheta_dPsi ! compute derivative of the soil moisture characteristic w.r.t. psi (m-1) - USE soil_utils_module,only:dPsi_dTheta ! compute derivative of the soil moisture characteristic w.r.t. theta (m) - USE soil_utils_module,only:dPsi_dTheta2 ! compute derivative in dPsi_dTheta (m) - USE soil_utils_module,only:dHydCond_dLiq ! compute derivative in hydraulic conductivity w.r.t. volumetric liquid water content (m s-1) - USE soil_utils_module,only:dHydCond_dPsi ! compute derivative in hydraulic conductivity w.r.t. matric head (s-1) - USE soil_utils_module,only:dIceImpede_dTemp ! compute the derivative in the ice impedance factor w.r.t. temperature (K-1) - ! compute hydraulic transmittance and derivatives for all layers - implicit none - ! input: model control - logical(lgt),intent(in) :: deriv_desired ! flag indicating if derivatives are desired - integer(i4b),intent(in) :: ixRichards ! index defining the option for Richards' equation (moisture or mixdform) - ! input: state and diagnostic variables - real(rkind),intent(in) :: scalarTempTrial ! temperature in each layer (K) - real(rkind),intent(in) :: scalarMatricHeadLiqTrial ! liquid matric head in each layer (m) - real(rkind),intent(in) :: scalarVolFracLiqTrial ! volumetric fraction of liquid water in a given layer (-) - real(rkind),intent(in) :: scalarVolFracIceTrial ! volumetric fraction of ice in a given layer (-) - ! input: pre-computed deriavatives - real(rkind),intent(in) :: dTheta_dTk ! derivative in volumetric liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: dPsiLiq_dTemp ! derivative in liquid water matric potential w.r.t. temperature (m K-1) - ! input: soil parameters - real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter (m-1) - real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter (-) - real(rkind),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) - real(rkind),intent(in) :: mpExp ! empirical exponent in macropore flow equation (-) - real(rkind),intent(in) :: theta_sat ! soil porosity (-) - real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) - real(rkind),intent(in) :: theta_mp ! volumetric liquid water content when macropore flow begins (-) - real(rkind),intent(in) :: f_impede ! ice impedence factor (-) - ! input: saturated hydraulic conductivity - real(rkind),intent(in) :: scalarSatHydCond ! saturated hydraulic conductivity at the mid-point of a given layer (m s-1) - real(rkind),intent(in) :: scalarSatHydCondMP ! saturated hydraulic conductivity of macropores at the mid-point of a given layer (m s-1) - ! output: derivative in the soil water characteristic - real(rkind),intent(out) :: scalardPsi_dTheta ! derivative in the soil water characteristic - real(rkind),intent(out) :: scalardTheta_dPsi ! derivative in the soil water characteristic - ! output: transmittance - real(rkind),intent(out) :: scalarHydCond ! hydraulic conductivity at layer mid-points (m s-1) - real(rkind),intent(out) :: scalarDiffuse ! diffusivity at layer mid-points (m2 s-1) - real(rkind),intent(out) :: iceImpedeFac ! ice impedence factor in each layer (-) - ! output: transmittance derivatives - real(rkind),intent(out) :: dHydCond_dVolLiq ! derivative in hydraulic conductivity w.r.t volumetric liquid water content (m s-1) - real(rkind),intent(out) :: dDiffuse_dVolLiq ! derivative in hydraulic diffusivity w.r.t volumetric liquid water content (m2 s-1) - real(rkind),intent(out) :: dHydCond_dMatric ! derivative in hydraulic conductivity w.r.t matric head (s-1) - real(rkind),intent(out) :: dHydCond_dTemp ! derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! local variables - real(rkind) :: localVolFracLiq ! local volumetric fraction of liquid water - real(rkind) :: scalarHydCondMP ! hydraulic conductivity of macropores at layer mid-points (m s-1) - real(rkind) :: dIceImpede_dT ! derivative in ice impedance factor w.r.t. temperature (K-1) - real(rkind) :: dHydCondMacro_dVolLiq ! derivative in hydraulic conductivity of macropores w.r.t volumetric liquid water content (m s-1) - real(rkind) :: dHydCondMacro_dMatric ! derivative in hydraulic conductivity of macropores w.r.t matric head (s-1) - real(rkind) :: dHydCondMicro_dMatric ! derivative in hydraulic conductivity of micropores w.r.t matric head (s-1) - real(rkind) :: dHydCondMicro_dTemp ! derivative in hydraulic conductivity of micropores w.r.t temperature (m s-1 K-1) - real(rkind) :: dPsi_dTheta2a ! derivative in dPsi_dTheta (analytical) - real(rkind) :: dIceImpede_dLiq ! derivative in ice impedence factor w.r.t. volumetric liquid water content (-) - real(rkind) :: hydCond_noIce ! hydraulic conductivity in the absence of ice (m s-1) - real(rkind) :: dK_dLiq__noIce ! derivative in hydraulic conductivity w.r.t volumetric liquid water content, in the absence of ice (m s-1) - real(rkind) :: dK_dPsi__noIce ! derivative in hydraulic conductivity w.r.t matric head, in the absence of ice (s-1) - real(rkind) :: relSatMP ! relative saturation of macropores (-) - ! local variables to test the derivative - logical(lgt),parameter :: testDeriv=.false. ! local flag to test the derivative - real(rkind) :: xConst ! LH_fus/(gravity*Tfreeze), used in freezing point depression equation (m K-1) - real(rkind) :: vTheta ! volumetric fraction of total water (-) - real(rkind) :: volLiq ! volumetric fraction of liquid water (-) - real(rkind) :: volIce ! volumetric fraction of ice (-) - real(rkind) :: volFracLiq1,volFracLiq2 ! different trial values of volumetric liquid water content (-) - real(rkind) :: effSat ! effective saturation (-) - real(rkind) :: psiLiq ! liquid water matric potential (m) - real(rkind) :: hydCon ! hydraulic conductivity (m s-1) - real(rkind) :: hydIce ! hydraulic conductivity after accounting for ice impedance (-) - real(rkind),parameter :: dx = 1.e-8_rkind ! finite difference increment (m) - ! initialize error control - err=0; message="diagv_node/" - - ! ***** - ! compute the derivative in the soil water characteristic - select case(ixRichards) - case(moisture) - scalardPsi_dTheta = dPsi_dTheta(scalarVolFracLiqTrial,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - scalardTheta_dPsi = realMissing ! (deliberately cause problems if this is ever used) - case(mixdform) - scalardTheta_dPsi = dTheta_dPsi(scalarMatricHeadLiqTrial,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - scalardPsi_dTheta = dPsi_dTheta(scalarVolFracLiqTrial,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if(testDeriv)then - volFracLiq1 = volFracLiq(scalarMatricHeadLiqTrial, vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - volFracLiq2 = volFracLiq(scalarMatricHeadLiqTrial+dx,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - end if ! (testing the derivative) - case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return - end select - - ! ***** - ! compute hydraulic conductivity and its derivative in each soil layer - - ! compute the ice impedence factor and its derivative w.r.t. volumetric liquid water content (-) - call iceImpede(scalarVolFracIceTrial,f_impede, & ! input - iceImpedeFac,dIceImpede_dLiq) ! output - - select case(ixRichards) - ! ***** moisture-based form of Richards' equation - case(moisture) - ! haven't included macropores yet - err=20; message=trim(message)//'still need to include macropores for the moisture-based form of Richards eqn'; return - ! compute the hydraulic conductivity (m s-1) and diffusivity (m2 s-1) for a given layer - hydCond_noIce = hydCond_liq(scalarVolFracLiqTrial,scalarSatHydCond,theta_res,theta_sat,vGn_m) - scalarHydCond = hydCond_noIce*iceImpedeFac - scalarDiffuse = scalardPsi_dTheta * scalarHydCond - ! compute derivative in hydraulic conductivity (m s-1) and hydraulic diffusivity (m2 s-1) - if(deriv_desired)then - if(scalarVolFracIceTrial > epsilon(iceImpedeFac))then - dK_dLiq__noIce = dHydCond_dLiq(scalarVolFracLiqTrial,scalarSatHydCond,theta_res,theta_sat,vGn_m,.true.) ! [.true. = analytical] - dHydCond_dVolLiq = hydCond_noIce*dIceImpede_dLiq + dK_dLiq__noIce*iceImpedeFac + ! check the need to compute analytical derivatives + if(deriv_desired .and. ixDerivMethod==analytical)then + desireAnal = .true. else - dHydCond_dVolLiq = dHydCond_dLiq(scalarVolFracLiqTrial,scalarSatHydCond,theta_res,theta_sat,vGn_m,.true.) + desireAnal = .false. end if - dPsi_dTheta2a = dPsi_dTheta2(scalarVolFracLiqTrial,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m,.true.) ! [.true. = analytical] compute derivative in dPsi_dTheta (m) - dDiffuse_dVolLiq = dHydCond_dVolLiq*scalardPsi_dTheta + scalarHydCond*dPsi_dTheta2a - dHydCond_dMatric = realMissing ! not used, so cause problems - end if - - ! ***** mixed form of Richards' equation -- just compute hydraulic condictivity - case(mixdform) - ! compute the hydraulic conductivity (m s-1) and diffusivity (m2 s-1) for a given layer - hydCond_noIce = hydCond_psi(scalarMatricHeadLiqTrial,scalarSatHydCond,vGn_alpha,vGn_n,vGn_m) - scalarDiffuse = realMissing ! not used, so cause problems - ! compute the hydraulic conductivity of macropores (m s-1) - localVolFracLiq = volFracLiq(scalarMatricHeadLiqTrial,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - scalarHydCondMP = hydCondMP_liq(localVolFracLiq,theta_sat,theta_mp,mpExp,scalarSatHydCondMP,scalarSatHydCond) - scalarHydCond = hydCond_noIce*iceImpedeFac + scalarHydCondMP - - ! compute derivative in hydraulic conductivity (m s-1) - if(deriv_desired)then - ! (compute derivative for macropores) - if(localVolFracLiq > theta_mp)then - relSatMP = (localVolFracLiq - theta_mp)/(theta_sat - theta_mp) - dHydCondMacro_dVolLiq = ((scalarSatHydCondMP - scalarSatHydCond)/(theta_sat - theta_mp))*mpExp*(relSatMP**(mpExp - 1._rkind)) - dHydCondMacro_dMatric = scalardTheta_dPsi*dHydCondMacro_dVolLiq + + ! check the need to compute numerical derivatives + if(deriv_desired .and. ixDerivMethod==numerical)then + nFlux=3 ! compute the derivatives using one-sided finite differences else - dHydCondMacro_dVolLiq = 0._rkind - dHydCondMacro_dMatric = 0._rkind + nFlux=0 ! compute analytical derivatives end if - ! (compute derivatives for micropores) - if(scalarVolFracIceTrial > verySmall)then - dK_dPsi__noIce = dHydCond_dPsi(scalarMatricHeadLiqTrial,scalarSatHydCond,vGn_alpha,vGn_n,vGn_m,.true.) ! analytical - dHydCondMicro_dTemp = dPsiLiq_dTemp*dK_dPsi__noIce ! m s-1 K-1 - dHydCondMicro_dMatric = hydCond_noIce*dIceImpede_dLiq*scalardTheta_dPsi + dK_dPsi__noIce*iceImpedeFac + + ! get the indices for the soil layers + if(scalarSolution)then + ixLayerDesired = pack(ixMatricHead, ixSoilOnlyHyd/=integerMissing) + ixTop = ixLayerDesired(1) + ixBot = ixLayerDesired(1) else - dHydCondMicro_dTemp = 0._rkind - dHydCondMicro_dMatric = dHydCond_dPsi(scalarMatricHeadLiqTrial,scalarSatHydCond,vGn_alpha,vGn_n,vGn_m,.true.) + ixTop = 1 + ixBot = nSoil + endif + + ! identify the number of layers that contain roots + nRoots = count(iLayerHeight(0:nSoil-1) < rootingDepth-verySmall) + if(nRoots==0)then + message=trim(message)//'no layers with roots' + err=20; return + endif + + ! identify lowest soil layer with ice + ! NOTE: cannot use count because there may be an unfrozen wedge + ixIce = 0 ! initialize the index of the ice layer (0 means no ice in the soil profile) + do iLayer=1,nSoil ! (loop through soil layers) + if(mLayerVolFracIceTrial(iLayer) > verySmall) ixIce = iLayer + end do + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + ! compute the transpiration sink term + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + + ! check the need to compute transpiration (NOTE: intent=inout) + if( .not. (scalarSolution .and. ixTop>1) )then + + ! compute the fraction of transpiration loss from each soil layer + if(scalarTranspireLim > tiny(scalarTranspireLim))then ! (transpiration may be non-zero even if the soil moisture limiting factor is zero) + mLayerTranspireFrac(:) = mLayerRootDensity(:)*mLayerTranspireLim(:)/scalarTranspireLim + else ! (possible for there to be non-zero conductance and therefore transpiration in this case) + mLayerTranspireFrac(:) = mLayerRootDensity(:) / sum(mLayerRootDensity) + end if + + ! check fractions sum to one + if(abs(sum(mLayerTranspireFrac) - 1._rkind) > verySmall)then + message=trim(message)//'fraction transpiration in soil layers does not sum to one' + err=20; return + endif + + ! compute transpiration loss from each soil layer (kg m-2 s-1 --> m s-1) + mLayerTranspire(:) = mLayerTranspireFrac(:)*scalarCanopyTranspiration/iden_water + ! derivatives in transpiration w.r.t. canopy state variables + mLayerdTrans_dCanWat(:) = mLayerTranspireFrac(:)*dCanopyTrans_dCanWat /iden_water + mLayerdTrans_dTCanair(:) = mLayerTranspireFrac(:)*dCanopyTrans_dTCanair/iden_water + mLayerdTrans_dTCanopy(:) = mLayerTranspireFrac(:)*dCanopyTrans_dTCanopy/iden_water + mLayerdTrans_dTGround(:) = mLayerTranspireFrac(:)*dCanopyTrans_dTGround/iden_water + + ! special case of prescribed head -- no transpiration + if(ixBcUpperSoilHydrology==prescribedHead) then + mLayerTranspire(:) = 0._rkind + ! derivatives in transpiration w.r.t. canopy state variables + mLayerdTrans_dCanWat(:) = 0._rkind + mLayerdTrans_dTCanair(:)= 0._rkind + mLayerdTrans_dTCanopy(:)= 0._rkind + mLayerdTrans_dTGround(:)= 0._rkind + endif + + endif ! if need to compute transpiration + + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + ! compute diagnostic variables at the nodes throughout the soil profile + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + do iSoil=ixTop,min(ixBot+1,nSoil) ! (loop through soil layers) + + call diagv_node(& + ! input: model control + desireAnal, & ! intent(in): flag indicating if derivatives are desired + ixRichards, & ! intent(in): index defining the option for Richards' equation (moisture or mixdform) + ! input: state variables + mLayerTempTrial(iSoil), & ! intent(in): temperature (K) + mLayerMatricHeadLiqTrial(iSoil), & ! intent(in): liquid matric head in each layer (m) + mLayerVolFracLiqTrial(iSoil), & ! intent(in): volumetric liquid water content in each soil layer (-) + mLayerVolFracIceTrial(iSoil), & ! intent(in): volumetric ice content in each soil layer (-) + ! input: pre-computed deriavatives + mLayerdTheta_dTk(iSoil), & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + dPsiLiq_dTemp(iSoil), & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) + ! input: soil parameters + vGn_alpha(iSoil), & ! intent(in): van Genutchen "alpha" parameter (m-1) + vGn_n(iSoil), & ! intent(in): van Genutchen "n" parameter (-) + vGn_m(iSoil), & ! intent(in): van Genutchen "m" parameter (-) + mpExp, & ! intent(in): empirical exponent in macropore flow equation (-) + theta_sat(iSoil), & ! intent(in): soil porosity (-) + theta_res(iSoil), & ! intent(in): soil residual volumetric water content (-) + theta_mp, & ! intent(in): volumetric liquid water content when macropore flow begins (-) + f_impede, & ! intent(in): ice impedence factor (-) + ! input: saturated hydraulic conductivity + mLayerSatHydCond(iSoil), & ! intent(in): saturated hydraulic conductivity at the mid-point of each layer (m s-1) + mLayerSatHydCondMP(iSoil), & ! intent(in): saturated hydraulic conductivity of macropores at the mid-point of each layer (m s-1) + ! output: derivative in the soil water characteristic + mLayerdPsi_dTheta(iSoil), & ! intent(out): derivative in the soil water characteristic + mLayerdTheta_dPsi(iSoil), & ! intent(out): derivative in the soil water characteristic + ! output: transmittance + mLayerHydCond(iSoil), & ! intent(out): hydraulic conductivity at layer mid-points (m s-1) + mLayerDiffuse(iSoil), & ! intent(out): diffusivity at layer mid-points (m2 s-1) + iceImpedeFac(iSoil), & ! intent(out): ice impedence factor in each layer (-) + ! output: transmittance derivatives + dHydCond_dVolLiq(iSoil), & ! intent(out): derivative in hydraulic conductivity w.r.t volumetric liquid water content (m s-1) + dDiffuse_dVolLiq(iSoil), & ! intent(out): derivative in hydraulic diffusivity w.r.t volumetric liquid water content (m2 s-1) + dHydCond_dMatric(iSoil), & ! intent(out): derivative in hydraulic conductivity w.r.t matric head (m s-1) + dHydCond_dTemp(iSoil), & ! intent(out): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + end do ! (looping through soil layers) + + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + ! compute infiltraton at the surface and its derivative w.r.t. mass in the upper soil layer + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + + ! set derivative w.r.t. state above to zero (does not exist) + dq_dHydStateAbove(0) = 0._rkind + dq_dNrgStateAbove(0) = 0._rkind + + ! either one or multiple flux calls, depending on if using analytical or numerical derivatives + do itry=nFlux,0,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) + + ! ===== + ! get input state variables... + ! ============================ + ! identify the type of perturbation + ! Currently we are ignoring the perturbations in the non-surface layers and with temperature + select case(itry) + + ! skip undesired perturbations + case(perturbStateAbove); cycle ! cannot perturb state above (does not exist) -- so keep cycling + case(perturbState); cycle ! perturbing the layer below the flux at the top interface + + ! un-perturbed case + case(unperturbed) + scalarVolFracLiqTrial = mLayerVolFracLiqTrial(1) + scalarMatricHeadLiqTrial = mLayerMatricHeadLiqTrial(1) + + ! perturb soil state (one-sided finite differences) + case(perturbStateBelow) + ! (perturbation depends on the form of Richards' equation) + select case(ixRichards) + case(moisture) + scalarVolFracLiqTrial = mLayerVolFracLiqTrial(1) + dx + scalarMatricHeadLiqTrial = mLayerMatricHeadLiqTrial(1) + case(mixdform) + scalarVolFracLiqTrial = mLayerVolFracLiqTrial(1) + scalarMatricHeadLiqTrial = mLayerMatricHeadLiqTrial(1) + dx + case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return + end select ! (form of Richards' equation + ! check for an unknown perturbation + case default; err=10; message=trim(message)//"unknown perturbation"; return + + end select ! (type of perturbation) + + ! ===== + ! compute surface flux and its derivative... + ! ========================================== + + call surfaceFlx(& + ! input: model control + doInfiltrate, & ! intent(in): flag indicating if desire to compute infiltration + desireAnal, & ! intent(in): flag indicating if derivatives are desired + ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) + ixBcUpperSoilHydrology, & ! intent(in): index defining the type of boundary conditions (neumann or diriclet) + nRoots, & ! intent(in): number of layers that contain roots + ixIce, & ! intent(in): index of lowest ice layer + nSoil, & ! intent(in): number of soil layers + ! input: state variables + mLayerTempTrial, & ! intent(in): temperature (K) + scalarMatricHeadLiqTrial, & ! intent(in): liquid matric head in the upper-most soil layer (m) + mLayerMatricHeadLiqTrial, & ! intent(in): liquid matric head in each soil layer (m) + scalarVolFracLiqTrial, & ! intent(in): volumetric liquid water content the upper-most soil layer (-) + mLayerVolFracLiqTrial, & ! intent(in): volumetric liquid water content in each soil layer (-) + mLayerVolFracIceTrial, & ! intent(in): volumetric ice content in each soil layer (-) + ! input: pre-computed deriavatives + mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) + mLayerdPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) + above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) + ! input: depth of upper-most soil layer (m) + mLayerDepth, & ! intent(in): depth of each soil layer (m) + iLayerHeight, & ! intent(in): height at the interface of each layer (m) + ! input: boundary conditions + upperBoundHead, & ! intent(in): upper boundary condition (m) + upperBoundTheta, & ! intent(in): upper boundary condition (-) + ! input: flux at the upper boundary + scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) + ! input: transmittance + iLayerSatHydCond(0), & ! intent(in): saturated hydraulic conductivity at the surface (m s-1) + dHydCond_dTemp(1), & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) + iceImpedeFac(1), & ! intent(in): ice impedence factor in the upper-most soil layer (-) + ! input: soil parameters + vGn_alpha(1), & ! intent(in): van Genutchen "alpha" parameter (m-1) + vGn_n(1), & ! intent(in): van Genutchen "n" parameter (-) + vGn_m(1), & ! intent(in): van Genutchen "m" parameter (-) + theta_sat(1), & ! intent(in): soil porosity (-) + theta_res(1), & ! intent(in): soil residual volumetric water content (-) + qSurfScale, & ! intent(in): scaling factor in the surface runoff parameterization (-) + zScale_TOPMODEL, & ! intent(in): scaling factor used to describe decrease in hydraulic conductivity with depth (m) + rootingDepth, & ! intent(in): rooting depth (m) + wettingFrontSuction, & ! intent(in): Green-Ampt wetting front suction (m) + soilIceScale, & ! intent(in): soil ice scaling factor in Gamma distribution used to define frozen area (m) + soilIceCV, & ! intent(in): soil ice CV in Gamma distribution used to define frozen area (-) + ! input-output: hydraulic conductivity and diffusivity at the surface + iLayerHydCond(0), & ! intent(inout): hydraulic conductivity at the surface (m s-1) + iLayerDiffuse(0), & ! intent(inout): hydraulic diffusivity at the surface (m2 s-1) + ! input-output: fluxes at layer interfaces and surface runoff + xMaxInfilRate, & ! intent(inout): maximum infiltration rate (m s-1) + scalarInfilArea, & ! intent(inout): fraction of unfrozen area where water can infiltrate (-) + scalarFrozenArea, & ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) + scalarSurfaceRunoff, & ! intent(out): surface runoff (m s-1) + scalarSurfaceInfiltration, & ! intent(out): surface infiltration (m s-1) + ! input-output: deriavtives in surface infiltration w.r.t. volumetric liquid water (m s-1) and matric head (s-1) in the upper-most soil layer + dq_dHydStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) + dq_dNrgStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + ! include base soil evaporation as the upper boundary flux + iLayerLiqFluxSoil(0) = scalarGroundEvaporation/iden_water + scalarSurfaceInfiltration + + ! get copies of surface flux to compute numerical derivatives + if(deriv_desired .and. ixDerivMethod==numerical)then + select case(itry) + case(unperturbed); scalarFlux = iLayerLiqFluxSoil(0) + case(perturbStateBelow); scalarFlux_dStateBelow = iLayerLiqFluxSoil(0) + case default; err=10; message=trim(message)//"unknown perturbation"; return + end select + end if + end do ! (looping through different flux calculations -- one or multiple calls depending if desire for numerical or analytical derivatives) + + dq_dHydStateBelow(0) = 0._rkind ! contribution will be in dq_dHydStateLayerSurfVec(1) + dq_dNrgStateBelow(0) = 0._rkind ! contribution will be in dq_dNrgStateLayerSurfVec(1) + + ! compute numerical derivatives + if(deriv_desired .and. ixDerivMethod==numerical)then + dq_dHydStateLayerSurfVec(1) = (scalarFlux_dStateBelow - scalarFlux)/dx ! change in surface flux w.r.t. change in the soil moisture in the top soil layer (m s-1) + dq_dHydStateLayerSurfVec(1) = 0._rkind ! Did not yet compute this perturbation end if - ! (combine derivatives) - dHydCond_dMatric = dHydCondMicro_dMatric + dHydCondMacro_dMatric - - ! (compute analytical derivative for change in ice impedance factor w.r.t. temperature) - call dIceImpede_dTemp(scalarVolFracIceTrial, & ! intent(in): trial value of volumetric ice content (-) - dTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - f_impede, & ! intent(in): ice impedance parameter (-) - dIceImpede_dT ) ! intent(out): derivative in ice impedance factor w.r.t. temperature (K-1) - ! (compute derivative in hydraulic conductivity w.r.t. temperature) - dHydCond_dTemp = hydCond_noIce*dIceImpede_dT + dHydCondMicro_dTemp*iceImpedeFac - ! (test derivative) - if(testDeriv)then - xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) - vTheta = scalarVolFracIceTrial + scalarVolFracLiqTrial - volLiq = volFracLiq(xConst*(scalarTempTrial+dx - Tfreeze),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - volIce = vTheta - volLiq - effSat = (volLiq - theta_res)/(theta_sat - volIce - theta_res) - psiLiq = matricHead(effSat,vGn_alpha,0._rkind,1._rkind,vGn_n,vGn_m) ! use effective saturation, so theta_res=0 and theta_sat=1 - hydCon = hydCond_psi(psiLiq,scalarSatHydCond,vGn_alpha,vGn_n,vGn_m) - call iceImpede(volIce,f_impede,iceImpedeFac,dIceImpede_dLiq) - hydIce = hydCon*iceImpedeFac - print*, 'test derivative: ', (psiLiq - scalarMatricHeadLiqTrial)/dx, dPsiLiq_dTemp - print*, 'test derivative: ', (hydCon - hydCond_noIce)/dx, dHydCondMicro_dTemp - print*, 'test derivative: ', (hydIce - scalarHydCond)/dx, dHydCond_dTemp - print*, 'press any key to continue'; read(*,*) ! (alternative to the deprecated 'pause' statement) - end if ! testing the derivative - ! (set values that are not used to missing) - dHydCond_dVolLiq = realMissing ! not used, so cause problems - dDiffuse_dVolLiq = realMissing ! not used, so cause problems - end if - - case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return - - end select - - ! if derivatives are not desired, then set values to missing - if(.not.deriv_desired)then - dHydCond_dVolLiq = realMissing ! not used, so cause problems - dDiffuse_dVolLiq = realMissing ! not used, so cause problems - dHydCond_dMatric = realMissing ! not used, so cause problems - end if - - end subroutine diagv_node - - - ! *************************************************************************************************************** - ! private subroutine surfaceFlx: compute the surface flux and its derivative - ! *************************************************************************************************************** - subroutine surfaceFlx(& - ! input: model control - doInfiltration, & ! intent(in): flag indicating if desire to compute infiltration - deriv_desired, & ! intent(in): flag indicating if derivatives are desired - ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) - bc_upper, & ! intent(in): index defining the type of boundary conditions (neumann or diriclet) - nRoots, & ! intent(in): number of layers that contain roots - ixIce, & ! intent(in): index of lowest ice layer - nSoil, & ! intent(in): number of soil layers - ! input: state variables - mLayerTemp, & ! intent(in): temperature (K) - scalarMatricHead, & ! intent(in): matric head in the upper-most soil layer (m) - mLayerMatricHead, & ! intent(in): matric head in each soil layer (m) - scalarVolFracLiq, & ! intent(in): volumetric liquid water content in the upper-most soil layer (-) - mLayerVolFracLiq, & ! intent(in): volumetric liquid water content in each soil layer (-) - mLayerVolFracIce, & ! intent(in): volumetric ice content in each soil layer (-) - ! input: pre-computed derivatives - dTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - dTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) - mLayerdPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) - above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water - above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature - above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) - ! input: depth of upper-most soil layer (m) - mLayerDepth, & ! intent(in): depth of each soil layer (m) - iLayerHeight, & ! intent(in): height at the interface of each layer (m) - ! input: boundary conditions - upperBoundHead, & ! intent(in): upper boundary condition (m) - upperBoundTheta, & ! intent(in): upper boundary condition (-) - ! input: flux at the upper boundary - scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) - ! input: transmittance - surfaceSatHydCond, & ! intent(in): saturated hydraulic conductivity at the surface (m s-1) - dHydCond_dTemp, & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) - iceImpedeFac, & ! intent(in): ice impedence factor in the upper-most soil layer (-) - ! input: soil parameters - vGn_alpha, & ! intent(in): van Genutchen "alpha" parameter (m-1) - vGn_n, & ! intent(in): van Genutchen "n" parameter (-) - vGn_m, & ! intent(in): van Genutchen "m" parameter (-) - theta_sat, & ! intent(in): soil porosity (-) - theta_res, & ! intent(in): soil residual volumetric water content (-) - qSurfScale, & ! intent(in): scaling factor in the surface runoff parameterization (-) - zScale_TOPMODEL, & ! intent(in): scaling factor used to describe decrease in hydraulic conductivity with depth (m) - rootingDepth, & ! intent(in): rooting depth (m) - wettingFrontSuction, & ! intent(in): Green-Ampt wetting front suction (m) - soilIceScale, & ! intent(in): soil ice scaling factor in Gamma distribution used to define frozen area (m) - soilIceCV, & ! intent(in): soil ice CV in Gamma distribution used to define frozen area (-) - ! input-output: hydraulic conductivity and diffusivity at the surface - surfaceHydCond, & ! intent(inout): hydraulic conductivity at the surface (m s-1) - surfaceDiffuse, & ! intent(inout): hydraulic diffusivity at the surface (m2 s-1) - ! input-output: fluxes at layer interfaces and surface runoff - xMaxInfilRate, & ! intent(inout): maximum infiltration rate (m s-1) - scalarInfilArea, & ! intent(inout): fraction of unfrozen area where water can infiltrate (-) - scalarFrozenArea, & ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) - scalarSurfaceRunoff, & ! intent(out): surface runoff (m s-1) - scalarSurfaceInfiltration, & ! intent(out): surface infiltration (m s-1) - ! input-output: deriavtives in surface infiltration w.r.t. volumetric liquid water (m s-1) and matric head (s-1) in the upper-most soil layer - dq_dHydStateVec, & ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) - dq_dNrgStateVec, & ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) - ! output: error control - err,message) ! intent(out): error control - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water as a function of matric head (-) - USE soil_utils_module,only:hydCond_psi ! compute hydraulic conductivity as a function of matric head (m s-1) - USE soil_utils_module,only:hydCond_liq ! compute hydraulic conductivity as a function of volumetric liquid water content (m s-1) - USE soil_utils_module,only:dPsi_dTheta ! compute derivative of the soil moisture characteristic w.r.t. theta (m) - USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists - USE soil_utils_module,only:gammp ! compute the cumulative probabilty based on the Gamma distribution - ! compute infiltraton at the surface and its derivative w.r.t. mass in the upper soil layer - implicit none - ! ----------------------------------------------------------------------------------------------------------------------------- - ! input: model control - logical(lgt),intent(in) :: doInfiltration ! flag indicating if desire to compute infiltration - logical(lgt),intent(in) :: deriv_desired ! flag to indicate if derivatives are desired - integer(i4b),intent(in) :: bc_upper ! index defining the type of boundary conditions - integer(i4b),intent(in) :: ixRichards ! index defining the option for Richards' equation (moisture or mixdform) - integer(i4b),intent(in) :: nRoots ! number of layers that contain roots - integer(i4b),intent(in) :: ixIce ! index of lowest ice layer - integer(i4b),intent(in) :: nSoil ! number of soil layers - ! input: state and diagnostic variables - real(rkind),intent(in) :: mLayerTemp(:) ! temperature (K) - real(rkind),intent(in) :: scalarMatricHead ! matric head in the upper-most soil layer (m) - real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head in each soil layer (m) - real(rkind),intent(in) :: scalarVolFracLiq ! volumetric liquid water content in the upper-most soil layer (-) - real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric liquid water content in each soil layer (-) - real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric ice content in each soil layer (-) - ! input: pre-computed derivatives, note all of these would need to be recomputed if wanted a numerical derivative - real(rkind),intent(in) :: dTheta_dTk(:) ! derivative in volumetric liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: dTheta_dPsi(:) ! derivative in the soil water characteristic w.r.t. psi (m-1) - real(rkind),intent(in) :: mLayerdPsi_dTheta(:) ! derivative in the soil water characteristic w.r.t. theta (m) - real(rkind),intent(in) :: above_soilLiqFluxDeriv ! derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water - real(rkind),intent(in) :: above_soildLiq_dTk ! derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature - real(rkind),intent(in) :: above_soilFracLiq ! fraction of liquid water layer above soil (canopy or snow) (-) - ! input: depth of upper-most soil layer (m) - real(rkind),intent(in) :: mLayerDepth(:) ! depth of upper-most soil layer (m) - real(rkind),intent(in) :: iLayerHeight(0:) ! height at the interface of each layer (m) - ! input: diriclet boundary conditions - real(rkind),intent(in) :: upperBoundHead ! upper boundary condition for matric head (m) - real(rkind),intent(in) :: upperBoundTheta ! upper boundary condition for volumetric liquid water content (-) - ! input: flux at the upper boundary - real(rkind),intent(in) :: scalarRainPlusMelt ! rain plus melt, used as input to the soil zone before computing surface runoff (m s-1) - ! input: transmittance - real(rkind),intent(in) :: surfaceSatHydCond ! saturated hydraulic conductivity at the surface (m s-1) - real(rkind),intent(in) :: dHydCond_dTemp ! derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) - real(rkind),intent(in) :: iceImpedeFac ! ice impedence factor in the upper-most soil layer (-) - ! input: soil parameters - real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter (m-1) - real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter (-) - real(rkind),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) - real(rkind),intent(in) :: theta_sat ! soil porosity (-) - real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) - real(rkind),intent(in) :: qSurfScale ! scaling factor in the surface runoff parameterization (-) - real(rkind),intent(in) :: zScale_TOPMODEL ! scaling factor used to describe decrease in hydraulic conductivity with depth (m) - real(rkind),intent(in) :: rootingDepth ! rooting depth (m) - real(rkind),intent(in) :: wettingFrontSuction ! Green-Ampt wetting front suction (m) - real(rkind),intent(in) :: soilIceScale ! soil ice scaling factor in Gamma distribution used to define frozen area (m) - real(rkind),intent(in) :: soilIceCV ! soil ice CV in Gamma distribution used to define frozen area (-) - ! ----------------------------------------------------------------------------------------------------------------------------- - ! input-output: hydraulic conductivity and diffusivity at the surface - ! NOTE: intent(inout) because infiltration may only be computed for the first iteration - real(rkind),intent(inout) :: surfaceHydCond ! hydraulic conductivity (m s-1) - real(rkind),intent(inout) :: surfaceDiffuse ! hydraulic diffusivity at the surface (m - ! output: surface runoff and infiltration flux (m s-1) - real(rkind),intent(inout) :: xMaxInfilRate ! maximum infiltration rate (m s-1) - real(rkind),intent(inout) :: scalarInfilArea ! fraction of unfrozen area where water can infiltrate (-) - real(rkind),intent(inout) :: scalarFrozenArea ! fraction of area that is considered impermeable due to soil ice (-) - real(rkind),intent(out) :: scalarSurfaceRunoff ! surface runoff (m s-1) - real(rkind),intent(out) :: scalarSurfaceInfiltration ! surface infiltration (m s-1) - ! output: derivatives in surface infiltration w.r.t. states in above soil snow or canopy and every soil layer - real(rkind),intent(out) :: dq_dHydStateVec(0:) ! derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) - real(rkind),intent(out) :: dq_dNrgStateVec(0:) ! derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ----------------------------------------------------------------------------------------------------------------------------- - ! local variables - ! (general) - integer(i4b) :: iLayer ! index of soil layer - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - real(rkind) :: fpart1,fpart2 ! different parts of a function - real(rkind) :: dpart1(1:nSoil),dpart2(1:nSoil) ! derivatives for different parts of a function - real(rkind) :: dfracCap(1:nSoil),dfInfRaw(1:nSoil) ! derivatives for different parts of a function -! (head boundary condition) - real(rkind) :: cFlux ! capillary flux (m s-1) - real(rkind) :: dNum ! numerical derivative - ! (simplified Green-Ampt infiltration) - real(rkind) :: rootZoneLiq ! depth of liquid water in the root zone (m) - real(rkind) :: rootZoneIce ! depth of ice in the root zone (m) - real(rkind) :: availCapacity ! available storage capacity in the root zone (m) - real(rkind) :: depthWettingFront ! depth to the wetting front (m) - real(rkind) :: hydCondWettingFront ! hydraulic conductivity at the wetting front (m s-1) - ! (saturated area associated with variable storage capacity) - real(rkind) :: fracCap ! fraction of pore space filled with liquid water and ice (-) - real(rkind) :: fInfRaw ! infiltrating area before imposing solution constraints (-) - real(rkind),parameter :: maxFracCap=0.995_rkind ! maximum fraction capacity -- used to avoid numerical problems associated with an enormous derivative - real(rkind),parameter :: scaleFactor=0.000001_rkind ! scale factor for the smoothing function (-) - real(rkind),parameter :: qSurfScaleMax=1000._rkind ! maximum surface runoff scaling factor (-) - ! (fraction of impermeable area associated with frozen ground) - real(rkind) :: alpha ! shape parameter in the Gamma distribution - real(rkind) :: xLimg ! upper limit of the integral - ! (derivatives) - real(rkind) :: dVolFracLiq_dWat(1:nSoil) ! derivative in vol fraction of liquid w.r.t. water state variable in root layers - real(rkind) :: dVolFracIce_dWat(1:nSoil) ! derivative in vol fraction of ice w.r.t. water state variable in root layers - real(rkind) :: dVolFracLiq_dTk(1:nSoil) ! derivative in vol fraction of liquid w.r.t. temperature in root layers - real(rkind) :: dVolFracIce_dTk(1:nSoil) ! derivative in vol fraction of ice w.r.t. temperature in root layers - real(rkind) :: dRootZoneLiq_dWat(1:nSoil) ! derivative in vol fraction of scalar root zone liquid w.r.t. water state variable in root layers - real(rkind) :: dRootZoneIce_dWat(1:nSoil) ! derivative in vol fraction of scalar root zone ice w.r.t. water state variable in root layers - real(rkind) :: dRootZoneLiq_dTk(1:nSoil) ! derivative in vol fraction of scalar root zone liquid w.r.t. temperature in root layers - real(rkind) :: dRootZoneIce_dTk(1:nSoil) ! derivative in vol fraction of scalar root zone ice w.r.t. temperature in root layers - real(rkind) :: dDepthWettingFront_dWat(1:nSoil) ! derivative in scalar depth of wetting front w.r.t. water state variable in root layers - real(rkind) :: dDepthWettingFront_dTk(1:nSoil) ! derivative in scalar depth of wetting front w.r.t. temperature in root layers - real(rkind) :: dXMaxInfilRate_dWat(1:nSoil) ! derivative in scalar max infiltration rate w.r.t. water state variable in root layers - real(rkind) :: dXMaxInfilRate_dTk(1:nSoil) ! derivative in scalar max infiltration rate w.r.t. temperature in root layers - real(rkind) :: dInfilArea_dWat(0:nSoil) ! derivative in scalar infiltration rate w.r.t. water state variable in canopy or snow and root layers - real(rkind) :: dInfilArea_dTk(0:nSoil) ! derivative in scalar infiltration rate w.r.t. temperature in canopy or snow and root layers - real(rkind) :: dFrozenArea_dWat(0:nSoil) ! derivative in scalar frozen area w.r.t. water state variable in canopy or snow and root layers - real(rkind) :: dFrozenArea_dTk(0:nSoil) ! derivative in scalar frozen area w.r.t. temperature in canopy or snow and root layers - real(rkind) :: dInfilRate_dWat(0:nSoil) ! derivative in scalar infiltration rate w.r.t. water state variable in canopy or snow and root layers - real(rkind) :: dInfilRate_dTk(0:nSoil) ! derivative in scalar infiltration rate w.r.t. temperature in canopy or snow and root layers - ! initialize error control - err=0; message="surfaceFlx/" - - ! initialize derivatives - dq_dHydStateVec(:) = 0._rkind - dq_dNrgStateVec(:) = 0._rkind + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + ! * compute fluxes and derivatives at layer interfaces... + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + + ! NOTE: computing flux at the bottom of the layer + + ! loop through soil layers + do iLayer=ixTop,min(ixBot,nSoil-1) + + ! either one or multiple flux calls, depending on if using analytical or numerical derivatives + do itry=nFlux,0,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) + + ! ===== + ! determine layer to perturb + ! ============================ + select case(itry) + ! skip undesired perturbations + case(perturbState); cycle ! perturbing the layers above and below the flux at the interface + ! identify the index for the perturbation + case(unperturbed); ixPerturb = 0 + case(perturbStateAbove); ixPerturb = 1 + case(perturbStateBelow); ixPerturb = 2 + case default; err=10; message=trim(message)//"unknown perturbation"; return + end select ! (identifying layer to of perturbation) + ! determine the index in the original vector + ixOriginal = iLayer + (ixPerturb-1) + + ! ===== + ! get input state variables... + ! ============================ + ! start with the un-perturbed case + vectorVolFracLiqTrial(1:2) = mLayerVolFracLiqTrial(iLayer:iLayer+1) + vectorMatricHeadLiqTrial(1:2) = mLayerMatricHeadLiqTrial(iLayer:iLayer+1) + ! make appropriate perturbations + if(ixPerturb > 0)then + select case(ixRichards) + case(moisture); vectorVolFracLiqTrial(ixPerturb) = vectorVolFracLiqTrial(ixPerturb) + dx + case(mixdform); vectorMatricHeadLiqTrial(ixPerturb) = vectorMatricHeadLiqTrial(ixPerturb) + dx + case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return + end select ! (form of Richards' equation) + end if + + ! ===== + ! get hydraulic conductivty... + ! ============================ + ! start with the un-perturbed case + vectorHydCondTrial(1:2) = mLayerHydCond(iLayer:iLayer+1) + vectorDiffuseTrial(1:2) = mLayerDiffuse(iLayer:iLayer+1) + ! make appropriate perturbations + if(ixPerturb > 0)then ! only recompute these if perturbed + select case(ixRichards) + case(moisture) + scalardPsi_dTheta = dPsi_dTheta(vectorVolFracLiqTrial(ixPerturb),vGn_alpha(ixPerturb),theta_res(ixPerturb),theta_sat(ixPerturb),vGn_n(ixPerturb),vGn_m(ixPerturb)) + vectorHydCondTrial(ixPerturb) = hydCond_liq(vectorVolFracLiqTrial(ixPerturb),mLayerSatHydCond(ixOriginal),theta_res(ixPerturb),theta_sat(ixPerturb),vGn_m(ixPerturb)) * iceImpedeFac(ixOriginal) + vectorDiffuseTrial(ixPerturb) = scalardPsi_dTheta * vectorHydCondTrial(ixPerturb) + case(mixdform) + scalarVolFracLiqTrial = volFracLiq(vectorMatricHeadLiqTrial(ixPerturb),vGn_alpha(ixPerturb),theta_res(ixPerturb),theta_sat(ixPerturb),vGn_n(ixPerturb),vGn_m(ixPerturb)) + scalarHydCondMicro = hydCond_psi(vectorMatricHeadLiqTrial(ixPerturb),mLayerSatHydCond(ixOriginal),vGn_alpha(ixPerturb),vGn_n(ixPerturb),vGn_m(ixPerturb)) * iceImpedeFac(ixOriginal) + scalarHydCondMacro = hydCondMP_liq(scalarVolFracLiqTrial,theta_sat(ixPerturb),theta_mp,mpExp,mLayerSatHydCondMP(ixOriginal),mLayerSatHydCond(ixOriginal)) + vectorHydCondTrial(ixPerturb) = scalarHydCondMicro + scalarHydCondMacro + case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return + end select ! (form of Richards' equation) + endif ! (recompute if perturbed) + + ! ===== + ! compute vertical flux at layer interface and its derivative w.r.t. the state above and the state below... + ! ========================================================================================================= + + ! NOTE: computing flux at the bottom of the layer + + call iLayerFlux(& + ! input: model control + desireAnal, & ! intent(in): flag indicating if derivatives are desired + ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) + ! input: state variables (adjacent layers) + vectorMatricHeadLiqTrial, & ! intent(in): liquid matric head at the soil nodes (m) + vectorVolFracLiqTrial, & ! intent(in): volumetric liquid water content at the soil nodes (-) + ! input: model coordinate variables (adjacent layers) + mLayerHeight(iLayer:iLayer+1), & ! intent(in): height of the soil nodes (m) + ! input: temperature derivatives + dPsiLiq_dTemp(iLayer:iLayer+1), & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) + dHydCond_dTemp(iLayer:iLayer+1), & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) + ! input: transmittance (adjacent layers) + vectorHydCondTrial, & ! intent(in): hydraulic conductivity at the soil nodes (m s-1) + vectorDiffuseTrial, & ! intent(in): hydraulic diffusivity at the soil nodes (m2 s-1) + ! input: transmittance derivatives (adjacent layers) + dHydCond_dVolLiq(iLayer:iLayer+1), & ! intent(in): change in hydraulic conductivity w.r.t. change in volumetric liquid water content (m s-1) + dDiffuse_dVolLiq(iLayer:iLayer+1), & ! intent(in): change in hydraulic diffusivity w.r.t. change in volumetric liquid water content (m2 s-1) + dHydCond_dMatric(iLayer:iLayer+1), & ! intent(in): change in hydraulic conductivity w.r.t. change in matric head (s-1) + ! output: tranmsmittance at the layer interface (scalars) + iLayerHydCond(iLayer), & ! intent(out): hydraulic conductivity at the interface between layers (m s-1) + iLayerDiffuse(iLayer), & ! intent(out): hydraulic diffusivity at the interface between layers (m2 s-1) + ! output: vertical flux at the layer interface (scalars) + iLayerLiqFluxSoil(iLayer), & ! intent(out): vertical flux of liquid water at the layer interface (m s-1) + ! output: derivatives in fluxes w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below (m s-1 or s-1) + dq_dHydStateAbove(iLayer), & ! intent(out): derivatives in the flux w.r.t. matric head or volumetric lquid water in the layer above (m s-1 or s-1) + dq_dHydStateBelow(iLayer), & ! intent(out): derivatives in the flux w.r.t. matric head or volumetric lquid water in the layer below (m s-1 or s-1) + ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) + dq_dNrgStateAbove(iLayer), & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) + dq_dNrgStateBelow(iLayer), & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + ! compute total vertical flux, to compute derivatives + if(deriv_desired .and. ixDerivMethod==numerical)then + select case(itry) + case(unperturbed); scalarFlux = iLayerLiqFluxSoil(iLayer) + case(perturbStateAbove); scalarFlux_dStateAbove = iLayerLiqFluxSoil(iLayer) + case(perturbStateBelow); scalarFlux_dStateBelow = iLayerLiqFluxSoil(iLayer) + case default; err=10; message=trim(message)//"unknown perturbation"; return + end select + end if + + end do ! (looping through different flux calculations -- one or multiple calls depending if desire for numerical or analytical derivatives) + + ! compute numerical derivatives + if(deriv_desired .and. ixDerivMethod==numerical)then + dq_dHydStateAbove(iLayer) = (scalarFlux_dStateAbove - scalarFlux)/dx ! change in drainage flux w.r.t. change in the state in the layer below (m s-1 or s-1) + dq_dHydStateBelow(iLayer) = (scalarFlux_dStateBelow - scalarFlux)/dx ! change in drainage flux w.r.t. change in the state in the layer below (m s-1 or s-1) + end if + + end do ! (looping through soil layers) + + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + ! * compute drainage flux from the bottom of the soil profile, and its derivative + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + + ! define the need to compute drainage + if( .not. (scalarSolution .and. ixTop epsilon(iceImpedeFac))then + dK_dLiq__noIce = dHydCond_dLiq(scalarVolFracLiqTrial,scalarSatHydCond,theta_res,theta_sat,vGn_m,.true.) ! [.true. = analytical] + dHydCond_dVolLiq = hydCond_noIce*dIceImpede_dLiq + dK_dLiq__noIce*iceImpedeFac + else + dHydCond_dVolLiq = dHydCond_dLiq(scalarVolFracLiqTrial,scalarSatHydCond,theta_res,theta_sat,vGn_m,.true.) + end if + dPsi_dTheta2a = dPsi_dTheta2(scalarVolFracLiqTrial,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m,.true.) ! [.true. = analytical] compute derivative in dPsi_dTheta (m) + dDiffuse_dVolLiq = dHydCond_dVolLiq*scalardPsi_dTheta + scalarHydCond*dPsi_dTheta2a + dHydCond_dMatric = realMissing ! not used, so cause problems + end if + + ! ***** mixed form of Richards' equation -- just compute hydraulic condictivity case(mixdform) - ! compute the hydraulic conductivity and diffusivity at the boundary - surfaceHydCond = hydCond_psi(upperBoundHead,surfaceSatHydCond,vGn_alpha,vGn_n,vGn_m) * iceImpedeFac - surfaceDiffuse = realMissing - ! compute the capillary flux - cflux = -surfaceHydCond*(scalarMatricHead - upperBoundHead) / (mLayerDepth(1)*0.5_rkind) + ! compute the hydraulic conductivity (m s-1) and diffusivity (m2 s-1) for a given layer + hydCond_noIce = hydCond_psi(scalarMatricHeadLiqTrial,scalarSatHydCond,vGn_alpha,vGn_n,vGn_m) + scalarDiffuse = realMissing ! not used, so cause problems + ! compute the hydraulic conductivity of macropores (m s-1) + localVolFracLiq = volFracLiq(scalarMatricHeadLiqTrial,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + scalarHydCondMP = hydCondMP_liq(localVolFracLiq,theta_sat,theta_mp,mpExp,scalarSatHydCondMP,scalarSatHydCond) + scalarHydCond = hydCond_noIce*iceImpedeFac + scalarHydCondMP + + ! compute derivative in hydraulic conductivity (m s-1) + if(deriv_desired)then + ! (compute derivative for macropores) + if(localVolFracLiq > theta_mp)then + relSatMP = (localVolFracLiq - theta_mp)/(theta_sat - theta_mp) + dHydCondMacro_dVolLiq = ((scalarSatHydCondMP - scalarSatHydCond)/(theta_sat - theta_mp))*mpExp*(relSatMP**(mpExp - 1._rkind)) + dHydCondMacro_dMatric = scalardTheta_dPsi*dHydCondMacro_dVolLiq + else + dHydCondMacro_dVolLiq = 0._rkind + dHydCondMacro_dMatric = 0._rkind + end if + ! (compute derivatives for micropores) + if(scalarVolFracIceTrial > verySmall)then + dK_dPsi__noIce = dHydCond_dPsi(scalarMatricHeadLiqTrial,scalarSatHydCond,vGn_alpha,vGn_n,vGn_m,.true.) ! analytical + dHydCondMicro_dTemp = dPsiLiq_dTemp*dK_dPsi__noIce ! m s-1 K-1 + dHydCondMicro_dMatric = hydCond_noIce*dIceImpede_dLiq*scalardTheta_dPsi + dK_dPsi__noIce*iceImpedeFac + else + dHydCondMicro_dTemp = 0._rkind + dHydCondMicro_dMatric = dHydCond_dPsi(scalarMatricHeadLiqTrial,scalarSatHydCond,vGn_alpha,vGn_n,vGn_m,.true.) + end if + ! (combine derivatives) + dHydCond_dMatric = dHydCondMicro_dMatric + dHydCondMacro_dMatric + + ! (compute analytical derivative for change in ice impedance factor w.r.t. temperature) + call dIceImpede_dTemp(scalarVolFracIceTrial, & ! intent(in): trial value of volumetric ice content (-) + dTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + f_impede, & ! intent(in): ice impedance parameter (-) + dIceImpede_dT ) ! intent(out): derivative in ice impedance factor w.r.t. temperature (K-1) + ! (compute derivative in hydraulic conductivity w.r.t. temperature) + dHydCond_dTemp = hydCond_noIce*dIceImpede_dT + dHydCondMicro_dTemp*iceImpedeFac + ! (test derivative) + if(testDeriv)then + xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) + vTheta = scalarVolFracIceTrial + scalarVolFracLiqTrial + volLiq = volFracLiq(xConst*(scalarTempTrial+dx - Tfreeze),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + volIce = vTheta - volLiq + effSat = (volLiq - theta_res)/(theta_sat - volIce - theta_res) + psiLiq = matricHead(effSat,vGn_alpha,0._rkind,1._rkind,vGn_n,vGn_m) ! use effective saturation, so theta_res=0 and theta_sat=1 + hydCon = hydCond_psi(psiLiq,scalarSatHydCond,vGn_alpha,vGn_n,vGn_m) + call iceImpede(volIce,f_impede,iceImpedeFac,dIceImpede_dLiq) + hydIce = hydCon*iceImpedeFac + print*, 'test derivative: ', (psiLiq - scalarMatricHeadLiqTrial)/dx, dPsiLiq_dTemp + print*, 'test derivative: ', (hydCon - hydCond_noIce)/dx, dHydCondMicro_dTemp + print*, 'test derivative: ', (hydIce - scalarHydCond)/dx, dHydCond_dTemp + print*, 'press any key to continue'; read(*,*) ! (alternative to the deprecated 'pause' statement) + end if ! testing the derivative + ! (set values that are not used to missing) + dHydCond_dVolLiq = realMissing ! not used, so cause problems + dDiffuse_dVolLiq = realMissing ! not used, so cause problems + end if + case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return - end select ! (form of Richards' eqn) - ! compute the total flux - scalarSurfaceInfiltration = cflux + surfaceHydCond - ! compute the derivative - if(deriv_desired)then - ! compute the hydrology derivative at the surface - select case(ixRichards) ! (form of Richards' equation) - case(moisture); dq_dHydStateVec(1) = -surfaceDiffuse/(mLayerDepth(1)/2._rkind) - case(mixdform); dq_dHydStateVec(1) = -surfaceHydCond/(mLayerDepth(1)/2._rkind) - case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return - end select - ! compute the energy derivative at the surface - dq_dNrgStateVec(1) = -(dHydCond_dTemp/2._rkind)*(scalarMatricHead - upperBoundHead)/(mLayerDepth(1)*0.5_rkind) + dHydCond_dTemp/2._rkind - ! compute the numerical derivative - !cflux = -surfaceHydCond*((scalarMatricHead+dx) - upperBoundHead) / (mLayerDepth(1)*0.5_rkind) - !surfaceInfiltration1 = cflux + surfaceHydCond - !dNum = (surfaceInfiltration1 - scalarSurfaceInfiltration)/dx - else - dNum = 0._rkind - end if - !write(*,'(a,1x,10(e30.20,1x))') 'scalarMatricHead, scalarSurfaceInfiltration, dq_dHydState, dNum = ', & - ! scalarMatricHead, scalarSurfaceInfiltration, dq_dHydState, dNum + + end select + + ! if derivatives are not desired, then set values to missing + if(.not.deriv_desired)then + dHydCond_dVolLiq = realMissing ! not used, so cause problems + dDiffuse_dVolLiq = realMissing ! not used, so cause problems + dHydCond_dMatric = realMissing ! not used, so cause problems + end if + +end subroutine diagv_node + + +! *************************************************************************************************************** +! private subroutine surfaceFlx: compute the surface flux and its derivative +! *************************************************************************************************************** +subroutine surfaceFlx(& + ! input: model control + doInfiltration, & ! intent(in): flag indicating if desire to compute infiltration + deriv_desired, & ! intent(in): flag indicating if derivatives are desired + ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) + bc_upper, & ! intent(in): index defining the type of boundary conditions (neumann or diriclet) + nRoots, & ! intent(in): number of layers that contain roots + ixIce, & ! intent(in): index of lowest ice layer + nSoil, & ! intent(in): number of soil layers + ! input: state variables + mLayerTemp, & ! intent(in): temperature (K) + scalarMatricHead, & ! intent(in): matric head in the upper-most soil layer (m) + mLayerMatricHead, & ! intent(in): matric head in each soil layer (m) + scalarVolFracLiq, & ! intent(in): volumetric liquid water content in the upper-most soil layer (-) + mLayerVolFracLiq, & ! intent(in): volumetric liquid water content in each soil layer (-) + mLayerVolFracIce, & ! intent(in): volumetric ice content in each soil layer (-) + ! input: pre-computed derivatives + dTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + dTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) + mLayerdPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) + above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) + ! input: depth of upper-most soil layer (m) + mLayerDepth, & ! intent(in): depth of each soil layer (m) + iLayerHeight, & ! intent(in): height at the interface of each layer (m) + ! input: boundary conditions + upperBoundHead, & ! intent(in): upper boundary condition (m) + upperBoundTheta, & ! intent(in): upper boundary condition (-) + ! input: flux at the upper boundary + scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) + ! input: transmittance + surfaceSatHydCond, & ! intent(in): saturated hydraulic conductivity at the surface (m s-1) + dHydCond_dTemp, & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) + iceImpedeFac, & ! intent(in): ice impedence factor in the upper-most soil layer (-) + ! input: soil parameters + vGn_alpha, & ! intent(in): van Genutchen "alpha" parameter (m-1) + vGn_n, & ! intent(in): van Genutchen "n" parameter (-) + vGn_m, & ! intent(in): van Genutchen "m" parameter (-) + theta_sat, & ! intent(in): soil porosity (-) + theta_res, & ! intent(in): soil residual volumetric water content (-) + qSurfScale, & ! intent(in): scaling factor in the surface runoff parameterization (-) + zScale_TOPMODEL, & ! intent(in): scaling factor used to describe decrease in hydraulic conductivity with depth (m) + rootingDepth, & ! intent(in): rooting depth (m) + wettingFrontSuction, & ! intent(in): Green-Ampt wetting front suction (m) + soilIceScale, & ! intent(in): soil ice scaling factor in Gamma distribution used to define frozen area (m) + soilIceCV, & ! intent(in): soil ice CV in Gamma distribution used to define frozen area (-) + ! input-output: hydraulic conductivity and diffusivity at the surface + surfaceHydCond, & ! intent(inout): hydraulic conductivity at the surface (m s-1) + surfaceDiffuse, & ! intent(inout): hydraulic diffusivity at the surface (m2 s-1) + ! input-output: fluxes at layer interfaces and surface runoff + xMaxInfilRate, & ! intent(inout): maximum infiltration rate (m s-1) + scalarInfilArea, & ! intent(inout): fraction of unfrozen area where water can infiltrate (-) + scalarFrozenArea, & ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) + scalarSurfaceRunoff, & ! intent(out): surface runoff (m s-1) + scalarSurfaceInfiltration, & ! intent(out): surface infiltration (m s-1) + ! input-output: deriavtives in surface infiltration w.r.t. volumetric liquid water (m s-1) and matric head (s-1) in the upper-most soil layer + dq_dHydStateVec, & ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) + dq_dNrgStateVec, & ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) + ! output: error control + err,message) ! intent(out): error control + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water as a function of matric head (-) + USE soil_utils_module,only:hydCond_psi ! compute hydraulic conductivity as a function of matric head (m s-1) + USE soil_utils_module,only:hydCond_liq ! compute hydraulic conductivity as a function of volumetric liquid water content (m s-1) + USE soil_utils_module,only:dPsi_dTheta ! compute derivative of the soil moisture characteristic w.r.t. theta (m) + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + USE soil_utils_module,only:gammp ! compute the cumulative probabilty based on the Gamma distribution + ! compute infiltraton at the surface and its derivative w.r.t. mass in the upper soil layer + implicit none + ! ----------------------------------------------------------------------------------------------------------------------------- + ! input: model control + logical(lgt),intent(in) :: doInfiltration ! flag indicating if desire to compute infiltration + logical(lgt),intent(in) :: deriv_desired ! flag to indicate if derivatives are desired + integer(i4b),intent(in) :: bc_upper ! index defining the type of boundary conditions + integer(i4b),intent(in) :: ixRichards ! index defining the option for Richards' equation (moisture or mixdform) + integer(i4b),intent(in) :: nRoots ! number of layers that contain roots + integer(i4b),intent(in) :: ixIce ! index of lowest ice layer + integer(i4b),intent(in) :: nSoil ! number of soil layers + ! input: state and diagnostic variables + real(rkind),intent(in) :: mLayerTemp(:) ! temperature (K) + real(rkind),intent(in) :: scalarMatricHead ! matric head in the upper-most soil layer (m) + real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head in each soil layer (m) + real(rkind),intent(in) :: scalarVolFracLiq ! volumetric liquid water content in the upper-most soil layer (-) + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric liquid water content in each soil layer (-) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric ice content in each soil layer (-) + ! input: pre-computed derivatives, note all of these would need to be recomputed if wanted a numerical derivative + real(rkind),intent(in) :: dTheta_dTk(:) ! derivative in volumetric liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: dTheta_dPsi(:) ! derivative in the soil water characteristic w.r.t. psi (m-1) + real(rkind),intent(in) :: mLayerdPsi_dTheta(:) ! derivative in the soil water characteristic w.r.t. theta (m) + real(rkind),intent(in) :: above_soilLiqFluxDeriv ! derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + real(rkind),intent(in) :: above_soildLiq_dTk ! derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + real(rkind),intent(in) :: above_soilFracLiq ! fraction of liquid water layer above soil (canopy or snow) (-) + ! input: depth of upper-most soil layer (m) + real(rkind),intent(in) :: mLayerDepth(:) ! depth of upper-most soil layer (m) + real(rkind),intent(in) :: iLayerHeight(0:) ! height at the interface of each layer (m) + ! input: diriclet boundary conditions + real(rkind),intent(in) :: upperBoundHead ! upper boundary condition for matric head (m) + real(rkind),intent(in) :: upperBoundTheta ! upper boundary condition for volumetric liquid water content (-) + ! input: flux at the upper boundary + real(rkind),intent(in) :: scalarRainPlusMelt ! rain plus melt, used as input to the soil zone before computing surface runoff (m s-1) + ! input: transmittance + real(rkind),intent(in) :: surfaceSatHydCond ! saturated hydraulic conductivity at the surface (m s-1) + real(rkind),intent(in) :: dHydCond_dTemp ! derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) + real(rkind),intent(in) :: iceImpedeFac ! ice impedence factor in the upper-most soil layer (-) + ! input: soil parameters + real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter (m-1) + real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter (-) + real(rkind),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) + real(rkind),intent(in) :: theta_sat ! soil porosity (-) + real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) + real(rkind),intent(in) :: qSurfScale ! scaling factor in the surface runoff parameterization (-) + real(rkind),intent(in) :: zScale_TOPMODEL ! scaling factor used to describe decrease in hydraulic conductivity with depth (m) + real(rkind),intent(in) :: rootingDepth ! rooting depth (m) + real(rkind),intent(in) :: wettingFrontSuction ! Green-Ampt wetting front suction (m) + real(rkind),intent(in) :: soilIceScale ! soil ice scaling factor in Gamma distribution used to define frozen area (m) + real(rkind),intent(in) :: soilIceCV ! soil ice CV in Gamma distribution used to define frozen area (-) + ! ----------------------------------------------------------------------------------------------------------------------------- + ! input-output: hydraulic conductivity and diffusivity at the surface + ! NOTE: intent(inout) because infiltration may only be computed for the first iteration + real(rkind),intent(inout) :: surfaceHydCond ! hydraulic conductivity (m s-1) + real(rkind),intent(inout) :: surfaceDiffuse ! hydraulic diffusivity at the surface (m + ! output: surface runoff and infiltration flux (m s-1) + real(rkind),intent(inout) :: xMaxInfilRate ! maximum infiltration rate (m s-1) + real(rkind),intent(inout) :: scalarInfilArea ! fraction of unfrozen area where water can infiltrate (-) + real(rkind),intent(inout) :: scalarFrozenArea ! fraction of area that is considered impermeable due to soil ice (-) + real(rkind),intent(out) :: scalarSurfaceRunoff ! surface runoff (m s-1) + real(rkind),intent(out) :: scalarSurfaceInfiltration ! surface infiltration (m s-1) + ! output: derivatives in surface infiltration w.r.t. states in above soil snow or canopy and every soil layer + real(rkind),intent(out) :: dq_dHydStateVec(0:) ! derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) + real(rkind),intent(out) :: dq_dNrgStateVec(0:) ! derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ----------------------------------------------------------------------------------------------------------------------------- + ! local variables + ! (general) + integer(i4b) :: iLayer ! index of soil layer + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind) :: fpart1,fpart2 ! different parts of a function + real(rkind) :: dpart1(1:nSoil),dpart2(1:nSoil) ! derivatives for different parts of a function + real(rkind) :: dfracCap(1:nSoil),dfInfRaw(1:nSoil) ! derivatives for different parts of a function + ! (head boundary condition) + real(rkind) :: cFlux ! capillary flux (m s-1) + real(rkind) :: dNum ! numerical derivative + ! (simplified Green-Ampt infiltration) + real(rkind) :: rootZoneLiq ! depth of liquid water in the root zone (m) + real(rkind) :: rootZoneIce ! depth of ice in the root zone (m) + real(rkind) :: availCapacity ! available storage capacity in the root zone (m) + real(rkind) :: depthWettingFront ! depth to the wetting front (m) + real(rkind) :: hydCondWettingFront ! hydraulic conductivity at the wetting front (m s-1) + ! (saturated area associated with variable storage capacity) + real(rkind) :: fracCap ! fraction of pore space filled with liquid water and ice (-) + real(rkind) :: fInfRaw ! infiltrating area before imposing solution constraints (-) + real(rkind),parameter :: maxFracCap=0.995_rkind ! maximum fraction capacity -- used to avoid numerical problems associated with an enormous derivative + real(rkind),parameter :: scaleFactor=0.000001_rkind ! scale factor for the smoothing function (-) + real(rkind),parameter :: qSurfScaleMax=1000._rkind ! maximum surface runoff scaling factor (-) + ! (fraction of impermeable area associated with frozen ground) + real(rkind) :: alpha ! shape parameter in the Gamma distribution + real(rkind) :: xLimg ! upper limit of the integral + ! (derivatives) + real(rkind) :: dVolFracLiq_dWat(1:nSoil) ! derivative in vol fraction of liquid w.r.t. water state variable in root layers + real(rkind) :: dVolFracIce_dWat(1:nSoil) ! derivative in vol fraction of ice w.r.t. water state variable in root layers + real(rkind) :: dVolFracLiq_dTk(1:nSoil) ! derivative in vol fraction of liquid w.r.t. temperature in root layers + real(rkind) :: dVolFracIce_dTk(1:nSoil) ! derivative in vol fraction of ice w.r.t. temperature in root layers + real(rkind) :: dRootZoneLiq_dWat(1:nSoil) ! derivative in vol fraction of scalar root zone liquid w.r.t. water state variable in root layers + real(rkind) :: dRootZoneIce_dWat(1:nSoil) ! derivative in vol fraction of scalar root zone ice w.r.t. water state variable in root layers + real(rkind) :: dRootZoneLiq_dTk(1:nSoil) ! derivative in vol fraction of scalar root zone liquid w.r.t. temperature in root layers + real(rkind) :: dRootZoneIce_dTk(1:nSoil) ! derivative in vol fraction of scalar root zone ice w.r.t. temperature in root layers + real(rkind) :: dDepthWettingFront_dWat(1:nSoil) ! derivative in scalar depth of wetting front w.r.t. water state variable in root layers + real(rkind) :: dDepthWettingFront_dTk(1:nSoil) ! derivative in scalar depth of wetting front w.r.t. temperature in root layers + real(rkind) :: dXMaxInfilRate_dWat(1:nSoil) ! derivative in scalar max infiltration rate w.r.t. water state variable in root layers + real(rkind) :: dXMaxInfilRate_dTk(1:nSoil) ! derivative in scalar max infiltration rate w.r.t. temperature in root layers + real(rkind) :: dInfilArea_dWat(0:nSoil) ! derivative in scalar infiltration rate w.r.t. water state variable in canopy or snow and root layers + real(rkind) :: dInfilArea_dTk(0:nSoil) ! derivative in scalar infiltration rate w.r.t. temperature in canopy or snow and root layers + real(rkind) :: dFrozenArea_dWat(0:nSoil) ! derivative in scalar frozen area w.r.t. water state variable in canopy or snow and root layers + real(rkind) :: dFrozenArea_dTk(0:nSoil) ! derivative in scalar frozen area w.r.t. temperature in canopy or snow and root layers + real(rkind) :: dInfilRate_dWat(0:nSoil) ! derivative in scalar infiltration rate w.r.t. water state variable in canopy or snow and root layers + real(rkind) :: dInfilRate_dTk(0:nSoil) ! derivative in scalar infiltration rate w.r.t. temperature in canopy or snow and root layers + + ! initialize error control + err=0; message="surfaceFlx/" + + ! initialize derivatives + dq_dHydStateVec(:) = 0._rkind + dq_dNrgStateVec(:) = 0._rkind ! ***** - ! flux condition - case(liquidFlux) - - ! force infiltration to be constant over the iterations - if(doInfiltration)then - - ! (process root layers only liquid and ice derivatives) - dVolFracLiq_dWat(:) = 0._rkind - dVolFracIce_dWat(:) = 0._rkind - dVolFracLiq_dTk(:) = 0._rkind - dVolFracIce_dTk(:) = 0._rkind - if(deriv_desired .and. nRoots > 0)then - select case(ixRichards) ! (form of Richards' equation) - case(moisture) - dVolFracLiq_dWat(:) = 1._rkind - dVolFracIce_dWat(:) = mLayerdPsi_dTheta(:) - 1._rkind - case(mixdform) - do iLayer=1,nRoots - Tcrit = crit_soilT( mLayerMatricHead(iLayer) ) - if(mLayerTemp(iLayer) < Tcrit)then - dVolFracLiq_dWat(iLayer) = 0._rkind - dVolFracIce_dWat(iLayer) = dTheta_dPsi(iLayer) - else - dVolFracLiq_dWat(iLayer) = dTheta_dPsi(iLayer) - dVolFracIce_dWat(iLayer) = 0._rkind + ! compute the surface flux and its derivative + select case(bc_upper) + + ! ***** + ! head condition + case(prescribedHead) + + ! surface runoff iz zero for the head condition + scalarSurfaceRunoff = 0._rkind + + ! compute transmission and the capillary flux + select case(ixRichards) ! (form of Richards' equation) + case(moisture) + ! compute the hydraulic conductivity and diffusivity at the boundary + surfaceHydCond = hydCond_liq(upperBoundTheta,surfaceSatHydCond,theta_res,theta_sat,vGn_m) * iceImpedeFac + surfaceDiffuse = dPsi_dTheta(upperBoundTheta,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * surfaceHydCond + ! compute the capillary flux + cflux = -surfaceDiffuse*(scalarVolFracLiq - upperBoundTheta) / (mLayerDepth(1)*0.5_rkind) + case(mixdform) + ! compute the hydraulic conductivity and diffusivity at the boundary + surfaceHydCond = hydCond_psi(upperBoundHead,surfaceSatHydCond,vGn_alpha,vGn_n,vGn_m) * iceImpedeFac + surfaceDiffuse = realMissing + ! compute the capillary flux + cflux = -surfaceHydCond*(scalarMatricHead - upperBoundHead) / (mLayerDepth(1)*0.5_rkind) + case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return + end select ! (form of Richards' eqn) + ! compute the total flux + scalarSurfaceInfiltration = cflux + surfaceHydCond + ! compute the derivative + if(deriv_desired)then + ! compute the hydrology derivative at the surface + select case(ixRichards) ! (form of Richards' equation) + case(moisture); dq_dHydStateVec(1) = -surfaceDiffuse/(mLayerDepth(1)/2._rkind) + case(mixdform); dq_dHydStateVec(1) = -surfaceHydCond/(mLayerDepth(1)/2._rkind) + case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return + end select + ! compute the energy derivative at the surface + dq_dNrgStateVec(1) = -(dHydCond_dTemp/2._rkind)*(scalarMatricHead - upperBoundHead)/(mLayerDepth(1)*0.5_rkind) + dHydCond_dTemp/2._rkind + else + dNum = 0._rkind + end if + + ! flux condition + case(liquidFlux) + + ! force infiltration to be constant over the iterations + if(doInfiltration)then + + ! (process root layers only liquid and ice derivatives) + dVolFracLiq_dWat(:) = 0._rkind + dVolFracIce_dWat(:) = 0._rkind + dVolFracLiq_dTk(:) = 0._rkind + dVolFracIce_dTk(:) = 0._rkind + if(deriv_desired .and. nRoots > 0)then + select case(ixRichards) ! (form of Richards' equation) + case(moisture) + dVolFracLiq_dWat(:) = 1._rkind + dVolFracIce_dWat(:) = mLayerdPsi_dTheta(:) - 1._rkind + case(mixdform) + do iLayer=1,nRoots + Tcrit = crit_soilT( mLayerMatricHead(iLayer) ) + if(mLayerTemp(iLayer) < Tcrit)then + dVolFracLiq_dWat(iLayer) = 0._rkind + dVolFracIce_dWat(iLayer) = dTheta_dPsi(iLayer) + else + dVolFracLiq_dWat(iLayer) = dTheta_dPsi(iLayer) + dVolFracIce_dWat(iLayer) = 0._rkind + endif + enddo + end select ! (form of Richards' equation) + dVolFracLiq_dTk(:) = dTheta_dTk(:) !already zeroed out if not below critical temperature + dVolFracIce_dTk(:) = -dVolFracLiq_dTk(:) !often can and will simplify one of these terms out endif - enddo - end select ! (form of Richards' equation) - dVolFracLiq_dTk(:) = dTheta_dTk(:) !already zeroed out if not below critical temperature - dVolFracIce_dTk(:) = -dVolFracLiq_dTk(:) !often can and will simplify one of these terms out - endif - - ! define the storage in the root zone (m) and derivatives - rootZoneLiq = 0._rkind - rootZoneIce = 0._rkind - dRootZoneLiq_dWat(:) = 0._rkind - dRootZoneIce_dWat(:) = 0._rkind - dRootZoneLiq_dTk(:) = 0._rkind - dRootZoneIce_dTk(:) = 0._rkind - - ! (process layers where the roots extend to the bottom of the layer) - if(nRoots > 1)then - do iLayer=1,nRoots-1 - rootZoneLiq = rootZoneLiq + mLayerVolFracLiq(iLayer)*mLayerDepth(iLayer) - rootZoneIce = rootZoneIce + mLayerVolFracIce(iLayer)*mLayerDepth(iLayer) - dRootZoneLiq_dWat(iLayer) = dVolFracLiq_dWat(iLayer)*mLayerDepth(iLayer) - dRootZoneIce_dWat(iLayer) = dVolFracIce_dWat(iLayer)*mLayerDepth(iLayer) - dRootZoneLiq_dTk(iLayer) = dVolFracLiq_dTk(iLayer) *mLayerDepth(iLayer) - dRootZoneIce_dTk(iLayer) = dVolFracIce_dTk(iLayer) *mLayerDepth(iLayer) - end do - end if - ! (process layers where the roots end in the current layer) - rootZoneLiq = rootZoneLiq + mLayerVolFracLiq(nRoots)*(rootingDepth - iLayerHeight(nRoots-1)) - rootZoneIce = rootZoneIce + mLayerVolFracIce(nRoots)*(rootingDepth - iLayerHeight(nRoots-1)) - dRootZoneLiq_dWat(nRoots) = dVolFracLiq_dWat(nRoots)*(rootingDepth - iLayerHeight(nRoots-1)) - dRootZoneIce_dWat(nRoots) = dVolFracIce_dWat(nRoots)*(rootingDepth - iLayerHeight(nRoots-1)) - dRootZoneLiq_dTk(nRoots) = dVolFracLiq_dTk(nRoots)* (rootingDepth - iLayerHeight(nRoots-1)) - dRootZoneIce_dTk(nRoots) = dVolFracIce_dTk(nRoots)* (rootingDepth - iLayerHeight(nRoots-1)) - - ! define available capacity to hold water (m) - availCapacity = theta_sat*rootingDepth - rootZoneIce - if(rootZoneLiq > availCapacity+verySmall)then - message=trim(message)//'liquid water in the root zone exceeds capacity' - err=20; return - end if - ! define the depth to the wetting front (m) and derivatives - depthWettingFront = (rootZoneLiq/availCapacity)*rootingDepth - dDepthWettingFront_dWat(:)=( dRootZoneLiq_dWat(:)*rootingDepth + dRootZoneIce_dWat(:)*depthWettingFront )/availCapacity - dDepthWettingFront_dTk(:) =( dRootZoneLiq_dTk(:)*rootingDepth + dRootZoneIce_dTk(:)*depthWettingFront )/availCapacity - - ! define the hydraulic conductivity at depth=depthWettingFront (m s-1) - hydCondWettingFront = surfaceSatHydCond * ( (1._rkind - depthWettingFront/sum(mLayerDepth))**(zScale_TOPMODEL - 1._rkind) ) - - ! define the maximum infiltration rate (m s-1) and derivatives - xMaxInfilRate = hydCondWettingFront*( (wettingFrontSuction + depthWettingFront)/depthWettingFront ) ! maximum infiltration rate (m s-1) - !write(*,'(a,1x,f9.3,1x,10(e20.10,1x))') 'depthWettingFront, surfaceSatHydCond, hydCondWettingFront, xMaxInfilRate = ', depthWettingFront, surfaceSatHydCond, hydCondWettingFront, xMaxInfilRate - fPart1 = hydCondWettingFront - fPart2 = (wettingFrontSuction + depthWettingFront)/depthWettingFront - dPart1(:) = surfaceSatHydCond*(zScale_TOPMODEL - 1._rkind) * ( (1._rkind - depthWettingFront/sum(mLayerDepth))**(zScale_TOPMODEL - 2._rkind) ) * (-dDepthWettingFront_dWat(:))/sum(mLayerDepth) - dPart2(:) = -dDepthWettingFront_dWat(:)*wettingFrontSuction / (depthWettingFront**2._rkind) - dXMaxInfilRate_dWat(:) = fPart1*dpart2(:) + fPart2*dPart1(:) - dPart1(:) = surfaceSatHydCond*(zScale_TOPMODEL - 1._rkind) * ( (1._rkind - depthWettingFront/sum(mLayerDepth))**(zScale_TOPMODEL - 2._rkind) ) * (-dDepthWettingFront_dTk(:))/sum(mLayerDepth) - dPart2(:) = -dDepthWettingFront_dTk(:)*wettingFrontSuction / (depthWettingFront**2._rkind) - dXMaxInfilRate_dTk(:) = fPart1*dpart2(:) + fPart2*dPart1(:) - - ! define the infiltrating area and derivatives for the non-frozen part of the cell/basin - if(qSurfScale < qSurfScaleMax)then - fracCap = rootZoneLiq/(maxFracCap*availCapacity) ! fraction of available root zone filled with water - fInfRaw = 1._rkind - exp(-qSurfScale*(1._rkind - fracCap)) ! infiltrating area -- allowed to violate solution constraints - scalarInfilArea = min(0.5_rkind*(fInfRaw + sqrt(fInfRaw**2._rkind + scaleFactor)), 1._rkind) ! infiltrating area -- constrained - if (0.5_rkind*(fInfRaw + sqrt(fInfRaw**2._rkind + scaleFactor))< 1._rkind) then - dfracCap(:) = ( dRootZoneLiq_dWat(:)/maxFracCap + dRootZoneIce_dWat(:)*fracCap )/availCapacity - dfInfRaw(:) = -qSurfScale*dfracCap(:) * exp(-qSurfScale*(1._rkind - fracCap)) - dInfilArea_dWat(1:nSoil) = 0.5_rkind*dfInfRaw(:) * (1._rkind + fInfRaw/sqrt(fInfRaw**2._rkind + scaleFactor)) - dfracCap(:) = ( dRootZoneLiq_dTk(:)/maxFracCap + dRootZoneIce_dTk(:)*fracCap )/availCapacity - dfInfRaw(:) = -qSurfScale*dfracCap(:) * exp(-qSurfScale*(1._rkind - fracCap)) - dInfilArea_dTk(1:nSoil) = 0.5_rkind*dfInfRaw(:) * (1._rkind + fInfRaw/sqrt(fInfRaw**2._rkind + scaleFactor)) - else ! scalarInfilArea = 1._rkind - dInfilArea_dWat(1:nSoil) = 0._rkind - dInfilArea_dTk(1:nSoil) = 0._rkind - endif - else - scalarInfilArea = 1._rkind - dInfilArea_dWat(1:nSoil) = 0._rkind - dInfilArea_dTk(1:nSoil) = 0._rkind - endif - dInfilArea_dWat(0) = 0._rkind - dInfilArea_dTk(0) = 0._rkind - - ! check to ensure we are not infiltrating into a fully saturated column - if(ixIce 0.9999_rkind*theta_sat*sum(mLayerDepth(ixIce+1:nRoots))) scalarInfilArea=0._rkind - !print*, 'ixIce, nRoots, scalarInfilArea = ', ixIce, nRoots, scalarInfilArea - !print*, 'sum(mLayerVolFracLiq(ixIce+1:nRoots)*mLayerDepth(ixIce+1:nRoots)) = ', sum(mLayerVolFracLiq(ixIce+1:nRoots)*mLayerDepth(ixIce+1:nRoots)) - !print*, 'theta_sat*sum(mLayerDepth(ixIce+1:nRoots)) = ', theta_sat*sum(mLayerDepth(ixIce+1:nRoots)) - endif + ! define the storage in the root zone (m) and derivatives + rootZoneLiq = 0._rkind + rootZoneIce = 0._rkind + dRootZoneLiq_dWat(:) = 0._rkind + dRootZoneIce_dWat(:) = 0._rkind + dRootZoneLiq_dTk(:) = 0._rkind + dRootZoneIce_dTk(:) = 0._rkind + + ! (process layers where the roots extend to the bottom of the layer) + if(nRoots > 1)then + do iLayer=1,nRoots-1 + rootZoneLiq = rootZoneLiq + mLayerVolFracLiq(iLayer)*mLayerDepth(iLayer) + rootZoneIce = rootZoneIce + mLayerVolFracIce(iLayer)*mLayerDepth(iLayer) + dRootZoneLiq_dWat(iLayer) = dVolFracLiq_dWat(iLayer)*mLayerDepth(iLayer) + dRootZoneIce_dWat(iLayer) = dVolFracIce_dWat(iLayer)*mLayerDepth(iLayer) + dRootZoneLiq_dTk(iLayer) = dVolFracLiq_dTk(iLayer) *mLayerDepth(iLayer) + dRootZoneIce_dTk(iLayer) = dVolFracIce_dTk(iLayer) *mLayerDepth(iLayer) + end do + end if + ! (process layers where the roots end in the current layer) + rootZoneLiq = rootZoneLiq + mLayerVolFracLiq(nRoots)*(rootingDepth - iLayerHeight(nRoots-1)) + rootZoneIce = rootZoneIce + mLayerVolFracIce(nRoots)*(rootingDepth - iLayerHeight(nRoots-1)) + dRootZoneLiq_dWat(nRoots) = dVolFracLiq_dWat(nRoots)*(rootingDepth - iLayerHeight(nRoots-1)) + dRootZoneIce_dWat(nRoots) = dVolFracIce_dWat(nRoots)*(rootingDepth - iLayerHeight(nRoots-1)) + dRootZoneLiq_dTk(nRoots) = dVolFracLiq_dTk(nRoots)* (rootingDepth - iLayerHeight(nRoots-1)) + dRootZoneIce_dTk(nRoots) = dVolFracIce_dTk(nRoots)* (rootingDepth - iLayerHeight(nRoots-1)) + + ! define available capacity to hold water (m) + availCapacity = theta_sat*rootingDepth - rootZoneIce + if(rootZoneLiq > availCapacity+verySmall)then + message=trim(message)//'liquid water in the root zone exceeds capacity' + err=20; return + end if + + ! define the depth to the wetting front (m) and derivatives + depthWettingFront = (rootZoneLiq/availCapacity)*rootingDepth + dDepthWettingFront_dWat(:)=( dRootZoneLiq_dWat(:)*rootingDepth + dRootZoneIce_dWat(:)*depthWettingFront )/availCapacity + dDepthWettingFront_dTk(:) =( dRootZoneLiq_dTk(:)*rootingDepth + dRootZoneIce_dTk(:)*depthWettingFront )/availCapacity + + ! define the hydraulic conductivity at depth=depthWettingFront (m s-1) + hydCondWettingFront = surfaceSatHydCond * ( (1._rkind - depthWettingFront/sum(mLayerDepth))**(zScale_TOPMODEL - 1._rkind) ) + + ! define the maximum infiltration rate (m s-1) and derivatives + xMaxInfilRate = hydCondWettingFront*( (wettingFrontSuction + depthWettingFront)/depthWettingFront ) ! maximum infiltration rate (m s-1) + !write(*,'(a,1x,f9.3,1x,10(e20.10,1x))') 'depthWettingFront, surfaceSatHydCond, hydCondWettingFront, xMaxInfilRate = ', depthWettingFront, surfaceSatHydCond, hydCondWettingFront, xMaxInfilRate + fPart1 = hydCondWettingFront + fPart2 = (wettingFrontSuction + depthWettingFront)/depthWettingFront + dPart1(:) = surfaceSatHydCond*(zScale_TOPMODEL - 1._rkind) * ( (1._rkind - depthWettingFront/sum(mLayerDepth))**(zScale_TOPMODEL - 2._rkind) ) * (-dDepthWettingFront_dWat(:))/sum(mLayerDepth) + dPart2(:) = -dDepthWettingFront_dWat(:)*wettingFrontSuction / (depthWettingFront**2._rkind) + dXMaxInfilRate_dWat(:) = fPart1*dpart2(:) + fPart2*dPart1(:) + dPart1(:) = surfaceSatHydCond*(zScale_TOPMODEL - 1._rkind) * ( (1._rkind - depthWettingFront/sum(mLayerDepth))**(zScale_TOPMODEL - 2._rkind) ) * (-dDepthWettingFront_dTk(:))/sum(mLayerDepth) + dPart2(:) = -dDepthWettingFront_dTk(:)*wettingFrontSuction / (depthWettingFront**2._rkind) + dXMaxInfilRate_dTk(:) = fPart1*dpart2(:) + fPart2*dPart1(:) + + ! define the infiltrating area and derivatives for the non-frozen part of the cell/basin + if(qSurfScale < qSurfScaleMax)then + fracCap = rootZoneLiq/(maxFracCap*availCapacity) ! fraction of available root zone filled with water + fInfRaw = 1._rkind - exp(-qSurfScale*(1._rkind - fracCap)) ! infiltrating area -- allowed to violate solution constraints + scalarInfilArea = min(0.5_rkind*(fInfRaw + sqrt(fInfRaw**2._rkind + scaleFactor)), 1._rkind) ! infiltrating area -- constrained + if (0.5_rkind*(fInfRaw + sqrt(fInfRaw**2._rkind + scaleFactor))< 1._rkind) then + dfracCap(:) = ( dRootZoneLiq_dWat(:)/maxFracCap + dRootZoneIce_dWat(:)*fracCap )/availCapacity + dfInfRaw(:) = -qSurfScale*dfracCap(:) * exp(-qSurfScale*(1._rkind - fracCap)) + dInfilArea_dWat(1:nSoil) = 0.5_rkind*dfInfRaw(:) * (1._rkind + fInfRaw/sqrt(fInfRaw**2._rkind + scaleFactor)) + dfracCap(:) = ( dRootZoneLiq_dTk(:)/maxFracCap + dRootZoneIce_dTk(:)*fracCap )/availCapacity + dfInfRaw(:) = -qSurfScale*dfracCap(:) * exp(-qSurfScale*(1._rkind - fracCap)) + dInfilArea_dTk(1:nSoil) = 0.5_rkind*dfInfRaw(:) * (1._rkind + fInfRaw/sqrt(fInfRaw**2._rkind + scaleFactor)) + else ! scalarInfilArea = 1._rkind + dInfilArea_dWat(1:nSoil) = 0._rkind + dInfilArea_dTk(1:nSoil) = 0._rkind + endif + else + scalarInfilArea = 1._rkind + dInfilArea_dWat(1:nSoil) = 0._rkind + dInfilArea_dTk(1:nSoil) = 0._rkind + endif + dInfilArea_dWat(0) = 0._rkind + dInfilArea_dTk(0) = 0._rkind - ! define the impermeable area and derivatives due to frozen ground - if(rootZoneIce > tiny(rootZoneIce))then ! (avoid divide by zero) - alpha = 1._rkind/(soilIceCV**2._rkind) ! shape parameter in the Gamma distribution - xLimg = alpha*soilIceScale/rootZoneIce ! upper limit of the integral - !scalarFrozenArea = 1._rkind - gammp(alpha,xLimg) ! fraction of frozen area - !if we use this, we will have a derivative of scalarFrozenArea w.r.t. water and temperature in each layer (through mLayerVolFracIce) - scalarFrozenArea = 0._rkind - dFrozenArea_dWat(1:nSoil) = 0._rkind - dFrozenArea_dTk(1:nSoil) = 0._rkind - else - scalarFrozenArea = 0._rkind - dFrozenArea_dWat(1:nSoil) = 0._rkind - dFrozenArea_dTk(1:nSoil) = 0._rkind - end if - dFrozenArea_dWat(0) = 0._rkind - dFrozenArea_dTk(0) = 0._rkind - !print*, 'scalarFrozenArea, rootZoneIce = ', scalarFrozenArea, rootZoneIce - - if (xMaxInfilRate < scalarRainPlusMelt) then ! = dXMaxInfilRate_d, dependent on layers not at surface - dInfilRate_dWat(0) = 0._rkind - dInfilRate_dTk(0) = 0._rkind - dInfilRate_dWat(1:nSoil) = dXMaxInfilRate_dWat(:) - dInfilRate_dTk(1:nSoil) = dXMaxInfilRate_dTk(:) - else ! = dRainPlusMelt_d, dependent on above layer (canopy or snow) water and temp - dInfilRate_dWat(0) = above_soilLiqFluxDeriv*above_soilFracLiq - dInfilRate_dTk(0) = above_soilLiqFluxDeriv*above_soildLiq_dTk - dInfilRate_dWat(1:nSoil) = 0._rkind - dInfilRate_dTk(1:nSoil) = 0._rkind - endif - - ! dq w.r.t. infiltration only, scalarRainPlusMelt accounted for in computeJacob module - dq_dHydStateVec(:) = (1._rkind - scalarFrozenArea) * ( dInfilArea_dWat(:)*min(scalarRainPlusMelt,xMaxInfilRate) + scalarInfilArea*dInfilRate_dWat(:) ) +& - (-dFrozenArea_dWat(:))*scalarInfilArea*min(scalarRainPlusMelt,xMaxInfilRate) - dq_dNrgStateVec(:) = (1._rkind - scalarFrozenArea) * ( dInfilArea_dTk(:) *min(scalarRainPlusMelt,xMaxInfilRate) + scalarInfilArea*dInfilRate_dTk(:) ) +& - (-dFrozenArea_dTk(:)) *scalarInfilArea*min(scalarRainPlusMelt,xMaxInfilRate) - - else ! do not compute infiltration after first flux call in a splitting operation - dq_dHydStateVec(:) = 0._rkind - dq_dNrgStateVec(:) = 0._rkind - - end if ! (if desire to compute infiltration) + ! check to ensure we are not infiltrating into a fully saturated column + if(ixIce 0.9999_rkind*theta_sat*sum(mLayerDepth(ixIce+1:nRoots))) scalarInfilArea=0._rkind + endif - ! compute infiltration (m s-1), if after first flux call in a splitting operation does not change - scalarSurfaceInfiltration = (1._rkind - scalarFrozenArea)*scalarInfilArea*min(scalarRainPlusMelt,xMaxInfilRate) + ! define the impermeable area and derivatives due to frozen ground + if(rootZoneIce > tiny(rootZoneIce))then ! (avoid divide by zero) + alpha = 1._rkind/(soilIceCV**2._rkind) ! shape parameter in the Gamma distribution + xLimg = alpha*soilIceScale/rootZoneIce ! upper limit of the integral - ! compute surface runoff (m s-1) - scalarSurfaceRunoff = scalarRainPlusMelt - scalarSurfaceInfiltration - !print*, 'scalarRainPlusMelt, xMaxInfilRate = ', scalarRainPlusMelt, xMaxInfilRate - !print*, 'scalarSurfaceInfiltration, scalarSurfaceRunoff = ', scalarSurfaceInfiltration, scalarSurfaceRunoff - !print*, '(1._rkind - scalarFrozenArea), (1._rkind - scalarFrozenArea)*scalarInfilArea = ', (1._rkind - scalarFrozenArea), (1._rkind - scalarFrozenArea)*scalarInfilArea + !if we use this, we will have a derivative of scalarFrozenArea w.r.t. water and temperature in each layer (through mLayerVolFracIce) + scalarFrozenArea = 0._rkind + dFrozenArea_dWat(1:nSoil) = 0._rkind + dFrozenArea_dTk(1:nSoil) = 0._rkind + else + scalarFrozenArea = 0._rkind + dFrozenArea_dWat(1:nSoil) = 0._rkind + dFrozenArea_dTk(1:nSoil) = 0._rkind + end if + dFrozenArea_dWat(0) = 0._rkind + dFrozenArea_dTk(0) = 0._rkind + + + if (xMaxInfilRate < scalarRainPlusMelt) then ! = dXMaxInfilRate_d, dependent on layers not at surface + dInfilRate_dWat(0) = 0._rkind + dInfilRate_dTk(0) = 0._rkind + dInfilRate_dWat(1:nSoil) = dXMaxInfilRate_dWat(:) + dInfilRate_dTk(1:nSoil) = dXMaxInfilRate_dTk(:) + else ! = dRainPlusMelt_d, dependent on above layer (canopy or snow) water and temp + dInfilRate_dWat(0) = above_soilLiqFluxDeriv*above_soilFracLiq + dInfilRate_dTk(0) = above_soilLiqFluxDeriv*above_soildLiq_dTk + dInfilRate_dWat(1:nSoil) = 0._rkind + dInfilRate_dTk(1:nSoil) = 0._rkind + endif - ! set surface hydraulic conductivity and diffusivity to missing (not used for flux condition) - surfaceHydCond = realMissing - surfaceDiffuse = realMissing + ! dq w.r.t. infiltration only, scalarRainPlusMelt accounted for in computeJacob module + dq_dHydStateVec(:) = (1._rkind - scalarFrozenArea) * ( dInfilArea_dWat(:)*min(scalarRainPlusMelt,xMaxInfilRate) + scalarInfilArea*dInfilRate_dWat(:) ) +& + (-dFrozenArea_dWat(:))*scalarInfilArea*min(scalarRainPlusMelt,xMaxInfilRate) + dq_dNrgStateVec(:) = (1._rkind - scalarFrozenArea) * ( dInfilArea_dTk(:) *min(scalarRainPlusMelt,xMaxInfilRate) + scalarInfilArea*dInfilRate_dTk(:) ) +& + (-dFrozenArea_dTk(:)) *scalarInfilArea*min(scalarRainPlusMelt,xMaxInfilRate) + + else ! do not compute infiltration after first flux call in a splitting operation + dq_dHydStateVec(:) = 0._rkind + dq_dNrgStateVec(:) = 0._rkind + + end if ! (if desire to compute infiltration) + + ! compute infiltration (m s-1), if after first flux call in a splitting operation does not change + scalarSurfaceInfiltration = (1._rkind - scalarFrozenArea)*scalarInfilArea*min(scalarRainPlusMelt,xMaxInfilRate) + + ! compute surface runoff (m s-1) + scalarSurfaceRunoff = scalarRainPlusMelt - scalarSurfaceInfiltration - ! ***** error check - case default; err=20; message=trim(message)//'unknown upper boundary condition for soil hydrology'; return - - end select ! (type of upper boundary condition) - - end subroutine surfaceFlx - - - ! *************************************************************************************************************** - ! private subroutine iLayerFlux: compute the fluxes and derivatives at layer interfaces - ! *************************************************************************************************************** - subroutine iLayerFlux(& - ! input: model control - deriv_desired, & ! intent(in): flag indicating if derivatives are desired - ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) - ! input: state variables (adjacent layers) - nodeMatricHeadLiqTrial, & ! intent(in): liquid matric head at the soil nodes (m) - nodeVolFracLiqTrial, & ! intent(in): volumetric liquid water content at the soil nodes (-) - ! input: model coordinate variables (adjacent layers) - nodeHeight, & ! intent(in): height of the soil nodes (m) - ! input: temperature derivatives - dPsiLiq_dTemp, & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) - dHydCond_dTemp, & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) - ! input: transmittance (adjacent layers) - nodeHydCondTrial, & ! intent(in): hydraulic conductivity at the soil nodes (m s-1) - nodeDiffuseTrial, & ! intent(in): hydraulic diffusivity at the soil nodes (m2 s-1) - ! input: transmittance derivatives (adjacent layers) - dHydCond_dVolLiq, & ! intent(in): derivative in hydraulic conductivity w.r.t. change in volumetric liquid water content (m s-1) - dDiffuse_dVolLiq, & ! intent(in): derivative in hydraulic diffusivity w.r.t. change in volumetric liquid water content (m2 s-1) - dHydCond_dMatric, & ! intent(in): derivative in hydraulic conductivity w.r.t. change in matric head (s-1) - ! output: tranmsmittance at the layer interface (scalars) - iLayerHydCond, & ! intent(out): hydraulic conductivity at the interface between layers (m s-1) - iLayerDiffuse, & ! intent(out): hydraulic diffusivity at the interface between layers (m2 s-1) - ! output: vertical flux at the layer interface (scalars) - iLayerLiqFluxSoil, & ! intent(out): vertical flux of liquid water at the layer interface (m s-1) - ! output: derivatives in fluxes w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below (m s-1 or s-1) - dq_dHydStateAbove, & ! intent(out): derivatives in the flux w.r.t. matric head or volumetric lquid water in the layer above (m s-1 or s-1) - dq_dHydStateBelow, & ! intent(out): derivatives in the flux w.r.t. matric head or volumetric lquid water in the layer below (m s-1 or s-1) - ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) - dq_dNrgStateAbove, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) - dq_dNrgStateBelow, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) - ! output: error control - err,message) ! intent(out): error control - ! ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - ! input: model control - logical(lgt),intent(in) :: deriv_desired ! flag indicating if derivatives are desired - integer(i4b),intent(in) :: ixRichards ! index defining the option for Richards' equation (moisture or mixdform) - ! input: state variables - real(rkind),intent(in) :: nodeMatricHeadLiqTrial(:) ! liquid matric head at the soil nodes (m) - real(rkind),intent(in) :: nodeVolFracLiqTrial(:) ! volumetric fraction of liquid water at the soil nodes (-) - ! input: model coordinate variables - real(rkind),intent(in) :: nodeHeight(:) ! height at the mid-point of the lower layer (m) - ! input: temperature derivatives - real(rkind),intent(in) :: dPsiLiq_dTemp(:) ! derivative in liquid water matric potential w.r.t. temperature (m K-1) - real(rkind),intent(in) :: dHydCond_dTemp(:) ! derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) - ! input: transmittance - real(rkind),intent(in) :: nodeHydCondTrial(:) ! hydraulic conductivity at layer mid-points (m s-1) - real(rkind),intent(in) :: nodeDiffuseTrial(:) ! diffusivity at layer mid-points (m2 s-1) - ! input: transmittance derivatives - real(rkind),intent(in) :: dHydCond_dVolLiq(:) ! derivative in hydraulic conductivity w.r.t volumetric liquid water content (m s-1) - real(rkind),intent(in) :: dDiffuse_dVolLiq(:) ! derivative in hydraulic diffusivity w.r.t volumetric liquid water content (m2 s-1) - real(rkind),intent(in) :: dHydCond_dMatric(:) ! derivative in hydraulic conductivity w.r.t matric head (m s-1) - ! output: tranmsmittance at the layer interface (scalars) - real(rkind),intent(out) :: iLayerHydCond ! hydraulic conductivity at the interface between layers (m s-1) - real(rkind),intent(out) :: iLayerDiffuse ! hydraulic diffusivity at the interface between layers (m2 s-1) - ! output: vertical flux at the layer interface (scalars) - real(rkind),intent(out) :: iLayerLiqFluxSoil ! vertical flux of liquid water at the layer interface (m s-1) - ! output: derivatives in fluxes w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below (m s-1 or s-1) - real(rkind),intent(out) :: dq_dHydStateAbove ! derivatives in the flux w.r.t. matric head or volumetric lquid water in the layer above (m s-1 or s-1) - real(rkind),intent(out) :: dq_dHydStateBelow ! derivatives in the flux w.r.t. matric head or volumetric lquid water in the layer below (m s-1 or s-1) - ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) - real(rkind),intent(out) :: dq_dNrgStateAbove ! derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) - real(rkind),intent(out) :: dq_dNrgStateBelow ! derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - ! local variables (named variables to provide index of 2-element vectors) - integer(i4b),parameter :: ixUpper=1 ! index of upper node in the 2-element vectors - integer(i4b),parameter :: ixLower=2 ! index of lower node in the 2-element vectors - logical(lgt),parameter :: useGeometric=.false. ! switch between the arithmetic and geometric mean - ! local variables (Darcy flux) - real(rkind) :: dPsi ! spatial difference in matric head (m) - real(rkind) :: dLiq ! spatial difference in volumetric liquid water (-) - real(rkind) :: dz ! spatial difference in layer mid-points (m) - real(rkind) :: cflux ! capillary flux (m s-1) - ! local variables (derivative in Darcy's flux) - real(rkind) :: dHydCondIface_dVolLiqAbove ! derivative in hydraulic conductivity at layer interface w.r.t. volumetric liquid water content in layer above - real(rkind) :: dHydCondIface_dVolLiqBelow ! derivative in hydraulic conductivity at layer interface w.r.t. volumetric liquid water content in layer below - real(rkind) :: dDiffuseIface_dVolLiqAbove ! derivative in hydraulic diffusivity at layer interface w.r.t. volumetric liquid water content in layer above - real(rkind) :: dDiffuseIface_dVolLiqBelow ! derivative in hydraulic diffusivity at layer interface w.r.t. volumetric liquid water content in layer below - real(rkind) :: dHydCondIface_dMatricAbove ! derivative in hydraulic conductivity at layer interface w.r.t. matric head in layer above - real(rkind) :: dHydCondIface_dMatricBelow ! derivative in hydraulic conductivity at layer interface w.r.t. matric head in layer below + ! set surface hydraulic conductivity and diffusivity to missing (not used for flux condition) + surfaceHydCond = realMissing + surfaceDiffuse = realMissing + + ! ***** error check + case default; err=20; message=trim(message)//'unknown upper boundary condition for soil hydrology'; return + + end select ! (type of upper boundary condition) + +end subroutine surfaceFlx + + +! *************************************************************************************************************** +! private subroutine iLayerFlux: compute the fluxes and derivatives at layer interfaces +! *************************************************************************************************************** +subroutine iLayerFlux(& + ! input: model control + deriv_desired, & ! intent(in): flag indicating if derivatives are desired + ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) + ! input: state variables (adjacent layers) + nodeMatricHeadLiqTrial, & ! intent(in): liquid matric head at the soil nodes (m) + nodeVolFracLiqTrial, & ! intent(in): volumetric liquid water content at the soil nodes (-) + ! input: model coordinate variables (adjacent layers) + nodeHeight, & ! intent(in): height of the soil nodes (m) + ! input: temperature derivatives + dPsiLiq_dTemp, & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) + dHydCond_dTemp, & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) + ! input: transmittance (adjacent layers) + nodeHydCondTrial, & ! intent(in): hydraulic conductivity at the soil nodes (m s-1) + nodeDiffuseTrial, & ! intent(in): hydraulic diffusivity at the soil nodes (m2 s-1) + ! input: transmittance derivatives (adjacent layers) + dHydCond_dVolLiq, & ! intent(in): derivative in hydraulic conductivity w.r.t. change in volumetric liquid water content (m s-1) + dDiffuse_dVolLiq, & ! intent(in): derivative in hydraulic diffusivity w.r.t. change in volumetric liquid water content (m2 s-1) + dHydCond_dMatric, & ! intent(in): derivative in hydraulic conductivity w.r.t. change in matric head (s-1) + ! output: tranmsmittance at the layer interface (scalars) + iLayerHydCond, & ! intent(out): hydraulic conductivity at the interface between layers (m s-1) + iLayerDiffuse, & ! intent(out): hydraulic diffusivity at the interface between layers (m2 s-1) + ! output: vertical flux at the layer interface (scalars) + iLayerLiqFluxSoil, & ! intent(out): vertical flux of liquid water at the layer interface (m s-1) + ! output: derivatives in fluxes w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below (m s-1 or s-1) + dq_dHydStateAbove, & ! intent(out): derivatives in the flux w.r.t. matric head or volumetric lquid water in the layer above (m s-1 or s-1) + dq_dHydStateBelow, & ! intent(out): derivatives in the flux w.r.t. matric head or volumetric lquid water in the layer below (m s-1 or s-1) + ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) + dq_dNrgStateAbove, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) + dq_dNrgStateBelow, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) + ! output: error control + err,message) ! intent(out): error control + ! ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + ! input: model control + logical(lgt),intent(in) :: deriv_desired ! flag indicating if derivatives are desired + integer(i4b),intent(in) :: ixRichards ! index defining the option for Richards' equation (moisture or mixdform) + ! input: state variables + real(rkind),intent(in) :: nodeMatricHeadLiqTrial(:) ! liquid matric head at the soil nodes (m) + real(rkind),intent(in) :: nodeVolFracLiqTrial(:) ! volumetric fraction of liquid water at the soil nodes (-) + ! input: model coordinate variables + real(rkind),intent(in) :: nodeHeight(:) ! height at the mid-point of the lower layer (m) + ! input: temperature derivatives + real(rkind),intent(in) :: dPsiLiq_dTemp(:) ! derivative in liquid water matric potential w.r.t. temperature (m K-1) + real(rkind),intent(in) :: dHydCond_dTemp(:) ! derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) + ! input: transmittance + real(rkind),intent(in) :: nodeHydCondTrial(:) ! hydraulic conductivity at layer mid-points (m s-1) + real(rkind),intent(in) :: nodeDiffuseTrial(:) ! diffusivity at layer mid-points (m2 s-1) + ! input: transmittance derivatives + real(rkind),intent(in) :: dHydCond_dVolLiq(:) ! derivative in hydraulic conductivity w.r.t volumetric liquid water content (m s-1) + real(rkind),intent(in) :: dDiffuse_dVolLiq(:) ! derivative in hydraulic diffusivity w.r.t volumetric liquid water content (m2 s-1) + real(rkind),intent(in) :: dHydCond_dMatric(:) ! derivative in hydraulic conductivity w.r.t matric head (m s-1) + ! output: tranmsmittance at the layer interface (scalars) + real(rkind),intent(out) :: iLayerHydCond ! hydraulic conductivity at the interface between layers (m s-1) + real(rkind),intent(out) :: iLayerDiffuse ! hydraulic diffusivity at the interface between layers (m2 s-1) + ! output: vertical flux at the layer interface (scalars) + real(rkind),intent(out) :: iLayerLiqFluxSoil ! vertical flux of liquid water at the layer interface (m s-1) + ! output: derivatives in fluxes w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below (m s-1 or s-1) + real(rkind),intent(out) :: dq_dHydStateAbove ! derivatives in the flux w.r.t. matric head or volumetric lquid water in the layer above (m s-1 or s-1) + real(rkind),intent(out) :: dq_dHydStateBelow ! derivatives in the flux w.r.t. matric head or volumetric lquid water in the layer below (m s-1 or s-1) + ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) + real(rkind),intent(out) :: dq_dNrgStateAbove ! derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) + real(rkind),intent(out) :: dq_dNrgStateBelow ! derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + ! local variables (named variables to provide index of 2-element vectors) + integer(i4b),parameter :: ixUpper=1 ! index of upper node in the 2-element vectors + integer(i4b),parameter :: ixLower=2 ! index of lower node in the 2-element vectors + logical(lgt),parameter :: useGeometric=.false. ! switch between the arithmetic and geometric mean + ! local variables (Darcy flux) + real(rkind) :: dPsi ! spatial difference in matric head (m) + real(rkind) :: dLiq ! spatial difference in volumetric liquid water (-) + real(rkind) :: dz ! spatial difference in layer mid-points (m) + real(rkind) :: cflux ! capillary flux (m s-1) + ! local variables (derivative in Darcy's flux) + real(rkind) :: dHydCondIface_dVolLiqAbove ! derivative in hydraulic conductivity at layer interface w.r.t. volumetric liquid water content in layer above + real(rkind) :: dHydCondIface_dVolLiqBelow ! derivative in hydraulic conductivity at layer interface w.r.t. volumetric liquid water content in layer below + real(rkind) :: dDiffuseIface_dVolLiqAbove ! derivative in hydraulic diffusivity at layer interface w.r.t. volumetric liquid water content in layer above + real(rkind) :: dDiffuseIface_dVolLiqBelow ! derivative in hydraulic diffusivity at layer interface w.r.t. volumetric liquid water content in layer below + real(rkind) :: dHydCondIface_dMatricAbove ! derivative in hydraulic conductivity at layer interface w.r.t. matric head in layer above + real(rkind) :: dHydCondIface_dMatricBelow ! derivative in hydraulic conductivity at layer interface w.r.t. matric head in layer below ! ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ! initialize error control err=0; message="iLayerFlux/" - ! ***** - ! compute the vertical flux of liquid water - ! compute the hydraulic conductivity at the interface - if(useGeometric)then - iLayerHydCond = (nodeHydCondTrial(ixLower) * nodeHydCondTrial(ixUpper))**0.5_rkind - else - iLayerHydCond = (nodeHydCondTrial(ixLower) + nodeHydCondTrial(ixUpper))*0.5_rkind - end if - !write(*,'(a,1x,5(e20.10,1x))') 'in iLayerFlux: iLayerHydCond, iLayerHydCondMP = ', iLayerHydCond, iLayerHydCondMP - ! compute the height difference between nodes - dz = nodeHeight(ixLower) - nodeHeight(ixUpper) - ! compute the capillary flux - select case(ixRichards) ! (form of Richards' equation) - case(moisture) - iLayerDiffuse = (nodeDiffuseTrial(ixLower) * nodeDiffuseTrial(ixUpper))**0.5_rkind - dLiq = nodeVolFracLiqTrial(ixLower) - nodeVolFracLiqTrial(ixUpper) - cflux = -iLayerDiffuse * dLiq/dz - case(mixdform) - iLayerDiffuse = realMissing - dPsi = nodeMatricHeadLiqTrial(ixLower) - nodeMatricHeadLiqTrial(ixUpper) - cflux = -iLayerHydCond * dPsi/dz - case default; err=10; message=trim(message)//"unable to identify option for Richards' equation"; return - end select - ! compute the total flux (add gravity flux, positive downwards) - iLayerLiqFluxSoil = cflux + iLayerHydCond - !write(*,'(a,1x,10(e20.10,1x))') 'iLayerLiqFluxSoil, dPsi, dz, cflux, iLayerHydCond = ', & - ! iLayerLiqFluxSoil, dPsi, dz, cflux, iLayerHydCond - - ! ** compute the derivatives - if(deriv_desired)then + ! ***** + ! compute the vertical flux of liquid water + ! compute the hydraulic conductivity at the interface + if(useGeometric)then + iLayerHydCond = (nodeHydCondTrial(ixLower) * nodeHydCondTrial(ixUpper))**0.5_rkind + else + iLayerHydCond = (nodeHydCondTrial(ixLower) + nodeHydCondTrial(ixUpper))*0.5_rkind + end if + !write(*,'(a,1x,5(e20.10,1x))') 'in iLayerFlux: iLayerHydCond, iLayerHydCondMP = ', iLayerHydCond, iLayerHydCondMP + ! compute the height difference between nodes + dz = nodeHeight(ixLower) - nodeHeight(ixUpper) + ! compute the capillary flux select case(ixRichards) ! (form of Richards' equation) - case(moisture) - ! still need to implement arithmetric mean for the moisture-based form - if(.not.useGeometric)then - message=trim(message)//'only currently implemented for geometric mean -- change local flag' - err=20; return - end if - ! derivatives in hydraulic conductivity at the layer interface (m s-1) - dHydCondIface_dVolLiqAbove = dHydCond_dVolLiq(ixUpper)*nodeHydCondTrial(ixLower) * 0.5_rkind/max(iLayerHydCond,verySmall) - dHydCondIface_dVolLiqBelow = dHydCond_dVolLiq(ixLower)*nodeHydCondTrial(ixUpper) * 0.5_rkind/max(iLayerHydCond,verySmall) - ! derivatives in hydraulic diffusivity at the layer interface (m2 s-1) - dDiffuseIface_dVolLiqAbove = dDiffuse_dVolLiq(ixUpper)*nodeDiffuseTrial(ixLower) * 0.5_rkind/max(iLayerDiffuse,verySmall) - dDiffuseIface_dVolLiqBelow = dDiffuse_dVolLiq(ixLower)*nodeDiffuseTrial(ixUpper) * 0.5_rkind/max(iLayerDiffuse,verySmall) - ! derivatives in the flux w.r.t. volumetric liquid water content - dq_dHydStateAbove = -dDiffuseIface_dVolLiqAbove*dLiq/dz + iLayerDiffuse/dz + dHydCondIface_dVolLiqAbove - dq_dHydStateBelow = -dDiffuseIface_dVolLiqBelow*dLiq/dz - iLayerDiffuse/dz + dHydCondIface_dVolLiqBelow - case(mixdform) - ! derivatives in hydraulic conductivity - if(useGeometric)then - dHydCondIface_dMatricAbove = dHydCond_dMatric(ixUpper)*nodeHydCondTrial(ixLower) * 0.5_rkind/max(iLayerHydCond,verySmall) - dHydCondIface_dMatricBelow = dHydCond_dMatric(ixLower)*nodeHydCondTrial(ixUpper) * 0.5_rkind/max(iLayerHydCond,verySmall) - else - dHydCondIface_dMatricAbove = dHydCond_dMatric(ixUpper)/2._rkind - dHydCondIface_dMatricBelow = dHydCond_dMatric(ixLower)/2._rkind - end if - ! derivatives in the flux w.r.t. matric head - dq_dHydStateAbove = -dHydCondIface_dMatricAbove*dPsi/dz + iLayerHydCond/dz + dHydCondIface_dMatricAbove - dq_dHydStateBelow = -dHydCondIface_dMatricBelow*dPsi/dz - iLayerHydCond/dz + dHydCondIface_dMatricBelow - ! derivative in the flux w.r.t. temperature - dq_dNrgStateAbove = -(dHydCond_dTemp(ixUpper)/2._rkind)*dPsi/dz + iLayerHydCond*dPsiLiq_dTemp(ixUpper)/dz + dHydCond_dTemp(ixUpper)/2._rkind - dq_dNrgStateBelow = -(dHydCond_dTemp(ixLower)/2._rkind)*dPsi/dz - iLayerHydCond*dPsiLiq_dTemp(ixLower)/dz + dHydCond_dTemp(ixLower)/2._rkind - case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return - end select - else - dq_dHydStateAbove = realMissing - dq_dHydStateBelow = realMissing - end if - - end subroutine iLayerFlux - - - ! *************************************************************************************************************** - ! private subroutine qDrainFlux: compute the drainage flux from the bottom of the soil profile and its derivative - ! *************************************************************************************************************** - subroutine qDrainFlux(& - ! input: model control - deriv_desired, & ! intent(in): flag indicating if derivatives are desired - ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) - bc_lower, & ! intent(in): index defining the type of boundary conditions - ! input: state variables - nodeMatricHead, & ! intent(in): matric head in the lowest unsaturated node (m) - nodeVolFracLiq, & ! intent(in): volumetric liquid water content the lowest unsaturated node (-) - ! input: model coordinate variables - nodeDepth, & ! intent(in): depth of the lowest unsaturated soil layer (m) - nodeHeight, & ! intent(in): height of the lowest unsaturated soil node (m) - ! input: boundary conditions - lowerBoundHead, & ! intent(in): lower boundary condition (m) - lowerBoundTheta, & ! intent(in): lower boundary condition (-) - ! input: derivative in soil water characteristix - node_dPsi_dTheta, & ! intent(in): derivative of the soil moisture characteristic w.r.t. theta (m) - ! input: transmittance - surfaceSatHydCond, & ! intent(in): saturated hydraulic conductivity at the surface (m s-1) - bottomSatHydCond, & ! intent(in): saturated hydraulic conductivity at the bottom of the unsaturated zone (m s-1) - nodeHydCond, & ! intent(in): hydraulic conductivity at the node itself (m s-1) - iceImpedeFac, & ! intent(in): ice impedence factor in the lower-most soil layer (-) - ! input: transmittance derivatives - dHydCond_dVolLiq, & ! intent(in): derivative in hydraulic conductivity w.r.t. volumetric liquid water content (m s-1) - dHydCond_dMatric, & ! intent(in): derivative in hydraulic conductivity w.r.t. matric head (s-1) - dHydCond_dTemp, & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) - ! input: soil parameters - vGn_alpha, & ! intent(in): van Genutchen "alpha" parameter (m-1) - vGn_n, & ! intent(in): van Genutchen "n" parameter (-) - vGn_m, & ! intent(in): van Genutchen "m" parameter (-) - theta_sat, & ! intent(in): soil porosity (-) - theta_res, & ! intent(in): soil residual volumetric water content (-) - kAnisotropic, & ! intent(in): anisotropy factor for lateral hydraulic conductivity (-) - zScale_TOPMODEL, & ! intent(in): TOPMODEL scaling factor (m) - ! output: hydraulic conductivity and diffusivity at the surface - bottomHydCond, & ! intent(out): hydraulic conductivity at the bottom of the unsatuarted zone (m s-1) - bottomDiffuse, & ! intent(out): hydraulic diffusivity at the bottom of the unsatuarted zone (m2 s-1) - ! output: drainage flux from the bottom of the soil profile - scalarDrainage, & ! intent(out): drainage flux from the bottom of the soil profile (m s-1) - ! output: derivatives in drainage flux - dq_dHydStateUnsat, & ! intent(out): change in drainage flux w.r.t. change in hydrology state variable in lowest unsaturated node (m s-1 or s-1) - dq_dNrgStateUnsat, & ! intent(out): change in drainage flux w.r.t. change in energy state variable in lowest unsaturated node (m s-1 K-1) - ! output: error control - err,message) ! intent(out): error control - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water as a function of matric head (-) - USE soil_utils_module,only:matricHead ! compute matric head as a function of volumetric fraction of liquid water (m) - USE soil_utils_module,only:hydCond_psi ! compute hydraulic conductivity as a function of matric head (m s-1) - USE soil_utils_module,only:hydCond_liq ! compute hydraulic conductivity as a function of volumetric liquid water content (m s-1) - USE soil_utils_module,only:dPsi_dTheta ! compute derivative of the soil moisture characteristic w.r.t. theta (m) - ! compute infiltraton at the surface and its derivative w.r.t. mass in the upper soil layer - implicit none - ! ----------------------------------------------------------------------------------------------------------------------------- - ! input: model control - logical(lgt),intent(in) :: deriv_desired ! flag to indicate if derivatives are desired - integer(i4b),intent(in) :: ixRichards ! index defining the option for Richards' equation (moisture or mixdform) - integer(i4b),intent(in) :: bc_lower ! index defining the type of boundary conditions - ! input: state and diagnostic variables - real(rkind),intent(in) :: nodeMatricHead ! matric head in the lowest unsaturated node (m) - real(rkind),intent(in) :: nodeVolFracLiq ! volumetric liquid water content in the lowest unsaturated node (-) - ! input: model coordinate variables - real(rkind),intent(in) :: nodeDepth ! depth of the lowest unsaturated soil layer (m) - real(rkind),intent(in) :: nodeHeight ! height of the lowest unsaturated soil node (m) - ! input: diriclet boundary conditions - real(rkind),intent(in) :: lowerBoundHead ! lower boundary condition for matric head (m) - real(rkind),intent(in) :: lowerBoundTheta ! lower boundary condition for volumetric liquid water content (-) - ! input: derivative in soil water characteristix - real(rkind),intent(in) :: node_dPsi_dTheta ! derivative of the soil moisture characteristic w.r.t. theta (m) - ! input: transmittance - real(rkind),intent(in) :: surfaceSatHydCond ! saturated hydraulic conductivity at the surface (m s-1) - real(rkind),intent(in) :: bottomSatHydCond ! saturated hydraulic conductivity at the bottom of the unsaturated zone (m s-1) - real(rkind),intent(in) :: nodeHydCond ! hydraulic conductivity at the node itself (m s-1) - real(rkind),intent(in) :: iceImpedeFac ! ice impedence factor in the upper-most soil layer (-) - ! input: transmittance derivatives - real(rkind),intent(in) :: dHydCond_dVolLiq ! derivative in hydraulic conductivity w.r.t. volumetric liquid water content (m s-1) - real(rkind),intent(in) :: dHydCond_dMatric ! derivative in hydraulic conductivity w.r.t. matric head (s-1) - real(rkind),intent(in) :: dHydCond_dTemp ! derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) - ! input: soil parameters - real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter (m-1) - real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter (-) - real(rkind),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) - real(rkind),intent(in) :: theta_sat ! soil porosity (-) - real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) - real(rkind),intent(in) :: kAnisotropic ! anisotropy factor for lateral hydraulic conductivity (-) - real(rkind),intent(in) :: zScale_TOPMODEL ! scale factor for TOPMODEL-ish baseflow parameterization (m) - ! ----------------------------------------------------------------------------------------------------------------------------- - ! output: hydraulic conductivity at the bottom of the unsaturated zone - real(rkind),intent(out) :: bottomHydCond ! hydraulic conductivity at the bottom of the unsaturated zone (m s-1) - real(rkind),intent(out) :: bottomDiffuse ! hydraulic diffusivity at the bottom of the unsatuarted zone (m2 s-1) - ! output: drainage flux from the bottom of the soil profile - real(rkind),intent(out) :: scalarDrainage ! drainage flux from the bottom of the soil profile (m s-1) - ! output: derivatives in drainage flux - real(rkind),intent(out) :: dq_dHydStateUnsat ! change in drainage flux w.r.t. change in state variable in lowest unsaturated node (m s-1 or s-1) - real(rkind),intent(out) :: dq_dNrgStateUnsat ! change in drainage flux w.r.t. change in energy state variable in lowest unsaturated node (m s-1 K-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ----------------------------------------------------------------------------------------------------------------------------- - ! local variables - real(rkind) :: zWater ! effective water table depth (m) - real(rkind) :: nodePsi ! matric head in the lowest unsaturated node (m) - real(rkind) :: cflux ! capillary flux (m s-1) - ! ----------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="qDrainFlux/" - - ! determine lower boundary condition - select case(bc_lower) - - ! --------------------------------------------------------------------------------------------- - ! * prescribed head - ! --------------------------------------------------------------------------------------------- - case(prescribedHead) - - ! compute fluxes - select case(ixRichards) ! (moisture-based form of Richards' equation) case(moisture) - ! compute the hydraulic conductivity and diffusivity at the boundary - bottomHydCond = hydCond_liq(lowerBoundTheta,bottomSatHydCond,theta_res,theta_sat,vGn_m) * iceImpedeFac - bottomDiffuse = dPsi_dTheta(lowerBoundTheta,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * bottomHydCond - ! compute the capillary flux - cflux = -bottomDiffuse*(lowerBoundTheta - nodeVolFracLiq) / (nodeDepth*0.5_rkind) + iLayerDiffuse = (nodeDiffuseTrial(ixLower) * nodeDiffuseTrial(ixUpper))**0.5_rkind + dLiq = nodeVolFracLiqTrial(ixLower) - nodeVolFracLiqTrial(ixUpper) + cflux = -iLayerDiffuse * dLiq/dz case(mixdform) - ! compute the hydraulic conductivity and diffusivity at the boundary - bottomHydCond = hydCond_psi(lowerBoundHead,bottomSatHydCond,vGn_alpha,vGn_n,vGn_m) * iceImpedeFac - bottomDiffuse = realMissing - ! compute the capillary flux - cflux = -bottomHydCond*(lowerBoundHead - nodeMatricHead) / (nodeDepth*0.5_rkind) - case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return - end select ! (form of Richards' eqn) - scalarDrainage = cflux + bottomHydCond + iLayerDiffuse = realMissing + dPsi = nodeMatricHeadLiqTrial(ixLower) - nodeMatricHeadLiqTrial(ixUpper) + cflux = -iLayerHydCond * dPsi/dz + case default; err=10; message=trim(message)//"unable to identify option for Richards' equation"; return + end select + ! compute the total flux (add gravity flux, positive downwards) + iLayerLiqFluxSoil = cflux + iLayerHydCond - ! compute derivatives - if(deriv_desired)then - ! hydrology derivatives - select case(ixRichards) ! (form of Richards' equation) - case(moisture); dq_dHydStateUnsat = bottomDiffuse/(nodeDepth/2._rkind) - case(mixdform); dq_dHydStateUnsat = bottomHydCond/(nodeDepth/2._rkind) - case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return - end select - ! energy derivatives - dq_dNrgStateUnsat = -(dHydCond_dTemp/2._rkind)*(lowerBoundHead - nodeMatricHead)/(nodeDepth*0.5_rkind) + dHydCond_dTemp/2._rkind - else ! (do not desire derivatives) - dq_dHydStateUnsat = realMissing - dq_dNrgStateUnsat = realMissing - end if - - ! --------------------------------------------------------------------------------------------- - ! * function of matric head in the bottom layer - ! --------------------------------------------------------------------------------------------- - case(funcBottomHead) - - ! compute fluxes - select case(ixRichards) - case(moisture); nodePsi = matricHead(nodeVolFracLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - case(mixdform); nodePsi = nodeMatricHead - end select - zWater = nodeHeight - nodePsi - scalarDrainage = kAnisotropic*surfaceSatHydCond * exp(-zWater/zScale_TOPMODEL) - - ! compute derivatives - if(deriv_desired)then - ! hydrology derivatives - select case(ixRichards) ! (form of Richards' equation) - case(moisture); dq_dHydStateUnsat = kAnisotropic*surfaceSatHydCond * node_dPsi_dTheta*exp(-zWater/zScale_TOPMODEL)/zScale_TOPMODEL - case(mixdform); dq_dHydStateUnsat = kAnisotropic*surfaceSatHydCond * exp(-zWater/zScale_TOPMODEL)/zScale_TOPMODEL - case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return - end select - ! energy derivatives - err=20; message=trim(message)//"not yet implemented energy derivatives"; return - else ! (do not desire derivatives) - dq_dHydStateUnsat = realMissing - dq_dNrgStateUnsat = realMissing - end if - - ! --------------------------------------------------------------------------------------------- - ! * free drainage - ! --------------------------------------------------------------------------------------------- - case(freeDrainage) - - ! compute flux - scalarDrainage = nodeHydCond*kAnisotropic - - ! compute derivatives - if(deriv_desired)then - ! hydrology derivatives + ! ** compute the derivatives + if(deriv_desired)then select case(ixRichards) ! (form of Richards' equation) - case(moisture); dq_dHydStateUnsat = dHydCond_dVolLiq*kAnisotropic - case(mixdform); dq_dHydStateUnsat = dHydCond_dMatric*kAnisotropic - case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return + case(moisture) + ! still need to implement arithmetric mean for the moisture-based form + if(.not.useGeometric)then + message=trim(message)//'only currently implemented for geometric mean -- change local flag' + err=20; return + end if + ! derivatives in hydraulic conductivity at the layer interface (m s-1) + dHydCondIface_dVolLiqAbove = dHydCond_dVolLiq(ixUpper)*nodeHydCondTrial(ixLower) * 0.5_rkind/max(iLayerHydCond,verySmall) + dHydCondIface_dVolLiqBelow = dHydCond_dVolLiq(ixLower)*nodeHydCondTrial(ixUpper) * 0.5_rkind/max(iLayerHydCond,verySmall) + ! derivatives in hydraulic diffusivity at the layer interface (m2 s-1) + dDiffuseIface_dVolLiqAbove = dDiffuse_dVolLiq(ixUpper)*nodeDiffuseTrial(ixLower) * 0.5_rkind/max(iLayerDiffuse,verySmall) + dDiffuseIface_dVolLiqBelow = dDiffuse_dVolLiq(ixLower)*nodeDiffuseTrial(ixUpper) * 0.5_rkind/max(iLayerDiffuse,verySmall) + ! derivatives in the flux w.r.t. volumetric liquid water content + dq_dHydStateAbove = -dDiffuseIface_dVolLiqAbove*dLiq/dz + iLayerDiffuse/dz + dHydCondIface_dVolLiqAbove + dq_dHydStateBelow = -dDiffuseIface_dVolLiqBelow*dLiq/dz - iLayerDiffuse/dz + dHydCondIface_dVolLiqBelow + case(mixdform) + ! derivatives in hydraulic conductivity + if(useGeometric)then + dHydCondIface_dMatricAbove = dHydCond_dMatric(ixUpper)*nodeHydCondTrial(ixLower) * 0.5_rkind/max(iLayerHydCond,verySmall) + dHydCondIface_dMatricBelow = dHydCond_dMatric(ixLower)*nodeHydCondTrial(ixUpper) * 0.5_rkind/max(iLayerHydCond,verySmall) + else + dHydCondIface_dMatricAbove = dHydCond_dMatric(ixUpper)/2._rkind + dHydCondIface_dMatricBelow = dHydCond_dMatric(ixLower)/2._rkind + end if + ! derivatives in the flux w.r.t. matric head + dq_dHydStateAbove = -dHydCondIface_dMatricAbove*dPsi/dz + iLayerHydCond/dz + dHydCondIface_dMatricAbove + dq_dHydStateBelow = -dHydCondIface_dMatricBelow*dPsi/dz - iLayerHydCond/dz + dHydCondIface_dMatricBelow + ! derivative in the flux w.r.t. temperature + dq_dNrgStateAbove = -(dHydCond_dTemp(ixUpper)/2._rkind)*dPsi/dz + iLayerHydCond*dPsiLiq_dTemp(ixUpper)/dz + dHydCond_dTemp(ixUpper)/2._rkind + dq_dNrgStateBelow = -(dHydCond_dTemp(ixLower)/2._rkind)*dPsi/dz - iLayerHydCond*dPsiLiq_dTemp(ixLower)/dz + dHydCond_dTemp(ixLower)/2._rkind + case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return end select - ! energy derivatives - dq_dNrgStateUnsat = dHydCond_dTemp*kAnisotropic - else ! (do not desire derivatives) - dq_dHydStateUnsat = realMissing - dq_dNrgStateUnsat = realMissing - end if - - - ! --------------------------------------------------------------------------------------------- - ! * zero flux - ! --------------------------------------------------------------------------------------------- - case(zeroFlux) - scalarDrainage = 0._rkind - if(deriv_desired)then - dq_dHydStateUnsat = 0._rkind - dq_dNrgStateUnsat = 0._rkind - else - dq_dHydStateUnsat = realMissing - dq_dNrgStateUnsat = realMissing - end if - - ! --------------------------------------------------------------------------------------------- - ! * error check - ! --------------------------------------------------------------------------------------------- - case default; err=20; message=trim(message)//'unknown lower boundary condition for soil hydrology'; return - - end select ! (type of boundary condition) - - end subroutine qDrainFlux - - - ! ******************************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************************* + else + dq_dHydStateAbove = realMissing + dq_dHydStateBelow = realMissing + end if +end subroutine iLayerFlux + + +! *************************************************************************************************************** +! private subroutine qDrainFlux: compute the drainage flux from the bottom of the soil profile and its derivative +! *************************************************************************************************************** +subroutine qDrainFlux(& + ! input: model control + deriv_desired, & ! intent(in): flag indicating if derivatives are desired + ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) + bc_lower, & ! intent(in): index defining the type of boundary conditions + ! input: state variables + nodeMatricHead, & ! intent(in): matric head in the lowest unsaturated node (m) + nodeVolFracLiq, & ! intent(in): volumetric liquid water content the lowest unsaturated node (-) + ! input: model coordinate variables + nodeDepth, & ! intent(in): depth of the lowest unsaturated soil layer (m) + nodeHeight, & ! intent(in): height of the lowest unsaturated soil node (m) + ! input: boundary conditions + lowerBoundHead, & ! intent(in): lower boundary condition (m) + lowerBoundTheta, & ! intent(in): lower boundary condition (-) + ! input: derivative in soil water characteristix + node_dPsi_dTheta, & ! intent(in): derivative of the soil moisture characteristic w.r.t. theta (m) + ! input: transmittance + surfaceSatHydCond, & ! intent(in): saturated hydraulic conductivity at the surface (m s-1) + bottomSatHydCond, & ! intent(in): saturated hydraulic conductivity at the bottom of the unsaturated zone (m s-1) + nodeHydCond, & ! intent(in): hydraulic conductivity at the node itself (m s-1) + iceImpedeFac, & ! intent(in): ice impedence factor in the lower-most soil layer (-) + ! input: transmittance derivatives + dHydCond_dVolLiq, & ! intent(in): derivative in hydraulic conductivity w.r.t. volumetric liquid water content (m s-1) + dHydCond_dMatric, & ! intent(in): derivative in hydraulic conductivity w.r.t. matric head (s-1) + dHydCond_dTemp, & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) + ! input: soil parameters + vGn_alpha, & ! intent(in): van Genutchen "alpha" parameter (m-1) + vGn_n, & ! intent(in): van Genutchen "n" parameter (-) + vGn_m, & ! intent(in): van Genutchen "m" parameter (-) + theta_sat, & ! intent(in): soil porosity (-) + theta_res, & ! intent(in): soil residual volumetric water content (-) + kAnisotropic, & ! intent(in): anisotropy factor for lateral hydraulic conductivity (-) + zScale_TOPMODEL, & ! intent(in): TOPMODEL scaling factor (m) + ! output: hydraulic conductivity and diffusivity at the surface + bottomHydCond, & ! intent(out): hydraulic conductivity at the bottom of the unsatuarted zone (m s-1) + bottomDiffuse, & ! intent(out): hydraulic diffusivity at the bottom of the unsatuarted zone (m2 s-1) + ! output: drainage flux from the bottom of the soil profile + scalarDrainage, & ! intent(out): drainage flux from the bottom of the soil profile (m s-1) + ! output: derivatives in drainage flux + dq_dHydStateUnsat, & ! intent(out): change in drainage flux w.r.t. change in hydrology state variable in lowest unsaturated node (m s-1 or s-1) + dq_dNrgStateUnsat, & ! intent(out): change in drainage flux w.r.t. change in energy state variable in lowest unsaturated node (m s-1 K-1) + ! output: error control + err,message) ! intent(out): error control + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water as a function of matric head (-) + USE soil_utils_module,only:matricHead ! compute matric head as a function of volumetric fraction of liquid water (m) + USE soil_utils_module,only:hydCond_psi ! compute hydraulic conductivity as a function of matric head (m s-1) + USE soil_utils_module,only:hydCond_liq ! compute hydraulic conductivity as a function of volumetric liquid water content (m s-1) + USE soil_utils_module,only:dPsi_dTheta ! compute derivative of the soil moisture characteristic w.r.t. theta (m) + ! compute infiltraton at the surface and its derivative w.r.t. mass in the upper soil layer + implicit none + ! ----------------------------------------------------------------------------------------------------------------------------- + ! input: model control + logical(lgt),intent(in) :: deriv_desired ! flag to indicate if derivatives are desired + integer(i4b),intent(in) :: ixRichards ! index defining the option for Richards' equation (moisture or mixdform) + integer(i4b),intent(in) :: bc_lower ! index defining the type of boundary conditions + ! input: state and diagnostic variables + real(rkind),intent(in) :: nodeMatricHead ! matric head in the lowest unsaturated node (m) + real(rkind),intent(in) :: nodeVolFracLiq ! volumetric liquid water content in the lowest unsaturated node (-) + ! input: model coordinate variables + real(rkind),intent(in) :: nodeDepth ! depth of the lowest unsaturated soil layer (m) + real(rkind),intent(in) :: nodeHeight ! height of the lowest unsaturated soil node (m) + ! input: diriclet boundary conditions + real(rkind),intent(in) :: lowerBoundHead ! lower boundary condition for matric head (m) + real(rkind),intent(in) :: lowerBoundTheta ! lower boundary condition for volumetric liquid water content (-) + ! input: derivative in soil water characteristix + real(rkind),intent(in) :: node_dPsi_dTheta ! derivative of the soil moisture characteristic w.r.t. theta (m) + ! input: transmittance + real(rkind),intent(in) :: surfaceSatHydCond ! saturated hydraulic conductivity at the surface (m s-1) + real(rkind),intent(in) :: bottomSatHydCond ! saturated hydraulic conductivity at the bottom of the unsaturated zone (m s-1) + real(rkind),intent(in) :: nodeHydCond ! hydraulic conductivity at the node itself (m s-1) + real(rkind),intent(in) :: iceImpedeFac ! ice impedence factor in the upper-most soil layer (-) + ! input: transmittance derivatives + real(rkind),intent(in) :: dHydCond_dVolLiq ! derivative in hydraulic conductivity w.r.t. volumetric liquid water content (m s-1) + real(rkind),intent(in) :: dHydCond_dMatric ! derivative in hydraulic conductivity w.r.t. matric head (s-1) + real(rkind),intent(in) :: dHydCond_dTemp ! derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) + ! input: soil parameters + real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter (m-1) + real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter (-) + real(rkind),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) + real(rkind),intent(in) :: theta_sat ! soil porosity (-) + real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) + real(rkind),intent(in) :: kAnisotropic ! anisotropy factor for lateral hydraulic conductivity (-) + real(rkind),intent(in) :: zScale_TOPMODEL ! scale factor for TOPMODEL-ish baseflow parameterization (m) + ! ----------------------------------------------------------------------------------------------------------------------------- + ! output: hydraulic conductivity at the bottom of the unsaturated zone + real(rkind),intent(out) :: bottomHydCond ! hydraulic conductivity at the bottom of the unsaturated zone (m s-1) + real(rkind),intent(out) :: bottomDiffuse ! hydraulic diffusivity at the bottom of the unsatuarted zone (m2 s-1) + ! output: drainage flux from the bottom of the soil profile + real(rkind),intent(out) :: scalarDrainage ! drainage flux from the bottom of the soil profile (m s-1) + ! output: derivatives in drainage flux + real(rkind),intent(out) :: dq_dHydStateUnsat ! change in drainage flux w.r.t. change in state variable in lowest unsaturated node (m s-1 or s-1) + real(rkind),intent(out) :: dq_dNrgStateUnsat ! change in drainage flux w.r.t. change in energy state variable in lowest unsaturated node (m s-1 K-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ----------------------------------------------------------------------------------------------------------------------------- + ! local variables + real(rkind) :: zWater ! effective water table depth (m) + real(rkind) :: nodePsi ! matric head in the lowest unsaturated node (m) + real(rkind) :: cflux ! capillary flux (m s-1) + ! ----------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="qDrainFlux/" + + ! determine lower boundary condition + select case(bc_lower) + + ! --------------------------------------------------------------------------------------------- + ! * prescribed head + ! --------------------------------------------------------------------------------------------- + case(prescribedHead) + + ! compute fluxes + select case(ixRichards) ! (moisture-based form of Richards' equation) + case(moisture) + ! compute the hydraulic conductivity and diffusivity at the boundary + bottomHydCond = hydCond_liq(lowerBoundTheta,bottomSatHydCond,theta_res,theta_sat,vGn_m) * iceImpedeFac + bottomDiffuse = dPsi_dTheta(lowerBoundTheta,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * bottomHydCond + ! compute the capillary flux + cflux = -bottomDiffuse*(lowerBoundTheta - nodeVolFracLiq) / (nodeDepth*0.5_rkind) + case(mixdform) + ! compute the hydraulic conductivity and diffusivity at the boundary + bottomHydCond = hydCond_psi(lowerBoundHead,bottomSatHydCond,vGn_alpha,vGn_n,vGn_m) * iceImpedeFac + bottomDiffuse = realMissing + ! compute the capillary flux + cflux = -bottomHydCond*(lowerBoundHead - nodeMatricHead) / (nodeDepth*0.5_rkind) + case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return + end select ! (form of Richards' eqn) + scalarDrainage = cflux + bottomHydCond + + ! compute derivatives + if(deriv_desired)then + ! hydrology derivatives + select case(ixRichards) ! (form of Richards' equation) + case(moisture); dq_dHydStateUnsat = bottomDiffuse/(nodeDepth/2._rkind) + case(mixdform); dq_dHydStateUnsat = bottomHydCond/(nodeDepth/2._rkind) + case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return + end select + ! energy derivatives + dq_dNrgStateUnsat = -(dHydCond_dTemp/2._rkind)*(lowerBoundHead - nodeMatricHead)/(nodeDepth*0.5_rkind) + dHydCond_dTemp/2._rkind + else ! (do not desire derivatives) + dq_dHydStateUnsat = realMissing + dq_dNrgStateUnsat = realMissing + end if + + ! --------------------------------------------------------------------------------------------- + ! * function of matric head in the bottom layer + ! --------------------------------------------------------------------------------------------- + case(funcBottomHead) + + ! compute fluxes + select case(ixRichards) + case(moisture); nodePsi = matricHead(nodeVolFracLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + case(mixdform); nodePsi = nodeMatricHead + end select + zWater = nodeHeight - nodePsi + scalarDrainage = kAnisotropic*surfaceSatHydCond * exp(-zWater/zScale_TOPMODEL) + + ! compute derivatives + if(deriv_desired)then + ! hydrology derivatives + select case(ixRichards) ! (form of Richards' equation) + case(moisture); dq_dHydStateUnsat = kAnisotropic*surfaceSatHydCond * node_dPsi_dTheta*exp(-zWater/zScale_TOPMODEL)/zScale_TOPMODEL + case(mixdform); dq_dHydStateUnsat = kAnisotropic*surfaceSatHydCond * exp(-zWater/zScale_TOPMODEL)/zScale_TOPMODEL + case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return + end select + ! energy derivatives + err=20; message=trim(message)//"not yet implemented energy derivatives"; return + else ! (do not desire derivatives) + dq_dHydStateUnsat = realMissing + dq_dNrgStateUnsat = realMissing + end if + + case(freeDrainage) + + ! compute flux + scalarDrainage = nodeHydCond*kAnisotropic + + ! compute derivatives + if(deriv_desired)then + ! hydrology derivatives + select case(ixRichards) ! (form of Richards' equation) + case(moisture); dq_dHydStateUnsat = dHydCond_dVolLiq*kAnisotropic + case(mixdform); dq_dHydStateUnsat = dHydCond_dMatric*kAnisotropic + case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return + end select + ! energy derivatives + dq_dNrgStateUnsat = dHydCond_dTemp*kAnisotropic + else ! (do not desire derivatives) + dq_dHydStateUnsat = realMissing + dq_dNrgStateUnsat = realMissing + end if + + case(zeroFlux) + scalarDrainage = 0._rkind + if(deriv_desired)then + dq_dHydStateUnsat = 0._rkind + dq_dNrgStateUnsat = 0._rkind + else + dq_dHydStateUnsat = realMissing + dq_dNrgStateUnsat = realMissing + end if + + case default; err=20; message=trim(message)//'unknown lower boundary condition for soil hydrology'; return + + end select ! (type of boundary condition) + +end subroutine qDrainFlux end module soilLiqFlx_module From bda1d891539843d1af2b07c477e1c0f1d7f19781 Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 27 Sep 2022 19:44:30 +0000 Subject: [PATCH 0369/1472] adjusted indentatino --- build/source/engine/computFlux.f90 | 1878 ++++++++++++++-------------- 1 file changed, 935 insertions(+), 943 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index c25266062..19716dbc6 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -101,954 +101,946 @@ module computFlux_module contains - ! ********************************************************************************************************* - ! public subroutine computFlux: compute model fluxes - ! ********************************************************************************************************* - subroutine computFlux(& - ! input-output: model control - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to denote the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - insideIDA, & ! intent(in): flag to indicate inside Sundials Solver (do not require longwave to be balanced) - drainageMeltPond, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) - ! input: state variables - scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) - scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) - mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) - mLayerMatricHeadLiqTrial, & ! intent(in): trial value for the liquid water matric potential in each soil layer (m) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - scalarAquiferStorageTrial,& ! intent(in): trial value of storage of water in the aquifer (m) - ! input: diagnostic variables defining the liquid water and ice content - scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) - mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liquid water content in each snow and soil layer (-) - mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) - ! input: data structures - model_decisions, & ! intent(in): model decisions - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): index data - ! input-output: data structures - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: flux vector and baseflow derivatives - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - fluxVec, & ! intent(out): flux vector (mixed units) - ! output: error control - err,message) ! intent(out): error code and error message - ! provide access to flux subroutines - USE vegNrgFlux_module,only:vegNrgFlux ! compute energy fluxes over vegetation - USE ssdNrgFlux_module,only:ssdNrgFlux ! compute energy fluxes throughout the snow and soil subdomains - USE vegLiqFlux_module,only:vegLiqFlux ! compute liquid water fluxes through vegetation - USE snowLiqFlx_module,only:snowLiqflx ! compute liquid water fluxes through snow - USE soilLiqFlx_module,only:soilLiqflx ! compute liquid water fluxes through soil - USE groundwatr_module,only:groundwatr ! compute the baseflow flux - USE bigAquifer_module,only:bigAquifer ! compute fluxes for the big aquifer - implicit none - ! --------------------------------------------------------------------------------------- - ! * dummy variables - ! --------------------------------------------------------------------------------------- - ! input-output: control - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - logical(lgt),intent(in) :: insideIDA ! flag if inside Sundials solver - real(rkind),intent(in) :: drainageMeltPond ! drainage from the surface melt pond (kg m-2 s-1) - ! input: state variables - real(rkind),intent(in) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind),intent(in) :: mLayerTempTrial(:) ! trial value for temperature of each snow/soil layer (K) - real(rkind),intent(in) :: mLayerMatricHeadLiqTrial(:) ! trial value for the liquid water matric potential (m) - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial value for the total water matric potential (m) - real(rkind),intent(in) :: scalarAquiferStorageTrial ! trial value of aquifer storage (m) - ! input: diagnostic variables - real(rkind),intent(in) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) - real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) - ! input: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(var_i), intent(in) :: type_data ! type of vegetation and soil - type(var_d), intent(in) :: attr_data ! spatial attributes - type(var_dlength), intent(in) :: mpar_data ! model parameters - type(var_d), intent(in) :: forc_data ! model forcing data - type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin - type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_ilength), intent(in) :: indx_data ! indices defining model states and layers - ! input-output: data structures - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! input-output: flux vector and baseflow derivatives - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - real(rkind),intent(out) :: fluxVec(:) ! model flux vector (mixed units) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! --------------------------------------------------------------------------------------- - ! * local variables - ! --------------------------------------------------------------------------------------- - integer(i4b) :: local_ixGroundwater ! local index for groundwater representation - integer(i4b) :: iLayer ! index of model layers - logical(lgt) :: doVegNrgFlux ! flag to compute the energy flux over vegetation - real(rkind),dimension(nSoil) :: dHydCond_dMatric ! derivative in hydraulic conductivity w.r.t matric head (s-1) - character(LEN=256) :: cmessage ! error message of downwind routine - real(rkind) :: above_soilLiqFluxDeriv ! derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water - real(rkind) :: above_soildLiq_dTk ! derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature - real(rkind) :: above_soilFracLiq ! fraction of liquid water layer above soil (canopy or snow) (-) - - ! -------------------------------------------------------------- - ! initialize error control - err=0; message='computFlux/' - - ! ***** - ! (0) PRELIMINARIES... - ! ******************** - - ! get the necessary variables for the flux computations - associate(& - - ! model decisions - ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization - ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) - - ! domain boundary conditions - upperBoundTemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) - scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in): [dp] rainfall rate (kg m-2 s-1) - - ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - - ! indices of model state variables for the vegetation subdomain - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain - ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer - - ! indices of model state variables for the snow+soil domain - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] type of layer (iname_soil or iname_snow) - - ! number of state variables of a specific type - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain - nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain - nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain - - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - - ! derivatives - dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) - dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(in): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature - - ! number of flux calls - numFluxCalls => diag_data%var(iLookDIAG%numFluxCalls)%dat(1) ,& ! intent(out): [dp] number of flux calls (-) - - ! net fluxes over the vegetation domain - scalarCanairNetNrgFlux => flux_data%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the canopy air space (W m-2) - scalarCanopyNetNrgFlux => flux_data%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the vegetation canopy (W m-2) - scalarGroundNetNrgFlux => flux_data%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the ground surface (W m-2) - scalarCanopyNetLiqFlux => flux_data%var(iLookFLUX%scalarCanopyNetLiqFlux)%dat(1) ,& ! intent(out): [dp] net liquid water flux for the vegetation canopy (kg m-2 s-1) - - ! net fluxes over the snow+soil domain - mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) - mLayerLiqFluxSnow => flux_data%var(iLookFLUX%mLayerLiqFluxSnow)%dat ,& ! intent(out): [dp] net liquid water flux for each snow layer (s-1) - mLayerLiqFluxSoil => flux_data%var(iLookFLUX%mLayerLiqFluxSoil)%dat ,& ! intent(out): [dp] net liquid water flux for each soil layer (s-1) - - ! evaporative fluxes - scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(out): [dp] canopy transpiration (kg m-2 s-1) - scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(out): [dp] canopy evaporation/condensation (kg m-2 s-1) - scalarGroundEvaporation => flux_data%var(iLookFLUX%scalarGroundEvaporation)%dat(1) ,& ! intent(out): [dp] ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(out): [dp(:)] transpiration loss from each soil layer (m s-1) - - ! fluxes for the snow+soil domain - iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(out): [dp(0:)] vertical energy flux at the interface of snow and soil layers - iLayerLiqFluxSnow => flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at snow layer interfaces (-) - iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at soil layer interfaces (-) - mLayerHydCond => flux_data%var(iLookFLUX%mLayerHydCond)%dat ,& ! intent(out): [dp(:)] hydraulic conductivity in each soil layer (m s-1) - mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(out): [dp(:)] baseflow from each soil layer (m s-1) - scalarSnowDrainage => flux_data%var(iLookFLUX%scalarSnowDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the snow profile (m s-1) - scalarSoilDrainage => flux_data%var(iLookFLUX%scalarSoilDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the soil profile (m s-1) - scalarSoilBaseflow => flux_data%var(iLookFLUX%scalarSoilBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the soil profile (m s-1) - - ! infiltration - scalarInfilArea => diag_data%var(iLookDIAG%scalarInfilArea )%dat(1) ,& ! intent(out): [dp] fraction of unfrozen area where water can infiltrate (-) - scalarFrozenArea => diag_data%var(iLookDIAG%scalarFrozenArea )%dat(1) ,& ! intent(out): [dp] fraction of area that is considered impermeable due to soil ice (-) - scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1) ,& ! intent(out): [dp] soil control on infiltration, zero or one - scalarMaxInfilRate => flux_data%var(iLookFLUX%scalarMaxInfilRate)%dat(1) ,& ! intent(out): [dp] maximum infiltration rate (m s-1) - scalarInfiltration => flux_data%var(iLookFLUX%scalarInfiltration)%dat(1) ,& ! intent(out): [dp] infiltration of water into the soil profile (m s-1) - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(inout): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(inout): [dp(:)] fraction of liquid water in each snow layer (-) - - ! boundary fluxes in the soil domain - scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(out): [dp] rain that reaches the ground without ever touching the canopy (kg m-2 s-1) - scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(out): [dp] drainage of liquid water from the vegetation canopy (kg m-2 s-1) - scalarRainPlusMelt => flux_data%var(iLookFLUX%scalarRainPlusMelt)%dat(1) ,& ! intent(out): [dp] rain plus melt (m s-1) - scalarSurfaceRunoff => flux_data%var(iLookFLUX%scalarSurfaceRunoff)%dat(1) ,& ! intent(out): [dp] surface runoff (m s-1) - scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1) ,& ! intent(out): [dp] exfiltration from the soil profile (m s-1) - mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat ,& ! intent(out): [dp(:)] column outflow from each soil layer (m3 s-1) - - ! fluxes for the aquifer - scalarAquiferTranspire => flux_data%var(iLookFLUX%scalarAquiferTranspire)%dat(1) ,& ! intent(out): [dp] transpiration loss from the aquifer (m s-1 - scalarAquiferRecharge => flux_data%var(iLookFLUX%scalarAquiferRecharge)%dat(1) ,& ! intent(out): [dp] recharge to the aquifer (m s-1) - scalarAquiferBaseflow => flux_data%var(iLookFLUX%scalarAquiferBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the aquifer (m s-1) - - ! total runoff - scalarTotalRunoff => flux_data%var(iLookFLUX%scalarTotalRunoff)%dat(1) ,& ! intent(out): [dp] total runoff (m s-1) - - ! derivatives in net vegetation energy fluxes w.r.t. relevant state variables - dCanairNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy air temperature - dCanairNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy temperature - dCanairNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. ground temperature - dCanopyNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy air temperature - dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy temperature - dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. ground temperature - dCanopyNetFlux_dCanWat => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in net canopy fluxes w.r.t. canopy total water content - dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy air temperature - dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy temperature - dGroundNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. ground temperature - dGroundNetFlux_dCanWat => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in net ground fluxes w.r.t. canopy total water content - - ! derivatives in evaporative fluxes w.r.t. relevant state variables - dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy air temperature - dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy temperature - dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. ground temperature - dCanopyEvaporation_dCanWat => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy total water content - dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy air temperature - dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy temperature - dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. ground temperature - dGroundEvaporation_dCanWat => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy total water content - - ! derivatives in transpiration - dCanopyTrans_dTCanair => deriv_data%var(iLookDERIV%dCanopyTrans_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTCanopy => deriv_data%var(iLookDERIV%dCanopyTrans_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTGround => deriv_data%var(iLookDERIV%dCanopyTrans_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - dCanopyTrans_dCanWat => deriv_data%var(iLookDERIV%dCanopyTrans_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy total water content (s-1) - - ! derivatives in canopy water w.r.t canopy temperature - dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(out): [dp] derivative of canopy liquid storage w.r.t. temperature - - ! derivatives in canopy liquid fluxes w.r.t. canopy water - scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ,& ! intent(out): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water - scalarThroughfallRainDeriv => deriv_data%var(iLookDERIV%scalarThroughfallRainDeriv )%dat(1) ,& ! intent(out): [dp] derivative in throughfall w.r.t. canopy liquid water - scalarCanopyLiqDrainageDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDrainageDeriv)%dat(1) ,& ! intent(out): [dp] derivative in canopy drainage w.r.t. canopy liquid water - - ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below - dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above - dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below - - ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below - dNrgFlux_dWatAbove => deriv_data%var(iLookDERIV%dNrgFlux_dWatAbove )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. water state in the layer above - dNrgFlux_dWatBelow => deriv_data%var(iLookDERIV%dNrgFlux_dWatBelow )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. water state in the layer below - - ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above - iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(out): [dp(:)] derivative in vertical liquid water flux at layer interfaces - - ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential - dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above - dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below - dq_dHydStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dHydStateLayerSurfVec )%dat ,& ! intent(out): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers - mLayerdTheta_dPsi => deriv_data%var(iLookDERIV%mLayerdTheta_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. psi - mLayerdPsi_dTheta => deriv_data%var(iLookDERIV%mLayerdPsi_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. theta - dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in compressibility w.r.t matric head - - ! derivative in baseflow flux w.r.t. aquifer storage - dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] erivative in baseflow flux w.r.t. aquifer storage (s-1) - - ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables - dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above - dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below - dq_dNrgStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dNrgStateLayerSurfVec )%dat ,& ! intent(out): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers - - ! derivatives in soil transpiration w.r.t. canopy state variables - mLayerdTrans_dTCanair => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanair )%dat ,& !intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature - mLayerdTrans_dTCanopy => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanopy )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy temperature - mLayerdTrans_dTGround => deriv_data%var(iLookDERIV%mLayerdTrans_dTGround )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. ground temperature - mLayerdTrans_dCanWat => deriv_data%var(iLookDERIV%mLayerdTrans_dCanWat )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy total water - - ! derivatives in aquifer transpiration w.r.t. canopy state variables - dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair )%dat(1) ,& !intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature - dAquiferTrans_dTCanopy => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanopy )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy temperature - dAquiferTrans_dTGround => deriv_data%var(iLookDERIV%dAquiferTrans_dTGround )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. ground temperature - dAquiferTrans_dCanWat => deriv_data%var(iLookDERIV%dAquiferTrans_dCanWat )%dat(1) & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy total water - - ) ! association to data in structures - - ! ***** - ! * PRELIMINARIES... - ! ****************** - - !print*, '***** nSnowSoilNrg, nSnowOnlyNrg, nSoilOnlyNrg, nSnowSoilHyd, nSnowOnlyHyd, nSoilOnlyHyd = ', & - ! nSnowSoilNrg, nSnowOnlyNrg, nSoilOnlyNrg, nSnowSoilHyd, nSnowOnlyHyd, nSoilOnlyHyd - - ! increment the number of flux calls - numFluxCalls = numFluxCalls+1 - - ! modify the groundwater representation for this single-column implementation - select case(ixSpatialGroundwater) - case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale - case(localColumn); local_ixGroundwater = ixGroundwater ! go with the specified decision - case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return - end select ! (modify the groundwater representation for this single-column implementation) - - ! initialize liquid water fluxes throughout the snow and soil domains - ! NOTE: used in the energy routines, which is called before the hydrology routines - if(firstFluxCall)then - if(nSnow > 0) iLayerLiqFluxSnow(0:nSnow) = 0._rkind - end if - - ! ***** - ! * CALCULATE ENERGY FLUXES OVER VEGETATION... - ! ********************************************* - - ! identify the need to calculate the energy flux over vegetation - doVegNrgFlux = (ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing .or. ixTopNrg/=integerMissing) - - ! check if there is a need to calculate the energy fluxes over vegetation - if(doVegNrgFlux)then - - ! derivative in canopy liquid storage w.r.t. canopy temperature - dCanLiq_dTcanopy = dTheta_dTkCanopy*iden_water*canopyDepth ! kg m-2 K-1 - - ! calculate the energy fluxes over vegetation - call vegNrgFlux(& - ! input: model control - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - insideIDA, & ! intent(in): flag to indicate inside Sundials Solver (do not require longwave to be balanced) - ! input: model state variables - upperBoundTemp, & ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature - scalarCanairTempTrial, & ! intent(in): trial value of the canopy air space temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - mLayerTempTrial(1), & ! intent(in): trial value of ground temperature (K) - scalarCanopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) - ! input: model derivatives - dCanLiq_dTcanopy, & ! intent(in): derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) - ! input/output: data structures - type_data, & ! intent(in): type of vegetation and soil - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): index data - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - bvar_data, & ! intent(in): model variables for the local basin - model_decisions, & ! intent(in): model decisions - ! output: liquid water fluxes associated with evaporation/transpiration - scalarCanopyTranspiration, & ! intent(out): canopy transpiration (kg m-2 s-1) - scalarCanopyEvaporation, & ! intent(out): canopy evaporation/condensation (kg m-2 s-1) - scalarGroundEvaporation, & ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - ! output: fluxes - scalarCanairNetNrgFlux, & ! intent(out): net energy flux for the canopy air space (W m-2) - scalarCanopyNetNrgFlux, & ! intent(out): net energy flux for the vegetation canopy (W m-2) - scalarGroundNetNrgFlux, & ! intent(out): net energy flux for the ground surface (W m-2) - ! output: flux derivatives - dCanairNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) - dCanairNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) - dCanairNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) - dCanopyNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) - dCanopyNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) - dCanopyNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - dGroundNetFlux_dCanairTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) - dGroundNetFlux_dCanopyTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) - dGroundNetFlux_dGroundTemp, & ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - ! output: liquid water flux derivatives (canopy evap) - dCanopyEvaporation_dCanWat, & ! intent(out): derivative in canopy evaporation w.r.t. canopy total water content (s-1) - dCanopyEvaporation_dTCanair, & ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTCanopy, & ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTGround, & ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - ! output: liquid water flux derivatives (ground evap) - dGroundEvaporation_dCanWat, & ! intent(out): derivative in ground evaporation w.r.t. canopy total water content (s-1) - dGroundEvaporation_dTCanair, & ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTCanopy, & ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTGround, & ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - ! output: transpiration derivatives - dCanopyTrans_dCanWat, & ! intent(out): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - dCanopyTrans_dTCanair, & ! intent(out): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTCanopy, & ! intent(out): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTGround, & ! intent(out): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - ! output: cross derivative terms - dCanopyNetFlux_dCanWat, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) - dGroundNetFlux_dCanWat, & ! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - ! check fluxes - if(globalPrintFlag)then - print*, '**' - write(*,'(a,1x,10(f30.20))') 'canopyDepth = ', canopyDepth - write(*,'(a,1x,10(f30.20))') 'mLayerDepth(1:2) = ', mLayerDepth(1:2) - write(*,'(a,1x,10(f30.20))') 'scalarCanairTempTrial = ', scalarCanairTempTrial ! trial value of the canopy air space temperature (K) - write(*,'(a,1x,10(f30.20))') 'scalarCanopyTempTrial = ', scalarCanopyTempTrial ! trial value of canopy temperature (K) - write(*,'(a,1x,10(f30.20))') 'mLayerTempTrial(1:2) = ', mLayerTempTrial(1:2) ! trial value of ground temperature (K) - write(*,'(a,1x,10(f30.20))') 'scalarCanairNetNrgFlux = ', scalarCanairNetNrgFlux - write(*,'(a,1x,10(f30.20))') 'scalarCanopyNetNrgFlux = ', scalarCanopyNetNrgFlux - write(*,'(a,1x,10(f30.20))') 'scalarGroundNetNrgFlux = ', scalarGroundNetNrgFlux - write(*,'(a,1x,10(f30.20))') 'dGroundNetFlux_dGroundTemp = ', dGroundNetFlux_dGroundTemp - endif ! if checking fluxes - - endif ! if calculating the energy fluxes over vegetation - - ! ***** - ! * CALCULATE ENERGY FLUXES THROUGH THE SNOW-SOIL DOMAIN... - ! ********************************************************** - - ! check the need to compute energy fluxes throughout the snow+soil domain - if(nSnowSoilNrg>0)then - - ! calculate energy fluxes at layer interfaces through the snow and soil domain - call ssdNrgFlux(& - ! input: model control - (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution - .true., & ! intent(in): flag indicating if derivatives are desired - ! input: fluxes and derivatives at the upper boundary - scalarGroundNetNrgFlux, & ! intent(in): total flux at the ground surface (W m-2) - dGroundNetFlux_dGroundTemp, & ! intent(in): derivative in total ground surface flux w.r.t. ground temperature (W m-2 K-1) - ! input: liquid water fluxes throughout the snow and soil domains - iLayerLiqFluxSnow, & ! intent(in): liquid flux at the interface of each snow layer (m s-1) - iLayerLiqFluxSoil, & ! intent(in): liquid flux at the interface of each soil layer (m s-1) - ! input: trial value of model state variables - mLayerTempTrial, & ! intent(in): trial temperature at the current iteration (K) - mLayerMatricHeadTrial, & ! intent(in): trial value for the total water matric potential in each soil layer (m) - mLayerVolFracLiqTrial, & ! intent(in): trial volumetric fraction of liquid water at the current iteration(-) - mLayerVolFracIceTrial, & ! intent(in): trial volumetric fraction of ice water at the current iteration(-) - ! input: pre-computed derivatives - mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - ! input-output: data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model indices - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output: fluxes and derivatives at all layer interfaces - iLayerNrgFlux, & ! intent(out): energy flux at the layer interfaces (W m-2) - dNrgFlux_dTempAbove, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (W m-2 K-1) - dNrgFlux_dTempBelow, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (W m-2 K-1) - dNrgFlux_dWatAbove, & ! intent(out): derivatives in the flux w.r.t. water state in the layer above - dNrgFlux_dWatBelow, & ! intent(out): derivatives in the flux w.r.t. water state in the layer below - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! calculate net energy fluxes for each snow and soil layer (J m-3 s-1) - do iLayer=1,nLayers - mLayerNrgFlux(iLayer) = -(iLayerNrgFlux(iLayer) - iLayerNrgFlux(iLayer-1))/mLayerDepth(iLayer) - if(globalPrintFlag)then - if(iLayer < 10) write(*,'(a,1x,i4,1x,10(f25.15,1x))') 'iLayer, iLayerNrgFlux(iLayer-1:iLayer), mLayerNrgFlux(iLayer) = ', iLayer, iLayerNrgFlux(iLayer-1:iLayer), mLayerNrgFlux(iLayer) - endif - end do - - endif ! if computing energy fluxes throughout the snow+soil domain - - - ! ***** - ! * CALCULATE THE LIQUID FLUX THROUGH VEGETATION... - ! ************************************************** - - ! check the need to compute the liquid water fluxes through vegetation - if(ixVegHyd/=integerMissing)then - - ! calculate liquid water fluxes through vegetation - call vegLiqFlux(& - ! input - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation - scalarCanopyLiqTrial, & ! intent(in): trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) - scalarRainfall, & ! intent(in): rainfall rate (kg m-2 s-1) - ! input-output: data structures - mpar_data, & ! intent(in): model parameters - diag_data, & ! intent(in): local HRU diagnostic model variables - ! output - scalarThroughfallRain, & ! intent(out): rain that reaches the ground without ever touching the canopy (kg m-2 s-1) - scalarCanopyLiqDrainage, & ! intent(out): drainage of liquid water from the vegetation canopy (kg m-2 s-1) - scalarThroughfallRainDeriv, & ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) - scalarCanopyLiqDrainageDeriv, & ! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! calculate the net liquid water flux for the vegetation canopy - scalarCanopyNetLiqFlux = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage - - ! calculate the total derivative in the downward liquid flux - scalarCanopyLiqDeriv = scalarThroughfallRainDeriv + scalarCanopyLiqDrainageDeriv - - ! test - if(globalPrintFlag)then - print*, '**' - print*, 'scalarRainfall = ', scalarRainfall - print*, 'scalarThroughfallRain = ', scalarThroughfallRain - print*, 'scalarCanopyEvaporation = ', scalarCanopyEvaporation - print*, 'scalarCanopyLiqDrainage = ', scalarCanopyLiqDrainage - print*, 'scalarCanopyNetLiqFlux = ', scalarCanopyNetLiqFlux - print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial - endif - - endif ! computing the liquid water fluxes through vegetation - - ! ***** - ! * CALCULATE THE LIQUID FLUX THROUGH SNOW... - ! ******************************************** - - ! check the need to compute liquid water fluxes through snow - if(nSnowOnlyHyd>0)then - - ! compute liquid fluxes through snow - call snowLiqFlx(& - ! input: model control - nSnow, & ! intent(in): number of snow layers - firstFluxCall, & ! intent(in): the first flux call (compute variables that are constant over the iterations) - (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution - ! input: forcing for the snow domain - scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) - scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) - ! input: model state vector - mLayerVolFracLiqTrial(1:nSnow), & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) - ! input-output: data structures - indx_data, & ! intent(in): model indices - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output: fluxes and derivatives - iLayerLiqFluxSnow(0:nSnow), & ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) - iLayerLiqFluxSnowDeriv(0:nSnow), & ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! define forcing for the soil domain - scalarRainPlusMelt = iLayerLiqFluxSnow(nSnow) ! drainage from the base of the snowpack - - ! calculate net liquid water fluxes for each snow layer (s-1) - do iLayer=1,nSnow - mLayerLiqFluxSnow(iLayer) = -(iLayerLiqFluxSnow(iLayer) - iLayerLiqFluxSnow(iLayer-1))/mLayerDepth(iLayer) - !write(*,'(a,1x,i4,1x,2(f16.10,1x))') 'iLayer, mLayerLiqFluxSnow(iLayer), iLayerLiqFluxSnow(iLayer-1) = ', & - ! iLayer, mLayerLiqFluxSnow(iLayer), iLayerLiqFluxSnow(iLayer-1) - end do - - ! compute drainage from the soil zone (needed for mass balance checks) - scalarSnowDrainage = iLayerLiqFluxSnow(nSnow) - - ! save bottom layer of snow derivatives - above_soilLiqFluxDeriv = iLayerLiqFluxSnowDeriv(nSnow) ! derivative in vertical liquid water flux at bottom snow layer interface - above_soildLiq_dTk = mLayerdTheta_dTk(nSnow) ! derivative in volumetric liquid water content in bottom snow layer w.r.t. temperature - above_soilFracLiq = mLayerFracLiqSnow(nSnow) ! fraction of liquid water in bottom snow layer (-) - - else - - ! define forcing for the soil domain for the case of no snow layers - ! NOTE: in case where nSnowOnlyHyd==0 AND snow layers exist, then scalarRainPlusMelt is taken from the previous flux evaluation - if(nSnow==0)then !no snow layers - scalarRainPlusMelt = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water & ! liquid flux from the canopy (m s-1) - + drainageMeltPond/iden_water ! melt of the snow without a layer (m s-1) - - if(ixVegHyd/=integerMissing)then - ! save canopy derivatives - above_soilLiqFluxDeriv = scalarCanopyLiqDeriv/iden_water ! derivative in (throughfall + drainage) w.r.t. canopy liquid water - above_soildLiq_dTk = dCanLiq_dTcanopy ! derivative of canopy liquid storage w.r.t. temperature - above_soilFracLiq = scalarFracLiqVeg ! fraction of liquid water in canopy (-) - else - above_soilLiqFluxDeriv = 0._rkind - above_soildLiq_dTk = 0._rkind - above_soilFracLiq = 0._rkind - endif - else ! snow layers, take from previous flux calculation - above_soilLiqFluxDeriv = iLayerLiqFluxSnowDeriv(nSnow) ! derivative in vertical liquid water flux at bottom snow layer interface - above_soildLiq_dTk = mLayerdTheta_dTk(nSnow) ! derivative in volumetric liquid water content in bottom snow layer w.r.t. temperature - above_soilFracLiq = mLayerFracLiqSnow(nSnow) ! fraction of liquid water in bottom snow layer (-) - endif ! snow layers or not - - endif ! if calculating the liquid flux through snow - - ! ***** - ! * CALCULATE THE LIQUID FLUX THROUGH SOIL... - ! ******************************************** - - ! check the need to calculate the liquid flux through soil - if(nSoilOnlyHyd>0)then - - ! calculate the liquid flux through soil - call soilLiqFlx(& - ! input: model control - nSoil, & ! intent(in): number of soil layers - firstSplitOper, & ! intent(in): flag indicating first flux call in a splitting operation - (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution - .true., & ! intent(in): flag indicating if derivatives are desired - ! input: trial state variables - mLayerTempTrial(nSnow+1:nLayers), & ! intent(in): trial temperature at the current iteration (K) - mLayerMatricHeadLiqTrial(1:nSoil), & ! intent(in): liquid water matric potential (m) - mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of ice (-) - ! input: pre-computed deriavatives - mLayerdTheta_dTk(nSnow+1:nLayers), & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - dPsiLiq_dTemp(1:nSoil), & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) - dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water - above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature - above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) - ! input: fluxes - scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) - scalarGroundEvaporation, & ! intent(in): ground evaporation (kg m-2 s-1) - scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) - ! input-output: data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model indices - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output: diagnostic variables for surface runoff - scalarMaxInfilRate, & ! intent(inout): maximum infiltration rate (m s-1) - scalarInfilArea, & ! intent(inout): fraction of unfrozen area where water can infiltrate (-) - scalarFrozenArea, & ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) - scalarSurfaceRunoff, & ! intent(inout): surface runoff (m s-1) - ! output: diagnostic variables for model layers - mLayerdTheta_dPsi, & ! intent(inout): derivative in the soil water characteristic w.r.t. psi (m-1) - mLayerdPsi_dTheta, & ! intent(inout): derivative in the soil water characteristic w.r.t. theta (m) - dHydCond_dMatric, & ! intent(inout): derivative in hydraulic conductivity w.r.t matric head (s-1) - ! output: fluxes - scalarInfiltration, & ! intent(inout): surface infiltration rate (m s-1) -- controls on infiltration only computed for iter==1 - iLayerLiqFluxSoil, & ! intent(inout): liquid fluxes at layer interfaces (m s-1) - mLayerTranspire, & ! intent(inout): transpiration loss from each soil layer (m s-1) - mLayerHydCond, & ! intent(inout): hydraulic conductivity in each layer (m s-1) - ! output: derivatives in fluxes w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below (m s-1 or s-1) - dq_dHydStateAbove, & ! intent(inout): derivatives in the flux w.r.t. matric head in the layer above (s-1) - dq_dHydStateBelow, & ! intent(inout): derivatives in the flux w.r.t. matric head in the layer below (s-1) - dq_dHydStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) - ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) - dq_dNrgStateAbove, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) - dq_dNrgStateBelow, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) - dq_dNrgStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) - ! output: derivatives in transpiration w.r.t. canopy state variables - mLayerdTrans_dTCanair, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature - mLayerdTrans_dTCanopy, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy temperature - mLayerdTrans_dTGround, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. ground temperature - mLayerdTrans_dCanWat, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy total water - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! calculate net liquid water fluxes for each soil layer (s-1) - do iLayer=1,nSoil - mLayerLiqFluxSoil(iLayer) = -(iLayerLiqFluxSoil(iLayer) - iLayerLiqFluxSoil(iLayer-1))/mLayerDepth(iLayer+nSnow) - !if(iLayer<8) write(*,'(a,1x,2(i4,1x),3(e20.10),f12.7)') 'iLayerLiqFluxSoil(iLayer-1), iLayerLiqFluxSoil(iLayer), mLayerLiqFluxSoil(iLayer) = ', iLayer-1, iLayer, & - ! iLayerLiqFluxSoil(iLayer-1), iLayerLiqFluxSoil(iLayer), mLayerLiqFluxSoil(iLayer), mLayerDepth(iLayer+nSnow) - end do - - ! calculate the soil control on infiltration - if(nSnow==0) then - ! * case of infiltration into soil - if(scalarMaxInfilRate > scalarRainPlusMelt)then ! infiltration is not rate-limited - scalarSoilControl = (1._rkind - scalarFrozenArea)*scalarInfilArea - else - scalarSoilControl = 0._rkind ! (scalarRainPlusMelt exceeds maximum infiltration rate - endif - else - ! * case of infiltration into snow - scalarSoilControl = 1._rkind - endif - - ! compute drainage from the soil zone (needed for mass balance checks and in aquifer recharge) - scalarSoilDrainage = iLayerLiqFluxSoil(nSoil) - - ! expand derivatives to the total water matric potential - ! NOTE: arrays are offset because computing derivatives in interface fluxes, at the top and bottom of the layer respectively - if(globalPrintFlag) print*, 'dPsiLiq_dPsi0(1:nSoil) = ', dPsiLiq_dPsi0(1:nSoil) - dq_dHydStateAbove(1:nSoil) = dq_dHydStateAbove(1:nSoil) *dPsiLiq_dPsi0(1:nSoil) - dq_dHydStateBelow(0:nSoil-1) = dq_dHydStateBelow(0:nSoil-1)*dPsiLiq_dPsi0(1:nSoil) - dq_dHydStateLayerSurfVec(1:nSoil) = dq_dHydStateLayerSurfVec(1:nSoil)*dPsiLiq_dPsi0(1:nSoil) - - endif ! if calculating the liquid flux through soil - - ! ***** - ! * CALCULATE THE GROUNDWATER FLOW... - ! ************************************ - - ! check if computing soil hydrology - if(nSoilOnlyHyd>0)then - - ! set baseflow fluxes to zero if the topmodel baseflow routine is not used - if(local_ixGroundwater/=qbaseTopmodel)then - ! (diagnostic variables in the data structures) - scalarExfiltration = 0._rkind ! exfiltration from the soil profile (m s-1) - mLayerColumnOutflow(:) = 0._rkind ! column outflow from each soil layer (m3 s-1) - ! (variables needed for the numerical solution) - mLayerBaseflow(:) = 0._rkind ! baseflow from each soil layer (m s-1) - - ! topmodel-ish shallow groundwater - else ! local_ixGroundwater==qbaseTopmodel - - ! check the derivative matrix is sized appropriately - if(size(dBaseflow_dMatric,1)/=nSoil .or. size(dBaseflow_dMatric,2)/=nSoil)then - message=trim(message)//'expect dBaseflow_dMatric to be nSoil x nSoil' - err=20; return - endif - - ! compute the baseflow flux - call groundwatr(& - ! input: model control - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - firstFluxCall, & ! intent(in): logical flag to compute index of the lowest saturated layer - ! input: state and diagnostic variables - mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) - mLayerMatricHeadLiqTrial, & ! intent(in): liquid water matric potential (m) - mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of ice (-) - ! input: data structures - attr_data, & ! intent(in): model attributes - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output - ixSaturation, & ! intent(inout) index of lowest saturated layer (NOTE: only computed on the first iteration) - mLayerBaseflow, & ! intent(out): baseflow from each soil layer (m s-1) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - endif ! computing baseflow flux - - ! compute total baseflow from the soil zone (needed for mass balance checks) - scalarSoilBaseflow = sum(mLayerBaseflow) - - ! compute total runoff - ! (Note: scalarSoilBaseflow is zero if topmodel is not used) - ! (Note: scalarSoilBaseflow may need to re-envisioned in topmodel formulation if parts of it flow into neighboring soil rather than exfiltrate) - scalarTotalRunoff = scalarSurfaceRunoff + scalarSoilDrainage + scalarSoilBaseflow - - endif ! if computing soil hydrology - - - ! ***** - ! (7) CALCULATE FLUXES FOR THE DEEP AQUIFER... - ! ******************************************** - - ! check if computing aquifer fluxes - if(ixAqWat/=integerMissing)then - - ! identify modeling decision - if(local_ixGroundwater==bigBucket)then - - ! compute fluxes for the big bucket - call bigAquifer(& - ! input: state variables and fluxes - scalarAquiferStorageTrial, & ! intent(in): trial value of aquifer storage (m) - scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) - scalarSoilDrainage, & ! intent(in): soil drainage (m s-1) - ! input: pre-computed derivatives - dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - ! input: diagnostic variables and parameters - mpar_data, & ! intent(in): model parameter structure - diag_data, & ! intent(in): diagnostic variable structure - ! output: fluxes - scalarAquiferTranspire, & ! intent(out): transpiration loss from the aquifer (m s-1) - scalarAquiferRecharge, & ! intent(out): recharge to the aquifer (m s-1) - scalarAquiferBaseflow, & ! intent(out): total baseflow from the aquifer (m s-1) - dBaseflow_dAquifer, & ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) - ! output: derivatives in transpiration w.r.t. canopy state variables - dAquiferTrans_dTCanair, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature - dAquiferTrans_dTCanopy, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy temperature - dAquiferTrans_dTGround, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. ground temperature - dAquiferTrans_dCanWat, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy total water - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! compute total runoff (overwrite previously calculated value before considering aquifer). - ! (Note: SoilDrainage goes into aquifer, not runoff) - scalarTotalRunoff = scalarSurfaceRunoff + scalarAquiferBaseflow - - ! if no aquifer, then fluxes are zero +! ********************************************************************************************************* +! public subroutine computFlux: compute model fluxes +! ********************************************************************************************************* +subroutine computFlux(& + ! input-output: model control + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to denote the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + insideIDA, & ! intent(in): flag to indicate inside Sundials Solver (do not require longwave to be balanced) + drainageMeltPond, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) + ! input: state variables + scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) + scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) + mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) + mLayerMatricHeadLiqTrial, & ! intent(in): trial value for the liquid water matric potential in each soil layer (m) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + scalarAquiferStorageTrial,& ! intent(in): trial value of storage of water in the aquifer (m) + ! input: diagnostic variables defining the liquid water and ice content + scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) + mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liquid water content in each snow and soil layer (-) + mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) + ! input: data structures + model_decisions, & ! intent(in): model decisions + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): index data + ! input-output: data structures + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: flux vector and baseflow derivatives + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + fluxVec, & ! intent(out): flux vector (mixed units) + ! output: error control + err,message) ! intent(out): error code and error message + ! provide access to flux subroutines + USE vegNrgFlux_module,only:vegNrgFlux ! compute energy fluxes over vegetation + USE ssdNrgFlux_module,only:ssdNrgFlux ! compute energy fluxes throughout the snow and soil subdomains + USE vegLiqFlux_module,only:vegLiqFlux ! compute liquid water fluxes through vegetation + USE snowLiqFlx_module,only:snowLiqflx ! compute liquid water fluxes through snow + USE soilLiqFlx_module,only:soilLiqflx ! compute liquid water fluxes through soil + USE groundwatr_module,only:groundwatr ! compute the baseflow flux + USE bigAquifer_module,only:bigAquifer ! compute fluxes for the big aquifer + implicit none + ! --------------------------------------------------------------------------------------- + ! * dummy variables + ! --------------------------------------------------------------------------------------- + ! input-output: control + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + logical(lgt),intent(in) :: insideIDA ! flag if inside Sundials solver + real(rkind),intent(in) :: drainageMeltPond ! drainage from the surface melt pond (kg m-2 s-1) + ! input: state variables + real(rkind),intent(in) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind),intent(in) :: mLayerTempTrial(:) ! trial value for temperature of each snow/soil layer (K) + real(rkind),intent(in) :: mLayerMatricHeadLiqTrial(:) ! trial value for the liquid water matric potential (m) + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial value for the total water matric potential (m) + real(rkind),intent(in) :: scalarAquiferStorageTrial ! trial value of aquifer storage (m) + ! input: diagnostic variables + real(rkind),intent(in) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) + ! input: data structures + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(var_i), intent(in) :: type_data ! type of vegetation and soil + type(var_d), intent(in) :: attr_data ! spatial attributes + type(var_dlength), intent(in) :: mpar_data ! model parameters + type(var_d), intent(in) :: forc_data ! model forcing data + type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin + type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_ilength), intent(in) :: indx_data ! indices defining model states and layers + ! input-output: data structures + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + ! input-output: flux vector and baseflow derivatives + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + real(rkind),intent(out) :: fluxVec(:) ! model flux vector (mixed units) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! --------------------------------------------------------------------------------------- + ! * local variables + ! --------------------------------------------------------------------------------------- + integer(i4b) :: local_ixGroundwater ! local index for groundwater representation + integer(i4b) :: iLayer ! index of model layers + logical(lgt) :: doVegNrgFlux ! flag to compute the energy flux over vegetation + real(rkind),dimension(nSoil) :: dHydCond_dMatric ! derivative in hydraulic conductivity w.r.t matric head (s-1) + character(LEN=256) :: cmessage ! error message of downwind routine + real(rkind) :: above_soilLiqFluxDeriv ! derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + real(rkind) :: above_soildLiq_dTk ! derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + real(rkind) :: above_soilFracLiq ! fraction of liquid water layer above soil (canopy or snow) (-) + + ! -------------------------------------------------------------- + ! initialize error control + err=0; message='computFlux/' + + ! ***** + ! (0) PRELIMINARIES... + ! ******************** + + ! get the necessary variables for the flux computations + associate(& + + ! model decisions + ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization + ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) + + ! domain boundary conditions + upperBoundTemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) + scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in): [dp] rainfall rate (kg m-2 s-1) + + ! canopy and layer depth + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + + ! indices of model state variables for the vegetation subdomain + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain + ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer + + ! indices of model state variables for the snow+soil domain + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] type of layer (iname_soil or iname_snow) + + ! number of state variables of a specific type + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain + nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + + ! derivatives + dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) + dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(in): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + + ! number of flux calls + numFluxCalls => diag_data%var(iLookDIAG%numFluxCalls)%dat(1) ,& ! intent(out): [dp] number of flux calls (-) + + ! net fluxes over the vegetation domain + scalarCanairNetNrgFlux => flux_data%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the canopy air space (W m-2) + scalarCanopyNetNrgFlux => flux_data%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the vegetation canopy (W m-2) + scalarGroundNetNrgFlux => flux_data%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the ground surface (W m-2) + scalarCanopyNetLiqFlux => flux_data%var(iLookFLUX%scalarCanopyNetLiqFlux)%dat(1) ,& ! intent(out): [dp] net liquid water flux for the vegetation canopy (kg m-2 s-1) + + ! net fluxes over the snow+soil domain + mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) + mLayerLiqFluxSnow => flux_data%var(iLookFLUX%mLayerLiqFluxSnow)%dat ,& ! intent(out): [dp] net liquid water flux for each snow layer (s-1) + mLayerLiqFluxSoil => flux_data%var(iLookFLUX%mLayerLiqFluxSoil)%dat ,& ! intent(out): [dp] net liquid water flux for each soil layer (s-1) + + ! evaporative fluxes + scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(out): [dp] canopy transpiration (kg m-2 s-1) + scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(out): [dp] canopy evaporation/condensation (kg m-2 s-1) + scalarGroundEvaporation => flux_data%var(iLookFLUX%scalarGroundEvaporation)%dat(1) ,& ! intent(out): [dp] ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(out): [dp(:)] transpiration loss from each soil layer (m s-1) + + ! fluxes for the snow+soil domain + iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(out): [dp(0:)] vertical energy flux at the interface of snow and soil layers + iLayerLiqFluxSnow => flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at snow layer interfaces (-) + iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at soil layer interfaces (-) + mLayerHydCond => flux_data%var(iLookFLUX%mLayerHydCond)%dat ,& ! intent(out): [dp(:)] hydraulic conductivity in each soil layer (m s-1) + mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(out): [dp(:)] baseflow from each soil layer (m s-1) + scalarSnowDrainage => flux_data%var(iLookFLUX%scalarSnowDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the snow profile (m s-1) + scalarSoilDrainage => flux_data%var(iLookFLUX%scalarSoilDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the soil profile (m s-1) + scalarSoilBaseflow => flux_data%var(iLookFLUX%scalarSoilBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the soil profile (m s-1) + + ! infiltration + scalarInfilArea => diag_data%var(iLookDIAG%scalarInfilArea )%dat(1) ,& ! intent(out): [dp] fraction of unfrozen area where water can infiltrate (-) + scalarFrozenArea => diag_data%var(iLookDIAG%scalarFrozenArea )%dat(1) ,& ! intent(out): [dp] fraction of area that is considered impermeable due to soil ice (-) + scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1) ,& ! intent(out): [dp] soil control on infiltration, zero or one + scalarMaxInfilRate => flux_data%var(iLookFLUX%scalarMaxInfilRate)%dat(1) ,& ! intent(out): [dp] maximum infiltration rate (m s-1) + scalarInfiltration => flux_data%var(iLookFLUX%scalarInfiltration)%dat(1) ,& ! intent(out): [dp] infiltration of water into the soil profile (m s-1) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(inout): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(inout): [dp(:)] fraction of liquid water in each snow layer (-) + + ! boundary fluxes in the soil domain + scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(out): [dp] rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(out): [dp] drainage of liquid water from the vegetation canopy (kg m-2 s-1) + scalarRainPlusMelt => flux_data%var(iLookFLUX%scalarRainPlusMelt)%dat(1) ,& ! intent(out): [dp] rain plus melt (m s-1) + scalarSurfaceRunoff => flux_data%var(iLookFLUX%scalarSurfaceRunoff)%dat(1) ,& ! intent(out): [dp] surface runoff (m s-1) + scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1) ,& ! intent(out): [dp] exfiltration from the soil profile (m s-1) + mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat ,& ! intent(out): [dp(:)] column outflow from each soil layer (m3 s-1) + + ! fluxes for the aquifer + scalarAquiferTranspire => flux_data%var(iLookFLUX%scalarAquiferTranspire)%dat(1) ,& ! intent(out): [dp] transpiration loss from the aquifer (m s-1 + scalarAquiferRecharge => flux_data%var(iLookFLUX%scalarAquiferRecharge)%dat(1) ,& ! intent(out): [dp] recharge to the aquifer (m s-1) + scalarAquiferBaseflow => flux_data%var(iLookFLUX%scalarAquiferBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the aquifer (m s-1) + + ! total runoff + scalarTotalRunoff => flux_data%var(iLookFLUX%scalarTotalRunoff)%dat(1) ,& ! intent(out): [dp] total runoff (m s-1) + + ! derivatives in net vegetation energy fluxes w.r.t. relevant state variables + dCanairNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy air temperature + dCanairNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy temperature + dCanairNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. ground temperature + dCanopyNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy air temperature + dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy temperature + dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. ground temperature + dCanopyNetFlux_dCanWat => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in net canopy fluxes w.r.t. canopy total water content + dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy air temperature + dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy temperature + dGroundNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. ground temperature + dGroundNetFlux_dCanWat => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in net ground fluxes w.r.t. canopy total water content + + ! derivatives in evaporative fluxes w.r.t. relevant state variables + dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy air temperature + dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy temperature + dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. ground temperature + dCanopyEvaporation_dCanWat => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy total water content + dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy air temperature + dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy temperature + dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. ground temperature + dGroundEvaporation_dCanWat => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy total water content + + ! derivatives in transpiration + dCanopyTrans_dTCanair => deriv_data%var(iLookDERIV%dCanopyTrans_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy => deriv_data%var(iLookDERIV%dCanopyTrans_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround => deriv_data%var(iLookDERIV%dCanopyTrans_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + dCanopyTrans_dCanWat => deriv_data%var(iLookDERIV%dCanopyTrans_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy total water content (s-1) + + ! derivatives in canopy water w.r.t canopy temperature + dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(out): [dp] derivative of canopy liquid storage w.r.t. temperature + + ! derivatives in canopy liquid fluxes w.r.t. canopy water + scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ,& ! intent(out): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water + scalarThroughfallRainDeriv => deriv_data%var(iLookDERIV%scalarThroughfallRainDeriv )%dat(1) ,& ! intent(out): [dp] derivative in throughfall w.r.t. canopy liquid water + scalarCanopyLiqDrainageDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDrainageDeriv)%dat(1) ,& ! intent(out): [dp] derivative in canopy drainage w.r.t. canopy liquid water + + ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below + dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above + dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below + + ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below + dNrgFlux_dWatAbove => deriv_data%var(iLookDERIV%dNrgFlux_dWatAbove )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. water state in the layer above + dNrgFlux_dWatBelow => deriv_data%var(iLookDERIV%dNrgFlux_dWatBelow )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. water state in the layer below + + ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above + iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(out): [dp(:)] derivative in vertical liquid water flux at layer interfaces + + ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential + dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above + dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + dq_dHydStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dHydStateLayerSurfVec )%dat ,& ! intent(out): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers + mLayerdTheta_dPsi => deriv_data%var(iLookDERIV%mLayerdTheta_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. psi + mLayerdPsi_dTheta => deriv_data%var(iLookDERIV%mLayerdPsi_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. theta + dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in compressibility w.r.t matric head + + ! derivative in baseflow flux w.r.t. aquifer storage + dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] erivative in baseflow flux w.r.t. aquifer storage (s-1) + + ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables + dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above + dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + dq_dNrgStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dNrgStateLayerSurfVec )%dat ,& ! intent(out): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers + + ! derivatives in soil transpiration w.r.t. canopy state variables + mLayerdTrans_dTCanair => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanair )%dat ,& !intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + mLayerdTrans_dTCanopy => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanopy )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy temperature + mLayerdTrans_dTGround => deriv_data%var(iLookDERIV%mLayerdTrans_dTGround )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. ground temperature + mLayerdTrans_dCanWat => deriv_data%var(iLookDERIV%mLayerdTrans_dCanWat )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy total water + + ! derivatives in aquifer transpiration w.r.t. canopy state variables + dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair )%dat(1) ,& !intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + dAquiferTrans_dTCanopy => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanopy )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy temperature + dAquiferTrans_dTGround => deriv_data%var(iLookDERIV%dAquiferTrans_dTGround )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. ground temperature + dAquiferTrans_dCanWat => deriv_data%var(iLookDERIV%dAquiferTrans_dCanWat )%dat(1) & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy total water + + ) ! association to data in structures + + ! ***** + ! * PRELIMINARIES... + ! ****************** + + ! increment the number of flux calls + numFluxCalls = numFluxCalls+1 + + ! modify the groundwater representation for this single-column implementation + select case(ixSpatialGroundwater) + case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale + case(localColumn); local_ixGroundwater = ixGroundwater ! go with the specified decision + case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return + end select ! (modify the groundwater representation for this single-column implementation) + + ! initialize liquid water fluxes throughout the snow and soil domains + ! NOTE: used in the energy routines, which is called before the hydrology routines + if(firstFluxCall)then + if(nSnow > 0) iLayerLiqFluxSnow(0:nSnow) = 0._rkind + end if + + ! ***** + ! * CALCULATE ENERGY FLUXES OVER VEGETATION... + ! ********************************************* + + ! identify the need to calculate the energy flux over vegetation + doVegNrgFlux = (ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing .or. ixTopNrg/=integerMissing) + + ! check if there is a need to calculate the energy fluxes over vegetation + if(doVegNrgFlux)then + + ! derivative in canopy liquid storage w.r.t. canopy temperature + dCanLiq_dTcanopy = dTheta_dTkCanopy*iden_water*canopyDepth ! kg m-2 K-1 + + ! calculate the energy fluxes over vegetation + call vegNrgFlux(& + ! input: model control + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + insideIDA, & ! intent(in): flag to indicate inside Sundials Solver (do not require longwave to be balanced) + ! input: model state variables + upperBoundTemp, & ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature + scalarCanairTempTrial, & ! intent(in): trial value of the canopy air space temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + mLayerTempTrial(1), & ! intent(in): trial value of ground temperature (K) + scalarCanopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) + ! input: model derivatives + dCanLiq_dTcanopy, & ! intent(in): derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) + ! input/output: data structures + type_data, & ! intent(in): type of vegetation and soil + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + bvar_data, & ! intent(in): model variables for the local basin + model_decisions, & ! intent(in): model decisions + ! output: liquid water fluxes associated with evaporation/transpiration + scalarCanopyTranspiration, & ! intent(out): canopy transpiration (kg m-2 s-1) + scalarCanopyEvaporation, & ! intent(out): canopy evaporation/condensation (kg m-2 s-1) + scalarGroundEvaporation, & ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + ! output: fluxes + scalarCanairNetNrgFlux, & ! intent(out): net energy flux for the canopy air space (W m-2) + scalarCanopyNetNrgFlux, & ! intent(out): net energy flux for the vegetation canopy (W m-2) + scalarGroundNetNrgFlux, & ! intent(out): net energy flux for the ground surface (W m-2) + ! output: flux derivatives + dCanairNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) + dCanairNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) + dCanairNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) + dCanopyNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) + dCanopyNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) + dCanopyNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) + dGroundNetFlux_dCanairTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) + dGroundNetFlux_dCanopyTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) + dGroundNetFlux_dGroundTemp, & ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + ! output: liquid water flux derivatives (canopy evap) + dCanopyEvaporation_dCanWat, & ! intent(out): derivative in canopy evaporation w.r.t. canopy total water content (s-1) + dCanopyEvaporation_dTCanair, & ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTCanopy, & ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTGround, & ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + ! output: liquid water flux derivatives (ground evap) + dGroundEvaporation_dCanWat, & ! intent(out): derivative in ground evaporation w.r.t. canopy total water content (s-1) + dGroundEvaporation_dTCanair, & ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTCanopy, & ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTGround, & ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + ! output: transpiration derivatives + dCanopyTrans_dCanWat, & ! intent(out): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair, & ! intent(out): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy, & ! intent(out): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround, & ! intent(out): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + ! output: cross derivative terms + dCanopyNetFlux_dCanWat, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) + dGroundNetFlux_dCanWat, & ! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + + ! check fluxes + if(globalPrintFlag)then + print*, '**' + write(*,'(a,1x,10(f30.20))') 'canopyDepth = ', canopyDepth + write(*,'(a,1x,10(f30.20))') 'mLayerDepth(1:2) = ', mLayerDepth(1:2) + write(*,'(a,1x,10(f30.20))') 'scalarCanairTempTrial = ', scalarCanairTempTrial ! trial value of the canopy air space temperature (K) + write(*,'(a,1x,10(f30.20))') 'scalarCanopyTempTrial = ', scalarCanopyTempTrial ! trial value of canopy temperature (K) + write(*,'(a,1x,10(f30.20))') 'mLayerTempTrial(1:2) = ', mLayerTempTrial(1:2) ! trial value of ground temperature (K) + write(*,'(a,1x,10(f30.20))') 'scalarCanairNetNrgFlux = ', scalarCanairNetNrgFlux + write(*,'(a,1x,10(f30.20))') 'scalarCanopyNetNrgFlux = ', scalarCanopyNetNrgFlux + write(*,'(a,1x,10(f30.20))') 'scalarGroundNetNrgFlux = ', scalarGroundNetNrgFlux + write(*,'(a,1x,10(f30.20))') 'dGroundNetFlux_dGroundTemp = ', dGroundNetFlux_dGroundTemp + endif ! if checking fluxes + + endif ! if calculating the energy fluxes over vegetation + + ! ***** + ! * CALCULATE ENERGY FLUXES THROUGH THE SNOW-SOIL DOMAIN... + ! ********************************************************** + + ! check the need to compute energy fluxes throughout the snow+soil domain + if(nSnowSoilNrg>0)then + + ! calculate energy fluxes at layer interfaces through the snow and soil domain + call ssdNrgFlux(& + ! input: model control + (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution + .true., & ! intent(in): flag indicating if derivatives are desired + ! input: fluxes and derivatives at the upper boundary + scalarGroundNetNrgFlux, & ! intent(in): total flux at the ground surface (W m-2) + dGroundNetFlux_dGroundTemp, & ! intent(in): derivative in total ground surface flux w.r.t. ground temperature (W m-2 K-1) + ! input: liquid water fluxes throughout the snow and soil domains + iLayerLiqFluxSnow, & ! intent(in): liquid flux at the interface of each snow layer (m s-1) + iLayerLiqFluxSoil, & ! intent(in): liquid flux at the interface of each soil layer (m s-1) + ! input: trial value of model state variables + mLayerTempTrial, & ! intent(in): trial temperature at the current iteration (K) + mLayerMatricHeadTrial, & ! intent(in): trial value for the total water matric potential in each soil layer (m) + mLayerVolFracLiqTrial, & ! intent(in): trial volumetric fraction of liquid water at the current iteration(-) + mLayerVolFracIceTrial, & ! intent(in): trial volumetric fraction of ice water at the current iteration(-) + ! input: pre-computed derivatives + mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + ! input-output: data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model indices + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output: fluxes and derivatives at all layer interfaces + iLayerNrgFlux, & ! intent(out): energy flux at the layer interfaces (W m-2) + dNrgFlux_dTempAbove, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (W m-2 K-1) + dNrgFlux_dTempBelow, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (W m-2 K-1) + dNrgFlux_dWatAbove, & ! intent(out): derivatives in the flux w.r.t. water state in the layer above + dNrgFlux_dWatBelow, & ! intent(out): derivatives in the flux w.r.t. water state in the layer below + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! calculate net energy fluxes for each snow and soil layer (J m-3 s-1) + do iLayer=1,nLayers + mLayerNrgFlux(iLayer) = -(iLayerNrgFlux(iLayer) - iLayerNrgFlux(iLayer-1))/mLayerDepth(iLayer) + if(globalPrintFlag)then + if(iLayer < 10) write(*,'(a,1x,i4,1x,10(f25.15,1x))') 'iLayer, iLayerNrgFlux(iLayer-1:iLayer), mLayerNrgFlux(iLayer) = ', iLayer, iLayerNrgFlux(iLayer-1:iLayer), mLayerNrgFlux(iLayer) + endif + end do + + endif ! if computing energy fluxes throughout the snow+soil domain + + ! ***** + ! * CALCULATE THE LIQUID FLUX THROUGH VEGETATION... + ! ************************************************** + + ! check the need to compute the liquid water fluxes through vegetation + if(ixVegHyd/=integerMissing)then + + ! calculate liquid water fluxes through vegetation + call vegLiqFlux(& + ! input + computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + scalarCanopyLiqTrial, & ! intent(in): trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) + scalarRainfall, & ! intent(in): rainfall rate (kg m-2 s-1) + ! input-output: data structures + mpar_data, & ! intent(in): model parameters + diag_data, & ! intent(in): local HRU diagnostic model variables + ! output + scalarThroughfallRain, & ! intent(out): rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + scalarCanopyLiqDrainage, & ! intent(out): drainage of liquid water from the vegetation canopy (kg m-2 s-1) + scalarThroughfallRainDeriv, & ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) + scalarCanopyLiqDrainageDeriv, & ! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! calculate the net liquid water flux for the vegetation canopy + scalarCanopyNetLiqFlux = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + + ! calculate the total derivative in the downward liquid flux + scalarCanopyLiqDeriv = scalarThroughfallRainDeriv + scalarCanopyLiqDrainageDeriv + + ! test + if(globalPrintFlag)then + print*, '**' + print*, 'scalarRainfall = ', scalarRainfall + print*, 'scalarThroughfallRain = ', scalarThroughfallRain + print*, 'scalarCanopyEvaporation = ', scalarCanopyEvaporation + print*, 'scalarCanopyLiqDrainage = ', scalarCanopyLiqDrainage + print*, 'scalarCanopyNetLiqFlux = ', scalarCanopyNetLiqFlux + print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial + endif + + endif ! computing the liquid water fluxes through vegetation + + ! ***** + ! * CALCULATE THE LIQUID FLUX THROUGH SNOW... + ! ******************************************** + + ! check the need to compute liquid water fluxes through snow + if(nSnowOnlyHyd>0)then + + ! compute liquid fluxes through snow + call snowLiqFlx(& + ! input: model control + nSnow, & ! intent(in): number of snow layers + firstFluxCall, & ! intent(in): the first flux call (compute variables that are constant over the iterations) + (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution + ! input: forcing for the snow domain + scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) + scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) + ! input: model state vector + mLayerVolFracLiqTrial(1:nSnow), & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) + ! input-output: data structures + indx_data, & ! intent(in): model indices + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + ! output: fluxes and derivatives + iLayerLiqFluxSnow(0:nSnow), & ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) + iLayerLiqFluxSnowDeriv(0:nSnow), & ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! define forcing for the soil domain + scalarRainPlusMelt = iLayerLiqFluxSnow(nSnow) ! drainage from the base of the snowpack + + ! calculate net liquid water fluxes for each snow layer (s-1) + do iLayer=1,nSnow + mLayerLiqFluxSnow(iLayer) = -(iLayerLiqFluxSnow(iLayer) - iLayerLiqFluxSnow(iLayer-1))/mLayerDepth(iLayer) + end do + + ! compute drainage from the soil zone (needed for mass balance checks) + scalarSnowDrainage = iLayerLiqFluxSnow(nSnow) + + ! save bottom layer of snow derivatives + above_soilLiqFluxDeriv = iLayerLiqFluxSnowDeriv(nSnow) ! derivative in vertical liquid water flux at bottom snow layer interface + above_soildLiq_dTk = mLayerdTheta_dTk(nSnow) ! derivative in volumetric liquid water content in bottom snow layer w.r.t. temperature + above_soilFracLiq = mLayerFracLiqSnow(nSnow) ! fraction of liquid water in bottom snow layer (-) + + else + + ! define forcing for the soil domain for the case of no snow layers + ! NOTE: in case where nSnowOnlyHyd==0 AND snow layers exist, then scalarRainPlusMelt is taken from the previous flux evaluation + if(nSnow==0)then !no snow layers + scalarRainPlusMelt = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water & ! liquid flux from the canopy (m s-1) + + drainageMeltPond/iden_water ! melt of the snow without a layer (m s-1) + + if(ixVegHyd/=integerMissing)then + ! save canopy derivatives + above_soilLiqFluxDeriv = scalarCanopyLiqDeriv/iden_water ! derivative in (throughfall + drainage) w.r.t. canopy liquid water + above_soildLiq_dTk = dCanLiq_dTcanopy ! derivative of canopy liquid storage w.r.t. temperature + above_soilFracLiq = scalarFracLiqVeg ! fraction of liquid water in canopy (-) + else + above_soilLiqFluxDeriv = 0._rkind + above_soildLiq_dTk = 0._rkind + above_soilFracLiq = 0._rkind + endif + else ! snow layers, take from previous flux calculation + above_soilLiqFluxDeriv = iLayerLiqFluxSnowDeriv(nSnow) ! derivative in vertical liquid water flux at bottom snow layer interface + above_soildLiq_dTk = mLayerdTheta_dTk(nSnow) ! derivative in volumetric liquid water content in bottom snow layer w.r.t. temperature + above_soilFracLiq = mLayerFracLiqSnow(nSnow) ! fraction of liquid water in bottom snow layer (-) + endif ! snow layers or not + + endif ! if calculating the liquid flux through snow + + ! ***** + ! * CALCULATE THE LIQUID FLUX THROUGH SOIL... + ! ******************************************** + + ! check the need to calculate the liquid flux through soil + if(nSoilOnlyHyd>0)then + + ! calculate the liquid flux through soil + call soilLiqFlx(& + ! input: model control + nSoil, & ! intent(in): number of soil layers + firstSplitOper, & ! intent(in): flag indicating first flux call in a splitting operation + (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution + .true., & ! intent(in): flag indicating if derivatives are desired + ! input: trial state variables + mLayerTempTrial(nSnow+1:nLayers), & ! intent(in): trial temperature at the current iteration (K) + mLayerMatricHeadLiqTrial(1:nSoil), & ! intent(in): liquid water matric potential (m) + mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of ice (-) + ! input: pre-computed deriavatives + mLayerdTheta_dTk(nSnow+1:nLayers), & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + dPsiLiq_dTemp(1:nSoil), & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) + dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) + ! input: fluxes + scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) + scalarGroundEvaporation, & ! intent(in): ground evaporation (kg m-2 s-1) + scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) + ! input-output: data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model indices + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output: diagnostic variables for surface runoff + scalarMaxInfilRate, & ! intent(inout): maximum infiltration rate (m s-1) + scalarInfilArea, & ! intent(inout): fraction of unfrozen area where water can infiltrate (-) + scalarFrozenArea, & ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) + scalarSurfaceRunoff, & ! intent(inout): surface runoff (m s-1) + ! output: diagnostic variables for model layers + mLayerdTheta_dPsi, & ! intent(inout): derivative in the soil water characteristic w.r.t. psi (m-1) + mLayerdPsi_dTheta, & ! intent(inout): derivative in the soil water characteristic w.r.t. theta (m) + dHydCond_dMatric, & ! intent(inout): derivative in hydraulic conductivity w.r.t matric head (s-1) + ! output: fluxes + scalarInfiltration, & ! intent(inout): surface infiltration rate (m s-1) -- controls on infiltration only computed for iter==1 + iLayerLiqFluxSoil, & ! intent(inout): liquid fluxes at layer interfaces (m s-1) + mLayerTranspire, & ! intent(inout): transpiration loss from each soil layer (m s-1) + mLayerHydCond, & ! intent(inout): hydraulic conductivity in each layer (m s-1) + ! output: derivatives in fluxes w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below (m s-1 or s-1) + dq_dHydStateAbove, & ! intent(inout): derivatives in the flux w.r.t. matric head in the layer above (s-1) + dq_dHydStateBelow, & ! intent(inout): derivatives in the flux w.r.t. matric head in the layer below (s-1) + dq_dHydStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) + ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) + dq_dNrgStateAbove, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) + dq_dNrgStateBelow, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) + dq_dNrgStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) + ! output: derivatives in transpiration w.r.t. canopy state variables + mLayerdTrans_dTCanair, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + mLayerdTrans_dTCanopy, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy temperature + mLayerdTrans_dTGround, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. ground temperature + mLayerdTrans_dCanWat, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy total water + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! calculate net liquid water fluxes for each soil layer (s-1) + do iLayer=1,nSoil + mLayerLiqFluxSoil(iLayer) = -(iLayerLiqFluxSoil(iLayer) - iLayerLiqFluxSoil(iLayer-1))/mLayerDepth(iLayer+nSnow) + end do + + ! calculate the soil control on infiltration + if(nSnow==0) then + ! * case of infiltration into soil + if(scalarMaxInfilRate > scalarRainPlusMelt)then ! infiltration is not rate-limited + scalarSoilControl = (1._rkind - scalarFrozenArea)*scalarInfilArea + else + scalarSoilControl = 0._rkind ! (scalarRainPlusMelt exceeds maximum infiltration rate + endif + else + ! * case of infiltration into snow + scalarSoilControl = 1._rkind + endif + + ! compute drainage from the soil zone (needed for mass balance checks and in aquifer recharge) + scalarSoilDrainage = iLayerLiqFluxSoil(nSoil) + + ! expand derivatives to the total water matric potential + ! NOTE: arrays are offset because computing derivatives in interface fluxes, at the top and bottom of the layer respectively + if(globalPrintFlag) print*, 'dPsiLiq_dPsi0(1:nSoil) = ', dPsiLiq_dPsi0(1:nSoil) + dq_dHydStateAbove(1:nSoil) = dq_dHydStateAbove(1:nSoil) *dPsiLiq_dPsi0(1:nSoil) + dq_dHydStateBelow(0:nSoil-1) = dq_dHydStateBelow(0:nSoil-1)*dPsiLiq_dPsi0(1:nSoil) + dq_dHydStateLayerSurfVec(1:nSoil) = dq_dHydStateLayerSurfVec(1:nSoil)*dPsiLiq_dPsi0(1:nSoil) + + endif ! if calculating the liquid flux through soil + + ! ***** + ! * CALCULATE THE GROUNDWATER FLOW... + ! ************************************ + + ! check if computing soil hydrology + if(nSoilOnlyHyd>0)then + + ! set baseflow fluxes to zero if the topmodel baseflow routine is not used + if(local_ixGroundwater/=qbaseTopmodel)then + ! (diagnostic variables in the data structures) + scalarExfiltration = 0._rkind ! exfiltration from the soil profile (m s-1) + mLayerColumnOutflow(:) = 0._rkind ! column outflow from each soil layer (m3 s-1) + ! (variables needed for the numerical solution) + mLayerBaseflow(:) = 0._rkind ! baseflow from each soil layer (m s-1) + + ! topmodel-ish shallow groundwater + else ! local_ixGroundwater==qbaseTopmodel + + ! check the derivative matrix is sized appropriately + if(size(dBaseflow_dMatric,1)/=nSoil .or. size(dBaseflow_dMatric,2)/=nSoil)then + message=trim(message)//'expect dBaseflow_dMatric to be nSoil x nSoil' + err=20; return + endif + + ! compute the baseflow flux + call groundwatr(& + ! input: model control + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + firstFluxCall, & ! intent(in): logical flag to compute index of the lowest saturated layer + ! input: state and diagnostic variables + mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) + mLayerMatricHeadLiqTrial, & ! intent(in): liquid water matric potential (m) + mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of ice (-) + ! input: data structures + attr_data, & ! intent(in): model attributes + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output + ixSaturation, & ! intent(inout) index of lowest saturated layer (NOTE: only computed on the first iteration) + mLayerBaseflow, & ! intent(out): baseflow from each soil layer (m s-1) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + endif ! computing baseflow flux + + ! compute total baseflow from the soil zone (needed for mass balance checks) + scalarSoilBaseflow = sum(mLayerBaseflow) + + ! compute total runoff + ! (Note: scalarSoilBaseflow is zero if topmodel is not used) + ! (Note: scalarSoilBaseflow may need to re-envisioned in topmodel formulation if parts of it flow into neighboring soil rather than exfiltrate) + scalarTotalRunoff = scalarSurfaceRunoff + scalarSoilDrainage + scalarSoilBaseflow + + endif ! if computing soil hydrology + + + ! ***** + ! (7) CALCULATE FLUXES FOR THE DEEP AQUIFER... + ! ******************************************** + + ! check if computing aquifer fluxes + if(ixAqWat/=integerMissing)then + + ! identify modeling decision + if(local_ixGroundwater==bigBucket)then + + ! compute fluxes for the big bucket + call bigAquifer(& + ! input: state variables and fluxes + scalarAquiferStorageTrial, & ! intent(in): trial value of aquifer storage (m) + scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) + scalarSoilDrainage, & ! intent(in): soil drainage (m s-1) + ! input: pre-computed derivatives + dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + ! input: diagnostic variables and parameters + mpar_data, & ! intent(in): model parameter structure + diag_data, & ! intent(in): diagnostic variable structure + ! output: fluxes + scalarAquiferTranspire, & ! intent(out): transpiration loss from the aquifer (m s-1) + scalarAquiferRecharge, & ! intent(out): recharge to the aquifer (m s-1) + scalarAquiferBaseflow, & ! intent(out): total baseflow from the aquifer (m s-1) + dBaseflow_dAquifer, & ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) + ! output: derivatives in transpiration w.r.t. canopy state variables + dAquiferTrans_dTCanair, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + dAquiferTrans_dTCanopy, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy temperature + dAquiferTrans_dTGround, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. ground temperature + dAquiferTrans_dCanWat, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy total water + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! compute total runoff (overwrite previously calculated value before considering aquifer). + ! (Note: SoilDrainage goes into aquifer, not runoff) + scalarTotalRunoff = scalarSurfaceRunoff + scalarAquiferBaseflow + + ! if no aquifer, then fluxes are zero + else + scalarAquiferTranspire = 0._rkind ! transpiration loss from the aquifer (m s-1) + scalarAquiferRecharge = 0._rkind ! recharge to the aquifer (m s-1) + scalarAquiferBaseflow = 0._rkind ! total baseflow from the aquifer (m s-1) + dBaseflow_dAquifer = 0._rkind ! change in baseflow flux w.r.t. aquifer storage (s-1) + end if ! no aquifer + + endif ! if computing aquifer fluxes + + ! ***** + ! (X) WRAP UP... + ! ************* + + ! define model flux vector for the vegetation sub-domain + if(ixCasNrg/=integerMissing) fluxVec(ixCasNrg) = scalarCanairNetNrgFlux/canopyDepth + if(ixVegNrg/=integerMissing) fluxVec(ixVegNrg) = scalarCanopyNetNrgFlux/canopyDepth + if(ixVegHyd/=integerMissing) fluxVec(ixVegHyd) = scalarCanopyNetLiqFlux ! NOTE: solid fluxes are handled separately + + ! populate the flux vector for energy + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + fluxVec( ixSnowSoilNrg(iLayer) ) = mLayerNrgFlux(iLayer) + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! populate the flux vector for hydrology + ! NOTE: ixVolFracWat and ixVolFracLiq can also include states in the soil domain, hence enable primary variable switching + if(nSnowSoilHyd>0)then ! check if any hydrology states exist + do iLayer=1,nLayers + if(ixSnowSoilHyd(iLayer)/=integerMissing)then ! check if a given hydrology state exists + select case( layerType(iLayer) ) + case(iname_snow); fluxVec( ixSnowSoilHyd(iLayer) ) = mLayerLiqFluxSnow(iLayer) + case(iname_soil); fluxVec( ixSnowSoilHyd(iLayer) ) = mLayerLiqFluxSoil(iLayer-nSnow) + case default; err=20; message=trim(message)//'expect layerType to be either iname_snow or iname_soil'; return + end select + endif ! if a given hydrology state exists + end do ! looping through non-missing energy state variables in the snow+soil domain + endif ! if any hydrology states exist + + ! compute the flux vector for the aquifer + if(ixAqWat/=integerMissing) fluxVec(ixAqWat) = scalarAquiferTranspire + scalarAquiferRecharge - scalarAquiferBaseflow + + ! set the first flux call to false + firstFluxCall=.false. + + ! end association to variables in the data structures + end associate + +end subroutine computFlux + + +! ********************************************************************************************************** +! public subroutine soilCmpres: compute soil compressibility (-) and its derivative w.r.t matric head (m-1) +! ********************************************************************************************************** +subroutine soilCmpres(& + ! input: + ixRichards, & ! intent(in): choice of option for Richards' equation + ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers + mLayerMatricHead, & ! intent(in): matric head at the start of the time step (m) + mLayerMatricHeadTrial, & ! intent(in): trial value of matric head (m) + mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) + mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice content in each soil layer (-) + specificStorage, & ! intent(in): specific storage coefficient (m-1) + theta_sat, & ! intent(in): soil porosity (-) + ! output: + compress, & ! intent(out): compressibility of the soil matrix (-) + dCompress_dPsi, & ! intent(out): derivative in compressibility w.r.t. matric head (m-1) + err,message) ! intent(out): error code and error message + implicit none + ! input: + integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation + integer(i4b),intent(in) :: ixBeg,ixEnd ! start and end indices defining desired layers + real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head at the start of the time step (m) + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial value for matric head (m) + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) + real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) + real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) + ! output: + real(rkind),intent(inout) :: compress(:) ! soil compressibility (-) + real(rkind),intent(inout) :: dCompress_dPsi(:) ! derivative in soil compressibility w.r.t. matric head (m-1) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local variables + integer(i4b) :: iLayer ! index of soil layer + ! -------------------------------------------------------------- + ! initialize error control + err=0; message='soilCmpres/' + ! (only compute for the mixed form of Richards' equation) + if(ixRichards==mixdform)then + do iLayer=1,size(mLayerMatricHead) + if(iLayer>=ixBeg .and. iLayer<=ixEnd)then + ! compute the derivative for the compressibility term (m-1) + dCompress_dPsi(iLayer) = specificStorage*(mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer))/theta_sat(iLayer) + ! compute the compressibility term (-) + compress(iLayer) = (mLayerMatricHeadTrial(iLayer) - mLayerMatricHead(iLayer))*dCompress_dPsi(iLayer) + endif + end do else - scalarAquiferTranspire = 0._rkind ! transpiration loss from the aquifer (m s-1) - scalarAquiferRecharge = 0._rkind ! recharge to the aquifer (m s-1) - scalarAquiferBaseflow = 0._rkind ! total baseflow from the aquifer (m s-1) - dBaseflow_dAquifer = 0._rkind ! change in baseflow flux w.r.t. aquifer storage (s-1) - end if ! no aquifer - - endif ! if computing aquifer fluxes - - ! ***** - ! (X) WRAP UP... - ! ************* - - ! define model flux vector for the vegetation sub-domain - if(ixCasNrg/=integerMissing) fluxVec(ixCasNrg) = scalarCanairNetNrgFlux/canopyDepth - if(ixVegNrg/=integerMissing) fluxVec(ixVegNrg) = scalarCanopyNetNrgFlux/canopyDepth - if(ixVegHyd/=integerMissing) fluxVec(ixVegHyd) = scalarCanopyNetLiqFlux ! NOTE: solid fluxes are handled separately - - ! populate the flux vector for energy - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - fluxVec( ixSnowSoilNrg(iLayer) ) = mLayerNrgFlux(iLayer) - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! populate the flux vector for hydrology - ! NOTE: ixVolFracWat and ixVolFracLiq can also include states in the soil domain, hence enable primary variable switching - if(nSnowSoilHyd>0)then ! check if any hydrology states exist - do iLayer=1,nLayers - if(ixSnowSoilHyd(iLayer)/=integerMissing)then ! check if a given hydrology state exists - select case( layerType(iLayer) ) - case(iname_snow); fluxVec( ixSnowSoilHyd(iLayer) ) = mLayerLiqFluxSnow(iLayer) - case(iname_soil); fluxVec( ixSnowSoilHyd(iLayer) ) = mLayerLiqFluxSoil(iLayer-nSnow) - case default; err=20; message=trim(message)//'expect layerType to be either iname_snow or iname_soil'; return - end select - endif ! if a given hydrology state exists - end do ! looping through non-missing energy state variables in the snow+soil domain - endif ! if any hydrology states exist - - ! compute the flux vector for the aquifer - if(ixAqWat/=integerMissing) fluxVec(ixAqWat) = scalarAquiferTranspire + scalarAquiferRecharge - scalarAquiferBaseflow - - ! set the first flux call to false - firstFluxCall=.false. - - ! end association to variables in the data structures - end associate - - end subroutine computFlux - - - ! ********************************************************************************************************** - ! public subroutine soilCmpres: compute soil compressibility (-) and its derivative w.r.t matric head (m-1) - ! ********************************************************************************************************** - subroutine soilCmpres(& - ! input: - ixRichards, & ! intent(in): choice of option for Richards' equation - ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers - mLayerMatricHead, & ! intent(in): matric head at the start of the time step (m) - mLayerMatricHeadTrial, & ! intent(in): trial value of matric head (m) - mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) - mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice content in each soil layer (-) - specificStorage, & ! intent(in): specific storage coefficient (m-1) - theta_sat, & ! intent(in): soil porosity (-) - ! output: - compress, & ! intent(out): compressibility of the soil matrix (-) - dCompress_dPsi, & ! intent(out): derivative in compressibility w.r.t. matric head (m-1) - err,message) ! intent(out): error code and error message - implicit none - ! input: - integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation - integer(i4b),intent(in) :: ixBeg,ixEnd ! start and end indices defining desired layers - real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head at the start of the time step (m) - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial value for matric head (m) - real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) - real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) - real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) - real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) - ! output: - real(rkind),intent(inout) :: compress(:) ! soil compressibility (-) - real(rkind),intent(inout) :: dCompress_dPsi(:) ! derivative in soil compressibility w.r.t. matric head (m-1) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! local variables - integer(i4b) :: iLayer ! index of soil layer - ! -------------------------------------------------------------- - ! initialize error control - err=0; message='soilCmpres/' - ! (only compute for the mixed form of Richards' equation) - if(ixRichards==mixdform)then - do iLayer=1,size(mLayerMatricHead) - if(iLayer>=ixBeg .and. iLayer<=ixEnd)then - ! compute the derivative for the compressibility term (m-1) - dCompress_dPsi(iLayer) = specificStorage*(mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer))/theta_sat(iLayer) - ! compute the compressibility term (-) - compress(iLayer) = (mLayerMatricHeadTrial(iLayer) - mLayerMatricHead(iLayer))*dCompress_dPsi(iLayer) - endif - end do - else - compress(:) = 0._rkind - dCompress_dPsi(:) = 0._rkind - end if - end subroutine soilCmpres - - - ! ********************************************************************************************************** - ! public subroutine soilCmpres: compute soil compressibility (-) and its derivative w.r.t matric head (m-1) - ! ********************************************************************************************************** - subroutine soilCmpresSundials(& - ! input: - ixRichards, & ! intent(in): choice of option for Richards' equation - ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers - mLayerMatricHeadPrime, & ! intent(in): matric head at the start of the time step (m) - mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) - mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice content in each soil layer (-) - specificStorage, & ! intent(in): specific storage coefficient (m-1) - theta_sat, & ! intent(in): soil porosity (-) - ! output: - compress, & ! intent(out): compressibility of the soil matrix (-) - dCompress_dPsi, & ! intent(out): derivative in compressibility w.r.t. matric head (m-1) - err,message) ! intent(out): error code and error message -implicit none -! input: -integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation -integer(i4b),intent(in) :: ixBeg,ixEnd ! start and end indices defining desired layers -real(rkind),intent(in) :: mLayerMatricHeadPrime(:) ! matric head at the start of the time step (m) -real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) -real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) -real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) -real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) -! output: -real(rkind),intent(inout) :: compress(:) ! soil compressibility (-) -real(rkind),intent(inout) :: dCompress_dPsi(:) ! derivative in soil compressibility w.r.t. matric head (m-1) -integer(i4b),intent(out) :: err ! error code -character(*),intent(out) :: message ! error message -! local variables -integer(i4b) :: iLayer ! index of soil layer -! -------------------------------------------------------------- -! initialize error control -err=0; message='soilCmpresSundials/' -! (only compute for the mixed form of Richards' equation) -if(ixRichards==mixdform)then + compress(:) = 0._rkind + dCompress_dPsi(:) = 0._rkind + end if +end subroutine soilCmpres + + +! ********************************************************************************************************** +! public subroutine soilCmpres: compute soil compressibility (-) and its derivative w.r.t matric head (m-1) +! ********************************************************************************************************** +subroutine soilCmpresSundials(& + ! input: + ixRichards, & ! intent(in): choice of option for Richards' equation + ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers + mLayerMatricHeadPrime, & ! intent(in): matric head at the start of the time step (m) + mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) + mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice content in each soil layer (-) + specificStorage, & ! intent(in): specific storage coefficient (m-1) + theta_sat, & ! intent(in): soil porosity (-) + ! output: + compress, & ! intent(out): compressibility of the soil matrix (-) + dCompress_dPsi, & ! intent(out): derivative in compressibility w.r.t. matric head (m-1) + err,message) ! intent(out): error code and error message + implicit none + ! input: + integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation + integer(i4b),intent(in) :: ixBeg,ixEnd ! start and end indices defining desired layers + real(rkind),intent(in) :: mLayerMatricHeadPrime(:) ! matric head at the start of the time step (m) + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) + real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) + real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) + ! output: + real(rkind),intent(inout) :: compress(:) ! soil compressibility (-) + real(rkind),intent(inout) :: dCompress_dPsi(:) ! derivative in soil compressibility w.r.t. matric head (m-1) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local variables + integer(i4b) :: iLayer ! index of soil layer + ! -------------------------------------------------------------- + ! initialize error control + err=0; message='soilCmpresSundials/' + ! (only compute for the mixed form of Richards' equation) + if(ixRichards==mixdform)then do iLayer=1,size(mLayerMatricHeadPrime) - if(iLayer>=ixBeg .and. iLayer<=ixEnd)then - ! compute the derivative for the compressibility term (m-1) - dCompress_dPsi(iLayer) = specificStorage*(mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer))/theta_sat(iLayer) - ! compute the compressibility term (-) - compress(iLayer) = mLayerMatricHeadPrime(iLayer) * dCompress_dPsi(iLayer) - endif + if(iLayer>=ixBeg .and. iLayer<=ixEnd)then + ! compute the derivative for the compressibility term (m-1) + dCompress_dPsi(iLayer) = specificStorage*(mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer))/theta_sat(iLayer) + ! compute the compressibility term (-) + compress(iLayer) = mLayerMatricHeadPrime(iLayer) * dCompress_dPsi(iLayer) + endif end do -else + else compress(:) = 0._rkind dCompress_dPsi(:) = 0._rkind -end if + end if end subroutine soilCmpresSundials end module computFlux_module From a567ff003c93cca597416122b3cbcec9a5f05f95 Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 27 Sep 2022 20:05:37 +0000 Subject: [PATCH 0370/1472] added indentation --- build/source/engine/vegNrgFlux.f90 | 6123 ++++++++++++++-------------- 1 file changed, 2995 insertions(+), 3128 deletions(-) diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 6c4ba0600..2814162ab 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -123,3241 +123,3108 @@ module vegNrgFlux_module logical(lgt) :: printflag ! flag to turn on printing contains - ! ******************************************************************************************************* - ! public subroutine vegNrgFlux: muster program to compute energy fluxes at vegetation and ground surfaces - ! ******************************************************************************************************* - subroutine vegNrgFlux(& - ! input: model control - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - insideIDA, & ! intent(in): flag to indicate inside Sundials Solver (do not require longwave to be balanced) - - ! input: model state variables - upperBoundTemp, & ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature - canairTempTrial, & ! intent(in): trial value of the canopy air space temperature (K) - canopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - groundTempTrial, & ! intent(in): trial value of ground temperature (K) - canopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) - canopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) - - ! input: model derivatives - dCanLiq_dTcanopy, & ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) - - ! input/output: data structures - type_data, & ! intent(in): type of vegetation and soil - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): state vector geometry - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - bvar_data, & ! intent(in): model variables for the local basin - model_decisions, & ! intent(in): model decisions - - ! output: liquid water fluxes associated with evaporation/transpiration (needed for coupling) - returnCanopyTranspiration, & ! intent(out): canopy transpiration (kg m-2 s-1) - returnCanopyEvaporation, & ! intent(out): canopy evaporation/condensation (kg m-2 s-1) - returnGroundEvaporation, & ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - - ! output: fluxes - canairNetFlux, & ! intent(out): net energy flux for the canopy air space (W m-2) - canopyNetFlux, & ! intent(out): net energy flux for the vegetation canopy (W m-2) - groundNetFlux, & ! intent(out): net energy flux for the ground surface (W m-2) - - ! output: energy flux derivatives - dCanairNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) - dCanairNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) - dCanairNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) - dCanopyNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) - dCanopyNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) - dCanopyNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - dGroundNetFlux_dCanairTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) - dGroundNetFlux_dCanopyTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) - dGroundNetFlux_dGroundTemp, & ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - - ! output liquid water flux derivarives (canopy evap) - dCanopyEvaporation_dCanWat, & ! intent(out): derivative in canopy evaporation w.r.t. canopy total water content (s-1) - dCanopyEvaporation_dTCanair, & ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTCanopy, & ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTGround, & ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - - ! output: liquid water flux derivarives (ground evap) - dGroundEvaporation_dCanWat, & ! intent(out): derivative in ground evaporation w.r.t. canopy total water content (s-1) - dGroundEvaporation_dTCanair, & ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTCanopy, & ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTGround, & ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - - ! output: transpiration derivatives - dCanopyTrans_dCanWat, & ! intent(out): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - dCanopyTrans_dTCanair, & ! intent(out): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTCanopy, & ! intent(out): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTGround, & ! intent(out): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - - ! output: cross derivative terms - dCanopyNetFlux_dCanWat, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) - dGroundNetFlux_dCanWat, & ! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) - - ! output: error control - err,message) ! intent(out): error control - - ! utilities - USE expIntegral_module,only:expInt ! function to calculate the exponential integral - ! conversion functions - USE conv_funcs_module,only:satVapPress ! function to compute the saturated vapor pressure (Pa) - USE conv_funcs_module,only:getLatentHeatValue ! function to identify latent heat of vaporization/sublimation (J kg-1) - ! stomatal resistance - USE stomResist_module,only:stomResist ! subroutine to calculate stomatal resistance - ! phase changes - USE snow_utils_module,only:fracliquid ! compute fraction of liquid water at a given temperature - - ! compute energy and mass fluxes for vegetation - implicit none - - ! --------------------------------------------------------------------------------------- - ! * dummy variables - ! --------------------------------------------------------------------------------------- - ! input: model control - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(in) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: insideIDA ! flag if inside Sundials solver - ! input: model state variables - real(rkind),intent(in) :: upperBoundTemp ! temperature of the upper boundary (K) --> NOTE: use air temperature - real(rkind),intent(in) :: canairTempTrial ! trial value of canopy air space temperature (K) - real(rkind),intent(in) :: canopyTempTrial ! trial value of canopy temperature (K) - real(rkind),intent(in) :: groundTempTrial ! trial value of ground temperature (K) - real(rkind),intent(in) :: canopyIceTrial ! trial value of mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: canopyLiqTrial ! trial value of mass of liquid water on the vegetation canopy (kg m-2) - - ! input: model derivatives - real(rkind),intent(in) :: dCanLiq_dTcanopy ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) - - ! input/output: data structures - type(var_i),intent(in) :: type_data ! type of vegetation and soil - type(var_d),intent(in) :: forc_data ! model forcing data - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! state vector geometry - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin - type(model_options),intent(in) :: model_decisions(:) ! model decisions - - ! output: liquid water fluxes associated with evaporation/transpiration (needed for coupling) - real(rkind),intent(out) :: returnCanopyTranspiration ! canopy transpiration (kg m-2 s-1) - real(rkind),intent(out) :: returnCanopyEvaporation ! canopy evaporation/condensation (kg m-2 s-1) - real(rkind),intent(out) :: returnGroundEvaporation ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - - ! output: fluxes - real(rkind),intent(out) :: canairNetFlux ! net energy flux for the canopy air space (W m-2) - real(rkind),intent(out) :: canopyNetFlux ! net energy flux for the vegetation canopy (W m-2) - real(rkind),intent(out) :: groundNetFlux ! net energy flux for the ground surface (W m-2) - - ! output: energy flux derivatives - real(rkind),intent(out) :: dCanairNetFlux_dCanairTemp ! derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) - real(rkind),intent(out) :: dCanairNetFlux_dCanopyTemp ! derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dCanairNetFlux_dGroundTemp ! derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) - real(rkind),intent(out) :: dCanopyNetFlux_dCanairTemp ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) - real(rkind),intent(out) :: dCanopyNetFlux_dCanopyTemp ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dCanopyNetFlux_dGroundTemp ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - real(rkind),intent(out) :: dGroundNetFlux_dCanairTemp ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) - real(rkind),intent(out) :: dGroundNetFlux_dCanopyTemp ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - - ! output: liquid flux derivatives (canopy evap) - real(rkind),intent(out) :: dCanopyEvaporation_dCanWat ! derivative in canopy evaporation w.r.t. canopy total water content (s-1) - real(rkind),intent(out) :: dCanopyEvaporation_dTCanair ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - real(rkind),intent(out) :: dCanopyEvaporation_dTCanopy ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - real(rkind),intent(out) :: dCanopyEvaporation_dTGround ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - - ! output: liquid flux derivatives (ground evap) - real(rkind),intent(out) :: dGroundEvaporation_dCanWat ! derivative in ground evaporation w.r.t. canopy total water content (s-1) - real(rkind),intent(out) :: dGroundEvaporation_dTCanair ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - real(rkind),intent(out) :: dGroundEvaporation_dTCanopy ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - real(rkind),intent(out) :: dGroundEvaporation_dTGround ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - -! output: transpiration derivatives - real(rkind),intent(out) :: dCanopyTrans_dCanWat ! intent(out): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - real(rkind),intent(out) :: dCanopyTrans_dTCanair ! intent(out): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - real(rkind),intent(out) :: dCanopyTrans_dTCanopy ! intent(out): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - real(rkind),intent(out) :: dCanopyTrans_dTGround ! intent(out): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - - ! output: cross derivative terms - real(rkind),intent(out) :: dCanopyNetFlux_dCanWat ! derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) - real(rkind),intent(out) :: dGroundNetFlux_dCanWat ! derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) - - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - - ! --------------------------------------------------------------------------------------- - ! * local variables - ! --------------------------------------------------------------------------------------- - ! local (general) - character(LEN=256) :: cmessage ! error message of downwind routine - real(rkind) :: VAI ! vegetation area index (m2 m-2) - real(rkind) :: exposedVAI ! exposed vegetation area index (m2 m-2) - real(rkind) :: totalCanopyWater ! total water on the vegetation canopy (kg m-2) - real(rkind) :: scalarAquiferStorage ! aquifer storage (m) - - ! local (compute numerical derivatives) - integer(i4b),parameter :: unperturbed=1 ! named variable to identify the case of unperturbed state variables - integer(i4b),parameter :: perturbStateGround=2 ! named variable to identify the case where we perturb the ground temperature - integer(i4b),parameter :: perturbStateCanopy=3 ! named variable to identify the case where we perturb the canopy temperature - integer(i4b),parameter :: perturbStateCanair=4 ! named variable to identify the case where we perturb the canopy air temperature - integer(i4b),parameter :: perturbStateCanWat=5 ! named variable to identify the case where we perturb the canopy total water content - integer(i4b) :: itry ! index of flux evaluation - integer(i4b) :: nFlux ! number of flux evaluations - real(rkind) :: groundTemp ! value of ground temperature used in flux calculations (may be perturbed) - real(rkind) :: canopyTemp ! value of canopy temperature used in flux calculations (may be perturbed) - real(rkind) :: canairTemp ! value of canopy air temperature used in flux calculations (may be perturbed) - real(rkind) :: canopyWat ! value of canopy total water used in flux calculations (may be perturbed) - real(rkind) :: canopyLiq ! value of canopy liquid water used in flux calculations (may be perturbed) - real(rkind) :: canopyIce ! value of canopy ice used in flux calculations (may be perturbed) - real(rkind) :: try0,try1 ! trial values to evaluate specific derivatives (testing only) - - ! local (saturation vapor pressure of veg) - real(rkind) :: TV_celcius ! vegetaion temperature (C) - real(rkind) :: TG_celcius ! ground temperature (C) - real(rkind) :: dSVPCanopy_dCanopyTemp ! derivative in canopy saturated vapor pressure w.r.t. vegetation temperature (Pa/K) - real(rkind) :: dSVPGround_dGroundTemp ! derivative in ground saturated vapor pressure w.r.t. ground temperature (Pa/K) - - ! local (wetted canopy area) - real(rkind) :: fracLiquidCanopy ! fraction of liquid water in the canopy (-) - real(rkind) :: canopyWetFraction ! trial value of the canopy wetted fraction (-) - real(rkind) :: dCanopyWetFraction_dWat ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) - real(rkind) :: dCanopyWetFraction_dT ! derivative in wetted fraction w.r.t. canopy temperature (K-1) - - ! local (longwave radiation) - real(rkind) :: expi ! exponential integral - real(rkind) :: scaleLAI ! scaled LAI (computing diffuse transmissivity) - real(rkind) :: diffuseTrans ! diffuse transmissivity (-) - real(rkind) :: groundEmissivity ! emissivity of the ground surface (-) - real(rkind),parameter :: vegEmissivity=0.98_rkind ! emissivity of vegetation (0.9665 in JULES) (-) - real(rkind),parameter :: soilEmissivity=0.98_rkind ! emmisivity of the soil (0.9665 in JULES) (-) - real(rkind),parameter :: snowEmissivity=0.99_rkind ! emissivity of snow (-) - real(rkind) :: dLWNetCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dLWNetGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dLWNetCanopy_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dLWNetGround_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) - - ! local (aerodynamic resistance) - real(rkind) :: scalarCanopyStabilityCorrection_old ! stability correction for the canopy (-) - real(rkind) :: scalarGroundStabilityCorrection_old ! stability correction for the ground surface (-) - - ! local (turbulent heat transfer) - real(rkind) :: z0Ground ! roughness length of the ground (ground below the canopy or non-vegetated surface) (m) - real(rkind) :: soilEvapFactor ! soil water control on evaporation from non-vegetated surfaces - real(rkind) :: soilRelHumidity_noSnow ! relative humidity in the soil pores [0-1] - real(rkind) :: scalarLeafConductance ! leaf conductance (m s-1) - real(rkind) :: scalarCanopyConductance ! canopy conductance (m s-1) - real(rkind) :: scalarGroundConductanceSH ! ground conductance for sensible heat (m s-1) - real(rkind) :: scalarGroundConductanceLH ! ground conductance for latent heat -- includes soil resistance (m s-1) - real(rkind) :: scalarEvapConductance ! conductance for evaporation (m s-1) - real(rkind) :: scalarTransConductance ! conductance for transpiration (m s-1) - real(rkind) :: scalarTotalConductanceSH ! total conductance for sensible heat (m s-1) - real(rkind) :: scalarTotalConductanceLH ! total conductance for latent heat (m s-1) - real(rkind) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - real(rkind) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - real(rkind) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - real(rkind) :: turbFluxCanair ! total turbulent heat fluxes exchanged at the canopy air space (W m-2) - real(rkind) :: turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) - real(rkind) :: turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) - - ! local (turbulent heat transfer -- compute numerical derivatives) - ! (temporary scalar resistances when states are perturbed) - real(rkind) :: trialLeafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) - real(rkind) :: trialGroundResistance ! below canopy aerodynamic resistance (s m-1) - real(rkind) :: trialCanopyResistance ! above canopy aerodynamic resistance (s m-1) - real(rkind) :: notUsed_RiBulkCanopy ! bulk Richardson number for the canopy (-) - real(rkind) :: notUsed_RiBulkGround ! bulk Richardson number for the ground surface (-) - real(rkind) :: notUsed_z0Canopy ! roughness length of the vegetation canopy (m) - real(rkind) :: notUsed_WindReductionFactor ! canopy wind reduction factor (-) - real(rkind) :: notUsed_ZeroPlaneDisplacement ! zero plane displacement (m) - real(rkind) :: notUsed_scalarCanopyStabilityCorrection ! stability correction for the canopy (-) - real(rkind) :: notUsed_scalarGroundStabilityCorrection ! stability correction for the ground surface (-) - real(rkind) :: notUsed_EddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) - real(rkind) :: notUsed_FrictionVelocity ! friction velocity (m s-1) - real(rkind) :: notUsed_WindspdCanopyTop ! windspeed at the top of the canopy (m s-1) - real(rkind) :: notUsed_WindspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) - real(rkind) :: notUsed_dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - real(rkind) :: notUsed_dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind) :: notUsed_dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - real(rkind) :: notUsed_dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind) :: notUsed_dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - - ! (fluxes after perturbations in model states -- canopy air space) - real(rkind) :: turbFluxCanair_dStateCanair ! turbulent exchange from the canopy air space to the atmosphere, after canopy air temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanair_dStateCanopy ! turbulent exchange from the canopy air space to the atmosphere, after canopy temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanair_dStateGround ! turbulent exchange from the canopy air space to the atmosphere, after ground temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanair_dStateCanWat ! turbulent exchange from the canopy air space to the atmosphere, after canopy total water content is perturbed (W m-2) - ! (fluxes after perturbations in model states -- vegetation canopy) - real(rkind) :: turbFluxCanopy_dStateCanair ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy air temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanopy_dStateCanopy ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanopy_dStateGround ! total turbulent heat fluxes from the canopy to the canopy air space, after ground temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanopy_dStateCanWat ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy total water content is perturbed (W m-2) - - ! (fluxes after perturbations in model states -- ground surface) - real(rkind) :: turbFluxGround_dStateCanair ! total turbulent heat fluxes from the ground to the canopy air space, after canopy air temperature is perturbed (W m-2) - real(rkind) :: turbFluxGround_dStateCanopy ! total turbulent heat fluxes from the ground to the canopy air space, after canopy temperature is perturbed (W m-2) - real(rkind) :: turbFluxGround_dStateGround ! total turbulent heat fluxes from the ground to the canopy air space, after ground temperature is perturbed (W m-2) - real(rkind) :: turbFluxGround_dStateCanWat ! total turbulent heat fluxes from the ground to the canopy air space, after canopy total water content is perturbed (W m-2) - - ! (fluxes after perturbations in model states -- canopy evaporation) - real(rkind) :: latHeatCanEvap_dStateCanair ! canopy evaporation after canopy air temperature is perturbed (W m-2) - real(rkind) :: latHeatCanEvap_dStateCanopy ! canopy evaporation after canopy temperature is perturbed (W m-2) - real(rkind) :: latHeatCanEvap_dStateGround ! canopy evaporation after ground temperature is perturbed (W m-2) - real(rkind) :: latHeatCanEvap_dStateCanWat ! canopy evaporation after canopy total water content is perturbed (W m-2) - - ! (flux derivatives -- canopy air space) - real(rkind) :: dTurbFluxCanair_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanair_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanair_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanair_dCanWat ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) - - ! (flux derivatives -- vegetation canopy) - real(rkind) :: dTurbFluxCanopy_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanopy_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanopy_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanopy_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - - ! (flux derivatives -- ground surface) - real(rkind) :: dTurbFluxGround_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dTurbFluxGround_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dTurbFluxGround_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dTurbFluxGround_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - - ! (liquid water flux derivatives -- canopy evap) - real(rkind) :: dLatHeatCanopyEvap_dCanWat ! derivative in latent heat of canopy evaporation w.r.t. canopy total water content (W kg-1) - real(rkind) :: dLatHeatCanopyEvap_dTCanair ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dLatHeatCanopyEvap_dTCanopy ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dLatHeatCanopyEvap_dTGround ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) - - ! (liquid water flux derivatives -- ground evap) - real(rkind) :: dLatHeatGroundEvap_dCanWat ! derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) - real(rkind) :: dLatHeatGroundEvap_dTCanair ! derivative in latent heat of ground evaporation w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) - - ! output: latent heat flux derivatives (canopy trans) - real(rkind) :: dLatHeatCanopyTrans_dCanWat ! derivative in the latent heat of canopy transpiration w.r.t. canopy total water (J kg-1 s-1) - real(rkind) :: dLatHeatCanopyTrans_dTCanair ! derivative in the latent heat of canopy transpiration w.r.t. canopy air temperature - real(rkind) :: dLatHeatCanopyTrans_dTCanopy ! derivative in the latent heat of canopy transpiration w.r.t. canopy temperature - real(rkind) :: dLatHeatCanopyTrans_dTGround ! derivative in the latent heat of canopy transpiration w.r.t. ground temperature - - ! --------------------------------------------------------------------------------------- - ! point to variables in the data structure - ! --------------------------------------------------------------------------------------- - associate(& - - ! input: model decisions - ix_bcUpprTdyn => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision, & ! intent(in): [i4b] choice of upper boundary condition for thermodynamics - ixDerivMethod => model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): [i4b] choice of method to compute derivatives - ix_veg_traits => model_decisions(iLookDECISIONS%veg_traits)%iDecision, & ! intent(in): [i4b] choice of parameterization for vegetation roughness length and displacement height - ix_canopyEmis => model_decisions(iLookDECISIONS%canopyEmis)%iDecision, & ! intent(in): [i4b] choice of parameterization for canopy emissivity - ix_windPrfile => model_decisions(iLookDECISIONS%windPrfile)%iDecision, & ! intent(in): [i4b] choice of canopy wind profile - ix_astability => model_decisions(iLookDECISIONS%astability)%iDecision, & ! intent(in): [i4b] choice of stability function - ix_soilStress => model_decisions(iLookDECISIONS%soilStress)%iDecision, & ! intent(in): [i4b] choice of function for the soil moisture control on stomatal resistance - ix_groundwatr => model_decisions(iLookDECISIONS%groundwatr)%iDecision, & ! intent(in): [i4b] choice of groundwater parameterization - ix_stomResist => model_decisions(iLookDECISIONS%stomResist)%iDecision, & ! intent(in): [i4b] choice of function for stomatal resistance - ix_spatial_gw => model_decisions(iLookDECISIONS%spatial_gw)%iDecision, & ! intent(in): [i4b] choice of groundwater representation (local, basin) - - ! input: layer geometry - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1), & ! intent(in): [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): [i4b] total number of layers - - ! input: physical attributes - vegTypeIndex => type_data%var(iLookTYPE%vegTypeIndex), & ! intent(in): [i4b] vegetation type index - soilTypeIndex => type_data%var(iLookTYPE%soilTypeIndex), & ! intent(in): [i4b] soil type index - - ! input: vegetation parameters - heightCanopyTop => mpar_data%var(iLookPARAM%heightCanopyTop)%dat(1), & ! intent(in): [dp] height at the top of the vegetation canopy (m) - heightCanopyBottom => mpar_data%var(iLookPARAM%heightCanopyBottom)%dat(1), & ! intent(in): [dp] height at the bottom of the vegetation canopy (m) - canopyWettingFactor => mpar_data%var(iLookPARAM%canopyWettingFactor)%dat(1), & ! intent(in): [dp] maximum wetted fraction of the canopy (-) - canopyWettingExp => mpar_data%var(iLookPARAM%canopyWettingExp)%dat(1), & ! intent(in): [dp] exponent in canopy wetting function (-) - scalarCanopyIceMax => diag_data%var(iLookDIAG%scalarCanopyIceMax)%dat(1), & ! intent(in): [dp] maximum interception storage capacity for ice (kg m-2) - scalarCanopyLiqMax => diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1), & ! intent(in): [dp] maximum interception storage capacity for liquid water (kg m-2) - - ! input: vegetation phenology - scalarLAI => diag_data%var(iLookDIAG%scalarLAI)%dat(1), & ! intent(in): [dp] one-sided leaf area index (m2 m-2) - scalarSAI => diag_data%var(iLookDIAG%scalarSAI)%dat(1), & ! intent(in): [dp] one-sided stem area index (m2 m-2) - scalarExposedLAI => diag_data%var(iLookDIAG%scalarExposedLAI)%dat(1), & ! intent(in): [dp] exposed leaf area index after burial by snow (m2 m-2) - scalarExposedSAI => diag_data%var(iLookDIAG%scalarExposedSAI)%dat(1), & ! intent(in): [dp] exposed stem area index after burial by snow (m2 m-2) - scalarGrowingSeasonIndex => diag_data%var(iLookDIAG%scalarGrowingSeasonIndex)%dat(1), & ! intent(in): [dp] growing season index (0=off, 1=on) - scalarFoliageNitrogenFactor => diag_data%var(iLookDIAG%scalarFoliageNitrogenFactor)%dat(1), & ! intent(in): [dp] foliage nitrogen concentration (1.0 = saturated) - - ! input: aerodynamic resistance parameters - z0Snow => mpar_data%var(iLookPARAM%z0Snow)%dat(1), & ! intent(in): [dp] roughness length of snow (m) - z0Soil => mpar_data%var(iLookPARAM%z0Soil)%dat(1), & ! intent(in): [dp] roughness length of soil (m) - z0CanopyParam => mpar_data%var(iLookPARAM%z0Canopy)%dat(1), & ! intent(in): [dp] roughness length of the canopy (m) - zpdFraction => mpar_data%var(iLookPARAM%zpdFraction)%dat(1), & ! intent(in): [dp] zero plane displacement / canopy height (-) - critRichNumber => mpar_data%var(iLookPARAM%critRichNumber)%dat(1), & ! intent(in): [dp] critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam => mpar_data%var(iLookPARAM%Louis79_bparam)%dat(1), & ! intent(in): [dp] parameter in Louis (1979) stability function - Louis79_cStar => mpar_data%var(iLookPARAM%Louis79_cStar)%dat(1), & ! intent(in): [dp] parameter in Louis (1979) stability function - Mahrt87_eScale => mpar_data%var(iLookPARAM%Mahrt87_eScale)%dat(1), & ! intent(in): [dp] exponential scaling factor in the Mahrt (1987) stability function - windReductionParam => mpar_data%var(iLookPARAM%windReductionParam)%dat(1), & ! intent(in): [dp] canopy wind reduction parameter (-) - leafExchangeCoeff => mpar_data%var(iLookPARAM%leafExchangeCoeff)%dat(1), & ! intent(in): [dp] turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) - leafDimension => mpar_data%var(iLookPARAM%leafDimension)%dat(1), & ! intent(in): [dp] characteristic leaf dimension (m) - - ! input: soil stress parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(1), & ! intent(in): [dp] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(1), & ! intent(in): [dp] residual volumetric liquid water content (-) - plantWiltPsi => mpar_data%var(iLookPARAM%plantWiltPsi)%dat(1), & ! intent(in): [dp] matric head at wilting point (m) - soilStressParam => mpar_data%var(iLookPARAM%soilStressParam)%dat(1), & ! intent(in): [dp] parameter in the exponential soil stress function (-) - critSoilWilting => mpar_data%var(iLookPARAM%critSoilWilting)%dat(1), & ! intent(in): [dp] critical vol. liq. water content when plants are wilting (-) - critSoilTranspire => mpar_data%var(iLookPARAM%critSoilTranspire)%dat(1), & ! intent(in): [dp] critical vol. liq. water content when transpiration is limited (-) - critAquiferTranspire => mpar_data%var(iLookPARAM%critAquiferTranspire)%dat(1), & ! intent(in): [dp] critical aquifer storage value when transpiration is limited (m) - minStomatalResistance => mpar_data%var(iLookPARAM%minStomatalResistance)%dat(1), & ! intent(in): [dp] mimimum stomatal resistance (s m-1) - - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - - ! input: forcing at the upper boundary - mHeight => diag_data%var(iLookDIAG%scalarAdjMeasHeight)%dat(1), & ! intent(in): [dp] measurement height (m) - airtemp => forc_data%var(iLookFORCE%airtemp), & ! intent(in): [dp] air temperature at some height above the surface (K) - windspd => forc_data%var(iLookFORCE%windspd), & ! intent(in): [dp] wind speed at some height above the surface (m s-1) - airpres => forc_data%var(iLookFORCE%airpres), & ! intent(in): [dp] air pressure at some height above the surface (Pa) - LWRadAtm => forc_data%var(iLookFORCE%LWRadAtm), & ! intent(in): [dp] downwelling longwave radiation at the upper boundary (W m-2) - scalarVPair => diag_data%var(iLookDIAG%scalarVPair)%dat(1), & ! intent(in): [dp] vapor pressure at some height above the surface (Pa) - scalarO2air => diag_data%var(iLookDIAG%scalarO2air)%dat(1), & ! intent(in): [dp] atmospheric o2 concentration (Pa) - scalarCO2air => diag_data%var(iLookDIAG%scalarCO2air)%dat(1), & ! intent(in): [dp] atmospheric co2 concentration (Pa) - scalarTwetbulb => diag_data%var(iLookDIAG%scalarTwetbulb)%dat(1), & ! intent(in): [dp] wetbulb temperature (K) - scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1), & ! intent(in): [dp] computed rainfall rate (kg m-2 s-1) - scalarSnowfall => flux_data%var(iLookFLUX%scalarSnowfall)%dat(1), & ! intent(in): [dp] computed snowfall rate (kg m-2 s-1) - scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1), & ! intent(in): [dp] rainfall through the vegetation canopy (kg m-2 s-1) - scalarThroughfallSnow => flux_data%var(iLookFLUX%scalarThroughfallSnow)%dat(1), & ! intent(in): [dp] snowfall through the vegetation canopy (kg m-2 s-1) - - ! input: water storage - ! NOTE: soil stress only computed at the start of the substep (firstFluxCall=.true.) - scalarSWE => prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(in): [dp] snow water equivalent on the ground (kg m-2) - scalarSnowDepth => prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(in): [dp] snow depth on the ground surface (m) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat, & ! intent(in): [dp(:)] volumetric fraction of liquid water in each layer (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat, & ! intent(in): [dp(:)] matric head in each soil layer (m) - localAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1), & ! intent(in): [dp] aquifer storage for the local column (m) - basinAquiferStorage => bvar_data%var(iLookBVAR%basin__AquiferStorage)%dat(1), & ! intent(in): [dp] aquifer storage for the single basin (m) - - ! input: shortwave radiation fluxes - scalarCanopySunlitLAI => diag_data%var(iLookDIAG%scalarCanopySunlitLAI)%dat(1), & ! intent(in): [dp] sunlit leaf area (-) - scalarCanopyShadedLAI => diag_data%var(iLookDIAG%scalarCanopyShadedLAI)%dat(1), & ! intent(in): [dp] shaded leaf area (-) - scalarCanopySunlitPAR => flux_data%var(iLookFLUX%scalarCanopySunlitPAR)%dat(1), & ! intent(in): [dp] average absorbed par for sunlit leaves (w m-2) - scalarCanopyShadedPAR => flux_data%var(iLookFLUX%scalarCanopyShadedPAR)%dat(1), & ! intent(in): [dp] average absorbed par for shaded leaves (w m-2) - scalarCanopyAbsorbedSolar => flux_data%var(iLookFLUX%scalarCanopyAbsorbedSolar)%dat(1), & ! intent(in): [dp] solar radiation absorbed by canopy (W m-2) - scalarGroundAbsorbedSolar => flux_data%var(iLookFLUX%scalarGroundAbsorbedSolar)%dat(1), & ! intent(in): [dp] solar radiation absorbed by ground (W m-2) - - ! output: fraction of wetted canopy area and fraction of snow on the ground - scalarCanopyWetFraction => diag_data%var(iLookDIAG%scalarCanopyWetFraction)%dat(1), & ! intent(out): [dp] fraction of canopy that is wet - scalarGroundSnowFraction => diag_data%var(iLookDIAG%scalarGroundSnowFraction)%dat(1), & ! intent(out): [dp] fraction of ground covered with snow (-) - - ! output: longwave radiation fluxes - scalarCanopyEmissivity => diag_data%var(iLookDIAG%scalarCanopyEmissivity)%dat(1), & ! intent(out): [dp] effective emissivity of the canopy (-) - scalarLWRadCanopy => flux_data%var(iLookFLUX%scalarLWRadCanopy)%dat(1), & ! intent(out): [dp] longwave radiation emitted from the canopy (W m-2) - scalarLWRadGround => flux_data%var(iLookFLUX%scalarLWRadGround)%dat(1), & ! intent(out): [dp] longwave radiation emitted at the ground surface (W m-2) - scalarLWRadUbound2Canopy => flux_data%var(iLookFLUX%scalarLWRadUbound2Canopy)%dat(1), & ! intent(out): [dp] downward atmospheric longwave radiation absorbed by the canopy (W m-2) - scalarLWRadUbound2Ground => flux_data%var(iLookFLUX%scalarLWRadUbound2Ground)%dat(1), & ! intent(out): [dp] downward atmospheric longwave radiation absorbed by the ground (W m-2) - scalarLWRadUbound2Ubound => flux_data%var(iLookFLUX%scalarLWRadUbound2Ubound)%dat(1), & ! intent(out): [dp] atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) - scalarLWRadCanopy2Ubound => flux_data%var(iLookFLUX%scalarLWRadCanopy2Ubound)%dat(1), & ! intent(out): [dp] longwave radiation emitted from canopy lost thru upper boundary (W m-2) - scalarLWRadCanopy2Ground => flux_data%var(iLookFLUX%scalarLWRadCanopy2Ground)%dat(1), & ! intent(out): [dp] longwave radiation emitted from canopy absorbed by the ground (W m-2) - scalarLWRadCanopy2Canopy => flux_data%var(iLookFLUX%scalarLWRadCanopy2Canopy)%dat(1), & ! intent(out): [dp] canopy longwave reflected from ground and absorbed by the canopy (W m-2) - scalarLWRadGround2Ubound => flux_data%var(iLookFLUX%scalarLWRadGround2Ubound)%dat(1), & ! intent(out): [dp] longwave radiation emitted from ground lost thru upper boundary (W m-2) - scalarLWRadGround2Canopy => flux_data%var(iLookFLUX%scalarLWRadGround2Canopy)%dat(1), & ! intent(out): [dp] longwave radiation emitted from ground and absorbed by the canopy (W m-2) - scalarLWNetCanopy => flux_data%var(iLookFLUX%scalarLWNetCanopy)%dat(1), & ! intent(out): [dp] net longwave radiation at the canopy (W m-2) - scalarLWNetGround => flux_data%var(iLookFLUX%scalarLWNetGround)%dat(1), & ! intent(out): [dp] net longwave radiation at the ground surface (W m-2) - scalarLWNetUbound => flux_data%var(iLookFLUX%scalarLWNetUbound)%dat(1), & ! intent(out): [dp] net longwave radiation at the upper boundary (W m-2) - - ! output: aerodynamic resistance - scalarZ0Canopy => diag_data%var(iLookDIAG%scalarZ0Canopy)%dat(1), & ! intent(out): [dp] roughness length of the canopy (m) - scalarWindReductionFactor => diag_data%var(iLookDIAG%scalarWindReductionFactor)%dat(1), & ! intent(out): [dp] canopy wind reduction factor (-) - scalarZeroPlaneDisplacement => diag_data%var(iLookDIAG%scalarZeroPlaneDisplacement)%dat(1), & ! intent(out): [dp] zero plane displacement (m) - scalarRiBulkCanopy => diag_data%var(iLookDIAG%scalarRiBulkCanopy)%dat(1), & ! intent(out): [dp] bulk Richardson number for the canopy (-) - scalarRiBulkGround => diag_data%var(iLookDIAG%scalarRiBulkGround)%dat(1), & ! intent(out): [dp] bulk Richardson number for the ground surface (-) - scalarEddyDiffusCanopyTop => flux_data%var(iLookFLUX%scalarEddyDiffusCanopyTop)%dat(1), & ! intent(out): [dp] eddy diffusivity for heat at the top of the canopy (m2 s-1) - scalarFrictionVelocity => flux_data%var(iLookFLUX%scalarFrictionVelocity)%dat(1), & ! intent(out): [dp] friction velocity (m s-1) - scalarWindspdCanopyTop => flux_data%var(iLookFLUX%scalarWindspdCanopyTop)%dat(1), & ! intent(out): [dp] windspeed at the top of the canopy (m s-1) - scalarWindspdCanopyBottom => flux_data%var(iLookFLUX%scalarWindspdCanopyBottom)%dat(1), & ! intent(out): [dp] windspeed at the height of the bottom of the canopy (m s-1) - scalarLeafResistance => flux_data%var(iLookFLUX%scalarLeafResistance)%dat(1), & ! intent(out): [dp] mean leaf boundary layer resistance per unit leaf area (s m-1) - scalarGroundResistance => flux_data%var(iLookFLUX%scalarGroundResistance)%dat(1), & ! intent(out): [dp] below canopy aerodynamic resistance (s m-1) - scalarCanopyResistance => flux_data%var(iLookFLUX%scalarCanopyResistance)%dat(1), & ! intent(out): [dp] above canopy aerodynamic resistance (s m-1) - - ! input/output: soil resistance -- intent(in) and intent(inout) because only called at the first flux call - mLayerRootDensity => diag_data%var(iLookDIAG%mLayerRootDensity)%dat, & ! intent(in): [dp] root density in each layer (-) - scalarAquiferRootFrac => diag_data%var(iLookDIAG%scalarAquiferRootFrac)%dat(1), & ! intent(in): [dp] fraction of roots below the lowest soil layer (-) - scalarTranspireLim => diag_data%var(iLookDIAG%scalarTranspireLim)%dat(1), & ! intent(inout): [dp] weighted average of the transpiration limiting factor (-) - mLayerTranspireLim => diag_data%var(iLookDIAG%mLayerTranspireLim)%dat, & ! intent(inout): [dp] transpiration limiting factor in each layer (-) - scalarTranspireLimAqfr => diag_data%var(iLookDIAG%scalarTranspireLimAqfr)%dat(1), & ! intent(inout): [dp] transpiration limiting factor for the aquifer (-) - scalarSoilRelHumidity => diag_data%var(iLookDIAG%scalarSoilRelHumidity)%dat(1), & ! intent(inout): [dp] relative humidity in the soil pores [0-1] - scalarSoilResistance => flux_data%var(iLookFLUX%scalarSoilResistance)%dat(1), & ! intent(inout): [dp] resistance from the soil (s m-1) - - ! input/output: stomatal resistance -- intent(inout) because only called at the first flux call - scalarStomResistSunlit => flux_data%var(iLookFLUX%scalarStomResistSunlit)%dat(1), & ! intent(inout): [dp] stomatal resistance for sunlit leaves (s m-1) - scalarStomResistShaded => flux_data%var(iLookFLUX%scalarStomResistShaded)%dat(1), & ! intent(inout): [dp] stomatal resistance for shaded leaves (s m-1) - scalarPhotosynthesisSunlit => flux_data%var(iLookFLUX%scalarPhotosynthesisSunlit)%dat(1), & ! intent(inout): [dp] sunlit photosynthesis (umolco2 m-2 s-1) - scalarPhotosynthesisShaded => flux_data%var(iLookFLUX%scalarPhotosynthesisShaded)%dat(1), & ! intent(inout): [dp] shaded photosynthesis (umolco2 m-2 s-1) - - ! output: turbulent heat fluxes - scalarLatHeatSubVapCanopy => diag_data%var(iLookDIAG%scalarLatHeatSubVapCanopy)%dat(1), & ! intent(inout): [dp] latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) - scalarLatHeatSubVapGround => diag_data%var(iLookDIAG%scalarLatHeatSubVapGround)%dat(1), & ! intent(inout): [dp] latent heat of sublimation/vaporization for the ground surface (J kg-1) - scalarSatVP_canopyTemp => diag_data%var(iLookDIAG%scalarSatVP_CanopyTemp)%dat(1), & ! intent(out): [dp] saturation vapor pressure at the temperature of the vegetation canopy (Pa) - scalarSatVP_groundTemp => diag_data%var(iLookDIAG%scalarSatVP_GroundTemp)%dat(1), & ! intent(out): [dp] saturation vapor pressure at the temperature of the ground surface (Pa) - scalarSenHeatTotal => flux_data%var(iLookFLUX%scalarSenHeatTotal)%dat(1), & ! intent(out): [dp] sensible heat from the canopy air space to the atmosphere (W m-2) - scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1), & ! intent(out): [dp] sensible heat flux from the canopy to the canopy air space (W m-2) - scalarSenHeatGround => flux_data%var(iLookFLUX%scalarSenHeatGround)%dat(1), & ! intent(out): [dp] sensible heat flux from ground surface below vegetation (W m-2) - scalarLatHeatTotal => flux_data%var(iLookFLUX%scalarLatHeatTotal)%dat(1), & ! intent(out): [dp] latent heat from the canopy air space to the atmosphere (W m-2) - scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1), & ! intent(out): [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - scalarLatHeatCanopyTrans => flux_data%var(iLookFLUX%scalarLatHeatCanopyTrans)%dat(1), & ! intent(out): [dp] latent heat flux for transpiration from the canopy to the canopy air space (W m-2) - scalarLatHeatGround => flux_data%var(iLookFLUX%scalarLatHeatGround)%dat(1), & ! intent(out): [dp] latent heat flux from ground surface below vegetation (W m-2) - - ! output: advective heat fluxes - scalarCanopyAdvectiveHeatFlux => flux_data%var(iLookFLUX%scalarCanopyAdvectiveHeatFlux)%dat(1), & ! intent(out): [dp] heat advected to the canopy surface with rain + snow (W m-2) - scalarGroundAdvectiveHeatFlux => flux_data%var(iLookFLUX%scalarGroundAdvectiveHeatFlux)%dat(1), & ! intent(out): [dp] heat advected to the ground surface with throughfall (W m-2) - - ! output: mass fluxes - scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1), & ! intent(out): [dp] canopy sublimation/frost (kg m-2 s-1) - scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! intent(out): [dp] snow sublimation/frost -- below canopy or non-vegetated (kg m-2 s-1) - - ! input/output: canopy air space variables - scalarVP_CanopyAir => diag_data%var(iLookDIAG%scalarVP_CanopyAir)%dat(1), & ! intent(inout): [dp] vapor pressure of the canopy air space (Pa) - scalarCanopyStabilityCorrection => diag_data%var(iLookDIAG%scalarCanopyStabilityCorrection)%dat(1),& ! intent(inout): [dp] stability correction for the canopy (-) - scalarGroundStabilityCorrection => diag_data%var(iLookDIAG%scalarGroundStabilityCorrection)%dat(1),& ! intent(inout): [dp] stability correction for the ground surface (-) - - ! output: liquid water fluxes - scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1), & ! intent(out): [dp] canopy transpiration (kg m-2 s-1) - scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1), & ! intent(out): [dp] canopy evaporation/condensation (kg m-2 s-1) - scalarGroundEvaporation => flux_data%var(iLookFLUX%scalarGroundEvaporation)%dat(1), & ! intent(out): [dp] ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - - ! output: derived fluxes - scalarTotalET => flux_data%var(iLookFLUX%scalarTotalET)%dat(1), & ! intent(out): [dp] total ET (kg m-2 s-1) - scalarNetRadiation => flux_data%var(iLookFLUX%scalarNetRadiation)%dat(1) & ! intent(out): [dp] net radiation (W m-2) - ) - ! --------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="vegNrgFlux/" - - ! initialize printflag - printflag = .false. - - ! identify the type of boundary condition for thermodynamics - select case(ix_bcUpprTdyn) +! ******************************************************************************************************* +! public subroutine vegNrgFlux: muster program to compute energy fluxes at vegetation and ground surfaces +! ******************************************************************************************************* +subroutine vegNrgFlux(& + ! input: model control + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + insideIDA, & ! intent(in): flag to indicate inside Sundials Solver (do not require longwave to be balanced) + + ! input: model state variables + upperBoundTemp, & ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature + canairTempTrial, & ! intent(in): trial value of the canopy air space temperature (K) + canopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + groundTempTrial, & ! intent(in): trial value of ground temperature (K) + canopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) + canopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) + + ! input: model derivatives + dCanLiq_dTcanopy, & ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) + + ! input/output: data structures + type_data, & ! intent(in): type of vegetation and soil + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): state vector geometry + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + bvar_data, & ! intent(in): model variables for the local basin + model_decisions, & ! intent(in): model decisions + + ! output: liquid water fluxes associated with evaporation/transpiration (needed for coupling) + returnCanopyTranspiration, & ! intent(out): canopy transpiration (kg m-2 s-1) + returnCanopyEvaporation, & ! intent(out): canopy evaporation/condensation (kg m-2 s-1) + returnGroundEvaporation, & ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + + ! output: fluxes + canairNetFlux, & ! intent(out): net energy flux for the canopy air space (W m-2) + canopyNetFlux, & ! intent(out): net energy flux for the vegetation canopy (W m-2) + groundNetFlux, & ! intent(out): net energy flux for the ground surface (W m-2) + + ! output: energy flux derivatives + dCanairNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) + dCanairNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) + dCanairNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) + dCanopyNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) + dCanopyNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) + dCanopyNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) + dGroundNetFlux_dCanairTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) + dGroundNetFlux_dCanopyTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) + dGroundNetFlux_dGroundTemp, & ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + + ! output liquid water flux derivarives (canopy evap) + dCanopyEvaporation_dCanWat, & ! intent(out): derivative in canopy evaporation w.r.t. canopy total water content (s-1) + dCanopyEvaporation_dTCanair, & ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTCanopy, & ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTGround, & ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + + ! output: liquid water flux derivarives (ground evap) + dGroundEvaporation_dCanWat, & ! intent(out): derivative in ground evaporation w.r.t. canopy total water content (s-1) + dGroundEvaporation_dTCanair, & ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTCanopy, & ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTGround, & ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + + ! output: transpiration derivatives + dCanopyTrans_dCanWat, & ! intent(out): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair, & ! intent(out): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy, & ! intent(out): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround, & ! intent(out): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + + ! output: cross derivative terms + dCanopyNetFlux_dCanWat, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) + dGroundNetFlux_dCanWat, & ! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) + + ! output: error control + err,message) ! intent(out): error control + + ! utilities + USE expIntegral_module,only:expInt ! function to calculate the exponential integral + ! conversion functions + USE conv_funcs_module,only:satVapPress ! function to compute the saturated vapor pressure (Pa) + USE conv_funcs_module,only:getLatentHeatValue ! function to identify latent heat of vaporization/sublimation (J kg-1) + ! stomatal resistance + USE stomResist_module,only:stomResist ! subroutine to calculate stomatal resistance + ! phase changes + USE snow_utils_module,only:fracliquid ! compute fraction of liquid water at a given temperature + + ! compute energy and mass fluxes for vegetation + implicit none + + ! --------------------------------------------------------------------------------------- + ! * dummy variables + ! --------------------------------------------------------------------------------------- + ! input: model control + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(in) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: insideIDA ! flag if inside Sundials solver + ! input: model state variables + real(rkind),intent(in) :: upperBoundTemp ! temperature of the upper boundary (K) --> NOTE: use air temperature + real(rkind),intent(in) :: canairTempTrial ! trial value of canopy air space temperature (K) + real(rkind),intent(in) :: canopyTempTrial ! trial value of canopy temperature (K) + real(rkind),intent(in) :: groundTempTrial ! trial value of ground temperature (K) + real(rkind),intent(in) :: canopyIceTrial ! trial value of mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: canopyLiqTrial ! trial value of mass of liquid water on the vegetation canopy (kg m-2) + + ! input: model derivatives + real(rkind),intent(in) :: dCanLiq_dTcanopy ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) + + ! input/output: data structures + type(var_i),intent(in) :: type_data ! type of vegetation and soil + type(var_d),intent(in) :: forc_data ! model forcing data + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! state vector geometry + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin + type(model_options),intent(in) :: model_decisions(:) ! model decisions + + ! output: liquid water fluxes associated with evaporation/transpiration (needed for coupling) + real(rkind),intent(out) :: returnCanopyTranspiration ! canopy transpiration (kg m-2 s-1) + real(rkind),intent(out) :: returnCanopyEvaporation ! canopy evaporation/condensation (kg m-2 s-1) + real(rkind),intent(out) :: returnGroundEvaporation ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + + ! output: fluxes + real(rkind),intent(out) :: canairNetFlux ! net energy flux for the canopy air space (W m-2) + real(rkind),intent(out) :: canopyNetFlux ! net energy flux for the vegetation canopy (W m-2) + real(rkind),intent(out) :: groundNetFlux ! net energy flux for the ground surface (W m-2) + + ! output: energy flux derivatives + real(rkind),intent(out) :: dCanairNetFlux_dCanairTemp ! derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) + real(rkind),intent(out) :: dCanairNetFlux_dCanopyTemp ! derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) + real(rkind),intent(out) :: dCanairNetFlux_dGroundTemp ! derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) + real(rkind),intent(out) :: dCanopyNetFlux_dCanairTemp ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) + real(rkind),intent(out) :: dCanopyNetFlux_dCanopyTemp ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) + real(rkind),intent(out) :: dCanopyNetFlux_dGroundTemp ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) + real(rkind),intent(out) :: dGroundNetFlux_dCanairTemp ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) + real(rkind),intent(out) :: dGroundNetFlux_dCanopyTemp ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) + real(rkind),intent(out) :: dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + + ! output: liquid flux derivatives (canopy evap) + real(rkind),intent(out) :: dCanopyEvaporation_dCanWat ! derivative in canopy evaporation w.r.t. canopy total water content (s-1) + real(rkind),intent(out) :: dCanopyEvaporation_dTCanair ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(rkind),intent(out) :: dCanopyEvaporation_dTCanopy ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(rkind),intent(out) :: dCanopyEvaporation_dTGround ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + + ! output: liquid flux derivatives (ground evap) + real(rkind),intent(out) :: dGroundEvaporation_dCanWat ! derivative in ground evaporation w.r.t. canopy total water content (s-1) + real(rkind),intent(out) :: dGroundEvaporation_dTCanair ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(rkind),intent(out) :: dGroundEvaporation_dTCanopy ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(rkind),intent(out) :: dGroundEvaporation_dTGround ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + + ! output: transpiration derivatives + real(rkind),intent(out) :: dCanopyTrans_dCanWat ! intent(out): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + real(rkind),intent(out) :: dCanopyTrans_dTCanair ! intent(out): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(rkind),intent(out) :: dCanopyTrans_dTCanopy ! intent(out): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(rkind),intent(out) :: dCanopyTrans_dTGround ! intent(out): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + + ! output: cross derivative terms + real(rkind),intent(out) :: dCanopyNetFlux_dCanWat ! derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) + real(rkind),intent(out) :: dGroundNetFlux_dCanWat ! derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) + + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + + ! --------------------------------------------------------------------------------------- + ! * local variables + ! --------------------------------------------------------------------------------------- + ! local (general) + character(LEN=256) :: cmessage ! error message of downwind routine + real(rkind) :: VAI ! vegetation area index (m2 m-2) + real(rkind) :: exposedVAI ! exposed vegetation area index (m2 m-2) + real(rkind) :: totalCanopyWater ! total water on the vegetation canopy (kg m-2) + real(rkind) :: scalarAquiferStorage ! aquifer storage (m) + + ! local (compute numerical derivatives) + integer(i4b),parameter :: unperturbed=1 ! named variable to identify the case of unperturbed state variables + integer(i4b),parameter :: perturbStateGround=2 ! named variable to identify the case where we perturb the ground temperature + integer(i4b),parameter :: perturbStateCanopy=3 ! named variable to identify the case where we perturb the canopy temperature + integer(i4b),parameter :: perturbStateCanair=4 ! named variable to identify the case where we perturb the canopy air temperature + integer(i4b),parameter :: perturbStateCanWat=5 ! named variable to identify the case where we perturb the canopy total water content + integer(i4b) :: itry ! index of flux evaluation + integer(i4b) :: nFlux ! number of flux evaluations + real(rkind) :: groundTemp ! value of ground temperature used in flux calculations (may be perturbed) + real(rkind) :: canopyTemp ! value of canopy temperature used in flux calculations (may be perturbed) + real(rkind) :: canairTemp ! value of canopy air temperature used in flux calculations (may be perturbed) + real(rkind) :: canopyWat ! value of canopy total water used in flux calculations (may be perturbed) + real(rkind) :: canopyLiq ! value of canopy liquid water used in flux calculations (may be perturbed) + real(rkind) :: canopyIce ! value of canopy ice used in flux calculations (may be perturbed) + real(rkind) :: try0,try1 ! trial values to evaluate specific derivatives (testing only) + + ! local (saturation vapor pressure of veg) + real(rkind) :: TV_celcius ! vegetaion temperature (C) + real(rkind) :: TG_celcius ! ground temperature (C) + real(rkind) :: dSVPCanopy_dCanopyTemp ! derivative in canopy saturated vapor pressure w.r.t. vegetation temperature (Pa/K) + real(rkind) :: dSVPGround_dGroundTemp ! derivative in ground saturated vapor pressure w.r.t. ground temperature (Pa/K) + + ! local (wetted canopy area) + real(rkind) :: fracLiquidCanopy ! fraction of liquid water in the canopy (-) + real(rkind) :: canopyWetFraction ! trial value of the canopy wetted fraction (-) + real(rkind) :: dCanopyWetFraction_dWat ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) + real(rkind) :: dCanopyWetFraction_dT ! derivative in wetted fraction w.r.t. canopy temperature (K-1) + + ! local (longwave radiation) + real(rkind) :: expi ! exponential integral + real(rkind) :: scaleLAI ! scaled LAI (computing diffuse transmissivity) + real(rkind) :: diffuseTrans ! diffuse transmissivity (-) + real(rkind) :: groundEmissivity ! emissivity of the ground surface (-) + real(rkind),parameter :: vegEmissivity=0.98_rkind ! emissivity of vegetation (0.9665 in JULES) (-) + real(rkind),parameter :: soilEmissivity=0.98_rkind ! emmisivity of the soil (0.9665 in JULES) (-) + real(rkind),parameter :: snowEmissivity=0.99_rkind ! emissivity of snow (-) + real(rkind) :: dLWNetCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dLWNetGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dLWNetCanopy_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dLWNetGround_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) + + ! local (aerodynamic resistance) + real(rkind) :: scalarCanopyStabilityCorrection_old ! stability correction for the canopy (-) + real(rkind) :: scalarGroundStabilityCorrection_old ! stability correction for the ground surface (-) + + ! local (turbulent heat transfer) + real(rkind) :: z0Ground ! roughness length of the ground (ground below the canopy or non-vegetated surface) (m) + real(rkind) :: soilEvapFactor ! soil water control on evaporation from non-vegetated surfaces + real(rkind) :: soilRelHumidity_noSnow ! relative humidity in the soil pores [0-1] + real(rkind) :: scalarLeafConductance ! leaf conductance (m s-1) + real(rkind) :: scalarCanopyConductance ! canopy conductance (m s-1) + real(rkind) :: scalarGroundConductanceSH ! ground conductance for sensible heat (m s-1) + real(rkind) :: scalarGroundConductanceLH ! ground conductance for latent heat -- includes soil resistance (m s-1) + real(rkind) :: scalarEvapConductance ! conductance for evaporation (m s-1) + real(rkind) :: scalarTransConductance ! conductance for transpiration (m s-1) + real(rkind) :: scalarTotalConductanceSH ! total conductance for sensible heat (m s-1) + real(rkind) :: scalarTotalConductanceLH ! total conductance for latent heat (m s-1) + real(rkind) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + real(rkind) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + real(rkind) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + real(rkind) :: turbFluxCanair ! total turbulent heat fluxes exchanged at the canopy air space (W m-2) + real(rkind) :: turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) + real(rkind) :: turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) + + ! local (turbulent heat transfer -- compute numerical derivatives) + ! (temporary scalar resistances when states are perturbed) + real(rkind) :: trialLeafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) + real(rkind) :: trialGroundResistance ! below canopy aerodynamic resistance (s m-1) + real(rkind) :: trialCanopyResistance ! above canopy aerodynamic resistance (s m-1) + real(rkind) :: notUsed_RiBulkCanopy ! bulk Richardson number for the canopy (-) + real(rkind) :: notUsed_RiBulkGround ! bulk Richardson number for the ground surface (-) + real(rkind) :: notUsed_z0Canopy ! roughness length of the vegetation canopy (m) + real(rkind) :: notUsed_WindReductionFactor ! canopy wind reduction factor (-) + real(rkind) :: notUsed_ZeroPlaneDisplacement ! zero plane displacement (m) + real(rkind) :: notUsed_scalarCanopyStabilityCorrection ! stability correction for the canopy (-) + real(rkind) :: notUsed_scalarGroundStabilityCorrection ! stability correction for the ground surface (-) + real(rkind) :: notUsed_EddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) + real(rkind) :: notUsed_FrictionVelocity ! friction velocity (m s-1) + real(rkind) :: notUsed_WindspdCanopyTop ! windspeed at the top of the canopy (m s-1) + real(rkind) :: notUsed_WindspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) + real(rkind) :: notUsed_dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + real(rkind) :: notUsed_dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind) :: notUsed_dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + real(rkind) :: notUsed_dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind) :: notUsed_dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + + ! (fluxes after perturbations in model states -- canopy air space) + real(rkind) :: turbFluxCanair_dStateCanair ! turbulent exchange from the canopy air space to the atmosphere, after canopy air temperature is perturbed (W m-2) + real(rkind) :: turbFluxCanair_dStateCanopy ! turbulent exchange from the canopy air space to the atmosphere, after canopy temperature is perturbed (W m-2) + real(rkind) :: turbFluxCanair_dStateGround ! turbulent exchange from the canopy air space to the atmosphere, after ground temperature is perturbed (W m-2) + real(rkind) :: turbFluxCanair_dStateCanWat ! turbulent exchange from the canopy air space to the atmosphere, after canopy total water content is perturbed (W m-2) + ! (fluxes after perturbations in model states -- vegetation canopy) + real(rkind) :: turbFluxCanopy_dStateCanair ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy air temperature is perturbed (W m-2) + real(rkind) :: turbFluxCanopy_dStateCanopy ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy temperature is perturbed (W m-2) + real(rkind) :: turbFluxCanopy_dStateGround ! total turbulent heat fluxes from the canopy to the canopy air space, after ground temperature is perturbed (W m-2) + real(rkind) :: turbFluxCanopy_dStateCanWat ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy total water content is perturbed (W m-2) + + ! (fluxes after perturbations in model states -- ground surface) + real(rkind) :: turbFluxGround_dStateCanair ! total turbulent heat fluxes from the ground to the canopy air space, after canopy air temperature is perturbed (W m-2) + real(rkind) :: turbFluxGround_dStateCanopy ! total turbulent heat fluxes from the ground to the canopy air space, after canopy temperature is perturbed (W m-2) + real(rkind) :: turbFluxGround_dStateGround ! total turbulent heat fluxes from the ground to the canopy air space, after ground temperature is perturbed (W m-2) + real(rkind) :: turbFluxGround_dStateCanWat ! total turbulent heat fluxes from the ground to the canopy air space, after canopy total water content is perturbed (W m-2) + + ! (fluxes after perturbations in model states -- canopy evaporation) + real(rkind) :: latHeatCanEvap_dStateCanair ! canopy evaporation after canopy air temperature is perturbed (W m-2) + real(rkind) :: latHeatCanEvap_dStateCanopy ! canopy evaporation after canopy temperature is perturbed (W m-2) + real(rkind) :: latHeatCanEvap_dStateGround ! canopy evaporation after ground temperature is perturbed (W m-2) + real(rkind) :: latHeatCanEvap_dStateCanWat ! canopy evaporation after canopy total water content is perturbed (W m-2) + + ! (flux derivatives -- canopy air space) + real(rkind) :: dTurbFluxCanair_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanair_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanair_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanair_dCanWat ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) + + ! (flux derivatives -- vegetation canopy) + real(rkind) :: dTurbFluxCanopy_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanopy_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanopy_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanopy_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + + ! (flux derivatives -- ground surface) + real(rkind) :: dTurbFluxGround_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dTurbFluxGround_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dTurbFluxGround_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dTurbFluxGround_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + + ! (liquid water flux derivatives -- canopy evap) + real(rkind) :: dLatHeatCanopyEvap_dCanWat ! derivative in latent heat of canopy evaporation w.r.t. canopy total water content (W kg-1) + real(rkind) :: dLatHeatCanopyEvap_dTCanair ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dLatHeatCanopyEvap_dTCanopy ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dLatHeatCanopyEvap_dTGround ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) + + ! (liquid water flux derivatives -- ground evap) + real(rkind) :: dLatHeatGroundEvap_dCanWat ! derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) + real(rkind) :: dLatHeatGroundEvap_dTCanair ! derivative in latent heat of ground evaporation w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) + + ! output: latent heat flux derivatives (canopy trans) + real(rkind) :: dLatHeatCanopyTrans_dCanWat ! derivative in the latent heat of canopy transpiration w.r.t. canopy total water (J kg-1 s-1) + real(rkind) :: dLatHeatCanopyTrans_dTCanair ! derivative in the latent heat of canopy transpiration w.r.t. canopy air temperature + real(rkind) :: dLatHeatCanopyTrans_dTCanopy ! derivative in the latent heat of canopy transpiration w.r.t. canopy temperature + real(rkind) :: dLatHeatCanopyTrans_dTGround ! derivative in the latent heat of canopy transpiration w.r.t. ground temperature + + ! --------------------------------------------------------------------------------------- + ! point to variables in the data structure + ! --------------------------------------------------------------------------------------- + associate(& + + ! input: model decisions + ix_bcUpprTdyn => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision, & ! intent(in): [i4b] choice of upper boundary condition for thermodynamics + ixDerivMethod => model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): [i4b] choice of method to compute derivatives + ix_veg_traits => model_decisions(iLookDECISIONS%veg_traits)%iDecision, & ! intent(in): [i4b] choice of parameterization for vegetation roughness length and displacement height + ix_canopyEmis => model_decisions(iLookDECISIONS%canopyEmis)%iDecision, & ! intent(in): [i4b] choice of parameterization for canopy emissivity + ix_windPrfile => model_decisions(iLookDECISIONS%windPrfile)%iDecision, & ! intent(in): [i4b] choice of canopy wind profile + ix_astability => model_decisions(iLookDECISIONS%astability)%iDecision, & ! intent(in): [i4b] choice of stability function + ix_soilStress => model_decisions(iLookDECISIONS%soilStress)%iDecision, & ! intent(in): [i4b] choice of function for the soil moisture control on stomatal resistance + ix_groundwatr => model_decisions(iLookDECISIONS%groundwatr)%iDecision, & ! intent(in): [i4b] choice of groundwater parameterization + ix_stomResist => model_decisions(iLookDECISIONS%stomResist)%iDecision, & ! intent(in): [i4b] choice of function for stomatal resistance + ix_spatial_gw => model_decisions(iLookDECISIONS%spatial_gw)%iDecision, & ! intent(in): [i4b] choice of groundwater representation (local, basin) + + ! input: layer geometry + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1), & ! intent(in): [i4b] number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): [i4b] total number of layers + + ! input: physical attributes + vegTypeIndex => type_data%var(iLookTYPE%vegTypeIndex), & ! intent(in): [i4b] vegetation type index + soilTypeIndex => type_data%var(iLookTYPE%soilTypeIndex), & ! intent(in): [i4b] soil type index + + ! input: vegetation parameters + heightCanopyTop => mpar_data%var(iLookPARAM%heightCanopyTop)%dat(1), & ! intent(in): [dp] height at the top of the vegetation canopy (m) + heightCanopyBottom => mpar_data%var(iLookPARAM%heightCanopyBottom)%dat(1), & ! intent(in): [dp] height at the bottom of the vegetation canopy (m) + canopyWettingFactor => mpar_data%var(iLookPARAM%canopyWettingFactor)%dat(1), & ! intent(in): [dp] maximum wetted fraction of the canopy (-) + canopyWettingExp => mpar_data%var(iLookPARAM%canopyWettingExp)%dat(1), & ! intent(in): [dp] exponent in canopy wetting function (-) + scalarCanopyIceMax => diag_data%var(iLookDIAG%scalarCanopyIceMax)%dat(1), & ! intent(in): [dp] maximum interception storage capacity for ice (kg m-2) + scalarCanopyLiqMax => diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1), & ! intent(in): [dp] maximum interception storage capacity for liquid water (kg m-2) + + ! input: vegetation phenology + scalarLAI => diag_data%var(iLookDIAG%scalarLAI)%dat(1), & ! intent(in): [dp] one-sided leaf area index (m2 m-2) + scalarSAI => diag_data%var(iLookDIAG%scalarSAI)%dat(1), & ! intent(in): [dp] one-sided stem area index (m2 m-2) + scalarExposedLAI => diag_data%var(iLookDIAG%scalarExposedLAI)%dat(1), & ! intent(in): [dp] exposed leaf area index after burial by snow (m2 m-2) + scalarExposedSAI => diag_data%var(iLookDIAG%scalarExposedSAI)%dat(1), & ! intent(in): [dp] exposed stem area index after burial by snow (m2 m-2) + scalarGrowingSeasonIndex => diag_data%var(iLookDIAG%scalarGrowingSeasonIndex)%dat(1), & ! intent(in): [dp] growing season index (0=off, 1=on) + scalarFoliageNitrogenFactor => diag_data%var(iLookDIAG%scalarFoliageNitrogenFactor)%dat(1), & ! intent(in): [dp] foliage nitrogen concentration (1.0 = saturated) + + ! input: aerodynamic resistance parameters + z0Snow => mpar_data%var(iLookPARAM%z0Snow)%dat(1), & ! intent(in): [dp] roughness length of snow (m) + z0Soil => mpar_data%var(iLookPARAM%z0Soil)%dat(1), & ! intent(in): [dp] roughness length of soil (m) + z0CanopyParam => mpar_data%var(iLookPARAM%z0Canopy)%dat(1), & ! intent(in): [dp] roughness length of the canopy (m) + zpdFraction => mpar_data%var(iLookPARAM%zpdFraction)%dat(1), & ! intent(in): [dp] zero plane displacement / canopy height (-) + critRichNumber => mpar_data%var(iLookPARAM%critRichNumber)%dat(1), & ! intent(in): [dp] critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam => mpar_data%var(iLookPARAM%Louis79_bparam)%dat(1), & ! intent(in): [dp] parameter in Louis (1979) stability function + Louis79_cStar => mpar_data%var(iLookPARAM%Louis79_cStar)%dat(1), & ! intent(in): [dp] parameter in Louis (1979) stability function + Mahrt87_eScale => mpar_data%var(iLookPARAM%Mahrt87_eScale)%dat(1), & ! intent(in): [dp] exponential scaling factor in the Mahrt (1987) stability function + windReductionParam => mpar_data%var(iLookPARAM%windReductionParam)%dat(1), & ! intent(in): [dp] canopy wind reduction parameter (-) + leafExchangeCoeff => mpar_data%var(iLookPARAM%leafExchangeCoeff)%dat(1), & ! intent(in): [dp] turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) + leafDimension => mpar_data%var(iLookPARAM%leafDimension)%dat(1), & ! intent(in): [dp] characteristic leaf dimension (m) + + ! input: soil stress parameters + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(1), & ! intent(in): [dp] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(1), & ! intent(in): [dp] residual volumetric liquid water content (-) + plantWiltPsi => mpar_data%var(iLookPARAM%plantWiltPsi)%dat(1), & ! intent(in): [dp] matric head at wilting point (m) + soilStressParam => mpar_data%var(iLookPARAM%soilStressParam)%dat(1), & ! intent(in): [dp] parameter in the exponential soil stress function (-) + critSoilWilting => mpar_data%var(iLookPARAM%critSoilWilting)%dat(1), & ! intent(in): [dp] critical vol. liq. water content when plants are wilting (-) + critSoilTranspire => mpar_data%var(iLookPARAM%critSoilTranspire)%dat(1), & ! intent(in): [dp] critical vol. liq. water content when transpiration is limited (-) + critAquiferTranspire => mpar_data%var(iLookPARAM%critAquiferTranspire)%dat(1), & ! intent(in): [dp] critical aquifer storage value when transpiration is limited (m) + minStomatalResistance => mpar_data%var(iLookPARAM%minStomatalResistance)%dat(1), & ! intent(in): [dp] mimimum stomatal resistance (s m-1) + + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + + ! input: forcing at the upper boundary + mHeight => diag_data%var(iLookDIAG%scalarAdjMeasHeight)%dat(1), & ! intent(in): [dp] measurement height (m) + airtemp => forc_data%var(iLookFORCE%airtemp), & ! intent(in): [dp] air temperature at some height above the surface (K) + windspd => forc_data%var(iLookFORCE%windspd), & ! intent(in): [dp] wind speed at some height above the surface (m s-1) + airpres => forc_data%var(iLookFORCE%airpres), & ! intent(in): [dp] air pressure at some height above the surface (Pa) + LWRadAtm => forc_data%var(iLookFORCE%LWRadAtm), & ! intent(in): [dp] downwelling longwave radiation at the upper boundary (W m-2) + scalarVPair => diag_data%var(iLookDIAG%scalarVPair)%dat(1), & ! intent(in): [dp] vapor pressure at some height above the surface (Pa) + scalarO2air => diag_data%var(iLookDIAG%scalarO2air)%dat(1), & ! intent(in): [dp] atmospheric o2 concentration (Pa) + scalarCO2air => diag_data%var(iLookDIAG%scalarCO2air)%dat(1), & ! intent(in): [dp] atmospheric co2 concentration (Pa) + scalarTwetbulb => diag_data%var(iLookDIAG%scalarTwetbulb)%dat(1), & ! intent(in): [dp] wetbulb temperature (K) + scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1), & ! intent(in): [dp] computed rainfall rate (kg m-2 s-1) + scalarSnowfall => flux_data%var(iLookFLUX%scalarSnowfall)%dat(1), & ! intent(in): [dp] computed snowfall rate (kg m-2 s-1) + scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1), & ! intent(in): [dp] rainfall through the vegetation canopy (kg m-2 s-1) + scalarThroughfallSnow => flux_data%var(iLookFLUX%scalarThroughfallSnow)%dat(1), & ! intent(in): [dp] snowfall through the vegetation canopy (kg m-2 s-1) + + ! input: water storage + ! NOTE: soil stress only computed at the start of the substep (firstFluxCall=.true.) + scalarSWE => prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(in): [dp] snow water equivalent on the ground (kg m-2) + scalarSnowDepth => prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(in): [dp] snow depth on the ground surface (m) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat, & ! intent(in): [dp(:)] volumetric fraction of liquid water in each layer (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat, & ! intent(in): [dp(:)] matric head in each soil layer (m) + localAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1), & ! intent(in): [dp] aquifer storage for the local column (m) + basinAquiferStorage => bvar_data%var(iLookBVAR%basin__AquiferStorage)%dat(1), & ! intent(in): [dp] aquifer storage for the single basin (m) + + ! input: shortwave radiation fluxes + scalarCanopySunlitLAI => diag_data%var(iLookDIAG%scalarCanopySunlitLAI)%dat(1), & ! intent(in): [dp] sunlit leaf area (-) + scalarCanopyShadedLAI => diag_data%var(iLookDIAG%scalarCanopyShadedLAI)%dat(1), & ! intent(in): [dp] shaded leaf area (-) + scalarCanopySunlitPAR => flux_data%var(iLookFLUX%scalarCanopySunlitPAR)%dat(1), & ! intent(in): [dp] average absorbed par for sunlit leaves (w m-2) + scalarCanopyShadedPAR => flux_data%var(iLookFLUX%scalarCanopyShadedPAR)%dat(1), & ! intent(in): [dp] average absorbed par for shaded leaves (w m-2) + scalarCanopyAbsorbedSolar => flux_data%var(iLookFLUX%scalarCanopyAbsorbedSolar)%dat(1), & ! intent(in): [dp] solar radiation absorbed by canopy (W m-2) + scalarGroundAbsorbedSolar => flux_data%var(iLookFLUX%scalarGroundAbsorbedSolar)%dat(1), & ! intent(in): [dp] solar radiation absorbed by ground (W m-2) + + ! output: fraction of wetted canopy area and fraction of snow on the ground + scalarCanopyWetFraction => diag_data%var(iLookDIAG%scalarCanopyWetFraction)%dat(1), & ! intent(out): [dp] fraction of canopy that is wet + scalarGroundSnowFraction => diag_data%var(iLookDIAG%scalarGroundSnowFraction)%dat(1), & ! intent(out): [dp] fraction of ground covered with snow (-) + + ! output: longwave radiation fluxes + scalarCanopyEmissivity => diag_data%var(iLookDIAG%scalarCanopyEmissivity)%dat(1), & ! intent(out): [dp] effective emissivity of the canopy (-) + scalarLWRadCanopy => flux_data%var(iLookFLUX%scalarLWRadCanopy)%dat(1), & ! intent(out): [dp] longwave radiation emitted from the canopy (W m-2) + scalarLWRadGround => flux_data%var(iLookFLUX%scalarLWRadGround)%dat(1), & ! intent(out): [dp] longwave radiation emitted at the ground surface (W m-2) + scalarLWRadUbound2Canopy => flux_data%var(iLookFLUX%scalarLWRadUbound2Canopy)%dat(1), & ! intent(out): [dp] downward atmospheric longwave radiation absorbed by the canopy (W m-2) + scalarLWRadUbound2Ground => flux_data%var(iLookFLUX%scalarLWRadUbound2Ground)%dat(1), & ! intent(out): [dp] downward atmospheric longwave radiation absorbed by the ground (W m-2) + scalarLWRadUbound2Ubound => flux_data%var(iLookFLUX%scalarLWRadUbound2Ubound)%dat(1), & ! intent(out): [dp] atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) + scalarLWRadCanopy2Ubound => flux_data%var(iLookFLUX%scalarLWRadCanopy2Ubound)%dat(1), & ! intent(out): [dp] longwave radiation emitted from canopy lost thru upper boundary (W m-2) + scalarLWRadCanopy2Ground => flux_data%var(iLookFLUX%scalarLWRadCanopy2Ground)%dat(1), & ! intent(out): [dp] longwave radiation emitted from canopy absorbed by the ground (W m-2) + scalarLWRadCanopy2Canopy => flux_data%var(iLookFLUX%scalarLWRadCanopy2Canopy)%dat(1), & ! intent(out): [dp] canopy longwave reflected from ground and absorbed by the canopy (W m-2) + scalarLWRadGround2Ubound => flux_data%var(iLookFLUX%scalarLWRadGround2Ubound)%dat(1), & ! intent(out): [dp] longwave radiation emitted from ground lost thru upper boundary (W m-2) + scalarLWRadGround2Canopy => flux_data%var(iLookFLUX%scalarLWRadGround2Canopy)%dat(1), & ! intent(out): [dp] longwave radiation emitted from ground and absorbed by the canopy (W m-2) + scalarLWNetCanopy => flux_data%var(iLookFLUX%scalarLWNetCanopy)%dat(1), & ! intent(out): [dp] net longwave radiation at the canopy (W m-2) + scalarLWNetGround => flux_data%var(iLookFLUX%scalarLWNetGround)%dat(1), & ! intent(out): [dp] net longwave radiation at the ground surface (W m-2) + scalarLWNetUbound => flux_data%var(iLookFLUX%scalarLWNetUbound)%dat(1), & ! intent(out): [dp] net longwave radiation at the upper boundary (W m-2) + + ! output: aerodynamic resistance + scalarZ0Canopy => diag_data%var(iLookDIAG%scalarZ0Canopy)%dat(1), & ! intent(out): [dp] roughness length of the canopy (m) + scalarWindReductionFactor => diag_data%var(iLookDIAG%scalarWindReductionFactor)%dat(1), & ! intent(out): [dp] canopy wind reduction factor (-) + scalarZeroPlaneDisplacement => diag_data%var(iLookDIAG%scalarZeroPlaneDisplacement)%dat(1), & ! intent(out): [dp] zero plane displacement (m) + scalarRiBulkCanopy => diag_data%var(iLookDIAG%scalarRiBulkCanopy)%dat(1), & ! intent(out): [dp] bulk Richardson number for the canopy (-) + scalarRiBulkGround => diag_data%var(iLookDIAG%scalarRiBulkGround)%dat(1), & ! intent(out): [dp] bulk Richardson number for the ground surface (-) + scalarEddyDiffusCanopyTop => flux_data%var(iLookFLUX%scalarEddyDiffusCanopyTop)%dat(1), & ! intent(out): [dp] eddy diffusivity for heat at the top of the canopy (m2 s-1) + scalarFrictionVelocity => flux_data%var(iLookFLUX%scalarFrictionVelocity)%dat(1), & ! intent(out): [dp] friction velocity (m s-1) + scalarWindspdCanopyTop => flux_data%var(iLookFLUX%scalarWindspdCanopyTop)%dat(1), & ! intent(out): [dp] windspeed at the top of the canopy (m s-1) + scalarWindspdCanopyBottom => flux_data%var(iLookFLUX%scalarWindspdCanopyBottom)%dat(1), & ! intent(out): [dp] windspeed at the height of the bottom of the canopy (m s-1) + scalarLeafResistance => flux_data%var(iLookFLUX%scalarLeafResistance)%dat(1), & ! intent(out): [dp] mean leaf boundary layer resistance per unit leaf area (s m-1) + scalarGroundResistance => flux_data%var(iLookFLUX%scalarGroundResistance)%dat(1), & ! intent(out): [dp] below canopy aerodynamic resistance (s m-1) + scalarCanopyResistance => flux_data%var(iLookFLUX%scalarCanopyResistance)%dat(1), & ! intent(out): [dp] above canopy aerodynamic resistance (s m-1) + + ! input/output: soil resistance -- intent(in) and intent(inout) because only called at the first flux call + mLayerRootDensity => diag_data%var(iLookDIAG%mLayerRootDensity)%dat, & ! intent(in): [dp] root density in each layer (-) + scalarAquiferRootFrac => diag_data%var(iLookDIAG%scalarAquiferRootFrac)%dat(1), & ! intent(in): [dp] fraction of roots below the lowest soil layer (-) + scalarTranspireLim => diag_data%var(iLookDIAG%scalarTranspireLim)%dat(1), & ! intent(inout): [dp] weighted average of the transpiration limiting factor (-) + mLayerTranspireLim => diag_data%var(iLookDIAG%mLayerTranspireLim)%dat, & ! intent(inout): [dp] transpiration limiting factor in each layer (-) + scalarTranspireLimAqfr => diag_data%var(iLookDIAG%scalarTranspireLimAqfr)%dat(1), & ! intent(inout): [dp] transpiration limiting factor for the aquifer (-) + scalarSoilRelHumidity => diag_data%var(iLookDIAG%scalarSoilRelHumidity)%dat(1), & ! intent(inout): [dp] relative humidity in the soil pores [0-1] + scalarSoilResistance => flux_data%var(iLookFLUX%scalarSoilResistance)%dat(1), & ! intent(inout): [dp] resistance from the soil (s m-1) + + ! input/output: stomatal resistance -- intent(inout) because only called at the first flux call + scalarStomResistSunlit => flux_data%var(iLookFLUX%scalarStomResistSunlit)%dat(1), & ! intent(inout): [dp] stomatal resistance for sunlit leaves (s m-1) + scalarStomResistShaded => flux_data%var(iLookFLUX%scalarStomResistShaded)%dat(1), & ! intent(inout): [dp] stomatal resistance for shaded leaves (s m-1) + scalarPhotosynthesisSunlit => flux_data%var(iLookFLUX%scalarPhotosynthesisSunlit)%dat(1), & ! intent(inout): [dp] sunlit photosynthesis (umolco2 m-2 s-1) + scalarPhotosynthesisShaded => flux_data%var(iLookFLUX%scalarPhotosynthesisShaded)%dat(1), & ! intent(inout): [dp] shaded photosynthesis (umolco2 m-2 s-1) + + ! output: turbulent heat fluxes + scalarLatHeatSubVapCanopy => diag_data%var(iLookDIAG%scalarLatHeatSubVapCanopy)%dat(1), & ! intent(inout): [dp] latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) + scalarLatHeatSubVapGround => diag_data%var(iLookDIAG%scalarLatHeatSubVapGround)%dat(1), & ! intent(inout): [dp] latent heat of sublimation/vaporization for the ground surface (J kg-1) + scalarSatVP_canopyTemp => diag_data%var(iLookDIAG%scalarSatVP_CanopyTemp)%dat(1), & ! intent(out): [dp] saturation vapor pressure at the temperature of the vegetation canopy (Pa) + scalarSatVP_groundTemp => diag_data%var(iLookDIAG%scalarSatVP_GroundTemp)%dat(1), & ! intent(out): [dp] saturation vapor pressure at the temperature of the ground surface (Pa) + scalarSenHeatTotal => flux_data%var(iLookFLUX%scalarSenHeatTotal)%dat(1), & ! intent(out): [dp] sensible heat from the canopy air space to the atmosphere (W m-2) + scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1), & ! intent(out): [dp] sensible heat flux from the canopy to the canopy air space (W m-2) + scalarSenHeatGround => flux_data%var(iLookFLUX%scalarSenHeatGround)%dat(1), & ! intent(out): [dp] sensible heat flux from ground surface below vegetation (W m-2) + scalarLatHeatTotal => flux_data%var(iLookFLUX%scalarLatHeatTotal)%dat(1), & ! intent(out): [dp] latent heat from the canopy air space to the atmosphere (W m-2) + scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1), & ! intent(out): [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + scalarLatHeatCanopyTrans => flux_data%var(iLookFLUX%scalarLatHeatCanopyTrans)%dat(1), & ! intent(out): [dp] latent heat flux for transpiration from the canopy to the canopy air space (W m-2) + scalarLatHeatGround => flux_data%var(iLookFLUX%scalarLatHeatGround)%dat(1), & ! intent(out): [dp] latent heat flux from ground surface below vegetation (W m-2) + + ! output: advective heat fluxes + scalarCanopyAdvectiveHeatFlux => flux_data%var(iLookFLUX%scalarCanopyAdvectiveHeatFlux)%dat(1), & ! intent(out): [dp] heat advected to the canopy surface with rain + snow (W m-2) + scalarGroundAdvectiveHeatFlux => flux_data%var(iLookFLUX%scalarGroundAdvectiveHeatFlux)%dat(1), & ! intent(out): [dp] heat advected to the ground surface with throughfall (W m-2) + + ! output: mass fluxes + scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1), & ! intent(out): [dp] canopy sublimation/frost (kg m-2 s-1) + scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! intent(out): [dp] snow sublimation/frost -- below canopy or non-vegetated (kg m-2 s-1) + + ! input/output: canopy air space variables + scalarVP_CanopyAir => diag_data%var(iLookDIAG%scalarVP_CanopyAir)%dat(1), & ! intent(inout): [dp] vapor pressure of the canopy air space (Pa) + scalarCanopyStabilityCorrection => diag_data%var(iLookDIAG%scalarCanopyStabilityCorrection)%dat(1),& ! intent(inout): [dp] stability correction for the canopy (-) + scalarGroundStabilityCorrection => diag_data%var(iLookDIAG%scalarGroundStabilityCorrection)%dat(1),& ! intent(inout): [dp] stability correction for the ground surface (-) + + ! output: liquid water fluxes + scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1), & ! intent(out): [dp] canopy transpiration (kg m-2 s-1) + scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1), & ! intent(out): [dp] canopy evaporation/condensation (kg m-2 s-1) + scalarGroundEvaporation => flux_data%var(iLookFLUX%scalarGroundEvaporation)%dat(1), & ! intent(out): [dp] ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + + ! output: derived fluxes + scalarTotalET => flux_data%var(iLookFLUX%scalarTotalET)%dat(1), & ! intent(out): [dp] total ET (kg m-2 s-1) + scalarNetRadiation => flux_data%var(iLookFLUX%scalarNetRadiation)%dat(1) & ! intent(out): [dp] net radiation (W m-2) + ) + ! --------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="vegNrgFlux/" + + ! initialize printflag + printflag = .false. + + ! identify the type of boundary condition for thermodynamics + select case(ix_bcUpprTdyn) + + ! ***** + ! (1) DIRICHLET OR ZERO FLUX BOUNDARY CONDITION... + ! ************************************************ + + ! NOTE: Vegetation fluxes are not computed in this case + + ! ** prescribed temperature or zero flux at the upper boundary of the snow-soil system + case(prescribedTemp,zeroFlux) + + ! derived fluxes + scalarTotalET = 0._rkind ! total ET (kg m-2 s-1) + scalarNetRadiation = 0._rkind ! net radiation (W m-2) + ! liquid water fluxes associated with evaporation/transpiration + scalarCanopyTranspiration = 0._rkind ! canopy transpiration (kg m-2 s-1) + scalarCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) + scalarGroundEvaporation = 0._rkind ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + ! solid water fluxes associated with sublimation/frost + scalarCanopySublimation = 0._rkind ! sublimation from the vegetation canopy ((kg m-2 s-1) + scalarSnowSublimation = 0._rkind ! sublimation from the snow surface ((kg m-2 s-1) + ! set canopy fluxes to zero (no canopy) + canairNetFlux = 0._rkind ! net energy flux for the canopy air space (W m-2) + canopyNetFlux = 0._rkind ! net energy flux for the vegetation canopy (W m-2) + ! set canopy derivatives to zero + dCanairNetFlux_dCanairTemp = 0._rkind ! derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) + dCanairNetFlux_dCanopyTemp = 0._rkind ! derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) + dCanairNetFlux_dGroundTemp = 0._rkind ! derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) + dCanopyNetFlux_dCanairTemp = 0._rkind ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) + dCanopyNetFlux_dCanopyTemp = 0._rkind ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) + dCanopyNetFlux_dGroundTemp = 0._rkind ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) + dGroundNetFlux_dCanairTemp = 0._rkind ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) + dGroundNetFlux_dCanopyTemp = 0._rkind ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) + ! set liquid flux derivatives to zero (canopy evap) + dCanopyEvaporation_dCanWat = 0._rkind ! derivative in canopy evaporation w.r.t. canopy total water content (s-1) + dCanopyEvaporation_dTCanair= 0._rkind ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTCanopy= 0._rkind ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTGround= 0._rkind ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + ! set liquid flux derivatives to zero (ground evap) + dGroundEvaporation_dCanWat = 0._rkind ! derivative in ground evaporation w.r.t. canopy total water content (s-1) + dGroundEvaporation_dTCanair= 0._rkind ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTCanopy= 0._rkind ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTGround= 0._rkind ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + ! set transpiration derivatives to zero + dCanopyTrans_dCanWat = 0._rkind ! derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair= 0._rkind ! derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy= 0._rkind ! derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround= 0._rkind ! derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + + ! compute fluxes and derivatives -- separate approach for prescribed temperature and zero flux, + if(ix_bcUpprTdyn == prescribedTemp)then + ! compute ground net flux (W m-2) + groundNetFlux = -diag_data%var(iLookDIAG%iLayerThermalC)%dat(0)*(groundTempTrial - upperBoundTemp)/(prog_data%var(iLookPROG%mLayerDepth)%dat(1)*0.5_rkind) + ! compute derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) inside soil and snow (ssd) energy flux routine + ! dGroundNetFlux_dGroundTemp = missingValue + elseif(ix_bcUpprTdyn == zeroFlux)then + groundNetFlux = 0._rkind + ! dGroundNetFlux_dGroundTemp = missingValue + else + err=20; message=trim(message)//'unable to identify upper boundary condition for thermodynamics: expect the case to be prescribedTemp or zeroFlux'; return + end if + + ! ***** + ! (2) NEUMANN BOUNDARY CONDITION... + ! ********************************* + + ! NOTE 1: This is the main routine for calculating vegetation fluxes + ! NOTE 2: This routine also calculates surface fluxes for the case where vegetation is buried with snow (or bare soil) + + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + ! ***** PRELIMINARIES ********************************************************************************************************************************************** + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + + ! * flux boundary condition + case(energyFlux) + + ! identify the appropriate groundwater variable + select case(ix_spatial_gw) + case(singleBasin); scalarAquiferStorage = basinAquiferStorage + case(localColumn); scalarAquiferStorage = localAquiferStorage + case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return + end select ! (modify the groundwater representation for this single-column implementation) + + ! set canopy stability corrections to the previous values + scalarCanopyStabilityCorrection_old = scalarCanopyStabilityCorrection ! stability correction for the canopy (-) + scalarGroundStabilityCorrection_old = scalarGroundStabilityCorrection ! stability correction for the ground surface (-) + + ! initialize variables to compute stomatal resistance + if(firstFluxCall .and. firstSubStep)then + ! vapor pressure in the canopy air space initialized as vapor pressure of air above the vegetation canopy + ! NOTE: this is needed for the stomatal resistance calculations + if(scalarVP_CanopyAir < 0._rkind)then + scalarVP_CanopyAir = scalarVPair - 1._rkind ! "small" offset used to assist in checking initial derivative calculations + end if + end if + + ! set latent heat of sublimation/vaporization for canopy and ground surface (Pa/K) + ! NOTE: variables are constant over the substep, to simplify relating energy and mass fluxes + if(firstFluxCall)then + scalarLatHeatSubVapCanopy = getLatentHeatValue(canopyTempTrial) + ! case when there is snow on the ground (EXCLUDE "snow without a layer" -- in this case, evaporate from the soil) + if(nSnow > 0)then + if(groundTempTrial > Tfreeze)then; err=20; message=trim(message)//'do not expect ground temperature > 0 when snow is on the ground'; return; end if + scalarLatHeatSubVapGround = LH_sub ! sublimation from snow + scalarGroundSnowFraction = 1._rkind + ! case when the ground is snow-free + else + scalarLatHeatSubVapGround = LH_vap ! evaporation of water in the soil pores: this occurs even if frozen because of super-cooled water + scalarGroundSnowFraction = 0._rkind + end if ! (if there is snow on the ground) + end if ! (if the first flux call) + + ! compute the roughness length of the ground (ground below the canopy or non-vegetated surface) + z0Ground = z0soil*(1._rkind - scalarGroundSnowFraction) + z0Snow*scalarGroundSnowFraction ! roughness length (m) + + ! compute the total vegetation area index (leaf plus stem) + VAI = scalarLAI + scalarSAI ! vegetation area index + exposedVAI = scalarExposedLAI + scalarExposedSAI ! exposed vegetation area index + + ! compute emissivity of the canopy (-) + if(computeVegFlux)then + select case(ix_canopyEmis) + ! *** simple exponential function + case(simplExp) + scalarCanopyEmissivity = 1._rkind - exp(-exposedVAI) ! effective emissivity of the canopy (-) + ! *** canopy emissivity parameterized as a function of diffuse transmissivity + case(difTrans) + ! compute the exponential integral + scaleLAI = 0.5_rkind*exposedVAI + expi = expInt(scaleLAI) + ! compute diffuse transmissivity (-) + diffuseTrans = (1._rkind - scaleLAI)*exp(-scaleLAI) + (scaleLAI**2._rkind)*expi + ! compute the canopy emissivity + scalarCanopyEmissivity = (1._rkind - diffuseTrans)*vegEmissivity + ! *** check we found the correct option + case default + err=20; message=trim(message)//'unable to identify option for canopy emissivity'; return + end select + end if + + ! ensure canopy longwave fluxes are zero when not computing canopy fluxes + if(.not.computeVegFlux) scalarCanopyEmissivity=0._rkind + + ! compute emissivity of the ground surface (-) + groundEmissivity = scalarGroundSnowFraction*snowEmissivity + (1._rkind - scalarGroundSnowFraction)*soilEmissivity ! emissivity of the ground surface (-) + + ! compute the fraction of canopy that is wet + ! NOTE: we either sublimate or evaporate over the entire substep + if(computeVegFlux)then + + ! compute the fraction of liquid water in the canopy (-) + totalCanopyWater = canopyLiqTrial + canopyIceTrial + if(totalCanopyWater > tiny(1.0_rkind))then + racLiquidCanopy = canopyLiqTrial / (canopyLiqTrial + canopyIceTrial) + else + fracLiquidCanopy = 0._rkind + end if + + ! get wetted fraction and derivatives + call wettedFrac(& + ! input + .true., & ! flag to denote if derivative is desired + (ixDerivMethod == numerical), & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) + (scalarLatHeatSubVapCanopy > LH_vap+verySmall), & ! flag to denote if the canopy is frozen + dCanLiq_dTcanopy, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) + fracLiquidCanopy, & ! fraction of liquid water on the canopy (-) + canopyLiqTrial, & ! canopy liquid water (kg m-2) + canopyIceTrial, & ! canopy ice (kg m-2) + scalarCanopyLiqMax, & ! maximum canopy liquid water (kg m-2) + scalarCanopyIceMax, & ! maximum canopy ice content (kg m-2) + canopyWettingFactor, & ! maximum wetted fraction of the canopy (-) + canopyWettingExp, & ! exponent in canopy wetting function (-) + ! output + scalarCanopyWetFraction, & ! canopy wetted fraction (-) + dCanopyWetFraction_dWat, & ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) + dCanopyWetFraction_dT, & ! derivative in wetted fraction w.r.t. canopy temperature (K-1) + err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + else + scalarCanopyWetFraction = 0._rkind ! canopy wetted fraction (-) + dCanopyWetFraction_dWat = 0._rkind ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) + dCanopyWetFraction_dT = 0._rkind ! derivative in wetted fraction w.r.t. canopy temperature (K-1) + end if + + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + ! ***** AERODYNAMIC RESISTANCE ***************************************************************************************************************************************** + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + + ! NOTE: compute for all iterations + + ! compute aerodynamic resistances + ! Refs: Choudhury and Monteith (4-layer model for heat budget of homogenous surfaces; QJRMS, 1988) + ! Niu and Yang (Canopy effects on snow processes; JGR, 2004) + ! Mahat et al. (Below-canopy turbulence in a snowmelt model, WRR, 2012) + call aeroResist(& + ! input: model control + computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) + (ixDerivMethod == analytical), & ! intent(in): logical flag if would like to compute analytical derivaties + ix_veg_traits, & ! intent(in): choice of parameterization for vegetation roughness length and displacement height + ix_windPrfile, & ! intent(in): choice of canopy wind profile + ix_astability, & ! intent(in): choice of stability function + ! input: above-canopy forcing data + mHeight, & ! intent(in): measurement height (m) + airtemp, & ! intent(in): air temperature at some height above the surface (K) + windspd, & ! intent(in): wind speed at some height above the surface (m s-1) + ! input: canopy and ground temperature + canairTempTrial, & ! intent(in): temperature of the canopy air space (K) + groundTempTrial, & ! intent(in): temperature of the ground surface (K) + ! input: diagnostic variables + exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) + scalarSnowDepth, & ! intent(in): snow depth (m) + ! input: parameters + z0Ground, & ! intent(in): roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) + z0CanopyParam, & ! intent(in): roughness length of the canopy (m) + zpdFraction, & ! intent(in): zero plane displacement / canopy height (-) + critRichNumber, & ! intent(in): critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam, & ! intent(in): parameter in Louis (1979) stability function + Mahrt87_eScale, & ! intent(in): exponential scaling factor in the Mahrt (1987) stability function + windReductionParam, & ! intent(in): canopy wind reduction parameter (-) + leafExchangeCoeff, & ! intent(in): turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) + leafDimension, & ! intent(in): characteristic leaf dimension (m) + heightCanopyTop, & ! intent(in): height at the top of the vegetation canopy (m) + heightCanopyBottom, & ! intent(in): height at the bottom of the vegetation canopy (m) + ! output: stability corrections + scalarRiBulkCanopy, & ! intent(out): bulk Richardson number for the canopy (-) + scalarRiBulkGround, & ! intent(out): bulk Richardson number for the ground surface (-) + scalarCanopyStabilityCorrection, & ! intent(out): stability correction for the canopy (-) + scalarGroundStabilityCorrection, & ! intent(out): stability correction for the ground surface (-) + ! output: scalar resistances + scalarZ0Canopy, & ! intent(out): roughness length of the canopy (m) + scalarWindReductionFactor, & ! intent(out): canopy wind reduction factor (-) + scalarZeroPlaneDisplacement, & ! intent(out): zero plane displacement (m) + scalarEddyDiffusCanopyTop, & ! intent(out): eddy diffusivity for heat at the top of the canopy (m2 s-1) + scalarFrictionVelocity, & ! intent(out): friction velocity (m s-1) + scalarWindspdCanopyTop, & ! intent(out): windspeed at the top of the canopy (m s-1) + scalarWindspdCanopyBottom, & ! intent(out): windspeed at the height of the bottom of the canopy (m s-1) + scalarLeafResistance, & ! intent(out): mean leaf boundary layer resistance per unit leaf area (s m-1) + scalarGroundResistance, & ! intent(out): below canopy aerodynamic resistance (s m-1) + scalarCanopyResistance, & ! intent(out): above canopy aerodynamic resistance (s m-1) + ! output: derivatives in scalar resistances + dGroundResistance_dTGround, & ! intent(out): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + dGroundResistance_dTCanopy, & ! intent(out): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + dGroundResistance_dTCanair, & ! intent(out): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + dCanopyResistance_dTCanopy, & ! intent(out): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + dCanopyResistance_dTCanair, & ! intent(out): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + ! output: error control + err,cmessage ) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + ! ***** STOMATAL RESISTANCE ***************************************************************************************************************************************** + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + + ! stomatal resistance is constant over the SUBSTEP + ! NOTE: This is a simplification, as stomatal resistance does depend on canopy temperature + ! This "short-cut" made because: + ! (1) computations are expensive; + ! (2) derivative calculations are rather complex (iterations within the Ball-Berry routine); and + ! (3) stomatal resistance does not change rapidly + if(firstFluxCall)then + + ! compute the saturation vapor pressure for vegetation temperature + TV_celcius = canopyTempTrial - Tfreeze + call satVapPress(TV_celcius, scalarSatVP_CanopyTemp, dSVPCanopy_dCanopyTemp) + + ! compute soil moisture factor controlling stomatal resistance + call soilResist(& + ! input (model decisions) + ix_soilStress, & ! intent(in): choice of function for the soil moisture control on stomatal resistance + ix_groundwatr, & ! intent(in): groundwater parameterization + ! input (state variables) + mLayerMatricHead(1:nSoil), & ! intent(in): matric head in each soil layer (m) + mLayerVolFracLiq(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water in each soil layer (-) + scalarAquiferStorage, & ! intent(in): aquifer storage (m) + ! input (diagnostic variables) + mLayerRootDensity(1:nSoil), & ! intent(in): root density in each layer (-) + scalarAquiferRootFrac, & ! intent(in): fraction of roots below the lowest soil layer (-) + ! input (parameters) + plantWiltPsi, & ! intent(in): matric head at wilting point (m) + soilStressParam, & ! intent(in): parameter in the exponential soil stress function (-) + critSoilWilting, & ! intent(in): critical vol. liq. water content when plants are wilting (-) + critSoilTranspire, & ! intent(in): critical vol. liq. water content when transpiration is limited (-) + critAquiferTranspire, & ! intent(in): critical aquifer storage value when transpiration is limited (m) + ! output + scalarTranspireLim, & ! intent(out): weighted average of the transpiration limiting factor (-) + mLayerTranspireLim(1:nSoil), & ! intent(out): transpiration limiting factor in each layer (-) + scalarTranspireLimAqfr, & ! intent(out): transpiration limiting factor for the aquifer (-) + err,cmessage ) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + ! compute stomatal resistance + call stomResist(& + ! input (state and diagnostic variables) + canopyTempTrial, & ! intent(in): temperature of the vegetation canopy (K) + scalarSatVP_CanopyTemp, & ! intent(in): saturation vapor pressure at the temperature of the veg canopy (Pa) + scalarVP_CanopyAir, & ! intent(in): canopy air vapor pressure (Pa) + ! input: data structures + type_data, & ! intent(in): type of vegetation and soil + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + model_decisions, & ! intent(in): model decisions + ! input-output: data structures + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output: error control + err,cmessage ) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + end if ! (if the first flux call in a given sub-step) + + + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + ! ***** LONGWAVE RADIATION ***************************************************************************************************************************************** + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + + ! compute canopy longwave radiation balance + call longwaveBal(& + ! input: model control + ixDerivMethod, & ! intent(in): method used to calculate flux derivatives + computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation + insideIDA, & ! intent(in): flag to indicate inside Sundials Solver (do not require longwave to be balanced) + ! input: canopy and ground temperature + canopyTempTrial, & ! intent(in): temperature of the vegetation canopy (K) + groundTempTrial, & ! intent(in): temperature of the ground surface (K) + ! input: canopy and ground emissivity + scalarCanopyEmissivity, & ! intent(in): canopy emissivity (-) + groundEmissivity, & ! intent(in): ground emissivity (-) + ! input: forcing + LWRadAtm, & ! intent(in): downwelling longwave radiation at the upper boundary (W m-2) + ! output: emitted radiation from the canopy and ground + scalarLWRadCanopy, & ! intent(out): longwave radiation emitted from the canopy (W m-2) + scalarLWRadGround, & ! intent(out): longwave radiation emitted at the ground surface (W m-2) + ! output: individual fluxes + scalarLWRadUbound2Canopy, & ! intent(out): downward atmospheric longwave radiation absorbed by the canopy (W m-2) + scalarLWRadUbound2Ground, & ! intent(out): downward atmospheric longwave radiation absorbed by the ground (W m-2) + scalarLWRadUbound2Ubound, & ! intent(out): atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) + scalarLWRadCanopy2Ubound, & ! intent(out): longwave radiation emitted from canopy lost thru upper boundary (W m-2) + scalarLWRadCanopy2Ground, & ! intent(out): longwave radiation emitted from canopy absorbed by the ground (W m-2) + scalarLWRadCanopy2Canopy, & ! intent(out): canopy longwave reflected from ground and absorbed by the canopy (W m-2) + scalarLWRadGround2Ubound, & ! intent(out): longwave radiation emitted from ground lost thru upper boundary (W m-2) + scalarLWRadGround2Canopy, & ! intent(out): longwave radiation emitted from ground and absorbed by the canopy (W m-2) + ! output: net fluxes + scalarLWNetCanopy, & ! intent(out): net longwave radiation at the canopy (W m-2) + scalarLWNetGround, & ! intent(out): net longwave radiation at the ground surface (W m-2) + scalarLWNetUbound, & ! intent(out): net longwave radiation at the upper boundary (W m-2) + ! output: flux derivatives + dLWNetCanopy_dTCanopy, & ! intent(out): derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) + dLWNetGround_dTGround, & ! intent(out): derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) + dLWNetCanopy_dTGround, & ! intent(out): derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) + dLWNetGround_dTCanopy, & ! intent(out): derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) + ! output: error control + err,cmessage ) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + ! ***** TURBULENT HEAT FLUXES ************************************************************************************************************************************** + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + + ! check the need to compute numerical derivatives + if(ixDerivMethod == numerical)then + nFlux=5 ! compute the derivatives using one-sided finite differences + else + nFlux=1 ! compute analytical derivatives + end if + + ! either one or multiple flux calls, depending on if using analytical or numerical derivatives + do itry=nFlux,1,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) + + ! ------------------------------------------------------------------------------------- + ! state perturbations for numerical deriavtives with one-sided finite differences + ! note: no perturbations performed using analytical derivatives (nFlux=1) + ! ------------------------------------------------------------------------------------- + + ! initialize for unperturbed state + canopyWetFraction = scalarCanopyWetFraction + + ! identify the type of perturbation + select case(itry) + + ! un-perturbed case + case(unperturbed) + groundTemp = groundTempTrial + canopyTemp = canopyTempTrial + canairTemp = canairTempTrial + canopyWat = totalCanopyWater + + ! perturb ground temperature + case(perturbStateGround) + groundTemp = groundTempTrial + dx + canopyTemp = canopyTempTrial + canairTemp = canairTempTrial + canopyWat = totalCanopyWater + + ! perturb canopy temperature + case(perturbStateCanopy) + groundTemp = groundTempTrial + canopyTemp = canopyTempTrial + dx + canairTemp = canairTempTrial + canopyWat = totalCanopyWater + + ! perturb canopy air temperature + case(perturbStateCanair) + groundTemp = groundTempTrial + canopyTemp = canopyTempTrial + canairTemp = canairTempTrial + dx + canopyWat = totalCanopyWater + + ! perturb canopy total water content + case(perturbStateCanWat) + groundTemp = groundTempTrial + canopyTemp = canopyTempTrial + canairTemp = canairTempTrial + canopyWat = totalCanopyWater + dx + + ! check for an unknown perturbation + case default; err=10; message=trim(message)//"unknown perturbation"; return + + end select ! (type of perturbation) + + ! perturbations in canopy total water content affect canopy wetted fraction + if(itry == perturbStateCanopy .OR. itry == perturbStateCanWat)then + + ! recalculate liquid and ice from total water + fracLiquidCanopy = fracliquid(canopyTemp,snowfrz_scale) + canopyLiq = fracLiquidCanopy*canopyWat + canopyIce = (1._rkind - fracLiquidCanopy)*canopyWat + + if(computeVegFlux)then + call wettedFrac(& + ! input + .false., & ! flag to denote if derivative is desired + (ixDerivMethod == numerical), & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) + (scalarLatHeatSubVapCanopy > LH_vap+verySmall), & ! flag to denote if the canopy is frozen + dCanLiq_dTcanopy, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) + fracLiquidCanopy, & ! fraction of liquid water on the canopy (-) + canopyLiq, & ! canopy liquid water (kg m-2) + canopyIce, & ! canopy ice (kg m-2) + scalarCanopyLiqMax, & ! maximum canopy liquid water (kg m-2) + scalarCanopyIceMax, & ! maximum canopy ice content (kg m-2) + canopyWettingFactor, & ! maximum wetted fraction of the canopy (-) + canopyWettingExp, & ! exponent in canopy wetting function (-) + ! output + canopyWetFraction, & ! canopy wetted fraction (-) + dCanopyWetFraction_dWat, & ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) + dCanopyWetFraction_dT, & ! derivative in wetted fraction w.r.t. canopy temperature (K-1) + err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + else + canopyWetFraction = 0._rkind + end if ! (desired computing vegetation flux) + + end if ! (re-computing wetted fraction for perturbed cases) + + ! compute the saturation vapor pressure for vegetation temperature + ! NOTE: saturated vapor pressure derivatives don't seem that accurate.... + TV_celcius = canopyTemp - Tfreeze + call satVapPress(TV_celcius, scalarSatVP_CanopyTemp, dSVPCanopy_dCanopyTemp) + + ! compute the saturation vapor pressure for ground temperature + ! NOTE: saturated vapor pressure derivatives don't seem that accurate.... + TG_celcius = groundTemp - Tfreeze + call satVapPress(TG_celcius, scalarSatVP_GroundTemp, dSVPGround_dGroundTemp) + + ! ------------------------------------------------------------------------------------- + ! calculation block (unperturbed fluxes returned [computed last]) + ! ------------------------------------------------------------------------------------- + + ! re-compute aerodynamic resistances for perturbed cases + ! NOTE: unperturbed fluxes computed earlier, and not over-written + if(itry /= unperturbed)then + call aeroResist(& + ! input: model control + computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) + .false., & ! intent(in): logical flag if would like to compute analytical derivaties + ix_veg_traits, & ! intent(in): choice of parameterization for vegetation roughness length and displacement height + ix_windPrfile, & ! intent(in): choice of canopy wind profile + ix_astability, & ! intent(in): choice of stability function + ! input: above-canopy forcing data + mHeight, & ! intent(in): measurement height (m) + airtemp, & ! intent(in): air temperature at some height above the surface (K) + windspd, & ! intent(in): wind speed at some height above the surface (m s-1) + ! input: temperature (canopy, ground, canopy air space) + canairTemp, & ! intent(in): temperature of the canopy air space (K) + groundTemp, & ! intent(in): ground temperature (K) + ! input: diagnostic variables + exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) + scalarSnowDepth, & ! intent(in): snow depth (m) + ! input: parameters + z0Ground, & ! intent(in): roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) + z0CanopyParam, & ! intent(in): roughness length of the canopy (m) + zpdFraction, & ! intent(in): zero plane displacement / canopy height (-) + critRichNumber, & ! intent(in): critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam, & ! intent(in): parameter in Louis (1979) stability function + Mahrt87_eScale, & ! intent(in): exponential scaling factor in the Mahrt (1987) stability function + windReductionParam, & ! intent(in): canopy wind reduction parameter (-) + leafExchangeCoeff, & ! intent(in): turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) + leafDimension, & ! intent(in): characteristic leaf dimension (m) + heightCanopyTop, & ! intent(in): height at the top of the vegetation canopy (m) + heightCanopyBottom, & ! intent(in): height at the bottom of the vegetation canopy (m) + ! output: stability corrections + notUsed_RiBulkCanopy, & ! intent(out): bulk Richardson number for the canopy (-) + notUsed_RiBulkGround, & ! intent(out): bulk Richardson number for the ground surface (-) + notUsed_scalarCanopyStabilityCorrection, & ! intent(out): stability correction for the canopy (-) + notUsed_scalarGroundStabilityCorrection, & ! intent(out): stability correction for the ground surface (-) + ! output: scalar resistances + notUsed_z0Canopy, & ! intent(out): roughness length of the canopy (m) + notUsed_WindReductionFactor, & ! intent(out): canopy wind reduction factor (-) + notUsed_ZeroPlaneDisplacement, & ! intent(out): zero plane displacement (m) + notUsed_EddyDiffusCanopyTop, & ! intent(out): eddy diffusivity for heat at the top of the canopy (m2 s-1) + notUsed_FrictionVelocity, & ! intent(out): friction velocity (m s-1) + notUsed_WindspdCanopyTop, & ! intent(out): windspeed at the top of the canopy (m s-1) + notUsed_WindspdCanopyBottom, & ! intent(out): windspeed at the height of the bottom of the canopy (m s-1) + trialLeafResistance, & ! intent(out): mean leaf boundary layer resistance per unit leaf area (s m-1) + trialGroundResistance, & ! intent(out): below canopy aerodynamic resistance (s m-1) + trialCanopyResistance, & ! intent(out): above canopy aerodynamic resistance (s m-1) + ! output: derivatives in scalar resistances + notUsed_dGroundResistance_dTGround, & ! intent(out): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + notUsed_dGroundResistance_dTCanopy, & ! intent(out): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + notUsed_dGroundResistance_dTCanair, & ! intent(out): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + notUsed_dCanopyResistance_dTCanopy, & ! intent(out): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + notUsed_dCanopyResistance_dTCanair, & ! intent(out): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + ! output: error control + err,cmessage ) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + + ! assign scalar resistances for un-perturbed cases + else + trialLeafResistance = scalarLeafResistance + trialGroundResistance = scalarGroundResistance + trialCanopyResistance = scalarCanopyResistance + + end if ! (re-computing resistances for perturbed cases) + + + ! compute the relative humidity in the top soil layer and the resistance at the ground surface + ! NOTE: computations are based on start-of-step values, so only compute for the first flux call + if(firstFluxCall)then + ! (soil water evaporation factor [0-1]) + soilEvapFactor = mLayerVolFracLiq(nSnow+1)/(theta_sat - theta_res) + ! (resistance from the soil [s m-1]) + scalarSoilResistance = scalarGroundSnowFraction*1._rkind + (1._rkind - scalarGroundSnowFraction)*EXP(8.25_rkind - 4.225_rkind*soilEvapFactor) ! Sellers (1992) + !scalarSoilResistance = scalarGroundSnowFraction*0._rkind + (1._rkind - scalarGroundSnowFraction)*exp(8.25_rkind - 6.0_rkind*soilEvapFactor) ! Niu adjustment to decrease resitance for wet soil + ! (relative humidity in the soil pores [0-1]) + if(mLayerMatricHead(1) > -1.e+6_rkind)then ! avoid problems with numerical precision when soil is very dry + soilRelHumidity_noSnow = exp( (mLayerMatricHead(1)*gravity) / (groundTemp*R_wv) ) + else + soilRelHumidity_noSnow = 0._rkind + end if ! (if matric head is very low) + scalarSoilRelHumidity = scalarGroundSnowFraction*1._rkind + (1._rkind - scalarGroundSnowFraction)*soilRelHumidity_noSnow + end if ! (if the first flux call) + + ! compute turbulent heat fluxes + call turbFluxes(& + ! input: model control + computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) + ixDerivMethod, & ! intent(in): method used to calculate flux derivatives + ! input: above-canopy forcing data + airtemp, & ! intent(in): air temperature at some height above the surface (K) + airpres, & ! intent(in): air pressure of the air above the vegetation canopy (Pa) + scalarVPair, & ! intent(in): vapor pressure of the air above the vegetation canopy (Pa) + ! input: latent heat of sublimation/vaporization + scalarLatHeatSubVapCanopy, & ! intent(in): latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) + scalarLatHeatSubVapGround, & ! intent(in): latent heat of sublimation/vaporization for the ground surface (J kg-1) + ! input: canopy/ground temperature and saturated vapor pressure + canairTemp, & ! intent(in): temperature of the canopy air space (K) + canopyTemp, & ! intent(in): canopy temperature (K) + groundTemp, & ! intent(in): ground temperature (K) + scalarSatVP_CanopyTemp, & ! intent(in): saturation vapor pressure at the temperature of the veg canopy (Pa) + scalarSatVP_GroundTemp, & ! intent(in): saturation vapor pressure at the temperature of the ground (Pa) + dSVPCanopy_dCanopyTemp, & ! intent(in): derivative in canopy saturation vapor pressure w.r.t. canopy temperature (Pa K-1) + dSVPGround_dGroundTemp, & ! intent(in): derivative in ground saturation vapor pressure w.r.t. ground temperature (Pa K-1) + ! input: diagnostic variables + exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) + canopyWetFraction, & ! intent(in): trial value for the fraction of canopy that is wet [0-1] + dCanopyWetFraction_dWat, & ! intent(in): derivative in the canopy wetted fraction w.r.t. total water content (kg-1 m-2) + dCanopyWetFraction_dT, & ! intent(in): derivative in wetted fraction w.r.t. canopy temperature (K-1) + scalarCanopySunlitLAI, & ! intent(in): sunlit leaf area (-) + scalarCanopyShadedLAI, & ! intent(in): shaded leaf area (-) + scalarSoilRelHumidity, & ! intent(in): relative humidity in the soil pores [0-1] + scalarSoilResistance, & ! intent(in): resistance from the soil (s m-1) + trialLeafResistance, & ! intent(in): mean leaf boundary layer resistance per unit leaf area (s m-1) + trialGroundResistance, & ! intent(in): below canopy aerodynamic resistance (s m-1) + trialCanopyResistance, & ! intent(in): above canopy aerodynamic resistance (s m-1) + scalarStomResistSunlit, & ! intent(in): stomatal resistance for sunlit leaves (s m-1) + scalarStomResistShaded, & ! intent(in): stomatal resistance for shaded leaves (s m-1) + ! input: derivatives in scalar resistances + dGroundResistance_dTGround, & ! intent(in): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + dGroundResistance_dTCanopy, & ! intent(in): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + dGroundResistance_dTCanair, & ! intent(in): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + dCanopyResistance_dTCanopy, & ! intent(in): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + dCanopyResistance_dTCanair, & ! intent(in): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + ! output: conductances (used to check derivative calculations) + scalarLeafConductance, & ! intent(out): leaf conductance (m s-1) + scalarCanopyConductance, & ! intent(out): canopy conductance (m s-1) + scalarGroundConductanceSH, & ! intent(out): ground conductance for sensible heat (m s-1) + scalarGroundConductanceLH, & ! intent(out): ground conductance for latent heat -- includes soil resistance (m s-1) + scalarEvapConductance, & ! intent(out): conductance for evaporation (m s-1) + scalarTransConductance, & ! intent(out): conductance for transpiration (m s-1) + scalarTotalConductanceSH, & ! intent(out): total conductance for sensible heat (m s-1) + scalarTotalConductanceLH, & ! intent(out): total conductance for latent heat (m s-1) + ! output: canopy air space variables + scalarVP_CanopyAir, & ! intent(out): vapor pressure of the canopy air space (Pa) + ! output: fluxes from the vegetation canopy + scalarSenHeatCanopy, & ! intent(out): sensible heat flux from the canopy to the canopy air space (W m-2) + scalarLatHeatCanopyEvap, & ! intent(out): latent heat flux associated with evaporation from the canopy to the canopy air space (W m-2) + scalarLatHeatCanopyTrans, & ! intent(out): latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) + ! output: fluxes from non-vegetated surfaces (ground surface below vegetation, bare ground, or snow covered vegetation) + scalarSenHeatGround, & ! intent(out): sensible heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) + scalarLatHeatGround, & ! intent(out): latent heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) + ! output: total heat fluxes to the atmosphere + scalarSenHeatTotal, & ! intent(out): total sensible heat flux to the atmosphere (W m-2) + scalarLatHeatTotal, & ! intent(out): total latent heat flux to the atmosphere (W m-2) + ! output: net fluxes + turbFluxCanair, & ! intent(out): net turbulent heat fluxes at the canopy air space (W m-2) + turbFluxCanopy, & ! intent(out): net turbulent heat fluxes at the canopy (W m-2) + turbFluxGround, & ! intent(out): net turbulent heat fluxes at the ground surface (W m-2) + ! output: energy flux derivatives + dTurbFluxCanair_dTCanair, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxCanair_dTCanopy, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxCanair_dTGround, & ! intent(out): derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanair, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanopy, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxCanopy_dTGround, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxGround_dTCanair, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxGround_dTCanopy, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxGround_dTGround, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + ! output: liquid flux derivatives (canopy evap) + dLatHeatCanopyEvap_dCanWat, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy total water content (W kg-1) + dLatHeatCanopyEvap_dTCanair, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) + dLatHeatCanopyEvap_dTCanopy, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) + dLatHeatCanopyEvap_dTGround, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) + ! output: liquid flux derivatives (ground evap) + dLatHeatGroundEvap_dCanWat, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) + dLatHeatGroundEvap_dTCanair, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy air temperature + dLatHeatGroundEvap_dTCanopy, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy temperature + dLatHeatGroundEvap_dTGround, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. ground temperature + ! output: latent heat flux derivatives (canopy trans) + dLatHeatCanopyTrans_dCanWat, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy total water (J kg-1 s-1) + dLatHeatCanopyTrans_dTCanair, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy air temperature + dLatHeatCanopyTrans_dTCanopy, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy temperature + dLatHeatCanopyTrans_dTGround, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. ground temperature + ! output: cross derivatives + dTurbFluxCanair_dCanWat, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxCanopy_dCanWat, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxGround_dCanWat, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + ! output: error control + err,cmessage ) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + + !notUsed_scalarCanopyStabilityCorrection ! stability correction for the canopy (-) + !notUsed_scalarGroundStabilityCorrection ! stability correction for the ground surface (-) + !notUsed_EddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) + !notUsed_FrictionVelocity ! friction velocity (m s-1) + !notUsed_WindspdCanopyTop ! windspeed at the top of the canopy (m s-1) + !notUsed_WindspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) + !trialLeafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) + !trialGroundResistance ! below canopy aerodynamic resistance (s m-1) + !trialCanopyResistance ! above canopy aerodynamic resistance (s m-1) + + ! save perturbed fluxes + if(ixDerivMethod == numerical)then + select case(itry) ! (select type of perturbation) + case(unperturbed) + try0 = turbFluxGround + exit + case(perturbStateCanair) + turbFluxCanair_dStateCanair = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) + turbFluxCanopy_dStateCanair = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) + turbFluxGround_dStateCanair = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) + latHeatCanEvap_dStateCanair = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) + case(perturbStateCanopy) + turbFluxCanair_dStateCanopy = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) + turbFluxCanopy_dStateCanopy = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) + turbFluxGround_dStateCanopy = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) + latHeatCanEvap_dStateCanopy = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) + case(perturbStateGround) + try1 = turbFluxGround + turbFluxCanair_dStateGround = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) + turbFluxCanopy_dStateGround = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) + turbFluxGround_dStateGround = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) + latHeatCanEvap_dStateGround = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) + case(perturbStateCanWat) + turbFluxCanair_dStateCanWat = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) + turbFluxCanopy_dStateCanWat = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) + turbFluxGround_dStateCanWat = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) + latHeatCanEvap_dStateCanWat = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) + case default; err=10; message=trim(message)//"unknown perturbation"; return + end select ! (type of perturbation) + end if ! (if numerical) + + end do ! (looping through different flux perturbations) + + + ! compute numerical derivatives + if(ixDerivMethod == numerical)then + ! derivatives w.r.t. canopy air temperature + dTurbFluxCanair_dTCanair = (turbFluxCanair_dStateCanair - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanair = (turbFluxCanopy_dStateCanair - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxGround_dTCanair = (turbFluxGround_dStateCanair - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dLatHeatCanopyEvap_dTCanair = (latHeatCanEvap_dStateCanair - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) + ! derivatives w.r.t. canopy temperature + dTurbFluxCanair_dTCanopy = (turbFluxCanair_dStateCanopy - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanopy = (turbFluxCanopy_dStateCanopy - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxGround_dTCanopy = (turbFluxGround_dStateCanopy - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dLatHeatCanopyEvap_dTCanopy = (latHeatCanEvap_dStateCanopy - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) + ! derivatives w.r.t. ground temperature + dTurbFluxCanair_dTGround = (turbFluxCanair_dStateGround - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxCanopy_dTGround = (turbFluxCanopy_dStateGround - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxGround_dTGround = (turbFluxGround_dStateGround - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + dLatHeatCanopyEvap_dTGround = (latHeatCanEvap_dStateGround - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) + ! derivatives w.r.t. canopy total water content + dTurbFluxCanair_dCanWat = (turbFluxCanair_dStateCanWat - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxCanopy_dCanWat = (turbFluxCanopy_dStateCanWat - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxGround_dCanWat = (turbFluxGround_dStateCanWat - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + dLatHeatCanopyEvap_dCanWat = (latHeatCanEvap_dStateCanWat - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy total water content (J kg-1 s-1) + end if + + + ! compute the heat advected with precipitation (W m-2) + ! NOTE: fluxes are in kg m-2 s-1, so no need to use density of water/ice here + scalarCanopyAdvectiveHeatFlux = -Cp_water*(scalarRainfall - scalarThroughfallRain)*(canopyTempTrial - scalarTwetbulb) + & + (-Cp_ice)*(scalarSnowfall - scalarThroughfallSnow)*(canopyTempTrial - scalarTwetbulb) + scalarGroundAdvectiveHeatFlux = -Cp_water*scalarThroughfallRain*(groundTempTrial - scalarTwetbulb) + & + (-Cp_ice)*scalarThroughfallSnow*(groundTempTrial - scalarTwetbulb) + + + ! compute the mass flux associated with transpiration and evaporation/sublimation (J m-2 s-1 --> kg m-2 s-1) + ! NOTE: remove water from the snow on the ground in preference to removing water from the water in soil pores + + ! (canopy transpiration/sublimation) + if(scalarLatHeatSubVapCanopy > LH_vap+verySmall)then ! sublimation + scalarCanopyEvaporation = 0._rkind + scalarCanopySublimation = scalarLatHeatCanopyEvap/LH_sub + if(scalarLatHeatCanopyTrans > 0._rkind)then ! flux directed towards the veg + scalarCanopySublimation = scalarCanopySublimation + scalarLatHeatCanopyTrans/LH_sub ! frost + scalarCanopyTranspiration = 0._rkind + else + scalarCanopyTranspiration = scalarLatHeatCanopyTrans/LH_vap ! transpiration is always vapor + end if + ! (canopy transpiration/evaporation) + else ! evaporation + scalarCanopyEvaporation = scalarLatHeatCanopyEvap/LH_vap + scalarCanopySublimation = 0._rkind + if(scalarLatHeatCanopyTrans > 0._rkind)then ! flux directed towards the veg + scalarCanopyEvaporation = scalarCanopyEvaporation + scalarLatHeatCanopyTrans/LH_vap + scalarCanopyTranspiration = 0._rkind + else + scalarCanopyTranspiration = scalarLatHeatCanopyTrans/LH_vap + end if + end if + ! (ground evaporation/sublimation) + if(scalarLatHeatSubVapGround > LH_vap+verySmall)then ! sublimation + ! NOTE: this should only occur when we have formed snow layers, so check + if(nSnow == 0)then; err=20; message=trim(message)//'only expect snow sublimation when we have formed some snow layers'; return; end if + scalarGroundEvaporation = 0._rkind ! ground evaporation is zero once the snowpack has formed + scalarSnowSublimation = scalarLatHeatGround/LH_sub + else + ! NOTE: this should only occur when we have no snow layers, so check + if(nSnow > 0)then; err=20; message=trim(message)//'only expect ground evaporation when there are no snow layers'; return; end if + scalarGroundEvaporation = scalarLatHeatGround/LH_vap + scalarSnowSublimation = 0._rkind ! no sublimation from snow if no snow layers have formed + end if + + + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + ! ***** AND STITCH EVERYTHING TOGETHER ***************************************************************************************************************************** + ! ******************************************************************************************************************************************************************* + ! ******************************************************************************************************************************************************************* + + ! compute derived fluxes + scalarTotalET = scalarGroundEvaporation + scalarCanopyEvaporation + scalarCanopyTranspiration + scalarNetRadiation = scalarCanopyAbsorbedSolar + scalarLWNetCanopy + scalarGroundAbsorbedSolar + scalarLWNetGround + + ! compute net fluxes at the canopy and ground surface + canairNetFlux = turbFluxCanair + canopyNetFlux = scalarCanopyAbsorbedSolar + scalarLWNetCanopy + turbFluxCanopy + scalarCanopyAdvectiveHeatFlux + groundNetFlux = scalarGroundAbsorbedSolar + scalarLWNetGround + turbFluxGround + scalarGroundAdvectiveHeatFlux + + ! compute the energy derivatives + dCanairNetFlux_dCanairTemp = dTurbFluxCanair_dTCanair + dCanairNetFlux_dCanopyTemp = dTurbFluxCanair_dTCanopy + dCanairNetFlux_dGroundTemp = dTurbFluxCanair_dTGround + dCanopyNetFlux_dCanairTemp = dTurbFluxCanopy_dTCanair + dCanopyNetFlux_dCanopyTemp = dLWNetCanopy_dTCanopy + dTurbFluxCanopy_dTCanopy - Cp_water*(scalarRainfall - scalarThroughfallRain) - Cp_ice*(scalarSnowfall - scalarThroughfallSnow) + dCanopyNetFlux_dGroundTemp = dLWNetCanopy_dTGround + dTurbFluxCanopy_dTGround + dGroundNetFlux_dCanairTemp = dTurbFluxGround_dTCanair + dGroundNetFlux_dCanopyTemp = dLWNetGround_dTCanopy + dTurbFluxGround_dTCanopy + dGroundNetFlux_dGroundTemp = dLWNetGround_dTGround + dTurbFluxGround_dTGround - Cp_water*scalarThroughfallRain - Cp_ice*scalarThroughfallSnow + + ! check if evaporation or sublimation + if(scalarLatHeatSubVapCanopy < LH_vap+verySmall)then ! evaporation + + ! compute the liquid water derivarives + dCanopyEvaporation_dCanWat = dLatHeatCanopyEvap_dCanWat/LH_vap ! (s-1) + dCanopyEvaporation_dTCanair = dLatHeatCanopyEvap_dTCanair/LH_vap ! (kg m-2 s-1 K-1) + dCanopyEvaporation_dTCanopy = dLatHeatCanopyEvap_dTCanopy/LH_vap ! (kg m-2 s-1 K-1) + dCanopyEvaporation_dTGround = dLatHeatCanopyEvap_dTGround/LH_vap ! (kg m-2 s-1 K-1) + + ! sublimation + else + dCanopyEvaporation_dCanWat = 0._rkind ! (s-1) + dCanopyEvaporation_dTCanair = 0._rkind ! (kg m-2 s-1 K-1) + dCanopyEvaporation_dTCanopy = 0._rkind ! (kg m-2 s-1 K-1) + dCanopyEvaporation_dTGround = 0._rkind ! (kg m-2 s-1 K-1) + end if + + ! transpiration + if(scalarLatHeatCanopyTrans > 0._rkind)then ! flux directed towards the veg + dCanopyTrans_dCanWat = 0._rkind + dCanopyTrans_dTCanair= 0._rkind + dCanopyTrans_dTCanopy= 0._rkind + dCanopyTrans_dTGround= 0._rkind + else + dCanopyTrans_dCanWat= dLatHeatCanopyTrans_dCanWat/LH_vap ! transpiration is always vapor + dCanopyTrans_dTCanair= dLatHeatCanopyTrans_dTCanair/LH_vap + dCanopyTrans_dTCanopy= dLatHeatCanopyTrans_dTCanopy/LH_vap + dCanopyTrans_dTGround= dLatHeatCanopyTrans_dTGround/LH_vap + end if + + + ! compute the liquid water derivarives (ground evap) + dGroundEvaporation_dCanWat = dLatHeatGroundEvap_dCanWat/LH_vap ! (s-1) + dGroundEvaporation_dTCanair = dLatHeatGroundEvap_dTCanair/LH_vap ! (kg m-2 s-1 K-1) + dGroundEvaporation_dTCanopy = dLatHeatGroundEvap_dTCanopy/LH_vap ! (kg m-2 s-1 K-1) + dGroundEvaporation_dTGround = dLatHeatGroundEvap_dTGround/LH_vap ! (kg m-2 s-1 K-1) + + ! compute the cross derivative terms (only related to turbulent fluxes; specifically canopy evaporation and transpiration) + dCanopyNetFlux_dCanWat = dTurbFluxCanopy_dCanWat ! derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) + dGroundNetFlux_dCanWat = dTurbFluxGround_dCanWat ! derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) + + ! * check + case default; err=10; message=trim(message)//'unable to identify upper boundary condition for thermodynamics'; return + + ! end case statement + end select ! upper boundary condition for thermodynamics + + ! return liquid fluxes (needed for coupling) + returnCanopyTranspiration = scalarCanopyTranspiration ! canopy transpiration (kg m-2 s-1) + returnCanopyEvaporation = scalarCanopyEvaporation ! canopy evaporation/condensation (kg m-2 s-1) + returnGroundEvaporation = scalarGroundEvaporation ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + + ! end associations + end associate + +end subroutine vegNrgFlux + + +! ******************************************************************************************************* +! public subroutine wettedFrac: compute wetted fraction of the canopy +! ******************************************************************************************************* +subroutine wettedFrac(& + ! input + deriv, & ! flag to denote if derivative is desired + derNum, & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) + frozen, & ! flag to denote if the canopy is frozen + dLiq_dT, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) + fracLiq, & ! fraction of liquid water on the canopy (-) + canopyLiq, & ! canopy liquid water (kg m-2) + canopyIce, & ! canopy ice (kg m-2) + canopyLiqMax, & ! maximum canopy liquid water (kg m-2) + canopyIceMax, & ! maximum canopy ice content (kg m-2) + canopyWettingFactor, & ! maximum wetted fraction of the canopy (-) + canopyWettingExp, & ! exponent in canopy wetting function (-) + ! output + canopyWetFraction, & ! canopy wetted fraction (-) + dCanopyWetFraction_dWat,& ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) + dCanopyWetFraction_dT, & ! derivative in wetted fraction w.r.t. canopy temperature (K-1) + err,message) ! error control + implicit none + ! input + logical(lgt),intent(in) :: deriv ! flag to denote if derivative is desired + logical(lgt),intent(in) :: derNum ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) + logical(lgt),intent(in) :: frozen ! flag to denote if the canopy is frozen + real(rkind),intent(in) :: dLiq_dT ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) + real(rkind),intent(in) :: fracLiq ! fraction of liquid water on the canopy (-) + real(rkind),intent(in) :: canopyLiq ! canopy liquid water (kg m-2) + real(rkind),intent(in) :: canopyIce ! canopy ice (kg m-2) + real(rkind),intent(in) :: canopyLiqMax ! maximum canopy liquid water (kg m-2) + real(rkind),intent(in) :: canopyIceMax ! maximum canopy ice content (kg m-2) + real(rkind),intent(in) :: canopyWettingFactor ! maximum wetted fraction of the canopy (-) + real(rkind),intent(in) :: canopyWettingExp ! exponent in canopy wetting function (-) + ! output + real(rkind),intent(out) :: canopyWetFraction ! canopy wetted fraction (-) + real(rkind),intent(out) :: dCanopyWetFraction_dWat ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) + real(rkind),intent(out) :: dCanopyWetFraction_dT ! derivative in wetted fraction w.r.t. canopy temperature (K-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local variables + logical(lgt),parameter :: smoothing=.true. ! flag to denote that smoothing is required + real(rkind) :: canopyWetFractionPert ! canopy wetted fraction after state perturbations (-) + real(rkind) :: canopyWetFractionDeriv ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) + ! ----------------------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='wettedFrac/' + + ! compute case where the canopy is frozen + if(frozen)then + ! compute fraction of liquid water on the canopy + call wetFraction((deriv .and. .not.derNum),smoothing,canopyIce,canopyIceMax,canopyWettingFactor,canopyWettingExp,canopyWetFraction,canopyWetFractionDeriv) + ! compute numerical derivative, if derivative is desired + if(deriv.and.derNum)then + call wetFraction((deriv .and. .not.derNum),smoothing,canopyIce+dx,canopyIceMax,canopyWettingFactor,canopyWettingExp,canopyWetFractionPert,canopyWetFractionDeriv) + canopyWetFractionDeriv = (canopyWetFractionPert - canopyWetFraction)/dx + end if + ! scale derivative by the fraction of water + ! NOTE: dIce/dWat = (1._rkind - fracLiq), hence dWet/dWat = dIce/dWat . dWet/dLiq + dCanopyWetFraction_dWat = canopyWetFractionDeriv*(1._rkind - fracLiq) + dCanopyWetFraction_dT = -canopyWetFractionDeriv*dLiq_dT ! NOTE: dIce/dT = -dLiq/dT + return + end if - ! ***** - ! (1) DIRICHLET OR ZERO FLUX BOUNDARY CONDITION... - ! ************************************************ - - ! NOTE: Vegetation fluxes are not computed in this case - - ! ** prescribed temperature or zero flux at the upper boundary of the snow-soil system - case(prescribedTemp,zeroFlux) - - ! derived fluxes - scalarTotalET = 0._rkind ! total ET (kg m-2 s-1) - scalarNetRadiation = 0._rkind ! net radiation (W m-2) - ! liquid water fluxes associated with evaporation/transpiration - scalarCanopyTranspiration = 0._rkind ! canopy transpiration (kg m-2 s-1) - scalarCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) - scalarGroundEvaporation = 0._rkind ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - ! solid water fluxes associated with sublimation/frost - scalarCanopySublimation = 0._rkind ! sublimation from the vegetation canopy ((kg m-2 s-1) - scalarSnowSublimation = 0._rkind ! sublimation from the snow surface ((kg m-2 s-1) - ! set canopy fluxes to zero (no canopy) - canairNetFlux = 0._rkind ! net energy flux for the canopy air space (W m-2) - canopyNetFlux = 0._rkind ! net energy flux for the vegetation canopy (W m-2) - ! set canopy derivatives to zero - dCanairNetFlux_dCanairTemp = 0._rkind ! derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) - dCanairNetFlux_dCanopyTemp = 0._rkind ! derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) - dCanairNetFlux_dGroundTemp = 0._rkind ! derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) - dCanopyNetFlux_dCanairTemp = 0._rkind ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) - dCanopyNetFlux_dCanopyTemp = 0._rkind ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) - dCanopyNetFlux_dGroundTemp = 0._rkind ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - dGroundNetFlux_dCanairTemp = 0._rkind ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) - dGroundNetFlux_dCanopyTemp = 0._rkind ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) - ! set liquid flux derivatives to zero (canopy evap) - dCanopyEvaporation_dCanWat = 0._rkind ! derivative in canopy evaporation w.r.t. canopy total water content (s-1) - dCanopyEvaporation_dTCanair= 0._rkind ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTCanopy= 0._rkind ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTGround= 0._rkind ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - ! set liquid flux derivatives to zero (ground evap) - dGroundEvaporation_dCanWat = 0._rkind ! derivative in ground evaporation w.r.t. canopy total water content (s-1) - dGroundEvaporation_dTCanair= 0._rkind ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTCanopy= 0._rkind ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTGround= 0._rkind ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - ! set transpiration derivatives to zero - dCanopyTrans_dCanWat = 0._rkind ! derivative in canopy transpiration w.r.t. canopy total water content (s-1) - dCanopyTrans_dTCanair= 0._rkind ! derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTCanopy= 0._rkind ! derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTGround= 0._rkind ! derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - - ! compute fluxes and derivatives -- separate approach for prescribed temperature and zero flux, - if(ix_bcUpprTdyn == prescribedTemp)then - ! compute ground net flux (W m-2) - groundNetFlux = -diag_data%var(iLookDIAG%iLayerThermalC)%dat(0)*(groundTempTrial - upperBoundTemp)/(prog_data%var(iLookPROG%mLayerDepth)%dat(1)*0.5_rkind) - ! compute derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) inside soil and snow (ssd) energy flux routine - ! dGroundNetFlux_dGroundTemp = missingValue - elseif(ix_bcUpprTdyn == zeroFlux)then - groundNetFlux = 0._rkind - ! dGroundNetFlux_dGroundTemp = missingValue - else - err=20; message=trim(message)//'unable to identify upper boundary condition for thermodynamics: expect the case to be prescribedTemp or zeroFlux'; return - end if + ! compute fraction of liquid water on the canopy + ! NOTE: if(.not.deriv) canopyWetFractionDeriv = 0._rkind + call wetFraction((deriv .and. .not.derNum),smoothing,canopyLiq,canopyLiqMax,canopyWettingFactor,canopyWettingExp,canopyWetFraction,canopyWetFractionDeriv) - ! ***** - ! (2) NEUMANN BOUNDARY CONDITION... - ! ********************************* - - ! NOTE 1: This is the main routine for calculating vegetation fluxes - ! NOTE 2: This routine also calculates surface fluxes for the case where vegetation is buried with snow (or bare soil) - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! ***** PRELIMINARIES ********************************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - - ! * flux boundary condition - case(energyFlux) - - ! identify the appropriate groundwater variable - select case(ix_spatial_gw) - case(singleBasin); scalarAquiferStorage = basinAquiferStorage - case(localColumn); scalarAquiferStorage = localAquiferStorage - case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return - end select ! (modify the groundwater representation for this single-column implementation) - - ! set canopy stability corrections to the previous values - scalarCanopyStabilityCorrection_old = scalarCanopyStabilityCorrection ! stability correction for the canopy (-) - scalarGroundStabilityCorrection_old = scalarGroundStabilityCorrection ! stability correction for the ground surface (-) - - ! initialize variables to compute stomatal resistance - if(firstFluxCall .and. firstSubStep)then - ! vapor pressure in the canopy air space initialized as vapor pressure of air above the vegetation canopy - ! NOTE: this is needed for the stomatal resistance calculations - if(scalarVP_CanopyAir < 0._rkind)then - scalarVP_CanopyAir = scalarVPair - 1._rkind ! "small" offset used to assist in checking initial derivative calculations - end if - end if - - ! set latent heat of sublimation/vaporization for canopy and ground surface (Pa/K) - ! NOTE: variables are constant over the substep, to simplify relating energy and mass fluxes - if(firstFluxCall)then - scalarLatHeatSubVapCanopy = getLatentHeatValue(canopyTempTrial) - ! case when there is snow on the ground (EXCLUDE "snow without a layer" -- in this case, evaporate from the soil) - if(nSnow > 0)then - if(groundTempTrial > Tfreeze)then; err=20; message=trim(message)//'do not expect ground temperature > 0 when snow is on the ground'; return; end if - scalarLatHeatSubVapGround = LH_sub ! sublimation from snow - scalarGroundSnowFraction = 1._rkind - ! case when the ground is snow-free + ! compute numerical derivative + if(deriv.and.derNum)then + call wetFraction((deriv .and. .not.derNum),smoothing,canopyLiq+dx,canopyLiqMax,canopyWettingFactor,canopyWettingExp,canopyWetFractionPert,canopyWetFractionDeriv) + canopyWetFractionDeriv = (canopyWetFractionPert - canopyWetFraction)/dx + end if + + ! scale derivative by the fraction of water + ! NOTE: dLiq/dWat = fracLiq, hence dWet/dWat = dLiq/dWat . dWet/dLiq + dCanopyWetFraction_dWat = canopyWetFractionDeriv*fracLiq + dCanopyWetFraction_dT = canopyWetFractionDeriv*dLiq_dT + +end subroutine wettedFrac + + +! ******************************************************************************************************* +! private subroutine wetFraction: compute fraction of canopy covered with liquid water +! ******************************************************************************************************* +subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFactor,canopyWettingExp,canopyWetFraction,canopyWetFractionDeriv) + implicit none + ! dummy variables + logical(lgt),intent(in) :: derDesire ! flag to denote if analytical derivatives are desired + logical(lgt),intent(in) :: smoothing ! flag to denote if smoothing is required + real(rkind),intent(in) :: canopyLiq ! liquid water content (kg m-2) + real(rkind),intent(in) :: canopyMax ! liquid water content (kg m-2) + real(rkind),intent(in) :: canopyWettingFactor ! maximum wetted fraction of the canopy (-) + real(rkind),intent(in) :: canopyWettingExp ! exponent in canopy wetting function (-) + + real(rkind),intent(out) :: canopyWetFraction ! canopy wetted fraction (-) + real(rkind),intent(out) :: canopyWetFractionDeriv ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) + ! local variables + real(rkind) :: relativeCanopyWater ! water stored on vegetation canopy, expressed as a fraction of maximum storage (-) + real(rkind) :: rawCanopyWetFraction ! initial value of the canopy wet fraction (before smoothing) + real(rkind) :: rawWetFractionDeriv ! derivative in canopy wet fraction w.r.t. storage (kg-1 m2) + real(rkind) :: smoothFunc ! smoothing function used to improve numerical stability at times with limited water storage (-) + real(rkind) :: smoothFuncDeriv ! derivative in the smoothing function w.r.t.canopy storage (kg-1 m2) + real(rkind) :: verySmall=epsilon(1._rkind) ! a very small number + ! -------------------------------------------------------------------------------------------------------------- + + ! compute relative canopy water + relativeCanopyWater = canopyLiq/canopyMax + + ! compute an initial value of the canopy wet fraction + ! - canopy below value where canopy is 100% wet + if(relativeCanopyWater < 1._rkind)then + rawCanopyWetFraction = canopyWettingFactor*(relativeCanopyWater**canopyWettingExp) + if(derDesire .and. relativeCanopyWater>verySmall)then + rawWetFractionDeriv = (canopyWettingFactor*canopyWettingExp/canopyMax)*relativeCanopyWater**(canopyWettingExp - 1._rkind) else - scalarLatHeatSubVapGround = LH_vap ! evaporation of water in the soil pores: this occurs even if frozen because of super-cooled water - scalarGroundSnowFraction = 0._rkind - end if ! (if there is snow on the ground) - end if ! (if the first flux call) - !write(*,'(a,1x,10(f30.10,1x))') 'groundTempTrial, scalarLatHeatSubVapGround = ', groundTempTrial, scalarLatHeatSubVapGround - - ! compute the roughness length of the ground (ground below the canopy or non-vegetated surface) - z0Ground = z0soil*(1._rkind - scalarGroundSnowFraction) + z0Snow*scalarGroundSnowFraction ! roughness length (m) - - ! compute the total vegetation area index (leaf plus stem) - VAI = scalarLAI + scalarSAI ! vegetation area index - exposedVAI = scalarExposedLAI + scalarExposedSAI ! exposed vegetation area index - - ! compute emissivity of the canopy (-) - if(computeVegFlux)then - select case(ix_canopyEmis) - ! *** simple exponential function - case(simplExp) - scalarCanopyEmissivity = 1._rkind - exp(-exposedVAI) ! effective emissivity of the canopy (-) - ! *** canopy emissivity parameterized as a function of diffuse transmissivity - case(difTrans) - ! compute the exponential integral - scaleLAI = 0.5_rkind*exposedVAI - expi = expInt(scaleLAI) - ! compute diffuse transmissivity (-) - diffuseTrans = (1._rkind - scaleLAI)*exp(-scaleLAI) + (scaleLAI**2._rkind)*expi - ! compute the canopy emissivity - scalarCanopyEmissivity = (1._rkind - diffuseTrans)*vegEmissivity - ! *** check we found the correct option - case default - err=20; message=trim(message)//'unable to identify option for canopy emissivity'; return - end select - end if + rawWetFractionDeriv = 0._rkind + end if - ! ensure canopy longwave fluxes are zero when not computing canopy fluxes - if(.not.computeVegFlux) scalarCanopyEmissivity=0._rkind + ! - canopy is at capacity (canopyWettingFactor) + else + rawCanopyWetFraction = canopyWettingFactor + rawWetFractionDeriv = 0._rkind + end if - ! compute emissivity of the ground surface (-) - groundEmissivity = scalarGroundSnowFraction*snowEmissivity + (1._rkind - scalarGroundSnowFraction)*soilEmissivity ! emissivity of the ground surface (-) + ! smooth canopy wetted fraction + if(smoothing)then + call logisticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) + canopyWetFraction = rawCanopyWetFraction*smoothFunc ! logistic smoother + else + canopyWetFraction = rawCanopyWetFraction + canopyWetFractionDeriv = rawWetFractionDeriv + end if - ! compute the fraction of canopy that is wet - ! NOTE: we either sublimate or evaporate over the entire substep - if(computeVegFlux)then + ! compute derivative (product rule) + if(derDesire .and. smoothing)then ! NOTE: raw derivative is used if not smoothing + canopyWetFractionDeriv = rawWetFractionDeriv*smoothFunc + rawCanopyWetFraction*smoothFuncDeriv + else + canopyWetFractionDeriv = 0._rkind + end if - ! compute the fraction of liquid water in the canopy (-) - totalCanopyWater = canopyLiqTrial + canopyIceTrial - if(totalCanopyWater > tiny(1.0_rkind))then - fracLiquidCanopy = canopyLiqTrial / (canopyLiqTrial + canopyIceTrial) +end subroutine wetFraction + + +! ******************************************************************************************************* +! private subroutine logisticSmoother: compute the smoothing function +! ******************************************************************************************************* +subroutine logisticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) + implicit none + ! dummy variables + logical(lgt),intent(in) :: derDesire ! flag to denote if analytical derivatives are desired + real(rkind),intent(in) :: canopyLiq ! liquid water content (kg m-2) + real(rkind),intent(out) :: smoothFunc ! smoothing function (-) + real(rkind),intent(out) :: smoothFuncDeriv ! derivative in smoothing function (kg-1 m-2) + ! local variables + real(rkind) :: xArg ! argument used in the smoothing function (-) + real(rkind) :: expX ! exp(-xArg) -- used multiple times + real(rkind),parameter :: smoothThresh=0.01_rkind ! mid-point of the smoothing function (kg m-2) + real(rkind),parameter :: smoothScale=0.001_rkind ! scaling factor for the smoothing function (kg m-2) + real(rkind),parameter :: xLimit=50._rkind ! don't compute exponents for > xLimit + ! -------------------------------------------------------------------------------------------------------------- + ! compute argument in the smoothing function + xArg = (canopyLiq - smoothThresh)/smoothScale + + ! only compute smoothing function for small exponents + if(xArg > -xLimit .and. xArg < xLimit)then ! avoid huge exponents + expX = exp(-xarg) ! (also used in the derivative) + smoothFunc = 1._rkind / (1._rkind + expX) ! (logistic smoother) + if(derDesire)then + smoothFuncDeriv = expX / (smoothScale * (1._rkind + expX)**2._rkind) ! (derivative in the smoothing function) else - fracLiquidCanopy = 0._rkind + smoothFuncDeriv = 0._rkind end if - ! get wetted fraction and derivatives - call wettedFrac(& - ! input - .true., & ! flag to denote if derivative is desired - (ixDerivMethod == numerical), & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) - (scalarLatHeatSubVapCanopy > LH_vap+verySmall), & ! flag to denote if the canopy is frozen - dCanLiq_dTcanopy, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) - fracLiquidCanopy, & ! fraction of liquid water on the canopy (-) - canopyLiqTrial, & ! canopy liquid water (kg m-2) - canopyIceTrial, & ! canopy ice (kg m-2) - scalarCanopyLiqMax, & ! maximum canopy liquid water (kg m-2) - scalarCanopyIceMax, & ! maximum canopy ice content (kg m-2) - canopyWettingFactor, & ! maximum wetted fraction of the canopy (-) - canopyWettingExp, & ! exponent in canopy wetting function (-) - ! output - scalarCanopyWetFraction, & ! canopy wetted fraction (-) - dCanopyWetFraction_dWat, & ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) - dCanopyWetFraction_dT, & ! derivative in wetted fraction w.r.t. canopy temperature (K-1) - err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + ! outside limits: special case of smooth exponents + else + if(xArg < 0._rkind)then; smoothFunc = 0._rkind ! xArg < -xLimit + else; smoothFunc = 1._rkind ! xArg > xLimit + end if + smoothFuncDeriv = 0._rkind + end if ! check for huge exponents + +end subroutine logisticSmoother + ! -------------------------------------------------------------------------------------------------------------- - else - scalarCanopyWetFraction = 0._rkind ! canopy wetted fraction (-) - dCanopyWetFraction_dWat = 0._rkind ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) - dCanopyWetFraction_dT = 0._rkind ! derivative in wetted fraction w.r.t. canopy temperature (K-1) - end if - !write(*,'(a,1x,L1,1x,f25.15,1x))') 'computeVegFlux, scalarCanopyWetFraction = ', computeVegFlux, scalarCanopyWetFraction - !print*, 'dCanopyWetFraction_dWat = ', dCanopyWetFraction_dWat - !print*, 'dCanopyWetFraction_dT = ', dCanopyWetFraction_dT - !print*, 'canopyLiqTrial = ', canopyLiqTrial - !print*, 'canopyIceTrial = ', canopyIceTrial - !print*, 'scalarCanopyLiqMax = ', scalarCanopyLiqMax - !print*, 'scalarCanopyIceMax = ', scalarCanopyIceMax - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! ***** AERODYNAMIC RESISTANCE ***************************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - - ! NOTE: compute for all iterations - - ! compute aerodynamic resistances - ! Refs: Choudhury and Monteith (4-layer model for heat budget of homogenous surfaces; QJRMS, 1988) - ! Niu and Yang (Canopy effects on snow processes; JGR, 2004) - ! Mahat et al. (Below-canopy turbulence in a snowmelt model, WRR, 2012) - call aeroResist(& - ! input: model control - computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) - (ixDerivMethod == analytical), & ! intent(in): logical flag if would like to compute analytical derivaties - ix_veg_traits, & ! intent(in): choice of parameterization for vegetation roughness length and displacement height - ix_windPrfile, & ! intent(in): choice of canopy wind profile - ix_astability, & ! intent(in): choice of stability function - ! input: above-canopy forcing data - mHeight, & ! intent(in): measurement height (m) - airtemp, & ! intent(in): air temperature at some height above the surface (K) - windspd, & ! intent(in): wind speed at some height above the surface (m s-1) - ! input: canopy and ground temperature - canairTempTrial, & ! intent(in): temperature of the canopy air space (K) - groundTempTrial, & ! intent(in): temperature of the ground surface (K) - ! input: diagnostic variables - exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) - scalarSnowDepth, & ! intent(in): snow depth (m) - ! input: parameters - z0Ground, & ! intent(in): roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) - z0CanopyParam, & ! intent(in): roughness length of the canopy (m) - zpdFraction, & ! intent(in): zero plane displacement / canopy height (-) - critRichNumber, & ! intent(in): critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam, & ! intent(in): parameter in Louis (1979) stability function - Mahrt87_eScale, & ! intent(in): exponential scaling factor in the Mahrt (1987) stability function - windReductionParam, & ! intent(in): canopy wind reduction parameter (-) - leafExchangeCoeff, & ! intent(in): turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) - leafDimension, & ! intent(in): characteristic leaf dimension (m) - heightCanopyTop, & ! intent(in): height at the top of the vegetation canopy (m) - heightCanopyBottom, & ! intent(in): height at the bottom of the vegetation canopy (m) - ! output: stability corrections - scalarRiBulkCanopy, & ! intent(out): bulk Richardson number for the canopy (-) - scalarRiBulkGround, & ! intent(out): bulk Richardson number for the ground surface (-) - scalarCanopyStabilityCorrection, & ! intent(out): stability correction for the canopy (-) - scalarGroundStabilityCorrection, & ! intent(out): stability correction for the ground surface (-) - ! output: scalar resistances - scalarZ0Canopy, & ! intent(out): roughness length of the canopy (m) - scalarWindReductionFactor, & ! intent(out): canopy wind reduction factor (-) - scalarZeroPlaneDisplacement, & ! intent(out): zero plane displacement (m) - scalarEddyDiffusCanopyTop, & ! intent(out): eddy diffusivity for heat at the top of the canopy (m2 s-1) - scalarFrictionVelocity, & ! intent(out): friction velocity (m s-1) - scalarWindspdCanopyTop, & ! intent(out): windspeed at the top of the canopy (m s-1) - scalarWindspdCanopyBottom, & ! intent(out): windspeed at the height of the bottom of the canopy (m s-1) - scalarLeafResistance, & ! intent(out): mean leaf boundary layer resistance per unit leaf area (s m-1) - scalarGroundResistance, & ! intent(out): below canopy aerodynamic resistance (s m-1) - scalarCanopyResistance, & ! intent(out): above canopy aerodynamic resistance (s m-1) - ! output: derivatives in scalar resistances - dGroundResistance_dTGround, & ! intent(out): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - dGroundResistance_dTCanopy, & ! intent(out): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - dGroundResistance_dTCanair, & ! intent(out): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - dCanopyResistance_dTCanopy, & ! intent(out): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - dCanopyResistance_dTCanair, & ! intent(out): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - ! output: error control - err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - !print*, scalarLeafResistance, & ! mean leaf boundary layer resistance per unit leaf area (s m-1) - ! scalarGroundResistance, & ! below canopy aerodynamic resistance (s m-1) - ! scalarCanopyResistance, & ! above canopy aerodynamic resistance (s m-1) - ! '(leaf, ground, canopy)' - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! ***** STOMATAL RESISTANCE ***************************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - - ! stomatal resistance is constant over the SUBSTEP - ! NOTE: This is a simplification, as stomatal resistance does depend on canopy temperature - ! This "short-cut" made because: - ! (1) computations are expensive; - ! (2) derivative calculations are rather complex (iterations within the Ball-Berry routine); and - ! (3) stomatal resistance does not change rapidly - if(firstFluxCall)then - - ! compute the saturation vapor pressure for vegetation temperature - TV_celcius = canopyTempTrial - Tfreeze - call satVapPress(TV_celcius, scalarSatVP_CanopyTemp, dSVPCanopy_dCanopyTemp) - - ! compute soil moisture factor controlling stomatal resistance - call soilResist(& - ! input (model decisions) - ix_soilStress, & ! intent(in): choice of function for the soil moisture control on stomatal resistance - ix_groundwatr, & ! intent(in): groundwater parameterization - ! input (state variables) - mLayerMatricHead(1:nSoil), & ! intent(in): matric head in each soil layer (m) - mLayerVolFracLiq(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water in each soil layer (-) - scalarAquiferStorage, & ! intent(in): aquifer storage (m) - ! input (diagnostic variables) - mLayerRootDensity(1:nSoil), & ! intent(in): root density in each layer (-) - scalarAquiferRootFrac, & ! intent(in): fraction of roots below the lowest soil layer (-) - ! input (parameters) - plantWiltPsi, & ! intent(in): matric head at wilting point (m) - soilStressParam, & ! intent(in): parameter in the exponential soil stress function (-) - critSoilWilting, & ! intent(in): critical vol. liq. water content when plants are wilting (-) - critSoilTranspire, & ! intent(in): critical vol. liq. water content when transpiration is limited (-) - critAquiferTranspire, & ! intent(in): critical aquifer storage value when transpiration is limited (m) - ! output - scalarTranspireLim, & ! intent(out): weighted average of the transpiration limiting factor (-) - mLayerTranspireLim(1:nSoil), & ! intent(out): transpiration limiting factor in each layer (-) - scalarTranspireLimAqfr, & ! intent(out): transpiration limiting factor for the aquifer (-) - err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - !print*, 'weighted average of the soil moiture factor controlling stomatal resistance (-) = ', scalarTranspireLim - - !write(*,'(a,1x,10(f20.10,1x))') 'canopyTempTrial, scalarSatVP_CanopyTemp, scalarVP_CanopyAir = ', & - ! canopyTempTrial, scalarSatVP_CanopyTemp, scalarVP_CanopyAir - - ! compute stomatal resistance - call stomResist(& - ! input (state and diagnostic variables) - canopyTempTrial, & ! intent(in): temperature of the vegetation canopy (K) - scalarSatVP_CanopyTemp, & ! intent(in): saturation vapor pressure at the temperature of the veg canopy (Pa) - scalarVP_CanopyAir, & ! intent(in): canopy air vapor pressure (Pa) - ! input: data structures - type_data, & ! intent(in): type of vegetation and soil - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - model_decisions, & ! intent(in): model decisions - ! input-output: data structures - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output: error control - err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - end if ! (if the first flux call in a given sub-step) - - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! ***** LONGWAVE RADIATION ***************************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - - ! compute canopy longwave radiation balance - call longwaveBal(& - ! input: model control - ixDerivMethod, & ! intent(in): method used to calculate flux derivatives - computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation - insideIDA, & ! intent(in): flag to indicate inside Sundials Solver (do not require longwave to be balanced) - ! input: canopy and ground temperature - canopyTempTrial, & ! intent(in): temperature of the vegetation canopy (K) - groundTempTrial, & ! intent(in): temperature of the ground surface (K) - ! input: canopy and ground emissivity - scalarCanopyEmissivity, & ! intent(in): canopy emissivity (-) - groundEmissivity, & ! intent(in): ground emissivity (-) - ! input: forcing - LWRadAtm, & ! intent(in): downwelling longwave radiation at the upper boundary (W m-2) - ! output: emitted radiation from the canopy and ground - scalarLWRadCanopy, & ! intent(out): longwave radiation emitted from the canopy (W m-2) - scalarLWRadGround, & ! intent(out): longwave radiation emitted at the ground surface (W m-2) - ! output: individual fluxes - scalarLWRadUbound2Canopy, & ! intent(out): downward atmospheric longwave radiation absorbed by the canopy (W m-2) - scalarLWRadUbound2Ground, & ! intent(out): downward atmospheric longwave radiation absorbed by the ground (W m-2) - scalarLWRadUbound2Ubound, & ! intent(out): atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) - scalarLWRadCanopy2Ubound, & ! intent(out): longwave radiation emitted from canopy lost thru upper boundary (W m-2) - scalarLWRadCanopy2Ground, & ! intent(out): longwave radiation emitted from canopy absorbed by the ground (W m-2) - scalarLWRadCanopy2Canopy, & ! intent(out): canopy longwave reflected from ground and absorbed by the canopy (W m-2) - scalarLWRadGround2Ubound, & ! intent(out): longwave radiation emitted from ground lost thru upper boundary (W m-2) - scalarLWRadGround2Canopy, & ! intent(out): longwave radiation emitted from ground and absorbed by the canopy (W m-2) - ! output: net fluxes - scalarLWNetCanopy, & ! intent(out): net longwave radiation at the canopy (W m-2) - scalarLWNetGround, & ! intent(out): net longwave radiation at the ground surface (W m-2) - scalarLWNetUbound, & ! intent(out): net longwave radiation at the upper boundary (W m-2) - ! output: flux derivatives - dLWNetCanopy_dTCanopy, & ! intent(out): derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) - dLWNetGround_dTGround, & ! intent(out): derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetCanopy_dTGround, & ! intent(out): derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetGround_dTCanopy, & ! intent(out): derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) - ! output: error control - err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - !print*, 'dLWNetCanopy_dTGround = ', dLWNetCanopy_dTGround - - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! ***** TURBULENT HEAT FLUXES ************************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - - ! check the need to compute numerical derivatives - if(ixDerivMethod == numerical)then - nFlux=5 ! compute the derivatives using one-sided finite differences - else +! ******************************************************************************************************* +! private subroutine longwaveBal: compute longwave radiation balance at the canopy and ground surface +! ******************************************************************************************************* +subroutine longwaveBal(& + ! input: model control + ixDerivMethod, & ! intent(in): choice of method used to compute derivative (analytical or numerical) + computeVegFlux, & ! intent(in): flag to compute fluxes over vegetati + insideIDA, & ! intent(in): flag to indicate inside Sundials Solver (do not require longwave to be balanced) + ! input: canopy and ground temperature + canopyTemp, & ! intent(in): canopy temperature (K) + groundTemp, & ! intent(in): ground temperature (K) + ! input: canopy and ground emissivity + emc, & ! intent(in): canopy emissivity (-) + emg, & ! intent(in): ground emissivity (-) + ! input: forcing + LWRadUbound, & ! intent(in): downwelling longwave radiation at the upper boundary (W m-2) + ! output: sources + LWRadCanopy, & ! intent(out): longwave radiation emitted from the canopy (W m-2) + LWRadGround, & ! intent(out): longwave radiation emitted at the ground surface (W m-2) + ! output: individual fluxes + LWRadUbound2Canopy, & ! intent(out): downward atmospheric longwave radiation absorbed by the canopy (W m-2) + LWRadUbound2Ground, & ! intent(out): downward atmospheric longwave radiation absorbed by the ground (W m-2) + LWRadUbound2Ubound, & ! intent(out): atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) + LWRadCanopy2Ubound, & ! intent(out): longwave radiation emitted from canopy lost thru upper boundary (W m-2) + LWRadCanopy2Ground, & ! intent(out): longwave radiation emitted from canopy absorbed by the ground (W m-2) + LWRadCanopy2Canopy, & ! intent(out): canopy longwave reflected from ground and absorbed by the canopy (W m-2) + LWRadGround2Ubound, & ! intent(out): longwave radiation emitted from ground lost thru upper boundary (W m-2) + LWRadGround2Canopy, & ! intent(out): longwave radiation emitted from ground and absorbed by the canopy (W m-2) + ! output: net fluxes + LWNetCanopy, & ! intent(out): net longwave radiation at the canopy (W m-2) + LWNetGround, & ! intent(out): net longwave radiation at the ground surface (W m-2) + LWNetUbound, & ! intent(out): net longwave radiation at the upper boundary (W m-2) + ! output: flux derivatives + dLWNetCanopy_dTCanopy, & ! intent(out): derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) + dLWNetGround_dTGround, & ! intent(out): derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) + dLWNetCanopy_dTGround, & ! intent(out): derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) + dLWNetGround_dTCanopy, & ! intent(out): derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) + ! output: error control + err,message ) ! intent(out): error control + ! ----------------------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input: model control + integer(i4b),intent(in) :: ixDerivMethod ! choice of method used to compute derivative (analytical or numerical) + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: insideIDA ! flag if inside Sundials solver + ! input: canopy and ground temperature + real(rkind),intent(in) :: canopyTemp ! canopy temperature (K) + real(rkind),intent(in) :: groundTemp ! ground temperature (K) + ! input: canopy and ground emissivity + real(rkind),intent(in) :: emc ! canopy emissivity (-) + real(rkind),intent(in) :: emg ! ground emissivity (-) + ! input: forcing + real(rkind),intent(in) :: LWRadUbound ! downwelling longwave radiation at the upper boundary (W m-2) + ! output: sources + real(rkind),intent(out) :: LWRadCanopy ! longwave radiation emitted from the canopy (W m-2) + real(rkind),intent(out) :: LWRadGround ! longwave radiation emitted at the ground surface (W m-2) + ! output: individual fluxes + real(rkind),intent(out) :: LWRadUbound2Canopy ! downward atmospheric longwave radiation absorbed by the canopy (W m-2) + real(rkind),intent(out) :: LWRadUbound2Ground ! downward atmospheric longwave radiation absorbed by the ground (W m-2) + real(rkind),intent(out) :: LWRadUbound2Ubound ! atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) + real(rkind),intent(out) :: LWRadCanopy2Ubound ! longwave radiation emitted from canopy lost thru upper boundary (W m-2) + real(rkind),intent(out) :: LWRadCanopy2Ground ! longwave radiation emitted from canopy absorbed by the ground (W m-2) + real(rkind),intent(out) :: LWRadCanopy2Canopy ! canopy longwave reflected from ground and absorbed by the canopy (W m-2) + real(rkind),intent(out) :: LWRadGround2Ubound ! longwave radiation emitted from ground lost thru upper boundary (W m-2) + real(rkind),intent(out) :: LWRadGround2Canopy ! longwave radiation emitted from ground and absorbed by the canopy (W m-2) + ! output: net fluxes + real(rkind),intent(out) :: LWNetCanopy ! net longwave radiation at the canopy (W m-2) + real(rkind),intent(out) :: LWNetGround ! net longwave radiation at the ground surface (W m-2) + real(rkind),intent(out) :: LWNetUbound ! net longwave radiation at the upper boundary (W m-2) + ! output: flux derivatives + real(rkind),intent(out) :: dLWNetCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) + real(rkind),intent(out) :: dLWNetGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) + real(rkind),intent(out) :: dLWNetCanopy_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) + real(rkind),intent(out) :: dLWNetGround_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ----------------------------------------------------------------------------------------------------------------------------------------------- + ! local variables + integer(i4b),parameter :: unperturbed=1 ! named variable to identify the case of unperturbed state variables + integer(i4b),parameter :: perturbStateCanopy=2 ! named variable to identify the case where we perturb the canopy temperature + integer(i4b),parameter :: perturbStateGround=3 ! named variable to identify the case where we perturb the ground temperature + integer(i4b) :: itry ! index of flux evaluation + integer(i4b) :: nFlux ! number of flux evaluations + real(rkind) :: TCan ! value of canopy temperature used in flux calculations (may be perturbed) + real(rkind) :: TGnd ! value of ground temperature used in flux calculations (may be perturbed) + real(rkind) :: fluxBalance ! check energy closure (W m-2) + real(rkind),parameter :: fluxTolerance=1.e-10_rkind ! tolerance for energy closure (W m-2) + real(rkind) :: dLWRadCanopy_dTCanopy ! derivative in emitted radiation at the canopy w.r.t. canopy temperature + real(rkind) :: dLWRadGround_dTGround ! derivative in emitted radiation at the ground w.r.t. ground temperature + real(rkind) :: LWNetCanopy_dStateCanopy ! net lw canopy flux after perturbation in canopy temperature + real(rkind) :: LWNetGround_dStateCanopy ! net lw ground flux after perturbation in canopy temperature + real(rkind) :: LWNetCanopy_dStateGround ! net lw canopy flux after perturbation in ground temperature + real(rkind) :: LWNetGround_dStateGround ! net lw ground flux after perturbation in ground temperature + ! ----------------------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='longwaveBal/' + + ! check the need to compute numerical derivatives + if(ixDerivMethod==numerical)then + nFlux=3 ! compute the derivatives using one-sided finite differences + else nFlux=1 ! compute analytical derivatives - end if + end if - ! either one or multiple flux calls, depending on if using analytical or numerical derivatives - do itry=nFlux,1,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) + ! either one or multiple flux calls, depending on if using analytical or numerical derivatives + do itry=nFlux,1,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) ! ------------------------------------------------------------------------------------- ! state perturbations for numerical deriavtives with one-sided finite differences ! note: no perturbations performed using analytical derivatives (nFlux=1) ! ------------------------------------------------------------------------------------- - ! initialize for unperturbed state - canopyWetFraction = scalarCanopyWetFraction - ! identify the type of perturbation select case(itry) - ! un-perturbed case - case(unperturbed) - groundTemp = groundTempTrial - canopyTemp = canopyTempTrial - canairTemp = canairTempTrial - canopyWat = totalCanopyWater - - ! perturb ground temperature - case(perturbStateGround) - groundTemp = groundTempTrial + dx - canopyTemp = canopyTempTrial - canairTemp = canairTempTrial - canopyWat = totalCanopyWater - - ! perturb canopy temperature - case(perturbStateCanopy) - groundTemp = groundTempTrial - canopyTemp = canopyTempTrial + dx - canairTemp = canairTempTrial - canopyWat = totalCanopyWater - - ! perturb canopy air temperature - case(perturbStateCanair) - groundTemp = groundTempTrial - canopyTemp = canopyTempTrial - canairTemp = canairTempTrial + dx - canopyWat = totalCanopyWater - - ! perturb canopy total water content - case(perturbStateCanWat) - groundTemp = groundTempTrial - canopyTemp = canopyTempTrial - canairTemp = canairTempTrial - canopyWat = totalCanopyWater + dx - - ! check for an unknown perturbation - case default; err=10; message=trim(message)//"unknown perturbation"; return + ! un-perturbed case + case(unperturbed) + TCan = canopyTemp + TGnd = groundTemp - end select ! (type of perturbation) + ! perturb canopy temperature + case(perturbStateCanopy) + TCan = canopyTemp + dx + TGnd = groundTemp - ! perturbations in canopy total water content affect canopy wetted fraction - if(itry == perturbStateCanopy .OR. itry == perturbStateCanWat)then + ! perturb ground temperature + case(perturbStateGround) + TCan = canopyTemp + TGnd = groundTemp + dx - ! recalculate liquid and ice from total water - fracLiquidCanopy = fracliquid(canopyTemp,snowfrz_scale) - canopyLiq = fracLiquidCanopy*canopyWat - canopyIce = (1._rkind - fracLiquidCanopy)*canopyWat + ! check for an unknown perturbation + case default; err=10; message=trim(message)//"unknown perturbation"; return - if(computeVegFlux)then - call wettedFrac(& - ! input - .false., & ! flag to denote if derivative is desired - (ixDerivMethod == numerical), & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) - (scalarLatHeatSubVapCanopy > LH_vap+verySmall), & ! flag to denote if the canopy is frozen - dCanLiq_dTcanopy, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) - fracLiquidCanopy, & ! fraction of liquid water on the canopy (-) - canopyLiq, & ! canopy liquid water (kg m-2) - canopyIce, & ! canopy ice (kg m-2) - scalarCanopyLiqMax, & ! maximum canopy liquid water (kg m-2) - scalarCanopyIceMax, & ! maximum canopy ice content (kg m-2) - canopyWettingFactor, & ! maximum wetted fraction of the canopy (-) - canopyWettingExp, & ! exponent in canopy wetting function (-) - ! output - canopyWetFraction, & ! canopy wetted fraction (-) - dCanopyWetFraction_dWat, & ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) - dCanopyWetFraction_dT, & ! derivative in wetted fraction w.r.t. canopy temperature (K-1) - err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - else - canopyWetFraction = 0._rkind - end if ! (desired computing vegetation flux) - - end if ! (re-computing wetted fraction for perturbed cases) - !print*, 'wetted fraction derivative = ', (canopyWetFraction - scalarCanopyWetFraction)/dx - !pause - - ! compute the saturation vapor pressure for vegetation temperature - ! NOTE: saturated vapor pressure derivatives don't seem that accurate.... - TV_celcius = canopyTemp - Tfreeze - call satVapPress(TV_celcius, scalarSatVP_CanopyTemp, dSVPCanopy_dCanopyTemp) - - ! compute the saturation vapor pressure for ground temperature - ! NOTE: saturated vapor pressure derivatives don't seem that accurate.... - TG_celcius = groundTemp - Tfreeze - call satVapPress(TG_celcius, scalarSatVP_GroundTemp, dSVPGround_dGroundTemp) + end select ! (type of perturbation) ! ------------------------------------------------------------------------------------- ! calculation block (unperturbed fluxes returned [computed last]) ! ------------------------------------------------------------------------------------- + ! NOTE: emc should be set to zero when not computing canopy fluxes - ! re-compute aerodynamic resistances for perturbed cases - ! NOTE: unperturbed fluxes computed earlier, and not over-written - if(itry /= unperturbed)then - call aeroResist(& - ! input: model control - computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) - .false., & ! intent(in): logical flag if would like to compute analytical derivaties - ix_veg_traits, & ! intent(in): choice of parameterization for vegetation roughness length and displacement height - ix_windPrfile, & ! intent(in): choice of canopy wind profile - ix_astability, & ! intent(in): choice of stability function - ! input: above-canopy forcing data - mHeight, & ! intent(in): measurement height (m) - airtemp, & ! intent(in): air temperature at some height above the surface (K) - windspd, & ! intent(in): wind speed at some height above the surface (m s-1) - ! input: temperature (canopy, ground, canopy air space) - canairTemp, & ! intent(in): temperature of the canopy air space (K) - groundTemp, & ! intent(in): ground temperature (K) - ! input: diagnostic variables - exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) - scalarSnowDepth, & ! intent(in): snow depth (m) - ! input: parameters - z0Ground, & ! intent(in): roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) - z0CanopyParam, & ! intent(in): roughness length of the canopy (m) - zpdFraction, & ! intent(in): zero plane displacement / canopy height (-) - critRichNumber, & ! intent(in): critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam, & ! intent(in): parameter in Louis (1979) stability function - Mahrt87_eScale, & ! intent(in): exponential scaling factor in the Mahrt (1987) stability function - windReductionParam, & ! intent(in): canopy wind reduction parameter (-) - leafExchangeCoeff, & ! intent(in): turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) - leafDimension, & ! intent(in): characteristic leaf dimension (m) - heightCanopyTop, & ! intent(in): height at the top of the vegetation canopy (m) - heightCanopyBottom, & ! intent(in): height at the bottom of the vegetation canopy (m) - ! output: stability corrections - notUsed_RiBulkCanopy, & ! intent(out): bulk Richardson number for the canopy (-) - notUsed_RiBulkGround, & ! intent(out): bulk Richardson number for the ground surface (-) - notUsed_scalarCanopyStabilityCorrection, & ! intent(out): stability correction for the canopy (-) - notUsed_scalarGroundStabilityCorrection, & ! intent(out): stability correction for the ground surface (-) - ! output: scalar resistances - notUsed_z0Canopy, & ! intent(out): roughness length of the canopy (m) - notUsed_WindReductionFactor, & ! intent(out): canopy wind reduction factor (-) - notUsed_ZeroPlaneDisplacement, & ! intent(out): zero plane displacement (m) - notUsed_EddyDiffusCanopyTop, & ! intent(out): eddy diffusivity for heat at the top of the canopy (m2 s-1) - notUsed_FrictionVelocity, & ! intent(out): friction velocity (m s-1) - notUsed_WindspdCanopyTop, & ! intent(out): windspeed at the top of the canopy (m s-1) - notUsed_WindspdCanopyBottom, & ! intent(out): windspeed at the height of the bottom of the canopy (m s-1) - trialLeafResistance, & ! intent(out): mean leaf boundary layer resistance per unit leaf area (s m-1) - trialGroundResistance, & ! intent(out): below canopy aerodynamic resistance (s m-1) - trialCanopyResistance, & ! intent(out): above canopy aerodynamic resistance (s m-1) - ! output: derivatives in scalar resistances - notUsed_dGroundResistance_dTGround, & ! intent(out): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - notUsed_dGroundResistance_dTCanopy, & ! intent(out): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - notUsed_dGroundResistance_dTCanair, & ! intent(out): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - notUsed_dCanopyResistance_dTCanopy, & ! intent(out): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - notUsed_dCanopyResistance_dTCanair, & ! intent(out): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - ! output: error control - err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - - ! assign scalar resistances for un-perturbed cases + ! compute longwave fluxes from canopy and the ground + if(computeVegFlux)then + LWRadCanopy = emc*sb*TCan**4._rkind ! longwave radiation emitted from the canopy (W m-2) else - trialLeafResistance = scalarLeafResistance - trialGroundResistance = scalarGroundResistance - trialCanopyResistance = scalarCanopyResistance - - end if ! (re-computing resistances for perturbed cases) - !print*, 'trialLeafResistance = ', trialLeafResistance - !print*, 'trialGroundResistance = ', trialGroundResistance - !print*, 'trialCanopyResistance = ', trialCanopyResistance - - ! compute the relative humidity in the top soil layer and the resistance at the ground surface - ! NOTE: computations are based on start-of-step values, so only compute for the first flux call - if(firstFluxCall)then - ! (soil water evaporation factor [0-1]) - soilEvapFactor = mLayerVolFracLiq(nSnow+1)/(theta_sat - theta_res) - ! (resistance from the soil [s m-1]) - scalarSoilResistance = scalarGroundSnowFraction*1._rkind + (1._rkind - scalarGroundSnowFraction)*EXP(8.25_rkind - 4.225_rkind*soilEvapFactor) ! Sellers (1992) - !scalarSoilResistance = scalarGroundSnowFraction*0._rkind + (1._rkind - scalarGroundSnowFraction)*exp(8.25_rkind - 6.0_rkind*soilEvapFactor) ! Niu adjustment to decrease resitance for wet soil - ! (relative humidity in the soil pores [0-1]) - if(mLayerMatricHead(1) > -1.e+6_rkind)then ! avoid problems with numerical precision when soil is very dry - soilRelHumidity_noSnow = exp( (mLayerMatricHead(1)*gravity) / (groundTemp*R_wv) ) - else - soilRelHumidity_noSnow = 0._rkind - end if ! (if matric head is very low) - scalarSoilRelHumidity = scalarGroundSnowFraction*1._rkind + (1._rkind - scalarGroundSnowFraction)*soilRelHumidity_noSnow - !print*, 'mLayerMatricHead(1), scalarSoilRelHumidity = ', mLayerMatricHead(1), scalarSoilRelHumidity - end if ! (if the first flux call) - - ! compute turbulent heat fluxes - call turbFluxes(& - ! input: model control - computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) - ixDerivMethod, & ! intent(in): method used to calculate flux derivatives - ! input: above-canopy forcing data - airtemp, & ! intent(in): air temperature at some height above the surface (K) - airpres, & ! intent(in): air pressure of the air above the vegetation canopy (Pa) - scalarVPair, & ! intent(in): vapor pressure of the air above the vegetation canopy (Pa) - ! input: latent heat of sublimation/vaporization - scalarLatHeatSubVapCanopy, & ! intent(in): latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) - scalarLatHeatSubVapGround, & ! intent(in): latent heat of sublimation/vaporization for the ground surface (J kg-1) - ! input: canopy/ground temperature and saturated vapor pressure - canairTemp, & ! intent(in): temperature of the canopy air space (K) - canopyTemp, & ! intent(in): canopy temperature (K) - groundTemp, & ! intent(in): ground temperature (K) - scalarSatVP_CanopyTemp, & ! intent(in): saturation vapor pressure at the temperature of the veg canopy (Pa) - scalarSatVP_GroundTemp, & ! intent(in): saturation vapor pressure at the temperature of the ground (Pa) - dSVPCanopy_dCanopyTemp, & ! intent(in): derivative in canopy saturation vapor pressure w.r.t. canopy temperature (Pa K-1) - dSVPGround_dGroundTemp, & ! intent(in): derivative in ground saturation vapor pressure w.r.t. ground temperature (Pa K-1) - ! input: diagnostic variables - exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) - canopyWetFraction, & ! intent(in): trial value for the fraction of canopy that is wet [0-1] - dCanopyWetFraction_dWat, & ! intent(in): derivative in the canopy wetted fraction w.r.t. total water content (kg-1 m-2) - dCanopyWetFraction_dT, & ! intent(in): derivative in wetted fraction w.r.t. canopy temperature (K-1) - scalarCanopySunlitLAI, & ! intent(in): sunlit leaf area (-) - scalarCanopyShadedLAI, & ! intent(in): shaded leaf area (-) - scalarSoilRelHumidity, & ! intent(in): relative humidity in the soil pores [0-1] - scalarSoilResistance, & ! intent(in): resistance from the soil (s m-1) - trialLeafResistance, & ! intent(in): mean leaf boundary layer resistance per unit leaf area (s m-1) - trialGroundResistance, & ! intent(in): below canopy aerodynamic resistance (s m-1) - trialCanopyResistance, & ! intent(in): above canopy aerodynamic resistance (s m-1) - scalarStomResistSunlit, & ! intent(in): stomatal resistance for sunlit leaves (s m-1) - scalarStomResistShaded, & ! intent(in): stomatal resistance for shaded leaves (s m-1) - ! input: derivatives in scalar resistances - dGroundResistance_dTGround, & ! intent(in): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - dGroundResistance_dTCanopy, & ! intent(in): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - dGroundResistance_dTCanair, & ! intent(in): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - dCanopyResistance_dTCanopy, & ! intent(in): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - dCanopyResistance_dTCanair, & ! intent(in): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - ! output: conductances (used to check derivative calculations) - scalarLeafConductance, & ! intent(out): leaf conductance (m s-1) - scalarCanopyConductance, & ! intent(out): canopy conductance (m s-1) - scalarGroundConductanceSH, & ! intent(out): ground conductance for sensible heat (m s-1) - scalarGroundConductanceLH, & ! intent(out): ground conductance for latent heat -- includes soil resistance (m s-1) - scalarEvapConductance, & ! intent(out): conductance for evaporation (m s-1) - scalarTransConductance, & ! intent(out): conductance for transpiration (m s-1) - scalarTotalConductanceSH, & ! intent(out): total conductance for sensible heat (m s-1) - scalarTotalConductanceLH, & ! intent(out): total conductance for latent heat (m s-1) - ! output: canopy air space variables - scalarVP_CanopyAir, & ! intent(out): vapor pressure of the canopy air space (Pa) - ! output: fluxes from the vegetation canopy - scalarSenHeatCanopy, & ! intent(out): sensible heat flux from the canopy to the canopy air space (W m-2) - scalarLatHeatCanopyEvap, & ! intent(out): latent heat flux associated with evaporation from the canopy to the canopy air space (W m-2) - scalarLatHeatCanopyTrans, & ! intent(out): latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) - ! output: fluxes from non-vegetated surfaces (ground surface below vegetation, bare ground, or snow covered vegetation) - scalarSenHeatGround, & ! intent(out): sensible heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) - scalarLatHeatGround, & ! intent(out): latent heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) - ! output: total heat fluxes to the atmosphere - scalarSenHeatTotal, & ! intent(out): total sensible heat flux to the atmosphere (W m-2) - scalarLatHeatTotal, & ! intent(out): total latent heat flux to the atmosphere (W m-2) - ! output: net fluxes - turbFluxCanair, & ! intent(out): net turbulent heat fluxes at the canopy air space (W m-2) - turbFluxCanopy, & ! intent(out): net turbulent heat fluxes at the canopy (W m-2) - turbFluxGround, & ! intent(out): net turbulent heat fluxes at the ground surface (W m-2) - ! output: energy flux derivatives - dTurbFluxCanair_dTCanair, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxCanair_dTCanopy, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxCanair_dTGround, & ! intent(out): derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanair, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanopy, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxCanopy_dTGround, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxGround_dTCanair, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxGround_dTCanopy, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxGround_dTGround, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - ! output: liquid flux derivatives (canopy evap) - dLatHeatCanopyEvap_dCanWat, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy total water content (W kg-1) - dLatHeatCanopyEvap_dTCanair, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTCanopy, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTGround, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) - ! output: liquid flux derivatives (ground evap) - dLatHeatGroundEvap_dCanWat, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) - dLatHeatGroundEvap_dTCanair, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy air temperature - dLatHeatGroundEvap_dTCanopy, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy temperature - dLatHeatGroundEvap_dTGround, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. ground temperature - ! output: latent heat flux derivatives (canopy trans) - dLatHeatCanopyTrans_dCanWat, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy total water (J kg-1 s-1) - dLatHeatCanopyTrans_dTCanair, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy air temperature - dLatHeatCanopyTrans_dTCanopy, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy temperature - dLatHeatCanopyTrans_dTGround, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. ground temperature - ! output: cross derivatives - dTurbFluxCanair_dCanWat, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) - dTurbFluxCanopy_dCanWat, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - dTurbFluxGround_dCanWat, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - ! output: error control - err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + LWRadCanopy = 0._rkind + end if + LWRadGround = emg*sb*TGnd**4._rkind ! longwave radiation emitted at the ground surface (W m-2) + + ! compute fluxes originating from the atmosphere + LWRadUbound2Canopy = (emc + (1._rkind - emc)*(1._rkind - emg)*emc)*LWRadUbound ! downward atmospheric longwave radiation absorbed by the canopy (W m-2) + LWRadUbound2Ground = (1._rkind - emc)*emg*LWRadUbound ! downward atmospheric longwave radiation absorbed by the ground (W m-2) + LWRadUbound2Ubound = (1._rkind - emc)*(1._rkind - emg)*(1._rkind - emc)*LWRadUbound ! atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) + + ! compute fluxes originating from the canopy + LWRadCanopy2Ubound = (1._rkind + (1._rkind - emc)*(1._rkind - emg))*LWRadCanopy ! longwave radiation emitted from canopy lost thru upper boundary (W m-2) + LWRadCanopy2Ground = emg*LWRadCanopy ! longwave radiation emitted from canopy absorbed by the ground (W m-2) + LWRadCanopy2Canopy = emc*(1._rkind - emg)*LWRadCanopy ! canopy longwave reflected from ground and absorbed by the canopy (W m-2) + + ! compute fluxes originating from the ground surface + LWRadGround2Ubound = (1._rkind - emc)*LWRadGround ! longwave radiation emitted from ground lost thru upper boundary (W m-2) + LWRadGround2Canopy = emc*LWRadGround ! longwave radiation emitted from ground and absorbed by the canopy (W m-2) + + ! compute net longwave radiation (W m-2) + LWNetCanopy = LWRadUbound2Canopy + LWRadGround2Canopy + LWRadCanopy2Canopy - 2._rkind*LWRadCanopy ! canopy + LWNetGround = LWRadUbound2Ground + LWRadCanopy2Ground - LWRadGround ! ground surface + LWNetUbound = LWRadUbound - LWRadUbound2Ubound - LWRadCanopy2Ubound - LWRadGround2Ubound ! upper boundary + + ! check the flux balance + if(.not.insideIDA)then + fluxBalance = LWNetUbound - (LWNetCanopy + LWNetGround) + if(abs(fluxBalance) > fluxTolerance)then + print*, 'fluxBalance = ', fluxBalance + print*, 'emg, emc = ', emg, emc + print*, 'TCan, TGnd = ', TCan, TGnd + print*, 'LWRadUbound = ', LWRadUbound + print*, 'LWRadCanopy = ', LWRadCanopy + print*, 'LWRadGround = ', LWRadGround + print*, 'LWRadUbound2Canopy = ', LWRadUbound2Canopy + print*, 'LWRadUbound2Ground = ', LWRadUbound2Ground + print*, 'LWRadUbound2Ubound = ', LWRadUbound2Ubound + print*, 'LWRadCanopy2Ubound = ', LWRadCanopy2Ubound + print*, 'LWRadCanopy2Ground = ', LWRadCanopy2Ground + print*, 'LWRadCanopy2Canopy = ', LWRadCanopy2Canopy + print*, 'LWRadGround2Ubound = ', LWRadGround2Ubound + print*, 'LWRadGround2Canopy = ', LWRadGround2Canopy + print*, 'LWNetCanopy = ', LWNetCanopy + print*, 'LWNetGround = ', LWNetGround + print*, 'LWNetUbound = ', LWNetUbound + message=trim(message)//'flux imbalance' + err=20; return + end if + end if - !write(*,'(a,f25.15)') 'scalarSenHeatTotal = ', scalarSenHeatTotal - !write(*,'(a,f25.15)') 'scalarSenHeatCanopy = ', scalarSenHeatCanopy - !write(*,'(a,f25.15)') 'scalarLatHeatCanopyEvap = ', scalarLatHeatCanopyEvap - !write(*,'(a,f25.15)') 'scalarLatHeatCanopyTrans = ', scalarLatHeatCanopyTrans - - !print*, 'scalarSenHeatGround = ', scalarSenHeatGround - !print*, 'scalarLatHeatGround = ', scalarLatHeatGround - - !notUsed_scalarCanopyStabilityCorrection ! stability correction for the canopy (-) - !notUsed_scalarGroundStabilityCorrection ! stability correction for the ground surface (-) - !notUsed_EddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) - !notUsed_FrictionVelocity ! friction velocity (m s-1) - !notUsed_WindspdCanopyTop ! windspeed at the top of the canopy (m s-1) - !notUsed_WindspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) - !trialLeafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) - !trialGroundResistance ! below canopy aerodynamic resistance (s m-1) - !trialCanopyResistance ! above canopy aerodynamic resistance (s m-1) - - ! save perturbed fluxes - if(ixDerivMethod == numerical)then - select case(itry) ! (select type of perturbation) - case(unperturbed) - try0 = turbFluxGround - exit - case(perturbStateCanair) - turbFluxCanair_dStateCanair = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) - turbFluxCanopy_dStateCanair = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) - turbFluxGround_dStateCanair = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) - latHeatCanEvap_dStateCanair = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) - case(perturbStateCanopy) - turbFluxCanair_dStateCanopy = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) - turbFluxCanopy_dStateCanopy = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) - turbFluxGround_dStateCanopy = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) - latHeatCanEvap_dStateCanopy = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) - case(perturbStateGround) - try1 = turbFluxGround - turbFluxCanair_dStateGround = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) - turbFluxCanopy_dStateGround = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) - turbFluxGround_dStateGround = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) - latHeatCanEvap_dStateGround = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) - case(perturbStateCanWat) - turbFluxCanair_dStateCanWat = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) - turbFluxCanopy_dStateCanWat = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) - turbFluxGround_dStateCanWat = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) - latHeatCanEvap_dStateCanWat = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) - case default; err=10; message=trim(message)//"unknown perturbation"; return - end select ! (type of perturbation) + ! -------------------------------------------------------------------------------------- + ! save perturbed fluxes to calculate numerical derivatives (one-sided finite difference) + ! -------------------------------------------------------------------------------------- + if(ixDerivMethod==numerical)then + select case(itry) ! (select type of perturbation) + case(unperturbed); exit + case(perturbStateCanopy) + LWNetCanopy_dStateCanopy = LWNetCanopy + LWNetGround_dStateCanopy = LWNetGround + case(perturbStateGround) + LWNetCanopy_dStateGround = LWNetCanopy + LWNetGround_dStateGround = LWNetGround + case default; err=10; message=trim(message)//"unknown perturbation"; return + end select ! (type of perturbation) end if ! (if numerical) - end do ! (looping through different flux perturbations) - - ! test derivative - !if(ixDerivMethod == numerical) print*, 'try0, try1 = ', try0, try1 - !if(ixDerivMethod == numerical) print*, 'derivative = ', (ixDerivMethod == numerical), (try1 - try0)/dx - !if(ixDerivMethod == analytical) print*, 'derivative = ', (ixDerivMethod == numerical), dTurbFluxGround_dTGround - !pause - - ! compute numerical derivatives - if(ixDerivMethod == numerical)then - ! derivatives w.r.t. canopy air temperature - dTurbFluxCanair_dTCanair = (turbFluxCanair_dStateCanair - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanair = (turbFluxCanopy_dStateCanair - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxGround_dTCanair = (turbFluxGround_dStateCanair - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTCanair = (latHeatCanEvap_dStateCanair - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) - ! derivatives w.r.t. canopy temperature - dTurbFluxCanair_dTCanopy = (turbFluxCanair_dStateCanopy - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanopy = (turbFluxCanopy_dStateCanopy - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxGround_dTCanopy = (turbFluxGround_dStateCanopy - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTCanopy = (latHeatCanEvap_dStateCanopy - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) - ! derivatives w.r.t. ground temperature - dTurbFluxCanair_dTGround = (turbFluxCanair_dStateGround - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxCanopy_dTGround = (turbFluxCanopy_dStateGround - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxGround_dTGround = (turbFluxGround_dStateGround - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTGround = (latHeatCanEvap_dStateGround - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) - ! derivatives w.r.t. canopy total water content - dTurbFluxCanair_dCanWat = (turbFluxCanair_dStateCanWat - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) - dTurbFluxCanopy_dCanWat = (turbFluxCanopy_dStateCanWat - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - dTurbFluxGround_dCanWat = (turbFluxGround_dStateCanWat - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - dLatHeatCanopyEvap_dCanWat = (latHeatCanEvap_dStateCanWat - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy total water content (J kg-1 s-1) - end if - !if(heightCanopyBottom < scalarSnowDepth+z0Ground) pause 'bottom of the canopy is covered' - - ! test - !print*, (ixDerivMethod == numerical) - !print*, 'dTurbFluxCanair_dTCanair = ', dTurbFluxCanair_dTCanair - !print*, 'dTurbFluxCanair_dTCanopy = ', dTurbFluxCanair_dTCanopy - !print*, 'dTurbFluxCanair_dTGround = ', dTurbFluxCanair_dTGround - !print*, 'dTurbFluxCanopy_dTCanair = ', dTurbFluxCanopy_dTCanair - !print*, 'dTurbFluxCanopy_dTCanopy = ', dTurbFluxCanopy_dTCanopy - !print*, 'dTurbFluxCanopy_dTGround = ', dTurbFluxCanopy_dTGround - !print*, 'dTurbFluxGround_dTCanair = ', dTurbFluxGround_dTCanair - !print*, 'dTurbFluxGround_dTCanopy = ', dTurbFluxGround_dTCanopy - !print*, 'dTurbFluxGround_dTGround = ', dTurbFluxGround_dTGround - !print*, 'dLatHeatCanopyEvap_dCanWat = ', dLatHeatCanopyEvap_dCanWat - !print*, 'dLatHeatCanopyEvap_dTCanair = ', dLatHeatCanopyEvap_dTCanair - !print*, 'dLatHeatCanopyEvap_dTCanopy = ', dLatHeatCanopyEvap_dTCanopy - !print*, 'dLatHeatCanopyEvap_dTGround = ', dLatHeatCanopyEvap_dTGround - !print*, 'dTurbFluxCanair_dCanWat = ', dTurbFluxCanair_dCanWat - !print*, 'dTurbFluxCanopy_dCanWat = ', dTurbFluxCanopy_dCanWat - !print*, 'dTurbFluxGround_dCanWat = ', dTurbFluxGround_dCanWat - !print*, '*****' - !pause - - !print*, 'scalarRainfall, scalarThroughfallRain, scalarSnowfall, scalarThroughfallSnow, canopyTempTrial, scalarTwetbulb = ', & - ! scalarRainfall, scalarThroughfallRain, scalarSnowfall, scalarThroughfallSnow, canopyTempTrial, scalarTwetbulb - - ! compute the heat advected with precipitation (W m-2) - ! NOTE: fluxes are in kg m-2 s-1, so no need to use density of water/ice here - scalarCanopyAdvectiveHeatFlux = -Cp_water*(scalarRainfall - scalarThroughfallRain)*(canopyTempTrial - scalarTwetbulb) + & - (-Cp_ice)*(scalarSnowfall - scalarThroughfallSnow)*(canopyTempTrial - scalarTwetbulb) - scalarGroundAdvectiveHeatFlux = -Cp_water*scalarThroughfallRain*(groundTempTrial - scalarTwetbulb) + & - (-Cp_ice)*scalarThroughfallSnow*(groundTempTrial - scalarTwetbulb) !+ & - ! (-Cp_water)*scalarCanopyLiqDrainage *(groundTempTrial - canopyTempTrial) + & - ! (-Cp_ice) *scalarCanopySnowUnloading*(groundTempTrial - canopyTempTrial) - !print*, 'scalarRainfall, scalarThroughfallRain, scalarSnowfall, scalarThroughfallSnow = ', scalarRainfall, scalarThroughfallRain, scalarSnowfall, scalarThroughfallSnow - !print*, 'scalarCanopyAdvectiveHeatFlux, scalarGroundAdvectiveHeatFlux = ', scalarCanopyAdvectiveHeatFlux, scalarGroundAdvectiveHeatFlux - - ! compute the mass flux associated with transpiration and evaporation/sublimation (J m-2 s-1 --> kg m-2 s-1) - ! NOTE: remove water from the snow on the ground in preference to removing water from the water in soil pores - !print*, 'scalarLatHeatCanopyTrans = ', scalarLatHeatCanopyTrans - !print*, 'scalarLatHeatGround = ', scalarLatHeatGround - ! (canopy transpiration/sublimation) - if(scalarLatHeatSubVapCanopy > LH_vap+verySmall)then ! sublimation - scalarCanopyEvaporation = 0._rkind - scalarCanopySublimation = scalarLatHeatCanopyEvap/LH_sub - if(scalarLatHeatCanopyTrans > 0._rkind)then ! flux directed towards the veg - scalarCanopySublimation = scalarCanopySublimation + scalarLatHeatCanopyTrans/LH_sub ! frost - scalarCanopyTranspiration = 0._rkind - else - scalarCanopyTranspiration = scalarLatHeatCanopyTrans/LH_vap ! transpiration is always vapor - end if - ! (canopy transpiration/evaporation) - else ! evaporation - scalarCanopyEvaporation = scalarLatHeatCanopyEvap/LH_vap - scalarCanopySublimation = 0._rkind - if(scalarLatHeatCanopyTrans > 0._rkind)then ! flux directed towards the veg - scalarCanopyEvaporation = scalarCanopyEvaporation + scalarLatHeatCanopyTrans/LH_vap - scalarCanopyTranspiration = 0._rkind - else - scalarCanopyTranspiration = scalarLatHeatCanopyTrans/LH_vap - end if - end if - ! (ground evaporation/sublimation) - if(scalarLatHeatSubVapGround > LH_vap+verySmall)then ! sublimation - ! NOTE: this should only occur when we have formed snow layers, so check - if(nSnow == 0)then; err=20; message=trim(message)//'only expect snow sublimation when we have formed some snow layers'; return; end if - scalarGroundEvaporation = 0._rkind ! ground evaporation is zero once the snowpack has formed - scalarSnowSublimation = scalarLatHeatGround/LH_sub - else - ! NOTE: this should only occur when we have no snow layers, so check - if(nSnow > 0)then; err=20; message=trim(message)//'only expect ground evaporation when there are no snow layers'; return; end if - scalarGroundEvaporation = scalarLatHeatGround/LH_vap - scalarSnowSublimation = 0._rkind ! no sublimation from snow if no snow layers have formed - end if - !print*, 'scalarSnowSublimation, scalarLatHeatGround = ', scalarSnowSublimation, scalarLatHeatGround - - !print*, 'canopyWetFraction, scalarCanopyEvaporation = ', canopyWetFraction, scalarCanopyEvaporation - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! ***** AND STITCH EVERYTHING TOGETHER ***************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - - ! compute derived fluxes - scalarTotalET = scalarGroundEvaporation + scalarCanopyEvaporation + scalarCanopyTranspiration - scalarNetRadiation = scalarCanopyAbsorbedSolar + scalarLWNetCanopy + scalarGroundAbsorbedSolar + scalarLWNetGround - - ! compute net fluxes at the canopy and ground surface - canairNetFlux = turbFluxCanair - canopyNetFlux = scalarCanopyAbsorbedSolar + scalarLWNetCanopy + turbFluxCanopy + scalarCanopyAdvectiveHeatFlux - groundNetFlux = scalarGroundAbsorbedSolar + scalarLWNetGround + turbFluxGround + scalarGroundAdvectiveHeatFlux - !write(*,'(a,1x,10(e17.10,1x))') 'canopyNetFlux, groundNetFlux, scalarLWNetCanopy, turbFluxCanopy, turbFluxGround, scalarLWNetGround, scalarCanopyAdvectiveHeatFlux = ', & - ! canopyNetFlux, groundNetFlux, scalarLWNetCanopy, turbFluxCanopy, turbFluxGround, scalarLWNetGround, scalarCanopyAdvectiveHeatFlux - !write(*,'(a,1x,10(e20.14,1x))') 'groundNetFlux, scalarGroundAbsorbedSolar, scalarLWNetGround, turbFluxGround, scalarGroundAdvectiveHeatFlux = ', & - ! groundNetFlux, scalarGroundAbsorbedSolar, scalarLWNetGround, turbFluxGround, scalarGroundAdvectiveHeatFlux - - ! compute the energy derivatives - dCanairNetFlux_dCanairTemp = dTurbFluxCanair_dTCanair - dCanairNetFlux_dCanopyTemp = dTurbFluxCanair_dTCanopy - dCanairNetFlux_dGroundTemp = dTurbFluxCanair_dTGround - dCanopyNetFlux_dCanairTemp = dTurbFluxCanopy_dTCanair - dCanopyNetFlux_dCanopyTemp = dLWNetCanopy_dTCanopy + dTurbFluxCanopy_dTCanopy - Cp_water*(scalarRainfall - scalarThroughfallRain) - Cp_ice*(scalarSnowfall - scalarThroughfallSnow) - dCanopyNetFlux_dGroundTemp = dLWNetCanopy_dTGround + dTurbFluxCanopy_dTGround - dGroundNetFlux_dCanairTemp = dTurbFluxGround_dTCanair - dGroundNetFlux_dCanopyTemp = dLWNetGround_dTCanopy + dTurbFluxGround_dTCanopy - dGroundNetFlux_dGroundTemp = dLWNetGround_dTGround + dTurbFluxGround_dTGround - Cp_water*scalarThroughfallRain - Cp_ice*scalarThroughfallSnow - - ! check if evaporation or sublimation - if(scalarLatHeatSubVapCanopy < LH_vap+verySmall)then ! evaporation - - ! compute the liquid water derivarives - dCanopyEvaporation_dCanWat = dLatHeatCanopyEvap_dCanWat/LH_vap ! (s-1) - dCanopyEvaporation_dTCanair = dLatHeatCanopyEvap_dTCanair/LH_vap ! (kg m-2 s-1 K-1) - dCanopyEvaporation_dTCanopy = dLatHeatCanopyEvap_dTCanopy/LH_vap ! (kg m-2 s-1 K-1) - dCanopyEvaporation_dTGround = dLatHeatCanopyEvap_dTGround/LH_vap ! (kg m-2 s-1 K-1) - - ! sublimation - else - dCanopyEvaporation_dCanWat = 0._rkind ! (s-1) - dCanopyEvaporation_dTCanair = 0._rkind ! (kg m-2 s-1 K-1) - dCanopyEvaporation_dTCanopy = 0._rkind ! (kg m-2 s-1 K-1) - dCanopyEvaporation_dTGround = 0._rkind ! (kg m-2 s-1 K-1) - end if - - ! transpiration - if(scalarLatHeatCanopyTrans > 0._rkind)then ! flux directed towards the veg - dCanopyTrans_dCanWat = 0._rkind - dCanopyTrans_dTCanair= 0._rkind - dCanopyTrans_dTCanopy= 0._rkind - dCanopyTrans_dTGround= 0._rkind - else - dCanopyTrans_dCanWat= dLatHeatCanopyTrans_dCanWat/LH_vap ! transpiration is always vapor - dCanopyTrans_dTCanair= dLatHeatCanopyTrans_dTCanair/LH_vap - dCanopyTrans_dTCanopy= dLatHeatCanopyTrans_dTCanopy/LH_vap - dCanopyTrans_dTGround= dLatHeatCanopyTrans_dTGround/LH_vap - end if + end do ! looping through different perturbations + ! ------------------------------------------------------------------------------------- + ! compute derivatives + ! ------------------------------------------------------------------------------------- + select case(ixDerivMethod) + + ! ***** analytical derivatives + case(analytical) + ! compute initial derivatives + dLWRadCanopy_dTCanopy = 4._rkind*emc*sb*TCan**3._rkind + dLWRadGround_dTGround = 4._rkind*emg*sb*TGnd**3._rkind + ! compute analytical derivatives + dLWNetCanopy_dTCanopy = (emc*(1._rkind - emg) - 2._rkind)*dLWRadCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) + dLWNetGround_dTGround = -dLWRadGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) + dLWNetCanopy_dTGround = emc*dLWRadGround_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) + dLWNetGround_dTCanopy = emg*dLWRadCanopy_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) + + ! ***** numerical derivatives + case(numerical) + ! compute numerical derivatives (one-sided finite differences) + dLWNetCanopy_dTCanopy = (LWNetCanopy_dStateCanopy - LWNetCanopy)/dx ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) + dLWNetGround_dTGround = (LWNetGround_dStateGround - LWNetGround)/dx ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) + dLWNetCanopy_dTGround = (LWNetCanopy_dStateGround - LWNetCanopy)/dx ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) + dLWNetGround_dTCanopy = (LWNetGround_dStateCanopy - LWNetGround)/dx ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) + + ! ***** error check + case default; err=10; message=trim(message)//"unknown method to calculate derivatives"; return + + end select ! (type of method to calculate derivatives) + +end subroutine longwaveBal + + +! ******************************************************************************************************* +! private subroutine aeroResist: compute aerodynamic resistances +! ******************************************************************************************************* +subroutine aeroResist(& + ! input: model control + computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) + derivDesired, & ! intent(in): flag to indicate if analytical derivatives are desired + ixVegTraits, & ! intent(in): choice of parameterization for vegetation roughness length and displacement height + ixWindProfile, & ! intent(in): choice of canopy wind profile + ixStability, & ! intent(in): choice of stability function + ! input: above-canopy forcing data + mHeight, & ! intent(in): measurement height (m) + airtemp, & ! intent(in): air temperature at some height above the surface (K) + windspd, & ! intent(in): wind speed at some height above the surface (m s-1) + ! input: temperature (canopy, ground, canopy air space) + canairTemp, & ! intent(in): temperature of the canopy air space (K) + groundTemp, & ! intent(in): ground temperature (K) + ! input: diagnostic variables + exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) + snowDepth, & ! intent(in): snow depth (m) + ! input: parameters + z0Ground, & ! intent(in): roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) + z0CanopyParam, & ! intent(in): roughness length of the canopy (m) + zpdFraction, & ! intent(in): zero plane displacement / canopy height (-) + critRichNumber, & ! intent(in): critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam, & ! intent(in): parameter in Louis (1979) stability function + Mahrt87_eScale, & ! intent(in): exponential scaling factor in the Mahrt (1987) stability function + windReductionParam, & ! intent(in): canopy wind reduction parameter (-) + leafExchangeCoeff, & ! intent(in): turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) + leafDimension, & ! intent(in): characteristic leaf dimension (m) + heightCanopyTop, & ! intent(in): height at the top of the vegetation canopy (m) + heightCanopyBottom, & ! intent(in): height at the bottom of the vegetation canopy (m) + ! output: stability corrections + RiBulkCanopy, & ! intent(out): bulk Richardson number for the canopy (-) + RiBulkGround, & ! intent(out): bulk Richardson number for the ground surface (-) + canopyStabilityCorrection, & ! intent(out): stability correction for the canopy (-) + groundStabilityCorrection, & ! intent(out): stability correction for the ground surface (-) + ! output: scalar resistances + z0Canopy, & ! intent(out): roughness length of the canopy (m) + windReductionFactor, & ! intent(out): canopy wind reduction factor (-) + zeroPlaneDisplacement, & ! intent(out): zero plane displacement (m) + eddyDiffusCanopyTop, & ! intent(out): eddy diffusivity for heat at the top of the canopy (m2 s-1) + frictionVelocity, & ! intent(out): friction velocity (m s-1) + windspdCanopyTop, & ! intent(out): windspeed at the top of the canopy (m s-1) + windspdCanopyBottom, & ! intent(out): windspeed at the height of the bottom of the canopy (m s-1) + leafResistance, & ! intent(out): mean leaf boundary layer resistance per unit leaf area (s m-1) + groundResistance, & ! intent(out): below canopy aerodynamic resistance (s m-1) + canopyResistance, & ! intent(out): above canopy aerodynamic resistance (s m-1) + ! output: derivatives in scalar resistances + dGroundResistance_dTGround, & ! intent(out): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + dGroundResistance_dTCanopy, & ! intent(out): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + dGroundResistance_dTCanair, & ! intent(out): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + dCanopyResistance_dTCanopy, & ! intent(out): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + dCanopyResistance_dTCanair, & ! intent(out): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + ! output: error control + err,message ) ! intent(out): error control + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! compute aerodynamic resistances + ! Refs: Choudhury and Monteith (4-layer model for heat budget of homogenous surfaces; QJRMS, 1988) + ! Niu and Yang (Canopy effects on snow processes; JGR, 2004) + ! Mahat et al. (Below-canopy turbulence in a snowmelt model, WRR, 2012) + implicit none + ! input: model control + logical(lgt),intent(in) :: computeVegFlux ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) + logical(lgt),intent(in) :: derivDesired ! logical flag to indicate if analytical derivatives are desired + integer(i4b),intent(in) :: ixVegTraits ! choice of parameterization for vegetation roughness length and displacement height + integer(i4b),intent(in) :: ixWindProfile ! choice of canopy wind profile + integer(i4b),intent(in) :: ixStability ! choice of stability function + ! input: above-canopy forcing data + real(rkind),intent(in) :: mHeight ! measurement height (m) + real(rkind),intent(in) :: airtemp ! air temperature at some height above the surface (K) + real(rkind),intent(in) :: windspd ! wind speed at some height above the surface (m s-1) + ! input: temperature (canopy, ground, canopy air space) + real(rkind),intent(in) :: canairTemp ! temperature of the canopy air space (K) + real(rkind),intent(in) :: groundTemp ! ground temperature (K) + ! input: diagnostic variables + real(rkind),intent(in) :: exposedVAI ! exposed vegetation area index -- leaf plus stem (m2 m-2) + real(rkind),intent(in) :: snowDepth ! snow depth (m) + ! input: parameters + real(rkind),intent(in) :: z0Ground ! roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) + real(rkind),intent(in) :: z0CanopyParam ! roughness length of the canopy (m) + real(rkind),intent(in) :: zpdFraction ! zero plane displacement / canopy height (-) + real(rkind),intent(in) :: critRichNumber ! critical value for the bulk Richardson number where turbulence ceases (-) + real(rkind),intent(in) :: Louis79_bparam ! parameter in Louis (1979) stability function + real(rkind),intent(in) :: Mahrt87_eScale ! exponential scaling factor in the Mahrt (1987) stability function + real(rkind),intent(in) :: windReductionParam ! canopy wind reduction parameter (-) + real(rkind),intent(in) :: leafExchangeCoeff ! turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) + real(rkind),intent(in) :: leafDimension ! characteristic leaf dimension (m) + real(rkind),intent(in) :: heightCanopyTop ! height at the top of the vegetation canopy (m) + real(rkind),intent(in) :: heightCanopyBottom ! height at the bottom of the vegetation canopy (m) + ! output: stability corrections + real(rkind),intent(out) :: RiBulkCanopy ! bulk Richardson number for the canopy (-) + real(rkind),intent(out) :: RiBulkGround ! bulk Richardson number for the ground surface (-) + real(rkind),intent(out) :: canopyStabilityCorrection ! stability correction for the canopy (-) + real(rkind),intent(out) :: groundStabilityCorrection ! stability correction for the ground surface (-) + ! output: scalar resistances + real(rkind),intent(out) :: z0Canopy ! roughness length of the vegetation canopy (m) + real(rkind),intent(out) :: windReductionFactor ! canopy wind reduction factor (-) + real(rkind),intent(out) :: zeroPlaneDisplacement ! zero plane displacement (m) + real(rkind),intent(out) :: eddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) + real(rkind),intent(out) :: frictionVelocity ! friction velocity (m s-1) + real(rkind),intent(out) :: windspdCanopyTop ! windspeed at the top of the canopy (m s-1) + real(rkind),intent(out) :: windspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) + real(rkind),intent(out) :: leafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) + real(rkind),intent(out) :: groundResistance ! below canopy aerodynamic resistance (s m-1) + real(rkind),intent(out) :: canopyResistance ! above canopy aerodynamic resistance (s m-1) + ! output: derivatives in scalar resistances + real(rkind),intent(out) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + real(rkind),intent(out) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind),intent(out) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + real(rkind),intent(out) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind),intent(out) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! local variables: general + character(LEN=256) :: cmessage ! error message of downwind routine + ! local variables: vegetation roughness and dispalcement height + real(rkind),parameter :: oneThird=1._rkind/3._rkind ! 1/3 + real(rkind),parameter :: twoThirds=2._rkind/3._rkind ! 2/3 + real(rkind),parameter :: C_r = 0.3 ! roughness element drag coefficient (-) from Raupach (BLM, 1994) + real(rkind),parameter :: C_s = 0.003_rkind ! substrate surface drag coefficient (-) from Raupach (BLM, 1994) + real(rkind),parameter :: approxDragCoef_max = 0.3_rkind ! maximum value of the approximate drag coefficient (-) from Raupach (BLM, 1994) + real(rkind),parameter :: psi_h = 0.193_rkind ! roughness sub-layer influence function (-) from Raupach (BLM, 1994) + real(rkind),parameter :: c_d1 = 7.5_rkind ! scaling parameter used to define displacement height (-) from Raupach (BLM, 1994) + real(rkind),parameter :: cd_CM = 0.2_rkind ! mean drag coefficient for individual leaves (-) from Choudhury and Monteith (QJRMS, 1988) + real(rkind) :: funcLAI ! temporary variable to calculate zero plane displacement for the canopy + real(rkind) :: fracCanopyHeight ! zero plane displacement expressed as a fraction of canopy height + real(rkind) :: approxDragCoef ! approximate drag coefficient used in the computation of canopy roughness length (-) + ! local variables: resistance + real(rkind) :: canopyExNeut ! surface-atmosphere exchange coefficient under neutral conditions (-) + real(rkind) :: groundExNeut ! surface-atmosphere exchange coefficient under neutral conditions (-) + real(rkind) :: sfc2AtmExchangeCoeff_canopy ! surface-atmosphere exchange coefficient after stability corrections (-) + real(rkind) :: groundResistanceNeutral ! ground resistance under neutral conditions (s m-1) + real(rkind) :: windConvFactor_fv ! factor to convert friction velocity to wind speed at top of canopy (-) + real(rkind) :: windConvFactor ! factor to convert wind speed at top of canopy to wind speed at a given height in the canopy (-) + real(rkind) :: referenceHeight ! z0Canopy+zeroPlaneDisplacement (m) + real(rkind) :: windspdRefHeight ! windspeed at the reference height (m/s) + real(rkind) :: heightAboveGround ! height above the snow surface (m) + real(rkind) :: heightCanopyTopAboveSnow ! height at the top of the vegetation canopy relative to snowpack (m) + real(rkind) :: heightCanopyBottomAboveSnow ! height at the bottom of the vegetation canopy relative to snowpack (m) + real(rkind),parameter :: xTolerance=0.1_rkind ! tolerance to handle the transition from exponential to log-below canopy + ! local variables: derivatives + real(rkind) :: dFV_dT ! derivative in friction velocity w.r.t. canopy air temperature + real(rkind) :: dED_dT ! derivative in eddy diffusivity at the top of the canopy w.r.t. canopy air temperature + real(rkind) :: dGR_dT ! derivative in neutral ground resistance w.r.t. canopy air temperature + real(rkind) :: tmp1,tmp2 ! temporary variables used in calculation of ground resistance + real(rkind) :: dCanopyStabilityCorrection_dRich ! derivative in stability correction w.r.t. Richardson number for the canopy (-) + real(rkind) :: dGroundStabilityCorrection_dRich ! derivative in stability correction w.r.t. Richardson number for the ground surface (-) + real(rkind) :: dCanopyStabilityCorrection_dAirTemp ! (not used) derivative in stability correction w.r.t. air temperature (K-1) + real(rkind) :: dGroundStabilityCorrection_dAirTemp ! (not used) derivative in stability correction w.r.t. air temperature (K-1) + real(rkind) :: dCanopyStabilityCorrection_dCasTemp ! derivative in canopy stability correction w.r.t. canopy air space temperature (K-1) + real(rkind) :: dGroundStabilityCorrection_dCasTemp ! derivative in ground stability correction w.r.t. canopy air space temperature (K-1) + real(rkind) :: dGroundStabilityCorrection_dSfcTemp ! derivative in ground stability correction w.r.t. surface temperature (K-1) + real(rkind) :: singleLeafConductance ! leaf boundary layer conductance (m s-1) + real(rkind) :: canopyLeafConductance ! leaf boundary layer conductance -- scaled up to the canopy (m s-1) + real(rkind) :: leaf2CanopyScaleFactor ! factor to scale from the leaf to the canopy [m s-(1/2)] + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='aeroResist/' - ! compute the liquid water derivarives (ground evap) - dGroundEvaporation_dCanWat = dLatHeatGroundEvap_dCanWat/LH_vap ! (s-1) - dGroundEvaporation_dTCanair = dLatHeatGroundEvap_dTCanair/LH_vap ! (kg m-2 s-1 K-1) - dGroundEvaporation_dTCanopy = dLatHeatGroundEvap_dTCanopy/LH_vap ! (kg m-2 s-1 K-1) - dGroundEvaporation_dTGround = dLatHeatGroundEvap_dTGround/LH_vap ! (kg m-2 s-1 K-1) - - ! compute the cross derivative terms (only related to turbulent fluxes; specifically canopy evaporation and transpiration) - dCanopyNetFlux_dCanWat = dTurbFluxCanopy_dCanWat ! derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) - dGroundNetFlux_dCanWat = dTurbFluxGround_dCanWat ! derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) - - !print*, (ixDerivMethod == numerical) - !print*, 'dGroundNetFlux_dCanairTemp = ', dGroundNetFlux_dCanairTemp - !print*, 'dCanopyNetFlux_dCanopyTemp = ', dCanopyNetFlux_dCanopyTemp - !print*, 'dGroundNetFlux_dCanopyTemp = ', dGroundNetFlux_dCanopyTemp - !print*, 'dCanopyNetFlux_dGroundTemp = ', dCanopyNetFlux_dGroundTemp - !print*, 'dGroundNetFlux_dGroundTemp = ', dGroundNetFlux_dGroundTemp - !print*, 'dLWNetCanopy_dTGround = ', dLWNetCanopy_dTGround - !print*, 'dTurbFluxCanopy_dTGround = ', dTurbFluxCanopy_dTGround - !pause - - ! * check - case default; err=10; message=trim(message)//'unable to identify upper boundary condition for thermodynamics'; return - - ! end case statement - end select ! upper boundary condition for thermodynamics - - ! return liquid fluxes (needed for coupling) - returnCanopyTranspiration = scalarCanopyTranspiration ! canopy transpiration (kg m-2 s-1) - returnCanopyEvaporation = scalarCanopyEvaporation ! canopy evaporation/condensation (kg m-2 s-1) - returnGroundEvaporation = scalarGroundEvaporation ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - - ! end associations - end associate - - - end subroutine vegNrgFlux - - - ! ******************************************************************************************************* - ! public subroutine wettedFrac: compute wetted fraction of the canopy - ! ******************************************************************************************************* - subroutine wettedFrac(& - ! input - deriv, & ! flag to denote if derivative is desired - derNum, & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) - frozen, & ! flag to denote if the canopy is frozen - dLiq_dT, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) - fracLiq, & ! fraction of liquid water on the canopy (-) - canopyLiq, & ! canopy liquid water (kg m-2) - canopyIce, & ! canopy ice (kg m-2) - canopyLiqMax, & ! maximum canopy liquid water (kg m-2) - canopyIceMax, & ! maximum canopy ice content (kg m-2) - canopyWettingFactor, & ! maximum wetted fraction of the canopy (-) - canopyWettingExp, & ! exponent in canopy wetting function (-) - ! output - canopyWetFraction, & ! canopy wetted fraction (-) - dCanopyWetFraction_dWat,& ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) - dCanopyWetFraction_dT, & ! derivative in wetted fraction w.r.t. canopy temperature (K-1) - err,message) ! error control - implicit none - ! input - logical(lgt),intent(in) :: deriv ! flag to denote if derivative is desired - logical(lgt),intent(in) :: derNum ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) - logical(lgt),intent(in) :: frozen ! flag to denote if the canopy is frozen - real(rkind),intent(in) :: dLiq_dT ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) - real(rkind),intent(in) :: fracLiq ! fraction of liquid water on the canopy (-) - real(rkind),intent(in) :: canopyLiq ! canopy liquid water (kg m-2) - real(rkind),intent(in) :: canopyIce ! canopy ice (kg m-2) - real(rkind),intent(in) :: canopyLiqMax ! maximum canopy liquid water (kg m-2) - real(rkind),intent(in) :: canopyIceMax ! maximum canopy ice content (kg m-2) - real(rkind),intent(in) :: canopyWettingFactor ! maximum wetted fraction of the canopy (-) - real(rkind),intent(in) :: canopyWettingExp ! exponent in canopy wetting function (-) - ! output - real(rkind),intent(out) :: canopyWetFraction ! canopy wetted fraction (-) - real(rkind),intent(out) :: dCanopyWetFraction_dWat ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) - real(rkind),intent(out) :: dCanopyWetFraction_dT ! derivative in wetted fraction w.r.t. canopy temperature (K-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! local variables - logical(lgt),parameter :: smoothing=.true. ! flag to denote that smoothing is required - real(rkind) :: canopyWetFractionPert ! canopy wetted fraction after state perturbations (-) - real(rkind) :: canopyWetFractionDeriv ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) - ! ----------------------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='wettedFrac/' - - ! compute case where the canopy is frozen - if(frozen)then - ! compute fraction of liquid water on the canopy - call wetFraction((deriv .and. .not.derNum),smoothing,canopyIce,canopyIceMax,canopyWettingFactor,canopyWettingExp,canopyWetFraction,canopyWetFractionDeriv) - ! compute numerical derivative, if derivative is desired - if(deriv.and.derNum)then - call wetFraction((deriv .and. .not.derNum),smoothing,canopyIce+dx,canopyIceMax,canopyWettingFactor,canopyWettingExp,canopyWetFractionPert,canopyWetFractionDeriv) - canopyWetFractionDeriv = (canopyWetFractionPert - canopyWetFraction)/dx + ! check that measurement height is above the top of the canopy + if(mHeight < heightCanopyTop)then + err=20; message=trim(message)//'measurement height is below the top of the canopy'; return end if - ! scale derivative by the fraction of water - ! NOTE: dIce/dWat = (1._rkind - fracLiq), hence dWet/dWat = dIce/dWat . dWet/dLiq - dCanopyWetFraction_dWat = canopyWetFractionDeriv*(1._rkind - fracLiq) - dCanopyWetFraction_dT = -canopyWetFractionDeriv*dLiq_dT ! NOTE: dIce/dT = -dLiq/dT - return - end if - ! compute fraction of liquid water on the canopy - ! NOTE: if(.not.deriv) canopyWetFractionDeriv = 0._rkind - call wetFraction((deriv .and. .not.derNum),smoothing,canopyLiq,canopyLiqMax,canopyWettingFactor,canopyWettingExp,canopyWetFraction,canopyWetFractionDeriv) + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! * compute vegetation poperties (could be done at the same time as phenology.. does not have to be in the flux routine!) + if(computeVegFlux) then ! (if vegetation is exposed) - ! compute numerical derivative - if(deriv.and.derNum)then - call wetFraction((deriv .and. .not.derNum),smoothing,canopyLiq+dx,canopyLiqMax,canopyWettingFactor,canopyWettingExp,canopyWetFractionPert,canopyWetFractionDeriv) - canopyWetFractionDeriv = (canopyWetFractionPert - canopyWetFraction)/dx - end if + ! ***** identify zero plane displacement, roughness length, and surface temperature for the canopy (m) + ! First, calculate new coordinate system above snow - use these to scale wind profiles and resistances + ! NOTE: the new coordinate system makes zeroPlaneDisplacement and z0Canopy consistent + heightCanopyTopAboveSnow = heightCanopyTop - snowDepth + heightCanopyBottomAboveSnow = max(heightCanopyBottom - snowDepth, 0.0_rkind) + select case(ixVegTraits) + + ! Raupach (BLM 1994) "Simplified expressions..." + case(Raupach_BLM1994) + ! (compute zero-plane displacement) + funcLAI = sqrt(c_d1*exposedVAI) + fracCanopyHeight = -(1._rkind - exp(-funcLAI))/funcLAI + 1._rkind + zeroPlaneDisplacement = fracCanopyHeight*(heightCanopyTopAboveSnow-heightCanopyBottomAboveSnow)+heightCanopyBottomAboveSnow + ! (coupute roughness length of the veg canopy) + approxDragCoef = min( sqrt(C_s + C_r*exposedVAI/2._rkind), approxDragCoef_max) + z0Canopy = (1._rkind - fracCanopyHeight) * exp(-vkc*approxDragCoef - psi_h) * (heightCanopyTopAboveSnow-heightCanopyBottomAboveSnow) + + ! Choudhury and Monteith (QJRMS 1988) "A four layer model for the heat budget..." + case(CM_QJRMS1988) + funcLAI = cd_CM*exposedVAI + zeroPlaneDisplacement = 1.1_rkind*heightCanopyTopAboveSnow*log(1._rkind + funcLAI**0.25_rkind) + if(funcLAI < 0.2_rkind)then + z0Canopy = z0Ground + 0.3_rkind*heightCanopyTopAboveSnow*funcLAI**0.5_rkind + else + z0Canopy = 0.3_rkind*heightCanopyTopAboveSnow*(1._rkind - zeroPlaneDisplacement/heightCanopyTopAboveSnow) + end if + + ! constant parameters dependent on the vegetation type + case(vegTypeTable) + zeroPlaneDisplacement = zpdFraction*heightCanopyTopAboveSnow ! zero-plane displacement (m) + z0Canopy = z0CanopyParam ! roughness length of the veg canopy (m) + + ! check + case default + err=10; message=trim(message)//"unknown parameterization for vegetation roughness length and displacement height"; return + + end select ! vegetation traits (z0, zpd) + + ! check zero plane displacement + if(zeroPlaneDisplacement < heightCanopyBottomAboveSnow)then + write(*,'(a,1x,10(f12.5,1x))') 'heightCanopyTop, snowDepth, heightCanopyTopAboveSnow, heightCanopyBottomAboveSnow, exposedVAI = ', & + heightCanopyTop, snowDepth, heightCanopyTopAboveSnow, heightCanopyBottomAboveSnow, exposedVAI + message=trim(message)//'zero plane displacement is below the canopy bottom' + err=20; return + endif + + ! check measurement height + if(mHeight < zeroPlaneDisplacement)then; err=20; message=trim(message)//'measurement height is below the displacement height'; return; end if + if(mHeight < z0Canopy)then; err=20; message=trim(message)//'measurement height is below the roughness length'; return; end if + + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! * compute resistance for the case where the canopy is exposed + ! compute the stability correction for resistance from canopy air space to air above the canopy (-) + call aStability(& + ! input + derivDesired, & ! input: logical flag to compute analytical derivatives + ixStability, & ! input: choice of stability function + ! input: forcing data, diagnostic and state variables + mHeight, & ! input: measurement height (m) + airTemp, & ! input: air temperature above the canopy (K) + canairTemp, & ! input: temperature of the canopy air space (K) + windspd, & ! input: wind speed above the canopy (m s-1) + ! input: stability parameters + critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam, & ! input: parameter in Louis (1979) stability function + Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function + ! output + RiBulkCanopy, & ! output: bulk Richardson number (-) + canopyStabilityCorrection, & ! output: stability correction for turbulent heat fluxes (-) + dCanopyStabilityCorrection_dRich, & ! output: derivative in stability correction w.r.t. Richardson number for the canopy (-) + dCanopyStabilityCorrection_dAirTemp, & ! output: (not used) derivative in stability correction w.r.t. air temperature (K-1) + dCanopyStabilityCorrection_dCasTemp, & ! output: derivative in stability correction w.r.t. canopy air space temperature (K-1) + err, cmessage ) ! output: error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - ! scale derivative by the fraction of water - ! NOTE: dLiq/dWat = fracLiq, hence dWet/dWat = dLiq/dWat . dWet/dLiq - dCanopyWetFraction_dWat = canopyWetFractionDeriv*fracLiq - dCanopyWetFraction_dT = canopyWetFractionDeriv*dLiq_dT - - ! test - !write(*,'(a,1x,2(L1,1x),10(f20.10,1x))') 'deriv, derNum, canopyWetFraction, canopyWetFractionDeriv = ', deriv, derNum, canopyWetFraction, canopyWetFractionDeriv - !if(deriv) pause 'testing canopy wet fraction' - - end subroutine wettedFrac - - - ! ******************************************************************************************************* - ! private subroutine wetFraction: compute fraction of canopy covered with liquid water - ! ******************************************************************************************************* - subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFactor,canopyWettingExp,canopyWetFraction,canopyWetFractionDeriv) - implicit none - ! dummy variables - logical(lgt),intent(in) :: derDesire ! flag to denote if analytical derivatives are desired - logical(lgt),intent(in) :: smoothing ! flag to denote if smoothing is required - real(rkind),intent(in) :: canopyLiq ! liquid water content (kg m-2) - real(rkind),intent(in) :: canopyMax ! liquid water content (kg m-2) - real(rkind),intent(in) :: canopyWettingFactor ! maximum wetted fraction of the canopy (-) - real(rkind),intent(in) :: canopyWettingExp ! exponent in canopy wetting function (-) - - real(rkind),intent(out) :: canopyWetFraction ! canopy wetted fraction (-) - real(rkind),intent(out) :: canopyWetFractionDeriv ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) - ! local variables - real(rkind) :: relativeCanopyWater ! water stored on vegetation canopy, expressed as a fraction of maximum storage (-) - real(rkind) :: rawCanopyWetFraction ! initial value of the canopy wet fraction (before smoothing) - real(rkind) :: rawWetFractionDeriv ! derivative in canopy wet fraction w.r.t. storage (kg-1 m2) - real(rkind) :: smoothFunc ! smoothing function used to improve numerical stability at times with limited water storage (-) - real(rkind) :: smoothFuncDeriv ! derivative in the smoothing function w.r.t.canopy storage (kg-1 m2) - real(rkind) :: verySmall=epsilon(1._rkind) ! a very small number - ! -------------------------------------------------------------------------------------------------------------- + ! compute turbulent exchange coefficient (-) + canopyExNeut = (vkc**2._rkind) / ( log((mHeight - zeroPlaneDisplacement)/z0Canopy))**2._rkind ! coefficient under conditions of neutral stability + sfc2AtmExchangeCoeff_canopy = canopyExNeut*canopyStabilityCorrection ! after stability corrections - ! compute relative canopy water - relativeCanopyWater = canopyLiq/canopyMax - !write(*,'(a,1x,e20.10,1x,2(f20.10,1x))') 'relativeCanopyWater, canopyLiq, canopyMax = ', relativeCanopyWater, canopyLiq, canopyMax + ! compute the friction velocity (m s-1) + frictionVelocity = windspd * sqrt(sfc2AtmExchangeCoeff_canopy) - ! compute an initial value of the canopy wet fraction - ! - canopy below value where canopy is 100% wet - if(relativeCanopyWater < 1._rkind)then - rawCanopyWetFraction = canopyWettingFactor*(relativeCanopyWater**canopyWettingExp) - if(derDesire .and. relativeCanopyWater>verySmall)then - rawWetFractionDeriv = (canopyWettingFactor*canopyWettingExp/canopyMax)*relativeCanopyWater**(canopyWettingExp - 1._rkind) - else - rawWetFractionDeriv = 0._rkind - end if + ! compute the above-canopy resistance (s m-1) + canopyResistance = 1._rkind/(sfc2AtmExchangeCoeff_canopy*windspd) + if(canopyResistance < 0._rkind)then; err=20; message=trim(message)//'canopy resistance < 0'; return; end if - ! - canopy is at capacity (canopyWettingFactor) - else - rawCanopyWetFraction = canopyWettingFactor - rawWetFractionDeriv = 0._rkind - end if + ! compute windspeed at the top of the canopy above snow depth (m s-1) + ! NOTE: stability corrections cancel out + windConvFactor_fv = log((heightCanopyTopAboveSnow - zeroPlaneDisplacement)/z0Canopy) / log((mHeight - snowDepth - zeroPlaneDisplacement)/z0Canopy) + windspdCanopyTop = windspd*windConvFactor_fv - ! smooth canopy wetted fraction - if(smoothing)then - call logisticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) - canopyWetFraction = rawCanopyWetFraction*smoothFunc ! logistic smoother - else - canopyWetFraction = rawCanopyWetFraction - canopyWetFractionDeriv = rawWetFractionDeriv - end if + ! compute the windspeed reduction + ! Refs: Norman et al. (Ag. Forest Met., 1995) -- citing Goudriaan (1977 manuscript "crop micrometeorology: a simulation study", Wageningen). + windReductionFactor = windReductionParam * exposedVAI**twoThirds * (heightCanopyTopAboveSnow - heightCanopyBottomAboveSnow)**oneThird / leafDimension**oneThird - ! compute derivative (product rule) - if(derDesire .and. smoothing)then ! NOTE: raw derivative is used if not smoothing - canopyWetFractionDeriv = rawWetFractionDeriv*smoothFunc + rawCanopyWetFraction*smoothFuncDeriv - else - canopyWetFractionDeriv = 0._rkind - end if + ! compute windspeed at the height z0Canopy+zeroPlaneDisplacement (m s-1) + referenceHeight = z0Canopy+zeroPlaneDisplacement + windConvFactor = exp(-windReductionFactor*(1._rkind - (referenceHeight/heightCanopyTopAboveSnow))) + windspdRefHeight = windspdCanopyTop*windConvFactor - end subroutine wetFraction - - - ! ******************************************************************************************************* - ! private subroutine logisticSmoother: compute the smoothing function - ! ******************************************************************************************************* - subroutine logisticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) - implicit none - ! dummy variables - logical(lgt),intent(in) :: derDesire ! flag to denote if analytical derivatives are desired - real(rkind),intent(in) :: canopyLiq ! liquid water content (kg m-2) - real(rkind),intent(out) :: smoothFunc ! smoothing function (-) - real(rkind),intent(out) :: smoothFuncDeriv ! derivative in smoothing function (kg-1 m-2) - ! local variables - real(rkind) :: xArg ! argument used in the smoothing function (-) - real(rkind) :: expX ! exp(-xArg) -- used multiple times - real(rkind),parameter :: smoothThresh=0.01_rkind ! mid-point of the smoothing function (kg m-2) - real(rkind),parameter :: smoothScale=0.001_rkind ! scaling factor for the smoothing function (kg m-2) - real(rkind),parameter :: xLimit=50._rkind ! don't compute exponents for > xLimit - ! -------------------------------------------------------------------------------------------------------------- - ! compute argument in the smoothing function - xArg = (canopyLiq - smoothThresh)/smoothScale - - ! only compute smoothing function for small exponents - if(xArg > -xLimit .and. xArg < xLimit)then ! avoid huge exponents - expX = exp(-xarg) ! (also used in the derivative) - smoothFunc = 1._rkind / (1._rkind + expX) ! (logistic smoother) - if(derDesire)then - smoothFuncDeriv = expX / (smoothScale * (1._rkind + expX)**2._rkind) ! (derivative in the smoothing function) - else - smoothFuncDeriv = 0._rkind - end if + ! compute windspeed at the bottom of the canopy relative to the snow depth (m s-1) + windConvFactor = exp(-windReductionFactor*(1._rkind - (heightCanopyBottomAboveSnow/heightCanopyTopAboveSnow))) + windspdCanopyBottom = windspdCanopyTop*windConvFactor - ! outside limits: special case of smooth exponents - else - if(xArg < 0._rkind)then; smoothFunc = 0._rkind ! xArg < -xLimit - else; smoothFunc = 1._rkind ! xArg > xLimit - end if - smoothFuncDeriv = 0._rkind - end if ! check for huge exponents + ! compute the leaf boundary layer resistance (s m-1) + singleLeafConductance = leafExchangeCoeff*sqrt(windspdCanopyTop/leafDimension) + leaf2CanopyScaleFactor = (2._rkind/windReductionFactor) * (1._rkind - exp(-windReductionFactor/2._rkind)) ! factor to scale from the leaf to the canopy + canopyLeafConductance = singleLeafConductance*leaf2CanopyScaleFactor + leafResistance = 1._rkind/(canopyLeafConductance) + if(leafResistance < 0._rkind)then; err=20; message=trim(message)//'leaf resistance < 0'; return; end if - end subroutine logisticSmoother - ! -------------------------------------------------------------------------------------------------------------- + ! compute eddy diffusivity for heat at the top of the canopy (m2 s-1) + ! Note: use of friction velocity here includes stability adjustments + ! Note: max used to avoid dividing by zero + eddyDiffusCanopyTop = max(vkc*FrictionVelocity*(heightCanopyTopAboveSnow - zeroPlaneDisplacement), mpe) + ! compute the resistance between the surface and canopy air UNDER NEUTRAL CONDITIONS (s m-1) - ! ******************************************************************************************************* - ! private subroutine longwaveBal: compute longwave radiation balance at the canopy and ground surface - ! ******************************************************************************************************* - subroutine longwaveBal(& - ! input: model control - ixDerivMethod, & ! intent(in): choice of method used to compute derivative (analytical or numerical) - computeVegFlux, & ! intent(in): flag to compute fluxes over vegetati - insideIDA, & ! intent(in): flag to indicate inside Sundials Solver (do not require longwave to be balanced) - ! input: canopy and ground temperature - canopyTemp, & ! intent(in): canopy temperature (K) - groundTemp, & ! intent(in): ground temperature (K) - ! input: canopy and ground emissivity - emc, & ! intent(in): canopy emissivity (-) - emg, & ! intent(in): ground emissivity (-) - ! input: forcing - LWRadUbound, & ! intent(in): downwelling longwave radiation at the upper boundary (W m-2) - ! output: sources - LWRadCanopy, & ! intent(out): longwave radiation emitted from the canopy (W m-2) - LWRadGround, & ! intent(out): longwave radiation emitted at the ground surface (W m-2) - ! output: individual fluxes - LWRadUbound2Canopy, & ! intent(out): downward atmospheric longwave radiation absorbed by the canopy (W m-2) - LWRadUbound2Ground, & ! intent(out): downward atmospheric longwave radiation absorbed by the ground (W m-2) - LWRadUbound2Ubound, & ! intent(out): atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) - LWRadCanopy2Ubound, & ! intent(out): longwave radiation emitted from canopy lost thru upper boundary (W m-2) - LWRadCanopy2Ground, & ! intent(out): longwave radiation emitted from canopy absorbed by the ground (W m-2) - LWRadCanopy2Canopy, & ! intent(out): canopy longwave reflected from ground and absorbed by the canopy (W m-2) - LWRadGround2Ubound, & ! intent(out): longwave radiation emitted from ground lost thru upper boundary (W m-2) - LWRadGround2Canopy, & ! intent(out): longwave radiation emitted from ground and absorbed by the canopy (W m-2) - ! output: net fluxes - LWNetCanopy, & ! intent(out): net longwave radiation at the canopy (W m-2) - LWNetGround, & ! intent(out): net longwave radiation at the ground surface (W m-2) - LWNetUbound, & ! intent(out): net longwave radiation at the upper boundary (W m-2) - ! output: flux derivatives - dLWNetCanopy_dTCanopy, & ! intent(out): derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) - dLWNetGround_dTGround, & ! intent(out): derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetCanopy_dTGround, & ! intent(out): derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetGround_dTCanopy, & ! intent(out): derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) - ! output: error control - err,message ) ! intent(out): error control - ! ----------------------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input: model control - integer(i4b),intent(in) :: ixDerivMethod ! choice of method used to compute derivative (analytical or numerical) - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: insideIDA ! flag if inside Sundials solver - ! input: canopy and ground temperature - real(rkind),intent(in) :: canopyTemp ! canopy temperature (K) - real(rkind),intent(in) :: groundTemp ! ground temperature (K) - ! input: canopy and ground emissivity - real(rkind),intent(in) :: emc ! canopy emissivity (-) - real(rkind),intent(in) :: emg ! ground emissivity (-) - ! input: forcing - real(rkind),intent(in) :: LWRadUbound ! downwelling longwave radiation at the upper boundary (W m-2) - ! output: sources - real(rkind),intent(out) :: LWRadCanopy ! longwave radiation emitted from the canopy (W m-2) - real(rkind),intent(out) :: LWRadGround ! longwave radiation emitted at the ground surface (W m-2) - ! output: individual fluxes - real(rkind),intent(out) :: LWRadUbound2Canopy ! downward atmospheric longwave radiation absorbed by the canopy (W m-2) - real(rkind),intent(out) :: LWRadUbound2Ground ! downward atmospheric longwave radiation absorbed by the ground (W m-2) - real(rkind),intent(out) :: LWRadUbound2Ubound ! atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) - real(rkind),intent(out) :: LWRadCanopy2Ubound ! longwave radiation emitted from canopy lost thru upper boundary (W m-2) - real(rkind),intent(out) :: LWRadCanopy2Ground ! longwave radiation emitted from canopy absorbed by the ground (W m-2) - real(rkind),intent(out) :: LWRadCanopy2Canopy ! canopy longwave reflected from ground and absorbed by the canopy (W m-2) - real(rkind),intent(out) :: LWRadGround2Ubound ! longwave radiation emitted from ground lost thru upper boundary (W m-2) - real(rkind),intent(out) :: LWRadGround2Canopy ! longwave radiation emitted from ground and absorbed by the canopy (W m-2) - ! output: net fluxes - real(rkind),intent(out) :: LWNetCanopy ! net longwave radiation at the canopy (W m-2) - real(rkind),intent(out) :: LWNetGround ! net longwave radiation at the ground surface (W m-2) - real(rkind),intent(out) :: LWNetUbound ! net longwave radiation at the upper boundary (W m-2) - ! output: flux derivatives - real(rkind),intent(out) :: dLWNetCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dLWNetGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) - real(rkind),intent(out) :: dLWNetCanopy_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) - real(rkind),intent(out) :: dLWNetGround_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ----------------------------------------------------------------------------------------------------------------------------------------------- - ! local variables - integer(i4b),parameter :: unperturbed=1 ! named variable to identify the case of unperturbed state variables - integer(i4b),parameter :: perturbStateCanopy=2 ! named variable to identify the case where we perturb the canopy temperature - integer(i4b),parameter :: perturbStateGround=3 ! named variable to identify the case where we perturb the ground temperature - integer(i4b) :: itry ! index of flux evaluation - integer(i4b) :: nFlux ! number of flux evaluations - real(rkind) :: TCan ! value of canopy temperature used in flux calculations (may be perturbed) - real(rkind) :: TGnd ! value of ground temperature used in flux calculations (may be perturbed) - real(rkind) :: fluxBalance ! check energy closure (W m-2) - real(rkind),parameter :: fluxTolerance=1.e-10_rkind ! tolerance for energy closure (W m-2) - real(rkind) :: dLWRadCanopy_dTCanopy ! derivative in emitted radiation at the canopy w.r.t. canopy temperature - real(rkind) :: dLWRadGround_dTGround ! derivative in emitted radiation at the ground w.r.t. ground temperature - real(rkind) :: LWNetCanopy_dStateCanopy ! net lw canopy flux after perturbation in canopy temperature - real(rkind) :: LWNetGround_dStateCanopy ! net lw ground flux after perturbation in canopy temperature - real(rkind) :: LWNetCanopy_dStateGround ! net lw canopy flux after perturbation in ground temperature - real(rkind) :: LWNetGround_dStateGround ! net lw ground flux after perturbation in ground temperature - ! ----------------------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='longwaveBal/' - - ! check the need to compute numerical derivatives - if(ixDerivMethod==numerical)then - nFlux=3 ! compute the derivatives using one-sided finite differences - else - nFlux=1 ! compute analytical derivatives - end if + ! case 1: assume exponential profile extends from the snow depth plus surface roughness length to the displacement height plus vegetation roughness + if(ixWindProfile==exponential .or. heightCanopyBottomAboveSnowz0Ground+xTolerance + else - ! ------------------------------------------------------------------------------------- - ! state perturbations for numerical deriavtives with one-sided finite differences - ! note: no perturbations performed using analytical derivatives (nFlux=1) - ! ------------------------------------------------------------------------------------- + ! compute the neutral ground resistance + ! (first, component between heightCanopyBottomAboveSnow and z0Canopy+zeroPlaneDisplacement) + tmp1 = exp(-windReductionFactor* heightCanopyBottomAboveSnow/heightCanopyTopAboveSnow) + tmp2 = exp(-windReductionFactor*(z0Canopy+zeroPlaneDisplacement)/heightCanopyTopAboveSnow) + groundResistanceNeutral = ( heightCanopyTopAboveSnow*exp(windReductionFactor) / (windReductionFactor*eddyDiffusCanopyTop) ) * (tmp1 - tmp2) + ! (add log-below-canopy component) + groundResistanceNeutral = groundResistanceNeutral + (1._rkind/(max(0.1_rkind,windspdCanopyBottom)*vkc**2._rkind))*(log(heightCanopyBottomAboveSnow/z0Ground))**2._rkind - ! identify the type of perturbation - select case(itry) + endif ! switch between exponential profile and log-below-canopy - ! un-perturbed case - case(unperturbed) - TCan = canopyTemp - TGnd = groundTemp + ! compute the stability correction for resistance from the ground to the canopy air space (-) + ! NOTE: here we are interested in the windspeed at height z0Canopy+zeroPlaneDisplacement + call aStability(& + ! input + derivDesired, & ! input: logical flag to compute analytical derivatives + ixStability, & ! input: choice of stability function + ! input: forcing data, diagnostic and state variables + referenceHeight, & ! input: height of the canopy air space temperature/wind (m) + canairTemp, & ! input: temperature of the canopy air space (K) + groundTemp, & ! input: temperature of the ground surface (K) + max(0.1_rkind,windspdRefHeight), & ! input: wind speed at height z0Canopy+zeroPlaneDisplacement (m s-1) + ! input: stability parameters + critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam, & ! input: parameter in Louis (1979) stability function + Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function + ! output + RiBulkGround, & ! output: bulk Richardson number (-) + groundStabilityCorrection, & ! output: stability correction for turbulent heat fluxes (-) + dGroundStabilityCorrection_dRich, & ! output: derivative in stability correction w.r.t. Richardson number for the canopy (-) + dGroundStabilityCorrection_dCasTemp, & ! output: derivative in stability correction w.r.t. canopy air space temperature (K-1) + dGroundStabilityCorrection_dSfcTemp, & ! output: derivative in stability correction w.r.t. surface temperature (K-1) + err, cmessage ) ! output: error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - ! perturb canopy temperature - case(perturbStateCanopy) - TCan = canopyTemp + dx - TGnd = groundTemp + ! compute the ground resistance + groundResistance = groundResistanceNeutral / groundStabilityCorrection + if(groundResistance < 0._rkind)then; err=20; message=trim(message)//'ground resistance < 0 [vegetation is present]'; return; end if - ! perturb ground temperature - case(perturbStateGround) - TCan = canopyTemp - TGnd = groundTemp + dx + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! * compute resistance for the case without a canopy (bare ground, or canopy completely buried with snow) + else - ! check for an unknown perturbation - case default; err=10; message=trim(message)//"unknown perturbation"; return + ! no canopy, so set huge resistances (not used) + canopyResistance = 1.e12_rkind ! not used: huge resistance, so conductance is essentially zero + leafResistance = 1.e12_rkind ! not used: huge resistance, so conductance is essentially zero - end select ! (type of perturbation) + ! check that measurement height above the ground surface is above the roughness length + if(mHeight < snowDepth+z0Ground)then; err=20; message=trim(message)//'measurement height < snow depth + roughness length'; return; end if - ! ------------------------------------------------------------------------------------- - ! calculation block (unperturbed fluxes returned [computed last]) - ! ------------------------------------------------------------------------------------- - ! NOTE: emc should be set to zero when not computing canopy fluxes + ! compute the resistance between the surface and canopy air UNDER NEUTRAL CONDITIONS (s m-1) + groundExNeut = (vkc**2._rkind) / ( log((mHeight - snowDepth)/z0Ground)**2._rkind) ! turbulent transfer coefficient under conditions of neutral stability (-) + groundResistanceNeutral = 1._rkind / (groundExNeut*windspd) - ! compute longwave fluxes from canopy and the ground - if(computeVegFlux)then - LWRadCanopy = emc*sb*TCan**4._rkind ! longwave radiation emitted from the canopy (W m-2) - else - LWRadCanopy = 0._rkind - end if - LWRadGround = emg*sb*TGnd**4._rkind ! longwave radiation emitted at the ground surface (W m-2) - - ! compute fluxes originating from the atmosphere - LWRadUbound2Canopy = (emc + (1._rkind - emc)*(1._rkind - emg)*emc)*LWRadUbound ! downward atmospheric longwave radiation absorbed by the canopy (W m-2) - LWRadUbound2Ground = (1._rkind - emc)*emg*LWRadUbound ! downward atmospheric longwave radiation absorbed by the ground (W m-2) - LWRadUbound2Ubound = (1._rkind - emc)*(1._rkind - emg)*(1._rkind - emc)*LWRadUbound ! atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) - - ! compute fluxes originating from the canopy - LWRadCanopy2Ubound = (1._rkind + (1._rkind - emc)*(1._rkind - emg))*LWRadCanopy ! longwave radiation emitted from canopy lost thru upper boundary (W m-2) - LWRadCanopy2Ground = emg*LWRadCanopy ! longwave radiation emitted from canopy absorbed by the ground (W m-2) - LWRadCanopy2Canopy = emc*(1._rkind - emg)*LWRadCanopy ! canopy longwave reflected from ground and absorbed by the canopy (W m-2) - - ! compute fluxes originating from the ground surface - LWRadGround2Ubound = (1._rkind - emc)*LWRadGround ! longwave radiation emitted from ground lost thru upper boundary (W m-2) - LWRadGround2Canopy = emc*LWRadGround ! longwave radiation emitted from ground and absorbed by the canopy (W m-2) - - ! compute net longwave radiation (W m-2) - LWNetCanopy = LWRadUbound2Canopy + LWRadGround2Canopy + LWRadCanopy2Canopy - 2._rkind*LWRadCanopy ! canopy - LWNetGround = LWRadUbound2Ground + LWRadCanopy2Ground - LWRadGround ! ground surface - LWNetUbound = LWRadUbound - LWRadUbound2Ubound - LWRadCanopy2Ubound - LWRadGround2Ubound ! upper boundary - - !print*, 'LWRadCanopy = ', LWRadCanopy - !print*, 'LWRadGround = ', LWRadGround - - !print*, 'LWNetCanopy = ', LWNetCanopy - !print*, 'LWNetGround = ', LWNetGround - !print*, 'LWNetUbound = ', LWNetUbound - - ! check the flux balance - if(.not.insideIDA)then - fluxBalance = LWNetUbound - (LWNetCanopy + LWNetGround) - if(abs(fluxBalance) > fluxTolerance)then - print*, 'fluxBalance = ', fluxBalance - print*, 'emg, emc = ', emg, emc - print*, 'TCan, TGnd = ', TCan, TGnd - print*, 'LWRadUbound = ', LWRadUbound - print*, 'LWRadCanopy = ', LWRadCanopy - print*, 'LWRadGround = ', LWRadGround - print*, 'LWRadUbound2Canopy = ', LWRadUbound2Canopy - print*, 'LWRadUbound2Ground = ', LWRadUbound2Ground - print*, 'LWRadUbound2Ubound = ', LWRadUbound2Ubound - print*, 'LWRadCanopy2Ubound = ', LWRadCanopy2Ubound - print*, 'LWRadCanopy2Ground = ', LWRadCanopy2Ground - print*, 'LWRadCanopy2Canopy = ', LWRadCanopy2Canopy - print*, 'LWRadGround2Ubound = ', LWRadGround2Ubound - print*, 'LWRadGround2Canopy = ', LWRadGround2Canopy - print*, 'LWNetCanopy = ', LWNetCanopy - print*, 'LWNetGround = ', LWNetGround - print*, 'LWNetUbound = ', LWNetUbound - message=trim(message)//'flux imbalance' + ! define height above the snow surface + heightAboveGround = mHeight - snowDepth + + ! check that measurement height above the ground surface is above the roughness length + if(heightAboveGround < z0Ground)then + print*, 'z0Ground = ', z0Ground + print*, 'mHeight = ', mHeight + print*, 'snowDepth = ', snowDepth + print*, 'heightAboveGround = ', heightAboveGround + message=trim(message)//'height above ground < roughness length [likely due to snow accumulation]' err=20; return - end if - end if + end if - ! -------------------------------------------------------------------------------------- - ! save perturbed fluxes to calculate numerical derivatives (one-sided finite difference) - ! -------------------------------------------------------------------------------------- - if(ixDerivMethod==numerical)then - select case(itry) ! (select type of perturbation) - case(unperturbed); exit - case(perturbStateCanopy) - LWNetCanopy_dStateCanopy = LWNetCanopy - LWNetGround_dStateCanopy = LWNetGround - case(perturbStateGround) - LWNetCanopy_dStateGround = LWNetCanopy - LWNetGround_dStateGround = LWNetGround - case default; err=10; message=trim(message)//"unknown perturbation"; return - end select ! (type of perturbation) - end if ! (if numerical) - - end do ! looping through different perturbations - - ! ------------------------------------------------------------------------------------- - ! compute derivatives - ! ------------------------------------------------------------------------------------- - select case(ixDerivMethod) - - ! ***** analytical derivatives - case(analytical) - ! compute initial derivatives - dLWRadCanopy_dTCanopy = 4._rkind*emc*sb*TCan**3._rkind - dLWRadGround_dTGround = 4._rkind*emg*sb*TGnd**3._rkind - ! compute analytical derivatives - dLWNetCanopy_dTCanopy = (emc*(1._rkind - emg) - 2._rkind)*dLWRadCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) - dLWNetGround_dTGround = -dLWRadGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetCanopy_dTGround = emc*dLWRadGround_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetGround_dTCanopy = emg*dLWRadCanopy_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) - - ! ***** numerical derivatives - case(numerical) - ! compute numerical derivatives (one-sided finite differences) - dLWNetCanopy_dTCanopy = (LWNetCanopy_dStateCanopy - LWNetCanopy)/dx ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) - dLWNetGround_dTGround = (LWNetGround_dStateGround - LWNetGround)/dx ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetCanopy_dTGround = (LWNetCanopy_dStateGround - LWNetCanopy)/dx ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetGround_dTCanopy = (LWNetGround_dStateCanopy - LWNetGround)/dx ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) - - ! ***** error check - case default; err=10; message=trim(message)//"unknown method to calculate derivatives"; return - - end select ! (type of method to calculate derivatives) - - end subroutine longwaveBal - - - ! ******************************************************************************************************* - ! private subroutine aeroResist: compute aerodynamic resistances - ! ******************************************************************************************************* - subroutine aeroResist(& - ! input: model control - computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) - derivDesired, & ! intent(in): flag to indicate if analytical derivatives are desired - ixVegTraits, & ! intent(in): choice of parameterization for vegetation roughness length and displacement height - ixWindProfile, & ! intent(in): choice of canopy wind profile - ixStability, & ! intent(in): choice of stability function - ! input: above-canopy forcing data - mHeight, & ! intent(in): measurement height (m) - airtemp, & ! intent(in): air temperature at some height above the surface (K) - windspd, & ! intent(in): wind speed at some height above the surface (m s-1) - ! input: temperature (canopy, ground, canopy air space) - canairTemp, & ! intent(in): temperature of the canopy air space (K) - groundTemp, & ! intent(in): ground temperature (K) - ! input: diagnostic variables - exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) - snowDepth, & ! intent(in): snow depth (m) - ! input: parameters - z0Ground, & ! intent(in): roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) - z0CanopyParam, & ! intent(in): roughness length of the canopy (m) - zpdFraction, & ! intent(in): zero plane displacement / canopy height (-) - critRichNumber, & ! intent(in): critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam, & ! intent(in): parameter in Louis (1979) stability function - Mahrt87_eScale, & ! intent(in): exponential scaling factor in the Mahrt (1987) stability function - windReductionParam, & ! intent(in): canopy wind reduction parameter (-) - leafExchangeCoeff, & ! intent(in): turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) - leafDimension, & ! intent(in): characteristic leaf dimension (m) - heightCanopyTop, & ! intent(in): height at the top of the vegetation canopy (m) - heightCanopyBottom, & ! intent(in): height at the bottom of the vegetation canopy (m) - ! output: stability corrections - RiBulkCanopy, & ! intent(out): bulk Richardson number for the canopy (-) - RiBulkGround, & ! intent(out): bulk Richardson number for the ground surface (-) - canopyStabilityCorrection, & ! intent(out): stability correction for the canopy (-) - groundStabilityCorrection, & ! intent(out): stability correction for the ground surface (-) - ! output: scalar resistances - z0Canopy, & ! intent(out): roughness length of the canopy (m) - windReductionFactor, & ! intent(out): canopy wind reduction factor (-) - zeroPlaneDisplacement, & ! intent(out): zero plane displacement (m) - eddyDiffusCanopyTop, & ! intent(out): eddy diffusivity for heat at the top of the canopy (m2 s-1) - frictionVelocity, & ! intent(out): friction velocity (m s-1) - windspdCanopyTop, & ! intent(out): windspeed at the top of the canopy (m s-1) - windspdCanopyBottom, & ! intent(out): windspeed at the height of the bottom of the canopy (m s-1) - leafResistance, & ! intent(out): mean leaf boundary layer resistance per unit leaf area (s m-1) - groundResistance, & ! intent(out): below canopy aerodynamic resistance (s m-1) - canopyResistance, & ! intent(out): above canopy aerodynamic resistance (s m-1) - ! output: derivatives in scalar resistances - dGroundResistance_dTGround, & ! intent(out): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - dGroundResistance_dTCanopy, & ! intent(out): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - dGroundResistance_dTCanair, & ! intent(out): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - dCanopyResistance_dTCanopy, & ! intent(out): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - dCanopyResistance_dTCanair, & ! intent(out): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - ! output: error control - err,message ) ! intent(out): error control - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! compute aerodynamic resistances - ! Refs: Choudhury and Monteith (4-layer model for heat budget of homogenous surfaces; QJRMS, 1988) - ! Niu and Yang (Canopy effects on snow processes; JGR, 2004) - ! Mahat et al. (Below-canopy turbulence in a snowmelt model, WRR, 2012) - implicit none - ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) - logical(lgt),intent(in) :: derivDesired ! logical flag to indicate if analytical derivatives are desired - integer(i4b),intent(in) :: ixVegTraits ! choice of parameterization for vegetation roughness length and displacement height - integer(i4b),intent(in) :: ixWindProfile ! choice of canopy wind profile - integer(i4b),intent(in) :: ixStability ! choice of stability function - ! input: above-canopy forcing data - real(rkind),intent(in) :: mHeight ! measurement height (m) - real(rkind),intent(in) :: airtemp ! air temperature at some height above the surface (K) - real(rkind),intent(in) :: windspd ! wind speed at some height above the surface (m s-1) - ! input: temperature (canopy, ground, canopy air space) - real(rkind),intent(in) :: canairTemp ! temperature of the canopy air space (K) - real(rkind),intent(in) :: groundTemp ! ground temperature (K) - ! input: diagnostic variables - real(rkind),intent(in) :: exposedVAI ! exposed vegetation area index -- leaf plus stem (m2 m-2) - real(rkind),intent(in) :: snowDepth ! snow depth (m) - ! input: parameters - real(rkind),intent(in) :: z0Ground ! roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) - real(rkind),intent(in) :: z0CanopyParam ! roughness length of the canopy (m) - real(rkind),intent(in) :: zpdFraction ! zero plane displacement / canopy height (-) - real(rkind),intent(in) :: critRichNumber ! critical value for the bulk Richardson number where turbulence ceases (-) - real(rkind),intent(in) :: Louis79_bparam ! parameter in Louis (1979) stability function - real(rkind),intent(in) :: Mahrt87_eScale ! exponential scaling factor in the Mahrt (1987) stability function - real(rkind),intent(in) :: windReductionParam ! canopy wind reduction parameter (-) - real(rkind),intent(in) :: leafExchangeCoeff ! turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) - real(rkind),intent(in) :: leafDimension ! characteristic leaf dimension (m) - real(rkind),intent(in) :: heightCanopyTop ! height at the top of the vegetation canopy (m) - real(rkind),intent(in) :: heightCanopyBottom ! height at the bottom of the vegetation canopy (m) - ! output: stability corrections - real(rkind),intent(out) :: RiBulkCanopy ! bulk Richardson number for the canopy (-) - real(rkind),intent(out) :: RiBulkGround ! bulk Richardson number for the ground surface (-) - real(rkind),intent(out) :: canopyStabilityCorrection ! stability correction for the canopy (-) - real(rkind),intent(out) :: groundStabilityCorrection ! stability correction for the ground surface (-) - ! output: scalar resistances - real(rkind),intent(out) :: z0Canopy ! roughness length of the vegetation canopy (m) - real(rkind),intent(out) :: windReductionFactor ! canopy wind reduction factor (-) - real(rkind),intent(out) :: zeroPlaneDisplacement ! zero plane displacement (m) - real(rkind),intent(out) :: eddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) - real(rkind),intent(out) :: frictionVelocity ! friction velocity (m s-1) - real(rkind),intent(out) :: windspdCanopyTop ! windspeed at the top of the canopy (m s-1) - real(rkind),intent(out) :: windspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) - real(rkind),intent(out) :: leafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) - real(rkind),intent(out) :: groundResistance ! below canopy aerodynamic resistance (s m-1) - real(rkind),intent(out) :: canopyResistance ! above canopy aerodynamic resistance (s m-1) - ! output: derivatives in scalar resistances - real(rkind),intent(out) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - real(rkind),intent(out) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind),intent(out) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - real(rkind),intent(out) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind),intent(out) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! local variables: general - character(LEN=256) :: cmessage ! error message of downwind routine - ! local variables: vegetation roughness and dispalcement height - real(rkind),parameter :: oneThird=1._rkind/3._rkind ! 1/3 - real(rkind),parameter :: twoThirds=2._rkind/3._rkind ! 2/3 - real(rkind),parameter :: C_r = 0.3 ! roughness element drag coefficient (-) from Raupach (BLM, 1994) - real(rkind),parameter :: C_s = 0.003_rkind ! substrate surface drag coefficient (-) from Raupach (BLM, 1994) - real(rkind),parameter :: approxDragCoef_max = 0.3_rkind ! maximum value of the approximate drag coefficient (-) from Raupach (BLM, 1994) - real(rkind),parameter :: psi_h = 0.193_rkind ! roughness sub-layer influence function (-) from Raupach (BLM, 1994) - real(rkind),parameter :: c_d1 = 7.5_rkind ! scaling parameter used to define displacement height (-) from Raupach (BLM, 1994) - real(rkind),parameter :: cd_CM = 0.2_rkind ! mean drag coefficient for individual leaves (-) from Choudhury and Monteith (QJRMS, 1988) - real(rkind) :: funcLAI ! temporary variable to calculate zero plane displacement for the canopy - real(rkind) :: fracCanopyHeight ! zero plane displacement expressed as a fraction of canopy height - real(rkind) :: approxDragCoef ! approximate drag coefficient used in the computation of canopy roughness length (-) - ! local variables: resistance - real(rkind) :: canopyExNeut ! surface-atmosphere exchange coefficient under neutral conditions (-) - real(rkind) :: groundExNeut ! surface-atmosphere exchange coefficient under neutral conditions (-) - real(rkind) :: sfc2AtmExchangeCoeff_canopy ! surface-atmosphere exchange coefficient after stability corrections (-) - real(rkind) :: groundResistanceNeutral ! ground resistance under neutral conditions (s m-1) - real(rkind) :: windConvFactor_fv ! factor to convert friction velocity to wind speed at top of canopy (-) - real(rkind) :: windConvFactor ! factor to convert wind speed at top of canopy to wind speed at a given height in the canopy (-) - real(rkind) :: referenceHeight ! z0Canopy+zeroPlaneDisplacement (m) - real(rkind) :: windspdRefHeight ! windspeed at the reference height (m/s) - real(rkind) :: heightAboveGround ! height above the snow surface (m) - real(rkind) :: heightCanopyTopAboveSnow ! height at the top of the vegetation canopy relative to snowpack (m) - real(rkind) :: heightCanopyBottomAboveSnow ! height at the bottom of the vegetation canopy relative to snowpack (m) - real(rkind),parameter :: xTolerance=0.1_rkind ! tolerance to handle the transition from exponential to log-below canopy - ! local variables: derivatives - real(rkind) :: dFV_dT ! derivative in friction velocity w.r.t. canopy air temperature - real(rkind) :: dED_dT ! derivative in eddy diffusivity at the top of the canopy w.r.t. canopy air temperature - real(rkind) :: dGR_dT ! derivative in neutral ground resistance w.r.t. canopy air temperature - real(rkind) :: tmp1,tmp2 ! temporary variables used in calculation of ground resistance - real(rkind) :: dCanopyStabilityCorrection_dRich ! derivative in stability correction w.r.t. Richardson number for the canopy (-) - real(rkind) :: dGroundStabilityCorrection_dRich ! derivative in stability correction w.r.t. Richardson number for the ground surface (-) - real(rkind) :: dCanopyStabilityCorrection_dAirTemp ! (not used) derivative in stability correction w.r.t. air temperature (K-1) - real(rkind) :: dGroundStabilityCorrection_dAirTemp ! (not used) derivative in stability correction w.r.t. air temperature (K-1) - real(rkind) :: dCanopyStabilityCorrection_dCasTemp ! derivative in canopy stability correction w.r.t. canopy air space temperature (K-1) - real(rkind) :: dGroundStabilityCorrection_dCasTemp ! derivative in ground stability correction w.r.t. canopy air space temperature (K-1) - real(rkind) :: dGroundStabilityCorrection_dSfcTemp ! derivative in ground stability correction w.r.t. surface temperature (K-1) - real(rkind) :: singleLeafConductance ! leaf boundary layer conductance (m s-1) - real(rkind) :: canopyLeafConductance ! leaf boundary layer conductance -- scaled up to the canopy (m s-1) - real(rkind) :: leaf2CanopyScaleFactor ! factor to scale from the leaf to the canopy [m s-(1/2)] - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='aeroResist/' - - ! check that measurement height is above the top of the canopy - if(mHeight < heightCanopyTop)then - err=20; message=trim(message)//'measurement height is below the top of the canopy'; return - end if + ! compute ground stability correction + call aStability(& + ! input + derivDesired, & ! input: logical flag to compute analytical derivatives + ixStability, & ! input: choice of stability function + ! input: forcing data, diagnostic and state variables + heightAboveGround, & ! input: measurement height above the ground surface (m) + airtemp, & ! input: temperature above the ground surface (K) + groundTemp, & ! input: trial value of surface temperature -- "surface" is either canopy or ground (K) + windspd, & ! input: wind speed above the ground surface (m s-1) + ! input: stability parameters + critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam, & ! input: parameter in Louis (1979) stability function + Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function + ! output + RiBulkGround, & ! output: bulk Richardson number (-) + groundStabilityCorrection, & ! output: stability correction for turbulent heat fluxes (-) + dGroundStabilityCorrection_dRich, & ! output: derivative in stability correction w.r.t. Richardson number for the ground surface (-) + dGroundStabilityCorrection_dAirTemp, & ! output: (not used) derivative in stability correction w.r.t. air temperature (K-1) + dGroundStabilityCorrection_dSfcTemp, & ! output: derivative in stability correction w.r.t. surface temperature (K-1) + err, cmessage ) ! output: error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + ! compute the ground resistance (after stability corrections) + groundResistance = groundResistanceNeutral/groundStabilityCorrection + if(groundResistance < 0._rkind)then; err=20; message=trim(message)//'ground resistance < 0 [no vegetation]'; return; end if + + ! set all canopy variables to missing (no canopy!) + z0Canopy = missingValue ! roughness length of the vegetation canopy (m) + RiBulkCanopy = missingValue ! bulk Richardson number for the canopy (-) + windReductionFactor = missingValue ! canopy wind reduction factor (-) + zeroPlaneDisplacement = missingValue ! zero plane displacement (m) + canopyStabilityCorrection = missingValue ! stability correction for the canopy (-) + eddyDiffusCanopyTop = missingValue ! eddy diffusivity for heat at the top of the canopy (m2 s-1) + frictionVelocity = missingValue ! friction velocity (m s-1) + windspdCanopyTop = missingValue ! windspeed at the top of the canopy (m s-1) + windspdCanopyBottom = missingValue ! windspeed at the height of the bottom of the canopy (m s-1) - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! * compute vegetation poperties (could be done at the same time as phenology.. does not have to be in the flux routine!) - if(computeVegFlux) then ! (if vegetation is exposed) - - ! ***** identify zero plane displacement, roughness length, and surface temperature for the canopy (m) - ! First, calculate new coordinate system above snow - use these to scale wind profiles and resistances - ! NOTE: the new coordinate system makes zeroPlaneDisplacement and z0Canopy consistent - heightCanopyTopAboveSnow = heightCanopyTop - snowDepth - heightCanopyBottomAboveSnow = max(heightCanopyBottom - snowDepth, 0.0_rkind) - select case(ixVegTraits) - - ! Raupach (BLM 1994) "Simplified expressions..." - case(Raupach_BLM1994) - ! (compute zero-plane displacement) - funcLAI = sqrt(c_d1*exposedVAI) - fracCanopyHeight = -(1._rkind - exp(-funcLAI))/funcLAI + 1._rkind - zeroPlaneDisplacement = fracCanopyHeight*(heightCanopyTopAboveSnow-heightCanopyBottomAboveSnow)+heightCanopyBottomAboveSnow - ! (coupute roughness length of the veg canopy) - approxDragCoef = min( sqrt(C_s + C_r*exposedVAI/2._rkind), approxDragCoef_max) - z0Canopy = (1._rkind - fracCanopyHeight) * exp(-vkc*approxDragCoef - psi_h) * (heightCanopyTopAboveSnow-heightCanopyBottomAboveSnow) - - ! Choudhury and Monteith (QJRMS 1988) "A four layer model for the heat budget..." - case(CM_QJRMS1988) - funcLAI = cd_CM*exposedVAI - zeroPlaneDisplacement = 1.1_rkind*heightCanopyTopAboveSnow*log(1._rkind + funcLAI**0.25_rkind) - if(funcLAI < 0.2_rkind)then - z0Canopy = z0Ground + 0.3_rkind*heightCanopyTopAboveSnow*funcLAI**0.5_rkind + end if ! (if no canopy) + + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! * compute derivatives + if(derivDesired)then ! if analytical derivatives are desired + + ! derivatives for the vegetation canopy + if(computeVegFlux) then ! (if vegetation is exposed) + + ! ***** compute derivatives w.r.t. canopy temperature + ! NOTE: derivatives are zero because using canopy air space temperature + dCanopyResistance_dTCanopy = 0._rkind ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + dGroundResistance_dTCanopy = 0._rkind ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + + ! ***** compute derivatives w.r.t. ground temperature (s m-1 K-1) + dGroundResistance_dTGround = -(groundResistanceNeutral*dGroundStabilityCorrection_dSfcTemp)/(groundStabilityCorrection**2._rkind) + + ! ***** compute derivatives w.r.t. temperature of the canopy air space (s m-1 K-1) + ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + dCanopyResistance_dTCanair = -dCanopyStabilityCorrection_dCasTemp/(windspd*canopyExNeut*canopyStabilityCorrection**2._rkind) + ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + ! (compute derivative in NEUTRAL ground resistance w.r.t. canopy air temperature (s m-1 K-1)) + dFV_dT = windspd*canopyExNeut*dCanopyStabilityCorrection_dCasTemp/(sqrt(sfc2AtmExchangeCoeff_canopy)*2._rkind) ! d(frictionVelocity)/d(canopy air temperature) + dED_dT = dFV_dT*vkc*(heightCanopyTopAboveSnow - zeroPlaneDisplacement) ! d(eddyDiffusCanopyTop)d(canopy air temperature) + dGR_dT = -dED_dT*(tmp1 - tmp2)*heightCanopyTopAboveSnow*exp(windReductionFactor) / (windReductionFactor*eddyDiffusCanopyTop**2._rkind) ! d(groundResistanceNeutral)/d(canopy air temperature) + ! (stitch everything together -- product rule) + dGroundResistance_dTCanair = dGR_dT/groundStabilityCorrection - groundResistanceNeutral*dGroundStabilityCorrection_dCasTemp/(groundStabilityCorrection**2._rkind) + + ! ***** compute resistances for non-vegetated surfaces (e.g., snow) else - z0Canopy = 0.3_rkind*heightCanopyTopAboveSnow*(1._rkind - zeroPlaneDisplacement/heightCanopyTopAboveSnow) - end if - ! constant parameters dependent on the vegetation type - case(vegTypeTable) - zeroPlaneDisplacement = zpdFraction*heightCanopyTopAboveSnow ! zero-plane displacement (m) - z0Canopy = z0CanopyParam ! roughness length of the veg canopy (m) + ! set canopy derivatives to zero (non-vegetated, remember) + dCanopyResistance_dTCanopy = 0._rkind + dGroundResistance_dTCanopy = 0._rkind - ! check - case default - err=10; message=trim(message)//"unknown parameterization for vegetation roughness length and displacement height"; return + ! compute derivatives for ground resistance + dGroundResistance_dTGround = -dGroundStabilityCorrection_dSfcTemp/(windspd*groundExNeut*groundStabilityCorrection**2._rkind) - end select ! vegetation traits (z0, zpd) + end if ! (switch between vegetated and non-vegetated surfaces) - ! check zero plane displacement - if(zeroPlaneDisplacement < heightCanopyBottomAboveSnow)then - write(*,'(a,1x,10(f12.5,1x))') 'heightCanopyTop, snowDepth, heightCanopyTopAboveSnow, heightCanopyBottomAboveSnow, exposedVAI = ', & - heightCanopyTop, snowDepth, heightCanopyTopAboveSnow, heightCanopyBottomAboveSnow, exposedVAI - message=trim(message)//'zero plane displacement is below the canopy bottom' - err=20; return - endif + ! * analytical derivatives not desired + else + dGroundResistance_dTGround = missingValue + dGroundResistance_dTCanopy = missingValue + dCanopyResistance_dTCanopy = missingValue + end if - ! check measurement height - if(mHeight < zeroPlaneDisplacement)then; err=20; message=trim(message)//'measurement height is below the displacement height'; return; end if - if(mHeight < z0Canopy)then; err=20; message=trim(message)//'measurement height is below the roughness length'; return; end if +end subroutine aeroResist + + +! ******************************************************************************************************* +! private subroutine soilResist: compute soil moisture factor controlling stomatal resistance +! ******************************************************************************************************* +subroutine soilResist(& + ! input (model decisions) + ixSoilResist, & ! intent(in): choice of function for the soil moisture control on stomatal resistance + ixGroundwater, & ! intent(in): choice of groundwater representation + ! input (state variables) + mLayerMatricHead, & ! intent(in): matric head in each layer (m) + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water in each layer + scalarAquiferStorage, & ! intent(in): aquifer storage (m) + ! input (diagnostic variables) + mLayerRootDensity, & ! intent(in): root density in each layer (-) + scalarAquiferRootFrac, & ! intent(in): fraction of roots below the lowest unsaturated layer (-) + ! input (parameters) + plantWiltPsi, & ! intent(in): matric head at wilting point (m) + soilStressParam, & ! intent(in): parameter in the exponential soil stress function (-) + critSoilWilting, & ! intent(in): critical vol. liq. water content when plants are wilting (-) + critSoilTranspire, & ! intent(in): critical vol. liq. water content when transpiration is limited (-) + critAquiferTranspire, & ! intent(in): critical aquifer storage value when transpiration is limited (m) + ! output + wAvgTranspireLimitFac, & ! intent(out): weighted average of the transpiration limiting factor (-) + mLayerTranspireLimitFac, & ! intent(out): transpiration limiting factor in each layer (-) + aquiferTranspireLimitFac, & ! intent(out): transpiration limiting factor for the aquifer (-) + err,message) ! intent(out): error control + ! ----------------------------------------------------------------------------------------------------------------------------------------- + USE mDecisions_module, only: NoahType,CLM_Type,SiB_Type ! options for the choice of function for the soil moisture control on stomatal resistance + USE mDecisions_module, only: bigBucket ! named variable that defines the "bigBucket" groundwater parameterization + implicit none + ! input (model decisions) + integer(i4b),intent(in) :: ixSoilResist ! choice of function for the soil moisture control on stomatal resistance + integer(i4b),intent(in) :: ixGroundwater ! choice of groundwater representation + ! input (variables) + real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head in each layer (m) + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid water in each layer (-) + real(rkind),intent(in) :: scalarAquiferStorage ! aquifer storage (m) + ! input (diagnostic variables) + real(rkind),intent(in) :: mLayerRootDensity(:) ! root density in each layer (-) + real(rkind),intent(in) :: scalarAquiferRootFrac ! fraction of roots below the lowest unsaturated layer (-) + ! input (parameters) + real(rkind),intent(in) :: plantWiltPsi ! matric head at wilting point (m) + real(rkind),intent(in) :: soilStressParam ! parameter in the exponential soil stress function (-) + real(rkind),intent(in) :: critSoilWilting ! critical vol. liq. water content when plants are wilting (-) + real(rkind),intent(in) :: critSoilTranspire ! critical vol. liq. water content when transpiration is limited (-) + real(rkind),intent(in) :: critAquiferTranspire ! critical aquifer storage value when transpiration is limited (m) + ! output + real(rkind),intent(out) :: wAvgTranspireLimitFac ! intent(out): weighted average of the transpiration limiting factor (-) + real(rkind),intent(out) :: mLayerTranspireLimitFac(:) ! intent(out): transpiration limiting factor in each layer (-) + real(rkind),intent(out) :: aquiferTranspireLimitFac ! intent(out): transpiration limiting factor for the aquifer (-) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local variables + real(rkind) :: gx ! stress function for the soil layers + real(rkind),parameter :: verySmall=epsilon(gx) ! a very small number + integer(i4b) :: iLayer ! index of soil layer + ! initialize error control + err=0; message='soilResist/' + + ! ** compute the factor limiting transpiration for each soil layer (-) + wAvgTranspireLimitFac = 0._rkind ! (initialize the weighted average) + do iLayer=1,size(mLayerMatricHead) + ! compute the soil stress function + select case(ixSoilResist) + case(NoahType) ! thresholded linear function of volumetric liquid water content + gx = (mLayerVolFracLiq(iLayer) - critSoilWilting) / (critSoilTranspire - critSoilWilting) + case(CLM_Type) ! thresholded linear function of matric head + if(mLayerMatricHead(iLayer) > plantWiltPsi)then + gx = 1._rkind - mLayerMatricHead(iLayer)/plantWiltPsi + else + gx = 0._rkind + end if + case(SiB_Type) ! exponential of the log of matric head + if(mLayerMatricHead(iLayer) < 0._rkind)then ! (unsaturated) + gx = 1._rkind - exp( -soilStressParam * ( log(plantWiltPsi/mLayerMatricHead(iLayer)) ) ) + else ! (saturated) + gx = 1._rkind + end if + case default ! check identified the option + err=20; message=trim(message)//'cannot identify option for soil resistance'; return + end select + ! save the factor for the given layer (ensure between zero and one) + mLayerTranspireLimitFac(iLayer) = min( max(verySmall,gx), 1._rkind) + ! compute the weighted average (weighted by root density) + wAvgTranspireLimitFac = wAvgTranspireLimitFac + mLayerTranspireLimitFac(iLayer)*mLayerRootDensity(iLayer) + end do ! (looping through soil layers) + + ! ** compute the factor limiting evaporation in the aquifer + if(scalarAquiferRootFrac > verySmall)then + ! check that aquifer root fraction is allowed + if(ixGroundwater /= bigBucket)then + message=trim(message)//'aquifer evaporation only allowed for the big groundwater bucket -- increase the soil depth to account for roots' + err=20; return + end if + ! compute the factor limiting evaporation for the aquifer + aquiferTranspireLimitFac = min(scalarAquiferStorage/critAquiferTranspire, 1._rkind) + else ! (if there are roots in the aquifer) + aquiferTranspireLimitFac = 0._rkind + end if + ! compute the weighted average (weighted by root density) + wAvgTranspireLimitFac = wAvgTranspireLimitFac + aquiferTranspireLimitFac*scalarAquiferRootFrac + +end subroutine soilResist + + +! ******************************************************************************** +! private subroutine turbFluxes: compute turbulent heat fluxes +! ******************************************************************************** +subroutine turbFluxes(& + ! input: model control + computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) + ixDerivMethod, & ! intent(in): choice of method used to compute derivative (analytical or numerical) + ! input: above-canopy forcing data + airtemp, & ! intent(in): air temperature at some height above the surface (K) + airpres, & ! intent(in): air pressure of the air above the vegetation canopy (Pa) + VPair, & ! intent(in): vapor pressure of the air above the vegetation canopy (Pa) + ! input: latent heat of sublimation/vaporization + latHeatSubVapCanopy, & ! intent(in): latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) + latHeatSubVapGround, & ! intent(in): latent heat of sublimation/vaporization for the ground surface (J kg-1) + ! input: canopy and ground temperature + canairTemp, & ! intent(in): temperature of the canopy air space (K) + canopyTemp, & ! intent(in): canopy temperature (K) + groundTemp, & ! intent(in): ground temperature (K) + satVP_CanopyTemp, & ! intent(in): saturation vapor pressure at the temperature of the veg canopy (Pa) + satVP_GroundTemp, & ! intent(in): saturation vapor pressure at the temperature of the ground (Pa) + dSVPCanopy_dCanopyTemp, & ! intent(in): derivative in canopy saturation vapor pressure w.r.t. canopy temperature (Pa K-1) + dSVPGround_dGroundTemp, & ! intent(in): derivative in ground saturation vapor pressure w.r.t. ground temperature (Pa K-1) + ! input: diagnostic variables + exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) + canopyWetFraction, & ! intent(in): fraction of canopy that is wet [0-1] + dCanopyWetFraction_dWat, & ! intent(in): derivative in the canopy wetted fraction w.r.t. total water content (kg-1 m-2) + dCanopyWetFraction_dT, & ! intent(in): derivative in wetted fraction w.r.t. canopy temperature (K-1) + canopySunlitLAI, & ! intent(in): sunlit leaf area (-) + canopyShadedLAI, & ! intent(in): shaded leaf area (-) + soilRelHumidity, & ! intent(in): relative humidity in the soil pores [0-1] + soilResistance, & ! intent(in): resistance from the soil (s m-1) + leafResistance, & ! intent(in): mean leaf boundary layer resistance per unit leaf area (s m-1) + groundResistance, & ! intent(in): below canopy aerodynamic resistance (s m-1) + canopyResistance, & ! intent(in): above canopy aerodynamic resistance (s m-1) + stomResistSunlit, & ! intent(in): stomatal resistance for sunlit leaves (s m-1) + stomResistShaded, & ! intent(in): stomatal resistance for shaded leaves (s m-1) + ! input: derivatives in scalar resistances + dGroundResistance_dTGround, & ! intent(in): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + dGroundResistance_dTCanopy, & ! intent(in): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + dGroundResistance_dTCanair, & ! intent(in): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + dCanopyResistance_dTCanopy, & ! intent(in): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + dCanopyResistance_dTCanair, & ! intent(in): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + ! output: conductances (used to check derivative calculations) + leafConductance, & ! intent(out): leaf conductance (m s-1) + canopyConductance, & ! intent(out): canopy conductance (m s-1) + groundConductanceSH, & ! intent(out): ground conductance for sensible heat (m s-1) + groundConductanceLH, & ! intent(out): ground conductance for latent heat -- includes soil resistance (m s-1) + evapConductance, & ! intent(out): conductance for evaporation (m s-1) + transConductance, & ! intent(out): conductance for transpiration (m s-1) + totalConductanceSH, & ! intent(out): total conductance for sensible heat (m s-1) + totalConductanceLH, & ! intent(out): total conductance for latent heat (m s-1) + ! output: canopy air space variables + VP_CanopyAir, & ! intent(out): vapor pressure of the canopy air space (Pa) + ! output: fluxes from the vegetation canopy + senHeatCanopy, & ! intent(out): sensible heat flux from the canopy to the canopy air space (W m-2) + latHeatCanopyEvap, & ! intent(out): latent heat flux associated with evaporation from the canopy to the canopy air space (W m-2) + latHeatCanopyTrans, & ! intent(out): latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) + ! output: fluxes from non-vegetated surfaces (ground surface below vegetation, bare ground, or snow covered vegetation) + senHeatGround, & ! intent(out): sensible heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) + latHeatGround, & ! intent(out): latent heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) + ! output: total heat fluxes to the atmosphere + senHeatTotal, & ! intent(out): total sensible heat flux to the atmosphere (W m-2) + latHeatTotal, & ! intent(out): total latent heat flux to the atmosphere (W m-2) + ! output: net fluxes + turbFluxCanair, & ! intent(out): net turbulent heat fluxes at the canopy air space (W m-2) + turbFluxCanopy, & ! intent(out): net turbulent heat fluxes at the canopy (W m-2) + turbFluxGround, & ! intent(out): net turbulent heat fluxes at the ground surface (W m-2) + ! output: flux derivatives + dTurbFluxCanair_dTCanair, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxCanair_dTCanopy, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxCanair_dTGround, & ! intent(out): derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanair, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanopy, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxCanopy_dTGround, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxGround_dTCanair, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxGround_dTCanopy, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxGround_dTGround, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + ! output: liquid flux derivatives (canopy evap) + dLatHeatCanopyEvap_dCanWat, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy total water content (J kg-1 s-1) + dLatHeatCanopyEvap_dTCanair, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) + dLatHeatCanopyEvap_dTCanopy, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) + dLatHeatCanopyEvap_dTGround, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) + ! output: liquid flux derivatives (ground evap) + dLatHeatGroundEvap_dCanWat, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) + dLatHeatGroundEvap_dTCanair, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy air temperature + dLatHeatGroundEvap_dTCanopy, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy temperature + dLatHeatGroundEvap_dTGround, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. ground temperature + ! output: latent heat flux derivatives (canopy trans) + dLatHeatCanopyTrans_dCanWat, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy total water (J kg-1 s-1) + dLatHeatCanopyTrans_dTCanair, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy air temperature + dLatHeatCanopyTrans_dTCanopy, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy temperature + dLatHeatCanopyTrans_dTGround, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. ground temperature + ! output: cross derivatives + dTurbFluxCanair_dCanWat, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxCanopy_dCanWat, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxGround_dCanWat, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + ! output: error control + err,message ) ! intent(out): error control ! ----------------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input: model control + logical(lgt),intent(in) :: computeVegFlux ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) + integer(i4b),intent(in) :: ixDerivMethod ! choice of method used to compute derivative (analytical or numerical) + ! input: above-canopy forcing data + real(rkind),intent(in) :: airtemp ! air temperature at some height above the surface (K) + real(rkind),intent(in) :: airpres ! air pressure of the air above the vegetation canopy (Pa) + real(rkind),intent(in) :: VPair ! vapor pressure of the air above the vegetation canopy (Pa) + ! input: latent heat of sublimation/vaporization + real(rkind),intent(in) :: latHeatSubVapCanopy ! latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) + real(rkind),intent(in) :: latHeatSubVapGround ! latent heat of sublimation/vaporization for the ground surface (J kg-1) + ! input: canopy and ground temperature + real(rkind),intent(in) :: canairTemp ! temperature of the canopy air space (K) + real(rkind),intent(in) :: canopyTemp ! canopy temperature (K) + real(rkind),intent(in) :: groundTemp ! ground temperature (K) + real(rkind),intent(in) :: satVP_CanopyTemp ! saturation vapor pressure at the temperature of the veg canopy (Pa) + real(rkind),intent(in) :: satVP_GroundTemp ! saturation vapor pressure at the temperature of the ground (Pa) + real(rkind),intent(in) :: dSVPCanopy_dCanopyTemp ! derivative in canopy saturation vapor pressure w.r.t. canopy temperature (Pa K-1) + real(rkind),intent(in) :: dSVPGround_dGroundTemp ! derivative in ground saturation vapor pressure w.r.t. ground temperature (Pa K-1) + ! input: diagnostic variables + real(rkind),intent(in) :: exposedVAI ! exposed vegetation area index -- leaf plus stem (m2 m-2) + real(rkind),intent(in) :: canopyWetFraction ! fraction of canopy that is wet [0-1] + real(rkind),intent(in) :: dCanopyWetFraction_dWat ! derivative in the canopy wetted fraction w.r.t. liquid water content (kg-1 m-2) + real(rkind),intent(in) :: dCanopyWetFraction_dT ! derivative in the canopy wetted fraction w.r.t. canopy temperature (K-1) + real(rkind),intent(in) :: canopySunlitLAI ! sunlit leaf area (-) + real(rkind),intent(in) :: canopyShadedLAI ! shaded leaf area (-) + real(rkind),intent(in) :: soilRelHumidity ! relative humidity in the soil pores [0-1] + real(rkind),intent(in) :: soilResistance ! resistance from the soil (s m-1) + real(rkind),intent(in) :: leafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) + real(rkind),intent(in) :: groundResistance ! below canopy aerodynamic resistance (s m-1) + real(rkind),intent(in) :: canopyResistance ! above canopy aerodynamic resistance (s m-1) + real(rkind),intent(in) :: stomResistSunlit ! stomatal resistance for sunlit leaves (s m-1) + real(rkind),intent(in) :: stomResistShaded ! stomatal resistance for shaded leaves (s m-1) + ! input: derivatives in scalar resistances + real(rkind),intent(in) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + real(rkind),intent(in) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind),intent(in) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + real(rkind),intent(in) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind),intent(in) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + ! --------------------------------------------------------------------------------------------------------------------------------------------------------------- + ! output: conductances -- used to test derivatives + real(rkind),intent(out) :: leafConductance ! leaf conductance (m s-1) + real(rkind),intent(out) :: canopyConductance ! canopy conductance (m s-1) + real(rkind),intent(out) :: groundConductanceSH ! ground conductance for sensible heat (m s-1) + real(rkind),intent(out) :: groundConductanceLH ! ground conductance for latent heat -- includes soil resistance (m s-1) + real(rkind),intent(out) :: evapConductance ! conductance for evaporation (m s-1) + real(rkind),intent(out) :: transConductance ! conductance for transpiration (m s-1) + real(rkind),intent(out) :: totalConductanceSH ! total conductance for sensible heat (m s-1) + real(rkind),intent(out) :: totalConductanceLH ! total conductance for latent heat (m s-1) + ! output: canopy air space variables + real(rkind),intent(out) :: VP_CanopyAir ! vapor pressure of the canopy air space (Pa) + ! output: fluxes from the vegetation canopy + real(rkind),intent(out) :: senHeatCanopy ! sensible heat flux from the canopy to the canopy air space (W m-2) + real(rkind),intent(out) :: latHeatCanopyEvap ! latent heat flux associated with evaporation from the canopy to the canopy air space (W m-2) + real(rkind),intent(out) :: latHeatCanopyTrans ! latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) + ! output: fluxes from non-vegetated surfaces (ground surface below vegetation, bare ground, or snow covered vegetation) + real(rkind),intent(out) :: senHeatGround ! sensible heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) + real(rkind),intent(out) :: latHeatGround ! latent heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) + ! output: total heat fluxes to the atmosphere + real(rkind),intent(out) :: senHeatTotal ! total sensible heat flux to the atmosphere (W m-2) + real(rkind),intent(out) :: latHeatTotal ! total latent heat flux to the atmosphere (W m-2) + ! output: net fluxes + real(rkind),intent(out) :: turbFluxCanair ! net turbulent heat fluxes at the canopy air space (W m-2) + real(rkind),intent(out) :: turbFluxCanopy ! net turbulent heat fluxes at the canopy (W m-2) + real(rkind),intent(out) :: turbFluxGround ! net turbulent heat fluxes at the ground surface (W m-2) + ! output: energy flux derivatives + real(rkind),intent(out) :: dTurbFluxCanair_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(rkind),intent(out) :: dTurbFluxCanair_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) + real(rkind),intent(out) :: dTurbFluxCanair_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) + real(rkind),intent(out) :: dTurbFluxCanopy_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(rkind),intent(out) :: dTurbFluxCanopy_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + real(rkind),intent(out) :: dTurbFluxCanopy_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + real(rkind),intent(out) :: dTurbFluxGround_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(rkind),intent(out) :: dTurbFluxGround_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + real(rkind),intent(out) :: dTurbFluxGround_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + ! output: liquid flux derivatives (canopy evap) + real(rkind),intent(out) :: dLatHeatCanopyEvap_dCanWat ! derivative in latent heat of canopy evaporation w.r.t. canopy total water content (W kg-1) + real(rkind),intent(out) :: dLatHeatCanopyEvap_dTCanair ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) + real(rkind),intent(out) :: dLatHeatCanopyEvap_dTCanopy ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) + real(rkind),intent(out) :: dLatHeatCanopyEvap_dTGround ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) + ! output: liquid flux derivatives (ground evap) + real(rkind),intent(out) :: dLatHeatGroundEvap_dCanWat ! derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) + real(rkind),intent(out) :: dLatHeatGroundEvap_dTCanair ! derivative in latent heat of ground evaporation w.r.t. canopy air temperature (W m-2 K-1) + real(rkind),intent(out) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) + real(rkind),intent(out) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) + ! output: latent heat flux derivatives (canopy trans) + real(rkind) :: dLatHeatCanopyTrans_dCanWat ! derivative in the latent heat of canopy transpiration w.r.t. canopy total water (J kg-1 s-1) + real(rkind) :: dLatHeatCanopyTrans_dTCanair ! derivative in the latent heat of canopy transpiration w.r.t. canopy air temperature + real(rkind) :: dLatHeatCanopyTrans_dTCanopy ! derivative in the latent heat of canopy transpiration w.r.t. canopy temperature + real(rkind) :: dLatHeatCanopyTrans_dTGround ! derivative in the latent heat of canopy transpiration flux w.r.t. ground temperature + ! output: cross derivatives + real(rkind),intent(out) :: dTurbFluxCanair_dCanWat ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) + real(rkind),intent(out) :: dTurbFluxCanopy_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + real(rkind),intent(out) :: dTurbFluxGround_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! * compute resistance for the case where the canopy is exposed - ! compute the stability correction for resistance from canopy air space to air above the canopy (-) - call aStability(& - ! input - derivDesired, & ! input: logical flag to compute analytical derivatives - ixStability, & ! input: choice of stability function - ! input: forcing data, diagnostic and state variables - mHeight, & ! input: measurement height (m) - airTemp, & ! input: air temperature above the canopy (K) - canairTemp, & ! input: temperature of the canopy air space (K) - windspd, & ! input: wind speed above the canopy (m s-1) - ! input: stability parameters - critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam, & ! input: parameter in Louis (1979) stability function - Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function - ! output - RiBulkCanopy, & ! output: bulk Richardson number (-) - canopyStabilityCorrection, & ! output: stability correction for turbulent heat fluxes (-) - dCanopyStabilityCorrection_dRich, & ! output: derivative in stability correction w.r.t. Richardson number for the canopy (-) - dCanopyStabilityCorrection_dAirTemp, & ! output: (not used) derivative in stability correction w.r.t. air temperature (K-1) - dCanopyStabilityCorrection_dCasTemp, & ! output: derivative in stability correction w.r.t. canopy air space temperature (K-1) - err, cmessage ) ! output: error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - ! compute turbulent exchange coefficient (-) - canopyExNeut = (vkc**2._rkind) / ( log((mHeight - zeroPlaneDisplacement)/z0Canopy))**2._rkind ! coefficient under conditions of neutral stability - sfc2AtmExchangeCoeff_canopy = canopyExNeut*canopyStabilityCorrection ! after stability corrections - - ! compute the friction velocity (m s-1) - frictionVelocity = windspd * sqrt(sfc2AtmExchangeCoeff_canopy) - - ! compute the above-canopy resistance (s m-1) - canopyResistance = 1._rkind/(sfc2AtmExchangeCoeff_canopy*windspd) - if(canopyResistance < 0._rkind)then; err=20; message=trim(message)//'canopy resistance < 0'; return; end if - - ! compute windspeed at the top of the canopy above snow depth (m s-1) - ! NOTE: stability corrections cancel out - windConvFactor_fv = log((heightCanopyTopAboveSnow - zeroPlaneDisplacement)/z0Canopy) / log((mHeight - snowDepth - zeroPlaneDisplacement)/z0Canopy) - windspdCanopyTop = windspd*windConvFactor_fv - - ! compute the windspeed reduction - ! Refs: Norman et al. (Ag. Forest Met., 1995) -- citing Goudriaan (1977 manuscript "crop micrometeorology: a simulation study", Wageningen). - windReductionFactor = windReductionParam * exposedVAI**twoThirds * (heightCanopyTopAboveSnow - heightCanopyBottomAboveSnow)**oneThird / leafDimension**oneThird - - ! compute windspeed at the height z0Canopy+zeroPlaneDisplacement (m s-1) - referenceHeight = z0Canopy+zeroPlaneDisplacement - windConvFactor = exp(-windReductionFactor*(1._rkind - (referenceHeight/heightCanopyTopAboveSnow))) - windspdRefHeight = windspdCanopyTop*windConvFactor - - ! compute windspeed at the bottom of the canopy relative to the snow depth (m s-1) - windConvFactor = exp(-windReductionFactor*(1._rkind - (heightCanopyBottomAboveSnow/heightCanopyTopAboveSnow))) - windspdCanopyBottom = windspdCanopyTop*windConvFactor - - ! compute the leaf boundary layer resistance (s m-1) - singleLeafConductance = leafExchangeCoeff*sqrt(windspdCanopyTop/leafDimension) - leaf2CanopyScaleFactor = (2._rkind/windReductionFactor) * (1._rkind - exp(-windReductionFactor/2._rkind)) ! factor to scale from the leaf to the canopy - canopyLeafConductance = singleLeafConductance*leaf2CanopyScaleFactor - leafResistance = 1._rkind/(canopyLeafConductance) - if(leafResistance < 0._rkind)then; err=20; message=trim(message)//'leaf resistance < 0'; return; end if - - ! compute eddy diffusivity for heat at the top of the canopy (m2 s-1) - ! Note: use of friction velocity here includes stability adjustments - ! Note: max used to avoid dividing by zero - eddyDiffusCanopyTop = max(vkc*FrictionVelocity*(heightCanopyTopAboveSnow - zeroPlaneDisplacement), mpe) - - ! compute the resistance between the surface and canopy air UNDER NEUTRAL CONDITIONS (s m-1) - - ! case 1: assume exponential profile extends from the snow depth plus surface roughness length to the displacement height plus vegetation roughness - if(ixWindProfile==exponential .or. heightCanopyBottomAboveSnowz0Ground+xTolerance - else + ! local variables -- general + real(rkind) :: fpart1,fpart2 ! different parts of a function + real(rkind) :: dPart0,dpart1,dpart2 ! derivatives for different parts of a function + ! local variables -- "constants" + real(rkind) :: volHeatCapacityAir ! volumetric heat capacity of air (J m-3) + real(rkind) :: latentHeatConstant ! latent heat constant (kg m-3 K-1) + ! local variables -- derivatives for energy conductances + real(rkind) :: dEvapCond_dCanopyTemp ! derivative in evap conductance w.r.t. canopy temperature + real(rkind) :: dTransCond_dCanopyTemp ! derivative in trans conductance w.r.t. canopy temperature + real(rkind) :: dCanopyCond_dCanairTemp ! derivative in canopy conductance w.r.t. canopy air temperature + real(rkind) :: dCanopyCond_dCanopyTemp ! derivative in canopy conductance w.r.t. canopy temperature + real(rkind) :: dGroundCondSH_dCanairTemp ! derivative in ground conductance of sensible heat w.r.t. canopy air temperature + real(rkind) :: dGroundCondSH_dCanopyTemp ! derivative in ground conductance of sensible heat w.r.t. canopy temperature + real(rkind) :: dGroundCondSH_dGroundTemp ! derivative in ground conductance of sensible heat w.r.t. ground temperature + ! local variables -- derivatives for mass conductances + real(rkind) :: dGroundCondLH_dCanairTemp ! derivative in ground conductance w.r.t. canopy air temperature + real(rkind) :: dGroundCondLH_dCanopyTemp ! derivative in ground conductance w.r.t. canopy temperature + real(rkind) :: dGroundCondLH_dGroundTemp ! derivative in ground conductance w.r.t. ground temperature + ! local variables -- derivatives for the canopy air space variables + real(rkind) :: fPart_VP ! part of the function for vapor pressure of the canopy air space + real(rkind) :: leafConductanceTr ! leaf conductance for transpiration (m s-1) + real(rkind) :: dVPCanopyAir_dTCanair ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the canopy air space + real(rkind) :: dVPCanopyAir_dTCanopy ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the canopy + real(rkind) :: dVPCanopyAir_dTGround ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the ground + real(rkind) :: dVPCanopyAir_dWetFrac ! derivative of vapor pressure in the canopy air space w.r.t. wetted fraction of the canopy + real(rkind) :: dVPCanopyAir_dCanWat ! derivative of vapor pressure in the canopy air space w.r.t. canopy total water content + ! local variables -- sensible heat flux derivatives + real(rkind) :: dSenHeatTotal_dTCanair ! derivative in the total sensible heat flux w.r.t. canopy air temperature + real(rkind) :: dSenHeatTotal_dTCanopy ! derivative in the total sensible heat flux w.r.t. canopy air temperature + real(rkind) :: dSenHeatTotal_dTGround ! derivative in the total sensible heat flux w.r.t. ground temperature + real(rkind) :: dSenHeatCanopy_dTCanair ! derivative in the canopy sensible heat flux w.r.t. canopy air temperature + real(rkind) :: dSenHeatCanopy_dTCanopy ! derivative in the canopy sensible heat flux w.r.t. canopy temperature + real(rkind) :: dSenHeatCanopy_dTGround ! derivative in the canopy sensible heat flux w.r.t. ground temperature + real(rkind) :: dSenHeatGround_dTCanair ! derivative in the ground sensible heat flux w.r.t. canopy air temperature + real(rkind) :: dSenHeatGround_dTCanopy ! derivative in the ground sensible heat flux w.r.t. canopy temperature + real(rkind) :: dSenHeatGround_dTGround ! derivative in the ground sensible heat flux w.r.t. ground temperature + ! local variables -- wetted fraction derivatives + real(rkind) :: dLatHeatCanopyEvap_dWetFrac ! derivative in the latent heat of canopy evaporation w.r.t. canopy wet fraction (W m-2) + real(rkind) :: dLatHeatCanopyTrans_dWetFrac ! derivative in the latent heat of canopy transpiration w.r.t. canopy wet fraction (W m-2) + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='turbFluxes/' - ! compute the neutral ground resistance - ! (first, component between heightCanopyBottomAboveSnow and z0Canopy+zeroPlaneDisplacement) - tmp1 = exp(-windReductionFactor* heightCanopyBottomAboveSnow/heightCanopyTopAboveSnow) - tmp2 = exp(-windReductionFactor*(z0Canopy+zeroPlaneDisplacement)/heightCanopyTopAboveSnow) - groundResistanceNeutral = ( heightCanopyTopAboveSnow*exp(windReductionFactor) / (windReductionFactor*eddyDiffusCanopyTop) ) * (tmp1 - tmp2) - ! (add log-below-canopy component) - groundResistanceNeutral = groundResistanceNeutral + (1._rkind/(max(0.1_rkind,windspdCanopyBottom)*vkc**2._rkind))*(log(heightCanopyBottomAboveSnow/z0Ground))**2._rkind - - endif ! switch between exponential profile and log-below-canopy - - ! compute the stability correction for resistance from the ground to the canopy air space (-) - ! NOTE: here we are interested in the windspeed at height z0Canopy+zeroPlaneDisplacement - call aStability(& - ! input - derivDesired, & ! input: logical flag to compute analytical derivatives - ixStability, & ! input: choice of stability function - ! input: forcing data, diagnostic and state variables - referenceHeight, & ! input: height of the canopy air space temperature/wind (m) - canairTemp, & ! input: temperature of the canopy air space (K) - groundTemp, & ! input: temperature of the ground surface (K) - max(0.1_rkind,windspdRefHeight), & ! input: wind speed at height z0Canopy+zeroPlaneDisplacement (m s-1) - ! input: stability parameters - critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam, & ! input: parameter in Louis (1979) stability function - Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function - ! output - RiBulkGround, & ! output: bulk Richardson number (-) - groundStabilityCorrection, & ! output: stability correction for turbulent heat fluxes (-) - dGroundStabilityCorrection_dRich, & ! output: derivative in stability correction w.r.t. Richardson number for the canopy (-) - dGroundStabilityCorrection_dCasTemp, & ! output: derivative in stability correction w.r.t. canopy air space temperature (K-1) - dGroundStabilityCorrection_dSfcTemp, & ! output: derivative in stability correction w.r.t. surface temperature (K-1) - err, cmessage ) ! output: error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - ! compute the ground resistance - groundResistance = groundResistanceNeutral / groundStabilityCorrection - if(groundResistance < 0._rkind)then; err=20; message=trim(message)//'ground resistance < 0 [vegetation is present]'; return; end if - - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! * compute resistance for the case without a canopy (bare ground, or canopy completely buried with snow) - else - - ! no canopy, so set huge resistances (not used) - canopyResistance = 1.e12_rkind ! not used: huge resistance, so conductance is essentially zero - leafResistance = 1.e12_rkind ! not used: huge resistance, so conductance is essentially zero - - ! check that measurement height above the ground surface is above the roughness length - if(mHeight < snowDepth+z0Ground)then; err=20; message=trim(message)//'measurement height < snow depth + roughness length'; return; end if - - ! compute the resistance between the surface and canopy air UNDER NEUTRAL CONDITIONS (s m-1) - groundExNeut = (vkc**2._rkind) / ( log((mHeight - snowDepth)/z0Ground)**2._rkind) ! turbulent transfer coefficient under conditions of neutral stability (-) - groundResistanceNeutral = 1._rkind / (groundExNeut*windspd) - - ! define height above the snow surface - heightAboveGround = mHeight - snowDepth - - ! check that measurement height above the ground surface is above the roughness length - if(heightAboveGround < z0Ground)then - print*, 'z0Ground = ', z0Ground - print*, 'mHeight = ', mHeight - print*, 'snowDepth = ', snowDepth - print*, 'heightAboveGround = ', heightAboveGround - message=trim(message)//'height above ground < roughness length [likely due to snow accumulation]' - err=20; return - end if + ! compute constants + volHeatCapacityAir = iden_air*cp_air ! volumetric heat capacity of air (J m-3) + latentHeatConstant = iden_air*w_ratio/airpres ! latent heat constant for (kg m-3 Pa-1) - ! compute ground stability correction - call aStability(& - ! input - derivDesired, & ! input: logical flag to compute analytical derivatives - ixStability, & ! input: choice of stability function - ! input: forcing data, diagnostic and state variables - heightAboveGround, & ! input: measurement height above the ground surface (m) - airtemp, & ! input: temperature above the ground surface (K) - groundTemp, & ! input: trial value of surface temperature -- "surface" is either canopy or ground (K) - windspd, & ! input: wind speed above the ground surface (m s-1) - ! input: stability parameters - critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam, & ! input: parameter in Louis (1979) stability function - Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function - ! output - RiBulkGround, & ! output: bulk Richardson number (-) - groundStabilityCorrection, & ! output: stability correction for turbulent heat fluxes (-) - dGroundStabilityCorrection_dRich, & ! output: derivative in stability correction w.r.t. Richardson number for the ground surface (-) - dGroundStabilityCorrection_dAirTemp, & ! output: (not used) derivative in stability correction w.r.t. air temperature (K-1) - dGroundStabilityCorrection_dSfcTemp, & ! output: derivative in stability correction w.r.t. surface temperature (K-1) - err, cmessage ) ! output: error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - ! compute the ground resistance (after stability corrections) - groundResistance = groundResistanceNeutral/groundStabilityCorrection - if(groundResistance < 0._rkind)then; err=20; message=trim(message)//'ground resistance < 0 [no vegetation]'; return; end if - - ! set all canopy variables to missing (no canopy!) - z0Canopy = missingValue ! roughness length of the vegetation canopy (m) - RiBulkCanopy = missingValue ! bulk Richardson number for the canopy (-) - windReductionFactor = missingValue ! canopy wind reduction factor (-) - zeroPlaneDisplacement = missingValue ! zero plane displacement (m) - canopyStabilityCorrection = missingValue ! stability correction for the canopy (-) - eddyDiffusCanopyTop = missingValue ! eddy diffusivity for heat at the top of the canopy (m2 s-1) - frictionVelocity = missingValue ! friction velocity (m s-1) - windspdCanopyTop = missingValue ! windspeed at the top of the canopy (m s-1) - windspdCanopyBottom = missingValue ! windspeed at the height of the bottom of the canopy (m s-1) - - end if ! (if no canopy) - - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! * compute derivatives - if(derivDesired)then ! if analytical derivatives are desired - - ! derivatives for the vegetation canopy - if(computeVegFlux) then ! (if vegetation is exposed) + ! ***** + ! * compute conductances, and derivatives... + ! ****************************************** - ! ***** compute derivatives w.r.t. canopy temperature - ! NOTE: derivatives are zero because using canopy air space temperature - dCanopyResistance_dTCanopy = 0._rkind ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - dGroundResistance_dTCanopy = 0._rkind ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - - ! ***** compute derivatives w.r.t. ground temperature (s m-1 K-1) - dGroundResistance_dTGround = -(groundResistanceNeutral*dGroundStabilityCorrection_dSfcTemp)/(groundStabilityCorrection**2._rkind) - - ! ***** compute derivatives w.r.t. temperature of the canopy air space (s m-1 K-1) - ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - dCanopyResistance_dTCanair = -dCanopyStabilityCorrection_dCasTemp/(windspd*canopyExNeut*canopyStabilityCorrection**2._rkind) - ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - ! (compute derivative in NEUTRAL ground resistance w.r.t. canopy air temperature (s m-1 K-1)) - dFV_dT = windspd*canopyExNeut*dCanopyStabilityCorrection_dCasTemp/(sqrt(sfc2AtmExchangeCoeff_canopy)*2._rkind) ! d(frictionVelocity)/d(canopy air temperature) - dED_dT = dFV_dT*vkc*(heightCanopyTopAboveSnow - zeroPlaneDisplacement) ! d(eddyDiffusCanopyTop)d(canopy air temperature) - dGR_dT = -dED_dT*(tmp1 - tmp2)*heightCanopyTopAboveSnow*exp(windReductionFactor) / (windReductionFactor*eddyDiffusCanopyTop**2._rkind) ! d(groundResistanceNeutral)/d(canopy air temperature) - ! (stitch everything together -- product rule) - dGroundResistance_dTCanair = dGR_dT/groundStabilityCorrection - groundResistanceNeutral*dGroundStabilityCorrection_dCasTemp/(groundStabilityCorrection**2._rkind) - - ! ***** compute resistances for non-vegetated surfaces (e.g., snow) + ! compute conductances for sensible heat (m s-1) + if(computeVegFlux)then + leafConductance = exposedVAI/leafResistance + leafConductanceTr = canopySunlitLAI/(leafResistance+stomResistSunlit) + canopyShadedLAI/(leafResistance+stomResistShaded) + canopyConductance = 1._rkind/canopyResistance else + leafConductance = 0._rkind + canopyConductance = 0._rkind + end if + groundConductanceSH = 1._rkind/groundResistance - ! set canopy derivatives to zero (non-vegetated, remember) - dCanopyResistance_dTCanopy = 0._rkind - dGroundResistance_dTCanopy = 0._rkind + ! compute total conductance for sensible heat + totalConductanceSH = leafConductance + groundConductanceSH + canopyConductance - ! compute derivatives for ground resistance - dGroundResistance_dTGround = -dGroundStabilityCorrection_dSfcTemp/(windspd*groundExNeut*groundStabilityCorrection**2._rkind) + ! compute conductances for latent heat (m s-1) + if(computeVegFlux)then + evapConductance = canopyWetFraction*leafConductance + transConductance = (1._rkind - canopyWetFraction) * leafConductanceTr + !write(*,'(a,10(f14.8,1x))') 'canopySunlitLAI, canopyShadedLAI, stomResistSunlit, stomResistShaded, leafResistance, canopyWetFraction = ', & + ! canopySunlitLAI, canopyShadedLAI, stomResistSunlit, stomResistShaded, leafResistance, canopyWetFraction + else + evapConductance = 0._rkind + transConductance = 0._rkind + end if + groundConductanceLH = 1._rkind/(groundResistance + soilResistance) ! NOTE: soilResistance accounts for fractional snow, and =0 when snow cover is 100% + totalConductanceLH = evapConductance + transConductance + groundConductanceLH + canopyConductance - end if ! (switch between vegetated and non-vegetated surfaces) + ! check sensible heat conductance + if(totalConductanceSH < -tinyVal .or. groundConductanceSH < -tinyVal .or. canopyConductance < -tinyVal)then + message=trim(message)//'negative conductance for sensible heat' + err=20; return + endif - ! * analytical derivatives not desired - else - dGroundResistance_dTGround = missingValue - dGroundResistance_dTCanopy = missingValue - dCanopyResistance_dTCanopy = missingValue - end if + ! check latent heat conductance + if(totalConductanceLH < tinyVal .or. groundConductanceLH < -tinyVal)then + message=trim(message)//'negative conductance for latent heat' + err=20; return + endif - ! test - !print*, 'dGroundResistance_dTGround = ', dGroundResistance_dTGround - !print*, 'dGroundResistance_dTCanopy = ', dGroundResistance_dTCanopy - !print*, 'dCanopyResistance_dTCanopy = ', dCanopyResistance_dTCanopy - !pause 'in aeroResist' - - end subroutine aeroResist - - - ! ******************************************************************************************************* - ! private subroutine soilResist: compute soil moisture factor controlling stomatal resistance - ! ******************************************************************************************************* - subroutine soilResist(& - ! input (model decisions) - ixSoilResist, & ! intent(in): choice of function for the soil moisture control on stomatal resistance - ixGroundwater, & ! intent(in): choice of groundwater representation - ! input (state variables) - mLayerMatricHead, & ! intent(in): matric head in each layer (m) - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water in each layer - scalarAquiferStorage, & ! intent(in): aquifer storage (m) - ! input (diagnostic variables) - mLayerRootDensity, & ! intent(in): root density in each layer (-) - scalarAquiferRootFrac, & ! intent(in): fraction of roots below the lowest unsaturated layer (-) - ! input (parameters) - plantWiltPsi, & ! intent(in): matric head at wilting point (m) - soilStressParam, & ! intent(in): parameter in the exponential soil stress function (-) - critSoilWilting, & ! intent(in): critical vol. liq. water content when plants are wilting (-) - critSoilTranspire, & ! intent(in): critical vol. liq. water content when transpiration is limited (-) - critAquiferTranspire, & ! intent(in): critical aquifer storage value when transpiration is limited (m) - ! output - wAvgTranspireLimitFac, & ! intent(out): weighted average of the transpiration limiting factor (-) - mLayerTranspireLimitFac, & ! intent(out): transpiration limiting factor in each layer (-) - aquiferTranspireLimitFac, & ! intent(out): transpiration limiting factor for the aquifer (-) - err,message) ! intent(out): error control - ! ----------------------------------------------------------------------------------------------------------------------------------------- - USE mDecisions_module, only: NoahType,CLM_Type,SiB_Type ! options for the choice of function for the soil moisture control on stomatal resistance - USE mDecisions_module, only: bigBucket ! named variable that defines the "bigBucket" groundwater parameterization - implicit none - ! input (model decisions) - integer(i4b),intent(in) :: ixSoilResist ! choice of function for the soil moisture control on stomatal resistance - integer(i4b),intent(in) :: ixGroundwater ! choice of groundwater representation - ! input (variables) - real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head in each layer (m) - real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid water in each layer (-) - real(rkind),intent(in) :: scalarAquiferStorage ! aquifer storage (m) - ! input (diagnostic variables) - real(rkind),intent(in) :: mLayerRootDensity(:) ! root density in each layer (-) - real(rkind),intent(in) :: scalarAquiferRootFrac ! fraction of roots below the lowest unsaturated layer (-) - ! input (parameters) - real(rkind),intent(in) :: plantWiltPsi ! matric head at wilting point (m) - real(rkind),intent(in) :: soilStressParam ! parameter in the exponential soil stress function (-) - real(rkind),intent(in) :: critSoilWilting ! critical vol. liq. water content when plants are wilting (-) - real(rkind),intent(in) :: critSoilTranspire ! critical vol. liq. water content when transpiration is limited (-) - real(rkind),intent(in) :: critAquiferTranspire ! critical aquifer storage value when transpiration is limited (m) - ! output - real(rkind),intent(out) :: wAvgTranspireLimitFac ! intent(out): weighted average of the transpiration limiting factor (-) - real(rkind),intent(out) :: mLayerTranspireLimitFac(:) ! intent(out): transpiration limiting factor in each layer (-) - real(rkind),intent(out) :: aquiferTranspireLimitFac ! intent(out): transpiration limiting factor for the aquifer (-) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! local variables - real(rkind) :: gx ! stress function for the soil layers - real(rkind),parameter :: verySmall=epsilon(gx) ! a very small number - integer(i4b) :: iLayer ! index of soil layer - ! initialize error control - err=0; message='soilResist/' - - ! ** compute the factor limiting transpiration for each soil layer (-) - wAvgTranspireLimitFac = 0._rkind ! (initialize the weighted average) - do iLayer=1,size(mLayerMatricHead) - ! compute the soil stress function - select case(ixSoilResist) - case(NoahType) ! thresholded linear function of volumetric liquid water content - gx = (mLayerVolFracLiq(iLayer) - critSoilWilting) / (critSoilTranspire - critSoilWilting) - case(CLM_Type) ! thresholded linear function of matric head - if(mLayerMatricHead(iLayer) > plantWiltPsi)then - gx = 1._rkind - mLayerMatricHead(iLayer)/plantWiltPsi + ! * compute derivatives + ! NOTE: it may be more efficient to compute these derivatives when computing resistances + if(ixDerivMethod == analytical)then + + ! compute derivatives in individual conductances for sensible heat w.r.t. canopy temperature (m s-1 K-1) + if(computeVegFlux)then + dEvapCond_dCanopyTemp = dCanopyWetFraction_dT*leafConductance ! derivative in evap conductance w.r.t. canopy temperature + dTransCond_dCanopyTemp = -dCanopyWetFraction_dT*leafConductanceTr ! derivative in trans conductance w.r.t. canopy temperature + dCanopyCond_dCanairTemp = -dCanopyResistance_dTCanair/canopyResistance**2._rkind ! derivative in canopy conductance w.r.t. canopy air emperature + dCanopyCond_dCanopyTemp = -dCanopyResistance_dTCanopy/canopyResistance**2._rkind ! derivative in canopy conductance w.r.t. canopy temperature + dGroundCondSH_dCanairTemp = -dGroundResistance_dTCanair/groundResistance**2._rkind ! derivative in ground conductance w.r.t. canopy air temperature + dGroundCondSH_dCanopyTemp = -dGroundResistance_dTCanopy/groundResistance**2._rkind ! derivative in ground conductance w.r.t. canopy temperature + dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2._rkind ! derivative in ground conductance w.r.t. ground temperature else - gx = 0._rkind + dEvapCond_dCanopyTemp = 0._rkind ! derivative in evap conductance w.r.t. canopy temperature + dTransCond_dCanopyTemp = 0._rkind ! derivative in trans conductance w.r.t. canopy temperature + dCanopyCond_dCanairTemp = 0._rkind ! derivative in canopy conductance w.r.t. canopy air emperature + dCanopyCond_dCanopyTemp = 0._rkind ! derivative in canopy conductance w.r.t. canopy temperature + dGroundCondSH_dCanairTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy air temperature + dGroundCondSH_dCanopyTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy temperature + dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2._rkind ! derivative in ground conductance w.r.t. ground temperature end if - case(SiB_Type) ! exponential of the log of matric head - if(mLayerMatricHead(iLayer) < 0._rkind)then ! (unsaturated) - gx = 1._rkind - exp( -soilStressParam * ( log(plantWiltPsi/mLayerMatricHead(iLayer)) ) ) - else ! (saturated) - gx = 1._rkind + + ! compute derivatives in individual conductances for latent heat w.r.t. canopy temperature (m s-1 K-1) + if(computeVegFlux)then + dGroundCondLH_dCanairTemp = -dGroundResistance_dTCanair/(groundResistance+soilResistance)**2._rkind ! derivative in ground conductance w.r.t. canopy air temperature + dGroundCondLH_dCanopyTemp = -dGroundResistance_dTCanopy/(groundResistance+soilResistance)**2._rkind ! derivative in ground conductance w.r.t. canopy temperature + dGroundCondLH_dGroundTemp = -dGroundResistance_dTGround/(groundResistance+soilResistance)**2._rkind ! derivative in ground conductance w.r.t. ground temperature + else + dGroundCondLH_dCanairTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy air temperature + dGroundCondLH_dCanopyTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy temperature + dGroundCondLH_dGroundTemp = -dGroundResistance_dTGround/(groundResistance+soilResistance)**2._rkind ! derivative in ground conductance w.r.t. ground temperature end if - case default ! check identified the option - err=20; message=trim(message)//'cannot identify option for soil resistance'; return - end select - ! save the factor for the given layer (ensure between zero and one) - mLayerTranspireLimitFac(iLayer) = min( max(verySmall,gx), 1._rkind) - ! compute the weighted average (weighted by root density) - wAvgTranspireLimitFac = wAvgTranspireLimitFac + mLayerTranspireLimitFac(iLayer)*mLayerRootDensity(iLayer) - end do ! (looping through soil layers) - - ! ** compute the factor limiting evaporation in the aquifer - if(scalarAquiferRootFrac > verySmall)then - ! check that aquifer root fraction is allowed - if(ixGroundwater /= bigBucket)then - message=trim(message)//'aquifer evaporation only allowed for the big groundwater bucket -- increase the soil depth to account for roots' - err=20; return - end if - ! compute the factor limiting evaporation for the aquifer - aquiferTranspireLimitFac = min(scalarAquiferStorage/critAquiferTranspire, 1._rkind) - else ! (if there are roots in the aquifer) - aquiferTranspireLimitFac = 0._rkind - end if - ! compute the weighted average (weighted by root density) - wAvgTranspireLimitFac = wAvgTranspireLimitFac + aquiferTranspireLimitFac*scalarAquiferRootFrac - - end subroutine soilResist - - - ! ******************************************************************************** - ! private subroutine turbFluxes: compute turbulent heat fluxes - ! ******************************************************************************** - subroutine turbFluxes(& - ! input: model control - computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) - ixDerivMethod, & ! intent(in): choice of method used to compute derivative (analytical or numerical) - ! input: above-canopy forcing data - airtemp, & ! intent(in): air temperature at some height above the surface (K) - airpres, & ! intent(in): air pressure of the air above the vegetation canopy (Pa) - VPair, & ! intent(in): vapor pressure of the air above the vegetation canopy (Pa) - ! input: latent heat of sublimation/vaporization - latHeatSubVapCanopy, & ! intent(in): latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) - latHeatSubVapGround, & ! intent(in): latent heat of sublimation/vaporization for the ground surface (J kg-1) - ! input: canopy and ground temperature - canairTemp, & ! intent(in): temperature of the canopy air space (K) - canopyTemp, & ! intent(in): canopy temperature (K) - groundTemp, & ! intent(in): ground temperature (K) - satVP_CanopyTemp, & ! intent(in): saturation vapor pressure at the temperature of the veg canopy (Pa) - satVP_GroundTemp, & ! intent(in): saturation vapor pressure at the temperature of the ground (Pa) - dSVPCanopy_dCanopyTemp, & ! intent(in): derivative in canopy saturation vapor pressure w.r.t. canopy temperature (Pa K-1) - dSVPGround_dGroundTemp, & ! intent(in): derivative in ground saturation vapor pressure w.r.t. ground temperature (Pa K-1) - ! input: diagnostic variables - exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) - canopyWetFraction, & ! intent(in): fraction of canopy that is wet [0-1] - dCanopyWetFraction_dWat, & ! intent(in): derivative in the canopy wetted fraction w.r.t. total water content (kg-1 m-2) - dCanopyWetFraction_dT, & ! intent(in): derivative in wetted fraction w.r.t. canopy temperature (K-1) - canopySunlitLAI, & ! intent(in): sunlit leaf area (-) - canopyShadedLAI, & ! intent(in): shaded leaf area (-) - soilRelHumidity, & ! intent(in): relative humidity in the soil pores [0-1] - soilResistance, & ! intent(in): resistance from the soil (s m-1) - leafResistance, & ! intent(in): mean leaf boundary layer resistance per unit leaf area (s m-1) - groundResistance, & ! intent(in): below canopy aerodynamic resistance (s m-1) - canopyResistance, & ! intent(in): above canopy aerodynamic resistance (s m-1) - stomResistSunlit, & ! intent(in): stomatal resistance for sunlit leaves (s m-1) - stomResistShaded, & ! intent(in): stomatal resistance for shaded leaves (s m-1) - ! input: derivatives in scalar resistances - dGroundResistance_dTGround, & ! intent(in): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - dGroundResistance_dTCanopy, & ! intent(in): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - dGroundResistance_dTCanair, & ! intent(in): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - dCanopyResistance_dTCanopy, & ! intent(in): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - dCanopyResistance_dTCanair, & ! intent(in): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - ! output: conductances (used to check derivative calculations) - leafConductance, & ! intent(out): leaf conductance (m s-1) - canopyConductance, & ! intent(out): canopy conductance (m s-1) - groundConductanceSH, & ! intent(out): ground conductance for sensible heat (m s-1) - groundConductanceLH, & ! intent(out): ground conductance for latent heat -- includes soil resistance (m s-1) - evapConductance, & ! intent(out): conductance for evaporation (m s-1) - transConductance, & ! intent(out): conductance for transpiration (m s-1) - totalConductanceSH, & ! intent(out): total conductance for sensible heat (m s-1) - totalConductanceLH, & ! intent(out): total conductance for latent heat (m s-1) - ! output: canopy air space variables - VP_CanopyAir, & ! intent(out): vapor pressure of the canopy air space (Pa) - ! output: fluxes from the vegetation canopy - senHeatCanopy, & ! intent(out): sensible heat flux from the canopy to the canopy air space (W m-2) - latHeatCanopyEvap, & ! intent(out): latent heat flux associated with evaporation from the canopy to the canopy air space (W m-2) - latHeatCanopyTrans, & ! intent(out): latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) - ! output: fluxes from non-vegetated surfaces (ground surface below vegetation, bare ground, or snow covered vegetation) - senHeatGround, & ! intent(out): sensible heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) - latHeatGround, & ! intent(out): latent heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) - ! output: total heat fluxes to the atmosphere - senHeatTotal, & ! intent(out): total sensible heat flux to the atmosphere (W m-2) - latHeatTotal, & ! intent(out): total latent heat flux to the atmosphere (W m-2) - ! output: net fluxes - turbFluxCanair, & ! intent(out): net turbulent heat fluxes at the canopy air space (W m-2) - turbFluxCanopy, & ! intent(out): net turbulent heat fluxes at the canopy (W m-2) - turbFluxGround, & ! intent(out): net turbulent heat fluxes at the ground surface (W m-2) - ! output: flux derivatives - dTurbFluxCanair_dTCanair, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxCanair_dTCanopy, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxCanair_dTGround, & ! intent(out): derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanair, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanopy, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxCanopy_dTGround, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxGround_dTCanair, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxGround_dTCanopy, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxGround_dTGround, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - ! output: liquid flux derivatives (canopy evap) - dLatHeatCanopyEvap_dCanWat, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy total water content (J kg-1 s-1) - dLatHeatCanopyEvap_dTCanair, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTCanopy, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTGround, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) - ! output: liquid flux derivatives (ground evap) - dLatHeatGroundEvap_dCanWat, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) - dLatHeatGroundEvap_dTCanair, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy air temperature - dLatHeatGroundEvap_dTCanopy, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy temperature - dLatHeatGroundEvap_dTGround, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. ground temperature - ! output: latent heat flux derivatives (canopy trans) - dLatHeatCanopyTrans_dCanWat, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy total water (J kg-1 s-1) - dLatHeatCanopyTrans_dTCanair, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy air temperature - dLatHeatCanopyTrans_dTCanopy, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy temperature - dLatHeatCanopyTrans_dTGround, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. ground temperature - ! output: cross derivatives - dTurbFluxCanair_dCanWat, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) - dTurbFluxCanopy_dCanWat, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - dTurbFluxGround_dCanWat, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - ! output: error control - err,message ) ! intent(out): error control - ! ----------------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) - integer(i4b),intent(in) :: ixDerivMethod ! choice of method used to compute derivative (analytical or numerical) - ! input: above-canopy forcing data - real(rkind),intent(in) :: airtemp ! air temperature at some height above the surface (K) - real(rkind),intent(in) :: airpres ! air pressure of the air above the vegetation canopy (Pa) - real(rkind),intent(in) :: VPair ! vapor pressure of the air above the vegetation canopy (Pa) - ! input: latent heat of sublimation/vaporization - real(rkind),intent(in) :: latHeatSubVapCanopy ! latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) - real(rkind),intent(in) :: latHeatSubVapGround ! latent heat of sublimation/vaporization for the ground surface (J kg-1) - ! input: canopy and ground temperature - real(rkind),intent(in) :: canairTemp ! temperature of the canopy air space (K) - real(rkind),intent(in) :: canopyTemp ! canopy temperature (K) - real(rkind),intent(in) :: groundTemp ! ground temperature (K) - real(rkind),intent(in) :: satVP_CanopyTemp ! saturation vapor pressure at the temperature of the veg canopy (Pa) - real(rkind),intent(in) :: satVP_GroundTemp ! saturation vapor pressure at the temperature of the ground (Pa) - real(rkind),intent(in) :: dSVPCanopy_dCanopyTemp ! derivative in canopy saturation vapor pressure w.r.t. canopy temperature (Pa K-1) - real(rkind),intent(in) :: dSVPGround_dGroundTemp ! derivative in ground saturation vapor pressure w.r.t. ground temperature (Pa K-1) - ! input: diagnostic variables - real(rkind),intent(in) :: exposedVAI ! exposed vegetation area index -- leaf plus stem (m2 m-2) - real(rkind),intent(in) :: canopyWetFraction ! fraction of canopy that is wet [0-1] - real(rkind),intent(in) :: dCanopyWetFraction_dWat ! derivative in the canopy wetted fraction w.r.t. liquid water content (kg-1 m-2) - real(rkind),intent(in) :: dCanopyWetFraction_dT ! derivative in the canopy wetted fraction w.r.t. canopy temperature (K-1) - real(rkind),intent(in) :: canopySunlitLAI ! sunlit leaf area (-) - real(rkind),intent(in) :: canopyShadedLAI ! shaded leaf area (-) - real(rkind),intent(in) :: soilRelHumidity ! relative humidity in the soil pores [0-1] - real(rkind),intent(in) :: soilResistance ! resistance from the soil (s m-1) - real(rkind),intent(in) :: leafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) - real(rkind),intent(in) :: groundResistance ! below canopy aerodynamic resistance (s m-1) - real(rkind),intent(in) :: canopyResistance ! above canopy aerodynamic resistance (s m-1) - real(rkind),intent(in) :: stomResistSunlit ! stomatal resistance for sunlit leaves (s m-1) - real(rkind),intent(in) :: stomResistShaded ! stomatal resistance for shaded leaves (s m-1) - ! input: derivatives in scalar resistances - real(rkind),intent(in) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - real(rkind),intent(in) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind),intent(in) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - real(rkind),intent(in) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind),intent(in) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - ! --------------------------------------------------------------------------------------------------------------------------------------------------------------- - ! output: conductances -- used to test derivatives - real(rkind),intent(out) :: leafConductance ! leaf conductance (m s-1) - real(rkind),intent(out) :: canopyConductance ! canopy conductance (m s-1) - real(rkind),intent(out) :: groundConductanceSH ! ground conductance for sensible heat (m s-1) - real(rkind),intent(out) :: groundConductanceLH ! ground conductance for latent heat -- includes soil resistance (m s-1) - real(rkind),intent(out) :: evapConductance ! conductance for evaporation (m s-1) - real(rkind),intent(out) :: transConductance ! conductance for transpiration (m s-1) - real(rkind),intent(out) :: totalConductanceSH ! total conductance for sensible heat (m s-1) - real(rkind),intent(out) :: totalConductanceLH ! total conductance for latent heat (m s-1) - ! output: canopy air space variables - real(rkind),intent(out) :: VP_CanopyAir ! vapor pressure of the canopy air space (Pa) - ! output: fluxes from the vegetation canopy - real(rkind),intent(out) :: senHeatCanopy ! sensible heat flux from the canopy to the canopy air space (W m-2) - real(rkind),intent(out) :: latHeatCanopyEvap ! latent heat flux associated with evaporation from the canopy to the canopy air space (W m-2) - real(rkind),intent(out) :: latHeatCanopyTrans ! latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) - ! output: fluxes from non-vegetated surfaces (ground surface below vegetation, bare ground, or snow covered vegetation) - real(rkind),intent(out) :: senHeatGround ! sensible heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) - real(rkind),intent(out) :: latHeatGround ! latent heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) - ! output: total heat fluxes to the atmosphere - real(rkind),intent(out) :: senHeatTotal ! total sensible heat flux to the atmosphere (W m-2) - real(rkind),intent(out) :: latHeatTotal ! total latent heat flux to the atmosphere (W m-2) - ! output: net fluxes - real(rkind),intent(out) :: turbFluxCanair ! net turbulent heat fluxes at the canopy air space (W m-2) - real(rkind),intent(out) :: turbFluxCanopy ! net turbulent heat fluxes at the canopy (W m-2) - real(rkind),intent(out) :: turbFluxGround ! net turbulent heat fluxes at the ground surface (W m-2) - ! output: energy flux derivatives - real(rkind),intent(out) :: dTurbFluxCanair_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(rkind),intent(out) :: dTurbFluxCanair_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dTurbFluxCanair_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) - real(rkind),intent(out) :: dTurbFluxCanopy_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(rkind),intent(out) :: dTurbFluxCanopy_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dTurbFluxCanopy_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - real(rkind),intent(out) :: dTurbFluxGround_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(rkind),intent(out) :: dTurbFluxGround_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dTurbFluxGround_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - ! output: liquid flux derivatives (canopy evap) - real(rkind),intent(out) :: dLatHeatCanopyEvap_dCanWat ! derivative in latent heat of canopy evaporation w.r.t. canopy total water content (W kg-1) - real(rkind),intent(out) :: dLatHeatCanopyEvap_dTCanair ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) - real(rkind),intent(out) :: dLatHeatCanopyEvap_dTCanopy ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dLatHeatCanopyEvap_dTGround ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) - ! output: liquid flux derivatives (ground evap) - real(rkind),intent(out) :: dLatHeatGroundEvap_dCanWat ! derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) - real(rkind),intent(out) :: dLatHeatGroundEvap_dTCanair ! derivative in latent heat of ground evaporation w.r.t. canopy air temperature (W m-2 K-1) - real(rkind),intent(out) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) - ! output: latent heat flux derivatives (canopy trans) - real(rkind) :: dLatHeatCanopyTrans_dCanWat ! derivative in the latent heat of canopy transpiration w.r.t. canopy total water (J kg-1 s-1) - real(rkind) :: dLatHeatCanopyTrans_dTCanair ! derivative in the latent heat of canopy transpiration w.r.t. canopy air temperature - real(rkind) :: dLatHeatCanopyTrans_dTCanopy ! derivative in the latent heat of canopy transpiration w.r.t. canopy temperature - real(rkind) :: dLatHeatCanopyTrans_dTGround ! derivative in the latent heat of canopy transpiration flux w.r.t. ground temperature - ! output: cross derivatives - real(rkind),intent(out) :: dTurbFluxCanair_dCanWat ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) - real(rkind),intent(out) :: dTurbFluxCanopy_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - real(rkind),intent(out) :: dTurbFluxGround_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! local variables -- general - real(rkind) :: fpart1,fpart2 ! different parts of a function - real(rkind) :: dPart0,dpart1,dpart2 ! derivatives for different parts of a function - ! local variables -- "constants" - real(rkind) :: volHeatCapacityAir ! volumetric heat capacity of air (J m-3) - real(rkind) :: latentHeatConstant ! latent heat constant (kg m-3 K-1) - ! local variables -- derivatives for energy conductances - real(rkind) :: dEvapCond_dCanopyTemp ! derivative in evap conductance w.r.t. canopy temperature - real(rkind) :: dTransCond_dCanopyTemp ! derivative in trans conductance w.r.t. canopy temperature - real(rkind) :: dCanopyCond_dCanairTemp ! derivative in canopy conductance w.r.t. canopy air temperature - real(rkind) :: dCanopyCond_dCanopyTemp ! derivative in canopy conductance w.r.t. canopy temperature - real(rkind) :: dGroundCondSH_dCanairTemp ! derivative in ground conductance of sensible heat w.r.t. canopy air temperature - real(rkind) :: dGroundCondSH_dCanopyTemp ! derivative in ground conductance of sensible heat w.r.t. canopy temperature - real(rkind) :: dGroundCondSH_dGroundTemp ! derivative in ground conductance of sensible heat w.r.t. ground temperature - ! local variables -- derivatives for mass conductances - real(rkind) :: dGroundCondLH_dCanairTemp ! derivative in ground conductance w.r.t. canopy air temperature - real(rkind) :: dGroundCondLH_dCanopyTemp ! derivative in ground conductance w.r.t. canopy temperature - real(rkind) :: dGroundCondLH_dGroundTemp ! derivative in ground conductance w.r.t. ground temperature - ! local variables -- derivatives for the canopy air space variables - real(rkind) :: fPart_VP ! part of the function for vapor pressure of the canopy air space - real(rkind) :: leafConductanceTr ! leaf conductance for transpiration (m s-1) - real(rkind) :: dVPCanopyAir_dTCanair ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the canopy air space - real(rkind) :: dVPCanopyAir_dTCanopy ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the canopy - real(rkind) :: dVPCanopyAir_dTGround ! derivative in the vapor pressure of the canopy air space w.r.t. temperature of the ground - real(rkind) :: dVPCanopyAir_dWetFrac ! derivative of vapor pressure in the canopy air space w.r.t. wetted fraction of the canopy - real(rkind) :: dVPCanopyAir_dCanWat ! derivative of vapor pressure in the canopy air space w.r.t. canopy total water content - ! local variables -- sensible heat flux derivatives - real(rkind) :: dSenHeatTotal_dTCanair ! derivative in the total sensible heat flux w.r.t. canopy air temperature - real(rkind) :: dSenHeatTotal_dTCanopy ! derivative in the total sensible heat flux w.r.t. canopy air temperature - real(rkind) :: dSenHeatTotal_dTGround ! derivative in the total sensible heat flux w.r.t. ground temperature - real(rkind) :: dSenHeatCanopy_dTCanair ! derivative in the canopy sensible heat flux w.r.t. canopy air temperature - real(rkind) :: dSenHeatCanopy_dTCanopy ! derivative in the canopy sensible heat flux w.r.t. canopy temperature - real(rkind) :: dSenHeatCanopy_dTGround ! derivative in the canopy sensible heat flux w.r.t. ground temperature - real(rkind) :: dSenHeatGround_dTCanair ! derivative in the ground sensible heat flux w.r.t. canopy air temperature - real(rkind) :: dSenHeatGround_dTCanopy ! derivative in the ground sensible heat flux w.r.t. canopy temperature - real(rkind) :: dSenHeatGround_dTGround ! derivative in the ground sensible heat flux w.r.t. ground temperature - ! local variables -- wetted fraction derivatives - real(rkind) :: dLatHeatCanopyEvap_dWetFrac ! derivative in the latent heat of canopy evaporation w.r.t. canopy wet fraction (W m-2) - real(rkind) :: dLatHeatCanopyTrans_dWetFrac ! derivative in the latent heat of canopy transpiration w.r.t. canopy wet fraction (W m-2) - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='turbFluxes/' - - ! compute constants - volHeatCapacityAir = iden_air*cp_air ! volumetric heat capacity of air (J m-3) - latentHeatConstant = iden_air*w_ratio/airpres ! latent heat constant for (kg m-3 Pa-1) - - ! ***** - ! * compute conductances, and derivatives... - ! ****************************************** - - ! compute conductances for sensible heat (m s-1) - if(computeVegFlux)then - leafConductance = exposedVAI/leafResistance - leafConductanceTr = canopySunlitLAI/(leafResistance+stomResistSunlit) + canopyShadedLAI/(leafResistance+stomResistShaded) - canopyConductance = 1._rkind/canopyResistance - else - leafConductance = 0._rkind - canopyConductance = 0._rkind - end if - groundConductanceSH = 1._rkind/groundResistance - - ! compute total conductance for sensible heat - totalConductanceSH = leafConductance + groundConductanceSH + canopyConductance - - ! compute conductances for latent heat (m s-1) - if(computeVegFlux)then - evapConductance = canopyWetFraction*leafConductance - transConductance = (1._rkind - canopyWetFraction) * leafConductanceTr - !write(*,'(a,10(f14.8,1x))') 'canopySunlitLAI, canopyShadedLAI, stomResistSunlit, stomResistShaded, leafResistance, canopyWetFraction = ', & - ! canopySunlitLAI, canopyShadedLAI, stomResistSunlit, stomResistShaded, leafResistance, canopyWetFraction - else - evapConductance = 0._rkind - transConductance = 0._rkind - end if - groundConductanceLH = 1._rkind/(groundResistance + soilResistance) ! NOTE: soilResistance accounts for fractional snow, and =0 when snow cover is 100% - totalConductanceLH = evapConductance + transConductance + groundConductanceLH + canopyConductance - - ! check sensible heat conductance - if(totalConductanceSH < -tinyVal .or. groundConductanceSH < -tinyVal .or. canopyConductance < -tinyVal)then - message=trim(message)//'negative conductance for sensible heat' - err=20; return - endif - - ! check latent heat conductance - if(totalConductanceLH < tinyVal .or. groundConductanceLH < -tinyVal)then - message=trim(message)//'negative conductance for latent heat' - err=20; return - endif - - ! * compute derivatives - ! NOTE: it may be more efficient to compute these derivatives when computing resistances - if(ixDerivMethod == analytical)then - - ! compute derivatives in individual conductances for sensible heat w.r.t. canopy temperature (m s-1 K-1) + end if ! (if computing analytical derivatives) + + ! ***** + ! * compute sensible and latent heat fluxes, and derivatives... + ! ************************************************************* + + ! * compute sensible and latent heat fluxes from the canopy to the canopy air space (W m-2) if(computeVegFlux)then - dEvapCond_dCanopyTemp = dCanopyWetFraction_dT*leafConductance ! derivative in evap conductance w.r.t. canopy temperature - dTransCond_dCanopyTemp = -dCanopyWetFraction_dT*leafConductanceTr ! derivative in trans conductance w.r.t. canopy temperature - dCanopyCond_dCanairTemp = -dCanopyResistance_dTCanair/canopyResistance**2._rkind ! derivative in canopy conductance w.r.t. canopy air emperature - dCanopyCond_dCanopyTemp = -dCanopyResistance_dTCanopy/canopyResistance**2._rkind ! derivative in canopy conductance w.r.t. canopy temperature - dGroundCondSH_dCanairTemp = -dGroundResistance_dTCanair/groundResistance**2._rkind ! derivative in ground conductance w.r.t. canopy air temperature - dGroundCondSH_dCanopyTemp = -dGroundResistance_dTCanopy/groundResistance**2._rkind ! derivative in ground conductance w.r.t. canopy temperature - dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2._rkind ! derivative in ground conductance w.r.t. ground temperature + + ! compute the vapor pressure in the canopy air space (Pa) + fPart_VP = canopyConductance*VPair + (evapConductance + transConductance)*satVP_CanopyTemp + groundConductanceLH*satVP_GroundTemp*soilRelHumidity + VP_CanopyAir = fPart_VP/totalConductanceLH + + ! compute sensible heat flux from the canopy air space to the atmosphere + ! NOTE: canairTemp is a state variable + senHeatTotal = -volHeatCapacityAir*canopyConductance*(canairTemp - airtemp) + + ! compute fluxes + senHeatCanopy = -volHeatCapacityAir*leafConductance*(canopyTemp - canairTemp) ! (positive downwards) + latHeatCanopyEvap = -latHeatSubVapCanopy*latentHeatConstant*evapConductance*(satVP_CanopyTemp - VP_CanopyAir) ! (positive downwards) + latHeatCanopyTrans = -LH_vap*latentHeatConstant*transConductance*(satVP_CanopyTemp - VP_CanopyAir) ! (positive downwards) + + + ! * no vegetation, so fluxes are zero else - dEvapCond_dCanopyTemp = 0._rkind ! derivative in evap conductance w.r.t. canopy temperature - dTransCond_dCanopyTemp = 0._rkind ! derivative in trans conductance w.r.t. canopy temperature - dCanopyCond_dCanairTemp = 0._rkind ! derivative in canopy conductance w.r.t. canopy air emperature - dCanopyCond_dCanopyTemp = 0._rkind ! derivative in canopy conductance w.r.t. canopy temperature - dGroundCondSH_dCanairTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy air temperature - dGroundCondSH_dCanopyTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy temperature - dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2._rkind ! derivative in ground conductance w.r.t. ground temperature + senHeatCanopy = 0._rkind + latHeatCanopyEvap = 0._rkind + latHeatCanopyTrans = 0._rkind end if - ! compute derivatives in individual conductances for latent heat w.r.t. canopy temperature (m s-1 K-1) + ! compute sensible and latent heat fluxes from the ground to the canopy air space (W m-2) if(computeVegFlux)then - dGroundCondLH_dCanairTemp = -dGroundResistance_dTCanair/(groundResistance+soilResistance)**2._rkind ! derivative in ground conductance w.r.t. canopy air temperature - dGroundCondLH_dCanopyTemp = -dGroundResistance_dTCanopy/(groundResistance+soilResistance)**2._rkind ! derivative in ground conductance w.r.t. canopy temperature - dGroundCondLH_dGroundTemp = -dGroundResistance_dTGround/(groundResistance+soilResistance)**2._rkind ! derivative in ground conductance w.r.t. ground temperature + senHeatGround = -volHeatCapacityAir*groundConductanceSH*(groundTemp - canairTemp) ! (positive downwards) + latHeatGround = -latHeatSubVapGround*latentHeatConstant*groundConductanceLH*(satVP_GroundTemp*soilRelHumidity - VP_CanopyAir) ! (positive downwards) else - dGroundCondLH_dCanairTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy air temperature - dGroundCondLH_dCanopyTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy temperature - dGroundCondLH_dGroundTemp = -dGroundResistance_dTGround/(groundResistance+soilResistance)**2._rkind ! derivative in ground conductance w.r.t. ground temperature + senHeatGround = -volHeatCapacityAir*groundConductanceSH*(groundTemp - airtemp) ! (positive downwards) + latHeatGround = -latHeatSubVapGround*latentHeatConstant*groundConductanceLH*(satVP_GroundTemp*soilRelHumidity - VPair) ! (positive downwards) + senHeatTotal = senHeatGround end if - end if ! (if computing analytical derivatives) - - ! ***** - ! * compute sensible and latent heat fluxes, and derivatives... - ! ************************************************************* - - ! * compute sensible and latent heat fluxes from the canopy to the canopy air space (W m-2) - if(computeVegFlux)then - - ! compute the vapor pressure in the canopy air space (Pa) - fPart_VP = canopyConductance*VPair + (evapConductance + transConductance)*satVP_CanopyTemp + groundConductanceLH*satVP_GroundTemp*soilRelHumidity - VP_CanopyAir = fPart_VP/totalConductanceLH - !write(*,'(a,10(f20.10,1x))') 'canopyConductance, evapConductance, transConductance, groundConductanceLH, soilRelHumidity = ', & - ! canopyConductance, evapConductance, transConductance, groundConductanceLH, soilRelHumidity - - ! compute sensible heat flux from the canopy air space to the atmosphere - ! NOTE: canairTemp is a state variable - senHeatTotal = -volHeatCapacityAir*canopyConductance*(canairTemp - airtemp) - !print*, 'canairTemp, airtemp, senHeatTotal = ', canairTemp, airtemp, senHeatTotal - - ! compute fluxes - senHeatCanopy = -volHeatCapacityAir*leafConductance*(canopyTemp - canairTemp) ! (positive downwards) - latHeatCanopyEvap = -latHeatSubVapCanopy*latentHeatConstant*evapConductance*(satVP_CanopyTemp - VP_CanopyAir) ! (positive downwards) - latHeatCanopyTrans = -LH_vap*latentHeatConstant*transConductance*(satVP_CanopyTemp - VP_CanopyAir) ! (positive downwards) - !write(*,'(a,10(f25.15,1x))') 'latHeatCanopyEvap, VP_CanopyAir = ', latHeatCanopyEvap, VP_CanopyAir - !write(*,'(a,10(f25.15,1x))') 'latHeatCanopyTrans, VP_CanopyAir = ', latHeatCanopyTrans, VP_CanopyAir - !write(*,'(a,10(f25.15,1x))') 'transConductance = ', transConductance - - ! check that energy for canopy evaporation does not exhaust the available water - ! NOTE: do this here, rather than enforcing solution constraints, because energy and mass solutions may be uncoupled - !if(latHeatSubVapCanopy > LH_vap+verySmall)then ! (sublimation) - ! maxFlux = -canopyIce*LH_sub/dt ! W m-2 - !else ! (evaporation) - ! maxFlux = -canopyLiquid*LH_vap/dt ! W m-2 - !end if - ! NOTE: fluxes are positive downwards - !if(latHeatCanopyEvap < maxFlux) latHeatCanopyEvap = maxFlux - !write(*,'(a,10(f20.10,1x))') 'maxFlux, latHeatCanopyEvap = ', maxFlux, latHeatCanopyEvap - - ! * no vegetation, so fluxes are zero - else - senHeatCanopy = 0._rkind - latHeatCanopyEvap = 0._rkind - latHeatCanopyTrans = 0._rkind - end if - ! compute sensible and latent heat fluxes from the ground to the canopy air space (W m-2) - if(computeVegFlux)then - senHeatGround = -volHeatCapacityAir*groundConductanceSH*(groundTemp - canairTemp) ! (positive downwards) - latHeatGround = -latHeatSubVapGround*latentHeatConstant*groundConductanceLH*(satVP_GroundTemp*soilRelHumidity - VP_CanopyAir) ! (positive downwards) - else - senHeatGround = -volHeatCapacityAir*groundConductanceSH*(groundTemp - airtemp) ! (positive downwards) - latHeatGround = -latHeatSubVapGround*latentHeatConstant*groundConductanceLH*(satVP_GroundTemp*soilRelHumidity - VPair) ! (positive downwards) - senHeatTotal = senHeatGround - end if - !write(*,'(a,10(f25.15,1x))') 'latHeatGround = ', latHeatGround + ! compute latent heat flux from the canopy air space to the atmosphere + ! NOTE: VP_CanopyAir is a diagnostic variable + latHeatTotal = latHeatCanopyEvap + latHeatCanopyTrans + latHeatGround - ! compute latent heat flux from the canopy air space to the atmosphere - ! NOTE: VP_CanopyAir is a diagnostic variable - latHeatTotal = latHeatCanopyEvap + latHeatCanopyTrans + latHeatGround + ! * compute derivatives + if(ixDerivMethod == analytical)then + + ! differentiate CANOPY fluxes + if(computeVegFlux)then + + ! compute derivatives of vapor pressure in the canopy air space w.r.t. all state variables + ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the canopy air space) + dPart1 = dCanopyCond_dCanairTemp*VPair + dGroundCondLH_dCanairTemp*satVP_GroundTemp*soilRelHumidity + dPart2 = -(dCanopyCond_dCanairTemp + dGroundCondLH_dCanairTemp)/(totalConductanceLH**2._rkind) + dVPCanopyAir_dTCanair = dPart1/totalConductanceLH + fPart_VP*dPart2 + ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the canopy) + dPart0 = (evapConductance + transConductance)*dSVPCanopy_dCanopyTemp + (dEvapCond_dCanopyTemp + dTransCond_dCanopyTemp)*satVP_CanopyTemp + dPart1 = dCanopyCond_dCanopyTemp*VPair + dPart0 + dGroundCondLH_dCanopyTemp*satVP_GroundTemp*soilRelHumidity + dPart2 = -(dCanopyCond_dCanopyTemp + dEvapCond_dCanopyTemp + dTransCond_dCanopyTemp + dGroundCondLH_dCanopyTemp)/(totalConductanceLH**2._rkind) + dVPCanopyAir_dTCanopy = dPart1/totalConductanceLH + fPart_VP*dPart2 + ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the ground) + dPart1 = dGroundCondLH_dGroundTemp*satVP_GroundTemp*soilRelHumidity + groundConductanceLH*dSVPGround_dGroundTemp*soilRelHumidity + dPart2 = -dGroundCondLH_dGroundTemp/(totalConductanceLH**2._rkind) + dVPCanopyAir_dTGround = dPart1/totalConductanceLH + fPart_VP*dPart2 + ! (derivative of vapor pressure in the canopy air space w.r.t. wetted fraction of the canopy) + dPart1 = (leafConductance - leafConductanceTr)*satVP_CanopyTemp + dPart2 = -(leafConductance - leafConductanceTr)/(totalConductanceLH**2._rkind) + dVPCanopyAir_dWetFrac = dPart1/totalConductanceLH + fPart_VP*dPart2 + dVPCanopyAir_dCanWat = dVPCanopyAir_dWetFrac*dCanopyWetFraction_dWat + + ! sensible heat from the canopy to the atmosphere + dSenHeatTotal_dTCanair = -volHeatCapacityAir*canopyConductance - volHeatCapacityAir*dCanopyCond_dCanairTemp*(canairTemp - airtemp) + dSenHeatTotal_dTCanopy = -volHeatCapacityAir*dCanopyCond_dCanopyTemp*(canairTemp - airtemp) + dSenHeatTotal_dTGround = 0._rkind + + ! sensible heat from the canopy to the canopy air space + dSenHeatCanopy_dTCanair = volHeatCapacityAir*leafConductance + dSenHeatCanopy_dTCanopy = -volHeatCapacityAir*leafConductance + dSenHeatCanopy_dTGround = 0._rkind + + ! sensible heat from the ground to the canopy air space + dSenHeatGround_dTCanair = -volHeatCapacityAir*dGroundCondSH_dCanairTemp*(groundTemp - canairTemp) + volHeatCapacityAir*groundConductanceSH + dSenHeatGround_dTCanopy = -volHeatCapacityAir*dGroundCondSH_dCanopyTemp*(groundTemp - canairTemp) + dSenHeatGround_dTGround = -volHeatCapacityAir*dGroundCondSH_dGroundTemp*(groundTemp - canairTemp) - volHeatCapacityAir*groundConductanceSH + + ! latent heat associated with canopy evaporation + ! (initial calculations) + fPart1 = -latHeatSubVapCanopy*latentHeatConstant*evapConductance + dPart1 = -latHeatSubVapCanopy*latentHeatConstant*dEvapCond_dCanopyTemp + fPart2 = satVP_CanopyTemp - VP_CanopyAir + dPart2 = dSVPCanopy_dCanopyTemp - dVPCanopyAir_dTCanopy + ! (derivatives) + dLatHeatCanopyEvap_dTCanair = fPart1*(-dVPCanopyAir_dTCanair) + dLatHeatCanopyEvap_dTCanopy = fPart1*dpart2 + fPart2*dPart1 + dLatHeatCanopyEvap_dTGround = fPart1*(-dVPCanopyAir_dTGround) + + ! latent heat associated with canopy transpiration + ! (initial calculations) + fPart1 = -LH_vap*latentHeatConstant*transConductance + dPart1 = -LH_vap*latentHeatConstant*dTransCond_dCanopyTemp + ! (derivatives) + dLatHeatCanopyTrans_dTCanair = fPart1*(-dVPCanopyAir_dTCanair) + dLatHeatCanopyTrans_dTCanopy = fPart1*dPart2 + fPart2*dPart1 + dLatHeatCanopyTrans_dTGround = fPart1*(-dVPCanopyAir_dTGround) + + ! latent heat flux from the ground + fPart1 = -latHeatSubVapGround*latentHeatConstant*groundConductanceLH ! function of the first part + fPart2 = (satVP_GroundTemp*soilRelHumidity - VP_CanopyAir) ! function of the second part + dLatHeatGroundEvap_dTCanair = -latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dCanairTemp*fPart2 - dVPCanopyAir_dTCanair*fPart1 + dLatHeatGroundEvap_dTCanopy = -latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dCanopyTemp*fPart2 - dVPCanopyAir_dTCanopy*fPart1 + dLatHeatGroundEvap_dTGround = -latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dGroundTemp*fPart2 + (dSVPGround_dGroundTemp*soilRelHumidity - dVPCanopyAir_dTGround)*fPart1 + + ! latent heat associated with canopy evaporation w.r.t. wetted fraction of the canopy + dPart1 = -latHeatSubVapCanopy*latentHeatConstant*leafConductance + fPart1 = dPart1*canopyWetFraction + dLatHeatCanopyEvap_dWetFrac = dPart1*(satVP_CanopyTemp - VP_CanopyAir) + fPart1*(-dVPCanopyAir_dWetFrac) + + ! latent heat associated with canopy transpiration w.r.t. wetted fraction of the canopy + dPart1 = LH_vap*latentHeatConstant*leafConductanceTr ! NOTE: positive, since (1 - wetFrac) + fPart1 = -dPart1*(1._rkind - canopyWetFraction) + dLatHeatCanopyTrans_dWetFrac = dPart1*(satVP_CanopyTemp - VP_CanopyAir) + fPart1*(-dVPCanopyAir_dWetFrac) + + ! latent heat associated with canopy transpiration w.r.t. canopy total water + dLatHeatCanopyTrans_dCanWat = dLatHeatCanopyTrans_dWetFrac*dCanopyWetFraction_dWat ! (J s-1 kg-1) + + + else ! canopy is undefined + + ! set derivatives for canopy fluxes to zero (no canopy, so fluxes are undefined) + dSenHeatTotal_dTCanair = 0._rkind + dSenHeatTotal_dTCanopy = 0._rkind + dSenHeatTotal_dTGround = 0._rkind + dSenHeatCanopy_dTCanair = 0._rkind + dSenHeatCanopy_dTCanopy = 0._rkind + dSenHeatCanopy_dTGround = 0._rkind + dLatHeatCanopyEvap_dTCanair = 0._rkind + dLatHeatCanopyEvap_dTCanopy = 0._rkind + dLatHeatCanopyEvap_dTGround = 0._rkind + dLatHeatCanopyTrans_dTCanair = 0._rkind + dLatHeatCanopyTrans_dTCanopy = 0._rkind + dLatHeatCanopyTrans_dTGround = 0._rkind + + ! set derivatives for wetted area and canopy transpiration to zero (no canopy, so fluxes are undefined) + dLatHeatCanopyEvap_dWetFrac = 0._rkind + dLatHeatCanopyEvap_dCanWat = 0._rkind + dLatHeatCanopyTrans_dCanWat = 0._rkind + dVPCanopyAir_dCanWat = 0._rkind + + ! set derivatives for ground fluxes w.r.t canopy temperature to zero (no canopy, so fluxes are undefined) + dSenHeatGround_dTCanair = 0._rkind + dSenHeatGround_dTCanopy = 0._rkind + dLatHeatGroundEvap_dTCanair = 0._rkind + dLatHeatGroundEvap_dTCanopy = 0._rkind + + ! compute derivatives for the ground fluxes w.r.t. ground temperature + dSenHeatGround_dTGround = (-volHeatCapacityAir*dGroundCondSH_dGroundTemp)*(groundTemp - airtemp) + & ! d(ground sensible heat flux)/d(ground temp) + (-volHeatCapacityAir*groundConductanceSH) + dLatHeatGroundEvap_dTGround = (-latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dGroundTemp)*(satVP_GroundTemp*soilRelHumidity - VPair) + & ! d(ground latent heat flux)/d(ground temp) + (-latHeatSubVapGround*latentHeatConstant*groundConductanceLH)*dSVPGround_dGroundTemp*soilRelHumidity + + end if ! (if canopy is defined) + + end if ! (if computing analytical derivatives) - ! * compute derivatives - if(ixDerivMethod == analytical)then - ! differentiate CANOPY fluxes - if(computeVegFlux)then + ! ***** + ! * compute net turbulent fluxes, and derivatives... + ! ************************************************** + + ! compute net fluxes + turbFluxCanair = senHeatTotal - senHeatCanopy - senHeatGround ! net turbulent flux at the canopy air space (W m-2) + turbFluxCanopy = senHeatCanopy + latHeatCanopyEvap + latHeatCanopyTrans ! net turbulent flux at the canopy (W m-2) + turbFluxGround = senHeatGround + latHeatGround ! net turbulent flux at the ground surface (W m-2) + + ! * compute derivatives + if(ixDerivMethod == analytical)then + ! (energy derivatives) + dTurbFluxCanair_dTCanair = dSenHeatTotal_dTCanair - dSenHeatCanopy_dTCanair - dSenHeatGround_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxCanair_dTCanopy = dSenHeatTotal_dTCanopy - dSenHeatCanopy_dTCanopy - dSenHeatGround_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxCanair_dTGround = dSenHeatTotal_dTGround - dSenHeatCanopy_dTGround - dSenHeatGround_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanair = dSenHeatCanopy_dTCanair + dLatHeatCanopyEvap_dTCanair + dLatHeatCanopyTrans_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanopy = dSenHeatCanopy_dTCanopy + dLatHeatCanopyEvap_dTCanopy + dLatHeatCanopyTrans_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxCanopy_dTGround = dSenHeatCanopy_dTGround + dLatHeatCanopyEvap_dTGround + dLatHeatCanopyTrans_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxGround_dTCanair = dSenHeatGround_dTCanair + dLatHeatGroundEvap_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxGround_dTCanopy = dSenHeatGround_dTCanopy + dLatHeatGroundEvap_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxGround_dTGround = dSenHeatGround_dTGround + dLatHeatGroundEvap_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + ! (liquid water derivatives) + dLatHeatCanopyEvap_dCanWat = dLatHeatCanopyEvap_dWetFrac*dCanopyWetFraction_dWat ! derivative in latent heat of canopy evaporation w.r.t. canopy total water (W kg-1) + dLatHeatGroundEvap_dCanWat = latHeatSubVapGround*latentHeatConstant*groundConductanceLH*dVPCanopyAir_dCanWat ! derivative in latent heat of ground evaporation w.r.t. canopy total water (J kg-1 s-1) + ! (cross deriavtives) + dTurbFluxCanair_dCanWat = 0._rkind ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxCanopy_dCanWat = dLatHeatCanopyEvap_dCanWat + dLatHeatCanopyTrans_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxGround_dCanWat = dLatHeatGroundEvap_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + else ! (just make sure we return something) + ! (energy derivatives) + dTurbFluxCanair_dTCanair = 0._rkind + dTurbFluxCanair_dTCanopy = 0._rkind + dTurbFluxCanair_dTGround = 0._rkind + dTurbFluxCanopy_dTCanair = 0._rkind + dTurbFluxCanopy_dTCanopy = 0._rkind + dTurbFluxCanopy_dTGround = 0._rkind + dTurbFluxGround_dTCanair = 0._rkind + dTurbFluxGround_dTCanopy = 0._rkind + dTurbFluxGround_dTGround = 0._rkind + ! (liquid water derivatives) + dLatHeatCanopyEvap_dCanWat = 0._rkind + dLatHeatGroundEvap_dCanWat = 0._rkind + ! (cross deriavtives) + dTurbFluxCanair_dCanWat = 0._rkind + dTurbFluxCanopy_dCanWat = 0._rkind + dTurbFluxGround_dCanWat = 0._rkind + end if - ! compute derivatives of vapor pressure in the canopy air space w.r.t. all state variables - ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the canopy air space) - dPart1 = dCanopyCond_dCanairTemp*VPair + dGroundCondLH_dCanairTemp*satVP_GroundTemp*soilRelHumidity - dPart2 = -(dCanopyCond_dCanairTemp + dGroundCondLH_dCanairTemp)/(totalConductanceLH**2._rkind) - dVPCanopyAir_dTCanair = dPart1/totalConductanceLH + fPart_VP*dPart2 - ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the canopy) - dPart0 = (evapConductance + transConductance)*dSVPCanopy_dCanopyTemp + (dEvapCond_dCanopyTemp + dTransCond_dCanopyTemp)*satVP_CanopyTemp - dPart1 = dCanopyCond_dCanopyTemp*VPair + dPart0 + dGroundCondLH_dCanopyTemp*satVP_GroundTemp*soilRelHumidity - dPart2 = -(dCanopyCond_dCanopyTemp + dEvapCond_dCanopyTemp + dTransCond_dCanopyTemp + dGroundCondLH_dCanopyTemp)/(totalConductanceLH**2._rkind) - dVPCanopyAir_dTCanopy = dPart1/totalConductanceLH + fPart_VP*dPart2 - ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the ground) - dPart1 = dGroundCondLH_dGroundTemp*satVP_GroundTemp*soilRelHumidity + groundConductanceLH*dSVPGround_dGroundTemp*soilRelHumidity - dPart2 = -dGroundCondLH_dGroundTemp/(totalConductanceLH**2._rkind) - dVPCanopyAir_dTGround = dPart1/totalConductanceLH + fPart_VP*dPart2 - ! (derivative of vapor pressure in the canopy air space w.r.t. wetted fraction of the canopy) - dPart1 = (leafConductance - leafConductanceTr)*satVP_CanopyTemp - dPart2 = -(leafConductance - leafConductanceTr)/(totalConductanceLH**2._rkind) - dVPCanopyAir_dWetFrac = dPart1/totalConductanceLH + fPart_VP*dPart2 - dVPCanopyAir_dCanWat = dVPCanopyAir_dWetFrac*dCanopyWetFraction_dWat - !write(*,'(a,5(f20.8,1x))') 'dVPCanopyAir_dTCanair, dVPCanopyAir_dTCanopy, dVPCanopyAir_dTGround, dVPCanopyAir_dWetFrac, dVPCanopyAir_dCanWat = ', & - ! dVPCanopyAir_dTCanair, dVPCanopyAir_dTCanopy, dVPCanopyAir_dTGround, dVPCanopyAir_dWetFrac, dVPCanopyAir_dCanWat - - ! sensible heat from the canopy to the atmosphere - dSenHeatTotal_dTCanair = -volHeatCapacityAir*canopyConductance - volHeatCapacityAir*dCanopyCond_dCanairTemp*(canairTemp - airtemp) - dSenHeatTotal_dTCanopy = -volHeatCapacityAir*dCanopyCond_dCanopyTemp*(canairTemp - airtemp) - dSenHeatTotal_dTGround = 0._rkind - !write(*,'(a,3(f20.8,1x))') 'dSenHeatTotal_dTCanair, dSenHeatTotal_dTCanopy, dSenHeatTotal_dTGround = ', & - ! dSenHeatTotal_dTCanair, dSenHeatTotal_dTCanopy, dSenHeatTotal_dTGround - - ! sensible heat from the canopy to the canopy air space - dSenHeatCanopy_dTCanair = volHeatCapacityAir*leafConductance - dSenHeatCanopy_dTCanopy = -volHeatCapacityAir*leafConductance - dSenHeatCanopy_dTGround = 0._rkind - !write(*,'(a,3(f20.8,1x))') 'dSenHeatCanopy_dTCanair, dSenHeatCanopy_dTCanopy, dSenHeatCanopy_dTGround = ', & - ! dSenHeatCanopy_dTCanair, dSenHeatCanopy_dTCanopy, dSenHeatCanopy_dTGround - - ! sensible heat from the ground to the canopy air space - dSenHeatGround_dTCanair = -volHeatCapacityAir*dGroundCondSH_dCanairTemp*(groundTemp - canairTemp) + volHeatCapacityAir*groundConductanceSH - dSenHeatGround_dTCanopy = -volHeatCapacityAir*dGroundCondSH_dCanopyTemp*(groundTemp - canairTemp) - dSenHeatGround_dTGround = -volHeatCapacityAir*dGroundCondSH_dGroundTemp*(groundTemp - canairTemp) - volHeatCapacityAir*groundConductanceSH - !write(*,'(a,3(f20.8,1x))') 'dSenHeatGround_dTCanair, dSenHeatGround_dTCanopy, dSenHeatGround_dTGround = ', & - ! dSenHeatGround_dTCanair, dSenHeatGround_dTCanopy, dSenHeatGround_dTGround - - ! latent heat associated with canopy evaporation - ! (initial calculations) - fPart1 = -latHeatSubVapCanopy*latentHeatConstant*evapConductance - dPart1 = -latHeatSubVapCanopy*latentHeatConstant*dEvapCond_dCanopyTemp - fPart2 = satVP_CanopyTemp - VP_CanopyAir - dPart2 = dSVPCanopy_dCanopyTemp - dVPCanopyAir_dTCanopy - ! (derivatives) - dLatHeatCanopyEvap_dTCanair = fPart1*(-dVPCanopyAir_dTCanair) - dLatHeatCanopyEvap_dTCanopy = fPart1*dpart2 + fPart2*dPart1 - dLatHeatCanopyEvap_dTGround = fPart1*(-dVPCanopyAir_dTGround) - !write(*,'(a,3(f20.8,1x))') 'dLatHeatCanopyEvap_dTCanair, dLatHeatCanopyEvap_dTCanopy, dLatHeatCanopyEvap_dTGround = ', & - ! dLatHeatCanopyEvap_dTCanair, dLatHeatCanopyEvap_dTCanopy, dLatHeatCanopyEvap_dTGround - - ! latent heat associated with canopy transpiration - ! (initial calculations) - fPart1 = -LH_vap*latentHeatConstant*transConductance - dPart1 = -LH_vap*latentHeatConstant*dTransCond_dCanopyTemp - ! (derivatives) - dLatHeatCanopyTrans_dTCanair = fPart1*(-dVPCanopyAir_dTCanair) - dLatHeatCanopyTrans_dTCanopy = fPart1*dPart2 + fPart2*dPart1 - dLatHeatCanopyTrans_dTGround = fPart1*(-dVPCanopyAir_dTGround) - !write(*,'(a,3(f20.8,1x))') 'dLatHeatCanopyTrans_dTCanair, dLatHeatCanopyTrans_dTCanopy, dLatHeatCanopyTrans_dTGround = ', & - ! dLatHeatCanopyTrans_dTCanair, dLatHeatCanopyTrans_dTCanopy, dLatHeatCanopyTrans_dTGround - - ! latent heat flux from the ground - fPart1 = -latHeatSubVapGround*latentHeatConstant*groundConductanceLH ! function of the first part - fPart2 = (satVP_GroundTemp*soilRelHumidity - VP_CanopyAir) ! function of the second part - dLatHeatGroundEvap_dTCanair = -latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dCanairTemp*fPart2 - dVPCanopyAir_dTCanair*fPart1 - dLatHeatGroundEvap_dTCanopy = -latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dCanopyTemp*fPart2 - dVPCanopyAir_dTCanopy*fPart1 - dLatHeatGroundEvap_dTGround = -latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dGroundTemp*fPart2 + (dSVPGround_dGroundTemp*soilRelHumidity - dVPCanopyAir_dTGround)*fPart1 - !write(*,'(a,3(f20.8,1x))') 'dLatHeatGroundEvap_dTCanair, dLatHeatGroundEvap_dTCanopy, dLatHeatGroundEvap_dTGround = ', & - ! dLatHeatGroundEvap_dTCanair, dLatHeatGroundEvap_dTCanopy, dLatHeatGroundEvap_dTGround - - ! latent heat associated with canopy evaporation w.r.t. wetted fraction of the canopy - dPart1 = -latHeatSubVapCanopy*latentHeatConstant*leafConductance - fPart1 = dPart1*canopyWetFraction - dLatHeatCanopyEvap_dWetFrac = dPart1*(satVP_CanopyTemp - VP_CanopyAir) + fPart1*(-dVPCanopyAir_dWetFrac) - - ! latent heat associated with canopy transpiration w.r.t. wetted fraction of the canopy - dPart1 = LH_vap*latentHeatConstant*leafConductanceTr ! NOTE: positive, since (1 - wetFrac) - fPart1 = -dPart1*(1._rkind - canopyWetFraction) - dLatHeatCanopyTrans_dWetFrac = dPart1*(satVP_CanopyTemp - VP_CanopyAir) + fPart1*(-dVPCanopyAir_dWetFrac) - !print*, 'dLatHeatCanopyTrans_dWetFrac = ', dLatHeatCanopyTrans_dWetFrac - - ! latent heat associated with canopy transpiration w.r.t. canopy total water - dLatHeatCanopyTrans_dCanWat = dLatHeatCanopyTrans_dWetFrac*dCanopyWetFraction_dWat ! (J s-1 kg-1) - !print*, 'dLatHeatCanopyTrans_dCanWat = ', dLatHeatCanopyTrans_dCanWat - - else ! canopy is undefined - - ! set derivatives for canopy fluxes to zero (no canopy, so fluxes are undefined) - dSenHeatTotal_dTCanair = 0._rkind - dSenHeatTotal_dTCanopy = 0._rkind - dSenHeatTotal_dTGround = 0._rkind - dSenHeatCanopy_dTCanair = 0._rkind - dSenHeatCanopy_dTCanopy = 0._rkind - dSenHeatCanopy_dTGround = 0._rkind - dLatHeatCanopyEvap_dTCanair = 0._rkind - dLatHeatCanopyEvap_dTCanopy = 0._rkind - dLatHeatCanopyEvap_dTGround = 0._rkind - dLatHeatCanopyTrans_dTCanair = 0._rkind - dLatHeatCanopyTrans_dTCanopy = 0._rkind - dLatHeatCanopyTrans_dTGround = 0._rkind - - ! set derivatives for wetted area and canopy transpiration to zero (no canopy, so fluxes are undefined) - dLatHeatCanopyEvap_dWetFrac = 0._rkind - dLatHeatCanopyEvap_dCanWat = 0._rkind - dLatHeatCanopyTrans_dCanWat = 0._rkind - dVPCanopyAir_dCanWat = 0._rkind - - ! set derivatives for ground fluxes w.r.t canopy temperature to zero (no canopy, so fluxes are undefined) - dSenHeatGround_dTCanair = 0._rkind - dSenHeatGround_dTCanopy = 0._rkind - dLatHeatGroundEvap_dTCanair = 0._rkind - dLatHeatGroundEvap_dTCanopy = 0._rkind - - ! compute derivatives for the ground fluxes w.r.t. ground temperature - dSenHeatGround_dTGround = (-volHeatCapacityAir*dGroundCondSH_dGroundTemp)*(groundTemp - airtemp) + & ! d(ground sensible heat flux)/d(ground temp) - (-volHeatCapacityAir*groundConductanceSH) - dLatHeatGroundEvap_dTGround = (-latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dGroundTemp)*(satVP_GroundTemp*soilRelHumidity - VPair) + & ! d(ground latent heat flux)/d(ground temp) - (-latHeatSubVapGround*latentHeatConstant*groundConductanceLH)*dSVPGround_dGroundTemp*soilRelHumidity - - !print*, 'dGroundCondLH_dGroundTemp = ', dGroundCondLH_dGroundTemp - - end if ! (if canopy is defined) - - end if ! (if computing analytical derivatives) - - - ! ***** - ! * compute net turbulent fluxes, and derivatives... - ! ************************************************** - - ! compute net fluxes - turbFluxCanair = senHeatTotal - senHeatCanopy - senHeatGround ! net turbulent flux at the canopy air space (W m-2) - turbFluxCanopy = senHeatCanopy + latHeatCanopyEvap + latHeatCanopyTrans ! net turbulent flux at the canopy (W m-2) - turbFluxGround = senHeatGround + latHeatGround ! net turbulent flux at the ground surface (W m-2) - !write(*,'(a,1x,3(f20.10,1x))') 'senHeatCanopy, latHeatCanopyEvap, latHeatCanopyTrans = ', senHeatCanopy, latHeatCanopyEvap, latHeatCanopyTrans +end subroutine turbFluxes + + +! ******************************************************************************************************* +! private subroutine aStability: compute stability corrections for turbulent heat fluxes (-) +! ******************************************************************************************************* +subroutine aStability(& + ! input: control + computeDerivative, & ! input: logical flag to compute analytical derivatives + ixStability, & ! input: choice of stability function + ! input: forcing data, diagnostic and state variables + mHeight, & ! input: measurement height (m) + airTemp, & ! input: air temperature (K) + sfcTemp, & ! input: surface temperature (K) + windspd, & ! input: wind speed (m s-1) + ! input: stability parameters + critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam, & ! input: parameter in Louis (1979) stability function + Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function + ! output + RiBulk, & ! output: bulk Richardson number (-) + stabilityCorrection, & ! output: stability correction for turbulent heat fluxes (-) + dStabilityCorrection_dRich, & ! output: derivative in stability correction w.r.t. Richardson number (-) + dStabilityCorrection_dAirTemp, & ! output: derivative in stability correction w.r.t. temperature (K-1) + dStabilityCorrection_dSfcTemp, & ! output: derivative in stability correction w.r.t. temperature (K-1) + err, message ) ! output: error control + implicit none + ! input: control + logical(lgt),intent(in) :: computeDerivative ! flag to compute the derivative + integer(i4b),intent(in) :: ixStability ! choice of stability function + ! input: forcing data, diagnostic and state variables + real(rkind),intent(in) :: mHeight ! measurement height (m) + real(rkind),intent(in) :: airtemp ! air temperature (K) + real(rkind),intent(in) :: sfcTemp ! surface temperature (K) + real(rkind),intent(in) :: windspd ! wind speed (m s-1) + ! input: stability parameters + real(rkind),intent(in) :: critRichNumber ! critical value for the bulk Richardson number where turbulence ceases (-) + real(rkind),intent(in) :: Louis79_bparam ! parameter in Louis (1979) stability function + real(rkind),intent(in) :: Mahrt87_eScale ! exponential scaling factor in the Mahrt (1987) stability function + ! output + real(rkind),intent(out) :: RiBulk ! bulk Richardson number (-) + real(rkind),intent(out) :: stabilityCorrection ! stability correction for turbulent heat fluxes (-) + real(rkind),intent(out) :: dStabilityCorrection_dRich ! derivative in stability correction w.r.t. Richardson number (-) + real(rkind),intent(out) :: dStabilityCorrection_dAirTemp ! derivative in stability correction w.r.t. air temperature (K-1) + real(rkind),intent(out) :: dStabilityCorrection_dSfcTemp ! derivative in stability correction w.r.t. surface temperature (K-1) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local + real(rkind), parameter :: verySmall=1.e-10_rkind ! a very small number (avoid stability of zero) + real(rkind) :: dRiBulk_dAirTemp ! derivative in the bulk Richardson number w.r.t. air temperature (K-1) + real(rkind) :: dRiBulk_dSfcTemp ! derivative in the bulk Richardson number w.r.t. surface temperature (K-1) + real(rkind) :: bPrime ! scaled "b" parameter for stability calculations in Louis (1979) + ! ----------------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='aStability/' - ! * compute derivatives - if(ixDerivMethod == analytical)then - ! (energy derivatives) - dTurbFluxCanair_dTCanair = dSenHeatTotal_dTCanair - dSenHeatCanopy_dTCanair - dSenHeatGround_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxCanair_dTCanopy = dSenHeatTotal_dTCanopy - dSenHeatCanopy_dTCanopy - dSenHeatGround_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxCanair_dTGround = dSenHeatTotal_dTGround - dSenHeatCanopy_dTGround - dSenHeatGround_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanair = dSenHeatCanopy_dTCanair + dLatHeatCanopyEvap_dTCanair + dLatHeatCanopyTrans_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanopy = dSenHeatCanopy_dTCanopy + dLatHeatCanopyEvap_dTCanopy + dLatHeatCanopyTrans_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxCanopy_dTGround = dSenHeatCanopy_dTGround + dLatHeatCanopyEvap_dTGround + dLatHeatCanopyTrans_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxGround_dTCanair = dSenHeatGround_dTCanair + dLatHeatGroundEvap_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxGround_dTCanopy = dSenHeatGround_dTCanopy + dLatHeatGroundEvap_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxGround_dTGround = dSenHeatGround_dTGround + dLatHeatGroundEvap_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - ! (liquid water derivatives) - dLatHeatCanopyEvap_dCanWat = dLatHeatCanopyEvap_dWetFrac*dCanopyWetFraction_dWat ! derivative in latent heat of canopy evaporation w.r.t. canopy total water (W kg-1) - dLatHeatGroundEvap_dCanWat = latHeatSubVapGround*latentHeatConstant*groundConductanceLH*dVPCanopyAir_dCanWat ! derivative in latent heat of ground evaporation w.r.t. canopy total water (J kg-1 s-1) - ! (cross deriavtives) - dTurbFluxCanair_dCanWat = 0._rkind ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) - dTurbFluxCanopy_dCanWat = dLatHeatCanopyEvap_dCanWat + dLatHeatCanopyTrans_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - dTurbFluxGround_dCanWat = dLatHeatGroundEvap_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - else ! (just make sure we return something) - ! (energy derivatives) - dTurbFluxCanair_dTCanair = 0._rkind - dTurbFluxCanair_dTCanopy = 0._rkind - dTurbFluxCanair_dTGround = 0._rkind - dTurbFluxCanopy_dTCanair = 0._rkind - dTurbFluxCanopy_dTCanopy = 0._rkind - dTurbFluxCanopy_dTGround = 0._rkind - dTurbFluxGround_dTCanair = 0._rkind - dTurbFluxGround_dTCanopy = 0._rkind - dTurbFluxGround_dTGround = 0._rkind - ! (liquid water derivatives) - dLatHeatCanopyEvap_dCanWat = 0._rkind - dLatHeatGroundEvap_dCanWat = 0._rkind - ! (cross deriavtives) - dTurbFluxCanair_dCanWat = 0._rkind - dTurbFluxCanopy_dCanWat = 0._rkind - dTurbFluxGround_dCanWat = 0._rkind - end if + ! compute the bulk Richardson number (-) + call bulkRichardson(& + ! input + airTemp, & ! input: air temperature (K) + sfcTemp, & ! input: surface temperature (K) + windspd, & ! input: wind speed (m s-1) + mHeight, & ! input: measurement height (m) + computeDerivative, & ! input: flag to compute the derivative + ! output + RiBulk, & ! output: bulk Richardson number (-) + dRiBulk_dAirTemp, & ! output: derivative in the bulk Richardson number w.r.t. air temperature (K-1) + dRiBulk_dSfcTemp, & ! output: derivative in the bulk Richardson number w.r.t. surface temperature (K-1) + err,message) ! output: error control + + ! set derivative to one if not computing it + if(.not.computeDerivative)then + dStabilityCorrection_dRich = 1._rkind + dStabilityCorrection_dAirTemp = 1._rkind + dStabilityCorrection_dSfcTemp = 1._rkind + end if - end subroutine turbFluxes - - - ! ******************************************************************************************************* - ! private subroutine aStability: compute stability corrections for turbulent heat fluxes (-) - ! ******************************************************************************************************* - subroutine aStability(& - ! input: control - computeDerivative, & ! input: logical flag to compute analytical derivatives - ixStability, & ! input: choice of stability function - ! input: forcing data, diagnostic and state variables - mHeight, & ! input: measurement height (m) - airTemp, & ! input: air temperature (K) - sfcTemp, & ! input: surface temperature (K) - windspd, & ! input: wind speed (m s-1) - ! input: stability parameters - critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam, & ! input: parameter in Louis (1979) stability function - Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function - ! output - RiBulk, & ! output: bulk Richardson number (-) - stabilityCorrection, & ! output: stability correction for turbulent heat fluxes (-) - dStabilityCorrection_dRich, & ! output: derivative in stability correction w.r.t. Richardson number (-) - dStabilityCorrection_dAirTemp, & ! output: derivative in stability correction w.r.t. temperature (K-1) - dStabilityCorrection_dSfcTemp, & ! output: derivative in stability correction w.r.t. temperature (K-1) - err, message ) ! output: error control - implicit none - ! input: control - logical(lgt),intent(in) :: computeDerivative ! flag to compute the derivative - integer(i4b),intent(in) :: ixStability ! choice of stability function - ! input: forcing data, diagnostic and state variables - real(rkind),intent(in) :: mHeight ! measurement height (m) - real(rkind),intent(in) :: airtemp ! air temperature (K) - real(rkind),intent(in) :: sfcTemp ! surface temperature (K) - real(rkind),intent(in) :: windspd ! wind speed (m s-1) - ! input: stability parameters - real(rkind),intent(in) :: critRichNumber ! critical value for the bulk Richardson number where turbulence ceases (-) - real(rkind),intent(in) :: Louis79_bparam ! parameter in Louis (1979) stability function - real(rkind),intent(in) :: Mahrt87_eScale ! exponential scaling factor in the Mahrt (1987) stability function - ! output - real(rkind),intent(out) :: RiBulk ! bulk Richardson number (-) - real(rkind),intent(out) :: stabilityCorrection ! stability correction for turbulent heat fluxes (-) - real(rkind),intent(out) :: dStabilityCorrection_dRich ! derivative in stability correction w.r.t. Richardson number (-) - real(rkind),intent(out) :: dStabilityCorrection_dAirTemp ! derivative in stability correction w.r.t. air temperature (K-1) - real(rkind),intent(out) :: dStabilityCorrection_dSfcTemp ! derivative in stability correction w.r.t. surface temperature (K-1) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! local - real(rkind), parameter :: verySmall=1.e-10_rkind ! a very small number (avoid stability of zero) - real(rkind) :: dRiBulk_dAirTemp ! derivative in the bulk Richardson number w.r.t. air temperature (K-1) - real(rkind) :: dRiBulk_dSfcTemp ! derivative in the bulk Richardson number w.r.t. surface temperature (K-1) - real(rkind) :: bPrime ! scaled "b" parameter for stability calculations in Louis (1979) - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='aStability/' - - ! compute the bulk Richardson number (-) - call bulkRichardson(& - ! input - airTemp, & ! input: air temperature (K) - sfcTemp, & ! input: surface temperature (K) - windspd, & ! input: wind speed (m s-1) - mHeight, & ! input: measurement height (m) - computeDerivative, & ! input: flag to compute the derivative - ! output - RiBulk, & ! output: bulk Richardson number (-) - dRiBulk_dAirTemp, & ! output: derivative in the bulk Richardson number w.r.t. air temperature (K-1) - dRiBulk_dSfcTemp, & ! output: derivative in the bulk Richardson number w.r.t. surface temperature (K-1) - err,message) ! output: error control - - ! set derivative to one if not computing it - if(.not.computeDerivative)then - dStabilityCorrection_dRich = 1._rkind - dStabilityCorrection_dAirTemp = 1._rkind - dStabilityCorrection_dSfcTemp = 1._rkind - end if + ! ***** process unstable cases + if(RiBulk<0._rkind)then + ! compute surface-atmosphere exchange coefficient (-) + stabilityCorrection = (1._rkind - 16._rkind*RiBulk)**0.5_rkind + ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) + if(computeDerivative)then + dStabilityCorrection_dRich = (-16._rkind) * 0.5_rkind*(1._rkind - 16._rkind*RiBulk)**(-0.5_rkind) + dStabilityCorrection_dAirTemp = dRiBulk_dAirTemp * dStabilityCorrection_dRich + dStabilityCorrection_dSfcTemp = dRiBulk_dSfcTemp * dStabilityCorrection_dRich + end if + return + end if - ! ***** process unstable cases - if(RiBulk<0._rkind)then - ! compute surface-atmosphere exchange coefficient (-) - stabilityCorrection = (1._rkind - 16._rkind*RiBulk)**0.5_rkind - ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) + ! ***** process stable cases + select case(ixStability) + + ! ("standard" stability correction, a la Anderson 1976) + case(standard) + ! compute surface-atmosphere exchange coefficient (-) + if(RiBulk < critRichNumber) stabilityCorrection = (1._rkind - 5._rkind*RiBulk)**2._rkind + if(RiBulk >= critRichNumber) stabilityCorrection = verySmall + ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) + if(computeDerivative)then + if(RiBulk < critRichNumber) dStabilityCorrection_dRich = (-5._rkind) * 2._rkind*(1._rkind - 5._rkind*RiBulk) + if(RiBulk >= critRichNumber) dStabilityCorrection_dRich = verySmall + end if + + ! (Louis 1979) + case(louisInversePower) + ! scale the "b" parameter for stable conditions + bprime = Louis79_bparam/2._rkind + ! compute surface-atmosphere exchange coefficient (-) + stabilityCorrection = 1._rkind / ( (1._rkind + bprime*RiBulk)**2._rkind ) + if(stabilityCorrection < epsilon(stabilityCorrection)) stabilityCorrection = epsilon(stabilityCorrection) + ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) + if(computeDerivative)then + dStabilityCorrection_dRich = bprime * (-2._rkind)*(1._rkind + bprime*RiBulk)**(-3._rkind) + end if + + ! (Mahrt 1987) + case(mahrtExponential) + ! compute surface-atmosphere exchange coefficient (-) + stabilityCorrection = exp(-Mahrt87_eScale * RiBulk) + if(stabilityCorrection < epsilon(stabilityCorrection)) stabilityCorrection = epsilon(stabilityCorrection) + ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) + if(computeDerivative)then + dStabilityCorrection_dRich = (-Mahrt87_eScale) * exp(-Mahrt87_eScale * RiBulk) + end if + + ! (return error if the stability correction method is not found) + case default + err=10; message=trim(message)//"optionNotFound[stability correction]"; return + + end select + + ! get the stability correction with respect to air temperature and surface temperature + ! NOTE: air temperature is used for canopy air temperature, which is a model state variable if(computeDerivative)then - dStabilityCorrection_dRich = (-16._rkind) * 0.5_rkind*(1._rkind - 16._rkind*RiBulk)**(-0.5_rkind) - dStabilityCorrection_dAirTemp = dRiBulk_dAirTemp * dStabilityCorrection_dRich - dStabilityCorrection_dSfcTemp = dRiBulk_dSfcTemp * dStabilityCorrection_dRich + dStabilityCorrection_dAirTemp = dRiBulk_dAirTemp * dStabilityCorrection_dRich + dStabilityCorrection_dSfcTemp = dRiBulk_dSfcTemp * dStabilityCorrection_dRich end if - return - end if - - ! ***** process stable cases - select case(ixStability) - - ! ("standard" stability correction, a la Anderson 1976) - case(standard) - ! compute surface-atmosphere exchange coefficient (-) - if(RiBulk < critRichNumber) stabilityCorrection = (1._rkind - 5._rkind*RiBulk)**2._rkind - if(RiBulk >= critRichNumber) stabilityCorrection = verySmall - ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) - if(computeDerivative)then - if(RiBulk < critRichNumber) dStabilityCorrection_dRich = (-5._rkind) * 2._rkind*(1._rkind - 5._rkind*RiBulk) - if(RiBulk >= critRichNumber) dStabilityCorrection_dRich = verySmall - end if - - ! (Louis 1979) - case(louisInversePower) - ! scale the "b" parameter for stable conditions - bprime = Louis79_bparam/2._rkind - ! compute surface-atmosphere exchange coefficient (-) - stabilityCorrection = 1._rkind / ( (1._rkind + bprime*RiBulk)**2._rkind ) - if(stabilityCorrection < epsilon(stabilityCorrection)) stabilityCorrection = epsilon(stabilityCorrection) - ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) - if(computeDerivative)then - dStabilityCorrection_dRich = bprime * (-2._rkind)*(1._rkind + bprime*RiBulk)**(-3._rkind) - end if - - ! (Mahrt 1987) - case(mahrtExponential) - ! compute surface-atmosphere exchange coefficient (-) - stabilityCorrection = exp(-Mahrt87_eScale * RiBulk) - if(stabilityCorrection < epsilon(stabilityCorrection)) stabilityCorrection = epsilon(stabilityCorrection) - ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) - if(computeDerivative)then - dStabilityCorrection_dRich = (-Mahrt87_eScale) * exp(-Mahrt87_eScale * RiBulk) - end if - - ! (return error if the stability correction method is not found) - case default - err=10; message=trim(message)//"optionNotFound[stability correction]"; return - - end select - - ! get the stability correction with respect to air temperature and surface temperature - ! NOTE: air temperature is used for canopy air temperature, which is a model state variable - if(computeDerivative)then - dStabilityCorrection_dAirTemp = dRiBulk_dAirTemp * dStabilityCorrection_dRich - dStabilityCorrection_dSfcTemp = dRiBulk_dSfcTemp * dStabilityCorrection_dRich - end if - end subroutine aStability - - - ! ******************************************************************************************************* - ! private subroutine bulkRichardson: compute bulk Richardson number - ! ******************************************************************************************************* - subroutine bulkRichardson(& - ! input - airTemp, & ! input: air temperature (K) - sfcTemp, & ! input: surface temperature (K) - windspd, & ! input: wind speed (m s-1) - mHeight, & ! input: measurement height (m) - computeDerivative, & ! input: flag to compute the derivative - ! output - RiBulk, & ! output: bulk Richardson number (-) - dRiBulk_dAirTemp, & ! output: derivative in the bulk Richardson number w.r.t. air temperature (K-1) - dRiBulk_dSfcTemp, & ! output: derivative in the bulk Richardson number w.r.t. surface temperature (K-1) - err,message) ! output: error control - implicit none - ! input - real(rkind),intent(in) :: airtemp ! air temperature (K) - real(rkind),intent(in) :: sfcTemp ! surface temperature (K) - real(rkind),intent(in) :: windspd ! wind speed (m s-1) - real(rkind),intent(in) :: mHeight ! measurement height (m) - logical(lgt),intent(in) :: computeDerivative ! flag to compute the derivative - ! output - real(rkind),intent(inout) :: RiBulk ! bulk Richardson number (-) - real(rkind),intent(out) :: dRiBulk_dAirTemp ! derivative in the bulk Richardson number w.r.t. air temperature (K-1) - real(rkind),intent(out) :: dRiBulk_dSfcTemp ! derivative in the bulk Richardson number w.r.t. surface temperature (K-1) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! local variables - real(rkind) :: T_grad ! gradient in temperature between the atmosphere and surface (K) - real(rkind) :: T_mean ! mean of the atmosphere and surface temperature (K) - real(rkind) :: RiMult ! dimensionless scaling factor (-) - ! initialize error control - err=0; message='bulkRichardson/' - ! compute local variables - T_grad = airtemp - sfcTemp - T_mean = 0.5_rkind*(airtemp + sfcTemp) - RiMult = (gravity*mHeight)/(windspd*windspd) - ! compute the Richardson number - RiBulk = (T_grad/T_mean) * RiMult - ! compute the derivative in the Richardson number - if(computeDerivative)then - dRiBulk_dAirTemp = RiMult/T_mean - RiMult*T_grad/(0.5_rkind*((airtemp + sfcTemp)**2._rkind)) - dRiBulk_dSfcTemp = -RiMult/T_mean - RiMult*T_grad/(0.5_rkind*((airtemp + sfcTemp)**2._rkind)) - else - dRiBulk_dAirTemp = 1._rkind - dRiBulk_dSfcTemp = 1._rkind - end if - end subroutine bulkRichardson +end subroutine aStability + + +! ******************************************************************************************************* +! private subroutine bulkRichardson: compute bulk Richardson number +! ******************************************************************************************************* +subroutine bulkRichardson(& + ! input + airTemp, & ! input: air temperature (K) + sfcTemp, & ! input: surface temperature (K) + windspd, & ! input: wind speed (m s-1) + mHeight, & ! input: measurement height (m) + computeDerivative, & ! input: flag to compute the derivative + ! output + RiBulk, & ! output: bulk Richardson number (-) + dRiBulk_dAirTemp, & ! output: derivative in the bulk Richardson number w.r.t. air temperature (K-1) + dRiBulk_dSfcTemp, & ! output: derivative in the bulk Richardson number w.r.t. surface temperature (K-1) + err,message) ! output: error control +implicit none +! input +real(rkind),intent(in) :: airtemp ! air temperature (K) +real(rkind),intent(in) :: sfcTemp ! surface temperature (K) +real(rkind),intent(in) :: windspd ! wind speed (m s-1) +real(rkind),intent(in) :: mHeight ! measurement height (m) +logical(lgt),intent(in) :: computeDerivative ! flag to compute the derivative +! output +real(rkind),intent(inout) :: RiBulk ! bulk Richardson number (-) +real(rkind),intent(out) :: dRiBulk_dAirTemp ! derivative in the bulk Richardson number w.r.t. air temperature (K-1) +real(rkind),intent(out) :: dRiBulk_dSfcTemp ! derivative in the bulk Richardson number w.r.t. surface temperature (K-1) +integer(i4b),intent(out) :: err ! error code +character(*),intent(out) :: message ! error message +! local variables +real(rkind) :: T_grad ! gradient in temperature between the atmosphere and surface (K) +real(rkind) :: T_mean ! mean of the atmosphere and surface temperature (K) +real(rkind) :: RiMult ! dimensionless scaling factor (-) + ! initialize error control + err=0; message='bulkRichardson/' + ! compute local variables + T_grad = airtemp - sfcTemp + T_mean = 0.5_rkind*(airtemp + sfcTemp) + RiMult = (gravity*mHeight)/(windspd*windspd) + ! compute the Richardson number + RiBulk = (T_grad/T_mean) * RiMult + ! compute the derivative in the Richardson number + if(computeDerivative)then + dRiBulk_dAirTemp = RiMult/T_mean - RiMult*T_grad/(0.5_rkind*((airtemp + sfcTemp)**2._rkind)) + dRiBulk_dSfcTemp = -RiMult/T_mean - RiMult*T_grad/(0.5_rkind*((airtemp + sfcTemp)**2._rkind)) + else + dRiBulk_dAirTemp = 1._rkind + dRiBulk_dSfcTemp = 1._rkind + end if +end subroutine bulkRichardson end module vegNrgFlux_module From 87cc62b4cd5b4de69c03803ab82b7e2af4796084 Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 27 Sep 2022 20:09:19 +0000 Subject: [PATCH 0371/1472] fixed typos --- build/source/engine/soilLiqFlx.f90 | 2 +- build/source/engine/vegNrgFlux.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index 0120c5c96..56afbc728 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -766,7 +766,7 @@ subroutine soilLiqFlx(& ! (use un-perturbed value) case default - s calarHydCondTrial = mLayerHydCond(nSoil) ! hydraulic conductivity at the mid-point of the lowest unsaturated soil layer (m s-1) + scalarHydCondTrial = mLayerHydCond(nSoil) ! hydraulic conductivity at the mid-point of the lowest unsaturated soil layer (m s-1) end select ! (re-computing hydraulic conductivity) diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 2814162ab..63f67d76d 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -795,7 +795,7 @@ subroutine vegNrgFlux(& ! compute the fraction of liquid water in the canopy (-) totalCanopyWater = canopyLiqTrial + canopyIceTrial if(totalCanopyWater > tiny(1.0_rkind))then - racLiquidCanopy = canopyLiqTrial / (canopyLiqTrial + canopyIceTrial) + fracLiquidCanopy = canopyLiqTrial / (canopyLiqTrial + canopyIceTrial) else fracLiquidCanopy = 0._rkind end if From db891199417e8b2bc09c53e257a4e0c605ed15cd Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 27 Sep 2022 20:14:56 +0000 Subject: [PATCH 0372/1472] adjusted indentation --- build/source/engine/ssdNrgFlux.f90 | 1328 ++++++++++++++-------------- 1 file changed, 662 insertions(+), 666 deletions(-) diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index da3e00b22..84c893d6c 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -270,716 +270,712 @@ subroutine ssdNrgFlux(& ! output: diagnostic fluxes iLayerConductiveFlux => flux_data%var(iLookFLUX%iLayerConductiveFlux)%dat, & ! intent(out): conductive energy flux at layer interfaces at end of time step (W m-2) iLayerAdvectiveFlux => flux_data%var(iLookFLUX%iLayerAdvectiveFlux)%dat & ! intent(out): advective energy flux at layer interfaces at end of time step (W m-2) - ) ! association of local variables with information in the data structures - ! ------------------------------------------------------------------------------------------------------------------------------------------------------ - ! initialize error control - err=0; message='ssdNrgFlux/' - - ! set conductive and advective fluxes to missing in the upper boundary - ! NOTE: advective flux at the upper boundary is included in the ground heat flux - iLayerConductiveFlux(0) = realMissing - iLayerAdvectiveFlux(0) = realMissing + ) ! association of local variables with information in the data structures + ! ------------------------------------------------------------------------------------------------------------------------------------------------------ + ! initialize error control + err=0; message='ssdNrgFlux/' + + ! set conductive and advective fluxes to missing in the upper boundary + ! NOTE: advective flux at the upper boundary is included in the ground heat flux + iLayerConductiveFlux(0) = realMissing + iLayerAdvectiveFlux(0) = realMissing + + ! check the need to compute numerical derivatives + if(ixDerivMethod==numerical)then + nFlux=5 ! compute the derivatives and cross derivates using one-sided finite differences + else + nFlux=0 ! compute analytical derivatives + end if - ! check the need to compute numerical derivatives - if(ixDerivMethod==numerical)then - nFlux=5 ! compute the derivatives and cross derivates using one-sided finite differences - else - nFlux=0 ! compute analytical derivatives - end if - - ! get the indices for the snow+soil layers - if(scalarSolution)then - ixLayerDesired = pack(ixLayerState, ixSnowSoilNrg/=integerMissing) - ixTop = ixLayerDesired(1) - ixBot = ixLayerDesired(1) - else - ixTop = 0 !include layer 0 in layer interface derivatives - ixBot = nLayers - endif + ! get the indices for the snow+soil layers + if(scalarSolution)then + ixLayerDesired = pack(ixLayerState, ixSnowSoilNrg/=integerMissing) + ixTop = ixLayerDesired(1) + ixBot = ixLayerDesired(1) + else + ixTop = 0 !include layer 0 in layer interface derivatives + ixBot = nLayers + endif - ! ------------------------------------------------------------------------------------------------------------------- - ! ***** compute the derivative in fluxes at layer interfaces w.r.t state in the layer above and the layer below ***** - ! ------------------------------------------------------------------------------------------------------------------- + ! ------------------------------------------------------------------------------------------------------------------- + ! ***** compute the derivative in fluxes at layer interfaces w.r.t state in the layer above and the layer below ***** + ! ------------------------------------------------------------------------------------------------------------------- - ! initialize un-used elements - ! ***** the upper boundary - dFlux_dTempAbove(0) = 0._rkind ! this will be in canopy - dFlux_dWatAbove(0) = 0._rkind ! this will be in canopy + ! initialize un-used elements + ! ***** the upper boundary + dFlux_dTempAbove(0) = 0._rkind ! this will be in canopy + dFlux_dWatAbove(0) = 0._rkind ! this will be in canopy - ! ***** the lower boundary - dFlux_dTempBelow(nLayers) = -huge(lowerBoundTemp) ! don't expect this to be used, so deliberately set to a ridiculous value to cause problems - dFlux_dWatBelow(nLayers) = -huge(lowerBoundTemp) ! don't expect this to be used, so deliberately set to a ridiculous value to cause problems + ! ***** the lower boundary + dFlux_dTempBelow(nLayers) = -huge(lowerBoundTemp) ! don't expect this to be used, so deliberately set to a ridiculous value to cause problems + dFlux_dWatBelow(nLayers) = -huge(lowerBoundTemp) ! don't expect this to be used, so deliberately set to a ridiculous value to cause problems - ! loop through INTERFACES... - do iLayer=ixTop,ixBot + ! loop through INTERFACES... + do iLayer=ixTop,ixBot - ! either one or multiple flux calls, depending on if using analytical or numerical derivatives - do itry=nFlux,0,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) + ! either one or multiple flux calls, depending on if using analytical or numerical derivatives + do itry=nFlux,0,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) - ! ===== - ! determine layer to perturb - ! ========================== - select case(itry) - ! skip undesired perturbations - case(perturbState); cycle ! perturbing the layers above and below the flux at the interface - ! identify the index for the perturbation - case(unperturbed); ixPerturb = 0 - case(perturbStateTempAbove) - if(iLayer==0) cycle ! cannot perturb state above (does not exist) -- so keep cycling - ixPerturb = 1 - case(perturbStateTempBelow) - if(iLayer==nLayers) cycle ! cannot perturb state below (does not exist) -- so keep cycling - ixPerturb = 2 - case(perturbStateWatAbove) - if(iLayer==0) cycle ! cannot perturb state above (does not exist) -- so keep cycling - ixPerturb = 3 - case(perturbStateWatBelow) - if(iLayer==nLayers) cycle ! cannot perturb state below (does not exist) -- so keep cycling - ixPerturb = 4 - case default; err=10; message=trim(message)//"unknown perturbation"; return - end select ! (identifying layer to of perturbation) - ! determine the index in the original vector - ixOriginal = iLayer + (ixPerturb-1) - - ! ===== - ! set indices and parameters needed for layer perturbation - ! ======================================================== - mLayer_ind(1) = iLayer - mLayer_ind(2) = iLayer+1 - if (iLayer==0 ) mLayer_ind(1) = 1 - if (iLayer==nLayers ) mLayer_ind(2) = nLayers - ! indices of interface are different at top layer since interface 0 exists - iLayer_ind = mLayer_ind - if (iLayer==0 ) iLayer_ind(1) = 0 - - ! ===== - ! get input state variables... - ! ============================ - ! start with the un-perturbed case - vectorVolFracLiqTrial(1:2) = mLayerVolFracLiqTrial(mLayer_ind) - ! need to protect against negative indexes - if ( mLayer_ind(1) .ge. nSnow .and. mLayer_ind(2) .ge. nSnow)then - if (iLayer==nSnow ) mLayer_ind(1) = nsnow+1 - vectorMatricHeadTrial(1:2) = mLayerMatricHeadTrial(mLayer_ind-nSnow) - end if - vectorTempTrial(1:2) = mLayerTempTrial(mLayer_ind) - vectorVolFracIceTrial(1:2) = mLayerVolFracIceTrial(mLayer_ind) - ! make appropriate perturbations, - if(ixPerturb > 2)then - vectorMatricHeadTrial(ixPerturb-2) = vectorMatricHeadTrial(ixPerturb-2) + dx - vectorVolFracLiqTrial(ixPerturb-2) = vectorVolFracLiqTrial(ixPerturb-2) + dx - else if(ixPerturb > 0)then - vectorTempTrial(ixPerturb) = vectorTempTrial(ixPerturb) + dx - endif - - ! ***** - ! * compute the volumetric fraction of liquid, ice, and air in each layer in response to perturbation ... - ! ******************************************************************************************************* - - do i = 1,2 !(layer above and below) - select case(layerType(mLayer_ind(i))) !(snow or soil) - - case(iname_soil) - j = mLayer_ind(i)-nSnow !soil layer - if(ixPerturb > 0)then ! only recompute these if perturbed - select case(ixRichards) ! (form of Richards' equation) - case(moisture) ! - vectorMatricHeadTrial(i) = matricHead(vectorVolFracLiqTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - Tcrit = crit_soilT(vectorMatricHeadTrial(i)) - !if change temp and below critical, it changes the state variable, seems like a problem FIX - if(vectorTempTrial(i) < Tcrit) then !if do not perturb temperature, this should not change - matricFHead = (LH_fus/gravity)*(vectorTempTrial(i) - Tfreeze)/Tfreeze - vectorVolFracLiqTrial(i) = volFracLiq(matricFHead,vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - endif - case(mixdform) - Tcrit = crit_soilT(vectorMatricHeadTrial(i)) - if(vectorTempTrial(i) < Tcrit) then !if do not perturb temperature, this should not change, but matricHeadTrial will have changed - matricFHead = (LH_fus/gravity)*(vectorTempTrial(i) - Tfreeze)/Tfreeze - vectorVolFracLiqTrial(i) = volFracLiq(matricFHead,vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - else - vectorVolFracLiqTrial(i) = volFracLiq(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - endif - end select ! (form of Richards' equation) - vectorVolFracIceTrial(i) = volFracLiq(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - vectorVolFracLiqTrial(i) - endif ! (recompute if perturbed) - ! derivatives, these need to be computed because they are computed only in soilLiqFlx which is called after this - vectordPsi_dTheta(i) = dPsi_dTheta(vectorVolFracLiqTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - vectordTheta_dPsi(i) = dTheta_dPsi(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - vectorFracLiqSnow(i) = realMissing - ! soil parameters - vectortheta_sat(i) = theta_sat(j) - vectoriden_soil(i) = iden_soil(j) - vectorthCond_soil(i) = thCond_soil(j) - vectorfrac_sand(i) = frac_sand(j) - vectorfrac_clay(i) = frac_clay(j) - - case(iname_snow) - fLiq = fracliquid(vectorTempTrial(i),snowfrz_scale) ! fraction of liquid water - if(ixPerturb > 0) vectorVolFracIceTrial(i) = ( vectorVolFracLiqTrial(i) / fLiq - vectorVolFracLiqTrial(i) )*(iden_water/iden_ice) ! use perturbed nodeVolTotWatTrial - ! derivatives - vectordPsi_dTheta(i) = realMissing - vectordTheta_dPsi(i) = realMissing - vectorFracLiqSnow(i) = mLayerFracLiqSnow(mLayer_ind(i)) - ! soil parameters do not exist - vectortheta_sat(i) = realMissing - vectoriden_soil(i) = realMissing - vectorthCond_soil(i) = realMissing - vectorfrac_sand(i) = realMissing - vectorfrac_clay(i) = realMissing - - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute volumetric fraction of air'; return - - end select !(snow or soil) - enddo !(layer above and below) - - ! ===== - ! get thermal conductivity at layer interface and its derivative w.r.t. the state above and the state below... - ! ============================================================================================================ - call iLayerThermalConduct(& - ! input: model control - deriv_desired, & ! intent(in): flag indicating if derivatives are desired - ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) - ixThCondSnow, & ! intent(in): choice of method for thermal conductivity of snow - ixThCondSoil, & ! intent(in): choice of method for thermal conductivity of soil - ! input: coordinate variables - nLayers, & ! intent(in): number of layers - iLayer, & ! intent(in): layer index for output - layerType(mLayer_ind), & ! intent(in): layer type (iname_soil or iname_snow) - ! input: state variables (adjacent layers) - vectorMatricHeadTrial, & ! intent(in): matric head at the nodes (m) - vectorVolFracLiqTrial, & ! intent(in): volumetric liquid water at the nodes (m) - vectorVolFracIceTrial, & ! intent(in): volumetric ice at the nodes (m) - vectorTempTrial, & ! intent(in): temperature at the nodes (m) - ! input: pre-computed derivatives - mLayerdTheta_dTk(mLayer_ind), & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - vectorFracLiqSnow, & ! intent(in): fraction of liquid water (-) - vectordTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) - vectordPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) - ! input: model coordinate variables (adjacent layers) - mLayerHeight(mLayer_ind), & ! intent(in): height at the mid-point of the node (m) - iLayerHeight(iLayer_ind), & ! intent(in): height at the interface of the nodes (m) - ! input: soil parameters - vectortheta_sat, & ! intent(in): soil porosity (-) - vectoriden_soil, & ! intent(in): intrinsic density of soil (kg m-3) - vectorthCond_soil, & ! intent(in): thermal conductivity of soil (W m-1 K-1) - vectorfrac_sand, & ! intent(in): fraction of sand (-) - vectorfrac_clay, & ! intent(in): fraction of clay (-) - ! input: snow parameters - fixedThermalCond_snow, & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) - ! output: conductivity at the layer interface (scalars) - iLayerThermalC(iLayer), & ! intent(inout): thermal conductivity at the interface of each layer (W m-1 K-1) - ! output: derivatives in thermal conductivity w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below - dThermalC_dHydStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dHydStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above - ! output: derivatives in thermal conductivity w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (W m-1 K-2) - dThermalC_dNrgStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dNrgStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above - ! output: error control - err,cmessage) ! intent(out): error control - - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - ! compute total vertical flux, to compute derivatives - if(deriv_desired .and. ixDerivMethod==numerical)then + ! ===== + ! determine layer to perturb + ! ========================== select case(itry) - case(unperturbed); scalarThermCFlux = iLayerThermalC(iLayer) - case(perturbStateTempAbove); scalarThermCFlux_dTempAbove = iLayerThermalC(iLayer) - case(perturbStateTempBelow); scalarThermCFlux_dTempBelow = iLayerThermalC(iLayer) - case(perturbStateWatAbove); scalarThermCFlux_dWatAbove = iLayerThermalC(iLayer) - case(perturbStateWatBelow); scalarThermCFlux_dWatBelow = iLayerThermalC(iLayer) + ! skip undesired perturbations + case(perturbState); cycle ! perturbing the layers above and below the flux at the interface + ! identify the index for the perturbation + case(unperturbed); ixPerturb = 0 + case(perturbStateTempAbove) + if(iLayer==0) cycle ! cannot perturb state above (does not exist) -- so keep cycling + ixPerturb = 1 + case(perturbStateTempBelow) + if(iLayer==nLayers) cycle ! cannot perturb state below (does not exist) -- so keep cycling + ixPerturb = 2 + case(perturbStateWatAbove) + if(iLayer==0) cycle ! cannot perturb state above (does not exist) -- so keep cycling + ixPerturb = 3 + case(perturbStateWatBelow) + if(iLayer==nLayers) cycle ! cannot perturb state below (does not exist) -- so keep cycling + ixPerturb = 4 case default; err=10; message=trim(message)//"unknown perturbation"; return - end select - end if + end select ! (identifying layer to of perturbation) + ! determine the index in the original vector + ixOriginal = iLayer + (ixPerturb-1) - end do ! (looping through different flux calculations -- one or multiple calls depending if desire for numerical or analytical derivatives) + ! ===== + ! set indices and parameters needed for layer perturbation + ! ======================================================== + mLayer_ind(1) = iLayer + mLayer_ind(2) = iLayer+1 + if (iLayer==0 ) mLayer_ind(1) = 1 + if (iLayer==nLayers ) mLayer_ind(2) = nLayers + ! indices of interface are different at top layer since interface 0 exists + iLayer_ind = mLayer_ind + if (iLayer==0 ) iLayer_ind(1) = 0 + ! ===== + ! get input state variables... + ! ============================ + ! start with the un-perturbed case + vectorVolFracLiqTrial(1:2) = mLayerVolFracLiqTrial(mLayer_ind) + ! need to protect against negative indexes + if ( mLayer_ind(1) .ge. nSnow .and. mLayer_ind(2) .ge. nSnow)then + if (iLayer==nSnow ) mLayer_ind(1) = nsnow+1 + vectorMatricHeadTrial(1:2) = mLayerMatricHeadTrial(mLayer_ind-nSnow) + end if + vectorTempTrial(1:2) = mLayerTempTrial(mLayer_ind) + vectorVolFracIceTrial(1:2) = mLayerVolFracIceTrial(mLayer_ind) + ! make appropriate perturbations, + if(ixPerturb > 2)then + vectorMatricHeadTrial(ixPerturb-2) = vectorMatricHeadTrial(ixPerturb-2) + dx + vectorVolFracLiqTrial(ixPerturb-2) = vectorVolFracLiqTrial(ixPerturb-2) + dx + else if(ixPerturb > 0)then + vectorTempTrial(ixPerturb) = vectorTempTrial(ixPerturb) + dx + endif + + ! ***** + ! * compute the volumetric fraction of liquid, ice, and air in each layer in response to perturbation ... + ! ******************************************************************************************************* + + do i = 1,2 !(layer above and below) + select case(layerType(mLayer_ind(i))) !(snow or soil) + + case(iname_soil) + j = mLayer_ind(i)-nSnow !soil layer + if(ixPerturb > 0)then ! only recompute these if perturbed + select case(ixRichards) ! (form of Richards' equation) + case(moisture) ! + vectorMatricHeadTrial(i) = matricHead(vectorVolFracLiqTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) + Tcrit = crit_soilT(vectorMatricHeadTrial(i)) + !if change temp and below critical, it changes the state variable, seems like a problem FIX + if(vectorTempTrial(i) < Tcrit) then !if do not perturb temperature, this should not change + matricFHead = (LH_fus/gravity)*(vectorTempTrial(i) - Tfreeze)/Tfreeze + vectorVolFracLiqTrial(i) = volFracLiq(matricFHead,vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) + endif + case(mixdform) + Tcrit = crit_soilT(vectorMatricHeadTrial(i)) + if(vectorTempTrial(i) < Tcrit) then !if do not perturb temperature, this should not change, but matricHeadTrial will have changed + matricFHead = (LH_fus/gravity)*(vectorTempTrial(i) - Tfreeze)/Tfreeze + vectorVolFracLiqTrial(i) = volFracLiq(matricFHead,vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) + else + vectorVolFracLiqTrial(i) = volFracLiq(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) + endif + end select ! (form of Richards' equation) + vectorVolFracIceTrial(i) = volFracLiq(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - vectorVolFracLiqTrial(i) + endif ! (recompute if perturbed) + ! derivatives, these need to be computed because they are computed only in soilLiqFlx which is called after this + vectordPsi_dTheta(i) = dPsi_dTheta(vectorVolFracLiqTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) + vectordTheta_dPsi(i) = dTheta_dPsi(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) + vectorFracLiqSnow(i) = realMissing + ! soil parameters + vectortheta_sat(i) = theta_sat(j) + vectoriden_soil(i) = iden_soil(j) + vectorthCond_soil(i) = thCond_soil(j) + vectorfrac_sand(i) = frac_sand(j) + vectorfrac_clay(i) = frac_clay(j) + + case(iname_snow) + fLiq = fracliquid(vectorTempTrial(i),snowfrz_scale) ! fraction of liquid water + if(ixPerturb > 0) vectorVolFracIceTrial(i) = ( vectorVolFracLiqTrial(i) / fLiq - vectorVolFracLiqTrial(i) )*(iden_water/iden_ice) ! use perturbed nodeVolTotWatTrial + ! derivatives + vectordPsi_dTheta(i) = realMissing + vectordTheta_dPsi(i) = realMissing + vectorFracLiqSnow(i) = mLayerFracLiqSnow(mLayer_ind(i)) + ! soil parameters do not exist + vectortheta_sat(i) = realMissing + vectoriden_soil(i) = realMissing + vectorthCond_soil(i) = realMissing + vectorfrac_sand(i) = realMissing + vectorfrac_clay(i) = realMissing + + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute volumetric fraction of air'; return + + end select !(snow or soil) + enddo !(layer above and below) - ! ***** the upper boundary - if(iLayer==0)then ! (upper boundary) + ! ===== + ! get thermal conductivity at layer interface and its derivative w.r.t. the state above and the state below... + ! ============================================================================================================ + call iLayerThermalConduct(& + ! input: model control + deriv_desired, & ! intent(in): flag indicating if derivatives are desired + ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) + ixThCondSnow, & ! intent(in): choice of method for thermal conductivity of snow + ixThCondSoil, & ! intent(in): choice of method for thermal conductivity of soil + ! input: coordinate variables + nLayers, & ! intent(in): number of layers + iLayer, & ! intent(in): layer index for output + layerType(mLayer_ind), & ! intent(in): layer type (iname_soil or iname_snow) + ! input: state variables (adjacent layers) + vectorMatricHeadTrial, & ! intent(in): matric head at the nodes (m) + vectorVolFracLiqTrial, & ! intent(in): volumetric liquid water at the nodes (m) + vectorVolFracIceTrial, & ! intent(in): volumetric ice at the nodes (m) + vectorTempTrial, & ! intent(in): temperature at the nodes (m) + ! input: pre-computed derivatives + mLayerdTheta_dTk(mLayer_ind), & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + vectorFracLiqSnow, & ! intent(in): fraction of liquid water (-) + vectordTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) + vectordPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) + ! input: model coordinate variables (adjacent layers) + mLayerHeight(mLayer_ind), & ! intent(in): height at the mid-point of the node (m) + iLayerHeight(iLayer_ind), & ! intent(in): height at the interface of the nodes (m) + ! input: soil parameters + vectortheta_sat, & ! intent(in): soil porosity (-) + vectoriden_soil, & ! intent(in): intrinsic density of soil (kg m-3) + vectorthCond_soil, & ! intent(in): thermal conductivity of soil (W m-1 K-1) + vectorfrac_sand, & ! intent(in): fraction of sand (-) + vectorfrac_clay, & ! intent(in): fraction of clay (-) + ! input: snow parameters + fixedThermalCond_snow, & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) + ! output: conductivity at the layer interface (scalars) + iLayerThermalC(iLayer), & ! intent(inout): thermal conductivity at the interface of each layer (W m-1 K-1) + ! output: derivatives in thermal conductivity w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below + dThermalC_dHydStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dHydStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + ! output: derivatives in thermal conductivity w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (W m-1 K-2) + dThermalC_dNrgStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dNrgStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + ! output: error control + err,cmessage) ! intent(out): error control + + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + ! compute total vertical flux, to compute derivatives + if(deriv_desired .and. ixDerivMethod==numerical)then + select case(itry) + case(unperturbed); scalarThermCFlux = iLayerThermalC(iLayer) + case(perturbStateTempAbove); scalarThermCFlux_dTempAbove = iLayerThermalC(iLayer) + case(perturbStateTempBelow); scalarThermCFlux_dTempBelow = iLayerThermalC(iLayer) + case(perturbStateWatAbove); scalarThermCFlux_dWatAbove = iLayerThermalC(iLayer) + case(perturbStateWatBelow); scalarThermCFlux_dWatBelow = iLayerThermalC(iLayer) + case default; err=10; message=trim(message)//"unknown perturbation"; return + end select + end if + + end do ! (looping through different flux calculations -- one or multiple calls depending if desire for numerical or analytical derivatives) + + + ! ***** the upper boundary + if(iLayer==0)then ! (upper boundary) + + ! identify the upper boundary condition + select case(ix_bcUpprTdyn) + + ! * prescribed temperature at the upper boundary + case(prescribedTemp) + dz = (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) + if(ixDerivMethod==analytical)then ! ** analytical derivatives + dFlux_dWatBelow(iLayer) = -dThermalC_dHydStateBelow * ( mLayerTempTrial(iLayer+1) - upperBoundTemp )/dz + dFlux_dTempBelow(iLayer) = -dThermalC_dNrgStateBelow * ( mLayerTempTrial(iLayer+1) - upperBoundTemp )/dz - iLayerThermalC(iLayer)/dz + else ! ** numerical derivatives + flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - upperBoundTemp ) / dz + flux2 = -scalarThermCFlux_dWatBelow*( mLayerTempTrial(iLayer+1) - upperBoundTemp ) / dz + dFlux_dWatBelow(iLayer) = (flux2 - flux0)/dx + flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - upperBoundTemp ) / dz + flux2 = -scalarThermCFlux_dTempBelow*((mLayerTempTrial(iLayer+1)+dx) - upperBoundTemp ) / dz + dFlux_dTempBelow(iLayer) = (flux2 - flux0)/dx + end if + + ! * zero flux at the upper boundary + case(zeroFlux) + dFlux_dWatBelow(iLayer) = 0._rkind + dFlux_dTempBelow(iLayer) = 0._rkind + + ! * compute flux inside vegetation energy flux routine, use here + case(energyFlux) + dFlux_dWatBelow(iLayer) = 0._rkind !dGroundNetFlux_dGroundWat, does not exist in vegNrgFlux + dFlux_dTempBelow(iLayer) = dGroundNetFlux_dGroundTemp + + case default; err=20; message=trim(message)//'unable to identify upper boundary condition for thermodynamics'; return + + end select ! (identifying the upper boundary condition for thermodynamics) + !dGroundNetFlux_dGroundWat = dFlux_dWatBelow(iLayer) ! this is true, but since not used in vegNrgFlux do not define + dGroundNetFlux_dGroundTemp = dFlux_dTempBelow(iLayer) ! need this in vegNrgFlux - ! identify the upper boundary condition - select case(ix_bcUpprTdyn) + ! ***** the lower boundary + else if(iLayer==nLayers)then ! (lower boundary) - ! * prescribed temperature at the upper boundary - case(prescribedTemp) - dz = (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) + ! identify the lower boundary condition + select case(ix_bcLowrTdyn) + + ! * prescribed temperature at the lower boundary + case(prescribedTemp) + dz = mLayerDepth(iLayer)*0.5_rkind if(ixDerivMethod==analytical)then ! ** analytical derivatives - dFlux_dWatBelow(iLayer) = -dThermalC_dHydStateBelow * ( mLayerTempTrial(iLayer+1) - upperBoundTemp )/dz - dFlux_dTempBelow(iLayer) = -dThermalC_dNrgStateBelow * ( mLayerTempTrial(iLayer+1) - upperBoundTemp )/dz - iLayerThermalC(iLayer)/dz + dFlux_dWatAbove(iLayer) = -dThermalC_dHydStateAbove * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + dFlux_dTempAbove(iLayer) = -dThermalC_dNrgStateAbove * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + iLayerThermalC(iLayer)/dz else ! ** numerical derivatives - flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - upperBoundTemp ) / dz - flux2 = -scalarThermCFlux_dWatBelow*( mLayerTempTrial(iLayer+1) - upperBoundTemp ) / dz - dFlux_dWatBelow(iLayer) = (flux2 - flux0)/dx - flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - upperBoundTemp ) / dz - flux2 = -scalarThermCFlux_dTempBelow*((mLayerTempTrial(iLayer+1)+dx) - upperBoundTemp ) / dz - dFlux_dTempBelow(iLayer) = (flux2 - flux0)/dx + flux0 = -scalarThermCFlux * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + flux1 = -scalarThermCFlux_dWatAbove * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + dFlux_dWatAbove(iLayer) = (flux1 - flux0)/dx + flux0 = -scalarThermCFlux * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + flux1 = -scalarThermCFlux_dTempAbove * ( lowerBoundTemp - (mLayerTempTrial(iLayer)+dx) )/dz + dFlux_dTempAbove(iLayer) = (flux1 - flux0)/dx end if - ! * zero flux at the upper boundary - case(zeroFlux) - dFlux_dWatBelow(iLayer) = 0._rkind - dFlux_dTempBelow(iLayer) = 0._rkind - - ! * compute flux inside vegetation energy flux routine, use here - case(energyFlux) - dFlux_dWatBelow(iLayer) = 0._rkind !dGroundNetFlux_dGroundWat, does not exist in vegNrgFlux - dFlux_dTempBelow(iLayer) = dGroundNetFlux_dGroundTemp + ! * zero flux at the lower boundary + case(zeroFlux) + dFlux_dWatAbove(iLayer) = 0._rkind + dFlux_dTempAbove(iLayer) = 0._rkind - case default; err=20; message=trim(message)//'unable to identify upper boundary condition for thermodynamics'; return + case default; err=20; message=trim(message)//'unable to identify lower boundary condition for thermodynamics'; return - end select ! (identifying the upper boundary condition for thermodynamics) - !dGroundNetFlux_dGroundWat = dFlux_dWatBelow(iLayer) ! this is true, but since not used in vegNrgFlux do not define - dGroundNetFlux_dGroundTemp = dFlux_dTempBelow(iLayer) ! need this in vegNrgFlux + end select ! (identifying the lower boundary condition for thermodynamics) - ! ***** the lower boundary - else if(iLayer==nLayers)then ! (lower boundary) + ! ***** internal layers - ! identify the lower boundary condition - select case(ix_bcLowrTdyn) - - ! * prescribed temperature at the lower boundary - case(prescribedTemp) - dz = mLayerDepth(iLayer)*0.5_rkind + else + dz = (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) if(ixDerivMethod==analytical)then ! ** analytical derivatives - dFlux_dWatAbove(iLayer) = -dThermalC_dHydStateAbove * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz - dFlux_dTempAbove(iLayer) = -dThermalC_dNrgStateAbove * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + iLayerThermalC(iLayer)/dz + dFlux_dWatAbove(iLayer) = -dThermalC_dHydStateAbove * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz + dFlux_dWatBelow(iLayer) = -dThermalC_dHydStateBelow * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz + dFlux_dTempAbove(iLayer) = -dThermalC_dNrgStateAbove * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz + iLayerThermalC(iLayer)/dz + dFlux_dTempBelow(iLayer) = -dThermalC_dNrgStateBelow * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz - iLayerThermalC(iLayer)/dz else ! ** numerical derivatives - flux0 = -scalarThermCFlux * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz - flux1 = -scalarThermCFlux_dWatAbove * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz + flux1 = -scalarThermCFlux_dWatAbove*( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz + flux2 = -scalarThermCFlux_dWatBelow*( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz dFlux_dWatAbove(iLayer) = (flux1 - flux0)/dx - flux0 = -scalarThermCFlux * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz - flux1 = -scalarThermCFlux_dTempAbove * ( lowerBoundTemp - (mLayerTempTrial(iLayer)+dx) )/dz + dFlux_dWatBelow(iLayer) = (flux2 - flux0)/dx + flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz + flux1 = -scalarThermCFlux_dTempAbove*( mLayerTempTrial(iLayer+1) - (mLayerTempTrial(iLayer)+dx)) / dz + flux2 = -scalarThermCFlux_dTempBelow*((mLayerTempTrial(iLayer+1)+dx) - mLayerTempTrial(iLayer) ) / dz dFlux_dTempAbove(iLayer) = (flux1 - flux0)/dx + dFlux_dTempBelow(iLayer) = (flux2 - flux0)/dx end if - ! * zero flux at the lower boundary - case(zeroFlux) - dFlux_dWatAbove(iLayer) = 0._rkind - dFlux_dTempAbove(iLayer) = 0._rkind + end if ! type of layer (upper, internal, or lower) - case default; err=20; message=trim(message)//'unable to identify lower boundary condition for thermodynamics'; return + end do ! (looping through layers) + ! ------------------------------------------------------------------------------------------------------------------------- + ! ***** compute the conductive fluxes at layer interfaces ***** + ! Compute flux after the derivatives, because need iLayerThermal as calculated above + ! ------------------------------------------------------------------------------------------------------------------------- + do iLayer=ixTop,ixBot ! (loop through model layers) + + if(iLayer==0)then ! (upper boundary fluxes -- positive downwards) + ! flux depends on the type of upper boundary condition + select case(ix_bcUpprTdyn) ! (identify the upper boundary condition for thermodynamics + case(prescribedTemp); iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*( mLayerTempTrial(iLayer+1) - upperBoundTemp )/ & + (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) + case(zeroFlux); iLayerConductiveFlux(iLayer) = 0._rkind + case(energyFlux); iLayerConductiveFlux(iLayer) = groundNetFlux !from vegNrgFlux module end select ! (identifying the lower boundary condition for thermodynamics) - ! ***** internal layers + else if(iLayer==nLayers)then ! (lower boundary fluxes -- positive downwards) + ! flux depends on the type of lower boundary condition + select case(ix_bcLowrTdyn) ! (identify the lower boundary condition for thermodynamics + case(prescribedTemp); iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*(lowerBoundTemp - mLayerTempTrial(iLayer))/(mLayerDepth(iLayer)*0.5_rkind) + case(zeroFlux); iLayerConductiveFlux(iLayer) = 0._rkind + end select ! (identifying the lower boundary condition for thermodynamics) - else - dz = (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) - if(ixDerivMethod==analytical)then ! ** analytical derivatives - dFlux_dWatAbove(iLayer) = -dThermalC_dHydStateAbove * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz - dFlux_dWatBelow(iLayer) = -dThermalC_dHydStateBelow * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz - dFlux_dTempAbove(iLayer) = -dThermalC_dNrgStateAbove * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz + iLayerThermalC(iLayer)/dz - dFlux_dTempBelow(iLayer) = -dThermalC_dNrgStateBelow * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz - iLayerThermalC(iLayer)/dz - else ! ** numerical derivatives - flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz - flux1 = -scalarThermCFlux_dWatAbove*( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz - flux2 = -scalarThermCFlux_dWatBelow*( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz - dFlux_dWatAbove(iLayer) = (flux1 - flux0)/dx - dFlux_dWatBelow(iLayer) = (flux2 - flux0)/dx - flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz - flux1 = -scalarThermCFlux_dTempAbove*( mLayerTempTrial(iLayer+1) - (mLayerTempTrial(iLayer)+dx)) / dz - flux2 = -scalarThermCFlux_dTempBelow*((mLayerTempTrial(iLayer+1)+dx) - mLayerTempTrial(iLayer) ) / dz - dFlux_dTempAbove(iLayer) = (flux1 - flux0)/dx - dFlux_dTempBelow(iLayer) = (flux2 - flux0)/dx - end if - - end if ! type of layer (upper, internal, or lower) - - end do ! (looping through layers) - - ! ------------------------------------------------------------------------------------------------------------------------- - ! ***** compute the conductive fluxes at layer interfaces ***** - ! Compute flux after the derivatives, because need iLayerThermal as calculated above - ! ------------------------------------------------------------------------------------------------------------------------- - do iLayer=ixTop,ixBot ! (loop through model layers) - - if(iLayer==0)then ! (upper boundary fluxes -- positive downwards) - ! flux depends on the type of upper boundary condition - select case(ix_bcUpprTdyn) ! (identify the upper boundary condition for thermodynamics - case(prescribedTemp); iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*( mLayerTempTrial(iLayer+1) - upperBoundTemp )/ & - (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) - case(zeroFlux); iLayerConductiveFlux(iLayer) = 0._rkind - case(energyFlux); iLayerConductiveFlux(iLayer) = groundNetFlux !from vegNrgFlux module - end select ! (identifying the lower boundary condition for thermodynamics) - - else if(iLayer==nLayers)then ! (lower boundary fluxes -- positive downwards) - ! flux depends on the type of lower boundary condition - select case(ix_bcLowrTdyn) ! (identify the lower boundary condition for thermodynamics - case(prescribedTemp); iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*(lowerBoundTemp - mLayerTempTrial(iLayer))/(mLayerDepth(iLayer)*0.5_rkind) - case(zeroFlux); iLayerConductiveFlux(iLayer) = 0._rkind - end select ! (identifying the lower boundary condition for thermodynamics) - - else ! (domain boundary fluxes -- positive downwards) - iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) / & - (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) - - !write(*,'(a,i4,1x,2(f9.3,1x))') 'iLayer, iLayerConductiveFlux(iLayer), iLayerThermalC(iLayer) = ', iLayer, iLayerConductiveFlux(iLayer), iLayerThermalC(iLayer) - end if ! (the type of layer) - end do ! looping through layers - - ! ------------------------------------------------------------------------------------------------------------------------- - ! ***** compute the advective fluxes at layer interfaces ***** - ! ------------------------------------------------------------------------------------------------------------------------- - do iLayer=ixTop,ixBot !(loop through model layers) - - if (iLayer==0) then - iLayerAdvectiveFlux(iLayer) = realMissing !advective flux at the upper boundary is included in the ground heat flux - else ! get the liquid flux at layer interfaces - select case(layerType(iLayer)) - case(iname_snow); qFlux = iLayerLiqFluxSnow(iLayer) - case(iname_soil); qFlux = iLayerLiqFluxSoil(iLayer-nSnow) - case default; err=20; message=trim(message)//'unable to identify layer type'; return - end select - ! compute fluxes at the lower boundary -- positive downwards - if(iLayer==nLayers)then - iLayerAdvectiveFlux(iLayer) = -Cp_water*iden_water*qFlux*(lowerBoundTemp - mLayerTempTrial(iLayer)) - ! compute fluxes within the domain -- positive downwards - else - iLayerAdvectiveFlux(iLayer) = -Cp_water*iden_water*qFlux*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) - end if - end if ! (all layers except surface) - end do ! looping through layers - - ! ------------------------------------------------------------------------------------------------------------------------- - ! ***** compute the total fluxes at layer interfaces ***** - ! ------------------------------------------------------------------------------------------------------------------------- - ! NOTE: ignore advective fluxes for now - iLayerNrgFlux(ixTop:ixBot) = iLayerConductiveFlux(ixTop:ixBot) - !print*, 'iLayerNrgFlux(0:4) = ', iLayerNrgFlux(0:4) - - ! end association of local variables with information in the data structures - end associate - - end subroutine ssdNrgFlux - - - ! ************************************************************************************************************************************* - ! private subroutine iLayerThermalConduct: compute diagnostic energy variables (thermal conductivity and heat capacity) and derivatives - ! ************************************************************************************************************************************* - subroutine iLayerThermalConduct(& - ! input: model control - deriv_desired, & ! intent(in): flag indicating if derivatives are desired - ixRichards, & ! intent(in): choice of option for Richards' equation - ixThCondSnow, & ! intent(in): choice of method for thermal conductivity of snow - ixThCondSoil, & ! intent(in): choice of method for thermal conductivity of soil - ! input: coordinate variables - nLayers, & ! intent(in): number of layers - ixLayerDesired, & ! intent(in): layer index for output - layerType, & ! intent(in): layer type (iname_soil or iname_snow) - ! input: state variables (adjacent layers) - nodeMatricHead, & ! intent(in): matric head at the nodes (m) - nodeVolFracLiq, & ! intent(in): volumetric liquid water content at the nodes (m) - nodeVolFracIce, & ! intent(in): volumetric ice at the nodes (m) - nodeTemp, & ! intent(in): temperature at the nodes (m) - ! input: pre-computed derivatives - dTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - fracLiqSnow, & ! intent(in) : fraction of liquid water (-) - dTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) - dPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) - ! input: model coordinate variables (adjacent layers) - nodeHeight, & ! intent(in): height at the mid-point of the node (m) - node_iHeight, & ! intent(in): height at the interface of the nodes (m) - ! input: soil parameters at nodes - theta_sat, & ! intent(in): soil porosity (-) - iden_soil, & !intrinsic density of soil (kg m-3) - thCond_soil, & ! thermal conductivity of soil (W m-1 K-1) - frac_sand, & ! intent(in): fraction of sand (-) - frac_clay, & ! fraction of clay (-) - ! input: snow parameters - fixedThermalCond_snow, & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) - ! output: conductivity at the layer interface (scalars) - iLayerThermalC, & ! intent(inout) thermal conductivity at the interface of each layer (W m-1 K-1) - ! output: derivatives in thermal conductivity w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below - dThermalC_dHydStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dHydStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above - ! output: derivatives in thermal conductivity w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (W m-1 K-2) - dThermalC_dNrgStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dNrgStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above - ! output: error control - err,message) ! intent(out): error control - - USE snow_utils_module,only:tcond_snow ! compute thermal conductivity of snow - USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists - ! constants - USE multiconst, only: gravity, & ! gravitational acceleration (m s-1) - Tfreeze, & ! freezing point of water (K) - iden_water,iden_ice,& ! intrinsic density of water and ice (kg m-3) - LH_fus ! latent heat of fusion (J kg-1) - - implicit none - ! -------------------------------------------------------------------------------------------------------------------------------------- - ! input: model control - logical(lgt),intent(in) :: deriv_desired ! flag to indicate if derivatives are desired - integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation - integer(i4b),intent(in) :: ixThCondSnow ! choice of method for thermal conductivity of snow - integer(i4b),intent(in) :: ixThCondSoil ! choice of method for thermal conductivity of soil - ! input: coordinate variables - integer(i4b),intent(in) :: nLayers ! number of layers - integer(i4b),intent(in) :: ixLayerDesired ! layer index for output - integer(i4b),intent(in) :: layerType(:) ! layer type (iname_soil or iname_snow) - ! input: state variables - real(rkind),intent(in) :: nodeMatricHead(:) ! trial vector of total water matric potential (m) - real(rkind),intent(in) :: nodeVolFracLiq(:) ! trial vector of volumetric liquid water content, recomputed with perturbed water state(-) - real(rkind),intent(in) :: nodeVolFracIce(:) ! trial vector of ice content, recomputed with perturbed water state(-) - real(rkind),intent(in) :: nodeTemp(:) ! trial vector of temperature (K) - ! input: pre-computed derivatives - real(rkind),intent(in) :: dTheta_dTk(:) ! derivative in volumetric liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: fracLiqSnow(:) ! fraction of liquid water (-) - real(rkind),intent(in) :: dTheta_dPsi(:) ! derivative in the soil water characteristic w.r.t. psi (m-1) - real(rkind),intent(in) :: dPsi_dTheta(:) ! derivative in the soil water characteristic w.r.t. theta (m) - ! input: model coordinate variables - real(rkind),intent(in) :: nodeHeight(:) ! height at the mid-point of the lower node (m) - real(rkind),intent(in) :: node_iHeight(:) ! height at the interface of each node (m) - ! input: soil parameters - real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) - real(rkind),intent(in) :: iden_soil(:) ! intrinsic density of soil (kg m-3) - real(rkind),intent(in) :: thCond_soil(:) ! thermal conductivity of soil (W m-1 K-1) - real(rkind),intent(in) :: frac_sand(:) ! intent(in): fraction of sand (-) - real(rkind),intent(in) :: frac_clay(:) ! fraction of clay (-) - ! input: snow parameters - real(rkind),intent(in) :: fixedThermalCond_snow ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) - ! output: thermal conductivity at layer interfaces - real(rkind),intent(inout) :: iLayerThermalC ! thermal conductivity at the interface of each layer (W m-1 K-1) - ! output: thermal conductivity derivatives at all layer interfaces - real(rkind),intent(out) :: dThermalC_dHydStateAbove ! derivatives in the thermal conductivity w.r.t. matric head or volumetric liquid water in the layer above - real(rkind),intent(out) :: dThermalC_dHydStateBelow ! derivatives in the thermal conductivity w.r.t. matric head or volumetric liquid water in the layer below - real(rkind),intent(out) :: dThermalC_dNrgStateAbove ! derivatives in the thermal conductivity w.r.t. temperature in the layer above (W m-1 K-2) - real(rkind),intent(out) :: dThermalC_dNrgStateBelow ! derivatives in the thermal conductivity w.r.t. temperature in the layer below (W m-1 K-2) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables (named variables to provide index of 2-element vectors) - integer(i4b),parameter :: ixUpper=1 ! index of upper node in the 2-element vectors - integer(i4b),parameter :: ixLower=2 ! index of lower node in the 2-element vectors - character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b) :: iLayer ! index of model layer - real(rkind) :: TCn ! thermal conductivity below the layer interface (W m-1 K-1) - real(rkind) :: TCp ! thermal conductivity above the layer interface (W m-1 K-1) - real(rkind) :: zdn ! height difference between interface and lower value (m) - real(rkind) :: zdp ! height difference between interface and upper value (m) - real(rkind) :: bulkden_soil ! bulk density of soil (kg m-3) - real(rkind) :: lambda_drysoil ! thermal conductivity of dry soil (W m-1) - real(rkind) :: lambda_wetsoil ! thermal conductivity of wet soil (W m-1) - real(rkind) :: lambda_wet ! thermal conductivity of the wet material - real(rkind) :: relativeSat ! relative saturation (-) - real(rkind) :: kerstenNum ! the Kersten number (-), defining weight applied to conductivity of the wet medium - real(rkind) :: den ! denominator in the thermal conductivity calculations - real(rkind) :: dThermalC_dWat(2) ! derivative in thermal conductivity w.r.t. matric head or volumetric liquid water - real(rkind) :: dThermalC_dNrg(2) ! derivative in thermal conductivity w.r.t. temperature - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - real(rkind) :: dlambda_wet_dWat ! derivative in thermal conductivity of wet material w.r.t.soil water state variable - real(rkind) :: dlambda_wet_dTk ! derivative in thermal conductivity of wet material w.r.t. temperature - real(rkind) :: dkerstenNum_dWat ! derivative in Kersten number w.r.t. soil water state variable - real(rkind) :: mLayerThermalC(2) ! thermal conductivity of each layer (W m-1 K-1) - real(rkind) :: dVolFracLiq_dWat ! derivative in vol fraction of liquid w.r.t. water state variable - real(rkind) :: dVolFracIce_dWat ! derivative in vol fraction of ice w.r.t. water state variable - real(rkind) :: dVolFracLiq_dTk ! derivative in vol fraction of liquid w.r.t. temperature - real(rkind) :: dVolFracIce_dTk ! derivative in vol fraction of ice w.r.t. temperature -! local variables to reproduce the thermal conductivity of Hansson et al. VZJ 2005 - real(rkind),parameter :: c1=0.55_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c2=0.8_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c3=3.07_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: c4=0.13_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c5=4._rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: f1=13.05_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: f2=1.06_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind) :: fArg,xArg ! temporary variables (see Hansson et al. VZJ 2005 for details) - real(rkind) :: dxArg_dWat,dxArg_dTk ! derivates of the temporary variables with respect to soil water state variable and temperature - - ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="iLayerThermalConduct/" - - ! loop through layers - do iLayer=ixUpper,ixLower - - ! compute the thermal conductivity of dry and wet soils (W m-1) - ! NOTE: this is actually constant over the simulation, and included here for clarity - if(ixThCondSoil==funcSoilWet .and. layerType(iLayer)==iname_soil)then - bulkden_soil = iden_soil(iLayer)*( 1._rkind - theta_sat(iLayer) ) - lambda_drysoil = (0.135_rkind*bulkden_soil + 64.7_rkind) / (iden_soil(iLayer) - 0.947_rkind*bulkden_soil) - lambda_wetsoil = (8.80_rkind*frac_sand(iLayer) + 2.92_rkind*frac_clay(iLayer)) / (frac_sand(iLayer) + frac_clay(iLayer)) - end if + else ! (domain boundary fluxes -- positive downwards) + iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) / & + (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) + + !write(*,'(a,i4,1x,2(f9.3,1x))') 'iLayer, iLayerConductiveFlux(iLayer), iLayerThermalC(iLayer) = ', iLayer, iLayerConductiveFlux(iLayer), iLayerThermalC(iLayer) + end if ! (the type of layer) + end do ! looping through layers + + ! ------------------------------------------------------------------------------------------------------------------------- + ! ***** compute the advective fluxes at layer interfaces ***** + ! ------------------------------------------------------------------------------------------------------------------------- + do iLayer=ixTop,ixBot !(loop through model layers) + + if (iLayer==0) then + iLayerAdvectiveFlux(iLayer) = realMissing !advective flux at the upper boundary is included in the ground heat flux + else ! get the liquid flux at layer interfaces + select case(layerType(iLayer)) + case(iname_snow); qFlux = iLayerLiqFluxSnow(iLayer) + case(iname_soil); qFlux = iLayerLiqFluxSoil(iLayer-nSnow) + case default; err=20; message=trim(message)//'unable to identify layer type'; return + end select + ! compute fluxes at the lower boundary -- positive downwards + if(iLayer==nLayers)then + iLayerAdvectiveFlux(iLayer) = -Cp_water*iden_water*qFlux*(lowerBoundTemp - mLayerTempTrial(iLayer)) + ! compute fluxes within the domain -- positive downwards + else + iLayerAdvectiveFlux(iLayer) = -Cp_water*iden_water*qFlux*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) + end if + end if ! (all layers except surface) + end do ! looping through layers - ! ***** - ! * compute the thermal conductivity of snow and soil and derivates at the mid-point of each layer... - ! *************************************************************************************************** - dThermalC_dWat(iLayer) = 0._rkind - dThermalC_dNrg(iLayer) = 0._rkind - - select case(layerType(iLayer)) - - ! ***** soil - case(iname_soil) - - ! (process derivatives) - dVolFracLiq_dWat = 0._rkind - dVolFracIce_dWat = 0._rkind - dVolFracLiq_dTk = 0._rkind - dVolFracIce_dTk = 0._rkind - if(deriv_desired)then - select case(ixRichards) ! (form of Richards' equation) - case(moisture) - dVolFracLiq_dWat = 1._rkind - dVolFracIce_dWat = dPsi_dTheta(iLayer) - 1._rkind - case(mixdform) - Tcrit = crit_soilT(nodeMatricHead(iLayer) ) - if(nodeTemp(iLayer) < Tcrit) then - dVolFracLiq_dWat = 0._rkind - dVolFracIce_dWat = dTheta_dPsi(iLayer) - else - dVolFracLiq_dWat = dTheta_dPsi(iLayer) - dVolFracIce_dWat = 0._rkind - endif - end select - dVolFracLiq_dTk = dTheta_dTk(iLayer) !already zeroed out if not below critical temperature - dVolFracIce_dTk = -dVolFracLiq_dTk !often can and will simplify one of these terms out - endif + ! ------------------------------------------------------------------------------------------------------------------------- + ! ***** compute the total fluxes at layer interfaces ***** + ! ------------------------------------------------------------------------------------------------------------------------- + ! NOTE: ignore advective fluxes for now + iLayerNrgFlux(ixTop:ixBot) = iLayerConductiveFlux(ixTop:ixBot) - ! select option for thermal conductivity of soil - select case(ixThCondSoil) + ! end association of local variables with information in the data structures + end associate - ! ** function of soil wetness - case(funcSoilWet) +end subroutine ssdNrgFlux - ! compute the thermal conductivity of the wet material (W m-1) - lambda_wet = lambda_wetsoil**( 1._rkind - theta_sat(iLayer) ) * lambda_water**theta_sat(iLayer) * lambda_ice**(theta_sat(iLayer) - nodeVolFracLiq(iLayer)) - dlambda_wet_dWat = -lambda_wet * log(lambda_ice) * dVolFracLiq_dWat - dlambda_wet_dTk = -lambda_wet * log(lambda_ice) * dVolFracLiq_dTk - relativeSat = (nodeVolFracIce(iLayer) + nodeVolFracLiq(iLayer))/theta_sat(iLayer) ! relative saturation - ! drelativeSat_dWat = dPsi0_dWat/theta_sat(iLayer), and drelativeSat_dTk = 0 (so dkerstenNum_dTk = 0) - ! compute the Kersten number (-) - if(relativeSat > 0.1_rkind)then ! log10(0.1) = -1 - kerstenNum = log10(relativeSat) + 1._rkind - dkerstenNum_dWat = (dVolFracIce_dWat + dVolFracLiq_dWat) / ( theta_sat(iLayer) * relativeSat * log(10._rkind) ) - else - kerstenNum = 0._rkind ! dry thermal conductivity - dkerstenNum_dWat = 0._rkind - endif - ! ...and, compute the thermal conductivity - mLayerThermalC(iLayer) = kerstenNum*lambda_wet + (1._rkind - kerstenNum)*lambda_drysoil - - ! compute derivatives - dThermalC_dWat(iLayer) = dkerstenNum_dWat * ( lambda_wet - lambda_drysoil ) + kerstenNum*dlambda_wet_dWat - dThermalC_dNrg(iLayer) = kerstenNum*dlambda_wet_dTk - - ! ** mixture of constituents - case(mixConstit) - mLayerThermalC(iLayer) = thCond_soil(iLayer) * ( 1._rkind - theta_sat(iLayer) ) + & ! soil component - lambda_ice * nodeVolFracIce(iLayer) + & ! ice component - lambda_water * nodeVolFracLiq(iLayer) + & ! liquid water component - lambda_air * ( theta_sat(iLayer) - (nodeVolFracIce(iLayer) + nodeVolFracLiq(iLayer)) ) ! air component - ! compute derivatives - dThermalC_dWat(iLayer) = lambda_ice*dVolFracIce_dWat + lambda_water*dVolFracLiq_dWat + lambda_air*(-dVolFracIce_dWat - dVolFracLiq_dWat) - dThermalC_dNrg(iLayer) = (lambda_ice - lambda_water) * dVolFracIce_dTk - - ! ** test case for the mizoguchi lab experiment, Hansson et al. VZJ 2004 - case(hanssonVZJ) - fArg = 1._rkind + f1*nodeVolFracIce(iLayer)**f2 - xArg = nodeVolFracLiq(iLayer) + fArg*nodeVolFracIce(iLayer) - dxArg_dWat = dVolFracLiq_dWat + dVolFracIce_dWat * (1._rkind + f1*(f2+1)*nodeVolFracIce(iLayer)**f2) - dxArg_dTk = dVolFracIce_dTk * f1*(f2+1)*nodeVolFracIce(iLayer)**f2 - ! ...and, compute the thermal conductivity - mLayerThermalC(iLayer) = c1 + c2*xArg + (c1 - c4)*exp(-(c3*xArg)**c5) - - ! compute derivatives - dThermalC_dWat(iLayer) = ( c2 - c5*c3*(c3*xArg)**(c5-1)*(c1 - c4)*exp(-(c3*xArg)**c5) ) * dxArg_dWat - dThermalC_dNrg(iLayer) = ( c2 - c5*c3*(c3*xArg)**(c5-1)*(c1 - c4)*exp(-(c3*xArg)**c5) ) * dxArg_dTk - - ! ** check - case default; err=20; message=trim(message)//'unable to identify option for thermal conductivity of soil'; return - - end select ! option for the thermal conductivity of soil - - ! ***** snow - case(iname_snow) - dVolFracIce_dWat = ( 1._rkind - fracLiqSnow(iLayer) )*(iden_water/iden_ice) - dVolFracIce_dTk = -dTheta_dTk(iLayer)*(iden_water/iden_ice) - - ! temporally constant thermal conductivity - if(ixThCondSnow==Smirnova2000)then - mLayerThermalC(iLayer) = fixedThermalCond_snow - dThermalC_dWat(iLayer) = 0._rkind - dThermalC_dNrg(iLayer) = 0._rkind - ! thermal conductivity as a function of snow density - else - call tcond_snow(nodeVolFracIce(iLayer)*iden_ice, & ! input: snow density (kg m-3) - mLayerThermalC(iLayer), & ! output: thermal conductivity (W m-1 K-1) - err,cmessage) ! output: error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - select case(ixThCondSnow) - case(Yen1965) - dThermalC_dWat(iLayer) = 2._rkind * 3.217d-6 * nodeVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dWat - dThermalC_dNrg(iLayer) = 2._rkind * 3.217d-6 * nodeVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dTk - case(Mellor1977) - dThermalC_dWat(iLayer) = 2._rkind * 2.576d-6 * nodeVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dWat - dThermalC_dNrg(iLayer) = 2._rkind * 2.576d-6 * nodeVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dTk - case(Jordan1991) - dThermalC_dWat(iLayer) = ( 7.75d-5 + 2._rkind * 1.105d-6 * nodeVolFracIce(iLayer) * iden_ice ) * (lambda_ice-lambda_air) * iden_ice * dVolFracIce_dWat - dThermalC_dNrg(iLayer) = ( 7.75d-5 + 2._rkind * 1.105d-6 * nodeVolFracIce(iLayer) * iden_ice ) * (lambda_ice-lambda_air) * iden_ice * dVolFracIce_dTk - end select ! option for the thermal conductivity of snow +! ************************************************************************************************************************************* +! private subroutine iLayerThermalConduct: compute diagnostic energy variables (thermal conductivity and heat capacity) and derivatives +! ************************************************************************************************************************************* +subroutine iLayerThermalConduct(& + ! input: model control + deriv_desired, & ! intent(in): flag indicating if derivatives are desired + ixRichards, & ! intent(in): choice of option for Richards' equation + ixThCondSnow, & ! intent(in): choice of method for thermal conductivity of snow + ixThCondSoil, & ! intent(in): choice of method for thermal conductivity of soil + ! input: coordinate variables + nLayers, & ! intent(in): number of layers + ixLayerDesired, & ! intent(in): layer index for output + layerType, & ! intent(in): layer type (iname_soil or iname_snow) + ! input: state variables (adjacent layers) + nodeMatricHead, & ! intent(in): matric head at the nodes (m) + nodeVolFracLiq, & ! intent(in): volumetric liquid water content at the nodes (m) + nodeVolFracIce, & ! intent(in): volumetric ice at the nodes (m) + nodeTemp, & ! intent(in): temperature at the nodes (m) + ! input: pre-computed derivatives + dTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + fracLiqSnow, & ! intent(in) : fraction of liquid water (-) + dTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) + dPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) + ! input: model coordinate variables (adjacent layers) + nodeHeight, & ! intent(in): height at the mid-point of the node (m) + node_iHeight, & ! intent(in): height at the interface of the nodes (m) + ! input: soil parameters at nodes + theta_sat, & ! intent(in): soil porosity (-) + iden_soil, & !intrinsic density of soil (kg m-3) + thCond_soil, & ! thermal conductivity of soil (W m-1 K-1) + frac_sand, & ! intent(in): fraction of sand (-) + frac_clay, & ! fraction of clay (-) + ! input: snow parameters + fixedThermalCond_snow, & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) + ! output: conductivity at the layer interface (scalars) + iLayerThermalC, & ! intent(inout) thermal conductivity at the interface of each layer (W m-1 K-1) + ! output: derivatives in thermal conductivity w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below + dThermalC_dHydStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dHydStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + ! output: derivatives in thermal conductivity w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (W m-1 K-2) + dThermalC_dNrgStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dNrgStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + ! output: error control + err,message) ! intent(out): error control + + USE snow_utils_module,only:tcond_snow ! compute thermal conductivity of snow + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + ! constants + USE multiconst, only: gravity, & ! gravitational acceleration (m s-1) + Tfreeze, & ! freezing point of water (K) + iden_water,iden_ice,& ! intrinsic density of water and ice (kg m-3) + LH_fus ! latent heat of fusion (J kg-1) + + implicit none + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! input: model control + logical(lgt),intent(in) :: deriv_desired ! flag to indicate if derivatives are desired + integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation + integer(i4b),intent(in) :: ixThCondSnow ! choice of method for thermal conductivity of snow + integer(i4b),intent(in) :: ixThCondSoil ! choice of method for thermal conductivity of soil + ! input: coordinate variables + integer(i4b),intent(in) :: nLayers ! number of layers + integer(i4b),intent(in) :: ixLayerDesired ! layer index for output + integer(i4b),intent(in) :: layerType(:) ! layer type (iname_soil or iname_snow) + ! input: state variables + real(rkind),intent(in) :: nodeMatricHead(:) ! trial vector of total water matric potential (m) + real(rkind),intent(in) :: nodeVolFracLiq(:) ! trial vector of volumetric liquid water content, recomputed with perturbed water state(-) + real(rkind),intent(in) :: nodeVolFracIce(:) ! trial vector of ice content, recomputed with perturbed water state(-) + real(rkind),intent(in) :: nodeTemp(:) ! trial vector of temperature (K) + ! input: pre-computed derivatives + real(rkind),intent(in) :: dTheta_dTk(:) ! derivative in volumetric liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: fracLiqSnow(:) ! fraction of liquid water (-) + real(rkind),intent(in) :: dTheta_dPsi(:) ! derivative in the soil water characteristic w.r.t. psi (m-1) + real(rkind),intent(in) :: dPsi_dTheta(:) ! derivative in the soil water characteristic w.r.t. theta (m) + ! input: model coordinate variables + real(rkind),intent(in) :: nodeHeight(:) ! height at the mid-point of the lower node (m) + real(rkind),intent(in) :: node_iHeight(:) ! height at the interface of each node (m) + ! input: soil parameters + real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) + real(rkind),intent(in) :: iden_soil(:) ! intrinsic density of soil (kg m-3) + real(rkind),intent(in) :: thCond_soil(:) ! thermal conductivity of soil (W m-1 K-1) + real(rkind),intent(in) :: frac_sand(:) ! intent(in): fraction of sand (-) + real(rkind),intent(in) :: frac_clay(:) ! fraction of clay (-) + ! input: snow parameters + real(rkind),intent(in) :: fixedThermalCond_snow ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) + ! output: thermal conductivity at layer interfaces + real(rkind),intent(inout) :: iLayerThermalC ! thermal conductivity at the interface of each layer (W m-1 K-1) + ! output: thermal conductivity derivatives at all layer interfaces + real(rkind),intent(out) :: dThermalC_dHydStateAbove ! derivatives in the thermal conductivity w.r.t. matric head or volumetric liquid water in the layer above + real(rkind),intent(out) :: dThermalC_dHydStateBelow ! derivatives in the thermal conductivity w.r.t. matric head or volumetric liquid water in the layer below + real(rkind),intent(out) :: dThermalC_dNrgStateAbove ! derivatives in the thermal conductivity w.r.t. temperature in the layer above (W m-1 K-2) + real(rkind),intent(out) :: dThermalC_dNrgStateBelow ! derivatives in the thermal conductivity w.r.t. temperature in the layer below (W m-1 K-2) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables (named variables to provide index of 2-element vectors) + integer(i4b),parameter :: ixUpper=1 ! index of upper node in the 2-element vectors + integer(i4b),parameter :: ixLower=2 ! index of lower node in the 2-element vectors + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iLayer ! index of model layer + real(rkind) :: TCn ! thermal conductivity below the layer interface (W m-1 K-1) + real(rkind) :: TCp ! thermal conductivity above the layer interface (W m-1 K-1) + real(rkind) :: zdn ! height difference between interface and lower value (m) + real(rkind) :: zdp ! height difference between interface and upper value (m) + real(rkind) :: bulkden_soil ! bulk density of soil (kg m-3) + real(rkind) :: lambda_drysoil ! thermal conductivity of dry soil (W m-1) + real(rkind) :: lambda_wetsoil ! thermal conductivity of wet soil (W m-1) + real(rkind) :: lambda_wet ! thermal conductivity of the wet material + real(rkind) :: relativeSat ! relative saturation (-) + real(rkind) :: kerstenNum ! the Kersten number (-), defining weight applied to conductivity of the wet medium + real(rkind) :: den ! denominator in the thermal conductivity calculations + real(rkind) :: dThermalC_dWat(2) ! derivative in thermal conductivity w.r.t. matric head or volumetric liquid water + real(rkind) :: dThermalC_dNrg(2) ! derivative in thermal conductivity w.r.t. temperature + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind) :: dlambda_wet_dWat ! derivative in thermal conductivity of wet material w.r.t.soil water state variable + real(rkind) :: dlambda_wet_dTk ! derivative in thermal conductivity of wet material w.r.t. temperature + real(rkind) :: dkerstenNum_dWat ! derivative in Kersten number w.r.t. soil water state variable + real(rkind) :: mLayerThermalC(2) ! thermal conductivity of each layer (W m-1 K-1) + real(rkind) :: dVolFracLiq_dWat ! derivative in vol fraction of liquid w.r.t. water state variable + real(rkind) :: dVolFracIce_dWat ! derivative in vol fraction of ice w.r.t. water state variable + real(rkind) :: dVolFracLiq_dTk ! derivative in vol fraction of liquid w.r.t. temperature + real(rkind) :: dVolFracIce_dTk ! derivative in vol fraction of ice w.r.t. temperature + ! local variables to reproduce the thermal conductivity of Hansson et al. VZJ 2005 + real(rkind),parameter :: c1=0.55_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c2=0.8_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c3=3.07_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: c4=0.13_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c5=4._rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: f1=13.05_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: f2=1.06_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind) :: fArg,xArg ! temporary variables (see Hansson et al. VZJ 2005 for details) + real(rkind) :: dxArg_dWat,dxArg_dTk ! derivates of the temporary variables with respect to soil water state variable and temperature + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="iLayerThermalConduct/" + + ! loop through layers + do iLayer=ixUpper,ixLower + + ! compute the thermal conductivity of dry and wet soils (W m-1) + ! NOTE: this is actually constant over the simulation, and included here for clarity + if(ixThCondSoil==funcSoilWet .and. layerType(iLayer)==iname_soil)then + bulkden_soil = iden_soil(iLayer)*( 1._rkind - theta_sat(iLayer) ) + lambda_drysoil = (0.135_rkind*bulkden_soil + 64.7_rkind) / (iden_soil(iLayer) - 0.947_rkind*bulkden_soil) + lambda_wetsoil = (8.80_rkind*frac_sand(iLayer) + 2.92_rkind*frac_clay(iLayer)) / (frac_sand(iLayer) + frac_clay(iLayer)) end if - ! * error check - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute thermal conductivity'; return + ! ***** + ! * compute the thermal conductivity of snow and soil and derivates at the mid-point of each layer... + ! *************************************************************************************************** + dThermalC_dWat(iLayer) = 0._rkind + dThermalC_dNrg(iLayer) = 0._rkind - end select - !print*, 'iLayer, mLayerThermalC(iLayer) = ', iLayer, mLayerThermalC(iLayer) + select case(layerType(iLayer)) - end do ! looping through layers - !pause + ! ***** soil + case(iname_soil) - ! ***** - ! * compute the thermal conductivity of snow at the interface of each layer... - ! **************************************************************************** - if (ixLayerDesired==0) then - ! special case of hansson - if(ixThCondSoil==hanssonVZJ)then - iLayerThermalC = 28._rkind*(0.5_rkind*(node_iHeight(ixLower) - node_iHeight(ixUpper))) ! these are indices 1,0 since was passed with 0:1 - dThermalC_dHydStateBelow = 0._rkind - dThermalC_dNrgStateBelow = 0._rkind - else - iLayerThermalC = mLayerThermalC(ixUpper) ! index was passed with 1:1 - dThermalC_dHydStateBelow = dThermalC_dWat(ixUpper) - dThermalC_dNrgStateBelow = dThermalC_dNrg(ixUpper) - end if - dThermalC_dHydStateAbove = realMissing - dThermalC_dNrgStateAbove = realMissing - else if (ixLayerDesired==nLayers ) then - ! assume the thermal conductivity at the domain boundaries is equal to the thermal conductivity of the layer - iLayerThermalC = mLayerThermalC(ixLower) ! index was passed with iLayers:iLayers - dThermalC_dHydStateAbove = dThermalC_dWat(ixLower) - dThermalC_dNrgStateAbove = dThermalC_dNrg(ixLower) - dThermalC_dHydStateBelow = realMissing - dThermalC_dNrgStateBelow = realMissing - else - ! get temporary variables - TCn = mLayerThermalC(ixUpper) ! thermal conductivity below the layer interface (W m-1 K-1) - TCp = mLayerThermalC(ixLower) ! thermal conductivity above the layer interface (W m-1 K-1) - zdn = node_iHeight(ixUpper) - nodeHeight(ixUpper) ! height difference between interface and lower value (m) - zdp = nodeHeight(ixLower) - node_iHeight(ixUpper) ! height difference between interface and upper value (m) - den = TCn*zdp + TCp*zdn ! denominator - ! compute thermal conductivity - if(TCn+TCp > epsilon(TCn))then - iLayerThermalC = (TCn*TCp*(zdn + zdp)) / den - dThermalC_dHydStateBelow = ( TCn*(zdn + zdp) - iLayerThermalC*zdn ) / den * dThermalC_dWat(ixLower) - dThermalC_dHydStateAbove = ( TCp*(zdn + zdp) - iLayerThermalC*zdp ) / den * dThermalC_dWat(ixUpper) - dThermalC_dNrgStateBelow = ( TCn*(zdn + zdp) - iLayerThermalC*zdn ) / den * dThermalC_dNrg(ixLower) - dThermalC_dNrgStateAbove = ( TCp*(zdn + zdp) - iLayerThermalC*zdp ) / den * dThermalC_dNrg(ixUpper) + ! (process derivatives) + dVolFracLiq_dWat = 0._rkind + dVolFracIce_dWat = 0._rkind + dVolFracLiq_dTk = 0._rkind + dVolFracIce_dTk = 0._rkind + if(deriv_desired)then + select case(ixRichards) ! (form of Richards' equation) + case(moisture) + dVolFracLiq_dWat = 1._rkind + dVolFracIce_dWat = dPsi_dTheta(iLayer) - 1._rkind + case(mixdform) + Tcrit = crit_soilT(nodeMatricHead(iLayer) ) + if(nodeTemp(iLayer) < Tcrit) then + dVolFracLiq_dWat = 0._rkind + dVolFracIce_dWat = dTheta_dPsi(iLayer) + else + dVolFracLiq_dWat = dTheta_dPsi(iLayer) + dVolFracIce_dWat = 0._rkind + endif + end select + dVolFracLiq_dTk = dTheta_dTk(iLayer) !already zeroed out if not below critical temperature + dVolFracIce_dTk = -dVolFracLiq_dTk !often can and will simplify one of these terms out + endif + + ! select option for thermal conductivity of soil + select case(ixThCondSoil) + + ! ** function of soil wetness + case(funcSoilWet) + + ! compute the thermal conductivity of the wet material (W m-1) + lambda_wet = lambda_wetsoil**( 1._rkind - theta_sat(iLayer) ) * lambda_water**theta_sat(iLayer) * lambda_ice**(theta_sat(iLayer) - nodeVolFracLiq(iLayer)) + dlambda_wet_dWat = -lambda_wet * log(lambda_ice) * dVolFracLiq_dWat + dlambda_wet_dTk = -lambda_wet * log(lambda_ice) * dVolFracLiq_dTk + + relativeSat = (nodeVolFracIce(iLayer) + nodeVolFracLiq(iLayer))/theta_sat(iLayer) ! relative saturation + ! drelativeSat_dWat = dPsi0_dWat/theta_sat(iLayer), and drelativeSat_dTk = 0 (so dkerstenNum_dTk = 0) + ! compute the Kersten number (-) + if(relativeSat > 0.1_rkind)then ! log10(0.1) = -1 + kerstenNum = log10(relativeSat) + 1._rkind + dkerstenNum_dWat = (dVolFracIce_dWat + dVolFracLiq_dWat) / ( theta_sat(iLayer) * relativeSat * log(10._rkind) ) + else + kerstenNum = 0._rkind ! dry thermal conductivity + dkerstenNum_dWat = 0._rkind + endif + ! ...and, compute the thermal conductivity + mLayerThermalC(iLayer) = kerstenNum*lambda_wet + (1._rkind - kerstenNum)*lambda_drysoil + + ! compute derivatives + dThermalC_dWat(iLayer) = dkerstenNum_dWat * ( lambda_wet - lambda_drysoil ) + kerstenNum*dlambda_wet_dWat + dThermalC_dNrg(iLayer) = kerstenNum*dlambda_wet_dTk + + ! ** mixture of constituents + case(mixConstit) + mLayerThermalC(iLayer) = thCond_soil(iLayer) * ( 1._rkind - theta_sat(iLayer) ) + & ! soil component + lambda_ice * nodeVolFracIce(iLayer) + & ! ice component + lambda_water * nodeVolFracLiq(iLayer) + & ! liquid water component + lambda_air * ( theta_sat(iLayer) - (nodeVolFracIce(iLayer) + nodeVolFracLiq(iLayer)) ) ! air component + ! compute derivatives + dThermalC_dWat(iLayer) = lambda_ice*dVolFracIce_dWat + lambda_water*dVolFracLiq_dWat + lambda_air*(-dVolFracIce_dWat - dVolFracLiq_dWat) + dThermalC_dNrg(iLayer) = (lambda_ice - lambda_water) * dVolFracIce_dTk + + ! ** test case for the mizoguchi lab experiment, Hansson et al. VZJ 2004 + case(hanssonVZJ) + fArg = 1._rkind + f1*nodeVolFracIce(iLayer)**f2 + xArg = nodeVolFracLiq(iLayer) + fArg*nodeVolFracIce(iLayer) + dxArg_dWat = dVolFracLiq_dWat + dVolFracIce_dWat * (1._rkind + f1*(f2+1)*nodeVolFracIce(iLayer)**f2) + dxArg_dTk = dVolFracIce_dTk * f1*(f2+1)*nodeVolFracIce(iLayer)**f2 + ! ...and, compute the thermal conductivity + mLayerThermalC(iLayer) = c1 + c2*xArg + (c1 - c4)*exp(-(c3*xArg)**c5) + + ! compute derivatives + dThermalC_dWat(iLayer) = ( c2 - c5*c3*(c3*xArg)**(c5-1)*(c1 - c4)*exp(-(c3*xArg)**c5) ) * dxArg_dWat + dThermalC_dNrg(iLayer) = ( c2 - c5*c3*(c3*xArg)**(c5-1)*(c1 - c4)*exp(-(c3*xArg)**c5) ) * dxArg_dTk + + ! ** check + case default; err=20; message=trim(message)//'unable to identify option for thermal conductivity of soil'; return + + end select ! option for the thermal conductivity of soil + + ! ***** snow + case(iname_snow) + dVolFracIce_dWat = ( 1._rkind - fracLiqSnow(iLayer) )*(iden_water/iden_ice) + dVolFracIce_dTk = -dTheta_dTk(iLayer)*(iden_water/iden_ice) + + ! temporally constant thermal conductivity + if(ixThCondSnow==Smirnova2000)then + mLayerThermalC(iLayer) = fixedThermalCond_snow + dThermalC_dWat(iLayer) = 0._rkind + dThermalC_dNrg(iLayer) = 0._rkind + ! thermal conductivity as a function of snow density + else + call tcond_snow(nodeVolFracIce(iLayer)*iden_ice, & ! input: snow density (kg m-3) + mLayerThermalC(iLayer), & ! output: thermal conductivity (W m-1 K-1) + err,cmessage) ! output: error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + select case(ixThCondSnow) + case(Yen1965) + dThermalC_dWat(iLayer) = 2._rkind * 3.217d-6 * nodeVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dWat + dThermalC_dNrg(iLayer) = 2._rkind * 3.217d-6 * nodeVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dTk + case(Mellor1977) + dThermalC_dWat(iLayer) = 2._rkind * 2.576d-6 * nodeVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dWat + dThermalC_dNrg(iLayer) = 2._rkind * 2.576d-6 * nodeVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dTk + case(Jordan1991) + dThermalC_dWat(iLayer) = ( 7.75d-5 + 2._rkind * 1.105d-6 * nodeVolFracIce(iLayer) * iden_ice ) * (lambda_ice-lambda_air) * iden_ice * dVolFracIce_dWat + dThermalC_dNrg(iLayer) = ( 7.75d-5 + 2._rkind * 1.105d-6 * nodeVolFracIce(iLayer) * iden_ice ) * (lambda_ice-lambda_air) * iden_ice * dVolFracIce_dTk + end select ! option for the thermal conductivity of snow + end if + + ! * error check + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute thermal conductivity'; return + + end select + + end do ! looping through layers + + ! ***** + ! * compute the thermal conductivity of snow at the interface of each layer... + ! **************************************************************************** + if (ixLayerDesired==0) then + ! special case of hansson + if(ixThCondSoil==hanssonVZJ)then + iLayerThermalC = 28._rkind*(0.5_rkind*(node_iHeight(ixLower) - node_iHeight(ixUpper))) ! these are indices 1,0 since was passed with 0:1 + dThermalC_dHydStateBelow = 0._rkind + dThermalC_dNrgStateBelow = 0._rkind + else + iLayerThermalC = mLayerThermalC(ixUpper) ! index was passed with 1:1 + dThermalC_dHydStateBelow = dThermalC_dWat(ixUpper) + dThermalC_dNrgStateBelow = dThermalC_dNrg(ixUpper) + end if + dThermalC_dHydStateAbove = realMissing + dThermalC_dNrgStateAbove = realMissing + else if (ixLayerDesired==nLayers ) then + ! assume the thermal conductivity at the domain boundaries is equal to the thermal conductivity of the layer + iLayerThermalC = mLayerThermalC(ixLower) ! index was passed with iLayers:iLayers + dThermalC_dHydStateAbove = dThermalC_dWat(ixLower) + dThermalC_dNrgStateAbove = dThermalC_dNrg(ixLower) + dThermalC_dHydStateBelow = realMissing + dThermalC_dNrgStateBelow = realMissing else - iLayerThermalC = (TCn*zdn + TCp*zdp) / (zdn + zdp) - dThermalC_dHydStateBelow = zdp / (zdn + zdp) * dThermalC_dWat(ixLower) - dThermalC_dHydStateAbove = zdn / (zdn + zdp) * dThermalC_dWat(ixUpper) - dThermalC_dNrgStateBelow = zdp / (zdn + zdp) * dThermalC_dNrg(ixLower) - dThermalC_dNrgStateAbove = zdn / (zdn + zdp) * dThermalC_dNrg(ixUpper) - end if - !write(*,'(a,1x,i4,1x,10(f9.3,1x))') 'iLayer, TCn, TCp, zdn, zdp, iLayerThermalC(iLayer) = ', iLayer, TCn, TCp, zdn, zdp, iLayerThermalC(iLayer) - endif - - end subroutine iLayerThermalConduct + ! get temporary variables + TCn = mLayerThermalC(ixUpper) ! thermal conductivity below the layer interface (W m-1 K-1) + TCp = mLayerThermalC(ixLower) ! thermal conductivity above the layer interface (W m-1 K-1) + zdn = node_iHeight(ixUpper) - nodeHeight(ixUpper) ! height difference between interface and lower value (m) + zdp = nodeHeight(ixLower) - node_iHeight(ixUpper) ! height difference between interface and upper value (m) + den = TCn*zdp + TCp*zdn ! denominator + ! compute thermal conductivity + if(TCn+TCp > epsilon(TCn))then + iLayerThermalC = (TCn*TCp*(zdn + zdp)) / den + dThermalC_dHydStateBelow = ( TCn*(zdn + zdp) - iLayerThermalC*zdn ) / den * dThermalC_dWat(ixLower) + dThermalC_dHydStateAbove = ( TCp*(zdn + zdp) - iLayerThermalC*zdp ) / den * dThermalC_dWat(ixUpper) + dThermalC_dNrgStateBelow = ( TCn*(zdn + zdp) - iLayerThermalC*zdn ) / den * dThermalC_dNrg(ixLower) + dThermalC_dNrgStateAbove = ( TCp*(zdn + zdp) - iLayerThermalC*zdp ) / den * dThermalC_dNrg(ixUpper) + else + iLayerThermalC = (TCn*zdn + TCp*zdp) / (zdn + zdp) + dThermalC_dHydStateBelow = zdp / (zdn + zdp) * dThermalC_dWat(ixLower) + dThermalC_dHydStateAbove = zdn / (zdn + zdp) * dThermalC_dWat(ixUpper) + dThermalC_dNrgStateBelow = zdp / (zdn + zdp) * dThermalC_dNrg(ixLower) + dThermalC_dNrgStateAbove = zdn / (zdn + zdp) * dThermalC_dNrg(ixUpper) + end if + endif + +end subroutine iLayerThermalConduct From 0d0abc02d1d3dc5dafc5171ba8234234758ee470 Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 27 Sep 2022 20:16:44 +0000 Subject: [PATCH 0373/1472] adjusted intentation --- build/source/engine/vegLiqFlux.f90 | 199 ++++++++++++++--------------- 1 file changed, 98 insertions(+), 101 deletions(-) diff --git a/build/source/engine/vegLiqFlux.f90 b/build/source/engine/vegLiqFlux.f90 index 28e7db977..78e0ab758 100644 --- a/build/source/engine/vegLiqFlux.f90 +++ b/build/source/engine/vegLiqFlux.f90 @@ -47,106 +47,103 @@ module vegLiqFlux_module contains - ! ************************************************************************************************ - ! public subroutine vegLiqFlux: compute water balance for the vegetation canopy - ! ************************************************************************************************ - subroutine vegLiqFlux(& - ! input - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation - scalarCanopyLiqTrial, & ! intent(in): trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) - scalarRainfall, & ! intent(in): rainfall rate (kg m-2 s-1) - ! input-output: data structures - mpar_data, & ! intent(in): model parameters - diag_data, & ! intent(in): local HRU model diagnostic variables - ! output - scalarThroughfallRain, & ! intent(out): rain that reaches the ground without ever touching the canopy (kg m-2 s-1) - scalarCanopyLiqDrainage, & ! intent(out): drainage of liquid water from the vegetation canopy (kg m-2 s-1) - scalarThroughfallRainDeriv, & ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) - scalarCanopyLiqDrainageDeriv, & ! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) - err,message) ! intent(out): error control - implicit none - ! input - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) - real(rkind),intent(in) :: scalarCanopyLiqTrial ! trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) - real(rkind),intent(in) :: scalarRainfall ! rainfall (kg m-2 s-1) - ! input-output: data structures - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for the local basin - ! output - real(rkind),intent(out) :: scalarThroughfallRain ! rain that reaches the ground without ever touching the canopy (kg m-2 s-1) - real(rkind),intent(out) :: scalarCanopyLiqDrainage ! drainage of liquid water from the vegetation canopy (kg m-2 s-1) - real(rkind),intent(out) :: scalarThroughfallRainDeriv ! derivative in throughfall w.r.t. canopy liquid water (s-1) - real(rkind),intent(out) :: scalarCanopyLiqDrainageDeriv ! derivative in canopy drainage w.r.t. canopy liquid water (s-1) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ------------------------------------------------------------------------------------------------------------------------------------------------------ - ! make association of local variables with information in the data structures - associate(& - ixCanopyInterception => model_decisions(iLookDECISIONS%cIntercept)%iDecision, & ! intent(in): index defining choice of parameterization for canopy interception - scalarCanopyLiqMax => diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1), & ! intent(in): maximum storage before canopy drainage begins (kg m-2 s-1) - scalarThroughfallScaleRain => mpar_data%var(iLookPARAM%throughfallScaleRain)%dat(1),& ! intent(in): fraction of rain that hits the ground without touching the canopy (-) - scalarCanopyDrainageCoeff => mpar_data%var(iLookPARAM%canopyDrainageCoeff)%dat(1) & ! intent(in): canopy drainage coefficient (s-1) - ) ! associating local variables with information in the data structures - ! ------------------------------------------------------------------------------------------------------------------------------------------------------ - ! initialize error control - err=0; message="vegLiqFlux/" - - ! set throughfall to inputs if vegetation is completely buried with snow - if(.not.computeVegFlux)then - scalarThroughfallRain = scalarRainfall - scalarCanopyLiqDrainage = 0._rkind - scalarThroughfallRainDeriv = 0._rkind - scalarCanopyLiqDrainageDeriv = 0._rkind - return - end if - - ! compute throughfall - select case(ixCanopyInterception) - - ! original model (no flexibility in canopy interception): 100% of rainfall is intercepted by the vegetation canopy - ! NOTE: this could be done with scalarThroughfallScaleRain=0, though requires setting scalarThroughfallScaleRain in all test cases - case(unDefined) - scalarThroughfallRain = 0._rkind - scalarThroughfallRainDeriv = 0._rkind - - ! fraction of rainfall hits the ground without ever touching the canopy - case(sparseCanopy) - scalarThroughfallRain = scalarThroughfallScaleRain*scalarRainfall - scalarThroughfallRainDeriv = 0._rkind - - ! throughfall a function of canopy storage - case(storageFunc) - - ! throughfall during wetting-up phase - if(scalarCanopyLiqTrial < scalarCanopyLiqMax)then - scalarThroughfallRain = scalarRainfall*(scalarCanopyLiqTrial/scalarCanopyLiqMax) - scalarThroughfallRainDeriv = scalarRainfall/scalarCanopyLiqMax - - ! all rain falls through the canopy when the canopy is at capacity - else - scalarThroughfallRain = scalarRainfall - scalarThroughfallRainDeriv = 0._rkind - end if - - case default; err=20; message=trim(message)//'unable to identify option for canopy interception'; return - - end select ! (option for canopy interception) - - ! compute canopy drainage - if(scalarCanopyLiqTrial > scalarCanopyLiqMax)then - scalarCanopyLiqDrainage = scalarCanopyDrainageCoeff*(scalarCanopyLiqTrial - scalarCanopyLiqMax) - scalarCanopyLiqDrainageDeriv = scalarCanopyDrainageCoeff - else - scalarCanopyLiqDrainage = 0._rkind - scalarCanopyLiqDrainageDeriv = 0._rkind - end if - - !write(*,'(a,1x,f25.15)') 'scalarCanopyLiqDrainage = ', scalarCanopyLiqDrainage - - ! end association of local variables with information in the data structures - end associate - - end subroutine vegLiqFlux - +! ************************************************************************************************ +! public subroutine vegLiqFlux: compute water balance for the vegetation canopy +! ************************************************************************************************ +subroutine vegLiqFlux(& + ! input + computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + scalarCanopyLiqTrial, & ! intent(in): trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) + scalarRainfall, & ! intent(in): rainfall rate (kg m-2 s-1) + ! input-output: data structures + mpar_data, & ! intent(in): model parameters + diag_data, & ! intent(in): local HRU model diagnostic variables + ! output + scalarThroughfallRain, & ! intent(out): rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + scalarCanopyLiqDrainage, & ! intent(out): drainage of liquid water from the vegetation canopy (kg m-2 s-1) + scalarThroughfallRainDeriv, & ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) + scalarCanopyLiqDrainageDeriv, & ! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) + err,message) ! intent(out): error control + implicit none + ! input + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) + real(rkind),intent(in) :: scalarCanopyLiqTrial ! trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) + real(rkind),intent(in) :: scalarRainfall ! rainfall (kg m-2 s-1) + ! input-output: data structures + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for the local basin + ! output + real(rkind),intent(out) :: scalarThroughfallRain ! rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + real(rkind),intent(out) :: scalarCanopyLiqDrainage ! drainage of liquid water from the vegetation canopy (kg m-2 s-1) + real(rkind),intent(out) :: scalarThroughfallRainDeriv ! derivative in throughfall w.r.t. canopy liquid water (s-1) + real(rkind),intent(out) :: scalarCanopyLiqDrainageDeriv ! derivative in canopy drainage w.r.t. canopy liquid water (s-1) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------------------------------------------------------------ + ! make association of local variables with information in the data structures + associate(& + ixCanopyInterception => model_decisions(iLookDECISIONS%cIntercept)%iDecision, & ! intent(in): index defining choice of parameterization for canopy interception + scalarCanopyLiqMax => diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1), & ! intent(in): maximum storage before canopy drainage begins (kg m-2 s-1) + scalarThroughfallScaleRain => mpar_data%var(iLookPARAM%throughfallScaleRain)%dat(1),& ! intent(in): fraction of rain that hits the ground without touching the canopy (-) + scalarCanopyDrainageCoeff => mpar_data%var(iLookPARAM%canopyDrainageCoeff)%dat(1) & ! intent(in): canopy drainage coefficient (s-1) + ) ! associating local variables with information in the data structures + ! ------------------------------------------------------------------------------------------------------------------------------------------------------ + ! initialize error control + err=0; message="vegLiqFlux/" + + ! set throughfall to inputs if vegetation is completely buried with snow + if(.not.computeVegFlux)then + scalarThroughfallRain = scalarRainfall + scalarCanopyLiqDrainage = 0._rkind + scalarThroughfallRainDeriv = 0._rkind + scalarCanopyLiqDrainageDeriv = 0._rkind + return + end if + + ! compute throughfall + select case(ixCanopyInterception) + + ! original model (no flexibility in canopy interception): 100% of rainfall is intercepted by the vegetation canopy + ! NOTE: this could be done with scalarThroughfallScaleRain=0, though requires setting scalarThroughfallScaleRain in all test cases + case(unDefined) + scalarThroughfallRain = 0._rkind + scalarThroughfallRainDeriv = 0._rkind + + ! fraction of rainfall hits the ground without ever touching the canopy + case(sparseCanopy) + scalarThroughfallRain = scalarThroughfallScaleRain*scalarRainfall + scalarThroughfallRainDeriv = 0._rkind + + ! throughfall a function of canopy storage + case(storageFunc) + + ! throughfall during wetting-up phase + if(scalarCanopyLiqTrial < scalarCanopyLiqMax)then + scalarThroughfallRain = scalarRainfall*(scalarCanopyLiqTrial/scalarCanopyLiqMax) + scalarThroughfallRainDeriv = scalarRainfall/scalarCanopyLiqMax + + ! all rain falls through the canopy when the canopy is at capacity + else + scalarThroughfallRain = scalarRainfall + scalarThroughfallRainDeriv = 0._rkind + end if + + case default; err=20; message=trim(message)//'unable to identify option for canopy interception'; return + + end select ! (option for canopy interception) + + ! compute canopy drainage + if(scalarCanopyLiqTrial > scalarCanopyLiqMax)then + scalarCanopyLiqDrainage = scalarCanopyDrainageCoeff*(scalarCanopyLiqTrial - scalarCanopyLiqMax) + scalarCanopyLiqDrainageDeriv = scalarCanopyDrainageCoeff + else + scalarCanopyLiqDrainage = 0._rkind + scalarCanopyLiqDrainageDeriv = 0._rkind + end if + + ! end association of local variables with information in the data structures + end associate + +end subroutine vegLiqFlux end module vegLiqFlux_module From 07ecea65bee9543fa676abf32b66837f45a956c8 Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 27 Sep 2022 20:20:13 +0000 Subject: [PATCH 0374/1472] fixed indentation --- build/source/engine/snowLiqFlx.f90 | 557 ++++++++++++++--------------- 1 file changed, 267 insertions(+), 290 deletions(-) diff --git a/build/source/engine/snowLiqFlx.f90 b/build/source/engine/snowLiqFlx.f90 index 091b629a1..e31612523 100644 --- a/build/source/engine/snowLiqFlx.f90 +++ b/build/source/engine/snowLiqFlx.f90 @@ -47,312 +47,289 @@ module snowLiqFlx_module contains - ! ************************************************************************************************ - ! public subroutine snowLiqFlx: compute liquid water flux through the snowpack - ! ************************************************************************************************ - subroutine snowLiqFlx(& - ! input: model control - nSnow, & ! intent(in): number of snow layers - firstFluxCall, & ! intent(in): the first flux call - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: forcing for the snow domain - scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) - scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) - ! input: model state vector - mLayerVolFracLiqTrial, & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) - ! input-output: data structures - indx_data, & ! intent(in): model indices - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output: fluxes and derivatives - iLayerLiqFluxSnow, & ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) - iLayerLiqFluxSnowDeriv, & ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) - ! output: error control - err,message) ! intent(out): error control - implicit none - ! input: model control - integer(i4b),intent(in) :: nSnow ! number of snow layers - logical(lgt),intent(in) :: firstFluxCall ! the first flux call - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - ! input: forcing for the snow domain - real(rkind),intent(in) :: scalarThroughfallRain ! computed throughfall rate (kg m-2 s-1) - real(rkind),intent(in) :: scalarCanopyLiqDrainage ! computed drainage of liquid water (kg m-2 s-1) - ! input: model state vector - real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value of volumetric fraction of liquid water at the current iteration (-) - ! input-output: data structures - type(var_ilength),intent(in) :: indx_data ! model indices - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - ! output: fluxes and derivatives - real(rkind),intent(inout) :: iLayerLiqFluxSnow(0:) ! vertical liquid water flux at layer interfaces (m s-1) - real(rkind),intent(inout) :: iLayerLiqFluxSnowDeriv(0:) ! derivative in vertical liquid water flux at layer interfaces (m s-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ------------------------------------------------------------------------------------------------------------------------------------------ - ! local variables - integer(i4b) :: i ! search index for scalar solution - integer(i4b) :: iLayer ! layer index - integer(i4b) :: ixTop ! top layer in subroutine call - integer(i4b) :: ixBot ! bottom layer in subroutine call - real(rkind) :: multResid ! multiplier for the residual water content (-) - real(rkind),parameter :: residThrs=550._rkind ! ice density threshold to reduce residual liquid water content (kg m-3) - real(rkind),parameter :: residScal=10._rkind ! scaling factor for residual liquid water content reduction factor (kg m-3) - real(rkind),parameter :: maxVolIceContent=0.7_rkind ! maximum volumetric ice content to store water (-) - real(rkind) :: availCap ! available storage capacity [0,1] (-) - real(rkind) :: relSaturn ! relative saturation [0,1] (-) - ! ------------------------------------------------------------------------------------------------------------------------------------------ - ! make association of local variables with information in the data structures - associate(& - ! input: layer indices - ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers - ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat, & ! intent(in): index in the state subset for hydrology state variables in the snow domain - ! input: snow properties and parameters - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow), & ! intent(in): volumetric ice content at the start of the time step (-) - Fcapil => mpar_data%var(iLookPARAM%Fcapil)%dat(1), & ! intent(in): capillary retention as a fraction of the total pore volume (-) - k_snow => mpar_data%var(iLookPARAM%k_snow)%dat(1), & ! intent(in): hydraulic conductivity of snow (m s-1), 0.0055 = approx. 20 m/hr, from UEB - mw_exp => mpar_data%var(iLookPARAM%mw_exp)%dat(1), & ! intent(in): exponent for meltwater flow (-) - ! input/output: diagnostic variables -- only computed for the first iteration - mLayerPoreSpace => diag_data%var(iLookDIAG%mLayerPoreSpace)%dat, & ! intent(inout): pore space in each snow layer (-) - mLayerThetaResid => diag_data%var(iLookDIAG%mLayerThetaResid)%dat & ! intent(inout): esidual volumetric liquid water content in each snow layer (-) - ) ! association of local variables with information in the data structures - ! ------------------------------------------------------------------------------------------------------------------------------------------ - ! initialize error control - err=0; message='snowLiqFlx/' +! ************************************************************************************************ +! public subroutine snowLiqFlx: compute liquid water flux through the snowpack +! ************************************************************************************************ +subroutine snowLiqFlx(& + ! input: model control + nSnow, & ! intent(in): number of snow layers + firstFluxCall, & ! intent(in): the first flux call + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: forcing for the snow domain + scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) + scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) + ! input: model state vector + mLayerVolFracLiqTrial, & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) + ! input-output: data structures + indx_data, & ! intent(in): model indices + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + ! output: fluxes and derivatives + iLayerLiqFluxSnow, & ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) + iLayerLiqFluxSnowDeriv, & ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) + ! output: error control + err,message) ! intent(out): error control + implicit none + ! input: model control + integer(i4b),intent(in) :: nSnow ! number of snow layers + logical(lgt),intent(in) :: firstFluxCall ! the first flux call + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + ! input: forcing for the snow domain + real(rkind),intent(in) :: scalarThroughfallRain ! computed throughfall rate (kg m-2 s-1) + real(rkind),intent(in) :: scalarCanopyLiqDrainage ! computed drainage of liquid water (kg m-2 s-1) + ! input: model state vector + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value of volumetric fraction of liquid water at the current iteration (-) + ! input-output: data structures + type(var_ilength),intent(in) :: indx_data ! model indices + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + ! output: fluxes and derivatives + real(rkind),intent(inout) :: iLayerLiqFluxSnow(0:) ! vertical liquid water flux at layer interfaces (m s-1) + real(rkind),intent(inout) :: iLayerLiqFluxSnowDeriv(0:) ! derivative in vertical liquid water flux at layer interfaces (m s-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------------------------------------------------ + ! local variables + integer(i4b) :: i ! search index for scalar solution + integer(i4b) :: iLayer ! layer index + integer(i4b) :: ixTop ! top layer in subroutine call + integer(i4b) :: ixBot ! bottom layer in subroutine call + real(rkind) :: multResid ! multiplier for the residual water content (-) + real(rkind),parameter :: residThrs=550._rkind ! ice density threshold to reduce residual liquid water content (kg m-3) + real(rkind),parameter :: residScal=10._rkind ! scaling factor for residual liquid water content reduction factor (kg m-3) + real(rkind),parameter :: maxVolIceContent=0.7_rkind ! maximum volumetric ice content to store water (-) + real(rkind) :: availCap ! available storage capacity [0,1] (-) + real(rkind) :: relSaturn ! relative saturation [0,1] (-) + ! ------------------------------------------------------------------------------------------------------------------------------------------ + ! make association of local variables with information in the data structures + associate(& + ! input: layer indices + ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers + ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat, & ! intent(in): index in the state subset for hydrology state variables in the snow domain + ! input: snow properties and parameters + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow), & ! intent(in): volumetric ice content at the start of the time step (-) + Fcapil => mpar_data%var(iLookPARAM%Fcapil)%dat(1), & ! intent(in): capillary retention as a fraction of the total pore volume (-) + k_snow => mpar_data%var(iLookPARAM%k_snow)%dat(1), & ! intent(in): hydraulic conductivity of snow (m s-1), 0.0055 = approx. 20 m/hr, from UEB + mw_exp => mpar_data%var(iLookPARAM%mw_exp)%dat(1), & ! intent(in): exponent for meltwater flow (-) + ! input/output: diagnostic variables -- only computed for the first iteration + mLayerPoreSpace => diag_data%var(iLookDIAG%mLayerPoreSpace)%dat, & ! intent(inout): pore space in each snow layer (-) + mLayerThetaResid => diag_data%var(iLookDIAG%mLayerThetaResid)%dat & ! intent(inout): esidual volumetric liquid water content in each snow layer (-) + ) ! association of local variables with information in the data structures + ! ------------------------------------------------------------------------------------------------------------------------------------------ + ! initialize error control + err=0; message='snowLiqFlx/' - ! check that the input vectors match nSnow - if(size(mLayerVolFracLiqTrial)/=nSnow .or. size(mLayerVolFracIce)/=nSnow .or. & - size(iLayerLiqFluxSnow)/=nSnow+1 .or. size(iLayerLiqFluxSnowDeriv)/=nSnow+1) then - err=20; message=trim(message)//'size mismatch of input/output vectors'; return - end if + ! check that the input vectors match nSnow + if(size(mLayerVolFracLiqTrial)/=nSnow .or. size(mLayerVolFracIce)/=nSnow .or. & + size(iLayerLiqFluxSnow)/=nSnow+1 .or. size(iLayerLiqFluxSnowDeriv)/=nSnow+1) then + err=20; message=trim(message)//'size mismatch of input/output vectors'; return + end if - ! check the meltwater exponent is >=1 - if(mw_exp<1._rkind)then; err=20; message=trim(message)//'meltwater exponent < 1'; return; end if + ! check the meltwater exponent is >=1 + if(mw_exp<1._rkind)then; err=20; message=trim(message)//'meltwater exponent < 1'; return; end if - ! get the indices for the snow+soil layers - ixTop = integerMissing - if(scalarSolution)then - ! WARNING: Previously this was implemented as: - ! ixLayerDesired = pack(ixLayerState, ixSnowOnlyHyd/=integerMissing) - ! ixTop = ixLayerDesired(1) - ! ixBot = ixLayerDesired(1) - ! This implementation can result in a segfault when using JRDN layering. - ! The segfault occurs when trying to access `mw_exp` in: - ! iLayerLiqFluxSnow(iLayer) = k_snow*relSaturn**mw_exp - ! Debugging found that the `pack` statement caused `mw_exp` to no longer be accessible. - ! We have not been able to determine the underlying reason for this segfault. - do i=1,size(ixSnowOnlyHyd) - if(ixSnowOnlyHyd(i) /= integerMissing)then - ixTop=ixLayerState(i) - ixBot=ixTop - exit ! break out of loop once found + ! get the indices for the snow+soil layers + ixTop = integerMissing + if(scalarSolution)then + do i=1,size(ixSnowOnlyHyd) + if(ixSnowOnlyHyd(i) /= integerMissing)then + ixTop=ixLayerState(i) + ixBot=ixTop + exit ! break out of loop once found + endif + end do + if(ixTop == integerMissing)then + err=20; message=trim(message)//'Unable to identify snow layer for scalar solution!'; return + end if + else + ixTop = 1 + ixBot = nSnow endif - end do - if(ixTop == integerMissing)then - err=20; message=trim(message)//'Unable to identify snow layer for scalar solution!'; return - end if - else - ixTop = 1 - ixBot = nSnow - endif - ! define the liquid flux at the upper boundary (m s-1) - iLayerLiqFluxSnow(0) = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water - iLayerLiqFluxSnowDeriv(0) = 0._rkind !computed inside computJacobSundials_module + ! define the liquid flux at the upper boundary (m s-1) + iLayerLiqFluxSnow(0) = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water + iLayerLiqFluxSnowDeriv(0) = 0._rkind !computed inside computJacobSundials_module - ! compute properties fixed over the time step - if(firstFluxCall)then - ! loop through snow layers - do iLayer=1,nSnow - ! compute the reduction in liquid water holding capacity at high snow density (-) - multResid = 1._rkind / ( 1._rkind + exp( (mLayerVolFracIce(iLayer)*iden_ice - residThrs) / residScal) ) - ! compute the pore space (-) - mLayerPoreSpace(iLayer) = 1._rkind - mLayerVolFracIce(iLayer) - ! compute the residual volumetric liquid water content (-) - mLayerThetaResid(iLayer) = Fcapil*mLayerPoreSpace(iLayer) * multResid - end do ! (looping through snow layers) - end if ! (if the first flux call) + ! compute properties fixed over the time step + if(firstFluxCall)then + ! loop through snow layers + do iLayer=1,nSnow + ! compute the reduction in liquid water holding capacity at high snow density (-) + multResid = 1._rkind / ( 1._rkind + exp( (mLayerVolFracIce(iLayer)*iden_ice - residThrs) / residScal) ) + ! compute the pore space (-) + mLayerPoreSpace(iLayer) = 1._rkind - mLayerVolFracIce(iLayer) + ! compute the residual volumetric liquid water content (-) + mLayerThetaResid(iLayer) = Fcapil*mLayerPoreSpace(iLayer) * multResid + end do ! (looping through snow layers) + end if ! (if the first flux call) - ! compute fluxes - do iLayer=ixTop,ixBot ! (loop through snow layers) - ! check that flow occurs - if(mLayerVolFracLiqTrial(iLayer) > mLayerThetaResid(iLayer))then - ! compute the relative saturation (-) - availCap = mLayerPoreSpace(iLayer) - mLayerThetaResid(iLayer) ! available capacity - relSaturn = (mLayerVolFracLiqTrial(iLayer) - mLayerThetaResid(iLayer)) / availCap ! relative saturation - iLayerLiqFluxSnow(iLayer) = k_snow*relSaturn**mw_exp - iLayerLiqFluxSnowDeriv(iLayer) = ( (k_snow*mw_exp)/availCap ) * relSaturn**(mw_exp - 1._rkind) - if(mLayerVolFracIce(iLayer) > maxVolIceContent)then ! NOTE: use start-of-step ice content, to avoid convergence problems - ! ** allow liquid water to pass through under very high ice density - iLayerLiqFluxSnow(iLayer) = iLayerLiqFluxSnow(iLayer) + iLayerLiqFluxSnow(iLayer-1) !NOTE: derivative may need to be updated in future. - end if - else ! flow does not occur - iLayerLiqFluxSnow(iLayer) = 0._rkind - iLayerLiqFluxSnowDeriv(iLayer) = 0._rkind - endif ! storage above residual content - end do ! loop through snow layers + ! compute fluxes + do iLayer=ixTop,ixBot ! (loop through snow layers) + ! check that flow occurs + if(mLayerVolFracLiqTrial(iLayer) > mLayerThetaResid(iLayer))then + ! compute the relative saturation (-) + availCap = mLayerPoreSpace(iLayer) - mLayerThetaResid(iLayer) ! available capacity + relSaturn = (mLayerVolFracLiqTrial(iLayer) - mLayerThetaResid(iLayer)) / availCap ! relative saturation + iLayerLiqFluxSnow(iLayer) = k_snow*relSaturn**mw_exp + iLayerLiqFluxSnowDeriv(iLayer) = ( (k_snow*mw_exp)/availCap ) * relSaturn**(mw_exp - 1._rkind) + if(mLayerVolFracIce(iLayer) > maxVolIceContent)then ! NOTE: use start-of-step ice content, to avoid convergence problems + ! ** allow liquid water to pass through under very high ice density + iLayerLiqFluxSnow(iLayer) = iLayerLiqFluxSnow(iLayer) + iLayerLiqFluxSnow(iLayer-1) !NOTE: derivative may need to be updated in future. + end if + else ! flow does not occur + iLayerLiqFluxSnow(iLayer) = 0._rkind + iLayerLiqFluxSnowDeriv(iLayer) = 0._rkind + endif ! storage above residual content + end do ! loop through snow layers - ! end association of local variables with information in the data structures - end associate + ! end association of local variables with information in the data structures + end associate - end subroutine snowLiqFlx +end subroutine snowLiqFlx +! ************************************************************************************************ +! public subroutine snowLiqFlxSundials: compute liquid water flux through the snowpack +! ************************************************************************************************ +subroutine snowLiqFlxSundials(& + ! input: model control + nSnow, & ! intent(in): number of snow layers + firstFluxCall, & ! intent(in): the first flux call + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: forcing for the snow domain + scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) + scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) + ! input: model state vector + mLayerVolFracIce, & ! intent(in) + mLayerVolFracLiqTrial, & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) + ! input-output: data structures + indx_data, & ! intent(in): model indices + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + ! output: fluxes and derivatives + iLayerLiqFluxSnow, & ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) + iLayerLiqFluxSnowDeriv, & ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) + ! output: error control + err,message) ! intent(out): error control + implicit none + ! input: model control + integer(i4b),intent(in) :: nSnow ! number of snow layers + logical(lgt),intent(in) :: firstFluxCall ! the first flux call + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + ! input: forcing for the snow domain + real(rkind),intent(in) :: scalarThroughfallRain ! computed throughfall rate (kg m-2 s-1) + real(rkind),intent(in) :: scalarCanopyLiqDrainage ! computed drainage of liquid water (kg m-2 s-1) + ! input: model state vector + real(rkind),intent(in) :: mLayerVolFracIce(:) + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value of volumetric fraction of liquid water at the current iteration (-) + ! input-output: data structures + type(var_ilength),intent(in) :: indx_data ! model indices + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + ! output: fluxes and derivatives + real(rkind),intent(inout) :: iLayerLiqFluxSnow(0:) ! vertical liquid water flux at layer interfaces (m s-1) + real(rkind),intent(inout) :: iLayerLiqFluxSnowDeriv(0:) ! derivative in vertical liquid water flux at layer interfaces (m s-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------------------------------------------------ + ! local variables + integer(i4b) :: i ! search index for scalar solution + integer(i4b) :: iLayer ! layer index + integer(i4b) :: ixTop ! top layer in subroutine call + integer(i4b) :: ixBot ! bottom layer in subroutine call + real(rkind) :: multResid ! multiplier for the residual water content (-) + real(rkind),parameter :: residThrs=550._rkind ! ice density threshold to reduce residual liquid water content (kg m-3) + real(rkind),parameter :: residScal=10._rkind ! scaling factor for residual liquid water content reduction factor (kg m-3) + real(rkind),parameter :: maxVolIceContent=0.7_rkind ! maximum volumetric ice content to store water (-) + real(rkind) :: availCap ! available storage capacity [0,1] (-) + real(rkind) :: relSaturn ! relative saturation [0,1] (-) + ! ------------------------------------------------------------------------------------------------------------------------------------------ + ! make association of local variables with information in the data structures + associate(& + ! input: layer indices + ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers + ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat, & ! intent(in): index in the state subset for hydrology state variables in the snow domain + ! input: snow properties and parameters + Fcapil => mpar_data%var(iLookPARAM%Fcapil)%dat(1), & ! intent(in): capillary retention as a fraction of the total pore volume (-) + k_snow => mpar_data%var(iLookPARAM%k_snow)%dat(1), & ! intent(in): hydraulic conductivity of snow (m s-1), 0.0055 = approx. 20 m/hr, from UEB + mw_exp => mpar_data%var(iLookPARAM%mw_exp)%dat(1), & ! intent(in): exponent for meltwater flow (-) + ! input/output: diagnostic variables -- only computed for the first iteration + mLayerPoreSpace => diag_data%var(iLookDIAG%mLayerPoreSpace)%dat, & ! intent(inout): pore space in each snow layer (-) + mLayerThetaResid => diag_data%var(iLookDIAG%mLayerThetaResid)%dat & ! intent(inout): esidual volumetric liquid water content in each snow layer (-) + ) ! association of local variables with information in the data structures + ! ------------------------------------------------------------------------------------------------------------------------------------------ + ! initialize error control + err=0; message='snowLiqFlxSundials/' + ! check that the input vectors match nSnow + if(size(mLayerVolFracLiqTrial)/=nSnow .or. size(mLayerVolFracIce)/=nSnow .or. & + size(iLayerLiqFluxSnow)/=nSnow+1 .or. size(iLayerLiqFluxSnowDeriv)/=nSnow+1) then + err=20; message=trim(message)//'size mismatch of input/output vectors'; return + end if - ! ************************************************************************************************ - ! public subroutine snowLiqFlxSundials: compute liquid water flux through the snowpack - ! ************************************************************************************************ - subroutine snowLiqFlxSundials(& - ! input: model control - nSnow, & ! intent(in): number of snow layers - firstFluxCall, & ! intent(in): the first flux call - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: forcing for the snow domain - scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) - scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) - ! input: model state vector - mLayerVolFracIce, & ! intent(in) - mLayerVolFracLiqTrial, & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) - ! input-output: data structures - indx_data, & ! intent(in): model indices - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output: fluxes and derivatives - iLayerLiqFluxSnow, & ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) - iLayerLiqFluxSnowDeriv, & ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) - ! output: error control - err,message) ! intent(out): error control - implicit none - ! input: model control - integer(i4b),intent(in) :: nSnow ! number of snow layers - logical(lgt),intent(in) :: firstFluxCall ! the first flux call - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - ! input: forcing for the snow domain - real(rkind),intent(in) :: scalarThroughfallRain ! computed throughfall rate (kg m-2 s-1) - real(rkind),intent(in) :: scalarCanopyLiqDrainage ! computed drainage of liquid water (kg m-2 s-1) - ! input: model state vector - real(rkind),intent(in) :: mLayerVolFracIce(:) - real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value of volumetric fraction of liquid water at the current iteration (-) - ! input-output: data structures - type(var_ilength),intent(in) :: indx_data ! model indices - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - ! output: fluxes and derivatives - real(rkind),intent(inout) :: iLayerLiqFluxSnow(0:) ! vertical liquid water flux at layer interfaces (m s-1) - real(rkind),intent(inout) :: iLayerLiqFluxSnowDeriv(0:) ! derivative in vertical liquid water flux at layer interfaces (m s-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ------------------------------------------------------------------------------------------------------------------------------------------ - ! local variables - integer(i4b) :: i ! search index for scalar solution - integer(i4b) :: iLayer ! layer index - integer(i4b) :: ixTop ! top layer in subroutine call - integer(i4b) :: ixBot ! bottom layer in subroutine call - real(rkind) :: multResid ! multiplier for the residual water content (-) - real(rkind),parameter :: residThrs=550._rkind ! ice density threshold to reduce residual liquid water content (kg m-3) - real(rkind),parameter :: residScal=10._rkind ! scaling factor for residual liquid water content reduction factor (kg m-3) - real(rkind),parameter :: maxVolIceContent=0.7_rkind ! maximum volumetric ice content to store water (-) - real(rkind) :: availCap ! available storage capacity [0,1] (-) - real(rkind) :: relSaturn ! relative saturation [0,1] (-) - ! ------------------------------------------------------------------------------------------------------------------------------------------ - ! make association of local variables with information in the data structures - associate(& - ! input: layer indices - ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers - ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat, & ! intent(in): index in the state subset for hydrology state variables in the snow domain - ! input: snow properties and parameters - Fcapil => mpar_data%var(iLookPARAM%Fcapil)%dat(1), & ! intent(in): capillary retention as a fraction of the total pore volume (-) - k_snow => mpar_data%var(iLookPARAM%k_snow)%dat(1), & ! intent(in): hydraulic conductivity of snow (m s-1), 0.0055 = approx. 20 m/hr, from UEB - mw_exp => mpar_data%var(iLookPARAM%mw_exp)%dat(1), & ! intent(in): exponent for meltwater flow (-) - ! input/output: diagnostic variables -- only computed for the first iteration - mLayerPoreSpace => diag_data%var(iLookDIAG%mLayerPoreSpace)%dat, & ! intent(inout): pore space in each snow layer (-) - mLayerThetaResid => diag_data%var(iLookDIAG%mLayerThetaResid)%dat & ! intent(inout): esidual volumetric liquid water content in each snow layer (-) - ) ! association of local variables with information in the data structures - ! ------------------------------------------------------------------------------------------------------------------------------------------ - ! initialize error control - err=0; message='snowLiqFlxSundials/' + ! check the meltwater exponent is >=1 + if(mw_exp<1._rkind)then; err=20; message=trim(message)//'meltwater exponent < 1'; return; end if - ! check that the input vectors match nSnow - if(size(mLayerVolFracLiqTrial)/=nSnow .or. size(mLayerVolFracIce)/=nSnow .or. & - size(iLayerLiqFluxSnow)/=nSnow+1 .or. size(iLayerLiqFluxSnowDeriv)/=nSnow+1) then - err=20; message=trim(message)//'size mismatch of input/output vectors'; return - end if - - ! check the meltwater exponent is >=1 - if(mw_exp<1._rkind)then; err=20; message=trim(message)//'meltwater exponent < 1'; return; end if - - ! get the indices for the snow+soil layers - ixTop = integerMissing - if(scalarSolution)then - ! WARNING: Previously this was implemented as: - ! ixLayerDesired = pack(ixLayerState, ixSnowOnlyHyd/=integerMissing) - ! ixTop = ixLayerDesired(1) - ! ixBot = ixLayerDesired(1) - ! This implementation can result in a segfault when using JRDN layering. - ! The segfault occurs when trying to access `mw_exp` in: - ! iLayerLiqFluxSnow(iLayer) = k_snow*relSaturn**mw_exp - ! Debugging found that the `pack` statement caused `mw_exp` to no longer be accessible. - ! We have not been able to determine the underlying reason for this segfault. - do i=1,size(ixSnowOnlyHyd) - if(ixSnowOnlyHyd(i) /= integerMissing)then - ixTop=ixLayerState(i) - ixBot=ixTop - exit ! break out of loop once found + ! get the indices for the snow+soil layers + ixTop = integerMissing + if(scalarSolution)then + do i=1,size(ixSnowOnlyHyd) + if(ixSnowOnlyHyd(i) /= integerMissing)then + ixTop=ixLayerState(i) + ixBot=ixTop + exit ! break out of loop once found + endif + end do + if(ixTop == integerMissing)then + err=20; message=trim(message)//'Unable to identify snow layer for scalar solution!'; return + end if + else + ixTop = 1 + ixBot = nSnow endif - end do - if(ixTop == integerMissing)then - err=20; message=trim(message)//'Unable to identify snow layer for scalar solution!'; return - end if - else - ixTop = 1 - ixBot = nSnow - endif - - ! define the liquid flux at the upper boundary (m s-1) - iLayerLiqFluxSnow(0) = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water - iLayerLiqFluxSnowDeriv(0) = 0._rkind - - ! compute properties fixed over the time step - if(firstFluxCall)then - ! loop through snow layers - do iLayer=1,nSnow - ! compute the reduction in liquid water holding capacity at high snow density (-) - multResid = 1._rkind / ( 1._rkind + exp( (mLayerVolFracIce(iLayer)*iden_ice - residThrs) / residScal) ) - ! compute the pore space (-) - mLayerPoreSpace(iLayer) = 1._rkind - mLayerVolFracIce(iLayer) - ! compute the residual volumetric liquid water content (-) - mLayerThetaResid(iLayer) = Fcapil*mLayerPoreSpace(iLayer) * multResid - end do ! (looping through snow layers) - end if ! (if the first flux call) - - ! compute fluxes - do iLayer=ixTop,ixBot ! (loop through snow layers) - ! check that flow occurs - if(mLayerVolFracLiqTrial(iLayer) > mLayerThetaResid(iLayer))then - ! compute the relative saturation (-) - availCap = mLayerPoreSpace(iLayer) - mLayerThetaResid(iLayer) ! available capacity - relSaturn = (mLayerVolFracLiqTrial(iLayer) - mLayerThetaResid(iLayer)) / availCap ! relative saturation - iLayerLiqFluxSnow(iLayer) = k_snow*relSaturn**mw_exp - iLayerLiqFluxSnowDeriv(iLayer) = ( (k_snow*mw_exp)/availCap ) * relSaturn**(mw_exp - 1._rkind) - if(mLayerVolFracIce(iLayer) > maxVolIceContent)then ! NOTE: use start-of-step ice content, to avoid convergence problems - ! ** allow liquid water to pass through under very high ice density - iLayerLiqFluxSnow(iLayer) = iLayerLiqFluxSnow(iLayer) + iLayerLiqFluxSnow(iLayer-1) !NOTE: derivative may need to be updated in future. - end if - else ! flow does not occur - iLayerLiqFluxSnow(iLayer) = 0._rkind - iLayerLiqFluxSnowDeriv(iLayer) = 0._rkind - endif ! storage above residual content - end do ! loop through snow layers - ! end association of local variables with information in the data structures - end associate + ! define the liquid flux at the upper boundary (m s-1) + iLayerLiqFluxSnow(0) = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water + iLayerLiqFluxSnowDeriv(0) = 0._rkind - end subroutine snowLiqFlxSundials + ! compute properties fixed over the time step + if(firstFluxCall)then + ! loop through snow layers + do iLayer=1,nSnow + ! compute the reduction in liquid water holding capacity at high snow density (-) + multResid = 1._rkind / ( 1._rkind + exp( (mLayerVolFracIce(iLayer)*iden_ice - residThrs) / residScal) ) + ! compute the pore space (-) + mLayerPoreSpace(iLayer) = 1._rkind - mLayerVolFracIce(iLayer) + ! compute the residual volumetric liquid water content (-) + mLayerThetaResid(iLayer) = Fcapil*mLayerPoreSpace(iLayer) * multResid + end do ! (looping through snow layers) + end if ! (if the first flux call) + ! compute fluxes + do iLayer=ixTop,ixBot ! (loop through snow layers) + ! check that flow occurs + if(mLayerVolFracLiqTrial(iLayer) > mLayerThetaResid(iLayer))then + ! compute the relative saturation (-) + availCap = mLayerPoreSpace(iLayer) - mLayerThetaResid(iLayer) ! available capacity + relSaturn = (mLayerVolFracLiqTrial(iLayer) - mLayerThetaResid(iLayer)) / availCap ! relative saturation + iLayerLiqFluxSnow(iLayer) = k_snow*relSaturn**mw_exp + iLayerLiqFluxSnowDeriv(iLayer) = ( (k_snow*mw_exp)/availCap ) * relSaturn**(mw_exp - 1._rkind) + if(mLayerVolFracIce(iLayer) > maxVolIceContent)then ! NOTE: use start-of-step ice content, to avoid convergence problems + ! ** allow liquid water to pass through under very high ice density + iLayerLiqFluxSnow(iLayer) = iLayerLiqFluxSnow(iLayer) + iLayerLiqFluxSnow(iLayer-1) !NOTE: derivative may need to be updated in future. + end if + else ! flow does not occur + iLayerLiqFluxSnow(iLayer) = 0._rkind + iLayerLiqFluxSnowDeriv(iLayer) = 0._rkind + endif ! storage above residual content + end do ! loop through snow layers + ! end association of local variables with information in the data structures + end associate +end subroutine snowLiqFlxSundials end module snowLiqFlx_module From 900cc513e3c752f21b85e620a1ed31f6912b0595 Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 27 Sep 2022 20:24:15 +0000 Subject: [PATCH 0375/1472] fixed indentation --- build/source/engine/groundwatr.f90 | 940 ++++++++++++++--------------- 1 file changed, 464 insertions(+), 476 deletions(-) diff --git a/build/source/engine/groundwatr.f90 b/build/source/engine/groundwatr.f90 index 33dda5296..6db3ed497 100644 --- a/build/source/engine/groundwatr.f90 +++ b/build/source/engine/groundwatr.f90 @@ -55,481 +55,469 @@ module groundwatr_module contains - ! ************************************************************************************************ - ! public subroutine groundwatr: compute the groundwater sink term in Richards' equation - ! ************************************************************************************************ - ! - ! Method - ! ------ - ! - ! Here we assume that water avaialble for shallow groundwater flow includes is all water above - ! "field capacity" below the depth zCrit, where zCrit is defined as the lowest point in the soil - ! profile where the volumetric liquid water content is less than field capacity. - ! - ! We further assume that transmssivity (m2 s-1) for each layer is defined asuming that the water - ! available for saturated flow is located at the bottom of the soil profile. Specifically: - ! trTotal(iLayer) = tran0*(zActive(iLayer)/soilDepth)**zScale_TOPMODEL - ! trSoil(iLayer) = trTotal(iLayer) - trTotal(iLayer+1) - ! where zActive(iLayer) is the effective water table thickness for all layers up to and including - ! the current layer (working from the bottom to the top). - ! - ! The outflow from each layer is then (m3 s-1) - ! mLayerOutflow(iLayer) = trSoil(iLayer)*tan_slope*contourLength - ! where contourLength is the width of a hillslope (m) parallel to a stream - ! - ! ************************************************************************************************ - subroutine groundwatr(& - - ! input: model control - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - getSatDepth, & ! intent(in): logical flag to compute index of the lowest saturated layer - - ! input: state and diagnostic variables - mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) - mLayerMatricHeadLiq, & ! intent(in): liquid water matric potential (m) - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water (-) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice (-) - - ! input/output: data structures - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - - ! output: baseflow - ixSaturation, & ! intent(inout) index of lowest saturated layer (NOTE: only computed on the first iteration) - mLayerBaseflow, & ! intent(out): baseflow from each soil layer (m s-1) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - - ! output: error control - err,message) ! intent(out): error control - ! --------------------------------------------------------------------------------------- - ! utility modules - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water as a function of matric head - USE soil_utils_module,only:hydCond_psi ! compute hydraulic conductivity as a function of matric head - implicit none - ! --------------------------------------------------------------------------------------- - ! * dummy variables - ! --------------------------------------------------------------------------------------- - ! input: model control - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - logical(lgt),intent(in) :: getSatDepth ! logical flag to compute index of the lowest saturated layer - ! input: state and diagnostic variables - real(rkind),intent(in) :: mLayerdTheta_dPsi(:) ! derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) - real(rkind),intent(in) :: mLayerMatricHeadLiq(:) ! matric head in each layer at the current iteration (m) - real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid water (-) - real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice (-) - ! input/output: data structures - type(var_d),intent(in) :: attr_data ! spatial attributes - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - ! output: baseflow - integer(i4b),intent(inout) :: ixSaturation ! index of lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: mLayerBaseflow(:) ! baseflow from each soil layer (m s-1) - real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! --------------------------------------------------------------------------------------- - ! * local variables - ! --------------------------------------------------------------------------------------- - ! general local variables - integer(i4b) :: iLayer ! index of soil layer - real(rkind),dimension(nSoil,nSoil) :: dBaseflow_dVolLiq ! derivative in the baseflow flux w.r.t. volumetric liquid water content (m s-1) - ! local variables to compute the numerical Jacobian - logical(lgt),parameter :: doNumericalJacobian=.false. ! flag to compute the numerical Jacobian - real(rkind),dimension(nSoil) :: mLayerMatricHeadPerturbed ! perturbed matric head (m) - real(rkind),dimension(nSoil) :: mLayerVolFracLiqPerturbed ! perturbed volumetric fraction of liquid water (-) - real(rkind),dimension(nSoil) :: mLayerBaseflowPerturbed ! perturbed baseflow (m s-1) - real(rkind),dimension(nSoil,nSoil) :: nJac ! numerical Jacobian (s-1) - ! *************************************************************************************** - ! *************************************************************************************** - ! initialize error control - err=0; message='groundwatr/' - ! --------------------------------------------------------------------------------------- - ! --------------------------------------------------------------------------------------- - ! associate variables in data structures - associate(& - - ! input: baseflow parameters - fieldCapacity => mpar_data%var(iLookPARAM%fieldCapacity)%dat(1), & ! intent(in): [dp] field capacity (-) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): [dp] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat, & ! intent(in): [dp] residual volumetric water content (-) - - ! input: van Genuchten soil parametrers - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat, & ! intent(in): [dp] van Genutchen "alpha" parameter (m-1) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat, & ! intent(in): [dp] van Genutchen "n" parameter (-) - vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat, & ! intent(in): [dp] van Genutchen "m" parameter (-) - - ! output: diagnostic variables - scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1), & ! intent(out):[dp] exfiltration from the soil profile (m s-1) - mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat & ! intent(out):[dp(:)] column outflow from each soil layer (m3 s-1) - - ) ! end association to variables in data structures - - ! ************************************************************************************************ - ! (1) compute the "active" portion of the soil profile - ! ************************************************************************************************ - - ! get index of the lowest saturated layer - if(getSatDepth)then ! NOTE: only compute for the first flux call - ixSaturation = nSoil+1 ! unsaturated profile when ixSaturation>nSoil - do iLayer=nSoil,1,-1 ! start at the lowest soil layer and work upwards to the top layer - if(mLayerVolFracLiq(iLayer) > fieldCapacity)then; ixSaturation = iLayer ! index of saturated layer -- keeps getting over-written as move upwards - else; exit; end if ! (only consider saturated layer at the bottom of the soil profile) - end do ! (looping through soil layers) - end if - - ! check for an early return (no layers are "active") - if(ixSaturation > nSoil)then - scalarExfiltration = 0._rkind ! exfiltration from the soil profile (m s-1) - mLayerColumnOutflow(:) = 0._rkind ! column outflow from each soil layer (m3 s-1) - mLayerBaseflow(:) = 0._rkind ! baseflow from each soil layer (m s-1) - dBaseflow_dMatric(:,:) = 0._rkind ! derivative in baseflow w.r.t. matric head (s-1) - return - end if ! if some layers are saturated - - ! ************************************************************************************************ - ! (2) compute the baseflow flux and its derivative w.r.t volumetric liquid water content - ! ************************************************************************************************ - - ! use private subroutine to compute baseflow (for multiple calls for numerical Jacobian) - call computeBaseflow(& - ! input: control and state variables - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - .true., & ! intent(in): .true. if analytical derivatives are desired - ixSaturation, & ! intent(in): index of upper-most "saturated" layer - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water in each soil layer (-) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice in each soil layer (-) - ! input/output: data structures - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output: fluxes and derivatives - mLayerBaseflow, & ! intent(out): baseflow flux in each soil layer (m s-1) - dBaseflow_dVolLiq) ! intent(out): derivative in baseflow w.r.t. volumetric liquid water content (s-1) - - ! use the chain rule to compute the baseflow derivative w.r.t. matric head (s-1) - do iLayer=1,nSoil - dBaseflow_dMatric(1:iLayer,iLayer) = dBaseflow_dVolLiq(1:iLayer,iLayer)*mLayerdTheta_dPsi(iLayer) - if(iLayer prog_data%var(iLookPROG%iLayerHeight)%dat(nLayers), & ! intent(in): [dp] total soil depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers),& ! intent(in): [dp(:)] depth of each soil layer (m) - - ! input: diagnostic variables - surfaceHydCond => flux_data%var(iLookFLUX%mLayerSatHydCondMP)%dat(1), & ! intent(in): [dp] saturated hydraulic conductivity at the surface (m s-1) - mLayerColumnInflow => flux_data%var(iLookFLUX%mLayerColumnInflow)%dat, & ! intent(in): [dp(:)] inflow into each soil layer (m3/s) - - ! input: local attributes - HRUarea => attr_data%var(iLookATTR%HRUarea), & ! intent(in): [dp] HRU area (m2) - tan_slope => attr_data%var(iLookATTR%tan_slope), & ! intent(in): [dp] tan water table slope, taken as tan local ground surface slope (-) - contourLength => attr_data%var(iLookATTR%contourLength), & ! intent(in): [dp] length of contour at downslope edge of HRU (m) - - ! input: baseflow parameters - zScale_TOPMODEL => mpar_data%var(iLookPARAM%zScale_TOPMODEL)%dat(1), & ! intent(in): [dp] TOPMODEL exponent (-) - kAnisotropic => mpar_data%var(iLookPARAM%kAnisotropic)%dat(1), & ! intent(in): [dp] anisotropy factor for lateral hydraulic conductivity (- - fieldCapacity => mpar_data%var(iLookPARAM%fieldCapacity)%dat(1), & ! intent(in): [dp] field capacity (-) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): [dp(:)] soil porosity (-) - - ! output: diagnostic variables - scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1), & ! intent(out):[dp] exfiltration from the soil profile (m s-1) - mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat & ! intent(out):[dp(:)] column outflow from each soil layer (m3 s-1) - - ) ! end association to variables in data structures - ! *********************************************************************************************************************** - ! *********************************************************************************************************************** - ! start routine here - - ! *********************************************************************************************************************** - ! (1) compute the baseflow flux in each soil layer - ! *********************************************************************************************************************** - - ! compute the maximum transmissivity - ! NOTE: this can be done as a pre-processing step - tran0 = kAnisotropic*surfaceHydCond*soilDepth/zScale_TOPMODEL ! maximum transmissivity (m2 s-1) - - ! compute the water table thickness (m) and transmissivity in each layer (m2 s-1) - do iLayer=nSoil,ixSaturation,-1 ! loop through "active" soil layers, from lowest to highest - ! define drainable water in each layer (m) - activePorosity = theta_sat(iLayer) - fieldCapacity ! "active" porosity (-) - drainableWater = mLayerDepth(iLayer)*(max(0._rkind,mLayerVolFracLiq(iLayer) - fieldCapacity))/activePorosity - ! compute layer transmissivity - if(iLayer==nSoil)then - zActive(iLayer) = drainableWater ! water table thickness associated with storage in a given layer (m) - trTotal(iLayer) = tran0*(zActive(iLayer)/soilDepth)**zScale_TOPMODEL ! total transmissivity for total depth zActive (m2 s-1) - trSoil(iLayer) = trTotal(iLayer) ! transmissivity of water in a given layer (m2 s-1) - else - zActive(iLayer) = zActive(iLayer+1) + drainableWater - trTotal(iLayer) = tran0*(zActive(iLayer)/soilDepth)**zScale_TOPMODEL - trSoil(iLayer) = trTotal(iLayer) - trTotal(iLayer+1) - end if - !write(*,'(a,1x,i4,1x,10(f20.15,1x))') 'iLayer, mLayerMatricHeadLiq(iLayer), mLayerVolFracLiq(iLayer), zActive(iLayer), trTotal(iLayer), trSoil(iLayer) = ', & - ! iLayer, mLayerMatricHeadLiq(iLayer), mLayerVolFracLiq(iLayer), zActive(iLayer), trTotal(iLayer), trSoil(iLayer) - end do ! looping through soil layers - - ! set un-used portions of the vectors to zero - if(ixSaturation>1)then - zActive(1:ixSaturation-1) = 0._rkind - trTotal(1:ixSaturation-1) = 0._rkind - trSoil(1:ixSaturation-1) = 0._rkind - end if - - ! compute the outflow from each layer (m3 s-1) - mLayerColumnOutflow(1:nSoil) = trSoil(1:nSoil)*tan_slope*contourLength - - ! compute total column inflow and total column outflow (m s-1) - totalColumnInflow = sum(mLayerColumnInflow(1:nSoil))/HRUarea - totalColumnOutflow = sum(mLayerColumnOutflow(1:nSoil))/HRUarea - - ! compute the available storage (m) - availStorage = sum(mLayerDepth(1:nSoil)*(theta_sat - (mLayerVolFracLiq(1:nSoil)+mLayerVolFracIce(1:nSoil))) ) - - ! compute the smoothing function (-) - if(availStorage < xMinEval)then - ! (compute the logistic function) - expF = exp((availStorage - xCenter)/xWidth) - logF = 1._rkind / (1._rkind + expF) - ! (compute the derivative in the logistic function w.r.t. volumetric liquid water content in each soil layer) - dLogFunc_dLiq(1:nSoil) = mLayerDepth(1:nSoil)*(expF/xWidth)/(1._rkind + expF)**2._rkind - else - logF = 0._rkind - dLogFunc_dLiq(:) = 0._rkind - end if - - ! compute the exfiltartion (m s-1) - if(totalColumnInflow > totalColumnOutflow .and. logF > tiny(1._rkind))then - scalarExfiltration = logF*(totalColumnInflow - totalColumnOutflow) ! m s-1 - !write(*,'(a,1x,10(f30.20,1x))') 'scalarExfiltration = ', scalarExfiltration - else - scalarExfiltration = 0._rkind - end if - - ! check - !write(*,'(a,1x,10(f30.20,1x))') 'zActive(1), soilDepth, availStorage, logF, scalarExfiltration = ', & - ! zActive(1), soilDepth, availStorage, logF, scalarExfiltration - !if(scalarExfiltration > tiny(1.0_rkind)) pause 'exfiltrating' - - ! compute the baseflow in each layer (m s-1) - mLayerBaseflow(1:nSoil) = (mLayerColumnOutflow(1:nSoil) - mLayerColumnInflow(1:nSoil))/HRUarea - - ! compute the total baseflow - qbTotal = sum(mLayerBaseflow) - - ! add exfiltration to the baseflow flux at the top layer - mLayerBaseflow(1) = mLayerBaseflow(1) + scalarExfiltration - mLayerColumnOutflow(1) = mLayerColumnOutflow(1) + scalarExfiltration*HRUarea - - ! test - if(printFlag)then - xDepth = sum(mLayerDepth(ixSaturation:nSoil)*(mLayerVolFracLiq(ixSaturation:nSoil) - fieldCapacity))/sum(theta_sat(ixSaturation:nSoil) - fieldCapacity) ! "effective" water table thickness (m) - xTran = tran0*(xDepth/soilDepth)**zScale_TOPMODEL ! transmissivity for the entire aquifer (m2 s-1) - xFlow = xTran*tan_slope*contourLength/HRUarea ! total column outflow (m s-1) - print*, 'ixSaturation = ', ixSaturation - write(*,'(a,1x,5(f30.20,1x))') 'tran0, soilDepth = ', tran0, soilDepth - write(*,'(a,1x,5(f30.20,1x))') 'surfaceHydCond, zScale_TOPMODEL = ', surfaceHydCond, zScale_TOPMODEL - write(*,'(a,1x,5(f30.20,1x))') 'xDepth, zActive(ixSaturation) = ', xDepth, zActive(ixSaturation) - write(*,'(a,1x,5(f30.20,1x))') 'xTran, trTotal(ixSaturation) = ', xTran, trTotal(ixSaturation) - write(*,'(a,1x,5(f30.20,1x))') 'xFlow, totalColumnOutflow = ', xFlow, sum(mLayerColumnOutflow(:))/HRUarea - !pause 'check groundwater' - end if - - ! *********************************************************************************************************************** - ! (2) compute the derivative in the baseflow flux w.r.t. volumetric liquid water content (m s-1) - ! *********************************************************************************************************************** - - ! initialize the derivative matrix - dBaseflow_dVolLiq(:,:) = 0._rkind - - ! check if derivatives are actually required - if(.not.derivDesired) return - - ! compute ratio of hillslope width to hillslope area (m m-2) - length2area = tan_slope*contourLength/HRUarea - - ! compute the ratio of layer depth to maximum water holding capacity (-) - depth2capacity(1:nSoil) = mLayerDepth(1:nSoil)/sum( (theta_sat(1:nSoil) - fieldCapacity)*mLayerDepth(1:nSoil) ) - - ! compute the change in dimensionless flux w.r.t. change in dimensionless storage (-) - dXdS(1:nSoil) = zScale_TOPMODEL*(zActive(1:nSoil)/SoilDepth)**(zScale_TOPMODEL - 1._rkind) - - ! loop through soil layers - do iLayer=1,nSoil - ! compute diagonal terms (s-1) - dBaseflow_dVolLiq(iLayer,iLayer) = tran0*dXdS(iLayer)*depth2capacity(iLayer)*length2area - ! compute off-diagonal terms - do jLayer=iLayer+1,nSoil ! (only dependent on layers below) - dBaseflow_dVolLiq(iLayer,jLayer) = tran0*(dXdS(iLayer) - dXdS(iLayer+1))*depth2capacity(jLayer)*length2area - end do ! looping through soil layers - end do ! looping through soil layers - - ! compute the derivative in the exfiltration flux w.r.t. volumetric liquid water content (m s-1) - if(qbTotal < 0._rkind)then - do iLayer=1,nSoil - dExfiltrate_dVolLiq(iLayer) = dBaseflow_dVolLiq(iLayer,iLayer)*logF + dLogFunc_dLiq(iLayer)*qbTotal - end do ! looping through soil layers - dBaseflow_dVolLiq(1,1:nSoil) = dBaseflow_dVolLiq(1,1:nSoil) - dExfiltrate_dVolLiq(1:nSoil) - end if - - ! end association to data in structures - end associate - - end subroutine computeBaseflow - +! ************************************************************************************************ +! public subroutine groundwatr: compute the groundwater sink term in Richards' equation +! ************************************************************************************************ +! +! Method +! ------ +! +! Here we assume that water avaialble for shallow groundwater flow includes is all water above +! "field capacity" below the depth zCrit, where zCrit is defined as the lowest point in the soil +! profile where the volumetric liquid water content is less than field capacity. +! +! We further assume that transmssivity (m2 s-1) for each layer is defined asuming that the water +! available for saturated flow is located at the bottom of the soil profile. Specifically: +! trTotal(iLayer) = tran0*(zActive(iLayer)/soilDepth)**zScale_TOPMODEL +! trSoil(iLayer) = trTotal(iLayer) - trTotal(iLayer+1) +! where zActive(iLayer) is the effective water table thickness for all layers up to and including +! the current layer (working from the bottom to the top). +! +! The outflow from each layer is then (m3 s-1) +! mLayerOutflow(iLayer) = trSoil(iLayer)*tan_slope*contourLength +! where contourLength is the width of a hillslope (m) parallel to a stream +! +! ************************************************************************************************ +subroutine groundwatr(& + + ! input: model control + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + getSatDepth, & ! intent(in): logical flag to compute index of the lowest saturated layer + + ! input: state and diagnostic variables + mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) + mLayerMatricHeadLiq, & ! intent(in): liquid water matric potential (m) + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water (-) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice (-) + + ! input/output: data structures + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + + ! output: baseflow + ixSaturation, & ! intent(inout) index of lowest saturated layer (NOTE: only computed on the first iteration) + mLayerBaseflow, & ! intent(out): baseflow from each soil layer (m s-1) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + + ! output: error control + err,message) ! intent(out): error control + ! --------------------------------------------------------------------------------------- + ! utility modules + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water as a function of matric head + USE soil_utils_module,only:hydCond_psi ! compute hydraulic conductivity as a function of matric head + implicit none + ! --------------------------------------------------------------------------------------- + ! * dummy variables + ! --------------------------------------------------------------------------------------- + ! input: model control + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + logical(lgt),intent(in) :: getSatDepth ! logical flag to compute index of the lowest saturated layer + ! input: state and diagnostic variables + real(rkind),intent(in) :: mLayerdTheta_dPsi(:) ! derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) + real(rkind),intent(in) :: mLayerMatricHeadLiq(:) ! matric head in each layer at the current iteration (m) + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid water (-) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice (-) + ! input/output: data structures + type(var_d),intent(in) :: attr_data ! spatial attributes + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + ! output: baseflow + integer(i4b),intent(inout) :: ixSaturation ! index of lowest saturated layer (NOTE: only computed on the first iteration) + real(rkind),intent(out) :: mLayerBaseflow(:) ! baseflow from each soil layer (m s-1) + real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! --------------------------------------------------------------------------------------- + ! * local variables + ! --------------------------------------------------------------------------------------- + ! general local variables + integer(i4b) :: iLayer ! index of soil layer + real(rkind),dimension(nSoil,nSoil) :: dBaseflow_dVolLiq ! derivative in the baseflow flux w.r.t. volumetric liquid water content (m s-1) + ! local variables to compute the numerical Jacobian + logical(lgt),parameter :: doNumericalJacobian=.false. ! flag to compute the numerical Jacobian + real(rkind),dimension(nSoil) :: mLayerMatricHeadPerturbed ! perturbed matric head (m) + real(rkind),dimension(nSoil) :: mLayerVolFracLiqPerturbed ! perturbed volumetric fraction of liquid water (-) + real(rkind),dimension(nSoil) :: mLayerBaseflowPerturbed ! perturbed baseflow (m s-1) + real(rkind),dimension(nSoil,nSoil) :: nJac ! numerical Jacobian (s-1) + ! *************************************************************************************** + ! *************************************************************************************** + ! initialize error control + err=0; message='groundwatr/' + ! --------------------------------------------------------------------------------------- + ! --------------------------------------------------------------------------------------- + ! associate variables in data structures + associate(& + + ! input: baseflow parameters + fieldCapacity => mpar_data%var(iLookPARAM%fieldCapacity)%dat(1), & ! intent(in): [dp] field capacity (-) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): [dp] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat, & ! intent(in): [dp] residual volumetric water content (-) + + ! input: van Genuchten soil parametrers + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat, & ! intent(in): [dp] van Genutchen "alpha" parameter (m-1) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat, & ! intent(in): [dp] van Genutchen "n" parameter (-) + vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat, & ! intent(in): [dp] van Genutchen "m" parameter (-) + + ! output: diagnostic variables + scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1), & ! intent(out):[dp] exfiltration from the soil profile (m s-1) + mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat & ! intent(out):[dp(:)] column outflow from each soil layer (m3 s-1) + + ) ! end association to variables in data structures + + ! ************************************************************************************************ + ! (1) compute the "active" portion of the soil profile + ! ************************************************************************************************ + + ! get index of the lowest saturated layer + if(getSatDepth)then ! NOTE: only compute for the first flux call + ixSaturation = nSoil+1 ! unsaturated profile when ixSaturation>nSoil + do iLayer=nSoil,1,-1 ! start at the lowest soil layer and work upwards to the top layer + if(mLayerVolFracLiq(iLayer) > fieldCapacity)then; ixSaturation = iLayer ! index of saturated layer -- keeps getting over-written as move upwards + else; exit; end if ! (only consider saturated layer at the bottom of the soil profile) + end do ! (looping through soil layers) + end if + + ! check for an early return (no layers are "active") + if(ixSaturation > nSoil)then + scalarExfiltration = 0._rkind ! exfiltration from the soil profile (m s-1) + mLayerColumnOutflow(:) = 0._rkind ! column outflow from each soil layer (m3 s-1) + mLayerBaseflow(:) = 0._rkind ! baseflow from each soil layer (m s-1) + dBaseflow_dMatric(:,:) = 0._rkind ! derivative in baseflow w.r.t. matric head (s-1) + return + end if ! if some layers are saturated + + ! ************************************************************************************************ + ! (2) compute the baseflow flux and its derivative w.r.t volumetric liquid water content + ! ************************************************************************************************ + + ! use private subroutine to compute baseflow (for multiple calls for numerical Jacobian) + call computeBaseflow(& + ! input: control and state variables + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + .true., & ! intent(in): .true. if analytical derivatives are desired + ixSaturation, & ! intent(in): index of upper-most "saturated" layer + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water in each soil layer (-) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice in each soil layer (-) + ! input/output: data structures + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output: fluxes and derivatives + mLayerBaseflow, & ! intent(out): baseflow flux in each soil layer (m s-1) + dBaseflow_dVolLiq) ! intent(out): derivative in baseflow w.r.t. volumetric liquid water content (s-1) + + ! use the chain rule to compute the baseflow derivative w.r.t. matric head (s-1) + do iLayer=1,nSoil + dBaseflow_dMatric(1:iLayer,iLayer) = dBaseflow_dVolLiq(1:iLayer,iLayer)*mLayerdTheta_dPsi(iLayer) + if(iLayer prog_data%var(iLookPROG%iLayerHeight)%dat(nLayers), & ! intent(in): [dp] total soil depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers),& ! intent(in): [dp(:)] depth of each soil layer (m) + + ! input: diagnostic variables + surfaceHydCond => flux_data%var(iLookFLUX%mLayerSatHydCondMP)%dat(1), & ! intent(in): [dp] saturated hydraulic conductivity at the surface (m s-1) + mLayerColumnInflow => flux_data%var(iLookFLUX%mLayerColumnInflow)%dat, & ! intent(in): [dp(:)] inflow into each soil layer (m3/s) + + ! input: local attributes + HRUarea => attr_data%var(iLookATTR%HRUarea), & ! intent(in): [dp] HRU area (m2) + tan_slope => attr_data%var(iLookATTR%tan_slope), & ! intent(in): [dp] tan water table slope, taken as tan local ground surface slope (-) + contourLength => attr_data%var(iLookATTR%contourLength), & ! intent(in): [dp] length of contour at downslope edge of HRU (m) + + ! input: baseflow parameters + zScale_TOPMODEL => mpar_data%var(iLookPARAM%zScale_TOPMODEL)%dat(1), & ! intent(in): [dp] TOPMODEL exponent (-) + kAnisotropic => mpar_data%var(iLookPARAM%kAnisotropic)%dat(1), & ! intent(in): [dp] anisotropy factor for lateral hydraulic conductivity (- + fieldCapacity => mpar_data%var(iLookPARAM%fieldCapacity)%dat(1), & ! intent(in): [dp] field capacity (-) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): [dp(:)] soil porosity (-) + + ! output: diagnostic variables + scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1), & ! intent(out):[dp] exfiltration from the soil profile (m s-1) + mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat & ! intent(out):[dp(:)] column outflow from each soil layer (m3 s-1) + + ) ! end association to variables in data structures + + ! *********************************************************************************************************************** + ! (1) compute the baseflow flux in each soil layer + ! *********************************************************************************************************************** + + ! compute the maximum transmissivity + ! NOTE: this can be done as a pre-processing step + tran0 = kAnisotropic*surfaceHydCond*soilDepth/zScale_TOPMODEL ! maximum transmissivity (m2 s-1) + + ! compute the water table thickness (m) and transmissivity in each layer (m2 s-1) + do iLayer=nSoil,ixSaturation,-1 ! loop through "active" soil layers, from lowest to highest + ! define drainable water in each layer (m) + activePorosity = theta_sat(iLayer) - fieldCapacity ! "active" porosity (-) + drainableWater = mLayerDepth(iLayer)*(max(0._rkind,mLayerVolFracLiq(iLayer) - fieldCapacity))/activePorosity + ! compute layer transmissivity + if(iLayer==nSoil)then + zActive(iLayer) = drainableWater ! water table thickness associated with storage in a given layer (m) + trTotal(iLayer) = tran0*(zActive(iLayer)/soilDepth)**zScale_TOPMODEL ! total transmissivity for total depth zActive (m2 s-1) + trSoil(iLayer) = trTotal(iLayer) ! transmissivity of water in a given layer (m2 s-1) + else + zActive(iLayer) = zActive(iLayer+1) + drainableWater + trTotal(iLayer) = tran0*(zActive(iLayer)/soilDepth)**zScale_TOPMODEL + trSoil(iLayer) = trTotal(iLayer) - trTotal(iLayer+1) + end if + end do ! looping through soil layers + + ! set un-used portions of the vectors to zero + if(ixSaturation>1)then + zActive(1:ixSaturation-1) = 0._rkind + trTotal(1:ixSaturation-1) = 0._rkind + trSoil(1:ixSaturation-1) = 0._rkind + end if + + ! compute the outflow from each layer (m3 s-1) + mLayerColumnOutflow(1:nSoil) = trSoil(1:nSoil)*tan_slope*contourLength + + ! compute total column inflow and total column outflow (m s-1) + totalColumnInflow = sum(mLayerColumnInflow(1:nSoil))/HRUarea + totalColumnOutflow = sum(mLayerColumnOutflow(1:nSoil))/HRUarea + + ! compute the available storage (m) + availStorage = sum(mLayerDepth(1:nSoil)*(theta_sat - (mLayerVolFracLiq(1:nSoil)+mLayerVolFracIce(1:nSoil))) ) + + ! compute the smoothing function (-) + if(availStorage < xMinEval)then + ! (compute the logistic function) + expF = exp((availStorage - xCenter)/xWidth) + logF = 1._rkind / (1._rkind + expF) + ! (compute the derivative in the logistic function w.r.t. volumetric liquid water content in each soil layer) + dLogFunc_dLiq(1:nSoil) = mLayerDepth(1:nSoil)*(expF/xWidth)/(1._rkind + expF)**2._rkind + else + logF = 0._rkind + dLogFunc_dLiq(:) = 0._rkind + end if + + ! compute the exfiltartion (m s-1) + if(totalColumnInflow > totalColumnOutflow .and. logF > tiny(1._rkind))then + scalarExfiltration = logF*(totalColumnInflow - totalColumnOutflow) ! m s-1 + else + scalarExfiltration = 0._rkind + end if + + ! compute the baseflow in each layer (m s-1) + mLayerBaseflow(1:nSoil) = (mLayerColumnOutflow(1:nSoil) - mLayerColumnInflow(1:nSoil))/HRUarea + + ! compute the total baseflow + qbTotal = sum(mLayerBaseflow) + + ! add exfiltration to the baseflow flux at the top layer + mLayerBaseflow(1) = mLayerBaseflow(1) + scalarExfiltration + mLayerColumnOutflow(1) = mLayerColumnOutflow(1) + scalarExfiltration*HRUarea + + ! test + if(printFlag)then + xDepth = sum(mLayerDepth(ixSaturation:nSoil)*(mLayerVolFracLiq(ixSaturation:nSoil) - fieldCapacity))/sum(theta_sat(ixSaturation:nSoil) - fieldCapacity) ! "effective" water table thickness (m) + xTran = tran0*(xDepth/soilDepth)**zScale_TOPMODEL ! transmissivity for the entire aquifer (m2 s-1) + xFlow = xTran*tan_slope*contourLength/HRUarea ! total column outflow (m s-1) + print*, 'ixSaturation = ', ixSaturation + write(*,'(a,1x,5(f30.20,1x))') 'tran0, soilDepth = ', tran0, soilDepth + write(*,'(a,1x,5(f30.20,1x))') 'surfaceHydCond, zScale_TOPMODEL = ', surfaceHydCond, zScale_TOPMODEL + write(*,'(a,1x,5(f30.20,1x))') 'xDepth, zActive(ixSaturation) = ', xDepth, zActive(ixSaturation) + write(*,'(a,1x,5(f30.20,1x))') 'xTran, trTotal(ixSaturation) = ', xTran, trTotal(ixSaturation) + write(*,'(a,1x,5(f30.20,1x))') 'xFlow, totalColumnOutflow = ', xFlow, sum(mLayerColumnOutflow(:))/HRUarea + !pause 'check groundwater' + end if + + ! *********************************************************************************************************************** + ! (2) compute the derivative in the baseflow flux w.r.t. volumetric liquid water content (m s-1) + ! *********************************************************************************************************************** + + ! initialize the derivative matrix + dBaseflow_dVolLiq(:,:) = 0._rkind + + ! check if derivatives are actually required + if(.not.derivDesired) return + + ! compute ratio of hillslope width to hillslope area (m m-2) + length2area = tan_slope*contourLength/HRUarea + + ! compute the ratio of layer depth to maximum water holding capacity (-) + depth2capacity(1:nSoil) = mLayerDepth(1:nSoil)/sum( (theta_sat(1:nSoil) - fieldCapacity)*mLayerDepth(1:nSoil) ) + + ! compute the change in dimensionless flux w.r.t. change in dimensionless storage (-) + dXdS(1:nSoil) = zScale_TOPMODEL*(zActive(1:nSoil)/SoilDepth)**(zScale_TOPMODEL - 1._rkind) + + ! loop through soil layers + do iLayer=1,nSoil + ! compute diagonal terms (s-1) + dBaseflow_dVolLiq(iLayer,iLayer) = tran0*dXdS(iLayer)*depth2capacity(iLayer)*length2area + ! compute off-diagonal terms + do jLayer=iLayer+1,nSoil ! (only dependent on layers below) + dBaseflow_dVolLiq(iLayer,jLayer) = tran0*(dXdS(iLayer) - dXdS(iLayer+1))*depth2capacity(jLayer)*length2area + end do ! looping through soil layers + end do ! looping through soil layers + + ! compute the derivative in the exfiltration flux w.r.t. volumetric liquid water content (m s-1) + if(qbTotal < 0._rkind)then + do iLayer=1,nSoil + dExfiltrate_dVolLiq(iLayer) = dBaseflow_dVolLiq(iLayer,iLayer)*logF + dLogFunc_dLiq(iLayer)*qbTotal + end do ! looping through soil layers + dBaseflow_dVolLiq(1,1:nSoil) = dBaseflow_dVolLiq(1,1:nSoil) - dExfiltrate_dVolLiq(1:nSoil) + end if + + ! end association to data in structures + end associate + +end subroutine computeBaseflow end module groundwatr_module From 7839df3bd2fd97e11f917764da863b401265a53e Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 27 Sep 2022 20:25:44 +0000 Subject: [PATCH 0376/1472] fixed indentation --- build/source/engine/bigAquifer.f90 | 211 ++++++++++++++--------------- 1 file changed, 103 insertions(+), 108 deletions(-) diff --git a/build/source/engine/bigAquifer.f90 b/build/source/engine/bigAquifer.f90 index 729c5ed64..c4ed4ca4b 100644 --- a/build/source/engine/bigAquifer.f90 +++ b/build/source/engine/bigAquifer.f90 @@ -40,113 +40,108 @@ module bigAquifer_module contains - ! *************************************************************************************************************** - ! public subroutine bigAquifer: compute aquifer water fluxes and their derivatives - ! *************************************************************************************************************** - subroutine bigAquifer(& - ! input: state variables and fluxes - scalarAquiferStorageTrial, & ! intent(in): trial value of aquifer storage (m) - scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) - scalarSoilDrainage, & ! intent(in): soil drainage (m s-1) - ! input: pre-computed derivatives - dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - ! input: diagnostic variables and parameters - mpar_data, & ! intent(in): model parameter structure - diag_data, & ! intent(in): diagnostic variable structure - ! output: fluxes - scalarAquiferTranspire, & ! intent(out): transpiration loss from the aquifer (m s-1) - scalarAquiferRecharge, & ! intent(out): recharge to the aquifer (m s-1) - scalarAquiferBaseflow, & ! intent(out): total baseflow from the aquifer (m s-1) - dBaseflow_dAquifer, & ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) - ! output: derivatives in transpiration w.r.t. canopy state variables - dAquiferTrans_dTCanair, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature - dAquiferTrans_dTCanopy, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy temperature - dAquiferTrans_dTGround, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. ground temperature - dAquiferTrans_dCanWat, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy total water - ! output: error control - err,message) ! intent(out): error control - ! named variables - USE var_lookup,only:iLookDIAG ! named variables for structure elements - USE var_lookup,only:iLookPARAM ! named variables for structure elements - ! data types - USE data_types,only:var_dlength ! x%var(:)%dat (rkind) - ! ------------------------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input: state variables, fluxes, and parameters - real(rkind),intent(in) :: scalarAquiferStorageTrial ! trial value of aquifer storage (m) - real(rkind),intent(in) :: scalarCanopyTranspiration ! canopy transpiration (kg m-2 s-1) - real(rkind),intent(in) :: scalarSoilDrainage ! soil drainage (m s-1) - ! input: pre-computed derivatves - real(rkind),intent(in) :: dCanopyTrans_dCanWat ! derivative in canopy transpiration w.r.t. canopy total water content (s-1) - real(rkind),intent(in) :: dCanopyTrans_dTCanair ! derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - real(rkind),intent(in) :: dCanopyTrans_dTCanopy ! derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - real(rkind),intent(in) :: dCanopyTrans_dTGround ! derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - ! input: diagnostic variables and parameters - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - ! output: fluxes - real(rkind),intent(out) :: scalarAquiferTranspire ! transpiration loss from the aquifer (m s-1) - real(rkind),intent(out) :: scalarAquiferRecharge ! recharge to the aquifer (m s-1) - real(rkind),intent(out) :: scalarAquiferBaseflow ! total baseflow from the aquifer (m s-1) - real(rkind),intent(out) :: dBaseflow_dAquifer ! change in baseflow flux w.r.t. aquifer storage (s-1) - ! output: derivatives in transpiration w.r.t. canopy state variables - real(rkind),intent(inout) :: dAquiferTrans_dTCanair ! derivatives in the aquifer transpiration flux w.r.t. canopy air temperature - real(rkind),intent(inout) :: dAquiferTrans_dTCanopy ! derivatives in the aquifer transpiration flux w.r.t. canopy temperature - real(rkind),intent(inout) :: dAquiferTrans_dTGround ! derivatives in the aquifer transpiration flux w.r.t. ground temperature - real(rkind),intent(inout) :: dAquiferTrans_dCanWat ! derivatives in the aquifer transpiration flux w.r.t. canopy total water - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ----------------------------------------------------------------------------------------------------------------------------------------------------- - ! local variables - real(rkind) :: aquiferTranspireFrac ! fraction of total transpiration that comes from the aquifer (-) - real(rkind) :: xTemp ! temporary variable (-) - ! ------------------------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='bigAquifer/' - - ! make association between local variables and the information in the data structures - associate(& - ! model diagnostic variables: contribution of the aquifer to transpiration - scalarTranspireLim => diag_data%var(iLookDIAG%scalarTranspireLim)%dat(1), & ! intent(in): [dp] weighted average of the transpiration limiting factor (-) - scalarAquiferRootFrac => diag_data%var(iLookDIAG%scalarAquiferRootFrac)%dat(1), & ! intent(in): [dp] fraction of roots below the lowest soil layer (-) - scalarTranspireLimAqfr => diag_data%var(iLookDIAG%scalarTranspireLimAqfr)%dat(1), & ! intent(in): [dp] transpiration limiting factor for the aquifer (-) - ! model parameters: baseflow flux - aquiferBaseflowRate => mpar_data%var(iLookPARAM%aquiferBaseflowRate)%dat(1), & ! intent(in): [dp] tbaseflow rate when aquiferStorage = aquiferScaleFactor (m s-1) - aquiferScaleFactor => mpar_data%var(iLookPARAM%aquiferScaleFactor)%dat(1), & ! intent(in): [dp] scaling factor for aquifer storage in the big bucket (m) - aquiferBaseflowExp => mpar_data%var(iLookPARAM%aquiferBaseflowExp)%dat(1) & ! intent(in): [dp] baseflow exponent (-) - ) ! associating local variables with the information in the data structures - - ! compute aquifer transpiration (m s-1) - aquiferTranspireFrac = scalarAquiferRootFrac*scalarTranspireLimAqfr/scalarTranspireLim ! fraction of total transpiration that comes from the aquifer (-) - scalarAquiferTranspire = aquiferTranspireFrac*scalarCanopyTranspiration/iden_water ! aquifer transpiration (kg m-2 s-1 --> m s-1) - ! derivatives in transpiration w.r.t. canopy state variables - dAquiferTrans_dCanWat = aquiferTranspireFrac*dCanopyTrans_dCanWat /iden_water - dAquiferTrans_dTCanair = aquiferTranspireFrac*dCanopyTrans_dTCanair/iden_water - dAquiferTrans_dTCanopy = aquiferTranspireFrac*dCanopyTrans_dTCanopy/iden_water - dAquiferTrans_dTGround = aquiferTranspireFrac*dCanopyTrans_dTGround/iden_water - - ! compute aquifer recharge (transfer variables -- included for generality for basin-wide aquifer) - scalarAquiferRecharge = scalarSoilDrainage ! m s-1 - - ! compute the aquifer baseflow (m s-1) - xTemp = scalarAquiferStorageTrial/aquiferScaleFactor - scalarAquiferBaseflow = aquiferBaseflowRate*(xTemp**aquiferBaseflowExp) - - ! compute the derivative in the net aquifer flux - dBaseflow_dAquifer = -(aquiferBaseflowExp*aquiferBaseflowRate*(xTemp**(aquiferBaseflowExp - 1._rkind)))/aquiferScaleFactor - - ! end association to data in structures - end associate - - end subroutine bigAquifer - - - ! ******************************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************************* - +! *************************************************************************************************************** +! public subroutine bigAquifer: compute aquifer water fluxes and their derivatives +! *************************************************************************************************************** +subroutine bigAquifer(& + ! input: state variables and fluxes + scalarAquiferStorageTrial, & ! intent(in): trial value of aquifer storage (m) + scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) + scalarSoilDrainage, & ! intent(in): soil drainage (m s-1) + ! input: pre-computed derivatives + dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + ! input: diagnostic variables and parameters + mpar_data, & ! intent(in): model parameter structure + diag_data, & ! intent(in): diagnostic variable structure + ! output: fluxes + scalarAquiferTranspire, & ! intent(out): transpiration loss from the aquifer (m s-1) + scalarAquiferRecharge, & ! intent(out): recharge to the aquifer (m s-1) + scalarAquiferBaseflow, & ! intent(out): total baseflow from the aquifer (m s-1) + dBaseflow_dAquifer, & ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) + ! output: derivatives in transpiration w.r.t. canopy state variables + dAquiferTrans_dTCanair, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + dAquiferTrans_dTCanopy, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy temperature + dAquiferTrans_dTGround, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. ground temperature + dAquiferTrans_dCanWat, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy total water + ! output: error control + err,message) ! intent(out): error control + ! named variables + USE var_lookup,only:iLookDIAG ! named variables for structure elements + USE var_lookup,only:iLookPARAM ! named variables for structure elements + ! data types + USE data_types,only:var_dlength ! x%var(:)%dat (rkind) + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input: state variables, fluxes, and parameters + real(rkind),intent(in) :: scalarAquiferStorageTrial ! trial value of aquifer storage (m) + real(rkind),intent(in) :: scalarCanopyTranspiration ! canopy transpiration (kg m-2 s-1) + real(rkind),intent(in) :: scalarSoilDrainage ! soil drainage (m s-1) + ! input: pre-computed derivatves + real(rkind),intent(in) :: dCanopyTrans_dCanWat ! derivative in canopy transpiration w.r.t. canopy total water content (s-1) + real(rkind),intent(in) :: dCanopyTrans_dTCanair ! derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(rkind),intent(in) :: dCanopyTrans_dTCanopy ! derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(rkind),intent(in) :: dCanopyTrans_dTGround ! derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + ! input: diagnostic variables and parameters + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + ! output: fluxes + real(rkind),intent(out) :: scalarAquiferTranspire ! transpiration loss from the aquifer (m s-1) + real(rkind),intent(out) :: scalarAquiferRecharge ! recharge to the aquifer (m s-1) + real(rkind),intent(out) :: scalarAquiferBaseflow ! total baseflow from the aquifer (m s-1) + real(rkind),intent(out) :: dBaseflow_dAquifer ! change in baseflow flux w.r.t. aquifer storage (s-1) + ! output: derivatives in transpiration w.r.t. canopy state variables + real(rkind),intent(inout) :: dAquiferTrans_dTCanair ! derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + real(rkind),intent(inout) :: dAquiferTrans_dTCanopy ! derivatives in the aquifer transpiration flux w.r.t. canopy temperature + real(rkind),intent(inout) :: dAquiferTrans_dTGround ! derivatives in the aquifer transpiration flux w.r.t. ground temperature + real(rkind),intent(inout) :: dAquiferTrans_dCanWat ! derivatives in the aquifer transpiration flux w.r.t. canopy total water + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ----------------------------------------------------------------------------------------------------------------------------------------------------- + ! local variables + real(rkind) :: aquiferTranspireFrac ! fraction of total transpiration that comes from the aquifer (-) + real(rkind) :: xTemp ! temporary variable (-) + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='bigAquifer/' + + ! make association between local variables and the information in the data structures + associate(& + ! model diagnostic variables: contribution of the aquifer to transpiration + scalarTranspireLim => diag_data%var(iLookDIAG%scalarTranspireLim)%dat(1), & ! intent(in): [dp] weighted average of the transpiration limiting factor (-) + scalarAquiferRootFrac => diag_data%var(iLookDIAG%scalarAquiferRootFrac)%dat(1), & ! intent(in): [dp] fraction of roots below the lowest soil layer (-) + scalarTranspireLimAqfr => diag_data%var(iLookDIAG%scalarTranspireLimAqfr)%dat(1), & ! intent(in): [dp] transpiration limiting factor for the aquifer (-) + ! model parameters: baseflow flux + aquiferBaseflowRate => mpar_data%var(iLookPARAM%aquiferBaseflowRate)%dat(1), & ! intent(in): [dp] tbaseflow rate when aquiferStorage = aquiferScaleFactor (m s-1) + aquiferScaleFactor => mpar_data%var(iLookPARAM%aquiferScaleFactor)%dat(1), & ! intent(in): [dp] scaling factor for aquifer storage in the big bucket (m) + aquiferBaseflowExp => mpar_data%var(iLookPARAM%aquiferBaseflowExp)%dat(1) & ! intent(in): [dp] baseflow exponent (-) + ) ! associating local variables with the information in the data structures + + ! compute aquifer transpiration (m s-1) + aquiferTranspireFrac = scalarAquiferRootFrac*scalarTranspireLimAqfr/scalarTranspireLim ! fraction of total transpiration that comes from the aquifer (-) + scalarAquiferTranspire = aquiferTranspireFrac*scalarCanopyTranspiration/iden_water ! aquifer transpiration (kg m-2 s-1 --> m s-1) + ! derivatives in transpiration w.r.t. canopy state variables + dAquiferTrans_dCanWat = aquiferTranspireFrac*dCanopyTrans_dCanWat /iden_water + dAquiferTrans_dTCanair = aquiferTranspireFrac*dCanopyTrans_dTCanair/iden_water + dAquiferTrans_dTCanopy = aquiferTranspireFrac*dCanopyTrans_dTCanopy/iden_water + dAquiferTrans_dTGround = aquiferTranspireFrac*dCanopyTrans_dTGround/iden_water + + ! compute aquifer recharge (transfer variables -- included for generality for basin-wide aquifer) + scalarAquiferRecharge = scalarSoilDrainage ! m s-1 + + ! compute the aquifer baseflow (m s-1) + xTemp = scalarAquiferStorageTrial/aquiferScaleFactor + scalarAquiferBaseflow = aquiferBaseflowRate*(xTemp**aquiferBaseflowExp) + + ! compute the derivative in the net aquifer flux + dBaseflow_dAquifer = -(aquiferBaseflowExp*aquiferBaseflowRate*(xTemp**(aquiferBaseflowExp - 1._rkind)))/aquiferScaleFactor + + ! end association to data in structures + end associate + +end subroutine bigAquifer end module bigAquifer_module From 39e717b377113044f088548e781984f60029dba8 Mon Sep 17 00:00:00 2001 From: Kyle Date: Wed, 28 Sep 2022 15:57:59 +0000 Subject: [PATCH 0377/1472] adjusted mDecisions File --- build/source/engine/mDecisions.f90 | 1158 ++++++++++++++-------------- 1 file changed, 560 insertions(+), 598 deletions(-) diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index c9b5f1e8c..72258c04f 100644 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -153,611 +153,573 @@ module mDecisions_module contains - ! ************************************************************************************************ - ! public subroutine mDecisions: save model decisions as named integers - ! ************************************************************************************************ - subroutine mDecisions(err,message) - ! model time structures - USE multiconst,only:secprday ! number of seconds in a day - USE var_lookup,only:iLookTIME ! named variables that identify indices in the time structures - USE globalData,only:refTime,refJulday ! reference time - USE globalData,only:oldTime ! time from the previous time step - USE globalData,only:startTime,finshTime ! start/end time of simulation - USE globalData,only:dJulianStart ! julian day of start time of simulation - USE globalData,only:dJulianFinsh ! julian day of end time of simulation - USE globalData,only:data_step ! length of data step (s) - USE globalData,only:numtim ! number of time steps in the simulation - ! model decision structures - USE globaldata,only:model_decisions ! model decision structure - USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure - ! forcing metadata - USE globalData,only:forc_meta ! metadata structures - USE var_lookup,only:iLookFORCE ! named variables to define structure elements - ! Noah-MP decision structures - USE noahmp_globals,only:DVEG ! decision for dynamic vegetation - USE noahmp_globals,only:OPT_RAD ! decision for canopy radiation - USE noahmp_globals,only:OPT_ALB ! decision for snow albedo - ! time utility programs - USE time_utils_module,only:extractTime ! extract time info from units string - USE time_utils_module,only:compjulday ! compute the julian day - USE time_utils_module,only:fracDay ! compute fractional day - USE summaFileManager,only: SIM_START_TM, SIM_END_TM ! time info from control file module - - implicit none - ! define output - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! define local variables - character(len=256) :: cmessage ! error message for downwind routine - real(rkind) :: dsec,dsec_tz ! second - ! initialize error control - err=0; message='mDecisions/' - - ! ------------------------------------------------------------------------------------------------- - ! ------------------------------------------------------------------------------------------------- - - ! read information from model decisions file, and populate model decisions structure - call readoption(err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - - ! ------------------------------------------------------------------------------------------------- - - ! put reference time information into the time structures - call extractTime(forc_meta(iLookFORCE%time)%varunit, & ! date-time string - refTime%var(iLookTIME%iyyy), & ! year - refTime%var(iLookTIME%im), & ! month - refTime%var(iLookTIME%id), & ! day - refTime%var(iLookTIME%ih), & ! hour - refTime%var(iLookTIME%imin), & ! minute - dsec, & ! second - refTime%var(iLookTIME%ih_tz), & ! time zone hour - refTime%var(iLookTIME%imin_tz), & ! time zone minute - dsec_tz, & ! time zone seconds - err,cmessage) ! error control - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - - ! compute the julian date (fraction of day) for the reference time - call compjulday(& - refTime%var(iLookTIME%iyyy), & ! year - refTime%var(iLookTIME%im), & ! month - refTime%var(iLookTIME%id), & ! day - refTime%var(iLookTIME%ih), & ! hour - refTime%var(iLookTIME%imin), & ! minute - 0._rkind, & ! second - refJulday, & ! julian date for the start of the simulation - err, cmessage) ! error control - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - - ! put simulation start time information into the time structures - call extractTime(trim(SIM_START_TM), & ! date-time string - startTime%var(iLookTIME%iyyy), & ! year - startTime%var(iLookTIME%im), & ! month - startTime%var(iLookTIME%id), & ! day - startTime%var(iLookTIME%ih), & ! hour - startTime%var(iLookTIME%imin), & ! minute - dsec, & ! second - startTime%var(iLookTIME%ih_tz), & ! time zone hour - startTime%var(iLookTIME%imin_tz), & ! time zone minnute - dsec_tz, & ! time zone seconds - err,cmessage) ! error control - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - - ! compute the julian date (fraction of day) for the start of the simulation - call compjulday(& - startTime%var(iLookTIME%iyyy), & ! year - startTime%var(iLookTIME%im), & ! month - startTime%var(iLookTIME%id), & ! day - startTime%var(iLookTIME%ih), & ! hour - startTime%var(iLookTIME%imin), & ! minute - 0._rkind, & ! second - dJulianStart, & ! julian date for the start of the simulation - err, cmessage) ! error control - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - - ! put simulation end time information into the time structures - call extractTime(trim(SIM_END_TM), & ! date-time string +! ************************************************************************************************ +! public subroutine mDecisions: save model decisions as named integers +! ************************************************************************************************ +subroutine mDecisions(err,message) + ! model time structures + USE multiconst,only:secprday ! number of seconds in a day + USE var_lookup,only:iLookTIME ! named variables that identify indices in the time structures + USE globalData,only:refTime,refJulday ! reference time + USE globalData,only:oldTime ! time from the previous time step + USE globalData,only:startTime,finshTime ! start/end time of simulation + USE globalData,only:dJulianStart ! julian day of start time of simulation + USE globalData,only:dJulianFinsh ! julian day of end time of simulation + USE globalData,only:data_step ! length of data step (s) + USE globalData,only:numtim ! number of time steps in the simulation + ! model decision structures + USE globaldata,only:model_decisions ! model decision structure + USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure + ! forcing metadata + USE globalData,only:forc_meta ! metadata structures + USE var_lookup,only:iLookFORCE ! named variables to define structure elements + ! Noah-MP decision structures + USE noahmp_globals,only:DVEG ! decision for dynamic vegetation + USE noahmp_globals,only:OPT_RAD ! decision for canopy radiation + USE noahmp_globals,only:OPT_ALB ! decision for snow albedo + ! time utility programs + USE time_utils_module,only:extractTime ! extract time info from units string + USE time_utils_module,only:compjulday ! compute the julian day + USE time_utils_module,only:fracDay ! compute fractional day + USE summaFileManager,only: SIM_START_TM, SIM_END_TM ! time info from control file module + + implicit none + ! define output + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! define local variables + character(len=256) :: cmessage ! error message for downwind routine + real(rkind) :: dsec,dsec_tz ! second + ! initialize error control + err=0; message='mDecisions/' + + ! read information from model decisions file, and populate model decisions structure + call readoption(err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + + ! put reference time information into the time structures + call extractTime(forc_meta(iLookFORCE%time)%varunit, & ! date-time string + refTime%var(iLookTIME%iyyy), & ! year + refTime%var(iLookTIME%im), & ! month + refTime%var(iLookTIME%id), & ! day + refTime%var(iLookTIME%ih), & ! hour + refTime%var(iLookTIME%imin), & ! minute + dsec, & ! second + refTime%var(iLookTIME%ih_tz), & ! time zone hour + refTime%var(iLookTIME%imin_tz), & ! time zone minute + dsec_tz, & ! time zone seconds + err,cmessage) ! error control + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + ! compute the julian date (fraction of day) for the reference time + call compjulday(& + refTime%var(iLookTIME%iyyy), & ! year + refTime%var(iLookTIME%im), & ! month + refTime%var(iLookTIME%id), & ! day + refTime%var(iLookTIME%ih), & ! hour + refTime%var(iLookTIME%imin), & ! minute + 0._rkind, & ! second + refJulday, & ! julian date for the start of the simulation + err, cmessage) ! error control + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + ! put simulation start time information into the time structures + call extractTime(trim(SIM_START_TM), & ! date-time string + startTime%var(iLookTIME%iyyy), & ! year + startTime%var(iLookTIME%im), & ! month + startTime%var(iLookTIME%id), & ! day + startTime%var(iLookTIME%ih), & ! hour + startTime%var(iLookTIME%imin), & ! minute + dsec, & ! second + startTime%var(iLookTIME%ih_tz), & ! time zone hour + startTime%var(iLookTIME%imin_tz), & ! time zone minnute + dsec_tz, & ! time zone seconds + err,cmessage) ! error control + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + ! compute the julian date (fraction of day) for the start of the simulation + call compjulday(& + startTime%var(iLookTIME%iyyy), & ! year + startTime%var(iLookTIME%im), & ! month + startTime%var(iLookTIME%id), & ! day + startTime%var(iLookTIME%ih), & ! hour + startTime%var(iLookTIME%imin), & ! minute + 0._rkind, & ! second + dJulianStart, & ! julian date for the start of the simulation + err, cmessage) ! error control + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + ! put simulation end time information into the time structures + call extractTime(trim(SIM_END_TM), & ! date-time string + finshTime%var(iLookTIME%iyyy), & ! year + finshTime%var(iLookTIME%im), & ! month + finshTime%var(iLookTIME%id), & ! day + finshTime%var(iLookTIME%ih), & ! hour + finshTime%var(iLookTIME%imin), & ! minute + dsec, & ! second + finshTime%var(iLookTIME%ih_tz), & ! time zone hour + finshTime%var(iLookTIME%imin_tz), & ! time zone minnute + dsec_tz, & ! time zone seconds + err,cmessage) ! error control + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + ! compute the julian date (fraction of day) for the end of the simulation + call compjulday(& finshTime%var(iLookTIME%iyyy), & ! year finshTime%var(iLookTIME%im), & ! month finshTime%var(iLookTIME%id), & ! day finshTime%var(iLookTIME%ih), & ! hour finshTime%var(iLookTIME%imin), & ! minute - dsec, & ! second - finshTime%var(iLookTIME%ih_tz), & ! time zone hour - finshTime%var(iLookTIME%imin_tz), & ! time zone minnute - dsec_tz, & ! time zone seconds - err,cmessage) ! error control - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - - ! compute the julian date (fraction of day) for the end of the simulation - call compjulday(& - finshTime%var(iLookTIME%iyyy), & ! year - finshTime%var(iLookTIME%im), & ! month - finshTime%var(iLookTIME%id), & ! day - finshTime%var(iLookTIME%ih), & ! hour - finshTime%var(iLookTIME%imin), & ! minute - 0._rkind, & ! second - dJulianFinsh, & ! julian date for the end of the simulation - err, cmessage) ! error control - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - - ! check start and finish time - write(*,'(a,i4,1x,4(i2,1x))') 'startTime: iyyy, im, id, ih, imin = ', startTime%var(1:5) - write(*,'(a,i4,1x,4(i2,1x))') 'finshTime: iyyy, im, id, ih, imin = ', finshTime%var(1:5) - - ! check that simulation end time is > start time - if(dJulianFinsh < dJulianStart)then; err=20; message=trim(message)//'end time of simulation occurs before start time'; return; end if - - ! initialize the old time vector (time from the previous time step) - oldTime%var(:) = startTime%var(:) - - ! compute the number of time steps - numtim = nint( (dJulianFinsh - dJulianStart)*secprday/data_step ) + 1 - write(*,'(a,1x,i10)') 'number of time steps = ', numtim - - ! ------------------------------------------------------------------------------------------------- - - ! set Noah-MP options - DVEG=3 ! option for dynamic vegetation - OPT_RAD=3 ! option for canopy radiation - OPT_ALB=2 ! option for snow albedo - - ! set zero option for thee category tables - ! NOTE: we want to keep track of these decisions, but not used in the physics routines - model_decisions(iLookDECISIONS%soilCatTbl)%iDecision = 0 - model_decisions(iLookDECISIONS%vegeParTbl)%iDecision = 0 - - ! identify the choice of function for the soil moisture control on stomatal resistance - select case(trim(model_decisions(iLookDECISIONS%soilStress)%cDecision)) - case('NoahType'); model_decisions(iLookDECISIONS%soilStress)%iDecision = NoahType ! thresholded linear function of volumetric liquid water content - case('CLM_Type'); model_decisions(iLookDECISIONS%soilStress)%iDecision = CLM_Type ! thresholded linear function of matric head - case('SiB_Type'); model_decisions(iLookDECISIONS%soilStress)%iDecision = SiB_Type ! exponential of the log of matric head - case default - err=10; message=trim(message)//"unknown soil moisture function [option="//trim(model_decisions(iLookDECISIONS%soilStress)%cDecision)//"]"; return - end select - - ! identify the choice of function for stomatal resistance - select case(trim(model_decisions(iLookDECISIONS%stomResist)%cDecision)) - case('BallBerry' ); model_decisions(iLookDECISIONS%stomResist)%iDecision = BallBerry ! Ball-Berry - case('Jarvis' ); model_decisions(iLookDECISIONS%stomResist)%iDecision = Jarvis ! Jarvis - case('simpleResistance' ); model_decisions(iLookDECISIONS%stomResist)%iDecision = simpleResistance ! simple resistance formulation - case('BallBerryFlex' ); model_decisions(iLookDECISIONS%stomResist)%iDecision = BallBerryFlex ! flexible Ball-Berry scheme - case('BallBerryTest' ); model_decisions(iLookDECISIONS%stomResist)%iDecision = BallBerryTest ! flexible Ball-Berry scheme (testing) - case default - err=10; message=trim(message)//"unknown stomatal resistance function [option="//trim(model_decisions(iLookDECISIONS%stomResist)%cDecision)//"]"; return - end select - - ! identify the leaf temperature controls on photosynthesis + stomatal resistance - if(model_decisions(iLookDECISIONS%stomResist)%iDecision >= BallBerryFlex)then - select case(trim(model_decisions(iLookDECISIONS%bbTempFunc)%cDecision)) - case('q10Func' ); model_decisions(iLookDECISIONS%bbTempFunc)%iDecision = q10Func - case('Arrhenius' ); model_decisions(iLookDECISIONS%bbTempFunc)%iDecision = Arrhenius - case default - err=10; message=trim(message)//"unknown leaf temperature function [option="//trim(model_decisions(iLookDECISIONS%bbTempFunc)%cDecision)//"]"; return - end select - end if - - ! identify the humidity controls on stomatal resistance - if(model_decisions(iLookDECISIONS%stomResist)%iDecision >= BallBerryFlex)then - select case(trim(model_decisions(iLookDECISIONS%bbHumdFunc)%cDecision)) - case('humidLeafSurface' ); model_decisions(iLookDECISIONS%bbHumdFunc)%iDecision = humidLeafSurface - case('scaledHyperbolic' ); model_decisions(iLookDECISIONS%bbHumdFunc)%iDecision = scaledHyperbolic - case default - err=10; message=trim(message)//"unknown humidity function [option="//trim(model_decisions(iLookDECISIONS%bbHumdFunc)%cDecision)//"]"; return - end select - end if - - ! identify functions for electron transport function (dependence of photosynthesis on PAR) - if(model_decisions(iLookDECISIONS%stomResist)%iDecision >= BallBerryFlex)then - select case(trim(model_decisions(iLookDECISIONS%bbElecFunc)%cDecision)) - case('linear' ); model_decisions(iLookDECISIONS%bbElecFunc)%iDecision = linear - case('linearJmax' ); model_decisions(iLookDECISIONS%bbElecFunc)%iDecision = linearJmax - case('quadraticJmax' ); model_decisions(iLookDECISIONS%bbElecFunc)%iDecision = quadraticJmax - case default - err=10; message=trim(message)//"unknown electron transport function [option="//trim(model_decisions(iLookDECISIONS%bbElecFunc)%cDecision)//"]"; return - end select - end if - - ! identify the use of the co2 compensation point in the stomatal conductance calaculations - if(model_decisions(iLookDECISIONS%stomResist)%iDecision >= BallBerryFlex)then - select case(trim(model_decisions(iLookDECISIONS%bbCO2point)%cDecision)) - case('origBWB' ); model_decisions(iLookDECISIONS%bbCO2point)%iDecision = origBWB - case('Leuning' ); model_decisions(iLookDECISIONS%bbCO2point)%iDecision = Leuning - case default - err=10; message=trim(message)//"unknown option for the co2 compensation point [option="//trim(model_decisions(iLookDECISIONS%bbCO2point)%cDecision)//"]"; return - end select - end if - - ! identify the iterative numerical solution method used in the Ball-Berry stomatal resistance parameterization - if(model_decisions(iLookDECISIONS%stomResist)%iDecision >= BallBerryFlex)then - select case(trim(model_decisions(iLookDECISIONS%bbNumerics)%cDecision)) - case('NoahMPsolution' ); model_decisions(iLookDECISIONS%bbNumerics)%iDecision = NoahMPsolution ! the NoahMP solution (and CLM4): fixed point iteration; max 3 iterations - case('newtonRaphson' ); model_decisions(iLookDECISIONS%bbNumerics)%iDecision = newtonRaphson ! full Newton-Raphson iterative solution to convergence - case default - err=10; message=trim(message)//"unknown option for the Ball-Berry numerical solution [option="//trim(model_decisions(iLookDECISIONS%bbNumerics)%cDecision)//"]"; return - end select - end if - - ! identify the controls on carbon assimilation - if(model_decisions(iLookDECISIONS%stomResist)%iDecision >= BallBerryFlex)then - select case(trim(model_decisions(iLookDECISIONS%bbAssimFnc)%cDecision)) - case('colimitation' ); model_decisions(iLookDECISIONS%bbAssimFnc)%iDecision = colimitation ! enable colimitation, as described by Collatz et al. (1991) and Sellers et al. (1996) - case('minFunc' ); model_decisions(iLookDECISIONS%bbAssimFnc)%iDecision = minFunc ! do not enable colimitation: use minimum of the three controls on carbon assimilation - case default - err=10; message=trim(message)//"unknown option for the controls on carbon assimilation [option="//trim(model_decisions(iLookDECISIONS%bbAssimFnc)%cDecision)//"]"; return - end select - end if - - ! identify the scaling of photosynthesis from the leaf to the canopy - if(model_decisions(iLookDECISIONS%stomResist)%iDecision >= BallBerryFlex)then - select case(trim(model_decisions(iLookDECISIONS%bbCanIntg8)%cDecision)) - case('constantScaling' ); model_decisions(iLookDECISIONS%bbCanIntg8)%iDecision = constantScaling ! constant scaling factor - case('laiScaling' ); model_decisions(iLookDECISIONS%bbCanIntg8)%iDecision = laiScaling ! exponential function of LAI (Leuning, Plant Cell Env 1995: "Scaling from..." [eq 9]) - case default - err=10; message=trim(message)//"unknown option for scaling of photosynthesis from the leaf to the canopy [option="//trim(model_decisions(iLookDECISIONS%bbCanIntg8)%cDecision)//"]"; return - end select - end if - - ! identify the numerical method - select case(trim(model_decisions(iLookDECISIONS%num_method)%cDecision)) - case('bEuler' ); model_decisions(iLookDECISIONS%num_method)%iDecision = bEuler ! home-grown backward Euler solution with long time steps - case('itertive' ); model_decisions(iLookDECISIONS%num_method)%iDecision = bEuler ! home-grown backward Euler solution (included for backwards compatibility) - case('sundials' ); model_decisions(iLookDECISIONS%num_method)%iDecision = sundials ! SUNDIALS/IDA solution - case default - err=10; message=trim(message)//"unknown numerical method [option="//trim(model_decisions(iLookDECISIONS%num_method)%cDecision)//"]"; return - end select + 0._rkind, & ! second + dJulianFinsh, & ! julian date for the end of the simulation + err, cmessage) ! error control + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + ! check start and finish time + write(*,'(a,i4,1x,4(i2,1x))') 'startTime: iyyy, im, id, ih, imin = ', startTime%var(1:5) + write(*,'(a,i4,1x,4(i2,1x))') 'finshTime: iyyy, im, id, ih, imin = ', finshTime%var(1:5) + + ! check that simulation end time is > start time + if(dJulianFinsh < dJulianStart)then; err=20; message=trim(message)//'end time of simulation occurs before start time'; return; end if + + ! initialize the old time vector (time from the previous time step) + oldTime%var(:) = startTime%var(:) + + ! compute the number of time steps + numtim = nint( (dJulianFinsh - dJulianStart)*secprday/data_step ) + 1 + write(*,'(a,1x,i10)') 'number of time steps = ', numtim + + + ! set Noah-MP options + DVEG=3 ! option for dynamic vegetation + OPT_RAD=3 ! option for canopy radiation + OPT_ALB=2 ! option for snow albedo + + ! set zero option for thee category tables + ! NOTE: we want to keep track of these decisions, but not used in the physics routines + model_decisions(iLookDECISIONS%soilCatTbl)%iDecision = 0 + model_decisions(iLookDECISIONS%vegeParTbl)%iDecision = 0 + + ! identify the choice of function for the soil moisture control on stomatal resistance + select case(trim(model_decisions(iLookDECISIONS%soilStress)%cDecision)) + case('NoahType'); model_decisions(iLookDECISIONS%soilStress)%iDecision = NoahType ! thresholded linear function of volumetric liquid water content + case('CLM_Type'); model_decisions(iLookDECISIONS%soilStress)%iDecision = CLM_Type ! thresholded linear function of matric head + case('SiB_Type'); model_decisions(iLookDECISIONS%soilStress)%iDecision = SiB_Type ! exponential of the log of matric head + case default + err=10; message=trim(message)//"unknown soil moisture function [option="//trim(model_decisions(iLookDECISIONS%soilStress)%cDecision)//"]"; return + end select - ! how to compute heat capacity in energy equation - select case(trim(model_decisions(iLookDECISIONS%howHeatCap)%cDecision)) - case('enthalpyFD'); model_decisions(iLookDECISIONS%howHeatCap)%iDecision = enthalpyFD ! enthalpyFD - case('closedForm'); model_decisions(iLookDECISIONS%howHeatCap)%iDecision = closedForm ! closedForm - case default - ! TODO: after adding howHeatCap decision in corresponding file we should delete the next line - model_decisions(iLookDECISIONS%howHeatCap)%iDecision = closedForm - ! err=10; message=trim(message)//"unknown Cp computation [option="//trim(model_decisions(iLookDECISIONS%howHeatCap)%cDecision)//"]"; return -end select - - ! identify the method used to calculate flux derivatives - select case(trim(model_decisions(iLookDECISIONS%fDerivMeth)%cDecision)) - case('numericl'); model_decisions(iLookDECISIONS%fDerivMeth)%iDecision = numerical ! numerical - case('analytic'); model_decisions(iLookDECISIONS%fDerivMeth)%iDecision = analytical ! analytical - case default - err=10; message=trim(message)//"unknown method used to calculate flux derivatives [option="//trim(model_decisions(iLookDECISIONS%fDerivMeth)%cDecision)//"]"; return - end select - - ! identify the method used to determine LAI and SAI - select case(trim(model_decisions(iLookDECISIONS%LAI_method)%cDecision)) - case('monTable'); model_decisions(iLookDECISIONS%LAI_method)%iDecision = monthlyTable ! LAI/SAI taken directly from a monthly table for different vegetation classes - case('specified'); model_decisions(iLookDECISIONS%LAI_method)%iDecision = specified ! LAI/SAI computed from green vegetation fraction and winterSAI and summerLAI parameters - case default - err=10; message=trim(message)//"unknown method to determine LAI and SAI [option="//trim(model_decisions(iLookDECISIONS%LAI_method)%cDecision)//"]"; return - end select - - ! identify the canopy interception parameterization - select case(trim(model_decisions(iLookDECISIONS%cIntercept)%cDecision)) - case('notPopulatedYet'); model_decisions(iLookDECISIONS%cIntercept)%iDecision = unDefined - case('sparseCanopy'); model_decisions(iLookDECISIONS%cIntercept)%iDecision = sparseCanopy - case('storageFunc'); model_decisions(iLookDECISIONS%cIntercept)%iDecision = storageFunc - case default - err=10; message=trim(message)//"unknown canopy interception parameterization [option="//trim(model_decisions(iLookDECISIONS%cIntercept)%cDecision)//"]"; return - end select - - ! identify the form of Richards' equation - select case(trim(model_decisions(iLookDECISIONS%f_Richards)%cDecision)) - case('moisture'); model_decisions(iLookDECISIONS%f_Richards)%iDecision = moisture ! moisture-based form - case('mixdform'); model_decisions(iLookDECISIONS%f_Richards)%iDecision = mixdform ! mixed form - case default - err=10; message=trim(message)//"unknown form of Richards' equation [option="//trim(model_decisions(iLookDECISIONS%f_Richards)%cDecision)//"]"; return - end select - - ! identify the groundwater parameterization - select case(trim(model_decisions(iLookDECISIONS%groundwatr)%cDecision)) - case('qTopmodl'); model_decisions(iLookDECISIONS%groundwatr)%iDecision = qbaseTopmodel ! TOPMODEL-ish baseflow parameterization - case('bigBuckt'); model_decisions(iLookDECISIONS%groundwatr)%iDecision = bigBucket ! a big bucket (lumped aquifer model) - case('noXplict'); model_decisions(iLookDECISIONS%groundwatr)%iDecision = noExplicit ! no explicit groundwater parameterization - case default - err=10; message=trim(message)//"unknown groundwater parameterization [option="//trim(model_decisions(iLookDECISIONS%groundwatr)%cDecision)//"]"; return - end select - - ! identify the hydraulic conductivity profile - select case(trim(model_decisions(iLookDECISIONS%hc_profile)%cDecision)) - case('constant'); model_decisions(iLookDECISIONS%hc_profile)%iDecision = constant ! constant hydraulic conductivity with depth - case('pow_prof'); model_decisions(iLookDECISIONS%hc_profile)%iDecision = powerLaw_profile ! power-law profile - case default - err=10; message=trim(message)//"unknown hydraulic conductivity profile [option="//trim(model_decisions(iLookDECISIONS%hc_profile)%cDecision)//"]"; return - end select - - ! identify the upper boundary conditions for thermodynamics - select case(trim(model_decisions(iLookDECISIONS%bcUpprTdyn)%cDecision)) - case('presTemp'); model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision = prescribedTemp ! prescribed temperature - case('nrg_flux'); model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision = energyFlux ! energy flux - case('zeroFlux'); model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision = zeroFlux ! zero flux - case default - err=10; message=trim(message)//"unknown upper boundary conditions for thermodynamics [option="//trim(model_decisions(iLookDECISIONS%bcUpprTdyn)%cDecision)//"]"; return - end select - - ! identify the lower boundary conditions for thermodynamics - select case(trim(model_decisions(iLookDECISIONS%bcLowrTdyn)%cDecision)) - case('presTemp'); model_decisions(iLookDECISIONS%bcLowrTdyn)%iDecision = prescribedTemp ! prescribed temperature - case('zeroFlux'); model_decisions(iLookDECISIONS%bcLowrTdyn)%iDecision = zeroFlux ! zero flux - case default - err=10; message=trim(message)//"unknown lower boundary conditions for thermodynamics [option="//trim(model_decisions(iLookDECISIONS%bcLowrTdyn)%cDecision)//"]"; return - end select - - ! identify the upper boundary conditions for soil hydrology - select case(trim(model_decisions(iLookDECISIONS%bcUpprSoiH)%cDecision)) - case('presHead'); model_decisions(iLookDECISIONS%bcUpprSoiH)%iDecision = prescribedHead ! prescribed head (volumetric liquid water content for mixed form of Richards' eqn) - case('liq_flux'); model_decisions(iLookDECISIONS%bcUpprSoiH)%iDecision = liquidFlux ! liquid water flux - case default - err=10; message=trim(message)//"unknown upper boundary conditions for soil hydrology [option="//trim(model_decisions(iLookDECISIONS%bcUpprSoiH)%cDecision)//"]"; return - end select - - ! identify the lower boundary conditions for soil hydrology - select case(trim(model_decisions(iLookDECISIONS%bcLowrSoiH)%cDecision)) - case('presHead'); model_decisions(iLookDECISIONS%bcLowrSoiH)%iDecision = prescribedHead ! prescribed head (volumetric liquid water content for mixed form of Richards' eqn) - case('bottmPsi'); model_decisions(iLookDECISIONS%bcLowrSoiH)%iDecision = funcBottomHead ! function of matric head in the lower-most layer - case('drainage'); model_decisions(iLookDECISIONS%bcLowrSoiH)%iDecision = freeDrainage ! free drainage - case('zeroFlux'); model_decisions(iLookDECISIONS%bcLowrSoiH)%iDecision = zeroFlux ! zero flux - case default - err=10; message=trim(message)//"unknown lower boundary conditions for soil hydrology [option="//trim(model_decisions(iLookDECISIONS%bcLowrSoiH)%cDecision)//"]"; return - end select - - ! identify the choice of parameterization for vegetation roughness length and displacement height - select case(trim(model_decisions(iLookDECISIONS%veg_traits)%cDecision)) - case('Raupach_BLM1994'); model_decisions(iLookDECISIONS%veg_traits)%iDecision = Raupach_BLM1994 ! Raupach (BLM 1994) "Simplified expressions..." - case('CM_QJRMS1988' ); model_decisions(iLookDECISIONS%veg_traits)%iDecision = CM_QJRMS1988 ! Choudhury and Monteith (QJRMS 1998) "A four layer model for the heat budget..." - case('vegTypeTable' ); model_decisions(iLookDECISIONS%veg_traits)%iDecision = vegTypeTable ! constant parameters dependent on the vegetation type - case default - err=10; message=trim(message)//"unknown parameterization for vegetation roughness length and displacement height [option="//trim(model_decisions(iLookDECISIONS%veg_traits)%cDecision)//"]"; return - end select - - ! identify the choice of parameterization for the rooting profile - ! NOTE: for backwards compatibility select powerLaw if rooting profile is undefined - select case(trim(model_decisions(iLookDECISIONS%rootProfil)%cDecision)) - case('powerLaw','notPopulatedYet'); model_decisions(iLookDECISIONS%rootProfil)%iDecision = powerLaw ! simple power-law rooting profile - case('doubleExp'); model_decisions(iLookDECISIONS%rootProfil)%iDecision = doubleExp ! the double exponential function of Xeng et al. (JHM 2001) - case default - err=10; message=trim(message)//"unknown parameterization for rooting profile [option="//trim(model_decisions(iLookDECISIONS%rootProfil)%cDecision)//"]"; return - end select - - ! identify the choice of parameterization for canopy emissivity - select case(trim(model_decisions(iLookDECISIONS%canopyEmis)%cDecision)) - case('simplExp'); model_decisions(iLookDECISIONS%canopyEmis)%iDecision = simplExp ! simple exponential function - case('difTrans'); model_decisions(iLookDECISIONS%canopyEmis)%iDecision = difTrans ! parameterized as a function of diffuse transmissivity - case default - err=10; message=trim(message)//"unknown parameterization for canopy emissivity [option="//trim(model_decisions(iLookDECISIONS%canopyEmis)%cDecision)//"]"; return - end select - - ! choice of parameterization for snow interception - select case(trim(model_decisions(iLookDECISIONS%snowIncept)%cDecision)) - case('stickySnow'); model_decisions(iLookDECISIONS%snowIncept)%iDecision = stickySnow ! maximum interception capacity an increasing function of temerature - case('lightSnow' ); model_decisions(iLookDECISIONS%snowIncept)%iDecision = lightSnow ! maximum interception capacity an inverse function of new snow density - case default - err=10; message=trim(message)//"unknown option for snow interception capacity[option="//trim(model_decisions(iLookDECISIONS%snowIncept)%cDecision)//"]"; return - end select - - ! identify the choice of wind profile - select case(trim(model_decisions(iLookDECISIONS%windPrfile)%cDecision)) - case('exponential' ); model_decisions(iLookDECISIONS%windPrfile)%iDecision = exponential ! exponential wind profile extends to the surface - case('logBelowCanopy'); model_decisions(iLookDECISIONS%windPrfile)%iDecision = logBelowCanopy ! logarithmic profile below the vegetation canopy - case default - err=10; message=trim(message)//"unknown option for choice of wind profile[option="//trim(model_decisions(iLookDECISIONS%windPrfile)%cDecision)//"]"; return - end select - - ! identify the choice of atmospheric stability function - select case(trim(model_decisions(iLookDECISIONS%astability)%cDecision)) - case('standard'); model_decisions(iLookDECISIONS%astability)%iDecision = standard ! standard MO similarity, a la Anderson (1976) - case('louisinv'); model_decisions(iLookDECISIONS%astability)%iDecision = louisInversePower ! Louis (1979) inverse power function - case('mahrtexp'); model_decisions(iLookDECISIONS%astability)%iDecision = mahrtExponential ! Mahrt (1987) exponential - case default - err=10; message=trim(message)//"unknown stability function [option="//trim(model_decisions(iLookDECISIONS%astability)%cDecision)//"]"; return - end select - - ! choice of canopy shortwave radiation method - select case(trim(model_decisions(iLookDECISIONS%canopySrad)%cDecision)) - case('noah_mp' ); model_decisions(iLookDECISIONS%canopySrad)%iDecision = noah_mp ! full Noah-MP implementation (including albedo) - case('CLM_2stream'); model_decisions(iLookDECISIONS%canopySrad)%iDecision = CLM_2stream ! CLM 2-stream model (see CLM documentation) - case('UEB_2stream'); model_decisions(iLookDECISIONS%canopySrad)%iDecision = UEB_2stream ! UEB 2-stream model (Mahat and Tarboton, WRR 2011) - case('NL_scatter' ); model_decisions(iLookDECISIONS%canopySrad)%iDecision = NL_scatter ! Simplified method Nijssen and Lettenmaier (JGR 1999) - case('BeersLaw' ); model_decisions(iLookDECISIONS%canopySrad)%iDecision = BeersLaw ! Beer's Law (as implemented in VIC) - case default - err=10; message=trim(message)//"unknown canopy radiation method [option="//trim(model_decisions(iLookDECISIONS%canopySrad)%cDecision)//"]"; return - end select - - ! choice of albedo representation - select case(trim(model_decisions(iLookDECISIONS%alb_method)%cDecision)) - case('conDecay'); model_decisions(iLookDECISIONS%alb_method)%iDecision = constantDecay ! constant decay (e.g., VIC, CLASS) - case('varDecay'); model_decisions(iLookDECISIONS%alb_method)%iDecision = variableDecay ! variable decay (e.g., BATS approach, with destructive metamorphism + soot content) - case default - err=10; message=trim(message)//"unknown option for snow albedo [option="//trim(model_decisions(iLookDECISIONS%alb_method)%cDecision)//"]"; return - end select - - ! choice of snow compaction routine - select case(trim(model_decisions(iLookDECISIONS%compaction)%cDecision)) - case('consettl'); model_decisions(iLookDECISIONS%compaction)%iDecision = constantSettlement ! constant settlement rate - case('anderson'); model_decisions(iLookDECISIONS%compaction)%iDecision = andersonEmpirical ! semi-empirical method of Anderson (1976) - case default - err=10; message=trim(message)//"unknown option for snow compaction [option="//trim(model_decisions(iLookDECISIONS%compaction)%cDecision)//"]"; return - end select - - ! choice of method to combine and sub-divide snow layers - select case(trim(model_decisions(iLookDECISIONS%snowLayers)%cDecision)) - case('jrdn1991'); model_decisions(iLookDECISIONS%snowLayers)%iDecision = sameRulesAllLayers ! SNTHERM option: same combination/sub-dividion rules applied to all layers - case('CLM_2010'); model_decisions(iLookDECISIONS%snowLayers)%iDecision = rulesDependLayerIndex ! CLM option: combination/sub-dividion rules depend on layer index - case default - err=10; message=trim(message)//"unknown option for combination/sub-division of snow layers [option="//trim(model_decisions(iLookDECISIONS%snowLayers)%cDecision)//"]"; return - end select - - ! choice of thermal conductivity representation for snow - select case(trim(model_decisions(iLookDECISIONS%thCondSnow)%cDecision)) - case('tyen1965'); model_decisions(iLookDECISIONS%thCondSnow)%iDecision = Yen1965 ! Yen (1965) - case('melr1977'); model_decisions(iLookDECISIONS%thCondSnow)%iDecision = Mellor1977 ! Mellor (1977) - case('jrdn1991'); model_decisions(iLookDECISIONS%thCondSnow)%iDecision = Jordan1991 ! Jordan (1991) - case('smnv2000'); model_decisions(iLookDECISIONS%thCondSnow)%iDecision = Smirnova2000 ! Smirnova et al. (2000) - case default - err=10; message=trim(message)//"unknown option for thermal conductivity of snow [option="//trim(model_decisions(iLookDECISIONS%thCondSnow)%cDecision)//"]"; return - end select - - ! choice of thermal conductivity representation for soil - select case(trim(model_decisions(iLookDECISIONS%thCondSoil)%cDecision)) - case('funcSoilWet'); model_decisions(iLookDECISIONS%thCondSoil)%iDecision = funcSoilWet ! function of soil wetness - case('mixConstit' ); model_decisions(iLookDECISIONS%thCondSoil)%iDecision = mixConstit ! mixture of constituents - case('hanssonVZJ' ); model_decisions(iLookDECISIONS%thCondSoil)%iDecision = hanssonVZJ ! test case for the mizoguchi lab experiment, Hansson et al. VZJ 2004 - case default - err=10; message=trim(message)//"unknown option for thermal conductivity of soil [option="//trim(model_decisions(iLookDECISIONS%thCondSoil)%cDecision)//"]"; return - end select - - ! choice of method for the spatial representation of groundwater - select case(trim(model_decisions(iLookDECISIONS%spatial_gw)%cDecision)) - case('localColumn'); model_decisions(iLookDECISIONS%spatial_gw)%iDecision = localColumn ! separate groundwater in each local soil column - case('singleBasin'); model_decisions(iLookDECISIONS%spatial_gw)%iDecision = singleBasin ! single groundwater store over the entire basin - case default - err=10; message=trim(message)//"unknown option for spatial representation of groundwater [option="//trim(model_decisions(iLookDECISIONS%spatial_gw)%cDecision)//"]"; return - end select - - ! choice of routing method - select case(trim(model_decisions(iLookDECISIONS%subRouting)%cDecision)) - case('timeDlay'); model_decisions(iLookDECISIONS%subRouting)%iDecision = timeDelay ! time-delay histogram - case('qInstant'); model_decisions(iLookDECISIONS%subRouting)%iDecision = qInstant ! instantaneous routing - case default - err=10; message=trim(message)//"unknown option for sub-grid routing [option="//trim(model_decisions(iLookDECISIONS%subRouting)%cDecision)//"]"; return - end select - - ! choice of new snow density - ! NOTE: use hedAndPom as the default, where density method is undefined (not populated yet) - select case(trim(model_decisions(iLookDECISIONS%snowDenNew)%cDecision)) - case('hedAndPom','notPopulatedYet'); model_decisions(iLookDECISIONS%snowDenNew)%iDecision = hedAndPom ! Hedstrom and Pomeroy (1998), expoential increase - case('anderson'); model_decisions(iLookDECISIONS%snowDenNew)%iDecision = anderson ! Anderson 1976 - case('pahaut_76'); model_decisions(iLookDECISIONS%snowDenNew)%iDecision = pahaut_76 ! Pahaut 1976, wind speed dependent (derived from Col de Porte, French Alps) - case('constDens'); model_decisions(iLookDECISIONS%snowDenNew)%iDecision = constDens ! Constant new snow density - case default - err=10; message=trim(message)//"unknown option for new snow density [option="//trim(model_decisions(iLookDECISIONS%snowDenNew)%cDecision)//"]"; return - end select - - ! choice of snow unloading from canopy - select case(trim(model_decisions(iLookDECISIONS%snowUnload)%cDecision)) - case('meltDripUnload','notPopulatedYet'); model_decisions(iLookDECISIONS%snowUnload)%iDecision = meltDripUnload ! Hedstrom and Pomeroy (1998), Storck et al 2002 (snowUnloadingCoeff & ratioDrip2Unloading) - case('windUnload'); model_decisions(iLookDECISIONS%snowUnload)%iDecision = windUnload ! Roesch et al 2001, formulate unloading based on wind and temperature - case default - err=10; message=trim(message)//"unknown option for snow unloading [option="//trim(model_decisions(iLookDECISIONS%snowUnload)%cDecision)//"]"; return - end select - - - ! ----------------------------------------------------------------------------------------------------------------------------------------------- - ! check for consistency among options - ! ----------------------------------------------------------------------------------------------------------------------------------------------- - - ! check there is prescribedHead for soil hydrology when zeroFlux or prescribedTemp for thermodynamics - !select case(model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision) - ! case(prescribedTemp,zeroFlux) - ! if(model_decisions(iLookDECISIONS%bcUpprSoiH)%iDecision /= prescribedHead)then - ! message=trim(message)//'upper boundary condition for soil hydology must be presHead with presTemp and zeroFlux options for thermodynamics' - ! err=20; return - ! end if - !end select - - ! check there is prescribedTemp or zeroFlux for thermodynamics when using prescribedHead for soil hydrology - !select case(model_decisions(iLookDECISIONS%bcUpprSoiH)%iDecision) - ! case(prescribedHead) - ! ! check that upper boundary condition for thermodynamics is presTemp or zeroFlux - ! select case(model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision) - ! case(prescribedTemp,zeroFlux) ! do nothing: this is OK - ! case default - ! message=trim(message)//'upper boundary condition for thermodynamics must be presTemp or zeroFlux with presHead option for soil hydology' - ! err=20; return - ! end select - !end select - - ! check zero flux lower boundary for topmodel baseflow option - select case(model_decisions(iLookDECISIONS%groundwatr)%iDecision) - case(qbaseTopmodel) - if(model_decisions(iLookDECISIONS%bcLowrSoiH)%iDecision /= zeroFlux)then - message=trim(message)//'lower boundary condition for soil hydology must be zeroFlux with qbaseTopmodel option for groundwater' - err=20; return - end if - end select - - ! check power-law profile is selected when using topmodel baseflow option - select case(model_decisions(iLookDECISIONS%groundwatr)%iDecision) - case(qbaseTopmodel) - if(model_decisions(iLookDECISIONS%hc_profile)%iDecision /= powerLaw_profile)then - message=trim(message)//'power-law transmissivity profile must be selected when using topmodel baseflow option' - err=20; return - end if - end select - - ! check bigBucket groundwater option is used when for spatial groundwater is singleBasin - if(model_decisions(iLookDECISIONS%spatial_gw)%iDecision == singleBasin)then - if(model_decisions(iLookDECISIONS%groundwatr)%iDecision /= bigBucket)then - message=trim(message)//'groundwater parameterization must be bigBucket when using singleBasin for spatial_gw' - err=20; return + ! identify the choice of function for stomatal resistance + select case(trim(model_decisions(iLookDECISIONS%stomResist)%cDecision)) + case('BallBerry' ); model_decisions(iLookDECISIONS%stomResist)%iDecision = BallBerry ! Ball-Berry + case('Jarvis' ); model_decisions(iLookDECISIONS%stomResist)%iDecision = Jarvis ! Jarvis + case('simpleResistance' ); model_decisions(iLookDECISIONS%stomResist)%iDecision = simpleResistance ! simple resistance formulation + case('BallBerryFlex' ); model_decisions(iLookDECISIONS%stomResist)%iDecision = BallBerryFlex ! flexible Ball-Berry scheme + case('BallBerryTest' ); model_decisions(iLookDECISIONS%stomResist)%iDecision = BallBerryTest ! flexible Ball-Berry scheme (testing) + case default + err=10; message=trim(message)//"unknown stomatal resistance function [option="//trim(model_decisions(iLookDECISIONS%stomResist)%cDecision)//"]"; return + end select + + ! identify the leaf temperature controls on photosynthesis + stomatal resistance + if(model_decisions(iLookDECISIONS%stomResist)%iDecision >= BallBerryFlex)then + select case(trim(model_decisions(iLookDECISIONS%bbTempFunc)%cDecision)) + case('q10Func' ); model_decisions(iLookDECISIONS%bbTempFunc)%iDecision = q10Func + case('Arrhenius' ); model_decisions(iLookDECISIONS%bbTempFunc)%iDecision = Arrhenius + case default + err=10; message=trim(message)//"unknown leaf temperature function [option="//trim(model_decisions(iLookDECISIONS%bbTempFunc)%cDecision)//"]"; return + end select + end if + + ! identify the humidity controls on stomatal resistance + if(model_decisions(iLookDECISIONS%stomResist)%iDecision >= BallBerryFlex)then + select case(trim(model_decisions(iLookDECISIONS%bbHumdFunc)%cDecision)) + case('humidLeafSurface' ); model_decisions(iLookDECISIONS%bbHumdFunc)%iDecision = humidLeafSurface + case('scaledHyperbolic' ); model_decisions(iLookDECISIONS%bbHumdFunc)%iDecision = scaledHyperbolic + case default + err=10; message=trim(message)//"unknown humidity function [option="//trim(model_decisions(iLookDECISIONS%bbHumdFunc)%cDecision)//"]"; return + end select + end if + + ! identify functions for electron transport function (dependence of photosynthesis on PAR) + if(model_decisions(iLookDECISIONS%stomResist)%iDecision >= BallBerryFlex)then + select case(trim(model_decisions(iLookDECISIONS%bbElecFunc)%cDecision)) + case('linear' ); model_decisions(iLookDECISIONS%bbElecFunc)%iDecision = linear + case('linearJmax' ); model_decisions(iLookDECISIONS%bbElecFunc)%iDecision = linearJmax + case('quadraticJmax' ); model_decisions(iLookDECISIONS%bbElecFunc)%iDecision = quadraticJmax + case default + err=10; message=trim(message)//"unknown electron transport function [option="//trim(model_decisions(iLookDECISIONS%bbElecFunc)%cDecision)//"]"; return + end select + end if + + ! identify the use of the co2 compensation point in the stomatal conductance calaculations + if(model_decisions(iLookDECISIONS%stomResist)%iDecision >= BallBerryFlex)then + select case(trim(model_decisions(iLookDECISIONS%bbCO2point)%cDecision)) + case('origBWB' ); model_decisions(iLookDECISIONS%bbCO2point)%iDecision = origBWB + case('Leuning' ); model_decisions(iLookDECISIONS%bbCO2point)%iDecision = Leuning + case default + err=10; message=trim(message)//"unknown option for the co2 compensation point [option="//trim(model_decisions(iLookDECISIONS%bbCO2point)%cDecision)//"]"; return + end select end if - end if - - ! ensure that the LAI seaonality option is switched off (this was a silly idea, in retrospect) - !if(model_decisions(iLookDECISIONS%LAI_method)%iDecision == specified)then - ! message=trim(message)//'parameterization of LAI in terms of seasonal cycle of green veg fraction was a silly idea '& - ! //' -- the LAI_method option ["specified"] is no longer supported' - ! err=20; return - !end if - - end subroutine mDecisions - - - ! ************************************************************************************************ - ! private subroutine readoption: read information from model decisions file - ! ************************************************************************************************ - subroutine readoption(err,message) - ! used to read information from model decisions file - USE ascii_util_module,only:file_open ! open file - USE ascii_util_module,only:linewidth ! max character number for one line - USE ascii_util_module,only:get_vlines ! get a vector of non-comment lines - USE summaFileManager,only:SETTINGS_PATH ! path for metadata files - USE summaFileManager,only:M_DECISIONS ! definition of modeling options - USE get_ixname_module,only:get_ixdecisions ! identify index of named variable - USE globalData,only:model_decisions ! model decision structure - implicit none - ! define output - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! define local variables - character(len=256) :: cmessage ! error message for downwind routine - character(LEN=256) :: infile ! input filename - integer(i4b) :: unt ! file unit (free unit output from file_open) - character(LEN=linewidth),allocatable :: charline(:) ! vector of character strings - integer(i4b) :: nDecisions ! number of model decisions - integer(i4b) :: iDecision ! index of model decisions - character(len=32) :: decision ! name of model decision - character(len=32) :: option ! option for model decision - integer(i4b) :: iVar ! index of the decision in the data structure - ! Start procedure here - err=0; message='readoption/' - ! build filename - infile = trim(SETTINGS_PATH)//trim(M_DECISIONS) - write(*,'(2(a,1x))') 'decisions file = ', trim(infile) - ! open file - call file_open(trim(infile),unt,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - ! get a list of character strings from non-comment lines - call get_vlines(unt,charline,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - ! close the file unit - close(unt) - ! get the number of model decisions - nDecisions = size(charline) - ! populate the model decisions structure - do iDecision=1,nDecisions - ! extract name of decision and the decision selected - read(charline(iDecision),*,iostat=err) option, decision - if (err/=0) then; err=30; message=trim(message)//"errorReadLine"; return; end if - ! get the index of the decision in the data structure - iVar = get_ixdecisions(trim(option)) - write(*,'(i4,1x,a)') iDecision, trim(option)//': '//trim(decision) - if(iVar<=0)then; err=40; message=trim(message)//"cannotFindDecisionIndex[name='"//trim(option)//"']"; return; end if - ! populate the model decisions structure - model_decisions(iVar)%cOption = trim(option) - model_decisions(iVar)%cDecision = trim(decision) - end do - end subroutine readoption + ! identify the iterative numerical solution method used in the Ball-Berry stomatal resistance parameterization + if(model_decisions(iLookDECISIONS%stomResist)%iDecision >= BallBerryFlex)then + select case(trim(model_decisions(iLookDECISIONS%bbNumerics)%cDecision)) + case('NoahMPsolution' ); model_decisions(iLookDECISIONS%bbNumerics)%iDecision = NoahMPsolution ! the NoahMP solution (and CLM4): fixed point iteration; max 3 iterations + case('newtonRaphson' ); model_decisions(iLookDECISIONS%bbNumerics)%iDecision = newtonRaphson ! full Newton-Raphson iterative solution to convergence + case default + err=10; message=trim(message)//"unknown option for the Ball-Berry numerical solution [option="//trim(model_decisions(iLookDECISIONS%bbNumerics)%cDecision)//"]"; return + end select + end if + + ! identify the controls on carbon assimilation + if(model_decisions(iLookDECISIONS%stomResist)%iDecision >= BallBerryFlex)then + select case(trim(model_decisions(iLookDECISIONS%bbAssimFnc)%cDecision)) + case('colimitation' ); model_decisions(iLookDECISIONS%bbAssimFnc)%iDecision = colimitation ! enable colimitation, as described by Collatz et al. (1991) and Sellers et al. (1996) + case('minFunc' ); model_decisions(iLookDECISIONS%bbAssimFnc)%iDecision = minFunc ! do not enable colimitation: use minimum of the three controls on carbon assimilation + case default + err=10; message=trim(message)//"unknown option for the controls on carbon assimilation [option="//trim(model_decisions(iLookDECISIONS%bbAssimFnc)%cDecision)//"]"; return + end select + end if + + ! identify the scaling of photosynthesis from the leaf to the canopy + if(model_decisions(iLookDECISIONS%stomResist)%iDecision >= BallBerryFlex)then + select case(trim(model_decisions(iLookDECISIONS%bbCanIntg8)%cDecision)) + case('constantScaling' ); model_decisions(iLookDECISIONS%bbCanIntg8)%iDecision = constantScaling ! constant scaling factor + case('laiScaling' ); model_decisions(iLookDECISIONS%bbCanIntg8)%iDecision = laiScaling ! exponential function of LAI (Leuning, Plant Cell Env 1995: "Scaling from..." [eq 9]) + case default + err=10; message=trim(message)//"unknown option for scaling of photosynthesis from the leaf to the canopy [option="//trim(model_decisions(iLookDECISIONS%bbCanIntg8)%cDecision)//"]"; return + end select + end if + + ! identify the numerical method + select case(trim(model_decisions(iLookDECISIONS%num_method)%cDecision)) + case('bEuler' ); model_decisions(iLookDECISIONS%num_method)%iDecision = bEuler ! home-grown backward Euler solution with long time steps + case('itertive' ); model_decisions(iLookDECISIONS%num_method)%iDecision = bEuler ! home-grown backward Euler solution (included for backwards compatibility) + case('sundials' ); model_decisions(iLookDECISIONS%num_method)%iDecision = sundials ! SUNDIALS/IDA solution + case default + err=10; message=trim(message)//"unknown numerical method [option="//trim(model_decisions(iLookDECISIONS%num_method)%cDecision)//"]"; return + end select + + ! how to compute heat capacity in energy equation + select case(trim(model_decisions(iLookDECISIONS%howHeatCap)%cDecision)) + case('enthalpyFD'); model_decisions(iLookDECISIONS%howHeatCap)%iDecision = enthalpyFD ! enthalpyFD + case('closedForm'); model_decisions(iLookDECISIONS%howHeatCap)%iDecision = closedForm ! closedForm + case default + err=10; message=trim(message)//"unknown Cp computation [option="//trim(model_decisions(iLookDECISIONS%howHeatCap)%cDecision)//"]"; return + end select + + ! identify the method used to calculate flux derivatives + select case(trim(model_decisions(iLookDECISIONS%fDerivMeth)%cDecision)) + case('numericl'); model_decisions(iLookDECISIONS%fDerivMeth)%iDecision = numerical ! numerical + case('analytic'); model_decisions(iLookDECISIONS%fDerivMeth)%iDecision = analytical ! analytical + case default + err=10; message=trim(message)//"unknown method used to calculate flux derivatives [option="//trim(model_decisions(iLookDECISIONS%fDerivMeth)%cDecision)//"]"; return + end select + + ! identify the method used to determine LAI and SAI + select case(trim(model_decisions(iLookDECISIONS%LAI_method)%cDecision)) + case('monTable'); model_decisions(iLookDECISIONS%LAI_method)%iDecision = monthlyTable ! LAI/SAI taken directly from a monthly table for different vegetation classes + case('specified'); model_decisions(iLookDECISIONS%LAI_method)%iDecision = specified ! LAI/SAI computed from green vegetation fraction and winterSAI and summerLAI parameters + case default + err=10; message=trim(message)//"unknown method to determine LAI and SAI [option="//trim(model_decisions(iLookDECISIONS%LAI_method)%cDecision)//"]"; return + end select + + ! identify the canopy interception parameterization + select case(trim(model_decisions(iLookDECISIONS%cIntercept)%cDecision)) + case('notPopulatedYet'); model_decisions(iLookDECISIONS%cIntercept)%iDecision = unDefined + case('sparseCanopy'); model_decisions(iLookDECISIONS%cIntercept)%iDecision = sparseCanopy + case('storageFunc'); model_decisions(iLookDECISIONS%cIntercept)%iDecision = storageFunc + case default + err=10; message=trim(message)//"unknown canopy interception parameterization [option="//trim(model_decisions(iLookDECISIONS%cIntercept)%cDecision)//"]"; return + end select + + ! identify the form of Richards' equation + select case(trim(model_decisions(iLookDECISIONS%f_Richards)%cDecision)) + case('moisture'); model_decisions(iLookDECISIONS%f_Richards)%iDecision = moisture ! moisture-based form + case('mixdform'); model_decisions(iLookDECISIONS%f_Richards)%iDecision = mixdform ! mixed form + case default + err=10; message=trim(message)//"unknown form of Richards' equation [option="//trim(model_decisions(iLookDECISIONS%f_Richards)%cDecision)//"]"; return + end select + + ! identify the groundwater parameterization + select case(trim(model_decisions(iLookDECISIONS%groundwatr)%cDecision)) + case('qTopmodl'); model_decisions(iLookDECISIONS%groundwatr)%iDecision = qbaseTopmodel ! TOPMODEL-ish baseflow parameterization + case('bigBuckt'); model_decisions(iLookDECISIONS%groundwatr)%iDecision = bigBucket ! a big bucket (lumped aquifer model) + case('noXplict'); model_decisions(iLookDECISIONS%groundwatr)%iDecision = noExplicit ! no explicit groundwater parameterization + case default + err=10; message=trim(message)//"unknown groundwater parameterization [option="//trim(model_decisions(iLookDECISIONS%groundwatr)%cDecision)//"]"; return + end select + + ! identify the hydraulic conductivity profile + select case(trim(model_decisions(iLookDECISIONS%hc_profile)%cDecision)) + case('constant'); model_decisions(iLookDECISIONS%hc_profile)%iDecision = constant ! constant hydraulic conductivity with depth + case('pow_prof'); model_decisions(iLookDECISIONS%hc_profile)%iDecision = powerLaw_profile ! power-law profile + case default + err=10; message=trim(message)//"unknown hydraulic conductivity profile [option="//trim(model_decisions(iLookDECISIONS%hc_profile)%cDecision)//"]"; return + end select + + ! identify the upper boundary conditions for thermodynamics + select case(trim(model_decisions(iLookDECISIONS%bcUpprTdyn)%cDecision)) + case('presTemp'); model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision = prescribedTemp ! prescribed temperature + case('nrg_flux'); model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision = energyFlux ! energy flux + case('zeroFlux'); model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision = zeroFlux ! zero flux + case default + err=10; message=trim(message)//"unknown upper boundary conditions for thermodynamics [option="//trim(model_decisions(iLookDECISIONS%bcUpprTdyn)%cDecision)//"]"; return + end select + + ! identify the lower boundary conditions for thermodynamics + select case(trim(model_decisions(iLookDECISIONS%bcLowrTdyn)%cDecision)) + case('presTemp'); model_decisions(iLookDECISIONS%bcLowrTdyn)%iDecision = prescribedTemp ! prescribed temperature + case('zeroFlux'); model_decisions(iLookDECISIONS%bcLowrTdyn)%iDecision = zeroFlux ! zero flux + case default + err=10; message=trim(message)//"unknown lower boundary conditions for thermodynamics [option="//trim(model_decisions(iLookDECISIONS%bcLowrTdyn)%cDecision)//"]"; return + end select + + ! identify the upper boundary conditions for soil hydrology + select case(trim(model_decisions(iLookDECISIONS%bcUpprSoiH)%cDecision)) + case('presHead'); model_decisions(iLookDECISIONS%bcUpprSoiH)%iDecision = prescribedHead ! prescribed head (volumetric liquid water content for mixed form of Richards' eqn) + case('liq_flux'); model_decisions(iLookDECISIONS%bcUpprSoiH)%iDecision = liquidFlux ! liquid water flux + case default + err=10; message=trim(message)//"unknown upper boundary conditions for soil hydrology [option="//trim(model_decisions(iLookDECISIONS%bcUpprSoiH)%cDecision)//"]"; return + end select + + ! identify the lower boundary conditions for soil hydrology + select case(trim(model_decisions(iLookDECISIONS%bcLowrSoiH)%cDecision)) + case('presHead'); model_decisions(iLookDECISIONS%bcLowrSoiH)%iDecision = prescribedHead ! prescribed head (volumetric liquid water content for mixed form of Richards' eqn) + case('bottmPsi'); model_decisions(iLookDECISIONS%bcLowrSoiH)%iDecision = funcBottomHead ! function of matric head in the lower-most layer + case('drainage'); model_decisions(iLookDECISIONS%bcLowrSoiH)%iDecision = freeDrainage ! free drainage + case('zeroFlux'); model_decisions(iLookDECISIONS%bcLowrSoiH)%iDecision = zeroFlux ! zero flux + case default + err=10; message=trim(message)//"unknown lower boundary conditions for soil hydrology [option="//trim(model_decisions(iLookDECISIONS%bcLowrSoiH)%cDecision)//"]"; return + end select + ! identify the choice of parameterization for vegetation roughness length and displacement height + select case(trim(model_decisions(iLookDECISIONS%veg_traits)%cDecision)) + case('Raupach_BLM1994'); model_decisions(iLookDECISIONS%veg_traits)%iDecision = Raupach_BLM1994 ! Raupach (BLM 1994) "Simplified expressions..." + case('CM_QJRMS1988' ); model_decisions(iLookDECISIONS%veg_traits)%iDecision = CM_QJRMS1988 ! Choudhury and Monteith (QJRMS 1998) "A four layer model for the heat budget..." + case('vegTypeTable' ); model_decisions(iLookDECISIONS%veg_traits)%iDecision = vegTypeTable ! constant parameters dependent on the vegetation type + case default + err=10; message=trim(message)//"unknown parameterization for vegetation roughness length and displacement height [option="//trim(model_decisions(iLookDECISIONS%veg_traits)%cDecision)//"]"; return + end select + + ! identify the choice of parameterization for the rooting profile + ! NOTE: for backwards compatibility select powerLaw if rooting profile is undefined + select case(trim(model_decisions(iLookDECISIONS%rootProfil)%cDecision)) + case('powerLaw','notPopulatedYet'); model_decisions(iLookDECISIONS%rootProfil)%iDecision = powerLaw ! simple power-law rooting profile + case('doubleExp'); model_decisions(iLookDECISIONS%rootProfil)%iDecision = doubleExp ! the double exponential function of Xeng et al. (JHM 2001) + case default + err=10; message=trim(message)//"unknown parameterization for rooting profile [option="//trim(model_decisions(iLookDECISIONS%rootProfil)%cDecision)//"]"; return + end select + + ! identify the choice of parameterization for canopy emissivity + select case(trim(model_decisions(iLookDECISIONS%canopyEmis)%cDecision)) + case('simplExp'); model_decisions(iLookDECISIONS%canopyEmis)%iDecision = simplExp ! simple exponential function + case('difTrans'); model_decisions(iLookDECISIONS%canopyEmis)%iDecision = difTrans ! parameterized as a function of diffuse transmissivity + case default + err=10; message=trim(message)//"unknown parameterization for canopy emissivity [option="//trim(model_decisions(iLookDECISIONS%canopyEmis)%cDecision)//"]"; return + end select + + ! choice of parameterization for snow interception + select case(trim(model_decisions(iLookDECISIONS%snowIncept)%cDecision)) + case('stickySnow'); model_decisions(iLookDECISIONS%snowIncept)%iDecision = stickySnow ! maximum interception capacity an increasing function of temerature + case('lightSnow' ); model_decisions(iLookDECISIONS%snowIncept)%iDecision = lightSnow ! maximum interception capacity an inverse function of new snow density + case default + err=10; message=trim(message)//"unknown option for snow interception capacity[option="//trim(model_decisions(iLookDECISIONS%snowIncept)%cDecision)//"]"; return + end select + + ! identify the choice of wind profile + select case(trim(model_decisions(iLookDECISIONS%windPrfile)%cDecision)) + case('exponential' ); model_decisions(iLookDECISIONS%windPrfile)%iDecision = exponential ! exponential wind profile extends to the surface + case('logBelowCanopy'); model_decisions(iLookDECISIONS%windPrfile)%iDecision = logBelowCanopy ! logarithmic profile below the vegetation canopy + case default + err=10; message=trim(message)//"unknown option for choice of wind profile[option="//trim(model_decisions(iLookDECISIONS%windPrfile)%cDecision)//"]"; return + end select + + ! identify the choice of atmospheric stability function + select case(trim(model_decisions(iLookDECISIONS%astability)%cDecision)) + case('standard'); model_decisions(iLookDECISIONS%astability)%iDecision = standard ! standard MO similarity, a la Anderson (1976) + case('louisinv'); model_decisions(iLookDECISIONS%astability)%iDecision = louisInversePower ! Louis (1979) inverse power function + case('mahrtexp'); model_decisions(iLookDECISIONS%astability)%iDecision = mahrtExponential ! Mahrt (1987) exponential + case default + err=10; message=trim(message)//"unknown stability function [option="//trim(model_decisions(iLookDECISIONS%astability)%cDecision)//"]"; return + end select + + ! choice of canopy shortwave radiation method + select case(trim(model_decisions(iLookDECISIONS%canopySrad)%cDecision)) + case('noah_mp' ); model_decisions(iLookDECISIONS%canopySrad)%iDecision = noah_mp ! full Noah-MP implementation (including albedo) + case('CLM_2stream'); model_decisions(iLookDECISIONS%canopySrad)%iDecision = CLM_2stream ! CLM 2-stream model (see CLM documentation) + case('UEB_2stream'); model_decisions(iLookDECISIONS%canopySrad)%iDecision = UEB_2stream ! UEB 2-stream model (Mahat and Tarboton, WRR 2011) + case('NL_scatter' ); model_decisions(iLookDECISIONS%canopySrad)%iDecision = NL_scatter ! Simplified method Nijssen and Lettenmaier (JGR 1999) + case('BeersLaw' ); model_decisions(iLookDECISIONS%canopySrad)%iDecision = BeersLaw ! Beer's Law (as implemented in VIC) + case default + err=10; message=trim(message)//"unknown canopy radiation method [option="//trim(model_decisions(iLookDECISIONS%canopySrad)%cDecision)//"]"; return + end select + + ! choice of albedo representation + select case(trim(model_decisions(iLookDECISIONS%alb_method)%cDecision)) + case('conDecay'); model_decisions(iLookDECISIONS%alb_method)%iDecision = constantDecay ! constant decay (e.g., VIC, CLASS) + case('varDecay'); model_decisions(iLookDECISIONS%alb_method)%iDecision = variableDecay ! variable decay (e.g., BATS approach, with destructive metamorphism + soot content) + case default + err=10; message=trim(message)//"unknown option for snow albedo [option="//trim(model_decisions(iLookDECISIONS%alb_method)%cDecision)//"]"; return + end select + + ! choice of snow compaction routine + select case(trim(model_decisions(iLookDECISIONS%compaction)%cDecision)) + case('consettl'); model_decisions(iLookDECISIONS%compaction)%iDecision = constantSettlement ! constant settlement rate + case('anderson'); model_decisions(iLookDECISIONS%compaction)%iDecision = andersonEmpirical ! semi-empirical method of Anderson (1976) + case default + err=10; message=trim(message)//"unknown option for snow compaction [option="//trim(model_decisions(iLookDECISIONS%compaction)%cDecision)//"]"; return + end select + + ! choice of method to combine and sub-divide snow layers + select case(trim(model_decisions(iLookDECISIONS%snowLayers)%cDecision)) + case('jrdn1991'); model_decisions(iLookDECISIONS%snowLayers)%iDecision = sameRulesAllLayers ! SNTHERM option: same combination/sub-dividion rules applied to all layers + case('CLM_2010'); model_decisions(iLookDECISIONS%snowLayers)%iDecision = rulesDependLayerIndex ! CLM option: combination/sub-dividion rules depend on layer index + case default + err=10; message=trim(message)//"unknown option for combination/sub-division of snow layers [option="//trim(model_decisions(iLookDECISIONS%snowLayers)%cDecision)//"]"; return + end select + + ! choice of thermal conductivity representation for snow + select case(trim(model_decisions(iLookDECISIONS%thCondSnow)%cDecision)) + case('tyen1965'); model_decisions(iLookDECISIONS%thCondSnow)%iDecision = Yen1965 ! Yen (1965) + case('melr1977'); model_decisions(iLookDECISIONS%thCondSnow)%iDecision = Mellor1977 ! Mellor (1977) + case('jrdn1991'); model_decisions(iLookDECISIONS%thCondSnow)%iDecision = Jordan1991 ! Jordan (1991) + case('smnv2000'); model_decisions(iLookDECISIONS%thCondSnow)%iDecision = Smirnova2000 ! Smirnova et al. (2000) + case default + err=10; message=trim(message)//"unknown option for thermal conductivity of snow [option="//trim(model_decisions(iLookDECISIONS%thCondSnow)%cDecision)//"]"; return + end select + + ! choice of thermal conductivity representation for soil + select case(trim(model_decisions(iLookDECISIONS%thCondSoil)%cDecision)) + case('funcSoilWet'); model_decisions(iLookDECISIONS%thCondSoil)%iDecision = funcSoilWet ! function of soil wetness + case('mixConstit' ); model_decisions(iLookDECISIONS%thCondSoil)%iDecision = mixConstit ! mixture of constituents + case('hanssonVZJ' ); model_decisions(iLookDECISIONS%thCondSoil)%iDecision = hanssonVZJ ! test case for the mizoguchi lab experiment, Hansson et al. VZJ 2004 + case default + err=10; message=trim(message)//"unknown option for thermal conductivity of soil [option="//trim(model_decisions(iLookDECISIONS%thCondSoil)%cDecision)//"]"; return + end select + + ! choice of method for the spatial representation of groundwater + select case(trim(model_decisions(iLookDECISIONS%spatial_gw)%cDecision)) + case('localColumn'); model_decisions(iLookDECISIONS%spatial_gw)%iDecision = localColumn ! separate groundwater in each local soil column + case('singleBasin'); model_decisions(iLookDECISIONS%spatial_gw)%iDecision = singleBasin ! single groundwater store over the entire basin + case default + err=10; message=trim(message)//"unknown option for spatial representation of groundwater [option="//trim(model_decisions(iLookDECISIONS%spatial_gw)%cDecision)//"]"; return + end select + + ! choice of routing method + select case(trim(model_decisions(iLookDECISIONS%subRouting)%cDecision)) + case('timeDlay'); model_decisions(iLookDECISIONS%subRouting)%iDecision = timeDelay ! time-delay histogram + case('qInstant'); model_decisions(iLookDECISIONS%subRouting)%iDecision = qInstant ! instantaneous routing + case default + err=10; message=trim(message)//"unknown option for sub-grid routing [option="//trim(model_decisions(iLookDECISIONS%subRouting)%cDecision)//"]"; return + end select + + ! choice of new snow density + ! NOTE: use hedAndPom as the default, where density method is undefined (not populated yet) + select case(trim(model_decisions(iLookDECISIONS%snowDenNew)%cDecision)) + case('hedAndPom','notPopulatedYet'); model_decisions(iLookDECISIONS%snowDenNew)%iDecision = hedAndPom ! Hedstrom and Pomeroy (1998), expoential increase + case('anderson'); model_decisions(iLookDECISIONS%snowDenNew)%iDecision = anderson ! Anderson 1976 + case('pahaut_76'); model_decisions(iLookDECISIONS%snowDenNew)%iDecision = pahaut_76 ! Pahaut 1976, wind speed dependent (derived from Col de Porte, French Alps) + case('constDens'); model_decisions(iLookDECISIONS%snowDenNew)%iDecision = constDens ! Constant new snow density + case default + err=10; message=trim(message)//"unknown option for new snow density [option="//trim(model_decisions(iLookDECISIONS%snowDenNew)%cDecision)//"]"; return + end select + + ! choice of snow unloading from canopy + select case(trim(model_decisions(iLookDECISIONS%snowUnload)%cDecision)) + case('meltDripUnload','notPopulatedYet'); model_decisions(iLookDECISIONS%snowUnload)%iDecision = meltDripUnload ! Hedstrom and Pomeroy (1998), Storck et al 2002 (snowUnloadingCoeff & ratioDrip2Unloading) + case('windUnload'); model_decisions(iLookDECISIONS%snowUnload)%iDecision = windUnload ! Roesch et al 2001, formulate unloading based on wind and temperature + case default + err=10; message=trim(message)//"unknown option for snow unloading [option="//trim(model_decisions(iLookDECISIONS%snowUnload)%cDecision)//"]"; return + end select + + + ! ----------------------------------------------------------------------------------------------------------------------------------------------- + ! check for consistency among options + ! ----------------------------------------------------------------------------------------------------------------------------------------------- + ! check zero flux lower boundary for topmodel baseflow option + select case(model_decisions(iLookDECISIONS%groundwatr)%iDecision) + case(qbaseTopmodel) + if(model_decisions(iLookDECISIONS%bcLowrSoiH)%iDecision /= zeroFlux)then + message=trim(message)//'lower boundary condition for soil hydology must be zeroFlux with qbaseTopmodel option for groundwater' + err=20; return + end if + end select + + ! check power-law profile is selected when using topmodel baseflow option + select case(model_decisions(iLookDECISIONS%groundwatr)%iDecision) + case(qbaseTopmodel) + if(model_decisions(iLookDECISIONS%hc_profile)%iDecision /= powerLaw_profile)then + message=trim(message)//'power-law transmissivity profile must be selected when using topmodel baseflow option' + err=20; return + end if + end select + + ! check bigBucket groundwater option is used when for spatial groundwater is singleBasin + if(model_decisions(iLookDECISIONS%spatial_gw)%iDecision == singleBasin)then + if(model_decisions(iLookDECISIONS%groundwatr)%iDecision /= bigBucket)then + message=trim(message)//'groundwater parameterization must be bigBucket when using singleBasin for spatial_gw' + err=20; return + end if + end if + +end subroutine mDecisions + + +! ************************************************************************************************ +! private subroutine readoption: read information from model decisions file +! ************************************************************************************************ +subroutine readoption(err,message) + ! used to read information from model decisions file + USE ascii_util_module,only:file_open ! open file + USE ascii_util_module,only:linewidth ! max character number for one line + USE ascii_util_module,only:get_vlines ! get a vector of non-comment lines + USE summaFileManager,only:SETTINGS_PATH ! path for metadata files + USE summaFileManager,only:M_DECISIONS ! definition of modeling options + USE get_ixname_module,only:get_ixdecisions ! identify index of named variable + USE globalData,only:model_decisions ! model decision structure + implicit none + ! define output + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! define local variables + character(len=256) :: cmessage ! error message for downwind routine + character(LEN=256) :: infile ! input filename + integer(i4b) :: unt ! file unit (free unit output from file_open) + character(LEN=linewidth),allocatable :: charline(:) ! vector of character strings + integer(i4b) :: nDecisions ! number of model decisions + integer(i4b) :: iDecision ! index of model decisions + character(len=32) :: decision ! name of model decision + character(len=32) :: option ! option for model decision + integer(i4b) :: iVar ! index of the decision in the data structure + ! Start procedure here + err=0; message='readoption/' + ! build filename + infile = trim(SETTINGS_PATH)//trim(M_DECISIONS) + write(*,'(2(a,1x))') 'decisions file = ', trim(infile) + ! open file + call file_open(trim(infile),unt,err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + ! get a list of character strings from non-comment lines + call get_vlines(unt,charline,err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + ! close the file unit + close(unt) + ! get the number of model decisions + nDecisions = size(charline) + ! populate the model decisions structure + do iDecision=1,nDecisions + ! extract name of decision and the decision selected + read(charline(iDecision),*,iostat=err) option, decision + if (err/=0) then; err=30; message=trim(message)//"errorReadLine"; return; end if + ! get the index of the decision in the data structure + iVar = get_ixdecisions(trim(option)) + write(*,'(i4,1x,a)') iDecision, trim(option)//': '//trim(decision) + if(iVar<=0)then; err=40; message=trim(message)//"cannotFindDecisionIndex[name='"//trim(option)//"']"; return; end if + ! populate the model decisions structure + model_decisions(iVar)%cOption = trim(option) + model_decisions(iVar)%cDecision = trim(decision) + end do +end subroutine readoption end module mDecisions_module From 5569b1b30898d26efa23d194cadc8186ffa2d250 Mon Sep 17 00:00:00 2001 From: Kyle Date: Wed, 28 Sep 2022 17:06:58 +0000 Subject: [PATCH 0378/1472] fixed indentation --- build/source/driver/summa_driver.f90 | 144 +++---- build/source/driver/summa_init.f90 | 561 +++++++++++++-------------- 2 files changed, 352 insertions(+), 353 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index d4bb7006a..ea01242f4 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -19,88 +19,88 @@ ! along with this program. If not, see . program summa_driver -! driver program for summa simulations -! ***************************************************************************** -! * use desired modules -! ***************************************************************************** -! data types -USE nrtype ! variable types, etc. -USE summa_type, only: summa1_type_dec ! master summa data type -! subroutines and functions: model setup -USE summa_init, only: summa_initialize ! used to allocate/initialize summa data structures -USE summa_setup, only: summa_paramSetup ! used to initialize parameter data structures (e.g. vegetation and soil parameters) -USE summa_restart, only: summa_readRestart ! used to read restart data and reset the model state -! subroutines and functions: model simulation -USE summa_forcing, only: summa_readForcing ! used to read forcing data -USE summa_modelRun, only: summa_runPhysics ! used to run the summa physics for one time step -USE summa_writeOutput, only: summa_writeOutputFiles ! used to write the summa output files -! utility functions -USE summa_util, only: stop_program ! used to stop the summa program (with errors) -USE summa_util, only: handle_err ! used to process errors -! global data -USE globalData, only: numtim ! number of model time steps -implicit none + ! driver program for summa simulations + ! ***************************************************************************** + ! * use desired modules + ! ***************************************************************************** + ! data types + USE nrtype ! variable types, etc. + USE summa_type, only: summa1_type_dec ! master summa data type + ! subroutines and functions: model setup + USE summa_init, only: summa_initialize ! used to allocate/initialize summa data structures + USE summa_setup, only: summa_paramSetup ! used to initialize parameter data structures (e.g. vegetation and soil parameters) + USE summa_restart, only: summa_readRestart ! used to read restart data and reset the model state + ! subroutines and functions: model simulation + USE summa_forcing, only: summa_readForcing ! used to read forcing data + USE summa_modelRun, only: summa_runPhysics ! used to run the summa physics for one time step + USE summa_writeOutput, only: summa_writeOutputFiles ! used to write the summa output files + ! utility functions + USE summa_util, only: stop_program ! used to stop the summa program (with errors) + USE summa_util, only: handle_err ! used to process errors + ! global data + USE globalData, only: numtim ! number of model time steps + implicit none -! ***************************************************************************** -! * variable definitions -! ***************************************************************************** -! define the master summa data structure -type(summa1_type_dec), allocatable :: summa1_struc(:) -! define parameters for the model simulation -integer(i4b), parameter :: n=1 ! number of instantiations -! define timing information -integer(i4b) :: modelTimeStep ! index of model time step -! error control -integer(i4b) :: err=0 ! error code -character(len=1024) :: message='' ! error message -integer(i4b) :: iStep + ! ***************************************************************************** + ! * variable definitions + ! ***************************************************************************** + ! define the master summa data structure + type(summa1_type_dec), allocatable :: summa1_struc(:) + ! define parameters for the model simulation + integer(i4b), parameter :: n=1 ! number of instantiations + ! define timing information + integer(i4b) :: modelTimeStep ! index of model time step + ! error control + integer(i4b) :: err=0 ! error code + character(len=1024) :: message='' ! error message + integer(i4b) :: iStep -! ***************************************************************************** -! * preliminaries -! ***************************************************************************** + ! ***************************************************************************** + ! * preliminaries + ! ***************************************************************************** -! allocate space for the master summa structure -allocate(summa1_struc(n), stat=err) -if(err/=0) call stop_program(1, 'problem allocating master summa structure') + ! allocate space for the master summa structure + allocate(summa1_struc(n), stat=err) + if(err/=0) call stop_program(1, 'problem allocating master summa structure') -! ***************************************************************************** -! * model setup/initialization -! ***************************************************************************** + ! ***************************************************************************** + ! * model setup/initialization + ! ***************************************************************************** -! declare and allocate summa data structures and initialize model state to known values -call summa_initialize(summa1_struc(n), err, message) -call handle_err(err, message) + ! declare and allocate summa data structures and initialize model state to known values + call summa_initialize(summa1_struc(n), err, message) + call handle_err(err, message) -! initialize parameter data structures (e.g. vegetation and soil parameters) -call summa_paramSetup(summa1_struc(n), err, message) -call handle_err(err, message) + ! initialize parameter data structures (e.g. vegetation and soil parameters) + call summa_paramSetup(summa1_struc(n), err, message) + call handle_err(err, message) -! read restart data and reset the model state -call summa_readRestart(summa1_struc(n), err, message) -call handle_err(err, message) + ! read restart data and reset the model state + call summa_readRestart(summa1_struc(n), err, message) + call handle_err(err, message) -! ***************************************************************************** -! * model simulation -! ***************************************************************************** -iStep = 1 -! loop through time -do modelTimeStep=1,numtim + ! ***************************************************************************** + ! * model simulation + ! ***************************************************************************** + iStep = 1 + ! loop through time + do modelTimeStep=1,numtim - ! read model forcing data - call summa_readForcing(modelTimeStep, summa1_struc(n), err, message) - call handle_err(err, message) + ! read model forcing data + call summa_readForcing(modelTimeStep, summa1_struc(n), err, message) + call handle_err(err, message) - print *, 'step ---> ', iStep - ! run the summa physics for one time step - call summa_runPhysics(modelTimeStep, summa1_struc(n), err, message) - call handle_err(err, message) + print *, 'step ---> ', iStep + ! run the summa physics for one time step + call summa_runPhysics(modelTimeStep, summa1_struc(n), err, message) + call handle_err(err, message) - ! write the model output - call summa_writeOutputFiles(modelTimeStep, summa1_struc(n), err, message) - call handle_err(err, message) - iStep = iStep + 1 -end do ! looping through time + ! write the model output + call summa_writeOutputFiles(modelTimeStep, summa1_struc(n), err, message) + call handle_err(err, message) + iStep = iStep + 1 + end do ! looping through time -! successful end -call stop_program(0, 'finished simulation successfully.') + ! successful end + call stop_program(0, 'finished simulation successfully.') end program summa_driver diff --git a/build/source/driver/summa_init.f90 b/build/source/driver/summa_init.f90 index dee5f4abb..5777e18e2 100644 --- a/build/source/driver/summa_init.f90 +++ b/build/source/driver/summa_init.f90 @@ -57,286 +57,285 @@ module summa_init public::summa_initialize contains - ! used to declare and allocate summa data structures and initialize model state to known values - subroutine summa_initialize(summa1_struc, err, message) - ! --------------------------------------------------------------------------------------- - ! * desired modules - ! --------------------------------------------------------------------------------------- - ! data types - USE nrtype ! variable types, etc. - USE summa_type, only:summa1_type_dec ! master summa data type - ! subroutines and functions: initial priming - USE summa_util, only:getCommandArguments ! process command line arguments - USE summaFileManager,only:summa_SetTimesDirsAndFiles ! sets directories and filenames - USE summa_globalData,only:summa_defineGlobalData ! used to define global summa data structures - USE time_utils_module,only:elapsedSec ! calculate the elapsed time - ! subroutines and functions: read dimensions (NOTE: NetCDF) - USE read_attrb_module,only:read_dimension ! module to read dimensions of GRU and HRU - USE read_icond_module,only:read_icond_nlayers ! module to read initial condition dimensions - ! subroutines and functions: allocate space - USE allocspace_module,only:allocGlobal ! module to allocate space for global data structures - USE allocspace_module,only:allocLocal ! module to allocate space for local data structures - ! timing variables - USE globalData,only:startInit,endInit ! date/time for the start and end of the initialization - USE globalData,only:elapsedInit ! elapsed time for the initialization - USE globalData,only:elapsedRead ! elapsed time for the data read - USE globalData,only:elapsedWrite ! elapsed time for the stats/write - USE globalData,only:elapsedPhysics ! elapsed time for the physics - ! model time structures - USE globalData,only:startTime ! start time - USE globalData,only:finshTime ! end time - USE globalData,only:refTime ! reference time - USE globalData,only:oldTime ! time from previous step - ! run time options - USE globalData,only:startGRU ! index of the starting GRU for parallelization run - USE globalData,only:checkHRU ! index of the HRU for a single HRU run - USE globalData,only:iRunMode ! define the current running mode - ! miscellaneous global data - USE globalData,only:ncid ! file id of netcdf output file - USE globalData,only:gru_struc ! gru-hru mapping structures - USE globalData,only:structInfo ! information on the data structures - USE globalData,only:output_fileSuffix ! suffix for the output file - ! --------------------------------------------------------------------------------------- - ! * variables - ! --------------------------------------------------------------------------------------- - implicit none - ! dummy variables - type(summa1_type_dec),intent(inout) :: summa1_struc ! master summa data structure - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! local variables - character(LEN=256) :: cmessage ! error message of downwind routine - character(len=256) :: restartFile ! restart file name - character(len=256) :: attrFile ! attributes file name - character(len=128) :: fmtGruOutput ! a format string used to write start and end GRU in output file names - integer(i4b) :: iStruct,iGRU ! looping variables - integer(i4b) :: fileGRU ! [used for filenames] number of GRUs in the input file - integer(i4b) :: fileHRU ! [used for filenames] number of HRUs in the input file - ! --------------------------------------------------------------------------------------- - ! associate to elements in the data structure - summaVars: associate(& - ! lookup table data structure - lookupStruct => summa1_struc%lookupStruct , & ! x%gru(:)%hru(:)%z(:)%var(:)%lookup(:) -- lookup tables - ! statistics structures - forcStat => summa1_struc%forcStat , & ! x%gru(:)%hru(:)%var(:)%dat -- model forcing data - progStat => summa1_struc%progStat , & ! x%gru(:)%hru(:)%var(:)%dat -- model prognostic (state) variables - diagStat => summa1_struc%diagStat , & ! x%gru(:)%hru(:)%var(:)%dat -- model diagnostic variables - fluxStat => summa1_struc%fluxStat , & ! x%gru(:)%hru(:)%var(:)%dat -- model fluxes - indxStat => summa1_struc%indxStat , & ! x%gru(:)%hru(:)%var(:)%dat -- model indices - bvarStat => summa1_struc%bvarStat , & ! x%gru(:)%var(:)%dat -- basin-average variables - - ! primary data structures (scalars) - timeStruct => summa1_struc%timeStruct , & ! x%var(:) -- model time data - forcStruct => summa1_struc%forcStruct , & ! x%gru(:)%hru(:)%var(:) -- model forcing data - attrStruct => summa1_struc%attrStruct , & ! x%gru(:)%hru(:)%var(:) -- local attributes for each HRU - typeStruct => summa1_struc%typeStruct , & ! x%gru(:)%hru(:)%var(:) -- local classification of soil veg etc. for each HRU - idStruct => summa1_struc%idStruct , & ! x%gru(:)%hru(:)%var(:) -- - - ! primary data structures (variable length vectors) - indxStruct => summa1_struc%indxStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model indices - mparStruct => summa1_struc%mparStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model parameters - progStruct => summa1_struc%progStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model prognostic (state) variables - diagStruct => summa1_struc%diagStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model diagnostic variables - fluxStruct => summa1_struc%fluxStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model fluxes - - ! basin-average structures - bparStruct => summa1_struc%bparStruct , & ! x%gru(:)%var(:) -- basin-average parameters - bvarStruct => summa1_struc%bvarStruct , & ! x%gru(:)%var(:)%dat -- basin-average variables - - ! ancillary data structures - dparStruct => summa1_struc%dparStruct , & ! x%gru(:)%hru(:)%var(:) -- default model parameters - - ! run time variables - computeVegFlux => summa1_struc%computeVegFlux , & ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) - dt_init => summa1_struc%dt_init , & ! used to initialize the length of the sub-step for each HRU - upArea => summa1_struc%upArea , & ! area upslope of each HRU - - ! miscellaneous variables - summa1open => summa1_struc%summa1open , & ! flag to define if the summa file is open?? - numout => summa1_struc%numout , & ! number of output variables?? - ts => summa1_struc%ts , & ! model time step ?? - nGRU => summa1_struc%nGRU , & ! number of grouped response units - nHRU => summa1_struc%nHRU , & ! number of global hydrologic response units - hruCount => summa1_struc%hruCount , & ! number of local hydrologic response units - greenVegFrac_monthly => summa1_struc%greenVegFrac_monthly, & ! fraction of green vegetation in each month (0-1) - summaFileManagerFile => summa1_struc%summaFileManagerFile & ! path/name of file defining directories and files - - ) ! assignment to variables in the data structures - ! --------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='summa_initialize/' - - ! initialize the start of the initialization - call date_and_time(values=startInit) - - ! ***************************************************************************** - ! *** inital priming -- get command line arguments, identify files, etc. - ! ***************************************************************************** - - ! initialize the netcdf file id - ncid(:) = integerMissing - - ! initialize the elapsed time for cumulative quantities - elapsedRead=0._rkind - elapsedWrite=0._rkind - elapsedPhysics=0._rkind - - ! get the command line arguments - call getCommandArguments(summa1_struc,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! set directories and files -- summaFileManager used as command-line argument - call summa_SetTimesDirsAndFiles(summaFileManagerFile,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! define global data (parameters, metadata) - call summa_defineGlobalData(err, cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! ***************************************************************************** - ! *** read the number of GRUs and HRUs - ! ***************************************************************************** - ! obtain the HRU and GRU dimensions in the LocalAttribute file - attrFile = trim(SETTINGS_PATH)//trim(LOCAL_ATTRIBUTES) - select case (iRunMode) - case(iRunModeFull); call read_dimension(trim(attrFile),fileGRU,fileHRU,nGRU,nHRU,err,cmessage) - case(iRunModeGRU ); call read_dimension(trim(attrFile),fileGRU,fileHRU,nGRU,nHRU,err,cmessage,startGRU=startGRU) - case(iRunModeHRU ); call read_dimension(trim(attrFile),fileGRU,fileHRU,nGRU,nHRU,err,cmessage,checkHRU=checkHRU) - end select - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! ***************************************************************************** - ! *** read the number of snow and soil layers - ! ***************************************************************************** - ! set restart filename and read the number of snow and soil layers from the initial conditions (restart) file - if(STATE_PATH == '') then - restartFile = trim(SETTINGS_PATH)//trim(MODEL_INITCOND) - else - restartFile = trim(STATE_PATH)//trim(MODEL_INITCOND) - endif - call read_icond_nlayers(trim(restartFile),nGRU,indx_meta,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! ***************************************************************************** - ! *** allocate space for data structures - ! ***************************************************************************** - - ! allocate time structures - do iStruct=1,4 - select case(iStruct) - case(1); call allocLocal(time_meta, startTime, err=err, message=cmessage) ! start time for the model simulation - case(2); call allocLocal(time_meta, finshTime, err=err, message=cmessage) ! end time for the model simulation - case(3); call allocLocal(time_meta, refTime, err=err, message=cmessage) ! reference time for the model simulation - case(4); call allocLocal(time_meta, oldTime, err=err, message=cmessage) ! time from the previous step - end select - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - end do ! looping through time structures - - ! allocate other data structures - do iStruct=1,size(structInfo) - ! allocate space - select case(trim(structInfo(iStruct)%structName)) - case('time' ); call allocGlobal(time_meta, timeStruct, err, cmessage) ! model forcing data - case('forc' ); call allocGlobal(forc_meta, forcStruct, err, cmessage) ! model forcing data - case('attr' ); call allocGlobal(attr_meta, attrStruct, err, cmessage) ! local attributes for each HRU - case('type' ); call allocGlobal(type_meta, typeStruct, err, cmessage) ! local classification of soil veg etc. for each HRU - case('id' ); call allocGlobal(id_meta, idStruct, err, message) ! local values of hru and gru IDs - case('mpar' ); call allocGlobal(mpar_meta, mparStruct, err, cmessage) ! model parameters - case('indx' ); call allocGlobal(indx_meta, indxStruct, err, cmessage) ! model variables - case('prog' ); call allocGlobal(prog_meta, progStruct, err, cmessage) ! model prognostic (state) variables - case('diag' ); call allocGlobal(diag_meta, diagStruct, err, cmessage) ! model diagnostic variables - case('flux' ); call allocGlobal(flux_meta, fluxStruct, err, cmessage) ! model fluxes - case('bpar' ); call allocGlobal(bpar_meta, bparStruct, err, cmessage) ! basin-average parameters - case('bvar' ); call allocGlobal(bvar_meta, bvarStruct, err, cmessage) ! basin-average variables - case('lookup'); call allocGlobal(lookup_meta, lookupStruct, err, cmessage) ! basin-average variables - case('deriv' ); cycle - case default; err=20; message='unable to find structure name: '//trim(structInfo(iStruct)%structName) - end select - ! check errors - if(err/=0)then - message=trim(message)//trim(cmessage)//'[structure = '//trim(structInfo(iStruct)%structName)//']' - return - endif - end do ! looping through data structures - - ! allocate space for default model parameters - ! NOTE: This is done here, rather than in the loop above, because dpar is not one of the "standard" data structures - call allocGlobal(mpar_meta,dparStruct,err,cmessage) ! default model parameters - if(err/=0)then - message=trim(message)//trim(cmessage)//' [problem allocating dparStruct]' - return - endif - - ! allocate space for the time step and computeVegFlux flags (recycled for each GRU for subsequent model calls) - allocate(dt_init%gru(nGRU),upArea%gru(nGRU),computeVegFlux%gru(nGRU),stat=err) - if(err/=0)then - message=trim(message)//'problem allocating space for dt_init, upArea, or computeVegFlux [GRU]' - return - endif - - ! allocate space for the HRUs - do iGRU=1,nGRU - hruCount = gru_struc(iGRU)%hruCount ! gru_struc populated in "read_dimension" - allocate(dt_init%gru(iGRU)%hru(hruCount),upArea%gru(iGRU)%hru(hruCount),computeVegFlux%gru(iGRU)%hru(hruCount),stat=err) - if(err/=0)then - message='problem allocating space for dt_init, upArea, or computeVegFlux [HRU]' - return - endif - end do - - ! ***************************************************************************** - ! *** allocate space for output statistics data structures - ! ***************************************************************************** - - ! loop through data structures - do iStruct=1,size(structInfo) - - ! allocate space - select case(trim(structInfo(iStruct)%structName)) - case('forc'); call allocGlobal(statForc_meta(:)%var_info,forcStat,err,cmessage) ! model forcing data - case('prog'); call allocGlobal(statProg_meta(:)%var_info,progStat,err,cmessage) ! model prognostic (state) variables - case('diag'); call allocGlobal(statDiag_meta(:)%var_info,diagStat,err,cmessage) ! model diagnostic variables - case('flux'); call allocGlobal(statFlux_meta(:)%var_info,fluxStat,err,cmessage) ! model fluxes - case('indx'); call allocGlobal(statIndx_meta(:)%var_info,indxStat,err,cmessage) ! index vars - case('bvar'); call allocGlobal(statBvar_meta(:)%var_info,bvarStat,err,cmessage) ! basin-average variables - case default; cycle - end select - - ! check errors - if(err/=0)then - message=trim(message)//trim(cmessage)//'[statistics for = '//trim(structInfo(iStruct)%structName)//']' - return - endif - - end do ! iStruct - - ! ***************************************************************************** - ! *** define the suffix for the model output file - ! ***************************************************************************** - - ! set up the output file names as: OUTPUT_PREFIX'_'output_fileSuffix'_'startGRU-endGRU_outfreq.nc or OUTPUT_PREFIX'_'output_fileSuffix'_'HRU_outfreq.nc; - if (output_fileSuffix(1:1) /= '_') output_fileSuffix='_'//trim(output_fileSuffix) ! separate output_fileSuffix from others by underscores - if (output_fileSuffix(len_trim(output_fileSuffix):len_trim(output_fileSuffix)) == '_') output_fileSuffix(len_trim(output_fileSuffix):len_trim(output_fileSuffix)) = ' ' - select case (iRunMode) - case(iRunModeGRU) - ! left zero padding for startGRU and endGRU - write(fmtGruOutput,"(i0)") ceiling(log10(real(fileGRU)+0.1)) ! maximum width of startGRU and endGRU - fmtGruOutput = "i"//trim(fmtGruOutput)//"."//trim(fmtGruOutput) ! construct the format string for startGRU and endGRU - fmtGruOutput = "('_G',"//trim(fmtGruOutput)//",'-',"//trim(fmtGruOutput)//")" - write(output_fileSuffix((len_trim(output_fileSuffix)+1):len(output_fileSuffix)),fmtGruOutput) startGRU,startGRU+nGRU-1 - case(iRunModeHRU) - write(output_fileSuffix((len_trim(output_fileSuffix)+1):len(output_fileSuffix)),"('_H',i0)") checkHRU - end select - - ! identify the end of the initialization - call date_and_time(values=endInit) - - ! aggregate the elapsed time for the initialization - elapsedInit = elapsedSec(startInit, endInit) - - ! end associate statements - end associate summaVars - - end subroutine summa_initialize +! used to declare and allocate summa data structures and initialize model state to known values +subroutine summa_initialize(summa1_struc, err, message) + ! --------------------------------------------------------------------------------------- + ! * desired modules + ! --------------------------------------------------------------------------------------- + ! data types + USE nrtype ! variable types, etc. + USE summa_type, only:summa1_type_dec ! master summa data type + ! subroutines and functions: initial priming + USE summa_util, only:getCommandArguments ! process command line arguments + USE summaFileManager,only:summa_SetTimesDirsAndFiles ! sets directories and filenames + USE summa_globalData,only:summa_defineGlobalData ! used to define global summa data structures + USE time_utils_module,only:elapsedSec ! calculate the elapsed time + ! subroutines and functions: read dimensions (NOTE: NetCDF) + USE read_attrb_module,only:read_dimension ! module to read dimensions of GRU and HRU + USE read_icond_module,only:read_icond_nlayers ! module to read initial condition dimensions + ! subroutines and functions: allocate space + USE allocspace_module,only:allocGlobal ! module to allocate space for global data structures + USE allocspace_module,only:allocLocal ! module to allocate space for local data structures + ! timing variables + USE globalData,only:startInit,endInit ! date/time for the start and end of the initialization + USE globalData,only:elapsedInit ! elapsed time for the initialization + USE globalData,only:elapsedRead ! elapsed time for the data read + USE globalData,only:elapsedWrite ! elapsed time for the stats/write + USE globalData,only:elapsedPhysics ! elapsed time for the physics + ! model time structures + USE globalData,only:startTime ! start time + USE globalData,only:finshTime ! end time + USE globalData,only:refTime ! reference time + USE globalData,only:oldTime ! time from previous step + ! run time options + USE globalData,only:startGRU ! index of the starting GRU for parallelization run + USE globalData,only:checkHRU ! index of the HRU for a single HRU run + USE globalData,only:iRunMode ! define the current running mode + ! miscellaneous global data + USE globalData,only:ncid ! file id of netcdf output file + USE globalData,only:gru_struc ! gru-hru mapping structures + USE globalData,only:structInfo ! information on the data structures + USE globalData,only:output_fileSuffix ! suffix for the output file + ! --------------------------------------------------------------------------------------- + ! * variables + ! --------------------------------------------------------------------------------------- + implicit none + ! dummy variables + type(summa1_type_dec),intent(inout) :: summa1_struc ! master summa data structure + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local variables + character(LEN=256) :: cmessage ! error message of downwind routine + character(len=256) :: restartFile ! restart file name + character(len=256) :: attrFile ! attributes file name + character(len=128) :: fmtGruOutput ! a format string used to write start and end GRU in output file names + integer(i4b) :: iStruct,iGRU ! looping variables + integer(i4b) :: fileGRU ! [used for filenames] number of GRUs in the input file + integer(i4b) :: fileHRU ! [used for filenames] number of HRUs in the input file + ! --------------------------------------------------------------------------------------- + ! associate to elements in the data structure + summaVars: associate(& + ! lookup table data structure + lookupStruct => summa1_struc%lookupStruct , & ! x%gru(:)%hru(:)%z(:)%var(:)%lookup(:) -- lookup tables + ! statistics structures + forcStat => summa1_struc%forcStat , & ! x%gru(:)%hru(:)%var(:)%dat -- model forcing data + progStat => summa1_struc%progStat , & ! x%gru(:)%hru(:)%var(:)%dat -- model prognostic (state) variables + diagStat => summa1_struc%diagStat , & ! x%gru(:)%hru(:)%var(:)%dat -- model diagnostic variables + fluxStat => summa1_struc%fluxStat , & ! x%gru(:)%hru(:)%var(:)%dat -- model fluxes + indxStat => summa1_struc%indxStat , & ! x%gru(:)%hru(:)%var(:)%dat -- model indices + bvarStat => summa1_struc%bvarStat , & ! x%gru(:)%var(:)%dat -- basin-average variables + + ! primary data structures (scalars) + timeStruct => summa1_struc%timeStruct , & ! x%var(:) -- model time data + forcStruct => summa1_struc%forcStruct , & ! x%gru(:)%hru(:)%var(:) -- model forcing data + attrStruct => summa1_struc%attrStruct , & ! x%gru(:)%hru(:)%var(:) -- local attributes for each HRU + typeStruct => summa1_struc%typeStruct , & ! x%gru(:)%hru(:)%var(:) -- local classification of soil veg etc. for each HRU + idStruct => summa1_struc%idStruct , & ! x%gru(:)%hru(:)%var(:) -- + + ! primary data structures (variable length vectors) + indxStruct => summa1_struc%indxStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model indices + mparStruct => summa1_struc%mparStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model parameters + progStruct => summa1_struc%progStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model prognostic (state) variables + diagStruct => summa1_struc%diagStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model diagnostic variables + fluxStruct => summa1_struc%fluxStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model fluxes + + ! basin-average structures + bparStruct => summa1_struc%bparStruct , & ! x%gru(:)%var(:) -- basin-average parameters + bvarStruct => summa1_struc%bvarStruct , & ! x%gru(:)%var(:)%dat -- basin-average variables + + ! ancillary data structures + dparStruct => summa1_struc%dparStruct , & ! x%gru(:)%hru(:)%var(:) -- default model parameters + + ! run time variables + computeVegFlux => summa1_struc%computeVegFlux , & ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) + dt_init => summa1_struc%dt_init , & ! used to initialize the length of the sub-step for each HRU + upArea => summa1_struc%upArea , & ! area upslope of each HRU + + ! miscellaneous variables + summa1open => summa1_struc%summa1open , & ! flag to define if the summa file is open?? + numout => summa1_struc%numout , & ! number of output variables?? + ts => summa1_struc%ts , & ! model time step ?? + nGRU => summa1_struc%nGRU , & ! number of grouped response units + nHRU => summa1_struc%nHRU , & ! number of global hydrologic response units + hruCount => summa1_struc%hruCount , & ! number of local hydrologic response units + greenVegFrac_monthly => summa1_struc%greenVegFrac_monthly, & ! fraction of green vegetation in each month (0-1) + summaFileManagerFile => summa1_struc%summaFileManagerFile & ! path/name of file defining directories and files + + ) ! assignment to variables in the data structures + ! --------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='summa_initialize/' + + ! initialize the start of the initialization + call date_and_time(values=startInit) + + ! ***************************************************************************** + ! *** inital priming -- get command line arguments, identify files, etc. + ! ***************************************************************************** + + ! initialize the netcdf file id + ncid(:) = integerMissing + + ! initialize the elapsed time for cumulative quantities + elapsedRead=0._rkind + elapsedWrite=0._rkind + elapsedPhysics=0._rkind + + ! get the command line arguments + call getCommandArguments(summa1_struc,err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! set directories and files -- summaFileManager used as command-line argument + call summa_SetTimesDirsAndFiles(summaFileManagerFile,err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! define global data (parameters, metadata) + call summa_defineGlobalData(err, cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! ***************************************************************************** + ! *** read the number of GRUs and HRUs + ! ***************************************************************************** + ! obtain the HRU and GRU dimensions in the LocalAttribute file + attrFile = trim(SETTINGS_PATH)//trim(LOCAL_ATTRIBUTES) + select case (iRunMode) + case(iRunModeFull); call read_dimension(trim(attrFile),fileGRU,fileHRU,nGRU,nHRU,err,cmessage) + case(iRunModeGRU ); call read_dimension(trim(attrFile),fileGRU,fileHRU,nGRU,nHRU,err,cmessage,startGRU=startGRU) + case(iRunModeHRU ); call read_dimension(trim(attrFile),fileGRU,fileHRU,nGRU,nHRU,err,cmessage,checkHRU=checkHRU) + end select + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! ***************************************************************************** + ! *** read the number of snow and soil layers + ! ***************************************************************************** + ! set restart filename and read the number of snow and soil layers from the initial conditions (restart) file + if(STATE_PATH == '') then + restartFile = trim(SETTINGS_PATH)//trim(MODEL_INITCOND) + else + restartFile = trim(STATE_PATH)//trim(MODEL_INITCOND) + endif + call read_icond_nlayers(trim(restartFile),nGRU,indx_meta,err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! ***************************************************************************** + ! *** allocate space for data structures + ! ***************************************************************************** + + ! allocate time structures + do iStruct=1,4 + select case(iStruct) + case(1); call allocLocal(time_meta, startTime, err=err, message=cmessage) ! start time for the model simulation + case(2); call allocLocal(time_meta, finshTime, err=err, message=cmessage) ! end time for the model simulation + case(3); call allocLocal(time_meta, refTime, err=err, message=cmessage) ! reference time for the model simulation + case(4); call allocLocal(time_meta, oldTime, err=err, message=cmessage) ! time from the previous step + end select + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + end do ! looping through time structures + + ! allocate other data structures + do iStruct=1,size(structInfo) + ! allocate space + select case(trim(structInfo(iStruct)%structName)) + case('time' ); call allocGlobal(time_meta, timeStruct, err, cmessage) ! model forcing data + case('forc' ); call allocGlobal(forc_meta, forcStruct, err, cmessage) ! model forcing data + case('attr' ); call allocGlobal(attr_meta, attrStruct, err, cmessage) ! local attributes for each HRU + case('type' ); call allocGlobal(type_meta, typeStruct, err, cmessage) ! local classification of soil veg etc. for each HRU + case('id' ); call allocGlobal(id_meta, idStruct, err, message) ! local values of hru and gru IDs + case('mpar' ); call allocGlobal(mpar_meta, mparStruct, err, cmessage) ! model parameters + case('indx' ); call allocGlobal(indx_meta, indxStruct, err, cmessage) ! model variables + case('prog' ); call allocGlobal(prog_meta, progStruct, err, cmessage) ! model prognostic (state) variables + case('diag' ); call allocGlobal(diag_meta, diagStruct, err, cmessage) ! model diagnostic variables + case('flux' ); call allocGlobal(flux_meta, fluxStruct, err, cmessage) ! model fluxes + case('bpar' ); call allocGlobal(bpar_meta, bparStruct, err, cmessage) ! basin-average parameters + case('bvar' ); call allocGlobal(bvar_meta, bvarStruct, err, cmessage) ! basin-average variables + case('lookup'); call allocGlobal(lookup_meta, lookupStruct, err, cmessage) ! basin-average variables + case('deriv' ); cycle + case default; err=20; message='unable to find structure name: '//trim(structInfo(iStruct)%structName) + end select + ! check errors + if(err/=0)then + message=trim(message)//trim(cmessage)//'[structure = '//trim(structInfo(iStruct)%structName)//']' + return + endif + end do ! looping through data structures + + ! allocate space for default model parameters + ! NOTE: This is done here, rather than in the loop above, because dpar is not one of the "standard" data structures + call allocGlobal(mpar_meta,dparStruct,err,cmessage) ! default model parameters + if(err/=0)then + message=trim(message)//trim(cmessage)//' [problem allocating dparStruct]' + return + endif + + ! allocate space for the time step and computeVegFlux flags (recycled for each GRU for subsequent model calls) + allocate(dt_init%gru(nGRU),upArea%gru(nGRU),computeVegFlux%gru(nGRU),stat=err) + if(err/=0)then + message=trim(message)//'problem allocating space for dt_init, upArea, or computeVegFlux [GRU]' + return + endif + + ! allocate space for the HRUs + do iGRU=1,nGRU + hruCount = gru_struc(iGRU)%hruCount ! gru_struc populated in "read_dimension" + allocate(dt_init%gru(iGRU)%hru(hruCount),upArea%gru(iGRU)%hru(hruCount),computeVegFlux%gru(iGRU)%hru(hruCount),stat=err) + if(err/=0)then + message='problem allocating space for dt_init, upArea, or computeVegFlux [HRU]' + return + endif + end do + + ! ***************************************************************************** + ! *** allocate space for output statistics data structures + ! ***************************************************************************** + + ! loop through data structures + do iStruct=1,size(structInfo) + + ! allocate space + select case(trim(structInfo(iStruct)%structName)) + case('forc'); call allocGlobal(statForc_meta(:)%var_info,forcStat,err,cmessage) ! model forcing data + case('prog'); call allocGlobal(statProg_meta(:)%var_info,progStat,err,cmessage) ! model prognostic (state) variables + case('diag'); call allocGlobal(statDiag_meta(:)%var_info,diagStat,err,cmessage) ! model diagnostic variables + case('flux'); call allocGlobal(statFlux_meta(:)%var_info,fluxStat,err,cmessage) ! model fluxes + case('indx'); call allocGlobal(statIndx_meta(:)%var_info,indxStat,err,cmessage) ! index vars + case('bvar'); call allocGlobal(statBvar_meta(:)%var_info,bvarStat,err,cmessage) ! basin-average variables + case default; cycle + end select + + ! check errors + if(err/=0)then + message=trim(message)//trim(cmessage)//'[statistics for = '//trim(structInfo(iStruct)%structName)//']' + return + endif + + end do ! iStruct + + ! ***************************************************************************** + ! *** define the suffix for the model output file + ! ***************************************************************************** + ! set up the output file names as: OUTPUT_PREFIX'_'output_fileSuffix'_'startGRU-endGRU_outfreq.nc or OUTPUT_PREFIX'_'output_fileSuffix'_'HRU_outfreq.nc; + if (output_fileSuffix(1:1) /= '_') output_fileSuffix='_'//trim(output_fileSuffix) ! separate output_fileSuffix from others by underscores + if (output_fileSuffix(len_trim(output_fileSuffix):len_trim(output_fileSuffix)) == '_') output_fileSuffix(len_trim(output_fileSuffix):len_trim(output_fileSuffix)) = ' ' + select case (iRunMode) + case(iRunModeGRU) + ! left zero padding for startGRU and endGRU + write(fmtGruOutput,"(i0)") ceiling(log10(real(fileGRU)+0.1)) ! maximum width of startGRU and endGRU + fmtGruOutput = "i"//trim(fmtGruOutput)//"."//trim(fmtGruOutput) ! construct the format string for startGRU and endGRU + fmtGruOutput = "('_G',"//trim(fmtGruOutput)//",'-',"//trim(fmtGruOutput)//")" + write(output_fileSuffix((len_trim(output_fileSuffix)+1):len(output_fileSuffix)),fmtGruOutput) startGRU,startGRU+nGRU-1 + case(iRunModeHRU) + write(output_fileSuffix((len_trim(output_fileSuffix)+1):len(output_fileSuffix)),"('_H',i0)") checkHRU + end select + + ! identify the end of the initialization + call date_and_time(values=endInit) + + ! aggregate the elapsed time for the initialization + elapsedInit = elapsedSec(startInit, endInit) + + ! end associate statements + end associate summaVars + +end subroutine summa_initialize end module summa_init From dc617b7138d2f3f69e77415a88cec89e3bbbd334 Mon Sep 17 00:00:00 2001 From: Kyle Date: Wed, 28 Sep 2022 17:10:16 +0000 Subject: [PATCH 0379/1472] fixed indentattion --- build/source/hookup/summaFileManager.f90 | 170 +++++++++++------------ 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/build/source/hookup/summaFileManager.f90 b/build/source/hookup/summaFileManager.f90 index 3170a12d4..3e2343860 100755 --- a/build/source/hookup/summaFileManager.f90 +++ b/build/source/hookup/summaFileManager.f90 @@ -56,100 +56,100 @@ MODULE summaFileManager contains - ! ************************************************************************************************** - ! public subroutine summa_SetTimesDirsAndFiles: Sets times, directories and filenames for summa run - ! ************************************************************************************************** - subroutine summa_SetTimesDirsAndFiles(summaFileManagerIn,err,message) - ! Purpose: Sets run times, directories and filenames for summa. - ! --- - USE ascii_util_module,only:file_open ! function to open file - USE ascii_util_module,only:linewidth ! max character number for one line - USE ascii_util_module,only:get_vlines ! function to get a vector of non-comment lines +! ************************************************************************************************** +! public subroutine summa_SetTimesDirsAndFiles: Sets times, directories and filenames for summa run +! ************************************************************************************************** +subroutine summa_SetTimesDirsAndFiles(summaFileManagerIn,err,message) + ! Purpose: Sets run times, directories and filenames for summa. + ! --- + USE ascii_util_module,only:file_open ! function to open file + USE ascii_util_module,only:linewidth ! max character number for one line + USE ascii_util_module,only:get_vlines ! function to get a vector of non-comment lines - implicit none + implicit none - ! input/output vars - character(*),intent(in) :: summaFileManagerIn - integer(i4b),intent(out) :: err - character(*),intent(out) :: message - ! local vars - character(*),parameter :: summaFileManagerHeader='SUMMA_FILE_MANAGER_V3.0.0' - integer(i4b),parameter :: runinfo_fileunit=67 ! file unit for run time information - character(len=8) :: cdate - character(len=10) :: ctime - character(len=256) :: cmessage ! error message for downwind routine - integer(i4b) :: unt ! file unit (free unit output from file_open) - character(LEN=linewidth),allocatable :: charline(:) ! vector of character strings - integer(i4b) :: iControl, nControl ! number of model info - character(len=summaPathLen) :: varEntry ! name of model info - character(len=32) :: option ! option for model info + ! input/output vars + character(*),intent(in) :: summaFileManagerIn + integer(i4b),intent(out) :: err + character(*),intent(out) :: message + ! local vars + character(*),parameter :: summaFileManagerHeader='SUMMA_FILE_MANAGER_V3.0.0' + integer(i4b),parameter :: runinfo_fileunit=67 ! file unit for run time information + character(len=8) :: cdate + character(len=10) :: ctime + character(len=256) :: cmessage ! error message for downwind routine + integer(i4b) :: unt ! file unit (free unit output from file_open) + character(LEN=linewidth),allocatable :: charline(:) ! vector of character strings + integer(i4b) :: iControl, nControl ! number of model info + character(len=summaPathLen) :: varEntry ! name of model info + character(len=32) :: option ! option for model info - err=0; message="summa_SetTimesDirsAndFiles/" + err=0; message="summa_SetTimesDirsAndFiles/" - ! read information from model control file, and populate model control structure - ! populates global control information structure + ! read information from model control file, and populate model control structure + ! populates global control information structure - ! open file, read non-comment lines, close file - call file_open(trim(summaFileManagerIn),unt,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage)//"/Failed to open control file [''"//trim(summaFileManagerIn)//"']"; err=-10; return; end if - call get_vlines(unt,charline,err,cmessage) ! 'charline' is a list of strings from non-comment lines - if(err/=0) then; message=trim(message)//trim(cmessage)//"/Control file read issue in get_vlines()"; return; end if - close(unt) + ! open file, read non-comment lines, close file + call file_open(trim(summaFileManagerIn),unt,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage)//"/Failed to open control file [''"//trim(summaFileManagerIn)//"']"; err=-10; return; end if + call get_vlines(unt,charline,err,cmessage) ! 'charline' is a list of strings from non-comment lines + if(err/=0) then; message=trim(message)//trim(cmessage)//"/Control file read issue in get_vlines()"; return; end if + close(unt) - ! get the number of model control file entries - nControl = size(charline) + ! get the number of model control file entries + nControl = size(charline) - ! populate the model control info structure - do iControl=1,nControl - ! extract name of decision and the decision selected - read(charline(iControl),*,iostat=err) option, varEntry - if (err/=0) then; err=30; message=trim(message)//"error reading charline array"; return; end if - ! get the index of the control file entry in the data structure - write(*,'(i4,1x,a)') iControl, trim(option)//': '//trim(varEntry) + ! populate the model control info structure + do iControl=1,nControl + ! extract name of decision and the decision selected + read(charline(iControl),*,iostat=err) option, varEntry + if (err/=0) then; err=30; message=trim(message)//"error reading charline array"; return; end if + ! get the index of the control file entry in the data structure + write(*,'(i4,1x,a)') iControl, trim(option)//': '//trim(varEntry) - ! assign entries from control file to module public variables; add checking as needed - select case(trim(option)) - case('controlVersion' ); - CONTROL_VRS = trim(varEntry); - if(trim(varEntry)/=trim(summaFileManagerHeader)) then - message=trim(message)//"unknown control file version in '"//trim(summaFileManagerIn)//" looking for "//trim(summaFileManagerHeader) - err=20 - return - end if - case('simStartTime' ); SIM_START_TM = trim(varEntry) ! start simulation time - case('simEndTime' ); SIM_END_TM = trim(varEntry) ! end simulation time - case('tmZoneInfo' ); NC_TIME_ZONE = trim(varEntry) ! time zone info - case('settingsPath' ); SETTINGS_PATH = trim(varEntry) ! settings directory - case('forcingPath' ); FORCING_PATH = trim(varEntry) ! input forcing directory - case('outputPath' ); OUTPUT_PATH = trim(varEntry) ! output directory - case('statePath' ); STATE_PATH = trim(varEntry) ! state file input/output directory - case('decisionsFile' ); M_DECISIONS = trim(varEntry) ! model decisions file - case('outputControlFile' ); OUTPUT_CONTROL = trim(varEntry) ! output control file - case('globalHruParamFile' ); LOCALPARAM_INFO = trim(varEntry) ! default/global hru-level param file - case('globalGruParamFile' ); BASINPARAM_INFO = trim(varEntry) ! default/global gru-level param file - case('attributeFile' ); LOCAL_ATTRIBUTES = trim(varEntry) ! attribute file - case('trialParamFile' ); PARAMETER_TRIAL = trim(varEntry) ! trial parameters file (hru and/or gru) - case('vegTableFile' ); VEGPARM = trim(varEntry) ! vegetation parameter table - case('soilTableFile' ); SOILPARM = trim(varEntry) ! soil parameter table - case('generalTableFile' ); GENPARM = trim(varEntry) ! general parameter table - case('noahmpTableFile' ); MPTABLE = trim(varEntry) ! noah mp parameter table - case('forcingListFile' ); FORCING_FILELIST = trim(varEntry) ! file listing forcing filenames - case('initConditionFile' ); MODEL_INITCOND = trim(varEntry) ! initial conditions file (cold State) - case('outFilePrefix' ); OUTPUT_PREFIX = trim(varEntry) ! filename root for output files - ! get to here if cannot find the variable - case default - err=10; message=trim(message)//"unknown control file option: "//trim(option); return - end select - end do + ! assign entries from control file to module public variables; add checking as needed + select case(trim(option)) + case('controlVersion' ); + CONTROL_VRS = trim(varEntry); + if(trim(varEntry)/=trim(summaFileManagerHeader)) then + message=trim(message)//"unknown control file version in '"//trim(summaFileManagerIn)//" looking for "//trim(summaFileManagerHeader) + err=20 + return + end if + case('simStartTime' ); SIM_START_TM = trim(varEntry) ! start simulation time + case('simEndTime' ); SIM_END_TM = trim(varEntry) ! end simulation time + case('tmZoneInfo' ); NC_TIME_ZONE = trim(varEntry) ! time zone info + case('settingsPath' ); SETTINGS_PATH = trim(varEntry) ! settings directory + case('forcingPath' ); FORCING_PATH = trim(varEntry) ! input forcing directory + case('outputPath' ); OUTPUT_PATH = trim(varEntry) ! output directory + case('statePath' ); STATE_PATH = trim(varEntry) ! state file input/output directory + case('decisionsFile' ); M_DECISIONS = trim(varEntry) ! model decisions file + case('outputControlFile' ); OUTPUT_CONTROL = trim(varEntry) ! output control file + case('globalHruParamFile' ); LOCALPARAM_INFO = trim(varEntry) ! default/global hru-level param file + case('globalGruParamFile' ); BASINPARAM_INFO = trim(varEntry) ! default/global gru-level param file + case('attributeFile' ); LOCAL_ATTRIBUTES = trim(varEntry) ! attribute file + case('trialParamFile' ); PARAMETER_TRIAL = trim(varEntry) ! trial parameters file (hru and/or gru) + case('vegTableFile' ); VEGPARM = trim(varEntry) ! vegetation parameter table + case('soilTableFile' ); SOILPARM = trim(varEntry) ! soil parameter table + case('generalTableFile' ); GENPARM = trim(varEntry) ! general parameter table + case('noahmpTableFile' ); MPTABLE = trim(varEntry) ! noah mp parameter table + case('forcingListFile' ); FORCING_FILELIST = trim(varEntry) ! file listing forcing filenames + case('initConditionFile' ); MODEL_INITCOND = trim(varEntry) ! initial conditions file (cold State) + case('outFilePrefix' ); OUTPUT_PREFIX = trim(varEntry) ! filename root for output files + ! get to here if cannot find the variable + case default + err=10; message=trim(message)//"unknown control file option: "//trim(option); return + end select + end do - ! before embarking on a run, check that the output directory is writable; write system date and time to a log file there - open(runinfo_fileunit,file=trim(OUTPUT_PATH)//"runinfo.txt",iostat=err) - if(err/=0)then; err=10; message=trim(message)//"cannot write to output directory '"//trim(OUTPUT_PATH)//"'"; return; end if - call date_and_time(cdate,ctime) - write(runinfo_fileunit,*) 'Run start time on system: ccyy='//cdate(1:4)//' - mm='//cdate(5:6)//' - dd='//cdate(7:8), & - ' - hh='//ctime(1:2)//' - mi='//ctime(3:4)//' - ss='//ctime(5:10) - close(runinfo_fileunit) + ! before embarking on a run, check that the output directory is writable; write system date and time to a log file there + open(runinfo_fileunit,file=trim(OUTPUT_PATH)//"runinfo.txt",iostat=err) + if(err/=0)then; err=10; message=trim(message)//"cannot write to output directory '"//trim(OUTPUT_PATH)//"'"; return; end if + call date_and_time(cdate,ctime) + write(runinfo_fileunit,*) 'Run start time on system: ccyy='//cdate(1:4)//' - mm='//cdate(5:6)//' - dd='//cdate(7:8), & + ' - hh='//ctime(1:2)//' - mi='//ctime(3:4)//' - ss='//ctime(5:10) + close(runinfo_fileunit) - end subroutine summa_SetTimesDirsAndFiles +end subroutine summa_SetTimesDirsAndFiles END MODULE summaFileManager From 85ef0951949f8c5adcad8dc42d846b584403f559 Mon Sep 17 00:00:00 2001 From: Kyle Date: Wed, 28 Sep 2022 17:12:19 +0000 Subject: [PATCH 0380/1472] fixed indentation --- build/source/driver/summa_globalData.f90 | 196 +++++++++++------------ 1 file changed, 98 insertions(+), 98 deletions(-) diff --git a/build/source/driver/summa_globalData.f90 b/build/source/driver/summa_globalData.f90 index b3f5cfd9a..058a00a88 100644 --- a/build/source/driver/summa_globalData.f90 +++ b/build/source/driver/summa_globalData.f90 @@ -62,103 +62,103 @@ module summa_globalData public::summa_defineGlobalData contains - subroutine summa_defineGlobalData(err, message) - ! --------------------------------------------------------------------------------------- - ! * desired modules - ! --------------------------------------------------------------------------------------- - ! data types - USE nrtype ! variable types, etc. - ! subroutines and functions: initial priming - USE,intrinsic :: ieee_arithmetic ! IEEE arithmetic (obviously) - ! subroutines and functions: define metadata structures - USE popMetadat_module,only:popMetadat ! module to populate metadata structures - USE flxMapping_module,only:flxMapping ! module to map fluxes to states - USE checkStruc_module,only:checkStruc ! module to check metadata structures - USE childStruc_module,only:childStruc ! module to create a child data structure - ! miscellaneous global data - USE globalData,only:dNaN ! double precision NaN - USE globalData,only:doJacobian ! flag to compute the Jacobian - USE globalData,only:structInfo ! information on the data structures - ! named variables that describe elements of child model structures - USE var_lookup,only:iLookVarType ! look-up values for variable type structure - USE var_lookup,only:childFLUX_MEAN ! look-up values for timestep-average model fluxes - ! --------------------------------------------------------------------------------------- - ! * variables - ! --------------------------------------------------------------------------------------- - implicit none - ! dummy variables - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! local variables - character(LEN=256) :: cmessage ! error message of downwind routine - logical(lgt), dimension(maxvarFlux) :: flux_mask ! mask defining desired flux variables - logical(lgt), dimension(maxvarForc) :: statForc_mask ! mask defining forc stats - logical(lgt), dimension(maxvarProg) :: statProg_mask ! mask defining prog stats - logical(lgt), dimension(maxvarDiag) :: statDiag_mask ! mask defining diag stats - logical(lgt), dimension(maxvarFlux) :: statFlux_mask ! mask defining flux stats - logical(lgt), dimension(maxvarIndx) :: statIndx_mask ! mask defining indx stats - logical(lgt), dimension(maxvarBvar) :: statBvar_mask ! mask defining bvar stats - integer(i4b) :: iStruct ! index of data structure - ! --------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='summa_defineGlobalData/' - - ! initialize the Jacobian flag - doJacobian=.false. ! initialize the Jacobian flag - - ! define double precision NaNs (shared in globalData) - dNaN = ieee_value(1._rkind, ieee_quiet_nan) - - ! populate metadata for all model variables - call popMetadat(err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! define mapping between fluxes and states - call flxMapping(err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! check data structures - call checkStruc(err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! define the mask to identify the subset of variables in the "child" data structure (just scalar variables) - flux_mask = (flux_meta(:)%vartype==iLookVarType%scalarv) - - ! create the averageFlux metadata structure - call childStruc(flux_meta, flux_mask, averageFlux_meta, childFLUX_MEAN, err, cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! child metadata structures - so that we do not carry full stats structures around everywhere - ! only carry stats for variables with output frequency > model time step - statForc_mask = (forc_meta(:)%vartype==iLookVarType%scalarv.and.forc_meta(:)%varDesire) - statProg_mask = (prog_meta(:)%vartype==iLookVarType%scalarv.and.prog_meta(:)%varDesire) - statDiag_mask = (diag_meta(:)%vartype==iLookVarType%scalarv.and.diag_meta(:)%varDesire) - statFlux_mask = (flux_meta(:)%vartype==iLookVarType%scalarv.and.flux_meta(:)%varDesire) - statIndx_mask = (indx_meta(:)%vartype==iLookVarType%scalarv.and.indx_meta(:)%varDesire) - statBvar_mask = (bvar_meta(:)%vartype==iLookVarType%scalarv.and.bvar_meta(:)%varDesire) - - ! create the stats metadata structures - do iStruct=1,size(structInfo) - select case (trim(structInfo(iStruct)%structName)) - case('forc'); call childStruc(forc_meta,statForc_mask,statForc_meta,forcChild_map,err,cmessage) - case('prog'); call childStruc(prog_meta,statProg_mask,statProg_meta,progChild_map,err,cmessage) - case('diag'); call childStruc(diag_meta,statDiag_mask,statDiag_meta,diagChild_map,err,cmessage) - case('flux'); call childStruc(flux_meta,statFlux_mask,statFlux_meta,fluxChild_map,err,cmessage) - case('indx'); call childStruc(indx_meta,statIndx_mask,statIndx_meta,indxChild_map,err,cmessage) - case('bvar'); call childStruc(bvar_meta,statBvar_mask,statBvar_meta,bvarChild_map,err,cmessage) - end select - ! check errors - if(err/=0)then; message=trim(message)//trim(cmessage)//'[statistics for = '//trim(structInfo(iStruct)%structName)//']'; return; endif - end do ! iStruct - - ! set all stats metadata to correct var types - statForc_meta(:)%vartype = iLookVarType%outstat - statProg_meta(:)%vartype = iLookVarType%outstat - statDiag_meta(:)%vartype = iLookVarType%outstat - statFlux_meta(:)%vartype = iLookVarType%outstat - statIndx_meta(:)%vartype = iLookVarType%outstat - statBvar_meta(:)%vartype = iLookVarType%outstat - - end subroutine summa_defineGlobalData +subroutine summa_defineGlobalData(err, message) + ! --------------------------------------------------------------------------------------- + ! * desired modules + ! --------------------------------------------------------------------------------------- + ! data types + USE nrtype ! variable types, etc. + ! subroutines and functions: initial priming + USE,intrinsic :: ieee_arithmetic ! IEEE arithmetic (obviously) + ! subroutines and functions: define metadata structures + USE popMetadat_module,only:popMetadat ! module to populate metadata structures + USE flxMapping_module,only:flxMapping ! module to map fluxes to states + USE checkStruc_module,only:checkStruc ! module to check metadata structures + USE childStruc_module,only:childStruc ! module to create a child data structure + ! miscellaneous global data + USE globalData,only:dNaN ! double precision NaN + USE globalData,only:doJacobian ! flag to compute the Jacobian + USE globalData,only:structInfo ! information on the data structures + ! named variables that describe elements of child model structures + USE var_lookup,only:iLookVarType ! look-up values for variable type structure + USE var_lookup,only:childFLUX_MEAN ! look-up values for timestep-average model fluxes + ! --------------------------------------------------------------------------------------- + ! * variables + ! --------------------------------------------------------------------------------------- + implicit none + ! dummy variables + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local variables + character(LEN=256) :: cmessage ! error message of downwind routine + logical(lgt), dimension(maxvarFlux) :: flux_mask ! mask defining desired flux variables + logical(lgt), dimension(maxvarForc) :: statForc_mask ! mask defining forc stats + logical(lgt), dimension(maxvarProg) :: statProg_mask ! mask defining prog stats + logical(lgt), dimension(maxvarDiag) :: statDiag_mask ! mask defining diag stats + logical(lgt), dimension(maxvarFlux) :: statFlux_mask ! mask defining flux stats + logical(lgt), dimension(maxvarIndx) :: statIndx_mask ! mask defining indx stats + logical(lgt), dimension(maxvarBvar) :: statBvar_mask ! mask defining bvar stats + integer(i4b) :: iStruct ! index of data structure + ! --------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='summa_defineGlobalData/' + + ! initialize the Jacobian flag + doJacobian=.false. ! initialize the Jacobian flag + + ! define double precision NaNs (shared in globalData) + dNaN = ieee_value(1._rkind, ieee_quiet_nan) + + ! populate metadata for all model variables + call popMetadat(err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! define mapping between fluxes and states + call flxMapping(err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! check data structures + call checkStruc(err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! define the mask to identify the subset of variables in the "child" data structure (just scalar variables) + flux_mask = (flux_meta(:)%vartype==iLookVarType%scalarv) + + ! create the averageFlux metadata structure + call childStruc(flux_meta, flux_mask, averageFlux_meta, childFLUX_MEAN, err, cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! child metadata structures - so that we do not carry full stats structures around everywhere + ! only carry stats for variables with output frequency > model time step + statForc_mask = (forc_meta(:)%vartype==iLookVarType%scalarv.and.forc_meta(:)%varDesire) + statProg_mask = (prog_meta(:)%vartype==iLookVarType%scalarv.and.prog_meta(:)%varDesire) + statDiag_mask = (diag_meta(:)%vartype==iLookVarType%scalarv.and.diag_meta(:)%varDesire) + statFlux_mask = (flux_meta(:)%vartype==iLookVarType%scalarv.and.flux_meta(:)%varDesire) + statIndx_mask = (indx_meta(:)%vartype==iLookVarType%scalarv.and.indx_meta(:)%varDesire) + statBvar_mask = (bvar_meta(:)%vartype==iLookVarType%scalarv.and.bvar_meta(:)%varDesire) + + ! create the stats metadata structures + do iStruct=1,size(structInfo) + select case (trim(structInfo(iStruct)%structName)) + case('forc'); call childStruc(forc_meta,statForc_mask,statForc_meta,forcChild_map,err,cmessage) + case('prog'); call childStruc(prog_meta,statProg_mask,statProg_meta,progChild_map,err,cmessage) + case('diag'); call childStruc(diag_meta,statDiag_mask,statDiag_meta,diagChild_map,err,cmessage) + case('flux'); call childStruc(flux_meta,statFlux_mask,statFlux_meta,fluxChild_map,err,cmessage) + case('indx'); call childStruc(indx_meta,statIndx_mask,statIndx_meta,indxChild_map,err,cmessage) + case('bvar'); call childStruc(bvar_meta,statBvar_mask,statBvar_meta,bvarChild_map,err,cmessage) + end select + ! check errors + if(err/=0)then; message=trim(message)//trim(cmessage)//'[statistics for = '//trim(structInfo(iStruct)%structName)//']'; return; endif + end do ! iStruct + + ! set all stats metadata to correct var types + statForc_meta(:)%vartype = iLookVarType%outstat + statProg_meta(:)%vartype = iLookVarType%outstat + statDiag_meta(:)%vartype = iLookVarType%outstat + statFlux_meta(:)%vartype = iLookVarType%outstat + statIndx_meta(:)%vartype = iLookVarType%outstat + statBvar_meta(:)%vartype = iLookVarType%outstat + +end subroutine summa_defineGlobalData end module summa_globalData From a1f55b5cdfba7cec0940dbab169e58a96f973373 Mon Sep 17 00:00:00 2001 From: Kyle Date: Wed, 28 Sep 2022 17:19:25 +0000 Subject: [PATCH 0381/1472] fixed indentation --- build/source/dshare/popMetadat.f90 | 2087 ++++++++++++++-------------- 1 file changed, 1042 insertions(+), 1045 deletions(-) diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 2acc7f59b..6dc139653 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -15,1068 +15,1065 @@ module popMetadat_module public::popMetadat contains - subroutine popMetadat(err,message) - ! data structures - USE data_types, only: var_info ! data type for metadata structure - USE globalData, only: time_meta ! data structure for time metadata - USE globalData, only: forc_meta ! data structure for forcing metadata - USE globalData, only: type_meta ! data structure for categorical metadata - USE globalData, only: id_meta ! data structure for hru and gru ID metadata - USE globalData, only: attr_meta ! data structure for attribute metadata - USE globalData, only: mpar_meta ! data structure for local parameter metadata - USE globalData, only: bpar_meta ! data structure for basin parameter metadata - USE globalData, only: bvar_meta ! data structure for basin model variable metadata - USE globalData, only: indx_meta ! data structure for index metadata - USE globalData, only: prog_meta ! data structure for local prognostic (state) variables - USE globalData, only: diag_meta ! data structure for local diagnostic variables - USE globalData, only: flux_meta ! data structure for local flux variables - USE globalData, only: deriv_meta ! data structure for local flux derivatives - USE globalData, only: lookup_meta ! data structure for lookup tables - ! structures of named variables - USE var_lookup, only: iLookTIME ! named variables for time data structure - USE var_lookup, only: iLookFORCE ! named variables for forcing data structure - USE var_lookup, only: iLookTYPE ! named variables for categorical attribute data structure - USE var_lookup, only: iLookID ! named variables for hru and gru ID metadata - USE var_lookup, only: iLookATTR ! named variables for real valued attribute data structure - USE var_lookup, only: iLookPARAM ! named variables for local parameter data structure - USE var_lookup, only: iLookBPAR ! named variables for basin parameter data structure - USE var_lookup, only: iLookBVAR ! named variables for basin model variable data structure - USE var_lookup, only: iLookINDEX ! named variables for index variable data structure - USE var_lookup, only: iLookPROG ! named variables for local state variables - USE var_lookup, only: iLookDIAG ! named variables for local diagnostic variables - USE var_lookup, only: iLookFLUX ! named variables for local flux variables - USE var_lookup, only: iLookDERIV ! named variables for local flux derivatives - USE var_lookup, only: iLookLOOKUP ! named variables for lookup tables - USE var_lookup, only: maxvarFreq ! number of output frequencies - USE var_lookup, only: maxvarStat ! number of statistics - USE get_ixName_module,only:get_ixVarType ! to turn vartype strings to integers - implicit none - ! dummy variables - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! internals - character(256) :: cmessage ! error message - integer,dimension(maxVarFreq) :: iMissVec ! vector of missing integers - ! initialize error control - err=0; message='popMetadat/' - - ! init arrays for structure constructors - iMissVec(:) = integerMissing - ! ----- - ! * model time structures... - ! -------------------------- - time_meta(iLookTIME%iyyy) = var_info('iyyy' , 'year' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - time_meta(iLookTIME%im) = var_info('im' , 'month' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - time_meta(iLookTIME%id) = var_info('id' , 'day' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - time_meta(iLookTIME%ih) = var_info('ih' , 'hour' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - time_meta(iLookTIME%imin) = var_info('imin' , 'minute' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - time_meta(iLookTIME%ih_tz) = var_info('ih_tz' , 'hour for time zone offset' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - time_meta(iLookTIME%imin_tz) = var_info('imin_tz', 'minute for time zone offset', '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - - ! ----- - ! * model forcing data... - ! ----------------------- - forc_meta(iLookFORCE%time) = var_info('time' , 'time since time reference' , 'seconds since 1990-1-1 0:0:0.0 -0:00', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - forc_meta(iLookFORCE%pptrate) = var_info('pptrate' , 'precipitation rate' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - forc_meta(iLookFORCE%SWRadAtm) = var_info('SWRadAtm', 'downward shortwave radiation at the upper boundary', 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - forc_meta(iLookFORCE%LWRadAtm) = var_info('LWRadAtm', 'downward longwave radiation at the upper boundary' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - forc_meta(iLookFORCE%airtemp) = var_info('airtemp' , 'air temperature at the measurement height' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - forc_meta(iLookFORCE%windspd) = var_info('windspd' , 'wind speed at the measurement height' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - forc_meta(iLookFORCE%airpres) = var_info('airpres' , 'air pressure at the the measurement height' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - forc_meta(iLookFORCE%spechum) = var_info('spechum' , 'specific humidity at the measurement height' , 'g g-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - - ! ----- - ! * categorical data... - ! --------------------- - type_meta(iLookTYPE%vegTypeIndex) = var_info('vegTypeIndex' , 'index defining vegetation type' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - type_meta(iLookTYPE%soilTypeIndex) = var_info('soilTypeIndex' , 'index defining soil type' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - type_meta(iLookTYPE%slopeTypeIndex) = var_info('slopeTypeIndex', 'index defining slope' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - type_meta(iLookTYPE%downHRUindex) = var_info('downHRUindex' , 'index of downslope HRU (0 = basin outlet)' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - - ! ----- - ! * hru and gru ID data... - ! --------------------- - id_meta(iLookID%hruId) = var_info('hruId' , 'ID defining the hydrologic response unit' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - - ! ----- - ! * site characteristics... - ! ------------------------- - attr_meta(iLookATTR%latitude) = var_info('latitude' , 'latitude' , 'degrees north', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - attr_meta(iLookATTR%longitude) = var_info('longitude' , 'longitude' , 'degrees east' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - attr_meta(iLookATTR%elevation) = var_info('elevation' , 'elevation' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - attr_meta(iLookATTR%tan_slope) = var_info('tan_slope' , 'tan water table slope (tan local ground surface slope)', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - attr_meta(iLookATTR%contourLength) = var_info('contourLength' , 'length of contour at downslope edge of HRU' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - attr_meta(iLookATTR%HRUarea) = var_info('HRUarea' , 'area of each HRU' , 'm2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - attr_meta(iLookATTR%mHeight) = var_info('mHeight' , 'measurement height above bare ground' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - attr_meta(iLookATTR%aspect) = var_info('aspect' , 'mean azimuth of HRU in degrees East of North (0)' , 'degrees' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - - ! ----- - ! * local parameter data... - ! ------------------------- - ! boundary conditions - mpar_meta(iLookPARAM%upperBoundHead) = var_info('upperBoundHead' , 'matric head at the upper boundary' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%lowerBoundHead) = var_info('lowerBoundHead' , 'matric head at the lower boundary' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%upperBoundTheta) = var_info('upperBoundTheta' , 'volumetric liquid water content at the upper boundary' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%lowerBoundTheta) = var_info('lowerBoundTheta' , 'volumetric liquid water content at the lower boundary' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%upperBoundTemp) = var_info('upperBoundTemp' , 'temperature of the upper boundary' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%lowerBoundTemp) = var_info('lowerBoundTemp' , 'temperature of the lower boundary' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! precipitation partitioning - mpar_meta(iLookPARAM%tempCritRain) = var_info('tempCritRain' , 'critical temperature where precipitation is rain' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%tempRangeTimestep) = var_info('tempRangeTimestep' , 'temperature range over the time step' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%frozenPrecipMultip) = var_info('frozenPrecipMultip' , 'frozen precipitation multiplier' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! snow properties - mpar_meta(iLookPARAM%snowfrz_scale) = var_info('snowfrz_scale' , 'scaling parameter for the freezing curve for snow' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%fixedThermalCond_snow) = var_info('fixedThermalCond_snow' , 'temporally constant thermal conductivity for snow' , 'W m-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! snow albedo - mpar_meta(iLookPARAM%albedoMax) = var_info('albedoMax' , 'maximum snow albedo (single spectral band)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%albedoMinWinter) = var_info('albedoMinWinter' , 'minimum snow albedo during winter (single spectral band)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%albedoMinSpring) = var_info('albedoMinSpring' , 'minimum snow albedo during spring (single spectral band)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%albedoMaxVisible) = var_info('albedoMaxVisible' , 'maximum snow albedo in the visible part of the spectrum' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%albedoMinVisible) = var_info('albedoMinVisible' , 'minimum snow albedo in the visible part of the spectrum' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%albedoMaxNearIR) = var_info('albedoMaxNearIR' , 'maximum snow albedo in the near infra-red part of the spectrum' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%albedoMinNearIR) = var_info('albedoMinNearIR' , 'minimum snow albedo in the near infra-red part of the spectrum' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%albedoDecayRate) = var_info('albedoDecayRate' , 'albedo decay rate' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%albedoSootLoad) = var_info('albedoSootLoad' , 'soot load factor' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%albedoRefresh) = var_info('albedoRefresh' , 'critical mass necessary for albedo refreshment' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! radiation transfer - mpar_meta(iLookPARAM%radExt_snow) = var_info('radExt_snow' , 'extinction coefficient for radiation penetration into snowpack' , 'm-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%directScale) = var_info('directScale' , 'scaling factor for fractional driect radiaion parameterization' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%Frad_direct) = var_info('Frad_direct' , 'fraction direct solar radiation' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%Frad_vis) = var_info('Frad_vis' , 'fraction radiation in visible part of spectrum' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! new snow density - mpar_meta(iLookPARAM%newSnowDenMin) = var_info('newSnowDenMin' , 'minimum new snow density' , 'kg m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%newSnowDenMult) = var_info('newSnowDenMult' , 'multiplier for new snow density' , 'kg m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%newSnowDenScal) = var_info('newSnowDenScal' , 'scaling factor for new snow density' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%constSnowDen) = var_info('constSnowDen' , 'Constant new snow density' , 'kg m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%newSnowDenAdd) = var_info('newSnowDenAdd' , 'Pahaut 1976, additive factor for new snow density' , 'kg m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%newSnowDenMultTemp) = var_info('newSnowDenMultTemp' , 'Pahaut 1976, multiplier for new snow density for air temperature' , 'kg m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%newSnowDenMultWind) = var_info('newSnowDenMultWind' , 'Pahaut 1976, multiplier for new snow density for wind speed' , 'kg m-7/2 s-1/2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%newSnowDenMultAnd) = var_info('newSnowDenMultAnd' , 'Anderson 1976, multiplier for new snow density (Anderson func)' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%newSnowDenBase) = var_info('newSnowDenBase' , 'Anderson 1976, base value that is rasied to the (3/2) power' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! snow compaction - mpar_meta(iLookPARAM%densScalGrowth) = var_info('densScalGrowth' , 'density scaling factor for grain growth' , 'kg-1 m3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%tempScalGrowth) = var_info('tempScalGrowth' , 'temperature scaling factor for grain growth' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%grainGrowthRate) = var_info('grainGrowthRate' , 'rate of grain growth' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%densScalOvrbdn) = var_info('densScalOvrbdn' , 'density scaling factor for overburden pressure' , 'kg-1 m3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%tempScalOvrbdn) = var_info('tempScalOvrbdn' , 'temperature scaling factor for overburden pressure' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%baseViscosity ) = var_info('baseViscosity ' , 'viscosity coefficient at T=T_frz and snow density=0' , 'kg s m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! water flow through snow - mpar_meta(iLookPARAM%Fcapil) = var_info('Fcapil' , 'capillary retention (fraction of total pore volume)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%k_snow) = var_info('k_snow' , 'hydraulic conductivity of snow' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%mw_exp) = var_info('mw_exp' , 'exponent for meltwater flow' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! turbulent heat fluxes - mpar_meta(iLookPARAM%z0Snow) = var_info('z0Snow' , 'roughness length of snow' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%z0Soil) = var_info('z0Soil' , 'roughness length of bare soil below the canopy' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%z0Canopy) = var_info('z0Canopy' , 'roughness length of the canopy' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zpdFraction) = var_info('zpdFraction' , 'zero plane displacement / canopy height' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%critRichNumber) = var_info('critRichNumber' , 'critical value for the bulk Richardson number' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%Louis79_bparam) = var_info('Louis79_bparam' , 'parameter in Louis (1979) stability function' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%Louis79_cStar) = var_info('Louis79_cStar' , 'parameter in Louis (1979) stability function' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%Mahrt87_eScale) = var_info('Mahrt87_eScale' , 'exponential scaling factor in the Mahrt (1987) stability function', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%leafExchangeCoeff) = var_info('leafExchangeCoeff' , 'turbulent exchange coeff between canopy surface and canopy air' , 'm s-(1/2)' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%windReductionParam) = var_info('windReductionParam' , 'canopy wind reduction parameter' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! stomatal conductance - mpar_meta(iLookPARAM%Kc25) = var_info('Kc25' , 'Michaelis-Menten constant for CO2 at 25 degrees C' , 'umol mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%Ko25) = var_info('Ko25' , 'Michaelis-Menten constant for O2 at 25 degrees C' , 'mol mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%Kc_qFac) = var_info('Kc_qFac' , 'factor in the q10 function defining temperature controls on Kc' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%Ko_qFac) = var_info('Ko_qFac' , 'factor in the q10 function defining temperature controls on Ko' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%kc_Ha) = var_info('kc_Ha' , 'activation energy for the Michaelis-Menten constant for CO2' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%ko_Ha) = var_info('ko_Ha' , 'activation energy for the Michaelis-Menten constant for O2' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%vcmax25_canopyTop) = var_info('vcmax25_canopyTop' , 'potential carboxylation rate at 25 degrees C at the canopy top' , 'umol co2 m-2 s-1', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%vcmax_qFac) = var_info('vcmax_qFac' , 'factor in the q10 function defining temperature controls on vcmax', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%vcmax_Ha) = var_info('vcmax_Ha' , 'activation energy in the vcmax function' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%vcmax_Hd) = var_info('vcmax_Hd' , 'deactivation energy in the vcmax function' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%vcmax_Sv) = var_info('vcmax_Sv' , 'entropy term in the vcmax function' , 'J mol-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%vcmax_Kn) = var_info('vcmax_Kn' , 'foliage nitrogen decay coefficient' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%jmax25_scale) = var_info('jmax25_scale' , 'scaling factor to relate jmax25 to vcmax25' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%jmax_Ha) = var_info('jmax_Ha' , 'activation energy in the jmax function' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%jmax_Hd) = var_info('jmax_Hd' , 'deactivation energy in the jmax function' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%jmax_Sv) = var_info('jmax_Sv' , 'entropy term in the jmax function' , 'J mol-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%fractionJ) = var_info('fractionJ' , 'fraction of light lost by other than the chloroplast lamellae' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%quantamYield) = var_info('quantamYield' , 'quantam yield' , 'mol e mol-1 q' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%vpScaleFactor) = var_info('vpScaleFactor' , 'vapor pressure scaling factor in stomatal conductance function' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%cond2photo_slope) = var_info('cond2photo_slope' , 'slope of conductance-photosynthesis relationship' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%minStomatalConductance)= var_info('minStomatalConductance', 'minimum stomatal conductance' , 'umol H2O m-2 s-1', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! vegetation properties - mpar_meta(iLookPARAM%winterSAI) = var_info('winterSAI' , 'stem area index prior to the start of the growing season' , 'm2 m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%summerLAI) = var_info('summerLAI' , 'maximum leaf area index at the peak of the growing season' , 'm2 m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%rootScaleFactor1) = var_info('rootScaleFactor1' , '1st scaling factor (a) in Y = 1 - 0.5*( exp(-aZ) + exp(-bZ) )' , 'm-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%rootScaleFactor2) = var_info('rootScaleFactor2' , '2nd scaling factor (b) in Y = 1 - 0.5*( exp(-aZ) + exp(-bZ) )' , 'm-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%rootingDepth) = var_info('rootingDepth' , 'rooting depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%rootDistExp) = var_info('rootDistExp' , 'exponent for the vertical distribution of root density' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%plantWiltPsi) = var_info('plantWiltPsi' , 'matric head at wilting point' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%soilStressParam) = var_info('soilStressParam' , 'parameter in the exponential soil stress function' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%critSoilWilting) = var_info('critSoilWilting' , 'critical vol. liq. water content when plants are wilting' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%critSoilTranspire) = var_info('critSoilTranspire' , 'critical vol. liq. water content when transpiration is limited' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%critAquiferTranspire) = var_info('critAquiferTranspire' , 'critical aquifer storage value when transpiration is limited' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%minStomatalResistance) = var_info('minStomatalResistance' , 'minimum stomatal resistance' , 's m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%leafDimension) = var_info('leafDimension' , 'characteristic leaf dimension' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%heightCanopyTop) = var_info('heightCanopyTop' , 'height of top of the vegetation canopy above ground surface' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%heightCanopyBottom) = var_info('heightCanopyBottom' , 'height of bottom of the vegetation canopy above ground surface' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%specificHeatVeg) = var_info('specificHeatVeg' , 'specific heat of vegetation' , 'J kg-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%maxMassVegetation) = var_info('maxMassVegetation' , 'maximum mass of vegetation (full foliage)' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%throughfallScaleSnow) = var_info('throughfallScaleSnow' , 'scaling factor for throughfall (snow)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%throughfallScaleRain) = var_info('throughfallScaleRain' , 'scaling factor for throughfall (rain)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%refInterceptCapSnow) = var_info('refInterceptCapSnow' , 'reference canopy interception capacity per unit leaf area (snow)' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%refInterceptCapRain) = var_info('refInterceptCapRain' , 'canopy interception capacity per unit leaf area (rain)' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%snowUnloadingCoeff) = var_info('snowUnloadingCoeff' , 'time constant for unloading of snow from the forest canopy' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%canopyDrainageCoeff) = var_info('canopyDrainageCoeff' , 'time constant for drainage of liquid water from the forest canopy', 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%ratioDrip2Unloading) = var_info('ratioDrip2Unloading' , 'ratio of canopy drip to unloading of snow from the forest canopy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%canopyWettingFactor) = var_info('canopyWettingFactor' , 'maximum wetted fraction of the canopy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%canopyWettingExp) = var_info('canopyWettingExp' , 'exponent in canopy wetting function' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%minTempUnloading) = var_info('minTempUnloading' , 'min temp for unloading in windySnow' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%rateTempUnloading) = var_info('rateTempUnloading' , 'how quickly to unload due to temperature' , 'K s' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%minWindUnloading) = var_info('minWindUnloading' , 'min wind speed for unloading in windySnow' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%rateWindUnloading) = var_info('rateWindUnloading' , 'how quickly to unload due to wind' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! soil properties - mpar_meta(iLookPARAM%soil_dens_intr) = var_info('soil_dens_intr' , 'intrinsic soil density' , 'kg m-3' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%thCond_soil) = var_info('thCond_soil' , 'thermal conductivity of soil (includes quartz and other minerals)', 'W m-1 K-1' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%frac_sand) = var_info('frac_sand' , 'fraction of sand' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%frac_silt) = var_info('frac_silt' , 'fraction of silt' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%frac_clay) = var_info('frac_clay' , 'fraction of clay' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%theta_sat) = var_info('theta_sat' , 'soil porosity' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%theta_res) = var_info('theta_res' , 'volumetric residual water content' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%vGn_alpha) = var_info('vGn_alpha' , 'van Genuchten "alpha" parameter' , 'm-1' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%vGn_n) = var_info('vGn_n' , 'van Genuchten "n" parameter' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%k_soil) = var_info('k_soil' , 'saturated hydraulic conductivity' , 'm s-1' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%k_macropore) = var_info('k_macropore' , 'saturated hydraulic conductivity for macropores' , 'm s-1' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) - ! scalar soil properties - mpar_meta(iLookPARAM%fieldCapacity) = var_info('fieldCapacity' , 'soil field capacity (vol liq water content when baseflow begins)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%wettingFrontSuction) = var_info('wettingFrontSuction' , 'Green-Ampt wetting front suction' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%theta_mp) = var_info('theta_mp' , 'volumetric liquid water content when macropore flow begins' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%mpExp) = var_info('mpExp' , 'empirical exponent in macropore flow equation' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%kAnisotropic) = var_info('kAnisotropic' , 'anisotropy factor for lateral hydraulic conductivity' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zScale_TOPMODEL) = var_info('zScale_TOPMODEL' , 'TOPMODEL scaling factor used in lower boundary condition for soil', 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%compactedDepth) = var_info('compactedDepth' , 'depth where k_soil reaches the compacted value given by CH78' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%aquiferBaseflowRate) = var_info('aquiferBaseflowRate' , 'baseflow rate when aquifer storage = aquiferScaleFactor' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%aquiferScaleFactor) = var_info('aquiferScaleFactor' , 'scaling factor for aquifer storage in the big bucket' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%aquiferBaseflowExp) = var_info('aquiferBaseflowExp' , 'baseflow exponent' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%qSurfScale) = var_info('qSurfScale' , 'scaling factor in the surface runoff parameterization' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%specificYield) = var_info('specificYield' , 'specific yield' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%specificStorage) = var_info('specificStorage' , 'specific storage coefficient' , 'm-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%f_impede) = var_info('f_impede' , 'ice impedence factor' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%soilIceScale) = var_info('soilIceScale' , 'scaling factor for depth of soil ice, used to get frozen fraction', 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%soilIceCV) = var_info('soilIceCV' , 'CV of depth of soil ice, used to get frozen fraction' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! algorithmic control parameters - mpar_meta(iLookPARAM%minwind) = var_info('minwind' , 'minimum wind speed' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%minstep) = var_info('minstep' , 'minimum length of the time step' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%maxstep) = var_info('maxstep' , 'maximum length of the time step' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%wimplicit) = var_info('wimplicit' , 'weight assigned to the start-of-step fluxes (alpha)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%maxiter) = var_info('maxiter' , 'maximum number of iterations' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_liquid) = var_info('relConvTol_liquid' , 'relative convergence tolerance for vol frac liq water' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_liquid) = var_info('absConvTol_liquid' , 'absolute convergence tolerance for vol frac liq water' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_matric) = var_info('relConvTol_matric' , 'relative convergence tolerance for matric head' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_matric) = var_info('absConvTol_matric' , 'absolute convergence tolerance for matric head' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_energy) = var_info('relConvTol_energy' , 'relative convergence tolerance for energy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_energy) = var_info('absConvTol_energy' , 'absolute convergence tolerance for energy' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_aquifr) = var_info('relConvTol_aquifr' , 'relative convergence tolerance for aquifer storage' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_aquifr) = var_info('absConvTol_aquifr' , 'absolute convergence tolerance for aquifer storage' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zmin) = var_info('zmin' , 'minimum layer depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zmax) = var_info('zmax' , 'maximum layer depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zminLayer1) = var_info('zminLayer1' , 'minimum layer depth for the 1st (top) layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zminLayer2) = var_info('zminLayer2' , 'minimum layer depth for the 2nd layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zminLayer3) = var_info('zminLayer3' , 'minimum layer depth for the 3rd layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zminLayer4) = var_info('zminLayer4' , 'minimum layer depth for the 4th layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zminLayer5) = var_info('zminLayer5' , 'minimum layer depth for the 5th (bottom) layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zmaxLayer1_lower) = var_info('zmaxLayer1_lower' , 'maximum layer depth for the 1st (top) layer when only 1 layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zmaxLayer2_lower) = var_info('zmaxLayer2_lower' , 'maximum layer depth for the 2nd layer when only 2 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zmaxLayer3_lower) = var_info('zmaxLayer3_lower' , 'maximum layer depth for the 3rd layer when only 3 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zmaxLayer4_lower) = var_info('zmaxLayer4_lower' , 'maximum layer depth for the 4th layer when only 4 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zmaxLayer1_upper) = var_info('zmaxLayer1_upper' , 'maximum layer depth for the 1st (top) layer when > 1 layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zmaxLayer2_upper) = var_info('zmaxLayer2_upper' , 'maximum layer depth for the 2nd layer when > 2 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zmaxLayer3_upper) = var_info('zmaxLayer3_upper' , 'maximum layer depth for the 3rd layer when > 3 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zmaxLayer4_upper) = var_info('zmaxLayer4_upper' , 'maximum layer depth for the 4th layer when > 4 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - - ! ----- - ! * basin parameter data... - ! ------------------------- - bpar_meta(iLookBPAR%basin__aquiferHydCond) = var_info('basin__aquiferHydCond' , 'hydraulic conductivity of the aquifer' , 'm s-1', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - bpar_meta(iLookBPAR%basin__aquiferScaleFactor) = var_info('basin__aquiferScaleFactor', 'scaling factor for aquifer storage in the big bucket' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - bpar_meta(iLookBPAR%basin__aquiferBaseflowExp) = var_info('basin__aquiferBaseflowExp', 'baseflow exponent for the big bucket' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - bpar_meta(iLookBPAR%routingGammaShape) = var_info('routingGammaShape' , 'shape parameter in Gamma distribution used for sub-grid routing', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - bpar_meta(iLookBPAR%routingGammaScale) = var_info('routingGammaScale' , 'scale parameter in Gamma distribution used for sub-grid routing', 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - - ! ----- - ! * local model prognostic (state) variables... - ! --------------------------------------------- - ! define variables for time stepping - prog_meta(iLookPROG%dt_init) = var_info('dt_init' , 'length of initial time step at start of next data interval' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! state variables for vegetation - prog_meta(iLookPROG%scalarCanopyIce) = var_info('scalarCanopyIce' , 'mass of ice on the vegetation canopy' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - prog_meta(iLookPROG%scalarCanopyLiq) = var_info('scalarCanopyLiq' , 'mass of liquid water on the vegetation canopy' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - prog_meta(iLookPROG%scalarCanopyWat) = var_info('scalarCanopyWat' , 'mass of total water on the vegetation canopy' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - prog_meta(iLookPROG%scalarCanairTemp) = var_info('scalarCanairTemp' , 'temperature of the canopy air space' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - prog_meta(iLookPROG%scalarCanopyTemp) = var_info('scalarCanopyTemp' , 'temperature of the vegetation canopy' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! state variables for snow - prog_meta(iLookPROG%spectralSnowAlbedoDiffuse) = var_info('spectralSnowAlbedoDiffuse' , 'diffuse snow albedo for individual spectral bands' , '-' , get_ixVarType('wLength'), iMissVec, iMissVec, .false.) - prog_meta(iLookPROG%scalarSnowAlbedo) = var_info('scalarSnowAlbedo' , 'snow albedo for the entire spectral band' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - prog_meta(iLookPROG%scalarSnowDepth) = var_info('scalarSnowDepth' , 'total snow depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - prog_meta(iLookPROG%scalarSWE) = var_info('scalarSWE' , 'snow water equivalent' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - prog_meta(iLookPROG%scalarSfcMeltPond) = var_info('scalarSfcMeltPond' , 'ponded water caused by melt of the "snow without a layer"' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! define state variables for the snow+soil domain - prog_meta(iLookPROG%mLayerTemp) = var_info('mLayerTemp' , 'temperature of each layer' , 'K' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - prog_meta(iLookPROG%mLayerVolFracIce) = var_info('mLayerVolFracIce' , 'volumetric fraction of ice in each layer' , '-' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - prog_meta(iLookPROG%mLayerVolFracLiq) = var_info('mLayerVolFracLiq' , 'volumetric fraction of liquid water in each layer' , '-' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - prog_meta(iLookPROG%mLayerVolFracWat) = var_info('mLayerVolFracWat' , 'volumetric fraction of total water in each layer' , '-' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - prog_meta(iLookPROG%mLayerMatricHead) = var_info('mLayerMatricHead' , 'matric head of water in the soil' , 'm' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - ! other state variables - prog_meta(iLookPROG%scalarAquiferStorage) = var_info('scalarAquiferStorage' , 'water required to bring aquifer to the bottom of the soil profile', 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - prog_meta(iLookPROG%scalarSurfaceTemp) = var_info('scalarSurfaceTemp' , 'surface temperature (just a copy of the upper-layer temperature)' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! define coordinate variables - prog_meta(iLookPROG%mLayerDepth) = var_info('mLayerDepth' , 'depth of each layer' , 'm' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - prog_meta(iLookPROG%mLayerHeight) = var_info('mLayerHeight' , 'height of the layer mid-point (top of soil = 0)' , 'm' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) - prog_meta(iLookPROG%iLayerHeight) = var_info('iLayerHeight' , 'height of the layer interface (top of soil = 0)' , 'm' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) - - ! ----- - ! * local model diagnostic variables... - ! ------------------------------------- - ! local properties - diag_meta(iLookDIAG%scalarCanopyDepth) = var_info('scalarCanopyDepth' , 'canopy depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarGreenVegFraction) = var_info('scalarGreenVegFraction' , 'green vegetation fraction (used to compute LAI)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarBulkVolHeatCapVeg) = var_info('scalarBulkVolHeatCapVeg' , 'bulk volumetric heat capacity of vegetation' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarCanopyEmissivity) = var_info('scalarCanopyEmissivity' , 'effective canopy emissivity' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarRootZoneTemp) = var_info('scalarRootZoneTemp' , 'average temperature of the root zone' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarLAI) = var_info('scalarLAI' , 'one-sided leaf area index' , 'm2 m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarSAI) = var_info('scalarSAI' , 'one-sided stem area index' , 'm2 m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarExposedLAI) = var_info('scalarExposedLAI' , 'exposed leaf area index (after burial by snow)' , 'm2 m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarExposedSAI) = var_info('scalarExposedSAI' , 'exposed stem area index (after burial by snow)' , 'm2 m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarAdjMeasHeight) = var_info('scalarAdjMeasHeight' , 'adjusted measurement height for cases snowDepth>mHeight' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarCanopyIceMax) = var_info('scalarCanopyIceMax' , 'maximum interception storage capacity for ice' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarCanopyLiqMax) = var_info('scalarCanopyLiqMax' , 'maximum interception storage capacity for liquid water' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarGrowingSeasonIndex) = var_info('scalarGrowingSeasonIndex' , 'growing season index (0=off, 1=on)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarVolHtCap_air) = var_info('scalarVolHtCap_air' , 'volumetric heat capacity air' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarVolHtCap_ice) = var_info('scalarVolHtCap_ice' , 'volumetric heat capacity ice' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarVolHtCap_soil) = var_info('scalarVolHtCap_soil' , 'volumetric heat capacity dry soil' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarVolHtCap_water) = var_info('scalarVolHtCap_water' , 'volumetric heat capacity liquid wat' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%mLayerVolHtCapBulk) = var_info('mLayerVolHtCapBulk' , 'volumetric heat capacity in each layer' , 'J m-3 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarLambda_drysoil) = var_info('scalarLambda_drysoil' , 'thermal conductivity of dry soil' , 'W m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarLambda_wetsoil) = var_info('scalarLambda_wetsoil' , 'thermal conductivity of wet soil' , 'W m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%mLayerThermalC) = var_info('mLayerThermalC' , 'thermal conductivity at the mid-point of each layer' , 'W m-1 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%iLayerThermalC) = var_info('iLayerThermalC' , 'thermal conductivity at the interface of each layer' , 'W m-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) -! enthalpy - diag_meta(iLookDIAG%scalarCanairEnthalpy) = var_info('scalarCanairEnthalpy' , 'enthalpy of the canopy air space' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarCanopyEnthalpy) = var_info('scalarCanopyEnthalpy' , 'enthalpy of the vegetation canopy' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%mLayerEnthalpy) = var_info('mLayerEnthalpy' , 'enthalpy of the snow+soil layers' , 'J m-3' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - ! forcing - diag_meta(iLookDIAG%scalarVPair) = var_info('scalarVPair' , 'vapor pressure of the air above the vegetation canopy' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarVP_CanopyAir) = var_info('scalarVP_CanopyAir' , 'vapor pressure of the canopy air space' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarTwetbulb) = var_info('scalarTwetbulb' , 'wet bulb temperature' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarSnowfallTemp) = var_info('scalarSnowfallTemp' , 'temperature of fresh snow' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarNewSnowDensity) = var_info('scalarNewSnowDensity' , 'density of fresh snow' , 'kg m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarO2air) = var_info('scalarO2air' , 'atmospheric o2 concentration' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarCO2air) = var_info('scalarCO2air' , 'atmospheric co2 concentration' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! shortwave radiation - diag_meta(iLookDIAG%scalarCosZenith) = var_info('scalarCosZenith' , 'cosine of the solar zenith angle' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarFractionDirect) = var_info('scalarFractionDirect' , 'fraction of direct radiation (0-1)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarCanopySunlitFraction) = var_info('scalarCanopySunlitFraction' , 'sunlit fraction of canopy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarCanopySunlitLAI) = var_info('scalarCanopySunlitLAI' , 'sunlit leaf area' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarCanopyShadedLAI) = var_info('scalarCanopyShadedLAI' , 'shaded leaf area' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%spectralAlbGndDirect) = var_info('spectralAlbGndDirect' , 'direct albedo of underlying surface for each spectral band' , '-' , get_ixVarType('wLength'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%spectralAlbGndDiffuse) = var_info('spectralAlbGndDiffuse' , 'diffuse albedo of underlying surface for each spectral band' , '-' , get_ixVarType('wLength'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarGroundAlbedo) = var_info('scalarGroundAlbedo' , 'albedo of the ground surface' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! turbulent heat transfer - diag_meta(iLookDIAG%scalarLatHeatSubVapCanopy) = var_info('scalarLatHeatSubVapCanopy' , 'latent heat of sublimation/vaporization used for veg canopy' , 'J kg-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarLatHeatSubVapGround) = var_info('scalarLatHeatSubVapGround' , 'latent heat of sublimation/vaporization used for ground surface' , 'J kg-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarSatVP_CanopyTemp) = var_info('scalarSatVP_CanopyTemp' , 'saturation vapor pressure at the temperature of vegetation canopy', 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarSatVP_GroundTemp) = var_info('scalarSatVP_GroundTemp' , 'saturation vapor pressure at the temperature of the ground' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarZ0Canopy) = var_info('scalarZ0Canopy' , 'roughness length of the canopy' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarWindReductionFactor) = var_info('scalarWindReductionFactor' , 'canopy wind reduction factor' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarZeroPlaneDisplacement) = var_info('scalarZeroPlaneDisplacement' , 'zero plane displacement' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarRiBulkCanopy) = var_info('scalarRiBulkCanopy' , 'bulk Richardson number for the canopy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarRiBulkGround) = var_info('scalarRiBulkGround' , 'bulk Richardson number for the ground surface' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarCanopyStabilityCorrection) = var_info('scalarCanopyStabilityCorrection', 'stability correction for the canopy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarGroundStabilityCorrection) = var_info('scalarGroundStabilityCorrection', 'stability correction for the ground surface' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! evapotranspiration - diag_meta(iLookDIAG%scalarIntercellularCO2Sunlit) = var_info('scalarIntercellularCO2Sunlit' , 'carbon dioxide partial pressure of leaf interior (sunlit leaves)' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarIntercellularCO2Shaded) = var_info('scalarIntercellularCO2Shaded' , 'carbon dioxide partial pressure of leaf interior (shaded leaves)' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarTranspireLim) = var_info('scalarTranspireLim' , 'aggregate soil moisture and aquifer control on transpiration' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarTranspireLimAqfr) = var_info('scalarTranspireLimAqfr' , 'aquifer storage control on transpiration' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarFoliageNitrogenFactor) = var_info('scalarFoliageNitrogenFactor' , 'foliage nitrogen concentration (1=saturated)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarSoilRelHumidity) = var_info('scalarSoilRelHumidity' , 'relative humidity in the soil pores in the upper-most soil layer' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%mLayerTranspireLim) = var_info('mLayerTranspireLim' , 'soil moist & veg limit on transpiration for each layer' , '-' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%mLayerRootDensity) = var_info('mLayerRootDensity' , 'fraction of roots in each soil layer' , '-' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarAquiferRootFrac) = var_info('scalarAquiferRootFrac' , 'fraction of roots below the soil profile (in the aquifer)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! canopy hydrology - diag_meta(iLookDIAG%scalarFracLiqVeg) = var_info('scalarFracLiqVeg' , 'fraction of liquid water on vegetation' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarCanopyWetFraction) = var_info('scalarCanopyWetFraction' , 'fraction canopy that is wet' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! snow hydrology - diag_meta(iLookDIAG%scalarSnowAge) = var_info('scalarSnowAge' , 'non-dimensional snow age' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarGroundSnowFraction) = var_info('scalarGroundSnowFraction' , 'fraction ground that is covered with snow' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%spectralSnowAlbedoDirect) = var_info('spectralSnowAlbedoDirect' , 'direct snow albedo for individual spectral bands' , '-' , get_ixVarType('wLength'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%mLayerFracLiqSnow) = var_info('mLayerFracLiqSnow' , 'fraction of liquid water in each snow layer' , '-' , get_ixVarType('midSnow'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%mLayerThetaResid) = var_info('mLayerThetaResid' , 'residual volumetric water content in each snow layer' , '-' , get_ixVarType('midSnow'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%mLayerPoreSpace) = var_info('mLayerPoreSpace' , 'total pore space in each snow layer' , '-' , get_ixVarType('midSnow'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%mLayerMeltFreeze) = var_info('mLayerMeltFreeze' , 'ice content change from melt/freeze in each layer' , 'kg m-3' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - ! soil hydrology - diag_meta(iLookDIAG%scalarInfilArea) = var_info('scalarInfilArea' , 'fraction of unfrozen area where water can infiltrate' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarFrozenArea) = var_info('scalarFrozenArea' , 'fraction of area that is considered impermeable due to soil ice' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarSoilControl) = var_info('scalarSoilControl' , 'soil control on infiltration (1=controlling; 0=not)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%mLayerVolFracAir) = var_info('mLayerVolFracAir' , 'volumetric fraction of air in each layer' , '-' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%mLayerTcrit) = var_info('mLayerTcrit' , 'critical soil temperature above which all water is unfrozen' , 'K' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%mLayerCompress) = var_info('mLayerCompress' , 'change in volumetric water content due to compression of soil' , '-' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarSoilCompress) = var_info('scalarSoilCompress' , 'change in total soil storage due to compression of soil matrix' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%mLayerMatricHeadLiq) = var_info('mLayerMatricHeadLiq' , 'matric potential of liquid water' , 'm' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - ! mass balance check - diag_meta(iLookDIAG%scalarSoilWatBalError) = var_info('scalarSoilWatBalError' , 'error in the total soil water balance' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarAquiferBalError) = var_info('scalarAquiferBalError' , 'error in the aquifer water balance' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarTotalSoilLiq) = var_info('scalarTotalSoilLiq' , 'total mass of liquid water in the soil' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarTotalSoilIce) = var_info('scalarTotalSoilIce' , 'total mass of ice in the soil' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarTotalSoilWat) = var_info('scalarTotalSoilWat' , 'total mass of water in the soil' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! variable shortcuts - diag_meta(iLookDIAG%scalarVGn_m) = var_info('scalarVGn_m' , 'van Genuchten "m" parameter' , '-' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarKappa) = var_info('scalarKappa' , 'constant in the freezing curve function' , 'm K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarVolLatHt_fus) = var_info('scalarVolLatHt_fus' , 'volumetric latent heat of fusion' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! timing information - diag_meta(iLookDIAG%numFluxCalls) = var_info('numFluxCalls' , 'number of flux calls' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%wallClockTime) = var_info('wallClockTime' , 'wall clock time' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - - ! ----- - ! * local model fluxes... - ! ----------------------- - ! net energy and mass fluxes for the vegetation domain - flux_meta(iLookFLUX%scalarCanairNetNrgFlux) = var_info('scalarCanairNetNrgFlux' , 'net energy flux for the canopy air space' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarCanopyNetNrgFlux) = var_info('scalarCanopyNetNrgFlux' , 'net energy flux for the vegetation canopy' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarGroundNetNrgFlux) = var_info('scalarGroundNetNrgFlux' , 'net energy flux for the ground surface' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarCanopyNetLiqFlux) = var_info('scalarCanopyNetLiqFlux' , 'net liquid water flux for the vegetation canopy' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! forcing - flux_meta(iLookFLUX%scalarRainfall) = var_info('scalarRainfall' , 'computed rainfall rate' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarSnowfall) = var_info('scalarSnowfall' , 'computed snowfall rate' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! shortwave radiation - flux_meta(iLookFLUX%spectralIncomingDirect) = var_info('spectralIncomingDirect' , 'incoming direct solar radiation in each wave band' , 'W m-2' , get_ixVarType('wLength'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%spectralIncomingDiffuse) = var_info('spectralIncomingDiffuse' , 'incoming diffuse solar radiation in each wave band' , 'W m-2' , get_ixVarType('wLength'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarCanopySunlitPAR) = var_info('scalarCanopySunlitPAR' , 'average absorbed par for sunlit leaves' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarCanopyShadedPAR) = var_info('scalarCanopyShadedPAR' , 'average absorbed par for shaded leaves' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%spectralBelowCanopyDirect) = var_info('spectralBelowCanopyDirect' , 'downward direct flux below veg layer for each spectral band' , 'W m-2' , get_ixVarType('wLength'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%spectralBelowCanopyDiffuse) = var_info('spectralBelowCanopyDiffuse' , 'downward diffuse flux below veg layer for each spectral band' , 'W m-2' , get_ixVarType('wLength'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarBelowCanopySolar) = var_info('scalarBelowCanopySolar' , 'solar radiation transmitted below the canopy' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarCanopyAbsorbedSolar) = var_info('scalarCanopyAbsorbedSolar' , 'solar radiation absorbed by canopy' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarGroundAbsorbedSolar) = var_info('scalarGroundAbsorbedSolar' , 'solar radiation absorbed by ground' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! longwave radiation - flux_meta(iLookFLUX%scalarLWRadCanopy) = var_info('scalarLWRadCanopy' , 'longwave radiation emitted from the canopy' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarLWRadGround) = var_info('scalarLWRadGround' , 'longwave radiation emitted at the ground surface' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarLWRadUbound2Canopy) = var_info('scalarLWRadUbound2Canopy' , 'downward atmospheric longwave radiation absorbed by the canopy' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarLWRadUbound2Ground) = var_info('scalarLWRadUbound2Ground' , 'downward atmospheric longwave radiation absorbed by the ground' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarLWRadUbound2Ubound) = var_info('scalarLWRadUbound2Ubound' , 'atmospheric radiation refl by ground + lost thru upper boundary' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarLWRadCanopy2Ubound) = var_info('scalarLWRadCanopy2Ubound' , 'longwave radiation emitted from canopy lost thru upper boundary' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarLWRadCanopy2Ground) = var_info('scalarLWRadCanopy2Ground' , 'longwave radiation emitted from canopy absorbed by the ground' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarLWRadCanopy2Canopy) = var_info('scalarLWRadCanopy2Canopy' , 'canopy longwave reflected from ground and absorbed by the canopy' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarLWRadGround2Ubound) = var_info('scalarLWRadGround2Ubound' , 'longwave radiation emitted from ground lost thru upper boundary' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarLWRadGround2Canopy) = var_info('scalarLWRadGround2Canopy' , 'longwave radiation emitted from ground and absorbed by the canopy', 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarLWNetCanopy) = var_info('scalarLWNetCanopy' , 'net longwave radiation at the canopy' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarLWNetGround) = var_info('scalarLWNetGround' , 'net longwave radiation at the ground surface' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarLWNetUbound) = var_info('scalarLWNetUbound' , 'net longwave radiation at the upper atmospheric boundary' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! turbulent heat transfer - flux_meta(iLookFLUX%scalarEddyDiffusCanopyTop) = var_info('scalarEddyDiffusCanopyTop' , 'eddy diffusivity for heat at the top of the canopy' , 'm2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarFrictionVelocity) = var_info('scalarFrictionVelocity' , 'friction velocity (canopy momentum sink)' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarWindspdCanopyTop) = var_info('scalarWindspdCanopyTop' , 'windspeed at the top of the canopy' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarWindspdCanopyBottom) = var_info('scalarWindspdCanopyBottom' , 'windspeed at the height of the bottom of the canopy' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarGroundResistance) = var_info('scalarGroundResistance' , 'below canopy aerodynamic resistance' , 's m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarCanopyResistance) = var_info('scalarCanopyResistance' , 'above canopy aerodynamic resistance' , 's m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarLeafResistance) = var_info('scalarLeafResistance' , 'mean leaf boundary layer resistance per unit leaf area' , 's m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarSoilResistance) = var_info('scalarSoilResistance' , 'soil surface resistance' , 's m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarSenHeatTotal) = var_info('scalarSenHeatTotal' , 'sensible heat from the canopy air space to the atmosphere' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarSenHeatCanopy) = var_info('scalarSenHeatCanopy' , 'sensible heat from the canopy to the canopy air space' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarSenHeatGround) = var_info('scalarSenHeatGround' , 'sensible heat from the ground (below canopy or non-vegetated)' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarLatHeatTotal) = var_info('scalarLatHeatTotal' , 'latent heat from the canopy air space to the atmosphere' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarLatHeatCanopyEvap) = var_info('scalarLatHeatCanopyEvap' , 'evaporation latent heat from the canopy to the canopy air space' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarLatHeatCanopyTrans) = var_info('scalarLatHeatCanopyTrans' , 'transpiration latent heat from the canopy to the canopy air space', 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarLatHeatGround) = var_info('scalarLatHeatGround' , 'latent heat from the ground (below canopy or non-vegetated)' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarCanopyAdvectiveHeatFlux) = var_info('scalarCanopyAdvectiveHeatFlux' , 'heat advected to the canopy with precipitation (snow + rain)' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarGroundAdvectiveHeatFlux) = var_info('scalarGroundAdvectiveHeatFlux' , 'heat advected to the ground with throughfall + unloading/drainage', 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarCanopySublimation) = var_info('scalarCanopySublimation' , 'canopy sublimation/frost' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarSnowSublimation) = var_info('scalarSnowSublimation' , 'snow sublimation/frost (below canopy or non-vegetated)' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! liquid water fluxes associated with evapotranspiration - flux_meta(iLookFLUX%scalarStomResistSunlit) = var_info('scalarStomResistSunlit' , 'stomatal resistance for sunlit leaves' , 's m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarStomResistShaded) = var_info('scalarStomResistShaded' , 'stomatal resistance for shaded leaves' , 's m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarPhotosynthesisSunlit) = var_info('scalarPhotosynthesisSunlit' , 'sunlit photosynthesis' , 'umolco2 m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarPhotosynthesisShaded) = var_info('scalarPhotosynthesisShaded' , 'shaded photosynthesis' , 'umolco2 m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarCanopyTranspiration) = var_info('scalarCanopyTranspiration' , 'canopy transpiration' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarCanopyEvaporation) = var_info('scalarCanopyEvaporation' , 'canopy evaporation/condensation' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarGroundEvaporation) = var_info('scalarGroundEvaporation' , 'ground evaporation/condensation (below canopy or non-vegetated)' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%mLayerTranspire) = var_info('mLayerTranspire' , 'transpiration loss from each soil layer' , 'm s-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - ! liquid and solid water fluxes through the canopy - flux_meta(iLookFLUX%scalarThroughfallSnow) = var_info('scalarThroughfallSnow' , 'snow that reaches the ground without ever touching the canopy' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarThroughfallRain) = var_info('scalarThroughfallRain' , 'rain that reaches the ground without ever touching the canopy' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarCanopySnowUnloading) = var_info('scalarCanopySnowUnloading' , 'unloading of snow from the vegetation canopy' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarCanopyLiqDrainage) = var_info('scalarCanopyLiqDrainage' , 'drainage of liquid water from the vegetation canopy' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarCanopyMeltFreeze) = var_info('scalarCanopyMeltFreeze' , 'melt/freeze of water stored in the canopy' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! energy fluxes and for the snow and soil domains - flux_meta(iLookFLUX%iLayerConductiveFlux) = var_info('iLayerConductiveFlux' , 'conductive energy flux at layer interfaces' , 'W m-2' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%iLayerAdvectiveFlux) = var_info('iLayerAdvectiveFlux' , 'advective energy flux at layer interfaces' , 'W m-2' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%iLayerNrgFlux) = var_info('iLayerNrgFlux' , 'energy flux at layer interfaces' , 'W m-2' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%mLayerNrgFlux) = var_info('mLayerNrgFlux' , 'net energy flux for each layer within the snow+soil domain' , 'J m-3 s-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - ! liquid water fluxes for the snow domain - flux_meta(iLookFLUX%scalarSnowDrainage) = var_info('scalarSnowDrainage' , 'drainage from the bottom of the snow profile' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%iLayerLiqFluxSnow) = var_info('iLayerLiqFluxSnow' , 'liquid flux at snow layer interfaces' , 'm s-1' , get_ixVarType('ifcSnow'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%mLayerLiqFluxSnow) = var_info('mLayerLiqFluxSnow' , 'net liquid water flux for each snow layer' , 's-1' , get_ixVarType('midSnow'), iMissVec, iMissVec, .false.) - ! liquid water fluxes for the soil domain - flux_meta(iLookFLUX%scalarRainPlusMelt) = var_info('scalarRainPlusMelt' , 'rain plus melt, used as input to soil before surface runoff' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarMaxInfilRate) = var_info('scalarMaxInfilRate' , 'maximum infiltration rate' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarInfiltration) = var_info('scalarInfiltration' , 'infiltration of water into the soil profile' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarExfiltration) = var_info('scalarExfiltration' , 'exfiltration of water from the top of the soil profile' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarSurfaceRunoff) = var_info('scalarSurfaceRunoff' , 'surface runoff' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%mLayerSatHydCondMP) = var_info('mLayerSatHydCondMP' , 'saturated hydraulic conductivity of macropores in each layer' , 'm s-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%mLayerSatHydCond) = var_info('mLayerSatHydCond' , 'saturated hydraulic conductivity in each layer' , 'm s-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%iLayerSatHydCond) = var_info('iLayerSatHydCond' , 'saturated hydraulic conductivity in each layer interface' , 'm s-1' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%mLayerHydCond) = var_info('mLayerHydCond' , 'hydraulic conductivity in each layer' , 'm s-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%iLayerLiqFluxSoil) = var_info('iLayerLiqFluxSoil' , 'liquid flux at soil layer interfaces' , 'm s-1' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%mLayerLiqFluxSoil) = var_info('mLayerLiqFluxSoil' , 'net liquid water flux for each soil layer' , 's-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%mLayerBaseflow) = var_info('mLayerBaseflow' , 'baseflow from each soil layer' , 'm s-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%mLayerColumnInflow) = var_info('mLayerColumnInflow' , 'total inflow to each layer in a given soil column' , 'm3 s-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%mLayerColumnOutflow) = var_info('mLayerColumnOutflow' , 'total outflow from each layer in a given soil column' , 'm3 s-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarSoilBaseflow) = var_info('scalarSoilBaseflow' , 'total baseflow from the soil profile' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarSoilDrainage) = var_info('scalarSoilDrainage' , 'drainage from the bottom of the soil profile' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarAquiferRecharge) = var_info('scalarAquiferRecharge' , 'recharge to the aquifer' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarAquiferTranspire) = var_info('scalarAquiferTranspire' , 'transpiration loss from the aquifer' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarAquiferBaseflow) = var_info('scalarAquiferBaseflow' , 'baseflow from the aquifer' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! derived variables - flux_meta(iLookFLUX%scalarTotalET) = var_info('scalarTotalET' , 'total ET' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarTotalRunoff) = var_info('scalarTotalRunoff' , 'total runoff' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - flux_meta(iLookFLUX%scalarNetRadiation) = var_info('scalarNetRadiation' , 'net radiation' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - - ! ----- - ! * local flux derivatives... - ! --------------------------- - ! derivatives in net vegetation energy fluxes w.r.t. relevant state variables - deriv_meta(iLookDERIV%dCanairNetFlux_dCanairTemp) = var_info('dCanairNetFlux_dCanairTemp' , 'derivative in net canopy air space flux w.r.t. canopy air temperature', 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dCanairNetFlux_dCanopyTemp) = var_info('dCanairNetFlux_dCanopyTemp' , 'derivative in net canopy air space flux w.r.t. canopy temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dCanairNetFlux_dGroundTemp) = var_info('dCanairNetFlux_dGroundTemp' , 'derivative in net canopy air space flux w.r.t. ground temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dCanopyNetFlux_dCanairTemp) = var_info('dCanopyNetFlux_dCanairTemp' , 'derivative in net canopy flux w.r.t. canopy air temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dCanopyNetFlux_dCanopyTemp) = var_info('dCanopyNetFlux_dCanopyTemp' , 'derivative in net canopy flux w.r.t. canopy temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dCanopyNetFlux_dGroundTemp) = var_info('dCanopyNetFlux_dGroundTemp' , 'derivative in net canopy flux w.r.t. ground temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dCanopyNetFlux_dCanWat) = var_info('dCanopyNetFlux_dCanWat' , 'derivative in net canopy fluxes w.r.t. canopy total water content' , 'J kg-1 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dGroundNetFlux_dCanairTemp) = var_info('dGroundNetFlux_dCanairTemp' , 'derivative in net ground flux w.r.t. canopy air temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dGroundNetFlux_dCanopyTemp) = var_info('dGroundNetFlux_dCanopyTemp' , 'derivative in net ground flux w.r.t. canopy temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dGroundNetFlux_dGroundTemp) = var_info('dGroundNetFlux_dGroundTemp' , 'derivative in net ground flux w.r.t. ground temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dGroundNetFlux_dCanWat) = var_info('dGroundNetFlux_dCanWat' , 'derivative in net ground fluxes w.r.t. canopy total water content' , 'J kg-1 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! derivatives in evaporative fluxes w.r.t. relevant state variables - deriv_meta(iLookDERIV%dCanopyEvaporation_dTCanair) = var_info('dCanopyEvaporation_dTCanair' , 'derivative in canopy evaporation w.r.t. canopy air temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dCanopyEvaporation_dTCanopy) = var_info('dCanopyEvaporation_dTCanopy' , 'derivative in canopy evaporation w.r.t. canopy temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dCanopyEvaporation_dTGround) = var_info('dCanopyEvaporation_dTGround' , 'derivative in canopy evaporation w.r.t. ground temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dCanopyEvaporation_dCanWat) = var_info('dCanopyEvaporation_dCanWat' , 'derivative in canopy evaporation w.r.t. canopy total water content' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dGroundEvaporation_dTCanair) = var_info('dGroundEvaporation_dTCanair' , 'derivative in ground evaporation w.r.t. canopy air temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dGroundEvaporation_dTCanopy) = var_info('dGroundEvaporation_dTCanopy' , 'derivative in ground evaporation w.r.t. canopy temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dGroundEvaporation_dTGround) = var_info('dGroundEvaporation_dTGround' , 'derivative in ground evaporation w.r.t. ground temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dGroundEvaporation_dCanWat) = var_info('dGroundEvaporation_dCanWat' , 'derivative in ground evaporation w.r.t. canopy total water content' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! derivatives in transpiration - deriv_meta(iLookDERIV%dCanopyTrans_dTCanair) = var_info('dCanopyTrans_dTCanair' , 'derivative in canopy transpiration w.r.t. canopy air temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dCanopyTrans_dTCanopy) = var_info('dCanopyTrans_dTCanopy' , 'derivative in canopy transpiration w.r.t. canopy temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dCanopyTrans_dTGround) = var_info('dCanopyTrans_dTGround' , 'derivative in canopy transpiration w.r.t. ground temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dCanopyTrans_dCanWat) = var_info('dCanopyTrans_dCanWat' , 'derivative in canopy transpiration w.r.t. canopy total water content' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! derivatives in canopy water w.r.t canopy temperature - deriv_meta(iLookDERIV%dTheta_dTkCanopy) = var_info('dTheta_dTkCanopy' , 'derivative of volumetric liquid water content w.r.t. temperature' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%d2Theta_dTkCanopy2) = var_info('d2Theta_dTkCanopy2' , 'second derivative of volumetric liquid water content w.r.t. temperature', 'K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dCanLiq_dTcanopy) = var_info('dCanLiq_dTcanopy' , 'derivative of canopy liquid storage w.r.t. temperature' , 'kg m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dFracLiqVeg_dTkCanopy) = var_info('dFracLiqVeg_dTkCanopy' , 'derivative in fraction of (throughfall + drainage) w.r.t. temperature', 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! derivatives in canopy liquid fluxes w.r.t. canopy water - deriv_meta(iLookDERIV%scalarCanopyLiqDeriv) = var_info('scalarCanopyLiqDeriv' , 'derivative in (throughfall + drainage) w.r.t. canopy liquid water' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%scalarThroughfallRainDeriv) = var_info('scalarThroughfallRainDeriv' , 'derivative in throughfall w.r.t. canopy liquid water' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%scalarCanopyLiqDrainageDeriv) = var_info('scalarCanopyLiqDrainageDeriv' , 'derivative in canopy drainage w.r.t. canopy liquid water' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below - deriv_meta(iLookDERIV%dNrgFlux_dTempAbove) = var_info('dNrgFlux_dTempAbove' , 'derivatives in the flux w.r.t. temperature in the layer above' , 'J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dNrgFlux_dTempBelow) = var_info('dNrgFlux_dTempBelow' , 'derivatives in the flux w.r.t. temperature in the layer below' , 'J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) - ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below - deriv_meta(iLookDERIV%dNrgFlux_dWatAbove) = var_info('dNrgFlux_dWatAbove' , 'derivatives in the flux w.r.t. water state in the layer above' , 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dNrgFlux_dWatBelow) = var_info('dNrgFlux_dWatBelow' , 'derivatives in the flux w.r.t. water state in the layer below' , 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) - ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above - deriv_meta(iLookDERIV%iLayerLiqFluxSnowDeriv) = var_info('iLayerLiqFluxSnowDeriv' , 'derivative in vertical liquid water flux at layer interfaces' , 'm s-1' , get_ixVarType('ifcSnow'), iMissVec, iMissVec, .false.) - ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables - deriv_meta(iLookDERIV%dVolTot_dPsi0) = var_info('dVolTot_dPsi0' , 'derivative in total water content w.r.t. total water matric potential', 'm-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%d2VolTot_d2Psi0) = var_info('d2VolTot_d2Psi0' , 'second derivative in total water content w.r.t. total water matric potential', 'm-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dCompress_dPsi) = var_info('dCompress_dPsi' , 'derivative in compressibility w.r.t matric head' , 'm-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%mLayerdTheta_dPsi) = var_info('mLayerdTheta_dPsi' , 'derivative in the soil water characteristic w.r.t. psi' , 'm-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%mLayerdPsi_dTheta) = var_info('mLayerdPsi_dTheta' , 'derivative in the soil water characteristic w.r.t. theta' , 'm' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dq_dHydStateAbove) = var_info('dq_dHydStateAbove' , 'change in flux at layer interfaces w.r.t. states in the layer above' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dq_dHydStateBelow) = var_info('dq_dHydStateBelow' , 'change in flux at layer interfaces w.r.t. states in the layer below' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dq_dHydStateLayerSurfVec) = var_info('dq_dHydStateLayerSurfVec' , 'change in the flux in soil surface interface w.r.t. state variables in layers' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) - ! derivative in baseflow flux w.r.t. aquifer storage - deriv_meta(iLookDERIV%dBaseflow_dAquifer) = var_info('dBaseflow_dAquifer' , 'derivative in baseflow flux w.r.t. aquifer storage' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables - deriv_meta(iLookDERIV%dq_dNrgStateAbove) = var_info('dq_dNrgStateAbove' , 'change in flux at layer interfaces w.r.t. states in the layer above' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dq_dNrgStateBelow) = var_info('dq_dNrgStateBelow' , 'change in flux at layer interfaces w.r.t. states in the layer below' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dq_dNrgStateLayerSurfVec) = var_info('dq_dNrgStateLayerSurfVec' , 'change in the flux in soil surface interface w.r.t. state variables in layers' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dPsiLiq_dTemp) = var_info('dPsiLiq_dTemp' , 'derivative in the liquid water matric potential w.r.t. temperature' , 'm K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dPsiLiq_dPsi0) = var_info('dPsiLiq_dPsi0' , 'derivative in liquid matric potential w.r.t. total matric potential' , '-' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - ! derivatives in soil transpiration w.r.t. canopy state variables - deriv_meta(iLookDERIV%mLayerdTrans_dTCanair) = var_info('mLayerdTrans_dTCanair' , 'derivatives in the soil layer transpiration flux w.r.t. canopy air temperature', 'm s-1 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%mLayerdTrans_dTCanopy) = var_info('mLayerdTrans_dTCanopy' , 'derivatives in the soil layer transpiration flux w.r.t. canopy temperature', 'm s-1 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%mLayerdTrans_dTGround) = var_info('mLayerdTrans_dTGround' , 'derivatives in the soil layer transpiration flux w.r.t. ground temperature', 'm s-1 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%mLayerdTrans_dCanWat) = var_info('mLayerdTrans_dCanWat' , 'derivatives in the soil layer transpiration flux w.r.t. canopy total water', 'm-1 s-1 kg-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - ! derivatives in aquifer transpiration w.r.t. canopy state variables - deriv_meta(iLookDERIV%dAquiferTrans_dTCanair) = var_info('dAquiferTrans_dTCanair' , 'derivative in the aquifer transpiration flux w.r.t. canopy air temperature', 'm s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dAquiferTrans_dTCanopy) = var_info('dAquiferTrans_dTCanopy' , 'derivative in the aquifer transpiration flux w.r.t. canopy temperature', 'm s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dAquiferTrans_dTGround) = var_info('dAquiferTrans_dTGround' , 'derivative in the aquifer transpiration flux w.r.t. ground temperature', 'm s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dAquiferTrans_dCanWat) = var_info('dAquiferTrans_dCanWat' , 'derivative in the aquifer transpiration flux w.r.t. canopy total water', 'm-1 s-1 kg-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature - deriv_meta(iLookDERIV%dFracLiqSnow_dTk) = var_info('dFracLiqSnow_dTk' , 'derivative in fraction of liquid snow w.r.t. temperature' , 'K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%mLayerdTheta_dTk) = var_info('mLayerdTheta_dTk' , 'derivative of volumetric liquid water content w.r.t. temperature' , 'K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%mLayerd2Theta_dTk2) = var_info('mLayerd2Theta_dTk2' , 'second derivative of volumetric liquid water content w.r.t. temperature', 'K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - ! derivate in bulk heat capacity w.r.t. relevant state variables - deriv_meta(iLookDERIV%dVolHtCapBulk_dPsi0) = var_info('dVolHtCapBulk_dPsi0' , 'derivative in bulk heat capacity w.r.t. matric potential' , 'J m-4 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dTheta) = var_info('dVolHtCapBulk_dTheta' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dCanWat) = var_info('dVolHtCapBulk_dCanWat' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dTk) = var_info('dVolHtCapBulk_dTk' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dTkCanopy) = var_info('dVolHtCapBulk_dTkCanopy' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! derivatives in time - deriv_meta(iLookDERIV%mLayerdTemp_dt) = var_info('mLayerdTemp_dt' , 'timestep change in layer temperature' , 'K' ,get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%scalarCanopydTemp_dt) = var_info('scalarCanopydTemp_dt' , 'timestep change in canopy temperature' , 'K' ,get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - - ! ----- - ! * basin-wide runoff and aquifer fluxes... - ! ----------------------------------------- - bvar_meta(iLookBVAR%basin__TotalArea) = var_info('basin__TotalArea' , 'total basin area' , 'm2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - bvar_meta(iLookBVAR%basin__SurfaceRunoff) = var_info('basin__SurfaceRunoff' , 'surface runoff' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - bvar_meta(iLookBVAR%basin__ColumnOutflow) = var_info('basin__ColumnOutflow' , 'outflow from all "outlet" HRUs (with no downstream HRU)', 'm3 s-1', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - bvar_meta(iLookBVAR%basin__AquiferStorage) = var_info('basin__AquiferStorage' , 'aquifer storage' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - bvar_meta(iLookBVAR%basin__AquiferRecharge) = var_info('basin__AquiferRecharge' , 'recharge to the aquifer' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - bvar_meta(iLookBVAR%basin__AquiferBaseflow) = var_info('basin__AquiferBaseflow' , 'baseflow from the aquifer' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - bvar_meta(iLookBVAR%basin__AquiferTranspire) = var_info('basin__AquiferTranspire', 'transpiration loss from the aquifer' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - bvar_meta(iLookBVAR%basin__TotalRunoff) = var_info('basin__TotalRunoff' , 'total runoff to channel from all active components' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - bvar_meta(iLookBVAR%basin__SoilDrainage) = var_info('basin__SoilDrainage' , 'soil drainage' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - bvar_meta(iLookBVAR%routingRunoffFuture) = var_info('routingRunoffFuture' , 'runoff in future time steps' , 'm s-1' , get_ixVarType('routing'), iMissVec, iMissVec, .false.) - bvar_meta(iLookBVAR%routingFractionFuture) = var_info('routingFractionFuture' , 'fraction of runoff in future time steps' , '-' , get_ixVarType('routing'), iMissVec, iMissVec, .false.) - bvar_meta(iLookBVAR%averageInstantRunoff) = var_info('averageInstantRunoff' , 'instantaneous runoff' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - bvar_meta(iLookBVAR%averageRoutedRunoff) = var_info('averageRoutedRunoff' , 'routed runoff' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - - ! ----- - ! * lookup tables... - ! ------------------ - - ! temperature and enthalpy - lookup_meta(iLookLOOKUP%temperature) = var_info('temperature' , 'value of temperature in the lookup table' , 'K' , get_ixVarType('unknown'), iMissVec, iMissVec, .false.) - lookup_meta(iLookLOOKUP%enthalpy) = var_info('enthalpy' , 'value of enthalpy in the lookup table' , 'J m-3' , get_ixVarType('unknown'), iMissVec, iMissVec, .false.) - lookup_meta(iLookLOOKUP%deriv2) = var_info('deriv2' , 'second derivatives of the interpolating function' , 'mixed' , get_ixVarType('unknown'), iMissVec, iMissVec, .false.) - - ! ----- - ! * model indices... - ! ------------------ - - ! number of model layers, and layer indices - indx_meta(iLookINDEX%nSnow) = var_info('nSnow' , 'number of snow layers' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%nSoil) = var_info('nSoil' , 'number of soil layers' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%nLayers) = var_info('nLayers' , 'total number of layers' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%layerType) = var_info('layerType' , 'index defining type of layer (snow or soil)' , '-', get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - ! number of state variables of different type - indx_meta(iLookINDEX%nCasNrg) = var_info('nCasNrg' , 'number of energy state variables for the canopy air space' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%nVegNrg) = var_info('nVegNrg' , 'number of energy state variables for the vegetation canopy' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%nVegMass) = var_info('nVegMass' , 'number of hydrology states for vegetation (mass of water)' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%nVegState) = var_info('nVegState' , 'number of vegetation state variables' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%nNrgState) = var_info('nNrgState' , 'number of energy state variables' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%nWatState) = var_info('nWatState' , 'number of "total water" states (vol. total water content)' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%nMatState) = var_info('nMatState' , 'number of matric head state variables' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%nMassState) = var_info('nMassState' , 'number of hydrology state variables (mass of water)' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%nState) = var_info('nState' , 'total number of model state variables' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! number of state variables within different domains in the snow+soil system - indx_meta(iLookINDEX%nSnowSoilNrg) = var_info('nSnowSoilNrg' , 'number of energy states in the snow+soil domain' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%nSnowOnlyNrg) = var_info('nSnowOnlyNrg' , 'number of energy states in the snow domain' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%nSoilOnlyNrg) = var_info('nSoilOnlyNrg' , 'number of energy states in the soil domain' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%nSnowSoilHyd) = var_info('nSnowSoilHyd' , 'number of hydrology states in the snow+soil domain' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%nSnowOnlyHyd) = var_info('nSnowOnlyHyd' , 'number of hydrology states in the snow domain' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%nSoilOnlyHyd) = var_info('nSoilOnlyHyd' , 'number of hydrology states in the soil domain' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! type of model state variables - indx_meta(iLookINDEX%ixControlVolume) = var_info('ixControlVolume' , 'index of the control volume for different domains (veg, snow, soil)' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixDomainType) = var_info('ixDomainType' , 'index of the type of domain (iname_veg, iname_snow, iname_soil)' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixStateType) = var_info('ixStateType' , 'index of the type of every state variable (iname_nrgCanair, ...)' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixHydType) = var_info('ixHydType' , 'index of the type of hydrology states in snow+soil domain' , '-', get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - ! type of model state variables (state subset) - indx_meta(iLookINDEX%ixDomainType_subset) = var_info('ixDomainType_subset' , '[state subset] id of domain for desired model state variables' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixStateType_subset) = var_info('ixStateType_subset' , '[state subset] type of desired model state variables' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) - ! mapping between state subset and the full state vector - indx_meta(iLookINDEX%ixMapFull2Subset) = var_info('ixMapFull2Subset' , 'list of indices of the state subset in the full state vector' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixMapSubset2Full) = var_info('ixMapSubset2Full' , 'list of indices of the full state vector in the state subset' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) - ! indices of model specific state variables - indx_meta(iLookINDEX%ixCasNrg) = var_info('ixCasNrg' , 'index of canopy air space energy state variable' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixVegNrg) = var_info('ixVegNrg' , 'index of canopy energy state variable' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixVegHyd) = var_info('ixVegHyd' , 'index of canopy hydrology state variable (mass)' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixTopNrg) = var_info('ixTopNrg' , 'index of upper-most energy state in the snow+soil subdomain' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixTopHyd) = var_info('ixTopHyd' , 'index of upper-most hydrology state in the snow+soil subdomain' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixAqWat) = var_info('ixAqWat' , 'index of storage of water in the aquifer' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! vectors of indices for specific state types - indx_meta(iLookINDEX%ixNrgOnly) = var_info('ixNrgOnly' , 'indices IN THE STATE SUBSET for energy states' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixHydOnly) = var_info('ixHydOnly' , 'indices IN THE STATE SUBSET for hydrology states in the snow+soil domain', '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixMatOnly) = var_info('ixMatOnly' , 'indices IN THE STATE SUBSET for matric head state variables' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixMassOnly) = var_info('ixMassOnly' , 'indices IN THE STATE SUBSET for hydrology states (mass of water)' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) - ! vectors of indices for specific state types within specific sub-domains - indx_meta(iLookINDEX%ixSnowSoilNrg) = var_info('ixSnowSoilNrg' , 'indices IN THE STATE SUBSET for energy states in the snow+soil domain' , '-', get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixSnowOnlyNrg) = var_info('ixSnowOnlyNrg' , 'indices IN THE STATE SUBSET for energy states in the snow domain' , '-', get_ixVarType('midSnow'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixSoilOnlyNrg) = var_info('ixSoilOnlyNrg' , 'indices IN THE STATE SUBSET for energy states in the soil domain' , '-', get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixSnowSoilHyd) = var_info('ixSnowSoilHyd' , 'indices IN THE STATE SUBSET for hydrology states in the snow+soil domain', '-', get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixSnowOnlyHyd) = var_info('ixSnowOnlyHyd' , 'indices IN THE STATE SUBSET for hydrology states in the snow domain' , '-', get_ixVarType('midSnow'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixSoilOnlyHyd) = var_info('ixSoilOnlyHyd' , 'indices IN THE STATE SUBSET for hydrology states in the soil domain' , '-', get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - ! vectors of indices for specfic state types within specific sub-domains - indx_meta(iLookINDEX%ixNrgCanair) = var_info('ixNrgCanair' , 'indices IN THE FULL VECTOR for energy states in canopy air space domain' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixNrgCanopy) = var_info('ixNrgCanopy' , 'indices IN THE FULL VECTOR for energy states in the canopy domain' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixHydCanopy) = var_info('ixHydCanopy' , 'indices IN THE FULL VECTOR for hydrology states in the canopy domain' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixNrgLayer) = var_info('ixNrgLayer' , 'indices IN THE FULL VECTOR for energy states in the snow+soil domain' , '-', get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixHydLayer) = var_info('ixHydLayer' , 'indices IN THE FULL VECTOR for hydrology states in the snow+soil domain' , '-', get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixWatAquifer) = var_info('ixWatAquifer' , 'indices IN THE FULL VECTOR for storage of water in the aquifer' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! vectors of indices for specific state types IN SPECIFIC SUB-DOMAINS - indx_meta(iLookINDEX%ixVolFracWat) = var_info('ixVolFracWat' , 'indices IN THE SNOW+SOIL VECTOR for hyd states' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixMatricHead) = var_info('ixMatricHead' , 'indices IN THE SOIL VECTOR for hyd states' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) - ! indices within state vectors - indx_meta(iLookINDEX%ixAllState) = var_info('ixAllState' , 'list of indices for all model state variables' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixSoilState) = var_info('ixSoilState' , 'list of indices for all soil layers' , '-', get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixLayerState) = var_info('ixLayerState' , 'list of indices for all model layers' , '-', get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%ixLayerActive) = var_info('ixLayerActive' , 'list of indices for active model layers (inactive=integerMissing)' , '-', get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - ! number of trials - indx_meta(iLookINDEX%numberFluxCalc) = var_info('numberFluxCalc' , 'number of flux calculations' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%numberStateSplit) = var_info('numberStateSplit' , 'number of state splitting solutions' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%numberDomainSplitNrg) = var_info('numberDomainSplitNrg' , 'number of domain splitting solutions for energy' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%numberDomainSplitMass) = var_info('numberDomainSplitMass', 'number of domain splitting solutions for mass' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - indx_meta(iLookINDEX%numberScalarSolutions) = var_info('numberScalarSolutions', 'number of scalar solutions' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - - ! read file to define model output (modifies metadata structures) - call read_output_file(err,cmessage) - if (err.ne.0) message=trim(message)//trim(cmessage) - - end subroutine popMetadat - - ! ------------------------------------------------ - ! subroutine to populate write commands from file input - ! ------------------------------------------------ - subroutine read_output_file(err,message) - USE netcdf - ! to get name of output control file from user - USE summaFileManager,only:SETTINGS_PATH ! path for metadata files - USE summaFileManager,only:OUTPUT_CONTROL ! file with output controls - - ! some dimensional parameters - USE globalData, only:outFreq ! output frequencies - USE var_lookup, only:maxvarFreq ! maximum # of output files - USE var_lookup, only:maxvarStat ! maximum # of statistics - - ! metadata structures - USE globalData, only: time_meta ! data structure for time metadata - USE globalData, only: forc_meta ! data structure for forcing metadata - USE globalData, only: type_meta ! data structure for categorical metadata - USE globalData, only: attr_meta ! data structure for attribute metadata - USE globalData, only: mpar_meta ! data structure for local parameter metadata - USE globalData, only: bpar_meta ! data structure for basin parameter metadata - USE globalData, only: bvar_meta ! data structure for basin model variable metadata - USE globalData, only: indx_meta ! data structure for index metadata - USE globalData, only: prog_meta ! data structure for local prognostic (state) variables - USE globalData, only: diag_meta ! data structure for local diagnostic variables - USE globalData, only: flux_meta ! data structure for local flux variables - USE globalData, only: deriv_meta ! data structure for local flux derivatives - USE globalData, only: outputPrecision ! data structure for output precision - USE globalData, only: outputCompressionLevel ! data structure for output netcdf deflate level - - ! structures of named variables - USE var_lookup, only: iLookTYPE ! named variables for categorical data - USE var_lookup, only: iLookID ! named variables for hru and gru ID metadata - USE var_lookup, only: iLookFORCE ! named variables for forcing data structure - USE var_lookup, only: iLookINDEX ! named variables for index variable data structure - USE var_lookup, only: iLookSTAT ! named variables for statitics variable data structure - USE var_lookup, only: iLookFREQ ! named variables for model output frequencies - - ! identify indices within structures - USE get_ixName_module,only:get_ixUnknown ! identify index in any structure - USE get_ixname_module,only:get_ixFreq ! identify index of model output frequency - USE get_ixname_module,only:get_ixStat ! identify index in ststistics structure - USE get_ixname_module,only:get_statName ! identify statistics name from the index - - ! modules to read ASCII data - USE ascii_util_module,only:file_open ! open file - USE ascii_util_module,only:linewidth ! match character number for one line - USE ascii_util_module,only:get_vlines ! get a vector of non-comment lines - USE ascii_util_module,only:split_line ! split a line into words - implicit none - - ! dummy variables - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - - ! define file format - integer(i4b),parameter :: noStatsDesired=1001 ! no statistic desired (temporally constant variables) - integer(i4b),parameter :: provideStatName=1002 ! provide the name of the desired statistic - integer(i4b),parameter :: provideStatFlags=1003 ! provide flags defining the desired statistic - integer(i4b) :: fileFormat ! the file format - - ! define statistics flags - logical(lgt),dimension(maxvarStat) :: statFlag ! vector of statistics flags - character(len=32) :: statName ! name of desired statistic - integer(i4b) :: iStat ! index of statistics vector - - ! define frequency of model output - character(len=64) :: freqName ! name of desired output frequency - integer(i4b) :: iFreq ! index of frequency vector - - ! general local variables - character(LEN=256) :: cmessage ! error message of downwind routine - character(LEN=256) :: outfile ! full path of model output file - integer(i4b) :: unt ! file unit - character(LEN=linewidth),allocatable :: charlines(:) ! vector of character strings - character(LEN=64),allocatable :: lineWords(:) ! vector to parse textline - integer(i4b) :: nWords ! number of words in line - character(LEN=128) :: varName ! variable name - character(LEN=5) :: structName ! name of structure - integer(i4b) :: vLine ! index for loop through variables - integer(i4b) :: vDex ! index into type lists - - ! initialize error control - err=0; message='read_output_file/' - - ! ********************************************************************************************** - ! (1) open file and read variable data - ! ********************************************************************************************** - outfile = trim(SETTINGS_PATH)//trim(OUTPUT_CONTROL) ! build filename - call file_open(trim(outfile),unt,err,cmessage) ! open file - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - ! ********************************************************************************************** - ! (2) read variable data (continue reading from previous point in the file) - ! ********************************************************************************************** - ! read the rest of the lines - call get_vlines(unt,charLines,err,cmessage) ! get a list of character strings from non-comment lines - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - close(unt) ! close the file - - ! ********************************************************************************************** - ! (3) loop to parse individual file lines - ! ********************************************************************************************** - - ! initialize output frequency - outFreq(:) = .false. - - ! loop through the lines in the file - do vLine = 1,size(charLines) - - ! parse the current line - call split_line(charLines(vLine),lineWords,err,cmessage) +subroutine popMetadat(err,message) + ! data structures + USE data_types, only: var_info ! data type for metadata structure + USE globalData, only: time_meta ! data structure for time metadata + USE globalData, only: forc_meta ! data structure for forcing metadata + USE globalData, only: type_meta ! data structure for categorical metadata + USE globalData, only: id_meta ! data structure for hru and gru ID metadata + USE globalData, only: attr_meta ! data structure for attribute metadata + USE globalData, only: mpar_meta ! data structure for local parameter metadata + USE globalData, only: bpar_meta ! data structure for basin parameter metadata + USE globalData, only: bvar_meta ! data structure for basin model variable metadata + USE globalData, only: indx_meta ! data structure for index metadata + USE globalData, only: prog_meta ! data structure for local prognostic (state) variables + USE globalData, only: diag_meta ! data structure for local diagnostic variables + USE globalData, only: flux_meta ! data structure for local flux variables + USE globalData, only: deriv_meta ! data structure for local flux derivatives + USE globalData, only: lookup_meta ! data structure for lookup tables + ! structures of named variables + USE var_lookup, only: iLookTIME ! named variables for time data structure + USE var_lookup, only: iLookFORCE ! named variables for forcing data structure + USE var_lookup, only: iLookTYPE ! named variables for categorical attribute data structure + USE var_lookup, only: iLookID ! named variables for hru and gru ID metadata + USE var_lookup, only: iLookATTR ! named variables for real valued attribute data structure + USE var_lookup, only: iLookPARAM ! named variables for local parameter data structure + USE var_lookup, only: iLookBPAR ! named variables for basin parameter data structure + USE var_lookup, only: iLookBVAR ! named variables for basin model variable data structure + USE var_lookup, only: iLookINDEX ! named variables for index variable data structure + USE var_lookup, only: iLookPROG ! named variables for local state variables + USE var_lookup, only: iLookDIAG ! named variables for local diagnostic variables + USE var_lookup, only: iLookFLUX ! named variables for local flux variables + USE var_lookup, only: iLookDERIV ! named variables for local flux derivatives + USE var_lookup, only: iLookLOOKUP ! named variables for lookup tables + USE var_lookup, only: maxvarFreq ! number of output frequencies + USE var_lookup, only: maxvarStat ! number of statistics + USE get_ixName_module,only:get_ixVarType ! to turn vartype strings to integers + implicit none + ! dummy variables + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! internals + character(256) :: cmessage ! error message + integer,dimension(maxVarFreq) :: iMissVec ! vector of missing integers + ! initialize error control + err=0; message='popMetadat/' + + ! init arrays for structure constructors + iMissVec(:) = integerMissing + ! ----- + ! * model time structures... + ! -------------------------- + time_meta(iLookTIME%iyyy) = var_info('iyyy' , 'year' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + time_meta(iLookTIME%im) = var_info('im' , 'month' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + time_meta(iLookTIME%id) = var_info('id' , 'day' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + time_meta(iLookTIME%ih) = var_info('ih' , 'hour' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + time_meta(iLookTIME%imin) = var_info('imin' , 'minute' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + time_meta(iLookTIME%ih_tz) = var_info('ih_tz' , 'hour for time zone offset' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + time_meta(iLookTIME%imin_tz) = var_info('imin_tz', 'minute for time zone offset', '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + + ! ----- + ! * model forcing data... + ! ----------------------- + forc_meta(iLookFORCE%time) = var_info('time' , 'time since time reference' , 'seconds since 1990-1-1 0:0:0.0 -0:00', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + forc_meta(iLookFORCE%pptrate) = var_info('pptrate' , 'precipitation rate' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + forc_meta(iLookFORCE%SWRadAtm) = var_info('SWRadAtm', 'downward shortwave radiation at the upper boundary', 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + forc_meta(iLookFORCE%LWRadAtm) = var_info('LWRadAtm', 'downward longwave radiation at the upper boundary' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + forc_meta(iLookFORCE%airtemp) = var_info('airtemp' , 'air temperature at the measurement height' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + forc_meta(iLookFORCE%windspd) = var_info('windspd' , 'wind speed at the measurement height' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + forc_meta(iLookFORCE%airpres) = var_info('airpres' , 'air pressure at the the measurement height' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + forc_meta(iLookFORCE%spechum) = var_info('spechum' , 'specific humidity at the measurement height' , 'g g-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + + ! ----- + ! * categorical data... + ! --------------------- + type_meta(iLookTYPE%vegTypeIndex) = var_info('vegTypeIndex' , 'index defining vegetation type' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + type_meta(iLookTYPE%soilTypeIndex) = var_info('soilTypeIndex' , 'index defining soil type' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + type_meta(iLookTYPE%slopeTypeIndex) = var_info('slopeTypeIndex', 'index defining slope' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + type_meta(iLookTYPE%downHRUindex) = var_info('downHRUindex' , 'index of downslope HRU (0 = basin outlet)' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + + ! ----- + ! * hru and gru ID data... + ! --------------------- + id_meta(iLookID%hruId) = var_info('hruId' , 'ID defining the hydrologic response unit' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + + ! ----- + ! * site characteristics... + ! ------------------------- + attr_meta(iLookATTR%latitude) = var_info('latitude' , 'latitude' , 'degrees north', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + attr_meta(iLookATTR%longitude) = var_info('longitude' , 'longitude' , 'degrees east' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + attr_meta(iLookATTR%elevation) = var_info('elevation' , 'elevation' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + attr_meta(iLookATTR%tan_slope) = var_info('tan_slope' , 'tan water table slope (tan local ground surface slope)', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + attr_meta(iLookATTR%contourLength) = var_info('contourLength' , 'length of contour at downslope edge of HRU' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + attr_meta(iLookATTR%HRUarea) = var_info('HRUarea' , 'area of each HRU' , 'm2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + attr_meta(iLookATTR%mHeight) = var_info('mHeight' , 'measurement height above bare ground' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + attr_meta(iLookATTR%aspect) = var_info('aspect' , 'mean azimuth of HRU in degrees East of North (0)' , 'degrees' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + + ! ----- + ! * local parameter data... + ! ------------------------- + ! boundary conditions + mpar_meta(iLookPARAM%upperBoundHead) = var_info('upperBoundHead' , 'matric head at the upper boundary' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%lowerBoundHead) = var_info('lowerBoundHead' , 'matric head at the lower boundary' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%upperBoundTheta) = var_info('upperBoundTheta' , 'volumetric liquid water content at the upper boundary' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%lowerBoundTheta) = var_info('lowerBoundTheta' , 'volumetric liquid water content at the lower boundary' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%upperBoundTemp) = var_info('upperBoundTemp' , 'temperature of the upper boundary' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%lowerBoundTemp) = var_info('lowerBoundTemp' , 'temperature of the lower boundary' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! precipitation partitioning + mpar_meta(iLookPARAM%tempCritRain) = var_info('tempCritRain' , 'critical temperature where precipitation is rain' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%tempRangeTimestep) = var_info('tempRangeTimestep' , 'temperature range over the time step' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%frozenPrecipMultip) = var_info('frozenPrecipMultip' , 'frozen precipitation multiplier' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! snow properties + mpar_meta(iLookPARAM%snowfrz_scale) = var_info('snowfrz_scale' , 'scaling parameter for the freezing curve for snow' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%fixedThermalCond_snow) = var_info('fixedThermalCond_snow' , 'temporally constant thermal conductivity for snow' , 'W m-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! snow albedo + mpar_meta(iLookPARAM%albedoMax) = var_info('albedoMax' , 'maximum snow albedo (single spectral band)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%albedoMinWinter) = var_info('albedoMinWinter' , 'minimum snow albedo during winter (single spectral band)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%albedoMinSpring) = var_info('albedoMinSpring' , 'minimum snow albedo during spring (single spectral band)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%albedoMaxVisible) = var_info('albedoMaxVisible' , 'maximum snow albedo in the visible part of the spectrum' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%albedoMinVisible) = var_info('albedoMinVisible' , 'minimum snow albedo in the visible part of the spectrum' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%albedoMaxNearIR) = var_info('albedoMaxNearIR' , 'maximum snow albedo in the near infra-red part of the spectrum' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%albedoMinNearIR) = var_info('albedoMinNearIR' , 'minimum snow albedo in the near infra-red part of the spectrum' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%albedoDecayRate) = var_info('albedoDecayRate' , 'albedo decay rate' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%albedoSootLoad) = var_info('albedoSootLoad' , 'soot load factor' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%albedoRefresh) = var_info('albedoRefresh' , 'critical mass necessary for albedo refreshment' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! radiation transfer + mpar_meta(iLookPARAM%radExt_snow) = var_info('radExt_snow' , 'extinction coefficient for radiation penetration into snowpack' , 'm-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%directScale) = var_info('directScale' , 'scaling factor for fractional driect radiaion parameterization' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%Frad_direct) = var_info('Frad_direct' , 'fraction direct solar radiation' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%Frad_vis) = var_info('Frad_vis' , 'fraction radiation in visible part of spectrum' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! new snow density + mpar_meta(iLookPARAM%newSnowDenMin) = var_info('newSnowDenMin' , 'minimum new snow density' , 'kg m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%newSnowDenMult) = var_info('newSnowDenMult' , 'multiplier for new snow density' , 'kg m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%newSnowDenScal) = var_info('newSnowDenScal' , 'scaling factor for new snow density' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%constSnowDen) = var_info('constSnowDen' , 'Constant new snow density' , 'kg m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%newSnowDenAdd) = var_info('newSnowDenAdd' , 'Pahaut 1976, additive factor for new snow density' , 'kg m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%newSnowDenMultTemp) = var_info('newSnowDenMultTemp' , 'Pahaut 1976, multiplier for new snow density for air temperature' , 'kg m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%newSnowDenMultWind) = var_info('newSnowDenMultWind' , 'Pahaut 1976, multiplier for new snow density for wind speed' , 'kg m-7/2 s-1/2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%newSnowDenMultAnd) = var_info('newSnowDenMultAnd' , 'Anderson 1976, multiplier for new snow density (Anderson func)' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%newSnowDenBase) = var_info('newSnowDenBase' , 'Anderson 1976, base value that is rasied to the (3/2) power' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! snow compaction + mpar_meta(iLookPARAM%densScalGrowth) = var_info('densScalGrowth' , 'density scaling factor for grain growth' , 'kg-1 m3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%tempScalGrowth) = var_info('tempScalGrowth' , 'temperature scaling factor for grain growth' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%grainGrowthRate) = var_info('grainGrowthRate' , 'rate of grain growth' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%densScalOvrbdn) = var_info('densScalOvrbdn' , 'density scaling factor for overburden pressure' , 'kg-1 m3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%tempScalOvrbdn) = var_info('tempScalOvrbdn' , 'temperature scaling factor for overburden pressure' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%baseViscosity ) = var_info('baseViscosity ' , 'viscosity coefficient at T=T_frz and snow density=0' , 'kg s m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! water flow through snow + mpar_meta(iLookPARAM%Fcapil) = var_info('Fcapil' , 'capillary retention (fraction of total pore volume)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%k_snow) = var_info('k_snow' , 'hydraulic conductivity of snow' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%mw_exp) = var_info('mw_exp' , 'exponent for meltwater flow' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! turbulent heat fluxes + mpar_meta(iLookPARAM%z0Snow) = var_info('z0Snow' , 'roughness length of snow' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%z0Soil) = var_info('z0Soil' , 'roughness length of bare soil below the canopy' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%z0Canopy) = var_info('z0Canopy' , 'roughness length of the canopy' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zpdFraction) = var_info('zpdFraction' , 'zero plane displacement / canopy height' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%critRichNumber) = var_info('critRichNumber' , 'critical value for the bulk Richardson number' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%Louis79_bparam) = var_info('Louis79_bparam' , 'parameter in Louis (1979) stability function' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%Louis79_cStar) = var_info('Louis79_cStar' , 'parameter in Louis (1979) stability function' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%Mahrt87_eScale) = var_info('Mahrt87_eScale' , 'exponential scaling factor in the Mahrt (1987) stability function', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%leafExchangeCoeff) = var_info('leafExchangeCoeff' , 'turbulent exchange coeff between canopy surface and canopy air' , 'm s-(1/2)' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%windReductionParam) = var_info('windReductionParam' , 'canopy wind reduction parameter' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! stomatal conductance + mpar_meta(iLookPARAM%Kc25) = var_info('Kc25' , 'Michaelis-Menten constant for CO2 at 25 degrees C' , 'umol mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%Ko25) = var_info('Ko25' , 'Michaelis-Menten constant for O2 at 25 degrees C' , 'mol mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%Kc_qFac) = var_info('Kc_qFac' , 'factor in the q10 function defining temperature controls on Kc' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%Ko_qFac) = var_info('Ko_qFac' , 'factor in the q10 function defining temperature controls on Ko' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%kc_Ha) = var_info('kc_Ha' , 'activation energy for the Michaelis-Menten constant for CO2' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%ko_Ha) = var_info('ko_Ha' , 'activation energy for the Michaelis-Menten constant for O2' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%vcmax25_canopyTop) = var_info('vcmax25_canopyTop' , 'potential carboxylation rate at 25 degrees C at the canopy top' , 'umol co2 m-2 s-1', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%vcmax_qFac) = var_info('vcmax_qFac' , 'factor in the q10 function defining temperature controls on vcmax', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%vcmax_Ha) = var_info('vcmax_Ha' , 'activation energy in the vcmax function' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%vcmax_Hd) = var_info('vcmax_Hd' , 'deactivation energy in the vcmax function' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%vcmax_Sv) = var_info('vcmax_Sv' , 'entropy term in the vcmax function' , 'J mol-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%vcmax_Kn) = var_info('vcmax_Kn' , 'foliage nitrogen decay coefficient' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%jmax25_scale) = var_info('jmax25_scale' , 'scaling factor to relate jmax25 to vcmax25' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%jmax_Ha) = var_info('jmax_Ha' , 'activation energy in the jmax function' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%jmax_Hd) = var_info('jmax_Hd' , 'deactivation energy in the jmax function' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%jmax_Sv) = var_info('jmax_Sv' , 'entropy term in the jmax function' , 'J mol-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%fractionJ) = var_info('fractionJ' , 'fraction of light lost by other than the chloroplast lamellae' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%quantamYield) = var_info('quantamYield' , 'quantam yield' , 'mol e mol-1 q' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%vpScaleFactor) = var_info('vpScaleFactor' , 'vapor pressure scaling factor in stomatal conductance function' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%cond2photo_slope) = var_info('cond2photo_slope' , 'slope of conductance-photosynthesis relationship' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%minStomatalConductance)= var_info('minStomatalConductance', 'minimum stomatal conductance' , 'umol H2O m-2 s-1', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! vegetation properties + mpar_meta(iLookPARAM%winterSAI) = var_info('winterSAI' , 'stem area index prior to the start of the growing season' , 'm2 m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%summerLAI) = var_info('summerLAI' , 'maximum leaf area index at the peak of the growing season' , 'm2 m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%rootScaleFactor1) = var_info('rootScaleFactor1' , '1st scaling factor (a) in Y = 1 - 0.5*( exp(-aZ) + exp(-bZ) )' , 'm-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%rootScaleFactor2) = var_info('rootScaleFactor2' , '2nd scaling factor (b) in Y = 1 - 0.5*( exp(-aZ) + exp(-bZ) )' , 'm-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%rootingDepth) = var_info('rootingDepth' , 'rooting depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%rootDistExp) = var_info('rootDistExp' , 'exponent for the vertical distribution of root density' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%plantWiltPsi) = var_info('plantWiltPsi' , 'matric head at wilting point' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%soilStressParam) = var_info('soilStressParam' , 'parameter in the exponential soil stress function' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%critSoilWilting) = var_info('critSoilWilting' , 'critical vol. liq. water content when plants are wilting' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%critSoilTranspire) = var_info('critSoilTranspire' , 'critical vol. liq. water content when transpiration is limited' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%critAquiferTranspire) = var_info('critAquiferTranspire' , 'critical aquifer storage value when transpiration is limited' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%minStomatalResistance) = var_info('minStomatalResistance' , 'minimum stomatal resistance' , 's m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%leafDimension) = var_info('leafDimension' , 'characteristic leaf dimension' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%heightCanopyTop) = var_info('heightCanopyTop' , 'height of top of the vegetation canopy above ground surface' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%heightCanopyBottom) = var_info('heightCanopyBottom' , 'height of bottom of the vegetation canopy above ground surface' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%specificHeatVeg) = var_info('specificHeatVeg' , 'specific heat of vegetation' , 'J kg-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%maxMassVegetation) = var_info('maxMassVegetation' , 'maximum mass of vegetation (full foliage)' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%throughfallScaleSnow) = var_info('throughfallScaleSnow' , 'scaling factor for throughfall (snow)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%throughfallScaleRain) = var_info('throughfallScaleRain' , 'scaling factor for throughfall (rain)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%refInterceptCapSnow) = var_info('refInterceptCapSnow' , 'reference canopy interception capacity per unit leaf area (snow)' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%refInterceptCapRain) = var_info('refInterceptCapRain' , 'canopy interception capacity per unit leaf area (rain)' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%snowUnloadingCoeff) = var_info('snowUnloadingCoeff' , 'time constant for unloading of snow from the forest canopy' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%canopyDrainageCoeff) = var_info('canopyDrainageCoeff' , 'time constant for drainage of liquid water from the forest canopy', 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%ratioDrip2Unloading) = var_info('ratioDrip2Unloading' , 'ratio of canopy drip to unloading of snow from the forest canopy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%canopyWettingFactor) = var_info('canopyWettingFactor' , 'maximum wetted fraction of the canopy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%canopyWettingExp) = var_info('canopyWettingExp' , 'exponent in canopy wetting function' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%minTempUnloading) = var_info('minTempUnloading' , 'min temp for unloading in windySnow' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%rateTempUnloading) = var_info('rateTempUnloading' , 'how quickly to unload due to temperature' , 'K s' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%minWindUnloading) = var_info('minWindUnloading' , 'min wind speed for unloading in windySnow' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%rateWindUnloading) = var_info('rateWindUnloading' , 'how quickly to unload due to wind' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! soil properties + mpar_meta(iLookPARAM%soil_dens_intr) = var_info('soil_dens_intr' , 'intrinsic soil density' , 'kg m-3' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%thCond_soil) = var_info('thCond_soil' , 'thermal conductivity of soil (includes quartz and other minerals)', 'W m-1 K-1' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%frac_sand) = var_info('frac_sand' , 'fraction of sand' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%frac_silt) = var_info('frac_silt' , 'fraction of silt' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%frac_clay) = var_info('frac_clay' , 'fraction of clay' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%theta_sat) = var_info('theta_sat' , 'soil porosity' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%theta_res) = var_info('theta_res' , 'volumetric residual water content' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%vGn_alpha) = var_info('vGn_alpha' , 'van Genuchten "alpha" parameter' , 'm-1' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%vGn_n) = var_info('vGn_n' , 'van Genuchten "n" parameter' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%k_soil) = var_info('k_soil' , 'saturated hydraulic conductivity' , 'm s-1' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%k_macropore) = var_info('k_macropore' , 'saturated hydraulic conductivity for macropores' , 'm s-1' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) + ! scalar soil properties + mpar_meta(iLookPARAM%fieldCapacity) = var_info('fieldCapacity' , 'soil field capacity (vol liq water content when baseflow begins)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%wettingFrontSuction) = var_info('wettingFrontSuction' , 'Green-Ampt wetting front suction' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%theta_mp) = var_info('theta_mp' , 'volumetric liquid water content when macropore flow begins' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%mpExp) = var_info('mpExp' , 'empirical exponent in macropore flow equation' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%kAnisotropic) = var_info('kAnisotropic' , 'anisotropy factor for lateral hydraulic conductivity' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zScale_TOPMODEL) = var_info('zScale_TOPMODEL' , 'TOPMODEL scaling factor used in lower boundary condition for soil', 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%compactedDepth) = var_info('compactedDepth' , 'depth where k_soil reaches the compacted value given by CH78' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%aquiferBaseflowRate) = var_info('aquiferBaseflowRate' , 'baseflow rate when aquifer storage = aquiferScaleFactor' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%aquiferScaleFactor) = var_info('aquiferScaleFactor' , 'scaling factor for aquifer storage in the big bucket' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%aquiferBaseflowExp) = var_info('aquiferBaseflowExp' , 'baseflow exponent' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%qSurfScale) = var_info('qSurfScale' , 'scaling factor in the surface runoff parameterization' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%specificYield) = var_info('specificYield' , 'specific yield' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%specificStorage) = var_info('specificStorage' , 'specific storage coefficient' , 'm-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%f_impede) = var_info('f_impede' , 'ice impedence factor' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%soilIceScale) = var_info('soilIceScale' , 'scaling factor for depth of soil ice, used to get frozen fraction', 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%soilIceCV) = var_info('soilIceCV' , 'CV of depth of soil ice, used to get frozen fraction' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! algorithmic control parameters + mpar_meta(iLookPARAM%minwind) = var_info('minwind' , 'minimum wind speed' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%minstep) = var_info('minstep' , 'minimum length of the time step' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%maxstep) = var_info('maxstep' , 'maximum length of the time step' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%wimplicit) = var_info('wimplicit' , 'weight assigned to the start-of-step fluxes (alpha)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%maxiter) = var_info('maxiter' , 'maximum number of iterations' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_liquid) = var_info('relConvTol_liquid' , 'relative convergence tolerance for vol frac liq water' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_liquid) = var_info('absConvTol_liquid' , 'absolute convergence tolerance for vol frac liq water' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_matric) = var_info('relConvTol_matric' , 'relative convergence tolerance for matric head' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_matric) = var_info('absConvTol_matric' , 'absolute convergence tolerance for matric head' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_energy) = var_info('relConvTol_energy' , 'relative convergence tolerance for energy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_energy) = var_info('absConvTol_energy' , 'absolute convergence tolerance for energy' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_aquifr) = var_info('relConvTol_aquifr' , 'relative convergence tolerance for aquifer storage' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_aquifr) = var_info('absConvTol_aquifr' , 'absolute convergence tolerance for aquifer storage' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zmin) = var_info('zmin' , 'minimum layer depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zmax) = var_info('zmax' , 'maximum layer depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zminLayer1) = var_info('zminLayer1' , 'minimum layer depth for the 1st (top) layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zminLayer2) = var_info('zminLayer2' , 'minimum layer depth for the 2nd layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zminLayer3) = var_info('zminLayer3' , 'minimum layer depth for the 3rd layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zminLayer4) = var_info('zminLayer4' , 'minimum layer depth for the 4th layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zminLayer5) = var_info('zminLayer5' , 'minimum layer depth for the 5th (bottom) layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zmaxLayer1_lower) = var_info('zmaxLayer1_lower' , 'maximum layer depth for the 1st (top) layer when only 1 layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zmaxLayer2_lower) = var_info('zmaxLayer2_lower' , 'maximum layer depth for the 2nd layer when only 2 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zmaxLayer3_lower) = var_info('zmaxLayer3_lower' , 'maximum layer depth for the 3rd layer when only 3 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zmaxLayer4_lower) = var_info('zmaxLayer4_lower' , 'maximum layer depth for the 4th layer when only 4 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zmaxLayer1_upper) = var_info('zmaxLayer1_upper' , 'maximum layer depth for the 1st (top) layer when > 1 layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zmaxLayer2_upper) = var_info('zmaxLayer2_upper' , 'maximum layer depth for the 2nd layer when > 2 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zmaxLayer3_upper) = var_info('zmaxLayer3_upper' , 'maximum layer depth for the 3rd layer when > 3 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zmaxLayer4_upper) = var_info('zmaxLayer4_upper' , 'maximum layer depth for the 4th layer when > 4 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + + ! ----- + ! * basin parameter data... + ! ------------------------- + bpar_meta(iLookBPAR%basin__aquiferHydCond) = var_info('basin__aquiferHydCond' , 'hydraulic conductivity of the aquifer' , 'm s-1', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + bpar_meta(iLookBPAR%basin__aquiferScaleFactor) = var_info('basin__aquiferScaleFactor', 'scaling factor for aquifer storage in the big bucket' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + bpar_meta(iLookBPAR%basin__aquiferBaseflowExp) = var_info('basin__aquiferBaseflowExp', 'baseflow exponent for the big bucket' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + bpar_meta(iLookBPAR%routingGammaShape) = var_info('routingGammaShape' , 'shape parameter in Gamma distribution used for sub-grid routing', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + bpar_meta(iLookBPAR%routingGammaScale) = var_info('routingGammaScale' , 'scale parameter in Gamma distribution used for sub-grid routing', 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + + ! ----- + ! * local model prognostic (state) variables... + ! --------------------------------------------- + ! define variables for time stepping + prog_meta(iLookPROG%dt_init) = var_info('dt_init' , 'length of initial time step at start of next data interval' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! state variables for vegetation + prog_meta(iLookPROG%scalarCanopyIce) = var_info('scalarCanopyIce' , 'mass of ice on the vegetation canopy' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + prog_meta(iLookPROG%scalarCanopyLiq) = var_info('scalarCanopyLiq' , 'mass of liquid water on the vegetation canopy' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + prog_meta(iLookPROG%scalarCanopyWat) = var_info('scalarCanopyWat' , 'mass of total water on the vegetation canopy' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + prog_meta(iLookPROG%scalarCanairTemp) = var_info('scalarCanairTemp' , 'temperature of the canopy air space' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + prog_meta(iLookPROG%scalarCanopyTemp) = var_info('scalarCanopyTemp' , 'temperature of the vegetation canopy' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! state variables for snow + prog_meta(iLookPROG%spectralSnowAlbedoDiffuse) = var_info('spectralSnowAlbedoDiffuse' , 'diffuse snow albedo for individual spectral bands' , '-' , get_ixVarType('wLength'), iMissVec, iMissVec, .false.) + prog_meta(iLookPROG%scalarSnowAlbedo) = var_info('scalarSnowAlbedo' , 'snow albedo for the entire spectral band' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + prog_meta(iLookPROG%scalarSnowDepth) = var_info('scalarSnowDepth' , 'total snow depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + prog_meta(iLookPROG%scalarSWE) = var_info('scalarSWE' , 'snow water equivalent' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + prog_meta(iLookPROG%scalarSfcMeltPond) = var_info('scalarSfcMeltPond' , 'ponded water caused by melt of the "snow without a layer"' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! define state variables for the snow+soil domain + prog_meta(iLookPROG%mLayerTemp) = var_info('mLayerTemp' , 'temperature of each layer' , 'K' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + prog_meta(iLookPROG%mLayerVolFracIce) = var_info('mLayerVolFracIce' , 'volumetric fraction of ice in each layer' , '-' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + prog_meta(iLookPROG%mLayerVolFracLiq) = var_info('mLayerVolFracLiq' , 'volumetric fraction of liquid water in each layer' , '-' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + prog_meta(iLookPROG%mLayerVolFracWat) = var_info('mLayerVolFracWat' , 'volumetric fraction of total water in each layer' , '-' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + prog_meta(iLookPROG%mLayerMatricHead) = var_info('mLayerMatricHead' , 'matric head of water in the soil' , 'm' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + ! other state variables + prog_meta(iLookPROG%scalarAquiferStorage) = var_info('scalarAquiferStorage' , 'water required to bring aquifer to the bottom of the soil profile', 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + prog_meta(iLookPROG%scalarSurfaceTemp) = var_info('scalarSurfaceTemp' , 'surface temperature (just a copy of the upper-layer temperature)' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! define coordinate variables + prog_meta(iLookPROG%mLayerDepth) = var_info('mLayerDepth' , 'depth of each layer' , 'm' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + prog_meta(iLookPROG%mLayerHeight) = var_info('mLayerHeight' , 'height of the layer mid-point (top of soil = 0)' , 'm' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + prog_meta(iLookPROG%iLayerHeight) = var_info('iLayerHeight' , 'height of the layer interface (top of soil = 0)' , 'm' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + + ! ----- + ! * local model diagnostic variables... + ! ------------------------------------- + ! local properties + diag_meta(iLookDIAG%scalarCanopyDepth) = var_info('scalarCanopyDepth' , 'canopy depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarGreenVegFraction) = var_info('scalarGreenVegFraction' , 'green vegetation fraction (used to compute LAI)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarBulkVolHeatCapVeg) = var_info('scalarBulkVolHeatCapVeg' , 'bulk volumetric heat capacity of vegetation' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarCanopyEmissivity) = var_info('scalarCanopyEmissivity' , 'effective canopy emissivity' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarRootZoneTemp) = var_info('scalarRootZoneTemp' , 'average temperature of the root zone' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarLAI) = var_info('scalarLAI' , 'one-sided leaf area index' , 'm2 m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarSAI) = var_info('scalarSAI' , 'one-sided stem area index' , 'm2 m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarExposedLAI) = var_info('scalarExposedLAI' , 'exposed leaf area index (after burial by snow)' , 'm2 m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarExposedSAI) = var_info('scalarExposedSAI' , 'exposed stem area index (after burial by snow)' , 'm2 m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarAdjMeasHeight) = var_info('scalarAdjMeasHeight' , 'adjusted measurement height for cases snowDepth>mHeight' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarCanopyIceMax) = var_info('scalarCanopyIceMax' , 'maximum interception storage capacity for ice' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarCanopyLiqMax) = var_info('scalarCanopyLiqMax' , 'maximum interception storage capacity for liquid water' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarGrowingSeasonIndex) = var_info('scalarGrowingSeasonIndex' , 'growing season index (0=off, 1=on)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarVolHtCap_air) = var_info('scalarVolHtCap_air' , 'volumetric heat capacity air' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarVolHtCap_ice) = var_info('scalarVolHtCap_ice' , 'volumetric heat capacity ice' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarVolHtCap_soil) = var_info('scalarVolHtCap_soil' , 'volumetric heat capacity dry soil' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarVolHtCap_water) = var_info('scalarVolHtCap_water' , 'volumetric heat capacity liquid wat' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%mLayerVolHtCapBulk) = var_info('mLayerVolHtCapBulk' , 'volumetric heat capacity in each layer' , 'J m-3 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarLambda_drysoil) = var_info('scalarLambda_drysoil' , 'thermal conductivity of dry soil' , 'W m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarLambda_wetsoil) = var_info('scalarLambda_wetsoil' , 'thermal conductivity of wet soil' , 'W m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%mLayerThermalC) = var_info('mLayerThermalC' , 'thermal conductivity at the mid-point of each layer' , 'W m-1 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%iLayerThermalC) = var_info('iLayerThermalC' , 'thermal conductivity at the interface of each layer' , 'W m-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + ! enthalpy + diag_meta(iLookDIAG%scalarCanairEnthalpy) = var_info('scalarCanairEnthalpy' , 'enthalpy of the canopy air space' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarCanopyEnthalpy) = var_info('scalarCanopyEnthalpy' , 'enthalpy of the vegetation canopy' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%mLayerEnthalpy) = var_info('mLayerEnthalpy' , 'enthalpy of the snow+soil layers' , 'J m-3' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + ! forcing + diag_meta(iLookDIAG%scalarVPair) = var_info('scalarVPair' , 'vapor pressure of the air above the vegetation canopy' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarVP_CanopyAir) = var_info('scalarVP_CanopyAir' , 'vapor pressure of the canopy air space' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarTwetbulb) = var_info('scalarTwetbulb' , 'wet bulb temperature' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarSnowfallTemp) = var_info('scalarSnowfallTemp' , 'temperature of fresh snow' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarNewSnowDensity) = var_info('scalarNewSnowDensity' , 'density of fresh snow' , 'kg m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarO2air) = var_info('scalarO2air' , 'atmospheric o2 concentration' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarCO2air) = var_info('scalarCO2air' , 'atmospheric co2 concentration' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! shortwave radiation + diag_meta(iLookDIAG%scalarCosZenith) = var_info('scalarCosZenith' , 'cosine of the solar zenith angle' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarFractionDirect) = var_info('scalarFractionDirect' , 'fraction of direct radiation (0-1)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarCanopySunlitFraction) = var_info('scalarCanopySunlitFraction' , 'sunlit fraction of canopy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarCanopySunlitLAI) = var_info('scalarCanopySunlitLAI' , 'sunlit leaf area' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarCanopyShadedLAI) = var_info('scalarCanopyShadedLAI' , 'shaded leaf area' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%spectralAlbGndDirect) = var_info('spectralAlbGndDirect' , 'direct albedo of underlying surface for each spectral band' , '-' , get_ixVarType('wLength'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%spectralAlbGndDiffuse) = var_info('spectralAlbGndDiffuse' , 'diffuse albedo of underlying surface for each spectral band' , '-' , get_ixVarType('wLength'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarGroundAlbedo) = var_info('scalarGroundAlbedo' , 'albedo of the ground surface' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! turbulent heat transfer + diag_meta(iLookDIAG%scalarLatHeatSubVapCanopy) = var_info('scalarLatHeatSubVapCanopy' , 'latent heat of sublimation/vaporization used for veg canopy' , 'J kg-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarLatHeatSubVapGround) = var_info('scalarLatHeatSubVapGround' , 'latent heat of sublimation/vaporization used for ground surface' , 'J kg-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarSatVP_CanopyTemp) = var_info('scalarSatVP_CanopyTemp' , 'saturation vapor pressure at the temperature of vegetation canopy', 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarSatVP_GroundTemp) = var_info('scalarSatVP_GroundTemp' , 'saturation vapor pressure at the temperature of the ground' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarZ0Canopy) = var_info('scalarZ0Canopy' , 'roughness length of the canopy' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarWindReductionFactor) = var_info('scalarWindReductionFactor' , 'canopy wind reduction factor' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarZeroPlaneDisplacement) = var_info('scalarZeroPlaneDisplacement' , 'zero plane displacement' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarRiBulkCanopy) = var_info('scalarRiBulkCanopy' , 'bulk Richardson number for the canopy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarRiBulkGround) = var_info('scalarRiBulkGround' , 'bulk Richardson number for the ground surface' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarCanopyStabilityCorrection) = var_info('scalarCanopyStabilityCorrection', 'stability correction for the canopy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarGroundStabilityCorrection) = var_info('scalarGroundStabilityCorrection', 'stability correction for the ground surface' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! evapotranspiration + diag_meta(iLookDIAG%scalarIntercellularCO2Sunlit) = var_info('scalarIntercellularCO2Sunlit' , 'carbon dioxide partial pressure of leaf interior (sunlit leaves)' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarIntercellularCO2Shaded) = var_info('scalarIntercellularCO2Shaded' , 'carbon dioxide partial pressure of leaf interior (shaded leaves)' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarTranspireLim) = var_info('scalarTranspireLim' , 'aggregate soil moisture and aquifer control on transpiration' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarTranspireLimAqfr) = var_info('scalarTranspireLimAqfr' , 'aquifer storage control on transpiration' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarFoliageNitrogenFactor) = var_info('scalarFoliageNitrogenFactor' , 'foliage nitrogen concentration (1=saturated)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarSoilRelHumidity) = var_info('scalarSoilRelHumidity' , 'relative humidity in the soil pores in the upper-most soil layer' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%mLayerTranspireLim) = var_info('mLayerTranspireLim' , 'soil moist & veg limit on transpiration for each layer' , '-' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%mLayerRootDensity) = var_info('mLayerRootDensity' , 'fraction of roots in each soil layer' , '-' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarAquiferRootFrac) = var_info('scalarAquiferRootFrac' , 'fraction of roots below the soil profile (in the aquifer)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! canopy hydrology + diag_meta(iLookDIAG%scalarFracLiqVeg) = var_info('scalarFracLiqVeg' , 'fraction of liquid water on vegetation' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarCanopyWetFraction) = var_info('scalarCanopyWetFraction' , 'fraction canopy that is wet' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! snow hydrology + diag_meta(iLookDIAG%scalarSnowAge) = var_info('scalarSnowAge' , 'non-dimensional snow age' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarGroundSnowFraction) = var_info('scalarGroundSnowFraction' , 'fraction ground that is covered with snow' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%spectralSnowAlbedoDirect) = var_info('spectralSnowAlbedoDirect' , 'direct snow albedo for individual spectral bands' , '-' , get_ixVarType('wLength'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%mLayerFracLiqSnow) = var_info('mLayerFracLiqSnow' , 'fraction of liquid water in each snow layer' , '-' , get_ixVarType('midSnow'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%mLayerThetaResid) = var_info('mLayerThetaResid' , 'residual volumetric water content in each snow layer' , '-' , get_ixVarType('midSnow'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%mLayerPoreSpace) = var_info('mLayerPoreSpace' , 'total pore space in each snow layer' , '-' , get_ixVarType('midSnow'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%mLayerMeltFreeze) = var_info('mLayerMeltFreeze' , 'ice content change from melt/freeze in each layer' , 'kg m-3' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + ! soil hydrology + diag_meta(iLookDIAG%scalarInfilArea) = var_info('scalarInfilArea' , 'fraction of unfrozen area where water can infiltrate' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarFrozenArea) = var_info('scalarFrozenArea' , 'fraction of area that is considered impermeable due to soil ice' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarSoilControl) = var_info('scalarSoilControl' , 'soil control on infiltration (1=controlling; 0=not)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%mLayerVolFracAir) = var_info('mLayerVolFracAir' , 'volumetric fraction of air in each layer' , '-' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%mLayerTcrit) = var_info('mLayerTcrit' , 'critical soil temperature above which all water is unfrozen' , 'K' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%mLayerCompress) = var_info('mLayerCompress' , 'change in volumetric water content due to compression of soil' , '-' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarSoilCompress) = var_info('scalarSoilCompress' , 'change in total soil storage due to compression of soil matrix' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%mLayerMatricHeadLiq) = var_info('mLayerMatricHeadLiq' , 'matric potential of liquid water' , 'm' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + ! mass balance check + diag_meta(iLookDIAG%scalarSoilWatBalError) = var_info('scalarSoilWatBalError' , 'error in the total soil water balance' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarAquiferBalError) = var_info('scalarAquiferBalError' , 'error in the aquifer water balance' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarTotalSoilLiq) = var_info('scalarTotalSoilLiq' , 'total mass of liquid water in the soil' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarTotalSoilIce) = var_info('scalarTotalSoilIce' , 'total mass of ice in the soil' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarTotalSoilWat) = var_info('scalarTotalSoilWat' , 'total mass of water in the soil' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! variable shortcuts + diag_meta(iLookDIAG%scalarVGn_m) = var_info('scalarVGn_m' , 'van Genuchten "m" parameter' , '-' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarKappa) = var_info('scalarKappa' , 'constant in the freezing curve function' , 'm K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarVolLatHt_fus) = var_info('scalarVolLatHt_fus' , 'volumetric latent heat of fusion' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! timing information + diag_meta(iLookDIAG%numFluxCalls) = var_info('numFluxCalls' , 'number of flux calls' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%wallClockTime) = var_info('wallClockTime' , 'wall clock time' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + + ! ----- + ! * local model fluxes... + ! ----------------------- + ! net energy and mass fluxes for the vegetation domain + flux_meta(iLookFLUX%scalarCanairNetNrgFlux) = var_info('scalarCanairNetNrgFlux' , 'net energy flux for the canopy air space' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarCanopyNetNrgFlux) = var_info('scalarCanopyNetNrgFlux' , 'net energy flux for the vegetation canopy' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarGroundNetNrgFlux) = var_info('scalarGroundNetNrgFlux' , 'net energy flux for the ground surface' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarCanopyNetLiqFlux) = var_info('scalarCanopyNetLiqFlux' , 'net liquid water flux for the vegetation canopy' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! forcing + flux_meta(iLookFLUX%scalarRainfall) = var_info('scalarRainfall' , 'computed rainfall rate' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarSnowfall) = var_info('scalarSnowfall' , 'computed snowfall rate' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! shortwave radiation + flux_meta(iLookFLUX%spectralIncomingDirect) = var_info('spectralIncomingDirect' , 'incoming direct solar radiation in each wave band' , 'W m-2' , get_ixVarType('wLength'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%spectralIncomingDiffuse) = var_info('spectralIncomingDiffuse' , 'incoming diffuse solar radiation in each wave band' , 'W m-2' , get_ixVarType('wLength'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarCanopySunlitPAR) = var_info('scalarCanopySunlitPAR' , 'average absorbed par for sunlit leaves' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarCanopyShadedPAR) = var_info('scalarCanopyShadedPAR' , 'average absorbed par for shaded leaves' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%spectralBelowCanopyDirect) = var_info('spectralBelowCanopyDirect' , 'downward direct flux below veg layer for each spectral band' , 'W m-2' , get_ixVarType('wLength'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%spectralBelowCanopyDiffuse) = var_info('spectralBelowCanopyDiffuse' , 'downward diffuse flux below veg layer for each spectral band' , 'W m-2' , get_ixVarType('wLength'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarBelowCanopySolar) = var_info('scalarBelowCanopySolar' , 'solar radiation transmitted below the canopy' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarCanopyAbsorbedSolar) = var_info('scalarCanopyAbsorbedSolar' , 'solar radiation absorbed by canopy' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarGroundAbsorbedSolar) = var_info('scalarGroundAbsorbedSolar' , 'solar radiation absorbed by ground' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! longwave radiation + flux_meta(iLookFLUX%scalarLWRadCanopy) = var_info('scalarLWRadCanopy' , 'longwave radiation emitted from the canopy' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarLWRadGround) = var_info('scalarLWRadGround' , 'longwave radiation emitted at the ground surface' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarLWRadUbound2Canopy) = var_info('scalarLWRadUbound2Canopy' , 'downward atmospheric longwave radiation absorbed by the canopy' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarLWRadUbound2Ground) = var_info('scalarLWRadUbound2Ground' , 'downward atmospheric longwave radiation absorbed by the ground' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarLWRadUbound2Ubound) = var_info('scalarLWRadUbound2Ubound' , 'atmospheric radiation refl by ground + lost thru upper boundary' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarLWRadCanopy2Ubound) = var_info('scalarLWRadCanopy2Ubound' , 'longwave radiation emitted from canopy lost thru upper boundary' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarLWRadCanopy2Ground) = var_info('scalarLWRadCanopy2Ground' , 'longwave radiation emitted from canopy absorbed by the ground' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarLWRadCanopy2Canopy) = var_info('scalarLWRadCanopy2Canopy' , 'canopy longwave reflected from ground and absorbed by the canopy' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarLWRadGround2Ubound) = var_info('scalarLWRadGround2Ubound' , 'longwave radiation emitted from ground lost thru upper boundary' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarLWRadGround2Canopy) = var_info('scalarLWRadGround2Canopy' , 'longwave radiation emitted from ground and absorbed by the canopy', 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarLWNetCanopy) = var_info('scalarLWNetCanopy' , 'net longwave radiation at the canopy' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarLWNetGround) = var_info('scalarLWNetGround' , 'net longwave radiation at the ground surface' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarLWNetUbound) = var_info('scalarLWNetUbound' , 'net longwave radiation at the upper atmospheric boundary' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! turbulent heat transfer + flux_meta(iLookFLUX%scalarEddyDiffusCanopyTop) = var_info('scalarEddyDiffusCanopyTop' , 'eddy diffusivity for heat at the top of the canopy' , 'm2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarFrictionVelocity) = var_info('scalarFrictionVelocity' , 'friction velocity (canopy momentum sink)' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarWindspdCanopyTop) = var_info('scalarWindspdCanopyTop' , 'windspeed at the top of the canopy' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarWindspdCanopyBottom) = var_info('scalarWindspdCanopyBottom' , 'windspeed at the height of the bottom of the canopy' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarGroundResistance) = var_info('scalarGroundResistance' , 'below canopy aerodynamic resistance' , 's m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarCanopyResistance) = var_info('scalarCanopyResistance' , 'above canopy aerodynamic resistance' , 's m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarLeafResistance) = var_info('scalarLeafResistance' , 'mean leaf boundary layer resistance per unit leaf area' , 's m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarSoilResistance) = var_info('scalarSoilResistance' , 'soil surface resistance' , 's m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarSenHeatTotal) = var_info('scalarSenHeatTotal' , 'sensible heat from the canopy air space to the atmosphere' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarSenHeatCanopy) = var_info('scalarSenHeatCanopy' , 'sensible heat from the canopy to the canopy air space' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarSenHeatGround) = var_info('scalarSenHeatGround' , 'sensible heat from the ground (below canopy or non-vegetated)' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarLatHeatTotal) = var_info('scalarLatHeatTotal' , 'latent heat from the canopy air space to the atmosphere' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarLatHeatCanopyEvap) = var_info('scalarLatHeatCanopyEvap' , 'evaporation latent heat from the canopy to the canopy air space' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarLatHeatCanopyTrans) = var_info('scalarLatHeatCanopyTrans' , 'transpiration latent heat from the canopy to the canopy air space', 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarLatHeatGround) = var_info('scalarLatHeatGround' , 'latent heat from the ground (below canopy or non-vegetated)' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarCanopyAdvectiveHeatFlux) = var_info('scalarCanopyAdvectiveHeatFlux' , 'heat advected to the canopy with precipitation (snow + rain)' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarGroundAdvectiveHeatFlux) = var_info('scalarGroundAdvectiveHeatFlux' , 'heat advected to the ground with throughfall + unloading/drainage', 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarCanopySublimation) = var_info('scalarCanopySublimation' , 'canopy sublimation/frost' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarSnowSublimation) = var_info('scalarSnowSublimation' , 'snow sublimation/frost (below canopy or non-vegetated)' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! liquid water fluxes associated with evapotranspiration + flux_meta(iLookFLUX%scalarStomResistSunlit) = var_info('scalarStomResistSunlit' , 'stomatal resistance for sunlit leaves' , 's m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarStomResistShaded) = var_info('scalarStomResistShaded' , 'stomatal resistance for shaded leaves' , 's m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarPhotosynthesisSunlit) = var_info('scalarPhotosynthesisSunlit' , 'sunlit photosynthesis' , 'umolco2 m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarPhotosynthesisShaded) = var_info('scalarPhotosynthesisShaded' , 'shaded photosynthesis' , 'umolco2 m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarCanopyTranspiration) = var_info('scalarCanopyTranspiration' , 'canopy transpiration' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarCanopyEvaporation) = var_info('scalarCanopyEvaporation' , 'canopy evaporation/condensation' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarGroundEvaporation) = var_info('scalarGroundEvaporation' , 'ground evaporation/condensation (below canopy or non-vegetated)' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%mLayerTranspire) = var_info('mLayerTranspire' , 'transpiration loss from each soil layer' , 'm s-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + ! liquid and solid water fluxes through the canopy + flux_meta(iLookFLUX%scalarThroughfallSnow) = var_info('scalarThroughfallSnow' , 'snow that reaches the ground without ever touching the canopy' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarThroughfallRain) = var_info('scalarThroughfallRain' , 'rain that reaches the ground without ever touching the canopy' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarCanopySnowUnloading) = var_info('scalarCanopySnowUnloading' , 'unloading of snow from the vegetation canopy' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarCanopyLiqDrainage) = var_info('scalarCanopyLiqDrainage' , 'drainage of liquid water from the vegetation canopy' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarCanopyMeltFreeze) = var_info('scalarCanopyMeltFreeze' , 'melt/freeze of water stored in the canopy' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! energy fluxes and for the snow and soil domains + flux_meta(iLookFLUX%iLayerConductiveFlux) = var_info('iLayerConductiveFlux' , 'conductive energy flux at layer interfaces' , 'W m-2' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%iLayerAdvectiveFlux) = var_info('iLayerAdvectiveFlux' , 'advective energy flux at layer interfaces' , 'W m-2' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%iLayerNrgFlux) = var_info('iLayerNrgFlux' , 'energy flux at layer interfaces' , 'W m-2' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%mLayerNrgFlux) = var_info('mLayerNrgFlux' , 'net energy flux for each layer within the snow+soil domain' , 'J m-3 s-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + ! liquid water fluxes for the snow domain + flux_meta(iLookFLUX%scalarSnowDrainage) = var_info('scalarSnowDrainage' , 'drainage from the bottom of the snow profile' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%iLayerLiqFluxSnow) = var_info('iLayerLiqFluxSnow' , 'liquid flux at snow layer interfaces' , 'm s-1' , get_ixVarType('ifcSnow'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%mLayerLiqFluxSnow) = var_info('mLayerLiqFluxSnow' , 'net liquid water flux for each snow layer' , 's-1' , get_ixVarType('midSnow'), iMissVec, iMissVec, .false.) + ! liquid water fluxes for the soil domain + flux_meta(iLookFLUX%scalarRainPlusMelt) = var_info('scalarRainPlusMelt' , 'rain plus melt, used as input to soil before surface runoff' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarMaxInfilRate) = var_info('scalarMaxInfilRate' , 'maximum infiltration rate' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarInfiltration) = var_info('scalarInfiltration' , 'infiltration of water into the soil profile' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarExfiltration) = var_info('scalarExfiltration' , 'exfiltration of water from the top of the soil profile' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarSurfaceRunoff) = var_info('scalarSurfaceRunoff' , 'surface runoff' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%mLayerSatHydCondMP) = var_info('mLayerSatHydCondMP' , 'saturated hydraulic conductivity of macropores in each layer' , 'm s-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%mLayerSatHydCond) = var_info('mLayerSatHydCond' , 'saturated hydraulic conductivity in each layer' , 'm s-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%iLayerSatHydCond) = var_info('iLayerSatHydCond' , 'saturated hydraulic conductivity in each layer interface' , 'm s-1' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%mLayerHydCond) = var_info('mLayerHydCond' , 'hydraulic conductivity in each layer' , 'm s-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%iLayerLiqFluxSoil) = var_info('iLayerLiqFluxSoil' , 'liquid flux at soil layer interfaces' , 'm s-1' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%mLayerLiqFluxSoil) = var_info('mLayerLiqFluxSoil' , 'net liquid water flux for each soil layer' , 's-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%mLayerBaseflow) = var_info('mLayerBaseflow' , 'baseflow from each soil layer' , 'm s-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%mLayerColumnInflow) = var_info('mLayerColumnInflow' , 'total inflow to each layer in a given soil column' , 'm3 s-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%mLayerColumnOutflow) = var_info('mLayerColumnOutflow' , 'total outflow from each layer in a given soil column' , 'm3 s-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarSoilBaseflow) = var_info('scalarSoilBaseflow' , 'total baseflow from the soil profile' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarSoilDrainage) = var_info('scalarSoilDrainage' , 'drainage from the bottom of the soil profile' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarAquiferRecharge) = var_info('scalarAquiferRecharge' , 'recharge to the aquifer' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarAquiferTranspire) = var_info('scalarAquiferTranspire' , 'transpiration loss from the aquifer' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarAquiferBaseflow) = var_info('scalarAquiferBaseflow' , 'baseflow from the aquifer' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! derived variables + flux_meta(iLookFLUX%scalarTotalET) = var_info('scalarTotalET' , 'total ET' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarTotalRunoff) = var_info('scalarTotalRunoff' , 'total runoff' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + flux_meta(iLookFLUX%scalarNetRadiation) = var_info('scalarNetRadiation' , 'net radiation' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + + ! ----- + ! * local flux derivatives... + ! --------------------------- + ! derivatives in net vegetation energy fluxes w.r.t. relevant state variables + deriv_meta(iLookDERIV%dCanairNetFlux_dCanairTemp) = var_info('dCanairNetFlux_dCanairTemp' , 'derivative in net canopy air space flux w.r.t. canopy air temperature', 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCanairNetFlux_dCanopyTemp) = var_info('dCanairNetFlux_dCanopyTemp' , 'derivative in net canopy air space flux w.r.t. canopy temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCanairNetFlux_dGroundTemp) = var_info('dCanairNetFlux_dGroundTemp' , 'derivative in net canopy air space flux w.r.t. ground temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCanopyNetFlux_dCanairTemp) = var_info('dCanopyNetFlux_dCanairTemp' , 'derivative in net canopy flux w.r.t. canopy air temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCanopyNetFlux_dCanopyTemp) = var_info('dCanopyNetFlux_dCanopyTemp' , 'derivative in net canopy flux w.r.t. canopy temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCanopyNetFlux_dGroundTemp) = var_info('dCanopyNetFlux_dGroundTemp' , 'derivative in net canopy flux w.r.t. ground temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCanopyNetFlux_dCanWat) = var_info('dCanopyNetFlux_dCanWat' , 'derivative in net canopy fluxes w.r.t. canopy total water content' , 'J kg-1 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dGroundNetFlux_dCanairTemp) = var_info('dGroundNetFlux_dCanairTemp' , 'derivative in net ground flux w.r.t. canopy air temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dGroundNetFlux_dCanopyTemp) = var_info('dGroundNetFlux_dCanopyTemp' , 'derivative in net ground flux w.r.t. canopy temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dGroundNetFlux_dGroundTemp) = var_info('dGroundNetFlux_dGroundTemp' , 'derivative in net ground flux w.r.t. ground temperature' , 'W m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dGroundNetFlux_dCanWat) = var_info('dGroundNetFlux_dCanWat' , 'derivative in net ground fluxes w.r.t. canopy total water content' , 'J kg-1 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! derivatives in evaporative fluxes w.r.t. relevant state variables + deriv_meta(iLookDERIV%dCanopyEvaporation_dTCanair) = var_info('dCanopyEvaporation_dTCanair' , 'derivative in canopy evaporation w.r.t. canopy air temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCanopyEvaporation_dTCanopy) = var_info('dCanopyEvaporation_dTCanopy' , 'derivative in canopy evaporation w.r.t. canopy temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCanopyEvaporation_dTGround) = var_info('dCanopyEvaporation_dTGround' , 'derivative in canopy evaporation w.r.t. ground temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCanopyEvaporation_dCanWat) = var_info('dCanopyEvaporation_dCanWat' , 'derivative in canopy evaporation w.r.t. canopy total water content' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dGroundEvaporation_dTCanair) = var_info('dGroundEvaporation_dTCanair' , 'derivative in ground evaporation w.r.t. canopy air temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dGroundEvaporation_dTCanopy) = var_info('dGroundEvaporation_dTCanopy' , 'derivative in ground evaporation w.r.t. canopy temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dGroundEvaporation_dTGround) = var_info('dGroundEvaporation_dTGround' , 'derivative in ground evaporation w.r.t. ground temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dGroundEvaporation_dCanWat) = var_info('dGroundEvaporation_dCanWat' , 'derivative in ground evaporation w.r.t. canopy total water content' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! derivatives in transpiration + deriv_meta(iLookDERIV%dCanopyTrans_dTCanair) = var_info('dCanopyTrans_dTCanair' , 'derivative in canopy transpiration w.r.t. canopy air temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCanopyTrans_dTCanopy) = var_info('dCanopyTrans_dTCanopy' , 'derivative in canopy transpiration w.r.t. canopy temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCanopyTrans_dTGround) = var_info('dCanopyTrans_dTGround' , 'derivative in canopy transpiration w.r.t. ground temperature' , 'kg m-2 s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCanopyTrans_dCanWat) = var_info('dCanopyTrans_dCanWat' , 'derivative in canopy transpiration w.r.t. canopy total water content' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! derivatives in canopy water w.r.t canopy temperature + deriv_meta(iLookDERIV%dTheta_dTkCanopy) = var_info('dTheta_dTkCanopy' , 'derivative of volumetric liquid water content w.r.t. temperature' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%d2Theta_dTkCanopy2) = var_info('d2Theta_dTkCanopy2' , 'second derivative of volumetric liquid water content w.r.t. temperature', 'K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCanLiq_dTcanopy) = var_info('dCanLiq_dTcanopy' , 'derivative of canopy liquid storage w.r.t. temperature' , 'kg m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dFracLiqVeg_dTkCanopy) = var_info('dFracLiqVeg_dTkCanopy' , 'derivative in fraction of (throughfall + drainage) w.r.t. temperature', 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! derivatives in canopy liquid fluxes w.r.t. canopy water + deriv_meta(iLookDERIV%scalarCanopyLiqDeriv) = var_info('scalarCanopyLiqDeriv' , 'derivative in (throughfall + drainage) w.r.t. canopy liquid water' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%scalarThroughfallRainDeriv) = var_info('scalarThroughfallRainDeriv' , 'derivative in throughfall w.r.t. canopy liquid water' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%scalarCanopyLiqDrainageDeriv) = var_info('scalarCanopyLiqDrainageDeriv' , 'derivative in canopy drainage w.r.t. canopy liquid water' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below + deriv_meta(iLookDERIV%dNrgFlux_dTempAbove) = var_info('dNrgFlux_dTempAbove' , 'derivatives in the flux w.r.t. temperature in the layer above' , 'J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dNrgFlux_dTempBelow) = var_info('dNrgFlux_dTempBelow' , 'derivatives in the flux w.r.t. temperature in the layer below' , 'J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below + deriv_meta(iLookDERIV%dNrgFlux_dWatAbove) = var_info('dNrgFlux_dWatAbove' , 'derivatives in the flux w.r.t. water state in the layer above' , 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dNrgFlux_dWatBelow) = var_info('dNrgFlux_dWatBelow' , 'derivatives in the flux w.r.t. water state in the layer below' , 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above + deriv_meta(iLookDERIV%iLayerLiqFluxSnowDeriv) = var_info('iLayerLiqFluxSnowDeriv' , 'derivative in vertical liquid water flux at layer interfaces' , 'm s-1' , get_ixVarType('ifcSnow'), iMissVec, iMissVec, .false.) + ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables + deriv_meta(iLookDERIV%dVolTot_dPsi0) = var_info('dVolTot_dPsi0' , 'derivative in total water content w.r.t. total water matric potential', 'm-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%d2VolTot_d2Psi0) = var_info('d2VolTot_d2Psi0' , 'second derivative in total water content w.r.t. total water matric potential', 'm-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCompress_dPsi) = var_info('dCompress_dPsi' , 'derivative in compressibility w.r.t matric head' , 'm-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%mLayerdTheta_dPsi) = var_info('mLayerdTheta_dPsi' , 'derivative in the soil water characteristic w.r.t. psi' , 'm-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%mLayerdPsi_dTheta) = var_info('mLayerdPsi_dTheta' , 'derivative in the soil water characteristic w.r.t. theta' , 'm' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dq_dHydStateAbove) = var_info('dq_dHydStateAbove' , 'change in flux at layer interfaces w.r.t. states in the layer above' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dq_dHydStateBelow) = var_info('dq_dHydStateBelow' , 'change in flux at layer interfaces w.r.t. states in the layer below' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dq_dHydStateLayerSurfVec) = var_info('dq_dHydStateLayerSurfVec' , 'change in the flux in soil surface interface w.r.t. state variables in layers' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) + ! derivative in baseflow flux w.r.t. aquifer storage + deriv_meta(iLookDERIV%dBaseflow_dAquifer) = var_info('dBaseflow_dAquifer' , 'derivative in baseflow flux w.r.t. aquifer storage' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables + deriv_meta(iLookDERIV%dq_dNrgStateAbove) = var_info('dq_dNrgStateAbove' , 'change in flux at layer interfaces w.r.t. states in the layer above' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dq_dNrgStateBelow) = var_info('dq_dNrgStateBelow' , 'change in flux at layer interfaces w.r.t. states in the layer below' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dq_dNrgStateLayerSurfVec) = var_info('dq_dNrgStateLayerSurfVec' , 'change in the flux in soil surface interface w.r.t. state variables in layers' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dPsiLiq_dTemp) = var_info('dPsiLiq_dTemp' , 'derivative in the liquid water matric potential w.r.t. temperature' , 'm K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dPsiLiq_dPsi0) = var_info('dPsiLiq_dPsi0' , 'derivative in liquid matric potential w.r.t. total matric potential' , '-' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + ! derivatives in soil transpiration w.r.t. canopy state variables + deriv_meta(iLookDERIV%mLayerdTrans_dTCanair) = var_info('mLayerdTrans_dTCanair' , 'derivatives in the soil layer transpiration flux w.r.t. canopy air temperature', 'm s-1 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%mLayerdTrans_dTCanopy) = var_info('mLayerdTrans_dTCanopy' , 'derivatives in the soil layer transpiration flux w.r.t. canopy temperature', 'm s-1 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%mLayerdTrans_dTGround) = var_info('mLayerdTrans_dTGround' , 'derivatives in the soil layer transpiration flux w.r.t. ground temperature', 'm s-1 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%mLayerdTrans_dCanWat) = var_info('mLayerdTrans_dCanWat' , 'derivatives in the soil layer transpiration flux w.r.t. canopy total water', 'm-1 s-1 kg-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + ! derivatives in aquifer transpiration w.r.t. canopy state variables + deriv_meta(iLookDERIV%dAquiferTrans_dTCanair) = var_info('dAquiferTrans_dTCanair' , 'derivative in the aquifer transpiration flux w.r.t. canopy air temperature', 'm s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dAquiferTrans_dTCanopy) = var_info('dAquiferTrans_dTCanopy' , 'derivative in the aquifer transpiration flux w.r.t. canopy temperature', 'm s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dAquiferTrans_dTGround) = var_info('dAquiferTrans_dTGround' , 'derivative in the aquifer transpiration flux w.r.t. ground temperature', 'm s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dAquiferTrans_dCanWat) = var_info('dAquiferTrans_dCanWat' , 'derivative in the aquifer transpiration flux w.r.t. canopy total water', 'm-1 s-1 kg-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature + deriv_meta(iLookDERIV%dFracLiqSnow_dTk) = var_info('dFracLiqSnow_dTk' , 'derivative in fraction of liquid snow w.r.t. temperature' , 'K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%mLayerdTheta_dTk) = var_info('mLayerdTheta_dTk' , 'derivative of volumetric liquid water content w.r.t. temperature' , 'K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%mLayerd2Theta_dTk2) = var_info('mLayerd2Theta_dTk2' , 'second derivative of volumetric liquid water content w.r.t. temperature', 'K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + ! derivate in bulk heat capacity w.r.t. relevant state variables + deriv_meta(iLookDERIV%dVolHtCapBulk_dPsi0) = var_info('dVolHtCapBulk_dPsi0' , 'derivative in bulk heat capacity w.r.t. matric potential' , 'J m-4 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dTheta) = var_info('dVolHtCapBulk_dTheta' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dCanWat) = var_info('dVolHtCapBulk_dCanWat' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dTk) = var_info('dVolHtCapBulk_dTk' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dTkCanopy) = var_info('dVolHtCapBulk_dTkCanopy' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! derivatives in time + deriv_meta(iLookDERIV%mLayerdTemp_dt) = var_info('mLayerdTemp_dt' , 'timestep change in layer temperature' , 'K' ,get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%scalarCanopydTemp_dt) = var_info('scalarCanopydTemp_dt' , 'timestep change in canopy temperature' , 'K' ,get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + + ! ----- + ! * basin-wide runoff and aquifer fluxes... + ! ----------------------------------------- + bvar_meta(iLookBVAR%basin__TotalArea) = var_info('basin__TotalArea' , 'total basin area' , 'm2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + bvar_meta(iLookBVAR%basin__SurfaceRunoff) = var_info('basin__SurfaceRunoff' , 'surface runoff' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + bvar_meta(iLookBVAR%basin__ColumnOutflow) = var_info('basin__ColumnOutflow' , 'outflow from all "outlet" HRUs (with no downstream HRU)', 'm3 s-1', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + bvar_meta(iLookBVAR%basin__AquiferStorage) = var_info('basin__AquiferStorage' , 'aquifer storage' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + bvar_meta(iLookBVAR%basin__AquiferRecharge) = var_info('basin__AquiferRecharge' , 'recharge to the aquifer' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + bvar_meta(iLookBVAR%basin__AquiferBaseflow) = var_info('basin__AquiferBaseflow' , 'baseflow from the aquifer' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + bvar_meta(iLookBVAR%basin__AquiferTranspire) = var_info('basin__AquiferTranspire', 'transpiration loss from the aquifer' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + bvar_meta(iLookBVAR%basin__TotalRunoff) = var_info('basin__TotalRunoff' , 'total runoff to channel from all active components' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + bvar_meta(iLookBVAR%basin__SoilDrainage) = var_info('basin__SoilDrainage' , 'soil drainage' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + bvar_meta(iLookBVAR%routingRunoffFuture) = var_info('routingRunoffFuture' , 'runoff in future time steps' , 'm s-1' , get_ixVarType('routing'), iMissVec, iMissVec, .false.) + bvar_meta(iLookBVAR%routingFractionFuture) = var_info('routingFractionFuture' , 'fraction of runoff in future time steps' , '-' , get_ixVarType('routing'), iMissVec, iMissVec, .false.) + bvar_meta(iLookBVAR%averageInstantRunoff) = var_info('averageInstantRunoff' , 'instantaneous runoff' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + bvar_meta(iLookBVAR%averageRoutedRunoff) = var_info('averageRoutedRunoff' , 'routed runoff' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + + ! ----- + ! * lookup tables... + ! ------------------ + + ! temperature and enthalpy + lookup_meta(iLookLOOKUP%temperature) = var_info('temperature' , 'value of temperature in the lookup table' , 'K' , get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + lookup_meta(iLookLOOKUP%enthalpy) = var_info('enthalpy' , 'value of enthalpy in the lookup table' , 'J m-3' , get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + lookup_meta(iLookLOOKUP%deriv2) = var_info('deriv2' , 'second derivatives of the interpolating function' , 'mixed' , get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + + ! ----- + ! * model indices... + ! ------------------ + + ! number of model layers, and layer indices + indx_meta(iLookINDEX%nSnow) = var_info('nSnow' , 'number of snow layers' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%nSoil) = var_info('nSoil' , 'number of soil layers' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%nLayers) = var_info('nLayers' , 'total number of layers' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%layerType) = var_info('layerType' , 'index defining type of layer (snow or soil)' , '-', get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + ! number of state variables of different type + indx_meta(iLookINDEX%nCasNrg) = var_info('nCasNrg' , 'number of energy state variables for the canopy air space' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%nVegNrg) = var_info('nVegNrg' , 'number of energy state variables for the vegetation canopy' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%nVegMass) = var_info('nVegMass' , 'number of hydrology states for vegetation (mass of water)' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%nVegState) = var_info('nVegState' , 'number of vegetation state variables' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%nNrgState) = var_info('nNrgState' , 'number of energy state variables' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%nWatState) = var_info('nWatState' , 'number of "total water" states (vol. total water content)' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%nMatState) = var_info('nMatState' , 'number of matric head state variables' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%nMassState) = var_info('nMassState' , 'number of hydrology state variables (mass of water)' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%nState) = var_info('nState' , 'total number of model state variables' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! number of state variables within different domains in the snow+soil system + indx_meta(iLookINDEX%nSnowSoilNrg) = var_info('nSnowSoilNrg' , 'number of energy states in the snow+soil domain' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%nSnowOnlyNrg) = var_info('nSnowOnlyNrg' , 'number of energy states in the snow domain' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%nSoilOnlyNrg) = var_info('nSoilOnlyNrg' , 'number of energy states in the soil domain' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%nSnowSoilHyd) = var_info('nSnowSoilHyd' , 'number of hydrology states in the snow+soil domain' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%nSnowOnlyHyd) = var_info('nSnowOnlyHyd' , 'number of hydrology states in the snow domain' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%nSoilOnlyHyd) = var_info('nSoilOnlyHyd' , 'number of hydrology states in the soil domain' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! type of model state variables + indx_meta(iLookINDEX%ixControlVolume) = var_info('ixControlVolume' , 'index of the control volume for different domains (veg, snow, soil)' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixDomainType) = var_info('ixDomainType' , 'index of the type of domain (iname_veg, iname_snow, iname_soil)' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixStateType) = var_info('ixStateType' , 'index of the type of every state variable (iname_nrgCanair, ...)' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixHydType) = var_info('ixHydType' , 'index of the type of hydrology states in snow+soil domain' , '-', get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + ! type of model state variables (state subset) + indx_meta(iLookINDEX%ixDomainType_subset) = var_info('ixDomainType_subset' , '[state subset] id of domain for desired model state variables' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixStateType_subset) = var_info('ixStateType_subset' , '[state subset] type of desired model state variables' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + ! mapping between state subset and the full state vector + indx_meta(iLookINDEX%ixMapFull2Subset) = var_info('ixMapFull2Subset' , 'list of indices of the state subset in the full state vector' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixMapSubset2Full) = var_info('ixMapSubset2Full' , 'list of indices of the full state vector in the state subset' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + ! indices of model specific state variables + indx_meta(iLookINDEX%ixCasNrg) = var_info('ixCasNrg' , 'index of canopy air space energy state variable' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixVegNrg) = var_info('ixVegNrg' , 'index of canopy energy state variable' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixVegHyd) = var_info('ixVegHyd' , 'index of canopy hydrology state variable (mass)' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixTopNrg) = var_info('ixTopNrg' , 'index of upper-most energy state in the snow+soil subdomain' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixTopHyd) = var_info('ixTopHyd' , 'index of upper-most hydrology state in the snow+soil subdomain' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixAqWat) = var_info('ixAqWat' , 'index of storage of water in the aquifer' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! vectors of indices for specific state types + indx_meta(iLookINDEX%ixNrgOnly) = var_info('ixNrgOnly' , 'indices IN THE STATE SUBSET for energy states' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixHydOnly) = var_info('ixHydOnly' , 'indices IN THE STATE SUBSET for hydrology states in the snow+soil domain', '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixMatOnly) = var_info('ixMatOnly' , 'indices IN THE STATE SUBSET for matric head state variables' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixMassOnly) = var_info('ixMassOnly' , 'indices IN THE STATE SUBSET for hydrology states (mass of water)' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + ! vectors of indices for specific state types within specific sub-domains + indx_meta(iLookINDEX%ixSnowSoilNrg) = var_info('ixSnowSoilNrg' , 'indices IN THE STATE SUBSET for energy states in the snow+soil domain' , '-', get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixSnowOnlyNrg) = var_info('ixSnowOnlyNrg' , 'indices IN THE STATE SUBSET for energy states in the snow domain' , '-', get_ixVarType('midSnow'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixSoilOnlyNrg) = var_info('ixSoilOnlyNrg' , 'indices IN THE STATE SUBSET for energy states in the soil domain' , '-', get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixSnowSoilHyd) = var_info('ixSnowSoilHyd' , 'indices IN THE STATE SUBSET for hydrology states in the snow+soil domain', '-', get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixSnowOnlyHyd) = var_info('ixSnowOnlyHyd' , 'indices IN THE STATE SUBSET for hydrology states in the snow domain' , '-', get_ixVarType('midSnow'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixSoilOnlyHyd) = var_info('ixSoilOnlyHyd' , 'indices IN THE STATE SUBSET for hydrology states in the soil domain' , '-', get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + ! vectors of indices for specfic state types within specific sub-domains + indx_meta(iLookINDEX%ixNrgCanair) = var_info('ixNrgCanair' , 'indices IN THE FULL VECTOR for energy states in canopy air space domain' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixNrgCanopy) = var_info('ixNrgCanopy' , 'indices IN THE FULL VECTOR for energy states in the canopy domain' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixHydCanopy) = var_info('ixHydCanopy' , 'indices IN THE FULL VECTOR for hydrology states in the canopy domain' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixNrgLayer) = var_info('ixNrgLayer' , 'indices IN THE FULL VECTOR for energy states in the snow+soil domain' , '-', get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixHydLayer) = var_info('ixHydLayer' , 'indices IN THE FULL VECTOR for hydrology states in the snow+soil domain' , '-', get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixWatAquifer) = var_info('ixWatAquifer' , 'indices IN THE FULL VECTOR for storage of water in the aquifer' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! vectors of indices for specific state types IN SPECIFIC SUB-DOMAINS + indx_meta(iLookINDEX%ixVolFracWat) = var_info('ixVolFracWat' , 'indices IN THE SNOW+SOIL VECTOR for hyd states' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixMatricHead) = var_info('ixMatricHead' , 'indices IN THE SOIL VECTOR for hyd states' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + ! indices within state vectors + indx_meta(iLookINDEX%ixAllState) = var_info('ixAllState' , 'list of indices for all model state variables' , '-', get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixSoilState) = var_info('ixSoilState' , 'list of indices for all soil layers' , '-', get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixLayerState) = var_info('ixLayerState' , 'list of indices for all model layers' , '-', get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%ixLayerActive) = var_info('ixLayerActive' , 'list of indices for active model layers (inactive=integerMissing)' , '-', get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + ! number of trials + indx_meta(iLookINDEX%numberFluxCalc) = var_info('numberFluxCalc' , 'number of flux calculations' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%numberStateSplit) = var_info('numberStateSplit' , 'number of state splitting solutions' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%numberDomainSplitNrg) = var_info('numberDomainSplitNrg' , 'number of domain splitting solutions for energy' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%numberDomainSplitMass) = var_info('numberDomainSplitMass', 'number of domain splitting solutions for mass' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + indx_meta(iLookINDEX%numberScalarSolutions) = var_info('numberScalarSolutions', 'number of scalar solutions' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + + ! read file to define model output (modifies metadata structures) + call read_output_file(err,cmessage) + if (err.ne.0) message=trim(message)//trim(cmessage) + +end subroutine popMetadat + +! ------------------------------------------------ +! subroutine to populate write commands from file input +! ------------------------------------------------ +subroutine read_output_file(err,message) + USE netcdf + ! to get name of output control file from user + USE summaFileManager,only:SETTINGS_PATH ! path for metadata files + USE summaFileManager,only:OUTPUT_CONTROL ! file with output controls + + ! some dimensional parameters + USE globalData, only:outFreq ! output frequencies + USE var_lookup, only:maxvarFreq ! maximum # of output files + USE var_lookup, only:maxvarStat ! maximum # of statistics + + ! metadata structures + USE globalData, only: time_meta ! data structure for time metadata + USE globalData, only: forc_meta ! data structure for forcing metadata + USE globalData, only: type_meta ! data structure for categorical metadata + USE globalData, only: attr_meta ! data structure for attribute metadata + USE globalData, only: mpar_meta ! data structure for local parameter metadata + USE globalData, only: bpar_meta ! data structure for basin parameter metadata + USE globalData, only: bvar_meta ! data structure for basin model variable metadata + USE globalData, only: indx_meta ! data structure for index metadata + USE globalData, only: prog_meta ! data structure for local prognostic (state) variables + USE globalData, only: diag_meta ! data structure for local diagnostic variables + USE globalData, only: flux_meta ! data structure for local flux variables + USE globalData, only: deriv_meta ! data structure for local flux derivatives + USE globalData, only: outputPrecision ! data structure for output precision + USE globalData, only: outputCompressionLevel ! data structure for output netcdf deflate level + + ! structures of named variables + USE var_lookup, only: iLookTYPE ! named variables for categorical data + USE var_lookup, only: iLookID ! named variables for hru and gru ID metadata + USE var_lookup, only: iLookFORCE ! named variables for forcing data structure + USE var_lookup, only: iLookINDEX ! named variables for index variable data structure + USE var_lookup, only: iLookSTAT ! named variables for statitics variable data structure + USE var_lookup, only: iLookFREQ ! named variables for model output frequencies + + ! identify indices within structures + USE get_ixName_module,only:get_ixUnknown ! identify index in any structure + USE get_ixname_module,only:get_ixFreq ! identify index of model output frequency + USE get_ixname_module,only:get_ixStat ! identify index in ststistics structure + USE get_ixname_module,only:get_statName ! identify statistics name from the index + + ! modules to read ASCII data + USE ascii_util_module,only:file_open ! open file + USE ascii_util_module,only:linewidth ! match character number for one line + USE ascii_util_module,only:get_vlines ! get a vector of non-comment lines + USE ascii_util_module,only:split_line ! split a line into words + implicit none + + ! dummy variables + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + + ! define file format + integer(i4b),parameter :: noStatsDesired=1001 ! no statistic desired (temporally constant variables) + integer(i4b),parameter :: provideStatName=1002 ! provide the name of the desired statistic + integer(i4b),parameter :: provideStatFlags=1003 ! provide flags defining the desired statistic + integer(i4b) :: fileFormat ! the file format + + ! define statistics flags + logical(lgt),dimension(maxvarStat) :: statFlag ! vector of statistics flags + character(len=32) :: statName ! name of desired statistic + integer(i4b) :: iStat ! index of statistics vector + + ! define frequency of model output + character(len=64) :: freqName ! name of desired output frequency + integer(i4b) :: iFreq ! index of frequency vector + + ! general local variables + character(LEN=256) :: cmessage ! error message of downwind routine + character(LEN=256) :: outfile ! full path of model output file + integer(i4b) :: unt ! file unit + character(LEN=linewidth),allocatable :: charlines(:) ! vector of character strings + character(LEN=64),allocatable :: lineWords(:) ! vector to parse textline + integer(i4b) :: nWords ! number of words in line + character(LEN=128) :: varName ! variable name + character(LEN=5) :: structName ! name of structure + integer(i4b) :: vLine ! index for loop through variables + integer(i4b) :: vDex ! index into type lists + + ! initialize error control + err=0; message='read_output_file/' + + ! ********************************************************************************************** + ! (1) open file and read variable data + ! ********************************************************************************************** + outfile = trim(SETTINGS_PATH)//trim(OUTPUT_CONTROL) ! build filename + call file_open(trim(outfile),unt,err,cmessage) ! open file if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - nWords = size(lineWords) - - ! define variable name - varName = trim(lineWords(nameIndex)) - - ! user cannot control time output - if (trim(varName)=='time') cycle - ! set precision if it is given - if (trim(varName)=='outputPrecision') then - statName = trim(lineWords(nWords)) - if (statName=='single' .or. statName=='float') then - outputPrecision = nf90_float - else if (statName=='double') then - outputPrecision = nf90_double - else - err=20 - cmessage='outputPrecision must be single, float, or double' - message=trim(message)//trim(cmessage)//trim(varName); - return - end if - cycle - end if - - ! set output netcdf file compression level if given. default is level 4. - if (trim(varName)=='outputCompressionLevel') then - statName = trim(lineWords(nWords)) - read(statName, *) outputCompressionLevel - if ((outputCompressionLevel .LT. 0) .or. (outputCompressionLevel .GT. 9)) then - err=20 - cmessage='outputCompressionLevel must be between 0 and 9.' - message=trim(message)//trim(cmessage)//trim(varName); - return - end if - cycle - end if - ! --- variables with multiple statistics options -------------------------- + ! ********************************************************************************************** + ! (2) read variable data (continue reading from previous point in the file) + ! ********************************************************************************************** + ! read the rest of the lines + call get_vlines(unt,charLines,err,cmessage) ! get a list of character strings from non-comment lines + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + close(unt) ! close the file + + ! ********************************************************************************************** + ! (3) loop to parse individual file lines + ! ********************************************************************************************** + + ! initialize output frequency + outFreq(:) = .false. + + ! loop through the lines in the file + do vLine = 1,size(charLines) + + ! parse the current line + call split_line(charLines(vLine),lineWords,err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + nWords = size(lineWords) + + ! define variable name + varName = trim(lineWords(nameIndex)) + + ! user cannot control time output + if (trim(varName)=='time') cycle + ! set precision if it is given + if (trim(varName)=='outputPrecision') then + statName = trim(lineWords(nWords)) + if (statName=='single' .or. statName=='float') then + outputPrecision = nf90_float + else if (statName=='double') then + outputPrecision = nf90_double + else + err=20 + cmessage='outputPrecision must be single, float, or double' + message=trim(message)//trim(cmessage)//trim(varName); + return + end if + cycle + end if - ! identify the data structure for the given variable (structName) and the variable index (vDex) - call get_ixUnknown(trim(varName),structName,vDex,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage)//trim(varName); return; end if; + ! set output netcdf file compression level if given. default is level 4. + if (trim(varName)=='outputCompressionLevel') then + statName = trim(lineWords(nWords)) + read(statName, *) outputCompressionLevel + if ((outputCompressionLevel .LT. 0) .or. (outputCompressionLevel .GT. 9)) then + err=20 + cmessage='outputCompressionLevel must be between 0 and 9.' + message=trim(message)//trim(cmessage)//trim(varName); + return + end if + cycle + end if - ! id variables should not be specified in output control file - if (trim(structName)=='id')then - print*,'id variable requested in outputControl, will be skipped: variable='//trim(varName) - cycle - end if + ! --- variables with multiple statistics options -------------------------- - ! --- identify the desired frequency in the metadata structure ----------- + ! identify the data structure for the given variable (structName) and the variable index (vDex) + call get_ixUnknown(trim(varName),structName,vDex,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage)//trim(varName); return; end if; - ! process time-varying variables - select case(trim(structName)) - case('indx','forc','prog','diag','flux','bvar','deriv') + ! id variables should not be specified in output control file + if (trim(structName)=='id')then + print*,'id variable requested in outputControl, will be skipped: variable='//trim(varName) + cycle + end if - ! * ensure that the frequency index exists for time varying variables - if(nWordsmaxvarFreq)then + message=trim(message)//'unable to identify desired output frequency for variable '//trim(varName)& + //' [entered "'//trim(freqName)//'"]' + err=20; return + endif + + ! temporally constant variables use timestep-level output (no aggregation) + case default + message=trim(message)//'unable to identify desired output frequency for variable '//trim(varName)& + //' [entered "'//trim(freqName)//'"];'& + //' outputting variable in timestep file' + iFreq = iLookFREQ%timestep + freqName = 'timestep' + end select - ! * define the frequency name - freqName = trim(lineWords(freqIndex)) + ! --- identify the desired statistic in the metadata structure ----------- + + ! * check the definition of statistics + ! there are three options to define the statistic: + ! option 0: file format = varName + ! option 0: file format = varName | outFreq + ! option 1: file format = varName | outFreq | statisticName + ! option 2: file format = varName | outFreq | inst | sum | mean | var | min | max | mode + select case(nWords) + case(nameIndex + 2, nameIndex); fileFormat=noStatsDesired ! no statistic desired (temporally constant variables) + case(freqIndex + 2 ); fileFormat=provideStatName ! provide the name of the desired statistic + case(freqIndex + 2*maxVarStat); fileFormat=provideStatFlags ! provide flags defining the desired statistic + case default + message=trim(message)//'unexpected format for variable '//trim(varName)& + //' (format = "'//trim(charLines(vLine))//'")' + err=20; return + end select - ! * identify index in vector - select case(freqName) - ! define cases where temporal aggregation is numeric - case('1'); iFreq = iLookFreq%timestep ! assume timestep-level output - case('24'); iFreq = iLookFreq%day ! assume daily output - ! define cases where temporal aggregation is defined using a text string - case default; iFreq = get_ixfreq(freqName) + ! * extract the statistic name + select case(fileFormat) + + ! no statistic desired (temporally constant variables) + case(noStatsDesired); statName = 'instant' ! use instantaneous values + + ! provide the name of the desired statistic + case(provideStatName); statName = trim(lineWords(freqIndex+2)) + + ! extract the statistic name from the flags + ! NOTE: cannot imagine why someone would want to do this now since the other option is easier + ! --> included for backwards compatibility + case(provideStatFlags) + ! get statistic name + statFlag(:) = .false. + do iStat = 1,maxVarStat + if (lineWords(freqIndex + 2*iStat)=='1') then + statFlag(iStat)=.true. + statName = get_statName(istat) + end if + end do + ! check actually defined the statistic (and only defined one statistic) + if(count(statFlag)/=1)then + message=trim(message)//'expect only one statistic is defined when using flags to define statistics'& + //': entered "'//trim(charLines(vLine))//'"' + err=20; return + endif + + ! check: should not get here since checked above + case default; err=20; message=trim(message)//'unexpected file format'; return end select - ! * check that we could find the index - if(iFreq<0 .or. iFreq>maxvarFreq)then - message=trim(message)//'unable to identify desired output frequency for variable '//trim(varName)& - //' [entered "'//trim(freqName)//'"]' - err=20; return + ! * get the statistics index + iStat = get_ixStat(trim(statName)) + if(iStat<0 .or. iStat>maxvarStat)then + message=trim(message)//'unable to identify desired statistic for variable '//trim(varName)& + //' [evaluating '//trim(statName)//']' + err=20; return endif - ! temporally constant variables use timestep-level output (no aggregation) - case default - message=trim(message)//'unable to identify desired output frequency for variable '//trim(varName)& - //' [entered "'//trim(freqName)//'"];'& - //' outputting variable in timestep file' - iFreq = iLookFREQ%timestep - freqName = 'timestep' - end select - - ! --- identify the desired statistic in the metadata structure ----------- - - ! * check the definition of statistics - ! there are three options to define the statistic: - ! option 0: file format = varName - ! option 0: file format = varName | outFreq - ! option 1: file format = varName | outFreq | statisticName - ! option 2: file format = varName | outFreq | inst | sum | mean | var | min | max | mode - select case(nWords) - case(nameIndex + 2, nameIndex); fileFormat=noStatsDesired ! no statistic desired (temporally constant variables) - case(freqIndex + 2 ); fileFormat=provideStatName ! provide the name of the desired statistic - case(freqIndex + 2*maxVarStat); fileFormat=provideStatFlags ! provide flags defining the desired statistic - case default - message=trim(message)//'unexpected format for variable '//trim(varName)& - //' (format = "'//trim(charLines(vLine))//'")' + ! --- populate the metadata that controls the model output --------------- + + ! identify data structure + select case (trim(structName)) + + ! temporally constant structures -- request instantaneous timestep-level output (no aggregation) + case('time' ); time_meta(vDex)%statIndex(iLookFREQ%timestep) = iLookSTAT%inst; time_meta(vDex)%varDesire=.true. ! timing data + case('bpar' ); bpar_meta(vDex)%statIndex(iLookFREQ%timestep) = iLookSTAT%inst; bpar_meta(vDex)%varDesire=.true. ! basin parameters + case('attr' ); attr_meta(vDex)%statIndex(iLookFREQ%timestep) = iLookSTAT%inst; attr_meta(vDex)%varDesire=.true. ! local attributes + case('type' ); type_meta(vDex)%statIndex(iLookFREQ%timestep) = iLookSTAT%inst; type_meta(vDex)%varDesire=.true. ! local classification + case('mpar' ); mpar_meta(vDex)%statIndex(iLookFREQ%timestep) = iLookSTAT%inst; mpar_meta(vDex)%varDesire=.true. ! model parameters + + ! index structures -- can only be output at the model time step + case('indx' ); indx_meta(vDex)%statIndex(iLookFREQ%timestep) = iLookSTAT%inst; indx_meta(vDex)%varDesire=.true. + if(iFreq/=iLookFREQ%timestep)then + message=trim(message)//'index variables can only be output at model timestep'& + //' [evaluating variable "'//trim(varName)//'" for output frequency "'//trim(freqName)//'"]' + err=20; return + endif + + ! temporally varying structures + case('forc' ); call popStat(forc_meta(vDex) , iFreq, iStat, err, cmessage) ! model forcing data + case('prog' ); call popStat(prog_meta(vDex) , iFreq, iStat, err, cmessage) ! model prognostics + case('diag' ); call popStat(diag_meta(vDex) , iFreq, iStat, err, cmessage) ! model diagnostics + case('flux' ); call popStat(flux_meta(vDex) , iFreq, iStat, err, cmessage) ! model fluxes + case('bvar' ); call popStat(bvar_meta(vDex) , iFreq, iStat, err, cmessage) ! basin variables + case('deriv'); call popStat(deriv_meta(vDex), iFreq, iStat, err, cmessage) ! model derivs + + ! error control + case default; err=20;message=trim(message)//'unable to identify lookup structure';return + + end select ! select data structure + + ! error control from popStat + if (err/=0) then; message=trim(message)//trim(cmessage);return; end if + + ! ensure that time is turned on + forc_meta(iLookForce%time)%statIndex(iLookFREQ%timestep) = iLookSTAT%inst + + ! set desired output frequency + outFreq(iFreq) = .true. + + end do ! loop through file lines with vline + + ! ********************************************************************************************** + ! (4) include time variable + ! ********************************************************************************************** + + ! force time to be written in every file + forc_meta(iLookFORCE%time)%varDesire = .true. + forc_meta(iLookFORCE%time)%statIndex(:) = iLookSTAT%inst + +end subroutine read_output_file + +! ******************************************************************************************** +! Subroutine popStat for populating the meta_data structures with information read in from file. +! This routine is called by read_output_file +! ******************************************************************************************** +subroutine popStat(meta, iFreq, iStat, err, message) + USE data_types,only:var_info ! meta_data type declaration + USE get_ixname_module,only:get_freqName ! get name of frequency from frequency index (error control) + implicit none + ! dummy variables + class(var_info),intent(inout) :: meta ! dummy meta_data structure + integer(i4b),intent(in) :: iFreq ! index in output frequency vector + integer(i4b),intent(in) :: iStat ! index in output statistics vector + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! initiate error handling + err=0; message='popStat/' + + ! check that the variable is not already defined for a given frequency + if(meta%statIndex(iFreq)/=integerMissing)then + message=trim(message)//'variable "'//trim(meta%varName)//'" is already defined '& + //'for output frequency "'//trim(get_freqName(iFreq))//'"' err=20; return - end select - - ! * extract the statistic name - select case(fileFormat) - - ! no statistic desired (temporally constant variables) - case(noStatsDesired); statName = 'instant' ! use instantaneous values - - ! provide the name of the desired statistic - case(provideStatName); statName = trim(lineWords(freqIndex+2)) - - ! extract the statistic name from the flags - ! NOTE: cannot imagine why someone would want to do this now since the other option is easier - ! --> included for backwards compatibility - case(provideStatFlags) - ! get statistic name - statFlag(:) = .false. - do iStat = 1,maxVarStat - if (lineWords(freqIndex + 2*iStat)=='1') then - statFlag(iStat)=.true. - statName = get_statName(istat) - end if - end do - ! check actually defined the statistic (and only defined one statistic) - if(count(statFlag)/=1)then - message=trim(message)//'expect only one statistic is defined when using flags to define statistics'& - //': entered "'//trim(charLines(vLine))//'"' - err=20; return - endif - - ! check: should not get here since checked above - case default; err=20; message=trim(message)//'unexpected file format'; return - end select - - ! * get the statistics index - iStat = get_ixStat(trim(statName)) - if(iStat<0 .or. iStat>maxvarStat)then - message=trim(message)//'unable to identify desired statistic for variable '//trim(varName)& - //' [evaluating '//trim(statName)//']' - err=20; return endif - ! --- populate the metadata that controls the model output --------------- + ! identify desired variabe + meta%varDesire = .true. - ! identify data structure - select case (trim(structName)) + ! populate structure + meta%statIndex(iFreq) = iStat - ! temporally constant structures -- request instantaneous timestep-level output (no aggregation) - case('time' ); time_meta(vDex)%statIndex(iLookFREQ%timestep) = iLookSTAT%inst; time_meta(vDex)%varDesire=.true. ! timing data - case('bpar' ); bpar_meta(vDex)%statIndex(iLookFREQ%timestep) = iLookSTAT%inst; bpar_meta(vDex)%varDesire=.true. ! basin parameters - case('attr' ); attr_meta(vDex)%statIndex(iLookFREQ%timestep) = iLookSTAT%inst; attr_meta(vDex)%varDesire=.true. ! local attributes - case('type' ); type_meta(vDex)%statIndex(iLookFREQ%timestep) = iLookSTAT%inst; type_meta(vDex)%varDesire=.true. ! local classification - case('mpar' ); mpar_meta(vDex)%statIndex(iLookFREQ%timestep) = iLookSTAT%inst; mpar_meta(vDex)%varDesire=.true. ! model parameters - - ! index structures -- can only be output at the model time step - case('indx' ); indx_meta(vDex)%statIndex(iLookFREQ%timestep) = iLookSTAT%inst; indx_meta(vDex)%varDesire=.true. - if(iFreq/=iLookFREQ%timestep)then - message=trim(message)//'index variables can only be output at model timestep'& - //' [evaluating variable "'//trim(varName)//'" for output frequency "'//trim(freqName)//'"]' - err=20; return - endif - - ! temporally varying structures - case('forc' ); call popStat(forc_meta(vDex) , iFreq, iStat, err, cmessage) ! model forcing data - case('prog' ); call popStat(prog_meta(vDex) , iFreq, iStat, err, cmessage) ! model prognostics - case('diag' ); call popStat(diag_meta(vDex) , iFreq, iStat, err, cmessage) ! model diagnostics - case('flux' ); call popStat(flux_meta(vDex) , iFreq, iStat, err, cmessage) ! model fluxes - case('bvar' ); call popStat(bvar_meta(vDex) , iFreq, iStat, err, cmessage) ! basin variables - case('deriv'); call popStat(deriv_meta(vDex), iFreq, iStat, err, cmessage) ! model derivs - - ! error control - case default; err=20;message=trim(message)//'unable to identify lookup structure';return - - end select ! select data structure - - ! error control from popStat - if (err/=0) then; message=trim(message)//trim(cmessage);return; end if - - ! ensure that time is turned on - forc_meta(iLookForce%time)%statIndex(iLookFREQ%timestep) = iLookSTAT%inst - - ! set desired output frequency - outFreq(iFreq) = .true. - - ! print output (debugging) - !write(*,'(a)') 'freqName = '//trim(freqName)//'; statName = '//trim(statName)//'; charLines(vLine) = '//trim(charLines(vLine)) - - end do ! loop through file lines with vline - - ! ********************************************************************************************** - ! (4) include time variable - ! ********************************************************************************************** - - ! force time to be written in every file - forc_meta(iLookFORCE%time)%varDesire = .true. - forc_meta(iLookFORCE%time)%statIndex(:) = iLookSTAT%inst - - end subroutine read_output_file - - ! ******************************************************************************************** - ! Subroutine popStat for populating the meta_data structures with information read in from file. - ! This routine is called by read_output_file - ! ******************************************************************************************** - subroutine popStat(meta, iFreq, iStat, err, message) - USE data_types,only:var_info ! meta_data type declaration - USE get_ixname_module,only:get_freqName ! get name of frequency from frequency index (error control) - implicit none - ! dummy variables - class(var_info),intent(inout) :: meta ! dummy meta_data structure - integer(i4b),intent(in) :: iFreq ! index in output frequency vector - integer(i4b),intent(in) :: iStat ! index in output statistics vector - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! initiate error handling - err=0; message='popStat/' - - ! check that the variable is not already defined for a given frequency - if(meta%statIndex(iFreq)/=integerMissing)then - message=trim(message)//'variable "'//trim(meta%varName)//'" is already defined '& - //'for output frequency "'//trim(get_freqName(iFreq))//'"' - err=20; return - endif - - ! identify desired variabe - meta%varDesire = .true. - - ! populate structure - meta%statIndex(iFreq) = iStat - - end subroutine popStat +end subroutine popStat end module popMetadat_module From aa263b5754f928958d2d62899519d4c4dbdec7d4 Mon Sep 17 00:00:00 2001 From: Kyle Date: Wed, 28 Sep 2022 17:45:36 +0000 Subject: [PATCH 0382/1472] updated indentation --- build/source/engine/t2enthalpy.f90 | 1375 ++++++++++++++-------------- 1 file changed, 676 insertions(+), 699 deletions(-) diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index f16b6f777..2a5030389 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -69,734 +69,711 @@ module t2enthalpy_module contains - ! ************************************************************************************************************************ - ! public subroutine T2E_lookup: define a look-up table to compute enthalpy based on temperature - ! ************************************************************************************************************************ - subroutine T2E_lookup(nSoil, & ! intent(in): number of soil layers - mpar_data, & ! intent(in): parameter data structure - lookup_data, & ! intent(inout): lookup table data structure - err,message) - USE nr_utility_module,only:arth ! use to build vectors with regular increments - USE spline_int_module,only:spline,splint ! use for cubic spline interpolation - USE soil_utils_module,only:volFracLiq ! use to compute the volumetric fraction of liquid water - implicit none - ! declare dummy variables - integer(i4b),intent(in) :: nSoil - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(zLookup),intent(inout) :: lookup_data ! lookup tables - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! declare local variables - character(len=128) :: cmessage ! error message in downwind routine - logical(lgt),parameter :: doTest=.false. ! flag to test - integer(i4b),parameter :: nLook=100 ! number of elements in the lookup table - integer(i4b),parameter :: nIntegr8=10000 ! number of points used in the numerical integration - real(rkind),parameter :: T_lower=260.0_rkind ! lowest temperature value where all liquid water is assumed frozen (K) - real(rkind),dimension(nLook) :: xTemp ! temporary vector - real(rkind) :: xIncr ! temporary increment - real(rkind) :: T_incr ! temperature increment - real(rkind),parameter :: T_test=272.9742_rkind ! test value for temperature (K) - real(rkind) :: E_test ! test value for enthalpy (J m-3) - integer(i4b) :: iVar ! loop through variables - integer(i4b) :: iSoil ! loop through soil layers - integer(i4b) :: iLook ! loop through lookup table - integer(i4b) :: jIntegr8 ! index for numerical integration - logical(lgt) :: check ! flag to check allocation - real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) - real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) - real(rkind) :: vFracIce ! volumetric fraction of ice (-) - real(rkind) :: matricHead ! matric head (m) - ! initialize error control - err=0; message="T2E_lookup/" - - ! get the values of temperature for the lookup table - xIncr = 1._rkind/real(nLook-1, kind(rkind)) - xTemp = T_lower + (Tfreeze - T_lower)*arth(0._rkind,xIncr,nLook)**0.25_rkind ! use **0.25 to give more values near freezing - - ! ----- - ! * allocate space for the lookup table... - ! ---------------------------------------- - - ! initialize checks - check=.false. - - ! allocate space for soil layers - if(allocated(lookup_data%z))then; check=.true.; else; allocate(lookup_data%z(nSoil), stat=err); endif - if(check) then; err=20; message=trim(message)//'lookup table z dimension was unexpectedly allocated already'; return; end if - if(err/=0)then; err=20; message=trim(message)//'problem allocating lookup table z dimension dimension'; return; end if - - ! allocate space for the variables in the lookup table - do iSoil=1,nSoil - if(allocated(lookup_data%z(iSoil)%var))then; check=.true.; else; allocate(lookup_data%z(iSoil)%var(maxvarLookup), stat=err); endif - if(check) then; err=20; message=trim(message)//'lookup table var dimension was unexpectedly allocated already'; return; end if - if(err/=0)then; err=20; message=trim(message)//'problem allocating lookup table var dimension dimension'; return; end if - - ! allocate space for the values in the lookup table - do iVar=1,maxvarLookup - if(allocated(lookup_data%z(iSoil)%var(iVar)%lookup))then; check=.true.; else; allocate(lookup_data%z(iSoil)%var(iVar)%lookup(nLook), stat=err); endif - if(check) then; err=20; message=trim(message)//'lookup table value dimension was unexpectedly allocated already'; return; end if - if(err/=0)then; err=20; message=trim(message)//'problem allocating lookup table vaule dimension dimension'; return; end if - - end do ! (looping through variables) - end do ! (looping through soil layers) - - ! loop through soil layers - do iSoil=1,nSoil +! ************************************************************************************************************************ +! public subroutine T2E_lookup: define a look-up table to compute enthalpy based on temperature +! ************************************************************************************************************************ +subroutine T2E_lookup(nSoil, & ! intent(in): number of soil layers + mpar_data, & ! intent(in): parameter data structure + lookup_data, & ! intent(inout): lookup table data structure + err,message) + USE nr_utility_module,only:arth ! use to build vectors with regular increments + USE spline_int_module,only:spline,splint ! use for cubic spline interpolation + USE soil_utils_module,only:volFracLiq ! use to compute the volumetric fraction of liquid water + implicit none + ! declare dummy variables + integer(i4b),intent(in) :: nSoil + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(zLookup),intent(inout) :: lookup_data ! lookup tables + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! declare local variables + character(len=128) :: cmessage ! error message in downwind routine + logical(lgt),parameter :: doTest=.false. ! flag to test + integer(i4b),parameter :: nLook=100 ! number of elements in the lookup table + integer(i4b),parameter :: nIntegr8=10000 ! number of points used in the numerical integration + real(rkind),parameter :: T_lower=260.0_rkind ! lowest temperature value where all liquid water is assumed frozen (K) + real(rkind),dimension(nLook) :: xTemp ! temporary vector + real(rkind) :: xIncr ! temporary increment + real(rkind) :: T_incr ! temperature increment + real(rkind),parameter :: T_test=272.9742_rkind ! test value for temperature (K) + real(rkind) :: E_test ! test value for enthalpy (J m-3) + integer(i4b) :: iVar ! loop through variables + integer(i4b) :: iSoil ! loop through soil layers + integer(i4b) :: iLook ! loop through lookup table + integer(i4b) :: jIntegr8 ! index for numerical integration + logical(lgt) :: check ! flag to check allocation + real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) + real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) + real(rkind) :: vFracIce ! volumetric fraction of ice (-) + real(rkind) :: matricHead ! matric head (m) + ! initialize error control + err=0; message="T2E_lookup/" + + ! get the values of temperature for the lookup table + xIncr = 1._rkind/real(nLook-1, kind(rkind)) + xTemp = T_lower + (Tfreeze - T_lower)*arth(0._rkind,xIncr,nLook)**0.25_rkind ! use **0.25 to give more values near freezing ! ----- - ! * make association to variables in the data structures... - ! --------------------------------------------------------- + ! * allocate space for the lookup table... + ! ---------------------------------------- - associate(& + ! initialize checks + check=.false. - ! associate model parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) , & ! scaling parameter for freezing (K-1) - soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(iSoil) , & ! intrinsic soil density (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(iSoil) , & ! soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(iSoil) , & ! volumetric residual water content (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(iSoil) , & ! van Genuchten "alpha" parameter (m-1) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(iSoil) , & ! van Genuchten "n" parameter (-) + ! allocate space for soil layers + if(allocated(lookup_data%z))then; check=.true.; else; allocate(lookup_data%z(nSoil), stat=err); endif + if(check) then; err=20; message=trim(message)//'lookup table z dimension was unexpectedly allocated already'; return; end if + if(err/=0)then; err=20; message=trim(message)//'problem allocating lookup table z dimension dimension'; return; end if - ! associate values in the lookup table - Tk => lookup_data%z(iSoil)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ey => lookup_data%z(iSoil)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) - E2 => lookup_data%z(iSoil)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + ! allocate space for the variables in the lookup table + do iSoil=1,nSoil + if(allocated(lookup_data%z(iSoil)%var))then; check=.true.; else; allocate(lookup_data%z(iSoil)%var(maxvarLookup), stat=err); endif + if(check) then; err=20; message=trim(message)//'lookup table var dimension was unexpectedly allocated already'; return; end if + if(err/=0)then; err=20; message=trim(message)//'problem allocating lookup table var dimension dimension'; return; end if - ) ! end associate statement + ! allocate space for the values in the lookup table + do iVar=1,maxvarLookup + if(allocated(lookup_data%z(iSoil)%var(iVar)%lookup))then; check=.true.; else; allocate(lookup_data%z(iSoil)%var(iVar)%lookup(nLook), stat=err); endif + if(check) then; err=20; message=trim(message)//'lookup table value dimension was unexpectedly allocated already'; return; end if + if(err/=0)then; err=20; message=trim(message)//'problem allocating lookup table vaule dimension dimension'; return; end if - ! compute vGn_m - vGn_m = 1._rkind - 1._rkind/vGn_n + end do ! (looping through variables) + end do ! (looping through soil layers) - ! ----- - ! * populate the lookup table... - ! ------------------------------ - - ! initialize temperature and enthalpy - Tk(nLook) = Tfreeze - Ey(nLook) = 0._rkind - - ! loop through lookup table - do iLook=(nLook-1),1,-1 - - ! update temperature and enthalpy - Tk(iLook) = Tk(iLook+1) - Ey(iLook) = Ey(iLook+1) - - ! get the temperature increment for the numerical integration - T_incr = (xTemp(iLook)-xTemp(iLook+1))/real(nIntegr8, kind(rkind)) - - ! numerical integration between different values of the lookup table - do jIntegr8=1,nIntegr8 - - ! update temperature - Tk(iLook) = Tk(iLook) + T_incr - - ! compute the volumetric liquid water and ice content - ! NOTE: assume saturation - matricHead = (LH_fus/gravity)*(Tk(iLook) - Tfreeze)/Tfreeze - vFracLiq = volFracLiq(matricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - vFracIce = theta_sat - vFracLiq - - ! compute enthalpy - ! NOTE: assume intrrinsic density of ice is the intrinsic density of water - ! NOTE: kg m-3 J kg-1 K-1 K - Ey(iLook) = Ey(iLook) + iden_water*Cp_water*vFracLiq*T_incr + iden_water*Cp_ice*vFracIce*T_incr - - end do ! numerical integration - - end do ! loop through lookup table - - ! use cubic spline interpolation to obtain enthalpy values at the desired values of temperature - call spline(Tk,Ey,1.e30_rkind,1.e30_rkind,E2,err,cmessage) ! get the second derivatives - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! check - !print*, 'Tk = ', Tk - !print*, 'Ey = ', Ey - !print*, 'E2 = ', E2 - - ! test - if(doTest)then - - ! calculate enthalpy - call splint(Tk,Ey,E2,T_test,E_test,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! write values - print*, 'doTest = ', doTest - print*, 'T_test = ', T_test ! temperature (K) - print*, 'E_test = ', E_test ! enthalpy (J m-3) - print*, 'theta_sat = ', theta_sat ! soil porosity (-) - print*, 'theta_res = ', theta_res ! volumetric residual water content (-) - print*, 'vGn_alpha = ', vGn_alpha ! van Genuchten "alpha" parameter (m-1) - print*, 'vGn_n = ', vGn_n ! van Genuchten "n" parameter (-) - print*, trim(message)//'PAUSE: Set doTest=.false. to complete simulations' - read(*,*) - - endif ! if testing - - ! end asssociation to variables in the data structures - end associate - - end do ! (looping through soil layers) - end subroutine T2E_lookup - - - ! ************************************************************************************************************************ - ! public subroutine t2enthalpy: compute enthalpy from temperature and total water content - ! ************************************************************************************************************************ - subroutine t2enthalpy(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in) - ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy, & ! intent(out): enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,message) ! intent(out): error control - ! ------------------------------------------------------------------------------------------------------------------------- - ! downwind routines - USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water - USE spline_int_module,only:splint ! use for cubic spline interpolation - implicit none - ! delare dummy variables - ! ------------------------------------------------------------------------------------------------------------------------- - ! input: data structures - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model indices - type(zLookup),intent(in) :: lookup_data ! lookup tables - ! input: state variables for the vegetation canopy - real(rkind),intent(in) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) - real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric fraction of Ice (-) - ! output: enthalpy - real(rkind),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - real(rkind),intent(out) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ------------------------------------------------------------------------------------------------------------------------- - ! declare local variables - character(len=128) :: cmessage ! error message in downwind routine - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixDomainType ! name of a given model domain - integer(i4b) :: ixControlIndex ! index within a given model domain - real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - real(rkind) :: psiLiq ! matric head of liquid water (m) - real(rkind) :: vFracWat ! volumetric fraction of total water, liquid+ice (-) - real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) - real(rkind) :: vFracIce ! volumetric fraction of ice (-) - real(rkind) :: enthTemp ! enthalpy at the temperature of the control volume (J m-3) - real(rkind) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) - real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) - real(rkind) :: enthWater ! enthalpy of total water (J m-3) - real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) - real(rkind) :: enthMix ! enthalpy of the mixed region, liquid+ice (J m-3) - real(rkind) :: enthLiq ! enthalpy of the liquid region (J m-3) - real(rkind) :: enthAir ! enthalpy of air (J m-3) - real(rkind) :: diffT - real(rkind) :: integral - real(rkind) :: enthIce - real(rkind) :: enthVeg - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - generalVars: associate(& - ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers - ! mapping between the full state vector and the state subset - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset - ! type of domain, type of state variable, and index of control volume within domain - ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) ! end associate statement - ! -------------------------------------------------------------------------------------------------------------------------------- - - ! initialize error control - err=0; message="t2enthalpy/" - - ! loop through model state variables - do iState=1,size(ixMapSubset2Full) - - ! ----- - ! - compute indices... - ! -------------------- - - ! get domain type, and index of the control volume within the domain - ixFullVector = ixMapSubset2Full(iState) ! index within full state vector - ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) - ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain - - ! check an energy state - if(ixStateType(ixFullVector)==iname_nrgCanair .or. ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer)then - - ! get the layer index - select case(ixDomainType) - case(iname_cas); iLayer = integerMissing - case(iname_veg); iLayer = integerMissing - case(iname_snow); iLayer = ixControlIndex - case(iname_soil); iLayer = ixControlIndex + nSnow - case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) - case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return - end select - - ! identify domain - select case(ixDomainType) - - ! ----- - ! - canopy air space... - ! --------------------- - case(iname_cas) - scalarCanairEnthalpy = Cp_air * iden_air * (scalarCanairTempTrial - Tfreeze) - ! ----- - ! - vegetation canopy... - ! ----------------------- - case(iname_veg) - ! association to necessary variables for vegetation - vegVars: associate(& - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): [dp] canopy depth (m) - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) - - diffT = scalarCanopyTempTrial - Tfreeze - enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth - if(diffT>=0._rkind)then - enthLiq = Cp_water * scalarCanopyWatTrial * diffT / canopyDepth - enthIce = 0._rkind - enthPhase = 0._rkind - else - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth - enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth - enthPhase = LH_fus * scalarCanopyIceTrial / canopyDepth - end if - - scalarCanopyEnthalpy = enthVeg + enthLiq + enthIce - enthPhase - - ! end association - end associate vegVars - - ! ----- - ! - snow... - ! --------- - case(iname_snow) - - ! association to necessary variables for snow - snowVars: associate(& - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) - - diffT = mLayerTempTrial(iLayer) - Tfreeze - if(diffT>=0._rkind)then - enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * diffT - enthIce = 0._rkind - enthAir = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) ) * diffT - enthPhase = 0._rkind - else - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral - enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) - enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) - enthPhase = iden_ice * LH_fus * mLayerVolFracIceTrial(iLayer) - end if - - mLayerEnthalpy(iLayer) = enthLiq + enthIce + enthAir - enthPhase - - ! end association - end associate snowVars + ! loop through soil layers + do iSoil=1,nSoil ! ----- - ! - soil... - ! --------- - case(iname_soil) - - ! make association to variables in the data structures... - soilVars: associate(& - - ! associate model parameters - soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex) , & ! intrinsic soil density (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex) , & ! soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex) , & ! volumetric residual water content (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) - - ! associate values in the lookup table - Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) - E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function - - ) ! end associate statement - - ! diagnostic variables - vGn_m = 1._rkind - 1._rkind/vGn_n - Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) - vFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - - - ! *** compute enthalpy of water for unfrozen conditions - if(mlayerTempTrial(iLayer) > Tcrit)then - enthWater = iden_water*Cp_water*vFracWat*(mlayerTempTrial(iLayer) - Tfreeze) ! valid for temperatures below freezing also - enthPhase = 0._rkind - - ! *** compute enthalpy of water for frozen conditions - else - ! calculate enthalpy at the temperature (cubic spline interpolation) - call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),enthTemp,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + ! * make association to variables in the data structures... + ! --------------------------------------------------------- - ! calculate enthalpy at the critical temperature (cubic spline interpolation) - call splint(Tk,Ey,E2,Tcrit,enthTcrit,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + associate(& - ! calculate the enthalpy of water - enthMix = enthTemp - enthTcrit ! enthalpy of the liquid+ice mix - enthLiq = iden_water*Cp_water*vFracWat*(Tcrit - Tfreeze) - enthWater = enthMix + enthLiq + ! associate model parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) , & ! scaling parameter for freezing (K-1) + soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(iSoil) , & ! intrinsic soil density (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(iSoil) , & ! soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(iSoil) , & ! volumetric residual water content (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(iSoil) , & ! van Genuchten "alpha" parameter (m-1) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(iSoil) , & ! van Genuchten "n" parameter (-) - ! *** compute the enthalpy associated with phase change - psiLiq = (mLayerTempTrial(iLayer) - Tfreeze)*LH_fus/(gravity*Tfreeze) - vFracLiq = volFracLiq(psiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - vFracIce = vFracWat - vFracLiq - enthPhase = iden_water*LH_fus*vFracIce + ! associate values in the lookup table + Tk => lookup_data%z(iSoil)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) + Ey => lookup_data%z(iSoil)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) + E2 => lookup_data%z(iSoil)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function - endif ! (if frozen conditions) + ) ! end associate statement - ! *** compute the enthalpy of soil - enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*(mlayerTempTrial(iLayer) - Tfreeze) + ! compute vGn_m + vGn_m = 1._rkind - 1._rkind/vGn_n - ! *** compute the enthalpy of air - enthAir = iden_air*Cp_air*(1._rkind - theta_sat - vFracWat)*(mlayerTempTrial(iLayer) - Tfreeze) + ! ----- + ! * populate the lookup table... + ! ------------------------------ - ! *** compute the total enthalpy (J m-3) - mLayerEnthalpy(iLayer) = enthSoil + enthWater + enthAir - enthPhase + ! initialize temperature and enthalpy + Tk(nLook) = Tfreeze + Ey(nLook) = 0._rkind -! print*, iLayer, enthSoil, enthWater, enthAir, enthPhase + ! loop through lookup table + do iLook=(nLook-1),1,-1 - end associate soilVars + ! update temperature and enthalpy + Tk(iLook) = Tk(iLook+1) + Ey(iLook) = Ey(iLook+1) - ! ----- - ! - checks... - ! ----------- - case(iname_aquifer); cycle ! aquifer: do nothing - case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return - end select + ! get the temperature increment for the numerical integration + T_incr = (xTemp(iLook)-xTemp(iLook+1))/real(nIntegr8, kind(rkind)) - end if ! if an energy layer - end do ! looping through state variables + ! numerical integration between different values of the lookup table + do jIntegr8=1,nIntegr8 - end associate generalVars + ! update temperature + Tk(iLook) = Tk(iLook) + T_incr - end subroutine t2enthalpy - + ! compute the volumetric liquid water and ice content + ! NOTE: assume saturation + matricHead = (LH_fus/gravity)*(Tk(iLook) - Tfreeze)/Tfreeze + vFracLiq = volFracLiq(matricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + vFracIce = theta_sat - vFracLiq - ! ************************************************************************************************************************ - ! public subroutine t2enthalpy_T: compute enthalpy from temperature and total water content - ! ************************************************************************************************************************ - subroutine t2enthalpy_T(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in) - ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy, & ! intent(out): enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,message) ! intent(out): error control - ! ------------------------------------------------------------------------------------------------------------------------- - ! downwind routines - USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water - USE spline_int_module,only:splint ! use for cubic spline interpolation - implicit none - ! delare dummy variables - ! ------------------------------------------------------------------------------------------------------------------------- - ! input: data structures - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model indices - type(zLookup),intent(in) :: lookup_data ! lookup tables - ! input: state variables for the vegetation canopy - real(rkind),intent(in) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) - real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric fraction of Ice (-) - ! output: enthalpy - real(rkind),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - real(rkind),intent(out) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ------------------------------------------------------------------------------------------------------------------------- - ! declare local variables - character(len=128) :: cmessage ! error message in downwind routine - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixDomainType ! name of a given model domain - integer(i4b) :: ixControlIndex ! index within a given model domain - real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - real(rkind) :: psiLiq ! matric head of liquid water (m) - real(rkind) :: vFracWat ! volumetric fraction of total water, liquid+ice (-) - real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) - real(rkind) :: vFracIce ! volumetric fraction of ice (-) - real(rkind) :: enthTemp ! enthalpy at the temperature of the control volume (J m-3) - real(rkind) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) - real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) - real(rkind) :: enthWater ! enthalpy of total water (J m-3) - real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) - real(rkind) :: enthMix ! enthalpy of the mixed region, liquid+ice (J m-3) - real(rkind) :: enthLiq ! enthalpy of the liquid region (J m-3) - real(rkind) :: enthAir ! enthalpy of air (J m-3) - real(rkind) :: diffT - real(rkind) :: integral - real(rkind) :: enthIce - real(rkind) :: enthVeg - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - generalVars: associate(& - ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers - ! mapping between the full state vector and the state subset - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset - ! type of domain, type of state variable, and index of control volume within domain - ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) ! end associate statement - ! -------------------------------------------------------------------------------------------------------------------------------- - - ! initialize error control - err=0; message="t2enthalpy_T/" - - ! loop through model state variables - do iState=1,size(ixMapSubset2Full) + ! compute enthalpy + ! NOTE: assume intrrinsic density of ice is the intrinsic density of water + ! NOTE: kg m-3 J kg-1 K-1 K + Ey(iLook) = Ey(iLook) + iden_water*Cp_water*vFracLiq*T_incr + iden_water*Cp_ice*vFracIce*T_incr - ! ----- - ! - compute indices... - ! -------------------- - - ! get domain type, and index of the control volume within the domain - ixFullVector = ixMapSubset2Full(iState) ! index within full state vector - ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) - ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain - - ! check an energy state - if(ixStateType(ixFullVector)==iname_nrgCanair .or. ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer)then - - ! get the layer index - select case(ixDomainType) - case(iname_cas); iLayer = integerMissing - case(iname_veg); iLayer = integerMissing - case(iname_snow); iLayer = ixControlIndex - case(iname_soil); iLayer = ixControlIndex + nSnow - case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) - case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return - end select - - ! identify domain - select case(ixDomainType) - - ! ----- - ! - canopy air space... - ! --------------------- - case(iname_cas) - scalarCanairEnthalpy = Cp_air * iden_air * (scalarCanairTempTrial - Tfreeze) - ! ----- - ! - vegetation canopy... - ! ----------------------- - case(iname_veg) - ! association to necessary variables for vegetation - vegVars: associate(& - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): [dp] canopy depth (m) - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) - - diffT = scalarCanopyTempTrial - Tfreeze - enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth - if(diffT>=0._rkind)then - enthLiq = Cp_water * scalarCanopyWatTrial * diffT / canopyDepth - enthIce = 0._rkind - enthPhase = 0._rkind - else - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth - enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth - enthPhase = LH_fus * scalarCanopyIceTrial / canopyDepth - end if - - scalarCanopyEnthalpy = enthVeg + enthLiq + enthIce + end do ! numerical integration - ! end association - end associate vegVars + end do ! loop through lookup table - ! ----- - ! - snow... - ! --------- - case(iname_snow) - - ! association to necessary variables for snow - snowVars: associate(& - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) - - diffT = mLayerTempTrial(iLayer) - Tfreeze - if(diffT>=0._rkind)then - enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * diffT - enthIce = 0._rkind - enthAir = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) ) * diffT - enthPhase = 0._rkind - else - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral - enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) - enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) - enthPhase = iden_ice * LH_fus * mLayerVolFracIceTrial(iLayer) - end if - - mLayerEnthalpy(iLayer) = enthLiq + enthIce + enthAir - - ! end association - end associate snowVars - - ! ----- - ! - soil... - ! --------- - case(iname_soil) - - ! make association to variables in the data structures... - soilVars: associate(& - - ! associate model parameters - soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex) , & ! intrinsic soil density (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex) , & ! soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex) , & ! volumetric residual water content (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) - - ! associate values in the lookup table - Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) - E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function - - ) ! end associate statement - - ! diagnostic variables - vGn_m = 1._rkind - 1._rkind/vGn_n - Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) - vFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - - - ! *** compute enthalpy of water for unfrozen conditions - if(mlayerTempTrial(iLayer) > Tcrit)then - enthWater = iden_water*Cp_water*vFracWat*(mlayerTempTrial(iLayer) - Tfreeze) ! valid for temperatures below freezing also - enthPhase = 0._rkind - - ! *** compute enthalpy of water for frozen conditions - else - ! calculate enthalpy at the temperature (cubic spline interpolation) - call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),enthTemp,err,cmessage) + ! use cubic spline interpolation to obtain enthalpy values at the desired values of temperature + call spline(Tk,Ey,1.e30_rkind,1.e30_rkind,E2,err,cmessage) ! get the second derivatives if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - ! calculate enthalpy at the critical temperature (cubic spline interpolation) - call splint(Tk,Ey,E2,Tcrit,enthTcrit,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! calculate the enthalpy of water - enthMix = enthTemp - enthTcrit ! enthalpy of the liquid+ice mix - enthLiq = iden_water*Cp_water*vFracWat*(Tcrit - Tfreeze) - enthWater = enthMix + enthLiq - - ! *** compute the enthalpy associated with phase change - psiLiq = (mLayerTempTrial(iLayer) - Tfreeze)*LH_fus/(gravity*Tfreeze) - vFracLiq = volFracLiq(psiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - vFracIce = vFracWat - vFracLiq - enthPhase = iden_water*LH_fus*vFracIce - - endif ! (if frozen conditions) - - ! *** compute the enthalpy of soil - enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*(mlayerTempTrial(iLayer) - Tfreeze) - - ! *** compute the enthalpy of air - enthAir = iden_air*Cp_air*(1._rkind - theta_sat - vFracWat)*(mlayerTempTrial(iLayer) - Tfreeze) - - ! *** compute the total enthalpy (J m-3) - mLayerEnthalpy(iLayer) = enthSoil + enthWater + enthAir - -! print*, iLayer, enthSoil, enthWater, enthAir, enthPhase - - end associate soilVars - - ! ----- - ! - checks... - ! ----------- - case(iname_aquifer); cycle ! aquifer: do nothing - case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return - end select - - end if ! if an energy layer - end do ! looping through state variables - - end associate generalVars + ! test + if(doTest)then + + ! calculate enthalpy + call splint(Tk,Ey,E2,T_test,E_test,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! write values + print*, 'doTest = ', doTest + print*, 'T_test = ', T_test ! temperature (K) + print*, 'E_test = ', E_test ! enthalpy (J m-3) + print*, 'theta_sat = ', theta_sat ! soil porosity (-) + print*, 'theta_res = ', theta_res ! volumetric residual water content (-) + print*, 'vGn_alpha = ', vGn_alpha ! van Genuchten "alpha" parameter (m-1) + print*, 'vGn_n = ', vGn_n ! van Genuchten "n" parameter (-) + print*, trim(message)//'PAUSE: Set doTest=.false. to complete simulations' + read(*,*) + + endif ! if testing + + ! end asssociation to variables in the data structures + end associate + + end do ! (looping through soil layers) +end subroutine T2E_lookup + + +! ************************************************************************************************************************ +! public subroutine t2enthalpy: compute enthalpy from temperature and total water content +! ************************************************************************************************************************ +subroutine t2enthalpy(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in) + ! output: enthalpy + scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,message) ! intent(out): error control + ! ------------------------------------------------------------------------------------------------------------------------- + ! downwind routines + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water + USE spline_int_module,only:splint ! use for cubic spline interpolation + implicit none + ! delare dummy variables + ! ------------------------------------------------------------------------------------------------------------------------- + ! input: data structures + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model indices + type(zLookup),intent(in) :: lookup_data ! lookup tables + ! input: state variables for the vegetation canopy + real(rkind),intent(in) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) + real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric fraction of Ice (-) + ! output: enthalpy + real(rkind),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) + real(rkind),intent(out) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------------------------------- + ! declare local variables + character(len=128) :: cmessage ! error message in downwind routine + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixDomainType ! name of a given model domain + integer(i4b) :: ixControlIndex ! index within a given model domain + real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind) :: psiLiq ! matric head of liquid water (m) + real(rkind) :: vFracWat ! volumetric fraction of total water, liquid+ice (-) + real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) + real(rkind) :: vFracIce ! volumetric fraction of ice (-) + real(rkind) :: enthTemp ! enthalpy at the temperature of the control volume (J m-3) + real(rkind) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) + real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) + real(rkind) :: enthWater ! enthalpy of total water (J m-3) + real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) + real(rkind) :: enthMix ! enthalpy of the mixed region, liquid+ice (J m-3) + real(rkind) :: enthLiq ! enthalpy of the liquid region (J m-3) + real(rkind) :: enthAir ! enthalpy of air (J m-3) + real(rkind) :: diffT + real(rkind) :: integral + real(rkind) :: enthIce + real(rkind) :: enthVeg + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + generalVars: associate(& + ! number of model layers, and layer type + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + ! mapping between the full state vector and the state subset + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ! type of domain, type of state variable, and index of control volume within domain + ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) ! end associate statement + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message="t2enthalpy/" + + ! loop through model state variables + do iState=1,size(ixMapSubset2Full) + + ! ----- + ! - compute indices... + ! -------------------- + + ! get domain type, and index of the control volume within the domain + ixFullVector = ixMapSubset2Full(iState) ! index within full state vector + ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) + ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain + + ! check an energy state + if(ixStateType(ixFullVector)==iname_nrgCanair .or. ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer)then + + ! get the layer index + select case(ixDomainType) + case(iname_cas); iLayer = integerMissing + case(iname_veg); iLayer = integerMissing + case(iname_snow); iLayer = ixControlIndex + case(iname_soil); iLayer = ixControlIndex + nSnow + case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + + ! identify domain + select case(ixDomainType) + + ! ----- + ! - canopy air space... + ! --------------------- + case(iname_cas) + scalarCanairEnthalpy = Cp_air * iden_air * (scalarCanairTempTrial - Tfreeze) + ! ----- + ! - vegetation canopy... + ! ----------------------- + case(iname_veg) + ! association to necessary variables for vegetation + vegVars: associate(& + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): [dp] canopy depth (m) + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) + + diffT = scalarCanopyTempTrial - Tfreeze + enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth + if(diffT>=0._rkind)then + enthLiq = Cp_water * scalarCanopyWatTrial * diffT / canopyDepth + enthIce = 0._rkind + enthPhase = 0._rkind + else + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth + enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth + enthPhase = LH_fus * scalarCanopyIceTrial / canopyDepth + end if + + scalarCanopyEnthalpy = enthVeg + enthLiq + enthIce - enthPhase + + ! end association + end associate vegVars + + ! ----- + ! - snow... + ! --------- + case(iname_snow) + + ! association to necessary variables for snow + snowVars: associate(& + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) + + diffT = mLayerTempTrial(iLayer) - Tfreeze + if(diffT>=0._rkind)then + enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * diffT + enthIce = 0._rkind + enthAir = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) ) * diffT + enthPhase = 0._rkind + else + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral + enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) + enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) + enthPhase = iden_ice * LH_fus * mLayerVolFracIceTrial(iLayer) + end if + + mLayerEnthalpy(iLayer) = enthLiq + enthIce + enthAir - enthPhase + + ! end association + end associate snowVars + + ! ----- + ! - soil... + ! --------- + case(iname_soil) + + ! make association to variables in the data structures... + soilVars: associate(& + + ! associate model parameters + soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex) , & ! intrinsic soil density (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex) , & ! soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex) , & ! volumetric residual water content (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) + + ! associate values in the lookup table + Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) + Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) + E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + + ) ! end associate statement + + ! diagnostic variables + vGn_m = 1._rkind - 1._rkind/vGn_n + Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) + vFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + + + ! *** compute enthalpy of water for unfrozen conditions + if(mlayerTempTrial(iLayer) > Tcrit)then + enthWater = iden_water*Cp_water*vFracWat*(mlayerTempTrial(iLayer) - Tfreeze) ! valid for temperatures below freezing also + enthPhase = 0._rkind + + ! *** compute enthalpy of water for frozen conditions + else + ! calculate enthalpy at the temperature (cubic spline interpolation) + call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),enthTemp,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! calculate enthalpy at the critical temperature (cubic spline interpolation) + call splint(Tk,Ey,E2,Tcrit,enthTcrit,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! calculate the enthalpy of water + enthMix = enthTemp - enthTcrit ! enthalpy of the liquid+ice mix + enthLiq = iden_water*Cp_water*vFracWat*(Tcrit - Tfreeze) + enthWater = enthMix + enthLiq + + ! *** compute the enthalpy associated with phase change + psiLiq = (mLayerTempTrial(iLayer) - Tfreeze)*LH_fus/(gravity*Tfreeze) + vFracLiq = volFracLiq(psiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + vFracIce = vFracWat - vFracLiq + enthPhase = iden_water*LH_fus*vFracIce + + endif ! (if frozen conditions) + + ! *** compute the enthalpy of soil + enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*(mlayerTempTrial(iLayer) - Tfreeze) + + ! *** compute the enthalpy of air + enthAir = iden_air*Cp_air*(1._rkind - theta_sat - vFracWat)*(mlayerTempTrial(iLayer) - Tfreeze) + + ! *** compute the total enthalpy (J m-3) + mLayerEnthalpy(iLayer) = enthSoil + enthWater + enthAir - enthPhase + + end associate soilVars + + ! ----- + ! - checks... + ! ----------- + case(iname_aquifer); cycle ! aquifer: do nothing + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + + end if ! if an energy layer + end do ! looping through state variables + + end associate generalVars + +end subroutine t2enthalpy + - end subroutine t2enthalpy_T +! ************************************************************************************************************************ +! public subroutine t2enthalpy_T: compute enthalpy from temperature and total water content +! ************************************************************************************************************************ +subroutine t2enthalpy_T(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in) + ! output: enthalpy + scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,message) ! intent(out): error control + ! ------------------------------------------------------------------------------------------------------------------------- + ! downwind routines + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water + USE spline_int_module,only:splint ! use for cubic spline interpolation + implicit none + ! delare dummy variables + ! ------------------------------------------------------------------------------------------------------------------------- + ! input: data structures + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model indices + type(zLookup),intent(in) :: lookup_data ! lookup tables + ! input: state variables for the vegetation canopy + real(rkind),intent(in) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) + real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric fraction of Ice (-) + ! output: enthalpy + real(rkind),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) + real(rkind),intent(out) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------------------------------- + ! declare local variables + character(len=128) :: cmessage ! error message in downwind routine + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixDomainType ! name of a given model domain + integer(i4b) :: ixControlIndex ! index within a given model domain + real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind) :: psiLiq ! matric head of liquid water (m) + real(rkind) :: vFracWat ! volumetric fraction of total water, liquid+ice (-) + real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) + real(rkind) :: vFracIce ! volumetric fraction of ice (-) + real(rkind) :: enthTemp ! enthalpy at the temperature of the control volume (J m-3) + real(rkind) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) + real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) + real(rkind) :: enthWater ! enthalpy of total water (J m-3) + real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) + real(rkind) :: enthMix ! enthalpy of the mixed region, liquid+ice (J m-3) + real(rkind) :: enthLiq ! enthalpy of the liquid region (J m-3) + real(rkind) :: enthAir ! enthalpy of air (J m-3) + real(rkind) :: diffT + real(rkind) :: integral + real(rkind) :: enthIce + real(rkind) :: enthVeg + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + generalVars: associate(& + ! number of model layers, and layer type + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + ! mapping between the full state vector and the state subset + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ! type of domain, type of state variable, and index of control volume within domain + ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) ! end associate statement + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message="t2enthalpy_T/" + + ! loop through model state variables + do iState=1,size(ixMapSubset2Full) + + ! ----- + ! - compute indices... + ! -------------------- + + ! get domain type, and index of the control volume within the domain + ixFullVector = ixMapSubset2Full(iState) ! index within full state vector + ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) + ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain + + ! check an energy state + if(ixStateType(ixFullVector)==iname_nrgCanair .or. ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer)then + + ! get the layer index + select case(ixDomainType) + case(iname_cas); iLayer = integerMissing + case(iname_veg); iLayer = integerMissing + case(iname_snow); iLayer = ixControlIndex + case(iname_soil); iLayer = ixControlIndex + nSnow + case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + + ! identify domain + select case(ixDomainType) + case(iname_cas) + scalarCanairEnthalpy = Cp_air * iden_air * (scalarCanairTempTrial - Tfreeze) + + case(iname_veg) + ! association to necessary variables for vegetation + vegVars: associate(& + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): [dp] canopy depth (m) + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) + + diffT = scalarCanopyTempTrial - Tfreeze + enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth + if(diffT>=0._rkind)then + enthLiq = Cp_water * scalarCanopyWatTrial * diffT / canopyDepth + enthIce = 0._rkind + enthPhase = 0._rkind + else + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth + enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth + enthPhase = LH_fus * scalarCanopyIceTrial / canopyDepth + end if + + scalarCanopyEnthalpy = enthVeg + enthLiq + enthIce + + end associate vegVars + + case(iname_snow) + + ! association to necessary variables for snow + snowVars: associate(& + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) + + diffT = mLayerTempTrial(iLayer) - Tfreeze + if(diffT>=0._rkind)then + enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * diffT + enthIce = 0._rkind + enthAir = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) ) * diffT + enthPhase = 0._rkind + else + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral + enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) + enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) + enthPhase = iden_ice * LH_fus * mLayerVolFracIceTrial(iLayer) + end if + + mLayerEnthalpy(iLayer) = enthLiq + enthIce + enthAir + + end associate snowVars + + case(iname_soil) + + ! make association to variables in the data structures... + soilVars: associate(& + + ! associate model parameters + soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex) , & ! intrinsic soil density (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex) , & ! soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex) , & ! volumetric residual water content (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) + + ! associate values in the lookup table + Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) + Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) + E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + + ) ! end associate statement + + ! diagnostic variables + vGn_m = 1._rkind - 1._rkind/vGn_n + Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) + vFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + + + ! *** compute enthalpy of water for unfrozen conditions + if(mlayerTempTrial(iLayer) > Tcrit)then + enthWater = iden_water*Cp_water*vFracWat*(mlayerTempTrial(iLayer) - Tfreeze) ! valid for temperatures below freezing also + enthPhase = 0._rkind + + ! *** compute enthalpy of water for frozen conditions + else + ! calculate enthalpy at the temperature (cubic spline interpolation) + call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),enthTemp,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! calculate enthalpy at the critical temperature (cubic spline interpolation) + call splint(Tk,Ey,E2,Tcrit,enthTcrit,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! calculate the enthalpy of water + enthMix = enthTemp - enthTcrit ! enthalpy of the liquid+ice mix + enthLiq = iden_water*Cp_water*vFracWat*(Tcrit - Tfreeze) + enthWater = enthMix + enthLiq + + ! *** compute the enthalpy associated with phase change + psiLiq = (mLayerTempTrial(iLayer) - Tfreeze)*LH_fus/(gravity*Tfreeze) + vFracLiq = volFracLiq(psiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + vFracIce = vFracWat - vFracLiq + enthPhase = iden_water*LH_fus*vFracIce + + endif ! (if frozen conditions) + + ! *** compute the enthalpy of soil + enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*(mlayerTempTrial(iLayer) - Tfreeze) + + ! *** compute the enthalpy of air + enthAir = iden_air*Cp_air*(1._rkind - theta_sat - vFracWat)*(mlayerTempTrial(iLayer) - Tfreeze) + + ! *** compute the total enthalpy (J m-3) + mLayerEnthalpy(iLayer) = enthSoil + enthWater + enthAir + + end associate soilVars + + ! ----- + ! - checks... + ! ----------- + case(iname_aquifer); cycle ! aquifer: do nothing + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + + end if ! if an energy layer + end do ! looping through state variables + + end associate generalVars + +end subroutine t2enthalpy_T end module t2enthalpy_module From 46c7e4ced3b1a8f544ff531a0273c8578cfc6336 Mon Sep 17 00:00:00 2001 From: Kyle Date: Wed, 28 Sep 2022 17:59:00 +0000 Subject: [PATCH 0383/1472] changed indentation --- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/updateVars.f90 | 1281 ++++++++++++++-------------- 2 files changed, 637 insertions(+), 646 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 01563b05f..8c94ec464 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -343,7 +343,7 @@ subroutine eval8summa(& if(stateVecTrial( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVecTrial( ixSnowSoilHyd(iLayer) ) > xMax) message=trim(message)//'layer water outside bounds,' if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax - endif ! if water states + endif ! if water states end do ! loop through non-missing hydrology state variables in the snow+soil domain diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 index d9a74d5f5..78af5fa36 100644 --- a/build/source/engine/updateVars.f90 +++ b/build/source/engine/updateVars.f90 @@ -105,654 +105,645 @@ module updateVars_module contains - ! ********************************************************************************************************** - ! public subroutine updateVars: compute diagnostic variables and derivatives - ! ********************************************************************************************************** - subroutine updateVars(& - ! input - do_adjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - lookup_data, & ! intent(in): lookup tables for a local HRU - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - ! output: error control - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input - logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze - type(zLookup), intent(in) :: lookup_data ! lookup tables for a local HRU - type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! output: variables for the vegetation canopy - real(rkind),intent(inout) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - real(rkind),intent(inout) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) - real(rkind),intent(inout) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - ! output: variables for the snow-soil domain - real(rkind),intent(inout) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(inout) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(rkind),intent(inout) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(inout) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric ice water content (-) - real(rkind),intent(inout) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! general local variables - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixDomainType ! name of a given model domain - integer(i4b) :: ixControlIndex ! index within a given model domain - integer(i4b) :: ixOther,ixOtherLocal ! index of the coupled state variable within the (full, local) vector - logical(lgt) :: isCoupled ! .true. if a given variable shared another state variable in the same control volume - logical(lgt) :: isNrgState ! .true. if a given variable is an energy state - logical(lgt),allocatable :: computedCoupling(:) ! .true. if computed the coupling for a given state variable - real(rkind) :: scalarVolFracLiq ! volumetric fraction of liquid water (-) - real(rkind) :: scalarVolFracIce ! volumetric fraction of ice (-) - real(rkind) :: Tcrit ! critical soil temperature below which ice exists (K) - real(rkind) :: xTemp ! temporary temperature (K) - real(rkind) :: fLiq ! fraction of liquid water (-) - real(rkind) :: effSat ! effective saturation (-) - real(rkind) :: avPore ! available pore space (-) - character(len=256) :: cMessage ! error message of downwind routine - logical(lgt),parameter :: printFlag=.false. ! flag to turn on printing - ! iterative solution for temperature - real(rkind) :: meltNrg ! energy for melt+freeze (J m-3) - real(rkind) :: residual ! residual in the energy equation (J m-3) - real(rkind) :: derivative ! derivative in the energy equation (J m-3 K-1) - real(rkind) :: tempInc ! iteration increment (K) - integer(i4b) :: iter ! iteration index - integer(i4b) :: niter ! number of iterations - integer(i4b),parameter :: maxiter=100 ! maximum number of iterations - real(rkind),parameter :: nrgConvTol=1.e-4_rkind ! convergence tolerance for energy (J m-3) - real(rkind),parameter :: tempConvTol=1.e-6_rkind ! convergence tolerance for temperature (K) - real(rkind) :: critDiff ! temperature difference from critical (K) - real(rkind) :: tempMin ! minimum bracket for temperature (K) - real(rkind) :: tempMax ! maximum bracket for temperature (K) - logical(lgt) :: bFlag ! flag to denote that iteration increment was constrained using bi-section - real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical temperature (K) - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - associate(& - ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! indices defining model states and layers - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ! indices in the full vector for specific domains - ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain - ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain - ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain - ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain - ! mapping between the full state vector and the state subset - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset - ! type of domain, type of state variable, and index of control volume within domain - ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ! depth-varying model parameters - vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) - ! model diagnostic variables (heat capacity) - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(in): [dp ] volumetric heat capacity of the vegetation (J m-3 K-1) - mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] volumetric heat capacity in each layer (J m-3 K-1) - ! model diagnostic variables (fraction of liquid water) - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) - ! model states for the vegetation canopy - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variable vectors for the snow-soil layers - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] total water matric potential (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) - ! model diagnostic variables from a previous solution - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - ! derivatives - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential - dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) - dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(out): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature - ! derivatives inside solver for Jacobian only - dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. temperature - mLayerdTemp_dt => deriv_data%var(iLookDERIV%mLayerdTemp_dt )%dat ,& ! intent(out): [dp(:)] timestep change in layer temperature - scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt )%dat(1) & ! intent(out): [dp ] timestep change in canopy temperature -) ! association with variables in the data structures - - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - - ! initialize error control - err=0; message='updateVars/' - - ! allocate space and assign values to the flag vector - allocate(computedCoupling(size(ixMapSubset2Full)),stat=err) ! .true. if computed the coupling for a given state variable - if(err/=0)then; message=trim(message)//'problem allocating computedCoupling'; return; endif - computedCoupling(:)=.false. - - ! loop through model state variables - do iState=1,size(ixMapSubset2Full) - - ! check the need for the computations - if(computedCoupling(iState)) cycle - - ! ----- - ! - compute indices... - ! -------------------- - - ! get domain type, and index of the control volume within the domain - ixFullVector = ixMapSubset2Full(iState) ! index within full state vector - ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) - ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain - - ! get the layer index - select case(ixDomainType) - case(iname_cas); cycle ! canopy air space: do nothing - case(iname_veg); iLayer = 0 - case(iname_snow); iLayer = ixControlIndex - case(iname_soil); iLayer = ixControlIndex + nSnow - case(iname_aquifer); cycle ! aquifer: do nothing - case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return - end select - - ! get the index of the other (energy or mass) state variable within the full state vector - select case(ixDomainType) - case(iname_veg) ; ixOther = merge(ixHydCanopy(1), ixNrgCanopy(1), ixStateType(ixFullVector)==iname_nrgCanopy) - case(iname_snow, iname_soil); ixOther = merge(ixHydLayer(iLayer),ixNrgLayer(iLayer),ixStateType(ixFullVector)==iname_nrgLayer) - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - end select - - ! get the index in the local state vector - ixOtherLocal = ixMapFull2Subset(ixOther) ! ixOtherLocal could equal integerMissing - if(ixOtherLocal/=integerMissing) computedCoupling(ixOtherLocal)=.true. - - ! check if we have a coupled solution - isCoupled = (ixOtherLocal/=integerMissing) - - ! check if we are an energy state - isNrgState = (ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer) - - if(printFlag)then - print*, 'iState = ', iState, size(ixMapSubset2Full) - print*, 'ixFullVector = ', ixFullVector - print*, 'ixDomainType = ', ixDomainType - print*, 'ixControlIndex = ', ixControlIndex - print*, 'ixOther = ', ixOther - print*, 'ixOtherLocal = ', ixOtherLocal - print*, 'do_adjustTemp = ', do_adjustTemp - print*, 'isCoupled = ', isCoupled - print*, 'isNrgState = ', isNrgState - endif - - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - - ! update hydrology state variables for the uncoupled solution - if(.not.isNrgState .and. .not.isCoupled)then - - ! update the total water from volumetric liquid water - if(ixStateType(ixFullVector)==iname_liqCanopy .or. ixStateType(ixFullVector)==iname_liqLayer)then - select case(ixDomainType) - case(iname_veg); scalarCanopyWatTrial = scalarCanopyLiqTrial + scalarCanopyIceTrial - case(iname_snow); mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer)*iden_ice/iden_water - case(iname_soil); mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer) ! no volume expansion - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, or iname_soil'; return - end select - endif - - ! update the total water and the total water matric potential - if(ixDomainType==iname_soil)then - select case( ixStateType(ixFullVector) ) - ! --> update the total water from the liquid water matric potential - case(iname_lmpLayer) - effSat = volFracLiq(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex)) ! effective saturation - avPore = theta_sat(ixControlIndex) - mLayerVolFracIceTrial(iLayer) - theta_res(ixControlIndex) ! available pore space - mLayerVolFracLiqTrial(iLayer) = effSat*avPore + theta_res(ixControlIndex) - mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer) ! no volume expansion - mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - !write(*,'(a,1x,i4,1x,3(f20.10,1x))') 'mLayerVolFracLiqTrial(iLayer) 1 = ', iLayer, mLayerVolFracLiqTrial(iLayer), mLayerVolFracIceTrial(iLayer), mLayerVolFracWatTrial(iLayer) - ! --> update the total water from the total water matric potential - case(iname_matLayer) - mLayerVolFracWatTrial(iLayer) = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - ! --> update the total water matric potential (assume already have mLayerVolFracWatTrial given block above) - case(iname_liqLayer, iname_watLayer) - mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - case default; err=20; message=trim(message)//'expect iname_lmpLayer, iname_matLayer, iname_liqLayer, or iname_watLayer'; return - end select - endif ! if in the soil domain - - endif ! if hydrology state variable or uncoupled solution - - ! compute the critical soil temperature below which ice exists - select case(ixDomainType) - case(iname_veg, iname_snow); Tcrit = Tfreeze - case(iname_soil); Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - end select - - ! initialize temperature - select case(ixDomainType) - case(iname_veg); xTemp = scalarCanopyTempTrial - case(iname_snow, iname_soil); xTemp = mLayerTempTrial(iLayer) - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - end select - - ! define brackets for the root - ! NOTE: start with an enormous range; updated quickly in the iterations - tempMin = xTemp - 10._rkind - tempMax = xTemp + 10._rkind - - ! get iterations (set to maximum iterations if adjusting the temperature) - niter = merge(maxiter, 1, do_adjustTemp) - - ! iterate - iterations: do iter=1,niter - - ! restrict temperature - if(xTemp <= tempMin .or. xTemp >= tempMax)then - xTemp = 0.5_rkind*(tempMin + tempMax) ! new value - bFlag = .true. - else - bFlag = .false. - endif - - ! ----- - ! - compute derivatives... - ! ------------------------ - - ! compute temperature time derivatives - select case(ixDomainType) - case(iname_veg); scalarCanopydTemp_dt = xTemp - scalarCanopyTemp - case(iname_snow, iname_soil); mLayerdTemp_dt(iLayer) = xTemp - mLayerTemp(iLayer) - end select - - ! compute the derivative in total water content w.r.t. total water matric potential (m-1) - ! NOTE 1: valid for frozen and unfrozen conditions - ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi - select case(ixDomainType) - case(iname_veg) - fLiq = fracLiquid(xTemp,snowfrz_scale) - dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) - case(iname_snow) - fLiq = fracLiquid(xTemp,snowfrz_scale) - dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air - case(iname_soil) - select case( ixStateType(ixFullVector) ) - case(iname_lmpLayer); dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore - case default; dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - end select - ! dVolHtCapBulk_dPsi0 uses the derivative in water retention curve above critical temp w.r.t.state variable dVolTot_dPsi0 - dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use - if(xTemp< Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) - if(xTemp>=Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_water * Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) - end select - - ! compute the derivative in liquid water content w.r.t. temperature - ! --> partially frozen: dependence of liquid water on temperature - if(xTemp unfrozen: no dependence of liquid water on temperature - else - select case(ixDomainType) - case(iname_veg); dTheta_dTkCanopy = 0._rkind; dVolHtCapBulk_dTkCanopy = 0._rkind - case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - end select ! domain type - endif - - ! ----- - ! - update volumetric fraction of liquid water and ice... - ! => case of hydrology state uncoupled with energy (and when not adjusting the temperature)... - ! ----------------------------------------------------------------------------------------------- - - ! case of hydrology state uncoupled with energy (and when not adjusting the temperature) - if(.not.do_adjustTemp .and. .not.isNrgState .and. .not.isCoupled)then - - ! compute the fraction of snow - select case(ixDomainType) - case(iname_veg); scalarFracLiqVeg = fracliquid(xTemp,snowfrz_scale) - case(iname_snow); mLayerFracLiqSnow(iLayer) = fracliquid(xTemp,snowfrz_scale) - case(iname_soil) ! do nothing - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - end select ! domain type - - ! ----- - ! - update volumetric fraction of liquid water and ice... - ! => case of energy state or coupled solution (or adjusting the temperature)... - ! -------------------------------------------------------------------------------- - - ! case of energy state OR coupled solution (or adjusting the temperature) - elseif(do_adjustTemp .or. ( (isNrgState .or. isCoupled) ) )then - - ! identify domain type - select case(ixDomainType) - - ! *** vegetation canopy - case(iname_veg) - - ! compute volumetric fraction of liquid water and ice - call updateSnow(xTemp, & ! intent(in) : temperature (K) - scalarCanopyWatTrial/(iden_water*canopyDepth),& ! intent(in) : volumetric fraction of total water (-) - snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) - scalarVolFracLiq, & ! intent(out) : trial volumetric fraction of liquid water (-) - scalarVolFracIce, & ! intent(out) : trial volumetric fraction if ice (-) - scalarFracLiqVeg, & ! intent(out) : fraction of liquid water (-) - err,cmessage) ! intent(out) : error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! compute mass of water on the canopy - ! NOTE: possibilities for speed-up here - scalarCanopyLiqTrial = scalarFracLiqVeg *scalarCanopyWatTrial !(kg m-2), scalarVolFracLiq*iden_water*canopyDepth - scalarCanopyIceTrial = (1._rkind - scalarFracLiqVeg)*scalarCanopyWatTrial !(kg m-2), scalarVolFracIce* iden_ice *canopyDepth - - ! *** snow layers - case(iname_snow) - - ! compute volumetric fraction of liquid water and ice - call updateSnow(xTemp, & ! intent(in) : temperature (K) - mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) - snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) - mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) - mLayerFracLiqSnow(iLayer), & ! intent(out) : fraction of liquid water (-) - err,cmessage) ! intent(out) : error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! *** soil layers - case(iname_soil) - - ! compute volumetric fraction of liquid water and ice - call updateSoil(xTemp, & ! intent(in) : temperature (K) - mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) - vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in) : soil parameters - mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) - mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) - err,cmessage) ! intent(out) : error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! check - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - - end select ! domain type - - ! final check - else - - ! do nothing (input = output) -- and check that we got here correctly - if( (isNrgState .or. isCoupled) )then - scalarVolFracLiq = realMissing - scalarVolFracIce = realMissing - else - message=trim(message)//'unexpected else branch' - err=20; return - endif - - endif ! if energy state or solution is coupled - - ! ----- - ! - update temperatures... - ! ------------------------ - - ! check the need to adjust temperature - if(do_adjustTemp)then - - ! get the melt energy - meltNrg = merge(LH_fus*iden_ice, LH_fus*iden_water, ixDomainType==iname_snow) - - ! compute the residual and the derivative - select case(ixDomainType) - - ! * vegetation - case(iname_veg) - call xTempSolve(& - ! constant over iterations - meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) - heatCap = scalarBulkVolHeatCapVeg ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) - tempInit = scalarCanopyTemp ,& ! intent(in) : initial temperature (K) - volFracIceInit = scalarCanopyIce/(iden_water*canopyDepth),& ! intent(in) : initial volumetric fraction of ice (-) - ! trial values - xTemp = xTemp ,& ! intent(inout) : trial value of temperature - dLiq_dT = dTheta_dTkCanopy ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) - volFracIceTrial = scalarVolFracIce ,& ! intent(in) : trial value for volumetric fraction of ice - ! residual and derivative - residual = residual ,& ! intent(out) : residual (J m-3) - derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) - - ! * snow and soil - case(iname_snow, iname_soil) - call xTempSolve(& - ! constant over iterations - meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) - heatCap = mLayerVolHtCapBulk(iLayer) ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) - tempInit = mLayerTemp(iLayer) ,& ! intent(in) : initial temperature (K) - volFracIceInit = mLayerVolFracIce(iLayer) ,& ! intent(in) : initial volumetric fraction of ice (-) - ! trial values - xTemp = xTemp ,& ! intent(inout) : trial value of temperature - dLiq_dT = mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) - volFracIceTrial = mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : trial value for volumetric fraction of ice - ! residual and derivative - residual = residual ,& ! intent(out) : residual (J m-3) - derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) - - ! * check - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - - end select ! domain type - - ! check validity of residual - if( ieee_is_nan(residual) )then - message=trim(message)//'residual is not valid' - err=20; return - endif - - ! update bracket - if(residual < 0._rkind)then - tempMax = min(xTemp,tempMax) - else - tempMin = max(tempMin,xTemp) - end if - - ! compute iteration increment - tempInc = residual/derivative ! K - - ! check - if(globalPrintFlag)& - write(*,'(i4,1x,e20.10,1x,5(f20.10,1x),L1)') iter, residual, xTemp-Tcrit, tempInc, Tcrit, tempMin, tempMax, bFlag - - ! check convergence - if(abs(residual) < nrgConvTol .or. abs(tempInc) < tempConvTol) exit iterations - - ! add constraints for snow temperature - if(ixDomainType==iname_veg .or. ixDomainType==iname_snow)then - if(tempInc > Tcrit - xTemp) tempInc=(Tcrit - xTemp)*0.5_rkind ! simple bi-section method - endif ! if the domain is vegetation or snow - - ! deal with the discontinuity between partially frozen and unfrozen soil - if(ixDomainType==iname_soil)then - ! difference from the temperature below which ice exists - critDiff = Tcrit - xTemp - ! --> initially frozen (T < Tcrit) - if(critDiff > 0._rkind)then - if(tempInc > critDiff) tempInc = critDiff + epsT ! set iteration increment to slightly above critical temperature - ! --> initially unfrozen (T > Tcrit) - else - if(tempInc < critDiff) tempInc = critDiff - epsT ! set iteration increment to slightly below critical temperature - endif - endif ! if the domain is soil - - ! update the temperature trial - xTemp = xTemp + tempInc - - ! check failed convergence - if(iter==maxiter)then - message=trim(message)//'failed to converge' - err=-20; return ! negative error code = try to recover - endif - - endif ! if adjusting the temperature - - end do iterations ! iterating - - ! save temperature - select case(ixDomainType) - case(iname_veg); scalarCanopyTempTrial = xTemp - case(iname_snow, iname_soil); mLayerTempTrial(iLayer) = xTemp - end select - - ! ======================================================================================================================================= - ! ======================================================================================================================================= - - ! ----- - ! - compute the liquid water matric potential (and necessay derivatives)... - ! ------------------------------------------------------------------------- - - ! only for soil - if(ixDomainType==iname_soil)then - - ! check liquid water (include tolerance) - if(mLayerVolFracLiqTrial(iLayer) > theta_sat(ixControlIndex)+epsT )then - message=trim(message)//'liquid water greater than porosity' - print*,'---------------' - print*,'porosity(theta_sat)=', theta_sat(ixControlIndex) - print*,'liq water =',mLayerVolFracLiqTrial(iLayer) - print*,'layer =',iLayer - print*,'---------------' - err=20; return - endif - - ! case of hydrology state uncoupled with energy - if(.not.isNrgState .and. .not.isCoupled)then - - ! derivatives relating liquid water matric potential to total water matric potential and temperature - dPsiLiq_dPsi0(ixControlIndex) = 1._rkind ! exact correspondence (psiLiq=psi0) - dPsiLiq_dTemp(ixControlIndex) = 0._rkind ! no relationship between liquid water matric potential and temperature - - ! case of energy state or coupled solution - else - - ! compute the liquid matric potential (and the derivatives w.r.t. total matric potential and temperature) - call liquidHead(& - ! input - mLayerMatricHeadTrial(ixControlIndex) ,& ! intent(in) : total water matric potential (m) - mLayerVolFracLiqTrial(iLayer) ,& ! intent(in) : volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : volumetric fraction of ice (-) - vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in) : soil parameters - dVolTot_dPsi0(ixControlIndex) ,& ! intent(in) : derivative in the soil water characteristic (m-1) - mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) - ! output - mLayerMatricHeadLiqTrial(ixControlIndex) ,& ! intent(out): liquid water matric potential (m) - dPsiLiq_dPsi0(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. the total water matric potential (-) - dPsiLiq_dTemp(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. temperature (m K-1) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - endif ! switch between hydrology and energy state - - endif ! if domain is soil - - end do ! looping through state variables - - ! deallocate space - deallocate(computedCoupling,stat=err) ! .true. if computed the coupling for a given state variable - if(err/=0)then; message=trim(message)//'problem deallocating computedCoupling'; return; endif - - ! end association to the variables in the data structures - end associate +! ********************************************************************************************************** +! public subroutine updateVars: compute diagnostic variables and derivatives +! ********************************************************************************************************** +subroutine updateVars(& + ! input + do_adjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + lookup_data, & ! intent(in): lookup tables for a local HRU + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output: variables for the vegetation canopy + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + ! output: error control + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input + logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze + type(zLookup), intent(in) :: lookup_data ! lookup tables for a local HRU + type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + ! output: variables for the vegetation canopy + real(rkind),intent(inout) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + real(rkind),intent(inout) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) + real(rkind),intent(inout) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + ! output: variables for the snow-soil domain + real(rkind),intent(inout) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(inout) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(rkind),intent(inout) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(inout) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric ice water content (-) + real(rkind),intent(inout) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! general local variables + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixDomainType ! name of a given model domain + integer(i4b) :: ixControlIndex ! index within a given model domain + integer(i4b) :: ixOther,ixOtherLocal ! index of the coupled state variable within the (full, local) vector + logical(lgt) :: isCoupled ! .true. if a given variable shared another state variable in the same control volume + logical(lgt) :: isNrgState ! .true. if a given variable is an energy state + logical(lgt),allocatable :: computedCoupling(:) ! .true. if computed the coupling for a given state variable + real(rkind) :: scalarVolFracLiq ! volumetric fraction of liquid water (-) + real(rkind) :: scalarVolFracIce ! volumetric fraction of ice (-) + real(rkind) :: Tcrit ! critical soil temperature below which ice exists (K) + real(rkind) :: xTemp ! temporary temperature (K) + real(rkind) :: fLiq ! fraction of liquid water (-) + real(rkind) :: effSat ! effective saturation (-) + real(rkind) :: avPore ! available pore space (-) + character(len=256) :: cMessage ! error message of downwind routine + logical(lgt),parameter :: printFlag=.false. ! flag to turn on printing + ! iterative solution for temperature + real(rkind) :: meltNrg ! energy for melt+freeze (J m-3) + real(rkind) :: residual ! residual in the energy equation (J m-3) + real(rkind) :: derivative ! derivative in the energy equation (J m-3 K-1) + real(rkind) :: tempInc ! iteration increment (K) + integer(i4b) :: iter ! iteration index + integer(i4b) :: niter ! number of iterations + integer(i4b),parameter :: maxiter=100 ! maximum number of iterations + real(rkind),parameter :: nrgConvTol=1.e-4_rkind ! convergence tolerance for energy (J m-3) + real(rkind),parameter :: tempConvTol=1.e-6_rkind ! convergence tolerance for temperature (K) + real(rkind) :: critDiff ! temperature difference from critical (K) + real(rkind) :: tempMin ! minimum bracket for temperature (K) + real(rkind) :: tempMax ! maximum bracket for temperature (K) + logical(lgt) :: bFlag ! flag to denote that iteration increment was constrained using bi-section + real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical temperature (K) + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + associate(& + ! number of model layers, and layer type + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! indices defining model states and layers + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ! indices in the full vector for specific domains + ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain + ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + ! mapping between the full state vector and the state subset + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ! type of domain, type of state variable, and index of control volume within domain + ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ! depth-varying model parameters + vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) + ! model diagnostic variables (heat capacity) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(in): [dp ] volumetric heat capacity of the vegetation (J m-3 K-1) + mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] volumetric heat capacity in each layer (J m-3 K-1) + ! model diagnostic variables (fraction of liquid water) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) + ! model states for the vegetation canopy + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variable vectors for the snow-soil layers + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] total water matric potential (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) + ! model diagnostic variables from a previous solution + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + ! derivatives + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential + dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) + dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(out): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature + ! derivatives inside solver for Jacobian only + dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. temperature + mLayerdTemp_dt => deriv_data%var(iLookDERIV%mLayerdTemp_dt )%dat ,& ! intent(out): [dp(:)] timestep change in layer temperature + scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt )%dat(1) & ! intent(out): [dp ] timestep change in canopy temperature + ) ! association with variables in the data structures + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message='updateVars/' + + ! allocate space and assign values to the flag vector + allocate(computedCoupling(size(ixMapSubset2Full)),stat=err) ! .true. if computed the coupling for a given state variable + if(err/=0)then; message=trim(message)//'problem allocating computedCoupling'; return; endif + computedCoupling(:)=.false. + + ! loop through model state variables + do iState=1,size(ixMapSubset2Full) + + ! check the need for the computations + if(computedCoupling(iState)) cycle + + ! ----- + ! - compute indices... + ! -------------------- + + ! get domain type, and index of the control volume within the domain + ixFullVector = ixMapSubset2Full(iState) ! index within full state vector + ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) + ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain + + ! get the layer index + select case(ixDomainType) + case(iname_cas); cycle ! canopy air space: do nothing + case(iname_veg); iLayer = 0 + case(iname_snow); iLayer = ixControlIndex + case(iname_soil); iLayer = ixControlIndex + nSnow + case(iname_aquifer); cycle ! aquifer: do nothing + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + + ! get the index of the other (energy or mass) state variable within the full state vector + select case(ixDomainType) + case(iname_veg) ; ixOther = merge(ixHydCanopy(1), ixNrgCanopy(1), ixStateType(ixFullVector)==iname_nrgCanopy) + case(iname_snow, iname_soil); ixOther = merge(ixHydLayer(iLayer),ixNrgLayer(iLayer),ixStateType(ixFullVector)==iname_nrgLayer) + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select + + ! get the index in the local state vector + ixOtherLocal = ixMapFull2Subset(ixOther) ! ixOtherLocal could equal integerMissing + if(ixOtherLocal/=integerMissing) computedCoupling(ixOtherLocal)=.true. + + ! check if we have a coupled solution + isCoupled = (ixOtherLocal/=integerMissing) + + ! check if we are an energy state + isNrgState = (ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer) + + if(printFlag)then + print*, 'iState = ', iState, size(ixMapSubset2Full) + print*, 'ixFullVector = ', ixFullVector + print*, 'ixDomainType = ', ixDomainType + print*, 'ixControlIndex = ', ixControlIndex + print*, 'ixOther = ', ixOther + print*, 'ixOtherLocal = ', ixOtherLocal + print*, 'do_adjustTemp = ', do_adjustTemp + print*, 'isCoupled = ', isCoupled + print*, 'isNrgState = ', isNrgState + endif + + ! update hydrology state variables for the uncoupled solution + if(.not.isNrgState .and. .not.isCoupled)then + + ! update the total water from volumetric liquid water + if(ixStateType(ixFullVector)==iname_liqCanopy .or. ixStateType(ixFullVector)==iname_liqLayer)then + select case(ixDomainType) + case(iname_veg); scalarCanopyWatTrial = scalarCanopyLiqTrial + scalarCanopyIceTrial + case(iname_snow); mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer)*iden_ice/iden_water + case(iname_soil); mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer) ! no volume expansion + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, or iname_soil'; return + end select + endif + + ! update the total water and the total water matric potential + if(ixDomainType==iname_soil)then + select case( ixStateType(ixFullVector) ) + ! --> update the total water from the liquid water matric potential + case(iname_lmpLayer) + effSat = volFracLiq(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex)) ! effective saturation + avPore = theta_sat(ixControlIndex) - mLayerVolFracIceTrial(iLayer) - theta_res(ixControlIndex) ! available pore space + mLayerVolFracLiqTrial(iLayer) = effSat*avPore + theta_res(ixControlIndex) + mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer) ! no volume expansion + mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + !write(*,'(a,1x,i4,1x,3(f20.10,1x))') 'mLayerVolFracLiqTrial(iLayer) 1 = ', iLayer, mLayerVolFracLiqTrial(iLayer), mLayerVolFracIceTrial(iLayer), mLayerVolFracWatTrial(iLayer) + ! --> update the total water from the total water matric potential + case(iname_matLayer) + mLayerVolFracWatTrial(iLayer) = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + ! --> update the total water matric potential (assume already have mLayerVolFracWatTrial given block above) + case(iname_liqLayer, iname_watLayer) + mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + case default; err=20; message=trim(message)//'expect iname_lmpLayer, iname_matLayer, iname_liqLayer, or iname_watLayer'; return + end select + endif ! if in the soil domain + + endif ! if hydrology state variable or uncoupled solution + + ! compute the critical soil temperature below which ice exists + select case(ixDomainType) + case(iname_veg, iname_snow); Tcrit = Tfreeze + case(iname_soil); Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select + + ! initialize temperature + select case(ixDomainType) + case(iname_veg); xTemp = scalarCanopyTempTrial + case(iname_snow, iname_soil); xTemp = mLayerTempTrial(iLayer) + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select + + ! define brackets for the root + ! NOTE: start with an enormous range; updated quickly in the iterations + tempMin = xTemp - 10._rkind + tempMax = xTemp + 10._rkind + + ! get iterations (set to maximum iterations if adjusting the temperature) + niter = merge(maxiter, 1, do_adjustTemp) + + ! iterate + iterations: do iter=1,niter + + ! restrict temperature + if(xTemp <= tempMin .or. xTemp >= tempMax)then + xTemp = 0.5_rkind*(tempMin + tempMax) ! new value + bFlag = .true. + else + bFlag = .false. + endif + + ! ----- + ! - compute derivatives... + ! ------------------------ + + ! compute temperature time derivatives + select case(ixDomainType) + case(iname_veg); scalarCanopydTemp_dt = xTemp - scalarCanopyTemp + case(iname_snow, iname_soil); mLayerdTemp_dt(iLayer) = xTemp - mLayerTemp(iLayer) + end select + + ! compute the derivative in total water content w.r.t. total water matric potential (m-1) + ! NOTE 1: valid for frozen and unfrozen conditions + ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi + select case(ixDomainType) + case(iname_veg) + fLiq = fracLiquid(xTemp,snowfrz_scale) + dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) + case(iname_snow) + fLiq = fracLiquid(xTemp,snowfrz_scale) + dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air + case(iname_soil) + select case( ixStateType(ixFullVector) ) + case(iname_lmpLayer); dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + case default; dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + end select + ! dVolHtCapBulk_dPsi0 uses the derivative in water retention curve above critical temp w.r.t.state variable dVolTot_dPsi0 + dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use + if(xTemp< Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) + if(xTemp>=Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_water * Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) + end select + + ! compute the derivative in liquid water content w.r.t. temperature + ! --> partially frozen: dependence of liquid water on temperature + if(xTemp unfrozen: no dependence of liquid water on temperature + else + select case(ixDomainType) + case(iname_veg); dTheta_dTkCanopy = 0._rkind; dVolHtCapBulk_dTkCanopy = 0._rkind + case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select ! domain type + endif + + ! ----- + ! - update volumetric fraction of liquid water and ice... + ! => case of hydrology state uncoupled with energy (and when not adjusting the temperature)... + ! ----------------------------------------------------------------------------------------------- + + ! case of hydrology state uncoupled with energy (and when not adjusting the temperature) + if(.not.do_adjustTemp .and. .not.isNrgState .and. .not.isCoupled)then + + ! compute the fraction of snow + select case(ixDomainType) + case(iname_veg); scalarFracLiqVeg = fracliquid(xTemp,snowfrz_scale) + case(iname_snow); mLayerFracLiqSnow(iLayer) = fracliquid(xTemp,snowfrz_scale) + case(iname_soil) ! do nothing + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select ! domain type + + ! ----- + ! - update volumetric fraction of liquid water and ice... + ! => case of energy state or coupled solution (or adjusting the temperature)... + ! -------------------------------------------------------------------------------- + + ! case of energy state OR coupled solution (or adjusting the temperature) + elseif(do_adjustTemp .or. ( (isNrgState .or. isCoupled) ) )then + + ! identify domain type + select case(ixDomainType) + + ! *** vegetation canopy + case(iname_veg) + + ! compute volumetric fraction of liquid water and ice + call updateSnow(xTemp, & ! intent(in) : temperature (K) + scalarCanopyWatTrial/(iden_water*canopyDepth),& ! intent(in) : volumetric fraction of total water (-) + snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) + scalarVolFracLiq, & ! intent(out) : trial volumetric fraction of liquid water (-) + scalarVolFracIce, & ! intent(out) : trial volumetric fraction if ice (-) + scalarFracLiqVeg, & ! intent(out) : fraction of liquid water (-) + err,cmessage) ! intent(out) : error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! compute mass of water on the canopy + ! NOTE: possibilities for speed-up here + scalarCanopyLiqTrial = scalarFracLiqVeg *scalarCanopyWatTrial !(kg m-2), scalarVolFracLiq*iden_water*canopyDepth + scalarCanopyIceTrial = (1._rkind - scalarFracLiqVeg)*scalarCanopyWatTrial !(kg m-2), scalarVolFracIce* iden_ice *canopyDepth + + ! *** snow layers + case(iname_snow) + + ! compute volumetric fraction of liquid water and ice + call updateSnow(xTemp, & ! intent(in) : temperature (K) + mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) + snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) + mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) + mLayerFracLiqSnow(iLayer), & ! intent(out) : fraction of liquid water (-) + err,cmessage) ! intent(out) : error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! *** soil layers + case(iname_soil) + + ! compute volumetric fraction of liquid water and ice + call updateSoil(xTemp, & ! intent(in) : temperature (K) + mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) + vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in) : soil parameters + mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) + mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) + err,cmessage) ! intent(out) : error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! check + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + + end select ! domain type + + ! final check + else + + ! do nothing (input = output) -- and check that we got here correctly + if( (isNrgState .or. isCoupled) )then + scalarVolFracLiq = realMissing + scalarVolFracIce = realMissing + else + message=trim(message)//'unexpected else branch' + err=20; return + endif + + endif ! if energy state or solution is coupled + + ! ----- + ! - update temperatures... + ! ------------------------ + + ! check the need to adjust temperature + if(do_adjustTemp)then + + ! get the melt energy + meltNrg = merge(LH_fus*iden_ice, LH_fus*iden_water, ixDomainType==iname_snow) + + ! compute the residual and the derivative + select case(ixDomainType) + + ! * vegetation + case(iname_veg) + call xTempSolve(& + ! constant over iterations + meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) + heatCap = scalarBulkVolHeatCapVeg ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) + tempInit = scalarCanopyTemp ,& ! intent(in) : initial temperature (K) + volFracIceInit = scalarCanopyIce/(iden_water*canopyDepth),& ! intent(in) : initial volumetric fraction of ice (-) + ! trial values + xTemp = xTemp ,& ! intent(inout) : trial value of temperature + dLiq_dT = dTheta_dTkCanopy ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) + volFracIceTrial = scalarVolFracIce ,& ! intent(in) : trial value for volumetric fraction of ice + ! residual and derivative + residual = residual ,& ! intent(out) : residual (J m-3) + derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) + + ! * snow and soil + case(iname_snow, iname_soil) + call xTempSolve(& + ! constant over iterations + meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) + heatCap = mLayerVolHtCapBulk(iLayer) ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) + tempInit = mLayerTemp(iLayer) ,& ! intent(in) : initial temperature (K) + volFracIceInit = mLayerVolFracIce(iLayer) ,& ! intent(in) : initial volumetric fraction of ice (-) + ! trial values + xTemp = xTemp ,& ! intent(inout) : trial value of temperature + dLiq_dT = mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) + volFracIceTrial = mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : trial value for volumetric fraction of ice + ! residual and derivative + residual = residual ,& ! intent(out) : residual (J m-3) + derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) + + ! * check + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + + end select ! domain type + + ! check validity of residual + if( ieee_is_nan(residual) )then + message=trim(message)//'residual is not valid' + err=20; return + endif + + ! update bracket + if(residual < 0._rkind)then + tempMax = min(xTemp,tempMax) + else + tempMin = max(tempMin,xTemp) + end if + + ! compute iteration increment + tempInc = residual/derivative ! K + + ! check + if(globalPrintFlag)& + write(*,'(i4,1x,e20.10,1x,5(f20.10,1x),L1)') iter, residual, xTemp-Tcrit, tempInc, Tcrit, tempMin, tempMax, bFlag + + ! check convergence + if(abs(residual) < nrgConvTol .or. abs(tempInc) < tempConvTol) exit iterations + + ! add constraints for snow temperature + if(ixDomainType==iname_veg .or. ixDomainType==iname_snow)then + if(tempInc > Tcrit - xTemp) tempInc=(Tcrit - xTemp)*0.5_rkind ! simple bi-section method + endif ! if the domain is vegetation or snow + + ! deal with the discontinuity between partially frozen and unfrozen soil + if(ixDomainType==iname_soil)then + ! difference from the temperature below which ice exists + critDiff = Tcrit - xTemp + ! --> initially frozen (T < Tcrit) + if(critDiff > 0._rkind)then + if(tempInc > critDiff) tempInc = critDiff + epsT ! set iteration increment to slightly above critical temperature + ! --> initially unfrozen (T > Tcrit) + else + if(tempInc < critDiff) tempInc = critDiff - epsT ! set iteration increment to slightly below critical temperature + endif + endif ! if the domain is soil + + ! update the temperature trial + xTemp = xTemp + tempInc + + ! check failed convergence + if(iter==maxiter)then + message=trim(message)//'failed to converge' + err=-20; return ! negative error code = try to recover + endif + + endif ! if adjusting the temperature + + end do iterations ! iterating + + ! save temperature + select case(ixDomainType) + case(iname_veg); scalarCanopyTempTrial = xTemp + case(iname_snow, iname_soil); mLayerTempTrial(iLayer) = xTemp + end select + + ! ======================================================================================================================================= + ! ======================================================================================================================================= + + ! ----- + ! - compute the liquid water matric potential (and necessay derivatives)... + ! ------------------------------------------------------------------------- + + ! only for soil + if(ixDomainType==iname_soil)then + + ! check liquid water (include tolerance) + if(mLayerVolFracLiqTrial(iLayer) > theta_sat(ixControlIndex)+epsT )then + message=trim(message)//'liquid water greater than porosity' + print*,'---------------' + print*,'porosity(theta_sat)=', theta_sat(ixControlIndex) + print*,'liq water =',mLayerVolFracLiqTrial(iLayer) + print*,'layer =',iLayer + print*,'---------------' + err=20; return + endif + + ! case of hydrology state uncoupled with energy + if(.not.isNrgState .and. .not.isCoupled)then + + ! derivatives relating liquid water matric potential to total water matric potential and temperature + dPsiLiq_dPsi0(ixControlIndex) = 1._rkind ! exact correspondence (psiLiq=psi0) + dPsiLiq_dTemp(ixControlIndex) = 0._rkind ! no relationship between liquid water matric potential and temperature + + ! case of energy state or coupled solution + else + + ! compute the liquid matric potential (and the derivatives w.r.t. total matric potential and temperature) + call liquidHead(& + ! input + mLayerMatricHeadTrial(ixControlIndex) ,& ! intent(in) : total water matric potential (m) + mLayerVolFracLiqTrial(iLayer) ,& ! intent(in) : volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : volumetric fraction of ice (-) + vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in) : soil parameters + dVolTot_dPsi0(ixControlIndex) ,& ! intent(in) : derivative in the soil water characteristic (m-1) + mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) + ! output + mLayerMatricHeadLiqTrial(ixControlIndex) ,& ! intent(out): liquid water matric potential (m) + dPsiLiq_dPsi0(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. the total water matric potential (-) + dPsiLiq_dTemp(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. temperature (m K-1) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + endif ! switch between hydrology and energy state + + endif ! if domain is soil + + end do ! looping through state variables + + deallocate(computedCoupling,stat=err) ! .true. if computed the coupling for a given state variable + if(err/=0)then; message=trim(message)//'problem deallocating computedCoupling'; return; endif + + end associate end subroutine updateVars - ! ********************************************************************************************************** - ! private subroutine xTempSolve: compute residual and derivative for temperature - ! ********************************************************************************************************** - subroutine xTempSolve(& - ! input: constant over iterations - meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) - heatCap ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) - tempInit ,& ! intent(in) : initial temperature (K) - volFracIceInit ,& ! intent(in) : initial volumetric fraction of ice (-) - ! input-output: trial values - xTemp ,& ! intent(inout) : trial value of temperature - dLiq_dT ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) - volFracIceTrial ,& ! intent(in) : trial value for volumetric fraction of ice - ! output: residual and derivative - residual ,& ! intent(out) : residual (J m-3) - derivative ) ! intent(out) : derivative (J m-3 K-1) - implicit none - ! input: constant over iterations - real(rkind),intent(in) :: meltNrg ! energy for melt+freeze (J m-3) - real(rkind),intent(in) :: heatCap ! volumetric heat capacity (J m-3 K-1) - real(rkind),intent(in) :: tempInit ! initial temperature (K) - real(rkind),intent(in) :: volFracIceInit ! initial volumetric fraction of ice (-) - ! input-output: trial values - real(rkind),intent(inout) :: xTemp ! trial value for temperature - real(rkind),intent(in) :: dLiq_dT ! derivative in liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: volFracIceTrial ! trial value for the volumetric fraction of ice (-) - ! output: residual and derivative - real(rkind),intent(out) :: residual ! residual (J m-3) - real(rkind),intent(out) :: derivative ! derivative (J m-3 K-1) - ! subroutine starts here - residual = -heatCap*(xTemp - tempInit) + meltNrg*(volFracIceTrial - volFracIceInit) ! J m-3 - derivative = heatCap + LH_fus*iden_water*dLiq_dT ! J m-3 K-1 - end subroutine xTempSolve +! ********************************************************************************************************** +! private subroutine xTempSolve: compute residual and derivative for temperature +! ********************************************************************************************************** +subroutine xTempSolve(& + ! input: constant over iterations + meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) + heatCap ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) + tempInit ,& ! intent(in) : initial temperature (K) + volFracIceInit ,& ! intent(in) : initial volumetric fraction of ice (-) + ! input-output: trial values + xTemp ,& ! intent(inout) : trial value of temperature + dLiq_dT ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) + volFracIceTrial ,& ! intent(in) : trial value for volumetric fraction of ice + ! output: residual and derivative + residual ,& ! intent(out) : residual (J m-3) + derivative ) ! intent(out) : derivative (J m-3 K-1) + implicit none + ! input: constant over iterations + real(rkind),intent(in) :: meltNrg ! energy for melt+freeze (J m-3) + real(rkind),intent(in) :: heatCap ! volumetric heat capacity (J m-3 K-1) + real(rkind),intent(in) :: tempInit ! initial temperature (K) + real(rkind),intent(in) :: volFracIceInit ! initial volumetric fraction of ice (-) + ! input-output: trial values + real(rkind),intent(inout) :: xTemp ! trial value for temperature + real(rkind),intent(in) :: dLiq_dT ! derivative in liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: volFracIceTrial ! trial value for the volumetric fraction of ice (-) + ! output: residual and derivative + real(rkind),intent(out) :: residual ! residual (J m-3) + real(rkind),intent(out) :: derivative ! derivative (J m-3 K-1) + ! subroutine starts here + residual = -heatCap*(xTemp - tempInit) + meltNrg*(volFracIceTrial - volFracIceInit) ! J m-3 + derivative = heatCap + LH_fus*iden_water*dLiq_dT ! J m-3 K-1 +end subroutine xTempSolve end module updateVars_module From 1077fa40aa0e1b9839ff9bd16f5bc18052ff4393 Mon Sep 17 00:00:00 2001 From: Kyle Date: Wed, 28 Sep 2022 18:02:58 +0000 Subject: [PATCH 0384/1472] fixed intentation --- build/source/engine/computResid.f90 | 369 ++++++++++++++-------------- 1 file changed, 183 insertions(+), 186 deletions(-) diff --git a/build/source/engine/computResid.f90 b/build/source/engine/computResid.f90 index 780e1b38c..6552d75a9 100644 --- a/build/source/engine/computResid.f90 +++ b/build/source/engine/computResid.f90 @@ -71,206 +71,203 @@ module computResid_module public::computResid contains - ! ********************************************************************************************************** - ! public subroutine computResid: compute the residual vector - ! ********************************************************************************************************** - subroutine computResid(& - ! input: model control - dt, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - ! input: flux vectors - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) - fVec, & ! intent(in): flux vector - ! input: state variables (already disaggregated into scalars and vectors) - scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) - scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) - scalarCanopyHydTrial, & ! intent(in): trial value of canopy water (kg m-2), either liquid water content or total water content - mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) - mLayerVolFracHydTrial, & ! intent(in): trial vector of volumetric water content (-), either liquid water content or total water content - scalarAquiferStorageTrial, & ! intent(in): trial value of storage of water in the aquifer (m) - ! input: diagnostic variables defining the liquid water and ice content (function of state variables) - scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) - mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) - ! input: data structures - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - flux_data, & ! intent(in): model fluxes for a local HRU - indx_data, & ! intent(in): index data - ! output - rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input: model control - real(rkind),intent(in) :: dt ! length of the time step (seconds) - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain - ! input: flux vectors - real(rkind),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) - real(rkind),intent(in) :: fVec(:) ! flux vector - ! input: state variables (already disaggregated into scalars and vectors) - real(rkind),intent(in) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind),intent(in) :: scalarCanopyHydTrial ! trial value for canopy water (kg m-2), either liquid water content or total water content - real(rkind),intent(in) :: mLayerTempTrial(:) ! trial value for temperature of each snow/soil layer (K) - real(rkind),intent(in) :: mLayerVolFracHydTrial(:) ! trial vector of volumetric water content (-), either liquid water content or total water content - real(rkind),intent(in) :: scalarAquiferStorageTrial ! trial value of aquifer storage (m) - ! input: diagnostic variables defining the liquid water and ice content (function of state variables) - real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) - ! input: data structures - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - ! output - real(rkind),intent(out) :: rAdd(:) ! additional (sink) terms on the RHS of the state equation - real(rkind),intent(out) :: rVec(:) ! NOTE: qp ! residual vector - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - ! -------------------------------------------------------------------------------------------------------------------------------- - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) - real(rkind) :: scalarCanopyHyd ! canopy water content (kg m-2), either liquid water content or total water content - real(rkind),dimension(nLayers) :: mLayerVolFracHyd ! vector of volumetric water content (-), either liquid water content or total water content - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - ! link to the necessary variables for the residual computations - associate(& - ! model state variables (vegetation canopy) - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) - ! model state variables (aquifer) - scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in): [dp] storage of water in the aquifer (m) - ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! model fluxes (sink terms in the soil domain) - mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(in): [dp] transpiration loss from each soil layer (m s-1) - mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(in): [dp(:)] baseflow from each soil layer (m s-1) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in storage associated with compression of the soil matrix (-) - ! number of state variables of a specific type - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain - ! model indices - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain - ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the soil subdomain - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of hydrology states in snow+soil domain - layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] named variables defining the type of layer in snow+soil domain - ) ! association to necessary variables for the residual computations - ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="computResid/" +! ********************************************************************************************************** +! public subroutine computResid: compute the residual vector +! ********************************************************************************************************** +subroutine computResid(& + ! input: model control + dt, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + ! input: flux vectors + sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + fVec, & ! intent(in): flux vector + ! input: state variables (already disaggregated into scalars and vectors) + scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) + scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) + scalarCanopyHydTrial, & ! intent(in): trial value of canopy water (kg m-2), either liquid water content or total water content + mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) + mLayerVolFracHydTrial, & ! intent(in): trial vector of volumetric water content (-), either liquid water content or total water content + scalarAquiferStorageTrial, & ! intent(in): trial value of storage of water in the aquifer (m) + ! input: diagnostic variables defining the liquid water and ice content (function of state variables) + scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) + mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) + ! input: data structures + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + flux_data, & ! intent(in): model fluxes for a local HRU + indx_data, & ! intent(in): index data + ! output + rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec, & ! intent(out): residual vector + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input: model control + real(rkind),intent(in) :: dt ! length of the time step (seconds) + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain + ! input: flux vectors + real(rkind),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + real(rkind),intent(in) :: fVec(:) ! flux vector + ! input: state variables (already disaggregated into scalars and vectors) + real(rkind),intent(in) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind),intent(in) :: scalarCanopyHydTrial ! trial value for canopy water (kg m-2), either liquid water content or total water content + real(rkind),intent(in) :: mLayerTempTrial(:) ! trial value for temperature of each snow/soil layer (K) + real(rkind),intent(in) :: mLayerVolFracHydTrial(:) ! trial vector of volumetric water content (-), either liquid water content or total water content + real(rkind),intent(in) :: scalarAquiferStorageTrial ! trial value of aquifer storage (m) + ! input: diagnostic variables defining the liquid water and ice content (function of state variables) + real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) + ! input: data structures + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + ! output + real(rkind),intent(out) :: rAdd(:) ! additional (sink) terms on the RHS of the state equation + real(rkind),intent(out) :: rVec(:) ! NOTE: qp ! residual vector + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + ! -------------------------------------------------------------------------------------------------------------------------------- + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) + real(rkind) :: scalarCanopyHyd ! canopy water content (kg m-2), either liquid water content or total water content + real(rkind),dimension(nLayers) :: mLayerVolFracHyd ! vector of volumetric water content (-), either liquid water content or total water content + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + ! link to the necessary variables for the residual computations + associate(& + ! model state variables (vegetation canopy) + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variables (snow and soil domains) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) + ! model state variables (aquifer) + scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in): [dp] storage of water in the aquifer (m) + ! canopy and layer depth + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! model fluxes (sink terms in the soil domain) + mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(in): [dp] transpiration loss from each soil layer (m s-1) + mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(in): [dp(:)] baseflow from each soil layer (m s-1) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in storage associated with compression of the soil matrix (-) + ! number of state variables of a specific type + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + ! model indices + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the soil subdomain + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of hydrology states in snow+soil domain + layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] named variables defining the type of layer in snow+soil domain + ) ! association to necessary variables for the residual computations + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="computResid/" - ! --- - ! * compute sink terms... - ! ----------------------- + ! --- + ! * compute sink terms... + ! ----------------------- - ! intialize additional terms on the RHS as zero - rAdd(:) = 0._rkind + ! intialize additional terms on the RHS as zero + rAdd(:) = 0._rkind - ! compute energy associated with melt freeze for the vegetation canopy - if(ixVegNrg/=integerMissing) rAdd(ixVegNrg) = rAdd(ixVegNrg) + LH_fus*(scalarCanopyIceTrial - scalarCanopyIce)/canopyDepth ! energy associated with melt/freeze (J m-3) + ! compute energy associated with melt freeze for the vegetation canopy + if(ixVegNrg/=integerMissing) rAdd(ixVegNrg) = rAdd(ixVegNrg) + LH_fus*(scalarCanopyIceTrial - scalarCanopyIce)/canopyDepth ! energy associated with melt/freeze (J m-3) - ! compute energy associated with melt/freeze for snow - ! NOTE: allow expansion of ice during melt-freeze for snow; deny expansion of ice during melt-freeze for soil - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - select case( layerType(iLayer) ) - case(iname_snow); rAdd( ixSnowSoilNrg(iLayer) ) = rAdd( ixSnowSoilNrg(iLayer) ) + LH_fus*iden_ice *(mLayerVolFracIceTrial(iLayer) - mLayerVolFracIce(iLayer)) - case(iname_soil); rAdd( ixSnowSoilNrg(iLayer) ) = rAdd( ixSnowSoilNrg(iLayer) ) + LH_fus*iden_water*(mLayerVolFracIceTrial(iLayer) - mLayerVolFracIce(iLayer)) - end select - end do ! looping through non-missing energy state variables in the snow+soil domain - endif + ! compute energy associated with melt/freeze for snow + ! NOTE: allow expansion of ice during melt-freeze for snow; deny expansion of ice during melt-freeze for soil + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + select case( layerType(iLayer) ) + case(iname_snow); rAdd( ixSnowSoilNrg(iLayer) ) = rAdd( ixSnowSoilNrg(iLayer) ) + LH_fus*iden_ice *(mLayerVolFracIceTrial(iLayer) - mLayerVolFracIce(iLayer)) + case(iname_soil); rAdd( ixSnowSoilNrg(iLayer) ) = rAdd( ixSnowSoilNrg(iLayer) ) + LH_fus*iden_water*(mLayerVolFracIceTrial(iLayer) - mLayerVolFracIce(iLayer)) + end select + end do ! looping through non-missing energy state variables in the snow+soil domain + endif - ! sink terms soil hydrology (-) - ! NOTE 1: state variable is volumetric water content, so melt-freeze is not included - ! NOTE 2: ground evaporation was already included in the flux at the upper boundary - ! NOTE 3: rAdd(ixSnowOnlyWat)=0, and is defined in the initialization above - ! NOTE 4: same sink terms for matric head and liquid matric potential - if(nSoilOnlyHyd>0)then - do concurrent (iLayer=1:nSoil,ixSoilOnlyHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) - rAdd( ixSoilOnlyHyd(iLayer) ) = rAdd( ixSoilOnlyHyd(iLayer) ) + dt*(mLayerTranspire(iLayer) - mLayerBaseflow(iLayer) )/mLayerDepth(iLayer+nSnow) - mLayerCompress(iLayer) - end do ! looping through non-missing energy state variables in the snow+soil domain - endif + ! sink terms soil hydrology (-) + ! NOTE 1: state variable is volumetric water content, so melt-freeze is not included + ! NOTE 2: ground evaporation was already included in the flux at the upper boundary + ! NOTE 3: rAdd(ixSnowOnlyWat)=0, and is defined in the initialization above + ! NOTE 4: same sink terms for matric head and liquid matric potential + if(nSoilOnlyHyd>0)then + do concurrent (iLayer=1:nSoil,ixSoilOnlyHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) + rAdd( ixSoilOnlyHyd(iLayer) ) = rAdd( ixSoilOnlyHyd(iLayer) ) + dt*(mLayerTranspire(iLayer) - mLayerBaseflow(iLayer) )/mLayerDepth(iLayer+nSnow) - mLayerCompress(iLayer) + end do ! looping through non-missing energy state variables in the snow+soil domain + endif - ! --- - ! * compute the residual vector... - ! -------------------------------- + ! --- + ! * compute the residual vector... + ! -------------------------------- - ! compute the residual vector for the vegetation canopy - ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision - ! --> energy balance - if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*scalarCanairTempTrial - ( (sMul(ixCasNrg)*scalarCanairTemp + fVec(ixCasNrg)*dt) + rAdd(ixCasNrg) ) - if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg)*scalarCanopyTempTrial - ( (sMul(ixVegNrg)*scalarCanopyTemp + fVec(ixVegNrg)*dt) + rAdd(ixVegNrg) ) + ! compute the residual vector for the vegetation canopy + ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision + ! --> energy balance + if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*scalarCanairTempTrial - ( (sMul(ixCasNrg)*scalarCanairTemp + fVec(ixCasNrg)*dt) + rAdd(ixCasNrg) ) + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg)*scalarCanopyTempTrial - ( (sMul(ixVegNrg)*scalarCanopyTemp + fVec(ixVegNrg)*dt) + rAdd(ixVegNrg) ) - ! --> mass balance - if(ixVegHyd/=integerMissing)then - scalarCanopyHyd = merge(scalarCanopyWat, scalarCanopyLiq, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) - rVec(ixVegHyd) = sMul(ixVegHyd)*scalarCanopyHydTrial - ( (sMul(ixVegHyd)*scalarCanopyHyd + fVec(ixVegHyd)*dt) + rAdd(ixVegHyd) ) - endif + ! --> mass balance + if(ixVegHyd/=integerMissing)then + scalarCanopyHyd = merge(scalarCanopyWat, scalarCanopyLiq, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) + rVec(ixVegHyd) = sMul(ixVegHyd)*scalarCanopyHydTrial - ( (sMul(ixVegHyd)*scalarCanopyHyd + fVec(ixVegHyd)*dt) + rAdd(ixVegHyd) ) + endif - ! compute the residual vector for the snow and soil sub-domains for energy - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) )*mLayerTempTrial(iLayer) - ( (sMul( ixSnowSoilNrg(iLayer) )*mLayerTemp(iLayer) + fVec( ixSnowSoilNrg(iLayer) )*dt) + rAdd( ixSnowSoilNrg(iLayer) ) ) - end do ! looping through non-missing energy state variables in the snow+soil domain - endif + ! compute the residual vector for the snow and soil sub-domains for energy + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) )*mLayerTempTrial(iLayer) - ( (sMul( ixSnowSoilNrg(iLayer) )*mLayerTemp(iLayer) + fVec( ixSnowSoilNrg(iLayer) )*dt) + rAdd( ixSnowSoilNrg(iLayer) ) ) + end do ! looping through non-missing energy state variables in the snow+soil domain + endif - ! compute the residual vector for the snow and soil sub-domains for hydrology - ! NOTE: residual depends on choice of state variable - if(nSnowSoilHyd>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) - ! (get the correct state variable) - mLayerVolFracHyd(iLayer) = merge(mLayerVolFracWat(iLayer), mLayerVolFracLiq(iLayer), (ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_matLayer) ) - ! (compute the residual) - rVec( ixSnowSoilHyd(iLayer) ) = mLayerVolFracHydTrial(iLayer) - ( (mLayerVolFracHyd(iLayer) + fVec( ixSnowSoilHyd(iLayer) )*dt) + rAdd( ixSnowSoilHyd(iLayer) ) ) - end do ! looping through non-missing energy state variables in the snow+soil domain - endif + ! compute the residual vector for the snow and soil sub-domains for hydrology + ! NOTE: residual depends on choice of state variable + if(nSnowSoilHyd>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) + ! (get the correct state variable) + mLayerVolFracHyd(iLayer) = merge(mLayerVolFracWat(iLayer), mLayerVolFracLiq(iLayer), (ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_matLayer) ) + ! (compute the residual) + rVec( ixSnowSoilHyd(iLayer) ) = mLayerVolFracHydTrial(iLayer) - ( (mLayerVolFracHyd(iLayer) + fVec( ixSnowSoilHyd(iLayer) )*dt) + rAdd( ixSnowSoilHyd(iLayer) ) ) + end do ! looping through non-missing energy state variables in the snow+soil domain + endif - ! compute the residual vector for the aquifer - if(ixAqWat/=integerMissing) rVec(ixAqWat) = sMul(ixAqWat)*scalarAquiferStorageTrial - ( (sMul(ixAqWat)*scalarAquiferStorage + fVec(ixAqWat)*dt) + rAdd(ixAqWat) ) + ! compute the residual vector for the aquifer + if(ixAqWat/=integerMissing) rVec(ixAqWat) = sMul(ixAqWat)*scalarAquiferStorageTrial - ( (sMul(ixAqWat)*scalarAquiferStorage + fVec(ixAqWat)*dt) + rAdd(ixAqWat) ) - ! print result - if(globalPrintFlag)then - write(*,'(a,1x,100(e12.5,1x))') 'rVec = ', rVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) - write(*,'(a,1x,100(e12.5,1x))') 'fVec = ', fVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) - !print*, 'PAUSE:'; read(*,*) - endif + if(globalPrintFlag)then + write(*,'(a,1x,100(e12.5,1x))') 'rVec = ', rVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) + write(*,'(a,1x,100(e12.5,1x))') 'fVec = ', fVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) + endif - ! check - if(any(isNan(rVec)))then - message=trim(message)//'we found some Indian bread (NaN) ' - write(*,'(a,1x,100(e12.5,1x))') 'rVec = ', rVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) - write(*,'(a,1x,100(e12.5,1x))') 'fVec = ', fVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) - err=20; return - endif + ! check + if(any(isNan(rVec)))then + message=trim(message)//'we found some Indian bread (NaN) ' + write(*,'(a,1x,100(e12.5,1x))') 'rVec = ', rVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) + write(*,'(a,1x,100(e12.5,1x))') 'fVec = ', fVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) + err=20; return + endif - ! end association with the necessary variabiles for the residual calculations - end associate + end associate - end subroutine computResid +end subroutine computResid end module computResid_module From 047fcf0b48727df66d1efce3e99a4654e2f75852 Mon Sep 17 00:00:00 2001 From: Kyle Date: Wed, 28 Sep 2022 19:37:04 +0000 Subject: [PATCH 0385/1472] fixed indentation --- build/source/engine/soil_utils.f90 | 1166 ++++++++++++++-------------- 1 file changed, 582 insertions(+), 584 deletions(-) diff --git a/build/source/engine/soil_utils.f90 b/build/source/engine/soil_utils.f90 index 4a854f6d6..2038a2f7d 100644 --- a/build/source/engine/soil_utils.f90 +++ b/build/source/engine/soil_utils.f90 @@ -58,317 +58,315 @@ module soil_utils_module contains - ! ****************************************************************************************************************************** - ! public subroutine iceImpede: compute the ice impedence factor - ! ****************************************************************************************************************************** - subroutine iceImpede(volFracIce,f_impede, & ! input - iceImpedeFactor,dIceImpede_dLiq) ! output - ! computes the ice impedence factor (separate function, as used multiple times) - implicit none - ! input variables - real(rkind),intent(in) :: volFracIce ! volumetric fraction of ice (-) - real(rkind),intent(in) :: f_impede ! ice impedence parameter (-) - ! output variables - real(rkind) :: iceImpedeFactor ! ice impedence factor (-) - real(rkind) :: dIceImpede_dLiq ! derivative in ice impedence factor w.r.t. volumetric liquid water content (-) - ! compute ice impedance factor as a function of volumetric ice content - iceImpedeFactor = 10._rkind**(-f_impede*volFracIce) - dIceImpede_dLiq = 0._rkind - - end subroutine iceImpede - - - ! ****************************************************************************************************************************** - ! public subroutine dIceImpede_dTemp: compute the derivative in the ice impedence factor w.r.t. temperature - ! ****************************************************************************************************************************** - subroutine dIceImpede_dTemp(volFracIce,dTheta_dT,f_impede,dIceImpede_dT) - ! computes the derivative in the ice impedance factor w.r.t. temperature - implicit none - ! input variables - real(rkind),intent(in) :: volFracIce ! volumetric fraction of ice (-) - real(rkind),intent(in) :: dTheta_dT ! derivative in volumetric liquid water content w.r.t temperature (K-1) - real(rkind),intent(in) :: f_impede ! ice impedence parameter (-) - ! output variables - real(rkind) :: dIceImpede_dT ! derivative in the ice impedance factor w.r.t. temperature (K-1) - ! -- - dIceImpede_dT = log(10._rkind)*f_impede*(10._rkind**(-f_impede*volFracIce))*dTheta_dT - end subroutine dIceImpede_dTemp - - - ! ****************************************************************************************************************************** - ! public subroutine: compute the liquid water matric potential (and the derivatives w.r.t. total matric potential and temperature) - ! ****************************************************************************************************************************** - subroutine liquidHead(& - ! input - matricHeadTotal ,& ! intent(in) : total water matric potential (m) - volFracLiq ,& ! intent(in) : volumetric fraction of liquid water (-) - volFracIce ,& ! intent(in) : volumetric fraction of ice (-) - vGn_alpha,vGn_n,theta_sat,theta_res,vGn_m,& ! intent(in) : soil parameters - dVolTot_dPsi0 ,& ! intent(in) : derivative in the soil water characteristic (m-1) - dTheta_dT ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) - ! output - matricHeadLiq ,& ! intent(out) : liquid water matric potential (m) - dPsiLiq_dPsi0 ,& ! intent(out) : derivative in the liquid water matric potential w.r.t. the total water matric potential (-) - dPsiLiq_dTemp ,& ! intent(out) : derivative in the liquid water matric potential w.r.t. temperature (m K-1) - err,message) ! intent(out) : error control - ! computes the liquid water matric potential (and the derivatives w.r.t. total matric potential and temperature) - implicit none - ! input - real(rkind),intent(in) :: matricHeadTotal ! total water matric potential (m) - real(rkind),intent(in) :: volFracLiq ! volumetric fraction of liquid water (-) - real(rkind),intent(in) :: volFracIce ! volumetric fraction of ice (-) - real(rkind),intent(in) :: vGn_alpha,vGn_n,theta_sat,theta_res,vGn_m ! soil parameters - real(rkind),intent(in) ,optional :: dVolTot_dPsi0 ! derivative in the soil water characteristic (m-1) - real(rkind),intent(in) ,optional :: dTheta_dT ! derivative in volumetric total water w.r.t. temperature (K-1) - ! output - real(rkind),intent(out) :: matricHeadLiq ! liquid water matric potential (m) - real(rkind),intent(out) ,optional :: dPsiLiq_dPsi0 ! derivative in the liquid water matric potential w.r.t. the total water matric potential (-) - real(rkind),intent(out) ,optional :: dPsiLiq_dTemp ! derivative in the liquid water matric potential w.r.t. temperature (m K-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! local - real(rkind) :: xNum,xDen ! temporary variables (numeratir, denominator) - real(rkind) :: effSat ! effective saturation (-) - real(rkind) :: dPsiLiq_dEffSat ! derivative in liquid water matric potential w.r.t. effective saturation (m) - real(rkind) :: dEffSat_dTemp ! derivative in effective saturation w.r.t. temperature (K-1) - ! ------------------------------------------------------------------------------------------------------------------------------ - ! initialize error control - err=0; message='liquidHead/' - - ! ** partially frozen soil - if(volFracIce > verySmall .and. matricHeadTotal < 0._rkind)then ! check that ice exists and that the soil is unsaturated - - ! ----- - ! - compute liquid water matric potential... - ! ------------------------------------------ - - ! - compute effective saturation - ! NOTE: include ice content as part of the solid porosity - major effect of ice is to reduce the pore size; ensure that effSat=1 at saturation - ! (from Zhao et al., J. Hydrol., 1997: Numerical analysis of simultaneous heat and mass transfer...) - xNum = volFracLiq - theta_res - xDen = theta_sat - volFracIce - theta_res - effSat = xNum/xDen ! effective saturation - - ! - matric head associated with liquid water - matricHeadLiq = matricHead(effSat,vGn_alpha,0._rkind,1._rkind,vGn_n,vGn_m) ! argument is effective saturation, so theta_res=0 and theta_sat=1 - - ! compute derivative in liquid water matric potential w.r.t. effective saturation (m) - if(present(dPsiLiq_dPsi0).or.present(dPsiLiq_dTemp))then - dPsiLiq_dEffSat = dPsi_dTheta(effSat,vGn_alpha,0._rkind,1._rkind,vGn_n,vGn_m) - endif - - ! ----- - ! - compute derivative in the liquid water matric potential w.r.t. the total water matric potential... - ! ---------------------------------------------------------------------------------------------------- - - ! check if the derivative is desired - if(present(dPsiLiq_dTemp))then - - ! (check required input derivative is present) - if(.not.present(dVolTot_dPsi0))then - message=trim(message)//'dVolTot_dPsi0 argument is missing' - err=20; return - endif - - ! (compute derivative in the liquid water matric potential w.r.t. the total water matric potential) - dPsiLiq_dPsi0 = dVolTot_dPsi0*dPsiLiq_dEffSat*xNum/(xDen**2._rkind) - - endif ! if dPsiLiq_dTemp is desired - - ! ----- - ! - compute the derivative in the liquid water matric potential w.r.t. temperature... - ! ----------------------------------------------------------------------------------- - - ! check if the derivative is desired - if(present(dPsiLiq_dTemp))then - - ! (check required input derivative is present) - if(.not.present(dTheta_dT))then - message=trim(message)//'dTheta_dT argument is missing' - err=20; return - endif - - ! (compute the derivative in the liquid water matric potential w.r.t. temperature) - dEffSat_dTemp = -dTheta_dT*xNum/(xDen**2._rkind) + dTheta_dT/xDen - dPsiLiq_dTemp = dPsiLiq_dEffSat*dEffSat_dTemp - - endif ! if dPsiLiq_dTemp is desired - - ! ** unfrozen soil - else ! (no ice) - matricHeadLiq = matricHeadTotal - if(present(dPsiLiq_dTemp)) dPsiLiq_dPsi0 = 1._rkind ! derivative=1 because values are identical - if(present(dPsiLiq_dTemp)) dPsiLiq_dTemp = 0._rkind ! derivative=0 because no impact of temperature for unfrozen conditions - end if ! (if ice exists) - - end subroutine liquidHead - - ! ****************************************************************************************************************************** - ! public function hydCondMP_liq: compute the hydraulic conductivity of macropores as a function of liquid water content (m s-1) - ! ****************************************************************************************************************************** - function hydCondMP_liq(volFracLiq,theta_sat,theta_mp,mpExp,satHydCond_ma,satHydCond_mi) - ! computes hydraulic conductivity given volFracLiq and soil hydraulic parameters - ! theta_sat, theta_mp, mpExp, satHydCond_ma, and satHydCond_mi - implicit none - ! dummies - real(rkind),intent(in) :: volFracLiq ! volumetric liquid water content (-) - real(rkind),intent(in) :: theta_sat ! soil porosity (-) - real(rkind),intent(in) :: theta_mp ! minimum volumetric liquid water content for macropore flow (-) - real(rkind),intent(in) :: mpExp ! empirical exponent in macropore flow equation (-) - real(rkind),intent(in) :: satHydCond_ma ! saturated hydraulic conductivity for macropores (m s-1) - real(rkind),intent(in) :: satHydCond_mi ! saturated hydraulic conductivity for micropores (m s-1) - real(rkind) :: hydCondMP_liq ! hydraulic conductivity (m s-1) - ! locals - real(rkind) :: theta_e ! effective soil moisture - if(volFracLiq > theta_mp)then +! ****************************************************************************************************************************** +! public subroutine iceImpede: compute the ice impedence factor +! ****************************************************************************************************************************** +subroutine iceImpede(volFracIce,f_impede, & ! input + iceImpedeFactor,dIceImpede_dLiq) ! output + ! computes the ice impedence factor (separate function, as used multiple times) + implicit none + ! input variables + real(rkind),intent(in) :: volFracIce ! volumetric fraction of ice (-) + real(rkind),intent(in) :: f_impede ! ice impedence parameter (-) + ! output variables + real(rkind) :: iceImpedeFactor ! ice impedence factor (-) + real(rkind) :: dIceImpede_dLiq ! derivative in ice impedence factor w.r.t. volumetric liquid water content (-) + ! compute ice impedance factor as a function of volumetric ice content + iceImpedeFactor = 10._rkind**(-f_impede*volFracIce) + dIceImpede_dLiq = 0._rkind + +end subroutine iceImpede + + +! ****************************************************************************************************************************** +! public subroutine dIceImpede_dTemp: compute the derivative in the ice impedence factor w.r.t. temperature +! ****************************************************************************************************************************** +subroutine dIceImpede_dTemp(volFracIce,dTheta_dT,f_impede,dIceImpede_dT) + ! computes the derivative in the ice impedance factor w.r.t. temperature + implicit none + ! input variables + real(rkind),intent(in) :: volFracIce ! volumetric fraction of ice (-) + real(rkind),intent(in) :: dTheta_dT ! derivative in volumetric liquid water content w.r.t temperature (K-1) + real(rkind),intent(in) :: f_impede ! ice impedence parameter (-) + ! output variables + real(rkind) :: dIceImpede_dT ! derivative in the ice impedance factor w.r.t. temperature (K-1) + ! -- + dIceImpede_dT = log(10._rkind)*f_impede*(10._rkind**(-f_impede*volFracIce))*dTheta_dT +end subroutine dIceImpede_dTemp + + +! ****************************************************************************************************************************** +! public subroutine: compute the liquid water matric potential (and the derivatives w.r.t. total matric potential and temperature) +! ****************************************************************************************************************************** +subroutine liquidHead(& + ! input + matricHeadTotal ,& ! intent(in) : total water matric potential (m) + volFracLiq ,& ! intent(in) : volumetric fraction of liquid water (-) + volFracIce ,& ! intent(in) : volumetric fraction of ice (-) + vGn_alpha,vGn_n,theta_sat,theta_res,vGn_m,& ! intent(in) : soil parameters + dVolTot_dPsi0 ,& ! intent(in) : derivative in the soil water characteristic (m-1) + dTheta_dT ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) + ! output + matricHeadLiq ,& ! intent(out) : liquid water matric potential (m) + dPsiLiq_dPsi0 ,& ! intent(out) : derivative in the liquid water matric potential w.r.t. the total water matric potential (-) + dPsiLiq_dTemp ,& ! intent(out) : derivative in the liquid water matric potential w.r.t. temperature (m K-1) + err,message) ! intent(out) : error control + ! computes the liquid water matric potential (and the derivatives w.r.t. total matric potential and temperature) + implicit none + ! input + real(rkind),intent(in) :: matricHeadTotal ! total water matric potential (m) + real(rkind),intent(in) :: volFracLiq ! volumetric fraction of liquid water (-) + real(rkind),intent(in) :: volFracIce ! volumetric fraction of ice (-) + real(rkind),intent(in) :: vGn_alpha,vGn_n,theta_sat,theta_res,vGn_m ! soil parameters + real(rkind),intent(in) ,optional :: dVolTot_dPsi0 ! derivative in the soil water characteristic (m-1) + real(rkind),intent(in) ,optional :: dTheta_dT ! derivative in volumetric total water w.r.t. temperature (K-1) + ! output + real(rkind),intent(out) :: matricHeadLiq ! liquid water matric potential (m) + real(rkind),intent(out) ,optional :: dPsiLiq_dPsi0 ! derivative in the liquid water matric potential w.r.t. the total water matric potential (-) + real(rkind),intent(out) ,optional :: dPsiLiq_dTemp ! derivative in the liquid water matric potential w.r.t. temperature (m K-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local + real(rkind) :: xNum,xDen ! temporary variables (numeratir, denominator) + real(rkind) :: effSat ! effective saturation (-) + real(rkind) :: dPsiLiq_dEffSat ! derivative in liquid water matric potential w.r.t. effective saturation (m) + real(rkind) :: dEffSat_dTemp ! derivative in effective saturation w.r.t. temperature (K-1) + ! ------------------------------------------------------------------------------------------------------------------------------ + ! initialize error control + err=0; message='liquidHead/' + + ! ** partially frozen soil + if(volFracIce > verySmall .and. matricHeadTotal < 0._rkind)then ! check that ice exists and that the soil is unsaturated + + ! ----- + ! - compute liquid water matric potential... + ! ------------------------------------------ + + ! - compute effective saturation + ! NOTE: include ice content as part of the solid porosity - major effect of ice is to reduce the pore size; ensure that effSat=1 at saturation + ! (from Zhao et al., J. Hydrol., 1997: Numerical analysis of simultaneous heat and mass transfer...) + xNum = volFracLiq - theta_res + xDen = theta_sat - volFracIce - theta_res + effSat = xNum/xDen ! effective saturation + + ! - matric head associated with liquid water + matricHeadLiq = matricHead(effSat,vGn_alpha,0._rkind,1._rkind,vGn_n,vGn_m) ! argument is effective saturation, so theta_res=0 and theta_sat=1 + + ! compute derivative in liquid water matric potential w.r.t. effective saturation (m) + if(present(dPsiLiq_dPsi0).or.present(dPsiLiq_dTemp))then + dPsiLiq_dEffSat = dPsi_dTheta(effSat,vGn_alpha,0._rkind,1._rkind,vGn_n,vGn_m) + endif + + ! ----- + ! - compute derivative in the liquid water matric potential w.r.t. the total water matric potential... + ! ---------------------------------------------------------------------------------------------------- + + ! check if the derivative is desired + if(present(dPsiLiq_dTemp))then + + ! (check required input derivative is present) + if(.not.present(dVolTot_dPsi0))then + message=trim(message)//'dVolTot_dPsi0 argument is missing' + err=20; return + endif + + ! (compute derivative in the liquid water matric potential w.r.t. the total water matric potential) + dPsiLiq_dPsi0 = dVolTot_dPsi0*dPsiLiq_dEffSat*xNum/(xDen**2._rkind) + + endif ! if dPsiLiq_dTemp is desired + + ! ----- + ! - compute the derivative in the liquid water matric potential w.r.t. temperature... + ! ----------------------------------------------------------------------------------- + + ! check if the derivative is desired + if(present(dPsiLiq_dTemp))then + + ! (check required input derivative is present) + if(.not.present(dTheta_dT))then + message=trim(message)//'dTheta_dT argument is missing' + err=20; return + endif + + ! (compute the derivative in the liquid water matric potential w.r.t. temperature) + dEffSat_dTemp = -dTheta_dT*xNum/(xDen**2._rkind) + dTheta_dT/xDen + dPsiLiq_dTemp = dPsiLiq_dEffSat*dEffSat_dTemp + + endif ! if dPsiLiq_dTemp is desired + + ! ** unfrozen soil + else ! (no ice) + matricHeadLiq = matricHeadTotal + if(present(dPsiLiq_dTemp)) dPsiLiq_dPsi0 = 1._rkind ! derivative=1 because values are identical + if(present(dPsiLiq_dTemp)) dPsiLiq_dTemp = 0._rkind ! derivative=0 because no impact of temperature for unfrozen conditions + end if ! (if ice exists) + +end subroutine liquidHead + +! ****************************************************************************************************************************** +! public function hydCondMP_liq: compute the hydraulic conductivity of macropores as a function of liquid water content (m s-1) +! ****************************************************************************************************************************** +function hydCondMP_liq(volFracLiq,theta_sat,theta_mp,mpExp,satHydCond_ma,satHydCond_mi) + ! computes hydraulic conductivity given volFracLiq and soil hydraulic parameters + ! theta_sat, theta_mp, mpExp, satHydCond_ma, and satHydCond_mi + implicit none + ! dummies + real(rkind),intent(in) :: volFracLiq ! volumetric liquid water content (-) + real(rkind),intent(in) :: theta_sat ! soil porosity (-) + real(rkind),intent(in) :: theta_mp ! minimum volumetric liquid water content for macropore flow (-) + real(rkind),intent(in) :: mpExp ! empirical exponent in macropore flow equation (-) + real(rkind),intent(in) :: satHydCond_ma ! saturated hydraulic conductivity for macropores (m s-1) + real(rkind),intent(in) :: satHydCond_mi ! saturated hydraulic conductivity for micropores (m s-1) + real(rkind) :: hydCondMP_liq ! hydraulic conductivity (m s-1) + ! locals + real(rkind) :: theta_e ! effective soil moisture + if(volFracLiq > theta_mp)then theta_e = (volFracLiq - theta_mp) / (theta_sat - theta_mp) hydCondMP_liq = (satHydCond_ma - satHydCond_mi) * (theta_e**mpExp) - else + else hydCondMP_liq = 0._rkind - end if - !write(*,'(a,4(f9.3,1x),2(e20.10))') 'in soil_utils: theta_mp, theta_sat, volFracLiq, hydCondMP_liq, satHydCond_ma, satHydCond_mi = ', & - ! theta_mp, theta_sat, volFracLiq, hydCondMP_liq, satHydCond_ma, satHydCond_mi - end function hydCondMP_liq - - - ! ****************************************************************************************************************************** - ! public function hydCond_psi: compute the hydraulic conductivity as a function of matric head (m s-1) - ! ****************************************************************************************************************************** - function hydCond_psi(psi,k_sat,alpha,n,m) - ! computes hydraulic conductivity given psi and soil hydraulic parameters k_sat, alpha, n, and m - implicit none - ! dummies - real(rkind),intent(in) :: psi ! soil water suction (m) - real(rkind),intent(in) :: k_sat ! saturated hydraulic conductivity (m s-1) - real(rkind),intent(in) :: alpha ! scaling parameter (m-1) - real(rkind),intent(in) :: n ! vGn "n" parameter (-) - real(rkind),intent(in) :: m ! vGn "m" parameter (-) - real(rkind) :: hydCond_psi ! hydraulic conductivity (m s-1) - if(psi<0._rkind)then + end if +end function hydCondMP_liq + + +! ****************************************************************************************************************************** +! public function hydCond_psi: compute the hydraulic conductivity as a function of matric head (m s-1) +! ****************************************************************************************************************************** +function hydCond_psi(psi,k_sat,alpha,n,m) + ! computes hydraulic conductivity given psi and soil hydraulic parameters k_sat, alpha, n, and m + implicit none + ! dummies + real(rkind),intent(in) :: psi ! soil water suction (m) + real(rkind),intent(in) :: k_sat ! saturated hydraulic conductivity (m s-1) + real(rkind),intent(in) :: alpha ! scaling parameter (m-1) + real(rkind),intent(in) :: n ! vGn "n" parameter (-) + real(rkind),intent(in) :: m ! vGn "m" parameter (-) + real(rkind) :: hydCond_psi ! hydraulic conductivity (m s-1) + if(psi<0._rkind)then hydCond_psi = k_sat * & ( ( (1._rkind - (psi*alpha)**(n-1._rkind) * (1._rkind + (psi*alpha)**n)**(-m))**2._rkind ) & - / ( (1._rkind + (psi*alpha)**n)**(m/2._rkind) ) ) - else + / ( (1._rkind + (psi*alpha)**n)**(m/2._rkind) ) ) + else hydCond_psi = k_sat - end if - end function hydCond_psi - - - ! ****************************************************************************************************************************** - ! public function hydCond_liq: compute the hydraulic conductivity as a function of volumetric liquid water content (m s-1) - ! ****************************************************************************************************************************** - function hydCond_liq(volFracLiq,k_sat,theta_res,theta_sat,m) - ! computes hydraulic conductivity given volFracLiq and soil hydraulic parameters k_sat, theta_sat, theta_res, and m - implicit none - ! dummies - real(rkind),intent(in) :: volFracLiq ! volumetric liquid water content (-) - real(rkind),intent(in) :: k_sat ! saturated hydraulic conductivity (m s-1) - real(rkind),intent(in) :: theta_res ! residual volumetric liquid water content (-) - real(rkind),intent(in) :: theta_sat ! soil porosity (-) - real(rkind),intent(in) :: m ! vGn "m" parameter (-) - real(rkind) :: hydCond_liq ! hydraulic conductivity (m s-1) - ! locals - real(rkind) :: theta_e ! effective soil moisture - if(volFracLiq < theta_sat)then + end if +end function hydCond_psi + + +! ****************************************************************************************************************************** +! public function hydCond_liq: compute the hydraulic conductivity as a function of volumetric liquid water content (m s-1) +! ****************************************************************************************************************************** +function hydCond_liq(volFracLiq,k_sat,theta_res,theta_sat,m) + ! computes hydraulic conductivity given volFracLiq and soil hydraulic parameters k_sat, theta_sat, theta_res, and m + implicit none + ! dummies + real(rkind),intent(in) :: volFracLiq ! volumetric liquid water content (-) + real(rkind),intent(in) :: k_sat ! saturated hydraulic conductivity (m s-1) + real(rkind),intent(in) :: theta_res ! residual volumetric liquid water content (-) + real(rkind),intent(in) :: theta_sat ! soil porosity (-) + real(rkind),intent(in) :: m ! vGn "m" parameter (-) + real(rkind) :: hydCond_liq ! hydraulic conductivity (m s-1) + ! locals + real(rkind) :: theta_e ! effective soil moisture + if(volFracLiq < theta_sat)then theta_e = (volFracLiq - theta_res) / (theta_sat - theta_res) hydCond_liq = k_sat*theta_e**(1._rkind/2._rkind) * (1._rkind - (1._rkind - theta_e**(1._rkind/m) )**m)**2._rkind - else + else hydCond_liq = k_sat - end if - end function hydCond_liq - - - ! ****************************************************************************************************************************** - ! public function volFracLiq: compute the volumetric liquid water content (-) - ! ****************************************************************************************************************************** - function volFracLiq(psi,alpha,theta_res,theta_sat,n,m) - ! computes the volumetric liquid water content given psi and soil hydraulic parameters theta_res, theta_sat, alpha, n, and m - implicit none - real(rkind),intent(in) :: psi ! soil water suction (m) - real(rkind),intent(in) :: alpha ! scaling parameter (m-1) - real(rkind),intent(in) :: theta_res ! residual volumetric water content (-) - real(rkind),intent(in) :: theta_sat ! porosity (-) - real(rkind),intent(in) :: n ! vGn "n" parameter (-) - real(rkind),intent(in) :: m ! vGn "m" parameter (-) - real(rkind) :: volFracLiq ! volumetric liquid water content (-) - if(psi<0._rkind)then + end if +end function hydCond_liq + + +! ****************************************************************************************************************************** +! public function volFracLiq: compute the volumetric liquid water content (-) +! ****************************************************************************************************************************** +function volFracLiq(psi,alpha,theta_res,theta_sat,n,m) + ! computes the volumetric liquid water content given psi and soil hydraulic parameters theta_res, theta_sat, alpha, n, and m + implicit none + real(rkind),intent(in) :: psi ! soil water suction (m) + real(rkind),intent(in) :: alpha ! scaling parameter (m-1) + real(rkind),intent(in) :: theta_res ! residual volumetric water content (-) + real(rkind),intent(in) :: theta_sat ! porosity (-) + real(rkind),intent(in) :: n ! vGn "n" parameter (-) + real(rkind),intent(in) :: m ! vGn "m" parameter (-) + real(rkind) :: volFracLiq ! volumetric liquid water content (-) + if(psi<0._rkind)then volFracLiq = theta_res + (theta_sat - theta_res)*(1._rkind + (alpha*psi)**n)**(-m) - else + else volFracLiq = theta_sat - end if - end function volFracLiq - - - ! ****************************************************************************************************************************** - ! public function matricHead: compute the matric head (m) based on the volumetric liquid water content - ! ****************************************************************************************************************************** - function matricHead(theta,alpha,theta_res,theta_sat,n,m) - ! computes the volumetric liquid water content given psi and soil hydraulic parameters theta_res, theta_sat, alpha, n, and m - implicit none - ! dummy variables - real(rkind),intent(in) :: theta ! volumetric liquid water content (-) - real(rkind),intent(in) :: alpha ! scaling parameter (m-1) - real(rkind),intent(in) :: theta_res ! residual volumetric water content (-) - real(rkind),intent(in) :: theta_sat ! porosity (-) - real(rkind),intent(in) :: n ! vGn "n" parameter (-) - real(rkind),intent(in) :: m ! vGn "m" parameter (-) - real(rkind) :: matricHead ! matric head (m) - ! local variables - real(rkind) :: effSat ! effective saturation (-) - real(rkind),parameter :: verySmall=epsilon(1._rkind) ! a very small number (avoid effective saturation of zero) - ! compute effective saturation - effSat = max(verySmall, (theta - theta_res) / (theta_sat - theta_res)) - ! compute matric head - if (effSat < 1._rkind .and. effSat > 0._rkind)then + end if +end function volFracLiq + + +! ****************************************************************************************************************************** +! public function matricHead: compute the matric head (m) based on the volumetric liquid water content +! ****************************************************************************************************************************** +function matricHead(theta,alpha,theta_res,theta_sat,n,m) + ! computes the volumetric liquid water content given psi and soil hydraulic parameters theta_res, theta_sat, alpha, n, and m + implicit none + ! dummy variables + real(rkind),intent(in) :: theta ! volumetric liquid water content (-) + real(rkind),intent(in) :: alpha ! scaling parameter (m-1) + real(rkind),intent(in) :: theta_res ! residual volumetric water content (-) + real(rkind),intent(in) :: theta_sat ! porosity (-) + real(rkind),intent(in) :: n ! vGn "n" parameter (-) + real(rkind),intent(in) :: m ! vGn "m" parameter (-) + real(rkind) :: matricHead ! matric head (m) + ! local variables + real(rkind) :: effSat ! effective saturation (-) + real(rkind),parameter :: verySmall=epsilon(1._rkind) ! a very small number (avoid effective saturation of zero) + ! compute effective saturation + effSat = max(verySmall, (theta - theta_res) / (theta_sat - theta_res)) + ! compute matric head + if (effSat < 1._rkind .and. effSat > 0._rkind)then matricHead = (1._rkind/alpha)*( effSat**(-1._rkind/m) - 1._rkind)**(1._rkind/n) - else + else matricHead = 0._rkind - end if - end function matricHead - - - ! ****************************************************************************************************************************** - ! public function dTheta_dPsi: compute the derivative of the soil water characteristic (m-1) - ! ****************************************************************************************************************************** - function dTheta_dPsi(psi,alpha,theta_res,theta_sat,n,m) - implicit none - real(rkind),intent(in) :: psi ! soil water suction (m) - real(rkind),intent(in) :: alpha ! scaling parameter (m-1) - real(rkind),intent(in) :: theta_res ! residual volumetric water content (-) - real(rkind),intent(in) :: theta_sat ! porosity (-) - real(rkind),intent(in) :: n ! vGn "n" parameter (-) - real(rkind),intent(in) :: m ! vGn "m" parameter (-) - real(rkind) :: dTheta_dPsi ! derivative of the soil water characteristic (m-1) - if(psi<=0._rkind)then + end if +end function matricHead + + +! ****************************************************************************************************************************** +! public function dTheta_dPsi: compute the derivative of the soil water characteristic (m-1) +! ****************************************************************************************************************************** +function dTheta_dPsi(psi,alpha,theta_res,theta_sat,n,m) + implicit none + real(rkind),intent(in) :: psi ! soil water suction (m) + real(rkind),intent(in) :: alpha ! scaling parameter (m-1) + real(rkind),intent(in) :: theta_res ! residual volumetric water content (-) + real(rkind),intent(in) :: theta_sat ! porosity (-) + real(rkind),intent(in) :: n ! vGn "n" parameter (-) + real(rkind),intent(in) :: m ! vGn "m" parameter (-) + real(rkind) :: dTheta_dPsi ! derivative of the soil water characteristic (m-1) + if(psi<=0._rkind)then dTheta_dPsi = (theta_sat-theta_res) * & - (-m*(1._rkind + (psi*alpha)**n)**(-m-1._rkind)) * n*(psi*alpha)**(n-1._rkind) * alpha + (-m*(1._rkind + (psi*alpha)**n)**(-m-1._rkind)) * n*(psi*alpha)**(n-1._rkind) * alpha if(abs(dTheta_dPsi) < epsilon(psi)) dTheta_dPsi = epsilon(psi) - else + else dTheta_dPsi = epsilon(psi) - end if - end function dTheta_dPsi - - - ! ****************************************************************************************************************************** - ! public function dPsi_dTheta: compute the derivative of the soil water characteristic (m-1) - ! ****************************************************************************************************************************** - function dPsi_dTheta(volFracLiq,alpha,theta_res,theta_sat,n,m) - implicit none - ! dummies - real(rkind),intent(in) :: volFracLiq ! volumetric liquid water content (-) - real(rkind),intent(in) :: alpha ! scaling parameter (m-1) - real(rkind),intent(in) :: theta_res ! residual volumetric water content (-) - real(rkind),intent(in) :: theta_sat ! porosity (-) - real(rkind),intent(in) :: n ! vGn "n" parameter (-) - real(rkind),intent(in) :: m ! vGn "m" parameter (-) - real(rkind) :: dPsi_dTheta ! derivative of the soil water characteristic (m) - ! locals - real(rkind) :: y1,d1 ! 1st function and derivative - real(rkind) :: y2,d2 ! 2nd function and derivative - real(rkind) :: theta_e ! effective soil moisture - ! check if less than saturation - if(volFracLiq < theta_sat)then + end if +end function dTheta_dPsi + + +! ****************************************************************************************************************************** +! public function dPsi_dTheta: compute the derivative of the soil water characteristic (m-1) +! ****************************************************************************************************************************** +function dPsi_dTheta(volFracLiq,alpha,theta_res,theta_sat,n,m) + implicit none + ! dummies + real(rkind),intent(in) :: volFracLiq ! volumetric liquid water content (-) + real(rkind),intent(in) :: alpha ! scaling parameter (m-1) + real(rkind),intent(in) :: theta_res ! residual volumetric water content (-) + real(rkind),intent(in) :: theta_sat ! porosity (-) + real(rkind),intent(in) :: n ! vGn "n" parameter (-) + real(rkind),intent(in) :: m ! vGn "m" parameter (-) + real(rkind) :: dPsi_dTheta ! derivative of the soil water characteristic (m) + ! locals + real(rkind) :: y1,d1 ! 1st function and derivative + real(rkind) :: y2,d2 ! 2nd function and derivative + real(rkind) :: theta_e ! effective soil moisture + ! check if less than saturation + if(volFracLiq < theta_sat)then ! compute effective water content theta_e = max(0.001,(volFracLiq - theta_res) / (theta_sat - theta_res)) ! compute the 1st function and derivative @@ -379,262 +377,262 @@ function dPsi_dTheta(volFracLiq,alpha,theta_res,theta_sat,n,m) d2 = (1._rkind/n)*y1**(1._rkind/n - 1._rkind) ! compute the final function value dPsi_dTheta = d1*d2/alpha - else + else dPsi_dTheta = 0._rkind - end if - end function dPsi_dTheta - - - ! ****************************************************************************************************************************** - ! public function dPsi_dTheta2: compute the derivative of dPsi_dTheta (m-1) - ! ****************************************************************************************************************************** - function dPsi_dTheta2(volFracLiq,alpha,theta_res,theta_sat,n,m,lTangent) - implicit none - ! dummies - real(rkind),intent(in) :: volFracLiq ! volumetric liquid water content (-) - real(rkind),intent(in) :: alpha ! scaling parameter (m-1) - real(rkind),intent(in) :: theta_res ! residual volumetric water content (-) - real(rkind),intent(in) :: theta_sat ! porosity (-) - real(rkind),intent(in) :: n ! vGn "n" parameter (-) - real(rkind),intent(in) :: m ! vGn "m" parameter (-) - logical(lgt),intent(in) :: lTangent ! method used to compute derivative (.true. = analytical) - real(rkind) :: dPsi_dTheta2 ! derivative of the soil water characteristic (m) - ! locals for analytical derivatives - real(rkind) :: xx ! temporary variable - real(rkind) :: y1,d1 ! 1st function and derivative - real(rkind) :: y2,d2 ! 2nd function and derivative - real(rkind) :: theta_e ! effective soil moisture - ! locals for numerical derivative - real(rkind) :: func0,func1 ! function evaluations - ! check if less than saturation - if(volFracLiq < theta_sat)then + end if +end function dPsi_dTheta + + +! ****************************************************************************************************************************** +! public function dPsi_dTheta2: compute the derivative of dPsi_dTheta (m-1) +! ****************************************************************************************************************************** +function dPsi_dTheta2(volFracLiq,alpha,theta_res,theta_sat,n,m,lTangent) + implicit none + ! dummies + real(rkind),intent(in) :: volFracLiq ! volumetric liquid water content (-) + real(rkind),intent(in) :: alpha ! scaling parameter (m-1) + real(rkind),intent(in) :: theta_res ! residual volumetric water content (-) + real(rkind),intent(in) :: theta_sat ! porosity (-) + real(rkind),intent(in) :: n ! vGn "n" parameter (-) + real(rkind),intent(in) :: m ! vGn "m" parameter (-) + logical(lgt),intent(in) :: lTangent ! method used to compute derivative (.true. = analytical) + real(rkind) :: dPsi_dTheta2 ! derivative of the soil water characteristic (m) + ! locals for analytical derivatives + real(rkind) :: xx ! temporary variable + real(rkind) :: y1,d1 ! 1st function and derivative + real(rkind) :: y2,d2 ! 2nd function and derivative + real(rkind) :: theta_e ! effective soil moisture + ! locals for numerical derivative + real(rkind) :: func0,func1 ! function evaluations + ! check if less than saturation + if(volFracLiq < theta_sat)then ! ***** compute analytical derivatives if(lTangent)then - ! compute the effective saturation - theta_e = (volFracLiq - theta_res) / (theta_sat - theta_res) - ! get the first function and derivative - y1 = (-1._rkind/m)*theta_e**(-1._rkind/m - 1._rkind) / (theta_sat - theta_res) - d1 = ( (m + 1._rkind) / (m**2._rkind * (theta_sat - theta_res)**2._rkind) ) * theta_e**(-1._rkind/m - 2._rkind) - ! get the second function and derivative - xx = theta_e**(-1._rkind/m) - 1._rkind - y2 = (1._rkind/n)*xx**(1._rkind/n - 1._rkind) - d2 = ( -(1._rkind - n)/((theta_sat - theta_res)*m*n**2._rkind) ) * xx**(1._rkind/n - 2._rkind) * theta_e**(-1._rkind/m - 1._rkind) - ! return the derivative - dPsi_dTheta2 = (d1*y2 + y1*d2)/alpha + ! compute the effective saturation + theta_e = (volFracLiq - theta_res) / (theta_sat - theta_res) + ! get the first function and derivative + y1 = (-1._rkind/m)*theta_e**(-1._rkind/m - 1._rkind) / (theta_sat - theta_res) + d1 = ( (m + 1._rkind) / (m**2._rkind * (theta_sat - theta_res)**2._rkind) ) * theta_e**(-1._rkind/m - 2._rkind) + ! get the second function and derivative + xx = theta_e**(-1._rkind/m) - 1._rkind + y2 = (1._rkind/n)*xx**(1._rkind/n - 1._rkind) + d2 = ( -(1._rkind - n)/((theta_sat - theta_res)*m*n**2._rkind) ) * xx**(1._rkind/n - 2._rkind) * theta_e**(-1._rkind/m - 1._rkind) + ! return the derivative + dPsi_dTheta2 = (d1*y2 + y1*d2)/alpha ! ***** compute numerical derivatives else - func0 = dPsi_dTheta(volFracLiq, alpha,theta_res,theta_sat,n,m) - func1 = dPsi_dTheta(volFracLiq+dx,alpha,theta_res,theta_sat,n,m) - dPsi_dTheta2 = (func1 - func0)/dx + func0 = dPsi_dTheta(volFracLiq, alpha,theta_res,theta_sat,n,m) + func1 = dPsi_dTheta(volFracLiq+dx,alpha,theta_res,theta_sat,n,m) + dPsi_dTheta2 = (func1 - func0)/dx end if - ! (case where volumetric liquid water content exceeds porosity) - else + ! (case where volumetric liquid water content exceeds porosity) + else dPsi_dTheta2 = 0._rkind - end if - end function dPsi_dTheta2 - - - ! ****************************************************************************************************************************** - ! public function dHydCond_dPsi: compute the derivative in hydraulic conductivity w.r.t. matric head (s-1) - ! ****************************************************************************************************************************** - function dHydCond_dPsi(psi,k_sat,alpha,n,m,lTangent) - ! computes the derivative in hydraulic conductivity w.r.t matric head, - ! given psi and soil hydraulic parameters k_sat, alpha, n, and m - implicit none - ! dummies - real(rkind),intent(in) :: psi ! soil water suction (m) - real(rkind),intent(in) :: k_sat ! saturated hydraulic conductivity (m s-1) - real(rkind),intent(in) :: alpha ! scaling parameter (m-1) - real(rkind),intent(in) :: n ! vGn "n" parameter (-) - real(rkind),intent(in) :: m ! vGn "m" parameter (-) - logical(lgt),intent(in) :: lTangent ! method used to compute derivative (.true. = analytical) - real(rkind) :: dHydCond_dPsi ! derivative in hydraulic conductivity w.r.t. matric head (s-1) - ! locals for analytical derivatives - real(rkind) :: f_x1 ! f(x) for part of the numerator - real(rkind) :: f_x2 ! f(x) for part of the numerator - real(rkind) :: f_nm ! f(x) for the numerator - real(rkind) :: f_dm ! f(x) for the denominator - real(rkind) :: d_x1 ! df(x)/dpsi for part of the numerator - real(rkind) :: d_x2 ! df(x)/dpsi for part of the numerator - real(rkind) :: d_nm ! df(x)/dpsi for the numerator - real(rkind) :: d_dm ! df(x)/dpsi for the denominator - ! locals for numerical derivatives - real(rkind) :: hydCond0 ! hydraulic condictivity value for base case - real(rkind) :: hydCond1 ! hydraulic condictivity value for perturbed case - ! derivative is zero if saturated - if(psi<0._rkind)then + end if +end function dPsi_dTheta2 + + +! ****************************************************************************************************************************** +! public function dHydCond_dPsi: compute the derivative in hydraulic conductivity w.r.t. matric head (s-1) +! ****************************************************************************************************************************** +function dHydCond_dPsi(psi,k_sat,alpha,n,m,lTangent) + ! computes the derivative in hydraulic conductivity w.r.t matric head, + ! given psi and soil hydraulic parameters k_sat, alpha, n, and m + implicit none + ! dummies + real(rkind),intent(in) :: psi ! soil water suction (m) + real(rkind),intent(in) :: k_sat ! saturated hydraulic conductivity (m s-1) + real(rkind),intent(in) :: alpha ! scaling parameter (m-1) + real(rkind),intent(in) :: n ! vGn "n" parameter (-) + real(rkind),intent(in) :: m ! vGn "m" parameter (-) + logical(lgt),intent(in) :: lTangent ! method used to compute derivative (.true. = analytical) + real(rkind) :: dHydCond_dPsi ! derivative in hydraulic conductivity w.r.t. matric head (s-1) + ! locals for analytical derivatives + real(rkind) :: f_x1 ! f(x) for part of the numerator + real(rkind) :: f_x2 ! f(x) for part of the numerator + real(rkind) :: f_nm ! f(x) for the numerator + real(rkind) :: f_dm ! f(x) for the denominator + real(rkind) :: d_x1 ! df(x)/dpsi for part of the numerator + real(rkind) :: d_x2 ! df(x)/dpsi for part of the numerator + real(rkind) :: d_nm ! df(x)/dpsi for the numerator + real(rkind) :: d_dm ! df(x)/dpsi for the denominator + ! locals for numerical derivatives + real(rkind) :: hydCond0 ! hydraulic condictivity value for base case + real(rkind) :: hydCond1 ! hydraulic condictivity value for perturbed case + ! derivative is zero if saturated + if(psi<0._rkind)then ! ***** compute analytical derivatives if(lTangent)then - ! compute the derivative for the numerator - f_x1 = (psi*alpha)**(n - 1._rkind) - f_x2 = (1._rkind + (psi*alpha)**n)**(-m) - d_x1 = alpha * (n - 1._rkind)*(psi*alpha)**(n - 2._rkind) - d_x2 = alpha * n*(psi*alpha)**(n - 1._rkind) * (-m)*(1._rkind + (psi*alpha)**n)**(-m - 1._rkind) - f_nm = (1._rkind - f_x1*f_x2)**2._rkind - d_nm = (-d_x1*f_x2 - f_x1*d_x2) * 2._rkind*(1._rkind - f_x1*f_x2) - ! compute the derivative for the denominator - f_dm = (1._rkind + (psi*alpha)**n)**(m/2._rkind) - d_dm = alpha * n*(psi*alpha)**(n - 1._rkind) * (m/2._rkind)*(1._rkind + (psi*alpha)**n)**(m/2._rkind - 1._rkind) - ! and combine - dHydCond_dPsi = k_sat*(d_nm*f_dm - d_dm*f_nm) / (f_dm**2._rkind) + ! compute the derivative for the numerator + f_x1 = (psi*alpha)**(n - 1._rkind) + f_x2 = (1._rkind + (psi*alpha)**n)**(-m) + d_x1 = alpha * (n - 1._rkind)*(psi*alpha)**(n - 2._rkind) + d_x2 = alpha * n*(psi*alpha)**(n - 1._rkind) * (-m)*(1._rkind + (psi*alpha)**n)**(-m - 1._rkind) + f_nm = (1._rkind - f_x1*f_x2)**2._rkind + d_nm = (-d_x1*f_x2 - f_x1*d_x2) * 2._rkind*(1._rkind - f_x1*f_x2) + ! compute the derivative for the denominator + f_dm = (1._rkind + (psi*alpha)**n)**(m/2._rkind) + d_dm = alpha * n*(psi*alpha)**(n - 1._rkind) * (m/2._rkind)*(1._rkind + (psi*alpha)**n)**(m/2._rkind - 1._rkind) + ! and combine + dHydCond_dPsi = k_sat*(d_nm*f_dm - d_dm*f_nm) / (f_dm**2._rkind) else - ! ***** compute numerical derivatives - hydcond0 = hydCond_psi(psi, k_sat,alpha,n,m) - hydcond1 = hydCond_psi(psi+dx,k_sat,alpha,n,m) - dHydCond_dPsi = (hydcond1 - hydcond0)/dx + ! ***** compute numerical derivatives + hydcond0 = hydCond_psi(psi, k_sat,alpha,n,m) + hydcond1 = hydCond_psi(psi+dx,k_sat,alpha,n,m) + dHydCond_dPsi = (hydcond1 - hydcond0)/dx end if - else + else dHydCond_dPsi = 0._rkind - end if - end function dHydCond_dPsi - - - ! ****************************************************************************************************************************** - ! public function dHydCond_dLiq: compute the derivative in hydraulic conductivity w.r.t. volumetric liquid water content (m s-1) - ! ****************************************************************************************************************************** - ! computes the derivative in hydraulic conductivity w.r.t the volumetric fraction of liquid water, - ! given volFracLiq and soil hydraulic parameters k_sat, theta_sat, theta_res, and m - ! ****************************************************************************************************************************** - function dHydCond_dLiq(volFracLiq,k_sat,theta_res,theta_sat,m,lTangent) - implicit none - ! dummies - real(rkind),intent(in) :: volFracLiq ! volumetric fraction of liquid water (-) - real(rkind),intent(in) :: k_sat ! saturated hydraulic conductivity (m s-1) - real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) - real(rkind),intent(in) :: theta_sat ! soil porosity (-) - real(rkind),intent(in) :: m ! vGn "m" parameter (-) - logical(lgt),intent(in) :: lTangent ! method used to compute derivative (.true. = analytical) - real(rkind) :: dHydCond_dLiq ! derivative in hydraulic conductivity w.r.t. matric head (s-1) - ! locals for analytical derivatives - real(rkind) :: theta_e ! effective soil moisture - real(rkind) :: f1 ! f(x) for the first function - real(rkind) :: d1 ! df(x)/dLiq for the first function - real(rkind) :: x1,x2 ! f(x) for different parts of the second function - real(rkind) :: p1,p2,p3 ! df(x)/dLiq for different parts of the second function - real(rkind) :: f2 ! f(x) for the second function - real(rkind) :: d2 ! df(x)/dLiq for the second function - ! locals for numerical derivatives - real(rkind) :: hydCond0 ! hydraulic condictivity value for base case - real(rkind) :: hydCond1 ! hydraulic condictivity value for perturbed case - ! derivative is zero if super-saturated - if(volFracLiq < theta_sat)then + end if +end function dHydCond_dPsi + + +! ****************************************************************************************************************************** +! public function dHydCond_dLiq: compute the derivative in hydraulic conductivity w.r.t. volumetric liquid water content (m s-1) +! ****************************************************************************************************************************** +! computes the derivative in hydraulic conductivity w.r.t the volumetric fraction of liquid water, +! given volFracLiq and soil hydraulic parameters k_sat, theta_sat, theta_res, and m +! ****************************************************************************************************************************** +function dHydCond_dLiq(volFracLiq,k_sat,theta_res,theta_sat,m,lTangent) + implicit none + ! dummies + real(rkind),intent(in) :: volFracLiq ! volumetric fraction of liquid water (-) + real(rkind),intent(in) :: k_sat ! saturated hydraulic conductivity (m s-1) + real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) + real(rkind),intent(in) :: theta_sat ! soil porosity (-) + real(rkind),intent(in) :: m ! vGn "m" parameter (-) + logical(lgt),intent(in) :: lTangent ! method used to compute derivative (.true. = analytical) + real(rkind) :: dHydCond_dLiq ! derivative in hydraulic conductivity w.r.t. matric head (s-1) + ! locals for analytical derivatives + real(rkind) :: theta_e ! effective soil moisture + real(rkind) :: f1 ! f(x) for the first function + real(rkind) :: d1 ! df(x)/dLiq for the first function + real(rkind) :: x1,x2 ! f(x) for different parts of the second function + real(rkind) :: p1,p2,p3 ! df(x)/dLiq for different parts of the second function + real(rkind) :: f2 ! f(x) for the second function + real(rkind) :: d2 ! df(x)/dLiq for the second function + ! locals for numerical derivatives + real(rkind) :: hydCond0 ! hydraulic condictivity value for base case + real(rkind) :: hydCond1 ! hydraulic condictivity value for perturbed case + ! derivative is zero if super-saturated + if(volFracLiq < theta_sat)then ! ***** compute analytical derivatives if(lTangent)then - ! compute the effective saturation - theta_e = (volFracLiq - theta_res) / (theta_sat - theta_res) - ! compute the function and derivative of the first fuction - f1 = k_sat*theta_e**0.5_rkind - d1 = k_sat*0.5_rkind*theta_e**(-0.5_rkind) / (theta_sat - theta_res) - ! compute the function and derivative of the second function - ! (first part) - x1 = 1._rkind - theta_e**(1._rkind/m) - p1 = (-1._rkind/m)*theta_e**(1._rkind/m - 1._rkind) / (theta_sat - theta_res) ! differentiate (1.d - theta_e**(1.d/m) - ! (second part) - x2 = x1**m - p2 = m*x1**(m - 1._rkind) - ! (final) - f2 = (1._rkind - x2)**2._rkind - p3 = -2._rkind*(1._rkind - x2) - ! (combine) - d2 = p1*p2*p3 - ! pull it all together - dHydCond_dLiq = (d1*f2 + d2*f1) + ! compute the effective saturation + theta_e = (volFracLiq - theta_res) / (theta_sat - theta_res) + ! compute the function and derivative of the first fuction + f1 = k_sat*theta_e**0.5_rkind + d1 = k_sat*0.5_rkind*theta_e**(-0.5_rkind) / (theta_sat - theta_res) + ! compute the function and derivative of the second function + ! (first part) + x1 = 1._rkind - theta_e**(1._rkind/m) + p1 = (-1._rkind/m)*theta_e**(1._rkind/m - 1._rkind) / (theta_sat - theta_res) ! differentiate (1.d - theta_e**(1.d/m) + ! (second part) + x2 = x1**m + p2 = m*x1**(m - 1._rkind) + ! (final) + f2 = (1._rkind - x2)**2._rkind + p3 = -2._rkind*(1._rkind - x2) + ! (combine) + d2 = p1*p2*p3 + ! pull it all together + dHydCond_dLiq = (d1*f2 + d2*f1) else - ! ***** compute numerical derivatives - hydcond0 = hydCond_liq(volFracLiq, k_sat,theta_res,theta_sat,m) - hydcond1 = hydCond_liq(volFracLiq+dx,k_sat,theta_res,theta_sat,m) - dHydCond_dLiq = (hydcond1 - hydcond0)/dx + ! ***** compute numerical derivatives + hydcond0 = hydCond_liq(volFracLiq, k_sat,theta_res,theta_sat,m) + hydcond1 = hydCond_liq(volFracLiq+dx,k_sat,theta_res,theta_sat,m) + dHydCond_dLiq = (hydcond1 - hydcond0)/dx end if - else + else dHydCond_dLiq = 0._rkind - end if - end function dHydCond_dLiq - - - ! ****************************************************************************************************************************** - ! public function RH_soilair: compute relative humidity of air in soil pore space - ! ****************************************************************************************************************************** - function RH_soilair(matpot,Tk) - implicit none - real(rkind),intent(in) :: matpot ! soil water suction -- matric potential (m) - real(rkind),intent(in) :: Tk ! temperature (K) - real(rkind) :: RH_soilair ! relative humidity of air in soil pore space - ! compute relative humidity (UNITS NOTE: Pa = kg m-1 s-2, so R_wv units = m2 s-2 K-1) - RH_soilair = exp( (gravity*matpot) / (R_wv*Tk) ) - end function RH_soilair - - - ! ****************************************************************************************************************************** - ! public function crit_soilT: compute the critical temperature above which all water is unfrozen - ! ****************************************************************************************************************************** - function crit_soilT(psi) - implicit none - real(rkind),intent(in) :: psi ! matric head (m) - real(rkind) :: crit_soilT ! critical soil temperature (K) - crit_soilT = Tfreeze + min(psi,0._rkind)*gravity*Tfreeze/LH_fus - end function crit_soilT - - - ! ****************************************************************************************************************************** - ! public function dTheta_dTk: differentiate the freezing curve w.r.t. temperature - ! ****************************************************************************************************************************** - function dTheta_dTk(Tk,theta_res,theta_sat,alpha,n,m) - implicit none - real(rkind),intent(in) :: Tk ! temperature (K) - real(rkind),intent(in) :: theta_res ! residual liquid water content (-) - real(rkind),intent(in) :: theta_sat ! porosity (-) - real(rkind),intent(in) :: alpha ! vGn scaling parameter (m-1) - real(rkind),intent(in) :: n ! vGn "n" parameter (-) - real(rkind),intent(in) :: m ! vGn "m" parameter (-) - real(rkind) :: dTheta_dTk ! derivative of the freezing curve w.r.t. temperature (K-1) - ! local variables - real(rkind) :: kappa ! constant (m K-1) - real(rkind) :: xtemp ! alpha*kappa*(Tk-Tfreeze) -- dimensionless variable (used more than once) - ! compute kappa (m K-1) - kappa = LH_fus/(gravity*Tfreeze) ! NOTE: J = kg m2 s-2 - ! define a tempory variable that is used more than once (-) - xtemp = alpha*kappa*(Tk-Tfreeze) - ! differentiate the freezing curve w.r.t. temperature -- making use of the chain rule - dTheta_dTk = (alpha*kappa) * n*xtemp**(n - 1._rkind) * (-m)*(1._rkind + xtemp**n)**(-m - 1._rkind) * (theta_sat - theta_res) - end function dTheta_dTk - - - ! ****************************************************************************************************************************** - ! public function gammp: compute cumulative probability using the Gamma distribution - ! ****************************************************************************************************************************** - FUNCTION gammp(a,x) - IMPLICIT NONE - real(rkind), INTENT(IN) :: a,x - real(rkind) :: gammp - if (x ITMAX) stop 'a too large, ITMAX too small in gcf' - if (present(gln)) then + end do + if (i > ITMAX) stop 'a too large, ITMAX too small in gcf' + if (present(gln)) then gln=gammln(a) gcf=exp(-x+a*log(x)-gln)*h - else + else gcf=exp(-x+a*log(x)-gammln(a))*h - end if - END FUNCTION gcf - - - ! ****************************************************************************************************************************** - ! private function gser: series development of the incomplete Gamma function - ! ****************************************************************************************************************************** - FUNCTION gser(a,x,gln) - IMPLICIT NONE - real(rkind), INTENT(IN) :: a,x - real(rkind), OPTIONAL, INTENT(OUT) :: gln - real(rkind) :: gser - INTEGER(I4B), PARAMETER :: ITMAX=100 - real(rkind), PARAMETER :: EPS=epsilon(x) - INTEGER(I4B) :: n - real(rkind) :: ap,del,summ - if (x == 0.0) then + end if +END FUNCTION gcf + + +! ****************************************************************************************************************************** +! private function gser: series development of the incomplete Gamma function +! ****************************************************************************************************************************** +FUNCTION gser(a,x,gln) + IMPLICIT NONE + real(rkind), INTENT(IN) :: a,x + real(rkind), OPTIONAL, INTENT(OUT) :: gln + real(rkind) :: gser + INTEGER(I4B), PARAMETER :: ITMAX=100 + real(rkind), PARAMETER :: EPS=epsilon(x) + INTEGER(I4B) :: n + real(rkind) :: ap,del,summ + if (x == 0.0) then gser=0.0 RETURN - end if - ap=a - summ=1.0_rkind/a - del=summ - do n=1,ITMAX + end if + ap=a + summ=1.0_rkind/a + del=summ + do n=1,ITMAX ap=ap+1.0_rkind del=del*x/ap summ=summ+del if (abs(del) < abs(summ)*EPS) exit - end do - if (n > ITMAX) stop 'a too large, ITMAX too small in gser' - if (present(gln)) then + end do + if (n > ITMAX) stop 'a too large, ITMAX too small in gser' + if (present(gln)) then gln=gammln(a) gser=summ*exp(-x+a*log(x)-gln) - else + else gser=summ*exp(-x+a*log(x)-gammln(a)) - end if - END FUNCTION gser - - - ! ****************************************************************************************************************************** - ! private function gammln: gamma function - ! ****************************************************************************************************************************** - FUNCTION gammln(xx) - USE nr_utility_module,only:arth ! use to build vectors with regular increments - IMPLICIT NONE - real(rkind), INTENT(IN) :: xx - real(rkind) :: gammln - real(rkind) :: tmp,x - real(rkind) :: stp = 2.5066282746310005_rkind - real(rkind), DIMENSION(6) :: coef = (/76.18009172947146_rkind,& - -86.50532032941677_rkind,24.01409824083091_rkind,& - -1.231739572450155_rkind,0.1208650973866179e-2_rkind,& - -0.5395239384953e-5_rkind/) - if(xx <= 0._rkind) stop 'xx > 0 in gammln' - x=xx - tmp=x+5.5_rkind - tmp=(x+0.5_rkind)*log(tmp)-tmp - gammln=tmp+log(stp*(1.000000000190015_rkind+& + end if +END FUNCTION gser + + +! ****************************************************************************************************************************** +! private function gammln: gamma function +! ****************************************************************************************************************************** +FUNCTION gammln(xx) + USE nr_utility_module,only:arth ! use to build vectors with regular increments + IMPLICIT NONE + real(rkind), INTENT(IN) :: xx + real(rkind) :: gammln + real(rkind) :: tmp,x + real(rkind) :: stp = 2.5066282746310005_rkind + real(rkind), DIMENSION(6) :: coef = (/76.18009172947146_rkind,& + -86.50532032941677_rkind,24.01409824083091_rkind,& + -1.231739572450155_rkind,0.1208650973866179e-2_rkind,& + -0.5395239384953e-5_rkind/) + if(xx <= 0._rkind) stop 'xx > 0 in gammln' + x=xx + tmp=x+5.5_rkind + tmp=(x+0.5_rkind)*log(tmp)-tmp + gammln=tmp+log(stp*(1.000000000190015_rkind+& sum(coef(:)/arth(x+1.0_rkind,1.0_rkind,size(coef))))/x) - END FUNCTION gammln +END FUNCTION gammln end module soil_utils_module From 7b75f382ab1d70641944aa3da5b1cce548c6ec64 Mon Sep 17 00:00:00 2001 From: Kyle Date: Wed, 28 Sep 2022 23:05:25 +0000 Subject: [PATCH 0386/1472] indentation changes to updatState.f90 --- build/source/engine/updatState.f90 | 254 ++++++++++++++--------------- 1 file changed, 124 insertions(+), 130 deletions(-) diff --git a/build/source/engine/updatState.f90 b/build/source/engine/updatState.f90 index f1a210b89..29c82f15b 100644 --- a/build/source/engine/updatState.f90 +++ b/build/source/engine/updatState.f90 @@ -35,133 +35,127 @@ module updatState_module contains - ! ************************************************************************************************************* - ! public subroutine updateSnow: compute phase change impacts on volumetric liquid water and ice (veg or soil) - ! ************************************************************************************************************* - subroutine updateSnow(& - ! input - mLayerTemp ,& ! intent(in): temperature (K) - mLayerTheta ,& ! intent(in): volume fraction of total water (-) - snowfrz_scale ,& ! intent(in): scaling parameter for the snow freezing curve (K-1) - ! output - mLayerVolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) - mLayerVolFracIce ,& ! intent(out): volumetric fraction of ice (-) - fLiq ,& ! intent(out): fraction of liquid water (-) - err,message) ! intent(out): error control - ! utility routines - USE snow_utils_module,only:fracliquid ! compute volumetric fraction of liquid water - implicit none - ! input variables - real(rkind),intent(in) :: mLayerTemp ! temperature (K) - real(rkind),intent(in) :: mLayerTheta ! volume fraction of total water (-) - real(rkind),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) - ! output variables - real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) - real(rkind),intent(out) :: fLiq ! fraction of liquid water (-) - ! error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - - ! initialize error control - err=0; message="updateSnow/" - - ! compute the volumetric fraction of liquid water and ice (-) - fLiq = fracliquid(mLayerTemp,snowfrz_scale) - mLayerVolFracLiq = fLiq*mLayerTheta - mLayerVolFracIce = (1._rkind - fLiq)*mLayerTheta*(iden_water/iden_ice) - !print*, 'mLayerTheta - (mLayerVolFracIce*(iden_ice/iden_water) + mLayerVolFracLiq) = ', mLayerTheta - (mLayerVolFracIce*(iden_ice/iden_water) + mLayerVolFracLiq) - !write(*,'(a,1x,4(f20.10,1x))') 'in updateSnow: fLiq, mLayerTheta, mLayerVolFracIce = ', & - ! fLiq, mLayerTheta, mLayerVolFracIce - !pause - - end subroutine updateSnow - - ! ************************************************************************************************************* - ! public subroutine updateSoil: compute phase change impacts on matric head and volumetric liquid water and ice - ! ************************************************************************************************************* - subroutine updateSoil(& - ! input - mLayerTemp ,& ! intent(in): temperature vector (K) - mLayerMatricHead ,& ! intent(in): matric head (m) - vGn_alpha ,& ! intent(in): van Genutchen "alpha" parameter - vGn_n ,& ! intent(in): van Genutchen "n" parameter - theta_sat ,& ! intent(in): soil porosity (-) - theta_res ,& ! intent(in): soil residual volumetric water content (-) - vGn_m ,& ! intent(in): van Genutchen "m" parameter (-) - ! output - mLayerVolFracWat ,& ! intent(out): volumetric fraction of total water (-) - mLayerVolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) - mLayerVolFracIce ,& ! intent(out): volumetric fraction of ice (-) - err,message) ! intent(out): error control - ! utility routines - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water based on matric head - USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric liquid water content - implicit none - ! input variables - real(rkind),intent(in) :: mLayerTemp ! estimate of temperature (K) - real(rkind),intent(in) :: mLayerMatricHead ! matric head (m) - real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter - real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter - real(rkind),intent(in) :: theta_sat ! soil porosity (-) - real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) - real(rkind),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) - ! output variables - real(rkind),intent(out) :: mLayerVolFracWat ! fractional volume of total water (-) - real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! define local variables - real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) - real(rkind) :: xConst ! constant in the freezing curve function (m K-1) - real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) - real(rkind),parameter :: tinyVal=epsilon(1._rkind) ! used in balance check - ! initialize error control - err=0; message="updateSoil/" - - ! compute fractional **volume** of total water (liquid plus ice) - mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if(mLayerVolFracWat > (theta_sat + tinyVal)) then - err=20 - message=trim(message)//'volume of liquid and ice (mLayerVolFracWat) exceeds porosity' - print*, 'mLayerVolFracWat = ', mLayerVolFracWat - print*, 'theta_sat (porosity) = ', theta_sat - print*, 'mLayerMatricHead = ', mLayerMatricHead - print*, 'theta_res = ', theta_res - print*, 'vGn_alpha = ', vGn_alpha - print*, 'vGn_n = ', vGn_n - print*, 'vGn_m = ', vGn_m - return - end if - - ! compute the critical soil temperature where all water is unfrozen (K) - ! (eq 17 in Dall'Amico 2011) - TcSoil = Tfreeze + min(mLayerMatricHead,0._rkind)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) - - ! *** compute volumetric fraction of liquid water and ice for partially frozen soil - if(mLayerTemp < TcSoil)then ! (check if soil temperature is less than the critical temperature) - - ! - volumetric liquid water content (-) - ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice - ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations - xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) - mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution - mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - - ! - volumetric ice content (-) - mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq - - ! *** compute volumetric fraction of liquid water and ice for unfrozen soil - else - - ! all water is unfrozen - mLayerPsiLiq = mLayerMatricHead - mLayerVolFracLiq = mLayerVolFracWat - mLayerVolFracIce = 0._rkind - - end if ! (check if soil is partially frozen) - - end subroutine updateSoil - -end module updatState_module +! ************************************************************************************************************* +! public subroutine updateSnow: compute phase change impacts on volumetric liquid water and ice (veg or soil) +! ************************************************************************************************************* +subroutine updateSnow(& + ! input + mLayerTemp ,& ! intent(in): temperature (K) + mLayerTheta ,& ! intent(in): volume fraction of total water (-) + snowfrz_scale ,& ! intent(in): scaling parameter for the snow freezing curve (K-1) + ! output + mLayerVolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) + mLayerVolFracIce ,& ! intent(out): volumetric fraction of ice (-) + fLiq ,& ! intent(out): fraction of liquid water (-) + err,message) ! intent(out): error control + ! utility routines + USE snow_utils_module,only:fracliquid ! compute volumetric fraction of liquid water + implicit none + ! input variables + real(rkind),intent(in) :: mLayerTemp ! temperature (K) + real(rkind),intent(in) :: mLayerTheta ! volume fraction of total water (-) + real(rkind),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) + ! output variables + real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) + real(rkind),intent(out) :: fLiq ! fraction of liquid water (-) + ! error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! initialize error control + err=0; message="updateSnow/" + + ! compute the volumetric fraction of liquid water and ice (-) + fLiq = fracliquid(mLayerTemp,snowfrz_scale) + mLayerVolFracLiq = fLiq*mLayerTheta + mLayerVolFracIce = (1._rkind - fLiq)*mLayerTheta*(iden_water/iden_ice) +end subroutine updateSnow + +! ************************************************************************************************************* +! public subroutine updateSoil: compute phase change impacts on matric head and volumetric liquid water and ice +! ************************************************************************************************************* +subroutine updateSoil(& + ! input + mLayerTemp ,& ! intent(in): temperature vector (K) + mLayerMatricHead ,& ! intent(in): matric head (m) + vGn_alpha ,& ! intent(in): van Genutchen "alpha" parameter + vGn_n ,& ! intent(in): van Genutchen "n" parameter + theta_sat ,& ! intent(in): soil porosity (-) + theta_res ,& ! intent(in): soil residual volumetric water content (-) + vGn_m ,& ! intent(in): van Genutchen "m" parameter (-) + ! output + mLayerVolFracWat ,& ! intent(out): volumetric fraction of total water (-) + mLayerVolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) + mLayerVolFracIce ,& ! intent(out): volumetric fraction of ice (-) + err,message) ! intent(out): error control + ! utility routines + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water based on matric head + USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric liquid water content + implicit none + ! input variables + real(rkind),intent(in) :: mLayerTemp ! estimate of temperature (K) + real(rkind),intent(in) :: mLayerMatricHead ! matric head (m) + real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter + real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter + real(rkind),intent(in) :: theta_sat ! soil porosity (-) + real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) + real(rkind),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) + ! output variables + real(rkind),intent(out) :: mLayerVolFracWat ! fractional volume of total water (-) + real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! define local variables + real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) + real(rkind) :: xConst ! constant in the freezing curve function (m K-1) + real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) + real(rkind),parameter :: tinyVal=epsilon(1._rkind) ! used in balance check + ! initialize error control + err=0; message="updateSoil/" + + ! compute fractional **volume** of total water (liquid plus ice) + mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + if(mLayerVolFracWat > (theta_sat + tinyVal)) then + err=20 + message=trim(message)//'volume of liquid and ice (mLayerVolFracWat) exceeds porosity' + print*, 'mLayerVolFracWat = ', mLayerVolFracWat + print*, 'theta_sat (porosity) = ', theta_sat + print*, 'mLayerMatricHead = ', mLayerMatricHead + print*, 'theta_res = ', theta_res + print*, 'vGn_alpha = ', vGn_alpha + print*, 'vGn_n = ', vGn_n + print*, 'vGn_m = ', vGn_m + return + end if + + ! compute the critical soil temperature where all water is unfrozen (K) + ! (eq 17 in Dall'Amico 2011) + TcSoil = Tfreeze + min(mLayerMatricHead,0._rkind)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) + + ! *** compute volumetric fraction of liquid water and ice for partially frozen soil + if(mLayerTemp < TcSoil)then ! (check if soil temperature is less than the critical temperature) + + ! - volumetric liquid water content (-) + ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice + ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations + xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) + mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution + mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + + ! - volumetric ice content (-) + mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq + + ! *** compute volumetric fraction of liquid water and ice for unfrozen soil + else + + ! all water is unfrozen + mLayerPsiLiq = mLayerMatricHead + mLayerVolFracLiq = mLayerVolFracWat + mLayerVolFracIce = 0._rkind + + end if ! (check if soil is partially frozen) + +end subroutine updateSoil + +end module updatState_module \ No newline at end of file From 7142ce60b92c468f1088bad481c0ed0526281efc Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 29 Sep 2022 22:33:39 +0000 Subject: [PATCH 0387/1472] removed here statement --- build/source/engine/coupled_em.f90 | 1 - build/source/engine/snow_utils.f90 | 114 ++++++++++++++--------------- 2 files changed, 57 insertions(+), 58 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 129653a79..d8c94eb40 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -735,7 +735,6 @@ subroutine coupled_em(& ixSolution, & ! intent(out): solution method used in this iteration err,cmessage) ! intent(out): error code and error message - ! check for all errors (error recovery within opSplittin) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if diff --git a/build/source/engine/snow_utils.f90 b/build/source/engine/snow_utils.f90 index 8ad277d30..2008a9ada 100644 --- a/build/source/engine/snow_utils.f90 +++ b/build/source/engine/snow_utils.f90 @@ -42,73 +42,73 @@ module snow_utils_module contains - ! *********************************************************************************************************** - ! public function fracliquid: compute fraction of liquid water - ! *********************************************************************************************************** - function fracliquid(Tk,fc_param) - implicit none - real(rkind),intent(in) :: Tk ! temperature (K) - real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) - real(rkind) :: fracliquid ! fraction of liquid water (-) - ! compute fraction of liquid water (-) - fracliquid = 1._rkind / ( 1._rkind + (fc_param*( Tfreeze - min(Tk,Tfreeze) ))**2._rkind ) - end function fracliquid +! *********************************************************************************************************** +! public function fracliquid: compute fraction of liquid water +! *********************************************************************************************************** +function fracliquid(Tk,fc_param) + implicit none + real(rkind),intent(in) :: Tk ! temperature (K) + real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) + real(rkind) :: fracliquid ! fraction of liquid water (-) + ! compute fraction of liquid water (-) + fracliquid = 1._rkind / ( 1._rkind + (fc_param*( Tfreeze - min(Tk,Tfreeze) ))**2._rkind ) +end function fracliquid - ! *********************************************************************************************************** - ! public function templiquid: invert the fraction of liquid water function - ! *********************************************************************************************************** - function templiquid(fracliquid,fc_param) - implicit none - real(rkind),intent(in) :: fracliquid ! fraction of liquid water (-) - real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) - real(rkind) :: templiquid ! temperature (K) - ! compute temperature based on the fraction of liquid water (K) - templiquid = Tfreeze - ((1._rkind/fracliquid - 1._rkind)/fc_param**2._rkind)**(0.5_rkind) - end function templiquid +! *********************************************************************************************************** +! public function templiquid: invert the fraction of liquid water function +! *********************************************************************************************************** +function templiquid(fracliquid,fc_param) + implicit none + real(rkind),intent(in) :: fracliquid ! fraction of liquid water (-) + real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) + real(rkind) :: templiquid ! temperature (K) + ! compute temperature based on the fraction of liquid water (K) + templiquid = Tfreeze - ((1._rkind/fracliquid - 1._rkind)/fc_param**2._rkind)**(0.5_rkind) +end function templiquid - ! *********************************************************************************************************** - ! public function dFracLiq_dTk: differentiate the freezing curve - ! *********************************************************************************************************** - function dFracLiq_dTk(Tk,fc_param) - implicit none - ! dummies - real(rkind),intent(in) :: Tk ! temperature (K) - real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) - real(rkind) :: dFracLiq_dTk ! differentiate the freezing curve (K-1) - ! locals - real(rkind) :: Tdep ! temperature depression (K) - real(rkind) :: Tdim ! dimensionless temperature (-) - ! compute local variables (just to make things more efficient) - Tdep = Tfreeze - min(Tk,Tfreeze) - Tdim = fc_param*Tdep - ! differentiate the freezing curve w.r.t temperature - dFracLiq_dTk = (fc_param*2._rkind*Tdim) / ( ( 1._rkind + Tdim**2._rkind)**2._rkind ) - end function dFracLiq_dTk +! *********************************************************************************************************** +! public function dFracLiq_dTk: differentiate the freezing curve +! *********************************************************************************************************** +function dFracLiq_dTk(Tk,fc_param) + implicit none + ! dummies + real(rkind),intent(in) :: Tk ! temperature (K) + real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) + real(rkind) :: dFracLiq_dTk ! differentiate the freezing curve (K-1) + ! locals + real(rkind) :: Tdep ! temperature depression (K) + real(rkind) :: Tdim ! dimensionless temperature (-) + ! compute local variables (just to make things more efficient) + Tdep = Tfreeze - min(Tk,Tfreeze) + Tdim = fc_param*Tdep + ! differentiate the freezing curve w.r.t temperature + dFracLiq_dTk = (fc_param*2._rkind*Tdim) / ( ( 1._rkind + Tdim**2._rkind)**2._rkind ) +end function dFracLiq_dTk - ! *********************************************************************************************************** - ! public subroutine tcond_snow: compute thermal conductivity of snow - ! *********************************************************************************************************** - subroutine tcond_snow(BulkDenIce,thermlcond,err,message) - implicit none - real(rkind),intent(in) :: BulkDenIce ! bulk density of ice (kg m-3) - real(rkind),intent(out) :: thermlcond ! thermal conductivity of snow (W m-1 K-1) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! initialize error control - err=0; message="tcond_snow/" - ! compute thermal conductivity of snow - select case(model_decisions(iLookDECISIONS%thCondSnow)%iDecision) +! *********************************************************************************************************** +! public subroutine tcond_snow: compute thermal conductivity of snow +! *********************************************************************************************************** +subroutine tcond_snow(BulkDenIce,thermlcond,err,message) + implicit none + real(rkind),intent(in) :: BulkDenIce ! bulk density of ice (kg m-3) + real(rkind),intent(out) :: thermlcond ! thermal conductivity of snow (W m-1 K-1) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! initialize error control + err=0; message="tcond_snow/" + ! compute thermal conductivity of snow + select case(model_decisions(iLookDECISIONS%thCondSnow)%iDecision) case(Yen1965); thermlcond = 3.217d-6 * BulkDenIce**2._rkind ! Yen (1965) case(Mellor1977); thermlcond = 2.576d-6 * BulkDenIce**2._rkind + 7.4d-2 ! Mellor (1977) case(Jordan1991); thermlcond = lambda_air + (7.75d-5*BulkDenIce + 1.105d-6*(BulkDenIce**2._rkind)) & - * (lambda_ice-lambda_air) ! Jordan (1991) + * (lambda_ice-lambda_air) ! Jordan (1991) case default - err=10; message=trim(message)//"unknownOption"; return - end select - end subroutine tcond_snow + err=10; message=trim(message)//"unknownOption"; return + end select +end subroutine tcond_snow end module snow_utils_module From bc81aa98467bc201122413b929600cb9b115acd5 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 30 Sep 2022 09:43:03 +0900 Subject: [PATCH 0388/1472] Indexing in ssdNrgFlux was slightly wrong --- build/source/engine/ssdNrgFlux.f90 | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index 84c893d6c..13d564d91 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -358,9 +358,13 @@ subroutine ssdNrgFlux(& ! start with the un-perturbed case vectorVolFracLiqTrial(1:2) = mLayerVolFracLiqTrial(mLayer_ind) ! need to protect against negative indexes - if ( mLayer_ind(1) .ge. nSnow .and. mLayer_ind(2) .ge. nSnow)then - if (iLayer==nSnow ) mLayer_ind(1) = nsnow+1 - vectorMatricHeadTrial(1:2) = mLayerMatricHeadTrial(mLayer_ind-nSnow) + if ( mLayer_ind(1) > nSnow .and. mLayer_ind(2) > nSnow)then + vectorMatricHeadTrial(1:2) = mLayerMatricHeadTrial(mLayer_ind-nSnow) + else if (mLayer_ind(2) > nSnow)then !mLayer_ind(1) == nSnow + vectorMatricHeadTrial(1) = realMissing + vectorMatricHeadTrial(2) = mLayerMatricHeadTrial(mLayer_ind(2)-nSnow) + else !snow layer + vectorMatricHeadTrial(1:2) = realMissing end if vectorTempTrial(1:2) = mLayerTempTrial(mLayer_ind) vectorVolFracIceTrial(1:2) = mLayerVolFracIceTrial(mLayer_ind) From b0a61ea667aea754afb6f50728c914fadd13e3ce Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 30 Sep 2022 10:10:38 +0900 Subject: [PATCH 0389/1472] Adding instructions on how to print Jacobians to sundials installation file --- sundials/installation.txt | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/sundials/installation.txt b/sundials/installation.txt index a610f89d7..b22fcd546 100644 --- a/sundials/installation.txt +++ b/sundials/installation.txt @@ -24,7 +24,7 @@ cp ../../summa/build/build_cmakeSundials_* build_cmake 5. The default compiler is gfortan. To change it (it should be the same as the netcdf build and the later summa build), you could also add the following option to cmake with your $(YOUR_GFORTRAN) -DCMAKE_Fortran_COMPILER=$(YOUR_GFORTRAN) -6. If the above went well, staying in the builddir directory run +6. If the above went well, staying in the buildir directory run % make % make install @@ -38,8 +38,39 @@ cp ../../summa/build/build_cmakeSundials_* build_cmake 11. Inside the /summa/build/ directory run % make -(12) On some installations you may need to add to your .bashrc file (and run source .bashrc) +12. On some installations you may need to add to your .bashrc file (and run source .bashrc) LD_LIBRARY_PATH='$(YOUR_HOME)/sundials/instdir/lib64' export LD_LIBRARY_PATH +NOTE. If you'd like to print the Jacobians going into the Sundials IDA solver, you need to do the following + +1. to file sundials-5.8.0/src/ida/ida_ls.c around line 1345, should be after: + + /* Call Jacobian routine */ + retval = idals_mem->jac(IDA_mem->ida_tn, IDA_mem->ida_cj, y, + yp, r, idals_mem->J, + idals_mem->J_data, vt1, vt2, vt3); + +THEN ADD: + + /* PRINT JACOBIAN*/ + doprint = 1; + if (doprint == 1) { + printf("Initial Jacobian"); + SUNDenseMatrix_Print(idals_mem->J,stdout); + SUNBandMatrix_Print(idals_mem->J,stdout); + } + +2. You will also need to add the parameter doprint, so in that file at line 1302 it should read + + IDALsMem idals_mem; + int doprint, retval; + +3. Now, you can get the printing by toggling on (doprint=1) or off (doprint=0) this, and recompiling sundials with make; make install inside buildir. + +This will make output files very large, so only use for testing. +If you want to print Finite Difference Jacobians and not the analytical Jacobian, you need to comment out the second line here (around line 373) in summa/build/source/engine/summaSolveSundialsIDA.f90 + + !comment this line out to use FD Jacobian + retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) From 4e0393d2e9b82053c5cd464f39e99dee26af0069 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 3 Oct 2022 12:11:03 +0900 Subject: [PATCH 0390/1472] eval8JacDAE rename computJacobSetup, now inside computJacobSundials evalJac4IDA computeJacob4IDA, now inside computJacobSundials evalDAE4IDA eval8summa4IDA, now inside eval8summaSundials --- build/my_makefile_cop | 3 - build/my_makefile_gra | 3 - build/my_makefile_mac | 3 - build/my_makefile_ric | 3 - build/source/engine/computJacobSundials.f90 | 525 +++++++++++++++--- build/source/engine/eval8JacDAE.f90 | 343 ------------ build/source/engine/eval8summaSundials.f90 | 142 ++++- build/source/engine/evalDAE4IDA.f90 | 164 ------ build/source/engine/evalJac4IDA.f90 | 131 ----- build/source/engine/soilLiqFlx.f90 | 2 +- build/source/engine/summaSolveSundialsIDA.f90 | 10 +- 11 files changed, 594 insertions(+), 735 deletions(-) delete mode 100644 build/source/engine/eval8JacDAE.f90 delete mode 100644 build/source/engine/evalDAE4IDA.f90 delete mode 100644 build/source/engine/evalJac4IDA.f90 diff --git a/build/my_makefile_cop b/build/my_makefile_cop index f91efa347..1b20f582b 100644 --- a/build/my_makefile_cop +++ b/build/my_makefile_cop @@ -229,10 +229,7 @@ SUMMA_SOLVER= \ computThermConduct.f90 \ computResidSundials.f90 \ eval8summaSundials.f90 \ - evalDAE4IDA.f90 \ computJacobSundials.f90 \ - eval8JacDAE.f90 \ - evalJac4IDA.f90 \ computSnowDepth.f90 \ summaSolveSundialsIDA.f90 \ systemSolvSundials.f90 \ diff --git a/build/my_makefile_gra b/build/my_makefile_gra index be98032b9..a63e6dd85 100644 --- a/build/my_makefile_gra +++ b/build/my_makefile_gra @@ -229,10 +229,7 @@ SUMMA_SOLVER= \ computThermConduct.f90 \ computResidSundials.f90 \ eval8summaSundials.f90 \ - evalDAE4IDA.f90 \ computJacobSundials.f90 \ - eval8JacDAE.f90 \ - evalJac4IDA.f90 \ computSnowDepth.f90 \ summaSolveSundialsIDA.f90 \ systemSolvSundials.f90 \ diff --git a/build/my_makefile_mac b/build/my_makefile_mac index ce0ee1083..114f40c21 100644 --- a/build/my_makefile_mac +++ b/build/my_makefile_mac @@ -229,10 +229,7 @@ SUMMA_SOLVER= \ computThermConduct.f90 \ computResidSundials.f90 \ eval8summaSundials.f90 \ - evalDAE4IDA.f90 \ computJacobSundials.f90 \ - eval8JacDAE.f90 \ - evalJac4IDA.f90 \ computSnowDepth.f90 \ summaSolveSundialsIDA.f90 \ systemSolvSundials.f90 \ diff --git a/build/my_makefile_ric b/build/my_makefile_ric index 037e81841..5bb604433 100644 --- a/build/my_makefile_ric +++ b/build/my_makefile_ric @@ -229,10 +229,7 @@ SUMMA_SOLVER= \ computThermConduct.f90 \ computResidSundials.f90 \ eval8summaSundials.f90 \ - evalDAE4IDA.f90 \ computJacobSundials.f90 \ - eval8JacDAE.f90 \ - evalJac4IDA.f90 \ computSnowDepth.f90 \ summaSolveSundialsIDA.f90 \ systemSolvSundials.f90 \ diff --git a/build/source/engine/computJacobSundials.f90 b/build/source/engine/computJacobSundials.f90 index 09a36cea9..524bdedaa 100644 --- a/build/source/engine/computJacobSundials.f90 +++ b/build/source/engine/computJacobSundials.f90 @@ -21,24 +21,25 @@ module computJacobSundials_module ! data types -USE nrtype +use nrtype ! derived types to define the data structures USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (rkind) + var_dlength, & ! data vector with variable length dimension (rkind) + model_options ! defines the model decisions ! named variables for structure elements +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure +USE var_lookup,only:iLookPARAM ! named variables for structure elements USE var_lookup,only:iLookPROG ! named variables for structure elements -USE var_lookup,only:iLookDIAG ! named variables for structure elements USE var_lookup,only:iLookINDEX ! named variables for structure elements +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookFLUX ! named variables for structure elements USE var_lookup,only:iLookDERIV ! named variables for structure elements -! look-up values for the form of Richards' equation -USE mDecisions_module,only: & - moisture, & ! moisture-based form of Richards' equation - mixdform ! mixed form of Richards' equation - ! access the global print flag USE globalData,only:globalPrintFlag @@ -60,6 +61,7 @@ module computJacobSundials_module USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers +USE globalData,only:model_decisions ! model decision structure ! access named variables to describe the form and structure of the matrices used in the numerical solver USE globalData,only: ku ! number of super-diagonal bands @@ -73,9 +75,31 @@ module computJacobSundials_module ! constants USE multiconst,only:& + Tfreeze, & ! temperature at freezing (K) LH_fus, & ! latent heat of fusion (J kg-1) + LH_vap, & ! latent heat of vaporization (J kg-1) + LH_sub, & ! latent heat of sublimation (J kg-1) + Cp_air, & ! specific heat of air (J kg-1 K-1) + iden_air, & ! intrinsic density of air (kg m-3) + iden_ice, & ! intrinsic density of ice (kg m-3) iden_water ! intrinsic density of liquid water (kg m-3) +! look-up values for the choice of groundwater representation (local-column, or single-basin) +USE mDecisions_module,only: & + localColumn, & ! separate groundwater representation in each local soil column + singleBasin ! single groundwater store over the entire basin + +! look-up values for the choice of groundwater parameterization +USE mDecisions_module,only: & + qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization + bigBucket, & ! a big bucket (lumped aquifer model) + noExplicit ! no explicit groundwater parameterization + +! look-up values for the form of Richards' equation +USE mDecisions_module,only: & + moisture, & ! moisture-based form of Richards' equation + mixdform ! mixed form of Richards' equation + implicit none ! define constants real(rkind),parameter :: verySmall=tiny(1.0_rkind) ! a very small number @@ -83,6 +107,9 @@ module computJacobSundials_module private public::computJacobSundials +public::computJacobSetup +public::computJacob4IDA + contains ! ********************************************************************************************************** @@ -350,15 +377,15 @@ subroutine computJacobSundials(& message=trim(message)//'unexpected shape of the Jacobian matrix: expect aJac(nBands,nState)' err=20; return endif - + ! ----- ! * energy and liquid fluxes over vegetation... ! --------------------------------------------- if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) - + ! * energy fluxes with the canopy water if(ixVegHyd/=integerMissing)then - + ! * cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixCasNrg),ixCasNrg) = -dCanopyEvaporation_dTCanair*dt ! dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy is the derivative in throughfall and canopy drainage with canopy temperature @@ -366,10 +393,10 @@ subroutine computJacobSundials(& ! * liquid water fluxes for vegetation canopy (-), dt*scalarFracLiqVeg*scalarCanopyLiqDeriv is the derivative in throughfall and canopy drainage with canopy water aJac(ixDiag, ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanWat - scalarCanopyLiqDeriv)*dt + 1._rkind * cj if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixTopNrg),ixTopNrg) = -dCanopyEvaporation_dTGround*dt - + ! * cross-derivative terms w.r.t. canopy water (kg-1 m2) if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water - + ! * cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth * cj & @@ -377,220 +404,220 @@ subroutine computJacobSundials(& + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth ! dF/dLiq if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) endif - + ! * -derivative terms w.r.t. canopy temperature (K-1) if(ixVegNrg/=integerMissing)then if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarCanopyLiqDeriv*dCanLiq_dTcanopy)/iden_water endif - + ! * energy fluxes with the canopy air space (J m-3 K-1) if(ixCasNrg/=integerMissing)then aJac(ixDiag, ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) * cj if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixCasNrg,ixVegNrg),ixVegNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanopyTemp) if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixCasNrg,ixTopNrg),ixTopNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dGroundTemp) endif - + ! * energy fluxes with the vegetation canopy (J m-3 K-1) if(ixVegNrg/=integerMissing)then if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixCasNrg),ixCasNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanairTemp) aJac(ixDiag, ixVegNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanopyTemp) + dMat(ixVegNrg) if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixTopNrg),ixTopNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dGroundTemp) endif - + ! * energy fluxes with the surface (J m-3 K-1) if(ixTopNrg/=integerMissing)then if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanairTemp) if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanopyTemp) endif - + endif ! if there is a need to compute energy fluxes within vegetation - + ! ----- ! * energy fluxes for the snow+soil domain... ! ------------------------------------------- if(nSnowSoilNrg>0)then do iLayer=1,nLayers ! loop through all layers in the snow+soil domain - + ! check if the state is in the subset if(ixSnowSoilNrg(iLayer)==integerMissing) cycle - + ! - define index within the state subset and the full state vector jState = ixSnowSoilNrg(iLayer) ! index within the state subset - + ! - diagonal elements aJac(ixDiag,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) - + ! - lower-diagonal elements if(iLayer>1)then if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowSoilNrg(iLayer-1),jState),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) endif - + ! - upper diagonal elements if(iLayer0)then do iLayer=1,nSnow ! loop through layers in the snow domain - + ! - check that the snow layer is desired if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle - + ! - define state indices for the current layer watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - + ! compute factor to convert liquid water derivative to total water derivative select case( ixHydType(iLayer) ) case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) case default; convLiq2tot = 1._rkind end select - + ! - diagonal elements aJac(ixDiag,watState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot + dMat(watState) * cj - + ! - lower-diagonal elements if(iLayer>1)then if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyHyd(iLayer-1),watState),watState) = 0._rkind ! sub-diagonal: no dependence on other layers endif - + ! - upper diagonal elements if(iLayer0 .and. nSnowOnlyNrg>0)then do iLayer=1,nSnow ! loop through layers in the snow domain - + ! - check that the snow layer is desired if(ixSnowOnlyNrg(iLayer)==integerMissing) cycle - + ! (define the energy state) nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector - + ! - define state indices for the current layer watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - + if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) - + ! - include derivatives of energy fluxes w.r.t water fluxes for current layer aJac(ixOffDiag(nrgState,watState),watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) & + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) & + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) - + ! - include derivatives of water fluxes w.r.t energy fluxes for current layer aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) - + ! (cross-derivative terms for the layer below) if(iLayer1)then if(ixSnowOnlyNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyNrg(iLayer-1),watState),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) ) endif - + ! (cross-derivative terms for the layer below) if(iLayer0)then !bottom snow layer and there is soil below if(ixSoilOnlyNrg(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyNrg(1),watState),watState) = (dt/mLayerDepth(nSnow+1))*(-dNrgFlux_dWatAbove(nSnow) ) endif - + endif ! (if the energy state for the current layer is within the state subset) - + end do ! (looping through snow layers) endif ! (if there are state variables for both water and energy in the snow domain) - + ! ----- ! * liquid water fluxes for the soil domain... ! -------------------------------------------- if(nSoilOnlyHyd>0)then do iLayer=1,nSoil - + ! - check that the soil layer is desired if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle - + ! - define state indices watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset - + ! - define indices of the soil layers jLayer = iLayer+nSnow ! index of layer in the snow+soil vector - + ! - compute the diagonal elements ! all terms *excluding* baseflow aJac(ixDiag,watState) = (dt/mLayerDepth(jLayer))*(-dq_dHydStateBelow(iLayer-1) + dq_dHydStateAbove(iLayer)) + dMat(watState) - + ! - compute the lower-diagonal elements if(iLayer>1)then if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),watState),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) endif - + ! - compute the upper-diagonal elements if(iLayer0 .and. nSoilOnlyNrg>0)then do iLayer=1,nSoilOnlyNrg - + ! - check that the soil layer is desired if(ixSoilOnlyNrg(iLayer)==integerMissing) cycle - + ! - define indices of the soil layers jLayer = iLayer+nSnow ! index of layer in the snow+soil vector - + ! - define the energy state variable nrgState = ixNrgLayer(jLayer) ! index within the full state vector - + ! - define index of hydrology state variable within the state subset watState = ixSoilOnlyHyd(iLayer) - + ! only compute derivatives if the water state for the current layer is within the state subset if(watstate/=integerMissing)then - + ! - include derivatives in liquid water fluxes w.r.t. temperature for current layer aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance - + ! - compute lower diagonal elements if(iLayer>1)then if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),nrgState),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 endif - + ! compute upper-diagonal elements if(iLayer1)then if(ixSoilOnlyNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyNrg(iLayer-1),watState),watState) = (dt/mLayerDepth(jLayer-1))*( dNrgFlux_dWatBelow(jLayer-1) ) elseif(iLayer==1 .and. nSnowOnlyNrg>0)then !top soil layer and there is snow above if(ixSnowOnlyNrg(nSnow)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyNrg(nSnow),watState),watState) = (dt/mLayerDepth(nSnow))*( dNrgFlux_dWatBelow(nSnow) ) endif - + ! (cross-derivative terms for the layer below) if(iLayer model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation + ! soil parameters + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) + ! model state variables + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) + ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision & + ) ! association to variables in the data structures + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="computJacobSetup/" + + ! extract variables from the model state vector + call varExtract(& + ! input + stateVec, & ! intent(in): model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) + mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) + ! output: variables for the aquifer + scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! extract derivative of variables from derivative of the model state vector + call varExtract(& + ! input + stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy + scalarCanairTempPrime, & ! intent(out): derivative of canopy air temperature (K) + scalarCanopyTempPrime, & ! intent(out): derivative of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(out): derivative of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(out): derivative of canopy liquid water (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempPrime, & ! intent(out): derivative of layer temperature (K) + mLayerVolFracWatPrime, & ! intent(out): derivative of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(out): derivative of volumetric liquid water content (-) + mLayerMatricHeadPrime, & ! intent(out): derivative of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(out): derivative of liquid water matric potential (m) + ! output: variables for the aquifer + scalarAquiferStoragePrime,& ! intent(out): derivative of storage of water in the aquifer (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + call updateVarsSundials(& + ! input + dt, & ! intent(in): time step + .true., & ! intent(in): logical flag if computing Jacobian for sundials solver + .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + mLayerVolFracWatTrial, & ! intent(in): use current vector for prev vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): use current vector for prev vector of total water matric potential (m) + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output: variables for the vegetation canopy + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2 + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempPrime, & ! + mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) + mLayerVolFracIcePrime, & ! + mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! ----- + ! * compute the Jacobian matrix... + ! -------------------------------- + + ! compute the analytical Jacobian matrix + ! NOTE: The derivatives were computed in the previous call to computFlux + ! This occurred either at the call to eval8summaSundials at the start of sysSolveSundials + ! or in the call to eval8summaSundials in the previous iteration + dt1 = 1._qp + call computJacobSundials(& + ! input: model control + cj, & ! intent(in): this scalar changes whenever the step size or method order changes + dt1, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + (ixGroundwater==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow + ixMatrix, & ! intent(in): form of the Jacobian matrix + specificStorage, & ! intent(in): specific storage coefficient (m-1) + theta_sat, & ! intent(in): soil porosity (-) + ixRichards, & ! intent(in): choice of option for Richards' equation + ! input: data structures + indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables + dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! input: state variables + mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) + mLayerTempPrime, & ! intent(in) + mLayerMatricHeadPrime, & ! intent(in) + mLayerMatricHeadLiqPrime, & ! intent(in) + mLayerVolFracWatPrime, & ! intent(in) + scalarCanopyTempTrial, & ! intent(in) + scalarCanopyTempPrime, & ! intent(in) derivative value for temperature of the vegetation canopy (K) + scalarCanopyWatPrime, & ! intetn(in) + ! input-output: Jacobian and its diagonal + dMat, & ! intent(inout): diagonal of the Jacobian matrix + Jac, & ! intent(out): Jacobian matrix + ! output: error control + err,cmessage) ! intent(out): error code and error message + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! end association with the information in the data structures + end associate + +end subroutine computJacobSetup + + +! ********************************************************************************************************** +! public function computJacob4IDA: the interface to compute the Jacobian matrix dF/dy + c dF/dy' for IDA solver +! ********************************************************************************************************** +! Return values: +! 0 = success, +! 1 = recoverable error, +! -1 = non-recoverable error +! ---------------------------------------------------------------- +integer(c_int) function computJacob4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & + sunmat_J, user_data, sunvec_temp1, sunvec_temp2, sunvec_temp3) & + result(ierr) bind(C,name='computJacob4IDA') + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use fsundials_nvector_mod + use fsundials_matrix_mod + use fnvector_serial_mod + use fsunmatrix_band_mod + use fsunmatrix_dense_mod + use type4IDA + + !======= Declarations ========= + implicit none + + ! calling variables + real(rkind), value :: t ! current time + real(rkind), value :: cj ! step size scaling factor + type(N_Vector) :: sunvec_y ! solution N_Vector + type(N_Vector) :: sunvec_yp ! derivative N_Vector + type(N_Vector) :: sunvec_r ! residual N_Vector + type(SUNMatrix) :: sunmat_J ! Jacobian SUNMatrix + type(c_ptr), value :: user_data ! user-defined data + type(N_Vector) :: sunvec_temp1 ! temporary N_Vector + type(N_Vector) :: sunvec_temp2 ! temporary N_Vector + type(N_Vector) :: sunvec_temp3 ! temporary N_Vector + + ! pointers to data in SUNDIALS vectors + real(rkind), pointer :: stateVec(:) ! state vector + real(rkind), pointer :: stateVecPrime(:)! derivative of the state vector + real(rkind), pointer :: rVec(:) ! residual vector + real(rkind), pointer :: Jac(:,:) ! Jacobian matrix + type(eqnsData), pointer :: eqns_data ! equations data + + !======= Internals ============ + + ! get equations data from user-defined data + call c_f_pointer(user_data, eqns_data) + + ! get data arrays from SUNDIALS vectors + stateVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_y) + stateVecPrime(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_yp) + rVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_r) + if (eqns_data%ixMatrix==ixBandMatrix) Jac(1:nBands, 1:eqns_data%nState) => FSUNBandMatrix_Data(sunmat_J) + if (eqns_data%ixMatrix==ixFullMatrix) Jac(1:eqns_data%nState, 1:eqns_data%nState) => FSUNDenseMatrix_Data(sunmat_J) + + ! compute Jacobian matrix + call computJacobSetup(& + ! input: model control + cj, & ! intent(in): this scalar changes whenever the step size or method order changes + eqns_data%dt, & ! intent(in): data step + eqns_data%nSnow, & ! intent(in): number of snow layers + eqns_data%nSoil, & ! intent(in): number of soil layers + eqns_data%nLayers, & ! intent(in): number of layers + eqns_data%ixMatrix, & ! intent(in): type of matrix (dense or banded) + eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVec, & ! intent(in): model state vector + stateVecPrime, & ! intent(in): model state vector + eqns_data%sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + eqns_data%mpar_data, & ! intent(in): model parameters + eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + eqns_data%indx_data, & ! intent(inou): index data + eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU + eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input: baseflow + eqns_data%dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! output + eqns_data%dMat, & ! intetn(inout): diagonal of the Jacobian matrix + Jac, & ! intent(out): Jacobian matrix + eqns_data%err,eqns_data%message) ! intent(out): error control + + if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif + if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif + + ! return success + ierr = 0 + return + +end function computJacob4IDA + + ! ********************************************************************************************************** ! private function: get the off-diagonal index in the band-diagonal matrix ! ********************************************************************************************************** diff --git a/build/source/engine/eval8JacDAE.f90 b/build/source/engine/eval8JacDAE.f90 deleted file mode 100644 index 139d9c9ec..000000000 --- a/build/source/engine/eval8JacDAE.f90 +++ /dev/null @@ -1,343 +0,0 @@ - -module eval8JacDAE_module - -! data types -USE nrtype - -! access missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing double precision number -USE globalData,only:quadMissing ! missing quadruple precision number - -! access the global print flag -USE globalData,only:globalPrintFlag - -! define access to state variables to print -USE globalData,only: iJac1 ! first layer of the Jacobian to print -USE globalData,only: iJac2 ! last layer of the Jacobian to print - -! domain types -USE globalData,only:iname_veg ! named variables for vegetation -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil - -! named variables to describe the state variable type -USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space -USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy -USE globalData,only:iname_watCanopy ! named variable defining the mass of water on the vegetation canopy -USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers -USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers -USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers -USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers -USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers -USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix -USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix -USE globalData,only:model_decisions ! model decision structure - -! constants -USE multiconst,only:& - Tfreeze, & ! temperature at freezing (K) - LH_fus, & ! latent heat of fusion (J kg-1) - LH_vap, & ! latent heat of vaporization (J kg-1) - LH_sub, & ! latent heat of sublimation (J kg-1) - Cp_air, & ! specific heat of air (J kg-1 K-1) - iden_air, & ! intrinsic density of air (kg m-3) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water ! intrinsic density of liquid water (kg m-3) - -! provide access to the derived types to define the data structures -USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - model_options ! defines the model decisions - -! indices that define elements of the data structures -USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure -USE var_lookup,only:iLookPARAM ! named variables for structure elements -USE var_lookup,only:iLookPROG ! named variables for structure elements -USE var_lookup,only:iLookINDEX ! named variables for structure elements -USE var_lookup,only:iLookDIAG ! named variables for structure elements -USE var_lookup,only:iLookFLUX ! named variables for structure elements -USE var_lookup,only:iLookDERIV ! named variables for structure elements - -! look-up values for the choice of groundwater representation (local-column, or single-basin) -USE mDecisions_module,only: & - localColumn, & ! separate groundwater representation in each local soil column - singleBasin ! single groundwater store over the entire basin - -! look-up values for the choice of groundwater parameterization -USE mDecisions_module,only: & - qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization - bigBucket, & ! a big bucket (lumped aquifer model) - noExplicit ! no explicit groundwater parameterization - -! look-up values for the form of Richards' equation -USE mDecisions_module,only: & - moisture, & ! moisture-based form of Richards' equation - mixdform ! mixed form of Richards' equation - -implicit none -private -public::eval8JacDAE - -contains - -! ********************************************************************************************************** -! public subroutine eval8JacDAE: compute the Jacobian matrix -! ********************************************************************************************************** -subroutine eval8JacDAE(& - ! input: model control - cj, & ! intent(in): this scalar changes whenever the step size or method order changes - dt, & ! intent(in): time step - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - ixMatrix, & ! intent(in): form of the Jacobian matrix - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVec, & ! intent(in): model state vector - stateVecPrime, & ! intent(in): derivative of model state vector - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input: baseflow - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! output: flux and residual vectors - dMat, & ! intent(inout): diagonal of Jacobian Matrix - Jac, & ! intent(out): jacobian matrix - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - ! provide access to subroutines - USE getVectorz_module, only:varExtract ! extract variables from the state vector - USE updateVarsSundials_module, only:updateVarsSundials ! update prognostic variables - USE computJacobSundials_module,only:computJacobSundials - implicit none - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - ! input: model control - real(rkind),intent(in) :: cj ! this scalar changes whenever the step size or method order changes - real(rkind),intent(in) :: dt ! time step - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - ! input: state vectors - real(rkind),intent(in) :: stateVec(:) ! model state vector - real(rkind),intent(in) :: stateVecPrime(:) ! model state vector - real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) - ! input: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(var_dlength), intent(in) :: mpar_data ! model parameters - type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU - ! output: data structures - type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - ! output: Jacobian - real(rkind), intent(inout) :: dMat(:) - real(rkind), intent(out) :: Jac(:,:) ! jacobian matrix - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - ! -------------------------------------------------------------------------------------------------------------------------------- - ! state variables - real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial value for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial value for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial value for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) - real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) - ! diagnostic variables - real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) - ! derivative of state variables - real(rkind) :: scalarCanairTempPrime ! derivative value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempPrime ! derivative value for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! derivative value for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! derivative value for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! derivative value for liquid water matric potential (m) - real(rkind) :: scalarAquiferStoragePrime ! derivative value of storage of water in the aquifer (m) - ! derivative of diagnostic variables - real(rkind) :: scalarCanopyLiqPrime ! derivative value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIcePrime ! derivative value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! derivative value for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! derivative value for volumetric fraction of ice (-) - ! other local variables - character(LEN=256) :: cmessage ! error message of downwind routine - real(rkind) :: dt1 - - ! -------------------------------------------------------------------------------------------------------------------------------- - ! association to variables in the data structures - ! -------------------------------------------------------------------------------------------------------------------------------- - associate(& - ! model decisions - ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation - ! soil parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) - ! model state variables - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) - ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision & - ) ! association to variables in the data structures - ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="eval8JacDAE/" - - ! extract variables from the model state vector - call varExtract(& - ! input - stateVec, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! extract derivative of variables from derivative of the model state vector - call varExtract(& - ! input - stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy - scalarCanairTempPrime, & ! intent(out): derivative of canopy air temperature (K) - scalarCanopyTempPrime, & ! intent(out): derivative of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(out): derivative of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(out): derivative of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempPrime, & ! intent(out): derivative of layer temperature (K) - mLayerVolFracWatPrime, & ! intent(out): derivative of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(out): derivative of volumetric liquid water content (-) - mLayerMatricHeadPrime, & ! intent(out): derivative of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(out): derivative of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStoragePrime,& ! intent(out): derivative of storage of water in the aquifer (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - call updateVarsSundials(& - ! input - dt, & ! intent(in): time step - .true., & ! intent(in): logical flag if computing Jacobian for sundials solver - .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - mLayerVolFracWatTrial, & ! intent(in): use current vector for prev vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): use current vector for prev vector of total water matric potential (m) - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2 - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! - mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) - mLayerVolFracIcePrime, & ! - mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! ----- - ! * compute the Jacobian matrix... - ! -------------------------------- - - ! compute the analytical Jacobian matrix - ! NOTE: The derivatives were computed in the previous call to computFlux - ! This occurred either at the call to eval8summaSundials at the start of sysSolveSundials - ! or in the call to eval8summaSundials in the previous iteration - dt1 = 1._qp - call computJacobSundials(& - ! input: model control - cj, & ! intent(in): this scalar changes whenever the step size or method order changes - dt1, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - (ixGroundwater==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow - ixMatrix, & ! intent(in): form of the Jacobian matrix - specificStorage, & ! intent(in): specific storage coefficient (m-1) - theta_sat, & ! intent(in): soil porosity (-) - ixRichards, & ! intent(in): choice of option for Richards' equation - ! input: data structures - indx_data, & ! intent(in): index data - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! input: state variables - mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) - mLayerTempPrime, & ! intent(in) - mLayerMatricHeadPrime, & ! intent(in) - mLayerMatricHeadLiqPrime, & ! intent(in) - mLayerVolFracWatPrime, & ! intent(in) - scalarCanopyTempTrial, & ! intent(in) - scalarCanopyTempPrime, & ! intent(in) derivative value for temperature of the vegetation canopy (K) - scalarCanopyWatPrime, & ! intetn(in) - ! input-output: Jacobian and its diagonal - dMat, & ! intent(inout): diagonal of the Jacobian matrix - Jac, & ! intent(out): Jacobian matrix - ! output: error control - err,cmessage) ! intent(out): error code and error message - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! end association with the information in the data structures - end associate - -end subroutine eval8JacDAE -end module eval8JacDAE_module diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index 0b9ae15fa..b28032dbf 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -8,6 +8,7 @@ module eval8summaSundials_module USE globalData,only:integerMissing ! missing integer USE globalData,only:realMissing ! missing double precision number USE globalData,only:quadMissing ! missing quadruple precision number +USE globalData,only:flux_meta ! metadata on the model fluxes ! access the global print flag USE globalData,only:globalPrintFlag @@ -32,7 +33,6 @@ module eval8summaSundials_module USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers USE globalData,only:model_decisions ! model decision structure - ! constants USE multiconst,only:& Tfreeze, & ! temperature at freezing (K) @@ -82,6 +82,8 @@ module eval8summaSundials_module implicit none private public::eval8summaSundials +public::eval8summa4IDA + contains @@ -681,7 +683,7 @@ subroutine eval8summaSundials(& deriv_data, & ! intent(out): derivatives in model fluxes w.r.t. relevant state variables ! input-output: flux vector and baseflow derivatives ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later in computeJacobSundials + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later in computJacobSundials fluxVec, & ! intent(out): flux vector (mixed units) ! output: error control err,cmessage) ! intent(out): error code and error message @@ -707,7 +709,6 @@ subroutine eval8summaSundials(& err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! compute the residual vector call computResidSundials(& ! input: model control @@ -742,12 +743,141 @@ subroutine eval8summaSundials(& resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation resVec, & ! intent(out): residual vector err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors ! end association with the information in the data structures end associate end subroutine eval8summaSundials + + +! ********************************************************************************************************** +! public function eval8summa4IDA: compute the residual vector F(t,y,y') required for IDA solver +! ********************************************************************************************************** +! Return values: +! 0 = success, +! 1 = recoverable error, +! -1 = non-recoverable error +! ---------------------------------------------------------------- +integer(c_int) function eval8summa4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_data) & + result(ierr) bind(C,name='eval8summa4IDA') + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use fida_mod + use fsundials_nvector_mod + use fnvector_serial_mod + use type4IDA + + !======= Declarations ========= + implicit none + + ! calling variables + real(rkind), value :: tres ! current time t + type(N_Vector) :: sunvec_y ! solution N_Vector y + type(N_Vector) :: sunvec_yp ! derivative N_Vector y' + type(N_Vector) :: sunvec_r ! residual N_Vector F(t,y,y') + type(c_ptr), value :: user_data ! user-defined data + + ! pointers to data in SUNDIALS vectors + type(eqnsData), pointer :: eqns_data ! equations data + real(rkind), pointer :: stateVec(:) + real(rkind), pointer :: stateVecPrime(:) + real(rkind), pointer :: rVec(:) + logical(lgt) :: feasible + integer(i4b) :: retval + real(c_double) :: stepsize_next(1) + !======= Internals ============ + + ! get equations data from user-defined data + call c_f_pointer(user_data, eqns_data) + + ! get data arrays from SUNDIALS vectors + stateVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_y) + stateVecPrime(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_yp) + rVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_r) + + retval = FIDAGetCurrentStep(eqns_data%ida_mem, stepsize_next) + if (retval /= 0) then + print *, 'Error in FIDAGetCurrentStep, retval = ', retval, '; halting' + stop 1 + end if + + ! compute the flux and the residual vector for a given state vector + call eval8summaSundials(& + ! input: model control + stepsize_next(1), & ! intent(in): current stepsize + eqns_data%dt, & ! intent(in): data step + eqns_data%nSnow, & ! intent(in): number of snow layers + eqns_data%nSoil, & ! intent(in): number of soil layers + eqns_data%nLayers, & ! intent(in): number of layers + eqns_data%nState, & ! intent(in): number of state variables in the current subset + .true., & ! intent(in): inside Sundials solver + eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation + eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVec, & ! intent(in): model state vector + stateVecPrime, & ! intent(in): model state vector + eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + eqns_data%lookup_data, & ! intent(in): lookup data + eqns_data%type_data, & ! intent(in): type of vegetation and soil + eqns_data%attr_data, & ! intent(in): spatial attributes + eqns_data%mpar_data, & ! intent(in): model parameters + eqns_data%forc_data, & ! intent(in): model forcing data + eqns_data%bvar_data, & ! intent(in): average model variables for the entire basin + eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + eqns_data%indx_data, & ! intent(inou): index data + eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU + eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) + eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian + eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) + eqns_data%scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) + eqns_data%scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + eqns_data%scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) + eqns_data%scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) + eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): value for enthalpy of the vegetation canopy (J m-3) + eqns_data%mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + eqns_data%mLayerTempPrev, & ! intent(in): vector of layer temperature (K) + eqns_data%mLayerMatricHeadLiqTrial,& ! intent(out): trial value for liquid water matric potential (m) + eqns_data%mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) + eqns_data%mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) + eqns_data%mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) + eqns_data%mLayerVolFracWatPrev, & ! intent(in): vector of volumetric total water content (-) + eqns_data%mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) + eqns_data%mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) + eqns_data%mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) + eqns_data%mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) + eqns_data%scalarAquiferStorageTrial, & ! intent(out): trial value of storage of water in the aquifer (m) + eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) + eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) + eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) + eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer + ! output + feasible, & ! intent(out): flag to denote the feasibility of the solution + eqns_data%fluxVec, & ! intent(out): flux vector + eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec, & ! intent(out): residual vector + eqns_data%err,eqns_data%message) ! intent(out): error control + + if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif + if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif + if(.not.feasible)then; eqns_data%message=trim(eqns_data%message)//'state vector not feasible'; ierr = 1; return; endif + + ! return success + ierr = 0 + return + +end function eval8summa4IDA + + end module eval8summaSundials_module diff --git a/build/source/engine/evalDAE4IDA.f90 b/build/source/engine/evalDAE4IDA.f90 deleted file mode 100644 index 22cdbc02d..000000000 --- a/build/source/engine/evalDAE4IDA.f90 +++ /dev/null @@ -1,164 +0,0 @@ -module evalDAE4IDA_module - - -!======= Inclusions =========== -use, intrinsic :: iso_c_binding -use nrtype -use type4IDA -USE globalData,only:model_decisions ! model decision structure -USE globalData,only:flux_meta ! metadata on the model fluxes -! provide access to the derived types to define the data structures -USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - model_options ! defines the model decisions -USE multiconst,only:iden_water ! intrinsic density of liquid water (kg m-3) -USE var_lookup,only:iLookDIAG -USE var_lookup,only:iLookPROG -USE var_lookup,only:iLookINDEX ! named variables for structure elements -USE var_lookup,only:iLookDERIV ! named variables for structure elements - - -! privacy -implicit none -private -public::evalDAE4IDA - - -contains - -! ********************************************************************************************************** -! public function evalDAE4IDA: compute the residual vector F(t,y,y') required for IDA solver -! ********************************************************************************************************** -! Return values: -! 0 = success, -! 1 = recoverable error, -! -1 = non-recoverable error -! ---------------------------------------------------------------- -integer(c_int) function evalDAE4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_data) & - result(ierr) bind(C,name='evalDAE4IDA') - - !======= Inclusions =========== - use, intrinsic :: iso_c_binding - use fida_mod - use fsundials_nvector_mod - use fnvector_serial_mod - use nrtype - use type4IDA - use eval8summaSundials_module,only:eval8summaSundials - - !======= Declarations ========= - implicit none - - ! calling variables - real(rkind), value :: tres ! current time t - type(N_Vector) :: sunvec_y ! solution N_Vector y - type(N_Vector) :: sunvec_yp ! derivative N_Vector y' - type(N_Vector) :: sunvec_r ! residual N_Vector F(t,y,y') - type(c_ptr), value :: user_data ! user-defined data - - - ! pointers to data in SUNDIALS vectors - type(eqnsData), pointer :: eqns_data ! equations data - real(rkind), pointer :: stateVec(:) - real(rkind), pointer :: stateVecPrime(:) - real(rkind), pointer :: rVec(:) - logical(lgt) :: feasible - integer(i4b) :: retval - real(c_double) :: stepsize_next(1) - !======= Internals ============ - - ! get equations data from user-defined data - call c_f_pointer(user_data, eqns_data) - - ! get data arrays from SUNDIALS vectors - stateVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_y) - stateVecPrime(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_yp) - rVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_r) - - retval = FIDAGetCurrentStep(eqns_data%ida_mem, stepsize_next) - if (retval /= 0) then - print *, 'Error in FIDAGetCurrentStep, retval = ', retval, '; halting' - stop 1 - end if - - ! compute the flux and the residual vector for a given state vector - call eval8summaSundials(& - ! input: model control - stepsize_next(1), & ! intent(in): current stepsize - eqns_data%dt, & ! intent(in): data step - eqns_data%nSnow, & ! intent(in): number of snow layers - eqns_data%nSoil, & ! intent(in): number of soil layers - eqns_data%nLayers, & ! intent(in): number of layers - eqns_data%nState, & ! intent(in): number of state variables in the current subset - .true., & ! intent(in): inside Sundials solver - eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation - eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVec, & ! intent(in): model state vector - stateVecPrime, & ! intent(in): model state vector - eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - eqns_data%lookup_data, & ! intent(in): lookup data - eqns_data%type_data, & ! intent(in): type of vegetation and soil - eqns_data%attr_data, & ! intent(in): spatial attributes - eqns_data%mpar_data, & ! intent(in): model parameters - eqns_data%forc_data, & ! intent(in): model forcing data - eqns_data%bvar_data, & ! intent(in): average model variables for the entire basin - eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - eqns_data%indx_data, & ! intent(inou): index data - eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU - eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) - eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian - eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) - eqns_data%scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) - eqns_data%scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - eqns_data%scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) - eqns_data%scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) - eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): value for enthalpy of the vegetation canopy (J m-3) - eqns_data%mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - eqns_data%mLayerTempPrev, & ! intent(in): vector of layer temperature (K) - eqns_data%mLayerMatricHeadLiqTrial,& ! intent(out): trial value for liquid water matric potential (m) - eqns_data%mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) - eqns_data%mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) - eqns_data%mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - eqns_data%mLayerVolFracWatPrev, & ! intent(in): vector of volumetric total water content (-) - eqns_data%mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) - eqns_data%mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) - eqns_data%mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - eqns_data%mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) - eqns_data%scalarAquiferStorageTrial, & ! intent(out): trial value of storage of water in the aquifer (m) - eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) - eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) - eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) - eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer - ! output - feasible, & ! intent(out): flag to denote the feasibility of the solution - eqns_data%fluxVec, & ! intent(out): flux vector - eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector - eqns_data%err,eqns_data%message) ! intent(out): error control - - if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif - if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif - if(.not.feasible)then; eqns_data%message=trim(eqns_data%message)//'state vector not feasible'; ierr = 1; return; endif - - ! return success - ierr = 0 - return - -end function evalDAE4IDA - - -end module evalDAE4IDA_module diff --git a/build/source/engine/evalJac4IDA.f90 b/build/source/engine/evalJac4IDA.f90 deleted file mode 100644 index 0995a9d5f..000000000 --- a/build/source/engine/evalJac4IDA.f90 +++ /dev/null @@ -1,131 +0,0 @@ -module evalJac4IDA_module - - -!======= Inclusions =========== -use, intrinsic :: iso_c_binding -use nrtype -use type4IDA -USE globalData,only:model_decisions ! model decision structure -! provide access to the derived types to define the data structures -USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - model_options ! defines the model decisions - - - -! privacy -implicit none -private -public::evalJac4IDA - - -contains - -! ********************************************************************************************************** -! public function evalJac4IDA: the interface to compute the Jacobian matrix dF/dy + c dF/dy' for IDA solver -! ********************************************************************************************************** -! Return values: -! 0 = success, -! 1 = recoverable error, -! -1 = non-recoverable error -! ---------------------------------------------------------------- -integer(c_int) function evalJac4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & - sunmat_J, user_data, sunvec_temp1, sunvec_temp2, sunvec_temp3) & - result(ierr) bind(C,name='evalJac4IDA') - - !======= Inclusions =========== - use, intrinsic :: iso_c_binding - use fsundials_nvector_mod - use fsundials_matrix_mod - use fnvector_serial_mod - use fsunmatrix_band_mod - use fsunmatrix_dense_mod - use nrtype - use type4IDA - use eval8JacDAE_module,only:eval8JacDAE ! compute Jacobian matrix - USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix - USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix - USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix - !======= Declarations ========= - implicit none - - ! calling variables - real(rkind), value :: t ! current time - real(rkind), value :: cj ! step size scaling factor - type(N_Vector) :: sunvec_y ! solution N_Vector - type(N_Vector) :: sunvec_yp ! derivative N_Vector - type(N_Vector) :: sunvec_r ! residual N_Vector - type(SUNMatrix) :: sunmat_J ! Jacobian SUNMatrix - type(c_ptr), value :: user_data ! user-defined data - type(N_Vector) :: sunvec_temp1 ! temporary N_Vector - type(N_Vector) :: sunvec_temp2 ! temporary N_Vector - type(N_Vector) :: sunvec_temp3 ! temporary N_Vector - - ! pointers to data in SUNDIALS vectors - real(rkind), pointer :: stateVec(:) ! state vector - real(rkind), pointer :: stateVecPrime(:)! derivative of the state vector - real(rkind), pointer :: rVec(:) ! residual vector - real(rkind), pointer :: Jac(:,:) ! Jacobian matrix - type(eqnsData), pointer :: eqns_data ! equations data - - - - !======= Internals ============ - - ! get equations data from user-defined data - call c_f_pointer(user_data, eqns_data) - - - ! get data arrays from SUNDIALS vectors - stateVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_y) - stateVecPrime(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_yp) - rVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_r) - if (eqns_data%ixMatrix==ixBandMatrix) Jac(1:nBands, 1:eqns_data%nState) => FSUNBandMatrix_Data(sunmat_J) - if (eqns_data%ixMatrix==ixFullMatrix) Jac(1:eqns_data%nState, 1:eqns_data%nState) => FSUNDenseMatrix_Data(sunmat_J) - - ! compute Jacobian matrix - call eval8JacDAE(& - ! input: model control - cj, & ! intent(in): this scalar changes whenever the step size or method order changes - eqns_data%dt, & ! intent(in): data step - eqns_data%nSnow, & ! intent(in): number of snow layers - eqns_data%nSoil, & ! intent(in): number of soil layers - eqns_data%nLayers, & ! intent(in): number of layers - eqns_data%ixMatrix, & ! intent(in): type of matrix (dense or banded) - eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVec, & ! intent(in): model state vector - stateVecPrime, & ! intent(in): model state vector - eqns_data%sMul, & ! intent(in): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - eqns_data%mpar_data, & ! intent(in): model parameters - eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - eqns_data%indx_data, & ! intent(inou): index data - eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU - eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input: baseflow - eqns_data%dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! output - eqns_data%dMat, & ! intetn(inout): diagonal of the Jacobian matrix - Jac, & ! intent(out): Jacobian matrix - eqns_data%err,eqns_data%message) ! intent(out): error control - - if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif - if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif - - ! return success - ierr = 0 - return - - - -end function evalJac4IDA - - -end module evalJac4IDA_module diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index 56afbc728..9ae0adb6a 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -1467,7 +1467,7 @@ subroutine surfaceFlx(& dInfilRate_dTk(1:nSoil) = 0._rkind endif - ! dq w.r.t. infiltration only, scalarRainPlusMelt accounted for in computeJacob module + ! dq w.r.t. infiltration only, scalarRainPlusMelt accounted for in computJacob module dq_dHydStateVec(:) = (1._rkind - scalarFrozenArea) * ( dInfilArea_dWat(:)*min(scalarRainPlusMelt,xMaxInfilRate) + scalarInfilArea*dInfilRate_dWat(:) ) +& (-dFrozenArea_dWat(:))*scalarInfilArea*min(scalarRainPlusMelt,xMaxInfilRate) dq_dNrgStateVec(:) = (1._rkind - scalarFrozenArea) * ( dInfilArea_dTk(:) *min(scalarRainPlusMelt,xMaxInfilRate) + scalarInfilArea*dInfilRate_dTk(:) ) +& diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 5530f62aa..d4e60f15a 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -143,10 +143,10 @@ subroutine summaSolveSundialsIDA( & USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver USE allocspace_module,only:allocLocal ! allocate local data structures - USE evalDAE4IDA_module,only:evalDAE4IDA ! DAE/ODE functions - USE evalJac4IDA_module,only:evalJac4IDA ! system Jacobian + USE eval8summaSundials_module,only:eval8summa4IDA ! DAE/ODE functions + USE eval8summaSundials_module,only:eval8summaSundials ! residual of DAE + USE computJacobSundials_module,only:computJacob4IDA ! system Jacobian USE tol4IDA_module,only:computWeight4IDA ! weigth required for tolerances - USE eval8summaSundials_module,only:eval8summaSundials ! residual of DAE USE var_derive_module,only:calcHeight ! height at layer interfaces and layer mid-point !======= Declarations ========= @@ -331,7 +331,7 @@ subroutine summaSolveSundialsIDA( & ! Initialize memory t0 = 0._rkind - retval = FIDAInit(ida_mem, c_funloc(evalDAE4IDA), t0, sunvec_y, sunvec_yp) + retval = FIDAInit(ida_mem, c_funloc(eval8summa4IDA), t0, sunvec_y, sunvec_yp) if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDAInit'; return; endif ! set tolerances @@ -370,7 +370,7 @@ subroutine summaSolveSundialsIDA( & ! Set the user-supplied Jacobian routine !comment this line out to use FD Jacobian - retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) + !retval = FIDASetJacFn(ida_mem, c_funloc(computJacob4IDA)) if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetJacFn'; return; endif ! Create Newton SUNNonlinearSolver object From 0c817019d9cb830a207ac4be259b16817017fa2f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 3 Oct 2022 12:27:27 +0900 Subject: [PATCH 0391/1472] added dt to computResidSundials for consistency with non-Sundials version --- build/source/engine/computResidSundials.f90 | 18 ++++++++++-------- build/source/engine/eval8summaSundials.f90 | 8 ++++++-- build/source/engine/summaSolveSundialsIDA.f90 | 1 - 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/build/source/engine/computResidSundials.f90 b/build/source/engine/computResidSundials.f90 index ac620aa8f..05bab6bad 100644 --- a/build/source/engine/computResidSundials.f90 +++ b/build/source/engine/computResidSundials.f90 @@ -58,6 +58,7 @@ module computResidSundials_module ! ********************************************************************************************************** subroutine computResidSundials(& ! input: model control + dt, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers @@ -92,6 +93,7 @@ subroutine computResidSundials(& ! -------------------------------------------------------------------------------------------------------------------------------- implicit none ! input: model control + real(rkind),intent(in) :: dt ! length of the time step (seconds) integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain @@ -190,7 +192,7 @@ subroutine computResidSundials(& ! NOTE 4: same sink terms for matric head and liquid matric potential if(nSoilOnlyHyd>0)then do concurrent (iLayer=1:nSoil,ixSoilOnlyHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) - rAdd( ixSoilOnlyHyd(iLayer) ) = rAdd( ixSoilOnlyHyd(iLayer) ) + (mLayerTranspire(iLayer) - mLayerBaseflow(iLayer) )/mLayerDepth(iLayer+nSnow) - mLayerCompress(iLayer) + rAdd( ixSoilOnlyHyd(iLayer) ) = rAdd( ixSoilOnlyHyd(iLayer) ) + dt*(mLayerTranspire(iLayer) - mLayerBaseflow(iLayer) )/mLayerDepth(iLayer+nSnow) - mLayerCompress(iLayer) end do ! looping through non-missing energy state variables in the snow+soil domain endif @@ -201,18 +203,18 @@ subroutine computResidSundials(& ! compute the residual vector for the vegetation canopy ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision ! --> energy balance - if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*scalarCanairTempPrime - ( fVec(ixCasNrg) + rAdd(ixCasNrg) ) - if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime + scalarCanopyCmTrial * scalarCanopyWatPrime/canopyDepth - ( fVec(ixVegNrg) + rAdd(ixVegNrg) ) + if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*scalarCanairTempPrime - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime + scalarCanopyCmTrial * scalarCanopyWatPrime/canopyDepth - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) ! --> mass balance if(ixVegHyd/=integerMissing)then scalarCanopyHydPrime = merge(scalarCanopyWatPrime, scalarCanopyLiqPrime, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) - rVec(ixVegHyd) = sMul(ixVegHyd)*scalarCanopyHydPrime - ( fVec(ixVegHyd) + rAdd(ixVegHyd) ) + rVec(ixVegHyd) = sMul(ixVegHyd)*scalarCanopyHydPrime - ( fVec(ixVegHyd)*dt + rAdd(ixVegHyd) ) endif ! compute the residual vector for the snow and soil sub-domains for energy if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) ) * mLayerTempPrime(iLayer) + mLayerCmTrial(iLayer) * mLayerVolFracWatPrime(iLayer) - ( fVec( ixSnowSoilNrg(iLayer) ) + rAdd( ixSnowSoilNrg(iLayer) ) ) + rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) ) * mLayerTempPrime(iLayer) + mLayerCmTrial(iLayer) * mLayerVolFracWatPrime(iLayer) - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) end do ! looping through non-missing energy state variables in the snow+soil domain endif @@ -223,12 +225,12 @@ subroutine computResidSundials(& ! (get the correct state variable) mLayerVolFracHydPrime(iLayer) = merge(mLayerVolFracWatPrime(iLayer), mLayerVolFracLiqPrime(iLayer), (ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_matLayer) ) ! (compute the residual) - rVec( ixSnowSoilHyd(iLayer) ) = mLayerVolFracHydPrime(iLayer) - ( fVec( ixSnowSoilHyd(iLayer) ) + rAdd( ixSnowSoilHyd(iLayer) ) ) + rVec( ixSnowSoilHyd(iLayer) ) = mLayerVolFracHydPrime(iLayer) - ( fVec( ixSnowSoilHyd(iLayer) )*dt + rAdd( ixSnowSoilHyd(iLayer) ) ) end do ! looping through non-missing energy state variables in the snow+soil domain endif ! compute the residual vector for the aquifer - if(ixAqWat/=integerMissing) rVec(ixAqWat) = sMul(ixAqWat)*scalarAquiferStoragePrime - ( fVec(ixAqWat) + rAdd(ixAqWat) ) + if(ixAqWat/=integerMissing) rVec(ixAqWat) = sMul(ixAqWat)*scalarAquiferStoragePrime - ( fVec(ixAqWat)*dt + rAdd(ixAqWat) ) ! print result if(globalPrintFlag)then @@ -335,7 +337,7 @@ subroutine printResidDAE( & if(ixAqWat/=integerMissing) print *, ' rVec(ixAqWat) = ', rVec(ixAqWat) - end associate + end associate end subroutine printResidDAE diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index b28032dbf..afa26b4bb 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -172,8 +172,8 @@ subroutine eval8summaSundials(& ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control - real(rkind),intent(in) :: dt_cur - real(rkind),intent(in) :: dt ! time step + real(rkind),intent(in) :: dt_cur ! current stepsize + real(rkind),intent(in) :: dt ! entire time step integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers @@ -239,6 +239,7 @@ subroutine eval8summaSundials(& ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- + real(rkind) :: dt1 ! residual step size ! state variables real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) @@ -709,9 +710,12 @@ subroutine eval8summaSundials(& err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + dt1 = 1._qp ! always 1 for inside sundials ! compute the residual vector call computResidSundials(& ! input: model control + dt1, & ! intent(in): length of the residual time step (seconds) nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index d4e60f15a..9e1dbf556 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -210,7 +210,6 @@ subroutine summaSolveSundialsIDA( & type(c_ptr) :: ida_mem ! IDA memory type(eqnsData), target :: eqns_data ! IDA type integer(i4b) :: retval, retvalr ! return value - integer(i4b) :: rootsfound(3) ! crossing direction of discontinuities logical(lgt) :: feasible ! feasibility flag real(qp) :: t0 ! staring time real(qp) :: dt_last(1) ! last time step From c02b6798fb842b1a4724e65c1e3987d3ecc149ec Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 4 Oct 2022 08:52:39 +0900 Subject: [PATCH 0392/1472] Was running FD jacobian, make run analytical. --- build/source/engine/summaSolveSundialsIDA.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 9e1dbf556..f46836e8e 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -369,7 +369,7 @@ subroutine summaSolveSundialsIDA( & ! Set the user-supplied Jacobian routine !comment this line out to use FD Jacobian - !retval = FIDASetJacFn(ida_mem, c_funloc(computJacob4IDA)) + retval = FIDASetJacFn(ida_mem, c_funloc(computJacob4IDA)) if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetJacFn'; return; endif ! Create Newton SUNNonlinearSolver object From f5c712b0ac0f97187d2f0d0297f3bef572c3cb5a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 4 Oct 2022 19:46:31 +0900 Subject: [PATCH 0393/1472] get rid of getVectorzAddSundials.f90, nothing here used --- build/my_makefile_cop | 1 - build/my_makefile_gra | 1 - build/my_makefile_mac | 1 - build/my_makefile_ric | 1 - build/source/engine/getVectorzAddSundials.f90 | 245 ------------------ 5 files changed, 249 deletions(-) delete mode 100644 build/source/engine/getVectorzAddSundials.f90 diff --git a/build/my_makefile_cop b/build/my_makefile_cop index 1b20f582b..261eecff4 100644 --- a/build/my_makefile_cop +++ b/build/my_makefile_cop @@ -269,7 +269,6 @@ NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) SUMMA_MODRUN = \ indexState.f90 \ getVectorz.f90 \ - getVectorzAddSundials.f90 \ t2enthalpy.f90 \ updateVars.f90 \ updateVarsSundials.f90 \ diff --git a/build/my_makefile_gra b/build/my_makefile_gra index a63e6dd85..3177fffd8 100644 --- a/build/my_makefile_gra +++ b/build/my_makefile_gra @@ -269,7 +269,6 @@ NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) SUMMA_MODRUN = \ indexState.f90 \ getVectorz.f90 \ - getVectorzAddSundials.f90 \ t2enthalpy.f90 \ updateVars.f90 \ updateVarsSundials.f90 \ diff --git a/build/my_makefile_mac b/build/my_makefile_mac index 114f40c21..b297da990 100644 --- a/build/my_makefile_mac +++ b/build/my_makefile_mac @@ -269,7 +269,6 @@ NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) SUMMA_MODRUN = \ indexState.f90 \ getVectorz.f90 \ - getVectorzAddSundials.f90 \ t2enthalpy.f90 \ updateVars.f90 \ updateVarsSundials.f90 \ diff --git a/build/my_makefile_ric b/build/my_makefile_ric index 5bb604433..7c2b5b7e7 100644 --- a/build/my_makefile_ric +++ b/build/my_makefile_ric @@ -269,7 +269,6 @@ NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) SUMMA_MODRUN = \ indexState.f90 \ getVectorz.f90 \ - getVectorzAddSundials.f90 \ t2enthalpy.f90 \ updateVars.f90 \ updateVarsSundials.f90 \ diff --git a/build/source/engine/getVectorzAddSundials.f90 b/build/source/engine/getVectorzAddSundials.f90 deleted file mode 100644 index 83545e2c9..000000000 --- a/build/source/engine/getVectorzAddSundials.f90 +++ /dev/null @@ -1,245 +0,0 @@ -module getVectorzAddSundials_module - -! data types -USE nrtype - -! missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing real number - -! access the global print flag -USE globalData,only:globalPrintFlag - -! domain types -USE globalData,only:iname_cas ! named variables for canopy air space -USE globalData,only:iname_veg ! named variables for vegetation canopy -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil - -! named variables to describe the state variable type -USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space -USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy -USE globalData,only:iname_watCanopy ! named variable defining the mass of total water on the vegetation canopy -USE globalData,only:iname_liqCanopy ! named variable defining the mass of liquid water on the vegetation canopy -USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers -USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers -USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers -USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers -USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers -USE globalData,only:iname_watAquifer ! named variable defining the water storage in the aquifer - -! metadata for information in the data structures -USE globalData,only:indx_meta ! metadata for the variables in the index structure - -! constants -USE multiconst,only:& - gravity, & ! acceleration of gravity (m s-2) - Tfreeze, & ! temperature at freezing (K) - Cp_air, & ! specific heat of air (J kg-1 K-1) - LH_fus, & ! latent heat of fusion (J kg-1) - iden_air, & ! intrinsic density of air (kg m-3) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water ! intrinsic density of liquid water (kg m-3) - -! provide access to the derived types to define the data structures -USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (rkind) - -! provide access to indices that define elements of the data structures -USE var_lookup,only:iLookDIAG ! named variables for structure elements -USE var_lookup,only:iLookPROG ! named variables for structure elements -USE var_lookup,only:iLookDERIV ! named variables for structure elements -USE var_lookup,only:iLookPARAM ! named variables for structure elements -USE var_lookup,only:iLookINDEX ! named variables for structure elements - -! provide access to routines to update states -USE updatState_module,only:updateSnow ! update snow states -USE updatState_module,only:updateSoil ! update soil states - -! provide access to functions for the constitutive functions and derivatives -USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) -USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) -USE soil_utils_module,only:dTheta_dTk ! differentiate the freezing curve w.r.t. temperature (soil) -USE soil_utils_module,only:dTheta_dPsi ! derivative in the soil water characteristic (soil) -USE soil_utils_module,only:dPsi_dTheta ! derivative in the soil water characteristic (soil) -USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content -USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water -USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists -USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential - -implicit none -private -public::residDiscontinuity -public::countDiscontinuity -contains - - - -! ********************************************************************************************************** -! public subroutine residDiscontinuity: -! ********************************************************************************************************** -subroutine residDiscontinuity(& - ! input - stateVec, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output - resid, & ! intent(out) - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input - real(rkind),intent(in) :: stateVec(:) ! model state vector (mixed units) - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - real(qp),intent(out) :: resid(:) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - integer(i4b) :: iCount - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - associate(& - ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers - ! indices defining model states and layers - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of the squifer storage state variable - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for energy states in the snow+soil subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for hydrology states in the snow+soil subdomain - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain - ! indices defining type of model state variables - ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] type of desired model state variables - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat & ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain - )! association with variables in the data structures - - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - - ! initialize error control - err=0; message='residDiscontinuity/' - - iCount = 1 - - - ! check if computing the vegetation flux - if(ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing)then - - ! temperature of the canopy air space - if(ixCasNrg/=integerMissing)then - resid(iCount) = stateVec(ixCasNrg) - Tfreeze ! scalarCanairTempTrial - Tfreeze - iCount = iCount + 1 - endif - - ! canopy temperature - if(ixVegNrg/=integerMissing)then - resid(iCount) = stateVec(ixVegNrg) - Tfreeze ! scalarCanopyTempTrial - Tfreeze - iCount = iCount + 1 - endif - - endif ! not computing the vegetation flux - - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - resid( iCount ) = stateVec( ixSnowSoilNrg(iLayer) ) - Tfreeze ! mLayerTempTrial(iLayer) - Tfreeze - iCount = iCount + 1 - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - end associate -end subroutine residDiscontinuity - -! ********************************************************************************************************** -! public subroutine countDiscontinuity: -! ********************************************************************************************************** -subroutine countDiscontinuity(& - ! input - stateVec, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output - countD, & ! intent(out) - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input - real(rkind),intent(in) :: stateVec(:) ! model state vector (mixed units) - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - integer(i4b),intent(out) :: countD - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - associate(& - ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers - ! indices defining model states and layers - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of the squifer storage state variable - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for energy states in the snow+soil subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices IN THE STATE SUBSET for hydrology states in the snow+soil subdomain - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain - ! indices defining type of model state variables - ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] type of desired model state variables - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat & ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain - )! association with variables in the data structures - - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - - ! initialize error control - err=0; message='countDiscontinuity/' - - ! *** extract state variables for the vegetation canopy - - countD = 0 - ! check if computing the vegetation flux - if(ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing)then - - ! temperature of the canopy air space - if(ixCasNrg/=integerMissing) countD = countD + 1 - - ! canopy temperature - if(ixVegNrg/=integerMissing) countD = countD + 1 - - - endif ! not computing the vegetation flux - - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - countD = countD + 1 - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - end associate - -end subroutine countDiscontinuity - -end module getVectorzAddSundials_module From 78021dfa9b07032e0ca8c317ccb717a46bbe47c6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 4 Oct 2022 20:00:26 +0900 Subject: [PATCH 0394/1472] initialize varExtract in all routines, it should be inout not just out for state variables --- build/source/engine/computJacobSundials.f90 | 70 +++++++++++++------ build/source/engine/eval8summa.f90 | 34 ++++----- build/source/engine/eval8summaSundials.f90 | 77 ++++++++++++--------- build/source/engine/getVectorz.f90 | 38 +++++----- build/source/engine/varSubstep.f90 | 34 ++++----- build/source/engine/varSubstepSundials.f90 | 59 ++++++++++------ 6 files changed, 185 insertions(+), 127 deletions(-) diff --git a/build/source/engine/computJacobSundials.f90 b/build/source/engine/computJacobSundials.f90 index 524bdedaa..518c4f2a2 100644 --- a/build/source/engine/computJacobSundials.f90 +++ b/build/source/engine/computJacobSundials.f90 @@ -1142,6 +1142,21 @@ subroutine computJacobSetup(& ! initialize error control err=0; message="computJacobSetup/" + ! initialize to state variable from the last update + ! should all be set to previous values if splits, but for now operator splitting is not hooked up + scalarCanairTempTrial = realMissing + scalarCanopyTempTrial = realMissing + scalarCanopyWatTrial = realMissing + scalarCanopyLiqTrial = realMissing + scalarCanopyIceTrial = realMissing + mLayerTempTrial = realMissing + mLayerVolFracWatTrial = realMissing + mLayerVolFracLiqTrial = realMissing + mLayerVolFracIceTrial = realMissing + mLayerMatricHeadTrial = realMissing + mLayerMatricHeadLiqTrial = realMissing + scalarAquiferStorageTrial = realMissing + ! extract variables from the model state vector call varExtract(& ! input @@ -1150,22 +1165,37 @@ subroutine computJacobSetup(& prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) + scalarAquiferStorageTrial,& ! intent(inout): trial value of storage of water in the aquifer (m) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + ! initialize to state variable from the last update + ! should all be set to previous values if splits, but for now operator splitting is not hooked up + scalarCanairTempPrime = realMissing + scalarCanopyTempPrime = realMissing + scalarCanopyWatPrime = realMissing + scalarCanopyLiqPrime = realMissing + scalarCanopyIcePrime = realMissing + mLayerTempPrime = realMissing + mLayerVolFracWatPrime = realMissing + mLayerVolFracLiqPrime = realMissing + mLayerVolFracIcePrime = realMissing + mLayerMatricHeadPrime = realMissing + mLayerMatricHeadLiqPrime = realMissing + scalarAquiferStoragePrime = realMissing + ! extract derivative of variables from derivative of the model state vector call varExtract(& ! input @@ -1174,18 +1204,18 @@ subroutine computJacobSetup(& prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy - scalarCanairTempPrime, & ! intent(out): derivative of canopy air temperature (K) - scalarCanopyTempPrime, & ! intent(out): derivative of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(out): derivative of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(out): derivative of canopy liquid water (kg m-2) + scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K) + scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): derivative of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - mLayerTempPrime, & ! intent(out): derivative of layer temperature (K) - mLayerVolFracWatPrime, & ! intent(out): derivative of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(out): derivative of volumetric liquid water content (-) - mLayerMatricHeadPrime, & ! intent(out): derivative of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(out): derivative of liquid water matric potential (m) + mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K) + mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (-) + mLayerMatricHeadPrime, & ! intent(inout): derivative of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): derivative of liquid water matric potential (m) ! output: variables for the aquifer - scalarAquiferStoragePrime,& ! intent(out): derivative of storage of water in the aquifer (m) + scalarAquiferStoragePrime,& ! intent(inout): derivative of storage of water in the aquifer (m) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 8c94ec464..ddf3ab697 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -367,17 +367,17 @@ subroutine eval8summa(& endif ! initialize to state variable from the last update - scalarCanairTempTrial = scalarCanairTemp - scalarCanopyTempTrial = scalarCanopyTemp - scalarCanopyWatTrial = scalarCanopyWat - scalarCanopyLiqTrial = scalarCanopyLiq - scalarCanopyIceTrial = scalarCanopyIce + scalarCanairTempTrial = scalarCanairTemp + scalarCanopyTempTrial = scalarCanopyTemp + scalarCanopyWatTrial = scalarCanopyWat + scalarCanopyLiqTrial = scalarCanopyLiq + scalarCanopyIceTrial = scalarCanopyIce mLayerTempTrial = mLayerTemp mLayerVolFracWatTrial = mLayerVolFracWat mLayerVolFracLiqTrial = mLayerVolFracLiq mLayerVolFracIceTrial = mLayerVolFracIce - mLayerMatricHeadTrial = mLayerMatricHead ! total water matric potential - mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq ! liquid water matric potential + mLayerMatricHeadTrial = mLayerMatricHead + mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq scalarAquiferStorageTrial = scalarAquiferStorage ! extract variables from the model state vector @@ -388,18 +388,18 @@ subroutine eval8summa(& prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) + scalarAquiferStorageTrial,& ! intent(inout): trial value of storage of water in the aquifer (m) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index afa26b4bb..fd7ba5ef2 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -409,7 +409,7 @@ subroutine eval8summaSundials(& mLayerMatricHeadTrial = mLayerMatricHeadPrev scalarAquiferStorageTrial = scalarAquiferStoragePrev - ! extract variables from the model state vector + ! extract states from the state vector call varExtract(& ! input stateVec, & ! intent(in): model state vector (mixed units) @@ -417,23 +417,37 @@ subroutine eval8summaSundials(& prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) + scalarAquiferStorageTrial,& ! intent(inout): trial value of storage of water in the aquifer (m) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! Extract the variables for stateVecPrime + ! initialize to state variable from the last update + ! should all be set to previous values if splits, but for now operator splitting is not hooked up + scalarCanairTempPrime = realMissing + scalarCanopyTempPrime = realMissing + scalarCanopyWatPrime = realMissing + scalarCanopyLiqPrime = realMissing + scalarCanopyIcePrime = realMissing + mLayerTempPrime = realMissing + mLayerVolFracWatPrime = realMissing + mLayerVolFracLiqPrime = realMissing + mLayerVolFracIcePrime = realMissing + mLayerMatricHeadPrime = realMissing + mLayerMatricHeadLiqPrime = realMissing + scalarAquiferStoragePrime = realMissing + call varExtract(& ! input stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) @@ -441,23 +455,22 @@ subroutine eval8summaSundials(& prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy - scalarCanairTempPrime, & ! intent(out): derivative of canopy air temperature (K) - scalarCanopyTempPrime, & ! intent(out): derivative of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(out): derivative of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(out): derivative of canopy liquid water (kg m-2) + scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K) + scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(niout): derivative of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - mLayerTempPrime, & ! intent(out): derivative of layer temperature (K) - mLayerVolFracWatPrime, & ! intent(out): derivative of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(out): derivative of volumetric liquid water content (-) - mLayerMatricHeadPrime, & ! intent(out): derivative of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(out): derivative of liquid water matric potential (m) + mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K) + mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (-) + mLayerMatricHeadPrime, & ! intent(inout): derivative of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): derivative of liquid water matric potential (m) ! output: variables for the aquifer - scalarAquiferStoragePrime,& ! intent(out): derivative of storage of water in the aquifer (m) + scalarAquiferStoragePrime,& ! intent(inout): derivative of storage of water in the aquifer (m) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - call updateVarsSundials(& ! input dt_cur, & @@ -475,10 +488,10 @@ subroutine eval8summaSundials(& scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2) + scalarCanopyTempPrime, & ! intent(inout): trial value of time derivative canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): trial value of time derivative canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): trial value of time derivative canopy liquid water (kg m-2) + scalarCanopyIcePrime, & ! intent(inout): trial value of time derivative canopy ice content (kg m-2) ! output: variables for the snow-soil domain mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) @@ -487,11 +500,11 @@ subroutine eval8summaSundials(& mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) mLayerTempPrime, & ! - mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) - mLayerVolFracIcePrime, & ! - mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) + mLayerVolFracWatPrime, & ! intent(inout): trial vector of time derivative volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): trial vector of time derivative volumetric liquid water content (-) + mLayerVolFracIcePrime, & ! intent(inout): trial vector of time derivative volumetric ice water content (-) + mLayerMatricHeadPrime, & ! intent(inout): trial vector of time derivative total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): trial vector of time derivative liquid water matric potential (m) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index 7fa99b73f..61bbae0cc 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -408,16 +408,16 @@ subroutine varExtract(& prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) ! output: variables for the aquifer scalarAquiferStorageTrial, & ! intent(out): trial value of storage of water in the aquifer (m) ! output: error control @@ -431,18 +431,18 @@ subroutine varExtract(& type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers ! output: variables for the vegetation canopy - real(rkind),intent(out) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) - real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind),intent(out) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - real(rkind),intent(out) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) + real(rkind),intent(inout) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) + real(rkind),intent(inout) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + real(rkind),intent(inout) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(out) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(rkind),intent(out) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind),intent(out) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) + real(rkind),intent(inout) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(inout) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(rkind),intent(inout) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(inout) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) ! output: variables for the aquifer - real(rkind),intent(out) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + real(rkind),intent(inout) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 98a87ac87..ab164f89e 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -666,17 +666,17 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! ------------------ ! initialize to state variable from the last update - scalarCanairTempTrial = scalarCanairTemp - scalarCanopyTempTrial = scalarCanopyTemp - scalarCanopyWatTrial = scalarCanopyWat - scalarCanopyLiqTrial = scalarCanopyLiq - scalarCanopyIceTrial = scalarCanopyIce + scalarCanairTempTrial = scalarCanairTemp + scalarCanopyTempTrial = scalarCanopyTemp + scalarCanopyWatTrial = scalarCanopyWat + scalarCanopyLiqTrial = scalarCanopyLiq + scalarCanopyIceTrial = scalarCanopyIce mLayerTempTrial = mLayerTemp mLayerVolFracWatTrial = mLayerVolFracWat mLayerVolFracLiqTrial = mLayerVolFracLiq mLayerVolFracIceTrial = mLayerVolFracIce - mLayerMatricHeadTrial = mLayerMatricHead ! total water matric potential - mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq ! liquid water matric potential + mLayerMatricHeadTrial = mLayerMatricHead + mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq scalarAquiferStorageTrial = scalarAquiferStorage ! extract states from the state vector @@ -687,18 +687,18 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) + scalarAquiferStorageTrial,& ! intent(inout): trial value of storage of water in the aquifer (m) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index 1d073403c..d6c541a1b 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -692,8 +692,8 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux mLayerVolFracWatTrial = mLayerVolFracWat mLayerVolFracLiqTrial = mLayerVolFracLiq mLayerVolFracIceTrial = mLayerVolFracIce - mLayerMatricHeadTrial = mLayerMatricHead ! total water matric potential - mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq ! liquid water matric potential + mLayerMatricHeadTrial = mLayerMatricHead + mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq scalarAquiferStorageTrial = scalarAquiferStorage ! extract states from the state vector @@ -704,22 +704,37 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(out): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) + scalarAquiferStorageTrial,& ! intent(inout): trial value of storage of water in the aquifer (m) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + ! initialize to state variable from the last update + ! should all be set to previous values if splits, but for now operator splitting is not hooked up + scalarCanairTempPrime = realMissing + scalarCanopyTempPrime = realMissing + scalarCanopyWatPrime = realMissing + scalarCanopyLiqPrime = realMissing + scalarCanopyIcePrime = realMissing + mLayerTempPrime = realMissing + mLayerVolFracWatPrime = realMissing + mLayerVolFracLiqPrime = realMissing + mLayerVolFracIcePrime = realMissing + mLayerMatricHeadPrime = realMissing + mLayerMatricHeadLiqPrime = realMissing + scalarAquiferStoragePrime = realMissing + call varExtract(& ! input stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) @@ -727,18 +742,18 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy - scalarCanairTempPrime, & ! intent(out): derivative of canopy air temperature (K) - scalarCanopyTempPrime, & ! intent(out): derivative of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(out): derivative of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(out): derivative of canopy liquid water (kg m-2) + scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K) + scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): derivative of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - mLayerTempPrime, & ! intent(out): derivative of layer temperature (K) - mLayerVolFracWatPrime, & ! intent(out): derivative of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(out): derivative of volumetric liquid water content (-) - mLayerMatricHeadPrime, & ! intent(out): derivative of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(out): derivative of liquid water matric potential (m) + mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K) + mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (-) + mLayerMatricHeadPrime, & ! intent(inout): derivative of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): derivative of liquid water matric potential (m) ! output: variables for the aquifer - scalarAquiferStoragePrime,& ! intent(out): derivative of storage of water in the aquifer (m) + scalarAquiferStoragePrime,& ! intent(inout): derivative of storage of water in the aquifer (m) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) From ac59434ff80c63356f9ecc6e035d8da53b5f4ac9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 4 Oct 2022 20:04:05 +0900 Subject: [PATCH 0395/1472] eval8summaSundials call inside systemSolvSundials, trying with flag on and off (works here with the flag oldway=.true., so off) --- build/source/engine/eval8summaSundials.f90 | 189 +++++++++--------- build/source/engine/summaSolveSundialsIDA.f90 | 65 +++--- build/source/engine/systemSolvSundials.f90 | 167 +++++++++++----- build/source/engine/varSubstepSundials.f90 | 12 +- 4 files changed, 261 insertions(+), 172 deletions(-) diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index fd7ba5ef2..89a623404 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -8,7 +8,6 @@ module eval8summaSundials_module USE globalData,only:integerMissing ! missing integer USE globalData,only:realMissing ! missing double precision number USE globalData,only:quadMissing ! missing quadruple precision number -USE globalData,only:flux_meta ! metadata on the model fluxes ! access the global print flag USE globalData,only:globalPrintFlag @@ -100,14 +99,14 @@ subroutine eval8summaSundials(& nState, & ! intent(in): total number of state variables insideIDA, & ! intent(in): flag to indicate if we are inside Sundials solver firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout) flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(inout) flag to indicate if we are processing the first flux call in a splitting operation + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVec, & ! intent(in): model state vector stateVecPrime, & ! intent(in): derivative of model state vector - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures model_decisions, & ! intent(in): model decisions lookup_data, & ! intent(in): lookup data @@ -122,20 +121,19 @@ subroutine eval8summaSundials(& diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + ! input-output: here we need to pass some extra variables that do not get updated in in the Sundials loops scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) scalarCanopyTempPrev, & ! intent(in): value of canopy temperature (K) scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) + scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) scalarCanopyEnthalpyPrev,& ! intent(in): value for enthalpy of the vegetation canopy (J m-3) mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) mLayerTempPrev, & ! intent(in): vector of layer temperature (K) mLayerMatricHeadLiqTrial,& ! intent(out): trial value for liquid water matric potential (m) - mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) + mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) mLayerVolFracWatPrev, & ! intent(in): vector of volumetric total water content (-) @@ -144,10 +142,13 @@ subroutine eval8summaSundials(& mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) - scalarAquiferStoragePrev,& ! intent(in): value of storage of water in the aquifer (m) + scalarAquiferStoragePrev,& ! intent(in): value of storage of water in the aquifer (m) mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) + ! input-output: baseflow ixSaturation, & ! intent(inout): index of the lowest saturated layer + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + ! output: flux and residual vectors feasible, & ! intent(out): flag to denote the feasibility of the solution fluxVec, & ! intent(out): flux vector resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation @@ -155,16 +156,16 @@ subroutine eval8summaSundials(& err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- ! provide access to subroutines - USE getVectorz_module, only:varExtract ! extract variables from the state vector - USE updateVarsSundials_module, only:updateVarsSundials ! update variables - USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy - USE computFlux_module, only:soilCmpresSundials ! compute soil compression - USE computFlux_module, only:computFlux ! compute fluxes given a state vector - USE computHeatCap_module,only:computHeatCap ! compute heat capacity - USE computHeatCap_module,only:computHeatCapAnalytic ! compute heat capacity + USE getVectorz_module, only:varExtract ! extract variables from the state vector + USE updateVarsSundials_module, only:updateVarsSundials ! update variables + USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy + USE computFlux_module, only:soilCmpresSundials ! compute soil compression + USE computFlux_module, only:computFlux ! compute fluxes given a state vector + USE computHeatCap_module,only:computHeatCap ! compute heat capacity + USE computHeatCap_module,only:computHeatCapAnalytic ! compute heat capacity USE computHeatCap_module,only:computCm USE computHeatCap_module, only:computStatMult - USE computResidSundials_module,only:computResidSundials ! compute residuals given a state vector + USE computResidSundials_module,only:computResidSundials ! compute residuals given a state vector USE computThermConduct_module,only:computThermConduct USE computEnthalpy_module,only:computEnthalpy USE computEnthalpy_module,only:computEnthalpyPrime @@ -178,7 +179,7 @@ subroutine eval8summaSundials(& integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers integer,intent(in) :: nState ! total number of state variables - logical(lgt),intent(in) :: insideIDA ! flag to indicate if we are inside Sundials solver + logical(lgt),intent(in) :: insideIDA ! flag to indicate if we are inside Sundials solver logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(inout) :: firstFluxCall logical(lgt),intent(inout) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation @@ -202,9 +203,7 @@ subroutine eval8summaSundials(& type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - ! output: flux and residual vectors + ! input-output: here we need to pass some extra variables that do not get updated in in the Sundials loops real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) real(rkind),intent(in) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) real(rkind),intent(out) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) @@ -228,7 +227,10 @@ subroutine eval8summaSundials(& real(rkind),intent(in) :: scalarAquiferStoragePrev ! value of storage of water in the aquifer (m) real(rkind),intent(in) :: mLayerEnthalpyPrev(:) ! vector of enthalpy for snow+soil layers (J m-3) real(rkind),intent(out) :: mLayerEnthalpyTrial(:) ! trial vector of enthalpy for snow+soil layers (J m-3) - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer + ! input-output: baseflow + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer + real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + ! output: flux and residual vectors logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution real(rkind),intent(out) :: fluxVec(:) ! flux vector real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation @@ -239,11 +241,11 @@ subroutine eval8summaSundials(& ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- - real(rkind) :: dt1 ! residual step size + real(rkind) :: dt1 ! residual step size ! state variables - real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - ! derivative of state variables + real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + ! derivative of state variables real(rkind) :: scalarCanairTempPrime ! derivative value for temperature of the canopy air space (K) real(rkind) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) real(rkind) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) @@ -258,20 +260,20 @@ subroutine eval8summaSundials(& real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! derivative value for volumetric fraction of liquid water (-) real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! derivative value for volumetric fraction of ice (-) ! enthalpy - real(rkind) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthalpyPrime ! enthalpy of each snow+soil layer (J m-3) + real(rkind) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpyPrime ! enthalpy of each snow+soil layer (J m-3) ! other local variables - integer(i4b) :: iLayer ! index of model layer in the snow+soil domain - integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain - integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine - integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) - real(rkind) :: xMin,xMax ! minimum and maximum values for water content - real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) - character(LEN=256) :: cmessage ! error message of downwind routine - real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy - real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil - logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step - logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m + integer(i4b) :: iLayer ! index of model layer in the snow+soil domain + integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain + integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine + integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) + real(rkind) :: xMin,xMax ! minimum and maximum values for water content + real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) + character(LEN=256) :: cmessage ! error message of downwind routine + real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy + real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil + logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step + logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m ! -------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures @@ -309,16 +311,16 @@ subroutine eval8summaSundials(& ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) - heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & ! intent(out): heat capacity for snow and soil + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) + heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(out): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & ! intent(out): heat capacity for snow and soil ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="eval8summaSundials/" feasible=.true. - ! check the feasibility of the solution + ! check the feasibility of the solution only if not inside Sundials solver if (.not.insideIDA) then ! check that the canopy air space temperature is reasonable if(ixCasNrg/=integerMissing)then @@ -399,6 +401,7 @@ subroutine eval8summaSundials(& endif ! initialize to state variable from the last update + ! should all be set to previous values if splits, but for now operator splitting is not hooked up scalarCanopyTempTrial = scalarCanopyTempPrev scalarCanopyLiqTrial = scalarCanopyLiqPrev scalarCanopyIceTrial = scalarCanopyIcePrev @@ -449,26 +452,26 @@ subroutine eval8summaSundials(& scalarAquiferStoragePrime = realMissing call varExtract(& - ! input - stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy + ! input + stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K) scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K) scalarCanopyWatPrime, & ! intent(niout): derivative of canopy total water (kg m-2) scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain + ! output: variables for the snow-soil domain mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K) mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (-) mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (-) mLayerMatricHeadPrime, & ! intent(inout): derivative of total water matric potential (m) mLayerMatricHeadLiqPrime, & ! intent(inout): derivative of liquid water matric potential (m) - ! output: variables for the aquifer + ! output: variables for the aquifer scalarAquiferStoragePrime,& ! intent(inout): derivative of storage of water in the aquifer (m) - ! output: error control - err,cmessage) ! intent(out): error control + ! output: error control + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) call updateVarsSundials(& @@ -517,7 +520,7 @@ subroutine eval8summaSundials(& endif if(updateCp)then - ! *** compute volumetric heat capacity C_p + ! *** compute volumetric heat capacity C_p if(model_decisions(iLookDECISIONS%howHeatCap)%iDecision == enthalpyFD)then ! compute H_T call t2enthalpy_T(& @@ -547,17 +550,17 @@ subroutine eval8summaSundials(& ! *** compute volumetric heat capacity C_p = dH_T/dT call computHeatCap(& ! input: control variables - nLayers, & ! intent(in): number of layers (soil+snow) - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) + nLayers, & ! intent(in): number of layers (soil+snow) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + diag_data, & ! intent(in): model diagnostic variables for a local HRU ! input: state variables scalarCanopyIceTrial, & ! intent(in): trial value for canopy ice content (kg m-2) scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) @@ -572,7 +575,7 @@ subroutine eval8summaSundials(& mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil ! output: error control err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! to conserve energy compute finite difference approximation of (theta_ice)' if(dt_cur > 1e-14_rkind) then scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur @@ -583,21 +586,21 @@ subroutine eval8summaSundials(& else ! if using closed formula of heat capacity call computHeatCapAnalytic(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) ! input: state variables scalarCanopyIceTrial, & ! intent(in) - scalarCanopyLiqTrial, & ! intent(in) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): fraction of liquid water at the start of the sub-step (-) + scalarCanopyLiqTrial, & ! intent(in) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): fraction of liquid water at the start of the sub-step (-) ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices ! output heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control endif ! compute multiplier of state vector @@ -630,21 +633,20 @@ subroutine eval8summaSundials(& err,cmessage) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if - end if ! updateCp - + endif ! updateCp if(needCm)then ! compute C_m call computCm(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux ! input: state variables scalarCanopyTempTrial, & ! intent(in) - mLayerTempTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) mLayerMatricHeadTrial, & ! intent(in) ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices ! output scalarCanopyCmTrial, & ! intent(out): Cm for vegetation mLayerCmTrial, & ! intent(out): Cm for soil and snow @@ -652,7 +654,7 @@ subroutine eval8summaSundials(& else scalarCanopyCmTrial = 0._qp mLayerCmTrial = 0._qp - end if ! needCm + endif ! needCm ! save the number of flux calls per time step @@ -705,14 +707,13 @@ subroutine eval8summaSundials(& firstSplitOper = .true. - ! compute soil compressibility (-) and its derivative w.r.t. matric head (m) ! NOTE: we already extracted trial matrix head and volumetric liquid water as part of the flux calculations call soilCmpresSundials(& ! input: ixRichards, & ! intent(in): choice of option for Richards' equation ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers - mLayerMatricHeadPrime(1:nSoil), & ! intent(in): matric head at the start of the time step (m) + mLayerMatricHeadPrime(1:nSoil), & ! intent(in): matric head at the start of the time step (m) mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric ice content in each soil layer (-) specificStorage, & ! intent(in): specific storage coefficient (m-1) @@ -724,9 +725,14 @@ subroutine eval8summaSundials(& if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - dt1 = 1._qp ! always 1 for inside sundials ! compute the residual vector - call computResidSundials(& + if (insideIDA)then + dt1 = 1._qp ! always 1 for sundials since using Prime derivatives + !else + ! dt1 = 1._qp + !endif + + call computResidSundials(& ! input: model control dt1, & ! intent(in): length of the residual time step (seconds) nSnow, & ! intent(in): number of snow layers @@ -760,7 +766,11 @@ subroutine eval8summaSundials(& resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation resVec, & ! intent(out): residual vector err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + else !currently not using residuals outside Sundials! + dt1 = 1._qp + endif ! end association with the information in the data structures end associate @@ -830,9 +840,9 @@ integer(c_int) function eval8summa4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%nLayers, & ! intent(in): number of layers eqns_data%nState, & ! intent(in): number of state variables in the current subset .true., & ! intent(in): inside Sundials solver - eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation + eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors @@ -853,8 +863,7 @@ integer(c_int) function eval8summa4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian + ! input-output: here we need to pass some extra variables that do not get updated in in the Sundials loops eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) @@ -878,8 +887,10 @@ integer(c_int) function eval8summa4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) - eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer - ! output + ! input-output: baseflow + eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer + eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + ! output: flux and residual vectors feasible, & ! intent(out): flag to denote the feasibility of the solution eqns_data%fluxVec, & ! intent(out): flux vector eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index f46836e8e..cfb136914 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -369,7 +369,7 @@ subroutine summaSolveSundialsIDA( & ! Set the user-supplied Jacobian routine !comment this line out to use FD Jacobian - retval = FIDASetJacFn(ida_mem, c_funloc(computJacob4IDA)) + !retval = FIDASetJacFn(ida_mem, c_funloc(computJacob4IDA)) if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetJacFn'; return; endif ! Create Newton SUNNonlinearSolver object @@ -397,16 +397,16 @@ subroutine summaSolveSundialsIDA( & ! need the following values for the first substep eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) eqns_data%scalarCanopyIcePrev = prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) - eqns_data%scalarCanopyLiqPrev = prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) - eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) + eqns_data%scalarCanopyLiqPrev = prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) !!? + eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) - eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) - eqns_data%mLayerVolFracLiqPrev(:) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(:) + mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) - eqns_data%scalarAquiferStoragePrev = prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) + eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) + eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) + eqns_data%mLayerVolFracLiqPrev(:) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(:) !!? eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) - eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) - mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) + eqns_data%scalarAquiferStoragePrev = prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1)!!? eqns_data%ixSaturation = ixSaturation !********************************************************************************** @@ -469,33 +469,34 @@ subroutine summaSolveSundialsIDA( & eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output - eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later for Jacobian + ! input-output: here we need to pass some extra variables that do not get updated in in the Sundials loops eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) eqns_data%scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) eqns_data%scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) eqns_data%scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) - eqns_data%scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) - eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): value for enthalpy of the vegetation canopy (J m-3) - eqns_data%mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - eqns_data%mLayerTempPrev, & ! intent(in): vector of layer temperature (K) - eqns_data%mLayerMatricHeadLiqTrial, & ! intent(out): trial value for liquid water matric potential (m) - eqns_data%mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) - eqns_data%mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) - eqns_data%mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - eqns_data%mLayerVolFracWatPrev, & ! intent(in): vector of volumetric total water content (-) - eqns_data%mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) - eqns_data%mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) - eqns_data%mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - eqns_data%mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) - eqns_data%scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) - eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) - eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) - eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) + eqns_data%scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) + eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): value for enthalpy of the vegetation canopy (J m-3) + eqns_data%mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + eqns_data%mLayerTempPrev, & ! intent(in): vector of layer temperature (K) + eqns_data%mLayerMatricHeadLiqTrial, & ! intent(out): trial value for liquid water matric potential (m) + eqns_data%mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) + eqns_data%mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) + eqns_data%mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) + eqns_data%mLayerVolFracWatPrev, & ! intent(in): vector of volumetric total water content (-) + eqns_data%mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) + eqns_data%mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) + eqns_data%mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) + eqns_data%mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) + eqns_data%scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) + eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) + eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) + eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) + ! input-output: baseflow eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer - ! output + eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + ! output: flux and residual vectors feasible, & ! intent(out): flag to denote the feasibility of the solution eqns_data%fluxVec, & ! intent(out): flux vector eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation @@ -515,15 +516,15 @@ subroutine summaSolveSundialsIDA( & eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial eqns_data%scalarCanopyLiqPrev = eqns_data%scalarCanopyLiqTrial + eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) eqns_data%mLayerVolFracLiqPrev(:) = eqns_data%mLayerVolFracLiqTrial(:) - eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) - eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial + eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial enddo ! while loop on one_step mode until time dt @@ -549,8 +550,6 @@ subroutine summaSolveSundialsIDA( & deallocate( eqns_data%mLayerMatricHeadLiqTrial ) deallocate( eqns_data%mLayerMatricHeadTrial ) deallocate( eqns_data%mLayerMatricHeadPrev ) - deallocate( eqns_data%fluxVec ) - deallocate( eqns_data%resSink ) deallocate( eqns_data%mLayerVolFracWatTrial ) deallocate( eqns_data%mLayerVolFracWatPrev ) deallocate( eqns_data%mLayerVolFracIceTrial ) @@ -560,6 +559,8 @@ subroutine summaSolveSundialsIDA( & deallocate( eqns_data%mLayerVolFracLiqPrev ) deallocate( eqns_data%mLayerEnthalpyTrial ) deallocate( eqns_data%mLayerEnthalpyPrev ) + deallocate( eqns_data%fluxVec ) + deallocate( eqns_data%resSink ) call FIDAFree(ida_mem) retval = FSUNNonlinSolFree(sunnonlin_NLS) diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index bac6ba478..5bfc76b6f 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -100,7 +100,7 @@ subroutine systemSolvSundials(& nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to denote first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution ! input/output: data structures @@ -123,14 +123,14 @@ subroutine systemSolvSundials(& stateVecPrime, & ! intent(out): updated state vector reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out): flag to denote that there was too much melt - dt_out, & ! intent(out) + dt_out, & ! intent(out) err,message) ! intent(out): error code and error message ! --------------------------------------------------------------------------------------- ! structure allocations USE allocspace_module,only:allocLocal ! allocate local data structures ! simulation of fluxes and residuals given a trial state vector USE eval8summa_module,only:eval8summa ! simulation of fluxes and residuals given a trial state vector - USE eval8summaSundials_module,only:eval8summaSundials + USE eval8summaSundials_module,only:eval8summaSundials ! simulation of fluxes and residuals given a trial state vector USE getVectorz_module,only:getScaling ! get the scaling vectors USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy USE tol4IDA_module,only:popTol4IDA ! pop tolerances @@ -146,7 +146,7 @@ subroutine systemSolvSundials(& integer(i4b),intent(in) :: nState ! total number of state variables logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call - logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt),intent(inout) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input/output: data structures @@ -169,7 +169,7 @@ subroutine systemSolvSundials(& real(rkind),intent(out) :: stateVecPrime(:) ! trial state vector (mixed units) logical(lgt),intent(out) :: reduceCoupledStep ! flag to reduce the length of the coupled step logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that there was too much melt - real(qp),intent(out) :: dt_out ! time step + real(qp),intent(out) :: dt_out ! time step integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -181,9 +181,9 @@ subroutine systemSolvSundials(& integer(i4b) :: local_ixGroundwater ! local index for groundwater representation real(rkind) :: bulkDensity ! bulk density of a given layer (kg m-3) real(rkind) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) - real(rkind),parameter :: tempAccelerate=0.00_rkind ! factor to force initial canopy temperatures to be close to air temperature - real(rkind),parameter :: xMinCanopyWater=0.0001_rkind ! minimum value to initialize canopy water (kg m-2) - real(rkind),parameter :: tinyStep=0.000001_rkind ! stupidly small time step (s) + real(rkind),parameter :: tempAccelerate=0.00_rkind ! factor to force initial canopy temperatures to be close to air temperature + real(rkind),parameter :: xMinCanopyWater=0.0001_rkind ! minimum value to initialize canopy water (kg m-2) + real(rkind),parameter :: tinyStep=0.000001_rkind ! stupidly small time step (s) ! ------------------------------------------------------------------------------------------------------ ! * model solver @@ -200,18 +200,17 @@ subroutine systemSolvSundials(& real(qp) :: sMul(nState) ! NOTE: qp ! multiplier for state vector for the residual calculations real(qp) :: rVec(nState) ! NOTE: qp ! residual vector real(rkind) :: rAdd(nState) ! additional terms in the residual vector - real(rkind) :: fOld ! function values (-); NOTE: dimensionless because scaled logical(lgt) :: feasible ! feasibility flag - real(rkind) :: atol(nState) ! absolute telerance - real(rkind) :: rtol(nState) ! relative tolerance + real(rkind) :: atol(nState) ! absolute telerance + real(rkind) :: rtol(nState) ! relative tolerance integer(i4b) :: iLayer ! index of model layer in the snow+soil domain real(rkind) :: xMin,xMax ! minimum and maximum values for water content real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) - type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a data step - integer(i4b) :: tol_iter ! iteration index - real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix - logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step - + type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a data step + real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix + logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step + real(rkind) :: fOld ! function values (-); NOTE: dimensionless because scaled + logical(lgt),parameter :: oldway=.true. ! --------------------------------------------------------------------------------------- ! point to variables in the data structures @@ -220,7 +219,7 @@ subroutine systemSolvSundials(& ! model decisions ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) - ! enthalpy + ! enthalpy scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) @@ -231,24 +230,32 @@ subroutine systemSolvSundials(& scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp] mass of liquid water on the vegetation canopy (kg m-2) scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) ! model state variables (snow and soil domains) mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) + ! model state variables (aquifer) + scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in): [dp] storage of water in the aquifer (m) + ! check the need to merge snow layers mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ! accelerate solution for temperature airtemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ! vector of energy and hydrology indices for the snow and soil domains ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ! layer geometry nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in): [i4b] total number of layers @@ -317,14 +324,14 @@ subroutine systemSolvSundials(& ! initialize state vectors call getScaling(& ! input - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers ! output - fScale, & ! intent(out): function scaling vector (mixed units) - xScale, & ! intent(out): variable scaling vector (mixed units) - sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) - dMat, & ! intent(out): diagonal of the Jacobian matrix (excludes fluxes) - err,cmessage) ! intent(out): error control + fScale, & ! intent(out): function scaling vector (mixed units) + xScale, & ! intent(out): variable scaling vector (mixed units) + sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) + dMat, & ! intent(out): diagonal of the Jacobian matrix (excludes fluxes) + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) ! initialize the trial state vectors @@ -365,25 +372,27 @@ subroutine systemSolvSundials(& if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! compute the flux and the residual vector for a given state vector - ! NOTE 1: The derivatives computed in eval8summa are used to calculate the Jacobian matrix for the first iteration - ! NOTE 2: The Jacobian matrix together with the residual vector is used to calculate the first iteration increment - - call eval8summa(& + ! NOTE: The derivatives computed in eval8summaSundials are used to calculate the Jacobian matrix for the first iteration + ! ARE THEY?? or just getting flux_init? + if (.not.oldway)then + call eval8summaSundials(& ! input: model control + dt, & ! intent(in): current stepsize dt, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): number of layers nState, & ! intent(in): number of state variables in the current subset + .false., & ! intent(in): we are outside Sundials solver loop firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVecTrial, & ! intent(in): model state vector fScale, & ! intent(in): function scaling vector - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures model_decisions, & ! intent(in): model decisions lookup_data, & ! intent(in): lookup tables @@ -393,23 +402,92 @@ subroutine systemSolvSundials(& forc_data, & ! intent(in): model forcing data bvar_data, & ! intent(in): average model variables for the entire basin prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): index data ! input-output: data structures + indx_data, & ! intent(inout): index data diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: here we need to pass some extra variables that do not get updated in in the Sundials loops + scalarCanopyTemp, & ! intent(out): trial value of canopy temperature (K) + scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) + scalarCanopyIce, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyIce, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiq, & ! intent(out): trial value of canopy liquid water (kg m-2) + scalarCanopyLiq, & ! intent(in): value of canopy liquid water (kg m-2) + scalarCanopyEnthalpy, & ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpy, & ! intent(in): value for enthalpy of the vegetation canopy (J m-3) + mLayerTemp, & ! intent(out): trial vector of layer temperature (K) + mLayerTemp, & ! intent(in): vector of layer temperature (K) + mLayerMatricHeadLiq, & ! intent(out): trial value for liquid water matric potential (m) + mLayerMatricHead, & ! intent(out): trial value for total water matric potential (m) + mLayerMatricHead, & ! intent(in): value for total water matric potential (m) + mLayerVolFracWat, & ! intent(out): trial vector of volumetric total water content (-) + mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) + mLayerVolFracIce, & ! intent(out): trial vector of volumetric ice water content (-) + mLayerVolFracIce, & ! intent(in): vector of volumetric ice water content (-) + mLayerVolFracLiq, & ! intent(out): trial vector of volumetric liquid water content (-) + mLayerVolFracLiq, & ! intent(in): vector of volumetric liquid water content (-) + scalarAquiferStorage, & ! intent(out): trial value of storage of water in the aquifer (m) + scalarAquiferStorage, & ! intent(in): value of storage of water in the aquifer (m) + mLayerEnthalpy, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) + mLayerEnthalpy, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) ! input-output: baseflow ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - ! output + ! output: flux and residual vectors feasible, & ! intent(out): flag to denote the feasibility of the solution fluxVec0, & ! intent(out): flux vector rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation rVec, & ! intent(out): residual vector - fOld, & ! intent(out): function evaluation err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif + endif + + if (oldway)then !dt is 7200 in residuals + call eval8summa(& + ! input: model control + dt, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): number of layers + nState, & ! intent(in): number of state variables in the current subset + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVecTrial, & ! intent(in): model state vector + fScale, & ! intent(in): function scaling vector + sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): index data + ! input-output: data structures + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + ! output + feasible, & ! intent(out): flag to denote the feasibility of the solution + fluxVec0, & ! intent(out): flux vector + rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec, & ! intent(out): residual vector + fOld, & ! intent(out): function evaluation + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif + endif ! copy over the initial flux structure since some model fluxes are not computed in the iterations do concurrent ( iVar=1:size(flux_meta) ) @@ -443,10 +521,10 @@ subroutine systemSolvSundials(& prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers - mpar_data, & ! intent(in): model parameters + mpar_data, & ! intent(in): model parameters ! output atol, & ! intent(out): absolute tolerances vector (mixed units) - rtol, & ! intent(out): relative tolerances vector (mixed units) + rtol, & ! intent(out): relative tolerances vector (mixed units) err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) @@ -462,9 +540,9 @@ subroutine systemSolvSundials(& mLayerCmpress_sum(:) = 0._rkind call summaSolveSundialsIDA(& - dt, & ! intent (in) data time step - atol, & ! intent (in) absolute telerance - rtol, & ! intent (in) relative tolerance + dt, & ! intent(in): data time step + atol, & ! intent(in): absolute telerance + rtol, & ! intent(in): relative tolerance nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): number of snow+soil layers @@ -494,10 +572,10 @@ subroutine systemSolvSundials(& deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step - tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt - mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix - dt_out, & ! intent(out): time step + idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step + tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt + mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix + dt_out, & ! intent(out): time step stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step err,cmessage) ! intent(out): error control @@ -517,9 +595,8 @@ subroutine systemSolvSundials(& flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / dt_out end do - diag_data%var(iLookDIAG%mLayerCompress)%dat(:) = mLayerCmpress_sum(:) - ! compute the total change in storage associated with compression of the soil matrix (kg m-2) + diag_data%var(iLookDIAG%mLayerCompress)%dat(:) = mLayerCmpress_sum(:) diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sum(diag_data%var(iLookDIAG%mLayerCompress)%dat(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water ! save the computed solution diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index d6c541a1b..5a98150c4 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -316,7 +316,7 @@ subroutine varSubstepSundials(& nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to denote first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution ! input/output: data structures @@ -683,11 +683,11 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux ! ------------------ ! initialize to state variable from the last update - scalarCanairTempTrial = scalarCanairTemp - scalarCanopyTempTrial = scalarCanopyTemp - scalarCanopyWatTrial = scalarCanopyWat - scalarCanopyLiqTrial = scalarCanopyLiq - scalarCanopyIceTrial = scalarCanopyIce + scalarCanairTempTrial = scalarCanairTemp + scalarCanopyTempTrial = scalarCanopyTemp + scalarCanopyWatTrial = scalarCanopyWat + scalarCanopyLiqTrial = scalarCanopyLiq + scalarCanopyIceTrial = scalarCanopyIce mLayerTempTrial = mLayerTemp mLayerVolFracWatTrial = mLayerVolFracWat mLayerVolFracLiqTrial = mLayerVolFracLiq From 0cc5e021780e62e77f245107cbe9a89cfe7a5b0c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 5 Oct 2022 10:44:25 +0900 Subject: [PATCH 0396/1472] cut oldway flag code and tidy up --- build/source/engine/eval8summaSundials.f90 | 89 +++++++++---------- build/source/engine/summaSolveSundialsIDA.f90 | 8 +- build/source/engine/systemSolvSundials.f90 | 50 +---------- 3 files changed, 47 insertions(+), 100 deletions(-) diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index 89a623404..08e9dadd4 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -118,42 +118,42 @@ subroutine eval8summaSundials(& prog_data, & ! intent(in): model prognostic variables for a local HRU ! input-output: data structures indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU + diag_data, & ! intent(inout) model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! input-output: here we need to pass some extra variables that do not get updated in in the Sundials loops - scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) - scalarCanopyTempPrev, & ! intent(in): value of canopy temperature (K) - scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) - scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) - scalarCanopyEnthalpyPrev,& ! intent(in): value for enthalpy of the vegetation canopy (J m-3) - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - mLayerTempPrev, & ! intent(in): vector of layer temperature (K) - mLayerMatricHeadLiqTrial,& ! intent(out): trial value for liquid water matric potential (m) - mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) - mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) - mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - mLayerVolFracWatPrev, & ! intent(in): vector of volumetric total water content (-) - mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) - mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) - mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) - scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) - scalarAquiferStoragePrev,& ! intent(in): value of storage of water in the aquifer (m) - mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) - mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) + scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) + scalarCanopyTempPrev, & ! intent(in): value of canopy temperature (K) + scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) + scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) + scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpyPrev,& ! intent(in): value for enthalpy of the vegetation canopy (J m-3) + mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + mLayerTempPrev, & ! intent(in): vector of layer temperature (K) + mLayerMatricHeadLiqTrial,& ! intent(out): trial value for liquid water matric potential (m) + mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) + mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) + mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) + mLayerVolFracWatPrev, & ! intent(in): vector of volumetric total water content (-) + mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) + mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) + mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) + mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) + scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) + scalarAquiferStoragePrev,& ! intent(in): value of storage of water in the aquifer (m) + mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) + mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + ixSaturation, & ! intent(inout): index of the lowest saturated layer + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors - feasible, & ! intent(out): flag to denote the feasibility of the solution - fluxVec, & ! intent(out): flux vector - resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - resVec, & ! intent(out): residual vector - err,message) ! intent(out): error control + feasible, & ! intent(out): flag to denote the feasibility of the solution + fluxVec, & ! intent(out): flux vector + resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + resVec, & ! intent(out): residual vector + err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- ! provide access to subroutines USE getVectorz_module, only:varExtract ! extract variables from the state vector @@ -401,7 +401,6 @@ subroutine eval8summaSundials(& endif ! initialize to state variable from the last update - ! should all be set to previous values if splits, but for now operator splitting is not hooked up scalarCanopyTempTrial = scalarCanopyTempPrev scalarCanopyLiqTrial = scalarCanopyLiqPrev scalarCanopyIceTrial = scalarCanopyIcePrev @@ -561,7 +560,7 @@ subroutine eval8summaSundials(& scalarCanopyIceTrial, & ! intent(in): trial value for canopy ice content (kg m-2) scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) @@ -724,13 +723,9 @@ subroutine eval8summaSundials(& err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! compute the residual vector if (insideIDA)then dt1 = 1._qp ! always 1 for sundials since using Prime derivatives - !else - ! dt1 = 1._qp - !endif call computResidSundials(& ! input: model control @@ -867,29 +862,29 @@ integer(c_int) function eval8summa4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) - eqns_data%scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) + eqns_data%scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) eqns_data%scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - eqns_data%scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) + eqns_data%scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) eqns_data%scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): value for enthalpy of the vegetation canopy (J m-3) eqns_data%mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) eqns_data%mLayerTempPrev, & ! intent(in): vector of layer temperature (K) eqns_data%mLayerMatricHeadLiqTrial,& ! intent(out): trial value for liquid water matric potential (m) - eqns_data%mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) - eqns_data%mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) + eqns_data%mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) + eqns_data%mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) eqns_data%mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) eqns_data%mLayerVolFracWatPrev, & ! intent(in): vector of volumetric total water content (-) eqns_data%mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) - eqns_data%mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) + eqns_data%mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) eqns_data%mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - eqns_data%mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) - eqns_data%scalarAquiferStorageTrial, & ! intent(out): trial value of storage of water in the aquifer (m) - eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) + eqns_data%mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) + eqns_data%scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) + eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) ! input-output: baseflow - eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer - eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer + eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors feasible, & ! intent(out): flag to denote the feasibility of the solution eqns_data%fluxVec, & ! intent(out): flux vector diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index cfb136914..de4b2125b 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -369,7 +369,7 @@ subroutine summaSolveSundialsIDA( & ! Set the user-supplied Jacobian routine !comment this line out to use FD Jacobian - !retval = FIDASetJacFn(ida_mem, c_funloc(computJacob4IDA)) + retval = FIDASetJacFn(ida_mem, c_funloc(computJacob4IDA)) if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetJacFn'; return; endif ! Create Newton SUNNonlinearSolver object @@ -397,16 +397,16 @@ subroutine summaSolveSundialsIDA( & ! need the following values for the first substep eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) eqns_data%scalarCanopyIcePrev = prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) - eqns_data%scalarCanopyLiqPrev = prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) !!? + eqns_data%scalarCanopyLiqPrev = prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) - eqns_data%mLayerVolFracLiqPrev(:) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(:) !!? + eqns_data%mLayerVolFracLiqPrev(:) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(:) eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) - eqns_data%scalarAquiferStoragePrev = prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1)!!? + eqns_data%scalarAquiferStoragePrev = prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) eqns_data%ixSaturation = ixSaturation !********************************************************************************** diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 5bfc76b6f..b64cadf56 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -210,7 +210,6 @@ subroutine systemSolvSundials(& real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step real(rkind) :: fOld ! function values (-); NOTE: dimensionless because scaled - logical(lgt),parameter :: oldway=.true. ! --------------------------------------------------------------------------------------- ! point to variables in the data structures @@ -372,9 +371,7 @@ subroutine systemSolvSundials(& if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! compute the flux and the residual vector for a given state vector - ! NOTE: The derivatives computed in eval8summaSundials are used to calculate the Jacobian matrix for the first iteration - ! ARE THEY?? or just getting flux_init? - if (.not.oldway)then + ! NOTE: The values calculated in eval8summaSundials are used to calculate the initial flux call eval8summaSundials(& ! input: model control dt, & ! intent(in): current stepsize @@ -444,51 +441,6 @@ subroutine systemSolvSundials(& if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif endif - if (oldway)then !dt is 7200 in residuals - call eval8summa(& - ! input: model control - dt, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): number of layers - nState, & ! intent(in): number of state variables in the current subset - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVecTrial, & ! intent(in): model state vector - fScale, & ! intent(in): function scaling vector - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): index data - ! input-output: data structures - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - ! output - feasible, & ! intent(out): flag to denote the feasibility of the solution - fluxVec0, & ! intent(out): flux vector - rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector - fOld, & ! intent(out): function evaluation - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif - endif - ! copy over the initial flux structure since some model fluxes are not computed in the iterations do concurrent ( iVar=1:size(flux_meta) ) flux_temp%var(iVar)%dat(:) = flux_init%var(iVar)%dat(:) From a359fecfdb7c0523c949ee336f7024c716c2d861 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 5 Oct 2022 11:04:25 +0900 Subject: [PATCH 0397/1472] missed if statement --- build/source/engine/systemSolvSundials.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index b64cadf56..054d34bec 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -439,7 +439,6 @@ subroutine systemSolvSundials(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif - endif ! copy over the initial flux structure since some model fluxes are not computed in the iterations do concurrent ( iVar=1:size(flux_meta) ) From 12ca3fee6e98d287e780580fe35165d2ce0eb60d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 11 Oct 2022 22:10:01 +0900 Subject: [PATCH 0398/1472] Added all terms depending on size of kl and ku in banded Jacobians, Sundials and BE. Laugh Tests now work. Pulled nBands and ixDiag out of global params because I think they should be different in Sundials (-kl) but didn't change yet. Fixed spaces in computJacob.f90 --- build/source/dshare/globalData.f90 | 10 +- build/source/engine/computJacob.f90 | 1690 ++++++++--------- build/source/engine/computJacobSundials.f90 | 99 +- build/source/engine/matrixOper.f90 | 11 +- build/source/engine/opSplittin.f90 | 9 +- build/source/engine/summaSolve.f90 | 2 +- build/source/engine/summaSolveSundialsIDA.f90 | 3 - build/source/engine/systemSolv.f90 | 10 +- build/source/engine/systemSolvSundials.f90 | 3 +- 9 files changed, 930 insertions(+), 907 deletions(-) diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 index fc5241120..c0b94f45b 100644 --- a/build/source/dshare/globalData.f90 +++ b/build/source/dshare/globalData.f90 @@ -150,13 +150,11 @@ MODULE globalData integer(i4b),parameter,public :: iname_watAquifer=3006 ! named variable defining the water storage in the aquifer ! define named variables to describe the form and structure of the band-diagonal matrices used in the numerical solver - ! NOTE: This indexing scheme provides the matrix structure expected by lapack. Specifically, lapack requires kl extra rows for additional storage. - ! Consequently, all indices are offset by kl and the total number of bands for storage is 2*kl+ku+1 instead of kl+ku+1. + ! NOTE: lapack requires kl extra rows for additional storage versus sundials + ! Consequently, in lapack all indices are offset by kl and the total number of bands for storage is 2*kl+ku+1 instead of kl+ku+1. integer(i4b),parameter,public :: nRHS=1 ! number of unknown variables on the RHS of the linear system A.X=B - integer(i4b),parameter,public :: ku=3 ! number of super-diagonal bands - integer(i4b),parameter,public :: kl=4 ! number of sub-diagonal bands - integer(i4b),parameter,public :: ixDiag=kl+ku+1 ! index for the diagonal band - integer(i4b),parameter,public :: nBands=2*kl+ku+1 ! length of the leading dimension of the band diagonal matrix + integer(i4b),parameter,public :: ku=3 ! number of super-diagonal bands, ku>=3 to accommodate coupled layer above + integer(i4b),parameter,public :: kl=4 ! number of sub-diagonal bands, kl>=4 to accommodate vegetation ! define named variables for the type of matrix used in the numerical solution. integer(i4b),parameter,public :: ixFullMatrix=1001 ! named variable for the full Jacobian matrix diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 2d92d0100..7843e863c 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -20,129 +20,128 @@ module computJacob_module - ! data types - USE nrtype - - ! derived types to define the data structures - USE data_types,only:& - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (rkind) - - ! named variables for structure elements - USE var_lookup,only:iLookPROG ! named variables for structure elements - USE var_lookup,only:iLookDIAG ! named variables for structure elements - USE var_lookup,only:iLookINDEX ! named variables for structure elements - USE var_lookup,only:iLookDERIV ! named variables for structure elements - - ! access the global print flag - USE globalData,only:globalPrintFlag - - ! access missing values - USE globalData,only:integerMissing ! missing integer - USE globalData,only:realMissing ! missing real number - - ! domain types - USE globalData,only:iname_veg ! named variables for vegetation - USE globalData,only:iname_snow ! named variables for snow - USE globalData,only:iname_soil ! named variables for soil - - ! named variables to describe the state variable type - USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space - USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy - USE globalData,only:iname_watCanopy ! named variable defining the mass of water on the vegetation canopy - USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers - USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers - USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers - USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers - USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers - - ! access named variables to describe the form and structure of the matrices used in the numerical solver - USE globalData,only: ku ! number of super-diagonal bands - USE globalData,only: kl ! number of sub-diagonal bands - USE globalData,only: ixDiag ! index for the diagonal band - USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix - USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix - USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix - USE globalData,only: iJac1 ! first layer of the Jacobian to print - USE globalData,only: iJac2 ! last layer of the Jacobian to print - - ! constants - USE multiconst,only:& - LH_fus, & ! latent heat of fusion (J kg-1) - iden_water ! intrinsic density of liquid water (kg m-3) - - implicit none - ! define constants - real(rkind),parameter :: verySmall=tiny(1.0_rkind) ! a very small number - integer(i4b),parameter :: ixBandOffset=kl+ku+1 ! offset in the band Jacobian matrix - - private - public::computJacob - contains - - ! ********************************************************************************************************** - ! public subroutine computJacob: compute the Jacobian matrix - ! ********************************************************************************************************** - subroutine computJacob(& - ! input: model control - dt, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - computeBaseflow, & ! intent(in): flag to indicate if we need to compute baseflow - ixMatrix, & ! intent(in): form of the Jacobian matrix - ! input: data structures - indx_data, & ! intent(in): index data - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! input-output: Jacobian and its diagonal - dMat, & ! intent(inout): diagonal of the Jacobian matrix - aJac, & ! intent(out): Jacobian matrix - ! output: error control - err,message) ! intent(out): error code and error message - ! ----------------------------------------------------------------------------------------------------------------- - implicit none - ! input: model control - real(rkind),intent(in) :: dt ! length of the time step (seconds) - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: computeBaseflow ! flag to indicate if computing baseflow - integer(i4b),intent(in) :: ixMatrix ! form of the Jacobian matrix - ! input: data structures - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - ! input-output: Jacobian and its diagonal - real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix - real(rkind),intent(out) :: aJac(:,:) ! Jacobian matrix - ! output variables - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------- - ! * local variables - ! -------------------------------------------------------------- - ! indices of model state variables - integer(i4b) :: jState ! index of state within the state subset - integer(i4b) :: qState ! index of cross-derivative state variable for baseflow - integer(i4b) :: nrgState ! energy state variable - integer(i4b) :: watState ! hydrology state variable - integer(i4b) :: nState ! number of state variables - ! indices of model layers - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: jLayer ! index of model layer within the full state vector (hydrology) - integer(i4b) :: pLayer ! indices of soil layers (used for the baseflow derivatives) - ! conversion factors - real(rkind) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative - ! -------------------------------------------------------------- - ! associate variables from data structures - associate(& +! data types +USE nrtype + +! derived types to define the data structures +USE data_types,only:& + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength ! data vector with variable length dimension (rkind) + +! named variables for structure elements +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements +USE var_lookup,only:iLookDERIV ! named variables for structure elements + +! access the global print flag +USE globalData,only:globalPrintFlag + +! access missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing real number + +! domain types +USE globalData,only:iname_veg ! named variables for vegetation +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil + +! named variables to describe the state variable type +USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space +USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy +USE globalData,only:iname_watCanopy ! named variable defining the mass of water on the vegetation canopy +USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers +USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers +USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers +USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers +USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers + +! access named variables to describe the form and structure of the matrices used in the numerical solver +USE globalData,only: ku ! number of super-diagonal bands, assume ku>=3 +USE globalData,only: kl ! number of sub-diagonal bands, assume kl>=4 +USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix +USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix +USE globalData,only: iJac1 ! first layer of the Jacobian to print +USE globalData,only: iJac2 ! last layer of the Jacobian to print + +! constants +USE multiconst,only:& + LH_fus, & ! latent heat of fusion (J kg-1) + iden_water ! intrinsic density of liquid water (kg m-3) + +implicit none +! define constants +real(rkind),parameter :: verySmall=tiny(1.0_rkind) ! a very small number + +private +public::computJacob +contains + +! ********************************************************************************************************** +! public subroutine computJacob: compute the Jacobian matrix +! ********************************************************************************************************** +subroutine computJacob(& + ! input: model control + dt, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + computeBaseflow, & ! intent(in): flag to indicate if we need to compute baseflow + ixMatrix, & ! intent(in): form of the Jacobian matrix + ! input: data structures + indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables + dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! input-output: Jacobian and its diagonal + dMat, & ! intent(inout): diagonal of the Jacobian matrix + aJac, & ! intent(out): Jacobian matrix + ! output: error control + err,message) ! intent(out): error code and error message + ! ----------------------------------------------------------------------------------------------------------------- + implicit none + ! input: model control + real(rkind),intent(in) :: dt ! length of the time step (seconds) + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: computeBaseflow ! flag to indicate if computing baseflow + integer(i4b),intent(in) :: ixMatrix ! form of the Jacobian matrix + ! input: data structures + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + ! input-output: Jacobian and its diagonal + real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix + real(rkind),intent(out) :: aJac(:,:) ! Jacobian matrix + ! output variables + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------- + ! * local variables + ! -------------------------------------------------------------- + integer(i4b),parameter :: ixDiag=kl+ku+1 ! index for the diagonal band for lapack + integer(i4b),parameter :: nBands=2*kl+ku+1 ! length of the leading dimension of the band diagonal matrix for lapack + ! indices of model state variables + integer(i4b) :: jState ! index of state within the state subset + integer(i4b) :: qState ! index of cross-derivative state variable for baseflow + integer(i4b) :: nrgState ! energy state variable + integer(i4b) :: watState ! hydrology state variable + integer(i4b) :: nState ! number of state variables + ! indices of model layers + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: jLayer ! index of model layer within the full state vector (hydrology) + integer(i4b) :: pLayer ! indices of soil layers (used for the baseflow derivatives) + ! conversion factors + real(rkind) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative + ! -------------------------------------------------------------- + ! associate variables from data structures + associate(& ! indices of model state variables ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable @@ -256,766 +255,753 @@ subroutine computJacob(& ! -------------------------------------------------------------- ! initialize error control err=0; message='computJacob/' - + ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* ! * PART 0: PRELIMINARIES (INITIALIZE JACOBIAN AND COMPUTE TIME-VARIABLE DIAGONAL TERMS) ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* - + ! get the number of state variables nState = size(dMat) - + ! initialize the Jacobian ! NOTE: this needs to be done every time, since Jacobian matrix is modified in the solver aJac(:,:) = 0._rkind ! analytical Jacobian matrix - + ! compute terms in the Jacobian for vegetation (excluding fluxes) ! NOTE: energy for vegetation is computed *within* the iteration loop as it includes phase change if(ixVegNrg/=integerMissing)then - dMat(ixVegNrg) = scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy & - + dVolHtCapBulk_dTkCanopy * scalarCanopydTemp_dt + dMat(ixVegNrg) = scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy & + + dVolHtCapBulk_dTkCanopy * scalarCanopydTemp_dt endif - + ! compute additional terms for the Jacobian for the snow-soil domain (excluding fluxes) ! NOTE: energy for snow+soil is computed *within* the iteration loop as it includes phase change do iLayer=1,nLayers - if(ixSnowSoilNrg(iLayer)/=integerMissing)then - dMat(ixSnowSoilNrg(iLayer)) = mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) & - + dVolHtCapBulk_dTk(iLayer) * mLayerdTemp_dt(iLayer) - endif + if(ixSnowSoilNrg(iLayer)/=integerMissing)then + dMat(ixSnowSoilNrg(iLayer)) = mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) & + + dVolHtCapBulk_dTk(iLayer) * mLayerdTemp_dt(iLayer) + endif end do - + ! compute additional terms for the Jacobian for the soil domain (excluding fluxes) do iLayer=1,nSoil - if(ixSoilOnlyHyd(iLayer)/=integerMissing)then - dMat(ixSoilOnlyHyd(iLayer)) = dVolTot_dPsi0(iLayer) + dCompress_dPsi(iLayer) - endif + if(ixSoilOnlyHyd(iLayer)/=integerMissing)then + dMat(ixSoilOnlyHyd(iLayer)) = dVolTot_dPsi0(iLayer) + dCompress_dPsi(iLayer) + endif end do - + ! define the form of the matrix select case(ixMatrix) - - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - ! * PART 1: BAND MATRIX - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - case(ixBandMatrix) - - ! check - if(size(aJac,1)/=nBands .or. size(aJac,2)/=size(dMat))then - message=trim(message)//'unexpected shape of the Jacobian matrix: expect aJac(nBands,nState)' - err=20; return - endif - - ! ----- - ! * energy and liquid fluxes over vegetation... - ! --------------------------------------------- - if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) - - ! * energy fluxes with the canopy water - if(ixVegHyd/=integerMissing)then - - ! * cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) - if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixCasNrg),ixCasNrg) = -dCanopyEvaporation_dTCanair*dt - ! dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy is the derivative in throughfall and canopy drainage with canopy temperature - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixVegNrg),ixVegNrg) = -dCanopyEvaporation_dTCanopy*dt + dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy - ! * liquid water fluxes for vegetation canopy (-), dt*scalarFracLiqVeg*scalarCanopyLiqDeriv is the derivative in throughfall and canopy drainage with canopy water - aJac(ixDiag, ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanWat - scalarCanopyLiqDeriv)*dt + 1._rkind - if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixTopNrg),ixTopNrg) = -dCanopyEvaporation_dTGround*dt - - ! * cross-derivative terms w.r.t. canopy water (kg-1 m2) - if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water - - ! * cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) - ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth & - + dVolHtCapBulk_dCanWat * scalarCanopydTemp_dt - (dt/canopyDepth) * dCanopyNetFlux_dCanWat ! dF/dLiq - if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) - endif - - ! * cross-derivative terms w.r.t. canopy temperature (K-1) - if(ixVegNrg/=integerMissing)then - if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarCanopyLiqDeriv*dCanLiq_dTcanopy)/iden_water - endif - - ! * energy fluxes with the canopy air space (J m-3 K-1) - if(ixCasNrg/=integerMissing)then - aJac(ixDiag, ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixCasNrg,ixVegNrg),ixVegNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanopyTemp) - if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixCasNrg,ixTopNrg),ixTopNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dGroundTemp) - endif - - ! * energy fluxes with the vegetation canopy (J m-3 K-1) - if(ixVegNrg/=integerMissing)then - if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixCasNrg),ixCasNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanairTemp) - aJac(ixDiag, ixVegNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanopyTemp) + dMat(ixVegNrg) - if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixTopNrg),ixTopNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dGroundTemp) - endif - - ! * energy fluxes with the surface (J m-3 K-1) - if(ixTopNrg/=integerMissing)then - if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanairTemp) - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanopyTemp) - endif - - endif ! if there is a need to compute energy fluxes within vegetation - - ! ----- - ! * energy fluxes for the snow+soil domain... - ! ------------------------------------------- - if(nSnowSoilNrg>0)then - do iLayer=1,nLayers ! loop through all layers in the snow+soil domain - - ! check if the state is in the subset - if(ixSnowSoilNrg(iLayer)==integerMissing) cycle - - ! - define index within the state subset and the full state vector - jState = ixSnowSoilNrg(iLayer) ! index within the state subset - - ! - diagonal elements - aJac(ixDiag,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) - - ! - lower-diagonal elements - if(iLayer>1)then - if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowSoilNrg(iLayer-1),jState),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) - endif - - ! - upper diagonal elements - if(iLayer0)then - do iLayer=1,nSnow ! loop through layers in the snow domain - - ! - check that the snow layer is desired - if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle - - ! - define state indices for the current layer - watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - - ! compute factor to convert liquid water derivative to total water derivative - select case( ixHydType(iLayer) ) - case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) - case default; convLiq2tot = 1._rkind - end select - - ! - diagonal elements - aJac(ixDiag,watState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot + dMat(watState) - - ! - lower-diagonal elements - if(iLayer>1)then - if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyHyd(iLayer-1),watState),watState) = 0._rkind ! sub-diagonal: no dependence on other layers - endif - - ! - upper diagonal elements - if(iLayer0 .and. nSnowOnlyNrg>0)then - do iLayer=1,nSnow ! loop through layers in the snow domain - - ! - check that the snow layer is desired - if(ixSnowOnlyNrg(iLayer)==integerMissing) cycle - - ! (define the energy state) - nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector - - ! - define state indices for the current layer - watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - - if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) - - ! - include derivatives of energy fluxes w.r.t water fluxes for current layer - aJac(ixOffDiag(nrgState,watState),watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water & - + dVolHtCapBulk_dTheta(iLayer) * mLayerdTemp_dt(iLayer) & - + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) ! (dF/dLiq) - - ! - include derivatives of water fluxes w.r.t energy fluxes for current layer - aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) - - ! (cross-derivative terms for the layer below) - if(iLayer < nSnow)then - aJac(ixOffDiag(ixSnowOnlyHyd(iLayer+1),nrgState),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 - endif ! (if there is a water state in the layer below the current layer in the given state subset) - - ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above - if(iLayer>1)then - if(ixSnowOnlyNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyNrg(iLayer-1),watState),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) ) - endif - - ! (cross-derivative terms for the layer below) - if(iLayer0)then !bottom snow layer and there is soil below - if(ixSoilOnlyNrg(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyNrg(1),watState),watState) = (dt/mLayerDepth(nSnow+1))*(-dNrgFlux_dWatAbove(nSnow) ) - endif - - endif ! (if the energy state for the current layer is within the state subset) - - end do ! (looping through snow layers) - endif ! (if there are state variables for both water and energy in the snow domain) - - ! ----- - ! * liquid water fluxes for the soil domain... - ! -------------------------------------------- - if(nSoilOnlyHyd>0)then - do iLayer=1,nSoil - - ! - check that the soil layer is desired - if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle - - ! - define state indices - watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset - - ! - define indices of the soil layers - jLayer = iLayer+nSnow ! index of layer in the snow+soil vector - - ! - compute the diagonal elements - ! all terms *excluding* baseflow - aJac(ixDiag,watState) = (dt/mLayerDepth(jLayer))*(-dq_dHydStateBelow(iLayer-1) + dq_dHydStateAbove(iLayer)) + dMat(watState) - - ! - compute the lower-diagonal elements - if(iLayer>1)then - if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),watState),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) - endif - - ! - compute the upper-diagonal elements - if(iLayer0)then !have snow above first soil layer - ! if(ixSnowOnlyHyd(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) - ! elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) - ! if(ixVegHyd/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) - ! endif - - endif ! (if the subset includes hydrology state variables in the soil domain) - - ! ----- - ! * liquid water fluxes for the aquifer... - ! ---------------------------------------- - if(ixAqWat/=integerMissing) then - aJac(ixDiag,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) - aJac(ixOffDiag(ixAqWat,ixSoilOnlyNrg(nSoil)),ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk - aJac(ixOffDiag(ixAqWat,ixSoilOnlyHyd(nSoil)),ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat - ! - do not include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) in banded structure - ! if(computeVegFlux)then - ! aJac(ixOffDiag(ixAqWat,ixCasNrg),ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) - ! aJac(ixOffDiag(ixAqWat,ixVegNrg),ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) - ! aJac(ixOffDiag(ixAqWat,ixVegHyd),ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 - ! aJac(ixOffDiag(ixAqWat,ixTopNrg),ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) - ! endif - endif - - ! ----- - ! * cross derivatives in the soil domain... - ! ----------------------------------------------------------------------------- - if(nSoilOnlyHyd>0 .and. nSoilOnlyNrg>0)then - do iLayer=1,nSoilOnlyNrg - - ! - check that the soil layer is desired - if(ixSoilOnlyNrg(iLayer)==integerMissing) cycle - - ! - define indices of the soil layers - jLayer = iLayer+nSnow ! index of layer in the snow+soil vector - - ! - define the energy state variable - nrgState = ixNrgLayer(jLayer) ! index within the full state vector - - ! - define index of hydrology state variable within the state subset - watState = ixSoilOnlyHyd(iLayer) - - ! only compute derivatives if the water state for the current layer is within the state subset - if(watstate/=integerMissing)then - - ! - include derivatives in liquid water fluxes w.r.t. temperature for current layer - aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance - - ! - compute lower diagonal elements - if(iLayer>1)then - if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),nrgState),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 - endif - - ! compute upper-diagonal elements - if(iLayer0)then + do iLayer=1,nLayers ! loop through all layers in the snow+soil domain + + ! check if the state is in the subset + if(ixSnowSoilNrg(iLayer)==integerMissing) cycle + + ! - define index within the state subset and the full state vector + jState = ixSnowSoilNrg(iLayer) ! index within the state subset + + ! - diagonal elements + aJac(ixDiag,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) + + ! - lower-diagonal elements + if(iLayer>1)then + if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowSoilNrg(iLayer-1),jState),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) + endif + + ! - upper diagonal elements + if(iLayer0)then + do iLayer=1,nSnow ! loop through layers in the snow domain + + ! - check that the snow layer is desired + if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle + + ! - define state indices for the current layer + watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset + + ! compute factor to convert liquid water derivative to total water derivative + select case( ixHydType(iLayer) ) + case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) + case default; convLiq2tot = 1._rkind + end select + + ! - diagonal elements + aJac(ixDiag,watState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot + dMat(watState) + + ! - lower-diagonal elements + if(iLayer>1)then + if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyHyd(iLayer-1),watState),watState) = 0._rkind ! sub-diagonal: no dependence on other layers + endif + + ! - upper diagonal elements + if(iLayer0 .and. nSnowOnlyNrg>0)then + do iLayer=1,nSnow ! loop through layers in the snow domain + + ! - check that the snow layer is desired + if(ixSnowOnlyNrg(iLayer)==integerMissing) cycle + + ! (define the energy state) + nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector + + ! - define state indices for the current layer + watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset + + if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) + + ! - include derivatives of energy fluxes w.r.t water fluxes for current layer + aJac(ixOffDiag(nrgState,watState),watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water & + + dVolHtCapBulk_dTheta(iLayer) * mLayerdTemp_dt(iLayer) & + + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) ! (dF/dLiq) + + ! - include derivatives of water fluxes w.r.t energy fluxes for current layer + aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) + + ! (cross-derivative terms for the layer below) + if(iLayer < nSnow)then + aJac(ixOffDiag(ixSnowOnlyHyd(iLayer+1),nrgState),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 + endif ! (if there is a water state in the layer below the current layer in the given state subset) + + ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above + if(iLayer>1)then + if(ixSnowOnlyNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyNrg(iLayer-1),watState),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) ) + endif + + ! (cross-derivative terms for the layer below) + if(iLayer0)then !bottom snow layer and there is soil below + if(ixSoilOnlyNrg(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyNrg(1),watState),watState) = (dt/mLayerDepth(nSnow+1))*(-dNrgFlux_dWatAbove(nSnow) ) + endif + + endif ! (if the energy state for the current layer is within the state subset) + + end do ! (looping through snow layers) + endif ! (if there are state variables for both water and energy in the snow domain) + + ! ----- + ! * liquid water fluxes for the soil domain... + ! -------------------------------------------- + if(nSoilOnlyHyd>0)then + do iLayer=1,nSoil + + ! - check that the soil layer is desired + if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle + + ! - define state indices + watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset + + ! - define indices of the soil layers + jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + + ! - compute the diagonal elements + ! all terms *excluding* baseflow + aJac(ixDiag,watState) = (dt/mLayerDepth(jLayer))*(-dq_dHydStateBelow(iLayer-1) + dq_dHydStateAbove(iLayer)) + dMat(watState) + + ! - compute the lower-diagonal elements + if(iLayer>1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),watState),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) + endif + + ! - compute the upper-diagonal elements + if(iLayer0)then !have snow above first soil layer + if(ixSnowOnlyHyd(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) + if(ixVegHyd/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) + endif + + endif ! (if the subset includes hydrology state variables in the soil domain) + + ! ----- + ! * liquid water fluxes for the aquifer... + ! ---------------------------------------- + if(ixAqWat/=integerMissing) then + aJac(ixDiag,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) + aJac(ixOffDiag(ixAqWat,ixSoilOnlyNrg(nSoil)),ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk + aJac(ixOffDiag(ixAqWat,ixSoilOnlyHyd(nSoil)),ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat + ! - only include banded derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration), would have to have few soil layers if(computeVegFlux)then - aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) - aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) - aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 + if(ixAqWat-ixCasNrg <= kl) aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) + if(ixAqWat-ixVegNrg <= kl) aJac(ixAqWat,ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) + if(ixAqWat-ixVegHyd <= kl) aJac(ixAqWat,ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 + if(ixAqWat-ixTopNrg <= kl) aJac(ixAqWat,ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) endif - aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) - endif - - ! - do not include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) in banded structure - ! if(computeVegFlux)then - ! aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanair(iLayer)) + aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) ! dVol/dT (K-1) - ! aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanopy(iLayer)) + aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) ! dVol/dT (K-1) - ! aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dCanWat(iLayer)) + aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) ! dVol/dLiq (kg m-2)-1 - ! aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTGround(iLayer)) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) - ! endif - - ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerdTemp_dt(jLayer) & - + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) - if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present - aJac(ixOffDiag(nrgState,watState),watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) + aJac(ixOffDiag(nrgState,watState),watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content - endif - - ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above - if(iLayer>1)then - if(ixSoilOnlyNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyNrg(iLayer-1),watState),watState) = (dt/mLayerDepth(jLayer-1))*( dNrgFlux_dWatBelow(jLayer-1) ) - elseif(iLayer==1 .and. nSnowOnlyNrg>0)then !top soil layer and there is snow above - if(ixSnowOnlyNrg(nSnow)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyNrg(nSnow),watState),watState) = (dt/mLayerDepth(nSnow))*( dNrgFlux_dWatBelow(nSnow) ) - endif - - ! (cross-derivative terms for the layer below) - if(iLayer0)then !have snow above first soil layer - ! if(ixSnowOnlyNrg(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) - !elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) - ! if(ixVegNrg/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) - !endif - - endif ! (if there are state variables for both water and energy in the soil domain) - - if(globalPrintFlag)then - print*, '** banded analytical Jacobian:' - write(*,'(a4,1x,100(i17,1x))') 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) - do iLayer=kl+1,nBands - write(*,'(i4,1x,100(e17.10,1x))') iLayer, (aJac(iLayer,jLayer),jLayer=min(iJac1,nState),min(iJac2,nState)) - end do - endif - - !print*, '** banded analytical Jacobian :' - !write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=1,size(aJac,2)) - !do iLayer=1,size(aJac,1) - ! write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(iLayer,1:size(aJac,2)) - !end do - !print *, '--------------------------------------------------------------' - - !print*, 'PAUSE: banded analytical Jacobian'; read(*,*) - - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - ! * PART 2: FULL MATRIX - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - case(ixFullMatrix) - - ! check - if(size(aJac,1)/=size(dMat) .or. size(aJac,2)/=size(dMat))then - message=trim(message)//'unexpected shape of the Jacobian matrix: expect aJac(nState,nState)' - err=20; return - endif - - ! ----- - ! * energy and liquid fluxes over vegetation... - ! --------------------------------------------- - if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) - - ! * energy fluxes with the canopy water - if(ixVegHyd/=integerMissing)then - - ! cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) - if(ixCasNrg/=integerMissing) aJac(ixVegHyd,ixCasNrg) = -dCanopyEvaporation_dTCanair*dt - ! dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy is the derivative in throughfall and canopy drainage with canopy temperature - if(ixVegNrg/=integerMissing) aJac(ixVegHyd,ixVegNrg) = -dCanopyEvaporation_dTCanopy*dt + dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy - ! * liquid water fluxes for vegetation canopy (-) - aJac(ixVegHyd,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanWat - scalarCanopyLiqDeriv)*dt + 1._rkind - if(ixTopNrg/=integerMissing) aJac(ixVegHyd,ixTopNrg) = -dCanopyEvaporation_dTGround*dt - - ! cross-derivative terms w.r.t. canopy water (kg-1 m2) - if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water - - ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) - ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth & - + dVolHtCapBulk_dCanWat * scalarCanopydTemp_dt - (dt/canopyDepth) * dCanopyNetFlux_dCanWat ! dF/dLiq - if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) - endif - - ! cross-derivative terms w.r.t. canopy temperature (K-1) - if(ixVegNrg/=integerMissing)then - if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegNrg) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarCanopyLiqDeriv*dCanLiq_dTcanopy)/iden_water - endif - - ! energy fluxes with the canopy air space (J m-3 K-1) - if(ixCasNrg/=integerMissing)then - aJac(ixCasNrg,ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) - if(ixVegNrg/=integerMissing) aJac(ixCasNrg,ixVegNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanopyTemp) - if(ixTopNrg/=integerMissing) aJac(ixCasNrg,ixTopNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dGroundTemp) - endif - - ! energy fluxes with the vegetation canopy (J m-3 K-1) - if(ixVegNrg/=integerMissing)then - if(ixCasNrg/=integerMissing) aJac(ixVegNrg,ixCasNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanairTemp) - aJac(ixVegNrg,ixVegNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanopyTemp) + dMat(ixVegNrg) - if(ixTopNrg/=integerMissing) aJac(ixVegNrg,ixTopNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dGroundTemp) - endif - - ! energy fluxes with the surface (J m-3 K-1) - if(ixTopNrg/=integerMissing)then - if(ixCasNrg/=integerMissing) aJac(ixTopNrg,ixCasNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanairTemp) - if(ixVegNrg/=integerMissing) aJac(ixTopNrg,ixVegNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanopyTemp) - endif - - endif ! if there is a need to compute energy fluxes within vegetation - - ! ----- - ! * energy fluxes for the snow+soil domain... - ! ------------------------------------------- - if(nSnowSoilNrg>0)then - do iLayer=1,nLayers ! loop through all layers in the snow+soil domain - - ! check if the state is in the subset - if(ixSnowSoilNrg(iLayer)==integerMissing) cycle - - ! - define index within the state subset and the full state vector - jState = ixSnowSoilNrg(iLayer) ! index within the state subset - - ! - diagonal elements - aJac(jState,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) - - ! - lower-diagonal elements - if(iLayer>1)then - if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixSnowSoilNrg(iLayer-1),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) endif - - ! - upper diagonal elements - if(iLayer0)then - do iLayer=1,nSnow ! loop through layers in the snow domain - - ! - check that the snow layer is desired - if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle - - ! - define state indices for the current layer - watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - - ! compute factor to convert liquid water derivative to total water derivative - select case( ixHydType(iLayer) ) - case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) - case default; convLiq2tot = 1._rkind - end select - - ! - diagonal elements - aJac(watState,watState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot + dMat(watState) - - ! - lower-diagonal elements - if(iLayer>1)then - if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSnowOnlyHyd(iLayer-1),watState) = 0._rkind ! sub-diagonal: no dependence on other layers - endif - - ! - upper diagonal elements - if(iLayer0 .and. nSnowOnlyNrg>0)then - do iLayer=1,nSnow ! loop through layers in the snow domain - - ! - check that the snow layer is desired - if(ixSnowOnlyNrg(iLayer)==integerMissing) cycle - - ! (define the energy state) - nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector - - ! - define state indices for the current layer - watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset - - if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) - - ! - include derivatives of energy fluxes w.r.t water fluxes for current layer - aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water & - + dVolHtCapBulk_dTheta(iLayer) * mLayerdTemp_dt(iLayer) & - + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) ! (dF/dLiq) - - ! - include derivatives of water fluxes w.r.t energy fluxes for current layer - aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) - - ! (cross-derivative terms for the layer below) - if(iLayer < nSnow)then - aJac(ixSnowOnlyHyd(iLayer+1),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 - endif ! (if there is a water state in the layer below the current layer in the given state subset) - - ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above - if(iLayer>1)then - if(ixSnowOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSnowOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) ) - endif - - ! (cross-derivative terms for the layer below) - if(iLayer0)then !bottom snow layer and there is soil below - if(ixSoilOnlyNrg(1)/=integerMissing) aJac(ixSoilOnlyNrg(1),watState) = (dt/mLayerDepth(nSnow+1))*(-dNrgFlux_dWatAbove(nSnow) ) - endif - - endif ! (if the energy state for the current layer is within the state subset) - - end do ! (looping through snow layers) - endif ! (if there are state variables for both water and energy in the snow domain) - - ! ----- - ! * liquid water fluxes for the soil domain... - ! -------------------------------------------- - if(nSoilOnlyHyd>0)then - do iLayer=1,nSoil - - ! - check that the soil layer is desired - if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle - - ! - define state indices - watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset - - ! - define indices of the soil layers - jLayer = iLayer+nSnow ! index of layer in the snow+soil vector - - ! - compute the diagonal elements - ! all terms *excluding* baseflow - aJac(watState,watState) = (dt/mLayerDepth(jLayer))*(-dq_dHydStateBelow(iLayer-1) + dq_dHydStateAbove(iLayer)) + dMat(watState) - - ! - compute the lower-diagonal elements - if(iLayer>1)then - if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) - endif - - ! - compute the upper-diagonal elements - if(iLayer0 .and. nSoilOnlyNrg>0)then + do iLayer=1,nSoilOnlyNrg + + ! - check that the soil layer is desired + if(ixSoilOnlyNrg(iLayer)==integerMissing) cycle + + ! - define indices of the soil layers + jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + + ! - define the energy state variable + nrgState = ixNrgLayer(jLayer) ! index within the full state vector + + ! - define index of hydrology state variable within the state subset + watState = ixSoilOnlyHyd(iLayer) + + ! only compute derivatives if the water state for the current layer is within the state subset + if(watstate/=integerMissing)then + + ! - include derivatives in liquid water fluxes w.r.t. temperature for current layer + aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance + + ! - compute lower diagonal elements + if(iLayer>1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),nrgState),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 + endif + + ! compute upper-diagonal elements + if(iLayer=4 + if(computeVegFlux)then + aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) + aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) + aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 + endif + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) + endif + + ! - only include banded terms for derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) in banded structure + if(computeVegFlux)then + if(watState-ixCasNrg <= kl) aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanair(iLayer)) + aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) ! dVol/dT (K-1) + if(watState-ixVegNrg <= kl) aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanopy(iLayer)) + aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) ! dVol/dT (K-1) + if(watState-ixVegHyd <= kl) aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dCanWat(iLayer)) + aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) ! dVol/dLiq (kg m-2)-1 + if(watState-ixTopNrg <= kl) aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTGround(iLayer)) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) + endif + + ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer + aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerdTemp_dt(jLayer) & + + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present + aJac(ixOffDiag(nrgState,watState),watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) + aJac(ixOffDiag(nrgState,watState),watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + endif + + ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above + if(iLayer>1)then + if(ixSoilOnlyNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyNrg(iLayer-1),watState),watState) = (dt/mLayerDepth(jLayer-1))*( dNrgFlux_dWatBelow(jLayer-1) ) + elseif(iLayer==1 .and. nSnowOnlyNrg>0)then !top soil layer and there is snow above + if(ixSnowOnlyNrg(nSnow)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyNrg(nSnow),watState),watState) = (dt/mLayerDepth(nSnow))*( dNrgFlux_dWatBelow(nSnow) ) + endif + + ! (cross-derivative terms for the layer below) + if(iLayer0)then !have snow above first soil layer + if(ixSnowOnlyNrg(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) + if(ixVegNrg/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) + endif + + endif ! (if there are state variables for both water and energy in the soil domain) + + if(globalPrintFlag)then + print*, '** banded analytical Jacobian:' + write(*,'(a4,1x,100(i17,1x))') 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) + do iLayer=kl+1,nBands + write(*,'(i4,1x,100(e17.10,1x))') iLayer, (aJac(iLayer,jLayer),jLayer=min(iJac1,nState),min(iJac2,nState)) + end do endif - - ! - include terms for baseflow - if(computeBaseflow .and. nSoilOnlyHyd==nSoil)then - do pLayer=1,nSoil - qState = ixSoilOnlyHyd(pLayer) ! hydrology state index within the state subset - aJac(watState,qState) = (dt/mLayerDepth(jLayer))*dBaseflow_dMatric(iLayer,pLayer) + aJac(watState,qState) - end do + + ! ********************************************************************************************************************************************************* + ! ********************************************************************************************************************************************************* + ! * PART 2: FULL MATRIX + ! ********************************************************************************************************************************************************* + ! ********************************************************************************************************************************************************* + case(ixFullMatrix) + + ! check + if(size(aJac,1)/=size(dMat) .or. size(aJac,2)/=size(dMat))then + message=trim(message)//'unexpected shape of the Jacobian matrix: expect aJac(nState,nState)' + err=20; return endif - - ! - include terms for surface infiltration below surface - if(ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),watState) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(iLayer) + aJac(ixSoilOnlyHyd(1),watState) - - end do ! (looping through hydrology states in the soil domain) - - ! - include terms for surface infiltration above surface - if(nSnowOnlyHyd>0)then !have snow above first soil layer - if(ixSnowOnlyHyd(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) - elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) - if(ixVegHyd/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegHyd) - endif - - - endif ! (if the subset includes hydrology state variables in the soil domain) - - ! ----- - ! * liquid water fluxes for the aquifer... - ! ---------------------------------------- - if(ixAqWat/=integerMissing) then - aJac(ixAqWat,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) - aJac(ixAqWat,ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk - aJac(ixAqWat,ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat - ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) - if(computeVegFlux)then - aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) - aJac(ixAqWat,ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) - aJac(ixAqWat,ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 - aJac(ixAqWat,ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) - endif - endif - - ! ----- - ! * cross derivatives in the soil domain... - ! ----------------------------------------------------------------------------- - if(nSoilOnlyHyd>0 .and. nSoilOnlyNrg>0)then - do iLayer=1,nSoilOnlyNrg - - ! - check that the soil layer is desired - if(ixSoilOnlyNrg(iLayer)==integerMissing) cycle - - ! - define indices of the soil layers - jLayer = iLayer+nSnow ! index of layer in the snow+soil vector - - ! - define the energy state variable - nrgState = ixNrgLayer(jLayer) ! index within the full state vector - - ! - define index of hydrology state variable within the state subset - watState = ixSoilOnlyHyd(iLayer) - - ! only compute derivatives if the water state for the current layer is within the state subset - if(watstate/=integerMissing)then - - ! - include derivatives in liquid water fluxes w.r.t. temperature for current layer - aJac(watState,nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance - - ! - compute lower diagonal elements - if(iLayer>1)then - if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 - endif - - ! compute upper-diagonal elements - if(iLayer0)then + do iLayer=1,nLayers ! loop through all layers in the snow+soil domain + + ! check if the state is in the subset + if(ixSnowSoilNrg(iLayer)==integerMissing) cycle + + ! - define index within the state subset and the full state vector + jState = ixSnowSoilNrg(iLayer) ! index within the state subset + + ! - diagonal elements + aJac(jState,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) + + ! - lower-diagonal elements + if(iLayer>1)then + if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixSnowSoilNrg(iLayer-1),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) + endif + + ! - upper diagonal elements + if(iLayer0)then + do iLayer=1,nSnow ! loop through layers in the snow domain + + ! - check that the snow layer is desired + if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle + + ! - define state indices for the current layer + watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset + + ! compute factor to convert liquid water derivative to total water derivative + select case( ixHydType(iLayer) ) + case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) + case default; convLiq2tot = 1._rkind + end select + + ! - diagonal elements + aJac(watState,watState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot + dMat(watState) + + ! - lower-diagonal elements + if(iLayer>1)then + if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSnowOnlyHyd(iLayer-1),watState) = 0._rkind ! sub-diagonal: no dependence on other layers + endif + + ! - upper diagonal elements + if(iLayer0 .and. nSnowOnlyNrg>0)then + do iLayer=1,nSnow ! loop through layers in the snow domain + + ! - check that the snow layer is desired + if(ixSnowOnlyNrg(iLayer)==integerMissing) cycle + + ! (define the energy state) + nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector + + ! - define state indices for the current layer + watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset + + if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) + + ! - include derivatives of energy fluxes w.r.t water fluxes for current layer + aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water & + + dVolHtCapBulk_dTheta(iLayer) * mLayerdTemp_dt(iLayer) & + + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) ! (dF/dLiq) + + ! - include derivatives of water fluxes w.r.t energy fluxes for current layer + aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) + + ! (cross-derivative terms for the layer below) + if(iLayer < nSnow)then + aJac(ixSnowOnlyHyd(iLayer+1),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 + endif ! (if there is a water state in the layer below the current layer in the given state subset) + + ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above + if(iLayer>1)then + if(ixSnowOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSnowOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dWatBelow(iLayer-1) ) + endif + + ! (cross-derivative terms for the layer below) + if(iLayer0)then !bottom snow layer and there is soil below + if(ixSoilOnlyNrg(1)/=integerMissing) aJac(ixSoilOnlyNrg(1),watState) = (dt/mLayerDepth(nSnow+1))*(-dNrgFlux_dWatAbove(nSnow) ) + endif + + endif ! (if the energy state for the current layer is within the state subset) + + end do ! (looping through snow layers) + endif ! (if there are state variables for both water and energy in the snow domain) + + ! ----- + ! * liquid water fluxes for the soil domain... + ! -------------------------------------------- + if(nSoilOnlyHyd>0)then + do iLayer=1,nSoil + + ! - check that the soil layer is desired + if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle + + ! - define state indices + watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset + + ! - define indices of the soil layers + jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + + ! - compute the diagonal elements + ! all terms *excluding* baseflow + aJac(watState,watState) = (dt/mLayerDepth(jLayer))*(-dq_dHydStateBelow(iLayer-1) + dq_dHydStateAbove(iLayer)) + dMat(watState) + + ! - compute the lower-diagonal elements + if(iLayer>1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) + endif + + ! - compute the upper-diagonal elements + if(iLayer0)then !have snow above first soil layer + if(ixSnowOnlyHyd(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) + if(ixVegHyd/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegHyd) + endif + + endif ! (if the subset includes hydrology state variables in the soil domain) + + ! ----- + ! * liquid water fluxes for the aquifer... + ! ---------------------------------------- + if(ixAqWat/=integerMissing) then + aJac(ixAqWat,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) + aJac(ixAqWat,ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk + aJac(ixAqWat,ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat + ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) if(computeVegFlux)then - aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) - aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) - aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 + aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) + aJac(ixAqWat,ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) + aJac(ixAqWat,ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 + aJac(ixAqWat,ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) endif - aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) - endif - - ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) - if(computeVegFlux)then - aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanair(iLayer)) + aJac(watState,ixCasNrg) ! dVol/dT (K-1) - aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanopy(iLayer)) + aJac(watState,ixVegNrg) ! dVol/dT (K-1) - aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dCanWat(iLayer)) + aJac(watState,ixVegHyd) ! dVol/dLiq (kg m-2)-1 - aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTGround(iLayer)) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) - endif - - ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerdTemp_dt(jLayer) & - + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) - if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present - aJac(nrgState,watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content - endif - - ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above - if(iLayer>1)then - if(ixSoilOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSoilOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(jLayer-1))*( dNrgFlux_dWatBelow(jLayer-1) ) - elseif(iLayer==1 .and. nSnowOnlyNrg>0)then !top soil layer and there is snow above - if(ixSnowOnlyNrg(nSnow)/=integerMissing) aJac(ixSnowOnlyNrg(nSnow),watState) = (dt/mLayerDepth(nSnow))*( dNrgFlux_dWatBelow(nSnow) ) - endif - - ! (cross-derivative terms for the layer below) - if(iLayer0)then !have snow above first soil layer - if(ixSnowOnlyNrg(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) - elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) - if(ixVegNrg/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegNrg) - endif - - endif ! (if there are state variables for both water and energy in the soil domain) - - ! print the Jacobian - if(globalPrintFlag)then - print*, '** analytical Jacobian (full):' - write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) - do iLayer=min(iJac1,nState),min(iJac2,nState) - write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(min(iJac1,nState):min(iJac2,nState),iLayer) - end do - endif - - !print*, '** analytical Jacobian (full):' - !write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=1,size(aJac,2)) - !do iLayer=1,size(aJac,1) - ! write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(iLayer,1:size(aJac,2)) - !end do - !print *, '--------------------------------------------------------------' - - ! *** - ! check - case default; err=20; message=trim(message)//'unable to identify option for the type of matrix'; return - + endif + + ! ----- + ! * cross derivatives in the soil domain... + ! ---------------------------------------- + if(nSoilOnlyHyd>0 .and. nSoilOnlyNrg>0)then + do iLayer=1,nSoilOnlyNrg + + ! - check that the soil layer is desired + if(ixSoilOnlyNrg(iLayer)==integerMissing) cycle + + ! - define indices of the soil layers + jLayer = iLayer+nSnow ! index of layer in the snow+soil vector + + ! - define the energy state variable + nrgState = ixNrgLayer(jLayer) ! index within the full state vector + + ! - define index of hydrology state variable within the state subset + watState = ixSoilOnlyHyd(iLayer) + + ! only compute derivatives if the water state for the current layer is within the state subset + if(watstate/=integerMissing)then + + ! - include derivatives in liquid water fluxes w.r.t. temperature for current layer + aJac(watState,nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance + + ! - compute lower diagonal elements + if(iLayer>1)then + if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 + endif + + ! compute upper-diagonal elements + if(iLayer verySmall)then ! ice is present + aJac(nrgState,watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + endif + + ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above + if(iLayer>1)then + if(ixSoilOnlyNrg(iLayer-1)/=integerMissing) aJac(ixSoilOnlyNrg(iLayer-1),watState) = (dt/mLayerDepth(jLayer-1))*( dNrgFlux_dWatBelow(jLayer-1) ) + elseif(iLayer==1 .and. nSnowOnlyNrg>0)then !top soil layer and there is snow above + if(ixSnowOnlyNrg(nSnow)/=integerMissing) aJac(ixSnowOnlyNrg(nSnow),watState) = (dt/mLayerDepth(nSnow))*( dNrgFlux_dWatBelow(nSnow) ) + endif + + ! (cross-derivative terms for the layer below) + if(iLayer0)then !have snow above first soil layer + if(ixSnowOnlyNrg(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) + if(ixVegNrg/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegNrg) + endif + + endif ! (if there are state variables for both water and energy in the soil domain) + + ! print the Jacobian + if(globalPrintFlag)then + print*, '** analytical Jacobian (full):' + write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) + do iLayer=min(iJac1,nState),min(iJac2,nState) + write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(min(iJac1,nState):min(iJac2,nState),iLayer) + end do + endif + + ! check + case default; err=20; message=trim(message)//'unable to identify option for the type of matrix'; return + end select ! type of matrix - + if(any(isNan(aJac)))then - print *, '******************************* WE FOUND NAN IN JACOBIAN ************************************' - stop 1 - message=trim(message)//'we found NaN' - err=20; return + print *, '******************************* WE FOUND NAN IN JACOBIAN ************************************' + stop 1 + message=trim(message)//'we found NaN' + err=20; return endif - - ! end association to variables in the data structures - end associate - - end subroutine computJacob - - - ! ********************************************************************************************************** - ! private function: get the off-diagonal index in the band-diagonal matrix - ! ********************************************************************************************************** - function ixOffDiag(jState,iState) - implicit none - integer(i4b),intent(in) :: jState ! off-diagonal state - integer(i4b),intent(in) :: iState ! diagonal state - integer(i4b) :: ixOffDiag ! off-diagonal index in gthe band-diagonal matrix - ixOffDiag = ixBandOffset + jState - iState - end function ixOffDiag - - end module computJacob_module \ No newline at end of file + + ! end association to variables in the data structures + end associate + +end subroutine computJacob + + +! ********************************************************************************************************** +! private function: get the off-diagonal index in the band-diagonal matrix +! ********************************************************************************************************** +function ixOffDiag(jState,iState) + implicit none + integer(i4b),intent(in) :: jState ! off-diagonal state + integer(i4b),intent(in) :: iState ! diagonal state + integer(i4b),parameter :: ixDiag=kl+ku+1 ! index for the diagonal, the offset in the band Jacobian matrix + integer(i4b) :: ixOffDiag ! off-diagonal index in gthe band-diagonal matrix + + ixOffDiag = ixDiag + jState - iState +end function ixOffDiag + +end module computJacob_module \ No newline at end of file diff --git a/build/source/engine/computJacobSundials.f90 b/build/source/engine/computJacobSundials.f90 index 518c4f2a2..3ed4be276 100644 --- a/build/source/engine/computJacobSundials.f90 +++ b/build/source/engine/computJacobSundials.f90 @@ -21,7 +21,7 @@ module computJacobSundials_module ! data types -use nrtype +USE nrtype ! derived types to define the data structures USE data_types,only:& @@ -64,10 +64,8 @@ module computJacobSundials_module USE globalData,only:model_decisions ! model decision structure ! access named variables to describe the form and structure of the matrices used in the numerical solver -USE globalData,only: ku ! number of super-diagonal bands -USE globalData,only: kl ! number of sub-diagonal bands -USE globalData,only: ixDiag ! index for the diagonal band -USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix +USE globalData,only: ku ! number of super-diagonal bands, assume ku>=3 +USE globalData,only: kl ! number of sub-diagonal bands, assume kl>=4 USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix USE globalData,only: iJac1 ! first layer of the Jacobian to print @@ -103,7 +101,6 @@ module computJacobSundials_module implicit none ! define constants real(rkind),parameter :: verySmall=tiny(1.0_rkind) ! a very small number -integer(i4b),parameter :: ixBandOffset=kl+ku+1 ! offset in the band Jacobian matrix private public::computJacobSundials @@ -186,6 +183,8 @@ subroutine computJacobSundials(& ! -------------------------------------------------------------- ! * local variables ! -------------------------------------------------------------- + integer(i4b),parameter :: ixDiag=kl+ku+1 ! index for the diagonal band for sundials + integer(i4b),parameter :: nBands=2*kl+ku+1 ! length of the leading dimension of the band diagonal matrix for sundials ! indices of model state variables integer(i4b) :: jState ! index of state within the state subset integer(i4b) :: qState ! index of cross-derivative state variable for baseflow @@ -391,7 +390,7 @@ subroutine computJacobSundials(& ! dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy is the derivative in throughfall and canopy drainage with canopy temperature if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixVegNrg),ixVegNrg) = -dCanopyEvaporation_dTCanopy*dt + dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy ! * liquid water fluxes for vegetation canopy (-), dt*scalarFracLiqVeg*scalarCanopyLiqDeriv is the derivative in throughfall and canopy drainage with canopy water - aJac(ixDiag, ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanWat - scalarCanopyLiqDeriv)*dt + 1._rkind * cj + aJac(ixDiag, ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanWat - scalarCanopyLiqDeriv)*dt + 1._rkind * cj if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixTopNrg),ixTopNrg) = -dCanopyEvaporation_dTGround*dt ! * cross-derivative terms w.r.t. canopy water (kg-1 m2) @@ -405,7 +404,7 @@ subroutine computJacobSundials(& if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) endif - ! * -derivative terms w.r.t. canopy temperature (K-1) + ! * cross-derivative terms w.r.t. canopy temperature (K-1) if(ixVegNrg/=integerMissing)then if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarCanopyLiqDeriv*dCanLiq_dTcanopy)/iden_water endif @@ -420,7 +419,7 @@ subroutine computJacobSundials(& ! * energy fluxes with the vegetation canopy (J m-3 K-1) if(ixVegNrg/=integerMissing)then if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixCasNrg),ixCasNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanairTemp) - aJac(ixDiag, ixVegNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanopyTemp) + dMat(ixVegNrg) + aJac(ixDiag, ixVegNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanopyTemp) + dMat(ixVegNrg) if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixTopNrg),ixTopNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dGroundTemp) endif @@ -511,7 +510,7 @@ subroutine computJacobSundials(& if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) - ! - include derivatives of energy fluxes w.r.t water fluxes for current layer + ! - include derivatives of energy fluxes w.r.t water fluxes for current layer aJac(ixOffDiag(nrgState,watState),watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) & + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) & @@ -571,8 +570,28 @@ subroutine computJacobSundials(& if(ixSoilOnlyHyd(iLayer+1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer+1),watState),watState) = (dt/mLayerDepth(jLayer+1))*(-dq_dHydStateAbove(iLayer)) endif + ! - only include banded terms for baseflow in banded structure + if(computeBaseflow .and. nSoilOnlyHyd==nSoil)then + do pLayer=1,nSoil + qState = ixSoilOnlyHyd(pLayer) ! hydrology state index within the state subset + if(qState/=integerMissing .and. (qstate - watState <= ku) .and. (watState - qstate <= kl)) & + aJac(ixOffDiag(watState,qState),qState) = (dt/mLayerDepth(jLayer))*dBaseflow_dMatric(iLayer,pLayer) + aJac(ixOffDiag(watState,qState),qState) + end do + endif + + ! - only include banded terms for surface infiltration below surface in banded structure; ixSoilOnlyHyd(1) - watState always <= kl + if(ixSoilOnlyHyd(1)/=integerMissing .and. (watState - ixSoilOnlyHyd(1) <= ku)) & + aJac(ixOffDiag(ixSoilOnlyHyd(1),watState),watState) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(iLayer) + aJac(ixOffDiag(ixSoilOnlyHyd(1),watState),watState) + end do ! (looping through hydrology states in the soil domain) + ! - include terms for surface infiltration above surface + if(nSnowOnlyHyd>0)then !have snow above first soil layer + if(ixSnowOnlyHyd(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyHyd(nSnow)),ixSnowOnlyHyd(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) + if(ixVegHyd/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) + endif + endif ! (if the subset includes hydrology state variables in the soil domain) ! ----- @@ -582,6 +601,13 @@ subroutine computJacobSundials(& aJac(ixDiag,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) * cj aJac(ixOffDiag(ixAqWat,ixSoilOnlyNrg(nSoil)),ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt aJac(ixOffDiag(ixAqWat,ixSoilOnlyHyd(nSoil)),ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt + ! - only include banded derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration), would have to have few soil layers + if(computeVegFlux)then + if(ixAqWat-ixCasNrg <= kl) aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) + if(ixAqWat-ixVegNrg <= kl) aJac(ixAqWat,ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) + if(ixAqWat-ixVegHyd <= kl) aJac(ixAqWat,ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 + if(ixAqWat-ixTopNrg <= kl) aJac(ixAqWat,ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) + endif endif ! ----- @@ -619,13 +645,21 @@ subroutine computJacobSundials(& endif ! - include derivatives of energy w.r.t. ground evaporation - if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer - if(computeVegFlux)then - aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) - aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) - aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 + if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer, assume here that kl>=4 + if(computeVegFlux)then + aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) + aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) + aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 + endif + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) endif - aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) + + ! - only include banded terms for derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) in banded structure + if(computeVegFlux)then + if(watState-ixCasNrg <= kl) aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanair(iLayer)) + aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) ! dVol/dT (K-1) + if(watState-ixVegNrg <= kl) aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanopy(iLayer)) + aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) ! dVol/dT (K-1) + if(watState-ixVegHyd <= kl) aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dCanWat(iLayer)) + aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) ! dVol/dLiq (kg m-2)-1 + if(watState-ixTopNrg <= kl) aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTGround(iLayer)) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer @@ -650,7 +684,18 @@ subroutine computJacobSundials(& endif ! (if the water state for the current layer is within the state subset) - end do ! (looping through soil layers) + ! - only include banded terms for surface infiltration below surface in banded structure; ixSoilOnlyHyd(1) - nrgState always <= kl + if(ixSoilOnlyHyd(1)/=integerMissing .and. (nrgState - ixSoilOnlyHyd(1) <= ku)) & + aJac(ixOffDiag(ixSoilOnlyHyd(1),nrgState),nrgState) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(iLayer) + aJac(ixOffDiag(ixSoilOnlyHyd(1),nrgState),nrgState) + + end do ! (looping through energy states in the soil domain) + + ! - include terms for surface infiltration above surface + if(nSnowOnlyNrg>0)then !have snow above first soil layer + if(ixSnowOnlyNrg(nSnow)/=integerMissing .and. ixSoilOnlyHyd(1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(1),ixSnowOnlyNrg(nSnow)),ixSnowOnlyNrg(nSnow)) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + elseif(computeVegFlux)then !have vegetation above first soil layer, ixTopHyd = ixSoilOnlyHyd(1) + if(ixVegNrg/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) + endif endif ! (if there are state variables for both water and energy in the soil domain) @@ -702,14 +747,14 @@ subroutine computJacobSundials(& if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) endif - ! * -derivative terms w.r.t. canopy temperature (K-1) + ! * cross-derivative terms w.r.t. canopy temperature (K-1) if(ixVegNrg/=integerMissing)then if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegNrg) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarCanopyLiqDeriv*dCanLiq_dTcanopy)/iden_water endif ! * energy fluxes with the canopy air space (J m-3 K-1) if(ixCasNrg/=integerMissing)then - aJac(ixCasNrg,ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) * cj + aJac(ixCasNrg,ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) * cj if(ixVegNrg/=integerMissing) aJac(ixCasNrg,ixVegNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanopyTemp) if(ixTopNrg/=integerMissing) aJac(ixCasNrg,ixTopNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dGroundTemp) endif @@ -872,7 +917,7 @@ subroutine computJacobSundials(& if(computeBaseflow .and. nSoilOnlyHyd==nSoil)then do pLayer=1,nSoil qState = ixSoilOnlyHyd(pLayer) ! hydrology state index within the state subset - aJac(watState,qState) = (dt/mLayerDepth(jLayer))*dBaseflow_dMatric(iLayer,pLayer) + aJac(watState,qState) + if(qState/=integerMissing) aJac(watState,qState) = (dt/mLayerDepth(jLayer))*dBaseflow_dMatric(iLayer,pLayer) + aJac(watState,qState) end do endif @@ -942,11 +987,11 @@ subroutine computJacobSundials(& ! - include derivatives of energy w.r.t. ground evaporation if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer - if(computeVegFlux)then - aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) - aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) - aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 - endif + if(computeVegFlux)then + aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) + aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) + aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 + endif aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) endif @@ -1333,6 +1378,7 @@ integer(c_int) function computJacob4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & implicit none ! calling variables + integer(i4b),parameter :: nBands=2*kl+ku+1 ! length of the leading dimension of the band diagonal matrix for sundials real(rkind), value :: t ! current time real(rkind), value :: cj ! step size scaling factor type(N_Vector) :: sunvec_y ! solution N_Vector @@ -1410,8 +1456,9 @@ function ixOffDiag(jState,iState) implicit none integer(i4b),intent(in) :: jState ! off-diagonal state integer(i4b),intent(in) :: iState ! diagonal state + integer(i4b),parameter :: ixDiag=kl+ku+1 ! index for the diagonal, the offset in the band Jacobian matrix integer(i4b) :: ixOffDiag ! off-diagonal index in gthe band-diagonal matrix - ixOffDiag = ixBandOffset + jState - iState + ixOffDiag = ixDiag + jState - iState end function ixOffDiag end module computJacobSundials_module diff --git a/build/source/engine/matrixOper.f90 b/build/source/engine/matrixOper.f90 index a3a3d6e15..bb96edabb 100644 --- a/build/source/engine/matrixOper.f90 +++ b/build/source/engine/matrixOper.f90 @@ -30,7 +30,6 @@ module matrixOper_module USE globalData,only: nRHS ! number of unknown variables on the RHS of the linear system A.X=B USE globalData,only: ku ! number of super-diagonal bands USE globalData,only: kl ! number of sub-diagonal bands -USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix USE globalData,only: iJac1 ! first layer of the Jacobian to print @@ -158,14 +157,16 @@ subroutine lapackSolv(ixMatrix,nState,aJac,rVec,xInc,err,message) ! dummy integer(i4b),intent(in) :: ixMatrix ! type of matrix (full Jacobian or band diagonal) integer(i4b),intent(in) :: nState ! number of state variables - real(rkind),intent(inout) :: aJac(:,:) ! input = the Jacobian matrix A; output = decomposed matrix - real(rkind),intent(in) :: rVec(:) ! the residual vector B - real(rkind),intent(out) :: xInc(:) ! the solution vector X + real(rkind),intent(inout) :: aJac(:,:) ! input = the Jacobian matrix A; output = decomposed matrix + real(rkind),intent(in) :: rVec(:) ! the residual vector B + real(rkind),intent(out) :: xInc(:) ! the solution vector X integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local - real(rkind) :: rhs(nState,1) ! the nState-by-nRHS matrix of matrix B, for the linear system A.X=B + real(rkind) :: rhs(nState,1) ! the nState-by-nRHS matrix of matrix B, for the linear system A.X=B integer(i4b) :: iPiv(nState) ! defines if row i of the matrix was interchanged with row iPiv(i) + integer(i4b),parameter :: nBands=2*kl+ku+1 ! length of the leading dimension of the band diagonal matrix for lapack + ! initialize error control select case(ixMatrix) case(ixFullMatrix); message='lapackSolv/dgesv/' diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 38c6b72d8..ae8ee86a6 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -31,13 +31,6 @@ module opSplittin_module USE globalData,only:realMissing ! missing double precision number USE globalData,only:quadMissing ! missing quadruple precision number -! access matrix information -USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix -USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix -USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix -USE globalData,only: iJac1 ! first layer of the Jacobian to print -USE globalData,only: iJac2 ! last layer of the Jacobian to print - ! domain types USE globalData,only:iname_cas ! named variables for the canopy air space USE globalData,only:iname_veg ! named variables for vegetation @@ -830,7 +823,7 @@ subroutine opSplittin(& ! check case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return end select - + if(err/=0)then message=trim(message)//trim(cmessage) if(err>0) return diff --git a/build/source/engine/summaSolve.f90 b/build/source/engine/summaSolve.f90 index a8955bb0d..086df3f79 100644 --- a/build/source/engine/summaSolve.f90 +++ b/build/source/engine/summaSolve.f90 @@ -38,7 +38,6 @@ module summaSolve_module ! access named variables to describe the form and structure of the matrices used in the numerical solver USE globalData,only: ku ! number of super-diagonal bands USE globalData,only: kl ! number of sub-diagonal bands -USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix USE globalData,only: iJac1 ! first layer of the Jacobian to print @@ -190,6 +189,7 @@ subroutine summaSolve(& ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- ! Jacobian matrix + integer(i4b),parameter :: nBands=2*kl+ku+1 ! length of the leading dimension of the band diagonal matrix for lapack logical(lgt),parameter :: doNumJacobian=.false. ! flag to compute the numerical Jacobian matrix logical(lgt),parameter :: testBandDiagonal=.false. ! flag to test the band diagonal Jacobian matrix real(rkind) :: nJac(nState,nState) ! numerical Jacobian matrix diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index de4b2125b..280bdb630 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -18,11 +18,8 @@ module summaSolveSundialsIDA_module USE globalData,only:quadMissing ! missing quadruple precision number ! access matrix information -USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix -USE globalData,only: iJac1 ! first layer of the Jacobian to print -USE globalData,only: iJac2 ! last layer of the Jacobian to print USE globalData,only: ku ! number of super-diagonal bands USE globalData,only: kl ! number of sub-diagonal bands diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 256ce8481..a2f4d65c5 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -32,7 +32,8 @@ module systemSolv_module USE globalData,only:quadMissing ! missing quadruple precision number ! access matrix information -USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix +USE globalData,only: ku ! number of super-diagonal bands, assume ku>=3 +USE globalData,only: kl ! number of sub-diagonal bands, assume kl>=4 USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix USE globalData,only: iJac1 ! first layer of the Jacobian to print @@ -205,6 +206,7 @@ subroutine systemSolv(& ! * model solver ! ------------------------------------------------------------------------------------------------------ logical(lgt),parameter :: forceFullMatrix=.false. ! flag to force the use of the full Jacobian matrix + integer(i4b),parameter :: nBands=2*kl+ku+1 ! length of the leading dimension of the band diagonal matrix for lapack integer(i4b) :: maxiter ! maximum number of iterations integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) integer(i4b) :: localMaxIter ! maximum number of iterations (depends on solution type) @@ -556,12 +558,12 @@ subroutine systemSolv(& stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt + resSinkNew(iState)) end do ! looping through non-missing water state variables in the soil domain endif - - if ( allocated(dBaseflow_dMatric) ) deallocate(dBaseflow_dMatric) + + if ( allocated(dBaseflow_dMatric) ) deallocate(dBaseflow_dMatric) ! end associate statements end associate globalVars - + end subroutine systemSolv diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 054d34bec..c974e03a1 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -14,7 +14,6 @@ module systemSolvSundials_module USE globalData,only:quadMissing ! missing quadruple precision number ! access matrix information -USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix USE globalData,only: iJac1 ! first layer of the Jacobian to print @@ -188,7 +187,7 @@ subroutine systemSolvSundials(& ! ------------------------------------------------------------------------------------------------------ ! * model solver ! ------------------------------------------------------------------------------------------------------ - logical(lgt),parameter :: forceFullMatrix=.true. ! flag to force the use of the full Jacobian matrix + logical(lgt),parameter :: forceFullMatrix=.false. ! flag to force the use of the full Jacobian matrix integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) type(var_dlength) :: flux_init ! model fluxes at the start of the time step real(rkind),allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! NOTE: allocatable, since not always needed From b641e7aa1f5ad2642cfbecbfc009c9056eaea851 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 11 Oct 2022 22:32:11 +0900 Subject: [PATCH 0399/1472] Put back ixDiag and nBands as global variables, sundials also uses lapack form of banded matrix --- build/source/dshare/globalData.f90 | 6 ++++-- build/source/engine/computJacob.f90 | 4 ++-- build/source/engine/computJacobSundials.f90 | 7 +++---- build/source/engine/matrixOper.f90 | 3 +-- build/source/engine/summaSolve.f90 | 2 +- build/source/engine/systemSolv.f90 | 4 +--- 6 files changed, 12 insertions(+), 14 deletions(-) diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 index c0b94f45b..313583e52 100644 --- a/build/source/dshare/globalData.f90 +++ b/build/source/dshare/globalData.f90 @@ -150,11 +150,13 @@ MODULE globalData integer(i4b),parameter,public :: iname_watAquifer=3006 ! named variable defining the water storage in the aquifer ! define named variables to describe the form and structure of the band-diagonal matrices used in the numerical solver - ! NOTE: lapack requires kl extra rows for additional storage versus sundials - ! Consequently, in lapack all indices are offset by kl and the total number of bands for storage is 2*kl+ku+1 instead of kl+ku+1. + ! NOTE: This indexing scheme provides the matrix structure expected by lapack and sundials. Specifically, they require kl extra rows for additional storage. + ! Consequently, all indices are offset by kl and the total number of bands for storage is 2*kl+ku+1 instead of kl+ku+1. integer(i4b),parameter,public :: nRHS=1 ! number of unknown variables on the RHS of the linear system A.X=B integer(i4b),parameter,public :: ku=3 ! number of super-diagonal bands, ku>=3 to accommodate coupled layer above integer(i4b),parameter,public :: kl=4 ! number of sub-diagonal bands, kl>=4 to accommodate vegetation + integer(i4b),parameter,public :: ixDiag=kl+ku+1 ! index for the diagonal band + integer(i4b),parameter,public :: nBands=2*kl+ku+1 ! length of the leading dimension of the band diagonal matrix ! define named variables for the type of matrix used in the numerical solution. integer(i4b),parameter,public :: ixFullMatrix=1001 ! named variable for the full Jacobian matrix diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 7843e863c..c2557abca 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -59,6 +59,8 @@ module computJacob_module ! access named variables to describe the form and structure of the matrices used in the numerical solver USE globalData,only: ku ! number of super-diagonal bands, assume ku>=3 USE globalData,only: kl ! number of sub-diagonal bands, assume kl>=4 +USE globalData,only: ixDiag ! index for the diagonal band +USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix USE globalData,only: iJac1 ! first layer of the Jacobian to print @@ -125,8 +127,6 @@ subroutine computJacob(& ! -------------------------------------------------------------- ! * local variables ! -------------------------------------------------------------- - integer(i4b),parameter :: ixDiag=kl+ku+1 ! index for the diagonal band for lapack - integer(i4b),parameter :: nBands=2*kl+ku+1 ! length of the leading dimension of the band diagonal matrix for lapack ! indices of model state variables integer(i4b) :: jState ! index of state within the state subset integer(i4b) :: qState ! index of cross-derivative state variable for baseflow diff --git a/build/source/engine/computJacobSundials.f90 b/build/source/engine/computJacobSundials.f90 index 3ed4be276..c64d0df0c 100644 --- a/build/source/engine/computJacobSundials.f90 +++ b/build/source/engine/computJacobSundials.f90 @@ -66,6 +66,8 @@ module computJacobSundials_module ! access named variables to describe the form and structure of the matrices used in the numerical solver USE globalData,only: ku ! number of super-diagonal bands, assume ku>=3 USE globalData,only: kl ! number of sub-diagonal bands, assume kl>=4 +USE globalData,only: ixDiag ! index for the diagonal band +USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix USE globalData,only: iJac1 ! first layer of the Jacobian to print @@ -183,8 +185,6 @@ subroutine computJacobSundials(& ! -------------------------------------------------------------- ! * local variables ! -------------------------------------------------------------- - integer(i4b),parameter :: ixDiag=kl+ku+1 ! index for the diagonal band for sundials - integer(i4b),parameter :: nBands=2*kl+ku+1 ! length of the leading dimension of the band diagonal matrix for sundials ! indices of model state variables integer(i4b) :: jState ! index of state within the state subset integer(i4b) :: qState ! index of cross-derivative state variable for baseflow @@ -1378,7 +1378,6 @@ integer(c_int) function computJacob4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & implicit none ! calling variables - integer(i4b),parameter :: nBands=2*kl+ku+1 ! length of the leading dimension of the band diagonal matrix for sundials real(rkind), value :: t ! current time real(rkind), value :: cj ! step size scaling factor type(N_Vector) :: sunvec_y ! solution N_Vector @@ -1456,8 +1455,8 @@ function ixOffDiag(jState,iState) implicit none integer(i4b),intent(in) :: jState ! off-diagonal state integer(i4b),intent(in) :: iState ! diagonal state - integer(i4b),parameter :: ixDiag=kl+ku+1 ! index for the diagonal, the offset in the band Jacobian matrix integer(i4b) :: ixOffDiag ! off-diagonal index in gthe band-diagonal matrix + ixOffDiag = ixDiag + jState - iState end function ixOffDiag diff --git a/build/source/engine/matrixOper.f90 b/build/source/engine/matrixOper.f90 index bb96edabb..a5fed26ad 100644 --- a/build/source/engine/matrixOper.f90 +++ b/build/source/engine/matrixOper.f90 @@ -30,6 +30,7 @@ module matrixOper_module USE globalData,only: nRHS ! number of unknown variables on the RHS of the linear system A.X=B USE globalData,only: ku ! number of super-diagonal bands USE globalData,only: kl ! number of sub-diagonal bands +USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix USE globalData,only: iJac1 ! first layer of the Jacobian to print @@ -165,8 +166,6 @@ subroutine lapackSolv(ixMatrix,nState,aJac,rVec,xInc,err,message) ! local real(rkind) :: rhs(nState,1) ! the nState-by-nRHS matrix of matrix B, for the linear system A.X=B integer(i4b) :: iPiv(nState) ! defines if row i of the matrix was interchanged with row iPiv(i) - integer(i4b),parameter :: nBands=2*kl+ku+1 ! length of the leading dimension of the band diagonal matrix for lapack - ! initialize error control select case(ixMatrix) case(ixFullMatrix); message='lapackSolv/dgesv/' diff --git a/build/source/engine/summaSolve.f90 b/build/source/engine/summaSolve.f90 index 086df3f79..a8955bb0d 100644 --- a/build/source/engine/summaSolve.f90 +++ b/build/source/engine/summaSolve.f90 @@ -38,6 +38,7 @@ module summaSolve_module ! access named variables to describe the form and structure of the matrices used in the numerical solver USE globalData,only: ku ! number of super-diagonal bands USE globalData,only: kl ! number of sub-diagonal bands +USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix USE globalData,only: iJac1 ! first layer of the Jacobian to print @@ -189,7 +190,6 @@ subroutine summaSolve(& ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- ! Jacobian matrix - integer(i4b),parameter :: nBands=2*kl+ku+1 ! length of the leading dimension of the band diagonal matrix for lapack logical(lgt),parameter :: doNumJacobian=.false. ! flag to compute the numerical Jacobian matrix logical(lgt),parameter :: testBandDiagonal=.false. ! flag to test the band diagonal Jacobian matrix real(rkind) :: nJac(nState,nState) ! numerical Jacobian matrix diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index a2f4d65c5..31fe2388c 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -32,8 +32,7 @@ module systemSolv_module USE globalData,only:quadMissing ! missing quadruple precision number ! access matrix information -USE globalData,only: ku ! number of super-diagonal bands, assume ku>=3 -USE globalData,only: kl ! number of sub-diagonal bands, assume kl>=4 +USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix USE globalData,only: iJac1 ! first layer of the Jacobian to print @@ -206,7 +205,6 @@ subroutine systemSolv(& ! * model solver ! ------------------------------------------------------------------------------------------------------ logical(lgt),parameter :: forceFullMatrix=.false. ! flag to force the use of the full Jacobian matrix - integer(i4b),parameter :: nBands=2*kl+ku+1 ! length of the leading dimension of the band diagonal matrix for lapack integer(i4b) :: maxiter ! maximum number of iterations integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) integer(i4b) :: localMaxIter ! maximum number of iterations (depends on solution type) From e602e51431b3fe9f06ad8b7738125f9975a6b05f Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 13 Oct 2022 14:18:47 +0000 Subject: [PATCH 0400/1472] adjusted makefile --- build/makefile | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/build/makefile b/build/makefile index 046e5d806..cc9363107 100644 --- a/build/makefile +++ b/build/makefile @@ -97,9 +97,9 @@ ifeq "$(FC)" "gfortran" endif # Production runs -FLAGS_NOAH = -ffree-form -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_COMM = -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_SUMMA = -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) # Debug runs # FLAGS_NOAH = -p -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -Wall @@ -228,10 +228,7 @@ SUMMA_SOLVER= \ computThermConduct.f90 \ computResidSundials.f90 \ eval8summaSundials.f90 \ - evalDAE4IDA.f90 \ computJacobSundials.f90 \ - eval8JacDAE.f90 \ - evalJac4IDA.f90 \ computSnowDepth.f90 \ summaSolveSundialsIDA.f90 \ systemSolvSundials.f90 \ @@ -271,7 +268,6 @@ NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) SUMMA_MODRUN = \ indexState.f90 \ getVectorz.f90 \ - getVectorzAddSundials.f90 \ t2enthalpy.f90 \ updateVars.f90 \ updateVarsSundials.f90 \ @@ -359,21 +355,7 @@ endif #====================================================================== # Compile - -production: FLAGS_NOAH += -g -O3 -production: FLAGS_COMM += -g -O3 -production: FLAGS_SUMMA += -g -O3 -production: all - - - -debug: FLAGS_NOAH += -g -O0 -debug: FLAGS_COMM += -g -O0 -debug: FLAGS_SUMMA += -g -O0 -debug: all - all: compile_noah compile_comm compile_summa link clean install - part: compile_noah compile_comm compile_summa check: From cfcdae995828740b797fc31dd02378eba4d4ff2c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 14 Oct 2022 20:56:03 +0900 Subject: [PATCH 0401/1472] Change to max_order=5, and then needed to turn off massBalance and nrgBalance checks for Sundials, since tied to convergence criteria for bEuler. I am not sure why this didn't trigger problems when max_order was 1. --- build/source/engine/coupled_em.f90 | 17 ++++++-- build/source/engine/opSplittin.f90 | 5 ++- build/source/engine/summaSolveSundialsIDA.f90 | 2 +- build/source/engine/varSubstepSundials.f90 | 40 +++++++++---------- 4 files changed, 38 insertions(+), 26 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index d8c94eb40..b91d94f38 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -84,6 +84,11 @@ module coupled_em_module localColumn ,& ! separate groundwater representation in each local soil column singleBasin ! single groundwater store over the entire basin +! look-up values for the numerical method +USE mDecisions_module,only: & + sundials ,& ! SUNDIALS/IDA solution + bEuler ! home-grown backward Euler solution with long time step + ! privacy implicit none private @@ -198,7 +203,8 @@ subroutine coupled_em(& logical(lgt) :: tooMuchSublim ! flag to denote that there was too much sublimation in a given time step logical(lgt) :: doLayerMerge ! flag to denote the need to merge snow layers logical(lgt) :: pauseFlag ! flag to pause execution - logical(lgt),parameter :: backwardsCompatibility=.true. ! flag to denote a desire to ensure backwards compatibility with previous branches. + logical(lgt),parameter :: backwardsCompatibility=.true. ! flag to denote a desire to ensure backwards compatibility with previous branches + logical(lgt) :: checkMassBalance ! flag to check the mass balance type(var_ilength) :: indx_temp ! temporary model index variables type(var_dlength) :: prog_temp ! temporary model prognostic variables type(var_dlength) :: diag_temp ! temporary model diagnostic variables @@ -1007,6 +1013,9 @@ subroutine coupled_em(& ! * balance checks for the canopy... ! ---------------------------------- + if (model_decisions(iLookDECISIONS%num_method)%iDecision==bEuler) checkMassBalance = .true. ! convergence criteria for bEuler + if (model_decisions(iLookDECISIONS%num_method)%iDecision==sundials) checkMassBalance = .false. ! sundials does not use this criteria + ! if computing the vegetation flux if(computeVegFlux)then @@ -1017,7 +1026,7 @@ subroutine coupled_em(& ! NOTE: need to put the balance checks in the sub-step loop so that we can re-compute if necessary scalarCanopyWatBalError = balanceCanopyWater1 - (balanceCanopyWater0 + (scalarSnowfall - averageThroughfallSnow)*data_step + (scalarRainfall - averageThroughfallRain)*data_step & - averageCanopySnowUnloading*data_step - averageCanopyLiqDrainage*data_step + averageCanopySublimation*data_step + averageCanopyEvaporation*data_step) - if(abs(scalarCanopyWatBalError) > absConvTol_liquid*iden_water*10._rkind)then + if(abs(scalarCanopyWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then print*, '** canopy water balance error:' write(*,'(a,1x,f20.10)') 'data_step = ', data_step write(*,'(a,1x,f20.10)') 'balanceCanopyWater0 = ', balanceCanopyWater0 @@ -1070,7 +1079,7 @@ subroutine coupled_em(& newSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) delSWE = newSWE - (oldSWE - sfcMeltPond) massBalance = delSWE - (effSnowfall + effRainfall + averageSnowSublimation - averageSnowDrainage*iden_water)*data_step - if(abs(massBalance) > absConvTol_liquid*iden_water*10._rkind)then + if(abs(massBalance) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then print*, 'nSnow = ', nSnow print*, 'nSub = ', nSub write(*,'(a,1x,f20.10)') 'data_step = ', data_step @@ -1124,7 +1133,7 @@ subroutine coupled_em(& ! check the soil water balance scalarSoilWatBalError = balanceSoilWater1 - (balanceSoilWater0 + (balanceSoilInflux + balanceSoilET - balanceSoilBaseflow - balanceSoilDrainage - totalSoilCompress) ) - if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues + if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues write(*,*) 'solution method = ', ixSolution write(*,'(a,1x,f20.10)') 'data_step = ', data_step write(*,'(a,1x,f20.10)') 'totalSoilCompress = ', totalSoilCompress diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index ae8ee86a6..fa44b03ab 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -105,7 +105,10 @@ module opSplittin_module USE mDecisions_module,only: & qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization bigBucket, & ! a big bucket (lumped aquifer model) - noExplicit, & ! no explicit groundwater parameterization + noExplicit ! no explicit groundwater parameterization + +! look-up values for the numerical method +USE mDecisions_module,only: & sundials, & ! SUNDIALS/IDA solution bEuler ! home-grown backward Euler solution with long time step diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 280bdb630..ea4787bce 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -619,7 +619,7 @@ subroutine setSolverParams(dt,ida_mem,retval) !======= Internals ============ real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 - integer,parameter :: max_order = 1 ! maximum BDF order, default = 5 + integer,parameter :: max_order = 5 ! maximum BDF order, default = 5 integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index 5a98150c4..5d6575eeb 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -394,8 +394,8 @@ subroutine varSubstepSundials(& endif ! identify the need to check the mass balance - checkMassBalance = .true. ! (.not.scalarSolution) - checkNrgBalance = .true. + checkMassBalance = .false. !do not check for Sundials + checkNrgBalance = .false. !do not check for Sundials ! update prognostic variables call updateProgSundials(dt_out,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control @@ -883,15 +883,15 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial if(abs(liqError) > absConvTol_liquid*10._rkind*iden_water)then ! *10 because of precision issues - write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 - write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 - write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt - write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt - write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt - write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - write(*,'(a,1x,f20.10)') 'liqError = ', liqError + !write(*,'(a,1x,f20.10)') 'dt = ', dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + !write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 + !write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 + !write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt + !write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt + !write(*,'(a,1x,f20.10)') 'liqError = ', liqError waterBalanceError = .true. return endif ! if there is a water balance error @@ -907,15 +907,15 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues - write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 - write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 - write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux - write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink - write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink - write(*,'(a,1x,f20.10)') 'compSink = ', compSink - write(*,'(a,1x,f20.10)') 'liqError = ', liqError - write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid + !write(*,'(a,1x,f20.10)') 'dt = ', dt + !write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 + !write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 + !write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux + !write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink + !write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink + !write(*,'(a,1x,f20.10)') 'compSink = ', compSink + !write(*,'(a,1x,f20.10)') 'liqError = ', liqError + !write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid waterBalanceError = .true. return endif ! if there is a water balance error From 0cf9f33cc32e070eef30f71beb9c6e366c9995b0 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 17 Oct 2022 16:54:21 +0900 Subject: [PATCH 0402/1472] update Sundials to 6.3.0 --- build/build_cmakeSundials_cop | 2 +- build/build_cmakeSundials_gra | 2 +- build/build_cmakeSundials_mac | 2 +- build/build_cmakeSundials_ric | 2 +- sundials/installation.txt | 8 ++++---- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build/build_cmakeSundials_cop b/build/build_cmakeSundials_cop index 220b75d84..fccfbfb26 100755 --- a/build/build_cmakeSundials_cop +++ b/build/build_cmakeSundials_cop @@ -3,6 +3,6 @@ # run script from the builddir directory with ./build_cmake # Note, using -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF because modified constraint code and did not modify examples -cmake ../../sundials-5.8.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir/examples +cmake ../../sundials-6.3.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir/examples diff --git a/build/build_cmakeSundials_gra b/build/build_cmakeSundials_gra index f43375c6a..af7e35adb 100755 --- a/build/build_cmakeSundials_gra +++ b/build/build_cmakeSundials_gra @@ -3,6 +3,6 @@ # run script from the builddir directory with ./build_cmake # Note, using -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF because modified constraint code and did not modify examples -cmake ../../sundials-5.8.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/home/avanb/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/home/avanb/SummaSundials/sundials/instdir/examples +cmake ../../sundials-6.3.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/home/avanb/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/home/avanb/SummaSundials/sundials/instdir/examples diff --git a/build/build_cmakeSundials_mac b/build/build_cmakeSundials_mac index 9a80200f0..83c6fcf75 100755 --- a/build/build_cmakeSundials_mac +++ b/build/build_cmakeSundials_mac @@ -3,6 +3,6 @@ # run script from the builddir directory with ./build_cmake # Note, using -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF because modified constraint code and did not modify examples -cmake ../../sundials-5.8.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/Users/amedin/Research/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/Users/amedin/Research/SummaSundials/sundials/instdir/examples +cmake ../../sundials-6.3.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/Users/amedin/Research/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/Users/amedin/Research/SummaSundials/sundials/instdir/examples diff --git a/build/build_cmakeSundials_ric b/build/build_cmakeSundials_ric index eabdaeeb5..a563ce505 100755 --- a/build/build_cmakeSundials_ric +++ b/build/build_cmakeSundials_ric @@ -3,6 +3,6 @@ # run script from the builddir directory with ./build_cmake # Note, using -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF because modified constraint code and did not modify examples -cmake ../../sundials-5.8.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/u1/gwu479/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/u1/gwu479/SummaSundials/sundials/instdir/examples +cmake ../../sundials-6.3.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/u1/gwu479/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/u1/gwu479/SummaSundials/sundials/instdir/examples diff --git a/sundials/installation.txt b/sundials/installation.txt index b22fcd546..2bfb04fae 100644 --- a/sundials/installation.txt +++ b/sundials/installation.txt @@ -1,12 +1,12 @@ 1. Download the latest release of IDA solver from SUNDIALS package in https://computing.llnl.gov/projects/sundials/sundials-software -% wget "https://github.com/LLNL/sundials/releases/download/v5.8.0/sundials-5.8.0.tar.gz" +% wget "https://github.com/LLNL/sundials/releases/download/v6.3.0/sundials-6.3.0.tar.gz" 2. Extract the corresponding compressed file -% tar -xzf sundials-5.8.0.tar.gz +% tar -xzf sundials-6.3.0.tar.gz 3. Read INSTALL_GUIDE.pdf and follow the installation instructions. Roughly, do the following: -Make a directory outside the sundials-5.8.0 folder and enter it, and make the install and build dirs, enter the build dir. +Make a directory outside the sundials-6.3.0 folder and enter it, and make the install and build dirs, enter the build dir. % mkdir sundials % cd sundials % mkdir instdir @@ -45,7 +45,7 @@ export LD_LIBRARY_PATH NOTE. If you'd like to print the Jacobians going into the Sundials IDA solver, you need to do the following -1. to file sundials-5.8.0/src/ida/ida_ls.c around line 1345, should be after: +1. to file sundials-6.3.0/src/ida/ida_ls.c around line 1345, should be after: /* Call Jacobian routine */ retval = idals_mem->jac(IDA_mem->ida_tn, IDA_mem->ida_cj, y, From 0392f6eac1885689dfca16d324a47e50012ec164 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 17 Oct 2022 16:57:29 +0900 Subject: [PATCH 0403/1472] clarify solver parameters between bEuler and sundials, maybe use the same rel*Tol and abs*Tol parameters in each? --- build/source/dshare/get_ixname.f90 | 24 ++++++------- build/source/dshare/popMetadat.f90 | 24 ++++++------- build/source/engine/summaSolveSundialsIDA.f90 | 11 +++--- build/source/engine/tol4IDA.f90 | 36 +++++++++---------- 4 files changed, 48 insertions(+), 47 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index e4fe3c7aa..704318558 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -377,18 +377,18 @@ function get_ixparam(varName) case('soilIceCV' ); get_ixparam = iLookPARAM%soilIceCV ! CV of depth of soil ice, used to get frozen fraction (-) ! algorithmic control parameters case('minwind' ); get_ixparam = iLookPARAM%minwind ! minimum wind speed (m s-1) - case('minstep' ); get_ixparam = iLookPARAM%minstep ! minimum length of the time step - case('maxstep' ); get_ixparam = iLookPARAM%maxstep ! maximum length of the time step - case('wimplicit' ); get_ixparam = iLookPARAM%wimplicit ! weight assigned to start-of-step fluxes - case('maxiter' ); get_ixparam = iLookPARAM%maxiter ! maximum number of iterations - case('relConvTol_liquid' ); get_ixparam = iLookPARAM%relConvTol_liquid ! relative convergence tolerance for vol frac liq water (-) - case('absConvTol_liquid' ); get_ixparam = iLookPARAM%absConvTol_liquid ! absolute convergence tolerance for vol frac liq water (-) - case('relConvTol_matric' ); get_ixparam = iLookPARAM%relConvTol_matric ! relative convergence tolerance for matric head (-) - case('absConvTol_matric' ); get_ixparam = iLookPARAM%absConvTol_matric ! absolute convergence tolerance for matric head (m) - case('relConvTol_energy' ); get_ixparam = iLookPARAM%relConvTol_energy ! relative convergence tolerance for energy (-) - case('absConvTol_energy' ); get_ixparam = iLookPARAM%absConvTol_energy ! absolute convergence tolerance for energy (J m-3) - case('relConvTol_aquifr' ); get_ixparam = iLookPARAM%relConvTol_aquifr ! relative convergence tolerance for aquifer storage (-) - case('absConvTol_aquifr' ); get_ixparam = iLookPARAM%absConvTol_aquifr ! absolute convergence tolerance for aquifer storage (m) + case('minstep' ); get_ixparam = iLookPARAM%minstep ! minimum length of the time step bEuler, not currently used + case('maxstep' ); get_ixparam = iLookPARAM%maxstep ! maximum length of the time step bEuler + case('wimplicit' ); get_ixparam = iLookPARAM%wimplicit ! weight assigned to start-of-step fluxes bEuler, not currently used + case('maxiter' ); get_ixparam = iLookPARAM%maxiter ! maximum number of iterations bEuler or nonlinear iterations Sundials + case('relConvTol_liquid' ); get_ixparam = iLookPARAM%relConvTol_liquid ! relative convergence tolerance for vol frac liq water (-) bEuler + case('absConvTol_liquid' ); get_ixparam = iLookPARAM%absConvTol_liquid ! absolute convergence tolerance for vol frac liq water (-) bEuler + case('relConvTol_matric' ); get_ixparam = iLookPARAM%relConvTol_matric ! relative convergence tolerance for matric head (-) bEuler + case('absConvTol_matric' ); get_ixparam = iLookPARAM%absConvTol_matric ! absolute convergence tolerance for matric head (m) bEuler + case('relConvTol_energy' ); get_ixparam = iLookPARAM%relConvTol_energy ! relative convergence tolerance for energy (-) bEuler + case('absConvTol_energy' ); get_ixparam = iLookPARAM%absConvTol_energy ! absolute convergence tolerance for energy (J m-3) bEuler + case('relConvTol_aquifr' ); get_ixparam = iLookPARAM%relConvTol_aquifr ! relative convergence tolerance for aquifer storage (-) bEuler + case('absConvTol_aquifr' ); get_ixparam = iLookPARAM%absConvTol_aquifr ! absolute convergence tolerance for aquifer storage (m) bEuler case('zmin' ); get_ixparam = iLookPARAM%zmin ! minimum layer depth (m) case('zmax' ); get_ixparam = iLookPARAM%zmax ! maximum layer depth (m) case('zminLayer1' ); get_ixparam = iLookPARAM%zminLayer1 ! minimum layer depth for the 1st (top) layer (m) diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 6dc139653..309887407 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -259,18 +259,18 @@ subroutine popMetadat(err,message) mpar_meta(iLookPARAM%soilIceCV) = var_info('soilIceCV' , 'CV of depth of soil ice, used to get frozen fraction' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! algorithmic control parameters mpar_meta(iLookPARAM%minwind) = var_info('minwind' , 'minimum wind speed' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%minstep) = var_info('minstep' , 'minimum length of the time step' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%maxstep) = var_info('maxstep' , 'maximum length of the time step' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%wimplicit) = var_info('wimplicit' , 'weight assigned to the start-of-step fluxes (alpha)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%maxiter) = var_info('maxiter' , 'maximum number of iterations' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_liquid) = var_info('relConvTol_liquid' , 'relative convergence tolerance for vol frac liq water' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_liquid) = var_info('absConvTol_liquid' , 'absolute convergence tolerance for vol frac liq water' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_matric) = var_info('relConvTol_matric' , 'relative convergence tolerance for matric head' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_matric) = var_info('absConvTol_matric' , 'absolute convergence tolerance for matric head' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_energy) = var_info('relConvTol_energy' , 'relative convergence tolerance for energy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_energy) = var_info('absConvTol_energy' , 'absolute convergence tolerance for energy' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_aquifr) = var_info('relConvTol_aquifr' , 'relative convergence tolerance for aquifer storage' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_aquifr) = var_info('absConvTol_aquifr' , 'absolute convergence tolerance for aquifer storage' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%minstep) = var_info('minstep' , 'minimum length of the time step bEuler, not currently used' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%maxstep) = var_info('maxstep' , 'maximum length of the time step bEuler' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%wimplicit) = var_info('wimplicit' , 'weight assigned to the start-of-step fluxes ,bEuler, not currently used', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%maxiter) = var_info('maxiter' , 'maximum number of iterations bEuler or nonlinear iterations Sundials', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_liquid) = var_info('relConvTol_liquid' , 'relative convergence tolerance for vol frac liq water bEuler' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_liquid) = var_info('absConvTol_liquid' , 'absolute convergence tolerance for vol frac liq water bEuler' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_matric) = var_info('relConvTol_matric' , 'relative convergence tolerance for matric head bEuler' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_matric) = var_info('absConvTol_matric' , 'absolute convergence tolerance for matric head bEuler' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_energy) = var_info('relConvTol_energy' , 'relative convergence tolerance for energy bEuler' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_energy) = var_info('absConvTol_energy' , 'absolute convergence tolerance for energy bEuler' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_aquifr) = var_info('relConvTol_aquifr' , 'relative convergence tolerance for aquifer storage bEuler' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_aquifr) = var_info('absConvTol_aquifr' , 'absolute convergence tolerance for aquifer storage bEuler' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%zmin) = var_info('zmin' , 'minimum layer depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%zmax) = var_info('zmax' , 'maximum layer depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%zminLayer1) = var_info('zminLayer1' , 'minimum layer depth for the 1st (top) layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index ea4787bce..8283d09a3 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -382,7 +382,7 @@ subroutine summaSolveSundialsIDA( & if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetStopTime'; return; endif ! Set solver parameters such as maximum order, number of iterations, ... - call setSolverParams(dt, ida_mem, retval) + call setSolverParams(dt, nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)), ida_mem, retval) if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in setSolverParams'; return; endif ! Disable error messages and warnings @@ -603,7 +603,7 @@ end subroutine setInitialCondition ! ---------------------------------------------------------------- ! setSolverParams: private routine to set parameters in ida solver ! ---------------------------------------------------------------- -subroutine setSolverParams(dt,ida_mem,retval) +subroutine setSolverParams(dt,nonlin_iter,ida_mem,retval) !======= Inclusions =========== USE, intrinsic :: iso_c_binding @@ -614,18 +614,19 @@ subroutine setSolverParams(dt,ida_mem,retval) ! calling variables real(rkind),intent(in) :: dt ! time step + integer,intent(in) :: nonlin_iter ! maximum number of nonlinear iterations, default = 4, set in parameters type(c_ptr),intent(inout) :: ida_mem ! IDA memory integer(i4b),intent(out) :: retval ! return value + !======= Internals ============ real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 integer,parameter :: max_order = 5 ! maximum BDF order, default = 5 - integer,parameter :: nonlin_iter = 100 ! maximun number of nonliear iterations, default = 4 integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 - integer(kind = 8),parameter :: max_step = 999999 ! maximum number of steps, dafault = 500 + integer(kind = 8),parameter :: max_step = 999999 ! maximum number of steps, default = 500 real(qp),parameter :: h_init = 0 ! initial stepsize - real(qp) :: h_max ! maximum stepsize, dafault = infinity + real(qp) :: h_max ! maximum stepsize, default = infinity ! Set the maximum BDF order retval = FIDASetMaxOrd(ida_mem, max_order) diff --git a/build/source/engine/tol4IDA.f90 b/build/source/engine/tol4IDA.f90 index 392557256..7709e7a3e 100644 --- a/build/source/engine/tol4IDA.f90 +++ b/build/source/engine/tol4IDA.f90 @@ -151,24 +151,24 @@ subroutine popTol4IDA(& ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- ! state subsets - integer(i4b) :: iState ! index of state within the snow+soil domain - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - integer(i4b) :: ixStateSubset ! index within the state subset - logical(lgt),dimension(nState) :: tolFlag ! flag to denote that the state is populated - real(rkind) :: absTolTempCas = 1e-6 - real(rkind) :: relTolTempCas = 1e-6 - real(rkind) :: absTolTempVeg = 1e-6 - real(rkind) :: relTolTempVeg = 1e-6 - real(rkind) :: absTolWatVeg = 1e-6 - real(rkind) :: relTolWatVeg = 1e-6 - real(rkind) :: absTolTempSoilSnow = 1e-6 - real(rkind) :: relTolTempSoilSnow = 1e-6 - real(rkind) :: absTolWatSnow = 1e-6 - real(rkind) :: relTolWatSnow = 1e-6 - real(rkind) :: absTolMatric = 1e-6 - real(rkind) :: relTolMatric = 1e-6 - real(rkind) :: absTolAquifr = 1e-6 - real(rkind) :: relTolAquifr = 1e-6 + integer(i4b) :: iState ! index of state within the snow+soil domain + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + integer(i4b) :: ixStateSubset ! index within the state subset + logical(lgt),dimension(nState) :: tolFlag ! flag to denote that the state is populated + real(rkind) :: absTolTempCas = 1e-6 ! could use absConvTol_energy 1e-6 = 1e-0 bEuler + real(rkind) :: relTolTempCas = 1e-6 ! could use relConvTol_energy 1e-6 = 1e-2 bEuler + real(rkind) :: absTolTempVeg = 1e-6 ! could use absConvTol_energy + real(rkind) :: relTolTempVeg = 1e-6 ! could use relConvTol_energy + real(rkind) :: absTolWatVeg = 1e-6 ! could use absConvTol_liquid 1e-6 = 1e-5 bEuler + real(rkind) :: relTolWatVeg = 1e-6 ! could use relConvTol_liquid 1e-6 = 1e-3 bEuler + real(rkind) :: absTolTempSoilSnow = 1e-6 ! could use absConvTol_energy + real(rkind) :: relTolTempSoilSnow = 1e-6 ! could use relConvTol_energy + real(rkind) :: absTolWatSnow = 1e-6 ! could use absConvTol_liquid + real(rkind) :: relTolWatSnow = 1e-6 ! could use relConvTol_liquid + real(rkind) :: absTolMatric = 1e-6 ! could use absConvTol_matric 1e-6 = 1e-6 bEuler + real(rkind) :: relTolMatric = 1e-6 ! could use relConvTol_matric 1e-6 = 1e-6 bEuler + real(rkind) :: absTolAquifr = 1e-6 ! could use absConvTol_aquifr 1e-6 = 1e-5 bEuler + real(rkind) :: relTolAquifr = 1e-6 ! could use relConvTol_aquifr 1e-6 = 1e-0 bEuler ! -------------------------------------------------------------------------------------------------------------------------------- From 3f1ae9daf4f67dddafcd5eb73f0573a96ffcab39 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 17 Oct 2022 17:41:13 +0900 Subject: [PATCH 0404/1472] Update solver to use sundials 6.3.0, needs SUNContext --- build/source/engine/summaSolveSundialsIDA.f90 | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 8283d09a3..e33a3ebe0 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -129,6 +129,7 @@ subroutine summaSolveSundialsIDA( & !======= Inclusions =========== USE fida_mod ! Fortran interface to IDA + USE fsundials_context_mod ! Fortran interface to SUNContext USE fnvector_serial_mod ! Fortran interface to serial N_Vector USE fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix USE fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver @@ -205,6 +206,7 @@ subroutine summaSolveSundialsIDA( & type(SUNLinearSolver), pointer :: sunlinsol_LS ! sundials linear solver type(SUNNonLinearSolver), pointer :: sunnonlin_NLS ! sundials nonlinear solver type(c_ptr) :: ida_mem ! IDA memory + type(c_ptr) :: sunctx ! SUNDIALS simulation context type(eqnsData), target :: eqns_data ! IDA type integer(i4b) :: retval, retvalr ! return value logical(lgt) :: feasible ! feasibility flag @@ -305,19 +307,20 @@ subroutine summaSolveSundialsIDA( & allocate( eqns_data%resSink(nState) ) startQuadrature = .true. + retval = FSUNContext_Create(c_null_ptr, sunctx) ! create serial vectors - sunvec_y => FN_VMake_Serial(nState, stateVec) + sunvec_y => FN_VMake_Serial(nState, stateVec, sunctx) if (.not. associated(sunvec_y)) then; err=20; message='summaSolveSundialsIDA: sunvec = NULL'; return; endif - sunvec_yp => FN_VMake_Serial(nState, stateVecPrime) + sunvec_yp => FN_VMake_Serial(nState, stateVecPrime, sunctx) if (.not. associated(sunvec_yp)) then; err=20; message='summaSolveSundialsIDA: sunvec = NULL'; return; endif ! Initialize solution vectors call setInitialCondition(nState, stateVecInit, sunvec_y, sunvec_yp) ! Create memory - ida_mem = FIDACreate() + ida_mem = FIDACreate(sunctx) if (.not. c_associated(ida_mem)) then; err=20; message='summaSolveSundialsIDA: ida_mem = NULL'; return; endif ! Attach user data to memory @@ -339,20 +342,20 @@ subroutine summaSolveSundialsIDA( & case(ixBandMatrix) mu = ku; lu = kl; ! Create banded SUNMatrix for use in linear solves - sunmat_A => FSUNBandMatrix(nState, mu, lu) + sunmat_A => FSUNBandMatrix(nState, mu, lu, sunctx) if (.not. associated(sunmat_A)) then; err=20; message='summaSolveSundialsIDA: sunmat = NULL'; return; endif ! Create banded SUNLinearSolver object - sunlinsol_LS => FSUNLinSol_Band(sunvec_y, sunmat_A) + sunlinsol_LS => FSUNLinSol_Band(sunvec_y, sunmat_A, sunctx) if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolveSundialsIDA: sunlinsol = NULL'; return; endif case(ixFullMatrix) ! Create dense SUNMatrix for use in linear solves - sunmat_A => FSUNDenseMatrix(nState, nState) + sunmat_A => FSUNDenseMatrix(nState, nState, sunctx) if (.not. associated(sunmat_A)) then; err=20; message='summaSolveSundialsIDA: sunmat = NULL'; return; endif ! Create dense SUNLinearSolver object - sunlinsol_LS => FSUNDenseLinearSolver(sunvec_y, sunmat_A) + sunlinsol_LS => FSUNLinSol_Dense(sunvec_y, sunmat_A, sunctx) if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolveSundialsIDA: sunlinsol = NULL'; return; endif ! check @@ -370,7 +373,7 @@ subroutine summaSolveSundialsIDA( & if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetJacFn'; return; endif ! Create Newton SUNNonlinearSolver object - sunnonlin_NLS => FSUNNonlinSol_Newton(sunvec_y) + sunnonlin_NLS => FSUNNonlinSol_Newton(sunvec_y, sunctx) if (.not. associated(sunnonlin_NLS)) then; err=20; message='summaSolveSundialsIDA: sunnonlinsol = NULL'; return; endif ! Attach the nonlinear solver @@ -565,6 +568,7 @@ subroutine summaSolveSundialsIDA( & call FSUNMatDestroy(sunmat_A) call FN_VDestroy(sunvec_y) call FN_VDestroy(sunvec_yp) + retval = FSUNContext_Free(sunctx) end subroutine summaSolveSundialsIDA From abbdf66fa5b327ea8be6fce9ff9efd834f801f10 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 20 Oct 2022 12:52:54 +0900 Subject: [PATCH 0405/1472] Not checking for ixSoilOnlyNrg and ixSoilOnlyHyd which was causing a seg fault when splitting. --- build/source/engine/computJacob.f90 | 8 ++++---- build/source/engine/computJacobSundials.f90 | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index c2557abca..947907ad1 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -528,8 +528,8 @@ subroutine computJacob(& ! ---------------------------------------- if(ixAqWat/=integerMissing) then aJac(ixDiag,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) - aJac(ixOffDiag(ixAqWat,ixSoilOnlyNrg(nSoil)),ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk - aJac(ixOffDiag(ixAqWat,ixSoilOnlyHyd(nSoil)),ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat + if(ixSoilOnlyNrg(nSoil)/=integerMissing) aJac(ixOffDiag(ixAqWat,ixSoilOnlyNrg(nSoil)),ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk + if(ixSoilOnlyHyd(nSoil)/=integerMissing) aJac(ixOffDiag(ixAqWat,ixSoilOnlyHyd(nSoil)),ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat ! - only include banded derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration), would have to have few soil layers if(computeVegFlux)then if(ixAqWat-ixCasNrg <= kl) aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) @@ -866,8 +866,8 @@ subroutine computJacob(& ! ---------------------------------------- if(ixAqWat/=integerMissing) then aJac(ixAqWat,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) - aJac(ixAqWat,ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk - aJac(ixAqWat,ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat + if(ixSoilOnlyNrg(nSoil)/=integerMissing) aJac(ixAqWat,ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk + if(ixSoilOnlyHyd(nSoil)/=integerMissing) aJac(ixAqWat,ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) if(computeVegFlux)then aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) diff --git a/build/source/engine/computJacobSundials.f90 b/build/source/engine/computJacobSundials.f90 index c64d0df0c..d73705fba 100644 --- a/build/source/engine/computJacobSundials.f90 +++ b/build/source/engine/computJacobSundials.f90 @@ -599,8 +599,8 @@ subroutine computJacobSundials(& ! ---------------------------------------- if(ixAqWat/=integerMissing) then aJac(ixDiag,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) * cj - aJac(ixOffDiag(ixAqWat,ixSoilOnlyNrg(nSoil)),ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt - aJac(ixOffDiag(ixAqWat,ixSoilOnlyHyd(nSoil)),ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt + if(ixSoilOnlyNrg(nSoil)/=integerMissing) aJac(ixOffDiag(ixAqWat,ixSoilOnlyNrg(nSoil)),ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt + if(ixSoilOnlyHyd(nSoil)/=integerMissing) aJac(ixOffDiag(ixAqWat,ixSoilOnlyHyd(nSoil)),ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! - only include banded derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration), would have to have few soil layers if(computeVegFlux)then if(ixAqWat-ixCasNrg <= kl) aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) @@ -940,8 +940,8 @@ subroutine computJacobSundials(& ! ---------------------------------------- if(ixAqWat/=integerMissing) then aJac(ixAqWat,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) * cj - aJac(ixAqWat,ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk - aJac(ixAqWat,ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat + if(ixSoilOnlyNrg(nSoil)/=integerMissing) aJac(ixAqWat,ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk + if(ixSoilOnlyHyd(nSoil)/=integerMissing) aJac(ixAqWat,ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) if(computeVegFlux)then aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) From eaa68bb73a503c737a666e79bad7c20a7c5e4c80 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 20 Oct 2022 13:00:05 +0900 Subject: [PATCH 0406/1472] wasn't checking for veg indices every time, and also banded aquifer did not have ixOffDiag indices for soil transpiration (likely didn't affect anything since not usually big enough kl to be used) --- build/source/engine/computJacob.f90 | 64 ++++++++++++-------- build/source/engine/computJacobSundials.f90 | 66 +++++++++++++-------- 2 files changed, 81 insertions(+), 49 deletions(-) diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 947907ad1..d2f6c4bae 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -532,10 +532,18 @@ subroutine computJacob(& if(ixSoilOnlyHyd(nSoil)/=integerMissing) aJac(ixOffDiag(ixAqWat,ixSoilOnlyHyd(nSoil)),ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat ! - only include banded derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration), would have to have few soil layers if(computeVegFlux)then - if(ixAqWat-ixCasNrg <= kl) aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) - if(ixAqWat-ixVegNrg <= kl) aJac(ixAqWat,ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) - if(ixAqWat-ixVegHyd <= kl) aJac(ixAqWat,ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 - if(ixAqWat-ixTopNrg <= kl) aJac(ixAqWat,ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) + if(ixCasNrg/=integerMissing)then + if(ixAqWat-ixCasNrg <= kl) aJac(ixOffDiag(ixAqWat,ixCasNrg),ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) + endif + if(ixVegNrg/=integerMissing)then + if(ixAqWat-ixVegNrg <= kl) aJac(ixOffDiag(ixAqWat,ixVegNrg),ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) + endif + if(ixVegHyd/=integerMissing)then + if(ixAqWat-ixVegHyd <= kl) aJac(ixOffDiag(ixAqWat,ixVegHyd),ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 + endif + if(ixTopNrg/=integerMissing)then + if(ixAqWat-ixTopNrg <= kl) aJac(ixOffDiag(ixAqWat,ixTopNrg),ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) + endif endif endif @@ -576,19 +584,27 @@ subroutine computJacob(& ! - include derivatives w.r.t. ground evaporation if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer, assume here that kl>=4 if(computeVegFlux)then - aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) - aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) - aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 + if(ixCasNrg/=integerMissing) aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) + if(ixVegHyd/=integerMissing) aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 endif - aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) + if(ixTopNrg/=integerMissing) aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) endif ! - only include banded terms for derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) in banded structure if(computeVegFlux)then - if(watState-ixCasNrg <= kl) aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanair(iLayer)) + aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) ! dVol/dT (K-1) - if(watState-ixVegNrg <= kl) aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanopy(iLayer)) + aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) ! dVol/dT (K-1) - if(watState-ixVegHyd <= kl) aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dCanWat(iLayer)) + aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) ! dVol/dLiq (kg m-2)-1 - if(watState-ixTopNrg <= kl) aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTGround(iLayer)) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) + if(ixCasNrg/=integerMissing)then + if(watState-ixCasNrg <= kl) aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanair(iLayer)) + aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) ! dVol/dT (K-1) + endif + if(ixVegNrg/=integerMissing)then + if(watState-ixVegNrg <= kl) aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanopy(iLayer)) + aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) ! dVol/dT (K-1) + endif + if(ixVegHyd/=integerMissing)then + if(watState-ixVegHyd <= kl) aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dCanWat(iLayer)) + aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) ! dVol/dLiq (kg m-2)-1 + endif + if(ixTopNrg/=integerMissing)then + if(watState-ixTopNrg <= kl) aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTGround(iLayer)) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) + endif endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer @@ -870,10 +886,10 @@ subroutine computJacob(& if(ixSoilOnlyHyd(nSoil)/=integerMissing) aJac(ixAqWat,ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) if(computeVegFlux)then - aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) - aJac(ixAqWat,ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) - aJac(ixAqWat,ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 - aJac(ixAqWat,ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) + if(ixCasNrg/=integerMissing) aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) + if(ixVegNrg/=integerMissing) aJac(ixAqWat,ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) + if(ixVegHyd/=integerMissing) aJac(ixAqWat,ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 + if(ixTopNrg/=integerMissing) aJac(ixAqWat,ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) endif endif @@ -914,19 +930,19 @@ subroutine computJacob(& ! - include derivatives of energy w.r.t. ground evaporation if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer if(computeVegFlux)then - aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) - aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) - aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 + if(ixCasNrg/=integerMissing) aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) + if(ixVegNrg/=integerMissing) aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) + if(ixVegHyd/=integerMissing) aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 endif - aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) + if(ixTopNrg/=integerMissing) aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) endif ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) if(computeVegFlux)then - aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanair(iLayer)) + aJac(watState,ixCasNrg) ! dVol/dT (K-1) - aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanopy(iLayer)) + aJac(watState,ixVegNrg) ! dVol/dT (K-1) - aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dCanWat(iLayer)) + aJac(watState,ixVegHyd) ! dVol/dLiq (kg m-2)-1 - aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTGround(iLayer)) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) + if(ixCasNrg/=integerMissing) aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanair(iLayer)) + aJac(watState,ixCasNrg) ! dVol/dT (K-1) + if(ixVegNrg/=integerMissing) aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanopy(iLayer)) + aJac(watState,ixVegNrg) ! dVol/dT (K-1) + if(ixVegHyd/=integerMissing) aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dCanWat(iLayer)) + aJac(watState,ixVegHyd) ! dVol/dLiq (kg m-2)-1 + if(ixTopNrg/=integerMissing) aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTGround(iLayer)) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer diff --git a/build/source/engine/computJacobSundials.f90 b/build/source/engine/computJacobSundials.f90 index d73705fba..0ff22bd3c 100644 --- a/build/source/engine/computJacobSundials.f90 +++ b/build/source/engine/computJacobSundials.f90 @@ -603,10 +603,18 @@ subroutine computJacobSundials(& if(ixSoilOnlyHyd(nSoil)/=integerMissing) aJac(ixOffDiag(ixAqWat,ixSoilOnlyHyd(nSoil)),ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! - only include banded derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration), would have to have few soil layers if(computeVegFlux)then - if(ixAqWat-ixCasNrg <= kl) aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) - if(ixAqWat-ixVegNrg <= kl) aJac(ixAqWat,ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) - if(ixAqWat-ixVegHyd <= kl) aJac(ixAqWat,ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 - if(ixAqWat-ixTopNrg <= kl) aJac(ixAqWat,ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) + if(ixCasNrg/=integerMissing)then + if(ixAqWat-ixCasNrg <= kl) aJac(ixOffDiag(ixAqWat,ixCasNrg),ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) + endif + if(ixVegNrg/=integerMissing)then + if(ixAqWat-ixVegNrg <= kl) aJac(ixOffDiag(ixAqWat,ixVegNrg),ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) + endif + if(ixVegHyd/=integerMissing)then + if(ixAqWat-ixVegHyd <= kl) aJac(ixOffDiag(ixAqWat,ixVegHyd),ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 + endif + if(ixTopNrg/=integerMissing)then + if(ixAqWat-ixTopNrg <= kl) aJac(ixOffDiag(ixAqWat,ixTopNrg),ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) + endif endif endif @@ -644,22 +652,30 @@ subroutine computJacobSundials(& if(ixSoilOnlyHyd(iLayer+1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer+1),nrgState),nrgState) = (dt/mLayerDepth(jLayer+1))*(-dq_dNrgStateAbove(iLayer)) ! K-1 endif - ! - include derivatives of energy w.r.t. ground evaporation + ! - include derivatives w.r.t. ground evaporation if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer, assume here that kl>=4 if(computeVegFlux)then - aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) - aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) - aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 + if(ixCasNrg/=integerMissing) aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) + if(ixVegHyd/=integerMissing) aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 endif - aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) + if(ixTopNrg/=integerMissing) aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) endif ! - only include banded terms for derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) in banded structure if(computeVegFlux)then - if(watState-ixCasNrg <= kl) aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanair(iLayer)) + aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) ! dVol/dT (K-1) - if(watState-ixVegNrg <= kl) aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanopy(iLayer)) + aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) ! dVol/dT (K-1) - if(watState-ixVegHyd <= kl) aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dCanWat(iLayer)) + aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) ! dVol/dLiq (kg m-2)-1 - if(watState-ixTopNrg <= kl) aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTGround(iLayer)) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) + if(ixCasNrg/=integerMissing)then + if(watState-ixCasNrg <= kl) aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanair(iLayer)) + aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) ! dVol/dT (K-1) + endif + if(ixVegNrg/=integerMissing)then + if(watState-ixVegNrg <= kl) aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanopy(iLayer)) + aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) ! dVol/dT (K-1) + endif + if(ixVegHyd/=integerMissing)then + if(watState-ixVegHyd <= kl) aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dCanWat(iLayer)) + aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) ! dVol/dLiq (kg m-2)-1 + endif + if(ixTopNrg/=integerMissing)then + if(watState-ixTopNrg <= kl) aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTGround(iLayer)) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) + endif endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer @@ -944,10 +960,10 @@ subroutine computJacobSundials(& if(ixSoilOnlyHyd(nSoil)/=integerMissing) aJac(ixAqWat,ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) if(computeVegFlux)then - aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) - aJac(ixAqWat,ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) - aJac(ixAqWat,ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 - aJac(ixAqWat,ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) + if(ixCasNrg/=integerMissing) aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) + if(ixVegNrg/=integerMissing) aJac(ixAqWat,ixVegNrg) = -dAquiferTrans_dTCanopy*dt ! dVol/dT (K-1) + if(ixVegHyd/=integerMissing) aJac(ixAqWat,ixVegHyd) = -dAquiferTrans_dCanWat*dt ! dVol/dLiq (kg m-2)-1 + if(ixTopNrg/=integerMissing) aJac(ixAqWat,ixTopNrg) = -dAquiferTrans_dTGround*dt ! dVol/dT (K-1) endif endif @@ -988,19 +1004,19 @@ subroutine computJacobSundials(& ! - include derivatives of energy w.r.t. ground evaporation if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer if(computeVegFlux)then - aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) - aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) - aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 + if(ixCasNrg/=integerMissing) aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) + if(ixVegNrg/=integerMissing) aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) + if(ixVegHyd/=integerMissing) aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 endif - aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) + if(ixTopNrg/=integerMissing) aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) endif ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) if(computeVegFlux)then - aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanair(iLayer)) + aJac(watState,ixCasNrg) ! dVol/dT (K-1) - aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanopy(iLayer)) + aJac(watState,ixVegNrg) ! dVol/dT (K-1) - aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dCanWat(iLayer)) + aJac(watState,ixVegHyd) ! dVol/dLiq (kg m-2)-1 - aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTGround(iLayer)) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) + if(ixCasNrg/=integerMissing) aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanair(iLayer)) + aJac(watState,ixCasNrg) ! dVol/dT (K-1) + if(ixVegNrg/=integerMissing) aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTCanopy(iLayer)) + aJac(watState,ixVegNrg) ! dVol/dT (K-1) + if(ixVegHyd/=integerMissing) aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dCanWat(iLayer)) + aJac(watState,ixVegHyd) ! dVol/dLiq (kg m-2)-1 + if(ixTopNrg/=integerMissing) aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-mLayerdTrans_dTGround(iLayer)) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer From dcc9c1b8ee5e3f3ddcd36f37d4edd7759334c4e0 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 21 Oct 2022 09:13:53 +0900 Subject: [PATCH 0407/1472] Comment clarification on howHeatCap decision --- build/source/engine/eval8summaSundials.f90 | 2 +- build/source/engine/mDecisions.f90 | 2 +- build/source/engine/systemSolvSundials.f90 | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index 08e9dadd4..af1be066d 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -582,7 +582,7 @@ subroutine eval8summaSundials(& mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur end do endif ! if dt_cur is not too samll - else ! if using closed formula of heat capacity + else ! if using closed formula of heat capacity, model_decisions(iLookDECISIONS%howHeatCap)%iDecision == closedForm call computHeatCapAnalytic(& ! input: control variables computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index 72258c04f..dbd97e4cc 100644 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -401,7 +401,7 @@ subroutine mDecisions(err,message) err=10; message=trim(message)//"unknown numerical method [option="//trim(model_decisions(iLookDECISIONS%num_method)%cDecision)//"]"; return end select - ! how to compute heat capacity in energy equation + ! how to compute heat capacity in energy equation, only has an effect if num_method==sundials. Choice enthalpyFD has better coincidence of energy conservation with sundials tolerance. select case(trim(model_decisions(iLookDECISIONS%howHeatCap)%cDecision)) case('enthalpyFD'); model_decisions(iLookDECISIONS%howHeatCap)%iDecision = enthalpyFD ! enthalpyFD case('closedForm'); model_decisions(iLookDECISIONS%howHeatCap)%iDecision = closedForm ! closedForm diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index c974e03a1..962e70d2d 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -128,7 +128,6 @@ subroutine systemSolvSundials(& ! structure allocations USE allocspace_module,only:allocLocal ! allocate local data structures ! simulation of fluxes and residuals given a trial state vector - USE eval8summa_module,only:eval8summa ! simulation of fluxes and residuals given a trial state vector USE eval8summaSundials_module,only:eval8summaSundials ! simulation of fluxes and residuals given a trial state vector USE getVectorz_module,only:getScaling ! get the scaling vectors USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy From 230c6a10f5aa33f368c7ac52bccb7906fde40b56 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 25 Oct 2022 20:30:56 +0900 Subject: [PATCH 0408/1472] Turn off domain splitting print --- build/my_makefile_mac | 3 +-- build/source/engine/opSplittin.f90 | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/build/my_makefile_mac b/build/my_makefile_mac index b297da990..8f62bd55c 100644 --- a/build/my_makefile_mac +++ b/build/my_makefile_mac @@ -19,7 +19,7 @@ # * FC - compiler suite # * FC_EXE - compiler executable # * INCLUDES - path to include files -# * ILDFLAGS - path to libraries to include +# * LDFLAGS - path to libraries to include # * LIBRARIES - libraries to include # * DIR_SUNDIALS - installation sundials directory # @@ -387,7 +387,6 @@ compile_noah: $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) # compile common routines - compile_comm: $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index fa44b03ab..a17f2761f 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -616,7 +616,7 @@ subroutine opSplittin(& ! domain splitting else - print*,"split domain" + !print*,"split domain" ! initialize to .false. fluxMask%var(iVar)%dat = .false. From ed48e3ee520042a816613f2ea5f38a477a1ac78d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 25 Oct 2022 21:20:52 +0900 Subject: [PATCH 0409/1472] Doesn't seem to be backtracking, check print --- build/source/engine/summaSolve.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/source/engine/summaSolve.f90 b/build/source/engine/summaSolve.f90 index a8955bb0d..a46e9cef7 100644 --- a/build/source/engine/summaSolve.f90 +++ b/build/source/engine/summaSolve.f90 @@ -421,6 +421,7 @@ subroutine lineSearchRefinement(doLineSearch,stateVecTrial,newtStepScaled,aJacSc ! The internal sub routine has access to all data ! Hence, we only need to include the variables of interest in lineSearch call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) + if (.not.feasible) print*, "backtrack",err,iLine if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! check line search From 0211f629a08e06692b4f3b95ddda1daec5a558ce Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 26 Oct 2022 09:57:01 +0900 Subject: [PATCH 0410/1472] should not return with an error if infeasible, should just return and refine linesearch --- build/source/engine/eval8summa.f90 | 4 +--- sundials/installation.txt | 11 +++++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index ddf3ab697..678f25777 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -352,9 +352,7 @@ subroutine eval8summa(& fluxVec(:) = realMissing resVec(:) = quadMissing fEval = realMissing - message=trim(message)//'non-feasible' - err=20; return - endif + return ! get the start and end indices for the soil compression calculations if(scalarSolution)then diff --git a/sundials/installation.txt b/sundials/installation.txt index 2bfb04fae..89188b73c 100644 --- a/sundials/installation.txt +++ b/sundials/installation.txt @@ -28,17 +28,20 @@ cp ../../summa/build/build_cmakeSundials_* build_cmake % make % make install -7. Enter summa directory and build summa as in /docs/SUMMA_installation.md. You will have to edit the SUMMA Makefile as below additionally (as well as install required libraries and link them as directed in the Makefile). +7. Enter summa directory and build summa as in /docs/SUMMA_installation.md. You will have to edit the SUMMA Makefile as below additionally (as well as install required libraries and link them as directed in the Makefile). You need to clone summa before you can change the makefile + git clone https://git.cs.usask.ca/numerical_simulations_lab/summa.git + 8. In SUMMA Makefile, define the FC_EXE to be the same compiler as you used for sundials and netcdf -9. In SUMMA Makefile, define the SUNDIALS installation directory +9. In SUMMA Makefile, define the SUNDIALS installation directory (and all the folders as required in the SUMMA instructions. DIR_SUNDIALS=$(YOUR_HOME)/sundials/instdir -11. Inside the /summa/build/ directory run +10. Inside the /summa/build/ directory run % make +or % source build_summa_* if you are using a specific * compiler -12. On some installations you may need to add to your .bashrc file (and run source .bashrc) +11. On some installations you may need to add to your .bashrc file (and run source .bashrc) LD_LIBRARY_PATH='$(YOUR_HOME)/sundials/instdir/lib64' export LD_LIBRARY_PATH From c51b91972781208d539f341b71a1da94aca51f78 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 26 Oct 2022 10:01:44 +0900 Subject: [PATCH 0411/1472] Missed if statement --- build/source/engine/eval8summa.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 678f25777..cf075a406 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -352,7 +352,8 @@ subroutine eval8summa(& fluxVec(:) = realMissing resVec(:) = quadMissing fEval = realMissing - return + return + endif ! get the start and end indices for the soil compression calculations if(scalarSolution)then From 6e4cec7cce22d5dff20d442a995779c58606ae0e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 26 Oct 2022 10:48:41 +0900 Subject: [PATCH 0412/1472] Need to back all the way out of lineSearch refinement if infeasible and do not print anything. --- build/source/engine/eval8summa.f90 | 20 ++++++++------------ build/source/engine/summaSolve.f90 | 3 +-- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index cf075a406..0b27577a7 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -286,37 +286,34 @@ subroutine eval8summa(& ! initialize error control err=0; message="eval8summa/" - ! check the feasibility of the solution + ! check the feasibility of the solution always with SUMMA BE + ! NOTE: we will not print infeasibilities since it does not indicate a failure, just a need to backtrack feasible=.true. ! check that the canopy air space temperature is reasonable if(ixCasNrg/=integerMissing)then if(stateVecTrial(ixCasNrg) > canopyTempMax) feasible=.false. - if(stateVecTrial(ixCasNrg) > canopyTempMax) message=trim(message)//'canopy air space temp high,' - if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVecTrial( ixCasNrg )', feasible, canopyTempMax, stateVecTrial(ixCasNrg) + !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVecTrial( ixCasNrg )', feasible, canopyTempMax, stateVecTrial(ixCasNrg) endif ! check that the canopy air space temperature is reasonable if(ixVegNrg/=integerMissing)then if(stateVecTrial(ixVegNrg) > canopyTempMax) feasible=.false. - if(stateVecTrial(ixVegNrg) > canopyTempMax) message=trim(message)//'canopy temp high,' - if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVecTrial( ixVegNrg )', feasible, canopyTempMax, stateVecTrial(ixVegNrg) + !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVecTrial( ixVegNrg )', feasible, canopyTempMax, stateVecTrial(ixVegNrg) endif ! check canopy liquid water is not negative if(ixVegHyd/=integerMissing)then if(stateVecTrial(ixVegHyd) < 0._rkind) feasible=.false. - if(stateVecTrial(ixVegHyd) < 0._rkind) message=trim(message)//'canopy water negative,' - if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVecTrial( ixVegHyd )', feasible, 0._rkind, stateVecTrial(ixVegHyd) + !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVecTrial( ixVegHyd )', feasible, 0._rkind, stateVecTrial(ixVegHyd) end if ! check snow temperature is below freezing if(count(ixSnowOnlyNrg/=integerMissing)>0)then if(any(stateVecTrial( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. - if(any(stateVecTrial( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) message=trim(message)//'snow temp above freezing,' - do iLayer=1,nSnow - if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVecTrial( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVecTrial( ixSnowOnlyNrg(iLayer) ) - enddo + !do iLayer=1,nSnow + ! if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVecTrial( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVecTrial( ixSnowOnlyNrg(iLayer) ) + !enddo endif ! loop through non-missing hydrology state variables in the snow+soil domain @@ -340,7 +337,6 @@ subroutine eval8summa(& ! --> check if(stateVecTrial( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVecTrial( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. - if(stateVecTrial( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVecTrial( ixSnowSoilHyd(iLayer) ) > xMax) message=trim(message)//'layer water outside bounds,' if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax endif ! if water states diff --git a/build/source/engine/summaSolve.f90 b/build/source/engine/summaSolve.f90 index a46e9cef7..5dee64990 100644 --- a/build/source/engine/summaSolve.f90 +++ b/build/source/engine/summaSolve.f90 @@ -421,7 +421,6 @@ subroutine lineSearchRefinement(doLineSearch,stateVecTrial,newtStepScaled,aJacSc ! The internal sub routine has access to all data ! Hence, we only need to include the variables of interest in lineSearch call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) - if (.not.feasible) print*, "backtrack",err,iLine if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! check line search @@ -434,7 +433,7 @@ subroutine lineSearchRefinement(doLineSearch,stateVecTrial,newtStepScaled,aJacSc end if ! check feasibility - if(.not.feasible) cycle + if(.not.feasible) return ! check convergence ! NOTE: some efficiency gains possible by scaling the full newton step outside the line search loop From e2a615d00f1d20413eb21b758a561e5f00678c16 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 26 Oct 2022 12:36:54 +0900 Subject: [PATCH 0413/1472] keep message of infeasible incase fails here --- build/source/engine/eval8summa.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 0b27577a7..473b6c98d 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -348,6 +348,7 @@ subroutine eval8summa(& fluxVec(:) = realMissing resVec(:) = quadMissing fEval = realMissing + message=trim(message)//'non-feasible' return endif From 22f6a89128356e998279f9cfd870fd73708772c0 Mon Sep 17 00:00:00 2001 From: kck540 Date: Wed, 26 Oct 2022 17:17:53 -0400 Subject: [PATCH 0414/1472] added configurable printing --- build/source/driver/summa_driver.f90 | 8 ++++---- build/source/dshare/globalData.f90 | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index ea01242f4..6e84a7733 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -39,6 +39,7 @@ program summa_driver USE summa_util, only: handle_err ! used to process errors ! global data USE globalData, only: numtim ! number of model time steps + USE globalData, only: print_step_freq implicit none ! ***************************************************************************** @@ -53,7 +54,6 @@ program summa_driver ! error control integer(i4b) :: err=0 ! error code character(len=1024) :: message='' ! error message - integer(i4b) :: iStep ! ***************************************************************************** ! * preliminaries @@ -82,7 +82,6 @@ program summa_driver ! ***************************************************************************** ! * model simulation ! ***************************************************************************** - iStep = 1 ! loop through time do modelTimeStep=1,numtim @@ -90,7 +89,9 @@ program summa_driver call summa_readForcing(modelTimeStep, summa1_struc(n), err, message) call handle_err(err, message) - print *, 'step ---> ', iStep + if (mod(modelTimeStep, print_step_freq) == 0)then + print *, 'step ---> ', modelTimeStep + endif ! run the summa physics for one time step call summa_runPhysics(modelTimeStep, summa1_struc(n), err, message) call handle_err(err, message) @@ -98,7 +99,6 @@ program summa_driver ! write the model output call summa_writeOutputFiles(modelTimeStep, summa1_struc(n), err, message) call handle_err(err, message) - iStep = iStep + 1 end do ! looping through time ! successful end diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 index 313583e52..0f50ade64 100644 --- a/build/source/dshare/globalData.f90 +++ b/build/source/dshare/globalData.f90 @@ -345,4 +345,7 @@ MODULE globalData integer(i4b),parameter,public :: nBand=2 ! number of spectral bands integer(i4b),parameter,public :: nTimeDelay=2000 ! number of time steps in the time delay histogram (default: ~1 season = 24*365/4) + + ! printing step frequency + integer(i4b),parameter,public :: print_step_freq = 1000 END MODULE globalData From 29cbba5c8203413c01119c5a1c3e8b654e329f91 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 27 Oct 2022 17:42:16 +0900 Subject: [PATCH 0415/1472] This might make NaNs in a sundials intermediate step, so make 0 if xtemp negative. --- build/source/engine/bigAquifer.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/source/engine/bigAquifer.f90 b/build/source/engine/bigAquifer.f90 index c4ed4ca4b..35e60b27c 100644 --- a/build/source/engine/bigAquifer.f90 +++ b/build/source/engine/bigAquifer.f90 @@ -134,6 +134,7 @@ subroutine bigAquifer(& ! compute the aquifer baseflow (m s-1) xTemp = scalarAquiferStorageTrial/aquiferScaleFactor + if (xTemp<0._rkind) xTemp = 0._rkind ! otherwise will give NaN in next line scalarAquiferBaseflow = aquiferBaseflowRate*(xTemp**aquiferBaseflowExp) ! compute the derivative in the net aquifer flux From 5b58d2a0f6bb4503a7424bad5bd9897648b053d5 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 28 Oct 2022 14:27:01 +0900 Subject: [PATCH 0416/1472] Turn off infeasibilities prints in Sundials too. --- build/source/engine/eval8summa.f90 | 4 ++-- build/source/engine/eval8summaSundials.f90 | 17 +++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 473b6c98d..1c6a7354e 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -287,7 +287,7 @@ subroutine eval8summa(& err=0; message="eval8summa/" ! check the feasibility of the solution always with SUMMA BE - ! NOTE: we will not print infeasibilities since it does not indicate a failure, just a need to backtrack + ! NOTE: we will not print infeasibilities since it does not indicate a failure, just a need to iterate until maxiter feasible=.true. ! check that the canopy air space temperature is reasonable @@ -337,7 +337,7 @@ subroutine eval8summa(& ! --> check if(stateVecTrial( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVecTrial( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. - if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax + !if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax endif ! if water states diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index af1be066d..7b3df2d63 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -321,35 +321,36 @@ subroutine eval8summaSundials(& feasible=.true. ! check the feasibility of the solution only if not inside Sundials solver + ! NOTE: we will not print infeasibilities since it does not indicate a failure, just a need to iterate until maxiter if (.not.insideIDA) then ! check that the canopy air space temperature is reasonable if(ixCasNrg/=integerMissing)then if(stateVec(ixCasNrg) > canopyTempMax) feasible=.false. if(stateVec(ixCasNrg) > canopyTempMax) message=trim(message)//'canopy air space temp high,' - if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixCasNrg )', feasible, canopyTempMax, stateVec(ixCasNrg) + !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixCasNrg )', feasible, canopyTempMax, stateVec(ixCasNrg) endif ! check that the canopy temperature is reasonable if(ixVegNrg/=integerMissing)then if(stateVec(ixVegNrg) > canopyTempMax) feasible=.false. if(stateVec(ixVegNrg) > canopyTempMax) message=trim(message)//'canopy temp high,' - if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixVegNrg )', feasible, canopyTempMax, stateVec(ixVegNrg) + !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixVegNrg )', feasible, canopyTempMax, stateVec(ixVegNrg) endif ! check canopy liquid water is not negative if(ixVegHyd/=integerMissing)then if(stateVec(ixVegHyd) < 0._rkind) feasible=.false. if(stateVec(ixVegHyd) < 0._rkind) message=trim(message)//'canopy water negative,' - if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVec( ixVegHyd )', feasible, 0._rkind, stateVec(ixVegHyd) + !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVec( ixVegHyd )', feasible, 0._rkind, stateVec(ixVegHyd) end if ! check snow temperature is below freezing if(count(ixSnowOnlyNrg/=integerMissing)>0)then if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) message=trim(message)//'snow temp above freezing,' - do iLayer=1,nSnow - if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVec( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVec( ixSnowOnlyNrg(iLayer) ) - enddo + !do iLayer=1,nSnow + ! if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVec( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVec( ixSnowOnlyNrg(iLayer) ) + !enddo endif ! loop through non-missing hydrology state variables in the snow+soil domain @@ -374,7 +375,7 @@ subroutine eval8summaSundials(& ! --> check if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) message=trim(message)//'layer water outside bounds,' - if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax + !if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax endif ! if water states @@ -385,7 +386,7 @@ subroutine eval8summaSundials(& fluxVec(:) = realMissing resVec(:) = quadMissing message=trim(message)//'non-feasible' - err=20; return + return end if end if ! ( feasibility check ) From 1201d590a0a4d10edc9963178599a208d4570801 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 31 Oct 2022 11:05:22 +0900 Subject: [PATCH 0417/1472] check output python script in utilities --- utils/check_bit_4_bit.py | 51 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 utils/check_bit_4_bit.py diff --git a/utils/check_bit_4_bit.py b/utils/check_bit_4_bit.py new file mode 100644 index 000000000..f3d265600 --- /dev/null +++ b/utils/check_bit_4_bit.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +import sys +import numpy as np +import xarray as xr +from pathlib import Path + +PRINT_MESSAGE = 'Test {0: >3} - Filename: {1: <64} - Total difference: {2: <8}' +ERROR_MESSAGE = 'Found differences in test cases: {}' +SUCCESS_MESSAGE = 'SUCCESSFUL TEST RUN: All files match!' +USAGE = 'USAGE: ./check_bit_4_bit.py BENCHMARK_DIRECTORY TEST_DIRECTORY' + +if len(sys.argv) != 3: + print(USAGE) + exit(1) + +bench_dir = sys.argv[1] +test_dir = sys.argv[2] + +bench_files = sorted(list(Path(bench_dir).glob('**/*.nc'))) +test_files = sorted(list(Path(test_dir).glob('**/*.nc'))) + + +assert len(bench_files) == len(test_files), \ + 'Found {} files but need {}!'.format(len(test_files), len(bench_files)) + + +def rem(li1, li2): + return list(set(li1) - set(li2)) + + +def compute_diffs(bench_files, test_files): + all_diffs = [] + all_tots = [] + for i, (f1, f2) in enumerate(zip(bench_files, test_files)): + ds1, ds2 = xr.open_dataset(str(f1)), xr.open_dataset(str(f2)) + diff = (ds1 - ds2).sum(dim='time') + tot = 0.0 + for v in rem(list(diff.variables.keys()), list(diff.dims.keys())): + tot += np.sum(diff[v].values) + all_diffs.append(diff) + all_tots.append(tot) + print(PRINT_MESSAGE.format(i, str(f1).split('/')[-1], tot)) + return all_diffs, all_tots + + +all_diffs, all_tots = compute_diffs(bench_files, test_files) + +assert np.sum(np.absolute(all_tots)) == 0.0, \ + ERROR_MESSAGE.format(np.argwhere(np.asarray(all_tots) != 0)) + +print(SUCCESS_MESSAGE) From 14d489932014be9f02811fb51294ae2c9eb7d412 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 31 Oct 2022 12:02:51 +0900 Subject: [PATCH 0418/1472] Zero out all new Jacobian terms --- build/source/engine/computJacob.f90 | 35 +++++++++++++++++++++++++---- build/source/engine/ssdNrgFlux.f90 | 6 ++++- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index d2f6c4bae..3d5402de6 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -262,6 +262,33 @@ subroutine computJacob(& ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* + ! UNDO bulk heat capacity depends on frac ice/liq + dVolHtCapBulk_dPsi0 = 0._rkind + dVolHtCapBulk_dTheta = 0._rkind + dVolHtCapBulk_dCanWat = 0._rkind + dVolHtCapBulk_dTk = 0._rkind + dVolHtCapBulk_dTkCanopy = 0._rkind + ! UNDO thermal conductivity at snow soil layer interfaces depends on frac ice/liq (ssdNrgFlux) + dNrgFlux_dWatAbove = 0._rkind + dNrgFlux_dWatBelow = 0._rkind + !dNrgFlux_dTempAbove, Have nonzero terms: in ssdNrgFlux make dThermalC_dNrgStateAbove = 0._rkind + !dNrgFlux_dTempBelow, Have nonzero terms: in ssdNrgFlux make dThermalC_dNrgStateBelow = 0._rkind + ! UNDO soil layer and aquifer transpiration depends on canopy nrg and wat (canopy transpiration) + mLayerdTrans_dTCanair = 0._rkind + mLayerdTrans_dTCanopy = 0._rkind + mLayerdTrans_dTGround = 0._rkind + mLayerdTrans_dCanWat = 0._rkind + dAquiferTrans_dTCanair = 0._rkind + dAquiferTrans_dTCanopy = 0._rkind + dAquiferTrans_dTGround = 0._rkind + dAquiferTrans_dCanWat = 0._rkind + ! UNDO aquifer recharge depends on soil drainage from interface above + !dq_dNrgStateAbove(nSoil), Used correctly elsewhere: zero out in aquifer equations a bit later + !dq_dHydStateAbove(nSoil), Used correctly elsewhere: zero out in aquifer equations a bit later + ! UNDO soil infiltration at surface depends on all layers below and above water and temp + dq_dHydStateLayerSurfVec = 0._rkind + dq_dNrgStateLayerSurfVec = 0._rkind + ! get the number of state variables nState = size(dMat) @@ -528,8 +555,8 @@ subroutine computJacob(& ! ---------------------------------------- if(ixAqWat/=integerMissing) then aJac(ixDiag,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) - if(ixSoilOnlyNrg(nSoil)/=integerMissing) aJac(ixOffDiag(ixAqWat,ixSoilOnlyNrg(nSoil)),ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk - if(ixSoilOnlyHyd(nSoil)/=integerMissing) aJac(ixOffDiag(ixAqWat,ixSoilOnlyHyd(nSoil)),ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat + !if(ixSoilOnlyNrg(nSoil)/=integerMissing) aJac(ixOffDiag(ixAqWat,ixSoilOnlyNrg(nSoil)),ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk + !if(ixSoilOnlyHyd(nSoil)/=integerMissing) aJac(ixOffDiag(ixAqWat,ixSoilOnlyHyd(nSoil)),ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat ! - only include banded derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration), would have to have few soil layers if(computeVegFlux)then if(ixCasNrg/=integerMissing)then @@ -882,8 +909,8 @@ subroutine computJacob(& ! ---------------------------------------- if(ixAqWat/=integerMissing) then aJac(ixAqWat,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) - if(ixSoilOnlyNrg(nSoil)/=integerMissing) aJac(ixAqWat,ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk - if(ixSoilOnlyHyd(nSoil)/=integerMissing) aJac(ixAqWat,ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat + !if(ixSoilOnlyNrg(nSoil)/=integerMissing) aJac(ixAqWat,ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk + !if(ixSoilOnlyHyd(nSoil)/=integerMissing) aJac(ixAqWat,ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) if(computeVegFlux)then if(ixCasNrg/=integerMissing) aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index 13d564d91..a35f5119c 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -227,7 +227,7 @@ subroutine ssdNrgFlux(& real(rkind),dimension(2) :: vectorthCond_soil ! layer above and below thermal conductivity of soil (W m-1 K-1) real(rkind),dimension(2) :: vectorfrac_sand ! layer above and below fraction of sand (-) real(rkind),dimension(2) :: vectorfrac_clay ! layer above and below fraction of clay (-) - ! recompute the perturbed version of iLayerThermalC, this could be the only version and remove the omputThermConduct_module + ! recompute the perturbed version of iLayerThermalC, this could be the only version and remove the computThermConduct_module real(rkind) :: dThermalC_dHydStateAbove ! derivative in the thermal conductivity w.r.t. water state in the layer above real(rkind) :: dThermalC_dHydStateBelow ! derivative in the thermal conductivity w.r.t. water state in the layer above real(rkind) :: dThermalC_dNrgStateAbove ! derivative in the thermal conductivity w.r.t. energy state in the layer above @@ -481,6 +481,10 @@ subroutine ssdNrgFlux(& ! output: error control err,cmessage) ! intent(out): error control + ! to zero out new Jacobian terms + dThermalC_dNrgStateAbove = 0._rkind + dThermalC_dNrgStateBelow = 0._rkind + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! compute total vertical flux, to compute derivatives From 632e29d16d1129b390d7e296bddfc70e4e2efe0e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 31 Oct 2022 12:59:35 +0900 Subject: [PATCH 0419/1472] need to make deriv_data inout if changing in computJacob --- build/source/engine/computJacob.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 3d5402de6..93150e54e 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -116,7 +116,7 @@ subroutine computJacob(& type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! input-output: Jacobian and its diagonal real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix From 2b5ed91452656cbb37e183857b8a171dde9c0747 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 31 Oct 2022 14:46:53 +0900 Subject: [PATCH 0420/1472] just a comment --- build/source/engine/ssdNrgFlux.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index 13d564d91..8f1422640 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -227,7 +227,7 @@ subroutine ssdNrgFlux(& real(rkind),dimension(2) :: vectorthCond_soil ! layer above and below thermal conductivity of soil (W m-1 K-1) real(rkind),dimension(2) :: vectorfrac_sand ! layer above and below fraction of sand (-) real(rkind),dimension(2) :: vectorfrac_clay ! layer above and below fraction of clay (-) - ! recompute the perturbed version of iLayerThermalC, this could be the only version and remove the omputThermConduct_module + ! recompute the perturbed version of iLayerThermalC, this could be the only version and remove the computThermConduct_module real(rkind) :: dThermalC_dHydStateAbove ! derivative in the thermal conductivity w.r.t. water state in the layer above real(rkind) :: dThermalC_dHydStateBelow ! derivative in the thermal conductivity w.r.t. water state in the layer above real(rkind) :: dThermalC_dNrgStateAbove ! derivative in the thermal conductivity w.r.t. energy state in the layer above From 6b3850d45157bcdc2a74a7f6cbb1ec75ea78a1df Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 31 Oct 2022 17:01:52 +0900 Subject: [PATCH 0421/1472] Don't check longwave balances in Sundials --- build/source/engine/computFlux.f90 | 6 +-- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/eval8summaSundials.f90 | 2 +- build/source/engine/systemSolvSundials.f90 | 2 +- build/source/engine/vegNrgFlux.f90 | 58 +++++++++++----------- 5 files changed, 34 insertions(+), 36 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 19716dbc6..813d32961 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -114,7 +114,7 @@ subroutine computFlux(& firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution - insideIDA, & ! intent(in): flag to indicate inside Sundials Solver (do not require longwave to be balanced) + checkLWBalance, & ! intent(in): flag to check longwave balance drainageMeltPond, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) ! input: state variables scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) @@ -168,7 +168,7 @@ subroutine computFlux(& logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - logical(lgt),intent(in) :: insideIDA ! flag if inside Sundials solver + logical(lgt),intent(in) :: checkLWBalance ! flag to check longwave balance real(rkind),intent(in) :: drainageMeltPond ! drainage from the surface melt pond (kg m-2 s-1) ! input: state variables real(rkind),intent(in) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) @@ -440,7 +440,7 @@ subroutine computFlux(& firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - insideIDA, & ! intent(in): flag to indicate inside Sundials Solver (do not require longwave to be balanced) + checkLWBalance, & ! intent(in): flag to check longwave balance ! input: model state variables upperBoundTemp, & ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature scalarCanairTempTrial, & ! intent(in): trial value of the canopy air space temperature (K) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 1c6a7354e..ea50c480e 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -474,7 +474,7 @@ subroutine eval8summa(& firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution - .true., & ! intent(in): balance longwave + .true., & ! intent(in): check longwave balance scalarSfcMeltPond/dt, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) ! input: state variables scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index 7b3df2d63..47ff610b6 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -670,7 +670,7 @@ subroutine eval8summaSundials(& firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution - insideIDA, & ! intent(in): logical flag if inside Sundials solver + .false., & ! intent(in): do not check longwave balance scalarSfcMeltPond/dt, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) ! input: state variables scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 962e70d2d..d63843bba 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -545,7 +545,7 @@ subroutine systemSolvSundials(& end do ! compute the total change in storage associated with compression of the soil matrix (kg m-2) - diag_data%var(iLookDIAG%mLayerCompress)%dat(:) = mLayerCmpress_sum(:) + diag_data%var(iLookDIAG%mLayerCompress)%dat(:) = mLayerCmpress_sum(:) diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sum(diag_data%var(iLookDIAG%mLayerCompress)%dat(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water ! save the computed solution diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 63f67d76d..e0aa76713 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -131,7 +131,7 @@ subroutine vegNrgFlux(& firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - insideIDA, & ! intent(in): flag to indicate inside Sundials Solver (do not require longwave to be balanced) + checkLWBalance, & ! intent(in): flag to check longwave balance ! input: model state variables upperBoundTemp, & ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature @@ -221,7 +221,7 @@ subroutine vegNrgFlux(& logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(in) :: firstFluxCall ! flag to indicate if we are processing the first flux call logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: insideIDA ! flag if inside Sundials solver + logical(lgt),intent(in) :: checkLWBalance ! flag to check longwave balance ! input: model state variables real(rkind),intent(in) :: upperBoundTemp ! temperature of the upper boundary (K) --> NOTE: use air temperature real(rkind),intent(in) :: canairTempTrial ! trial value of canopy air space temperature (K) @@ -969,7 +969,7 @@ subroutine vegNrgFlux(& ! input: model control ixDerivMethod, & ! intent(in): method used to calculate flux derivatives computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation - insideIDA, & ! intent(in): flag to indicate inside Sundials Solver (do not require longwave to be balanced) + checkLWBalance, & ! intent(in): flag to check longwave balance ! input: canopy and ground temperature canopyTempTrial, & ! intent(in): temperature of the vegetation canopy (K) groundTempTrial, & ! intent(in): temperature of the ground surface (K) @@ -1696,8 +1696,8 @@ end subroutine logisticSmoother subroutine longwaveBal(& ! input: model control ixDerivMethod, & ! intent(in): choice of method used to compute derivative (analytical or numerical) - computeVegFlux, & ! intent(in): flag to compute fluxes over vegetati - insideIDA, & ! intent(in): flag to indicate inside Sundials Solver (do not require longwave to be balanced) + computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation + checkLWBalance, & ! intent(in): flag to check longwave balance ! input: canopy and ground temperature canopyTemp, & ! intent(in): canopy temperature (K) groundTemp, & ! intent(in): ground temperature (K) @@ -1732,9 +1732,9 @@ subroutine longwaveBal(& ! ----------------------------------------------------------------------------------------------------------------------------------------------- implicit none ! input: model control - integer(i4b),intent(in) :: ixDerivMethod ! choice of method used to compute derivative (analytical or numerical) - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: insideIDA ! flag if inside Sundials solver + integer(i4b),intent(in) :: ixDerivMethod ! choice of method used to compute derivative (analytical or numerical) + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: checkLWBalance ! flag to check longwave balance ! input: canopy and ground temperature real(rkind),intent(in) :: canopyTemp ! canopy temperature (K) real(rkind),intent(in) :: groundTemp ! ground temperature (K) @@ -1859,29 +1859,27 @@ subroutine longwaveBal(& LWNetUbound = LWRadUbound - LWRadUbound2Ubound - LWRadCanopy2Ubound - LWRadGround2Ubound ! upper boundary ! check the flux balance - if(.not.insideIDA)then fluxBalance = LWNetUbound - (LWNetCanopy + LWNetGround) - if(abs(fluxBalance) > fluxTolerance)then - print*, 'fluxBalance = ', fluxBalance - print*, 'emg, emc = ', emg, emc - print*, 'TCan, TGnd = ', TCan, TGnd - print*, 'LWRadUbound = ', LWRadUbound - print*, 'LWRadCanopy = ', LWRadCanopy - print*, 'LWRadGround = ', LWRadGround - print*, 'LWRadUbound2Canopy = ', LWRadUbound2Canopy - print*, 'LWRadUbound2Ground = ', LWRadUbound2Ground - print*, 'LWRadUbound2Ubound = ', LWRadUbound2Ubound - print*, 'LWRadCanopy2Ubound = ', LWRadCanopy2Ubound - print*, 'LWRadCanopy2Ground = ', LWRadCanopy2Ground - print*, 'LWRadCanopy2Canopy = ', LWRadCanopy2Canopy - print*, 'LWRadGround2Ubound = ', LWRadGround2Ubound - print*, 'LWRadGround2Canopy = ', LWRadGround2Canopy - print*, 'LWNetCanopy = ', LWNetCanopy - print*, 'LWNetGround = ', LWNetGround - print*, 'LWNetUbound = ', LWNetUbound - message=trim(message)//'flux imbalance' - err=20; return - end if + if(abs(fluxBalance) > fluxTolerance .and. checkLWBalance)then + print*, 'fluxBalance = ', fluxBalance + print*, 'emg, emc = ', emg, emc + print*, 'TCan, TGnd = ', TCan, TGnd + print*, 'LWRadUbound = ', LWRadUbound + print*, 'LWRadCanopy = ', LWRadCanopy + print*, 'LWRadGround = ', LWRadGround + print*, 'LWRadUbound2Canopy = ', LWRadUbound2Canopy + print*, 'LWRadUbound2Ground = ', LWRadUbound2Ground + print*, 'LWRadUbound2Ubound = ', LWRadUbound2Ubound + print*, 'LWRadCanopy2Ubound = ', LWRadCanopy2Ubound + print*, 'LWRadCanopy2Ground = ', LWRadCanopy2Ground + print*, 'LWRadCanopy2Canopy = ', LWRadCanopy2Canopy + print*, 'LWRadGround2Ubound = ', LWRadGround2Ubound + print*, 'LWRadGround2Canopy = ', LWRadGround2Canopy + print*, 'LWNetCanopy = ', LWNetCanopy + print*, 'LWNetGround = ', LWNetGround + print*, 'LWNetUbound = ', LWNetUbound + message=trim(message)//'flux imbalance' + err=20; return end if ! -------------------------------------------------------------------------------------- From 3baf1fb2cc0b9dbf51124d03fb1957ea883e5861 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 1 Nov 2022 12:30:26 +0900 Subject: [PATCH 0422/1472] just delete Makefile, everything besides folder engine and dshare is spaces fixes or should stay --- build/source/driver/Makefile | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 build/source/driver/Makefile diff --git a/build/source/driver/Makefile b/build/source/driver/Makefile deleted file mode 100644 index e69de29bb..000000000 From 76985f77b956a648e37fbd13af230151b28efadf Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 1 Nov 2022 13:25:21 +0900 Subject: [PATCH 0423/1472] looking for changes, just more lookup tables and comments --- build/source/engine/diagn_evar.f90 | 2 +- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/opSplittin.f90 | 2 +- build/source/engine/summaSolve.f90 | 2 +- build/source/engine/systemSolv.f90 | 2 +- build/source/engine/updateVars.f90 | 2 +- build/source/engine/varSubstep.f90 | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/build/source/engine/diagn_evar.f90 b/build/source/engine/diagn_evar.f90 index 697f08e50..d3021249f 100644 --- a/build/source/engine/diagn_evar.f90 +++ b/build/source/engine/diagn_evar.f90 @@ -1,5 +1,5 @@ ! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2015 NCAR/RAL +! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington ! ! This file is part of SUMMA ! diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index ea50c480e..019d6e9c2 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -1,5 +1,5 @@ ! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2015 NCAR/RAL +! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington ! ! This file is part of SUMMA ! diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index a17f2761f..9751eaef8 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -1,5 +1,5 @@ ! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2015 NCAR/RAL +! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington ! ! This file is part of SUMMA ! diff --git a/build/source/engine/summaSolve.f90 b/build/source/engine/summaSolve.f90 index 5dee64990..a776703df 100644 --- a/build/source/engine/summaSolve.f90 +++ b/build/source/engine/summaSolve.f90 @@ -1,5 +1,5 @@ ! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2015 NCAR/RAL +! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington ! ! This file is part of SUMMA ! diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 31fe2388c..94a090149 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -1,5 +1,5 @@ ! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2015 NCAR/RAL +! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington ! ! This file is part of SUMMA ! diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 index 78af5fa36..fae6278b5 100644 --- a/build/source/engine/updateVars.f90 +++ b/build/source/engine/updateVars.f90 @@ -1,5 +1,5 @@ ! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2015 NCAR/RAL +! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington ! ! This file is part of SUMMA ! diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index ab164f89e..fa3864a4a 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -1,5 +1,5 @@ ! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2015 NCAR/RAL +! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington ! ! This file is part of SUMMA ! From 4a58240916e7c1d6ab13546b497d5d1ed1c0bcf2 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 1 Nov 2022 14:02:01 +0900 Subject: [PATCH 0424/1472] Put new Jacobian terms back in --- build/source/engine/computJacob.f90 | 29 +---------------------------- build/source/engine/ssdNrgFlux.f90 | 4 ---- 2 files changed, 1 insertion(+), 32 deletions(-) diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 93150e54e..48f9e3fbf 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -116,7 +116,7 @@ subroutine computJacob(& type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! input-output: Jacobian and its diagonal real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix @@ -262,33 +262,6 @@ subroutine computJacob(& ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* - ! UNDO bulk heat capacity depends on frac ice/liq - dVolHtCapBulk_dPsi0 = 0._rkind - dVolHtCapBulk_dTheta = 0._rkind - dVolHtCapBulk_dCanWat = 0._rkind - dVolHtCapBulk_dTk = 0._rkind - dVolHtCapBulk_dTkCanopy = 0._rkind - ! UNDO thermal conductivity at snow soil layer interfaces depends on frac ice/liq (ssdNrgFlux) - dNrgFlux_dWatAbove = 0._rkind - dNrgFlux_dWatBelow = 0._rkind - !dNrgFlux_dTempAbove, Have nonzero terms: in ssdNrgFlux make dThermalC_dNrgStateAbove = 0._rkind - !dNrgFlux_dTempBelow, Have nonzero terms: in ssdNrgFlux make dThermalC_dNrgStateBelow = 0._rkind - ! UNDO soil layer and aquifer transpiration depends on canopy nrg and wat (canopy transpiration) - mLayerdTrans_dTCanair = 0._rkind - mLayerdTrans_dTCanopy = 0._rkind - mLayerdTrans_dTGround = 0._rkind - mLayerdTrans_dCanWat = 0._rkind - dAquiferTrans_dTCanair = 0._rkind - dAquiferTrans_dTCanopy = 0._rkind - dAquiferTrans_dTGround = 0._rkind - dAquiferTrans_dCanWat = 0._rkind - ! UNDO aquifer recharge depends on soil drainage from interface above - !dq_dNrgStateAbove(nSoil), Used correctly elsewhere: zero out in aquifer equations a bit later - !dq_dHydStateAbove(nSoil), Used correctly elsewhere: zero out in aquifer equations a bit later - ! UNDO soil infiltration at surface depends on all layers below and above water and temp - dq_dHydStateLayerSurfVec = 0._rkind - dq_dNrgStateLayerSurfVec = 0._rkind - ! get the number of state variables nState = size(dMat) diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index a35f5119c..8f1422640 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -481,10 +481,6 @@ subroutine ssdNrgFlux(& ! output: error control err,cmessage) ! intent(out): error control - ! to zero out new Jacobian terms - dThermalC_dNrgStateAbove = 0._rkind - dThermalC_dNrgStateBelow = 0._rkind - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! compute total vertical flux, to compute derivatives From d1115dafe84d96c0bf6fc9e80c6a19762a2d453e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 1 Nov 2022 16:23:42 +0900 Subject: [PATCH 0425/1472] backwards compatibility with howHeatCap decision and making all feasiblity errors read the same --- build/source/engine/mDecisions.f90 | 6 +++++- build/source/engine/summaSolve.f90 | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index dbd97e4cc..7361dcad3 100644 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -406,7 +406,11 @@ subroutine mDecisions(err,message) case('enthalpyFD'); model_decisions(iLookDECISIONS%howHeatCap)%iDecision = enthalpyFD ! enthalpyFD case('closedForm'); model_decisions(iLookDECISIONS%howHeatCap)%iDecision = closedForm ! closedForm case default - err=10; message=trim(message)//"unknown Cp computation [option="//trim(model_decisions(iLookDECISIONS%howHeatCap)%cDecision)//"]"; return + if (model_decisions(iLookDECISIONS%num_method)%iDecision==bEuler)then + model_decisions(iLookDECISIONS%howHeatCap)%iDecision = closedForm ! not used (included for backwards compatibility) + else + err=10; message=trim(message)//"unknown Cp computation [option="//trim(model_decisions(iLookDECISIONS%howHeatCap)%cDecision)//"]"; return + endif end select ! identify the method used to calculate flux derivatives diff --git a/build/source/engine/summaSolve.f90 b/build/source/engine/summaSolve.f90 index a776703df..685352eba 100644 --- a/build/source/engine/summaSolve.f90 +++ b/build/source/engine/summaSolve.f90 @@ -671,7 +671,7 @@ subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fl if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! check feasibility (should be feasible because of the call to imposeConstraints - if(.not.feasible)then; err=20; message=trim(message)//'infeasible solution'; return; endif + if(.not.feasible)then; err=20; message=trim(message)//'state vector not feasible'; return; endif ! check convergence converged = checkConv(resVecNew,xInc,stateVecNew) @@ -724,7 +724,7 @@ subroutine getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,message) if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! check that the trial value is feasible (should not happen because of the call to impose constraints) - if(.not.feasible)then; message=trim(message)//'state vector is not feasible'; err=20; return; endif + if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif ! update brackets if(real(resVecNew(1), rkind)<0._rkind)then From 06ca01ed29fd1a5fc1e51bd3f80b999edaee0139 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 7 Nov 2022 22:49:06 +0900 Subject: [PATCH 0426/1472] spaces --- build/source/engine/coupled_em.f90 | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index b91d94f38..97a309d21 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -816,19 +816,19 @@ subroutine coupled_em(& end if ! (if computing the vegetation flux) call computSnowDepth(& - dt_sub, & ! intent(in) - nSnow, & ! intent(in) - scalarSnowSublimation, & ! intent(in) - mLayerVolFracLiq, & ! intent(inout) - mLayerVolFracIce, & ! intent(inout) - prog_data%var(iLookPROG%mLayerTemp)%dat, & ! intent(in) - diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat, & ! intent(in) - mpar_data, & ! intent(in) - ! output - tooMuchSublim, & ! intent(out): flag to denote that there was too much sublimation in a given time step - mLayerDepth, & ! intent(inout) - ! error control - err,message) ! intent(out): error control + dt_sub, & ! intent(in) + nSnow, & ! intent(in) + scalarSnowSublimation, & ! intent(in) + mLayerVolFracLiq, & ! intent(inout) + mLayerVolFracIce, & ! intent(inout) + prog_data%var(iLookPROG%mLayerTemp)%dat, & ! intent(in) + diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat, & ! intent(in) + mpar_data, & ! intent(in) + ! output + tooMuchSublim, & ! intent(out): flag to denote that there was too much sublimation in a given time step + mLayerDepth, & ! intent(inout) + ! error control + err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if ! process the flag for too much sublimation From 8a12eeea7c71ea6f1e6fa145a4568818985012ea Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 4 Nov 2022 12:53:20 +0900 Subject: [PATCH 0427/1472] change inputs to be matric head liq, correct in most cases except where calculating critT --- build/source/engine/computFlux.f90 | 4 ++++ build/source/engine/soilLiqFlx.f90 | 32 ++++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 813d32961..1d62eb631 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -699,6 +699,10 @@ subroutine computFlux(& .true., & ! intent(in): flag indicating if derivatives are desired ! input: trial state variables mLayerTempTrial(nSnow+1:nLayers), & ! intent(in): trial temperature at the current iteration (K) +<<<<<<< HEAD +======= + mLayerMatricHeadTrial(1:nSoil), & ! intent(in): matric potential (m) +>>>>>>> d92f5885 (change inputs to be matric head liq, correct in most cases, except where calculating critT) mLayerMatricHeadLiqTrial(1:nSoil), & ! intent(in): liquid water matric potential (m) mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water (-) mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of ice (-) diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index 9ae0adb6a..bf275ec0f 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -96,6 +96,10 @@ subroutine soilLiqFlx(& deriv_desired, & ! intent(in): flag indicating if derivatives are desired ! input: trial state variables mLayerTempTrial, & ! intent(in): temperature (K) +<<<<<<< HEAD +======= + mLayerMatricHeadTrial, & ! intent(in): matric head (m) +>>>>>>> d92f5885 (change inputs to be matric head liq, correct in most cases, except where calculating critT) mLayerMatricHeadLiqTrial, & ! intent(in): liquid matric head (m) mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water (-) mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice (-) @@ -165,6 +169,10 @@ subroutine soilLiqFlx(& logical(lgt),intent(in) :: deriv_desired ! flag indicating if derivatives are desired ! input: trial model state variables real(rkind),intent(in) :: mLayerTempTrial(:) ! temperature in each layer at the current iteration (m) +<<<<<<< HEAD +======= + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! matric head in each layer at the current iteration (m) +>>>>>>> d92f5885 (change inputs to be matric head liq, correct in most cases, except where calculating critT) real(rkind),intent(in) :: mLayerMatricHeadLiqTrial(:) ! liquid matric head in each layer at the current iteration (m) real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! volumetric fraction of liquid water at the current iteration (-) real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! volumetric fraction of ice at the current iteration (-) @@ -516,7 +524,11 @@ subroutine soilLiqFlx(& ! input: state variables mLayerTempTrial, & ! intent(in): temperature (K) scalarMatricHeadLiqTrial, & ! intent(in): liquid matric head in the upper-most soil layer (m) +<<<<<<< HEAD mLayerMatricHeadLiqTrial, & ! intent(in): liquid matric head in each soil layer (m) +======= + mLayerMatricHeadTrial, & ! intent(in): matric head in each soil layer (m) +>>>>>>> d92f5885 (change inputs to be matric head liq, correct in most cases, except where calculating critT) scalarVolFracLiqTrial, & ! intent(in): volumetric liquid water content the upper-most soil layer (-) mLayerVolFracLiqTrial, & ! intent(in): volumetric liquid water content in each soil layer (-) mLayerVolFracIceTrial, & ! intent(in): volumetric ice content in each soil layer (-) @@ -1104,7 +1116,7 @@ subroutine surfaceFlx(& nSoil, & ! intent(in): number of soil layers ! input: state variables mLayerTemp, & ! intent(in): temperature (K) - scalarMatricHead, & ! intent(in): matric head in the upper-most soil layer (m) + scalarMatricHeadLiq, & ! intent(in): liquid matric head in the upper-most soil layer (m) mLayerMatricHead, & ! intent(in): matric head in each soil layer (m) scalarVolFracLiq, & ! intent(in): volumetric liquid water content in the upper-most soil layer (-) mLayerVolFracLiq, & ! intent(in): volumetric liquid water content in each soil layer (-) @@ -1173,7 +1185,7 @@ subroutine surfaceFlx(& integer(i4b),intent(in) :: nSoil ! number of soil layers ! input: state and diagnostic variables real(rkind),intent(in) :: mLayerTemp(:) ! temperature (K) - real(rkind),intent(in) :: scalarMatricHead ! matric head in the upper-most soil layer (m) + real(rkind),intent(in) :: scalarMatricHeadLiq ! liquid matric head in the upper-most soil layer (m) real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head in each soil layer (m) real(rkind),intent(in) :: scalarVolFracLiq ! volumetric liquid water content in the upper-most soil layer (-) real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric liquid water content in each soil layer (-) @@ -1303,7 +1315,7 @@ subroutine surfaceFlx(& surfaceHydCond = hydCond_psi(upperBoundHead,surfaceSatHydCond,vGn_alpha,vGn_n,vGn_m) * iceImpedeFac surfaceDiffuse = realMissing ! compute the capillary flux - cflux = -surfaceHydCond*(scalarMatricHead - upperBoundHead) / (mLayerDepth(1)*0.5_rkind) + cflux = -surfaceHydCond*(scalarMatricHeadLiq - upperBoundHead) / (mLayerDepth(1)*0.5_rkind) case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return end select ! (form of Richards' eqn) ! compute the total flux @@ -1317,7 +1329,7 @@ subroutine surfaceFlx(& case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return end select ! compute the energy derivative at the surface - dq_dNrgStateVec(1) = -(dHydCond_dTemp/2._rkind)*(scalarMatricHead - upperBoundHead)/(mLayerDepth(1)*0.5_rkind) + dHydCond_dTemp/2._rkind + dq_dNrgStateVec(1) = -(dHydCond_dTemp/2._rkind)*(scalarMatricHeadLiq - upperBoundHead)/(mLayerDepth(1)*0.5_rkind) + dHydCond_dTemp/2._rkind else dNum = 0._rkind end if @@ -1664,7 +1676,7 @@ subroutine qDrainFlux(& ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) bc_lower, & ! intent(in): index defining the type of boundary conditions ! input: state variables - nodeMatricHead, & ! intent(in): matric head in the lowest unsaturated node (m) + nodeMatricHeadLiq, & ! intent(in): liquid matric head in the lowest unsaturated node (m) nodeVolFracLiq, & ! intent(in): volumetric liquid water content the lowest unsaturated node (-) ! input: model coordinate variables nodeDepth, & ! intent(in): depth of the lowest unsaturated soil layer (m) @@ -1714,7 +1726,11 @@ subroutine qDrainFlux(& integer(i4b),intent(in) :: ixRichards ! index defining the option for Richards' equation (moisture or mixdform) integer(i4b),intent(in) :: bc_lower ! index defining the type of boundary conditions ! input: state and diagnostic variables +<<<<<<< HEAD real(rkind),intent(in) :: nodeMatricHead ! matric head in the lowest unsaturated node (m) +======= + real(rkind),intent(in) :: nodeMatricHeadLiq ! liquid matric head in the lowest unsaturated node (m) +>>>>>>> d92f5885 (change inputs to be matric head liq, correct in most cases, except where calculating critT) real(rkind),intent(in) :: nodeVolFracLiq ! volumetric liquid water content in the lowest unsaturated node (-) ! input: model coordinate variables real(rkind),intent(in) :: nodeDepth ! depth of the lowest unsaturated soil layer (m) @@ -1783,7 +1799,7 @@ subroutine qDrainFlux(& bottomHydCond = hydCond_psi(lowerBoundHead,bottomSatHydCond,vGn_alpha,vGn_n,vGn_m) * iceImpedeFac bottomDiffuse = realMissing ! compute the capillary flux - cflux = -bottomHydCond*(lowerBoundHead - nodeMatricHead) / (nodeDepth*0.5_rkind) + cflux = -bottomHydCond*(lowerBoundHead - nodeMatricHeadLiq) / (nodeDepth*0.5_rkind) case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return end select ! (form of Richards' eqn) scalarDrainage = cflux + bottomHydCond @@ -1797,7 +1813,7 @@ subroutine qDrainFlux(& case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return end select ! energy derivatives - dq_dNrgStateUnsat = -(dHydCond_dTemp/2._rkind)*(lowerBoundHead - nodeMatricHead)/(nodeDepth*0.5_rkind) + dHydCond_dTemp/2._rkind + dq_dNrgStateUnsat = -(dHydCond_dTemp/2._rkind)*(lowerBoundHead - nodeMatricHeadLiq)/(nodeDepth*0.5_rkind) + dHydCond_dTemp/2._rkind else ! (do not desire derivatives) dq_dHydStateUnsat = realMissing dq_dNrgStateUnsat = realMissing @@ -1811,7 +1827,7 @@ subroutine qDrainFlux(& ! compute fluxes select case(ixRichards) case(moisture); nodePsi = matricHead(nodeVolFracLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - case(mixdform); nodePsi = nodeMatricHead + case(mixdform); nodePsi = nodeMatricHeadLiq end select zWater = nodeHeight - nodePsi scalarDrainage = kAnisotropic*surfaceSatHydCond * exp(-zWater/zScale_TOPMODEL) From 510525f80717dc91aa767d1884e5edddd7331488 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 7 Nov 2022 23:02:49 +0900 Subject: [PATCH 0428/1472] cleaning up last commit --- build/source/engine/computFlux.f90 | 3 --- build/source/engine/soilLiqFlx.f90 | 26 ++++++-------------------- 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 1d62eb631..4149859b2 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -699,10 +699,7 @@ subroutine computFlux(& .true., & ! intent(in): flag indicating if derivatives are desired ! input: trial state variables mLayerTempTrial(nSnow+1:nLayers), & ! intent(in): trial temperature at the current iteration (K) -<<<<<<< HEAD -======= mLayerMatricHeadTrial(1:nSoil), & ! intent(in): matric potential (m) ->>>>>>> d92f5885 (change inputs to be matric head liq, correct in most cases, except where calculating critT) mLayerMatricHeadLiqTrial(1:nSoil), & ! intent(in): liquid water matric potential (m) mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water (-) mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of ice (-) diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index bf275ec0f..84bab394b 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -96,10 +96,7 @@ subroutine soilLiqFlx(& deriv_desired, & ! intent(in): flag indicating if derivatives are desired ! input: trial state variables mLayerTempTrial, & ! intent(in): temperature (K) -<<<<<<< HEAD -======= mLayerMatricHeadTrial, & ! intent(in): matric head (m) ->>>>>>> d92f5885 (change inputs to be matric head liq, correct in most cases, except where calculating critT) mLayerMatricHeadLiqTrial, & ! intent(in): liquid matric head (m) mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water (-) mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice (-) @@ -169,10 +166,7 @@ subroutine soilLiqFlx(& logical(lgt),intent(in) :: deriv_desired ! flag indicating if derivatives are desired ! input: trial model state variables real(rkind),intent(in) :: mLayerTempTrial(:) ! temperature in each layer at the current iteration (m) -<<<<<<< HEAD -======= real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! matric head in each layer at the current iteration (m) ->>>>>>> d92f5885 (change inputs to be matric head liq, correct in most cases, except where calculating critT) real(rkind),intent(in) :: mLayerMatricHeadLiqTrial(:) ! liquid matric head in each layer at the current iteration (m) real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! volumetric fraction of liquid water at the current iteration (-) real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! volumetric fraction of ice at the current iteration (-) @@ -524,11 +518,7 @@ subroutine soilLiqFlx(& ! input: state variables mLayerTempTrial, & ! intent(in): temperature (K) scalarMatricHeadLiqTrial, & ! intent(in): liquid matric head in the upper-most soil layer (m) -<<<<<<< HEAD - mLayerMatricHeadLiqTrial, & ! intent(in): liquid matric head in each soil layer (m) -======= mLayerMatricHeadTrial, & ! intent(in): matric head in each soil layer (m) ->>>>>>> d92f5885 (change inputs to be matric head liq, correct in most cases, except where calculating critT) scalarVolFracLiqTrial, & ! intent(in): volumetric liquid water content the upper-most soil layer (-) mLayerVolFracLiqTrial, & ! intent(in): volumetric liquid water content in each soil layer (-) mLayerVolFracIceTrial, & ! intent(in): volumetric ice content in each soil layer (-) @@ -1286,7 +1276,7 @@ subroutine surfaceFlx(& ! initialize error control err=0; message="surfaceFlx/" - + ! initialize derivatives dq_dHydStateVec(:) = 0._rkind dq_dNrgStateVec(:) = 0._rkind @@ -1478,17 +1468,17 @@ subroutine surfaceFlx(& dInfilRate_dWat(1:nSoil) = 0._rkind dInfilRate_dTk(1:nSoil) = 0._rkind endif - + ! dq w.r.t. infiltration only, scalarRainPlusMelt accounted for in computJacob module dq_dHydStateVec(:) = (1._rkind - scalarFrozenArea) * ( dInfilArea_dWat(:)*min(scalarRainPlusMelt,xMaxInfilRate) + scalarInfilArea*dInfilRate_dWat(:) ) +& (-dFrozenArea_dWat(:))*scalarInfilArea*min(scalarRainPlusMelt,xMaxInfilRate) dq_dNrgStateVec(:) = (1._rkind - scalarFrozenArea) * ( dInfilArea_dTk(:) *min(scalarRainPlusMelt,xMaxInfilRate) + scalarInfilArea*dInfilRate_dTk(:) ) +& (-dFrozenArea_dTk(:)) *scalarInfilArea*min(scalarRainPlusMelt,xMaxInfilRate) - + else ! do not compute infiltration after first flux call in a splitting operation dq_dHydStateVec(:) = 0._rkind dq_dNrgStateVec(:) = 0._rkind - + end if ! (if desire to compute infiltration) ! compute infiltration (m s-1), if after first flux call in a splitting operation does not change @@ -1496,11 +1486,11 @@ subroutine surfaceFlx(& ! compute surface runoff (m s-1) scalarSurfaceRunoff = scalarRainPlusMelt - scalarSurfaceInfiltration - + ! set surface hydraulic conductivity and diffusivity to missing (not used for flux condition) surfaceHydCond = realMissing surfaceDiffuse = realMissing - + ! ***** error check case default; err=20; message=trim(message)//'unknown upper boundary condition for soil hydrology'; return @@ -1726,11 +1716,7 @@ subroutine qDrainFlux(& integer(i4b),intent(in) :: ixRichards ! index defining the option for Richards' equation (moisture or mixdform) integer(i4b),intent(in) :: bc_lower ! index defining the type of boundary conditions ! input: state and diagnostic variables -<<<<<<< HEAD - real(rkind),intent(in) :: nodeMatricHead ! matric head in the lowest unsaturated node (m) -======= real(rkind),intent(in) :: nodeMatricHeadLiq ! liquid matric head in the lowest unsaturated node (m) ->>>>>>> d92f5885 (change inputs to be matric head liq, correct in most cases, except where calculating critT) real(rkind),intent(in) :: nodeVolFracLiq ! volumetric liquid water content in the lowest unsaturated node (-) ! input: model coordinate variables real(rkind),intent(in) :: nodeDepth ! depth of the lowest unsaturated soil layer (m) From 9985c64a992a45e90ee1f2ecb25e998c37e83ad6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 7 Nov 2022 23:06:50 +0900 Subject: [PATCH 0429/1472] mLayerMeltFreeze was not being calculated without snow, fixed here --- build/source/engine/opSplittin.f90 | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 9751eaef8..0317822d5 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -952,10 +952,9 @@ subroutine opSplittin(& if(ixCoupling/=fullyCoupled .or. nSubsteps>1) dtMultiplier=0.5_rkind ! compute the melt in each snow and soil layer - if(nSnow>0)then - diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(1:nSnow) = -( mLayerVolFracIce(1:nSnow) - mLayerVolFracIceInit(1:nSnow) ) * iden_ice - diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(nSnow+1:nLayers) = -(mLayerVolFracIce(nSnow+1:nLayers) - mLayerVolFracIceInit(nSnow+1:nLayers))*iden_water - endif + if(nSnow>0)& + mLayerMeltFreeze( 1:nSnow ) = -(mLayerVolFracIce( 1:nSnow ) - mLayerVolFracIceInit( 1:nSnow ))*iden_ice + mLayerMeltFreeze(nSnow+1:nLayers) = -(mLayerVolFracIce(nSnow+1:nLayers) - mLayerVolFracIceInit(nSnow+1:nLayers))*iden_water ! end associate statements end associate globalVars From 968464becc599ffa2f3d179fbe5860a6908f4c7f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 7 Nov 2022 14:49:11 +0900 Subject: [PATCH 0430/1472] remove some unnecessary USE statements --- build/source/engine/getVectorz.f90 | 2 -- 1 file changed, 2 deletions(-) diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index 61bbae0cc..f6a320134 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -87,8 +87,6 @@ module getVectorz_module USE soil_utils_module,only:dPsi_dTheta ! derivative in the soil water characteristic (soil) USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water -USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists -USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential implicit none private From 5e323b8a26b09ae8ee750e2111adc4fb5fc77e4a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 7 Nov 2022 23:23:20 +0900 Subject: [PATCH 0431/1472] missing iLayerLiqFluxSoil initialize at 0 in computFlux --- build/source/engine/computFlux.f90 | 4 +++- build/source/engine/coupled_em.f90 | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 4149859b2..5687a8ad9 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -418,7 +418,9 @@ subroutine computFlux(& ! initialize liquid water fluxes throughout the snow and soil domains ! NOTE: used in the energy routines, which is called before the hydrology routines if(firstFluxCall)then - if(nSnow > 0) iLayerLiqFluxSnow(0:nSnow) = 0._rkind + if(nSnow>0)& + iLayerLiqFluxSnow(0:nSnow) = 0._rkind + iLayerLiqFluxSoil(0:nSoil) = 0._rkind end if ! ***** diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 97a309d21..287b71df6 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -355,7 +355,7 @@ subroutine coupled_em(& err=20; return endif if(nSnow>0) liqSnowInit = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow) - liqSoilInit = mLayerVolFracLiq + liqSoilInit = mLayerVolFracLiq endif ! end association of local variables with information in the data structures From 3271045c54e7e0ceded23ed236ef9eb97c283e71 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 10 Nov 2022 12:58:19 +0900 Subject: [PATCH 0432/1472] always have to do layer 0 even if not in ixTop ixBot in ssdNrgFlux --- build/source/engine/ssdNrgFlux.f90 | 46 ++++++++++++++++++------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index 8f1422640..3c6b1785f 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -186,7 +186,7 @@ subroutine ssdNrgFlux(& ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! local variables character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b) :: i,j,iLayer ! index of model layers + integer(i4b) :: i,j,iLayer,iLayer0 ! index of model layers integer(i4b) :: ixLayerDesired(1) ! layer desired (scalar solution) integer(i4b) :: ixTop ! top layer in subroutine call integer(i4b) :: ixBot ! bottom layer in subroutine call @@ -293,7 +293,7 @@ subroutine ssdNrgFlux(& ixTop = ixLayerDesired(1) ixBot = ixLayerDesired(1) else - ixTop = 0 !include layer 0 in layer interface derivatives + ixTop = 1 !include layer 0 in layer interface derivatives ixBot = nLayers endif @@ -310,8 +310,13 @@ subroutine ssdNrgFlux(& dFlux_dTempBelow(nLayers) = -huge(lowerBoundTemp) ! don't expect this to be used, so deliberately set to a ridiculous value to cause problems dFlux_dWatBelow(nLayers) = -huge(lowerBoundTemp) ! don't expect this to be used, so deliberately set to a ridiculous value to cause problems - ! loop through INTERFACES... - do iLayer=ixTop,ixBot + ! loop through INTERFACES... + do iLayer0=ixTop-1,ixBot + if(ixTop-1/=0 .and. iLayer==ixTop-1)then + iLayer = 0 !need to always do layer 0 + else + iLayer = iLayer0 + endif ! either one or multiple flux calls, depending on if using analytical or numerical derivatives do itry=nFlux,0,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) @@ -506,7 +511,7 @@ subroutine ssdNrgFlux(& ! * prescribed temperature at the upper boundary case(prescribedTemp) - dz = (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) + dz = mLayerHeight(iLayer+1)*0.5_rkind if(ixDerivMethod==analytical)then ! ** analytical derivatives dFlux_dWatBelow(iLayer) = -dThermalC_dHydStateBelow * ( mLayerTempTrial(iLayer+1) - upperBoundTemp )/dz dFlux_dTempBelow(iLayer) = -dThermalC_dNrgStateBelow * ( mLayerTempTrial(iLayer+1) - upperBoundTemp )/dz - iLayerThermalC(iLayer)/dz @@ -595,23 +600,22 @@ subroutine ssdNrgFlux(& ! ***** compute the conductive fluxes at layer interfaces ***** ! Compute flux after the derivatives, because need iLayerThermal as calculated above ! ------------------------------------------------------------------------------------------------------------------------- - do iLayer=ixTop,ixBot ! (loop through model layers) + do iLayer0=ixTop-1,ixBot + if(ixTop-1/=0 .and. iLayer==ixTop-1)then + iLayer = 0 !need to always do layer 0 + else + iLayer = iLayer0 + endif if(iLayer==0)then ! (upper boundary fluxes -- positive downwards) - ! flux depends on the type of upper boundary condition - select case(ix_bcUpprTdyn) ! (identify the upper boundary condition for thermodynamics - case(prescribedTemp); iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*( mLayerTempTrial(iLayer+1) - upperBoundTemp )/ & - (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) - case(zeroFlux); iLayerConductiveFlux(iLayer) = 0._rkind - case(energyFlux); iLayerConductiveFlux(iLayer) = groundNetFlux !from vegNrgFlux module - end select ! (identifying the lower boundary condition for thermodynamics) + iLayerConductiveFlux(iLayer) = groundNetFlux !from vegNrgFlux module else if(iLayer==nLayers)then ! (lower boundary fluxes -- positive downwards) ! flux depends on the type of lower boundary condition - select case(ix_bcLowrTdyn) ! (identify the lower boundary condition for thermodynamics - case(prescribedTemp); iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*(lowerBoundTemp - mLayerTempTrial(iLayer))/(mLayerDepth(iLayer)*0.5_rkind) - case(zeroFlux); iLayerConductiveFlux(iLayer) = 0._rkind - end select ! (identifying the lower boundary condition for thermodynamics) + select case(ix_bcLowrTdyn) ! (identify the lower boundary condition for thermodynamics + case(prescribedTemp); iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*(lowerBoundTemp - mLayerTempTrial(iLayer))/(mLayerDepth(iLayer)*0.5_rkind) + case(zeroFlux); iLayerConductiveFlux(iLayer) = 0._rkind + end select ! (identifying the lower boundary condition for thermodynamics) else ! (domain boundary fluxes -- positive downwards) iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) / & @@ -624,7 +628,12 @@ subroutine ssdNrgFlux(& ! ------------------------------------------------------------------------------------------------------------------------- ! ***** compute the advective fluxes at layer interfaces ***** ! ------------------------------------------------------------------------------------------------------------------------- - do iLayer=ixTop,ixBot !(loop through model layers) + do iLayer0=ixTop-1,ixBot + if(ixTop-1/=0 .and. iLayer==ixTop-1)then + iLayer = 0 !need to always do layer 0 + else + iLayer = iLayer0 + endif if (iLayer==0) then iLayerAdvectiveFlux(iLayer) = realMissing !advective flux at the upper boundary is included in the ground heat flux @@ -648,6 +657,7 @@ subroutine ssdNrgFlux(& ! ***** compute the total fluxes at layer interfaces ***** ! ------------------------------------------------------------------------------------------------------------------------- ! NOTE: ignore advective fluxes for now + iLayerNrgFlux(0) = iLayerConductiveFlux(0) iLayerNrgFlux(ixTop:ixBot) = iLayerConductiveFlux(ixTop:ixBot) ! end association of local variables with information in the data structures From 1d05590fa20cb0dd52b5362d677c0c2eafd5d106 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 10 Nov 2022 15:47:30 +0900 Subject: [PATCH 0433/1472] need to go off diag_data not named variable in opSplittin --- build/source/engine/opSplittin.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 0317822d5..1543b589c 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -953,8 +953,8 @@ subroutine opSplittin(& ! compute the melt in each snow and soil layer if(nSnow>0)& - mLayerMeltFreeze( 1:nSnow ) = -(mLayerVolFracIce( 1:nSnow ) - mLayerVolFracIceInit( 1:nSnow ))*iden_ice - mLayerMeltFreeze(nSnow+1:nLayers) = -(mLayerVolFracIce(nSnow+1:nLayers) - mLayerVolFracIceInit(nSnow+1:nLayers))*iden_water + diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(1:nSnow) = -( mLayerVolFracIce(1:nSnow) - mLayerVolFracIceInit(1:nSnow) ) * iden_ice + diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(nSnow+1:nLayers) = -(mLayerVolFracIce(nSnow+1:nLayers) - mLayerVolFracIceInit(nSnow+1:nLayers))*iden_water ! end associate statements end associate globalVars From b1417fe3c41e84fd23bd1f7eaee9a401d4ce700f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 16 Nov 2022 12:30:32 +0900 Subject: [PATCH 0434/1472] turn off enthalpy calculation in with BE flag to match old summa BE --- build/source/driver/summa_setup.f90 | 20 +++++++++++--------- build/source/engine/eval8summa.f90 | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index 6d5204511..00cd7ae23 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -129,10 +129,10 @@ subroutine summa_paramSetup(summa1_struc, err, message) ! basin-average structures bparStruct => summa1_struc%bparStruct , & ! x%gru(:)%var(:) -- basin-average parameters bvarStruct => summa1_struc%bvarStruct , & ! x%gru(:)%var(:)%dat -- basin-average variables - + ! lookup table structure lookupStruct => summa1_struc%lookupStruct , & ! x%gru(:)%hru(:)%z(:)%var(:)%lookup -- lookup-tables - + ! miscellaneous variables upArea => summa1_struc%upArea , & ! area upslope of each HRU nGRU => summa1_struc%nGRU , & ! number of grouped response units @@ -292,13 +292,15 @@ subroutine summa_paramSetup(summa1_struc, err, message) call E2T_lookup(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! calculate a lookup table to compute enthalpy from temperature - call T2E_lookup(gru_struc(iGRU)%hruInfo(iHRU)%nSoil, & ! intent(in): number of soil layers - mparStruct%gru(iGRU)%hru(iHRU), & ! intent(in): parameter data structure - lookupStruct%gru(iGRU)%hru(iHRU), & ! intent(inout): lookup table data structure - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - + ! calculate a lookup table to compute enthalpy from temperature, only for sundials + if(model_decisions(iLookDECISIONS%num_method)%iDecision == sundials)then + call T2E_lookup(gru_struc(iGRU)%hruInfo(iHRU)%nSoil, & ! intent(in): number of soil layers + mparStruct%gru(iGRU)%hru(iHRU), & ! intent(in): parameter data structure + lookupStruct%gru(iGRU)%hru(iHRU), & ! intent(inout): lookup table data structure + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + endif + ! overwrite the vegetation height HVT(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex)) = mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%heightCanopyTop)%dat(1) HVB(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex)) = mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%heightCanopyBottom)%dat(1) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 019d6e9c2..64ef563b8 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -210,7 +210,7 @@ subroutine eval8summa(& real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) ! diagnostic variables - logical(lgt),parameter :: needEnthalpy=.true. ! flag to compute enthalpy + logical(lgt),parameter :: needEnthalpy=.false. ! flag to compute enthalpy, turn off to match Summa BE real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) From 06bed7738f262fe606bcfb763a2d5192267abb9d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 16 Nov 2022 14:58:14 +0900 Subject: [PATCH 0435/1472] need to add decision lookup value --- build/source/driver/summa_setup.f90 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index 00cd7ae23..7f8365e1b 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -39,6 +39,11 @@ module summa_setup ! metadata structures USE globalData,only:mpar_meta,bpar_meta ! parameter metadata structures +! look-up values for the numerical method +USE mDecisions_module,only: & + bEuler, & ! home-grown backward Euler solution with long time steps + sundials ! SUNDIALS/IDA solution + ! named variables to define the decisions for snow layers USE mDecisions_module,only:& sameRulesAllLayers, & ! SNTHERM option: same combination/sub-dividion rules applied to all layers From 35e70f0c1d8099ab51523a81a0f82e3d59437133 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 19 Nov 2022 00:00:39 +0900 Subject: [PATCH 0436/1472] remove comment --- build/source/engine/ssdNrgFlux.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index 3c6b1785f..b03103560 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -293,7 +293,7 @@ subroutine ssdNrgFlux(& ixTop = ixLayerDesired(1) ixBot = ixLayerDesired(1) else - ixTop = 1 !include layer 0 in layer interface derivatives + ixTop = 1 ixBot = nLayers endif From 131e5c2b3634f4d27e53c4b26d1ad15fb6d66e4b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 19 Nov 2022 00:09:03 +0900 Subject: [PATCH 0437/1472] missing Jacobian terms in master --- build/source/engine/computJacob.f90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 48f9e3fbf..d2f6c4bae 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -528,8 +528,8 @@ subroutine computJacob(& ! ---------------------------------------- if(ixAqWat/=integerMissing) then aJac(ixDiag,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) - !if(ixSoilOnlyNrg(nSoil)/=integerMissing) aJac(ixOffDiag(ixAqWat,ixSoilOnlyNrg(nSoil)),ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk - !if(ixSoilOnlyHyd(nSoil)/=integerMissing) aJac(ixOffDiag(ixAqWat,ixSoilOnlyHyd(nSoil)),ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat + if(ixSoilOnlyNrg(nSoil)/=integerMissing) aJac(ixOffDiag(ixAqWat,ixSoilOnlyNrg(nSoil)),ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk + if(ixSoilOnlyHyd(nSoil)/=integerMissing) aJac(ixOffDiag(ixAqWat,ixSoilOnlyHyd(nSoil)),ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat ! - only include banded derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration), would have to have few soil layers if(computeVegFlux)then if(ixCasNrg/=integerMissing)then @@ -882,8 +882,8 @@ subroutine computJacob(& ! ---------------------------------------- if(ixAqWat/=integerMissing) then aJac(ixAqWat,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) - !if(ixSoilOnlyNrg(nSoil)/=integerMissing) aJac(ixAqWat,ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk - !if(ixSoilOnlyHyd(nSoil)/=integerMissing) aJac(ixAqWat,ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat + if(ixSoilOnlyNrg(nSoil)/=integerMissing) aJac(ixAqWat,ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk + if(ixSoilOnlyHyd(nSoil)/=integerMissing) aJac(ixAqWat,ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) if(computeVegFlux)then if(ixCasNrg/=integerMissing) aJac(ixAqWat,ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) From f2c7ca60a3bb99db03eac96645c4b12a64e8178c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 23 Nov 2022 16:54:47 +0900 Subject: [PATCH 0438/1472] iLayer should be iLayer0 in some places in ssdNrgFlux, I think this was the large bug --- build/source/engine/ssdNrgFlux.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index b03103560..81f3da208 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -312,7 +312,7 @@ subroutine ssdNrgFlux(& ! loop through INTERFACES... do iLayer0=ixTop-1,ixBot - if(ixTop-1/=0 .and. iLayer==ixTop-1)then + if(ixTop-1/=0 .and. iLayer0==ixTop-1)then iLayer = 0 !need to always do layer 0 else iLayer = iLayer0 @@ -601,7 +601,7 @@ subroutine ssdNrgFlux(& ! Compute flux after the derivatives, because need iLayerThermal as calculated above ! ------------------------------------------------------------------------------------------------------------------------- do iLayer0=ixTop-1,ixBot - if(ixTop-1/=0 .and. iLayer==ixTop-1)then + if(ixTop-1/=0 .and. iLayer0==ixTop-1)then iLayer = 0 !need to always do layer 0 else iLayer = iLayer0 @@ -629,7 +629,7 @@ subroutine ssdNrgFlux(& ! ***** compute the advective fluxes at layer interfaces ***** ! ------------------------------------------------------------------------------------------------------------------------- do iLayer0=ixTop-1,ixBot - if(ixTop-1/=0 .and. iLayer==ixTop-1)then + if(ixTop-1/=0 .and. iLayer0==ixTop-1)then iLayer = 0 !need to always do layer 0 else iLayer = iLayer0 From bf46a0c773c9911cf3d4a4787588c5499ac7fd52 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 30 Nov 2022 12:56:00 +0900 Subject: [PATCH 0439/1472] Move heat capacity derivatives from updateVars into computHeatCap, and thermal conductivity derivatives from ssdNrgFlux into computThermConduct. Now, only use these if turn on flag updateCp= .true. flag in eval8summa eva8summaSundials. Otherwise these derivatives are 0 inside diagn_evar. Old way was effectively updateCp= .false. for eval8summa, and it should probably always be true since it makes a big difference. Note, enthalpy derivatives for howHeatCp = enthalpyFD are not yet correct (and there are other issues with this choice, use howHeatCp = closedForm for now). --- build/my_makefile_cop | 10 +- build/my_makefile_gra | 10 +- build/my_makefile_mac | 10 +- build/my_makefile_ric | 10 +- build/source/dshare/get_ixname.f90 | 16 +- build/source/dshare/popMetadat.f90 | 16 +- build/source/dshare/var_lookup.f90 | 21 +- build/source/engine/computFlux.f90 | 7 - build/source/engine/computHeatCap.f90 | 305 +++++-- build/source/engine/computJacob.f90 | 10 +- build/source/engine/computJacobSundials.f90 | 10 +- build/source/engine/computThermConduct.f90 | 220 +++-- build/source/engine/diagn_evar.f90 | 26 +- build/source/engine/eval8summa.f90 | 223 ++++- build/source/engine/eval8summaSundials.f90 | 104 ++- build/source/engine/ssdNrgFlux.f90 | 893 +++----------------- build/source/engine/summaSolve.f90 | 6 +- build/source/engine/systemSolv.f90 | 4 +- build/source/engine/updateVars.f90 | 49 +- build/source/engine/updateVarsSundials.f90 | 57 +- 20 files changed, 859 insertions(+), 1148 deletions(-) diff --git a/build/my_makefile_cop b/build/my_makefile_cop index 261eecff4..39bfb4c47 100644 --- a/build/my_makefile_cop +++ b/build/my_makefile_cop @@ -217,16 +217,16 @@ SUMMA_SOLVER= \ soilLiqFlx.f90 \ bigAquifer.f90 \ computFlux.f90 \ - computResid.f90 \ - computJacob.f90 \ - eval8summa.f90 \ - summaSolve.f90 \ - systemSolv.f90 \ type4IDA.f90 \ tol4IDA.f90 \ computEnthalpy.f90 \ computHeatCap.f90 \ computThermConduct.f90 \ + computResid.f90 \ + computJacob.f90 \ + eval8summa.f90 \ + summaSolve.f90 \ + systemSolv.f90 \ computResidSundials.f90 \ eval8summaSundials.f90 \ computJacobSundials.f90 \ diff --git a/build/my_makefile_gra b/build/my_makefile_gra index 3177fffd8..03389b411 100644 --- a/build/my_makefile_gra +++ b/build/my_makefile_gra @@ -217,16 +217,16 @@ SUMMA_SOLVER= \ soilLiqFlx.f90 \ bigAquifer.f90 \ computFlux.f90 \ - computResid.f90 \ - computJacob.f90 \ - eval8summa.f90 \ - summaSolve.f90 \ - systemSolv.f90 \ type4IDA.f90 \ tol4IDA.f90 \ computEnthalpy.f90 \ computHeatCap.f90 \ computThermConduct.f90 \ + computResid.f90 \ + computJacob.f90 \ + eval8summa.f90 \ + summaSolve.f90 \ + systemSolv.f90 \ computResidSundials.f90 \ eval8summaSundials.f90 \ computJacobSundials.f90 \ diff --git a/build/my_makefile_mac b/build/my_makefile_mac index 8f62bd55c..bd8965786 100644 --- a/build/my_makefile_mac +++ b/build/my_makefile_mac @@ -217,16 +217,16 @@ SUMMA_SOLVER= \ soilLiqFlx.f90 \ bigAquifer.f90 \ computFlux.f90 \ - computResid.f90 \ - computJacob.f90 \ - eval8summa.f90 \ - summaSolve.f90 \ - systemSolv.f90 \ type4IDA.f90 \ tol4IDA.f90 \ computEnthalpy.f90 \ computHeatCap.f90 \ computThermConduct.f90 \ + computResid.f90 \ + computJacob.f90 \ + eval8summa.f90 \ + summaSolve.f90 \ + systemSolv.f90 \ computResidSundials.f90 \ eval8summaSundials.f90 \ computJacobSundials.f90 \ diff --git a/build/my_makefile_ric b/build/my_makefile_ric index 7c2b5b7e7..5cd15287b 100644 --- a/build/my_makefile_ric +++ b/build/my_makefile_ric @@ -217,16 +217,16 @@ SUMMA_SOLVER= \ soilLiqFlx.f90 \ bigAquifer.f90 \ computFlux.f90 \ - computResid.f90 \ - computJacob.f90 \ - eval8summa.f90 \ - summaSolve.f90 \ - systemSolv.f90 \ type4IDA.f90 \ tol4IDA.f90 \ computEnthalpy.f90 \ computHeatCap.f90 \ computThermConduct.f90 \ + computResid.f90 \ + computJacob.f90 \ + eval8summa.f90 \ + summaSolve.f90 \ + systemSolv.f90 \ computResidSundials.f90 \ eval8summaSundials.f90 \ computJacobSundials.f90 \ diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 704318558..482300649 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -490,6 +490,16 @@ function get_ixdiag(varName) case('scalarLambda_wetsoil' ); get_ixdiag = iLookDIAG%scalarLambda_wetsoil ! thermal conductivity of wet soil (W m-1) case('mLayerThermalC' ); get_ixdiag = iLookDIAG%mLayerThermalC ! thermal conductivity at the mid-point of each layer (W m-1 K-1) case('iLayerThermalC' ); get_ixdiag = iLookDIAG%iLayerThermalC ! thermal conductivity at the interface of each layer (W m-1 K-1) + ! energy derivatives that might be treated as constant if heat capacity and thermal conductivity not updated + case('dVolHtCapBulk_dPsi0' ); get_ixdiag = iLookDIAG%dVolHtCapBulk_dPsi0 ! derivative in bulk heat capacity w.r.t. matric potential + case('dVolHtCapBulk_dTheta' ); get_ixdiag = iLookDIAG%dVolHtCapBulk_dTheta ! derivative in bulk heat capacity w.r.t. volumetric water content + case('dVolHtCapBulk_dCanWat' ); get_ixdiag = iLookDIAG%dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. volumetric water content + case('dVolHtCapBulk_dTk' ); get_ixdiag = iLookDIAG%dVolHtCapBulk_dTk ! derivative in bulk heat capacity w.r.t. temperature + case('dVolHtCapBulk_dTkCanopy' ); get_ixdiag = iLookDIAG%dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature + case('dThermalC_dTempAbove' ); get_ixdiag = iLookDIAG%dThermalC_dTempAbove ! derivative in the thermal conductivity w.r.t. energy state in the layer above + case('dThermalC_dTempBelow' ); get_ixdiag = iLookDIAG%dThermalC_dTempBelow ! derivative in the thermal conductivity w.r.t. energy state in the layer above + case('dThermalC_dWatAbove' ); get_ixdiag = iLookDIAG%dThermalC_dWatAbove ! derivative in the thermal conductivity w.r.t. water state in the layer above + case('dThermalC_dWatBelow' ); get_ixdiag = iLookDIAG%dThermalC_dWatBelow ! derivative in the thermal conductivity w.r.t. water state in the layer above ! enthalpy case('scalarCanairEnthalpy' ); get_ixdiag = iLookDIAG%scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) case('scalarCanopyEnthalpy' ); get_ixdiag = iLookDIAG%scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) @@ -776,12 +786,6 @@ function get_ixderiv(varName) case('dFracLiqSnow_dTk' ); get_ixderiv = iLookDERIV%dFracLiqSnow_dTk ! derivative in fraction of liquid snow w.r.t. temperature case('mLayerdTheta_dTk' ); get_ixderiv = iLookDERIV%mLayerdTheta_dTk ! derivative of volumetric liquid water content w.r.t. temperature (K-1) case('mLayerd2Theta_dTk2' ); get_ixderiv = iLookDERIV%mLayerd2Theta_dTk2 ! second derivative of volumetric liquid water content w.r.t. temperature - ! derivate in bulk heat capacity w.r.t. relevant state variables - case('dVolHtCapBulk_dPsi0' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dPsi0 ! derivative in bulk heat capacity w.r.t. matric potential - case('dVolHtCapBulk_dTheta' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTheta ! derivative in bulk heat capacity w.r.t. volumetric water content - case('dVolHtCapBulk_dCanWat' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. volumetric water content - case('dVolHtCapBulk_dTk' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTk ! derivative in bulk heat capacity w.r.t. temperature - case('dVolHtCapBulk_dTkCanopy' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature ! derivatives in time case( 'mLayerdTemp_dt' ); get_ixderiv = iLookDERIV%mLayerdTemp_dt ! timestep change in layer temperature case( 'scalarCanopydTemp_dt' ); get_ixderiv = iLookDERIV%scalarCanopydTemp_dt ! timestep change in canopy temperature diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 309887407..ca684c256 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -353,6 +353,16 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%scalarLambda_wetsoil) = var_info('scalarLambda_wetsoil' , 'thermal conductivity of wet soil' , 'W m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%mLayerThermalC) = var_info('mLayerThermalC' , 'thermal conductivity at the mid-point of each layer' , 'W m-1 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%iLayerThermalC) = var_info('iLayerThermalC' , 'thermal conductivity at the interface of each layer' , 'W m-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + ! energy derivatives that might be treated as constant if heat capacity and thermal conductivity not updated + diag_meta(iLookDIAG%dVolHtCapBulk_dPsi0) = var_info('dVolHtCapBulk_dPsi0' , 'derivative in bulk heat capacity w.r.t. matric potential' , 'J m-4 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%dVolHtCapBulk_dTheta) = var_info('dVolHtCapBulk_dTheta' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%dVolHtCapBulk_dCanWat) = var_info('dVolHtCapBulk_dCanWat' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%dVolHtCapBulk_dTk) = var_info('dVolHtCapBulk_dTk' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%dVolHtCapBulk_dTkCanopy) = var_info('dVolHtCapBulk_dTkCanopy' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%dThermalC_dTempAbove) = var_info('dThermalC_dTempAbove' , 'derivative in the thermal conductivity w.r.t. energy in the layer above','J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%dThermalC_dTempBelow) = var_info('dThermalC_dTempBelow' , 'derivative in the thermal conductivity w.r.t. energy in the layer above','J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%dThermalC_dWatAbove) = var_info('dThermalC_dWatAbove' , 'derivative in the thermal conductivity w.r.t. water in the layer above', 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%dThermalC_dWatBelow) = var_info('dThermalC_dWatBelow' , 'derivative in the thermal conductivity w.r.t. water in the layer above', 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) ! enthalpy diag_meta(iLookDIAG%scalarCanairEnthalpy) = var_info('scalarCanairEnthalpy' , 'enthalpy of the canopy air space' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarCanopyEnthalpy) = var_info('scalarCanopyEnthalpy' , 'enthalpy of the vegetation canopy' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) @@ -611,12 +621,6 @@ subroutine popMetadat(err,message) deriv_meta(iLookDERIV%dFracLiqSnow_dTk) = var_info('dFracLiqSnow_dTk' , 'derivative in fraction of liquid snow w.r.t. temperature' , 'K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%mLayerdTheta_dTk) = var_info('mLayerdTheta_dTk' , 'derivative of volumetric liquid water content w.r.t. temperature' , 'K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%mLayerd2Theta_dTk2) = var_info('mLayerd2Theta_dTk2' , 'second derivative of volumetric liquid water content w.r.t. temperature', 'K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - ! derivate in bulk heat capacity w.r.t. relevant state variables - deriv_meta(iLookDERIV%dVolHtCapBulk_dPsi0) = var_info('dVolHtCapBulk_dPsi0' , 'derivative in bulk heat capacity w.r.t. matric potential' , 'J m-4 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dTheta) = var_info('dVolHtCapBulk_dTheta' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dCanWat) = var_info('dVolHtCapBulk_dCanWat' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dTk) = var_info('dVolHtCapBulk_dTk' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dTkCanopy) = var_info('dVolHtCapBulk_dTkCanopy' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! derivatives in time deriv_meta(iLookDERIV%mLayerdTemp_dt) = var_info('mLayerdTemp_dt' , 'timestep change in layer temperature' , 'K' ,get_ixVarType('midToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%scalarCanopydTemp_dt) = var_info('scalarCanopydTemp_dt' , 'timestep change in canopy temperature' , 'K' ,get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index be96c7212..7a31ebd2f 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -367,6 +367,16 @@ MODULE var_lookup integer(i4b) :: scalarLambda_wetsoil = integerMissing ! thermal conductivity of wet soil (W m-1 K-1) integer(i4b) :: mLayerThermalC = integerMissing ! thermal conductivity at the mid-point of each layer (W m-1 K-1) integer(i4b) :: iLayerThermalC = integerMissing ! thermal conductivity at the interface of each layer (W m-1 K-1) + ! energy derivatives that might be treated as constant if heat capacity and thermal conductivity not updated + integer(i4b) :: dVolHtCapBulk_dPsi0 = integerMissing ! derivative in bulk heat capacity w.r.t. matric potential + integer(i4b) :: dVolHtCapBulk_dTheta = integerMissing ! derivative in bulk heat capacity w.r.t. volumetric water content + integer(i4b) :: dVolHtCapBulk_dCanWat = integerMissing ! derivative in bulk heat capacity w.r.t. volumetric water content + integer(i4b) :: dVolHtCapBulk_dTk = integerMissing ! derivative in bulk heat capacity w.r.t. temperature + integer(i4b) :: dVolHtCapBulk_dTkCanopy = integerMissing ! derivative in bulk heat capacity w.r.t. temperature + integer(i4b) :: dThermalC_dTempAbove = integerMissing ! derivative in the thermal conductivity w.r.t. energy state in the layer above + integer(i4b) :: dThermalC_dTempBelow = integerMissing ! derivative in the thermal conductivity w.r.t. energy state in the layer above + integer(i4b) :: dThermalC_dWatAbove = integerMissing ! derivative in the thermal conductivity w.r.t. water state in the layer above + integer(i4b) :: dThermalC_dWatBelow = integerMissing ! derivative in the thermal conductivity w.r.t. water state in the layer above ! enthalpy integer(i4b) :: scalarCanairEnthalpy = integerMissing ! enthalpy of the canopy air space (J m-3) integer(i4b) :: scalarCanopyEnthalpy = integerMissing ! enthalpy of the vegetation canopy (J m-3) @@ -629,12 +639,6 @@ MODULE var_lookup integer(i4b) :: dFracLiqSnow_dTk = integerMissing ! derivative in fraction of liquid snow w.r.t. temperature integer(i4b) :: mLayerdTheta_dTk = integerMissing ! derivative of volumetric liquid water content w.r.t. temperature (K-1) integer(i4b) :: mLayerd2Theta_dTk2 = integerMissing ! second derivative of volumetric liquid water content w.r.t. temperature - ! derivate in bulk heat capacity w.r.t. relevant state variables - integer(i4b) :: dVolHtCapBulk_dPsi0 = integerMissing ! derivative in bulk heat capacity w.r.t. matric potential - integer(i4b) :: dVolHtCapBulk_dTheta = integerMissing ! derivative in bulk heat capacity w.r.t. volumetric water content - integer(i4b) :: dVolHtCapBulk_dCanWat = integerMissing ! derivative in bulk heat capacity w.r.t. volumetric water content - integer(i4b) :: dVolHtCapBulk_dTk = integerMissing ! derivative in bulk heat capacity w.r.t. temperature - integer(i4b) :: dVolHtCapBulk_dTkCanopy = integerMissing ! derivative in bulk heat capacity w.r.t. temperature ! derivatives in time integer(i4b) :: mLayerdTemp_dt = integerMissing ! timestep change in layer temperature integer(i4b) :: scalarCanopydTemp_dt = integerMissing ! timestep change in canopy temperature @@ -862,7 +866,8 @@ MODULE var_lookup 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,& 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,& 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,& - 81, 82, 83, 84, 85, 86, 87) + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,& + 91, 92, 93, 94, 95, 96) ! named variables: model fluxes type(iLook_flux), public,parameter :: iLookFLUX =iLook_flux ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& @@ -881,7 +886,7 @@ MODULE var_lookup 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,& 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,& 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,& - 61, 62, 63, 64, 65, 66, 67) + 61, 62) ! named variables: model indices type(iLook_index), public,parameter :: iLookINDEX =ilook_index ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 5687a8ad9..04245d10a 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -529,7 +529,6 @@ subroutine computFlux(& call ssdNrgFlux(& ! input: model control (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution - .true., & ! intent(in): flag indicating if derivatives are desired ! input: fluxes and derivatives at the upper boundary scalarGroundNetNrgFlux, & ! intent(in): total flux at the ground surface (W m-2) dGroundNetFlux_dGroundTemp, & ! intent(in): derivative in total ground surface flux w.r.t. ground temperature (W m-2 K-1) @@ -538,12 +537,6 @@ subroutine computFlux(& iLayerLiqFluxSoil, & ! intent(in): liquid flux at the interface of each soil layer (m s-1) ! input: trial value of model state variables mLayerTempTrial, & ! intent(in): trial temperature at the current iteration (K) - mLayerMatricHeadTrial, & ! intent(in): trial value for the total water matric potential in each soil layer (m) - mLayerVolFracLiqTrial, & ! intent(in): trial volumetric fraction of liquid water at the current iteration(-) - mLayerVolFracIceTrial, & ! intent(in): trial volumetric fraction of ice water at the current iteration(-) - ! input: pre-computed derivatives - mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) ! input-output: data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model indices diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index f6d9f8ad5..be2b0f281 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -34,7 +34,7 @@ module computHeatCap_module ! physical constants USE multiconst,only:& - Tfreeze, & ! freezing point of water (K) + Tfreeze, & ! freezing point of water (K) iden_air, & ! intrinsic density of air (kg m-3) iden_ice, & ! intrinsic density of ice (kg m-3) iden_water, & ! intrinsic density of water (kg m-3) @@ -57,6 +57,7 @@ module computHeatCap_module ! missing values USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing real ! named variables that define the layer type USE globalData,only:iname_snow ! snow @@ -79,64 +80,83 @@ module computHeatCap_module ! ********************************************************************************************************** subroutine computHeatCap(& ! input: control variables - nLayers, & ! intent(in): number of layers (soil+snow) - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) - ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - diag_data, & ! intent(in): model diagnostic variables for a local HRU + nLayers, & ! intent(in): number of layers (soil+snow) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) + ! input output data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! input: state variables - scalarCanopyIce, & ! intent(in) - scalarCanopyLiquid, & ! intent(in) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) - scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial temperature - mLayerTempPrev, & ! intent(in): previous temperature - mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil - mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil + scalarCanopyIce, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiquid, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial temperature + mLayerTempPrev, & ! intent(in): previous temperature + mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil + mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil + mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) + ! input: pre-computed derivatives + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output - heatCapVeg, & - mLayerHeatCap, & ! intent(out): heat capacity for snow and soil + heatCapVeg, & + mLayerHeatCap, & ! intent(out): heat capacity for snow and soil ! output: error control - err,message) ! intent(out): error control + err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------------- + ! provide access to external subroutines + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + ! -------------------------------------------------------------------------------------------------------------------------------- ! input: control variables logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) + ! input: pre-computed derivatives + real(rkind),intent(in) :: dTheta_dTkCanopy ! derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: scalarFracLiqVeg ! fraction of canopy liquid water (-) + real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative of volumetric liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) + real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) ! input/output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model layer indices ! input: integer(i4b),intent(in) :: nLayers - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) real(rkind),intent(in) :: scalarCanopyLiquid - real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature + real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature real(rkind),intent(in) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) real(rkind),intent(in) :: scalarCanopyEnthalpyPrev ! intent(in): previous enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(in) :: scalarCanopyTempPrev ! Previous value of canopy temperature - real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) - real(rkind),intent(in) :: mLayerTempTrial(:) - real(rkind),intent(in) :: mLayerTempPrev(:) - real(rkind),intent(in) :: mLayerEnthalpyTrial(:) - real(rkind),intent(in) :: mLayerEnthalpyPrev(:) + real(rkind),intent(in) :: scalarCanopyTempPrev ! Previous value of canopy temperature + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) + real(rkind),intent(in) :: mLayerTempTrial(:) ! trial temperature + real(rkind),intent(in) :: mLayerTempPrev(:) ! previous temperature + real(rkind),intent(in) :: mLayerEnthalpyTrial(:) ! trial enthalpy for snow and soil + real(rkind),intent(in) :: mLayerEnthalpyPrev(:) ! previous enthalpy for snow and soil + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! vector of total water matric potential (m) ! output: real(qp),intent(out) :: heatCapVeg - real(qp),intent(out) :: mLayerHeatCap(:) + real(qp),intent(out) :: mLayerHeatCap(:) ! heat capacity for snow and soil integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: iSoil ! index of soil layer real(rkind) :: delT real(rkind) :: delEnt - integer(i4b) :: iSoil ! index of soil layer + real(rkind) :: fLiq ! fraction of liquid water + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) ! -------------------------------------------------------------------------------------------------------------------------------- ! associate variables in data structure associate(& @@ -148,26 +168,45 @@ subroutine computHeatCap(& maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) ! input: depth varying soil parameters iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): soil porosity (-) + ! output: derivatives + dVolHtCapBulk_dPsi0 => diag_data%var(iLookDIAG%dVolHtCapBulk_dPsi0)%dat, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => diag_data%var(iLookDIAG%dVolHtCapBulk_dTheta)%dat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => diag_data%var(iLookDIAG%dVolHtCapBulk_dCanWat)%dat(1), & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => diag_data%var(iLookDIAG%dVolHtCapBulk_dTk)%dat, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => diag_data%var(iLookDIAG%dVolHtCapBulk_dTkCanopy)%dat(1) & ! intent(out): derivative in bulk heat capacity w.r.t. temperature ) ! end associate statemen ! initialize error control err=0; message="computHeatCap/" - + ! initialize the soil layer iSoil=integerMissing ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) if(computeVegFlux)then delT = scalarCanopyTempTrial - scalarCanopyTempPrev - if(abs(delT) <= 1e-14_rkind)then - heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component - Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component - Cp_ice*scalarCanopyIce/canopyDepth ! ice component - else + if(abs(delT) <= 1e-14_rkind)then + heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component + Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component + Cp_ice*scalarCanopyIce/canopyDepth ! ice component + + !derivatives + fLiq = scalarFracLiqVeg + dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) + if(scalarCanopyTempTrial < Tfreeze)then + dVolHtCapBulk_dTkCanopy = iden_water * (-Cp_ice + Cp_water) * dTheta_dTkCanopy ! no derivative in air + else + dVolHtCapBulk_dTkCanopy = 0._rkind + endif + + else delEnt = scalarCanopyEnthalpyTrial - scalarCanopyEnthalpyPrev heatCapVeg = delEnt / delT - end if - end if + ! NEED TO COMPUTE THESE STILL + dVolHtCapBulk_dCanWat = 0._rkind ! dCanEnthalpy_dWat / delT + dVolHtCapBulk_dTkCanopy = 0._rkind ! ( dCanEnthalpy_dTk - delEnt/delT ) / delT + endif + endif ! loop through layers do iLayer=1,nLayers @@ -178,26 +217,55 @@ subroutine computHeatCap(& select case(layerType(iLayer)) ! * soil case(iname_soil) - mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component - iden_ice * Cp_Ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component + mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component + iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component + !derivatives + dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use + Tcrit = crit_soilT( mLayerMatricHeadTrial(iSoil) ) + if( mLayerTempTrial(iLayer) < Tcrit)then + dVolHtCapBulk_dPsi0(iSoil) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(iSoil) + dVolHtCapBulk_dTk(iLayer) = (-iden_ice * Cp_ice + iden_water * Cp_water) * mLayerdTheta_dTk(iLayer) + else + dVolHtCapBulk_dPsi0(iSoil) = (iden_water*Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(iSoil) + dVolHtCapBulk_dTk(iLayer) = 0._rkind + endif + case(iname_snow) - mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return + mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component + !derivatives + fLiq = mLayerFracLiqSnow(iLayer) + dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air + if( mLayerTempTrial(iLayer) < Tfreeze)then + dVolHtCapBulk_dTk(iLayer) = ( iden_water * (-Cp_ice + Cp_water) + iden_air * (iden_water/iden_ice - 1._rkind) * Cp_air ) * mLayerdTheta_dTk(iLayer) + else + dVolHtCapBulk_dTk(iLayer) = 0._rkind + endif + + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return end select else delEnt = mLayerEnthalpyTrial(iLayer) - mLayerEnthalpyPrev(iLayer) mLayerHeatCap(iLayer) = delEnt / delT + ! NEED TO COMPUTE THESE STILL + if(iLayer>nSnow)then + iSoil = iLayer-nSnow + dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use + dVolHtCapBulk_dPsi0(iSoil) = 0._rkind ! dEnthalpy_dWat(iLayer) / delT + else + dVolHtCapBulk_dTheta(iLayer) = 0._rkind ! dEnthalpy_dWat(iLayer) / delT + endif + dVolHtCapBulk_dTk(iLayer) = 0._rkind ! ( dEnthalpy_dTk(iLayer) - delEnt/delT ) / delT endif end do ! looping through layers - + end associate end subroutine computHeatCap - + ! ********************************************************************************************************** ! public subroutine computStatMult: get scale factors ! ********************************************************************************************************** @@ -269,7 +337,6 @@ subroutine computStatMult(& where(ixStateType_subset==iname_watCanopy) sMul = 1._rkind ! nothing else on the left hand side where(ixStateType_subset==iname_liqCanopy) sMul = 1._rkind ! nothing else on the left hand side - ! define the energy multiplier for the state vector for residual calculations (snow-soil domain) if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) @@ -295,7 +362,7 @@ subroutine computStatMult(& end associate ! end association to variables in the data structure where vector length does not change end subroutine computStatMult - + ! ********************************************************************************************************** ! public subroutine computHeatCapAnalytic: compute diagnostic energy variables (heat capacity) ! ********************************************************************************************************** @@ -304,30 +371,54 @@ subroutine computHeatCapAnalytic(& computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux canopyDepth, & ! intent(in): canopy depth (m) ! input: state variables - scalarCanopyIce, & ! intent(in) - scalarCanopyLiquid, & ! intent(in) + scalarCanopyIce, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiquid, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyTemp, & ! intent(in): trial value of canopy temperature (K) mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices + mLayerTemp, & ! intent(in): trial value of layer temperature (K) + mLayerMatricHead, & ! intent(in): total water matric potential (m) + ! input: pre-computed derivatives + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + ! input output data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! output heatCapVeg, & mLayerHeatCap, & ! output: error control err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------------- + ! provide access to external subroutines + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: model control logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) real(rkind),intent(in) :: scalarCanopyLiquid - real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) + real(rkind),intent(in) :: scalarCanopyTemp ! value of canopy temperature (kg m-2) + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) + real(rkind),intent(in) :: mLayerTemp(:) ! vector of temperature (-) + real(rkind),intent(in) :: mLayerMatricHead(:) ! vector of total water matric potential (m) + + ! input: pre-computed derivatives + real(rkind),intent(in) :: dTheta_dTkCanopy ! derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: scalarFracLiqVeg ! fraction of canopy liquid water (-) + real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative of volumetric liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) + real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) + ! input/output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model layer indices + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU ! output: error control real(qp),intent(out) :: heatCapVeg real(qp),intent(out) :: mLayerHeatCap(:) @@ -338,6 +429,8 @@ subroutine computHeatCapAnalytic(& character(LEN=256) :: cmessage ! error message of downwind routine integer(i4b) :: iLayer ! index of model layer integer(i4b) :: iSoil ! index of soil layer + real(rkind) :: fLiq ! fraction of liquid water + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) ! -------------------------------------------------------------------------------------------------------------------------------- ! associate variables in data structure associate(& @@ -350,7 +443,13 @@ subroutine computHeatCapAnalytic(& maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) ! input: depth varying soil parameters iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): soil porosity (-) + ! output: derivatives + dVolHtCapBulk_dPsi0 => diag_data%var(iLookDIAG%dVolHtCapBulk_dPsi0)%dat, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => diag_data%var(iLookDIAG%dVolHtCapBulk_dTheta)%dat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => diag_data%var(iLookDIAG%dVolHtCapBulk_dCanWat)%dat(1), & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => diag_data%var(iLookDIAG%dVolHtCapBulk_dTk)%dat, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => diag_data%var(iLookDIAG%dVolHtCapBulk_dTkCanopy)%dat(1) & ! intent(out): derivative in bulk heat capacity w.r.t. temperature ) ! end associate statement ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -361,9 +460,18 @@ subroutine computHeatCapAnalytic(& ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) if(computeVegFlux)then - heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component - Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component - Cp_ice*scalarCanopyIce/canopyDepth ! ice component + heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component + Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component + Cp_ice*scalarCanopyIce/canopyDepth ! ice component + + !derivatives + fLiq = scalarFracLiqVeg + dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) + if(scalarCanopyTemp < Tfreeze)then + dVolHtCapBulk_dTkCanopy = iden_water * (-Cp_ice + Cp_water) * dTheta_dTkCanopy ! no derivative in air + else + dVolHtCapBulk_dTkCanopy = 0._rkind + endif end if ! loop through layers @@ -378,14 +486,35 @@ subroutine computHeatCapAnalytic(& select case(layerType(iLayer)) ! * soil case(iname_soil) - mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component - iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component + mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component + iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component + + !derivatives + dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use + Tcrit = crit_soilT( mLayerMatricHead(iSoil) ) + if( mLayerTemp(iLayer) < Tcrit)then + dVolHtCapBulk_dPsi0(iSoil) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(iSoil) + dVolHtCapBulk_dTk(iLayer) = (-iden_ice * Cp_ice + iden_water * Cp_water) * mLayerdTheta_dTk(iLayer) + else + dVolHtCapBulk_dPsi0(iSoil) = (iden_water*Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(iSoil) + dVolHtCapBulk_dTk(iLayer) = 0._rkind + endif + case(iname_snow) - mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component + mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component + !derivatives + fLiq = mLayerFracLiqSnow(iLayer) + dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air + if( mLayerTemp(iLayer) < Tfreeze)then + dVolHtCapBulk_dTk(iLayer) = ( iden_water * (-Cp_ice + Cp_water) + iden_air * (iden_water/iden_ice - 1._rkind) * Cp_air ) * mLayerdTheta_dTk(iLayer) + else + dVolHtCapBulk_dTk(iLayer) = 0._rkind + endif + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return end select @@ -395,7 +524,7 @@ subroutine computHeatCapAnalytic(& ! end association to variables in the data structure end associate end subroutine computHeatCapAnalytic - + ! ********************************************************************************************************** ! public subroutine computCm ! ********************************************************************************************************** @@ -404,7 +533,7 @@ subroutine computCm(& computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux ! input: state variables scalarCanopyTemp, & ! intent(in) - mLayerTemp, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerTemp, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) mLayerMatricHead, & ! intent(in) ! input data structures mpar_data, & ! intent(in): model parameters @@ -420,9 +549,9 @@ subroutine computCm(& ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: model control logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - real(rkind),intent(in) :: scalarCanopyTemp ! value of canopy ice content (kg m-2) - real(rkind),intent(in) :: mLayerTemp(:) ! vector of volumetric liquid water content (-) - real(rkind),intent(in) :: mLayerMatricHead(:) ! vector of total water matric potential (m) + real(rkind),intent(in) :: scalarCanopyTemp ! value of canopy temperature (kg m-2) + real(rkind),intent(in) :: mLayerTemp(:) ! vector of temperature (-) + real(rkind),intent(in) :: mLayerMatricHead(:) ! vector of total water matric potential (m) ! input/output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model layer indices @@ -436,10 +565,10 @@ subroutine computCm(& character(LEN=256) :: cmessage ! error message of downwind routine integer(i4b) :: iLayer ! index of model layer integer(i4b) :: iSoil ! index of soil layer - real(rkind) :: g1 - real(rkind) :: g2 - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - + real(rkind) :: g1 + real(rkind) :: g2 + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + ! -------------------------------------------------------------------------------------------------------------------------------- ! associate variables in data structure associate(& @@ -457,15 +586,15 @@ subroutine computCm(& iSoil=integerMissing ! compute Cm of vegetation - ! Note that scalarCanopyCm/iden_water is computed + ! Note that scalarCanopyCm/iden_water is computed if(computeVegFlux)then g2 = scalarCanopyTemp - Tfreeze g1 = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * g2) if(scalarCanopyTemp < Tfreeze)then - scalarCanopyCm = Cp_water * g1 + Cp_ice * (g2 - g1) + scalarCanopyCm = Cp_water * g1 + Cp_ice * (g2 - g1) else scalarCanopyCm = Cp_water * g2 - end if + end if end if ! loop through layers @@ -475,7 +604,7 @@ subroutine computCm(& if(iLayer>nSnow) iSoil = iLayer-nSnow ! ***** - ! * compute Cm of of each layer + ! * compute Cm of of each layer ! ******************************************************************* select case(layerType(iLayer)) ! * soil @@ -487,13 +616,13 @@ subroutine computCm(& else mLayerCm(iLayer) = (iden_water * Cp_water - iden_air * Cp_air) * g2 end if - + case(iname_snow) g2 = mLayerTemp(iLayer) - Tfreeze g1 = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * g2) mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( g2 - g1 ) & + (iden_water * Cp_water - iden_air * Cp_air) * g1 - + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute Cm'; return end select diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index d2f6c4bae..9673982cf 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -234,11 +234,11 @@ subroutine computJacob(& ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk )%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature ! derivative in bulk heat capacity w.r.t. relevant state variables - dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dPsi0 => diag_data%var(iLookDIAG%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => diag_data%var(iLookDIAG%dVolHtCapBulk_dTheta )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => diag_data%var(iLookDIAG%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => diag_data%var(iLookDIAG%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => diag_data%var(iLookDIAG%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. temperature ! derivatives in time mLayerdTemp_dt => deriv_data%var(iLookDERIV%mLayerdTemp_dt )%dat ,& ! intent(in): [dp(:)] timestep change in layer temperature scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt )%dat(1) ,& ! intent(in): [dp ] timestep change in canopy temperature diff --git a/build/source/engine/computJacobSundials.f90 b/build/source/engine/computJacobSundials.f90 index 0ff22bd3c..877982cc1 100644 --- a/build/source/engine/computJacobSundials.f90 +++ b/build/source/engine/computJacobSundials.f90 @@ -297,11 +297,11 @@ subroutine computJacobSundials(& mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk )%dat ,& ! intent(in): [dp(:)] derivative in volumetric liquid water content w.r.t. temperature mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2 )%dat ,& ! intent(in): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature ! derivate in bulk heat capacity w.r.t. relevant state variables - dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dPsi0 => diag_data%var(iLookDIAG%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => diag_data%var(iLookDIAG%dVolHtCapBulk_dTheta )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => diag_data%var(iLookDIAG%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => diag_data%var(iLookDIAG%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => diag_data%var(iLookDIAG%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. temperature ! diagnostic variables scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg )%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg )%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) diff --git a/build/source/engine/computThermConduct.f90 b/build/source/engine/computThermConduct.f90 index 7d0bb9107..a3306b544 100644 --- a/build/source/engine/computThermConduct.f90 +++ b/build/source/engine/computThermConduct.f90 @@ -10,20 +10,10 @@ module computThermConduct_module var_ilength, & ! data vector with variable length dimension (i4b) var_dlength ! data vector with variable length dimension (rkind) -! named variables defining elements in the data structures -USE var_lookup,only:iLookPARAM,iLookPROG,iLookDIAG,iLookINDEX ! named variables for structure elements -USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure - ! physical constants USE multiconst,only:& - iden_air, & ! intrinsic density of air (kg m-3) iden_ice, & ! intrinsic density of ice (kg m-3) iden_water, & ! intrinsic density of water (kg m-3) - ! specific heat - Cp_air, & ! specific heat of air (J kg-1 K-1) - Cp_ice, & ! specific heat of ice (J kg-1 K-1) - Cp_soil, & ! specific heat of soil (J kg-1 K-1) - Cp_water, & ! specific heat of liquid water (J kg-1 K-1) ! thermal conductivity lambda_air, & ! thermal conductivity of air (J s-1 m-1) lambda_ice, & ! thermal conductivity of ice (J s-1 m-1) @@ -37,16 +27,32 @@ module computThermConduct_module USE globalData,only:iname_snow ! snow USE globalData,only:iname_soil ! soil +! named variables +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookFLUX ! named variables for structure elements +USE var_lookup,only:iLookPARAM ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements + + ! provide access to named variables for thermal conductivity of soil USE globalData,only:model_decisions ! model decision structure +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure -! decisions for thermal conductivity of soil -USE mDecisions_module,only:Smirnova2000 ! option for temporally constant thermal conductivity - -! decisions for thermal conductivity of soil -USE mDecisions_module,only: funcSoilWet, & ! function of soil wetness - mixConstit, & ! mixture of constituents - hanssonVZJ ! test case for the mizoguchi lab experiment, Hansson et al. VZJ 2004 +! provide access to look-up values for model decisions +USE mDecisions_module,only: & + ! look-up values for choice of thermal conductivity representation for snow + Yen1965, & ! Yen (1965) + Mellor1977, & ! Mellor (1977) + Jordan1991, & ! Jordan (1991) + Smirnova2000, & ! Smirnova et al. (2000) + ! look-up values for choice of thermal conductivity representation for soil + funcSoilWet, & ! function of soil wetness + mixConstit, & ! mixture of constituents + hanssonVZJ, & ! test case for the mizoguchi lab experiment, Hansson et al. VZJ 2004 + ! look-up values for the form of Richards' equation + moisture, & ! moisture-based form of Richards' equation + mixdform ! mixed form of Richards' equation ! privacy implicit none @@ -60,19 +66,24 @@ module computThermConduct_module real(rkind),parameter :: dx=1.e-6_rkind ! finite difference increment contains - ! ********************************************************************************************************** -! public subroutine computThermConduct: compute diagnostic energy variables (thermal conductivity and heat capacity) +! public subroutine computThermConduct: recompute diagnostic energy variables (thermal conductivity and heat capacity) ! ********************************************************************************************************** subroutine computThermConduct(& ! input: control variables computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + nLayers, & ! intent(in): total number of layers canopyDepth, & ! intent(in): canopy depth (m) ! input: state variables - scalarCanopyIce, & ! intent(in): canopy ice content (kg m-2) - scalarCanopyLiquid, & ! intent(in): canopy liquid water content (kg m-2) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + scalarCanopyIce, & ! intent(in): canopy ice content (kg m-2) + scalarCanopyLiquid, & ! intent(in): canopy liquid water content (kg m-2) + mLayerTemp, & ! intent(in): temperature at the current iteration (K) + mLayerMatricHead, & ! intent(in): matric head at the current iteration(m) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + ! input: pre-computed derivatives + mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) ! input/output: data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices @@ -80,17 +91,29 @@ subroutine computThermConduct(& diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! output: error control err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------------- - ! provide access to external subroutines - USE snow_utils_module,only:tcond_snow ! compute thermal conductivity of snow + + ! utility modules + USE snow_utils_module,only:tcond_snow ! compute thermal conductivity of snow + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + USE soil_utils_module,only:dTheta_dPsi ! compute derivative of the soil moisture characteristic w.r.t. psi (m-1) + USE soil_utils_module,only:dPsi_dTheta ! compute derivative of the soil moisture characteristic w.r.t. theta (m) + + implicit none ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: model control logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) + ! input: trial model state variables real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) real(rkind),intent(in) :: scalarCanopyLiquid - real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) + real(rkind),intent(in) :: mLayerTemp(:) ! temperature in each layer at the current iteration (m) + real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head in each layer at the current iteration (m) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice at the current iteration (-) + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid at the current iteration (-) + ! input: pre-computed derivatives + real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative in volumetric liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) ! input/output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model layer indices @@ -115,25 +138,37 @@ subroutine computThermConduct(& real(rkind) :: relativeSat ! relative saturation (-) real(rkind) :: kerstenNum ! the Kersten number (-), defining weight applied to conductivity of the wet medium real(rkind) :: den ! denominator in the thermal conductivity calculations + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind),dimension(nLayers) :: dThermalC_dWat ! derivative in thermal conductivity w.r.t. matric head or volumetric liquid water + real(rkind),dimension(nLayers) :: dThermalC_dNrg ! derivative in thermal conductivity w.r.t. temperature + real(rkind) :: dlambda_wet_dWat ! derivative in thermal conductivity of wet material w.r.t.soil water state variable + real(rkind) :: dlambda_wet_dTk ! derivative in thermal conductivity of wet material w.r.t. temperature + real(rkind) :: dkerstenNum_dWat ! derivative in Kersten number w.r.t. soil water state variable + real(rkind) :: dVolFracLiq_dWat ! derivative in vol fraction of liquid w.r.t. water state variable + real(rkind) :: dVolFracIce_dWat ! derivative in vol fraction of ice w.r.t. water state variable + real(rkind) :: dVolFracLiq_dTk ! derivative in vol fraction of liquid w.r.t. temperature + real(rkind) :: dVolFracIce_dTk ! derivative in vol fraction of ice w.r.t. temperature ! local variables to reproduce the thermal conductivity of Hansson et al. VZJ 2005 - real(rkind),parameter :: c1=0.55_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c2=0.8_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c3=3.07_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: c4=0.13_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c5=4._rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: f1=13.05_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: f2=1.06_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: c1=0.55_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c2=0.8_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c3=3.07_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: c4=0.13_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c5=4._rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: f1=13.05_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: f2=1.06_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) real(rkind) :: fArg,xArg ! temporary variables (see Hansson et al. VZJ 2005 for details) + real(rkind) :: dxArg_dWat,dxArg_dTk ! derivates of the temporary variables with respect to soil water state variable and temperature + ! -------------------------------------------------------------------------------------------------------------------------------- ! associate variables in data structure associate(& ! input: model decisions + ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision, & ! intent(in): index of the form of Richards' equation ixThCondSnow => model_decisions(iLookDECISIONS%thCondSnow)%iDecision, & ! intent(in): choice of method for thermal conductivity of snow ixThCondSoil => model_decisions(iLookDECISIONS%thCondSoil)%iDecision, & ! intent(in): choice of method for thermal conductivity of soil ! input: coordinate variables nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1), & ! intent(in): number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) mLayerHeight => prog_data%var(iLookPROG%mLayerHeight)%dat, & ! intent(in): height at the mid-point of each layer (m) iLayerHeight => prog_data%var(iLookPROG%iLayerHeight)%dat, & ! intent(in): height at the interface of each layer (m) @@ -148,11 +183,21 @@ subroutine computThermConduct(& frac_sand => mpar_data%var(iLookPARAM%frac_sand)%dat, & ! intent(in): fraction of sand (-) frac_silt => mpar_data%var(iLookPARAM%frac_silt)%dat, & ! intent(in): fraction of silt (-) frac_clay => mpar_data%var(iLookPARAM%frac_clay)%dat, & ! intent(in): fraction of clay (-) - ! output: diagnostic variables + vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat, & ! intent(in): [dp(:)] van Genutchen "m" parameter (-) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat, & ! intent(in): [dp(:)] van Genutchen "n" parameter (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat, & ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat, & ! intent(in): [dp(:)] soil residual volumetric water content (-) + ! input: snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ! output: diagnostic variables and derivatives (diagnostic as may be treated as constant) mLayerThermalC => diag_data%var(iLookDIAG%mLayerThermalC)%dat, & ! intent(out): thermal conductivity at the mid-point of each layer (W m-1 K-1) iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(out): thermal conductivity at the interface of each layer (W m-1 K-1) - mLayerVolFracAir => diag_data%var(iLookDIAG%mLayerVolFracAir)%dat & ! intent(out): volumetric fraction of air in each layer (-) - ) ! end associate statement + mLayerVolFracAir => diag_data%var(iLookDIAG%mLayerVolFracAir)%dat, & ! intent(out): volumetric fraction of air in each layer (-) + dThermalC_dWatAbove => diag_data%var(iLookDIAG%dThermalC_dWatAbove)%dat, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow => diag_data%var(iLookDIAG%dThermalC_dWatBelow)%dat, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dTempAbove => diag_data%var(iLookDIAG%dThermalC_dTempAbove)%dat, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow => diag_data%var(iLookDIAG%dThermalC_dTempBelow)%dat & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + ) ! association of local variables with information in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="computThermConduct/" @@ -182,15 +227,35 @@ subroutine computThermConduct(& case(iname_snow); mLayerVolFracAir(iLayer) = 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute volumetric fraction of air'; return end select - + ! ***** - ! * compute the thermal conductivity of snow and soil at the mid-point of each layer... - ! ************************************************************************************* + ! * compute the thermal conductivity of snow and soil and derivatives at the mid-point of each layer... + ! *************************************************************************************************** + dThermalC_dWat(iLayer) = 0._rkind + dThermalC_dNrg(iLayer) = 0._rkind + select case(layerType(iLayer)) ! ***** soil case(iname_soil) + select case(ixRichards) ! (form of Richards' equation) + case(moisture) + dVolFracLiq_dWat = 1._rkind + dVolFracIce_dWat = dPsi_dTheta(mLayerVolFracLiq(iLayer),vGn_alpha(iSoil),theta_res(iSoil),theta_sat(iSoil),vGn_n(iSoil),vGn_m(iSoil)) - 1._rkind + case(mixdform) + Tcrit = crit_soilT( mLayerMatricHead(iSoil) ) + if(mLayerTemp(iLayer) < Tcrit) then + dVolFracLiq_dWat = 0._rkind + dVolFracIce_dWat = dTheta_dPsi(mLayerMatricHead(iSoil),vGn_alpha(iSoil),theta_res(iSoil),theta_sat(iSoil),vGn_n(iSoil),vGn_m(iSoil)) + else + dVolFracLiq_dWat = dTheta_dPsi(mLayerMatricHead(iSoil),vGn_alpha(iSoil),theta_res(iSoil),theta_sat(iSoil),vGn_n(iSoil),vGn_m(iSoil)) + dVolFracIce_dWat = 0._rkind + endif + end select + dVolFracLiq_dTk = mLayerdTheta_dTk(iLayer) !already zeroed out if not below critical temperature + dVolFracIce_dTk = -dVolFracLiq_dTk !often can and will simplify one of these terms out + ! select option for thermal conductivity of soil select case(ixThCondSoil) @@ -199,29 +264,49 @@ subroutine computThermConduct(& ! compute the thermal conductivity of the wet material (W m-1) lambda_wet = lambda_wetsoil**( 1._rkind - theta_sat(iSoil) ) * lambda_water**theta_sat(iSoil) * lambda_ice**(theta_sat(iSoil) - mLayerVolFracLiq(iLayer)) + dlambda_wet_dWat = -lambda_wet * log(lambda_ice) * dVolFracLiq_dWat + dlambda_wet_dTk = -lambda_wet * log(lambda_ice) * dVolFracLiq_dTk + relativeSat = (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer))/theta_sat(iSoil) ! relative saturation + ! drelativeSat_dWat = dPsi0_dWat/theta_sat(iLayer), and drelativeSat_dTk = 0 (so dkerstenNum_dTk = 0) ! compute the Kersten number (-) if(relativeSat > 0.1_rkind)then ! log10(0.1) = -1 kerstenNum = log10(relativeSat) + 1._rkind + dkerstenNum_dWat = (dVolFracIce_dWat + dVolFracLiq_dWat) / ( theta_sat(iSoil) * relativeSat * log(10._rkind) ) else kerstenNum = 0._rkind ! dry thermal conductivity + dkerstenNum_dWat = 0._rkind endif ! ...and, compute the thermal conductivity mLayerThermalC(iLayer) = kerstenNum*lambda_wet + (1._rkind - kerstenNum)*lambda_drysoil + ! compute derivatives + dThermalC_dWat(iLayer) = dkerstenNum_dWat * ( lambda_wet - lambda_drysoil ) + kerstenNum*dlambda_wet_dWat + dThermalC_dNrg(iLayer) = kerstenNum*dlambda_wet_dTk + ! ** mixture of constituents case(mixConstit) - mLayerThermalC(iLayer) = thCond_soil(iSoil) * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component - lambda_ice * mLayerVolFracIce(iLayer) + & ! ice component - lambda_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - lambda_air * mLayerVolFracAir(iLayer) ! air component + mLayerThermalC(iLayer) = thCond_soil(iSoil) * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component + lambda_ice * mLayerVolFracIce(iLayer) + & ! ice component + lambda_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + lambda_air * mLayerVolFracAir(iLayer) ! air component + ! compute derivatives + dThermalC_dWat(iLayer) = lambda_ice*dVolFracIce_dWat + lambda_water*dVolFracLiq_dWat + lambda_air*(-dVolFracIce_dWat - dVolFracLiq_dWat) + dThermalC_dNrg(iLayer) = (lambda_ice - lambda_water) * dVolFracIce_dTk ! ** test case for the mizoguchi lab experiment, Hansson et al. VZJ 2004 case(hanssonVZJ) fArg = 1._rkind + f1*mLayerVolFracIce(iLayer)**f2 xArg = mLayerVolFracLiq(iLayer) + fArg*mLayerVolFracIce(iLayer) + dxArg_dWat = dVolFracLiq_dWat + dVolFracIce_dWat * (1._rkind + f1*(f2+1)*mLayerVolFracIce(iLayer)**f2) + dxArg_dTk = dVolFracIce_dTk * f1*(f2+1)*mLayerVolFracIce(iLayer)**f2 + ! ...and, compute the thermal conductivity mLayerThermalC(iLayer) = c1 + c2*xArg + (c1 - c4)*exp(-(c3*xArg)**c5) + ! compute derivatives + dThermalC_dWat(iLayer) = ( c2 - c5*c3*(c3*xArg)**(c5-1)*(c1 - c4)*exp(-(c3*xArg)**c5) ) * dxArg_dWat + dThermalC_dNrg(iLayer) = ( c2 - c5*c3*(c3*xArg)**(c5-1)*(c1 - c4)*exp(-(c3*xArg)**c5) ) * dxArg_dTk + ! ** check case default; err=20; message=trim(message)//'unable to identify option for thermal conductivity of soil'; return @@ -229,16 +314,32 @@ subroutine computThermConduct(& ! ***** snow case(iname_snow) + dVolFracIce_dWat = ( 1._rkind - mLayerFracLiqSnow(iLayer) )*(iden_water/iden_ice) + dVolFracIce_dTk = -mLayerdTheta_dTk(iLayer)*(iden_water/iden_ice) + ! temporally constant thermal conductivity if(ixThCondSnow==Smirnova2000)then mLayerThermalC(iLayer) = fixedThermalCond_snow + dThermalC_dWat(iLayer) = 0._rkind + dThermalC_dNrg(iLayer) = 0._rkind ! thermal conductivity as a function of snow density else call tcond_snow(mLayerVolFracIce(iLayer)*iden_ice, & ! input: snow density (kg m-3) - mLayerThermalC(iLayer), & ! output: thermal conductivity (W m-1 K-1) - err,cmessage) ! output: error control + mLayerThermalC(iLayer), & ! output: thermal conductivity (W m-1 K-1) + err,cmessage) ! output: error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - endif + select case(ixThCondSnow) + case(Yen1965) + dThermalC_dWat(iLayer) = 2._rkind * 3.217d-6 * mLayerVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dWat + dThermalC_dNrg(iLayer) = 2._rkind * 3.217d-6 * mLayerVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dTk + case(Mellor1977) + dThermalC_dWat(iLayer) = 2._rkind * 2.576d-6 * mLayerVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dWat + dThermalC_dNrg(iLayer) = 2._rkind * 2.576d-6 * mLayerVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dTk + case(Jordan1991) + dThermalC_dWat(iLayer) = ( 7.75d-5 + 2._rkind * 1.105d-6 * mLayerVolFracIce(iLayer) * iden_ice ) * (lambda_ice-lambda_air) * iden_ice * dVolFracIce_dWat + dThermalC_dNrg(iLayer) = ( 7.75d-5 + 2._rkind * 1.105d-6 * mLayerVolFracIce(iLayer) * iden_ice ) * (lambda_ice-lambda_air) * iden_ice * dVolFracIce_dTk + end select ! option for the thermal conductivity of snow + end if ! * error check case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute thermal conductivity'; return @@ -246,13 +347,12 @@ subroutine computThermConduct(& end select end do ! looping through layers - !pause ! ***** ! * compute the thermal conductivity of snow at the interface of each layer... ! **************************************************************************** do iLayer=1,nLayers-1 ! (loop through layers) - ! get temporary variables + ! get temporary variables TCn = mLayerThermalC(iLayer) ! thermal conductivity below the layer interface (W m-1 K-1) TCp = mLayerThermalC(iLayer+1) ! thermal conductivity above the layer interface (W m-1 K-1) zdn = iLayerHeight(iLayer) - mLayerHeight(iLayer) ! height difference between interface and lower value (m) @@ -261,20 +361,38 @@ subroutine computThermConduct(& ! compute thermal conductivity if(TCn+TCp > epsilon(TCn))then iLayerThermalC(iLayer) = (TCn*TCp*(zdn + zdp)) / den + dThermalC_dWatBelow(iLayer) = ( TCn*(zdn + zdp) - iLayerThermalC(iLayer)*zdn ) / den * dThermalC_dWat(iLayer+1) + dThermalC_dWatAbove(iLayer) = ( TCp*(zdn + zdp) - iLayerThermalC(iLayer)*zdp ) / den * dThermalC_dWat(iLayer) + dThermalC_dTempBelow(iLayer) = ( TCn*(zdn + zdp) - iLayerThermalC(iLayer)*zdn ) / den * dThermalC_dNrg(iLayer+1) + dThermalC_dTempAbove(iLayer) = ( TCp*(zdn + zdp) - iLayerThermalC(iLayer)*zdp ) / den * dThermalC_dNrg(iLayer) else iLayerThermalC(iLayer) = (TCn*zdn + TCp*zdp) / (zdn + zdp) + dThermalC_dWatBelow(iLayer) = zdp / (zdn + zdp) * dThermalC_dWat(iLayer+1) + dThermalC_dWatAbove(iLayer) = zdn / (zdn + zdp) * dThermalC_dWat(iLayer) + dThermalC_dTempBelow(iLayer) = zdp / (zdn + zdp) * dThermalC_dNrg(iLayer+1) + dThermalC_dTempAbove(iLayer) = zdn / (zdn + zdp) * dThermalC_dNrg(iLayer) endif end do ! looping through layers ! special case of hansson if(ixThCondSoil==hanssonVZJ)then iLayerThermalC(0) = 28._rkind*(0.5_rkind*(iLayerHeight(1) - iLayerHeight(0))) + dThermalC_dWatBelow(0) = 0._rkind + dThermalC_dTempBelow(0) = 0._rkind else iLayerThermalC(0) = mLayerThermalC(1) + dThermalC_dWatBelow(0) = dThermalC_dWat(1) + dThermalC_dTempBelow(0) = dThermalC_dNrg(1) end if + dThermalC_dWatAbove(0) = realMissing + dThermalC_dTempAbove(0) = realMissing ! assume the thermal conductivity at the domain boundaries is equal to the thermal conductivity of the layer iLayerThermalC(nLayers) = mLayerThermalC(nLayers) + dThermalC_dWatAbove(nLayers) = dThermalC_dWat(nLayers) + dThermalC_dTempAbove(nLayers) = dThermalC_dNrg(nLayers) + dThermalC_dWatBelow(nLayers) = realMissing + dThermalC_dTempBelow(nLayers) = realMissing ! end association to variables in the data structure end associate diff --git a/build/source/engine/diagn_evar.f90 b/build/source/engine/diagn_evar.f90 index d3021249f..d42e9e878 100644 --- a/build/source/engine/diagn_evar.f90 +++ b/build/source/engine/diagn_evar.f90 @@ -168,7 +168,17 @@ subroutine diagn_evar(& mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat, & ! intent(out): volumetric heat capacity in each layer (J m-3 K-1) mLayerThermalC => diag_data%var(iLookDIAG%mLayerThermalC)%dat, & ! intent(out): thermal conductivity at the mid-point of each layer (W m-1 K-1) iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(out): thermal conductivity at the interface of each layer (W m-1 K-1) - mLayerVolFracAir => diag_data%var(iLookDIAG%mLayerVolFracAir)%dat & ! intent(out): volumetric fraction of air in each layer (-) + mLayerVolFracAir => diag_data%var(iLookDIAG%mLayerVolFracAir)%dat, & ! intent(out): volumetric fraction of air in each layer (-) + ! energy derivatives initialized as constant + dVolHtCapBulk_dPsi0 => diag_data%var(iLookDIAG%dVolHtCapBulk_dPsi0)%dat, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => diag_data%var(iLookDIAG%dVolHtCapBulk_dTheta)%dat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => diag_data%var(iLookDIAG%dVolHtCapBulk_dCanWat)%dat(1), & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => diag_data%var(iLookDIAG%dVolHtCapBulk_dTk)%dat, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => diag_data%var(iLookDIAG%dVolHtCapBulk_dTkCanopy)%dat(1), & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dThermalC_dWatAbove => diag_data%var(iLookDIAG%dThermalC_dWatAbove)%dat, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow => diag_data%var(iLookDIAG%dThermalC_dWatBelow)%dat, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dTempAbove => diag_data%var(iLookDIAG%dThermalC_dTempAbove)%dat, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow => diag_data%var(iLookDIAG%dThermalC_dTempBelow)%dat & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above ) ! end associate statement ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -225,7 +235,7 @@ subroutine diagn_evar(& mLayerVolHtCapBulk(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component iden_air * Cp_air * mLayerVolFracAir(iLayer) ! air component - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute volumetric heat capacity'; return end select ! ***** @@ -292,7 +302,6 @@ subroutine diagn_evar(& !print*, 'iLayer, mLayerThermalC(iLayer) = ', iLayer, mLayerThermalC(iLayer) end do ! looping through layers - !pause ! ***** ! * compute the thermal conductivity of snow at the interface of each layer... @@ -323,6 +332,17 @@ subroutine diagn_evar(& ! assume the thermal conductivity at the domain boundaries is equal to the thermal conductivity of the layer iLayerThermalC(nLayers) = mLayerThermalC(nLayers) + ! set derivatives to 0 for constant through step + dVolHtCapBulk_dPsi0 = 0._rkind + dVolHtCapBulk_dTheta = 0._rkind + dVolHtCapBulk_dCanWat = 0._rkind + dVolHtCapBulk_dTk = 0._rkind + dVolHtCapBulk_dTkCanopy = 0._rkind + dThermalC_dWatAbove = 0._rkind + dThermalC_dWatBelow = 0._rkind + dThermalC_dTempAbove = 0._rkind + dThermalC_dTempBelow = 0._rkind + ! end association to variables in the data structure end associate diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 64ef563b8..fa7864942 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -82,7 +82,8 @@ module eval8summa_module ! look-up values for the choice of groundwater representation (local-column, or single-basin) USE mDecisions_module,only: & localColumn, & ! separate groundwater representation in each local soil column - singleBasin ! single groundwater store over the entire basin + singleBasin, & ! single groundwater store over the entire basin + enthalpyFD ! heat capacity using enthalpy ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: & @@ -120,7 +121,7 @@ subroutine eval8summa(& ! input: state vectors stateVecTrial, & ! intent(in): model state vector fScale, & ! intent(in): function scaling vector - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures model_decisions, & ! intent(in): model decisions lookup_data, & ! intent(in): lookup tables @@ -147,17 +148,22 @@ subroutine eval8summa(& err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- ! provide access to subroutines - USE getVectorz_module, only:varExtract ! extract variables from the state vector - USE updateVars_module, only:updateVars ! update prognostic variables - USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy - USE computFlux_module, only:soilCmpres ! compute soil compression, use non-sundials version because sundials version needs mLayerMatricHeadPrime - USE computFlux_module, only:computFlux ! compute fluxes given a state vector - USE computResid_module,only:computResid ! compute residuals given a state vector + USE getVectorz_module, only:varExtract ! extract variables from the state vector + USE updateVars_module, only:updateVars ! update prognostic variables + USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy + USE computFlux_module, only:soilCmpres ! compute soil compression, use non-sundials version because sundials version needs mLayerMatricHeadPrime + USE computFlux_module, only:computFlux ! compute fluxes given a state vector + USE computHeatCap_module,only:computHeatCap ! recompute heat capacity and derivatives + USE computHeatCap_module,only:computHeatCapAnalytic ! recompute heat capacity and derivatives + USE computHeatCap_module,only:computCm + USE computHeatCap_module, only:computStatMult ! recompute state multiplier + USE computResid_module,only:computResid ! compute residuals given a state vector + USE computThermConduct_module,only:computThermConduct ! recompute thermal conductivity and derivatives implicit none ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control - real(rkind),intent(in) :: dt ! length of the time step (seconds) + real(rkind),intent(in) :: dt ! length of the time step (seconds) integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers @@ -168,9 +174,9 @@ subroutine eval8summa(& logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors - real(rkind),intent(in) :: stateVecTrial(:) ! model state vector - real(rkind),intent(in) :: fScale(:) ! function scaling vector - real(rkind),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + real(rkind),intent(in) :: stateVecTrial(:) ! model state vector + real(rkind),intent(in) :: fScale(:) ! function scaling vector + real(rkind),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) ! input: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions type(zLookup), intent(in) :: lookup_data ! lookup tables @@ -187,13 +193,13 @@ subroutine eval8summa(& type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution - real(rkind),intent(out) :: fluxVec(:) ! flux vector - real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation - real(rkind),intent(out) :: resVec(:) ! NOTE: qp ! residual vector - real(rkind),intent(out) :: fEval ! function evaluation + real(rkind),intent(out) :: fluxVec(:) ! flux vector + real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation + real(rkind),intent(out) :: resVec(:) ! NOTE: qp ! residual vector + real(rkind),intent(out) :: fEval ! function evaluation ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -210,22 +216,29 @@ subroutine eval8summa(& real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) ! diagnostic variables - logical(lgt),parameter :: needEnthalpy=.false. ! flag to compute enthalpy, turn off to match Summa BE real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) + real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector of enthalpy for snow+soil layers (J m-3) + ! other local variables - integer(i4b) :: iLayer ! index of model layer in the snow+soil domain - integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain - integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine - integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) + integer(i4b) :: iLayer ! index of model layer in the snow+soil domain + integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain + integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine + integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) real(rkind) :: xMin,xMax ! minimum and maximum values for water content real(rkind) :: scalarCanopyHydTrial ! trial value for mass of water on the vegetation canopy (kg m-2) - real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) + real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) real(rkind),dimension(nLayers) :: mLayerVolFracHydTrial ! trial value for volumetric fraction of water (-), general vector merged from Wat and Liq real(rkind),dimension(nState) :: rVecScaled ! scaled residual vector - character(LEN=256) :: cmessage ! error message of downwind routine + character(LEN=256) :: cmessage ! error message of downwind routine + real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy + real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil + logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step + logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m + ! -------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- @@ -251,13 +264,13 @@ subroutine eval8summa(& mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in): [dp] storage of water in the aquifer (m) ! model diagnostic variables from a previous solution - scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) + scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) ! enthalpy scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) @@ -266,8 +279,10 @@ subroutine eval8summa(& scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2) mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in storage associated with compression of the soil matrix (-) ! derivatives + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature ! mapping ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) @@ -280,7 +295,9 @@ subroutine eval8summa(& ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain - layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) + heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(out): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & ! intent(out): heat capacity for snow and soil ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -426,9 +443,11 @@ subroutine eval8summa(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! compute enthalpy (J m-3) - if(needEnthalpy)then - call t2enthalpy(& + if(updateCp)then + ! *** compute volumetric heat capacity C_p + if(model_decisions(iLookDECISIONS%howHeatCap)%iDecision == enthalpyFD)then + ! compute H_T + call t2enthalpy(& ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure @@ -446,19 +465,139 @@ subroutine eval8summa(& mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) ! output: enthalpy scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) ! output: error control err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - endif ! if computing enthalpy - - ! print the water content - if(globalPrintFlag)then - if(iJac11 = numerical derivatives with one-sided finite differences) - integer(i4b) :: itry ! index of different flux calculations - integer(i4b),parameter :: unperturbed=0 ! named variable to identify the case of unperturbed state variables - integer(i4b),parameter :: perturbState=1 ! named variable to identify the case where we perturb the state in the current layer - integer(i4b),parameter :: perturbStateTempAbove=2 ! named variable to identify the case where we perturb the state layer above - integer(i4b),parameter :: perturbStateTempBelow=3 ! named variable to identify the case where we perturb the state layer below - integer(i4b),parameter :: perturbStateWatAbove=4 ! named variable to identify the case where we perturb the state layer above - integer(i4b),parameter :: perturbStateWatBelow=5 ! named variable to identify the case where we perturb the state layer below - integer(i4b) :: ixPerturb ! index of element in 2-element vector to perturb - integer(i4b) :: ixOriginal ! index of perturbed element in the original vector - real(rkind) :: scalarThermCFlux ! thermal conductivity (W m-1 K-1) - real(rkind) :: scalarThermCFlux_dTempAbove ! thermal conductivity with perturbation to the temperature state above (W m-1 K-1) - real(rkind) :: scalarThermCFlux_dTempBelow ! thermal conductivity with perturbation to the temperature state below (W m-1 K-1) - real(rkind) :: scalarThermCFlux_dWatAbove ! thermal conductivity with perturbation to the water state above - real(rkind) :: scalarThermCFlux_dWatBelow ! thermal conductivity with perturbation to the water state below real(rkind) :: flux0,flux1,flux2 ! fluxes used to calculate derivatives (W m-2) - ! compute fluxes and derivatives at layer interfaces - integer(i4b),dimension(2) :: mLayer_ind ! indices of above and below layers - integer(i4b),dimension(2) :: iLayer_ind ! indices of above and below interfaces - real(rkind) :: matricFHead ! matric head for frozen soil - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - real(rkind) :: fLiq ! fraction of liquid water (-) - real(rkind),dimension(2) :: vectorMatricHeadTrial ! trial value of matric head (m) - real(rkind),dimension(2) :: vectorVolFracLiqTrial ! trial value of volumetric liquid content (-) - real(rkind),dimension(2) :: vectorVolFracIceTrial ! trial value of volumetric ice content (-) - real(rkind),dimension(2) :: vectorTempTrial ! trial value of temperature (K) - real(rkind),dimension(2) :: vectordTheta_dPsi ! derivative in the soil water characteristic w.r.t. psi (m-1) - real(rkind),dimension(2) :: vectordPsi_dTheta ! derivative in the soil water characteristic w.r.t. theta (m) - real(rkind),dimension(2) :: vectorFracLiqSnow ! fraction of liquid water (-) - real(rkind),dimension(2) :: vectortheta_sat ! layer above and below soil porosity (-) - real(rkind),dimension(2) :: vectoriden_soil ! layer above and below density of soil (kg m-3) - real(rkind),dimension(2) :: vectorthCond_soil ! layer above and below thermal conductivity of soil (W m-1 K-1) - real(rkind),dimension(2) :: vectorfrac_sand ! layer above and below fraction of sand (-) - real(rkind),dimension(2) :: vectorfrac_clay ! layer above and below fraction of clay (-) - ! recompute the perturbed version of iLayerThermalC, this could be the only version and remove the computThermConduct_module - real(rkind) :: dThermalC_dHydStateAbove ! derivative in the thermal conductivity w.r.t. water state in the layer above - real(rkind) :: dThermalC_dHydStateBelow ! derivative in the thermal conductivity w.r.t. water state in the layer above - real(rkind) :: dThermalC_dNrgStateAbove ! derivative in the thermal conductivity w.r.t. energy state in the layer above - real(rkind) :: dThermalC_dNrgStateBelow ! derivative in the thermal conductivity w.r.t. energy state in the layer above ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! make association of local variables with information in the data structures associate(& - ixDerivMethod => model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): method used to calculate flux derivatives - ix_bcUpprTdyn => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision, & ! intent(in): method used to calculate the upper boundary condition for thermodynamics - ix_bcLowrTdyn => model_decisions(iLookDECISIONS%bcLowrTdyn)%iDecision, & ! intent(in): method used to calculate the lower boundary condition for thermodynamics - ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision, & ! intent(in): index of the form of Richards' equation - ixThCondSnow => model_decisions(iLookDECISIONS%thCondSnow)%iDecision, & ! intent(in): choice of method for thermal conductivity of snow - ixThCondSoil => model_decisions(iLookDECISIONS%thCondSoil)%iDecision, & ! intent(in): choice of method for thermal conductivity of soil - ! input: model coordinates + ixDerivMethod => model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): method used to calculate flux derivatives + ix_bcUpprTdyn => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision, & ! intent(in): method used to calculate the upper boundary condition for thermodynamics + ix_bcLowrTdyn => model_decisions(iLookDECISIONS%bcLowrTdyn)%iDecision, & ! intent(in): method used to calculate the lower boundary condition for thermodynamics + ! input: coordinate variables nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat, & ! intent(in): index in the state subset for energy state variables in the snow+soil domain - ! input: thermal properties mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): depth of each layer (m) mLayerHeight => prog_data%var(iLookPROG%mLayerHeight)%dat, & ! intent(in): height at the mid-point of each layer (m) + iLayerHeight => prog_data%var(iLookPROG%iLayerHeight)%dat, & ! intent(in): height at the interface of each layer (m) + ! input: thermal properties upperBoundTemp => mpar_data%var(iLookPARAM%upperBoundTemp)%dat(1), & ! intent(in): temperature of the upper boundary (K) lowerBoundTemp => mpar_data%var(iLookPARAM%lowerBoundTemp)%dat(1), & ! intent(in): temperature of the lower boundary (K) - iLayerHeight => prog_data%var(iLookPROG%iLayerHeight)%dat, & ! intent(in): height at the interface of each layer (m) - fixedThermalCond_snow => mpar_data%var(iLookPARAM%fixedThermalCond_snow)%dat(1), & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) - iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(inout): thermal conductivity at the interface of each layer (W m-1 K-1) - ! input: depth varying soil parameters - iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) - thCond_soil => mpar_data%var(iLookPARAM%thCond_soil)%dat, & ! intent(in): thermal conductivity of soil (W m-1 K-1) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): soil porosity (-) - frac_sand => mpar_data%var(iLookPARAM%frac_sand)%dat, & ! intent(in): fraction of sand (-) - frac_clay => mpar_data%var(iLookPARAM%frac_clay)%dat, & ! intent(in): fraction of clay (-) - vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat, & ! intent(in): [dp(:)] van Genutchen "m" parameter (-) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat, & ! intent(in): [dp(:)] van Genutchen "n" parameter (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat, & ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat, & ! intent(in): [dp(:)] soil residual volumetric water content (-) - ! input: snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(in): thermal conductivity at the interface of each layer (W m-1 K-1) + dThermalC_dWatAbove => diag_data%var(iLookDIAG%dThermalC_dWatAbove)%dat, & ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow => diag_data%var(iLookDIAG%dThermalC_dWatBelow)%dat, & ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dTempAbove => diag_data%var(iLookDIAG%dThermalC_dTempAbove)%dat, & ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow => diag_data%var(iLookDIAG%dThermalC_dTempBelow)%dat, & ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above ! output: diagnostic fluxes - iLayerConductiveFlux => flux_data%var(iLookFLUX%iLayerConductiveFlux)%dat, & ! intent(out): conductive energy flux at layer interfaces at end of time step (W m-2) - iLayerAdvectiveFlux => flux_data%var(iLookFLUX%iLayerAdvectiveFlux)%dat & ! intent(out): advective energy flux at layer interfaces at end of time step (W m-2) + iLayerConductiveFlux => flux_data%var(iLookFLUX%iLayerConductiveFlux)%dat, & ! intent(out): conductive energy flux at layer interfaces at end of time step (W m-2) + iLayerAdvectiveFlux => flux_data%var(iLookFLUX%iLayerAdvectiveFlux)%dat & ! intent(out): advective energy flux at layer interfaces at end of time step (W m-2) ) ! association of local variables with information in the data structures ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! initialize error control err=0; message='ssdNrgFlux/' ! set conductive and advective fluxes to missing in the upper boundary - ! NOTE: advective flux at the upper boundary is included in the ground heat flux iLayerConductiveFlux(0) = realMissing - iLayerAdvectiveFlux(0) = realMissing - - ! check the need to compute numerical derivatives - if(ixDerivMethod==numerical)then - nFlux=5 ! compute the derivatives and cross derivates using one-sided finite differences - else - nFlux=0 ! compute analytical derivatives - end if + iLayerAdvectiveFlux(0) = realMissing !included in the ground heat flux ! get the indices for the snow+soil layers if(scalarSolution)then @@ -297,6 +182,53 @@ subroutine ssdNrgFlux(& ixBot = nLayers endif + ! ------------------------------------------------------------------------------------------------------------------------- + ! ***** compute the conductive fluxes at layer interfaces ***** + ! ------------------------------------------------------------------------------------------------------------------------- + do iLayer=ixTop,ixBot + + if(iLayer==nLayers)then ! (lower boundary fluxes -- positive downwards) + ! flux depends on the type of lower boundary condition + select case(ix_bcLowrTdyn) ! (identify the lower boundary condition for thermodynamics + case(prescribedTemp); iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*(lowerBoundTemp - mLayerTempTrial(iLayer))/(mLayerDepth(iLayer)*0.5_rkind) + case(zeroFlux); iLayerConductiveFlux(iLayer) = 0._rkind + end select ! (identifying the lower boundary condition for thermodynamics) + + else ! (domain boundary fluxes -- positive downwards) + iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) / & + (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) + + !write(*,'(a,i4,1x,2(f9.3,1x))') 'iLayer, iLayerConductiveFlux(iLayer), iLayerThermalC(iLayer) = ', iLayer, iLayerConductiveFlux(iLayer), iLayerThermalC(iLayer) + end if ! (the type of layer) + end do ! looping through layers + + ! ------------------------------------------------------------------------------------------------------------------------- + ! ***** compute the advective fluxes at layer interfaces ***** + ! ------------------------------------------------------------------------------------------------------------------------- + do iLayer=ixTop,ixBot + + ! get the liquid flux at layer interfaces + select case(layerType(iLayer)) + case(iname_snow); qFlux = iLayerLiqFluxSnow(iLayer) + case(iname_soil); qFlux = iLayerLiqFluxSoil(iLayer-nSnow) + case default; err=20; message=trim(message)//'unable to identify layer type'; return + end select + ! compute fluxes at the lower boundary -- positive downwards + if(iLayer==nLayers)then + iLayerAdvectiveFlux(iLayer) = -Cp_water*iden_water*qFlux*(lowerBoundTemp - mLayerTempTrial(iLayer)) + ! compute fluxes within the domain -- positive downwards + else + iLayerAdvectiveFlux(iLayer) = -Cp_water*iden_water*qFlux*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) + end if + end do ! looping through layers + + ! ------------------------------------------------------------------------------------------------------------------------- + ! ***** compute the total fluxes at layer interfaces ***** + ! ------------------------------------------------------------------------------------------------------------------------- + ! NOTE: ignore advective fluxes for now + iLayerNrgFlux(0) = groundNetFlux !from vegNrgFlux module + iLayerNrgFlux(ixTop:ixBot) = iLayerConductiveFlux(ixTop:ixBot) + ! ------------------------------------------------------------------------------------------------------------------- ! ***** compute the derivative in fluxes at layer interfaces w.r.t state in the layer above and the layer below ***** ! ------------------------------------------------------------------------------------------------------------------- @@ -310,238 +242,42 @@ subroutine ssdNrgFlux(& dFlux_dTempBelow(nLayers) = -huge(lowerBoundTemp) ! don't expect this to be used, so deliberately set to a ridiculous value to cause problems dFlux_dWatBelow(nLayers) = -huge(lowerBoundTemp) ! don't expect this to be used, so deliberately set to a ridiculous value to cause problems - ! loop through INTERFACES... - do iLayer0=ixTop-1,ixBot - if(ixTop-1/=0 .and. iLayer0==ixTop-1)then - iLayer = 0 !need to always do layer 0 - else - iLayer = iLayer0 - endif - - ! either one or multiple flux calls, depending on if using analytical or numerical derivatives - do itry=nFlux,0,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) - - ! ===== - ! determine layer to perturb - ! ========================== - select case(itry) - ! skip undesired perturbations - case(perturbState); cycle ! perturbing the layers above and below the flux at the interface - ! identify the index for the perturbation - case(unperturbed); ixPerturb = 0 - case(perturbStateTempAbove) - if(iLayer==0) cycle ! cannot perturb state above (does not exist) -- so keep cycling - ixPerturb = 1 - case(perturbStateTempBelow) - if(iLayer==nLayers) cycle ! cannot perturb state below (does not exist) -- so keep cycling - ixPerturb = 2 - case(perturbStateWatAbove) - if(iLayer==0) cycle ! cannot perturb state above (does not exist) -- so keep cycling - ixPerturb = 3 - case(perturbStateWatBelow) - if(iLayer==nLayers) cycle ! cannot perturb state below (does not exist) -- so keep cycling - ixPerturb = 4 - case default; err=10; message=trim(message)//"unknown perturbation"; return - end select ! (identifying layer to of perturbation) - ! determine the index in the original vector - ixOriginal = iLayer + (ixPerturb-1) - - ! ===== - ! set indices and parameters needed for layer perturbation - ! ======================================================== - mLayer_ind(1) = iLayer - mLayer_ind(2) = iLayer+1 - if (iLayer==0 ) mLayer_ind(1) = 1 - if (iLayer==nLayers ) mLayer_ind(2) = nLayers - ! indices of interface are different at top layer since interface 0 exists - iLayer_ind = mLayer_ind - if (iLayer==0 ) iLayer_ind(1) = 0 - - ! ===== - ! get input state variables... - ! ============================ - ! start with the un-perturbed case - vectorVolFracLiqTrial(1:2) = mLayerVolFracLiqTrial(mLayer_ind) - ! need to protect against negative indexes - if ( mLayer_ind(1) > nSnow .and. mLayer_ind(2) > nSnow)then - vectorMatricHeadTrial(1:2) = mLayerMatricHeadTrial(mLayer_ind-nSnow) - else if (mLayer_ind(2) > nSnow)then !mLayer_ind(1) == nSnow - vectorMatricHeadTrial(1) = realMissing - vectorMatricHeadTrial(2) = mLayerMatricHeadTrial(mLayer_ind(2)-nSnow) - else !snow layer - vectorMatricHeadTrial(1:2) = realMissing - end if - vectorTempTrial(1:2) = mLayerTempTrial(mLayer_ind) - vectorVolFracIceTrial(1:2) = mLayerVolFracIceTrial(mLayer_ind) - ! make appropriate perturbations, - if(ixPerturb > 2)then - vectorMatricHeadTrial(ixPerturb-2) = vectorMatricHeadTrial(ixPerturb-2) + dx - vectorVolFracLiqTrial(ixPerturb-2) = vectorVolFracLiqTrial(ixPerturb-2) + dx - else if(ixPerturb > 0)then - vectorTempTrial(ixPerturb) = vectorTempTrial(ixPerturb) + dx - endif - - ! ***** - ! * compute the volumetric fraction of liquid, ice, and air in each layer in response to perturbation ... - ! ******************************************************************************************************* - - do i = 1,2 !(layer above and below) - select case(layerType(mLayer_ind(i))) !(snow or soil) - - case(iname_soil) - j = mLayer_ind(i)-nSnow !soil layer - if(ixPerturb > 0)then ! only recompute these if perturbed - select case(ixRichards) ! (form of Richards' equation) - case(moisture) ! - vectorMatricHeadTrial(i) = matricHead(vectorVolFracLiqTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - Tcrit = crit_soilT(vectorMatricHeadTrial(i)) - !if change temp and below critical, it changes the state variable, seems like a problem FIX - if(vectorTempTrial(i) < Tcrit) then !if do not perturb temperature, this should not change - matricFHead = (LH_fus/gravity)*(vectorTempTrial(i) - Tfreeze)/Tfreeze - vectorVolFracLiqTrial(i) = volFracLiq(matricFHead,vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - endif - case(mixdform) - Tcrit = crit_soilT(vectorMatricHeadTrial(i)) - if(vectorTempTrial(i) < Tcrit) then !if do not perturb temperature, this should not change, but matricHeadTrial will have changed - matricFHead = (LH_fus/gravity)*(vectorTempTrial(i) - Tfreeze)/Tfreeze - vectorVolFracLiqTrial(i) = volFracLiq(matricFHead,vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - else - vectorVolFracLiqTrial(i) = volFracLiq(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - endif - end select ! (form of Richards' equation) - vectorVolFracIceTrial(i) = volFracLiq(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - vectorVolFracLiqTrial(i) - endif ! (recompute if perturbed) - ! derivatives, these need to be computed because they are computed only in soilLiqFlx which is called after this - vectordPsi_dTheta(i) = dPsi_dTheta(vectorVolFracLiqTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - vectordTheta_dPsi(i) = dTheta_dPsi(vectorMatricHeadTrial(i),vGn_alpha(j),theta_res(j),theta_sat(j),vGn_n(j),vGn_m(j)) - vectorFracLiqSnow(i) = realMissing - ! soil parameters - vectortheta_sat(i) = theta_sat(j) - vectoriden_soil(i) = iden_soil(j) - vectorthCond_soil(i) = thCond_soil(j) - vectorfrac_sand(i) = frac_sand(j) - vectorfrac_clay(i) = frac_clay(j) - - case(iname_snow) - fLiq = fracliquid(vectorTempTrial(i),snowfrz_scale) ! fraction of liquid water - if(ixPerturb > 0) vectorVolFracIceTrial(i) = ( vectorVolFracLiqTrial(i) / fLiq - vectorVolFracLiqTrial(i) )*(iden_water/iden_ice) ! use perturbed nodeVolTotWatTrial - ! derivatives - vectordPsi_dTheta(i) = realMissing - vectordTheta_dPsi(i) = realMissing - vectorFracLiqSnow(i) = mLayerFracLiqSnow(mLayer_ind(i)) - ! soil parameters do not exist - vectortheta_sat(i) = realMissing - vectoriden_soil(i) = realMissing - vectorthCond_soil(i) = realMissing - vectorfrac_sand(i) = realMissing - vectorfrac_clay(i) = realMissing - - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute volumetric fraction of air'; return - - end select !(snow or soil) - enddo !(layer above and below) - - ! ===== - ! get thermal conductivity at layer interface and its derivative w.r.t. the state above and the state below... - ! ============================================================================================================ - call iLayerThermalConduct(& - ! input: model control - deriv_desired, & ! intent(in): flag indicating if derivatives are desired - ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) - ixThCondSnow, & ! intent(in): choice of method for thermal conductivity of snow - ixThCondSoil, & ! intent(in): choice of method for thermal conductivity of soil - ! input: coordinate variables - nLayers, & ! intent(in): number of layers - iLayer, & ! intent(in): layer index for output - layerType(mLayer_ind), & ! intent(in): layer type (iname_soil or iname_snow) - ! input: state variables (adjacent layers) - vectorMatricHeadTrial, & ! intent(in): matric head at the nodes (m) - vectorVolFracLiqTrial, & ! intent(in): volumetric liquid water at the nodes (m) - vectorVolFracIceTrial, & ! intent(in): volumetric ice at the nodes (m) - vectorTempTrial, & ! intent(in): temperature at the nodes (m) - ! input: pre-computed derivatives - mLayerdTheta_dTk(mLayer_ind), & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - vectorFracLiqSnow, & ! intent(in): fraction of liquid water (-) - vectordTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) - vectordPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) - ! input: model coordinate variables (adjacent layers) - mLayerHeight(mLayer_ind), & ! intent(in): height at the mid-point of the node (m) - iLayerHeight(iLayer_ind), & ! intent(in): height at the interface of the nodes (m) - ! input: soil parameters - vectortheta_sat, & ! intent(in): soil porosity (-) - vectoriden_soil, & ! intent(in): intrinsic density of soil (kg m-3) - vectorthCond_soil, & ! intent(in): thermal conductivity of soil (W m-1 K-1) - vectorfrac_sand, & ! intent(in): fraction of sand (-) - vectorfrac_clay, & ! intent(in): fraction of clay (-) - ! input: snow parameters - fixedThermalCond_snow, & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) - ! output: conductivity at the layer interface (scalars) - iLayerThermalC(iLayer), & ! intent(inout): thermal conductivity at the interface of each layer (W m-1 K-1) - ! output: derivatives in thermal conductivity w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below - dThermalC_dHydStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dHydStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above - ! output: derivatives in thermal conductivity w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (W m-1 K-2) - dThermalC_dNrgStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dNrgStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above - ! output: error control - err,cmessage) ! intent(out): error control - - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - ! compute total vertical flux, to compute derivatives - if(deriv_desired .and. ixDerivMethod==numerical)then - select case(itry) - case(unperturbed); scalarThermCFlux = iLayerThermalC(iLayer) - case(perturbStateTempAbove); scalarThermCFlux_dTempAbove = iLayerThermalC(iLayer) - case(perturbStateTempBelow); scalarThermCFlux_dTempBelow = iLayerThermalC(iLayer) - case(perturbStateWatAbove); scalarThermCFlux_dWatAbove = iLayerThermalC(iLayer) - case(perturbStateWatBelow); scalarThermCFlux_dWatBelow = iLayerThermalC(iLayer) - case default; err=10; message=trim(message)//"unknown perturbation"; return - end select - end if - - end do ! (looping through different flux calculations -- one or multiple calls depending if desire for numerical or analytical derivatives) - - - ! ***** the upper boundary - if(iLayer==0)then ! (upper boundary) - - ! identify the upper boundary condition - select case(ix_bcUpprTdyn) - - ! * prescribed temperature at the upper boundary - case(prescribedTemp) - dz = mLayerHeight(iLayer+1)*0.5_rkind - if(ixDerivMethod==analytical)then ! ** analytical derivatives - dFlux_dWatBelow(iLayer) = -dThermalC_dHydStateBelow * ( mLayerTempTrial(iLayer+1) - upperBoundTemp )/dz - dFlux_dTempBelow(iLayer) = -dThermalC_dNrgStateBelow * ( mLayerTempTrial(iLayer+1) - upperBoundTemp )/dz - iLayerThermalC(iLayer)/dz - else ! ** numerical derivatives - flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - upperBoundTemp ) / dz - flux2 = -scalarThermCFlux_dWatBelow*( mLayerTempTrial(iLayer+1) - upperBoundTemp ) / dz - dFlux_dWatBelow(iLayer) = (flux2 - flux0)/dx - flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - upperBoundTemp ) / dz - flux2 = -scalarThermCFlux_dTempBelow*((mLayerTempTrial(iLayer+1)+dx) - upperBoundTemp ) / dz - dFlux_dTempBelow(iLayer) = (flux2 - flux0)/dx - end if - - ! * zero flux at the upper boundary - case(zeroFlux) - dFlux_dWatBelow(iLayer) = 0._rkind - dFlux_dTempBelow(iLayer) = 0._rkind - - ! * compute flux inside vegetation energy flux routine, use here - case(energyFlux) - dFlux_dWatBelow(iLayer) = 0._rkind !dGroundNetFlux_dGroundWat, does not exist in vegNrgFlux - dFlux_dTempBelow(iLayer) = dGroundNetFlux_dGroundTemp + ! ***** the upper boundary, always do + select case(ix_bcUpprTdyn) + + ! * prescribed temperature at the upper boundary + case(prescribedTemp) + dz = mLayerHeight(1)*0.5_rkind + dFlux_dWatBelow(0) = -dThermalC_dWatBelow(0) * ( mLayerTempTrial(1) - upperBoundTemp )/dz + if(ixDerivMethod==analytical)then ! ** analytical derivatives for temperature + dFlux_dTempBelow(0) = -dThermalC_dTempBelow(0) * ( mLayerTempTrial(1) - upperBoundTemp )/dz - iLayerThermalC(0)/dz + else ! ** numerical derivatives for constant step iLayerThermalC + flux0 = -iLayerThermalC(0) * ( mLayerTempTrial(1) - upperBoundTemp ) / dz + flux2 = -iLayerThermalC(0) * ((mLayerTempTrial(1)+dx) - upperBoundTemp ) / dz + dFlux_dTempBelow(0) = (flux2 - flux0)/dx + end if + + ! * zero flux at the upper boundary + case(zeroFlux) + dFlux_dWatBelow(0) = 0._rkind + dFlux_dTempBelow(0) = 0._rkind + + ! * compute flux inside vegetation energy flux routine, use here + case(energyFlux) + dFlux_dWatBelow(0) = 0._rkind !dGroundNetFlux_dGroundWat, does not exist in vegNrgFlux + dFlux_dTempBelow(0) = dGroundNetFlux_dGroundTemp + + case default; err=20; message=trim(message)//'unable to identify upper boundary condition for thermodynamics'; return + + end select ! (identifying the upper boundary condition for thermodynamics) + !dGroundNetFlux_dGroundWat = dFlux_dWatBelow(0) ! this is true, but since not used in vegNrgFlux do not define + dGroundNetFlux_dGroundTemp = dFlux_dTempBelow(0) ! need this in vegNrgFlux - case default; err=20; message=trim(message)//'unable to identify upper boundary condition for thermodynamics'; return - - end select ! (identifying the upper boundary condition for thermodynamics) - !dGroundNetFlux_dGroundWat = dFlux_dWatBelow(iLayer) ! this is true, but since not used in vegNrgFlux do not define - dGroundNetFlux_dGroundTemp = dFlux_dTempBelow(iLayer) ! need this in vegNrgFlux + ! loop through INTERFACES... + do iLayer=ixTop,ixBot ! ***** the lower boundary - else if(iLayer==nLayers)then ! (lower boundary) + if(iLayer==nLayers)then ! (lower boundary) ! identify the lower boundary condition select case(ix_bcLowrTdyn) @@ -549,15 +285,12 @@ subroutine ssdNrgFlux(& ! * prescribed temperature at the lower boundary case(prescribedTemp) dz = mLayerDepth(iLayer)*0.5_rkind - if(ixDerivMethod==analytical)then ! ** analytical derivatives - dFlux_dWatAbove(iLayer) = -dThermalC_dHydStateAbove * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz - dFlux_dTempAbove(iLayer) = -dThermalC_dNrgStateAbove * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + iLayerThermalC(iLayer)/dz - else ! ** numerical derivatives - flux0 = -scalarThermCFlux * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz - flux1 = -scalarThermCFlux_dWatAbove * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz - dFlux_dWatAbove(iLayer) = (flux1 - flux0)/dx - flux0 = -scalarThermCFlux * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz - flux1 = -scalarThermCFlux_dTempAbove * ( lowerBoundTemp - (mLayerTempTrial(iLayer)+dx) )/dz + dFlux_dWatAbove(iLayer) = -dThermalC_dWatAbove(iLayer) * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + if(ixDerivMethod==analytical)then ! ** analytical derivatives + dFlux_dTempAbove(iLayer) = -dThermalC_dTempAbove(iLayer) * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + iLayerThermalC(iLayer)/dz + else ! ** numerical derivatives for constant step iLayerThermalC + flux0 = -iLayerThermalC(iLayer)*(lowerBoundTemp - (mLayerTempTrial(iLayer) ))/dz + flux1 = -iLayerThermalC(iLayer)*(lowerBoundTemp - (mLayerTempTrial(iLayer)+dx))/dz dFlux_dTempAbove(iLayer) = (flux1 - flux0)/dx end if @@ -574,20 +307,15 @@ subroutine ssdNrgFlux(& else dz = (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) - if(ixDerivMethod==analytical)then ! ** analytical derivatives - dFlux_dWatAbove(iLayer) = -dThermalC_dHydStateAbove * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz - dFlux_dWatBelow(iLayer) = -dThermalC_dHydStateBelow * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz - dFlux_dTempAbove(iLayer) = -dThermalC_dNrgStateAbove * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz + iLayerThermalC(iLayer)/dz - dFlux_dTempBelow(iLayer) = -dThermalC_dNrgStateBelow * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz - iLayerThermalC(iLayer)/dz - else ! ** numerical derivatives - flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz - flux1 = -scalarThermCFlux_dWatAbove*( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz - flux2 = -scalarThermCFlux_dWatBelow*( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz - dFlux_dWatAbove(iLayer) = (flux1 - flux0)/dx - dFlux_dWatBelow(iLayer) = (flux2 - flux0)/dx - flux0 = -scalarThermCFlux *( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz - flux1 = -scalarThermCFlux_dTempAbove*( mLayerTempTrial(iLayer+1) - (mLayerTempTrial(iLayer)+dx)) / dz - flux2 = -scalarThermCFlux_dTempBelow*((mLayerTempTrial(iLayer+1)+dx) - mLayerTempTrial(iLayer) ) / dz + dFlux_dWatAbove(iLayer) = -dThermalC_dWatAbove(iLayer) * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz + dFlux_dWatBelow(iLayer) = -dThermalC_dWatBelow(iLayer) * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz + if(ixDerivMethod==analytical)then ! ** analytical derivatives + dFlux_dTempAbove(iLayer) = -dThermalC_dTempAbove(iLayer) * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz + iLayerThermalC(iLayer)/dz + dFlux_dTempBelow(iLayer) = -dThermalC_dTempBelow(iLayer) * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz - iLayerThermalC(iLayer)/dz + else ! ** numerical derivatives for constant step iLayerThermalC + flux0 = -iLayerThermalC(iLayer)*( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz + flux1 = -iLayerThermalC(iLayer)*( mLayerTempTrial(iLayer+1) - (mLayerTempTrial(iLayer)+dx)) / dz + flux2 = -iLayerThermalC(iLayer)*((mLayerTempTrial(iLayer+1)+dx) - mLayerTempTrial(iLayer) ) / dz dFlux_dTempAbove(iLayer) = (flux1 - flux0)/dx dFlux_dTempBelow(iLayer) = (flux2 - flux0)/dx end if @@ -596,402 +324,11 @@ subroutine ssdNrgFlux(& end do ! (looping through layers) - ! ------------------------------------------------------------------------------------------------------------------------- - ! ***** compute the conductive fluxes at layer interfaces ***** - ! Compute flux after the derivatives, because need iLayerThermal as calculated above - ! ------------------------------------------------------------------------------------------------------------------------- - do iLayer0=ixTop-1,ixBot - if(ixTop-1/=0 .and. iLayer0==ixTop-1)then - iLayer = 0 !need to always do layer 0 - else - iLayer = iLayer0 - endif - - if(iLayer==0)then ! (upper boundary fluxes -- positive downwards) - iLayerConductiveFlux(iLayer) = groundNetFlux !from vegNrgFlux module - - else if(iLayer==nLayers)then ! (lower boundary fluxes -- positive downwards) - ! flux depends on the type of lower boundary condition - select case(ix_bcLowrTdyn) ! (identify the lower boundary condition for thermodynamics - case(prescribedTemp); iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*(lowerBoundTemp - mLayerTempTrial(iLayer))/(mLayerDepth(iLayer)*0.5_rkind) - case(zeroFlux); iLayerConductiveFlux(iLayer) = 0._rkind - end select ! (identifying the lower boundary condition for thermodynamics) - - else ! (domain boundary fluxes -- positive downwards) - iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) / & - (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) - - !write(*,'(a,i4,1x,2(f9.3,1x))') 'iLayer, iLayerConductiveFlux(iLayer), iLayerThermalC(iLayer) = ', iLayer, iLayerConductiveFlux(iLayer), iLayerThermalC(iLayer) - end if ! (the type of layer) - end do ! looping through layers - - ! ------------------------------------------------------------------------------------------------------------------------- - ! ***** compute the advective fluxes at layer interfaces ***** - ! ------------------------------------------------------------------------------------------------------------------------- - do iLayer0=ixTop-1,ixBot - if(ixTop-1/=0 .and. iLayer0==ixTop-1)then - iLayer = 0 !need to always do layer 0 - else - iLayer = iLayer0 - endif - - if (iLayer==0) then - iLayerAdvectiveFlux(iLayer) = realMissing !advective flux at the upper boundary is included in the ground heat flux - else ! get the liquid flux at layer interfaces - select case(layerType(iLayer)) - case(iname_snow); qFlux = iLayerLiqFluxSnow(iLayer) - case(iname_soil); qFlux = iLayerLiqFluxSoil(iLayer-nSnow) - case default; err=20; message=trim(message)//'unable to identify layer type'; return - end select - ! compute fluxes at the lower boundary -- positive downwards - if(iLayer==nLayers)then - iLayerAdvectiveFlux(iLayer) = -Cp_water*iden_water*qFlux*(lowerBoundTemp - mLayerTempTrial(iLayer)) - ! compute fluxes within the domain -- positive downwards - else - iLayerAdvectiveFlux(iLayer) = -Cp_water*iden_water*qFlux*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) - end if - end if ! (all layers except surface) - end do ! looping through layers - - ! ------------------------------------------------------------------------------------------------------------------------- - ! ***** compute the total fluxes at layer interfaces ***** - ! ------------------------------------------------------------------------------------------------------------------------- - ! NOTE: ignore advective fluxes for now - iLayerNrgFlux(0) = iLayerConductiveFlux(0) - iLayerNrgFlux(ixTop:ixBot) = iLayerConductiveFlux(ixTop:ixBot) - ! end association of local variables with information in the data structures end associate end subroutine ssdNrgFlux -! ************************************************************************************************************************************* -! private subroutine iLayerThermalConduct: compute diagnostic energy variables (thermal conductivity and heat capacity) and derivatives -! ************************************************************************************************************************************* -subroutine iLayerThermalConduct(& - ! input: model control - deriv_desired, & ! intent(in): flag indicating if derivatives are desired - ixRichards, & ! intent(in): choice of option for Richards' equation - ixThCondSnow, & ! intent(in): choice of method for thermal conductivity of snow - ixThCondSoil, & ! intent(in): choice of method for thermal conductivity of soil - ! input: coordinate variables - nLayers, & ! intent(in): number of layers - ixLayerDesired, & ! intent(in): layer index for output - layerType, & ! intent(in): layer type (iname_soil or iname_snow) - ! input: state variables (adjacent layers) - nodeMatricHead, & ! intent(in): matric head at the nodes (m) - nodeVolFracLiq, & ! intent(in): volumetric liquid water content at the nodes (m) - nodeVolFracIce, & ! intent(in): volumetric ice at the nodes (m) - nodeTemp, & ! intent(in): temperature at the nodes (m) - ! input: pre-computed derivatives - dTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - fracLiqSnow, & ! intent(in) : fraction of liquid water (-) - dTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) - dPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) - ! input: model coordinate variables (adjacent layers) - nodeHeight, & ! intent(in): height at the mid-point of the node (m) - node_iHeight, & ! intent(in): height at the interface of the nodes (m) - ! input: soil parameters at nodes - theta_sat, & ! intent(in): soil porosity (-) - iden_soil, & !intrinsic density of soil (kg m-3) - thCond_soil, & ! thermal conductivity of soil (W m-1 K-1) - frac_sand, & ! intent(in): fraction of sand (-) - frac_clay, & ! fraction of clay (-) - ! input: snow parameters - fixedThermalCond_snow, & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) - ! output: conductivity at the layer interface (scalars) - iLayerThermalC, & ! intent(inout) thermal conductivity at the interface of each layer (W m-1 K-1) - ! output: derivatives in thermal conductivity w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below - dThermalC_dHydStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dHydStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above - ! output: derivatives in thermal conductivity w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (W m-1 K-2) - dThermalC_dNrgStateAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dNrgStateBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above - ! output: error control - err,message) ! intent(out): error control - - USE snow_utils_module,only:tcond_snow ! compute thermal conductivity of snow - USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists - ! constants - USE multiconst, only: gravity, & ! gravitational acceleration (m s-1) - Tfreeze, & ! freezing point of water (K) - iden_water,iden_ice,& ! intrinsic density of water and ice (kg m-3) - LH_fus ! latent heat of fusion (J kg-1) - - implicit none - ! -------------------------------------------------------------------------------------------------------------------------------------- - ! input: model control - logical(lgt),intent(in) :: deriv_desired ! flag to indicate if derivatives are desired - integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation - integer(i4b),intent(in) :: ixThCondSnow ! choice of method for thermal conductivity of snow - integer(i4b),intent(in) :: ixThCondSoil ! choice of method for thermal conductivity of soil - ! input: coordinate variables - integer(i4b),intent(in) :: nLayers ! number of layers - integer(i4b),intent(in) :: ixLayerDesired ! layer index for output - integer(i4b),intent(in) :: layerType(:) ! layer type (iname_soil or iname_snow) - ! input: state variables - real(rkind),intent(in) :: nodeMatricHead(:) ! trial vector of total water matric potential (m) - real(rkind),intent(in) :: nodeVolFracLiq(:) ! trial vector of volumetric liquid water content, recomputed with perturbed water state(-) - real(rkind),intent(in) :: nodeVolFracIce(:) ! trial vector of ice content, recomputed with perturbed water state(-) - real(rkind),intent(in) :: nodeTemp(:) ! trial vector of temperature (K) - ! input: pre-computed derivatives - real(rkind),intent(in) :: dTheta_dTk(:) ! derivative in volumetric liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: fracLiqSnow(:) ! fraction of liquid water (-) - real(rkind),intent(in) :: dTheta_dPsi(:) ! derivative in the soil water characteristic w.r.t. psi (m-1) - real(rkind),intent(in) :: dPsi_dTheta(:) ! derivative in the soil water characteristic w.r.t. theta (m) - ! input: model coordinate variables - real(rkind),intent(in) :: nodeHeight(:) ! height at the mid-point of the lower node (m) - real(rkind),intent(in) :: node_iHeight(:) ! height at the interface of each node (m) - ! input: soil parameters - real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) - real(rkind),intent(in) :: iden_soil(:) ! intrinsic density of soil (kg m-3) - real(rkind),intent(in) :: thCond_soil(:) ! thermal conductivity of soil (W m-1 K-1) - real(rkind),intent(in) :: frac_sand(:) ! intent(in): fraction of sand (-) - real(rkind),intent(in) :: frac_clay(:) ! fraction of clay (-) - ! input: snow parameters - real(rkind),intent(in) :: fixedThermalCond_snow ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) - ! output: thermal conductivity at layer interfaces - real(rkind),intent(inout) :: iLayerThermalC ! thermal conductivity at the interface of each layer (W m-1 K-1) - ! output: thermal conductivity derivatives at all layer interfaces - real(rkind),intent(out) :: dThermalC_dHydStateAbove ! derivatives in the thermal conductivity w.r.t. matric head or volumetric liquid water in the layer above - real(rkind),intent(out) :: dThermalC_dHydStateBelow ! derivatives in the thermal conductivity w.r.t. matric head or volumetric liquid water in the layer below - real(rkind),intent(out) :: dThermalC_dNrgStateAbove ! derivatives in the thermal conductivity w.r.t. temperature in the layer above (W m-1 K-2) - real(rkind),intent(out) :: dThermalC_dNrgStateBelow ! derivatives in the thermal conductivity w.r.t. temperature in the layer below (W m-1 K-2) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables (named variables to provide index of 2-element vectors) - integer(i4b),parameter :: ixUpper=1 ! index of upper node in the 2-element vectors - integer(i4b),parameter :: ixLower=2 ! index of lower node in the 2-element vectors - character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b) :: iLayer ! index of model layer - real(rkind) :: TCn ! thermal conductivity below the layer interface (W m-1 K-1) - real(rkind) :: TCp ! thermal conductivity above the layer interface (W m-1 K-1) - real(rkind) :: zdn ! height difference between interface and lower value (m) - real(rkind) :: zdp ! height difference between interface and upper value (m) - real(rkind) :: bulkden_soil ! bulk density of soil (kg m-3) - real(rkind) :: lambda_drysoil ! thermal conductivity of dry soil (W m-1) - real(rkind) :: lambda_wetsoil ! thermal conductivity of wet soil (W m-1) - real(rkind) :: lambda_wet ! thermal conductivity of the wet material - real(rkind) :: relativeSat ! relative saturation (-) - real(rkind) :: kerstenNum ! the Kersten number (-), defining weight applied to conductivity of the wet medium - real(rkind) :: den ! denominator in the thermal conductivity calculations - real(rkind) :: dThermalC_dWat(2) ! derivative in thermal conductivity w.r.t. matric head or volumetric liquid water - real(rkind) :: dThermalC_dNrg(2) ! derivative in thermal conductivity w.r.t. temperature - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - real(rkind) :: dlambda_wet_dWat ! derivative in thermal conductivity of wet material w.r.t.soil water state variable - real(rkind) :: dlambda_wet_dTk ! derivative in thermal conductivity of wet material w.r.t. temperature - real(rkind) :: dkerstenNum_dWat ! derivative in Kersten number w.r.t. soil water state variable - real(rkind) :: mLayerThermalC(2) ! thermal conductivity of each layer (W m-1 K-1) - real(rkind) :: dVolFracLiq_dWat ! derivative in vol fraction of liquid w.r.t. water state variable - real(rkind) :: dVolFracIce_dWat ! derivative in vol fraction of ice w.r.t. water state variable - real(rkind) :: dVolFracLiq_dTk ! derivative in vol fraction of liquid w.r.t. temperature - real(rkind) :: dVolFracIce_dTk ! derivative in vol fraction of ice w.r.t. temperature - ! local variables to reproduce the thermal conductivity of Hansson et al. VZJ 2005 - real(rkind),parameter :: c1=0.55_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c2=0.8_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c3=3.07_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: c4=0.13_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c5=4._rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: f1=13.05_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: f2=1.06_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind) :: fArg,xArg ! temporary variables (see Hansson et al. VZJ 2005 for details) - real(rkind) :: dxArg_dWat,dxArg_dTk ! derivates of the temporary variables with respect to soil water state variable and temperature - - ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="iLayerThermalConduct/" - - ! loop through layers - do iLayer=ixUpper,ixLower - - ! compute the thermal conductivity of dry and wet soils (W m-1) - ! NOTE: this is actually constant over the simulation, and included here for clarity - if(ixThCondSoil==funcSoilWet .and. layerType(iLayer)==iname_soil)then - bulkden_soil = iden_soil(iLayer)*( 1._rkind - theta_sat(iLayer) ) - lambda_drysoil = (0.135_rkind*bulkden_soil + 64.7_rkind) / (iden_soil(iLayer) - 0.947_rkind*bulkden_soil) - lambda_wetsoil = (8.80_rkind*frac_sand(iLayer) + 2.92_rkind*frac_clay(iLayer)) / (frac_sand(iLayer) + frac_clay(iLayer)) - end if - - ! ***** - ! * compute the thermal conductivity of snow and soil and derivates at the mid-point of each layer... - ! *************************************************************************************************** - dThermalC_dWat(iLayer) = 0._rkind - dThermalC_dNrg(iLayer) = 0._rkind - - select case(layerType(iLayer)) - - ! ***** soil - case(iname_soil) - - ! (process derivatives) - dVolFracLiq_dWat = 0._rkind - dVolFracIce_dWat = 0._rkind - dVolFracLiq_dTk = 0._rkind - dVolFracIce_dTk = 0._rkind - if(deriv_desired)then - select case(ixRichards) ! (form of Richards' equation) - case(moisture) - dVolFracLiq_dWat = 1._rkind - dVolFracIce_dWat = dPsi_dTheta(iLayer) - 1._rkind - case(mixdform) - Tcrit = crit_soilT(nodeMatricHead(iLayer) ) - if(nodeTemp(iLayer) < Tcrit) then - dVolFracLiq_dWat = 0._rkind - dVolFracIce_dWat = dTheta_dPsi(iLayer) - else - dVolFracLiq_dWat = dTheta_dPsi(iLayer) - dVolFracIce_dWat = 0._rkind - endif - end select - dVolFracLiq_dTk = dTheta_dTk(iLayer) !already zeroed out if not below critical temperature - dVolFracIce_dTk = -dVolFracLiq_dTk !often can and will simplify one of these terms out - endif - - ! select option for thermal conductivity of soil - select case(ixThCondSoil) - - ! ** function of soil wetness - case(funcSoilWet) - - ! compute the thermal conductivity of the wet material (W m-1) - lambda_wet = lambda_wetsoil**( 1._rkind - theta_sat(iLayer) ) * lambda_water**theta_sat(iLayer) * lambda_ice**(theta_sat(iLayer) - nodeVolFracLiq(iLayer)) - dlambda_wet_dWat = -lambda_wet * log(lambda_ice) * dVolFracLiq_dWat - dlambda_wet_dTk = -lambda_wet * log(lambda_ice) * dVolFracLiq_dTk - - relativeSat = (nodeVolFracIce(iLayer) + nodeVolFracLiq(iLayer))/theta_sat(iLayer) ! relative saturation - ! drelativeSat_dWat = dPsi0_dWat/theta_sat(iLayer), and drelativeSat_dTk = 0 (so dkerstenNum_dTk = 0) - ! compute the Kersten number (-) - if(relativeSat > 0.1_rkind)then ! log10(0.1) = -1 - kerstenNum = log10(relativeSat) + 1._rkind - dkerstenNum_dWat = (dVolFracIce_dWat + dVolFracLiq_dWat) / ( theta_sat(iLayer) * relativeSat * log(10._rkind) ) - else - kerstenNum = 0._rkind ! dry thermal conductivity - dkerstenNum_dWat = 0._rkind - endif - ! ...and, compute the thermal conductivity - mLayerThermalC(iLayer) = kerstenNum*lambda_wet + (1._rkind - kerstenNum)*lambda_drysoil - - ! compute derivatives - dThermalC_dWat(iLayer) = dkerstenNum_dWat * ( lambda_wet - lambda_drysoil ) + kerstenNum*dlambda_wet_dWat - dThermalC_dNrg(iLayer) = kerstenNum*dlambda_wet_dTk - - ! ** mixture of constituents - case(mixConstit) - mLayerThermalC(iLayer) = thCond_soil(iLayer) * ( 1._rkind - theta_sat(iLayer) ) + & ! soil component - lambda_ice * nodeVolFracIce(iLayer) + & ! ice component - lambda_water * nodeVolFracLiq(iLayer) + & ! liquid water component - lambda_air * ( theta_sat(iLayer) - (nodeVolFracIce(iLayer) + nodeVolFracLiq(iLayer)) ) ! air component - ! compute derivatives - dThermalC_dWat(iLayer) = lambda_ice*dVolFracIce_dWat + lambda_water*dVolFracLiq_dWat + lambda_air*(-dVolFracIce_dWat - dVolFracLiq_dWat) - dThermalC_dNrg(iLayer) = (lambda_ice - lambda_water) * dVolFracIce_dTk - - ! ** test case for the mizoguchi lab experiment, Hansson et al. VZJ 2004 - case(hanssonVZJ) - fArg = 1._rkind + f1*nodeVolFracIce(iLayer)**f2 - xArg = nodeVolFracLiq(iLayer) + fArg*nodeVolFracIce(iLayer) - dxArg_dWat = dVolFracLiq_dWat + dVolFracIce_dWat * (1._rkind + f1*(f2+1)*nodeVolFracIce(iLayer)**f2) - dxArg_dTk = dVolFracIce_dTk * f1*(f2+1)*nodeVolFracIce(iLayer)**f2 - ! ...and, compute the thermal conductivity - mLayerThermalC(iLayer) = c1 + c2*xArg + (c1 - c4)*exp(-(c3*xArg)**c5) - - ! compute derivatives - dThermalC_dWat(iLayer) = ( c2 - c5*c3*(c3*xArg)**(c5-1)*(c1 - c4)*exp(-(c3*xArg)**c5) ) * dxArg_dWat - dThermalC_dNrg(iLayer) = ( c2 - c5*c3*(c3*xArg)**(c5-1)*(c1 - c4)*exp(-(c3*xArg)**c5) ) * dxArg_dTk - - ! ** check - case default; err=20; message=trim(message)//'unable to identify option for thermal conductivity of soil'; return - - end select ! option for the thermal conductivity of soil - - ! ***** snow - case(iname_snow) - dVolFracIce_dWat = ( 1._rkind - fracLiqSnow(iLayer) )*(iden_water/iden_ice) - dVolFracIce_dTk = -dTheta_dTk(iLayer)*(iden_water/iden_ice) - - ! temporally constant thermal conductivity - if(ixThCondSnow==Smirnova2000)then - mLayerThermalC(iLayer) = fixedThermalCond_snow - dThermalC_dWat(iLayer) = 0._rkind - dThermalC_dNrg(iLayer) = 0._rkind - ! thermal conductivity as a function of snow density - else - call tcond_snow(nodeVolFracIce(iLayer)*iden_ice, & ! input: snow density (kg m-3) - mLayerThermalC(iLayer), & ! output: thermal conductivity (W m-1 K-1) - err,cmessage) ! output: error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - select case(ixThCondSnow) - case(Yen1965) - dThermalC_dWat(iLayer) = 2._rkind * 3.217d-6 * nodeVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dWat - dThermalC_dNrg(iLayer) = 2._rkind * 3.217d-6 * nodeVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dTk - case(Mellor1977) - dThermalC_dWat(iLayer) = 2._rkind * 2.576d-6 * nodeVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dWat - dThermalC_dNrg(iLayer) = 2._rkind * 2.576d-6 * nodeVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dTk - case(Jordan1991) - dThermalC_dWat(iLayer) = ( 7.75d-5 + 2._rkind * 1.105d-6 * nodeVolFracIce(iLayer) * iden_ice ) * (lambda_ice-lambda_air) * iden_ice * dVolFracIce_dWat - dThermalC_dNrg(iLayer) = ( 7.75d-5 + 2._rkind * 1.105d-6 * nodeVolFracIce(iLayer) * iden_ice ) * (lambda_ice-lambda_air) * iden_ice * dVolFracIce_dTk - end select ! option for the thermal conductivity of snow - end if - - ! * error check - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute thermal conductivity'; return - - end select - - end do ! looping through layers - - ! ***** - ! * compute the thermal conductivity of snow at the interface of each layer... - ! **************************************************************************** - if (ixLayerDesired==0) then - ! special case of hansson - if(ixThCondSoil==hanssonVZJ)then - iLayerThermalC = 28._rkind*(0.5_rkind*(node_iHeight(ixLower) - node_iHeight(ixUpper))) ! these are indices 1,0 since was passed with 0:1 - dThermalC_dHydStateBelow = 0._rkind - dThermalC_dNrgStateBelow = 0._rkind - else - iLayerThermalC = mLayerThermalC(ixUpper) ! index was passed with 1:1 - dThermalC_dHydStateBelow = dThermalC_dWat(ixUpper) - dThermalC_dNrgStateBelow = dThermalC_dNrg(ixUpper) - end if - dThermalC_dHydStateAbove = realMissing - dThermalC_dNrgStateAbove = realMissing - else if (ixLayerDesired==nLayers ) then - ! assume the thermal conductivity at the domain boundaries is equal to the thermal conductivity of the layer - iLayerThermalC = mLayerThermalC(ixLower) ! index was passed with iLayers:iLayers - dThermalC_dHydStateAbove = dThermalC_dWat(ixLower) - dThermalC_dNrgStateAbove = dThermalC_dNrg(ixLower) - dThermalC_dHydStateBelow = realMissing - dThermalC_dNrgStateBelow = realMissing - else - ! get temporary variables - TCn = mLayerThermalC(ixUpper) ! thermal conductivity below the layer interface (W m-1 K-1) - TCp = mLayerThermalC(ixLower) ! thermal conductivity above the layer interface (W m-1 K-1) - zdn = node_iHeight(ixUpper) - nodeHeight(ixUpper) ! height difference between interface and lower value (m) - zdp = nodeHeight(ixLower) - node_iHeight(ixUpper) ! height difference between interface and upper value (m) - den = TCn*zdp + TCp*zdn ! denominator - ! compute thermal conductivity - if(TCn+TCp > epsilon(TCn))then - iLayerThermalC = (TCn*TCp*(zdn + zdp)) / den - dThermalC_dHydStateBelow = ( TCn*(zdn + zdp) - iLayerThermalC*zdn ) / den * dThermalC_dWat(ixLower) - dThermalC_dHydStateAbove = ( TCp*(zdn + zdp) - iLayerThermalC*zdp ) / den * dThermalC_dWat(ixUpper) - dThermalC_dNrgStateBelow = ( TCn*(zdn + zdp) - iLayerThermalC*zdn ) / den * dThermalC_dNrg(ixLower) - dThermalC_dNrgStateAbove = ( TCp*(zdn + zdp) - iLayerThermalC*zdp ) / den * dThermalC_dNrg(ixUpper) - else - iLayerThermalC = (TCn*zdn + TCp*zdp) / (zdn + zdp) - dThermalC_dHydStateBelow = zdp / (zdn + zdp) * dThermalC_dWat(ixLower) - dThermalC_dHydStateAbove = zdn / (zdn + zdp) * dThermalC_dWat(ixUpper) - dThermalC_dNrgStateBelow = zdp / (zdn + zdp) * dThermalC_dNrg(ixLower) - dThermalC_dNrgStateAbove = zdn / (zdn + zdp) * dThermalC_dNrg(ixUpper) - end if - endif - -end subroutine iLayerThermalConduct - - - end module ssdNrgFlux_module diff --git a/build/source/engine/summaSolve.f90 b/build/source/engine/summaSolve.f90 index 685352eba..76e2adc05 100644 --- a/build/source/engine/summaSolve.f90 +++ b/build/source/engine/summaSolve.f90 @@ -104,7 +104,7 @@ subroutine summaSolve(& fScale, & ! intent(in): function scaling vector xScale, & ! intent(in): "variable" scaling vector, i.e., for state variables rVec, & ! intent(in): residual vector - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout): diagonal matrix (excludes flux derivatives) fOld, & ! intent(in): old function evaluation ! input: data structures @@ -156,7 +156,7 @@ subroutine summaSolve(& real(rkind),intent(in) :: fScale(:) ! function scaling vector real(rkind),intent(in) :: xScale(:) ! "variable" scaling vector, i.e., for state variables real(rkind),intent(in) :: rVec(:) ! NOTE: qp ! residual vector - real(rkind),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + real(rkind),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(rkind),intent(inout) :: dMat(:) ! diagonal matrix (excludes flux derivatives) real(rkind),intent(in) :: fOld ! old function evaluation ! input: data structures @@ -940,7 +940,7 @@ subroutine eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err ! input: state vectors stateVecNew, & ! intent(in): updated model state vector fScale, & ! intent(in): function scaling vector - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures model_decisions, & ! intent(in): model decisions lookup_data, & ! intent(in): lookup tables diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 94a090149..366617102 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -381,7 +381,7 @@ subroutine systemSolv(& ! input: state vectors stateVecTrial, & ! intent(in): model state vector fScale, & ! intent(in): function scaling vector - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures model_decisions, & ! intent(in): model decisions lookup_data, & ! intent(in): lookup tables @@ -473,7 +473,7 @@ subroutine systemSolv(& fScale, & ! intent(in): function scaling vector xScale, & ! intent(in): "variable" scaling vector, i.e., for state variables rVec, & ! intent(in): residual vector - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout): diagonal matrix (excludes flux derivatives) fOld, & ! intent(in): old function evaluation ! input: data structures diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 index fae6278b5..71b1a318b 100644 --- a/build/source/engine/updateVars.f90 +++ b/build/source/engine/updateVars.f90 @@ -53,13 +53,8 @@ module updateVars_module ! constants USE multiconst,only:& - gravity, & ! acceleration of gravity (m s-2) Tfreeze, & ! temperature at freezing (K) - Cp_air, & ! specific heat of air (J kg-1 K-1) - Cp_ice, & ! specific heat of ice (J kg-1 K-1) - Cp_water, & ! specific heat of liquid water (J kg-1 K-1) LH_fus, & ! latent heat of fusion (J kg-1) - iden_air, & ! intrinsic density of air (kg m-3) iden_ice, & ! intrinsic density of ice (kg m-3) iden_water ! intrinsic density of liquid water (kg m-3) @@ -252,11 +247,6 @@ subroutine updateVars(& mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature ! derivatives inside solver for Jacobian only - dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. temperature mLayerdTemp_dt => deriv_data%var(iLookDERIV%mLayerdTemp_dt )%dat ,& ! intent(out): [dp(:)] timestep change in layer temperature scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt )%dat(1) & ! intent(out): [dp ] timestep change in canopy temperature ) ! association with variables in the data structures @@ -408,45 +398,28 @@ subroutine updateVars(& ! compute the derivative in total water content w.r.t. total water matric potential (m-1) ! NOTE 1: valid for frozen and unfrozen conditions ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi - select case(ixDomainType) - case(iname_veg) - fLiq = fracLiquid(xTemp,snowfrz_scale) - dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) - case(iname_snow) - fLiq = fracLiquid(xTemp,snowfrz_scale) - dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air - case(iname_soil) - select case( ixStateType(ixFullVector) ) - case(iname_lmpLayer); dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore - case default; dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - end select - ! dVolHtCapBulk_dPsi0 uses the derivative in water retention curve above critical temp w.r.t.state variable dVolTot_dPsi0 - dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use - if(xTemp< Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) - if(xTemp>=Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_water * Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) - end select + if(ixDomainType==iname_soil)then + select case( ixStateType(ixFullVector) ) + case(iname_lmpLayer); dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + case default; dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + end select + endif ! compute the derivative in liquid water content w.r.t. temperature ! --> partially frozen: dependence of liquid water on temperature if(xTemp unfrozen: no dependence of liquid water on temperature else select case(ixDomainType) - case(iname_veg); dTheta_dTkCanopy = 0._rkind; dVolHtCapBulk_dTkCanopy = 0._rkind - case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind + case(iname_veg); dTheta_dTkCanopy = 0._rkind + case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return end select ! domain type endif diff --git a/build/source/engine/updateVarsSundials.f90 b/build/source/engine/updateVarsSundials.f90 index 29d496ae0..655446a91 100644 --- a/build/source/engine/updateVarsSundials.f90 +++ b/build/source/engine/updateVarsSundials.f90 @@ -53,13 +53,8 @@ module updateVarsSundials_module ! constants USE multiconst,only:& - gravity, & ! acceleration of gravity (m s-2) Tfreeze, & ! temperature at freezing (K) - Cp_air, & ! specific heat of air (J kg-1 K-1) - Cp_ice, & ! specific heat of ice (J kg-1 K-1) - Cp_water, & ! specific heat of liquid water (J kg-1 K-1) LH_fus, & ! latent heat of fusion (J kg-1) - iden_air, & ! intrinsic density of air (kg m-3) iden_ice, & ! intrinsic density of ice (kg m-3) iden_water ! intrinsic density of liquid water (kg m-3) @@ -280,11 +275,6 @@ subroutine updateVarsSundials(& dFracLiqSnow_dTk => deriv_data%var(iLookDERIV%dFracLiqSnow_dTk )%dat ,& ! intent(out): [dp(:)] derivative in fraction of liquid snow w.r.t. temperature dFracLiqVeg_dTkCanopy => deriv_data%var(iLookDERIV%dFracLiqVeg_dTkCanopy )%dat(1) ,& ! intent(out): [dp ] derivative in fraction of (throughfall + drainage) w.r.t. temperature ! derivatives inside solver for Jacobian only - dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. temperature d2VolTot_d2Psi0 => deriv_data%var(iLookDERIV%d2VolTot_d2Psi0 )%dat ,& ! intent(out): [dp(:)] second derivative in total water content w.r.t. total water matric potential mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2 )%dat ,& ! intent(out): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature d2Theta_dTkCanopy2 => deriv_data%var(iLookDERIV%d2Theta_dTkCanopy2 )%dat(1) & ! intent(out): [dp ] second derivative of volumetric liquid water content w.r.t. temperature @@ -452,41 +442,21 @@ subroutine updateVarsSundials(& ! compute the derivative in bulk heat capacity w.r.t. total water content or water matric potential (m-1) ! compute the derivative in total water content w.r.t. total water matric potential (m-1) ! NOTE 1: valid for frozen and unfrozen conditions - ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi, dVolHtCapBulk_dPsi0 may be wrong - select case(ixDomainType) - case(iname_veg) - if(computJac)then - fLiq = fracLiquid(xTemp,snowfrz_scale) - dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) - endif - - case(iname_snow) - - if(computJac)then - fLiq = fracLiquid(xTemp,snowfrz_scale) - dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air - endif - case(iname_soil) - select case( ixStateType(ixFullVector) ) - case(iname_lmpLayer) - dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore - if(computJac) d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore - case default - dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - if(computJac) d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),& + ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi + if(ixDomainType==iname_soil)then + select case( ixStateType(ixFullVector) ) + case(iname_lmpLayer) + dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + if(computJac) d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + case default + dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + if(computJac) d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),& vGn_n(ixControlIndex),vGn_m(ixControlIndex)) end select - ! dVolHtCapBulk_dPsi0 uses the derivative in water retention curve above critical temp w.r.t.state variable dVolTot_dPsi0 - if(computJac)then - dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use - if(xTemp< Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) - if(xTemp>=Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_water * Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) - endif - end select + endif ! compute the derivative in liquid water content w.r.t. temperature ! --> partially frozen: dependence of liquid water on temperature - ! compute the derivative in bulk heat capacity w.r.t. temperature if(xTemp unfrozen: no dependence of liquid water on temperature else select case(ixDomainType) - case(iname_veg); dTheta_dTkCanopy = 0._rkind; d2Theta_dTkCanopy2 = 0._rkind; dFracLiqVeg_dTkCanopy = 0._rkind; dVolHtCapBulk_dTkCanopy = 0._rkind - case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind; mLayerd2Theta_dTk2(iLayer) = 0._rkind; dFracLiqSnow_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind + case(iname_veg); dTheta_dTkCanopy = 0._rkind; d2Theta_dTkCanopy2 = 0._rkind; dFracLiqVeg_dTkCanopy = 0._rkind + case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind; mLayerd2Theta_dTk2(iLayer) = 0._rkind; dFracLiqSnow_dTk(iLayer) = 0._rkind end select ! domain type endif From b69592b0c1e2d528d8af9922ddd2fc5d8a931854 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 30 Nov 2022 23:36:20 +0900 Subject: [PATCH 0440/1472] Put derivatives in enthalpy calculation so can use in computHeatCap. Only call the enthalpy function, t2enthalpy, if using howHeatCap = enthalpyFD. Currently have this in the Summa BE routines too, but may want to remove it. The only function using the phase change in enthalpy is the nrg check in varSubstepSundials, and this is turned off right now. This part needs more work. Use howHeatCap = closedForm. --- build/source/driver/summa_setup.f90 | 11 +- build/source/engine/computHeatCap.f90 | 96 ++-- build/source/engine/eval8summa.f90 | 91 ++-- build/source/engine/eval8summaSundials.f90 | 80 ++-- build/source/engine/mDecisions.f90 | 10 +- build/source/engine/opSplittin.f90 | 4 +- build/source/engine/systemSolv.f90 | 96 +++- build/source/engine/systemSolvSundials.f90 | 43 +- build/source/engine/t2enthalpy.f90 | 519 ++++++++------------- build/source/engine/updateVars.f90 | 5 +- build/source/engine/updateVarsSundials.f90 | 2 +- build/source/engine/varSubstepSundials.f90 | 37 +- 12 files changed, 488 insertions(+), 506 deletions(-) diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index 7f8365e1b..6b1765586 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -39,10 +39,9 @@ module summa_setup ! metadata structures USE globalData,only:mpar_meta,bpar_meta ! parameter metadata structures -! look-up values for the numerical method -USE mDecisions_module,only: & - bEuler, & ! home-grown backward Euler solution with long time steps - sundials ! SUNDIALS/IDA solution +! look-up values for the choice of heat capacity computation +USE mDecisions_module,only: & + enthalpyFD ! heat capacity using enthalpy ! named variables to define the decisions for snow layers USE mDecisions_module,only:& @@ -297,8 +296,8 @@ subroutine summa_paramSetup(summa1_struc, err, message) call E2T_lookup(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! calculate a lookup table to compute enthalpy from temperature, only for sundials - if(model_decisions(iLookDECISIONS%num_method)%iDecision == sundials)then + ! calculate a lookup table to compute enthalpy from temperature, only for enthalpyFD + if(model_decisions(iLookDECISIONS%howHeatCap)%iDecision == enthalpyFD)then call T2E_lookup(gru_struc(iGRU)%hruInfo(iHRU)%nSoil, & ! intent(in): number of soil layers mparStruct%gru(iGRU)%hru(iHRU), & ! intent(in): parameter data structure lookupStruct%gru(iGRU)%hru(iHRU), & ! intent(inout): lookup table data structure diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index be2b0f281..2e8616bc1 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -107,8 +107,12 @@ subroutine computHeatCap(& mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + dCanEnthalpy_dTk, & ! intent(in): derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat, & ! intent(in): derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk, & ! intent(in): derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat, & ! intent(in): derivatives in layer enthalpy w.r.t. water state ! output - heatCapVeg, & + heatCapVeg, & ! intent(out): heat capacity for canopy mLayerHeatCap, & ! intent(out): heat capacity for snow and soil ! output: error control err,message) ! intent(out): error control @@ -117,22 +121,16 @@ subroutine computHeatCap(& USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists ! -------------------------------------------------------------------------------------------------------------------------------- ! input: control variables - logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) - ! input: pre-computed derivatives - real(rkind),intent(in) :: dTheta_dTkCanopy ! derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: scalarFracLiqVeg ! fraction of canopy liquid water (-) - real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative of volumetric liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) - real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) + integer(i4b),intent(in) :: nLayers ! number of layers (soil+snow) + logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) ! input/output: data structures - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model layer indices - ! input: - integer(i4b),intent(in) :: nLayers - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) - real(rkind),intent(in) :: scalarCanopyLiquid + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model layer indices + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + ! input: state variables + real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) + real(rkind),intent(in) :: scalarCanopyLiquid ! trial value for the liquid water on the vegetation canopy (kg m-2) real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature real(rkind),intent(in) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) real(rkind),intent(in) :: scalarCanopyEnthalpyPrev ! intent(in): previous enthalpy of the vegetation canopy (J m-3) @@ -144,11 +142,21 @@ subroutine computHeatCap(& real(rkind),intent(in) :: mLayerEnthalpyTrial(:) ! trial enthalpy for snow and soil real(rkind),intent(in) :: mLayerEnthalpyPrev(:) ! previous enthalpy for snow and soil real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! vector of total water matric potential (m) + ! input: pre-computed derivatives + real(rkind),intent(in) :: dTheta_dTkCanopy ! derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: scalarFracLiqVeg ! fraction of canopy liquid water (-) + real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative of volumetric liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) + real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) + real(rkind),intent(in) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature + real(rkind),intent(in) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state + real(rkind),intent(in) :: dEnthalpy_dTk(:) ! derivatives in layer enthalpy w.r.t. temperature + real(rkind),intent(in) :: dEnthalpy_dWat(:) ! derivatives in layer enthalpy w.r.t. water state ! output: - real(qp),intent(out) :: heatCapVeg + real(qp),intent(out) :: heatCapVeg ! heat capacity for canopy real(qp),intent(out) :: mLayerHeatCap(:) ! heat capacity for snow and soil - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables integer(i4b) :: iLayer ! index of model layer @@ -190,7 +198,7 @@ subroutine computHeatCap(& Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component Cp_ice*scalarCanopyIce/canopyDepth ! ice component - !derivatives + ! derivatives fLiq = scalarFracLiqVeg dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) if(scalarCanopyTempTrial < Tfreeze)then @@ -202,9 +210,9 @@ subroutine computHeatCap(& else delEnt = scalarCanopyEnthalpyTrial - scalarCanopyEnthalpyPrev heatCapVeg = delEnt / delT - ! NEED TO COMPUTE THESE STILL - dVolHtCapBulk_dCanWat = 0._rkind ! dCanEnthalpy_dWat / delT - dVolHtCapBulk_dTkCanopy = 0._rkind ! ( dCanEnthalpy_dTk - delEnt/delT ) / delT + ! derivatives + dVolHtCapBulk_dCanWat = dCanEnthalpy_dWat / delT + dVolHtCapBulk_dTkCanopy = ( dCanEnthalpy_dTk - delEnt/delT ) / delT endif endif @@ -221,7 +229,7 @@ subroutine computHeatCap(& iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component - !derivatives + ! derivatives dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use Tcrit = crit_soilT( mLayerMatricHeadTrial(iSoil) ) if( mLayerTempTrial(iLayer) < Tcrit)then @@ -236,7 +244,7 @@ subroutine computHeatCap(& mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component - !derivatives + ! derivatives fLiq = mLayerFracLiqSnow(iLayer) dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air if( mLayerTempTrial(iLayer) < Tfreeze)then @@ -250,15 +258,15 @@ subroutine computHeatCap(& else delEnt = mLayerEnthalpyTrial(iLayer) - mLayerEnthalpyPrev(iLayer) mLayerHeatCap(iLayer) = delEnt / delT - ! NEED TO COMPUTE THESE STILL + ! derivatives if(iLayer>nSnow)then iSoil = iLayer-nSnow dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use - dVolHtCapBulk_dPsi0(iSoil) = 0._rkind ! dEnthalpy_dWat(iLayer) / delT + dVolHtCapBulk_dPsi0(iSoil) = dEnthalpy_dWat(iLayer) / delT else - dVolHtCapBulk_dTheta(iLayer) = 0._rkind ! dEnthalpy_dWat(iLayer) / delT + dVolHtCapBulk_dTheta(iLayer) = dEnthalpy_dWat(iLayer) / delT endif - dVolHtCapBulk_dTk(iLayer) = 0._rkind ! ( dEnthalpy_dTk(iLayer) - delEnt/delT ) / delT + dVolHtCapBulk_dTk(iLayer) = ( dEnthalpy_dTk(iLayer) - delEnt/delT ) / delT endif end do ! looping through layers @@ -389,8 +397,8 @@ subroutine computHeatCapAnalytic(& indx_data, & ! intent(in): model layer indices diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! output - heatCapVeg, & - mLayerHeatCap, & + heatCapVeg, & ! intent(out): heat capacity for canopy + mLayerHeatCap, & ! intent(out): heat capacity for snow and soil ! output: error control err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------------- @@ -407,21 +415,19 @@ subroutine computHeatCapAnalytic(& real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) real(rkind),intent(in) :: mLayerTemp(:) ! vector of temperature (-) real(rkind),intent(in) :: mLayerMatricHead(:) ! vector of total water matric potential (m) - ! input: pre-computed derivatives real(rkind),intent(in) :: dTheta_dTkCanopy ! derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) real(rkind),intent(in) :: scalarFracLiqVeg ! fraction of canopy liquid water (-) real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative of volumetric liquid water content w.r.t. temperature (K-1) real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) - ! input/output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model layer indices type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU ! output: error control - real(qp),intent(out) :: heatCapVeg - real(qp),intent(out) :: mLayerHeatCap(:) + real(qp),intent(out) :: heatCapVeg ! heat capacity for canopy + real(qp),intent(out) :: mLayerHeatCap(:) ! heat capacity for snow and soil integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- @@ -464,7 +470,7 @@ subroutine computHeatCapAnalytic(& Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component Cp_ice*scalarCanopyIce/canopyDepth ! ice component - !derivatives + ! derivatives fLiq = scalarFracLiqVeg dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) if(scalarCanopyTemp < Tfreeze)then @@ -491,7 +497,7 @@ subroutine computHeatCapAnalytic(& iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component - !derivatives + ! derivatives dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use Tcrit = crit_soilT( mLayerMatricHead(iSoil) ) if( mLayerTemp(iLayer) < Tcrit)then @@ -506,7 +512,7 @@ subroutine computHeatCapAnalytic(& mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component - !derivatives + ! derivatives fLiq = mLayerFracLiqSnow(iLayer) dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air if( mLayerTemp(iLayer) < Tfreeze)then @@ -532,12 +538,12 @@ subroutine computCm(& ! input: control variables computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux ! input: state variables - scalarCanopyTemp, & ! intent(in) - mLayerTemp, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - mLayerMatricHead, & ! intent(in) + scalarCanopyTemp, & ! intent(in): value of canopy temperature (kg m-2) + mLayerTemp, & ! intent(in): vector of temperature (-) + mLayerMatricHead, & ! intent(in): vector of total water matric potential (m) ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices ! output scalarCanopyCm, & ! intent(out): Cm for vegetation mLayerCm, & ! intent(out): Cm for soil and snow @@ -556,8 +562,8 @@ subroutine computCm(& type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model layer indices ! output: error control - real(qp),intent(out) :: scalarCanopyCm - real(qp),intent(out) :: mLayerCm(:) + real(qp),intent(out) :: scalarCanopyCm ! Cm for vegetation + real(qp),intent(out) :: mLayerCm(:) ! Cm for soil and snow integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index fa7864942..5ebec90ea 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -79,23 +79,11 @@ module eval8summa_module USE var_lookup,only:iLookFLUX ! named variables for structure elements USE var_lookup,only:iLookDERIV ! named variables for structure elements -! look-up values for the choice of groundwater representation (local-column, or single-basin) +! look-up values for the choice of heat capacity computation USE mDecisions_module,only: & - localColumn, & ! separate groundwater representation in each local soil column - singleBasin, & ! single groundwater store over the entire basin + closedForm, & ! heat capacity using closed form, not using enthalpy enthalpyFD ! heat capacity using enthalpy -! look-up values for the choice of groundwater parameterization -USE mDecisions_module,only: & - qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization - bigBucket, & ! a big bucket (lumped aquifer model) - noExplicit ! no explicit groundwater parameterization - -! look-up values for the form of Richards' equation -USE mDecisions_module,only: & - moisture, & ! moisture-based form of Richards' equation - mixdform ! mixed form of Richards' equation - implicit none private public::eval8summa @@ -220,9 +208,13 @@ subroutine eval8summa(& real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) + ! enthalpy real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector of enthalpy for snow+soil layers (J m-3) - + real(rkind) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature + real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state + real(rkind),dimension(nLayers) :: dEnthalpy_dTk ! derivatives in layer enthalpy w.r.t. temperature + real(rkind),dimension(nLayers) :: dEnthalpy_dWat ! derivatives in layer enthalpy w.r.t. water state ! other local variables integer(i4b) :: iLayer ! index of model layer in the snow+soil domain integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain @@ -244,6 +236,7 @@ subroutine eval8summa(& ! -------------------------------------------------------------------------------------------------------------------------------- associate(& ! model decisions + ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation ! snow parameters snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) @@ -445,32 +438,44 @@ subroutine eval8summa(& if(updateCp)then ! *** compute volumetric heat capacity C_p - if(model_decisions(iLookDECISIONS%howHeatCap)%iDecision == enthalpyFD)then - ! compute H_T + if(ixHowHeatCap == enthalpyFD)then + ! compute H_T without phase change call t2enthalpy(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) - ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,cmessage) ! intent(out): error control + .false., & ! intent(in): logical flag to not include phase change in enthalpy + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) + ! input: pre-computed derivatives + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + ! output: enthalpy + scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state + ! output: error control + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + ! *** compute volumetric heat capacity C_p = dH_T/dT call computHeatCap(& ! input: control variables @@ -501,16 +506,20 @@ subroutine eval8summa(& mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + dCanEnthalpy_dTk, & ! intent(in): derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat, & ! intent(in): derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk, & ! intent(in): derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat, & ! intent(in): derivatives in layer enthalpy w.r.t. water state ! output heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! update values mLayerEnthalpy = mLayerEnthalpyTrial scalarCanopyEnthalpy = scalarCanopyEnthalpyTrial - else ! if using closed formula of heat capacity, model_decisions(iLookDECISIONS%howHeatCap)%iDecision == closedForm + else if(ixHowHeatCap == closedForm)then call computHeatCapAnalytic(& ! input: control variables computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux @@ -537,7 +546,7 @@ subroutine eval8summa(& heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control endif ! compute multiplier of state vector diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index 1a80c0971..f0742a21c 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -61,23 +61,11 @@ module eval8summaSundials_module USE var_lookup,only:iLookFLUX ! named variables for structure elements USE var_lookup,only:iLookDERIV ! named variables for structure elements -! look-up values for the choice of groundwater representation (local-column, or single-basin) +! look-up values for the choice of heat capacity computation USE mDecisions_module,only: & - localColumn, & ! separate groundwater representation in each local soil column - singleBasin, & ! single groundwater store over the entire basin + closedForm, & ! heat capacity using closed form, not using enthalpy enthalpyFD ! heat capacity using enthalpy -! look-up values for the choice of groundwater parameterization -USE mDecisions_module,only: & - qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization - bigBucket, & ! a big bucket (lumped aquifer model) - noExplicit ! no explicit groundwater parameterization - -! look-up values for the form of Richards' equation -USE mDecisions_module,only: & - moisture, & ! moisture-based form of Richards' equation - mixdform ! mixed form of Richards' equation - implicit none private public::eval8summaSundials @@ -158,7 +146,7 @@ subroutine eval8summaSundials(& ! provide access to subroutines USE getVectorz_module, only:varExtract ! extract variables from the state vector USE updateVarsSundials_module, only:updateVarsSundials ! update variables - USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy + USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy USE computFlux_module, only:soilCmpresSundials ! compute soil compression USE computFlux_module, only:computFlux ! compute fluxes given a state vector USE computHeatCap_module,only:computHeatCap ! recompute heat capacity and derivatives @@ -167,8 +155,6 @@ subroutine eval8summaSundials(& USE computHeatCap_module, only:computStatMult ! recompute state multiplier USE computResidSundials_module,only:computResidSundials ! compute residuals given a state vector USE computThermConduct_module,only:computThermConduct ! recompute thermal conductivity and derivatives - USE computEnthalpy_module,only:computEnthalpy - USE computEnthalpy_module,only:computEnthalpyPrime implicit none ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- @@ -241,10 +227,10 @@ subroutine eval8summaSundials(& ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- - real(rkind) :: dt1 ! residual step size + real(rkind) :: dt1 ! residual step size ! state variables - real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) ! derivative of state variables real(rkind) :: scalarCanairTempPrime ! derivative value for temperature of the canopy air space (K) real(rkind) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) @@ -260,26 +246,31 @@ subroutine eval8summaSundials(& real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! derivative value for volumetric fraction of liquid water (-) real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! derivative value for volumetric fraction of ice (-) ! enthalpy - real(rkind) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthalpyPrime ! enthalpy of each snow+soil layer (J m-3) + real(rkind) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpyPrime ! enthalpy of each snow+soil layer (J m-3) + real(rkind) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature + real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state + real(rkind),dimension(nLayers) :: dEnthalpy_dTk ! derivatives in layer enthalpy w.r.t. temperature + real(rkind),dimension(nLayers) :: dEnthalpy_dWat ! derivatives in layer enthalpy w.r.t. water state ! other local variables - integer(i4b) :: iLayer ! index of model layer in the snow+soil domain - integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain - integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine - integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) - real(rkind) :: xMin,xMax ! minimum and maximum values for water content - real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) - character(LEN=256) :: cmessage ! error message of downwind routine - real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy - real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil - logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step - logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m + integer(i4b) :: iLayer ! index of model layer in the snow+soil domain + integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain + integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine + integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) + real(rkind) :: xMin,xMax ! minimum and maximum values for water content + real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) + character(LEN=256) :: cmessage ! error message of downwind routine + real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy + real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil + logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step + logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m ! -------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- associate(& ! model decisions + ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation ! snow parameters snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) @@ -519,9 +510,10 @@ subroutine eval8summaSundials(& if(updateCp)then ! *** compute volumetric heat capacity C_p - if(model_decisions(iLookDECISIONS%howHeatCap)%iDecision == enthalpyFD)then - ! compute H_T - call t2enthalpy_T(& + if(ixHowHeatCap == enthalpyFD)then + ! compute H_T without phase change + call t2enthalpy(& + .false., & ! intent(in): logical flag to not include phase change in enthalpy ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure @@ -537,10 +529,20 @@ subroutine eval8summaSundials(& mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) + ! input: pre-computed derivatives + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -575,6 +577,10 @@ subroutine eval8summaSundials(& mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + dCanEnthalpy_dTk, & ! intent(in): derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat, & ! intent(in): derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk, & ! intent(in): derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat, & ! intent(in): derivatives in layer enthalpy w.r.t. water state ! output heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil @@ -588,7 +594,7 @@ subroutine eval8summaSundials(& mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur end do endif ! if dt_cur is not too small - else ! if using closed formula of heat capacity, model_decisions(iLookDECISIONS%howHeatCap)%iDecision == closedForm + else if(ixHowHeatCap == closedForm)then call computHeatCapAnalytic(& ! input: control variables computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index 7361dcad3..60f8c6b0e 100644 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -401,13 +401,13 @@ subroutine mDecisions(err,message) err=10; message=trim(message)//"unknown numerical method [option="//trim(model_decisions(iLookDECISIONS%num_method)%cDecision)//"]"; return end select - ! how to compute heat capacity in energy equation, only has an effect if num_method==sundials. Choice enthalpyFD has better coincidence of energy conservation with sundials tolerance. + ! how to compute heat capacity in energy equation, choice enthalpyFD has better coincidence of energy conservation with sundials tolerance. select case(trim(model_decisions(iLookDECISIONS%howHeatCap)%cDecision)) - case('enthalpyFD'); model_decisions(iLookDECISIONS%howHeatCap)%iDecision = enthalpyFD ! enthalpyFD - case('closedForm'); model_decisions(iLookDECISIONS%howHeatCap)%iDecision = closedForm ! closedForm + case('enthalpyFD'); model_decisions(iLookDECISIONS%howHeatCap)%iDecision = enthalpyFD ! heat capacity using enthalpy + case('closedForm'); model_decisions(iLookDECISIONS%howHeatCap)%iDecision = closedForm ! heat capacity using closed form, not using enthalpy case default - if (model_decisions(iLookDECISIONS%num_method)%iDecision==bEuler)then - model_decisions(iLookDECISIONS%howHeatCap)%iDecision = closedForm ! not used (included for backwards compatibility) + if (trim(model_decisions(iLookDECISIONS%num_method)%cDecision)=='itertive')then + model_decisions(iLookDECISIONS%howHeatCap)%iDecision = closedForm ! included for backwards compatibility else err=10; message=trim(message)//"unknown Cp computation [option="//trim(model_decisions(iLookDECISIONS%howHeatCap)%cDecision)//"]"; return endif diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 1543b589c..19f1c604f 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -784,9 +784,7 @@ subroutine opSplittin(& tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt dt_out, & ! intent(out) err,cmessage) ! intent(out) : error code and error message - - dt = dt_out - + dt = dt_out case(bEuler) call varSubstep(& ! input: model control diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 366617102..69c17e996 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -72,6 +72,7 @@ module systemSolv_module USE var_lookup,only:iLookPARAM ! named variables for structure elements USE var_lookup,only:iLookINDEX ! named variables for structure elements USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure +USE var_lookup,only:iLookDERIV ! named variables for structure elements ! provide access to the derived types to define the data structures USE data_types,only:& @@ -82,10 +83,14 @@ module systemSolv_module zLookup, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions +! look-up values for the choice of heat capacity computation +USE mDecisions_module,only: & + enthalpyFD ! heat capacity using enthalpy + ! look-up values for the choice of groundwater representation (local-column, or single-basin) -USE mDecisions_module,only: & - localColumn, & ! separate groundwater representation in each local soil column - singleBasin ! single groundwater store over the entire basin +USE mDecisions_module,only: & + localColumn, & ! separate groundwater representation in each local soil column + singleBasin ! single groundwater store over the entire basin ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: & @@ -149,6 +154,7 @@ subroutine systemSolv(& USE summaSolve_module,only:summaSolve ! calculate the iteration increment, evaluate the new state, and refine if necessary USE getVectorz_module,only:getScaling ! get the scaling vectors USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy + USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy implicit none ! --------------------------------------------------------------------------------------- ! * dummy variables @@ -204,12 +210,12 @@ subroutine systemSolv(& ! ------------------------------------------------------------------------------------------------------ ! * model solver ! ------------------------------------------------------------------------------------------------------ - logical(lgt),parameter :: forceFullMatrix=.false. ! flag to force the use of the full Jacobian matrix - integer(i4b) :: maxiter ! maximum number of iterations - integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) - integer(i4b) :: localMaxIter ! maximum number of iterations (depends on solution type) - integer(i4b), parameter :: scalarMaxIter=100 ! maximum number of iterations for the scalar solution - type(var_dlength) :: flux_init ! model fluxes at the start of the time step + logical(lgt),parameter :: forceFullMatrix=.false. ! flag to force the use of the full Jacobian matrix + integer(i4b) :: maxiter ! maximum number of iterations + integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) + integer(i4b) :: localMaxIter ! maximum number of iterations (depends on solution type) + integer(i4b), parameter :: scalarMaxIter=100 ! maximum number of iterations for the scalar solution + type(var_dlength) :: flux_init ! model fluxes at the start of the time step real(rkind),allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! NOTE: allocatable, since not always needed real(rkind) :: stateVecNew(nState) ! new state vector (mixed units) real(rkind) :: fluxVec0(nState) ! flux vector (mixed units) @@ -221,22 +227,46 @@ subroutine systemSolv(& real(rkind) :: rAdd(nState) ! additional terms in the residual vector real(rkind) :: fOld,fNew ! function values (-); NOTE: dimensionless because scaled real(rkind) :: xMin,xMax ! state minimum and maximum (mixed units) - logical(lgt) :: converged ! convergence flag - logical(lgt) :: feasible ! feasibility flag + logical(lgt) :: converged ! convergence flag + logical(lgt) :: feasible ! feasibility flag real(rkind) :: resSinkNew(nState) ! additional terms in the residual vector real(rkind) :: fluxVecNew(nState) ! new flux vector real(rkind) :: resVecNew(nState) ! NOTE: qp ! new residual vector + ! enthalpy derivatives + real(rkind) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature + real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state + real(rkind) :: dEnthalpy_dTk(nState) ! derivatives in layer enthalpy w.r.t. temperature + real(rkind) :: dEnthalpy_dWat(nState) ! derivatives in layer enthalpy w.r.t. water state ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- globalVars: associate(& ! model decisions + ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) + ! enthalpy + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) + ! derivatives, diagnositic for enthalpy + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + ! model state variables + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variables (snow and soil domains) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) ! check the need to merge snow layers - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) ! accelerate solution for temperature @@ -363,6 +393,44 @@ subroutine systemSolv(& if(ixCasNrg/=integerMissing) stateVecTrial(ixCasNrg) = stateVecInit(ixCasNrg) + (airtemp - stateVecInit(ixCasNrg))*tempAccelerate if(ixVegNrg/=integerMissing) stateVecTrial(ixVegNrg) = stateVecInit(ixVegNrg) + (airtemp - stateVecInit(ixVegNrg))*tempAccelerate + if(ixHowHeatCap == enthalpyFD)then + ! compute H_T at the beginning of the data step without phase change + call t2enthalpy(& + .false., & ! intent(in): logical flag to not include phase change in enthalpy + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTemp, & ! intent(in): value of canopy air temperature (K) + scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) + scalarCanopyWat, & ! intent(in): value of canopy total water (kg m-2) + scalarCanopyIce, & ! intent(in): value for canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTemp, & ! intent(in): vector of layer temperature (K) + mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) + mLayerMatricHead, & ! intent(in): vector of total water matric potential (m) + mLayerVolFracIce, & ! intent(in): vector of volumetric fraction of ice (-) + ! input: pre-computed derivatives + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + ! output: enthalpy + scalarCanairEnthalpy, & ! intent(out): temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) + dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + endif + ! compute the flux and the residual vector for a given state vector ! NOTE 1: The derivatives computed in eval8summa are used to calculate the Jacobian matrix for the first iteration ! NOTE 2: The Jacobian matrix together with the residual vector is used to calculate the first iteration increment diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index d63843bba..2651c459b 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -64,10 +64,14 @@ module systemSolvSundials_module zLookup, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions +! look-up values for the choice of heat capacity computation +USE mDecisions_module,only: & + enthalpyFD ! heat capacity using enthalpy + ! look-up values for the choice of groundwater representation (local-column, or single-basin) -USE mDecisions_module,only: & - localColumn, & ! separate groundwater representation in each local soil column - singleBasin ! single groundwater store over the entire basin +USE mDecisions_module,only: & + localColumn, & ! separate groundwater representation in each local soil column + singleBasin ! single groundwater store over the entire basin ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: & @@ -133,7 +137,7 @@ subroutine systemSolvSundials(& USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy USE tol4IDA_module,only:popTol4IDA ! pop tolerances USE summaSolveSundialsIDA_module,only:summaSolveSundialsIDA ! solve DAE by IDA - USE t2enthalpy_module, only:t2enthalpy_T ! compute enthalpy + USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy use, intrinsic :: iso_c_binding implicit none ! --------------------------------------------------------------------------------------- @@ -208,18 +212,30 @@ subroutine systemSolvSundials(& real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step real(rkind) :: fOld ! function values (-); NOTE: dimensionless because scaled + ! enthalpy derivatives + real(rkind) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature + real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state + real(rkind) :: dEnthalpy_dTk(nState) ! derivatives in layer enthalpy w.r.t. temperature + real(rkind) :: dEnthalpy_dWat(nState) ! derivatives in layer enthalpy w.r.t. water state ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- globalVars: associate(& ! model decisions + ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) ! enthalpy scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) + ! derivatives, diagnostic for enthalpy + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) ! soil parameters theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) @@ -343,8 +359,10 @@ subroutine systemSolvSundials(& if(ixCasNrg/=integerMissing) stateVecTrial(ixCasNrg) = stateVecInit(ixCasNrg) + (airtemp - stateVecInit(ixCasNrg))*tempAccelerate if(ixVegNrg/=integerMissing) stateVecTrial(ixVegNrg) = stateVecInit(ixVegNrg) + (airtemp - stateVecInit(ixVegNrg))*tempAccelerate - ! compute H_T at the beginning of the data step - call t2enthalpy_T(& + if(ixHowHeatCap == enthalpyFD)then + ! compute H_T at the beginning of the data step without phase change + call t2enthalpy(& + .false., & ! intent(in): logical flag to not include phase change in enthalpy ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure @@ -360,13 +378,24 @@ subroutine systemSolvSundials(& mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) mLayerMatricHead, & ! intent(in): vector of total water matric potential (m) mLayerVolFracIce, & ! intent(in): vector of volumetric fraction of ice (-) + ! input: pre-computed derivatives + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy scalarCanairEnthalpy, & ! intent(out): temperature component of enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpy, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) mLayerEnthalpy, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) + dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state ! output: error control err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + endif ! compute the flux and the residual vector for a given state vector ! NOTE: The values calculated in eval8summaSundials are used to calculate the initial flux diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index 2a5030389..bc43d4944 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -63,7 +63,6 @@ module t2enthalpy_module private public::T2E_lookup public::t2enthalpy -public::t2enthalpy_T ! define the look-up table used to compute temperature based on enthalpy contains @@ -73,9 +72,9 @@ module t2enthalpy_module ! public subroutine T2E_lookup: define a look-up table to compute enthalpy based on temperature ! ************************************************************************************************************************ subroutine T2E_lookup(nSoil, & ! intent(in): number of soil layers - mpar_data, & ! intent(in): parameter data structure - lookup_data, & ! intent(inout): lookup table data structure - err,message) + mpar_data, & ! intent(in): parameter data structure + lookup_data, & ! intent(inout): lookup table data structure + err,message) USE nr_utility_module,only:arth ! use to build vectors with regular increments USE spline_int_module,only:spline,splint ! use for cubic spline interpolation USE soil_utils_module,only:volFracLiq ! use to compute the volumetric fraction of liquid water @@ -91,21 +90,21 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number of logical(lgt),parameter :: doTest=.false. ! flag to test integer(i4b),parameter :: nLook=100 ! number of elements in the lookup table integer(i4b),parameter :: nIntegr8=10000 ! number of points used in the numerical integration - real(rkind),parameter :: T_lower=260.0_rkind ! lowest temperature value where all liquid water is assumed frozen (K) - real(rkind),dimension(nLook) :: xTemp ! temporary vector - real(rkind) :: xIncr ! temporary increment - real(rkind) :: T_incr ! temperature increment - real(rkind),parameter :: T_test=272.9742_rkind ! test value for temperature (K) - real(rkind) :: E_test ! test value for enthalpy (J m-3) + real(rkind),parameter :: T_lower=260.0_rkind ! lowest temperature value where all liquid water is assumed frozen (K) + real(rkind),dimension(nLook) :: xTemp ! temporary vector + real(rkind) :: xIncr ! temporary increment + real(rkind) :: T_incr ! temperature increment + real(rkind),parameter :: T_test=272.9742_rkind ! test value for temperature (K) + real(rkind) :: E_test ! test value for enthalpy (J m-3) integer(i4b) :: iVar ! loop through variables integer(i4b) :: iSoil ! loop through soil layers integer(i4b) :: iLook ! loop through lookup table integer(i4b) :: jIntegr8 ! index for numerical integration logical(lgt) :: check ! flag to check allocation - real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) - real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) - real(rkind) :: vFracIce ! volumetric fraction of ice (-) - real(rkind) :: matricHead ! matric head (m) + real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) + real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) + real(rkind) :: volFracIce ! volumetric fraction of ice (-) + real(rkind) :: matricHead ! matric head (m) ! initialize error control err=0; message="T2E_lookup/" @@ -195,12 +194,12 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number of ! NOTE: assume saturation matricHead = (LH_fus/gravity)*(Tk(iLook) - Tfreeze)/Tfreeze vFracLiq = volFracLiq(matricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - vFracIce = theta_sat - vFracLiq + volFracIce = theta_sat - vFracLiq ! compute enthalpy ! NOTE: assume intrrinsic density of ice is the intrinsic density of water ! NOTE: kg m-3 J kg-1 K-1 K - Ey(iLook) = Ey(iLook) + iden_water*Cp_water*vFracLiq*T_incr + iden_water*Cp_ice*vFracIce*T_incr + Ey(iLook) = Ey(iLook) + iden_water*Cp_water*vFracLiq*T_incr + iden_water*Cp_ice*volFracIce*T_incr end do ! numerical integration @@ -241,6 +240,7 @@ end subroutine T2E_lookup ! public subroutine t2enthalpy: compute enthalpy from temperature and total water content ! ************************************************************************************************************************ subroutine t2enthalpy(& + doPhase, & ! intent(in): logical flag to include phase change in enthalpy or not ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure @@ -256,25 +256,38 @@ subroutine t2enthalpy(& mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) mLayerVolFracIceTrial, & ! intent(in) + ! input: pre-computed derivatives + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) mLayerEnthalpy, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state ! output: error control err,message) ! intent(out): error control ! ------------------------------------------------------------------------------------------------------------------------- ! downwind routines USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water + USE soil_utils_module,only:dTheta_dPsi ! derivative in the soil water characteristic (soil) USE spline_int_module,only:splint ! use for cubic spline interpolation implicit none ! delare dummy variables ! ------------------------------------------------------------------------------------------------------------------------- + ! input: decisions + logical(lgt),intent(in) :: doPhase ! logical flag to include phase change in enthalpy or not ! input: data structures - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model indices - type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model indices + type(zLookup),intent(in) :: lookup_data ! lookup tables ! input: state variables for the vegetation canopy real(rkind),intent(in) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) @@ -285,316 +298,69 @@ subroutine t2enthalpy(& real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric fraction of Ice (-) + ! input: pre-computed derivatives + real(rkind),intent(in) :: dTheta_dTkCanopy ! derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: scalarFracLiqVeg ! fraction of canopy liquid water (-) + real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative of volumetric liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) + real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy real(rkind),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) real(rkind),intent(out) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) real(rkind),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) + ! output: derivatives + real(rkind),intent(out) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature + real(rkind),intent(out) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state + real(rkind),intent(out) :: dEnthalpy_dTk(:) ! derivatives in layer enthalpy w.r.t. temperature + real(rkind),intent(out) :: dEnthalpy_dWat(:) ! derivatives in layer enthalpy w.r.t. water state ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------------------------------- ! declare local variables - character(len=128) :: cmessage ! error message in downwind routine - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixDomainType ! name of a given model domain - integer(i4b) :: ixControlIndex ! index within a given model domain + character(len=128) :: cmessage ! error message in downwind routine + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixDomainType ! name of a given model domain + integer(i4b) :: ixControlIndex ! index within a given model domain real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) real(rkind) :: psiLiq ! matric head of liquid water (m) - real(rkind) :: vFracWat ! volumetric fraction of total water, liquid+ice (-) - real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) - real(rkind) :: vFracIce ! volumetric fraction of ice (-) - real(rkind) :: enthTemp ! enthalpy at the temperature of the control volume (J m-3) - real(rkind) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) - real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) - real(rkind) :: enthWater ! enthalpy of total water (J m-3) + real(rkind) :: volFracWat ! volumetric fraction of total water, liquid+ice (-) + real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) + real(rkind) :: volFracIce ! volumetric fraction of ice (-) + real(rkind) :: diffT ! temperature difference from Tfreeze + real(rkind) :: integral ! integral of snow freezing curve + real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head + real(rkind) :: dVolFracLiq_dTk ! derivative of volumetric fraction of liquid water with temperature + real(rkind) :: d_integral_dTk ! derivative of integral with temperature + ! enthalpy + real(rkind) :: enthVeg ! enthalpy of the vegetation (J m-3) real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) real(rkind) :: enthMix ! enthalpy of the mixed region, liquid+ice (J m-3) real(rkind) :: enthLiq ! enthalpy of the liquid region (J m-3) + real(rkind) :: enthIce ! enthalpy of the ice region (J m-3) real(rkind) :: enthAir ! enthalpy of air (J m-3) - real(rkind) :: diffT - real(rkind) :: integral - real(rkind) :: enthIce - real(rkind) :: enthVeg - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - generalVars: associate(& - ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers - ! mapping between the full state vector and the state subset - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset - ! type of domain, type of state variable, and index of control volume within domain - ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) ! end associate statement - ! -------------------------------------------------------------------------------------------------------------------------------- - - ! initialize error control - err=0; message="t2enthalpy/" - - ! loop through model state variables - do iState=1,size(ixMapSubset2Full) - - ! ----- - ! - compute indices... - ! -------------------- - - ! get domain type, and index of the control volume within the domain - ixFullVector = ixMapSubset2Full(iState) ! index within full state vector - ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) - ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain - - ! check an energy state - if(ixStateType(ixFullVector)==iname_nrgCanair .or. ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer)then - - ! get the layer index - select case(ixDomainType) - case(iname_cas); iLayer = integerMissing - case(iname_veg); iLayer = integerMissing - case(iname_snow); iLayer = ixControlIndex - case(iname_soil); iLayer = ixControlIndex + nSnow - case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) - case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return - end select - - ! identify domain - select case(ixDomainType) - - ! ----- - ! - canopy air space... - ! --------------------- - case(iname_cas) - scalarCanairEnthalpy = Cp_air * iden_air * (scalarCanairTempTrial - Tfreeze) - ! ----- - ! - vegetation canopy... - ! ----------------------- - case(iname_veg) - ! association to necessary variables for vegetation - vegVars: associate(& - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): [dp] canopy depth (m) - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) - - diffT = scalarCanopyTempTrial - Tfreeze - enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth - if(diffT>=0._rkind)then - enthLiq = Cp_water * scalarCanopyWatTrial * diffT / canopyDepth - enthIce = 0._rkind - enthPhase = 0._rkind - else - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth - enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth - enthPhase = LH_fus * scalarCanopyIceTrial / canopyDepth - end if - - scalarCanopyEnthalpy = enthVeg + enthLiq + enthIce - enthPhase - - ! end association - end associate vegVars - - ! ----- - ! - snow... - ! --------- - case(iname_snow) - - ! association to necessary variables for snow - snowVars: associate(& - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) - - diffT = mLayerTempTrial(iLayer) - Tfreeze - if(diffT>=0._rkind)then - enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * diffT - enthIce = 0._rkind - enthAir = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) ) * diffT - enthPhase = 0._rkind - else - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral - enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) - enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) - enthPhase = iden_ice * LH_fus * mLayerVolFracIceTrial(iLayer) - end if - - mLayerEnthalpy(iLayer) = enthLiq + enthIce + enthAir - enthPhase - - ! end association - end associate snowVars - - ! ----- - ! - soil... - ! --------- - case(iname_soil) - - ! make association to variables in the data structures... - soilVars: associate(& - - ! associate model parameters - soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex) , & ! intrinsic soil density (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex) , & ! soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex) , & ! volumetric residual water content (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) - - ! associate values in the lookup table - Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) - E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function - - ) ! end associate statement - - ! diagnostic variables - vGn_m = 1._rkind - 1._rkind/vGn_n - Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) - vFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - - - ! *** compute enthalpy of water for unfrozen conditions - if(mlayerTempTrial(iLayer) > Tcrit)then - enthWater = iden_water*Cp_water*vFracWat*(mlayerTempTrial(iLayer) - Tfreeze) ! valid for temperatures below freezing also - enthPhase = 0._rkind - - ! *** compute enthalpy of water for frozen conditions - else - ! calculate enthalpy at the temperature (cubic spline interpolation) - call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),enthTemp,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! calculate enthalpy at the critical temperature (cubic spline interpolation) - call splint(Tk,Ey,E2,Tcrit,enthTcrit,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! calculate the enthalpy of water - enthMix = enthTemp - enthTcrit ! enthalpy of the liquid+ice mix - enthLiq = iden_water*Cp_water*vFracWat*(Tcrit - Tfreeze) - enthWater = enthMix + enthLiq - - ! *** compute the enthalpy associated with phase change - psiLiq = (mLayerTempTrial(iLayer) - Tfreeze)*LH_fus/(gravity*Tfreeze) - vFracLiq = volFracLiq(psiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - vFracIce = vFracWat - vFracLiq - enthPhase = iden_water*LH_fus*vFracIce - - endif ! (if frozen conditions) - - ! *** compute the enthalpy of soil - enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*(mlayerTempTrial(iLayer) - Tfreeze) - - ! *** compute the enthalpy of air - enthAir = iden_air*Cp_air*(1._rkind - theta_sat - vFracWat)*(mlayerTempTrial(iLayer) - Tfreeze) - - ! *** compute the total enthalpy (J m-3) - mLayerEnthalpy(iLayer) = enthSoil + enthWater + enthAir - enthPhase - - end associate soilVars - - ! ----- - ! - checks... - ! ----------- - case(iname_aquifer); cycle ! aquifer: do nothing - case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return - end select - - end if ! if an energy layer - end do ! looping through state variables - - end associate generalVars - -end subroutine t2enthalpy - - -! ************************************************************************************************************************ -! public subroutine t2enthalpy_T: compute enthalpy from temperature and total water content -! ************************************************************************************************************************ -subroutine t2enthalpy_T(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in) - ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy, & ! intent(out): enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,message) ! intent(out): error control - ! ------------------------------------------------------------------------------------------------------------------------- - ! downwind routines - USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water - USE spline_int_module,only:splint ! use for cubic spline interpolation - implicit none - ! delare dummy variables - ! ------------------------------------------------------------------------------------------------------------------------- - ! input: data structures - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model indices - type(zLookup),intent(in) :: lookup_data ! lookup tables - ! input: state variables for the vegetation canopy - real(rkind),intent(in) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) - real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric fraction of Ice (-) - ! output: enthalpy - real(rkind),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - real(rkind),intent(out) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ------------------------------------------------------------------------------------------------------------------------- - ! declare local variables - character(len=128) :: cmessage ! error message in downwind routine - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixDomainType ! name of a given model domain - integer(i4b) :: ixControlIndex ! index within a given model domain - real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - real(rkind) :: psiLiq ! matric head of liquid water (m) - real(rkind) :: vFracWat ! volumetric fraction of total water, liquid+ice (-) - real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) - real(rkind) :: vFracIce ! volumetric fraction of ice (-) real(rkind) :: enthTemp ! enthalpy at the temperature of the control volume (J m-3) real(rkind) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) real(rkind) :: enthWater ! enthalpy of total water (J m-3) - real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) - real(rkind) :: enthMix ! enthalpy of the mixed region, liquid+ice (J m-3) - real(rkind) :: enthLiq ! enthalpy of the liquid region (J m-3) - real(rkind) :: enthAir ! enthalpy of air (J m-3) - real(rkind) :: diffT - real(rkind) :: integral - real(rkind) :: enthIce - real(rkind) :: enthVeg + ! enthalpy derivatives + real(rkind) :: dEnthVeg_dTk ! derivative of enthalpy of the vegetation with temperature + real(rkind) :: dEnthSoil_dTk ! derivative of enthalpy of the soil with temperature + real(rkind) :: dEnthLiq_dTk ! derivative of enthalpy of the liquid with temperature + real(rkind) :: dEnthIce_dTk ! derivative of enthalpy of the ice with temperature + real(rkind) :: dEnthAir_dTk ! derivative of enthalpy of the air with temperature + real(rkind) :: dEnthPhase_dTk ! derivative of enthalpy of the phase change with temperature + real(rkind) :: dEnthWater_dTk ! derivative of enthalpy of the total water with temperature + real(rkind) :: dEnthVeg_dWat ! derivative of enthalpy of the vegetation with water state + real(rkind) :: dEnthSoil_dWat ! derivative of enthalpy of the soil with water state + real(rkind) :: dEnthLiq_dWat ! derivative of enthalpy of the liquid with water state + real(rkind) :: dEnthIce_dWat ! derivative of enthalpy of the ice with water state + real(rkind) :: dEnthAir_dWat ! derivative of enthalpy of the air with water state + real(rkind) :: dEnthPhase_dWat ! derivative of enthalpy of the phase change with water state + real(rkind) :: dEnthWater_dWat ! derivative of enthalpy of the total water with water state ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures generalVars: associate(& @@ -615,7 +381,7 @@ subroutine t2enthalpy_T(& ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="t2enthalpy_T/" + err=0; message="t2enthalpy/" ! loop through model state variables do iState=1,size(ixMapSubset2Full) @@ -642,6 +408,22 @@ subroutine t2enthalpy_T(& case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return end select + ! Initialize + dEnthVeg_dTk = 0._rkind + dEnthSoil_dTk = 0._rkind + dEnthLiq_dTk = 0._rkind + dEnthIce_dTk = 0._rkind + dEnthAir_dTk = 0._rkind + dEnthPhase_dTk = 0._rkind + dEnthWater_dTk = 0._rkind + dEnthVeg_dWat = 0._rkind + dEnthSoil_dWat = 0._rkind + dEnthLiq_dWat = 0._rkind + dEnthIce_dWat = 0._rkind + dEnthAir_dWat = 0._rkind + dEnthPhase_dWat = 0._rkind + dEnthWater_dWat = 0._rkind + ! identify domain select case(ixDomainType) case(iname_cas) @@ -649,36 +431,57 @@ subroutine t2enthalpy_T(& case(iname_veg) ! association to necessary variables for vegetation - vegVars: associate(& + vegVars: associate(& canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): [dp] canopy depth (m) specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) + ) diffT = scalarCanopyTempTrial - Tfreeze enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth + ! enthalpy derivatives + dEnthVeg_dTk = specificHeatVeg * maxMassVegetation / canopyDepth if(diffT>=0._rkind)then enthLiq = Cp_water * scalarCanopyWatTrial * diffT / canopyDepth enthIce = 0._rkind enthPhase = 0._rkind + ! derivatives + dEnthLiq_dTk = Cp_water * scalarCanopyWatTrial / canopyDepth + dEnthLiq_dWat = Cp_water * diffT / canopyDepth else integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth enthPhase = LH_fus * scalarCanopyIceTrial / canopyDepth - end if - - scalarCanopyEnthalpy = enthVeg + enthLiq + enthIce + ! derivatives + d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2._rkind) + ! enthalpy derivatives + dEnthLiq_dTk = Cp_water * scalarCanopyWatTrial * d_integral_dTk / canopyDepth + dEnthIce_dTk = Cp_ice * scalarCanopyWatTrial * ( 1._rkind - d_integral_dTk ) / canopyDepth + dEnthPhase_dTk = -LH_fus * dTheta_dTkCanopy / canopyDepth ! dCanopyIce_dTk = -dTheta_dTkCanopy + dEnthLiq_dWat = Cp_water * integral / canopyDepth + dEnthIce_dWat = Cp_ice * ( diffT - integral ) / canopyDepth + dEnthPhase_dWat = LH_fus * ( 1._rkind - scalarFracLiqVeg ) / canopyDepth ! dCanopyIce_dWat = ( 1._rkind - scalarFracLiqVeg ) + endif + + scalarCanopyEnthalpy = enthVeg + enthLiq + enthIce + dCanEnthalpy_dTk = dEnthVeg_dTk + dEnthLiq_dTk + dEnthIce_dTk + dCanEnthalpy_dWat = dEnthVeg_dWat + dEnthLiq_dWat + dEnthIce_dWat + if (doPhase)then + scalarCanopyEnthalpy = scalarCanopyEnthalpy - enthPhase + dCanEnthalpy_dTk = dCanEnthalpy_dTk - dEnthPhase_dTk + dCanEnthalpy_dWat = dCanEnthalpy_dWat - dEnthPhase_dWat + endif end associate vegVars case(iname_snow) - + ! association to necessary variables for snow snowVars: associate(& snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) + ) diffT = mLayerTempTrial(iLayer) - Tfreeze if(diffT>=0._rkind)then @@ -686,15 +489,38 @@ subroutine t2enthalpy_T(& enthIce = 0._rkind enthAir = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) ) * diffT enthPhase = 0._rkind + ! enthalpy derivatives + dEnthLiq_dTk = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) + dEnthAir_dTk = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) ) + dEnthLiq_dWat = iden_water * Cp_water * diffT + dEnthAir_dWat = -iden_air * Cp_air * diffT else integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) - enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) + enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) enthPhase = iden_ice * LH_fus * mLayerVolFracIceTrial(iLayer) - end if - - mLayerEnthalpy(iLayer) = enthLiq + enthIce + enthAir + ! derivatives + d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2._rkind) + ! enthalpy derivatives + dEnthLiq_dTk = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * d_integral_dTk + dEnthIce_dTk = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( 1._rkind - d_integral_dTk ) + dEnthAir_dTk = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(1._rkind-d_integral_dTk) + d_integral_dTk ) ) + dEnthPhase_dTk = -iden_water * LH_fus * mLayerdTheta_dTk(iLayer) ! dVolFracIce_dTk = -mLayerdTheta_dTk(iLayer)*(iden_water/iden_ice) + dEnthLiq_dWat = iden_water * Cp_water * integral + dEnthIce_dWat = iden_water * Cp_ice * ( diffT - integral ) + dEnthAir_dWat = -iden_air * Cp_air * ( (iden_water/iden_ice)*(diffT-integral) + integral ) + dEnthPhase_dWat = iden_water * LH_fus * ( 1._rkind - mLayerFracLiqSnow(iLayer) )! dVolFracIce_dWat = ( 1._rkind - mLayerFracLiqSnow(iLayer) )*(iden_water/iden_ice) + endif + + mLayerEnthalpy(iLayer) = enthLiq + enthIce + enthAir + dEnthalpy_dTk(iLayer) = dEnthLiq_dTk + dEnthIce_dTk + dEnthAir_dTk + dEnthalpy_dWat(iLayer) = dEnthLiq_dWat + dEnthIce_dWat + dEnthAir_dWat + if (doPhase)then + mLayerEnthalpy(iLayer) = mLayerEnthalpy(iLayer) - enthPhase + dEnthalpy_dTk(iLayer) = dEnthalpy_dTk(iLayer) - dEnthPhase_dTk + dEnthalpy_dWat(iLayer) = dEnthalpy_dWat(iLayer) - dEnthPhase_dWat + endif end associate snowVars @@ -720,13 +546,16 @@ subroutine t2enthalpy_T(& ! diagnostic variables vGn_m = 1._rkind - 1._rkind/vGn_n Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) - vFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - + volFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + diffT = mLayerTempTrial(iLayer) - Tfreeze ! *** compute enthalpy of water for unfrozen conditions if(mlayerTempTrial(iLayer) > Tcrit)then - enthWater = iden_water*Cp_water*vFracWat*(mlayerTempTrial(iLayer) - Tfreeze) ! valid for temperatures below freezing also + enthWater = iden_water*Cp_water*volFracWat*diffT ! valid for temperatures below freezing also enthPhase = 0._rkind + ! enthalpy derivatives + dEnthWater_dTk = iden_water*Cp_water*volFracWat + dEnthWater_dWat = iden_water*Cp_water*dVolTot_dPsi0(ixControlIndex) !dVolFracWat_dWat = dVolTot_dPsi0(ixControlIndex) ! *** compute enthalpy of water for frozen conditions else @@ -740,25 +569,45 @@ subroutine t2enthalpy_T(& ! calculate the enthalpy of water enthMix = enthTemp - enthTcrit ! enthalpy of the liquid+ice mix - enthLiq = iden_water*Cp_water*vFracWat*(Tcrit - Tfreeze) + enthLiq = iden_water*Cp_water*volFracWat*(Tcrit - Tfreeze) enthWater = enthMix + enthLiq ! *** compute the enthalpy associated with phase change - psiLiq = (mLayerTempTrial(iLayer) - Tfreeze)*LH_fus/(gravity*Tfreeze) + psiLiq = diffT*LH_fus/(gravity*Tfreeze) vFracLiq = volFracLiq(psiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - vFracIce = vFracWat - vFracLiq - enthPhase = iden_water*LH_fus*vFracIce - + volFracIce = volFracWat - vFracLiq + enthPhase = iden_water*LH_fus*volFracIce + ! derivatives + dVolFracLiq_dTk = dTheta_dPsi(psiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m)*LH_fus/(gravity*Tfreeze) + dTcrit_dPsi0 = 0._rkind + if (mlayerTempTrial(iLayer)<0._rkind) dTcrit_dPsi0 = gravity*Tfreeze/LH_fus + ! enthalpy derivatives + dEnthWater_dTk = 0._rkind ! THIS NEEDS TO BE COMPUTED, d_enthMix_dTk using splines + dEnthPhase_dTk = -iden_water*LH_fus*dVolFracLiq_dTk + dEnthWater_dWat = iden_water*Cp_water*dVolTot_dPsi0(ixControlIndex)*(Tcrit - Tfreeze) + iden_water*Cp_water*volFracWat*dTcrit_dPsi0 + dEnthPhase_dWat = iden_water*LH_fus*dVolTot_dPsi0(ixControlIndex) endif ! (if frozen conditions) ! *** compute the enthalpy of soil - enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*(mlayerTempTrial(iLayer) - Tfreeze) + enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*diffT + ! enthalpy derivatives + dEnthSoil_dTk = soil_dens_intr*Cp_soil*(1._rkind - theta_sat) ! *** compute the enthalpy of air - enthAir = iden_air*Cp_air*(1._rkind - theta_sat - vFracWat)*(mlayerTempTrial(iLayer) - Tfreeze) + enthAir = iden_air*Cp_air*(1._rkind - theta_sat - volFracWat)*diffT + ! enthalpy derivatives + dEnthAir_dTk = iden_air*Cp_air*(1._rkind - theta_sat - volFracWat) + dEnthAir_dWat = -iden_air*Cp_air*dVolTot_dPsi0(ixControlIndex)*diffT ! *** compute the total enthalpy (J m-3) mLayerEnthalpy(iLayer) = enthSoil + enthWater + enthAir + dEnthalpy_dTk(iLayer) = dEnthSoil_dTk + dEnthWater_dTk + dEnthAir_dTk + dEnthalpy_dWat(iLayer) = dEnthSoil_dWat + dEnthWater_dWat + dEnthAir_dWat + if (doPhase)then + mLayerEnthalpy(iLayer) = mLayerEnthalpy(iLayer) - enthPhase + dEnthalpy_dTk(iLayer) = dEnthalpy_dTk(iLayer) - dEnthPhase_dTk + dEnthalpy_dWat(iLayer) = dEnthalpy_dWat(iLayer) - dEnthPhase_dWat + endif end associate soilVars @@ -774,6 +623,6 @@ subroutine t2enthalpy_T(& end associate generalVars -end subroutine t2enthalpy_T +end subroutine t2enthalpy end module t2enthalpy_module diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 index 71b1a318b..3795c4caf 100644 --- a/build/source/engine/updateVars.f90 +++ b/build/source/engine/updateVars.f90 @@ -73,9 +73,6 @@ module updateVars_module USE var_lookup,only:iLookPARAM ! named variables for structure elements USE var_lookup,only:iLookINDEX ! named variables for structure elements -! provide access to the routines to calculate enthalpy -USE t2enthalpy_module,only:t2enthalpy - ! provide access to routines to update states USE updatState_module,only:updateSnow ! update snow states USE updatState_module,only:updateSoil ! update soil states @@ -628,7 +625,7 @@ subroutine updateVars(& ! ======================================================================================================================================= ! ----- - ! - compute the liquid water matric potential (and necessay derivatives)... + ! - compute the liquid water matric potential (and necessary derivatives)... ! ------------------------------------------------------------------------- ! only for soil diff --git a/build/source/engine/updateVarsSundials.f90 b/build/source/engine/updateVarsSundials.f90 index 655446a91..acb8e7154 100644 --- a/build/source/engine/updateVarsSundials.f90 +++ b/build/source/engine/updateVarsSundials.f90 @@ -720,7 +720,7 @@ subroutine updateVarsSundials(& ! ======================================================================================================================================= ! ----- - ! - compute the liquid water matric potential (and necessay derivatives)... + ! - compute the liquid water matric potential (and necessary derivatives)... ! ------------------------------------------------------------------------- ! only for soil diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index 5d6575eeb..47b7fc3a7 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -56,6 +56,7 @@ module varSubstepSundials_module USE var_lookup,only:iLookDIAG ! named variables for structure elements USE var_lookup,only:iLookPARAM ! named variables for structure elements USE var_lookup,only:iLookINDEX ! named variables for structure elements +USE var_lookup,only:iLookDERIV ! named variables for structure elements ! look up structure for variable types USE var_lookup,only:iLookVarType @@ -395,7 +396,7 @@ subroutine varSubstepSundials(& ! identify the need to check the mass balance checkMassBalance = .false. !do not check for Sundials - checkNrgBalance = .false. !do not check for Sundials + checkNrgBalance = .false. !do not check for Sundials, also only check if ixHowHeatCap == enthalpyFD ! update prognostic variables call updateProgSundials(dt_out,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control @@ -535,8 +536,7 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux waterBalanceError,nrgFluxModified,err,message) ! output: flags and error control USE getVectorz_module,only:varExtract ! extract variables from the state vector USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables - USE computEnthalpy_module,only:computEnthalpy - USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy + USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy implicit none ! model control real(rkind) ,intent(in) :: dt ! time step (s) @@ -612,6 +612,11 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux real(rkind) :: scalarCanairEnthalpyTrial ! enthalpy of the canopy air space (J m-3) real(rkind) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! enthalpy of snow + soil (J m-3) + ! enthalpy derivatives + real(rkind) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature + real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state + real(rkind),dimension(nLayers) :: dEnthalpy_dTk ! derivatives in layer enthalpy w.r.t. temperature + real(rkind),dimension(nLayers) :: dEnthalpy_dWat ! derivatives in layer enthalpy w.r.t. water state ! ------------------------------------------------------------------------------------------------------------------- ! ------------------------------------------------------------------------------------------------------------------- @@ -659,9 +664,15 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout) : [dp(:)] matric head (m) mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(inout) : [dp(:)] matric potential of liquid water (m) ! enthalpy - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(inout): [dp(:)] enthalpy of the snow+soil layers (J m-3) + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(inout): [dp(:)] enthalpy of the snow+soil layers (J m-3) + ! derivatives, diagnositic for enthalpy + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) ! model state variables (aquifer) scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(inout) : [dp(:)] storage of water in the aquifer (m) ! error tolerance @@ -758,7 +769,6 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! update diagnostic variables call updateVarsSundials(& ! input @@ -801,10 +811,11 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux ! ---- ! * check energy balance !------------------------ - ! NOTE: for now, we just compute enthalpy + ! NOTE: for now, we just compute enthalpy with phase change, should check, and should also mirror in varSubstep non-Sundials if(checkNrgBalance)then ! compute enthalpy at t_{n+1} call t2enthalpy(& + .true., & ! intent(in): logical flag to include phase change in enthalpy ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure @@ -820,10 +831,20 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) + ! input: pre-computed derivatives + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif From fa4a7aa3b4a8fcf96df17ff9884960bec97321fd Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 2 Dec 2022 13:23:47 +0900 Subject: [PATCH 0441/1472] don't need layer height --- build/source/engine/ssdNrgFlux.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index 3dc852d12..8864da0f4 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -151,7 +151,6 @@ subroutine ssdNrgFlux(& ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat, & ! intent(in): index in the state subset for energy state variables in the snow+soil domain mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): depth of each layer (m) mLayerHeight => prog_data%var(iLookPROG%mLayerHeight)%dat, & ! intent(in): height at the mid-point of each layer (m) - iLayerHeight => prog_data%var(iLookPROG%iLayerHeight)%dat, & ! intent(in): height at the interface of each layer (m) ! input: thermal properties upperBoundTemp => mpar_data%var(iLookPARAM%upperBoundTemp)%dat(1), & ! intent(in): temperature of the upper boundary (K) lowerBoundTemp => mpar_data%var(iLookPARAM%lowerBoundTemp)%dat(1), & ! intent(in): temperature of the lower boundary (K) From e7540268f2933eb1115ba0bb833e1c8e3f7851c4 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 2 Dec 2022 20:16:36 +0900 Subject: [PATCH 0442/1472] order of the terms in this equation, not the zero adding, made the difference. --- build/source/engine/computJacob.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 9673982cf..5c5c401c9 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -611,7 +611,7 @@ subroutine computJacob(& aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerdTemp_dt(jLayer) & + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present - aJac(ixOffDiag(nrgState,watState),watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) + aJac(ixOffDiag(nrgState,watState),watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + aJac(ixOffDiag(nrgState,watState),watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water + aJac(ixOffDiag(nrgState,watState),watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content endif ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above @@ -949,7 +949,7 @@ subroutine computJacob(& aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerdTemp_dt(jLayer) & + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present - aJac(nrgState,watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + aJac(nrgState,watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content endif ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above From d3904b51793e401811aeaaa9d48a14f0e2eb3b83 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 6 Dec 2022 22:25:45 +0900 Subject: [PATCH 0443/1472] making the error output follow the actual nc file index so it's more intuitive --- build/source/engine/run_oneGRU.f90 | 3 ++- build/source/engine/run_oneHRU.f90 | 8 +++++--- utils/subsetGRU.sh | 5 ++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/build/source/engine/run_oneGRU.f90 b/build/source/engine/run_oneGRU.f90 index 6d40e68ab..0d7cd0e29 100644 --- a/build/source/engine/run_oneGRU.f90 +++ b/build/source/engine/run_oneGRU.f90 @@ -140,7 +140,7 @@ subroutine run_oneGRU(& logical(lgt) :: computeVegFluxFlag ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) ! initialize error control - err=0; write(message, '(A24,I0,A2)' ) 'run_oneGRU (gru index = ',gruInfo%gru_nc,')/' + err=0; write(message, '(A24,I0,A2)' ) 'run_oneGRU (gru nc = ',gruInfo%gru_nc -1,', gruId = ',gruInfo%gru_id,')/' !netcdf index starts with 0 if want to subset ! ----- basin initialization -------------------------------------------------------------------------------------------- @@ -182,6 +182,7 @@ subroutine run_oneGRU(& ! simulation for a single HRU call run_oneHRU(& ! model control + gruInfo%hruInfo(iHRU)%hru_nc, & ! intent(in): hru count Id gruInfo%hruInfo(iHRU)%hru_id, & ! intent(in): hruId dt_init(iHRU), & ! intent(inout): initial time step computeVegFluxFlag, & ! intent(inout): flag to indicate if we are computing fluxes over vegetation (false=no, true=yes) diff --git a/build/source/engine/run_oneHRU.f90 b/build/source/engine/run_oneHRU.f90 index e2de95b16..f9d751723 100644 --- a/build/source/engine/run_oneHRU.f90 +++ b/build/source/engine/run_oneHRU.f90 @@ -85,6 +85,7 @@ module run_oneHRU_module ! simulation for a single HRU subroutine run_oneHRU(& ! model control + hru_nc, & ! intent(in): hru index in netcdf hruId, & ! intent(in): hruId dt_init, & ! intent(inout): used to initialize the length of the sub-step for each HRU computeVegFlux, & ! intent(inout): flag to indicate if we are computing fluxes over vegetation (false=no, true=yes) @@ -113,8 +114,9 @@ subroutine run_oneHRU(& implicit none ! ----- define dummy variables ------------------------------------------------------------------------------------------ - + ! model control + integer(i4b) , intent(in) :: hru_nc ! hru index in netcdf integer(8) , intent(in) :: hruId ! hruId real(rkind) , intent(inout) :: dt_init ! used to initialize the length of the sub-step for each HRU logical(lgt) , intent(inout) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (false=no, true=yes) @@ -143,8 +145,8 @@ subroutine run_oneHRU(& real(rkind) , allocatable :: zSoilReverseSign(:) ! height at bottom of each soil layer, negative downwards (m) ! initialize error control - err=0; write(message, '(A20,I0,A2)' ) 'run_oneHRU (hruId = ',hruId,')/' - + err=0; write(message, '(A20,I0,A2)' ) 'run_oneHRU (hru nc = ',hru_nc -1 ,', hruId = ',hruId,')/' !netcdf index starts with 0 if want to subset + ! ----- hru initialization --------------------------------------------------------------------------------------------- ! water pixel: do nothing diff --git a/utils/subsetGRU.sh b/utils/subsetGRU.sh index f499d44bb..e3de4299f 100644 --- a/utils/subsetGRU.sh +++ b/utils/subsetGRU.sh @@ -11,7 +11,6 @@ module load nco/5.0.6 # GRU want to subset, change to do another GRU # 155th basin in the slurm batch run, offset-th script (offset is SLURM_ARRAY_TASK_ID # could also just set this as GRU_file from G* on output nc files (here the equation output is 487981). -# GRU_file is not the actual GRUid, it is one off from the nc files, so subtract one before ncks offset=942 gruCount=518 @@ -23,9 +22,9 @@ gruCount=518 GRU_file=487980 nbasin_slurm=$((GRU_file-gruCount*offset)) -GRU_id=$((GRU_file - 1)) +GRU_id=$GRU_file HRU_id=$GRU_id -echo "file name id is ${GRU_file}, actual GRU id is ${GRU_id}, number in batch is ${nbasin_slurm}" +echo "file name id is ${GRU_file}, GRU id is ${GRU_id}, HRU id is ${HRU_id}, number in batch is ${nbasin_slurm}" # top paths, change these to yours homeDir=/globalhome/gwu479/HPC/ From 2bdbd19f1bbec18172eef9c19aa1678e80436b21 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 7 Dec 2022 09:55:27 +0900 Subject: [PATCH 0444/1472] k_macropore must be > k_soil warning in var_derive, old bug from Martyn --- build/source/engine/var_derive.f90 | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/build/source/engine/var_derive.f90 b/build/source/engine/var_derive.f90 index 404f42447..e53c7754e 100644 --- a/build/source/engine/var_derive.f90 +++ b/build/source/engine/var_derive.f90 @@ -341,6 +341,11 @@ subroutine satHydCond(mpar_data,indx_data,prog_data,flux_data,err,message) else iLayerSatHydCond(iLayer-nSnow) = 0.5_rkind * (k_soil(iLayer-nSnow) + k_soil(iLayer+1-nSnow) ) * ifcDepthScaleFactor endif + ! - check macropore conductivity > micropore conductivity + if(k_macropore(iLayer-nSnow) < k_soil(iLayer-nSnow))then + message=trim(message)//"hydraulic conductivity for macropores is less than the hydraulic conductivity for micropores" + err=20; return + endif ! - conductivity at layer midpoints if(compactedDepth/iLayerHeight(nLayers) /= 1._rkind) then ! avoid divide by zero midDepthScaleFactor = ( (1._rkind - mLayerHeight(iLayer)/iLayerHeight(nLayers))**(zScale_TOPMODEL - 1._rkind) ) / & @@ -351,12 +356,12 @@ subroutine satHydCond(mpar_data,indx_data,prog_data,flux_data,err,message) mLayerSatHydCond(iLayer-nSnow) = k_soil(iLayer-nSnow) * midDepthScaleFactor mLayerSatHydCondMP(iLayer-nSnow) = k_macropore(iLayer-nSnow) * midDepthScaleFactor end if - + !print*, iLayer, nSnow !print*, 'compactedDepth = ', compactedDepth - !print*, 'k_macropore = ', k_macropore - !print*, 'mLayerHeight(iLayer) = ', mLayerHeight(iLayer) + !print*, 'k_soil, k_macropore = ', k_soil, k_macropore + !print*, 'mLayerHeight(iLayer) = ', mLayerHeight(iLayer) !print*, 'iLayerHeight(nLayers) = ', iLayerHeight(nLayers) - !print*, 'iLayer, mLayerSatHydCondMP(iLayer-nSnow) = ', mLayerSatHydCondMP(iLayer-nSnow) + !print*, 'iLayer, mLayerSatHydCond, mLayerSatHydCondMP = ', mLayerSatHydCond(iLayer-nSnow), mLayerSatHydCondMP(iLayer-nSnow) ! error check (errors checked earlier also, so should not get here) case default From 5409f57ee39f52f11f06b7431bd7053c17d5c873 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 7 Dec 2022 10:32:16 +0900 Subject: [PATCH 0445/1472] update scalarSnowDrainage and NaN definition --- build/source/dshare/globalData.f90 | 4 ++-- build/source/engine/computFlux.f90 | 2 +- build/source/engine/computResid.f90 | 2 +- build/source/engine/layerMerge.f90 | 2 -- build/source/engine/summaSolve.f90 | 2 +- build/source/engine/var_derive.f90 | 10 +--------- 6 files changed, 6 insertions(+), 16 deletions(-) diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 index 0f50ade64..8fe721277 100644 --- a/build/source/dshare/globalData.f90 +++ b/build/source/dshare/globalData.f90 @@ -199,8 +199,8 @@ MODULE globalData ! * part 2: globally constant variables/structures that require initialization ! ---------------------------------------------------------------------------------------------------------------- - ! define Indian bread (NaN) - real(rkind),save,public :: dNaN + ! define Not-a-Number (NaN) + real(rkind),save,public :: dNaN ! define default parameter values and parameter bounds type(par_info),save,public :: localParFallback(maxvarMpar) ! local column default parameters diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 04245d10a..4b80010f1 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -658,7 +658,7 @@ subroutine computFlux(& ! NOTE: in case where nSnowOnlyHyd==0 AND snow layers exist, then scalarRainPlusMelt is taken from the previous flux evaluation if(nSnow==0)then !no snow layers scalarRainPlusMelt = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water & ! liquid flux from the canopy (m s-1) - + drainageMeltPond/iden_water ! melt of the snow without a layer (m s-1) + + scalarSnowDrainage if(ixVegHyd/=integerMissing)then ! save canopy derivatives diff --git a/build/source/engine/computResid.f90 b/build/source/engine/computResid.f90 index 6552d75a9..9209c78d4 100644 --- a/build/source/engine/computResid.f90 +++ b/build/source/engine/computResid.f90 @@ -260,7 +260,7 @@ subroutine computResid(& ! check if(any(isNan(rVec)))then - message=trim(message)//'we found some Indian bread (NaN) ' + message=trim(message)//'vector of residuals contains NaN value(s) ' ! formerly known as the Indian bread error write(*,'(a,1x,100(e12.5,1x))') 'rVec = ', rVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) write(*,'(a,1x,100(e12.5,1x))') 'fVec = ', fVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) err=20; return diff --git a/build/source/engine/layerMerge.f90 b/build/source/engine/layerMerge.f90 index a423811af..c0fbf88b7 100644 --- a/build/source/engine/layerMerge.f90 +++ b/build/source/engine/layerMerge.f90 @@ -248,8 +248,6 @@ subroutine layerMerge(& end do ! (looping through snow layers) - !print*, 'ksnow = ', ksnow - ! exit if finished if(kSnow==nCheck)exit diff --git a/build/source/engine/summaSolve.f90 b/build/source/engine/summaSolve.f90 index 76e2adc05..91e5757e6 100644 --- a/build/source/engine/summaSolve.f90 +++ b/build/source/engine/summaSolve.f90 @@ -613,7 +613,7 @@ subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fl err=20; return endif - ! initialize brackets to double precision Indian bread + ! initialize brackets to rkind precision NaN if(iter==1)then xMax = dNaN xMin = dNaN diff --git a/build/source/engine/var_derive.f90 b/build/source/engine/var_derive.f90 index e53c7754e..979c326f7 100644 --- a/build/source/engine/var_derive.f90 +++ b/build/source/engine/var_derive.f90 @@ -356,12 +356,6 @@ subroutine satHydCond(mpar_data,indx_data,prog_data,flux_data,err,message) mLayerSatHydCond(iLayer-nSnow) = k_soil(iLayer-nSnow) * midDepthScaleFactor mLayerSatHydCondMP(iLayer-nSnow) = k_macropore(iLayer-nSnow) * midDepthScaleFactor end if - !print*, iLayer, nSnow - !print*, 'compactedDepth = ', compactedDepth - !print*, 'k_soil, k_macropore = ', k_soil, k_macropore - !print*, 'mLayerHeight(iLayer) = ', mLayerHeight(iLayer) - !print*, 'iLayerHeight(nLayers) = ', iLayerHeight(nLayers) - !print*, 'iLayer, mLayerSatHydCond, mLayerSatHydCondMP = ', mLayerSatHydCond(iLayer-nSnow), mLayerSatHydCondMP(iLayer-nSnow) ! error check (errors checked earlier also, so should not get here) case default @@ -372,9 +366,7 @@ subroutine satHydCond(mpar_data,indx_data,prog_data,flux_data,err,message) !if(iLayer > nSnow)& ! avoid layer 0 ! write(*,'(a,1x,i4,1x,2(f11.5,1x,e20.10,1x))') 'satHydCond: ', iLayer, mLayerHeight(iLayer), mLayerSatHydCond(iLayer-nSnow), iLayerHeight(iLayer), iLayerSatHydCond(iLayer-nSnow) end do ! looping through soil layers - !print*, trim(model_decisions(iLookDECISIONS%hc_profile)%cDecision) - !print*, 'k_soil, k_macropore, zScale_TOPMODEL = ', k_soil, k_macropore, zScale_TOPMODEL - !pause ' in satHydCond' + end associate end subroutine satHydCond From 00a10bd7707a7aafbae842cdac982c1443142289 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 7 Dec 2022 13:28:09 +0900 Subject: [PATCH 0446/1472] put macropore condition into paramCheck instead of where it is used. --- build/source/engine/paramCheck.f90 | 18 ++++++++++++++++-- build/source/engine/var_derive.f90 | 5 ----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/build/source/engine/paramCheck.f90 b/build/source/engine/paramCheck.f90 index 03fec6a6a..8c8ce5778 100644 --- a/build/source/engine/paramCheck.f90 +++ b/build/source/engine/paramCheck.f90 @@ -107,7 +107,9 @@ subroutine paramCheck(mpar_data,err,message) ! ------------------------------------------------------------------------------------------------------------------------------------------- ! ***** - ! * check parameter dependencies... + ! * check soil parameter dependencies... + ! theta_res < critSoilWilting < critSoilTranspire < fieldCapacit < theta_sat + ! k_macropore < k_soil ! ********************************* ! associations @@ -121,7 +123,9 @@ subroutine paramCheck(mpar_data,err,message) ! soil properties fieldCapacity => mpar_data%var(iLookPARAM%fieldCapacity)%dat(1), & ! intent(in): [dp] field capacity (-) theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat & ! intent(in): [dp(:)] soil residual volumetric water content (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat, & ! intent(in): [dp(:)] soil residual volumetric water content (-) + k_soil => mpar_data%var(iLookPARAM%k_soil)%dat, & ! intent(in): [dp(:)] saturated hydraulic conductivity at the compacted depth (m s-1) + k_macropore => mpar_data%var(iLookPARAM%k_macropore)%dat & ! intent(in): [dp(:)] saturated hydraulic conductivity at the compacted depth for macropores (m s-1) ) ! associations to parameters ! check canopy geometry @@ -168,10 +172,20 @@ subroutine paramCheck(mpar_data,err,message) ! check porosity if( any(theta_sat < theta_res) )then + print*, 'theta_res = ', theta_res + print*, 'theta_sat = ', theta_sat write(message,'(a,i0,a)') trim(message)//'porosity is less than the residual liquid water content' err=20; return endif + ! - check macropore and micropore conductivity + if( any(k_macropore < k_soil) )then + print*, 'k_macropore = ', k_macropore + print*, 'k_soil = ', k_soil + write(message,'(a,i0,a)') trim(message)//"hydraulic conductivity for macropores is less than the hydraulic conductivity for micropores" + err=20; return + endif + ! end associations to parameter dependencies end associate diff --git a/build/source/engine/var_derive.f90 b/build/source/engine/var_derive.f90 index 979c326f7..6f3485fc7 100644 --- a/build/source/engine/var_derive.f90 +++ b/build/source/engine/var_derive.f90 @@ -341,11 +341,6 @@ subroutine satHydCond(mpar_data,indx_data,prog_data,flux_data,err,message) else iLayerSatHydCond(iLayer-nSnow) = 0.5_rkind * (k_soil(iLayer-nSnow) + k_soil(iLayer+1-nSnow) ) * ifcDepthScaleFactor endif - ! - check macropore conductivity > micropore conductivity - if(k_macropore(iLayer-nSnow) < k_soil(iLayer-nSnow))then - message=trim(message)//"hydraulic conductivity for macropores is less than the hydraulic conductivity for micropores" - err=20; return - endif ! - conductivity at layer midpoints if(compactedDepth/iLayerHeight(nLayers) /= 1._rkind) then ! avoid divide by zero midDepthScaleFactor = ( (1._rkind - mLayerHeight(iLayer)/iLayerHeight(nLayers))**(zScale_TOPMODEL - 1._rkind) ) / & From 3893e644faf579d56b8f51d3dbba245da5537052 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 7 Dec 2022 13:59:48 +0900 Subject: [PATCH 0447/1472] Format was wrong --- build/source/engine/run_oneGRU.f90 | 2 +- build/source/engine/run_oneHRU.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/run_oneGRU.f90 b/build/source/engine/run_oneGRU.f90 index 0d7cd0e29..ee2a03889 100644 --- a/build/source/engine/run_oneGRU.f90 +++ b/build/source/engine/run_oneGRU.f90 @@ -140,7 +140,7 @@ subroutine run_oneGRU(& logical(lgt) :: computeVegFluxFlag ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) ! initialize error control - err=0; write(message, '(A24,I0,A2)' ) 'run_oneGRU (gru nc = ',gruInfo%gru_nc -1,', gruId = ',gruInfo%gru_id,')/' !netcdf index starts with 0 if want to subset + err=0; write(message, '(A21,I0,A10,I0,A2)' ) 'run_oneGRU (gru nc = ',gruInfo%gru_nc -1,', gruId = ',gruInfo%gru_id,')/' !netcdf index starts with 0 if want to subset ! ----- basin initialization -------------------------------------------------------------------------------------------- diff --git a/build/source/engine/run_oneHRU.f90 b/build/source/engine/run_oneHRU.f90 index f9d751723..a8119e4b9 100644 --- a/build/source/engine/run_oneHRU.f90 +++ b/build/source/engine/run_oneHRU.f90 @@ -145,7 +145,7 @@ subroutine run_oneHRU(& real(rkind) , allocatable :: zSoilReverseSign(:) ! height at bottom of each soil layer, negative downwards (m) ! initialize error control - err=0; write(message, '(A20,I0,A2)' ) 'run_oneHRU (hru nc = ',hru_nc -1 ,', hruId = ',hruId,')/' !netcdf index starts with 0 if want to subset + err=0; write(message, '(A21,I0,A10,I0,A2)' ) 'run_oneHRU (hru nc = ',hru_nc -1 ,', hruId = ',hruId,')/' !netcdf index starts with 0 if want to subset ! ----- hru initialization --------------------------------------------------------------------------------------------- From 6ee06519c76a073102486e5855b93f90dcbaaf92 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 7 Dec 2022 14:29:42 +0900 Subject: [PATCH 0448/1472] the scalarSnowDrainage was a pull request from Guoqiang, and we are not yet sure if it should be done. it may just be a variable name swap. keeping it the old way for now. --- build/source/engine/computFlux.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 4b80010f1..04245d10a 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -658,7 +658,7 @@ subroutine computFlux(& ! NOTE: in case where nSnowOnlyHyd==0 AND snow layers exist, then scalarRainPlusMelt is taken from the previous flux evaluation if(nSnow==0)then !no snow layers scalarRainPlusMelt = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water & ! liquid flux from the canopy (m s-1) - + scalarSnowDrainage + + drainageMeltPond/iden_water ! melt of the snow without a layer (m s-1) if(ixVegHyd/=integerMissing)then ! save canopy derivatives From 8d0f712d9a34dd79073a5e81e456da07cab65a5d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 15 Dec 2022 10:41:52 +0900 Subject: [PATCH 0449/1472] added Jacobian terms for dEnthWater soil --- build/source/driver/summa_setup.f90 | 2 +- build/source/engine/convE2Temp.f90 | 15 ++++++++------- build/source/engine/spline_int.f90 | 15 +++++++++------ build/source/engine/t2enthalpy.f90 | 18 +++++++++++------- 4 files changed, 29 insertions(+), 21 deletions(-) diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index 6b1765586..1b95adc24 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -75,7 +75,7 @@ subroutine summa_paramSetup(summa1_struc, err, message) USE paramCheck_module,only:paramCheck ! module to check consistency of model parameters USE pOverwrite_module,only:pOverwrite ! module to overwrite default parameter values with info from the Noah tables USE read_param_module,only:read_param ! module to read model parameter sets - USE ConvE2Temp_module,only:E2T_lookup ! module to calculate a look-up table for the temperature-enthalpy conversion + USE convE2Temp_module,only:E2T_lookup ! module to calculate a look-up table for the temperature-enthalpy conversion USE t2enthalpy_module,only:T2E_lookup ! module to calculate a look-up table for the temperature-enthalpy conversion USE var_derive_module,only:fracFuture ! module to calculate the fraction of runoff in future time steps (time delay histogram) USE module_sf_noahmplsm,only:read_mp_veg_parameters ! module to read NOAH vegetation tables diff --git a/build/source/engine/convE2Temp.f90 b/build/source/engine/convE2Temp.f90 index ab6a9f5d1..3ee8e8ba3 100644 --- a/build/source/engine/convE2Temp.f90 +++ b/build/source/engine/convE2Temp.f90 @@ -59,12 +59,13 @@ subroutine E2T_lookup(mpar_data,err,message) character(*),intent(out) :: message ! error message ! declare local variables character(len=128) :: cmessage ! error message in downwind routine - real(rkind),parameter :: T_start=260.0_rkind ! start temperature value where all liquid water is assumed frozen (K) - real(rkind) :: T_incr,E_incr ! temperature/enthalpy increments - real(rkind),dimension(nlook) :: Tk ! initial temperature vector - real(rkind),dimension(nlook) :: Ey ! initial enthalpy vector - real(rkind),parameter :: waterWght=1._rkind ! weight applied to total water (kg m-3) --- cancels out - real(rkind),dimension(nlook) :: T2deriv ! 2nd derivatives of the interpolating function at tabulated points + real(rkind),parameter :: T_start=260.0_rkind ! start temperature value where all liquid water is assumed frozen (K) + real(rkind) :: T_incr,E_incr ! temperature/enthalpy increments + real(rkind),dimension(nlook) :: Tk ! initial temperature vector + real(rkind),dimension(nlook) :: Ey ! initial enthalpy vector + real(rkind),parameter :: waterWght=1._rkind ! weight applied to total water (kg m-3) --- cancels out + real(rkind),dimension(nlook) :: T2deriv ! 2nd derivatives of the interpolating function at tabulated points + real(rkind) :: dT ! derivative of temperature with enthalpy at E_lookup integer(i4b) :: ilook ! loop through lookup table ! initialize error control err=0; message="E2T_lookup/" @@ -84,7 +85,7 @@ subroutine E2T_lookup(mpar_data,err,message) call spline(Ey,Tk,1.e30_rkind,1.e30_rkind,T2deriv,err,cmessage) ! get the second derivatives if(err/=0) then; message=trim(message)//trim(cmessage); return; end if do ilook=1,nlook - call splint(Ey,Tk,T2deriv,E_lookup(ilook),T_lookup(ilook),err,cmessage) + call splint(Ey,Tk,T2deriv,E_lookup(ilook),T_lookup(ilook),dT,err,cmessage) if(err/=0) then; message=trim(message)//trim(cmessage); return; end if !write(*,'(i6,1x,2(f20.4,1x))') ilook, E_lookup(ilook), T_lookup(ilook) end do diff --git a/build/source/engine/spline_int.f90 b/build/source/engine/spline_int.f90 index 28efd3bf2..2141c0fe6 100644 --- a/build/source/engine/spline_int.f90 +++ b/build/source/engine/spline_int.f90 @@ -57,19 +57,19 @@ SUBROUTINE spline(x,y,yp1,ypn,y2,err,message) END SUBROUTINE spline ! ************************************************************* - ! new subroutine: splint + ! new subroutine: splint and local derivative with x ! ************************************************************* - SUBROUTINE splint(xa,ya,y2a,x,y,err,message) + SUBROUTINE splint(xa,ya,y2a,x,y,dy,err,message) IMPLICIT NONE ! declare dummy variables real(rkind), DIMENSION(:), INTENT(IN) :: xa,ya,y2a real(rkind), INTENT(IN) :: x - real(rkind), INTENT(OUT) :: y - integer(i4b),intent(out) :: err - character(*),intent(out) :: message + real(rkind), INTENT(OUT) :: y, dy + integer(i4b),intent(out) :: err + character(*),intent(out) :: message ! declare local variables INTEGER(I4B) :: khi,klo,n - real(rkind) :: a,b,h + real(rkind) :: a,b,h,da,db ! check size of input vectors if (size(xa)==size(ya) .and. size(ya)==size(y2a)) then n=size(xa) @@ -83,7 +83,10 @@ SUBROUTINE splint(xa,ya,y2a,x,y,err,message) if (h == 0.0_rkind) then; err=20; message="f-splint/badXinput"; return; end if a=(xa(khi)-x)/h b=(x-xa(klo))/h + da = -1.0_rkind/h + db = 1.0_rkind/h y=a*ya(klo)+b*ya(khi)+((a**3-a)*y2a(klo)+(b**3-b)*y2a(khi))*(h**2)/6.0_rkind + dy = da*ya(klo)+db*ya(khi)+((3.0_rkind*da*a**2-da)*y2a(klo)+(3.0_rkind*db*b**2-db)*y2a(khi))*(h**2)/6.0_rkind END SUBROUTINE splint ! ************************************************************* diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index bc43d4944..142adbb33 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -96,6 +96,7 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number of real(rkind) :: T_incr ! temperature increment real(rkind),parameter :: T_test=272.9742_rkind ! test value for temperature (K) real(rkind) :: E_test ! test value for enthalpy (J m-3) + real(rkind) :: dE ! derivative of enthalpy with temperature at T_test integer(i4b) :: iVar ! loop through variables integer(i4b) :: iSoil ! loop through soil layers integer(i4b) :: iLook ! loop through lookup table @@ -213,7 +214,7 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number of if(doTest)then ! calculate enthalpy - call splint(Tk,Ey,E2,T_test,E_test,err,cmessage) + call splint(Tk,Ey,E2,T_test,E_test,dE,err,cmessage) if(err/=0) then; message=trim(message)//trim(cmessage); return; end if ! write values @@ -335,6 +336,9 @@ subroutine t2enthalpy(& real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head real(rkind) :: dVolFracLiq_dTk ! derivative of volumetric fraction of liquid water with temperature real(rkind) :: d_integral_dTk ! derivative of integral with temperature + real(rkind) :: dE ! derivative of enthalpy with temperature at layer temperature + real(rkind) :: dEcrit ! derivative of enthalpy with temperature at critical temperature + ! enthalpy real(rkind) :: enthVeg ! enthalpy of the vegetation (J m-3) real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) @@ -560,11 +564,11 @@ subroutine t2enthalpy(& ! *** compute enthalpy of water for frozen conditions else ! calculate enthalpy at the temperature (cubic spline interpolation) - call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),enthTemp,err,cmessage) + call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),enthTemp,dE,err,cmessage) if(err/=0) then; message=trim(message)//trim(cmessage); return; end if ! calculate enthalpy at the critical temperature (cubic spline interpolation) - call splint(Tk,Ey,E2,Tcrit,enthTcrit,err,cmessage) + call splint(Tk,Ey,E2,Tcrit,enthTcrit,dEcrit,err,cmessage) if(err/=0) then; message=trim(message)//trim(cmessage); return; end if ! calculate the enthalpy of water @@ -580,11 +584,11 @@ subroutine t2enthalpy(& ! derivatives dVolFracLiq_dTk = dTheta_dPsi(psiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m)*LH_fus/(gravity*Tfreeze) dTcrit_dPsi0 = 0._rkind - if (mlayerTempTrial(iLayer)<0._rkind) dTcrit_dPsi0 = gravity*Tfreeze/LH_fus + if (mLayerMatricHeadTrial(ixControlIndex)<0._rkind) dTcrit_dPsi0 = gravity*Tfreeze/LH_fus ! enthalpy derivatives - dEnthWater_dTk = 0._rkind ! THIS NEEDS TO BE COMPUTED, d_enthMix_dTk using splines + dEnthWater_dTk = dE dEnthPhase_dTk = -iden_water*LH_fus*dVolFracLiq_dTk - dEnthWater_dWat = iden_water*Cp_water*dVolTot_dPsi0(ixControlIndex)*(Tcrit - Tfreeze) + iden_water*Cp_water*volFracWat*dTcrit_dPsi0 + dEnthWater_dWat = -dEcrit*dTcrit_dPsi0 + iden_water*Cp_water*dVolTot_dPsi0(ixControlIndex)*(Tcrit - Tfreeze) + iden_water*Cp_water*volFracWat*dTcrit_dPsi0 dEnthPhase_dWat = iden_water*LH_fus*dVolTot_dPsi0(ixControlIndex) endif ! (if frozen conditions) @@ -602,7 +606,7 @@ subroutine t2enthalpy(& ! *** compute the total enthalpy (J m-3) mLayerEnthalpy(iLayer) = enthSoil + enthWater + enthAir dEnthalpy_dTk(iLayer) = dEnthSoil_dTk + dEnthWater_dTk + dEnthAir_dTk - dEnthalpy_dWat(iLayer) = dEnthSoil_dWat + dEnthWater_dWat + dEnthAir_dWat + dEnthalpy_dWat(iLayer) = dEnthSoil_dWat + dEnthWater_dWat + dEnthAir_dWat if (doPhase)then mLayerEnthalpy(iLayer) = mLayerEnthalpy(iLayer) - enthPhase dEnthalpy_dTk(iLayer) = dEnthalpy_dTk(iLayer) - dEnthPhase_dTk From e0e9a774be4f121e04932add40b397e0349d52cc Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 3 Jan 2023 23:25:16 +0900 Subject: [PATCH 0450/1472] add some utilities --- utils/SUMMA_concat_split_summa.py | 110 ++++++++++++++++++ ...SUMMA_timeseries_to_statistics_parallel.py | 92 +++++++++++++++ utils/sims_to_stats.sh | 24 ++++ utils/subsetGRU.sh | 3 +- 4 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 utils/SUMMA_concat_split_summa.py create mode 100644 utils/SUMMA_timeseries_to_statistics_parallel.py create mode 100755 utils/sims_to_stats.sh diff --git a/utils/SUMMA_concat_split_summa.py b/utils/SUMMA_concat_split_summa.py new file mode 100644 index 000000000..d3f4899ef --- /dev/null +++ b/utils/SUMMA_concat_split_summa.py @@ -0,0 +1,110 @@ +# concatenate the outputs of a split domain summa run +# written originally by Manab Saharia, updated by Hongli Liu and Andy Wood +# modified by W. Knoben (2021) +# Usage: python SUMMA_concat_split_summa.py [path/to/split/outputs/] [input_file_*_pattern.nc] [output_file.nc]. + +import sys +import warnings +warnings.simplefilter(action='ignore', category=FutureWarning) +import os +from glob import glob +import netCDF4 as nc +import numpy as np + +# --- check args +if len(sys.argv) != 4: + print("Usage: %s " % sys.argv[0]) + sys.exit(0) +# otherwise continue +ncdir = sys.argv[1] # eg './v1/06280300/' +file_pattern = sys.argv[2] # eg '*_G*_day.nc' +summa_runoff = sys.argv[3] # eg 'gage_06280300_day.nc' + +# get list of split summa output files (hardwired pattern) +outfilelist = glob((ncdir+'/'+file_pattern)) +outfilelist.sort() # not needed, perhaps + +# count the number of gru and hru +gru_num = 0 +hru_num = 0 +for file in outfilelist: + # f = nc.Dataset(os.path.join(ncdir, file)) + f = nc.Dataset(file) + gru_num = gru_num+len(f.dimensions['gru']) + hru_num = hru_num+len(f.dimensions['hru']) + +# write output +# with nc.Dataset(os.path.join(ncdir, outfilelist[0])) as src: +with nc.Dataset(outfilelist[0]) as src: + with nc.Dataset(os.path.join(ncdir, summa_runoff), "w") as dst: + + # copy dimensions + for name, dimension in src.dimensions.items(): + if name == 'gru': + dst.createDimension(name, gru_num) + elif name == 'hru': + dst.createDimension(name, hru_num) + else: + dst.createDimension(name, (len(dimension) if not dimension.isunlimited() else None)) + + # copy variable attributes all at once via dictionary + gru_vars = [] # variable name, gru axis in variable dimension for concatenation. + hru_vars = [] + for name, variable in src.variables.items(): + x = dst.createVariable(name, variable.datatype, variable.dimensions) + dst[name].setncatts(src[name].__dict__) + # Note here the variable dimension name is the same, but size has been updated for gru and hru. + + # Assign different values depending on dimension + dims = variable.dimensions + if 'gru' in dims: + gru_vars.append([name,dims.index('gru')]) + elif 'hru' in dims: + hru_vars.append([name,dims.index('hru')]) + else: + dst[name][:]=src[name][:] + + # read values for gru and hru dimensioned variables + Dict = {} + gru_vars_num = len(gru_vars) + hru_vars_num = len(hru_vars) + for i,file in enumerate(outfilelist): + + print("combining file %d %s" % (i,file)) + # f = nc.Dataset(os.path.join(ncdir, file)) + f = nc.Dataset(file) + for j in range(gru_vars_num): + gru_var_name = gru_vars[j][0] + dim_index = gru_vars[j][1] + data=f[gru_var_name][:] + if i == 0: + Dict[gru_var_name]=data + else: + Dict[gru_var_name]=np.concatenate((Dict[gru_var_name],data),axis=dim_index) + + for j in range(hru_vars_num): + hru_var_name = hru_vars[j][0] + dim_index = hru_vars[j][1] + data=f[hru_var_name][:] + if i == 0: + Dict[hru_var_name]=data + else: + Dict[hru_var_name]=np.concatenate((Dict[hru_var_name],data),axis=dim_index) + + # assign values for gru and hru dimensioned variables + for j in range(gru_vars_num): + dst.variables[gru_vars[j][0]][:] = Dict[gru_vars[j][0]] + for j in range(hru_vars_num): + dst.variables[hru_vars[j][0]][:] = Dict[hru_vars[j][0]] + + # Temporarily create gruId from hruId + #if gru_num == hru_num: + # gruId = dst.createVariable('gruId', dst['hruId'].datatype, ('gru',)) + # gruId.long_name = "ID of group of response unit (GRU)" + # gruId.units = dst['hruId'].units + # dst.variables['gruId'][:] = dst.variables['hruId'][:] + #else: + # print('Warning: gruId variable cannot be created since it has different size from hruId') + +print("wrote output: %s" % (ncdir+summa_runoff)) +print('Done') diff --git a/utils/SUMMA_timeseries_to_statistics_parallel.py b/utils/SUMMA_timeseries_to_statistics_parallel.py new file mode 100644 index 000000000..adf78485e --- /dev/null +++ b/utils/SUMMA_timeseries_to_statistics_parallel.py @@ -0,0 +1,92 @@ +# SUMMA can produce split-domain output files if the model is used with the -g command line option. +'''Loads timeseries of simulated variables and computes a variety of statistics.''' + +# This script analyzes the resulting files and summarizes the timeseries of a (set of) variable(s) into a statistical value. +# Currently implemented are finding the maximum and mean value across the entire time series. +# Outputs are stored in a single file that covers the full spatial extent of the domain. +# Note that this requires the Python package multiprocessing, which is not included in the provided environment.yml and requirements.txt files. +# written originally by W. Knoben, modified by A. Van Beusekom (2023) + +import os +import glob +import xarray as xr +from pathlib import Path +import multiprocessing as mp + +# Settings +method_name = 'be64' +src_dir = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/summa-' + method_name +src_pat = 'run1_G*_timestep.nc' +des_dir = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics/' + method_name +des_fil = 'run1_summa_day_stats_{}_{}_{}.nc' +settings= {'averageRoutedRunoff': 'mean'; 'wallClockTime': 'mean'; 'scalarTotalET': 'mean'; 'scalarSWE': 'mean'; 'scalarCanopyWat': 'mean'; 'scalarTotalSoilWat': 'mean'} +} +viz_fil = 'run1_summa_day_stats_{}_{}.nc' +viz_fil = viz_fil.format(','.join(settings.keys()),','.join(settings.values())) + +# Make sure we're dealing with the right kind of inputs +src_dir = Path(src_dir) +des_dir = Path(des_dir) + +# Ensure the output path exists +des_dir.mkdir(parents=True, exist_ok=True) + +# Get the names of all inputs +src_files = glob.glob(str( src_dir / src_pat )) +src_files.sort() + +# -- functions +def run_loop(file): + + # extract the subset IDs + subset = file.split('/')[-1].split('_')[1] + + # pass if file exists already + if os.path.isfile(des_dir / 'run1_summa_day_stats_mean_wallClockTime_{}'.format(subset)): + return + + # open file + dat = xr.open_dataset(file) + + # compute the requested statistics + for var,stat in settings.items(): + + # Select the case + if stat == 'mean': + new = dat[var].mean(dim='time') + elif stat == 'max': + new = dat[var].max(dim='time') + + new.to_netcdf(des_dir / des_fil.format(stat,var,subset)) + return + +def merge_subsets_into_one(src,pattern,des,name): + + '''Merges all files in {src} that match {pattern} into one file stored in /{des}/{name.nc}''' + + # Find all files + src_files = glob.glob(str( src / pattern )) + + # Merge into one + out = xr.merge([xr.open_dataset(file) for file in src_files]) + + # save to file + out.to_netcdf(des / name) + + return #nothing +# -- end functions + +# -- start parallel processing +ncpus = int(os.environ.get('SLURM_CPUS_PER_TASK',default=1)) +if __name__ == "__main__": + pool = mp.Pool(processes=ncpus) + pool.map(run_loop,src_files) + pool.close() +# -- end parallel processing + +# merge the individual files into one for further vizualization +merge_subsets_into_one(des_dir,des_fil.replace('{}','*'),des_dir,viz_fil) + +# remove the individual files for cleanliness +for file in glob.glob(des_fil.replace('{}','*')): + os.remove(file) \ No newline at end of file diff --git a/utils/sims_to_stats.sh b/utils/sims_to_stats.sh new file mode 100755 index 000000000..117650ed7 --- /dev/null +++ b/utils/sims_to_stats.sh @@ -0,0 +1,24 @@ +#!/bin/bash +module load StdEnv/2020 intel/2020.1.217 openmpi/4.0.3 cdo/1.9.8 + +# Define the simulation paths +root_path="/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/concat_summa-" +declare -a method_name=("sundials_1en6" "sundials_1en8") #"be1" "be32" "be64") +arraylength=${#method_name[@]} + +# Define a location where we can temporarily store the simulation files so we don't need to swap back and forth +dest_path="/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/simulation_stats" +mkdir -p $dest_path + +# Loop over all simulations and find the mean values we're after +for (( i=0; i<${arraylength}; i++ )); +do + summa_in="$root_path${method_name[$i]}.nc" + summa_out="${dest_path}/SUMMA_mean_${method_name[$i]}.nc" + + #echo "index: $i, SUMMA in : $summa_in" + #echo "index: $i, SUMMA out: $summa_out" + + echo "Working on ${method_name[$i]}" + cdo timmean -select,name=scalarTotalET,scalarTotalRunoff,gruId,hruId ${summa_in} ${summa_out} +done diff --git a/utils/subsetGRU.sh b/utils/subsetGRU.sh index e3de4299f..9c8fbf7d3 100644 --- a/utils/subsetGRU.sh +++ b/utils/subsetGRU.sh @@ -3,6 +3,7 @@ # # Inside forcingFile_out need to change forcingPath to = desForcingPath # Inside forcingFile_out need to change initConditionFile, attributeFile, trialParamFile to = _${GRU_file} versions +# written originally by A. Van Beusekom 2023, hardwired paths run on Copernicus module load StdEnv/2020 module load gcc/9.3.0 @@ -24,7 +25,7 @@ nbasin_slurm=$((GRU_file-gruCount*offset)) GRU_id=$GRU_file HRU_id=$GRU_id -echo "file name id is ${GRU_file}, GRU id is ${GRU_id}, HRU id is ${HRU_id}, number in batch is ${nbasin_slurm}" +echo "file name id is ${GRU_file}, GRU id is ${GRU_id}, HRU id is ${HRU_id}, number in batch is ${nbasin_slurm}" # top paths, change these to yours homeDir=/globalhome/gwu479/HPC/ From 4e8cba35d68fc3956c6876b23e1cf07fd903fea5 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 4 Jan 2023 18:00:02 +0900 Subject: [PATCH 0451/1472] adding utilities for plotting results --- utils/SUMMA_concat_groups_split_summa.py | 117 ++++++++ utils/SUMMA_concat_split_summa.py | 110 -------- ...SUMMA_timeseries_to_statistics_parallel.py | 37 ++- utils/plot_per_GRU.py | 252 ++++++++++++++++++ utils/sims_to_stats.sh | 24 -- 5 files changed, 392 insertions(+), 148 deletions(-) create mode 100644 utils/SUMMA_concat_groups_split_summa.py delete mode 100644 utils/SUMMA_concat_split_summa.py create mode 100644 utils/plot_per_GRU.py delete mode 100755 utils/sims_to_stats.sh diff --git a/utils/SUMMA_concat_groups_split_summa.py b/utils/SUMMA_concat_groups_split_summa.py new file mode 100644 index 000000000..558c62344 --- /dev/null +++ b/utils/SUMMA_concat_groups_split_summa.py @@ -0,0 +1,117 @@ +# concatenate the outputs of a split domain summa run into fewer groups +# written originally by Manab Saharia, updated by Hongli Liu and Andy Wood, and W. Knoben +# modified by A. Van Beusekom (2023) +# Usage: python SUMMA_concat_split_summa.py + +import sys +import warnings +warnings.simplefilter(action='ignore', category=FutureWarning) +import os +from glob import glob +import netCDF4 as nc +import numpy as np + +method_name = 'sundials_1en8' +catby_num = 2 #number of files to cat into one +ncdir = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/summa-' + method_name + '/' +file_pattern = 'run1_G*_timestep.nc' +ctdir = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/cat_summa-' + method_name + '/' + +# get list of split summa output files (hardwired pattern) +outfilelist0 = glob((ncdir+'/'+file_pattern)) +outfilelist0.sort() + +# make new outlist of catby_num +for i in 0:(len(outfilelist0)/catby_nums): + outfilelist = [outfilelist0[catby_nums*i:catby_nums*(i+1)] + print([i,len(outfilelist0)/catby_nums,outfilelist]) + # count the number of gru and hru + gru_num = 0 + hru_num = 0 + subset0 = outfilelist[0].split('/')[-1].split('_')[1] + subset1 = outfilelist[-1].split('/')[-1].split('_')[1] + out_name = 'run1_'+subset0[0:7]+subset1[7:14]+'_timestep.nc' # will fail if GRU numbers are more than 5 digits + + for file in outfilelist: + # f = nc.Dataset(os.path.join(ncdir, file)) + f = nc.Dataset(file) + gru_num = gru_num+len(f.dimensions['gru']) + hru_num = hru_num+len(f.dimensions['hru']) + # extract the subset IDs + + # write output + # with nc.Dataset(os.path.join(ncdir, outfilelist[0])) as src: + with nc.Dataset(outfilelist[0]) as src: + with nc.Dataset(os.path.join(ncdir, ctdir+out_name), "w") as dst: + + # copy dimensions + for name, dimension in src.dimensions.items(): + if name == 'gru': + dst.createDimension(name, gru_num) + elif name == 'hru': + dst.createDimension(name, hru_num) + else: + dst.createDimension(name, (len(dimension) if not dimension.isunlimited() else None)) + + # copy variable attributes all at once via dictionary + gru_vars = [] # variable name, gru axis in variable dimension for concatenation. + hru_vars = [] + for name, variable in src.variables.items(): + x = dst.createVariable(name, variable.datatype, variable.dimensions) + dst[name].setncatts(src[name].__dict__) + # Note here the variable dimension name is the same, but size has been updated for gru and hru. + + # Assign different values depending on dimension + dims = variable.dimensions + if 'gru' in dims: + gru_vars.append([name,dims.index('gru')]) + elif 'hru' in dims: + hru_vars.append([name,dims.index('hru')]) + else: + dst[name][:]=src[name][:] + + # read values for gru and hru dimensioned variables + Dict = {} + gru_vars_num = len(gru_vars) + hru_vars_num = len(hru_vars) + for i,file in enumerate(outfilelist): + + print("combining file %d %s" % (i,file)) + # f = nc.Dataset(os.path.join(ncdir, file)) + f = nc.Dataset(file) + for j in range(gru_vars_num): + gru_var_name = gru_vars[j][0] + dim_index = gru_vars[j][1] + data=f[gru_var_name][:] + if i == 0: + Dict[gru_var_name]=data + else: + Dict[gru_var_name]=np.concatenate((Dict[gru_var_name],data),axis=dim_index) + + for j in range(hru_vars_num): + hru_var_name = hru_vars[j][0] + dim_index = hru_vars[j][1] + data=f[hru_var_name][:] + if i == 0: + Dict[hru_var_name]=data + else: + Dict[hru_var_name]=np.concatenate((Dict[hru_var_name],data),axis=dim_index) + + # assign values for gru and hru dimensioned variables + for j in range(gru_vars_num): + dst.variables[gru_vars[j][0]][:] = Dict[gru_vars[j][0]] + for j in range(hru_vars_num): + dst.variables[hru_vars[j][0]][:] = Dict[hru_vars[j][0]] + + # Temporarily create gruId from hruId + #if gru_num == hru_num: + # gruId = dst.createVariable('gruId', dst['hruId'].datatype, ('gru',)) + # gruId.long_name = "ID of group of response unit (GRU)" + # gruId.units = dst['hruId'].units + # dst.variables['gruId'][:] = dst.variables['hruId'][:] + #else: + # print('Warning: gruId variable cannot be created since it has different size from hruId') + + print("wrote output: %s" % (ncdir+ctdir+out_name)) + +print('Done') diff --git a/utils/SUMMA_concat_split_summa.py b/utils/SUMMA_concat_split_summa.py deleted file mode 100644 index d3f4899ef..000000000 --- a/utils/SUMMA_concat_split_summa.py +++ /dev/null @@ -1,110 +0,0 @@ -# concatenate the outputs of a split domain summa run -# written originally by Manab Saharia, updated by Hongli Liu and Andy Wood -# modified by W. Knoben (2021) -# Usage: python SUMMA_concat_split_summa.py [path/to/split/outputs/] [input_file_*_pattern.nc] [output_file.nc]. - -import sys -import warnings -warnings.simplefilter(action='ignore', category=FutureWarning) -import os -from glob import glob -import netCDF4 as nc -import numpy as np - -# --- check args -if len(sys.argv) != 4: - print("Usage: %s " % sys.argv[0]) - sys.exit(0) -# otherwise continue -ncdir = sys.argv[1] # eg './v1/06280300/' -file_pattern = sys.argv[2] # eg '*_G*_day.nc' -summa_runoff = sys.argv[3] # eg 'gage_06280300_day.nc' - -# get list of split summa output files (hardwired pattern) -outfilelist = glob((ncdir+'/'+file_pattern)) -outfilelist.sort() # not needed, perhaps - -# count the number of gru and hru -gru_num = 0 -hru_num = 0 -for file in outfilelist: - # f = nc.Dataset(os.path.join(ncdir, file)) - f = nc.Dataset(file) - gru_num = gru_num+len(f.dimensions['gru']) - hru_num = hru_num+len(f.dimensions['hru']) - -# write output -# with nc.Dataset(os.path.join(ncdir, outfilelist[0])) as src: -with nc.Dataset(outfilelist[0]) as src: - with nc.Dataset(os.path.join(ncdir, summa_runoff), "w") as dst: - - # copy dimensions - for name, dimension in src.dimensions.items(): - if name == 'gru': - dst.createDimension(name, gru_num) - elif name == 'hru': - dst.createDimension(name, hru_num) - else: - dst.createDimension(name, (len(dimension) if not dimension.isunlimited() else None)) - - # copy variable attributes all at once via dictionary - gru_vars = [] # variable name, gru axis in variable dimension for concatenation. - hru_vars = [] - for name, variable in src.variables.items(): - x = dst.createVariable(name, variable.datatype, variable.dimensions) - dst[name].setncatts(src[name].__dict__) - # Note here the variable dimension name is the same, but size has been updated for gru and hru. - - # Assign different values depending on dimension - dims = variable.dimensions - if 'gru' in dims: - gru_vars.append([name,dims.index('gru')]) - elif 'hru' in dims: - hru_vars.append([name,dims.index('hru')]) - else: - dst[name][:]=src[name][:] - - # read values for gru and hru dimensioned variables - Dict = {} - gru_vars_num = len(gru_vars) - hru_vars_num = len(hru_vars) - for i,file in enumerate(outfilelist): - - print("combining file %d %s" % (i,file)) - # f = nc.Dataset(os.path.join(ncdir, file)) - f = nc.Dataset(file) - for j in range(gru_vars_num): - gru_var_name = gru_vars[j][0] - dim_index = gru_vars[j][1] - data=f[gru_var_name][:] - if i == 0: - Dict[gru_var_name]=data - else: - Dict[gru_var_name]=np.concatenate((Dict[gru_var_name],data),axis=dim_index) - - for j in range(hru_vars_num): - hru_var_name = hru_vars[j][0] - dim_index = hru_vars[j][1] - data=f[hru_var_name][:] - if i == 0: - Dict[hru_var_name]=data - else: - Dict[hru_var_name]=np.concatenate((Dict[hru_var_name],data),axis=dim_index) - - # assign values for gru and hru dimensioned variables - for j in range(gru_vars_num): - dst.variables[gru_vars[j][0]][:] = Dict[gru_vars[j][0]] - for j in range(hru_vars_num): - dst.variables[hru_vars[j][0]][:] = Dict[hru_vars[j][0]] - - # Temporarily create gruId from hruId - #if gru_num == hru_num: - # gruId = dst.createVariable('gruId', dst['hruId'].datatype, ('gru',)) - # gruId.long_name = "ID of group of response unit (GRU)" - # gruId.units = dst['hruId'].units - # dst.variables['gruId'][:] = dst.variables['hruId'][:] - #else: - # print('Warning: gruId variable cannot be created since it has different size from hruId') - -print("wrote output: %s" % (ncdir+summa_runoff)) -print('Done') diff --git a/utils/SUMMA_timeseries_to_statistics_parallel.py b/utils/SUMMA_timeseries_to_statistics_parallel.py index adf78485e..4d7a6f752 100644 --- a/utils/SUMMA_timeseries_to_statistics_parallel.py +++ b/utils/SUMMA_timeseries_to_statistics_parallel.py @@ -14,48 +14,57 @@ import multiprocessing as mp # Settings -method_name = 'be64' +method_name = 'sundials_1en6' +bench_name = 'cat_sundials_1en8' src_dir = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/summa-' + method_name +ben_dir = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/summa-' + bench_name src_pat = 'run1_G*_timestep.nc' -des_dir = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics/' + method_name -des_fil = 'run1_summa_day_stats_{}_{}_{}.nc' -settings= {'averageRoutedRunoff': 'mean'; 'wallClockTime': 'mean'; 'scalarTotalET': 'mean'; 'scalarSWE': 'mean'; 'scalarCanopyWat': 'mean'; 'scalarTotalSoilWat': 'mean'} -} -viz_fil = 'run1_summa_day_stats_{}_{}.nc' +des_dir = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics' +des_fil = method_name + '_hrly_diff_stats_{}_{}_{}.nc' +stat = 'max' +settings= {'averageRoutedRunoff': stat; 'wallClockTime': stat; 'scalarTotalET': stat; 'scalarSWE': stat; 'scalarCanopyWat': stat; 'scalarTotalSoilWat': stat} + +viz_fil = method_name + '_hrly_diff_stats_{}_{}.nc' viz_fil = viz_fil.format(','.join(settings.keys()),','.join(settings.values())) # Make sure we're dealing with the right kind of inputs src_dir = Path(src_dir) +ben_dir = Path(ben_dir) des_dir = Path(des_dir) # Ensure the output path exists des_dir.mkdir(parents=True, exist_ok=True) -# Get the names of all inputs +# Get the names of all inputs, assuming folders have same splits of domains and same file names src_files = glob.glob(str( src_dir / src_pat )) src_files.sort() # -- functions -def run_loop(file): +def run_loop(file,bench): # extract the subset IDs subset = file.split('/')[-1].split('_')[1] - # pass if file exists already - if os.path.isfile(des_dir / 'run1_summa_day_stats_mean_wallClockTime_{}'.format(subset)): - return - # open file dat = xr.open_dataset(file) + ben = xr.open_dataset(bench) + + diff = (np.fabs(dat - ben)) + # get rid of gru dimension, assuming they are same as the often are (everything now as hruId) + diff = diff.drop_vars(['hruId','gruId']) #'wallClockTime' + m = diff.drop_dims('hru') + m = m.rename({'gru': 'hru'}) + diff = diff.drop_dims('gru') + diff = xr.merge([diff,m]) # compute the requested statistics for var,stat in settings.items(): # Select the case if stat == 'mean': - new = dat[var].mean(dim='time') + new = diff[var].mean(dim='time') elif stat == 'max': - new = dat[var].max(dim='time') + new = diff[var].max(dim='time') new.to_netcdf(des_dir / des_fil.format(stat,var,subset)) return diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py new file mode 100644 index 000000000..9392e57ff --- /dev/null +++ b/utils/plot_per_GRU.py @@ -0,0 +1,252 @@ +# written originally by W. Knoben, modified by A. Van Beusekom (2023) + +## Visualize statistics per GRU +## Needs: +# Catchment shapefile with GRU delineation +# SUMMA output statistics + +## Special note +# SUMMA simulations have been preprocessed into single value statistics per model element, using auxiliary scripts in ~/utils +# To improve visualization of large lakes, HydroLAKES lake delineations are plotted on top of the catchment GRUs and river segments. +# Dealing with HydroLAKES inputs is not considered within scope of the workflow and therefore requires some manual downloading and preprocessing of this data for those who wish to reproduce this step. +# The relevant code is easily disabled by switching the plot_lakes = True flag to False. + +# modules +import pyproj +import matplotlib +import numpy as np +import xarray as xr +import geopandas as gpd +from pathlib import Path +import matplotlib.pyplot as plt +import matplotlib.gridspec as gridspec + +# Simulation statistics file locations +method_name = 'sundials_1en6' +stat = 'max' +settings= {'averageRoutedRunoff': stat; 'wallClockTime': stat; 'scalarTotalET': stat; 'scalarSWE': stat; 'scalarCanopyWat': stat; 'scalarTotalSoilWat': stat} +viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') +viz_fil = method_name + '_hrly_diff_stats_{}_{}.nc' +viz_fil = viz_fil.format(','.join(settings.keys()),','.join(settings.values())) + +# Specify variables of interest +plot_vars = ['averageRoutedRunoff','wallClockTime','scalarTotalET','scalarSWE','scalarCanopyWat','scalarTotalSoilWat'] +fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed.png' +fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) + +# Get the albers shapes +main = Path('/home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/albers_projection') + +# Plot lakes? +plot_lakes = False +# lakes shapefile WHERE IS THIS +lake_path = Path('C:/Globus endpoint/HydroLAKES/HydroLAKES_polys_v10_shp') +lake_name = 'HydroLAKES_polys_v10_subset_NA.shp' + +## Control file handling +# Store the name of the 'active' file in a variable +controlFile = 'plot_control_NorthAmerica.txt' + +# Function to extract a given setting from the control file +def read_from_control( file, setting ): + + # Open controlFile and ... + with open(file) as contents: + for line in contents: + + # ... find the line with the requested setting + if setting in line and not line.startswith('#'): + break + + # Extract the setting's value + substring = line.split('|',1)[1] # Remove the setting's name (split into 2 based on '|', keep only 2nd part) + substring = substring.split('#',1)[0] # Remove comments, does nothing if no '#' is found + substring = substring.strip() # Remove leading and trailing whitespace, tabs, newlines + + # Return this value + return substring + +# Function to specify a default path +def make_default_path(suffix): + + # Get the root path + rootPath = Path( read_from_control(controlFile,'root_path') ) + + # Get the domain folder + domainName = read_from_control(controlFile,'domain_name') + domainFolder = 'domain_' + domainName + + # Specify the forcing path + defaultPath = rootPath / domainFolder / suffix + + return defaultPath + + +## Catchment shapefile location and variable names + +# HM catchment shapefile path & name +hm_catchment_path = read_from_control(controlFolder/controlFile,'catchment_shp_path') +hm_catchment_name = read_from_control(controlFolder/controlFile,'catchment_shp_name') +# Specify default path if needed +if hm_catchment_path == 'default': + hm_catchment_path = make_default_path('shapefiles/catchment') # outputs a Path() +else: + hm_catchment_path = Path(hm_catchment_path) # make sure a user-specified path is a Path() + +# Find the GRU and HRU identifiers +hm_hruid = read_from_control(controlFolder/controlFile,'catchment_shp_hruid') + + +## River network shapefile location and variable names + +# River network path & name +river_network_path = read_from_control(controlFolder/controlFile,'river_network_shp_path') +river_network_name = read_from_control(controlFolder/controlFile,'river_network_shp_name') +# Specify default path if needed +if river_network_path == 'default': + river_network_path = make_default_path('shapefiles/river_network') # outputs a Path() +else: + river_network_path = Path(river_network_path) # make sure a user-specified path is a Path() + +# Find the segment ID +seg_id = read_from_control(controlFolder/controlFile,'river_network_shp_segid') + + +## Load all shapefiles and project to Albers Conformal Conic + +# catchment shapefile +bas = gpd.read_file(hm_catchment_path/hm_catchment_name) +# river network shapefile +riv = gpd.read_file(river_network_path/river_network_name) +# lakes shapefile +if plot_lakes: lakes = gpd.read_file(lake_path/lake_name) + +# Set the target CRS +acc = 'ESRI:102008' + +# Reproject +bas_albers = bas.to_crs(acc) +riv_albers = riv.to_crs(acc) +if plot_lakes: lak_albers = lakes.to_crs(acc) + +# Print the median basin size for curiousity +print('median area = {} m^2'.format(bas['HRU_area'].median() / 10**6)) +print('mean area = {} m^2'.format(bas['HRU_area'].mean() / 10**6)) + +#median area = 33.06877343600296 m^2 +#mean area = 40.19396140285971 m^2 + +lak_albers = gpd.read_file(main/'lakes.shp') +riv_albers = gpd.read_file(main/'river.shp') +bas_albers = gpd.read_file(main/'basin.shp') + +## Pre-processing, map SUMMA sims to catchment shapes +# Get the aggregated statistics of SUMMA simulations +summa = xr.open_dataset(viz_dir/vis_fil) + +# Match the accummulated values to the correct HRU IDs in the shapefile +hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile +for plot_var in plot_vars: + bas_albers[plot_var] = summa[plot_var].sel(hru=hru_ids_shp.values) + + +# Select lakes of a certain size for plotting +if plot_lakes: + minSize = 1000 # km2 + in_domain = (lak_albers['Country'] == 'Canada') | \ + (lak_albers['Country'] == 'United States of America') | \ + (lak_albers['Country'] == 'Mexico') + out_domain = (lak_albers['Pour_long'] > -80) & (lak_albers['Pour_lat'] > 65) # Exclude Baffin Island + large_lakes_albers = lak_albers.loc[(lak_albers['Lake_area'] > minSize) & in_domain & (~out_domain) ] +# Set the lake color +if plot_lakes: + lake_col = (8/255,81/255,156/255) + +##Figure + +# Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug +if 'compressed' in fig_fil: + plt.rcParams.update({'font.size': 25}) +else: + plt.rcParams.update({'font.size': 100}) + +# Flip the evaporation values so that they become positive, not if plotting diffs +#bas_albers['plot_ET'] = bas_albers['scalarTotalET'] * -1 +#bas_albers['plot_ET'] = bas_albers['plot_ET'].where(bas_albers['scalarTotalET'] != -9999, np.nan) + +if 'compressed' in fig_fil: + fig,axs = plt.subplots(3,2,figsize=(35,33)) +else: + fig,axs = plt.subplots(3,2,figsize=(140,133)) + +# colorbar axes +cax1 = fig.add_axes([0.473,0.60,0.02,0.3]) +cax2 = fig.add_axes([0.97 ,0.60,0.02,0.3]) +cax3 = fig.add_axes([0.473,0.10,0.02,0.3]) +cax4 = fig.add_axes([0.97 ,0.10,0.02,0.3]) + +plt.tight_layout() + +# add maps +var = 'scalarSWE' +bas_albers.plot(ax=axs[0,0], column=var, edgecolor='none', legend=True,\ + cmap='Greys_r', cax=cax1, norm=norm, zorder=0) +axs[0,0].set_title('(a) Snow Water Equivalent Absolute '+stat+ ' Diffs') +axs[0,0].axis('off') +cax1.set_ylabel('scalarSWE $[kg~m^{-2}]$',labelpad=-600) + +# SM +var = 'scalarTotalSoilWat' +norm = matplotlib.colors.LogNorm(vmin=bas_albers[var].min(), vmax=bas_albers[var].max()) +bas_albers.plot(ax=axs[0,1], column=var, edgecolor='none', legend=True,\ + cmap='cividis_r', cax=cax2, norm=norm, zorder=0) +axs[0,1].set_title('(b) Total soil water content Absolute '+stat+ ' Diffs') +axs[0,1].axis('off') +cax2.set_ylabel('scalarTotalSoilWat $[kg~m^{-2}]$',labelpad=-600) + +# ET +var = 'scalarTotalET' +norm = matplotlib.colors.LogNorm(vmin=bas_albers[var].min(), vmax=bas_albers[var].max()) +bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True,\ + cmap='viridis', cax=cax3, norm=norm, zorder=0) +axs[1,0].set_title('(c) Total evapotranspiration Absolute '+stat+ ' Diffs') +axs[1,0].axis('off') +cax3.set_ylabel('scalarTotalET $[kg~m^{-2}~s^{-1}]$',labelpad=-600) + +# CanWat +var = 'scalarCanopyWat' +norm = matplotlib.colors.LogNorm(vmin=bas_albers[var].min(), vmax=bas_albers[var].max()) +bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True,\ + cmap='viridis_r', cax=cax3, norm=norm, zorder=0) +axs[1,1].set_title('(d) Total water on the vegetation canopy Absolute '+stat+ ' Diffs') +axs[1,1].axis('off') +cax3.set_ylabel('scalarCanopyWat $[kg~m^{-2}]$',labelpad=-600) + +# Runoff +var = 'averageRoutedRunoff' +norm = matplotlib.colors.LogNorm(vmin=bas_albers[var].min(), vmax=bas_albers[var].max()) +bas_albers.plot(ax=axs[2,0], column=var, edgecolor='none', legend=True,\ + cmap='Blues', cax=cax3, norm=norm, zorder=0) +axs[2,0].set_title('(e) Routed runoff Absolute '+stat+ ' Diffs') +axs[2,0].axis('off') +cax3.set_ylabel('averageRoutedRunoff $[m~s^{-1}]$',labelpad=-600) + +# Clock time +var = 'wallClockTime(' +norm = matplotlib.colors.LogNorm(vmin=bas_albers[var].min(), vmax=bas_albers[var].max()) +bas_albers.plot(ax=axs[2,1], column=var, edgecolor='none', legend=True,\ + cmap='Greys', cax=cax3, norm=norm, zorder=0) +axs[2,1].set_title('(f) Wall clock time Absolute '+stat+ ' Diffs') +axs[2,1].axis('off') +cax3.set_ylabel('wallClockTime( $[s]$',labelpad=-600) + + +# lakes +if plot_lakes: + large_lakes_albers.plot(ax=axs[0,0], color=lake_col, zorder=1) + large_lakes_albers.plot(ax=axs[0,1], color=lake_col, zorder=1) + large_lakes_albers.plot(ax=axs[1,0], color=lake_col, zorder=1) + large_lakes_albers.plot(ax=axs[1,1], color=lake_col, zorder=1) + +# Save +plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=True) diff --git a/utils/sims_to_stats.sh b/utils/sims_to_stats.sh deleted file mode 100755 index 117650ed7..000000000 --- a/utils/sims_to_stats.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -module load StdEnv/2020 intel/2020.1.217 openmpi/4.0.3 cdo/1.9.8 - -# Define the simulation paths -root_path="/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/concat_summa-" -declare -a method_name=("sundials_1en6" "sundials_1en8") #"be1" "be32" "be64") -arraylength=${#method_name[@]} - -# Define a location where we can temporarily store the simulation files so we don't need to swap back and forth -dest_path="/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/simulation_stats" -mkdir -p $dest_path - -# Loop over all simulations and find the mean values we're after -for (( i=0; i<${arraylength}; i++ )); -do - summa_in="$root_path${method_name[$i]}.nc" - summa_out="${dest_path}/SUMMA_mean_${method_name[$i]}.nc" - - #echo "index: $i, SUMMA in : $summa_in" - #echo "index: $i, SUMMA out: $summa_out" - - echo "Working on ${method_name[$i]}" - cdo timmean -select,name=scalarTotalET,scalarTotalRunoff,gruId,hruId ${summa_in} ${summa_out} -done From 3b2b1b9de2b5135fe2ca3f6c3f7b2b5ab10d968c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 5 Jan 2023 11:43:11 +0900 Subject: [PATCH 0452/1472] debugged plotting files --- utils/check_bit_4_bit.py | 51 ------- utils/check_bit_4_bit_withTol.py | 130 ++++++++++++++++++ ..._summa.py => concat_groups_split_summa.py} | 22 +-- utils/plot_per_GRU.py | 2 +- ...MA_summarize_logs.py => summarize_logs.py} | 2 +- ...arallel.py => timeseries_to_statistics.py} | 53 ++++--- 6 files changed, 179 insertions(+), 81 deletions(-) delete mode 100644 utils/check_bit_4_bit.py create mode 100644 utils/check_bit_4_bit_withTol.py rename utils/{SUMMA_concat_groups_split_summa.py => concat_groups_split_summa.py} (84%) rename utils/{SUMMA_summarize_logs.py => summarize_logs.py} (98%) rename utils/{SUMMA_timeseries_to_statistics_parallel.py => timeseries_to_statistics.py} (68%) diff --git a/utils/check_bit_4_bit.py b/utils/check_bit_4_bit.py deleted file mode 100644 index f3d265600..000000000 --- a/utils/check_bit_4_bit.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python -import sys -import numpy as np -import xarray as xr -from pathlib import Path - -PRINT_MESSAGE = 'Test {0: >3} - Filename: {1: <64} - Total difference: {2: <8}' -ERROR_MESSAGE = 'Found differences in test cases: {}' -SUCCESS_MESSAGE = 'SUCCESSFUL TEST RUN: All files match!' -USAGE = 'USAGE: ./check_bit_4_bit.py BENCHMARK_DIRECTORY TEST_DIRECTORY' - -if len(sys.argv) != 3: - print(USAGE) - exit(1) - -bench_dir = sys.argv[1] -test_dir = sys.argv[2] - -bench_files = sorted(list(Path(bench_dir).glob('**/*.nc'))) -test_files = sorted(list(Path(test_dir).glob('**/*.nc'))) - - -assert len(bench_files) == len(test_files), \ - 'Found {} files but need {}!'.format(len(test_files), len(bench_files)) - - -def rem(li1, li2): - return list(set(li1) - set(li2)) - - -def compute_diffs(bench_files, test_files): - all_diffs = [] - all_tots = [] - for i, (f1, f2) in enumerate(zip(bench_files, test_files)): - ds1, ds2 = xr.open_dataset(str(f1)), xr.open_dataset(str(f2)) - diff = (ds1 - ds2).sum(dim='time') - tot = 0.0 - for v in rem(list(diff.variables.keys()), list(diff.dims.keys())): - tot += np.sum(diff[v].values) - all_diffs.append(diff) - all_tots.append(tot) - print(PRINT_MESSAGE.format(i, str(f1).split('/')[-1], tot)) - return all_diffs, all_tots - - -all_diffs, all_tots = compute_diffs(bench_files, test_files) - -assert np.sum(np.absolute(all_tots)) == 0.0, \ - ERROR_MESSAGE.format(np.argwhere(np.asarray(all_tots) != 0)) - -print(SUCCESS_MESSAGE) diff --git a/utils/check_bit_4_bit_withTol.py b/utils/check_bit_4_bit_withTol.py new file mode 100644 index 000000000..f34a18e32 --- /dev/null +++ b/utils/check_bit_4_bit_withTol.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python + +#module load python/3.10 +#module load scipy-stack +#source nump_xarray/bin/activate + + +import sys +import numpy as np +import xarray as xr +from pathlib import Path + +PRINT_MESSAGE = 'Test {0: >3} - Filename: {1: <64} - Variable: {2: <64} - Mean and Max difference: {3: <8}, {4: <8}' +FINAL_MESSAGE = 'Variable: {0: >20} - HRU: {1: <8} - Max: {2: <25} - Time: {3: <30}' +ERROR_MESSAGE = 'Found differences in test cases: {}' +SUCCESS_MESSAGE = 'SUCCESSFUL TEST RUN: All files match within tolerence!' +USAGE = 'USAGE: python check_bit_4_bit_withTol.py BENCHMARK_DIRECTORY TEST_DIRECTORY TOLERENCE' + +#if len(sys.argv) != 4: +# print(USAGE) +# exit(1) + +bench_dir = 'summa_be' #sys.argv[1] +test_dir = 'summa-sundials_be' #sys.argv[2] +tol = 0.1 #sys.argv[3] + +bench_files = sorted(list(Path(bench_dir).glob('**/*.nc'))) +test_files = sorted(list(Path(test_dir).glob('**/*.nc'))) + + +assert len(bench_files) == len(test_files), \ + 'Found {} files but need {}!'.format(len(test_files), len(bench_files)) + + +def rem(li1, li2): + return list(set(li1) - set(li2)) + + +def compute_diffs(bench_files, test_files): + all_diffs_mean = [] + all_times_max = [] + all_diffs_max = [] + all_tots_mean = [] + all_tots_max = [] + for i, (f1, f2) in enumerate(zip(bench_files, test_files)): + ds1, ds2 = xr.open_dataset(str(f1)), xr.open_dataset(str(f2)) + #m = (ds1['scalarSWE'].sel(hru = 71003766)- ds2['scalarSWE'].sel(hru = 71003766)) + m = (ds1['scalarSWE'].sel(time = '1981-05-02T14:00:00.000013408')- ds2['scalarSWE'].sel(time = '1981-05-02T14:00:00.000013408')) + m.plot() + diff = (np.fabs(ds1 - ds2)) + # get rid of gru dimension, assuming they are same as the often are + diff = diff.drop_vars(['hruId','gruId','wallClockTime']) + m = diff.drop_dims('hru') + m = m.rename({'gru': 'hru'}) + diff = diff.drop_dims('gru') + diff = xr.merge([diff,m]) + # take means and maxes + diff_mean = diff.mean(dim='time') + time_max = diff.idxmax(dim='time') + #for v in rem(list(diff.variables.keys()), list(diff.dims.keys())): + # m = diff[v].sel(hru = np.int(diff[v].idxmax(dim="hru").values[0])) + # print(v, m.idxmax().values,np.int(diff[v].idxmax(dim="hru").values[0]),diff[v].max(dim="hru").values[0]) + diff_max = diff.max(dim='time') + tot_mean = diff_mean.mean(dim='hru') + tot_max = diff_max.max(dim='hru') + #for v in rem(list(diff_mean.variables.keys()), list(diff_mean.dims.keys())): + #print(PRINT_MESSAGE.format(i, str(f1).split('/')[-1], v, tot_mean[v].values, tot_max[v].values)) + all_diffs_mean.append(diff_mean) + all_times_max.append(time_max) + all_diffs_max.append(diff_max) + all_tots_mean.append(tot_mean) + all_tots_max.append(tot_max) + return all_diffs_mean, all_times_max, all_diffs_max, all_tots_mean, all_tots_max,i + + +all_diffs_mean, all_times_max, all_diffs_max, all_tots_mean, all_tots_max,i = compute_diffs(bench_files, test_files) + +combined_mean = xr.concat(all_diffs_mean, dim='hru') +combined_mean.to_netcdf("all_diffs_mean.nc") + +combined_time = xr.concat(all_times_max, dim='hru') +combined_max = xr.concat(all_diffs_max, dim='hru') +combined_max.to_netcdf("all_diffs_max.nc") +max_ind = combined_max.idxmax(dim='hru') + +combined_tmean = xr.concat(all_tots_mean, dim='hru') +combined_tmax = xr.concat(all_tots_max, dim='hru') + +#assert np.fabs(combined_tmean).max(dim='hru') - tol*(i-1) <= 0.0, \ +# ERROR_MESSAGE.format(np.argwhere(np.asarray(combined_tmean)-tol*(i-1)> 0.0)) + +#print(SUCCESS_MESSAGE) +print(combined_tmean) + + +# HRU - 71000000 is the GRU if a 71* HRU, gruId, hruId is the first HRU, if value is first HRU then all values same +for v in rem(list(combined_max.variables.keys()), list(combined_time.dims.keys())): + print(FINAL_MESSAGE.format(v, np.int(max_ind[v].values), combined_max[v].sel(hru = np.int(max_ind[v].values)).values, combined_time[v].sel(hru = np.int(max_ind[v].values)).values)) + + + +bench_files = "run_3766_summa_be.nc" +f1 = bench_files + +#test_files = ["run_3766_summa_be_conv.nc","run_3766_summa-sundials_beclosed_new.nc","run_3766_summa-sundials_closed_new.nc","run_3766_summa-sundials_closed.nc","run_3766_summa-sundials_enthal_new.nc","run_3766_summa-sundials_enthal_new_noderiv.nc","run_3766_summa-sundials_enthal.nc","run_3766_summa-sundials_beclosed_new_noCp.nc","run_3766_summa-sundials_closed_new_noCp.nc"] +#test_files = ["run_3766_summa-sundials_beclosed_new.nc","run_3766_summa-sundials_beclosed_new_noCp.nc","run_3766_summa-sundials_beclosed_new_noCp_oldJac.nc"] +test_files = ["run_3766_sun_updateCp_withDeriv.nc","run_3766_be_updateCp_withDeriv.nc","run_3766_be_original_withDeriv.nc","run_3766_be_orginal.nc"] +vars = ['averageRoutedRunoff','scalarTotalET','scalarTotalSoilWat','scalarSWE','scalarCanopyWat'] + +for j, v in enumerate(vars): + for i, f2 in enumerate(test_files): + ds1, ds2 = xr.open_dataset(f1), xr.open_dataset(f2) + m = ds1[v]- ds2[v] + m.plot() + + plt.gca().legend(test_files) + plt.show() + +test_files = ["run_3766_nrgWatTermOrderSwitch_commita2c16c8c.nc","run_3766_nrgWatTermOrderSame_commitec1f42b4.nc"] +vars = ['averageRoutedRunoff','scalarTotalET','scalarTotalSoilWat','scalarSWE','scalarCanopyWat'] + +for j, v in enumerate(vars): + for i, f2 in enumerate(test_files): + ds1, ds2 = xr.open_dataset(f1), xr.open_dataset(f2) + m = ds1[v]- ds2[v] + m.plot() + + plt.gca().legend(test_files) + plt.show() + diff --git a/utils/SUMMA_concat_groups_split_summa.py b/utils/concat_groups_split_summa.py similarity index 84% rename from utils/SUMMA_concat_groups_split_summa.py rename to utils/concat_groups_split_summa.py index 558c62344..aed814ca3 100644 --- a/utils/SUMMA_concat_groups_split_summa.py +++ b/utils/concat_groups_split_summa.py @@ -1,7 +1,7 @@ # concatenate the outputs of a split domain summa run into fewer groups # written originally by Manab Saharia, updated by Hongli Liu and Andy Wood, and W. Knoben # modified by A. Van Beusekom (2023) -# Usage: python SUMMA_concat_split_summa.py +# Usage: python concat_split_summa.py import sys import warnings @@ -13,24 +13,27 @@ method_name = 'sundials_1en8' catby_num = 2 #number of files to cat into one -ncdir = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/summa-' + method_name + '/' +top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' +#top_fold = '/Users/amedin/Research/USask/test_py/' + +ncdir = top_fold + 'summa-' + method_name file_pattern = 'run1_G*_timestep.nc' -ctdir = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/cat_summa-' + method_name + '/' +ctdir = top_fold + 'summa-' + method_name + '_cat' # get list of split summa output files (hardwired pattern) outfilelist0 = glob((ncdir+'/'+file_pattern)) outfilelist0.sort() # make new outlist of catby_num -for i in 0:(len(outfilelist0)/catby_nums): - outfilelist = [outfilelist0[catby_nums*i:catby_nums*(i+1)] - print([i,len(outfilelist0)/catby_nums,outfilelist]) +for i in range(0,int(len(outfilelist0)/catby_num)): + outfilelist = outfilelist0[catby_num*i:catby_num*(i+1)] + print((i,len(outfilelist0)/catby_num,outfilelist)) # count the number of gru and hru gru_num = 0 hru_num = 0 subset0 = outfilelist[0].split('/')[-1].split('_')[1] subset1 = outfilelist[-1].split('/')[-1].split('_')[1] - out_name = 'run1_'+subset0[0:7]+subset1[7:14]+'_timestep.nc' # will fail if GRU numbers are more than 5 digits + out_name = 'run1_'+subset0[0:7]+subset1[7:14]+'_timestep.nc' # will fail if GRU numbers are more than 6 digits for file in outfilelist: # f = nc.Dataset(os.path.join(ncdir, file)) @@ -40,9 +43,8 @@ # extract the subset IDs # write output - # with nc.Dataset(os.path.join(ncdir, outfilelist[0])) as src: with nc.Dataset(outfilelist[0]) as src: - with nc.Dataset(os.path.join(ncdir, ctdir+out_name), "w") as dst: + with nc.Dataset(ctdir+'/'+out_name, "w") as dst: # copy dimensions for name, dimension in src.dimensions.items(): @@ -112,6 +114,6 @@ #else: # print('Warning: gruId variable cannot be created since it has different size from hruId') - print("wrote output: %s" % (ncdir+ctdir+out_name)) + print("wrote output: %s" % (ctdir+'/'+out_name)) print('Done') diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 9392e57ff..05ae45ca6 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -24,7 +24,7 @@ # Simulation statistics file locations method_name = 'sundials_1en6' stat = 'max' -settings= {'averageRoutedRunoff': stat; 'wallClockTime': stat; 'scalarTotalET': stat; 'scalarSWE': stat; 'scalarCanopyWat': stat; 'scalarTotalSoilWat': stat} +settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') viz_fil = method_name + '_hrly_diff_stats_{}_{}.nc' viz_fil = viz_fil.format(','.join(settings.keys()),','.join(settings.values())) diff --git a/utils/SUMMA_summarize_logs.py b/utils/summarize_logs.py similarity index 98% rename from utils/SUMMA_summarize_logs.py rename to utils/summarize_logs.py index 6efb355cd..5ec62d657 100644 --- a/utils/SUMMA_summarize_logs.py +++ b/utils/summarize_logs.py @@ -1,6 +1,6 @@ '''Summarize all SUMMA logs in a folder. Assumes all .txt files in folder are SUMMA logs. Summary file is placed inside the log folder. Specifying a summary file name is optional. -Usage: python SUMMA_summarize_logs.py [log_folder] [name_of_summary_file.txt] [log file extension]''' +Usage: python summarize_logs.py [log_folder] [name_of_summary_file.txt] [log file extension]''' # Modules import os diff --git a/utils/SUMMA_timeseries_to_statistics_parallel.py b/utils/timeseries_to_statistics.py similarity index 68% rename from utils/SUMMA_timeseries_to_statistics_parallel.py rename to utils/timeseries_to_statistics.py index 4d7a6f752..2408660a4 100644 --- a/utils/SUMMA_timeseries_to_statistics_parallel.py +++ b/utils/timeseries_to_statistics.py @@ -4,25 +4,28 @@ # This script analyzes the resulting files and summarizes the timeseries of a (set of) variable(s) into a statistical value. # Currently implemented are finding the maximum and mean value across the entire time series. # Outputs are stored in a single file that covers the full spatial extent of the domain. -# Note that this requires the Python package multiprocessing, which is not included in the provided environment.yml and requirements.txt files. # written originally by W. Knoben, modified by A. Van Beusekom (2023) import os import glob import xarray as xr from pathlib import Path -import multiprocessing as mp +import numpy as np +#import multiprocessing as mp # Settings method_name = 'sundials_1en6' -bench_name = 'cat_sundials_1en8' -src_dir = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/summa-' + method_name -ben_dir = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/summa-' + bench_name +bench_name = 'sundials_1en8_cat' +top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' +#top_fold = '/Users/amedin/Research/USask/test_py/' + +src_dir = top_fold + 'summa-' + method_name +ben_dir = top_fold + 'summa-' + bench_name src_pat = 'run1_G*_timestep.nc' -des_dir = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics' +des_dir = top_fold + 'statistics' des_fil = method_name + '_hrly_diff_stats_{}_{}_{}.nc' stat = 'max' -settings= {'averageRoutedRunoff': stat; 'wallClockTime': stat; 'scalarTotalET': stat; 'scalarSWE': stat; 'scalarCanopyWat': stat; 'scalarTotalSoilWat': stat} +settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} viz_fil = method_name + '_hrly_diff_stats_{}_{}.nc' viz_fil = viz_fil.format(','.join(settings.keys()),','.join(settings.values())) @@ -38,20 +41,25 @@ # Get the names of all inputs, assuming folders have same splits of domains and same file names src_files = glob.glob(str( src_dir / src_pat )) src_files.sort() +ben_files = glob.glob(str( ben_dir / src_pat )) +ben_files.sort() + +assert len(ben_files) == len(src_files), \ + 'Found {} files but need {}!'.format(len(src_files), len(ben_files)) + # -- functions def run_loop(file,bench): - + # extract the subset IDs subset = file.split('/')[-1].split('_')[1] # open file - dat = xr.open_dataset(file) - ben = xr.open_dataset(bench) + dat,ben = xr.open_dataset(file), xr.open_dataset(bench) diff = (np.fabs(dat - ben)) # get rid of gru dimension, assuming they are same as the often are (everything now as hruId) - diff = diff.drop_vars(['hruId','gruId']) #'wallClockTime' + diff = diff.drop_vars(['hruId','gruId']) m = diff.drop_dims('hru') m = m.rename({'gru': 'hru'}) diff = diff.drop_dims('gru') @@ -67,6 +75,9 @@ def run_loop(file,bench): new = diff[var].max(dim='time') new.to_netcdf(des_dir / des_fil.format(stat,var,subset)) + + print("wrote output: %s" % (top_fold + 'statistics/' +subset)) + return def merge_subsets_into_one(src,pattern,des,name): @@ -85,17 +96,23 @@ def merge_subsets_into_one(src,pattern,des,name): return #nothing # -- end functions +for i, (file, bench) in enumerate(zip(src_files,ben_files)): + run_loop(file, bench) + # -- start parallel processing -ncpus = int(os.environ.get('SLURM_CPUS_PER_TASK',default=1)) -if __name__ == "__main__": - pool = mp.Pool(processes=ncpus) - pool.map(run_loop,src_files) - pool.close() +#ncpus = int(os.environ.get('SLURM_CPUS_PER_TASK',default=1)) +#if __name__ == "__main__": +# pool = mp.Pool(processes=ncpus) +# pool.map(run_loop,src_files) +# pool.close() # -- end parallel processing + # merge the individual files into one for further vizualization merge_subsets_into_one(des_dir,des_fil.replace('{}','*'),des_dir,viz_fil) # remove the individual files for cleanliness -for file in glob.glob(des_fil.replace('{}','*')): - os.remove(file) \ No newline at end of file +for file in glob.glob(str(des_dir / des_fil.replace('{}','*'))): + os.remove(file) + + \ No newline at end of file From f987ff98c1fa86e64a9e48a95aaba6908af2c120 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 5 Jan 2023 12:03:16 +0900 Subject: [PATCH 0453/1472] more plotting --- utils/concat_groups_split_summa.py | 2 -- utils/plot_per_GRU.py | 54 ++++++++++++++++-------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/utils/concat_groups_split_summa.py b/utils/concat_groups_split_summa.py index aed814ca3..fb3129ed7 100644 --- a/utils/concat_groups_split_summa.py +++ b/utils/concat_groups_split_summa.py @@ -3,10 +3,8 @@ # modified by A. Van Beusekom (2023) # Usage: python concat_split_summa.py -import sys import warnings warnings.simplefilter(action='ignore', category=FutureWarning) -import os from glob import glob import netCDF4 as nc import numpy as np diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 05ae45ca6..4105ffab6 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -12,14 +12,14 @@ # The relevant code is easily disabled by switching the plot_lakes = True flag to False. # modules -import pyproj +#import pyproj import matplotlib -import numpy as np +#import numpy as np import xarray as xr import geopandas as gpd from pathlib import Path import matplotlib.pyplot as plt -import matplotlib.gridspec as gridspec +#import matplotlib.gridspec as gridspec # Simulation statistics file locations method_name = 'sundials_1en6' @@ -83,10 +83,9 @@ def make_default_path(suffix): ## Catchment shapefile location and variable names - # HM catchment shapefile path & name -hm_catchment_path = read_from_control(controlFolder/controlFile,'catchment_shp_path') -hm_catchment_name = read_from_control(controlFolder/controlFile,'catchment_shp_name') +hm_catchment_path = read_from_control(controlFile,'catchment_shp_path') +hm_catchment_name = read_from_control(controlFile,'catchment_shp_name') # Specify default path if needed if hm_catchment_path == 'default': hm_catchment_path = make_default_path('shapefiles/catchment') # outputs a Path() @@ -94,14 +93,15 @@ def make_default_path(suffix): hm_catchment_path = Path(hm_catchment_path) # make sure a user-specified path is a Path() # Find the GRU and HRU identifiers -hm_hruid = read_from_control(controlFolder/controlFile,'catchment_shp_hruid') +hm_hruid = read_from_control(controlFile,'catchment_shp_hruid') ## River network shapefile location and variable names - +# Plot rivers? +plot_rivers = False # River network path & name -river_network_path = read_from_control(controlFolder/controlFile,'river_network_shp_path') -river_network_name = read_from_control(controlFolder/controlFile,'river_network_shp_name') +river_network_path = read_from_control(controlFile,'river_network_shp_path') +river_network_name = read_from_control(controlFile,'river_network_shp_name') # Specify default path if needed if river_network_path == 'default': river_network_path = make_default_path('shapefiles/river_network') # outputs a Path() @@ -109,25 +109,30 @@ def make_default_path(suffix): river_network_path = Path(river_network_path) # make sure a user-specified path is a Path() # Find the segment ID -seg_id = read_from_control(controlFolder/controlFile,'river_network_shp_segid') +seg_id = read_from_control(controlFile,'river_network_shp_segid') -## Load all shapefiles and project to Albers Conformal Conic +## Load all shapefiles and project to Albers Conformal Conic and reproject +# Set the target CRS +acc = 'ESRI:102008' # catchment shapefile bas = gpd.read_file(hm_catchment_path/hm_catchment_name) +bas_albers = bas.to_crs(acc) +bas_albers = gpd.read_file(main/'basin.shp') + # river network shapefile -riv = gpd.read_file(river_network_path/river_network_name) -# lakes shapefile -if plot_lakes: lakes = gpd.read_file(lake_path/lake_name) +if plot_rivers: + riv = gpd.read_file(river_network_path/river_network_name) + riv_albers = riv.to_crs(acc) + riv_albers = gpd.read_file(main/'river.shp') -# Set the target CRS -acc = 'ESRI:102008' +# lakes shapefile +if plot_lakes: + lakes = gpd.read_file(lake_path/lake_name) + lakes.to_crs(acc) + lak_albers = gpd.read_file(main/'lakes.shp') -# Reproject -bas_albers = bas.to_crs(acc) -riv_albers = riv.to_crs(acc) -if plot_lakes: lak_albers = lakes.to_crs(acc) # Print the median basin size for curiousity print('median area = {} m^2'.format(bas['HRU_area'].median() / 10**6)) @@ -136,13 +141,9 @@ def make_default_path(suffix): #median area = 33.06877343600296 m^2 #mean area = 40.19396140285971 m^2 -lak_albers = gpd.read_file(main/'lakes.shp') -riv_albers = gpd.read_file(main/'river.shp') -bas_albers = gpd.read_file(main/'basin.shp') - ## Pre-processing, map SUMMA sims to catchment shapes # Get the aggregated statistics of SUMMA simulations -summa = xr.open_dataset(viz_dir/vis_fil) +summa = xr.open_dataset(viz_dir/viz_fil) # Match the accummulated values to the correct HRU IDs in the shapefile hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile @@ -189,6 +190,7 @@ def make_default_path(suffix): # add maps var = 'scalarSWE' +norm = matplotlib.colors.LogNorm(vmin=bas_albers[var].min(), vmax=bas_albers[var].max()) bas_albers.plot(ax=axs[0,0], column=var, edgecolor='none', legend=True,\ cmap='Greys_r', cax=cax1, norm=norm, zorder=0) axs[0,0].set_title('(a) Snow Water Equivalent Absolute '+stat+ ' Diffs') From 6217aabad8c336af64cf15e09fe067e0e8533c48 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 5 Jan 2023 12:08:35 +0900 Subject: [PATCH 0454/1472] missing file --- utils/plot_control_NorthAmerica.txt | 231 ++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 utils/plot_control_NorthAmerica.txt diff --git a/utils/plot_control_NorthAmerica.txt b/utils/plot_control_NorthAmerica.txt new file mode 100644 index 000000000..7d8708711 --- /dev/null +++ b/utils/plot_control_NorthAmerica.txt @@ -0,0 +1,231 @@ +# SUMMA workflow setting file. +# a lot of this is unneeded for Sundials paper plots so not updating values +# Characters '|' and '#' are used as separators to find the actual setting values. Any text behind '|' is assumed to be part of the setting value, unless preceded by '#'. + +# Note on path specification +# If deviating from default paths, a full path must be specified. E.g. '/home/user/non-default/path' + + +# Modeling domain settings +root_path | /home/avanb/projects/rpp-kshook/wknoben/CWARHM_data # Root folder where data will be stored. +domain_name | NorthAmerica # Used as part of the root folder name for the prepared data. + + +# Shapefile settings - SUMMA catchment file +catchment_shp_path | default # If 'default', uses 'root_path/domain_[name]/shapefiles/catchment'. +catchment_shp_name | merit_hydro_basins_and_coastal_hillslopes_merged_NA.shp # Name of the catchment shapefile. Requires extension '.shp'. +catchment_shp_gruid | COMID # Name of the GRU ID column (can be any numeric value, HRU's within a single GRU have the same GRU ID). +catchment_shp_hruid | COMID # Name of the HRU ID column (consecutive from 1 to total number of HRUs, must be unique). +catchment_shp_area | HRU_area # Name of the catchment area column. Area must be in units [m^2] +catchment_shp_lat | center_lat # Name of the latitude column. Should be a value representative for the HRU. Typically the centroid. +catchment_shp_lon | center_lon # Name of the longitude column. Should be a value representative for the HRU. Typically the centroid. + + +# Shapefile settings - mizuRoute river network file +river_network_shp_path | default # If 'default', uses 'root_path/domain_[name]/shapefiles/river_network'. +river_network_shp_name | merit_river_na_full.shp # Name of the river network shapefile. Requires extension '.shp'. +river_network_shp_segid | COMID # Name of the segment ID column. +river_network_shp_downsegid | NextDownID # Name of the downstream segment ID column. +river_network_shp_slope | slope # Name of the slope column. Slope must be in in units [length/length]. +river_network_shp_length | length # Name of the segment length column. Length must be in units [m]. + + +# Shapefile settings - mizuRoute catchment file +river_basin_shp_path | /home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/catchment/ # Same file as the SUMMA catchments +river_basin_shp_name | merit_hydro_basins_and_coastal_hillslopes_merged_NA.shp # Name of the routing subbasins shapefile needed for remapping. Requires extension '.shp'. +river_basin_shp_rm_hruid | COMID # Name of the routing basin ID column. +river_basin_shp_area | HRU_area # Name of the catchment area column. Area must be in units [m^2] +river_basin_shp_hru_to_seg | COMID # Name of the column that shows which river segment each HRU connects to. HRUs and river segments have the same COMID so this works. + + +# Shapefile settings - SUMMA-to-mizuRoute +river_basin_needs_remap | no # 'no' if routing basins map 1:1 onto model GRUs. 'yes' if river segments span multiple GRUs or if multiple segments are inside a single GRU. + + +# Install settings +github_summa | https://github.com/ncar/summa # Replace this with the path to your own fork if you forked the repo. +github_mizuroute | https://github.com/ncar/mizuroute # Replace this with the path to your own fork if you forked the repo. +install_path_summa | default # If 'default', clones source code into 'root_path/installs/summa'. +install_path_mizuroute | default # If 'default', clones source code into 'root_path/installs/mizuRoute'. +exe_name_summa | summa.exe # Name of the compiled executable. +exe_name_mizuroute | mizuroute.exe # Name of the compiled executable. + + +# Forcing settings +forcing_raw_time | 1979,1984 # Years to download: Jan-[from],Dec-[to]. +forcing_raw_space | 85/-179.5/5/-50 # Bounding box of the shapefile: lat_max/lon_min/lat_min/lon_max. Will be converted to ERA5 download coordinates in script. Order and use of '/' to separate values is mandatory. +forcing_time_step_size | 3600 # Size of the forcing time step in [s]. Must be constant. +forcing_measurement_height | 3 # Reference height for forcing measurements [m]. +forcing_shape_path | default # If 'default', uses 'root_path/domain_[name]/shapefiles/forcing'. +forcing_shape_name | era5_grid.shp # Name of the forcing shapefile. Requires extension '.shp'. +forcing_shape_lat_name | lat # Name of the latitude field that contains the latitude of ERA5 data points. +forcing_shape_lon_name | lon # Name of the longitude field that contains the latitude of ERA5 data points. +forcing_geo_path | default # If 'default', uses 'root_path/domain_[name]/forcing/0_geopotential'. +forcing_raw_path | default # If 'default', uses 'root_path/domain_[name]/forcing/1_raw_data'. +forcing_merged_path | default # If 'default', uses 'root_path/domain_[name]/forcing/2_merged_data'. +forcing_easymore_path | default # If 'default', uses 'root_path/domain_[name]/forcing/3_temp_easymore'. +forcing_basin_avg_path | default # If 'default', uses 'root_path/domain_[name]/forcing/3_basin_averaged_data'. +forcing_summa_path | default # If 'default', uses 'root_path/domain_[name]/forcing/4_SUMMA_input'. + + +# Parameter settings - DEM +parameter_dem_main_url | http://hydro.iis.u-tokyo.ac.jp/~yamadai/MERIT_Hydro/distribute/v1.0.1/ # Primary download URL for MERIT Hydro adjusted elevation data. Needs to be appended with filenames. +parameter_dem_file_template | elv_{}{}.tar # Template for download file names. +parameter_dem_raw_path | default # If 'default', uses 'root_path/domain_[name]/parameters/dem/1_MERIT_hydro_raw_data'. +parameter_dem_unpack_path | default # If 'default', uses 'root_path/domain_[name]/parameters/dem/2_MERIT_hydro_unpacked_data'. +parameter_dem_vrt1_path | default # If 'default', uses 'root_path/domain_[name]/parameters/dem/3_vrt'. +parameter_dem_vrt2_path | default # If 'default', uses 'root_path/domain_[name]/parameters/dem/4_domain_vrt'. +parameter_dem_tif_path | default # If 'default', uses 'root_path/domain_[name]/parameters/dem/5_elevation'. +parameter_dem_tif_name | elevation.tif # Name of the final DEM for the domain. Must be in .tif format. + + +# Parameter settings - soil +parameter_soil_hydro_ID | 1361509511e44adfba814f6950c6e742 # ID of the Hydroshare resource to download. +parameter_soil_raw_path | default # If 'default', uses 'root_path/domain_[name]/parameters/soilclass/1_soil_classes_global'. +parameter_soil_domain_path | default # If 'default', uses 'root_path/domain_[name]/parameters/soilclass/2_soil_classes_domain'. +parameter_soil_tif_name | soil_classes.tif # Name of the final soil class overview for the domain. Must be in .tif format. + + +# Parameter settings - land +parameter_land_list_path | default # If 'default', uses 'summaWorkflow_public/3b_parameters/MODIS_MCD12Q1_V6/1_download/'. Location of file with data download links. +parameter_land_list_name | daac_mcd12q1_data_links.txt # Name of file that contains list of MODIS download urls. +parameter_land_raw_path | /home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/parameters/landclass/1_MODIS_raw_data # If 'default', uses 'root_path/domain_[name]/parameters/landclass/1_MODIS_raw_data'. +parameter_land_vrt1_path | default # If 'default', uses 'root_path/domain_[name]/parameters/landclass/2_vrt_native_crs'. Virtual dataset composed of .hdf files. +parameter_land_vrt2_path | default # If 'default', uses 'root_path/domain_[name]/parameters/landclass/3_vrt_epsg_4326'. Virtual dataset projected in EPSG:4326. +parameter_land_vrt3_path | default # If 'default', uses 'root_path/domain_[name]/parameters/landclass/4_domain_vrt_epsg_4326'. Virtual dataset cropped to model domain. +parameter_land_vrt4_path | default # If 'default', uses 'root_path/domain_[name]/parameters/landclass/5_multiband_domain_vrt_epsg_4326'. Multiband cropped virtual dataset. +parameter_land_tif_path | default # If 'default', uses 'root_path/domain_[name]/parameters/landclass/6_tif_multiband'. +parameter_land_mode_path | default # If 'default', uses 'root_path/domain_[name]/parameters/landclass/7_mode_land_class'. +parameter_land_tif_name | land_classes.tif # Name of the final landclass overview for the domain. Must be in .tif format. + + +# Intersection settings +intersect_dem_path | default # If 'default', uses 'root_path/domain_[name]/shapefiles/catchment_intersection/with_dem'. +intersect_dem_name | catchment_with_merit_dem.shp # Name of the shapefile with intersection between catchment and MERIT Hydro DEM, stored in column 'elev_mean'. +intersect_soil_path | default # If 'default', uses 'root_path/domain_[name]/shapefiles/catchment_intersection/with_soilgrids'. +intersect_soil_name | catchment_with_soilgrids.shp # Name of the shapefile with intersection between catchment and SOILGRIDS-derived USDA soil classes, stored in columns 'USDA_{1,...n}' +intersect_land_path | default # If 'default', uses 'root_path/domain_[name]/shapefiles/catchment_intersection/with_modis'. +intersect_land_name | catchment_with_modis.shp # Name of the shapefile with intersection between catchment and MODIS-derived IGBP land classes, stored in columns 'IGBP_{1,...n}' +intersect_forcing_path | default # If 'default', uses 'root_path/domain_[name]/shapefiles/catchment_intersection/with_forcing'. +intersect_routing_path | default # If 'default', uses 'root_path/domain_[name]/shapefiles/catchment_intersection/with_routing'. +intersect_routing_name | catchment_with_routing_basins.shp # Name of the shapefile with intersection between hydrologic model catchments and routing model catchments. + + +# Experiment settings - general +experiment_id | run1 # Descriptor of the modelling experiment; used as output folder name. +experiment_time_start | default # Simulation start. If 'default', constructs this from 'forcing_raw_time' setting and uses all downloaded forcing data; e.g. '1979-01-01 00:00'. +experiment_time_end | default # Simulation end. If 'default', constructs this from 'forcing_raw_time' setting and uses all downloaded forcing data; e.g. '1979-12-31 23:00'. +experiment_output_summa | default # If 'default', uses 'root_path/domain_[name]/simulations/[experiment_id]/SUMMA'. +experiment_output_mizuRoute | default # If 'default', uses 'root_path/domain_[name]/simulations/[experiment_id]/mizuRoute'. +experiment_log_summa | default # If 'default', uses 'root_path/domain_[name]/simulations/[experiment_id]/SUMMA/SUMMA_logs'. +experiment_log_mizuroute | default # If 'default', uses 'root_path/domain_[name]/simulations/[experiment_id]/mizuRoute/mizuRoute_logs'. +experiment_backup_settings | yes # Flag to (not) create a copy of the model settings in the output folder; "no" or "yes". Copying settings may be undesirable if files are large. + + +# Experiment settings - SUMMA +settings_summa_path | default # If 'default', uses 'root_path/domain_[name]/settings/SUMMA'. +settings_summa_filemanager | fileManager.txt # Name of the file with the SUMMA inputs. +settings_summa_coldstate | coldState.nc # Name of the file with intial states. +settings_summa_trialParams | trialParams.nc # Name of the file that can contain trial parameter values (note, can be empty of any actual parameter values but must be provided and must contain an 'hruId' variable). +settings_summa_forcing_list | forcingFileList.txt # Name of the file that has the list of forcing files. +settings_summa_attributes | attributes.nc # Name of the attributes file. +settings_summa_connect_HRUs | no # Attribute setting: "no" or "yes". Tricky concept, see README in ./5_model_input/SUMMA/3f_attributes. If no; all HRUs modeled as independent columns (downHRUindex = 0). If yes; HRUs within each GRU are connected based on relative HRU elevation (highest = upstream, lowest = outlet). +settings_summa_trialParam_n | 1 # Number of trial parameter specifications. Specify 0 if none are wanted (they can still be included in this file but won't be read). +settings_summa_trialParam_1 | maxstep,900 # Name of trial parameter and value to assign. Value assumed to be float. + + +# Experiment settings - mizuRoute +settings_mizu_path | default # If 'default', uses 'root_path/domain_[name]/settings/mizuRoute'. +settings_mizu_parameters | param.nml.default # Name of the routing parameters file. +settings_mizu_topology | topology.nc # Name of the river network topology file. +settings_mizu_remap | routing_remap.nc # Name of the optional catchment remapping file, for cases when SUMMA uses different catchments than mizuRoute. +settings_mizu_control_file | mizuroute.control # Name of the control file. +settings_mizu_routing_var | averageRoutedRunoff # Name of SUMMA output variable to use for routing. +settings_mizu_routing_units | m/s # Units of the variable to be routed. +settings_mizu_routing_dt | 3600 # Size of the routing time step [s]. +settings_mizu_output_freq | annual # Frequency with which mizuRoute generates new output files. Must be one of 'single', 'day', 'month', 'annual'. +settings_mizu_output_vars | 0 # Routing output. '0' for both KWT and IRF; '1' IRF only; '2' KWT only. +settings_mizu_within_basin | 0 # '0' (no) or '1' (IRF routing). Flag to enable within-basin routing by mizuRoute. Should be set to 0 if SUMMA is run with "subRouting" decision "timeDlay". +settings_mizu_make_outlet | n/a # Segment ID or IDs that should be set as network outlet. Specify multiple IDs separated by commas: X,Y,Z. Specify no IDs as: n/a. Note that this can also be done in the network shapefile. + + +# Postprocessing settings +visualization_folder | default # If 'default', uses 'root_path/domain_[name]/visualization'. + + +# Default folder structure +# Example of the resulting folder structure in "root_path". +# New domains will go into their own folder. + +- summWorkflow_data + | + |_ domain_BowAtBanff + | | + | |_ forcing + | | |_ 0_geopotential + | | |_ 1_raw_data + | | |_ 2_merged_data + | | |_ 3_basin_averaged_data + | | |_ 4_SUMMA_input + | | + | |_ parameters + | | |_ soilclass + | | | |_ 1_soil_classes_global + | | | |_ 2_soil_classes_domain + | | | + | | |_ landclass + | | | |_ 1_MODIS_raw_data + | | | |_ 2_vrt_native_crs + | | | |_ 3_vrt_epsg_4326 + | | | |_ 4_domain_vrt_epsg_4326 + | | | |_ 5_multiband_domain_vrt_epsg_4326 + | | | |_ 6_tif_multiband + | | | |_ 7_mode_land_class + | | | + | | |_ dem + | | |_ 1_MERIT_hydro_raw_data + | | |_ 2_MERIT_hydro_unpacked_data + | | |_ 3_vrt + | | |_ 4_domain_vrt + | | |_ 5_elevation + | | + | |_ settings + | | |_ mizuRoute + | | |_ SUMMA + | | + | |_ shapefiles + | | |_ catchment + | | |_ catchment_intersection + | | | |_ with_dem + | | | |_ with_forcing + | | | |_ with_soil + | | | |_ with_veg + | | |_ forcing + | | |_ river_basins + | | |_ river_network + | | + | |_ simulations + | | |_run1 + | | | |_ 0_settings_backup + | | | | |_ summa + | | | | |_ mizuRoute + | | | |_ summa + | | | | |_run_settings + | | | | |_SUMMA_logs + | | | |_ mizuRoute + | | | | |_run_settings + | | | | |_mizuRoute_logs + | | |_run2 + | | |_ ... + | | + | |_ visualization + | + |_ domain_global + | |_ ... + | + |_ domain_northAmerica + | |_ ... + | + |_ installs + |_ mizuRoute + |_ SUMMA \ No newline at end of file From 0804b202c79987e1464f7ed1207a9871342e6039 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 5 Jan 2023 14:00:11 +0900 Subject: [PATCH 0455/1472] plotting code now paralllel --- utils/concat_groups_split_summa.py | 209 ++++++++++++++++------------- utils/plot_per_GRU.py | 7 + utils/timeseries_to_statistics.py | 17 ++- 3 files changed, 135 insertions(+), 98 deletions(-) diff --git a/utils/concat_groups_split_summa.py b/utils/concat_groups_split_summa.py index fb3129ed7..743060421 100644 --- a/utils/concat_groups_split_summa.py +++ b/utils/concat_groups_split_summa.py @@ -1,18 +1,26 @@ # concatenate the outputs of a split domain summa run into fewer groups # written originally by Manab Saharia, updated by Hongli Liu and Andy Wood, and W. Knoben # modified by A. Van Beusekom (2023) -# Usage: python concat_split_summa.py +# Best to comment out parallel processing lines and run that way on Graham or for full dataset + +# Run: +# module load python/3.10 +# module load scipy-stack +# source nump_xarray/bin/activate +# python concat_groups_split_summa.py + import warnings warnings.simplefilter(action='ignore', category=FutureWarning) from glob import glob import netCDF4 as nc import numpy as np +#import multiprocessing as mp method_name = 'sundials_1en8' catby_num = 2 #number of files to cat into one top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' -#top_fold = '/Users/amedin/Research/USask/test_py/' +#top_fold = '/Users/amedin/Research/USask/test_py/' #testing ncdir = top_fold + 'summa-' + method_name file_pattern = 'run1_G*_timestep.nc' @@ -22,96 +30,109 @@ outfilelist0 = glob((ncdir+'/'+file_pattern)) outfilelist0.sort() +# -- functions +def get_stat(g,catby_num,outfilelist0,ctdir): + outfilelist = outfilelist0[(catby_num*g):(catby_num*(g+1))] + gru_num = 0 + hru_num = 0 + subset0 = outfilelist[0].split('/')[-1].split('_')[1] + subset1 = outfilelist[-1].split('/')[-1].split('_')[1] + out_name = 'run1_'+subset0[0:7]+subset1[7:14]+'_timestep.nc' # will fail if GRU numbers are more than 6 digits + + for file in outfilelist: + f = nc.Dataset(file) + gru_num = gru_num+len(f.dimensions['gru']) + hru_num = hru_num+len(f.dimensions['hru']) + # extract the subset IDs + + # write output + with nc.Dataset(outfilelist[0]) as src: + with nc.Dataset(ctdir+'/'+out_name, "w") as dst: + # copy dimensions + for name, dimension in src.dimensions.items(): + if name == 'gru': + dst.createDimension(name, gru_num) + elif name == 'hru': + dst.createDimension(name, hru_num) + else: + dst.createDimension(name, (len(dimension) if not dimension.isunlimited() else None)) + + # copy variable attributes all at once via dictionary + gru_vars = [] # variable name, gru axis in variable dimension for concatenation. + hru_vars = [] + for name, variable in src.variables.items(): + x = dst.createVariable(name, variable.datatype, variable.dimensions) + dst[name].setncatts(src[name].__dict__) + # Note here the variable dimension name is the same, but size has been updated for gru and hru. + + # Assign different values depending on dimension + dims = variable.dimensions + if 'gru' in dims: + gru_vars.append([name,dims.index('gru')]) + elif 'hru' in dims: + hru_vars.append([name,dims.index('hru')]) + else: + dst[name][:]=src[name][:] + + # read values for gru and hru dimensioned variables + Dict = {} + gru_vars_num = len(gru_vars) + hru_vars_num = len(hru_vars) + for i,file in enumerate(outfilelist): + + print("combining file %d %s" % (i,file)) + # f = nc.Dataset(os.path.join(ncdir, file)) + f = nc.Dataset(file) + for j in range(gru_vars_num): + gru_var_name = gru_vars[j][0] + dim_index = gru_vars[j][1] + data=f[gru_var_name][:] + if i == 0: + Dict[gru_var_name]=data + else: + Dict[gru_var_name]=np.concatenate((Dict[gru_var_name],data),axis=dim_index) + + for j in range(hru_vars_num): + hru_var_name = hru_vars[j][0] + dim_index = hru_vars[j][1] + data=f[hru_var_name][:] + if i == 0: + Dict[hru_var_name]=data + else: + Dict[hru_var_name]=np.concatenate((Dict[hru_var_name],data),axis=dim_index) + + # assign values for gru and hru dimensioned variables + for j in range(gru_vars_num): + dst.variables[gru_vars[j][0]][:] = Dict[gru_vars[j][0]] + for j in range(hru_vars_num): + dst.variables[hru_vars[j][0]][:] = Dict[hru_vars[j][0]] + + # Temporarily create gruId from hruId + #if gru_num == hru_num: + # gruId = dst.createVariable('gruId', dst['hruId'].datatype, ('gru',)) + # gruId.long_name = "ID of group of response unit (GRU)" + # gruId.units = dst['hruId'].units + # dst.variables['gruId'][:] = dst.variables['hruId'][:] + #else: + # print('Warning: gruId variable cannot be created since it has different size from hruId') + + print("wrote output: %s" % (ctdir+'/'+out_name)) + + return #nothing +# -- end functions + # make new outlist of catby_num -for i in range(0,int(len(outfilelist0)/catby_num)): - outfilelist = outfilelist0[catby_num*i:catby_num*(i+1)] - print((i,len(outfilelist0)/catby_num,outfilelist)) - # count the number of gru and hru - gru_num = 0 - hru_num = 0 - subset0 = outfilelist[0].split('/')[-1].split('_')[1] - subset1 = outfilelist[-1].split('/')[-1].split('_')[1] - out_name = 'run1_'+subset0[0:7]+subset1[7:14]+'_timestep.nc' # will fail if GRU numbers are more than 6 digits - - for file in outfilelist: - # f = nc.Dataset(os.path.join(ncdir, file)) - f = nc.Dataset(file) - gru_num = gru_num+len(f.dimensions['gru']) - hru_num = hru_num+len(f.dimensions['hru']) - # extract the subset IDs - - # write output - with nc.Dataset(outfilelist[0]) as src: - with nc.Dataset(ctdir+'/'+out_name, "w") as dst: - - # copy dimensions - for name, dimension in src.dimensions.items(): - if name == 'gru': - dst.createDimension(name, gru_num) - elif name == 'hru': - dst.createDimension(name, hru_num) - else: - dst.createDimension(name, (len(dimension) if not dimension.isunlimited() else None)) - - # copy variable attributes all at once via dictionary - gru_vars = [] # variable name, gru axis in variable dimension for concatenation. - hru_vars = [] - for name, variable in src.variables.items(): - x = dst.createVariable(name, variable.datatype, variable.dimensions) - dst[name].setncatts(src[name].__dict__) - # Note here the variable dimension name is the same, but size has been updated for gru and hru. - - # Assign different values depending on dimension - dims = variable.dimensions - if 'gru' in dims: - gru_vars.append([name,dims.index('gru')]) - elif 'hru' in dims: - hru_vars.append([name,dims.index('hru')]) - else: - dst[name][:]=src[name][:] - - # read values for gru and hru dimensioned variables - Dict = {} - gru_vars_num = len(gru_vars) - hru_vars_num = len(hru_vars) - for i,file in enumerate(outfilelist): - - print("combining file %d %s" % (i,file)) - # f = nc.Dataset(os.path.join(ncdir, file)) - f = nc.Dataset(file) - for j in range(gru_vars_num): - gru_var_name = gru_vars[j][0] - dim_index = gru_vars[j][1] - data=f[gru_var_name][:] - if i == 0: - Dict[gru_var_name]=data - else: - Dict[gru_var_name]=np.concatenate((Dict[gru_var_name],data),axis=dim_index) - - for j in range(hru_vars_num): - hru_var_name = hru_vars[j][0] - dim_index = hru_vars[j][1] - data=f[hru_var_name][:] - if i == 0: - Dict[hru_var_name]=data - else: - Dict[hru_var_name]=np.concatenate((Dict[hru_var_name],data),axis=dim_index) - - # assign values for gru and hru dimensioned variables - for j in range(gru_vars_num): - dst.variables[gru_vars[j][0]][:] = Dict[gru_vars[j][0]] - for j in range(hru_vars_num): - dst.variables[hru_vars[j][0]][:] = Dict[hru_vars[j][0]] - - # Temporarily create gruId from hruId - #if gru_num == hru_num: - # gruId = dst.createVariable('gruId', dst['hruId'].datatype, ('gru',)) - # gruId.long_name = "ID of group of response unit (GRU)" - # gruId.units = dst['hruId'].units - # dst.variables['gruId'][:] = dst.variables['hruId'][:] - #else: - # print('Warning: gruId variable cannot be created since it has different size from hruId') - - print("wrote output: %s" % (ctdir+'/'+out_name)) - -print('Done') +for g in range(0,int(len(outfilelist0)/catby_num)): + get_stat(g,catby_num,outfilelist0,ctdir) + + +# -- start parallel processing +#ncpus = int(os.environ.get('SLURM_CPUS_PER_TASK',default=1)) +#if __name__ == "__main__": +# pool = mp.Pool(processes=ncpus) +# results = [pool.apply_async(get_stat, args=(g,ctdir,catby_num,outfilelist0,ctdir)) for g in range(0,int(len(outfilelist0)/catby_num)] +# dojob = [p.get() for p in results] +# pool.close() +# -- end parallel processing + + diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 4105ffab6..808497712 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -11,6 +11,13 @@ # Dealing with HydroLAKES inputs is not considered within scope of the workflow and therefore requires some manual downloading and preprocessing of this data for those who wish to reproduce this step. # The relevant code is easily disabled by switching the plot_lakes = True flag to False. +# Run: +# module load python/3.10 +# module load scipy-stack +# source nump_xarray/bin/activate +# python plot_per_GRU.py + + # modules #import pyproj import matplotlib diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 2408660a4..300a0e8d2 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -5,6 +5,14 @@ # Currently implemented are finding the maximum and mean value across the entire time series. # Outputs are stored in a single file that covers the full spatial extent of the domain. # written originally by W. Knoben, modified by A. Van Beusekom (2023) +# Best to comment out parallel processing lines and run that way on Graham or for full dataset + +# Run: +# module load python/3.10 +# module load scipy-stack +# source nump_xarray/bin/activate +# python timeseries_to_statistics.py + import os import glob @@ -17,7 +25,7 @@ method_name = 'sundials_1en6' bench_name = 'sundials_1en8_cat' top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' -#top_fold = '/Users/amedin/Research/USask/test_py/' +#top_fold = '/Users/amedin/Research/USask/test_py/' #testing src_dir = top_fold + 'summa-' + method_name ben_dir = top_fold + 'summa-' + bench_name @@ -78,7 +86,7 @@ def run_loop(file,bench): print("wrote output: %s" % (top_fold + 'statistics/' +subset)) - return + return #nothing def merge_subsets_into_one(src,pattern,des,name): @@ -96,14 +104,15 @@ def merge_subsets_into_one(src,pattern,des,name): return #nothing # -- end functions -for i, (file, bench) in enumerate(zip(src_files,ben_files)): +for (file, bench) in zip(src_files,ben_files): run_loop(file, bench) # -- start parallel processing #ncpus = int(os.environ.get('SLURM_CPUS_PER_TASK',default=1)) #if __name__ == "__main__": # pool = mp.Pool(processes=ncpus) -# pool.map(run_loop,src_files) +# results = [pool.apply_async(run_loop, args=(file, bench)) for (file, bench) in zip(src_files,ben_files)] +# dojob = [p.get() for p in results] # pool.close() # -- end parallel processing From f2b759207aee039039a11ca0c9b62c132978e009 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 5 Jan 2023 14:33:17 +0900 Subject: [PATCH 0456/1472] testing flags for plotting --- utils/concat_groups_split_summa.py | 43 ++++++++++++++++-------------- utils/timeseries_to_statistics.py | 37 ++++++++++++++----------- 2 files changed, 45 insertions(+), 35 deletions(-) diff --git a/utils/concat_groups_split_summa.py b/utils/concat_groups_split_summa.py index 743060421..c95fb8282 100644 --- a/utils/concat_groups_split_summa.py +++ b/utils/concat_groups_split_summa.py @@ -9,18 +9,20 @@ # source nump_xarray/bin/activate # python concat_groups_split_summa.py - -import warnings -warnings.simplefilter(action='ignore', category=FutureWarning) +import os from glob import glob import netCDF4 as nc import numpy as np -#import multiprocessing as mp method_name = 'sundials_1en8' -catby_num = 2 #number of files to cat into one -top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' -#top_fold = '/Users/amedin/Research/USask/test_py/' #testing +catby_num = 2 #number of files to cat into one, if had to divide runs from regular batches into sub-batches to finish in 7 days +top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' + +testing = False +if testing: + top_fold = '/Users/amedin/Research/USask/test_py/' +else: + import multiprocessing as mp ncdir = top_fold + 'summa-' + method_name file_pattern = 'run1_G*_timestep.nc' @@ -121,18 +123,19 @@ def get_stat(g,catby_num,outfilelist0,ctdir): return #nothing # -- end functions -# make new outlist of catby_num -for g in range(0,int(len(outfilelist0)/catby_num)): - get_stat(g,catby_num,outfilelist0,ctdir) - - -# -- start parallel processing -#ncpus = int(os.environ.get('SLURM_CPUS_PER_TASK',default=1)) -#if __name__ == "__main__": -# pool = mp.Pool(processes=ncpus) -# results = [pool.apply_async(get_stat, args=(g,ctdir,catby_num,outfilelist0,ctdir)) for g in range(0,int(len(outfilelist0)/catby_num)] -# dojob = [p.get() for p in results] -# pool.close() -# -- end parallel processing + +if testing: + # -- no parallel processing + for g in range(0,int(len(outfilelist0)/catby_num)): + get_stat(g,catby_num,outfilelist0,ctdir) +else: + # -- start parallel processing + ncpus = int(os.environ.get('SLURM_CPUS_PER_TASK',default=1)) + if __name__ == "__main__": + pool = mp.Pool(processes=ncpus) + results = [pool.apply_async(get_stat, args=(g,ctdir,catby_num,outfilelist0,ctdir)) for g in range(0,int(len(outfilelist0)/catby_num))] + dojob = [p.get() for p in results] + pool.close() + # -- end parallel processing diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 300a0e8d2..32a448c95 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -19,13 +19,17 @@ import xarray as xr from pathlib import Path import numpy as np -#import multiprocessing as mp # Settings method_name = 'sundials_1en6' -bench_name = 'sundials_1en8_cat' -top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' -#top_fold = '/Users/amedin/Research/USask/test_py/' #testing +bench_name = 'sundials_1en8_cat' +top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' + +testing = False +if testing: + top_fold = '/Users/amedin/Research/USask/test_py/' +else: + import multiprocessing as mp src_dir = top_fold + 'summa-' + method_name ben_dir = top_fold + 'summa-' + bench_name @@ -104,17 +108,20 @@ def merge_subsets_into_one(src,pattern,des,name): return #nothing # -- end functions -for (file, bench) in zip(src_files,ben_files): - run_loop(file, bench) - -# -- start parallel processing -#ncpus = int(os.environ.get('SLURM_CPUS_PER_TASK',default=1)) -#if __name__ == "__main__": -# pool = mp.Pool(processes=ncpus) -# results = [pool.apply_async(run_loop, args=(file, bench)) for (file, bench) in zip(src_files,ben_files)] -# dojob = [p.get() for p in results] -# pool.close() -# -- end parallel processing + +if testing: + # -- no parallel processing + for (file, bench) in zip(src_files,ben_files): + run_loop(file, bench) +else: + # -- start parallel processing + ncpus = int(os.environ.get('SLURM_CPUS_PER_TASK',default=1)) + if __name__ == "__main__": + pool = mp.Pool(processes=ncpus) + results = [pool.apply_async(run_loop, args=(file, bench)) for (file, bench) in zip(src_files,ben_files)] + dojob = [p.get() for p in results] + pool.close() + # -- end parallel processing # merge the individual files into one for further vizualization From 0713af69e6cf529bfad2841fc3ada2670399242d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 6 Jan 2023 09:08:01 +0900 Subject: [PATCH 0457/1472] This line is incorrect, as it was you could not make a timestep less than 3600 seconds --- build/source/engine/coupled_em.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 287b71df6..a80716df1 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -906,7 +906,7 @@ subroutine coupled_em(& endif ! adjust length of the sub-step (make sure that we don't exceed the step) - dt_sub = data_step - dt_solv !min(data_step - dt_solv, dt_sub) + dt_sub = min(data_step - dt_solv, dt_sub) end do substeps ! (sub-step loop) From 965b15f2222f9d11d5ddb3b2ab91cfd1d4bed6cb Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 6 Jan 2023 11:39:23 +0900 Subject: [PATCH 0458/1472] plotting code --- utils/concat_groups_split_summa.py | 16 +++++++++++++++- utils/plot_per_GRU.py | 14 +++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/utils/concat_groups_split_summa.py b/utils/concat_groups_split_summa.py index c95fb8282..5cc0ef7cf 100644 --- a/utils/concat_groups_split_summa.py +++ b/utils/concat_groups_split_summa.py @@ -18,6 +18,10 @@ catby_num = 2 #number of files to cat into one, if had to divide runs from regular batches into sub-batches to finish in 7 days top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' +missing = False # if appending nan hrus to batch because failed +missgru = 72055933 # batch 205 summa-be32 value +misshru = missgru # could be different + testing = False if testing: top_fold = '/Users/amedin/Research/USask/test_py/' @@ -109,6 +113,16 @@ def get_stat(g,catby_num,outfilelist0,ctdir): for j in range(hru_vars_num): dst.variables[hru_vars[j][0]][:] = Dict[hru_vars[j][0]] + #if missing HRUs, this is slow + if missing: + new_index = np.append(dst["gru"].values,missgru) + dst.reindex({"gru": new_index}) + dst.sel(gru=missgru)["gruId"] = missgru + + new_index = np.append(dst["hru"].values,misshru) + dst.reindex({"hru": new_index}) + dst.sel(gru=misshru)["hruId"] = misshru + # Temporarily create gruId from hruId #if gru_num == hru_num: # gruId = dst.createVariable('gruId', dst['hruId'].datatype, ('gru',)) @@ -133,7 +147,7 @@ def get_stat(g,catby_num,outfilelist0,ctdir): ncpus = int(os.environ.get('SLURM_CPUS_PER_TASK',default=1)) if __name__ == "__main__": pool = mp.Pool(processes=ncpus) - results = [pool.apply_async(get_stat, args=(g,ctdir,catby_num,outfilelist0,ctdir)) for g in range(0,int(len(outfilelist0)/catby_num))] + results = [pool.apply_async(get_stat, args=(g,catby_num,outfilelist0,ctdir)) for g in range(0,int(len(outfilelist0)/catby_num))] dojob = [p.get() for p in results] pool.close() # -- end parallel processing diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 808497712..6d218d54c 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -19,14 +19,22 @@ # modules -#import pyproj +import conda +import os + +conda_file_dir = conda.__file__ +conda_dir = conda_file_dir.split('lib')[0] +proj_lib = os.path.join(os.path.join(conda_dir, 'share'), 'proj') +os.environ["PROJ_LIB"] = proj_lib + +import pyproj import matplotlib -#import numpy as np +import numpy as np import xarray as xr import geopandas as gpd from pathlib import Path import matplotlib.pyplot as plt -#import matplotlib.gridspec as gridspec +import matplotlib.gridspec as gridspec # Simulation statistics file locations method_name = 'sundials_1en6' From ec203522944e87de34b6d387c5e4130aec249f7a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 6 Jan 2023 13:28:21 +0900 Subject: [PATCH 0459/1472] upgrading commands for environments in plotting --- utils/check_bit_4_bit_withTol.py | 12 ++++++++---- utils/concat_groups_split_summa.py | 13 +++++++++---- utils/plot_per_GRU.py | 20 ++++++++------------ utils/timeseries_to_statistics.py | 12 ++++++++---- 4 files changed, 33 insertions(+), 24 deletions(-) diff --git a/utils/check_bit_4_bit_withTol.py b/utils/check_bit_4_bit_withTol.py index f34a18e32..c71a75901 100644 --- a/utils/check_bit_4_bit_withTol.py +++ b/utils/check_bit_4_bit_withTol.py @@ -1,9 +1,13 @@ #!/usr/bin/env python -#module load python/3.10 -#module load scipy-stack -#source nump_xarray/bin/activate - +# module load gcc/9.3.0 +# module load scipy-stack +# module load gdal/3.2.3 +# virtualenv --no-download $SLURM_TMPDIR/env +# source $SLURM_TMPDIR/env/bin/activate +# pip install --no-index --upgrade pip +# pip install --no-index -r requirements.txt +# python check_bit_4_bit_withTol.py import sys import numpy as np diff --git a/utils/concat_groups_split_summa.py b/utils/concat_groups_split_summa.py index 5cc0ef7cf..402fceb78 100644 --- a/utils/concat_groups_split_summa.py +++ b/utils/concat_groups_split_summa.py @@ -4,10 +4,15 @@ # Best to comment out parallel processing lines and run that way on Graham or for full dataset # Run: -# module load python/3.10 -# module load scipy-stack -# source nump_xarray/bin/activate -# python concat_groups_split_summa.py +# module load gcc/9.3.0 +# module load scipy-stack +# module load gdal/3.2.3 +# virtualenv --no-download $SLURM_TMPDIR/env +# source $SLURM_TMPDIR/env/bin/activate +# pip install --no-index --upgrade pip +# pip install --no-index -r requirements.txt +# python concat_groups_split_summa.py + import os from glob import glob diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 6d218d54c..8e936a9d4 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -12,21 +12,17 @@ # The relevant code is easily disabled by switching the plot_lakes = True flag to False. # Run: -# module load python/3.10 -# module load scipy-stack -# source nump_xarray/bin/activate -# python plot_per_GRU.py +# module load gcc/9.3.0 +# module load scipy-stack +# module load gdal/3.2.3 +# virtualenv --no-download $SLURM_TMPDIR/env +# source $SLURM_TMPDIR/env/bin/activate +# pip install --no-index --upgrade pip +# pip install --no-index -r requirements.txt +# python plot_per_GRU.py # modules -import conda -import os - -conda_file_dir = conda.__file__ -conda_dir = conda_file_dir.split('lib')[0] -proj_lib = os.path.join(os.path.join(conda_dir, 'share'), 'proj') -os.environ["PROJ_LIB"] = proj_lib - import pyproj import matplotlib import numpy as np diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 32a448c95..6d88621fa 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -8,10 +8,14 @@ # Best to comment out parallel processing lines and run that way on Graham or for full dataset # Run: -# module load python/3.10 -# module load scipy-stack -# source nump_xarray/bin/activate -# python timeseries_to_statistics.py +# module load gcc/9.3.0 +# module load scipy-stack +# module load gdal/3.2.3 +# virtualenv --no-download $SLURM_TMPDIR/env +# source $SLURM_TMPDIR/env/bin/activate +# pip install --no-index --upgrade pip +# pip install --no-index -r requirements.txt +# python timeseries_to_statistics.py import os From 823ea27d241a05341c60999ff6da380425c4ad18 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 6 Jan 2023 13:30:15 +0900 Subject: [PATCH 0460/1472] plotting requiremets --- utils/requirements.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 utils/requirements.txt diff --git a/utils/requirements.txt b/utils/requirements.txt new file mode 100644 index 000000000..4f6d62c6f --- /dev/null +++ b/utils/requirements.txt @@ -0,0 +1,13 @@ +cftime==1.6.2+computecanada +click==8.1.3+computecanada +click-plugins==1.1.1+computecanada +cligj==0.7.2+computecanada +Fiona==1.8.21+computecanada +geopandas==0.11.1+computecanada +h5netcdf==0.7.4+computecanada +h5py==3.7.0+computecanada +munch==2.5.0+computecanada +netCDF4==1.5.8+computecanada +pyproj==3.3.0+computecanada +Shapely==1.8.2+computecanada +xarray==2022.6.0+computecanada \ No newline at end of file From 7b4595c02ad5da5b0ed8e1d677d76d0ea20c89e5 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 9 Jan 2023 10:46:13 +0900 Subject: [PATCH 0461/1472] plotting --- utils/check_bit_4_bit_withTol.py | 7 ------- utils/concat_groups_split_summa.py | 7 ------- utils/plot_per_GRU.py | 10 ++-------- utils/requirements.txt | 13 ------------- utils/timeseries_to_statistics.py | 7 ------- 5 files changed, 2 insertions(+), 42 deletions(-) delete mode 100644 utils/requirements.txt diff --git a/utils/check_bit_4_bit_withTol.py b/utils/check_bit_4_bit_withTol.py index c71a75901..1738ca98c 100644 --- a/utils/check_bit_4_bit_withTol.py +++ b/utils/check_bit_4_bit_withTol.py @@ -1,12 +1,5 @@ #!/usr/bin/env python -# module load gcc/9.3.0 -# module load scipy-stack -# module load gdal/3.2.3 -# virtualenv --no-download $SLURM_TMPDIR/env -# source $SLURM_TMPDIR/env/bin/activate -# pip install --no-index --upgrade pip -# pip install --no-index -r requirements.txt # python check_bit_4_bit_withTol.py import sys diff --git a/utils/concat_groups_split_summa.py b/utils/concat_groups_split_summa.py index 402fceb78..5f1c5945b 100644 --- a/utils/concat_groups_split_summa.py +++ b/utils/concat_groups_split_summa.py @@ -4,13 +4,6 @@ # Best to comment out parallel processing lines and run that way on Graham or for full dataset # Run: -# module load gcc/9.3.0 -# module load scipy-stack -# module load gdal/3.2.3 -# virtualenv --no-download $SLURM_TMPDIR/env -# source $SLURM_TMPDIR/env/bin/activate -# pip install --no-index --upgrade pip -# pip install --no-index -r requirements.txt # python concat_groups_split_summa.py diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 8e936a9d4..8b1c6da0a 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -12,18 +12,12 @@ # The relevant code is easily disabled by switching the plot_lakes = True flag to False. # Run: -# module load gcc/9.3.0 -# module load scipy-stack -# module load gdal/3.2.3 -# virtualenv --no-download $SLURM_TMPDIR/env -# source $SLURM_TMPDIR/env/bin/activate -# pip install --no-index --upgrade pip -# pip install --no-index -r requirements.txt # python plot_per_GRU.py # modules -import pyproj +import pyproj +import fiona import matplotlib import numpy as np import xarray as xr diff --git a/utils/requirements.txt b/utils/requirements.txt deleted file mode 100644 index 4f6d62c6f..000000000 --- a/utils/requirements.txt +++ /dev/null @@ -1,13 +0,0 @@ -cftime==1.6.2+computecanada -click==8.1.3+computecanada -click-plugins==1.1.1+computecanada -cligj==0.7.2+computecanada -Fiona==1.8.21+computecanada -geopandas==0.11.1+computecanada -h5netcdf==0.7.4+computecanada -h5py==3.7.0+computecanada -munch==2.5.0+computecanada -netCDF4==1.5.8+computecanada -pyproj==3.3.0+computecanada -Shapely==1.8.2+computecanada -xarray==2022.6.0+computecanada \ No newline at end of file diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 6d88621fa..621a136c6 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -8,13 +8,6 @@ # Best to comment out parallel processing lines and run that way on Graham or for full dataset # Run: -# module load gcc/9.3.0 -# module load scipy-stack -# module load gdal/3.2.3 -# virtualenv --no-download $SLURM_TMPDIR/env -# source $SLURM_TMPDIR/env/bin/activate -# pip install --no-index --upgrade pip -# pip install --no-index -r requirements.txt # python timeseries_to_statistics.py From 8cac3682cbbb3f594fe15c53a5be4f4825b23f87 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 9 Jan 2023 12:36:23 +0900 Subject: [PATCH 0462/1472] errors in gpd plotting code --- utils/plot_per_GRU.py | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 8b1c6da0a..086f19ac0 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -121,27 +121,27 @@ def make_default_path(suffix): # Set the target CRS acc = 'ESRI:102008' -# catchment shapefile -bas = gpd.read_file(hm_catchment_path/hm_catchment_name) -bas_albers = bas.to_crs(acc) +# catchment shapefile, first 2 lines throw error so cutting them +#bas = gpd.read_file(hm_catchment_path/hm_catchment_name) +#bas_albers = bas.to_crs(acc) bas_albers = gpd.read_file(main/'basin.shp') -# river network shapefile +# river network shapefile, first 2 lines throw error so cutting them if plot_rivers: - riv = gpd.read_file(river_network_path/river_network_name) - riv_albers = riv.to_crs(acc) + #riv = gpd.read_file(river_network_path/river_network_name) + #riv_albers = riv.to_crs(acc) riv_albers = gpd.read_file(main/'river.shp') -# lakes shapefile +# lakes shapefile, first 2 lines throw error so cutting them if plot_lakes: - lakes = gpd.read_file(lake_path/lake_name) - lakes.to_crs(acc) + #lakes = gpd.read_file(lake_path/lake_name) + #lak_albers = lakes.to_crs(acc) lak_albers = gpd.read_file(main/'lakes.shp') # Print the median basin size for curiousity -print('median area = {} m^2'.format(bas['HRU_area'].median() / 10**6)) -print('mean area = {} m^2'.format(bas['HRU_area'].mean() / 10**6)) +#print('median area = {} m^2'.format(bas['HRU_area'].median() / 10**6)) +#print('mean area = {} m^2'.format(bas['HRU_area'].mean() / 10**6)) #median area = 33.06877343600296 m^2 #mean area = 40.19396140285971 m^2 @@ -190,6 +190,8 @@ def make_default_path(suffix): cax2 = fig.add_axes([0.97 ,0.60,0.02,0.3]) cax3 = fig.add_axes([0.473,0.10,0.02,0.3]) cax4 = fig.add_axes([0.97 ,0.10,0.02,0.3]) +cax5 = fig.add_axes([0.473,0.10,0.02,0.3]) +cax6 = fig.add_axes([0.97 ,0.10,0.02,0.3]) plt.tight_layout() @@ -200,7 +202,7 @@ def make_default_path(suffix): cmap='Greys_r', cax=cax1, norm=norm, zorder=0) axs[0,0].set_title('(a) Snow Water Equivalent Absolute '+stat+ ' Diffs') axs[0,0].axis('off') -cax1.set_ylabel('scalarSWE $[kg~m^{-2}]$',labelpad=-600) +cax1.set_ylabel('scalarSWE $[kg~m^{-2}]$',labelpad=-100) # SM var = 'scalarTotalSoilWat' @@ -209,7 +211,7 @@ def make_default_path(suffix): cmap='cividis_r', cax=cax2, norm=norm, zorder=0) axs[0,1].set_title('(b) Total soil water content Absolute '+stat+ ' Diffs') axs[0,1].axis('off') -cax2.set_ylabel('scalarTotalSoilWat $[kg~m^{-2}]$',labelpad=-600) +cax2.set_ylabel('scalarTotalSoilWat $[kg~m^{-2}]$',labelpad=-100) # ET var = 'scalarTotalET' @@ -218,16 +220,16 @@ def make_default_path(suffix): cmap='viridis', cax=cax3, norm=norm, zorder=0) axs[1,0].set_title('(c) Total evapotranspiration Absolute '+stat+ ' Diffs') axs[1,0].axis('off') -cax3.set_ylabel('scalarTotalET $[kg~m^{-2}~s^{-1}]$',labelpad=-600) +cax3.set_ylabel('scalarTotalET $[kg~m^{-2}~s^{-1}]$',labelpad=-100) # CanWat var = 'scalarCanopyWat' norm = matplotlib.colors.LogNorm(vmin=bas_albers[var].min(), vmax=bas_albers[var].max()) bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True,\ - cmap='viridis_r', cax=cax3, norm=norm, zorder=0) + cmap='viridis_r', cax=cax4, norm=norm, zorder=0) axs[1,1].set_title('(d) Total water on the vegetation canopy Absolute '+stat+ ' Diffs') axs[1,1].axis('off') -cax3.set_ylabel('scalarCanopyWat $[kg~m^{-2}]$',labelpad=-600) +cax4.set_ylabel('scalarCanopyWat $[kg~m^{-2}]$',labelpad=-100) # Runoff var = 'averageRoutedRunoff' @@ -236,7 +238,7 @@ def make_default_path(suffix): cmap='Blues', cax=cax3, norm=norm, zorder=0) axs[2,0].set_title('(e) Routed runoff Absolute '+stat+ ' Diffs') axs[2,0].axis('off') -cax3.set_ylabel('averageRoutedRunoff $[m~s^{-1}]$',labelpad=-600) +cax5.set_ylabel('averageRoutedRunoff $[m~s^{-1}]$',labelpad=-100) # Clock time var = 'wallClockTime(' @@ -245,7 +247,7 @@ def make_default_path(suffix): cmap='Greys', cax=cax3, norm=norm, zorder=0) axs[2,1].set_title('(f) Wall clock time Absolute '+stat+ ' Diffs') axs[2,1].axis('off') -cax3.set_ylabel('wallClockTime( $[s]$',labelpad=-600) +cax6.set_ylabel('wallClockTime( $[s]$',labelpad=-100) # lakes @@ -254,6 +256,8 @@ def make_default_path(suffix): large_lakes_albers.plot(ax=axs[0,1], color=lake_col, zorder=1) large_lakes_albers.plot(ax=axs[1,0], color=lake_col, zorder=1) large_lakes_albers.plot(ax=axs[1,1], color=lake_col, zorder=1) + large_lakes_albers.plot(ax=axs[2,0], color=lake_col, zorder=1) + large_lakes_albers.plot(ax=axs[2,1], color=lake_col, zorder=1) # Save plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=True) From 890cfc83b676ff5e5d4b0d6699778c1387cd70c7 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 9 Jan 2023 12:36:50 +0900 Subject: [PATCH 0463/1472] errors in gbd plotting code --- utils/plot_per_GRU.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 086f19ac0..3c12b3ab9 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -186,12 +186,12 @@ def make_default_path(suffix): fig,axs = plt.subplots(3,2,figsize=(140,133)) # colorbar axes -cax1 = fig.add_axes([0.473,0.60,0.02,0.3]) -cax2 = fig.add_axes([0.97 ,0.60,0.02,0.3]) -cax3 = fig.add_axes([0.473,0.10,0.02,0.3]) -cax4 = fig.add_axes([0.97 ,0.10,0.02,0.3]) -cax5 = fig.add_axes([0.473,0.10,0.02,0.3]) -cax6 = fig.add_axes([0.97 ,0.10,0.02,0.3]) +cax1 = fig.add_axes([0.473,0.73,0.02,0.3]) +cax2 = fig.add_axes([0.97 ,0.73,0.02,0.3]) +cax3 = fig.add_axes([0.473,0.40,0.02,0.3]) +cax4 = fig.add_axes([0.97 ,0.40,0.02,0.3]) +cax5 = fig.add_axes([0.473,0.07,0.02,0.3]) +cax6 = fig.add_axes([0.97 ,0.07,0.02,0.3]) plt.tight_layout() From c2b7af0a981dfb0a5f062d0ed335287828a85125 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 9 Jan 2023 16:19:51 +0900 Subject: [PATCH 0464/1472] deal with 0's in lognormal plots, plot max and rmse --- utils/plot_per_GRU.py | 37 +++++++++++++++---------------- utils/timeseries_to_statistics.py | 16 +++++++------ 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 3c12b3ab9..7472efd15 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -25,10 +25,11 @@ from pathlib import Path import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec +import copy # Simulation statistics file locations method_name = 'sundials_1en6' -stat = 'max' +stat = 'max' #max or rmse settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') viz_fil = method_name + '_hrly_diff_stats_{}_{}.nc' @@ -43,7 +44,7 @@ main = Path('/home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/albers_projection') # Plot lakes? -plot_lakes = False +plot_lakes = True # lakes shapefile WHERE IS THIS lake_path = Path('C:/Globus endpoint/HydroLAKES/HydroLAKES_polys_v10_shp') lake_name = 'HydroLAKES_polys_v10_subset_NA.shp' @@ -195,56 +196,54 @@ def make_default_path(suffix): plt.tight_layout() +my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap +my_cmap.set_bad(my_cmap.colors[0]) + # add maps -var = 'scalarSWE' -norm = matplotlib.colors.LogNorm(vmin=bas_albers[var].min(), vmax=bas_albers[var].max()) -bas_albers.plot(ax=axs[0,0], column=var, edgecolor='none', legend=True,\ - cmap='Greys_r', cax=cax1, norm=norm, zorder=0) +var = 'scalarSWE' +bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True,\ + cmap=my_cmap, cax=cax1, norm=matplotlib.colors.LogNorm(), zorder=0) axs[0,0].set_title('(a) Snow Water Equivalent Absolute '+stat+ ' Diffs') axs[0,0].axis('off') cax1.set_ylabel('scalarSWE $[kg~m^{-2}]$',labelpad=-100) # SM var = 'scalarTotalSoilWat' -norm = matplotlib.colors.LogNorm(vmin=bas_albers[var].min(), vmax=bas_albers[var].max()) -bas_albers.plot(ax=axs[0,1], column=var, edgecolor='none', legend=True,\ - cmap='cividis_r', cax=cax2, norm=norm, zorder=0) +bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True,\ + cmap=my_cmap, cax=cax2, norm=matplotlib.colors.LogNorm(), zorder=0) axs[0,1].set_title('(b) Total soil water content Absolute '+stat+ ' Diffs') axs[0,1].axis('off') cax2.set_ylabel('scalarTotalSoilWat $[kg~m^{-2}]$',labelpad=-100) # ET var = 'scalarTotalET' -norm = matplotlib.colors.LogNorm(vmin=bas_albers[var].min(), vmax=bas_albers[var].max()) bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True,\ - cmap='viridis', cax=cax3, norm=norm, zorder=0) + cmap=my_cmap, cax=cax3, norm=matplotlib.colors.LogNorm(), zorder=0) axs[1,0].set_title('(c) Total evapotranspiration Absolute '+stat+ ' Diffs') axs[1,0].axis('off') cax3.set_ylabel('scalarTotalET $[kg~m^{-2}~s^{-1}]$',labelpad=-100) # CanWat var = 'scalarCanopyWat' -norm = matplotlib.colors.LogNorm(vmin=bas_albers[var].min(), vmax=bas_albers[var].max()) bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True,\ - cmap='viridis_r', cax=cax4, norm=norm, zorder=0) + cmap=my_cmap, cax=cax4, norm=matplotlib.colors.LogNorm(), zorder=0) axs[1,1].set_title('(d) Total water on the vegetation canopy Absolute '+stat+ ' Diffs') axs[1,1].axis('off') cax4.set_ylabel('scalarCanopyWat $[kg~m^{-2}]$',labelpad=-100) # Runoff var = 'averageRoutedRunoff' -norm = matplotlib.colors.LogNorm(vmin=bas_albers[var].min(), vmax=bas_albers[var].max()) -bas_albers.plot(ax=axs[2,0], column=var, edgecolor='none', legend=True,\ - cmap='Blues', cax=cax3, norm=norm, zorder=0) +bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True,\ + cmap=my_cmap, cax=cax5, norm=matplotlib.colors.LogNorm(), zorder=0) axs[2,0].set_title('(e) Routed runoff Absolute '+stat+ ' Diffs') axs[2,0].axis('off') cax5.set_ylabel('averageRoutedRunoff $[m~s^{-1}]$',labelpad=-100) # Clock time var = 'wallClockTime(' -norm = matplotlib.colors.LogNorm(vmin=bas_albers[var].min(), vmax=bas_albers[var].max()) -bas_albers.plot(ax=axs[2,1], column=var, edgecolor='none', legend=True,\ - cmap='Greys', cax=cax3, norm=norm, zorder=0) +bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True,\ + cmap=my_cmap, cax=cax6, norm=matplotlib.colors.LogNorm(), zorder=0) + axs[2,1].set_title('(f) Wall clock time Absolute '+stat+ ' Diffs') axs[2,1].axis('off') cax6.set_ylabel('wallClockTime( $[s]$',labelpad=-100) diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 621a136c6..8f2fedb9b 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -33,7 +33,7 @@ src_pat = 'run1_G*_timestep.nc' des_dir = top_fold + 'statistics' des_fil = method_name + '_hrly_diff_stats_{}_{}_{}.nc' -stat = 'max' +stat = 'max' #max or rmse settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} viz_fil = method_name + '_hrly_diff_stats_{}_{}.nc' @@ -66,9 +66,10 @@ def run_loop(file,bench): # open file dat,ben = xr.open_dataset(file), xr.open_dataset(bench) - diff = (np.fabs(dat - ben)) + #diff = (np.fabs(dat - ben)) #1-norm + diff = (np.square(dat - ben)) #2-norm # get rid of gru dimension, assuming they are same as the often are (everything now as hruId) - diff = diff.drop_vars(['hruId','gruId']) + diff = diff.drop_vars(['hruId','gruId']) m = diff.drop_dims('hru') m = m.rename({'gru': 'hru'}) diff = diff.drop_dims('gru') @@ -78,10 +79,11 @@ def run_loop(file,bench): for var,stat in settings.items(): # Select the case - if stat == 'mean': - new = diff[var].mean(dim='time') - elif stat == 'max': - new = diff[var].max(dim='time') + if stat == 'rmse': + #new = diff[var].mean(dim='time') #1-norm + new = ((diff[var].mean(dim='time'))**(1/2)) #RMSE SHOULD THIS BE NORMALIZED? colorbar will normalize + if stat == 'max': + new = diff[var].max(dim='time') #same regardless new.to_netcdf(des_dir / des_fil.format(stat,var,subset)) From dc84a76f17d13a2033b3d8387c03a944615fbd2e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 9 Jan 2023 17:12:55 +0900 Subject: [PATCH 0465/1472] fix variable name and 0 plotting lognorm --- utils/plot_per_GRU.py | 24 ++++++++++++------------ utils/timeseries_to_statistics.py | 8 ++++---- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 7472efd15..10f58b5a1 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -201,48 +201,48 @@ def make_default_path(suffix): # add maps var = 'scalarSWE' -bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True,\ - cmap=my_cmap, cax=cax1, norm=matplotlib.colors.LogNorm(), zorder=0) +bas_albers.plot(ax=axs[0,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ + cax=cax1, norm=matplotlib.colors.LogNorm(),interpolation='nearest',zorder=0) axs[0,0].set_title('(a) Snow Water Equivalent Absolute '+stat+ ' Diffs') axs[0,0].axis('off') cax1.set_ylabel('scalarSWE $[kg~m^{-2}]$',labelpad=-100) # SM var = 'scalarTotalSoilWat' -bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True,\ - cmap=my_cmap, cax=cax2, norm=matplotlib.colors.LogNorm(), zorder=0) +bas_albers.plot(ax=axs[0,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ + cmap=my_cmap, cax=cax2, norm=matplotlib.colors.LogNorm(),interpolation='nearest',zorder=0) axs[0,1].set_title('(b) Total soil water content Absolute '+stat+ ' Diffs') axs[0,1].axis('off') cax2.set_ylabel('scalarTotalSoilWat $[kg~m^{-2}]$',labelpad=-100) # ET var = 'scalarTotalET' -bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True,\ - cmap=my_cmap, cax=cax3, norm=matplotlib.colors.LogNorm(), zorder=0) +bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ + cax=cax3, norm=matplotlib.colors.LogNorm(), interpolation='nearest',zorder=0) axs[1,0].set_title('(c) Total evapotranspiration Absolute '+stat+ ' Diffs') axs[1,0].axis('off') cax3.set_ylabel('scalarTotalET $[kg~m^{-2}~s^{-1}]$',labelpad=-100) # CanWat var = 'scalarCanopyWat' -bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True,\ - cmap=my_cmap, cax=cax4, norm=matplotlib.colors.LogNorm(), zorder=0) +bas_albers.plot(ax=axs[1,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ + cax=cax4, norm=matplotlib.colors.LogNorm(), interpolation='nearest',zorder=0) axs[1,1].set_title('(d) Total water on the vegetation canopy Absolute '+stat+ ' Diffs') axs[1,1].axis('off') cax4.set_ylabel('scalarCanopyWat $[kg~m^{-2}]$',labelpad=-100) # Runoff var = 'averageRoutedRunoff' -bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True,\ - cmap=my_cmap, cax=cax5, norm=matplotlib.colors.LogNorm(), zorder=0) +bas_albers.plot(ax=axs[2,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ + cax=cax5, norm=matplotlib.colors.LogNorm(), interpolation='nearest',zorder=0) axs[2,0].set_title('(e) Routed runoff Absolute '+stat+ ' Diffs') axs[2,0].axis('off') cax5.set_ylabel('averageRoutedRunoff $[m~s^{-1}]$',labelpad=-100) # Clock time var = 'wallClockTime(' -bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True,\ - cmap=my_cmap, cax=cax6, norm=matplotlib.colors.LogNorm(), zorder=0) +bas_albers.plot(ax=axs[2,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ + cax=cax6, norm=matplotlib.colors.LogNorm(), zorder=0, interpolation='nearest',zorder=0) axs[2,1].set_title('(f) Wall clock time Absolute '+stat+ ' Diffs') axs[2,1].axis('off') diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 8f2fedb9b..491b0afa5 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -33,7 +33,7 @@ src_pat = 'run1_G*_timestep.nc' des_dir = top_fold + 'statistics' des_fil = method_name + '_hrly_diff_stats_{}_{}_{}.nc' -stat = 'max' #max or rmse +stat = 'rmse' #max or rmse settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} viz_fil = method_name + '_hrly_diff_stats_{}_{}.nc' @@ -76,13 +76,13 @@ def run_loop(file,bench): diff = xr.merge([diff,m]) # compute the requested statistics - for var,stat in settings.items(): + for var,stat0 in settings.items(): # Select the case - if stat == 'rmse': + if stat0 == 'rmse': #new = diff[var].mean(dim='time') #1-norm new = ((diff[var].mean(dim='time'))**(1/2)) #RMSE SHOULD THIS BE NORMALIZED? colorbar will normalize - if stat == 'max': + if stat0 == 'max': new = diff[var].max(dim='time') #same regardless new.to_netcdf(des_dir / des_fil.format(stat,var,subset)) From 8d106d11de414673463e3eaf3d0458b8218a276f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 9 Jan 2023 18:10:52 +0900 Subject: [PATCH 0466/1472] trying to fix vmin error --- utils/concat_groups_split_summa.py | 2 +- utils/plot_per_GRU.py | 39 +++++++++++++++++++----------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/utils/concat_groups_split_summa.py b/utils/concat_groups_split_summa.py index 5f1c5945b..489b7c40e 100644 --- a/utils/concat_groups_split_summa.py +++ b/utils/concat_groups_split_summa.py @@ -111,7 +111,7 @@ def get_stat(g,catby_num,outfilelist0,ctdir): for j in range(hru_vars_num): dst.variables[hru_vars[j][0]][:] = Dict[hru_vars[j][0]] - #if missing HRUs, this is slow + #if missing HRUs, this is slow or broken if missing: new_index = np.append(dst["gru"].values,missgru) dst.reindex({"gru": new_index}) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 10f58b5a1..7aa4f3aa7 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -199,52 +199,63 @@ def make_default_path(suffix): my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap my_cmap.set_bad(my_cmap.colors[0]) +if stat == 'rmse': + stat_word = 'RMSE' +if stat == 'max': + stat_word = 'Abs Max' + # add maps -var = 'scalarSWE' +var = 'scalarSWE' +minval = (bas_albers[var].where(bas_albers[var]>0)).min() bas_albers.plot(ax=axs[0,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax1, norm=matplotlib.colors.LogNorm(),interpolation='nearest',zorder=0) -axs[0,0].set_title('(a) Snow Water Equivalent Absolute '+stat+ ' Diffs') + cax=cax1, norm=matplotlib.colors.LogNorm(vmin=minval),interpolation='nearest',zorder=0) +axs[0,0].set_title('(a) Snow Water Equivalent '+stat_word+ ' Diffs') axs[0,0].axis('off') cax1.set_ylabel('scalarSWE $[kg~m^{-2}]$',labelpad=-100) # SM var = 'scalarTotalSoilWat' +minval = (bas_albers[var].where(bas_albers[var]>0)).min() bas_albers.plot(ax=axs[0,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cmap=my_cmap, cax=cax2, norm=matplotlib.colors.LogNorm(),interpolation='nearest',zorder=0) -axs[0,1].set_title('(b) Total soil water content Absolute '+stat+ ' Diffs') + cax=cax2, norm=matplotlib.colors.LogNorm(vmin=minval),interpolation='nearest',zorder=0) +axs[0,1].set_title('(b) Total soil water content '+stat_word+ ' Diffs') axs[0,1].axis('off') cax2.set_ylabel('scalarTotalSoilWat $[kg~m^{-2}]$',labelpad=-100) # ET var = 'scalarTotalET' +minval = (bas_albers[var].where(bas_albers[var]>0)).min() bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax3, norm=matplotlib.colors.LogNorm(), interpolation='nearest',zorder=0) -axs[1,0].set_title('(c) Total evapotranspiration Absolute '+stat+ ' Diffs') + cax=cax3, norm=matplotlib.colors.LogNorm(vmin=minval), interpolation='nearest',zorder=0) +axs[1,0].set_title('(c) Total evapotranspiration '+stat_word+ ' Diffs') axs[1,0].axis('off') cax3.set_ylabel('scalarTotalET $[kg~m^{-2}~s^{-1}]$',labelpad=-100) # CanWat var = 'scalarCanopyWat' +minval = (bas_albers[var].where(bas_albers[var]>0)).min() bas_albers.plot(ax=axs[1,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax4, norm=matplotlib.colors.LogNorm(), interpolation='nearest',zorder=0) -axs[1,1].set_title('(d) Total water on the vegetation canopy Absolute '+stat+ ' Diffs') + cax=cax4, norm=matplotlib.colors.LogNorm(vmin=minval), interpolation='nearest',zorder=0) +axs[1,1].set_title('(d) Total water on the vegetation canopy '+stat_word+ ' Diffs') axs[1,1].axis('off') cax4.set_ylabel('scalarCanopyWat $[kg~m^{-2}]$',labelpad=-100) # Runoff var = 'averageRoutedRunoff' +minval = (bas_albers[var].where(bas_albers[var]>0)).min() bas_albers.plot(ax=axs[2,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax5, norm=matplotlib.colors.LogNorm(), interpolation='nearest',zorder=0) -axs[2,0].set_title('(e) Routed runoff Absolute '+stat+ ' Diffs') + cax=cax5, norm=matplotlib.colors.LogNorm(vmin=minval), interpolation='nearest',zorder=0) +axs[2,0].set_title('(e) Routed runoff '+stat_word+ ' Diffs') axs[2,0].axis('off') cax5.set_ylabel('averageRoutedRunoff $[m~s^{-1}]$',labelpad=-100) # Clock time -var = 'wallClockTime(' +var = 'wallClockTime' +minval = (bas_albers[var].where(bas_albers[var]>0)).min() bas_albers.plot(ax=axs[2,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax6, norm=matplotlib.colors.LogNorm(), zorder=0, interpolation='nearest',zorder=0) + cax=cax6, norm=matplotlib.colors.LogNorm(vmin=minval), interpolation='nearest',zorder=0) -axs[2,1].set_title('(f) Wall clock time Absolute '+stat+ ' Diffs') +axs[2,1].set_title('(f) Wall clock time '+stat_word+ ' Diffs') axs[2,1].axis('off') cax6.set_ylabel('wallClockTime( $[s]$',labelpad=-100) From e259b695648122da78a98cd2d24dff1d4751d597 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 9 Jan 2023 20:00:05 +0900 Subject: [PATCH 0467/1472] remove interpolation --- utils/plot_per_GRU.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 7aa4f3aa7..c19ad926c 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -208,7 +208,7 @@ def make_default_path(suffix): var = 'scalarSWE' minval = (bas_albers[var].where(bas_albers[var]>0)).min() bas_albers.plot(ax=axs[0,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax1, norm=matplotlib.colors.LogNorm(vmin=minval),interpolation='nearest',zorder=0) + cax=cax1, norm=matplotlib.colors.LogNorm(vmin=minval),zorder=0) axs[0,0].set_title('(a) Snow Water Equivalent '+stat_word+ ' Diffs') axs[0,0].axis('off') cax1.set_ylabel('scalarSWE $[kg~m^{-2}]$',labelpad=-100) @@ -217,7 +217,7 @@ def make_default_path(suffix): var = 'scalarTotalSoilWat' minval = (bas_albers[var].where(bas_albers[var]>0)).min() bas_albers.plot(ax=axs[0,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax2, norm=matplotlib.colors.LogNorm(vmin=minval),interpolation='nearest',zorder=0) + cax=cax2, norm=matplotlib.colors.LogNorm(vmin=minval),zorder=0) axs[0,1].set_title('(b) Total soil water content '+stat_word+ ' Diffs') axs[0,1].axis('off') cax2.set_ylabel('scalarTotalSoilWat $[kg~m^{-2}]$',labelpad=-100) @@ -226,7 +226,7 @@ def make_default_path(suffix): var = 'scalarTotalET' minval = (bas_albers[var].where(bas_albers[var]>0)).min() bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax3, norm=matplotlib.colors.LogNorm(vmin=minval), interpolation='nearest',zorder=0) + cax=cax3, norm=matplotlib.colors.LogNorm(vmin=minval),zorder=0) axs[1,0].set_title('(c) Total evapotranspiration '+stat_word+ ' Diffs') axs[1,0].axis('off') cax3.set_ylabel('scalarTotalET $[kg~m^{-2}~s^{-1}]$',labelpad=-100) @@ -235,7 +235,7 @@ def make_default_path(suffix): var = 'scalarCanopyWat' minval = (bas_albers[var].where(bas_albers[var]>0)).min() bas_albers.plot(ax=axs[1,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax4, norm=matplotlib.colors.LogNorm(vmin=minval), interpolation='nearest',zorder=0) + cax=cax4, norm=matplotlib.colors.LogNorm(vmin=minval),zorder=0) axs[1,1].set_title('(d) Total water on the vegetation canopy '+stat_word+ ' Diffs') axs[1,1].axis('off') cax4.set_ylabel('scalarCanopyWat $[kg~m^{-2}]$',labelpad=-100) @@ -244,7 +244,7 @@ def make_default_path(suffix): var = 'averageRoutedRunoff' minval = (bas_albers[var].where(bas_albers[var]>0)).min() bas_albers.plot(ax=axs[2,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax5, norm=matplotlib.colors.LogNorm(vmin=minval), interpolation='nearest',zorder=0) + cax=cax5, norm=matplotlib.colors.LogNorm(vmin=minval), zorder=0) axs[2,0].set_title('(e) Routed runoff '+stat_word+ ' Diffs') axs[2,0].axis('off') cax5.set_ylabel('averageRoutedRunoff $[m~s^{-1}]$',labelpad=-100) @@ -253,7 +253,7 @@ def make_default_path(suffix): var = 'wallClockTime' minval = (bas_albers[var].where(bas_albers[var]>0)).min() bas_albers.plot(ax=axs[2,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax6, norm=matplotlib.colors.LogNorm(vmin=minval), interpolation='nearest',zorder=0) + cax=cax6, norm=matplotlib.colors.LogNorm(vmin=minval),zorder=0) axs[2,1].set_title('(f) Wall clock time '+stat_word+ ' Diffs') axs[2,1].axis('off') From 52121ed239d94115a61cfbe64503830ab5286f35 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 9 Jan 2023 20:44:49 +0900 Subject: [PATCH 0468/1472] fill with min value plotting --- utils/plot_per_GRU.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index c19ad926c..3a27c21a7 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -205,17 +205,22 @@ def make_default_path(suffix): stat_word = 'Abs Max' # add maps +# SWE var = 'scalarSWE' minval = (bas_albers[var].where(bas_albers[var]>0)).min() +a = bas_albers[var].where(bas_albers[var]>0) +bas_albers[var] = a.fillna(minval) bas_albers.plot(ax=axs[0,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ cax=cax1, norm=matplotlib.colors.LogNorm(vmin=minval),zorder=0) axs[0,0].set_title('(a) Snow Water Equivalent '+stat_word+ ' Diffs') axs[0,0].axis('off') cax1.set_ylabel('scalarSWE $[kg~m^{-2}]$',labelpad=-100) -# SM +# SoilWat var = 'scalarTotalSoilWat' minval = (bas_albers[var].where(bas_albers[var]>0)).min() +a = bas_albers[var].where(bas_albers[var]>0) +bas_albers[var] = a.fillna(minval) bas_albers.plot(ax=axs[0,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ cax=cax2, norm=matplotlib.colors.LogNorm(vmin=minval),zorder=0) axs[0,1].set_title('(b) Total soil water content '+stat_word+ ' Diffs') @@ -225,6 +230,8 @@ def make_default_path(suffix): # ET var = 'scalarTotalET' minval = (bas_albers[var].where(bas_albers[var]>0)).min() +a = bas_albers[var].where(bas_albers[var]>0) +bas_albers[var] = a.fillna(minval) bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ cax=cax3, norm=matplotlib.colors.LogNorm(vmin=minval),zorder=0) axs[1,0].set_title('(c) Total evapotranspiration '+stat_word+ ' Diffs') @@ -234,6 +241,8 @@ def make_default_path(suffix): # CanWat var = 'scalarCanopyWat' minval = (bas_albers[var].where(bas_albers[var]>0)).min() +a = bas_albers[var].where(bas_albers[var]>0) +bas_albers[var] = a.fillna(minval) bas_albers.plot(ax=axs[1,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ cax=cax4, norm=matplotlib.colors.LogNorm(vmin=minval),zorder=0) axs[1,1].set_title('(d) Total water on the vegetation canopy '+stat_word+ ' Diffs') @@ -243,6 +252,8 @@ def make_default_path(suffix): # Runoff var = 'averageRoutedRunoff' minval = (bas_albers[var].where(bas_albers[var]>0)).min() +a = bas_albers[var].where(bas_albers[var]>0) +bas_albers[var] = a.fillna(minval) bas_albers.plot(ax=axs[2,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ cax=cax5, norm=matplotlib.colors.LogNorm(vmin=minval), zorder=0) axs[2,0].set_title('(e) Routed runoff '+stat_word+ ' Diffs') @@ -252,6 +263,8 @@ def make_default_path(suffix): # Clock time var = 'wallClockTime' minval = (bas_albers[var].where(bas_albers[var]>0)).min() +a = bas_albers[var].where(bas_albers[var]>0) +bas_albers[var] = a.fillna(minval) bas_albers.plot(ax=axs[2,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ cax=cax6, norm=matplotlib.colors.LogNorm(vmin=minval),zorder=0) From e0dc4cb54e695af6768f7c85fe4214a5427824e5 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 9 Jan 2023 21:10:36 +0900 Subject: [PATCH 0469/1472] cut lognorm plotting, can't get to work (vmin vmax error) --- utils/plot_per_GRU.py | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 3a27c21a7..e238d899a 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -207,67 +207,52 @@ def make_default_path(suffix): # add maps # SWE var = 'scalarSWE' -minval = (bas_albers[var].where(bas_albers[var]>0)).min() -a = bas_albers[var].where(bas_albers[var]>0) -bas_albers[var] = a.fillna(minval) +#minval = (bas_albers[var].where(bas_albers[var]>0)).min() +#a = bas_albers[var].where(bas_albers[var]>0) +#bas_albers[var] = a.fillna(minval) +#norm=matplotlib.colors.LogNorm(vmin=minval) bas_albers.plot(ax=axs[0,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax1, norm=matplotlib.colors.LogNorm(vmin=minval),zorder=0) + cax=cax1, zorder=0) axs[0,0].set_title('(a) Snow Water Equivalent '+stat_word+ ' Diffs') axs[0,0].axis('off') cax1.set_ylabel('scalarSWE $[kg~m^{-2}]$',labelpad=-100) # SoilWat var = 'scalarTotalSoilWat' -minval = (bas_albers[var].where(bas_albers[var]>0)).min() -a = bas_albers[var].where(bas_albers[var]>0) -bas_albers[var] = a.fillna(minval) bas_albers.plot(ax=axs[0,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax2, norm=matplotlib.colors.LogNorm(vmin=minval),zorder=0) + cax=cax2, zorder=0) axs[0,1].set_title('(b) Total soil water content '+stat_word+ ' Diffs') axs[0,1].axis('off') cax2.set_ylabel('scalarTotalSoilWat $[kg~m^{-2}]$',labelpad=-100) # ET var = 'scalarTotalET' -minval = (bas_albers[var].where(bas_albers[var]>0)).min() -a = bas_albers[var].where(bas_albers[var]>0) -bas_albers[var] = a.fillna(minval) bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax3, norm=matplotlib.colors.LogNorm(vmin=minval),zorder=0) + cax=cax3, zorder=0) axs[1,0].set_title('(c) Total evapotranspiration '+stat_word+ ' Diffs') axs[1,0].axis('off') cax3.set_ylabel('scalarTotalET $[kg~m^{-2}~s^{-1}]$',labelpad=-100) # CanWat var = 'scalarCanopyWat' -minval = (bas_albers[var].where(bas_albers[var]>0)).min() -a = bas_albers[var].where(bas_albers[var]>0) -bas_albers[var] = a.fillna(minval) bas_albers.plot(ax=axs[1,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax4, norm=matplotlib.colors.LogNorm(vmin=minval),zorder=0) + cax=cax4, zorder=0) axs[1,1].set_title('(d) Total water on the vegetation canopy '+stat_word+ ' Diffs') axs[1,1].axis('off') cax4.set_ylabel('scalarCanopyWat $[kg~m^{-2}]$',labelpad=-100) # Runoff var = 'averageRoutedRunoff' -minval = (bas_albers[var].where(bas_albers[var]>0)).min() -a = bas_albers[var].where(bas_albers[var]>0) -bas_albers[var] = a.fillna(minval) bas_albers.plot(ax=axs[2,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax5, norm=matplotlib.colors.LogNorm(vmin=minval), zorder=0) + cax=cax5, zorder=0) axs[2,0].set_title('(e) Routed runoff '+stat_word+ ' Diffs') axs[2,0].axis('off') cax5.set_ylabel('averageRoutedRunoff $[m~s^{-1}]$',labelpad=-100) # Clock time var = 'wallClockTime' -minval = (bas_albers[var].where(bas_albers[var]>0)).min() -a = bas_albers[var].where(bas_albers[var]>0) -bas_albers[var] = a.fillna(minval) bas_albers.plot(ax=axs[2,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax6, norm=matplotlib.colors.LogNorm(vmin=minval),zorder=0) - + cax=cax6, zorder=0) axs[2,1].set_title('(f) Wall clock time '+stat_word+ ' Diffs') axs[2,1].axis('off') cax6.set_ylabel('wallClockTime( $[s]$',labelpad=-100) From 1521180cff43d94615306776d2fc8b8a6fef3fa1 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 10 Jan 2023 16:46:57 +0900 Subject: [PATCH 0470/1472] make plotting parallel --- utils/concat_groups_split_summa.py | 2 +- utils/plot_per_GRU.py | 139 +++++++++++------------------ utils/timeseries_to_statistics.py | 7 +- 3 files changed, 58 insertions(+), 90 deletions(-) diff --git a/utils/concat_groups_split_summa.py b/utils/concat_groups_split_summa.py index 489b7c40e..613225151 100644 --- a/utils/concat_groups_split_summa.py +++ b/utils/concat_groups_split_summa.py @@ -12,7 +12,7 @@ import netCDF4 as nc import numpy as np -method_name = 'sundials_1en8' +method_name = 'be64' catby_num = 2 #number of files to cat into one, if had to divide runs from regular batches into sub-batches to finish in 7 days top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index e238d899a..931eba5a8 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -16,6 +16,7 @@ # modules +import os import pyproj import fiona import matplotlib @@ -24,19 +25,26 @@ import geopandas as gpd from pathlib import Path import matplotlib.pyplot as plt -import matplotlib.gridspec as gridspec import copy +import multiprocessing as mp + # Simulation statistics file locations method_name = 'sundials_1en6' -stat = 'max' #max or rmse +stat = 'rmse' #max or rmse settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} -viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') -viz_fil = method_name + '_hrly_diff_stats_{}_{}.nc' +#viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') +viz_dir = Path('/home/avanb/scratch/statistics') +viz_fil = method_name + '_hrly_diff_stats_{}_{}2.nc' viz_fil = viz_fil.format(','.join(settings.keys()),','.join(settings.values())) # Specify variables of interest -plot_vars = ['averageRoutedRunoff','wallClockTime','scalarTotalET','scalarSWE','scalarCanopyWat','scalarTotalSoilWat'] +plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] +plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] +leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] +if stat=='rmse': maxes = [60,30,5e-6,0.2,1e-8,200] +if stat=='max' : maxes = [] + fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) @@ -46,8 +54,8 @@ # Plot lakes? plot_lakes = True # lakes shapefile WHERE IS THIS -lake_path = Path('C:/Globus endpoint/HydroLAKES/HydroLAKES_polys_v10_shp') -lake_name = 'HydroLAKES_polys_v10_subset_NA.shp' +#lake_path = Path('C:/Globus endpoint/HydroLAKES/HydroLAKES_polys_v10_shp') +#lake_name = 'HydroLAKES_polys_v10_subset_NA.shp' ## Control file handling # Store the name of the 'active' file in a variable @@ -140,12 +148,6 @@ def make_default_path(suffix): lak_albers = gpd.read_file(main/'lakes.shp') -# Print the median basin size for curiousity -#print('median area = {} m^2'.format(bas['HRU_area'].median() / 10**6)) -#print('mean area = {} m^2'.format(bas['HRU_area'].mean() / 10**6)) - -#median area = 33.06877343600296 m^2 -#mean area = 40.19396140285971 m^2 ## Pre-processing, map SUMMA sims to catchment shapes # Get the aggregated statistics of SUMMA simulations @@ -156,7 +158,6 @@ def make_default_path(suffix): for plot_var in plot_vars: bas_albers[plot_var] = summa[plot_var].sel(hru=hru_ids_shp.values) - # Select lakes of a certain size for plotting if plot_lakes: minSize = 1000 # km2 @@ -187,85 +188,51 @@ def make_default_path(suffix): fig,axs = plt.subplots(3,2,figsize=(140,133)) # colorbar axes -cax1 = fig.add_axes([0.473,0.73,0.02,0.3]) -cax2 = fig.add_axes([0.97 ,0.73,0.02,0.3]) -cax3 = fig.add_axes([0.473,0.40,0.02,0.3]) -cax4 = fig.add_axes([0.97 ,0.40,0.02,0.3]) -cax5 = fig.add_axes([0.473,0.07,0.02,0.3]) -cax6 = fig.add_axes([0.97 ,0.07,0.02,0.3]) +f_x_mat = [0.473,0.97,0.473,0.97,0.473,0.97] +f_y_mat = [0.69,0.69,0.36,0.36,0.03,0.03] plt.tight_layout() my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap -my_cmap.set_bad(my_cmap.colors[0]) +#my_cmap.set_bad(my_cmap.colors[0]) if stat == 'rmse': - stat_word = 'RMSE' + stat_word = ' Hourly RMSE' if stat == 'max': - stat_word = 'Abs Max' - -# add maps -# SWE -var = 'scalarSWE' -#minval = (bas_albers[var].where(bas_albers[var]>0)).min() -#a = bas_albers[var].where(bas_albers[var]>0) -#bas_albers[var] = a.fillna(minval) -#norm=matplotlib.colors.LogNorm(vmin=minval) -bas_albers.plot(ax=axs[0,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax1, zorder=0) -axs[0,0].set_title('(a) Snow Water Equivalent '+stat_word+ ' Diffs') -axs[0,0].axis('off') -cax1.set_ylabel('scalarSWE $[kg~m^{-2}]$',labelpad=-100) - -# SoilWat -var = 'scalarTotalSoilWat' -bas_albers.plot(ax=axs[0,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax2, zorder=0) -axs[0,1].set_title('(b) Total soil water content '+stat_word+ ' Diffs') -axs[0,1].axis('off') -cax2.set_ylabel('scalarTotalSoilWat $[kg~m^{-2}]$',labelpad=-100) - -# ET -var = 'scalarTotalET' -bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax3, zorder=0) -axs[1,0].set_title('(c) Total evapotranspiration '+stat_word+ ' Diffs') -axs[1,0].axis('off') -cax3.set_ylabel('scalarTotalET $[kg~m^{-2}~s^{-1}]$',labelpad=-100) - -# CanWat -var = 'scalarCanopyWat' -bas_albers.plot(ax=axs[1,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax4, zorder=0) -axs[1,1].set_title('(d) Total water on the vegetation canopy '+stat_word+ ' Diffs') -axs[1,1].axis('off') -cax4.set_ylabel('scalarCanopyWat $[kg~m^{-2}]$',labelpad=-100) - -# Runoff -var = 'averageRoutedRunoff' -bas_albers.plot(ax=axs[2,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax5, zorder=0) -axs[2,0].set_title('(e) Routed runoff '+stat_word+ ' Diffs') -axs[2,0].axis('off') -cax5.set_ylabel('averageRoutedRunoff $[m~s^{-1}]$',labelpad=-100) - -# Clock time -var = 'wallClockTime' -bas_albers.plot(ax=axs[2,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax6, zorder=0) -axs[2,1].set_title('(f) Wall clock time '+stat_word+ ' Diffs') -axs[2,1].axis('off') -cax6.set_ylabel('wallClockTime( $[s]$',labelpad=-100) - - -# lakes -if plot_lakes: - large_lakes_albers.plot(ax=axs[0,0], color=lake_col, zorder=1) - large_lakes_albers.plot(ax=axs[0,1], color=lake_col, zorder=1) - large_lakes_albers.plot(ax=axs[1,0], color=lake_col, zorder=1) - large_lakes_albers.plot(ax=axs[1,1], color=lake_col, zorder=1) - large_lakes_albers.plot(ax=axs[2,0], color=lake_col, zorder=1) - large_lakes_albers.plot(ax=axs[2,1], color=lake_col, zorder=1) + stat_word = ' Hourly max abs error' + +def run_loop(i,var,the_max,f_x,f_y): + #vmin = (bas_albers[var].where(bas_albers[var]>0)).min() + #a = bas_albers[var].where(bas_albers[var]>0) + #bas_albers[var] = a.fillna(vmin) + vmin,vmax = bas_albers[var].min(), bas_albers[var].max() + vmin,vmax = 0, the_max + norm=matplotlib.colors.Powernorm(vmin=vmin,vmax=vmax,gamma=0.5) + + # Data + sm = bas_albers.plot(ax=axs[i], column=var, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) + + # Custom colorbar + cax = fig.add_axes([f_x,f_y,0.02,0.3]) + cbr = fig.colorbar(sm, cax=cax, extend='max') + cbr.ax.set_title('[{}]'.format(leg_titl[i])) + + axs[i].set_title(plt_titl[i] + stat_word) + axs[i].axis('off') + + # lakes + if plot_lakes: large_lakes_albers.plot(ax=axs[i], color=lake_col, zorder=1) + + +# -- start parallel processing +ncpus = int(os.environ.get('SLURM_CPUS_PER_TASK',default=1)) +if __name__ == "__main__": + pool = mp.Pool(processes=ncpus) + results = [pool.apply_async(run_loop, args=(i,var,the_max,f_x,f_y)) for i,(var,the_max,f_x,f_y) in enumerate(zip(plot_vars,maxes,f_x_mat,f_y_mat))] + dojob = [p.get() for p in results] + pool.close() +# -- end parallel processing + # Save plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=True) diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 491b0afa5..573583028 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -18,7 +18,7 @@ import numpy as np # Settings -method_name = 'sundials_1en6' +method_name = 'be1' bench_name = 'sundials_1en8_cat' top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' @@ -31,9 +31,10 @@ src_dir = top_fold + 'summa-' + method_name ben_dir = top_fold + 'summa-' + bench_name src_pat = 'run1_G*_timestep.nc' -des_dir = top_fold + 'statistics' +#des_dir = top_fold + 'statistics' +des_dir = '/home/avanb/scratch/statistics' des_fil = method_name + '_hrly_diff_stats_{}_{}_{}.nc' -stat = 'rmse' #max or rmse +stat = 'max' #max or rmse settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} viz_fil = method_name + '_hrly_diff_stats_{}_{}.nc' From 303bdb0054fce485c682ab32e481a266e767d8cc Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 10 Jan 2023 19:58:15 +0900 Subject: [PATCH 0471/1472] max was wrong --- utils/plot_per_GRU.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 931eba5a8..a076a6c42 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -43,7 +43,7 @@ plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] if stat=='rmse': maxes = [60,30,5e-6,0.2,1e-8,200] -if stat=='max' : maxes = [] +if stat=='max' : maxes = [200,100,1e-5,2,] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) From eec54fa01ed9a6461fa69709e22a136888f15490 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 10 Jan 2023 20:00:44 +0900 Subject: [PATCH 0472/1472] maxes wrong --- utils/plot_per_GRU.py | 2 +- utils/timeseries_to_statistics.py | 15 ++++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index a076a6c42..c2bf618ea 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -43,7 +43,7 @@ plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] if stat=='rmse': maxes = [60,30,5e-6,0.2,1e-8,200] -if stat=='max' : maxes = [200,100,1e-5,2,] +if stat=='max' : maxes = [200,100,1e-5,2,4e-8,10000] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 573583028..2fa1cf72e 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -66,9 +66,10 @@ def run_loop(file,bench): # open file dat,ben = xr.open_dataset(file), xr.open_dataset(bench) - - #diff = (np.fabs(dat - ben)) #1-norm - diff = (np.square(dat - ben)) #2-norm + diff = (np.fabs(dat - ben)) + for var,stat0 in settings.items(): + # Select the case, redo if not max (slighly inefficient) + if stat0 == 'rmse':diff[var] = np.square(diff[var]) #2-norm # get rid of gru dimension, assuming they are same as the often are (everything now as hruId) diff = diff.drop_vars(['hruId','gruId']) m = diff.drop_dims('hru') @@ -78,13 +79,9 @@ def run_loop(file,bench): # compute the requested statistics for var,stat0 in settings.items(): - # Select the case - if stat0 == 'rmse': - #new = diff[var].mean(dim='time') #1-norm - new = ((diff[var].mean(dim='time'))**(1/2)) #RMSE SHOULD THIS BE NORMALIZED? colorbar will normalize - if stat0 == 'max': - new = diff[var].max(dim='time') #same regardless + if stat0 == 'rmse':new = ((diff[var].mean(dim='time'))**(1/2)) #RMSE SHOULD THIS BE NORMALIZED? colorbar will normalize + if stat0 == 'max': new = diff[var].max(dim='time') #same regardless new.to_netcdf(des_dir / des_fil.format(stat,var,subset)) From 39e5c6f001d375ba4ff767153976dc05e7649a84 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 11 Jan 2023 12:01:38 +0900 Subject: [PATCH 0473/1472] try several plotting routines --- utils/plot_per_GRU.py | 40 ++--- utils/plot_per_GRUorig.py | 275 +++++++++++++++++++++++++++++ utils/plot_per_GRUparellel.py | 280 ++++++++++++++++++++++++++++++ utils/timeseries_to_statistics.py | 9 +- 4 files changed, 582 insertions(+), 22 deletions(-) create mode 100644 utils/plot_per_GRUorig.py create mode 100644 utils/plot_per_GRUparellel.py diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index c2bf618ea..2a39b3682 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -26,8 +26,6 @@ from pathlib import Path import matplotlib.pyplot as plt import copy -import multiprocessing as mp - # Simulation statistics file locations method_name = 'sundials_1en6' @@ -35,17 +33,17 @@ settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} #viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') viz_dir = Path('/home/avanb/scratch/statistics') -viz_fil = method_name + '_hrly_diff_stats_{}_{}2.nc' +viz_fil = method_name + '_hrly_diff_stats_{}_{}.nc' viz_fil = viz_fil.format(','.join(settings.keys()),','.join(settings.values())) # Specify variables of interest plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] -if stat=='rmse': maxes = [60,30,5e-6,0.2,1e-8,200] -if stat=='max' : maxes = [200,100,1e-5,2,4e-8,10000] +if stat=='rmse': maxes = [60,30,5e-6,0.2,1e-8,10] +if stat=='max' : maxes = [100,50,5e-4,4,35e-8,10] -fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed.png' +fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed2.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) # Get the albers shapes @@ -187,6 +185,8 @@ def make_default_path(suffix): else: fig,axs = plt.subplots(3,2,figsize=(140,133)) +plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines + # colorbar axes f_x_mat = [0.473,0.97,0.473,0.97,0.473,0.97] f_y_mat = [0.69,0.69,0.36,0.36,0.03,0.03] @@ -205,34 +205,34 @@ def run_loop(i,var,the_max,f_x,f_y): #vmin = (bas_albers[var].where(bas_albers[var]>0)).min() #a = bas_albers[var].where(bas_albers[var]>0) #bas_albers[var] = a.fillna(vmin) - vmin,vmax = bas_albers[var].min(), bas_albers[var].max() + #vmin,vmax = bas_albers[var].min(), bas_albers[var].max() vmin,vmax = 0, the_max - norm=matplotlib.colors.Powernorm(vmin=vmin,vmax=vmax,gamma=0.5) + norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) + r = i//2 + c = i-r*2 # Data - sm = bas_albers.plot(ax=axs[i], column=var, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) + sm = bas_albers.plot(ax=axs[r,c], column=var, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) # Custom colorbar cax = fig.add_axes([f_x,f_y,0.02,0.3]) cbr = fig.colorbar(sm, cax=cax, extend='max') cbr.ax.set_title('[{}]'.format(leg_titl[i])) + + # wall Clock doesn't do difference + if var == 'wallClockTime': + if stat == 'rmse': stat_word = ' Hourly mean' + if stat == 'rmse': stat_word = ' Hourly max' - axs[i].set_title(plt_titl[i] + stat_word) - axs[i].axis('off') + axs[r,c].set_title(plt_titl[i] + stat_word) + axs[r,c].axis('off') # lakes if plot_lakes: large_lakes_albers.plot(ax=axs[i], color=lake_col, zorder=1) -# -- start parallel processing -ncpus = int(os.environ.get('SLURM_CPUS_PER_TASK',default=1)) -if __name__ == "__main__": - pool = mp.Pool(processes=ncpus) - results = [pool.apply_async(run_loop, args=(i,var,the_max,f_x,f_y)) for i,(var,the_max,f_x,f_y) in enumerate(zip(plot_vars,maxes,f_x_mat,f_y_mat))] - dojob = [p.get() for p in results] - pool.close() -# -- end parallel processing - +for i,(var,the_max,f_x,f_y) in enumerate(zip(plot_vars,maxes,f_x_mat,f_y_mat)): + run_loop(i,var,the_max,f_x,f_y) # Save plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=True) diff --git a/utils/plot_per_GRUorig.py b/utils/plot_per_GRUorig.py new file mode 100644 index 000000000..a8e81add8 --- /dev/null +++ b/utils/plot_per_GRUorig.py @@ -0,0 +1,275 @@ +# written originally by W. Knoben, modified by A. Van Beusekom (2023) + +## Visualize statistics per GRU +## Needs: +# Catchment shapefile with GRU delineation +# SUMMA output statistics + +## Special note +# SUMMA simulations have been preprocessed into single value statistics per model element, using auxiliary scripts in ~/utils +# To improve visualization of large lakes, HydroLAKES lake delineations are plotted on top of the catchment GRUs and river segments. +# Dealing with HydroLAKES inputs is not considered within scope of the workflow and therefore requires some manual downloading and preprocessing of this data for those who wish to reproduce this step. +# The relevant code is easily disabled by switching the plot_lakes = True flag to False. + +# Run: +# python plot_per_GRU.py + + +# modules +import pyproj +import fiona +import matplotlib +import numpy as np +import xarray as xr +import geopandas as gpd +from pathlib import Path +import matplotlib.pyplot as plt +import matplotlib.gridspec as gridspec +import copy + +# Simulation statistics file locations +method_name = 'sundials_1en6' +stat = 'max' #max or rmse +settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} +viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') +viz_fil = method_name + '_hrly_diff_stats_{}_{}.nc' +viz_fil = viz_fil.format(','.join(settings.keys()),','.join(settings.values())) + +# Specify variables of interest +plot_vars = ['averageRoutedRunoff','wallClockTime','scalarTotalET','scalarSWE','scalarCanopyWat','scalarTotalSoilWat'] +fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed.png' +fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) + +# Get the albers shapes +main = Path('/home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/albers_projection') + +# Plot lakes? +plot_lakes = True +# lakes shapefile WHERE IS THIS +lake_path = Path('C:/Globus endpoint/HydroLAKES/HydroLAKES_polys_v10_shp') +lake_name = 'HydroLAKES_polys_v10_subset_NA.shp' + +## Control file handling +# Store the name of the 'active' file in a variable +controlFile = 'plot_control_NorthAmerica.txt' + +# Function to extract a given setting from the control file +def read_from_control( file, setting ): + + # Open controlFile and ... + with open(file) as contents: + for line in contents: + + # ... find the line with the requested setting + if setting in line and not line.startswith('#'): + break + + # Extract the setting's value + substring = line.split('|',1)[1] # Remove the setting's name (split into 2 based on '|', keep only 2nd part) + substring = substring.split('#',1)[0] # Remove comments, does nothing if no '#' is found + substring = substring.strip() # Remove leading and trailing whitespace, tabs, newlines + + # Return this value + return substring + +# Function to specify a default path +def make_default_path(suffix): + + # Get the root path + rootPath = Path( read_from_control(controlFile,'root_path') ) + + # Get the domain folder + domainName = read_from_control(controlFile,'domain_name') + domainFolder = 'domain_' + domainName + + # Specify the forcing path + defaultPath = rootPath / domainFolder / suffix + + return defaultPath + + +## Catchment shapefile location and variable names +# HM catchment shapefile path & name +hm_catchment_path = read_from_control(controlFile,'catchment_shp_path') +hm_catchment_name = read_from_control(controlFile,'catchment_shp_name') +# Specify default path if needed +if hm_catchment_path == 'default': + hm_catchment_path = make_default_path('shapefiles/catchment') # outputs a Path() +else: + hm_catchment_path = Path(hm_catchment_path) # make sure a user-specified path is a Path() + +# Find the GRU and HRU identifiers +hm_hruid = read_from_control(controlFile,'catchment_shp_hruid') + + +## River network shapefile location and variable names +# Plot rivers? +plot_rivers = False +# River network path & name +river_network_path = read_from_control(controlFile,'river_network_shp_path') +river_network_name = read_from_control(controlFile,'river_network_shp_name') +# Specify default path if needed +if river_network_path == 'default': + river_network_path = make_default_path('shapefiles/river_network') # outputs a Path() +else: + river_network_path = Path(river_network_path) # make sure a user-specified path is a Path() + +# Find the segment ID +seg_id = read_from_control(controlFile,'river_network_shp_segid') + + +## Load all shapefiles and project to Albers Conformal Conic and reproject +# Set the target CRS +acc = 'ESRI:102008' + +# catchment shapefile, first 2 lines throw error so cutting them +#bas = gpd.read_file(hm_catchment_path/hm_catchment_name) +#bas_albers = bas.to_crs(acc) +bas_albers = gpd.read_file(main/'basin.shp') + +# river network shapefile, first 2 lines throw error so cutting them +if plot_rivers: + #riv = gpd.read_file(river_network_path/river_network_name) + #riv_albers = riv.to_crs(acc) + riv_albers = gpd.read_file(main/'river.shp') + +# lakes shapefile, first 2 lines throw error so cutting them +if plot_lakes: + #lakes = gpd.read_file(lake_path/lake_name) + #lak_albers = lakes.to_crs(acc) + lak_albers = gpd.read_file(main/'lakes.shp') + + +# Print the median basin size for curiousity +#print('median area = {} m^2'.format(bas['HRU_area'].median() / 10**6)) +#print('mean area = {} m^2'.format(bas['HRU_area'].mean() / 10**6)) + +#median area = 33.06877343600296 m^2 +#mean area = 40.19396140285971 m^2 + +## Pre-processing, map SUMMA sims to catchment shapes +# Get the aggregated statistics of SUMMA simulations +summa = xr.open_dataset(viz_dir/viz_fil) + +# Match the accummulated values to the correct HRU IDs in the shapefile +hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile +for plot_var in plot_vars: + bas_albers[plot_var] = summa[plot_var].sel(hru=hru_ids_shp.values) + + +# Select lakes of a certain size for plotting +if plot_lakes: + minSize = 1000 # km2 + in_domain = (lak_albers['Country'] == 'Canada') | \ + (lak_albers['Country'] == 'United States of America') | \ + (lak_albers['Country'] == 'Mexico') + out_domain = (lak_albers['Pour_long'] > -80) & (lak_albers['Pour_lat'] > 65) # Exclude Baffin Island + large_lakes_albers = lak_albers.loc[(lak_albers['Lake_area'] > minSize) & in_domain & (~out_domain) ] +# Set the lake color +if plot_lakes: + lake_col = (8/255,81/255,156/255) + +##Figure + +# Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug +if 'compressed' in fig_fil: + plt.rcParams.update({'font.size': 25}) +else: + plt.rcParams.update({'font.size': 100}) + +# Flip the evaporation values so that they become positive, not if plotting diffs +#bas_albers['plot_ET'] = bas_albers['scalarTotalET'] * -1 +#bas_albers['plot_ET'] = bas_albers['plot_ET'].where(bas_albers['scalarTotalET'] != -9999, np.nan) + +if 'compressed' in fig_fil: + fig,axs = plt.subplots(3,2,figsize=(35,33)) +else: + fig,axs = plt.subplots(3,2,figsize=(140,133)) + +# colorbar axes +cax1 = fig.add_axes([0.473,0.69,0.02,0.3]) +cax2 = fig.add_axes([0.97 ,0.69,0.02,0.3]) +cax3 = fig.add_axes([0.473,0.36,0.02,0.3]) +cax4 = fig.add_axes([0.97 ,0.36,0.02,0.3]) +cax5 = fig.add_axes([0.473,0.03,0.02,0.3]) +cax6 = fig.add_axes([0.97 ,0.03,0.02,0.3]) + +plt.tight_layout() + +my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap +my_cmap.set_bad(my_cmap.colors[0]) + +if stat == 'rmse': + stat_word = 'RMSE' +if stat == 'max': + stat_word = 'Abs Max' + +# add maps +# SWE +var = 'scalarSWE' +#minval = (bas_albers[var].where(bas_albers[var]>0)).min() +#a = bas_albers[var].where(bas_albers[var]>0) +#bas_albers[var] = a.fillna(minval) +#norm=matplotlib.colors.LogNorm(vmin=minval) +bas_albers.plot(ax=axs[0,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ + cax=cax1, zorder=0) +axs[0,0].set_title('(a) Snow Water Equivalent '+stat_word+ ' Diffs') +axs[0,0].axis('off') +cax1.set_ylabel('scalarSWE $[kg~m^{-2}]$',labelpad=-100) + +# SoilWat +var = 'scalarTotalSoilWat' +bas_albers.plot(ax=axs[0,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ + cax=cax2, zorder=0) +axs[0,1].set_title('(b) Total soil water content '+stat_word+ ' Diffs') +axs[0,1].axis('off') +cax2.set_ylabel('scalarTotalSoilWat $[kg~m^{-2}]$',labelpad=-100) + +# ET +var = 'scalarTotalET' +bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ + cax=cax3, zorder=0) +axs[1,0].set_title('(c) Total evapotranspiration '+stat_word+ ' Diffs') +axs[1,0].axis('off') +cax3.set_ylabel('scalarTotalET $[kg~m^{-2}~s^{-1}]$',labelpad=-100) + +# CanWat +var = 'scalarCanopyWat' +bas_albers.plot(ax=axs[1,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ + cax=cax4, zorder=0) +axs[1,1].set_title('(d) Total water on the vegetation canopy '+stat_word+ ' Diffs') +axs[1,1].axis('off') +cax4.set_ylabel('scalarCanopyWat $[kg~m^{-2}]$',labelpad=-100) + +# Runoff +var = 'averageRoutedRunoff' +bas_albers.plot(ax=axs[2,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ + cax=cax5, zorder=0) +axs[2,0].set_title('(e) Routed runoff '+stat_word+ ' Diffs') +axs[2,0].axis('off') +cax5.set_ylabel('averageRoutedRunoff $[m~s^{-1}]$',labelpad=-100) + +# Clock time +var = 'wallClockTime' +bas_albers.plot(ax=axs[2,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ + cax=cax6, zorder=0) +if stat == 'rmse': + stat_word = 'Mean' +if stat == 'max': + stat_word = 'Max' +axs[2,1].set_title('(f) Wall clock time '+stat_word) +axs[2,1].axis('off') +cax6.set_ylabel('wallClockTime( $[s]$',labelpad=-100) + + +# lakes +if plot_lakes: + large_lakes_albers.plot(ax=axs[0,0], color=lake_col, zorder=1) + large_lakes_albers.plot(ax=axs[0,1], color=lake_col, zorder=1) + large_lakes_albers.plot(ax=axs[1,0], color=lake_col, zorder=1) + large_lakes_albers.plot(ax=axs[1,1], color=lake_col, zorder=1) + large_lakes_albers.plot(ax=axs[2,0], color=lake_col, zorder=1) + large_lakes_albers.plot(ax=axs[2,1], color=lake_col, zorder=1) + +# Save +plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=True) diff --git a/utils/plot_per_GRUparellel.py b/utils/plot_per_GRUparellel.py new file mode 100644 index 000000000..eb7dd30b4 --- /dev/null +++ b/utils/plot_per_GRUparellel.py @@ -0,0 +1,280 @@ +# written originally by W. Knoben, modified by A. Van Beusekom (2023) + +## Visualize statistics per GRU +## Needs: +# Catchment shapefile with GRU delineation +# SUMMA output statistics + +## Special note +# SUMMA simulations have been preprocessed into single value statistics per model element, using auxiliary scripts in ~/utils +# To improve visualization of large lakes, HydroLAKES lake delineations are plotted on top of the catchment GRUs and river segments. +# Dealing with HydroLAKES inputs is not considered within scope of the workflow and therefore requires some manual downloading and preprocessing of this data for those who wish to reproduce this step. +# The relevant code is easily disabled by switching the plot_lakes = True flag to False. + +# Run: +# python plot_per_GRU.py + + +# modules +import os +import pyproj +import fiona +import matplotlib +import numpy as np +import xarray as xr +import geopandas as gpd +from pathlib import Path +import matplotlib.pyplot as plt +import copy +# for parallellization +import multiprocessing as mp +from functools import partial +import io +from copy import deepcopy +from PIL import Image + + +# Simulation statistics file locations +method_name = 'sundials_1en6' +stat = 'rmse' #max or rmse +settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} +#viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') +viz_dir = Path('/home/avanb/scratch/statistics') +viz_fil = method_name + '_hrly_diff_stats_{}_{}.nc' +viz_fil = viz_fil.format(','.join(settings.keys()),','.join(settings.values())) + +# Specify variables of interest +plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] +plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] +leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] +if stat=='rmse': maxes = [60,30,5e-6,0.2,1e-8,200] +if stat=='max' : maxes = [100,50,5e-4,4,35e-8,50000] + +fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed2.png' +fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) + +# Get the albers shapes +main = Path('/home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/albers_projection') + +# Plot lakes? +plot_lakes = True +# lakes shapefile WHERE IS THIS +#lake_path = Path('C:/Globus endpoint/HydroLAKES/HydroLAKES_polys_v10_shp') +#lake_name = 'HydroLAKES_polys_v10_subset_NA.shp' + +## Control file handling +# Store the name of the 'active' file in a variable +controlFile = 'plot_control_NorthAmerica.txt' + +# Function to extract a given setting from the control file +def read_from_control( file, setting ): + + # Open controlFile and ... + with open(file) as contents: + for line in contents: + + # ... find the line with the requested setting + if setting in line and not line.startswith('#'): + break + + # Extract the setting's value + substring = line.split('|',1)[1] # Remove the setting's name (split into 2 based on '|', keep only 2nd part) + substring = substring.split('#',1)[0] # Remove comments, does nothing if no '#' is found + substring = substring.strip() # Remove leading and trailing whitespace, tabs, newlines + + # Return this value + return substring + +# Function to specify a default path +def make_default_path(suffix): + + # Get the root path + rootPath = Path( read_from_control(controlFile,'root_path') ) + + # Get the domain folder + domainName = read_from_control(controlFile,'domain_name') + domainFolder = 'domain_' + domainName + + # Specify the forcing path + defaultPath = rootPath / domainFolder / suffix + + return defaultPath + + +## Catchment shapefile location and variable names +# HM catchment shapefile path & name +hm_catchment_path = read_from_control(controlFile,'catchment_shp_path') +hm_catchment_name = read_from_control(controlFile,'catchment_shp_name') +# Specify default path if needed +if hm_catchment_path == 'default': + hm_catchment_path = make_default_path('shapefiles/catchment') # outputs a Path() +else: + hm_catchment_path = Path(hm_catchment_path) # make sure a user-specified path is a Path() + +# Find the GRU and HRU identifiers +hm_hruid = read_from_control(controlFile,'catchment_shp_hruid') + + +## River network shapefile location and variable names +# Plot rivers? +plot_rivers = False +# River network path & name +river_network_path = read_from_control(controlFile,'river_network_shp_path') +river_network_name = read_from_control(controlFile,'river_network_shp_name') +# Specify default path if needed +if river_network_path == 'default': + river_network_path = make_default_path('shapefiles/river_network') # outputs a Path() +else: + river_network_path = Path(river_network_path) # make sure a user-specified path is a Path() + +# Find the segment ID +seg_id = read_from_control(controlFile,'river_network_shp_segid') + + +## Load all shapefiles and project to Albers Conformal Conic and reproject +# Set the target CRS +acc = 'ESRI:102008' + +# catchment shapefile, first 2 lines throw error so cutting them +#bas = gpd.read_file(hm_catchment_path/hm_catchment_name) +#bas_albers = bas.to_crs(acc) +bas_albers = gpd.read_file(main/'basin.shp') + +# river network shapefile, first 2 lines throw error so cutting them +if plot_rivers: + #riv = gpd.read_file(river_network_path/river_network_name) + #riv_albers = riv.to_crs(acc) + riv_albers = gpd.read_file(main/'river.shp') + +# lakes shapefile, first 2 lines throw error so cutting them +if plot_lakes: + #lakes = gpd.read_file(lake_path/lake_name) + #lak_albers = lakes.to_crs(acc) + lak_albers = gpd.read_file(main/'lakes.shp') + + + +## Pre-processing, map SUMMA sims to catchment shapes +# Get the aggregated statistics of SUMMA simulations +summa = xr.open_dataset(viz_dir/viz_fil) + +# Match the accummulated values to the correct HRU IDs in the shapefile +hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile +for plot_var in plot_vars: + bas_albers[plot_var] = summa[plot_var].sel(hru=hru_ids_shp.values) + +# Select lakes of a certain size for plotting +if plot_lakes: + minSize = 1000 # km2 + in_domain = (lak_albers['Country'] == 'Canada') | \ + (lak_albers['Country'] == 'United States of America') | \ + (lak_albers['Country'] == 'Mexico') + out_domain = (lak_albers['Pour_long'] > -80) & (lak_albers['Pour_lat'] > 65) # Exclude Baffin Island + large_lakes_albers = lak_albers.loc[(lak_albers['Lake_area'] > minSize) & in_domain & (~out_domain) ] +# Set the lake color +if plot_lakes: + lake_col = (8/255,81/255,156/255) + +##Figure + +# Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug +if 'compressed' in fig_fil: + plt.rcParams.update({'font.size': 25}) +else: + plt.rcParams.update({'font.size': 100}) + +# Flip the evaporation values so that they become positive, not if plotting diffs +#bas_albers['plot_ET'] = bas_albers['scalarTotalET'] * -1 +#bas_albers['plot_ET'] = bas_albers['plot_ET'].where(bas_albers['scalarTotalET'] != -9999, np.nan) + +plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines + +# colorbar axes +f_x_mat = [0.473,0.97,0.473,0.97,0.473,0.97] +f_y_mat = [0.69,0.69,0.36,0.36,0.03,0.03] + +plt.tight_layout() + +my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap +#my_cmap.set_bad(my_cmap.colors[0]) + +if stat == 'rmse': + stat_word = ' Hourly RMSE' +if stat == 'max': + stat_word = ' Hourly max abs error' + +def make_plot(var,the_max,f_x,f_y,plt_t,leg_t, fig, axes): + #vmin = (bas_albers[var].where(bas_albers[var]>0)).min() + #a = bas_albers[var].where(bas_albers[var]>0) + #bas_albers[var] = a.fillna(vmin) + #vmin,vmax = bas_albers[var].min(), bas_albers[var].max() + vmin,vmax = 0, the_max + norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) + + # Data + sm = bas_albers.plot(ax=axes,column=var, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) + + # Custom colorbar + cax = fig.add_axes([f_x,f_y,0.02,0.3]) + cbr = fig.colorbar(sm, cax=cax, extend='max') + cbr.ax.set_title('[{}]'.format(leg_t)) + + # wall Clock doesn't do difference + if var == 'wallClockTime': + if stat == 'rmse': stat_word = ' Hourly mean' + if stat == 'rmse': stat_word = ' Hourly max' + + axes.set_title(plt_t+ stat_word) + axes.axis('off') + + # lakes + if plot_lakes: large_lakes_albers.plot(color=lake_col, zorder=1) + + +#From https://github.com/paulgavrikov/parallel-matplotlib-grid +def parallel_plot(plot_fn, data1,data2,data3,data4,data5,data6): + if 'compressed' in fig_fil: + fig,axes = plt.subplots(3,2,figsize=(35,33)) + else: + fig,axes = plt.subplots(3,2,figsize=(140,133)) + + + with mp.Pool() as pool: + for ax, rastered in zip(axes.ravel(), pool.map(partial(_parallel_plot_worker, plot_fn=plot_fn), data1,data2,data3,data4,data5,data6)): + ax.imshow(rastered) + + # The following code hides axes + ax.get_xaxis().set_ticks([]) + ax.get_yaxis().set_ticks([]) + for spine in ax.spines.values(): + spine.set_visible(False) + + plt.subplots_adjust(hspace=0, wspace=0) + +def _parallel_plot_worker( data1,data2,data3,data4,data5,data6, plot_fn): + fig = plt.figure() + matplotlib.font_manager._get_font.cache_clear() # necessary to reduce text corruption artifacts + axes = plt.axes() + + plot_fn( data1,data2,data3,data4,data5,data6, fig, axes) + pil_img = rasterize(fig) + plt.close() + + return pil_img + + +def rasterize(fig): + buf = io.BytesIO() + fig.savefig(buf, format='png', bbox_inches='tight') + buf.seek(0) + pil_img = deepcopy(Image.open(buf)) + buf.close() + + return pil_img + +# Run +parallel_plot(make_plot,plot_vars,maxes,f_x_mat,f_y_mat,plt_titl,leg_titl) + + +# Save +plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=True) diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 2fa1cf72e..dacb322c4 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -18,11 +18,11 @@ import numpy as np # Settings -method_name = 'be1' +method_name = 'sundials_1en6' bench_name = 'sundials_1en8_cat' top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' -testing = False +testing = True if testing: top_fold = '/Users/amedin/Research/USask/test_py/' else: @@ -70,6 +70,9 @@ def run_loop(file,bench): for var,stat0 in settings.items(): # Select the case, redo if not max (slighly inefficient) if stat0 == 'rmse':diff[var] = np.square(diff[var]) #2-norm + # wall clock don't do difference + if var == 'wallClockTime': diff[var] = dat[var] + # get rid of gru dimension, assuming they are same as the often are (everything now as hruId) diff = diff.drop_vars(['hruId','gruId']) m = diff.drop_dims('hru') @@ -82,6 +85,8 @@ def run_loop(file,bench): # Select the case if stat0 == 'rmse':new = ((diff[var].mean(dim='time'))**(1/2)) #RMSE SHOULD THIS BE NORMALIZED? colorbar will normalize if stat0 == 'max': new = diff[var].max(dim='time') #same regardless + # wall clock don't do difference + if var == 'wallClockTime' and stat0 == 'rmse': new = diff[var].mean(dim='time') new.to_netcdf(des_dir / des_fil.format(stat,var,subset)) From 9dbb91c085935f0835fee038641bab66fb4829f8 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 11 Jan 2023 12:12:25 +0900 Subject: [PATCH 0474/1472] add command line inputs so can run as script --- utils/plot_per_GRU.py | 9 ++++++--- utils/plot_per_GRUorig.py | 9 ++++++--- utils/plot_per_GRUparellel.py | 8 +++++--- utils/timeseries_to_statistics.py | 8 +++++--- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 2a39b3682..0add859fb 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -12,10 +12,11 @@ # The relevant code is easily disabled by switching the plot_lakes = True flag to False. # Run: -# python plot_per_GRU.py +# python plot_per_GRU.py sundials_1en6 rmse # modules +import sys import os import pyproj import fiona @@ -27,9 +28,11 @@ import matplotlib.pyplot as plt import copy +# The first input argument specifies the run where the files are +method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) +stat = sys.argv[2] # max or rmse + # Simulation statistics file locations -method_name = 'sundials_1en6' -stat = 'rmse' #max or rmse settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} #viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') viz_dir = Path('/home/avanb/scratch/statistics') diff --git a/utils/plot_per_GRUorig.py b/utils/plot_per_GRUorig.py index a8e81add8..26efa264a 100644 --- a/utils/plot_per_GRUorig.py +++ b/utils/plot_per_GRUorig.py @@ -12,10 +12,11 @@ # The relevant code is easily disabled by switching the plot_lakes = True flag to False. # Run: -# python plot_per_GRU.py +# python plot_per_GRU.py sundials_1en6 rmse # modules +import sys import pyproj import fiona import matplotlib @@ -27,9 +28,11 @@ import matplotlib.gridspec as gridspec import copy +# The first input argument specifies the run where the files are +method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) +stat = sys.argv[2] # max or rmse + # Simulation statistics file locations -method_name = 'sundials_1en6' -stat = 'max' #max or rmse settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') viz_fil = method_name + '_hrly_diff_stats_{}_{}.nc' diff --git a/utils/plot_per_GRUparellel.py b/utils/plot_per_GRUparellel.py index eb7dd30b4..38af55907 100644 --- a/utils/plot_per_GRUparellel.py +++ b/utils/plot_per_GRUparellel.py @@ -12,10 +12,11 @@ # The relevant code is easily disabled by switching the plot_lakes = True flag to False. # Run: -# python plot_per_GRU.py +# python plot_per_GRU.py sundials_1en6 rmse # modules +import sys import os import pyproj import fiona @@ -33,10 +34,11 @@ from copy import deepcopy from PIL import Image +# The first input argument specifies the run where the files are +method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) +stat = sys.argv[2] # max or rmse # Simulation statistics file locations -method_name = 'sundials_1en6' -stat = 'rmse' #max or rmse settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} #viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') viz_dir = Path('/home/avanb/scratch/statistics') diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index dacb322c4..6e7889510 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -10,15 +10,18 @@ # Run: # python timeseries_to_statistics.py - +import sys import os import glob import xarray as xr from pathlib import Path import numpy as np +# The first input argument specifies the run where the files are +method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) +stat = sys.argv[2] # max or rmse + # Settings -method_name = 'sundials_1en6' bench_name = 'sundials_1en8_cat' top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' @@ -34,7 +37,6 @@ #des_dir = top_fold + 'statistics' des_dir = '/home/avanb/scratch/statistics' des_fil = method_name + '_hrly_diff_stats_{}_{}_{}.nc' -stat = 'max' #max or rmse settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} viz_fil = method_name + '_hrly_diff_stats_{}_{}.nc' From 2d33d294ba02b4d803ff5568cd5966048eb49d86 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 11 Jan 2023 13:16:27 +0900 Subject: [PATCH 0475/1472] deal with nans in wallclock --- utils/concat_groups_split_summa.py | 1 - utils/plot_per_GRU.py | 6 +++--- utils/plot_per_GRUparellel.py | 6 +++--- utils/timeseries_to_statistics.py | 13 +++++++++---- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/utils/concat_groups_split_summa.py b/utils/concat_groups_split_summa.py index 613225151..1159c4d92 100644 --- a/utils/concat_groups_split_summa.py +++ b/utils/concat_groups_split_summa.py @@ -6,7 +6,6 @@ # Run: # python concat_groups_split_summa.py - import os from glob import glob import netCDF4 as nc diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 0add859fb..ac0cbbf33 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -43,8 +43,8 @@ plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] -if stat=='rmse': maxes = [60,30,5e-6,0.2,1e-8,10] -if stat=='max' : maxes = [100,50,5e-4,4,35e-8,10] +if stat=='rmse': maxes = [60,30,5e-6,0.2,1e-8,1e-2] +if stat=='max' : maxes = [100,50,5e-4,4,35e-8,100] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed2.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) @@ -197,7 +197,7 @@ def make_default_path(suffix): plt.tight_layout() my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap -#my_cmap.set_bad(my_cmap.colors[0]) +my_cmap.set_bad(my_cmap.colors[0]) #any nans if stat == 'rmse': stat_word = ' Hourly RMSE' diff --git a/utils/plot_per_GRUparellel.py b/utils/plot_per_GRUparellel.py index 38af55907..291a50ffe 100644 --- a/utils/plot_per_GRUparellel.py +++ b/utils/plot_per_GRUparellel.py @@ -49,8 +49,8 @@ plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] -if stat=='rmse': maxes = [60,30,5e-6,0.2,1e-8,200] -if stat=='max' : maxes = [100,50,5e-4,4,35e-8,50000] +if stat=='rmse': maxes = [60,30,5e-6,0.2,1e-8,1e-2] +if stat=='max' : maxes = [100,50,5e-4,4,35e-8,100] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed2.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) @@ -198,7 +198,7 @@ def make_default_path(suffix): plt.tight_layout() my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap -#my_cmap.set_bad(my_cmap.colors[0]) +my_cmap.set_bad(my_cmap.colors[0]) #any nans if stat == 'rmse': stat_word = ' Hourly RMSE' diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 6e7889510..9b691c29e 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -8,7 +8,7 @@ # Best to comment out parallel processing lines and run that way on Graham or for full dataset # Run: -# python timeseries_to_statistics.py +# python timeseries_to_statistics.py sundials_1en6 rmse import sys import os @@ -20,12 +20,14 @@ # The first input argument specifies the run where the files are method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) stat = sys.argv[2] # max or rmse +#method_name = 'be1' +#stat = "max" # Settings bench_name = 'sundials_1en8_cat' top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' -testing = True +testing = False if testing: top_fold = '/Users/amedin/Research/USask/test_py/' else: @@ -73,8 +75,11 @@ def run_loop(file,bench): # Select the case, redo if not max (slighly inefficient) if stat0 == 'rmse':diff[var] = np.square(diff[var]) #2-norm # wall clock don't do difference - if var == 'wallClockTime': diff[var] = dat[var] - + # sometimes wall clock only gives -9999 the whole run, make these nan and plot as lowest value (somewhat rare) + if var == 'wallClockTime': + diff[var] = dat[var] + diff[var] = diff[var].where(diff[var]>0) + # get rid of gru dimension, assuming they are same as the often are (everything now as hruId) diff = diff.drop_vars(['hruId','gruId']) m = diff.drop_dims('hru') From f285def08fe07cad233daa0f14ea3cb18c9fa50d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 11 Jan 2023 17:18:08 +0900 Subject: [PATCH 0476/1472] plot histograms and parallel edits --- utils/hist_per_GRU.py | 115 ++++++++++++++++++ utils/plot_per_GRU.py | 7 +- ...GRUparellel.py => plot_per_GRUparallel.py} | 12 +- utils/timeseries_to_statistics.py | 45 ++++--- 4 files changed, 149 insertions(+), 30 deletions(-) create mode 100644 utils/hist_per_GRU.py rename utils/{plot_per_GRUparellel.py => plot_per_GRUparallel.py} (98%) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py new file mode 100644 index 000000000..2c9308d59 --- /dev/null +++ b/utils/hist_per_GRU.py @@ -0,0 +1,115 @@ +# written originally by W. Knoben, modified by A. Van Beusekom (2023) + +## Visualize statistics per GRU +## Needs: +# Catchment shapefile with GRU delineation +# SUMMA output statistics + +## Special note +# SUMMA simulations have been preprocessed into single value statistics per model element, using auxiliary scripts in ~/utils +# To improve visualization of large lakes, HydroLAKES lake delineations are plotted on top of the catchment GRUs and river segments. +# Dealing with HydroLAKES inputs is not considered within scope of the workflow and therefore requires some manual downloading and preprocessing of this data for those who wish to reproduce this step. +# The relevant code is easily disabled by switching the plot_lakes = True flag to False. + +# Run: +# python hist_per_GRU.py sundials_1en6 rmse + + +# modules +import os +import matplotlib +import numpy as np +import xarray as xr +from pathlib import Path +import matplotlib.pyplot as plt +import copy + +#viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') +viz_dir = Path('/home/avanb/scratch/statistics') + +testing = False +if testing: + viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') + method_name = 'be1' + stat = "rmse" +else: + import sys + # The first input argument specifies the run where the files are + method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) + stat = sys.argv[2] # max or rmse + +# Simulation statistics file locations +settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} +viz_fil = method_name + '_hrly_diff_stats_{}_{}.nc' +viz_fil = viz_fil.format(','.join(settings.keys()),','.join(settings.values())) + + +# Specify variables of interest +plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] +plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] +leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] + +fig_fil = method_name + '_hrly_diff_hist_{}_{}_compressed.png' +fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) + +## Pre-processing, map SUMMA sims to catchment shapes +# Get the aggregated statistics of SUMMA simulations +summa = xr.open_dataset(viz_dir/viz_fil) + +##Figure + +# Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug +if 'compressed' in fig_fil: + plt.rcParams.update({'font.size': 25}) +else: + plt.rcParams.update({'font.size': 100}) + +# Flip the evaporation values so that they become positive, not if plotting diffs +#bas_albers['plot_ET'] = bas_albers['scalarTotalET'] * -1 +#bas_albers['plot_ET'] = bas_albers['plot_ET'].where(bas_albers['scalarTotalET'] != -9999, np.nan) + +if 'compressed' in fig_fil: + fig,axs = plt.subplots(3,2,figsize=(35,33)) +else: + fig,axs = plt.subplots(3,2,figsize=(140,133)) + +plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines + +# colorbar axes +f_x_mat = [0.473,0.97,0.473,0.97,0.473,0.97] +f_y_mat = [0.69,0.69,0.36,0.36,0.03,0.03] + +#plt.tight_layout() + + +def run_loop(i,var,f_x,f_y): + r = i//2 + c = i-r*2 + + # Data + sm = summa[var].plot.hist(ax=axs[r,c]) + + # Custom colorbar + #cax = fig.add_axes([f_x,f_y,0.02,0.3]) + #cbr = fig.colorbar(sm, cax=cax, extend='max') + #cbr.ax.set_title('[{}]'.format(leg_titl[i])) + + if stat == 'rmse': + stat_word = ' Hourly RMSE' + if stat == 'max': + stat_word = ' Hourly max abs error' + + # wall Clock doesn't do difference + if var == 'wallClockTime': + if stat == 'rmse': stat_word = ' Hourly mean' + if stat == 'max': stat_word = ' Hourly max' + + axs[r,c].set_title(plt_titl[i] + stat_word) + axs[r,c].set_xlabel('[{}]'.format(leg_titl[i])) + + +for i,(var,f_x,f_y) in enumerate(zip(plot_vars,f_x_mat,f_y_mat)): + run_loop(i,var,f_x,f_y) + +# Save +plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=True) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index ac0cbbf33..24d66a9fb 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -222,10 +222,15 @@ def run_loop(i,var,the_max,f_x,f_y): cbr = fig.colorbar(sm, cax=cax, extend='max') cbr.ax.set_title('[{}]'.format(leg_titl[i])) + if stat == 'rmse': + stat_word = ' Hourly RMSE' + if stat == 'max': + stat_word = ' Hourly max abs error' + # wall Clock doesn't do difference if var == 'wallClockTime': if stat == 'rmse': stat_word = ' Hourly mean' - if stat == 'rmse': stat_word = ' Hourly max' + if stat == 'max': stat_word = ' Hourly max' axs[r,c].set_title(plt_titl[i] + stat_word) axs[r,c].axis('off') diff --git a/utils/plot_per_GRUparellel.py b/utils/plot_per_GRUparallel.py similarity index 98% rename from utils/plot_per_GRUparellel.py rename to utils/plot_per_GRUparallel.py index 291a50ffe..64a2a38db 100644 --- a/utils/plot_per_GRUparellel.py +++ b/utils/plot_per_GRUparallel.py @@ -199,11 +199,6 @@ def make_default_path(suffix): my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap my_cmap.set_bad(my_cmap.colors[0]) #any nans - -if stat == 'rmse': - stat_word = ' Hourly RMSE' -if stat == 'max': - stat_word = ' Hourly max abs error' def make_plot(var,the_max,f_x,f_y,plt_t,leg_t, fig, axes): #vmin = (bas_albers[var].where(bas_albers[var]>0)).min() @@ -221,10 +216,15 @@ def make_plot(var,the_max,f_x,f_y,plt_t,leg_t, fig, axes): cbr = fig.colorbar(sm, cax=cax, extend='max') cbr.ax.set_title('[{}]'.format(leg_t)) + if stat == 'rmse': + stat_word = ' Hourly RMSE' + if stat == 'max': + stat_word = ' Hourly max abs error' + # wall Clock doesn't do difference if var == 'wallClockTime': if stat == 'rmse': stat_word = ' Hourly mean' - if stat == 'rmse': stat_word = ' Hourly max' + if stat == 'max': stat_word = ' Hourly max' axes.set_title(plt_t+ stat_word) axes.axis('off') diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 9b691c29e..09f9def00 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -10,34 +10,34 @@ # Run: # python timeseries_to_statistics.py sundials_1en6 rmse -import sys import os import glob import xarray as xr from pathlib import Path import numpy as np -# The first input argument specifies the run where the files are -method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) -stat = sys.argv[2] # max or rmse -#method_name = 'be1' -#stat = "max" - # Settings bench_name = 'sundials_1en8_cat' top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' +#des_dir = top_fold + 'statistics' +des_dir = '/home/avanb/scratch/statistics' testing = False -if testing: +if testing: top_fold = '/Users/amedin/Research/USask/test_py/' + des_dir = top_fold + 'statistics' + method_name = 'be1' + stat = "max" else: import multiprocessing as mp + import sys + # The first input argument specifies the run where the files are + method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) + stat = sys.argv[2] # max or rmse src_dir = top_fold + 'summa-' + method_name ben_dir = top_fold + 'summa-' + bench_name src_pat = 'run1_G*_timestep.nc' -#des_dir = top_fold + 'statistics' -des_dir = '/home/avanb/scratch/statistics' des_fil = method_name + '_hrly_diff_stats_{}_{}_{}.nc' settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} @@ -64,24 +64,24 @@ # -- functions def run_loop(file,bench): - + # extract the subset IDs subset = file.split('/')[-1].split('_')[1] # open file dat,ben = xr.open_dataset(file), xr.open_dataset(bench) - diff = (np.fabs(dat - ben)) + diff = (np.fabs(dat - ben)) for var,stat0 in settings.items(): # Select the case, redo if not max (slighly inefficient) - if stat0 == 'rmse':diff[var] = np.square(diff[var]) #2-norm + if stat0 == 'rmse':diff[var] = np.square(diff[var]) #2-norm # wall clock don't do difference # sometimes wall clock only gives -9999 the whole run, make these nan and plot as lowest value (somewhat rare) - if var == 'wallClockTime': + if var == 'wallClockTime': diff[var] = dat[var] diff[var] = diff[var].where(diff[var]>0) - + # get rid of gru dimension, assuming they are same as the often are (everything now as hruId) - diff = diff.drop_vars(['hruId','gruId']) + diff = diff.drop_vars(['hruId','gruId']) m = diff.drop_dims('hru') m = m.rename({'gru': 'hru'}) diff = diff.drop_dims('gru') @@ -96,9 +96,9 @@ def run_loop(file,bench): if var == 'wallClockTime' and stat0 == 'rmse': new = diff[var].mean(dim='time') new.to_netcdf(des_dir / des_fil.format(stat,var,subset)) - + print("wrote output: %s" % (top_fold + 'statistics/' +subset)) - + return #nothing def merge_subsets_into_one(src,pattern,des,name): @@ -118,9 +118,9 @@ def merge_subsets_into_one(src,pattern,des,name): # -- end functions -if testing: +if testing: # -- no parallel processing - for (file, bench) in zip(src_files,ben_files): + for (file, bench) in zip(src_files,ben_files): run_loop(file, bench) else: # -- start parallel processing @@ -132,12 +132,11 @@ def merge_subsets_into_one(src,pattern,des,name): pool.close() # -- end parallel processing - + # merge the individual files into one for further vizualization merge_subsets_into_one(des_dir,des_fil.replace('{}','*'),des_dir,viz_fil) # remove the individual files for cleanliness for file in glob.glob(str(des_dir / des_fil.replace('{}','*'))): os.remove(file) - - \ No newline at end of file + From 24e7692665e6f93612f857f1598d4d95999b710e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 12 Jan 2023 12:29:05 +0900 Subject: [PATCH 0477/1472] plot histograms together --- utils/hist_per_GRU.py | 55 ++++++++++++++++++----------------- utils/plot_per_GRU.py | 4 +-- utils/plot_per_GRUparallel.py | 4 +-- 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 2c9308d59..90a4483e2 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -12,7 +12,7 @@ # The relevant code is easily disabled by switching the plot_lakes = True flag to False. # Run: -# python hist_per_GRU.py sundials_1en6 rmse +# python hist_per_GRU.py rmse # modules @@ -21,6 +21,7 @@ import numpy as np import xarray as xr from pathlib import Path +import matplotlib.mlab as mlab import matplotlib.pyplot as plt import copy @@ -30,18 +31,21 @@ testing = False if testing: viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name = 'be1' - stat = "rmse" + stat = "max" else: import sys # The first input argument specifies the run where the files are - method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) - stat = sys.argv[2] # max or rmse + stat = sys.argv[1] # max or rmse # Simulation statistics file locations +method_name1='sundials_1en6' #keep constant? +method_name2='be1' #maybe make this an argument + settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} -viz_fil = method_name + '_hrly_diff_stats_{}_{}.nc' -viz_fil = viz_fil.format(','.join(settings.keys()),','.join(settings.values())) +viz_fil1 = method_name1 + '_hrly_diff_stats_{}_{}.nc' +viz_fil1 = viz_fil1.format(','.join(settings.keys()),','.join(settings.values())) +viz_fil2 = method_name2 + '_hrly_diff_stats_{}_{}.nc' +viz_fil2 = viz_fil2.format(','.join(settings.keys()),','.join(settings.values())) # Specify variables of interest @@ -49,12 +53,16 @@ plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] -fig_fil = method_name + '_hrly_diff_hist_{}_{}_compressed.png' +fig_fil = method_name1 + method_name2 + '_hrly_diff_hist_{}_{}_compressed.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) +# possibly want to use these to shrink the axes a bit +if stat=='rmse': maxes = [120,80,3e-5,0.2,1e-8,1e-2] +if stat=='max' : maxes = [100,50,5e-4,4,35e-8,100] ## Pre-processing, map SUMMA sims to catchment shapes # Get the aggregated statistics of SUMMA simulations -summa = xr.open_dataset(viz_dir/viz_fil) +summa1 = xr.open_dataset(viz_dir/viz_fil1) +summa2 = xr.open_dataset(viz_dir/viz_fil2) ##Figure @@ -73,26 +81,17 @@ else: fig,axs = plt.subplots(3,2,figsize=(140,133)) -plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines - -# colorbar axes -f_x_mat = [0.473,0.97,0.473,0.97,0.473,0.97] -f_y_mat = [0.69,0.69,0.36,0.36,0.03,0.03] - -#plt.tight_layout() - -def run_loop(i,var,f_x,f_y): +def run_loop(i,var,mx): r = i//2 c = i-r*2 - + vmin = 0 + num_bins = 200 + mx = max([summa1[var].max(),summa2[var].max()]).values + # Data - sm = summa[var].plot.hist(ax=axs[r,c]) - - # Custom colorbar - #cax = fig.add_axes([f_x,f_y,0.02,0.3]) - #cbr = fig.colorbar(sm, cax=cax, extend='max') - #cbr.ax.set_title('[{}]'.format(leg_titl[i])) + sm = summa1[var].plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=method_name1,linewidth=2.0,range=(0,mx)) + sm = summa2[var].plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=1,label=method_name2,linewidth=2.0,range=(0,mx)) if stat == 'rmse': stat_word = ' Hourly RMSE' @@ -104,12 +103,14 @@ def run_loop(i,var,f_x,f_y): if stat == 'rmse': stat_word = ' Hourly mean' if stat == 'max': stat_word = ' Hourly max' + axs[r,c].legend() axs[r,c].set_title(plt_titl[i] + stat_word) axs[r,c].set_xlabel('[{}]'.format(leg_titl[i])) + axs[r,c].set_ylabel('GRU count') -for i,(var,f_x,f_y) in enumerate(zip(plot_vars,f_x_mat,f_y_mat)): - run_loop(i,var,f_x,f_y) +for i,(var,mx) in enumerate(zip(plot_vars,maxes)): + run_loop(i,var,mx) # Save plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=True) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 24d66a9fb..a9be323da 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -43,8 +43,8 @@ plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] -if stat=='rmse': maxes = [60,30,5e-6,0.2,1e-8,1e-2] -if stat=='max' : maxes = [100,50,5e-4,4,35e-8,100] +if stat=='rmse': maxes = [10,10,5e-6,0.2,8e-9,7e-3] +if stat=='max' : maxes = [40,40,5e-4,4,35e-8,10] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed2.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) diff --git a/utils/plot_per_GRUparallel.py b/utils/plot_per_GRUparallel.py index 64a2a38db..4ca5688d4 100644 --- a/utils/plot_per_GRUparallel.py +++ b/utils/plot_per_GRUparallel.py @@ -49,8 +49,8 @@ plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] -if stat=='rmse': maxes = [60,30,5e-6,0.2,1e-8,1e-2] -if stat=='max' : maxes = [100,50,5e-4,4,35e-8,100] +if stat=='rmse': maxes = [10,10,5e-6,0.2,8e-9,7e-3] +if stat=='max' : maxes = [40,40,5e-4,4,35e-8,10] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed2.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) From a246bf1950a8d2c7dcc541badc6114e63073a055 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 12 Jan 2023 12:42:00 +0900 Subject: [PATCH 0478/1472] mv off scratch --- utils/hist_per_GRU.py | 3 +-- utils/plot_per_GRU.py | 3 +-- utils/plot_per_GRUparallel.py | 3 +-- utils/timeseries_to_statistics.py | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 90a4483e2..e8dbbf3fd 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -25,8 +25,7 @@ import matplotlib.pyplot as plt import copy -#viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') -viz_dir = Path('/home/avanb/scratch/statistics') +viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') testing = False if testing: diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index a9be323da..e0a08a999 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -34,8 +34,7 @@ # Simulation statistics file locations settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} -#viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') -viz_dir = Path('/home/avanb/scratch/statistics') +viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') viz_fil = method_name + '_hrly_diff_stats_{}_{}.nc' viz_fil = viz_fil.format(','.join(settings.keys()),','.join(settings.values())) diff --git a/utils/plot_per_GRUparallel.py b/utils/plot_per_GRUparallel.py index 4ca5688d4..2794d845c 100644 --- a/utils/plot_per_GRUparallel.py +++ b/utils/plot_per_GRUparallel.py @@ -40,8 +40,7 @@ # Simulation statistics file locations settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} -#viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') -viz_dir = Path('/home/avanb/scratch/statistics') +viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') viz_fil = method_name + '_hrly_diff_stats_{}_{}.nc' viz_fil = viz_fil.format(','.join(settings.keys()),','.join(settings.values())) diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 09f9def00..4d829da75 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -19,8 +19,7 @@ # Settings bench_name = 'sundials_1en8_cat' top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' -#des_dir = top_fold + 'statistics' -des_dir = '/home/avanb/scratch/statistics' +des_dir = top_fold + 'statistics' testing = False if testing: From 5029988212b055e17a99c09e15e8f84cfb60cdb1 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 12 Jan 2023 12:49:12 +0900 Subject: [PATCH 0479/1472] use hist zoom --- utils/hist_per_GRU.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index e8dbbf3fd..545cd8020 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -52,7 +52,7 @@ plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] -fig_fil = method_name1 + method_name2 + '_hrly_diff_hist_{}_{}_compressed.png' +fig_fil = method_name1 + method_name2 + '_hrly_diff_hist_{}_{}__zoom_compressed.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) # possibly want to use these to shrink the axes a bit if stat=='rmse': maxes = [120,80,3e-5,0.2,1e-8,1e-2] @@ -86,7 +86,10 @@ def run_loop(i,var,mx): c = i-r*2 vmin = 0 num_bins = 200 - mx = max([summa1[var].max(),summa2[var].max()]).values + if 'zoom' in fig_fil: + mx = mx + else: + mx = max([summa1[var].max(),summa2[var].max()]).values # Data sm = summa1[var].plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=method_name1,linewidth=2.0,range=(0,mx)) From e8ebe763e6580dadc600698580185c5c62207238 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 12 Jan 2023 13:09:16 +0900 Subject: [PATCH 0480/1472] add batch files for running --- utils/concat_groups_split_summa.py | 13 ++++++++----- utils/hist_per_GRU.py | 1 - utils/process_concat.sh | 22 ++++++++++++++++++++++ utils/process_diff.sh | 21 +++++++++++++++++++++ utils/process_hist.sh | 23 +++++++++++++++++++++++ utils/process_plot.sh | 25 +++++++++++++++++++++++++ utils/process_stat.sh | 23 +++++++++++++++++++++++ 7 files changed, 122 insertions(+), 6 deletions(-) create mode 100644 utils/process_concat.sh create mode 100644 utils/process_diff.sh create mode 100644 utils/process_hist.sh create mode 100644 utils/process_plot.sh create mode 100644 utils/process_stat.sh diff --git a/utils/concat_groups_split_summa.py b/utils/concat_groups_split_summa.py index 1159c4d92..22e890377 100644 --- a/utils/concat_groups_split_summa.py +++ b/utils/concat_groups_split_summa.py @@ -4,14 +4,13 @@ # Best to comment out parallel processing lines and run that way on Graham or for full dataset # Run: -# python concat_groups_split_summa.py +# python concat_groups_split_summa.py sundials_1en8 import os from glob import glob import netCDF4 as nc import numpy as np -method_name = 'be64' catby_num = 2 #number of files to cat into one, if had to divide runs from regular batches into sub-batches to finish in 7 days top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' @@ -20,10 +19,14 @@ misshru = missgru # could be different testing = False -if testing: +if testing: top_fold = '/Users/amedin/Research/USask/test_py/' + method_name = 'sundials_1en8' else: import multiprocessing as mp + import sys + # The first input argument specifies the run where the files are + method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en8 or be64) ncdir = top_fold + 'summa-' + method_name file_pattern = 'run1_G*_timestep.nc' @@ -130,12 +133,12 @@ def get_stat(g,catby_num,outfilelist0,ctdir): # print('Warning: gruId variable cannot be created since it has different size from hruId') print("wrote output: %s" % (ctdir+'/'+out_name)) - + return #nothing # -- end functions -if testing: +if testing: # -- no parallel processing for g in range(0,int(len(outfilelist0)/catby_num)): get_stat(g,catby_num,outfilelist0,ctdir) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 545cd8020..e15b89634 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -84,7 +84,6 @@ def run_loop(i,var,mx): r = i//2 c = i-r*2 - vmin = 0 num_bins = 200 if 'zoom' in fig_fil: mx = mx diff --git a/utils/process_concat.sh b/utils/process_concat.sh new file mode 100644 index 000000000..4ce574bb6 --- /dev/null +++ b/utils/process_concat.sh @@ -0,0 +1,22 @@ +#!/bin/bash +#SBATCH --ntasks-per-node=1 +#SBATCH --cpus-per-task=32 +#SBATCH --mem-per-cpu=2GB +#SBATCH --time=1-00:00 +#SBATCH --job-name=CONCAT +#SBATCH --mail-user=gwu479@usask.ca +#SBATCH --mail-type=ALL +#SBATCH --account=rpp-kshook +#SBATCH --output=/home/avanb/TestScripts/output/slurm-%A_%a.out + +module load gcc/9.3.0 +module load geo-stack +virtualenv --no-download $SLURM_TMPDIR/env +source $SLURM_TMPDIR/env/bin/activate +pip install --no-index --upgrade pip +pip install --no-index h5netcdf +pip install --no-index h5py +pip install --no-index xarray +pip install --no-index netCDF4 + +python concat_groups_split_summa.py sundials_1en8 diff --git a/utils/process_diff.sh b/utils/process_diff.sh new file mode 100644 index 000000000..db619a766 --- /dev/null +++ b/utils/process_diff.sh @@ -0,0 +1,21 @@ +#!/bin/bash +#SBATCH --ntasks-per-node=1 +#SBATCH --cpus-per-task=32 +#SBATCH --time=1-00:00 +#SBATCH --job-name=DIFFFF +#SBATCH --mail-user=gwu479@usask.ca +#SBATCH --mail-type=ALL +#SBATCH --account=rpp-kshook +#SBATCH --output=/home/avanb/TestScripts/output/slurm-%A_%a.out + +module load gcc/9.3.0 +module load geo-stack +virtualenv --no-download $SLURM_TMPDIR/env +source $SLURM_TMPDIR/env/bin/activate +pip install --no-index --upgrade pip +pip install --no-index h5netcdf +pip install --no-index h5py +pip install --no-index xarray +pip install --no-index netCDF4 + +python check_bit_4_bit_withTol.py /home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/summa-sundials_be_noJac/ /home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/summa-be/ 0.1 > out.txt diff --git a/utils/process_hist.sh b/utils/process_hist.sh new file mode 100644 index 000000000..58d82fccf --- /dev/null +++ b/utils/process_hist.sh @@ -0,0 +1,23 @@ +#!/bin/bash +#SBATCH --ntasks-per-node=1 +#SBATCH --cpus-per-task=1 +#SBATCH --mem-per-cpu=2GB +#SBATCH --time=1-00:00 +#SBATCH --job-name=HIST +#SBATCH --mail-user=gwu479@usask.ca +#SBATCH --mail-type=ALL +#SBATCH --account=rpp-kshook +#SBATCH --output=/home/avanb/TestScripts/output/slurm-%A_%a.out + +module load gcc/9.3.0 +module load geo-stack +virtualenv --no-download $SLURM_TMPDIR/env +source $SLURM_TMPDIR/env/bin/activate +pip install --no-index --upgrade pip +pip install --no-index h5netcdf +pip install --no-index h5py +pip install --no-index xarray +pip install --no-index netCDF4 + +python hist_per_GRU.py rmse +python hist_per_GRU.py max diff --git a/utils/process_plot.sh b/utils/process_plot.sh new file mode 100644 index 000000000..9b43be0b6 --- /dev/null +++ b/utils/process_plot.sh @@ -0,0 +1,25 @@ +#!/bin/bash +#SBATCH --ntasks-per-node=1 +#SBATCH --cpus-per-task=1 +#SBATCH --mem-per-cpu=50GB +#SBATCH --time=1-00:00 +#SBATCH --job-name=GDB +#SBATCH --mail-user=gwu479@usask.ca +#SBATCH --mail-type=ALL +#SBATCH --account=rpp-kshook +#SBATCH --output=/home/avanb/TestScripts/output/slurm-%A_%a.out + +module load gcc/9.3.0 +module load geo-stack +virtualenv --no-download $SLURM_TMPDIR/env +source $SLURM_TMPDIR/env/bin/activate +pip install --no-index --upgrade pip +pip install --no-index pyproj +pip install --no-index geopandas +pip install --no-index h5netcdf +pip install --no-index h5py +pip install --no-index xarray +pip install --no-index netCDF4 + +python plot_per_GRU.py sundials_1en6 rmse +python plot_per_GRU.py sundials_1en6 max \ No newline at end of file diff --git a/utils/process_stat.sh b/utils/process_stat.sh new file mode 100644 index 000000000..39ae6065d --- /dev/null +++ b/utils/process_stat.sh @@ -0,0 +1,23 @@ +#!/bin/bash +#SBATCH --ntasks-per-node=1 +#SBATCH --cpus-per-task=32 +#SBATCH --mem-per-cpu=3GB +#SBATCH --time=1-00:00 +#SBATCH --job-name=STAT +#SBATCH --mail-user=gwu479@usask.ca +#SBATCH --mail-type=ALL +#SBATCH --account=rpp-kshook +#SBATCH --output=/home/avanb/TestScripts/output/slurm-%A_%a.out + +module load gcc/9.3.0 +module load geo-stack +virtualenv --no-download $SLURM_TMPDIR/env +source $SLURM_TMPDIR/env/bin/activate +pip install --no-index --upgrade pip +pip install --no-index h5netcdf +pip install --no-index h5py +pip install --no-index xarray +pip install --no-index netCDF4 + +python timeseries_to_statistics.py sundials_1en6 rmse +python timeseries_to_statistics.py sundials_1en6 max From 49b8dca9629d939c33440dee311fe847e81f8319 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 12 Jan 2023 20:27:58 +0900 Subject: [PATCH 0481/1472] sometimes these need more memory --- utils/process_concat.sh | 2 +- utils/process_diff.sh | 2 +- utils/process_stat.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/process_concat.sh b/utils/process_concat.sh index 4ce574bb6..ef4141963 100644 --- a/utils/process_concat.sh +++ b/utils/process_concat.sh @@ -1,7 +1,7 @@ #!/bin/bash #SBATCH --ntasks-per-node=1 #SBATCH --cpus-per-task=32 -#SBATCH --mem-per-cpu=2GB +#SBATCH --mem-per-cpu=6GB #SBATCH --time=1-00:00 #SBATCH --job-name=CONCAT #SBATCH --mail-user=gwu479@usask.ca diff --git a/utils/process_diff.sh b/utils/process_diff.sh index db619a766..13ea8b667 100644 --- a/utils/process_diff.sh +++ b/utils/process_diff.sh @@ -2,7 +2,7 @@ #SBATCH --ntasks-per-node=1 #SBATCH --cpus-per-task=32 #SBATCH --time=1-00:00 -#SBATCH --job-name=DIFFFF +#SBATCH --job-name=DIFF #SBATCH --mail-user=gwu479@usask.ca #SBATCH --mail-type=ALL #SBATCH --account=rpp-kshook diff --git a/utils/process_stat.sh b/utils/process_stat.sh index 39ae6065d..31f79905a 100644 --- a/utils/process_stat.sh +++ b/utils/process_stat.sh @@ -1,7 +1,7 @@ #!/bin/bash #SBATCH --ntasks-per-node=1 #SBATCH --cpus-per-task=32 -#SBATCH --mem-per-cpu=3GB +#SBATCH --mem-per-cpu=6GB #SBATCH --time=1-00:00 #SBATCH --job-name=STAT #SBATCH --mail-user=gwu479@usask.ca From b6f2046f17eb62c75898455a3756df2762e558d5 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 12 Jan 2023 23:48:45 +0900 Subject: [PATCH 0482/1472] update bounds zoom --- utils/hist_per_GRU.py | 5 +++-- utils/plot_per_GRU.py | 4 ++-- utils/plot_per_GRUparallel.py | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index e15b89634..b2be3bf84 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -55,8 +55,9 @@ fig_fil = method_name1 + method_name2 + '_hrly_diff_hist_{}_{}__zoom_compressed.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) # possibly want to use these to shrink the axes a bit -if stat=='rmse': maxes = [120,80,3e-5,0.2,1e-8,1e-2] -if stat=='max' : maxes = [100,50,5e-4,4,35e-8,100] +if stat=='rmse': maxes = [10,20,1e-5,0.1,8e-9,7e-3] +if stat=='max' : maxes = [40,40,4e-4,3,35e-8,10] + ## Pre-processing, map SUMMA sims to catchment shapes # Get the aggregated statistics of SUMMA simulations diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index e0a08a999..d6e0f34f8 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -42,8 +42,8 @@ plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] -if stat=='rmse': maxes = [10,10,5e-6,0.2,8e-9,7e-3] -if stat=='max' : maxes = [40,40,5e-4,4,35e-8,10] +if stat=='rmse': maxes = [10,20,1e-5,0.1,8e-9,7e-3] +if stat=='max' : maxes = [40,40,4e-4,3,35e-8,10] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed2.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) diff --git a/utils/plot_per_GRUparallel.py b/utils/plot_per_GRUparallel.py index 2794d845c..0b545f99c 100644 --- a/utils/plot_per_GRUparallel.py +++ b/utils/plot_per_GRUparallel.py @@ -48,8 +48,8 @@ plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] -if stat=='rmse': maxes = [10,10,5e-6,0.2,8e-9,7e-3] -if stat=='max' : maxes = [40,40,5e-4,4,35e-8,10] +if stat=='rmse': maxes = [10,20,1e-5,0.1,8e-9,7e-3] +if stat=='max' : maxes = [40,40,4e-4,3,35e-8,10] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed2.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) From 0689f1cbd5e6e4eb602e3bb2df1328a1a1c0b1ad Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 13 Jan 2023 00:05:07 +0900 Subject: [PATCH 0483/1472] zoom bounds --- utils/hist_per_GRU.py | 4 ++-- utils/plot_per_GRU.py | 4 ++-- utils/plot_per_GRUparallel.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index b2be3bf84..82b87c912 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -55,8 +55,8 @@ fig_fil = method_name1 + method_name2 + '_hrly_diff_hist_{}_{}__zoom_compressed.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) # possibly want to use these to shrink the axes a bit -if stat=='rmse': maxes = [10,20,1e-5,0.1,8e-9,7e-3] -if stat=='max' : maxes = [40,40,4e-4,3,35e-8,10] +if stat=='rmse': maxes = [4,15,8e-6,0.08,7e-9,7e-3] +if stat=='max' : maxes = [20,30,3e-4,2,35e-8,2] ## Pre-processing, map SUMMA sims to catchment shapes diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index d6e0f34f8..e6efb4741 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -42,8 +42,8 @@ plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] -if stat=='rmse': maxes = [10,20,1e-5,0.1,8e-9,7e-3] -if stat=='max' : maxes = [40,40,4e-4,3,35e-8,10] +if stat=='rmse': maxes = [4,15,8e-6,0.08,7e-9,7e-3] +if stat=='max' : maxes = [20,30,3e-4,2,35e-8,2] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed2.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) diff --git a/utils/plot_per_GRUparallel.py b/utils/plot_per_GRUparallel.py index 0b545f99c..cb08d1656 100644 --- a/utils/plot_per_GRUparallel.py +++ b/utils/plot_per_GRUparallel.py @@ -48,8 +48,8 @@ plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] -if stat=='rmse': maxes = [10,20,1e-5,0.1,8e-9,7e-3] -if stat=='max' : maxes = [40,40,4e-4,3,35e-8,10] +if stat=='rmse': maxes = [4,15,8e-6,0.08,7e-9,7e-3] +if stat=='max' : maxes = [20,30,3e-4,2,35e-8,2] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed2.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) From 157234fa1907dba67676fdce1be1961312df5b30 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 13 Jan 2023 00:12:59 +0900 Subject: [PATCH 0484/1472] zoom bounds --- utils/hist_per_GRU.py | 4 ++-- utils/plot_per_GRU.py | 4 ++-- utils/plot_per_GRUparallel.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 82b87c912..c3d9f3bfc 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -55,8 +55,8 @@ fig_fil = method_name1 + method_name2 + '_hrly_diff_hist_{}_{}__zoom_compressed.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) # possibly want to use these to shrink the axes a bit -if stat=='rmse': maxes = [4,15,8e-6,0.08,7e-9,7e-3] -if stat=='max' : maxes = [20,30,3e-4,2,35e-8,2] +if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,7e-3] +if stat=='max' : maxes = [20,30,3e-4,2,35e-8,1] ## Pre-processing, map SUMMA sims to catchment shapes diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index e6efb4741..9b11e7657 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -42,8 +42,8 @@ plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] -if stat=='rmse': maxes = [4,15,8e-6,0.08,7e-9,7e-3] -if stat=='max' : maxes = [20,30,3e-4,2,35e-8,2] +if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,7e-3] +if stat=='max' : maxes = [20,30,3e-4,2,35e-8,1] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed2.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) diff --git a/utils/plot_per_GRUparallel.py b/utils/plot_per_GRUparallel.py index cb08d1656..bff899cef 100644 --- a/utils/plot_per_GRUparallel.py +++ b/utils/plot_per_GRUparallel.py @@ -48,8 +48,8 @@ plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] -if stat=='rmse': maxes = [4,15,8e-6,0.08,7e-9,7e-3] -if stat=='max' : maxes = [20,30,3e-4,2,35e-8,2] +if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,7e-3] +if stat=='max' : maxes = [20,30,3e-4,2,35e-8,1] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed2.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) From fb12f5b557b828cc3c3fef05ea58fc55e4eaef95 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 13 Jan 2023 21:14:15 +0900 Subject: [PATCH 0485/1472] fixing gru plotting --- utils/hist_per_GRU.py | 2 +- utils/plot_per_GRU.py | 6 ++++-- utils/plot_per_GRUparallel.py | 6 ++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index c3d9f3bfc..b7e9e6921 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -56,7 +56,7 @@ fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) # possibly want to use these to shrink the axes a bit if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,7e-3] -if stat=='max' : maxes = [20,30,3e-4,2,35e-8,1] +if stat=='max' : maxes = [20,30,3e-4,2,35e-8,0.7] ## Pre-processing, map SUMMA sims to catchment shapes diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 9b11e7657..b73b973ea 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -43,7 +43,7 @@ plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,7e-3] -if stat=='max' : maxes = [20,30,3e-4,2,35e-8,1] +if stat=='max' : maxes = [20,30,3e-4,2,35e-8,0.7] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed2.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) @@ -214,10 +214,12 @@ def run_loop(i,var,the_max,f_x,f_y): c = i-r*2 # Data - sm = bas_albers.plot(ax=axs[r,c], column=var, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) + bas_albers.plot(ax=axs[r,c], column=var, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) # Custom colorbar cax = fig.add_axes([f_x,f_y,0.02,0.3]) + sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=norm) + sm._A = [] cbr = fig.colorbar(sm, cax=cax, extend='max') cbr.ax.set_title('[{}]'.format(leg_titl[i])) diff --git a/utils/plot_per_GRUparallel.py b/utils/plot_per_GRUparallel.py index bff899cef..e227225ce 100644 --- a/utils/plot_per_GRUparallel.py +++ b/utils/plot_per_GRUparallel.py @@ -49,7 +49,7 @@ plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,7e-3] -if stat=='max' : maxes = [20,30,3e-4,2,35e-8,1] +if stat=='max' : maxes = [20,30,3e-4,2,35e-8,0.7] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed2.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) @@ -208,10 +208,12 @@ def make_plot(var,the_max,f_x,f_y,plt_t,leg_t, fig, axes): norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) # Data - sm = bas_albers.plot(ax=axes,column=var, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) + bas_albers.plot(ax=axes,column=var, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) # Custom colorbar cax = fig.add_axes([f_x,f_y,0.02,0.3]) + sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=norm) + sm._A = [] cbr = fig.colorbar(sm, cax=cax, extend='max') cbr.ax.set_title('[{}]'.format(leg_t)) From 5b2843b98b40258b5d2612061cbb36349703ff28 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 13 Jan 2023 21:22:14 +0900 Subject: [PATCH 0486/1472] plt should be matplotlib? --- utils/plot_per_GRU.py | 3 ++- utils/plot_per_GRUparallel.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index b73b973ea..3f4b10861 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -210,6 +210,7 @@ def run_loop(i,var,the_max,f_x,f_y): #vmin,vmax = bas_albers[var].min(), bas_albers[var].max() vmin,vmax = 0, the_max norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) + #norm=matplotlib.colors.Normalize(vmin=vmin, vmax=vmax) r = i//2 c = i-r*2 @@ -218,7 +219,7 @@ def run_loop(i,var,the_max,f_x,f_y): # Custom colorbar cax = fig.add_axes([f_x,f_y,0.02,0.3]) - sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=norm) + sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) sm._A = [] cbr = fig.colorbar(sm, cax=cax, extend='max') cbr.ax.set_title('[{}]'.format(leg_titl[i])) diff --git a/utils/plot_per_GRUparallel.py b/utils/plot_per_GRUparallel.py index e227225ce..2a2233968 100644 --- a/utils/plot_per_GRUparallel.py +++ b/utils/plot_per_GRUparallel.py @@ -206,13 +206,14 @@ def make_plot(var,the_max,f_x,f_y,plt_t,leg_t, fig, axes): #vmin,vmax = bas_albers[var].min(), bas_albers[var].max() vmin,vmax = 0, the_max norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) + #norm=matplotlib.colors.Normalize(vmin=vmin, vmax=vmax) # Data bas_albers.plot(ax=axes,column=var, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) # Custom colorbar cax = fig.add_axes([f_x,f_y,0.02,0.3]) - sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=norm) + sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) sm._A = [] cbr = fig.colorbar(sm, cax=cax, extend='max') cbr.ax.set_title('[{}]'.format(leg_t)) From d4a883602cd446f794bfbb8bcdbfb7d394162f7d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 14 Jan 2023 21:07:27 +0900 Subject: [PATCH 0487/1472] plot_per_GRU works now --- utils/plot_per_GRU.py | 4 ++-- utils/plot_per_GRUparallel.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 3f4b10861..5fbf088fc 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -45,7 +45,7 @@ if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,7e-3] if stat=='max' : maxes = [20,30,3e-4,2,35e-8,0.7] -fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed2.png' +fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) # Get the albers shapes @@ -238,7 +238,7 @@ def run_loop(i,var,the_max,f_x,f_y): axs[r,c].axis('off') # lakes - if plot_lakes: large_lakes_albers.plot(ax=axs[i], color=lake_col, zorder=1) + if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) for i,(var,the_max,f_x,f_y) in enumerate(zip(plot_vars,maxes,f_x_mat,f_y_mat)): diff --git a/utils/plot_per_GRUparallel.py b/utils/plot_per_GRUparallel.py index 2a2233968..ee9ce3db1 100644 --- a/utils/plot_per_GRUparallel.py +++ b/utils/plot_per_GRUparallel.py @@ -232,7 +232,7 @@ def make_plot(var,the_max,f_x,f_y,plt_t,leg_t, fig, axes): axes.axis('off') # lakes - if plot_lakes: large_lakes_albers.plot(color=lake_col, zorder=1) + if plot_lakes: large_lakes_albers.plot(ax=axes,color=lake_col, zorder=1) #From https://github.com/paulgavrikov/parallel-matplotlib-grid From c4949392d66604659cc7da63d6151715e430673e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 14 Jan 2023 22:29:25 +0900 Subject: [PATCH 0488/1472] fixes to parallel plot --- utils/plot_per_GRUparallel.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/utils/plot_per_GRUparallel.py b/utils/plot_per_GRUparallel.py index ee9ce3db1..13cd6e097 100644 --- a/utils/plot_per_GRUparallel.py +++ b/utils/plot_per_GRUparallel.py @@ -199,7 +199,8 @@ def make_default_path(suffix): my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap my_cmap.set_bad(my_cmap.colors[0]) #any nans -def make_plot(var,the_max,f_x,f_y,plt_t,leg_t, fig, axes): +def make_plot(data, fig, axes): + var,the_max,f_x,f_y,plt_t,leg_t = data[0:6] #vmin = (bas_albers[var].where(bas_albers[var]>0)).min() #a = bas_albers[var].where(bas_albers[var]>0) #bas_albers[var] = a.fillna(vmin) @@ -236,7 +237,8 @@ def make_plot(var,the_max,f_x,f_y,plt_t,leg_t, fig, axes): #From https://github.com/paulgavrikov/parallel-matplotlib-grid -def parallel_plot(plot_fn, data1,data2,data3,data4,data5,data6): +# https://towardsdatascience.com/plotting-in-parallel-with-matplotlib-and-python-f7efb3d944de +def parallel_plot(plot_fn, data): if 'compressed' in fig_fil: fig,axes = plt.subplots(3,2,figsize=(35,33)) else: @@ -244,7 +246,7 @@ def parallel_plot(plot_fn, data1,data2,data3,data4,data5,data6): with mp.Pool() as pool: - for ax, rastered in zip(axes.ravel(), pool.map(partial(_parallel_plot_worker, plot_fn=plot_fn), data1,data2,data3,data4,data5,data6)): + for ax, rastered in zip(axes.ravel(), pool.map(partial(_parallel_plot_worker, plot_fn=plot_fn), data)): ax.imshow(rastered) # The following code hides axes @@ -255,12 +257,12 @@ def parallel_plot(plot_fn, data1,data2,data3,data4,data5,data6): plt.subplots_adjust(hspace=0, wspace=0) -def _parallel_plot_worker( data1,data2,data3,data4,data5,data6, plot_fn): +def _parallel_plot_worker( data, plot_fn): fig = plt.figure() matplotlib.font_manager._get_font.cache_clear() # necessary to reduce text corruption artifacts axes = plt.axes() - plot_fn( data1,data2,data3,data4,data5,data6, fig, axes) + plot_fn( data, fig, axes) pil_img = rasterize(fig) plt.close() @@ -277,7 +279,8 @@ def rasterize(fig): return pil_img # Run -parallel_plot(make_plot,plot_vars,maxes,f_x_mat,f_y_mat,plt_titl,leg_titl) +data = np.array(list(zip(plot_vars,maxes,f_x_mat,f_y_mat,plt_titl,leg_titl))) +parallel_plot(make_plot,data) # Save From ee8bb92b35d9dc77377dbd0ee468d997ab7ed7f3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 16 Jan 2023 18:15:48 +0900 Subject: [PATCH 0489/1472] hist plots more than 2 methods --- utils/hist_per_GRU.py | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index b7e9e6921..a070ba34d 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -37,32 +37,34 @@ stat = sys.argv[1] # max or rmse # Simulation statistics file locations -method_name1='sundials_1en6' #keep constant? -method_name2='be1' #maybe make this an argument +method_name=['sundials_1en6', 'be1','be32'] #maybe make this an argument settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} -viz_fil1 = method_name1 + '_hrly_diff_stats_{}_{}.nc' -viz_fil1 = viz_fil1.format(','.join(settings.keys()),','.join(settings.values())) -viz_fil2 = method_name2 + '_hrly_diff_stats_{}_{}.nc' -viz_fil2 = viz_fil2.format(','.join(settings.keys()),','.join(settings.values())) - +viz_fil = method_name.copy() +for i, m in enumerate(method_name): + viz_fil[i] = m + '_hrly_diff_stats_{}_{}.nc' + viz_fil[i] = viz_fil[i].format(','.join(settings.keys()),','.join(settings.values())) # Specify variables of interest plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] -fig_fil = method_name1 + method_name2 + '_hrly_diff_hist_{}_{}__zoom_compressed.png' +fig_fil ='' +for m in method_name: + fig_fil = fig_fil + m +fig_fil = fig_fil + '_hrly_diff_hist_{}_{}__zoom_compressed.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) # possibly want to use these to shrink the axes a bit if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,7e-3] if stat=='max' : maxes = [20,30,3e-4,2,35e-8,0.7] -## Pre-processing, map SUMMA sims to catchment shapes # Get the aggregated statistics of SUMMA simulations -summa1 = xr.open_dataset(viz_dir/viz_fil1) -summa2 = xr.open_dataset(viz_dir/viz_fil2) +summa = {} +for i, m in enumerate(method_name): + summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) + ##Figure @@ -89,11 +91,15 @@ def run_loop(i,var,mx): if 'zoom' in fig_fil: mx = mx else: - mx = max([summa1[var].max(),summa2[var].max()]).values + mx = 0.0 + for m in method_name: + s = summa[m] + mx = max(s[var].max(),mx) # Data - sm = summa1[var].plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=method_name1,linewidth=2.0,range=(0,mx)) - sm = summa2[var].plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=1,label=method_name2,linewidth=2.0,range=(0,mx)) + for m in method_name: + s = summa[m] + s[var].plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=m,linewidth=2.0,range=(0,mx)) if stat == 'rmse': stat_word = ' Hourly RMSE' From d8f91fdb4665a2edefa67417263fa5d2ef5e2038 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 16 Jan 2023 18:27:41 +0900 Subject: [PATCH 0490/1472] remove parallel plotting as still does not work --- utils/check_bit_4_bit_withTol.py | 61 ++++--- utils/plot_per_GRUorig.py | 278 ------------------------------ utils/plot_per_GRUparallel.py | 287 ------------------------------- 3 files changed, 33 insertions(+), 593 deletions(-) delete mode 100644 utils/plot_per_GRUorig.py delete mode 100644 utils/plot_per_GRUparallel.py diff --git a/utils/check_bit_4_bit_withTol.py b/utils/check_bit_4_bit_withTol.py index 1738ca98c..378eae7c6 100644 --- a/utils/check_bit_4_bit_withTol.py +++ b/utils/check_bit_4_bit_withTol.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# python check_bit_4_bit_withTol.py +# python check_bit_4_bit_withTol.py [bench_dir] [test_dir] [tol] import sys import numpy as np @@ -17,9 +17,15 @@ # print(USAGE) # exit(1) -bench_dir = 'summa_be' #sys.argv[1] -test_dir = 'summa-sundials_be' #sys.argv[2] -tol = 0.1 #sys.argv[3] +testing = False +if testing: + bench_dir = 'summa_be' #sys.argv[1] + test_dir = 'summa-sundials_be' #sys.argv[2] + tol = 0.1 #sys.argv[3] +else: + bench_dir = sys.argv[1] + test_dir = sys.argv[2] + tol = sys.argv[3] bench_files = sorted(list(Path(bench_dir).glob('**/*.nc'))) test_files = sorted(list(Path(test_dir).glob('**/*.nc'))) @@ -94,34 +100,33 @@ def compute_diffs(bench_files, test_files): for v in rem(list(combined_max.variables.keys()), list(combined_time.dims.keys())): print(FINAL_MESSAGE.format(v, np.int(max_ind[v].values), combined_max[v].sel(hru = np.int(max_ind[v].values)).values, combined_time[v].sel(hru = np.int(max_ind[v].values)).values)) +if testing: + bench_files = "run_3766_summa_be.nc" + f1 = bench_files + #test_files = ["run_3766_summa_be_conv.nc","run_3766_summa-sundials_beclosed_new.nc","run_3766_summa-sundials_closed_new.nc","run_3766_summa-sundials_closed.nc","run_3766_summa-sundials_enthal_new.nc","run_3766_summa-sundials_enthal_new_noderiv.nc","run_3766_summa-sundials_enthal.nc","run_3766_summa-sundials_beclosed_new_noCp.nc","run_3766_summa-sundials_closed_new_noCp.nc"] + #test_files = ["run_3766_summa-sundials_beclosed_new.nc","run_3766_summa-sundials_beclosed_new_noCp.nc","run_3766_summa-sundials_beclosed_new_noCp_oldJac.nc"] + test_files = ["run_3766_sun_updateCp_withDeriv.nc","run_3766_be_updateCp_withDeriv.nc","run_3766_be_original_withDeriv.nc","run_3766_be_orginal.nc"] + vars = ['averageRoutedRunoff','scalarTotalET','scalarTotalSoilWat','scalarSWE','scalarCanopyWat'] -bench_files = "run_3766_summa_be.nc" -f1 = bench_files + for j, v in enumerate(vars): + for i, f2 in enumerate(test_files): + ds1, ds2 = xr.open_dataset(f1), xr.open_dataset(f2) + m = ds1[v]- ds2[v] + m.plot() -#test_files = ["run_3766_summa_be_conv.nc","run_3766_summa-sundials_beclosed_new.nc","run_3766_summa-sundials_closed_new.nc","run_3766_summa-sundials_closed.nc","run_3766_summa-sundials_enthal_new.nc","run_3766_summa-sundials_enthal_new_noderiv.nc","run_3766_summa-sundials_enthal.nc","run_3766_summa-sundials_beclosed_new_noCp.nc","run_3766_summa-sundials_closed_new_noCp.nc"] -#test_files = ["run_3766_summa-sundials_beclosed_new.nc","run_3766_summa-sundials_beclosed_new_noCp.nc","run_3766_summa-sundials_beclosed_new_noCp_oldJac.nc"] -test_files = ["run_3766_sun_updateCp_withDeriv.nc","run_3766_be_updateCp_withDeriv.nc","run_3766_be_original_withDeriv.nc","run_3766_be_orginal.nc"] -vars = ['averageRoutedRunoff','scalarTotalET','scalarTotalSoilWat','scalarSWE','scalarCanopyWat'] + plt.gca().legend(test_files) + plt.show() -for j, v in enumerate(vars): - for i, f2 in enumerate(test_files): - ds1, ds2 = xr.open_dataset(f1), xr.open_dataset(f2) - m = ds1[v]- ds2[v] - m.plot() - - plt.gca().legend(test_files) - plt.show() + test_files = ["run_3766_nrgWatTermOrderSwitch_commita2c16c8c.nc","run_3766_nrgWatTermOrderSame_commitec1f42b4.nc"] + vars = ['averageRoutedRunoff','scalarTotalET','scalarTotalSoilWat','scalarSWE','scalarCanopyWat'] -test_files = ["run_3766_nrgWatTermOrderSwitch_commita2c16c8c.nc","run_3766_nrgWatTermOrderSame_commitec1f42b4.nc"] -vars = ['averageRoutedRunoff','scalarTotalET','scalarTotalSoilWat','scalarSWE','scalarCanopyWat'] - -for j, v in enumerate(vars): - for i, f2 in enumerate(test_files): - ds1, ds2 = xr.open_dataset(f1), xr.open_dataset(f2) - m = ds1[v]- ds2[v] - m.plot() + for j, v in enumerate(vars): + for i, f2 in enumerate(test_files): + ds1, ds2 = xr.open_dataset(f1), xr.open_dataset(f2) + m = ds1[v]- ds2[v] + m.plot() - plt.gca().legend(test_files) - plt.show() + plt.gca().legend(test_files) + plt.show() diff --git a/utils/plot_per_GRUorig.py b/utils/plot_per_GRUorig.py deleted file mode 100644 index 26efa264a..000000000 --- a/utils/plot_per_GRUorig.py +++ /dev/null @@ -1,278 +0,0 @@ -# written originally by W. Knoben, modified by A. Van Beusekom (2023) - -## Visualize statistics per GRU -## Needs: -# Catchment shapefile with GRU delineation -# SUMMA output statistics - -## Special note -# SUMMA simulations have been preprocessed into single value statistics per model element, using auxiliary scripts in ~/utils -# To improve visualization of large lakes, HydroLAKES lake delineations are plotted on top of the catchment GRUs and river segments. -# Dealing with HydroLAKES inputs is not considered within scope of the workflow and therefore requires some manual downloading and preprocessing of this data for those who wish to reproduce this step. -# The relevant code is easily disabled by switching the plot_lakes = True flag to False. - -# Run: -# python plot_per_GRU.py sundials_1en6 rmse - - -# modules -import sys -import pyproj -import fiona -import matplotlib -import numpy as np -import xarray as xr -import geopandas as gpd -from pathlib import Path -import matplotlib.pyplot as plt -import matplotlib.gridspec as gridspec -import copy - -# The first input argument specifies the run where the files are -method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) -stat = sys.argv[2] # max or rmse - -# Simulation statistics file locations -settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} -viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') -viz_fil = method_name + '_hrly_diff_stats_{}_{}.nc' -viz_fil = viz_fil.format(','.join(settings.keys()),','.join(settings.values())) - -# Specify variables of interest -plot_vars = ['averageRoutedRunoff','wallClockTime','scalarTotalET','scalarSWE','scalarCanopyWat','scalarTotalSoilWat'] -fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed.png' -fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) - -# Get the albers shapes -main = Path('/home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/albers_projection') - -# Plot lakes? -plot_lakes = True -# lakes shapefile WHERE IS THIS -lake_path = Path('C:/Globus endpoint/HydroLAKES/HydroLAKES_polys_v10_shp') -lake_name = 'HydroLAKES_polys_v10_subset_NA.shp' - -## Control file handling -# Store the name of the 'active' file in a variable -controlFile = 'plot_control_NorthAmerica.txt' - -# Function to extract a given setting from the control file -def read_from_control( file, setting ): - - # Open controlFile and ... - with open(file) as contents: - for line in contents: - - # ... find the line with the requested setting - if setting in line and not line.startswith('#'): - break - - # Extract the setting's value - substring = line.split('|',1)[1] # Remove the setting's name (split into 2 based on '|', keep only 2nd part) - substring = substring.split('#',1)[0] # Remove comments, does nothing if no '#' is found - substring = substring.strip() # Remove leading and trailing whitespace, tabs, newlines - - # Return this value - return substring - -# Function to specify a default path -def make_default_path(suffix): - - # Get the root path - rootPath = Path( read_from_control(controlFile,'root_path') ) - - # Get the domain folder - domainName = read_from_control(controlFile,'domain_name') - domainFolder = 'domain_' + domainName - - # Specify the forcing path - defaultPath = rootPath / domainFolder / suffix - - return defaultPath - - -## Catchment shapefile location and variable names -# HM catchment shapefile path & name -hm_catchment_path = read_from_control(controlFile,'catchment_shp_path') -hm_catchment_name = read_from_control(controlFile,'catchment_shp_name') -# Specify default path if needed -if hm_catchment_path == 'default': - hm_catchment_path = make_default_path('shapefiles/catchment') # outputs a Path() -else: - hm_catchment_path = Path(hm_catchment_path) # make sure a user-specified path is a Path() - -# Find the GRU and HRU identifiers -hm_hruid = read_from_control(controlFile,'catchment_shp_hruid') - - -## River network shapefile location and variable names -# Plot rivers? -plot_rivers = False -# River network path & name -river_network_path = read_from_control(controlFile,'river_network_shp_path') -river_network_name = read_from_control(controlFile,'river_network_shp_name') -# Specify default path if needed -if river_network_path == 'default': - river_network_path = make_default_path('shapefiles/river_network') # outputs a Path() -else: - river_network_path = Path(river_network_path) # make sure a user-specified path is a Path() - -# Find the segment ID -seg_id = read_from_control(controlFile,'river_network_shp_segid') - - -## Load all shapefiles and project to Albers Conformal Conic and reproject -# Set the target CRS -acc = 'ESRI:102008' - -# catchment shapefile, first 2 lines throw error so cutting them -#bas = gpd.read_file(hm_catchment_path/hm_catchment_name) -#bas_albers = bas.to_crs(acc) -bas_albers = gpd.read_file(main/'basin.shp') - -# river network shapefile, first 2 lines throw error so cutting them -if plot_rivers: - #riv = gpd.read_file(river_network_path/river_network_name) - #riv_albers = riv.to_crs(acc) - riv_albers = gpd.read_file(main/'river.shp') - -# lakes shapefile, first 2 lines throw error so cutting them -if plot_lakes: - #lakes = gpd.read_file(lake_path/lake_name) - #lak_albers = lakes.to_crs(acc) - lak_albers = gpd.read_file(main/'lakes.shp') - - -# Print the median basin size for curiousity -#print('median area = {} m^2'.format(bas['HRU_area'].median() / 10**6)) -#print('mean area = {} m^2'.format(bas['HRU_area'].mean() / 10**6)) - -#median area = 33.06877343600296 m^2 -#mean area = 40.19396140285971 m^2 - -## Pre-processing, map SUMMA sims to catchment shapes -# Get the aggregated statistics of SUMMA simulations -summa = xr.open_dataset(viz_dir/viz_fil) - -# Match the accummulated values to the correct HRU IDs in the shapefile -hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile -for plot_var in plot_vars: - bas_albers[plot_var] = summa[plot_var].sel(hru=hru_ids_shp.values) - - -# Select lakes of a certain size for plotting -if plot_lakes: - minSize = 1000 # km2 - in_domain = (lak_albers['Country'] == 'Canada') | \ - (lak_albers['Country'] == 'United States of America') | \ - (lak_albers['Country'] == 'Mexico') - out_domain = (lak_albers['Pour_long'] > -80) & (lak_albers['Pour_lat'] > 65) # Exclude Baffin Island - large_lakes_albers = lak_albers.loc[(lak_albers['Lake_area'] > minSize) & in_domain & (~out_domain) ] -# Set the lake color -if plot_lakes: - lake_col = (8/255,81/255,156/255) - -##Figure - -# Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug -if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 25}) -else: - plt.rcParams.update({'font.size': 100}) - -# Flip the evaporation values so that they become positive, not if plotting diffs -#bas_albers['plot_ET'] = bas_albers['scalarTotalET'] * -1 -#bas_albers['plot_ET'] = bas_albers['plot_ET'].where(bas_albers['scalarTotalET'] != -9999, np.nan) - -if 'compressed' in fig_fil: - fig,axs = plt.subplots(3,2,figsize=(35,33)) -else: - fig,axs = plt.subplots(3,2,figsize=(140,133)) - -# colorbar axes -cax1 = fig.add_axes([0.473,0.69,0.02,0.3]) -cax2 = fig.add_axes([0.97 ,0.69,0.02,0.3]) -cax3 = fig.add_axes([0.473,0.36,0.02,0.3]) -cax4 = fig.add_axes([0.97 ,0.36,0.02,0.3]) -cax5 = fig.add_axes([0.473,0.03,0.02,0.3]) -cax6 = fig.add_axes([0.97 ,0.03,0.02,0.3]) - -plt.tight_layout() - -my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap -my_cmap.set_bad(my_cmap.colors[0]) - -if stat == 'rmse': - stat_word = 'RMSE' -if stat == 'max': - stat_word = 'Abs Max' - -# add maps -# SWE -var = 'scalarSWE' -#minval = (bas_albers[var].where(bas_albers[var]>0)).min() -#a = bas_albers[var].where(bas_albers[var]>0) -#bas_albers[var] = a.fillna(minval) -#norm=matplotlib.colors.LogNorm(vmin=minval) -bas_albers.plot(ax=axs[0,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax1, zorder=0) -axs[0,0].set_title('(a) Snow Water Equivalent '+stat_word+ ' Diffs') -axs[0,0].axis('off') -cax1.set_ylabel('scalarSWE $[kg~m^{-2}]$',labelpad=-100) - -# SoilWat -var = 'scalarTotalSoilWat' -bas_albers.plot(ax=axs[0,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax2, zorder=0) -axs[0,1].set_title('(b) Total soil water content '+stat_word+ ' Diffs') -axs[0,1].axis('off') -cax2.set_ylabel('scalarTotalSoilWat $[kg~m^{-2}]$',labelpad=-100) - -# ET -var = 'scalarTotalET' -bas_albers.plot(ax=axs[1,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax3, zorder=0) -axs[1,0].set_title('(c) Total evapotranspiration '+stat_word+ ' Diffs') -axs[1,0].axis('off') -cax3.set_ylabel('scalarTotalET $[kg~m^{-2}~s^{-1}]$',labelpad=-100) - -# CanWat -var = 'scalarCanopyWat' -bas_albers.plot(ax=axs[1,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax4, zorder=0) -axs[1,1].set_title('(d) Total water on the vegetation canopy '+stat_word+ ' Diffs') -axs[1,1].axis('off') -cax4.set_ylabel('scalarCanopyWat $[kg~m^{-2}]$',labelpad=-100) - -# Runoff -var = 'averageRoutedRunoff' -bas_albers.plot(ax=axs[2,0], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax5, zorder=0) -axs[2,0].set_title('(e) Routed runoff '+stat_word+ ' Diffs') -axs[2,0].axis('off') -cax5.set_ylabel('averageRoutedRunoff $[m~s^{-1}]$',labelpad=-100) - -# Clock time -var = 'wallClockTime' -bas_albers.plot(ax=axs[2,1], column=var, edgecolor='none', legend=True, cmap=my_cmap, \ - cax=cax6, zorder=0) -if stat == 'rmse': - stat_word = 'Mean' -if stat == 'max': - stat_word = 'Max' -axs[2,1].set_title('(f) Wall clock time '+stat_word) -axs[2,1].axis('off') -cax6.set_ylabel('wallClockTime( $[s]$',labelpad=-100) - - -# lakes -if plot_lakes: - large_lakes_albers.plot(ax=axs[0,0], color=lake_col, zorder=1) - large_lakes_albers.plot(ax=axs[0,1], color=lake_col, zorder=1) - large_lakes_albers.plot(ax=axs[1,0], color=lake_col, zorder=1) - large_lakes_albers.plot(ax=axs[1,1], color=lake_col, zorder=1) - large_lakes_albers.plot(ax=axs[2,0], color=lake_col, zorder=1) - large_lakes_albers.plot(ax=axs[2,1], color=lake_col, zorder=1) - -# Save -plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=True) diff --git a/utils/plot_per_GRUparallel.py b/utils/plot_per_GRUparallel.py deleted file mode 100644 index 13cd6e097..000000000 --- a/utils/plot_per_GRUparallel.py +++ /dev/null @@ -1,287 +0,0 @@ -# written originally by W. Knoben, modified by A. Van Beusekom (2023) - -## Visualize statistics per GRU -## Needs: -# Catchment shapefile with GRU delineation -# SUMMA output statistics - -## Special note -# SUMMA simulations have been preprocessed into single value statistics per model element, using auxiliary scripts in ~/utils -# To improve visualization of large lakes, HydroLAKES lake delineations are plotted on top of the catchment GRUs and river segments. -# Dealing with HydroLAKES inputs is not considered within scope of the workflow and therefore requires some manual downloading and preprocessing of this data for those who wish to reproduce this step. -# The relevant code is easily disabled by switching the plot_lakes = True flag to False. - -# Run: -# python plot_per_GRU.py sundials_1en6 rmse - - -# modules -import sys -import os -import pyproj -import fiona -import matplotlib -import numpy as np -import xarray as xr -import geopandas as gpd -from pathlib import Path -import matplotlib.pyplot as plt -import copy -# for parallellization -import multiprocessing as mp -from functools import partial -import io -from copy import deepcopy -from PIL import Image - -# The first input argument specifies the run where the files are -method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) -stat = sys.argv[2] # max or rmse - -# Simulation statistics file locations -settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} -viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') -viz_fil = method_name + '_hrly_diff_stats_{}_{}.nc' -viz_fil = viz_fil.format(','.join(settings.keys()),','.join(settings.values())) - -# Specify variables of interest -plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] -plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] -leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] -if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,7e-3] -if stat=='max' : maxes = [20,30,3e-4,2,35e-8,0.7] - -fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed2.png' -fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) - -# Get the albers shapes -main = Path('/home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/albers_projection') - -# Plot lakes? -plot_lakes = True -# lakes shapefile WHERE IS THIS -#lake_path = Path('C:/Globus endpoint/HydroLAKES/HydroLAKES_polys_v10_shp') -#lake_name = 'HydroLAKES_polys_v10_subset_NA.shp' - -## Control file handling -# Store the name of the 'active' file in a variable -controlFile = 'plot_control_NorthAmerica.txt' - -# Function to extract a given setting from the control file -def read_from_control( file, setting ): - - # Open controlFile and ... - with open(file) as contents: - for line in contents: - - # ... find the line with the requested setting - if setting in line and not line.startswith('#'): - break - - # Extract the setting's value - substring = line.split('|',1)[1] # Remove the setting's name (split into 2 based on '|', keep only 2nd part) - substring = substring.split('#',1)[0] # Remove comments, does nothing if no '#' is found - substring = substring.strip() # Remove leading and trailing whitespace, tabs, newlines - - # Return this value - return substring - -# Function to specify a default path -def make_default_path(suffix): - - # Get the root path - rootPath = Path( read_from_control(controlFile,'root_path') ) - - # Get the domain folder - domainName = read_from_control(controlFile,'domain_name') - domainFolder = 'domain_' + domainName - - # Specify the forcing path - defaultPath = rootPath / domainFolder / suffix - - return defaultPath - - -## Catchment shapefile location and variable names -# HM catchment shapefile path & name -hm_catchment_path = read_from_control(controlFile,'catchment_shp_path') -hm_catchment_name = read_from_control(controlFile,'catchment_shp_name') -# Specify default path if needed -if hm_catchment_path == 'default': - hm_catchment_path = make_default_path('shapefiles/catchment') # outputs a Path() -else: - hm_catchment_path = Path(hm_catchment_path) # make sure a user-specified path is a Path() - -# Find the GRU and HRU identifiers -hm_hruid = read_from_control(controlFile,'catchment_shp_hruid') - - -## River network shapefile location and variable names -# Plot rivers? -plot_rivers = False -# River network path & name -river_network_path = read_from_control(controlFile,'river_network_shp_path') -river_network_name = read_from_control(controlFile,'river_network_shp_name') -# Specify default path if needed -if river_network_path == 'default': - river_network_path = make_default_path('shapefiles/river_network') # outputs a Path() -else: - river_network_path = Path(river_network_path) # make sure a user-specified path is a Path() - -# Find the segment ID -seg_id = read_from_control(controlFile,'river_network_shp_segid') - - -## Load all shapefiles and project to Albers Conformal Conic and reproject -# Set the target CRS -acc = 'ESRI:102008' - -# catchment shapefile, first 2 lines throw error so cutting them -#bas = gpd.read_file(hm_catchment_path/hm_catchment_name) -#bas_albers = bas.to_crs(acc) -bas_albers = gpd.read_file(main/'basin.shp') - -# river network shapefile, first 2 lines throw error so cutting them -if plot_rivers: - #riv = gpd.read_file(river_network_path/river_network_name) - #riv_albers = riv.to_crs(acc) - riv_albers = gpd.read_file(main/'river.shp') - -# lakes shapefile, first 2 lines throw error so cutting them -if plot_lakes: - #lakes = gpd.read_file(lake_path/lake_name) - #lak_albers = lakes.to_crs(acc) - lak_albers = gpd.read_file(main/'lakes.shp') - - - -## Pre-processing, map SUMMA sims to catchment shapes -# Get the aggregated statistics of SUMMA simulations -summa = xr.open_dataset(viz_dir/viz_fil) - -# Match the accummulated values to the correct HRU IDs in the shapefile -hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile -for plot_var in plot_vars: - bas_albers[plot_var] = summa[plot_var].sel(hru=hru_ids_shp.values) - -# Select lakes of a certain size for plotting -if plot_lakes: - minSize = 1000 # km2 - in_domain = (lak_albers['Country'] == 'Canada') | \ - (lak_albers['Country'] == 'United States of America') | \ - (lak_albers['Country'] == 'Mexico') - out_domain = (lak_albers['Pour_long'] > -80) & (lak_albers['Pour_lat'] > 65) # Exclude Baffin Island - large_lakes_albers = lak_albers.loc[(lak_albers['Lake_area'] > minSize) & in_domain & (~out_domain) ] -# Set the lake color -if plot_lakes: - lake_col = (8/255,81/255,156/255) - -##Figure - -# Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug -if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 25}) -else: - plt.rcParams.update({'font.size': 100}) - -# Flip the evaporation values so that they become positive, not if plotting diffs -#bas_albers['plot_ET'] = bas_albers['scalarTotalET'] * -1 -#bas_albers['plot_ET'] = bas_albers['plot_ET'].where(bas_albers['scalarTotalET'] != -9999, np.nan) - -plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines - -# colorbar axes -f_x_mat = [0.473,0.97,0.473,0.97,0.473,0.97] -f_y_mat = [0.69,0.69,0.36,0.36,0.03,0.03] - -plt.tight_layout() - -my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap -my_cmap.set_bad(my_cmap.colors[0]) #any nans - -def make_plot(data, fig, axes): - var,the_max,f_x,f_y,plt_t,leg_t = data[0:6] - #vmin = (bas_albers[var].where(bas_albers[var]>0)).min() - #a = bas_albers[var].where(bas_albers[var]>0) - #bas_albers[var] = a.fillna(vmin) - #vmin,vmax = bas_albers[var].min(), bas_albers[var].max() - vmin,vmax = 0, the_max - norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) - #norm=matplotlib.colors.Normalize(vmin=vmin, vmax=vmax) - - # Data - bas_albers.plot(ax=axes,column=var, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) - - # Custom colorbar - cax = fig.add_axes([f_x,f_y,0.02,0.3]) - sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) - sm._A = [] - cbr = fig.colorbar(sm, cax=cax, extend='max') - cbr.ax.set_title('[{}]'.format(leg_t)) - - if stat == 'rmse': - stat_word = ' Hourly RMSE' - if stat == 'max': - stat_word = ' Hourly max abs error' - - # wall Clock doesn't do difference - if var == 'wallClockTime': - if stat == 'rmse': stat_word = ' Hourly mean' - if stat == 'max': stat_word = ' Hourly max' - - axes.set_title(plt_t+ stat_word) - axes.axis('off') - - # lakes - if plot_lakes: large_lakes_albers.plot(ax=axes,color=lake_col, zorder=1) - - -#From https://github.com/paulgavrikov/parallel-matplotlib-grid -# https://towardsdatascience.com/plotting-in-parallel-with-matplotlib-and-python-f7efb3d944de -def parallel_plot(plot_fn, data): - if 'compressed' in fig_fil: - fig,axes = plt.subplots(3,2,figsize=(35,33)) - else: - fig,axes = plt.subplots(3,2,figsize=(140,133)) - - - with mp.Pool() as pool: - for ax, rastered in zip(axes.ravel(), pool.map(partial(_parallel_plot_worker, plot_fn=plot_fn), data)): - ax.imshow(rastered) - - # The following code hides axes - ax.get_xaxis().set_ticks([]) - ax.get_yaxis().set_ticks([]) - for spine in ax.spines.values(): - spine.set_visible(False) - - plt.subplots_adjust(hspace=0, wspace=0) - -def _parallel_plot_worker( data, plot_fn): - fig = plt.figure() - matplotlib.font_manager._get_font.cache_clear() # necessary to reduce text corruption artifacts - axes = plt.axes() - - plot_fn( data, fig, axes) - pil_img = rasterize(fig) - plt.close() - - return pil_img - - -def rasterize(fig): - buf = io.BytesIO() - fig.savefig(buf, format='png', bbox_inches='tight') - buf.seek(0) - pil_img = deepcopy(Image.open(buf)) - buf.close() - - return pil_img - -# Run -data = np.array(list(zip(plot_vars,maxes,f_x_mat,f_y_mat,plt_titl,leg_titl))) -parallel_plot(make_plot,data) - - -# Save -plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=True) From ec004476366fca2a1a4959061645a2e5661614ff Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 16 Jan 2023 22:38:51 +0900 Subject: [PATCH 0491/1472] bound adjustment and memory --- utils/hist_per_GRU.py | 6 +++--- utils/plot_per_GRU.py | 4 ++-- utils/process_concat.sh | 2 +- utils/process_stat.sh | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index a070ba34d..1f6c349e1 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -37,7 +37,7 @@ stat = sys.argv[1] # max or rmse # Simulation statistics file locations -method_name=['sundials_1en6', 'be1','be32'] #maybe make this an argument +method_name=['sundials_1en6','be32','be1'] #maybe make this an argument settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} viz_fil = method_name.copy() @@ -56,8 +56,8 @@ fig_fil = fig_fil + '_hrly_diff_hist_{}_{}__zoom_compressed.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) # possibly want to use these to shrink the axes a bit -if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,7e-3] -if stat=='max' : maxes = [20,30,3e-4,2,35e-8,0.7] +if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,13e-3] +if stat=='max' : maxes = [20,30,3e-4,2,4e-7,0.7] # Get the aggregated statistics of SUMMA simulations diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 5fbf088fc..6ea5246aa 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -42,8 +42,8 @@ plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] -if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,7e-3] -if stat=='max' : maxes = [20,30,3e-4,2,35e-8,0.7] +if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,13e-3] +if stat=='max' : maxes = [20,30,3e-4,2,4e-7,0.7] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed.png' fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) diff --git a/utils/process_concat.sh b/utils/process_concat.sh index ef4141963..c27aa669c 100644 --- a/utils/process_concat.sh +++ b/utils/process_concat.sh @@ -1,7 +1,7 @@ #!/bin/bash #SBATCH --ntasks-per-node=1 #SBATCH --cpus-per-task=32 -#SBATCH --mem-per-cpu=6GB +#SBATCH --mem-per-cpu=4GB #SBATCH --time=1-00:00 #SBATCH --job-name=CONCAT #SBATCH --mail-user=gwu479@usask.ca diff --git a/utils/process_stat.sh b/utils/process_stat.sh index 31f79905a..5f8abbbc7 100644 --- a/utils/process_stat.sh +++ b/utils/process_stat.sh @@ -1,7 +1,7 @@ #!/bin/bash #SBATCH --ntasks-per-node=1 #SBATCH --cpus-per-task=32 -#SBATCH --mem-per-cpu=6GB +#SBATCH --mem-per-cpu=4GB #SBATCH --time=1-00:00 #SBATCH --job-name=STAT #SBATCH --mail-user=gwu479@usask.ca From 4b517192c332975f61b72fcc07faabb912b21b19 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 18 Jan 2023 00:14:53 +0900 Subject: [PATCH 0492/1472] add scatter plot and change how computing statistics --- utils/hist_per_GRU.py | 62 ++++++++-------- utils/plot_per_GRU.py | 41 +++++------ utils/process_hist.sh | 2 +- utils/process_plot.sh | 6 +- utils/process_scat.sh | 23 ++++++ utils/process_stat.sh | 5 +- utils/scat_per_GRU.py | 115 ++++++++++++++++++++++++++++++ utils/timeseries_to_statistics.py | 70 ++++++++++-------- 8 files changed, 239 insertions(+), 85 deletions(-) create mode 100644 utils/process_scat.sh create mode 100644 utils/scat_per_GRU.py diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 1f6c349e1..187309779 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -1,18 +1,14 @@ -# written originally by W. Knoben, modified by A. Van Beusekom (2023) +# written by A. Van Beusekom (2023) ## Visualize statistics per GRU ## Needs: -# Catchment shapefile with GRU delineation # SUMMA output statistics ## Special note # SUMMA simulations have been preprocessed into single value statistics per model element, using auxiliary scripts in ~/utils -# To improve visualization of large lakes, HydroLAKES lake delineations are plotted on top of the catchment GRUs and river segments. -# Dealing with HydroLAKES inputs is not considered within scope of the workflow and therefore requires some manual downloading and preprocessing of this data for those who wish to reproduce this step. -# The relevant code is easily disabled by switching the plot_lakes = True flag to False. - # Run: -# python hist_per_GRU.py rmse +# python hist_per_GRU.py [stat] +# where stat is rmse or maxe # modules @@ -27,45 +23,40 @@ viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') -testing = False +testing = True if testing: + stat = 'rmse' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - stat = "max" + method_name=['sundials_1en6','be1'] #maybe make this an argument else: import sys # The first input argument specifies the run where the files are stat = sys.argv[1] # max or rmse - -# Simulation statistics file locations -method_name=['sundials_1en6','be32','be1'] #maybe make this an argument + method_name=['sundials_1en6','be32','be1'] #maybe make this an argument -settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} +# Simulation statistics file locations +settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] viz_fil = method_name.copy() for i, m in enumerate(method_name): - viz_fil[i] = m + '_hrly_diff_stats_{}_{}.nc' - viz_fil[i] = viz_fil[i].format(','.join(settings.keys()),','.join(settings.values())) + viz_fil[i] = m + '_hrly_diff_stats_{}.nc' + viz_fil[i] = viz_fil[i].format(','.join(settings)) # Specify variables of interest plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] -fig_fil ='' -for m in method_name: - fig_fil = fig_fil + m -fig_fil = fig_fil + '_hrly_diff_hist_{}_{}__zoom_compressed.png' -fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) +fig_fil = '{}_hrly_diff_hist_{}_{}_zoom_compressed.png' +fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) # possibly want to use these to shrink the axes a bit if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,13e-3] -if stat=='max' : maxes = [20,30,3e-4,2,4e-7,0.7] - +if stat=='maxe' : maxes = [20,30,3e-4,2,4e-7,0.7] # Get the aggregated statistics of SUMMA simulations summa = {} for i, m in enumerate(method_name): summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) - ##Figure # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug @@ -88,28 +79,33 @@ def run_loop(i,var,mx): r = i//2 c = i-r*2 num_bins = 200 + stat0 = stat + if var == 'wallClockTime': + if stat == 'rmse': stat0 = 'mean' + if stat == 'maxe': stat0 = 'amax' + if 'zoom' in fig_fil: mx = mx else: mx = 0.0 for m in method_name: - s = summa[m] - mx = max(s[var].max(),mx) + s = summa[m][var].sel(stat=stat0) + if stat == 'maxe': s = np.fabs(s) # make absolute value norm + mx = max(s.max(),mx) # Data for m in method_name: - s = summa[m] - s[var].plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=m,linewidth=2.0,range=(0,mx)) + s = summa[m][var].sel(stat=stat0) + if stat == 'maxe': s = np.fabs(s) # make absolute value norm + s.plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=m,linewidth=2.0,range=(0,mx)) - if stat == 'rmse': - stat_word = ' Hourly RMSE' - if stat == 'max': - stat_word = ' Hourly max abs error' + if stat == 'rmse': stat_word = ' Hourly RMSE' + if stat == 'maxe': stat_word = ' Hourly max abs error' # wall Clock doesn't do difference if var == 'wallClockTime': if stat == 'rmse': stat_word = ' Hourly mean' - if stat == 'max': stat_word = ' Hourly max' + if stat == 'maxe': stat_word = ' Hourly max' axs[r,c].legend() axs[r,c].set_title(plt_titl[i] + stat_word) @@ -121,4 +117,4 @@ def run_loop(i,var,mx): run_loop(i,var,mx) # Save -plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=True) +plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 6ea5246aa..64919ecca 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -12,7 +12,8 @@ # The relevant code is easily disabled by switching the plot_lakes = True flag to False. # Run: -# python plot_per_GRU.py sundials_1en6 rmse +# python plot_per_GRU.py sundials_1en6 [stat] +# where stat is rmse or maxe # modules @@ -30,23 +31,23 @@ # The first input argument specifies the run where the files are method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) -stat = sys.argv[2] # max or rmse +stat = sys.argv[2] # maxe or rmse # Simulation statistics file locations -settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} +settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') -viz_fil = method_name + '_hrly_diff_stats_{}_{}.nc' -viz_fil = viz_fil.format(','.join(settings.keys()),','.join(settings.values())) +viz_fil = method_name + '_hrly_diff_stats_{}.nc' +viz_fil = viz_fil.format(','.join(settings)) # Specify variables of interest -plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] +plot_vars = settings plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,13e-3] -if stat=='max' : maxes = [20,30,3e-4,2,4e-7,0.7] +if stat=='maxe': maxes = [20,30,3e-4,2,4e-7,0.7] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed.png' -fig_fil = fig_fil.format(','.join(settings.keys()),','.join(settings.values())) +fig_fil = fig_fil.format(','.join(settings),stat) # Get the albers shapes main = Path('/home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/albers_projection') @@ -156,7 +157,13 @@ def make_default_path(suffix): # Match the accummulated values to the correct HRU IDs in the shapefile hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile for plot_var in plot_vars: - bas_albers[plot_var] = summa[plot_var].sel(hru=hru_ids_shp.values) + stat0 = stat + if plot_var == 'wallClockTime': + if stat == 'rmse': stat0 = 'mean' + if stat == 'maxe': stat0 = 'amax' + s = summa[plot_var].sel(stat=stat0) + if stat == 'maxe': s = np.fabs(s) # make absolute value norm, max is not not all positive + bas_albers[plot_var] = s.sel(hru=hru_ids_shp.values) # Select lakes of a certain size for plotting if plot_lakes: @@ -196,12 +203,8 @@ def make_default_path(suffix): plt.tight_layout() my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap -my_cmap.set_bad(my_cmap.colors[0]) #any nans - -if stat == 'rmse': - stat_word = ' Hourly RMSE' -if stat == 'max': - stat_word = ' Hourly max abs error' +#my_cmap.set_bad(my_cmap.colors[0]) #any nans, maybe should be white? +my_cmap.set_bad(color='white') #nan color white def run_loop(i,var,the_max,f_x,f_y): #vmin = (bas_albers[var].where(bas_albers[var]>0)).min() @@ -224,15 +227,13 @@ def run_loop(i,var,the_max,f_x,f_y): cbr = fig.colorbar(sm, cax=cax, extend='max') cbr.ax.set_title('[{}]'.format(leg_titl[i])) - if stat == 'rmse': - stat_word = ' Hourly RMSE' - if stat == 'max': - stat_word = ' Hourly max abs error' + if stat == 'rmse': stat_word = ' Hourly RMSE' + if stat == 'maxe': stat_word = ' Hourly max abs error' # wall Clock doesn't do difference if var == 'wallClockTime': if stat == 'rmse': stat_word = ' Hourly mean' - if stat == 'max': stat_word = ' Hourly max' + if stat == 'maxe': stat_word = ' Hourly max' axs[r,c].set_title(plt_titl[i] + stat_word) axs[r,c].axis('off') diff --git a/utils/process_hist.sh b/utils/process_hist.sh index 58d82fccf..8793c8f98 100644 --- a/utils/process_hist.sh +++ b/utils/process_hist.sh @@ -20,4 +20,4 @@ pip install --no-index xarray pip install --no-index netCDF4 python hist_per_GRU.py rmse -python hist_per_GRU.py max +python hist_per_GRU.py maxe diff --git a/utils/process_plot.sh b/utils/process_plot.sh index 9b43be0b6..6d7ec0fce 100644 --- a/utils/process_plot.sh +++ b/utils/process_plot.sh @@ -22,4 +22,8 @@ pip install --no-index xarray pip install --no-index netCDF4 python plot_per_GRU.py sundials_1en6 rmse -python plot_per_GRU.py sundials_1en6 max \ No newline at end of file +python plot_per_GRU.py sundials_1en6 maxe +python plot_per_GRU.py be1 rmse +python plot_per_GRU.py be1 maxe +python plot_per_GRU.py be32 rmse +python plot_per_GRU.py be32 maxe \ No newline at end of file diff --git a/utils/process_scat.sh b/utils/process_scat.sh new file mode 100644 index 000000000..1bb018b70 --- /dev/null +++ b/utils/process_scat.sh @@ -0,0 +1,23 @@ +#!/bin/bash +#SBATCH --ntasks-per-node=1 +#SBATCH --cpus-per-task=1 +#SBATCH --mem-per-cpu=2GB +#SBATCH --time=1-00:00 +#SBATCH --job-name=SCAT +#SBATCH --mail-user=gwu479@usask.ca +#SBATCH --mail-type=ALL +#SBATCH --account=rpp-kshook +#SBATCH --output=/home/avanb/TestScripts/output/slurm-%A_%a.out + +module load gcc/9.3.0 +module load geo-stack +virtualenv --no-download $SLURM_TMPDIR/env +source $SLURM_TMPDIR/env/bin/activate +pip install --no-index --upgrade pip +pip install --no-index h5netcdf +pip install --no-index h5py +pip install --no-index xarray +pip install --no-index netCDF4 + +python scat_per_GRU.py rmse +python scat_per_GRU.py maxe diff --git a/utils/process_stat.sh b/utils/process_stat.sh index 5f8abbbc7..965096a03 100644 --- a/utils/process_stat.sh +++ b/utils/process_stat.sh @@ -19,5 +19,6 @@ pip install --no-index h5py pip install --no-index xarray pip install --no-index netCDF4 -python timeseries_to_statistics.py sundials_1en6 rmse -python timeseries_to_statistics.py sundials_1en6 max +python timeseries_to_statistics.py sundials_1en6 +python timeseries_to_statistics.py be1 +python timeseries_to_statistics.py be32 diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py new file mode 100644 index 000000000..5be0f7be4 --- /dev/null +++ b/utils/scat_per_GRU.py @@ -0,0 +1,115 @@ +# written by A. Van Beusekom (2023) + +## Visualize statistics per GRU +## Needs: +# SUMMA output statistics + +## Special note +# SUMMA simulations have been preprocessed into single value statistics per model element, using auxiliary scripts in ~/utils +# Run: +# python scat_per_GRU.py rmse +# where stat is rmse or maxe + +# modules +import os +import matplotlib +import numpy as np +import xarray as xr +from pathlib import Path +import matplotlib.mlab as mlab +import matplotlib.pyplot as plt +import copy + +viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') + +testing = True +if testing: + stat = 'rmse' + viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') + method_name=['sundials_1en6','be1'] #maybe make this an argument +else: + import sys + # The first input argument specifies the run where the files are + stat = sys.argv[1] # max or rmse + method_name=['sundials_1en6','be32','be1'] #maybe make this an argument + +# Simulation statistics file locations +settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] +viz_fil = method_name.copy() +for i, m in enumerate(method_name): + viz_fil[i] = m + '_hrly_diff_stats_{}.nc' + viz_fil[i] = viz_fil[i].format(','.join(settings)) + +# Specify variables of interest +plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] +plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] +leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] + +fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' +fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) +# possibly want to use these to shrink the axes a bit +if stat=='rmse': + maxes = [2,15,8e-6,0.08,7e-9,100] + maxes0 = [2,15,8e-6,0.08,7e-9,13e-3] +if stat=='maxe' : + maxes = [20,30,3e-4,2,4e-7,5000] + maxes0 = [100,2000,-1e-3,20,2e-5,0.7] + +# Get the aggregated statistics of SUMMA simulations +summa = {} +for i, m in enumerate(method_name): + summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) + +##Figure + +# Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug +if 'compressed' in fig_fil: + plt.rcParams.update({'font.size': 25}) +else: + plt.rcParams.update({'font.size': 100}) + +# Flip the evaporation values so that they become positive, not if plotting diffs +#bas_albers['plot_ET'] = bas_albers['scalarTotalET'] * -1 +#bas_albers['plot_ET'] = bas_albers['plot_ET'].where(bas_albers['scalarTotalET'] != -9999, np.nan) + +if 'compressed' in fig_fil: + fig,axs = plt.subplots(3,2,figsize=(35,33)) +else: + fig,axs = plt.subplots(3,2,figsize=(140,133)) + + +def run_loop(i,var,mx,mx0): + r = i//2 + c = i-r*2 + if stat == 'rmse': stat0 = 'mean' + if stat == 'maxe': stat0 = 'amax' + + # Data + for m in method_name: + s = summa[m][var].sel(stat=[stat,stat0]) + if stat == 'maxe': s.loc[dict(stat='maxe')] = np.fabs(s.loc[dict(stat='maxe')]) # make absolute value norm + axs[r,c].scatter(x=s.sel(stat=stat).values,y=s.sel(stat=stat0).values,zorder=0,label=m) + if 'zoom' in fig_fil: + axs[r,c].set_xlim(0,mx) + if mx0<0: axs[r,c].set_ylim(mx0,0) + if mx0>0: axs[r,c].set_ylim(0,mx0) + + if stat == 'rmse': + stat_word = 'Hourly RMSE ' + stat0_word ='Hourly mean ' + if stat == 'maxe': + stat_word = ' Hourly max abs error ' + stat0_word =' Hourly max ' + + axs[r,c].legend() + axs[r,c].set_title(plt_titl[i]) + axs[r,c].set_xlabel(stat_word + '[{}]'.format(leg_titl[i])) + axs[r,c].set_ylabel(stat0_word + '[{}]'.format(leg_titl[i])) + + +for i,(var,mx,mx0) in enumerate(zip(plot_vars,maxes,maxes0)): + run_loop(i,var,mx,mx0) + +# Save +plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=True) + diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 4d829da75..72c37eaf1 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -21,27 +21,25 @@ top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' des_dir = top_fold + 'statistics' -testing = False +testing = True if testing: top_fold = '/Users/amedin/Research/USask/test_py/' des_dir = top_fold + 'statistics' - method_name = 'be1' - stat = "max" + method_name = 'sundials_1en6' else: import multiprocessing as mp import sys # The first input argument specifies the run where the files are method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) - stat = sys.argv[2] # max or rmse src_dir = top_fold + 'summa-' + method_name ben_dir = top_fold + 'summa-' + bench_name src_pat = 'run1_G*_timestep.nc' -des_fil = method_name + '_hrly_diff_stats_{}_{}_{}.nc' -settings= {'averageRoutedRunoff': stat, 'wallClockTime': stat, 'scalarTotalET': stat, 'scalarSWE': stat, 'scalarCanopyWat': stat, 'scalarTotalSoilWat': stat} +des_fil = method_name + '_hrly_diff_stats_{}_{}.nc' +settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] -viz_fil = method_name + '_hrly_diff_stats_{}_{}.nc' -viz_fil = viz_fil.format(','.join(settings.keys()),','.join(settings.values())) +viz_fil = method_name + '_hrly_diff_stats_{}.nc' +viz_fil = viz_fil.format(','.join(settings)) # Make sure we're dealing with the right kind of inputs src_dir = Path(src_dir) @@ -69,32 +67,48 @@ def run_loop(file,bench): # open file dat,ben = xr.open_dataset(file), xr.open_dataset(bench) - diff = (np.fabs(dat - ben)) - for var,stat0 in settings.items(): - # Select the case, redo if not max (slighly inefficient) - if stat0 == 'rmse':diff[var] = np.square(diff[var]) #2-norm - # wall clock don't do difference - # sometimes wall clock only gives -9999 the whole run, make these nan and plot as lowest value (somewhat rare) - if var == 'wallClockTime': - diff[var] = dat[var] - diff[var] = diff[var].where(diff[var]>0) - + # sometimes gives -9999 the whole run (non-compute), make these nan and plot as lowest value 0 in geographic + dat = dat.where(dat!=-9999) + ben = ben.where(ben!=-9999) + # some weird negative values in runoff if not routed + dat['averageRoutedRunoff'] = dat['averageRoutedRunoff'].where(dat['averageRoutedRunoff']>=0) + ben['averageRoutedRunoff'] = ben['averageRoutedRunoff'].where(ben['averageRoutedRunoff']>=0) + #dat['averageRoutedRunoff'] = dat['averageRoutedRunoff'].fillna(0) + #ben['averageRoutedRunoff'] = ben['averageRoutedRunoff'].fillna(0) + diff = dat - ben + # get rid of gru dimension, assuming they are same as the often are (everything now as hruId) diff = diff.drop_vars(['hruId','gruId']) m = diff.drop_dims('hru') m = m.rename({'gru': 'hru'}) diff = diff.drop_dims('gru') diff = xr.merge([diff,m]) - - # compute the requested statistics - for var,stat0 in settings.items(): - # Select the case - if stat0 == 'rmse':new = ((diff[var].mean(dim='time'))**(1/2)) #RMSE SHOULD THIS BE NORMALIZED? colorbar will normalize - if stat0 == 'max': new = diff[var].max(dim='time') #same regardless - # wall clock don't do difference - if var == 'wallClockTime' and stat0 == 'rmse': new = diff[var].mean(dim='time') - - new.to_netcdf(des_dir / des_fil.format(stat,var,subset)) + + dat = dat.drop_vars(['hruId','gruId']) + m = dat.drop_dims('hru') + m = m.rename({'gru': 'hru'}) + dat = dat.drop_dims('gru') + dat = xr.merge([dat,m]) + + for var in settings: + mean = dat[var].mean(dim='time') + mean = mean.expand_dims("stat").assign_coords(stat=("stat",["mean"])) + + na_mx = np.fabs(dat[var]).max()+1 + amx = np.fabs(dat[var].fillna(na_mx)).argmax(dim=['time']) + amax = dat[var].isel(amx).drop_vars('time') + amax = amax.expand_dims("stat").assign_coords(stat=("stat",["amax"])) + + rmse = (np.square(diff[var]).mean(dim='time'))**(1/2) #RMSE SHOULD THIS BE NORMALIZED? colorbar will normalize + rmse = rmse.expand_dims("stat").assign_coords(stat=("stat",["rmse"])) + + na_mx = np.fabs(diff[var]).max()+1 + amx = np.fabs(diff[var].fillna(na_mx)).argmax(dim=['time']) + maxe = diff[var].isel(amx).drop_vars('time') + maxe = maxe.expand_dims("stat").assign_coords(stat=("stat",["maxe"])) + + new = xr.merge([mean,amax,rmse,maxe]) + new.to_netcdf(des_dir / des_fil.format(var,subset)) print("wrote output: %s" % (top_fold + 'statistics/' +subset)) From f2bf6c9ed9c16a46d720fc3823b0380c06baaaaa Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 18 Jan 2023 10:44:46 +0900 Subject: [PATCH 0493/1472] Turn off testing, comments for averageRoutedRunoff with large negative values? --- build/source/engine/qTimeDelay.f90 | 3 +++ utils/hist_per_GRU.py | 2 +- utils/scat_per_GRU.py | 2 +- utils/timeseries_to_statistics.py | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/build/source/engine/qTimeDelay.f90 b/build/source/engine/qTimeDelay.f90 index ba5a5aa16..84dfeb47b 100644 --- a/build/source/engine/qTimeDelay.f90 +++ b/build/source/engine/qTimeDelay.f90 @@ -59,6 +59,7 @@ subroutine qOverland(& integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! internal + real(rkind),parameter :: valueMissing=-9999._rkind ! missing value integer(i4b) :: nTDH ! number of points in the time-delay histogram integer(i4b) :: iFuture ! index in time delay histogram ! initialize error control @@ -91,6 +92,8 @@ subroutine qOverland(& case default; err=20; message=trim(message)//'cannot find option for sub-grid routing'; return end select ! (select option for sub-grid routing) + !if (averageRoutedRunoff < 0._rkind) averageRoutedRunoff = valueMissing + end subroutine qOverland diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 187309779..1b9553ceb 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -23,7 +23,7 @@ viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') -testing = True +testing = False if testing: stat = 'rmse' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index 5be0f7be4..4a957ce9e 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -22,7 +22,7 @@ viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') -testing = True +testing = False if testing: stat = 'rmse' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 72c37eaf1..cee4fb307 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -21,7 +21,7 @@ top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' des_dir = top_fold + 'statistics' -testing = True +testing = False if testing: top_fold = '/Users/amedin/Research/USask/test_py/' des_dir = top_fold + 'statistics' From b110d58ae1f1195010ec5f8f90a0800b14bd9d1a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 18 Jan 2023 11:55:26 +0900 Subject: [PATCH 0494/1472] Fixing colorbar, note that with extended colorbar the exponential will be written over as a bug in matplotlib 2022 --- utils/plot_per_GRU.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 64919ecca..ae84807d7 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -198,7 +198,7 @@ def make_default_path(suffix): # colorbar axes f_x_mat = [0.473,0.97,0.473,0.97,0.473,0.97] -f_y_mat = [0.69,0.69,0.36,0.36,0.03,0.03] +f_y_mat = [0.73,0.73,0.40,0.40,0.067,0.067] plt.tight_layout() @@ -221,11 +221,12 @@ def run_loop(i,var,the_max,f_x,f_y): bas_albers.plot(ax=axs[r,c], column=var, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) # Custom colorbar - cax = fig.add_axes([f_x,f_y,0.02,0.3]) + cax = fig.add_axes([f_x,f_y,0.02,0.2]) sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) sm._A = [] cbr = fig.colorbar(sm, cax=cax, extend='max') - cbr.ax.set_title('[{}]'.format(leg_titl[i])) + cbr.ax.set_ylabel('[{}]'.format(leg_titl[i]), rotation=270) + cbr.ax.yaxis.set_offset_position('right') if stat == 'rmse': stat_word = ' Hourly RMSE' if stat == 'maxe': stat_word = ' Hourly max abs error' From 4a8dce33dbee6def2752e00766671900f8b02234 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 18 Jan 2023 17:02:02 +0900 Subject: [PATCH 0495/1472] fiona needs to be imported after xarray or screws it up --- build/source/engine/qTimeDelay.f90 | 3 +++ utils/hist_per_GRU.py | 2 -- utils/plot_per_GRU.py | 9 +++------ utils/scat_per_GRU.py | 5 ++--- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/build/source/engine/qTimeDelay.f90 b/build/source/engine/qTimeDelay.f90 index 84dfeb47b..241d99a2e 100644 --- a/build/source/engine/qTimeDelay.f90 +++ b/build/source/engine/qTimeDelay.f90 @@ -92,6 +92,9 @@ subroutine qOverland(& case default; err=20; message=trim(message)//'cannot find option for sub-grid routing'; return end select ! (select option for sub-grid routing) + ! For open water SUMMA doesn't run any calculations + ! the values for any output variables in the netCDF will stay at the value at which they were initialized, which may be a large negative + ! Coast may be similarly large and negative !if (averageRoutedRunoff < 0._rkind) averageRoutedRunoff = valueMissing diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 1b9553ceb..ca0dc24b2 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -10,14 +10,12 @@ # python hist_per_GRU.py [stat] # where stat is rmse or maxe - # modules import os import matplotlib import numpy as np import xarray as xr from pathlib import Path -import matplotlib.mlab as mlab import matplotlib.pyplot as plt import copy diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index ae84807d7..1480b9921 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -19,15 +19,15 @@ # modules import sys import os -import pyproj -import fiona import matplotlib import numpy as np import xarray as xr -import geopandas as gpd from pathlib import Path import matplotlib.pyplot as plt import copy +import pyproj +import fiona +import geopandas as gpd # The first input argument specifies the run where the files are method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) @@ -148,8 +148,6 @@ def make_default_path(suffix): #lak_albers = lakes.to_crs(acc) lak_albers = gpd.read_file(main/'lakes.shp') - - ## Pre-processing, map SUMMA sims to catchment shapes # Get the aggregated statistics of SUMMA simulations summa = xr.open_dataset(viz_dir/viz_fil) @@ -242,7 +240,6 @@ def run_loop(i,var,the_max,f_x,f_y): # lakes if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) - for i,(var,the_max,f_x,f_y) in enumerate(zip(plot_vars,maxes,f_x_mat,f_y_mat)): run_loop(i,var,the_max,f_x,f_y) diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index 4a957ce9e..b3a1c73b4 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -16,7 +16,6 @@ import numpy as np import xarray as xr from pathlib import Path -import matplotlib.mlab as mlab import matplotlib.pyplot as plt import copy @@ -88,7 +87,7 @@ def run_loop(i,var,mx,mx0): for m in method_name: s = summa[m][var].sel(stat=[stat,stat0]) if stat == 'maxe': s.loc[dict(stat='maxe')] = np.fabs(s.loc[dict(stat='maxe')]) # make absolute value norm - axs[r,c].scatter(x=s.sel(stat=stat).values,y=s.sel(stat=stat0).values,zorder=0,label=m) + axs[r,c].scatter(x=s.sel(stat=stat).values,y=s.sel(stat=stat0).values,s=5,zorder=0,label=m) if 'zoom' in fig_fil: axs[r,c].set_xlim(0,mx) if mx0<0: axs[r,c].set_ylim(mx0,0) @@ -111,5 +110,5 @@ def run_loop(i,var,mx,mx0): run_loop(i,var,mx,mx0) # Save -plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=True) +plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) From d7c17dadf27508e6cf950a8f517647dbe4b80812 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 18 Jan 2023 17:47:33 +0900 Subject: [PATCH 0496/1472] changing order of plotting --- utils/hist_per_GRU.py | 4 ++-- utils/scat_per_GRU.py | 12 +++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index ca0dc24b2..b45cd6f5d 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -25,12 +25,12 @@ if testing: stat = 'rmse' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['sundials_1en6','be1'] #maybe make this an argument + method_name=['be1','sundials_1en6'] #maybe make this an argument else: import sys # The first input argument specifies the run where the files are stat = sys.argv[1] # max or rmse - method_name=['sundials_1en6','be32','be1'] #maybe make this an argument + method_name=['be32','be1','sundials_1en6',] #maybe make this an argument # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index b3a1c73b4..9a99744c6 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -25,12 +25,12 @@ if testing: stat = 'rmse' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['sundials_1en6','be1'] #maybe make this an argument + method_name=['be1','sundials_1en6'] #maybe make this an argument else: import sys # The first input argument specifies the run where the files are stat = sys.argv[1] # max or rmse - method_name=['sundials_1en6','be32','be1'] #maybe make this an argument + method_name=['be32','be1','sundials_1en6',] #maybe make this an argument # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] @@ -87,7 +87,7 @@ def run_loop(i,var,mx,mx0): for m in method_name: s = summa[m][var].sel(stat=[stat,stat0]) if stat == 'maxe': s.loc[dict(stat='maxe')] = np.fabs(s.loc[dict(stat='maxe')]) # make absolute value norm - axs[r,c].scatter(x=s.sel(stat=stat).values,y=s.sel(stat=stat0).values,s=5,zorder=0,label=m) + axs[r,c].scatter(x=s.sel(stat=stat).values,y=s.sel(stat=stat0).values,s=1,zorder=0,label=m) if 'zoom' in fig_fil: axs[r,c].set_xlim(0,mx) if mx0<0: axs[r,c].set_ylim(mx0,0) @@ -100,11 +100,13 @@ def run_loop(i,var,mx,mx0): stat_word = ' Hourly max abs error ' stat0_word =' Hourly max ' - axs[r,c].legend() + lgnd = axs[r,c].legend() + for j, m in enumerate(method_name): + lgnd.legendHandles[j]._sizes = [80] axs[r,c].set_title(plt_titl[i]) axs[r,c].set_xlabel(stat_word + '[{}]'.format(leg_titl[i])) axs[r,c].set_ylabel(stat0_word + '[{}]'.format(leg_titl[i])) - + for i,(var,mx,mx0) in enumerate(zip(plot_vars,maxes,maxes0)): run_loop(i,var,mx,mx0) From bb6d660bd8d1e5f1f5d53a8350b6721bba7f1d0b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 18 Jan 2023 22:37:26 +0900 Subject: [PATCH 0497/1472] fixing colorbar --- utils/plot_per_GRU.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 1480b9921..15122f9fa 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -219,12 +219,12 @@ def run_loop(i,var,the_max,f_x,f_y): bas_albers.plot(ax=axs[r,c], column=var, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) # Custom colorbar - cax = fig.add_axes([f_x,f_y,0.02,0.2]) + cax = fig.add_axes([f_x,f_y,0.02,0.25]) sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) sm._A = [] - cbr = fig.colorbar(sm, cax=cax, extend='max') - cbr.ax.set_ylabel('[{}]'.format(leg_titl[i]), rotation=270) - cbr.ax.yaxis.set_offset_position('right') + cbr = fig.colorbar(sm, cax=cax) #, extend='max') #if max extend can't get title right + cbr.ax.set_title('[{}]'.format(leg_titl[i])) + #cbr.ax.yaxis.set_offset_position('right') if stat == 'rmse': stat_word = ' Hourly RMSE' if stat == 'maxe': stat_word = ' Hourly max abs error' From 639258ee9a47d0213b9c514f3aab225e30218ab1 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 19 Jan 2023 10:23:36 +0900 Subject: [PATCH 0498/1472] colorbar --- utils/plot_per_GRU.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 15122f9fa..897a82f6a 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -195,8 +195,8 @@ def make_default_path(suffix): plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines # colorbar axes -f_x_mat = [0.473,0.97,0.473,0.97,0.473,0.97] -f_y_mat = [0.73,0.73,0.40,0.40,0.067,0.067] +f_x_mat = [0.453,0.95,0.453,0.96,0.453,0.95] +f_y_mat = [0.71,0.71,0.38,0.38,0.047,0.047] plt.tight_layout() @@ -223,7 +223,7 @@ def run_loop(i,var,the_max,f_x,f_y): sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) sm._A = [] cbr = fig.colorbar(sm, cax=cax) #, extend='max') #if max extend can't get title right - cbr.ax.set_title('[{}]'.format(leg_titl[i])) + cbr.ax.set_label('[{}]'.format(leg_titl[i]), labelpad=20, rotation=270) #cbr.ax.yaxis.set_offset_position('right') if stat == 'rmse': stat_word = ' Hourly RMSE' From aeafe38588cff67c3267102d8798eab682ad66a6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 19 Jan 2023 12:25:07 +0900 Subject: [PATCH 0499/1472] add be64 and colorbar --- utils/hist_per_GRU.py | 2 +- utils/plot_per_GRU.py | 4 ++-- utils/process_plot.sh | 4 +++- utils/process_stat.sh | 1 + utils/scat_per_GRU.py | 2 +- utils/timeseries_to_statistics.py | 2 +- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index b45cd6f5d..ea550d67b 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -30,7 +30,7 @@ import sys # The first input argument specifies the run where the files are stat = sys.argv[1] # max or rmse - method_name=['be32','be1','sundials_1en6',] #maybe make this an argument + method_name=['be64','be32','be1','sundials_1en6'] #maybe make this an argument # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 897a82f6a..1c369b50c 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -195,7 +195,7 @@ def make_default_path(suffix): plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines # colorbar axes -f_x_mat = [0.453,0.95,0.453,0.96,0.453,0.95] +f_x_mat = [0.443,0.94,0.443,0.94,0.443,0.94] f_y_mat = [0.71,0.71,0.38,0.38,0.047,0.047] plt.tight_layout() @@ -223,7 +223,7 @@ def run_loop(i,var,the_max,f_x,f_y): sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) sm._A = [] cbr = fig.colorbar(sm, cax=cax) #, extend='max') #if max extend can't get title right - cbr.ax.set_label('[{}]'.format(leg_titl[i]), labelpad=20, rotation=270) + cbr.ax.set_label('[{}]'.format(leg_titl[i]), labelpad=40, rotation=270) #cbr.ax.yaxis.set_offset_position('right') if stat == 'rmse': stat_word = ' Hourly RMSE' diff --git a/utils/process_plot.sh b/utils/process_plot.sh index 6d7ec0fce..1cd504980 100644 --- a/utils/process_plot.sh +++ b/utils/process_plot.sh @@ -26,4 +26,6 @@ python plot_per_GRU.py sundials_1en6 maxe python plot_per_GRU.py be1 rmse python plot_per_GRU.py be1 maxe python plot_per_GRU.py be32 rmse -python plot_per_GRU.py be32 maxe \ No newline at end of file +python plot_per_GRU.py be32 maxe +python plot_per_GRU.py be64 rmse +python plot_per_GRU.py be64 maxe \ No newline at end of file diff --git a/utils/process_stat.sh b/utils/process_stat.sh index 965096a03..83033d926 100644 --- a/utils/process_stat.sh +++ b/utils/process_stat.sh @@ -22,3 +22,4 @@ pip install --no-index netCDF4 python timeseries_to_statistics.py sundials_1en6 python timeseries_to_statistics.py be1 python timeseries_to_statistics.py be32 +python timeseries_to_statistics.py be64 diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index 9a99744c6..101098b57 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -30,7 +30,7 @@ import sys # The first input argument specifies the run where the files are stat = sys.argv[1] # max or rmse - method_name=['be32','be1','sundials_1en6',] #maybe make this an argument + method_name=['be64','be32','be1','sundials_1en6'] #maybe make this an argument # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index cee4fb307..6190d2fd3 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -17,7 +17,7 @@ import numpy as np # Settings -bench_name = 'sundials_1en8_cat' +bench_name = 'sundials_1en8' top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' des_dir = top_fold + 'statistics' From 86f05151200006f9ff437ba50ffe8d0193ce3a60 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 19 Jan 2023 19:47:31 +0900 Subject: [PATCH 0500/1472] code cleanup --- utils/timeseries_to_statistics.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 6190d2fd3..8f070db4f 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -19,12 +19,10 @@ # Settings bench_name = 'sundials_1en8' top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' -des_dir = top_fold + 'statistics' testing = False if testing: top_fold = '/Users/amedin/Research/USask/test_py/' - des_dir = top_fold + 'statistics' method_name = 'sundials_1en6' else: import multiprocessing as mp @@ -32,6 +30,7 @@ # The first input argument specifies the run where the files are method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) +des_dir = top_fold + 'statistics' src_dir = top_fold + 'summa-' + method_name ben_dir = top_fold + 'summa-' + bench_name src_pat = 'run1_G*_timestep.nc' From f0e2998f7e5995173139d2451ad8ff403abaed59 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 19 Jan 2023 20:09:39 +0900 Subject: [PATCH 0501/1472] code cleanup --- utils/concat_groups_split_summa.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/concat_groups_split_summa.py b/utils/concat_groups_split_summa.py index 22e890377..abbc8d0a9 100644 --- a/utils/concat_groups_split_summa.py +++ b/utils/concat_groups_split_summa.py @@ -28,9 +28,9 @@ # The first input argument specifies the run where the files are method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en8 or be64) -ncdir = top_fold + 'summa-' + method_name +ncdir = top_fold + 'summa-' + method_name + '_nocat' file_pattern = 'run1_G*_timestep.nc' -ctdir = top_fold + 'summa-' + method_name + '_cat' +ctdir = top_fold + 'summa-' + method_name # get list of split summa output files (hardwired pattern) outfilelist0 = glob((ncdir+'/'+file_pattern)) From 854205b76dfb3920d502f898531c4ff67126b5a3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 20 Jan 2023 09:02:28 +0900 Subject: [PATCH 0502/1472] initial BMI commit --- build/my_makefile_cop | 30 +- build/my_makefile_gra | 30 +- build/my_makefile_mac | 29 +- build/my_makefile_ric | 30 +- .../multi_driverDoesAllTheotherThings.f90 | 43 + build/source/driver/summa_bmi.f90 | 2069 +++++++++++++++++ build/source/driver/test_bmi.f90 | 52 + 7 files changed, 2276 insertions(+), 7 deletions(-) create mode 100755 build/source/driver/multi_driverDoesAllTheotherThings.f90 create mode 100755 build/source/driver/summa_bmi.f90 create mode 100644 build/source/driver/test_bmi.f90 diff --git a/build/my_makefile_cop b/build/my_makefile_cop index 39bfb4c47..fd5745955 100644 --- a/build/my_makefile_cop +++ b/build/my_makefile_cop @@ -199,6 +199,11 @@ SUMMA_UTILMS= \ matrixOper.f90 UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) +# interface modules +SUMMA_INTERFACE= \ + summa_bmi.f90 +INTERFACE = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_INTERFACE)) + # Model guts SUMMA_MODGUT= \ MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) @@ -300,7 +305,7 @@ NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) # ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) +SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER $(INTERFACE) # Define the driver routine SUMMA_DRIVER= \ @@ -318,8 +323,14 @@ SUMMA_DRIVER= \ summa_driver.f90 DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) +# Define BMI testing infrastructure +BMI_TEST = \ + test_bmi.f90 +BMI = $(patsubst %, $(DRIVER_DIR)/%, $(BMI_TEST)) + # Define the executable DRIVER__EX = summa_sundials.exe +BMI__EX = test_bmi.exe # Define version number VERSIONFILE = $(DRIVER_DIR)/summaversion.inc @@ -358,6 +369,8 @@ endif # Compile all: compile_noah compile_comm compile_summa link clean install part: compile_noah compile_comm compile_summa +bmi: compile_noah compile_comm compile_bmi link_bmi clean install_test + check: $(info) @@ -387,7 +400,6 @@ compile_noah: $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) # compile common routines - compile_comm: $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) @@ -396,10 +408,18 @@ compile_summa: update_version $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ $(INCLUDES) $(INC_SUNDIALS) +compile_bmi: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(BMI) \ + $(INCLUDES) $(INC_SUNDIALS) + # link routines link: $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) +# link routines +link_bmi: + $(FC_EXE) -pg -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(BMI__EX) + # Remove object files clean: rm -f *.o @@ -411,3 +431,9 @@ install: @mkdir -p $(EXE_PATH) @mv $(DRIVER__EX) $(EXE_PATH) $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) + +# Copy the executable to the bin directory +install_test: + @mkdir -p $(EXE_PATH) + @mv $(BMI__EX) $(EXE_PATH) + $(info $(BMI__EX) successfully installed in $(EXE_PATH)) diff --git a/build/my_makefile_gra b/build/my_makefile_gra index 03389b411..7d5b92f66 100644 --- a/build/my_makefile_gra +++ b/build/my_makefile_gra @@ -199,6 +199,11 @@ SUMMA_UTILMS= \ matrixOper.f90 UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) +# interface modules +SUMMA_INTERFACE= \ + summa_bmi.f90 +INTERFACE = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_INTERFACE)) + # Model guts SUMMA_MODGUT= \ MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) @@ -300,7 +305,7 @@ NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) # ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) +SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER $(INTERFACE) # Define the driver routine SUMMA_DRIVER= \ @@ -318,8 +323,14 @@ SUMMA_DRIVER= \ summa_driver.f90 DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) +# Define BMI testing infrastructure +BMI_TEST = \ + test_bmi.f90 +BMI = $(patsubst %, $(DRIVER_DIR)/%, $(BMI_TEST)) + # Define the executable DRIVER__EX = summa_sundials.exe +BMI__EX = test_bmi.exe # Define version number VERSIONFILE = $(DRIVER_DIR)/summaversion.inc @@ -358,6 +369,8 @@ endif # Compile all: compile_noah compile_comm compile_summa link clean install part: compile_noah compile_comm compile_summa +bmi: compile_noah compile_comm compile_bmi link_bmi clean install_test + check: $(info) @@ -387,7 +400,6 @@ compile_noah: $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) # compile common routines - compile_comm: $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) @@ -396,10 +408,18 @@ compile_summa: update_version $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ $(INCLUDES) $(INC_SUNDIALS) +compile_bmi: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(BMI) \ + $(INCLUDES) $(INC_SUNDIALS) + # link routines link: $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) +# link routines +link_bmi: + $(FC_EXE) -pg -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(BMI__EX) + # Remove object files clean: rm -f *.o @@ -411,3 +431,9 @@ install: @mkdir -p $(EXE_PATH) @mv $(DRIVER__EX) $(EXE_PATH) $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) + +# Copy the executable to the bin directory +install_test: + @mkdir -p $(EXE_PATH) + @mv $(BMI__EX) $(EXE_PATH) + $(info $(BMI__EX) successfully installed in $(EXE_PATH)) diff --git a/build/my_makefile_mac b/build/my_makefile_mac index bd8965786..3d0ad1940 100644 --- a/build/my_makefile_mac +++ b/build/my_makefile_mac @@ -199,6 +199,11 @@ SUMMA_UTILMS= \ matrixOper.f90 UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) +# interface modules +SUMMA_INTERFACE= \ + summa_bmi.f90 +INTERFACE = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_INTERFACE)) + # Model guts SUMMA_MODGUT= \ MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) @@ -300,7 +305,7 @@ NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) # ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) +SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER $(INTERFACE) # Define the driver routine SUMMA_DRIVER= \ @@ -318,8 +323,14 @@ SUMMA_DRIVER= \ summa_driver.f90 DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) +# Define BMI testing infrastructure +BMI_TEST = \ + test_bmi.f90 +BMI = $(patsubst %, $(DRIVER_DIR)/%, $(BMI_TEST)) + # Define the executable DRIVER__EX = summa_sundials.exe +BMI__EX = test_bmi.exe # Define version number VERSIONFILE = $(DRIVER_DIR)/summaversion.inc @@ -358,6 +369,8 @@ endif # Compile all: compile_noah compile_comm compile_summa link clean install part: compile_noah compile_comm compile_summa +bmi: compile_noah compile_comm compile_bmi link_bmi clean install_test + check: $(info) @@ -395,10 +408,18 @@ compile_summa: update_version $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ $(INCLUDES) $(INC_SUNDIALS) +compile_bmi: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(BMI) \ + $(INCLUDES) $(INC_SUNDIALS) + # link routines link: $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) +# link routines +link_bmi: + $(FC_EXE) -pg -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(BMI__EX) + # Remove object files clean: rm -f *.o @@ -410,3 +431,9 @@ install: @mkdir -p $(EXE_PATH) @mv $(DRIVER__EX) $(EXE_PATH) $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) + +# Copy the executable to the bin directory +install_test: + @mkdir -p $(EXE_PATH) + @mv $(BMI__EX) $(EXE_PATH) + $(info $(BMI__EX) successfully installed in $(EXE_PATH)) diff --git a/build/my_makefile_ric b/build/my_makefile_ric index 5cd15287b..29a553613 100644 --- a/build/my_makefile_ric +++ b/build/my_makefile_ric @@ -199,6 +199,11 @@ SUMMA_UTILMS= \ matrixOper.f90 UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) +# interface modules +SUMMA_INTERFACE= \ + summa_bmi.f90 +INTERFACE = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_INTERFACE)) + # Model guts SUMMA_MODGUT= \ MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) @@ -300,7 +305,7 @@ NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) # ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) +SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER $(INTERFACE) # Define the driver routine SUMMA_DRIVER= \ @@ -318,8 +323,14 @@ SUMMA_DRIVER= \ summa_driver.f90 DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) +# Define BMI testing infrastructure +BMI_TEST = \ + test_bmi.f90 +BMI = $(patsubst %, $(DRIVER_DIR)/%, $(BMI_TEST)) + # Define the executable DRIVER__EX = summa_sundials.exe +BMI__EX = test_bmi.exe # Define version number VERSIONFILE = $(DRIVER_DIR)/summaversion.inc @@ -358,6 +369,8 @@ endif # Compile all: compile_noah compile_comm compile_summa link clean install part: compile_noah compile_comm compile_summa +bmi: compile_noah compile_comm compile_bmi link_bmi clean install_test + check: $(info) @@ -387,7 +400,6 @@ compile_noah: $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) # compile common routines - compile_comm: $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) @@ -396,10 +408,18 @@ compile_summa: update_version $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ $(INCLUDES) $(INC_SUNDIALS) +compile_bmi: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(BMI) \ + $(INCLUDES) $(INC_SUNDIALS) + # link routines link: $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) +# link routines +link_bmi: + $(FC_EXE) -pg -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(BMI__EX) + # Remove object files clean: rm -f *.o @@ -411,3 +431,9 @@ install: @mkdir -p $(EXE_PATH) @mv $(DRIVER__EX) $(EXE_PATH) $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) + +# Copy the executable to the bin directory +install_test: + @mkdir -p $(EXE_PATH) + @mv $(BMI__EX) $(EXE_PATH) + $(info $(BMI__EX) successfully installed in $(EXE_PATH)) diff --git a/build/source/driver/multi_driverDoesAllTheotherThings.f90 b/build/source/driver/multi_driverDoesAllTheotherThings.f90 new file mode 100755 index 000000000..3db87c717 --- /dev/null +++ b/build/source/driver/multi_driverDoesAllTheotherThings.f90 @@ -0,0 +1,43 @@ +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2015 NCAR/RAL +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . + +program multi_driver +! used to evaluate different methods for simulating snow processes +! ***************************************************************************** +! use desired modules +! ***************************************************************************** +use,intrinsic :: iso_c_binding +USE,intrinsic :: ieee_arithmetic ! IEEE arithmetic (obviously) +use summa_bmi, only: initialize, update, finalize, modelTimeStep +USE globalData,only:numtim ! number of time steps +implicit none + +integer(kind=c_int) :: istat + +!'main routine' +istat = initialize() + +! loop through time +do modelTimeStep=1,numtim + istat = update() +end do ! (looping through time) +istat = finalize() + +end program multi_driver diff --git a/build/source/driver/summa_bmi.f90 b/build/source/driver/summa_bmi.f90 new file mode 100755 index 000000000..dc4c3072b --- /dev/null +++ b/build/source/driver/summa_bmi.f90 @@ -0,0 +1,2069 @@ +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2015 NCAR/RAL +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . + +module summa_bmi +! used to evaluate different methods for simulating snow processes +! ***************************************************************************** +! use desired modules +! ***************************************************************************** +use,intrinsic :: iso_c_binding +USE nrtype ! variable types, etc. +USE netcdf ! netcdf libraries +USE,intrinsic :: ieee_arithmetic ! IEEE arithmetic (obviously) +! provide access to subroutines and functions +USE summaFileManager,only:summa_SetDirsUndPhiles ! sets directories and filenames +USE module_sf_noahmplsm,only:read_mp_veg_parameters ! module to read NOAH vegetation tables +USE module_sf_noahmplsm,only:isWater ! parameter for water land cover type +USE nr_utility_module,only:arth ! get a sequence of numbers +USE nr_utility_module,only:indexx ! sort vectors in ascending order +USE ascii_util_module,only:file_open ! open ascii file +USE ascii_util_module,only:get_vlines ! read a vector of non-comment lines from an ASCII file +USE ascii_util_module,only:split_line ! extract the list of variable names from the character string +use time_utils_module,only:elapsedSec ! calculate the elapsed time +USE allocspace_module,only:allocGlobal ! module to allocate space for global data structures +USE allocspace_module,only:allocLocal ! module to allocate space for local data structures +USE childStruc_module,only:childStruc ! module to create a child data structure +USE mDecisions_module,only:mDecisions ! module to read model decisions +USE popMetadat_module,only:popMetadat ! module to populate metadata structures +USE flxMapping_module,only:flxMapping ! module to map fluxes to states +USE checkStruc_module,only:checkStruc ! module to check metadata structures +USE def_output_module,only:def_output ! module to define model output +USE ffile_info_module,only:ffile_info ! module to read information on forcing datafile +USE read_attrb_module,only:read_dimension ! module to read dimensions of GRU and HRU +USE read_attrb_module,only:read_attrb ! module to read local attributes +USE read_pinit_module,only:read_pinit ! module to read initial model parameter values +USE paramCheck_module,only:paramCheck ! module to check consistency of model parameters +USE check_icond_module,only:check_icond ! module to check initial conditions +USE read_icond_module,only:read_icond ! module to read initial conditions +USE read_icond_module,only:read_icond_nlayers ! module to read initial conditions +USE pOverwrite_module,only:pOverwrite ! module to overwrite default parameter values with info from the Noah tables +USE read_param_module,only:read_param ! module to read model parameter sets +USE ConvE2Temp_module,only:E2T_lookup ! module to calculate a look-up table for the temperature-enthalpy conversion +USE var_derive_module,only:calcHeight ! module to calculate height at layer interfaces and layer mid-point +USE var_derive_module,only:v_shortcut ! module to calculate "short-cut" variables +USE var_derive_module,only:rootDensty ! module to calculate the vertical distribution of roots +USE var_derive_module,only:satHydCond ! module to calculate the saturated hydraulic conductivity in each soil layer +USE var_derive_module,only:fracFuture ! module to calculate the fraction of runoff in future time steps (time delay histogram) +USE read_force_module,only:read_force ! module to read model forcing data +USE modelwrite_module,only:writeParm,writeTime ! module to write model attributes and parameters +USE modelwrite_module,only:writeData,writeBasin ! module to write model output +USE modelwrite_module,only:writeRestart ! module to write model Restart +USE vegPhenlgy_module,only:vegPhenlgy ! module to compute vegetation phenology +USE run_oneGRU_module,only:run_oneGRU ! module to run for one GRU +USE groundwatr_module,only:groundwatr ! module to simulate regional groundwater balance +USE qTimeDelay_module,only:qOverland ! module to route water through an "unresolved" river network +USE netcdf_util_module,only:nc_file_close ! module to handle netcdf stuff for inputs and outputs +! provide access to file paths +USE summaFileManager,only:SETNGS_PATH ! define path to settings files (e.g., Noah vegetation tables) +USE summaFileManager,only:MODEL_INITCOND ! name of model initial conditions file +USE summaFileManager,only:LOCAL_ATTRIBUTES ! name of model initial attributes file +USE summaFileManager,only:OUTPUT_PATH,OUTPUT_PREFIX ! define output file +USE summaFileManager,only:LOCALPARAM_INFO,BASINPARAM_INFO ! files defining the default values and constraints for model parameters +! provide access to the derived types to define the data structures +USE data_types,only:& + ! no spatial dimension + var_i, & ! x%var(:) (i4b) + var_d, & ! x%var(:) (dp) + var_ilength, & ! x%var(:)%dat (i4b) + var_dlength, & ! x%var(:)%dat (dp) + ! no variable dimension + hru_i, & ! x%hru(:) (i4b) + hru_d, & ! x%hru(:) (dp) + ! gru dimension + gru_int, & ! x%gru(:)%var(:) (i4b) + gru_double, & ! x%gru(:)%var(:) (dp) + gru_intVec, & ! x%gru(:)%var(:)%dat (i4b) + gru_doubleVec, & ! x%gru(:)%var(:)%dat (dp) + ! gru+hru dimension + gru_hru_int, & ! x%gru(:)%hru(:)%var(:) (i4b) + gru_hru_double, & ! x%gru(:)%hru(:)%var(:) (dp) + gru_hru_intVec, & ! x%gru(:)%hru(:)%var(:)%dat (i4b) + gru_hru_doubleVec ! x%gru(:)%hru(:)%var(:)%dat (dp) +USE data_types,only:extended_info ! extended metadata structure +! provide access to runtime options +USE globalData,only:iRunModeFull,iRunModeGRU,iRunModeHRU +! provide access to metadata structures +USE globalData,only:time_meta,forc_meta,attr_meta,type_meta ! metadata structures +USE globalData,only:prog_meta,diag_meta,flux_meta ! metadata structures +USE globalData,only:mpar_meta,indx_meta ! metadata structures +USE globalData,only:bpar_meta,bvar_meta ! metadata structures +USE globalData,only:averageFlux_meta ! metadata for time-step average fluxes +USE globalData,only:model_decisions ! model decision structure +! provide access to global data +USE globalData,only:dNaN ! double precision NaN +USE globalData,only:refTime ! reference time +USE globalData,only:startTime ! start time +USE globalData,only:finshTime ! end time +USE globalData,only:doJacobian ! flag to compute the Jacobian +USE globalData,only:gru_struc ! gru-hru mapping structures +USE globalData,only:localParFallback ! local column default parameters +USE globalData,only:basinParFallback ! basin-average default parameters +USE globalData,only:structInfo ! information on the data structures +USE globalData,only:numtim ! number of time steps +USE globalData,only:urbanVegCategory ! vegetation category for urban areas +USE globalData,only:greenVegFrac_monthly ! fraction of green vegetation in each month (0-1) +USE globalData,only:globalPrintFlag ! global print flag +USE globalData,only:integerMissing ! missing integer value +USE globalData,only:realMissing ! missing double precision value +USE globalData,only:yes,no ! .true. and .false. +USE globalData,only:dJulianStart ! julian day of start time of simulation +USE globalData,only:dJulianFinsh ! julian day of end time of simulation +USE globalData,only:data_step ! length of time steps for the outermost timeloo +USE multiconst,only:secprday +! provide access to Noah-MP parameters +USE NOAHMP_VEG_PARAMETERS,only:SAIM,LAIM ! 2-d tables for stem area index and leaf area index (vegType,month) +USE NOAHMP_VEG_PARAMETERS,only:HVT,HVB ! height at the top and bottom of vegetation (vegType) +USE var_lookup,only:maxvarTime ! size of variable vectors +USE var_lookup,only:maxvarForc,maxvarProg,maxvarDiag ! size of variable vectors +USE var_lookup,only:maxvarFlux,maxvarIndx,maxvarBvar ! size of variable vectors +! provide access to the named variables that describe elements of parent model structures +USE var_lookup,only:iLookTIME,iLookFORCE ! look-up values for time and forcing data structures +USE var_lookup,only:iLookTYPE ! look-up values for classification of veg, soils etc. +USE var_lookup,only:iLookATTR ! look-up values for local attributes +USE var_lookup,only:iLookPARAM ! look-up values for local column model parameters +USE var_lookup,only:iLookINDEX ! look-up values for local column index variables +USE var_lookup,only:iLookPROG ! look-up values for local column model prognostic (state) variables +USE var_lookup,only:iLookDIAG ! look-up values for local column model diagnostic variables +USE var_lookup,only:iLookFLUX ! look-up values for local column model fluxes +USE var_lookup,only:iLookBVAR ! look-up values for basin-average model variables +USE var_lookup,only:iLookBPAR ! look-up values for basin-average model parameters +USE var_lookup,only:iLookDECISIONS ! look-up values for model decisions +USE var_lookup,only:iLookVarType ! look-up values for variable type structure +USE var_lookup,only:iLookFreq ! look-up values for model output frequency +! provide access to the named variables that describe elements of child model structures +USE var_lookup,only:childFLUX_MEAN ! look-up values for timestep-average model fluxes +! provide access to the named variables that describe model decisions +USE mDecisions_module,only: & ! look-up values for method used to compute derivative + numerical, & ! numerical solution + analytical ! analytical solution +USE mDecisions_module,only:& ! look-up values for LAI decisions + monthlyTable,& ! LAI/SAI taken directly from a monthly table for different vegetation classes + specified ! LAI/SAI computed from green vegetation fraction and winterSAI and summerLAI parameters +USE mDecisions_module,only:& ! look-up values for the choice of method for the spatial representation of groundwater + localColumn, & ! separate groundwater representation in each local soil column + singleBasin ! single groundwater store over the entire basin +USE mDecisions_module,only:& + sameRulesAllLayers, & ! SNTHERM option: same combination/sub-dividion rules applied to all layers + rulesDependLayerIndex ! CLM option: combination/sub-dividion rules depend on layer index +USE output_stats,only:calcStats ! module for compiling output statistics +USE var_lookup,only:maxvarFreq ! maximum # of output files +USE globalData,only:ncid ! file id of netcdf output file +implicit none + +! ***************************************************************************** +! (0) variable definitions +! ***************************************************************************** +! define the statistics structures +type(gru_hru_doubleVec) :: forcStat ! x%gru(:)%hru(:)%var(:)%dat -- model forcing data +type(gru_hru_doubleVec) :: progStat ! x%gru(:)%hru(:)%var(:)%dat -- model prognostic (state) variables +type(gru_hru_doubleVec) :: diagStat ! x%gru(:)%hru(:)%var(:)%dat -- model diagnostic variables +type(gru_hru_doubleVec) :: fluxStat ! x%gru(:)%hru(:)%var(:)%dat -- model fluxes +type(gru_hru_doubleVec) :: indxStat ! x%gru(:)%hru(:)%var(:)%dat -- model indices +type(gru_doubleVec) :: bvarStat ! x%gru(:)%var(:)%dat -- basin-average variabl +! define the primary data structures (scalars) +type(var_i) :: timeStruct ! x%var(:) -- model time data +type(gru_hru_double) :: forcStruct ! x%gru(:)%hru(:)%var(:) -- model forcing data +type(gru_hru_double) :: attrStruct ! x%gru(:)%hru(:)%var(:) -- local attributes for each HRU +type(gru_hru_int) :: typeStruct ! x%gru(:)%hru(:)%var(:) -- local classification of soil veg etc. for each HRU +! define the primary data structures (variable length vectors) +type(gru_hru_intVec) :: indxStruct ! x%gru(:)%hru(:)%var(:)%dat -- model indices +type(gru_hru_doubleVec) :: mparStruct ! x%gru(:)%hru(:)%var(:)%dat -- model parameters +type(gru_hru_doubleVec) :: progStruct ! x%gru(:)%hru(:)%var(:)%dat -- model prognostic (state) variables +type(gru_hru_doubleVec) :: diagStruct ! x%gru(:)%hru(:)%var(:)%dat -- model diagnostic variables +type(gru_hru_doubleVec) :: fluxStruct ! x%gru(:)%hru(:)%var(:)%dat -- model fluxes +! define the basin-average structures +type(gru_double) :: bparStruct ! x%gru(:)%var(:) -- basin-average parameters +type(gru_doubleVec) :: bvarStruct ! x%gru(:)%var(:)%dat -- basin-average variables +! define the ancillary data structures +type(gru_hru_double) :: dparStruct ! x%gru(:)%hru(:)%var(:) -- default model parameters +! define indices +integer(i4b) :: iStruct ! loop through data structures +integer(i4b) :: iGRU,jGRU,kGRU ! index of grouped response unit +integer(i4b) :: iHRU,jHRU,kHRU ! index of the hydrologic response unit +integer(i4b) :: nGRU ! number of grouped response units +integer(i4b) :: nHRU ! number of global hydrologic response units +integer(i4b) :: hruCount ! number of local hydrologic response units +integer(i4b) :: modelTimeStep=0 ! index of model time step +integer(i4b),dimension(maxvarTime) :: oldTimeVec ! old time vector +integer(i4b),dimension(maxvarFreq) :: statCounter=0 ! time counter for stats +integer(i4b),dimension(maxvarFreq) :: outputTimeStep=0 ! timestep in output files +logical(lgt),dimension(maxvarFreq) :: resetStats=.true. ! flags to reset statistics +logical(lgt),dimension(maxvarFreq) :: finalizeStats=.false. ! flags to reset statistics +! define the time output +logical(lgt) :: printProgress ! flag to print progress +integer(i4b),parameter :: ixProgress_im=1000 ! named variable to print progress once per month +integer(i4b),parameter :: ixProgress_id=1001 ! named variable to print progress once per day +integer(i4b),parameter :: ixProgress_ih=1002 ! named variable to print progress once per hour +integer(i4b),parameter :: ixProgress_never=1003 ! named variable to print progress never +integer(i4b) :: ixProgress=ixProgress_id ! define frequency to write progress +! define the re-start file +logical(lgt) :: printRestart ! flag to print a re-start file +integer(i4b),parameter :: ixRestart_iy=1000 ! named variable to print a re-start file once per year +integer(i4b),parameter :: ixRestart_im=1001 ! named variable to print a re-start file once per month +integer(i4b),parameter :: ixRestart_id=1002 ! named variable to print a re-start file once per day +integer(i4b),parameter :: ixRestart_end=1003 ! named variable to print a re-start file at the end of a run +integer(i4b),parameter :: ixRestart_never=1004 ! named variable to print a re-start file never +integer(i4b) :: ixRestart=ixRestart_never ! define frequency to write restart files +! define output file +integer(i4b) :: ctime1(8) ! initial time +character(len=256) :: output_fileSuffix='' ! suffix for the output file +character(len=256) :: summaFileManagerFile='' ! path/name of file defining directories and files +character(len=256) :: fileout='' ! output filename +integer(i4b),parameter :: noNewFiles=1001 ! no new output files +integer(i4b),parameter :: newFileEveryOct1=1002 ! create a new file on Oct 1 every year (start of the USA water year) +integer(i4b) :: newOutputFile=noNewFiles ! option for new output files +logical(lgt) :: defNewOutputFile=.false. ! flag to define new output files +! define model control structures +logical(lgt) :: computeVegFluxFlag ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) +type(hru_i),allocatable :: computeVegFlux(:) ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) +type(hru_d),allocatable :: dt_init(:) ! used to initialize the length of the sub-step for each HRU +type(hru_d),allocatable :: upArea(:) ! area upslope of each HRU +! general local variables +integer(i4b) :: ivar ! index of model variable +logical(lgt) :: flux_mask(maxvarFlux) ! mask defining desired flux variables +integer(i4b) :: forcNcid=integerMissing ! netcdf id for current netcdf forcing file +integer(i4b) :: iFile=1 ! index of current forcing file from forcing file list +integer(i4b) :: forcingStep=integerMissing ! index of current time step in current forcing file +real(dp) :: notUsed_canopyDepth ! NOT USED: canopy depth (m) +real(dp) :: notUsed_exposedVAI ! NOT USED: exposed vegetation area index (m2 m-2) +! error control +integer(i4b) :: err=0 ! error code +character(len=1024) :: message='' ! error message +! output control +integer(i4b) :: iFreq ! index for looping through output files +logical(lgt) :: statForc_mask(maxvarForc) ! mask defining forc stats +logical(lgt) :: statProg_mask(maxvarProg) ! mask defining prog stats +logical(lgt) :: statDiag_mask(maxvarDiag) ! mask defining diag stats +logical(lgt) :: statFlux_mask(maxvarFlux) ! mask defining flux stats +logical(lgt) :: statIndx_mask(maxvarIndx) ! mask defining indx stats +logical(lgt) :: statBvar_mask(maxvarBvar) ! mask defining bvar stats +integer(i4b),allocatable :: forcChild_map(:) ! index of the child data structure: stats forc +integer(i4b),allocatable :: progChild_map(:) ! index of the child data structure: stats prog +integer(i4b),allocatable :: diagChild_map(:) ! index of the child data structure: stats diag +integer(i4b),allocatable :: fluxChild_map(:) ! index of the child data structure: stats flux +integer(i4b),allocatable :: indxChild_map(:) ! index of the child data structure: stats indx +integer(i4b),allocatable :: bvarChild_map(:) ! index of the child data structure: stats bvar +type(extended_info),allocatable :: statForc_meta(:) ! child metadata for stats +type(extended_info),allocatable :: statProg_meta(:) ! child metadata for stats +type(extended_info),allocatable :: statDiag_meta(:) ! child metadata for stats +type(extended_info),allocatable :: statFlux_meta(:) ! child metadata for stats +type(extended_info),allocatable :: statIndx_meta(:) ! child metadata for stats +type(extended_info),allocatable :: statBvar_meta(:) ! child metadata for stats +! stuff for restart file +character(len=256) :: timeString ! protion of restart file name that contains the write-out time +character(len=256) :: restartFile ! restart file name +character(len=256) :: attrFile ! attributes file name +! open MP functions +integer(i4b) :: omp_get_num_threads ! get the number of threads +! parallelize the model run +integer(i4b) :: nThreads ! number of threads +integer(i4b), allocatable :: ixExpense(:) ! ranked index GRU w.r.t. computational expense +integer(i4b), allocatable :: totalFluxCalls(:) ! total number of flux calls for each GRU +integer(i4b) :: nHRUrun ! number of HRUs in the run domain +integer(i4b) :: maxLayers ! maximum number of layers +integer(i4b) :: maxSnowLayers ! maximum number of snow layers +integer(i4b) :: startGRU ! index of the starting GRU for parallelization run +integer(i4b) :: checkHRU ! index of the HRU for a single HRU run +integer(i4b) :: fileGRU ! number of GRUs in the input file +integer(i4b) :: fileHRU ! number of HRUs in the input file +integer(i4b) :: iRunMode ! define the current running mode +character(len=128) :: fmtGruOutput ! a format string used to write start and end GRU in output file names +! timing information +integer*8 :: openMPstart,openMPend ! time for the start of the parallelization section +integer*8, allocatable :: timeGRUstart(:) ! time GRUs start +real(dp), allocatable :: timeGRUcompleted(:) ! time required to complete each GRU +real(dp), allocatable :: timeGRU(:) ! time spent on each GRU +integer(i4b), dimension(8) :: startInit,endInit ! date/time for the start and end of the initialization +real(dp) :: elapsedInit ! elapsed time for the initialization +integer(i4b), dimension(8) :: startRead,endRead ! date/time for the start and end of the data read +real(dp) :: elapsedRead ! elapsed time for the data read +integer(i4b), dimension(8) :: startWrite,endWrite ! date/time for the start and end of the stats/write +real(dp) :: elapsedWrite ! elapsed time for the stats/write +integer(i4b), dimension(8) :: startPhysics,endPhysics ! date/time for the start and end of the physics +real(dp) :: elapsedPhysics ! elapsed time for the physics + +! version information generated during compiling +INCLUDE 'summaversion.inc' + + +contains + + + function get_start_time() result(ret) bind(c, name="get_summa_start_time") + implicit none + real(KIND=C_FLOAT) :: ret + ret = dJulianStart ! unit days + end function get_start_time + + + function get_current_time() result(ret) bind(c, name="get_summa_current_time") + implicit none + real(KIND=C_FLOAT) :: ret + + if(modelTimeStep==1)then + ret = dJulianStart + else + ret = dJulianStart + (data_step*real(modelTimeStep-1,dp))/secprday ! unit days + end if + end function get_current_time + + + function get_end_time() result(ret) bind(c, name="get_summa_end_time") + implicit none + real(KIND=C_FLOAT) :: ret + ret = dJulianFinsh ! unit days + end function get_end_time + + + function get_time_step() result(ret) bind(c, name="get_summa_time_step") + implicit none + integer :: ret + ret = data_step ! unit seconds +end function get_time_step + + + function get_num_output_fields() result(ret) bind(c, name="get_num_ovars") + implicit none + integer :: ret + ret = 16 + end function get_num_output_fields + + + subroutine to_c_string(source, dest) + implicit none + + character(len=32), intent(in) :: source + character(kind=C_CHAR), intent(out) :: dest(*) + integer :: j,k + + k = LEN(TRIM(source)) + do j=1,k + dest(j) = source(j:j) + end do + dest(k+1) = C_NULL_CHAR + + end subroutine to_c_string + + + subroutine get_output_name(index, dest) bind(c, name="get_ovar_name") + implicit none + integer :: index + character(len=32) :: source + character(kind=c_char)::dest(*) + do iGRU = 1, nGRU + do jHRU = 1, gru_struc(iGRU)%hruCount + select case (index) + case (1) + source = 'total runoff' + call to_c_string(source, dest) + case (2) + source = 'evaporation from soil' + call to_c_string(source, dest) + case (3) + source = 'precipitation' + call to_c_string(source, dest) + case (4) + source = 'evaporation from vegetation' + call to_c_string(source, dest) + case (5) + source = 'transpiration from vegetation' + call to_c_string(source, dest) + case (6) + source = 'sublimation from snow surface' + call to_c_string(source, dest) + case (7) + source = 'sublimation from vegetation surface' + call to_c_string(source, dest) + case (8) + source = 'snow water equivalent' + call to_c_string(source, dest) + case (9) + source = 'soil moisture' + call to_c_string(source, dest) + case (10) + source = 'canopy moisture' + call to_c_string(source, dest) + case (11) + source = 'net radiation' + call to_c_string(source, dest) + case (12) + source = 'latent heat' + call to_c_string(source, dest) + case (13) + source = 'sensible heat' + call to_c_string(source, dest) + case (14) + source = 'canopy air energy flux' + call to_c_string(source, dest) + case (15) + source = 'vegetation energy flux' + call to_c_string(source, dest) + case (16) + source = 'ground energy flux' + call to_c_string(source, dest) + end select + end do + end do + end subroutine get_output_name + + + subroutine get_output_units(index, dest) bind(c, name="get_ovar_units") + implicit none + integer :: index + character(len=32) :: source + character(kind=c_char)::dest(*) + + do iGRU = 1, nGRU + do jHRU = 1, gru_struc(iGRU)%hruCount + select case (index) + case (1) ! total runoff + source = 'm s-1' + call to_c_string(source, dest) + case (2) ! evaporation from soil + source = 'kg m-2 s-1' + call to_c_string(source, dest) + case (3) ! precipitation rate + source = 'kg m-2 s-1' + call to_c_string(source, dest) + case (4) ! evaporation from vegetation + source = 'kg m-2 s-1' + call to_c_string(source, dest) + case (5) ! transpiration from vegetation + source = 'kg m-2 s-1' + call to_c_string(source, dest) + case (6) ! sublimation from snow surface + source = 'kg m-2 s-1' + call to_c_string(source, dest) + case (7) ! sublimation from vegetation surface + source = 'kg m-2 s-1' + call to_c_string(source, dest) + case (8) ! snow water equivalent + source = 'kg m-2' + call to_c_string(source, dest) + case (9) ! soil moisture + source = 'kg m-2' + call to_c_string(source, dest) + case (10) ! canopy moisture + source = 'kg m-2' + call to_c_string(source, dest) + case (11) ! net radiation + source = 'W m-2' + call to_c_string(source, dest) + case (12) ! latent heat + source = 'W m-2' + call to_c_string(source, dest) + case (13) ! sensible heat + source = 'W m-2' + call to_c_string(source, dest) + case (14) ! canopy air energy flux + source = 'W m-2' + call to_c_string(source, dest) + case (15) ! vegetation energy flux + source = 'W m-2' + call to_c_string(source, dest) + case (16) ! ground energy flux + source = 'W m-2' + call to_c_string(source, dest) + end select + end do + end do + end subroutine get_output_units + + + function get_num_basins() result(ret) bind(c, name="get_num_basins") + use globalData, only: nHRUfile + + implicit none + integer :: ret + ret = nHRUfile + + end function get_num_basins + + + subroutine get_basin_field(index, targetarr) bind(c, name="get_ovar_values") + use globalData, only: nHRUfile + + implicit none + integer, intent(in) :: index + real(kind=C_FLOAT), intent(out) :: targetarr(nHRUfile) + integer :: iGRU, jHRU + + do iGRU = 1, nGRU + do jHRU = 1, gru_struc(iGRU)%hruCount + select case (index) + case (1) ! total runoff + targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & + fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarTotalRunoff)%dat(1) + case (2) ! evaporation from soil + targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & + fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarGroundEvaporation)%dat(1) + !case (3) ! precipitation + ! targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & + ! forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORC%pptrate)%dat(1) + case (4) ! evaporation from vegetation + targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & + fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) + case (5) ! transpiration from vegetation + targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & + fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) + case (6) ! sublimation from snow surface + targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & + fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarSnowSublimation)%dat(1) + case (7) ! sublimation from vegetation surface + targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & + fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopySublimation)%dat(1) + case (8) ! snow water equivalent + targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & + progStruct%gru(iGRU)%hru(jHRU)%var(iLookPROG%scalarSWE)%dat(1) + case (9) ! soil moisture + targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & + diagStruct%gru(iGRU)%hru(jHRU)%var(iLookDIAG%scalarTotalSoilWat)%dat(1) + case (10) ! canopy moisture + targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & + progStruct%gru(iGRU)%hru(jHRU)%var(iLookPROG%scalarCanopyWat)%dat(1) + case (11) ! net radiation + targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & + fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarNetRadiation)%dat(1) + case (12) ! latent heat + targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & + fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarLatHeatTotal)%dat(1) + case (13) ! sensible heat + targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & + fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarSenHeatTotal)%dat(1) + case (14) ! canopy air energy flux + targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & + fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1) + case (15) ! vegetation energy flux + targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & + fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1) + case (16) ! ground energy flux + targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & + fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1) + end select + end do + end do + + end subroutine get_basin_field + + + subroutine get_latlons(targetlatarr, targetlonarr) bind(c, name="get_latlons") + + use, intrinsic :: ISO_C_BINDING + use var_lookup, only: iLook_attr + use globalData, only: nHRUfile, gru_struc + + implicit none + + real(kind=C_FLOAT), intent(out) :: targetlatarr(nHRUfile) + real(kind=C_FLOAT), intent(out) :: targetlonarr(nHRUfile) + integer :: iGRU, jHRU + + do iGRU = 1, nGRU + do jHRU = 1, gru_struc(iGRU)%hruCount + targetlatarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = attrStruct%gru(iGRU)%hru(jHRU)%var(iLookATTR%latitude) + targetlonarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = attrStruct%gru(iGRU)%hru(jHRU)%var(iLookATTR%longitude) + end do + end do + + end subroutine get_latlons + + + function initialize(dir) RESULT(istat) bind(c, name="init_summa") + use, intrinsic :: iso_c_binding + character(kind=c_char), dimension(*), intent(in), optional :: dir + integer(kind=c_int) :: istat + integer :: i + + istat=0 + + ! ***************************************************************************** + ! *** inital priming -- get command line arguments, identify files, etc. + ! ***************************************************************************** + + ! initialize the Jacobian flag + doJacobian=.false. ! initialize the Jacobian flag + ncid(:) = integerMissing ! initialize netcdf file id + + ! if initialize is called with an argument pointing to the file manager file + if (present(dir)) then + do i=1,256 + if (dir(i) == C_NULL_CHAR) then + exit + end if + summaFileManagerFile(i:i)=dir(i) ! copying character array to string + end do + endif + + ! get the command line arguments + ! if using the BMI interface, make sure summaFileManagerFile before executing getCommandArguments + call getCommandArguments() + + ! define double precision NaNs (shared in globalData) + dNaN = ieee_value(1._dp, ieee_quiet_nan) + + ! initialize the elapsed time + elapsedRead=0._dp + elapsedWrite=0._dp + elapsedPhysics=0._dp + + ! get the initial time + call date_and_time(values=ctime1) + print "(A,I2.2,':',I2.2,':',I2.2)", 'start at ',ctime1(5:7) + + ! initialize the start of the initialization + call date_and_time(values=startInit) + + ! set directories and files -- summaFileManager used as command-line argument + call summa_SetDirsUndPhiles(summaFileManagerFile,err,message); call handle_err(err,message) + + ! allocate time structures + call allocLocal(time_meta, refTime, err=err, message=message); call handle_err(err,message) ! reference time for the model simulation + call allocLocal(time_meta, startTime, err=err, message=message); call handle_err(err,message) ! start time for the model simulation + call allocLocal(time_meta, finshTime, err=err, message=message); call handle_err(err,message) ! end time for the model simulation + + ! ***************************************************************************** + ! *** populate/check metadata structures + ! ***************************************************************************** + + ! populate metadata for all model variables + call popMetadat(err,message); call handle_err(err,message) + + ! define mapping between fluxes and states + call flxMapping(err,message); call handle_err(err,message) + + ! check data structures + call checkStruc(err,message); call handle_err(err,message) + + ! define the mask to identify the subset of variables in the "child" data structure (just scalar variables) + flux_mask = (flux_meta(:)%vartype==iLookVarType%scalarv) + + ! create the averageFlux metadata structure + call childStruc(flux_meta, flux_mask, averageFlux_meta, childFLUX_MEAN, err, message) + call handle_err(err,message) + + ! ***************************************************************************** + ! *** read the number of GRUs and HRUs, and allocate the gru-hru mapping structures + ! ***************************************************************************** + ! obtain the HRU and GRU dimensions in the LocalAttribute file + attrFile = trim(SETNGS_PATH)//trim(LOCAL_ATTRIBUTES) + select case (iRunMode) + case(iRunModeFull); call read_dimension(trim(attrFile),fileGRU,fileHRU,nGRU,nHRU,err,message) + case(iRunModeGRU ); call read_dimension(trim(attrFile),fileGRU,fileHRU,nGRU,nHRU,err,message,startGRU=startGRU) + case(iRunModeHRU ); call read_dimension(trim(attrFile),fileGRU,fileHRU,nGRU,nHRU,err,message,checkHRU=checkHRU) + end select + call handle_err(err,message) + + ! ***************************************************************************** + ! *** read model attributes + ! ***************************************************************************** + ! read number of snow and soil layers + restartFile = trim(SETNGS_PATH)//trim(MODEL_INITCOND) + call read_icond_nlayers(trim(restartFile),nGRU,indx_meta,err,message) + call handle_err(err,message) + + ! ***************************************************************************** + ! *** allocate space for other data structures + ! ***************************************************************************** + ! loop through data structures + do iStruct=1,size(structInfo) + ! allocate space + select case(trim(structInfo(iStruct)%structName)) + case('time'); call allocGlobal(time_meta, timeStruct, err, message) ! model forcing data + case('forc'); call allocGlobal(forc_meta, forcStruct, err, message) ! model forcing data + case('attr'); call allocGlobal(attr_meta, attrStruct, err, message) ! local attributes for each HRU + case('type'); call allocGlobal(type_meta, typeStruct, err, message) ! local classification of soil veg etc. for each HRU + case('mpar'); call allocGlobal(mpar_meta, mparStruct, err, message) ! model parameters + case('indx'); call allocGlobal(indx_meta, indxStruct, err, message) ! model variables + case('prog'); call allocGlobal(prog_meta, progStruct, err, message) ! model prognostic (state) variables + case('diag'); call allocGlobal(diag_meta, diagStruct, err, message) ! model diagnostic variables + case('flux'); call allocGlobal(flux_meta, fluxStruct, err, message) ! model fluxes + case('bpar'); call allocGlobal(bpar_meta, bparStruct, err, message) ! basin-average parameters + case('bvar'); call allocGlobal(bvar_meta, bvarStruct, err, message) ! basin-average variables + case('deriv'); cycle + case default; err=20; message='unable to find structure name: '//trim(structInfo(iStruct)%structName) + end select + ! check errors + call handle_err(err,trim(message)//'[structure = '//trim(structInfo(iStruct)%structName)//']') + end do ! looping through data structures + + ! ***************************************************************************** + ! *** allocate space for other data structures + ! allocate space for default model parameters + ! NOTE: This is done here, rather than in the loop above, because dpar is not one of the "standard" data structures + ! ***************************************************************************** + call allocGlobal(mpar_meta,dparStruct,err,message) ! default model parameters + call handle_err(err,trim(message)//' [problem allocating dparStruct]') + + ! allocate space for the time step and computeVegFlux flags (recycled for each GRU for subsequent calls to coupled_em) + allocate(dt_init(nGRU),upArea(nGRU),computeVegFlux(nGRU),stat=err) + call handle_err(err,'problem allocating space for dt_init, upArea, or computeVegFlux [GRU]') + + ! allocate space for the HRUs + do iGRU=1,nGRU + hruCount = gru_struc(iGRU)%hruCount + allocate(dt_init(iGRU)%hru(hruCount),upArea(iGRU)%hru(hruCount),computeVegFlux(iGRU)%hru(hruCount),stat=err) + call handle_err(err,'problem allocating space for dt_init, upArea, or computeVegFlux [HRU]') + end do + + ! ***************************************************************************** + ! *** read local attributes for each HRU + ! ***************************************************************************** + call read_attrb(trim(attrFile),nGRU,attrStruct,typeStruct,err,message) + call handle_err(err,message) + + ! get the number of HRUs in the run domain + nHRUrun = sum(gru_struc%hruCount) + + ! ***************************************************************************** + ! *** read description of model forcing datafile used in each HRU + ! ***************************************************************************** + call ffile_info(nGRU,err,message); call handle_err(err,message) + + ! ***************************************************************************** + ! *** read model decisions + ! ***************************************************************************** + call mDecisions(err,message); call handle_err(err,message) + + ! get the maximum number of snow layers + select case(model_decisions(iLookDECISIONS%snowLayers)%iDecision) + case(sameRulesAllLayers); maxSnowLayers = 100 + case(rulesDependLayerIndex); maxSnowLayers = 5 + case default; call handle_err(20,'unable to identify option to combine/sub-divide snow layers') + end select ! (option to combine/sub-divide snow layers) + + ! get the maximum number of layers + maxLayers = gru_struc(1)%hruInfo(1)%nSoil + maxSnowLayers + + ! ***************************************************************************** + ! *** allocate space for output statistics data structures + ! ***************************************************************************** + + ! child metadata structures - so that we do not carry full stats structures around everywhere + ! only carry stats for variables with output frequency > model time step + statForc_mask = (forc_meta(:)%vartype==iLookVarType%scalarv.and.forc_meta(:)%varDesire) + statProg_mask = (prog_meta(:)%vartype==iLookVarType%scalarv.and.prog_meta(:)%varDesire) + statDiag_mask = (diag_meta(:)%vartype==iLookVarType%scalarv.and.diag_meta(:)%varDesire) + statFlux_mask = (flux_meta(:)%vartype==iLookVarType%scalarv.and.flux_meta(:)%varDesire) + statIndx_mask = (indx_meta(:)%vartype==iLookVarType%scalarv.and.indx_meta(:)%varDesire) + statBvar_mask = (bvar_meta(:)%vartype==iLookVarType%scalarv.and.bvar_meta(:)%varDesire) + + ! create the stats metadata structures + do iStruct=1,size(structInfo) + select case (trim(structInfo(iStruct)%structName)) + case('forc'); call childStruc(forc_meta,statForc_mask,statForc_meta,forcChild_map,err,message) + case('prog'); call childStruc(prog_meta,statProg_mask,statProg_meta,progChild_map,err,message) + case('diag'); call childStruc(diag_meta,statDiag_mask,statDiag_meta,diagChild_map,err,message) + case('flux'); call childStruc(flux_meta,statFlux_mask,statFlux_meta,fluxChild_map,err,message) + case('indx'); call childStruc(indx_meta,statIndx_mask,statIndx_meta,indxChild_map,err,message) + case('bvar'); call childStruc(bvar_meta,statBvar_mask,statBvar_meta,bvarChild_map,err,message) + end select + ! check errors + call handle_err(err,trim(message)//'[statistics for = '//trim(structInfo(iStruct)%structName)//']') + end do ! iStruct + + ! set all stats metadata to correct var types + statForc_meta(:)%vartype = iLookVarType%outstat + statProg_meta(:)%vartype = iLookVarType%outstat + statDiag_meta(:)%vartype = iLookVarType%outstat + statFlux_meta(:)%vartype = iLookVarType%outstat + statIndx_meta(:)%vartype = iLookVarType%outstat + statBvar_meta(:)%vartype = iLookVarType%outstat + + ! loop through data structures + do iStruct=1,size(structInfo) + + ! allocate space + select case(trim(structInfo(iStruct)%structName)) + case('forc'); call allocGlobal(statForc_meta(:)%var_info,forcStat,err,message) ! model forcing data + case('prog'); call allocGlobal(statProg_meta(:)%var_info,progStat,err,message) ! model prognostic (state) variables + case('diag'); call allocGlobal(statDiag_meta(:)%var_info,diagStat,err,message) ! model diagnostic variables + case('flux'); call allocGlobal(statFlux_meta(:)%var_info,fluxStat,err,message) ! model fluxes + case('indx'); call allocGlobal(statIndx_meta(:)%var_info,indxStat,err,message) ! index vars + case('bvar'); call allocGlobal(statBvar_meta(:)%var_info,bvarStat,err,message) ! basin-average variables + case default; cycle + end select + + ! check errors + call handle_err(err,trim(message)//'[statistics for = '//trim(structInfo(iStruct)%structName)//']') + + end do ! iStruct + + ! ***************************************************************************** + ! *** read default model parameters + ! ***************************************************************************** + ! read default values and constraints for model parameters (local column, and basin-average) + call read_pinit(LOCALPARAM_INFO,.TRUE., mpar_meta,localParFallback,err,message); call handle_err(err,message) + call read_pinit(BASINPARAM_INFO,.FALSE.,bpar_meta,basinParFallback,err,message); call handle_err(err,message) + + ! ***************************************************************************** + ! *** read Noah vegetation and soil tables + ! ***************************************************************************** + ! define monthly fraction of green vegetation + greenVegFrac_monthly = (/0.01_dp, 0.02_dp, 0.03_dp, 0.07_dp, 0.50_dp, 0.90_dp, 0.95_dp, 0.96_dp, 0.65_dp, 0.24_dp, 0.11_dp, 0.02_dp/) + + ! read Noah soil and vegetation tables + call soil_veg_gen_parm(trim(SETNGS_PATH)//'VEGPARM.TBL', & ! filename for vegetation table + trim(SETNGS_PATH)//'SOILPARM.TBL', & ! filename for soils table + trim(SETNGS_PATH)//'GENPARM.TBL', & ! filename for general table + trim(model_decisions(iLookDECISIONS%vegeParTbl)%cDecision), & ! classification system used for vegetation + trim(model_decisions(iLookDECISIONS%soilCatTbl)%cDecision)) ! classification system used for soils + + ! read Noah-MP vegetation tables + call read_mp_veg_parameters(trim(SETNGS_PATH)//'MPTABLE.TBL', & ! filename for Noah-MP table + trim(model_decisions(iLookDECISIONS%vegeParTbl)%cDecision)) ! classification system used for vegetation + + ! define urban vegetation category + select case(trim(model_decisions(iLookDECISIONS%vegeParTbl)%cDecision)) + case('USGS'); urbanVegCategory = 1 + case('MODIFIED_IGBP_MODIS_NOAH'); urbanVegCategory = 13 + case('plumberCABLE'); urbanVegCategory = -999 + case('plumberCHTESSEL'); urbanVegCategory = -999 + case('plumberSUMMA'); urbanVegCategory = -999 + case default; call handle_err(30,'unable to identify vegetation category') + end select + + ! set default model parameters + do iGRU=1,nGRU + do iHRU=1,gru_struc(iGRU)%hruCount + ! set parmameters to their default value + dparStruct%gru(iGRU)%hru(iHRU)%var(:) = localParFallback(:)%default_val ! x%hru(:)%var(:) + ! overwrite default model parameters with information from the Noah-MP tables + call pOverwrite(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex), & ! vegetation category + typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%soilTypeIndex), & ! soil category + dparStruct%gru(iGRU)%hru(iHRU)%var, & ! default model parameters + err,message); call handle_err(err,message) ! error control + ! copy over to the parameter structure + ! NOTE: constant for the dat(:) dimension (normally depth) + do ivar=1,size(localParFallback) + mparStruct%gru(iGRU)%hru(iHRU)%var(ivar)%dat(:) = dparStruct%gru(iGRU)%hru(iHRU)%var(ivar) + end do ! looping through variables + end do ! looping through HRUs + ! set default for basin-average parameters + bparStruct%gru(iGRU)%var(:) = basinParFallback(:)%default_val + end do ! looping through GRUs + + ! ***************************************************************************** + ! *** read trial model parameter values for each HRU, and populate initial data structures + ! ***************************************************************************** + call read_param(iRunMode,checkHRU,startGRU,nHRU,nGRU,typeStruct,mparStruct,bparStruct,err,message); call handle_err(err,message) + + ! ***************************************************************************** + ! *** compute derived model variables that are pretty much constant for the basin as a whole + ! ***************************************************************************** + ! loop through GRUs + do iGRU=1,nGRU + + ! calculate the fraction of runoff in future time steps + call fracFuture(bparStruct%gru(iGRU)%var, & ! vector of basin-average model parameters + bvarStruct%gru(iGRU), & ! data structure of basin-average variables + err,message) ! error control + call handle_err(err,message) + + ! loop through local HRUs + do iHRU=1,gru_struc(iGRU)%hruCount + + kHRU=0 + ! check the network topology (only expect there to be one downslope HRU) + do jHRU=1,gru_struc(iGRU)%hruCount + if(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%downHRUindex) == typeStruct%gru(iGRU)%hru(jHRU)%var(iLookTYPE%hruId))then + if(kHRU==0)then ! check there is a unique match + kHRU=jHRU + else + call handle_err(20,'multi_driver: only expect there to be one downslope HRU') + end if ! (check there is a unique match) + end if ! (if identified a downslope HRU) + end do + + ! check that the parameters are consistent + call paramCheck(mparStruct%gru(iGRU)%hru(iHRU),err,message); call handle_err(err,message) + + ! calculate a look-up table for the temperature-enthalpy conversion + call E2T_lookup(mparStruct%gru(iGRU)%hru(iHRU),err,message); call handle_err(err,message) + + end do ! HRU + end do ! GRU + + ! read description of model initial conditions -- also initializes model structure components + call read_icond(restartFile, & ! intent(in): name of initial conditions file + nGRU, & ! intent(in): number of response units + mparStruct, & ! intent(in): model parameters + progStruct, & ! intent(inout): model prognostic variables + indxStruct, & ! intent(inout): model indices + err,message) ! intent(out): error control + call handle_err(err,message) + + ! check initial conditions + call check_icond(nGRU, & ! number of response units + progStruct, & ! model prognostic (state) variables + mparStruct, & ! model parameters + indxStruct, & ! layer indexes + err,message) ! error control + call handle_err(err,message) + + ! loop through GRUs + do iGRU=1,nGRU + ! loop through local HRUs + do iHRU=1,gru_struc(iGRU)%hruCount + + ! re-calculate height of each layer + call calcHeight(& + ! input/output: data structures + indxStruct%gru(iGRU)%hru(iHRU), & ! intent(in): layer type + progStruct%gru(iGRU)%hru(iHRU), & ! intent(inout): model prognostic (state) variables for a local HRU + ! output: error control + err,message); call handle_err(err,message) + + ! calculate vertical distribution of root density + call rootDensty(mparStruct%gru(iGRU)%hru(iHRU), & ! vector of model parameters + indxStruct%gru(iGRU)%hru(iHRU), & ! data structure of model indices + progStruct%gru(iGRU)%hru(iHRU), & ! data structure of model prognostic (state) variables + diagStruct%gru(iGRU)%hru(iHRU), & ! data structure of model diagnostic variables + err,message) ! error control + call handle_err(err,message) + + ! calculate saturated hydraulic conductivity in each soil layer + call satHydCond(mparStruct%gru(iGRU)%hru(iHRU), & ! vector of model parameters + indxStruct%gru(iGRU)%hru(iHRU), & ! data structure of model indices + progStruct%gru(iGRU)%hru(iHRU), & ! data structure of model prognostic (state) variables + fluxStruct%gru(iGRU)%hru(iHRU), & ! data structure of model fluxes + err,message) ! error control + call handle_err(err,message) + + ! calculate "short-cut" variables such as volumetric heat capacity + call v_shortcut(mparStruct%gru(iGRU)%hru(iHRU), & ! vector of model parameters + diagStruct%gru(iGRU)%hru(iHRU), & ! data structure of model diagnostic variables + err,message) ! error control + call handle_err(err,message) + + ! overwrite the vegetation height + HVT(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex)) = mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%heightCanopyTop)%dat(1) + HVB(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex)) = mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%heightCanopyBottom)%dat(1) + + ! overwrite the tables for LAI and SAI + if(model_decisions(iLookDECISIONS%LAI_method)%iDecision == specified)then + SAIM(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex),:) = mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%winterSAI)%dat(1) + LAIM(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex),:) = mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%summerLAI)%dat(1)*greenVegFrac_monthly + endif + + ! initialize canopy drip + ! NOTE: canopy drip from the previous time step is used to compute throughfall for the current time step + fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) = 0._dp ! not used + end do ! (looping through HRUs) + + ! compute total area of the upstream HRUS that flow into each HRU + do iHRU=1,gru_struc(iGRU)%hruCount + upArea(iGRU)%hru(iHRU) = 0._dp + do jHRU=1,gru_struc(iGRU)%hruCount + ! check if jHRU flows into iHRU; assume no exchange between GRUs + if(typeStruct%gru(iGRU)%hru(jHRU)%var(iLookTYPE%downHRUindex)==typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%hruId))then + upArea(iGRU)%hru(iHRU) = upArea(iGRU)%hru(iHRU) + attrStruct%gru(iGRU)%hru(jHRU)%var(iLookATTR%HRUarea) + endif ! (if jHRU is an upstream HRU) + end do ! jHRU + end do ! iHRU + + ! identify the total basin area for a GRU (m2) + associate(totalArea => bvarStruct%gru(iGRU)%var(iLookBVAR%basin__totalArea)%dat(1) ) + totalArea = 0._dp + do iHRU=1,gru_struc(iGRU)%hruCount + totalArea = totalArea + attrStruct%gru(iGRU)%hru(iHRU)%var(iLookATTR%HRUarea) + end do + end associate + + ! initialize aquifer storage + ! NOTE: this is ugly: need to add capabilities to initialize basin-wide state variables + ! There are two options for groundwater: + ! (1) where groundwater is included in the local column (i.e., the HRUs); and + ! (2) where groundwater is included for the single basin (i.e., the GRUS, where multiple HRUS drain into a GRU). + ! For water balance calculations it is important to ensure that the local aquifer storage is zero if groundwater is treated as a basin-average state variable (singleBasin); + ! and ensure that basin-average aquifer storage is zero when groundwater is included in the local columns (localColumn). + select case(model_decisions(iLookDECISIONS%spatial_gw)%iDecision) + ! the basin-average aquifer storage is not used if the groundwater is included in the local column + case(localColumn) + bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 0._dp ! set to zero to be clear that there is no basin-average aquifer storage in this configuration + ! NOTE: the local column aquifer storage is not used if the groundwater is basin-average + ! (i.e., where multiple HRUs drain to a basin-average aquifer) + case(singleBasin) + bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 1._dp + do iHRU=1,gru_struc(iGRU)%hruCount + progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarAquiferStorage)%dat(1) = 0._dp ! set to zero to be clear that there is no local aquifer storage in this configuration + end do + case default; call handle_err(20,'unable to identify decision for regional representation of groundwater') + end select + + ! initialize time step length for each HRU + do iHRU=1,gru_struc(iGRU)%hruCount + dt_init(iGRU)%hru(iHRU) = progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%dt_init)%dat(1) ! seconds + end do + + end do ! (looping through GRUs) + + + ! ***************************************************************************** + ! *** initialize first output sequence + ! ***************************************************************************** + ! define the output file + ! NOTE: currently assumes that nSoil is constant across the model domain + + ! set up the output file names as: OUTPUT_PREFIX_spinup|waterYear_output_fileSuffix_startGRU-endGRU_outfreq.nc or OUTPUT_PREFIX_spinup|waterYear_output_fileSuffix_HRU_outfreq.nc; + if (OUTPUT_PREFIX(len_trim(OUTPUT_PREFIX):len_trim(OUTPUT_PREFIX)) /= '_') OUTPUT_PREFIX=trim(OUTPUT_PREFIX)//'_' ! separate OUTPUT_PREFIX from others by underscore + if (output_fileSuffix(1:1) /= '_') output_fileSuffix='_'//trim(output_fileSuffix) ! separate output_fileSuffix from others by underscores + if (output_fileSuffix(len_trim(output_fileSuffix):len_trim(output_fileSuffix)) == '_') output_fileSuffix(len_trim(output_fileSuffix):len_trim(output_fileSuffix)) = ' ' + select case (iRunMode) + case(iRunModeGRU) + ! left zero padding for startGRU and endGRU + write(fmtGruOutput,"(i0)") ceiling(log10(real(fileGRU)+0.1)) ! maximum width of startGRU and endGRU + fmtGruOutput = "i"//trim(fmtGruOutput)//"."//trim(fmtGruOutput) ! construct the format string for startGRU and endGRU + fmtGruOutput = "('_G',"//trim(fmtGruOutput)//",'-',"//trim(fmtGruOutput)//")" + write(output_fileSuffix((len_trim(output_fileSuffix)+1):len(output_fileSuffix)),fmtGruOutput) startGRU,startGRU+nGRU-1 + case(iRunModeHRU) + write(output_fileSuffix((len_trim(output_fileSuffix)+1):len(output_fileSuffix)),"('_H',i0)") checkHRU + end select + + ! define file output + select case(newOutputFile) + case(noNewFiles); fileout = trim(OUTPUT_PATH)//trim(OUTPUT_PREFIX)//'output'//trim(output_fileSuffix) + case default ; fileout = trim(OUTPUT_PATH)//trim(OUTPUT_PREFIX)//'spinup'//trim(output_fileSuffix) + end select + call def_output(summaVersion,buildTime,gitBranch,gitHash,nGRU,nHRU,gru_struc(1)%hruInfo(1)%nSoil,fileout,err,message) + call handle_err(err,message) + + ! write local model attributes and parameters to the model output file + do iGRU=1,nGRU + do iHRU=1,gru_struc(iGRU)%hruCount + call writeParm(gru_struc(iGRU)%hruInfo(iHRU)%hru_ix,attrStruct%gru(iGRU)%hru(iHRU),attr_meta,err,message); call handle_err(err,'[attr]/'//message) + call writeParm(gru_struc(iGRU)%hruInfo(iHRU)%hru_ix,typeStruct%gru(iGRU)%hru(iHRU),type_meta,err,message); call handle_err(err,'[type]/'//message) + call writeParm(gru_struc(iGRU)%hruInfo(iHRU)%hru_ix,mparStruct%gru(iGRU)%hru(iHRU),mpar_meta,err,message); call handle_err(err,'[mpar]'//message) + enddo ! HRU + call writeParm(iGRU,bparStruct%gru(iGRU),bpar_meta,err,message); call handle_err(err,'[bpar]/'//message) + end do ! GRU + + ! identify the end of the initialization + call date_and_time(values=endInit) + + ! aggregate the elapsed time for the initialization + elapsedInit = elapsedSec(startInit, endInit) + + ! initialize time step index + statCounter(1:maxVarFreq) = 1 + outputTimeStep(1:maxVarFreq) = 1 + + ! initialize flags to reset/finalize statistics + resetStats(:) = .true. ! start by resetting statistics + finalizeStats(:) = .false. ! do not finalize stats on the first time step + + ! set stats flag for the timestep-level output + finalizeStats(iLookFreq%timestep)=.true. + + ! allocate space for GRU timing + allocate(totalFluxCalls(nGRU), timeGRU(nGRU), timeGRUstart(nGRU), timeGRUcompleted(nGRU), ixExpense(nGRU), stat=err) + call handle_err(err,'unable to allocate space for GRU timing') + timeGRU(:) = realMissing ! initialize because used for ranking + istat = update() + +end function initialize + + +! **************************************************************************** +! *** loop through time until +! **************************************************************************** + +function update_until(tend) result(istat) bind(c, name="update_summa_until") + use, intrinsic :: iso_c_binding + real(kind=c_float),intent(in) :: tend ! unit days (julian days, same as get_model_time) + integer(kind=c_int) :: istat + + real(kind=c_float) current + current = get_current_time() + istat=0 + + ! loop through time + if (modelTimeStep == 0) then + modelTimeStep = 1 + end if + + do while (current < tend .and. modelTimeStep < numtim) + + istat = update() + current = get_current_time() + + modelTimeStep = modelTimeStep + 1 + + end do ! (looping through time) +end function + + +! **************************************************************************** +! *** loop through time +! **************************************************************************** + +FUNCTION update() RESULT(istat) bind(c, name="update_summa") + USE, INTRINSIC :: ISO_C_BINDING + integer(kind=c_int) :: istat + + istat=0 + + ! initialize the start of the data read + call date_and_time(values=startRead) + + ! read forcing data + print*, modelTimeStep + call read_force(& + ! input + modelTimeStep, & ! intent(in): time step index + ! input-output + iFile, & ! intent(inout): index of current forcing file in forcing file list + forcingStep, & ! intent(inout): index of read position in time dimension in current netcdf file + forcNcid, & ! intent(inout): netcdf file identifier for the current forcing file + ! output + timeStruct%var, & ! intent(out): time data structure (integer) + forcStruct, & ! intent(out): forcing data structure (double precision) + err, message) ! intent(out): error control + call handle_err(err,message) + + ! identify the end of the data read + call date_and_time(values=endRead) + + ! aggregate the elapsed time for the data read + elapsedRead = elapsedRead + elapsedSec(startRead, endRead) + + ! set print flag + globalPrintFlag=.false. + + ! reset output counters/flags + if(modelTimeStep>1)then + do iFreq=1,maxVarFreq ! loop through output frequencies + + ! define the need to finalize statistics + ! NOTE: time vector is configured so that ih=0 at the start of the day, hence day in oldTime and timeStruct%var differ + select case(iFreq) + case(iLookFreq%day ); finalizeStats(iFreq)=(oldTimeVec(iLookTime%id )/=timeStruct%var(iLookTime%id )) ! daily aggregation + case(iLookFreq%month ); finalizeStats(iFreq)=(oldTimeVec(iLookTime%im )/=timeStruct%var(iLookTime%im )) ! monthly aggregation + case(iLookFreq%annual ); finalizeStats(iFreq)=(oldTimeVec(iLookTime%iyyy)/=timeStruct%var(iLookTime%iyyy)) ! yearly (annual) aggregation + case(iLookFreq%timestep); finalizeStats(iFreq)=.true. ! timestep-level output (no temporal aggregation) + case default; call handle_err(20,'unable to identify output frequency') + end select + + ! reset ouput timestep + if(resetStats(iFreq)) statCounter(iFreq)=1 + + end do ! looping through output frequencies + endif ! if modelTimeStep>1 + + ! print progress + select case(ixProgress) + case(ixProgress_im); printProgress = (timeStruct%var(iLookTIME%id) == 1 .and. timeStruct%var(iLookTIME%ih) == 0 .and. timeStruct%var(iLookTIME%imin) == 0) + case(ixProgress_id); printProgress = (timeStruct%var(iLookTIME%ih) == 0 .and. timeStruct%var(iLookTIME%imin) == 0) + case(ixProgress_ih); printProgress = (timeStruct%var(iLookTIME%imin) == 0) + case(ixProgress_never); printProgress = .false. + case default; call handle_err(20,'unable to identify option for the restart file') + end select + if(printProgress) write(*,'(i4,1x,5(i2,1x))') timeStruct%var + ! write(*,'(i4,1x,5(i2,1x))') timeStruct%var + + ! NOTE: this is done because of the check in coupled_em if computeVegFlux changes in subsequent time steps + ! (if computeVegFlux changes, then the number of state variables changes, and we need to reoranize the data structures) + ! compute the exposed LAI and SAI and whether veg is buried by snow + if(modelTimeStep==1)then + do iGRU=1,nGRU + do iHRU=1,gru_struc(iGRU)%hruCount + + ! get vegetation phenology + call vegPhenlgy(& + ! input/output: data structures + model_decisions, & ! intent(in): model decisions + typeStruct%gru(iGRU)%hru(iHRU), & ! intent(in): type of vegetation and soil + attrStruct%gru(iGRU)%hru(iHRU), & ! intent(in): spatial attributes + mparStruct%gru(iGRU)%hru(iHRU), & ! intent(in): model parameters + progStruct%gru(iGRU)%hru(iHRU), & ! intent(in): model prognostic variables for a local HRU + diagStruct%gru(iGRU)%hru(iHRU), & ! intent(inout): model diagnostic variables for a local HRU + ! output + computeVegFluxFlag, & ! intent(out): flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) + notUsed_canopyDepth, & ! intent(out): NOT USED: canopy depth (m) + notUsed_exposedVAI, & ! intent(out): NOT USED: exposed vegetation area index (m2 m-2) + err,message) ! intent(out): error control + call handle_err(err,message) + + ! save the flag for computing the vegetation fluxes + if(computeVegFluxFlag) computeVegFlux(iGRU)%hru(iHRU) = yes + if(.not.computeVegFluxFlag) computeVegFlux(iGRU)%hru(iHRU) = no + + ! define the green vegetation fraction of the grid box (used to compute LAI) + diagStruct%gru(iGRU)%hru(iHRU)%var(iLookDIAG%scalarGreenVegFraction)%dat(1) = greenVegFrac_monthly(timeStruct%var(iLookTIME%im)) + + end do ! looping through HRUs + end do ! looping through GRUs + end if ! if the first time step + + ! **************************************************************************** + ! *** model simulation + ! **************************************************************************** + + ! initialize the start of the physics + call date_and_time(values=startPhysics) + + ! ----- rank the GRUs in terms of their anticipated computational expense ----- + + ! estimate computational expense based on persistence + ! -- assume that that expensive GRUs from a previous time step are also expensive in the current time step + + ! compute the total number of flux calls from the previous time step + do jGRU=1,nGRU + totalFluxCalls(jGRU) = 0._dp + do iHRU=1,gru_struc(jGRU)%hruCount + totalFluxCalls(jGRU) = totalFluxCalls(jGRU) + indxStruct%gru(jGRU)%hru(iHRU)%var(iLookINDEX%numberFluxCalc)%dat(1) + end do + end do + + ! get the indices that can rank the computational expense + !call indexx(totalFluxCalls, ixExpense) ! ranking of each GRU w.r.t. computational expense + call indexx(timeGRU, ixExpense) ! ranking of each GRU w.r.t. computational expense + ixExpense=ixExpense(nGRU:1:-1) ! reverse ranking: now largest to smallest + + ! initialize the GRU count + ! NOTE: this needs to be outside the parallel section so it is not reinitialized by different threads + kGRU=0 + + ! initialize the time that the openMP section starts + call system_clock(openMPstart) + + ! ----- use openMP directives to run GRUs in parallel ------------------------- + + ! start of parallel section: define shared and private structure elements + !$omp parallel default(none) & + !$omp private(iGRU, jGRU) & ! GRU indices are private for a given thread + !$omp shared(openMPstart, openMPend, nThreads) & ! access constant variables + !$omp shared(timeGRUstart, timeGRUcompleted, timeGRU, ixExpense, kGRU) & ! time variables shared + !$omp shared(gru_struc, dt_init, computeVegFlux) & ! subroutine inputs + !$omp shared(timeStruct, typeStruct, attrStruct, mparStruct, indxStruct, & + !$omp forcStruct, progStruct, diagStruct, fluxStruct, bvarStruct) & + !$omp private(err, message) & + !$omp firstprivate(nGRU) + + nThreads = 1 + !$ nThreads = omp_get_num_threads() + + ! use dynamic scheduling with chunk size of one: + ! -- new chunks are assigned to threads when they become available + ! -- start with the more expensive GRUs, and add the less expensive GRUs as threads come available + + !$omp do schedule(dynamic, 1) ! chunk size of 1 + do jGRU=1,nGRU ! loop through GRUs + + !----- process GRUs in order of computational expense ------------------------- + + !$omp critical(setGRU) + + ! assign expensive GRUs to threads that enter first + kGRU = kGRU+1 + iGRU = ixExpense(kGRU) + + ! get the time that the GRU started + call system_clock( timeGRUstart(iGRU) ) + + ! print progress + !write(*,'(a,1x,5(i4,1x),f20.10,1x)') 'iGRU, jGRU, kGRU, nThreads = ', iGRU, jGRU, kGRU, nThreads, timeGRU(iGRU) + + !$omp end critical(setGRU) + + !----- run simulation for a single GRU ---------------------------------------- + + call run_oneGRU(& + ! model control + gru_struc(iGRU), & ! intent(inout): HRU information for given GRU (# HRUs, #snow+soil layers) + dt_init(iGRU)%hru, & ! intent(inout): used to initialize the length of the sub-step for each HRU + computeVegFlux(iGRU)%hru, & ! intent(inout): flag to indicate if we are computing fluxes over vegetation (false=no, true=yes) + ! data structures (input) + timeStruct%var, & ! intent(in): model time data + typeStruct%gru(iGRU), & ! intent(in): local classification of soil veg etc. for each HRU + attrStruct%gru(iGRU), & ! intent(in): local attributes for each HRU + ! data structures (input-output) + mparStruct%gru(iGRU), & ! intent(inout): local model parameters + indxStruct%gru(iGRU), & ! intent(inout): model indices + forcStruct%gru(iGRU), & ! intent(inout): model forcing data + progStruct%gru(iGRU), & ! intent(inout): prognostic variables for a local HRU + diagStruct%gru(iGRU), & ! intent(inout): diagnostic variables for a local HRU + fluxStruct%gru(iGRU), & ! intent(inout): model fluxes for a local HRU + bvarStruct%gru(iGRU), & ! intent(inout): basin-average variables + ! error control + err,message) ! intent(out): error control + + !----- save timing information ------------------------------------------------ + + !$omp critical(saveTiming) + + ! check errors + call handle_err(err,message) + + ! save timing information + call system_clock(openMPend) + timeGRU(iGRU) = real(openMPend - timeGRUstart(iGRU), kind(dp)) + timeGRUcompleted(iGRU) = real(openMPend - openMPstart , kind(dp)) + + !$omp end critical(saveTiming) + + end do ! (looping through GRUs) + !$omp end do + !$omp end parallel + + ! identify the end of the physics + call date_and_time(values=endPhysics) + + ! aggregate the elapsed time for the physics + elapsedPhysics = elapsedPhysics + elapsedSec(startPhysics, endPhysics) + + ! pause + !print*, 'driver/PAUSE: timestep '; read(*,*) + + ! **************************************************************************** + ! *** model calculate statistics + ! **************************************************************************** + + ! initialize the start of the data write + call date_and_time(values=startWrite) + + ! loop through GRUs and HRUs + do iGRU=1,nGRU + do iHRU=1,gru_struc(iGRU)%hruCount + + ! calculate output Statistics + call calcStats(forcStat%gru(iGRU)%hru(iHRU)%var,forcStruct%gru(iGRU)%hru(iHRU)%var,statForc_meta,resetStats,finalizeStats,statCounter,err,message); call handle_err(err,message) + call calcStats(progStat%gru(iGRU)%hru(iHRU)%var,progStruct%gru(iGRU)%hru(iHRU)%var,statProg_meta,resetStats,finalizeStats,statCounter,err,message); call handle_err(err,message) + call calcStats(diagStat%gru(iGRU)%hru(iHRU)%var,diagStruct%gru(iGRU)%hru(iHRU)%var,statDiag_meta,resetStats,finalizeStats,statCounter,err,message); call handle_err(err,message) + call calcStats(fluxStat%gru(iGRU)%hru(iHRU)%var,fluxStruct%gru(iGRU)%hru(iHRU)%var,statFlux_meta,resetStats,finalizeStats,statCounter,err,message); call handle_err(err,message) + call calcStats(indxStat%gru(iGRU)%hru(iHRU)%var,indxStruct%gru(iGRU)%hru(iHRU)%var,statIndx_meta,resetStats,finalizeStats,statCounter,err,message); call handle_err(err,message) + + end do ! (looping through HRUs) + + ! calc basin stats + call calcStats(bvarStat%gru(iGRU)%var(:),bvarStruct%gru(iGRU)%var(:),statBvar_meta,resetStats,finalizeStats,statCounter,err,message); call handle_err(err,message) + + ! write basin-average variables + call writeBasin(iGRU,finalizeStats,outputTimeStep,bvar_meta,bvarStat%gru(iGRU)%var,bvarStruct%gru(iGRU)%var,bvarChild_map,err,message); call handle_err(err,message) + + end do ! (looping through GRUs) + + ! write time information + call WriteTime(finalizeStats,outputTimeStep,time_meta,timeStruct%var,err,message) + + ! write the model output to the NetCDF file + ! Passes the full metadata structure rather than the stats metadata structure because + ! we have the option to write out data of types other than statistics. + ! Thus, we must also pass the stats parent->child maps from childStruct. + call writeData(finalizeStats,outputTimeStep,nHRUrun,maxLayers,forc_meta,forcStat,forcStruct,forcChild_map,indxStruct,err,message); call handle_err(err,message) + call writeData(finalizeStats,outputTimeStep,nHRUrun,maxLayers,prog_meta,progStat,progStruct,progChild_map,indxStruct,err,message); call handle_err(err,message) + call writeData(finalizeStats,outputTimeStep,nHRUrun,maxLayers,diag_meta,diagStat,diagStruct,diagChild_map,indxStruct,err,message); call handle_err(err,message) + call writeData(finalizeStats,outputTimeStep,nHRUrun,maxLayers,flux_meta,fluxStat,fluxStruct,fluxChild_map,indxStruct,err,message); call handle_err(err,message) + call writeData(finalizeStats,outputTimeStep,nHRUrun,maxLayers,indx_meta,indxStat,indxStruct,indxChild_map,indxStruct,err,message); call handle_err(err,message) + + ! increment output file timestep + do iFreq = 1,maxvarFreq + statCounter(iFreq) = statCounter(iFreq)+1 + if(finalizeStats(iFreq)) outputTimeStep(iFreq) = outputTimeStep(iFreq) + 1 + end do + + ! increment forcingStep + forcingStep=forcingStep+1 + + ! if finalized stats, then reset stats on the next time step + resetStats(:) = finalizeStats(:) + + ! save time vector + oldTimeVec(:) = timeStruct%var + + ! identify the end of the data write section + call date_and_time(values=endWrite) + + ! aggregate the elapsed time for the stats/writing + elapsedWrite = elapsedWrite + elapsedSec(startWrite, endWrite) + + !print*, 'PAUSE: in driver: testing differences'; read(*,*) + !stop 'end of time step' + + ! ***************************************************************************** + ! *** create a new NetCDF output file, and write parameters and forcing data + ! ***************************************************************************** + + ! define the need to create a new output file + select case(newOutputFile) + ! (don't ever create a new output file) + case(noNewFiles); defNewOutputFile=.false. + ! (check for the start of the USA water year) + case(newFileEveryOct1) + defNewOutputFile = (timeStruct%var(iLookTIME%im) ==10 .and. & ! month = October + timeStruct%var(iLookTIME%id) ==1 .and. & ! day = 1 + timeStruct%var(iLookTIME%ih) ==0 .and. & ! hour = 1 + timeStruct%var(iLookTIME%imin)==0) ! minute = 0 + ! (check that we found the option) + case default; call handle_err(20,'unable to identify the option to define new output files') + end select + + ! create hte new output file + if(defNewOutputFile)then + + ! close any output files that are already open + do iFreq = 1,maxvarFreq + if (ncid(iFreq)/=integerMissing) then + call nc_file_close(ncid(iFreq),err,message) + call handle_err(err,message) + end if + end do + + ! define the filename + write(fileout,'(a,i0,a,i0,a)') trim(OUTPUT_PATH)//trim(OUTPUT_PREFIX),& + timeStruct%var(iLookTIME%iyyy),'-',timeStruct%var(iLookTIME%iyyy)+1,& + trim(output_fileSuffix) + + ! define the file + call def_output(summaVersion,buildTime,gitBranch,gitHash,nGRU,nHRU,gru_struc(1)%hruInfo(1)%nSoil,fileout,err,message) + call handle_err(err,message) + + ! write parameters for each HRU, and re-set indices + do iGRU=1,nGRU + do iHRU=1,gru_struc(iGRU)%hruCount + call writeParm(gru_struc(iGRU)%hruInfo(iHRU)%hru_ix,attrStruct%gru(iGRU)%hru(iHRU),attr_meta,err,message); call handle_err(err,'[attr]/'//message) + call writeParm(gru_struc(iGRU)%hruInfo(iHRU)%hru_ix,typeStruct%gru(iGRU)%hru(iHRU),type_meta,err,message); call handle_err(err,'[type]/'//message) + call writeParm(gru_struc(iGRU)%hruInfo(iHRU)%hru_ix,mparStruct%gru(iGRU)%hru(iHRU),mpar_meta,err,message); call handle_err(err,'[mpar]'//message) + ! re-initalize the indices for model writing + outputTimeStep(:)=1 + end do ! (looping through HRUs) + call writeParm(integerMissing,bparStruct%gru(iGRU),bpar_meta,err,message); call handle_err(err,message) + end do ! (looping through GRUs) + + end if ! if defining a new file + + ! ***************************************************************************** + ! *** write restart file + ! ***************************************************************************** + + ! query whether this timestep requires a re-start file + select case(ixRestart) + case(ixRestart_iy); printRestart = (timeStruct%var(iLookTIME%im) == 1 .and. timeStruct%var(iLookTIME%id) == 1 .and. timeStruct%var(iLookTIME%ih) == 0 .and. timeStruct%var(iLookTIME%imin) == 0) + case(ixRestart_im); printRestart = (timeStruct%var(iLookTIME%id) == 1 .and. timeStruct%var(iLookTIME%ih) == 0 .and. timeStruct%var(iLookTIME%imin) == 0) + case(ixRestart_id); printRestart = (timeStruct%var(iLookTIME%ih) == 0 .and. timeStruct%var(iLookTIME%imin) == 0) + case(ixRestart_end); printRestart = (timeStruct%var(iLookTIME%im) == finshTime%var(2) .and. timeStruct%var(iLookTIME%id) == finshTime%var(3) .and. timeStruct%var(iLookTIME%ih) == finshTime%var(4) .and. timeStruct%var(iLookTIME%imin) == finshTime%var(5)) + case(ixRestart_never); printRestart = .false. + case default; call handle_err(20,'unable to identify option for the restart file') + end select + + ! print a restart file if requested + if(printRestart)then + write(timeString,'(a,i4,3(a,i2.2))') '_',timeStruct%var(iLookTIME%iyyy),'-',timeStruct%var(iLookTIME%im),'-',timeStruct%var(iLookTIME%id),'-',timeStruct%var(iLookTIME%ih) + restartFile=trim(OUTPUT_PATH)//trim(OUTPUT_PREFIX)//'_'//trim('summaRestart')//trim(timeString)//trim(output_fileSuffix)//'.nc' + call writeRestart(restartFile,nGRU,nHRU,prog_meta,progStruct,maxLayers,maxSnowLayers,indx_meta,indxStruct,err,message) + call handle_err(err,message) + end if + + +end function update + + +FUNCTION finalize() RESULT(istat) bind(c, name="finalize_summa") + +USE, INTRINSIC :: ISO_C_BINDING +integer(kind=c_int) :: istat + +istat=0 + + + ! close any remaining output files + do iFreq = 1,maxvarFreq + if (ncid(iFreq).ne.integerMissing) then + call nc_file_close(ncid(iFreq),err,message) + call handle_err(err,message) + end if + end do + + ! deallocate space used to determine the GRU computational expense + deallocate(totalFluxCalls, ixExpense, timeGRU, stat=err) + call handle_err(err,'unable to deallocate space for GRU timing') + + ! deallocate space for dt_init and upArea + deallocate(dt_init,upArea,stat=err); call handle_err(err,'unable to deallocate space for dt_init and upArea') + + call stop_program('finished simulation successfully.') + +end function finalize + + + ! ************************************************************************************************** + ! internal function to obtain the command line arguments + ! ************************************************************************************************** + subroutine getCommandArguments() + implicit none + integer(i4b) :: iArgument ! index of command line argument + integer(i4b) :: nArgument ! number of command line arguments + character(len=256),allocatable :: argString(:) ! string to store command line arguments + integer(i4b) :: nLocalArgument ! number of command line arguments to read for a switch + character(len=70), parameter :: spaces = '' + nArgument = command_argument_count() + ! check numbers of command-line arguments and obtain all arguments + if (nArgument < 1) then + call printCommandHelp() + end if + + allocate(argString(nArgument)) + do iArgument = 1,nArgument + call get_command_argument(iArgument,argString(iArgument)) + ! print versions if needed + if (trim(argString(iArgument)) == '-v' .or. trim(argString(iArgument)) == '--version') then + ! print version numbers + + print "(A)", '----------------------------------------------------------------------' + print "(A)", ' SUMMA - Structure for Unifying Multiple Modeling Alternatives ' + print "(A)", spaces(1:int((70 - len_trim(summaVersion) - 9) / 2))//'Version: ' //trim(summaVersion) + print "(A)", spaces(1:int((70 - len_trim(buildTime) - 12) / 2)) //'Build Time: '//trim(buildTime) + print "(A)", spaces(1:int((70 - len_trim(gitBranch) - 12) / 2)) //'Git Branch: '//trim(gitBranch) + print "(A)", spaces(1:int((70 - len_trim(gitHash) - 10) / 2)) //'Git Hash: ' //trim(gitHash) + print "(A)", '----------------------------------------------------------------------' + if (nArgument == 1) stop + end if + end do + + ! initialize command line argument variables + startGRU = integerMissing; checkHRU = integerMissing + nGRU = integerMissing; nHRU = integerMissing + newOutputFile = noNewFiles + iRunMode = iRunModeFull + + ! loop through all command arguments + nLocalArgument = 0 + do iArgument = 1,nArgument + if (nLocalArgument>0) then; nLocalArgument = nLocalArgument -1; cycle; end if ! skip the arguments have been read + select case (trim(argString(iArgument))) + + case ('-m', '--master') + ! update arguments + nLocalArgument = 1 + if (iArgument+nLocalArgument>nArgument) call handle_err(1,"missing argument file_suffix; type 'summa.exe --help' for correct usage") + ! get name of master control file + summaFileManagerFile=trim(argString(iArgument+1)) + print "(A)", "file_master is '"//trim(summaFileManagerFile)//"'." + + ! define the formation of new output files + case ('-n', '--newFile') + ! check that the number of command line arguments is correct + nLocalArgument = 1 ! expect just one argument for new output files + if (iArgument+nLocalArgument>nArgument) call handle_err(1,"missing argument file_suffix; type 'summa.exe --help' for correct usage") + ! get the decision for the formation of new output files + select case( trim(argString(iArgument+1)) ) + case('noNewFiles'); newOutputFile = noNewFiles + case('newFileEveryOct1'); newOutputFile = newFileEveryOct1 + case default; call handle_err(1,'unknown option for new output file: expect "noNewFiles" or "newFileEveryOct1"') + end select + + case ('-s', '--suffix') + ! define file suffix + nLocalArgument = 1 + ! check if the number of command line arguments is correct + if (iArgument+nLocalArgument>nArgument) call handle_err(1,"missing argument file_suffix; type 'summa.exe --help' for correct usage") + output_fileSuffix=trim(argString(iArgument+1)) + print "(A)", "file_suffix is '"//trim(output_fileSuffix)//"'." + + case ('-h', '--hru') + ! define a single HRU run + if (iRunMode == iRunModeGRU) call handle_err(1,"single-HRU run and GRU-parallelization run cannot be both selected.") + iRunMode=iRunModeHRU + nLocalArgument = 1 + ! check if the number of command line arguments is correct + if (iArgument+nLocalArgument>nArgument) call handle_err(1,"missing argument checkHRU; type 'summa.exe --help' for correct usage") + read(argString(iArgument+1),*) checkHRU ! read the index of the HRU for a single HRU run + nHRU=1; nGRU=1 ! nHRU and nGRU are both one in this case + ! examines the checkHRU is correct + if (checkHRU<1) then + call handle_err(1,"illegal iHRU specification; type 'summa.exe --help' for correct usage") + else + print '(A)',' Single-HRU run activated. HRU '//trim(argString(iArgument+1))//' is selected for simulation.' + end if + + case ('-g','--gru') + ! define a GRU parallelization run; get the starting GRU and countGRU + if (iRunMode == iRunModeHRU) call handle_err(1,"single-HRU run and GRU-parallelization run cannot be both selected.") + iRunMode=iRunModeGRU + nLocalArgument = 2 + ! check if the number of command line arguments is correct + if (iArgument+nLocalArgument>nArgument) call handle_err(1,"missing argument startGRU or countGRU; type 'summa.exe --help' for correct usage") + read(argString(iArgument+1),*) startGRU ! read the argument of startGRU + read(argString(iArgument+2),*) nGRU ! read the argument of countGRU + if (startGRU<1 .or. nGRU<1) then + call handle_err(1,'startGRU and countGRU must be larger than 1.') + else + print '(A)', ' GRU-Parallelization run activated. '//trim(argString(iArgument+2))//' GRUs are selected for simulation.' + end if + + case ('-p', '--progress') + ! define the frequency to print progress + nLocalArgument = 1 + ! check if the number of command line arguments is correct + if (iArgument+nLocalArgument>nArgument) call handle_err(1, "missing argument freqProgress; type 'summa.exe --help' for correct usage") + select case (trim(argString(iArgument+1))) + case ('m' , 'month'); ixProgress = ixProgress_im + case ('d' , 'day'); ixProgress = ixProgress_id + case ('h' , 'hour'); ixProgress = ixProgress_ih + case ('n' , 'never'); ixProgress = ixProgress_never + case default; call handle_err(1,'unknown frequency to print progress') + end select + + case ('-r', '--restart') + ! define the frequency to write restart files + nLocalArgument = 1 + ! check if the number of command line arguments is correct + if (iArgument+nLocalArgument>nArgument) call handle_err(1, "missing argument freqRestart; type 'summa.exe --help' for correct usage") + select case (trim(argString(iArgument+1))) + case ('y' , 'year'); ixRestart = ixRestart_iy + case ('m' , 'month'); ixRestart = ixRestart_im + case ('d' , 'day'); ixRestart = ixRestart_id + case ('e' , 'end'); ixRestart = ixRestart_end + case ('n' , 'never'); ixRestart = ixRestart_never + case default; call handle_err(1,'unknown frequency to write restart files') + end select + + ! do nothing + case ('-v','--version') + + ! print help message + case ('--help') + call printCommandHelp + + case default + call printCommandHelp + call handle_err(1, 'unknown command line option') + + end select + end do ! looping through command line arguments + + ! check if master_file has been received. + if (len(trim(summaFileManagerFile))==0) call handle_err(1, "master_file is not received; type 'summa.exe --help' for correct usage") + + ! set startGRU for full run + if (iRunMode==iRunModeFull) startGRU=1 + + end subroutine getCommandArguments + + ! ************************************************************************************************** + ! internal subroutine to print the correct command line usage of SUMMA + ! ************************************************************************************************** + subroutine printCommandHelp() + implicit none + ! command line usage + print "(//A)",'Usage: summa.exe -m master_file [-s fileSuffix] [-g startGRU countGRU] [-h iHRU] [-r freqRestart] [-p freqProgress] [-c]' + print "(A,/)", ' summa.exe summa executable' + print "(A)", 'Running options:' + print "(A)", ' -m --master Define path/name of master file (required)' + print "(A)", ' -n --newFile Define frequency [noNewFiles,newFileEveryOct1] of new output files' + print "(A)", ' -s --suffix Add fileSuffix to the output files' + print "(A)", ' -g --gru Run a subset of countGRU GRUs starting from index startGRU' + print "(A)", ' -h --hru Run a single HRU with index of iHRU' + print "(A)", ' -r --restart Define frequency [y,m,d,e,never] to write restart files' + print "(A)", ' -p --progress Define frequency [m,d,h,never] to print progress' + print "(A)", ' -v --version Display version information of the current built' + stop + end subroutine printCommandHelp + + ! ************************************************************************************************** + ! internal subroutine handle_err: error handler + ! ************************************************************************************************** + subroutine handle_err(err,message) + ! used to handle error codes + USE var_lookup,only:iLookPROG,iLookDIAG,iLookFLUX,iLookPARAM,iLookINDEX ! named variables defining elements in data structure + implicit none + ! dummy variables + integer(i4b),intent(in) :: err ! error code + character(*),intent(in) :: message ! error message + ! local variables + integer(i4b) :: nc_err ! error code of nc_close + character(len=256) :: cmessage ! error message of the downwind routine + + ! return if A-OK + if(err==0) return + ! process error messages + if (err>0) then + write(*,'(//a/)') 'FATAL ERROR: '//trim(message) + else + write(*,'(//a/)') 'WARNING: '//trim(message); print*,'(can keep going, but stopping anyway)' + endif + ! dump variables + print*, 'error, variable dump:' + if(allocated(timeStruct%var))then + ! print time step + print*, 'modelTimeStep = ', modelTimeStep + ! print information for the HRUs + if(iGRU<=nGRU)then + if(iHRU<=gru_struc(iGRU)%hruCount)then + print*, 'initial time step = ', dt_init(iGRU)%hru(iHRU) + print*, 'HRU index = ', typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%hruId) + print*, 'pptrate = ', forcStruct%gru(iGRU)%hru(iHRU)%var(iLookFORCE%pptrate) + print*, 'airtemp = ', forcStruct%gru(iGRU)%hru(iHRU)%var(iLookFORCE%airtemp) + print*, 'theta_res = ', mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%theta_res)%dat(1) ! soil residual volumetric water content (-) + print*, 'theta_sat = ', mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%theta_sat)%dat(1) ! soil porosity (-) + print*, 'plantWiltPsi = ', mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%plantWiltPsi)%dat(1) ! matric head at wilting point (m) + print*, 'soilStressParam = ', mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%soilStressParam)%dat(1) ! parameter in the exponential soil stress function (-) + print*, 'critSoilWilting = ', mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%critSoilWilting)%dat(1) ! critical vol. liq. water content when plants are wilting (-) + print*, 'critSoilTranspire = ', mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%critSoilTranspire)%dat(1) ! critical vol. liq. water content when transpiration is limited (-) + print*, 'scalarSWE = ', progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarSWE)%dat(1) + print*, 'scalarSnowDepth = ', progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarSnowDepth)%dat(1) + print*, 'scalarCanopyTemp = ', progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyTemp)%dat(1) + print*, 'scalarRainPlusMelt = ', fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%scalarRainPlusMelt)%dat(1) + write(*,'(a,100(i4,1x))' ) 'layerType = ', indxStruct%gru(iGRU)%hru(iHRU)%var(iLookINDEX%layerType)%dat + write(*,'(a,100(f11.5,1x))') 'mLayerDepth = ', progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerDepth)%dat + write(*,'(a,100(f11.5,1x))') 'mLayerTemp = ', progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerTemp)%dat + write(*,'(a,100(f11.5,1x))') 'mLayerVolFracIce = ', progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracIce)%dat + write(*,'(a,100(f11.5,1x))') 'mLayerVolFracLiq = ', progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracLiq)%dat + print*, 'mLayerMatricHead = ', progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerMatricHead)%dat + print*, 'column inflow = ', fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%mLayerColumnInflow)%dat + endif ! if HRU is valid + endif ! if GRU is valid + endif ! if the time structure is allocated + print*,'error code = ', err + if(allocated(timeStruct%var)) print*, timeStruct%var + !write(*,'(a)') trim(message) + + ! close any remaining output files + do iFreq = 1,maxvarFreq + if (ncid(iFreq).ne.integerMissing) then + call nc_file_close(ncid(iFreq),nc_err,cmessage) + if(nc_err/=0) print*, trim(cmessage) + end if + end do + + stop 1 + end subroutine handle_err + + ! ************************************************************************************************** + ! internal subroutine stop_program: stop program execution + ! ************************************************************************************************** + subroutine stop_program(message) + ! used to stop program execution + implicit none + ! define dummy variables + character(*),intent(in)::message + ! define the local variables + integer(i4b),parameter :: outunit=6 ! write to screen + integer(i4b) :: ctime2(8) ! final time + real(dp) :: elpSec ! elapsed seconds + + ! close any remaining output files + ! NOTE: use the direct NetCDF call with no error checking since the file may already be closed + do iFreq = 1,maxvarFreq + if (ncid(iFreq).ne.integerMissing) then + err = nf90_close(ncid(iFreq)) + end if + end do + + ! get the final date and time + call date_and_time(values=ctime2) + + elpSec = elapsedSec(ctime1,ctime2) + + ! print initial and final date and time + write(outunit,"(/,A,I4,'-',I2.2,'-',I2.2,2x,I2,':',I2.2,':',I2.2,'.',I3.3)") 'initial date/time = ',ctime1(1:3),ctime1(5:8) + write(outunit,"(A,I4,'-',I2.2,'-',I2.2,2x,I2,':',I2.2,':',I2.2,'.',I3.3)") ' final date/time = ',ctime2(1:3),ctime2(5:8) + ! print elapsed time for the initialization + write(outunit,"(/,A,1PG15.7,A)") ' elapsed init = ', elapsedInit, ' s' + write(outunit,"(A,1PG15.7,A)") ' fraction init = ', elapsedInit/elpSec, ' s' + ! print elapsed time for the data read + write(outunit,"(/,A,1PG15.7,A)") ' elapsed read = ', elapsedRead, ' s' + write(outunit,"(A,1PG15.7,A)") ' fraction read = ', elapsedRead/elpSec, ' s' + ! print elapsed time for the data write + write(outunit,"(/,A,1PG15.7,A)") ' elapsed write = ', elapsedWrite, ' s' + write(outunit,"(A,1PG15.7,A)") ' fraction write = ', elapsedWrite/elpSec, ' s' + ! print elapsed time for the physics + write(outunit,"(/,A,1PG15.7,A)") ' elapsed physics = ', elapsedPhysics, ' s' + write(outunit,"(A,1PG15.7,A)") ' fraction physics = ', elapsedPhysics/elpSec, ' s' + ! print total elapsed time + write(outunit,"(/,A,1PG15.7,A)") ' elapsed time = ', elpSec, ' s' + write(outunit,"(A,1PG15.7,A)") ' or ', elpSec/60_dp, ' m' + write(outunit,"(A,1PG15.7,A)") ' or ', elpSec/3600_dp, ' h' + write(outunit,"(A,1PG15.7,A/)") ' or ', elpSec/86400_dp, ' d' + ! print the number of threads + write(outunit,"(A,i10,/)") ' number threads = ', nThreads + ! stop with message + print*,'FORTRAN STOP: '//trim(message) + stop + end subroutine + +end module summa_bmi + + + ! ************************************************************************************************** + ! private subroutine SOIL_VEG_GEN_PARM: Read soil, vegetation and other model parameters (from NOAH) + ! ************************************************************************************************** +!----------------------------------------------------------------- +SUBROUTINE SOIL_VEG_GEN_PARM(FILENAME_VEGTABLE, FILENAME_SOILTABLE, FILENAME_GENERAL, MMINLU, MMINSL) +!----------------------------------------------------------------- + use module_sf_noahlsm, only : shdtbl, nrotbl, rstbl, rgltbl, & + & hstbl, snuptbl, maxalb, laimintbl, & + & bb, drysmc, f11, maxsmc, laimaxtbl, & + & emissmintbl, emissmaxtbl, albedomintbl, & + & albedomaxtbl, wltsmc, qtz, refsmc, & + & z0mintbl, z0maxtbl, & + & satpsi, satdk, satdw, & + & theta_res, theta_sat, vGn_alpha, vGn_n, k_soil, & ! MPC add van Genutchen parameters + & fxexp_data, lvcoef_data, & + & lutype, maxalb, & + & slope_data, frzk_data, bare, cmcmax_data, & + & cfactr_data, csoil_data, czil_data, & + & refkdt_data, natural, refdk_data, & + & rsmax_data, salp_data, sbeta_data, & + & zbot_data, smhigh_data, smlow_data, & + & lucats, topt_data, slcats, slpcats, sltype + + IMPLICIT NONE + + CHARACTER(LEN=*), INTENT(IN) :: FILENAME_VEGTABLE, FILENAME_SOILTABLE, FILENAME_GENERAL + CHARACTER(LEN=*), INTENT(IN) :: MMINLU, MMINSL + integer :: LUMATCH, IINDEX, LC, NUM_SLOPE + integer :: ierr + INTEGER , PARAMETER :: OPEN_OK = 0 + + character*128 :: mess , message + +!-----SPECIFY VEGETATION RELATED CHARACTERISTICS : +! ALBBCK: SFC albedo (in percentage) +! Z0: Roughness length (m) +! SHDFAC: Green vegetation fraction (in percentage) +! Note: The ALBEDO, Z0, and SHDFAC values read from the following table +! ALBEDO, amd Z0 are specified in LAND-USE TABLE; and SHDFAC is +! the monthly green vegetation data +! CMXTBL: MAX CNPY Capacity (m) +! NROTBL: Rooting depth (layer) +! RSMIN: Mimimum stomatal resistance (s m-1) +! RSMAX: Max. stomatal resistance (s m-1) +! RGL: Parameters used in radiation stress function +! HS: Parameter used in vapor pressure deficit functio +! TOPT: Optimum transpiration air temperature. (K) +! CMCMAX: Maximum canopy water capacity +! CFACTR: Parameter used in the canopy inteception calculati +! SNUP: Threshold snow depth (in water equivalent m) that +! implies 100% snow cover +! LAI: Leaf area index (dimensionless) +! MAXALB: Upper bound on maximum albedo over deep snow +! +!-----READ IN VEGETAION PROPERTIES FROM VEGPARM.TBL +! + + OPEN(19, FILE=trim(FILENAME_VEGTABLE),FORM='FORMATTED',STATUS='OLD',IOSTAT=ierr) + IF(ierr .NE. OPEN_OK ) THEN + WRITE(message,FMT='(A)') & + 'module_sf_noahlsm.F: soil_veg_gen_parm: failure opening VEGPARM.TBL' + CALL wrf_error_fatal ( message ) + END IF + + LUMATCH=0 + + FIND_LUTYPE : DO WHILE (LUMATCH == 0) + READ (19,*,END=2002) + READ (19,*,END=2002)LUTYPE + READ (19,*)LUCATS,IINDEX + + IF(LUTYPE.EQ.MMINLU)THEN + WRITE( mess , * ) 'LANDUSE TYPE = ' // TRIM ( LUTYPE ) // ' FOUND', LUCATS,' CATEGORIES' + ! CALL wrf_message( mess ) + LUMATCH=1 + ELSE + call wrf_message ( "Skipping over LUTYPE = " // TRIM ( LUTYPE ) ) + DO LC = 1, LUCATS+12 + read(19,*) + ENDDO + ENDIF + ENDDO FIND_LUTYPE +! prevent possible array overwrite, Bill Bovermann, IBM, May 6, 2008 + IF ( SIZE(SHDTBL) < LUCATS .OR. & + SIZE(NROTBL) < LUCATS .OR. & + SIZE(RSTBL) < LUCATS .OR. & + SIZE(RGLTBL) < LUCATS .OR. & + SIZE(HSTBL) < LUCATS .OR. & + SIZE(SNUPTBL) < LUCATS .OR. & + SIZE(MAXALB) < LUCATS .OR. & + SIZE(LAIMINTBL) < LUCATS .OR. & + SIZE(LAIMAXTBL) < LUCATS .OR. & + SIZE(Z0MINTBL) < LUCATS .OR. & + SIZE(Z0MAXTBL) < LUCATS .OR. & + SIZE(ALBEDOMINTBL) < LUCATS .OR. & + SIZE(ALBEDOMAXTBL) < LUCATS .OR. & + SIZE(EMISSMINTBL ) < LUCATS .OR. & + SIZE(EMISSMAXTBL ) < LUCATS ) THEN + CALL wrf_error_fatal('Table sizes too small for value of LUCATS in module_sf_noahdrv.F') + ENDIF + + IF(LUTYPE.EQ.MMINLU)THEN + DO LC=1,LUCATS + READ (19,*)IINDEX,SHDTBL(LC), & + NROTBL(LC),RSTBL(LC),RGLTBL(LC),HSTBL(LC), & + SNUPTBL(LC),MAXALB(LC), LAIMINTBL(LC), & + LAIMAXTBL(LC),EMISSMINTBL(LC), & + EMISSMAXTBL(LC), ALBEDOMINTBL(LC), & + ALBEDOMAXTBL(LC), Z0MINTBL(LC), Z0MAXTBL(LC) + ENDDO + + READ (19,*) + READ (19,*)TOPT_DATA + READ (19,*) + READ (19,*)CMCMAX_DATA + READ (19,*) + READ (19,*)CFACTR_DATA + READ (19,*) + READ (19,*)RSMAX_DATA + READ (19,*) + READ (19,*)BARE + READ (19,*) + READ (19,*)NATURAL + ENDIF + +2002 CONTINUE + + CLOSE (19) + IF (LUMATCH == 0) then + CALL wrf_error_fatal ("Land Use Dataset '"//MMINLU//"' not found in VEGPARM.TBL.") + ENDIF + +! +!-----READ IN SOIL PROPERTIES FROM SOILPARM.TBL +! + OPEN(19, FILE=trim(FILENAME_SOILTABLE),FORM='FORMATTED',STATUS='OLD',IOSTAT=ierr) + IF(ierr .NE. OPEN_OK ) THEN + WRITE(message,FMT='(A)') & + 'module_sf_noahlsm.F: soil_veg_gen_parm: failure opening SOILPARM.TBL' + CALL wrf_error_fatal ( message ) + END IF + + WRITE(mess,*) 'INPUT SOIL TEXTURE CLASSIFICATION = ', TRIM ( MMINSL ) + ! CALL wrf_message( mess ) + + LUMATCH=0 + + ! MPC add a new soil table + FIND_soilTYPE : DO WHILE (LUMATCH == 0) + READ (19,*) + READ (19,*,END=2003)SLTYPE + READ (19,*)SLCATS,IINDEX + IF(SLTYPE.EQ.MMINSL)THEN + WRITE( mess , * ) 'SOIL TEXTURE CLASSIFICATION = ', TRIM ( SLTYPE ) , ' FOUND', & + SLCATS,' CATEGORIES' + ! CALL wrf_message ( mess ) + LUMATCH=1 + ELSE + call wrf_message ( "Skipping over SLTYPE = " // TRIM ( SLTYPE ) ) + DO LC = 1, SLCATS + read(19,*) + ENDDO + ENDIF + ENDDO FIND_soilTYPE + ! prevent possible array overwrite, Bill Bovermann, IBM, May 6, 2008 + IF ( SIZE(BB ) < SLCATS .OR. & + SIZE(DRYSMC) < SLCATS .OR. & + SIZE(F11 ) < SLCATS .OR. & + SIZE(MAXSMC) < SLCATS .OR. & + SIZE(REFSMC) < SLCATS .OR. & + SIZE(SATPSI) < SLCATS .OR. & + SIZE(SATDK ) < SLCATS .OR. & + SIZE(SATDW ) < SLCATS .OR. & + SIZE(WLTSMC) < SLCATS .OR. & + SIZE(QTZ ) < SLCATS ) THEN + CALL wrf_error_fatal('Table sizes too small for value of SLCATS in module_sf_noahdrv.F') + ENDIF + + ! MPC add new soil table + select case(trim(SLTYPE)) + case('STAS','STAS-RUC') ! original soil tables + DO LC=1,SLCATS + READ (19,*) IINDEX,BB(LC),DRYSMC(LC),F11(LC),MAXSMC(LC),& + REFSMC(LC),SATPSI(LC),SATDK(LC), SATDW(LC), & + WLTSMC(LC), QTZ(LC) + ENDDO + case('ROSETTA') ! new soil table + DO LC=1,SLCATS + READ (19,*) IINDEX,& + ! new soil parameters (from Rosetta) + theta_res(LC), theta_sat(LC), & + vGn_alpha(LC), vGn_n(LC), k_soil(LC), & + ! original soil parameters + BB(LC),DRYSMC(LC),F11(LC),MAXSMC(LC),& + REFSMC(LC),SATPSI(LC),SATDK(LC), SATDW(LC), & + WLTSMC(LC), QTZ(LC) + ENDDO + case default + CALL wrf_message( 'SOIL TEXTURE IN INPUT FILE DOES NOT ' ) + CALL wrf_message( 'MATCH SOILPARM TABLE' ) + CALL wrf_error_fatal ( 'INCONSISTENT OR MISSING SOILPARM FILE' ) + end select + +2003 CONTINUE + + CLOSE (19) + + IF(LUMATCH.EQ.0)THEN + CALL wrf_message( 'SOIL TEXTURE IN INPUT FILE DOES NOT ' ) + CALL wrf_message( 'MATCH SOILPARM TABLE' ) + CALL wrf_error_fatal ( 'INCONSISTENT OR MISSING SOILPARM FILE' ) + ENDIF + +! +!-----READ IN GENERAL PARAMETERS FROM GENPARM.TBL +! + OPEN(19, FILE=trim(FILENAME_GENERAL),FORM='FORMATTED',STATUS='OLD',IOSTAT=ierr) + IF(ierr .NE. OPEN_OK ) THEN + WRITE(message,FMT='(A)') & + 'module_sf_noahlsm.F: soil_veg_gen_parm: failure opening GENPARM.TBL' + CALL wrf_error_fatal ( message ) + END IF + + READ (19,*) + READ (19,*) + READ (19,*) NUM_SLOPE + + SLPCATS=NUM_SLOPE +! prevent possible array overwrite, Bill Bovermann, IBM, May 6, 2008 + IF ( SIZE(slope_data) < NUM_SLOPE ) THEN + CALL wrf_error_fatal('NUM_SLOPE too large for slope_data array in module_sf_noahdrv') + ENDIF + + DO LC=1,SLPCATS + READ (19,*)SLOPE_DATA(LC) + ENDDO + + READ (19,*) + READ (19,*)SBETA_DATA + READ (19,*) + READ (19,*)FXEXP_DATA + READ (19,*) + READ (19,*)CSOIL_DATA + READ (19,*) + READ (19,*)SALP_DATA + READ (19,*) + READ (19,*)REFDK_DATA + READ (19,*) + READ (19,*)REFKDT_DATA + READ (19,*) + READ (19,*)FRZK_DATA + READ (19,*) + READ (19,*)ZBOT_DATA + READ (19,*) + READ (19,*)CZIL_DATA + READ (19,*) + READ (19,*)SMLOW_DATA + READ (19,*) + READ (19,*)SMHIGH_DATA + READ (19,*) + READ (19,*)LVCOEF_DATA + CLOSE (19) + +!----------------------------------------------------------------- +END SUBROUTINE SOIL_VEG_GEN_PARM +!----------------------------------------------------------------- diff --git a/build/source/driver/test_bmi.f90 b/build/source/driver/test_bmi.f90 new file mode 100644 index 000000000..44f967cf5 --- /dev/null +++ b/build/source/driver/test_bmi.f90 @@ -0,0 +1,52 @@ +program test_bmi + + use summa_bmi, only: initialize, update, finalize, & + get_output_name, get_output_units, & + get_num_basins, get_num_output_fields, & + get_latlons, get_basin_field, modelTimeStep + use globalData,only:numtim + + implicit none + + character(64), allocatable :: dir + character(64) :: fldid, fldun + real, allocatable :: lats(:), lons(:), Q(:) + integer :: istat, iseq, idt, count, ifld, nflds, ndt + + write(0,*) "" + write(0,*) "INITIALIZING " + write(0,*) "-------------------------------------------------------" + istat = initialize(dir)!, iseq) + + count = get_num_basins() + write(0,*) "The number of basins is", count + allocate(lats(count), lons(count), Q(count)) + if(istat/=0) stop istat + + call get_latlons(lats, lons) + write(0,*) "The latitudes are", lats + write(0,*) "The longitudes are", lons + nflds = get_num_output_fields() + + write(0,*) "" + write(0,*) "BEGIN TIME LOOP" + write(0,*) "-------------------------------------------------------" + DO modelTimeStep = 1, numtim + istat = update() + if(istat/=0) stop istat + write(0,*) "------------- TS ", modelTimeStep, "-------------" + call get_basin_field( 1, Q) + call get_output_name( 1, fldid) + call get_output_units(1, fldun) + write(0,*) fldid, " max = ", maxval(Q), ", min = ", minval(Q), " " , fldun + enddo + + write(0,*) "" + write(0,*) "FINALIZING" + write(0,*) "-------------------------------------------------------" + istat = finalize() + if(istat/=0) stop istat + deallocate(lats, lons, Q) + stop 0 + +end program From 90c059d957562769adf3360590c37168d3cfd3af Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 20 Jan 2023 09:09:00 +0900 Subject: [PATCH 0503/1472] names of files --- utils/hist_per_GRU.py | 6 ++++-- utils/scat_per_GRU.py | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index ea550d67b..9b0e959c2 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -44,8 +44,10 @@ plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] -fig_fil = '{}_hrly_diff_hist_{}_{}_zoom_compressed.png' -fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) +#fig_fil = '{}_hrly_diff_hist_{}_{}_zoom_compressed.png' +#fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) +fig_fil = 'Hrly_diff_hist_{}_{}_zoom_compressed.png' +fig_fil = fig_fil.format(','.join(settings),stat) # possibly want to use these to shrink the axes a bit if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,13e-3] if stat=='maxe' : maxes = [20,30,3e-4,2,4e-7,0.7] diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index 101098b57..4d22901b7 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -44,8 +44,10 @@ plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] -fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' -fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) +#fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' +#fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) +fig_fil = 'Hrly_diff_scat_{}_{}_zoom_compressed.png' +fig_fil = fig_fil.format(','.join(settings),stat) # possibly want to use these to shrink the axes a bit if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,100] From 174b9323826b69aefef638b92210a4c177b90f57 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 20 Jan 2023 09:43:42 +0900 Subject: [PATCH 0504/1472] missed y --- utils/plot_per_GRU.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 1c369b50c..922f7d3cf 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -223,7 +223,7 @@ def run_loop(i,var,the_max,f_x,f_y): sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) sm._A = [] cbr = fig.colorbar(sm, cax=cax) #, extend='max') #if max extend can't get title right - cbr.ax.set_label('[{}]'.format(leg_titl[i]), labelpad=40, rotation=270) + cbr.ax.set_ylabel('[{}]'.format(leg_titl[i]), labelpad=40, rotation=270) #cbr.ax.yaxis.set_offset_position('right') if stat == 'rmse': stat_word = ' Hourly RMSE' From 8e49fd0d52715350717baebd5e33574f06f91aab Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 23 Jan 2023 21:50:41 +0900 Subject: [PATCH 0505/1472] add kgem --- utils/hist_per_GRU.py | 21 ++++++++----- utils/plot_per_GRU.py | 32 +++++++++++--------- utils/process_hist.sh | 1 + utils/process_plot.sh | 7 +---- utils/process_scat.sh | 1 + utils/scat_per_GRU.py | 35 +++++++++------------- utils/timeseries_to_statistics.py | 50 ++++++++++++++++++++++++------- 7 files changed, 88 insertions(+), 59 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 9b0e959c2..6e5184c5f 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -8,7 +8,7 @@ # SUMMA simulations have been preprocessed into single value statistics per model element, using auxiliary scripts in ~/utils # Run: # python hist_per_GRU.py [stat] -# where stat is rmse or maxe +# where stat is rmse or maxe or kgem # modules import os @@ -23,13 +23,13 @@ testing = False if testing: - stat = 'rmse' + stat = 'kgem' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be1','sundials_1en6'] #maybe make this an argument + method_name=['be64','be1','sundials_1en6'] #maybe make this an argument else: import sys # The first input argument specifies the run where the files are - stat = sys.argv[1] # max or rmse + stat = sys.argv[1] method_name=['be64','be32','be1','sundials_1en6'] #maybe make this an argument # Simulation statistics file locations @@ -51,6 +51,7 @@ # possibly want to use these to shrink the axes a bit if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,13e-3] if stat=='maxe' : maxes = [20,30,3e-4,2,4e-7,0.7] +if stat=='kgem' : maxes = [0.8,0.8,0.8,0.8,0.8,13e-3] # Get the aggregated statistics of SUMMA simulations summa = {} @@ -81,30 +82,36 @@ def run_loop(i,var,mx): num_bins = 200 stat0 = stat if var == 'wallClockTime': - if stat == 'rmse': stat0 = 'mean' + if stat == 'rmse' or stat == 'kgem': stat0 = 'mean' if stat == 'maxe': stat0 = 'amax' if 'zoom' in fig_fil: mx = mx + mn = mx else: mx = 0.0 + mn = 1.0 for m in method_name: s = summa[m][var].sel(stat=stat0) if stat == 'maxe': s = np.fabs(s) # make absolute value norm mx = max(s.max(),mx) + if stat=='kgem' : mn = min(s.min(),mn) # Data for m in method_name: s = summa[m][var].sel(stat=stat0) if stat == 'maxe': s = np.fabs(s) # make absolute value norm - s.plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=m,linewidth=2.0,range=(0,mx)) + range = (0,mx) + if stat=='kgem' and var!='wallClockTime' : range = (mn,1) + s.plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=m,linewidth=2.0,range=range) if stat == 'rmse': stat_word = ' Hourly RMSE' if stat == 'maxe': stat_word = ' Hourly max abs error' + if stat == 'kgem': stat_word = ' Hourly KGEm' # wall Clock doesn't do difference if var == 'wallClockTime': - if stat == 'rmse': stat_word = ' Hourly mean' + if stat == 'rmse' or stat == 'kgem': stat_word = ' Hourly mean' if stat == 'maxe': stat_word = ' Hourly max' axs[r,c].legend() diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 922f7d3cf..2085b1f0b 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -13,7 +13,7 @@ # Run: # python plot_per_GRU.py sundials_1en6 [stat] -# where stat is rmse or maxe +# where stat is rmse or maxe or kgem # modules @@ -25,13 +25,13 @@ from pathlib import Path import matplotlib.pyplot as plt import copy -import pyproj +import pyproj import fiona import geopandas as gpd # The first input argument specifies the run where the files are method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) -stat = sys.argv[2] # maxe or rmse +stat = sys.argv[2] # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] @@ -45,6 +45,7 @@ leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,13e-3] if stat=='maxe': maxes = [20,30,3e-4,2,4e-7,0.7] +if stat=='kgem' : maxes = [0.8,0.8,0.8,0.8,0.8,13e-3] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed.png' fig_fil = fig_fil.format(','.join(settings),stat) @@ -157,7 +158,7 @@ def make_default_path(suffix): for plot_var in plot_vars: stat0 = stat if plot_var == 'wallClockTime': - if stat == 'rmse': stat0 = 'mean' + if stat == 'rmse' or stat == 'kgem': stat0 = 'mean' if stat == 'maxe': stat0 = 'amax' s = summa[plot_var].sel(stat=stat0) if stat == 'maxe': s = np.fabs(s) # make absolute value norm, max is not not all positive @@ -203,7 +204,7 @@ def make_default_path(suffix): my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap #my_cmap.set_bad(my_cmap.colors[0]) #any nans, maybe should be white? my_cmap.set_bad(color='white') #nan color white - + def run_loop(i,var,the_max,f_x,f_y): #vmin = (bas_albers[var].where(bas_albers[var]>0)).min() #a = bas_albers[var].where(bas_albers[var]>0) @@ -211,36 +212,39 @@ def run_loop(i,var,the_max,f_x,f_y): #vmin,vmax = bas_albers[var].min(), bas_albers[var].max() vmin,vmax = 0, the_max norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) - #norm=matplotlib.colors.Normalize(vmin=vmin, vmax=vmax) + if stat=='kgem' and var!='wallClockTime': + vmin,vmax = the_max, 1.0 + norm=matplotlib.colors.Normalize(vmin=vmin, vmax=vmax) r = i//2 c = i-r*2 - + # Data bas_albers.plot(ax=axs[r,c], column=var, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) - + # Custom colorbar cax = fig.add_axes([f_x,f_y,0.02,0.25]) sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) sm._A = [] cbr = fig.colorbar(sm, cax=cax) #, extend='max') #if max extend can't get title right cbr.ax.set_ylabel('[{}]'.format(leg_titl[i]), labelpad=40, rotation=270) - #cbr.ax.yaxis.set_offset_position('right') - + #cbr.ax.yaxis.set_offset_position('right') + if stat == 'rmse': stat_word = ' Hourly RMSE' if stat == 'maxe': stat_word = ' Hourly max abs error' - + if stat == 'kgem': stat_word = ' Hourly KGEm' + # wall Clock doesn't do difference if var == 'wallClockTime': - if stat == 'rmse': stat_word = ' Hourly mean' + if stat == 'rmse' or stat == 'kgem': stat_word = ' Hourly mean' if stat == 'maxe': stat_word = ' Hourly max' axs[r,c].set_title(plt_titl[i] + stat_word) axs[r,c].axis('off') - + # lakes if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) -for i,(var,the_max,f_x,f_y) in enumerate(zip(plot_vars,maxes,f_x_mat,f_y_mat)): +for i,(var,the_max,f_x,f_y) in enumerate(zip(plot_vars,maxes,f_x_mat,f_y_mat)): run_loop(i,var,the_max,f_x,f_y) # Save diff --git a/utils/process_hist.sh b/utils/process_hist.sh index 8793c8f98..860373d30 100644 --- a/utils/process_hist.sh +++ b/utils/process_hist.sh @@ -21,3 +21,4 @@ pip install --no-index netCDF4 python hist_per_GRU.py rmse python hist_per_GRU.py maxe +python hist_per_GRU.py kgem diff --git a/utils/process_plot.sh b/utils/process_plot.sh index 1cd504980..ff35b398e 100644 --- a/utils/process_plot.sh +++ b/utils/process_plot.sh @@ -23,9 +23,4 @@ pip install --no-index netCDF4 python plot_per_GRU.py sundials_1en6 rmse python plot_per_GRU.py sundials_1en6 maxe -python plot_per_GRU.py be1 rmse -python plot_per_GRU.py be1 maxe -python plot_per_GRU.py be32 rmse -python plot_per_GRU.py be32 maxe -python plot_per_GRU.py be64 rmse -python plot_per_GRU.py be64 maxe \ No newline at end of file +python plot_per_GRU.py sundials_1en6 kgem \ No newline at end of file diff --git a/utils/process_scat.sh b/utils/process_scat.sh index 1bb018b70..ad0c4ba4c 100644 --- a/utils/process_scat.sh +++ b/utils/process_scat.sh @@ -21,3 +21,4 @@ pip install --no-index netCDF4 python scat_per_GRU.py rmse python scat_per_GRU.py maxe +python scat_per_GRU.py kgem diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index 4d22901b7..eb474cb9d 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -7,8 +7,8 @@ ## Special note # SUMMA simulations have been preprocessed into single value statistics per model element, using auxiliary scripts in ~/utils # Run: -# python scat_per_GRU.py rmse -# where stat is rmse or maxe +# python scat_per_GRU.py [stat] +# where stat is rmse or maxe or kgem # modules import os @@ -23,13 +23,13 @@ testing = False if testing: - stat = 'rmse' + stat = 'kgem' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be1','sundials_1en6'] #maybe make this an argument + method_name=['be64','be1','sundials_1en6'] #maybe make this an argument else: import sys # The first input argument specifies the run where the files are - stat = sys.argv[1] # max or rmse + stat = sys.argv[1] method_name=['be64','be32','be1','sundials_1en6'] #maybe make this an argument # Simulation statistics file locations @@ -46,15 +46,8 @@ #fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' #fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) -fig_fil = 'Hrly_diff_scat_{}_{}_zoom_compressed.png' +fig_fil = 'Hrly_diff_scat_{}_{}_compressed.png' fig_fil = fig_fil.format(','.join(settings),stat) -# possibly want to use these to shrink the axes a bit -if stat=='rmse': - maxes = [2,15,8e-6,0.08,7e-9,100] - maxes0 = [2,15,8e-6,0.08,7e-9,13e-3] -if stat=='maxe' : - maxes = [20,30,3e-4,2,4e-7,5000] - maxes0 = [100,2000,-1e-3,20,2e-5,0.7] # Get the aggregated statistics of SUMMA simulations summa = {} @@ -79,10 +72,10 @@ fig,axs = plt.subplots(3,2,figsize=(140,133)) -def run_loop(i,var,mx,mx0): +def run_loop(i,var0): r = i//2 c = i-r*2 - if stat == 'rmse': stat0 = 'mean' + if stat == 'rmse' or stat == 'kgem': stat0 = 'mean' if stat == 'maxe': stat0 = 'amax' # Data @@ -90,10 +83,6 @@ def run_loop(i,var,mx,mx0): s = summa[m][var].sel(stat=[stat,stat0]) if stat == 'maxe': s.loc[dict(stat='maxe')] = np.fabs(s.loc[dict(stat='maxe')]) # make absolute value norm axs[r,c].scatter(x=s.sel(stat=stat).values,y=s.sel(stat=stat0).values,s=1,zorder=0,label=m) - if 'zoom' in fig_fil: - axs[r,c].set_xlim(0,mx) - if mx0<0: axs[r,c].set_ylim(mx0,0) - if mx0>0: axs[r,c].set_ylim(0,mx0) if stat == 'rmse': stat_word = 'Hourly RMSE ' @@ -101,6 +90,10 @@ def run_loop(i,var,mx,mx0): if stat == 'maxe': stat_word = ' Hourly max abs error ' stat0_word =' Hourly max ' + if stat == 'kgem': + stat_word = ' Hourly KGEm' + stat0_word ='Hourly mean ' + lgnd = axs[r,c].legend() for j, m in enumerate(method_name): @@ -110,8 +103,8 @@ def run_loop(i,var,mx,mx0): axs[r,c].set_ylabel(stat0_word + '[{}]'.format(leg_titl[i])) -for i,(var,mx,mx0) in enumerate(zip(plot_vars,maxes,maxes0)): - run_loop(i,var,mx,mx0) +for i,var in enumerate(plot_vars): + run_loop(i,var) # Save plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 8f070db4f..a4b56c331 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -7,8 +7,14 @@ # written originally by W. Knoben, modified by A. Van Beusekom (2023) # Best to comment out parallel processing lines and run that way on Graham or for full dataset +# Uses modified KGEm calculation avoids the amplified values when mean is small +# and avoids the KGE value dependence on the units of measurement (as discussed by Santos et al. 2018; Clark et al. 2021). +# The KGEm values range from -∞ to 1, with 1 being a perfect match with the benchmark results. +# Similar to Beck et al.(2020), we scaled KGEm values to avoid the heavy influence of large negative values. +# This results in KGE values that range between -1 and 1, with lower KGE values indicating larger differences from bench. + # Run: -# python timeseries_to_statistics.py sundials_1en6 rmse +# python timeseries_to_statistics.py sundials_1en6 import os import glob @@ -23,7 +29,7 @@ testing = False if testing: top_fold = '/Users/amedin/Research/USask/test_py/' - method_name = 'sundials_1en6' + method_name = 'be1' else: import multiprocessing as mp import sys @@ -54,6 +60,13 @@ ben_files = glob.glob(str( ben_dir / src_pat )) ben_files.sort() +# definitions for KGE computation +def covariance(x,y,dims=None): + return xr.dot(x-x.mean(dims), y-y.mean(dims), dims=dims) / x.count(dims) + +def correlation(x,y,dims=None): + return (covariance(x,y,dims)) / (x.std(dims) * y.std(dims)) + assert len(ben_files) == len(src_files), \ 'Found {} files but need {}!'.format(len(src_files), len(ben_files)) @@ -74,21 +87,23 @@ def run_loop(file,bench): ben['averageRoutedRunoff'] = ben['averageRoutedRunoff'].where(ben['averageRoutedRunoff']>=0) #dat['averageRoutedRunoff'] = dat['averageRoutedRunoff'].fillna(0) #ben['averageRoutedRunoff'] = ben['averageRoutedRunoff'].fillna(0) - diff = dat - ben # get rid of gru dimension, assuming they are same as the often are (everything now as hruId) - diff = diff.drop_vars(['hruId','gruId']) - m = diff.drop_dims('hru') - m = m.rename({'gru': 'hru'}) - diff = diff.drop_dims('gru') - diff = xr.merge([diff,m]) - dat = dat.drop_vars(['hruId','gruId']) m = dat.drop_dims('hru') m = m.rename({'gru': 'hru'}) dat = dat.drop_dims('gru') - dat = xr.merge([dat,m]) + dat = xr.merge([dat,m]) + ben = ben.drop_vars(['hruId','gruId']) + m = ben.drop_dims('hru') + m = m.rename({'gru': 'hru'}) + ben = ben.drop_dims('gru') + ben = xr.merge([ben,m]) + + diff = dat - ben + the_hru = np.array(ben['hru']) + for var in settings: mean = dat[var].mean(dim='time') mean = mean.expand_dims("stat").assign_coords(stat=("stat",["mean"])) @@ -105,8 +120,21 @@ def run_loop(file,bench): amx = np.fabs(diff[var].fillna(na_mx)).argmax(dim=['time']) maxe = diff[var].isel(amx).drop_vars('time') maxe = maxe.expand_dims("stat").assign_coords(stat=("stat",["maxe"])) + + r = correlation(dat[var],ben[var],dims='time') + kgem = 1 - np.sqrt( np.square(r-1) + + np.square( dat[var].std(dim='time')/ben[var].std(dim='time') - 1) + + np.square( (dat[var].mean(dim='time')-ben[var].mean(dim='time'))/ben[var].std(dim='time') ) ) + + #if constant and identical, want this as 1.0 -- correlation with a constant = 0 and std dev = 0\n", + for h in the_hru: + ss = dat[var].sel(hru=h) + tt = ben[var].sel(hru=h) + kgem.loc[h] =kgem.sel(hru=h).where(np.allclose(ss,tt, atol = 1e-10)==False, other=1.0) + kgem = kgem/(2.0-kgem) + kgem = kgem.expand_dims("stat").assign_coords(stat=("stat",["kgem"])) - new = xr.merge([mean,amax,rmse,maxe]) + new = xr.merge([mean,amax,rmse,maxe,kgem]) new.to_netcdf(des_dir / des_fil.format(var,subset)) print("wrote output: %s" % (top_fold + 'statistics/' +subset)) From c37bac1ddcee9fb00deeeef0df1880587f85c02a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 24 Jan 2023 11:32:39 +0900 Subject: [PATCH 0506/1472] kgem plotting --- utils/hist_per_GRU.py | 2 +- utils/plot_per_GRU.py | 15 ++++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 6e5184c5f..f50961402 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -51,7 +51,7 @@ # possibly want to use these to shrink the axes a bit if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,13e-3] if stat=='maxe' : maxes = [20,30,3e-4,2,4e-7,0.7] -if stat=='kgem' : maxes = [0.8,0.8,0.8,0.8,0.8,13e-3] +if stat=='kgem' : maxes = [0.9,0.7,0.9,0.95,0.95,13e-3] # Get the aggregated statistics of SUMMA simulations summa = {} diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 2085b1f0b..04ed9fc20 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -45,7 +45,7 @@ leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,13e-3] if stat=='maxe': maxes = [20,30,3e-4,2,4e-7,0.7] -if stat=='kgem' : maxes = [0.8,0.8,0.8,0.8,0.8,13e-3] +if stat=='kgem' : maxes = [0.9,0.7,0.9,0.95,0.95,13e-3] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed.png' fig_fil = fig_fil.format(','.join(settings),stat) @@ -201,18 +201,15 @@ def make_default_path(suffix): plt.tight_layout() -my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap -#my_cmap.set_bad(my_cmap.colors[0]) #any nans, maybe should be white? -my_cmap.set_bad(color='white') #nan color white - def run_loop(i,var,the_max,f_x,f_y): - #vmin = (bas_albers[var].where(bas_albers[var]>0)).min() - #a = bas_albers[var].where(bas_albers[var]>0) - #bas_albers[var] = a.fillna(vmin) - #vmin,vmax = bas_albers[var].min(), bas_albers[var].max() + my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap + my_cmap.set_bad(color='white') #nan color white vmin,vmax = 0, the_max norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) if stat=='kgem' and var!='wallClockTime': + my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno')) # copy the default cmap + my_cmap.set_bad(color='white') #nan color white + vmin,vmax = the_max, 1.0 norm=matplotlib.colors.Normalize(vmin=vmin, vmax=vmax) r = i//2 From 2180a7b32ba77e2c10b6bd73e1acea1dbece21dc Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 27 Jan 2023 02:01:40 +0900 Subject: [PATCH 0507/1472] trying to fix the changing of scalarCanopyWat if it is 0 --- build/source/engine/systemSolv.f90 | 12 ++++++------ build/source/engine/systemSolvSundials.f90 | 12 ++++++------ build/source/engine/updateVarsSundials.f90 | 2 +- build/source/netcdf/read_icond.f90 | 5 +++++ 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 69c17e996..e939ac9b8 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -384,14 +384,14 @@ subroutine systemSolv(& ! initialize the trial state vectors stateVecTrial = stateVecInit + ! Changing in read_icond, not here ! need to intialize canopy water at a positive value - if(ixVegHyd/=integerMissing)then - if(stateVecTrial(ixVegHyd) < xMinCanopyWater) stateVecTrial(ixVegHyd) = stateVecTrial(ixVegHyd) + xMinCanopyWater - endif - + !if(ixVegHyd/=integerMissing)then + ! if(stateVecTrial(ixVegHyd) < xMinCanopyWater) stateVecTrial(ixVegHyd) = stateVecTrial(ixVegHyd) + xMinCanopyWater + !endif ! try to accelerate solution for energy - if(ixCasNrg/=integerMissing) stateVecTrial(ixCasNrg) = stateVecInit(ixCasNrg) + (airtemp - stateVecInit(ixCasNrg))*tempAccelerate - if(ixVegNrg/=integerMissing) stateVecTrial(ixVegNrg) = stateVecInit(ixVegNrg) + (airtemp - stateVecInit(ixVegNrg))*tempAccelerate + !if(ixCasNrg/=integerMissing) stateVecTrial(ixCasNrg) = stateVecInit(ixCasNrg) + (airtemp - stateVecInit(ixCasNrg))*tempAccelerate + !if(ixVegNrg/=integerMissing) stateVecTrial(ixVegNrg) = stateVecInit(ixVegNrg) + (airtemp - stateVecInit(ixVegNrg))*tempAccelerate if(ixHowHeatCap == enthalpyFD)then ! compute H_T at the beginning of the data step without phase change diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 2651c459b..8f8eabd4d 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -350,14 +350,14 @@ subroutine systemSolvSundials(& ! initialize the trial state vectors stateVecTrial = stateVecInit + ! Changing in read_icond, not here ! need to intialize canopy water at a positive value - if(ixVegHyd/=integerMissing)then - if(stateVecTrial(ixVegHyd) < xMinCanopyWater) stateVecTrial(ixVegHyd) = stateVecTrial(ixVegHyd) + xMinCanopyWater - endif - + !if(ixVegHyd/=integerMissing)then + ! if(stateVecTrial(ixVegHyd) < xMinCanopyWater) stateVecTrial(ixVegHyd) = stateVecTrial(ixVegHyd) + xMinCanopyWater + !endif ! try to accelerate solution for energy - if(ixCasNrg/=integerMissing) stateVecTrial(ixCasNrg) = stateVecInit(ixCasNrg) + (airtemp - stateVecInit(ixCasNrg))*tempAccelerate - if(ixVegNrg/=integerMissing) stateVecTrial(ixVegNrg) = stateVecInit(ixVegNrg) + (airtemp - stateVecInit(ixVegNrg))*tempAccelerate + !if(ixCasNrg/=integerMissing) stateVecTrial(ixCasNrg) = stateVecInit(ixCasNrg) + (airtemp - stateVecInit(ixCasNrg))*tempAccelerate + !if(ixVegNrg/=integerMissing) stateVecTrial(ixVegNrg) = stateVecInit(ixVegNrg) + (airtemp - stateVecInit(ixVegNrg))*tempAccelerate if(ixHowHeatCap == enthalpyFD)then ! compute H_T at the beginning of the data step without phase change diff --git a/build/source/engine/updateVarsSundials.f90 b/build/source/engine/updateVarsSundials.f90 index acb8e7154..35b026873 100644 --- a/build/source/engine/updateVarsSundials.f90 +++ b/build/source/engine/updateVarsSundials.f90 @@ -610,7 +610,7 @@ subroutine updateVarsSundials(& ! ------------------------ ! check the need to adjust temperature (will always be false if inside solver) - ! can be true if inside varSubstepSundials, outside solver, but currently will not work so turn off + ! can be true if inside varSubstepSundials, outside solver, but currently will not work so turn off always and not sure should ever do this if(do_adjustTemp .and. computJac)then ! get the melt energy diff --git a/build/source/netcdf/read_icond.f90 b/build/source/netcdf/read_icond.f90 index f31f74c32..c33b0795f 100644 --- a/build/source/netcdf/read_icond.f90 +++ b/build/source/netcdf/read_icond.f90 @@ -323,6 +323,11 @@ subroutine read_icond(iconFile, & ! intent(in): name of progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarSnowAlbedo)%dat(1) = mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%albedoMax)%dat(1) endif + ! make sure canopy water is positive + if( progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyLiq)%dat(1) < 0.0001_rkind)then + progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyLiq)%dat(1) = 0.0001_rkind + endif + ! initialize the spectral albedo progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%spectralSnowAlbedoDiffuse)%dat(1:nBand) = progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarSnowAlbedo)%dat(1) From c0f7fa266e337fad55b77bc8e51e3e97c5c7e579 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 27 Jan 2023 02:01:40 +0900 Subject: [PATCH 0508/1472] trying to fix the changing of scalarCanopyWat if it is 0 --- build/source/engine/systemSolv.f90 | 12 ++++++------ build/source/engine/systemSolvSundials.f90 | 12 ++++++------ build/source/engine/updateVarsSundials.f90 | 2 +- build/source/netcdf/read_icond.f90 | 5 +++++ 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 69c17e996..e939ac9b8 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -384,14 +384,14 @@ subroutine systemSolv(& ! initialize the trial state vectors stateVecTrial = stateVecInit + ! Changing in read_icond, not here ! need to intialize canopy water at a positive value - if(ixVegHyd/=integerMissing)then - if(stateVecTrial(ixVegHyd) < xMinCanopyWater) stateVecTrial(ixVegHyd) = stateVecTrial(ixVegHyd) + xMinCanopyWater - endif - + !if(ixVegHyd/=integerMissing)then + ! if(stateVecTrial(ixVegHyd) < xMinCanopyWater) stateVecTrial(ixVegHyd) = stateVecTrial(ixVegHyd) + xMinCanopyWater + !endif ! try to accelerate solution for energy - if(ixCasNrg/=integerMissing) stateVecTrial(ixCasNrg) = stateVecInit(ixCasNrg) + (airtemp - stateVecInit(ixCasNrg))*tempAccelerate - if(ixVegNrg/=integerMissing) stateVecTrial(ixVegNrg) = stateVecInit(ixVegNrg) + (airtemp - stateVecInit(ixVegNrg))*tempAccelerate + !if(ixCasNrg/=integerMissing) stateVecTrial(ixCasNrg) = stateVecInit(ixCasNrg) + (airtemp - stateVecInit(ixCasNrg))*tempAccelerate + !if(ixVegNrg/=integerMissing) stateVecTrial(ixVegNrg) = stateVecInit(ixVegNrg) + (airtemp - stateVecInit(ixVegNrg))*tempAccelerate if(ixHowHeatCap == enthalpyFD)then ! compute H_T at the beginning of the data step without phase change diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 2651c459b..8f8eabd4d 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -350,14 +350,14 @@ subroutine systemSolvSundials(& ! initialize the trial state vectors stateVecTrial = stateVecInit + ! Changing in read_icond, not here ! need to intialize canopy water at a positive value - if(ixVegHyd/=integerMissing)then - if(stateVecTrial(ixVegHyd) < xMinCanopyWater) stateVecTrial(ixVegHyd) = stateVecTrial(ixVegHyd) + xMinCanopyWater - endif - + !if(ixVegHyd/=integerMissing)then + ! if(stateVecTrial(ixVegHyd) < xMinCanopyWater) stateVecTrial(ixVegHyd) = stateVecTrial(ixVegHyd) + xMinCanopyWater + !endif ! try to accelerate solution for energy - if(ixCasNrg/=integerMissing) stateVecTrial(ixCasNrg) = stateVecInit(ixCasNrg) + (airtemp - stateVecInit(ixCasNrg))*tempAccelerate - if(ixVegNrg/=integerMissing) stateVecTrial(ixVegNrg) = stateVecInit(ixVegNrg) + (airtemp - stateVecInit(ixVegNrg))*tempAccelerate + !if(ixCasNrg/=integerMissing) stateVecTrial(ixCasNrg) = stateVecInit(ixCasNrg) + (airtemp - stateVecInit(ixCasNrg))*tempAccelerate + !if(ixVegNrg/=integerMissing) stateVecTrial(ixVegNrg) = stateVecInit(ixVegNrg) + (airtemp - stateVecInit(ixVegNrg))*tempAccelerate if(ixHowHeatCap == enthalpyFD)then ! compute H_T at the beginning of the data step without phase change diff --git a/build/source/engine/updateVarsSundials.f90 b/build/source/engine/updateVarsSundials.f90 index acb8e7154..35b026873 100644 --- a/build/source/engine/updateVarsSundials.f90 +++ b/build/source/engine/updateVarsSundials.f90 @@ -610,7 +610,7 @@ subroutine updateVarsSundials(& ! ------------------------ ! check the need to adjust temperature (will always be false if inside solver) - ! can be true if inside varSubstepSundials, outside solver, but currently will not work so turn off + ! can be true if inside varSubstepSundials, outside solver, but currently will not work so turn off always and not sure should ever do this if(do_adjustTemp .and. computJac)then ! get the melt energy diff --git a/build/source/netcdf/read_icond.f90 b/build/source/netcdf/read_icond.f90 index f31f74c32..c33b0795f 100644 --- a/build/source/netcdf/read_icond.f90 +++ b/build/source/netcdf/read_icond.f90 @@ -323,6 +323,11 @@ subroutine read_icond(iconFile, & ! intent(in): name of progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarSnowAlbedo)%dat(1) = mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%albedoMax)%dat(1) endif + ! make sure canopy water is positive + if( progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyLiq)%dat(1) < 0.0001_rkind)then + progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyLiq)%dat(1) = 0.0001_rkind + endif + ! initialize the spectral albedo progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%spectralSnowAlbedoDiffuse)%dat(1:nBand) = progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarSnowAlbedo)%dat(1) From 57d595ea09a8681857ba007864435344461e2b6b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 30 Jan 2023 10:38:32 +0900 Subject: [PATCH 0509/1472] print statements --- build/source/engine/coupled_em.f90 | 30 ++++++++++-------- build/source/engine/varSubstep.f90 | 36 +++++++++++----------- build/source/engine/varSubstepSundials.f90 | 36 +++++++++++----------- 3 files changed, 53 insertions(+), 49 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index a80716df1..38fb96246 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1026,20 +1026,21 @@ subroutine coupled_em(& ! NOTE: need to put the balance checks in the sub-step loop so that we can re-compute if necessary scalarCanopyWatBalError = balanceCanopyWater1 - (balanceCanopyWater0 + (scalarSnowfall - averageThroughfallSnow)*data_step + (scalarRainfall - averageThroughfallRain)*data_step & - averageCanopySnowUnloading*data_step - averageCanopyLiqDrainage*data_step + averageCanopySublimation*data_step + averageCanopyEvaporation*data_step) + print*, 'coupled_em canopy, tolerance', absConvTol_liquid*iden_water*10._rkind + write(*,'(a,1x,e20.10)') 'data_step = ', data_step + write(*,'(a,1x,e20.10)') 'balanceCanopyWater0 = ', balanceCanopyWater0 + write(*,'(a,1x,e20.10)') 'balanceCanopyWater1 = ', balanceCanopyWater1 + write(*,'(a,1x,e20.10)') 'scalarSnowfall = ', scalarSnowfall + write(*,'(a,1x,e20.10)') 'scalarRainfall = ', scalarRainfall + write(*,'(a,1x,e20.10)') '(scalarSnowfall - averageThroughfallSnow) = ', (scalarSnowfall - averageThroughfallSnow)!*data_step + write(*,'(a,1x,e20.10)') '(scalarRainfall - averageThroughfallRain) = ', (scalarRainfall - averageThroughfallRain)!*data_step + write(*,'(a,1x,e20.10)') 'averageCanopySnowUnloading = ', averageCanopySnowUnloading!*data_step + write(*,'(a,1x,e20.10)') 'averageCanopyLiqDrainage = ', averageCanopyLiqDrainage!*data_step + write(*,'(a,1x,e20.10)') 'averageCanopySublimation = ', averageCanopySublimation!*data_step + write(*,'(a,1x,e20.10)') 'averageCanopyEvaporation = ', averageCanopyEvaporation!*data_step + write(*,'(a,1x,e20.10)') 'scalarCanopyWatBalError = ', scalarCanopyWatBalError if(abs(scalarCanopyWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then print*, '** canopy water balance error:' - write(*,'(a,1x,f20.10)') 'data_step = ', data_step - write(*,'(a,1x,f20.10)') 'balanceCanopyWater0 = ', balanceCanopyWater0 - write(*,'(a,1x,f20.10)') 'balanceCanopyWater1 = ', balanceCanopyWater1 - write(*,'(a,1x,f20.10)') 'scalarSnowfall = ', scalarSnowfall - write(*,'(a,1x,f20.10)') 'scalarRainfall = ', scalarRainfall - write(*,'(a,1x,f20.10)') '(scalarSnowfall - averageThroughfallSnow) = ', (scalarSnowfall - averageThroughfallSnow)!*data_step - write(*,'(a,1x,f20.10)') '(scalarRainfall - averageThroughfallRain) = ', (scalarRainfall - averageThroughfallRain)!*data_step - write(*,'(a,1x,f20.10)') 'averageCanopySnowUnloading = ', averageCanopySnowUnloading!*data_step - write(*,'(a,1x,f20.10)') 'averageCanopyLiqDrainage = ', averageCanopyLiqDrainage!*data_step - write(*,'(a,1x,f20.10)') 'averageCanopySublimation = ', averageCanopySublimation!*data_step - write(*,'(a,1x,f20.10)') 'averageCanopyEvaporation = ', averageCanopyEvaporation!*data_step - write(*,'(a,1x,f20.10)') 'scalarCanopyWatBalError = ', scalarCanopyWatBalError message=trim(message)//'canopy hydrology does not balance' err=20; return end if @@ -1079,7 +1080,7 @@ subroutine coupled_em(& newSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) delSWE = newSWE - (oldSWE - sfcMeltPond) massBalance = delSWE - (effSnowfall + effRainfall + averageSnowSublimation - averageSnowDrainage*iden_water)*data_step - if(abs(massBalance) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then + print*, 'coupled_em snow, tolerance', absConvTol_liquid*iden_water*10._rkind print*, 'nSnow = ', nSnow print*, 'nSub = ', nSub write(*,'(a,1x,f20.10)') 'data_step = ', data_step @@ -1092,6 +1093,7 @@ subroutine coupled_em(& write(*,'(a,1x,f20.10)') 'snwDrainage = ', averageSnowDrainage*iden_water*data_step write(*,'(a,1x,f20.10)') 'sfcMeltPond = ', sfcMeltPond write(*,'(a,1x,f20.10)') 'massBalance = ', massBalance + if(abs(massBalance) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then message=trim(message)//'SWE does not balance' err=20; return endif ! if failed mass balance check @@ -1134,6 +1136,7 @@ subroutine coupled_em(& ! check the soil water balance scalarSoilWatBalError = balanceSoilWater1 - (balanceSoilWater0 + (balanceSoilInflux + balanceSoilET - balanceSoilBaseflow - balanceSoilDrainage - totalSoilCompress) ) if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues + print*, 'coupled_em soil, tolerance', absConvTol_liquid*iden_water*10._rkind write(*,*) 'solution method = ', ixSolution write(*,'(a,1x,f20.10)') 'data_step = ', data_step write(*,'(a,1x,f20.10)') 'totalSoilCompress = ', totalSoilCompress @@ -1149,6 +1152,7 @@ subroutine coupled_em(& write(*,'(a,1x,f20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError/iden_water write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid ! error control + if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues message=trim(message)//'soil hydrology does not balance' err=20; return end if diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index fa3864a4a..870622a4b 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -791,15 +791,15 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! check the mass balance fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial - !write(*,'(a,1x,f20.10)') 'dt = ', dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - !write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 - !write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 - !write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt - !write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - !write(*,'(a,1x,f20.10)') 'liqError = ', liqError + write(*,'(a,1x,f20.10)') 'dt = ', dt + write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 + write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 + write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt + write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt + write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt + write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt + write(*,'(a,1x,f20.10)') 'liqError = ', liqError if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues waterBalanceError = .true. return @@ -815,16 +815,16 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) + write(*,'(a,1x,f20.10)') 'dt = ', dt + write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 + write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 + write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux + write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink + write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink + write(*,'(a,1x,f20.10)') 'compSink = ', compSink + write(*,'(a,1x,f20.10)') 'liqError = ', liqError + write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues - !write(*,'(a,1x,f20.10)') 'dt = ', dt - !write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 - !write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 - !write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux - !write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink - !write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink - !write(*,'(a,1x,f20.10)') 'compSink = ', compSink - !write(*,'(a,1x,f20.10)') 'liqError = ', liqError - !write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid waterBalanceError = .true. return endif ! if there is a water balance error diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index 47b7fc3a7..f81dee840 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -903,16 +903,16 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux ! check the mass balance fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial + write(*,'(a,1x,f20.10)') 'dt = ', dt + write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 + write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 + write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt + write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt + write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt + write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt + write(*,'(a,1x,f20.10)') 'liqError = ', liqError if(abs(liqError) > absConvTol_liquid*10._rkind*iden_water)then ! *10 because of precision issues - !write(*,'(a,1x,f20.10)') 'dt = ', dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - !write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 - !write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 - !write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt - !write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - !write(*,'(a,1x,f20.10)') 'liqError = ', liqError waterBalanceError = .true. return endif ! if there is a water balance error @@ -927,16 +927,16 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) + write(*,'(a,1x,f20.10)') 'dt = ', dt + write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 + write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 + write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux + write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink + write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink + write(*,'(a,1x,f20.10)') 'compSink = ', compSink + write(*,'(a,1x,f20.10)') 'liqError = ', liqError + write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues - !write(*,'(a,1x,f20.10)') 'dt = ', dt - !write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 - !write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 - !write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux - !write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink - !write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink - !write(*,'(a,1x,f20.10)') 'compSink = ', compSink - !write(*,'(a,1x,f20.10)') 'liqError = ', liqError - !write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid waterBalanceError = .true. return endif ! if there is a water balance error From 484fc3409a4636238a4d56587526c58a37be45d9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 30 Jan 2023 10:39:48 +0900 Subject: [PATCH 0510/1472] decrease sundials tolerance --- build/source/engine/tol4IDA.f90 | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/build/source/engine/tol4IDA.f90 b/build/source/engine/tol4IDA.f90 index 7709e7a3e..8c16f22d2 100644 --- a/build/source/engine/tol4IDA.f90 +++ b/build/source/engine/tol4IDA.f90 @@ -155,21 +155,20 @@ subroutine popTol4IDA(& integer(i4b) :: iLayer ! index of layer within the snow+soil domain integer(i4b) :: ixStateSubset ! index within the state subset logical(lgt),dimension(nState) :: tolFlag ! flag to denote that the state is populated - real(rkind) :: absTolTempCas = 1e-6 ! could use absConvTol_energy 1e-6 = 1e-0 bEuler - real(rkind) :: relTolTempCas = 1e-6 ! could use relConvTol_energy 1e-6 = 1e-2 bEuler - real(rkind) :: absTolTempVeg = 1e-6 ! could use absConvTol_energy - real(rkind) :: relTolTempVeg = 1e-6 ! could use relConvTol_energy - real(rkind) :: absTolWatVeg = 1e-6 ! could use absConvTol_liquid 1e-6 = 1e-5 bEuler - real(rkind) :: relTolWatVeg = 1e-6 ! could use relConvTol_liquid 1e-6 = 1e-3 bEuler - real(rkind) :: absTolTempSoilSnow = 1e-6 ! could use absConvTol_energy - real(rkind) :: relTolTempSoilSnow = 1e-6 ! could use relConvTol_energy - real(rkind) :: absTolWatSnow = 1e-6 ! could use absConvTol_liquid - real(rkind) :: relTolWatSnow = 1e-6 ! could use relConvTol_liquid - real(rkind) :: absTolMatric = 1e-6 ! could use absConvTol_matric 1e-6 = 1e-6 bEuler - real(rkind) :: relTolMatric = 1e-6 ! could use relConvTol_matric 1e-6 = 1e-6 bEuler - real(rkind) :: absTolAquifr = 1e-6 ! could use absConvTol_aquifr 1e-6 = 1e-5 bEuler - real(rkind) :: relTolAquifr = 1e-6 ! could use relConvTol_aquifr 1e-6 = 1e-0 bEuler - + real(rkind) :: absTolTempCas = 1e-8 ! could use absConvTol_energy 1e-6 = 1e-0 bEuler + real(rkind) :: relTolTempCas = 1e-8 ! could use relConvTol_energy 1e-6 = 1e-2 bEuler + real(rkind) :: absTolTempVeg = 1e-8 ! could use absConvTol_energy + real(rkind) :: relTolTempVeg = 1e-8 ! could use relConvTol_energy + real(rkind) :: absTolWatVeg = 1e-8 ! could use absConvTol_liquid 1e-6 = 1e-5 bEuler + real(rkind) :: relTolWatVeg = 1e-8 ! could use relConvTol_liquid 1e-6 = 1e-3 bEuler + real(rkind) :: absTolTempSoilSnow = 1e-8 ! could use absConvTol_energy + real(rkind) :: relTolTempSoilSnow = 1e-8 ! could use relConvTol_energy + real(rkind) :: absTolWatSnow = 1e-8 ! could use absConvTol_liquid + real(rkind) :: relTolWatSnow = 1e-8 ! could use relConvTol_liquid + real(rkind) :: absTolMatric = 1e-8 ! could use absConvTol_matric 1e-6 = 1e-6 bEuler + real(rkind) :: relTolMatric = 1e-8 ! could use relConvTol_matric 1e-6 = 1e-6 bEuler + real(rkind) :: absTolAquifr = 1e-8 ! could use absConvTol_aquifr 1e-6 = 1e-5 bEuler + real(rkind) :: relTolAquifr = 1e-8 ! could use relConvTol_aquifr 1e-6 = 1e-0 bEuler ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- From 18bf24dd1446abdc5eb669b6fb9cb3cf22012399 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 30 Jan 2023 11:31:11 +0900 Subject: [PATCH 0511/1472] missed if statement --- build/source/engine/coupled_em.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 38fb96246..b7ab56f41 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1135,7 +1135,6 @@ subroutine coupled_em(& ! check the soil water balance scalarSoilWatBalError = balanceSoilWater1 - (balanceSoilWater0 + (balanceSoilInflux + balanceSoilET - balanceSoilBaseflow - balanceSoilDrainage - totalSoilCompress) ) - if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues print*, 'coupled_em soil, tolerance', absConvTol_liquid*iden_water*10._rkind write(*,*) 'solution method = ', ixSolution write(*,'(a,1x,f20.10)') 'data_step = ', data_step From 1908951b320d7e79c8203ea4a45fd1ab48a49aea Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 30 Jan 2023 11:40:43 +0900 Subject: [PATCH 0512/1472] print statements as exponential --- build/source/engine/coupled_em.f90 | 44 +++++++++++----------- build/source/engine/varSubstep.f90 | 32 ++++++++-------- build/source/engine/varSubstepSundials.f90 | 32 ++++++++-------- 3 files changed, 54 insertions(+), 54 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index b7ab56f41..3c9cc675f 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1027,7 +1027,7 @@ subroutine coupled_em(& scalarCanopyWatBalError = balanceCanopyWater1 - (balanceCanopyWater0 + (scalarSnowfall - averageThroughfallSnow)*data_step + (scalarRainfall - averageThroughfallRain)*data_step & - averageCanopySnowUnloading*data_step - averageCanopyLiqDrainage*data_step + averageCanopySublimation*data_step + averageCanopyEvaporation*data_step) print*, 'coupled_em canopy, tolerance', absConvTol_liquid*iden_water*10._rkind - write(*,'(a,1x,e20.10)') 'data_step = ', data_step + write(*,'(a,1x,f20.10)') 'data_step = ', data_step write(*,'(a,1x,e20.10)') 'balanceCanopyWater0 = ', balanceCanopyWater0 write(*,'(a,1x,e20.10)') 'balanceCanopyWater1 = ', balanceCanopyWater1 write(*,'(a,1x,e20.10)') 'scalarSnowfall = ', scalarSnowfall @@ -1084,15 +1084,15 @@ subroutine coupled_em(& print*, 'nSnow = ', nSnow print*, 'nSub = ', nSub write(*,'(a,1x,f20.10)') 'data_step = ', data_step - write(*,'(a,1x,f20.10)') 'oldSWE = ', oldSWE - write(*,'(a,1x,f20.10)') 'newSWE = ', newSWE - write(*,'(a,1x,f20.10)') 'delSWE = ', delSWE - write(*,'(a,1x,f20.10)') 'effRainfall = ', effRainfall*data_step - write(*,'(a,1x,f20.10)') 'effSnowfall = ', effSnowfall*data_step - write(*,'(a,1x,f20.10)') 'sublimation = ', averageSnowSublimation*data_step - write(*,'(a,1x,f20.10)') 'snwDrainage = ', averageSnowDrainage*iden_water*data_step - write(*,'(a,1x,f20.10)') 'sfcMeltPond = ', sfcMeltPond - write(*,'(a,1x,f20.10)') 'massBalance = ', massBalance + write(*,'(a,1x,e20.10)') 'oldSWE = ', oldSWE + write(*,'(a,1x,e20.10)') 'newSWE = ', newSWE + write(*,'(a,1x,e20.10)') 'delSWE = ', delSWE + write(*,'(a,1x,e20.10)') 'effRainfall = ', effRainfall*data_step + write(*,'(a,1x,e20.10)') 'effSnowfall = ', effSnowfall*data_step + write(*,'(a,1x,e20.10)') 'sublimation = ', averageSnowSublimation*data_step + write(*,'(a,1x,e20.10)') 'snwDrainage = ', averageSnowDrainage*iden_water*data_step + write(*,'(a,1x,e20.10)') 'sfcMeltPond = ', sfcMeltPond + write(*,'(a,1x,e20.10)') 'massBalance = ', massBalance if(abs(massBalance) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then message=trim(message)//'SWE does not balance' err=20; return @@ -1138,18 +1138,18 @@ subroutine coupled_em(& print*, 'coupled_em soil, tolerance', absConvTol_liquid*iden_water*10._rkind write(*,*) 'solution method = ', ixSolution write(*,'(a,1x,f20.10)') 'data_step = ', data_step - write(*,'(a,1x,f20.10)') 'totalSoilCompress = ', totalSoilCompress - write(*,'(a,1x,f20.10)') 'scalarTotalSoilLiq = ', scalarTotalSoilLiq - write(*,'(a,1x,f20.10)') 'scalarTotalSoilIce = ', scalarTotalSoilIce - write(*,'(a,1x,f20.10)') 'balanceSoilWater0 = ', balanceSoilWater0 - write(*,'(a,1x,f20.10)') 'balanceSoilWater1 = ', balanceSoilWater1 - write(*,'(a,1x,f20.10)') 'balanceSoilInflux = ', balanceSoilInflux - write(*,'(a,1x,f20.10)') 'balanceSoilBaseflow = ', balanceSoilBaseflow - write(*,'(a,1x,f20.10)') 'balanceSoilDrainage = ', balanceSoilDrainage - write(*,'(a,1x,f20.10)') 'balanceSoilET = ', balanceSoilET - write(*,'(a,1x,f20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError - write(*,'(a,1x,f20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError/iden_water - write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid + write(*,'(a,1x,e20.10)') 'totalSoilCompress = ', totalSoilCompress + write(*,'(a,1x,e20.10)') 'scalarTotalSoilLiq = ', scalarTotalSoilLiq + write(*,'(a,1x,e20.10)') 'scalarTotalSoilIce = ', scalarTotalSoilIce + write(*,'(a,1x,e20.10)') 'balanceSoilWater0 = ', balanceSoilWater0 + write(*,'(a,1x,e20.10)') 'balanceSoilWater1 = ', balanceSoilWater1 + write(*,'(a,1x,e20.10)') 'balanceSoilInflux = ', balanceSoilInflux + write(*,'(a,1x,e20.10)') 'balanceSoilBaseflow = ', balanceSoilBaseflow + write(*,'(a,1x,e20.10)') 'balanceSoilDrainage = ', balanceSoilDrainage + write(*,'(a,1x,e20.10)') 'balanceSoilET = ', balanceSoilET + write(*,'(a,1x,e20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError + write(*,'(a,1x,e20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError/iden_water + write(*,'(a,1x,e20.10)') 'absConvTol_liquid = ', absConvTol_liquid ! error control if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues message=trim(message)//'soil hydrology does not balance' diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 870622a4b..ef831f59b 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -792,14 +792,14 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 - write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 - write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt - write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt - write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt - write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - write(*,'(a,1x,f20.10)') 'liqError = ', liqError + write(*,'(a,1x,e20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + write(*,'(a,1x,e20.10)') 'canopyBalance0 = ', canopyBalance0 + write(*,'(a,1x,e20.10)') 'canopyBalance1 = ', canopyBalance1 + write(*,'(a,1x,e20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt + write(*,'(a,1x,e20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt + write(*,'(a,1x,e20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt + write(*,'(a,1x,e20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt + write(*,'(a,1x,e20.10)') 'liqError = ', liqError if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues waterBalanceError = .true. return @@ -816,14 +816,14 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 - write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 - write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux - write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink - write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink - write(*,'(a,1x,f20.10)') 'compSink = ', compSink - write(*,'(a,1x,f20.10)') 'liqError = ', liqError - write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid + write(*,'(a,1x,e20.10)') 'soilBalance0 = ', soilBalance0 + write(*,'(a,1x,e20.10)') 'soilBalance1 = ', soilBalance1 + write(*,'(a,1x,e20.10)') 'vertFlux = ', vertFlux + write(*,'(a,1x,e20.10)') 'tranSink = ', tranSink + write(*,'(a,1x,e20.10)') 'baseSink = ', baseSink + write(*,'(a,1x,e20.10)') 'compSink = ', compSink + write(*,'(a,1x,e20.10)') 'liqError = ', liqError + write(*,'(a,1x,e20.10)') 'absConvTol_liquid = ', absConvTol_liquid if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues waterBalanceError = .true. return diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index f81dee840..e3e6d4333 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -904,14 +904,14 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 - write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 - write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt - write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt - write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt - write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - write(*,'(a,1x,f20.10)') 'liqError = ', liqError + write(*,'(a,1x,e20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + write(*,'(a,1x,e20.10)') 'canopyBalance0 = ', canopyBalance0 + write(*,'(a,1x,e20.10)') 'canopyBalance1 = ', canopyBalance1 + write(*,'(a,1x,e20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt + write(*,'(a,1x,e20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt + write(*,'(a,1x,e20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt + write(*,'(a,1x,e20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt + write(*,'(a,1x,e20.10)') 'liqError = ', liqError if(abs(liqError) > absConvTol_liquid*10._rkind*iden_water)then ! *10 because of precision issues waterBalanceError = .true. return @@ -928,14 +928,14 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 - write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 - write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux - write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink - write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink - write(*,'(a,1x,f20.10)') 'compSink = ', compSink - write(*,'(a,1x,f20.10)') 'liqError = ', liqError - write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid + write(*,'(a,1x,e20.10)') 'soilBalance0 = ', soilBalance0 + write(*,'(a,1x,e20.10)') 'soilBalance1 = ', soilBalance1 + write(*,'(a,1x,e20.10)') 'vertFlux = ', vertFlux + write(*,'(a,1x,e20.10)') 'tranSink = ', tranSink + write(*,'(a,1x,e20.10)') 'baseSink = ', baseSink + write(*,'(a,1x,e20.10)') 'compSink = ', compSink + write(*,'(a,1x,e20.10)') 'liqError = ', liqError + write(*,'(a,1x,e20.10)') 'absConvTol_liquid = ', absConvTol_liquid if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues waterBalanceError = .true. return From 84ae5d82ea0a132fca96317db14afb01147bc473 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 30 Jan 2023 11:42:37 +0900 Subject: [PATCH 0513/1472] change print step --- build/source/dshare/globalData.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 index 8fe721277..065960479 100644 --- a/build/source/dshare/globalData.f90 +++ b/build/source/dshare/globalData.f90 @@ -347,5 +347,5 @@ MODULE globalData ! printing step frequency - integer(i4b),parameter,public :: print_step_freq = 1000 + integer(i4b),parameter,public :: print_step_freq = 1 END MODULE globalData From 7fe5a19b48b793db5c904793da2e34d3dcb16e2b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 30 Jan 2023 12:46:56 +0900 Subject: [PATCH 0514/1472] more prints and try computing canopy water balance off canopy water variable, not ice+liq --- build/source/engine/coupled_em.f90 | 5 ++++- build/source/engine/varSubstepSundials.f90 | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 3c9cc675f..084fb0313 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -998,6 +998,8 @@ subroutine coupled_em(& ! state variables in the vegetation canopy scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! canopy liquid water (kg m-2) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! canopy ice content (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! canopy ice content (kg m-2) + ! state variables in the soil domain mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers) ,& ! depth of each soil layer (m) mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat(nSnow+1:nLayers) ,& ! volumetric ice content in each soil layer (-) @@ -1020,7 +1022,7 @@ subroutine coupled_em(& if(computeVegFlux)then ! canopy water balance - balanceCanopyWater1 = scalarCanopyLiq + scalarCanopyIce + balanceCanopyWater1 = scalarCanopyWat ! balance checks for the canopy ! NOTE: need to put the balance checks in the sub-step loop so that we can re-compute if necessary @@ -1029,6 +1031,7 @@ subroutine coupled_em(& print*, 'coupled_em canopy, tolerance', absConvTol_liquid*iden_water*10._rkind write(*,'(a,1x,f20.10)') 'data_step = ', data_step write(*,'(a,1x,e20.10)') 'balanceCanopyWater0 = ', balanceCanopyWater0 + write(*,'(a,1x,e20.10)') 'balanceCanopyWater old way = ', scalarCanopyLiq + scalarCanopyIce write(*,'(a,1x,e20.10)') 'balanceCanopyWater1 = ', balanceCanopyWater1 write(*,'(a,1x,e20.10)') 'scalarSnowfall = ', scalarSnowfall write(*,'(a,1x,e20.10)') 'scalarRainfall = ', scalarRainfall diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index e3e6d4333..c085e8832 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -395,7 +395,7 @@ subroutine varSubstepSundials(& endif ! identify the need to check the mass balance - checkMassBalance = .false. !do not check for Sundials + checkMassBalance = .true. !do not check for Sundials checkNrgBalance = .false. !do not check for Sundials, also only check if ixHowHeatCap == enthalpyFD ! update prognostic variables From 50458ce493522517477e990d0b52cfcd17b4a15f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 30 Jan 2023 13:19:10 +0900 Subject: [PATCH 0515/1472] make liqerror canopy take snow into account, and add variables to print --- build/source/engine/varSubstep.f90 | 26 +++++++++++------- build/source/engine/varSubstepSundials.f90 | 31 ++++++++++++++-------- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index ef831f59b..0357a35d3 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -626,6 +626,9 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in) : [dp(:)] change in storage associated with compression of the soil matrix (-) scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the vegetation canopy (kg m-2 s-1) scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the snow surface (kg m-2 s-1) + scalarSnowfall => flux_data%var(iLookFLUX%scalarSnowfall)%dat(1) ,& ! intent(in) : [dp] computed snowfall rate (kg m-2 s-1) + scalarThroughfallSnow => flux_data%var(iLookFLUX%scalarThroughfallSnow)%dat(1) ,& ! intent(in) : [dp] snow that reaches the ground without ever touching the canopy (kg m-2 s-1) + scalarCanopySnowUnloading => flux_data%var(iLookFLUX%scalarCanopySnowUnloading)%dat(1) ,& ! intent(in) : [dp] unloading of snow from the vegetion canopy (kg m-2 s-1) ! energy fluxes scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ,& ! intent(in) : [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ,& ! intent(in) : [dp] sensible heat flux from the canopy to the canopy air space (W m-2) @@ -789,17 +792,22 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe endif ! cases where fluxes empty the canopy ! check the mass balance - fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage & + + scalarSnowfall - scalarThroughfallSnow - scalarCanopySnowUnloading - scalarCanopyLiqDrainage + scalarCanopySublimation liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,e20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - write(*,'(a,1x,e20.10)') 'canopyBalance0 = ', canopyBalance0 - write(*,'(a,1x,e20.10)') 'canopyBalance1 = ', canopyBalance1 - write(*,'(a,1x,e20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt - write(*,'(a,1x,e20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt - write(*,'(a,1x,e20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt - write(*,'(a,1x,e20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - write(*,'(a,1x,e20.10)') 'liqError = ', liqError + write(*,'(a,1x,e20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + write(*,'(a,1x,e20.10)') 'canopyBalance0 = ', canopyBalance0 + write(*,'(a,1x,e20.10)') 'canopyBalance1 = ', canopyBalance1 + write(*,'(a,1x,e20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt + write(*,'(a,1x,e20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt + write(*,'(a,1x,e20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt + write(*,'(a,1x,e20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt + write(*,'(a,1x,e20.10)') 'scalarSnowfall*dt = ', scalarSnowfall*dt + write(*,'(a,1x,e20.10)') 'scalarCanopySublimatione*dt = ', scalarCanopySublimation*dt + write(*,'(a,1x,e20.10)') 'scalarCanopySnowUnloading*dt = ', scalarCanopySnowUnloading*dt + write(*,'(a,1x,e20.10)') 'scalarThroughfallSnow*dt = ', scalarThroughfallSnow*dt + write(*,'(a,1x,e20.10)') 'liqError = ', liqError if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues waterBalanceError = .true. return diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index c085e8832..5aa5138ab 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -644,6 +644,10 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in) : [dp(:)] change in storage associated with compression of the soil matrix (-) scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the vegetation canopy (kg m-2 s-1) scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the snow surface (kg m-2 s-1) + scalarSnowfall => flux_data%var(iLookFLUX%scalarSnowfall)%dat(1) ,& ! intent(in) : [dp] computed snowfall rate (kg m-2 s-1) + scalarThroughfallSnow => flux_data%var(iLookFLUX%scalarThroughfallSnow)%dat(1) ,& ! intent(in) : [dp] snow that reaches the ground without ever touching the canopy (kg m-2 s-1) + scalarCanopySnowUnloading => flux_data%var(iLookFLUX%scalarCanopySnowUnloading)%dat(1) ,& ! intent(in) : [dp] unloading of snow from the vegetion canopy (kg m-2 s-1) + ! energy fluxes scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ,& ! intent(in) : [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ,& ! intent(in) : [dp] sensible heat flux from the canopy to the canopy air space (W m-2) @@ -901,18 +905,23 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux endif ! cases where fluxes empty the canopy ! check the mass balance - fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage & + + scalarSnowfall - scalarThroughfallSnow - scalarCanopySnowUnloading - scalarCanopyLiqDrainage + scalarCanopySublimation liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial - write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,e20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - write(*,'(a,1x,e20.10)') 'canopyBalance0 = ', canopyBalance0 - write(*,'(a,1x,e20.10)') 'canopyBalance1 = ', canopyBalance1 - write(*,'(a,1x,e20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt - write(*,'(a,1x,e20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt - write(*,'(a,1x,e20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt - write(*,'(a,1x,e20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - write(*,'(a,1x,e20.10)') 'liqError = ', liqError - if(abs(liqError) > absConvTol_liquid*10._rkind*iden_water)then ! *10 because of precision issues + write(*,'(a,1x,f20.10)') 'dt = ', dt + write(*,'(a,1x,e20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + write(*,'(a,1x,e20.10)') 'canopyBalance0 = ', canopyBalance0 + write(*,'(a,1x,e20.10)') 'canopyBalance1 = ', canopyBalance1 + write(*,'(a,1x,e20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt + write(*,'(a,1x,e20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt + write(*,'(a,1x,e20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt + write(*,'(a,1x,e20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt + write(*,'(a,1x,e20.10)') 'scalarSnowfall*dt = ', scalarSnowfall*dt + write(*,'(a,1x,e20.10)') 'scalarCanopySublimatione*dt = ', scalarCanopySublimation*dt + write(*,'(a,1x,e20.10)') 'scalarCanopySnowUnloading*dt = ', scalarCanopySnowUnloading*dt + write(*,'(a,1x,e20.10)') 'scalarThroughfallSnow*dt = ', scalarThroughfallSnow*dt + write(*,'(a,1x,e20.10)') 'liqError = ', liqError if(abs(liqError) > absConvTol_liquid*10._rkind*iden_water)then ! *10 because of precision issues + if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues waterBalanceError = .true. return endif ! if there is a water balance error From cf9cf7cd30a9b35b733c809234b7df6898103e56 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 30 Jan 2023 13:21:58 +0900 Subject: [PATCH 0516/1472] messed up if statement --- build/source/engine/varSubstepSundials.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index 5aa5138ab..f7404f8b1 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -920,7 +920,7 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux write(*,'(a,1x,e20.10)') 'scalarCanopySublimatione*dt = ', scalarCanopySublimation*dt write(*,'(a,1x,e20.10)') 'scalarCanopySnowUnloading*dt = ', scalarCanopySnowUnloading*dt write(*,'(a,1x,e20.10)') 'scalarThroughfallSnow*dt = ', scalarThroughfallSnow*dt - write(*,'(a,1x,e20.10)') 'liqError = ', liqError if(abs(liqError) > absConvTol_liquid*10._rkind*iden_water)then ! *10 because of precision issues + write(*,'(a,1x,e20.10)') 'liqError = ', liqError if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues waterBalanceError = .true. return From 325c7a7e5275532399cef62f2b1c5f8fa15b794d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 30 Jan 2023 13:52:35 +0900 Subject: [PATCH 0517/1472] mass check in varSubstep should be looking at non-updated variables. redid "cases where fluxes empty the canopy", not sure this is correct --- build/source/engine/varSubstep.f90 | 20 +++++++++++--------- build/source/engine/varSubstepSundials.f90 | 13 +++++++------ 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 0357a35d3..ff641b465 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -661,8 +661,8 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe waterBalanceError=.false. ! get storage at the start of the step - canopyBalance0 = merge(scalarCanopyWat, realMissing, computeVegFlux) - soilBalance0 = sum( (mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) + canopyBalance0 = merge(scalarCanopyLiq + scalarCanopyIce, realMissing, computeVegFlux) + soilBalance0 = sum( (mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) ! ----- ! * update states... @@ -753,11 +753,13 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe if(ixVegHyd/=integerMissing)then ! handle cases where fluxes empty the canopy - fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage & + + scalarSnowfall - scalarThroughfallSnow - scalarCanopySnowUnloading + scalarCanopySublimation if(-fluxNet*dt > canopyBalance0)then ! --> first add water - canopyBalance1 = canopyBalance0 + (scalarRainfall - scalarThroughfallRain)*dt + canopyBalance1 = canopyBalance0 + (scalarRainfall - scalarThroughfallRain)*dt & + + (scalarSnowfall - scalarThroughfallSnow)*dt ! --> next, remove canopy evaporation -- put the unsatisfied evap into sensible heat canopyBalance1 = canopyBalance1 + scalarCanopyEvaporation*dt @@ -772,10 +774,10 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg endif - ! --> next, remove canopy drainage - canopyBalance1 = canopyBalance1 - scalarCanopyLiqDrainage*dt + ! --> next, remove canopy drainage, snow unloading, sublimination + canopyBalance1 = canopyBalance1 + (-scalarCanopyLiqDrainage- scalarCanopySnowUnloading + scalarCanopySublimation)*dt if(canopyBalance1 < 0._rkind)then - superflousWat = -canopyBalance1/dt ! kg m-2 s-1 + superflousWat = -canopyBalance1/dt ! kg m-2 s-1 canopyBalance1 = 0._rkind scalarCanopyLiqDrainage = scalarCanopyLiqDrainage + superflousWat endif @@ -793,7 +795,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! check the mass balance fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage & - + scalarSnowfall - scalarThroughfallSnow - scalarCanopySnowUnloading - scalarCanopyLiqDrainage + scalarCanopySublimation + + scalarSnowfall - scalarThroughfallSnow - scalarCanopySnowUnloading + scalarCanopySublimation liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial write(*,'(a,1x,f20.10)') 'dt = ', dt write(*,'(a,1x,e20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial @@ -804,7 +806,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe write(*,'(a,1x,e20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt write(*,'(a,1x,e20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt write(*,'(a,1x,e20.10)') 'scalarSnowfall*dt = ', scalarSnowfall*dt - write(*,'(a,1x,e20.10)') 'scalarCanopySublimatione*dt = ', scalarCanopySublimation*dt + write(*,'(a,1x,e20.10)') 'scalarCanopySublimation*dt = ', scalarCanopySublimation*dt write(*,'(a,1x,e20.10)') 'scalarCanopySnowUnloading*dt = ', scalarCanopySnowUnloading*dt write(*,'(a,1x,e20.10)') 'scalarThroughfallSnow*dt = ', scalarThroughfallSnow*dt write(*,'(a,1x,e20.10)') 'liqError = ', liqError diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index f7404f8b1..dec89a030 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -690,8 +690,8 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux waterBalanceError=.false. ! get storage at the start of the step - canopyBalance0 = merge(scalarCanopyWat, realMissing, computeVegFlux) - soilBalance0 = sum( (mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) + canopyBalance0 = merge(scalarCanopyLiq + scalarCanopyIce, realMissing, computeVegFlux) + soilBalance0 = sum( (mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) ! ----- ! * update states... @@ -866,7 +866,8 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux if(ixVegHyd/=integerMissing)then ! handle cases where fluxes empty the canopy - fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage & + + scalarSnowfall - scalarThroughfallSnow - scalarCanopySnowUnloading + scalarCanopySublimation if(-fluxNet*dt > canopyBalance0)then ! --> first add water @@ -885,8 +886,8 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg endif - ! --> next, remove canopy drainage - canopyBalance1 = canopyBalance1 - scalarCanopyLiqDrainage*dt + ! --> next, remove canopy drainage, snow unloading, sublimination + canopyBalance1 = canopyBalance1 + (-scalarCanopyLiqDrainage- scalarCanopySnowUnloading + scalarCanopySublimation)*dt if(canopyBalance1 < 0._rkind)then superflousWat = -canopyBalance1/dt ! kg m-2 s-1 canopyBalance1 = 0._rkind @@ -917,7 +918,7 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux write(*,'(a,1x,e20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt write(*,'(a,1x,e20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt write(*,'(a,1x,e20.10)') 'scalarSnowfall*dt = ', scalarSnowfall*dt - write(*,'(a,1x,e20.10)') 'scalarCanopySublimatione*dt = ', scalarCanopySublimation*dt + write(*,'(a,1x,e20.10)') 'scalarCanopySublimation*dt = ', scalarCanopySublimation*dt write(*,'(a,1x,e20.10)') 'scalarCanopySnowUnloading*dt = ', scalarCanopySnowUnloading*dt write(*,'(a,1x,e20.10)') 'scalarThroughfallSnow*dt = ', scalarThroughfallSnow*dt write(*,'(a,1x,e20.10)') 'liqError = ', liqError From 4ff60baa22abe35c0b996fac33135a5f459cd223 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 30 Jan 2023 15:24:02 +0900 Subject: [PATCH 0518/1472] update canopyWater after newSnowfall and changes outside solver --- build/source/engine/coupled_em.f90 | 4 ++-- build/source/engine/varSubstep.f90 | 20 +++++--------------- build/source/engine/varSubstepSundials.f90 | 19 +++++-------------- 3 files changed, 12 insertions(+), 31 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 084fb0313..0de59cfa2 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -503,6 +503,7 @@ subroutine coupled_em(& ! ----------------------------------------------- ! NOTE 1: this needs to be done before solving the energy and liquid water equations, to account for the heat advected with precipitation (and throughfall/unloading) ! NOTE 2: the unloading flux is computed using canopy drip (scalarCanopyLiqDrainage) from the previous time step + ! this changes canopy ice call canopySnow(& ! input: model control data_step, & ! intent(in): time step (seconds) @@ -999,7 +1000,6 @@ subroutine coupled_em(& scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! canopy liquid water (kg m-2) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! canopy ice content (kg m-2) scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! canopy ice content (kg m-2) - ! state variables in the soil domain mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers) ,& ! depth of each soil layer (m) mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat(nSnow+1:nLayers) ,& ! volumetric ice content in each soil layer (-) @@ -1020,7 +1020,7 @@ subroutine coupled_em(& ! if computing the vegetation flux if(computeVegFlux)then - + scalarCanopyWat = scalarCanopyLiq + scalarCanopyIce !update ! canopy water balance balanceCanopyWater1 = scalarCanopyWat diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index ff641b465..40268067a 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -626,9 +626,6 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in) : [dp(:)] change in storage associated with compression of the soil matrix (-) scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the vegetation canopy (kg m-2 s-1) scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the snow surface (kg m-2 s-1) - scalarSnowfall => flux_data%var(iLookFLUX%scalarSnowfall)%dat(1) ,& ! intent(in) : [dp] computed snowfall rate (kg m-2 s-1) - scalarThroughfallSnow => flux_data%var(iLookFLUX%scalarThroughfallSnow)%dat(1) ,& ! intent(in) : [dp] snow that reaches the ground without ever touching the canopy (kg m-2 s-1) - scalarCanopySnowUnloading => flux_data%var(iLookFLUX%scalarCanopySnowUnloading)%dat(1) ,& ! intent(in) : [dp] unloading of snow from the vegetion canopy (kg m-2 s-1) ! energy fluxes scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ,& ! intent(in) : [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ,& ! intent(in) : [dp] sensible heat flux from the canopy to the canopy air space (W m-2) @@ -753,13 +750,11 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe if(ixVegHyd/=integerMissing)then ! handle cases where fluxes empty the canopy - fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage & - + scalarSnowfall - scalarThroughfallSnow - scalarCanopySnowUnloading + scalarCanopySublimation + fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage if(-fluxNet*dt > canopyBalance0)then ! --> first add water - canopyBalance1 = canopyBalance0 + (scalarRainfall - scalarThroughfallRain)*dt & - + (scalarSnowfall - scalarThroughfallSnow)*dt + canopyBalance1 = canopyBalance0 + (scalarRainfall - scalarThroughfallRain)*dt ! --> next, remove canopy evaporation -- put the unsatisfied evap into sensible heat canopyBalance1 = canopyBalance1 + scalarCanopyEvaporation*dt @@ -774,8 +769,8 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg endif - ! --> next, remove canopy drainage, snow unloading, sublimination - canopyBalance1 = canopyBalance1 + (-scalarCanopyLiqDrainage- scalarCanopySnowUnloading + scalarCanopySublimation)*dt + ! --> next, remove canopy drainage + canopyBalance1 = canopyBalance1 -scalarCanopyLiqDrainage*dt if(canopyBalance1 < 0._rkind)then superflousWat = -canopyBalance1/dt ! kg m-2 s-1 canopyBalance1 = 0._rkind @@ -794,8 +789,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe endif ! cases where fluxes empty the canopy ! check the mass balance - fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage & - + scalarSnowfall - scalarThroughfallSnow - scalarCanopySnowUnloading + scalarCanopySublimation + fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial write(*,'(a,1x,f20.10)') 'dt = ', dt write(*,'(a,1x,e20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial @@ -805,10 +799,6 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe write(*,'(a,1x,e20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt write(*,'(a,1x,e20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt write(*,'(a,1x,e20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - write(*,'(a,1x,e20.10)') 'scalarSnowfall*dt = ', scalarSnowfall*dt - write(*,'(a,1x,e20.10)') 'scalarCanopySublimation*dt = ', scalarCanopySublimation*dt - write(*,'(a,1x,e20.10)') 'scalarCanopySnowUnloading*dt = ', scalarCanopySnowUnloading*dt - write(*,'(a,1x,e20.10)') 'scalarThroughfallSnow*dt = ', scalarThroughfallSnow*dt write(*,'(a,1x,e20.10)') 'liqError = ', liqError if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues waterBalanceError = .true. diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index dec89a030..fb0ee4b30 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -644,9 +644,6 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in) : [dp(:)] change in storage associated with compression of the soil matrix (-) scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the vegetation canopy (kg m-2 s-1) scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the snow surface (kg m-2 s-1) - scalarSnowfall => flux_data%var(iLookFLUX%scalarSnowfall)%dat(1) ,& ! intent(in) : [dp] computed snowfall rate (kg m-2 s-1) - scalarThroughfallSnow => flux_data%var(iLookFLUX%scalarThroughfallSnow)%dat(1) ,& ! intent(in) : [dp] snow that reaches the ground without ever touching the canopy (kg m-2 s-1) - scalarCanopySnowUnloading => flux_data%var(iLookFLUX%scalarCanopySnowUnloading)%dat(1) ,& ! intent(in) : [dp] unloading of snow from the vegetion canopy (kg m-2 s-1) ! energy fluxes scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ,& ! intent(in) : [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) @@ -866,8 +863,7 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux if(ixVegHyd/=integerMissing)then ! handle cases where fluxes empty the canopy - fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage & - + scalarSnowfall - scalarThroughfallSnow - scalarCanopySnowUnloading + scalarCanopySublimation + fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage if(-fluxNet*dt > canopyBalance0)then ! --> first add water @@ -886,10 +882,10 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg endif - ! --> next, remove canopy drainage, snow unloading, sublimination - canopyBalance1 = canopyBalance1 + (-scalarCanopyLiqDrainage- scalarCanopySnowUnloading + scalarCanopySublimation)*dt + ! --> next, remove canopy drainage + canopyBalance1 = canopyBalance1 -scalarCanopyLiqDrainage*dt if(canopyBalance1 < 0._rkind)then - superflousWat = -canopyBalance1/dt ! kg m-2 s-1 + superflousWat = -canopyBalance1/dt ! kg m-2 s-1 canopyBalance1 = 0._rkind scalarCanopyLiqDrainage = scalarCanopyLiqDrainage + superflousWat endif @@ -906,8 +902,7 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux endif ! cases where fluxes empty the canopy ! check the mass balance - fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage & - + scalarSnowfall - scalarThroughfallSnow - scalarCanopySnowUnloading - scalarCanopyLiqDrainage + scalarCanopySublimation + fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial write(*,'(a,1x,f20.10)') 'dt = ', dt write(*,'(a,1x,e20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial @@ -917,10 +912,6 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux write(*,'(a,1x,e20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt write(*,'(a,1x,e20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt write(*,'(a,1x,e20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - write(*,'(a,1x,e20.10)') 'scalarSnowfall*dt = ', scalarSnowfall*dt - write(*,'(a,1x,e20.10)') 'scalarCanopySublimation*dt = ', scalarCanopySublimation*dt - write(*,'(a,1x,e20.10)') 'scalarCanopySnowUnloading*dt = ', scalarCanopySnowUnloading*dt - write(*,'(a,1x,e20.10)') 'scalarThroughfallSnow*dt = ', scalarThroughfallSnow*dt write(*,'(a,1x,e20.10)') 'liqError = ', liqError if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues waterBalanceError = .true. From b0d76384770f469e994ddda607b1ba85b1be5fef Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 30 Jan 2023 20:40:28 +0900 Subject: [PATCH 0519/1472] print statements --- build/source/engine/coupled_em.f90 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 0de59cfa2..e3f896424 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -744,6 +744,7 @@ subroutine coupled_em(& ! check for all errors (error recovery within opSplittin) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + print*, prog_data%var(iLookPROG%scalarCanopyWat)%dat(1),prog_data%var(iLookPROG%scalarCanopyIce)%dat(1)+prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,'wat1' ! process the flag for too much melt @@ -769,6 +770,7 @@ subroutine coupled_em(& ! try again cycle substeps endif + print*, prog_data%var(iLookPROG%scalarCanopyWat)%dat(1),prog_data%var(iLookPROG%scalarCanopyIce)%dat(1)+prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,'wat2' ! update first step firstSubStep=.false. @@ -908,6 +910,7 @@ subroutine coupled_em(& ! adjust length of the sub-step (make sure that we don't exceed the step) dt_sub = min(data_step - dt_solv, dt_sub) + print*, prog_data%var(iLookPROG%scalarCanopyWat)%dat(1),prog_data%var(iLookPROG%scalarCanopyIce)%dat(1)+prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,'wat3' end do substeps ! (sub-step loop) @@ -935,6 +938,7 @@ subroutine coupled_em(& ! output: error control err,cmessage) ! error control if(err/=0)then; err=30; message=trim(message)//trim(cmessage); return; end if + print*, prog_data%var(iLookPROG%scalarCanopyWat)%dat(1),prog_data%var(iLookPROG%scalarCanopyIce)%dat(1)+prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,'wat4' ! re-compute snow depth and SWE if(nSnow > 0)then @@ -1010,7 +1014,7 @@ subroutine coupled_em(& scalarTotalSoilIce => diag_data%var(iLookDIAG%scalarTotalSoilIce)%dat(1) ,& ! total ice in the soil column (kg m-2) scalarTotalSoilLiq => diag_data%var(iLookDIAG%scalarTotalSoilLiq)%dat(1) & ! total liquid water in the soil column (kg m-2) ) ! (association of local variables with information in the data structures - + print*, prog_data%var(iLookPROG%scalarCanopyWat)%dat(1),prog_data%var(iLookPROG%scalarCanopyIce)%dat(1)+prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,'wat2' ! ----- ! * balance checks for the canopy... ! ---------------------------------- From 9943858b90d56cd94d91cf0d8ebdeab37f76c775 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 30 Jan 2023 21:20:31 +0900 Subject: [PATCH 0520/1472] update layer water outside solver --- build/source/engine/coupled_em.f90 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index e3f896424..a12ac83be 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -869,12 +869,14 @@ subroutine coupled_em(& err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - ! recompute snow depth and SWE + ! recompute snow depth, SWE, and layer water if(nSnow > 0)then prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow)) prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow)*iden_water + & prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice) & * prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) + prog_data%var(iLookPROG%mLayerVolFracWat)%dat(1:nSnow) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow) & + + prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice/iden_water end if ! increment fluxes @@ -940,12 +942,14 @@ subroutine coupled_em(& if(err/=0)then; err=30; message=trim(message)//trim(cmessage); return; end if print*, prog_data%var(iLookPROG%scalarCanopyWat)%dat(1),prog_data%var(iLookPROG%scalarCanopyIce)%dat(1)+prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,'wat4' - ! re-compute snow depth and SWE + ! re-compute snow depth, SWE, and top layer water if(nSnow > 0)then prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow)) prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow)*iden_water + & prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice) & * prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) + prog_data%var(iLookPROG%mLayerVolFracWat)%dat(1) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1) & + + prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1)*iden_ice/iden_water end if ! re-assign dimension lengths From ac14cf1f1eeb1667e94cfe891dc2868020157ebf Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 30 Jan 2023 21:20:47 +0900 Subject: [PATCH 0521/1472] print statement --- build/source/engine/coupled_em.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index a12ac83be..187157958 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1018,7 +1018,7 @@ subroutine coupled_em(& scalarTotalSoilIce => diag_data%var(iLookDIAG%scalarTotalSoilIce)%dat(1) ,& ! total ice in the soil column (kg m-2) scalarTotalSoilLiq => diag_data%var(iLookDIAG%scalarTotalSoilLiq)%dat(1) & ! total liquid water in the soil column (kg m-2) ) ! (association of local variables with information in the data structures - print*, prog_data%var(iLookPROG%scalarCanopyWat)%dat(1),prog_data%var(iLookPROG%scalarCanopyIce)%dat(1)+prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,'wat2' + print*, prog_data%var(iLookPROG%scalarCanopyWat)%dat(1),prog_data%var(iLookPROG%scalarCanopyIce)%dat(1)+prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,'wat5' ! ----- ! * balance checks for the canopy... ! ---------------------------------- From 708a12510d66cc7eb6482cbeca6a16e1d5f55111 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 30 Jan 2023 21:52:55 +0900 Subject: [PATCH 0522/1472] remove print statements and recalculate water at point changed it --- build/source/engine/coupled_em.f90 | 25 ++++++---------------- build/source/engine/varSubstepSundials.f90 | 1 - 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 187157958..4e563dc11 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -744,8 +744,6 @@ subroutine coupled_em(& ! check for all errors (error recovery within opSplittin) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - print*, prog_data%var(iLookPROG%scalarCanopyWat)%dat(1),prog_data%var(iLookPROG%scalarCanopyIce)%dat(1)+prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,'wat1' - ! process the flag for too much melt if(tooMuchMelt)then @@ -770,7 +768,6 @@ subroutine coupled_em(& ! try again cycle substeps endif - print*, prog_data%var(iLookPROG%scalarCanopyWat)%dat(1),prog_data%var(iLookPROG%scalarCanopyIce)%dat(1)+prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,'wat2' ! update first step firstSubStep=.false. @@ -786,6 +783,7 @@ subroutine coupled_em(& scalarSenHeatGround => flux_data%var(iLookFLUX%scalarSenHeatGround)%dat(1), & ! sensible heat flux from ground surface below vegetation (W m-2) scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1), & ! liquid water stored on the vegetation canopy (kg m-2) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! ice stored on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! canopy ice content (kg m-2) mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat, & ! volumetric fraction of ice in the snow+soil domain (-) mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat, & ! volumetric fraction of liquid water in the snow+soil domain (-) mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat & ! depth of each snow+soil layer (m) @@ -816,6 +814,9 @@ subroutine coupled_em(& scalarCanopyLiq = 0._rkind endif + ! update water + scalarCanopyWat = scalarCanopyLiq + scalarCanopyIce + end if ! (if computing the vegetation flux) call computSnowDepth(& @@ -912,7 +913,6 @@ subroutine coupled_em(& ! adjust length of the sub-step (make sure that we don't exceed the step) dt_sub = min(data_step - dt_solv, dt_sub) - print*, prog_data%var(iLookPROG%scalarCanopyWat)%dat(1),prog_data%var(iLookPROG%scalarCanopyIce)%dat(1)+prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,'wat3' end do substeps ! (sub-step loop) @@ -940,7 +940,6 @@ subroutine coupled_em(& ! output: error control err,cmessage) ! error control if(err/=0)then; err=30; message=trim(message)//trim(cmessage); return; end if - print*, prog_data%var(iLookPROG%scalarCanopyWat)%dat(1),prog_data%var(iLookPROG%scalarCanopyIce)%dat(1)+prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,'wat4' ! re-compute snow depth, SWE, and top layer water if(nSnow > 0)then @@ -1005,8 +1004,6 @@ subroutine coupled_em(& averageGroundEvaporation => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarGroundEvaporation) )%dat(1) ,& ! soil evaporation (kg m-2 s-1) averageCanopyTranspiration => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopyTranspiration))%dat(1) ,& ! canopy transpiration (kg m-2 s-1) ! state variables in the vegetation canopy - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! canopy liquid water (kg m-2) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! canopy ice content (kg m-2) scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! canopy ice content (kg m-2) ! state variables in the soil domain mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers) ,& ! depth of each soil layer (m) @@ -1018,7 +1015,7 @@ subroutine coupled_em(& scalarTotalSoilIce => diag_data%var(iLookDIAG%scalarTotalSoilIce)%dat(1) ,& ! total ice in the soil column (kg m-2) scalarTotalSoilLiq => diag_data%var(iLookDIAG%scalarTotalSoilLiq)%dat(1) & ! total liquid water in the soil column (kg m-2) ) ! (association of local variables with information in the data structures - print*, prog_data%var(iLookPROG%scalarCanopyWat)%dat(1),prog_data%var(iLookPROG%scalarCanopyIce)%dat(1)+prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,'wat5' + ! ----- ! * balance checks for the canopy... ! ---------------------------------- @@ -1028,8 +1025,7 @@ subroutine coupled_em(& ! if computing the vegetation flux if(computeVegFlux)then - scalarCanopyWat = scalarCanopyLiq + scalarCanopyIce !update - ! canopy water balance + ! get the canopy water balance at the end of the time step balanceCanopyWater1 = scalarCanopyWat ! balance checks for the canopy @@ -1039,7 +1035,6 @@ subroutine coupled_em(& print*, 'coupled_em canopy, tolerance', absConvTol_liquid*iden_water*10._rkind write(*,'(a,1x,f20.10)') 'data_step = ', data_step write(*,'(a,1x,e20.10)') 'balanceCanopyWater0 = ', balanceCanopyWater0 - write(*,'(a,1x,e20.10)') 'balanceCanopyWater old way = ', scalarCanopyLiq + scalarCanopyIce write(*,'(a,1x,e20.10)') 'balanceCanopyWater1 = ', balanceCanopyWater1 write(*,'(a,1x,e20.10)') 'scalarSnowfall = ', scalarSnowfall write(*,'(a,1x,e20.10)') 'scalarRainfall = ', scalarRainfall @@ -1062,14 +1057,6 @@ subroutine coupled_em(& ! * balance checks for SWE... ! --------------------------- - ! recompute snow depth (m) and SWE (kg m-2) - if(nSnow > 0)then - prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow)) - prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow)*iden_water + & - prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice) & - * prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) - end if - ! check the individual layers if(printBalance .and. nSnow>0)then write(*,'(a,1x,10(f12.8,1x))') 'liqSnowInit = ', liqSnowInit diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index fb0ee4b30..b010d0769 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -1050,7 +1050,6 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux scalarCanopyIceTrial = scalarCanopyIceTrial - scalarCanopyLiqTrial scalarCanopyLiqTrial = 0._rkind - ! encountered an inconsistency: spit the dummy else print*, 'dt = ', dt From 63eec1e3f518b6c23a6ba690b6aa20f8813c8fcd Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 30 Jan 2023 23:20:01 +0900 Subject: [PATCH 0523/1472] compressibility of layers going to 0, should not, so printing stuff --- build/source/engine/computFlux.f90 | 1 + build/source/engine/coupled_em.f90 | 1 - build/source/engine/varSubstep.f90 | 1 + build/source/engine/varSubstepSundials.f90 | 1 + 4 files changed, 3 insertions(+), 1 deletion(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 04245d10a..c5cc7edd9 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -979,6 +979,7 @@ subroutine soilCmpres(& dCompress_dPsi(iLayer) = specificStorage*(mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer))/theta_sat(iLayer) ! compute the compressibility term (-) compress(iLayer) = (mLayerMatricHeadTrial(iLayer) - mLayerMatricHead(iLayer))*dCompress_dPsi(iLayer) + print*,mLayerVolFracLiqTrial(iLayer), mLayerVolFracIceTrial(iLayer),mLayerMatricHeadTrial(iLayer), mLayerMatricHead(iLayer),'oh' endif end do else diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 4e563dc11..ecb18a7cd 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1146,7 +1146,6 @@ subroutine coupled_em(& write(*,'(a,1x,e20.10)') 'balanceSoilDrainage = ', balanceSoilDrainage write(*,'(a,1x,e20.10)') 'balanceSoilET = ', balanceSoilET write(*,'(a,1x,e20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError - write(*,'(a,1x,e20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError/iden_water write(*,'(a,1x,e20.10)') 'absConvTol_liquid = ', absConvTol_liquid ! error control if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 40268067a..608cb22b7 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -814,6 +814,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe tranSink = sum(mLayerTranspire)*dt ! m s-1 --> m baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m + print*, mLayerCompress(1:nSoil) liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) write(*,'(a,1x,f20.10)') 'dt = ', dt write(*,'(a,1x,e20.10)') 'soilBalance0 = ', soilBalance0 diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index b010d0769..e44ce9dd7 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -927,6 +927,7 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux tranSink = sum(mLayerTranspire)*dt ! m s-1 --> m baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m + print*, mLayerCompress(1:nSoil) liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) write(*,'(a,1x,f20.10)') 'dt = ', dt write(*,'(a,1x,e20.10)') 'soilBalance0 = ', soilBalance0 From 3b47327c9539aeb7df6c4629d53a0cc0a6e327cd Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 31 Jan 2023 12:40:07 +0900 Subject: [PATCH 0524/1472] print statements for compressiblity --- build/source/engine/computFlux.f90 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index c5cc7edd9..aa74be9f8 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -978,8 +978,8 @@ subroutine soilCmpres(& ! compute the derivative for the compressibility term (m-1) dCompress_dPsi(iLayer) = specificStorage*(mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer))/theta_sat(iLayer) ! compute the compressibility term (-) - compress(iLayer) = (mLayerMatricHeadTrial(iLayer) - mLayerMatricHead(iLayer))*dCompress_dPsi(iLayer) - print*,mLayerVolFracLiqTrial(iLayer), mLayerVolFracIceTrial(iLayer),mLayerMatricHeadTrial(iLayer), mLayerMatricHead(iLayer),'oh' + compress(iLayer) = (mLayerMatricHeadTrial(iLayer) - mLayerMatricHead(iLayer))*dCompress_dPsi(iLayer) !at the current time step only + if (iLayer==1) print*,mLayerVolFracLiqTrial(iLayer), mLayerVolFracIceTrial(iLayer),mLayerMatricHeadTrial(iLayer), mLayerMatricHead(iLayer),'oh' endif end do else @@ -1032,6 +1032,7 @@ subroutine soilCmpresSundials(& dCompress_dPsi(iLayer) = specificStorage*(mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer))/theta_sat(iLayer) ! compute the compressibility term (-) compress(iLayer) = mLayerMatricHeadPrime(iLayer) * dCompress_dPsi(iLayer) + if (iLayer==1) print*,mLayerVolFracLiqTrial(iLayer), mLayerVolFracIceTrial(iLayer),mLayerMatricHeadPrime(iLayer),'oh' endif end do else From 9b6907f9ced7c00246c43daa23deba8a7d7b97a5 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 31 Jan 2023 14:26:42 +0900 Subject: [PATCH 0525/1472] Soil compress was being applied additively as a sum over the time step in sundials, because it wasn't written as a per second term. Changed, including the definition of mLayerCompress. --- build/err | 13 ++++++++ build/source/dshare/get_ixname.f90 | 4 +-- build/source/dshare/popMetadat.f90 | 4 +-- build/source/dshare/var_lookup.f90 | 4 +-- build/source/engine/computFlux.f90 | 32 +++++++++---------- build/source/engine/computResid.f90 | 2 +- build/source/engine/computResidSundials.f90 | 2 +- build/source/engine/coupled_em.f90 | 14 ++++---- build/source/engine/eval8summa.f90 | 7 ++-- build/source/engine/eval8summaSundials.f90 | 7 ++-- build/source/engine/summaSolveSundialsIDA.f90 | 8 ----- build/source/engine/systemSolvSundials.f90 | 15 +-------- build/source/engine/varSubstep.f90 | 1 - build/source/engine/varSubstepSundials.f90 | 1 - 14 files changed, 53 insertions(+), 61 deletions(-) create mode 100644 build/err diff --git a/build/err b/build/err new file mode 100644 index 000000000..5b6055d4d --- /dev/null +++ b/build/err @@ -0,0 +1,13 @@ +/opt/local/bin/gfortran -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -c /Users/amedin/Research/SummaSundials/summa/build/source/engine/nrtype.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/f2008funcs.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/nr_utility.f90 /Users/amedin/Research/SummaSundials/summa/build/source/noah-mp/module_model_constants.F /Users/amedin/Research/SummaSundials/summa/build/source/noah-mp/module_sf_noahutl.F /Users/amedin/Research/SummaSundials/summa/build/source/noah-mp/module_sf_noahlsm.F /Users/amedin/Research/SummaSundials/summa/build/source/noah-mp/module_sf_noahmplsm.F +/opt/local/bin/gfortran -O3 -ffree-line-length-none -fmax-errors=0 -c /Users/amedin/Research/SummaSundials/summa/build/source/engine/nrtype.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/f2008funcs.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/nr_utility.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/expIntegral.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/spline_int.f90 /Users/amedin/Research/SummaSundials/summa/build/source/hookup/ascii_util.f90 /Users/amedin/Research/SummaSundials/summa/build/source/hookup/summaFileManager.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/multiconst.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/var_lookup.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/data_types.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/globalData.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/flxMapping.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/get_ixname.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/popMetadat.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/outpt_stat.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/time_utils.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/mDecisions.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/snow_utils.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/soil_utils.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/soil_utilsAddSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/updatState.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/updatStateSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/matrixOper.f90 -I/opt/local/include -I/opt/local/lib +echo "character(len=64), parameter :: summaVersion = 'v3.1.0'" > /Users/amedin/Research/SummaSundials/summa/build/source/driver/summaversion.inc +echo "character(len=64), parameter :: buildTime = 'Tue Jan 31 14:21:31 JST 2023'" >> /Users/amedin/Research/SummaSundials/summa/build/source/driver/summaversion.inc +echo "character(len=64), parameter :: gitBranch = 'BE_massBal-0-g3b47327c'" >> /Users/amedin/Research/SummaSundials/summa/build/source/driver/summaversion.inc +echo "character(len=64), parameter :: gitHash = '3b47327c9539aeb7df6c4629d53a0cc0a6e327cd'" >> /Users/amedin/Research/SummaSundials/summa/build/source/driver/summaversion.inc +/opt/local/bin/gfortran -O3 -ffree-line-length-none -fmax-errors=0 -c /Users/amedin/Research/SummaSundials/summa/build/source/netcdf/netcdf_util.f90 /Users/amedin/Research/SummaSundials/summa/build/source/netcdf/def_output.f90 /Users/amedin/Research/SummaSundials/summa/build/source/netcdf/modelwrite.f90 /Users/amedin/Research/SummaSundials/summa/build/source/netcdf/read_icond.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/conv_funcs.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/sunGeomtry.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/convE2Temp.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/allocspace.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/checkStruc.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/childStruc.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/ffile_info.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/read_attrb.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/read_pinit.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/pOverwrite.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/read_param.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/paramCheck.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/check_icond.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/indexState.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/getVectorz.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/t2enthalpy.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/updateVars.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/updateVarsSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/var_derive.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/read_force.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/derivforce.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/snowAlbedo.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/canopySnow.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/tempAdjust.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/snwCompact.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/layerMerge.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/layerDivide.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/volicePack.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/qTimeDelay.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/vegPhenlgy.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/diagn_evar.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/stomResist.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/groundwatr.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/vegSWavRad.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/vegNrgFlux.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/ssdNrgFlux.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/vegLiqFlux.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/snowLiqFlx.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/soilLiqFlx.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/bigAquifer.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computFlux.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/type4IDA.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/tol4IDA.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computEnthalpy.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computHeatCap.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computThermConduct.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computResid.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computJacob.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/eval8summa.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/summaSolve.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/systemSolv.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computResidSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/eval8summaSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computJacobSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computSnowDepth.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/summaSolveSundialsIDA.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/systemSolvSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/varSubstep.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/varSubstepSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/opSplittin.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/coupled_em.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/run_oneHRU.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/run_oneGRU.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_type.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_util.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_alarms.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_globalData.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_defineOutput.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_init.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_setup.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_restart.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_forcing.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_modelRun.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_writeOutput.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_driver.f90 \ + -I/opt/local/include -I/opt/local/lib -I/Users/amedin/Research/SummaSundials/sundials/instdir/include -I/Users/amedin/Research/SummaSundials/sundials/instdir/fortran +/opt/local/bin/gfortran -g *.o -L/opt/local/lib -llapack -lgfortran -lnetcdff -lnetcdf -L/Users/amedin/Research/SummaSundials/sundials/instdir/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod -o summa_sundials.exe +rm -f *.o +rm -f *.mod +rm -f soil_veg_gen_parm__genmod.f90 +summa_sundials.exe successfully installed in /Users/amedin/Research/SummaSundials/summa/bin diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 482300649..04aa49da2 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -560,8 +560,8 @@ function get_ixdiag(varName) case('scalarSoilControl' ); get_ixdiag = iLookDIAG%scalarSoilControl ! soil control on infiltration: 1=controlling; 0=not (-) case('mLayerVolFracAir' ); get_ixdiag = iLookDIAG%mLayerVolFracAir ! volumetric fraction of air in each layer (-) case('mLayerTcrit' ); get_ixdiag = iLookDIAG%mLayerTcrit ! critical soil temperature above which all water is unfrozen (K) - case('mLayerCompress' ); get_ixdiag = iLookDIAG%mLayerCompress ! change in volumetric water content due to compression of soil (-) - case('scalarSoilCompress' ); get_ixdiag = iLookDIAG%scalarSoilCompress ! change in total soil storage due to compression of the soil matrix (kg m-2) + case('mLayerCompress' ); get_ixdiag = iLookDIAG%mLayerCompress ! change in volumetric water content due to compression of soil (s-1) + case('scalarSoilCompress' ); get_ixdiag = iLookDIAG%scalarSoilCompress ! change in total soil storage due to compression of the soil matrix (kg m-2 s-1) case('mLayerMatricHeadLiq' ); get_ixdiag = iLookDIAG%mLayerMatricHeadLiq ! matric potential of liquid water (m) ! mass balance check case('scalarSoilWatBalError' ); get_ixdiag = iLookDIAG%scalarSoilWatBalError ! error in the total soil water balance (kg m-2) diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index ca684c256..286f8baad 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -423,8 +423,8 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%scalarSoilControl) = var_info('scalarSoilControl' , 'soil control on infiltration (1=controlling; 0=not)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%mLayerVolFracAir) = var_info('mLayerVolFracAir' , 'volumetric fraction of air in each layer' , '-' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%mLayerTcrit) = var_info('mLayerTcrit' , 'critical soil temperature above which all water is unfrozen' , 'K' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%mLayerCompress) = var_info('mLayerCompress' , 'change in volumetric water content due to compression of soil' , '-' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarSoilCompress) = var_info('scalarSoilCompress' , 'change in total soil storage due to compression of soil matrix' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%mLayerCompress) = var_info('mLayerCompress' , 'change in volumetric water content due to compression of soil' , 's-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarSoilCompress) = var_info('scalarSoilCompress' , 'change in total soil storage due to compression of soil matrix' , 'kg m-2 s-1 ' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%mLayerMatricHeadLiq) = var_info('mLayerMatricHeadLiq' , 'matric potential of liquid water' , 'm' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) ! mass balance check diag_meta(iLookDIAG%scalarSoilWatBalError) = var_info('scalarSoilWatBalError' , 'error in the total soil water balance' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 7a31ebd2f..75f172ae1 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -437,8 +437,8 @@ MODULE var_lookup integer(i4b) :: scalarSoilControl = integerMissing ! soil control on infiltration: 1=controlling; 0=not (-) integer(i4b) :: mLayerVolFracAir = integerMissing ! volumetric fraction of air in each layer (-) integer(i4b) :: mLayerTcrit = integerMissing ! critical soil temperature above which all water is unfrozen (K) - integer(i4b) :: mLayerCompress = integerMissing ! change in volumetric water content due to compression of soil (-) - integer(i4b) :: scalarSoilCompress = integerMissing ! change in total soil storage due to compression of the soil matrix (kg m-2) + integer(i4b) :: mLayerCompress = integerMissing ! change in volumetric water content due to compression of soil (s-1) + integer(i4b) :: scalarSoilCompress = integerMissing ! change in total soil storage due to compression of the soil matrix (kg m-2 s-1) integer(i4b) :: mLayerMatricHeadLiq = integerMissing ! matric potential of liquid water (m) ! mass balance check integer(i4b) :: scalarSoilWatBalError = integerMissing ! error in the total soil water balance (kg m-2) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index aa74be9f8..c4a3a35f1 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -939,6 +939,7 @@ end subroutine computFlux ! ********************************************************************************************************** subroutine soilCmpres(& ! input: + dt, & ! intent(in): length of the time step (seconds) ixRichards, & ! intent(in): choice of option for Richards' equation ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers mLayerMatricHead, & ! intent(in): matric head at the start of the time step (m) @@ -948,22 +949,23 @@ subroutine soilCmpres(& specificStorage, & ! intent(in): specific storage coefficient (m-1) theta_sat, & ! intent(in): soil porosity (-) ! output: - compress, & ! intent(out): compressibility of the soil matrix (-) + compress, & ! intent(out): compressibility of the soil matrix (-), per second dCompress_dPsi, & ! intent(out): derivative in compressibility w.r.t. matric head (m-1) err,message) ! intent(out): error code and error message implicit none ! input: + real(rkind),intent(in) :: dt ! length of the time step (seconds) integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation integer(i4b),intent(in) :: ixBeg,ixEnd ! start and end indices defining desired layers - real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head at the start of the time step (m) - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial value for matric head (m) - real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) - real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) - real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) - real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) + real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head at the start of the time step (m) + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial value for matric head (m) + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) + real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) + real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) ! output: - real(rkind),intent(inout) :: compress(:) ! soil compressibility (-) - real(rkind),intent(inout) :: dCompress_dPsi(:) ! derivative in soil compressibility w.r.t. matric head (m-1) + real(rkind),intent(inout) :: compress(:) ! soil compressibility (-) + real(rkind),intent(inout) :: dCompress_dPsi(:) ! derivative in soil compressibility w.r.t. matric head (m-1) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local variables @@ -975,11 +977,10 @@ subroutine soilCmpres(& if(ixRichards==mixdform)then do iLayer=1,size(mLayerMatricHead) if(iLayer>=ixBeg .and. iLayer<=ixEnd)then - ! compute the derivative for the compressibility term (m-1) + ! compute the derivative for the compressibility term (m-1), no volume expansion for total water dCompress_dPsi(iLayer) = specificStorage*(mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer))/theta_sat(iLayer) - ! compute the compressibility term (-) - compress(iLayer) = (mLayerMatricHeadTrial(iLayer) - mLayerMatricHead(iLayer))*dCompress_dPsi(iLayer) !at the current time step only - if (iLayer==1) print*,mLayerVolFracLiqTrial(iLayer), mLayerVolFracIceTrial(iLayer),mLayerMatricHeadTrial(iLayer), mLayerMatricHead(iLayer),'oh' + ! compute the compressibility term (-) per second + compress(iLayer) = (mLayerMatricHeadTrial(iLayer) - mLayerMatricHead(iLayer))*dCompress_dPsi(iLayer)/dt endif end do else @@ -1028,11 +1029,10 @@ subroutine soilCmpresSundials(& if(ixRichards==mixdform)then do iLayer=1,size(mLayerMatricHeadPrime) if(iLayer>=ixBeg .and. iLayer<=ixEnd)then - ! compute the derivative for the compressibility term (m-1) + ! compute the derivative for the compressibility term (m-1), no volume expansion for total water dCompress_dPsi(iLayer) = specificStorage*(mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer))/theta_sat(iLayer) - ! compute the compressibility term (-) + ! compute the compressibility term (-) instantaneously compress(iLayer) = mLayerMatricHeadPrime(iLayer) * dCompress_dPsi(iLayer) - if (iLayer==1) print*,mLayerVolFracLiqTrial(iLayer), mLayerVolFracIceTrial(iLayer),mLayerMatricHeadPrime(iLayer),'oh' endif end do else diff --git a/build/source/engine/computResid.f90 b/build/source/engine/computResid.f90 index 9209c78d4..83db2340d 100644 --- a/build/source/engine/computResid.f90 +++ b/build/source/engine/computResid.f90 @@ -212,7 +212,7 @@ subroutine computResid(& ! NOTE 4: same sink terms for matric head and liquid matric potential if(nSoilOnlyHyd>0)then do concurrent (iLayer=1:nSoil,ixSoilOnlyHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) - rAdd( ixSoilOnlyHyd(iLayer) ) = rAdd( ixSoilOnlyHyd(iLayer) ) + dt*(mLayerTranspire(iLayer) - mLayerBaseflow(iLayer) )/mLayerDepth(iLayer+nSnow) - mLayerCompress(iLayer) + rAdd( ixSoilOnlyHyd(iLayer) ) = rAdd( ixSoilOnlyHyd(iLayer) ) + dt*(mLayerTranspire(iLayer) - mLayerBaseflow(iLayer) )/mLayerDepth(iLayer+nSnow) - dt*mLayerCompress(iLayer) end do ! looping through non-missing energy state variables in the snow+soil domain endif diff --git a/build/source/engine/computResidSundials.f90 b/build/source/engine/computResidSundials.f90 index 05bab6bad..cae6e3165 100644 --- a/build/source/engine/computResidSundials.f90 +++ b/build/source/engine/computResidSundials.f90 @@ -192,7 +192,7 @@ subroutine computResidSundials(& ! NOTE 4: same sink terms for matric head and liquid matric potential if(nSoilOnlyHyd>0)then do concurrent (iLayer=1:nSoil,ixSoilOnlyHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) - rAdd( ixSoilOnlyHyd(iLayer) ) = rAdd( ixSoilOnlyHyd(iLayer) ) + dt*(mLayerTranspire(iLayer) - mLayerBaseflow(iLayer) )/mLayerDepth(iLayer+nSnow) - mLayerCompress(iLayer) + rAdd( ixSoilOnlyHyd(iLayer) ) = rAdd( ixSoilOnlyHyd(iLayer) ) + dt*(mLayerTranspire(iLayer) - mLayerBaseflow(iLayer) )/mLayerDepth(iLayer+nSnow) - dt*mLayerCompress(iLayer) end do ! looping through non-missing energy state variables in the snow+soil domain endif diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index ecb18a7cd..733a8d91a 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -218,7 +218,7 @@ subroutine coupled_em(& real(rkind) :: massBalance ! mass balance error (kg m-2) ! balance checks integer(i4b) :: iVar ! loop through model variables - real(rkind) :: totalSoilCompress ! total soil compression (kg m-2) + real(rkind) :: balanceSoilCompress ! total soil compression (kg m-2) real(rkind) :: scalarCanopyWatBalError! water balance error for the vegetation canopy (kg m-2) real(rkind) :: scalarSoilWatBalError ! water balance error (kg m-2) real(rkind) :: scalarInitCanopyLiq ! initial liquid water on the vegetation canopy (kg m-2) @@ -308,9 +308,8 @@ subroutine coupled_em(& call allocLocal(averageFlux_meta(:)%var_info,flux_mean,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - ! initialize compression and surface melt pond + ! initialize surface melt pond sfcMeltPond = 0._rkind ! change in storage associated with the surface melt pond (kg m-2) - totalSoilCompress = 0._rkind ! change in soil storage associated with compression of the matrix (kg m-2) ! initialize mean fluxes do iVar=1,size(averageFlux_meta) @@ -889,9 +888,6 @@ subroutine coupled_em(& ! increment change in storage associated with the surface melt pond (kg m-2) if(nSnow==0) sfcMeltPond = sfcMeltPond + prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) - ! increment soil compression (kg m-2) - totalSoilCompress = totalSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression over whole layer (kg m-2) - ! **************************************************************************************************** ! *** END MAIN SOLVER ******************************************************************************** ! **************************************************************************************************** @@ -1001,6 +997,7 @@ subroutine coupled_em(& averageSoilInflux => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarInfiltration) )%dat(1) ,& ! influx of water at the top of the soil profile (m s-1) averageSoilDrainage => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSoilDrainage) )%dat(1) ,& ! drainage from the bottom of the soil profile (m s-1) averageSoilBaseflow => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSoilBaseflow) )%dat(1) ,& ! total baseflow from throughout the soil profile (m s-1) + averageSoilCompress => flux_mean%var(childFLUX_MEAN(iLookDIAG%scalarSoilCompress) )%dat(1) ,& ! soil compression (kg m-2 s-1) averageGroundEvaporation => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarGroundEvaporation) )%dat(1) ,& ! soil evaporation (kg m-2 s-1) averageCanopyTranspiration => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopyTranspiration))%dat(1) ,& ! canopy transpiration (kg m-2 s-1) ! state variables in the vegetation canopy @@ -1116,6 +1113,7 @@ subroutine coupled_em(& balanceSoilBaseflow = averageSoilBaseflow*iden_water*data_step balanceSoilDrainage = averageSoilDrainage*iden_water*data_step balanceSoilET = (averageCanopyTranspiration + averageGroundEvaporation)*data_step + balanceSoilCompress = averageSoilCompress*data_step ! check the individual layers if(printBalance)then @@ -1132,11 +1130,11 @@ subroutine coupled_em(& endif ! check the soil water balance - scalarSoilWatBalError = balanceSoilWater1 - (balanceSoilWater0 + (balanceSoilInflux + balanceSoilET - balanceSoilBaseflow - balanceSoilDrainage - totalSoilCompress) ) + scalarSoilWatBalError = balanceSoilWater1 - (balanceSoilWater0 + (balanceSoilInflux + balanceSoilET - balanceSoilBaseflow - balanceSoilDrainage - balanceSoilCompress) ) print*, 'coupled_em soil, tolerance', absConvTol_liquid*iden_water*10._rkind write(*,*) 'solution method = ', ixSolution write(*,'(a,1x,f20.10)') 'data_step = ', data_step - write(*,'(a,1x,e20.10)') 'totalSoilCompress = ', totalSoilCompress + write(*,'(a,1x,e20.10)') 'balanceSoilCompress = ', balanceSoilCompress write(*,'(a,1x,e20.10)') 'scalarTotalSoilLiq = ', scalarTotalSoilLiq write(*,'(a,1x,e20.10)') 'scalarTotalSoilIce = ', scalarTotalSoilIce write(*,'(a,1x,e20.10)') 'balanceSoilWater0 = ', balanceSoilWater0 diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 5ebec90ea..21f888c42 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -269,8 +269,8 @@ subroutine eval8summa(& scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) ! soil compression - scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in storage associated with compression of the soil matrix (-) + scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2 s-1) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in volumetric water content due to compression of soil (s-1) ! derivatives dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential @@ -662,6 +662,7 @@ subroutine eval8summa(& ! use non-sundials version because sundials version needs mLayerMatricHeadPrime call soilCmpres(& ! input: + dt, & ! intent(in): length of the time step (seconds) ixRichards, & ! intent(in): choice of option for Richards' equation ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers mLayerMatricHead(1:nSoil), & ! intent(in): matric head at the start of the time step (m) @@ -676,7 +677,7 @@ subroutine eval8summa(& err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! compute the total change in storage associated with compression of the soil matrix (kg m-2) + ! compute the total change in storage associated with compression of the soil matrix (kg m-2 s-1) scalarSoilCompress = sum(mLayerCompress(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water ! vegetation domain: get the correct water states (total water, or liquid water, depending on the state type) diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index f0742a21c..9cd07ea9e 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -287,8 +287,8 @@ subroutine eval8summaSundials(& mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) ! soil compression - scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in storage associated with compression of the soil matrix (-) + scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2 s-1) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in volumetric water content due to compression of soil (s-1) ! derivatives dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential @@ -752,6 +752,9 @@ subroutine eval8summaSundials(& err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + ! compute the total change in storage associated with compression of the soil matrix (kg m-2 s-1) + scalarSoilCompress = sum(mLayerCompress(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water + ! compute the residual vector if (insideIDA)then dt1 = 1._qp ! always 1 for sundials since using Prime derivatives diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index e33a3ebe0..b4c653c6c 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -120,7 +120,6 @@ subroutine summaSolveSundialsIDA( & ixSaturation, & ! intent(inout) index of the lowest saturated layer (NOTE: only computed on the first iteration) idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt - mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix dt_out, & ! intent(out): time step stateVec, & ! intent(out): model state vector stateVecPrime, & ! intent(out): derivative of model state vector @@ -184,7 +183,6 @@ subroutine summaSolveSundialsIDA( & type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: flux_sum ! sum of fluxes type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(rkind),intent(inout) :: mLayerCmpress_sum(:) ! sum of soil compress ! output: state vectors integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer real(rkind),intent(inout) :: stateVec(:) ! model state vector (y) @@ -215,7 +213,6 @@ subroutine summaSolveSundialsIDA( & integer(kind = 8) :: mu, lu ! in banded matrix mode integer(i4b) :: iVar logical(lgt) :: startQuadrature - real(rkind) :: mLayerMatricHeadLiqPrev(nSoil) real(qp) :: h_init integer(c_long) :: nState ! total number of state variables real(rkind) :: rVec(nStat) @@ -400,7 +397,6 @@ subroutine summaSolveSundialsIDA( & eqns_data%scalarCanopyLiqPrev = prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) - mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) @@ -508,9 +504,6 @@ subroutine summaSolveSundialsIDA( & flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * dt_last(1) end do - ! sum of mLayerCmpress - mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & - * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) ! save required quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial @@ -518,7 +511,6 @@ subroutine summaSolveSundialsIDA( & eqns_data%scalarCanopyLiqPrev = eqns_data%scalarCanopyLiqTrial eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) - mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 8f8eabd4d..e6e07f7ce 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -209,7 +209,6 @@ subroutine systemSolvSundials(& real(rkind) :: xMin,xMax ! minimum and maximum values for water content real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a data step - real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step real(rkind) :: fOld ! function values (-); NOTE: dimensionless because scaled ! enthalpy derivatives @@ -476,9 +475,6 @@ subroutine systemSolvSundials(& call allocLocal(flux_meta(:),flux_sum,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - ! allocate space for mLayerCmpress_sum - allocate( mLayerCmpress_sum(nSoil) ) - ! check the need to merge snow layers if(nSnow>0)then ! compute the energy required to melt the top snow layer (J m-2) @@ -514,9 +510,6 @@ subroutine systemSolvSundials(& flux_sum%var(iVar)%dat(:) = 0._rkind end do - ! initialize sum of compression of the soil matrix - mLayerCmpress_sum(:) = 0._rkind - call summaSolveSundialsIDA(& dt, & ! intent(in): data time step atol, & ! intent(in): absolute telerance @@ -552,8 +545,7 @@ subroutine systemSolvSundials(& ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt - mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix - dt_out, & ! intent(out): time step + dt_out, & ! intent(out): time step stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step err,cmessage) ! intent(out): error control @@ -573,15 +565,10 @@ subroutine systemSolvSundials(& flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / dt_out end do - ! compute the total change in storage associated with compression of the soil matrix (kg m-2) - diag_data%var(iLookDIAG%mLayerCompress)%dat(:) = mLayerCmpress_sum(:) - diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sum(diag_data%var(iLookDIAG%mLayerCompress)%dat(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water - ! save the computed solution stateVecTrial = stateVecNew ! free memory - deallocate(mLayerCmpress_sum) deallocate(dBaseflow_dMatric) ! end associate statements diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 608cb22b7..40268067a 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -814,7 +814,6 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe tranSink = sum(mLayerTranspire)*dt ! m s-1 --> m baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m - print*, mLayerCompress(1:nSoil) liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) write(*,'(a,1x,f20.10)') 'dt = ', dt write(*,'(a,1x,e20.10)') 'soilBalance0 = ', soilBalance0 diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index e44ce9dd7..b010d0769 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -927,7 +927,6 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux tranSink = sum(mLayerTranspire)*dt ! m s-1 --> m baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m - print*, mLayerCompress(1:nSoil) liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) write(*,'(a,1x,f20.10)') 'dt = ', dt write(*,'(a,1x,e20.10)') 'soilBalance0 = ', soilBalance0 From f43de06388122ea42ec08ef7f88a7a487c2ec7fb Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 31 Jan 2023 18:27:21 +0900 Subject: [PATCH 0526/1472] just adding comments, when do smaller steps with BE then updating snow layers, and SWE/ surface melt rate more, and with sundials will be constant through unless fail step --- build/source/engine/summaSolveSundialsIDA.f90 | 1 + build/source/engine/varSubstepSundials.f90 | 1 + 2 files changed, 2 insertions(+) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index b4c653c6c..4c488ab24 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -661,6 +661,7 @@ end subroutine setSolverParams ! ********************************************************************************************************* ! private subroutine implctMelt: compute melt of the "snow without a layer" +! currently not used, but maybe should be used as SWE updates through data window ! ********************************************************************************************************* subroutine implctMelt(& ! input/output: integrated snowpack properties diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index b010d0769..01ff87e11 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -857,6 +857,7 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux ! ----------------------- ! NOTE: should not need to do this, since mass balance is checked in the solver + ! for sundials will not work since fluxes are instantaneous if(checkMassBalance)then ! check mass balance for the canopy From b8d1bca5db7b6054622eb27baa9b883f630d476c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 31 Jan 2023 22:41:39 +0900 Subject: [PATCH 0527/1472] trying to do only steps that are not inside opSplitting on first and last substeps, plus any merged layers, to try to match like Sundials. --- build/err | 13 ------------- build/source/engine/coupled_em.f90 | 30 ++++++++++++++++++------------ 2 files changed, 18 insertions(+), 25 deletions(-) delete mode 100644 build/err diff --git a/build/err b/build/err deleted file mode 100644 index 5b6055d4d..000000000 --- a/build/err +++ /dev/null @@ -1,13 +0,0 @@ -/opt/local/bin/gfortran -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -c /Users/amedin/Research/SummaSundials/summa/build/source/engine/nrtype.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/f2008funcs.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/nr_utility.f90 /Users/amedin/Research/SummaSundials/summa/build/source/noah-mp/module_model_constants.F /Users/amedin/Research/SummaSundials/summa/build/source/noah-mp/module_sf_noahutl.F /Users/amedin/Research/SummaSundials/summa/build/source/noah-mp/module_sf_noahlsm.F /Users/amedin/Research/SummaSundials/summa/build/source/noah-mp/module_sf_noahmplsm.F -/opt/local/bin/gfortran -O3 -ffree-line-length-none -fmax-errors=0 -c /Users/amedin/Research/SummaSundials/summa/build/source/engine/nrtype.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/f2008funcs.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/nr_utility.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/expIntegral.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/spline_int.f90 /Users/amedin/Research/SummaSundials/summa/build/source/hookup/ascii_util.f90 /Users/amedin/Research/SummaSundials/summa/build/source/hookup/summaFileManager.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/multiconst.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/var_lookup.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/data_types.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/globalData.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/flxMapping.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/get_ixname.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/popMetadat.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/outpt_stat.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/time_utils.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/mDecisions.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/snow_utils.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/soil_utils.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/soil_utilsAddSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/updatState.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/updatStateSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/matrixOper.f90 -I/opt/local/include -I/opt/local/lib -echo "character(len=64), parameter :: summaVersion = 'v3.1.0'" > /Users/amedin/Research/SummaSundials/summa/build/source/driver/summaversion.inc -echo "character(len=64), parameter :: buildTime = 'Tue Jan 31 14:21:31 JST 2023'" >> /Users/amedin/Research/SummaSundials/summa/build/source/driver/summaversion.inc -echo "character(len=64), parameter :: gitBranch = 'BE_massBal-0-g3b47327c'" >> /Users/amedin/Research/SummaSundials/summa/build/source/driver/summaversion.inc -echo "character(len=64), parameter :: gitHash = '3b47327c9539aeb7df6c4629d53a0cc0a6e327cd'" >> /Users/amedin/Research/SummaSundials/summa/build/source/driver/summaversion.inc -/opt/local/bin/gfortran -O3 -ffree-line-length-none -fmax-errors=0 -c /Users/amedin/Research/SummaSundials/summa/build/source/netcdf/netcdf_util.f90 /Users/amedin/Research/SummaSundials/summa/build/source/netcdf/def_output.f90 /Users/amedin/Research/SummaSundials/summa/build/source/netcdf/modelwrite.f90 /Users/amedin/Research/SummaSundials/summa/build/source/netcdf/read_icond.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/conv_funcs.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/sunGeomtry.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/convE2Temp.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/allocspace.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/checkStruc.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/childStruc.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/ffile_info.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/read_attrb.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/read_pinit.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/pOverwrite.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/read_param.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/paramCheck.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/check_icond.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/indexState.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/getVectorz.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/t2enthalpy.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/updateVars.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/updateVarsSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/var_derive.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/read_force.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/derivforce.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/snowAlbedo.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/canopySnow.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/tempAdjust.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/snwCompact.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/layerMerge.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/layerDivide.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/volicePack.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/qTimeDelay.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/vegPhenlgy.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/diagn_evar.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/stomResist.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/groundwatr.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/vegSWavRad.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/vegNrgFlux.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/ssdNrgFlux.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/vegLiqFlux.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/snowLiqFlx.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/soilLiqFlx.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/bigAquifer.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computFlux.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/type4IDA.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/tol4IDA.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computEnthalpy.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computHeatCap.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computThermConduct.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computResid.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computJacob.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/eval8summa.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/summaSolve.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/systemSolv.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computResidSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/eval8summaSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computJacobSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computSnowDepth.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/summaSolveSundialsIDA.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/systemSolvSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/varSubstep.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/varSubstepSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/opSplittin.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/coupled_em.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/run_oneHRU.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/run_oneGRU.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_type.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_util.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_alarms.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_globalData.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_defineOutput.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_init.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_setup.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_restart.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_forcing.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_modelRun.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_writeOutput.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_driver.f90 \ - -I/opt/local/include -I/opt/local/lib -I/Users/amedin/Research/SummaSundials/sundials/instdir/include -I/Users/amedin/Research/SummaSundials/sundials/instdir/fortran -/opt/local/bin/gfortran -g *.o -L/opt/local/lib -llapack -lgfortran -lnetcdff -lnetcdf -L/Users/amedin/Research/SummaSundials/sundials/instdir/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod -o summa_sundials.exe -rm -f *.o -rm -f *.mod -rm -f soil_veg_gen_parm__genmod.f90 -summa_sundials.exe successfully installed in /Users/amedin/Research/SummaSundials/summa/bin diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 733a8d91a..a25ea36a6 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -198,6 +198,7 @@ subroutine coupled_em(& real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) integer(i4b) :: ixSolution ! solution method used by opSplitting logical(lgt) :: firstSubStep ! flag to denote if the first time step + logical(lgt) :: lastSubStep ! flag to denote if the last time step logical(lgt) :: stepFailure ! flag to denote the need to reduce length of the coupled step and try again logical(lgt) :: tooMuchMelt ! flag to denote that there was too much melt in a given time step logical(lgt) :: tooMuchSublim ! flag to denote that there was too much sublimation in a given time step @@ -280,8 +281,9 @@ subroutine coupled_em(& modifiedLayers = .false. ! flag to denote that snow layers were modified modifiedVegState = .false. ! flag to denote that vegetation states were modified - ! define the first step + ! define the first step and last step firstSubStep = .true. + lastSubStep = .false. ! count the number of snow and soil layers ! NOTE: need to re-compute the number of snow and soil layers at the start of each sub-step because the number of layers may change @@ -687,11 +689,11 @@ subroutine coupled_em(& if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if - ! *** compute melt of the "snow without a layer"... + ! *** compute melt of the "snow without a layer" at beginning of data window ... ! ------------------------------------------------- ! NOTE: forms a surface melt pond, which drains into the upper-most soil layer through the time step ! (check for the special case of "snow without a layer") - if(nSnow==0)then + if(nSnow==0 .and. (firstSubStep .or. modifiedVegState .or. modifiedLayers)) then call implctMelt(& ! input/output: integrated snowpack properties prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(inout): snow water equivalent (kg m-2) @@ -768,11 +770,13 @@ subroutine coupled_em(& cycle substeps endif - ! update first step - firstSubStep=.false. + ! update first step and last step + firstSubStep = .false. + if (dt_solv + dt_sub >= data_step) lastSubStep = .true. - ! *** remove ice due to sublimation... + ! *** remove ice due to sublimation at end of data window... ! -------------------------------------------------------------- + if (lastSubStep .or. modifiedVegState .or. modifiedLayers)then sublime: associate(& scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1), & ! sublimation from the vegetation canopy (kg m-2 s-1) scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! sublimation from the snow surface (kg m-2 s-1) @@ -879,19 +883,21 @@ subroutine coupled_em(& + prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice/iden_water end if - ! increment fluxes - dt_wght = dt_sub/data_step ! define weight applied to each sub-step - do iVar=1,size(averageFlux_meta) - flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:)*dt_wght - end do - ! increment change in storage associated with the surface melt pond (kg m-2) if(nSnow==0) sfcMeltPond = sfcMeltPond + prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) + endif !if lastSubStep + ! **************************************************************************************************** ! *** END MAIN SOLVER ******************************************************************************** ! **************************************************************************************************** + ! increment fluxes + dt_wght = dt_sub/data_step ! define weight applied to each sub-step + do iVar=1,size(averageFlux_meta) + flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:)*dt_wght + end do + ! increment sub-step dt_solv = dt_solv + dt_sub From 89444cae87a702c5215933d0d9d0895ee0352640 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 1 Feb 2023 14:39:08 +0900 Subject: [PATCH 0528/1472] first implementation of outer loop only updates --- build/source/engine/coupled_em.f90 | 19 ++++-- build/source/engine/summaSolveSundialsIDA.f90 | 64 ------------------- build/source/engine/varSubstepSundials.f90 | 2 +- 3 files changed, 15 insertions(+), 70 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index a25ea36a6..14c6e5772 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -197,8 +197,6 @@ subroutine coupled_em(& real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) integer(i4b) :: ixSolution ! solution method used by opSplitting - logical(lgt) :: firstSubStep ! flag to denote if the first time step - logical(lgt) :: lastSubStep ! flag to denote if the last time step logical(lgt) :: stepFailure ! flag to denote the need to reduce length of the coupled step and try again logical(lgt) :: tooMuchMelt ! flag to denote that there was too much melt in a given time step logical(lgt) :: tooMuchSublim ! flag to denote that there was too much sublimation in a given time step @@ -241,6 +239,12 @@ subroutine coupled_em(& ! timing information real(rkind) :: startTime ! start time (used to compute wall clock time) real(rkind) :: endTime ! end time (used to compute wall clock time) + ! outer loop control + logical(lgt) :: firstSubStep ! flag to denote if the first time step + logical(lgt) :: lastSubStep ! flag to denote if the last time step + logical(lqt) :: outer_steps ! flag to denote if doing the outer_steps surrounding the call to sopSplitting + logical(lgt), parameter :: check_outer=.true. ! flag to check if we want to only do the outer_steps on first, last, and layer index changes + ! ---------------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="coupled_em/" @@ -688,12 +692,15 @@ subroutine coupled_em(& err,cmessage) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if + outer_steps = .true. + if (check_outer .and. .not.(firstSubStep .or. modifiedVegState .or. modifiedLayers)) outer_steps = .false. ! *** compute melt of the "snow without a layer" at beginning of data window ... ! ------------------------------------------------- ! NOTE: forms a surface melt pond, which drains into the upper-most soil layer through the time step ! (check for the special case of "snow without a layer") - if(nSnow==0 .and. (firstSubStep .or. modifiedVegState .or. modifiedLayers)) then + ! if only compute on outer loop, + if(nSnow==0 .and. outer_steps) then call implctMelt(& ! input/output: integrated snowpack properties prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(inout): snow water equivalent (kg m-2) @@ -706,6 +713,7 @@ subroutine coupled_em(& ! output: error control err,cmessage ) ! intent(out): error control if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + end if ! *** solve model equations... @@ -713,7 +721,6 @@ subroutine coupled_em(& ! save input step dtSave = dt_sub - ! get the new solution call opSplittin(& ! input: model control @@ -773,10 +780,12 @@ subroutine coupled_em(& ! update first step and last step firstSubStep = .false. if (dt_solv + dt_sub >= data_step) lastSubStep = .true. + outer_steps = .true. + if (check_outer .and. .not.(lastSubStep .or. modifiedVegState .or. modifiedLayers)) outer_steps = .false. ! *** remove ice due to sublimation at end of data window... ! -------------------------------------------------------------- - if (lastSubStep .or. modifiedVegState .or. modifiedLayers)then + if (outer_steps)then sublime: associate(& scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1), & ! sublimation from the vegetation canopy (kg m-2 s-1) scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! sublimation from the snow surface (kg m-2 s-1) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 4c488ab24..1e2a96b02 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -659,68 +659,4 @@ subroutine setSolverParams(dt,nonlin_iter,ida_mem,retval) end subroutine setSolverParams -! ********************************************************************************************************* -! private subroutine implctMelt: compute melt of the "snow without a layer" -! currently not used, but maybe should be used as SWE updates through data window -! ********************************************************************************************************* -subroutine implctMelt(& - ! input/output: integrated snowpack properties - scalarSWE, & ! intent(inout): snow water equivalent (kg m-2) - scalarSnowDepth, & ! intent(inout): snow depth (m) - scalarSfcMeltPond, & ! intent(inout): surface melt pond (kg m-2) - ! input/output: properties of the upper-most soil layer - soilTemp, & ! intent(inout): surface layer temperature (K) - soilDepth, & ! intent(inout): surface layer depth (m) - soilHeatcap, & ! intent(inout): surface layer volumetric heat capacity (J m-3 K-1) - ! output: error control - err,message ) ! intent(out): error control - implicit none - ! input/output: integrated snowpack properties - real(rkind),intent(inout) :: scalarSWE ! snow water equivalent (kg m-2) - real(rkind),intent(inout) :: scalarSnowDepth ! snow depth (m) - real(rkind),intent(inout) :: scalarSfcMeltPond ! surface melt pond (kg m-2) - ! input/output: properties of the upper-most soil layer - real(rkind),intent(inout) :: soilTemp ! surface layer temperature (K) - real(rkind),intent(inout) :: soilDepth ! surface layer depth (m) - real(rkind),intent(inout) :: soilHeatcap ! surface layer volumetric heat capacity (J m-3 K-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! local variables - real(rkind) :: nrgRequired ! energy required to melt all the snow (J m-2) - real(rkind) :: nrgAvailable ! energy available to melt the snow (J m-2) - real(rkind) :: snwDensity ! snow density (kg m-3) - ! initialize error control - err=0; message='implctMelt/' - - if(scalarSWE > 0._rkind)then - ! only melt if temperature of the top soil layer is greater than Tfreeze - if(soilTemp > Tfreeze)then - ! compute the energy required to melt all the snow (J m-2) - nrgRequired = scalarSWE*LH_fus - ! compute the energy available to melt the snow (J m-2) - nrgAvailable = soilHeatcap*(soilTemp - Tfreeze)*soilDepth - ! compute the snow density (not saved) - snwDensity = scalarSWE/scalarSnowDepth - ! compute the amount of melt, and update SWE (kg m-2) - if(nrgAvailable > nrgRequired)then - scalarSfcMeltPond = scalarSWE - scalarSWE = 0._rkind - else - scalarSfcMeltPond = nrgAvailable/LH_fus - scalarSWE = scalarSWE - scalarSfcMeltPond - end if - ! update depth - scalarSnowDepth = scalarSWE/snwDensity - ! update temperature of the top soil layer (K) - soilTemp = soilTemp - (LH_fus*scalarSfcMeltPond/soilDepth)/soilHeatcap - else ! melt is zero if the temperature of the top soil layer is less than Tfreeze - scalarSfcMeltPond = 0._rkind ! kg m-2 - end if ! (if the temperature of the top soil layer is greater than Tfreeze) - else ! melt is zero if the "snow without a layer" does not exist - scalarSfcMeltPond = 0._rkind ! kg m-2 - end if ! (if the "snow without a layer" exists) - -end subroutine implctMelt - end module summaSolveSundialsIDA_module diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index 01ff87e11..fb3547a87 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -395,7 +395,7 @@ subroutine varSubstepSundials(& endif ! identify the need to check the mass balance - checkMassBalance = .true. !do not check for Sundials + checkMassBalance = .false. !do not check for Sundials checkNrgBalance = .false. !do not check for Sundials, also only check if ixHowHeatCap == enthalpyFD ! update prognostic variables From e6f00b9c4683eb20f46dd78d062ba5bf14f0441c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 2 Feb 2023 11:32:54 +0900 Subject: [PATCH 0529/1472] remove prints and experimental outer loop stuff, just keep fixes to code that are necessary --- build/source/dshare/globalData.f90 | 2 +- build/source/engine/coupled_em.f90 | 119 +++++++++------------ build/source/engine/tol4IDA.f90 | 28 ++--- build/source/engine/varSubstep.f90 | 35 +++--- build/source/engine/varSubstepSundials.f90 | 35 +++--- 5 files changed, 100 insertions(+), 119 deletions(-) diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 index 065960479..8fe721277 100644 --- a/build/source/dshare/globalData.f90 +++ b/build/source/dshare/globalData.f90 @@ -347,5 +347,5 @@ MODULE globalData ! printing step frequency - integer(i4b),parameter,public :: print_step_freq = 1 + integer(i4b),parameter,public :: print_step_freq = 1000 END MODULE globalData diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 14c6e5772..d1c8c045b 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -197,6 +197,7 @@ subroutine coupled_em(& real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) integer(i4b) :: ixSolution ! solution method used by opSplitting + logical(lgt) :: firstSubStep ! flag to denote if the first time step logical(lgt) :: stepFailure ! flag to denote the need to reduce length of the coupled step and try again logical(lgt) :: tooMuchMelt ! flag to denote that there was too much melt in a given time step logical(lgt) :: tooMuchSublim ! flag to denote that there was too much sublimation in a given time step @@ -239,11 +240,6 @@ subroutine coupled_em(& ! timing information real(rkind) :: startTime ! start time (used to compute wall clock time) real(rkind) :: endTime ! end time (used to compute wall clock time) - ! outer loop control - logical(lgt) :: firstSubStep ! flag to denote if the first time step - logical(lgt) :: lastSubStep ! flag to denote if the last time step - logical(lqt) :: outer_steps ! flag to denote if doing the outer_steps surrounding the call to sopSplitting - logical(lgt), parameter :: check_outer=.true. ! flag to check if we want to only do the outer_steps on first, last, and layer index changes ! ---------------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -285,9 +281,8 @@ subroutine coupled_em(& modifiedLayers = .false. ! flag to denote that snow layers were modified modifiedVegState = .false. ! flag to denote that vegetation states were modified - ! define the first step and last step + ! define the first step firstSubStep = .true. - lastSubStep = .false. ! count the number of snow and soil layers ! NOTE: need to re-compute the number of snow and soil layers at the start of each sub-step because the number of layers may change @@ -692,15 +687,12 @@ subroutine coupled_em(& err,cmessage) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if - outer_steps = .true. - if (check_outer .and. .not.(firstSubStep .or. modifiedVegState .or. modifiedLayers)) outer_steps = .false. - ! *** compute melt of the "snow without a layer" at beginning of data window ... + ! *** compute melt of the "snow without a layer"... ! ------------------------------------------------- ! NOTE: forms a surface melt pond, which drains into the upper-most soil layer through the time step ! (check for the special case of "snow without a layer") - ! if only compute on outer loop, - if(nSnow==0 .and. outer_steps) then + if(nSnow==0)then call implctMelt(& ! input/output: integrated snowpack properties prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(inout): snow water equivalent (kg m-2) @@ -777,15 +769,11 @@ subroutine coupled_em(& cycle substeps endif - ! update first step and last step + ! update first step firstSubStep = .false. - if (dt_solv + dt_sub >= data_step) lastSubStep = .true. - outer_steps = .true. - if (check_outer .and. .not.(lastSubStep .or. modifiedVegState .or. modifiedLayers)) outer_steps = .false. - ! *** remove ice due to sublimation at end of data window... + ! *** remove ice due to sublimation... ! -------------------------------------------------------------- - if (outer_steps)then sublime: associate(& scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1), & ! sublimation from the vegetation canopy (kg m-2 s-1) scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! sublimation from the snow surface (kg m-2 s-1) @@ -892,21 +880,19 @@ subroutine coupled_em(& + prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice/iden_water end if + ! increment fluxes + dt_wght = dt_sub/data_step ! define weight applied to each sub-step + do iVar=1,size(averageFlux_meta) + flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:)*dt_wght + end do + ! increment change in storage associated with the surface melt pond (kg m-2) if(nSnow==0) sfcMeltPond = sfcMeltPond + prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) - endif !if lastSubStep - ! **************************************************************************************************** ! *** END MAIN SOLVER ******************************************************************************** ! **************************************************************************************************** - ! increment fluxes - dt_wght = dt_sub/data_step ! define weight applied to each sub-step - do iVar=1,size(averageFlux_meta) - flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:)*dt_wght - end do - ! increment sub-step dt_solv = dt_solv + dt_sub @@ -1044,21 +1030,20 @@ subroutine coupled_em(& ! NOTE: need to put the balance checks in the sub-step loop so that we can re-compute if necessary scalarCanopyWatBalError = balanceCanopyWater1 - (balanceCanopyWater0 + (scalarSnowfall - averageThroughfallSnow)*data_step + (scalarRainfall - averageThroughfallRain)*data_step & - averageCanopySnowUnloading*data_step - averageCanopyLiqDrainage*data_step + averageCanopySublimation*data_step + averageCanopyEvaporation*data_step) - print*, 'coupled_em canopy, tolerance', absConvTol_liquid*iden_water*10._rkind - write(*,'(a,1x,f20.10)') 'data_step = ', data_step - write(*,'(a,1x,e20.10)') 'balanceCanopyWater0 = ', balanceCanopyWater0 - write(*,'(a,1x,e20.10)') 'balanceCanopyWater1 = ', balanceCanopyWater1 - write(*,'(a,1x,e20.10)') 'scalarSnowfall = ', scalarSnowfall - write(*,'(a,1x,e20.10)') 'scalarRainfall = ', scalarRainfall - write(*,'(a,1x,e20.10)') '(scalarSnowfall - averageThroughfallSnow) = ', (scalarSnowfall - averageThroughfallSnow)!*data_step - write(*,'(a,1x,e20.10)') '(scalarRainfall - averageThroughfallRain) = ', (scalarRainfall - averageThroughfallRain)!*data_step - write(*,'(a,1x,e20.10)') 'averageCanopySnowUnloading = ', averageCanopySnowUnloading!*data_step - write(*,'(a,1x,e20.10)') 'averageCanopyLiqDrainage = ', averageCanopyLiqDrainage!*data_step - write(*,'(a,1x,e20.10)') 'averageCanopySublimation = ', averageCanopySublimation!*data_step - write(*,'(a,1x,e20.10)') 'averageCanopyEvaporation = ', averageCanopyEvaporation!*data_step - write(*,'(a,1x,e20.10)') 'scalarCanopyWatBalError = ', scalarCanopyWatBalError if(abs(scalarCanopyWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then print*, '** canopy water balance error:' + write(*,'(a,1x,f20.10)') 'data_step = ', data_step + write(*,'(a,1x,f20.10)') 'balanceCanopyWater0 = ', balanceCanopyWater0 + write(*,'(a,1x,f20.10)') 'balanceCanopyWater1 = ', balanceCanopyWater1 + write(*,'(a,1x,f20.10)') 'scalarSnowfall = ', scalarSnowfall + write(*,'(a,1x,f20.10)') 'scalarRainfall = ', scalarRainfall + write(*,'(a,1x,f20.10)') '(scalarSnowfall - averageThroughfallSnow) = ', (scalarSnowfall - averageThroughfallSnow)!*data_step + write(*,'(a,1x,f20.10)') '(scalarRainfall - averageThroughfallRain) = ', (scalarRainfall - averageThroughfallRain)!*data_step + write(*,'(a,1x,f20.10)') 'averageCanopySnowUnloading = ', averageCanopySnowUnloading!*data_step + write(*,'(a,1x,f20.10)') 'averageCanopyLiqDrainage = ', averageCanopyLiqDrainage!*data_step + write(*,'(a,1x,f20.10)') 'averageCanopySublimation = ', averageCanopySublimation!*data_step + write(*,'(a,1x,f20.10)') 'averageCanopyEvaporation = ', averageCanopyEvaporation!*data_step + write(*,'(a,1x,f20.10)') 'scalarCanopyWatBalError = ', scalarCanopyWatBalError message=trim(message)//'canopy hydrology does not balance' err=20; return end if @@ -1090,22 +1075,21 @@ subroutine coupled_em(& newSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) delSWE = newSWE - (oldSWE - sfcMeltPond) massBalance = delSWE - (effSnowfall + effRainfall + averageSnowSublimation - averageSnowDrainage*iden_water)*data_step - print*, 'coupled_em snow, tolerance', absConvTol_liquid*iden_water*10._rkind - print*, 'nSnow = ', nSnow - print*, 'nSub = ', nSub - write(*,'(a,1x,f20.10)') 'data_step = ', data_step - write(*,'(a,1x,e20.10)') 'oldSWE = ', oldSWE - write(*,'(a,1x,e20.10)') 'newSWE = ', newSWE - write(*,'(a,1x,e20.10)') 'delSWE = ', delSWE - write(*,'(a,1x,e20.10)') 'effRainfall = ', effRainfall*data_step - write(*,'(a,1x,e20.10)') 'effSnowfall = ', effSnowfall*data_step - write(*,'(a,1x,e20.10)') 'sublimation = ', averageSnowSublimation*data_step - write(*,'(a,1x,e20.10)') 'snwDrainage = ', averageSnowDrainage*iden_water*data_step - write(*,'(a,1x,e20.10)') 'sfcMeltPond = ', sfcMeltPond - write(*,'(a,1x,e20.10)') 'massBalance = ', massBalance - if(abs(massBalance) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then - message=trim(message)//'SWE does not balance' - err=20; return + if(abs(massBalance) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then + print*, 'nSnow = ', nSnow + print*, 'nSub = ', nSub + write(*,'(a,1x,f20.10)') 'data_step = ', data_step + write(*,'(a,1x,f20.10)') 'oldSWE = ', oldSWE + write(*,'(a,1x,f20.10)') 'newSWE = ', newSWE + write(*,'(a,1x,f20.10)') 'delSWE = ', delSWE + write(*,'(a,1x,f20.10)') 'effRainfall = ', effRainfall*data_step + write(*,'(a,1x,f20.10)') 'effSnowfall = ', effSnowfall*data_step + write(*,'(a,1x,f20.10)') 'sublimation = ', averageSnowSublimation*data_step + write(*,'(a,1x,f20.10)') 'snwDrainage = ', averageSnowDrainage*iden_water*data_step + write(*,'(a,1x,f20.10)') 'sfcMeltPond = ', sfcMeltPond + write(*,'(a,1x,f20.10)') 'massBalance = ', massBalance + message=trim(message)//'SWE does not balance' + err=20; return endif ! if failed mass balance check endif ! if snow layers exist @@ -1146,22 +1130,21 @@ subroutine coupled_em(& ! check the soil water balance scalarSoilWatBalError = balanceSoilWater1 - (balanceSoilWater0 + (balanceSoilInflux + balanceSoilET - balanceSoilBaseflow - balanceSoilDrainage - balanceSoilCompress) ) - print*, 'coupled_em soil, tolerance', absConvTol_liquid*iden_water*10._rkind + if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues write(*,*) 'solution method = ', ixSolution write(*,'(a,1x,f20.10)') 'data_step = ', data_step - write(*,'(a,1x,e20.10)') 'balanceSoilCompress = ', balanceSoilCompress - write(*,'(a,1x,e20.10)') 'scalarTotalSoilLiq = ', scalarTotalSoilLiq - write(*,'(a,1x,e20.10)') 'scalarTotalSoilIce = ', scalarTotalSoilIce - write(*,'(a,1x,e20.10)') 'balanceSoilWater0 = ', balanceSoilWater0 - write(*,'(a,1x,e20.10)') 'balanceSoilWater1 = ', balanceSoilWater1 - write(*,'(a,1x,e20.10)') 'balanceSoilInflux = ', balanceSoilInflux - write(*,'(a,1x,e20.10)') 'balanceSoilBaseflow = ', balanceSoilBaseflow - write(*,'(a,1x,e20.10)') 'balanceSoilDrainage = ', balanceSoilDrainage - write(*,'(a,1x,e20.10)') 'balanceSoilET = ', balanceSoilET - write(*,'(a,1x,e20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError - write(*,'(a,1x,e20.10)') 'absConvTol_liquid = ', absConvTol_liquid + write(*,'(a,1x,f20.10)') 'balanceSoilCompress = ', balanceSoilCompress + write(*,'(a,1x,f20.10)') 'scalarTotalSoilLiq = ', scalarTotalSoilLiq + write(*,'(a,1x,f20.10)') 'scalarTotalSoilIce = ', scalarTotalSoilIce + write(*,'(a,1x,f20.10)') 'balanceSoilWater0 = ', balanceSoilWater0 + write(*,'(a,1x,f20.10)') 'balanceSoilWater1 = ', balanceSoilWater1 + write(*,'(a,1x,f20.10)') 'balanceSoilInflux = ', balanceSoilInflux + write(*,'(a,1x,f20.10)') 'balanceSoilBaseflow = ', balanceSoilBaseflow + write(*,'(a,1x,f20.10)') 'balanceSoilDrainage = ', balanceSoilDrainage + write(*,'(a,1x,f20.10)') 'balanceSoilET = ', balanceSoilET + write(*,'(a,1x,f20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError + write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid ! error control - if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues message=trim(message)//'soil hydrology does not balance' err=20; return end if diff --git a/build/source/engine/tol4IDA.f90 b/build/source/engine/tol4IDA.f90 index 8c16f22d2..c33481089 100644 --- a/build/source/engine/tol4IDA.f90 +++ b/build/source/engine/tol4IDA.f90 @@ -155,20 +155,20 @@ subroutine popTol4IDA(& integer(i4b) :: iLayer ! index of layer within the snow+soil domain integer(i4b) :: ixStateSubset ! index within the state subset logical(lgt),dimension(nState) :: tolFlag ! flag to denote that the state is populated - real(rkind) :: absTolTempCas = 1e-8 ! could use absConvTol_energy 1e-6 = 1e-0 bEuler - real(rkind) :: relTolTempCas = 1e-8 ! could use relConvTol_energy 1e-6 = 1e-2 bEuler - real(rkind) :: absTolTempVeg = 1e-8 ! could use absConvTol_energy - real(rkind) :: relTolTempVeg = 1e-8 ! could use relConvTol_energy - real(rkind) :: absTolWatVeg = 1e-8 ! could use absConvTol_liquid 1e-6 = 1e-5 bEuler - real(rkind) :: relTolWatVeg = 1e-8 ! could use relConvTol_liquid 1e-6 = 1e-3 bEuler - real(rkind) :: absTolTempSoilSnow = 1e-8 ! could use absConvTol_energy - real(rkind) :: relTolTempSoilSnow = 1e-8 ! could use relConvTol_energy - real(rkind) :: absTolWatSnow = 1e-8 ! could use absConvTol_liquid - real(rkind) :: relTolWatSnow = 1e-8 ! could use relConvTol_liquid - real(rkind) :: absTolMatric = 1e-8 ! could use absConvTol_matric 1e-6 = 1e-6 bEuler - real(rkind) :: relTolMatric = 1e-8 ! could use relConvTol_matric 1e-6 = 1e-6 bEuler - real(rkind) :: absTolAquifr = 1e-8 ! could use absConvTol_aquifr 1e-6 = 1e-5 bEuler - real(rkind) :: relTolAquifr = 1e-8 ! could use relConvTol_aquifr 1e-6 = 1e-0 bEuler + real(rkind) :: absTolTempCas = 1e-6 ! could use absConvTol_energy 1e-6 = 1e-0 bEuler + real(rkind) :: relTolTempCas = 1e-6 ! could use relConvTol_energy 1e-6 = 1e-2 bEuler + real(rkind) :: absTolTempVeg = 1e-6 ! could use absConvTol_energy + real(rkind) :: relTolTempVeg = 1e-6 ! could use relConvTol_energy + real(rkind) :: absTolWatVeg = 1e-6 ! could use absConvTol_liquid 1e-6 = 1e-5 bEuler + real(rkind) :: relTolWatVeg = 1e-6 ! could use relConvTol_liquid 1e-6 = 1e-3 bEuler + real(rkind) :: absTolTempSoilSnow = 1e-6 ! could use absConvTol_energy + real(rkind) :: relTolTempSoilSnow = 1e-6 ! could use relConvTol_energy + real(rkind) :: absTolWatSnow = 1e-6 ! could use absConvTol_liquid + real(rkind) :: relTolWatSnow = 1e-6 ! could use relConvTol_liquid + real(rkind) :: absTolMatric = 1e-6 ! could use absConvTol_matric 1e-6 = 1e-6 bEuler + real(rkind) :: relTolMatric = 1e-6 ! could use relConvTol_matric 1e-6 = 1e-6 bEuler + real(rkind) :: absTolAquifr = 1e-6 ! could use absConvTol_aquifr 1e-6 = 1e-5 bEuler + real(rkind) :: relTolAquifr = 1e-6 ! could use relConvTol_aquifr 1e-6 = 1e-0 bEuler ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 40268067a..193ca3fae 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -791,16 +791,16 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! check the mass balance fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial - write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,e20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - write(*,'(a,1x,e20.10)') 'canopyBalance0 = ', canopyBalance0 - write(*,'(a,1x,e20.10)') 'canopyBalance1 = ', canopyBalance1 - write(*,'(a,1x,e20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt - write(*,'(a,1x,e20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt - write(*,'(a,1x,e20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt - write(*,'(a,1x,e20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - write(*,'(a,1x,e20.10)') 'liqError = ', liqError if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues + write(*,'(a,1x,f20.10)') 'dt = ', dt + write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 + write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 + write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt + write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt + write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt + write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt + write(*,'(a,1x,f20.10)') 'liqError = ', liqError waterBalanceError = .true. return endif ! if there is a water balance error @@ -815,16 +815,15 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) - write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,e20.10)') 'soilBalance0 = ', soilBalance0 - write(*,'(a,1x,e20.10)') 'soilBalance1 = ', soilBalance1 - write(*,'(a,1x,e20.10)') 'vertFlux = ', vertFlux - write(*,'(a,1x,e20.10)') 'tranSink = ', tranSink - write(*,'(a,1x,e20.10)') 'baseSink = ', baseSink - write(*,'(a,1x,e20.10)') 'compSink = ', compSink - write(*,'(a,1x,e20.10)') 'liqError = ', liqError - write(*,'(a,1x,e20.10)') 'absConvTol_liquid = ', absConvTol_liquid if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues + write(*,'(a,1x,f20.10)') 'dt = ', dt + write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 + write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 + write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux + write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink + write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink + write(*,'(a,1x,f20.10)') 'compSink = ', compSink + write(*,'(a,1x,f20.10)') 'liqError = ', liqError waterBalanceError = .true. return endif ! if there is a water balance error diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index fb3547a87..7718089da 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -905,16 +905,16 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux ! check the mass balance fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial - write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,e20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - write(*,'(a,1x,e20.10)') 'canopyBalance0 = ', canopyBalance0 - write(*,'(a,1x,e20.10)') 'canopyBalance1 = ', canopyBalance1 - write(*,'(a,1x,e20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt - write(*,'(a,1x,e20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt - write(*,'(a,1x,e20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt - write(*,'(a,1x,e20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - write(*,'(a,1x,e20.10)') 'liqError = ', liqError if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues + write(*,'(a,1x,f20.10)') 'dt = ', dt + write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 + write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 + write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt + write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt + write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt + write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt + write(*,'(a,1x,f20.10)') 'liqError = ', liqError waterBalanceError = .true. return endif ! if there is a water balance error @@ -929,16 +929,15 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) - write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,e20.10)') 'soilBalance0 = ', soilBalance0 - write(*,'(a,1x,e20.10)') 'soilBalance1 = ', soilBalance1 - write(*,'(a,1x,e20.10)') 'vertFlux = ', vertFlux - write(*,'(a,1x,e20.10)') 'tranSink = ', tranSink - write(*,'(a,1x,e20.10)') 'baseSink = ', baseSink - write(*,'(a,1x,e20.10)') 'compSink = ', compSink - write(*,'(a,1x,e20.10)') 'liqError = ', liqError - write(*,'(a,1x,e20.10)') 'absConvTol_liquid = ', absConvTol_liquid if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues + write(*,'(a,1x,f20.10)') 'dt = ', dt + write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 + write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 + write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux + write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink + write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink + write(*,'(a,1x,f20.10)') 'compSink = ', compSink + write(*,'(a,1x,f20.10)') 'liqError = ', liqError waterBalanceError = .true. return endif ! if there is a water balance error From 482a9b5cfa03b2b5fe54ea99308cd05417bd8d8e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 2 Feb 2023 11:44:58 +0900 Subject: [PATCH 0530/1472] all merged files are to fix error Sundials soil compression (now kept in per second form) and not updating water with ice and liquid changes --- build/source/engine/coupled_em.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index d1c8c045b..6c7169dcc 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1143,7 +1143,6 @@ subroutine coupled_em(& write(*,'(a,1x,f20.10)') 'balanceSoilDrainage = ', balanceSoilDrainage write(*,'(a,1x,f20.10)') 'balanceSoilET = ', balanceSoilET write(*,'(a,1x,f20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError - write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid ! error control message=trim(message)//'soil hydrology does not balance' err=20; return From ef4613ed8ee798f04cc123d65ede55eead113a81 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 7 Feb 2023 12:52:24 +0900 Subject: [PATCH 0531/1472] first commit changing inner substep --- build/file | 13 + build/source/engine/coupled_em.f90 | 1 + build/source/engine/eval8summa.f90 | 14 +- build/source/engine/opSplittin.f90 | 5 +- build/source/engine/summaSolve.f90 | 13 +- build/source/engine/systemSolv.f90 | 28 +- build/source/engine/systemSolvSundials.f90 | 10 +- build/source/engine/varSubstep.f90 | 1053 +++++++++++--------- build/source/engine/varSubstepSundials.f90 | 32 +- 9 files changed, 623 insertions(+), 546 deletions(-) create mode 100644 build/file diff --git a/build/file b/build/file new file mode 100644 index 000000000..a3384a553 --- /dev/null +++ b/build/file @@ -0,0 +1,13 @@ +/opt/local/bin/gfortran -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -c /Users/amedin/Research/SummaSundials/summa/build/source/engine/nrtype.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/f2008funcs.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/nr_utility.f90 /Users/amedin/Research/SummaSundials/summa/build/source/noah-mp/module_model_constants.F /Users/amedin/Research/SummaSundials/summa/build/source/noah-mp/module_sf_noahutl.F /Users/amedin/Research/SummaSundials/summa/build/source/noah-mp/module_sf_noahlsm.F /Users/amedin/Research/SummaSundials/summa/build/source/noah-mp/module_sf_noahmplsm.F +/opt/local/bin/gfortran -O3 -ffree-line-length-none -fmax-errors=0 -c /Users/amedin/Research/SummaSundials/summa/build/source/engine/nrtype.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/f2008funcs.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/nr_utility.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/expIntegral.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/spline_int.f90 /Users/amedin/Research/SummaSundials/summa/build/source/hookup/ascii_util.f90 /Users/amedin/Research/SummaSundials/summa/build/source/hookup/summaFileManager.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/multiconst.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/var_lookup.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/data_types.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/globalData.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/flxMapping.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/get_ixname.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/popMetadat.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/outpt_stat.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/time_utils.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/mDecisions.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/snow_utils.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/soil_utils.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/soil_utilsAddSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/updatState.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/updatStateSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/matrixOper.f90 -I/opt/local/include -I/opt/local/lib +echo "character(len=64), parameter :: summaVersion = 'v3.1.0'" > /Users/amedin/Research/SummaSundials/summa/build/source/driver/summaversion.inc +echo "character(len=64), parameter :: buildTime = 'Mon Feb 6 22:25:54 JST 2023'" >> /Users/amedin/Research/SummaSundials/summa/build/source/driver/summaversion.inc +echo "character(len=64), parameter :: gitBranch = 'master-0-g482a9b5c'" >> /Users/amedin/Research/SummaSundials/summa/build/source/driver/summaversion.inc +echo "character(len=64), parameter :: gitHash = '482a9b5cfa03b2b5fe54ea99308cd05417bd8d8e'" >> /Users/amedin/Research/SummaSundials/summa/build/source/driver/summaversion.inc +/opt/local/bin/gfortran -O3 -ffree-line-length-none -fmax-errors=0 -c /Users/amedin/Research/SummaSundials/summa/build/source/netcdf/netcdf_util.f90 /Users/amedin/Research/SummaSundials/summa/build/source/netcdf/def_output.f90 /Users/amedin/Research/SummaSundials/summa/build/source/netcdf/modelwrite.f90 /Users/amedin/Research/SummaSundials/summa/build/source/netcdf/read_icond.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/conv_funcs.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/sunGeomtry.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/convE2Temp.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/allocspace.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/checkStruc.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/childStruc.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/ffile_info.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/read_attrb.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/read_pinit.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/pOverwrite.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/read_param.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/paramCheck.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/check_icond.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/indexState.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/getVectorz.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/t2enthalpy.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/updateVars.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/updateVarsSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/var_derive.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/read_force.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/derivforce.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/snowAlbedo.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/canopySnow.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/tempAdjust.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/snwCompact.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/layerMerge.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/layerDivide.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/volicePack.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/qTimeDelay.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/vegPhenlgy.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/diagn_evar.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/stomResist.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/groundwatr.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/vegSWavRad.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/vegNrgFlux.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/ssdNrgFlux.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/vegLiqFlux.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/snowLiqFlx.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/soilLiqFlx.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/bigAquifer.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computFlux.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/type4IDA.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/tol4IDA.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computEnthalpy.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computHeatCap.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computThermConduct.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computResid.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computJacob.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/eval8summa.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/summaSolve.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/systemSolv.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computResidSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/eval8summaSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computJacobSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computSnowDepth.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/summaSolveSundialsIDA.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/systemSolvSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/varSubstep.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/varSubstepSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/opSplittin.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/coupled_em.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/run_oneHRU.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/run_oneGRU.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_type.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_util.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_alarms.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_globalData.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_defineOutput.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_init.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_setup.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_restart.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_forcing.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_modelRun.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_writeOutput.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_driver.f90 \ + -I/opt/local/include -I/opt/local/lib -I/Users/amedin/Research/SummaSundials/sundials/instdir/include -I/Users/amedin/Research/SummaSundials/sundials/instdir/fortran +/opt/local/bin/gfortran -g *.o -L/opt/local/lib -llapack -lgfortran -lnetcdff -lnetcdf -L/Users/amedin/Research/SummaSundials/sundials/instdir/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod -o summa_sundials.exe +rm -f *.o +rm -f *.mod +rm -f soil_veg_gen_parm__genmod.f90 +summa_sundials.exe successfully installed in /Users/amedin/Research/SummaSundials/summa/bin diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 6c7169dcc..61474f1f1 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -363,6 +363,7 @@ subroutine coupled_em(& ! short-cut to the algorithmic control parameters ! NOTE - temporary assignment of minstep to foce something reasonable + ! change maxstep with hard code here to make only the outer loop computations here in coupled_em happen more frequently for num_method = bEuler or sundials minstep = 10._rkind ! mpar_data%var(iLookPARAM%minstep)%dat(1) ! minimum time step (s) maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 21f888c42..2c802cb0f 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -96,7 +96,8 @@ module eval8summa_module ! ********************************************************************************************************** subroutine eval8summa(& ! input: model control - dt, & ! intent(in): length of the time step (seconds) + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): entire time step nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers @@ -151,7 +152,8 @@ subroutine eval8summa(& ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control - real(rkind),intent(in) :: dt ! length of the time step (seconds) + real(rkind),intent(in) :: dt_cur ! current stepsize + real(rkind),intent(in) :: dt ! entire time step integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers @@ -662,11 +664,11 @@ subroutine eval8summa(& ! use non-sundials version because sundials version needs mLayerMatricHeadPrime call soilCmpres(& ! input: - dt, & ! intent(in): length of the time step (seconds) + dt_cur, & ! intent(in): length of the time step (seconds) ixRichards, & ! intent(in): choice of option for Richards' equation ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers - mLayerMatricHead(1:nSoil), & ! intent(in): matric head at the start of the time step (m) - mLayerMatricHeadTrial(1:nSoil), & ! intent(in): trial value of matric head (m) + mLayerMatricHead(1:nSoil), & ! intent(in): matric head at the start of the time step (m) + mLayerMatricHeadTrial(1:nSoil), & ! intent(in): trial value of matric head (m) mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric ice content in each soil layer (-) specificStorage, & ! intent(in): specific storage coefficient (m-1) @@ -693,7 +695,7 @@ subroutine eval8summa(& ! compute the residual vector call computResid(& ! input: model control - dt, & ! intent(in): length of the time step (seconds) + dt_cur, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 19f1c604f..f8a159888 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -782,7 +782,7 @@ subroutine opSplittin(& failedMinimumStep, & ! intent(out) : flag for failed substeps reduceCoupledStep, & ! intent(out) : flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt - dt_out, & ! intent(out) + dt_out, & ! intent(out) err,cmessage) ! intent(out) : error code and error message dt = dt_out case(bEuler) @@ -820,8 +820,9 @@ subroutine opSplittin(& failedMinimumStep, & ! intent(out) : flag for failed substeps reduceCoupledStep, & ! intent(out) : flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt + dt_out, & ! intent(out) err,cmessage) ! intent(out) : error code and error message - ! check + dt = dt_out case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return end select diff --git a/build/source/engine/summaSolve.f90 b/build/source/engine/summaSolve.f90 index 91e5757e6..3908f1f22 100644 --- a/build/source/engine/summaSolve.f90 +++ b/build/source/engine/summaSolve.f90 @@ -86,7 +86,8 @@ module summaSolve_module ! ********************************************************************************************************* subroutine summaSolve(& ! input: model control - dt, & ! intent(in): length of the time step (seconds) + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): length of the entire time step (seconds) iter, & ! intent(in): iteration index nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -138,7 +139,8 @@ subroutine summaSolve(& implicit none ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control - real(rkind),intent(in) :: dt ! length of the time step (seconds) + real(rkind),intent(in) :: dt_cur ! current stepsize + real(rkind),intent(in) :: dt ! entire time step integer(i4b),intent(in) :: iter ! interation index integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers @@ -233,7 +235,7 @@ subroutine summaSolve(& ! or in the call to eval8summa in the previous iteration (within lineSearchRefinement or trustRegionRefinement) call computJacob(& ! input: model control - dt, & ! intent(in): length of the time step (seconds) + dt_cur, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers @@ -806,7 +808,7 @@ subroutine numJacobian(stateVec,dMat,nJac,err,message) ! compute the row of the Jacobian matrix select case(ixNumType) case(ixNumRes); nJac(:,iJac) = real(resVecJac - resVecInit, kind(rkind) )/dx ! Jacobian based on residuals - case(ixNumFlux); nJac(:,iJac) = -dt*(fluxVecJac(:) - fluxVecInit(:))/dx ! Jacobian based on fluxes + case(ixNumFlux); nJac(:,iJac) = -dt_cur*(fluxVecJac(:) - fluxVecInit(:))/dx ! Jacobian based on fluxes case default; err=20; message=trim(message)//'Jacobian option not found'; return end select @@ -855,7 +857,7 @@ subroutine testBandMat(check,err,message) ! compute the full Jacobian matrix call computJacob(& ! input: model control - dt, & ! intent(in): length of the time step (seconds) + dt_cur, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers @@ -927,6 +929,7 @@ subroutine eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err ! compute the flux and the residual vector for a given state vector call eval8summa(& ! input: model control + dt_cur, & ! intent(in): current stepsize dt, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index e939ac9b8..549e12ebb 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -117,6 +117,7 @@ module systemSolv_module ! ********************************************************************************************************** subroutine systemSolv(& ! input: model control + dt_cur, & ! intent(in): current stepsize dt, & ! intent(in): time step (s) nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to denote first sub-step @@ -160,7 +161,8 @@ subroutine systemSolv(& ! * dummy variables ! --------------------------------------------------------------------------------------- ! input: model control - real(rkind),intent(in) :: dt ! time step (seconds) + real(rkind),intent(in) :: dt_cur ! current stepsize + real(rkind),intent(in) :: dt ! entire time step integer(i4b),intent(in) :: nState ! total number of state variables logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call @@ -305,7 +307,7 @@ subroutine systemSolv(& ! --------------- ! check - if(dt < tinyStep)then + if(dt_cur < tinyStep)then message=trim(message)//'dt is tiny' err=20; return endif @@ -436,7 +438,8 @@ subroutine systemSolv(& ! NOTE 2: The Jacobian matrix together with the residual vector is used to calculate the first iteration increment call eval8summa(& ! input: model control - dt, & ! intent(in): length of the time step (seconds) + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): length of the entire time step (seconds) nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): number of layers @@ -488,7 +491,7 @@ subroutine systemSolv(& bulkDensity = mLayerVolFracIce(1)*iden_ice + mLayerVolFracLiq(1)*iden_water volEnthalpy = temp2ethpy(mLayerTemp(1),bulkDensity,snowfrz_scale) ! set flag and error codes for too much melt - if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt)then + if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_cur)then tooMuchMelt=.true. message=trim(message)//'net flux in the top snow layer can melt all the snow in the top layer' err=-20; return ! negative error code to denote a warning @@ -510,10 +513,6 @@ subroutine systemSolv(& ! iterate do iter=1,localMaxIter - ! print iteration count - !print*, '*** iter, maxiter, dt = ', iter, localMaxiter, dt - !print*, trim(message)//'before summaSolve' - ! keep track of the number of iterations niter = iter+1 ! +1 because xFluxResid was moved outside the iteration loop (for backwards compatibility) @@ -523,7 +522,8 @@ subroutine systemSolv(& ! 3) Computes new fluxes and derivatives, new residuals, and (if necessary) refines the state vector call summaSolve(& ! input: model control - dt, & ! intent(in): length of the time step (seconds) + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): length of the entire time step (seconds) iter, & ! intent(in): iteration index nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -582,12 +582,6 @@ subroutine systemSolv(& rVec = resVecNew stateVecTrial = stateVecNew - ! print progress - !write(*,'(a,10(f16.14,1x))') 'rVec = ', rVec ( min(nState,iJac1) : min(nState,iJac2) ) - !write(*,'(a,10(f16.10,1x))') 'fluxVecNew = ', fluxVecNew ( min(nState,iJac1) : min(nState,iJac2) )*dt - !write(*,'(a,10(f16.10,1x))') 'stateVecTrial = ', stateVecTrial ( min(nState,iJac1) : min(nState,iJac2) ) - !print*, 'PAUSE: check states and fluxes'; read(*,*) - ! exit iteration loop if converged if(converged) exit @@ -612,7 +606,7 @@ subroutine systemSolv(& if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) iState = ixSnowSoilNrg(iLayer) - stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt + resSinkNew(iState))/real(sMul(iState), rkind) + stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState))/real(sMul(iState), rkind) end do ! looping through non-missing energy state variables in the snow+soil domain endif @@ -621,7 +615,7 @@ subroutine systemSolv(& if(nSnowSoilHyd>0)then do concurrent (iLayer=1:nSnow,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing water state variables in the snow domain) iState = ixSnowSoilHyd(iLayer) - stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt + resSinkNew(iState)) + stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState)) end do ! looping through non-missing water state variables in the soil domain endif diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index e6e07f7ce..b46a60d3e 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -99,6 +99,7 @@ module systemSolvSundials_module ! ********************************************************************************************************** subroutine systemSolvSundials(& ! input: model control + dt_cur, & ! intent(in): current stepsize dt, & ! intent(in): time step (s) nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to denote first sub-step @@ -144,6 +145,7 @@ subroutine systemSolvSundials(& ! * dummy variables ! --------------------------------------------------------------------------------------- ! input: model control + real(rkind),intent(in) :: dt_cur ! current stepsize real(rkind),intent(in) :: dt ! time step (seconds) integer(i4b),intent(in) :: nState ! total number of state variables logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step @@ -285,7 +287,7 @@ subroutine systemSolvSundials(& ! --------------- ! check - if(dt < tinyStep)then + if(dt_cur < tinyStep)then message=trim(message)//'dt is tiny' err=20; return endif @@ -400,7 +402,7 @@ subroutine systemSolvSundials(& ! NOTE: The values calculated in eval8summaSundials are used to calculate the initial flux call eval8summaSundials(& ! input: model control - dt, & ! intent(in): current stepsize + dt_cur, & ! intent(in): current stepsize dt, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -481,7 +483,7 @@ subroutine systemSolvSundials(& bulkDensity = mLayerVolFracIce(1)*iden_ice + mLayerVolFracLiq(1)*iden_water volEnthalpy = temp2ethpy(mLayerTemp(1),bulkDensity,snowfrz_scale) ! set flag and error codes for too much melt - if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt)then + if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_cur)then tooMuchMelt=.true. message=trim(message)//'net flux in the top snow layer can melt all the snow in the top layer' err=-20; return ! negative error code to denote a warning @@ -511,7 +513,7 @@ subroutine systemSolvSundials(& end do call summaSolveSundialsIDA(& - dt, & ! intent(in): data time step + dt_cur, & ! intent(in): data time step atol, & ! intent(in): absolute telerance rtol, & ! intent(in): relative tolerance nSnow, & ! intent(in): number of snow layers diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 193ca3fae..1251069de 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -56,6 +56,7 @@ module varSubstep_module USE var_lookup,only:iLookDIAG ! named variables for structure elements USE var_lookup,only:iLookPARAM ! named variables for structure elements USE var_lookup,only:iLookINDEX ! named variables for structure elements +USE var_lookup,only:iLookDERIV ! named variables for structure elements ! look up structure for variable types USE var_lookup,only:iLookVarType @@ -116,6 +117,7 @@ subroutine varSubstep(& failedMinimumStep, & ! intent(out) : flag to denote success of substepping for a given split reduceCoupledStep, & ! intent(out) : flag to denote need to reduce the length of the coupled step tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt + dt_out, & ! intent(out) err,message) ! intent(out) : error code and error message ! --------------------------------------------------------------------------------------- ! structure allocations @@ -145,62 +147,65 @@ subroutine varSubstep(& type(var_flagVec),intent(in) :: fluxMask ! flags to denote if the flux is calculated in the given state subset type(var_ilength),intent(inout) :: fluxCount ! number of times that the flux is updated (should equal nSubsteps) ! input/output: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(zLookup),intent(in) :: lookup_data ! lookup tables - type(var_i),intent(in) :: type_data ! type of vegetation and soil - type(var_d),intent(in) :: attr_data ! spatial attributes - type(var_d),intent(in) :: forc_data ! model forcing data - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU - type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_i),intent(in) :: type_data ! type of vegetation and soil + type(var_d),intent(in) :: attr_data ! spatial attributes + type(var_d),intent(in) :: forc_data ! model forcing data + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU + type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin ! output: model control - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) real(rkind),intent(out) :: dtMultiplier ! substep multiplier (-) - integer(i4b),intent(out) :: nSubsteps ! number of substeps taken for a given split - logical(lgt),intent(out) :: failedMinimumStep ! flag to denote success of substepping for a given split - logical(lgt),intent(out) :: reduceCoupledStep ! flag to denote need to reduce the length of the coupled step - logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: nSubsteps ! number of substeps taken for a given split + logical(lgt),intent(out) :: failedMinimumStep ! flag to denote success of substepping for a given split + logical(lgt),intent(out) :: reduceCoupledStep ! flag to denote need to reduce the length of the coupled step + logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt + real(qp),intent(out) :: dt_out + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! --------------------------------------------------------------------------------------- ! * general local variables ! --------------------------------------------------------------------------------------- ! error control - character(LEN=256) :: cmessage ! error message of downwind routine + character(LEN=256) :: cmessage ! error message of downwind routine ! general local variables - integer(i4b) :: iVar ! index of variables in data structures - integer(i4b) :: iSoil ! index of soil layers - integer(i4b) :: ixLayer ! index in a given domain - integer(i4b), dimension(1) :: ixMin,ixMax ! bounds of a given flux vector + integer(i4b) :: iVar ! index of variables in data structures + integer(i4b) :: iSoil ! index of soil layers + integer(i4b) :: ixLayer ! index in a given domain + integer(i4b), dimension(1) :: ixMin,ixMax ! bounds of a given flux vector ! time stepping real(rkind) :: dtSum ! sum of time from successful steps (seconds) real(rkind) :: dt_wght ! weight given to a given flux calculation real(rkind) :: dtSubstep ! length of a substep (s) + real(rkind) :: maxstep ! maximum time step length (seconds) ! adaptive sub-stepping for the explicit solution - logical(lgt) :: failedSubstep ! flag to denote success of substepping for a given split - real(rkind),parameter :: safety=0.85_rkind ! safety factor in adaptive sub-stepping - real(rkind),parameter :: reduceMin=0.1_rkind ! mimimum factor that time step is reduced - real(rkind),parameter :: increaseMax=4.0_rkind ! maximum factor that time step is increased + logical(lgt) :: failedSubstep ! flag to denote success of substepping for a given split + real(rkind),parameter :: safety=0.85_rkind ! safety factor in adaptive sub-stepping + real(rkind),parameter :: reduceMin=0.1_rkind ! mimimum factor that time step is reduced + real(rkind),parameter :: increaseMax=4.0_rkind ! maximum factor that time step is increased ! adaptive sub-stepping for the implicit solution - integer(i4b) :: niter ! number of iterations taken - integer(i4b),parameter :: n_inc=5 ! minimum number of iterations to increase time step - integer(i4b),parameter :: n_dec=15 ! maximum number of iterations to decrease time step - real(rkind),parameter :: F_inc = 1.25_rkind ! factor used to increase time step - real(rkind),parameter :: F_dec = 0.90_rkind ! factor used to decrease time step + integer(i4b) :: niter ! number of iterations taken + integer(i4b),parameter :: n_inc=5 ! minimum number of iterations to increase time step + integer(i4b),parameter :: n_dec=15 ! maximum number of iterations to decrease time step + real(rkind),parameter :: F_inc = 1.25_rkind ! factor used to increase time step + real(rkind),parameter :: F_dec = 0.90_rkind ! factor used to decrease time step ! state and flux vectors real(rkind) :: untappedMelt(nState) ! un-tapped melt energy (J m-3 s-1) real(rkind) :: stateVecInit(nState) ! initial state vector (mixed units) real(rkind) :: stateVecTrial(nState) ! trial state vector (mixed units) - type(var_dlength) :: flux_temp ! temporary model fluxes + type(var_dlength) :: flux_temp ! temporary model fluxes ! flags - logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt) :: checkMassBalance ! flag to check the mass balance - logical(lgt) :: waterBalanceError ! flag to denote that there is a water balance error - logical(lgt) :: nrgFluxModified ! flag to denote that the energy fluxes were modified + logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt) :: checkMassBalance ! flag to check the mass balance + logical(lgt) :: checkNrgBalance + logical(lgt) :: waterBalanceError ! flag to denote that there is a water balance error + logical(lgt) :: nrgFluxModified ! flag to denote that the energy fluxes were modified ! energy fluxes real(rkind) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) @@ -246,7 +251,9 @@ subroutine varSubstep(& failedMinimumStep=.false. ! initialize the length of the substep - dtSubstep = dtInit + ! change maxstep with hard code here to make only the newton step loop in systemSolve happen more frequently for num_method = bEuler + maxstep = 1800._rkind !mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) + dtSubstep = min(dtInit,maxstep) ! allocate space for the temporary model flux structure call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) @@ -278,10 +285,6 @@ subroutine varSubstep(& ! initialize error control err=0; message='varSubstep/' - !write(*,'(a,1x,3(f13.2,1x))') '***** new subStep: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt - !print*, 'scalarCanopyIce = ', prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) - !print*, 'scalarCanopyTemp = ', prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) - ! ----- ! * populate state vectors... ! --------------------------- @@ -306,6 +309,7 @@ subroutine varSubstep(& call systemSolv(& ! input: model control dtSubstep, & ! intent(in): time step (s) + dtInit, & ! intent(in): entire time step (s) nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to denote first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call @@ -399,9 +403,11 @@ subroutine varSubstep(& ! identify the need to check the mass balance checkMassBalance = .true. ! (.not.scalarSolution) + checkNrgBalance = .false. ! only check if ixHowHeatCap == enthalpyFD + ! update prognostic variables - call updateProg(dtSubstep,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,checkMassBalance, & ! input: model control + call updateProg(dt_out,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,checkMassBalance, checkNrgBalance, & ! input: model control lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures waterBalanceError,nrgFluxModified,err,cmessage) ! output: flags and error control if(err/=0)then @@ -434,29 +440,30 @@ subroutine varSubstep(& else cycle substeps endif + dt_out = dtSubstep endif ! if errors in prognostic update ! get the total energy fluxes (modified in updateProg) if(nrgFluxModified .or. indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then - sumCanopyEvaporation = sumCanopyEvaporation + dtSubstep*flux_temp%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dtSubstep*flux_temp%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy = sumSenHeatCanopy + dtSubstep*flux_temp%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) + sumCanopyEvaporation = sumCanopyEvaporation + dt_out*flux_temp%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dt_out*flux_temp%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = sumSenHeatCanopy + dt_out*flux_temp%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) else - sumCanopyEvaporation = sumCanopyEvaporation + dtSubstep*flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dtSubstep*flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy = sumSenHeatCanopy + dtSubstep*flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) + sumCanopyEvaporation = sumCanopyEvaporation + dt_out*flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dt_out*flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = sumSenHeatCanopy + dt_out*flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) endif ! if energy fluxes were modified ! get the total soil compression if (count(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat/=integerMissing)>0) then ! scalar compression if(.not.scalarSolution .or. iStateSplit==nSoil)& - sumSoilCompress = sumSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression + sumSoilCompress = sumSoilCompress + dt_out*diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression ! vector compression do iSoil=1,nSoil if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& - sumLayerCompress(iSoil) = sumLayerCompress(iSoil) + diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) ! soil compression in layers + sumLayerCompress(iSoil) = sumLayerCompress(iSoil) + dt_out*diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) ! soil compression in layers end do endif @@ -465,7 +472,7 @@ subroutine varSubstep(& write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt ! increment fluxes - dt_wght = dtSubstep/dt ! (define weight applied to each splitting operation) + dt_wght = dt_out/dt ! (define weight applied to each splitting operation) do iVar=1,size(flux_meta) if(count(fluxMask%var(iVar)%dat)>0) then @@ -499,7 +506,6 @@ subroutine varSubstep(& ! increment the sub-step legth dtSum = dtSum + dtSubstep - !print*, 'dtSum, dtSubstep, dt, nSubsteps = ', dtSum, dtSubstep, dt, nSubsteps ! check that we have completed the sub-step if(dtSum >= dt-verySmall)then @@ -513,9 +519,9 @@ subroutine varSubstep(& end do substeps ! time steps for variable-dependent sub-stepping ! save the energy fluxes - flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt ! canopy evaporation/condensation (kg m-2 s-1) - flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt ! sensible heat flux from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt_out ! canopy evaporation/condensation (kg m-2 s-1) + flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt_out ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt_out ! sensible heat flux from the canopy to the canopy air space (W m-2) ! save the soil compression diagnostics diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress @@ -540,176 +546,193 @@ end subroutine varSubstep ! ********************************************************************************************************** ! private subroutine updateProg: update prognostic variables ! ********************************************************************************************************** - subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,checkMassBalance, & ! input: model control - lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures +subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,checkMassBalance, checkNrgBalance, & ! input: model control + lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures waterBalanceError,nrgFluxModified,err,message) ! output: flags and error control - USE getVectorz_module,only:varExtract ! extract variables from the state vector - USE updateVars_module,only:updateVars ! update prognostic variables - implicit none - ! model control - real(rkind) ,intent(in) :: dt ! time step (s) - integer(i4b) ,intent(in) :: nSnow ! number of snow layers - integer(i4b) ,intent(in) :: nSoil ! number of soil layers - integer(i4b) ,intent(in) :: nLayers ! total number of layers - logical(lgt) ,intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature - logical(lgt) ,intent(in) :: computeVegFlux ! flag to compute the vegetation flux - real(rkind) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) - real(rkind) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) - logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance - ! data structures - type(zLookup), intent(in) :: lookup_data ! lookup tables - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! indices for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! flags and error control - logical(lgt) ,intent(out) :: waterBalanceError ! flag to denote that there is a water balance error - logical(lgt) ,intent(out) :: nrgFluxModified ! flag to denote that the energy fluxes were modified - integer(i4b) ,intent(out) :: err ! error code - character(*) ,intent(out) :: message ! error message + USE getVectorz_module,only:varExtract ! extract variables from the state vector + USE updateVars_module,only:updateVars ! update prognostic variables + USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy + implicit none + ! model control + real(rkind) ,intent(in) :: dt ! time step (s) + integer(i4b) ,intent(in) :: nSnow ! number of snow layers + integer(i4b) ,intent(in) :: nSoil ! number of soil layers + integer(i4b) ,intent(in) :: nLayers ! total number of layers + logical(lgt) ,intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature + logical(lgt) ,intent(in) :: computeVegFlux ! flag to compute the vegetation flux + real(rkind) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) + real(rkind) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) + logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance + logical(lgt) ,intent(in) :: checkNrgBalance ! flag to check the energy balance + ! data structures + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! indices for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + ! flags and error control + logical(lgt) ,intent(out) :: waterBalanceError ! flag to denote that there is a water balance error + logical(lgt) ,intent(out) :: nrgFluxModified ! flag to denote that the energy fluxes were modified + integer(i4b) ,intent(out) :: err ! error code + character(*) ,intent(out) :: message ! error message ! ================================================================================================================== - ! general - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: ixSubset ! index within the state subset - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixControlIndex ! index within a given domain - real(rkind) :: volMelt ! volumetric melt (kg m-3) - real(rkind),parameter :: verySmall=epsilon(1._rkind)*2._rkind ! a very small number (deal with precision issues) - ! mass balance - real(rkind) :: canopyBalance0,canopyBalance1 ! canopy storage at start/end of time step - real(rkind) :: soilBalance0,soilBalance1 ! soil storage at start/end of time step - real(rkind) :: vertFlux ! change in storage due to vertical fluxes - real(rkind) :: tranSink,baseSink,compSink ! change in storage due to sink terms - real(rkind) :: liqError ! water balance error - real(rkind) :: fluxNet ! net water fluxes (kg m-2 s-1) - real(rkind) :: superflousWat ! superflous water used for evaporation (kg m-2 s-1) - real(rkind) :: superflousNrg ! superflous energy that cannot be used for evaporation (W m-2 [J m-2 s-1]) - character(LEN=256) :: cmessage ! error message of downwind routine - ! trial state variables - real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial vector for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector for liquid water matric potential (m) - real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) - ! diagnostic variables - real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric fraction of ice (-) - ! ------------------------------------------------------------------------------------------------------------------- - - ! ------------------------------------------------------------------------------------------------------------------- - ! point to flux variables in the data structure - associate(& - ! get indices for mass balance - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in) : [i4b] index of canopy hydrology state variable (mass) - ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the soil domain - ! get indices for the un-tapped melt - ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in) : [i4b(:)] list of indices for all energy states - ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in) : [i4b(:)] indices defining the domain of the state (iname_veg, iname_snow, iname_soil) - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in) : [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in) : [i4b(:)] [state subset] list of indices of the full state vector in the state subset - ! water fluxes - scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in) : [dp] rainfall rate (kg m-2 s-1) - scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(in) : [dp] rain reaches ground without touching the canopy (kg m-2 s-1) - scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(in) : [dp] canopy evaporation/condensation (kg m-2 s-1) - scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(in) : [dp] canopy transpiration (kg m-2 s-1) - scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(in) : [dp] drainage liquid water from vegetation canopy (kg m-2 s-1) - iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(in) : [dp(0:)] vertical liquid water flux at soil layer interfaces (-) - mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(in) : [dp(:)] transpiration loss from each soil layer (m s-1) - mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(in) : [dp(:)] baseflow from each soil layer (m s-1) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in) : [dp(:)] change in storage associated with compression of the soil matrix (-) - scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the vegetation canopy (kg m-2 s-1) - scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the snow surface (kg m-2 s-1) - ! energy fluxes - scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ,& ! intent(in) : [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ,& ! intent(in) : [dp] sensible heat flux from the canopy to the canopy air space (W m-2) - ! domain depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in) : [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in) : [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! model state variables (vegetation canopy) - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout) : [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout) : [dp] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout) : [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout) : [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout) : [dp(:)] matric head (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(inout) : [dp(:)] matric potential of liquid water (m) - ! model state variables (aquifer) - scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(inout) : [dp(:)] storage of water in the aquifer (m) - ! error tolerance - absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1) & ! intent(in) : [dp] absolute convergence tolerance for vol frac liq water (-) - ) ! associating flux variables in the data structure - ! ------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='updateProg/' - - ! initialize water balance error - waterBalanceError=.false. - - ! get storage at the start of the step - canopyBalance0 = merge(scalarCanopyLiq + scalarCanopyIce, realMissing, computeVegFlux) - soilBalance0 = sum( (mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) - - ! ----- - ! * update states... - ! ------------------ - - ! initialize to state variable from the last update - scalarCanairTempTrial = scalarCanairTemp - scalarCanopyTempTrial = scalarCanopyTemp - scalarCanopyWatTrial = scalarCanopyWat - scalarCanopyLiqTrial = scalarCanopyLiq - scalarCanopyIceTrial = scalarCanopyIce - mLayerTempTrial = mLayerTemp - mLayerVolFracWatTrial = mLayerVolFracWat - mLayerVolFracLiqTrial = mLayerVolFracLiq - mLayerVolFracIceTrial = mLayerVolFracIce - mLayerMatricHeadTrial = mLayerMatricHead - mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq - scalarAquiferStorageTrial = scalarAquiferStorage - - ! extract states from the state vector - call varExtract(& - ! input - stateVecTrial, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(inout): trial value of storage of water in the aquifer (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - !print*, 'after varExtract: scalarCanopyTempTrial =', scalarCanopyTempTrial ! trial value of canopy temperature (K) - !print*, 'after varExtract: scalarCanopyWatTrial =', scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - !print*, 'after varExtract: scalarCanopyLiqTrial =', scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) - !print*, 'after varExtract: scalarCanopyIceTrial =', scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - - ! update diagnostic variables - call updateVars(& + ! general + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: ixSubset ! index within the state subset + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixControlIndex ! index within a given domain + real(rkind) :: volMelt ! volumetric melt (kg m-3) + real(rkind),parameter :: verySmall=epsilon(1._rkind)*2._rkind ! a very small number (deal with precision issues) + ! mass balance + real(rkind) :: canopyBalance0,canopyBalance1 ! canopy storage at start/end of time step + real(rkind) :: soilBalance0,soilBalance1 ! soil storage at start/end of time step + real(rkind) :: vertFlux ! change in storage due to vertical fluxes + real(rkind) :: tranSink,baseSink,compSink ! change in storage due to sink terms + real(rkind) :: liqError ! water balance error + real(rkind) :: fluxNet ! net water fluxes (kg m-2 s-1) + real(rkind) :: superflousWat ! superflous water used for evaporation (kg m-2 s-1) + real(rkind) :: superflousNrg ! superflous energy that cannot be used for evaporation (W m-2 [J m-2 s-1]) + character(LEN=256) :: cmessage ! error message of downwind routine + ! trial state variables + real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial vector for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector for liquid water matric potential (m) + real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) + ! diagnostic variables + real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric fraction of ice (-) + real(rkind) :: scalarCanairEnthalpyTrial ! enthalpy of the canopy air space (J m-3) + real(rkind) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! enthalpy of snow + soil (J m-3) + ! enthalpy derivatives + real(rkind) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature + real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state + real(rkind),dimension(nLayers) :: dEnthalpy_dTk ! derivatives in layer enthalpy w.r.t. temperature + real(rkind),dimension(nLayers) :: dEnthalpy_dWat ! derivatives in layer enthalpy w.r.t. water state + ! ------------------------------------------------------------------------------------------------------------------- + + ! ------------------------------------------------------------------------------------------------------------------- + ! point to flux variables in the data structure + associate(& + ! get indices for mass balance + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in) : [i4b] index of canopy hydrology state variable (mass) + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the soil domain + ! get indices for the un-tapped melt + ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in) : [i4b(:)] list of indices for all energy states + ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in) : [i4b(:)] indices defining the domain of the state (iname_veg, iname_snow, iname_soil) + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in) : [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in) : [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ! water fluxes + scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in) : [dp] rainfall rate (kg m-2 s-1) + scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(in) : [dp] rain reaches ground without touching the canopy (kg m-2 s-1) + scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(in) : [dp] canopy evaporation/condensation (kg m-2 s-1) + scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(in) : [dp] canopy transpiration (kg m-2 s-1) + scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(in) : [dp] drainage liquid water from vegetation canopy (kg m-2 s-1) + iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(in) : [dp(0:)] vertical liquid water flux at soil layer interfaces (-) + iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(in) : + mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) + mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(in) : [dp(:)] transpiration loss from each soil layer (m s-1) + mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(in) : [dp(:)] baseflow from each soil layer (m s-1) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in) : [dp(:)] change in storage associated with compression of the soil matrix (-) + scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the vegetation canopy (kg m-2 s-1) + scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the snow surface (kg m-2 s-1) + ! energy fluxes + scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ,& ! intent(in) : [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ,& ! intent(in) : [dp] sensible heat flux from the canopy to the canopy air space (W m-2) + ! domain depth + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in) : [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in) : [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! model state variables (vegetation canopy) + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the vegetation canopy (K) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout) : [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout) : [dp] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout) : [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variables (snow and soil domains) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout) : [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout) : [dp(:)] matric head (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(inout) : [dp(:)] matric potential of liquid water (m) + ! enthalpy + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(inout): [dp(:)] enthalpy of the snow+soil layers (J m-3) + ! derivatives, diagnositic for enthalpy + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + ! model state variables (aquifer) + scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(inout) : [dp(:)] storage of water in the aquifer (m) + ! error tolerance + absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1) & ! intent(in) : [dp] absolute convergence tolerance for vol frac liq water (-) + ) ! associating flux variables in the data structure + ! ------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='updateProg/' + + ! initialize water balancmLayerVolFracWatTriale error + waterBalanceError=.false. + + ! get storage at the start of the step + canopyBalance0 = merge(scalarCanopyLiq + scalarCanopyIce, realMissing, computeVegFlux) + soilBalance0 = sum( (mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) + + ! ----- + ! * update states... + ! ------------------ + + ! initialize to state variable from the last update + scalarCanairTempTrial = scalarCanairTemp + scalarCanopyTempTrial = scalarCanopyTemp + scalarCanopyWatTrial = scalarCanopyWat + scalarCanopyLiqTrial = scalarCanopyLiq + scalarCanopyIceTrial = scalarCanopyIce + mLayerTempTrial = mLayerTemp + mLayerVolFracWatTrial = mLayerVolFracWat + mLayerVolFracLiqTrial = mLayerVolFracLiq + mLayerVolFracIceTrial = mLayerVolFracIce + mLayerMatricHeadTrial = mLayerMatricHead + mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq + scalarAquiferStorageTrial = scalarAquiferStorage + + ! extract states from the state vector + call varExtract(& + ! input + stateVecTrial, & ! intent(in): model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + ! output: variables for the aquifer + scalarAquiferStorageTrial,& ! intent(inout): trial value of storage of water in the aquifer (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! update diagnostic variables + call updateVars(& ! input doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze lookup_data, & ! intent(in): lookup tables for a local HRU @@ -732,278 +755,318 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) ! output: error control err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - !print*, 'after updateVars: scalarCanopyTempTrial =', scalarCanopyTempTrial ! trial value of canopy temperature (K) - !print*, 'after updateVars: scalarCanopyWatTrial =', scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - !print*, 'after updateVars: scalarCanopyLiqTrial =', scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) - !print*, 'after updateVars: scalarCanopyIceTrial =', scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - - ! ----- - ! * check mass balance... - ! ----------------------- - - ! NOTE: should not need to do this, since mass balance is checked in the solver - if(checkMassBalance)then - - ! check mass balance for the canopy - if(ixVegHyd/=integerMissing)then - - ! handle cases where fluxes empty the canopy - fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage - if(-fluxNet*dt > canopyBalance0)then - - ! --> first add water - canopyBalance1 = canopyBalance0 + (scalarRainfall - scalarThroughfallRain)*dt - - ! --> next, remove canopy evaporation -- put the unsatisfied evap into sensible heat - canopyBalance1 = canopyBalance1 + scalarCanopyEvaporation*dt - if(canopyBalance1 < 0._rkind)then - ! * get superfluous water and energy - superflousWat = -canopyBalance1/dt ! kg m-2 s-1 - superflousNrg = superflousWat*LH_vap ! W m-2 (J m-2 s-1) - ! * update fluxes and states - canopyBalance1 = 0._rkind - scalarCanopyEvaporation = scalarCanopyEvaporation + superflousWat - scalarLatHeatCanopyEvap = scalarLatHeatCanopyEvap + superflousNrg - scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg - endif + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! ---- + ! * check energy balance + !------------------------ + if(checkNrgBalance)then + ! compute enthalpy at t_{n+1} + call t2enthalpy(& + .true., & ! intent(in): logical flag to include phase change in enthalpy + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) + ! input: pre-computed derivatives + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + ! output: enthalpy + scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! --> next, remove canopy drainage - canopyBalance1 = canopyBalance1 -scalarCanopyLiqDrainage*dt - if(canopyBalance1 < 0._rkind)then - superflousWat = -canopyBalance1/dt ! kg m-2 s-1 - canopyBalance1 = 0._rkind - scalarCanopyLiqDrainage = scalarCanopyLiqDrainage + superflousWat endif - ! update the trial state - scalarCanopyWatTrial = canopyBalance1 - - ! set the modification flag - nrgFluxModified = .true. - - else - canopyBalance1 = canopyBalance0 + fluxNet*dt - nrgFluxModified = .false. - endif ! cases where fluxes empty the canopy - - ! check the mass balance - fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage - liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial - if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues - write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 - write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 - write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt - write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt - write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt - write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - write(*,'(a,1x,f20.10)') 'liqError = ', liqError - waterBalanceError = .true. - return - endif ! if there is a water balance error - endif ! if veg canopy - - ! check mass balance for soil - ! NOTE: fatal errors, though possible to recover using negative error codes - if(count(ixSoilOnlyHyd/=integerMissing)==nSoil)then - soilBalance1 = sum( (mLayerVolFracLiqTrial(nSnow+1:nLayers) + mLayerVolFracIceTrial(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) - vertFlux = -(iLayerLiqFluxSoil(nSoil) - iLayerLiqFluxSoil(0))*dt ! m s-1 --> m - tranSink = sum(mLayerTranspire)*dt ! m s-1 --> m - baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m - compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m - liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) - if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues - write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 - write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 - write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux - write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink - write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink - write(*,'(a,1x,f20.10)') 'compSink = ', compSink - write(*,'(a,1x,f20.10)') 'liqError = ', liqError - waterBalanceError = .true. - return - endif ! if there is a water balance error - endif ! if hydrology states exist in the soil domain - - endif ! if checking the mass balance - - ! ----- - ! * remove untapped melt energy... - ! -------------------------------- - - ! only work with energy state variables - if(size(ixNrgOnly)>0)then ! energy state variables exist - - ! loop through energy state variables - do iState=1,size(ixNrgOnly) - - ! get index of the control volume within the domain - ixSubset = ixNrgOnly(iState) ! index within the state subset - ixFullVector = ixMapSubset2Full(ixSubset) ! index within full state vector - ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain - - ! compute volumetric melt (kg m-3) - volMelt = dt*untappedMelt(ixSubset)/LH_fus ! (kg m-3) - - ! update ice content - select case( ixDomainType(ixFullVector) ) - case(iname_cas); cycle ! do nothing, since there is no snow stored in the canopy air space - case(iname_veg); scalarCanopyIceTrial = scalarCanopyIceTrial - volMelt*canopyDepth ! (kg m-2) - case(iname_snow); mLayerVolFracIceTrial(ixControlIndex) = mLayerVolFracIceTrial(ixControlIndex) - volMelt/iden_ice ! (-) - case(iname_soil); mLayerVolFracIceTrial(ixControlIndex+nSnow) = mLayerVolFracIceTrial(ixControlIndex+nSnow) - volMelt/iden_water ! (-) - case default; err=20; message=trim(message)//'unable to identify domain type [remove untapped melt energy]'; return - end select - - ! update liquid water content - select case( ixDomainType(ixFullVector) ) - case(iname_cas); cycle ! do nothing, since there is no snow stored in the canopy air space - case(iname_veg); scalarCanopyLiqTrial = scalarCanopyLiqTrial + volMelt*canopyDepth ! (kg m-2) - case(iname_snow); mLayerVolFracLiqTrial(ixControlIndex) = mLayerVolFracLiqTrial(ixControlIndex) + volMelt/iden_water ! (-) - case(iname_soil); mLayerVolFracLiqTrial(ixControlIndex+nSnow) = mLayerVolFracLiqTrial(ixControlIndex+nSnow) + volMelt/iden_water ! (-) - case default; err=20; message=trim(message)//'unable to identify domain type [remove untapped melt energy]'; return - end select - - end do ! looping through energy variables - - ! ======================================================================================================== - - ! *** ice - - ! --> check if we removed too much water - if(scalarCanopyIceTrial < 0._rkind .or. any(mLayerVolFracIceTrial < 0._rkind) )then - - ! ** - ! canopy within numerical precision - if(scalarCanopyIceTrial < 0._rkind)then - - if(scalarCanopyIceTrial > -verySmall)then - scalarCanopyLiqTrial = scalarCanopyLiqTrial - scalarCanopyIceTrial - scalarCanopyIceTrial = 0._rkind - - ! encountered an inconsistency: spit the dummy - else - print*, 'dt = ', dt - print*, 'untappedMelt = ', untappedMelt - print*, 'untappedMelt*dt = ', untappedMelt*dt - print*, 'scalarCanopyiceTrial = ', scalarCanopyIceTrial - message=trim(message)//'melted more than the available water' - err=20; return - endif ! (inconsistency) - - endif ! if checking the canopy - - ! ** - ! snow+soil within numerical precision - do iState=1,size(mLayerVolFracIceTrial) - - ! snow layer within numerical precision - if(mLayerVolFracIceTrial(iState) < 0._rkind)then - - if(mLayerVolFracIceTrial(iState) > -verySmall)then - mLayerVolFracLiqTrial(iState) = mLayerVolFracLiqTrial(iState) - mLayerVolFracIceTrial(iState) - mLayerVolFracIceTrial(iState) = 0._rkind - - ! encountered an inconsistency: spit the dummy - else - print*, 'dt = ', dt - print*, 'untappedMelt = ', untappedMelt - print*, 'untappedMelt*dt = ', untappedMelt*dt - print*, 'mLayerVolFracIceTrial = ', mLayerVolFracIceTrial - message=trim(message)//'melted more than the available water' - err=20; return - endif ! (inconsistency) - - endif ! if checking a snow layer - - end do ! (looping through state variables) - - endif ! (if we removed too much water) - - ! ======================================================================================================== - - ! *** liquid water - - ! --> check if we removed too much water - if(scalarCanopyLiqTrial < 0._rkind .or. any(mLayerVolFracLiqTrial < 0._rkind) )then - - ! ** - ! canopy within numerical precision - if(scalarCanopyLiqTrial < 0._rkind)then - - if(scalarCanopyLiqTrial > -verySmall)then - scalarCanopyIceTrial = scalarCanopyIceTrial - scalarCanopyLiqTrial - scalarCanopyLiqTrial = 0._rkind - - ! encountered an inconsistency: spit the dummy - else - print*, 'dt = ', dt - print*, 'untappedMelt = ', untappedMelt - print*, 'untappedMelt*dt = ', untappedMelt*dt - print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial - message=trim(message)//'frozen more than the available water' - err=20; return - endif ! (inconsistency) - - endif ! checking the canopy - - ! ** - ! snow+soil within numerical precision - do iState=1,size(mLayerVolFracLiqTrial) - - ! snow layer within numerical precision - if(mLayerVolFracLiqTrial(iState) < 0._rkind)then - - if(mLayerVolFracLiqTrial(iState) > -verySmall)then - mLayerVolFracIceTrial(iState) = mLayerVolFracIceTrial(iState) - mLayerVolFracLiqTrial(iState) - mLayerVolFracLiqTrial(iState) = 0._rkind - - ! encountered an inconsistency: spit the dummy - else - print*, 'dt = ', dt - print*, 'untappedMelt = ', untappedMelt - print*, 'untappedMelt*dt = ', untappedMelt*dt - print*, 'mLayerVolFracLiqTrial = ', mLayerVolFracLiqTrial - message=trim(message)//'frozen more than the available water' - err=20; return - endif ! (inconsistency) - - endif ! checking a snow layer - - end do ! (looping through state variables) - - endif ! (if we removed too much water) - - endif ! (if energy state variables exist) - - ! ----- - ! * update prognostic variables... - ! -------------------------------- - - ! update state variables for the vegetation canopy - scalarCanairTemp = scalarCanairTempTrial ! trial value of canopy air temperature (K) - scalarCanopyTemp = scalarCanopyTempTrial ! trial value of canopy temperature (K) - scalarCanopyWat = scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - scalarCanopyLiq = scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) - scalarCanopyIce = scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - - ! update state variables for the snow+soil domain - mLayerTemp = mLayerTempTrial ! trial vector of layer temperature (K) - mLayerVolFracWat = mLayerVolFracWatTrial ! trial vector of volumetric total water content (-) - mLayerVolFracLiq = mLayerVolFracLiqTrial ! trial vector of volumetric liquid water content (-) - mLayerVolFracIce = mLayerVolFracIceTrial ! trial vector of volumetric ice water content (-) - mLayerMatricHead = mLayerMatricHeadTrial ! trial vector of matric head (m) - mLayerMatricHeadLiq = mLayerMatricHeadLiqTrial ! trial vector of matric head (m) - - ! update state variables for the aquifer - scalarAquiferStorage = scalarAquiferStorageTrial - - ! end associations to info in the data structures - end associate - - end subroutine updateProg + ! ----- + ! * check mass balance... + ! ----------------------- + + ! NOTE: should not need to do this, since mass balance is checked in the solver + if(checkMassBalance)then + + ! check mass balance for the canopy + if(ixVegHyd/=integerMissing)then + + ! handle cases where fluxes empty the canopy + fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + if(-fluxNet*dt > canopyBalance0)then + + ! --> first add water + canopyBalance1 = canopyBalance0 + (scalarRainfall - scalarThroughfallRain)*dt + + ! --> next, remove canopy evaporation -- put the unsatisfied evap into sensible heat + canopyBalance1 = canopyBalance1 + scalarCanopyEvaporation*dt + if(canopyBalance1 < 0._rkind)then + ! * get superfluous water and energy + superflousWat = -canopyBalance1/dt ! kg m-2 s-1 + superflousNrg = superflousWat*LH_vap ! W m-2 (J m-2 s-1) + ! * update fluxes and states + canopyBalance1 = 0._rkind + scalarCanopyEvaporation = scalarCanopyEvaporation + superflousWat + scalarLatHeatCanopyEvap = scalarLatHeatCanopyEvap + superflousNrg + scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg + endif + + ! --> next, remove canopy drainage + canopyBalance1 = canopyBalance1 -scalarCanopyLiqDrainage*dt + if(canopyBalance1 < 0._rkind)then + superflousWat = -canopyBalance1/dt ! kg m-2 s-1 + canopyBalance1 = 0._rkind + scalarCanopyLiqDrainage = scalarCanopyLiqDrainage + superflousWat + endif + + ! update the trial state + scalarCanopyWatTrial = canopyBalance1 + + ! set the modification flag + nrgFluxModified = .true. + + else + canopyBalance1 = canopyBalance0 + fluxNet*dt + nrgFluxModified = .false. + endif ! cases where fluxes empty the canopy + + ! check the mass balance + fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial + if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues + write(*,'(a,1x,f20.10)') 'dt = ', dt + write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 + write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 + write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt + write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt + write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt + write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt + write(*,'(a,1x,f20.10)') 'liqError = ', liqError + waterBalanceError = .true. + return + endif ! if there is a water balance error + endif ! if veg canopy + + ! check mass balance for soil + ! NOTE: fatal errors, though possible to recover using negative error codes + if(count(ixSoilOnlyHyd/=integerMissing)==nSoil)then + soilBalance1 = sum( (mLayerVolFracLiqTrial(nSnow+1:nLayers) + mLayerVolFracIceTrial(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) + vertFlux = -(iLayerLiqFluxSoil(nSoil) - iLayerLiqFluxSoil(0))*dt ! m s-1 --> m + tranSink = sum(mLayerTranspire)*dt ! m s-1 --> m + baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m + compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m + liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) + if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues + write(*,'(a,1x,f20.10)') 'dt = ', dt + write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 + write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 + write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux + write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink + write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink + write(*,'(a,1x,f20.10)') 'compSink = ', compSink + write(*,'(a,1x,f20.10)') 'liqError = ', liqError + waterBalanceError = .true. + return + endif ! if there is a water balance error + endif ! if hydrology states exist in the soil domain + endif ! if checking the mass balance + + ! ----- + ! * remove untapped melt energy... + ! -------------------------------- + + ! only work with energy state variables + if(size(ixNrgOnly)>0)then ! energy state variables exist + + ! loop through energy state variables + do iState=1,size(ixNrgOnly) + + ! get index of the control volume within the domain + ixSubset = ixNrgOnly(iState) ! index within the state subset + ixFullVector = ixMapSubset2Full(ixSubset) ! index within full state vector + ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain + + ! compute volumetric melt (kg m-3) + volMelt = dt*untappedMelt(ixSubset)/LH_fus ! (kg m-3) + + ! update ice content + select case( ixDomainType(ixFullVector) ) + case(iname_cas); cycle ! do nothing, since there is no snow stored in the canopy air space + case(iname_veg); scalarCanopyIceTrial = scalarCanopyIceTrial - volMelt*canopyDepth ! (kg m-2) + case(iname_snow); mLayerVolFracIceTrial(ixControlIndex) = mLayerVolFracIceTrial(ixControlIndex) - volMelt/iden_ice ! (-) + case(iname_soil); mLayerVolFracIceTrial(ixControlIndex+nSnow) = mLayerVolFracIceTrial(ixControlIndex+nSnow) - volMelt/iden_water ! (-) + case default; err=20; message=trim(message)//'unable to identify domain type [remove untapped melt energy]'; return + end select + + ! update liquid water content + select case( ixDomainType(ixFullVector) ) + case(iname_cas); cycle ! do nothing, since there is no snow stored in the canopy air space + case(iname_veg); scalarCanopyLiqTrial = scalarCanopyLiqTrial + volMelt*canopyDepth ! (kg m-2) + case(iname_snow); mLayerVolFracLiqTrial(ixControlIndex) = mLayerVolFracLiqTrial(ixControlIndex) + volMelt/iden_water ! (-) + case(iname_soil); mLayerVolFracLiqTrial(ixControlIndex+nSnow) = mLayerVolFracLiqTrial(ixControlIndex+nSnow) + volMelt/iden_water ! (-) + case default; err=20; message=trim(message)//'unable to identify domain type [remove untapped melt energy]'; return + end select + + end do ! looping through energy variables + + ! ======================================================================================================== + + ! *** ice + + ! --> check if we removed too much water + if(scalarCanopyIceTrial < 0._rkind .or. any(mLayerVolFracIceTrial < 0._rkind) )then + + ! ** + ! canopy within numerical precision + if(scalarCanopyIceTrial < 0._rkind)then + + if(scalarCanopyIceTrial > -verySmall)then + scalarCanopyLiqTrial = scalarCanopyLiqTrial - scalarCanopyIceTrial + scalarCanopyIceTrial = 0._rkind + + ! encountered an inconsistency: spit the dummy + else + print*, 'dt = ', dt + print*, 'untappedMelt = ', untappedMelt + print*, 'untappedMelt*dt = ', untappedMelt*dt + print*, 'scalarCanopyiceTrial = ', scalarCanopyIceTrial + message=trim(message)//'melted more than the available water' + err=20; return + endif ! (inconsistency) + + endif ! if checking the canopy + ! ** + ! snow+soil within numerical precision + do iState=1,size(mLayerVolFracIceTrial) + + ! snow layer within numerical precision + if(mLayerVolFracIceTrial(iState) < 0._rkind)then + + if(mLayerVolFracIceTrial(iState) > -verySmall)then + mLayerVolFracLiqTrial(iState) = mLayerVolFracLiqTrial(iState) - mLayerVolFracIceTrial(iState) + mLayerVolFracIceTrial(iState) = 0._rkind + + ! encountered an inconsistency: spit the dummy + else + print*, 'dt = ', dt + print*, 'untappedMelt = ', untappedMelt + print*, 'untappedMelt*dt = ', untappedMelt*dt + print*, 'mLayerVolFracIceTrial = ', mLayerVolFracIceTrial + message=trim(message)//'melted more than the available water' + err=20; return + endif ! (inconsistency) + + endif ! if checking a snow layer + + end do ! (looping through state variables) + + endif ! (if we removed too much water) + + ! ======================================================================================================== + + ! *** liquid water + + ! --> check if we removed too much water + if(scalarCanopyLiqTrial < 0._rkind .or. any(mLayerVolFracLiqTrial < 0._rkind) )then + + ! ** + ! canopy within numerical precision + if(scalarCanopyLiqTrial < 0._rkind)then + + if(scalarCanopyLiqTrial > -verySmall)then + scalarCanopyIceTrial = scalarCanopyIceTrial - scalarCanopyLiqTrial + scalarCanopyLiqTrial = 0._rkind + + ! encountered an inconsistency: spit the dummy + else + print*, 'dt = ', dt + print*, 'untappedMelt = ', untappedMelt + print*, 'untappedMelt*dt = ', untappedMelt*dt + print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial + message=trim(message)//'frozen more than the available water' + err=20; return + endif ! (inconsistency) + endif ! checking the canopy + + ! ** + ! snow+soil within numerical precision + do iState=1,size(mLayerVolFracLiqTrial) + + ! snow layer within numerical precision + if(mLayerVolFracLiqTrial(iState) < 0._rkind)then + + if(mLayerVolFracLiqTrial(iState) > -verySmall)then + mLayerVolFracIceTrial(iState) = mLayerVolFracIceTrial(iState) - mLayerVolFracLiqTrial(iState) + mLayerVolFracLiqTrial(iState) = 0._rkind + + ! encountered an inconsistency: spit the dummy + else + print*, 'dt = ', dt + print*, 'untappedMelt = ', untappedMelt + print*, 'untappedMelt*dt = ', untappedMelt*dt + print*, 'mLayerVolFracLiqTrial = ', mLayerVolFracLiqTrial + message=trim(message)//'frozen more than the available water' + err=20; return + endif ! (inconsistency) + + endif ! checking a snow layer + + end do ! (looping through state variables) + + endif ! (if we removed too much water) + + endif ! (if energy state variables exist) + + ! ----- + ! * update enthalpy as a diagnostic variable... + ! -------------------------------- + scalarCanairEnthalpy = scalarCanairEnthalpyTrial + scalarCanopyEnthalpy = scalarCanopyEnthalpyTrial + mLayerEnthalpy = mLayerEnthalpyTrial + + ! ----- + ! * update prognostic variables... + ! -------------------------------- + ! update state variables for the vegetation canopy + scalarCanairTemp = scalarCanairTempTrial ! trial value of canopy air temperature (K) + scalarCanopyTemp = scalarCanopyTempTrial ! trial value of canopy temperature (K) + scalarCanopyWat = scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + scalarCanopyLiq = scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) + scalarCanopyIce = scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + + ! update state variables for the snow+soil domain + mLayerTemp = mLayerTempTrial ! trial vector of layer temperature (K) + mLayerVolFracWat = mLayerVolFracWatTrial ! trial vector of volumetric total water content (-) + mLayerVolFracLiq = mLayerVolFracLiqTrial ! trial vector of volumetric liquid water content (-) + mLayerVolFracIce = mLayerVolFracIceTrial ! trial vector of volumetric ice water content (-) + mLayerMatricHead = mLayerMatricHeadTrial ! trial vector of matric head (m) + mLayerMatricHeadLiq = mLayerMatricHeadLiqTrial ! trial vector of matric head (m) + + ! update state variables for the aquifer + scalarAquiferStorage = scalarAquiferStorageTrial + + ! end associations to info in the data structures + end associate + +end subroutine updateProg end module varSubstep_module diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index 7718089da..56f7d707e 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -128,7 +128,6 @@ subroutine varSubstepSundials(& ! structure allocations USE allocspace_module,only:allocLocal ! allocate local data structures ! simulation of fluxes and residuals given a trial state vector - USE systemSolv_module,only:systemSolv ! solve the system of equations for one time step USE getVectorz_module,only:popStateVec ! populate the state vector USE getVectorz_module,only:varExtract ! extract variables from the state vector USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables @@ -172,7 +171,7 @@ subroutine varSubstepSundials(& logical(lgt),intent(out) :: failedMinimumStep ! flag to denote success of substepping for a given split logical(lgt),intent(out) :: reduceCoupledStep ! flag to denote need to reduce the length of the coupled step logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt - real(qp),intent(out) :: dt_out + real(qp),intent(out) :: dt_out integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! --------------------------------------------------------------------------------------- @@ -314,6 +313,7 @@ subroutine varSubstepSundials(& call systemSolvSundials(& ! input: model control dtSubstep, & ! intent(in): time step (s) + dtInit, & ! intent(in): entire time step (s), right now same as dtSubstep but might change with operator splitting nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to denote first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call @@ -340,7 +340,7 @@ subroutine varSubstepSundials(& stateVecPrime, & ! intent(out): updated state vector reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt - dt_out, & ! intent(out): time step (s) + dt_out, & ! intent(out): time step (s) err,cmessage) ! intent(out): error code and error message if(err/=0)then @@ -355,7 +355,7 @@ subroutine varSubstepSundials(& ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry if(tooMuchMelt .or. reduceCoupledStep) return - ! identify failure + ! identify failure, this should not happen because will be fixed inside Sundials failedSubstep = (err<0) ! reduce step based on failure @@ -450,11 +450,11 @@ subroutine varSubstepSundials(& if (count(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat/=integerMissing)>0) then ! scalar compression if(.not.scalarSolution .or. iStateSplit==nSoil)& - sumSoilCompress = sumSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression + sumSoilCompress = sumSoilCompress + dt_out*diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression ! vector compression do iSoil=1,nSoil if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& - sumLayerCompress(iSoil) = sumLayerCompress(iSoil) + diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) ! soil compression in layers + sumLayerCompress(iSoil) = sumLayerCompress(iSoil) + dt_out*diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) ! soil compression in layers end do endif @@ -644,7 +644,6 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in) : [dp(:)] change in storage associated with compression of the soil matrix (-) scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the vegetation canopy (kg m-2 s-1) scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the snow surface (kg m-2 s-1) - ! energy fluxes scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ,& ! intent(in) : [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ,& ! intent(in) : [dp] sensible heat flux from the canopy to the canopy air space (W m-2) @@ -812,7 +811,6 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux ! ---- ! * check energy balance !------------------------ - ! NOTE: for now, we just compute enthalpy with phase change, should check, and should also mirror in varSubstep non-Sundials if(checkNrgBalance)then ! compute enthalpy at t_{n+1} call t2enthalpy(& @@ -833,19 +831,19 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) - dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature - dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state - dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature - dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state + dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif From 9e1fe68584ace6c9295058d3176d4467f4fa2532 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 7 Feb 2023 20:01:38 -0600 Subject: [PATCH 0532/1472] make act same as before edits --- build/source/engine/varSubstep.f90 | 17 +++++++++-------- build/source/engine/varSubstepSundials.f90 | 8 ++++---- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 1251069de..2f79077ff 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -252,8 +252,8 @@ subroutine varSubstep(& ! initialize the length of the substep ! change maxstep with hard code here to make only the newton step loop in systemSolve happen more frequently for num_method = bEuler - maxstep = 1800._rkind !mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) - dtSubstep = min(dtInit,maxstep) + maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) + dtSubstep = dtInit !min(dtInit,maxstep) ! allocate space for the temporary model flux structure call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) @@ -281,6 +281,7 @@ subroutine varSubstep(& ! loop through substeps ! NOTE: continuous do statement with exit clause substeps: do + print*, dtSubstep,maxStep,dtInit ! initialize error control err=0; message='varSubstep/' @@ -405,7 +406,7 @@ subroutine varSubstep(& checkMassBalance = .true. ! (.not.scalarSolution) checkNrgBalance = .false. ! only check if ixHowHeatCap == enthalpyFD - + dt_out = dtSubstep ! update prognostic variables call updateProg(dt_out,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,checkMassBalance, checkNrgBalance, & ! input: model control lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures @@ -440,9 +441,9 @@ subroutine varSubstep(& else cycle substeps endif - dt_out = dtSubstep endif ! if errors in prognostic update + dt_out = dtSubstep ! get the total energy fluxes (modified in updateProg) if(nrgFluxModified .or. indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then @@ -519,9 +520,9 @@ subroutine varSubstep(& end do substeps ! time steps for variable-dependent sub-stepping ! save the energy fluxes - flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt_out ! canopy evaporation/condensation (kg m-2 s-1) - flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt_out ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt_out ! sensible heat flux from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt ! canopy evaporation/condensation (kg m-2 s-1) + flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt ! sensible heat flux from the canopy to the canopy air space (W m-2) ! save the soil compression diagnostics diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress @@ -682,7 +683,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! initialize error control err=0; message='updateProg/' - ! initialize water balancmLayerVolFracWatTriale error + ! initialize water balancmLayerVolFracWatTrial error waterBalanceError=.false. ! get storage at the start of the step diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index 56f7d707e..ad0e259f2 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -505,9 +505,9 @@ subroutine varSubstepSundials(& end do substeps ! time steps for variable-dependent sub-stepping ! save the energy fluxes - flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt_out ! canopy evaporation/condensation (kg m-2 s-1) - flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt_out ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt_out ! sensible heat flux from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt ! canopy evaporation/condensation (kg m-2 s-1) + flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt ! sensible heat flux from the canopy to the canopy air space (W m-2) ! save the soil compression diagnostics diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress @@ -682,7 +682,7 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux ! initialize error control err=0; message='updateProgSundials/' - ! initialize water balancmLayerVolFracWatTriale error + ! initialize water balancmLayerVolFracWatTrial error waterBalanceError=.false. ! get storage at the start of the step From a416046fe66774ba73bf7ea4c994dfc6af0773c3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 7 Feb 2023 23:45:17 -0600 Subject: [PATCH 0533/1472] maxstep works now, and correct how dt_out works in sundials code --- build/source/engine/computSnowDepth.f90 | 2 +- build/source/engine/opSplittin.f90 | 5 --- build/source/engine/summaSolveSundialsIDA.f90 | 8 ++--- build/source/engine/systemSolvSundials.f90 | 7 ++-- build/source/engine/varSubstep.f90 | 35 ++++++++----------- build/source/engine/varSubstepSundials.f90 | 30 +++++++--------- 6 files changed, 36 insertions(+), 51 deletions(-) diff --git a/build/source/engine/computSnowDepth.f90 b/build/source/engine/computSnowDepth.f90 index 552020eb0..045044e5e 100644 --- a/build/source/engine/computSnowDepth.f90 +++ b/build/source/engine/computSnowDepth.f90 @@ -82,7 +82,7 @@ subroutine computSnowDepth(& ! * compute change in ice content of the top snow layer due to sublimation... ! --------------------------------------------------------------------------- ! initialize the flags - tooMuchSublim=.false. ! too much sublimination (merge snow layers) + tooMuchSublim=.false. ! too much sublimation (merge snow layers) ! NOTE: this is done BEFORE densification if(nSnow > 0)then ! snow layers exist diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index f8a159888..c14f53f4e 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -291,7 +291,6 @@ subroutine opSplittin(& logical(lgt) :: failedMinimumStep ! flag to denote failure of substepping for a given split integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) integer(i4b) :: nCoupling - real(qp) :: dt_out ! ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- @@ -782,9 +781,7 @@ subroutine opSplittin(& failedMinimumStep, & ! intent(out) : flag for failed substeps reduceCoupledStep, & ! intent(out) : flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt - dt_out, & ! intent(out) err,cmessage) ! intent(out) : error code and error message - dt = dt_out case(bEuler) call varSubstep(& ! input: model control @@ -820,9 +817,7 @@ subroutine opSplittin(& failedMinimumStep, & ! intent(out) : flag for failed substeps reduceCoupledStep, & ! intent(out) : flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt - dt_out, & ! intent(out) err,cmessage) ! intent(out) : error code and error message - dt = dt_out case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return end select diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 1e2a96b02..dbcd162f8 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -120,7 +120,7 @@ subroutine summaSolveSundialsIDA( & ixSaturation, & ! intent(inout) index of the lowest saturated layer (NOTE: only computed on the first iteration) idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt - dt_out, & ! intent(out): time step + dt_out, & ! intent(out): time step sum for data window at termination of sundials stateVec, & ! intent(out): model state vector stateVecPrime, & ! intent(out): derivative of model state vector err,message & ! intent(out): error control @@ -188,8 +188,8 @@ subroutine summaSolveSundialsIDA( & real(rkind),intent(inout) :: stateVec(:) ! model state vector (y) real(rkind),intent(inout) :: stateVecPrime(:) ! model state vector (y') logical(lgt),intent(out) :: idaSucceeds ! flag to indicate if IDA is successful - logical(lgt),intent(inout) :: tooMuchMelt ! flag to denote that there was too much melt - real(qp),intent(out) :: dt_out ! time step + logical(lgt),intent(inout) :: tooMuchMelt ! flag to denote that there was too much melt + real(qp),intent(out) :: dt_out ! time step sum for data window at termination of sundials ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -532,7 +532,7 @@ subroutine summaSolveSundialsIDA( & flux_data = eqns_data%flux_data deriv_data = eqns_data%deriv_data ixSaturation = eqns_data%ixSaturation - dt_out = tret(1) + dt_out = tret(1) ! should be dt, probably do not need to keep this endif ! free memory diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index b46a60d3e..a83cdc6a6 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -127,7 +127,6 @@ subroutine systemSolvSundials(& stateVecPrime, & ! intent(out): updated state vector reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out): flag to denote that there was too much melt - dt_out, & ! intent(out) err,message) ! intent(out): error code and error message ! --------------------------------------------------------------------------------------- ! structure allocations @@ -173,13 +172,13 @@ subroutine systemSolvSundials(& real(rkind),intent(out) :: stateVecPrime(:) ! trial state vector (mixed units) logical(lgt),intent(out) :: reduceCoupledStep ! flag to reduce the length of the coupled step logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that there was too much melt - real(qp),intent(out) :: dt_out ! time step integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! --------------------------------------------------------------------------------------- ! * general local variables ! --------------------------------------------------------------------------------------- + real(qp) :: dt_out ! time step sum for data window at termination of sundials character(LEN=256) :: cmessage ! error message of downwind routine integer(i4b) :: iVar ! index of variable integer(i4b) :: local_ixGroundwater ! local index for groundwater representation @@ -514,7 +513,7 @@ subroutine systemSolvSundials(& call summaSolveSundialsIDA(& dt_cur, & ! intent(in): data time step - atol, & ! intent(in): absolute telerance + atol, & ! intent(in): absolute tolerance rtol, & ! intent(in): relative tolerance nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -547,7 +546,7 @@ subroutine systemSolvSundials(& ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt - dt_out, & ! intent(out): time step + dt_out, & ! intent(out): time step sum for data window at termination of sundials stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step err,cmessage) ! intent(out): error control diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 2f79077ff..3f3eefb46 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -117,7 +117,6 @@ subroutine varSubstep(& failedMinimumStep, & ! intent(out) : flag to denote success of substepping for a given split reduceCoupledStep, & ! intent(out) : flag to denote need to reduce the length of the coupled step tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt - dt_out, & ! intent(out) err,message) ! intent(out) : error code and error message ! --------------------------------------------------------------------------------------- ! structure allocations @@ -166,7 +165,6 @@ subroutine varSubstep(& logical(lgt),intent(out) :: failedMinimumStep ! flag to denote success of substepping for a given split logical(lgt),intent(out) :: reduceCoupledStep ! flag to denote need to reduce the length of the coupled step logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt - real(qp),intent(out) :: dt_out integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! --------------------------------------------------------------------------------------- @@ -252,8 +250,8 @@ subroutine varSubstep(& ! initialize the length of the substep ! change maxstep with hard code here to make only the newton step loop in systemSolve happen more frequently for num_method = bEuler - maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) - dtSubstep = dtInit !min(dtInit,maxstep) + maxstep = 1800._rkind !mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) + dtSubstep = dtInit ! allocate space for the temporary model flux structure call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) @@ -281,7 +279,7 @@ subroutine varSubstep(& ! loop through substeps ! NOTE: continuous do statement with exit clause substeps: do - print*, dtSubstep,maxStep,dtInit + dtSubstep = min(dtSubstep,maxstep) ! initialize error control err=0; message='varSubstep/' @@ -394,9 +392,7 @@ subroutine varSubstep(& ! * update model fluxes... ! ------------------------ - ! NOTE: if we get to here then we are accepting the step - - ! NOTE: we get to here if iterations are successful + ! NOTE: if we get to here then we are accepting the step of dtSubstep if(err/=0)then message=trim(message)//'expect err=0 if updating fluxes' return @@ -406,9 +402,8 @@ subroutine varSubstep(& checkMassBalance = .true. ! (.not.scalarSolution) checkNrgBalance = .false. ! only check if ixHowHeatCap == enthalpyFD - dt_out = dtSubstep ! update prognostic variables - call updateProg(dt_out,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,checkMassBalance, checkNrgBalance, & ! input: model control + call updateProg(dtSubstep,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,checkMassBalance, checkNrgBalance, & ! input: model control lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures waterBalanceError,nrgFluxModified,err,cmessage) ! output: flags and error control if(err/=0)then @@ -443,28 +438,27 @@ subroutine varSubstep(& endif endif ! if errors in prognostic update - dt_out = dtSubstep ! get the total energy fluxes (modified in updateProg) if(nrgFluxModified .or. indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then - sumCanopyEvaporation = sumCanopyEvaporation + dt_out*flux_temp%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dt_out*flux_temp%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy = sumSenHeatCanopy + dt_out*flux_temp%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) + sumCanopyEvaporation = sumCanopyEvaporation + dtSubstep*flux_temp%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dtSubstep*flux_temp%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = sumSenHeatCanopy + dtSubstep*flux_temp%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) else - sumCanopyEvaporation = sumCanopyEvaporation + dt_out*flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dt_out*flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy = sumSenHeatCanopy + dt_out*flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) + sumCanopyEvaporation = sumCanopyEvaporation + dtSubstep*flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dtSubstep*flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = sumSenHeatCanopy + dtSubstep*flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) endif ! if energy fluxes were modified ! get the total soil compression if (count(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat/=integerMissing)>0) then ! scalar compression if(.not.scalarSolution .or. iStateSplit==nSoil)& - sumSoilCompress = sumSoilCompress + dt_out*diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression + sumSoilCompress = sumSoilCompress + dtSubstep*diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression ! vector compression do iSoil=1,nSoil if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& - sumLayerCompress(iSoil) = sumLayerCompress(iSoil) + dt_out*diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) ! soil compression in layers + sumLayerCompress(iSoil) = sumLayerCompress(iSoil) + dtSubstep*diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) ! soil compression in layers end do endif @@ -473,7 +467,7 @@ subroutine varSubstep(& write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt ! increment fluxes - dt_wght = dt_out/dt ! (define weight applied to each splitting operation) + dt_wght = dtSubstep/dt ! (define weight applied to each splitting operation) do iVar=1,size(flux_meta) if(count(fluxMask%var(iVar)%dat)>0) then @@ -518,6 +512,7 @@ subroutine varSubstep(& dtSubstep = min(dt - dtSum, max(dtSubstep*dtMultiplier, dt_min) ) end do substeps ! time steps for variable-dependent sub-stepping + ! NOTE: if we get to here then we are accepting then dtSum should dt ! save the energy fluxes flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt ! canopy evaporation/condensation (kg m-2 s-1) diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 index ad0e259f2..cbe14a156 100644 --- a/build/source/engine/varSubstepSundials.f90 +++ b/build/source/engine/varSubstepSundials.f90 @@ -122,7 +122,6 @@ subroutine varSubstepSundials(& failedMinimumStep, & ! intent(out) : flag to denote success of substepping for a given split reduceCoupledStep, & ! intent(out) : flag to denote need to reduce the length of the coupled step tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt - dt_out, & ! intent(out) err,message) ! intent(out) : error code and error message ! --------------------------------------------------------------------------------------- ! structure allocations @@ -171,7 +170,6 @@ subroutine varSubstepSundials(& logical(lgt),intent(out) :: failedMinimumStep ! flag to denote success of substepping for a given split logical(lgt),intent(out) :: reduceCoupledStep ! flag to denote need to reduce the length of the coupled step logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt - real(qp),intent(out) :: dt_out integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! --------------------------------------------------------------------------------------- @@ -340,7 +338,6 @@ subroutine varSubstepSundials(& stateVecPrime, & ! intent(out): updated state vector reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt - dt_out, & ! intent(out): time step (s) err,cmessage) ! intent(out): error code and error message if(err/=0)then @@ -386,9 +383,7 @@ subroutine varSubstepSundials(& ! * update model fluxes... ! ------------------------ - ! NOTE: if we get to here then we are accepting the step - - ! NOTE: we get to here if iterations are successful + ! NOTE: if we get to here then we are accepting the step of dtSubstep if(err/=0)then message=trim(message)//'expect err=0 if updating fluxes' return @@ -399,7 +394,7 @@ subroutine varSubstepSundials(& checkNrgBalance = .false. !do not check for Sundials, also only check if ixHowHeatCap == enthalpyFD ! update prognostic variables - call updateProgSundials(dt_out,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control + call updateProgSundials(dtSubstep,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures waterBalanceError,nrgFluxModified,err,cmessage) ! output: flags and error control if(err/=0)then @@ -437,24 +432,24 @@ subroutine varSubstepSundials(& ! get the total energy fluxes (modified in updateProgSundials) if(nrgFluxModified .or. indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then - sumCanopyEvaporation = sumCanopyEvaporation + dt_out*flux_temp%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dt_out*flux_temp%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy = sumSenHeatCanopy + dt_out*flux_temp%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) + sumCanopyEvaporation = sumCanopyEvaporation + dtSubstep*flux_temp%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dtSubstep*flux_temp%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = sumSenHeatCanopy + dtSubstep*flux_temp%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) else - sumCanopyEvaporation = sumCanopyEvaporation + dt_out*flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dt_out*flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy = sumSenHeatCanopy + dt_out*flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) + sumCanopyEvaporation = sumCanopyEvaporation + dtSubstep*flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dtSubstep*flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = sumSenHeatCanopy + dtSubstep*flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) endif ! if energy fluxes were modified ! get the total soil compression if (count(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat/=integerMissing)>0) then ! scalar compression if(.not.scalarSolution .or. iStateSplit==nSoil)& - sumSoilCompress = sumSoilCompress + dt_out*diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression + sumSoilCompress = sumSoilCompress + dtSubstep*diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression ! vector compression do iSoil=1,nSoil if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& - sumLayerCompress(iSoil) = sumLayerCompress(iSoil) + dt_out*diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) ! soil compression in layers + sumLayerCompress(iSoil) = sumLayerCompress(iSoil) + dtSubstep*diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) ! soil compression in layers end do endif @@ -463,7 +458,7 @@ subroutine varSubstepSundials(& write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt ! increment fluxes - dt_wght = 1._qp !dt_out/dt ! (define weight applied to each splitting operation) + dt_wght = 1._qp !dtSubstep/dt ! (define weight applied to each splitting operation) do iVar=1,size(flux_meta) if(count(fluxMask%var(iVar)%dat)>0) then @@ -503,11 +498,12 @@ subroutine varSubstepSundials(& dtSubstep = min(dt - dtSum, max(dtSubstep*dtMultiplier, dt_min) ) end do substeps ! time steps for variable-dependent sub-stepping + ! NOTE: if we get to here then we are accepting then dtSum should dt ! save the energy fluxes flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt ! canopy evaporation/condensation (kg m-2 s-1) flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt ! sensible heat flux from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt ! sensible heat flux from the canopy to the canopy air space (W m-2) ! save the soil compression diagnostics diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress From 9d66b742b9b9243d2f4f4d7224ed91683ecf6163 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 8 Feb 2023 15:04:56 -0600 Subject: [PATCH 0534/1472] combine varSubstep and varSubstepSundials and get rid of varSubstepSundials. maxstep in varSubstep is in code but defaulted to do full step --- build/my_makefile_cop | 3 +- build/my_makefile_gra | 3 +- build/my_makefile_mac | 3 +- build/my_makefile_ric | 3 +- build/source/engine/opSplittin.f90 | 46 +- build/source/engine/varSubstep.f90 | 1051 ++++++++++-------- build/source/engine/varSubstepSundials.f90 | 1120 -------------------- 7 files changed, 609 insertions(+), 1620 deletions(-) delete mode 100644 build/source/engine/varSubstepSundials.f90 diff --git a/build/my_makefile_cop b/build/my_makefile_cop index 39bfb4c47..52eb7e2a1 100644 --- a/build/my_makefile_cop +++ b/build/my_makefile_cop @@ -233,8 +233,7 @@ SUMMA_SOLVER= \ computSnowDepth.f90 \ summaSolveSundialsIDA.f90 \ systemSolvSundials.f90 \ - varSubstep.f90 \ - varSubstepSundials.f90 \ + varSubstep.f90 \ opSplittin.f90 \ coupled_em.f90 \ run_oneHRU.f90 \ diff --git a/build/my_makefile_gra b/build/my_makefile_gra index 03389b411..feb5ebecc 100644 --- a/build/my_makefile_gra +++ b/build/my_makefile_gra @@ -233,8 +233,7 @@ SUMMA_SOLVER= \ computSnowDepth.f90 \ summaSolveSundialsIDA.f90 \ systemSolvSundials.f90 \ - varSubstep.f90 \ - varSubstepSundials.f90 \ + varSubstep.f90 \ opSplittin.f90 \ coupled_em.f90 \ run_oneHRU.f90 \ diff --git a/build/my_makefile_mac b/build/my_makefile_mac index bd8965786..ee33bee1f 100644 --- a/build/my_makefile_mac +++ b/build/my_makefile_mac @@ -233,8 +233,7 @@ SUMMA_SOLVER= \ computSnowDepth.f90 \ summaSolveSundialsIDA.f90 \ systemSolvSundials.f90 \ - varSubstep.f90 \ - varSubstepSundials.f90 \ + varSubstep.f90 \ opSplittin.f90 \ coupled_em.f90 \ run_oneHRU.f90 \ diff --git a/build/my_makefile_ric b/build/my_makefile_ric index 5cd15287b..e9265a43d 100644 --- a/build/my_makefile_ric +++ b/build/my_makefile_ric @@ -233,8 +233,7 @@ SUMMA_SOLVER= \ computSnowDepth.f90 \ summaSolveSundialsIDA.f90 \ systemSolvSundials.f90 \ - varSubstep.f90 \ - varSubstepSundials.f90 \ + varSubstep.f90 \ opSplittin.f90 \ coupled_em.f90 \ run_oneHRU.f90 \ diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index c14f53f4e..d2e942a8b 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -198,7 +198,6 @@ subroutine opSplittin(& ! population/extraction of state vectors USE indexState_module,only:indexSplit ! get state indices USE varSubstep_module,only:varSubstep ! complete substeps for a given split - USE varSubstepSundials_module,only:varSubstepSundials ! complete substeps for a given split ! identify name of variable type (for error message) USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages implicit none @@ -744,10 +743,8 @@ subroutine opSplittin(& ! keep track of the number of scalar solutions if(ixSolution==scalar) numberScalarSolutions = numberScalarSolutions + 1 - ! solve variable subset for one full time step - select case(ixNumericalMethod) - case(sundials) - call varSubstepSundials(& + ! solve variable subset for one full time steps + call varSubstep(& ! input: model control dt, & ! intent(in) : time step (s) dtInit, & ! intent(in) : initial time step (seconds) @@ -782,45 +779,6 @@ subroutine opSplittin(& reduceCoupledStep, & ! intent(out) : flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt err,cmessage) ! intent(out) : error code and error message - case(bEuler) - call varSubstep(& - ! input: model control - dt, & ! intent(in) : time step (s) - dtInit, & ! intent(in) : initial time step (seconds) - dt_min, & ! intent(in) : minimum time step (seconds) - nSubset, & ! intent(in) : total number of variables in the state subset - doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature - firstSubStep, & ! intent(in) : flag to denote first sub-step - firstFluxCall, & ! intent(inout) : flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in) : flag to denote if computing energy flux over vegetation - (ixSolution==scalar), & ! intent(in) : flag to denote computing the scalar solution - iStateSplit, & ! intent(in) : index of the layer in the splitting operation - fluxMask, & ! intent(in) : mask for the fluxes used in this given state subset - fluxCount, & ! intent(inout) : number of times fluxes are updated (should equal nsubstep) - ! input/output: data structures - model_decisions, & ! intent(in) : model decisions - lookup_data, & ! intent(in) : lookup tables - type_data, & ! intent(in) : type of vegetation and soil - attr_data, & ! intent(in) : spatial attributes - forc_data, & ! intent(in) : model forcing data - mpar_data, & ! intent(in) : model parameters - indx_data, & ! intent(inout) : index data - prog_data, & ! intent(inout) : model prognostic variables for a local HRU - diag_data, & ! intent(inout) : model diagnostic variables for a local HRU - flux_data, & ! intent(inout) : model fluxes for a local HRU - deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables - bvar_data, & ! intent(in) : model variables for the local basin - ! output: control - ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) - dtMultiplier, & ! intent(out) : substep multiplier (-) - nSubsteps, & ! intent(out) : number of substeps taken for a given split - failedMinimumStep, & ! intent(out) : flag for failed substeps - reduceCoupledStep, & ! intent(out) : flag to reduce the length of the coupled step - tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt - err,cmessage) ! intent(out) : error code and error message - case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return - end select - if(err/=0)then message=trim(message)//trim(cmessage) if(err>0) return diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 3f3eefb46..d42cd6182 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -57,6 +57,7 @@ module varSubstep_module USE var_lookup,only:iLookPARAM ! named variables for structure elements USE var_lookup,only:iLookINDEX ! named variables for structure elements USE var_lookup,only:iLookDERIV ! named variables for structure elements +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure ! look up structure for variable types USE var_lookup,only:iLookVarType @@ -69,6 +70,11 @@ module varSubstep_module iden_ice, & ! intrinsic density of ice (kg m-3) iden_water ! intrinsic density of liquid water (kg m-3) +! look-up values for the numerical method +USE mDecisions_module,only: & + sundials, & ! SUNDIALS/IDA solution + bEuler ! home-grown backward Euler solution with long time step + ! safety: set private unless specified otherwise implicit none private @@ -80,472 +86,521 @@ module varSubstep_module contains - ! ********************************************************************************************************** - ! public subroutine varSubstep: run the model for a collection of substeps for a given state subset - ! ********************************************************************************************************** - subroutine varSubstep(& - ! input: model control - dt, & ! intent(in) : time step (s) - dtInit, & ! intent(in) : initial time step (seconds) - dt_min, & ! intent(in) : minimum time step (seconds) - nState, & ! intent(in) : total number of state variables - doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature - firstSubStep, & ! intent(in) : flag to denote first sub-step - firstFluxCall, & ! intent(inout) : flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in) : flag to denote if computing energy flux over vegetation - scalarSolution, & ! intent(in) : flag to denote implementing the scalar solution - iStateSplit, & ! intent(in) : index of the state in the splitting operation - fluxMask, & ! intent(in) : mask for the fluxes used in this given state subset - fluxCount, & ! intent(inout) : number of times that fluxes are updated (should equal nSubsteps) - ! input/output: data structures - model_decisions, & ! intent(in) : model decisions - lookup_data, & ! intent(in) : lookup tables - type_data, & ! intent(in) : type of vegetation and soil - attr_data, & ! intent(in) : spatial attributes - forc_data, & ! intent(in) : model forcing data - mpar_data, & ! intent(in) : model parameters - indx_data, & ! intent(inout) : index data - prog_data, & ! intent(inout) : model prognostic variables for a local HRU - diag_data, & ! intent(inout) : model diagnostic variables for a local HRU - flux_data, & ! intent(inout) : model fluxes for a local HRU - deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables - bvar_data, & ! intent(in) : model variables for the local basin - ! output: model control - ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) - dtMultiplier, & ! intent(out) : substep multiplier (-) - nSubsteps, & ! intent(out) : number of substeps taken for a given split - failedMinimumStep, & ! intent(out) : flag to denote success of substepping for a given split - reduceCoupledStep, & ! intent(out) : flag to denote need to reduce the length of the coupled step - tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt - err,message) ! intent(out) : error code and error message - ! --------------------------------------------------------------------------------------- - ! structure allocations - USE allocspace_module,only:allocLocal ! allocate local data structures - ! simulation of fluxes and residuals given a trial state vector - USE systemSolv_module,only:systemSolv ! solve the system of equations for one time step - USE getVectorz_module,only:popStateVec ! populate the state vector - USE getVectorz_module,only:varExtract ! extract variables from the state vector - USE updateVars_module,only:updateVars ! update prognostic variables - ! identify name of variable type (for error message) - USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages - implicit none - ! --------------------------------------------------------------------------------------- - ! * dummy variables - ! --------------------------------------------------------------------------------------- - ! input: model control - real(rkind),intent(in) :: dt ! time step (seconds) - real(rkind),intent(in) :: dtInit ! initial time step (seconds) - real(rkind),intent(in) :: dt_min ! minimum time step (seconds) - integer(i4b),intent(in) :: nState ! total number of state variables - logical(lgt),intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) - logical(lgt),intent(in) :: scalarSolution ! flag to denote implementing the scalar solution - integer(i4b),intent(in) :: iStateSplit ! index of the state in the splitting operation - type(var_flagVec),intent(in) :: fluxMask ! flags to denote if the flux is calculated in the given state subset - type(var_ilength),intent(inout) :: fluxCount ! number of times that the flux is updated (should equal nSubsteps) - ! input/output: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(zLookup),intent(in) :: lookup_data ! lookup tables - type(var_i),intent(in) :: type_data ! type of vegetation and soil - type(var_d),intent(in) :: attr_data ! spatial attributes - type(var_d),intent(in) :: forc_data ! model forcing data - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU - type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin - ! output: model control - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: dtMultiplier ! substep multiplier (-) - integer(i4b),intent(out) :: nSubsteps ! number of substeps taken for a given split - logical(lgt),intent(out) :: failedMinimumStep ! flag to denote success of substepping for a given split - logical(lgt),intent(out) :: reduceCoupledStep ! flag to denote need to reduce the length of the coupled step - logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! --------------------------------------------------------------------------------------- - ! * general local variables - ! --------------------------------------------------------------------------------------- - ! error control - character(LEN=256) :: cmessage ! error message of downwind routine - ! general local variables - integer(i4b) :: iVar ! index of variables in data structures - integer(i4b) :: iSoil ! index of soil layers - integer(i4b) :: ixLayer ! index in a given domain - integer(i4b), dimension(1) :: ixMin,ixMax ! bounds of a given flux vector - ! time stepping - real(rkind) :: dtSum ! sum of time from successful steps (seconds) - real(rkind) :: dt_wght ! weight given to a given flux calculation - real(rkind) :: dtSubstep ! length of a substep (s) - real(rkind) :: maxstep ! maximum time step length (seconds) - ! adaptive sub-stepping for the explicit solution - logical(lgt) :: failedSubstep ! flag to denote success of substepping for a given split - real(rkind),parameter :: safety=0.85_rkind ! safety factor in adaptive sub-stepping - real(rkind),parameter :: reduceMin=0.1_rkind ! mimimum factor that time step is reduced - real(rkind),parameter :: increaseMax=4.0_rkind ! maximum factor that time step is increased - ! adaptive sub-stepping for the implicit solution - integer(i4b) :: niter ! number of iterations taken - integer(i4b),parameter :: n_inc=5 ! minimum number of iterations to increase time step - integer(i4b),parameter :: n_dec=15 ! maximum number of iterations to decrease time step - real(rkind),parameter :: F_inc = 1.25_rkind ! factor used to increase time step - real(rkind),parameter :: F_dec = 0.90_rkind ! factor used to decrease time step - ! state and flux vectors - real(rkind) :: untappedMelt(nState) ! un-tapped melt energy (J m-3 s-1) - real(rkind) :: stateVecInit(nState) ! initial state vector (mixed units) - real(rkind) :: stateVecTrial(nState) ! trial state vector (mixed units) - type(var_dlength) :: flux_temp ! temporary model fluxes - ! flags - logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt) :: checkMassBalance ! flag to check the mass balance - logical(lgt) :: checkNrgBalance - logical(lgt) :: waterBalanceError ! flag to denote that there is a water balance error - logical(lgt) :: nrgFluxModified ! flag to denote that the energy fluxes were modified - ! energy fluxes - real(rkind) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) - real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) - real(rkind) :: sumSoilCompress - real(rkind),allocatable :: sumLayerCompress(:) - ! --------------------------------------------------------------------------------------- - ! point to variables in the data structures - ! --------------------------------------------------------------------------------------- - globalVars: associate(& - ! number of layers - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! mapping between state vectors and control volumes - ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat ,& ! intent(in): [i4b(:)] list of indices for all active layers (inactive=integerMissing) - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) - ! model state variables (vegetation canopy) - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout): [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout): [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout): [dp] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat & ! intent(inout): [dp(:)] matric potential of liquid water (m) - ) ! end association with variables in the data structures - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - ! Procedure starts here - - ! initialize error control - err=0; message='varSubstep/' - - ! initialize flag for the success of the substepping - failedMinimumStep=.false. - - ! initialize the length of the substep - ! change maxstep with hard code here to make only the newton step loop in systemSolve happen more frequently for num_method = bEuler - maxstep = 1800._rkind !mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) - dtSubstep = dtInit - - ! allocate space for the temporary model flux structure - call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! initialize the model fluxes (some model fluxes are not computed in the iterations) - do iVar=1,size(flux_data%var) - flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) - end do - - ! initialize the total energy fluxes (modified in updateProg) - sumCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap = 0._rkind ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy = 0._rkind ! sensible heat flux from the canopy to the canopy air space (W m-2) - sumSoilCompress = 0._rkind ! total soil compression - allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._rkind ! soil compression by layer - - ! define the first flux call in a splitting operation - firstSplitOper = (.not.scalarSolution .or. iStateSplit==1) - - ! initialize subStep - dtSum = 0._rkind ! keep track of the portion of the time step that is completed - nSubsteps = 0 - - ! loop through substeps - ! NOTE: continuous do statement with exit clause - substeps: do - dtSubstep = min(dtSubstep,maxstep) - - ! initialize error control - err=0; message='varSubstep/' - - ! ----- - ! * populate state vectors... - ! --------------------------- - - ! initialize state vectors - call popStateVec(& - ! input - nState, & ! intent(in): number of desired state variables - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output - stateVecInit, & ! intent(out): initial model state vector (mixed units) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - ! ----- - ! * iterative solution... - ! ----------------------- - - ! solve the system of equations for a given state subset - call systemSolv(& - ! input: model control - dtSubstep, & ! intent(in): time step (s) - dtInit, & ! intent(in): entire time step (s) - nState, & ! intent(in): total number of state variables - firstSubStep, & ! intent(in): flag to denote first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation - scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution - ! input/output: data structures - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): index data - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_temp, & ! intent(inout): model fluxes for a local HRU - bvar_data, & ! intent(in): model variables for the local basin - model_decisions, & ! intent(in): model decisions - stateVecInit, & ! intent(in): initial state vector - ! output: model control - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - untappedMelt, & ! intent(out): un-tapped melt energy (J m-3 s-1) - stateVecTrial, & ! intent(out): updated state vector - reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step - tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt - niter, & ! intent(out): number of iterations taken - err,cmessage) ! intent(out): error code and error message - if(err/=0)then - message=trim(message)//trim(cmessage) - if(err>0) return - endif - - ! if too much melt or need to reduce length of the coupled step then return - ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry - if(tooMuchMelt .or. reduceCoupledStep) return - - ! identify failure - failedSubstep = (err<0) +! ********************************************************************************************************** +! public subroutine varSubstep: run the model for a collection of substeps for a given state subset +! ********************************************************************************************************** +subroutine varSubstep(& + ! input: model control + dt, & ! intent(in) : time step (s) + dtInit, & ! intent(in) : initial time step (seconds) + dt_min, & ! intent(in) : minimum time step (seconds) + nState, & ! intent(in) : total number of state variables + doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature + firstSubStep, & ! intent(in) : flag to denote first sub-step + firstFluxCall, & ! intent(inout) : flag to indicate if we are processing the first flux call + computeVegFlux, & ! intent(in) : flag to denote if computing energy flux over vegetation + scalarSolution, & ! intent(in) : flag to denote implementing the scalar solution + iStateSplit, & ! intent(in) : index of the state in the splitting operation + fluxMask, & ! intent(in) : mask for the fluxes used in this given state subset + fluxCount, & ! intent(inout) : number of times that fluxes are updated (should equal nSubsteps) + ! input/output: data structures + model_decisions, & ! intent(in) : model decisions + lookup_data, & ! intent(in) : lookup tables + type_data, & ! intent(in) : type of vegetation and soil + attr_data, & ! intent(in) : spatial attributes + forc_data, & ! intent(in) : model forcing data + mpar_data, & ! intent(in) : model parameters + indx_data, & ! intent(inout) : index data + prog_data, & ! intent(inout) : model prognostic variables for a local HRU + diag_data, & ! intent(inout) : model diagnostic variables for a local HRU + flux_data, & ! intent(inout) : model fluxes for a local HRU + deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables + bvar_data, & ! intent(in) : model variables for the local basin + ! output: model control + ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) + dtMultiplier, & ! intent(out) : substep multiplier (-) + nSubsteps, & ! intent(out) : number of substeps taken for a given split + failedMinimumStep, & ! intent(out) : flag to denote success of substepping for a given split + reduceCoupledStep, & ! intent(out) : flag to denote need to reduce the length of the coupled step + tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt + err,message) ! intent(out) : error code and error message + ! --------------------------------------------------------------------------------------- + ! structure allocations + USE allocspace_module,only:allocLocal ! allocate local data structures + ! simulation of fluxes and residuals given a trial state vector + USE getVectorz_module,only:popStateVec ! populate the state vector + USE getVectorz_module,only:varExtract ! extract variables from the state vector + USE systemSolvSundials_module,only:systemSolvSundials ! solve the system of equations for one time step + USE systemSolv_module,only:systemSolv ! solve the system of equations for one time step + ! identify name of variable type (for error message) + USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages + implicit none + ! --------------------------------------------------------------------------------------- + ! * dummy variables + ! --------------------------------------------------------------------------------------- + ! input: model control + real(rkind),intent(in) :: dt ! time step (seconds) + real(rkind),intent(in) :: dtInit ! initial time step (seconds) + real(rkind),intent(in) :: dt_min ! minimum time step (seconds) + integer(i4b),intent(in) :: nState ! total number of state variables + logical(lgt),intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) + logical(lgt),intent(in) :: scalarSolution ! flag to denote implementing the scalar solution + integer(i4b),intent(in) :: iStateSplit ! index of the state in the splitting operation + type(var_flagVec),intent(in) :: fluxMask ! flags to denote if the flux is calculated in the given state subset + type(var_ilength),intent(inout) :: fluxCount ! number of times that the flux is updated (should equal nSubsteps) + ! input/output: data structures + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_i),intent(in) :: type_data ! type of vegetation and soil + type(var_d),intent(in) :: attr_data ! spatial attributes + type(var_d),intent(in) :: forc_data ! model forcing data + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU + type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin + ! output: model control + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + real(rkind),intent(out) :: dtMultiplier ! substep multiplier (-) + integer(i4b),intent(out) :: nSubsteps ! number of substeps taken for a given split + logical(lgt),intent(out) :: failedMinimumStep ! flag to denote success of substepping for a given split + logical(lgt),intent(out) :: reduceCoupledStep ! flag to denote need to reduce the length of the coupled step + logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! --------------------------------------------------------------------------------------- + ! * general local variables + ! --------------------------------------------------------------------------------------- + ! error control + character(LEN=256) :: cmessage ! error message of downwind routine + ! general local variables + integer(i4b) :: iVar ! index of variables in data structures + integer(i4b) :: iSoil ! index of soil layers + integer(i4b) :: ixLayer ! index in a given domain + integer(i4b), dimension(1) :: ixMin,ixMax ! bounds of a given flux vector + ! time stepping + real(rkind) :: dtSum ! sum of time from successful steps (seconds) + real(rkind) :: dt_wght ! weight given to a given flux calculation + real(rkind) :: dtSubstep ! length of a substep (s) + real(rkind) :: maxstep ! maximum time step length (seconds) + ! adaptive sub-stepping for the explicit solution + logical(lgt) :: failedSubstep ! flag to denote success of substepping for a given split + real(rkind),parameter :: safety=0.85_rkind ! safety factor in adaptive sub-stepping + real(rkind),parameter :: reduceMin=0.1_rkind ! mimimum factor that time step is reduced + real(rkind),parameter :: increaseMax=4.0_rkind ! maximum factor that time step is increased + ! adaptive sub-stepping for the implicit solution + integer(i4b) :: niter ! number of iterations taken + integer(i4b),parameter :: n_inc=5 ! minimum number of iterations to increase time step + integer(i4b),parameter :: n_dec=15 ! maximum number of iterations to decrease time step + real(rkind),parameter :: F_inc = 1.25_rkind ! factor used to increase time step + real(rkind),parameter :: F_dec = 0.90_rkind ! factor used to decrease time step + ! state and flux vectors + real(rkind) :: untappedMelt(nState) ! un-tapped melt energy (J m-3 s-1) + real(rkind) :: stateVecInit(nState) ! initial state vector (mixed units) + real(rkind) :: stateVecTrial(nState) ! trial state vector (mixed units) + real(rkind) :: stateVecPrime(nState) ! trial state vector (mixed units) + type(var_dlength) :: flux_temp ! temporary model fluxes + ! flags + logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt) :: checkMassBalance ! flag to check the mass balance + logical(lgt) :: checkNrgBalance + logical(lgt) :: waterBalanceError ! flag to denote that there is a water balance error + logical(lgt) :: nrgFluxModified ! flag to denote that the energy fluxes were modified + ! energy fluxes + real(rkind) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) + real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) + real(rkind) :: sumSoilCompress + real(rkind),allocatable :: sumLayerCompress(:) + ! --------------------------------------------------------------------------------------- + ! point to variables in the data structures + ! --------------------------------------------------------------------------------------- + globalVars: associate(& + ! model decisions + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical method, backward Euler or SUNDIALS/IDA + ! number of layers + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! mapping between state vectors and control volumes + ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat ,& ! intent(in): [i4b(:)] list of indices for all active layers (inactive=integerMissing) + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) + ! model state variables (vegetation canopy) + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout): [dp] temperature of the vegetation canopy (K) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout): [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout): [dp] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout): [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variables (snow and soil domains) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat & ! intent(inout): [dp(:)] matric potential of liquid water (m) + ) ! end association with variables in the data structures + ! ********************************************************************************************************************************************************* + ! ********************************************************************************************************************************************************* + ! Procedure starts here + + ! initialize error control + err=0; message='varSubstep/' + + ! initialize flag for the success of the substepping + failedMinimumStep=.false. + + ! initialize the length of the substep + dtSubstep = dtInit + + ! change maxstep with hard code here to make only the newton step loop in systemSolve happen more frequently for num_method = bEuler with BE>1 + maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) + + ! initalize flag for checking if energy fluxes had been modified + nrgFluxModified = .false. + + ! allocate space for the temporary model flux structure + call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! initialize the model fluxes (some model fluxes are not computed in the iterations) + do iVar=1,size(flux_data%var) + flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + end do + + ! initialize the total energy fluxes (modified in updateProg) + sumCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = 0._rkind ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = 0._rkind ! sensible heat flux from the canopy to the canopy air space (W m-2) + sumSoilCompress = 0._rkind ! total soil compression + allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._rkind ! soil compression by layer + + ! define the first flux call in a splitting operation + firstSplitOper = (.not.scalarSolution .or. iStateSplit==1) + + ! initialize subStep + dtSum = 0._rkind ! keep track of the portion of the time step that is completed + nSubsteps = 0 + + ! loop through substeps + ! NOTE: continuous do statement with exit clause + substeps: do + dtSubstep = min(dtSubstep,maxstep) + + ! initialize error control + err=0; message='varSubstep/' + + ! ----- + ! * populate state vectors... + ! --------------------------- + + ! initialize state vectors + call popStateVec(& + ! input + nState, & ! intent(in): number of desired state variables + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output + stateVecInit, & ! intent(out): initial model state vector (mixed units) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + + ! ----- + ! * iterative solution... + ! ----------------------- + ! solve the system of equations for a given state subset + select case(ixNumericalMethod) + case(sundials) + call systemSolvSundials(& + ! input: model control + dtSubstep, & ! intent(in): time step (s) + dtInit, & ! intent(in): entire time step (s), right now same as dtSubstep but might change with operator splitting + nState, & ! intent(in): total number of state variables + firstSubStep, & ! intent(in): flag to denote first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution + ! input/output: data structures + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): index data + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_temp, & ! intent(inout): model fluxes for a local HRU + bvar_data, & ! intent(in): model variables for the local basin + model_decisions, & ! intent(in): model decisions + stateVecInit, & ! intent(in): initial state vector + ! output: model control + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + stateVecTrial, & ! intent(out): updated state vector + stateVecPrime, & ! intent(out): updated state vector + reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step + tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt + err,cmessage) ! intent(out): error code and error message + untappedMelt(:) = 0._rkind ! set untapped melt energy to zero + niter = 0 ! will not use + case(bEuler) + call systemSolv(& + ! input: model control + dtSubstep, & ! intent(in): time step (s) + dtInit, & ! intent(in): entire time step (s) + nState, & ! intent(in): total number of state variables + firstSubStep, & ! intent(in): flag to denote first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution + ! input/output: data structures + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): index data + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_temp, & ! intent(inout): model fluxes for a local HRU + bvar_data, & ! intent(in): model variables for the local basin + model_decisions, & ! intent(in): model decisions + stateVecInit, & ! intent(in): initial state vector + ! output: model control + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + untappedMelt, & ! intent(out): un-tapped melt energy (J m-3 s-1) + stateVecTrial, & ! intent(out): updated state vector + reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step + tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt + niter, & ! intent(out): number of iterations taken + err,cmessage) ! intent(out): error code and error message + stateVecPrime = stateVecTrial ! will not use, dummy + case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return + end select + + if(err/=0)then + message=trim(message)//trim(cmessage) + if(err>0) return + endif - ! check - if(globalPrintFlag)then - print*, 'niter, failedSubstep, dtSubstep = ', niter, failedSubstep, dtSubstep - print*, trim(cmessage) - endif + ! if too much melt or need to reduce length of the coupled step then return + ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry + if(tooMuchMelt .or. reduceCoupledStep) return - ! reduce step based on failure - if(failedSubstep)then - err=0; message='varSubstep/' ! recover from failed convergence - dtMultiplier = 0.5_rkind ! system failure: step halving - else - - ! ** implicit Euler: adjust step length based on iteration count - if(nitern_dec)then - dtMultiplier = F_dec - else - dtMultiplier = 1._rkind - endif + ! identify failure, should not happen in sundials + failedSubstep = (err<0) - endif ! switch between failure and success - - ! check if we failed the substep - if(failedSubstep)then + ! check + if(globalPrintFlag)then + print*, 'niter, failedSubstep, dtSubstep = ', niter, failedSubstep, dtSubstep + print*, trim(cmessage) + endif - ! check that the substep is greater than the minimum step - if(dtSubstep*dtMultiplier exit, and either (1) try another solution method; or (2) reduce coupled step - failedMinimumStep=.true. - exit subSteps + ! reduce step based on failure + if(failedSubstep)then + err=0; message='varSubstep/' ! recover from failed convergence + dtMultiplier = 0.5_rkind ! system failure: step halving + else + ! ** implicit Euler: adjust step length based on iteration count + if(nitern_dec)then + dtMultiplier = F_dec + else + dtMultiplier = 1._rkind + endif + endif ! switch between failure and success + + ! check if we failed the substep + if(failedSubstep)then + + ! check that the substep is greater than the minimum step + if(dtSubstep*dtMultiplier exit, and either (1) try another solution method; or (2) reduce coupled step + failedMinimumStep=.true. + exit subSteps + + else ! step is still OK + dtSubstep = dtSubstep*dtMultiplier + cycle subSteps + endif ! if step is less than the minimum + + endif ! if failed the substep + + ! ----- + ! * update model fluxes... + ! ------------------------ + + ! NOTE: if we get to here then we are accepting the step of dtSubstep + if(err/=0)then + message=trim(message)//'expect err=0 if updating fluxes' + return + endif - else ! step is still OK - dtSubstep = dtSubstep*dtMultiplier - cycle subSteps - endif ! if step is less than the minimum + ! identify the need to check the mass balance, only for bEuler as fluxes are not not for checking in sundials + select case(ixNumericalMethod) + case(sundials); checkMassBalance = .false. + case(bEuler); checkMassBalance = .true. ! (.not.scalarSolution) + case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return + end select + ! identify the need to check the energy balance, DOES NOT WORK YET and only check if ixHowHeatCap == enthalpyFD + checkNrgBalance = .false. + + ! update prognostic variables + call updateProg(dtSubstep,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control + model_decisions,lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures + waterBalanceError,nrgFluxModified,err,cmessage) ! output: flags and error control + if(err/=0)then + message=trim(message)//trim(cmessage) + if(err>0) return + endif - endif ! if failed the substep + ! if water balance error then reduce the length of the coupled step + if(waterBalanceError)then + message=trim(message)//'water balance error' + reduceCoupledStep=.true. + err=-20; return + endif - ! ----- - ! * update model fluxes... - ! ------------------------ + if(globalPrintFlag)& + print*, trim(cmessage)//': dt = ', dtSubstep - ! NOTE: if we get to here then we are accepting the step of dtSubstep - if(err/=0)then - message=trim(message)//'expect err=0 if updating fluxes' - return - endif + ! recover from errors in prognostic update + if(err<0)then - ! identify the need to check the mass balance - checkMassBalance = .true. ! (.not.scalarSolution) - checkNrgBalance = .false. ! only check if ixHowHeatCap == enthalpyFD - - ! update prognostic variables - call updateProg(dtSubstep,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,checkMassBalance, checkNrgBalance, & ! input: model control - lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures - waterBalanceError,nrgFluxModified,err,cmessage) ! output: flags and error control - if(err/=0)then - message=trim(message)//trim(cmessage) - if(err>0) return - endif + ! modify step + err=0 ! error recovery + dtSubstep = dtSubstep/2._rkind - ! if water balance error then reduce the length of the coupled step - if(waterBalanceError)then - message=trim(message)//'water balance error' - reduceCoupledStep=.true. - err=-20; return - endif - - if(globalPrintFlag)& - print*, trim(cmessage)//': dt = ', dtSubstep - - ! recover from errors in prognostic update - if(err<0)then - - ! modify step - err=0 ! error recovery - dtSubstep = dtSubstep/2._rkind - - ! check minimum: fail minimum step if there is an error in the update - if(dtSubstep0) then - ! scalar compression - if(.not.scalarSolution .or. iStateSplit==nSoil)& - sumSoilCompress = sumSoilCompress + dtSubstep*diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression - ! vector compression - do iSoil=1,nSoil - if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& - sumLayerCompress(iSoil) = sumLayerCompress(iSoil) + dtSubstep*diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) ! soil compression in layers - end do - endif - - ! print progress - if(globalPrintFlag)& - write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt - - ! increment fluxes - dt_wght = dtSubstep/dt ! (define weight applied to each splitting operation) - do iVar=1,size(flux_meta) - if(count(fluxMask%var(iVar)%dat)>0) then - - !print*, flux_meta(iVar)%varname, fluxMask%var(iVar)%dat - - ! ** no domain splitting - if(count(ixLayerActive/=integerMissing)==nLayers)then - flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght - fluxCount%var(iVar)%dat(:) = fluxCount%var(iVar)%dat(:) + 1 - - ! ** domain splitting - else - ixMin=lbound(flux_data%var(iVar)%dat) - ixMax=ubound(flux_data%var(iVar)%dat) - do ixLayer=ixMin(1),ixMax(1) - if(fluxMask%var(iVar)%dat(ixLayer)) then - flux_data%var(iVar)%dat(ixLayer) = flux_data%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght - fluxCount%var(iVar)%dat(ixLayer) = fluxCount%var(iVar)%dat(ixLayer) + 1 + ! check minimum: fail minimum step if there is an error in the update + if(dtSubstep0) then + ! scalar compression + if(.not.scalarSolution .or. iStateSplit==nSoil)& + sumSoilCompress = sumSoilCompress + dtSubstep*diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression + ! vector compression + do iSoil=1,nSoil + if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& + sumLayerCompress(iSoil) = sumLayerCompress(iSoil) + dtSubstep*diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) ! soil compression in layers + end do endif - end do - endif ! (domain splitting) - endif ! (if the flux is desired) - end do ! (loop through fluxes) + ! print progress + if(globalPrintFlag)& + write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt - ! ------------------------------------------------------ - ! ------------------------------------------------------ + ! increment fluxes, define weight applied to each splitting operation only possible in bEuler right now + select case(ixNumericalMethod) + case(sundials); dt_wght = 1._qp + case(bEuler); dt_wght = dtSubstep/dt + case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return + end select - ! increment the number of substeps - nSubsteps = nSubsteps+1 + do iVar=1,size(flux_meta) + if(count(fluxMask%var(iVar)%dat)>0) then - ! increment the sub-step legth - dtSum = dtSum + dtSubstep + ! ** no domain splitting + if(count(ixLayerActive/=integerMissing)==nLayers)then + flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght + fluxCount%var(iVar)%dat(:) = fluxCount%var(iVar)%dat(:) + 1 - ! check that we have completed the sub-step - if(dtSum >= dt-verySmall)then - failedMinimumStep=.false. - exit subSteps - endif - - ! adjust length of the sub-step (make sure that we don't exceed the step) - dtSubstep = min(dt - dtSum, max(dtSubstep*dtMultiplier, dt_min) ) + ! ** domain splitting + else + ixMin=lbound(flux_data%var(iVar)%dat) + ixMax=ubound(flux_data%var(iVar)%dat) + do ixLayer=ixMin(1),ixMax(1) + if(fluxMask%var(iVar)%dat(ixLayer)) then + flux_data%var(iVar)%dat(ixLayer) = flux_data%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght + fluxCount%var(iVar)%dat(ixLayer) = fluxCount%var(iVar)%dat(ixLayer) + 1 + endif + end do + endif ! (domain splitting) + + endif ! (if the flux is desired) + end do ! (loop through fluxes) + + ! increment the number of substeps + nSubsteps = nSubsteps+1 + + ! increment the sub-step legth + dtSum = dtSum + dtSubstep + + ! check that we have completed the sub-step + if(dtSum >= dt-verySmall)then + failedMinimumStep=.false. + exit subSteps + endif - end do substeps ! time steps for variable-dependent sub-stepping - ! NOTE: if we get to here then we are accepting then dtSum should dt + ! adjust length of the sub-step (make sure that we don't exceed the step) + dtSubstep = min(dt - dtSum, max(dtSubstep*dtMultiplier, dt_min) ) - ! save the energy fluxes - flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt ! canopy evaporation/condensation (kg m-2 s-1) - flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt ! sensible heat flux from the canopy to the canopy air space (W m-2) + end do substeps ! time steps for variable-dependent sub-stepping + ! NOTE: if we get to here then we are accepting then dtSum should dt - ! save the soil compression diagnostics - diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress - do iSoil=1,nSoil - if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& - diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) = sumLayerCompress(iSoil) - end do - deallocate(sumLayerCompress) + ! save the energy fluxes + flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt ! canopy evaporation/condensation (kg m-2 s-1) + flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt ! sensible heat flux from the canopy to the canopy air space (W m-2) - ! end associate statements - end associate globalVars + ! save the soil compression diagnostics + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress + do iSoil=1,nSoil + if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& + diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) = sumLayerCompress(iSoil) + end do + deallocate(sumLayerCompress) - ! update error codes - if(failedMinimumStep)then - err=-20 ! negative = recoverable error - message=trim(message)//'failed minimum step' - endif + ! end associate statements + end associate globalVars - end subroutine varSubstep + ! update error codes + if(failedMinimumStep)then + err=-20 ! negative = recoverable error + message=trim(message)//'failed minimum step' + endif +end subroutine varSubstep - ! ********************************************************************************************************** - ! private subroutine updateProg: update prognostic variables - ! ********************************************************************************************************** -subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,checkMassBalance, checkNrgBalance, & ! input: model control - lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures - waterBalanceError,nrgFluxModified,err,message) ! output: flags and error control +! ********************************************************************************************************** +! private subroutine updateProg: update prognostic variables +! ********************************************************************************************************** +subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control + model_decisions,lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures + waterBalanceError,nrgFluxModified,err,message) ! output: flags and error control USE getVectorz_module,only:varExtract ! extract variables from the state vector + USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables USE updateVars_module,only:updateVars ! update prognostic variables USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy implicit none @@ -558,10 +613,12 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe logical(lgt) ,intent(in) :: computeVegFlux ! flag to compute the vegetation flux real(rkind) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) real(rkind) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) + real(rkind) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance logical(lgt) ,intent(in) :: checkNrgBalance ! flag to check the energy balance ! data structures - type(zLookup),intent(in) :: lookup_data ! lookup tables + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup),intent(in) :: lookup_data ! lookup tables type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! indices for a local HRU type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU @@ -573,7 +630,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe logical(lgt) ,intent(out) :: nrgFluxModified ! flag to denote that the energy fluxes were modified integer(i4b) ,intent(out) :: err ! error code character(*) ,intent(out) :: message ! error message - ! ================================================================================================================== + ! ================================================================================================================== ! general integer(i4b) :: iState ! index of model state variable integer(i4b) :: ixSubset ! index within the state subset @@ -605,6 +662,20 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric fraction of liquid water (-) real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric fraction of ice (-) + ! derivative of state variables + real(rkind) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatPrime ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempPrime ! trial vector for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! trial vector for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! trial vector for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! trial vector for liquid water matric potential (m) + real(rkind) :: scalarAquiferStoragePrime ! trial value for storage of water in the aquifer (m) + ! diagnostic variables + real(rkind) :: scalarCanopyLiqPrime ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector for volumetric fraction of ice (-) real(rkind) :: scalarCanairEnthalpyTrial ! enthalpy of the canopy air space (J m-3) real(rkind) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! enthalpy of snow + soil (J m-3) @@ -618,6 +689,8 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! ------------------------------------------------------------------------------------------------------------------- ! point to flux variables in the data structure associate(& + ! model decisions + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical method, backward Euler or SUNDIALS/IDA ! get indices for mass balance ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in) : [i4b] index of canopy hydrology state variable (mass) ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the soil domain @@ -674,9 +747,9 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! error tolerance absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1) & ! intent(in) : [dp] absolute convergence tolerance for vol frac liq water (-) ) ! associating flux variables in the data structure - ! ------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='updateProg/' + ! ------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='updateProg/' ! initialize water balancmLayerVolFracWatTrial error waterBalanceError=.false. @@ -727,8 +800,86 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! update diagnostic variables - call updateVars(& + ! initialize to state variable from the last update + ! should all be set to previous values if splits, but for now operator splitting is not hooked up + scalarCanairTempPrime = realMissing + scalarCanopyTempPrime = realMissing + scalarCanopyWatPrime = realMissing + scalarCanopyLiqPrime = realMissing + scalarCanopyIcePrime = realMissing + mLayerTempPrime = realMissing + mLayerVolFracWatPrime = realMissing + mLayerVolFracLiqPrime = realMissing + mLayerVolFracIcePrime = realMissing + mLayerMatricHeadPrime = realMissing + mLayerMatricHeadLiqPrime = realMissing + scalarAquiferStoragePrime = realMissing + + select case(ixNumericalMethod) + case(sundials) + call varExtract(& + ! input + stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy + scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K) + scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): derivative of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K) + mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (-) + mLayerMatricHeadPrime, & ! intent(inout): derivative of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): derivative of liquid water matric potential (m) + ! output: variables for the aquifer + scalarAquiferStoragePrime,& ! intent(inout): derivative of storage of water in the aquifer (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! update diagnostic variables + call updateVarsSundials(& + ! input + dt, & + .false., & ! intent(in): logical flag if computing Jacobian for Sundials solver + doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + mLayerVolFracWatTrial, & ! intent(in): use current vector for prev vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): use current vector for prev vector of total water matric potential (m) + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output: variables for the vegetation canopy + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempPrime, & ! + mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) + mLayerVolFracIcePrime, & ! + mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) + ! output: error control + err,cmessage) ! intent(out): error control + case(bEuler) + ! update diagnostic variables + call updateVars(& ! input doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze lookup_data, & ! intent(in): lookup tables for a local HRU @@ -751,6 +902,9 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) ! output: error control err,cmessage) ! intent(out): error control + case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return + end select + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! ---- @@ -800,6 +954,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! ----------------------- ! NOTE: should not need to do this, since mass balance is checked in the solver + ! for sundials will not work since fluxes are instantaneous so not currently checked if(checkMassBalance)then ! check mass balance for the canopy diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 deleted file mode 100644 index cbe14a156..000000000 --- a/build/source/engine/varSubstepSundials.f90 +++ /dev/null @@ -1,1120 +0,0 @@ -! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2015 NCAR/RAL -! -! This file is part of SUMMA -! -! For more information see: http://www.ral.ucar.edu/projects/summa -! -! This program is free software: you can redistribute it and/or modify -! it under the terms of the GNU General Public License as published by -! the Free Software Foundation, either version 3 of the License, or -! (at your option) any later version. -! -! This program is distributed in the hope that it will be useful, -! but WITHOUT ANY WARRANTY; without even the implied warranty of -! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -! GNU General Public License for more details. -! -! You should have received a copy of the GNU General Public License -! along with this program. If not, see . - -module varSubstepSundials_module - -! data types -USE nrtype - -! access missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing double precision number -USE globalData,only:quadMissing ! missing quadruple precision number - -! access the global print flag -USE globalData,only:globalPrintFlag - -! domain types -USE globalData,only:iname_cas ! named variables for the canopy air space -USE globalData,only:iname_veg ! named variables for vegetation -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil - -! global metadata -USE globalData,only:flux_meta ! metadata on the model fluxes - -! derived types to define the data structures -USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_flagVec, & ! data vector with variable length dimension (i4b) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! data vector with variable length dimension (rkind) - model_options ! defines the model decisions - -! provide access to indices that define elements of the data structures -USE var_lookup,only:iLookFLUX ! named variables for structure elements -USE var_lookup,only:iLookPROG ! named variables for structure elements -USE var_lookup,only:iLookDIAG ! named variables for structure elements -USE var_lookup,only:iLookPARAM ! named variables for structure elements -USE var_lookup,only:iLookINDEX ! named variables for structure elements -USE var_lookup,only:iLookDERIV ! named variables for structure elements - -! look up structure for variable types -USE var_lookup,only:iLookVarType - -! constants -USE multiconst,only:& - Tfreeze, & ! freezing temperature (K) - LH_fus, & ! latent heat of fusion (J kg-1) - LH_vap, & ! latent heat of vaporization (J kg-1) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water, & ! intrinsic density of liquid water (kg m-3) - ! specific heat - Cp_air, & ! specific heat of air (J kg-1 K-1) - Cp_ice, & ! specific heat of ice (J kg-1 K-1) - Cp_soil, & ! specific heat of soil (J kg-1 K-1) - Cp_water ! specific heat of liquid water (J kg-1 K-1) - -! safety: set private unless specified otherwise -implicit none -private -public::varSubstepSundials - -! algorithmic parameters -real(rkind),parameter :: verySmall=1.e-6_rkind ! used as an additive constant to check if substantial difference among real numbers - -contains - - -! ********************************************************************************************************** -! public subroutine varSubstepSundials: run the model for a collection of substeps for a given state subset -! ********************************************************************************************************** -subroutine varSubstepSundials(& - ! input: model control - dt, & ! intent(in) : time step (s) - dtInit, & ! intent(in) : initial time step (seconds) - dt_min, & ! intent(in) : minimum time step (seconds) - nState, & ! intent(in) : total number of state variables - doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature - firstSubStep, & ! intent(in) : flag to denote first sub-step - firstFluxCall, & ! intent(inout) : flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in) : flag to denote if computing energy flux over vegetation - scalarSolution, & ! intent(in) : flag to denote implementing the scalar solution - iStateSplit, & ! intent(in) : index of the state in the splitting operation - fluxMask, & ! intent(in) : mask for the fluxes used in this given state subset - fluxCount, & ! intent(inout) : number of times that fluxes are updated (should equal nSubsteps) - ! input/output: data structures - model_decisions, & ! intent(in) : model decisions - lookup_data, & ! intent(in) : lookup tables - type_data, & ! intent(in) : type of vegetation and soil - attr_data, & ! intent(in) : spatial attributes - forc_data, & ! intent(in) : model forcing data - mpar_data, & ! intent(in) : model parameters - indx_data, & ! intent(inout) : index data - prog_data, & ! intent(inout) : model prognostic variables for a local HRU - diag_data, & ! intent(inout) : model diagnostic variables for a local HRU - flux_data, & ! intent(inout) : model fluxes for a local HRU - deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables - bvar_data, & ! intent(in) : model variables for the local basin - ! output: model control - ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) - dtMultiplier, & ! intent(out) : substep multiplier (-) - nSubsteps, & ! intent(out) : number of substeps taken for a given split - failedMinimumStep, & ! intent(out) : flag to denote success of substepping for a given split - reduceCoupledStep, & ! intent(out) : flag to denote need to reduce the length of the coupled step - tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt - err,message) ! intent(out) : error code and error message - ! --------------------------------------------------------------------------------------- - ! structure allocations - USE allocspace_module,only:allocLocal ! allocate local data structures - ! simulation of fluxes and residuals given a trial state vector - USE getVectorz_module,only:popStateVec ! populate the state vector - USE getVectorz_module,only:varExtract ! extract variables from the state vector - USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables - ! identify name of variable type (for error message) - USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages - USE systemSolvSundials_module,only:systemSolvSundials - implicit none - ! --------------------------------------------------------------------------------------- - ! * dummy variables - ! --------------------------------------------------------------------------------------- - ! input: model control - real(rkind),intent(in) :: dt ! time step (seconds) - real(rkind),intent(in) :: dtInit ! initial time step (seconds) - real(rkind),intent(in) :: dt_min ! minimum time step (seconds) - integer(i4b),intent(in) :: nState ! total number of state variables - logical(lgt),intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) - logical(lgt),intent(in) :: scalarSolution ! flag to denote implementing the scalar solution - integer(i4b),intent(in) :: iStateSplit ! index of the state in the splitting operation - type(var_flagVec),intent(in) :: fluxMask ! flags to denote if the flux is calculated in the given state subset - type(var_ilength),intent(inout) :: fluxCount ! number of times that the flux is updated (should equal nSubsteps) - ! input/output: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(zLookup),intent(in) :: lookup_data ! lookup tables - type(var_i),intent(in) :: type_data ! type of vegetation and soil - type(var_d),intent(in) :: attr_data ! spatial attributes - type(var_d),intent(in) :: forc_data ! model forcing data - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU - type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin - ! output: model control - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: dtMultiplier ! substep multiplier (-) - integer(i4b),intent(out) :: nSubsteps ! number of substeps taken for a given split - logical(lgt),intent(out) :: failedMinimumStep ! flag to denote success of substepping for a given split - logical(lgt),intent(out) :: reduceCoupledStep ! flag to denote need to reduce the length of the coupled step - logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! --------------------------------------------------------------------------------------- - ! * general local variables - ! --------------------------------------------------------------------------------------- - ! error control - character(LEN=256) :: cmessage ! error message of downwind routine - ! general local variables - integer(i4b) :: iVar ! index of variables in data structures - integer(i4b) :: iSoil ! index of soil layers - integer(i4b) :: ixLayer ! index in a given domain - integer(i4b), dimension(1) :: ixMin,ixMax ! bounds of a given flux vector - ! time stepping - real(rkind) :: dtSum ! sum of time from successful steps (seconds) - real(rkind) :: dt_wght ! weight given to a given flux calculation - real(rkind) :: dtSubstep ! length of a substep (s) - ! adaptive sub-stepping for the explicit solution - logical(lgt) :: failedSubstep ! flag to denote success of substepping for a given split - real(rkind),parameter :: safety=0.85_rkind ! safety factor in adaptive sub-stepping - real(rkind),parameter :: reduceMin=0.1_rkind ! mimimum factor that time step is reduced - real(rkind),parameter :: increaseMax=4.0_rkind ! maximum factor that time step is increased - ! adaptive sub-stepping for the implicit solution - integer(i4b),parameter :: n_inc=5 ! minimum number of iterations to increase time step - integer(i4b),parameter :: n_dec=15 ! maximum number of iterations to decrease time step - real(rkind),parameter :: F_inc = 1.25_rkind ! factor used to increase time step - real(rkind),parameter :: F_dec = 0.90_rkind ! factor used to decrease time step - ! state and flux vectors - real(rkind) :: untappedMelt(nState) ! un-tapped melt energy (J m-3 s-1) - real(rkind) :: stateVecInit(nState) ! initial state vector (mixed units) - real(rkind) :: stateVecTrial(nState) ! trial state vector (mixed units) - real(rkind) :: stateVecPrime(nState) ! trial state vector (mixed units) - type(var_dlength) :: flux_temp ! temporary model fluxes - ! flags - logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt) :: checkMassBalance ! flag to check the mass balance - logical(lgt) :: checkNrgBalance - logical(lgt) :: waterBalanceError ! flag to denote that there is a water balance error - logical(lgt) :: nrgFluxModified ! flag to denote that the energy fluxes were modified - ! energy fluxes - real(rkind) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) - real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) - real(rkind) :: sumSoilCompress - real(rkind),allocatable :: sumLayerCompress(:) - ! --------------------------------------------------------------------------------------- - ! point to variables in the data structures - ! --------------------------------------------------------------------------------------- - globalVars: associate(& - ! number of layers - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! mapping between state vectors and control volumes - ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat ,& ! intent(in): [i4b(:)] list of indices for all active layers (inactive=integerMissing) - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) - ! model state variables (vegetation canopy) - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout): [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout): [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout): [dp] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat & ! intent(inout): [dp(:)] matric potential of liquid water (m) - ) ! end association with variables in the data structures - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - ! Procedure starts here - - ! initialize error control - err=0; message='varSubstepSundials/' - - ! initialize flag for the success of the substepping - failedMinimumStep=.false. - - ! initialize the length of the substep - dtSubstep = dtInit - - ! initalize flag for checking if energy fluxes had been modified - nrgFluxModified = .false. - - ! allocate space for the temporary model flux structure - call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! initialize the model fluxes (some model fluxes are not computed in the iterations) - do iVar=1,size(flux_data%var) - flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) - end do - - ! initialize the total energy fluxes (modified in updateProgSundials) - sumCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap = 0._rkind ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy = 0._rkind ! sensible heat flux from the canopy to the canopy air space (W m-2) - sumSoilCompress = 0._rkind ! total soil compression - allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._rkind ! soil compression by layer - - ! define the first flux call in a splitting operation - firstSplitOper = (.not.scalarSolution .or. iStateSplit==1) - - ! initialize subStep - dtSum = 0._rkind ! keep track of the portion of the time step that is completed - nSubsteps = 0 - - ! loop through substeps - ! NOTE: continuous do statement with exit clause - substeps: do - - ! initialize error control - err=0; message='varSubstepSundials/' - - ! ----- - ! * populate state vectors... - ! --------------------------- - - ! initialize state vectors - call popStateVec(& - ! input - nState, & ! intent(in): number of desired state variables - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output - stateVecInit, & ! intent(out): initial model state vector (mixed units) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - ! ----- - ! * iterative solution... - ! ----------------------- - ! solve the system of equations for a given state subset - call systemSolvSundials(& - ! input: model control - dtSubstep, & ! intent(in): time step (s) - dtInit, & ! intent(in): entire time step (s), right now same as dtSubstep but might change with operator splitting - nState, & ! intent(in): total number of state variables - firstSubStep, & ! intent(in): flag to denote first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation - scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution - ! input/output: data structures - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): index data - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_temp, & ! intent(inout): model fluxes for a local HRU - bvar_data, & ! intent(in): model variables for the local basin - model_decisions, & ! intent(in): model decisions - stateVecInit, & ! intent(in): initial state vector - ! output: model control - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - stateVecTrial, & ! intent(out): updated state vector - stateVecPrime, & ! intent(out): updated state vector - reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step - tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt - err,cmessage) ! intent(out): error code and error message - - if(err/=0)then - message=trim(message)//trim(cmessage) - if(err>0) return - endif - - ! set untapped melt energy to zero - untappedMelt(:) = 0._rkind - - ! if too much melt or need to reduce length of the coupled step then return - ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry - if(tooMuchMelt .or. reduceCoupledStep) return - - ! identify failure, this should not happen because will be fixed inside Sundials - failedSubstep = (err<0) - - ! reduce step based on failure - if(failedSubstep)then - err=0; message='varSubstepSundials/' ! recover from failed convergence - dtMultiplier = 0.5_rkind ! system failure: step halving - else - - endif ! switch between failure and success - - ! check if we failed the substep - if(failedSubstep)then - - ! check that the substep is greater than the minimum step - if(dtSubstep*dtMultiplier exit, and either (1) try another solution method; or (2) reduce coupled step - failedMinimumStep=.true. - exit subSteps - - else ! step is still OK - dtSubstep = dtSubstep*dtMultiplier - cycle subSteps - endif ! if step is less than the minimum - - endif ! if failed the substep - - ! ----- - ! * update model fluxes... - ! ------------------------ - - ! NOTE: if we get to here then we are accepting the step of dtSubstep - if(err/=0)then - message=trim(message)//'expect err=0 if updating fluxes' - return - endif - - ! identify the need to check the mass balance - checkMassBalance = .false. !do not check for Sundials - checkNrgBalance = .false. !do not check for Sundials, also only check if ixHowHeatCap == enthalpyFD - - ! update prognostic variables - call updateProgSundials(dtSubstep,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control - lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures - waterBalanceError,nrgFluxModified,err,cmessage) ! output: flags and error control - if(err/=0)then - message=trim(message)//trim(cmessage) - if(err>0) return - endif - - ! if water balance error then reduce the length of the coupled step - if(waterBalanceError)then - message=trim(message)//'water balance error' - reduceCoupledStep=.true. - err=-20; return - endif - - if(globalPrintFlag)& - print*, trim(cmessage)//': dt = ', dtSubstep - - ! recover from errors in prognostic update - if(err<0)then - - ! modify step - err=0 ! error recovery - dtSubstep = dtSubstep/2._rkind - - ! check minimum: fail minimum step if there is an error in the update - if(dtSubstep0) then - ! scalar compression - if(.not.scalarSolution .or. iStateSplit==nSoil)& - sumSoilCompress = sumSoilCompress + dtSubstep*diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression - ! vector compression - do iSoil=1,nSoil - if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& - sumLayerCompress(iSoil) = sumLayerCompress(iSoil) + dtSubstep*diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) ! soil compression in layers - end do - endif - - ! print progress - if(globalPrintFlag)& - write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt - - ! increment fluxes - dt_wght = 1._qp !dtSubstep/dt ! (define weight applied to each splitting operation) - do iVar=1,size(flux_meta) - if(count(fluxMask%var(iVar)%dat)>0) then - - ! ** no domain splitting - if(count(ixLayerActive/=integerMissing)==nLayers)then - flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght - fluxCount%var(iVar)%dat(:) = fluxCount%var(iVar)%dat(:) + 1 - - ! ** domain splitting - else - ixMin=lbound(flux_data%var(iVar)%dat) - ixMax=ubound(flux_data%var(iVar)%dat) - do ixLayer=ixMin(1),ixMax(1) - if(fluxMask%var(iVar)%dat(ixLayer)) then - flux_data%var(iVar)%dat(ixLayer) = flux_data%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght - fluxCount%var(iVar)%dat(ixLayer) = fluxCount%var(iVar)%dat(ixLayer) + 1 - endif - end do - endif ! (domain splitting) - - endif ! (if the flux is desired) - end do ! (loop through fluxes) - - ! increment the number of substeps - nSubsteps = nSubsteps+1 - - ! increment the sub-step legth - dtSum = dtSum + dtSubstep - - ! check that we have completed the sub-step - if(dtSum >= dt-verySmall)then - failedMinimumStep=.false. - exit subSteps - endif - - ! adjust length of the sub-step (make sure that we don't exceed the step) - dtSubstep = min(dt - dtSum, max(dtSubstep*dtMultiplier, dt_min) ) - - end do substeps ! time steps for variable-dependent sub-stepping - ! NOTE: if we get to here then we are accepting then dtSum should dt - - ! save the energy fluxes - flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt ! canopy evaporation/condensation (kg m-2 s-1) - flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt ! sensible heat flux from the canopy to the canopy air space (W m-2) - - ! save the soil compression diagnostics - diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress - do iSoil=1,nSoil - if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& - diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) = sumLayerCompress(iSoil) - end do - deallocate(sumLayerCompress) - - ! end associate statements - end associate globalVars - - ! update error codes - if(failedMinimumStep)then - err=-20 ! negative = recoverable error - message=trim(message)//'failed minimum step' - endif -end subroutine varSubstepSundials - - -! ********************************************************************************************************** -! private subroutine updateProgSundials: update prognostic variables -! ********************************************************************************************************** -subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control - lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures - waterBalanceError,nrgFluxModified,err,message) ! output: flags and error control - USE getVectorz_module,only:varExtract ! extract variables from the state vector - USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables - USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy - implicit none - ! model control - real(rkind) ,intent(in) :: dt ! time step (s) - integer(i4b) ,intent(in) :: nSnow ! number of snow layers - integer(i4b) ,intent(in) :: nSoil ! number of soil layers - integer(i4b) ,intent(in) :: nLayers ! total number of layers - logical(lgt) ,intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature - logical(lgt) ,intent(in) :: computeVegFlux ! flag to compute the vegetation flux - real(rkind) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) - real(rkind) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) - real(rkind) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) - logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance - logical(lgt) ,intent(in) :: checkNrgBalance ! flag to check the energy balance - ! data structures - type(zLookup),intent(in) :: lookup_data ! lookup tables - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! indices for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! flags and error control - logical(lgt) ,intent(out) :: waterBalanceError ! flag to denote that there is a water balance error - logical(lgt) ,intent(out) :: nrgFluxModified ! flag to denote that the energy fluxes were modified - integer(i4b) ,intent(out) :: err ! error code - character(*) ,intent(out) :: message ! error message - ! ================================================================================================================== - ! general - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: ixSubset ! index within the state subset - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixControlIndex ! index within a given domain - real(rkind) :: volMelt ! volumetric melt (kg m-3) - real(rkind),parameter :: verySmall=epsilon(1._rkind)*2._rkind ! a very small number (deal with precision issues) - ! mass balance - real(rkind) :: canopyBalance0,canopyBalance1 ! canopy storage at start/end of time step - real(rkind) :: soilBalance0,soilBalance1 ! soil storage at start/end of time step - real(rkind) :: vertFlux ! change in storage due to vertical fluxes - real(rkind) :: tranSink,baseSink,compSink ! change in storage due to sink terms - real(rkind) :: liqError ! water balance error - real(rkind) :: fluxNet ! net water fluxes (kg m-2 s-1) - real(rkind) :: superflousWat ! superflous water used for evaporation (kg m-2 s-1) - real(rkind) :: superflousNrg ! superflous energy that cannot be used for evaporation (W m-2 [J m-2 s-1]) - character(LEN=256) :: cmessage ! error message of downwind routine - ! trial state variables - real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial vector for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector for liquid water matric potential (m) - real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) - ! diagnostic variables - real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric fraction of ice (-) - ! derivative of state variables - real(rkind) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatPrime ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempPrime ! trial vector for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! trial vector for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! trial vector for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! trial vector for liquid water matric potential (m) - real(rkind) :: scalarAquiferStoragePrime ! trial value for storage of water in the aquifer (m) - ! diagnostic variables - real(rkind) :: scalarCanopyLiqPrime ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector for volumetric fraction of ice (-) - real(rkind) :: scalarCanairEnthalpyTrial ! enthalpy of the canopy air space (J m-3) - real(rkind) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! enthalpy of snow + soil (J m-3) - ! enthalpy derivatives - real(rkind) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature - real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state - real(rkind),dimension(nLayers) :: dEnthalpy_dTk ! derivatives in layer enthalpy w.r.t. temperature - real(rkind),dimension(nLayers) :: dEnthalpy_dWat ! derivatives in layer enthalpy w.r.t. water state - ! ------------------------------------------------------------------------------------------------------------------- - - ! ------------------------------------------------------------------------------------------------------------------- - ! point to flux variables in the data structure - associate(& - ! get indices for mass balance - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in) : [i4b] index of canopy hydrology state variable (mass) - ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the soil domain - ! get indices for the un-tapped melt - ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in) : [i4b(:)] list of indices for all energy states - ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in) : [i4b(:)] indices defining the domain of the state (iname_veg, iname_snow, iname_soil) - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in) : [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in) : [i4b(:)] [state subset] list of indices of the full state vector in the state subset - ! water fluxes - scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in) : [dp] rainfall rate (kg m-2 s-1) - scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(in) : [dp] rain reaches ground without touching the canopy (kg m-2 s-1) - scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(in) : [dp] canopy evaporation/condensation (kg m-2 s-1) - scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(in) : [dp] canopy transpiration (kg m-2 s-1) - scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(in) : [dp] drainage liquid water from vegetation canopy (kg m-2 s-1) - iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(in) : [dp(0:)] vertical liquid water flux at soil layer interfaces (-) - iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(in) : - mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) - mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(in) : [dp(:)] transpiration loss from each soil layer (m s-1) - mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(in) : [dp(:)] baseflow from each soil layer (m s-1) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in) : [dp(:)] change in storage associated with compression of the soil matrix (-) - scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the vegetation canopy (kg m-2 s-1) - scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the snow surface (kg m-2 s-1) - ! energy fluxes - scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ,& ! intent(in) : [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ,& ! intent(in) : [dp] sensible heat flux from the canopy to the canopy air space (W m-2) - ! domain depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in) : [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in) : [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! model state variables (vegetation canopy) - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout) : [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout) : [dp] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout) : [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout) : [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout) : [dp(:)] matric head (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(inout) : [dp(:)] matric potential of liquid water (m) - ! enthalpy - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(inout): [dp(:)] enthalpy of the snow+soil layers (J m-3) - ! derivatives, diagnositic for enthalpy - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) - ! model state variables (aquifer) - scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(inout) : [dp(:)] storage of water in the aquifer (m) - ! error tolerance - absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1) & ! intent(in) : [dp] absolute convergence tolerance for vol frac liq water (-) - ) ! associating flux variables in the data structure - ! ------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='updateProgSundials/' - - ! initialize water balancmLayerVolFracWatTrial error - waterBalanceError=.false. - - ! get storage at the start of the step - canopyBalance0 = merge(scalarCanopyLiq + scalarCanopyIce, realMissing, computeVegFlux) - soilBalance0 = sum( (mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) - - ! ----- - ! * update states... - ! ------------------ - - ! initialize to state variable from the last update - scalarCanairTempTrial = scalarCanairTemp - scalarCanopyTempTrial = scalarCanopyTemp - scalarCanopyWatTrial = scalarCanopyWat - scalarCanopyLiqTrial = scalarCanopyLiq - scalarCanopyIceTrial = scalarCanopyIce - mLayerTempTrial = mLayerTemp - mLayerVolFracWatTrial = mLayerVolFracWat - mLayerVolFracLiqTrial = mLayerVolFracLiq - mLayerVolFracIceTrial = mLayerVolFracIce - mLayerMatricHeadTrial = mLayerMatricHead - mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq - scalarAquiferStorageTrial = scalarAquiferStorage - - ! extract states from the state vector - call varExtract(& - ! input - stateVecTrial, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(inout): trial value of storage of water in the aquifer (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! initialize to state variable from the last update - ! should all be set to previous values if splits, but for now operator splitting is not hooked up - scalarCanairTempPrime = realMissing - scalarCanopyTempPrime = realMissing - scalarCanopyWatPrime = realMissing - scalarCanopyLiqPrime = realMissing - scalarCanopyIcePrime = realMissing - mLayerTempPrime = realMissing - mLayerVolFracWatPrime = realMissing - mLayerVolFracLiqPrime = realMissing - mLayerVolFracIcePrime = realMissing - mLayerMatricHeadPrime = realMissing - mLayerMatricHeadLiqPrime = realMissing - scalarAquiferStoragePrime = realMissing - - call varExtract(& - ! input - stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy - scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K) - scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): derivative of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K) - mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (-) - mLayerMatricHeadPrime, & ! intent(inout): derivative of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): derivative of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStoragePrime,& ! intent(inout): derivative of storage of water in the aquifer (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! update diagnostic variables - call updateVarsSundials(& - ! input - dt, & - .false., & ! intent(in): logical flag if computing Jacobian for Sundials solver - doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - mLayerVolFracWatTrial, & ! intent(in): use current vector for prev vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): use current vector for prev vector of total water matric potential (m) - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! - mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) - mLayerVolFracIcePrime, & ! - mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! ---- - ! * check energy balance - !------------------------ - if(checkNrgBalance)then - ! compute enthalpy at t_{n+1} - call t2enthalpy(& - .true., & ! intent(in): logical flag to include phase change in enthalpy - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) - ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - ! output: enthalpy - scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) - dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature - dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state - dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature - dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - endif - - ! ----- - ! * check mass balance... - ! ----------------------- - - ! NOTE: should not need to do this, since mass balance is checked in the solver - ! for sundials will not work since fluxes are instantaneous - if(checkMassBalance)then - - ! check mass balance for the canopy - if(ixVegHyd/=integerMissing)then - - ! handle cases where fluxes empty the canopy - fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage - if(-fluxNet*dt > canopyBalance0)then - - ! --> first add water - canopyBalance1 = canopyBalance0 + (scalarRainfall - scalarThroughfallRain)*dt - - ! --> next, remove canopy evaporation -- put the unsatisfied evap into sensible heat - canopyBalance1 = canopyBalance1 + scalarCanopyEvaporation*dt - if(canopyBalance1 < 0._rkind)then - ! * get superfluous water and energy - superflousWat = -canopyBalance1/dt ! kg m-2 s-1 - superflousNrg = superflousWat*LH_vap ! W m-2 (J m-2 s-1) - ! * update fluxes and states - canopyBalance1 = 0._rkind - scalarCanopyEvaporation = scalarCanopyEvaporation + superflousWat - scalarLatHeatCanopyEvap = scalarLatHeatCanopyEvap + superflousNrg - scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg - endif - - ! --> next, remove canopy drainage - canopyBalance1 = canopyBalance1 -scalarCanopyLiqDrainage*dt - if(canopyBalance1 < 0._rkind)then - superflousWat = -canopyBalance1/dt ! kg m-2 s-1 - canopyBalance1 = 0._rkind - scalarCanopyLiqDrainage = scalarCanopyLiqDrainage + superflousWat - endif - - ! update the trial state - scalarCanopyWatTrial = canopyBalance1 - - ! set the modification flag - nrgFluxModified = .true. - - else - canopyBalance1 = canopyBalance0 + fluxNet*dt - nrgFluxModified = .false. - endif ! cases where fluxes empty the canopy - - ! check the mass balance - fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage - liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial - if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues - write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 - write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 - write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt - write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt - write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt - write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - write(*,'(a,1x,f20.10)') 'liqError = ', liqError - waterBalanceError = .true. - return - endif ! if there is a water balance error - endif ! if veg canopy - - ! check mass balance for soil - ! NOTE: fatal errors, though possible to recover using negative error codes - if(count(ixSoilOnlyHyd/=integerMissing)==nSoil)then - soilBalance1 = sum( (mLayerVolFracLiqTrial(nSnow+1:nLayers) + mLayerVolFracIceTrial(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) - vertFlux = -(iLayerLiqFluxSoil(nSoil) - iLayerLiqFluxSoil(0))*dt ! m s-1 --> m - tranSink = sum(mLayerTranspire)*dt ! m s-1 --> m - baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m - compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m - liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) - if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues - write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 - write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 - write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux - write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink - write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink - write(*,'(a,1x,f20.10)') 'compSink = ', compSink - write(*,'(a,1x,f20.10)') 'liqError = ', liqError - waterBalanceError = .true. - return - endif ! if there is a water balance error - endif ! if hydrology states exist in the soil domain - endif ! if checking the mass balance - - ! ----- - ! * remove untapped melt energy... - ! -------------------------------- - - ! only work with energy state variables - if(size(ixNrgOnly)>0)then ! energy state variables exist - - ! loop through energy state variables - do iState=1,size(ixNrgOnly) - - ! get index of the control volume within the domain - ixSubset = ixNrgOnly(iState) ! index within the state subset - ixFullVector = ixMapSubset2Full(ixSubset) ! index within full state vector - ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain - - ! compute volumetric melt (kg m-3) - volMelt = dt*untappedMelt(ixSubset)/LH_fus ! (kg m-3) - - ! update ice content - select case( ixDomainType(ixFullVector) ) - case(iname_cas); cycle ! do nothing, since there is no snow stored in the canopy air space - case(iname_veg); scalarCanopyIceTrial = scalarCanopyIceTrial - volMelt*canopyDepth ! (kg m-2) - case(iname_snow); mLayerVolFracIceTrial(ixControlIndex) = mLayerVolFracIceTrial(ixControlIndex) - volMelt/iden_ice ! (-) - case(iname_soil); mLayerVolFracIceTrial(ixControlIndex+nSnow) = mLayerVolFracIceTrial(ixControlIndex+nSnow) - volMelt/iden_water ! (-) - case default; err=20; message=trim(message)//'unable to identify domain type [remove untapped melt energy]'; return - end select - - ! update liquid water content - select case( ixDomainType(ixFullVector) ) - case(iname_cas); cycle ! do nothing, since there is no snow stored in the canopy air space - case(iname_veg); scalarCanopyLiqTrial = scalarCanopyLiqTrial + volMelt*canopyDepth ! (kg m-2) - case(iname_snow); mLayerVolFracLiqTrial(ixControlIndex) = mLayerVolFracLiqTrial(ixControlIndex) + volMelt/iden_water ! (-) - case(iname_soil); mLayerVolFracLiqTrial(ixControlIndex+nSnow) = mLayerVolFracLiqTrial(ixControlIndex+nSnow) + volMelt/iden_water ! (-) - case default; err=20; message=trim(message)//'unable to identify domain type [remove untapped melt energy]'; return - end select - - end do ! looping through energy variables - - ! ======================================================================================================== - - ! *** ice - - ! --> check if we removed too much water - if(scalarCanopyIceTrial < 0._rkind .or. any(mLayerVolFracIceTrial < 0._rkind) )then - - ! ** - ! canopy within numerical precision - if(scalarCanopyIceTrial < 0._rkind)then - - if(scalarCanopyIceTrial > -verySmall)then - scalarCanopyLiqTrial = scalarCanopyLiqTrial - scalarCanopyIceTrial - scalarCanopyIceTrial = 0._rkind - - ! encountered an inconsistency: spit the dummy - else - print*, 'dt = ', dt - print*, 'untappedMelt = ', untappedMelt - print*, 'untappedMelt*dt = ', untappedMelt*dt - print*, 'scalarCanopyiceTrial = ', scalarCanopyIceTrial - message=trim(message)//'melted more than the available water' - err=20; return - endif ! (inconsistency) - - endif ! if checking the canopy - ! ** - ! snow+soil within numerical precision - do iState=1,size(mLayerVolFracIceTrial) - - ! snow layer within numerical precision - if(mLayerVolFracIceTrial(iState) < 0._rkind)then - - if(mLayerVolFracIceTrial(iState) > -verySmall)then - mLayerVolFracLiqTrial(iState) = mLayerVolFracLiqTrial(iState) - mLayerVolFracIceTrial(iState) - mLayerVolFracIceTrial(iState) = 0._rkind - - ! encountered an inconsistency: spit the dummy - else - print*, 'dt = ', dt - print*, 'untappedMelt = ', untappedMelt - print*, 'untappedMelt*dt = ', untappedMelt*dt - print*, 'mLayerVolFracIceTrial = ', mLayerVolFracIceTrial - message=trim(message)//'melted more than the available water' - err=20; return - endif ! (inconsistency) - - endif ! if checking a snow layer - - end do ! (looping through state variables) - - endif ! (if we removed too much water) - - ! ======================================================================================================== - - ! *** liquid water - - ! --> check if we removed too much water - if(scalarCanopyLiqTrial < 0._rkind .or. any(mLayerVolFracLiqTrial < 0._rkind) )then - - ! ** - ! canopy within numerical precision - if(scalarCanopyLiqTrial < 0._rkind)then - - if(scalarCanopyLiqTrial > -verySmall)then - scalarCanopyIceTrial = scalarCanopyIceTrial - scalarCanopyLiqTrial - scalarCanopyLiqTrial = 0._rkind - - ! encountered an inconsistency: spit the dummy - else - print*, 'dt = ', dt - print*, 'untappedMelt = ', untappedMelt - print*, 'untappedMelt*dt = ', untappedMelt*dt - print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial - message=trim(message)//'frozen more than the available water' - err=20; return - endif ! (inconsistency) - endif ! checking the canopy - - ! ** - ! snow+soil within numerical precision - do iState=1,size(mLayerVolFracLiqTrial) - - ! snow layer within numerical precision - if(mLayerVolFracLiqTrial(iState) < 0._rkind)then - - if(mLayerVolFracLiqTrial(iState) > -verySmall)then - mLayerVolFracIceTrial(iState) = mLayerVolFracIceTrial(iState) - mLayerVolFracLiqTrial(iState) - mLayerVolFracLiqTrial(iState) = 0._rkind - - ! encountered an inconsistency: spit the dummy - else - print*, 'dt = ', dt - print*, 'untappedMelt = ', untappedMelt - print*, 'untappedMelt*dt = ', untappedMelt*dt - print*, 'mLayerVolFracLiqTrial = ', mLayerVolFracLiqTrial - message=trim(message)//'frozen more than the available water' - err=20; return - endif ! (inconsistency) - - endif ! checking a snow layer - - end do ! (looping through state variables) - - endif ! (if we removed too much water) - - endif ! (if energy state variables exist) - - ! ----- - ! * update enthalpy as a diagnostic variable... - ! -------------------------------- - scalarCanairEnthalpy = scalarCanairEnthalpyTrial - scalarCanopyEnthalpy = scalarCanopyEnthalpyTrial - mLayerEnthalpy = mLayerEnthalpyTrial - - ! ----- - ! * update prognostic variables... - ! -------------------------------- - ! update state variables for the vegetation canopy - scalarCanairTemp = scalarCanairTempTrial ! trial value of canopy air temperature (K) - scalarCanopyTemp = scalarCanopyTempTrial ! trial value of canopy temperature (K) - scalarCanopyWat = scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - scalarCanopyLiq = scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) - scalarCanopyIce = scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - - ! update state variables for the snow+soil domain - mLayerTemp = mLayerTempTrial ! trial vector of layer temperature (K) - mLayerVolFracWat = mLayerVolFracWatTrial ! trial vector of volumetric total water content (-) - mLayerVolFracLiq = mLayerVolFracLiqTrial ! trial vector of volumetric liquid water content (-) - mLayerVolFracIce = mLayerVolFracIceTrial ! trial vector of volumetric ice water content (-) - mLayerMatricHead = mLayerMatricHeadTrial ! trial vector of matric head (m) - mLayerMatricHeadLiq = mLayerMatricHeadLiqTrial ! trial vector of matric head (m) - - ! update state variables for the aquifer - scalarAquiferStorage = scalarAquiferStorageTrial - - ! end associations to info in the data structures - end associate - -end subroutine updateProgSundials - -end module varSubstepSundials_module From 123772a05584cd8f411f9a6344b165fb7bd81120 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 8 Feb 2023 15:06:35 -0600 Subject: [PATCH 0535/1472] fix the comments --- build/source/engine/opSplittin.f90 | 2 +- build/source/engine/updateVarsSundials.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index d2e942a8b..63258a929 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -160,7 +160,7 @@ module opSplittin_module ! (1) Attempt different solutions in the following order: (a) fully coupled; (b) split by state type and by ! domain type for a given energy and mass split (vegetation, snow, and soil); and (c) scalar solution ! for a given state type and domain subset. -! (2) For a given split, compute a variable number of substeps (in varSubstepSundials). +! (2) For a given split, compute a variable number of substeps (in varSubstep). ! ********************************************************************************************************** subroutine opSplittin(& ! input: model control diff --git a/build/source/engine/updateVarsSundials.f90 b/build/source/engine/updateVarsSundials.f90 index 35b026873..6301202bb 100644 --- a/build/source/engine/updateVarsSundials.f90 +++ b/build/source/engine/updateVarsSundials.f90 @@ -610,7 +610,7 @@ subroutine updateVarsSundials(& ! ------------------------ ! check the need to adjust temperature (will always be false if inside solver) - ! can be true if inside varSubstepSundials, outside solver, but currently will not work so turn off always and not sure should ever do this + ! can be true if inside varSubstep, outside solver, but currently will not work so turn off always and not sure should ever do this if(do_adjustTemp .and. computJac)then ! get the melt energy From e537c091c9c99ab403146cccb7290716adb89152 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 8 Feb 2023 23:29:00 -0600 Subject: [PATCH 0536/1472] trying to control loops of operator splitting, does not run well --- build/source/engine/coupled_em.f90 | 438 +++++++++++---------- build/source/engine/eval8summa.f90 | 4 +- build/source/engine/eval8summaSundials.f90 | 4 +- build/source/engine/opSplittin.f90 | 8 +- build/source/engine/summaSolve.f90 | 4 +- build/source/engine/systemSolv.f90 | 6 +- build/source/engine/varSubstep.f90 | 43 +- 7 files changed, 275 insertions(+), 232 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 61474f1f1..f86bd15e5 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -179,6 +179,7 @@ subroutine coupled_em(& real(rkind) :: dt_solv ! seconds in the data step that have been completed real(rkind) :: dtMultiplier ! time step multiplier (-) based on what happenned in "opSplittin" real(rkind) :: minstep,maxstep ! minimum and maximum time step length (seconds) + real(rkind) :: maxstep_op ! maximum time step length (seconds) to run opSplitting over integer(i4b) :: nsub ! number of substeps logical(lgt) :: computeVegFluxOld ! flag to indicate if we are computing fluxes over vegetation on the previous sub step logical(lgt) :: includeAquifer ! flag to denote that an aquifer is included @@ -240,6 +241,11 @@ subroutine coupled_em(& ! timing information real(rkind) :: startTime ! start time (used to compute wall clock time) real(rkind) :: endTime ! end time (used to compute wall clock time) + ! outer loop control + logical(lgt) :: firstInnerStep ! flag to denote if the first time step in maxstep subStep + logical(lgt) :: lastInnerStep ! flag to denote if the last time step in maxstep subStep + logical(lgt) :: do_outer ! flag to denote if doing the outer steps surrounding the call to sopSplitting + real(rkind) :: dt_solvInner ! seconds in the maxstep subStep that have been completed ! ---------------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -281,8 +287,10 @@ subroutine coupled_em(& modifiedLayers = .false. ! flag to denote that snow layers were modified modifiedVegState = .false. ! flag to denote that vegetation states were modified - ! define the first step + ! define the first step and first and last inner steps firstSubStep = .true. + firstInnerStep = .true. + lastInnerStep = .false. ! count the number of snow and soil layers ! NOTE: need to re-compute the number of snow and soil layers at the start of each sub-step because the number of layers may change @@ -363,9 +371,11 @@ subroutine coupled_em(& ! short-cut to the algorithmic control parameters ! NOTE - temporary assignment of minstep to foce something reasonable - ! change maxstep with hard code here to make only the outer loop computations here in coupled_em happen more frequently for num_method = bEuler or sundials + ! change maxstep with hard code here to make the outer and inner loop computations here in coupled_em happen more frequently for num_method = bEuler or sundials + ! change maxstep_op with hard code here to make the inner loop computations in opSplitting happen more frequently for num_method = bEuler or sundials minstep = 10._rkind ! mpar_data%var(iLookPARAM%minstep)%dat(1) ! minimum time step (s) maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) + maxstep_op = 900._rkind !mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) to run opSplitting over ! compute the number of layers with roots nLayersRoots = count(prog_data%var(iLookPROG%iLayerHeight)%dat(nSnow:nLayers-1) < mpar_data%var(iLookPARAM%rootingDepth)%dat(1)-verySmall) @@ -551,8 +561,9 @@ subroutine coupled_em(& ! **************************************************************************************************** ! initialize the length of the sub-step - dt_solv = 0._rkind ! length of time step that has been completed (s) - dt_init = min(data_step,maxstep) ! initial substep length (s) + dt_solv = 0._rkind ! length of time step that has been completed (s) + dt_solvInner = 0._rkind ! length of time step that has been completed (s) in maxstep subStep + dt_init = min(data_step,maxstep,maxstep_op) ! initial substep length (s) dt_sub = dt_init ! length of substep dtSave = dt_init ! length of substep @@ -613,107 +624,114 @@ subroutine coupled_em(& nSoil = count(indx_data%var(iLookINDEX%layerType)%dat==iname_soil) nLayers = nSnow+nSoil + ! check outer + do_outer = .true. + if (maxstep_op < maxstep .and. .not.(firstInnerStep .or. stepFailure)) do_outer = .false. + ! *** merge/sub-divide snow layers... ! ----------------------------------- - call volicePack(& - ! input/output: model data structures - doLayerMerge, & ! intent(in): flag to force merge of snow layers - model_decisions, & ! intent(in): model decisions - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): type of each layer - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output - modifiedLayers, & ! intent(out): flag to denote that layers were modified - err,cmessage) ! intent(out): error control - if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if - - ! save the number of snow and soil layers - nSnow = indx_data%var(iLookINDEX%nSnow)%dat(1) - nSoil = indx_data%var(iLookINDEX%nSoil)%dat(1) - nLayers = indx_data%var(iLookINDEX%nLayers)%dat(1) - - ! compute the indices for the model state variables - if(firstSubStep .or. modifiedVegState .or. modifiedLayers)then - call indexState(computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - includeAquifer, & ! intent(in): flag to denote if included the aquifer - nSnow,nSoil,nLayers, & ! intent(in): number of snow and soil layers, and total number of layers - indx_data, & ! intent(inout): indices defining model states and layers - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - end if - - ! recreate the temporary data structures - ! NOTE: resizeData(meta, old, new, ..) - if(modifiedVegState .or. modifiedLayers)then - - ! create temporary data structures for prognostic variables - call resizeData(prog_meta(:),prog_data,prog_temp,copy=.true.,err=err,message=cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! create temporary data structures for diagnostic variables - call resizeData(diag_meta(:),diag_data,diag_temp,copy=.true.,err=err,message=cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! create temporary data structures for index variables - call resizeData(indx_meta(:),indx_data,indx_temp,copy=.true.,err=err,message=cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - do iVar=1,size(indx_data%var) - select case(stepFailure) - case(.false.); indx_temp%var(iVar)%dat(:) = indx_data%var(iVar)%dat(:) - case(.true.); indx_data%var(iVar)%dat(:) = indx_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - endif ! if modified the states - - ! define the number of state variables - nState = indx_data%var(iLookINDEX%nState)%dat(1) - - ! *** compute diagnostic variables for each layer... - ! -------------------------------------------------- - ! NOTE: this needs to be done AFTER volicePack, since layers may have been sub-divided and/or merged - call diagn_evar(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): canopy depth (m) - ! input/output: data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if - - - ! *** compute melt of the "snow without a layer"... - ! ------------------------------------------------- - ! NOTE: forms a surface melt pond, which drains into the upper-most soil layer through the time step - ! (check for the special case of "snow without a layer") - if(nSnow==0)then - call implctMelt(& - ! input/output: integrated snowpack properties - prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(inout): snow water equivalent (kg m-2) - prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(inout): snow depth (m) - prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1), & ! intent(inout): surface melt pond (kg m-2) - ! input/output: properties of the upper-most soil layer - prog_data%var(iLookPROG%mLayerTemp)%dat(nSnow+1), & ! intent(inout): surface layer temperature (K) - prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1), & ! intent(inout): surface layer depth (m) - diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat(nSnow+1),& ! intent(inout): surface layer volumetric heat capacity (J m-3 K-1) - ! output: error control - err,cmessage ) ! intent(out): error control - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - - end if + if(do_outer)then + call volicePack(& + ! input/output: model data structures + doLayerMerge, & ! intent(in): flag to force merge of snow layers + model_decisions, & ! intent(in): model decisions + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): type of each layer + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output + modifiedLayers, & ! intent(out): flag to denote that layers were modified + err,cmessage) ! intent(out): error control + if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if + + ! save the number of snow and soil layers + nSnow = indx_data%var(iLookINDEX%nSnow)%dat(1) + nSoil = indx_data%var(iLookINDEX%nSoil)%dat(1) + nLayers = indx_data%var(iLookINDEX%nLayers)%dat(1) + + ! compute the indices for the model state variables + if(firstSubStep .or. modifiedVegState .or. modifiedLayers)then + call indexState(computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + includeAquifer, & ! intent(in): flag to denote if included the aquifer + nSnow,nSoil,nLayers, & ! intent(in): number of snow and soil layers, and total number of layers + indx_data, & ! intent(inout): indices defining model states and layers + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + end if + + ! recreate the temporary data structures + ! NOTE: resizeData(meta, old, new, ..) + if(modifiedVegState .or. modifiedLayers)then + + ! create temporary data structures for prognostic variables + call resizeData(prog_meta(:),prog_data,prog_temp,copy=.true.,err=err,message=cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! create temporary data structures for diagnostic variables + call resizeData(diag_meta(:),diag_data,diag_temp,copy=.true.,err=err,message=cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! create temporary data structures for index variables + call resizeData(indx_meta(:),indx_data,indx_temp,copy=.true.,err=err,message=cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + do iVar=1,size(indx_data%var) + select case(stepFailure) + case(.false.); indx_temp%var(iVar)%dat(:) = indx_data%var(iVar)%dat(:) + case(.true.); indx_data%var(iVar)%dat(:) = indx_temp%var(iVar)%dat(:) + end select + end do ! looping through variables + + endif ! if modified the states + + ! define the number of state variables + nState = indx_data%var(iLookINDEX%nState)%dat(1) + + ! *** compute diagnostic variables for each layer... + ! -------------------------------------------------- + ! NOTE: this needs to be done AFTER volicePack, since layers may have been sub-divided and/or merged + call diagn_evar(& + ! input: control variables + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): canopy depth (m) + ! input/output: data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if + + ! *** compute melt of the "snow without a layer"... + ! ------------------------------------------------- + ! NOTE: forms a surface melt pond, which drains into the upper-most soil layer through the time step + ! (check for the special case of "snow without a layer") + ! this pond melts evenly over entire time of maxstep until it gets recomputed because based on SWE when computed + if(nSnow==0) then + call implctMelt(& + ! input/output: integrated snowpack properties + prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(inout): snow water equivalent (kg m-2) + prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(inout): snow depth (m) + prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1), & ! intent(inout): surface melt pond (kg m-2) + ! input/output: properties of the upper-most soil layer + prog_data%var(iLookPROG%mLayerTemp)%dat(nSnow+1), & ! intent(inout): surface layer temperature (K) + prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1), & ! intent(inout): surface layer depth (m) + diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat(nSnow+1),& ! intent(inout): surface layer volumetric heat capacity (J m-3 K-1) + ! output: error control + err,cmessage ) ! intent(out): error control + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + endif + + endif ! (do_outer loop) ! *** solve model equations... ! ---------------------------- ! save input step dtSave = dt_sub + ! get the new solution call opSplittin(& ! input: model control @@ -722,7 +740,9 @@ subroutine coupled_em(& nLayers, & ! intent(in): total number of layers nState, & ! intent(in): total number of layers dt_sub, & ! intent(inout): length of the model sub-step + maxstep, & ! intent(in): length drainage pond drains in (nsub==1), & ! intent(in): logical flag to denote the first substep + firstInnerStep, & ! intent(in): flag to denote if the first time step in maxstep subStep computeVegFlux, & ! intent(in): logical flag to compute fluxes within the vegetation canopy ! input/output: data structures type_data, & ! intent(in): type of vegetation and soil @@ -770,116 +790,139 @@ subroutine coupled_em(& cycle substeps endif - ! update first step + ! update first step and first and last inner steps firstSubStep = .false. + firstInnerStep = .false. + if(dt_solvInner + dt_sub >= maxstep) lastInnerStep = .true. + ! increment sub-step + dt_solvInner = dt_solvInner + dt_sub + + ! check outer + do_outer = .true. + if(maxstep_op < maxstep .and. .not.lastInnerStep) do_outer = .false. ! *** remove ice due to sublimation... ! -------------------------------------------------------------- - sublime: associate(& - scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1), & ! sublimation from the vegetation canopy (kg m-2 s-1) - scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! sublimation from the snow surface (kg m-2 s-1) - scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1), & ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1), & ! sensible heat flux from the canopy to the canopy air space (W m-2) - scalarLatHeatGround => flux_data%var(iLookFLUX%scalarLatHeatGround)%dat(1), & ! latent heat flux from ground surface below vegetation (W m-2) - scalarSenHeatGround => flux_data%var(iLookFLUX%scalarSenHeatGround)%dat(1), & ! sensible heat flux from ground surface below vegetation (W m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1), & ! liquid water stored on the vegetation canopy (kg m-2) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! ice stored on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! canopy ice content (kg m-2) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat, & ! volumetric fraction of ice in the snow+soil domain (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat, & ! volumetric fraction of liquid water in the snow+soil domain (-) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat & ! depth of each snow+soil layer (m) - ) ! associations to variables in data structures - - ! * compute change in canopy ice content due to sublimation... - ! ------------------------------------------------------------ - if(computeVegFlux)then - - ! remove mass of ice on the canopy - scalarCanopyIce = scalarCanopyIce + scalarCanopySublimation*dt_sub - - ! if removed all ice, take the remaining sublimation from water - if(scalarCanopyIce < 0._rkind)then - scalarCanopyLiq = scalarCanopyLiq + scalarCanopyIce - scalarCanopyIce = 0._rkind + if(do_outer)then + sublime: associate(& + scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1), & ! sublimation from the vegetation canopy (kg m-2 s-1) + scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! sublimation from the snow surface (kg m-2 s-1) + scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1), & ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1), & ! sensible heat flux from the canopy to the canopy air space (W m-2) + scalarLatHeatGround => flux_data%var(iLookFLUX%scalarLatHeatGround)%dat(1), & ! latent heat flux from ground surface below vegetation (W m-2) + scalarSenHeatGround => flux_data%var(iLookFLUX%scalarSenHeatGround)%dat(1), & ! sensible heat flux from ground surface below vegetation (W m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1), & ! liquid water stored on the vegetation canopy (kg m-2) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! ice stored on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! canopy ice content (kg m-2) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat, & ! volumetric fraction of ice in the snow+soil domain (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat, & ! volumetric fraction of liquid water in the snow+soil domain (-) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat & ! depth of each snow+soil layer (m) + ) ! associations to variables in data structures + + ! * compute change in canopy ice content due to sublimation... + ! ------------------------------------------------------------ + if(computeVegFlux)then + + ! remove mass of ice on the canopy + scalarCanopyIce = scalarCanopyIce + scalarCanopySublimation*dt_sub + + ! if removed all ice, take the remaining sublimation from water + if(scalarCanopyIce < 0._rkind)then + scalarCanopyLiq = scalarCanopyLiq + scalarCanopyIce + scalarCanopyIce = 0._rkind + endif + + ! modify fluxes if there is insufficient canopy water to support the converged sublimation rate over the time step dt_sub + if(scalarCanopyLiq < 0._rkind)then + ! --> superfluous sublimation flux + superflousSub = -scalarCanopyLiq/dt_sub ! kg m-2 s-1 + superflousNrg = superflousSub*LH_sub ! W m-2 (J m-2 s-1) + ! --> update fluxes and states + scalarCanopySublimation = scalarCanopySublimation + superflousSub + scalarLatHeatCanopyEvap = scalarLatHeatCanopyEvap + superflousNrg + scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg + scalarCanopyLiq = 0._rkind + endif + + ! update water + scalarCanopyWat = scalarCanopyLiq + scalarCanopyIce + + end if ! (if computing the vegetation flux) + + call computSnowDepth(& + dt_sub, & ! intent(in) + nSnow, & ! intent(in) + scalarSnowSublimation, & ! intent(in) + mLayerVolFracLiq, & ! intent(inout) + mLayerVolFracIce, & ! intent(inout) + prog_data%var(iLookPROG%mLayerTemp)%dat, & ! intent(in) + diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat, & ! intent(in) + mpar_data, & ! intent(in) + ! output + tooMuchSublim, & ! intent(out): flag to denote that there was too much sublimation in a given time step + mLayerDepth, & ! intent(inout) + ! error control + err,message) ! intent(out): error control + if(err/=0)then; err=55; return; end if + + ! process the flag for too much sublimation + if(tooMuchSublim)then + stepFailure = .true. + doLayerMerge = .true. + else + doLayerMerge = .false. endif - ! modify fluxes if there is insufficient canopy water to support the converged sublimation rate over the time step dt_sub - if(scalarCanopyLiq < 0._rkind)then - ! --> superfluous sublimation flux - superflousSub = -scalarCanopyLiq/dt_sub ! kg m-2 s-1 - superflousNrg = superflousSub*LH_sub ! W m-2 (J m-2 s-1) - ! --> update fluxes and states - scalarCanopySublimation = scalarCanopySublimation + superflousSub - scalarLatHeatCanopyEvap = scalarLatHeatCanopyEvap + superflousNrg - scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg - scalarCanopyLiq = 0._rkind + ! handle special case of the step failure + ! NOTE: need to revert back to the previous state vector that we were happy with and reduce the time step + if(stepFailure)then + ! halve step + dt_sub = dtSave/2._rkind + ! check that the step is not tiny + if(dt_sub < minstep)then + print*,ixSolution + print*, 'dtSave, dt_sub', dtSave, dt_sub + message=trim(message)//'length of the coupled step is below the minimum step length' + err=20; return + endif + ! try again + cycle substeps endif - ! update water - scalarCanopyWat = scalarCanopyLiq + scalarCanopyIce - - end if ! (if computing the vegetation flux) - - call computSnowDepth(& - dt_sub, & ! intent(in) - nSnow, & ! intent(in) - scalarSnowSublimation, & ! intent(in) - mLayerVolFracLiq, & ! intent(inout) - mLayerVolFracIce, & ! intent(inout) - prog_data%var(iLookPROG%mLayerTemp)%dat, & ! intent(in) - diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat, & ! intent(in) - mpar_data, & ! intent(in) - ! output - tooMuchSublim, & ! intent(out): flag to denote that there was too much sublimation in a given time step - mLayerDepth, & ! intent(inout) - ! error control - err,message) ! intent(out): error control - if(err/=0)then; err=55; return; end if - - ! process the flag for too much sublimation - if(tooMuchSublim)then - stepFailure = .true. - doLayerMerge = .true. - else - doLayerMerge = .false. - endif + end associate sublime - ! handle special case of the step failure - ! NOTE: need to revert back to the previous state vector that we were happy with and reduce the time step - if(stepFailure)then - ! halve step - dt_sub = dtSave/2._rkind - ! check that the step is not tiny - if(dt_sub < minstep)then - print*,ixSolution - print*, 'dtSave, dt_sub', dtSave, dt_sub - message=trim(message)//'length of the coupled step is below the minimum step length' - err=20; return - endif - ! try again - cycle substeps + ! update coordinate variables + call calcHeight(& + ! input/output: data structures + indx_data, & ! intent(in): layer type + prog_data, & ! intent(inout): model variables for a local HRU + ! output: error control + err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + ! recompute snow depth, SWE, and layer water + if(nSnow > 0)then + prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow)) + prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow)*iden_water + & + prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice) & + * prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) + prog_data%var(iLookPROG%mLayerVolFracWat)%dat(1:nSnow) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow) & + + prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice/iden_water endif - end associate sublime + ! increment change in storage associated with the surface melt pond (kg m-2) + if(nSnow==0) sfcMeltPond = sfcMeltPond + prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) - ! update coordinate variables - call calcHeight(& - ! input/output: data structures - indx_data, & ! intent(in): layer type - prog_data, & ! intent(inout): model variables for a local HRU - ! output: error control - err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + ! update time increment and first and last inner steps + dt_solvInner = 0._rkind + firstInnerStep = .true. + lastInnerStep = .false. - ! recompute snow depth, SWE, and layer water - if(nSnow > 0)then - prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow)) - prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow)*iden_water + & - prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice) & - * prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) - prog_data%var(iLookPROG%mLayerVolFracWat)%dat(1:nSnow) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow) & - + prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice/iden_water - end if + endif ! (do_outer loop) + + ! **************************************************************************************************** + ! *** END MAIN SOLVER ******************************************************************************** + ! **************************************************************************************************** ! increment fluxes dt_wght = dt_sub/data_step ! define weight applied to each sub-step @@ -887,13 +930,6 @@ subroutine coupled_em(& flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:)*dt_wght end do - ! increment change in storage associated with the surface melt pond (kg m-2) - if(nSnow==0) sfcMeltPond = sfcMeltPond + prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) - - ! **************************************************************************************************** - ! *** END MAIN SOLVER ******************************************************************************** - ! **************************************************************************************************** - ! increment sub-step dt_solv = dt_solv + dt_sub diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 2c802cb0f..4cf2b0ac7 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -97,7 +97,7 @@ module eval8summa_module subroutine eval8summa(& ! input: model control dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): entire time step + dt, & ! intent(in): entire time step for drainage pond rate nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers @@ -153,7 +153,7 @@ subroutine eval8summa(& ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control real(rkind),intent(in) :: dt_cur ! current stepsize - real(rkind),intent(in) :: dt ! entire time step + real(rkind),intent(in) :: dt ! entire time step for drainage pond rate integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index 9cd07ea9e..2797d065a 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -80,7 +80,7 @@ module eval8summaSundials_module subroutine eval8summaSundials(& ! input: model control dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): entire time step + dt, & ! intent(in): entire time step for drainage pond rate nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers @@ -160,7 +160,7 @@ subroutine eval8summaSundials(& ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control real(rkind),intent(in) :: dt_cur ! current stepsize - real(rkind),intent(in) :: dt ! entire time step + real(rkind),intent(in) :: dt ! entire time step for drainage pond rate integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 63258a929..6eff23c7b 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -169,7 +169,9 @@ subroutine opSplittin(& nLayers, & ! intent(in): total number of layers nState, & ! intent(in): total number of state variables dt, & ! intent(inout): time step (s) + tdrainage, & ! intent(in): length drainage pond drains in firstSubStep, & ! intent(in): flag to denote first sub-step + firstInnerStep, & ! intent(in): flag to denote if the first time step in maxstep subStep computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation ! input/output: data structures type_data, & ! intent(in): type of vegetation and soil @@ -210,6 +212,7 @@ subroutine opSplittin(& integer(i4b),intent(in) :: nLayers ! total number of layers integer(i4b),intent(in) :: nState ! total number of state variables real(rkind),intent(inout) :: dt ! time step (seconds) + real(rkind),intent(in) :: tdrainage ! length drainage pond drains in logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) ! input/output: data structures @@ -290,6 +293,7 @@ subroutine opSplittin(& logical(lgt) :: failedMinimumStep ! flag to denote failure of substepping for a given split integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) integer(i4b) :: nCoupling + logical(lgt) :: firstInnerStep ! flag to denote if the first time step in maxstep subStep ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- @@ -376,6 +380,7 @@ subroutine opSplittin(& ! initialize the first success call firstSuccess=.false. + if (.not.firstInnerStep) firstSuccess=.true. ! initialize the flags tooMuchMelt=.false. ! too much melt (merge snow layers) @@ -567,8 +572,6 @@ subroutine opSplittin(& ! avoid redundant case where vector solution is of length 1 if(ixSolution==vector .and. count(stateMask)==1) cycle solution - - ! ----- ! * assemble vectors for a given split... ! --------------------------------------- @@ -749,6 +752,7 @@ subroutine opSplittin(& dt, & ! intent(in) : time step (s) dtInit, & ! intent(in) : initial time step (seconds) dt_min, & ! intent(in) : minimum time step (seconds) + tdrainage, & ! intent(in) : length drainage pond drains in nSubset, & ! intent(in) : total number of variables in the state subset doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature firstSubStep, & ! intent(in) : flag to denote first sub-step diff --git a/build/source/engine/summaSolve.f90 b/build/source/engine/summaSolve.f90 index 3908f1f22..1e7329584 100644 --- a/build/source/engine/summaSolve.f90 +++ b/build/source/engine/summaSolve.f90 @@ -87,7 +87,7 @@ module summaSolve_module subroutine summaSolve(& ! input: model control dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): length of the entire time step (seconds) + dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate iter, & ! intent(in): iteration index nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -140,7 +140,7 @@ subroutine summaSolve(& ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control real(rkind),intent(in) :: dt_cur ! current stepsize - real(rkind),intent(in) :: dt ! entire time step + real(rkind),intent(in) :: dt ! entire time step for drainage pond rate integer(i4b),intent(in) :: iter ! interation index integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 549e12ebb..295eac229 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -162,7 +162,7 @@ subroutine systemSolv(& ! --------------------------------------------------------------------------------------- ! input: model control real(rkind),intent(in) :: dt_cur ! current stepsize - real(rkind),intent(in) :: dt ! entire time step + real(rkind),intent(in) :: dt ! entire time step for drainage pond rate integer(i4b),intent(in) :: nState ! total number of state variables logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call @@ -439,7 +439,7 @@ subroutine systemSolv(& call eval8summa(& ! input: model control dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): length of the entire time step (seconds) + dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): number of layers @@ -523,7 +523,7 @@ subroutine systemSolv(& call summaSolve(& ! input: model control dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): length of the entire time step (seconds) + dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate iter, & ! intent(in): iteration index nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index d42cd6182..503f31970 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -94,6 +94,7 @@ subroutine varSubstep(& dt, & ! intent(in) : time step (s) dtInit, & ! intent(in) : initial time step (seconds) dt_min, & ! intent(in) : minimum time step (seconds) + tdrainage, & ! intent(in) : length drainage pond drains in nState, & ! intent(in) : total number of state variables doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature firstSubStep, & ! intent(in) : flag to denote first sub-step @@ -142,6 +143,7 @@ subroutine varSubstep(& real(rkind),intent(in) :: dt ! time step (seconds) real(rkind),intent(in) :: dtInit ! initial time step (seconds) real(rkind),intent(in) :: dt_min ! minimum time step (seconds) + real(rkind),intent(in) :: tdrainage ! length drainage pond drains in integer(i4b),intent(in) :: nState ! total number of state variables logical(lgt),intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step @@ -260,7 +262,8 @@ subroutine varSubstep(& ! initialize the length of the substep dtSubstep = dtInit - ! change maxstep with hard code here to make only the newton step loop in systemSolve happen more frequently for num_method = bEuler with BE>1 + ! change maxstep with hard code here to make only the newton step loop in systemSolv* happen more frequently for num_method = bEuler or sundials + ! NOTE: this may just be amplifying the splitting error if maxstep is smaller than the full possible step maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) ! initalize flag for checking if energy fluxes had been modified @@ -322,7 +325,7 @@ subroutine varSubstep(& call systemSolvSundials(& ! input: model control dtSubstep, & ! intent(in): time step (s) - dtInit, & ! intent(in): entire time step (s), right now same as dtSubstep but might change with operator splitting + tdrainage, & ! intent(in): entire time step (s), right now same as dtSubstep but might change with operator splitting nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to denote first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call @@ -356,7 +359,7 @@ subroutine varSubstep(& call systemSolv(& ! input: model control dtSubstep, & ! intent(in): time step (s) - dtInit, & ! intent(in): entire time step (s) + tdrainage, & ! intent(in): entire time step (s) nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to denote first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call @@ -1003,15 +1006,15 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues - write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 - write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 - write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt - write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt - write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt - write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - write(*,'(a,1x,f20.10)') 'liqError = ', liqError + !write(*,'(a,1x,f20.10)') 'dt = ', dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + !write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 + !write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 + !write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt + !write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt + !write(*,'(a,1x,f20.10)') 'liqError = ', liqError waterBalanceError = .true. return endif ! if there is a water balance error @@ -1027,14 +1030,14 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues - write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 - write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 - write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux - write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink - write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink - write(*,'(a,1x,f20.10)') 'compSink = ', compSink - write(*,'(a,1x,f20.10)') 'liqError = ', liqError + !write(*,'(a,1x,f20.10)') 'dt = ', dt + !write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 + !write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 + !write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux + !write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink + !write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink + !write(*,'(a,1x,f20.10)') 'compSink = ', compSink + !write(*,'(a,1x,f20.10)') 'liqError = ', liqError waterBalanceError = .true. return endif ! if there is a water balance error From 1adaac72aa5d0f42ad201b78434d2509915790ae Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 10 Feb 2023 21:27:38 -0600 Subject: [PATCH 0537/1472] big change is need to let it have a thick layers if it needs to merge layers (volicePack). also need to save indexing and resizing of data only on outer. code should work now --- build/source/engine/coupled_em.f90 | 287 +++++++++++++++-------------- build/source/engine/volicePack.f90 | 32 ++-- 2 files changed, 167 insertions(+), 152 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index f86bd15e5..efc59ea61 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -206,7 +206,8 @@ subroutine coupled_em(& logical(lgt) :: pauseFlag ! flag to pause execution logical(lgt),parameter :: backwardsCompatibility=.true. ! flag to denote a desire to ensure backwards compatibility with previous branches logical(lgt) :: checkMassBalance ! flag to check the mass balance - type(var_ilength) :: indx_temp ! temporary model index variables + type(var_ilength) :: indx_temp ! temporary model index variables saved only on outer loop + type(var_ilength) :: indx_temp0 ! temporary model index variables saved every time type(var_dlength) :: prog_temp ! temporary model prognostic variables type(var_dlength) :: diag_temp ! temporary model diagnostic variables ! check SWE @@ -311,6 +312,7 @@ subroutine coupled_em(& ! create temporary data structures for index variables call resizeData(indx_meta(:),indx_data,indx_temp,err=err,message=cmessage) + call resizeData(indx_meta(:),indx_data,indx_temp0,err=err,message=cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif ! allocate space for the local fluxes @@ -375,7 +377,7 @@ subroutine coupled_em(& ! change maxstep_op with hard code here to make the inner loop computations in opSplitting happen more frequently for num_method = bEuler or sundials minstep = 10._rkind ! mpar_data%var(iLookPARAM%minstep)%dat(1) ! minimum time step (s) maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) - maxstep_op = 900._rkind !mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) to run opSplitting over + maxstep_op = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) to run opSplitting over ! compute the number of layers with roots nLayersRoots = count(prog_data%var(iLookPROG%iLayerHeight)%dat(nSnow:nLayers-1) < mpar_data%var(iLookPARAM%rootingDepth)%dat(1)-verySmall) @@ -587,142 +589,145 @@ subroutine coupled_em(& ! NOTE: this is necessary because the length of index variables depends on a given split ! --> the resize here is overwritten later (in indexSplit) ! --> admittedly ugly, and retained for now - if(stepFailure)then + if(stepFailure)then ! resize temp to current, later current = lastInnerSTep call resizeData(indx_meta(:),indx_temp,indx_data,err=err,message=cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - else - call resizeData(indx_meta(:),indx_data,indx_temp,err=err,message=cmessage) + else ! resize current data to temp0, temp0 is saved for next run + call resizeData(indx_meta(:),indx_data,indx_temp0,err=err,message=cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + do iVar=1,size(indx_data%var) + indx_temp0%var(iVar)%dat(:) = indx_data%var(iVar)%dat(:) + end do endif - ! save/recover copies of index variables - do iVar=1,size(indx_data%var) - select case(stepFailure) - case(.false.); indx_temp%var(iVar)%dat(:) = indx_data%var(iVar)%dat(:) - case(.true.); indx_data%var(iVar)%dat(:) = indx_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - ! save/recover copies of prognostic variables - do iVar=1,size(prog_data%var) - select case(stepFailure) - case(.false.); prog_temp%var(iVar)%dat(:) = prog_data%var(iVar)%dat(:) - case(.true.); prog_data%var(iVar)%dat(:) = prog_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - ! save/recover copies of diagnostic variables - do iVar=1,size(diag_data%var) - select case(stepFailure) - case(.false.); diag_temp%var(iVar)%dat(:) = diag_data%var(iVar)%dat(:) - case(.true.); diag_data%var(iVar)%dat(:) = diag_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - ! re-assign dimension lengths - nSnow = count(indx_data%var(iLookINDEX%layerType)%dat==iname_snow) - nSoil = count(indx_data%var(iLookINDEX%layerType)%dat==iname_soil) - nLayers = nSnow+nSoil - - ! check outer + ! check if on outer loop do_outer = .true. if (maxstep_op < maxstep .and. .not.(firstInnerStep .or. stepFailure)) do_outer = .false. - - ! *** merge/sub-divide snow layers... - ! ----------------------------------- if(do_outer)then - call volicePack(& - ! input/output: model data structures - doLayerMerge, & ! intent(in): flag to force merge of snow layers - model_decisions, & ! intent(in): model decisions - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): type of each layer - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output - modifiedLayers, & ! intent(out): flag to denote that layers were modified - err,cmessage) ! intent(out): error control - if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if - - ! save the number of snow and soil layers - nSnow = indx_data%var(iLookINDEX%nSnow)%dat(1) - nSoil = indx_data%var(iLookINDEX%nSoil)%dat(1) - nLayers = indx_data%var(iLookINDEX%nLayers)%dat(1) - - ! compute the indices for the model state variables - if(firstSubStep .or. modifiedVegState .or. modifiedLayers)then - call indexState(computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - includeAquifer, & ! intent(in): flag to denote if included the aquifer - nSnow,nSoil,nLayers, & ! intent(in): number of snow and soil layers, and total number of layers - indx_data, & ! intent(inout): indices defining model states and layers - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - end if - - ! recreate the temporary data structures - ! NOTE: resizeData(meta, old, new, ..) - if(modifiedVegState .or. modifiedLayers)then - - ! create temporary data structures for prognostic variables - call resizeData(prog_meta(:),prog_data,prog_temp,copy=.true.,err=err,message=cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! create temporary data structures for diagnostic variables - call resizeData(diag_meta(:),diag_data,diag_temp,copy=.true.,err=err,message=cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! create temporary data structures for index variables - call resizeData(indx_meta(:),indx_data,indx_temp,copy=.true.,err=err,message=cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - do iVar=1,size(indx_data%var) - select case(stepFailure) - case(.false.); indx_temp%var(iVar)%dat(:) = indx_data%var(iVar)%dat(:) - case(.true.); indx_data%var(iVar)%dat(:) = indx_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - endif ! if modified the states - - ! define the number of state variables - nState = indx_data%var(iLookINDEX%nState)%dat(1) - - ! *** compute diagnostic variables for each layer... - ! -------------------------------------------------- - ! NOTE: this needs to be done AFTER volicePack, since layers may have been sub-divided and/or merged - call diagn_evar(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): canopy depth (m) - ! input/output: data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if - - ! *** compute melt of the "snow without a layer"... - ! ------------------------------------------------- - ! NOTE: forms a surface melt pond, which drains into the upper-most soil layer through the time step - ! (check for the special case of "snow without a layer") - ! this pond melts evenly over entire time of maxstep until it gets recomputed because based on SWE when computed - if(nSnow==0) then - call implctMelt(& - ! input/output: integrated snowpack properties - prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(inout): snow water equivalent (kg m-2) - prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(inout): snow depth (m) - prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1), & ! intent(inout): surface melt pond (kg m-2) - ! input/output: properties of the upper-most soil layer - prog_data%var(iLookPROG%mLayerTemp)%dat(nSnow+1), & ! intent(inout): surface layer temperature (K) - prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1), & ! intent(inout): surface layer depth (m) - diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat(nSnow+1),& ! intent(inout): surface layer volumetric heat capacity (J m-3 K-1) - ! output: error control - err,cmessage ) ! intent(out): error control - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - endif + + ! save/recover copies of index variables, temp saved on lastInnerStep, failed starts at lastInnerSTep + do iVar=1,size(indx_data%var) + select case(stepFailure) + case(.false.); indx_temp%var(iVar)%dat(:) = indx_data%var(iVar)%dat(:) + case(.true.); indx_data%var(iVar)%dat(:) = indx_temp%var(iVar)%dat(:) + end select + end do ! looping through variables + + ! save/recover copies of prognostic variables + do iVar=1,size(prog_data%var) + select case(stepFailure) + case(.false.); prog_temp%var(iVar)%dat(:) = prog_data%var(iVar)%dat(:) + case(.true.); prog_data%var(iVar)%dat(:) = prog_temp%var(iVar)%dat(:) + end select + end do ! looping through variables + + ! save/recover copies of diagnostic variables + do iVar=1,size(diag_data%var) + select case(stepFailure) + case(.false.); diag_temp%var(iVar)%dat(:) = diag_data%var(iVar)%dat(:) + case(.true.); diag_data%var(iVar)%dat(:) = diag_temp%var(iVar)%dat(:) + end select + end do ! looping through variables + + ! re-assign dimension lengths + nSnow = count(indx_data%var(iLookINDEX%layerType)%dat==iname_snow) + nSoil = count(indx_data%var(iLookINDEX%layerType)%dat==iname_soil) + nLayers = nSnow+nSoil + + ! *** merge/sub-divide snow layers... + ! ----------------------------------- + call volicePack(& + ! input/output: model data structures + doLayerMerge, & ! intent(in): flag to force merge of snow layers + model_decisions, & ! intent(in): model decisions + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): type of each layer + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output + modifiedLayers, & ! intent(out): flag to denote that layers were modified + err,cmessage) ! intent(out): error control + if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if + + ! save the number of snow and soil layers + nSnow = indx_data%var(iLookINDEX%nSnow)%dat(1) + nSoil = indx_data%var(iLookINDEX%nSoil)%dat(1) + nLayers = indx_data%var(iLookINDEX%nLayers)%dat(1) + + ! compute the indices for the model state variables + if(firstSubStep .or. modifiedVegState .or. modifiedLayers)then + call indexState(computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + includeAquifer, & ! intent(in): flag to denote if included the aquifer + nSnow,nSoil,nLayers, & ! intent(in): number of snow and soil layers, and total number of layers + indx_data, & ! intent(inout): indices defining model states and layers + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + end if + + ! recreate the temporary data structures + ! NOTE: resizeData(meta, old, new, ..) + if(modifiedVegState .or. modifiedLayers)then + + ! create temporary data structures for prognostic variables + call resizeData(prog_meta(:),prog_data,prog_temp,copy=.true.,err=err,message=cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! create temporary data structures for diagnostic variables + call resizeData(diag_meta(:),diag_data,diag_temp,copy=.true.,err=err,message=cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! create temporary data structures for index variables + call resizeData(indx_meta(:),indx_data,indx_temp,copy=.true.,err=err,message=cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + do iVar=1,size(indx_data%var) + select case(stepFailure) + case(.false.); indx_temp%var(iVar)%dat(:) = indx_data%var(iVar)%dat(:) + case(.true.); indx_data%var(iVar)%dat(:) = indx_temp%var(iVar)%dat(:) + end select + end do ! looping through variables + + endif ! if modified the states + + ! define the number of state variables + nState = indx_data%var(iLookINDEX%nState)%dat(1) + + ! *** compute diagnostic variables for each layer... + ! -------------------------------------------------- + ! NOTE: this needs to be done AFTER volicePack, since layers may have been sub-divided and/or merged + call diagn_evar(& + ! input: control variables + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): canopy depth (m) + ! input/output: data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if + + ! *** compute melt of the "snow without a layer"... + ! ------------------------------------------------- + ! NOTE: forms a surface melt pond, which drains into the upper-most soil layer through the time step + ! (check for the special case of "snow without a layer") + ! this pond melts evenly over entire time of maxstep until it gets recomputed because based on SWE when computed + if(nSnow==0) then + call implctMelt(& + ! input/output: integrated snowpack properties + prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(inout): snow water equivalent (kg m-2) + prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(inout): snow depth (m) + prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1), & ! intent(inout): surface melt pond (kg m-2) + ! input/output: properties of the upper-most soil layer + prog_data%var(iLookPROG%mLayerTemp)%dat(nSnow+1), & ! intent(inout): surface layer temperature (K) + prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1), & ! intent(inout): surface layer depth (m) + diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat(nSnow+1),& ! intent(inout): surface layer volumetric heat capacity (J m-3 K-1) + ! output: error control + err,cmessage ) ! intent(out): error control + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + endif endif ! (do_outer loop) @@ -786,7 +791,9 @@ subroutine coupled_em(& message=trim(message)//'length of the coupled step is below the minimum step length' err=20; return endif - ! try again + ! try again, restart step + dt_solv = dt_solv - dt_solvInner + dt_solvInner = 0._rkind cycle substeps endif @@ -798,12 +805,13 @@ subroutine coupled_em(& ! increment sub-step dt_solvInner = dt_solvInner + dt_sub - ! check outer + ! check if on outer loop do_outer = .true. if(maxstep_op < maxstep .and. .not.lastInnerStep) do_outer = .false. - ! *** remove ice due to sublimation... - ! -------------------------------------------------------------- if(do_outer)then + + ! *** remove ice due to sublimation... + ! -------------------------------------------------------------- sublime: associate(& scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1), & ! sublimation from the vegetation canopy (kg m-2 s-1) scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! sublimation from the snow surface (kg m-2 s-1) @@ -885,7 +893,9 @@ subroutine coupled_em(& message=trim(message)//'length of the coupled step is below the minimum step length' err=20; return endif - ! try again + ! try again, restart step (at end inner step) + dt_solv = dt_solv - maxstep_op + dt_solvInner = 0._rkind cycle substeps endif @@ -1055,8 +1065,11 @@ subroutine coupled_em(& ! * balance checks for the canopy... ! ---------------------------------- - if (model_decisions(iLookDECISIONS%num_method)%iDecision==bEuler) checkMassBalance = .true. ! convergence criteria for bEuler - if (model_decisions(iLookDECISIONS%num_method)%iDecision==sundials) checkMassBalance = .false. ! sundials does not use this criteria + if (model_decisions(iLookDECISIONS%num_method)%iDecision==bEuler)then ! convergence criteria for bEuler + if (maxstep_op >= maxstep) checkMassBalance = .true. + if (maxstep_op < maxstep) checkMassBalance = .false. ! cannot check since not saving fluxes on inner loops + endif + if (model_decisions(iLookDECISIONS%num_method)%iDecision==sundials) checkMassBalance = .false. ! cannot check since not saving fluxes on inner loops ! if computing the vegetation flux if(computeVegFlux)then diff --git a/build/source/engine/volicePack.f90 b/build/source/engine/volicePack.f90 index 218dfbba0..8205b84c2 100644 --- a/build/source/engine/volicePack.f90 +++ b/build/source/engine/volicePack.f90 @@ -97,20 +97,22 @@ subroutine volicePack(& ! initialize error control err=0; message='volicePack/' - ! divide snow layers if too thick - call layerDivide(& - ! input/output: model data structures - model_decisions, & ! intent(in): model decisions - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): type of each layer - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output - divideLayer, & ! intent(out): flag to denote that layers were modified - err,cmessage) ! intent(out): error control - if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if - + ! divide snow layers if too thick, don't do it if need to merge + if (.not.tooMuchMelt)then + call layerDivide(& + ! input/output: model data structures + model_decisions, & ! intent(in): model decisions + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): type of each layer + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output + divideLayer, & ! intent(out): flag to denote that layers were modified + err,cmessage) ! intent(out): error control + if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if + endif + ! print *, 'in coupled_em divideLayer = ', divideLayer ! merge snow layers if they are too thin @@ -127,7 +129,7 @@ subroutine volicePack(& mergedLayers, & ! intent(out): flag to denote that layers were modified err,cmessage) ! intent(out): error control if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if - + ! print *, 'in coupled_em mergeLayers = ', mergedLayers ! update the number of layers From 5d078be251bec018e72aee9cffdb292b5e5d009f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 10 Feb 2023 22:56:16 -0600 Subject: [PATCH 0538/1472] sublimation needs to be done on maxstep not substep. really should move this into the solver --- build/source/engine/coupled_em.f90 | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index efc59ea61..e40ff3846 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -811,6 +811,7 @@ subroutine coupled_em(& if(do_outer)then ! *** remove ice due to sublimation... + ! NOTE: In the future this should be moved into the solver, makes a big difference ! -------------------------------------------------------------- sublime: associate(& scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1), & ! sublimation from the vegetation canopy (kg m-2 s-1) @@ -832,7 +833,7 @@ subroutine coupled_em(& if(computeVegFlux)then ! remove mass of ice on the canopy - scalarCanopyIce = scalarCanopyIce + scalarCanopySublimation*dt_sub + scalarCanopyIce = scalarCanopyIce + scalarCanopySublimation*maxstep ! if removed all ice, take the remaining sublimation from water if(scalarCanopyIce < 0._rkind)then @@ -840,10 +841,10 @@ subroutine coupled_em(& scalarCanopyIce = 0._rkind endif - ! modify fluxes if there is insufficient canopy water to support the converged sublimation rate over the time step dt_sub + ! modify fluxes if there is insufficient canopy water to support the converged sublimation rate over the whole time step if(scalarCanopyLiq < 0._rkind)then ! --> superfluous sublimation flux - superflousSub = -scalarCanopyLiq/dt_sub ! kg m-2 s-1 + superflousSub = -scalarCanopyLiq/maxstep ! kg m-2 s-1 superflousNrg = superflousSub*LH_sub ! W m-2 (J m-2 s-1) ! --> update fluxes and states scalarCanopySublimation = scalarCanopySublimation + superflousSub @@ -858,7 +859,7 @@ subroutine coupled_em(& end if ! (if computing the vegetation flux) call computSnowDepth(& - dt_sub, & ! intent(in) + maxstep, & ! intent(in) nSnow, & ! intent(in) scalarSnowSublimation, & ! intent(in) mLayerVolFracLiq, & ! intent(inout) @@ -894,7 +895,7 @@ subroutine coupled_em(& err=20; return endif ! try again, restart step (at end inner step) - dt_solv = dt_solv - maxstep_op + dt_solv = 0._rkind dt_solvInner = 0._rkind cycle substeps endif From f235c6b2e7366eacf6fd8f60f0fb60b4e0c57307 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 10 Feb 2023 23:43:03 -0600 Subject: [PATCH 0539/1472] add notes to sublimation and fix outer step if --- build/source/engine/coupled_em.f90 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index e40ff3846..10e802dfe 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -602,7 +602,7 @@ subroutine coupled_em(& ! check if on outer loop do_outer = .true. - if (maxstep_op < maxstep .and. .not.(firstInnerStep .or. stepFailure)) do_outer = .false. + if (.not.(firstInnerStep .or. stepFailure)) do_outer = .false. if(do_outer)then ! save/recover copies of index variables, temp saved on lastInnerStep, failed starts at lastInnerSTep @@ -807,11 +807,11 @@ subroutine coupled_em(& ! check if on outer loop do_outer = .true. - if(maxstep_op < maxstep .and. .not.lastInnerStep) do_outer = .false. + if(.not.lastInnerStep) do_outer = .false. if(do_outer)then ! *** remove ice due to sublimation... - ! NOTE: In the future this should be moved into the solver, makes a big difference + ! NOTE: In the future this should be moved into the solver, makes a big difference, and here only applying last substep amount to whole thing ! -------------------------------------------------------------- sublime: associate(& scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1), & ! sublimation from the vegetation canopy (kg m-2 s-1) @@ -822,10 +822,10 @@ subroutine coupled_em(& scalarSenHeatGround => flux_data%var(iLookFLUX%scalarSenHeatGround)%dat(1), & ! sensible heat flux from ground surface below vegetation (W m-2) scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1), & ! liquid water stored on the vegetation canopy (kg m-2) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! ice stored on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! canopy ice content (kg m-2) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat, & ! volumetric fraction of ice in the snow+soil domain (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat, & ! volumetric fraction of liquid water in the snow+soil domain (-) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat & ! depth of each snow+soil layer (m) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1), & ! canopy ice content (kg m-2) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat, & ! volumetric fraction of ice in the snow+soil domain (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat, & ! volumetric fraction of liquid water in the snow+soil domain (-) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat & ! depth of each snow+soil layer (m) ) ! associations to variables in data structures ! * compute change in canopy ice content due to sublimation... From 713fa0d9bea12524575b841768de008202641df1 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 11 Feb 2023 20:48:55 -0600 Subject: [PATCH 0540/1472] add step correction for when fail step do half steps --- build/source/engine/coupled_em.f90 | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 10e802dfe..ec27594e2 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -180,6 +180,7 @@ subroutine coupled_em(& real(rkind) :: dtMultiplier ! time step multiplier (-) based on what happenned in "opSplittin" real(rkind) :: minstep,maxstep ! minimum and maximum time step length (seconds) real(rkind) :: maxstep_op ! maximum time step length (seconds) to run opSplitting over + real(rkind) :: whole_step ! step the surface pond drainage and sublimation calculated over integer(i4b) :: nsub ! number of substeps logical(lgt) :: computeVegFluxOld ! flag to indicate if we are computing fluxes over vegetation on the previous sub step logical(lgt) :: includeAquifer ! flag to denote that an aquifer is included @@ -378,6 +379,7 @@ subroutine coupled_em(& minstep = 10._rkind ! mpar_data%var(iLookPARAM%minstep)%dat(1) ! minimum time step (s) maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) maxstep_op = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) to run opSplitting over + if(maxstep_op > maxstep) maxstep_op = maxstep ! compute the number of layers with roots nLayersRoots = count(prog_data%var(iLookPROG%iLayerHeight)%dat(nSnow:nLayers-1) < mpar_data%var(iLookPARAM%rootingDepth)%dat(1)-verySmall) @@ -600,9 +602,9 @@ subroutine coupled_em(& end do endif - ! check if on outer loop + ! check if on outer loop, always do outer if after failed step and on small step do_outer = .true. - if (.not.(firstInnerStep .or. stepFailure)) do_outer = .false. + if ( dt_sub == maxstep_op .and. .not.(firstInnerStep .or. stepFailure) ) do_outer = .false. if(do_outer)then ! save/recover copies of index variables, temp saved on lastInnerStep, failed starts at lastInnerSTep @@ -736,6 +738,8 @@ subroutine coupled_em(& ! save input step dtSave = dt_sub + whole_step = maxstep + if(dt_sub < maxstep_op) whole_step = dt_sub ! only happens if fails a step in the maxstep ! get the new solution call opSplittin(& @@ -745,7 +749,7 @@ subroutine coupled_em(& nLayers, & ! intent(in): total number of layers nState, & ! intent(in): total number of layers dt_sub, & ! intent(inout): length of the model sub-step - maxstep, & ! intent(in): length drainage pond drains in + whole_step, & ! intent(in): length drainage pond drains in (nsub==1), & ! intent(in): logical flag to denote the first substep firstInnerStep, & ! intent(in): flag to denote if the first time step in maxstep subStep computeVegFlux, & ! intent(in): logical flag to compute fluxes within the vegetation canopy @@ -805,9 +809,9 @@ subroutine coupled_em(& ! increment sub-step dt_solvInner = dt_solvInner + dt_sub - ! check if on outer loop + ! check if on outer loop, always do outer if after failed step and on small step do_outer = .true. - if(.not.lastInnerStep) do_outer = .false. + if( dt_sub == maxstep_op .and. .not.lastInnerStep ) do_outer = .false. if(do_outer)then ! *** remove ice due to sublimation... @@ -828,6 +832,9 @@ subroutine coupled_em(& mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat & ! depth of each snow+soil layer (m) ) ! associations to variables in data structures + whole_step = maxstep + if(dt_sub < maxstep_op) whole_step = dt_sub ! only happens if fails a step in the maxstep + ! * compute change in canopy ice content due to sublimation... ! ------------------------------------------------------------ if(computeVegFlux)then @@ -844,7 +851,7 @@ subroutine coupled_em(& ! modify fluxes if there is insufficient canopy water to support the converged sublimation rate over the whole time step if(scalarCanopyLiq < 0._rkind)then ! --> superfluous sublimation flux - superflousSub = -scalarCanopyLiq/maxstep ! kg m-2 s-1 + superflousSub = -scalarCanopyLiq/whole_step ! kg m-2 s-1 superflousNrg = superflousSub*LH_sub ! W m-2 (J m-2 s-1) ! --> update fluxes and states scalarCanopySublimation = scalarCanopySublimation + superflousSub @@ -859,7 +866,7 @@ subroutine coupled_em(& end if ! (if computing the vegetation flux) call computSnowDepth(& - maxstep, & ! intent(in) + whole_step, & ! intent(in) nSnow, & ! intent(in) scalarSnowSublimation, & ! intent(in) mLayerVolFracLiq, & ! intent(inout) @@ -1067,7 +1074,7 @@ subroutine coupled_em(& ! ---------------------------------- if (model_decisions(iLookDECISIONS%num_method)%iDecision==bEuler)then ! convergence criteria for bEuler - if (maxstep_op >= maxstep) checkMassBalance = .true. + if (maxstep_op == maxstep) checkMassBalance = .true. if (maxstep_op < maxstep) checkMassBalance = .false. ! cannot check since not saving fluxes on inner loops endif if (model_decisions(iLookDECISIONS%num_method)%iDecision==sundials) checkMassBalance = .false. ! cannot check since not saving fluxes on inner loops From a7afe972e90158b8ad10f3410ea8c124db4b2e14 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 11 Feb 2023 22:40:52 -0600 Subject: [PATCH 0541/1472] fixed memory error --- build/source/engine/coupled_em.f90 | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index ec27594e2..5a09f631c 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -570,6 +570,7 @@ subroutine coupled_em(& dt_init = min(data_step,maxstep,maxstep_op) ! initial substep length (s) dt_sub = dt_init ! length of substep dtSave = dt_init ! length of substep + whole_step = maxstep ! initialize the number of sub-steps nsub=0 @@ -607,6 +608,11 @@ subroutine coupled_em(& if ( dt_sub == maxstep_op .and. .not.(firstInnerStep .or. stepFailure) ) do_outer = .false. if(do_outer)then + if(.not.stepFailure)then + call resizeData(indx_meta(:),indx_data,indx_temp,err=err,message=cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + endif + ! save/recover copies of index variables, temp saved on lastInnerStep, failed starts at lastInnerSTep do iVar=1,size(indx_data%var) select case(stepFailure) @@ -731,6 +737,9 @@ subroutine coupled_em(& if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if endif + whole_step = maxstep + if(dt_sub < maxstep_op) whole_step = dt_sub ! only happens if fails a step in the maxstep + endif ! (do_outer loop) ! *** solve model equations... @@ -738,9 +747,6 @@ subroutine coupled_em(& ! save input step dtSave = dt_sub - whole_step = maxstep - if(dt_sub < maxstep_op) whole_step = dt_sub ! only happens if fails a step in the maxstep - ! get the new solution call opSplittin(& ! input: model control @@ -840,7 +846,7 @@ subroutine coupled_em(& if(computeVegFlux)then ! remove mass of ice on the canopy - scalarCanopyIce = scalarCanopyIce + scalarCanopySublimation*maxstep + scalarCanopyIce = scalarCanopyIce + scalarCanopySublimation*whole_step ! if removed all ice, take the remaining sublimation from water if(scalarCanopyIce < 0._rkind)then From 858bab318ec7d258e6e7845b05b6018248c4ec98 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sun, 12 Feb 2023 00:30:30 -0600 Subject: [PATCH 0542/1472] soil compress should be divided by dt --- build/source/engine/varSubstep.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 503f31970..75a17c200 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -578,10 +578,10 @@ subroutine varSubstep(& flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt ! sensible heat flux from the canopy to the canopy air space (W m-2) ! save the soil compression diagnostics - diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress/dt do iSoil=1,nSoil if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& - diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) = sumLayerCompress(iSoil) + diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) = sumLayerCompress(iSoil)/dt end do deallocate(sumLayerCompress) From 480cf48cda972fd961489fa1237331b254a3f9b3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 14 Feb 2023 23:13:46 -0600 Subject: [PATCH 0543/1472] move flux_data update from end of varSubstep to coupled_em where it can be done only on the outer_loop if we want. also updated some inout definitions that should only be in --- build/source/engine/coupled_em.f90 | 48 +++++++++++++++--- build/source/engine/opSplittin.f90 | 80 ++++++++++++++++++------------ build/source/engine/varSubstep.f90 | 40 +++++---------- 3 files changed, 104 insertions(+), 64 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 5a09f631c..c103f27c9 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -59,6 +59,7 @@ module coupled_em_module USE globalData,only:averageFlux_meta ! metadata on the timestep-average model flux structure ! global data +USE globalData,only:integerMissing ! missing integer USE globalData,only:data_step ! time step of forcing data (s) USE globalData,only:model_decisions ! model decision structure USE globalData,only:globalPrintFlag ! the global print flag @@ -179,7 +180,7 @@ subroutine coupled_em(& real(rkind) :: dt_solv ! seconds in the data step that have been completed real(rkind) :: dtMultiplier ! time step multiplier (-) based on what happenned in "opSplittin" real(rkind) :: minstep,maxstep ! minimum and maximum time step length (seconds) - real(rkind) :: maxstep_op ! maximum time step length (seconds) to run opSplitting over + real(rkind) :: maxstep_op ! maximum time step length (seconds) to run opSplittin over real(rkind) :: whole_step ! step the surface pond drainage and sublimation calculated over integer(i4b) :: nsub ! number of substeps logical(lgt) :: computeVegFluxOld ! flag to indicate if we are computing fluxes over vegetation on the previous sub step @@ -198,7 +199,7 @@ subroutine coupled_em(& real(rkind) :: massLiquid ! mass liquid water (kg m-2) real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) - integer(i4b) :: ixSolution ! solution method used by opSplitting + integer(i4b) :: ixSolution ! solution method used by opSplittin logical(lgt) :: firstSubStep ! flag to denote if the first time step logical(lgt) :: stepFailure ! flag to denote the need to reduce length of the coupled step and try again logical(lgt) :: tooMuchMelt ! flag to denote that there was too much melt in a given time step @@ -219,6 +220,13 @@ subroutine coupled_em(& real(rkind) :: effSnowfall ! effective snowfall (kg m-2 s-1) real(rkind) :: sfcMeltPond ! surface melt pond (kg m-2) real(rkind) :: massBalance ! mass balance error (kg m-2) + ! energy fluxes + integer(i4b) :: iSoil ! index of soil layers + real(rkind) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) + real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) + real(rkind) :: sumSoilCompress ! sum of total soil compression + real(rkind),allocatable :: sumLayerCompress(:) ! sum of soil compression by layer ! balance checks integer(i4b) :: iVar ! loop through model variables real(rkind) :: balanceSoilCompress ! total soil compression (kg m-2) @@ -246,7 +254,7 @@ subroutine coupled_em(& ! outer loop control logical(lgt) :: firstInnerStep ! flag to denote if the first time step in maxstep subStep logical(lgt) :: lastInnerStep ! flag to denote if the last time step in maxstep subStep - logical(lgt) :: do_outer ! flag to denote if doing the outer steps surrounding the call to sopSplitting + logical(lgt) :: do_outer ! flag to denote if doing the outer steps surrounding the call to opSplittin real(rkind) :: dt_solvInner ! seconds in the maxstep subStep that have been completed ! ---------------------------------------------------------------------------------------------------------------------------------------------- @@ -375,10 +383,10 @@ subroutine coupled_em(& ! short-cut to the algorithmic control parameters ! NOTE - temporary assignment of minstep to foce something reasonable ! change maxstep with hard code here to make the outer and inner loop computations here in coupled_em happen more frequently for num_method = bEuler or sundials - ! change maxstep_op with hard code here to make the inner loop computations in opSplitting happen more frequently for num_method = bEuler or sundials + ! change maxstep_op with hard code here to make the inner loop computations in opSplittin happen more frequently for num_method = bEuler or sundials minstep = 10._rkind ! mpar_data%var(iLookPARAM%minstep)%dat(1) ! minimum time step (s) maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) - maxstep_op = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) to run opSplitting over + maxstep_op = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) to run opSplittin over if(maxstep_op > maxstep) maxstep_op = maxstep ! compute the number of layers with roots @@ -592,7 +600,7 @@ subroutine coupled_em(& ! NOTE: this is necessary because the length of index variables depends on a given split ! --> the resize here is overwritten later (in indexSplit) ! --> admittedly ugly, and retained for now - if(stepFailure)then ! resize temp to current, later current = lastInnerSTep + if(stepFailure)then ! resize temp to current data, later in code current data is set to lastInnerStep data call resizeData(indx_meta(:),indx_temp,indx_data,err=err,message=cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif else ! resize current data to temp0, temp0 is saved for next run @@ -671,6 +679,7 @@ subroutine coupled_em(& indx_data, & ! intent(inout): indices defining model states and layers err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + end if ! recreate the temporary data structures @@ -737,6 +746,13 @@ subroutine coupled_em(& if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if endif + ! initialize the total energy fluxes over whole step (modified in updateProg in opSplittin in varSubstep) + sumCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = 0._rkind ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = 0._rkind ! sensible heat flux from the canopy to the canopy air space (W m-2) + sumSoilCompress = 0._rkind ! total soil compression + allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._rkind ! soil compression by layer + whole_step = maxstep if(dt_sub < maxstep_op) whole_step = dt_sub ! only happens if fails a step in the maxstep @@ -771,6 +787,12 @@ subroutine coupled_em(& bvar_data, & ! intent(in): model variables for the local basin lookup_data, & ! intent(in): lookup tables model_decisions, & ! intent(in): model decisions + ! energy fluxes over whole_step added on by dt_sub + sumCanopyEvaporation, & ! intent(inout): sum of canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap, & ! intent(inout): sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy, & ! intent(inout): sum of sensible heat flux from the canopy to the canopy air space (W m-2) + sumSoilCompress, & ! intent(inout): sum of total soil compression + sumLayerCompress, & ! intent(inout): sum of soil compression by layer ! output: model control dtMultiplier, & ! intent(out): substep multiplier (-) tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt @@ -804,6 +826,7 @@ subroutine coupled_em(& ! try again, restart step dt_solv = dt_solv - dt_solvInner dt_solvInner = 0._rkind + deallocate(sumLayerCompress) cycle substeps endif @@ -820,6 +843,19 @@ subroutine coupled_em(& if( dt_sub == maxstep_op .and. .not.lastInnerStep ) do_outer = .false. if(do_outer)then + ! apply the energy fluxes + flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /whole_step ! canopy evaporation/condensation (kg m-2 s-1) + flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /whole_step ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /whole_step ! sensible heat flux from the canopy to the canopy air space (W m-2) + + ! apply the soil compression diagnostics + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress/whole_step + do iSoil=1,nSoil + if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& + diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) = sumLayerCompress(iSoil)/whole_step + end do + deallocate(sumLayerCompress) + ! *** remove ice due to sublimation... ! NOTE: In the future this should be moved into the solver, makes a big difference, and here only applying last substep amount to whole thing ! -------------------------------------------------------------- diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 6eff23c7b..f1711e74d 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -164,33 +164,39 @@ module opSplittin_module ! ********************************************************************************************************** subroutine opSplittin(& ! input: model control - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - nState, & ! intent(in): total number of state variables - dt, & ! intent(inout): time step (s) - tdrainage, & ! intent(in): length drainage pond drains in - firstSubStep, & ! intent(in): flag to denote first sub-step - firstInnerStep, & ! intent(in): flag to denote if the first time step in maxstep subStep - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + nState, & ! intent(in): total number of state variables + dt, & ! intent(in): time step (s) + tdrainage, & ! intent(in): length drainage pond drains in + firstSubStep, & ! intent(in): flag to denote first sub-step + firstInnerStep, & ! intent(in): flag to denote if the first time step in maxstep subStep + computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation ! input/output: data structures - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): index data - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - bvar_data, & ! intent(in): model variables for the local basin - lookup_data, & ! intent(in): lookup tables - model_decisions,& ! intent(in): model decisions + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): index data + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + bvar_data, & ! intent(in): model variables for the local basin + lookup_data, & ! intent(in): lookup tables + model_decisions, & ! intent(in): model decisions + ! energy fluxes + sumCanopyEvaporation, & ! intent(inout): sum of canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap, & ! intent(inout): sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy, & ! intent(inout): sum of sensible heat flux from the canopy to the canopy air space (W m-2) + sumSoilCompress, & ! intent(inout): sum of total soil compression + sumLayerCompress, & ! intent(inout): sum of soil compression by layer ! output: model control - dtMultiplier, & ! intent(out): substep multiplier (-) - tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt - stepFailure, & ! intent(out): flag to denote step failure - ixCoupling, & ! intent(out): coupling method used in this iteration - err,message) ! intent(out): error code and error message + dtMultiplier, & ! intent(out): substep multiplier (-) + tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt + stepFailure, & ! intent(out): flag to denote step failure + ixCoupling, & ! intent(out): coupling method used in this iteration + err,message) ! intent(out): error code and error message ! --------------------------------------------------------------------------------------- ! structure allocations USE allocspace_module,only:allocLocal ! allocate local data structures @@ -211,7 +217,7 @@ subroutine opSplittin(& integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers integer(i4b),intent(in) :: nState ! total number of state variables - real(rkind),intent(inout) :: dt ! time step (seconds) + real(rkind),intent(in) :: dt ! time step (seconds) real(rkind),intent(in) :: tdrainage ! length drainage pond drains in logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) @@ -227,6 +233,12 @@ subroutine opSplittin(& type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin type(zLookup), intent(in) :: lookup_data ! lookup tables type(model_options),intent(in) :: model_decisions(:) ! model decisions + ! energy fluxes + real(rkind),intent(out) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) + real(rkind),intent(out) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + real(rkind),intent(out) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) + real(rkind),intent(out) :: sumSoilCompress ! sum of total soil compression + real(rkind),intent(out) :: sumLayerCompress(:) ! sum of soil compression by layer ! output: model control real(rkind),intent(out) :: dtMultiplier ! substep multiplier (-) logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt @@ -255,14 +267,14 @@ subroutine opSplittin(& ! * operator splitting ! ------------------------------------------------------------------------------------------------------ ! minimum timestep - real(rkind),parameter :: dtmin_coupled=1800._rkind ! minimum time step for the fully coupled solution (seconds) - real(rkind),parameter :: dtmin_split=60._rkind ! minimum time step for the fully split solution (seconds) - real(rkind),parameter :: dtmin_scalar=10._rkind ! minimum time step for the scalar solution (seconds) + real(rkind),parameter :: dtmin_coupled=1800._rkind ! minimum time step for the fully coupled solution (seconds) + real(rkind),parameter :: dtmin_split=60._rkind ! minimum time step for the fully split solution (seconds) + real(rkind),parameter :: dtmin_scalar=10._rkind ! minimum time step for the scalar solution (seconds) real(rkind) :: dt_min ! minimum time step (seconds) real(rkind) :: dtInit ! initial time step (seconds) ! explicit error tolerance (depends on state type split, so defined here) - real(rkind),parameter :: errorTolLiqFlux=0.01_rkind ! error tolerance in the explicit solution (liquid flux) - real(rkind),parameter :: errorTolNrgFlux=10._rkind ! error tolerance in the explicit solution (energy flux) + real(rkind),parameter :: errorTolLiqFlux=0.01_rkind ! error tolerance in the explicit solution (liquid flux) + real(rkind),parameter :: errorTolNrgFlux=10._rkind ! error tolerance in the explicit solution (energy flux) ! number of substeps taken for a given split integer(i4b) :: nSubsteps ! number of substeps taken for a given split ! named variables defining the coupling and solution method @@ -775,6 +787,12 @@ subroutine opSplittin(& flux_data, & ! intent(inout) : model fluxes for a local HRU deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables bvar_data, & ! intent(in) : model variables for the local basin + ! energy fluxes + sumCanopyEvaporation, & ! intent(inout) : sum of canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap, & ! intent(inout) : sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy, & ! intent(inout) : sum of sensible heat flux from the canopy to the canopy air space (W m-2) + sumSoilCompress, & ! intent(inout) : sum of total soil compression + sumLayerCompress, & ! intent(inout) : sum of soil compression by layer ! output: control ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) dtMultiplier, & ! intent(out) : substep multiplier (-) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 75a17c200..a6553f8de 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -117,6 +117,12 @@ subroutine varSubstep(& flux_data, & ! intent(inout) : model fluxes for a local HRU deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables bvar_data, & ! intent(in) : model variables for the local basin + ! energy fluxes + sumCanopyEvaporation,& !intent(inout): sum of canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap,& !intent(inout): sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy, & ! intent(inout) : sum of sensible heat flux from the canopy to the canopy air space (W m-2) + sumSoilCompress, & ! intent(inout) : sum of total soil compression + sumLayerCompress, & ! intent(inout) : sum of soil compression by layer ! output: model control ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) dtMultiplier, & ! intent(out) : substep multiplier (-) @@ -166,6 +172,12 @@ subroutine varSubstep(& type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin + ! energy fluxes + real(rkind),intent(inout) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) + real(rkind),intent(inout) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + real(rkind),intent(inout) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) + real(rkind),intent(inout) :: sumSoilCompress ! sum of total soil compression + real(rkind),intent(inout) :: sumLayerCompress(:) ! sum of soil compression by layer ! output: model control integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) real(rkind),intent(out) :: dtMultiplier ! substep multiplier (-) @@ -213,12 +225,6 @@ subroutine varSubstep(& logical(lgt) :: checkNrgBalance logical(lgt) :: waterBalanceError ! flag to denote that there is a water balance error logical(lgt) :: nrgFluxModified ! flag to denote that the energy fluxes were modified - ! energy fluxes - real(rkind) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) - real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) - real(rkind) :: sumSoilCompress - real(rkind),allocatable :: sumLayerCompress(:) ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- @@ -264,7 +270,7 @@ subroutine varSubstep(& ! change maxstep with hard code here to make only the newton step loop in systemSolv* happen more frequently for num_method = bEuler or sundials ! NOTE: this may just be amplifying the splitting error if maxstep is smaller than the full possible step - maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) + maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) ! initalize flag for checking if energy fluxes had been modified nrgFluxModified = .false. @@ -278,13 +284,6 @@ subroutine varSubstep(& flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) end do - ! initialize the total energy fluxes (modified in updateProg) - sumCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap = 0._rkind ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy = 0._rkind ! sensible heat flux from the canopy to the canopy air space (W m-2) - sumSoilCompress = 0._rkind ! total soil compression - allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._rkind ! soil compression by layer - ! define the first flux call in a splitting operation firstSplitOper = (.not.scalarSolution .or. iStateSplit==1) @@ -572,19 +571,6 @@ subroutine varSubstep(& end do substeps ! time steps for variable-dependent sub-stepping ! NOTE: if we get to here then we are accepting then dtSum should dt - ! save the energy fluxes - flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt ! canopy evaporation/condensation (kg m-2 s-1) - flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt ! sensible heat flux from the canopy to the canopy air space (W m-2) - - ! save the soil compression diagnostics - diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress/dt - do iSoil=1,nSoil - if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& - diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) = sumLayerCompress(iSoil)/dt - end do - deallocate(sumLayerCompress) - ! end associate statements end associate globalVars From d410bd2af5a91a7dac267422bafe869bb2d0be99 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 16 Feb 2023 22:09:21 +0900 Subject: [PATCH 0544/1472] firstInnerStep if stepfailure --- build/source/engine/coupled_em.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index c103f27c9..01f03fef5 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -613,7 +613,8 @@ subroutine coupled_em(& ! check if on outer loop, always do outer if after failed step and on small step do_outer = .true. - if ( dt_sub == maxstep_op .and. .not.(firstInnerStep .or. stepFailure) ) do_outer = .false. + if(stepFailure) firstInnerStep = .true. + if ( dt_sub == maxstep_op .and. .not.firstInnerStep ) do_outer = .false. if(do_outer)then if(.not.stepFailure)then From b4a318f5e8a210a1e7cb0d2e8755963156e52162 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 16 Feb 2023 22:30:12 +0900 Subject: [PATCH 0545/1472] errors in inout and if statement --- build/source/engine/coupled_em.f90 | 4 ++-- build/source/engine/varSubstep.f90 | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 01f03fef5..2ea3731df 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -771,7 +771,7 @@ subroutine coupled_em(& nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers nState, & ! intent(in): total number of layers - dt_sub, & ! intent(inout): length of the model sub-step + dt_sub, & ! intent(in): length of the model sub-step whole_step, & ! intent(in): length drainage pond drains in (nsub==1), & ! intent(in): logical flag to denote the first substep firstInnerStep, & ! intent(in): flag to denote if the first time step in maxstep subStep @@ -869,7 +869,7 @@ subroutine coupled_em(& scalarSenHeatGround => flux_data%var(iLookFLUX%scalarSenHeatGround)%dat(1), & ! sensible heat flux from ground surface below vegetation (W m-2) scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1), & ! liquid water stored on the vegetation canopy (kg m-2) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! ice stored on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1), & ! canopy ice content (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1), & ! canopy ice content (kg m-2) mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat, & ! volumetric fraction of ice in the snow+soil domain (-) mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat, & ! volumetric fraction of liquid water in the snow+soil domain (-) mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat & ! depth of each snow+soil layer (m) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index a6553f8de..cb21f4dd5 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -523,12 +523,8 @@ subroutine varSubstep(& if(globalPrintFlag)& write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt - ! increment fluxes, define weight applied to each splitting operation only possible in bEuler right now - select case(ixNumericalMethod) - case(sundials); dt_wght = 1._qp - case(bEuler); dt_wght = dtSubstep/dt - case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return - end select + ! increment fluxes, define weight applied to each substep + dt_wght = dtSubstep/dt do iVar=1,size(flux_meta) if(count(fluxMask%var(iVar)%dat)>0) then From d5843885c2430fe73da69cdbeb82b39054541565 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 17 Feb 2023 14:41:22 +0900 Subject: [PATCH 0546/1472] changing some things to only be done on first step, mLayerMeltFreeze and flux initialization --- build/source/engine/coupled_em.f90 | 19 +++++- build/source/engine/opSplittin.f90 | 102 ++++++++++++----------------- build/source/engine/varSubstep.f90 | 3 +- 3 files changed, 61 insertions(+), 63 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 2ea3731df..192f6f19a 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -212,6 +212,7 @@ subroutine coupled_em(& type(var_ilength) :: indx_temp0 ! temporary model index variables saved every time type(var_dlength) :: prog_temp ! temporary model prognostic variables type(var_dlength) :: diag_temp ! temporary model diagnostic variables + real(rkind),allocatable :: mLayerVolFracIceInit(:)! initial vector for volumetric fraction of ice (-) ! check SWE real(rkind) :: oldSWE ! SWE at the start of the substep real(rkind) :: newSWE ! SWE at the end of the substep @@ -611,7 +612,7 @@ subroutine coupled_em(& end do endif - ! check if on outer loop, always do outer if after failed step and on small step + ! check if on outer loop, always do outer if after failed step and on then on reduced timestep do_outer = .true. if(stepFailure) firstInnerStep = .true. if ( dt_sub == maxstep_op .and. .not.firstInnerStep ) do_outer = .false. @@ -754,6 +755,10 @@ subroutine coupled_em(& sumSoilCompress = 0._rkind ! total soil compression allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._rkind ! soil compression by layer + ! save volumetric ice content at the start of the step + ! NOTE: used for volumetric loss due to melt-freeze + allocate(mLayerVolFracIceInit(nLayers)); mLayerVolFracIceInit = prog_data%var(iLookPROG%mLayerVolFracIce)%dat + whole_step = maxstep if(dt_sub < maxstep_op) whole_step = dt_sub ! only happens if fails a step in the maxstep @@ -828,6 +833,7 @@ subroutine coupled_em(& dt_solv = dt_solv - dt_solvInner dt_solvInner = 0._rkind deallocate(sumLayerCompress) + deallocate(mLayerVolFracIceInit) cycle substeps endif @@ -857,10 +863,12 @@ subroutine coupled_em(& end do deallocate(sumLayerCompress) - ! *** remove ice due to sublimation... + + ! *** remove ice due to sublimation and freeze calculations... ! NOTE: In the future this should be moved into the solver, makes a big difference, and here only applying last substep amount to whole thing ! -------------------------------------------------------------- sublime: associate(& + mLayerMeltFreeze => diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat, & ! melt-freeze in each snow and soil layer (kg m-3) scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1), & ! sublimation from the vegetation canopy (kg m-2 s-1) scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! sublimation from the snow surface (kg m-2 s-1) scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1), & ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) @@ -875,6 +883,13 @@ subroutine coupled_em(& mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat & ! depth of each snow+soil layer (m) ) ! associations to variables in data structures + + ! compute the melt in each snow and soil layer + if(nSnow>0)& + mLayerMeltFreeze(1:nSnow) = -( mLayerVolFracIce(1:nSnow) - mLayerVolFracIceInit(1:nSnow) ) * iden_ice + mLayerMeltFreeze(nSnow+1:nLayers) = -(mLayerVolFracIce(nSnow+1:nLayers) - mLayerVolFracIceInit(nSnow+1:nLayers))*iden_water + deallocate(mLayerVolFracIceInit) + whole_step = maxstep if(dt_sub < maxstep_op) whole_step = dt_sub ! only happens if fails a step in the maxstep diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index f1711e74d..1e00d317a 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -354,7 +354,6 @@ subroutine opSplittin(& ! model diagnostic variables (fraction of liquid water) scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) - mLayerMeltFreeze => diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat ,& ! intent(out): [dp(:)] melt-freeze in each snow and soil layer (kg m-3) ! model state variables (vegetation canopy) scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(out): [dp] temperature of the canopy air space (K) scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(out): [dp] temperature of the vegetation canopy (K) @@ -410,10 +409,6 @@ subroutine opSplittin(& ! compute the total water content in the vegetation canopy scalarCanopyWat = scalarCanopyLiq + scalarCanopyIce ! kg m-2 - ! save volumetric ice content at the start of the step - ! NOTE: used for volumetric loss due to melt-freeze - mLayerVolFracIceInit(:) = mLayerVolFracIce(:) - ! compute the total water content in snow and soil ! NOTE: no ice expansion allowed for soil if(nSnow>0)& @@ -455,28 +450,26 @@ subroutine opSplittin(& call allocLocal(deriv_meta(:),deriv_data,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - ! intialize the flux conter - do iVar=1,size(flux_meta) ! loop through fluxes - fluxCount%var(iVar)%dat(:) = 0 - end do - - ! initialize the model fluxes - do iVar=1,size(flux_meta) ! loop through fluxes - if(flux2state_orig(iVar)%state1==integerMissing .and. flux2state_orig(iVar)%state2==integerMissing) cycle ! flux does not depend on state (e.g., input) - if(flux2state_orig(iVar)%state1==iname_watCanopy .and. .not.computeVegFlux) cycle ! use input fluxes in cases where there is no canopy - flux_data%var(iVar)%dat(:) = 0._rkind - end do - - ! initialize derivatives - do iVar=1,size(deriv_meta) - deriv_data%var(iVar)%dat(:) = 0._rkind - end do + ! intialize the flux counter on first inner step + if(firstInnerStep) then + do iVar=1,size(flux_meta) ! loop through fluxes + fluxCount%var(iVar)%dat(:) = 0 + end do + + ! initialize the model fluxes on first inner step + do iVar=1,size(flux_meta) ! loop through fluxes + if(flux2state_orig(iVar)%state1==integerMissing .and. flux2state_orig(iVar)%state2==integerMissing) cycle ! flux does not depend on state (e.g., input) + if(flux2state_orig(iVar)%state1==iname_watCanopy .and. .not.computeVegFlux) cycle ! use input fluxes in cases where there is no canopy + flux_data%var(iVar)%dat(:) = 0._rkind + end do + + ! initialize derivatives on first inner step + do iVar=1,size(deriv_meta) + deriv_data%var(iVar)%dat(:) = 0._rkind + end do + endif ! ========================================================================================================================================== - ! ========================================================================================================================================== - ! ========================================================================================================================================== - ! ========================================================================================================================================== - ! loop through different coupling strategies coupling: do ixCoupling=1,nCoupling @@ -713,8 +706,6 @@ subroutine opSplittin(& end associate stateSubset - ! ******************************************************************************************************************************* - ! ******************************************************************************************************************************* ! ******************************************************************************************************************************* ! ***** trial with a given solution method... @@ -727,29 +718,31 @@ subroutine opSplittin(& ! reset the flag for the first flux call if(.not.firstSuccess) firstFluxCall=.true. - ! save/recover copies of prognostic variables - do iVar=1,size(prog_data%var) - select case(failure) - case(.false.); prog_temp%var(iVar)%dat(:) = prog_data%var(iVar)%dat(:) - case(.true.); prog_data%var(iVar)%dat(:) = prog_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - ! save/recover copies of diagnostic variables - do iVar=1,size(diag_data%var) - select case(failure) - case(.false.); diag_temp%var(iVar)%dat(:) = diag_data%var(iVar)%dat(:) - case(.true.); diag_data%var(iVar)%dat(:) = diag_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - ! save/recover copies of model fluxes - do iVar=1,size(flux_data%var) - select case(failure) - case(.false.); flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) - case(.true.); flux_data%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) - end select - end do ! looping through variables + if(firstInnerStep)then + ! save/recover copies of prognostic variables + do iVar=1,size(prog_data%var) + select case(failure) + case(.false.); prog_temp%var(iVar)%dat(:) = prog_data%var(iVar)%dat(:) + case(.true.); prog_data%var(iVar)%dat(:) = prog_temp%var(iVar)%dat(:) + end select + end do ! looping through variables + + ! save/recover copies of diagnostic variables + do iVar=1,size(diag_data%var) + select case(failure) + case(.false.); diag_temp%var(iVar)%dat(:) = diag_data%var(iVar)%dat(:) + case(.true.); diag_data%var(iVar)%dat(:) = diag_temp%var(iVar)%dat(:) + end select + end do ! looping through variables + + ! save/recover copies of model fluxes + do iVar=1,size(flux_data%var) + select case(failure) + case(.false.); flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + case(.true.); flux_data%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) + end select + end do ! looping through variables + endif ! ----- ! * solve variable subset for one time step... @@ -879,15 +872,11 @@ subroutine opSplittin(& ! ***** trial with a given solution method... ! ******************************************************************************************************************************* - ! ******************************************************************************************************************************* - ! ******************************************************************************************************************************* end do domainSplit ! domain type splitting loop - end do stateThenDomain ! switch between the state and the domain - ! ----- ! * reset state variables for the mass split... ! --------------------------------------------- @@ -925,11 +914,6 @@ subroutine opSplittin(& ! use step halving if unable to complete the fully coupled solution in one substep if(ixCoupling/=fullyCoupled .or. nSubsteps>1) dtMultiplier=0.5_rkind - ! compute the melt in each snow and soil layer - if(nSnow>0)& - diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(1:nSnow) = -( mLayerVolFracIce(1:nSnow) - mLayerVolFracIceInit(1:nSnow) ) * iden_ice - diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(nSnow+1:nLayers) = -(mLayerVolFracIce(nSnow+1:nLayers) - mLayerVolFracIceInit(nSnow+1:nLayers))*iden_water - ! end associate statements end associate globalVars diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index cb21f4dd5..3e4832486 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -523,9 +523,8 @@ subroutine varSubstep(& if(globalPrintFlag)& write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt - ! increment fluxes, define weight applied to each substep + ! increment fluxes as average over timestep dt_wght = dtSubstep/dt - do iVar=1,size(flux_meta) if(count(fluxMask%var(iVar)%dat)>0) then From 8287edbb1af80368f7bb3969fa4927c541ed3617 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 17 Feb 2023 15:24:23 +0900 Subject: [PATCH 0547/1472] average flux should be computed over whole timestep like drainage --- build/source/engine/coupled_em.f90 | 2 +- build/source/engine/opSplittin.f90 | 26 +++++++++++++------------- build/source/engine/varSubstep.f90 | 12 ++++++------ 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 192f6f19a..100caa306 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -777,7 +777,7 @@ subroutine coupled_em(& nLayers, & ! intent(in): total number of layers nState, & ! intent(in): total number of layers dt_sub, & ! intent(in): length of the model sub-step - whole_step, & ! intent(in): length drainage pond drains in + whole_step, & ! intent(in): length of whole step for surface drainage and average flux (nsub==1), & ! intent(in): logical flag to denote the first substep firstInnerStep, & ! intent(in): flag to denote if the first time step in maxstep subStep computeVegFlux, & ! intent(in): logical flag to compute fluxes within the vegetation canopy diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 1e00d317a..e5b4d9b10 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -169,7 +169,7 @@ subroutine opSplittin(& nLayers, & ! intent(in): total number of layers nState, & ! intent(in): total number of state variables dt, & ! intent(in): time step (s) - tdrainage, & ! intent(in): length drainage pond drains in + whole_step, & ! intent(in): length of whole step for surface drainage and average flux firstSubStep, & ! intent(in): flag to denote first sub-step firstInnerStep, & ! intent(in): flag to denote if the first time step in maxstep subStep computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation @@ -218,7 +218,7 @@ subroutine opSplittin(& integer(i4b),intent(in) :: nLayers ! total number of layers integer(i4b),intent(in) :: nState ! total number of state variables real(rkind),intent(in) :: dt ! time step (seconds) - real(rkind),intent(in) :: tdrainage ! length drainage pond drains in + real(rkind),intent(in) :: whole_step ! length of whole step for surface drainage and average flux logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) ! input/output: data structures @@ -450,25 +450,25 @@ subroutine opSplittin(& call allocLocal(deriv_meta(:),deriv_data,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - ! intialize the flux counter on first inner step - if(firstInnerStep) then - do iVar=1,size(flux_meta) ! loop through fluxes - fluxCount%var(iVar)%dat(:) = 0 - end do + ! intialize the flux counter + do iVar=1,size(flux_meta) ! loop through fluxes + fluxCount%var(iVar)%dat(:) = 0 + end do + if(firstInnerStep) then ! initialize the model fluxes on first inner step do iVar=1,size(flux_meta) ! loop through fluxes if(flux2state_orig(iVar)%state1==integerMissing .and. flux2state_orig(iVar)%state2==integerMissing) cycle ! flux does not depend on state (e.g., input) if(flux2state_orig(iVar)%state1==iname_watCanopy .and. .not.computeVegFlux) cycle ! use input fluxes in cases where there is no canopy flux_data%var(iVar)%dat(:) = 0._rkind end do - - ! initialize derivatives on first inner step - do iVar=1,size(deriv_meta) - deriv_data%var(iVar)%dat(:) = 0._rkind - end do endif + ! initialize derivatives + do iVar=1,size(deriv_meta) + deriv_data%var(iVar)%dat(:) = 0._rkind + end do + ! ========================================================================================================================================== ! loop through different coupling strategies coupling: do ixCoupling=1,nCoupling @@ -757,7 +757,7 @@ subroutine opSplittin(& dt, & ! intent(in) : time step (s) dtInit, & ! intent(in) : initial time step (seconds) dt_min, & ! intent(in) : minimum time step (seconds) - tdrainage, & ! intent(in) : length drainage pond drains in + whole_step, & ! intent(in) : length of whole step for surface drainage and average flux nSubset, & ! intent(in) : total number of variables in the state subset doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature firstSubStep, & ! intent(in) : flag to denote first sub-step diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 3e4832486..c0eeb2964 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -94,7 +94,7 @@ subroutine varSubstep(& dt, & ! intent(in) : time step (s) dtInit, & ! intent(in) : initial time step (seconds) dt_min, & ! intent(in) : minimum time step (seconds) - tdrainage, & ! intent(in) : length drainage pond drains in + whole_step, & ! intent(in) : length of whole step for surface drainage and average flux nState, & ! intent(in) : total number of state variables doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature firstSubStep, & ! intent(in) : flag to denote first sub-step @@ -149,7 +149,7 @@ subroutine varSubstep(& real(rkind),intent(in) :: dt ! time step (seconds) real(rkind),intent(in) :: dtInit ! initial time step (seconds) real(rkind),intent(in) :: dt_min ! minimum time step (seconds) - real(rkind),intent(in) :: tdrainage ! length drainage pond drains in + real(rkind),intent(in) :: whole_step ! length of whole step for surface drainage and average flux integer(i4b),intent(in) :: nState ! total number of state variables logical(lgt),intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step @@ -324,7 +324,7 @@ subroutine varSubstep(& call systemSolvSundials(& ! input: model control dtSubstep, & ! intent(in): time step (s) - tdrainage, & ! intent(in): entire time step (s), right now same as dtSubstep but might change with operator splitting + whole_step, & ! intent(in): entire time step (s), right now same as dtSubstep but might change with operator splitting nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to denote first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call @@ -358,7 +358,7 @@ subroutine varSubstep(& call systemSolv(& ! input: model control dtSubstep, & ! intent(in): time step (s) - tdrainage, & ! intent(in): entire time step (s) + whole_step, & ! intent(in): entire time step (s) nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to denote first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call @@ -523,8 +523,8 @@ subroutine varSubstep(& if(globalPrintFlag)& write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt - ! increment fluxes as average over timestep - dt_wght = dtSubstep/dt + ! increment fluxes as average over entire timestep + dt_wght = dtSubstep/whole_step do iVar=1,size(flux_meta) if(count(fluxMask%var(iVar)%dat)>0) then From ae3a7cf7b8539641c6f1567c6786207786b486a2 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 17 Feb 2023 22:03:02 +0900 Subject: [PATCH 0548/1472] added more fluxes to canopy end of coupled_em calculation, I think every flux_data in the canopy routines should be in here but that would be quite lengthy, should look into a better way to do this with prettier code so it is correct --- build/source/engine/coupled_em.f90 | 26 +++++++++++++++++------ build/source/engine/opSplittin.f90 | 9 ++++++++ build/source/engine/soilLiqFlx.f90 | 2 +- build/source/engine/varSubstep.f90 | 34 ++++++++++++++++++++---------- 4 files changed, 52 insertions(+), 19 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 100caa306..c154ff15f 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -226,6 +226,9 @@ subroutine coupled_em(& real(rkind) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) + real(rkind) :: sumCanopyTranspiration ! sum of canopy transpiration (kg m-2 s-1) + real(rkind) :: sumLatHeatCanopyTrans ! sum of latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) + real(rkind) :: sumCanopySublimation ! sum of sublimation from the vegetation canopy ((kg m-2 s-1) real(rkind) :: sumSoilCompress ! sum of total soil compression real(rkind),allocatable :: sumLayerCompress(:) ! sum of soil compression by layer ! balance checks @@ -749,10 +752,13 @@ subroutine coupled_em(& endif ! initialize the total energy fluxes over whole step (modified in updateProg in opSplittin in varSubstep) - sumCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap = 0._rkind ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy = 0._rkind ! sensible heat flux from the canopy to the canopy air space (W m-2) - sumSoilCompress = 0._rkind ! total soil compression + sumCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = 0._rkind ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = 0._rkind ! sensible heat flux from the canopy to the canopy air space (W m-2) + sumCanopyTranspiration= 0._rkind ! canopy transpiration (kg m-2 s-1) + sumLatHeatCanopyTrans = 0._rkind ! latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) + sumCanopySublimation = 0._rkind ! sublimation from the vegetation canopy ((kg m-2 s-1) + sumSoilCompress = 0._rkind ! total soil compression allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._rkind ! soil compression by layer ! save volumetric ice content at the start of the step @@ -797,6 +803,9 @@ subroutine coupled_em(& sumCanopyEvaporation, & ! intent(inout): sum of canopy evaporation/condensation (kg m-2 s-1) sumLatHeatCanopyEvap, & ! intent(inout): sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) sumSenHeatCanopy, & ! intent(inout): sum of sensible heat flux from the canopy to the canopy air space (W m-2) + sumCanopyTranspiration, & ! intent(inout): sum of canopy transpiration (kg m-2 s-1) + sumLatHeatCanopyTrans, & ! intent(inout): sum of latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) + sumCanopySublimation, & ! intent(inout): sum of sublimation from the vegetation canopy ((kg m-2 s-1) sumSoilCompress, & ! intent(inout): sum of total soil compression sumLayerCompress, & ! intent(inout): sum of soil compression by layer ! output: model control @@ -851,9 +860,12 @@ subroutine coupled_em(& if(do_outer)then ! apply the energy fluxes - flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /whole_step ! canopy evaporation/condensation (kg m-2 s-1) - flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /whole_step ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /whole_step ! sensible heat flux from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /whole_step ! canopy evaporation/condensation (kg m-2 s-1) + flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /whole_step ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /whole_step ! sensible heat flux from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1)= sumCanopyTranspiration/whole_step ! canopy transpiration (kg m-2 s-1) + flux_data%var(iLookFLUX%scalarLatHeatCanopyTrans)%dat(1) = sumLatHeatCanopyTrans /whole_step ! latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) = sumCanopySublimation /whole_step ! sublimation from the vegetation canopy ((kg m-2 s-1) ! apply the soil compression diagnostics diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress/whole_step diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index e5b4d9b10..5c9a1cea6 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -189,6 +189,9 @@ subroutine opSplittin(& sumCanopyEvaporation, & ! intent(inout): sum of canopy evaporation/condensation (kg m-2 s-1) sumLatHeatCanopyEvap, & ! intent(inout): sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) sumSenHeatCanopy, & ! intent(inout): sum of sensible heat flux from the canopy to the canopy air space (W m-2) + sumCanopyTranspiration,&! intent(inout): sum of canopy transpiration (kg m-2 s-1) + sumLatHeatCanopyTrans,& ! intent(inout): sum of latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) + sumCanopySublimation, & ! intent(inout): sum of sublimation from the vegetation canopy ((kg m-2 s-1) sumSoilCompress, & ! intent(inout): sum of total soil compression sumLayerCompress, & ! intent(inout): sum of soil compression by layer ! output: model control @@ -237,6 +240,9 @@ subroutine opSplittin(& real(rkind),intent(out) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) real(rkind),intent(out) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) real(rkind),intent(out) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) + real(rkind),intent(out) :: sumCanopyTranspiration ! sum of canopy transpiration (kg m-2 s-1) + real(rkind),intent(out) :: sumLatHeatCanopyTrans ! sum of latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) + real(rkind),intent(out) :: sumCanopySublimation ! sum of sublimation from the vegetation canopy ((kg m-2 s-1) real(rkind),intent(out) :: sumSoilCompress ! sum of total soil compression real(rkind),intent(out) :: sumLayerCompress(:) ! sum of soil compression by layer ! output: model control @@ -784,6 +790,9 @@ subroutine opSplittin(& sumCanopyEvaporation, & ! intent(inout) : sum of canopy evaporation/condensation (kg m-2 s-1) sumLatHeatCanopyEvap, & ! intent(inout) : sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) sumSenHeatCanopy, & ! intent(inout) : sum of sensible heat flux from the canopy to the canopy air space (W m-2) + sumCanopyTranspiration, & ! intent(inout) : sum of canopy transpiration (kg m-2 s-1) + sumLatHeatCanopyTrans, & ! intent(inout) : sum of latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) + sumCanopySublimation, & ! intent(inout) : sum of sublimation from the vegetation canopy ((kg m-2 s-1) sumSoilCompress, & ! intent(inout) : sum of total soil compression sumLayerCompress, & ! intent(inout) : sum of soil compression by layer ! output: control diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index 84bab394b..df51f376c 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -92,7 +92,7 @@ subroutine soilLiqFlx(& ! input: model control nSoil, & ! intent(in): number of soil layers doInfiltrate, & ! intent(in): flag to compute infiltration - scalarSolution, & ! intent(in): flag to indicate the scalar solution + scalarSolution, & ! intent(in): flag to indicate the scalar solution deriv_desired, & ! intent(in): flag indicating if derivatives are desired ! input: trial state variables mLayerTempTrial, & ! intent(in): temperature (K) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index c0eeb2964..5f0b51b26 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -118,9 +118,12 @@ subroutine varSubstep(& deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables bvar_data, & ! intent(in) : model variables for the local basin ! energy fluxes - sumCanopyEvaporation,& !intent(inout): sum of canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap,& !intent(inout): sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy, & ! intent(inout) : sum of sensible heat flux from the canopy to the canopy air space (W m-2) + sumCanopyEvaporation, & ! intent(inout): sum of canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap, & ! intent(inout): sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy, & ! intent(inout): sum of sensible heat flux from the canopy to the canopy air space (W m-2) + sumCanopyTranspiration,& ! intent(inout): sum of canopy transpiration (kg m-2 s-1) + sumLatHeatCanopyTrans, & ! intent(inout): sum of latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) + sumCanopySublimation, & ! intent(inout): sum of sublimation from the vegetation canopy ((kg m-2 s-1) sumSoilCompress, & ! intent(inout) : sum of total soil compression sumLayerCompress, & ! intent(inout) : sum of soil compression by layer ! output: model control @@ -176,6 +179,9 @@ subroutine varSubstep(& real(rkind),intent(inout) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) real(rkind),intent(inout) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) real(rkind),intent(inout) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) + real(rkind),intent(inout) :: sumCanopyTranspiration ! sum of canopy transpiration (kg m-2 s-1) + real(rkind),intent(inout) :: sumLatHeatCanopyTrans ! sum of latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) + real(rkind),intent(inout) :: sumCanopySublimation ! sum of sublimation from the vegetation canopy ((kg m-2 s-1) real(rkind),intent(inout) :: sumSoilCompress ! sum of total soil compression real(rkind),intent(inout) :: sumLayerCompress(:) ! sum of soil compression by layer ! output: model control @@ -324,7 +330,7 @@ subroutine varSubstep(& call systemSolvSundials(& ! input: model control dtSubstep, & ! intent(in): time step (s) - whole_step, & ! intent(in): entire time step (s), right now same as dtSubstep but might change with operator splitting + whole_step, & ! intent(in): entire time step (s), right now same as dtSubstep but might change with operator splitting nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to denote first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call @@ -358,7 +364,7 @@ subroutine varSubstep(& call systemSolv(& ! input: model control dtSubstep, & ! intent(in): time step (s) - whole_step, & ! intent(in): entire time step (s) + whole_step, & ! intent(in): entire time step (s) nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to denote first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call @@ -498,13 +504,19 @@ subroutine varSubstep(& ! get the total energy fluxes (modified in updateProg) if(nrgFluxModified .or. indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then - sumCanopyEvaporation = sumCanopyEvaporation + dtSubstep*flux_temp%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dtSubstep*flux_temp%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy = sumSenHeatCanopy + dtSubstep*flux_temp%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) + sumCanopyEvaporation = sumCanopyEvaporation + dtSubstep*flux_temp%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dtSubstep*flux_temp%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = sumSenHeatCanopy + dtSubstep*flux_temp%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) + sumCanopyTranspiration= sumCanopyTranspiration+ dtSubstep*flux_temp%var(iLookFLUX%scalarCanopyTranspiration)%dat(1)! canopy transpiration (kg m-2 s-1) + sumLatHeatCanopyTrans = sumLatHeatCanopyTrans + dtSubstep*flux_temp%var(iLookFLUX%scalarLatHeatCanopyTrans)%dat(1) ! latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) + sumCanopySublimation = sumCanopySublimation + dtSubstep*flux_temp%var(iLookFLUX%scalarCanopySublimation)%dat(1) ! sublimation from the vegetation canopy ((kg m-2 s-1) else - sumCanopyEvaporation = sumCanopyEvaporation + dtSubstep*flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dtSubstep*flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy = sumSenHeatCanopy + dtSubstep*flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) + sumCanopyEvaporation = sumCanopyEvaporation + dtSubstep*flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dtSubstep*flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = sumSenHeatCanopy + dtSubstep*flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) + sumCanopyTranspiration= sumCanopyTranspiration+ dtSubstep*flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1)! canopy transpiration (kg m-2 s-1) + sumLatHeatCanopyTrans = sumLatHeatCanopyTrans + dtSubstep*flux_data%var(iLookFLUX%scalarLatHeatCanopyTrans)%dat(1) ! latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) + sumCanopySublimation = sumCanopySublimation + dtSubstep*flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) ! sublimation from the vegetation canopy ((kg m-2 s-1) endif ! if energy fluxes were modified ! get the total soil compression From d397ac02055189cfe770fedd12dee7a4ad821861 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 17 Feb 2023 22:42:21 +0900 Subject: [PATCH 0549/1472] turn off special flux update for canopy and add back in to only update top layer transpiration in comments ... leave off for now --- build/source/engine/coupled_em.f90 | 22 +++++++++++----------- build/source/engine/varSubstep.f90 | 10 +++++++++- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index c154ff15f..d0fafca73 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -860,19 +860,19 @@ subroutine coupled_em(& if(do_outer)then ! apply the energy fluxes - flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /whole_step ! canopy evaporation/condensation (kg m-2 s-1) - flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /whole_step ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /whole_step ! sensible heat flux from the canopy to the canopy air space (W m-2) - flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1)= sumCanopyTranspiration/whole_step ! canopy transpiration (kg m-2 s-1) - flux_data%var(iLookFLUX%scalarLatHeatCanopyTrans)%dat(1) = sumLatHeatCanopyTrans /whole_step ! latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) - flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) = sumCanopySublimation /whole_step ! sublimation from the vegetation canopy ((kg m-2 s-1) + !flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /whole_step ! canopy evaporation/condensation (kg m-2 s-1) + !flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /whole_step ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + !flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /whole_step ! sensible heat flux from the canopy to the canopy air space (W m-2) + !flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1)= sumCanopyTranspiration/whole_step ! canopy transpiration (kg m-2 s-1) + !flux_data%var(iLookFLUX%scalarLatHeatCanopyTrans)%dat(1) = sumLatHeatCanopyTrans /whole_step ! latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) + !flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) = sumCanopySublimation /whole_step ! sublimation from the vegetation canopy ((kg m-2 s-1) ! apply the soil compression diagnostics - diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress/whole_step - do iSoil=1,nSoil - if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& - diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) = sumLayerCompress(iSoil)/whole_step - end do + !diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress/whole_step + !do iSoil=1,nSoil + ! if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& + ! diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) = sumLayerCompress(iSoil)/whole_step + !end do deallocate(sumLayerCompress) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 5f0b51b26..13a3636eb 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -551,7 +551,15 @@ subroutine varSubstep(& ixMax=ubound(flux_data%var(iVar)%dat) do ixLayer=ixMin(1),ixMax(1) if(fluxMask%var(iVar)%dat(ixLayer)) then - flux_data%var(iVar)%dat(ixLayer) = flux_data%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght + + ! Makes Jacobian more diagonal if do this, but less correct + ! special case of the transpiration sink from soil layers: only computed for the top soil layer + !if(iVar==iLookFlux%mLayerTranspire)then + ! if(ixLayer==1) flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght + ! standard case + !else + flux_data%var(iVar)%dat(ixLayer) = flux_data%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght + !endif fluxCount%var(iVar)%dat(ixLayer) = fluxCount%var(iVar)%dat(ixLayer) + 1 endif end do From b69c8a9d2e06900474947c1b24a04ae274879fc2 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 17 Feb 2023 22:54:36 +0900 Subject: [PATCH 0550/1472] put fluxes back in --- build/source/engine/coupled_em.f90 | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index d0fafca73..c154ff15f 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -860,19 +860,19 @@ subroutine coupled_em(& if(do_outer)then ! apply the energy fluxes - !flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /whole_step ! canopy evaporation/condensation (kg m-2 s-1) - !flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /whole_step ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - !flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /whole_step ! sensible heat flux from the canopy to the canopy air space (W m-2) - !flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1)= sumCanopyTranspiration/whole_step ! canopy transpiration (kg m-2 s-1) - !flux_data%var(iLookFLUX%scalarLatHeatCanopyTrans)%dat(1) = sumLatHeatCanopyTrans /whole_step ! latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) - !flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) = sumCanopySublimation /whole_step ! sublimation from the vegetation canopy ((kg m-2 s-1) + flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /whole_step ! canopy evaporation/condensation (kg m-2 s-1) + flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /whole_step ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /whole_step ! sensible heat flux from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1)= sumCanopyTranspiration/whole_step ! canopy transpiration (kg m-2 s-1) + flux_data%var(iLookFLUX%scalarLatHeatCanopyTrans)%dat(1) = sumLatHeatCanopyTrans /whole_step ! latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) = sumCanopySublimation /whole_step ! sublimation from the vegetation canopy ((kg m-2 s-1) ! apply the soil compression diagnostics - !diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress/whole_step - !do iSoil=1,nSoil - ! if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& - ! diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) = sumLayerCompress(iSoil)/whole_step - !end do + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress/whole_step + do iSoil=1,nSoil + if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& + diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) = sumLayerCompress(iSoil)/whole_step + end do deallocate(sumLayerCompress) From ea4b8c9186a399bcb79c5b57de27129c16cd0ffc Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 17 Feb 2023 23:18:16 +0900 Subject: [PATCH 0551/1472] take out fluxes added --- build/source/engine/coupled_em.f90 | 12 ------------ build/source/engine/opSplittin.f90 | 9 --------- build/source/engine/varSubstep.f90 | 12 ------------ 3 files changed, 33 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index c154ff15f..20b1e2537 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -226,9 +226,6 @@ subroutine coupled_em(& real(rkind) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) - real(rkind) :: sumCanopyTranspiration ! sum of canopy transpiration (kg m-2 s-1) - real(rkind) :: sumLatHeatCanopyTrans ! sum of latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) - real(rkind) :: sumCanopySublimation ! sum of sublimation from the vegetation canopy ((kg m-2 s-1) real(rkind) :: sumSoilCompress ! sum of total soil compression real(rkind),allocatable :: sumLayerCompress(:) ! sum of soil compression by layer ! balance checks @@ -755,9 +752,6 @@ subroutine coupled_em(& sumCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) sumLatHeatCanopyEvap = 0._rkind ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) sumSenHeatCanopy = 0._rkind ! sensible heat flux from the canopy to the canopy air space (W m-2) - sumCanopyTranspiration= 0._rkind ! canopy transpiration (kg m-2 s-1) - sumLatHeatCanopyTrans = 0._rkind ! latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) - sumCanopySublimation = 0._rkind ! sublimation from the vegetation canopy ((kg m-2 s-1) sumSoilCompress = 0._rkind ! total soil compression allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._rkind ! soil compression by layer @@ -803,9 +797,6 @@ subroutine coupled_em(& sumCanopyEvaporation, & ! intent(inout): sum of canopy evaporation/condensation (kg m-2 s-1) sumLatHeatCanopyEvap, & ! intent(inout): sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) sumSenHeatCanopy, & ! intent(inout): sum of sensible heat flux from the canopy to the canopy air space (W m-2) - sumCanopyTranspiration, & ! intent(inout): sum of canopy transpiration (kg m-2 s-1) - sumLatHeatCanopyTrans, & ! intent(inout): sum of latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) - sumCanopySublimation, & ! intent(inout): sum of sublimation from the vegetation canopy ((kg m-2 s-1) sumSoilCompress, & ! intent(inout): sum of total soil compression sumLayerCompress, & ! intent(inout): sum of soil compression by layer ! output: model control @@ -863,9 +854,6 @@ subroutine coupled_em(& flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /whole_step ! canopy evaporation/condensation (kg m-2 s-1) flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /whole_step ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /whole_step ! sensible heat flux from the canopy to the canopy air space (W m-2) - flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1)= sumCanopyTranspiration/whole_step ! canopy transpiration (kg m-2 s-1) - flux_data%var(iLookFLUX%scalarLatHeatCanopyTrans)%dat(1) = sumLatHeatCanopyTrans /whole_step ! latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) - flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) = sumCanopySublimation /whole_step ! sublimation from the vegetation canopy ((kg m-2 s-1) ! apply the soil compression diagnostics diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress/whole_step diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 5c9a1cea6..e5b4d9b10 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -189,9 +189,6 @@ subroutine opSplittin(& sumCanopyEvaporation, & ! intent(inout): sum of canopy evaporation/condensation (kg m-2 s-1) sumLatHeatCanopyEvap, & ! intent(inout): sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) sumSenHeatCanopy, & ! intent(inout): sum of sensible heat flux from the canopy to the canopy air space (W m-2) - sumCanopyTranspiration,&! intent(inout): sum of canopy transpiration (kg m-2 s-1) - sumLatHeatCanopyTrans,& ! intent(inout): sum of latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) - sumCanopySublimation, & ! intent(inout): sum of sublimation from the vegetation canopy ((kg m-2 s-1) sumSoilCompress, & ! intent(inout): sum of total soil compression sumLayerCompress, & ! intent(inout): sum of soil compression by layer ! output: model control @@ -240,9 +237,6 @@ subroutine opSplittin(& real(rkind),intent(out) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) real(rkind),intent(out) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) real(rkind),intent(out) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) - real(rkind),intent(out) :: sumCanopyTranspiration ! sum of canopy transpiration (kg m-2 s-1) - real(rkind),intent(out) :: sumLatHeatCanopyTrans ! sum of latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) - real(rkind),intent(out) :: sumCanopySublimation ! sum of sublimation from the vegetation canopy ((kg m-2 s-1) real(rkind),intent(out) :: sumSoilCompress ! sum of total soil compression real(rkind),intent(out) :: sumLayerCompress(:) ! sum of soil compression by layer ! output: model control @@ -790,9 +784,6 @@ subroutine opSplittin(& sumCanopyEvaporation, & ! intent(inout) : sum of canopy evaporation/condensation (kg m-2 s-1) sumLatHeatCanopyEvap, & ! intent(inout) : sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) sumSenHeatCanopy, & ! intent(inout) : sum of sensible heat flux from the canopy to the canopy air space (W m-2) - sumCanopyTranspiration, & ! intent(inout) : sum of canopy transpiration (kg m-2 s-1) - sumLatHeatCanopyTrans, & ! intent(inout) : sum of latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) - sumCanopySublimation, & ! intent(inout) : sum of sublimation from the vegetation canopy ((kg m-2 s-1) sumSoilCompress, & ! intent(inout) : sum of total soil compression sumLayerCompress, & ! intent(inout) : sum of soil compression by layer ! output: control diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 13a3636eb..53b0a304a 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -121,9 +121,6 @@ subroutine varSubstep(& sumCanopyEvaporation, & ! intent(inout): sum of canopy evaporation/condensation (kg m-2 s-1) sumLatHeatCanopyEvap, & ! intent(inout): sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) sumSenHeatCanopy, & ! intent(inout): sum of sensible heat flux from the canopy to the canopy air space (W m-2) - sumCanopyTranspiration,& ! intent(inout): sum of canopy transpiration (kg m-2 s-1) - sumLatHeatCanopyTrans, & ! intent(inout): sum of latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) - sumCanopySublimation, & ! intent(inout): sum of sublimation from the vegetation canopy ((kg m-2 s-1) sumSoilCompress, & ! intent(inout) : sum of total soil compression sumLayerCompress, & ! intent(inout) : sum of soil compression by layer ! output: model control @@ -179,9 +176,6 @@ subroutine varSubstep(& real(rkind),intent(inout) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) real(rkind),intent(inout) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) real(rkind),intent(inout) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) - real(rkind),intent(inout) :: sumCanopyTranspiration ! sum of canopy transpiration (kg m-2 s-1) - real(rkind),intent(inout) :: sumLatHeatCanopyTrans ! sum of latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) - real(rkind),intent(inout) :: sumCanopySublimation ! sum of sublimation from the vegetation canopy ((kg m-2 s-1) real(rkind),intent(inout) :: sumSoilCompress ! sum of total soil compression real(rkind),intent(inout) :: sumLayerCompress(:) ! sum of soil compression by layer ! output: model control @@ -507,16 +501,10 @@ subroutine varSubstep(& sumCanopyEvaporation = sumCanopyEvaporation + dtSubstep*flux_temp%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dtSubstep*flux_temp%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) sumSenHeatCanopy = sumSenHeatCanopy + dtSubstep*flux_temp%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) - sumCanopyTranspiration= sumCanopyTranspiration+ dtSubstep*flux_temp%var(iLookFLUX%scalarCanopyTranspiration)%dat(1)! canopy transpiration (kg m-2 s-1) - sumLatHeatCanopyTrans = sumLatHeatCanopyTrans + dtSubstep*flux_temp%var(iLookFLUX%scalarLatHeatCanopyTrans)%dat(1) ! latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) - sumCanopySublimation = sumCanopySublimation + dtSubstep*flux_temp%var(iLookFLUX%scalarCanopySublimation)%dat(1) ! sublimation from the vegetation canopy ((kg m-2 s-1) else sumCanopyEvaporation = sumCanopyEvaporation + dtSubstep*flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dtSubstep*flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) sumSenHeatCanopy = sumSenHeatCanopy + dtSubstep*flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) - sumCanopyTranspiration= sumCanopyTranspiration+ dtSubstep*flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1)! canopy transpiration (kg m-2 s-1) - sumLatHeatCanopyTrans = sumLatHeatCanopyTrans + dtSubstep*flux_data%var(iLookFLUX%scalarLatHeatCanopyTrans)%dat(1) ! latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) - sumCanopySublimation = sumCanopySublimation + dtSubstep*flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) ! sublimation from the vegetation canopy ((kg m-2 s-1) endif ! if energy fluxes were modified ! get the total soil compression From 0422a398c84a15b2d2dc3701347c6cc052fc652e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 23 Feb 2023 21:14:05 +0900 Subject: [PATCH 0552/1472] files in place for BMI with most of functions and makefiles, not debugged yet --- build/build_cmakeBMI_cop | 5 + build/build_cmakeBMI_gra | 5 + build/build_cmakeBMI_mac | 5 + build/build_cmakeSundials_ric | 8 - build/build_summa_ric | 1 - build/my_makefile_cop | 51 +- build/my_makefile_gra | 51 +- build/my_makefile_mac | 51 +- build/my_makefile_ric | 439 ---- .../multi_driverDoesAllTheotherThings.f90 | 43 - build/source/driver/summa_bmi.f90 | 2069 ----------------- build/source/driver/summa_driver.f90 | 1029 +++++++- build/source/driver/summa_run.f90 | 52 + build/source/driver/summa_runBMI.f90 | 90 + build/source/driver/test_bmi.f90 | 52 - sundials/flags_params.txt | 26 - sundials/installation.txt | 79 - 17 files changed, 1220 insertions(+), 2836 deletions(-) create mode 100755 build/build_cmakeBMI_cop create mode 100755 build/build_cmakeBMI_gra create mode 100755 build/build_cmakeBMI_mac delete mode 100755 build/build_cmakeSundials_ric delete mode 100755 build/build_summa_ric delete mode 100644 build/my_makefile_ric delete mode 100755 build/source/driver/multi_driverDoesAllTheotherThings.f90 delete mode 100755 build/source/driver/summa_bmi.f90 create mode 100644 build/source/driver/summa_run.f90 create mode 100644 build/source/driver/summa_runBMI.f90 delete mode 100644 build/source/driver/test_bmi.f90 delete mode 100644 sundials/flags_params.txt delete mode 100644 sundials/installation.txt diff --git a/build/build_cmakeBMI_cop b/build/build_cmakeBMI_cop new file mode 100755 index 000000000..d08d728eb --- /dev/null +++ b/build/build_cmakeBMI_cop @@ -0,0 +1,5 @@ +# from ../../bmi/builddir, run +# cp ../../summa/build/build_cmakeBMI_cop build_cmake +# run script from the builddir directory with ./build_cmake + +cmake ../../bmi-fortran/ -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/globalhome/gwu479/HPC/SummaSundials/bmi/instdir \ No newline at end of file diff --git a/build/build_cmakeBMI_gra b/build/build_cmakeBMI_gra new file mode 100755 index 000000000..2d8716a1b --- /dev/null +++ b/build/build_cmakeBMI_gra @@ -0,0 +1,5 @@ +# from ../../bmi/builddir, run +# cp ../../summa/build/build_cmakeBMI_gra build_cmake +# run script from the builddir directory with ./build_cmake + +cmake ../../bmi-fortran/ -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/home/avanb/SummaSundials/bmi/instdir \ No newline at end of file diff --git a/build/build_cmakeBMI_mac b/build/build_cmakeBMI_mac new file mode 100755 index 000000000..1069760ce --- /dev/null +++ b/build/build_cmakeBMI_mac @@ -0,0 +1,5 @@ +# from ../../bmi/builddir, run +# cp ../../summa/build/build_cmakeBMI_mac build_cmake +# run script from the builddir directory with ./build_cmake + +cmake ../../bmi-fortran/ -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/Users/amedin/Research/SummaSundials/bmi/instdir \ No newline at end of file diff --git a/build/build_cmakeSundials_ric b/build/build_cmakeSundials_ric deleted file mode 100755 index a563ce505..000000000 --- a/build/build_cmakeSundials_ric +++ /dev/null @@ -1,8 +0,0 @@ -# from ../../sundials/builddir, run -# cp ../../summa/build/build_cmakeSundials_ric build_cmake -# run script from the builddir directory with ./build_cmake -# Note, using -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF because modified constraint code and did not modify examples - -cmake ../../sundials-6.3.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/u1/gwu479/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/u1/gwu479/SummaSundials/sundials/instdir/examples - - diff --git a/build/build_summa_ric b/build/build_summa_ric deleted file mode 100755 index b37eca78e..000000000 --- a/build/build_summa_ric +++ /dev/null @@ -1 +0,0 @@ -make -f my_makefile_ric diff --git a/build/my_makefile_cop b/build/my_makefile_cop index fd5745955..3a50d8cfa 100644 --- a/build/my_makefile_cop +++ b/build/my_makefile_cop @@ -15,13 +15,12 @@ # To troubleshoot your paths and setup, type 'make check' # # At a minimum you will need to set the following: -# * F_MASTER - top level summa directory +# * F_MASTER_TOP - top level directory # * FC - compiler suite # * FC_EXE - compiler executable # * INCLUDES - path to include files # * ILDFLAGS - path to libraries to include # * LIBRARIES - libraries to include -# * DIR_SUNDIALS - installation sundials directory # # Some further options can be specified for OpenMP, etc. See in Part 0 of # the Makefile. You don't need to make any changes in PART 1 and @@ -40,7 +39,8 @@ # Define core directory below which everything resides. This is the # parent directory of the 'build' directory -F_MASTER =/globalhome/gwu479/HPC/SummaSundials/summa +F_MASTER_TOP = /globalhome/gwu479/HPC/SummaSundials +F_MASTER = $(F_MASTER_TOP)/summa # Define the Fortran Compiler. If you are using gfortran, then this needs # to be version 6 or higher. This variable is simply used to select the right @@ -65,10 +65,14 @@ INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include LDFLAGS = -L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas -DIR_SUNDIALS=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir +DIR_SUNDIALS= $(F_MASTER_TOP)/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod +DIR_BMI= $(F_MASTER_TOP)/instdir +INC_BMI=-I$(DIR_BMI)/include +LIB_BMI=-L$(DIR_BMI)/lib -lbmif + # Eventually we plan move to a real configure script, but for now we like # to keep track of successful compilations of SUMMA on different platforms # and with different compilers. If you are successful compiling SUMMA, @@ -199,11 +203,6 @@ SUMMA_UTILMS= \ matrixOper.f90 UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) -# interface modules -SUMMA_INTERFACE= \ - summa_bmi.f90 -INTERFACE = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_INTERFACE)) - # Model guts SUMMA_MODGUT= \ MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) @@ -305,9 +304,9 @@ NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) # ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER $(INTERFACE) +SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) -# Define the driver routine +# Define the driver routines SUMMA_DRIVER= \ summa_type.f90 \ summa_util.f90 \ @@ -320,17 +319,22 @@ SUMMA_DRIVER= \ summa_forcing.f90 \ summa_modelRun.f90 \ summa_writeOutput.f90 \ - summa_driver.f90 + summa_driver.f90 DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) +# Define SUMMA running infrastructure +SUMMA_RUN = \ + summa_run.f90 +SUMMA = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_RUN)) + # Define BMI testing infrastructure BMI_TEST = \ - test_bmi.f90 + summa_runBMI.f90 BMI = $(patsubst %, $(DRIVER_DIR)/%, $(BMI_TEST)) # Define the executable DRIVER__EX = summa_sundials.exe -BMI__EX = test_bmi.exe +BMI__EX = summa_bmi.exe # Define version number VERSIONFILE = $(DRIVER_DIR)/summaversion.inc @@ -386,6 +390,9 @@ check: $(info SUNDIALSDIR: $(DIR_SUNDIALS)) $(info INC_SUNDIALS: $(INC_SUNDIALS)) $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) + $(info BMIDIR: $(DIR_BMI)) + $(info INC_BMI: $(INC_BMI)) + $(info LIB_BMI: $(LIB_BMI)) $(info) # update version information @@ -399,26 +406,22 @@ update_version: compile_noah: $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) -# compile common routines -compile_comm: - $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) - # compile SUMMA routines compile_summa: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ - $(INCLUDES) $(INC_SUNDIALS) + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(SUMMA) \ + $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) compile_bmi: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(BMI) \ - $(INCLUDES) $(INC_SUNDIALS) + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(BMI) \ + $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) # link routines link: - $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) + $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(DRIVER__EX) # link routines link_bmi: - $(FC_EXE) -pg -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(BMI__EX) + $(FC_EXE) -pg -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(BMI__EX) # Remove object files clean: diff --git a/build/my_makefile_gra b/build/my_makefile_gra index 7d5b92f66..bf05f5c31 100644 --- a/build/my_makefile_gra +++ b/build/my_makefile_gra @@ -15,13 +15,12 @@ # To troubleshoot your paths and setup, type 'make check' # # At a minimum you will need to set the following: -# * F_MASTER - top level summa directory +# * F_MASTER_TOP - top level directory # * FC - compiler suite # * FC_EXE - compiler executable # * INCLUDES - path to include files # * ILDFLAGS - path to libraries to include # * LIBRARIES - libraries to include -# * DIR_SUNDIALS - installation sundials directory # # Some further options can be specified for OpenMP, etc. See in Part 0 of # the Makefile. You don't need to make any changes in PART 1 and @@ -40,7 +39,8 @@ # Define core directory below which everything resides. This is the # parent directory of the 'build' directory -F_MASTER =/home/avanb/SummaSundials/summa +F_MASTER_TOP = /home/avanb/SummaSundials +F_MASTER = $(F_MASTER_TOP)/summa # Define the Fortran Compiler. If you are using gfortran, then this needs # to be version 6 or higher. This variable is simply used to select the right @@ -65,10 +65,14 @@ INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include LDFLAGS = -L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas -DIR_SUNDIALS=/home/avanb/SummaSundials/sundials/instdir +DIR_SUNDIALS= $(F_MASTER_TOP)/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod +DIR_BMI= $(F_MASTER_TOP)/instdir +INC_BMI=-I$(DIR_BMI)/include +LIB_BMI=-L$(DIR_BMI)/lib -lbmif + # Eventually we plan move to a real configure script, but for now we like # to keep track of successful compilations of SUMMA on different platforms # and with different compilers. If you are successful compiling SUMMA, @@ -199,11 +203,6 @@ SUMMA_UTILMS= \ matrixOper.f90 UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) -# interface modules -SUMMA_INTERFACE= \ - summa_bmi.f90 -INTERFACE = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_INTERFACE)) - # Model guts SUMMA_MODGUT= \ MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) @@ -305,9 +304,9 @@ NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) # ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER $(INTERFACE) +SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) -# Define the driver routine +# Define the driver routines SUMMA_DRIVER= \ summa_type.f90 \ summa_util.f90 \ @@ -320,17 +319,22 @@ SUMMA_DRIVER= \ summa_forcing.f90 \ summa_modelRun.f90 \ summa_writeOutput.f90 \ - summa_driver.f90 + summa_driver.f90 DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) +# Define SUMMA running infrastructure +SUMMA_RUN = \ + summa_run.f90 +SUMMA = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_RUN)) + # Define BMI testing infrastructure BMI_TEST = \ - test_bmi.f90 + summa_runBMI.f90 BMI = $(patsubst %, $(DRIVER_DIR)/%, $(BMI_TEST)) # Define the executable DRIVER__EX = summa_sundials.exe -BMI__EX = test_bmi.exe +BMI__EX = summa_bmi.exe # Define version number VERSIONFILE = $(DRIVER_DIR)/summaversion.inc @@ -386,6 +390,9 @@ check: $(info SUNDIALSDIR: $(DIR_SUNDIALS)) $(info INC_SUNDIALS: $(INC_SUNDIALS)) $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) + $(info BMIDIR: $(DIR_BMI)) + $(info INC_BMI: $(INC_BMI)) + $(info LIB_BMI: $(LIB_BMI)) $(info) # update version information @@ -399,26 +406,22 @@ update_version: compile_noah: $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) -# compile common routines -compile_comm: - $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) - # compile SUMMA routines compile_summa: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ - $(INCLUDES) $(INC_SUNDIALS) + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(SUMMA) \ + $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) compile_bmi: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(BMI) \ - $(INCLUDES) $(INC_SUNDIALS) + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(BMI) \ + $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) # link routines link: - $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) + $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(DRIVER__EX) # link routines link_bmi: - $(FC_EXE) -pg -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(BMI__EX) + $(FC_EXE) -pg -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(BMI__EX) # Remove object files clean: diff --git a/build/my_makefile_mac b/build/my_makefile_mac index 3d0ad1940..75fc8e0b0 100644 --- a/build/my_makefile_mac +++ b/build/my_makefile_mac @@ -15,13 +15,12 @@ # To troubleshoot your paths and setup, type 'make check' # # At a minimum you will need to set the following: -# * F_MASTER - top level summa directory +# * F_MASTER_TOP - top level directory # * FC - compiler suite # * FC_EXE - compiler executable # * INCLUDES - path to include files # * LDFLAGS - path to libraries to include # * LIBRARIES - libraries to include -# * DIR_SUNDIALS - installation sundials directory # # Some further options can be specified for OpenMP, etc. See in Part 0 of # the Makefile. You don't need to make any changes in PART 1 and @@ -40,7 +39,8 @@ # Define core directory below which everything resides. This is the # parent directory of the 'build' directory -F_MASTER = /Users/amedin/Research/SummaSundials/summa +F_MASTER_TOP = /Users/amedin/Research/SummaSundials +F_MASTER = $(F_MASTER_TOP)/summa # Define the Fortran Compiler. If you are using gfortran, then this needs # to be version 6 or higher. This variable is simply used to select the right @@ -65,10 +65,14 @@ INCLUDES = -I/opt/local/include -I/opt/local/lib LDFLAGS = -L/opt/local/lib LIBRARIES = $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf -DIR_SUNDIALS=/Users/amedin/Research/SummaSundials/sundials/instdir +DIR_SUNDIALS= $(F_MASTER_TOP)/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod +DIR_BMI= $(F_MASTER_TOP)/instdir +INC_BMI=-I$(DIR_BMI)/include +LIB_BMI=-L$(DIR_BMI)/lib -lbmif + # Eventually we plan move to a real configure script, but for now we like # to keep track of successful compilations of SUMMA on different platforms # and with different compilers. If you are successful compiling SUMMA, @@ -199,11 +203,6 @@ SUMMA_UTILMS= \ matrixOper.f90 UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) -# interface modules -SUMMA_INTERFACE= \ - summa_bmi.f90 -INTERFACE = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_INTERFACE)) - # Model guts SUMMA_MODGUT= \ MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) @@ -305,9 +304,9 @@ NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) # ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER $(INTERFACE) +SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) -# Define the driver routine +# Define the driver routines SUMMA_DRIVER= \ summa_type.f90 \ summa_util.f90 \ @@ -320,17 +319,22 @@ SUMMA_DRIVER= \ summa_forcing.f90 \ summa_modelRun.f90 \ summa_writeOutput.f90 \ - summa_driver.f90 + summa_driver.f90 DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) +# Define SUMMA running infrastructure +SUMMA_RUN = \ + summa_run.f90 +SUMMA = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_RUN)) + # Define BMI testing infrastructure BMI_TEST = \ - test_bmi.f90 + summa_runBMI.f90 BMI = $(patsubst %, $(DRIVER_DIR)/%, $(BMI_TEST)) # Define the executable DRIVER__EX = summa_sundials.exe -BMI__EX = test_bmi.exe +BMI__EX = summa_bmi.exe # Define version number VERSIONFILE = $(DRIVER_DIR)/summaversion.inc @@ -386,6 +390,9 @@ check: $(info SUNDIALSDIR: $(DIR_SUNDIALS)) $(info INC_SUNDIALS: $(INC_SUNDIALS)) $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) + $(info BMIDIR: $(DIR_BMI)) + $(info INC_BMI: $(INC_BMI)) + $(info LIB_BMI: $(LIB_BMI)) $(info) # update version information @@ -399,26 +406,22 @@ update_version: compile_noah: $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) -# compile common routines -compile_comm: - $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) - # compile SUMMA routines compile_summa: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ - $(INCLUDES) $(INC_SUNDIALS) + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(SUMMA) \ + $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) compile_bmi: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(BMI) \ - $(INCLUDES) $(INC_SUNDIALS) + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(BMI) \ + $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) # link routines link: - $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) + $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(DRIVER__EX) # link routines link_bmi: - $(FC_EXE) -pg -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(BMI__EX) + $(FC_EXE) -pg -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(BMI__EX) # Remove object files clean: diff --git a/build/my_makefile_ric b/build/my_makefile_ric deleted file mode 100644 index 29a553613..000000000 --- a/build/my_makefile_ric +++ /dev/null @@ -1,439 +0,0 @@ -#======================================================================== -# Makefile to compile SUMMA SUNDIALS -#======================================================================== -# -# Recommended use: Copy this file to Makefile.local, edit it to your -# heart's content, and then run `make -f build/Makefile.local` from -# your top level SUMMA directory. Don't include the Makefile.local in -# any pull requests you make. -# -# Note that Makefile configurations that we commonly use can be found on -# the SUMMA wiki at: -# https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration -# feel free to add yours to that page. -# -# To troubleshoot your paths and setup, type 'make check' -# -# At a minimum you will need to set the following: -# * F_MASTER - top level summa directory -# * FC - compiler suite -# * FC_EXE - compiler executable -# * INCLUDES - path to include files -# * ILDFLAGS - path to libraries to include -# * LIBRARIES - libraries to include -# * DIR_SUNDIALS - installation sundials directory -# -# Some further options can be specified for OpenMP, etc. See in Part 0 of -# the Makefile. You don't need to make any changes in PART 1 and -# following unless you are doing SUMMA development and changed what -# needs to be compiled - -#======================================================================== -# PART 0: User-configurable part -#======================================================================== - -# The variables can be specified in one of two ways: -# * delete the '##' in front of the variable, fill out the entry, -# save the file and run make -# * make no changes to this file, but specify the variables in your -# environment before you run make - -# Define core directory below which everything resides. This is the -# parent directory of the 'build' directory -F_MASTER =/u1/gwu479/SummaSundials/summa - -# Define the Fortran Compiler. If you are using gfortran, then this needs -# to be version 6 or higher. This variable is simply used to select the right -# compiler flags in the ifeq statements in this Makefile. The compiler -# executable is set separately as FC_EXE -# Currently this is either gfortran or ifort -FC = gfortran - -# Define the path for the compiler executable. This is the actual executable -# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin/gfortran-mp-11 -# FC and FC_EXE have to be consistent -FC_EXE = gfortran - -# Define the NetCDF and LAPACK libraries and path to include files. -# INCLUDES needs to be of the form (no quotes around the string): -# INCLUDES = -I -I -I<...> -I -# LIBRARIES needs to be of the form ( no quotes around the string): -# LIBRARIES = '-L -lnetcdff -L -lblas -L -l' -# If none of this makes sense, please talk to your system -# administrator. -INCLUDES = -I/usr/include -I/usr/local/include -LDFLAGS = -L/usr/local/lib -LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas -lnetcdf - -DIR_SUNDIALS=/u1/gwu479/SummaSundials/sundials/instdir -INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran -LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod - -# Eventually we plan move to a real configure script, but for now we like -# to keep track of successful compilations of SUMMA on different platforms -# and with different compilers. If you are successful compiling SUMMA, -# please add your configuration (operating system and compiler plus -# part 0 of the Makefile) to the SUMMA wiki on github. - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# ------------ define compiler flags ---------------------------------------- - -# define open MP flags -isOpenMP = -FLAGS_OMP = -LIBOPENMP = - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# gfortran compiler flags -ifeq "$(FC)" "gfortran" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -fopenmp - endif - -# Production runs -FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) - -# Debug runs -#FLAGS_NOAH = -p -g -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -#FLAGS_COMM = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -#FLAGS_SUMMA = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds - -endif - -# ifort compiler flags -ifeq "$(FC)" "ifort" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -qopenmp - endif - -# Production runs -FLAGS_NOAH = -O3 -noerror_limit -FR -auto -fltconsistency $(FLAGS_OMP) -FLAGS_COMM = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) - -# Debug runs -#FLAGS_NOAH = -O0 -p -g -warn nounused -noerror_limit -FR -auto -WB -traceback -fltconsistency -#FLAGS_COMM = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -#FLAGS_SUMMA = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -endif - -#======================================================================== -# PART 1: Define directory paths -#======================================================================== - -# Core directory that contains source code -F_KORE_DIR = $(F_MASTER)/build/source - -# Location of the compiled modules -MOD_PATH = $(F_MASTER)/build - -# Define the directory for the executables -EXE_PATH = $(F_MASTER)/bin - -#======================================================================== -# PART 2: Assemble all of the SUMMA sub-routines -#======================================================================== - -# Define directories -DRIVER_DIR = $(F_KORE_DIR)/driver -HOOKUP_DIR = $(F_KORE_DIR)/hookup -NETCDF_DIR = $(F_KORE_DIR)/netcdf -DSHARE_DIR = $(F_KORE_DIR)/dshare -NUMREC_DIR = $(F_KORE_DIR)/numrec -NOAHMP_DIR = $(F_KORE_DIR)/noah-mp -ENGINE_DIR = $(F_KORE_DIR)/engine - -# utilities -SUMMA_NRUTIL= \ - nrtype.f90 \ - f2008funcs.f90 \ - nr_utility.f90 -NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) - -# -# Numerical recipes procedures -# NOTE: all numerical recipes procedures are now replaced with free versions -SUMMA_NRPROC= \ - expIntegral.f90 \ - spline_int.f90 -NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) - -# Hook-up modules (set files and directory paths) -SUMMA_HOOKUP= \ - ascii_util.f90 \ - summaFileManager.f90 - -HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) - -# Data modules -SUMMA_DATAMS= \ - multiconst.f90 \ - var_lookup.f90 \ - data_types.f90 \ - globalData.f90 \ - flxMapping.f90 \ - get_ixname.f90 \ - popMetadat.f90 \ - outpt_stat.f90 -DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) - -# utility modules -SUMMA_UTILMS= \ - time_utils.f90 \ - mDecisions.f90 \ - snow_utils.f90 \ - soil_utils.f90 \ - soil_utilsAddSundials.f90 \ - updatState.f90 \ - updatStateSundials.f90 \ - matrixOper.f90 -UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) - -# interface modules -SUMMA_INTERFACE= \ - summa_bmi.f90 -INTERFACE = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_INTERFACE)) - -# Model guts -SUMMA_MODGUT= \ - MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) - -# Solver -SUMMA_SOLVER= \ - vegPhenlgy.f90 \ - diagn_evar.f90 \ - stomResist.f90 \ - groundwatr.f90 \ - vegSWavRad.f90 \ - vegNrgFlux.f90 \ - ssdNrgFlux.f90 \ - vegLiqFlux.f90 \ - snowLiqFlx.f90 \ - soilLiqFlx.f90 \ - bigAquifer.f90 \ - computFlux.f90 \ - type4IDA.f90 \ - tol4IDA.f90 \ - computEnthalpy.f90 \ - computHeatCap.f90 \ - computThermConduct.f90 \ - computResid.f90 \ - computJacob.f90 \ - eval8summa.f90 \ - summaSolve.f90 \ - systemSolv.f90 \ - computResidSundials.f90 \ - eval8summaSundials.f90 \ - computJacobSundials.f90 \ - computSnowDepth.f90 \ - summaSolveSundialsIDA.f90 \ - systemSolvSundials.f90 \ - varSubstep.f90 \ - varSubstepSundials.f90 \ - opSplittin.f90 \ - coupled_em.f90 \ - run_oneHRU.f90 \ - run_oneGRU.f90 -SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) - -# Define routines for SUMMA preliminaries -SUMMA_PRELIM= \ - conv_funcs.f90 \ - sunGeomtry.f90 \ - convE2Temp.f90 \ - allocspace.f90 \ - checkStruc.f90 \ - childStruc.f90 \ - ffile_info.f90 \ - read_attrb.f90 \ - read_pinit.f90 \ - pOverwrite.f90 \ - read_param.f90 \ - paramCheck.f90 \ - check_icond.f90 -PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) - -SUMMA_NOAHMP= \ - module_model_constants.F \ - module_sf_noahutl.F \ - module_sf_noahlsm.F \ - module_sf_noahmplsm.F -NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) - -# Define routines for the SUMMA model runs -SUMMA_MODRUN = \ - indexState.f90 \ - getVectorz.f90 \ - t2enthalpy.f90 \ - updateVars.f90 \ - updateVarsSundials.f90 \ - var_derive.f90 \ - read_force.f90 \ - derivforce.f90 \ - snowAlbedo.f90 \ - canopySnow.f90 \ - tempAdjust.f90 \ - snwCompact.f90 \ - layerMerge.f90 \ - layerDivide.f90 \ - volicePack.f90 \ - qTimeDelay.f90 -MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) - -# Define routines for the solver -SUMMA_MSOLVE = \ - -# Define NetCDF routines -SUMMA_NETCDF = \ - netcdf_util.f90 \ - def_output.f90 \ - modelwrite.f90 \ - read_icond.f90 -NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) - -# ... stitch together common programs -COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) - -# ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER $(INTERFACE) - -# Define the driver routine -SUMMA_DRIVER= \ - summa_type.f90 \ - summa_util.f90 \ - summa_alarms.f90 \ - summa_globalData.f90 \ - summa_defineOutput.f90 \ - summa_init.f90 \ - summa_setup.f90 \ - summa_restart.f90 \ - summa_forcing.f90 \ - summa_modelRun.f90 \ - summa_writeOutput.f90 \ - summa_driver.f90 -DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) - -# Define BMI testing infrastructure -BMI_TEST = \ - test_bmi.f90 -BMI = $(patsubst %, $(DRIVER_DIR)/%, $(BMI_TEST)) - -# Define the executable -DRIVER__EX = summa_sundials.exe -BMI__EX = test_bmi.exe - -# Define version number -VERSIONFILE = $(DRIVER_DIR)/summaversion.inc -VERSION = $(shell git tag | tail -n 1) -BULTTIM = $(shell date) -GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') -GITHASH = $(shell git rev-parse HEAD) - -#======================================================================== -# PART 3: Checks -#====================================================================== -# make sure that the paths are defined. These are just some high level checks -ifndef F_MASTER - $(error F_MASTER is undefined) -endif -ifndef FC - $(error FC is undefined: Specify your compiler) -endif -ifndef FC_EXE - $(error FC_EXE is undefined: Specify your compiler executable) -endif -ifndef FLAGS_SUMMA - $(error Specify flags for your compiler: $(FC)) -endif -ifndef INCLUDES - $(error INCLUDES is undefined) -endif -ifndef LIBRARIES - $(error LIBRARIES is undefined) -endif - -#======================================================================== -# PART 4: compilation -#====================================================================== - -# Compile -all: compile_noah compile_comm compile_summa link clean install -part: compile_noah compile_comm compile_summa -bmi: compile_noah compile_comm compile_bmi link_bmi clean install_test - - -check: - $(info) - $(info Displaying make variables:) - $(info F_MASTER : $(F_MASTER)) - $(info EXE_PATH : $(EXE_PATH)) - $(info FC : $(FC)) - $(info INCLUDES : $(INCLUDES)) - $(info LIBRARIES : $(LIBRARIES)) - $(info FLAGS_NOAH : $(FLAGS_NOAH)) - $(info FLAGS_COMM : $(FLAGS_COMM)) - $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) - $(info SUNDIALSDIR: $(DIR_SUNDIALS)) - $(info INC_SUNDIALS: $(INC_SUNDIALS)) - $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) - $(info) - -# update version information -update_version: - echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) - echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) - -# compile Noah-MP routines -compile_noah: - $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) - -# compile common routines -compile_comm: - $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) - -# compile SUMMA routines -compile_summa: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ - $(INCLUDES) $(INC_SUNDIALS) - -compile_bmi: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(BMI) \ - $(INCLUDES) $(INC_SUNDIALS) - -# link routines -link: - $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) - -# link routines -link_bmi: - $(FC_EXE) -pg -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(BMI__EX) - -# Remove object files -clean: - rm -f *.o - rm -f *.mod - rm -f soil_veg_gen_parm__genmod.f90 - -# Copy the executable to the bin directory -install: - @mkdir -p $(EXE_PATH) - @mv $(DRIVER__EX) $(EXE_PATH) - $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) - -# Copy the executable to the bin directory -install_test: - @mkdir -p $(EXE_PATH) - @mv $(BMI__EX) $(EXE_PATH) - $(info $(BMI__EX) successfully installed in $(EXE_PATH)) diff --git a/build/source/driver/multi_driverDoesAllTheotherThings.f90 b/build/source/driver/multi_driverDoesAllTheotherThings.f90 deleted file mode 100755 index 3db87c717..000000000 --- a/build/source/driver/multi_driverDoesAllTheotherThings.f90 +++ /dev/null @@ -1,43 +0,0 @@ -! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2015 NCAR/RAL -! -! This file is part of SUMMA -! -! For more information see: http://www.ral.ucar.edu/projects/summa -! -! This program is free software: you can redistribute it and/or modify -! it under the terms of the GNU General Public License as published by -! the Free Software Foundation, either version 3 of the License, or -! (at your option) any later version. -! -! This program is distributed in the hope that it will be useful, -! but WITHOUT ANY WARRANTY; without even the implied warranty of -! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -! GNU General Public License for more details. -! -! You should have received a copy of the GNU General Public License -! along with this program. If not, see . - -program multi_driver -! used to evaluate different methods for simulating snow processes -! ***************************************************************************** -! use desired modules -! ***************************************************************************** -use,intrinsic :: iso_c_binding -USE,intrinsic :: ieee_arithmetic ! IEEE arithmetic (obviously) -use summa_bmi, only: initialize, update, finalize, modelTimeStep -USE globalData,only:numtim ! number of time steps -implicit none - -integer(kind=c_int) :: istat - -!'main routine' -istat = initialize() - -! loop through time -do modelTimeStep=1,numtim - istat = update() -end do ! (looping through time) -istat = finalize() - -end program multi_driver diff --git a/build/source/driver/summa_bmi.f90 b/build/source/driver/summa_bmi.f90 deleted file mode 100755 index dc4c3072b..000000000 --- a/build/source/driver/summa_bmi.f90 +++ /dev/null @@ -1,2069 +0,0 @@ -! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2015 NCAR/RAL -! -! This file is part of SUMMA -! -! For more information see: http://www.ral.ucar.edu/projects/summa -! -! This program is free software: you can redistribute it and/or modify -! it under the terms of the GNU General Public License as published by -! the Free Software Foundation, either version 3 of the License, or -! (at your option) any later version. -! -! This program is distributed in the hope that it will be useful, -! but WITHOUT ANY WARRANTY; without even the implied warranty of -! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -! GNU General Public License for more details. -! -! You should have received a copy of the GNU General Public License -! along with this program. If not, see . - -module summa_bmi -! used to evaluate different methods for simulating snow processes -! ***************************************************************************** -! use desired modules -! ***************************************************************************** -use,intrinsic :: iso_c_binding -USE nrtype ! variable types, etc. -USE netcdf ! netcdf libraries -USE,intrinsic :: ieee_arithmetic ! IEEE arithmetic (obviously) -! provide access to subroutines and functions -USE summaFileManager,only:summa_SetDirsUndPhiles ! sets directories and filenames -USE module_sf_noahmplsm,only:read_mp_veg_parameters ! module to read NOAH vegetation tables -USE module_sf_noahmplsm,only:isWater ! parameter for water land cover type -USE nr_utility_module,only:arth ! get a sequence of numbers -USE nr_utility_module,only:indexx ! sort vectors in ascending order -USE ascii_util_module,only:file_open ! open ascii file -USE ascii_util_module,only:get_vlines ! read a vector of non-comment lines from an ASCII file -USE ascii_util_module,only:split_line ! extract the list of variable names from the character string -use time_utils_module,only:elapsedSec ! calculate the elapsed time -USE allocspace_module,only:allocGlobal ! module to allocate space for global data structures -USE allocspace_module,only:allocLocal ! module to allocate space for local data structures -USE childStruc_module,only:childStruc ! module to create a child data structure -USE mDecisions_module,only:mDecisions ! module to read model decisions -USE popMetadat_module,only:popMetadat ! module to populate metadata structures -USE flxMapping_module,only:flxMapping ! module to map fluxes to states -USE checkStruc_module,only:checkStruc ! module to check metadata structures -USE def_output_module,only:def_output ! module to define model output -USE ffile_info_module,only:ffile_info ! module to read information on forcing datafile -USE read_attrb_module,only:read_dimension ! module to read dimensions of GRU and HRU -USE read_attrb_module,only:read_attrb ! module to read local attributes -USE read_pinit_module,only:read_pinit ! module to read initial model parameter values -USE paramCheck_module,only:paramCheck ! module to check consistency of model parameters -USE check_icond_module,only:check_icond ! module to check initial conditions -USE read_icond_module,only:read_icond ! module to read initial conditions -USE read_icond_module,only:read_icond_nlayers ! module to read initial conditions -USE pOverwrite_module,only:pOverwrite ! module to overwrite default parameter values with info from the Noah tables -USE read_param_module,only:read_param ! module to read model parameter sets -USE ConvE2Temp_module,only:E2T_lookup ! module to calculate a look-up table for the temperature-enthalpy conversion -USE var_derive_module,only:calcHeight ! module to calculate height at layer interfaces and layer mid-point -USE var_derive_module,only:v_shortcut ! module to calculate "short-cut" variables -USE var_derive_module,only:rootDensty ! module to calculate the vertical distribution of roots -USE var_derive_module,only:satHydCond ! module to calculate the saturated hydraulic conductivity in each soil layer -USE var_derive_module,only:fracFuture ! module to calculate the fraction of runoff in future time steps (time delay histogram) -USE read_force_module,only:read_force ! module to read model forcing data -USE modelwrite_module,only:writeParm,writeTime ! module to write model attributes and parameters -USE modelwrite_module,only:writeData,writeBasin ! module to write model output -USE modelwrite_module,only:writeRestart ! module to write model Restart -USE vegPhenlgy_module,only:vegPhenlgy ! module to compute vegetation phenology -USE run_oneGRU_module,only:run_oneGRU ! module to run for one GRU -USE groundwatr_module,only:groundwatr ! module to simulate regional groundwater balance -USE qTimeDelay_module,only:qOverland ! module to route water through an "unresolved" river network -USE netcdf_util_module,only:nc_file_close ! module to handle netcdf stuff for inputs and outputs -! provide access to file paths -USE summaFileManager,only:SETNGS_PATH ! define path to settings files (e.g., Noah vegetation tables) -USE summaFileManager,only:MODEL_INITCOND ! name of model initial conditions file -USE summaFileManager,only:LOCAL_ATTRIBUTES ! name of model initial attributes file -USE summaFileManager,only:OUTPUT_PATH,OUTPUT_PREFIX ! define output file -USE summaFileManager,only:LOCALPARAM_INFO,BASINPARAM_INFO ! files defining the default values and constraints for model parameters -! provide access to the derived types to define the data structures -USE data_types,only:& - ! no spatial dimension - var_i, & ! x%var(:) (i4b) - var_d, & ! x%var(:) (dp) - var_ilength, & ! x%var(:)%dat (i4b) - var_dlength, & ! x%var(:)%dat (dp) - ! no variable dimension - hru_i, & ! x%hru(:) (i4b) - hru_d, & ! x%hru(:) (dp) - ! gru dimension - gru_int, & ! x%gru(:)%var(:) (i4b) - gru_double, & ! x%gru(:)%var(:) (dp) - gru_intVec, & ! x%gru(:)%var(:)%dat (i4b) - gru_doubleVec, & ! x%gru(:)%var(:)%dat (dp) - ! gru+hru dimension - gru_hru_int, & ! x%gru(:)%hru(:)%var(:) (i4b) - gru_hru_double, & ! x%gru(:)%hru(:)%var(:) (dp) - gru_hru_intVec, & ! x%gru(:)%hru(:)%var(:)%dat (i4b) - gru_hru_doubleVec ! x%gru(:)%hru(:)%var(:)%dat (dp) -USE data_types,only:extended_info ! extended metadata structure -! provide access to runtime options -USE globalData,only:iRunModeFull,iRunModeGRU,iRunModeHRU -! provide access to metadata structures -USE globalData,only:time_meta,forc_meta,attr_meta,type_meta ! metadata structures -USE globalData,only:prog_meta,diag_meta,flux_meta ! metadata structures -USE globalData,only:mpar_meta,indx_meta ! metadata structures -USE globalData,only:bpar_meta,bvar_meta ! metadata structures -USE globalData,only:averageFlux_meta ! metadata for time-step average fluxes -USE globalData,only:model_decisions ! model decision structure -! provide access to global data -USE globalData,only:dNaN ! double precision NaN -USE globalData,only:refTime ! reference time -USE globalData,only:startTime ! start time -USE globalData,only:finshTime ! end time -USE globalData,only:doJacobian ! flag to compute the Jacobian -USE globalData,only:gru_struc ! gru-hru mapping structures -USE globalData,only:localParFallback ! local column default parameters -USE globalData,only:basinParFallback ! basin-average default parameters -USE globalData,only:structInfo ! information on the data structures -USE globalData,only:numtim ! number of time steps -USE globalData,only:urbanVegCategory ! vegetation category for urban areas -USE globalData,only:greenVegFrac_monthly ! fraction of green vegetation in each month (0-1) -USE globalData,only:globalPrintFlag ! global print flag -USE globalData,only:integerMissing ! missing integer value -USE globalData,only:realMissing ! missing double precision value -USE globalData,only:yes,no ! .true. and .false. -USE globalData,only:dJulianStart ! julian day of start time of simulation -USE globalData,only:dJulianFinsh ! julian day of end time of simulation -USE globalData,only:data_step ! length of time steps for the outermost timeloo -USE multiconst,only:secprday -! provide access to Noah-MP parameters -USE NOAHMP_VEG_PARAMETERS,only:SAIM,LAIM ! 2-d tables for stem area index and leaf area index (vegType,month) -USE NOAHMP_VEG_PARAMETERS,only:HVT,HVB ! height at the top and bottom of vegetation (vegType) -USE var_lookup,only:maxvarTime ! size of variable vectors -USE var_lookup,only:maxvarForc,maxvarProg,maxvarDiag ! size of variable vectors -USE var_lookup,only:maxvarFlux,maxvarIndx,maxvarBvar ! size of variable vectors -! provide access to the named variables that describe elements of parent model structures -USE var_lookup,only:iLookTIME,iLookFORCE ! look-up values for time and forcing data structures -USE var_lookup,only:iLookTYPE ! look-up values for classification of veg, soils etc. -USE var_lookup,only:iLookATTR ! look-up values for local attributes -USE var_lookup,only:iLookPARAM ! look-up values for local column model parameters -USE var_lookup,only:iLookINDEX ! look-up values for local column index variables -USE var_lookup,only:iLookPROG ! look-up values for local column model prognostic (state) variables -USE var_lookup,only:iLookDIAG ! look-up values for local column model diagnostic variables -USE var_lookup,only:iLookFLUX ! look-up values for local column model fluxes -USE var_lookup,only:iLookBVAR ! look-up values for basin-average model variables -USE var_lookup,only:iLookBPAR ! look-up values for basin-average model parameters -USE var_lookup,only:iLookDECISIONS ! look-up values for model decisions -USE var_lookup,only:iLookVarType ! look-up values for variable type structure -USE var_lookup,only:iLookFreq ! look-up values for model output frequency -! provide access to the named variables that describe elements of child model structures -USE var_lookup,only:childFLUX_MEAN ! look-up values for timestep-average model fluxes -! provide access to the named variables that describe model decisions -USE mDecisions_module,only: & ! look-up values for method used to compute derivative - numerical, & ! numerical solution - analytical ! analytical solution -USE mDecisions_module,only:& ! look-up values for LAI decisions - monthlyTable,& ! LAI/SAI taken directly from a monthly table for different vegetation classes - specified ! LAI/SAI computed from green vegetation fraction and winterSAI and summerLAI parameters -USE mDecisions_module,only:& ! look-up values for the choice of method for the spatial representation of groundwater - localColumn, & ! separate groundwater representation in each local soil column - singleBasin ! single groundwater store over the entire basin -USE mDecisions_module,only:& - sameRulesAllLayers, & ! SNTHERM option: same combination/sub-dividion rules applied to all layers - rulesDependLayerIndex ! CLM option: combination/sub-dividion rules depend on layer index -USE output_stats,only:calcStats ! module for compiling output statistics -USE var_lookup,only:maxvarFreq ! maximum # of output files -USE globalData,only:ncid ! file id of netcdf output file -implicit none - -! ***************************************************************************** -! (0) variable definitions -! ***************************************************************************** -! define the statistics structures -type(gru_hru_doubleVec) :: forcStat ! x%gru(:)%hru(:)%var(:)%dat -- model forcing data -type(gru_hru_doubleVec) :: progStat ! x%gru(:)%hru(:)%var(:)%dat -- model prognostic (state) variables -type(gru_hru_doubleVec) :: diagStat ! x%gru(:)%hru(:)%var(:)%dat -- model diagnostic variables -type(gru_hru_doubleVec) :: fluxStat ! x%gru(:)%hru(:)%var(:)%dat -- model fluxes -type(gru_hru_doubleVec) :: indxStat ! x%gru(:)%hru(:)%var(:)%dat -- model indices -type(gru_doubleVec) :: bvarStat ! x%gru(:)%var(:)%dat -- basin-average variabl -! define the primary data structures (scalars) -type(var_i) :: timeStruct ! x%var(:) -- model time data -type(gru_hru_double) :: forcStruct ! x%gru(:)%hru(:)%var(:) -- model forcing data -type(gru_hru_double) :: attrStruct ! x%gru(:)%hru(:)%var(:) -- local attributes for each HRU -type(gru_hru_int) :: typeStruct ! x%gru(:)%hru(:)%var(:) -- local classification of soil veg etc. for each HRU -! define the primary data structures (variable length vectors) -type(gru_hru_intVec) :: indxStruct ! x%gru(:)%hru(:)%var(:)%dat -- model indices -type(gru_hru_doubleVec) :: mparStruct ! x%gru(:)%hru(:)%var(:)%dat -- model parameters -type(gru_hru_doubleVec) :: progStruct ! x%gru(:)%hru(:)%var(:)%dat -- model prognostic (state) variables -type(gru_hru_doubleVec) :: diagStruct ! x%gru(:)%hru(:)%var(:)%dat -- model diagnostic variables -type(gru_hru_doubleVec) :: fluxStruct ! x%gru(:)%hru(:)%var(:)%dat -- model fluxes -! define the basin-average structures -type(gru_double) :: bparStruct ! x%gru(:)%var(:) -- basin-average parameters -type(gru_doubleVec) :: bvarStruct ! x%gru(:)%var(:)%dat -- basin-average variables -! define the ancillary data structures -type(gru_hru_double) :: dparStruct ! x%gru(:)%hru(:)%var(:) -- default model parameters -! define indices -integer(i4b) :: iStruct ! loop through data structures -integer(i4b) :: iGRU,jGRU,kGRU ! index of grouped response unit -integer(i4b) :: iHRU,jHRU,kHRU ! index of the hydrologic response unit -integer(i4b) :: nGRU ! number of grouped response units -integer(i4b) :: nHRU ! number of global hydrologic response units -integer(i4b) :: hruCount ! number of local hydrologic response units -integer(i4b) :: modelTimeStep=0 ! index of model time step -integer(i4b),dimension(maxvarTime) :: oldTimeVec ! old time vector -integer(i4b),dimension(maxvarFreq) :: statCounter=0 ! time counter for stats -integer(i4b),dimension(maxvarFreq) :: outputTimeStep=0 ! timestep in output files -logical(lgt),dimension(maxvarFreq) :: resetStats=.true. ! flags to reset statistics -logical(lgt),dimension(maxvarFreq) :: finalizeStats=.false. ! flags to reset statistics -! define the time output -logical(lgt) :: printProgress ! flag to print progress -integer(i4b),parameter :: ixProgress_im=1000 ! named variable to print progress once per month -integer(i4b),parameter :: ixProgress_id=1001 ! named variable to print progress once per day -integer(i4b),parameter :: ixProgress_ih=1002 ! named variable to print progress once per hour -integer(i4b),parameter :: ixProgress_never=1003 ! named variable to print progress never -integer(i4b) :: ixProgress=ixProgress_id ! define frequency to write progress -! define the re-start file -logical(lgt) :: printRestart ! flag to print a re-start file -integer(i4b),parameter :: ixRestart_iy=1000 ! named variable to print a re-start file once per year -integer(i4b),parameter :: ixRestart_im=1001 ! named variable to print a re-start file once per month -integer(i4b),parameter :: ixRestart_id=1002 ! named variable to print a re-start file once per day -integer(i4b),parameter :: ixRestart_end=1003 ! named variable to print a re-start file at the end of a run -integer(i4b),parameter :: ixRestart_never=1004 ! named variable to print a re-start file never -integer(i4b) :: ixRestart=ixRestart_never ! define frequency to write restart files -! define output file -integer(i4b) :: ctime1(8) ! initial time -character(len=256) :: output_fileSuffix='' ! suffix for the output file -character(len=256) :: summaFileManagerFile='' ! path/name of file defining directories and files -character(len=256) :: fileout='' ! output filename -integer(i4b),parameter :: noNewFiles=1001 ! no new output files -integer(i4b),parameter :: newFileEveryOct1=1002 ! create a new file on Oct 1 every year (start of the USA water year) -integer(i4b) :: newOutputFile=noNewFiles ! option for new output files -logical(lgt) :: defNewOutputFile=.false. ! flag to define new output files -! define model control structures -logical(lgt) :: computeVegFluxFlag ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) -type(hru_i),allocatable :: computeVegFlux(:) ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) -type(hru_d),allocatable :: dt_init(:) ! used to initialize the length of the sub-step for each HRU -type(hru_d),allocatable :: upArea(:) ! area upslope of each HRU -! general local variables -integer(i4b) :: ivar ! index of model variable -logical(lgt) :: flux_mask(maxvarFlux) ! mask defining desired flux variables -integer(i4b) :: forcNcid=integerMissing ! netcdf id for current netcdf forcing file -integer(i4b) :: iFile=1 ! index of current forcing file from forcing file list -integer(i4b) :: forcingStep=integerMissing ! index of current time step in current forcing file -real(dp) :: notUsed_canopyDepth ! NOT USED: canopy depth (m) -real(dp) :: notUsed_exposedVAI ! NOT USED: exposed vegetation area index (m2 m-2) -! error control -integer(i4b) :: err=0 ! error code -character(len=1024) :: message='' ! error message -! output control -integer(i4b) :: iFreq ! index for looping through output files -logical(lgt) :: statForc_mask(maxvarForc) ! mask defining forc stats -logical(lgt) :: statProg_mask(maxvarProg) ! mask defining prog stats -logical(lgt) :: statDiag_mask(maxvarDiag) ! mask defining diag stats -logical(lgt) :: statFlux_mask(maxvarFlux) ! mask defining flux stats -logical(lgt) :: statIndx_mask(maxvarIndx) ! mask defining indx stats -logical(lgt) :: statBvar_mask(maxvarBvar) ! mask defining bvar stats -integer(i4b),allocatable :: forcChild_map(:) ! index of the child data structure: stats forc -integer(i4b),allocatable :: progChild_map(:) ! index of the child data structure: stats prog -integer(i4b),allocatable :: diagChild_map(:) ! index of the child data structure: stats diag -integer(i4b),allocatable :: fluxChild_map(:) ! index of the child data structure: stats flux -integer(i4b),allocatable :: indxChild_map(:) ! index of the child data structure: stats indx -integer(i4b),allocatable :: bvarChild_map(:) ! index of the child data structure: stats bvar -type(extended_info),allocatable :: statForc_meta(:) ! child metadata for stats -type(extended_info),allocatable :: statProg_meta(:) ! child metadata for stats -type(extended_info),allocatable :: statDiag_meta(:) ! child metadata for stats -type(extended_info),allocatable :: statFlux_meta(:) ! child metadata for stats -type(extended_info),allocatable :: statIndx_meta(:) ! child metadata for stats -type(extended_info),allocatable :: statBvar_meta(:) ! child metadata for stats -! stuff for restart file -character(len=256) :: timeString ! protion of restart file name that contains the write-out time -character(len=256) :: restartFile ! restart file name -character(len=256) :: attrFile ! attributes file name -! open MP functions -integer(i4b) :: omp_get_num_threads ! get the number of threads -! parallelize the model run -integer(i4b) :: nThreads ! number of threads -integer(i4b), allocatable :: ixExpense(:) ! ranked index GRU w.r.t. computational expense -integer(i4b), allocatable :: totalFluxCalls(:) ! total number of flux calls for each GRU -integer(i4b) :: nHRUrun ! number of HRUs in the run domain -integer(i4b) :: maxLayers ! maximum number of layers -integer(i4b) :: maxSnowLayers ! maximum number of snow layers -integer(i4b) :: startGRU ! index of the starting GRU for parallelization run -integer(i4b) :: checkHRU ! index of the HRU for a single HRU run -integer(i4b) :: fileGRU ! number of GRUs in the input file -integer(i4b) :: fileHRU ! number of HRUs in the input file -integer(i4b) :: iRunMode ! define the current running mode -character(len=128) :: fmtGruOutput ! a format string used to write start and end GRU in output file names -! timing information -integer*8 :: openMPstart,openMPend ! time for the start of the parallelization section -integer*8, allocatable :: timeGRUstart(:) ! time GRUs start -real(dp), allocatable :: timeGRUcompleted(:) ! time required to complete each GRU -real(dp), allocatable :: timeGRU(:) ! time spent on each GRU -integer(i4b), dimension(8) :: startInit,endInit ! date/time for the start and end of the initialization -real(dp) :: elapsedInit ! elapsed time for the initialization -integer(i4b), dimension(8) :: startRead,endRead ! date/time for the start and end of the data read -real(dp) :: elapsedRead ! elapsed time for the data read -integer(i4b), dimension(8) :: startWrite,endWrite ! date/time for the start and end of the stats/write -real(dp) :: elapsedWrite ! elapsed time for the stats/write -integer(i4b), dimension(8) :: startPhysics,endPhysics ! date/time for the start and end of the physics -real(dp) :: elapsedPhysics ! elapsed time for the physics - -! version information generated during compiling -INCLUDE 'summaversion.inc' - - -contains - - - function get_start_time() result(ret) bind(c, name="get_summa_start_time") - implicit none - real(KIND=C_FLOAT) :: ret - ret = dJulianStart ! unit days - end function get_start_time - - - function get_current_time() result(ret) bind(c, name="get_summa_current_time") - implicit none - real(KIND=C_FLOAT) :: ret - - if(modelTimeStep==1)then - ret = dJulianStart - else - ret = dJulianStart + (data_step*real(modelTimeStep-1,dp))/secprday ! unit days - end if - end function get_current_time - - - function get_end_time() result(ret) bind(c, name="get_summa_end_time") - implicit none - real(KIND=C_FLOAT) :: ret - ret = dJulianFinsh ! unit days - end function get_end_time - - - function get_time_step() result(ret) bind(c, name="get_summa_time_step") - implicit none - integer :: ret - ret = data_step ! unit seconds -end function get_time_step - - - function get_num_output_fields() result(ret) bind(c, name="get_num_ovars") - implicit none - integer :: ret - ret = 16 - end function get_num_output_fields - - - subroutine to_c_string(source, dest) - implicit none - - character(len=32), intent(in) :: source - character(kind=C_CHAR), intent(out) :: dest(*) - integer :: j,k - - k = LEN(TRIM(source)) - do j=1,k - dest(j) = source(j:j) - end do - dest(k+1) = C_NULL_CHAR - - end subroutine to_c_string - - - subroutine get_output_name(index, dest) bind(c, name="get_ovar_name") - implicit none - integer :: index - character(len=32) :: source - character(kind=c_char)::dest(*) - do iGRU = 1, nGRU - do jHRU = 1, gru_struc(iGRU)%hruCount - select case (index) - case (1) - source = 'total runoff' - call to_c_string(source, dest) - case (2) - source = 'evaporation from soil' - call to_c_string(source, dest) - case (3) - source = 'precipitation' - call to_c_string(source, dest) - case (4) - source = 'evaporation from vegetation' - call to_c_string(source, dest) - case (5) - source = 'transpiration from vegetation' - call to_c_string(source, dest) - case (6) - source = 'sublimation from snow surface' - call to_c_string(source, dest) - case (7) - source = 'sublimation from vegetation surface' - call to_c_string(source, dest) - case (8) - source = 'snow water equivalent' - call to_c_string(source, dest) - case (9) - source = 'soil moisture' - call to_c_string(source, dest) - case (10) - source = 'canopy moisture' - call to_c_string(source, dest) - case (11) - source = 'net radiation' - call to_c_string(source, dest) - case (12) - source = 'latent heat' - call to_c_string(source, dest) - case (13) - source = 'sensible heat' - call to_c_string(source, dest) - case (14) - source = 'canopy air energy flux' - call to_c_string(source, dest) - case (15) - source = 'vegetation energy flux' - call to_c_string(source, dest) - case (16) - source = 'ground energy flux' - call to_c_string(source, dest) - end select - end do - end do - end subroutine get_output_name - - - subroutine get_output_units(index, dest) bind(c, name="get_ovar_units") - implicit none - integer :: index - character(len=32) :: source - character(kind=c_char)::dest(*) - - do iGRU = 1, nGRU - do jHRU = 1, gru_struc(iGRU)%hruCount - select case (index) - case (1) ! total runoff - source = 'm s-1' - call to_c_string(source, dest) - case (2) ! evaporation from soil - source = 'kg m-2 s-1' - call to_c_string(source, dest) - case (3) ! precipitation rate - source = 'kg m-2 s-1' - call to_c_string(source, dest) - case (4) ! evaporation from vegetation - source = 'kg m-2 s-1' - call to_c_string(source, dest) - case (5) ! transpiration from vegetation - source = 'kg m-2 s-1' - call to_c_string(source, dest) - case (6) ! sublimation from snow surface - source = 'kg m-2 s-1' - call to_c_string(source, dest) - case (7) ! sublimation from vegetation surface - source = 'kg m-2 s-1' - call to_c_string(source, dest) - case (8) ! snow water equivalent - source = 'kg m-2' - call to_c_string(source, dest) - case (9) ! soil moisture - source = 'kg m-2' - call to_c_string(source, dest) - case (10) ! canopy moisture - source = 'kg m-2' - call to_c_string(source, dest) - case (11) ! net radiation - source = 'W m-2' - call to_c_string(source, dest) - case (12) ! latent heat - source = 'W m-2' - call to_c_string(source, dest) - case (13) ! sensible heat - source = 'W m-2' - call to_c_string(source, dest) - case (14) ! canopy air energy flux - source = 'W m-2' - call to_c_string(source, dest) - case (15) ! vegetation energy flux - source = 'W m-2' - call to_c_string(source, dest) - case (16) ! ground energy flux - source = 'W m-2' - call to_c_string(source, dest) - end select - end do - end do - end subroutine get_output_units - - - function get_num_basins() result(ret) bind(c, name="get_num_basins") - use globalData, only: nHRUfile - - implicit none - integer :: ret - ret = nHRUfile - - end function get_num_basins - - - subroutine get_basin_field(index, targetarr) bind(c, name="get_ovar_values") - use globalData, only: nHRUfile - - implicit none - integer, intent(in) :: index - real(kind=C_FLOAT), intent(out) :: targetarr(nHRUfile) - integer :: iGRU, jHRU - - do iGRU = 1, nGRU - do jHRU = 1, gru_struc(iGRU)%hruCount - select case (index) - case (1) ! total runoff - targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & - fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarTotalRunoff)%dat(1) - case (2) ! evaporation from soil - targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & - fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarGroundEvaporation)%dat(1) - !case (3) ! precipitation - ! targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & - ! forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORC%pptrate)%dat(1) - case (4) ! evaporation from vegetation - targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & - fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) - case (5) ! transpiration from vegetation - targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & - fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) - case (6) ! sublimation from snow surface - targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & - fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarSnowSublimation)%dat(1) - case (7) ! sublimation from vegetation surface - targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & - fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopySublimation)%dat(1) - case (8) ! snow water equivalent - targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & - progStruct%gru(iGRU)%hru(jHRU)%var(iLookPROG%scalarSWE)%dat(1) - case (9) ! soil moisture - targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & - diagStruct%gru(iGRU)%hru(jHRU)%var(iLookDIAG%scalarTotalSoilWat)%dat(1) - case (10) ! canopy moisture - targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & - progStruct%gru(iGRU)%hru(jHRU)%var(iLookPROG%scalarCanopyWat)%dat(1) - case (11) ! net radiation - targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & - fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarNetRadiation)%dat(1) - case (12) ! latent heat - targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & - fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarLatHeatTotal)%dat(1) - case (13) ! sensible heat - targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & - fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarSenHeatTotal)%dat(1) - case (14) ! canopy air energy flux - targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & - fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1) - case (15) ! vegetation energy flux - targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & - fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1) - case (16) ! ground energy flux - targetarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = & - fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1) - end select - end do - end do - - end subroutine get_basin_field - - - subroutine get_latlons(targetlatarr, targetlonarr) bind(c, name="get_latlons") - - use, intrinsic :: ISO_C_BINDING - use var_lookup, only: iLook_attr - use globalData, only: nHRUfile, gru_struc - - implicit none - - real(kind=C_FLOAT), intent(out) :: targetlatarr(nHRUfile) - real(kind=C_FLOAT), intent(out) :: targetlonarr(nHRUfile) - integer :: iGRU, jHRU - - do iGRU = 1, nGRU - do jHRU = 1, gru_struc(iGRU)%hruCount - targetlatarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = attrStruct%gru(iGRU)%hru(jHRU)%var(iLookATTR%latitude) - targetlonarr((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = attrStruct%gru(iGRU)%hru(jHRU)%var(iLookATTR%longitude) - end do - end do - - end subroutine get_latlons - - - function initialize(dir) RESULT(istat) bind(c, name="init_summa") - use, intrinsic :: iso_c_binding - character(kind=c_char), dimension(*), intent(in), optional :: dir - integer(kind=c_int) :: istat - integer :: i - - istat=0 - - ! ***************************************************************************** - ! *** inital priming -- get command line arguments, identify files, etc. - ! ***************************************************************************** - - ! initialize the Jacobian flag - doJacobian=.false. ! initialize the Jacobian flag - ncid(:) = integerMissing ! initialize netcdf file id - - ! if initialize is called with an argument pointing to the file manager file - if (present(dir)) then - do i=1,256 - if (dir(i) == C_NULL_CHAR) then - exit - end if - summaFileManagerFile(i:i)=dir(i) ! copying character array to string - end do - endif - - ! get the command line arguments - ! if using the BMI interface, make sure summaFileManagerFile before executing getCommandArguments - call getCommandArguments() - - ! define double precision NaNs (shared in globalData) - dNaN = ieee_value(1._dp, ieee_quiet_nan) - - ! initialize the elapsed time - elapsedRead=0._dp - elapsedWrite=0._dp - elapsedPhysics=0._dp - - ! get the initial time - call date_and_time(values=ctime1) - print "(A,I2.2,':',I2.2,':',I2.2)", 'start at ',ctime1(5:7) - - ! initialize the start of the initialization - call date_and_time(values=startInit) - - ! set directories and files -- summaFileManager used as command-line argument - call summa_SetDirsUndPhiles(summaFileManagerFile,err,message); call handle_err(err,message) - - ! allocate time structures - call allocLocal(time_meta, refTime, err=err, message=message); call handle_err(err,message) ! reference time for the model simulation - call allocLocal(time_meta, startTime, err=err, message=message); call handle_err(err,message) ! start time for the model simulation - call allocLocal(time_meta, finshTime, err=err, message=message); call handle_err(err,message) ! end time for the model simulation - - ! ***************************************************************************** - ! *** populate/check metadata structures - ! ***************************************************************************** - - ! populate metadata for all model variables - call popMetadat(err,message); call handle_err(err,message) - - ! define mapping between fluxes and states - call flxMapping(err,message); call handle_err(err,message) - - ! check data structures - call checkStruc(err,message); call handle_err(err,message) - - ! define the mask to identify the subset of variables in the "child" data structure (just scalar variables) - flux_mask = (flux_meta(:)%vartype==iLookVarType%scalarv) - - ! create the averageFlux metadata structure - call childStruc(flux_meta, flux_mask, averageFlux_meta, childFLUX_MEAN, err, message) - call handle_err(err,message) - - ! ***************************************************************************** - ! *** read the number of GRUs and HRUs, and allocate the gru-hru mapping structures - ! ***************************************************************************** - ! obtain the HRU and GRU dimensions in the LocalAttribute file - attrFile = trim(SETNGS_PATH)//trim(LOCAL_ATTRIBUTES) - select case (iRunMode) - case(iRunModeFull); call read_dimension(trim(attrFile),fileGRU,fileHRU,nGRU,nHRU,err,message) - case(iRunModeGRU ); call read_dimension(trim(attrFile),fileGRU,fileHRU,nGRU,nHRU,err,message,startGRU=startGRU) - case(iRunModeHRU ); call read_dimension(trim(attrFile),fileGRU,fileHRU,nGRU,nHRU,err,message,checkHRU=checkHRU) - end select - call handle_err(err,message) - - ! ***************************************************************************** - ! *** read model attributes - ! ***************************************************************************** - ! read number of snow and soil layers - restartFile = trim(SETNGS_PATH)//trim(MODEL_INITCOND) - call read_icond_nlayers(trim(restartFile),nGRU,indx_meta,err,message) - call handle_err(err,message) - - ! ***************************************************************************** - ! *** allocate space for other data structures - ! ***************************************************************************** - ! loop through data structures - do iStruct=1,size(structInfo) - ! allocate space - select case(trim(structInfo(iStruct)%structName)) - case('time'); call allocGlobal(time_meta, timeStruct, err, message) ! model forcing data - case('forc'); call allocGlobal(forc_meta, forcStruct, err, message) ! model forcing data - case('attr'); call allocGlobal(attr_meta, attrStruct, err, message) ! local attributes for each HRU - case('type'); call allocGlobal(type_meta, typeStruct, err, message) ! local classification of soil veg etc. for each HRU - case('mpar'); call allocGlobal(mpar_meta, mparStruct, err, message) ! model parameters - case('indx'); call allocGlobal(indx_meta, indxStruct, err, message) ! model variables - case('prog'); call allocGlobal(prog_meta, progStruct, err, message) ! model prognostic (state) variables - case('diag'); call allocGlobal(diag_meta, diagStruct, err, message) ! model diagnostic variables - case('flux'); call allocGlobal(flux_meta, fluxStruct, err, message) ! model fluxes - case('bpar'); call allocGlobal(bpar_meta, bparStruct, err, message) ! basin-average parameters - case('bvar'); call allocGlobal(bvar_meta, bvarStruct, err, message) ! basin-average variables - case('deriv'); cycle - case default; err=20; message='unable to find structure name: '//trim(structInfo(iStruct)%structName) - end select - ! check errors - call handle_err(err,trim(message)//'[structure = '//trim(structInfo(iStruct)%structName)//']') - end do ! looping through data structures - - ! ***************************************************************************** - ! *** allocate space for other data structures - ! allocate space for default model parameters - ! NOTE: This is done here, rather than in the loop above, because dpar is not one of the "standard" data structures - ! ***************************************************************************** - call allocGlobal(mpar_meta,dparStruct,err,message) ! default model parameters - call handle_err(err,trim(message)//' [problem allocating dparStruct]') - - ! allocate space for the time step and computeVegFlux flags (recycled for each GRU for subsequent calls to coupled_em) - allocate(dt_init(nGRU),upArea(nGRU),computeVegFlux(nGRU),stat=err) - call handle_err(err,'problem allocating space for dt_init, upArea, or computeVegFlux [GRU]') - - ! allocate space for the HRUs - do iGRU=1,nGRU - hruCount = gru_struc(iGRU)%hruCount - allocate(dt_init(iGRU)%hru(hruCount),upArea(iGRU)%hru(hruCount),computeVegFlux(iGRU)%hru(hruCount),stat=err) - call handle_err(err,'problem allocating space for dt_init, upArea, or computeVegFlux [HRU]') - end do - - ! ***************************************************************************** - ! *** read local attributes for each HRU - ! ***************************************************************************** - call read_attrb(trim(attrFile),nGRU,attrStruct,typeStruct,err,message) - call handle_err(err,message) - - ! get the number of HRUs in the run domain - nHRUrun = sum(gru_struc%hruCount) - - ! ***************************************************************************** - ! *** read description of model forcing datafile used in each HRU - ! ***************************************************************************** - call ffile_info(nGRU,err,message); call handle_err(err,message) - - ! ***************************************************************************** - ! *** read model decisions - ! ***************************************************************************** - call mDecisions(err,message); call handle_err(err,message) - - ! get the maximum number of snow layers - select case(model_decisions(iLookDECISIONS%snowLayers)%iDecision) - case(sameRulesAllLayers); maxSnowLayers = 100 - case(rulesDependLayerIndex); maxSnowLayers = 5 - case default; call handle_err(20,'unable to identify option to combine/sub-divide snow layers') - end select ! (option to combine/sub-divide snow layers) - - ! get the maximum number of layers - maxLayers = gru_struc(1)%hruInfo(1)%nSoil + maxSnowLayers - - ! ***************************************************************************** - ! *** allocate space for output statistics data structures - ! ***************************************************************************** - - ! child metadata structures - so that we do not carry full stats structures around everywhere - ! only carry stats for variables with output frequency > model time step - statForc_mask = (forc_meta(:)%vartype==iLookVarType%scalarv.and.forc_meta(:)%varDesire) - statProg_mask = (prog_meta(:)%vartype==iLookVarType%scalarv.and.prog_meta(:)%varDesire) - statDiag_mask = (diag_meta(:)%vartype==iLookVarType%scalarv.and.diag_meta(:)%varDesire) - statFlux_mask = (flux_meta(:)%vartype==iLookVarType%scalarv.and.flux_meta(:)%varDesire) - statIndx_mask = (indx_meta(:)%vartype==iLookVarType%scalarv.and.indx_meta(:)%varDesire) - statBvar_mask = (bvar_meta(:)%vartype==iLookVarType%scalarv.and.bvar_meta(:)%varDesire) - - ! create the stats metadata structures - do iStruct=1,size(structInfo) - select case (trim(structInfo(iStruct)%structName)) - case('forc'); call childStruc(forc_meta,statForc_mask,statForc_meta,forcChild_map,err,message) - case('prog'); call childStruc(prog_meta,statProg_mask,statProg_meta,progChild_map,err,message) - case('diag'); call childStruc(diag_meta,statDiag_mask,statDiag_meta,diagChild_map,err,message) - case('flux'); call childStruc(flux_meta,statFlux_mask,statFlux_meta,fluxChild_map,err,message) - case('indx'); call childStruc(indx_meta,statIndx_mask,statIndx_meta,indxChild_map,err,message) - case('bvar'); call childStruc(bvar_meta,statBvar_mask,statBvar_meta,bvarChild_map,err,message) - end select - ! check errors - call handle_err(err,trim(message)//'[statistics for = '//trim(structInfo(iStruct)%structName)//']') - end do ! iStruct - - ! set all stats metadata to correct var types - statForc_meta(:)%vartype = iLookVarType%outstat - statProg_meta(:)%vartype = iLookVarType%outstat - statDiag_meta(:)%vartype = iLookVarType%outstat - statFlux_meta(:)%vartype = iLookVarType%outstat - statIndx_meta(:)%vartype = iLookVarType%outstat - statBvar_meta(:)%vartype = iLookVarType%outstat - - ! loop through data structures - do iStruct=1,size(structInfo) - - ! allocate space - select case(trim(structInfo(iStruct)%structName)) - case('forc'); call allocGlobal(statForc_meta(:)%var_info,forcStat,err,message) ! model forcing data - case('prog'); call allocGlobal(statProg_meta(:)%var_info,progStat,err,message) ! model prognostic (state) variables - case('diag'); call allocGlobal(statDiag_meta(:)%var_info,diagStat,err,message) ! model diagnostic variables - case('flux'); call allocGlobal(statFlux_meta(:)%var_info,fluxStat,err,message) ! model fluxes - case('indx'); call allocGlobal(statIndx_meta(:)%var_info,indxStat,err,message) ! index vars - case('bvar'); call allocGlobal(statBvar_meta(:)%var_info,bvarStat,err,message) ! basin-average variables - case default; cycle - end select - - ! check errors - call handle_err(err,trim(message)//'[statistics for = '//trim(structInfo(iStruct)%structName)//']') - - end do ! iStruct - - ! ***************************************************************************** - ! *** read default model parameters - ! ***************************************************************************** - ! read default values and constraints for model parameters (local column, and basin-average) - call read_pinit(LOCALPARAM_INFO,.TRUE., mpar_meta,localParFallback,err,message); call handle_err(err,message) - call read_pinit(BASINPARAM_INFO,.FALSE.,bpar_meta,basinParFallback,err,message); call handle_err(err,message) - - ! ***************************************************************************** - ! *** read Noah vegetation and soil tables - ! ***************************************************************************** - ! define monthly fraction of green vegetation - greenVegFrac_monthly = (/0.01_dp, 0.02_dp, 0.03_dp, 0.07_dp, 0.50_dp, 0.90_dp, 0.95_dp, 0.96_dp, 0.65_dp, 0.24_dp, 0.11_dp, 0.02_dp/) - - ! read Noah soil and vegetation tables - call soil_veg_gen_parm(trim(SETNGS_PATH)//'VEGPARM.TBL', & ! filename for vegetation table - trim(SETNGS_PATH)//'SOILPARM.TBL', & ! filename for soils table - trim(SETNGS_PATH)//'GENPARM.TBL', & ! filename for general table - trim(model_decisions(iLookDECISIONS%vegeParTbl)%cDecision), & ! classification system used for vegetation - trim(model_decisions(iLookDECISIONS%soilCatTbl)%cDecision)) ! classification system used for soils - - ! read Noah-MP vegetation tables - call read_mp_veg_parameters(trim(SETNGS_PATH)//'MPTABLE.TBL', & ! filename for Noah-MP table - trim(model_decisions(iLookDECISIONS%vegeParTbl)%cDecision)) ! classification system used for vegetation - - ! define urban vegetation category - select case(trim(model_decisions(iLookDECISIONS%vegeParTbl)%cDecision)) - case('USGS'); urbanVegCategory = 1 - case('MODIFIED_IGBP_MODIS_NOAH'); urbanVegCategory = 13 - case('plumberCABLE'); urbanVegCategory = -999 - case('plumberCHTESSEL'); urbanVegCategory = -999 - case('plumberSUMMA'); urbanVegCategory = -999 - case default; call handle_err(30,'unable to identify vegetation category') - end select - - ! set default model parameters - do iGRU=1,nGRU - do iHRU=1,gru_struc(iGRU)%hruCount - ! set parmameters to their default value - dparStruct%gru(iGRU)%hru(iHRU)%var(:) = localParFallback(:)%default_val ! x%hru(:)%var(:) - ! overwrite default model parameters with information from the Noah-MP tables - call pOverwrite(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex), & ! vegetation category - typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%soilTypeIndex), & ! soil category - dparStruct%gru(iGRU)%hru(iHRU)%var, & ! default model parameters - err,message); call handle_err(err,message) ! error control - ! copy over to the parameter structure - ! NOTE: constant for the dat(:) dimension (normally depth) - do ivar=1,size(localParFallback) - mparStruct%gru(iGRU)%hru(iHRU)%var(ivar)%dat(:) = dparStruct%gru(iGRU)%hru(iHRU)%var(ivar) - end do ! looping through variables - end do ! looping through HRUs - ! set default for basin-average parameters - bparStruct%gru(iGRU)%var(:) = basinParFallback(:)%default_val - end do ! looping through GRUs - - ! ***************************************************************************** - ! *** read trial model parameter values for each HRU, and populate initial data structures - ! ***************************************************************************** - call read_param(iRunMode,checkHRU,startGRU,nHRU,nGRU,typeStruct,mparStruct,bparStruct,err,message); call handle_err(err,message) - - ! ***************************************************************************** - ! *** compute derived model variables that are pretty much constant for the basin as a whole - ! ***************************************************************************** - ! loop through GRUs - do iGRU=1,nGRU - - ! calculate the fraction of runoff in future time steps - call fracFuture(bparStruct%gru(iGRU)%var, & ! vector of basin-average model parameters - bvarStruct%gru(iGRU), & ! data structure of basin-average variables - err,message) ! error control - call handle_err(err,message) - - ! loop through local HRUs - do iHRU=1,gru_struc(iGRU)%hruCount - - kHRU=0 - ! check the network topology (only expect there to be one downslope HRU) - do jHRU=1,gru_struc(iGRU)%hruCount - if(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%downHRUindex) == typeStruct%gru(iGRU)%hru(jHRU)%var(iLookTYPE%hruId))then - if(kHRU==0)then ! check there is a unique match - kHRU=jHRU - else - call handle_err(20,'multi_driver: only expect there to be one downslope HRU') - end if ! (check there is a unique match) - end if ! (if identified a downslope HRU) - end do - - ! check that the parameters are consistent - call paramCheck(mparStruct%gru(iGRU)%hru(iHRU),err,message); call handle_err(err,message) - - ! calculate a look-up table for the temperature-enthalpy conversion - call E2T_lookup(mparStruct%gru(iGRU)%hru(iHRU),err,message); call handle_err(err,message) - - end do ! HRU - end do ! GRU - - ! read description of model initial conditions -- also initializes model structure components - call read_icond(restartFile, & ! intent(in): name of initial conditions file - nGRU, & ! intent(in): number of response units - mparStruct, & ! intent(in): model parameters - progStruct, & ! intent(inout): model prognostic variables - indxStruct, & ! intent(inout): model indices - err,message) ! intent(out): error control - call handle_err(err,message) - - ! check initial conditions - call check_icond(nGRU, & ! number of response units - progStruct, & ! model prognostic (state) variables - mparStruct, & ! model parameters - indxStruct, & ! layer indexes - err,message) ! error control - call handle_err(err,message) - - ! loop through GRUs - do iGRU=1,nGRU - ! loop through local HRUs - do iHRU=1,gru_struc(iGRU)%hruCount - - ! re-calculate height of each layer - call calcHeight(& - ! input/output: data structures - indxStruct%gru(iGRU)%hru(iHRU), & ! intent(in): layer type - progStruct%gru(iGRU)%hru(iHRU), & ! intent(inout): model prognostic (state) variables for a local HRU - ! output: error control - err,message); call handle_err(err,message) - - ! calculate vertical distribution of root density - call rootDensty(mparStruct%gru(iGRU)%hru(iHRU), & ! vector of model parameters - indxStruct%gru(iGRU)%hru(iHRU), & ! data structure of model indices - progStruct%gru(iGRU)%hru(iHRU), & ! data structure of model prognostic (state) variables - diagStruct%gru(iGRU)%hru(iHRU), & ! data structure of model diagnostic variables - err,message) ! error control - call handle_err(err,message) - - ! calculate saturated hydraulic conductivity in each soil layer - call satHydCond(mparStruct%gru(iGRU)%hru(iHRU), & ! vector of model parameters - indxStruct%gru(iGRU)%hru(iHRU), & ! data structure of model indices - progStruct%gru(iGRU)%hru(iHRU), & ! data structure of model prognostic (state) variables - fluxStruct%gru(iGRU)%hru(iHRU), & ! data structure of model fluxes - err,message) ! error control - call handle_err(err,message) - - ! calculate "short-cut" variables such as volumetric heat capacity - call v_shortcut(mparStruct%gru(iGRU)%hru(iHRU), & ! vector of model parameters - diagStruct%gru(iGRU)%hru(iHRU), & ! data structure of model diagnostic variables - err,message) ! error control - call handle_err(err,message) - - ! overwrite the vegetation height - HVT(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex)) = mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%heightCanopyTop)%dat(1) - HVB(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex)) = mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%heightCanopyBottom)%dat(1) - - ! overwrite the tables for LAI and SAI - if(model_decisions(iLookDECISIONS%LAI_method)%iDecision == specified)then - SAIM(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex),:) = mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%winterSAI)%dat(1) - LAIM(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex),:) = mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%summerLAI)%dat(1)*greenVegFrac_monthly - endif - - ! initialize canopy drip - ! NOTE: canopy drip from the previous time step is used to compute throughfall for the current time step - fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) = 0._dp ! not used - end do ! (looping through HRUs) - - ! compute total area of the upstream HRUS that flow into each HRU - do iHRU=1,gru_struc(iGRU)%hruCount - upArea(iGRU)%hru(iHRU) = 0._dp - do jHRU=1,gru_struc(iGRU)%hruCount - ! check if jHRU flows into iHRU; assume no exchange between GRUs - if(typeStruct%gru(iGRU)%hru(jHRU)%var(iLookTYPE%downHRUindex)==typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%hruId))then - upArea(iGRU)%hru(iHRU) = upArea(iGRU)%hru(iHRU) + attrStruct%gru(iGRU)%hru(jHRU)%var(iLookATTR%HRUarea) - endif ! (if jHRU is an upstream HRU) - end do ! jHRU - end do ! iHRU - - ! identify the total basin area for a GRU (m2) - associate(totalArea => bvarStruct%gru(iGRU)%var(iLookBVAR%basin__totalArea)%dat(1) ) - totalArea = 0._dp - do iHRU=1,gru_struc(iGRU)%hruCount - totalArea = totalArea + attrStruct%gru(iGRU)%hru(iHRU)%var(iLookATTR%HRUarea) - end do - end associate - - ! initialize aquifer storage - ! NOTE: this is ugly: need to add capabilities to initialize basin-wide state variables - ! There are two options for groundwater: - ! (1) where groundwater is included in the local column (i.e., the HRUs); and - ! (2) where groundwater is included for the single basin (i.e., the GRUS, where multiple HRUS drain into a GRU). - ! For water balance calculations it is important to ensure that the local aquifer storage is zero if groundwater is treated as a basin-average state variable (singleBasin); - ! and ensure that basin-average aquifer storage is zero when groundwater is included in the local columns (localColumn). - select case(model_decisions(iLookDECISIONS%spatial_gw)%iDecision) - ! the basin-average aquifer storage is not used if the groundwater is included in the local column - case(localColumn) - bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 0._dp ! set to zero to be clear that there is no basin-average aquifer storage in this configuration - ! NOTE: the local column aquifer storage is not used if the groundwater is basin-average - ! (i.e., where multiple HRUs drain to a basin-average aquifer) - case(singleBasin) - bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 1._dp - do iHRU=1,gru_struc(iGRU)%hruCount - progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarAquiferStorage)%dat(1) = 0._dp ! set to zero to be clear that there is no local aquifer storage in this configuration - end do - case default; call handle_err(20,'unable to identify decision for regional representation of groundwater') - end select - - ! initialize time step length for each HRU - do iHRU=1,gru_struc(iGRU)%hruCount - dt_init(iGRU)%hru(iHRU) = progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%dt_init)%dat(1) ! seconds - end do - - end do ! (looping through GRUs) - - - ! ***************************************************************************** - ! *** initialize first output sequence - ! ***************************************************************************** - ! define the output file - ! NOTE: currently assumes that nSoil is constant across the model domain - - ! set up the output file names as: OUTPUT_PREFIX_spinup|waterYear_output_fileSuffix_startGRU-endGRU_outfreq.nc or OUTPUT_PREFIX_spinup|waterYear_output_fileSuffix_HRU_outfreq.nc; - if (OUTPUT_PREFIX(len_trim(OUTPUT_PREFIX):len_trim(OUTPUT_PREFIX)) /= '_') OUTPUT_PREFIX=trim(OUTPUT_PREFIX)//'_' ! separate OUTPUT_PREFIX from others by underscore - if (output_fileSuffix(1:1) /= '_') output_fileSuffix='_'//trim(output_fileSuffix) ! separate output_fileSuffix from others by underscores - if (output_fileSuffix(len_trim(output_fileSuffix):len_trim(output_fileSuffix)) == '_') output_fileSuffix(len_trim(output_fileSuffix):len_trim(output_fileSuffix)) = ' ' - select case (iRunMode) - case(iRunModeGRU) - ! left zero padding for startGRU and endGRU - write(fmtGruOutput,"(i0)") ceiling(log10(real(fileGRU)+0.1)) ! maximum width of startGRU and endGRU - fmtGruOutput = "i"//trim(fmtGruOutput)//"."//trim(fmtGruOutput) ! construct the format string for startGRU and endGRU - fmtGruOutput = "('_G',"//trim(fmtGruOutput)//",'-',"//trim(fmtGruOutput)//")" - write(output_fileSuffix((len_trim(output_fileSuffix)+1):len(output_fileSuffix)),fmtGruOutput) startGRU,startGRU+nGRU-1 - case(iRunModeHRU) - write(output_fileSuffix((len_trim(output_fileSuffix)+1):len(output_fileSuffix)),"('_H',i0)") checkHRU - end select - - ! define file output - select case(newOutputFile) - case(noNewFiles); fileout = trim(OUTPUT_PATH)//trim(OUTPUT_PREFIX)//'output'//trim(output_fileSuffix) - case default ; fileout = trim(OUTPUT_PATH)//trim(OUTPUT_PREFIX)//'spinup'//trim(output_fileSuffix) - end select - call def_output(summaVersion,buildTime,gitBranch,gitHash,nGRU,nHRU,gru_struc(1)%hruInfo(1)%nSoil,fileout,err,message) - call handle_err(err,message) - - ! write local model attributes and parameters to the model output file - do iGRU=1,nGRU - do iHRU=1,gru_struc(iGRU)%hruCount - call writeParm(gru_struc(iGRU)%hruInfo(iHRU)%hru_ix,attrStruct%gru(iGRU)%hru(iHRU),attr_meta,err,message); call handle_err(err,'[attr]/'//message) - call writeParm(gru_struc(iGRU)%hruInfo(iHRU)%hru_ix,typeStruct%gru(iGRU)%hru(iHRU),type_meta,err,message); call handle_err(err,'[type]/'//message) - call writeParm(gru_struc(iGRU)%hruInfo(iHRU)%hru_ix,mparStruct%gru(iGRU)%hru(iHRU),mpar_meta,err,message); call handle_err(err,'[mpar]'//message) - enddo ! HRU - call writeParm(iGRU,bparStruct%gru(iGRU),bpar_meta,err,message); call handle_err(err,'[bpar]/'//message) - end do ! GRU - - ! identify the end of the initialization - call date_and_time(values=endInit) - - ! aggregate the elapsed time for the initialization - elapsedInit = elapsedSec(startInit, endInit) - - ! initialize time step index - statCounter(1:maxVarFreq) = 1 - outputTimeStep(1:maxVarFreq) = 1 - - ! initialize flags to reset/finalize statistics - resetStats(:) = .true. ! start by resetting statistics - finalizeStats(:) = .false. ! do not finalize stats on the first time step - - ! set stats flag for the timestep-level output - finalizeStats(iLookFreq%timestep)=.true. - - ! allocate space for GRU timing - allocate(totalFluxCalls(nGRU), timeGRU(nGRU), timeGRUstart(nGRU), timeGRUcompleted(nGRU), ixExpense(nGRU), stat=err) - call handle_err(err,'unable to allocate space for GRU timing') - timeGRU(:) = realMissing ! initialize because used for ranking - istat = update() - -end function initialize - - -! **************************************************************************** -! *** loop through time until -! **************************************************************************** - -function update_until(tend) result(istat) bind(c, name="update_summa_until") - use, intrinsic :: iso_c_binding - real(kind=c_float),intent(in) :: tend ! unit days (julian days, same as get_model_time) - integer(kind=c_int) :: istat - - real(kind=c_float) current - current = get_current_time() - istat=0 - - ! loop through time - if (modelTimeStep == 0) then - modelTimeStep = 1 - end if - - do while (current < tend .and. modelTimeStep < numtim) - - istat = update() - current = get_current_time() - - modelTimeStep = modelTimeStep + 1 - - end do ! (looping through time) -end function - - -! **************************************************************************** -! *** loop through time -! **************************************************************************** - -FUNCTION update() RESULT(istat) bind(c, name="update_summa") - USE, INTRINSIC :: ISO_C_BINDING - integer(kind=c_int) :: istat - - istat=0 - - ! initialize the start of the data read - call date_and_time(values=startRead) - - ! read forcing data - print*, modelTimeStep - call read_force(& - ! input - modelTimeStep, & ! intent(in): time step index - ! input-output - iFile, & ! intent(inout): index of current forcing file in forcing file list - forcingStep, & ! intent(inout): index of read position in time dimension in current netcdf file - forcNcid, & ! intent(inout): netcdf file identifier for the current forcing file - ! output - timeStruct%var, & ! intent(out): time data structure (integer) - forcStruct, & ! intent(out): forcing data structure (double precision) - err, message) ! intent(out): error control - call handle_err(err,message) - - ! identify the end of the data read - call date_and_time(values=endRead) - - ! aggregate the elapsed time for the data read - elapsedRead = elapsedRead + elapsedSec(startRead, endRead) - - ! set print flag - globalPrintFlag=.false. - - ! reset output counters/flags - if(modelTimeStep>1)then - do iFreq=1,maxVarFreq ! loop through output frequencies - - ! define the need to finalize statistics - ! NOTE: time vector is configured so that ih=0 at the start of the day, hence day in oldTime and timeStruct%var differ - select case(iFreq) - case(iLookFreq%day ); finalizeStats(iFreq)=(oldTimeVec(iLookTime%id )/=timeStruct%var(iLookTime%id )) ! daily aggregation - case(iLookFreq%month ); finalizeStats(iFreq)=(oldTimeVec(iLookTime%im )/=timeStruct%var(iLookTime%im )) ! monthly aggregation - case(iLookFreq%annual ); finalizeStats(iFreq)=(oldTimeVec(iLookTime%iyyy)/=timeStruct%var(iLookTime%iyyy)) ! yearly (annual) aggregation - case(iLookFreq%timestep); finalizeStats(iFreq)=.true. ! timestep-level output (no temporal aggregation) - case default; call handle_err(20,'unable to identify output frequency') - end select - - ! reset ouput timestep - if(resetStats(iFreq)) statCounter(iFreq)=1 - - end do ! looping through output frequencies - endif ! if modelTimeStep>1 - - ! print progress - select case(ixProgress) - case(ixProgress_im); printProgress = (timeStruct%var(iLookTIME%id) == 1 .and. timeStruct%var(iLookTIME%ih) == 0 .and. timeStruct%var(iLookTIME%imin) == 0) - case(ixProgress_id); printProgress = (timeStruct%var(iLookTIME%ih) == 0 .and. timeStruct%var(iLookTIME%imin) == 0) - case(ixProgress_ih); printProgress = (timeStruct%var(iLookTIME%imin) == 0) - case(ixProgress_never); printProgress = .false. - case default; call handle_err(20,'unable to identify option for the restart file') - end select - if(printProgress) write(*,'(i4,1x,5(i2,1x))') timeStruct%var - ! write(*,'(i4,1x,5(i2,1x))') timeStruct%var - - ! NOTE: this is done because of the check in coupled_em if computeVegFlux changes in subsequent time steps - ! (if computeVegFlux changes, then the number of state variables changes, and we need to reoranize the data structures) - ! compute the exposed LAI and SAI and whether veg is buried by snow - if(modelTimeStep==1)then - do iGRU=1,nGRU - do iHRU=1,gru_struc(iGRU)%hruCount - - ! get vegetation phenology - call vegPhenlgy(& - ! input/output: data structures - model_decisions, & ! intent(in): model decisions - typeStruct%gru(iGRU)%hru(iHRU), & ! intent(in): type of vegetation and soil - attrStruct%gru(iGRU)%hru(iHRU), & ! intent(in): spatial attributes - mparStruct%gru(iGRU)%hru(iHRU), & ! intent(in): model parameters - progStruct%gru(iGRU)%hru(iHRU), & ! intent(in): model prognostic variables for a local HRU - diagStruct%gru(iGRU)%hru(iHRU), & ! intent(inout): model diagnostic variables for a local HRU - ! output - computeVegFluxFlag, & ! intent(out): flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) - notUsed_canopyDepth, & ! intent(out): NOT USED: canopy depth (m) - notUsed_exposedVAI, & ! intent(out): NOT USED: exposed vegetation area index (m2 m-2) - err,message) ! intent(out): error control - call handle_err(err,message) - - ! save the flag for computing the vegetation fluxes - if(computeVegFluxFlag) computeVegFlux(iGRU)%hru(iHRU) = yes - if(.not.computeVegFluxFlag) computeVegFlux(iGRU)%hru(iHRU) = no - - ! define the green vegetation fraction of the grid box (used to compute LAI) - diagStruct%gru(iGRU)%hru(iHRU)%var(iLookDIAG%scalarGreenVegFraction)%dat(1) = greenVegFrac_monthly(timeStruct%var(iLookTIME%im)) - - end do ! looping through HRUs - end do ! looping through GRUs - end if ! if the first time step - - ! **************************************************************************** - ! *** model simulation - ! **************************************************************************** - - ! initialize the start of the physics - call date_and_time(values=startPhysics) - - ! ----- rank the GRUs in terms of their anticipated computational expense ----- - - ! estimate computational expense based on persistence - ! -- assume that that expensive GRUs from a previous time step are also expensive in the current time step - - ! compute the total number of flux calls from the previous time step - do jGRU=1,nGRU - totalFluxCalls(jGRU) = 0._dp - do iHRU=1,gru_struc(jGRU)%hruCount - totalFluxCalls(jGRU) = totalFluxCalls(jGRU) + indxStruct%gru(jGRU)%hru(iHRU)%var(iLookINDEX%numberFluxCalc)%dat(1) - end do - end do - - ! get the indices that can rank the computational expense - !call indexx(totalFluxCalls, ixExpense) ! ranking of each GRU w.r.t. computational expense - call indexx(timeGRU, ixExpense) ! ranking of each GRU w.r.t. computational expense - ixExpense=ixExpense(nGRU:1:-1) ! reverse ranking: now largest to smallest - - ! initialize the GRU count - ! NOTE: this needs to be outside the parallel section so it is not reinitialized by different threads - kGRU=0 - - ! initialize the time that the openMP section starts - call system_clock(openMPstart) - - ! ----- use openMP directives to run GRUs in parallel ------------------------- - - ! start of parallel section: define shared and private structure elements - !$omp parallel default(none) & - !$omp private(iGRU, jGRU) & ! GRU indices are private for a given thread - !$omp shared(openMPstart, openMPend, nThreads) & ! access constant variables - !$omp shared(timeGRUstart, timeGRUcompleted, timeGRU, ixExpense, kGRU) & ! time variables shared - !$omp shared(gru_struc, dt_init, computeVegFlux) & ! subroutine inputs - !$omp shared(timeStruct, typeStruct, attrStruct, mparStruct, indxStruct, & - !$omp forcStruct, progStruct, diagStruct, fluxStruct, bvarStruct) & - !$omp private(err, message) & - !$omp firstprivate(nGRU) - - nThreads = 1 - !$ nThreads = omp_get_num_threads() - - ! use dynamic scheduling with chunk size of one: - ! -- new chunks are assigned to threads when they become available - ! -- start with the more expensive GRUs, and add the less expensive GRUs as threads come available - - !$omp do schedule(dynamic, 1) ! chunk size of 1 - do jGRU=1,nGRU ! loop through GRUs - - !----- process GRUs in order of computational expense ------------------------- - - !$omp critical(setGRU) - - ! assign expensive GRUs to threads that enter first - kGRU = kGRU+1 - iGRU = ixExpense(kGRU) - - ! get the time that the GRU started - call system_clock( timeGRUstart(iGRU) ) - - ! print progress - !write(*,'(a,1x,5(i4,1x),f20.10,1x)') 'iGRU, jGRU, kGRU, nThreads = ', iGRU, jGRU, kGRU, nThreads, timeGRU(iGRU) - - !$omp end critical(setGRU) - - !----- run simulation for a single GRU ---------------------------------------- - - call run_oneGRU(& - ! model control - gru_struc(iGRU), & ! intent(inout): HRU information for given GRU (# HRUs, #snow+soil layers) - dt_init(iGRU)%hru, & ! intent(inout): used to initialize the length of the sub-step for each HRU - computeVegFlux(iGRU)%hru, & ! intent(inout): flag to indicate if we are computing fluxes over vegetation (false=no, true=yes) - ! data structures (input) - timeStruct%var, & ! intent(in): model time data - typeStruct%gru(iGRU), & ! intent(in): local classification of soil veg etc. for each HRU - attrStruct%gru(iGRU), & ! intent(in): local attributes for each HRU - ! data structures (input-output) - mparStruct%gru(iGRU), & ! intent(inout): local model parameters - indxStruct%gru(iGRU), & ! intent(inout): model indices - forcStruct%gru(iGRU), & ! intent(inout): model forcing data - progStruct%gru(iGRU), & ! intent(inout): prognostic variables for a local HRU - diagStruct%gru(iGRU), & ! intent(inout): diagnostic variables for a local HRU - fluxStruct%gru(iGRU), & ! intent(inout): model fluxes for a local HRU - bvarStruct%gru(iGRU), & ! intent(inout): basin-average variables - ! error control - err,message) ! intent(out): error control - - !----- save timing information ------------------------------------------------ - - !$omp critical(saveTiming) - - ! check errors - call handle_err(err,message) - - ! save timing information - call system_clock(openMPend) - timeGRU(iGRU) = real(openMPend - timeGRUstart(iGRU), kind(dp)) - timeGRUcompleted(iGRU) = real(openMPend - openMPstart , kind(dp)) - - !$omp end critical(saveTiming) - - end do ! (looping through GRUs) - !$omp end do - !$omp end parallel - - ! identify the end of the physics - call date_and_time(values=endPhysics) - - ! aggregate the elapsed time for the physics - elapsedPhysics = elapsedPhysics + elapsedSec(startPhysics, endPhysics) - - ! pause - !print*, 'driver/PAUSE: timestep '; read(*,*) - - ! **************************************************************************** - ! *** model calculate statistics - ! **************************************************************************** - - ! initialize the start of the data write - call date_and_time(values=startWrite) - - ! loop through GRUs and HRUs - do iGRU=1,nGRU - do iHRU=1,gru_struc(iGRU)%hruCount - - ! calculate output Statistics - call calcStats(forcStat%gru(iGRU)%hru(iHRU)%var,forcStruct%gru(iGRU)%hru(iHRU)%var,statForc_meta,resetStats,finalizeStats,statCounter,err,message); call handle_err(err,message) - call calcStats(progStat%gru(iGRU)%hru(iHRU)%var,progStruct%gru(iGRU)%hru(iHRU)%var,statProg_meta,resetStats,finalizeStats,statCounter,err,message); call handle_err(err,message) - call calcStats(diagStat%gru(iGRU)%hru(iHRU)%var,diagStruct%gru(iGRU)%hru(iHRU)%var,statDiag_meta,resetStats,finalizeStats,statCounter,err,message); call handle_err(err,message) - call calcStats(fluxStat%gru(iGRU)%hru(iHRU)%var,fluxStruct%gru(iGRU)%hru(iHRU)%var,statFlux_meta,resetStats,finalizeStats,statCounter,err,message); call handle_err(err,message) - call calcStats(indxStat%gru(iGRU)%hru(iHRU)%var,indxStruct%gru(iGRU)%hru(iHRU)%var,statIndx_meta,resetStats,finalizeStats,statCounter,err,message); call handle_err(err,message) - - end do ! (looping through HRUs) - - ! calc basin stats - call calcStats(bvarStat%gru(iGRU)%var(:),bvarStruct%gru(iGRU)%var(:),statBvar_meta,resetStats,finalizeStats,statCounter,err,message); call handle_err(err,message) - - ! write basin-average variables - call writeBasin(iGRU,finalizeStats,outputTimeStep,bvar_meta,bvarStat%gru(iGRU)%var,bvarStruct%gru(iGRU)%var,bvarChild_map,err,message); call handle_err(err,message) - - end do ! (looping through GRUs) - - ! write time information - call WriteTime(finalizeStats,outputTimeStep,time_meta,timeStruct%var,err,message) - - ! write the model output to the NetCDF file - ! Passes the full metadata structure rather than the stats metadata structure because - ! we have the option to write out data of types other than statistics. - ! Thus, we must also pass the stats parent->child maps from childStruct. - call writeData(finalizeStats,outputTimeStep,nHRUrun,maxLayers,forc_meta,forcStat,forcStruct,forcChild_map,indxStruct,err,message); call handle_err(err,message) - call writeData(finalizeStats,outputTimeStep,nHRUrun,maxLayers,prog_meta,progStat,progStruct,progChild_map,indxStruct,err,message); call handle_err(err,message) - call writeData(finalizeStats,outputTimeStep,nHRUrun,maxLayers,diag_meta,diagStat,diagStruct,diagChild_map,indxStruct,err,message); call handle_err(err,message) - call writeData(finalizeStats,outputTimeStep,nHRUrun,maxLayers,flux_meta,fluxStat,fluxStruct,fluxChild_map,indxStruct,err,message); call handle_err(err,message) - call writeData(finalizeStats,outputTimeStep,nHRUrun,maxLayers,indx_meta,indxStat,indxStruct,indxChild_map,indxStruct,err,message); call handle_err(err,message) - - ! increment output file timestep - do iFreq = 1,maxvarFreq - statCounter(iFreq) = statCounter(iFreq)+1 - if(finalizeStats(iFreq)) outputTimeStep(iFreq) = outputTimeStep(iFreq) + 1 - end do - - ! increment forcingStep - forcingStep=forcingStep+1 - - ! if finalized stats, then reset stats on the next time step - resetStats(:) = finalizeStats(:) - - ! save time vector - oldTimeVec(:) = timeStruct%var - - ! identify the end of the data write section - call date_and_time(values=endWrite) - - ! aggregate the elapsed time for the stats/writing - elapsedWrite = elapsedWrite + elapsedSec(startWrite, endWrite) - - !print*, 'PAUSE: in driver: testing differences'; read(*,*) - !stop 'end of time step' - - ! ***************************************************************************** - ! *** create a new NetCDF output file, and write parameters and forcing data - ! ***************************************************************************** - - ! define the need to create a new output file - select case(newOutputFile) - ! (don't ever create a new output file) - case(noNewFiles); defNewOutputFile=.false. - ! (check for the start of the USA water year) - case(newFileEveryOct1) - defNewOutputFile = (timeStruct%var(iLookTIME%im) ==10 .and. & ! month = October - timeStruct%var(iLookTIME%id) ==1 .and. & ! day = 1 - timeStruct%var(iLookTIME%ih) ==0 .and. & ! hour = 1 - timeStruct%var(iLookTIME%imin)==0) ! minute = 0 - ! (check that we found the option) - case default; call handle_err(20,'unable to identify the option to define new output files') - end select - - ! create hte new output file - if(defNewOutputFile)then - - ! close any output files that are already open - do iFreq = 1,maxvarFreq - if (ncid(iFreq)/=integerMissing) then - call nc_file_close(ncid(iFreq),err,message) - call handle_err(err,message) - end if - end do - - ! define the filename - write(fileout,'(a,i0,a,i0,a)') trim(OUTPUT_PATH)//trim(OUTPUT_PREFIX),& - timeStruct%var(iLookTIME%iyyy),'-',timeStruct%var(iLookTIME%iyyy)+1,& - trim(output_fileSuffix) - - ! define the file - call def_output(summaVersion,buildTime,gitBranch,gitHash,nGRU,nHRU,gru_struc(1)%hruInfo(1)%nSoil,fileout,err,message) - call handle_err(err,message) - - ! write parameters for each HRU, and re-set indices - do iGRU=1,nGRU - do iHRU=1,gru_struc(iGRU)%hruCount - call writeParm(gru_struc(iGRU)%hruInfo(iHRU)%hru_ix,attrStruct%gru(iGRU)%hru(iHRU),attr_meta,err,message); call handle_err(err,'[attr]/'//message) - call writeParm(gru_struc(iGRU)%hruInfo(iHRU)%hru_ix,typeStruct%gru(iGRU)%hru(iHRU),type_meta,err,message); call handle_err(err,'[type]/'//message) - call writeParm(gru_struc(iGRU)%hruInfo(iHRU)%hru_ix,mparStruct%gru(iGRU)%hru(iHRU),mpar_meta,err,message); call handle_err(err,'[mpar]'//message) - ! re-initalize the indices for model writing - outputTimeStep(:)=1 - end do ! (looping through HRUs) - call writeParm(integerMissing,bparStruct%gru(iGRU),bpar_meta,err,message); call handle_err(err,message) - end do ! (looping through GRUs) - - end if ! if defining a new file - - ! ***************************************************************************** - ! *** write restart file - ! ***************************************************************************** - - ! query whether this timestep requires a re-start file - select case(ixRestart) - case(ixRestart_iy); printRestart = (timeStruct%var(iLookTIME%im) == 1 .and. timeStruct%var(iLookTIME%id) == 1 .and. timeStruct%var(iLookTIME%ih) == 0 .and. timeStruct%var(iLookTIME%imin) == 0) - case(ixRestart_im); printRestart = (timeStruct%var(iLookTIME%id) == 1 .and. timeStruct%var(iLookTIME%ih) == 0 .and. timeStruct%var(iLookTIME%imin) == 0) - case(ixRestart_id); printRestart = (timeStruct%var(iLookTIME%ih) == 0 .and. timeStruct%var(iLookTIME%imin) == 0) - case(ixRestart_end); printRestart = (timeStruct%var(iLookTIME%im) == finshTime%var(2) .and. timeStruct%var(iLookTIME%id) == finshTime%var(3) .and. timeStruct%var(iLookTIME%ih) == finshTime%var(4) .and. timeStruct%var(iLookTIME%imin) == finshTime%var(5)) - case(ixRestart_never); printRestart = .false. - case default; call handle_err(20,'unable to identify option for the restart file') - end select - - ! print a restart file if requested - if(printRestart)then - write(timeString,'(a,i4,3(a,i2.2))') '_',timeStruct%var(iLookTIME%iyyy),'-',timeStruct%var(iLookTIME%im),'-',timeStruct%var(iLookTIME%id),'-',timeStruct%var(iLookTIME%ih) - restartFile=trim(OUTPUT_PATH)//trim(OUTPUT_PREFIX)//'_'//trim('summaRestart')//trim(timeString)//trim(output_fileSuffix)//'.nc' - call writeRestart(restartFile,nGRU,nHRU,prog_meta,progStruct,maxLayers,maxSnowLayers,indx_meta,indxStruct,err,message) - call handle_err(err,message) - end if - - -end function update - - -FUNCTION finalize() RESULT(istat) bind(c, name="finalize_summa") - -USE, INTRINSIC :: ISO_C_BINDING -integer(kind=c_int) :: istat - -istat=0 - - - ! close any remaining output files - do iFreq = 1,maxvarFreq - if (ncid(iFreq).ne.integerMissing) then - call nc_file_close(ncid(iFreq),err,message) - call handle_err(err,message) - end if - end do - - ! deallocate space used to determine the GRU computational expense - deallocate(totalFluxCalls, ixExpense, timeGRU, stat=err) - call handle_err(err,'unable to deallocate space for GRU timing') - - ! deallocate space for dt_init and upArea - deallocate(dt_init,upArea,stat=err); call handle_err(err,'unable to deallocate space for dt_init and upArea') - - call stop_program('finished simulation successfully.') - -end function finalize - - - ! ************************************************************************************************** - ! internal function to obtain the command line arguments - ! ************************************************************************************************** - subroutine getCommandArguments() - implicit none - integer(i4b) :: iArgument ! index of command line argument - integer(i4b) :: nArgument ! number of command line arguments - character(len=256),allocatable :: argString(:) ! string to store command line arguments - integer(i4b) :: nLocalArgument ! number of command line arguments to read for a switch - character(len=70), parameter :: spaces = '' - nArgument = command_argument_count() - ! check numbers of command-line arguments and obtain all arguments - if (nArgument < 1) then - call printCommandHelp() - end if - - allocate(argString(nArgument)) - do iArgument = 1,nArgument - call get_command_argument(iArgument,argString(iArgument)) - ! print versions if needed - if (trim(argString(iArgument)) == '-v' .or. trim(argString(iArgument)) == '--version') then - ! print version numbers - - print "(A)", '----------------------------------------------------------------------' - print "(A)", ' SUMMA - Structure for Unifying Multiple Modeling Alternatives ' - print "(A)", spaces(1:int((70 - len_trim(summaVersion) - 9) / 2))//'Version: ' //trim(summaVersion) - print "(A)", spaces(1:int((70 - len_trim(buildTime) - 12) / 2)) //'Build Time: '//trim(buildTime) - print "(A)", spaces(1:int((70 - len_trim(gitBranch) - 12) / 2)) //'Git Branch: '//trim(gitBranch) - print "(A)", spaces(1:int((70 - len_trim(gitHash) - 10) / 2)) //'Git Hash: ' //trim(gitHash) - print "(A)", '----------------------------------------------------------------------' - if (nArgument == 1) stop - end if - end do - - ! initialize command line argument variables - startGRU = integerMissing; checkHRU = integerMissing - nGRU = integerMissing; nHRU = integerMissing - newOutputFile = noNewFiles - iRunMode = iRunModeFull - - ! loop through all command arguments - nLocalArgument = 0 - do iArgument = 1,nArgument - if (nLocalArgument>0) then; nLocalArgument = nLocalArgument -1; cycle; end if ! skip the arguments have been read - select case (trim(argString(iArgument))) - - case ('-m', '--master') - ! update arguments - nLocalArgument = 1 - if (iArgument+nLocalArgument>nArgument) call handle_err(1,"missing argument file_suffix; type 'summa.exe --help' for correct usage") - ! get name of master control file - summaFileManagerFile=trim(argString(iArgument+1)) - print "(A)", "file_master is '"//trim(summaFileManagerFile)//"'." - - ! define the formation of new output files - case ('-n', '--newFile') - ! check that the number of command line arguments is correct - nLocalArgument = 1 ! expect just one argument for new output files - if (iArgument+nLocalArgument>nArgument) call handle_err(1,"missing argument file_suffix; type 'summa.exe --help' for correct usage") - ! get the decision for the formation of new output files - select case( trim(argString(iArgument+1)) ) - case('noNewFiles'); newOutputFile = noNewFiles - case('newFileEveryOct1'); newOutputFile = newFileEveryOct1 - case default; call handle_err(1,'unknown option for new output file: expect "noNewFiles" or "newFileEveryOct1"') - end select - - case ('-s', '--suffix') - ! define file suffix - nLocalArgument = 1 - ! check if the number of command line arguments is correct - if (iArgument+nLocalArgument>nArgument) call handle_err(1,"missing argument file_suffix; type 'summa.exe --help' for correct usage") - output_fileSuffix=trim(argString(iArgument+1)) - print "(A)", "file_suffix is '"//trim(output_fileSuffix)//"'." - - case ('-h', '--hru') - ! define a single HRU run - if (iRunMode == iRunModeGRU) call handle_err(1,"single-HRU run and GRU-parallelization run cannot be both selected.") - iRunMode=iRunModeHRU - nLocalArgument = 1 - ! check if the number of command line arguments is correct - if (iArgument+nLocalArgument>nArgument) call handle_err(1,"missing argument checkHRU; type 'summa.exe --help' for correct usage") - read(argString(iArgument+1),*) checkHRU ! read the index of the HRU for a single HRU run - nHRU=1; nGRU=1 ! nHRU and nGRU are both one in this case - ! examines the checkHRU is correct - if (checkHRU<1) then - call handle_err(1,"illegal iHRU specification; type 'summa.exe --help' for correct usage") - else - print '(A)',' Single-HRU run activated. HRU '//trim(argString(iArgument+1))//' is selected for simulation.' - end if - - case ('-g','--gru') - ! define a GRU parallelization run; get the starting GRU and countGRU - if (iRunMode == iRunModeHRU) call handle_err(1,"single-HRU run and GRU-parallelization run cannot be both selected.") - iRunMode=iRunModeGRU - nLocalArgument = 2 - ! check if the number of command line arguments is correct - if (iArgument+nLocalArgument>nArgument) call handle_err(1,"missing argument startGRU or countGRU; type 'summa.exe --help' for correct usage") - read(argString(iArgument+1),*) startGRU ! read the argument of startGRU - read(argString(iArgument+2),*) nGRU ! read the argument of countGRU - if (startGRU<1 .or. nGRU<1) then - call handle_err(1,'startGRU and countGRU must be larger than 1.') - else - print '(A)', ' GRU-Parallelization run activated. '//trim(argString(iArgument+2))//' GRUs are selected for simulation.' - end if - - case ('-p', '--progress') - ! define the frequency to print progress - nLocalArgument = 1 - ! check if the number of command line arguments is correct - if (iArgument+nLocalArgument>nArgument) call handle_err(1, "missing argument freqProgress; type 'summa.exe --help' for correct usage") - select case (trim(argString(iArgument+1))) - case ('m' , 'month'); ixProgress = ixProgress_im - case ('d' , 'day'); ixProgress = ixProgress_id - case ('h' , 'hour'); ixProgress = ixProgress_ih - case ('n' , 'never'); ixProgress = ixProgress_never - case default; call handle_err(1,'unknown frequency to print progress') - end select - - case ('-r', '--restart') - ! define the frequency to write restart files - nLocalArgument = 1 - ! check if the number of command line arguments is correct - if (iArgument+nLocalArgument>nArgument) call handle_err(1, "missing argument freqRestart; type 'summa.exe --help' for correct usage") - select case (trim(argString(iArgument+1))) - case ('y' , 'year'); ixRestart = ixRestart_iy - case ('m' , 'month'); ixRestart = ixRestart_im - case ('d' , 'day'); ixRestart = ixRestart_id - case ('e' , 'end'); ixRestart = ixRestart_end - case ('n' , 'never'); ixRestart = ixRestart_never - case default; call handle_err(1,'unknown frequency to write restart files') - end select - - ! do nothing - case ('-v','--version') - - ! print help message - case ('--help') - call printCommandHelp - - case default - call printCommandHelp - call handle_err(1, 'unknown command line option') - - end select - end do ! looping through command line arguments - - ! check if master_file has been received. - if (len(trim(summaFileManagerFile))==0) call handle_err(1, "master_file is not received; type 'summa.exe --help' for correct usage") - - ! set startGRU for full run - if (iRunMode==iRunModeFull) startGRU=1 - - end subroutine getCommandArguments - - ! ************************************************************************************************** - ! internal subroutine to print the correct command line usage of SUMMA - ! ************************************************************************************************** - subroutine printCommandHelp() - implicit none - ! command line usage - print "(//A)",'Usage: summa.exe -m master_file [-s fileSuffix] [-g startGRU countGRU] [-h iHRU] [-r freqRestart] [-p freqProgress] [-c]' - print "(A,/)", ' summa.exe summa executable' - print "(A)", 'Running options:' - print "(A)", ' -m --master Define path/name of master file (required)' - print "(A)", ' -n --newFile Define frequency [noNewFiles,newFileEveryOct1] of new output files' - print "(A)", ' -s --suffix Add fileSuffix to the output files' - print "(A)", ' -g --gru Run a subset of countGRU GRUs starting from index startGRU' - print "(A)", ' -h --hru Run a single HRU with index of iHRU' - print "(A)", ' -r --restart Define frequency [y,m,d,e,never] to write restart files' - print "(A)", ' -p --progress Define frequency [m,d,h,never] to print progress' - print "(A)", ' -v --version Display version information of the current built' - stop - end subroutine printCommandHelp - - ! ************************************************************************************************** - ! internal subroutine handle_err: error handler - ! ************************************************************************************************** - subroutine handle_err(err,message) - ! used to handle error codes - USE var_lookup,only:iLookPROG,iLookDIAG,iLookFLUX,iLookPARAM,iLookINDEX ! named variables defining elements in data structure - implicit none - ! dummy variables - integer(i4b),intent(in) :: err ! error code - character(*),intent(in) :: message ! error message - ! local variables - integer(i4b) :: nc_err ! error code of nc_close - character(len=256) :: cmessage ! error message of the downwind routine - - ! return if A-OK - if(err==0) return - ! process error messages - if (err>0) then - write(*,'(//a/)') 'FATAL ERROR: '//trim(message) - else - write(*,'(//a/)') 'WARNING: '//trim(message); print*,'(can keep going, but stopping anyway)' - endif - ! dump variables - print*, 'error, variable dump:' - if(allocated(timeStruct%var))then - ! print time step - print*, 'modelTimeStep = ', modelTimeStep - ! print information for the HRUs - if(iGRU<=nGRU)then - if(iHRU<=gru_struc(iGRU)%hruCount)then - print*, 'initial time step = ', dt_init(iGRU)%hru(iHRU) - print*, 'HRU index = ', typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%hruId) - print*, 'pptrate = ', forcStruct%gru(iGRU)%hru(iHRU)%var(iLookFORCE%pptrate) - print*, 'airtemp = ', forcStruct%gru(iGRU)%hru(iHRU)%var(iLookFORCE%airtemp) - print*, 'theta_res = ', mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%theta_res)%dat(1) ! soil residual volumetric water content (-) - print*, 'theta_sat = ', mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%theta_sat)%dat(1) ! soil porosity (-) - print*, 'plantWiltPsi = ', mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%plantWiltPsi)%dat(1) ! matric head at wilting point (m) - print*, 'soilStressParam = ', mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%soilStressParam)%dat(1) ! parameter in the exponential soil stress function (-) - print*, 'critSoilWilting = ', mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%critSoilWilting)%dat(1) ! critical vol. liq. water content when plants are wilting (-) - print*, 'critSoilTranspire = ', mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%critSoilTranspire)%dat(1) ! critical vol. liq. water content when transpiration is limited (-) - print*, 'scalarSWE = ', progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarSWE)%dat(1) - print*, 'scalarSnowDepth = ', progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarSnowDepth)%dat(1) - print*, 'scalarCanopyTemp = ', progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyTemp)%dat(1) - print*, 'scalarRainPlusMelt = ', fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%scalarRainPlusMelt)%dat(1) - write(*,'(a,100(i4,1x))' ) 'layerType = ', indxStruct%gru(iGRU)%hru(iHRU)%var(iLookINDEX%layerType)%dat - write(*,'(a,100(f11.5,1x))') 'mLayerDepth = ', progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerDepth)%dat - write(*,'(a,100(f11.5,1x))') 'mLayerTemp = ', progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerTemp)%dat - write(*,'(a,100(f11.5,1x))') 'mLayerVolFracIce = ', progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracIce)%dat - write(*,'(a,100(f11.5,1x))') 'mLayerVolFracLiq = ', progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracLiq)%dat - print*, 'mLayerMatricHead = ', progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerMatricHead)%dat - print*, 'column inflow = ', fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%mLayerColumnInflow)%dat - endif ! if HRU is valid - endif ! if GRU is valid - endif ! if the time structure is allocated - print*,'error code = ', err - if(allocated(timeStruct%var)) print*, timeStruct%var - !write(*,'(a)') trim(message) - - ! close any remaining output files - do iFreq = 1,maxvarFreq - if (ncid(iFreq).ne.integerMissing) then - call nc_file_close(ncid(iFreq),nc_err,cmessage) - if(nc_err/=0) print*, trim(cmessage) - end if - end do - - stop 1 - end subroutine handle_err - - ! ************************************************************************************************** - ! internal subroutine stop_program: stop program execution - ! ************************************************************************************************** - subroutine stop_program(message) - ! used to stop program execution - implicit none - ! define dummy variables - character(*),intent(in)::message - ! define the local variables - integer(i4b),parameter :: outunit=6 ! write to screen - integer(i4b) :: ctime2(8) ! final time - real(dp) :: elpSec ! elapsed seconds - - ! close any remaining output files - ! NOTE: use the direct NetCDF call with no error checking since the file may already be closed - do iFreq = 1,maxvarFreq - if (ncid(iFreq).ne.integerMissing) then - err = nf90_close(ncid(iFreq)) - end if - end do - - ! get the final date and time - call date_and_time(values=ctime2) - - elpSec = elapsedSec(ctime1,ctime2) - - ! print initial and final date and time - write(outunit,"(/,A,I4,'-',I2.2,'-',I2.2,2x,I2,':',I2.2,':',I2.2,'.',I3.3)") 'initial date/time = ',ctime1(1:3),ctime1(5:8) - write(outunit,"(A,I4,'-',I2.2,'-',I2.2,2x,I2,':',I2.2,':',I2.2,'.',I3.3)") ' final date/time = ',ctime2(1:3),ctime2(5:8) - ! print elapsed time for the initialization - write(outunit,"(/,A,1PG15.7,A)") ' elapsed init = ', elapsedInit, ' s' - write(outunit,"(A,1PG15.7,A)") ' fraction init = ', elapsedInit/elpSec, ' s' - ! print elapsed time for the data read - write(outunit,"(/,A,1PG15.7,A)") ' elapsed read = ', elapsedRead, ' s' - write(outunit,"(A,1PG15.7,A)") ' fraction read = ', elapsedRead/elpSec, ' s' - ! print elapsed time for the data write - write(outunit,"(/,A,1PG15.7,A)") ' elapsed write = ', elapsedWrite, ' s' - write(outunit,"(A,1PG15.7,A)") ' fraction write = ', elapsedWrite/elpSec, ' s' - ! print elapsed time for the physics - write(outunit,"(/,A,1PG15.7,A)") ' elapsed physics = ', elapsedPhysics, ' s' - write(outunit,"(A,1PG15.7,A)") ' fraction physics = ', elapsedPhysics/elpSec, ' s' - ! print total elapsed time - write(outunit,"(/,A,1PG15.7,A)") ' elapsed time = ', elpSec, ' s' - write(outunit,"(A,1PG15.7,A)") ' or ', elpSec/60_dp, ' m' - write(outunit,"(A,1PG15.7,A)") ' or ', elpSec/3600_dp, ' h' - write(outunit,"(A,1PG15.7,A/)") ' or ', elpSec/86400_dp, ' d' - ! print the number of threads - write(outunit,"(A,i10,/)") ' number threads = ', nThreads - ! stop with message - print*,'FORTRAN STOP: '//trim(message) - stop - end subroutine - -end module summa_bmi - - - ! ************************************************************************************************** - ! private subroutine SOIL_VEG_GEN_PARM: Read soil, vegetation and other model parameters (from NOAH) - ! ************************************************************************************************** -!----------------------------------------------------------------- -SUBROUTINE SOIL_VEG_GEN_PARM(FILENAME_VEGTABLE, FILENAME_SOILTABLE, FILENAME_GENERAL, MMINLU, MMINSL) -!----------------------------------------------------------------- - use module_sf_noahlsm, only : shdtbl, nrotbl, rstbl, rgltbl, & - & hstbl, snuptbl, maxalb, laimintbl, & - & bb, drysmc, f11, maxsmc, laimaxtbl, & - & emissmintbl, emissmaxtbl, albedomintbl, & - & albedomaxtbl, wltsmc, qtz, refsmc, & - & z0mintbl, z0maxtbl, & - & satpsi, satdk, satdw, & - & theta_res, theta_sat, vGn_alpha, vGn_n, k_soil, & ! MPC add van Genutchen parameters - & fxexp_data, lvcoef_data, & - & lutype, maxalb, & - & slope_data, frzk_data, bare, cmcmax_data, & - & cfactr_data, csoil_data, czil_data, & - & refkdt_data, natural, refdk_data, & - & rsmax_data, salp_data, sbeta_data, & - & zbot_data, smhigh_data, smlow_data, & - & lucats, topt_data, slcats, slpcats, sltype - - IMPLICIT NONE - - CHARACTER(LEN=*), INTENT(IN) :: FILENAME_VEGTABLE, FILENAME_SOILTABLE, FILENAME_GENERAL - CHARACTER(LEN=*), INTENT(IN) :: MMINLU, MMINSL - integer :: LUMATCH, IINDEX, LC, NUM_SLOPE - integer :: ierr - INTEGER , PARAMETER :: OPEN_OK = 0 - - character*128 :: mess , message - -!-----SPECIFY VEGETATION RELATED CHARACTERISTICS : -! ALBBCK: SFC albedo (in percentage) -! Z0: Roughness length (m) -! SHDFAC: Green vegetation fraction (in percentage) -! Note: The ALBEDO, Z0, and SHDFAC values read from the following table -! ALBEDO, amd Z0 are specified in LAND-USE TABLE; and SHDFAC is -! the monthly green vegetation data -! CMXTBL: MAX CNPY Capacity (m) -! NROTBL: Rooting depth (layer) -! RSMIN: Mimimum stomatal resistance (s m-1) -! RSMAX: Max. stomatal resistance (s m-1) -! RGL: Parameters used in radiation stress function -! HS: Parameter used in vapor pressure deficit functio -! TOPT: Optimum transpiration air temperature. (K) -! CMCMAX: Maximum canopy water capacity -! CFACTR: Parameter used in the canopy inteception calculati -! SNUP: Threshold snow depth (in water equivalent m) that -! implies 100% snow cover -! LAI: Leaf area index (dimensionless) -! MAXALB: Upper bound on maximum albedo over deep snow -! -!-----READ IN VEGETAION PROPERTIES FROM VEGPARM.TBL -! - - OPEN(19, FILE=trim(FILENAME_VEGTABLE),FORM='FORMATTED',STATUS='OLD',IOSTAT=ierr) - IF(ierr .NE. OPEN_OK ) THEN - WRITE(message,FMT='(A)') & - 'module_sf_noahlsm.F: soil_veg_gen_parm: failure opening VEGPARM.TBL' - CALL wrf_error_fatal ( message ) - END IF - - LUMATCH=0 - - FIND_LUTYPE : DO WHILE (LUMATCH == 0) - READ (19,*,END=2002) - READ (19,*,END=2002)LUTYPE - READ (19,*)LUCATS,IINDEX - - IF(LUTYPE.EQ.MMINLU)THEN - WRITE( mess , * ) 'LANDUSE TYPE = ' // TRIM ( LUTYPE ) // ' FOUND', LUCATS,' CATEGORIES' - ! CALL wrf_message( mess ) - LUMATCH=1 - ELSE - call wrf_message ( "Skipping over LUTYPE = " // TRIM ( LUTYPE ) ) - DO LC = 1, LUCATS+12 - read(19,*) - ENDDO - ENDIF - ENDDO FIND_LUTYPE -! prevent possible array overwrite, Bill Bovermann, IBM, May 6, 2008 - IF ( SIZE(SHDTBL) < LUCATS .OR. & - SIZE(NROTBL) < LUCATS .OR. & - SIZE(RSTBL) < LUCATS .OR. & - SIZE(RGLTBL) < LUCATS .OR. & - SIZE(HSTBL) < LUCATS .OR. & - SIZE(SNUPTBL) < LUCATS .OR. & - SIZE(MAXALB) < LUCATS .OR. & - SIZE(LAIMINTBL) < LUCATS .OR. & - SIZE(LAIMAXTBL) < LUCATS .OR. & - SIZE(Z0MINTBL) < LUCATS .OR. & - SIZE(Z0MAXTBL) < LUCATS .OR. & - SIZE(ALBEDOMINTBL) < LUCATS .OR. & - SIZE(ALBEDOMAXTBL) < LUCATS .OR. & - SIZE(EMISSMINTBL ) < LUCATS .OR. & - SIZE(EMISSMAXTBL ) < LUCATS ) THEN - CALL wrf_error_fatal('Table sizes too small for value of LUCATS in module_sf_noahdrv.F') - ENDIF - - IF(LUTYPE.EQ.MMINLU)THEN - DO LC=1,LUCATS - READ (19,*)IINDEX,SHDTBL(LC), & - NROTBL(LC),RSTBL(LC),RGLTBL(LC),HSTBL(LC), & - SNUPTBL(LC),MAXALB(LC), LAIMINTBL(LC), & - LAIMAXTBL(LC),EMISSMINTBL(LC), & - EMISSMAXTBL(LC), ALBEDOMINTBL(LC), & - ALBEDOMAXTBL(LC), Z0MINTBL(LC), Z0MAXTBL(LC) - ENDDO - - READ (19,*) - READ (19,*)TOPT_DATA - READ (19,*) - READ (19,*)CMCMAX_DATA - READ (19,*) - READ (19,*)CFACTR_DATA - READ (19,*) - READ (19,*)RSMAX_DATA - READ (19,*) - READ (19,*)BARE - READ (19,*) - READ (19,*)NATURAL - ENDIF - -2002 CONTINUE - - CLOSE (19) - IF (LUMATCH == 0) then - CALL wrf_error_fatal ("Land Use Dataset '"//MMINLU//"' not found in VEGPARM.TBL.") - ENDIF - -! -!-----READ IN SOIL PROPERTIES FROM SOILPARM.TBL -! - OPEN(19, FILE=trim(FILENAME_SOILTABLE),FORM='FORMATTED',STATUS='OLD',IOSTAT=ierr) - IF(ierr .NE. OPEN_OK ) THEN - WRITE(message,FMT='(A)') & - 'module_sf_noahlsm.F: soil_veg_gen_parm: failure opening SOILPARM.TBL' - CALL wrf_error_fatal ( message ) - END IF - - WRITE(mess,*) 'INPUT SOIL TEXTURE CLASSIFICATION = ', TRIM ( MMINSL ) - ! CALL wrf_message( mess ) - - LUMATCH=0 - - ! MPC add a new soil table - FIND_soilTYPE : DO WHILE (LUMATCH == 0) - READ (19,*) - READ (19,*,END=2003)SLTYPE - READ (19,*)SLCATS,IINDEX - IF(SLTYPE.EQ.MMINSL)THEN - WRITE( mess , * ) 'SOIL TEXTURE CLASSIFICATION = ', TRIM ( SLTYPE ) , ' FOUND', & - SLCATS,' CATEGORIES' - ! CALL wrf_message ( mess ) - LUMATCH=1 - ELSE - call wrf_message ( "Skipping over SLTYPE = " // TRIM ( SLTYPE ) ) - DO LC = 1, SLCATS - read(19,*) - ENDDO - ENDIF - ENDDO FIND_soilTYPE - ! prevent possible array overwrite, Bill Bovermann, IBM, May 6, 2008 - IF ( SIZE(BB ) < SLCATS .OR. & - SIZE(DRYSMC) < SLCATS .OR. & - SIZE(F11 ) < SLCATS .OR. & - SIZE(MAXSMC) < SLCATS .OR. & - SIZE(REFSMC) < SLCATS .OR. & - SIZE(SATPSI) < SLCATS .OR. & - SIZE(SATDK ) < SLCATS .OR. & - SIZE(SATDW ) < SLCATS .OR. & - SIZE(WLTSMC) < SLCATS .OR. & - SIZE(QTZ ) < SLCATS ) THEN - CALL wrf_error_fatal('Table sizes too small for value of SLCATS in module_sf_noahdrv.F') - ENDIF - - ! MPC add new soil table - select case(trim(SLTYPE)) - case('STAS','STAS-RUC') ! original soil tables - DO LC=1,SLCATS - READ (19,*) IINDEX,BB(LC),DRYSMC(LC),F11(LC),MAXSMC(LC),& - REFSMC(LC),SATPSI(LC),SATDK(LC), SATDW(LC), & - WLTSMC(LC), QTZ(LC) - ENDDO - case('ROSETTA') ! new soil table - DO LC=1,SLCATS - READ (19,*) IINDEX,& - ! new soil parameters (from Rosetta) - theta_res(LC), theta_sat(LC), & - vGn_alpha(LC), vGn_n(LC), k_soil(LC), & - ! original soil parameters - BB(LC),DRYSMC(LC),F11(LC),MAXSMC(LC),& - REFSMC(LC),SATPSI(LC),SATDK(LC), SATDW(LC), & - WLTSMC(LC), QTZ(LC) - ENDDO - case default - CALL wrf_message( 'SOIL TEXTURE IN INPUT FILE DOES NOT ' ) - CALL wrf_message( 'MATCH SOILPARM TABLE' ) - CALL wrf_error_fatal ( 'INCONSISTENT OR MISSING SOILPARM FILE' ) - end select - -2003 CONTINUE - - CLOSE (19) - - IF(LUMATCH.EQ.0)THEN - CALL wrf_message( 'SOIL TEXTURE IN INPUT FILE DOES NOT ' ) - CALL wrf_message( 'MATCH SOILPARM TABLE' ) - CALL wrf_error_fatal ( 'INCONSISTENT OR MISSING SOILPARM FILE' ) - ENDIF - -! -!-----READ IN GENERAL PARAMETERS FROM GENPARM.TBL -! - OPEN(19, FILE=trim(FILENAME_GENERAL),FORM='FORMATTED',STATUS='OLD',IOSTAT=ierr) - IF(ierr .NE. OPEN_OK ) THEN - WRITE(message,FMT='(A)') & - 'module_sf_noahlsm.F: soil_veg_gen_parm: failure opening GENPARM.TBL' - CALL wrf_error_fatal ( message ) - END IF - - READ (19,*) - READ (19,*) - READ (19,*) NUM_SLOPE - - SLPCATS=NUM_SLOPE -! prevent possible array overwrite, Bill Bovermann, IBM, May 6, 2008 - IF ( SIZE(slope_data) < NUM_SLOPE ) THEN - CALL wrf_error_fatal('NUM_SLOPE too large for slope_data array in module_sf_noahdrv') - ENDIF - - DO LC=1,SLPCATS - READ (19,*)SLOPE_DATA(LC) - ENDDO - - READ (19,*) - READ (19,*)SBETA_DATA - READ (19,*) - READ (19,*)FXEXP_DATA - READ (19,*) - READ (19,*)CSOIL_DATA - READ (19,*) - READ (19,*)SALP_DATA - READ (19,*) - READ (19,*)REFDK_DATA - READ (19,*) - READ (19,*)REFKDT_DATA - READ (19,*) - READ (19,*)FRZK_DATA - READ (19,*) - READ (19,*)ZBOT_DATA - READ (19,*) - READ (19,*)CZIL_DATA - READ (19,*) - READ (19,*)SMLOW_DATA - READ (19,*) - READ (19,*)SMHIGH_DATA - READ (19,*) - READ (19,*)LVCOEF_DATA - CLOSE (19) - -!----------------------------------------------------------------- -END SUBROUTINE SOIL_VEG_GEN_PARM -!----------------------------------------------------------------- diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 6e84a7733..4b734bfca 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -18,13 +18,15 @@ ! You should have received a copy of the GNU General Public License ! along with this program. If not, see . -program summa_driver - ! driver program for summa simulations +module summa_bmi + ! provides functions needed for summa driver routines adding BMI functions ! ***************************************************************************** ! * use desired modules ! ***************************************************************************** ! data types + USE,intrinsic :: iso_c_binding, only: c_ptr, c_loc, c_f_pointer USE nrtype ! variable types, etc. + use bmif_2_0 ! BMI libraries USE summa_type, only: summa1_type_dec ! master summa data type ! subroutines and functions: model setup USE summa_init, only: summa_initialize ! used to allocate/initialize summa data structures @@ -40,67 +42,1000 @@ program summa_driver ! global data USE globalData, only: numtim ! number of model time steps USE globalData, only: print_step_freq + USE globalData, only: dJulianStart ! julian day of start time of simulation + USE globalData, only: dJulianFinsh ! julian day of end time of simulation + USE globalData, only: data_step ! length of time steps for the outermost timeloop + USE globalData, only: nHRUfile ! number of HRUs in the file, = gru_struc(iGRU)%hruCount and summa1_struc%nHRU + USE globalData, only: gru_struc ! gru-hru mapping structures + USE multiconst, only: secprday ! number of seconds in a day + ! provide access to the named variables that describe elements of parent model structures + USE var_lookup, only: iLookATTR ! look-up values for local attributes + USE var_lookup, only: iLookFLUX ! named variables for flux variables + USE var_lookup, only: iLookDIAG ! named variables for diagnostic variables + USE var_lookup, only: iLookPROG ! named variables for prognostic variables + implicit none + ! Define the attributes of the model. + type :: summa_model + integer(i4b) :: timeStep ! index of model time step + end type summa_model + + type, extends (bmi) :: summa_bmi + private + type (summa_model) :: model + contains + procedure :: get_component_name => summa_component_name + procedure :: get_input_item_count => summa_input_item_count + procedure :: get_output_item_count => summa_output_item_count + procedure :: get_input_var_names => summa_input_var_names + procedure :: get_output_var_names => summa_output_var_names + procedure :: initialize => summa_initialize + procedure :: finalize => summa_finalize + procedure :: get_start_time => summa_start_time + procedure :: get_end_time => summa_end_time + procedure :: get_current_time => summa_current_time + procedure :: get_time_step => summa_time_step + procedure :: get_time_units => summa_time_units + procedure :: update => summa_update + procedure :: update_until => summa_update_until + procedure :: get_var_grid => summa_var_grid + procedure :: get_grid_type => summa_grid_type + procedure :: get_grid_rank => summa_grid_rank + procedure :: get_grid_shape => summa_grid_shape + procedure :: get_grid_size => summa_grid_size + procedure :: get_grid_spacing => summa_grid_spacing + procedure :: get_grid_origin => summa_grid_origin + procedure :: get_grid_x => summa_grid_x + procedure :: get_grid_y => summa_grid_y + procedure :: get_grid_z => summa_grid_z + procedure :: get_grid_node_count => summa_grid_node_count + procedure :: get_grid_edge_count => summa_grid_edge_count + procedure :: get_grid_face_count => summa_grid_face_count + procedure :: get_grid_edge_nodes => summa_grid_edge_nodes + procedure :: get_grid_face_edges => summa_grid_face_edges + procedure :: get_grid_face_nodes => summa_grid_face_nodes + procedure :: get_grid_nodes_per_face => summa_grid_nodes_per_face + procedure :: get_var_type => summa_var_type + procedure :: get_var_units => summa_var_units + procedure :: get_var_itemsize => summa_var_itemsize + procedure :: get_var_nbytes => summa_var_nbytes + procedure :: get_var_location => summa_var_location + procedure :: get_value_int => summa_get_int + procedure :: get_value_float => summa_get_float + procedure :: get_value_double => summa_get_double + generic :: get_value => & + get_value_int, & + get_value_float, & + get_value_double + procedure :: get_value_ptr_int => summa_get_ptr_int + procedure :: get_value_ptr_float => summa_get_ptr_float + procedure :: get_value_ptr_double => summa_get_ptr_double + generic :: get_value_ptr => & + get_value_ptr_int, & + get_value_ptr_float, & + get_value_ptr_double + procedure :: get_value_at_indices_int => summa_get_at_indices_int + procedure :: get_value_at_indices_float => summa_get_at_indices_float + procedure :: get_value_at_indices_double => summa_get_at_indices_double + generic :: get_value_at_indices => & + get_value_at_indices_int, & + get_value_at_indices_float, & + get_value_at_indices_double + procedure :: set_value_int => summa_set_int + procedure :: set_value_float => summa_set_float + procedure :: set_value_double => summa_set_double + generic :: set_value => & + set_value_int, & + set_value_float, & + set_value_double + procedure :: set_value_at_indices_int => summa_set_at_indices_int + procedure :: set_value_at_indices_float => summa_set_at_indices_float + procedure :: set_value_at_indices_double => summa_set_at_indices_double + generic :: set_value_at_indices => & + set_value_at_indices_int, & + set_value_at_indices_float, & + set_value_at_indices_double + procedure :: print_model_info + end type summa_bmi + + private + public :: summa_bmi + ! ***************************************************************************** ! * variable definitions ! ***************************************************************************** + character (len=BMI_MAX_COMPONENT_NAME), target :: & + component_name = "Structure for Unifying Multiple Modeling Alternatives: SUMMA" ! define the master summa data structure type(summa1_type_dec), allocatable :: summa1_struc(:) ! define parameters for the model simulation integer(i4b), parameter :: n=1 ! number of instantiations - ! define timing information - integer(i4b) :: modelTimeStep ! index of model time step ! error control integer(i4b) :: err=0 ! error code character(len=1024) :: message='' ! error message + ! Exchange items + integer, parameter :: input_item_count = 1 + integer, parameter :: output_item_count = 16 + character (len=BMI_MAX_VAR_NAME), target,dimension(input_item_count) :: input_items + character (len=BMI_MAX_VAR_NAME), target,dimension(output_item_count) :: output_items + ! --------------------------------------------------------------------------------------- - ! ***************************************************************************** - ! * preliminaries - ! ***************************************************************************** + contains - ! allocate space for the master summa structure - allocate(summa1_struc(n), stat=err) - if(err/=0) call stop_program(1, 'problem allocating master summa structure') + ! ***************************************************************************** + ! * model setup/initialization + ! ***************************************************************************** + function summa_initialize(this, config_file) result (bmi_status) + class (summa_bmi), intent(out) :: this + character (len=*), intent(in) :: config_file + integer :: bmi_status, i - ! ***************************************************************************** - ! * model setup/initialization - ! ***************************************************************************** + ! initialize time steps + this%model%timeStep == 0 - ! declare and allocate summa data structures and initialize model state to known values - call summa_initialize(summa1_struc(n), err, message) - call handle_err(err, message) + ! allocate space for the master summa structure, could happen outside of BMI function + allocate(summa1_struc(n), stat=err) + if(err/=0) call stop_program(1, 'problem allocating master summa structure') - ! initialize parameter data structures (e.g. vegetation and soil parameters) - call summa_paramSetup(summa1_struc(n), err, message) - call handle_err(err, message) + ! if using the BMI interface, there is an argument pointing to the file manager file + ! then make sure summaFileManagerFile is set before executing initialization + if (len(config_file) > 0) then + do i=1,256 + if (dir(i) == C_NULL_CHAR) exit + summa1_struc%summaFileManagerFile(i:i)=config_file(i) ! copying character array to string + end do + endif - ! read restart data and reset the model state - call summa_readRestart(summa1_struc(n), err, message) - call handle_err(err, message) + ! declare and allocate summa data structures and initialize model state to known values + call summa_initialize(summa1_struc(n), err, message) + call handle_err(err, message) - ! ***************************************************************************** - ! * model simulation - ! ***************************************************************************** - ! loop through time - do modelTimeStep=1,numtim - - ! read model forcing data - call summa_readForcing(modelTimeStep, summa1_struc(n), err, message) - call handle_err(err, message) - - if (mod(modelTimeStep, print_step_freq) == 0)then - print *, 'step ---> ', modelTimeStep - endif - ! run the summa physics for one time step - call summa_runPhysics(modelTimeStep, summa1_struc(n), err, message) - call handle_err(err, message) - - ! write the model output - call summa_writeOutputFiles(modelTimeStep, summa1_struc(n), err, message) - call handle_err(err, message) - end do ! looping through time - - ! successful end - call stop_program(0, 'finished simulation successfully.') -end program summa_driver + ! initialize parameter data structures (e.g. vegetation and soil parameters) + call summa_paramSetup(summa1_struc(n), err, message) + call handle_err(err, message) + + ! read restart data and reset the model state + call summa_readRestart(summa1_struc(n), err, message) + call handle_err(err, message) + + bmi_status = BMI_SUCCESS + end function summa_initialize + + ! ***************************************************************************** + ! * advance model by one time step. + ! ***************************************************************************** + function summa_update(this) result (bmi_status) + class (summa_bmi), intent(inout) :: this + integer :: bmi_status + + ! read model forcing data + call summa_readForcing(this%model%timeStep, summa1_struc(n), err, message) + call handle_err(err, message) + + if (mod(this%model%timeStep, print_step_freq) == 0)then + print *, 'step ---> ', this%model%timeStep + endif + ! run the summa physics for one time step + call summa_runPhysics(this%model%timeStep, summa1_struc(n), err, message) + call handle_err(err, message) + + ! write the model output + call summa_writeOutputFiles(this%model%timeStep, summa1_struc(n), err, message) + call handle_err(err, message) + + bmi_status = BMI_SUCCESS + end function summa_update + + ! **************************************************************************** + ! * advance the model until the given time + ! **************************************************************************** + function summa_update_until(this, time) result (bmi_status) + class (summa_bmi), intent(inout) :: this + double precision, intent(in) :: time ! unit days (julian days, same as get_model_time) + integer :: bmi_status, istat + real :: current + + current = this%current_time() + if (time < current) then + bmi_status = BMI_FAILURE + return + end if + + ! loop through time + if (this%model%timeStep == 0) then + this%model%timeStep = 1 + end if + + do while (current < time .and. this%model%timeStep < numtim) + istat = this%update() + current = this%current_time() + this%model%timeStep = this%model%timeStep + 1 + end do ! (looping through time) + + bmi_status = BMI_SUCCESS + end function summa_update_until + + ! ***************************************************************************** + ! * successful end + ! ***************************************************************************** + function summa_finalize(this) result (bmi_status) + class (summa_bmi), intent(inout) :: this + integer :: bmi_status + + call stop_program(0, 'finished simulation successfully.') + bmi_status = BMI_SUCCESS + end function finalize + + ! ***************************************************************************** + ! * extra BMI functions + ! ***************************************************************************** + + ! Get the name of the model + function summa_component_name(this, name) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), pointer, intent(out) :: name + integer :: bmi_status + + name => component_name + bmi_status = BMI_SUCCESS + end function summa_component_name + + ! Count the input variables + function summa_input_item_count(this, count) result (bmi_status) + class (summa_bmi), intent(in) :: this + integer, intent(out) :: count + integer :: bmi_status + + count = input_item_count + bmi_status = BMI_SUCCESS + end function summa_input_item_count + + ! Count the output variables + function summa_output_item_count(this, count) result (bmi_status) + class (summa_bmi), intent(in) :: this + integer, intent(out) :: count + integer :: bmi_status + + count = output_item_count + bmi_status = BMI_SUCCESS + end function summa_output_item_count + + ! List input variables (none) + function summa_input_var_names(this, names) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (*), pointer, intent(out) :: names(:) + integer :: bmi_status + + input_items(1) = '' + names => input_items + bmi_status = BMI_SUCCESS + end function summa_input_var_names + + ! List output variables NAMES SHOULD BE STANDARDIZED "https://csdms.colorado.edu/wiki/CSDMS_Standard_Names" + function summa_output_var_names(this, names) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (*), pointer, intent(out) :: names(:) + integer :: bmi_status, i + + output_items(1) = 'land_surface_water__runoff_volume_flux' + output_items(2) = 'land_surface_water__evaporation_mass_flux' + output_items(3) = 'atmosphere_water__precipitation_mass_flux' + output_items(4) = 'land_vegetation_water__evaporation_mass_flux' + output_items(5) = 'land_vegetation_water__transpiration_mass_flux' + output_items(6) = 'snowpack__sublimation_mass_flux' + output_items(7) = 'land_vegetation_water__sublimation_mass_flux' + output_items(8) = 'snowpack_mass' + output_items(9) = 'soil_water__mass' + output_items(10)= 'land_vegetation_water__mass' + output_items(11)= 'land_surface_radiation~net~total__energy_flux' + output_items(12)= 'land_atmosphere_heat~net~latent__energy_flux' !(incoming to the *atmosphere*, since atmosphere is last) + output_items(13)= 'land_atmosphere_heat~net~sensible__energy_flux' !(incoming to the *atmosphere*, since atmosphere is last) + output_items(14)= 'atmosphere_energy~net~total__energy_flux' + output_items(15)= 'land_vegetation_energy~net~total__energy_flux' + output_items(16)= 'land_surface_energy~net~total__energy_flux' + names => output_items + bmi_status = BMI_SUCCESS + end function summa_output_var_names + + ! Model start time + function summa_start_time(this, time) result (bmi_status) + class (summa_bmi), intent(in) :: this + double precision, intent(out) :: time + integer :: bmi_status + + time = dJulianStart ! unit days + bmi_status = BMI_SUCCESS + end function summa_start_time + + ! Model end time + function summa_end_time(this, time) result (bmi_status) + class (summa_bmi), intent(in) :: this + double precision, intent(out) :: time + integer :: bmi_status + + time = dJulianFinsh ! unit days + bmi_status = BMI_SUCCESS + end function summa_end_time + + ! Model current time + function summa_current_time(this, time) result (bmi_status) + class (summa_bmi), intent(in) :: this + double precision, intent(out) :: time + integer :: bmi_status + + if(this%model%timeStep==1)then + time = dJulianStart ! unit days + else + time = dJulianStart + (data_step*real(this%model%timeStep-1,dp))/secprday ! unit days + end if + bmi_status = BMI_SUCCESS + end function summa_current_time + + ! Model time step + function summa_time_step(this, time_step) result (bmi_status) + class (summa_bmi), intent(in) :: this + double precision, intent(out) :: time_step + integer :: bmi_status + + time_step = data_step ! unit seconds + bmi_status = BMI_SUCCESS + end function summa_time_step + + ! Model time units + function summa_time_units(this, units) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(out) :: units + integer :: bmi_status + + units = "s" + bmi_status = BMI_SUCCESS + end function summa_time_units + + ! Get the grid id for a particular variable + function summa_var_grid(this, name, grid) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + integer, intent(out) :: grid + integer :: bmi_status + + select case(name) + case default + grid = 0 + bmi_status = BMI_SUCCESS + end select + end function summa_var_grid + + ! The type of a variable's grid + function summa_grid_type(this, grid, type) result (bmi_status) + class (summa_bmi), intent(in) :: this + integer, intent(in) :: grid + character (len=*), intent(out) :: type + integer :: bmi_status + + select case(grid) + case(0) + type = 'points' + bmi_status = BMI_SUCCESS + case default + shape(:) = -1 + bmi_status = BMI_FAILURE + end select + end function summa_grid_type + + ! The number of dimensions of a grid, latitude and longitude and elevation + function summa_grid_rank(this, grid, rank) result (bmi_status) + class (summa_bmi), intent(in) :: this + integer, intent(in) :: grid + integer, intent(out) :: rank + integer :: bmi_status + + select case(grid) + case(0) + rank = 3 + bmi_status = BMI_SUCCESS + case default + rank = -1 + bmi_status = BMI_FAILURE + end select + end function summa_grid_rank + + ! The dimensions of a grid, not applicable to unstructured + function summa_grid_shape(this, grid, shape) result (bmi_status) + class (summa_bmi), intent(in) :: this + integer, intent(in) :: grid + integer, dimension(:), intent(out) :: shape + integer :: bmi_status + + select case(grid) + case default + shape(:) = -1 + bmi_status = BMI_FAILURE + end select + end function summa_grid_shape + + ! The total number of elements in a grid + function summa_grid_size(this, grid, size) result (bmi_status) + class (summa_bmi), intent(in) :: this + integer, intent(in) :: grid + integer, intent(out) :: size + integer :: bmi_status + + select case(grid) + case default + size = nHRUcount + bmi_status = BMI_FAILURE + end select + end function summa_grid_size + + ! The distance between nodes of a grid, not applicable to unstructured + function summa_grid_spacing(this, grid, spacing) result (bmi_status) + class (summa_bmi), intent(in) :: this + integer, intent(in) :: grid + double precision, dimension(:), intent(out) :: spacing + integer :: bmi_status + + select case(grid) + case default + spacing(:) = -1.d0 + bmi_status = BMI_FAILURE + end select + end function summa_grid_spacing + + ! Coordinates of grid origin, not applicable to unstructured + function summa_grid_origin(this, grid, origin) result (bmi_status) + class (summa_bmi), intent(in) :: this + integer, intent(in) :: grid + double precision, dimension(:), intent(out) :: origin + integer :: bmi_status + + select case(grid) + case default + origin(:) = -1.d0 + bmi_status = BMI_FAILURE + end select + end function summa_grid_origin + + ! X-coordinates of grid nodes, longitude (degrees east) + function summa_grid_x(this, grid, x) result (bmi_status) + class (summa_bmi), intent(in) :: this + integer, intent(in) :: grid + double precision, dimension(:), intent(out) :: x + integer :: bmi_status, iGRU, jHRU + summaVars: associate(attrStruct => summa1_struc%attrStruct) ! x%gru(:)%hru(:)%var(:) -- local attributes for each HRU + + select case(grid) + case default + do iGRU = 1, summa1_struc%nGRU + do jHRU = 1, gru_struc(iGRU)%hruCount + x((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = attrStruct%gru(iGRU)%hru(jHRU)%var(iLookATTR%longitude) + end do + end do + bmi_status = BMI_SUCCESS + end select + end associate summaVars + end function summa_grid_x + + ! Y-coordinates of grid nodes, latitude (degrees north) + function summa_grid_y(this, grid, y) result (bmi_status) + class (summa_bmi), intent(in) :: this + integer, intent(in) :: grid + double precision, dimension(:), intent(out) :: y + integer :: bmi_status, iGRU, jHRU + summaVars: associate(attrStruct => summa1_struc%attrStruct) ! x%gru(:)%hru(:)%var(:) -- local attributes for each HRU + + select case(grid) + case default + do iGRU = 1, summa1_struc%nGRU + do jHRU = 1, gru_struc(iGRU)%hruCount + y((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = attrStruct%gru(iGRU)%hru(jHRU)%var(iLookATTR%latitude) + end do + end do + bmi_status = BMI_SUCCESS + end select + end associate summaVars + end function summa_grid_y + + ! Z-coordinates of grid nodes, elevation (m) + function summa_grid_z(this, grid, z) result (bmi_status) + class (summa_bmi), intent(in) :: this + integer, intent(in) :: grid + double precision, dimension(:), intent(out) :: z + integer :: bmi_status, iGRU, jHRUs + summaVars: associate(attrStruct => summa1_struc%attrStruct) ! x%gru(:)%hru(:)%var(:) -- local attributes for each HRU + + select case(grid) + case default + do iGRU = 1, summa1_struc%nGRU + do jHRU = 1, gru_struc(iGRU)%hruCount + z((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = attrStruct%gru(iGRU)%hru(jHRU)%var(iLookATTR%elevation) + end do + end do + bmi_status = BMI_SUCCESS + end select + end associate summaVars + end function summa_grid_z + + ! Get the number of nodes in an unstructured grid + function summa_grid_node_count(this, grid, count) result(bmi_status) + class(summa_bmi), intent(in) :: this + integer, intent(in) :: grid + integer, intent(out) :: count + integer :: bmi_status + + select case(grid) + case default + count = nHRUfile + bmi_status = BMI_SUCCESS + end select + end function summa_grid_node_count + + ! Get the number of edges in an unstructured grid, points is 0 BUT COULD BE FOR GRUs + function summa_grid_edge_count(this, grid, count) result(bmi_status) + class(summa_bmi), intent(in) :: this + integer, intent(in) :: grid + integer, intent(out) :: count + integer :: bmi_status + + select case(grid) + case default + count = 0 + bmi_status = BMI_SUCCESS + end select + end function summa_grid_edge_count + + ! Get the number of faces in an unstructured grid, points is 0 BUT COULD BE FOR GRUs + function summa_grid_face_count(this, grid, count) result(bmi_status) + class(summa_bmi), intent(in) :: this + integer, intent(in) :: grid + integer, intent(out) :: count + integer :: bmi_status + + select case(grid) + case default + count = 0 + bmi_status = BMI_SUCCESS + end select + end function summa_grid_face_count + + ! Get the edge-node connectivity, points is 0 BUT COULD BE FOR GRUs + function summa_grid_edge_nodes(this, grid, edge_nodes) result(bmi_status) + class(summa_bmi), intent(in) :: this + integer, intent(in) :: grid + integer, dimension(:), intent(out) :: edge_nodes + integer :: bmi_status + + select case(grid) + case default + edge_nodes(:) = -1 + bmi_status = BMI_FAILURE + end select + end function summa_grid_edge_nodes + + ! Get the face-edge connectivity, points is 0 BUT COULD BE FOR GRUs + function summa_grid_face_edges(this, grid, face_edges) result(bmi_status) + class(summa_bmi), intent(in) :: this + integer, intent(in) :: grid + integer, dimension(:), intent(out) :: face_edges + integer :: bmi_status + + select case(grid) + case default + face_edges(:) = -1 + bmi_status = BMI_FAILURE + end select + end function summa_grid_face_edges + + ! Get the face-node connectivity, points is 0 BUT COULD BE FOR GRUs + function summa_grid_face_nodes(this, grid, face_nodes) result(bmi_status) + class(summa_bmi), intent(in) :: this + integer, intent(in) :: grid + integer, dimension(:), intent(out) :: face_nodes + integer :: bmi_status + + select case(grid) + case default + face_nodes(:) = -1 + bmi_status = BMI_FAILURE + end select + end function summa_grid_face_nodes + + ! Get the number of nodes for each face, points is 0 BUT COULD BE FOR GRUs + function summa_grid_nodes_per_face(this, grid, nodes_per_face) result(bmi_status) + class(summa_bmi), intent(in) :: this + integer, intent(in) :: grid + integer, dimension(:), intent(out) :: nodes_per_face + integer :: bmi_status + + select case(grid) + case default + nodes_per_face(:) = -1 + bmi_status = BMI_SUCCESS + end select + end function summa_grid_nodes_per_face + + ! The data type of the variable, as a string + function summa_var_type(this, name, units) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + character (len=*), intent(out) :: type + integer :: bmi_status + + select case(name) + case default + type = "real" + bmi_status = BMI_SUCCESS + end select + end function summa_var_type + + ! The units of the given variable + function summa_var_units(this, name, units) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + character (len=*), intent(out) :: units + integer :: bmi_status + + select case (name) + case('land_surface_water__runoff_volume_flux') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS + case('land_surface_water__evaporation_mass_flux') ; units = 'kg m-2 s-1'; bmi_status = BMI_SUCCESS + case('atmosphere_water__precipitation_mass_flux') ; units = 'kg m-2 s-1'; bmi_status = BMI_SUCCESS + case('land_vegetation_water__evaporation_mass_flux') ; units = 'kg m-2 s-1'; bmi_status = BMI_SUCCESS + case('land_vegetation_water__transpiration_mass_flux'); units = 'kg m-2 s-1'; bmi_status = BMI_SUCCESS + case('snowpack__sublimation_mass_flux') ; units = 'kg m-2 s-1'; bmi_status = BMI_SUCCESS + case('land_vegetation_water__sublimation_mass_flux') ; units = 'kg m-2 s-1'; bmi_status = BMI_SUCCESS + case('snowpack_mass') ; units = 'kg m-2' ; bmi_status = BMI_SUCCESS + case('soil_water__mass') ; units = 'kg m-2' ; bmi_status = BMI_SUCCESS + case('land_vegetation_water__mass') ; units = 'kg m-2' ; bmi_status = BMI_SUCCESS + case('land_surface_radiation~net~total__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS + case('land_atmosphere_heat~net~latent__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS + case('land_atmosphere_heat~net~sensible__energy_flux'); units = 'W m-2' ; bmi_status = BMI_SUCCESS + case('atmosphere_energy~net~total__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS + case('land_vegetation_energy~net~total__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS + case('land_surface_energy~net~total__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS + case default; units = "-"; bmi_status = BMI_FAILURE + end select + end function summa_var_units + + ! Memory use per array element + function summa_var_itemsize(this, name, size) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + integer, intent(out) :: size + real :: targetarr + integer :: bmi_status + + select case(name) + case default + size = sizeof(get_basin_field(name, 1, 1, targetarr)) ! 'sizeof' in gcc & ifort + bmi_status = BMI_SUCCESS + end select + end function summa_var_itemsize + + ! The size of the given variable + function summa_var_nbytes(this, name, nbytes) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + integer, intent(out) :: nbytes + integer :: bmi_status + integer :: s1, s2, s3, grid, grid_size, item_size + + s1 = this%get_var_grid(name, grid) + s2 = this%get_grid_size(grid, grid_size) + s3 = this%get_var_itemsize(name, item_size) + if ((s1 == BMI_SUCCESS).and.(s2 == BMI_SUCCESS).and.(s3 == BMI_SUCCESS)) then + nbytes = item_size * grid_size + bmi_status = BMI_SUCCESS + else + nbytes = -1 + bmi_status = BMI_FAILURE + end if + end function summa_var_nbytes + + ! The location (node, face, edge) of the given variable + function summa_var_location(this, name, location) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + character (len=*), intent(out) :: location + integer :: bmi_status + + select case(name) + case default + location = "node" + bmi_status = BMI_SUCCESS + end select + end function summa_var_location + + ! Get a copy of a integer variable's values, flattened + function summa_get_int(this, name, dest) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + integer, intent(inout) :: dest(:) + integer :: bmi_status + + select case(name) + case default + dest(:) = -1 + bmi_status = BMI_FAILURE + end select + end function summa_get_int + + ! Get a copy of a real variable's values, flattened + function summa_get_float(this, name, dest) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + real, intent(inout) :: dest(:) + real :: targetarr + integer :: bmi_status, iGRU, jHRU + + select case(name) + case default + do iGRU = 1, summa1_struc%nGRU + do jHRU = 1, gru_struc(iGRU)%hruCount + dest((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = get_basin_field(name, iGRU, jHRU, targetarr) + end do + end do + bmi_status = BMI_SUCCESS + end select + end function summa_get_float + + ! Get a copy of a double variable's values, flattened + function summa_get_double(this, name, dest) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + double precision, intent(inout) :: dest(:) + integer :: bmi_status + + select case(name) + case default + dest(:) = -1.d0 + bmi_status = BMI_FAILURE + end select + end function summa_get_double + + ! Get a reference to an integer-valued variable, flattened + function summa_get_ptr_int(this, name, dest_ptr) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + integer, pointer, intent(inout) :: dest_ptr(:) + integer :: bmi_status, n_elements + type (c_ptr) :: src + + select case(name) + case default + bmi_status = BMI_FAILURE + end select + end function summa_get_ptr_int + + ! Get a reference to a real-valued variable, flattened + function summa_get_ptr_float(this, name, dest_ptr) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + real, pointer, intent(inout) :: dest_ptr(:) + integer :: bmi_status, n_elements + real :: targetarr + type (c_ptr) :: src + + select case(name) + case default + src = c_loc(get_basin_field(name, 1, 1, targetarr)) + n_elements = nHRUcount + call c_f_pointer(src, dest_ptr, [n_elements]) + bmi_status = BMI_SUCCESS + end select + end function summa_get_ptr_float + + ! Get a reference to an double-valued variable, flattened + function summa_get_ptr_double(this, name, dest_ptr) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + double precision, pointer, intent(inout) :: dest_ptr(:) + integer :: bmi_status, n_elements + type (c_ptr) :: src + + select case(name) + case default + bmi_status = BMI_FAILURE + end select + end function summa_get_ptr_double + + ! Get values of an integer variable at the given locations + function summa_get_at_indices_int(this, name, dest, inds) & + result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + integer, intent(inout) :: dest(:) + integer, intent(in) :: inds(:) + integer :: bmi_status, i, n_elements + type (c_ptr) src + integer, pointer :: src_flattened(:) + + select case(name) + case default + bmi_status = BMI_FAILURE + end select + end function summa_get_at_indices_int + + ! Get values of a real variable at the given locations + function summa_get_at_indices_float(this, name, dest, inds) & + result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + real, intent(inout) :: dest(:) + integer, intent(in) :: inds(:) + integer :: bmi_status, i, n_elements + real :: targetarr + type (c_ptr) src + real, pointer :: src_flattened(:) + + select case(name) + case default + src = c_loc(get_basin_field(name, 1, 1, targetarr)) + call c_f_pointer(src, src_flattened, [n_elements]) + n_elements = size(inds) + do i = 1, n_elements + dest(i) = src_flattened(inds(i)) + end do + bmi_status = BMI_SUCCESS + end select + end function summa_get_at_indices_float + + ! Get values of a double variable at the given locations + function summa_get_at_indices_double(this, name, dest, inds) & + result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + double precision, intent(inout) :: dest(:) + integer, intent(in) :: inds(:) + integer :: bmi_status, i, n_elements + type (c_ptr) src + double precision, pointer :: src_flattened(:) + + select case(name) + case default + bmi_status = BMI_FAILURE + end select + end function summa_get_at_indices_double + + ! Set new integer values + function summa_set_int(this, name, src) result (bmi_status) + class (summa_bmi), intent(inout) :: this + character (len=*), intent(in) :: name + integer, intent(in) :: src(:) + integer :: bmi_status + + select case(name) + case default + bmi_status = BMI_FAILURE + end select + end function summa_set_int + + ! Set new real values, ONLY FOR INPUT VARIABLES + function summa_set_float(this, name, src) result (bmi_status) + class (summa_bmi), intent(inout) :: this + character (len=*), intent(in) :: name + real, intent(in) :: src(:) + integer :: bmi_status + + select case(name) + case default + bmi_status = BMI_FAILURE + end select + end function summa_set_float + + ! Set new double values + function summa_set_double(this, name, src) result (bmi_status) + class (summa_bmi), intent(inout) :: this + character (len=*), intent(in) :: name + double precision, intent(in) :: src(:) + integer :: bmi_status + + select case(name) + case default + bmi_status = BMI_FAILURE + end select + end function summa_set_double + + ! Set integer values at particular locations + function summa_set_at_indices_int(this, name, inds, src) & + result (bmi_status) + class (summa_bmi), intent(inout) :: this + character (len=*), intent(in) :: name + integer, intent(in) :: inds(:) + integer, intent(in) :: src(:) + integer :: bmi_status + type (c_ptr) dest + integer, pointer :: dest_flattened(:) + integer :: i + + select case(name) + case default + bmi_status = BMI_FAILURE + end select + end function summa_set_at_indices_int + + ! Set real values at particular locations, ONLY FOR INPUT VARIABLES + function summa_set_at_indices_float(this, name, inds, src) & + result (bmi_status) + class (summa_bmi), intent(inout) :: this + character (len=*), intent(in) :: name + integer, intent(in) :: inds(:) + real, intent(in) :: src(:) + integer :: bmi_status + type (c_ptr) dest + real, pointer :: dest_flattened(:) + integer :: i + + select case(name) + case default + bmi_status = BMI_FAILURE + end select + end function summa_set_at_indices_float + + ! Set double values at particular locations + function summa_set_at_indices_double(this, name, inds, src) & + result (bmi_status) + class (summa_bmi), intent(inout) :: this + character (len=*), intent(in) :: name + integer, intent(in) :: inds(:) + double precision, intent(in) :: src(:) + integer :: bmi_status + type (c_ptr) dest + double precision, pointer :: dest_flattened(:) + integer :: i + + select case(name) + case default + bmi_status = BMI_FAILURE + end select + end function summa_set_at_indices_double + + ! non-BMI helper function to get fields + function get_basin_field(name, iGRU, jHRU, targetarr) + implicit none + integer, intent(in) :: name, iGRU, jHRU + real, intent(out) :: targetarr + summaVars: associate(& + forcStruct => summa1_struc%forcStruct , & ! x%gru(:)%hru(:)%var(:) -- model forcing data + progStruct => summa1_struc%progStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model prognostic (state) variables + diagStruct => summa1_struc%diagStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model diagnostic variables + fluxStruct => summa1_struc%fluxStruct & ! x%gru(:)%hru(:)%var(:)%dat -- model fluxes + ) + + select case (name) + case('land_surface_water__runoff_volume_flux') + targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarTotalRunoff)%dat(1) + case('land_surface_water__evaporation_mass_flux') + targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarGroundEvaporation)%dat(1) + case('atmosphere_water__precipitation_mass_flux') + targetarr = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORC%pptrate)%dat(1) + case('land_vegetation_water__evaporation_mass_flux') + targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) + case('land_vegetation_water__transpiration_mass_flux') + targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) + case('snowpack__sublimation_mass_flux') + targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarSnowSublimation)%dat(1) + case('land_vegetation_water__sublimation_mass_flux') + targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopySublimation)%dat(1) + case('snowpack_mass') + targetarr = progStruct%gru(iGRU)%hru(jHRU)%var(iLookPROG%scalarSWE)%dat(1) + case('soil_water__mass') + targetarr = diagStruct%gru(iGRU)%hru(jHRU)%var(iLookDIAG%scalarTotalSoilWat)%dat(1) + case('land_vegetation_water__mass') + targetarr = progStruct%gru(iGRU)%hru(jHRU)%var(iLookPROG%scalarCanopyWat)%dat(1) + case('land_surface_radiation~net~total__energy_flux') + targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarNetRadiation)%dat(1) + case('land_atmosphere_heat~net~latent__energy_flux') + targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarLatHeatTotal)%dat(1) + case('land_atmosphere_heat~net~sensible__energy_flux') + targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarSenHeatTotal)%dat(1) + case('atmosphere_energy~net~total__energy_flux') + targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1) + case('land_vegetation_energy~net~total__energy_flux') + targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1) + case('land_surface_energy~net~total__energy_flux') + targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1) + end select + end associate summaVars + end function get_basin_field + +end program summa_bmi diff --git a/build/source/driver/summa_run.f90 b/build/source/driver/summa_run.f90 new file mode 100644 index 000000000..f7dd7126c --- /dev/null +++ b/build/source/driver/summa_run.f90 @@ -0,0 +1,52 @@ +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . + +program summa_run + ! driver program for summa simulations + ! ***************************************************************************** + ! * use desired modules + ! ***************************************************************************** + ! subroutines and functions: model simulation + USE summa_bmi, only: initialize, update, finalize + ! global data + USE globalData,only:numtim ! number of time steps + + implicit none + + ! ***************************************************************************** + ! * variable definitions + ! ***************************************************************************** + type (summa_bmi) :: model + integer(i4b) :: istat + ! define timing information + integer(i4b) :: modelTimeStep ! index of model time step + + ! ***************************************************************************** + ! * model simulation + ! ***************************************************************************** + istat = model%initialize() + + ! loop through time + do modelTimeStep=1,numtim + istat = model%update() + end do ! (looping through time) + istat = model%finalize() + +end program summa_run diff --git a/build/source/driver/summa_runBMI.f90 b/build/source/driver/summa_runBMI.f90 new file mode 100644 index 000000000..ba47e0b28 --- /dev/null +++ b/build/source/driver/summa_runBMI.f90 @@ -0,0 +1,90 @@ +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . + +program summa_runBMI + ! driver program for summa simulations + ! ***************************************************************************** + ! * use desired modules + ! ***************************************************************************** + ! subroutines and functions: model simulation + use summa_bmi + use, intrinsic :: iso_fortran_env, only : file_unit=>input_unit + + implicit none + + ! ***************************************************************************** + ! * variable definitions + ! ***************************************************************************** + character (len=*), parameter :: output_file = "summa_bmi.out" + character (len=*), parameter :: var_name = "land_surface_water__runoff_volume_flux" + type (summa_bmi) :: model + integer :: arg_count = 0 + character (len=80) :: arg + integer(i4b) :: i, j, istat, grid_id, grid_size + real :: current_time, end_time + real, allocatable :: runoff(:) + + ! ***************************************************************************** + ! * model simulation + ! ***************************************************************************** + do while (arg_count <= 1) + call get_command_argument(arg_count, arg) + arg_count = arg_count + 1 + end do + + if (len_trim(arg) == 0) then + write(*,"(a)") "Usage: summa_bmi CONFIGURATION_FILE" + write(*,"(a)") + write(*,"(a)") "Run the summa model through its BMI with a configuration file." + write(*,"(a)") "Output is written to the file `summa_bmi.out`." + stop + end if + + open(file_unit,file=output_file) + + write(file_unit,"(a)") "Initialize model." + istat = model%initialize(arg) + + istat = model%get_current_time(current_time) + istat = model%get_end_time(end_time) + istat = model%get_var_grid(var_name, grid_id) + istat = model%get_grid_size(grid_id, grid_size) + + allocate(runoff(grid_size)) + + do while (current_time <= end_time) + write(file_unit,"(a, f6.1)") "Model values at time = ", current_time + istat = model%get_value(var_name, runoff) + do j = 1, grid_size + write (file_unit,"(f6.1)", advance="no") runoff(j) + end do + write (file_unit,*) + end do + istat = model%update() + istat = model%get_current_time(current_time) + end do + + deallocate(runoff) + istat = model%finalize() + write(file_unit,"(a)") "Finalize model." + + close(file_unit) + +end program summa_runBMI diff --git a/build/source/driver/test_bmi.f90 b/build/source/driver/test_bmi.f90 deleted file mode 100644 index 44f967cf5..000000000 --- a/build/source/driver/test_bmi.f90 +++ /dev/null @@ -1,52 +0,0 @@ -program test_bmi - - use summa_bmi, only: initialize, update, finalize, & - get_output_name, get_output_units, & - get_num_basins, get_num_output_fields, & - get_latlons, get_basin_field, modelTimeStep - use globalData,only:numtim - - implicit none - - character(64), allocatable :: dir - character(64) :: fldid, fldun - real, allocatable :: lats(:), lons(:), Q(:) - integer :: istat, iseq, idt, count, ifld, nflds, ndt - - write(0,*) "" - write(0,*) "INITIALIZING " - write(0,*) "-------------------------------------------------------" - istat = initialize(dir)!, iseq) - - count = get_num_basins() - write(0,*) "The number of basins is", count - allocate(lats(count), lons(count), Q(count)) - if(istat/=0) stop istat - - call get_latlons(lats, lons) - write(0,*) "The latitudes are", lats - write(0,*) "The longitudes are", lons - nflds = get_num_output_fields() - - write(0,*) "" - write(0,*) "BEGIN TIME LOOP" - write(0,*) "-------------------------------------------------------" - DO modelTimeStep = 1, numtim - istat = update() - if(istat/=0) stop istat - write(0,*) "------------- TS ", modelTimeStep, "-------------" - call get_basin_field( 1, Q) - call get_output_name( 1, fldid) - call get_output_units(1, fldun) - write(0,*) fldid, " max = ", maxval(Q), ", min = ", minval(Q), " " , fldun - enddo - - write(0,*) "" - write(0,*) "FINALIZING" - write(0,*) "-------------------------------------------------------" - istat = finalize() - if(istat/=0) stop istat - deallocate(lats, lons, Q) - stop 0 - -end program diff --git a/sundials/flags_params.txt b/sundials/flags_params.txt deleted file mode 100644 index 50242d808..000000000 --- a/sundials/flags_params.txt +++ /dev/null @@ -1,26 +0,0 @@ - -To switch between SUMMA-BE and SUMMA-SUNDIALS, the "diffEqSolv" variabe has been added to the var_lookup module. -A user should add this variable to the model_decision file with one of the values "backwEuler" or "sundialIDA". - For now, this value is by default "sundialIDA" in mDecisions.f90. - -In energy equation, the heat capacity is computed using either the analytical (closed) formula or the finite differece formula (dH_T/dT). The -"howHeatCap" varialbe has been added to the var_lookup module to handle such decision. A user should add this variable to the model_decision -file with one of the values "closedForm" or "enthalpyFD". For now, this value is by default "colsedForm" in mDecisions.f90. - -All SUMMA-SUNDIALS files are in build/source/engine. The important ones are as following: - -systemSolvSundials.f90: contains public subroutine systemSolvSundials which runs the coupled energy-mass model for one timestep - -solveByIDA.f90: contains public subroutine solveByIDA which solves the differential equation system F(y,y') = 0 by IDA (y is the state vector) - and private subroutines setInitialCondition and setSolverParams. - setSolverParams can be used to to set paprmeters (maximum order, number of nonlinear iteration , etc) in IDA solver - -evalDAE4IDA.f90: contains public function evalDAE4IDA which is the interface wrapper for computing the residual vector F(t,y,y') required for IDA solver - -eval8DAE.f90: contains public subroutine eval8DAE which computes the residual vector mainly by calling varExtractSundials, updateVarsSundials, computFlux, - and computResidDAE. We also switch between different forms of the energy equation in this subroutine. - -evalJac4IDA.f90: contains public function evalJac4IDA which is the interface wrapper for computing the Jacobian matrix dF/dy + c dF/dy' for IDA solver - -eval8JacDAE.f90: contains public subroutine eval8JacDAE which computes the residual vector and the Jacobian matrix mainly by calling computJacDAE - diff --git a/sundials/installation.txt b/sundials/installation.txt deleted file mode 100644 index 89188b73c..000000000 --- a/sundials/installation.txt +++ /dev/null @@ -1,79 +0,0 @@ - -1. Download the latest release of IDA solver from SUNDIALS package in https://computing.llnl.gov/projects/sundials/sundials-software -% wget "https://github.com/LLNL/sundials/releases/download/v6.3.0/sundials-6.3.0.tar.gz" - -2. Extract the corresponding compressed file -% tar -xzf sundials-6.3.0.tar.gz - -3. Read INSTALL_GUIDE.pdf and follow the installation instructions. Roughly, do the following: -Make a directory outside the sundials-6.3.0 folder and enter it, and make the install and build dirs, enter the build dir. -% mkdir sundials -% cd sundials -% mkdir instdir -% mkdir buildir -% cd /buildir -NOTE if you need to recompile after a system upgrade, delete the contents of these directories before running cmake in the next step! - -4. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, the following setting needs to be enabled while running the cmake inside the builddir, using your home directory as $(YOUR_HOME) -% cmake ../sundials-5.8.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON DCMAKE_INSTALL_PREFIX=$(YOUR_HOME)/sundials/instdir -DEXAMPLES_INSTALL_PATH=$(YOUR_HOME)/sundials/instdir/examples -You can do this by running from inside buildir, where the star is the system specific file with hardcoded paths: -cp ../../summa/build/build_cmakeSundials_* build_cmake -./build_cmake - - -5. The default compiler is gfortan. To change it (it should be the same as the netcdf build and the later summa build), you could also add the following option to cmake with your $(YOUR_GFORTRAN) - -DCMAKE_Fortran_COMPILER=$(YOUR_GFORTRAN) - -6. If the above went well, staying in the buildir directory run -% make -% make install - -7. Enter summa directory and build summa as in /docs/SUMMA_installation.md. You will have to edit the SUMMA Makefile as below additionally (as well as install required libraries and link them as directed in the Makefile). You need to clone summa before you can change the makefile - git clone https://git.cs.usask.ca/numerical_simulations_lab/summa.git - - -8. In SUMMA Makefile, define the FC_EXE to be the same compiler as you used for sundials and netcdf - -9. In SUMMA Makefile, define the SUNDIALS installation directory (and all the folders as required in the SUMMA instructions. - DIR_SUNDIALS=$(YOUR_HOME)/sundials/instdir - -10. Inside the /summa/build/ directory run -% make -or % source build_summa_* if you are using a specific * compiler - -11. On some installations you may need to add to your .bashrc file (and run source .bashrc) -LD_LIBRARY_PATH='$(YOUR_HOME)/sundials/instdir/lib64' -export LD_LIBRARY_PATH - - -NOTE. If you'd like to print the Jacobians going into the Sundials IDA solver, you need to do the following - -1. to file sundials-6.3.0/src/ida/ida_ls.c around line 1345, should be after: - - /* Call Jacobian routine */ - retval = idals_mem->jac(IDA_mem->ida_tn, IDA_mem->ida_cj, y, - yp, r, idals_mem->J, - idals_mem->J_data, vt1, vt2, vt3); - -THEN ADD: - - /* PRINT JACOBIAN*/ - doprint = 1; - if (doprint == 1) { - printf("Initial Jacobian"); - SUNDenseMatrix_Print(idals_mem->J,stdout); - SUNBandMatrix_Print(idals_mem->J,stdout); - } - -2. You will also need to add the parameter doprint, so in that file at line 1302 it should read - - IDALsMem idals_mem; - int doprint, retval; - -3. Now, you can get the printing by toggling on (doprint=1) or off (doprint=0) this, and recompiling sundials with make; make install inside buildir. - -This will make output files very large, so only use for testing. -If you want to print Finite Difference Jacobians and not the analytical Jacobian, you need to comment out the second line here (around line 373) in summa/build/source/engine/summaSolveSundialsIDA.f90 - - !comment this line out to use FD Jacobian - retval = FIDASetJacFn(ida_mem, c_funloc(evalJac4IDA)) From 136c49ea7cc446732517308a1c535d629d0f6b2a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Feb 2023 11:14:53 +0900 Subject: [PATCH 0553/1472] fixed makefiles --- build/my_makefile_cop | 6 +++++- build/my_makefile_gra | 6 +++++- build/my_makefile_mac | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/build/my_makefile_cop b/build/my_makefile_cop index 3a50d8cfa..6761005a8 100644 --- a/build/my_makefile_cop +++ b/build/my_makefile_cop @@ -69,7 +69,7 @@ DIR_SUNDIALS= $(F_MASTER_TOP)/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod -DIR_BMI= $(F_MASTER_TOP)/instdir +DIR_BMI= $(F_MASTER_TOP)/bmi/instdir INC_BMI=-I$(DIR_BMI)/include LIB_BMI=-L$(DIR_BMI)/lib -lbmif @@ -402,6 +402,10 @@ update_version: echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) +# compile common routines +compile_comm: + $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) + # compile Noah-MP routines compile_noah: $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) diff --git a/build/my_makefile_gra b/build/my_makefile_gra index bf05f5c31..5702a6282 100644 --- a/build/my_makefile_gra +++ b/build/my_makefile_gra @@ -69,7 +69,7 @@ DIR_SUNDIALS= $(F_MASTER_TOP)/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod -DIR_BMI= $(F_MASTER_TOP)/instdir +DIR_BMI= $(F_MASTER_TOP)/bmi/instdir INC_BMI=-I$(DIR_BMI)/include LIB_BMI=-L$(DIR_BMI)/lib -lbmif @@ -402,6 +402,10 @@ update_version: echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) +# compile common routines +compile_comm: + $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) + # compile Noah-MP routines compile_noah: $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) diff --git a/build/my_makefile_mac b/build/my_makefile_mac index 75fc8e0b0..ee00e4c0e 100644 --- a/build/my_makefile_mac +++ b/build/my_makefile_mac @@ -69,7 +69,7 @@ DIR_SUNDIALS= $(F_MASTER_TOP)/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod -DIR_BMI= $(F_MASTER_TOP)/instdir +DIR_BMI= $(F_MASTER_TOP)/bmi/instdir INC_BMI=-I$(DIR_BMI)/include LIB_BMI=-L$(DIR_BMI)/lib -lbmif @@ -402,6 +402,10 @@ update_version: echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) +# compile common routines +compile_comm: + $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) + # compile Noah-MP routines compile_noah: $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) From d00cc1d207419c8d6240c8de165fdf7f9d68e657 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Feb 2023 18:48:04 +0900 Subject: [PATCH 0554/1472] compiles now --- build/my_makefile_cop | 33 +-- build/my_makefile_gra | 33 +-- build/my_makefile_mac | 33 +-- build/source/driver/summa_driver.f90 | 292 ++++++++++++++------------- build/source/driver/summa_run.f90 | 10 +- build/source/driver/summa_runBMI.f90 | 23 +-- 6 files changed, 231 insertions(+), 193 deletions(-) diff --git a/build/my_makefile_cop b/build/my_makefile_cop index 6761005a8..ad5e136a1 100644 --- a/build/my_makefile_cop +++ b/build/my_makefile_cop @@ -371,9 +371,10 @@ endif #====================================================================== # Compile -all: compile_noah compile_comm compile_summa link clean install -part: compile_noah compile_comm compile_summa -bmi: compile_noah compile_comm compile_bmi link_bmi clean install_test +all: compile_noah compile_comm compile_rout compile_test link_test clean_test compile_summa link clean install install_test +part: compile_noah compile_comm compile_rout +summa: compile_noah compile_comm compile_rout compile_summa link clean install +bmi: compile_noah compile_comm compile_rout compile_test link_test clean install_test check: @@ -411,21 +412,29 @@ compile_noah: $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) # compile SUMMA routines -compile_summa: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(SUMMA) \ +compile_rout: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) -compile_bmi: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(BMI) \ - $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) +# compile run scripts +compile_summa: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA) -# link routines +# compile test scripts +compile_test: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(BMI) + +# link run routines link: $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(DRIVER__EX) -# link routines -link_bmi: - $(FC_EXE) -pg -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(BMI__EX) +# link test routines +link_test: + $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(BMI__EX) + +# remove test script objects +clean_test: + rm -f summa_runBMI.o # Remove object files clean: diff --git a/build/my_makefile_gra b/build/my_makefile_gra index 5702a6282..c9f5228f2 100644 --- a/build/my_makefile_gra +++ b/build/my_makefile_gra @@ -371,9 +371,10 @@ endif #====================================================================== # Compile -all: compile_noah compile_comm compile_summa link clean install -part: compile_noah compile_comm compile_summa -bmi: compile_noah compile_comm compile_bmi link_bmi clean install_test +all: compile_noah compile_comm compile_rout compile_test link_test clean_test compile_summa link clean install install_test +part: compile_noah compile_comm compile_rout +summa: compile_noah compile_comm compile_rout compile_summa link clean install +bmi: compile_noah compile_comm compile_rout compile_test link_test clean install_test check: @@ -411,21 +412,29 @@ compile_noah: $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) # compile SUMMA routines -compile_summa: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(SUMMA) \ +compile_rout: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) -compile_bmi: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(BMI) \ - $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) +# compile run scripts +compile_summa: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA) -# link routines +# compile test scripts +compile_test: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(BMI) + +# link run routines link: $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(DRIVER__EX) -# link routines -link_bmi: - $(FC_EXE) -pg -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(BMI__EX) +# link test routines +link_test: + $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(BMI__EX) + +# remove test script objects +clean_test: + rm -f summa_runBMI.o # Remove object files clean: diff --git a/build/my_makefile_mac b/build/my_makefile_mac index ee00e4c0e..61324cd21 100644 --- a/build/my_makefile_mac +++ b/build/my_makefile_mac @@ -371,9 +371,10 @@ endif #====================================================================== # Compile -all: compile_noah compile_comm compile_summa link clean install -part: compile_noah compile_comm compile_summa -bmi: compile_noah compile_comm compile_bmi link_bmi clean install_test +all: compile_noah compile_comm compile_rout compile_test link_test clean_test compile_summa link clean install install_test +part: compile_noah compile_comm compile_rout +summa: compile_noah compile_comm compile_rout compile_summa link clean install +bmi: compile_noah compile_comm compile_rout compile_test link_test clean install_test check: @@ -411,21 +412,29 @@ compile_noah: $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) # compile SUMMA routines -compile_summa: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(SUMMA) \ +compile_rout: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) -compile_bmi: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(BMI) \ - $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) +# compile run scripts +compile_summa: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA) -# link routines +# compile test scripts +compile_test: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(BMI) + +# link run routines link: $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(DRIVER__EX) -# link routines -link_bmi: - $(FC_EXE) -pg -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(BMI__EX) +# link test routines +link_test: + $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(BMI__EX) + +# remove test script objects +clean_test: + rm -f summa_runBMI.o # Remove object files clean: diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 4b734bfca..7051c370f 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -18,7 +18,7 @@ ! You should have received a copy of the GNU General Public License ! along with this program. If not, see . -module summa_bmi +module summa_driver ! provides functions needed for summa driver routines adding BMI functions ! ***************************************************************************** ! * use desired modules @@ -49,16 +49,19 @@ module summa_bmi USE globalData, only: gru_struc ! gru-hru mapping structures USE multiconst, only: secprday ! number of seconds in a day ! provide access to the named variables that describe elements of parent model structures - USE var_lookup, only: iLookATTR ! look-up values for local attributes - USE var_lookup, only: iLookFLUX ! named variables for flux variables - USE var_lookup, only: iLookDIAG ! named variables for diagnostic variables - USE var_lookup, only: iLookPROG ! named variables for prognostic variables + USE var_lookup, only: iLookATTR ! named variables for real valued attribute data structure + USE var_lookup, only: iLookFORCE ! named variables for forcing data structure + USE var_lookup, only: iLookFLUX ! named variables for local flux variables + USE var_lookup, only: iLookDIAG ! named variables for local diagnostic variables + USE var_lookup, only: iLookPROG ! named variables for local prognostic variables implicit none ! Define the attributes of the model. type :: summa_model integer(i4b) :: timeStep ! index of model time step + !type(summa1_type_dec), allocatable :: summa1_struc(:) + type(summa1_type_dec) :: summa1_struc(1) end type summa_model type, extends (bmi) :: summa_bmi @@ -70,7 +73,7 @@ module summa_bmi procedure :: get_output_item_count => summa_output_item_count procedure :: get_input_var_names => summa_input_var_names procedure :: get_output_var_names => summa_output_var_names - procedure :: initialize => summa_initialize + procedure :: initialize => summa_bmi_initialize procedure :: finalize => summa_finalize procedure :: get_start_time => summa_start_time procedure :: get_end_time => summa_end_time @@ -136,7 +139,6 @@ module summa_bmi set_value_at_indices_int, & set_value_at_indices_float, & set_value_at_indices_double - procedure :: print_model_info end type summa_bmi private @@ -147,13 +149,8 @@ module summa_bmi ! ***************************************************************************** character (len=BMI_MAX_COMPONENT_NAME), target :: & component_name = "Structure for Unifying Multiple Modeling Alternatives: SUMMA" - ! define the master summa data structure - type(summa1_type_dec), allocatable :: summa1_struc(:) ! define parameters for the model simulation integer(i4b), parameter :: n=1 ! number of instantiations - ! error control - integer(i4b) :: err=0 ! error code - character(len=1024) :: message='' ! error message ! Exchange items integer, parameter :: input_item_count = 1 integer, parameter :: output_item_count = 16 @@ -166,62 +163,68 @@ module summa_bmi ! ***************************************************************************** ! * model setup/initialization ! ***************************************************************************** - function summa_initialize(this, config_file) result (bmi_status) + function summa_bmi_initialize(this, config_file) result (bmi_status) class (summa_bmi), intent(out) :: this character (len=*), intent(in) :: config_file + ! error control + integer(i4b) :: err=0 ! error code + character(len=1024) :: message='' ! error message integer :: bmi_status, i ! initialize time steps - this%model%timeStep == 0 + this%model%timeStep = 0 ! allocate space for the master summa structure, could happen outside of BMI function - allocate(summa1_struc(n), stat=err) - if(err/=0) call stop_program(1, 'problem allocating master summa structure') + !allocate(this%model%summa1_struc(n), stat=err) + !if(err/=0) call stop_program(1, 'problem allocating master summa structure') ! if using the BMI interface, there is an argument pointing to the file manager file ! then make sure summaFileManagerFile is set before executing initialization - if (len(config_file) > 0) then - do i=1,256 - if (dir(i) == C_NULL_CHAR) exit - summa1_struc%summaFileManagerFile(i:i)=config_file(i) ! copying character array to string - end do + if (len(config_file) > 0)then + this%model%summa1_struc(n)%summaFileManagerFile=config_file + !do i=1,256 + ! summa1_struc(n)%summaFileManagerFile(i:i)=config_file(i) ! copying character array to string + !end do endif ! declare and allocate summa data structures and initialize model state to known values - call summa_initialize(summa1_struc(n), err, message) + call summa_initialize(this%model%summa1_struc(n), err, message) call handle_err(err, message) ! initialize parameter data structures (e.g. vegetation and soil parameters) - call summa_paramSetup(summa1_struc(n), err, message) + call summa_paramSetup(this%model%summa1_struc(n), err, message) call handle_err(err, message) ! read restart data and reset the model state - call summa_readRestart(summa1_struc(n), err, message) + call summa_readRestart(this%model%summa1_struc(n), err, message) call handle_err(err, message) bmi_status = BMI_SUCCESS - end function summa_initialize + end function summa_bmi_initialize ! ***************************************************************************** ! * advance model by one time step. ! ***************************************************************************** function summa_update(this) result (bmi_status) class (summa_bmi), intent(inout) :: this + ! error control + integer(i4b) :: err=0 ! error code + character(len=1024) :: message='' ! error message integer :: bmi_status ! read model forcing data - call summa_readForcing(this%model%timeStep, summa1_struc(n), err, message) + call summa_readForcing(this%model%timeStep, this%model%summa1_struc(n), err, message) call handle_err(err, message) if (mod(this%model%timeStep, print_step_freq) == 0)then print *, 'step ---> ', this%model%timeStep endif ! run the summa physics for one time step - call summa_runPhysics(this%model%timeStep, summa1_struc(n), err, message) + call summa_runPhysics(this%model%timeStep, this%model%summa1_struc(n), err, message) call handle_err(err, message) ! write the model output - call summa_writeOutputFiles(this%model%timeStep, summa1_struc(n), err, message) + call summa_writeOutputFiles(this%model%timeStep, this%model%summa1_struc(n), err, message) call handle_err(err, message) bmi_status = BMI_SUCCESS @@ -234,9 +237,9 @@ function summa_update_until(this, time) result (bmi_status) class (summa_bmi), intent(inout) :: this double precision, intent(in) :: time ! unit days (julian days, same as get_model_time) integer :: bmi_status, istat - real :: current + double precision :: current - current = this%current_time() + istat = this%get_current_time(current) if (time < current) then bmi_status = BMI_FAILURE return @@ -249,7 +252,7 @@ function summa_update_until(this, time) result (bmi_status) do while (current < time .and. this%model%timeStep < numtim) istat = this%update() - current = this%current_time() + istat = this%get_current_time(current) this%model%timeStep = this%model%timeStep + 1 end do ! (looping through time) @@ -265,7 +268,7 @@ function summa_finalize(this) result (bmi_status) call stop_program(0, 'finished simulation successfully.') bmi_status = BMI_SUCCESS - end function finalize + end function summa_finalize ! ***************************************************************************** ! * extra BMI functions @@ -312,7 +315,7 @@ function summa_input_var_names(this, names) result (bmi_status) bmi_status = BMI_SUCCESS end function summa_input_var_names - ! List output variables NAMES SHOULD BE STANDARDIZED "https://csdms.colorado.edu/wiki/CSDMS_Standard_Names" + ! List output variables standardized as "https://csdms.colorado.edu/wiki/CSDMS_Standard_Names" function summa_output_var_names(this, names) result (bmi_status) class (summa_bmi), intent(in) :: this character (*), pointer, intent(out) :: names(:) @@ -418,7 +421,7 @@ function summa_grid_type(this, grid, type) result (bmi_status) type = 'points' bmi_status = BMI_SUCCESS case default - shape(:) = -1 + type = "-" bmi_status = BMI_FAILURE end select end function summa_grid_type @@ -463,7 +466,7 @@ function summa_grid_size(this, grid, size) result (bmi_status) select case(grid) case default - size = nHRUcount + size = nHRUfile bmi_status = BMI_FAILURE end select end function summa_grid_size @@ -502,17 +505,18 @@ function summa_grid_x(this, grid, x) result (bmi_status) integer, intent(in) :: grid double precision, dimension(:), intent(out) :: x integer :: bmi_status, iGRU, jHRU - summaVars: associate(attrStruct => summa1_struc%attrStruct) ! x%gru(:)%hru(:)%var(:) -- local attributes for each HRU - select case(grid) - case default - do iGRU = 1, summa1_struc%nGRU - do jHRU = 1, gru_struc(iGRU)%hruCount - x((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = attrStruct%gru(iGRU)%hru(jHRU)%var(iLookATTR%longitude) - end do - end do - bmi_status = BMI_SUCCESS - end select + summaVars: associate(attrStruct => this%model%summa1_struc(n)%attrStruct & ! x%gru(:)%hru(:)%var(:) -- local attributes for each HRU + ) + select case(grid) + case default + do iGRU = 1, this%model%summa1_struc(n)%nGRU + do jHRU = 1, gru_struc(iGRU)%hruCount + x((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = attrStruct%gru(iGRU)%hru(jHRU)%var(iLookATTR%longitude) + end do + end do + bmi_status = BMI_SUCCESS + end select end associate summaVars end function summa_grid_x @@ -522,17 +526,18 @@ function summa_grid_y(this, grid, y) result (bmi_status) integer, intent(in) :: grid double precision, dimension(:), intent(out) :: y integer :: bmi_status, iGRU, jHRU - summaVars: associate(attrStruct => summa1_struc%attrStruct) ! x%gru(:)%hru(:)%var(:) -- local attributes for each HRU - select case(grid) - case default - do iGRU = 1, summa1_struc%nGRU - do jHRU = 1, gru_struc(iGRU)%hruCount - y((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = attrStruct%gru(iGRU)%hru(jHRU)%var(iLookATTR%latitude) - end do - end do - bmi_status = BMI_SUCCESS - end select + summaVars: associate(attrStruct => this%model%summa1_struc(n)%attrStruct & ! x%gru(:)%hru(:)%var(:) -- local attributes for each HRU + ) + select case(grid) + case default + do iGRU = 1, this%model%summa1_struc(n)%nGRU + do jHRU = 1, gru_struc(iGRU)%hruCount + y((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = attrStruct%gru(iGRU)%hru(jHRU)%var(iLookATTR%latitude) + end do + end do + bmi_status = BMI_SUCCESS + end select end associate summaVars end function summa_grid_y @@ -541,18 +546,19 @@ function summa_grid_z(this, grid, z) result (bmi_status) class (summa_bmi), intent(in) :: this integer, intent(in) :: grid double precision, dimension(:), intent(out) :: z - integer :: bmi_status, iGRU, jHRUs - summaVars: associate(attrStruct => summa1_struc%attrStruct) ! x%gru(:)%hru(:)%var(:) -- local attributes for each HRU + integer :: bmi_status, iGRU, jHRU - select case(grid) - case default - do iGRU = 1, summa1_struc%nGRU - do jHRU = 1, gru_struc(iGRU)%hruCount - z((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = attrStruct%gru(iGRU)%hru(jHRU)%var(iLookATTR%elevation) - end do - end do - bmi_status = BMI_SUCCESS - end select + summaVars: associate(attrStruct => this%model%summa1_struc(n)%attrStruct & ! x%gru(:)%hru(:)%var(:) -- local attributes for each HRU + ) + select case(grid) + case default + do iGRU = 1, this%model%summa1_struc(n)%nGRU + do jHRU = 1, gru_struc(iGRU)%hruCount + z((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = attrStruct%gru(iGRU)%hru(jHRU)%var(iLookATTR%elevation) + end do + end do + bmi_status = BMI_SUCCESS + end select end associate summaVars end function summa_grid_z @@ -655,7 +661,7 @@ function summa_grid_nodes_per_face(this, grid, nodes_per_face) result(bmi_status end function summa_grid_nodes_per_face ! The data type of the variable, as a string - function summa_var_type(this, name, units) result (bmi_status) + function summa_var_type(this, name, type) result (bmi_status) class (summa_bmi), intent(in) :: this character (len=*), intent(in) :: name character (len=*), intent(out) :: type @@ -701,12 +707,13 @@ function summa_var_itemsize(this, name, size) result (bmi_status) class (summa_bmi), intent(in) :: this character (len=*), intent(in) :: name integer, intent(out) :: size - real :: targetarr + real ,target :: targetarr(nHRUfile) integer :: bmi_status select case(name) case default - size = sizeof(get_basin_field(name, 1, 1, targetarr)) ! 'sizeof' in gcc & ifort + call get_basin_field(this, name, 1, targetarr) ! See near bottom of file + size = sizeof(targetarr(1)) ! 'sizeof' in gcc & ifort bmi_status = BMI_SUCCESS end select end function summa_var_itemsize @@ -764,16 +771,13 @@ function summa_get_float(this, name, dest) result (bmi_status) class (summa_bmi), intent(in) :: this character (len=*), intent(in) :: name real, intent(inout) :: dest(:) - real :: targetarr - integer :: bmi_status, iGRU, jHRU + real, target :: targetarr(nHRUfile) + integer :: bmi_status select case(name) case default - do iGRU = 1, summa1_struc%nGRU - do jHRU = 1, gru_struc(iGRU)%hruCount - dest((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = get_basin_field(name, iGRU, jHRU, targetarr) - end do - end do + call get_basin_field(this, name, nHRUfile, targetarr) + dest = targetarr bmi_status = BMI_SUCCESS end select end function summa_get_float @@ -812,15 +816,16 @@ function summa_get_ptr_float(this, name, dest_ptr) result (bmi_status) character (len=*), intent(in) :: name real, pointer, intent(inout) :: dest_ptr(:) integer :: bmi_status, n_elements - real :: targetarr + real, target :: targetarr(nHRUfile) type (c_ptr) :: src select case(name) case default - src = c_loc(get_basin_field(name, 1, 1, targetarr)) - n_elements = nHRUcount - call c_f_pointer(src, dest_ptr, [n_elements]) - bmi_status = BMI_SUCCESS + call get_basin_field(this, name, 1, targetarr) ! See near bottom of file + src = c_loc(targetarr(1)) + n_elements = nHRUfile + call c_f_pointer(src, dest_ptr, [n_elements]) + bmi_status = BMI_SUCCESS end select end function summa_get_ptr_float @@ -839,8 +844,7 @@ function summa_get_ptr_double(this, name, dest_ptr) result (bmi_status) end function summa_get_ptr_double ! Get values of an integer variable at the given locations - function summa_get_at_indices_int(this, name, dest, inds) & - result (bmi_status) + function summa_get_at_indices_int(this, name, dest, inds) result (bmi_status) class (summa_bmi), intent(in) :: this character (len=*), intent(in) :: name integer, intent(inout) :: dest(:) @@ -856,20 +860,20 @@ function summa_get_at_indices_int(this, name, dest, inds) & end function summa_get_at_indices_int ! Get values of a real variable at the given locations - function summa_get_at_indices_float(this, name, dest, inds) & - result (bmi_status) + function summa_get_at_indices_float(this, name, dest, inds) result (bmi_status) class (summa_bmi), intent(in) :: this character (len=*), intent(in) :: name real, intent(inout) :: dest(:) integer, intent(in) :: inds(:) integer :: bmi_status, i, n_elements - real :: targetarr + real, target :: targetarr(nHRUfile) type (c_ptr) src real, pointer :: src_flattened(:) select case(name) case default - src = c_loc(get_basin_field(name, 1, 1, targetarr)) + call get_basin_field(this, name, 1, targetarr) ! See near bottom of file + src = c_loc(targetarr(1)) call c_f_pointer(src, src_flattened, [n_elements]) n_elements = size(inds) do i = 1, n_elements @@ -880,8 +884,7 @@ function summa_get_at_indices_float(this, name, dest, inds) & end function summa_get_at_indices_float ! Get values of a double variable at the given locations - function summa_get_at_indices_double(this, name, dest, inds) & - result (bmi_status) + function summa_get_at_indices_double(this, name, dest, inds) result (bmi_status) class (summa_bmi), intent(in) :: this character (len=*), intent(in) :: name double precision, intent(inout) :: dest(:) @@ -936,8 +939,7 @@ function summa_set_double(this, name, src) result (bmi_status) end function summa_set_double ! Set integer values at particular locations - function summa_set_at_indices_int(this, name, inds, src) & - result (bmi_status) + function summa_set_at_indices_int(this, name, inds, src) result (bmi_status) class (summa_bmi), intent(inout) :: this character (len=*), intent(in) :: name integer, intent(in) :: inds(:) @@ -954,8 +956,7 @@ function summa_set_at_indices_int(this, name, inds, src) & end function summa_set_at_indices_int ! Set real values at particular locations, ONLY FOR INPUT VARIABLES - function summa_set_at_indices_float(this, name, inds, src) & - result (bmi_status) + function summa_set_at_indices_float(this, name, inds, src) result (bmi_status) class (summa_bmi), intent(inout) :: this character (len=*), intent(in) :: name integer, intent(in) :: inds(:) @@ -972,8 +973,7 @@ function summa_set_at_indices_float(this, name, inds, src) & end function summa_set_at_indices_float ! Set double values at particular locations - function summa_set_at_indices_double(this, name, inds, src) & - result (bmi_status) + function summa_set_at_indices_double(this, name, inds, src) result (bmi_status) class (summa_bmi), intent(inout) :: this character (len=*), intent(in) :: name integer, intent(in) :: inds(:) @@ -989,53 +989,63 @@ function summa_set_at_indices_double(this, name, inds, src) & end select end function summa_set_at_indices_double - ! non-BMI helper function to get fields - function get_basin_field(name, iGRU, jHRU, targetarr) + ! non-BMI helper function to get fields, only get first do_nHRU of them + subroutine get_basin_field(this, name, do_nHRU, targetarr) implicit none - integer, intent(in) :: name, iGRU, jHRU - real, intent(out) :: targetarr - summaVars: associate(& - forcStruct => summa1_struc%forcStruct , & ! x%gru(:)%hru(:)%var(:) -- model forcing data - progStruct => summa1_struc%progStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model prognostic (state) variables - diagStruct => summa1_struc%diagStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model diagnostic variables - fluxStruct => summa1_struc%fluxStruct & ! x%gru(:)%hru(:)%var(:)%dat -- model fluxes - ) + class (summa_bmi), intent(in) :: this + integer, intent(in) :: do_nHRU + character (len=*), intent(in) :: name + real, target, intent(out) :: targetarr(nHRUfile) + integer :: iGRU, jHRU, i - select case (name) - case('land_surface_water__runoff_volume_flux') - targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarTotalRunoff)%dat(1) - case('land_surface_water__evaporation_mass_flux') - targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarGroundEvaporation)%dat(1) - case('atmosphere_water__precipitation_mass_flux') - targetarr = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORC%pptrate)%dat(1) - case('land_vegetation_water__evaporation_mass_flux') - targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) - case('land_vegetation_water__transpiration_mass_flux') - targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) - case('snowpack__sublimation_mass_flux') - targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarSnowSublimation)%dat(1) - case('land_vegetation_water__sublimation_mass_flux') - targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopySublimation)%dat(1) - case('snowpack_mass') - targetarr = progStruct%gru(iGRU)%hru(jHRU)%var(iLookPROG%scalarSWE)%dat(1) - case('soil_water__mass') - targetarr = diagStruct%gru(iGRU)%hru(jHRU)%var(iLookDIAG%scalarTotalSoilWat)%dat(1) - case('land_vegetation_water__mass') - targetarr = progStruct%gru(iGRU)%hru(jHRU)%var(iLookPROG%scalarCanopyWat)%dat(1) - case('land_surface_radiation~net~total__energy_flux') - targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarNetRadiation)%dat(1) - case('land_atmosphere_heat~net~latent__energy_flux') - targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarLatHeatTotal)%dat(1) - case('land_atmosphere_heat~net~sensible__energy_flux') - targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarSenHeatTotal)%dat(1) - case('atmosphere_energy~net~total__energy_flux') - targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1) - case('land_vegetation_energy~net~total__energy_flux') - targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1) - case('land_surface_energy~net~total__energy_flux') - targetarr = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1) - end select + summaVars: associate(& + forcStruct => this%model%summa1_struc(n)%forcStruct , & ! x%gru(:)%hru(:)%var(:) -- model forcing data + progStruct => this%model%summa1_struc(n)%progStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model prognostic (state) variables + diagStruct => this%model%summa1_struc(n)%diagStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model diagnostic variables + fluxStruct => this%model%summa1_struc(n)%fluxStruct & ! x%gru(:)%hru(:)%var(:)%dat -- model fluxes + ) + targetarr = -999.0 + do iGRU = 1, this%model%summa1_struc(n)%nGRU + do jHRU = 1, gru_struc(iGRU)%hruCount + i = (iGRU-1) * gru_struc(iGRU)%hruCount + jHRU + if (i > do_nHRU) return + select case (name) + case('land_surface_water__runoff_volume_flux') + targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarTotalRunoff)%dat(1) + case('land_surface_water__evaporation_mass_flux') + targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarGroundEvaporation)%dat(1) + case('atmosphere_water__precipitation_mass_flux') + targetarr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%pptrate) + case('land_vegetation_water__evaporation_mass_flux') + targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) + case('land_vegetation_water__transpiration_mass_flux') + targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) + case('snowpack__sublimation_mass_flux') + targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarSnowSublimation)%dat(1) + case('land_vegetation_water__sublimation_mass_flux') + targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopySublimation)%dat(1) + case('snowpack_mass') + targetarr(i) = progStruct%gru(iGRU)%hru(jHRU)%var(iLookPROG%scalarSWE)%dat(1) + case('soil_water__mass') + targetarr(i) = diagStruct%gru(iGRU)%hru(jHRU)%var(iLookDIAG%scalarTotalSoilWat)%dat(1) + case('land_vegetation_water__mass') + targetarr(i) = progStruct%gru(iGRU)%hru(jHRU)%var(iLookPROG%scalarCanopyWat)%dat(1) + case('land_surface_radiation~net~total__energy_flux') + targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarNetRadiation)%dat(1) + case('land_atmosphere_heat~net~latent__energy_flux') + targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarLatHeatTotal)%dat(1) + case('land_atmosphere_heat~net~sensible__energy_flux') + targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarSenHeatTotal)%dat(1) + case('atmosphere_energy~net~total__energy_flux') + targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1) + case('land_vegetation_energy~net~total__energy_flux') + targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1) + case('land_surface_energy~net~total__energy_flux') + targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1) + end select + end do + end do end associate summaVars - end function get_basin_field + end subroutine get_basin_field -end program summa_bmi +end module summa_driver diff --git a/build/source/driver/summa_run.f90 b/build/source/driver/summa_run.f90 index f7dd7126c..668556f15 100644 --- a/build/source/driver/summa_run.f90 +++ b/build/source/driver/summa_run.f90 @@ -23,10 +23,11 @@ program summa_run ! ***************************************************************************** ! * use desired modules ! ***************************************************************************** + USE nrtype ! variable types, etc. ! subroutines and functions: model simulation - USE summa_bmi, only: initialize, update, finalize + USE summa_driver ! global data - USE globalData,only:numtim ! number of time steps + USE globalData,only:numtim ! number of time steps implicit none @@ -36,12 +37,13 @@ program summa_run type (summa_bmi) :: model integer(i4b) :: istat ! define timing information - integer(i4b) :: modelTimeStep ! index of model time step + integer(i4b) :: modelTimeStep ! index of model time step ! ***************************************************************************** ! * model simulation ! ***************************************************************************** - istat = model%initialize() + ! give this a 0 length argument to use fileManager from summa standard command arguments + istat = model%initialize('') ! loop through time do modelTimeStep=1,numtim diff --git a/build/source/driver/summa_runBMI.f90 b/build/source/driver/summa_runBMI.f90 index ba47e0b28..1645f7458 100644 --- a/build/source/driver/summa_runBMI.f90 +++ b/build/source/driver/summa_runBMI.f90 @@ -24,7 +24,7 @@ program summa_runBMI ! * use desired modules ! ***************************************************************************** ! subroutines and functions: model simulation - use summa_bmi + use summa_driver use, intrinsic :: iso_fortran_env, only : file_unit=>input_unit implicit none @@ -37,8 +37,8 @@ program summa_runBMI type (summa_bmi) :: model integer :: arg_count = 0 character (len=80) :: arg - integer(i4b) :: i, j, istat, grid_id, grid_size - real :: current_time, end_time + integer :: i, j, istat, grid_id, grid_size + double precision :: current_time, end_time real, allocatable :: runoff(:) ! ***************************************************************************** @@ -70,15 +70,14 @@ program summa_runBMI allocate(runoff(grid_size)) do while (current_time <= end_time) - write(file_unit,"(a, f6.1)") "Model values at time = ", current_time - istat = model%get_value(var_name, runoff) - do j = 1, grid_size - write (file_unit,"(f6.1)", advance="no") runoff(j) - end do - write (file_unit,*) - end do - istat = model%update() - istat = model%get_current_time(current_time) + write(file_unit,"(a, f6.1)") "Model values at time = ", current_time + istat = model%get_value(var_name, runoff) + do j = 1, grid_size + write (file_unit,"(f6.1)", advance="no") runoff(j) + end do + write (file_unit,*) + istat = model%update() + istat = model%get_current_time(current_time) end do deallocate(runoff) From 276911de113b3aa32f5d54bf1c6aed1210204a07 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 27 Feb 2023 11:07:43 +0900 Subject: [PATCH 0555/1472] compiles and runs now --- build/my_makefile_cop | 3 +-- build/my_makefile_gra | 3 +-- build/my_makefile_mac | 5 ++--- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/build/my_makefile_cop b/build/my_makefile_cop index ad5e136a1..143e8bbd4 100644 --- a/build/my_makefile_cop +++ b/build/my_makefile_cop @@ -413,8 +413,7 @@ compile_noah: # compile SUMMA routines compile_rout: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ - $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) # compile run scripts compile_summa: update_version diff --git a/build/my_makefile_gra b/build/my_makefile_gra index c9f5228f2..543e229d6 100644 --- a/build/my_makefile_gra +++ b/build/my_makefile_gra @@ -413,8 +413,7 @@ compile_noah: # compile SUMMA routines compile_rout: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ - $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) # compile run scripts compile_summa: update_version diff --git a/build/my_makefile_mac b/build/my_makefile_mac index 61324cd21..00c67eacc 100644 --- a/build/my_makefile_mac +++ b/build/my_makefile_mac @@ -71,7 +71,7 @@ LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fid DIR_BMI= $(F_MASTER_TOP)/bmi/instdir INC_BMI=-I$(DIR_BMI)/include -LIB_BMI=-L$(DIR_BMI)/lib -lbmif +LIB_BMI=-L$(DIR_BMI)/lib -lbmif -Xlinker -rpath -Xlinker $(DIR_BMI)/lib # Eventually we plan move to a real configure script, but for now we like # to keep track of successful compilations of SUMMA on different platforms @@ -413,8 +413,7 @@ compile_noah: # compile SUMMA routines compile_rout: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ - $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) # compile run scripts compile_summa: update_version From 1e085977057b31ce13339f6b7f93106294669379 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 27 Feb 2023 21:47:42 +0900 Subject: [PATCH 0556/1472] All BMI works as expected --- build/source/driver/summa_driver.f90 | 49 ++++++++++++++-------------- build/source/driver/summa_run.f90 | 3 +- build/source/driver/summa_runBMI.f90 | 7 ++-- build/source/driver/summa_util.f90 | 4 +-- 4 files changed, 31 insertions(+), 32 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 7051c370f..bdd8e0401 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -45,7 +45,6 @@ module summa_driver USE globalData, only: dJulianStart ! julian day of start time of simulation USE globalData, only: dJulianFinsh ! julian day of end time of simulation USE globalData, only: data_step ! length of time steps for the outermost timeloop - USE globalData, only: nHRUfile ! number of HRUs in the file, = gru_struc(iGRU)%hruCount and summa1_struc%nHRU USE globalData, only: gru_struc ! gru-hru mapping structures USE multiconst, only: secprday ! number of seconds in a day ! provide access to the named variables that describe elements of parent model structures @@ -180,11 +179,10 @@ function summa_bmi_initialize(this, config_file) result (bmi_status) ! if using the BMI interface, there is an argument pointing to the file manager file ! then make sure summaFileManagerFile is set before executing initialization + ! Note, if this is more than 80 characters the pre-built BMI libraries will fail if (len(config_file) > 0)then - this%model%summa1_struc(n)%summaFileManagerFile=config_file - !do i=1,256 - ! summa1_struc(n)%summaFileManagerFile(i:i)=config_file(i) ! copying character array to string - !end do + this%model%summa1_struc(n)%summaFileManagerFile=trim(config_file) + print "(A)", "file_master is '"//trim(config_file)//"'." endif ! declare and allocate summa data structures and initialize model state to known values @@ -199,6 +197,7 @@ function summa_bmi_initialize(this, config_file) result (bmi_status) call summa_readRestart(this%model%summa1_struc(n), err, message) call handle_err(err, message) + ! done with initialization bmi_status = BMI_SUCCESS end function summa_bmi_initialize @@ -212,6 +211,9 @@ function summa_update(this) result (bmi_status) character(len=1024) :: message='' ! error message integer :: bmi_status + ! start, advance time, as model uses this time step throughout + this%model%timeStep = this%model%timeStep + 1 + ! read model forcing data call summa_readForcing(this%model%timeStep, this%model%summa1_struc(n), err, message) call handle_err(err, message) @@ -227,6 +229,7 @@ function summa_update(this) result (bmi_status) call summa_writeOutputFiles(this%model%timeStep, this%model%summa1_struc(n), err, message) call handle_err(err, message) + ! done with step bmi_status = BMI_SUCCESS end function summa_update @@ -236,7 +239,7 @@ end function summa_update function summa_update_until(this, time) result (bmi_status) class (summa_bmi), intent(inout) :: this double precision, intent(in) :: time ! unit days (julian days, same as get_model_time) - integer :: bmi_status, istat + integer :: bmi_status, istat, n_steps, i double precision :: current istat = this%get_current_time(current) @@ -245,17 +248,10 @@ function summa_update_until(this, time) result (bmi_status) return end if - ! loop through time - if (this%model%timeStep == 0) then - this%model%timeStep = 1 - end if - - do while (current < time .and. this%model%timeStep < numtim) + n_steps = nint( (time - current)*secprday/data_step ) + 1 ! model can only do a full data_step + do i = 1, n_steps istat = this%update() - istat = this%get_current_time(current) - this%model%timeStep = this%model%timeStep + 1 - end do ! (looping through time) - + end do bmi_status = BMI_SUCCESS end function summa_update_until @@ -465,8 +461,11 @@ function summa_grid_size(this, grid, size) result (bmi_status) integer :: bmi_status select case(grid) + case(0) + size = sum(gru_struc(:)%hruCount) + bmi_status = BMI_SUCCESS case default - size = nHRUfile + size = -1 bmi_status = BMI_FAILURE end select end function summa_grid_size @@ -571,7 +570,7 @@ function summa_grid_node_count(this, grid, count) result(bmi_status) select case(grid) case default - count = nHRUfile + count = sum(gru_struc(:)%hruCount) bmi_status = BMI_SUCCESS end select end function summa_grid_node_count @@ -707,7 +706,7 @@ function summa_var_itemsize(this, name, size) result (bmi_status) class (summa_bmi), intent(in) :: this character (len=*), intent(in) :: name integer, intent(out) :: size - real ,target :: targetarr(nHRUfile) + real ,target :: targetarr(sum(gru_struc(:)%hruCount)) integer :: bmi_status select case(name) @@ -771,12 +770,12 @@ function summa_get_float(this, name, dest) result (bmi_status) class (summa_bmi), intent(in) :: this character (len=*), intent(in) :: name real, intent(inout) :: dest(:) - real, target :: targetarr(nHRUfile) + real, target :: targetarr(sum(gru_struc(:)%hruCount)) integer :: bmi_status select case(name) case default - call get_basin_field(this, name, nHRUfile, targetarr) + call get_basin_field(this, name, sum(gru_struc(:)%hruCount), targetarr) dest = targetarr bmi_status = BMI_SUCCESS end select @@ -816,14 +815,14 @@ function summa_get_ptr_float(this, name, dest_ptr) result (bmi_status) character (len=*), intent(in) :: name real, pointer, intent(inout) :: dest_ptr(:) integer :: bmi_status, n_elements - real, target :: targetarr(nHRUfile) + real, target :: targetarr(sum(gru_struc(:)%hruCount)) type (c_ptr) :: src select case(name) case default call get_basin_field(this, name, 1, targetarr) ! See near bottom of file src = c_loc(targetarr(1)) - n_elements = nHRUfile + n_elements = sum(gru_struc(:)%hruCount) call c_f_pointer(src, dest_ptr, [n_elements]) bmi_status = BMI_SUCCESS end select @@ -866,7 +865,7 @@ function summa_get_at_indices_float(this, name, dest, inds) result (bmi_status) real, intent(inout) :: dest(:) integer, intent(in) :: inds(:) integer :: bmi_status, i, n_elements - real, target :: targetarr(nHRUfile) + real, target :: targetarr(sum(gru_struc(:)%hruCount)) type (c_ptr) src real, pointer :: src_flattened(:) @@ -995,7 +994,7 @@ subroutine get_basin_field(this, name, do_nHRU, targetarr) class (summa_bmi), intent(in) :: this integer, intent(in) :: do_nHRU character (len=*), intent(in) :: name - real, target, intent(out) :: targetarr(nHRUfile) + real, target, intent(out) :: targetarr(sum(gru_struc(:)%hruCount)) integer :: iGRU, jHRU, i summaVars: associate(& diff --git a/build/source/driver/summa_run.f90 b/build/source/driver/summa_run.f90 index 668556f15..27f5ba8fe 100644 --- a/build/source/driver/summa_run.f90 +++ b/build/source/driver/summa_run.f90 @@ -45,7 +45,8 @@ program summa_run ! give this a 0 length argument to use fileManager from summa standard command arguments istat = model%initialize('') - ! loop through time + ! loop through time where numtim has been already computed as + ! numtim = nint( (dJulianFinsh - dJulianStart)*secprday/data_step ) + 1 do modelTimeStep=1,numtim istat = model%update() end do ! (looping through time) diff --git a/build/source/driver/summa_runBMI.f90 b/build/source/driver/summa_runBMI.f90 index 1645f7458..60578a56f 100644 --- a/build/source/driver/summa_runBMI.f90 +++ b/build/source/driver/summa_runBMI.f90 @@ -50,7 +50,7 @@ program summa_runBMI end do if (len_trim(arg) == 0) then - write(*,"(a)") "Usage: summa_bmi CONFIGURATION_FILE" + write(*,"(a)") "Usage: summa_bmi.exe CONFIGURATION_FILE<80char" write(*,"(a)") write(*,"(a)") "Run the summa model through its BMI with a configuration file." write(*,"(a)") "Output is written to the file `summa_bmi.out`." @@ -64,16 +64,17 @@ program summa_runBMI istat = model%get_current_time(current_time) istat = model%get_end_time(end_time) +! variable will need to be on a grid that is constant through simulation and set in initialization istat = model%get_var_grid(var_name, grid_id) istat = model%get_grid_size(grid_id, grid_size) allocate(runoff(grid_size)) do while (current_time <= end_time) - write(file_unit,"(a, f6.1)") "Model values at time = ", current_time + write(file_unit,"(a, f10.2)") "Model values at time = ", current_time istat = model%get_value(var_name, runoff) do j = 1, grid_size - write (file_unit,"(f6.1)", advance="no") runoff(j) + write (file_unit,"(e13.5)", advance="no") runoff(j) end do write (file_unit,*) istat = model%update() diff --git a/build/source/driver/summa_util.f90 b/build/source/driver/summa_util.f90 index f310e3997..35c2e58d2 100644 --- a/build/source/driver/summa_util.f90 +++ b/build/source/driver/summa_util.f90 @@ -248,9 +248,7 @@ subroutine getCommandArguments(summa1_struc,err,message) call printCommandHelp case default - call printCommandHelp - message='unknown command line option' - err=1; return + ! assume BMI, already set master control file end select end do ! looping through command line arguments From 6db53957e372c66aa03460fe50fbe7eda866e16c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 28 Feb 2023 07:15:21 +0900 Subject: [PATCH 0557/1472] let summa1_type be allocatable again, should have uncommented that before --- build/source/driver/summa_driver.f90 | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index bdd8e0401..dfcc2fd8e 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -59,8 +59,7 @@ module summa_driver ! Define the attributes of the model. type :: summa_model integer(i4b) :: timeStep ! index of model time step - !type(summa1_type_dec), allocatable :: summa1_struc(:) - type(summa1_type_dec) :: summa1_struc(1) + type(summa1_type_dec), allocatable :: summa1_struc(:) end type summa_model type, extends (bmi) :: summa_bmi @@ -174,8 +173,8 @@ function summa_bmi_initialize(this, config_file) result (bmi_status) this%model%timeStep = 0 ! allocate space for the master summa structure, could happen outside of BMI function - !allocate(this%model%summa1_struc(n), stat=err) - !if(err/=0) call stop_program(1, 'problem allocating master summa structure') + allocate(this%model%summa1_struc(n), stat=err) + if(err/=0) call stop_program(1, 'problem allocating master summa structure') ! if using the BMI interface, there is an argument pointing to the file manager file ! then make sure summaFileManagerFile is set before executing initialization From 182427fc48e79cafe4064f38209de1167a33b580 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 1 Mar 2023 15:16:26 +0900 Subject: [PATCH 0558/1472] flux_data was inputted intot the solver as average over timestep in varSubStep, but instantaneous in sundials, so change that with flux_sum. clean up some comments, --- build/source/engine/coupled_em.f90 | 21 ++++- build/source/engine/opSplittin.f90 | 76 +++++++++---------- build/source/engine/summaSolveSundialsIDA.f90 | 10 +-- build/source/engine/systemSolvSundials.f90 | 5 +- build/source/engine/varSubstep.f90 | 12 +-- 5 files changed, 67 insertions(+), 57 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 20b1e2537..fb29f0a76 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -53,6 +53,7 @@ module coupled_em_module USE var_lookup,only:childFLUX_MEAN ! metadata +USE globalData,only:flux_meta ! metadata on the model fluxes USE globalData,only:indx_meta ! metadata on the model index variables USE globalData,only:diag_meta ! metadata on the model diagnostic variables USE globalData,only:prog_meta ! metadata on the model prognostic variables @@ -223,6 +224,7 @@ subroutine coupled_em(& real(rkind) :: massBalance ! mass balance error (kg m-2) ! energy fluxes integer(i4b) :: iSoil ! index of soil layers + type(var_dlength) :: flux_sum(:) ! sum of fluxes model fluxes for a local HRU over a whole_step real(rkind) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) @@ -623,7 +625,7 @@ subroutine coupled_em(& if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif endif - ! save/recover copies of index variables, temp saved on lastInnerStep, failed starts at lastInnerSTep + ! save/recover copies of index variables, temp saved on lastInnerStep, failed starts at lastInnerStep do iVar=1,size(indx_data%var) select case(stepFailure) case(.false.); indx_temp%var(iVar)%dat(:) = indx_data%var(iVar)%dat(:) @@ -753,7 +755,12 @@ subroutine coupled_em(& sumLatHeatCanopyEvap = 0._rkind ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) sumSenHeatCanopy = 0._rkind ! sensible heat flux from the canopy to the canopy air space (W m-2) sumSoilCompress = 0._rkind ! total soil compression - allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._rkind ! soil compression by layer + allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._rkind ! soil compression by layer + call allocLocal(flux_meta(:),flux_sum,nSnow,nSoil,err,cmessage) ! flux_sum structure + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + do concurrent ( iVar=1:size(flux_meta) ) + flux_sum%var(iVar)%dat(:) = 0._rkind + end do ! save volumetric ice content at the start of the step ! NOTE: used for volumetric loss due to melt-freeze @@ -794,6 +801,7 @@ subroutine coupled_em(& lookup_data, & ! intent(in): lookup tables model_decisions, & ! intent(in): model decisions ! energy fluxes over whole_step added on by dt_sub + flux_sum, & ! intent(inout) : sum of model fluxes for a local HRU over a whole_step sumCanopyEvaporation, & ! intent(inout): sum of canopy evaporation/condensation (kg m-2 s-1) sumLatHeatCanopyEvap, & ! intent(inout): sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) sumSenHeatCanopy, & ! intent(inout): sum of sensible heat flux from the canopy to the canopy air space (W m-2) @@ -850,7 +858,12 @@ subroutine coupled_em(& if( dt_sub == maxstep_op .and. .not.lastInnerStep ) do_outer = .false. if(do_outer)then - ! apply the energy fluxes + ! compute average flux for whole_step + do iVar=1,size(flux_meta) + flux_data%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) /whole_step + end do + + ! apply the canopy energy fluxes flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /whole_step ! canopy evaporation/condensation (kg m-2 s-1) flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /whole_step ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /whole_step ! sensible heat flux from the canopy to the canopy air space (W m-2) @@ -1075,7 +1088,7 @@ subroutine coupled_em(& err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - ! overwrite flux_data with flux_mean (returns timestep-average fluxes for scalar variables) + ! overwrite flux_data with flux_mean for data_step (returns timestep-average fluxes for scalar variables) do iVar=1,size(averageFlux_meta) flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:) = flux_mean%var(iVar)%dat(:) end do diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index e5b4d9b10..37c2b4da6 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -172,6 +172,7 @@ subroutine opSplittin(& whole_step, & ! intent(in): length of whole step for surface drainage and average flux firstSubStep, & ! intent(in): flag to denote first sub-step firstInnerStep, & ! intent(in): flag to denote if the first time step in maxstep subStep + firstInnerStep, & ! intent(in): flag to denote if the last time step in maxstep subStep computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation ! input/output: data structures type_data, & ! intent(in): type of vegetation and soil @@ -234,11 +235,12 @@ subroutine opSplittin(& type(zLookup), intent(in) :: lookup_data ! lookup tables type(model_options),intent(in) :: model_decisions(:) ! model decisions ! energy fluxes - real(rkind),intent(out) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) - real(rkind),intent(out) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - real(rkind),intent(out) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) - real(rkind),intent(out) :: sumSoilCompress ! sum of total soil compression - real(rkind),intent(out) :: sumLayerCompress(:) ! sum of soil compression by layer + type(var_dlength),intent(inout) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt + real(rkind),intent(inout) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) + real(rkind),intent(inout) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + real(rkind),intent(inout) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) + real(rkind),intent(inout :: sumSoilCompress ! sum of total soil compression + real(rkind),intent(niout) :: sumLayerCompress(:) ! sum of soil compression by layer ! output: model control real(rkind),intent(out) :: dtMultiplier ! substep multiplier (-) logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt @@ -455,14 +457,12 @@ subroutine opSplittin(& fluxCount%var(iVar)%dat(:) = 0 end do - if(firstInnerStep) then - ! initialize the model fluxes on first inner step - do iVar=1,size(flux_meta) ! loop through fluxes - if(flux2state_orig(iVar)%state1==integerMissing .and. flux2state_orig(iVar)%state2==integerMissing) cycle ! flux does not depend on state (e.g., input) - if(flux2state_orig(iVar)%state1==iname_watCanopy .and. .not.computeVegFlux) cycle ! use input fluxes in cases where there is no canopy - flux_data%var(iVar)%dat(:) = 0._rkind - end do - endif + ! initialize the model fluxes + do iVar=1,size(flux_meta) ! loop through fluxes + if(flux2state_orig(iVar)%state1==integerMissing .and. flux2state_orig(iVar)%state2==integerMissing) cycle ! flux does not depend on state (e.g., input) + if(flux2state_orig(iVar)%state1==iname_watCanopy .and. .not.computeVegFlux) cycle ! use input fluxes in cases where there is no canopy + flux_data%var(iVar)%dat(:) = 0._rkind + end do ! initialize derivatives do iVar=1,size(deriv_meta) @@ -718,31 +718,30 @@ subroutine opSplittin(& ! reset the flag for the first flux call if(.not.firstSuccess) firstFluxCall=.true. - if(firstInnerStep)then - ! save/recover copies of prognostic variables - do iVar=1,size(prog_data%var) - select case(failure) - case(.false.); prog_temp%var(iVar)%dat(:) = prog_data%var(iVar)%dat(:) - case(.true.); prog_data%var(iVar)%dat(:) = prog_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - ! save/recover copies of diagnostic variables - do iVar=1,size(diag_data%var) - select case(failure) - case(.false.); diag_temp%var(iVar)%dat(:) = diag_data%var(iVar)%dat(:) - case(.true.); diag_data%var(iVar)%dat(:) = diag_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - ! save/recover copies of model fluxes - do iVar=1,size(flux_data%var) - select case(failure) - case(.false.); flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) - case(.true.); flux_data%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - endif + ! update variables, also updated inside Sundials (if fail a split will need these) + ! save/recover copies of prognostic variables + do iVar=1,size(prog_data%var) + select case(failure) + case(.false.); prog_temp%var(iVar)%dat(:) = prog_data%var(iVar)%dat(:) + case(.true.); prog_data%var(iVar)%dat(:) = prog_temp%var(iVar)%dat(:) + end select + end do ! looping through variables + + ! save/recover copies of diagnostic variables + do iVar=1,size(diag_data%var) + select case(failure) + case(.false.); diag_temp%var(iVar)%dat(:) = diag_data%var(iVar)%dat(:) + case(.true.); diag_data%var(iVar)%dat(:) = diag_temp%var(iVar)%dat(:) + end select + end do ! looping through variables + + ! save/recover copies of model fluxes + do iVar=1,size(flux_data%var) + select case(failure) + case(.false.); flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + case(.true.); flux_data%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) + end select + end do ! looping through variables ! ----- ! * solve variable subset for one time step... @@ -781,6 +780,7 @@ subroutine opSplittin(& deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables bvar_data, & ! intent(in) : model variables for the local basin ! energy fluxes + flux_sum, & ! intent(inout) : sum of model fluxes for a local HRU over a dt sumCanopyEvaporation, & ! intent(inout) : sum of canopy evaporation/condensation (kg m-2 s-1) sumLatHeatCanopyEvap, & ! intent(inout) : sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) sumSenHeatCanopy, & ! intent(inout) : sum of sensible heat flux from the canopy to the canopy air space (W m-2) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index dbcd162f8..b449743b2 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -112,15 +112,14 @@ subroutine summaSolveSundialsIDA( & ! input-output: data structures indx_data, & ! intent(in): index data diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_temp, & ! intent(inout): model fluxes for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU - flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step + flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a dt deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output ixSaturation, & ! intent(inout) index of the lowest saturated layer (NOTE: only computed on the first iteration) idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt - dt_out, & ! intent(out): time step sum for data window at termination of sundials + dt_out, & ! intent(out): time step sum for entire data window at termination of sundials stateVec, & ! intent(out): model state vector stateVecPrime, & ! intent(out): derivative of model state vector err,message & ! intent(out): error control @@ -179,9 +178,8 @@ subroutine summaSolveSundialsIDA( & type(var_ilength), intent(in) :: indx_data ! indices defining model states and layers ! input-output: data structures type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_temp ! model fluxes for a local HRU type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: flux_sum ! sum of fluxes + type(var_dlength),intent(inout) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! output: state vectors integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer @@ -189,7 +187,7 @@ subroutine summaSolveSundialsIDA( & real(rkind),intent(inout) :: stateVecPrime(:) ! model state vector (y') logical(lgt),intent(out) :: idaSucceeds ! flag to indicate if IDA is successful logical(lgt),intent(inout) :: tooMuchMelt ! flag to denote that there was too much melt - real(qp),intent(out) :: dt_out ! time step sum for data window at termination of sundials + real(qp),intent(out) :: dt_out ! time step sum for entire data window at termination of sundials ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index a83cdc6a6..485ecce0d 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -209,7 +209,7 @@ subroutine systemSolvSundials(& integer(i4b) :: iLayer ! index of model layer in the snow+soil domain real(rkind) :: xMin,xMax ! minimum and maximum values for water content real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) - type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a data step + type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt_out (=dt) logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step real(rkind) :: fOld ! function values (-); NOTE: dimensionless because scaled ! enthalpy derivatives @@ -538,7 +538,6 @@ subroutine systemSolvSundials(& indx_data, & ! intent(in): index data ! input-output: data structures diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) flux_temp, & ! intent(inout): model fluxes for a local HRU flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables @@ -546,7 +545,7 @@ subroutine systemSolvSundials(& ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt - dt_out, & ! intent(out): time step sum for data window at termination of sundials + dt_out, & ! intent(out): time step sum for entire data window at termination of sundials stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step err,cmessage) ! intent(out): error control diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 53b0a304a..c87434f00 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -118,6 +118,7 @@ subroutine varSubstep(& deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables bvar_data, & ! intent(in) : model variables for the local basin ! energy fluxes + flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a whole_step sumCanopyEvaporation, & ! intent(inout): sum of canopy evaporation/condensation (kg m-2 s-1) sumLatHeatCanopyEvap, & ! intent(inout): sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) sumSenHeatCanopy, & ! intent(inout): sum of sensible heat flux from the canopy to the canopy air space (W m-2) @@ -173,6 +174,7 @@ subroutine varSubstep(& type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin ! energy fluxes + real(rkind),intent(inout) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a whole_step real(rkind),intent(inout) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) real(rkind),intent(inout) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) real(rkind),intent(inout) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) @@ -523,14 +525,13 @@ subroutine varSubstep(& if(globalPrintFlag)& write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt - ! increment fluxes as average over entire timestep - dt_wght = dtSubstep/whole_step + ! get the total energy fluxes do iVar=1,size(flux_meta) if(count(fluxMask%var(iVar)%dat)>0) then ! ** no domain splitting if(count(ixLayerActive/=integerMissing)==nLayers)then - flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght + flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + dtSubstep*flux_temp%var(iVar)%dat(:) fluxCount%var(iVar)%dat(:) = fluxCount%var(iVar)%dat(:) + 1 ! ** domain splitting @@ -539,14 +540,13 @@ subroutine varSubstep(& ixMax=ubound(flux_data%var(iVar)%dat) do ixLayer=ixMin(1),ixMax(1) if(fluxMask%var(iVar)%dat(ixLayer)) then - ! Makes Jacobian more diagonal if do this, but less correct ! special case of the transpiration sink from soil layers: only computed for the top soil layer !if(iVar==iLookFlux%mLayerTranspire)then - ! if(ixLayer==1) flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght + ! if(ixLayer==1) flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + dtSubstep*flux_temp%var(iVar)%dat(:) ! standard case !else - flux_data%var(iVar)%dat(ixLayer) = flux_data%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght + flux_sum%var(iVar)%dat(ixLayer) = flux_sum%var(iVar)%dat(ixLayer) + dtSubstep*flux_temp%var(iVar)%dat(ixLayer) !endif fluxCount%var(iVar)%dat(ixLayer) = fluxCount%var(iVar)%dat(ixLayer) + 1 endif From ec037f1a9d24558f232e0b7ec747245bcae36d1a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 1 Mar 2023 16:18:06 +0900 Subject: [PATCH 0559/1472] take the initialization out of opSplittin. Not sure this is needed at all since will be computed in updatVars. --- build/source/engine/coupled_em.f90 | 43 +++++++++++++++++++ build/source/engine/opSplittin.f90 | 66 +----------------------------- build/source/engine/snowLiqFlx.f90 | 2 +- 3 files changed, 45 insertions(+), 66 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index fb29f0a76..681d3f6e6 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -766,6 +766,49 @@ subroutine coupled_em(& ! NOTE: used for volumetric loss due to melt-freeze allocate(mLayerVolFracIceInit(nLayers)); mLayerVolFracIceInit = prog_data%var(iLookPROG%mLayerVolFracIce)%dat + ! make sure have consistent state variables to start, later done in updateVars + ! associate local variables with information in the data structures + init: associate(& + ! depth-varying soil parameters + vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) + ! state variables in the vegetation canopy + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(out): [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(out): [dp] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(out): [dp] mass of total water on the vegetation canopy (kg m-2) + ! state variables in the snow and soil domains + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(out): [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(out): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(out): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(out): [dp(:)] matric head (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat & ! intent(out): [dp(:)] matric potential of liquid water (m) + ) ! associations to variables in data structures + + ! compute the total water content in the vegetation canopy + scalarCanopyWat = scalarCanopyLiq + scalarCanopyIce ! kg m-2 + + ! compute the total water content in snow and soil + ! NOTE: no ice expansion allowed for soil + if(nSnow>0)& + mLayerVolFracWat( 1:nSnow ) = mLayerVolFracLiq( 1:nSnow ) + mLayerVolFracIce( 1:nSnow )*(iden_ice/iden_water) + mLayerVolFracWat(nSnow+1:nLayers) = mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) + + ! compute the liquid water matric potential (m) + ! NOTE: include ice content as part of the solid porosity - major effect of ice is to reduce the pore size; ensure that effSat=1 at saturation + ! (from Zhao et al., J. Hydrol., 1997: Numerical analysis of simultaneous heat and mass transfer...) + do iSoil=1,nSoil + call liquidHead(mLayerMatricHead(iSoil),mLayerVolFracLiq(nSnow+iSoil),mLayerVolFracIce(nSnow+iSoil), & ! input: state variables + vGn_alpha(iSoil),vGn_n(iSoil),theta_sat(iSoil),theta_res(iSoil),vGn_m(iSoil), & ! input: parameters + matricHeadLiq=mLayerMatricHeadLiq(iSoil), & ! output: liquid water matric potential (m) + err=err,message=cmessage) ! output: error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + end do ! looping through soil layers (computing liquid water matric potential) + + end associate init + whole_step = maxstep if(dt_sub < maxstep_op) whole_step = dt_sub ! only happens if fails a step in the maxstep diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 37c2b4da6..6d4057bf7 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -96,16 +96,6 @@ module opSplittin_module zLookup, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions -! look-up values for the choice of groundwater representation (local-column, or single-basin) -USE mDecisions_module,only: & - localColumn, & ! separate groundwater representation in each local soil column - singleBasin ! single groundwater store over the entire basin - -! look-up values for the choice of groundwater parameterization -USE mDecisions_module,only: & - qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization - bigBucket, & ! a big bucket (lumped aquifer model) - noExplicit ! no explicit groundwater parameterization ! look-up values for the numerical method USE mDecisions_module,only: & @@ -264,7 +254,6 @@ subroutine opSplittin(& type(var_dlength) :: diag_temp ! temporary model diagnostic variables type(var_dlength) :: flux_temp ! temporary model fluxes type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(rkind),dimension(nLayers) :: mLayerVolFracIceInit ! initial vector for volumetric fraction of ice (-) ! ------------------------------------------------------------------------------------------------------ ! * operator splitting ! ------------------------------------------------------------------------------------------------------ @@ -314,11 +303,7 @@ subroutine opSplittin(& globalVars: associate(& ! model decisions - ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization - ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical method, backward Euler or SUNDIALS/IDA - ! domain boundary conditions - airtemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) ! vector of energy and hydrology indices for the snow and soil domains ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain @@ -339,36 +324,7 @@ subroutine opSplittin(& numberStateSplit => indx_data%var(iLookINDEX%numberStateSplit )%dat(1) ,& ! intent(inout): [i4b] number of state splitting solutions (-) numberDomainSplitNrg => indx_data%var(iLookINDEX%numberDomainSplitNrg )%dat(1) ,& ! intent(inout): [i4b] number of domain splitting solutions for energy (-) numberDomainSplitMass => indx_data%var(iLookINDEX%numberDomainSplitMass)%dat(1) ,& ! intent(inout): [i4b] number of domain splitting solutions for mass (-) - numberScalarSolutions => indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1) ,& ! intent(inout): [i4b] number of scalar solutions (-) - ! domain configuration - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ! depth-varying soil parameters - vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) - ! soil parameters - specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) - ! model diagnostic variables (fraction of liquid water) - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) - ! model state variables (vegetation canopy) - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(out): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(out): [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(out): [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(out): [dp] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(out): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(out): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(out): [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(out): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(out): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(out): [dp(:)] matric head (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat & ! intent(out): [dp(:)] matric potential of liquid water (m) + numberScalarSolutions => indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1) & ! intent(inout): [i4b] number of scalar solutions (-) ) ! --------------------------------------------------------------------------------------- ! initialize error control @@ -408,26 +364,6 @@ subroutine opSplittin(& ! initialize the state check stateCheck(:) = 0 - ! compute the total water content in the vegetation canopy - scalarCanopyWat = scalarCanopyLiq + scalarCanopyIce ! kg m-2 - - ! compute the total water content in snow and soil - ! NOTE: no ice expansion allowed for soil - if(nSnow>0)& - mLayerVolFracWat( 1:nSnow ) = mLayerVolFracLiq( 1:nSnow ) + mLayerVolFracIce( 1:nSnow )*(iden_ice/iden_water) - mLayerVolFracWat(nSnow+1:nLayers) = mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) - - ! compute the liquid water matric potential (m) - ! NOTE: include ice content as part of the solid porosity - major effect of ice is to reduce the pore size; ensure that effSat=1 at saturation - ! (from Zhao et al., J. Hydrol., 1997: Numerical analysis of simultaneous heat and mass transfer...) - do iSoil=1,nSoil - call liquidHead(mLayerMatricHead(iSoil),mLayerVolFracLiq(nSnow+iSoil),mLayerVolFracIce(nSnow+iSoil), & ! input: state variables - vGn_alpha(iSoil),vGn_n(iSoil),theta_sat(iSoil),theta_res(iSoil),vGn_m(iSoil), & ! input: parameters - matricHeadLiq=mLayerMatricHeadLiq(iSoil), & ! output: liquid water matric potential (m) - err=err,message=cmessage) ! output: error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - end do ! looping through soil layers (computing liquid water matric potential) - ! allocate space for the flux mask (used to define when fluxes are updated) call allocLocal(flux_meta(:),fluxMask,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif diff --git a/build/source/engine/snowLiqFlx.f90 b/build/source/engine/snowLiqFlx.f90 index e31612523..5bb65076a 100644 --- a/build/source/engine/snowLiqFlx.f90 +++ b/build/source/engine/snowLiqFlx.f90 @@ -202,7 +202,7 @@ subroutine snowLiqFlxSundials(& scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) ! input: model state vector - mLayerVolFracIce, & ! intent(in) + mLayerVolFracIce, & ! intent(in) mLayerVolFracLiqTrial, & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) ! input-output: data structures indx_data, & ! intent(in): model indices From d2448c912e80e30b88457f3f788fae1f4d622924 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 1 Mar 2023 17:56:54 +0900 Subject: [PATCH 0560/1472] compiles now --- build/source/engine/coupled_em.f90 | 6 ++++-- build/source/engine/opSplittin.f90 | 9 +++------ build/source/engine/varSubstep.f90 | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 681d3f6e6..d254dc723 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -128,6 +128,8 @@ subroutine coupled_em(& ! structure allocations USE allocspace_module,only:allocLocal ! allocate local data structures USE allocspace_module,only:resizeData ! clone a data structure + ! simulation of fluxes and residuals given a trial state vector + USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential ! preliminary subroutines USE vegPhenlgy_module,only:vegPhenlgy ! compute vegetation phenology USE vegNrgFlux_module,only:wettedFrac ! compute wetted fraction of the canopy (used in sw radiation fluxes) @@ -224,7 +226,7 @@ subroutine coupled_em(& real(rkind) :: massBalance ! mass balance error (kg m-2) ! energy fluxes integer(i4b) :: iSoil ! index of soil layers - type(var_dlength) :: flux_sum(:) ! sum of fluxes model fluxes for a local HRU over a whole_step + type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a whole_step real(rkind) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) @@ -844,7 +846,7 @@ subroutine coupled_em(& lookup_data, & ! intent(in): lookup tables model_decisions, & ! intent(in): model decisions ! energy fluxes over whole_step added on by dt_sub - flux_sum, & ! intent(inout) : sum of model fluxes for a local HRU over a whole_step + flux_sum, & ! intent(inout): sum of model fluxes for a local HRU over a whole_step sumCanopyEvaporation, & ! intent(inout): sum of canopy evaporation/condensation (kg m-2 s-1) sumLatHeatCanopyEvap, & ! intent(inout): sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) sumSenHeatCanopy, & ! intent(inout): sum of sensible heat flux from the canopy to the canopy air space (W m-2) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 6d4057bf7..dd91ba104 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -161,7 +161,6 @@ subroutine opSplittin(& dt, & ! intent(in): time step (s) whole_step, & ! intent(in): length of whole step for surface drainage and average flux firstSubStep, & ! intent(in): flag to denote first sub-step - firstInnerStep, & ! intent(in): flag to denote if the first time step in maxstep subStep firstInnerStep, & ! intent(in): flag to denote if the last time step in maxstep subStep computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation ! input/output: data structures @@ -177,6 +176,7 @@ subroutine opSplittin(& lookup_data, & ! intent(in): lookup tables model_decisions, & ! intent(in): model decisions ! energy fluxes + flux_sum, & ! intent(inout): sum of model fluxes for a local HRU over a whole_step sumCanopyEvaporation, & ! intent(inout): sum of canopy evaporation/condensation (kg m-2 s-1) sumLatHeatCanopyEvap, & ! intent(inout): sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) sumSenHeatCanopy, & ! intent(inout): sum of sensible heat flux from the canopy to the canopy air space (W m-2) @@ -191,9 +191,6 @@ subroutine opSplittin(& ! --------------------------------------------------------------------------------------- ! structure allocations USE allocspace_module,only:allocLocal ! allocate local data structures - ! simulation of fluxes and residuals given a trial state vector - USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content - USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential ! population/extraction of state vectors USE indexState_module,only:indexSplit ! get state indices USE varSubstep_module,only:varSubstep ! complete substeps for a given split @@ -229,8 +226,8 @@ subroutine opSplittin(& real(rkind),intent(inout) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) real(rkind),intent(inout) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) real(rkind),intent(inout) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) - real(rkind),intent(inout :: sumSoilCompress ! sum of total soil compression - real(rkind),intent(niout) :: sumLayerCompress(:) ! sum of soil compression by layer + real(rkind),intent(inout) :: sumSoilCompress ! sum of total soil compression + real(rkind),intent(inout) :: sumLayerCompress(:) ! sum of soil compression by layer ! output: model control real(rkind),intent(out) :: dtMultiplier ! substep multiplier (-) logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index c87434f00..9d3894236 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -174,7 +174,7 @@ subroutine varSubstep(& type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin ! energy fluxes - real(rkind),intent(inout) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a whole_step + type(var_dlength),intent(inout) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a whole_step real(rkind),intent(inout) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) real(rkind),intent(inout) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) real(rkind),intent(inout) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) From 6a1cdc1fd41dfbda16706ad09d342c13a8c87d2f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 2 Mar 2023 15:24:28 +0900 Subject: [PATCH 0561/1472] we are outputting average flux and compression over dt (shorter for BE 32 or 112 sec, sundials is 1 hr) and the end solution for diag variables and prog variables (includes state) we want average flux and compression because use to do mass calculation against flux and variable change and only saving diag/prog at beginning of dt for mass balance checks in updatProg --- build/source/engine/coupled_em.f90 | 36 +++++------- build/source/engine/opSplittin.f90 | 21 ------- build/source/engine/summaSolveSundialsIDA.f90 | 10 +++- build/source/engine/systemSolvSundials.f90 | 14 ++++- build/source/engine/varSubstep.f90 | 55 ++++++++++++------- 5 files changed, 71 insertions(+), 65 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index d254dc723..77606663d 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -227,9 +227,6 @@ subroutine coupled_em(& ! energy fluxes integer(i4b) :: iSoil ! index of soil layers type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a whole_step - real(rkind) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) - real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) real(rkind) :: sumSoilCompress ! sum of total soil compression real(rkind),allocatable :: sumLayerCompress(:) ! sum of soil compression by layer ! balance checks @@ -620,6 +617,7 @@ subroutine coupled_em(& do_outer = .true. if(stepFailure) firstInnerStep = .true. if ( dt_sub == maxstep_op .and. .not.firstInnerStep ) do_outer = .false. + if(do_outer)then if(.not.stepFailure)then @@ -752,10 +750,7 @@ subroutine coupled_em(& if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if endif - ! initialize the total energy fluxes over whole step (modified in updateProg in opSplittin in varSubstep) - sumCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap = 0._rkind ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy = 0._rkind ! sensible heat flux from the canopy to the canopy air space (W m-2) + ! initialize the compression and total energy fluxes to average over whole step (averaged over substep in varSubStep) sumSoilCompress = 0._rkind ! total soil compression allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._rkind ! soil compression by layer call allocLocal(flux_meta(:),flux_sum,nSnow,nSoil,err,cmessage) ! flux_sum structure @@ -845,13 +840,6 @@ subroutine coupled_em(& bvar_data, & ! intent(in): model variables for the local basin lookup_data, & ! intent(in): lookup tables model_decisions, & ! intent(in): model decisions - ! energy fluxes over whole_step added on by dt_sub - flux_sum, & ! intent(inout): sum of model fluxes for a local HRU over a whole_step - sumCanopyEvaporation, & ! intent(inout): sum of canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap, & ! intent(inout): sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy, & ! intent(inout): sum of sensible heat flux from the canopy to the canopy air space (W m-2) - sumSoilCompress, & ! intent(inout): sum of total soil compression - sumLayerCompress, & ! intent(inout): sum of soil compression by layer ! output: model control dtMultiplier, & ! intent(out): substep multiplier (-) tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt @@ -890,6 +878,17 @@ subroutine coupled_em(& cycle substeps endif + ! get the total soil compression + sumSoilCompress = sumSoilCompress + dt_sub*diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression + do iSoil=1,nSoil + if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& + sumLayerCompress(iSoil) = sumLayerCompress(iSoil) + dt_sub*diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) ! soil compression in layers + end do + ! get the total energy fluxes + do iVar=1,size(flux_meta) + flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + dt_sub*flux_data%var(iVar)%dat(:) + end do + ! update first step and first and last inner steps firstSubStep = .false. firstInnerStep = .false. @@ -908,20 +907,15 @@ subroutine coupled_em(& flux_data%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) /whole_step end do - ! apply the canopy energy fluxes - flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /whole_step ! canopy evaporation/condensation (kg m-2 s-1) - flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /whole_step ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /whole_step ! sensible heat flux from the canopy to the canopy air space (W m-2) - - ! apply the soil compression diagnostics + ! compute average soil compression diagnostics for whole_step diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress/whole_step + ! vector compression do iSoil=1,nSoil if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) = sumLayerCompress(iSoil)/whole_step end do deallocate(sumLayerCompress) - ! *** remove ice due to sublimation and freeze calculations... ! NOTE: In the future this should be moved into the solver, makes a big difference, and here only applying last substep amount to whole thing ! -------------------------------------------------------------- diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index dd91ba104..68309a23a 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -175,13 +175,6 @@ subroutine opSplittin(& bvar_data, & ! intent(in): model variables for the local basin lookup_data, & ! intent(in): lookup tables model_decisions, & ! intent(in): model decisions - ! energy fluxes - flux_sum, & ! intent(inout): sum of model fluxes for a local HRU over a whole_step - sumCanopyEvaporation, & ! intent(inout): sum of canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap, & ! intent(inout): sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy, & ! intent(inout): sum of sensible heat flux from the canopy to the canopy air space (W m-2) - sumSoilCompress, & ! intent(inout): sum of total soil compression - sumLayerCompress, & ! intent(inout): sum of soil compression by layer ! output: model control dtMultiplier, & ! intent(out): substep multiplier (-) tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt @@ -221,13 +214,6 @@ subroutine opSplittin(& type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin type(zLookup), intent(in) :: lookup_data ! lookup tables type(model_options),intent(in) :: model_decisions(:) ! model decisions - ! energy fluxes - type(var_dlength),intent(inout) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt - real(rkind),intent(inout) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) - real(rkind),intent(inout) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - real(rkind),intent(inout) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) - real(rkind),intent(inout) :: sumSoilCompress ! sum of total soil compression - real(rkind),intent(inout) :: sumLayerCompress(:) ! sum of soil compression by layer ! output: model control real(rkind),intent(out) :: dtMultiplier ! substep multiplier (-) logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt @@ -712,13 +698,6 @@ subroutine opSplittin(& flux_data, & ! intent(inout) : model fluxes for a local HRU deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables bvar_data, & ! intent(in) : model variables for the local basin - ! energy fluxes - flux_sum, & ! intent(inout) : sum of model fluxes for a local HRU over a dt - sumCanopyEvaporation, & ! intent(inout) : sum of canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap, & ! intent(inout) : sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy, & ! intent(inout) : sum of sensible heat flux from the canopy to the canopy air space (W m-2) - sumSoilCompress, & ! intent(inout) : sum of total soil compression - sumLayerCompress, & ! intent(inout) : sum of soil compression by layer ! output: control ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) dtMultiplier, & ! intent(out) : substep multiplier (-) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index b449743b2..f4353eb60 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -115,6 +115,7 @@ subroutine summaSolveSundialsIDA( & flux_data, & ! intent(inout): model fluxes for a local HRU flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a dt deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + mLayerCmpress_sum, & ! intent(inout): sum of compression of the soil matrix ! output ixSaturation, & ! intent(inout) index of the lowest saturated layer (NOTE: only computed on the first iteration) idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step @@ -181,6 +182,7 @@ subroutine summaSolveSundialsIDA( & type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + real(rkind),intent(inout) :: mLayerCmpress_sum(:) ! sum of soil compress ! output: state vectors integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer real(rkind),intent(inout) :: stateVec(:) ! model state vector (y) @@ -211,6 +213,7 @@ subroutine summaSolveSundialsIDA( & integer(kind = 8) :: mu, lu ! in banded matrix mode integer(i4b) :: iVar logical(lgt) :: startQuadrature + real(rkind) :: mLayerMatricHeadLiqPrev(nSoil) real(qp) :: h_init integer(c_long) :: nState ! total number of state variables real(rkind) :: rVec(nStat) @@ -395,6 +398,7 @@ subroutine summaSolveSundialsIDA( & eqns_data%scalarCanopyLiqPrev = prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) + mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) @@ -499,9 +503,12 @@ subroutine summaSolveSundialsIDA( & ! sum of fluxes do iVar=1,size(flux_meta) - flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * dt_last(1) + flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * dt_last(1) end do + ! sum of mLayerCmpress + mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & + * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) * dt_last(1) ! save required quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial @@ -509,6 +516,7 @@ subroutine summaSolveSundialsIDA( & eqns_data%scalarCanopyLiqPrev = eqns_data%scalarCanopyLiqTrial eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) + mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 485ecce0d..c11965597 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -210,6 +210,7 @@ subroutine systemSolvSundials(& real(rkind) :: xMin,xMax ! minimum and maximum values for water content real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt_out (=dt) + real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step real(rkind) :: fOld ! function values (-); NOTE: dimensionless because scaled ! enthalpy derivatives @@ -307,6 +308,9 @@ subroutine systemSolvSundials(& call allocLocal(flux_meta(:),flux_init,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + ! allocate space for mLayerCmpress_sum at the start of the time step + allocate( mLayerCmpress_sum(nSoil) ) + ! allocate space for the baseflow derivatives ! NOTE: needs allocation because only used when baseflow sinks are active if(ixGroundwater==qbaseTopmodel)then @@ -506,10 +510,13 @@ subroutine systemSolvSundials(& !------------------- ! * solving F(y,y') = 0 by IDA. Here, y is the state vector ! ------------------ - ! initialize flux_sum + + ! initialize flux_sum do concurrent ( iVar=1:size(flux_meta) ) flux_sum%var(iVar)%dat(:) = 0._rkind end do + ! initialize sum of compression of the soil matrix + mLayerCmpress_sum(:) = 0._rkind call summaSolveSundialsIDA(& dt_cur, & ! intent(in): data time step @@ -541,6 +548,7 @@ subroutine systemSolvSundials(& flux_temp, & ! intent(inout): model fluxes for a local HRU flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + mLayerCmpress_sum, & ! intent(inout): sum of compression of the soil matrix ! output ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step @@ -565,6 +573,10 @@ subroutine systemSolvSundials(& flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / dt_out end do + ! compute the total change in storage associated with compression of the soil matrix (kg m-2) + diag_data%var(iLookDIAG%mLayerCompress)%dat(:) = mLayerCmpress_sum(:) / dt_out + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sum(diag_data%var(iLookDIAG%mLayerCompress)%dat(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water + ! save the computed solution stateVecTrial = stateVecNew diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 9d3894236..09031562c 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -117,13 +117,6 @@ subroutine varSubstep(& flux_data, & ! intent(inout) : model fluxes for a local HRU deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables bvar_data, & ! intent(in) : model variables for the local basin - ! energy fluxes - flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a whole_step - sumCanopyEvaporation, & ! intent(inout): sum of canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap, & ! intent(inout): sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy, & ! intent(inout): sum of sensible heat flux from the canopy to the canopy air space (W m-2) - sumSoilCompress, & ! intent(inout) : sum of total soil compression - sumLayerCompress, & ! intent(inout) : sum of soil compression by layer ! output: model control ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) dtMultiplier, & ! intent(out) : substep multiplier (-) @@ -173,13 +166,6 @@ subroutine varSubstep(& type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin - ! energy fluxes - type(var_dlength),intent(inout) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a whole_step - real(rkind),intent(inout) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) - real(rkind),intent(inout) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - real(rkind),intent(inout) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) - real(rkind),intent(inout) :: sumSoilCompress ! sum of total soil compression - real(rkind),intent(inout) :: sumLayerCompress(:) ! sum of soil compression by layer ! output: model control integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) real(rkind),intent(out) :: dtMultiplier ! substep multiplier (-) @@ -227,6 +213,12 @@ subroutine varSubstep(& logical(lgt) :: checkNrgBalance logical(lgt) :: waterBalanceError ! flag to denote that there is a water balance error logical(lgt) :: nrgFluxModified ! flag to denote that the energy fluxes were modified + ! energy fluxes + real(rkind) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) + real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) + real(rkind) :: sumSoilCompress ! sum of total soil compression + real(rkind),allocatable :: sumLayerCompress(:) ! sum of soil compression by layer ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- @@ -286,6 +278,13 @@ subroutine varSubstep(& flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) end do + ! initialize the total energy fluxes (modified in updateProg) + sumCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = 0._rkind ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = 0._rkind ! sensible heat flux from the canopy to the canopy air space (W m-2) + sumSoilCompress = 0._rkind ! total soil compression + allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._rkind ! soil compression by layer + ! define the first flux call in a splitting operation firstSplitOper = (.not.scalarSolution .or. iStateSplit==1) @@ -452,7 +451,7 @@ subroutine varSubstep(& return endif - ! identify the need to check the mass balance, only for bEuler as fluxes are not not for checking in sundials + ! identify the need to check the mass balance, only for bEuler because of how store variables select case(ixNumericalMethod) case(sundials); checkMassBalance = .false. case(bEuler); checkMassBalance = .true. ! (.not.scalarSolution) @@ -498,7 +497,7 @@ subroutine varSubstep(& endif ! if errors in prognostic update - ! get the total energy fluxes (modified in updateProg) + ! get the total energy fluxes (modified in updateProg), have to do differently because of splitting in bEuler if(nrgFluxModified .or. indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then sumCanopyEvaporation = sumCanopyEvaporation + dtSubstep*flux_temp%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dtSubstep*flux_temp%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) @@ -531,7 +530,7 @@ subroutine varSubstep(& ! ** no domain splitting if(count(ixLayerActive/=integerMissing)==nLayers)then - flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + dtSubstep*flux_temp%var(iVar)%dat(:) + flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + dtSubstep*flux_temp%var(iVar)%dat(:)/dt fluxCount%var(iVar)%dat(:) = fluxCount%var(iVar)%dat(:) + 1 ! ** domain splitting @@ -543,10 +542,10 @@ subroutine varSubstep(& ! Makes Jacobian more diagonal if do this, but less correct ! special case of the transpiration sink from soil layers: only computed for the top soil layer !if(iVar==iLookFlux%mLayerTranspire)then - ! if(ixLayer==1) flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + dtSubstep*flux_temp%var(iVar)%dat(:) + ! if(ixLayer==1) flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + dtSubstep*flux_temp%var(iVar)%dat(:)/dt ! standard case !else - flux_sum%var(iVar)%dat(ixLayer) = flux_sum%var(iVar)%dat(ixLayer) + dtSubstep*flux_temp%var(iVar)%dat(ixLayer) + flux_data%var(iVar)%dat(ixLayer) = flux_data%var(iVar)%dat(ixLayer) + dtSubstep*flux_temp%var(iVar)%dat(ixLayer)/dt !endif fluxCount%var(iVar)%dat(ixLayer) = fluxCount%var(iVar)%dat(ixLayer) + 1 endif @@ -574,6 +573,19 @@ subroutine varSubstep(& end do substeps ! time steps for variable-dependent sub-stepping ! NOTE: if we get to here then we are accepting then dtSum should dt + ! save the energy fluxes as averages + flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt ! canopy evaporation/condensation (kg m-2 s-1) + flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt ! sensible heat flux from the canopy to the canopy air space (W m-2) + + ! save the soil compression diagnostics as averages + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress/dt + do iSoil=1,nSoil + if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& + diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) = sumLayerCompress(iSoil)/dt + end do + deallocate(sumLayerCompress) + ! end associate statements end associate globalVars @@ -946,7 +958,8 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! ----------------------- ! NOTE: should not need to do this, since mass balance is checked in the solver - ! for sundials will not work since fluxes are instantaneous so not currently checked + ! for sundials will not work since fluxes are averaged over data window for output but canopyBalance0 only off last step + ! so not currently checked in sundials if(checkMassBalance)then ! check mass balance for the canopy @@ -1009,7 +1022,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe endif ! if there is a water balance error endif ! if veg canopy - ! check mass balance for soil + ! check mass balance for soil, again not checked for sundials ! NOTE: fatal errors, though possible to recover using negative error codes if(count(ixSoilOnlyHyd/=integerMissing)==nSoil)then soilBalance1 = sum( (mLayerVolFracLiqTrial(nSnow+1:nLayers) + mLayerVolFracIceTrial(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) From 2e8ae62275c40357b7b7eab86fd21bb2bb5ac34f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 2 Mar 2023 17:44:53 +0900 Subject: [PATCH 0562/1472] missing deallocates --- build/source/engine/coupled_em.f90 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 77606663d..522af30d0 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -873,6 +873,7 @@ subroutine coupled_em(& ! try again, restart step dt_solv = dt_solv - dt_solvInner dt_solvInner = 0._rkind + deallocate(flux_sum) deallocate(sumLayerCompress) deallocate(mLayerVolFracIceInit) cycle substeps @@ -906,6 +907,7 @@ subroutine coupled_em(& do iVar=1,size(flux_meta) flux_data%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) /whole_step end do + deallocate(flux_sum) ! compute average soil compression diagnostics for whole_step diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress/whole_step From 1f2202f1231048d773b9925f0c3a24ade1b46eb7 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 2 Mar 2023 20:00:19 +0900 Subject: [PATCH 0563/1472] fixing scalarSoilCompres --- build/file | 13 - build/makefile | 412 ----------------------------- build/source/engine/coupled_em.f90 | 63 +---- build/source/engine/varSubstep.f90 | 9 +- 4 files changed, 17 insertions(+), 480 deletions(-) delete mode 100644 build/file delete mode 100644 build/makefile diff --git a/build/file b/build/file deleted file mode 100644 index a3384a553..000000000 --- a/build/file +++ /dev/null @@ -1,13 +0,0 @@ -/opt/local/bin/gfortran -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -c /Users/amedin/Research/SummaSundials/summa/build/source/engine/nrtype.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/f2008funcs.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/nr_utility.f90 /Users/amedin/Research/SummaSundials/summa/build/source/noah-mp/module_model_constants.F /Users/amedin/Research/SummaSundials/summa/build/source/noah-mp/module_sf_noahutl.F /Users/amedin/Research/SummaSundials/summa/build/source/noah-mp/module_sf_noahlsm.F /Users/amedin/Research/SummaSundials/summa/build/source/noah-mp/module_sf_noahmplsm.F -/opt/local/bin/gfortran -O3 -ffree-line-length-none -fmax-errors=0 -c /Users/amedin/Research/SummaSundials/summa/build/source/engine/nrtype.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/f2008funcs.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/nr_utility.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/expIntegral.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/spline_int.f90 /Users/amedin/Research/SummaSundials/summa/build/source/hookup/ascii_util.f90 /Users/amedin/Research/SummaSundials/summa/build/source/hookup/summaFileManager.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/multiconst.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/var_lookup.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/data_types.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/globalData.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/flxMapping.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/get_ixname.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/popMetadat.f90 /Users/amedin/Research/SummaSundials/summa/build/source/dshare/outpt_stat.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/time_utils.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/mDecisions.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/snow_utils.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/soil_utils.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/soil_utilsAddSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/updatState.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/updatStateSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/matrixOper.f90 -I/opt/local/include -I/opt/local/lib -echo "character(len=64), parameter :: summaVersion = 'v3.1.0'" > /Users/amedin/Research/SummaSundials/summa/build/source/driver/summaversion.inc -echo "character(len=64), parameter :: buildTime = 'Mon Feb 6 22:25:54 JST 2023'" >> /Users/amedin/Research/SummaSundials/summa/build/source/driver/summaversion.inc -echo "character(len=64), parameter :: gitBranch = 'master-0-g482a9b5c'" >> /Users/amedin/Research/SummaSundials/summa/build/source/driver/summaversion.inc -echo "character(len=64), parameter :: gitHash = '482a9b5cfa03b2b5fe54ea99308cd05417bd8d8e'" >> /Users/amedin/Research/SummaSundials/summa/build/source/driver/summaversion.inc -/opt/local/bin/gfortran -O3 -ffree-line-length-none -fmax-errors=0 -c /Users/amedin/Research/SummaSundials/summa/build/source/netcdf/netcdf_util.f90 /Users/amedin/Research/SummaSundials/summa/build/source/netcdf/def_output.f90 /Users/amedin/Research/SummaSundials/summa/build/source/netcdf/modelwrite.f90 /Users/amedin/Research/SummaSundials/summa/build/source/netcdf/read_icond.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/conv_funcs.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/sunGeomtry.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/convE2Temp.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/allocspace.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/checkStruc.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/childStruc.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/ffile_info.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/read_attrb.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/read_pinit.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/pOverwrite.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/read_param.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/paramCheck.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/check_icond.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/indexState.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/getVectorz.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/t2enthalpy.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/updateVars.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/updateVarsSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/var_derive.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/read_force.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/derivforce.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/snowAlbedo.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/canopySnow.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/tempAdjust.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/snwCompact.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/layerMerge.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/layerDivide.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/volicePack.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/qTimeDelay.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/vegPhenlgy.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/diagn_evar.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/stomResist.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/groundwatr.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/vegSWavRad.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/vegNrgFlux.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/ssdNrgFlux.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/vegLiqFlux.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/snowLiqFlx.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/soilLiqFlx.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/bigAquifer.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computFlux.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/type4IDA.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/tol4IDA.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computEnthalpy.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computHeatCap.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computThermConduct.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computResid.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computJacob.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/eval8summa.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/summaSolve.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/systemSolv.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computResidSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/eval8summaSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computJacobSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/computSnowDepth.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/summaSolveSundialsIDA.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/systemSolvSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/varSubstep.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/varSubstepSundials.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/opSplittin.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/coupled_em.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/run_oneHRU.f90 /Users/amedin/Research/SummaSundials/summa/build/source/engine/run_oneGRU.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_type.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_util.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_alarms.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_globalData.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_defineOutput.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_init.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_setup.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_restart.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_forcing.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_modelRun.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_writeOutput.f90 /Users/amedin/Research/SummaSundials/summa/build/source/driver/summa_driver.f90 \ - -I/opt/local/include -I/opt/local/lib -I/Users/amedin/Research/SummaSundials/sundials/instdir/include -I/Users/amedin/Research/SummaSundials/sundials/instdir/fortran -/opt/local/bin/gfortran -g *.o -L/opt/local/lib -llapack -lgfortran -lnetcdff -lnetcdf -L/Users/amedin/Research/SummaSundials/sundials/instdir/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod -o summa_sundials.exe -rm -f *.o -rm -f *.mod -rm -f soil_veg_gen_parm__genmod.f90 -summa_sundials.exe successfully installed in /Users/amedin/Research/SummaSundials/summa/bin diff --git a/build/makefile b/build/makefile deleted file mode 100644 index cc9363107..000000000 --- a/build/makefile +++ /dev/null @@ -1,412 +0,0 @@ -#======================================================================== -# Makefile to compile SUMMA SUNDIALS -#======================================================================== -# -# Recommended use: Copy this file to Makefile.local, edit it to your -# heart's content, and then run `make -f build/Makefile.local` from -# your top level SUMMA directory. Don't include the Makefile.local in -# any pull requests you make. -# -# Note that Makefile configurations that we commonly use can be found on -# the SUMMA wiki at: -# https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration -# feel free to add yours to that page. -# -# To troubleshoot your paths and setup, type 'make check' -# -# At a minimum you will need to set the following: -# * F_MASTER - top level summa directory -# * FC - compiler suite -# * FC_EXE - compiler executable -# * INCLUDES - path to include files -# * ILDFLAGS - path to libraries to include -# * LIBRARIES - libraries to include -# * DIR_SUNDIALS - installation sundials directory -# -# Some further options can be specified for OpenMP, etc. See in Part 0 of -# the Makefile. You don't need to make any changes in PART 1 and -# following unless you are doing SUMMA development and changed what -# needs to be compiled - -#======================================================================== -# PART 0: User-configurable part -#======================================================================== - -# The variables can be specified in one of two ways: -# * delete the '##' in front of the variable, fill out the entry, -# save the file and run make -# * make no changes to this file, but specify the variables in your -# environment before you run make - -# Define core directory below which everything resides. This is the -# parent directory of the 'build' directory -F_MASTER =/SUMMA - -# Define the Fortran Compiler. If you are using gfortran, then this needs -# to be version 6 or higher. This variable is simply used to select the right -# compiler flags in the ifeq statements in this Makefile. The compiler -# executable is set separately as FC_EXE -# Currently this is either gfortran or ifort -FC = gfortran - -# Define the path for the compiler executable. This is the actual executable -# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin/gfortran-mp-11 -# FC and FC_EXE have to be consistent -FC_EXE = gfortran - -# Define the NetCDF and LAPACK libraries and path to include files. -# INCLUDES needs to be of the form (no quotes around the string): -# INCLUDES = -I -I -I<...> -I -# LIBRARIES needs to be of the form ( no quotes around the string): -# LIBRARIES = '-L -lnetcdff -L -lblas -L -l' -# If none of this makes sense, please talk to your system -# administrator. -INCLUDES = -I/usr/include -I/usr/local/include -LIBRARIES = -L/usr/lib -L/usr/local/lib -lnetcdff -lopenblas - -DIR_SUNDIALS=/code/sundials/instdir -INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran -LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod - -# Eventually we plan move to a real configure script, but for now we like -# to keep track of successful compilations of SUMMA on different platforms -# and with different compilers. If you are successful compiling SUMMA, -# please add your configuration (operating system and compiler plus -# part 0 of the Makefile) to the SUMMA wiki on github. - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# ------------ define compiler flags ---------------------------------------- - -# define open MP flags -isOpenMP = -FLAGS_OMP = -LIBOPENMP = - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# gfortran compiler flags -ifeq "$(FC)" "gfortran" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -fopenmp - endif - -# Production runs -FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) - -# Debug runs -# FLAGS_NOAH = -p -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -Wall -# FLAGS_COMM = -p -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -# FLAGS_SUMMA = -p -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds - -endif - -# ifort compiler flags -ifeq "$(FC)" "ifort" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -qopenmp - endif - -# Production runs -FLAGS_NOAH = -O3 -noerror_limit -FR -auto -fltconsistency $(FLAGS_OMP) -FLAGS_COMM = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) - -# Debug runs -#FLAGS_NOAH = -O0 -p -g -warn nounused -noerror_limit -FR -auto -WB -traceback -fltconsistency -#FLAGS_COMM = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -#FLAGS_SUMMA = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -endif - -#======================================================================== -# PART 1: Define directory paths -#======================================================================== - -# Core directory that contains source code -F_KORE_DIR = $(F_MASTER)/build/source - -# Location of the compiled modules -MOD_PATH = $(F_MASTER)/build - -# Define the directory for the executables -EXE_PATH = $(F_MASTER)/bin - -#======================================================================== -# PART 2: Assemble all of the SUMMA sub-routines -#======================================================================== - -# Define directories -DRIVER_DIR = $(F_KORE_DIR)/driver -HOOKUP_DIR = $(F_KORE_DIR)/hookup -NETCDF_DIR = $(F_KORE_DIR)/netcdf -DSHARE_DIR = $(F_KORE_DIR)/dshare -NUMREC_DIR = $(F_KORE_DIR)/numrec -NOAHMP_DIR = $(F_KORE_DIR)/noah-mp -ENGINE_DIR = $(F_KORE_DIR)/engine - -# utilities -SUMMA_NRUTIL= \ - nrtype.f90 \ - f2008funcs.f90 \ - nr_utility.f90 -NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) - -# -# Numerical recipes procedures -# NOTE: all numerical recipes procedures are now replaced with free versions -SUMMA_NRPROC= \ - expIntegral.f90 \ - spline_int.f90 -NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) - -# Hook-up modules (set files and directory paths) -SUMMA_HOOKUP= \ - ascii_util.f90 \ - summaFileManager.f90 - -HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) - -# Data modules -SUMMA_DATAMS= \ - multiconst.f90 \ - var_lookup.f90 \ - data_types.f90 \ - globalData.f90 \ - flxMapping.f90 \ - get_ixname.f90 \ - popMetadat.f90 \ - outpt_stat.f90 -DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) - -# utility modules -SUMMA_UTILMS= \ - time_utils.f90 \ - mDecisions.f90 \ - snow_utils.f90 \ - soil_utils.f90 \ - soil_utilsAddSundials.f90 \ - updatState.f90 \ - updatStateSundials.f90 \ - matrixOper.f90 -UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) - -# Model guts -SUMMA_MODGUT= \ - MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) - -# Solver -SUMMA_SOLVER= \ - vegPhenlgy.f90 \ - diagn_evar.f90 \ - stomResist.f90 \ - groundwatr.f90 \ - vegSWavRad.f90 \ - vegNrgFlux.f90 \ - ssdNrgFlux.f90 \ - vegLiqFlux.f90 \ - snowLiqFlx.f90 \ - soilLiqFlx.f90 \ - bigAquifer.f90 \ - computFlux.f90 \ - computResid.f90 \ - computJacob.f90 \ - eval8summa.f90 \ - summaSolve.f90 \ - systemSolv.f90 \ - type4IDA.f90 \ - tol4IDA.f90 \ - computEnthalpy.f90 \ - computHeatCap.f90 \ - computThermConduct.f90 \ - computResidSundials.f90 \ - eval8summaSundials.f90 \ - computJacobSundials.f90 \ - computSnowDepth.f90 \ - summaSolveSundialsIDA.f90 \ - systemSolvSundials.f90 \ - varSubstep.f90 \ - varSubstepSundials.f90 \ - opSplittin.f90 \ - coupled_em.f90 \ - run_oneHRU.f90 \ - run_oneGRU.f90 -SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) - -# Define routines for SUMMA preliminaries -SUMMA_PRELIM= \ - conv_funcs.f90 \ - sunGeomtry.f90 \ - convE2Temp.f90 \ - allocspace.f90 \ - checkStruc.f90 \ - childStruc.f90 \ - ffile_info.f90 \ - read_attrb.f90 \ - read_pinit.f90 \ - pOverwrite.f90 \ - read_param.f90 \ - paramCheck.f90 \ - check_icond.f90 -PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) - -SUMMA_NOAHMP= \ - module_model_constants.F \ - module_sf_noahutl.F \ - module_sf_noahlsm.F \ - module_sf_noahmplsm.F -NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) - -# Define routines for the SUMMA model runs -SUMMA_MODRUN = \ - indexState.f90 \ - getVectorz.f90 \ - t2enthalpy.f90 \ - updateVars.f90 \ - updateVarsSundials.f90 \ - var_derive.f90 \ - read_force.f90 \ - derivforce.f90 \ - snowAlbedo.f90 \ - canopySnow.f90 \ - tempAdjust.f90 \ - snwCompact.f90 \ - layerMerge.f90 \ - layerDivide.f90 \ - volicePack.f90 \ - qTimeDelay.f90 -MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) - -# Define routines for the solver -SUMMA_MSOLVE = \ - -# Define NetCDF routines -SUMMA_NETCDF = \ - netcdf_util.f90 \ - def_output.f90 \ - modelwrite.f90 \ - read_icond.f90 -NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) - -# ... stitch together common programs -COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) - -# ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) - -# Define the driver routine -SUMMA_DRIVER= \ - summa_type.f90 \ - summa_util.f90 \ - summa_alarms.f90 \ - summa_globalData.f90 \ - summa_defineOutput.f90 \ - summa_init.f90 \ - summa_setup.f90 \ - summa_restart.f90 \ - summa_forcing.f90 \ - summa_modelRun.f90 \ - summa_writeOutput.f90 \ - summa_driver.f90 -DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) - -# Define the executable -DRIVER__EX = summa_sundials.exe - -# Define version number -VERSIONFILE = $(DRIVER_DIR)/summaversion.inc -VERSION = $(shell git tag | tail -n 1) -BULTTIM = $(shell date) -GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') -GITHASH = $(shell git rev-parse HEAD) - -#======================================================================== -# PART 3: Checks -#====================================================================== -# make sure that the paths are defined. These are just some high level checks -ifndef F_MASTER - $(error F_MASTER is undefined) -endif -ifndef FC - $(error FC is undefined: Specify your compiler) -endif -ifndef FC_EXE - $(error FC_EXE is undefined: Specify your compiler executable) -endif -ifndef FLAGS_SUMMA - $(error Specify flags for your compiler: $(FC)) -endif -ifndef INCLUDES - $(error INCLUDES is undefined) -endif -ifndef LIBRARIES - $(error LIBRARIES is undefined) -endif - -#======================================================================== -# PART 4: compilation -#====================================================================== - -# Compile -all: compile_noah compile_comm compile_summa link clean install -part: compile_noah compile_comm compile_summa - -check: - $(info) - $(info Displaying make variables:) - $(info F_MASTER : $(F_MASTER)) - $(info EXE_PATH : $(EXE_PATH)) - $(info FC : $(FC)) - $(info INCLUDES : $(INCLUDES)) - $(info LIBRARIES : $(LIBRARIES)) - $(info FLAGS_NOAH : $(FLAGS_NOAH)) - $(info FLAGS_COMM : $(FLAGS_COMM)) - $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) - $(info SUNDIALSDIR: $(DIR_SUNDIALS)) - $(info INC_SUNDIALS: $(INC_SUNDIALS)) - $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) - $(info) - -# update version information -update_version: - echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) - echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) - -# compile Noah-MP routines -compile_noah: - $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) - -# compile common routines - -compile_comm: - $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) - -# compile SUMMA routines -compile_summa: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ - $(INCLUDES) $(INC_SUNDIALS) - -# link routines -link: - $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) - -# Remove object files -clean: - rm -f *.o - rm -f *.mod - rm -f soil_veg_gen_parm__genmod.f90 - -# Copy the executable to the bin directory -install: - @mkdir -p $(EXE_PATH) - @mv $(DRIVER__EX) $(EXE_PATH) - $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 522af30d0..bfdd41e16 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -60,7 +60,6 @@ module coupled_em_module USE globalData,only:averageFlux_meta ! metadata on the timestep-average model flux structure ! global data -USE globalData,only:integerMissing ! missing integer USE globalData,only:data_step ! time step of forcing data (s) USE globalData,only:model_decisions ! model decision structure USE globalData,only:globalPrintFlag ! the global print flag @@ -190,7 +189,6 @@ subroutine coupled_em(& logical(lgt) :: includeAquifer ! flag to denote that an aquifer is included logical(lgt) :: modifiedLayers ! flag to denote that snow layers were modified logical(lgt) :: modifiedVegState ! flag to denote that vegetation states were modified - type(var_dlength) :: flux_mean ! timestep-average model fluxes for a local HRU integer(i4b) :: nLayersRoots ! number of soil layers that contain roots real(rkind) :: exposedVAI ! exposed vegetation area index real(rkind) :: dCanopyWetFraction_dWat ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) @@ -226,9 +224,8 @@ subroutine coupled_em(& real(rkind) :: massBalance ! mass balance error (kg m-2) ! energy fluxes integer(i4b) :: iSoil ! index of soil layers - type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a whole_step - real(rkind) :: sumSoilCompress ! sum of total soil compression - real(rkind),allocatable :: sumLayerCompress(:) ! sum of soil compression by layer + type(var_dlength) :: flux_mean ! timestep-average model fluxes for a local HRU + real(rkind) :: meanSoilCompress ! timestep-average soil compression ! balance checks integer(i4b) :: iVar ! loop through model variables real(rkind) :: balanceSoilCompress ! total soil compression (kg m-2) @@ -333,10 +330,12 @@ subroutine coupled_em(& ! initialize surface melt pond sfcMeltPond = 0._rkind ! change in storage associated with the surface melt pond (kg m-2) - ! initialize mean fluxes + ! initialize fluxes to average over whole step (averaged over substep in varSubStep) do iVar=1,size(averageFlux_meta) flux_mean%var(iVar)%dat(:) = 0._rkind end do + ! initialize the compression to average over whole step (averaged over substep in varSubStep) + meanSoilCompress = 0._rkind ! total soil compression ! associate local variables with information in the data structures associate(& @@ -750,15 +749,6 @@ subroutine coupled_em(& if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if endif - ! initialize the compression and total energy fluxes to average over whole step (averaged over substep in varSubStep) - sumSoilCompress = 0._rkind ! total soil compression - allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._rkind ! soil compression by layer - call allocLocal(flux_meta(:),flux_sum,nSnow,nSoil,err,cmessage) ! flux_sum structure - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - do concurrent ( iVar=1:size(flux_meta) ) - flux_sum%var(iVar)%dat(:) = 0._rkind - end do - ! save volumetric ice content at the start of the step ! NOTE: used for volumetric loss due to melt-freeze allocate(mLayerVolFracIceInit(nLayers)); mLayerVolFracIceInit = prog_data%var(iLookPROG%mLayerVolFracIce)%dat @@ -873,23 +863,10 @@ subroutine coupled_em(& ! try again, restart step dt_solv = dt_solv - dt_solvInner dt_solvInner = 0._rkind - deallocate(flux_sum) - deallocate(sumLayerCompress) deallocate(mLayerVolFracIceInit) cycle substeps endif - ! get the total soil compression - sumSoilCompress = sumSoilCompress + dt_sub*diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression - do iSoil=1,nSoil - if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& - sumLayerCompress(iSoil) = sumLayerCompress(iSoil) + dt_sub*diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) ! soil compression in layers - end do - ! get the total energy fluxes - do iVar=1,size(flux_meta) - flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + dt_sub*flux_data%var(iVar)%dat(:) - end do - ! update first step and first and last inner steps firstSubStep = .false. firstInnerStep = .false. @@ -903,21 +880,6 @@ subroutine coupled_em(& if( dt_sub == maxstep_op .and. .not.lastInnerStep ) do_outer = .false. if(do_outer)then - ! compute average flux for whole_step - do iVar=1,size(flux_meta) - flux_data%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) /whole_step - end do - deallocate(flux_sum) - - ! compute average soil compression diagnostics for whole_step - diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress/whole_step - ! vector compression - do iSoil=1,nSoil - if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& - diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) = sumLayerCompress(iSoil)/whole_step - end do - deallocate(sumLayerCompress) - ! *** remove ice due to sublimation and freeze calculations... ! NOTE: In the future this should be moved into the solver, makes a big difference, and here only applying last substep amount to whole thing ! -------------------------------------------------------------- @@ -1054,11 +1016,13 @@ subroutine coupled_em(& ! *** END MAIN SOLVER ******************************************************************************** ! **************************************************************************************************** - ! increment fluxes + ! increment fluxes and soil compression dt_wght = dt_sub/data_step ! define weight applied to each sub-step do iVar=1,size(averageFlux_meta) flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:)*dt_wght end do + meanSoilCompress = meanSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1)*dt_wght ! total soil compression + flux_mean%var(childFLUX_MEAN(iLookDIAG%scalarSoilCompress))%dat(1) = meanSoilCompress ! increment sub-step dt_solv = dt_solv + dt_sub @@ -1129,16 +1093,13 @@ subroutine coupled_em(& err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - ! overwrite flux_data with flux_mean for data_step (returns timestep-average fluxes for scalar variables) + ! overwrite flux_data and soil compression with the timestep-average value (returns timestep-average fluxes for scalar variables) do iVar=1,size(averageFlux_meta) flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:) = flux_mean%var(iVar)%dat(:) end do + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = meanSoilCompress ! *********************************************************************************************************************************** - ! *********************************************************************************************************************************** - ! *********************************************************************************************************************************** - ! *********************************************************************************************************************************** - ! --- ! *** balance checks... ! --------------------- @@ -1329,10 +1290,10 @@ subroutine coupled_em(& ! save the surface temperature (just to make things easier to visualize) prog_data%var(iLookPROG%scalarSurfaceTemp)%dat(1) = prog_data%var(iLookPROG%mLayerTemp)%dat(1) - ! overwrite flux data with the timestep-average value + ! again overwrite flux data with timestep-average value if(.not.backwardsCompatibility)then do iVar=1,size(flux_mean%var) - flux_data%var(averageFlux_meta(iVar)%ixParent)%dat = flux_mean%var(iVar)%dat + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat = flux_mean%var(iVar)%dat end do end if diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 09031562c..30205bc0e 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -524,13 +524,14 @@ subroutine varSubstep(& if(globalPrintFlag)& write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt - ! get the total energy fluxes + ! increment fluxes + dt_wght = dtSubstep/dt ! define weight applied to each sub-step do iVar=1,size(flux_meta) if(count(fluxMask%var(iVar)%dat)>0) then ! ** no domain splitting if(count(ixLayerActive/=integerMissing)==nLayers)then - flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + dtSubstep*flux_temp%var(iVar)%dat(:)/dt + flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght fluxCount%var(iVar)%dat(:) = fluxCount%var(iVar)%dat(:) + 1 ! ** domain splitting @@ -542,10 +543,10 @@ subroutine varSubstep(& ! Makes Jacobian more diagonal if do this, but less correct ! special case of the transpiration sink from soil layers: only computed for the top soil layer !if(iVar==iLookFlux%mLayerTranspire)then - ! if(ixLayer==1) flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + dtSubstep*flux_temp%var(iVar)%dat(:)/dt + ! if(ixLayer==1) flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght ! standard case !else - flux_data%var(iVar)%dat(ixLayer) = flux_data%var(iVar)%dat(ixLayer) + dtSubstep*flux_temp%var(iVar)%dat(ixLayer)/dt + flux_data%var(iVar)%dat(ixLayer) = flux_data%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght !endif fluxCount%var(iVar)%dat(ixLayer) = fluxCount%var(iVar)%dat(ixLayer) + 1 endif From c441b2655067a7108ff8a887a801136e07ffbf23 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 2 Mar 2023 21:32:02 +0900 Subject: [PATCH 0564/1472] put update of water back in before every step of split --- build/source/engine/opSplittin.f90 | 59 +++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 68309a23a..da93f3198 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -184,6 +184,9 @@ subroutine opSplittin(& ! --------------------------------------------------------------------------------------- ! structure allocations USE allocspace_module,only:allocLocal ! allocate local data structures + ! simulation of fluxes and residuals given a trial state vector + USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content + USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential ! population/extraction of state vectors USE indexState_module,only:indexSplit ! get state indices USE varSubstep_module,only:varSubstep ! complete substeps for a given split @@ -307,7 +310,37 @@ subroutine opSplittin(& numberStateSplit => indx_data%var(iLookINDEX%numberStateSplit )%dat(1) ,& ! intent(inout): [i4b] number of state splitting solutions (-) numberDomainSplitNrg => indx_data%var(iLookINDEX%numberDomainSplitNrg )%dat(1) ,& ! intent(inout): [i4b] number of domain splitting solutions for energy (-) numberDomainSplitMass => indx_data%var(iLookINDEX%numberDomainSplitMass)%dat(1) ,& ! intent(inout): [i4b] number of domain splitting solutions for mass (-) - numberScalarSolutions => indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1) & ! intent(inout): [i4b] number of scalar solutions (-) + numberScalarSolutions => indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1) ,& ! intent(inout): [i4b] number of scalar solutions (-) + ! domain configuration + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ! depth-varying soil parameters + vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) + ! soil parameters + specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) + ! model diagnostic variables (fraction of liquid water) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) + mLayerMeltFreeze => diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat ,& ! intent(out): [dp(:)] melt-freeze in each snow and soil layer (kg m-3) + ! model state variables (vegetation canopy) + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(out): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(out): [dp] temperature of the vegetation canopy (K) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(out): [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(out): [dp] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(out): [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variables (snow and soil domains) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(out): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(out): [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(out): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(out): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(out): [dp(:)] matric head (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat & ! intent(out): [dp(:)] matric potential of liquid water (m) ) ! --------------------------------------------------------------------------------------- ! initialize error control @@ -347,6 +380,30 @@ subroutine opSplittin(& ! initialize the state check stateCheck(:) = 0 + ! compute the total water content in the vegetation canopy + scalarCanopyWat = scalarCanopyLiq + scalarCanopyIce ! kg m-2 + + ! save volumetric ice content at the start of the step + ! NOTE: used for volumetric loss due to melt-freeze + !mLayerVolFracIceInit(:) = mLayerVolFracIce(:) + + ! compute the total water content in snow and soil + ! NOTE: no ice expansion allowed for soil + if(nSnow>0)& + mLayerVolFracWat( 1:nSnow ) = mLayerVolFracLiq( 1:nSnow ) + mLayerVolFracIce( 1:nSnow )*(iden_ice/iden_water) + mLayerVolFracWat(nSnow+1:nLayers) = mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) + + ! compute the liquid water matric potential (m) + ! NOTE: include ice content as part of the solid porosity - major effect of ice is to reduce the pore size; ensure that effSat=1 at saturation + ! (from Zhao et al., J. Hydrol., 1997: Numerical analysis of simultaneous heat and mass transfer...) + do iSoil=1,nSoil + call liquidHead(mLayerMatricHead(iSoil),mLayerVolFracLiq(nSnow+iSoil),mLayerVolFracIce(nSnow+iSoil), & ! input: state variables + vGn_alpha(iSoil),vGn_n(iSoil),theta_sat(iSoil),theta_res(iSoil),vGn_m(iSoil), & ! input: parameters + matricHeadLiq=mLayerMatricHeadLiq(iSoil), & ! output: liquid water matric potential (m) + err=err,message=cmessage) ! output: error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + end do ! looping through soil layers (computing liquid water matric potential) + ! allocate space for the flux mask (used to define when fluxes are updated) call allocLocal(flux_meta(:),fluxMask,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif From 8c509f90c1c87f6fad747ab7851f1770782a03a9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 2 Mar 2023 22:40:30 +0900 Subject: [PATCH 0565/1472] Revert "put update of water back in before every step of split" This reverts commit c441b2655067a7108ff8a887a801136e07ffbf23. --- build/source/engine/opSplittin.f90 | 59 +----------------------------- 1 file changed, 1 insertion(+), 58 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index da93f3198..68309a23a 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -184,9 +184,6 @@ subroutine opSplittin(& ! --------------------------------------------------------------------------------------- ! structure allocations USE allocspace_module,only:allocLocal ! allocate local data structures - ! simulation of fluxes and residuals given a trial state vector - USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content - USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential ! population/extraction of state vectors USE indexState_module,only:indexSplit ! get state indices USE varSubstep_module,only:varSubstep ! complete substeps for a given split @@ -310,37 +307,7 @@ subroutine opSplittin(& numberStateSplit => indx_data%var(iLookINDEX%numberStateSplit )%dat(1) ,& ! intent(inout): [i4b] number of state splitting solutions (-) numberDomainSplitNrg => indx_data%var(iLookINDEX%numberDomainSplitNrg )%dat(1) ,& ! intent(inout): [i4b] number of domain splitting solutions for energy (-) numberDomainSplitMass => indx_data%var(iLookINDEX%numberDomainSplitMass)%dat(1) ,& ! intent(inout): [i4b] number of domain splitting solutions for mass (-) - numberScalarSolutions => indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1) ,& ! intent(inout): [i4b] number of scalar solutions (-) - ! domain configuration - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ! depth-varying soil parameters - vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) - ! soil parameters - specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) - ! model diagnostic variables (fraction of liquid water) - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) - mLayerMeltFreeze => diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat ,& ! intent(out): [dp(:)] melt-freeze in each snow and soil layer (kg m-3) - ! model state variables (vegetation canopy) - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(out): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(out): [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(out): [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(out): [dp] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(out): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(out): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(out): [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(out): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(out): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(out): [dp(:)] matric head (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat & ! intent(out): [dp(:)] matric potential of liquid water (m) + numberScalarSolutions => indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1) & ! intent(inout): [i4b] number of scalar solutions (-) ) ! --------------------------------------------------------------------------------------- ! initialize error control @@ -380,30 +347,6 @@ subroutine opSplittin(& ! initialize the state check stateCheck(:) = 0 - ! compute the total water content in the vegetation canopy - scalarCanopyWat = scalarCanopyLiq + scalarCanopyIce ! kg m-2 - - ! save volumetric ice content at the start of the step - ! NOTE: used for volumetric loss due to melt-freeze - !mLayerVolFracIceInit(:) = mLayerVolFracIce(:) - - ! compute the total water content in snow and soil - ! NOTE: no ice expansion allowed for soil - if(nSnow>0)& - mLayerVolFracWat( 1:nSnow ) = mLayerVolFracLiq( 1:nSnow ) + mLayerVolFracIce( 1:nSnow )*(iden_ice/iden_water) - mLayerVolFracWat(nSnow+1:nLayers) = mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) - - ! compute the liquid water matric potential (m) - ! NOTE: include ice content as part of the solid porosity - major effect of ice is to reduce the pore size; ensure that effSat=1 at saturation - ! (from Zhao et al., J. Hydrol., 1997: Numerical analysis of simultaneous heat and mass transfer...) - do iSoil=1,nSoil - call liquidHead(mLayerMatricHead(iSoil),mLayerVolFracLiq(nSnow+iSoil),mLayerVolFracIce(nSnow+iSoil), & ! input: state variables - vGn_alpha(iSoil),vGn_n(iSoil),theta_sat(iSoil),theta_res(iSoil),vGn_m(iSoil), & ! input: parameters - matricHeadLiq=mLayerMatricHeadLiq(iSoil), & ! output: liquid water matric potential (m) - err=err,message=cmessage) ! output: error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - end do ! looping through soil layers (computing liquid water matric potential) - ! allocate space for the flux mask (used to define when fluxes are updated) call allocLocal(flux_meta(:),fluxMask,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif From 758219ea6b16a87cc2b6916532b59637fcf6b575 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 3 Mar 2023 16:42:55 +0900 Subject: [PATCH 0566/1472] turn on mass checking for all bEuler, fix scalarCanopyIceMax, some comments --- build/source/engine/coupled_em.f90 | 15 +++++++++------ build/source/engine/varSubstep.f90 | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index bfdd41e16..eec555858 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -471,7 +471,7 @@ subroutine coupled_em(& prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1), & ! canopy liquid water (kg m-2) prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! canopy ice (kg m-2) diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1), & ! maximum canopy liquid water (kg m-2) - diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1), & ! maximum canopy ice content (kg m-2) + diag_data%var(iLookDIAG%scalarCanopyIceMax)%dat(1), & ! maximum canopy ice content (kg m-2) mpar_data%var(iLookPARAM%canopyWettingFactor)%dat(1), & ! maximum wetted fraction of the canopy (-) mpar_data%var(iLookPARAM%canopyWettingExp)%dat(1), & ! exponent in canopy wetting function (-) ! output @@ -1146,10 +1146,13 @@ subroutine coupled_em(& ! * balance checks for the canopy... ! ---------------------------------- - if (model_decisions(iLookDECISIONS%num_method)%iDecision==bEuler)then ! convergence criteria for bEuler - if (maxstep_op == maxstep) checkMassBalance = .true. - if (maxstep_op < maxstep) checkMassBalance = .false. ! cannot check since not saving fluxes on inner loops - endif + !if (model_decisions(iLookDECISIONS%num_method)%iDecision==bEuler)then ! convergence criteria for bEuler + ! if (maxstep_op == maxstep) checkMassBalance = .true. + ! if (maxstep_op < maxstep) checkMassBalance = .false. ! cannot check since not saving fluxes on inner loops + !endif + !if (model_decisions(iLookDECISIONS%num_method)%iDecision==sundials) checkMassBalance = .false. ! cannot check since not saving fluxes on inner loops + + if (model_decisions(iLookDECISIONS%num_method)%iDecision==bEuler) checkMassBalance = .true. ! convergence criteria for bEuler if (model_decisions(iLookDECISIONS%num_method)%iDecision==sundials) checkMassBalance = .false. ! cannot check since not saving fluxes on inner loops ! if computing the vegetation flux @@ -1290,7 +1293,7 @@ subroutine coupled_em(& ! save the surface temperature (just to make things easier to visualize) prog_data%var(iLookPROG%scalarSurfaceTemp)%dat(1) = prog_data%var(iLookPROG%mLayerTemp)%dat(1) - ! again overwrite flux data with timestep-average value + ! overwrite flux data with timestep-average value for all flux_mean vars, hard-coded to not happen if(.not.backwardsCompatibility)then do iVar=1,size(flux_mean%var) flux_data%var(averageFlux_meta(iVar)%ixParent)%dat = flux_mean%var(iVar)%dat diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 30205bc0e..dbe11fc5f 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -1048,7 +1048,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe endif ! if checking the mass balance ! ----- - ! * remove untapped melt energy... + ! * remove untapped melt energy... always 0 at the moment but if use should be in solved as affects state ! -------------------------------- ! only work with energy state variables From a7e17aeaa74861cca73e21c51424be67c8daf0a5 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 3 Mar 2023 16:56:43 +0900 Subject: [PATCH 0567/1472] printing canIce --- build/source/engine/coupled_em.f90 | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index eec555858..d0c296792 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -355,7 +355,7 @@ subroutine coupled_em(& ! save the liquid water and ice on the vegetation canopy scalarInitCanopyLiq = scalarCanopyLiq ! initial liquid water on the vegetation canopy (kg m-2) scalarInitCanopyIce = scalarCanopyIce ! initial ice on the vegetation canopy (kg m-2) - + print*,scalarCanopyIce, 'canIce init' ! compute total soil moisture and ice at the *START* of the step (kg m-2) scalarTotalSoilLiq = sum(iden_water*mLayerVolFracLiq(1:nSoil)*mLayerDepth(1:nSoil)) scalarTotalSoilIce = sum(iden_water*mLayerVolFracIce(1:nSoil)*mLayerDepth(1:nSoil)) ! NOTE: no expansion and hence use iden_water @@ -543,7 +543,7 @@ subroutine coupled_em(& ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - + print*,prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), 'canIce after canSnow before water updates' ! adjust canopy temperature to account for new snow if(computeVegFlux)then ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) call tempAdjust(& @@ -898,8 +898,7 @@ subroutine coupled_em(& mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat, & ! volumetric fraction of liquid water in the snow+soil domain (-) mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat & ! depth of each snow+soil layer (m) ) ! associations to variables in data structures - - + print*,scalarCanopyIce, 'canIce beforeSublim after iteration water updates' ! compute the melt in each snow and soil layer if(nSnow>0)& mLayerMeltFreeze(1:nSnow) = -( mLayerVolFracIce(1:nSnow) - mLayerVolFracIceInit(1:nSnow) ) * iden_ice @@ -915,7 +914,7 @@ subroutine coupled_em(& ! remove mass of ice on the canopy scalarCanopyIce = scalarCanopyIce + scalarCanopySublimation*whole_step - + print*,scalarCanopyIce, scalarCanopySublimation, whole_step,'canIce afterSublim, sublim, step' ! if removed all ice, take the remaining sublimation from water if(scalarCanopyIce < 0._rkind)then scalarCanopyLiq = scalarCanopyLiq + scalarCanopyIce From a6123779d7e252d9799af7988a195dab8fbe3383 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 3 Mar 2023 20:40:44 +0900 Subject: [PATCH 0568/1472] first flux call was not assigned right --- build/source/engine/opSplittin.f90 | 1 + build/source/engine/varSubstep.f90 | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 68309a23a..d84a0649f 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -469,6 +469,7 @@ subroutine opSplittin(& ! initialize the first flux call firstFluxCall=.true. + if (.not.firstInnerStep) firstFluxCall=.false ! get the number of split layers select case(ixSolution) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index dbe11fc5f..2c1edea09 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -497,7 +497,7 @@ subroutine varSubstep(& endif ! if errors in prognostic update - ! get the total energy fluxes (modified in updateProg), have to do differently because of splitting in bEuler + ! get the total energy fluxes (modified in updateProg), have to do differently because only updated on firstFluxCall if(nrgFluxModified .or. indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then sumCanopyEvaporation = sumCanopyEvaporation + dtSubstep*flux_temp%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dtSubstep*flux_temp%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) @@ -960,7 +960,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! NOTE: should not need to do this, since mass balance is checked in the solver ! for sundials will not work since fluxes are averaged over data window for output but canopyBalance0 only off last step - ! so not currently checked in sundials + ! so not currently checked in sundials, could cause problems if should modify nrgFlux if(checkMassBalance)then ! check mass balance for the canopy From 3522eb330f5158e456221271ca9f1df4eef07ccb Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 3 Mar 2023 23:36:09 +0900 Subject: [PATCH 0569/1472] need to not zero fluxes if on inner step --- build/source/engine/coupled_em.f90 | 20 +++++++++----------- build/source/engine/opSplittin.f90 | 14 ++++++++------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index d0c296792..9688c3350 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -355,7 +355,7 @@ subroutine coupled_em(& ! save the liquid water and ice on the vegetation canopy scalarInitCanopyLiq = scalarCanopyLiq ! initial liquid water on the vegetation canopy (kg m-2) scalarInitCanopyIce = scalarCanopyIce ! initial ice on the vegetation canopy (kg m-2) - print*,scalarCanopyIce, 'canIce init' + ! compute total soil moisture and ice at the *START* of the step (kg m-2) scalarTotalSoilLiq = sum(iden_water*mLayerVolFracLiq(1:nSoil)*mLayerDepth(1:nSoil)) scalarTotalSoilIce = sum(iden_water*mLayerVolFracIce(1:nSoil)*mLayerDepth(1:nSoil)) ! NOTE: no expansion and hence use iden_water @@ -543,7 +543,7 @@ subroutine coupled_em(& ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - print*,prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), 'canIce after canSnow before water updates' + ! adjust canopy temperature to account for new snow if(computeVegFlux)then ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) call tempAdjust(& @@ -898,7 +898,8 @@ subroutine coupled_em(& mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat, & ! volumetric fraction of liquid water in the snow+soil domain (-) mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat & ! depth of each snow+soil layer (m) ) ! associations to variables in data structures - print*,scalarCanopyIce, 'canIce beforeSublim after iteration water updates' + + ! compute the melt in each snow and soil layer if(nSnow>0)& mLayerMeltFreeze(1:nSnow) = -( mLayerVolFracIce(1:nSnow) - mLayerVolFracIceInit(1:nSnow) ) * iden_ice @@ -914,7 +915,7 @@ subroutine coupled_em(& ! remove mass of ice on the canopy scalarCanopyIce = scalarCanopyIce + scalarCanopySublimation*whole_step - print*,scalarCanopyIce, scalarCanopySublimation, whole_step,'canIce afterSublim, sublim, step' + ! if removed all ice, take the remaining sublimation from water if(scalarCanopyIce < 0._rkind)then scalarCanopyLiq = scalarCanopyLiq + scalarCanopyIce @@ -1145,13 +1146,10 @@ subroutine coupled_em(& ! * balance checks for the canopy... ! ---------------------------------- - !if (model_decisions(iLookDECISIONS%num_method)%iDecision==bEuler)then ! convergence criteria for bEuler - ! if (maxstep_op == maxstep) checkMassBalance = .true. - ! if (maxstep_op < maxstep) checkMassBalance = .false. ! cannot check since not saving fluxes on inner loops - !endif - !if (model_decisions(iLookDECISIONS%num_method)%iDecision==sundials) checkMassBalance = .false. ! cannot check since not saving fluxes on inner loops - - if (model_decisions(iLookDECISIONS%num_method)%iDecision==bEuler) checkMassBalance = .true. ! convergence criteria for bEuler + if (model_decisions(iLookDECISIONS%num_method)%iDecision==bEuler)then ! convergence criteria for bEuler + if (maxstep_op == maxstep) checkMassBalance = .true. + if (maxstep_op < maxstep) checkMassBalance = .false. ! cannot check since not saving fluxes on inner loops + endif if (model_decisions(iLookDECISIONS%num_method)%iDecision==sundials) checkMassBalance = .false. ! cannot check since not saving fluxes on inner loops ! if computing the vegetation flux diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index d84a0649f..20b86b8a5 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -377,11 +377,13 @@ subroutine opSplittin(& end do ! initialize the model fluxes - do iVar=1,size(flux_meta) ! loop through fluxes - if(flux2state_orig(iVar)%state1==integerMissing .and. flux2state_orig(iVar)%state2==integerMissing) cycle ! flux does not depend on state (e.g., input) - if(flux2state_orig(iVar)%state1==iname_watCanopy .and. .not.computeVegFlux) cycle ! use input fluxes in cases where there is no canopy - flux_data%var(iVar)%dat(:) = 0._rkind - end do + if (firstInnerStep)then + do iVar=1,size(flux_meta) ! loop through fluxes + if(flux2state_orig(iVar)%state1==integerMissing .and. flux2state_orig(iVar)%state2==integerMissing) cycle ! flux does not depend on state (e.g., input) + if(flux2state_orig(iVar)%state1==iname_watCanopy .and. .not.computeVegFlux) cycle ! use input fluxes in cases where there is no canopy + flux_data%var(iVar)%dat(:) = 0._rkind + end do + endif ! initialize derivatives do iVar=1,size(deriv_meta) @@ -469,7 +471,7 @@ subroutine opSplittin(& ! initialize the first flux call firstFluxCall=.true. - if (.not.firstInnerStep) firstFluxCall=.false + if (.not.firstInnerStep) firstFluxCall=.false. ! get the number of split layers select case(ixSolution) From 8faab033138efd8c20231b19604cfab91a277588 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 6 Mar 2023 17:48:38 +0900 Subject: [PATCH 0570/1472] attempt to get fluxes to add correctly --- build/source/engine/opSplittin.f90 | 19 ++++++++++++------- build/source/engine/varSubstep.f90 | 17 ++++++++++++----- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 20b86b8a5..3b8cdc70d 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -236,6 +236,7 @@ subroutine opSplittin(& type(var_dlength) :: prog_temp ! temporary model prognostic variables type(var_dlength) :: diag_temp ! temporary model diagnostic variables type(var_dlength) :: flux_temp ! temporary model fluxes + type(var_dlength) :: flux_mean ! mean model fluxes type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! ------------------------------------------------------------------------------------------------------ ! * operator splitting @@ -367,6 +368,10 @@ subroutine opSplittin(& call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + ! allocate space for the mean flux variable structure + call allocLocal(flux_meta(:),flux_mean,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + ! allocate space for the derivative structure call allocLocal(deriv_meta(:),deriv_data,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if @@ -377,13 +382,12 @@ subroutine opSplittin(& end do ! initialize the model fluxes - if (firstInnerStep)then - do iVar=1,size(flux_meta) ! loop through fluxes - if(flux2state_orig(iVar)%state1==integerMissing .and. flux2state_orig(iVar)%state2==integerMissing) cycle ! flux does not depend on state (e.g., input) - if(flux2state_orig(iVar)%state1==iname_watCanopy .and. .not.computeVegFlux) cycle ! use input fluxes in cases where there is no canopy - flux_data%var(iVar)%dat(:) = 0._rkind - end do - endif + do iVar=1,size(flux_meta) ! loop through fluxes + if(flux2state_orig(iVar)%state1==integerMissing .and. flux2state_orig(iVar)%state2==integerMissing) cycle ! flux does not depend on state (e.g., input) + if(flux2state_orig(iVar)%state1==iname_watCanopy .and. .not.computeVegFlux) cycle ! use input fluxes in cases where there is no canopy + if (firstInnerStep) flux_data%var(iVar)%dat(:) = 0._rkind + flux_mean%var(iVar)%dat(:) = 0._rkind + end do ! initialize derivatives do iVar=1,size(deriv_meta) @@ -699,6 +703,7 @@ subroutine opSplittin(& prog_data, & ! intent(inout) : model prognostic variables for a local HRU diag_data, & ! intent(inout) : model diagnostic variables for a local HRU flux_data, & ! intent(inout) : model fluxes for a local HRU + flux_mean, & ! intent(inout) : mean model fluxes for a local HRU deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables bvar_data, & ! intent(in) : model variables for the local basin ! output: control diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 2c1edea09..fc6b40946 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -114,7 +114,8 @@ subroutine varSubstep(& indx_data, & ! intent(inout) : index data prog_data, & ! intent(inout) : model prognostic variables for a local HRU diag_data, & ! intent(inout) : model diagnostic variables for a local HRU - flux_data, & ! intent(inout) : model fluxes for a local HRU + flux_data, & ! intent(inout) : model fluxes for a local HRU + flux_mean, & ! intent(inout) : mean model fluxes for a local HRU deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables bvar_data, & ! intent(in) : model variables for the local basin ! output: model control @@ -164,6 +165,7 @@ subroutine varSubstep(& type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: flux_mean ! mean model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin ! output: model control @@ -497,7 +499,7 @@ subroutine varSubstep(& endif ! if errors in prognostic update - ! get the total energy fluxes (modified in updateProg), have to do differently because only updated on firstFluxCall + ! get the total energy fluxes (modified in updateProg), have to do differently if(nrgFluxModified .or. indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then sumCanopyEvaporation = sumCanopyEvaporation + dtSubstep*flux_temp%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dtSubstep*flux_temp%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) @@ -531,7 +533,7 @@ subroutine varSubstep(& ! ** no domain splitting if(count(ixLayerActive/=integerMissing)==nLayers)then - flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght + flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght fluxCount%var(iVar)%dat(:) = fluxCount%var(iVar)%dat(:) + 1 ! ** domain splitting @@ -543,10 +545,10 @@ subroutine varSubstep(& ! Makes Jacobian more diagonal if do this, but less correct ! special case of the transpiration sink from soil layers: only computed for the top soil layer !if(iVar==iLookFlux%mLayerTranspire)then - ! if(ixLayer==1) flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght + ! if(ixLayer==1) flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght ! standard case !else - flux_data%var(iVar)%dat(ixLayer) = flux_data%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght + flux_mean%var(iVar)%dat(ixLayer) = flux_mean%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght !endif fluxCount%var(iVar)%dat(ixLayer) = fluxCount%var(iVar)%dat(ixLayer) + 1 endif @@ -574,6 +576,11 @@ subroutine varSubstep(& end do substeps ! time steps for variable-dependent sub-stepping ! NOTE: if we get to here then we are accepting then dtSum should dt + ! save the fluxes as averages + do iVar=1,size(flux_meta) + if(count(fluxMask%var(iVar)%dat)>0) flux_data%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + enddo + ! save the energy fluxes as averages flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt ! canopy evaporation/condensation (kg m-2 s-1) flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) From f5a3ec7ffff3656584ce29a81bf64c480341be54 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 6 Mar 2023 23:39:38 +0900 Subject: [PATCH 0571/1472] undoing flux_mean collection in opSplittin, don't think necessary --- build/source/engine/coupled_em.f90 | 2 +- build/source/engine/opSplittin.f90 | 9 +-------- build/source/engine/varSubstep.f90 | 15 ++++----------- 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 9688c3350..9f06d06b5 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -940,7 +940,7 @@ subroutine coupled_em(& end if ! (if computing the vegetation flux) call computSnowDepth(& - whole_step, & ! intent(in) + whole_step, & ! intent(in) nSnow, & ! intent(in) scalarSnowSublimation, & ! intent(in) mLayerVolFracLiq, & ! intent(inout) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 3b8cdc70d..760c642a7 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -236,7 +236,6 @@ subroutine opSplittin(& type(var_dlength) :: prog_temp ! temporary model prognostic variables type(var_dlength) :: diag_temp ! temporary model diagnostic variables type(var_dlength) :: flux_temp ! temporary model fluxes - type(var_dlength) :: flux_mean ! mean model fluxes type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! ------------------------------------------------------------------------------------------------------ ! * operator splitting @@ -368,10 +367,6 @@ subroutine opSplittin(& call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - ! allocate space for the mean flux variable structure - call allocLocal(flux_meta(:),flux_mean,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - ! allocate space for the derivative structure call allocLocal(deriv_meta(:),deriv_data,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if @@ -385,8 +380,7 @@ subroutine opSplittin(& do iVar=1,size(flux_meta) ! loop through fluxes if(flux2state_orig(iVar)%state1==integerMissing .and. flux2state_orig(iVar)%state2==integerMissing) cycle ! flux does not depend on state (e.g., input) if(flux2state_orig(iVar)%state1==iname_watCanopy .and. .not.computeVegFlux) cycle ! use input fluxes in cases where there is no canopy - if (firstInnerStep) flux_data%var(iVar)%dat(:) = 0._rkind - flux_mean%var(iVar)%dat(:) = 0._rkind + flux_data%var(iVar)%dat(:) = 0._rkind end do ! initialize derivatives @@ -703,7 +697,6 @@ subroutine opSplittin(& prog_data, & ! intent(inout) : model prognostic variables for a local HRU diag_data, & ! intent(inout) : model diagnostic variables for a local HRU flux_data, & ! intent(inout) : model fluxes for a local HRU - flux_mean, & ! intent(inout) : mean model fluxes for a local HRU deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables bvar_data, & ! intent(in) : model variables for the local basin ! output: control diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index fc6b40946..317e8e56f 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -114,8 +114,7 @@ subroutine varSubstep(& indx_data, & ! intent(inout) : index data prog_data, & ! intent(inout) : model prognostic variables for a local HRU diag_data, & ! intent(inout) : model diagnostic variables for a local HRU - flux_data, & ! intent(inout) : model fluxes for a local HRU - flux_mean, & ! intent(inout) : mean model fluxes for a local HRU + flux_data, & ! intent(inout) : model fluxes for a local HRU deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables bvar_data, & ! intent(in) : model variables for the local basin ! output: model control @@ -165,7 +164,6 @@ subroutine varSubstep(& type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: flux_mean ! mean model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin ! output: model control @@ -533,7 +531,7 @@ subroutine varSubstep(& ! ** no domain splitting if(count(ixLayerActive/=integerMissing)==nLayers)then - flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght + flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght fluxCount%var(iVar)%dat(:) = fluxCount%var(iVar)%dat(:) + 1 ! ** domain splitting @@ -545,10 +543,10 @@ subroutine varSubstep(& ! Makes Jacobian more diagonal if do this, but less correct ! special case of the transpiration sink from soil layers: only computed for the top soil layer !if(iVar==iLookFlux%mLayerTranspire)then - ! if(ixLayer==1) flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght + ! if(ixLayer==1) flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght ! standard case !else - flux_mean%var(iVar)%dat(ixLayer) = flux_mean%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght + flux_data%var(iVar)%dat(ixLayer) = flux_data%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght !endif fluxCount%var(iVar)%dat(ixLayer) = fluxCount%var(iVar)%dat(ixLayer) + 1 endif @@ -576,11 +574,6 @@ subroutine varSubstep(& end do substeps ! time steps for variable-dependent sub-stepping ! NOTE: if we get to here then we are accepting then dtSum should dt - ! save the fluxes as averages - do iVar=1,size(flux_meta) - if(count(fluxMask%var(iVar)%dat)>0) flux_data%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) - enddo - ! save the energy fluxes as averages flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt ! canopy evaporation/condensation (kg m-2 s-1) flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) From a533c20067eea6cd4696825ab6193bf95eb1ade8 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 6 Mar 2023 23:46:36 +0900 Subject: [PATCH 0572/1472] Revert "undoing flux_mean collection in opSplittin, don't think necessary" This reverts commit f5a3ec7ffff3656584ce29a81bf64c480341be54. --- build/source/engine/coupled_em.f90 | 2 +- build/source/engine/opSplittin.f90 | 9 ++++++++- build/source/engine/varSubstep.f90 | 15 +++++++++++---- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 9f06d06b5..9688c3350 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -940,7 +940,7 @@ subroutine coupled_em(& end if ! (if computing the vegetation flux) call computSnowDepth(& - whole_step, & ! intent(in) + whole_step, & ! intent(in) nSnow, & ! intent(in) scalarSnowSublimation, & ! intent(in) mLayerVolFracLiq, & ! intent(inout) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 760c642a7..3b8cdc70d 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -236,6 +236,7 @@ subroutine opSplittin(& type(var_dlength) :: prog_temp ! temporary model prognostic variables type(var_dlength) :: diag_temp ! temporary model diagnostic variables type(var_dlength) :: flux_temp ! temporary model fluxes + type(var_dlength) :: flux_mean ! mean model fluxes type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! ------------------------------------------------------------------------------------------------------ ! * operator splitting @@ -367,6 +368,10 @@ subroutine opSplittin(& call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + ! allocate space for the mean flux variable structure + call allocLocal(flux_meta(:),flux_mean,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + ! allocate space for the derivative structure call allocLocal(deriv_meta(:),deriv_data,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if @@ -380,7 +385,8 @@ subroutine opSplittin(& do iVar=1,size(flux_meta) ! loop through fluxes if(flux2state_orig(iVar)%state1==integerMissing .and. flux2state_orig(iVar)%state2==integerMissing) cycle ! flux does not depend on state (e.g., input) if(flux2state_orig(iVar)%state1==iname_watCanopy .and. .not.computeVegFlux) cycle ! use input fluxes in cases where there is no canopy - flux_data%var(iVar)%dat(:) = 0._rkind + if (firstInnerStep) flux_data%var(iVar)%dat(:) = 0._rkind + flux_mean%var(iVar)%dat(:) = 0._rkind end do ! initialize derivatives @@ -697,6 +703,7 @@ subroutine opSplittin(& prog_data, & ! intent(inout) : model prognostic variables for a local HRU diag_data, & ! intent(inout) : model diagnostic variables for a local HRU flux_data, & ! intent(inout) : model fluxes for a local HRU + flux_mean, & ! intent(inout) : mean model fluxes for a local HRU deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables bvar_data, & ! intent(in) : model variables for the local basin ! output: control diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 317e8e56f..fc6b40946 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -114,7 +114,8 @@ subroutine varSubstep(& indx_data, & ! intent(inout) : index data prog_data, & ! intent(inout) : model prognostic variables for a local HRU diag_data, & ! intent(inout) : model diagnostic variables for a local HRU - flux_data, & ! intent(inout) : model fluxes for a local HRU + flux_data, & ! intent(inout) : model fluxes for a local HRU + flux_mean, & ! intent(inout) : mean model fluxes for a local HRU deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables bvar_data, & ! intent(in) : model variables for the local basin ! output: model control @@ -164,6 +165,7 @@ subroutine varSubstep(& type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: flux_mean ! mean model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin ! output: model control @@ -531,7 +533,7 @@ subroutine varSubstep(& ! ** no domain splitting if(count(ixLayerActive/=integerMissing)==nLayers)then - flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght + flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght fluxCount%var(iVar)%dat(:) = fluxCount%var(iVar)%dat(:) + 1 ! ** domain splitting @@ -543,10 +545,10 @@ subroutine varSubstep(& ! Makes Jacobian more diagonal if do this, but less correct ! special case of the transpiration sink from soil layers: only computed for the top soil layer !if(iVar==iLookFlux%mLayerTranspire)then - ! if(ixLayer==1) flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght + ! if(ixLayer==1) flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght ! standard case !else - flux_data%var(iVar)%dat(ixLayer) = flux_data%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght + flux_mean%var(iVar)%dat(ixLayer) = flux_mean%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght !endif fluxCount%var(iVar)%dat(ixLayer) = fluxCount%var(iVar)%dat(ixLayer) + 1 endif @@ -574,6 +576,11 @@ subroutine varSubstep(& end do substeps ! time steps for variable-dependent sub-stepping ! NOTE: if we get to here then we are accepting then dtSum should dt + ! save the fluxes as averages + do iVar=1,size(flux_meta) + if(count(fluxMask%var(iVar)%dat)>0) flux_data%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + enddo + ! save the energy fluxes as averages flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt ! canopy evaporation/condensation (kg m-2 s-1) flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) From 96ca717beed738d33d745d6240fbc07fff4efc9a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 7 Mar 2023 13:12:37 +0900 Subject: [PATCH 0573/1472] gathering sublimation to be applied over the whole_step --- build/source/engine/coupled_em.f90 | 125 +++++++++++++++++------------ 1 file changed, 74 insertions(+), 51 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 9688c3350..9a42dadee 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -226,6 +226,14 @@ subroutine coupled_em(& integer(i4b) :: iSoil ! index of soil layers type(var_dlength) :: flux_mean ! timestep-average model fluxes for a local HRU real(rkind) :: meanSoilCompress ! timestep-average soil compression + ! sublimation sums over substep and means over data_step + real(rkind) :: sumCanopySublimation ! sum of sublimation from the vegetation canopy (kg m-2 s-1) over substep + real(rkind) :: sumSnowSublimation ! sum of sublimation from the snow surface (kg m-2 s-1) over substep + real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) over substep + real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) over substep + real(rkind) :: meanCanopySublimation ! mean sublimation from the vegetation canopy (kg m-2 s-1) over data_step + real(rkind) :: meanLatHeatCanopyEvap ! mean latent heat flux for evaporation from the canopy to the canopy air space (W m-2) over data_step + real(rkind) :: meanSenHeatCanopy ! mean sensible heat flux from the canopy to the canopy air space (W m-2) over data_step ! balance checks integer(i4b) :: iVar ! loop through model variables real(rkind) :: balanceSoilCompress ! total soil compression (kg m-2) @@ -330,12 +338,16 @@ subroutine coupled_em(& ! initialize surface melt pond sfcMeltPond = 0._rkind ! change in storage associated with the surface melt pond (kg m-2) - ! initialize fluxes to average over whole step (averaged over substep in varSubStep) + ! initialize fluxes to average over maxstep (averaged over substep in varSubStep) do iVar=1,size(averageFlux_meta) flux_mean%var(iVar)%dat(:) = 0._rkind end do - ! initialize the compression to average over whole step (averaged over substep in varSubStep) - meanSoilCompress = 0._rkind ! total soil compression + ! initialize the compression to average over data_step (averaged over substep in varSubStep) + meanSoilCompress = 0._rkind ! mean total soil compression + ! initialize the canopy sublimation to average over data_step (averaged over substep in varSubStep) + meanCanopySublimation = 0._rkind ! mean sublimation from the vegetation canopy (kg m-2 s-1) over data_step + meanLatHeatCanopyEvap = 0._rkind ! mean latent heat flux for evaporation from the canopy to the canopy air space (W m-2) over data_step + meanSenHeatCanopy = 0._rkind ! mean sensible heat flux from the canopy to the canopy air space (W m-2) over data_step ! associate local variables with information in the data structures associate(& @@ -403,7 +415,6 @@ subroutine coupled_em(& ! save SWE oldSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) - ! *** compute phenology... ! ------------------------ @@ -799,6 +810,12 @@ subroutine coupled_em(& whole_step = maxstep if(dt_sub < maxstep_op) whole_step = dt_sub ! only happens if fails a step in the maxstep + ! initialize sublimation sums to average over whole_step + sumCanopySublimation = 0._rkind + sumSnowSublimation = 0._rkind + sumLatHeatCanopyEvap = 0._rkind + sumSenHeatCanopy = 0._rkind + endif ! (do_outer loop) ! *** solve model equations... @@ -867,6 +884,12 @@ subroutine coupled_em(& cycle substeps endif + ! increment sublimation sums + sumCanopySublimation = sumCanopySublimation + dt_sub*flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) + sumSnowSublimation = sumSnowSublimation + dt_sub*flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) + sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dt_sub*flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) + sumSenHeatCanopy = sumSenHeatCanopy + dt_sub*flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) + ! update first step and first and last inner steps firstSubStep = .false. firstInnerStep = .false. @@ -881,25 +904,19 @@ subroutine coupled_em(& if(do_outer)then ! *** remove ice due to sublimation and freeze calculations... - ! NOTE: In the future this should be moved into the solver, makes a big difference, and here only applying last substep amount to whole thing + ! NOTE: In the future this should be moved into the solver, makes a big difference ! -------------------------------------------------------------- sublime: associate(& - mLayerMeltFreeze => diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat, & ! melt-freeze in each snow and soil layer (kg m-3) - scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1), & ! sublimation from the vegetation canopy (kg m-2 s-1) - scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! sublimation from the snow surface (kg m-2 s-1) - scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1), & ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1), & ! sensible heat flux from the canopy to the canopy air space (W m-2) - scalarLatHeatGround => flux_data%var(iLookFLUX%scalarLatHeatGround)%dat(1), & ! latent heat flux from ground surface below vegetation (W m-2) - scalarSenHeatGround => flux_data%var(iLookFLUX%scalarSenHeatGround)%dat(1), & ! sensible heat flux from ground surface below vegetation (W m-2) + mLayerMeltFreeze => diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat, & ! melt-freeze in each snow and soil layer (kg m-3) scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1), & ! liquid water stored on the vegetation canopy (kg m-2) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! ice stored on the vegetation canopy (kg m-2) scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1), & ! canopy ice content (kg m-2) mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat, & ! volumetric fraction of ice in the snow+soil domain (-) mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat, & ! volumetric fraction of liquid water in the snow+soil domain (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat, & ! volumetric fraction of total water (-) mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat & ! depth of each snow+soil layer (m) ) ! associations to variables in data structures - ! compute the melt in each snow and soil layer if(nSnow>0)& mLayerMeltFreeze(1:nSnow) = -( mLayerVolFracIce(1:nSnow) - mLayerVolFracIceInit(1:nSnow) ) * iden_ice @@ -914,7 +931,7 @@ subroutine coupled_em(& if(computeVegFlux)then ! remove mass of ice on the canopy - scalarCanopyIce = scalarCanopyIce + scalarCanopySublimation*whole_step + scalarCanopyIce = scalarCanopyIce + sumCanopySublimation ! if removed all ice, take the remaining sublimation from water if(scalarCanopyIce < 0._rkind)then @@ -922,16 +939,16 @@ subroutine coupled_em(& scalarCanopyIce = 0._rkind endif - ! modify fluxes if there is insufficient canopy water to support the converged sublimation rate over the whole time step + ! modify fluxes and mean fluxes if there is insufficient canopy water to support the converged sublimation rate over the whole time step if(scalarCanopyLiq < 0._rkind)then ! --> superfluous sublimation flux superflousSub = -scalarCanopyLiq/whole_step ! kg m-2 s-1 superflousNrg = superflousSub*LH_sub ! W m-2 (J m-2 s-1) ! --> update fluxes and states - scalarCanopySublimation = scalarCanopySublimation + superflousSub - scalarLatHeatCanopyEvap = scalarLatHeatCanopyEvap + superflousNrg - scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg - scalarCanopyLiq = 0._rkind + sumCanopySublimation = sumCanopySublimation + superflousSub*whole_step + sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + superflousNrg*whole_step + sumSenHeatCanopy = sumSenHeatCanopy - superflousNrg*whole_step + scalarCanopyLiq = 0._rkind endif ! update water @@ -940,17 +957,17 @@ subroutine coupled_em(& end if ! (if computing the vegetation flux) call computSnowDepth(& - whole_step, & ! intent(in) - nSnow, & ! intent(in) - scalarSnowSublimation, & ! intent(in) - mLayerVolFracLiq, & ! intent(inout) - mLayerVolFracIce, & ! intent(inout) - prog_data%var(iLookPROG%mLayerTemp)%dat, & ! intent(in) - diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat, & ! intent(in) - mpar_data, & ! intent(in) + whole_step, & ! intent(in) + nSnow, & ! intent(in) + sumSnowSublimation/whole_step, & ! intent(in) + mLayerVolFracLiq, & ! intent(inout) + mLayerVolFracIce, & ! intent(inout) + prog_data%var(iLookPROG%mLayerTemp)%dat, & ! intent(in) + mLayerMeltFreeze, & ! intent(in) + mpar_data, & ! intent(in) ! output - tooMuchSublim, & ! intent(out): flag to denote that there was too much sublimation in a given time step - mLayerDepth, & ! intent(inout) + tooMuchSublim, & ! intent(out): flag to denote that there was too much sublimation in a given time step + mLayerDepth, & ! intent(inout) ! error control err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if @@ -981,26 +998,29 @@ subroutine coupled_em(& cycle substeps endif - end associate sublime + ! update coordinate variables + call calcHeight(& + ! input/output: data structures + indx_data, & ! intent(in): layer type + prog_data, & ! intent(inout): model variables for a local HRU + ! output: error control + err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - ! update coordinate variables - call calcHeight(& - ! input/output: data structures - indx_data, & ! intent(in): layer type - prog_data, & ! intent(inout): model variables for a local HRU - ! output: error control - err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - - ! recompute snow depth, SWE, and layer water - if(nSnow > 0)then - prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow)) - prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow)*iden_water + & - prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice) & - * prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) - prog_data%var(iLookPROG%mLayerVolFracWat)%dat(1:nSnow) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow) & - + prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice/iden_water - endif + ! increment mean canopy sublimation + meanCanopySublimation = meanCanopySublimation + sumCanopySublimation/whole_step + meanLatHeatCanopyEvap = meanLatHeatCanopyEvap + sumLatHeatCanopyEvap/whole_step + meanSenHeatCanopy = meanSenHeatCanopy + sumSenHeatCanopy/whole_step + + ! recompute snow depth, SWE, and layer water + if(nSnow > 0)then + prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( mLayerDepth(1:nSnow) ) + prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (mLayerVolFracLiq(1:nSnow)*iden_water & + + mLayerVolFracIce(1:nSnow)*iden_ice) * mLayerDepth(1:nSnow) ) + mLayerVolFracWat(1:nSnow) = mLayerVolFracLiq(1:nSnow) + mLayerVolFracIce(1:nSnow)*iden_ice/iden_water + endif + + end associate sublime ! increment change in storage associated with the surface melt pond (kg m-2) if(nSnow==0) sfcMeltPond = sfcMeltPond + prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) @@ -1016,13 +1036,16 @@ subroutine coupled_em(& ! *** END MAIN SOLVER ******************************************************************************** ! **************************************************************************************************** - ! increment fluxes and soil compression + ! increment mean fluxes, soil compression, and canopy sublimation dt_wght = dt_sub/data_step ! define weight applied to each sub-step do iVar=1,size(averageFlux_meta) - flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:)*dt_wght + flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:)*dt_wght end do - meanSoilCompress = meanSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1)*dt_wght ! total soil compression + meanSoilCompress = meanSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1)*dt_wght flux_mean%var(childFLUX_MEAN(iLookDIAG%scalarSoilCompress))%dat(1) = meanSoilCompress + flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySublimation))%dat(1) = meanCanopySublimation ! these two will be equal unless insufficient canopy water for sublim + flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarLatHeatCanopyEvap))%dat(1) = meanLatHeatCanopyEvap ! these two will be equal unless insufficient canopy water for sublim + flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSenHeatCanopy))%dat(1) = meanSenHeatCanopy ! these two will be equal unless insufficient canopy water for sublim ! increment sub-step dt_solv = dt_solv + dt_sub From 2069abfa6538a380f496de44aa67836e8c1fbe5c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 7 Mar 2023 15:08:16 +0900 Subject: [PATCH 0574/1472] sundials compression had an extra multiplier of dt_last, and checkMassBalance in all solutions in coupled_em, we are collecting average timestep fluxes --- build/source/engine/coupled_em.f90 | 16 +++------------- build/source/engine/summaSolveSundialsIDA.f90 | 4 ++-- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 9a42dadee..e97ca54f9 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -64,11 +64,6 @@ module coupled_em_module USE globalData,only:model_decisions ! model decision structure USE globalData,only:globalPrintFlag ! the global print flag -! look-up values for the numerical method -USE mDecisions_module,only: & - bEuler, & ! home-grown backward Euler solution with long time steps - sundials ! SUNDIALS/IDA solution - ! look-up values for the maximum interception capacity USE mDecisions_module,only: & stickySnow, & ! maximum interception capacity an increasing function of temerature @@ -395,8 +390,8 @@ subroutine coupled_em(& ! short-cut to the algorithmic control parameters ! NOTE - temporary assignment of minstep to foce something reasonable - ! change maxstep with hard code here to make the outer and inner loop computations here in coupled_em happen more frequently for num_method = bEuler or sundials - ! change maxstep_op with hard code here to make the inner loop computations in opSplittin happen more frequently for num_method = bEuler or sundials + ! change maxstep with hard code here to make the outer and inner loop computations here in coupled_em happen more frequently + ! change maxstep_op with hard code here to make the inner loop computations in opSplittin happen more frequently minstep = 10._rkind ! mpar_data%var(iLookPARAM%minstep)%dat(1) ! minimum time step (s) maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) maxstep_op = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) to run opSplittin over @@ -1168,12 +1163,7 @@ subroutine coupled_em(& ! ----- ! * balance checks for the canopy... ! ---------------------------------- - - if (model_decisions(iLookDECISIONS%num_method)%iDecision==bEuler)then ! convergence criteria for bEuler - if (maxstep_op == maxstep) checkMassBalance = .true. - if (maxstep_op < maxstep) checkMassBalance = .false. ! cannot check since not saving fluxes on inner loops - endif - if (model_decisions(iLookDECISIONS%num_method)%iDecision==sundials) checkMassBalance = .false. ! cannot check since not saving fluxes on inner loops + checkMassBalance = .true. ! if computing the vegetation flux if(computeVegFlux)then diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index f4353eb60..e9de5ac3c 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -506,9 +506,9 @@ subroutine summaSolveSundialsIDA( & flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * dt_last(1) end do - ! sum of mLayerCmpress + ! sum of mLayerCmpress over the time step, since using prev value not * dt_last(1) mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & - * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) * dt_last(1) + * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) ! save required quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial From c6b30bb4b4d94a3122b63f597a5a08ef246f5680 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 7 Mar 2023 16:22:16 +0900 Subject: [PATCH 0575/1472] compressibility in the sundials solver should be collected with the change in the entire matric head, not just liquid --- build/source/engine/summaSolveSundialsIDA.f90 | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index e9de5ac3c..9e74777a0 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -213,7 +213,6 @@ subroutine summaSolveSundialsIDA( & integer(kind = 8) :: mu, lu ! in banded matrix mode integer(i4b) :: iVar logical(lgt) :: startQuadrature - real(rkind) :: mLayerMatricHeadLiqPrev(nSoil) real(qp) :: h_init integer(c_long) :: nState ! total number of state variables real(rkind) :: rVec(nStat) @@ -398,7 +397,6 @@ subroutine summaSolveSundialsIDA( & eqns_data%scalarCanopyLiqPrev = prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) - mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) @@ -508,7 +506,7 @@ subroutine summaSolveSundialsIDA( & ! sum of mLayerCmpress over the time step, since using prev value not * dt_last(1) mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & - * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) + * ( eqns_data%mLayerMatricHeadTrial(:) - eqns_data%mLayerMatricHeadPrev(:) ) ! save required quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial @@ -516,7 +514,6 @@ subroutine summaSolveSundialsIDA( & eqns_data%scalarCanopyLiqPrev = eqns_data%scalarCanopyLiqTrial eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) - mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) From 7537dee2443825d9b2bdf257e4f4f7712f813a79 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 8 Mar 2023 10:19:57 +0900 Subject: [PATCH 0576/1472] some comments and correct dt_solv for step reset --- build/source/engine/coupled_em.f90 | 19 +++++++------------ build/source/engine/volicePack.f90 | 14 +++++++------- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index e97ca54f9..12a9684cf 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -139,7 +139,6 @@ subroutine coupled_em(& USE time_utils_module,only:elapsedSec ! calculate the elapsed time ! additional subroutines USE tempAdjust_module,only:tempAdjust ! adjust snow temperature associated with new snowfall - USE snwDensify_module,only:snwDensify ! snow densification (compaction and cavitation) USE var_derive_module,only:calcHeight ! module to calculate height at layer interfaces and layer mid-point USE computSnowDepth_module,only:computSnowDepth @@ -769,14 +768,14 @@ subroutine coupled_em(& theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) ! state variables in the vegetation canopy - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(out): [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(out): [dp] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp] mass of liquid water on the vegetation canopy (kg m-2) scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(out): [dp] mass of total water on the vegetation canopy (kg m-2) ! state variables in the snow and soil domains - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(out): [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(out): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(out): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(out): [dp(:)] matric head (m) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] matric head (m) mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat & ! intent(out): [dp(:)] matric potential of liquid water (m) ) ! associations to variables in data structures @@ -873,7 +872,6 @@ subroutine coupled_em(& err=20; return endif ! try again, restart step - dt_solv = dt_solv - dt_solvInner dt_solvInner = 0._rkind deallocate(mLayerVolFracIceInit) cycle substeps @@ -890,9 +888,6 @@ subroutine coupled_em(& firstInnerStep = .false. if(dt_solvInner + dt_sub >= maxstep) lastInnerStep = .true. - ! increment sub-step - dt_solvInner = dt_solvInner + dt_sub - ! check if on outer loop, always do outer if after failed step and on small step do_outer = .true. if( dt_sub == maxstep_op .and. .not.lastInnerStep ) do_outer = .false. @@ -902,7 +897,7 @@ subroutine coupled_em(& ! NOTE: In the future this should be moved into the solver, makes a big difference ! -------------------------------------------------------------- sublime: associate(& - mLayerMeltFreeze => diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat, & ! melt-freeze in each snow and soil layer (kg m-3) + mLayerMeltFreeze => diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat, & ! melt-freeze in each snow and soil layer (kg m-3) scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1), & ! liquid water stored on the vegetation canopy (kg m-2) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! ice stored on the vegetation canopy (kg m-2) scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1), & ! canopy ice content (kg m-2) @@ -988,7 +983,6 @@ subroutine coupled_em(& err=20; return endif ! try again, restart step (at end inner step) - dt_solv = 0._rkind dt_solvInner = 0._rkind cycle substeps endif @@ -1043,6 +1037,7 @@ subroutine coupled_em(& flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSenHeatCanopy))%dat(1) = meanSenHeatCanopy ! these two will be equal unless insufficient canopy water for sublim ! increment sub-step + dt_solvInner = dt_solvInner + dt_sub dt_solv = dt_solv + dt_sub ! save the time step to initialize the subsequent step diff --git a/build/source/engine/volicePack.f90 b/build/source/engine/volicePack.f90 index 8205b84c2..5006c888c 100644 --- a/build/source/engine/volicePack.f90 +++ b/build/source/engine/volicePack.f90 @@ -159,10 +159,10 @@ subroutine newsnwfall(& ! input/output: state variables scalarSWE, & ! SWE (kg m-2) scalarSnowDepth, & ! total snow depth (m) - surfaceLayerTemp, & ! temperature of each layer (K) - surfaceLayerDepth, & ! depth of each layer (m) - surfaceLayerVolFracIce, & ! volumetric fraction of ice in each layer (-) - surfaceLayerVolFracLiq, & ! volumetric fraction of liquid water in each layer (-) + surfaceLayerTemp, & ! temperature of surface layer (K) + surfaceLayerDepth, & ! depth of surface layer (m) + surfaceLayerVolFracIce, & ! volumetric fraction of ice in surface layer (-) + surfaceLayerVolFracLiq, & ! volumetric fraction of liquid water in surface layer (-) ! output: error control err,message ) ! error control ! computational modules @@ -181,10 +181,10 @@ subroutine newsnwfall(& ! input/output: state variables real(rkind),intent(inout) :: scalarSWE ! SWE (kg m-2) real(rkind),intent(inout) :: scalarSnowDepth ! total snow depth (m) - real(rkind),intent(inout) :: surfaceLayerTemp ! temperature of each layer (K) + real(rkind),intent(inout) :: surfaceLayerTemp ! temperature of surface layer (K) real(rkind),intent(inout) :: surfaceLayerDepth ! depth of each layer (m) - real(rkind),intent(inout) :: surfaceLayerVolFracIce ! volumetric fraction of ice in each layer (-) - real(rkind),intent(inout) :: surfaceLayerVolFracLiq ! volumetric fraction of liquid water in each layer (-) + real(rkind),intent(inout) :: surfaceLayerVolFracIce ! volumetric fraction of ice in surface layer (-) + real(rkind),intent(inout) :: surfaceLayerVolFracLiq ! volumetric fraction of liquid water in surface layer (-) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message From ac0ca05cfb5cf824a0dba8fae9a50d8d16495a53 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 8 Mar 2023 14:19:23 +0900 Subject: [PATCH 0577/1472] To mimic BE varSubsteps, when fail should reduce whole_step (what running varStep over and what doing the outer loop on) and not dt_sub --- build/source/engine/coupled_em.f90 | 126 +++++++++++++++-------------- 1 file changed, 67 insertions(+), 59 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 12a9684cf..167efebe1 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -170,7 +170,7 @@ subroutine coupled_em(& integer(i4b) :: nSoil ! number of soil layers integer(i4b) :: nLayers ! total number of layers integer(i4b) :: nState ! total number of state variables - real(rkind) :: dtSave ! length of last input model sub-step (seconds) + real(rkind) :: dtSave ! length of last input model whole sub-step (seconds) real(rkind) :: dt_sub ! length of model sub-step (seconds) real(rkind) :: dt_wght ! weight applied to model sub-step (dt_sub/data_step) real(rkind) :: dt_solv ! seconds in the data step that have been completed @@ -219,15 +219,14 @@ subroutine coupled_em(& ! energy fluxes integer(i4b) :: iSoil ! index of soil layers type(var_dlength) :: flux_mean ! timestep-average model fluxes for a local HRU + type(var_dlength) :: flux_inner ! inner step average model fluxes for a local HRU real(rkind) :: meanSoilCompress ! timestep-average soil compression + real(rkind) :: innerSoilCompress ! inner step average soil compression ! sublimation sums over substep and means over data_step real(rkind) :: sumCanopySublimation ! sum of sublimation from the vegetation canopy (kg m-2 s-1) over substep real(rkind) :: sumSnowSublimation ! sum of sublimation from the snow surface (kg m-2 s-1) over substep real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) over substep real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) over substep - real(rkind) :: meanCanopySublimation ! mean sublimation from the vegetation canopy (kg m-2 s-1) over data_step - real(rkind) :: meanLatHeatCanopyEvap ! mean latent heat flux for evaporation from the canopy to the canopy air space (W m-2) over data_step - real(rkind) :: meanSenHeatCanopy ! mean sensible heat flux from the canopy to the canopy air space (W m-2) over data_step ! balance checks integer(i4b) :: iVar ! loop through model variables real(rkind) :: balanceSoilCompress ! total soil compression (kg m-2) @@ -328,20 +327,23 @@ subroutine coupled_em(& ! allocate space for the local fluxes call allocLocal(averageFlux_meta(:)%var_info,flux_mean,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + call allocLocal(averageFlux_meta(:)%var_info,flux_inner,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + ! initialize surface melt pond sfcMeltPond = 0._rkind ! change in storage associated with the surface melt pond (kg m-2) - ! initialize fluxes to average over maxstep (averaged over substep in varSubStep) + ! initialize fluxes to average over data_step (averaged over substep in varSubStep) do iVar=1,size(averageFlux_meta) flux_mean%var(iVar)%dat(:) = 0._rkind end do - ! initialize the compression to average over data_step (averaged over substep in varSubStep) meanSoilCompress = 0._rkind ! mean total soil compression - ! initialize the canopy sublimation to average over data_step (averaged over substep in varSubStep) - meanCanopySublimation = 0._rkind ! mean sublimation from the vegetation canopy (kg m-2 s-1) over data_step - meanLatHeatCanopyEvap = 0._rkind ! mean latent heat flux for evaporation from the canopy to the canopy air space (W m-2) over data_step - meanSenHeatCanopy = 0._rkind ! mean sensible heat flux from the canopy to the canopy air space (W m-2) over data_step + ! initialize fluxes to average over whole_step (averaged over substep in varSubStep) + do iVar=1,size(averageFlux_meta) + flux_inner%var(iVar)%dat(:) = 0._rkind + end do + innerSoilCompress = 0._rkind ! mean total soil compression ! associate local variables with information in the data structures associate(& @@ -394,7 +396,6 @@ subroutine coupled_em(& minstep = 10._rkind ! mpar_data%var(iLookPARAM%minstep)%dat(1) ! minimum time step (s) maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) maxstep_op = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) to run opSplittin over - if(maxstep_op > maxstep) maxstep_op = maxstep ! compute the number of layers with roots nLayersRoots = count(prog_data%var(iLookPROG%iLayerHeight)%dat(nSnow:nLayers-1) < mpar_data%var(iLookPARAM%rootingDepth)%dat(1)-verySmall) @@ -578,13 +579,13 @@ subroutine coupled_em(& ! *** MAIN SOLVER ************************************************************************************ ! **************************************************************************************************** - ! initialize the length of the sub-step - dt_solv = 0._rkind ! length of time step that has been completed (s) - dt_solvInner = 0._rkind ! length of time step that has been completed (s) in maxstep subStep - dt_init = min(data_step,maxstep,maxstep_op) ! initial substep length (s) - dt_sub = dt_init ! length of substep - dtSave = dt_init ! length of substep + ! initialize the length of the sub-step and counters whole_step = maxstep + dt_solv = 0._rkind ! length of time step that has been completed (s) + dt_solvInner = 0._rkind ! length of time step that has been completed (s) in whole_step subStep + dt_init = min(data_step,whole_step,maxstep_op) ! initial substep length (s) + dt_sub = dt_init + dtSave = whole_step ! length of whole substep ! initialize the number of sub-steps nsub=0 @@ -592,6 +593,8 @@ subroutine coupled_em(& ! loop through sub-steps substeps: do ! continuous do statement with exit clause (alternative to "while") + dt_sub = min(data_step,whole_step,maxstep_op,dt_sub) ! adjust for possible whole_step changes + ! print progress if(globalPrintFlag)then write(*,'(a,1x,4(f13.5,1x))') ' start of step: dt_init, dt_sub, dt_solv, data_step: ', dt_init, dt_sub, dt_solv, data_step @@ -617,10 +620,10 @@ subroutine coupled_em(& end do endif - ! check if on outer loop, always do outer if after failed step and on then on reduced timestep - do_outer = .true. + ! check if on outer loop, always do outer if after failed step and on then on reduced whole_step + do_outer = .false. if(stepFailure) firstInnerStep = .true. - if ( dt_sub == maxstep_op .and. .not.firstInnerStep ) do_outer = .false. + if(firstInnerStep) do_outer = .true. if(do_outer)then @@ -801,21 +804,26 @@ subroutine coupled_em(& end associate init - whole_step = maxstep - if(dt_sub < maxstep_op) whole_step = dt_sub ! only happens if fails a step in the maxstep + ! correct increments (if need to redo inner step) and reset increment + dt_solv = dt_solv - dt_solvInner + dt_solvInner = 0._rkind ! initialize sublimation sums to average over whole_step sumCanopySublimation = 0._rkind sumSnowSublimation = 0._rkind sumLatHeatCanopyEvap = 0._rkind sumSenHeatCanopy = 0._rkind + ! initialize fluxes to average over whole_step (averaged over substep in varSubStep) + do iVar=1,size(averageFlux_meta) + flux_inner%var(iVar)%dat(:) = 0._rkind + end do endif ! (do_outer loop) ! *** solve model equations... ! ---------------------------- ! save input step - dtSave = dt_sub + dtSave = whole_step ! get the new solution call opSplittin(& @@ -862,17 +870,16 @@ subroutine coupled_em(& ! handle special case of the step failure ! NOTE: need to revert back to the previous state vector that we were happy with and reduce the time step if(stepFailure)then - ! halve step - dt_sub = dtSave/2._rkind + ! halve whole_step, for more frequent outer loop updates + whole_step = dtSave/2._rkind ! check that the step is not tiny - if(dt_sub < minstep)then + if(whole_step < minstep)then print*,ixSolution - print*, 'dtSave, dt_sub', dtSave, dt_sub + print*, 'dtSave, dt_sub', dtSave, whole_step message=trim(message)//'length of the coupled step is below the minimum step length' err=20; return endif ! try again, restart step - dt_solvInner = 0._rkind deallocate(mLayerVolFracIceInit) cycle substeps endif @@ -886,11 +893,13 @@ subroutine coupled_em(& ! update first step and first and last inner steps firstSubStep = .false. firstInnerStep = .false. - if(dt_solvInner + dt_sub >= maxstep) lastInnerStep = .true. + if(dt_solvInner + dt_sub >= whole_step) lastInnerStep = .true. + if(dt_solv + dt_sub >= data_step-verySmall) lastInnerStep = .true. + + ! check if on outer loop + do_outer = .false. + if(lastInnerStep) do_outer = .true. - ! check if on outer loop, always do outer if after failed step and on small step - do_outer = .true. - if( dt_sub == maxstep_op .and. .not.lastInnerStep ) do_outer = .false. if(do_outer)then ! *** remove ice due to sublimation and freeze calculations... @@ -913,9 +922,6 @@ subroutine coupled_em(& mLayerMeltFreeze(nSnow+1:nLayers) = -(mLayerVolFracIce(nSnow+1:nLayers) - mLayerVolFracIceInit(nSnow+1:nLayers))*iden_water deallocate(mLayerVolFracIceInit) - whole_step = maxstep - if(dt_sub < maxstep_op) whole_step = dt_sub ! only happens if fails a step in the maxstep - ! * compute change in canopy ice content due to sublimation... ! ------------------------------------------------------------ if(computeVegFlux)then @@ -973,17 +979,16 @@ subroutine coupled_em(& ! handle special case of the step failure ! NOTE: need to revert back to the previous state vector that we were happy with and reduce the time step if(stepFailure)then - ! halve step - dt_sub = dtSave/2._rkind + ! halve whole_step, for more frequent outer loop updates + whole_step = dtSave/2._rkind ! check that the step is not tiny - if(dt_sub < minstep)then + if(whole_step < minstep)then print*,ixSolution - print*, 'dtSave, dt_sub', dtSave, dt_sub + print*, 'dtSave, dt_sub', dtSave, whole_step message=trim(message)//'length of the coupled step is below the minimum step length' err=20; return endif ! try again, restart step (at end inner step) - dt_solvInner = 0._rkind cycle substeps endif @@ -996,11 +1001,6 @@ subroutine coupled_em(& err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - ! increment mean canopy sublimation - meanCanopySublimation = meanCanopySublimation + sumCanopySublimation/whole_step - meanLatHeatCanopyEvap = meanLatHeatCanopyEvap + sumLatHeatCanopyEvap/whole_step - meanSenHeatCanopy = meanSenHeatCanopy + sumSenHeatCanopy/whole_step - ! recompute snow depth, SWE, and layer water if(nSnow > 0)then prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( mLayerDepth(1:nSnow) ) @@ -1014,32 +1014,40 @@ subroutine coupled_em(& ! increment change in storage associated with the surface melt pond (kg m-2) if(nSnow==0) sfcMeltPond = sfcMeltPond + prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) - ! update time increment and first and last inner steps - dt_solvInner = 0._rkind - firstInnerStep = .true. - lastInnerStep = .false. - endif ! (do_outer loop) ! **************************************************************************************************** ! *** END MAIN SOLVER ******************************************************************************** ! **************************************************************************************************** - ! increment mean fluxes, soil compression, and canopy sublimation - dt_wght = dt_sub/data_step ! define weight applied to each sub-step + ! increment mean fluxes, soil compression, and canopy sublimation, reset on whole_step + dt_wght = dt_sub/whole_step ! define weight applied to each sub-step do iVar=1,size(averageFlux_meta) - flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:)*dt_wght + flux_inner%var(iVar)%dat(:) = flux_inner%var(iVar)%dat(:) + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:)*dt_wght end do - meanSoilCompress = meanSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1)*dt_wght - flux_mean%var(childFLUX_MEAN(iLookDIAG%scalarSoilCompress))%dat(1) = meanSoilCompress - flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySublimation))%dat(1) = meanCanopySublimation ! these two will be equal unless insufficient canopy water for sublim - flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarLatHeatCanopyEvap))%dat(1) = meanLatHeatCanopyEvap ! these two will be equal unless insufficient canopy water for sublim - flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSenHeatCanopy))%dat(1) = meanSenHeatCanopy ! these two will be equal unless insufficient canopy water for sublim + innerSoilCompress = innerSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1)*dt_wght - ! increment sub-step + ! increment sub-step accepted step dt_solvInner = dt_solvInner + dt_sub dt_solv = dt_solv + dt_sub + ! update first and last inner steps if did successful lastInnerStep, increment fluxes over data_step + if (lastInnerStep)then + firstInnerStep = .true. + lastInnerStep = .false. + dt_solvInner = 0._rkind + + dt_wght = whole_step/data_step ! define weight applied to each sub-step + do iVar=1,size(averageFlux_meta) + flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_inner%var(averageFlux_meta(iVar)%ixParent)%dat(:)*dt_wght + end do + meanSoilCompress = meanSoilCompress + innerSoilCompress + flux_mean%var(childFLUX_MEAN(iLookDIAG%scalarSoilCompress))%dat(1) = meanSoilCompress + flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySublimation))%dat(1) = sumCanopySublimation/whole_step ! these two will be equal unless insufficient canopy water for sublim + flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarLatHeatCanopyEvap))%dat(1) = sumLatHeatCanopyEvap/whole_step ! these two will be equal unless insufficient canopy water for sublim + flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSenHeatCanopy))%dat(1) = sumSenHeatCanopy/whole_step ! these two will be equal unless insufficient canopy water for sublim + endif + ! save the time step to initialize the subsequent step if(dt_solv Date: Wed, 8 Mar 2023 14:32:42 +0900 Subject: [PATCH 0578/1472] flux_inner was added incorrectly --- build/source/engine/coupled_em.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 167efebe1..e7902f0cc 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1039,7 +1039,7 @@ subroutine coupled_em(& dt_wght = whole_step/data_step ! define weight applied to each sub-step do iVar=1,size(averageFlux_meta) - flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_inner%var(averageFlux_meta(iVar)%ixParent)%dat(:)*dt_wght + flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_inner%var(iVar)%dat(:)*dt_wght end do meanSoilCompress = meanSoilCompress + innerSoilCompress flux_mean%var(childFLUX_MEAN(iLookDIAG%scalarSoilCompress))%dat(1) = meanSoilCompress From 8efcc31a0e4ba00ca20d96ca20afa04de17b9f22 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 9 Mar 2023 10:42:44 +0900 Subject: [PATCH 0579/1472] need to save flux_mean incase fail a minimum step in splitting --- build/source/engine/opSplittin.f90 | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 3b8cdc70d..aa1f70765 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -237,6 +237,7 @@ subroutine opSplittin(& type(var_dlength) :: diag_temp ! temporary model diagnostic variables type(var_dlength) :: flux_temp ! temporary model fluxes type(var_dlength) :: flux_mean ! mean model fluxes + type(var_dlength) :: flux_mntemp ! temporary mean model fluxes type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! ------------------------------------------------------------------------------------------------------ ! * operator splitting @@ -372,6 +373,10 @@ subroutine opSplittin(& call allocLocal(flux_meta(:),flux_mean,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + ! allocate space for the temporary mean flux variable structure + call allocLocal(flux_meta(:),flux_mntemp,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + ! allocate space for the derivative structure call allocLocal(deriv_meta(:),deriv_data,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if @@ -661,11 +666,15 @@ subroutine opSplittin(& end select end do ! looping through variables - ! save/recover copies of model fluxes + ! save/recover copies of model fluxes and mean fluxes do iVar=1,size(flux_data%var) select case(failure) - case(.false.); flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) - case(.true.); flux_data%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) + case(.false.) + flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_mntemp%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + case(.true.) + flux_data%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) + flux_mean%var(iVar)%dat(:) = flux_mntemp%var(iVar)%dat(:) end select end do ! looping through variables From 8cb5c50f51c6b9953f0dd0c21d38b91f4008b911 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 14 Mar 2023 13:39:40 +0900 Subject: [PATCH 0580/1472] adding computeCm stuff to summa BE files to make computResidSundials and computResid the same --- this is all 0 at the moment so does not change any results --- build/source/engine/computResid.f90 | 39 ++++++++----- build/source/engine/computResidSundials.f90 | 64 ++++++++++----------- build/source/engine/eval8summa.f90 | 20 ++----- build/source/engine/eval8summaSundials.f90 | 28 ++++----- 4 files changed, 78 insertions(+), 73 deletions(-) diff --git a/build/source/engine/computResid.f90 b/build/source/engine/computResid.f90 index 83db2340d..5bc6805d7 100644 --- a/build/source/engine/computResid.f90 +++ b/build/source/engine/computResid.f90 @@ -86,13 +86,17 @@ subroutine computResid(& ! input: state variables (already disaggregated into scalars and vectors) scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) - scalarCanopyHydTrial, & ! intent(in): trial value of canopy water (kg m-2), either liquid water content or total water content + scalarCanopyWatTrial, & ! intent(in): trial value for the water on the vegetation canopy (kg m-2) mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) - mLayerVolFracHydTrial, & ! intent(in): trial vector of volumetric water content (-), either liquid water content or total water content scalarAquiferStorageTrial, & ! intent(in): trial value of storage of water in the aquifer (m) ! input: diagnostic variables defining the liquid water and ice content (function of state variables) scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value for the liq on the vegetation canopy (kg m-2) mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) + mLayerVolFracWatTrial, & ! intent(in): trial value for the volumetric water in each snow and soil layer (-) + mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liq in each snow and soil layer (-) + scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) + mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -115,13 +119,17 @@ subroutine computResid(& ! input: state variables (already disaggregated into scalars and vectors) real(rkind),intent(in) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind),intent(in) :: scalarCanopyHydTrial ! trial value for canopy water (kg m-2), either liquid water content or total water content + real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value for canopy total water content (kg m-2) real(rkind),intent(in) :: mLayerTempTrial(:) ! trial value for temperature of each snow/soil layer (K) - real(rkind),intent(in) :: mLayerVolFracHydTrial(:) ! trial vector of volumetric water content (-), either liquid water content or total water content real(rkind),intent(in) :: scalarAquiferStorageTrial ! trial value of aquifer storage (m) ! input: diagnostic variables defining the liquid water and ice content (function of state variables) real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: scalarCanopyLiqTrial ! trial value for the liq on the vegetation canopy (kg m-2) real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) + real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial value for the volumetric water in each snow and soil layer (-) + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for the volumetric water in each snow and soil layer (-) + real(qp),intent(in) :: scalarCanopyCmTrial ! Cm of vegetation canopy (-) + real(qp),intent(in) :: mLayerCmTrial(:) ! Cm of each snow and soil layer (-) ! input: data structures type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU @@ -137,7 +145,9 @@ subroutine computResid(& ! -------------------------------------------------------------------------------------------------------------------------------- integer(i4b) :: iLayer ! index of layer within the snow+soil domain integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) + real(rkind) :: scalarCanopyHydTrial ! trial value of canopy water content (kg m-2), either liquid water content or total water content real(rkind) :: scalarCanopyHyd ! canopy water content (kg m-2), either liquid water content or total water content + real(rkind),dimension(nLayers) :: mLayerVolFracHydTrial ! trial vector of volumetric water content (-), either liquid water content or total water content real(rkind),dimension(nLayers) :: mLayerVolFracHyd ! vector of volumetric water content (-), either liquid water content or total water content ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- @@ -223,19 +233,21 @@ subroutine computResid(& ! compute the residual vector for the vegetation canopy ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision ! --> energy balance - if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*scalarCanairTempTrial - ( (sMul(ixCasNrg)*scalarCanairTemp + fVec(ixCasNrg)*dt) + rAdd(ixCasNrg) ) - if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg)*scalarCanopyTempTrial - ( (sMul(ixVegNrg)*scalarCanopyTemp + fVec(ixVegNrg)*dt) + rAdd(ixVegNrg) ) - + if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*scalarCanairTempTrial - ( sMul(ixCasNrg)*scalarCanairTemp + fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg)*scalarCanopyTempTrial + scalarCanopyCmTrial * scalarCanopyWatTrial/canopyDepth & + - ( sMul(ixVegNrg)*scalarCanopyTemp + scalarCanopyCmTrial * scalarCanopyWat/canopyDepth + fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) ! --> mass balance if(ixVegHyd/=integerMissing)then - scalarCanopyHyd = merge(scalarCanopyWat, scalarCanopyLiq, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) - rVec(ixVegHyd) = sMul(ixVegHyd)*scalarCanopyHydTrial - ( (sMul(ixVegHyd)*scalarCanopyHyd + fVec(ixVegHyd)*dt) + rAdd(ixVegHyd) ) + scalarCanopyHydTrial = merge(scalarCanopyWatTrial, scalarCanopyLiqTrial, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) + scalarCanopyHyd = merge(scalarCanopyWat, scalarCanopyLiq, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) + rVec(ixVegHyd) = sMul(ixVegHyd)*scalarCanopyHydTrial - ( sMul(ixVegHyd)*scalarCanopyHyd + fVec(ixVegHyd)*dt + rAdd(ixVegHyd) ) endif ! compute the residual vector for the snow and soil sub-domains for energy if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) )*mLayerTempTrial(iLayer) - ( (sMul( ixSnowSoilNrg(iLayer) )*mLayerTemp(iLayer) + fVec( ixSnowSoilNrg(iLayer) )*dt) + rAdd( ixSnowSoilNrg(iLayer) ) ) + rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) )*mLayerTempTrial(iLayer) + mLayerCmTrial(iLayer) * mLayerVolFracWatTrial(iLayer) & + - ( sMul( ixSnowSoilNrg(iLayer) )*mLayerTemp(iLayer) + mLayerCmTrial(iLayer) * mLayerVolFracWat(iLayer) + fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) end do ! looping through non-missing energy state variables in the snow+soil domain endif @@ -244,14 +256,15 @@ subroutine computResid(& if(nSnowSoilHyd>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) ! (get the correct state variable) - mLayerVolFracHyd(iLayer) = merge(mLayerVolFracWat(iLayer), mLayerVolFracLiq(iLayer), (ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_matLayer) ) + mLayerVolFracHydTrial(iLayer) = merge(mLayerVolFracWatTrial(iLayer), mLayerVolFracLiqTrial(iLayer) , (ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_matLayer) ) + mLayerVolFracHyd(iLayer) = merge(mLayerVolFracWat(iLayer), mLayerVolFracLiq(iLayer), (ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_matLayer) ) ! (compute the residual) - rVec( ixSnowSoilHyd(iLayer) ) = mLayerVolFracHydTrial(iLayer) - ( (mLayerVolFracHyd(iLayer) + fVec( ixSnowSoilHyd(iLayer) )*dt) + rAdd( ixSnowSoilHyd(iLayer) ) ) + rVec( ixSnowSoilHyd(iLayer) ) = mLayerVolFracHydTrial(iLayer) - ( mLayerVolFracHyd(iLayer) + fVec( ixSnowSoilHyd(iLayer) )*dt + rAdd( ixSnowSoilHyd(iLayer) ) ) end do ! looping through non-missing energy state variables in the snow+soil domain endif ! compute the residual vector for the aquifer - if(ixAqWat/=integerMissing) rVec(ixAqWat) = sMul(ixAqWat)*scalarAquiferStorageTrial - ( (sMul(ixAqWat)*scalarAquiferStorage + fVec(ixAqWat)*dt) + rAdd(ixAqWat) ) + if(ixAqWat/=integerMissing) rVec(ixAqWat) = sMul(ixAqWat)*scalarAquiferStorageTrial - ( sMul(ixAqWat)*scalarAquiferStorage + fVec(ixAqWat)*dt + rAdd(ixAqWat) ) if(globalPrintFlag)then write(*,'(a,1x,100(e12.5,1x))') 'rVec = ', rVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) diff --git a/build/source/engine/computResidSundials.f90 b/build/source/engine/computResidSundials.f90 index cae6e3165..31aa7f35c 100644 --- a/build/source/engine/computResidSundials.f90 +++ b/build/source/engine/computResidSundials.f90 @@ -66,21 +66,21 @@ subroutine computResidSundials(& sMul, & ! intent(in): state vector multiplier (used in the residual calculations) fVec, & ! intent(in): flux vector ! input: state variables (already disaggregated into scalars and vectors) - scalarCanopyTempTrial, & ! intent(in): - mLayerTempTrial, & ! intent(in) - scalarCanairTempPrime, & ! intent(in): trial value for the temperature of the canopy air space (K) - scalarCanopyTempPrime, & ! intent(in): trial value for the temperature of the vegetation canopy (K) - scalarCanopyWatPrime, & ! - mLayerTempPrime, & ! intent(in): trial value for the temperature of each snow and soil layer (K) water content - scalarAquiferStoragePrime, & ! intent(in): trial value of storage of water in the aquifer (m) + scalarCanopyTempTrial, & ! intent(in):: trial value for the temperature of the vegetation canopy (K) + mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) + scalarCanairTempPrime, & ! intent(in): Prime value for the temperature of the canopy air space (K s-1) + scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K s-1) + scalarCanopyWatPrime, & ! intent(in): Prime value for the water on the vegetation canopy (kg m-2 s-1) + mLayerTempPrime, & ! intent(in): Prime value for the temperature of each snow and soil layer (K s-1) + scalarAquiferStoragePrime, & ! intent(in): Prime value of storage of water in the aquifer (m s-1) ! input: diagnostic variables defining the liquid water and ice content (function of state variables) - scalarCanopyIcePrime, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) - scalarCanopyLiqPrime, & ! intent(in): - mLayerVolFracIcePrime, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) - mLayerVolFracWatPrime, & - mLayerVolFracLiqPrime, & - scalarCanopyCmTrial, & - mLayerCmTrial, & ! intent(in) + scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2 s-1) + scalarCanopyLiqPrime, & ! intent(in): Prime value for the liq on the vegetation canopy (kg m-2 s-1) + mLayerVolFracIcePrime, & ! intent(in): Prime value for the volumetric ice in each snow and soil layer (s-1) + mLayerVolFracWatPrime, & ! intent(in): Prime value for the volumetric water in each snow and soil layer (s-1) + mLayerVolFracLiqPrime, & ! intent(in): Prime value for the volumetric liq in each snow and soil layer (s-1) + scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) + mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -101,21 +101,21 @@ subroutine computResidSundials(& real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(rkind),intent(in) :: fVec(:) ! flux vector ! input: state variables (already disaggregated into scalars and vectors) - real(rkind),intent(in) :: scalarCanopyTempTrial - real(rkind),intent(in) :: mLayerTempTrial(:) - real(rkind),intent(in) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) - real(rkind),intent(in) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) - real(rkind),intent(in) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) - real(rkind),intent(in) :: mLayerTempPrime(:) ! trial value for temperature of each snow/soil layer (K) content - real(rkind),intent(in) :: scalarAquiferStoragePrime ! trial value of aquifer storage (m) + real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind),intent(in) :: mLayerTempTrial(:) ! trial value for the temperature of each snow and soil layer (K) + real(rkind),intent(in) :: scalarCanairTempPrime ! Prime value for temperature of the canopy air space (K s-1) + real(rkind),intent(in) :: scalarCanopyTempPrime ! Prime value for temperature of the vegetation canopy (K s-1) + real(rkind),intent(in) :: scalarCanopyWatPrime ! Prime value for canopy total water content (kg m-2 s-1) + real(rkind),intent(in) :: mLayerTempPrime(:) ! Prime value for temperature of each snow/soil layer (K s-1) content + real(rkind),intent(in) :: scalarAquiferStoragePrime ! Prime value of aquifer storage (m s-1) ! input: diagnostic variables defining the liquid water and ice content (function of state variables) - real(rkind),intent(in) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: scalarCanopyLiqPrime - real(rkind),intent(in) :: mLayerVolFracIcePrime(:) ! trial value for volumetric fraction of ice (-) - real(rkind),intent(in) :: mLayerVolFracLiqPrime(:) - real(rkind),intent(in) :: mLayerVolFracWatPrime(:) - real(qp),intent(in) :: scalarCanopyCmTrial - real(qp),intent(in) :: mLayerCmTrial(:) + real(rkind),intent(in) :: scalarCanopyIcePrime ! Prime value for mass of ice on the vegetation canopy (kg m-2 s-1) + real(rkind),intent(in) :: scalarCanopyLiqPrime ! Prime value for the liq on the vegetation canopy (kg m-2 s-1) + real(rkind),intent(in) :: mLayerVolFracIcePrime(:) ! Prime value for volumetric fraction of ice (s-1) + real(rkind),intent(in) :: mLayerVolFracWatPrime(:) ! Prime value for the volumetric water in each snow and soil layer (s-1) + real(rkind),intent(in) :: mLayerVolFracLiqPrime(:) ! Prime value for the volumetric water in each snow and soil layer (s-1) + real(qp),intent(in) :: scalarCanopyCmTrial ! Cm of vegetation canopy (-) + real(qp),intent(in) :: mLayerCmTrial(:) ! Cm of each snow and soil layer (-) ! input: data structures type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU @@ -131,8 +131,8 @@ subroutine computResidSundials(& ! -------------------------------------------------------------------------------------------------------------------------------- integer(i4b) :: iLayer ! index of layer within the snow+soil domain integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) - real(rkind),dimension(nLayers) :: mLayerVolFracHydPrime ! vector of volumetric water content (-), either liquid water content or total water content real(rkind) :: scalarCanopyHydPrime ! trial value for canopy water (kg m-2), either liquid water content or total water content + real(rkind),dimension(nLayers) :: mLayerVolFracHydPrime ! vector of volumetric water content (-), either liquid water content or total water content ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! link to the necessary variables for the residual computations @@ -204,11 +204,11 @@ subroutine computResidSundials(& ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision ! --> energy balance if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*scalarCanairTempPrime - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) - if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime + scalarCanopyCmTrial * scalarCanopyWatPrime/canopyDepth - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime + scalarCanopyCmTrial * scalarCanopyWatPrime/canopyDepth - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) ! --> mass balance if(ixVegHyd/=integerMissing)then scalarCanopyHydPrime = merge(scalarCanopyWatPrime, scalarCanopyLiqPrime, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) - rVec(ixVegHyd) = sMul(ixVegHyd)*scalarCanopyHydPrime - ( fVec(ixVegHyd)*dt + rAdd(ixVegHyd) ) + rVec(ixVegHyd) = sMul(ixVegHyd)*scalarCanopyHydPrime - ( fVec(ixVegHyd)*dt + rAdd(ixVegHyd) ) endif ! compute the residual vector for the snow and soil sub-domains for energy @@ -223,7 +223,7 @@ subroutine computResidSundials(& if(nSnowSoilHyd>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) ! (get the correct state variable) - mLayerVolFracHydPrime(iLayer) = merge(mLayerVolFracWatPrime(iLayer), mLayerVolFracLiqPrime(iLayer), (ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_matLayer) ) + mLayerVolFracHydPrime(iLayer) = merge(mLayerVolFracWatPrime(iLayer), mLayerVolFracLiqPrime(iLayer), (ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_matLayer) ) ! (compute the residual) rVec( ixSnowSoilHyd(iLayer) ) = mLayerVolFracHydPrime(iLayer) - ( fVec( ixSnowSoilHyd(iLayer) )*dt + rAdd( ixSnowSoilHyd(iLayer) ) ) end do ! looping through non-missing energy state variables in the snow+soil domain diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 4cf2b0ac7..7e6b7711d 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -223,9 +223,7 @@ subroutine eval8summa(& integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) real(rkind) :: xMin,xMax ! minimum and maximum values for water content - real(rkind) :: scalarCanopyHydTrial ! trial value for mass of water on the vegetation canopy (kg m-2) real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) - real(rkind),dimension(nLayers) :: mLayerVolFracHydTrial ! trial value for volumetric fraction of water (-), general vector merged from Wat and Liq real(rkind),dimension(nState) :: rVecScaled ! scaled residual vector character(LEN=256) :: cmessage ! error message of downwind routine real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy @@ -682,16 +680,6 @@ subroutine eval8summa(& ! compute the total change in storage associated with compression of the soil matrix (kg m-2 s-1) scalarSoilCompress = sum(mLayerCompress(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water - ! vegetation domain: get the correct water states (total water, or liquid water, depending on the state type) - if(computeVegFlux)then - scalarCanopyHydTrial = merge(scalarCanopyWatTrial, scalarCanopyLiqTrial, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) - else - scalarCanopyHydTrial = realMissing - endif - - ! snow+soil domain: get the correct water states (total water, or liquid water, depending on the state type) - mLayerVolFracHydTrial = merge(mLayerVolFracWatTrial, mLayerVolFracLiqTrial, (ixHydType==iname_watLayer .or. ixHydType==iname_matLayer) ) - ! compute the residual vector call computResid(& ! input: model control @@ -705,13 +693,17 @@ subroutine eval8summa(& ! input: state variables (already disaggregated into scalars and vectors) scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) - scalarCanopyHydTrial, & ! intent(in): trial value of canopy hydrology state variable (kg m-2) + scalarCanopyWatTrial, & ! intent(in): trial value for the water on the vegetation canopy (kg m-2) mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) - mLayerVolFracHydTrial, & ! intent(in): trial vector of volumetric water content (-) scalarAquiferStorageTrial, & ! intent(in): trial value of storage of water in the aquifer (m) ! input: diagnostic variables defining the liquid water and ice content (function of state variables) scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value for the liq on the vegetation canopy (kg m-2) mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) + mLayerVolFracWatTrial, & ! intent(in): trial value for the volumetric water in each snow and soil layer (-) + mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liq in each snow and soil layer (-) + scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) + mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index 2797d065a..de94e3c77 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -769,21 +769,21 @@ subroutine eval8summaSundials(& sMul, & ! intent(in): state vector multiplier (used in the residual calculations) fluxVec, & ! intent(in): flux vector ! input: state variables (already disaggregated into scalars and vectors) - scalarCanopyTempTrial, & ! intent(in): - mLayerTempTrial, & ! intent(in) - scalarCanairTempPrime, & ! intent(in): Prime value for the temperature of the canopy air space (K) - scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K) - scalarCanopyWatPrime, & - mLayerTempPrime, & ! intent(in): Prime value for the temperature of each snow and soil layer (K) - scalarAquiferStoragePrime, & ! intent(in): Prime value of storage of water in the aquifer (m) + scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) + mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) + scalarCanairTempPrime, & ! intent(in): Prime value for the temperature of the canopy air space (K s-1) + scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K s-1) + scalarCanopyWatPrime, & ! intent(in): Prime value for the water on the vegetation canopy (kg m-2 s-1) + mLayerTempPrime, & ! intent(in): Prime value for the temperature of each snow and soil layer (K s-1) + scalarAquiferStoragePrime, & ! intent(in): Prime value of storage of water in the aquifer (m s-1) ! input: diagnostic variables defining the liquid water and ice content (function of state variables) - scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2) - scalarCanopyLiqPrime, & ! intent(in): - mLayerVolFracIcePrime, & ! intent(in): Prime value for the volumetric ice in each snow and soil layer (-) - mLayerVolFracWatPrime, & - mLayerVolFracLiqPrime, & - scalarCanopyCmTrial, & ! intent(in) Cm of vegetation canopy - mLayerCmTrial, & ! intent(in) Cm of soil and snow + scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2 s-1) + scalarCanopyLiqPrime, & ! intent(in): Prime value for the liq on the vegetation canopy (kg m-2 s-1) + mLayerVolFracIcePrime, & ! intent(in): Prime value for the volumetric ice in each snow and soil layer (s-1) + mLayerVolFracWatPrime, & ! intent(in): Prime value for the volumetric water in each snow and soil layer (s-1) + mLayerVolFracLiqPrime, & ! intent(in): Prime value for the volumetric liq in each snow and soil layer (s-1) + scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) + mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU From ba5fe17730e12efa141a552dd9e2971f1bebbead Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 15 Mar 2023 14:51:59 +0900 Subject: [PATCH 0581/1472] all prime time derivatives of progressive variables should be computed analytically in sundials, makes big difference in error with higher tolerances --- build/source/engine/eval8summaSundials.f90 | 13 ++++----- build/source/engine/soil_utils.f90 | 8 +++--- build/source/engine/updatState.f90 | 25 +++-------------- build/source/engine/updatStateSundials.f90 | 31 ++++++++++------------ build/source/engine/updateVarsSundials.f90 | 4 --- 5 files changed, 29 insertions(+), 52 deletions(-) diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index de94e3c77..ab5c304a7 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -587,13 +587,14 @@ subroutine eval8summaSundials(& ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + ! Below commented out because it seems more correct for Sundials to use the analytical form ! to conserve energy compute finite difference approximation of (theta_ice)' - if(dt_cur > 1e-14_rkind) then - scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur - do concurrent (iLayer=1:nLayers) - mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur - end do - endif ! if dt_cur is not too small + !if(dt_cur > 1e-14_rkind) then + ! scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur + ! do concurrent (iLayer=1:nLayers) + ! mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur + ! end do + !endif ! if dt_cur is not too small else if(ixHowHeatCap == closedForm)then call computHeatCapAnalytic(& ! input: control variables diff --git a/build/source/engine/soil_utils.f90 b/build/source/engine/soil_utils.f90 index 2038a2f7d..00436d0ea 100644 --- a/build/source/engine/soil_utils.f90 +++ b/build/source/engine/soil_utils.f90 @@ -339,11 +339,11 @@ function dTheta_dPsi(psi,alpha,theta_res,theta_sat,n,m) real(rkind),intent(in) :: m ! vGn "m" parameter (-) real(rkind) :: dTheta_dPsi ! derivative of the soil water characteristic (m-1) if(psi<=0._rkind)then - dTheta_dPsi = (theta_sat-theta_res) * & - (-m*(1._rkind + (psi*alpha)**n)**(-m-1._rkind)) * n*(psi*alpha)**(n-1._rkind) * alpha - if(abs(dTheta_dPsi) < epsilon(psi)) dTheta_dPsi = epsilon(psi) + dTheta_dPsi = (theta_sat-theta_res) * & + (-m*(1._rkind + (psi*alpha)**n)**(-m-1._rkind)) * n*(psi*alpha)**(n-1._rkind) * alpha + if(abs(dTheta_dPsi) < epsilon(psi)) dTheta_dPsi = epsilon(psi) else - dTheta_dPsi = epsilon(psi) + dTheta_dPsi = epsilon(psi) end if end function dTheta_dPsi diff --git a/build/source/engine/updatState.f90 b/build/source/engine/updatState.f90 index 29c82f15b..7ae216c17 100644 --- a/build/source/engine/updatState.f90 +++ b/build/source/engine/updatState.f90 @@ -116,18 +116,7 @@ subroutine updateSoil(& ! compute fractional **volume** of total water (liquid plus ice) mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if(mLayerVolFracWat > (theta_sat + tinyVal)) then - err=20 - message=trim(message)//'volume of liquid and ice (mLayerVolFracWat) exceeds porosity' - print*, 'mLayerVolFracWat = ', mLayerVolFracWat - print*, 'theta_sat (porosity) = ', theta_sat - print*, 'mLayerMatricHead = ', mLayerMatricHead - print*, 'theta_res = ', theta_res - print*, 'vGn_alpha = ', vGn_alpha - print*, 'vGn_n = ', vGn_n - print*, 'vGn_m = ', vGn_m - return - end if + if(mLayerVolFracWat > (theta_sat + tinyVal))then; err=20; message=trim(message)//'volume of liquid and ice exceeds porosity'; return; end if ! compute the critical soil temperature where all water is unfrozen (K) ! (eq 17 in Dall'Amico 2011) @@ -135,7 +124,6 @@ subroutine updateSoil(& ! *** compute volumetric fraction of liquid water and ice for partially frozen soil if(mLayerTemp < TcSoil)then ! (check if soil temperature is less than the critical temperature) - ! - volumetric liquid water content (-) ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations @@ -143,18 +131,13 @@ subroutine updateSoil(& mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - ! - volumetric ice content (-) - mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq - ! *** compute volumetric fraction of liquid water and ice for unfrozen soil - else - - ! all water is unfrozen - mLayerPsiLiq = mLayerMatricHead + else !( mLayerTemp >= TcSoil, all water is unfrozen, mLayerPsiLiq = mLayerMatricHead ) mLayerVolFracLiq = mLayerVolFracWat - mLayerVolFracIce = 0._rkind end if ! (check if soil is partially frozen) + ! - volumetric ice content (-) + mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq end subroutine updateSoil diff --git a/build/source/engine/updatStateSundials.f90 b/build/source/engine/updatStateSundials.f90 index 4f3738c78..793672b16 100644 --- a/build/source/engine/updatStateSundials.f90 +++ b/build/source/engine/updatStateSundials.f90 @@ -124,24 +124,26 @@ subroutine updateSoilSundials(& real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) + real(rkind),parameter :: tinyVal=epsilon(1._rkind) ! used in balance check real(rkind) :: dt_inv ! inverse of timestep ! initialize error control err=0; message="updateSoilSundials/" ! compute fractional **volume** of total water (liquid plus ice) mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if (.not.insideIDA)then ! calculate dt current, or use dt current as it can change here - if( abs(mLayerMatricHead - mLayerMatricHeadPrev) < verySmall )then !this difference is set as 0 inside varSubstep - dt_inv = 1._rkind/dt - else - dt_inv = mLayerMatricHeadPrime / (mLayerMatricHead - mLayerMatricHeadPrev) ! - endif - mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev)*dt_inv - else ! inside Sundials: instantaneous derivative will always be a full step size as input here + ! Below commented out because it seems more correct for Sundials to use the analytical form + !if (.not.insideIDA)then ! calculate dt current, or use dt current as it can change here + ! if( abs(mLayerMatricHead - mLayerMatricHeadPrev) < verySmall )then !this difference is set as 0 inside varSubstep + ! dt_inv = 1._rkind/dt + ! else + ! dt_inv = mLayerMatricHeadPrime / (mLayerMatricHead - mLayerMatricHeadPrev) ! + ! endif + ! mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev)*dt_inv + !else ! inside Sundials: instantaneous derivative will always be a full step size as input here mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime - endif + !endif - if(mLayerVolFracWat > theta_sat)then; err=20; message=trim(message)//'volume of liquid and ice exceeds porosity'; return; end if + if(mLayerVolFracWat > (theta_sat + tinyVal))then; err=20; message=trim(message)//'volume of liquid and ice exceeds porosity'; return; end if ! compute the critical soil temperature where all water is unfrozen (K) ! (eq 17 in Dall'Amico 2011) @@ -154,17 +156,12 @@ subroutine updateSoilSundials(& xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if(mLayerPsiLiq<0._rkind)then - mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime - else - mLayerVolFracLiqPrime = 0._rkind - endif + mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime ! *** compute volumetric fraction of liquid water for unfrozen soil - else !( mLayerTemp >= TcSoil, all water is unfrozen ) + else !( mLayerTemp >= TcSoil, all water is unfrozen, mLayerPsiLiq = mLayerMatricHead ) mLayerVolFracLiq = mLayerVolFracWat mLayerVolFracLiqPrime = mLayerVolFracWatPrime - mLayerVolFracIcePrime = 0._rkind end if ! (check if soil is partially frozen) diff --git a/build/source/engine/updateVarsSundials.f90 b/build/source/engine/updateVarsSundials.f90 index 6301202bb..056e34ef3 100644 --- a/build/source/engine/updateVarsSundials.f90 +++ b/build/source/engine/updateVarsSundials.f90 @@ -378,7 +378,6 @@ subroutine updateVarsSundials(& select case( ixStateType(ixFullVector) ) ! --> update the total water from the liquid water matric potential case(iname_lmpLayer) - effSat = volFracLiq(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex)) ! effective saturation avPore = theta_sat(ixControlIndex) - mLayerVolFracIceTrial(iLayer) - theta_res(ixControlIndex) ! available pore space mLayerVolFracLiqTrial(iLayer) = effSat*avPore + theta_res(ixControlIndex) @@ -388,12 +387,10 @@ subroutine updateVarsSundials(& mLayerMatricHeadPrime(ixControlIndex) = dPsi_dTheta(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) * mLayerVolFracWatPrime(iLayer) ! --> update the total water from the total water matric potential case(iname_matLayer) - mLayerVolFracWatTrial(iLayer) = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) mLayerVolFracWatPrime(iLayer) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) *mLayerMatricHeadPrime(ixControlIndex) ! --> update the total water matric potential (assume already have mLayerVolFracWatTrial given block above) case(iname_liqLayer, iname_watLayer) - mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) mLayerMatricHeadPrime(ixControlIndex) = dPsi_dTheta(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) * mLayerVolFracWatPrime(iLayer) case default; err=20; message=trim(message)//'expect iname_lmpLayer, iname_matLayer, iname_liqLayer, or iname_watLayer'; return @@ -439,7 +436,6 @@ subroutine updateVarsSundials(& ! - compute derivatives... ! ------------------------ - ! compute the derivative in bulk heat capacity w.r.t. total water content or water matric potential (m-1) ! compute the derivative in total water content w.r.t. total water matric potential (m-1) ! NOTE 1: valid for frozen and unfrozen conditions ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi From 60490623ba0971b1c32a82abd8738e17fd57ffe6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 15 Mar 2023 15:06:42 +0900 Subject: [PATCH 0582/1472] removing excess function inputs based on previous commit changes, shouldn't change results --- build/source/engine/computJacobSundials.f90 | 3 --- build/source/engine/eval8summaSundials.f90 | 3 --- build/source/engine/updatStateSundials.f90 | 21 +-------------------- build/source/engine/updateVarsSundials.f90 | 10 ---------- build/source/engine/varSubstep.f90 | 3 --- 5 files changed, 1 insertion(+), 39 deletions(-) diff --git a/build/source/engine/computJacobSundials.f90 b/build/source/engine/computJacobSundials.f90 index 877982cc1..2c31cab01 100644 --- a/build/source/engine/computJacobSundials.f90 +++ b/build/source/engine/computJacobSundials.f90 @@ -1283,14 +1283,11 @@ subroutine computJacobSetup(& call updateVarsSundials(& ! input - dt, & ! intent(in): time step .true., & ! intent(in): logical flag if computing Jacobian for sundials solver .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU - mLayerVolFracWatTrial, & ! intent(in): use current vector for prev vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): use current vector for prev vector of total water matric potential (m) diag_data, & ! intent(inout): model diagnostic variables for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index ab5c304a7..796fdc13a 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -472,14 +472,11 @@ subroutine eval8summaSundials(& ! update diagnostic variables and derivatives call updateVarsSundials(& ! input - dt_cur, & .false., & ! intent(in): logical flag if computing Jacobian for Sundials solver .false., & ! intent(in): logical flag to adjust temperature to account for the energy mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU - mLayerVolFracWatPrev, & ! intent(in): previous vector of total water matric potential (m) - mLayerMatricHeadPrev, & ! intent(in): previous vector of volumetric total water content (-) diag_data, & ! intent(inout): model diagnostic variables for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy diff --git a/build/source/engine/updatStateSundials.f90 b/build/source/engine/updatStateSundials.f90 index 793672b16..843a805a1 100644 --- a/build/source/engine/updatStateSundials.f90 +++ b/build/source/engine/updatStateSundials.f90 @@ -71,12 +71,8 @@ end subroutine updateSnowSundials ! *********************************************************************************************************************************** subroutine updateSoilSundials(& ! input - dt ,& ! intent(in): time step - insideIDA ,& ! intent(in): flag if inside Sundials solver mLayerTemp ,& ! intent(in): temperature (K) mLayerMatricHead ,& ! intent(in): total water matric potential (m) - mLayerMatricHeadPrev ,& ! intent(in): total water matric potential previous time step (m) - mLayerVolFracWatPrev ,& ! intent(in): volumetric fraction of total water previous time step (-) mLayerTempPrime ,& ! intent(in): temperature time derivative (K/s) mLayerMatricHeadPrime, & ! intent(in): total water matric potential time derivative (m/s) vGn_alpha ,& ! intent(in): van Genutchen "alpha" parameter @@ -98,12 +94,8 @@ subroutine updateSoilSundials(& USE soil_utils_module,only:dTheta_dPsi implicit none ! input variables - real(rkind),intent(in) :: dt - logical(lgt),intent(in) :: insideIDA ! flag if inside Sundials solver real(rkind),intent(in) :: mLayerTemp ! estimate of temperature (K) real(rkind),intent(in) :: mLayerMatricHead ! matric head (m) - real(rkind),intent(in) :: mLayerMatricHeadPrev ! matric head previous time step (m) - real(rkind),intent(in) :: mLayerVolFracWatPrev ! volumetric fraction of total waterprevious time step (m) real(rkind),intent(in) :: mLayerTempPrime ! temperature time derivative (K/s) real(rkind),intent(in) :: mLayerMatricHeadPrime ! matric head time derivative (m/s) real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter @@ -125,23 +117,12 @@ subroutine updateSoilSundials(& real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) real(rkind),parameter :: tinyVal=epsilon(1._rkind) ! used in balance check - real(rkind) :: dt_inv ! inverse of timestep ! initialize error control err=0; message="updateSoilSundials/" ! compute fractional **volume** of total water (liquid plus ice) mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - ! Below commented out because it seems more correct for Sundials to use the analytical form - !if (.not.insideIDA)then ! calculate dt current, or use dt current as it can change here - ! if( abs(mLayerMatricHead - mLayerMatricHeadPrev) < verySmall )then !this difference is set as 0 inside varSubstep - ! dt_inv = 1._rkind/dt - ! else - ! dt_inv = mLayerMatricHeadPrime / (mLayerMatricHead - mLayerMatricHeadPrev) ! - ! endif - ! mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev)*dt_inv - !else ! inside Sundials: instantaneous derivative will always be a full step size as input here - mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime - !endif + mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime if(mLayerVolFracWat > (theta_sat + tinyVal))then; err=20; message=trim(message)//'volume of liquid and ice exceeds porosity'; return; end if diff --git a/build/source/engine/updateVarsSundials.f90 b/build/source/engine/updateVarsSundials.f90 index 056e34ef3..94be63347 100644 --- a/build/source/engine/updateVarsSundials.f90 +++ b/build/source/engine/updateVarsSundials.f90 @@ -103,14 +103,11 @@ module updateVarsSundials_module ! ********************************************************************************************************** subroutine updateVarsSundials(& ! input - dt, & ! intent(in): time step computJac, & ! intent(in): logical flag if computing Jacobian for Sundials solve do_adjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU - mLayerVolFracWatPrev, & ! intent(in): previous vector of total water matric potential (m) - mLayerMatricHeadPrev, & ! intent(in): previous vector of volumetric total water content (-) diag_data, & ! intent(inout): model diagnostic variables for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy @@ -141,14 +138,11 @@ subroutine updateVarsSundials(& ! -------------------------------------------------------------------------------------------------------------------------------- implicit none ! input - real(rkind) ,intent(in) :: dt ! time step logical(lgt) ,intent(in) :: computJac ! flag if computing Jacobian for Sundials solver logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - real(rkind),intent(in) :: mLayerVolFracWatPrev(:) ! previous vector of total water matric potential (m) - real(rkind),intent(in) :: mLayerMatricHeadPrev(:) ! previous vector of volumetric total water content (-) type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy @@ -561,12 +555,8 @@ subroutine updateVarsSundials(& ! compute volumetric fraction of liquid water and ice call updateSoilSundials(& - dt, & ! intent(in) : time step - computJac, & ! intent(in) : logical flag if inside Sundials solver xTemp, & ! intent(in) : temperature (K) mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) - mLayerMatricHeadPrev(ixControlIndex), & ! intent(in) : previous values, will be same as current if computJac - mLayerVolFracWatPrev(iLayer), & ! intent(in) : previous values, will be same as current if computJac mLayerTempPrime(iLayer), & ! intent(in) : temperature time derivative (K/s) mLayerMatricHeadPrime(ixControlIndex), & ! intent(in) : total water matric potential time derivative (m/s) vGn_alpha(ixControlIndex), & ! intent(in) : van Genutchen "alpha" parameter diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index fc6b40946..7cc7b5bb0 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -855,14 +855,11 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! update diagnostic variables call updateVarsSundials(& ! input - dt, & .false., & ! intent(in): logical flag if computing Jacobian for Sundials solver doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU - mLayerVolFracWatTrial, & ! intent(in): use current vector for prev vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): use current vector for prev vector of total water matric potential (m) diag_data, & ! intent(inout): model diagnostic variables for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy From de05d60bbc1b5f5feb9139e59865fb403a913628 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 16 Mar 2023 22:17:31 +0900 Subject: [PATCH 0583/1472] Adding rootfinding with flag --- build/source/engine/summaSolveSundialsIDA.f90 | 271 ++++++++++++++++-- build/source/engine/type4IDA.f90 | 108 +++---- 2 files changed, 305 insertions(+), 74 deletions(-) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 9e74777a0..64a20d3f3 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -142,7 +142,7 @@ subroutine summaSolveSundialsIDA( & USE allocspace_module,only:allocLocal ! allocate local data structures USE eval8summaSundials_module,only:eval8summa4IDA ! DAE/ODE functions USE eval8summaSundials_module,only:eval8summaSundials ! residual of DAE - USE computJacobSundials_module,only:computJacob4IDA ! system Jacobian + USE computJacobSundials_module,only:computJacob4IDA ! system Jacobian USE tol4IDA_module,only:computWeight4IDA ! weigth required for tolerances USE var_derive_module,only:calcHeight ! height at layer interfaces and layer mid-point @@ -167,7 +167,7 @@ subroutine summaSolveSundialsIDA( & ! input: state vectors real(rkind),intent(in) :: stateVecInit(:) ! model state vector real(qp),intent(in) :: sMul(:) ! state vector multiplier (used in the residual calculations) - real(rkind), intent(inout) :: dMat(:) + real(rkind), intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures type(zLookup),intent(in) :: lookup_data ! lookup tables type(var_i), intent(in) :: type_data ! type of vegetation and soil @@ -210,18 +210,17 @@ subroutine summaSolveSundialsIDA( & logical(lgt) :: feasible ! feasibility flag real(qp) :: t0 ! staring time real(qp) :: dt_last(1) ! last time step - integer(kind = 8) :: mu, lu ! in banded matrix mode - integer(i4b) :: iVar - logical(lgt) :: startQuadrature - real(qp) :: h_init - integer(c_long) :: nState ! total number of state variables - real(rkind) :: rVec(nStat) - real(qp) :: tret(1) - logical(lgt) :: mergedLayers - logical(lgt),parameter :: offErrWarnMessage = .false. - real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) - real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) - integer(i4b) :: i + integer(kind = 8) :: mu, lu ! in banded matrix mode in Sundials type + integer(c_long) :: nState ! total number of state variables in Sundials type + real(rkind) :: rVec(nStat) ! residual vector + integer(i4b) :: iVar, i ! indices + integer(i4b) :: nRoot ! total number of roots (events) to find + real(qp) :: tret(1) ! time in data window + integer(i4b),allocatable :: rootsfound(:) ! crossing direction of discontinuities + integer(i4b),allocatable :: rootdir(:) ! forced crossing direction of discontinuities + logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true + logical(lgt),parameter :: detect_events = .true. ! flag to do event detection and restarting, default true + logical(lgt),parameter :: use_fdJac = .false. ! flag to use finite difference Jacobian, default false ! ----------------------------------------------------------------------------------------------------- @@ -251,7 +250,7 @@ subroutine summaSolveSundialsIDA( & eqns_data%sMul = sMul allocate( eqns_data%dMat(nState) ) - eqns_data%dMat = dMat + eqns_data%dMat = dMat ! allocate space for the temporary prognostic variable structure call allocLocal(prog_meta(:),eqns_data%prog_data,nSnow,nSoil,err,message) @@ -303,7 +302,6 @@ subroutine summaSolveSundialsIDA( & allocate( eqns_data%fluxVec(nState) ) allocate( eqns_data%resSink(nState) ) - startQuadrature = .true. retval = FSUNContext_Create(c_null_ptr, sunctx) ! create serial vectors @@ -334,6 +332,28 @@ subroutine summaSolveSundialsIDA( & retval = FIDAWFtolerances(ida_mem, c_funloc(computWeight4IDA)) if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDAWFtolerances'; return; endif + ! initialize rootfinding problem and allocate space, counting roots + if(detect_events)then + nRoot = 0 + if(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing) nRoot = nRoot+1 + if(nSnow>0)then + do i = 1,nSnow + if(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing) nRoot = nRoot+1 + enddo + endif + if(nSoil>0)then + do i = 1,nSoil + if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing) nRoot = nRoot+1 + if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing) nRoot = nRoot+1 + enddo + endif + allocate( rootsfound(nRoot) ) + allocate( rootdir(nRoot) ) + rootdir = 0 + retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4IDA)) + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif + endif + ! define the form of the matrix select case(ixMatrix) case(ixBandMatrix) @@ -365,9 +385,10 @@ subroutine summaSolveSundialsIDA( & if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetLinearSolver'; return; endif ! Set the user-supplied Jacobian routine - !comment this line out to use FD Jacobian - retval = FIDASetJacFn(ida_mem, c_funloc(computJacob4IDA)) - if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetJacFn'; return; endif + if(.not.use_fdJac)then + retval = FIDASetJacFn(ida_mem, c_funloc(computJacob4IDA)) + if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetJacFn'; return; endif + endif ! Create Newton SUNNonlinearSolver object sunnonlin_NLS => FSUNNonlinSol_Newton(sunvec_y, sunctx) @@ -388,7 +409,7 @@ subroutine summaSolveSundialsIDA( & ! Disable error messages and warnings if(offErrWarnMessage) then retval = FIDASetErrFile(ida_mem, c_null_ptr) - retval = FIDASetNoInactiveRootWarn(ida_mem) + if(detect_events) retval = FIDASetNoInactiveRootWarn(ida_mem) endif ! need the following values for the first substep @@ -405,6 +426,13 @@ subroutine summaSolveSundialsIDA( & eqns_data%scalarAquiferStoragePrev = prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) eqns_data%ixSaturation = ixSaturation + ! call this at beginning of data window to reduce root bouncing (only looking in one direction) + if(detect_events)then + call find_rootdir(eqns_data, rootdir) + retval = FIDASetRootDirection(ida_mem, rootdir) + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetRootDirection'; return; endif + endif + !********************************************************************************** !****************************** Main Solver *************************************** !************************* loop on one_step mode ********************************** @@ -412,6 +440,7 @@ subroutine summaSolveSundialsIDA( & tret(1) = t0 ! intial time do while(tret(1) < dt) + eqns_data%firstFluxCall = .false. eqns_data%firstSplitOper = .true. ! call IDASolve, advance solver just one internal step @@ -521,6 +550,25 @@ subroutine summaSolveSundialsIDA( & eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial + ! Restart for where vegetation and layers cross freezing point + if(detect_events)then + if (retvalr .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) + ! rootsfound[i]= +1 indicates that gi is increasing, -1 g[i] decreasing, 0 no root + retval = FIDAGetRootInfo(ida_mem, rootsfound) + if (retval < 0) then; err=20; message='solveByIDA: error in FIDAGetRootInfo'; return; endif + print '(a,f15.7,2x,17(i2,2x))', "time, rootsfound[] = ", tret(1), rootsfound + ! Reininitialize solver for running after discontinuity and restart + retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAReInit'; return; endif + if(dt_last(1)<0.01_rkind)then ! don't keep calling if step is small + retval = FIDARootInit(ida_mem, 0, c_funloc(layerDisCont4IDA)) + else + retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4IDA)) + endif + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif + endif + endif + enddo ! while loop on one_step mode until time dt !****************************** End of Main Solver *************************************** @@ -556,6 +604,10 @@ subroutine summaSolveSundialsIDA( & deallocate( eqns_data%mLayerEnthalpyPrev ) deallocate( eqns_data%fluxVec ) deallocate( eqns_data%resSink ) + if(detect_events)then + deallocate( rootsfound ) + deallocate( rootdir ) + endif call FIDAFree(ida_mem) retval = FSUNNonlinSolFree(sunnonlin_NLS) @@ -662,4 +714,183 @@ subroutine setSolverParams(dt,nonlin_iter,ida_mem,retval) end subroutine setSolverParams + +! ---------------------------------------------------------------------------------------- +! find_rootdir: private routine to determine which direction to look for the root, by +! determining if the variable is greater or less than the root. Need to do this to prevent +! bouncing around solution +! ---------------------------------------------------------------------------------------- + subroutine find_rootdir(eqns_data,rootdir) + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use fsundials_nvector_mod + use fnvector_serial_mod + use soil_utils_module,only:crit_soilT ! compute the critical temperature below which ice exists + use globalData,only:integerMissing ! missing integer + use var_lookup,only:iLookINDEX ! named variables for structure elements + use multiconst,only:Tfreeze ! freezing point of pure water (K) + + !======= Declarations ========= + implicit none + + ! calling variables + type(eqnsData),intent(in) :: eqns_data ! equations data + integer(i4b),intent(inout) :: rootdir(:) ! root function directions to search + + ! local variables + integer(i4b) :: i,ind ! indices + integer(i4b) :: nState ! number of states + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers + real(rkind) :: xPsi ! matric head at layer (m) + real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) + + ! get equations data variables + nState = eqns_data%nState + nSnow = eqns_data%nSnow + nSoil = eqns_data%nSoil + + ! initialize + rootdir = 0 + ind = 0 + + ! identify the critical point when vegetation begins to freeze + if(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then + ind = ind+1 + rootdir(ind) = 1 + if(eqns_data%scalarCanopyTempPrev > Tfreeze) rootdir(ind) = -1 + endif + + if(nSnow>0)then + do i = 1,nSnow + ! identify the critical point when the snow layer begins to freeze + if(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing)then + ind = ind+1 + rootdir(ind) = 1 + if(eqns_data%mLayerTempPrev(i) > Tfreeze) rootdir(ind) = -1 + endif + end do + endif + + if(nSoil>0)then + do i = 1,nSoil + xPsi = eqns_data%mLayerMatricHeadPrev(i) + ! identify the critical point when soil matrix potential goes below 0 and Tfreeze depends only on temp + if (eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing)then + ind = ind+1 + rootdir(ind) = 1 + if(xPsi > 0 ) rootdir(ind) = -1 + endif + ! identify the critical point when the soil layer begins to freeze + if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing)then + ind = ind+1 + TcSoil = crit_soilT(xPsi) + rootdir(ind) = 1 + if(eqns_data%mLayerTempPrev(i+nSnow) > TcSoil) rootdir(ind) = -1 + endif + end do + endif + + end subroutine find_rootdir + + +! ---------------------------------------------------------------------------------------- +! layerDisCont4IDA: The root function routine to find soil matrix potential = 0, +! soil temp = critical frozen point, and snow and veg temp = Tfreeze +! ---------------------------------------------------------------------------------------- +! Return values: +! 0 = success, +! 1 = recoverable error, +! -1 = non-recoverable error +! ---------------------------------------------------------------------------------------- + integer(c_int) function layerDisCont4IDA(t, sunvec_u, sunvec_up, gout, user_data) & + result(ierr) bind(C,name='layerDisCont4IDA') + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use fsundials_nvector_mod + use fnvector_serial_mod + use soil_utils_module,only:crit_soilT ! compute the critical temperature below which ice exists + use globalData,only:integerMissing ! missing integer + use var_lookup,only:iLookINDEX ! named variables for structure elements + use multiconst,only:Tfreeze ! freezing point of pure water (K) + + !======= Declarations ========= + implicit none + + ! calling variables + real(c_double), value :: t ! current time + type(N_Vector) :: sunvec_u ! solution N_Vector + type(N_Vector) :: sunvec_up ! derivative N_Vector + real(c_double) :: gout(999) ! root function values, if (nVeg + nSnow + 2*nSoil)>999, problem + type(c_ptr), value :: user_data ! user-defined data + + ! local variables + integer(i4b) :: i,ind ! indices + integer(i4b) :: nState ! number of states + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers + real(rkind) :: xPsi ! matric head at layer (m) + real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) + + ! pointers to data in SUNDIALS vectors + real(c_double), pointer :: uu(:) + type(eqnsData), pointer :: eqns_data ! equations data + + !======= Internals ============ + ! get equations data from user-defined data + call c_f_pointer(user_data, eqns_data) + nState = eqns_data%nState + nSnow = eqns_data%nSnow + nSoil = eqns_data%nSoil + + ! get data array from SUNDIALS vector + uu(1:nState) => FN_VGetArrayPointer(sunvec_u) + + ! initialize + gout = 0._rkind + ind = 0 + + ! identify the critical point when vegetation begins to freeze + if(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then + ind = ind+1 + gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)) - Tfreeze + endif + + if(nSnow>0)then + do i = 1,nSnow + ! identify the critical point when the snow layer begins to freeze + if(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing)then + ind = ind+1 + gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)) - Tfreeze + endif + end do + endif + + if(nSoil>0)then + do i = 1,nSoil + ! identify the critical point when soil matrix potential goes below 0 and Tfreeze depends only on temp + if (eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing)then + ind = ind+1 + xPsi = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)) + gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)) + else + xPsi = eqns_data%prog_data%var(iLookPROG%mLayerMatricHead)%dat(i) + endif + ! identify the critical point when the soil layer begins to freeze + if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing)then + ind = ind+1 + TcSoil = crit_soilT(xPsi) + gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) - TcSoil + endif + end do + endif + + ! return success + ierr = 0 + return + + end function layerDisCont4IDA + end module summaSolveSundialsIDA_module diff --git a/build/source/engine/type4IDA.f90 b/build/source/engine/type4IDA.f90 index 7b0af42f6..334b808eb 100644 --- a/build/source/engine/type4IDA.f90 +++ b/build/source/engine/type4IDA.f90 @@ -15,65 +15,65 @@ module type4IDA implicit none type eqnsData - type(c_ptr) :: ida_mem ! IDA memory - real(rkind) :: dt ! time step - integer(i4b) :: nSnow ! number of snow layers - integer(i4b) :: nSoil ! number of soil layers - integer(i4b) :: nLayers ! total number of layers - integer :: nState ! total number of state variables - integer(i4b) :: ixMatrix ! form of matrix (dense or banded) - logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt) :: firstFluxCall - logical(lgt) :: firstSplitOper - logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution - type(zLookup) :: lookup_data ! lookup tables - type(var_i) :: type_data ! type of vegetation and soil - type(var_d) :: attr_data ! spatial attributes - type(var_dlength) :: mpar_data ! model parameters - type(var_d) :: forc_data ! model forcing data - type(var_dlength) :: bvar_data ! model variables for the local basin - type(var_dlength) :: prog_data ! prognostic variables for a local HRU - type(var_ilength) :: indx_data ! indices defining model states and layers - type(var_dlength) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength) :: flux_data ! model fluxes for a local HRU - type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(rkind) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind) :: scalarCanopyTempPrev ! previous value of canopy temperature (K) - real(rkind) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - real(rkind) :: scalarCanopyIcePrev ! value of canopy ice content (kg m-2) at previous step - real(rkind) :: scalarCanopyLiqTrial ! trial value of canopy ice content (kg m-2) - real(rkind) :: scalarCanopyLiqPrev ! value of canopy ice content (kg m-2) at previous step - real(rkind) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) - real(rkind) :: scalarCanopyEnthalpyPrev ! previous enthalpy of the vegetation canopy (J m-3) - real(qp), allocatable :: sMul(:) ! state vector multiplier (used in the residual calculations) - real(rkind), allocatable :: dMat(:) ! diagonal of the Jacobian matrix - real(rkind), allocatable :: fluxVec(:) ! flux vector - real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation - real(rkind), allocatable :: atol(:) ! vector of absolute tolerances - real(rkind), allocatable :: rtol(:) ! vector of relative tolerances - real(rkind), allocatable :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) + type(c_ptr) :: ida_mem ! IDA memory + real(rkind) :: dt ! data step + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers + integer(i4b) :: nLayers ! total number of layers + integer(i4b) :: nState ! total number of state variables + integer(i4b) :: ixMatrix ! form of matrix (dense or banded) + logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt) :: firstFluxCall + logical(lgt) :: firstSplitOper + logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution + type(zLookup) :: lookup_data ! lookup tables + type(var_i) :: type_data ! type of vegetation and soil + type(var_d) :: attr_data ! spatial attributes + type(var_dlength) :: mpar_data ! model parameters + type(var_d) :: forc_data ! model forcing data + type(var_dlength) :: bvar_data ! model variables for the local basin + type(var_dlength) :: prog_data ! prognostic variables for a local HRU + type(var_ilength) :: indx_data ! indices defining model states and layers + type(var_dlength) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength) :: flux_data ! model fluxes for a local HRU + type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + real(rkind) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind) :: scalarCanopyTempPrev ! previous value of canopy temperature (K) + real(rkind) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + real(rkind) :: scalarCanopyIcePrev ! value of canopy ice content (kg m-2) at previous step + real(rkind) :: scalarCanopyLiqTrial ! trial value of canopy ice content (kg m-2) + real(rkind) :: scalarCanopyLiqPrev ! value of canopy ice content (kg m-2) at previous step + real(rkind) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) + real(rkind) :: scalarCanopyEnthalpyPrev ! previous enthalpy of the vegetation canopy (J m-3) + real(qp), allocatable :: sMul(:) ! state vector multiplier (used in the residual calculations) + real(rkind), allocatable :: dMat(:) ! diagonal of the Jacobian matrix + real(rkind), allocatable :: fluxVec(:) ! flux vector + real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation + real(rkind), allocatable :: atol(:) ! vector of absolute tolerances + real(rkind), allocatable :: rtol(:) ! vector of relative tolerances + real(rkind), allocatable :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) real(rkind), allocatable :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind), allocatable :: mLayerMatricHeadPrev(:) ! vector of total water matric potential (m) at previous step + real(rkind), allocatable :: mLayerMatricHeadPrev(:) ! vector of total water matric potential (m) at previous step real(rkind), allocatable :: mLayerVolFracWatTrial(:) ! trial value for volumetric fraction of total water (-) - real(rkind), allocatable :: mLayerVolFracWatPrev(:) ! value for volumetric fraction of total water (-) at previous step + real(rkind), allocatable :: mLayerVolFracWatPrev(:) ! value for volumetric fraction of total water (-) at previous step real(rkind), allocatable :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) - real(rkind), allocatable :: mLayerVolFracIcePrev(:) ! value for volumetric fraction of ice (-) at previous step + real(rkind), allocatable :: mLayerVolFracIcePrev(:) ! value for volumetric fraction of ice (-) at previous step real(rkind), allocatable :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) - real(rkind), allocatable :: mLayerVolFracLiqPrev(:) ! value for volumetric fraction of liquid water (-) at previous step - real(rkind) :: scalarAquiferStoragePrev - real(rkind) :: scalarAquiferStorageTrial - real(rkind), allocatable :: mLayerEnthalpyTrial(:) ! trial enthalpy of snow and soil (J m-3) - real(rkind), allocatable :: mLayerEnthalpyPrev(:) ! enthalpy of snow and soil (J m-3) at previous step - real(rkind), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind), allocatable :: mLayerTempPrev(:) ! vector of layer temperature (K) at previous step - real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - integer :: ixSaturation - integer(i4b) :: err ! error code - character(len = 50) :: message ! error message + real(rkind), allocatable :: mLayerVolFracLiqPrev(:) ! value for volumetric fraction of liquid water (-) at previous step + real(rkind) :: scalarAquiferStoragePrev + real(rkind) :: scalarAquiferStorageTrial + real(rkind), allocatable :: mLayerEnthalpyTrial(:) ! trial enthalpy of snow and soil (J m-3) + real(rkind), allocatable :: mLayerEnthalpyPrev(:) ! enthalpy of snow and soil (J m-3) at previous step + real(rkind), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind), allocatable :: mLayerTempPrev(:) ! vector of layer temperature (K) at previous step + real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + integer(i4b) :: ixSaturation + integer(i4b) :: err ! error code + character(len = 50) :: message ! error message end type eqnsData - - + + end module type4IDA From a2f8bd26a1649a86f0edc458ccd88ed1b6187c57 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 16 Mar 2023 23:02:14 +0900 Subject: [PATCH 0584/1472] do off indx_data for clarity --- build/source/engine/summaSolveSundialsIDA.f90 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 64a20d3f3..0e28ec072 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -335,16 +335,16 @@ subroutine summaSolveSundialsIDA( & ! initialize rootfinding problem and allocate space, counting roots if(detect_events)then nRoot = 0 - if(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing) nRoot = nRoot+1 + if(indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing) nRoot = nRoot+1 if(nSnow>0)then do i = 1,nSnow - if(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing) nRoot = nRoot+1 + if(indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing) nRoot = nRoot+1 enddo endif if(nSoil>0)then do i = 1,nSoil - if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing) nRoot = nRoot+1 - if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing) nRoot = nRoot+1 + if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing) nRoot = nRoot+1 + if(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing) nRoot = nRoot+1 enddo endif allocate( rootsfound(nRoot) ) @@ -409,7 +409,7 @@ subroutine summaSolveSundialsIDA( & ! Disable error messages and warnings if(offErrWarnMessage) then retval = FIDASetErrFile(ida_mem, c_null_ptr) - if(detect_events) retval = FIDASetNoInactiveRootWarn(ida_mem) + retval = FIDASetNoInactiveRootWarn(ida_mem) endif ! need the following values for the first substep From 88c45cbc32adb43790074c9130c18c5d5f511547 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 17 Mar 2023 11:42:43 +0900 Subject: [PATCH 0585/1472] Roots work now with flag detect_events. Things I do not like in the root setup: turning off rootfinding if step <0.01, declaring gout of roots as size 999 since cannot allocate, only letting it check one direction based on start of data window. --- build/source/engine/summaSolveSundialsIDA.f90 | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 0e28ec072..4e367031b 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -335,16 +335,16 @@ subroutine summaSolveSundialsIDA( & ! initialize rootfinding problem and allocate space, counting roots if(detect_events)then nRoot = 0 - if(indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing) nRoot = nRoot+1 + if(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing) nRoot = nRoot+1 if(nSnow>0)then do i = 1,nSnow - if(indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing) nRoot = nRoot+1 + if(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing) nRoot = nRoot+1 enddo endif if(nSoil>0)then do i = 1,nSoil - if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing) nRoot = nRoot+1 - if(indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing) nRoot = nRoot+1 + if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing) nRoot = nRoot+1 + if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing) nRoot = nRoot+1 enddo endif allocate( rootsfound(nRoot) ) @@ -352,6 +352,10 @@ subroutine summaSolveSundialsIDA( & rootdir = 0 retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4IDA)) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif + else ! will not use, allocate at something + nRoot = 1 + allocate( rootsfound(nRoot) ) + allocate( rootdir(nRoot) ) endif ! define the form of the matrix @@ -554,13 +558,13 @@ subroutine summaSolveSundialsIDA( & if(detect_events)then if (retvalr .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) ! rootsfound[i]= +1 indicates that gi is increasing, -1 g[i] decreasing, 0 no root - retval = FIDAGetRootInfo(ida_mem, rootsfound) - if (retval < 0) then; err=20; message='solveByIDA: error in FIDAGetRootInfo'; return; endif - print '(a,f15.7,2x,17(i2,2x))', "time, rootsfound[] = ", tret(1), rootsfound + !retval = FIDAGetRootInfo(ida_mem, rootsfound) + !if (retval < 0) then; err=20; message='solveByIDA: error in FIDAGetRootInfo'; return; endif + !print '(a,f15.7,2x,17(i2,2x))', "time, rootsfound[] = ", tret(1), rootsfound ! Reininitialize solver for running after discontinuity and restart retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAReInit'; return; endif - if(dt_last(1)<0.01_rkind)then ! don't keep calling if step is small + if(dt_last(1) < 0.01_rkind)then ! don't keep calling if step is small retval = FIDARootInit(ida_mem, 0, c_funloc(layerDisCont4IDA)) else retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4IDA)) @@ -604,10 +608,8 @@ subroutine summaSolveSundialsIDA( & deallocate( eqns_data%mLayerEnthalpyPrev ) deallocate( eqns_data%fluxVec ) deallocate( eqns_data%resSink ) - if(detect_events)then - deallocate( rootsfound ) - deallocate( rootdir ) - endif + deallocate( rootsfound ) + deallocate( rootdir ) call FIDAFree(ida_mem) retval = FSUNNonlinSolFree(sunnonlin_NLS) @@ -752,7 +754,6 @@ subroutine find_rootdir(eqns_data,rootdir) nSoil = eqns_data%nSoil ! initialize - rootdir = 0 ind = 0 ! identify the critical point when vegetation begins to freeze @@ -849,7 +850,6 @@ integer(c_int) function layerDisCont4IDA(t, sunvec_u, sunvec_up, gout, user_data uu(1:nState) => FN_VGetArrayPointer(sunvec_u) ! initialize - gout = 0._rkind ind = 0 ! identify the critical point when vegetation begins to freeze @@ -874,7 +874,7 @@ integer(c_int) function layerDisCont4IDA(t, sunvec_u, sunvec_up, gout, user_data if (eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing)then ind = ind+1 xPsi = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)) - gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)) + gout(1+nSnow+2*i-1) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)) else xPsi = eqns_data%prog_data%var(iLookPROG%mLayerMatricHead)%dat(i) endif @@ -882,7 +882,7 @@ integer(c_int) function layerDisCont4IDA(t, sunvec_u, sunvec_up, gout, user_data if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing)then ind = ind+1 TcSoil = crit_soilT(xPsi) - gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) - TcSoil + gout(1+nSnow+2*i) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) - TcSoil endif end do endif From 97168e21710d62c39531a537dc147b1ef496857a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 17 Mar 2023 12:27:55 +0900 Subject: [PATCH 0586/1472] Remove the 0.01 and let call roots as much as it wants --- build/source/engine/summaSolveSundialsIDA.f90 | 6 ------ 1 file changed, 6 deletions(-) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 4e367031b..52358635a 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -564,12 +564,6 @@ subroutine summaSolveSundialsIDA( & ! Reininitialize solver for running after discontinuity and restart retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAReInit'; return; endif - if(dt_last(1) < 0.01_rkind)then ! don't keep calling if step is small - retval = FIDARootInit(ida_mem, 0, c_funloc(layerDisCont4IDA)) - else - retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4IDA)) - endif - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif endif endif From afb3a4a32102d4d0c87e2a1e35fc8df2cc4add43 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 18 Mar 2023 10:51:52 +0900 Subject: [PATCH 0587/1472] ngen make files stub --- build/build_cmakeNGen_cop | 9 + ...build_cmakeBMI_gra => build_cmakeNGen_mac} | 4 +- build/build_cmakeSundials_gra | 8 - build/build_summa_gra | 6 - build/my_makefile_gra | 454 ------------------ 5 files changed, 11 insertions(+), 470 deletions(-) create mode 100755 build/build_cmakeNGen_cop rename build/{build_cmakeBMI_gra => build_cmakeNGen_mac} (56%) delete mode 100755 build/build_cmakeSundials_gra delete mode 100755 build/build_summa_gra delete mode 100644 build/my_makefile_gra diff --git a/build/build_cmakeNGen_cop b/build/build_cmakeNGen_cop new file mode 100755 index 000000000..4877bb748 --- /dev/null +++ b/build/build_cmakeNGen_cop @@ -0,0 +1,9 @@ +# from ../../ngen/builddir, run +# cp ../../summa/build/build_cmakeBMI_mac build_cmake +# run script from the builddir directory with ./build_cmake + + +cmake -B ngen/ -S . -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ + +cmake -B /ngen -S . && +cmake --build . --target ngen \ No newline at end of file diff --git a/build/build_cmakeBMI_gra b/build/build_cmakeNGen_mac similarity index 56% rename from build/build_cmakeBMI_gra rename to build/build_cmakeNGen_mac index 2d8716a1b..1069760ce 100755 --- a/build/build_cmakeBMI_gra +++ b/build/build_cmakeNGen_mac @@ -1,5 +1,5 @@ # from ../../bmi/builddir, run -# cp ../../summa/build/build_cmakeBMI_gra build_cmake +# cp ../../summa/build/build_cmakeBMI_mac build_cmake # run script from the builddir directory with ./build_cmake -cmake ../../bmi-fortran/ -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/home/avanb/SummaSundials/bmi/instdir \ No newline at end of file +cmake ../../bmi-fortran/ -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/Users/amedin/Research/SummaSundials/bmi/instdir \ No newline at end of file diff --git a/build/build_cmakeSundials_gra b/build/build_cmakeSundials_gra deleted file mode 100755 index af7e35adb..000000000 --- a/build/build_cmakeSundials_gra +++ /dev/null @@ -1,8 +0,0 @@ -# from ../../sundials/builddir, run -# cp ../../summa/build/build_cmakeSundials_gra build_cmake -# run script from the builddir directory with ./build_cmake -# Note, using -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF because modified constraint code and did not modify examples - -cmake ../../sundials-6.3.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/home/avanb/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/home/avanb/SummaSundials/sundials/instdir/examples - - diff --git a/build/build_summa_gra b/build/build_summa_gra deleted file mode 100755 index c428dfdd5..000000000 --- a/build/build_summa_gra +++ /dev/null @@ -1,6 +0,0 @@ -module load StdEnv/2020 -module load gcc/9.3.0 -module load openblas/0.3.17 -module load netcdf-fortran/4.5.2 - -make -f my_makefile_gra diff --git a/build/my_makefile_gra b/build/my_makefile_gra deleted file mode 100644 index 543e229d6..000000000 --- a/build/my_makefile_gra +++ /dev/null @@ -1,454 +0,0 @@ -#======================================================================== -# Makefile to compile SUMMA SUNDIALS -#======================================================================== -# -# Recommended use: Copy this file to Makefile.local, edit it to your -# heart's content, and then run `make -f build/Makefile.local` from -# your top level SUMMA directory. Don't include the Makefile.local in -# any pull requests you make. -# -# Note that Makefile configurations that we commonly use can be found on -# the SUMMA wiki at: -# https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration -# feel free to add yours to that page. -# -# To troubleshoot your paths and setup, type 'make check' -# -# At a minimum you will need to set the following: -# * F_MASTER_TOP - top level directory -# * FC - compiler suite -# * FC_EXE - compiler executable -# * INCLUDES - path to include files -# * ILDFLAGS - path to libraries to include -# * LIBRARIES - libraries to include -# -# Some further options can be specified for OpenMP, etc. See in Part 0 of -# the Makefile. You don't need to make any changes in PART 1 and -# following unless you are doing SUMMA development and changed what -# needs to be compiled - -#======================================================================== -# PART 0: User-configurable part -#======================================================================== - -# The variables can be specified in one of two ways: -# * delete the '##' in front of the variable, fill out the entry, -# save the file and run make -# * make no changes to this file, but specify the variables in your -# environment before you run make - -# Define core directory below which everything resides. This is the -# parent directory of the 'build' directory -F_MASTER_TOP = /home/avanb/SummaSundials -F_MASTER = $(F_MASTER_TOP)/summa - -# Define the Fortran Compiler. If you are using gfortran, then this needs -# to be version 6 or higher. This variable is simply used to select the right -# compiler flags in the ifeq statements in this Makefile. The compiler -# executable is set separately as FC_EXE -# Currently this is either gfortran or ifort -FC = gfortran - -# Define the path for the compiler executable. This is the actual executable -# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin/gfortran-mp-11 -# FC and FC_EXE have to be consistent -FC_EXE = gfortran - -# Define the NetCDF and LAPACK libraries and path to include files. -# INCLUDES needs to be of the form (no quotes around the string): -# INCLUDES = -I -I -I<...> -I -# LIBRARIES needs to be of the form ( no quotes around the string): -# LIBRARIES = '-L -lnetcdff -L -lblas -L -l' -# If none of this makes sense, please talk to your system -# administrator. -INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include -LDFLAGS = -L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib -LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas - -DIR_SUNDIALS= $(F_MASTER_TOP)/sundials/instdir -INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran -LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod - -DIR_BMI= $(F_MASTER_TOP)/bmi/instdir -INC_BMI=-I$(DIR_BMI)/include -LIB_BMI=-L$(DIR_BMI)/lib -lbmif - -# Eventually we plan move to a real configure script, but for now we like -# to keep track of successful compilations of SUMMA on different platforms -# and with different compilers. If you are successful compiling SUMMA, -# please add your configuration (operating system and compiler plus -# part 0 of the Makefile) to the SUMMA wiki on github. - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# ------------ define compiler flags ---------------------------------------- - -# define open MP flags -isOpenMP = -FLAGS_OMP = -LIBOPENMP = - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# gfortran compiler flags -ifeq "$(FC)" "gfortran" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -fopenmp - endif - -# Production runs -FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) - -# Debug runs -#FLAGS_NOAH = -p -g -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -#FLAGS_COMM = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -#FLAGS_SUMMA = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds - -endif - -# ifort compiler flags -ifeq "$(FC)" "ifort" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -qopenmp - endif - -# Production runs -FLAGS_NOAH = -O3 -noerror_limit -FR -auto -fltconsistency $(FLAGS_OMP) -FLAGS_COMM = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) - -# Debug runs -#FLAGS_NOAH = -O0 -p -g -warn nounused -noerror_limit -FR -auto -WB -traceback -fltconsistency -#FLAGS_COMM = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -#FLAGS_SUMMA = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -endif - -#======================================================================== -# PART 1: Define directory paths -#======================================================================== - -# Core directory that contains source code -F_KORE_DIR = $(F_MASTER)/build/source - -# Location of the compiled modules -MOD_PATH = $(F_MASTER)/build - -# Define the directory for the executables -EXE_PATH = $(F_MASTER)/bin - -#======================================================================== -# PART 2: Assemble all of the SUMMA sub-routines -#======================================================================== - -# Define directories -DRIVER_DIR = $(F_KORE_DIR)/driver -HOOKUP_DIR = $(F_KORE_DIR)/hookup -NETCDF_DIR = $(F_KORE_DIR)/netcdf -DSHARE_DIR = $(F_KORE_DIR)/dshare -NUMREC_DIR = $(F_KORE_DIR)/numrec -NOAHMP_DIR = $(F_KORE_DIR)/noah-mp -ENGINE_DIR = $(F_KORE_DIR)/engine - -# utilities -SUMMA_NRUTIL= \ - nrtype.f90 \ - f2008funcs.f90 \ - nr_utility.f90 -NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) - -# -# Numerical recipes procedures -# NOTE: all numerical recipes procedures are now replaced with free versions -SUMMA_NRPROC= \ - expIntegral.f90 \ - spline_int.f90 -NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) - -# Hook-up modules (set files and directory paths) -SUMMA_HOOKUP= \ - ascii_util.f90 \ - summaFileManager.f90 - -HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) - -# Data modules -SUMMA_DATAMS= \ - multiconst.f90 \ - var_lookup.f90 \ - data_types.f90 \ - globalData.f90 \ - flxMapping.f90 \ - get_ixname.f90 \ - popMetadat.f90 \ - outpt_stat.f90 -DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) - -# utility modules -SUMMA_UTILMS= \ - time_utils.f90 \ - mDecisions.f90 \ - snow_utils.f90 \ - soil_utils.f90 \ - soil_utilsAddSundials.f90 \ - updatState.f90 \ - updatStateSundials.f90 \ - matrixOper.f90 -UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) - -# Model guts -SUMMA_MODGUT= \ - MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) - -# Solver -SUMMA_SOLVER= \ - vegPhenlgy.f90 \ - diagn_evar.f90 \ - stomResist.f90 \ - groundwatr.f90 \ - vegSWavRad.f90 \ - vegNrgFlux.f90 \ - ssdNrgFlux.f90 \ - vegLiqFlux.f90 \ - snowLiqFlx.f90 \ - soilLiqFlx.f90 \ - bigAquifer.f90 \ - computFlux.f90 \ - type4IDA.f90 \ - tol4IDA.f90 \ - computEnthalpy.f90 \ - computHeatCap.f90 \ - computThermConduct.f90 \ - computResid.f90 \ - computJacob.f90 \ - eval8summa.f90 \ - summaSolve.f90 \ - systemSolv.f90 \ - computResidSundials.f90 \ - eval8summaSundials.f90 \ - computJacobSundials.f90 \ - computSnowDepth.f90 \ - summaSolveSundialsIDA.f90 \ - systemSolvSundials.f90 \ - varSubstep.f90 \ - varSubstepSundials.f90 \ - opSplittin.f90 \ - coupled_em.f90 \ - run_oneHRU.f90 \ - run_oneGRU.f90 -SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) - -# Define routines for SUMMA preliminaries -SUMMA_PRELIM= \ - conv_funcs.f90 \ - sunGeomtry.f90 \ - convE2Temp.f90 \ - allocspace.f90 \ - checkStruc.f90 \ - childStruc.f90 \ - ffile_info.f90 \ - read_attrb.f90 \ - read_pinit.f90 \ - pOverwrite.f90 \ - read_param.f90 \ - paramCheck.f90 \ - check_icond.f90 -PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) - -SUMMA_NOAHMP= \ - module_model_constants.F \ - module_sf_noahutl.F \ - module_sf_noahlsm.F \ - module_sf_noahmplsm.F -NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) - -# Define routines for the SUMMA model runs -SUMMA_MODRUN = \ - indexState.f90 \ - getVectorz.f90 \ - t2enthalpy.f90 \ - updateVars.f90 \ - updateVarsSundials.f90 \ - var_derive.f90 \ - read_force.f90 \ - derivforce.f90 \ - snowAlbedo.f90 \ - canopySnow.f90 \ - tempAdjust.f90 \ - snwCompact.f90 \ - layerMerge.f90 \ - layerDivide.f90 \ - volicePack.f90 \ - qTimeDelay.f90 -MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) - -# Define routines for the solver -SUMMA_MSOLVE = \ - -# Define NetCDF routines -SUMMA_NETCDF = \ - netcdf_util.f90 \ - def_output.f90 \ - modelwrite.f90 \ - read_icond.f90 -NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) - -# ... stitch together common programs -COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) - -# ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) - -# Define the driver routines -SUMMA_DRIVER= \ - summa_type.f90 \ - summa_util.f90 \ - summa_alarms.f90 \ - summa_globalData.f90 \ - summa_defineOutput.f90 \ - summa_init.f90 \ - summa_setup.f90 \ - summa_restart.f90 \ - summa_forcing.f90 \ - summa_modelRun.f90 \ - summa_writeOutput.f90 \ - summa_driver.f90 -DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) - -# Define SUMMA running infrastructure -SUMMA_RUN = \ - summa_run.f90 -SUMMA = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_RUN)) - -# Define BMI testing infrastructure -BMI_TEST = \ - summa_runBMI.f90 -BMI = $(patsubst %, $(DRIVER_DIR)/%, $(BMI_TEST)) - -# Define the executable -DRIVER__EX = summa_sundials.exe -BMI__EX = summa_bmi.exe - -# Define version number -VERSIONFILE = $(DRIVER_DIR)/summaversion.inc -VERSION = $(shell git tag | tail -n 1) -BULTTIM = $(shell date) -GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') -GITHASH = $(shell git rev-parse HEAD) - -#======================================================================== -# PART 3: Checks -#====================================================================== -# make sure that the paths are defined. These are just some high level checks -ifndef F_MASTER - $(error F_MASTER is undefined) -endif -ifndef FC - $(error FC is undefined: Specify your compiler) -endif -ifndef FC_EXE - $(error FC_EXE is undefined: Specify your compiler executable) -endif -ifndef FLAGS_SUMMA - $(error Specify flags for your compiler: $(FC)) -endif -ifndef INCLUDES - $(error INCLUDES is undefined) -endif -ifndef LIBRARIES - $(error LIBRARIES is undefined) -endif - -#======================================================================== -# PART 4: compilation -#====================================================================== - -# Compile -all: compile_noah compile_comm compile_rout compile_test link_test clean_test compile_summa link clean install install_test -part: compile_noah compile_comm compile_rout -summa: compile_noah compile_comm compile_rout compile_summa link clean install -bmi: compile_noah compile_comm compile_rout compile_test link_test clean install_test - - -check: - $(info) - $(info Displaying make variables:) - $(info F_MASTER : $(F_MASTER)) - $(info EXE_PATH : $(EXE_PATH)) - $(info FC : $(FC)) - $(info INCLUDES : $(INCLUDES)) - $(info LIBRARIES : $(LIBRARIES)) - $(info FLAGS_NOAH : $(FLAGS_NOAH)) - $(info FLAGS_COMM : $(FLAGS_COMM)) - $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) - $(info SUNDIALSDIR: $(DIR_SUNDIALS)) - $(info INC_SUNDIALS: $(INC_SUNDIALS)) - $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) - $(info BMIDIR: $(DIR_BMI)) - $(info INC_BMI: $(INC_BMI)) - $(info LIB_BMI: $(LIB_BMI)) - $(info) - -# update version information -update_version: - echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) - echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) - -# compile common routines -compile_comm: - $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) - -# compile Noah-MP routines -compile_noah: - $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) - -# compile SUMMA routines -compile_rout: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) - -# compile run scripts -compile_summa: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA) - -# compile test scripts -compile_test: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(BMI) - -# link run routines -link: - $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(DRIVER__EX) - -# link test routines -link_test: - $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(BMI__EX) - -# remove test script objects -clean_test: - rm -f summa_runBMI.o - -# Remove object files -clean: - rm -f *.o - rm -f *.mod - rm -f soil_veg_gen_parm__genmod.f90 - -# Copy the executable to the bin directory -install: - @mkdir -p $(EXE_PATH) - @mv $(DRIVER__EX) $(EXE_PATH) - $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) - -# Copy the executable to the bin directory -install_test: - @mkdir -p $(EXE_PATH) - @mv $(BMI__EX) $(EXE_PATH) - $(info $(BMI__EX) successfully installed in $(EXE_PATH)) From 52d550e532e24b6c31e7c29335c9a9f0788d2b52 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 18 Mar 2023 21:56:08 +0900 Subject: [PATCH 0588/1472] indexing was wrong in gout --- build/source/engine/summaSolveSundialsIDA.f90 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 52358635a..6f506404f 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -78,6 +78,8 @@ module summaSolveSundialsIDA_module implicit none private::setInitialCondition private::setSolverParams + private::find_rootdir + public::layerDisCont4IDA public::summaSolveSundialsIDA contains @@ -868,7 +870,7 @@ integer(c_int) function layerDisCont4IDA(t, sunvec_u, sunvec_up, gout, user_data if (eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing)then ind = ind+1 xPsi = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)) - gout(1+nSnow+2*i-1) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)) + gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)) else xPsi = eqns_data%prog_data%var(iLookPROG%mLayerMatricHead)%dat(i) endif @@ -876,7 +878,7 @@ integer(c_int) function layerDisCont4IDA(t, sunvec_u, sunvec_up, gout, user_data if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing)then ind = ind+1 TcSoil = crit_soilT(xPsi) - gout(1+nSnow+2*i) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) - TcSoil + gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) - TcSoil endif end do endif From 551b8efaa979d3fe2ddee508be0dc0b3f68d73b0 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 20 Mar 2023 11:50:46 +0900 Subject: [PATCH 0589/1472] should be a 0 as a real --- build/source/engine/summaSolveSundialsIDA.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 6f506404f..d5905ff69 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -777,7 +777,7 @@ subroutine find_rootdir(eqns_data,rootdir) if (eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing)then ind = ind+1 rootdir(ind) = 1 - if(xPsi > 0 ) rootdir(ind) = -1 + if(xPsi > 0._rkind ) rootdir(ind) = -1 endif ! identify the critical point when the soil layer begins to freeze if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing)then From 1d9631797ddf2fe62d5102b020b1b5b0b4184369 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 20 Mar 2023 23:51:21 +0900 Subject: [PATCH 0590/1472] fix snowLiqFlx to use ice trial --- build/source/engine/computFlux.f90 | 1 + build/source/engine/computSnowDepth.f90 | 84 ++++++------- build/source/engine/snowLiqFlx.f90 | 159 ++---------------------- 3 files changed, 51 insertions(+), 193 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index c4a3a35f1..25c36108a 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -623,6 +623,7 @@ subroutine computFlux(& scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) ! input: model state vector + mLayerVolFracIceTrial(1:nSnow), & ! intent(in): trial value of volumetric fraction of ice at the current iteration (-) mLayerVolFracLiqTrial(1:nSnow), & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) ! input-output: data structures indx_data, & ! intent(in): model indices diff --git a/build/source/engine/computSnowDepth.f90 b/build/source/engine/computSnowDepth.f90 index 045044e5e..568294aa0 100644 --- a/build/source/engine/computSnowDepth.f90 +++ b/build/source/engine/computSnowDepth.f90 @@ -13,21 +13,21 @@ module computSnowDepth_module ! data types USE data_types,only:& - var_i, & ! x%var(:) (i4b) - var_d, & ! x%var(:) (rkind) - var_ilength, & ! x%var(:)%dat (i4b) - var_dlength, & ! x%var(:)%dat (rkind) - zLookup ! x%z(:)%var(:)%lookup(:) (rkind) + var_i, & ! x%var(:) (i4b) + var_d, & ! x%var(:) (rkind) + var_ilength, & ! x%var(:)%dat (i4b) + var_dlength, & ! x%var(:)%dat (rkind) + zLookup ! x%z(:)%var(:)%lookup(:) (rkind) ! named variables for parent structures -USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure -USE var_lookup,only:iLookPROG ! named variables for structure elements -USE var_lookup,only:iLookDIAG ! named variables for structure elements -USE var_lookup,only:iLookFLUX ! named variables for structure elements -USE var_lookup,only:iLookPARAM ! named variables for structure elements -USE var_lookup,only:iLookINDEX ! named variables for structure elements -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookFLUX ! named variables for structure elements +USE var_lookup,only:iLookPARAM ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil ! privacy @@ -43,33 +43,33 @@ module computSnowDepth_module ! public subroutine computSnowDepth: compute snow depth for one sub timestep ! ************************************************************************************************ subroutine computSnowDepth(& - dt_sub, & - nSnow, & ! intent(in) + dt_sub, & + nSnow, & ! intent(in) scalarSnowSublimation, & ! intent(in) - mLayerVolFracLiq, & ! intent(inout) - mLayerVolFracIce, & ! intent(inout) - mLayerTemp, & ! intent(in) - mLayerMeltFreeze, & ! intent(in) - mpar_data, & ! intent(in) + mLayerVolFracLiq, & ! intent(inout) + mLayerVolFracIce, & ! intent(inout) + mLayerTemp, & ! intent(in) + mLayerMeltFreeze, & ! intent(in) + mpar_data, & ! intent(in) ! output tooMuchSublim, & ! intent(out): flag to denote that there was too much sublimation in a given time step - mLayerDepth, & ! intent(inout) + mLayerDepth, & ! intent(inout) ! error control - err,message) ! intent(out): error control + err,message) ! intent(out): error control USE snwDensify_module,only:snwDensify ! snow densification (compaction and cavitation) implicit none - real(qp),intent(in) :: dt_sub + real(qp),intent(in) :: dt_sub integer(i4b),intent(in) :: nSnow ! number of snow layers - real(rkind),intent(in) :: scalarSnowSublimation - real(rkind),intent(inout) :: mLayerVolFracLiq(:) - real(rkind),intent(inout) :: mLayerVolFracIce(:) - real(rkind),intent(in) :: mLayerTemp(:) - real(rkind),intent(in) :: mLayerMeltFreeze(:) + real(rkind),intent(in) :: scalarSnowSublimation + real(rkind),intent(inout) :: mLayerVolFracLiq(:) + real(rkind),intent(inout) :: mLayerVolFracIce(:) + real(rkind),intent(in) :: mLayerTemp(:) + real(rkind),intent(in) :: mLayerMeltFreeze(:) type(var_dlength),intent(in) :: mpar_data ! model parameters logical(lgt) :: tooMuchSublim ! flag to denote that there was too much sublimation in a given time step - real(rkind),intent(inout) :: mLayerDepth(:) + real(rkind),intent(inout) :: mLayerDepth(:) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -122,21 +122,21 @@ subroutine computSnowDepth(& if(nSnow>0)then call snwDensify(& ! intent(in): variables - dt_sub, & ! intent(in): time step (s) - nSnow, & ! intent(in): number of snow layers - mLayerTemp(1:nSnow), & ! intent(in): temperature of each layer (K) - mLayerMeltFreeze(1:nSnow), & ! intent(in): volumetric melt in each layer (kg m-3) + dt_sub, & ! intent(in): time step (s) + nSnow, & ! intent(in): number of snow layers + mLayerTemp(1:nSnow), & ! intent(in): temperature of each layer (K) + mLayerMeltFreeze(1:nSnow), & ! intent(in): volumetric melt in each layer (kg m-3) ! intent(in): parameters - mpar_data%var(iLookPARAM%densScalGrowth)%dat(1), & ! intent(in): density scaling factor for grain growth (kg-1 m3) - mpar_data%var(iLookPARAM%tempScalGrowth)%dat(1), & ! intent(in): temperature scaling factor for grain growth (K-1) - mpar_data%var(iLookPARAM%grainGrowthRate)%dat(1), & ! intent(in): rate of grain growth (s-1) - mpar_data%var(iLookPARAM%densScalOvrbdn)%dat(1), & ! intent(in): density scaling factor for overburden pressure (kg-1 m3) - mpar_data%var(iLookPARAM%tempScalOvrbdn)%dat(1), & ! intent(in): temperature scaling factor for overburden pressure (K-1) - mpar_data%var(iLookPARAM%baseViscosity)%dat(1), & ! intent(in): viscosity coefficient at T=T_frz and snow density=0 (kg m-2 s) + mpar_data%var(iLookPARAM%densScalGrowth)%dat(1), & ! intent(in): density scaling factor for grain growth (kg-1 m3) + mpar_data%var(iLookPARAM%tempScalGrowth)%dat(1), & ! intent(in): temperature scaling factor for grain growth (K-1) + mpar_data%var(iLookPARAM%grainGrowthRate)%dat(1), & ! intent(in): rate of grain growth (s-1) + mpar_data%var(iLookPARAM%densScalOvrbdn)%dat(1), & ! intent(in): density scaling factor for overburden pressure (kg-1 m3) + mpar_data%var(iLookPARAM%tempScalOvrbdn)%dat(1), & ! intent(in): temperature scaling factor for overburden pressure (K-1) + mpar_data%var(iLookPARAM%baseViscosity)%dat(1), & ! intent(in): viscosity coefficient at T=T_frz and snow density=0 (kg m-2 s) ! intent(inout): state variables - mLayerDepth(1:nSnow), & ! intent(inout): depth of each layer (m) - mLayerVolFracLiq(1:nSnow), & ! intent(inout): volumetric fraction of liquid water after itertations (-) - mLayerVolFracIce(1:nSnow), & ! intent(inout): volumetric fraction of ice after itertations (-) + mLayerDepth(1:nSnow), & ! intent(inout): depth of each layer (m) + mLayerVolFracLiq(1:nSnow), & ! intent(inout): volumetric fraction of liquid water after itertations (-) + mLayerVolFracIce(1:nSnow), & ! intent(inout): volumetric fraction of ice after itertations (-) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if diff --git a/build/source/engine/snowLiqFlx.f90 b/build/source/engine/snowLiqFlx.f90 index 5bb65076a..7dd0c106a 100644 --- a/build/source/engine/snowLiqFlx.f90 +++ b/build/source/engine/snowLiqFlx.f90 @@ -43,7 +43,6 @@ module snowLiqFlx_module implicit none private public::snowLiqFlx -public::snowLiqFlxSundials contains @@ -59,6 +58,7 @@ subroutine snowLiqFlx(& scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) ! input: model state vector + mLayerVolFracIceTrial, & ! intent(in): trial value of volumetric fraction of ice at the current iteration (-) mLayerVolFracLiqTrial, & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) ! input-output: data structures indx_data, & ! intent(in): model indices @@ -79,6 +79,7 @@ subroutine snowLiqFlx(& real(rkind),intent(in) :: scalarThroughfallRain ! computed throughfall rate (kg m-2 s-1) real(rkind),intent(in) :: scalarCanopyLiqDrainage ! computed drainage of liquid water (kg m-2 s-1) ! input: model state vector + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value of volumetric fraction of ice at the current iteration (-) real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value of volumetric fraction of liquid water at the current iteration (-) ! input-output: data structures type(var_ilength),intent(in) :: indx_data ! model indices @@ -110,7 +111,6 @@ subroutine snowLiqFlx(& ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat, & ! intent(in): index in the state subset for hydrology state variables in the snow domain ! input: snow properties and parameters - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow), & ! intent(in): volumetric ice content at the start of the time step (-) Fcapil => mpar_data%var(iLookPARAM%Fcapil)%dat(1), & ! intent(in): capillary retention as a fraction of the total pore volume (-) k_snow => mpar_data%var(iLookPARAM%k_snow)%dat(1), & ! intent(in): hydraulic conductivity of snow (m s-1), 0.0055 = approx. 20 m/hr, from UEB mw_exp => mpar_data%var(iLookPARAM%mw_exp)%dat(1), & ! intent(in): exponent for meltwater flow (-) @@ -118,12 +118,12 @@ subroutine snowLiqFlx(& mLayerPoreSpace => diag_data%var(iLookDIAG%mLayerPoreSpace)%dat, & ! intent(inout): pore space in each snow layer (-) mLayerThetaResid => diag_data%var(iLookDIAG%mLayerThetaResid)%dat & ! intent(inout): esidual volumetric liquid water content in each snow layer (-) ) ! association of local variables with information in the data structures - ! ------------------------------------------------------------------------------------------------------------------------------------------ + ! ------------------------------------------------------------------------------------------------------------------------------------------ ! initialize error control - err=0; message='snowLiqFlx/' + err=0; message='snowLiqFlxSundials/' ! check that the input vectors match nSnow - if(size(mLayerVolFracLiqTrial)/=nSnow .or. size(mLayerVolFracIce)/=nSnow .or. & + if(size(mLayerVolFracLiqTrial)/=nSnow .or. size(mLayerVolFracIceTrial)/=nSnow .or. & size(iLayerLiqFluxSnow)/=nSnow+1 .or. size(iLayerLiqFluxSnowDeriv)/=nSnow+1) then err=20; message=trim(message)//'size mismatch of input/output vectors'; return end if @@ -158,9 +158,9 @@ subroutine snowLiqFlx(& ! loop through snow layers do iLayer=1,nSnow ! compute the reduction in liquid water holding capacity at high snow density (-) - multResid = 1._rkind / ( 1._rkind + exp( (mLayerVolFracIce(iLayer)*iden_ice - residThrs) / residScal) ) + multResid = 1._rkind / ( 1._rkind + exp( (mLayerVolFracIceTrial(iLayer)*iden_ice - residThrs) / residScal) ) ! compute the pore space (-) - mLayerPoreSpace(iLayer) = 1._rkind - mLayerVolFracIce(iLayer) + mLayerPoreSpace(iLayer) = 1._rkind - mLayerVolFracIceTrial(iLayer) ! compute the residual volumetric liquid water content (-) mLayerThetaResid(iLayer) = Fcapil*mLayerPoreSpace(iLayer) * multResid end do ! (looping through snow layers) @@ -175,7 +175,7 @@ subroutine snowLiqFlx(& relSaturn = (mLayerVolFracLiqTrial(iLayer) - mLayerThetaResid(iLayer)) / availCap ! relative saturation iLayerLiqFluxSnow(iLayer) = k_snow*relSaturn**mw_exp iLayerLiqFluxSnowDeriv(iLayer) = ( (k_snow*mw_exp)/availCap ) * relSaturn**(mw_exp - 1._rkind) - if(mLayerVolFracIce(iLayer) > maxVolIceContent)then ! NOTE: use start-of-step ice content, to avoid convergence problems + if(mLayerVolFracIceTrial(iLayer) > maxVolIceContent)then ! NOTE: use start-of-step ice content, to avoid convergence problems ! ** allow liquid water to pass through under very high ice density iLayerLiqFluxSnow(iLayer) = iLayerLiqFluxSnow(iLayer) + iLayerLiqFluxSnow(iLayer-1) !NOTE: derivative may need to be updated in future. end if @@ -189,147 +189,4 @@ subroutine snowLiqFlx(& end associate end subroutine snowLiqFlx - -! ************************************************************************************************ -! public subroutine snowLiqFlxSundials: compute liquid water flux through the snowpack -! ************************************************************************************************ -subroutine snowLiqFlxSundials(& - ! input: model control - nSnow, & ! intent(in): number of snow layers - firstFluxCall, & ! intent(in): the first flux call - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: forcing for the snow domain - scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) - scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) - ! input: model state vector - mLayerVolFracIce, & ! intent(in) - mLayerVolFracLiqTrial, & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) - ! input-output: data structures - indx_data, & ! intent(in): model indices - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output: fluxes and derivatives - iLayerLiqFluxSnow, & ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) - iLayerLiqFluxSnowDeriv, & ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) - ! output: error control - err,message) ! intent(out): error control - implicit none - ! input: model control - integer(i4b),intent(in) :: nSnow ! number of snow layers - logical(lgt),intent(in) :: firstFluxCall ! the first flux call - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - ! input: forcing for the snow domain - real(rkind),intent(in) :: scalarThroughfallRain ! computed throughfall rate (kg m-2 s-1) - real(rkind),intent(in) :: scalarCanopyLiqDrainage ! computed drainage of liquid water (kg m-2 s-1) - ! input: model state vector - real(rkind),intent(in) :: mLayerVolFracIce(:) - real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value of volumetric fraction of liquid water at the current iteration (-) - ! input-output: data structures - type(var_ilength),intent(in) :: indx_data ! model indices - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - ! output: fluxes and derivatives - real(rkind),intent(inout) :: iLayerLiqFluxSnow(0:) ! vertical liquid water flux at layer interfaces (m s-1) - real(rkind),intent(inout) :: iLayerLiqFluxSnowDeriv(0:) ! derivative in vertical liquid water flux at layer interfaces (m s-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ------------------------------------------------------------------------------------------------------------------------------------------ - ! local variables - integer(i4b) :: i ! search index for scalar solution - integer(i4b) :: iLayer ! layer index - integer(i4b) :: ixTop ! top layer in subroutine call - integer(i4b) :: ixBot ! bottom layer in subroutine call - real(rkind) :: multResid ! multiplier for the residual water content (-) - real(rkind),parameter :: residThrs=550._rkind ! ice density threshold to reduce residual liquid water content (kg m-3) - real(rkind),parameter :: residScal=10._rkind ! scaling factor for residual liquid water content reduction factor (kg m-3) - real(rkind),parameter :: maxVolIceContent=0.7_rkind ! maximum volumetric ice content to store water (-) - real(rkind) :: availCap ! available storage capacity [0,1] (-) - real(rkind) :: relSaturn ! relative saturation [0,1] (-) - ! ------------------------------------------------------------------------------------------------------------------------------------------ - ! make association of local variables with information in the data structures - associate(& - ! input: layer indices - ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers - ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat, & ! intent(in): index in the state subset for hydrology state variables in the snow domain - ! input: snow properties and parameters - Fcapil => mpar_data%var(iLookPARAM%Fcapil)%dat(1), & ! intent(in): capillary retention as a fraction of the total pore volume (-) - k_snow => mpar_data%var(iLookPARAM%k_snow)%dat(1), & ! intent(in): hydraulic conductivity of snow (m s-1), 0.0055 = approx. 20 m/hr, from UEB - mw_exp => mpar_data%var(iLookPARAM%mw_exp)%dat(1), & ! intent(in): exponent for meltwater flow (-) - ! input/output: diagnostic variables -- only computed for the first iteration - mLayerPoreSpace => diag_data%var(iLookDIAG%mLayerPoreSpace)%dat, & ! intent(inout): pore space in each snow layer (-) - mLayerThetaResid => diag_data%var(iLookDIAG%mLayerThetaResid)%dat & ! intent(inout): esidual volumetric liquid water content in each snow layer (-) - ) ! association of local variables with information in the data structures - ! ------------------------------------------------------------------------------------------------------------------------------------------ - ! initialize error control - err=0; message='snowLiqFlxSundials/' - - ! check that the input vectors match nSnow - if(size(mLayerVolFracLiqTrial)/=nSnow .or. size(mLayerVolFracIce)/=nSnow .or. & - size(iLayerLiqFluxSnow)/=nSnow+1 .or. size(iLayerLiqFluxSnowDeriv)/=nSnow+1) then - err=20; message=trim(message)//'size mismatch of input/output vectors'; return - end if - - ! check the meltwater exponent is >=1 - if(mw_exp<1._rkind)then; err=20; message=trim(message)//'meltwater exponent < 1'; return; end if - - ! get the indices for the snow+soil layers - ixTop = integerMissing - if(scalarSolution)then - do i=1,size(ixSnowOnlyHyd) - if(ixSnowOnlyHyd(i) /= integerMissing)then - ixTop=ixLayerState(i) - ixBot=ixTop - exit ! break out of loop once found - endif - end do - if(ixTop == integerMissing)then - err=20; message=trim(message)//'Unable to identify snow layer for scalar solution!'; return - end if - else - ixTop = 1 - ixBot = nSnow - endif - - ! define the liquid flux at the upper boundary (m s-1) - iLayerLiqFluxSnow(0) = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water - iLayerLiqFluxSnowDeriv(0) = 0._rkind - - ! compute properties fixed over the time step - if(firstFluxCall)then - ! loop through snow layers - do iLayer=1,nSnow - ! compute the reduction in liquid water holding capacity at high snow density (-) - multResid = 1._rkind / ( 1._rkind + exp( (mLayerVolFracIce(iLayer)*iden_ice - residThrs) / residScal) ) - ! compute the pore space (-) - mLayerPoreSpace(iLayer) = 1._rkind - mLayerVolFracIce(iLayer) - ! compute the residual volumetric liquid water content (-) - mLayerThetaResid(iLayer) = Fcapil*mLayerPoreSpace(iLayer) * multResid - end do ! (looping through snow layers) - end if ! (if the first flux call) - - ! compute fluxes - do iLayer=ixTop,ixBot ! (loop through snow layers) - ! check that flow occurs - if(mLayerVolFracLiqTrial(iLayer) > mLayerThetaResid(iLayer))then - ! compute the relative saturation (-) - availCap = mLayerPoreSpace(iLayer) - mLayerThetaResid(iLayer) ! available capacity - relSaturn = (mLayerVolFracLiqTrial(iLayer) - mLayerThetaResid(iLayer)) / availCap ! relative saturation - iLayerLiqFluxSnow(iLayer) = k_snow*relSaturn**mw_exp - iLayerLiqFluxSnowDeriv(iLayer) = ( (k_snow*mw_exp)/availCap ) * relSaturn**(mw_exp - 1._rkind) - if(mLayerVolFracIce(iLayer) > maxVolIceContent)then ! NOTE: use start-of-step ice content, to avoid convergence problems - ! ** allow liquid water to pass through under very high ice density - iLayerLiqFluxSnow(iLayer) = iLayerLiqFluxSnow(iLayer) + iLayerLiqFluxSnow(iLayer-1) !NOTE: derivative may need to be updated in future. - end if - else ! flow does not occur - iLayerLiqFluxSnow(iLayer) = 0._rkind - iLayerLiqFluxSnowDeriv(iLayer) = 0._rkind - endif ! storage above residual content - end do ! loop through snow layers - ! end association of local variables with information in the data structures - end associate - -end subroutine snowLiqFlxSundials end module snowLiqFlx_module From 26aea7231d1e98f91894a11a2ad24b5b195d47ac Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 20 Mar 2023 23:52:10 +0900 Subject: [PATCH 0591/1472] divide by data_step --- build/source/engine/coupled_em.f90 | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index e7902f0cc..65679f1a5 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -330,7 +330,6 @@ subroutine coupled_em(& call allocLocal(averageFlux_meta(:)%var_info,flux_inner,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - ! initialize surface melt pond sfcMeltPond = 0._rkind ! change in storage associated with the surface melt pond (kg m-2) @@ -919,7 +918,7 @@ subroutine coupled_em(& ! compute the melt in each snow and soil layer if(nSnow>0)& mLayerMeltFreeze(1:nSnow) = -( mLayerVolFracIce(1:nSnow) - mLayerVolFracIceInit(1:nSnow) ) * iden_ice - mLayerMeltFreeze(nSnow+1:nLayers) = -(mLayerVolFracIce(nSnow+1:nLayers) - mLayerVolFracIceInit(nSnow+1:nLayers))*iden_water + mLayerMeltFreeze(nSnow+1:nLayers) = -( mLayerVolFracIce(nSnow+1:nLayers) - mLayerVolFracIceInit(nSnow+1:nLayers) )*iden_water deallocate(mLayerVolFracIceInit) ! * compute change in canopy ice content due to sublimation... @@ -1041,11 +1040,11 @@ subroutine coupled_em(& do iVar=1,size(averageFlux_meta) flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_inner%var(iVar)%dat(:)*dt_wght end do - meanSoilCompress = meanSoilCompress + innerSoilCompress + meanSoilCompress = meanSoilCompress + innerSoilCompress*dt_wght flux_mean%var(childFLUX_MEAN(iLookDIAG%scalarSoilCompress))%dat(1) = meanSoilCompress - flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySublimation))%dat(1) = sumCanopySublimation/whole_step ! these two will be equal unless insufficient canopy water for sublim - flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarLatHeatCanopyEvap))%dat(1) = sumLatHeatCanopyEvap/whole_step ! these two will be equal unless insufficient canopy water for sublim - flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSenHeatCanopy))%dat(1) = sumSenHeatCanopy/whole_step ! these two will be equal unless insufficient canopy water for sublim + flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySublimation))%dat(1) = sumCanopySublimation/data_step ! these two will be equal unless insufficient canopy water for sublim + flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarLatHeatCanopyEvap))%dat(1) = sumLatHeatCanopyEvap/data_step ! these two will be equal unless insufficient canopy water for sublim + flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSenHeatCanopy))%dat(1) = sumSenHeatCanopy/data_step ! these two will be equal unless insufficient canopy water for sublim endif ! save the time step to initialize the subsequent step From b2555d25e987686693341026a9d94e1324c9f6be Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 20 Mar 2023 23:54:01 +0900 Subject: [PATCH 0592/1472] let look for root dir every step --- build/source/engine/summaSolveSundialsIDA.f90 | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index d5905ff69..9e24854b7 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -220,6 +220,7 @@ subroutine summaSolveSundialsIDA( & real(qp) :: tret(1) ! time in data window integer(i4b),allocatable :: rootsfound(:) ! crossing direction of discontinuities integer(i4b),allocatable :: rootdir(:) ! forced crossing direction of discontinuities + logical(lgt) :: tinystep ! if step goes below small size logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true logical(lgt),parameter :: detect_events = .true. ! flag to do event detection and restarting, default true logical(lgt),parameter :: use_fdJac = .false. ! flag to use finite difference Jacobian, default false @@ -432,12 +433,7 @@ subroutine summaSolveSundialsIDA( & eqns_data%scalarAquiferStoragePrev = prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) eqns_data%ixSaturation = ixSaturation - ! call this at beginning of data window to reduce root bouncing (only looking in one direction) - if(detect_events)then - call find_rootdir(eqns_data, rootdir) - retval = FIDASetRootDirection(ida_mem, rootdir) - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetRootDirection'; return; endif - endif + tinystep = .false. !********************************************************************************** !****************************** Main Solver *************************************** @@ -447,6 +443,13 @@ subroutine summaSolveSundialsIDA( & tret(1) = t0 ! intial time do while(tret(1) < dt) + ! call this at beginning of step to reduce root bouncing (only looking in one direction) + if(detect_events .and. .not.tinystep)then + call find_rootdir(eqns_data, rootdir) + retval = FIDASetRootDirection(ida_mem, rootdir) + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetRootDirection'; return; endif + endif + eqns_data%firstFluxCall = .false. eqns_data%firstSplitOper = .true. ! call IDASolve, advance solver just one internal step @@ -566,6 +569,14 @@ subroutine summaSolveSundialsIDA( & ! Reininitialize solver for running after discontinuity and restart retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAReInit'; return; endif + if(dt_last(1) < 0.01_rkind)then ! don't keep calling if step is small + retval = FIDARootInit(ida_mem, 0, c_funloc(layerDisCont4IDA)) + tinystep = .true. + else + retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4IDA)) + tinystep = .false. + endif + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif endif endif From a2728b05d87d20b8a6ac4d52790d36262f18fc7b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 21 Mar 2023 00:22:43 +0900 Subject: [PATCH 0593/1472] all snowLiqFlx should use start of step ice --- build/source/engine/computFlux.f90 | 1 - build/source/engine/snowLiqFlx.f90 | 16 ++++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 25c36108a..c4a3a35f1 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -623,7 +623,6 @@ subroutine computFlux(& scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) ! input: model state vector - mLayerVolFracIceTrial(1:nSnow), & ! intent(in): trial value of volumetric fraction of ice at the current iteration (-) mLayerVolFracLiqTrial(1:nSnow), & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) ! input-output: data structures indx_data, & ! intent(in): model indices diff --git a/build/source/engine/snowLiqFlx.f90 b/build/source/engine/snowLiqFlx.f90 index 7dd0c106a..ea3f1a55e 100644 --- a/build/source/engine/snowLiqFlx.f90 +++ b/build/source/engine/snowLiqFlx.f90 @@ -58,7 +58,6 @@ subroutine snowLiqFlx(& scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) ! input: model state vector - mLayerVolFracIceTrial, & ! intent(in): trial value of volumetric fraction of ice at the current iteration (-) mLayerVolFracLiqTrial, & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) ! input-output: data structures indx_data, & ! intent(in): model indices @@ -79,7 +78,6 @@ subroutine snowLiqFlx(& real(rkind),intent(in) :: scalarThroughfallRain ! computed throughfall rate (kg m-2 s-1) real(rkind),intent(in) :: scalarCanopyLiqDrainage ! computed drainage of liquid water (kg m-2 s-1) ! input: model state vector - real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value of volumetric fraction of ice at the current iteration (-) real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value of volumetric fraction of liquid water at the current iteration (-) ! input-output: data structures type(var_ilength),intent(in) :: indx_data ! model indices @@ -111,6 +109,7 @@ subroutine snowLiqFlx(& ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat, & ! intent(in): index in the state subset for hydrology state variables in the snow domain ! input: snow properties and parameters + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow), & ! intent(in): volumetric ice content at the start of the time step (-) Fcapil => mpar_data%var(iLookPARAM%Fcapil)%dat(1), & ! intent(in): capillary retention as a fraction of the total pore volume (-) k_snow => mpar_data%var(iLookPARAM%k_snow)%dat(1), & ! intent(in): hydraulic conductivity of snow (m s-1), 0.0055 = approx. 20 m/hr, from UEB mw_exp => mpar_data%var(iLookPARAM%mw_exp)%dat(1), & ! intent(in): exponent for meltwater flow (-) @@ -120,10 +119,10 @@ subroutine snowLiqFlx(& ) ! association of local variables with information in the data structures ! ------------------------------------------------------------------------------------------------------------------------------------------ ! initialize error control - err=0; message='snowLiqFlxSundials/' + err=0; message='snowLiqFlx/' ! check that the input vectors match nSnow - if(size(mLayerVolFracLiqTrial)/=nSnow .or. size(mLayerVolFracIceTrial)/=nSnow .or. & + if(size(mLayerVolFracLiqTrial)/=nSnow .or. size(mLayerVolFracIce)/=nSnow .or. & size(iLayerLiqFluxSnow)/=nSnow+1 .or. size(iLayerLiqFluxSnowDeriv)/=nSnow+1) then err=20; message=trim(message)//'size mismatch of input/output vectors'; return end if @@ -151,16 +150,16 @@ subroutine snowLiqFlx(& ! define the liquid flux at the upper boundary (m s-1) iLayerLiqFluxSnow(0) = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water - iLayerLiqFluxSnowDeriv(0) = 0._rkind !computed inside computJacobSundials_module + iLayerLiqFluxSnowDeriv(0) = 0._rkind !computed inside computJacob ! compute properties fixed over the time step if(firstFluxCall)then ! loop through snow layers do iLayer=1,nSnow ! compute the reduction in liquid water holding capacity at high snow density (-) - multResid = 1._rkind / ( 1._rkind + exp( (mLayerVolFracIceTrial(iLayer)*iden_ice - residThrs) / residScal) ) + multResid = 1._rkind / ( 1._rkind + exp( (mLayerVolFracIce(iLayer)*iden_ice - residThrs) / residScal) ) ! compute the pore space (-) - mLayerPoreSpace(iLayer) = 1._rkind - mLayerVolFracIceTrial(iLayer) + mLayerPoreSpace(iLayer) = 1._rkind - mLayerVolFracIce(iLayer) ! compute the residual volumetric liquid water content (-) mLayerThetaResid(iLayer) = Fcapil*mLayerPoreSpace(iLayer) * multResid end do ! (looping through snow layers) @@ -175,7 +174,7 @@ subroutine snowLiqFlx(& relSaturn = (mLayerVolFracLiqTrial(iLayer) - mLayerThetaResid(iLayer)) / availCap ! relative saturation iLayerLiqFluxSnow(iLayer) = k_snow*relSaturn**mw_exp iLayerLiqFluxSnowDeriv(iLayer) = ( (k_snow*mw_exp)/availCap ) * relSaturn**(mw_exp - 1._rkind) - if(mLayerVolFracIceTrial(iLayer) > maxVolIceContent)then ! NOTE: use start-of-step ice content, to avoid convergence problems + if(mLayerVolFracIce(iLayer) > maxVolIceContent)then ! NOTE: use start-of-step ice content, to avoid convergence problems ! ** allow liquid water to pass through under very high ice density iLayerLiqFluxSnow(iLayer) = iLayerLiqFluxSnow(iLayer) + iLayerLiqFluxSnow(iLayer-1) !NOTE: derivative may need to be updated in future. end if @@ -189,4 +188,5 @@ subroutine snowLiqFlx(& end associate end subroutine snowLiqFlx + end module snowLiqFlx_module From c67cabe7023a5d4a37acf0a8be881065807ff170 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 21 Mar 2023 02:03:16 +0900 Subject: [PATCH 0594/1472] fix balance calculation for nSnow 0 in datastep --- build/source/engine/coupled_em.f90 | 20 ++++++++++--------- build/source/engine/summaSolveSundialsIDA.f90 | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 65679f1a5..8290f3bb2 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -212,7 +212,8 @@ subroutine coupled_em(& real(rkind) :: oldSWE ! SWE at the start of the substep real(rkind) :: newSWE ! SWE at the end of the substep real(rkind) :: delSWE ! change in SWE over the subtep - real(rkind) :: effRainfall ! effective rainfall (kg m-2 s-1) + real(rkind) :: innerEffRainfall ! inner step average effective rainfall into snow (kg m-2 s-1) + real(rkind) :: effRainfall ! timestep-average effective rainfall into snow (kg m-2 s-1) real(rkind) :: effSnowfall ! effective snowfall (kg m-2 s-1) real(rkind) :: sfcMeltPond ! surface melt pond (kg m-2) real(rkind) :: massBalance ! mass balance error (kg m-2) @@ -222,6 +223,7 @@ subroutine coupled_em(& type(var_dlength) :: flux_inner ! inner step average model fluxes for a local HRU real(rkind) :: meanSoilCompress ! timestep-average soil compression real(rkind) :: innerSoilCompress ! inner step average soil compression + ! sublimation sums over substep and means over data_step real(rkind) :: sumCanopySublimation ! sum of sublimation from the vegetation canopy (kg m-2 s-1) over substep real(rkind) :: sumSnowSublimation ! sum of sublimation from the snow surface (kg m-2 s-1) over substep @@ -337,12 +339,8 @@ subroutine coupled_em(& do iVar=1,size(averageFlux_meta) flux_mean%var(iVar)%dat(:) = 0._rkind end do - meanSoilCompress = 0._rkind ! mean total soil compression - ! initialize fluxes to average over whole_step (averaged over substep in varSubStep) - do iVar=1,size(averageFlux_meta) - flux_inner%var(iVar)%dat(:) = 0._rkind - end do - innerSoilCompress = 0._rkind ! mean total soil compression + meanSoilCompress = 0._rkind ! mean total soil compression + effRainfall = 0._rkind ! mean total effective rainfall over snow ! associate local variables with information in the data structures associate(& @@ -584,7 +582,7 @@ subroutine coupled_em(& dt_solvInner = 0._rkind ! length of time step that has been completed (s) in whole_step subStep dt_init = min(data_step,whole_step,maxstep_op) ! initial substep length (s) dt_sub = dt_init - dtSave = whole_step ! length of whole substep + dtSave = whole_step ! length of whole substep ! initialize the number of sub-steps nsub=0 @@ -816,6 +814,8 @@ subroutine coupled_em(& do iVar=1,size(averageFlux_meta) flux_inner%var(iVar)%dat(:) = 0._rkind end do + innerSoilCompress = 0._rkind ! mean total soil compression + innerEffRainfall = 0._rkind ! mean total effective rainfall over snow endif ! (do_outer loop) @@ -1025,6 +1025,7 @@ subroutine coupled_em(& flux_inner%var(iVar)%dat(:) = flux_inner%var(iVar)%dat(:) + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:)*dt_wght end do innerSoilCompress = innerSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1)*dt_wght + if (nSnow>0)innerEffRainfall = innerEffRainfall + ( flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) + flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) )*dt_wght ! increment sub-step accepted step dt_solvInner = dt_solvInner + dt_sub @@ -1041,6 +1042,7 @@ subroutine coupled_em(& flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_inner%var(iVar)%dat(:)*dt_wght end do meanSoilCompress = meanSoilCompress + innerSoilCompress*dt_wght + effRainfall = effRainfall + innerEffRainfall*dt_wght flux_mean%var(childFLUX_MEAN(iLookDIAG%scalarSoilCompress))%dat(1) = meanSoilCompress flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySublimation))%dat(1) = sumCanopySublimation/data_step ! these two will be equal unless insufficient canopy water for sublim flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarLatHeatCanopyEvap))%dat(1) = sumLatHeatCanopyEvap/data_step ! these two will be equal unless insufficient canopy water for sublim @@ -1217,7 +1219,7 @@ subroutine coupled_em(& ! check SWE if(nSnow>0)then effSnowfall = averageThroughfallSnow + averageCanopySnowUnloading - effRainfall = averageThroughfallRain + averageCanopyLiqDrainage + !effRainfall is averageThroughfallRain + averageCanopyLiqDrainage only over snow newSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) delSWE = newSWE - (oldSWE - sfcMeltPond) massBalance = delSWE - (effSnowfall + effRainfall + averageSnowSublimation - averageSnowDrainage*iden_water)*data_step diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 9e24854b7..7b826aa9f 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -440,7 +440,7 @@ subroutine summaSolveSundialsIDA( & !************************* loop on one_step mode ********************************** !********************************************************************************** - tret(1) = t0 ! intial time + tret(1) = t0 ! initial time do while(tret(1) < dt) ! call this at beginning of step to reduce root bouncing (only looking in one direction) From 769e84168b9549997c5dde01671d29db319a687f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 22 Mar 2023 16:26:15 +0900 Subject: [PATCH 0595/1472] track installation notes --- sundials_bmi/bmi_interface.txt | 10 +++ sundials_bmi/flags_params_sundials.txt | 19 ++++++ sundials_bmi/installation.txt | 84 ++++++++++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 sundials_bmi/bmi_interface.txt create mode 100644 sundials_bmi/flags_params_sundials.txt create mode 100644 sundials_bmi/installation.txt diff --git a/sundials_bmi/bmi_interface.txt b/sundials_bmi/bmi_interface.txt new file mode 100644 index 000000000..05987af0f --- /dev/null +++ b/sundials_bmi/bmi_interface.txt @@ -0,0 +1,10 @@ +To run a BMI interface, run the executable /bin/summa_bmi.exe +To run as previously, run the executable /bin/summa_sundials.exe with appropriate command line arguments. + +All BMI files are in build/source/driver. The important ones are as following: + +summa_bmi.f90: this contains the code that was in summa_driver.f90 and adds to it the BMI functions. It is now a module and uses the BMI library. + +summa_driver.f90: this is the main program called by summa_sundials.exe that will call all other modules similar to previous editions of the code. + +summa_driverBMI.f90: this is the main program called by summa_bmi.exe that will call all other modules in the BMI interface mode. \ No newline at end of file diff --git a/sundials_bmi/flags_params_sundials.txt b/sundials_bmi/flags_params_sundials.txt new file mode 100644 index 000000000..30dbebb11 --- /dev/null +++ b/sundials_bmi/flags_params_sundials.txt @@ -0,0 +1,19 @@ + +To switch between SUMMA-BE and SUMMA-SUNDIALS, the num_method in the model_decision file can be either one of the values "bEuler" (choice "itertive" is backward compatible) or "sundials". + +In energy equation, the heat capacity is computed using either the analytical (closed) formula or the finite difference formula (dH_T/dT). The "howHeatCap" variable has been added to the var_lookup module to handle such decision. A user should add this variable to the model_decision file with one of the values "closedForm" or "enthalpyFD". Choice of num_method as "itertive" will set num_method=bEuler and howHeatCap=closedForm. + +All SUMMA-SUNDIALS files are in build/source/engine. The important ones are as following: + +systemSolvSundials.f90: contains public subroutine systemSolvSundials which runs the coupled energy-mass model for one timestep + +summaSolveSundialsIDA.f90: contains public subroutine summaSolveSundialsIDA which solves the differential equation system F(y,y') = 0 by IDA (y is the state vector) and private subroutines setInitialCondition and setSolverParams. Subroutine setSolverParams can be used to to set parameters (maximum order, number of nonlinear iteration , etc) in IDA solver + +computResidSundials.f90: contains public function computResidSundials which is the interface wrapper for computing the residual vector F(t,y,y') required for IDA solver + +eval8summaSundials.f90: contains public subroutine eval8summaSundials which computes the residual vector mainly by calling varExtract, updateVarsSundials, computFlux,and computResidSundials. We also switch between different forms of the energy equation in this subroutine. + +computJacobSundials.f90: contains public function computJacobSundials which is the interface wrapper for computing the Jacobian matrix dF/dy + c dF/dy' for IDA solver + +eval8JacDAE.f90: contains public subroutine eval8JacDAE which computes the residual vector and the Jacobian matrix mainly by calling computJacobSundials + diff --git a/sundials_bmi/installation.txt b/sundials_bmi/installation.txt new file mode 100644 index 000000000..c27ce4ecd --- /dev/null +++ b/sundials_bmi/installation.txt @@ -0,0 +1,84 @@ + +1. First install BMI. Download the latest release of BMI from https://github.com/csdms/bmi-fortran.git +% git clone https://github.com/csdms/bmi-fortran.git + +2. Make a directory outside the bmi-fortran folder and enter it, and make the install and build dirs, enter the build dir. +% export BMIF_VERSION=2.0 +% mkdir bmi +% cd bmi +% mkdir instdir +% mkdir buildir +% cd /buildir +NOTE if you need to recompile after a system upgrade, delete the contents of these directories before running cmake in the next step! + +3. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, the following setting needs to be enabled while running the cmake inside the builddir, using your home directory as $(YOUR_HOME) +% cmake ../../bmi-fortran/ -DCMAKE_INSTALL_PREFIX=$(YOUR_HOME)/bmi/instdir + +-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON -DCMAKE_INSTALL_RPATH=/Users/amedin/Research/SummaSundials/bmi/instdir/lib -DCMAKE_SKIP_BUILD_RPATH=OFF -DCMAKE_BUILD_RPATH=/Users/amedin/Research/SummaSundials/bmi/instdir/lib + + +INSTALL_RPATH to a specific folder and set BUILD_WITH_INSTALL_RPATH to TRUE +You can do this by running from inside buildir, where the star is the system specific file with hardcoded paths: +cp ../../summa/build/build_cmakeBMI_* build_cmake +./build_cmake + +4. The default compiler is gfortan. To change it (it should be the same as the netcdf build and the later summa build), you could also add the following option to cmake with your $(YOUR_GFORTRAN) + -DCMAKE_Fortran_COMPILER=$(YOUR_GFORTRAN) + +5. If the above went well, staying in the buildir directory run + +#!/usr/bin/env bash +# Updates runtime paths for executables on macOS. +install_name_tool -change @rpath/libbmif.2.0.dylib $(YOUR_HOME)/bmi/instdir/lib/libbmif.2.0.dylib + +% make +% make install + +6. If you are using Sundials, now install that. Download the latest release of IDA solver from SUNDIALS package in https://computing.llnl.gov/projects/sundials/sundials-software +% wget "https://github.com/LLNL/sundials/releases/download/v6.3.0/sundials-6.3.0.tar.gz" + +7. Extract the corresponding compressed file +% tar -xzf sundials-6.3.0.tar.gz + +8. Read INSTALL_GUIDE.pdf and follow the installation instructions. Roughly, do the following: +Make a directory outside the sundials-6.3.0 folder and enter it, and make the install and build dirs, enter the build dir. +% mkdir sundials +% cd sundials +% mkdir instdir +% mkdir buildir +% cd /buildir +NOTE if you need to recompile after a system upgrade, delete the contents of these directories before running cmake in the next step! + +9. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, the following setting needs to be enabled while running the cmake inside the builddir, using your home directory as $(YOUR_HOME) +% cmake ../sundials-5.8.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON DCMAKE_INSTALL_PREFIX=$(YOUR_HOME)/sundials/instdir -DEXAMPLES_INSTALL_PATH=$(YOUR_HOME)/sundials/instdir/examples +You can do this by running from inside buildir, where the star is the system specific file with hardcoded paths: +cp ../../summa/build/build_cmakeSundials_* build_cmake +./build_cmake + +10. The default compiler is gfortan. To change it (it should be the same as the netcdf build and the later summa build), you could also add the following option to cmake with your $(YOUR_GFORTRAN) + -DCMAKE_Fortran_COMPILER=$(YOUR_GFORTRAN) + +11. If the above went well, staying in the buildir directory run +% make +% make install + +12. Enter summa directory and build summa as in /docs/SUMMA_installation.md. You will have to edit the SUMMA Makefile as below additionally (as well as install required libraries and link them as directed in the Makefile). You need to clone summa before you can change the makefile + git clone https://git.cs.usask.ca/numerical_simulations_lab/summa.git + +13. In SUMMA Makefile, define the FC_EXE to be the same compiler as you used for BMI and netcdf + +14. In SUMMA Makefile, define the SUNDIALS installation directory (and all the folders as required in the SUMMA instructions. + DIR_SUNDIALS=$(YOUR_HOME)/sundials/instdir + +15. Inside the /summa/build/ directory run +% make +or % source build_summa_* if you are using a specific * compiler + +16. On linux installations you may need to add to your .bashrc file (and run source $(YOUR_HOME)/.bashrc) +export LD_LIBRARY_PATH=$(YOUR_HOME)/sundials/instdir/lib64 +export LD_LIBRARY_PATH=$(YOUR_HOME)/bmi/instdir/lib64 + +This does not work on macOS, sundials libraries should be found as complied but the BMI libraries will need to use the Xlinker commands +LIB_BMI=-L$(DIR_BMI)/lib -lbmif -Xlinker -rpath -Xlinker $(DIR_BMI)/lib + + From 0eaebecacd80ad8014484b9974afc8fd42afcf53 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 22 Mar 2023 17:04:40 +0900 Subject: [PATCH 0596/1472] updating summa to latest master --- build/makefile | 412 ---- build/my_makefile_cop | 1 - build/my_makefile_mac | 1 - build/source/engine/computFlux.f90 | 30 +- build/source/engine/computJacobSundials.f90 | 3 - build/source/engine/computResid.f90 | 41 +- build/source/engine/computResidSundials.f90 | 66 +- build/source/engine/computSnowDepth.f90 | 86 +- build/source/engine/coupled_em.f90 | 737 +++--- build/source/engine/eval8summa.f90 | 39 +- build/source/engine/eval8summaSundials.f90 | 55 +- build/source/engine/opSplittin.f90 | 235 +- build/source/engine/snowLiqFlx.f90 | 147 +- build/source/engine/soilLiqFlx.f90 | 2 +- build/source/engine/soil_utils.f90 | 8 +- build/source/engine/summaSolve.f90 | 13 +- build/source/engine/summaSolveSundialsIDA.f90 | 364 ++- build/source/engine/systemSolv.f90 | 28 +- build/source/engine/systemSolvSundials.f90 | 35 +- build/source/engine/tol4IDA.f90 | 1 - build/source/engine/type4IDA.f90 | 108 +- build/source/engine/updatState.f90 | 25 +- build/source/engine/updatStateSundials.f90 | 34 +- build/source/engine/updateVarsSundials.f90 | 16 +- build/source/engine/varSubstep.f90 | 2003 +++++++++-------- build/source/engine/varSubstepSundials.f90 | 1126 --------- build/source/engine/volicePack.f90 | 46 +- 27 files changed, 2178 insertions(+), 3484 deletions(-) delete mode 100644 build/makefile delete mode 100644 build/source/engine/varSubstepSundials.f90 diff --git a/build/makefile b/build/makefile deleted file mode 100644 index cc9363107..000000000 --- a/build/makefile +++ /dev/null @@ -1,412 +0,0 @@ -#======================================================================== -# Makefile to compile SUMMA SUNDIALS -#======================================================================== -# -# Recommended use: Copy this file to Makefile.local, edit it to your -# heart's content, and then run `make -f build/Makefile.local` from -# your top level SUMMA directory. Don't include the Makefile.local in -# any pull requests you make. -# -# Note that Makefile configurations that we commonly use can be found on -# the SUMMA wiki at: -# https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration -# feel free to add yours to that page. -# -# To troubleshoot your paths and setup, type 'make check' -# -# At a minimum you will need to set the following: -# * F_MASTER - top level summa directory -# * FC - compiler suite -# * FC_EXE - compiler executable -# * INCLUDES - path to include files -# * ILDFLAGS - path to libraries to include -# * LIBRARIES - libraries to include -# * DIR_SUNDIALS - installation sundials directory -# -# Some further options can be specified for OpenMP, etc. See in Part 0 of -# the Makefile. You don't need to make any changes in PART 1 and -# following unless you are doing SUMMA development and changed what -# needs to be compiled - -#======================================================================== -# PART 0: User-configurable part -#======================================================================== - -# The variables can be specified in one of two ways: -# * delete the '##' in front of the variable, fill out the entry, -# save the file and run make -# * make no changes to this file, but specify the variables in your -# environment before you run make - -# Define core directory below which everything resides. This is the -# parent directory of the 'build' directory -F_MASTER =/SUMMA - -# Define the Fortran Compiler. If you are using gfortran, then this needs -# to be version 6 or higher. This variable is simply used to select the right -# compiler flags in the ifeq statements in this Makefile. The compiler -# executable is set separately as FC_EXE -# Currently this is either gfortran or ifort -FC = gfortran - -# Define the path for the compiler executable. This is the actual executable -# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin/gfortran-mp-11 -# FC and FC_EXE have to be consistent -FC_EXE = gfortran - -# Define the NetCDF and LAPACK libraries and path to include files. -# INCLUDES needs to be of the form (no quotes around the string): -# INCLUDES = -I -I -I<...> -I -# LIBRARIES needs to be of the form ( no quotes around the string): -# LIBRARIES = '-L -lnetcdff -L -lblas -L -l' -# If none of this makes sense, please talk to your system -# administrator. -INCLUDES = -I/usr/include -I/usr/local/include -LIBRARIES = -L/usr/lib -L/usr/local/lib -lnetcdff -lopenblas - -DIR_SUNDIALS=/code/sundials/instdir -INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran -LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod - -# Eventually we plan move to a real configure script, but for now we like -# to keep track of successful compilations of SUMMA on different platforms -# and with different compilers. If you are successful compiling SUMMA, -# please add your configuration (operating system and compiler plus -# part 0 of the Makefile) to the SUMMA wiki on github. - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# ------------ define compiler flags ---------------------------------------- - -# define open MP flags -isOpenMP = -FLAGS_OMP = -LIBOPENMP = - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# gfortran compiler flags -ifeq "$(FC)" "gfortran" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -fopenmp - endif - -# Production runs -FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) - -# Debug runs -# FLAGS_NOAH = -p -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -Wall -# FLAGS_COMM = -p -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -# FLAGS_SUMMA = -p -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds - -endif - -# ifort compiler flags -ifeq "$(FC)" "ifort" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -qopenmp - endif - -# Production runs -FLAGS_NOAH = -O3 -noerror_limit -FR -auto -fltconsistency $(FLAGS_OMP) -FLAGS_COMM = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) - -# Debug runs -#FLAGS_NOAH = -O0 -p -g -warn nounused -noerror_limit -FR -auto -WB -traceback -fltconsistency -#FLAGS_COMM = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -#FLAGS_SUMMA = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -endif - -#======================================================================== -# PART 1: Define directory paths -#======================================================================== - -# Core directory that contains source code -F_KORE_DIR = $(F_MASTER)/build/source - -# Location of the compiled modules -MOD_PATH = $(F_MASTER)/build - -# Define the directory for the executables -EXE_PATH = $(F_MASTER)/bin - -#======================================================================== -# PART 2: Assemble all of the SUMMA sub-routines -#======================================================================== - -# Define directories -DRIVER_DIR = $(F_KORE_DIR)/driver -HOOKUP_DIR = $(F_KORE_DIR)/hookup -NETCDF_DIR = $(F_KORE_DIR)/netcdf -DSHARE_DIR = $(F_KORE_DIR)/dshare -NUMREC_DIR = $(F_KORE_DIR)/numrec -NOAHMP_DIR = $(F_KORE_DIR)/noah-mp -ENGINE_DIR = $(F_KORE_DIR)/engine - -# utilities -SUMMA_NRUTIL= \ - nrtype.f90 \ - f2008funcs.f90 \ - nr_utility.f90 -NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) - -# -# Numerical recipes procedures -# NOTE: all numerical recipes procedures are now replaced with free versions -SUMMA_NRPROC= \ - expIntegral.f90 \ - spline_int.f90 -NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) - -# Hook-up modules (set files and directory paths) -SUMMA_HOOKUP= \ - ascii_util.f90 \ - summaFileManager.f90 - -HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) - -# Data modules -SUMMA_DATAMS= \ - multiconst.f90 \ - var_lookup.f90 \ - data_types.f90 \ - globalData.f90 \ - flxMapping.f90 \ - get_ixname.f90 \ - popMetadat.f90 \ - outpt_stat.f90 -DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) - -# utility modules -SUMMA_UTILMS= \ - time_utils.f90 \ - mDecisions.f90 \ - snow_utils.f90 \ - soil_utils.f90 \ - soil_utilsAddSundials.f90 \ - updatState.f90 \ - updatStateSundials.f90 \ - matrixOper.f90 -UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) - -# Model guts -SUMMA_MODGUT= \ - MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) - -# Solver -SUMMA_SOLVER= \ - vegPhenlgy.f90 \ - diagn_evar.f90 \ - stomResist.f90 \ - groundwatr.f90 \ - vegSWavRad.f90 \ - vegNrgFlux.f90 \ - ssdNrgFlux.f90 \ - vegLiqFlux.f90 \ - snowLiqFlx.f90 \ - soilLiqFlx.f90 \ - bigAquifer.f90 \ - computFlux.f90 \ - computResid.f90 \ - computJacob.f90 \ - eval8summa.f90 \ - summaSolve.f90 \ - systemSolv.f90 \ - type4IDA.f90 \ - tol4IDA.f90 \ - computEnthalpy.f90 \ - computHeatCap.f90 \ - computThermConduct.f90 \ - computResidSundials.f90 \ - eval8summaSundials.f90 \ - computJacobSundials.f90 \ - computSnowDepth.f90 \ - summaSolveSundialsIDA.f90 \ - systemSolvSundials.f90 \ - varSubstep.f90 \ - varSubstepSundials.f90 \ - opSplittin.f90 \ - coupled_em.f90 \ - run_oneHRU.f90 \ - run_oneGRU.f90 -SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) - -# Define routines for SUMMA preliminaries -SUMMA_PRELIM= \ - conv_funcs.f90 \ - sunGeomtry.f90 \ - convE2Temp.f90 \ - allocspace.f90 \ - checkStruc.f90 \ - childStruc.f90 \ - ffile_info.f90 \ - read_attrb.f90 \ - read_pinit.f90 \ - pOverwrite.f90 \ - read_param.f90 \ - paramCheck.f90 \ - check_icond.f90 -PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) - -SUMMA_NOAHMP= \ - module_model_constants.F \ - module_sf_noahutl.F \ - module_sf_noahlsm.F \ - module_sf_noahmplsm.F -NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) - -# Define routines for the SUMMA model runs -SUMMA_MODRUN = \ - indexState.f90 \ - getVectorz.f90 \ - t2enthalpy.f90 \ - updateVars.f90 \ - updateVarsSundials.f90 \ - var_derive.f90 \ - read_force.f90 \ - derivforce.f90 \ - snowAlbedo.f90 \ - canopySnow.f90 \ - tempAdjust.f90 \ - snwCompact.f90 \ - layerMerge.f90 \ - layerDivide.f90 \ - volicePack.f90 \ - qTimeDelay.f90 -MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) - -# Define routines for the solver -SUMMA_MSOLVE = \ - -# Define NetCDF routines -SUMMA_NETCDF = \ - netcdf_util.f90 \ - def_output.f90 \ - modelwrite.f90 \ - read_icond.f90 -NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) - -# ... stitch together common programs -COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) - -# ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) - -# Define the driver routine -SUMMA_DRIVER= \ - summa_type.f90 \ - summa_util.f90 \ - summa_alarms.f90 \ - summa_globalData.f90 \ - summa_defineOutput.f90 \ - summa_init.f90 \ - summa_setup.f90 \ - summa_restart.f90 \ - summa_forcing.f90 \ - summa_modelRun.f90 \ - summa_writeOutput.f90 \ - summa_driver.f90 -DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) - -# Define the executable -DRIVER__EX = summa_sundials.exe - -# Define version number -VERSIONFILE = $(DRIVER_DIR)/summaversion.inc -VERSION = $(shell git tag | tail -n 1) -BULTTIM = $(shell date) -GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') -GITHASH = $(shell git rev-parse HEAD) - -#======================================================================== -# PART 3: Checks -#====================================================================== -# make sure that the paths are defined. These are just some high level checks -ifndef F_MASTER - $(error F_MASTER is undefined) -endif -ifndef FC - $(error FC is undefined: Specify your compiler) -endif -ifndef FC_EXE - $(error FC_EXE is undefined: Specify your compiler executable) -endif -ifndef FLAGS_SUMMA - $(error Specify flags for your compiler: $(FC)) -endif -ifndef INCLUDES - $(error INCLUDES is undefined) -endif -ifndef LIBRARIES - $(error LIBRARIES is undefined) -endif - -#======================================================================== -# PART 4: compilation -#====================================================================== - -# Compile -all: compile_noah compile_comm compile_summa link clean install -part: compile_noah compile_comm compile_summa - -check: - $(info) - $(info Displaying make variables:) - $(info F_MASTER : $(F_MASTER)) - $(info EXE_PATH : $(EXE_PATH)) - $(info FC : $(FC)) - $(info INCLUDES : $(INCLUDES)) - $(info LIBRARIES : $(LIBRARIES)) - $(info FLAGS_NOAH : $(FLAGS_NOAH)) - $(info FLAGS_COMM : $(FLAGS_COMM)) - $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) - $(info SUNDIALSDIR: $(DIR_SUNDIALS)) - $(info INC_SUNDIALS: $(INC_SUNDIALS)) - $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) - $(info) - -# update version information -update_version: - echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) - echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) - -# compile Noah-MP routines -compile_noah: - $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) - -# compile common routines - -compile_comm: - $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) - -# compile SUMMA routines -compile_summa: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ - $(INCLUDES) $(INC_SUNDIALS) - -# link routines -link: - $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) - -# Remove object files -clean: - rm -f *.o - rm -f *.mod - rm -f soil_veg_gen_parm__genmod.f90 - -# Copy the executable to the bin directory -install: - @mkdir -p $(EXE_PATH) - @mv $(DRIVER__EX) $(EXE_PATH) - $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) diff --git a/build/my_makefile_cop b/build/my_makefile_cop index 143e8bbd4..2419b0941 100644 --- a/build/my_makefile_cop +++ b/build/my_makefile_cop @@ -238,7 +238,6 @@ SUMMA_SOLVER= \ summaSolveSundialsIDA.f90 \ systemSolvSundials.f90 \ varSubstep.f90 \ - varSubstepSundials.f90 \ opSplittin.f90 \ coupled_em.f90 \ run_oneHRU.f90 \ diff --git a/build/my_makefile_mac b/build/my_makefile_mac index 00c67eacc..f35600792 100644 --- a/build/my_makefile_mac +++ b/build/my_makefile_mac @@ -238,7 +238,6 @@ SUMMA_SOLVER= \ summaSolveSundialsIDA.f90 \ systemSolvSundials.f90 \ varSubstep.f90 \ - varSubstepSundials.f90 \ opSplittin.f90 \ coupled_em.f90 \ run_oneHRU.f90 \ diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 04245d10a..c4a3a35f1 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -939,6 +939,7 @@ end subroutine computFlux ! ********************************************************************************************************** subroutine soilCmpres(& ! input: + dt, & ! intent(in): length of the time step (seconds) ixRichards, & ! intent(in): choice of option for Richards' equation ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers mLayerMatricHead, & ! intent(in): matric head at the start of the time step (m) @@ -948,22 +949,23 @@ subroutine soilCmpres(& specificStorage, & ! intent(in): specific storage coefficient (m-1) theta_sat, & ! intent(in): soil porosity (-) ! output: - compress, & ! intent(out): compressibility of the soil matrix (-) + compress, & ! intent(out): compressibility of the soil matrix (-), per second dCompress_dPsi, & ! intent(out): derivative in compressibility w.r.t. matric head (m-1) err,message) ! intent(out): error code and error message implicit none ! input: + real(rkind),intent(in) :: dt ! length of the time step (seconds) integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation integer(i4b),intent(in) :: ixBeg,ixEnd ! start and end indices defining desired layers - real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head at the start of the time step (m) - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial value for matric head (m) - real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) - real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) - real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) - real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) + real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head at the start of the time step (m) + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial value for matric head (m) + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) + real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) + real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) ! output: - real(rkind),intent(inout) :: compress(:) ! soil compressibility (-) - real(rkind),intent(inout) :: dCompress_dPsi(:) ! derivative in soil compressibility w.r.t. matric head (m-1) + real(rkind),intent(inout) :: compress(:) ! soil compressibility (-) + real(rkind),intent(inout) :: dCompress_dPsi(:) ! derivative in soil compressibility w.r.t. matric head (m-1) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local variables @@ -975,10 +977,10 @@ subroutine soilCmpres(& if(ixRichards==mixdform)then do iLayer=1,size(mLayerMatricHead) if(iLayer>=ixBeg .and. iLayer<=ixEnd)then - ! compute the derivative for the compressibility term (m-1) + ! compute the derivative for the compressibility term (m-1), no volume expansion for total water dCompress_dPsi(iLayer) = specificStorage*(mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer))/theta_sat(iLayer) - ! compute the compressibility term (-) - compress(iLayer) = (mLayerMatricHeadTrial(iLayer) - mLayerMatricHead(iLayer))*dCompress_dPsi(iLayer) + ! compute the compressibility term (-) per second + compress(iLayer) = (mLayerMatricHeadTrial(iLayer) - mLayerMatricHead(iLayer))*dCompress_dPsi(iLayer)/dt endif end do else @@ -1027,9 +1029,9 @@ subroutine soilCmpresSundials(& if(ixRichards==mixdform)then do iLayer=1,size(mLayerMatricHeadPrime) if(iLayer>=ixBeg .and. iLayer<=ixEnd)then - ! compute the derivative for the compressibility term (m-1) + ! compute the derivative for the compressibility term (m-1), no volume expansion for total water dCompress_dPsi(iLayer) = specificStorage*(mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer))/theta_sat(iLayer) - ! compute the compressibility term (-) + ! compute the compressibility term (-) instantaneously compress(iLayer) = mLayerMatricHeadPrime(iLayer) * dCompress_dPsi(iLayer) endif end do diff --git a/build/source/engine/computJacobSundials.f90 b/build/source/engine/computJacobSundials.f90 index 877982cc1..2c31cab01 100644 --- a/build/source/engine/computJacobSundials.f90 +++ b/build/source/engine/computJacobSundials.f90 @@ -1283,14 +1283,11 @@ subroutine computJacobSetup(& call updateVarsSundials(& ! input - dt, & ! intent(in): time step .true., & ! intent(in): logical flag if computing Jacobian for sundials solver .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU - mLayerVolFracWatTrial, & ! intent(in): use current vector for prev vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): use current vector for prev vector of total water matric potential (m) diag_data, & ! intent(inout): model diagnostic variables for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy diff --git a/build/source/engine/computResid.f90 b/build/source/engine/computResid.f90 index 9209c78d4..5bc6805d7 100644 --- a/build/source/engine/computResid.f90 +++ b/build/source/engine/computResid.f90 @@ -86,13 +86,17 @@ subroutine computResid(& ! input: state variables (already disaggregated into scalars and vectors) scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) - scalarCanopyHydTrial, & ! intent(in): trial value of canopy water (kg m-2), either liquid water content or total water content + scalarCanopyWatTrial, & ! intent(in): trial value for the water on the vegetation canopy (kg m-2) mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) - mLayerVolFracHydTrial, & ! intent(in): trial vector of volumetric water content (-), either liquid water content or total water content scalarAquiferStorageTrial, & ! intent(in): trial value of storage of water in the aquifer (m) ! input: diagnostic variables defining the liquid water and ice content (function of state variables) scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value for the liq on the vegetation canopy (kg m-2) mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) + mLayerVolFracWatTrial, & ! intent(in): trial value for the volumetric water in each snow and soil layer (-) + mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liq in each snow and soil layer (-) + scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) + mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -115,13 +119,17 @@ subroutine computResid(& ! input: state variables (already disaggregated into scalars and vectors) real(rkind),intent(in) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind),intent(in) :: scalarCanopyHydTrial ! trial value for canopy water (kg m-2), either liquid water content or total water content + real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value for canopy total water content (kg m-2) real(rkind),intent(in) :: mLayerTempTrial(:) ! trial value for temperature of each snow/soil layer (K) - real(rkind),intent(in) :: mLayerVolFracHydTrial(:) ! trial vector of volumetric water content (-), either liquid water content or total water content real(rkind),intent(in) :: scalarAquiferStorageTrial ! trial value of aquifer storage (m) ! input: diagnostic variables defining the liquid water and ice content (function of state variables) real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: scalarCanopyLiqTrial ! trial value for the liq on the vegetation canopy (kg m-2) real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) + real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial value for the volumetric water in each snow and soil layer (-) + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for the volumetric water in each snow and soil layer (-) + real(qp),intent(in) :: scalarCanopyCmTrial ! Cm of vegetation canopy (-) + real(qp),intent(in) :: mLayerCmTrial(:) ! Cm of each snow and soil layer (-) ! input: data structures type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU @@ -137,7 +145,9 @@ subroutine computResid(& ! -------------------------------------------------------------------------------------------------------------------------------- integer(i4b) :: iLayer ! index of layer within the snow+soil domain integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) + real(rkind) :: scalarCanopyHydTrial ! trial value of canopy water content (kg m-2), either liquid water content or total water content real(rkind) :: scalarCanopyHyd ! canopy water content (kg m-2), either liquid water content or total water content + real(rkind),dimension(nLayers) :: mLayerVolFracHydTrial ! trial vector of volumetric water content (-), either liquid water content or total water content real(rkind),dimension(nLayers) :: mLayerVolFracHyd ! vector of volumetric water content (-), either liquid water content or total water content ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- @@ -212,7 +222,7 @@ subroutine computResid(& ! NOTE 4: same sink terms for matric head and liquid matric potential if(nSoilOnlyHyd>0)then do concurrent (iLayer=1:nSoil,ixSoilOnlyHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) - rAdd( ixSoilOnlyHyd(iLayer) ) = rAdd( ixSoilOnlyHyd(iLayer) ) + dt*(mLayerTranspire(iLayer) - mLayerBaseflow(iLayer) )/mLayerDepth(iLayer+nSnow) - mLayerCompress(iLayer) + rAdd( ixSoilOnlyHyd(iLayer) ) = rAdd( ixSoilOnlyHyd(iLayer) ) + dt*(mLayerTranspire(iLayer) - mLayerBaseflow(iLayer) )/mLayerDepth(iLayer+nSnow) - dt*mLayerCompress(iLayer) end do ! looping through non-missing energy state variables in the snow+soil domain endif @@ -223,19 +233,21 @@ subroutine computResid(& ! compute the residual vector for the vegetation canopy ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision ! --> energy balance - if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*scalarCanairTempTrial - ( (sMul(ixCasNrg)*scalarCanairTemp + fVec(ixCasNrg)*dt) + rAdd(ixCasNrg) ) - if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg)*scalarCanopyTempTrial - ( (sMul(ixVegNrg)*scalarCanopyTemp + fVec(ixVegNrg)*dt) + rAdd(ixVegNrg) ) - + if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*scalarCanairTempTrial - ( sMul(ixCasNrg)*scalarCanairTemp + fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg)*scalarCanopyTempTrial + scalarCanopyCmTrial * scalarCanopyWatTrial/canopyDepth & + - ( sMul(ixVegNrg)*scalarCanopyTemp + scalarCanopyCmTrial * scalarCanopyWat/canopyDepth + fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) ! --> mass balance if(ixVegHyd/=integerMissing)then - scalarCanopyHyd = merge(scalarCanopyWat, scalarCanopyLiq, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) - rVec(ixVegHyd) = sMul(ixVegHyd)*scalarCanopyHydTrial - ( (sMul(ixVegHyd)*scalarCanopyHyd + fVec(ixVegHyd)*dt) + rAdd(ixVegHyd) ) + scalarCanopyHydTrial = merge(scalarCanopyWatTrial, scalarCanopyLiqTrial, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) + scalarCanopyHyd = merge(scalarCanopyWat, scalarCanopyLiq, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) + rVec(ixVegHyd) = sMul(ixVegHyd)*scalarCanopyHydTrial - ( sMul(ixVegHyd)*scalarCanopyHyd + fVec(ixVegHyd)*dt + rAdd(ixVegHyd) ) endif ! compute the residual vector for the snow and soil sub-domains for energy if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) )*mLayerTempTrial(iLayer) - ( (sMul( ixSnowSoilNrg(iLayer) )*mLayerTemp(iLayer) + fVec( ixSnowSoilNrg(iLayer) )*dt) + rAdd( ixSnowSoilNrg(iLayer) ) ) + rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) )*mLayerTempTrial(iLayer) + mLayerCmTrial(iLayer) * mLayerVolFracWatTrial(iLayer) & + - ( sMul( ixSnowSoilNrg(iLayer) )*mLayerTemp(iLayer) + mLayerCmTrial(iLayer) * mLayerVolFracWat(iLayer) + fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) end do ! looping through non-missing energy state variables in the snow+soil domain endif @@ -244,14 +256,15 @@ subroutine computResid(& if(nSnowSoilHyd>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) ! (get the correct state variable) - mLayerVolFracHyd(iLayer) = merge(mLayerVolFracWat(iLayer), mLayerVolFracLiq(iLayer), (ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_matLayer) ) + mLayerVolFracHydTrial(iLayer) = merge(mLayerVolFracWatTrial(iLayer), mLayerVolFracLiqTrial(iLayer) , (ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_matLayer) ) + mLayerVolFracHyd(iLayer) = merge(mLayerVolFracWat(iLayer), mLayerVolFracLiq(iLayer), (ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_matLayer) ) ! (compute the residual) - rVec( ixSnowSoilHyd(iLayer) ) = mLayerVolFracHydTrial(iLayer) - ( (mLayerVolFracHyd(iLayer) + fVec( ixSnowSoilHyd(iLayer) )*dt) + rAdd( ixSnowSoilHyd(iLayer) ) ) + rVec( ixSnowSoilHyd(iLayer) ) = mLayerVolFracHydTrial(iLayer) - ( mLayerVolFracHyd(iLayer) + fVec( ixSnowSoilHyd(iLayer) )*dt + rAdd( ixSnowSoilHyd(iLayer) ) ) end do ! looping through non-missing energy state variables in the snow+soil domain endif ! compute the residual vector for the aquifer - if(ixAqWat/=integerMissing) rVec(ixAqWat) = sMul(ixAqWat)*scalarAquiferStorageTrial - ( (sMul(ixAqWat)*scalarAquiferStorage + fVec(ixAqWat)*dt) + rAdd(ixAqWat) ) + if(ixAqWat/=integerMissing) rVec(ixAqWat) = sMul(ixAqWat)*scalarAquiferStorageTrial - ( sMul(ixAqWat)*scalarAquiferStorage + fVec(ixAqWat)*dt + rAdd(ixAqWat) ) if(globalPrintFlag)then write(*,'(a,1x,100(e12.5,1x))') 'rVec = ', rVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) diff --git a/build/source/engine/computResidSundials.f90 b/build/source/engine/computResidSundials.f90 index 05bab6bad..31aa7f35c 100644 --- a/build/source/engine/computResidSundials.f90 +++ b/build/source/engine/computResidSundials.f90 @@ -66,21 +66,21 @@ subroutine computResidSundials(& sMul, & ! intent(in): state vector multiplier (used in the residual calculations) fVec, & ! intent(in): flux vector ! input: state variables (already disaggregated into scalars and vectors) - scalarCanopyTempTrial, & ! intent(in): - mLayerTempTrial, & ! intent(in) - scalarCanairTempPrime, & ! intent(in): trial value for the temperature of the canopy air space (K) - scalarCanopyTempPrime, & ! intent(in): trial value for the temperature of the vegetation canopy (K) - scalarCanopyWatPrime, & ! - mLayerTempPrime, & ! intent(in): trial value for the temperature of each snow and soil layer (K) water content - scalarAquiferStoragePrime, & ! intent(in): trial value of storage of water in the aquifer (m) + scalarCanopyTempTrial, & ! intent(in):: trial value for the temperature of the vegetation canopy (K) + mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) + scalarCanairTempPrime, & ! intent(in): Prime value for the temperature of the canopy air space (K s-1) + scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K s-1) + scalarCanopyWatPrime, & ! intent(in): Prime value for the water on the vegetation canopy (kg m-2 s-1) + mLayerTempPrime, & ! intent(in): Prime value for the temperature of each snow and soil layer (K s-1) + scalarAquiferStoragePrime, & ! intent(in): Prime value of storage of water in the aquifer (m s-1) ! input: diagnostic variables defining the liquid water and ice content (function of state variables) - scalarCanopyIcePrime, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) - scalarCanopyLiqPrime, & ! intent(in): - mLayerVolFracIcePrime, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) - mLayerVolFracWatPrime, & - mLayerVolFracLiqPrime, & - scalarCanopyCmTrial, & - mLayerCmTrial, & ! intent(in) + scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2 s-1) + scalarCanopyLiqPrime, & ! intent(in): Prime value for the liq on the vegetation canopy (kg m-2 s-1) + mLayerVolFracIcePrime, & ! intent(in): Prime value for the volumetric ice in each snow and soil layer (s-1) + mLayerVolFracWatPrime, & ! intent(in): Prime value for the volumetric water in each snow and soil layer (s-1) + mLayerVolFracLiqPrime, & ! intent(in): Prime value for the volumetric liq in each snow and soil layer (s-1) + scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) + mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -101,21 +101,21 @@ subroutine computResidSundials(& real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(rkind),intent(in) :: fVec(:) ! flux vector ! input: state variables (already disaggregated into scalars and vectors) - real(rkind),intent(in) :: scalarCanopyTempTrial - real(rkind),intent(in) :: mLayerTempTrial(:) - real(rkind),intent(in) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) - real(rkind),intent(in) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) - real(rkind),intent(in) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) - real(rkind),intent(in) :: mLayerTempPrime(:) ! trial value for temperature of each snow/soil layer (K) content - real(rkind),intent(in) :: scalarAquiferStoragePrime ! trial value of aquifer storage (m) + real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind),intent(in) :: mLayerTempTrial(:) ! trial value for the temperature of each snow and soil layer (K) + real(rkind),intent(in) :: scalarCanairTempPrime ! Prime value for temperature of the canopy air space (K s-1) + real(rkind),intent(in) :: scalarCanopyTempPrime ! Prime value for temperature of the vegetation canopy (K s-1) + real(rkind),intent(in) :: scalarCanopyWatPrime ! Prime value for canopy total water content (kg m-2 s-1) + real(rkind),intent(in) :: mLayerTempPrime(:) ! Prime value for temperature of each snow/soil layer (K s-1) content + real(rkind),intent(in) :: scalarAquiferStoragePrime ! Prime value of aquifer storage (m s-1) ! input: diagnostic variables defining the liquid water and ice content (function of state variables) - real(rkind),intent(in) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: scalarCanopyLiqPrime - real(rkind),intent(in) :: mLayerVolFracIcePrime(:) ! trial value for volumetric fraction of ice (-) - real(rkind),intent(in) :: mLayerVolFracLiqPrime(:) - real(rkind),intent(in) :: mLayerVolFracWatPrime(:) - real(qp),intent(in) :: scalarCanopyCmTrial - real(qp),intent(in) :: mLayerCmTrial(:) + real(rkind),intent(in) :: scalarCanopyIcePrime ! Prime value for mass of ice on the vegetation canopy (kg m-2 s-1) + real(rkind),intent(in) :: scalarCanopyLiqPrime ! Prime value for the liq on the vegetation canopy (kg m-2 s-1) + real(rkind),intent(in) :: mLayerVolFracIcePrime(:) ! Prime value for volumetric fraction of ice (s-1) + real(rkind),intent(in) :: mLayerVolFracWatPrime(:) ! Prime value for the volumetric water in each snow and soil layer (s-1) + real(rkind),intent(in) :: mLayerVolFracLiqPrime(:) ! Prime value for the volumetric water in each snow and soil layer (s-1) + real(qp),intent(in) :: scalarCanopyCmTrial ! Cm of vegetation canopy (-) + real(qp),intent(in) :: mLayerCmTrial(:) ! Cm of each snow and soil layer (-) ! input: data structures type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU @@ -131,8 +131,8 @@ subroutine computResidSundials(& ! -------------------------------------------------------------------------------------------------------------------------------- integer(i4b) :: iLayer ! index of layer within the snow+soil domain integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) - real(rkind),dimension(nLayers) :: mLayerVolFracHydPrime ! vector of volumetric water content (-), either liquid water content or total water content real(rkind) :: scalarCanopyHydPrime ! trial value for canopy water (kg m-2), either liquid water content or total water content + real(rkind),dimension(nLayers) :: mLayerVolFracHydPrime ! vector of volumetric water content (-), either liquid water content or total water content ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! link to the necessary variables for the residual computations @@ -192,7 +192,7 @@ subroutine computResidSundials(& ! NOTE 4: same sink terms for matric head and liquid matric potential if(nSoilOnlyHyd>0)then do concurrent (iLayer=1:nSoil,ixSoilOnlyHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) - rAdd( ixSoilOnlyHyd(iLayer) ) = rAdd( ixSoilOnlyHyd(iLayer) ) + dt*(mLayerTranspire(iLayer) - mLayerBaseflow(iLayer) )/mLayerDepth(iLayer+nSnow) - mLayerCompress(iLayer) + rAdd( ixSoilOnlyHyd(iLayer) ) = rAdd( ixSoilOnlyHyd(iLayer) ) + dt*(mLayerTranspire(iLayer) - mLayerBaseflow(iLayer) )/mLayerDepth(iLayer+nSnow) - dt*mLayerCompress(iLayer) end do ! looping through non-missing energy state variables in the snow+soil domain endif @@ -204,11 +204,11 @@ subroutine computResidSundials(& ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision ! --> energy balance if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*scalarCanairTempPrime - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) - if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime + scalarCanopyCmTrial * scalarCanopyWatPrime/canopyDepth - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime + scalarCanopyCmTrial * scalarCanopyWatPrime/canopyDepth - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) ! --> mass balance if(ixVegHyd/=integerMissing)then scalarCanopyHydPrime = merge(scalarCanopyWatPrime, scalarCanopyLiqPrime, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) - rVec(ixVegHyd) = sMul(ixVegHyd)*scalarCanopyHydPrime - ( fVec(ixVegHyd)*dt + rAdd(ixVegHyd) ) + rVec(ixVegHyd) = sMul(ixVegHyd)*scalarCanopyHydPrime - ( fVec(ixVegHyd)*dt + rAdd(ixVegHyd) ) endif ! compute the residual vector for the snow and soil sub-domains for energy @@ -223,7 +223,7 @@ subroutine computResidSundials(& if(nSnowSoilHyd>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) ! (get the correct state variable) - mLayerVolFracHydPrime(iLayer) = merge(mLayerVolFracWatPrime(iLayer), mLayerVolFracLiqPrime(iLayer), (ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_matLayer) ) + mLayerVolFracHydPrime(iLayer) = merge(mLayerVolFracWatPrime(iLayer), mLayerVolFracLiqPrime(iLayer), (ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_matLayer) ) ! (compute the residual) rVec( ixSnowSoilHyd(iLayer) ) = mLayerVolFracHydPrime(iLayer) - ( fVec( ixSnowSoilHyd(iLayer) )*dt + rAdd( ixSnowSoilHyd(iLayer) ) ) end do ! looping through non-missing energy state variables in the snow+soil domain diff --git a/build/source/engine/computSnowDepth.f90 b/build/source/engine/computSnowDepth.f90 index 552020eb0..568294aa0 100644 --- a/build/source/engine/computSnowDepth.f90 +++ b/build/source/engine/computSnowDepth.f90 @@ -13,21 +13,21 @@ module computSnowDepth_module ! data types USE data_types,only:& - var_i, & ! x%var(:) (i4b) - var_d, & ! x%var(:) (rkind) - var_ilength, & ! x%var(:)%dat (i4b) - var_dlength, & ! x%var(:)%dat (rkind) - zLookup ! x%z(:)%var(:)%lookup(:) (rkind) + var_i, & ! x%var(:) (i4b) + var_d, & ! x%var(:) (rkind) + var_ilength, & ! x%var(:)%dat (i4b) + var_dlength, & ! x%var(:)%dat (rkind) + zLookup ! x%z(:)%var(:)%lookup(:) (rkind) ! named variables for parent structures -USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure -USE var_lookup,only:iLookPROG ! named variables for structure elements -USE var_lookup,only:iLookDIAG ! named variables for structure elements -USE var_lookup,only:iLookFLUX ! named variables for structure elements -USE var_lookup,only:iLookPARAM ! named variables for structure elements -USE var_lookup,only:iLookINDEX ! named variables for structure elements -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookFLUX ! named variables for structure elements +USE var_lookup,only:iLookPARAM ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil ! privacy @@ -43,33 +43,33 @@ module computSnowDepth_module ! public subroutine computSnowDepth: compute snow depth for one sub timestep ! ************************************************************************************************ subroutine computSnowDepth(& - dt_sub, & - nSnow, & ! intent(in) + dt_sub, & + nSnow, & ! intent(in) scalarSnowSublimation, & ! intent(in) - mLayerVolFracLiq, & ! intent(inout) - mLayerVolFracIce, & ! intent(inout) - mLayerTemp, & ! intent(in) - mLayerMeltFreeze, & ! intent(in) - mpar_data, & ! intent(in) + mLayerVolFracLiq, & ! intent(inout) + mLayerVolFracIce, & ! intent(inout) + mLayerTemp, & ! intent(in) + mLayerMeltFreeze, & ! intent(in) + mpar_data, & ! intent(in) ! output tooMuchSublim, & ! intent(out): flag to denote that there was too much sublimation in a given time step - mLayerDepth, & ! intent(inout) + mLayerDepth, & ! intent(inout) ! error control - err,message) ! intent(out): error control + err,message) ! intent(out): error control USE snwDensify_module,only:snwDensify ! snow densification (compaction and cavitation) implicit none - real(qp),intent(in) :: dt_sub + real(qp),intent(in) :: dt_sub integer(i4b),intent(in) :: nSnow ! number of snow layers - real(rkind),intent(in) :: scalarSnowSublimation - real(rkind),intent(inout) :: mLayerVolFracLiq(:) - real(rkind),intent(inout) :: mLayerVolFracIce(:) - real(rkind),intent(in) :: mLayerTemp(:) - real(rkind),intent(in) :: mLayerMeltFreeze(:) + real(rkind),intent(in) :: scalarSnowSublimation + real(rkind),intent(inout) :: mLayerVolFracLiq(:) + real(rkind),intent(inout) :: mLayerVolFracIce(:) + real(rkind),intent(in) :: mLayerTemp(:) + real(rkind),intent(in) :: mLayerMeltFreeze(:) type(var_dlength),intent(in) :: mpar_data ! model parameters logical(lgt) :: tooMuchSublim ! flag to denote that there was too much sublimation in a given time step - real(rkind),intent(inout) :: mLayerDepth(:) + real(rkind),intent(inout) :: mLayerDepth(:) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -82,7 +82,7 @@ subroutine computSnowDepth(& ! * compute change in ice content of the top snow layer due to sublimation... ! --------------------------------------------------------------------------- ! initialize the flags - tooMuchSublim=.false. ! too much sublimination (merge snow layers) + tooMuchSublim=.false. ! too much sublimation (merge snow layers) ! NOTE: this is done BEFORE densification if(nSnow > 0)then ! snow layers exist @@ -122,21 +122,21 @@ subroutine computSnowDepth(& if(nSnow>0)then call snwDensify(& ! intent(in): variables - dt_sub, & ! intent(in): time step (s) - nSnow, & ! intent(in): number of snow layers - mLayerTemp(1:nSnow), & ! intent(in): temperature of each layer (K) - mLayerMeltFreeze(1:nSnow), & ! intent(in): volumetric melt in each layer (kg m-3) + dt_sub, & ! intent(in): time step (s) + nSnow, & ! intent(in): number of snow layers + mLayerTemp(1:nSnow), & ! intent(in): temperature of each layer (K) + mLayerMeltFreeze(1:nSnow), & ! intent(in): volumetric melt in each layer (kg m-3) ! intent(in): parameters - mpar_data%var(iLookPARAM%densScalGrowth)%dat(1), & ! intent(in): density scaling factor for grain growth (kg-1 m3) - mpar_data%var(iLookPARAM%tempScalGrowth)%dat(1), & ! intent(in): temperature scaling factor for grain growth (K-1) - mpar_data%var(iLookPARAM%grainGrowthRate)%dat(1), & ! intent(in): rate of grain growth (s-1) - mpar_data%var(iLookPARAM%densScalOvrbdn)%dat(1), & ! intent(in): density scaling factor for overburden pressure (kg-1 m3) - mpar_data%var(iLookPARAM%tempScalOvrbdn)%dat(1), & ! intent(in): temperature scaling factor for overburden pressure (K-1) - mpar_data%var(iLookPARAM%baseViscosity)%dat(1), & ! intent(in): viscosity coefficient at T=T_frz and snow density=0 (kg m-2 s) + mpar_data%var(iLookPARAM%densScalGrowth)%dat(1), & ! intent(in): density scaling factor for grain growth (kg-1 m3) + mpar_data%var(iLookPARAM%tempScalGrowth)%dat(1), & ! intent(in): temperature scaling factor for grain growth (K-1) + mpar_data%var(iLookPARAM%grainGrowthRate)%dat(1), & ! intent(in): rate of grain growth (s-1) + mpar_data%var(iLookPARAM%densScalOvrbdn)%dat(1), & ! intent(in): density scaling factor for overburden pressure (kg-1 m3) + mpar_data%var(iLookPARAM%tempScalOvrbdn)%dat(1), & ! intent(in): temperature scaling factor for overburden pressure (K-1) + mpar_data%var(iLookPARAM%baseViscosity)%dat(1), & ! intent(in): viscosity coefficient at T=T_frz and snow density=0 (kg m-2 s) ! intent(inout): state variables - mLayerDepth(1:nSnow), & ! intent(inout): depth of each layer (m) - mLayerVolFracLiq(1:nSnow), & ! intent(inout): volumetric fraction of liquid water after itertations (-) - mLayerVolFracIce(1:nSnow), & ! intent(inout): volumetric fraction of ice after itertations (-) + mLayerDepth(1:nSnow), & ! intent(inout): depth of each layer (m) + mLayerVolFracLiq(1:nSnow), & ! intent(inout): volumetric fraction of liquid water after itertations (-) + mLayerVolFracIce(1:nSnow), & ! intent(inout): volumetric fraction of ice after itertations (-) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index a80716df1..8290f3bb2 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -53,6 +53,7 @@ module coupled_em_module USE var_lookup,only:childFLUX_MEAN ! metadata +USE globalData,only:flux_meta ! metadata on the model fluxes USE globalData,only:indx_meta ! metadata on the model index variables USE globalData,only:diag_meta ! metadata on the model diagnostic variables USE globalData,only:prog_meta ! metadata on the model prognostic variables @@ -63,11 +64,6 @@ module coupled_em_module USE globalData,only:model_decisions ! model decision structure USE globalData,only:globalPrintFlag ! the global print flag -! look-up values for the numerical method -USE mDecisions_module,only: & - bEuler, & ! home-grown backward Euler solution with long time steps - sundials ! SUNDIALS/IDA solution - ! look-up values for the maximum interception capacity USE mDecisions_module,only: & stickySnow, & ! maximum interception capacity an increasing function of temerature @@ -126,6 +122,8 @@ subroutine coupled_em(& ! structure allocations USE allocspace_module,only:allocLocal ! allocate local data structures USE allocspace_module,only:resizeData ! clone a data structure + ! simulation of fluxes and residuals given a trial state vector + USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential ! preliminary subroutines USE vegPhenlgy_module,only:vegPhenlgy ! compute vegetation phenology USE vegNrgFlux_module,only:wettedFrac ! compute wetted fraction of the canopy (used in sw radiation fluxes) @@ -141,7 +139,6 @@ subroutine coupled_em(& USE time_utils_module,only:elapsedSec ! calculate the elapsed time ! additional subroutines USE tempAdjust_module,only:tempAdjust ! adjust snow temperature associated with new snowfall - USE snwDensify_module,only:snwDensify ! snow densification (compaction and cavitation) USE var_derive_module,only:calcHeight ! module to calculate height at layer interfaces and layer mid-point USE computSnowDepth_module,only:computSnowDepth @@ -173,18 +170,19 @@ subroutine coupled_em(& integer(i4b) :: nSoil ! number of soil layers integer(i4b) :: nLayers ! total number of layers integer(i4b) :: nState ! total number of state variables - real(rkind) :: dtSave ! length of last input model sub-step (seconds) + real(rkind) :: dtSave ! length of last input model whole sub-step (seconds) real(rkind) :: dt_sub ! length of model sub-step (seconds) real(rkind) :: dt_wght ! weight applied to model sub-step (dt_sub/data_step) real(rkind) :: dt_solv ! seconds in the data step that have been completed real(rkind) :: dtMultiplier ! time step multiplier (-) based on what happenned in "opSplittin" real(rkind) :: minstep,maxstep ! minimum and maximum time step length (seconds) + real(rkind) :: maxstep_op ! maximum time step length (seconds) to run opSplittin over + real(rkind) :: whole_step ! step the surface pond drainage and sublimation calculated over integer(i4b) :: nsub ! number of substeps logical(lgt) :: computeVegFluxOld ! flag to indicate if we are computing fluxes over vegetation on the previous sub step logical(lgt) :: includeAquifer ! flag to denote that an aquifer is included logical(lgt) :: modifiedLayers ! flag to denote that snow layers were modified logical(lgt) :: modifiedVegState ! flag to denote that vegetation states were modified - type(var_dlength) :: flux_mean ! timestep-average model fluxes for a local HRU integer(i4b) :: nLayersRoots ! number of soil layers that contain roots real(rkind) :: exposedVAI ! exposed vegetation area index real(rkind) :: dCanopyWetFraction_dWat ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) @@ -196,7 +194,7 @@ subroutine coupled_em(& real(rkind) :: massLiquid ! mass liquid water (kg m-2) real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) - integer(i4b) :: ixSolution ! solution method used by opSplitting + integer(i4b) :: ixSolution ! solution method used by opSplittin logical(lgt) :: firstSubStep ! flag to denote if the first time step logical(lgt) :: stepFailure ! flag to denote the need to reduce length of the coupled step and try again logical(lgt) :: tooMuchMelt ! flag to denote that there was too much melt in a given time step @@ -205,20 +203,35 @@ subroutine coupled_em(& logical(lgt) :: pauseFlag ! flag to pause execution logical(lgt),parameter :: backwardsCompatibility=.true. ! flag to denote a desire to ensure backwards compatibility with previous branches logical(lgt) :: checkMassBalance ! flag to check the mass balance - type(var_ilength) :: indx_temp ! temporary model index variables + type(var_ilength) :: indx_temp ! temporary model index variables saved only on outer loop + type(var_ilength) :: indx_temp0 ! temporary model index variables saved every time type(var_dlength) :: prog_temp ! temporary model prognostic variables type(var_dlength) :: diag_temp ! temporary model diagnostic variables + real(rkind),allocatable :: mLayerVolFracIceInit(:)! initial vector for volumetric fraction of ice (-) ! check SWE real(rkind) :: oldSWE ! SWE at the start of the substep real(rkind) :: newSWE ! SWE at the end of the substep real(rkind) :: delSWE ! change in SWE over the subtep - real(rkind) :: effRainfall ! effective rainfall (kg m-2 s-1) + real(rkind) :: innerEffRainfall ! inner step average effective rainfall into snow (kg m-2 s-1) + real(rkind) :: effRainfall ! timestep-average effective rainfall into snow (kg m-2 s-1) real(rkind) :: effSnowfall ! effective snowfall (kg m-2 s-1) real(rkind) :: sfcMeltPond ! surface melt pond (kg m-2) real(rkind) :: massBalance ! mass balance error (kg m-2) + ! energy fluxes + integer(i4b) :: iSoil ! index of soil layers + type(var_dlength) :: flux_mean ! timestep-average model fluxes for a local HRU + type(var_dlength) :: flux_inner ! inner step average model fluxes for a local HRU + real(rkind) :: meanSoilCompress ! timestep-average soil compression + real(rkind) :: innerSoilCompress ! inner step average soil compression + + ! sublimation sums over substep and means over data_step + real(rkind) :: sumCanopySublimation ! sum of sublimation from the vegetation canopy (kg m-2 s-1) over substep + real(rkind) :: sumSnowSublimation ! sum of sublimation from the snow surface (kg m-2 s-1) over substep + real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) over substep + real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) over substep ! balance checks integer(i4b) :: iVar ! loop through model variables - real(rkind) :: totalSoilCompress ! total soil compression (kg m-2) + real(rkind) :: balanceSoilCompress ! total soil compression (kg m-2) real(rkind) :: scalarCanopyWatBalError! water balance error for the vegetation canopy (kg m-2) real(rkind) :: scalarSoilWatBalError ! water balance error (kg m-2) real(rkind) :: scalarInitCanopyLiq ! initial liquid water on the vegetation canopy (kg m-2) @@ -240,6 +253,12 @@ subroutine coupled_em(& ! timing information real(rkind) :: startTime ! start time (used to compute wall clock time) real(rkind) :: endTime ! end time (used to compute wall clock time) + ! outer loop control + logical(lgt) :: firstInnerStep ! flag to denote if the first time step in maxstep subStep + logical(lgt) :: lastInnerStep ! flag to denote if the last time step in maxstep subStep + logical(lgt) :: do_outer ! flag to denote if doing the outer steps surrounding the call to opSplittin + real(rkind) :: dt_solvInner ! seconds in the maxstep subStep that have been completed + ! ---------------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="coupled_em/" @@ -280,8 +299,10 @@ subroutine coupled_em(& modifiedLayers = .false. ! flag to denote that snow layers were modified modifiedVegState = .false. ! flag to denote that vegetation states were modified - ! define the first step + ! define the first step and first and last inner steps firstSubStep = .true. + firstInnerStep = .true. + lastInnerStep = .false. ! count the number of snow and soil layers ! NOTE: need to re-compute the number of snow and soil layers at the start of each sub-step because the number of layers may change @@ -302,20 +323,24 @@ subroutine coupled_em(& ! create temporary data structures for index variables call resizeData(indx_meta(:),indx_data,indx_temp,err=err,message=cmessage) + call resizeData(indx_meta(:),indx_data,indx_temp0,err=err,message=cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif ! allocate space for the local fluxes call allocLocal(averageFlux_meta(:)%var_info,flux_mean,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + call allocLocal(averageFlux_meta(:)%var_info,flux_inner,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - ! initialize compression and surface melt pond + ! initialize surface melt pond sfcMeltPond = 0._rkind ! change in storage associated with the surface melt pond (kg m-2) - totalSoilCompress = 0._rkind ! change in soil storage associated with compression of the matrix (kg m-2) - ! initialize mean fluxes + ! initialize fluxes to average over data_step (averaged over substep in varSubStep) do iVar=1,size(averageFlux_meta) flux_mean%var(iVar)%dat(:) = 0._rkind end do + meanSoilCompress = 0._rkind ! mean total soil compression + effRainfall = 0._rkind ! mean total effective rainfall over snow ! associate local variables with information in the data structures associate(& @@ -363,8 +388,11 @@ subroutine coupled_em(& ! short-cut to the algorithmic control parameters ! NOTE - temporary assignment of minstep to foce something reasonable + ! change maxstep with hard code here to make the outer and inner loop computations here in coupled_em happen more frequently + ! change maxstep_op with hard code here to make the inner loop computations in opSplittin happen more frequently minstep = 10._rkind ! mpar_data%var(iLookPARAM%minstep)%dat(1) ! minimum time step (s) maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) + maxstep_op = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) to run opSplittin over ! compute the number of layers with roots nLayersRoots = count(prog_data%var(iLookPROG%iLayerHeight)%dat(nSnow:nLayers-1) < mpar_data%var(iLookPARAM%rootingDepth)%dat(1)-verySmall) @@ -379,7 +407,6 @@ subroutine coupled_em(& ! save SWE oldSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) - ! *** compute phenology... ! ------------------------ @@ -447,7 +474,7 @@ subroutine coupled_em(& prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1), & ! canopy liquid water (kg m-2) prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! canopy ice (kg m-2) diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1), & ! maximum canopy liquid water (kg m-2) - diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1), & ! maximum canopy ice content (kg m-2) + diag_data%var(iLookDIAG%scalarCanopyIceMax)%dat(1), & ! maximum canopy ice content (kg m-2) mpar_data%var(iLookPARAM%canopyWettingFactor)%dat(1), & ! maximum wetted fraction of the canopy (-) mpar_data%var(iLookPARAM%canopyWettingExp)%dat(1), & ! exponent in canopy wetting function (-) ! output @@ -503,6 +530,7 @@ subroutine coupled_em(& ! ----------------------------------------------- ! NOTE 1: this needs to be done before solving the energy and liquid water equations, to account for the heat advected with precipitation (and throughfall/unloading) ! NOTE 2: the unloading flux is computed using canopy drip (scalarCanopyLiqDrainage) from the previous time step + ! this changes canopy ice call canopySnow(& ! input: model control data_step, & ! intent(in): time step (seconds) @@ -548,11 +576,13 @@ subroutine coupled_em(& ! *** MAIN SOLVER ************************************************************************************ ! **************************************************************************************************** - ! initialize the length of the sub-step - dt_solv = 0._rkind ! length of time step that has been completed (s) - dt_init = min(data_step,maxstep) ! initial substep length (s) - dt_sub = dt_init ! length of substep - dtSave = dt_init ! length of substep + ! initialize the length of the sub-step and counters + whole_step = maxstep + dt_solv = 0._rkind ! length of time step that has been completed (s) + dt_solvInner = 0._rkind ! length of time step that has been completed (s) in whole_step subStep + dt_init = min(data_step,whole_step,maxstep_op) ! initial substep length (s) + dt_sub = dt_init + dtSave = whole_step ! length of whole substep ! initialize the number of sub-steps nsub=0 @@ -560,6 +590,8 @@ subroutine coupled_em(& ! loop through sub-steps substeps: do ! continuous do statement with exit clause (alternative to "while") + dt_sub = min(data_step,whole_step,maxstep_op,dt_sub) ! adjust for possible whole_step changes + ! print progress if(globalPrintFlag)then write(*,'(a,1x,4(f13.5,1x))') ' start of step: dt_init, dt_sub, dt_solv, data_step: ', dt_init, dt_sub, dt_solv, data_step @@ -574,90 +606,30 @@ subroutine coupled_em(& ! NOTE: this is necessary because the length of index variables depends on a given split ! --> the resize here is overwritten later (in indexSplit) ! --> admittedly ugly, and retained for now - if(stepFailure)then + if(stepFailure)then ! resize temp to current data, later in code current data is set to lastInnerStep data call resizeData(indx_meta(:),indx_temp,indx_data,err=err,message=cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - else - call resizeData(indx_meta(:),indx_data,indx_temp,err=err,message=cmessage) + else ! resize current data to temp0, temp0 is saved for next run + call resizeData(indx_meta(:),indx_data,indx_temp0,err=err,message=cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + do iVar=1,size(indx_data%var) + indx_temp0%var(iVar)%dat(:) = indx_data%var(iVar)%dat(:) + end do endif - ! save/recover copies of index variables - do iVar=1,size(indx_data%var) - select case(stepFailure) - case(.false.); indx_temp%var(iVar)%dat(:) = indx_data%var(iVar)%dat(:) - case(.true.); indx_data%var(iVar)%dat(:) = indx_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - ! save/recover copies of prognostic variables - do iVar=1,size(prog_data%var) - select case(stepFailure) - case(.false.); prog_temp%var(iVar)%dat(:) = prog_data%var(iVar)%dat(:) - case(.true.); prog_data%var(iVar)%dat(:) = prog_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - ! save/recover copies of diagnostic variables - do iVar=1,size(diag_data%var) - select case(stepFailure) - case(.false.); diag_temp%var(iVar)%dat(:) = diag_data%var(iVar)%dat(:) - case(.true.); diag_data%var(iVar)%dat(:) = diag_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - ! re-assign dimension lengths - nSnow = count(indx_data%var(iLookINDEX%layerType)%dat==iname_snow) - nSoil = count(indx_data%var(iLookINDEX%layerType)%dat==iname_soil) - nLayers = nSnow+nSoil - - ! *** merge/sub-divide snow layers... - ! ----------------------------------- - call volicePack(& - ! input/output: model data structures - doLayerMerge, & ! intent(in): flag to force merge of snow layers - model_decisions, & ! intent(in): model decisions - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): type of each layer - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output - modifiedLayers, & ! intent(out): flag to denote that layers were modified - err,cmessage) ! intent(out): error control - if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if - - ! save the number of snow and soil layers - nSnow = indx_data%var(iLookINDEX%nSnow)%dat(1) - nSoil = indx_data%var(iLookINDEX%nSoil)%dat(1) - nLayers = indx_data%var(iLookINDEX%nLayers)%dat(1) - - ! compute the indices for the model state variables - if(firstSubStep .or. modifiedVegState .or. modifiedLayers)then - call indexState(computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - includeAquifer, & ! intent(in): flag to denote if included the aquifer - nSnow,nSoil,nLayers, & ! intent(in): number of snow and soil layers, and total number of layers - indx_data, & ! intent(inout): indices defining model states and layers - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - end if - - ! recreate the temporary data structures - ! NOTE: resizeData(meta, old, new, ..) - if(modifiedVegState .or. modifiedLayers)then + ! check if on outer loop, always do outer if after failed step and on then on reduced whole_step + do_outer = .false. + if(stepFailure) firstInnerStep = .true. + if(firstInnerStep) do_outer = .true. - ! create temporary data structures for prognostic variables - call resizeData(prog_meta(:),prog_data,prog_temp,copy=.true.,err=err,message=cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + if(do_outer)then - ! create temporary data structures for diagnostic variables - call resizeData(diag_meta(:),diag_data,diag_temp,copy=.true.,err=err,message=cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! create temporary data structures for index variables - call resizeData(indx_meta(:),indx_data,indx_temp,copy=.true.,err=err,message=cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + if(.not.stepFailure)then + call resizeData(indx_meta(:),indx_data,indx_temp,err=err,message=cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + endif + ! save/recover copies of index variables, temp saved on lastInnerStep, failed starts at lastInnerStep do iVar=1,size(indx_data%var) select case(stepFailure) case(.false.); indx_temp%var(iVar)%dat(:) = indx_data%var(iVar)%dat(:) @@ -665,52 +637,192 @@ subroutine coupled_em(& end select end do ! looping through variables - endif ! if modified the states + ! save/recover copies of prognostic variables + do iVar=1,size(prog_data%var) + select case(stepFailure) + case(.false.); prog_temp%var(iVar)%dat(:) = prog_data%var(iVar)%dat(:) + case(.true.); prog_data%var(iVar)%dat(:) = prog_temp%var(iVar)%dat(:) + end select + end do ! looping through variables - ! define the number of state variables - nState = indx_data%var(iLookINDEX%nState)%dat(1) + ! save/recover copies of diagnostic variables + do iVar=1,size(diag_data%var) + select case(stepFailure) + case(.false.); diag_temp%var(iVar)%dat(:) = diag_data%var(iVar)%dat(:) + case(.true.); diag_data%var(iVar)%dat(:) = diag_temp%var(iVar)%dat(:) + end select + end do ! looping through variables - ! *** compute diagnostic variables for each layer... - ! -------------------------------------------------- - ! NOTE: this needs to be done AFTER volicePack, since layers may have been sub-divided and/or merged - call diagn_evar(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): canopy depth (m) - ! input/output: data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if - - - ! *** compute melt of the "snow without a layer"... - ! ------------------------------------------------- - ! NOTE: forms a surface melt pond, which drains into the upper-most soil layer through the time step - ! (check for the special case of "snow without a layer") - if(nSnow==0)then - call implctMelt(& - ! input/output: integrated snowpack properties - prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(inout): snow water equivalent (kg m-2) - prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(inout): snow depth (m) - prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1), & ! intent(inout): surface melt pond (kg m-2) - ! input/output: properties of the upper-most soil layer - prog_data%var(iLookPROG%mLayerTemp)%dat(nSnow+1), & ! intent(inout): surface layer temperature (K) - prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1), & ! intent(inout): surface layer depth (m) - diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat(nSnow+1),& ! intent(inout): surface layer volumetric heat capacity (J m-3 K-1) + ! re-assign dimension lengths + nSnow = count(indx_data%var(iLookINDEX%layerType)%dat==iname_snow) + nSoil = count(indx_data%var(iLookINDEX%layerType)%dat==iname_soil) + nLayers = nSnow+nSoil + + ! *** merge/sub-divide snow layers... + ! ----------------------------------- + call volicePack(& + ! input/output: model data structures + doLayerMerge, & ! intent(in): flag to force merge of snow layers + model_decisions, & ! intent(in): model decisions + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): type of each layer + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output + modifiedLayers, & ! intent(out): flag to denote that layers were modified + err,cmessage) ! intent(out): error control + if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if + + ! save the number of snow and soil layers + nSnow = indx_data%var(iLookINDEX%nSnow)%dat(1) + nSoil = indx_data%var(iLookINDEX%nSoil)%dat(1) + nLayers = indx_data%var(iLookINDEX%nLayers)%dat(1) + + ! compute the indices for the model state variables + if(firstSubStep .or. modifiedVegState .or. modifiedLayers)then + call indexState(computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + includeAquifer, & ! intent(in): flag to denote if included the aquifer + nSnow,nSoil,nLayers, & ! intent(in): number of snow and soil layers, and total number of layers + indx_data, & ! intent(inout): indices defining model states and layers + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + end if + + ! recreate the temporary data structures + ! NOTE: resizeData(meta, old, new, ..) + if(modifiedVegState .or. modifiedLayers)then + + ! create temporary data structures for prognostic variables + call resizeData(prog_meta(:),prog_data,prog_temp,copy=.true.,err=err,message=cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! create temporary data structures for diagnostic variables + call resizeData(diag_meta(:),diag_data,diag_temp,copy=.true.,err=err,message=cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! create temporary data structures for index variables + call resizeData(indx_meta(:),indx_data,indx_temp,copy=.true.,err=err,message=cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + do iVar=1,size(indx_data%var) + select case(stepFailure) + case(.false.); indx_temp%var(iVar)%dat(:) = indx_data%var(iVar)%dat(:) + case(.true.); indx_data%var(iVar)%dat(:) = indx_temp%var(iVar)%dat(:) + end select + end do ! looping through variables + + endif ! if modified the states + + ! define the number of state variables + nState = indx_data%var(iLookINDEX%nState)%dat(1) + + ! *** compute diagnostic variables for each layer... + ! -------------------------------------------------- + ! NOTE: this needs to be done AFTER volicePack, since layers may have been sub-divided and/or merged + call diagn_evar(& + ! input: control variables + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): canopy depth (m) + ! input/output: data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! output: error control - err,cmessage ) ! intent(out): error control - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - end if + err,cmessage) ! intent(out): error control + if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if + + ! *** compute melt of the "snow without a layer"... + ! ------------------------------------------------- + ! NOTE: forms a surface melt pond, which drains into the upper-most soil layer through the time step + ! (check for the special case of "snow without a layer") + ! this pond melts evenly over entire time of maxstep until it gets recomputed because based on SWE when computed + if(nSnow==0) then + call implctMelt(& + ! input/output: integrated snowpack properties + prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(inout): snow water equivalent (kg m-2) + prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(inout): snow depth (m) + prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1), & ! intent(inout): surface melt pond (kg m-2) + ! input/output: properties of the upper-most soil layer + prog_data%var(iLookPROG%mLayerTemp)%dat(nSnow+1), & ! intent(inout): surface layer temperature (K) + prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1), & ! intent(inout): surface layer depth (m) + diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat(nSnow+1),& ! intent(inout): surface layer volumetric heat capacity (J m-3 K-1) + ! output: error control + err,cmessage ) ! intent(out): error control + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + endif + + ! save volumetric ice content at the start of the step + ! NOTE: used for volumetric loss due to melt-freeze + allocate(mLayerVolFracIceInit(nLayers)); mLayerVolFracIceInit = prog_data%var(iLookPROG%mLayerVolFracIce)%dat + + ! make sure have consistent state variables to start, later done in updateVars + ! associate local variables with information in the data structures + init: associate(& + ! depth-varying soil parameters + vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) + ! state variables in the vegetation canopy + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(out): [dp] mass of total water on the vegetation canopy (kg m-2) + ! state variables in the snow and soil domains + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(out): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] matric head (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat & ! intent(out): [dp(:)] matric potential of liquid water (m) + ) ! associations to variables in data structures + + ! compute the total water content in the vegetation canopy + scalarCanopyWat = scalarCanopyLiq + scalarCanopyIce ! kg m-2 + + ! compute the total water content in snow and soil + ! NOTE: no ice expansion allowed for soil + if(nSnow>0)& + mLayerVolFracWat( 1:nSnow ) = mLayerVolFracLiq( 1:nSnow ) + mLayerVolFracIce( 1:nSnow )*(iden_ice/iden_water) + mLayerVolFracWat(nSnow+1:nLayers) = mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) + + ! compute the liquid water matric potential (m) + ! NOTE: include ice content as part of the solid porosity - major effect of ice is to reduce the pore size; ensure that effSat=1 at saturation + ! (from Zhao et al., J. Hydrol., 1997: Numerical analysis of simultaneous heat and mass transfer...) + do iSoil=1,nSoil + call liquidHead(mLayerMatricHead(iSoil),mLayerVolFracLiq(nSnow+iSoil),mLayerVolFracIce(nSnow+iSoil), & ! input: state variables + vGn_alpha(iSoil),vGn_n(iSoil),theta_sat(iSoil),theta_res(iSoil),vGn_m(iSoil), & ! input: parameters + matricHeadLiq=mLayerMatricHeadLiq(iSoil), & ! output: liquid water matric potential (m) + err=err,message=cmessage) ! output: error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + end do ! looping through soil layers (computing liquid water matric potential) + + end associate init + + ! correct increments (if need to redo inner step) and reset increment + dt_solv = dt_solv - dt_solvInner + dt_solvInner = 0._rkind + + ! initialize sublimation sums to average over whole_step + sumCanopySublimation = 0._rkind + sumSnowSublimation = 0._rkind + sumLatHeatCanopyEvap = 0._rkind + sumSenHeatCanopy = 0._rkind + ! initialize fluxes to average over whole_step (averaged over substep in varSubStep) + do iVar=1,size(averageFlux_meta) + flux_inner%var(iVar)%dat(:) = 0._rkind + end do + innerSoilCompress = 0._rkind ! mean total soil compression + innerEffRainfall = 0._rkind ! mean total effective rainfall over snow + + endif ! (do_outer loop) ! *** solve model equations... ! ---------------------------- ! save input step - dtSave = dt_sub - + dtSave = whole_step ! get the new solution call opSplittin(& @@ -719,8 +831,10 @@ subroutine coupled_em(& nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers nState, & ! intent(in): total number of layers - dt_sub, & ! intent(inout): length of the model sub-step + dt_sub, & ! intent(in): length of the model sub-step + whole_step, & ! intent(in): length of whole step for surface drainage and average flux (nsub==1), & ! intent(in): logical flag to denote the first substep + firstInnerStep, & ! intent(in): flag to denote if the first time step in maxstep subStep computeVegFlux, & ! intent(in): logical flag to compute fluxes within the vegetation canopy ! input/output: data structures type_data, & ! intent(in): type of vegetation and soil @@ -744,7 +858,6 @@ subroutine coupled_em(& ! check for all errors (error recovery within opSplittin) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - ! process the flag for too much melt if(tooMuchMelt)then stepFailure = .true. @@ -756,143 +869,186 @@ subroutine coupled_em(& ! handle special case of the step failure ! NOTE: need to revert back to the previous state vector that we were happy with and reduce the time step if(stepFailure)then - ! halve step - dt_sub = dtSave/2._rkind + ! halve whole_step, for more frequent outer loop updates + whole_step = dtSave/2._rkind ! check that the step is not tiny - if(dt_sub < minstep)then + if(whole_step < minstep)then print*,ixSolution - print*, 'dtSave, dt_sub', dtSave, dt_sub + print*, 'dtSave, dt_sub', dtSave, whole_step message=trim(message)//'length of the coupled step is below the minimum step length' err=20; return endif - ! try again + ! try again, restart step + deallocate(mLayerVolFracIceInit) cycle substeps endif - ! update first step - firstSubStep=.false. - - ! *** remove ice due to sublimation... - ! -------------------------------------------------------------- - sublime: associate(& - scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1), & ! sublimation from the vegetation canopy (kg m-2 s-1) - scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! sublimation from the snow surface (kg m-2 s-1) - scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1), & ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1), & ! sensible heat flux from the canopy to the canopy air space (W m-2) - scalarLatHeatGround => flux_data%var(iLookFLUX%scalarLatHeatGround)%dat(1), & ! latent heat flux from ground surface below vegetation (W m-2) - scalarSenHeatGround => flux_data%var(iLookFLUX%scalarSenHeatGround)%dat(1), & ! sensible heat flux from ground surface below vegetation (W m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1), & ! liquid water stored on the vegetation canopy (kg m-2) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! ice stored on the vegetation canopy (kg m-2) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat, & ! volumetric fraction of ice in the snow+soil domain (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat, & ! volumetric fraction of liquid water in the snow+soil domain (-) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat & ! depth of each snow+soil layer (m) - ) ! associations to variables in data structures - - ! * compute change in canopy ice content due to sublimation... - ! ------------------------------------------------------------ - if(computeVegFlux)then - - ! remove mass of ice on the canopy - scalarCanopyIce = scalarCanopyIce + scalarCanopySublimation*dt_sub - - ! if removed all ice, take the remaining sublimation from water - if(scalarCanopyIce < 0._rkind)then - scalarCanopyLiq = scalarCanopyLiq + scalarCanopyIce - scalarCanopyIce = 0._rkind + ! increment sublimation sums + sumCanopySublimation = sumCanopySublimation + dt_sub*flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) + sumSnowSublimation = sumSnowSublimation + dt_sub*flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) + sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dt_sub*flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) + sumSenHeatCanopy = sumSenHeatCanopy + dt_sub*flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) + + ! update first step and first and last inner steps + firstSubStep = .false. + firstInnerStep = .false. + if(dt_solvInner + dt_sub >= whole_step) lastInnerStep = .true. + if(dt_solv + dt_sub >= data_step-verySmall) lastInnerStep = .true. + + ! check if on outer loop + do_outer = .false. + if(lastInnerStep) do_outer = .true. + + if(do_outer)then + + ! *** remove ice due to sublimation and freeze calculations... + ! NOTE: In the future this should be moved into the solver, makes a big difference + ! -------------------------------------------------------------- + sublime: associate(& + mLayerMeltFreeze => diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat, & ! melt-freeze in each snow and soil layer (kg m-3) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1), & ! liquid water stored on the vegetation canopy (kg m-2) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! ice stored on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1), & ! canopy ice content (kg m-2) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat, & ! volumetric fraction of ice in the snow+soil domain (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat, & ! volumetric fraction of liquid water in the snow+soil domain (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat, & ! volumetric fraction of total water (-) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat & ! depth of each snow+soil layer (m) + ) ! associations to variables in data structures + + ! compute the melt in each snow and soil layer + if(nSnow>0)& + mLayerMeltFreeze(1:nSnow) = -( mLayerVolFracIce(1:nSnow) - mLayerVolFracIceInit(1:nSnow) ) * iden_ice + mLayerMeltFreeze(nSnow+1:nLayers) = -( mLayerVolFracIce(nSnow+1:nLayers) - mLayerVolFracIceInit(nSnow+1:nLayers) )*iden_water + deallocate(mLayerVolFracIceInit) + + ! * compute change in canopy ice content due to sublimation... + ! ------------------------------------------------------------ + if(computeVegFlux)then + + ! remove mass of ice on the canopy + scalarCanopyIce = scalarCanopyIce + sumCanopySublimation + + ! if removed all ice, take the remaining sublimation from water + if(scalarCanopyIce < 0._rkind)then + scalarCanopyLiq = scalarCanopyLiq + scalarCanopyIce + scalarCanopyIce = 0._rkind + endif + + ! modify fluxes and mean fluxes if there is insufficient canopy water to support the converged sublimation rate over the whole time step + if(scalarCanopyLiq < 0._rkind)then + ! --> superfluous sublimation flux + superflousSub = -scalarCanopyLiq/whole_step ! kg m-2 s-1 + superflousNrg = superflousSub*LH_sub ! W m-2 (J m-2 s-1) + ! --> update fluxes and states + sumCanopySublimation = sumCanopySublimation + superflousSub*whole_step + sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + superflousNrg*whole_step + sumSenHeatCanopy = sumSenHeatCanopy - superflousNrg*whole_step + scalarCanopyLiq = 0._rkind + endif + + ! update water + scalarCanopyWat = scalarCanopyLiq + scalarCanopyIce + + end if ! (if computing the vegetation flux) + + call computSnowDepth(& + whole_step, & ! intent(in) + nSnow, & ! intent(in) + sumSnowSublimation/whole_step, & ! intent(in) + mLayerVolFracLiq, & ! intent(inout) + mLayerVolFracIce, & ! intent(inout) + prog_data%var(iLookPROG%mLayerTemp)%dat, & ! intent(in) + mLayerMeltFreeze, & ! intent(in) + mpar_data, & ! intent(in) + ! output + tooMuchSublim, & ! intent(out): flag to denote that there was too much sublimation in a given time step + mLayerDepth, & ! intent(inout) + ! error control + err,message) ! intent(out): error control + if(err/=0)then; err=55; return; end if + + ! process the flag for too much sublimation + if(tooMuchSublim)then + stepFailure = .true. + doLayerMerge = .true. + else + doLayerMerge = .false. endif - ! modify fluxes if there is insufficient canopy water to support the converged sublimation rate over the time step dt_sub - if(scalarCanopyLiq < 0._rkind)then - ! --> superfluous sublimation flux - superflousSub = -scalarCanopyLiq/dt_sub ! kg m-2 s-1 - superflousNrg = superflousSub*LH_sub ! W m-2 (J m-2 s-1) - ! --> update fluxes and states - scalarCanopySublimation = scalarCanopySublimation + superflousSub - scalarLatHeatCanopyEvap = scalarLatHeatCanopyEvap + superflousNrg - scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg - scalarCanopyLiq = 0._rkind + ! handle special case of the step failure + ! NOTE: need to revert back to the previous state vector that we were happy with and reduce the time step + if(stepFailure)then + ! halve whole_step, for more frequent outer loop updates + whole_step = dtSave/2._rkind + ! check that the step is not tiny + if(whole_step < minstep)then + print*,ixSolution + print*, 'dtSave, dt_sub', dtSave, whole_step + message=trim(message)//'length of the coupled step is below the minimum step length' + err=20; return + endif + ! try again, restart step (at end inner step) + cycle substeps endif - end if ! (if computing the vegetation flux) - - call computSnowDepth(& - dt_sub, & ! intent(in) - nSnow, & ! intent(in) - scalarSnowSublimation, & ! intent(in) - mLayerVolFracLiq, & ! intent(inout) - mLayerVolFracIce, & ! intent(inout) - prog_data%var(iLookPROG%mLayerTemp)%dat, & ! intent(in) - diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat, & ! intent(in) - mpar_data, & ! intent(in) - ! output - tooMuchSublim, & ! intent(out): flag to denote that there was too much sublimation in a given time step - mLayerDepth, & ! intent(inout) - ! error control - err,message) ! intent(out): error control - if(err/=0)then; err=55; return; end if - - ! process the flag for too much sublimation - if(tooMuchSublim)then - stepFailure = .true. - doLayerMerge = .true. - else - doLayerMerge = .false. - endif - - ! handle special case of the step failure - ! NOTE: need to revert back to the previous state vector that we were happy with and reduce the time step - if(stepFailure)then - ! halve step - dt_sub = dtSave/2._rkind - ! check that the step is not tiny - if(dt_sub < minstep)then - print*,ixSolution - print*, 'dtSave, dt_sub', dtSave, dt_sub - message=trim(message)//'length of the coupled step is below the minimum step length' - err=20; return + ! update coordinate variables + call calcHeight(& + ! input/output: data structures + indx_data, & ! intent(in): layer type + prog_data, & ! intent(inout): model variables for a local HRU + ! output: error control + err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + ! recompute snow depth, SWE, and layer water + if(nSnow > 0)then + prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( mLayerDepth(1:nSnow) ) + prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (mLayerVolFracLiq(1:nSnow)*iden_water & + + mLayerVolFracIce(1:nSnow)*iden_ice) * mLayerDepth(1:nSnow) ) + mLayerVolFracWat(1:nSnow) = mLayerVolFracLiq(1:nSnow) + mLayerVolFracIce(1:nSnow)*iden_ice/iden_water endif - ! try again - cycle substeps - endif - - end associate sublime - - ! update coordinate variables - call calcHeight(& - ! input/output: data structures - indx_data, & ! intent(in): layer type - prog_data, & ! intent(inout): model variables for a local HRU - ! output: error control - err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - - ! recompute snow depth and SWE - if(nSnow > 0)then - prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow)) - prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow)*iden_water + & - prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice) & - * prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) - end if - ! increment fluxes - dt_wght = dt_sub/data_step ! define weight applied to each sub-step - do iVar=1,size(averageFlux_meta) - flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:)*dt_wght - end do + end associate sublime - ! increment change in storage associated with the surface melt pond (kg m-2) - if(nSnow==0) sfcMeltPond = sfcMeltPond + prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) + ! increment change in storage associated with the surface melt pond (kg m-2) + if(nSnow==0) sfcMeltPond = sfcMeltPond + prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) - ! increment soil compression (kg m-2) - totalSoilCompress = totalSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression over whole layer (kg m-2) + endif ! (do_outer loop) ! **************************************************************************************************** ! *** END MAIN SOLVER ******************************************************************************** ! **************************************************************************************************** - ! increment sub-step + ! increment mean fluxes, soil compression, and canopy sublimation, reset on whole_step + dt_wght = dt_sub/whole_step ! define weight applied to each sub-step + do iVar=1,size(averageFlux_meta) + flux_inner%var(iVar)%dat(:) = flux_inner%var(iVar)%dat(:) + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:)*dt_wght + end do + innerSoilCompress = innerSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1)*dt_wght + if (nSnow>0)innerEffRainfall = innerEffRainfall + ( flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) + flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) )*dt_wght + + ! increment sub-step accepted step + dt_solvInner = dt_solvInner + dt_sub dt_solv = dt_solv + dt_sub + ! update first and last inner steps if did successful lastInnerStep, increment fluxes over data_step + if (lastInnerStep)then + firstInnerStep = .true. + lastInnerStep = .false. + dt_solvInner = 0._rkind + + dt_wght = whole_step/data_step ! define weight applied to each sub-step + do iVar=1,size(averageFlux_meta) + flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_inner%var(iVar)%dat(:)*dt_wght + end do + meanSoilCompress = meanSoilCompress + innerSoilCompress*dt_wght + effRainfall = effRainfall + innerEffRainfall*dt_wght + flux_mean%var(childFLUX_MEAN(iLookDIAG%scalarSoilCompress))%dat(1) = meanSoilCompress + flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySublimation))%dat(1) = sumCanopySublimation/data_step ! these two will be equal unless insufficient canopy water for sublim + flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarLatHeatCanopyEvap))%dat(1) = sumLatHeatCanopyEvap/data_step ! these two will be equal unless insufficient canopy water for sublim + flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSenHeatCanopy))%dat(1) = sumSenHeatCanopy/data_step ! these two will be equal unless insufficient canopy water for sublim + endif + ! save the time step to initialize the subsequent step if(dt_solv 0)then prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow)) prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow)*iden_water + & prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice) & * prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) + prog_data%var(iLookPROG%mLayerVolFracWat)%dat(1) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1) & + + prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1)*iden_ice/iden_water end if ! re-assign dimension lengths @@ -957,16 +1115,13 @@ subroutine coupled_em(& err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - ! overwrite flux_data with flux_mean (returns timestep-average fluxes for scalar variables) + ! overwrite flux_data and soil compression with the timestep-average value (returns timestep-average fluxes for scalar variables) do iVar=1,size(averageFlux_meta) flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:) = flux_mean%var(iVar)%dat(:) end do + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = meanSoilCompress ! *********************************************************************************************************************************** - ! *********************************************************************************************************************************** - ! *********************************************************************************************************************************** - ! *********************************************************************************************************************************** - ! --- ! *** balance checks... ! --------------------- @@ -993,11 +1148,11 @@ subroutine coupled_em(& averageSoilInflux => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarInfiltration) )%dat(1) ,& ! influx of water at the top of the soil profile (m s-1) averageSoilDrainage => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSoilDrainage) )%dat(1) ,& ! drainage from the bottom of the soil profile (m s-1) averageSoilBaseflow => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSoilBaseflow) )%dat(1) ,& ! total baseflow from throughout the soil profile (m s-1) + averageSoilCompress => flux_mean%var(childFLUX_MEAN(iLookDIAG%scalarSoilCompress) )%dat(1) ,& ! soil compression (kg m-2 s-1) averageGroundEvaporation => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarGroundEvaporation) )%dat(1) ,& ! soil evaporation (kg m-2 s-1) averageCanopyTranspiration => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopyTranspiration))%dat(1) ,& ! canopy transpiration (kg m-2 s-1) ! state variables in the vegetation canopy - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! canopy liquid water (kg m-2) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! canopy ice content (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! canopy ice content (kg m-2) ! state variables in the soil domain mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers) ,& ! depth of each soil layer (m) mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat(nSnow+1:nLayers) ,& ! volumetric ice content in each soil layer (-) @@ -1012,15 +1167,12 @@ subroutine coupled_em(& ! ----- ! * balance checks for the canopy... ! ---------------------------------- - - if (model_decisions(iLookDECISIONS%num_method)%iDecision==bEuler) checkMassBalance = .true. ! convergence criteria for bEuler - if (model_decisions(iLookDECISIONS%num_method)%iDecision==sundials) checkMassBalance = .false. ! sundials does not use this criteria + checkMassBalance = .true. ! if computing the vegetation flux if(computeVegFlux)then - - ! canopy water balance - balanceCanopyWater1 = scalarCanopyLiq + scalarCanopyIce + ! get the canopy water balance at the end of the time step + balanceCanopyWater1 = scalarCanopyWat ! balance checks for the canopy ! NOTE: need to put the balance checks in the sub-step loop so that we can re-compute if necessary @@ -1050,14 +1202,6 @@ subroutine coupled_em(& ! * balance checks for SWE... ! --------------------------- - ! recompute snow depth (m) and SWE (kg m-2) - if(nSnow > 0)then - prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow)) - prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow)*iden_water + & - prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice) & - * prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) - end if - ! check the individual layers if(printBalance .and. nSnow>0)then write(*,'(a,1x,10(f12.8,1x))') 'liqSnowInit = ', liqSnowInit @@ -1075,25 +1219,25 @@ subroutine coupled_em(& ! check SWE if(nSnow>0)then effSnowfall = averageThroughfallSnow + averageCanopySnowUnloading - effRainfall = averageThroughfallRain + averageCanopyLiqDrainage + !effRainfall is averageThroughfallRain + averageCanopyLiqDrainage only over snow newSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) delSWE = newSWE - (oldSWE - sfcMeltPond) massBalance = delSWE - (effSnowfall + effRainfall + averageSnowSublimation - averageSnowDrainage*iden_water)*data_step if(abs(massBalance) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then - print*, 'nSnow = ', nSnow - print*, 'nSub = ', nSub - write(*,'(a,1x,f20.10)') 'data_step = ', data_step - write(*,'(a,1x,f20.10)') 'oldSWE = ', oldSWE - write(*,'(a,1x,f20.10)') 'newSWE = ', newSWE - write(*,'(a,1x,f20.10)') 'delSWE = ', delSWE - write(*,'(a,1x,f20.10)') 'effRainfall = ', effRainfall*data_step - write(*,'(a,1x,f20.10)') 'effSnowfall = ', effSnowfall*data_step - write(*,'(a,1x,f20.10)') 'sublimation = ', averageSnowSublimation*data_step - write(*,'(a,1x,f20.10)') 'snwDrainage = ', averageSnowDrainage*iden_water*data_step - write(*,'(a,1x,f20.10)') 'sfcMeltPond = ', sfcMeltPond - write(*,'(a,1x,f20.10)') 'massBalance = ', massBalance - message=trim(message)//'SWE does not balance' - err=20; return + print*, 'nSnow = ', nSnow + print*, 'nSub = ', nSub + write(*,'(a,1x,f20.10)') 'data_step = ', data_step + write(*,'(a,1x,f20.10)') 'oldSWE = ', oldSWE + write(*,'(a,1x,f20.10)') 'newSWE = ', newSWE + write(*,'(a,1x,f20.10)') 'delSWE = ', delSWE + write(*,'(a,1x,f20.10)') 'effRainfall = ', effRainfall*data_step + write(*,'(a,1x,f20.10)') 'effSnowfall = ', effSnowfall*data_step + write(*,'(a,1x,f20.10)') 'sublimation = ', averageSnowSublimation*data_step + write(*,'(a,1x,f20.10)') 'snwDrainage = ', averageSnowDrainage*iden_water*data_step + write(*,'(a,1x,f20.10)') 'sfcMeltPond = ', sfcMeltPond + write(*,'(a,1x,f20.10)') 'massBalance = ', massBalance + message=trim(message)//'SWE does not balance' + err=20; return endif ! if failed mass balance check endif ! if snow layers exist @@ -1116,6 +1260,7 @@ subroutine coupled_em(& balanceSoilBaseflow = averageSoilBaseflow*iden_water*data_step balanceSoilDrainage = averageSoilDrainage*iden_water*data_step balanceSoilET = (averageCanopyTranspiration + averageGroundEvaporation)*data_step + balanceSoilCompress = averageSoilCompress*data_step ! check the individual layers if(printBalance)then @@ -1132,11 +1277,11 @@ subroutine coupled_em(& endif ! check the soil water balance - scalarSoilWatBalError = balanceSoilWater1 - (balanceSoilWater0 + (balanceSoilInflux + balanceSoilET - balanceSoilBaseflow - balanceSoilDrainage - totalSoilCompress) ) + scalarSoilWatBalError = balanceSoilWater1 - (balanceSoilWater0 + (balanceSoilInflux + balanceSoilET - balanceSoilBaseflow - balanceSoilDrainage - balanceSoilCompress) ) if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues write(*,*) 'solution method = ', ixSolution write(*,'(a,1x,f20.10)') 'data_step = ', data_step - write(*,'(a,1x,f20.10)') 'totalSoilCompress = ', totalSoilCompress + write(*,'(a,1x,f20.10)') 'balanceSoilCompress = ', balanceSoilCompress write(*,'(a,1x,f20.10)') 'scalarTotalSoilLiq = ', scalarTotalSoilLiq write(*,'(a,1x,f20.10)') 'scalarTotalSoilIce = ', scalarTotalSoilIce write(*,'(a,1x,f20.10)') 'balanceSoilWater0 = ', balanceSoilWater0 @@ -1146,8 +1291,6 @@ subroutine coupled_em(& write(*,'(a,1x,f20.10)') 'balanceSoilDrainage = ', balanceSoilDrainage write(*,'(a,1x,f20.10)') 'balanceSoilET = ', balanceSoilET write(*,'(a,1x,f20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError - write(*,'(a,1x,f20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError/iden_water - write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid ! error control message=trim(message)//'soil hydrology does not balance' err=20; return @@ -1164,10 +1307,10 @@ subroutine coupled_em(& ! save the surface temperature (just to make things easier to visualize) prog_data%var(iLookPROG%scalarSurfaceTemp)%dat(1) = prog_data%var(iLookPROG%mLayerTemp)%dat(1) - ! overwrite flux data with the timestep-average value + ! overwrite flux data with timestep-average value for all flux_mean vars, hard-coded to not happen if(.not.backwardsCompatibility)then do iVar=1,size(flux_mean%var) - flux_data%var(averageFlux_meta(iVar)%ixParent)%dat = flux_mean%var(iVar)%dat + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat = flux_mean%var(iVar)%dat end do end if diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 5ebec90ea..7e6b7711d 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -96,7 +96,8 @@ module eval8summa_module ! ********************************************************************************************************** subroutine eval8summa(& ! input: model control - dt, & ! intent(in): length of the time step (seconds) + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): entire time step for drainage pond rate nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers @@ -151,7 +152,8 @@ subroutine eval8summa(& ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control - real(rkind),intent(in) :: dt ! length of the time step (seconds) + real(rkind),intent(in) :: dt_cur ! current stepsize + real(rkind),intent(in) :: dt ! entire time step for drainage pond rate integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers @@ -221,9 +223,7 @@ subroutine eval8summa(& integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) real(rkind) :: xMin,xMax ! minimum and maximum values for water content - real(rkind) :: scalarCanopyHydTrial ! trial value for mass of water on the vegetation canopy (kg m-2) real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) - real(rkind),dimension(nLayers) :: mLayerVolFracHydTrial ! trial value for volumetric fraction of water (-), general vector merged from Wat and Liq real(rkind),dimension(nState) :: rVecScaled ! scaled residual vector character(LEN=256) :: cmessage ! error message of downwind routine real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy @@ -269,8 +269,8 @@ subroutine eval8summa(& scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) ! soil compression - scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in storage associated with compression of the soil matrix (-) + scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2 s-1) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in volumetric water content due to compression of soil (s-1) ! derivatives dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential @@ -662,10 +662,11 @@ subroutine eval8summa(& ! use non-sundials version because sundials version needs mLayerMatricHeadPrime call soilCmpres(& ! input: + dt_cur, & ! intent(in): length of the time step (seconds) ixRichards, & ! intent(in): choice of option for Richards' equation ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers - mLayerMatricHead(1:nSoil), & ! intent(in): matric head at the start of the time step (m) - mLayerMatricHeadTrial(1:nSoil), & ! intent(in): trial value of matric head (m) + mLayerMatricHead(1:nSoil), & ! intent(in): matric head at the start of the time step (m) + mLayerMatricHeadTrial(1:nSoil), & ! intent(in): trial value of matric head (m) mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric ice content in each soil layer (-) specificStorage, & ! intent(in): specific storage coefficient (m-1) @@ -676,23 +677,13 @@ subroutine eval8summa(& err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! compute the total change in storage associated with compression of the soil matrix (kg m-2) + ! compute the total change in storage associated with compression of the soil matrix (kg m-2 s-1) scalarSoilCompress = sum(mLayerCompress(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water - ! vegetation domain: get the correct water states (total water, or liquid water, depending on the state type) - if(computeVegFlux)then - scalarCanopyHydTrial = merge(scalarCanopyWatTrial, scalarCanopyLiqTrial, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) - else - scalarCanopyHydTrial = realMissing - endif - - ! snow+soil domain: get the correct water states (total water, or liquid water, depending on the state type) - mLayerVolFracHydTrial = merge(mLayerVolFracWatTrial, mLayerVolFracLiqTrial, (ixHydType==iname_watLayer .or. ixHydType==iname_matLayer) ) - ! compute the residual vector call computResid(& ! input: model control - dt, & ! intent(in): length of the time step (seconds) + dt_cur, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers @@ -702,13 +693,17 @@ subroutine eval8summa(& ! input: state variables (already disaggregated into scalars and vectors) scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) - scalarCanopyHydTrial, & ! intent(in): trial value of canopy hydrology state variable (kg m-2) + scalarCanopyWatTrial, & ! intent(in): trial value for the water on the vegetation canopy (kg m-2) mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) - mLayerVolFracHydTrial, & ! intent(in): trial vector of volumetric water content (-) scalarAquiferStorageTrial, & ! intent(in): trial value of storage of water in the aquifer (m) ! input: diagnostic variables defining the liquid water and ice content (function of state variables) scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value for the liq on the vegetation canopy (kg m-2) mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) + mLayerVolFracWatTrial, & ! intent(in): trial value for the volumetric water in each snow and soil layer (-) + mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liq in each snow and soil layer (-) + scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) + mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index f0742a21c..796fdc13a 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -80,7 +80,7 @@ module eval8summaSundials_module subroutine eval8summaSundials(& ! input: model control dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): entire time step + dt, & ! intent(in): entire time step for drainage pond rate nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers @@ -160,7 +160,7 @@ subroutine eval8summaSundials(& ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control real(rkind),intent(in) :: dt_cur ! current stepsize - real(rkind),intent(in) :: dt ! entire time step + real(rkind),intent(in) :: dt ! entire time step for drainage pond rate integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers @@ -287,8 +287,8 @@ subroutine eval8summaSundials(& mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) ! soil compression - scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in storage associated with compression of the soil matrix (-) + scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2 s-1) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in volumetric water content due to compression of soil (s-1) ! derivatives dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential @@ -472,14 +472,11 @@ subroutine eval8summaSundials(& ! update diagnostic variables and derivatives call updateVarsSundials(& ! input - dt_cur, & .false., & ! intent(in): logical flag if computing Jacobian for Sundials solver .false., & ! intent(in): logical flag to adjust temperature to account for the energy mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU - mLayerVolFracWatPrev, & ! intent(in): previous vector of total water matric potential (m) - mLayerMatricHeadPrev, & ! intent(in): previous vector of volumetric total water content (-) diag_data, & ! intent(inout): model diagnostic variables for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy @@ -587,13 +584,14 @@ subroutine eval8summaSundials(& ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + ! Below commented out because it seems more correct for Sundials to use the analytical form ! to conserve energy compute finite difference approximation of (theta_ice)' - if(dt_cur > 1e-14_rkind) then - scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur - do concurrent (iLayer=1:nLayers) - mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur - end do - endif ! if dt_cur is not too small + !if(dt_cur > 1e-14_rkind) then + ! scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur + ! do concurrent (iLayer=1:nLayers) + ! mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur + ! end do + !endif ! if dt_cur is not too small else if(ixHowHeatCap == closedForm)then call computHeatCapAnalytic(& ! input: control variables @@ -752,6 +750,9 @@ subroutine eval8summaSundials(& err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + ! compute the total change in storage associated with compression of the soil matrix (kg m-2 s-1) + scalarSoilCompress = sum(mLayerCompress(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water + ! compute the residual vector if (insideIDA)then dt1 = 1._qp ! always 1 for sundials since using Prime derivatives @@ -766,21 +767,21 @@ subroutine eval8summaSundials(& sMul, & ! intent(in): state vector multiplier (used in the residual calculations) fluxVec, & ! intent(in): flux vector ! input: state variables (already disaggregated into scalars and vectors) - scalarCanopyTempTrial, & ! intent(in): - mLayerTempTrial, & ! intent(in) - scalarCanairTempPrime, & ! intent(in): Prime value for the temperature of the canopy air space (K) - scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K) - scalarCanopyWatPrime, & - mLayerTempPrime, & ! intent(in): Prime value for the temperature of each snow and soil layer (K) - scalarAquiferStoragePrime, & ! intent(in): Prime value of storage of water in the aquifer (m) + scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) + mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) + scalarCanairTempPrime, & ! intent(in): Prime value for the temperature of the canopy air space (K s-1) + scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K s-1) + scalarCanopyWatPrime, & ! intent(in): Prime value for the water on the vegetation canopy (kg m-2 s-1) + mLayerTempPrime, & ! intent(in): Prime value for the temperature of each snow and soil layer (K s-1) + scalarAquiferStoragePrime, & ! intent(in): Prime value of storage of water in the aquifer (m s-1) ! input: diagnostic variables defining the liquid water and ice content (function of state variables) - scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2) - scalarCanopyLiqPrime, & ! intent(in): - mLayerVolFracIcePrime, & ! intent(in): Prime value for the volumetric ice in each snow and soil layer (-) - mLayerVolFracWatPrime, & - mLayerVolFracLiqPrime, & - scalarCanopyCmTrial, & ! intent(in) Cm of vegetation canopy - mLayerCmTrial, & ! intent(in) Cm of soil and snow + scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2 s-1) + scalarCanopyLiqPrime, & ! intent(in): Prime value for the liq on the vegetation canopy (kg m-2 s-1) + mLayerVolFracIcePrime, & ! intent(in): Prime value for the volumetric ice in each snow and soil layer (s-1) + mLayerVolFracWatPrime, & ! intent(in): Prime value for the volumetric water in each snow and soil layer (s-1) + mLayerVolFracLiqPrime, & ! intent(in): Prime value for the volumetric liq in each snow and soil layer (s-1) + scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) + mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 19f1c604f..aa1f70765 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -96,16 +96,6 @@ module opSplittin_module zLookup, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions -! look-up values for the choice of groundwater representation (local-column, or single-basin) -USE mDecisions_module,only: & - localColumn, & ! separate groundwater representation in each local soil column - singleBasin ! single groundwater store over the entire basin - -! look-up values for the choice of groundwater parameterization -USE mDecisions_module,only: & - qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization - bigBucket, & ! a big bucket (lumped aquifer model) - noExplicit ! no explicit groundwater parameterization ! look-up values for the numerical method USE mDecisions_module,only: & @@ -160,45 +150,43 @@ module opSplittin_module ! (1) Attempt different solutions in the following order: (a) fully coupled; (b) split by state type and by ! domain type for a given energy and mass split (vegetation, snow, and soil); and (c) scalar solution ! for a given state type and domain subset. -! (2) For a given split, compute a variable number of substeps (in varSubstepSundials). +! (2) For a given split, compute a variable number of substeps (in varSubstep). ! ********************************************************************************************************** subroutine opSplittin(& ! input: model control - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - nState, & ! intent(in): total number of state variables - dt, & ! intent(inout): time step (s) - firstSubStep, & ! intent(in): flag to denote first sub-step - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + nState, & ! intent(in): total number of state variables + dt, & ! intent(in): time step (s) + whole_step, & ! intent(in): length of whole step for surface drainage and average flux + firstSubStep, & ! intent(in): flag to denote first sub-step + firstInnerStep, & ! intent(in): flag to denote if the last time step in maxstep subStep + computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation ! input/output: data structures - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): index data - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - bvar_data, & ! intent(in): model variables for the local basin - lookup_data, & ! intent(in): lookup tables - model_decisions,& ! intent(in): model decisions + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): index data + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + bvar_data, & ! intent(in): model variables for the local basin + lookup_data, & ! intent(in): lookup tables + model_decisions, & ! intent(in): model decisions ! output: model control - dtMultiplier, & ! intent(out): substep multiplier (-) - tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt - stepFailure, & ! intent(out): flag to denote step failure - ixCoupling, & ! intent(out): coupling method used in this iteration - err,message) ! intent(out): error code and error message + dtMultiplier, & ! intent(out): substep multiplier (-) + tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt + stepFailure, & ! intent(out): flag to denote step failure + ixCoupling, & ! intent(out): coupling method used in this iteration + err,message) ! intent(out): error code and error message ! --------------------------------------------------------------------------------------- ! structure allocations USE allocspace_module,only:allocLocal ! allocate local data structures - ! simulation of fluxes and residuals given a trial state vector - USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content - USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential ! population/extraction of state vectors USE indexState_module,only:indexSplit ! get state indices USE varSubstep_module,only:varSubstep ! complete substeps for a given split - USE varSubstepSundials_module,only:varSubstepSundials ! complete substeps for a given split ! identify name of variable type (for error message) USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages implicit none @@ -210,7 +198,8 @@ subroutine opSplittin(& integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers integer(i4b),intent(in) :: nState ! total number of state variables - real(rkind),intent(inout) :: dt ! time step (seconds) + real(rkind),intent(in) :: dt ! time step (seconds) + real(rkind),intent(in) :: whole_step ! length of whole step for surface drainage and average flux logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) ! input/output: data structures @@ -247,20 +236,21 @@ subroutine opSplittin(& type(var_dlength) :: prog_temp ! temporary model prognostic variables type(var_dlength) :: diag_temp ! temporary model diagnostic variables type(var_dlength) :: flux_temp ! temporary model fluxes + type(var_dlength) :: flux_mean ! mean model fluxes + type(var_dlength) :: flux_mntemp ! temporary mean model fluxes type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(rkind),dimension(nLayers) :: mLayerVolFracIceInit ! initial vector for volumetric fraction of ice (-) ! ------------------------------------------------------------------------------------------------------ ! * operator splitting ! ------------------------------------------------------------------------------------------------------ ! minimum timestep - real(rkind),parameter :: dtmin_coupled=1800._rkind ! minimum time step for the fully coupled solution (seconds) - real(rkind),parameter :: dtmin_split=60._rkind ! minimum time step for the fully split solution (seconds) - real(rkind),parameter :: dtmin_scalar=10._rkind ! minimum time step for the scalar solution (seconds) + real(rkind),parameter :: dtmin_coupled=1800._rkind ! minimum time step for the fully coupled solution (seconds) + real(rkind),parameter :: dtmin_split=60._rkind ! minimum time step for the fully split solution (seconds) + real(rkind),parameter :: dtmin_scalar=10._rkind ! minimum time step for the scalar solution (seconds) real(rkind) :: dt_min ! minimum time step (seconds) real(rkind) :: dtInit ! initial time step (seconds) ! explicit error tolerance (depends on state type split, so defined here) - real(rkind),parameter :: errorTolLiqFlux=0.01_rkind ! error tolerance in the explicit solution (liquid flux) - real(rkind),parameter :: errorTolNrgFlux=10._rkind ! error tolerance in the explicit solution (energy flux) + real(rkind),parameter :: errorTolLiqFlux=0.01_rkind ! error tolerance in the explicit solution (liquid flux) + real(rkind),parameter :: errorTolNrgFlux=10._rkind ! error tolerance in the explicit solution (energy flux) ! number of substeps taken for a given split integer(i4b) :: nSubsteps ! number of substeps taken for a given split ! named variables defining the coupling and solution method @@ -291,18 +281,14 @@ subroutine opSplittin(& logical(lgt) :: failedMinimumStep ! flag to denote failure of substepping for a given split integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) integer(i4b) :: nCoupling - real(qp) :: dt_out ! + logical(lgt) :: firstInnerStep ! flag to denote if the first time step in maxstep subStep ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- globalVars: associate(& ! model decisions - ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization - ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical method, backward Euler or SUNDIALS/IDA - ! domain boundary conditions - airtemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) ! vector of energy and hydrology indices for the snow and soil domains ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain @@ -323,37 +309,7 @@ subroutine opSplittin(& numberStateSplit => indx_data%var(iLookINDEX%numberStateSplit )%dat(1) ,& ! intent(inout): [i4b] number of state splitting solutions (-) numberDomainSplitNrg => indx_data%var(iLookINDEX%numberDomainSplitNrg )%dat(1) ,& ! intent(inout): [i4b] number of domain splitting solutions for energy (-) numberDomainSplitMass => indx_data%var(iLookINDEX%numberDomainSplitMass)%dat(1) ,& ! intent(inout): [i4b] number of domain splitting solutions for mass (-) - numberScalarSolutions => indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1) ,& ! intent(inout): [i4b] number of scalar solutions (-) - ! domain configuration - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ! depth-varying soil parameters - vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) - ! soil parameters - specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) - ! model diagnostic variables (fraction of liquid water) - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) - mLayerMeltFreeze => diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat ,& ! intent(out): [dp(:)] melt-freeze in each snow and soil layer (kg m-3) - ! model state variables (vegetation canopy) - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(out): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(out): [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(out): [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(out): [dp] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(out): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(out): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(out): [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(out): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(out): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(out): [dp(:)] matric head (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat & ! intent(out): [dp(:)] matric potential of liquid water (m) + numberScalarSolutions => indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1) & ! intent(inout): [i4b] number of scalar solutions (-) ) ! --------------------------------------------------------------------------------------- ! initialize error control @@ -378,6 +334,7 @@ subroutine opSplittin(& ! initialize the first success call firstSuccess=.false. + if (.not.firstInnerStep) firstSuccess=.true. ! initialize the flags tooMuchMelt=.false. ! too much melt (merge snow layers) @@ -392,30 +349,6 @@ subroutine opSplittin(& ! initialize the state check stateCheck(:) = 0 - ! compute the total water content in the vegetation canopy - scalarCanopyWat = scalarCanopyLiq + scalarCanopyIce ! kg m-2 - - ! save volumetric ice content at the start of the step - ! NOTE: used for volumetric loss due to melt-freeze - mLayerVolFracIceInit(:) = mLayerVolFracIce(:) - - ! compute the total water content in snow and soil - ! NOTE: no ice expansion allowed for soil - if(nSnow>0)& - mLayerVolFracWat( 1:nSnow ) = mLayerVolFracLiq( 1:nSnow ) + mLayerVolFracIce( 1:nSnow )*(iden_ice/iden_water) - mLayerVolFracWat(nSnow+1:nLayers) = mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) - - ! compute the liquid water matric potential (m) - ! NOTE: include ice content as part of the solid porosity - major effect of ice is to reduce the pore size; ensure that effSat=1 at saturation - ! (from Zhao et al., J. Hydrol., 1997: Numerical analysis of simultaneous heat and mass transfer...) - do iSoil=1,nSoil - call liquidHead(mLayerMatricHead(iSoil),mLayerVolFracLiq(nSnow+iSoil),mLayerVolFracIce(nSnow+iSoil), & ! input: state variables - vGn_alpha(iSoil),vGn_n(iSoil),theta_sat(iSoil),theta_res(iSoil),vGn_m(iSoil), & ! input: parameters - matricHeadLiq=mLayerMatricHeadLiq(iSoil), & ! output: liquid water matric potential (m) - err=err,message=cmessage) ! output: error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - end do ! looping through soil layers (computing liquid water matric potential) - ! allocate space for the flux mask (used to define when fluxes are updated) call allocLocal(flux_meta(:),fluxMask,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif @@ -436,11 +369,19 @@ subroutine opSplittin(& call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + ! allocate space for the mean flux variable structure + call allocLocal(flux_meta(:),flux_mean,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! allocate space for the temporary mean flux variable structure + call allocLocal(flux_meta(:),flux_mntemp,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + ! allocate space for the derivative structure call allocLocal(deriv_meta(:),deriv_data,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - ! intialize the flux conter + ! intialize the flux counter do iVar=1,size(flux_meta) ! loop through fluxes fluxCount%var(iVar)%dat(:) = 0 end do @@ -449,7 +390,8 @@ subroutine opSplittin(& do iVar=1,size(flux_meta) ! loop through fluxes if(flux2state_orig(iVar)%state1==integerMissing .and. flux2state_orig(iVar)%state2==integerMissing) cycle ! flux does not depend on state (e.g., input) if(flux2state_orig(iVar)%state1==iname_watCanopy .and. .not.computeVegFlux) cycle ! use input fluxes in cases where there is no canopy - flux_data%var(iVar)%dat(:) = 0._rkind + if (firstInnerStep) flux_data%var(iVar)%dat(:) = 0._rkind + flux_mean%var(iVar)%dat(:) = 0._rkind end do ! initialize derivatives @@ -458,10 +400,6 @@ subroutine opSplittin(& end do ! ========================================================================================================================================== - ! ========================================================================================================================================== - ! ========================================================================================================================================== - ! ========================================================================================================================================== - ! loop through different coupling strategies coupling: do ixCoupling=1,nCoupling @@ -542,6 +480,7 @@ subroutine opSplittin(& ! initialize the first flux call firstFluxCall=.true. + if (.not.firstInnerStep) firstFluxCall=.false. ! get the number of split layers select case(ixSolution) @@ -569,8 +508,6 @@ subroutine opSplittin(& ! avoid redundant case where vector solution is of length 1 if(ixSolution==vector .and. count(stateMask)==1) cycle solution - - ! ----- ! * assemble vectors for a given split... ! --------------------------------------- @@ -700,8 +637,6 @@ subroutine opSplittin(& end associate stateSubset - ! ******************************************************************************************************************************* - ! ******************************************************************************************************************************* ! ******************************************************************************************************************************* ! ***** trial with a given solution method... @@ -714,6 +649,7 @@ subroutine opSplittin(& ! reset the flag for the first flux call if(.not.firstSuccess) firstFluxCall=.true. + ! update variables, also updated inside Sundials (if fail a split will need these) ! save/recover copies of prognostic variables do iVar=1,size(prog_data%var) select case(failure) @@ -730,11 +666,15 @@ subroutine opSplittin(& end select end do ! looping through variables - ! save/recover copies of model fluxes + ! save/recover copies of model fluxes and mean fluxes do iVar=1,size(flux_data%var) select case(failure) - case(.false.); flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) - case(.true.); flux_data%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) + case(.false.) + flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_mntemp%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + case(.true.) + flux_data%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) + flux_mean%var(iVar)%dat(:) = flux_mntemp%var(iVar)%dat(:) end select end do ! looping through variables @@ -745,14 +685,13 @@ subroutine opSplittin(& ! keep track of the number of scalar solutions if(ixSolution==scalar) numberScalarSolutions = numberScalarSolutions + 1 - ! solve variable subset for one full time step - select case(ixNumericalMethod) - case(sundials) - call varSubstepSundials(& + ! solve variable subset for one full time steps + call varSubstep(& ! input: model control dt, & ! intent(in) : time step (s) dtInit, & ! intent(in) : initial time step (seconds) dt_min, & ! intent(in) : minimum time step (seconds) + whole_step, & ! intent(in) : length of whole step for surface drainage and average flux nSubset, & ! intent(in) : total number of variables in the state subset doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature firstSubStep, & ! intent(in) : flag to denote first sub-step @@ -773,6 +712,7 @@ subroutine opSplittin(& prog_data, & ! intent(inout) : model prognostic variables for a local HRU diag_data, & ! intent(inout) : model diagnostic variables for a local HRU flux_data, & ! intent(inout) : model fluxes for a local HRU + flux_mean, & ! intent(inout) : mean model fluxes for a local HRU deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables bvar_data, & ! intent(in) : model variables for the local basin ! output: control @@ -782,49 +722,7 @@ subroutine opSplittin(& failedMinimumStep, & ! intent(out) : flag for failed substeps reduceCoupledStep, & ! intent(out) : flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt - dt_out, & ! intent(out) err,cmessage) ! intent(out) : error code and error message - dt = dt_out - case(bEuler) - call varSubstep(& - ! input: model control - dt, & ! intent(in) : time step (s) - dtInit, & ! intent(in) : initial time step (seconds) - dt_min, & ! intent(in) : minimum time step (seconds) - nSubset, & ! intent(in) : total number of variables in the state subset - doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature - firstSubStep, & ! intent(in) : flag to denote first sub-step - firstFluxCall, & ! intent(inout) : flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in) : flag to denote if computing energy flux over vegetation - (ixSolution==scalar), & ! intent(in) : flag to denote computing the scalar solution - iStateSplit, & ! intent(in) : index of the layer in the splitting operation - fluxMask, & ! intent(in) : mask for the fluxes used in this given state subset - fluxCount, & ! intent(inout) : number of times fluxes are updated (should equal nsubstep) - ! input/output: data structures - model_decisions, & ! intent(in) : model decisions - lookup_data, & ! intent(in) : lookup tables - type_data, & ! intent(in) : type of vegetation and soil - attr_data, & ! intent(in) : spatial attributes - forc_data, & ! intent(in) : model forcing data - mpar_data, & ! intent(in) : model parameters - indx_data, & ! intent(inout) : index data - prog_data, & ! intent(inout) : model prognostic variables for a local HRU - diag_data, & ! intent(inout) : model diagnostic variables for a local HRU - flux_data, & ! intent(inout) : model fluxes for a local HRU - deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables - bvar_data, & ! intent(in) : model variables for the local basin - ! output: control - ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) - dtMultiplier, & ! intent(out) : substep multiplier (-) - nSubsteps, & ! intent(out) : number of substeps taken for a given split - failedMinimumStep, & ! intent(out) : flag for failed substeps - reduceCoupledStep, & ! intent(out) : flag to reduce the length of the coupled step - tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt - err,cmessage) ! intent(out) : error code and error message - ! check - case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return - end select - if(err/=0)then message=trim(message)//trim(cmessage) if(err>0) return @@ -903,15 +801,11 @@ subroutine opSplittin(& ! ***** trial with a given solution method... ! ******************************************************************************************************************************* - ! ******************************************************************************************************************************* - ! ******************************************************************************************************************************* end do domainSplit ! domain type splitting loop - end do stateThenDomain ! switch between the state and the domain - ! ----- ! * reset state variables for the mass split... ! --------------------------------------------- @@ -949,11 +843,6 @@ subroutine opSplittin(& ! use step halving if unable to complete the fully coupled solution in one substep if(ixCoupling/=fullyCoupled .or. nSubsteps>1) dtMultiplier=0.5_rkind - ! compute the melt in each snow and soil layer - if(nSnow>0)& - diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(1:nSnow) = -( mLayerVolFracIce(1:nSnow) - mLayerVolFracIceInit(1:nSnow) ) * iden_ice - diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat(nSnow+1:nLayers) = -(mLayerVolFracIce(nSnow+1:nLayers) - mLayerVolFracIceInit(nSnow+1:nLayers))*iden_water - ! end associate statements end associate globalVars diff --git a/build/source/engine/snowLiqFlx.f90 b/build/source/engine/snowLiqFlx.f90 index e31612523..ea3f1a55e 100644 --- a/build/source/engine/snowLiqFlx.f90 +++ b/build/source/engine/snowLiqFlx.f90 @@ -43,7 +43,6 @@ module snowLiqFlx_module implicit none private public::snowLiqFlx -public::snowLiqFlxSundials contains @@ -118,7 +117,7 @@ subroutine snowLiqFlx(& mLayerPoreSpace => diag_data%var(iLookDIAG%mLayerPoreSpace)%dat, & ! intent(inout): pore space in each snow layer (-) mLayerThetaResid => diag_data%var(iLookDIAG%mLayerThetaResid)%dat & ! intent(inout): esidual volumetric liquid water content in each snow layer (-) ) ! association of local variables with information in the data structures - ! ------------------------------------------------------------------------------------------------------------------------------------------ + ! ------------------------------------------------------------------------------------------------------------------------------------------ ! initialize error control err=0; message='snowLiqFlx/' @@ -151,7 +150,7 @@ subroutine snowLiqFlx(& ! define the liquid flux at the upper boundary (m s-1) iLayerLiqFluxSnow(0) = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water - iLayerLiqFluxSnowDeriv(0) = 0._rkind !computed inside computJacobSundials_module + iLayerLiqFluxSnowDeriv(0) = 0._rkind !computed inside computJacob ! compute properties fixed over the time step if(firstFluxCall)then @@ -190,146 +189,4 @@ subroutine snowLiqFlx(& end subroutine snowLiqFlx -! ************************************************************************************************ -! public subroutine snowLiqFlxSundials: compute liquid water flux through the snowpack -! ************************************************************************************************ -subroutine snowLiqFlxSundials(& - ! input: model control - nSnow, & ! intent(in): number of snow layers - firstFluxCall, & ! intent(in): the first flux call - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: forcing for the snow domain - scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) - scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) - ! input: model state vector - mLayerVolFracIce, & ! intent(in) - mLayerVolFracLiqTrial, & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) - ! input-output: data structures - indx_data, & ! intent(in): model indices - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output: fluxes and derivatives - iLayerLiqFluxSnow, & ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) - iLayerLiqFluxSnowDeriv, & ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) - ! output: error control - err,message) ! intent(out): error control - implicit none - ! input: model control - integer(i4b),intent(in) :: nSnow ! number of snow layers - logical(lgt),intent(in) :: firstFluxCall ! the first flux call - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - ! input: forcing for the snow domain - real(rkind),intent(in) :: scalarThroughfallRain ! computed throughfall rate (kg m-2 s-1) - real(rkind),intent(in) :: scalarCanopyLiqDrainage ! computed drainage of liquid water (kg m-2 s-1) - ! input: model state vector - real(rkind),intent(in) :: mLayerVolFracIce(:) - real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value of volumetric fraction of liquid water at the current iteration (-) - ! input-output: data structures - type(var_ilength),intent(in) :: indx_data ! model indices - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - ! output: fluxes and derivatives - real(rkind),intent(inout) :: iLayerLiqFluxSnow(0:) ! vertical liquid water flux at layer interfaces (m s-1) - real(rkind),intent(inout) :: iLayerLiqFluxSnowDeriv(0:) ! derivative in vertical liquid water flux at layer interfaces (m s-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ------------------------------------------------------------------------------------------------------------------------------------------ - ! local variables - integer(i4b) :: i ! search index for scalar solution - integer(i4b) :: iLayer ! layer index - integer(i4b) :: ixTop ! top layer in subroutine call - integer(i4b) :: ixBot ! bottom layer in subroutine call - real(rkind) :: multResid ! multiplier for the residual water content (-) - real(rkind),parameter :: residThrs=550._rkind ! ice density threshold to reduce residual liquid water content (kg m-3) - real(rkind),parameter :: residScal=10._rkind ! scaling factor for residual liquid water content reduction factor (kg m-3) - real(rkind),parameter :: maxVolIceContent=0.7_rkind ! maximum volumetric ice content to store water (-) - real(rkind) :: availCap ! available storage capacity [0,1] (-) - real(rkind) :: relSaturn ! relative saturation [0,1] (-) - ! ------------------------------------------------------------------------------------------------------------------------------------------ - ! make association of local variables with information in the data structures - associate(& - ! input: layer indices - ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers - ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat, & ! intent(in): index in the state subset for hydrology state variables in the snow domain - ! input: snow properties and parameters - Fcapil => mpar_data%var(iLookPARAM%Fcapil)%dat(1), & ! intent(in): capillary retention as a fraction of the total pore volume (-) - k_snow => mpar_data%var(iLookPARAM%k_snow)%dat(1), & ! intent(in): hydraulic conductivity of snow (m s-1), 0.0055 = approx. 20 m/hr, from UEB - mw_exp => mpar_data%var(iLookPARAM%mw_exp)%dat(1), & ! intent(in): exponent for meltwater flow (-) - ! input/output: diagnostic variables -- only computed for the first iteration - mLayerPoreSpace => diag_data%var(iLookDIAG%mLayerPoreSpace)%dat, & ! intent(inout): pore space in each snow layer (-) - mLayerThetaResid => diag_data%var(iLookDIAG%mLayerThetaResid)%dat & ! intent(inout): esidual volumetric liquid water content in each snow layer (-) - ) ! association of local variables with information in the data structures - ! ------------------------------------------------------------------------------------------------------------------------------------------ - ! initialize error control - err=0; message='snowLiqFlxSundials/' - - ! check that the input vectors match nSnow - if(size(mLayerVolFracLiqTrial)/=nSnow .or. size(mLayerVolFracIce)/=nSnow .or. & - size(iLayerLiqFluxSnow)/=nSnow+1 .or. size(iLayerLiqFluxSnowDeriv)/=nSnow+1) then - err=20; message=trim(message)//'size mismatch of input/output vectors'; return - end if - - ! check the meltwater exponent is >=1 - if(mw_exp<1._rkind)then; err=20; message=trim(message)//'meltwater exponent < 1'; return; end if - - ! get the indices for the snow+soil layers - ixTop = integerMissing - if(scalarSolution)then - do i=1,size(ixSnowOnlyHyd) - if(ixSnowOnlyHyd(i) /= integerMissing)then - ixTop=ixLayerState(i) - ixBot=ixTop - exit ! break out of loop once found - endif - end do - if(ixTop == integerMissing)then - err=20; message=trim(message)//'Unable to identify snow layer for scalar solution!'; return - end if - else - ixTop = 1 - ixBot = nSnow - endif - - ! define the liquid flux at the upper boundary (m s-1) - iLayerLiqFluxSnow(0) = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water - iLayerLiqFluxSnowDeriv(0) = 0._rkind - - ! compute properties fixed over the time step - if(firstFluxCall)then - ! loop through snow layers - do iLayer=1,nSnow - ! compute the reduction in liquid water holding capacity at high snow density (-) - multResid = 1._rkind / ( 1._rkind + exp( (mLayerVolFracIce(iLayer)*iden_ice - residThrs) / residScal) ) - ! compute the pore space (-) - mLayerPoreSpace(iLayer) = 1._rkind - mLayerVolFracIce(iLayer) - ! compute the residual volumetric liquid water content (-) - mLayerThetaResid(iLayer) = Fcapil*mLayerPoreSpace(iLayer) * multResid - end do ! (looping through snow layers) - end if ! (if the first flux call) - - ! compute fluxes - do iLayer=ixTop,ixBot ! (loop through snow layers) - ! check that flow occurs - if(mLayerVolFracLiqTrial(iLayer) > mLayerThetaResid(iLayer))then - ! compute the relative saturation (-) - availCap = mLayerPoreSpace(iLayer) - mLayerThetaResid(iLayer) ! available capacity - relSaturn = (mLayerVolFracLiqTrial(iLayer) - mLayerThetaResid(iLayer)) / availCap ! relative saturation - iLayerLiqFluxSnow(iLayer) = k_snow*relSaturn**mw_exp - iLayerLiqFluxSnowDeriv(iLayer) = ( (k_snow*mw_exp)/availCap ) * relSaturn**(mw_exp - 1._rkind) - if(mLayerVolFracIce(iLayer) > maxVolIceContent)then ! NOTE: use start-of-step ice content, to avoid convergence problems - ! ** allow liquid water to pass through under very high ice density - iLayerLiqFluxSnow(iLayer) = iLayerLiqFluxSnow(iLayer) + iLayerLiqFluxSnow(iLayer-1) !NOTE: derivative may need to be updated in future. - end if - else ! flow does not occur - iLayerLiqFluxSnow(iLayer) = 0._rkind - iLayerLiqFluxSnowDeriv(iLayer) = 0._rkind - endif ! storage above residual content - end do ! loop through snow layers - ! end association of local variables with information in the data structures - end associate - -end subroutine snowLiqFlxSundials end module snowLiqFlx_module diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index 84bab394b..df51f376c 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -92,7 +92,7 @@ subroutine soilLiqFlx(& ! input: model control nSoil, & ! intent(in): number of soil layers doInfiltrate, & ! intent(in): flag to compute infiltration - scalarSolution, & ! intent(in): flag to indicate the scalar solution + scalarSolution, & ! intent(in): flag to indicate the scalar solution deriv_desired, & ! intent(in): flag indicating if derivatives are desired ! input: trial state variables mLayerTempTrial, & ! intent(in): temperature (K) diff --git a/build/source/engine/soil_utils.f90 b/build/source/engine/soil_utils.f90 index 2038a2f7d..00436d0ea 100644 --- a/build/source/engine/soil_utils.f90 +++ b/build/source/engine/soil_utils.f90 @@ -339,11 +339,11 @@ function dTheta_dPsi(psi,alpha,theta_res,theta_sat,n,m) real(rkind),intent(in) :: m ! vGn "m" parameter (-) real(rkind) :: dTheta_dPsi ! derivative of the soil water characteristic (m-1) if(psi<=0._rkind)then - dTheta_dPsi = (theta_sat-theta_res) * & - (-m*(1._rkind + (psi*alpha)**n)**(-m-1._rkind)) * n*(psi*alpha)**(n-1._rkind) * alpha - if(abs(dTheta_dPsi) < epsilon(psi)) dTheta_dPsi = epsilon(psi) + dTheta_dPsi = (theta_sat-theta_res) * & + (-m*(1._rkind + (psi*alpha)**n)**(-m-1._rkind)) * n*(psi*alpha)**(n-1._rkind) * alpha + if(abs(dTheta_dPsi) < epsilon(psi)) dTheta_dPsi = epsilon(psi) else - dTheta_dPsi = epsilon(psi) + dTheta_dPsi = epsilon(psi) end if end function dTheta_dPsi diff --git a/build/source/engine/summaSolve.f90 b/build/source/engine/summaSolve.f90 index 91e5757e6..1e7329584 100644 --- a/build/source/engine/summaSolve.f90 +++ b/build/source/engine/summaSolve.f90 @@ -86,7 +86,8 @@ module summaSolve_module ! ********************************************************************************************************* subroutine summaSolve(& ! input: model control - dt, & ! intent(in): length of the time step (seconds) + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate iter, & ! intent(in): iteration index nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -138,7 +139,8 @@ subroutine summaSolve(& implicit none ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control - real(rkind),intent(in) :: dt ! length of the time step (seconds) + real(rkind),intent(in) :: dt_cur ! current stepsize + real(rkind),intent(in) :: dt ! entire time step for drainage pond rate integer(i4b),intent(in) :: iter ! interation index integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers @@ -233,7 +235,7 @@ subroutine summaSolve(& ! or in the call to eval8summa in the previous iteration (within lineSearchRefinement or trustRegionRefinement) call computJacob(& ! input: model control - dt, & ! intent(in): length of the time step (seconds) + dt_cur, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers @@ -806,7 +808,7 @@ subroutine numJacobian(stateVec,dMat,nJac,err,message) ! compute the row of the Jacobian matrix select case(ixNumType) case(ixNumRes); nJac(:,iJac) = real(resVecJac - resVecInit, kind(rkind) )/dx ! Jacobian based on residuals - case(ixNumFlux); nJac(:,iJac) = -dt*(fluxVecJac(:) - fluxVecInit(:))/dx ! Jacobian based on fluxes + case(ixNumFlux); nJac(:,iJac) = -dt_cur*(fluxVecJac(:) - fluxVecInit(:))/dx ! Jacobian based on fluxes case default; err=20; message=trim(message)//'Jacobian option not found'; return end select @@ -855,7 +857,7 @@ subroutine testBandMat(check,err,message) ! compute the full Jacobian matrix call computJacob(& ! input: model control - dt, & ! intent(in): length of the time step (seconds) + dt_cur, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers @@ -927,6 +929,7 @@ subroutine eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err ! compute the flux and the residual vector for a given state vector call eval8summa(& ! input: model control + dt_cur, & ! intent(in): current stepsize dt, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index e33a3ebe0..7b826aa9f 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -78,6 +78,8 @@ module summaSolveSundialsIDA_module implicit none private::setInitialCondition private::setSolverParams + private::find_rootdir + public::layerDisCont4IDA public::summaSolveSundialsIDA contains @@ -112,16 +114,15 @@ subroutine summaSolveSundialsIDA( & ! input-output: data structures indx_data, & ! intent(in): index data diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_temp, & ! intent(inout): model fluxes for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU - flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step + flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a dt deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + mLayerCmpress_sum, & ! intent(inout): sum of compression of the soil matrix ! output ixSaturation, & ! intent(inout) index of the lowest saturated layer (NOTE: only computed on the first iteration) idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt - mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix - dt_out, & ! intent(out): time step + dt_out, & ! intent(out): time step sum for entire data window at termination of sundials stateVec, & ! intent(out): model state vector stateVecPrime, & ! intent(out): derivative of model state vector err,message & ! intent(out): error control @@ -143,7 +144,7 @@ subroutine summaSolveSundialsIDA( & USE allocspace_module,only:allocLocal ! allocate local data structures USE eval8summaSundials_module,only:eval8summa4IDA ! DAE/ODE functions USE eval8summaSundials_module,only:eval8summaSundials ! residual of DAE - USE computJacobSundials_module,only:computJacob4IDA ! system Jacobian + USE computJacobSundials_module,only:computJacob4IDA ! system Jacobian USE tol4IDA_module,only:computWeight4IDA ! weigth required for tolerances USE var_derive_module,only:calcHeight ! height at layer interfaces and layer mid-point @@ -168,7 +169,7 @@ subroutine summaSolveSundialsIDA( & ! input: state vectors real(rkind),intent(in) :: stateVecInit(:) ! model state vector real(qp),intent(in) :: sMul(:) ! state vector multiplier (used in the residual calculations) - real(rkind), intent(inout) :: dMat(:) + real(rkind), intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures type(zLookup),intent(in) :: lookup_data ! lookup tables type(var_i), intent(in) :: type_data ! type of vegetation and soil @@ -180,9 +181,8 @@ subroutine summaSolveSundialsIDA( & type(var_ilength), intent(in) :: indx_data ! indices defining model states and layers ! input-output: data structures type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_temp ! model fluxes for a local HRU type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: flux_sum ! sum of fluxes + type(var_dlength),intent(inout) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables real(rkind),intent(inout) :: mLayerCmpress_sum(:) ! sum of soil compress ! output: state vectors @@ -190,8 +190,8 @@ subroutine summaSolveSundialsIDA( & real(rkind),intent(inout) :: stateVec(:) ! model state vector (y) real(rkind),intent(inout) :: stateVecPrime(:) ! model state vector (y') logical(lgt),intent(out) :: idaSucceeds ! flag to indicate if IDA is successful - logical(lgt),intent(inout) :: tooMuchMelt ! flag to denote that there was too much melt - real(qp),intent(out) :: dt_out ! time step + logical(lgt),intent(inout) :: tooMuchMelt ! flag to denote that there was too much melt + real(qp),intent(out) :: dt_out ! time step sum for entire data window at termination of sundials ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -212,19 +212,18 @@ subroutine summaSolveSundialsIDA( & logical(lgt) :: feasible ! feasibility flag real(qp) :: t0 ! staring time real(qp) :: dt_last(1) ! last time step - integer(kind = 8) :: mu, lu ! in banded matrix mode - integer(i4b) :: iVar - logical(lgt) :: startQuadrature - real(rkind) :: mLayerMatricHeadLiqPrev(nSoil) - real(qp) :: h_init - integer(c_long) :: nState ! total number of state variables - real(rkind) :: rVec(nStat) - real(qp) :: tret(1) - logical(lgt) :: mergedLayers - logical(lgt),parameter :: offErrWarnMessage = .false. - real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) - real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) - integer(i4b) :: i + integer(kind = 8) :: mu, lu ! in banded matrix mode in Sundials type + integer(c_long) :: nState ! total number of state variables in Sundials type + real(rkind) :: rVec(nStat) ! residual vector + integer(i4b) :: iVar, i ! indices + integer(i4b) :: nRoot ! total number of roots (events) to find + real(qp) :: tret(1) ! time in data window + integer(i4b),allocatable :: rootsfound(:) ! crossing direction of discontinuities + integer(i4b),allocatable :: rootdir(:) ! forced crossing direction of discontinuities + logical(lgt) :: tinystep ! if step goes below small size + logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true + logical(lgt),parameter :: detect_events = .true. ! flag to do event detection and restarting, default true + logical(lgt),parameter :: use_fdJac = .false. ! flag to use finite difference Jacobian, default false ! ----------------------------------------------------------------------------------------------------- @@ -254,7 +253,7 @@ subroutine summaSolveSundialsIDA( & eqns_data%sMul = sMul allocate( eqns_data%dMat(nState) ) - eqns_data%dMat = dMat + eqns_data%dMat = dMat ! allocate space for the temporary prognostic variable structure call allocLocal(prog_meta(:),eqns_data%prog_data,nSnow,nSoil,err,message) @@ -306,7 +305,6 @@ subroutine summaSolveSundialsIDA( & allocate( eqns_data%fluxVec(nState) ) allocate( eqns_data%resSink(nState) ) - startQuadrature = .true. retval = FSUNContext_Create(c_null_ptr, sunctx) ! create serial vectors @@ -337,6 +335,32 @@ subroutine summaSolveSundialsIDA( & retval = FIDAWFtolerances(ida_mem, c_funloc(computWeight4IDA)) if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDAWFtolerances'; return; endif + ! initialize rootfinding problem and allocate space, counting roots + if(detect_events)then + nRoot = 0 + if(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing) nRoot = nRoot+1 + if(nSnow>0)then + do i = 1,nSnow + if(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing) nRoot = nRoot+1 + enddo + endif + if(nSoil>0)then + do i = 1,nSoil + if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing) nRoot = nRoot+1 + if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing) nRoot = nRoot+1 + enddo + endif + allocate( rootsfound(nRoot) ) + allocate( rootdir(nRoot) ) + rootdir = 0 + retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4IDA)) + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif + else ! will not use, allocate at something + nRoot = 1 + allocate( rootsfound(nRoot) ) + allocate( rootdir(nRoot) ) + endif + ! define the form of the matrix select case(ixMatrix) case(ixBandMatrix) @@ -368,9 +392,10 @@ subroutine summaSolveSundialsIDA( & if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetLinearSolver'; return; endif ! Set the user-supplied Jacobian routine - !comment this line out to use FD Jacobian - retval = FIDASetJacFn(ida_mem, c_funloc(computJacob4IDA)) - if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetJacFn'; return; endif + if(.not.use_fdJac)then + retval = FIDASetJacFn(ida_mem, c_funloc(computJacob4IDA)) + if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetJacFn'; return; endif + endif ! Create Newton SUNNonlinearSolver object sunnonlin_NLS => FSUNNonlinSol_Newton(sunvec_y, sunctx) @@ -400,7 +425,6 @@ subroutine summaSolveSundialsIDA( & eqns_data%scalarCanopyLiqPrev = prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) - mLayerMatricHeadLiqPrev(:) = diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat(:) eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) @@ -409,13 +433,23 @@ subroutine summaSolveSundialsIDA( & eqns_data%scalarAquiferStoragePrev = prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) eqns_data%ixSaturation = ixSaturation + tinystep = .false. + !********************************************************************************** !****************************** Main Solver *************************************** !************************* loop on one_step mode ********************************** !********************************************************************************** - tret(1) = t0 ! intial time + tret(1) = t0 ! initial time do while(tret(1) < dt) + + ! call this at beginning of step to reduce root bouncing (only looking in one direction) + if(detect_events .and. .not.tinystep)then + call find_rootdir(eqns_data, rootdir) + retval = FIDASetRootDirection(ida_mem, rootdir) + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetRootDirection'; return; endif + endif + eqns_data%firstFluxCall = .false. eqns_data%firstSplitOper = .true. ! call IDASolve, advance solver just one internal step @@ -505,12 +539,12 @@ subroutine summaSolveSundialsIDA( & ! sum of fluxes do iVar=1,size(flux_meta) - flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * dt_last(1) + flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * dt_last(1) end do - ! sum of mLayerCmpress + ! sum of mLayerCmpress over the time step, since using prev value not * dt_last(1) mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & - * ( eqns_data%mLayerMatricHeadLiqTrial(:) - mLayerMatricHeadLiqPrev(:) ) + * ( eqns_data%mLayerMatricHeadTrial(:) - eqns_data%mLayerMatricHeadPrev(:) ) ! save required quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial @@ -518,7 +552,6 @@ subroutine summaSolveSundialsIDA( & eqns_data%scalarCanopyLiqPrev = eqns_data%scalarCanopyLiqTrial eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) - mLayerMatricHeadLiqPrev(:) = eqns_data%mLayerMatricHeadLiqTrial(:) eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) @@ -526,6 +559,27 @@ subroutine summaSolveSundialsIDA( & eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial + ! Restart for where vegetation and layers cross freezing point + if(detect_events)then + if (retvalr .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) + ! rootsfound[i]= +1 indicates that gi is increasing, -1 g[i] decreasing, 0 no root + !retval = FIDAGetRootInfo(ida_mem, rootsfound) + !if (retval < 0) then; err=20; message='solveByIDA: error in FIDAGetRootInfo'; return; endif + !print '(a,f15.7,2x,17(i2,2x))', "time, rootsfound[] = ", tret(1), rootsfound + ! Reininitialize solver for running after discontinuity and restart + retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAReInit'; return; endif + if(dt_last(1) < 0.01_rkind)then ! don't keep calling if step is small + retval = FIDARootInit(ida_mem, 0, c_funloc(layerDisCont4IDA)) + tinystep = .true. + else + retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4IDA)) + tinystep = .false. + endif + if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif + endif + endif + enddo ! while loop on one_step mode until time dt !****************************** End of Main Solver *************************************** @@ -540,7 +594,7 @@ subroutine summaSolveSundialsIDA( & flux_data = eqns_data%flux_data deriv_data = eqns_data%deriv_data ixSaturation = eqns_data%ixSaturation - dt_out = tret(1) + dt_out = tret(1) ! should be dt, probably do not need to keep this endif ! free memory @@ -561,6 +615,8 @@ subroutine summaSolveSundialsIDA( & deallocate( eqns_data%mLayerEnthalpyPrev ) deallocate( eqns_data%fluxVec ) deallocate( eqns_data%resSink ) + deallocate( rootsfound ) + deallocate( rootdir ) call FIDAFree(ida_mem) retval = FSUNNonlinSolFree(sunnonlin_NLS) @@ -667,67 +723,181 @@ subroutine setSolverParams(dt,nonlin_iter,ida_mem,retval) end subroutine setSolverParams -! ********************************************************************************************************* -! private subroutine implctMelt: compute melt of the "snow without a layer" -! ********************************************************************************************************* -subroutine implctMelt(& - ! input/output: integrated snowpack properties - scalarSWE, & ! intent(inout): snow water equivalent (kg m-2) - scalarSnowDepth, & ! intent(inout): snow depth (m) - scalarSfcMeltPond, & ! intent(inout): surface melt pond (kg m-2) - ! input/output: properties of the upper-most soil layer - soilTemp, & ! intent(inout): surface layer temperature (K) - soilDepth, & ! intent(inout): surface layer depth (m) - soilHeatcap, & ! intent(inout): surface layer volumetric heat capacity (J m-3 K-1) - ! output: error control - err,message ) ! intent(out): error control - implicit none - ! input/output: integrated snowpack properties - real(rkind),intent(inout) :: scalarSWE ! snow water equivalent (kg m-2) - real(rkind),intent(inout) :: scalarSnowDepth ! snow depth (m) - real(rkind),intent(inout) :: scalarSfcMeltPond ! surface melt pond (kg m-2) - ! input/output: properties of the upper-most soil layer - real(rkind),intent(inout) :: soilTemp ! surface layer temperature (K) - real(rkind),intent(inout) :: soilDepth ! surface layer depth (m) - real(rkind),intent(inout) :: soilHeatcap ! surface layer volumetric heat capacity (J m-3 K-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! local variables - real(rkind) :: nrgRequired ! energy required to melt all the snow (J m-2) - real(rkind) :: nrgAvailable ! energy available to melt the snow (J m-2) - real(rkind) :: snwDensity ! snow density (kg m-3) - ! initialize error control - err=0; message='implctMelt/' - - if(scalarSWE > 0._rkind)then - ! only melt if temperature of the top soil layer is greater than Tfreeze - if(soilTemp > Tfreeze)then - ! compute the energy required to melt all the snow (J m-2) - nrgRequired = scalarSWE*LH_fus - ! compute the energy available to melt the snow (J m-2) - nrgAvailable = soilHeatcap*(soilTemp - Tfreeze)*soilDepth - ! compute the snow density (not saved) - snwDensity = scalarSWE/scalarSnowDepth - ! compute the amount of melt, and update SWE (kg m-2) - if(nrgAvailable > nrgRequired)then - scalarSfcMeltPond = scalarSWE - scalarSWE = 0._rkind - else - scalarSfcMeltPond = nrgAvailable/LH_fus - scalarSWE = scalarSWE - scalarSfcMeltPond - end if - ! update depth - scalarSnowDepth = scalarSWE/snwDensity - ! update temperature of the top soil layer (K) - soilTemp = soilTemp - (LH_fus*scalarSfcMeltPond/soilDepth)/soilHeatcap - else ! melt is zero if the temperature of the top soil layer is less than Tfreeze - scalarSfcMeltPond = 0._rkind ! kg m-2 - end if ! (if the temperature of the top soil layer is greater than Tfreeze) - else ! melt is zero if the "snow without a layer" does not exist - scalarSfcMeltPond = 0._rkind ! kg m-2 - end if ! (if the "snow without a layer" exists) - -end subroutine implctMelt + +! ---------------------------------------------------------------------------------------- +! find_rootdir: private routine to determine which direction to look for the root, by +! determining if the variable is greater or less than the root. Need to do this to prevent +! bouncing around solution +! ---------------------------------------------------------------------------------------- + subroutine find_rootdir(eqns_data,rootdir) + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use fsundials_nvector_mod + use fnvector_serial_mod + use soil_utils_module,only:crit_soilT ! compute the critical temperature below which ice exists + use globalData,only:integerMissing ! missing integer + use var_lookup,only:iLookINDEX ! named variables for structure elements + use multiconst,only:Tfreeze ! freezing point of pure water (K) + + !======= Declarations ========= + implicit none + + ! calling variables + type(eqnsData),intent(in) :: eqns_data ! equations data + integer(i4b),intent(inout) :: rootdir(:) ! root function directions to search + + ! local variables + integer(i4b) :: i,ind ! indices + integer(i4b) :: nState ! number of states + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers + real(rkind) :: xPsi ! matric head at layer (m) + real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) + + ! get equations data variables + nState = eqns_data%nState + nSnow = eqns_data%nSnow + nSoil = eqns_data%nSoil + + ! initialize + ind = 0 + + ! identify the critical point when vegetation begins to freeze + if(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then + ind = ind+1 + rootdir(ind) = 1 + if(eqns_data%scalarCanopyTempPrev > Tfreeze) rootdir(ind) = -1 + endif + + if(nSnow>0)then + do i = 1,nSnow + ! identify the critical point when the snow layer begins to freeze + if(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing)then + ind = ind+1 + rootdir(ind) = 1 + if(eqns_data%mLayerTempPrev(i) > Tfreeze) rootdir(ind) = -1 + endif + end do + endif + + if(nSoil>0)then + do i = 1,nSoil + xPsi = eqns_data%mLayerMatricHeadPrev(i) + ! identify the critical point when soil matrix potential goes below 0 and Tfreeze depends only on temp + if (eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing)then + ind = ind+1 + rootdir(ind) = 1 + if(xPsi > 0._rkind ) rootdir(ind) = -1 + endif + ! identify the critical point when the soil layer begins to freeze + if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing)then + ind = ind+1 + TcSoil = crit_soilT(xPsi) + rootdir(ind) = 1 + if(eqns_data%mLayerTempPrev(i+nSnow) > TcSoil) rootdir(ind) = -1 + endif + end do + endif + + end subroutine find_rootdir + + +! ---------------------------------------------------------------------------------------- +! layerDisCont4IDA: The root function routine to find soil matrix potential = 0, +! soil temp = critical frozen point, and snow and veg temp = Tfreeze +! ---------------------------------------------------------------------------------------- +! Return values: +! 0 = success, +! 1 = recoverable error, +! -1 = non-recoverable error +! ---------------------------------------------------------------------------------------- + integer(c_int) function layerDisCont4IDA(t, sunvec_u, sunvec_up, gout, user_data) & + result(ierr) bind(C,name='layerDisCont4IDA') + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use fsundials_nvector_mod + use fnvector_serial_mod + use soil_utils_module,only:crit_soilT ! compute the critical temperature below which ice exists + use globalData,only:integerMissing ! missing integer + use var_lookup,only:iLookINDEX ! named variables for structure elements + use multiconst,only:Tfreeze ! freezing point of pure water (K) + + !======= Declarations ========= + implicit none + + ! calling variables + real(c_double), value :: t ! current time + type(N_Vector) :: sunvec_u ! solution N_Vector + type(N_Vector) :: sunvec_up ! derivative N_Vector + real(c_double) :: gout(999) ! root function values, if (nVeg + nSnow + 2*nSoil)>999, problem + type(c_ptr), value :: user_data ! user-defined data + + ! local variables + integer(i4b) :: i,ind ! indices + integer(i4b) :: nState ! number of states + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers + real(rkind) :: xPsi ! matric head at layer (m) + real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) + + ! pointers to data in SUNDIALS vectors + real(c_double), pointer :: uu(:) + type(eqnsData), pointer :: eqns_data ! equations data + + !======= Internals ============ + ! get equations data from user-defined data + call c_f_pointer(user_data, eqns_data) + nState = eqns_data%nState + nSnow = eqns_data%nSnow + nSoil = eqns_data%nSoil + + ! get data array from SUNDIALS vector + uu(1:nState) => FN_VGetArrayPointer(sunvec_u) + + ! initialize + ind = 0 + + ! identify the critical point when vegetation begins to freeze + if(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then + ind = ind+1 + gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)) - Tfreeze + endif + + if(nSnow>0)then + do i = 1,nSnow + ! identify the critical point when the snow layer begins to freeze + if(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing)then + ind = ind+1 + gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)) - Tfreeze + endif + end do + endif + + if(nSoil>0)then + do i = 1,nSoil + ! identify the critical point when soil matrix potential goes below 0 and Tfreeze depends only on temp + if (eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing)then + ind = ind+1 + xPsi = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)) + gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)) + else + xPsi = eqns_data%prog_data%var(iLookPROG%mLayerMatricHead)%dat(i) + endif + ! identify the critical point when the soil layer begins to freeze + if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing)then + ind = ind+1 + TcSoil = crit_soilT(xPsi) + gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) - TcSoil + endif + end do + endif + + ! return success + ierr = 0 + return + + end function layerDisCont4IDA end module summaSolveSundialsIDA_module diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index e939ac9b8..295eac229 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -117,6 +117,7 @@ module systemSolv_module ! ********************************************************************************************************** subroutine systemSolv(& ! input: model control + dt_cur, & ! intent(in): current stepsize dt, & ! intent(in): time step (s) nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to denote first sub-step @@ -160,7 +161,8 @@ subroutine systemSolv(& ! * dummy variables ! --------------------------------------------------------------------------------------- ! input: model control - real(rkind),intent(in) :: dt ! time step (seconds) + real(rkind),intent(in) :: dt_cur ! current stepsize + real(rkind),intent(in) :: dt ! entire time step for drainage pond rate integer(i4b),intent(in) :: nState ! total number of state variables logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call @@ -305,7 +307,7 @@ subroutine systemSolv(& ! --------------- ! check - if(dt < tinyStep)then + if(dt_cur < tinyStep)then message=trim(message)//'dt is tiny' err=20; return endif @@ -436,7 +438,8 @@ subroutine systemSolv(& ! NOTE 2: The Jacobian matrix together with the residual vector is used to calculate the first iteration increment call eval8summa(& ! input: model control - dt, & ! intent(in): length of the time step (seconds) + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): number of layers @@ -488,7 +491,7 @@ subroutine systemSolv(& bulkDensity = mLayerVolFracIce(1)*iden_ice + mLayerVolFracLiq(1)*iden_water volEnthalpy = temp2ethpy(mLayerTemp(1),bulkDensity,snowfrz_scale) ! set flag and error codes for too much melt - if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt)then + if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_cur)then tooMuchMelt=.true. message=trim(message)//'net flux in the top snow layer can melt all the snow in the top layer' err=-20; return ! negative error code to denote a warning @@ -510,10 +513,6 @@ subroutine systemSolv(& ! iterate do iter=1,localMaxIter - ! print iteration count - !print*, '*** iter, maxiter, dt = ', iter, localMaxiter, dt - !print*, trim(message)//'before summaSolve' - ! keep track of the number of iterations niter = iter+1 ! +1 because xFluxResid was moved outside the iteration loop (for backwards compatibility) @@ -523,7 +522,8 @@ subroutine systemSolv(& ! 3) Computes new fluxes and derivatives, new residuals, and (if necessary) refines the state vector call summaSolve(& ! input: model control - dt, & ! intent(in): length of the time step (seconds) + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate iter, & ! intent(in): iteration index nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -582,12 +582,6 @@ subroutine systemSolv(& rVec = resVecNew stateVecTrial = stateVecNew - ! print progress - !write(*,'(a,10(f16.14,1x))') 'rVec = ', rVec ( min(nState,iJac1) : min(nState,iJac2) ) - !write(*,'(a,10(f16.10,1x))') 'fluxVecNew = ', fluxVecNew ( min(nState,iJac1) : min(nState,iJac2) )*dt - !write(*,'(a,10(f16.10,1x))') 'stateVecTrial = ', stateVecTrial ( min(nState,iJac1) : min(nState,iJac2) ) - !print*, 'PAUSE: check states and fluxes'; read(*,*) - ! exit iteration loop if converged if(converged) exit @@ -612,7 +606,7 @@ subroutine systemSolv(& if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) iState = ixSnowSoilNrg(iLayer) - stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt + resSinkNew(iState))/real(sMul(iState), rkind) + stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState))/real(sMul(iState), rkind) end do ! looping through non-missing energy state variables in the snow+soil domain endif @@ -621,7 +615,7 @@ subroutine systemSolv(& if(nSnowSoilHyd>0)then do concurrent (iLayer=1:nSnow,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing water state variables in the snow domain) iState = ixSnowSoilHyd(iLayer) - stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt + resSinkNew(iState)) + stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState)) end do ! looping through non-missing water state variables in the soil domain endif diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 8f8eabd4d..c11965597 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -99,6 +99,7 @@ module systemSolvSundials_module ! ********************************************************************************************************** subroutine systemSolvSundials(& ! input: model control + dt_cur, & ! intent(in): current stepsize dt, & ! intent(in): time step (s) nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to denote first sub-step @@ -126,7 +127,6 @@ subroutine systemSolvSundials(& stateVecPrime, & ! intent(out): updated state vector reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out): flag to denote that there was too much melt - dt_out, & ! intent(out) err,message) ! intent(out): error code and error message ! --------------------------------------------------------------------------------------- ! structure allocations @@ -144,6 +144,7 @@ subroutine systemSolvSundials(& ! * dummy variables ! --------------------------------------------------------------------------------------- ! input: model control + real(rkind),intent(in) :: dt_cur ! current stepsize real(rkind),intent(in) :: dt ! time step (seconds) integer(i4b),intent(in) :: nState ! total number of state variables logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step @@ -171,13 +172,13 @@ subroutine systemSolvSundials(& real(rkind),intent(out) :: stateVecPrime(:) ! trial state vector (mixed units) logical(lgt),intent(out) :: reduceCoupledStep ! flag to reduce the length of the coupled step logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that there was too much melt - real(qp),intent(out) :: dt_out ! time step integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! --------------------------------------------------------------------------------------- ! * general local variables ! --------------------------------------------------------------------------------------- + real(qp) :: dt_out ! time step sum for data window at termination of sundials character(LEN=256) :: cmessage ! error message of downwind routine integer(i4b) :: iVar ! index of variable integer(i4b) :: local_ixGroundwater ! local index for groundwater representation @@ -208,7 +209,7 @@ subroutine systemSolvSundials(& integer(i4b) :: iLayer ! index of model layer in the snow+soil domain real(rkind) :: xMin,xMax ! minimum and maximum values for water content real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) - type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a data step + type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt_out (=dt) real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step real(rkind) :: fOld ! function values (-); NOTE: dimensionless because scaled @@ -286,7 +287,7 @@ subroutine systemSolvSundials(& ! --------------- ! check - if(dt < tinyStep)then + if(dt_cur < tinyStep)then message=trim(message)//'dt is tiny' err=20; return endif @@ -307,6 +308,9 @@ subroutine systemSolvSundials(& call allocLocal(flux_meta(:),flux_init,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + ! allocate space for mLayerCmpress_sum at the start of the time step + allocate( mLayerCmpress_sum(nSoil) ) + ! allocate space for the baseflow derivatives ! NOTE: needs allocation because only used when baseflow sinks are active if(ixGroundwater==qbaseTopmodel)then @@ -401,7 +405,7 @@ subroutine systemSolvSundials(& ! NOTE: The values calculated in eval8summaSundials are used to calculate the initial flux call eval8summaSundials(& ! input: model control - dt, & ! intent(in): current stepsize + dt_cur, & ! intent(in): current stepsize dt, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -476,16 +480,13 @@ subroutine systemSolvSundials(& call allocLocal(flux_meta(:),flux_sum,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - ! allocate space for mLayerCmpress_sum - allocate( mLayerCmpress_sum(nSoil) ) - ! check the need to merge snow layers if(nSnow>0)then ! compute the energy required to melt the top snow layer (J m-2) bulkDensity = mLayerVolFracIce(1)*iden_ice + mLayerVolFracLiq(1)*iden_water volEnthalpy = temp2ethpy(mLayerTemp(1),bulkDensity,snowfrz_scale) ! set flag and error codes for too much melt - if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt)then + if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_cur)then tooMuchMelt=.true. message=trim(message)//'net flux in the top snow layer can melt all the snow in the top layer' err=-20; return ! negative error code to denote a warning @@ -509,17 +510,17 @@ subroutine systemSolvSundials(& !------------------- ! * solving F(y,y') = 0 by IDA. Here, y is the state vector ! ------------------ - ! initialize flux_sum + + ! initialize flux_sum do concurrent ( iVar=1:size(flux_meta) ) flux_sum%var(iVar)%dat(:) = 0._rkind end do - ! initialize sum of compression of the soil matrix mLayerCmpress_sum(:) = 0._rkind call summaSolveSundialsIDA(& - dt, & ! intent(in): data time step - atol, & ! intent(in): absolute telerance + dt_cur, & ! intent(in): data time step + atol, & ! intent(in): absolute tolerance rtol, & ! intent(in): relative tolerance nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -544,16 +545,15 @@ subroutine systemSolvSundials(& indx_data, & ! intent(in): index data ! input-output: data structures diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) flux_temp, & ! intent(inout): model fluxes for a local HRU flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + mLayerCmpress_sum, & ! intent(inout): sum of compression of the soil matrix ! output ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt - mLayerCmpress_sum, & ! intent(out): sum of compression of the soil matrix - dt_out, & ! intent(out): time step + dt_out, & ! intent(out): time step sum for entire data window at termination of sundials stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step err,cmessage) ! intent(out): error control @@ -574,14 +574,13 @@ subroutine systemSolvSundials(& end do ! compute the total change in storage associated with compression of the soil matrix (kg m-2) - diag_data%var(iLookDIAG%mLayerCompress)%dat(:) = mLayerCmpress_sum(:) + diag_data%var(iLookDIAG%mLayerCompress)%dat(:) = mLayerCmpress_sum(:) / dt_out diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sum(diag_data%var(iLookDIAG%mLayerCompress)%dat(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water ! save the computed solution stateVecTrial = stateVecNew ! free memory - deallocate(mLayerCmpress_sum) deallocate(dBaseflow_dMatric) ! end associate statements diff --git a/build/source/engine/tol4IDA.f90 b/build/source/engine/tol4IDA.f90 index 7709e7a3e..c33481089 100644 --- a/build/source/engine/tol4IDA.f90 +++ b/build/source/engine/tol4IDA.f90 @@ -170,7 +170,6 @@ subroutine popTol4IDA(& real(rkind) :: absTolAquifr = 1e-6 ! could use absConvTol_aquifr 1e-6 = 1e-5 bEuler real(rkind) :: relTolAquifr = 1e-6 ! could use relConvTol_aquifr 1e-6 = 1e-0 bEuler - ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures diff --git a/build/source/engine/type4IDA.f90 b/build/source/engine/type4IDA.f90 index 7b0af42f6..334b808eb 100644 --- a/build/source/engine/type4IDA.f90 +++ b/build/source/engine/type4IDA.f90 @@ -15,65 +15,65 @@ module type4IDA implicit none type eqnsData - type(c_ptr) :: ida_mem ! IDA memory - real(rkind) :: dt ! time step - integer(i4b) :: nSnow ! number of snow layers - integer(i4b) :: nSoil ! number of soil layers - integer(i4b) :: nLayers ! total number of layers - integer :: nState ! total number of state variables - integer(i4b) :: ixMatrix ! form of matrix (dense or banded) - logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt) :: firstFluxCall - logical(lgt) :: firstSplitOper - logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution - type(zLookup) :: lookup_data ! lookup tables - type(var_i) :: type_data ! type of vegetation and soil - type(var_d) :: attr_data ! spatial attributes - type(var_dlength) :: mpar_data ! model parameters - type(var_d) :: forc_data ! model forcing data - type(var_dlength) :: bvar_data ! model variables for the local basin - type(var_dlength) :: prog_data ! prognostic variables for a local HRU - type(var_ilength) :: indx_data ! indices defining model states and layers - type(var_dlength) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength) :: flux_data ! model fluxes for a local HRU - type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(rkind) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind) :: scalarCanopyTempPrev ! previous value of canopy temperature (K) - real(rkind) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - real(rkind) :: scalarCanopyIcePrev ! value of canopy ice content (kg m-2) at previous step - real(rkind) :: scalarCanopyLiqTrial ! trial value of canopy ice content (kg m-2) - real(rkind) :: scalarCanopyLiqPrev ! value of canopy ice content (kg m-2) at previous step - real(rkind) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) - real(rkind) :: scalarCanopyEnthalpyPrev ! previous enthalpy of the vegetation canopy (J m-3) - real(qp), allocatable :: sMul(:) ! state vector multiplier (used in the residual calculations) - real(rkind), allocatable :: dMat(:) ! diagonal of the Jacobian matrix - real(rkind), allocatable :: fluxVec(:) ! flux vector - real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation - real(rkind), allocatable :: atol(:) ! vector of absolute tolerances - real(rkind), allocatable :: rtol(:) ! vector of relative tolerances - real(rkind), allocatable :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) + type(c_ptr) :: ida_mem ! IDA memory + real(rkind) :: dt ! data step + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers + integer(i4b) :: nLayers ! total number of layers + integer(i4b) :: nState ! total number of state variables + integer(i4b) :: ixMatrix ! form of matrix (dense or banded) + logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt) :: firstFluxCall + logical(lgt) :: firstSplitOper + logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution + type(zLookup) :: lookup_data ! lookup tables + type(var_i) :: type_data ! type of vegetation and soil + type(var_d) :: attr_data ! spatial attributes + type(var_dlength) :: mpar_data ! model parameters + type(var_d) :: forc_data ! model forcing data + type(var_dlength) :: bvar_data ! model variables for the local basin + type(var_dlength) :: prog_data ! prognostic variables for a local HRU + type(var_ilength) :: indx_data ! indices defining model states and layers + type(var_dlength) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength) :: flux_data ! model fluxes for a local HRU + type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + real(rkind) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind) :: scalarCanopyTempPrev ! previous value of canopy temperature (K) + real(rkind) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + real(rkind) :: scalarCanopyIcePrev ! value of canopy ice content (kg m-2) at previous step + real(rkind) :: scalarCanopyLiqTrial ! trial value of canopy ice content (kg m-2) + real(rkind) :: scalarCanopyLiqPrev ! value of canopy ice content (kg m-2) at previous step + real(rkind) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) + real(rkind) :: scalarCanopyEnthalpyPrev ! previous enthalpy of the vegetation canopy (J m-3) + real(qp), allocatable :: sMul(:) ! state vector multiplier (used in the residual calculations) + real(rkind), allocatable :: dMat(:) ! diagonal of the Jacobian matrix + real(rkind), allocatable :: fluxVec(:) ! flux vector + real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation + real(rkind), allocatable :: atol(:) ! vector of absolute tolerances + real(rkind), allocatable :: rtol(:) ! vector of relative tolerances + real(rkind), allocatable :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) real(rkind), allocatable :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind), allocatable :: mLayerMatricHeadPrev(:) ! vector of total water matric potential (m) at previous step + real(rkind), allocatable :: mLayerMatricHeadPrev(:) ! vector of total water matric potential (m) at previous step real(rkind), allocatable :: mLayerVolFracWatTrial(:) ! trial value for volumetric fraction of total water (-) - real(rkind), allocatable :: mLayerVolFracWatPrev(:) ! value for volumetric fraction of total water (-) at previous step + real(rkind), allocatable :: mLayerVolFracWatPrev(:) ! value for volumetric fraction of total water (-) at previous step real(rkind), allocatable :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) - real(rkind), allocatable :: mLayerVolFracIcePrev(:) ! value for volumetric fraction of ice (-) at previous step + real(rkind), allocatable :: mLayerVolFracIcePrev(:) ! value for volumetric fraction of ice (-) at previous step real(rkind), allocatable :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) - real(rkind), allocatable :: mLayerVolFracLiqPrev(:) ! value for volumetric fraction of liquid water (-) at previous step - real(rkind) :: scalarAquiferStoragePrev - real(rkind) :: scalarAquiferStorageTrial - real(rkind), allocatable :: mLayerEnthalpyTrial(:) ! trial enthalpy of snow and soil (J m-3) - real(rkind), allocatable :: mLayerEnthalpyPrev(:) ! enthalpy of snow and soil (J m-3) at previous step - real(rkind), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind), allocatable :: mLayerTempPrev(:) ! vector of layer temperature (K) at previous step - real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - integer :: ixSaturation - integer(i4b) :: err ! error code - character(len = 50) :: message ! error message + real(rkind), allocatable :: mLayerVolFracLiqPrev(:) ! value for volumetric fraction of liquid water (-) at previous step + real(rkind) :: scalarAquiferStoragePrev + real(rkind) :: scalarAquiferStorageTrial + real(rkind), allocatable :: mLayerEnthalpyTrial(:) ! trial enthalpy of snow and soil (J m-3) + real(rkind), allocatable :: mLayerEnthalpyPrev(:) ! enthalpy of snow and soil (J m-3) at previous step + real(rkind), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind), allocatable :: mLayerTempPrev(:) ! vector of layer temperature (K) at previous step + real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + integer(i4b) :: ixSaturation + integer(i4b) :: err ! error code + character(len = 50) :: message ! error message end type eqnsData - - + + end module type4IDA diff --git a/build/source/engine/updatState.f90 b/build/source/engine/updatState.f90 index 29c82f15b..7ae216c17 100644 --- a/build/source/engine/updatState.f90 +++ b/build/source/engine/updatState.f90 @@ -116,18 +116,7 @@ subroutine updateSoil(& ! compute fractional **volume** of total water (liquid plus ice) mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if(mLayerVolFracWat > (theta_sat + tinyVal)) then - err=20 - message=trim(message)//'volume of liquid and ice (mLayerVolFracWat) exceeds porosity' - print*, 'mLayerVolFracWat = ', mLayerVolFracWat - print*, 'theta_sat (porosity) = ', theta_sat - print*, 'mLayerMatricHead = ', mLayerMatricHead - print*, 'theta_res = ', theta_res - print*, 'vGn_alpha = ', vGn_alpha - print*, 'vGn_n = ', vGn_n - print*, 'vGn_m = ', vGn_m - return - end if + if(mLayerVolFracWat > (theta_sat + tinyVal))then; err=20; message=trim(message)//'volume of liquid and ice exceeds porosity'; return; end if ! compute the critical soil temperature where all water is unfrozen (K) ! (eq 17 in Dall'Amico 2011) @@ -135,7 +124,6 @@ subroutine updateSoil(& ! *** compute volumetric fraction of liquid water and ice for partially frozen soil if(mLayerTemp < TcSoil)then ! (check if soil temperature is less than the critical temperature) - ! - volumetric liquid water content (-) ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations @@ -143,18 +131,13 @@ subroutine updateSoil(& mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - ! - volumetric ice content (-) - mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq - ! *** compute volumetric fraction of liquid water and ice for unfrozen soil - else - - ! all water is unfrozen - mLayerPsiLiq = mLayerMatricHead + else !( mLayerTemp >= TcSoil, all water is unfrozen, mLayerPsiLiq = mLayerMatricHead ) mLayerVolFracLiq = mLayerVolFracWat - mLayerVolFracIce = 0._rkind end if ! (check if soil is partially frozen) + ! - volumetric ice content (-) + mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq end subroutine updateSoil diff --git a/build/source/engine/updatStateSundials.f90 b/build/source/engine/updatStateSundials.f90 index 4f3738c78..843a805a1 100644 --- a/build/source/engine/updatStateSundials.f90 +++ b/build/source/engine/updatStateSundials.f90 @@ -71,12 +71,8 @@ end subroutine updateSnowSundials ! *********************************************************************************************************************************** subroutine updateSoilSundials(& ! input - dt ,& ! intent(in): time step - insideIDA ,& ! intent(in): flag if inside Sundials solver mLayerTemp ,& ! intent(in): temperature (K) mLayerMatricHead ,& ! intent(in): total water matric potential (m) - mLayerMatricHeadPrev ,& ! intent(in): total water matric potential previous time step (m) - mLayerVolFracWatPrev ,& ! intent(in): volumetric fraction of total water previous time step (-) mLayerTempPrime ,& ! intent(in): temperature time derivative (K/s) mLayerMatricHeadPrime, & ! intent(in): total water matric potential time derivative (m/s) vGn_alpha ,& ! intent(in): van Genutchen "alpha" parameter @@ -98,12 +94,8 @@ subroutine updateSoilSundials(& USE soil_utils_module,only:dTheta_dPsi implicit none ! input variables - real(rkind),intent(in) :: dt - logical(lgt),intent(in) :: insideIDA ! flag if inside Sundials solver real(rkind),intent(in) :: mLayerTemp ! estimate of temperature (K) real(rkind),intent(in) :: mLayerMatricHead ! matric head (m) - real(rkind),intent(in) :: mLayerMatricHeadPrev ! matric head previous time step (m) - real(rkind),intent(in) :: mLayerVolFracWatPrev ! volumetric fraction of total waterprevious time step (m) real(rkind),intent(in) :: mLayerTempPrime ! temperature time derivative (K/s) real(rkind),intent(in) :: mLayerMatricHeadPrime ! matric head time derivative (m/s) real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter @@ -124,24 +116,15 @@ subroutine updateSoilSundials(& real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) - real(rkind) :: dt_inv ! inverse of timestep + real(rkind),parameter :: tinyVal=epsilon(1._rkind) ! used in balance check ! initialize error control err=0; message="updateSoilSundials/" ! compute fractional **volume** of total water (liquid plus ice) mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if (.not.insideIDA)then ! calculate dt current, or use dt current as it can change here - if( abs(mLayerMatricHead - mLayerMatricHeadPrev) < verySmall )then !this difference is set as 0 inside varSubstep - dt_inv = 1._rkind/dt - else - dt_inv = mLayerMatricHeadPrime / (mLayerMatricHead - mLayerMatricHeadPrev) ! - endif - mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev)*dt_inv - else ! inside Sundials: instantaneous derivative will always be a full step size as input here - mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime - endif - - if(mLayerVolFracWat > theta_sat)then; err=20; message=trim(message)//'volume of liquid and ice exceeds porosity'; return; end if + mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime + + if(mLayerVolFracWat > (theta_sat + tinyVal))then; err=20; message=trim(message)//'volume of liquid and ice exceeds porosity'; return; end if ! compute the critical soil temperature where all water is unfrozen (K) ! (eq 17 in Dall'Amico 2011) @@ -154,17 +137,12 @@ subroutine updateSoilSundials(& xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if(mLayerPsiLiq<0._rkind)then - mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime - else - mLayerVolFracLiqPrime = 0._rkind - endif + mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime ! *** compute volumetric fraction of liquid water for unfrozen soil - else !( mLayerTemp >= TcSoil, all water is unfrozen ) + else !( mLayerTemp >= TcSoil, all water is unfrozen, mLayerPsiLiq = mLayerMatricHead ) mLayerVolFracLiq = mLayerVolFracWat mLayerVolFracLiqPrime = mLayerVolFracWatPrime - mLayerVolFracIcePrime = 0._rkind end if ! (check if soil is partially frozen) diff --git a/build/source/engine/updateVarsSundials.f90 b/build/source/engine/updateVarsSundials.f90 index 35b026873..94be63347 100644 --- a/build/source/engine/updateVarsSundials.f90 +++ b/build/source/engine/updateVarsSundials.f90 @@ -103,14 +103,11 @@ module updateVarsSundials_module ! ********************************************************************************************************** subroutine updateVarsSundials(& ! input - dt, & ! intent(in): time step computJac, & ! intent(in): logical flag if computing Jacobian for Sundials solve do_adjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU - mLayerVolFracWatPrev, & ! intent(in): previous vector of total water matric potential (m) - mLayerMatricHeadPrev, & ! intent(in): previous vector of volumetric total water content (-) diag_data, & ! intent(inout): model diagnostic variables for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy @@ -141,14 +138,11 @@ subroutine updateVarsSundials(& ! -------------------------------------------------------------------------------------------------------------------------------- implicit none ! input - real(rkind) ,intent(in) :: dt ! time step logical(lgt) ,intent(in) :: computJac ! flag if computing Jacobian for Sundials solver logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - real(rkind),intent(in) :: mLayerVolFracWatPrev(:) ! previous vector of total water matric potential (m) - real(rkind),intent(in) :: mLayerMatricHeadPrev(:) ! previous vector of volumetric total water content (-) type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy @@ -378,7 +372,6 @@ subroutine updateVarsSundials(& select case( ixStateType(ixFullVector) ) ! --> update the total water from the liquid water matric potential case(iname_lmpLayer) - effSat = volFracLiq(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex)) ! effective saturation avPore = theta_sat(ixControlIndex) - mLayerVolFracIceTrial(iLayer) - theta_res(ixControlIndex) ! available pore space mLayerVolFracLiqTrial(iLayer) = effSat*avPore + theta_res(ixControlIndex) @@ -388,12 +381,10 @@ subroutine updateVarsSundials(& mLayerMatricHeadPrime(ixControlIndex) = dPsi_dTheta(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) * mLayerVolFracWatPrime(iLayer) ! --> update the total water from the total water matric potential case(iname_matLayer) - mLayerVolFracWatTrial(iLayer) = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) mLayerVolFracWatPrime(iLayer) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) *mLayerMatricHeadPrime(ixControlIndex) ! --> update the total water matric potential (assume already have mLayerVolFracWatTrial given block above) case(iname_liqLayer, iname_watLayer) - mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) mLayerMatricHeadPrime(ixControlIndex) = dPsi_dTheta(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) * mLayerVolFracWatPrime(iLayer) case default; err=20; message=trim(message)//'expect iname_lmpLayer, iname_matLayer, iname_liqLayer, or iname_watLayer'; return @@ -439,7 +430,6 @@ subroutine updateVarsSundials(& ! - compute derivatives... ! ------------------------ - ! compute the derivative in bulk heat capacity w.r.t. total water content or water matric potential (m-1) ! compute the derivative in total water content w.r.t. total water matric potential (m-1) ! NOTE 1: valid for frozen and unfrozen conditions ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi @@ -565,12 +555,8 @@ subroutine updateVarsSundials(& ! compute volumetric fraction of liquid water and ice call updateSoilSundials(& - dt, & ! intent(in) : time step - computJac, & ! intent(in) : logical flag if inside Sundials solver xTemp, & ! intent(in) : temperature (K) mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) - mLayerMatricHeadPrev(ixControlIndex), & ! intent(in) : previous values, will be same as current if computJac - mLayerVolFracWatPrev(iLayer), & ! intent(in) : previous values, will be same as current if computJac mLayerTempPrime(iLayer), & ! intent(in) : temperature time derivative (K/s) mLayerMatricHeadPrime(ixControlIndex), & ! intent(in) : total water matric potential time derivative (m/s) vGn_alpha(ixControlIndex), & ! intent(in) : van Genutchen "alpha" parameter @@ -610,7 +596,7 @@ subroutine updateVarsSundials(& ! ------------------------ ! check the need to adjust temperature (will always be false if inside solver) - ! can be true if inside varSubstepSundials, outside solver, but currently will not work so turn off always and not sure should ever do this + ! can be true if inside varSubstep, outside solver, but currently will not work so turn off always and not sure should ever do this if(do_adjustTemp .and. computJac)then ! get the melt energy diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index fa3864a4a..7cc7b5bb0 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -56,6 +56,8 @@ module varSubstep_module USE var_lookup,only:iLookDIAG ! named variables for structure elements USE var_lookup,only:iLookPARAM ! named variables for structure elements USE var_lookup,only:iLookINDEX ! named variables for structure elements +USE var_lookup,only:iLookDERIV ! named variables for structure elements +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure ! look up structure for variable types USE var_lookup,only:iLookVarType @@ -68,6 +70,11 @@ module varSubstep_module iden_ice, & ! intrinsic density of ice (kg m-3) iden_water ! intrinsic density of liquid water (kg m-3) +! look-up values for the numerical method +USE mDecisions_module,only: & + sundials, & ! SUNDIALS/IDA solution + bEuler ! home-grown backward Euler solution with long time step + ! safety: set private unless specified otherwise implicit none private @@ -79,637 +86,809 @@ module varSubstep_module contains - ! ********************************************************************************************************** - ! public subroutine varSubstep: run the model for a collection of substeps for a given state subset - ! ********************************************************************************************************** - subroutine varSubstep(& - ! input: model control - dt, & ! intent(in) : time step (s) - dtInit, & ! intent(in) : initial time step (seconds) - dt_min, & ! intent(in) : minimum time step (seconds) - nState, & ! intent(in) : total number of state variables - doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature - firstSubStep, & ! intent(in) : flag to denote first sub-step - firstFluxCall, & ! intent(inout) : flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in) : flag to denote if computing energy flux over vegetation - scalarSolution, & ! intent(in) : flag to denote implementing the scalar solution - iStateSplit, & ! intent(in) : index of the state in the splitting operation - fluxMask, & ! intent(in) : mask for the fluxes used in this given state subset - fluxCount, & ! intent(inout) : number of times that fluxes are updated (should equal nSubsteps) - ! input/output: data structures - model_decisions, & ! intent(in) : model decisions - lookup_data, & ! intent(in) : lookup tables - type_data, & ! intent(in) : type of vegetation and soil - attr_data, & ! intent(in) : spatial attributes - forc_data, & ! intent(in) : model forcing data - mpar_data, & ! intent(in) : model parameters - indx_data, & ! intent(inout) : index data - prog_data, & ! intent(inout) : model prognostic variables for a local HRU - diag_data, & ! intent(inout) : model diagnostic variables for a local HRU - flux_data, & ! intent(inout) : model fluxes for a local HRU - deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables - bvar_data, & ! intent(in) : model variables for the local basin - ! output: model control - ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) - dtMultiplier, & ! intent(out) : substep multiplier (-) - nSubsteps, & ! intent(out) : number of substeps taken for a given split - failedMinimumStep, & ! intent(out) : flag to denote success of substepping for a given split - reduceCoupledStep, & ! intent(out) : flag to denote need to reduce the length of the coupled step - tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt - err,message) ! intent(out) : error code and error message - ! --------------------------------------------------------------------------------------- - ! structure allocations - USE allocspace_module,only:allocLocal ! allocate local data structures - ! simulation of fluxes and residuals given a trial state vector - USE systemSolv_module,only:systemSolv ! solve the system of equations for one time step - USE getVectorz_module,only:popStateVec ! populate the state vector - USE getVectorz_module,only:varExtract ! extract variables from the state vector - USE updateVars_module,only:updateVars ! update prognostic variables - ! identify name of variable type (for error message) - USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages - implicit none - ! --------------------------------------------------------------------------------------- - ! * dummy variables - ! --------------------------------------------------------------------------------------- - ! input: model control - real(rkind),intent(in) :: dt ! time step (seconds) - real(rkind),intent(in) :: dtInit ! initial time step (seconds) - real(rkind),intent(in) :: dt_min ! minimum time step (seconds) - integer(i4b),intent(in) :: nState ! total number of state variables - logical(lgt),intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) - logical(lgt),intent(in) :: scalarSolution ! flag to denote implementing the scalar solution - integer(i4b),intent(in) :: iStateSplit ! index of the state in the splitting operation - type(var_flagVec),intent(in) :: fluxMask ! flags to denote if the flux is calculated in the given state subset - type(var_ilength),intent(inout) :: fluxCount ! number of times that the flux is updated (should equal nSubsteps) - ! input/output: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(zLookup),intent(in) :: lookup_data ! lookup tables - type(var_i),intent(in) :: type_data ! type of vegetation and soil - type(var_d),intent(in) :: attr_data ! spatial attributes - type(var_d),intent(in) :: forc_data ! model forcing data - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU - type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin - ! output: model control - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: dtMultiplier ! substep multiplier (-) - integer(i4b),intent(out) :: nSubsteps ! number of substeps taken for a given split - logical(lgt),intent(out) :: failedMinimumStep ! flag to denote success of substepping for a given split - logical(lgt),intent(out) :: reduceCoupledStep ! flag to denote need to reduce the length of the coupled step - logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! --------------------------------------------------------------------------------------- - ! * general local variables - ! --------------------------------------------------------------------------------------- - ! error control - character(LEN=256) :: cmessage ! error message of downwind routine - ! general local variables - integer(i4b) :: iVar ! index of variables in data structures - integer(i4b) :: iSoil ! index of soil layers - integer(i4b) :: ixLayer ! index in a given domain - integer(i4b), dimension(1) :: ixMin,ixMax ! bounds of a given flux vector - ! time stepping - real(rkind) :: dtSum ! sum of time from successful steps (seconds) - real(rkind) :: dt_wght ! weight given to a given flux calculation - real(rkind) :: dtSubstep ! length of a substep (s) - ! adaptive sub-stepping for the explicit solution - logical(lgt) :: failedSubstep ! flag to denote success of substepping for a given split - real(rkind),parameter :: safety=0.85_rkind ! safety factor in adaptive sub-stepping - real(rkind),parameter :: reduceMin=0.1_rkind ! mimimum factor that time step is reduced - real(rkind),parameter :: increaseMax=4.0_rkind ! maximum factor that time step is increased - ! adaptive sub-stepping for the implicit solution - integer(i4b) :: niter ! number of iterations taken - integer(i4b),parameter :: n_inc=5 ! minimum number of iterations to increase time step - integer(i4b),parameter :: n_dec=15 ! maximum number of iterations to decrease time step - real(rkind),parameter :: F_inc = 1.25_rkind ! factor used to increase time step - real(rkind),parameter :: F_dec = 0.90_rkind ! factor used to decrease time step - ! state and flux vectors - real(rkind) :: untappedMelt(nState) ! un-tapped melt energy (J m-3 s-1) - real(rkind) :: stateVecInit(nState) ! initial state vector (mixed units) - real(rkind) :: stateVecTrial(nState) ! trial state vector (mixed units) - type(var_dlength) :: flux_temp ! temporary model fluxes - ! flags - logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt) :: checkMassBalance ! flag to check the mass balance - logical(lgt) :: waterBalanceError ! flag to denote that there is a water balance error - logical(lgt) :: nrgFluxModified ! flag to denote that the energy fluxes were modified - ! energy fluxes - real(rkind) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) - real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) - real(rkind) :: sumSoilCompress - real(rkind),allocatable :: sumLayerCompress(:) - ! --------------------------------------------------------------------------------------- - ! point to variables in the data structures - ! --------------------------------------------------------------------------------------- - globalVars: associate(& - ! number of layers - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! mapping between state vectors and control volumes - ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat ,& ! intent(in): [i4b(:)] list of indices for all active layers (inactive=integerMissing) - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) - ! model state variables (vegetation canopy) - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout): [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout): [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout): [dp] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat & ! intent(inout): [dp(:)] matric potential of liquid water (m) - ) ! end association with variables in the data structures - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - ! Procedure starts here - - ! initialize error control - err=0; message='varSubstep/' - - ! initialize flag for the success of the substepping - failedMinimumStep=.false. - - ! initialize the length of the substep - dtSubstep = dtInit - - ! allocate space for the temporary model flux structure - call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! initialize the model fluxes (some model fluxes are not computed in the iterations) - do iVar=1,size(flux_data%var) - flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) - end do - - ! initialize the total energy fluxes (modified in updateProg) - sumCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap = 0._rkind ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy = 0._rkind ! sensible heat flux from the canopy to the canopy air space (W m-2) - sumSoilCompress = 0._rkind ! total soil compression - allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._rkind ! soil compression by layer - - ! define the first flux call in a splitting operation - firstSplitOper = (.not.scalarSolution .or. iStateSplit==1) - - ! initialize subStep - dtSum = 0._rkind ! keep track of the portion of the time step that is completed - nSubsteps = 0 - - ! loop through substeps - ! NOTE: continuous do statement with exit clause - substeps: do - - ! initialize error control - err=0; message='varSubstep/' - - !write(*,'(a,1x,3(f13.2,1x))') '***** new subStep: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt - !print*, 'scalarCanopyIce = ', prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) - !print*, 'scalarCanopyTemp = ', prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) - - ! ----- - ! * populate state vectors... - ! --------------------------- - - ! initialize state vectors - call popStateVec(& - ! input - nState, & ! intent(in): number of desired state variables - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output - stateVecInit, & ! intent(out): initial model state vector (mixed units) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - ! ----- - ! * iterative solution... - ! ----------------------- - - ! solve the system of equations for a given state subset - call systemSolv(& - ! input: model control - dtSubstep, & ! intent(in): time step (s) - nState, & ! intent(in): total number of state variables - firstSubStep, & ! intent(in): flag to denote first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation - scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution - ! input/output: data structures - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): index data - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_temp, & ! intent(inout): model fluxes for a local HRU - bvar_data, & ! intent(in): model variables for the local basin - model_decisions, & ! intent(in): model decisions - stateVecInit, & ! intent(in): initial state vector - ! output: model control - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - untappedMelt, & ! intent(out): un-tapped melt energy (J m-3 s-1) - stateVecTrial, & ! intent(out): updated state vector - reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step - tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt - niter, & ! intent(out): number of iterations taken - err,cmessage) ! intent(out): error code and error message - if(err/=0)then - message=trim(message)//trim(cmessage) - if(err>0) return - endif - - ! if too much melt or need to reduce length of the coupled step then return - ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry - if(tooMuchMelt .or. reduceCoupledStep) return - - ! identify failure - failedSubstep = (err<0) - - ! check - if(globalPrintFlag)then - print*, 'niter, failedSubstep, dtSubstep = ', niter, failedSubstep, dtSubstep - print*, trim(cmessage) - endif - - ! reduce step based on failure - if(failedSubstep)then - err=0; message='varSubstep/' ! recover from failed convergence - dtMultiplier = 0.5_rkind ! system failure: step halving - else - - ! ** implicit Euler: adjust step length based on iteration count - if(nitern_dec)then - dtMultiplier = F_dec - else - dtMultiplier = 1._rkind - endif - - endif ! switch between failure and success - - ! check if we failed the substep - if(failedSubstep)then - - ! check that the substep is greater than the minimum step - if(dtSubstep*dtMultiplier exit, and either (1) try another solution method; or (2) reduce coupled step - failedMinimumStep=.true. - exit subSteps - - else ! step is still OK - dtSubstep = dtSubstep*dtMultiplier - cycle subSteps - endif ! if step is less than the minimum - - endif ! if failed the substep - - ! ----- - ! * update model fluxes... - ! ------------------------ - - ! NOTE: if we get to here then we are accepting the step - - ! NOTE: we get to here if iterations are successful - if(err/=0)then - message=trim(message)//'expect err=0 if updating fluxes' - return - endif +! ********************************************************************************************************** +! public subroutine varSubstep: run the model for a collection of substeps for a given state subset +! ********************************************************************************************************** +subroutine varSubstep(& + ! input: model control + dt, & ! intent(in) : time step (s) + dtInit, & ! intent(in) : initial time step (seconds) + dt_min, & ! intent(in) : minimum time step (seconds) + whole_step, & ! intent(in) : length of whole step for surface drainage and average flux + nState, & ! intent(in) : total number of state variables + doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature + firstSubStep, & ! intent(in) : flag to denote first sub-step + firstFluxCall, & ! intent(inout) : flag to indicate if we are processing the first flux call + computeVegFlux, & ! intent(in) : flag to denote if computing energy flux over vegetation + scalarSolution, & ! intent(in) : flag to denote implementing the scalar solution + iStateSplit, & ! intent(in) : index of the state in the splitting operation + fluxMask, & ! intent(in) : mask for the fluxes used in this given state subset + fluxCount, & ! intent(inout) : number of times that fluxes are updated (should equal nSubsteps) + ! input/output: data structures + model_decisions, & ! intent(in) : model decisions + lookup_data, & ! intent(in) : lookup tables + type_data, & ! intent(in) : type of vegetation and soil + attr_data, & ! intent(in) : spatial attributes + forc_data, & ! intent(in) : model forcing data + mpar_data, & ! intent(in) : model parameters + indx_data, & ! intent(inout) : index data + prog_data, & ! intent(inout) : model prognostic variables for a local HRU + diag_data, & ! intent(inout) : model diagnostic variables for a local HRU + flux_data, & ! intent(inout) : model fluxes for a local HRU + flux_mean, & ! intent(inout) : mean model fluxes for a local HRU + deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables + bvar_data, & ! intent(in) : model variables for the local basin + ! output: model control + ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) + dtMultiplier, & ! intent(out) : substep multiplier (-) + nSubsteps, & ! intent(out) : number of substeps taken for a given split + failedMinimumStep, & ! intent(out) : flag to denote success of substepping for a given split + reduceCoupledStep, & ! intent(out) : flag to denote need to reduce the length of the coupled step + tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt + err,message) ! intent(out) : error code and error message + ! --------------------------------------------------------------------------------------- + ! structure allocations + USE allocspace_module,only:allocLocal ! allocate local data structures + ! simulation of fluxes and residuals given a trial state vector + USE getVectorz_module,only:popStateVec ! populate the state vector + USE getVectorz_module,only:varExtract ! extract variables from the state vector + USE systemSolvSundials_module,only:systemSolvSundials ! solve the system of equations for one time step + USE systemSolv_module,only:systemSolv ! solve the system of equations for one time step + ! identify name of variable type (for error message) + USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages + implicit none + ! --------------------------------------------------------------------------------------- + ! * dummy variables + ! --------------------------------------------------------------------------------------- + ! input: model control + real(rkind),intent(in) :: dt ! time step (seconds) + real(rkind),intent(in) :: dtInit ! initial time step (seconds) + real(rkind),intent(in) :: dt_min ! minimum time step (seconds) + real(rkind),intent(in) :: whole_step ! length of whole step for surface drainage and average flux + integer(i4b),intent(in) :: nState ! total number of state variables + logical(lgt),intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) + logical(lgt),intent(in) :: scalarSolution ! flag to denote implementing the scalar solution + integer(i4b),intent(in) :: iStateSplit ! index of the state in the splitting operation + type(var_flagVec),intent(in) :: fluxMask ! flags to denote if the flux is calculated in the given state subset + type(var_ilength),intent(inout) :: fluxCount ! number of times that the flux is updated (should equal nSubsteps) + ! input/output: data structures + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_i),intent(in) :: type_data ! type of vegetation and soil + type(var_d),intent(in) :: attr_data ! spatial attributes + type(var_d),intent(in) :: forc_data ! model forcing data + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU + type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: flux_mean ! mean model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin + ! output: model control + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + real(rkind),intent(out) :: dtMultiplier ! substep multiplier (-) + integer(i4b),intent(out) :: nSubsteps ! number of substeps taken for a given split + logical(lgt),intent(out) :: failedMinimumStep ! flag to denote success of substepping for a given split + logical(lgt),intent(out) :: reduceCoupledStep ! flag to denote need to reduce the length of the coupled step + logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! --------------------------------------------------------------------------------------- + ! * general local variables + ! --------------------------------------------------------------------------------------- + ! error control + character(LEN=256) :: cmessage ! error message of downwind routine + ! general local variables + integer(i4b) :: iVar ! index of variables in data structures + integer(i4b) :: iSoil ! index of soil layers + integer(i4b) :: ixLayer ! index in a given domain + integer(i4b), dimension(1) :: ixMin,ixMax ! bounds of a given flux vector + ! time stepping + real(rkind) :: dtSum ! sum of time from successful steps (seconds) + real(rkind) :: dt_wght ! weight given to a given flux calculation + real(rkind) :: dtSubstep ! length of a substep (s) + real(rkind) :: maxstep ! maximum time step length (seconds) + ! adaptive sub-stepping for the explicit solution + logical(lgt) :: failedSubstep ! flag to denote success of substepping for a given split + real(rkind),parameter :: safety=0.85_rkind ! safety factor in adaptive sub-stepping + real(rkind),parameter :: reduceMin=0.1_rkind ! mimimum factor that time step is reduced + real(rkind),parameter :: increaseMax=4.0_rkind ! maximum factor that time step is increased + ! adaptive sub-stepping for the implicit solution + integer(i4b) :: niter ! number of iterations taken + integer(i4b),parameter :: n_inc=5 ! minimum number of iterations to increase time step + integer(i4b),parameter :: n_dec=15 ! maximum number of iterations to decrease time step + real(rkind),parameter :: F_inc = 1.25_rkind ! factor used to increase time step + real(rkind),parameter :: F_dec = 0.90_rkind ! factor used to decrease time step + ! state and flux vectors + real(rkind) :: untappedMelt(nState) ! un-tapped melt energy (J m-3 s-1) + real(rkind) :: stateVecInit(nState) ! initial state vector (mixed units) + real(rkind) :: stateVecTrial(nState) ! trial state vector (mixed units) + real(rkind) :: stateVecPrime(nState) ! trial state vector (mixed units) + type(var_dlength) :: flux_temp ! temporary model fluxes + ! flags + logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt) :: checkMassBalance ! flag to check the mass balance + logical(lgt) :: checkNrgBalance + logical(lgt) :: waterBalanceError ! flag to denote that there is a water balance error + logical(lgt) :: nrgFluxModified ! flag to denote that the energy fluxes were modified + ! energy fluxes + real(rkind) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) + real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) + real(rkind) :: sumSoilCompress ! sum of total soil compression + real(rkind),allocatable :: sumLayerCompress(:) ! sum of soil compression by layer + ! --------------------------------------------------------------------------------------- + ! point to variables in the data structures + ! --------------------------------------------------------------------------------------- + globalVars: associate(& + ! model decisions + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical method, backward Euler or SUNDIALS/IDA + ! number of layers + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! mapping between state vectors and control volumes + ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat ,& ! intent(in): [i4b(:)] list of indices for all active layers (inactive=integerMissing) + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) + ! model state variables (vegetation canopy) + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout): [dp] temperature of the vegetation canopy (K) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout): [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout): [dp] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout): [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variables (snow and soil domains) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat & ! intent(inout): [dp(:)] matric potential of liquid water (m) + ) ! end association with variables in the data structures + ! ********************************************************************************************************************************************************* + ! ********************************************************************************************************************************************************* + ! Procedure starts here + + ! initialize error control + err=0; message='varSubstep/' + + ! initialize flag for the success of the substepping + failedMinimumStep=.false. + + ! initialize the length of the substep + dtSubstep = dtInit + + ! change maxstep with hard code here to make only the newton step loop in systemSolv* happen more frequently for num_method = bEuler or sundials + ! NOTE: this may just be amplifying the splitting error if maxstep is smaller than the full possible step + maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) + + ! initalize flag for checking if energy fluxes had been modified + nrgFluxModified = .false. - ! identify the need to check the mass balance - checkMassBalance = .true. ! (.not.scalarSolution) + ! allocate space for the temporary model flux structure + call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! initialize the model fluxes (some model fluxes are not computed in the iterations) + do iVar=1,size(flux_data%var) + flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + end do + + ! initialize the total energy fluxes (modified in updateProg) + sumCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = 0._rkind ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = 0._rkind ! sensible heat flux from the canopy to the canopy air space (W m-2) + sumSoilCompress = 0._rkind ! total soil compression + allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._rkind ! soil compression by layer + + ! define the first flux call in a splitting operation + firstSplitOper = (.not.scalarSolution .or. iStateSplit==1) + + ! initialize subStep + dtSum = 0._rkind ! keep track of the portion of the time step that is completed + nSubsteps = 0 + + ! loop through substeps + ! NOTE: continuous do statement with exit clause + substeps: do + dtSubstep = min(dtSubstep,maxstep) + + ! initialize error control + err=0; message='varSubstep/' + + ! ----- + ! * populate state vectors... + ! --------------------------- + + ! initialize state vectors + call popStateVec(& + ! input + nState, & ! intent(in): number of desired state variables + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output + stateVecInit, & ! intent(out): initial model state vector (mixed units) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + + ! ----- + ! * iterative solution... + ! ----------------------- + ! solve the system of equations for a given state subset + select case(ixNumericalMethod) + case(sundials) + call systemSolvSundials(& + ! input: model control + dtSubstep, & ! intent(in): time step (s) + whole_step, & ! intent(in): entire time step (s), right now same as dtSubstep but might change with operator splitting + nState, & ! intent(in): total number of state variables + firstSubStep, & ! intent(in): flag to denote first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution + ! input/output: data structures + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): index data + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_temp, & ! intent(inout): model fluxes for a local HRU + bvar_data, & ! intent(in): model variables for the local basin + model_decisions, & ! intent(in): model decisions + stateVecInit, & ! intent(in): initial state vector + ! output: model control + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + stateVecTrial, & ! intent(out): updated state vector + stateVecPrime, & ! intent(out): updated state vector + reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step + tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt + err,cmessage) ! intent(out): error code and error message + untappedMelt(:) = 0._rkind ! set untapped melt energy to zero + niter = 0 ! will not use + case(bEuler) + call systemSolv(& + ! input: model control + dtSubstep, & ! intent(in): time step (s) + whole_step, & ! intent(in): entire time step (s) + nState, & ! intent(in): total number of state variables + firstSubStep, & ! intent(in): flag to denote first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution + ! input/output: data structures + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): index data + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_temp, & ! intent(inout): model fluxes for a local HRU + bvar_data, & ! intent(in): model variables for the local basin + model_decisions, & ! intent(in): model decisions + stateVecInit, & ! intent(in): initial state vector + ! output: model control + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + untappedMelt, & ! intent(out): un-tapped melt energy (J m-3 s-1) + stateVecTrial, & ! intent(out): updated state vector + reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step + tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt + niter, & ! intent(out): number of iterations taken + err,cmessage) ! intent(out): error code and error message + stateVecPrime = stateVecTrial ! will not use, dummy + case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return + end select + + if(err/=0)then + message=trim(message)//trim(cmessage) + if(err>0) return + endif - ! update prognostic variables - call updateProg(dtSubstep,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,checkMassBalance, & ! input: model control - lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures - waterBalanceError,nrgFluxModified,err,cmessage) ! output: flags and error control - if(err/=0)then - message=trim(message)//trim(cmessage) - if(err>0) return - endif + ! if too much melt or need to reduce length of the coupled step then return + ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry + if(tooMuchMelt .or. reduceCoupledStep) return - ! if water balance error then reduce the length of the coupled step - if(waterBalanceError)then - message=trim(message)//'water balance error' - reduceCoupledStep=.true. - err=-20; return - endif + ! identify failure, should not happen in sundials + failedSubstep = (err<0) - if(globalPrintFlag)& - print*, trim(cmessage)//': dt = ', dtSubstep - - ! recover from errors in prognostic update - if(err<0)then - - ! modify step - err=0 ! error recovery - dtSubstep = dtSubstep/2._rkind - - ! check minimum: fail minimum step if there is an error in the update - if(dtSubstep0) then - ! scalar compression - if(.not.scalarSolution .or. iStateSplit==nSoil)& - sumSoilCompress = sumSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression - ! vector compression - do iSoil=1,nSoil - if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& - sumLayerCompress(iSoil) = sumLayerCompress(iSoil) + diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) ! soil compression in layers - end do - endif + ! check + if(globalPrintFlag)then + print*, 'niter, failedSubstep, dtSubstep = ', niter, failedSubstep, dtSubstep + print*, trim(cmessage) + endif - ! print progress - if(globalPrintFlag)& - write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt - - ! increment fluxes - dt_wght = dtSubstep/dt ! (define weight applied to each splitting operation) - do iVar=1,size(flux_meta) - if(count(fluxMask%var(iVar)%dat)>0) then - - !print*, flux_meta(iVar)%varname, fluxMask%var(iVar)%dat - - ! ** no domain splitting - if(count(ixLayerActive/=integerMissing)==nLayers)then - flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght - fluxCount%var(iVar)%dat(:) = fluxCount%var(iVar)%dat(:) + 1 - - ! ** domain splitting - else - ixMin=lbound(flux_data%var(iVar)%dat) - ixMax=ubound(flux_data%var(iVar)%dat) - do ixLayer=ixMin(1),ixMax(1) - if(fluxMask%var(iVar)%dat(ixLayer)) then - flux_data%var(iVar)%dat(ixLayer) = flux_data%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght - fluxCount%var(iVar)%dat(ixLayer) = fluxCount%var(iVar)%dat(ixLayer) + 1 + ! reduce step based on failure + if(failedSubstep)then + err=0; message='varSubstep/' ! recover from failed convergence + dtMultiplier = 0.5_rkind ! system failure: step halving + else + ! ** implicit Euler: adjust step length based on iteration count + if(nitern_dec)then + dtMultiplier = F_dec + else + dtMultiplier = 1._rkind + endif + endif ! switch between failure and success + + ! check if we failed the substep + if(failedSubstep)then + + ! check that the substep is greater than the minimum step + if(dtSubstep*dtMultiplier exit, and either (1) try another solution method; or (2) reduce coupled step + failedMinimumStep=.true. + exit subSteps + + else ! step is still OK + dtSubstep = dtSubstep*dtMultiplier + cycle subSteps + endif ! if step is less than the minimum + + endif ! if failed the substep + + ! ----- + ! * update model fluxes... + ! ------------------------ + + ! NOTE: if we get to here then we are accepting the step of dtSubstep + if(err/=0)then + message=trim(message)//'expect err=0 if updating fluxes' + return endif - end do - endif ! (domain splitting) - endif ! (if the flux is desired) - end do ! (loop through fluxes) + ! identify the need to check the mass balance, only for bEuler because of how store variables + select case(ixNumericalMethod) + case(sundials); checkMassBalance = .false. + case(bEuler); checkMassBalance = .true. ! (.not.scalarSolution) + case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return + end select + ! identify the need to check the energy balance, DOES NOT WORK YET and only check if ixHowHeatCap == enthalpyFD + checkNrgBalance = .false. + + ! update prognostic variables + call updateProg(dtSubstep,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control + model_decisions,lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures + waterBalanceError,nrgFluxModified,err,cmessage) ! output: flags and error control + if(err/=0)then + message=trim(message)//trim(cmessage) + if(err>0) return + endif - ! ------------------------------------------------------ - ! ------------------------------------------------------ + ! if water balance error then reduce the length of the coupled step + if(waterBalanceError)then + message=trim(message)//'water balance error' + reduceCoupledStep=.true. + err=-20; return + endif - ! increment the number of substeps - nSubsteps = nSubsteps+1 + if(globalPrintFlag)& + print*, trim(cmessage)//': dt = ', dtSubstep + + ! recover from errors in prognostic update + if(err<0)then + + ! modify step + err=0 ! error recovery + dtSubstep = dtSubstep/2._rkind + + ! check minimum: fail minimum step if there is an error in the update + if(dtSubstep0) then + ! scalar compression + if(.not.scalarSolution .or. iStateSplit==nSoil)& + sumSoilCompress = sumSoilCompress + dtSubstep*diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression + ! vector compression + do iSoil=1,nSoil + if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& + sumLayerCompress(iSoil) = sumLayerCompress(iSoil) + dtSubstep*diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) ! soil compression in layers + end do + endif - ! increment the sub-step legth - dtSum = dtSum + dtSubstep - !print*, 'dtSum, dtSubstep, dt, nSubsteps = ', dtSum, dtSubstep, dt, nSubsteps + ! print progress + if(globalPrintFlag)& + write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt + + ! increment fluxes + dt_wght = dtSubstep/dt ! define weight applied to each sub-step + do iVar=1,size(flux_meta) + if(count(fluxMask%var(iVar)%dat)>0) then + + ! ** no domain splitting + if(count(ixLayerActive/=integerMissing)==nLayers)then + flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght + fluxCount%var(iVar)%dat(:) = fluxCount%var(iVar)%dat(:) + 1 + + ! ** domain splitting + else + ixMin=lbound(flux_data%var(iVar)%dat) + ixMax=ubound(flux_data%var(iVar)%dat) + do ixLayer=ixMin(1),ixMax(1) + if(fluxMask%var(iVar)%dat(ixLayer)) then + ! Makes Jacobian more diagonal if do this, but less correct + ! special case of the transpiration sink from soil layers: only computed for the top soil layer + !if(iVar==iLookFlux%mLayerTranspire)then + ! if(ixLayer==1) flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght + ! standard case + !else + flux_mean%var(iVar)%dat(ixLayer) = flux_mean%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght + !endif + fluxCount%var(iVar)%dat(ixLayer) = fluxCount%var(iVar)%dat(ixLayer) + 1 + endif + end do + endif ! (domain splitting) + + endif ! (if the flux is desired) + end do ! (loop through fluxes) + + ! increment the number of substeps + nSubsteps = nSubsteps+1 + + ! increment the sub-step legth + dtSum = dtSum + dtSubstep + + ! check that we have completed the sub-step + if(dtSum >= dt-verySmall)then + failedMinimumStep=.false. + exit subSteps + endif - ! check that we have completed the sub-step - if(dtSum >= dt-verySmall)then - failedMinimumStep=.false. - exit subSteps + ! adjust length of the sub-step (make sure that we don't exceed the step) + dtSubstep = min(dt - dtSum, max(dtSubstep*dtMultiplier, dt_min) ) + + end do substeps ! time steps for variable-dependent sub-stepping + ! NOTE: if we get to here then we are accepting then dtSum should dt + + ! save the fluxes as averages + do iVar=1,size(flux_meta) + if(count(fluxMask%var(iVar)%dat)>0) flux_data%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + enddo + + ! save the energy fluxes as averages + flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt ! canopy evaporation/condensation (kg m-2 s-1) + flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt ! sensible heat flux from the canopy to the canopy air space (W m-2) + + ! save the soil compression diagnostics as averages + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress/dt + do iSoil=1,nSoil + if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& + diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) = sumLayerCompress(iSoil)/dt + end do + deallocate(sumLayerCompress) + + ! end associate statements + end associate globalVars + + ! update error codes + if(failedMinimumStep)then + err=-20 ! negative = recoverable error + message=trim(message)//'failed minimum step' endif - - ! adjust length of the sub-step (make sure that we don't exceed the step) - dtSubstep = min(dt - dtSum, max(dtSubstep*dtMultiplier, dt_min) ) - - end do substeps ! time steps for variable-dependent sub-stepping - - ! save the energy fluxes - flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt ! canopy evaporation/condensation (kg m-2 s-1) - flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt ! sensible heat flux from the canopy to the canopy air space (W m-2) - - ! save the soil compression diagnostics - diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress - do iSoil=1,nSoil - if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& - diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) = sumLayerCompress(iSoil) - end do - deallocate(sumLayerCompress) - - ! end associate statements - end associate globalVars - - ! update error codes - if(failedMinimumStep)then - err=-20 ! negative = recoverable error - message=trim(message)//'failed minimum step' - endif - - end subroutine varSubstep - - - ! ********************************************************************************************************** - ! private subroutine updateProg: update prognostic variables - ! ********************************************************************************************************** - subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,checkMassBalance, & ! input: model control - lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures - waterBalanceError,nrgFluxModified,err,message) ! output: flags and error control - USE getVectorz_module,only:varExtract ! extract variables from the state vector - USE updateVars_module,only:updateVars ! update prognostic variables - implicit none - ! model control - real(rkind) ,intent(in) :: dt ! time step (s) - integer(i4b) ,intent(in) :: nSnow ! number of snow layers - integer(i4b) ,intent(in) :: nSoil ! number of soil layers - integer(i4b) ,intent(in) :: nLayers ! total number of layers - logical(lgt) ,intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature - logical(lgt) ,intent(in) :: computeVegFlux ! flag to compute the vegetation flux - real(rkind) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) - real(rkind) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) - logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance - ! data structures - type(zLookup), intent(in) :: lookup_data ! lookup tables - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! indices for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! flags and error control - logical(lgt) ,intent(out) :: waterBalanceError ! flag to denote that there is a water balance error - logical(lgt) ,intent(out) :: nrgFluxModified ! flag to denote that the energy fluxes were modified - integer(i4b) ,intent(out) :: err ! error code - character(*) ,intent(out) :: message ! error message - ! ================================================================================================================== - ! general - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: ixSubset ! index within the state subset - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixControlIndex ! index within a given domain - real(rkind) :: volMelt ! volumetric melt (kg m-3) - real(rkind),parameter :: verySmall=epsilon(1._rkind)*2._rkind ! a very small number (deal with precision issues) - ! mass balance - real(rkind) :: canopyBalance0,canopyBalance1 ! canopy storage at start/end of time step - real(rkind) :: soilBalance0,soilBalance1 ! soil storage at start/end of time step - real(rkind) :: vertFlux ! change in storage due to vertical fluxes - real(rkind) :: tranSink,baseSink,compSink ! change in storage due to sink terms - real(rkind) :: liqError ! water balance error - real(rkind) :: fluxNet ! net water fluxes (kg m-2 s-1) - real(rkind) :: superflousWat ! superflous water used for evaporation (kg m-2 s-1) - real(rkind) :: superflousNrg ! superflous energy that cannot be used for evaporation (W m-2 [J m-2 s-1]) - character(LEN=256) :: cmessage ! error message of downwind routine - ! trial state variables - real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial vector for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector for liquid water matric potential (m) - real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) - ! diagnostic variables - real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric fraction of ice (-) - ! ------------------------------------------------------------------------------------------------------------------- - - ! ------------------------------------------------------------------------------------------------------------------- - ! point to flux variables in the data structure - associate(& - ! get indices for mass balance - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in) : [i4b] index of canopy hydrology state variable (mass) - ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the soil domain - ! get indices for the un-tapped melt - ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in) : [i4b(:)] list of indices for all energy states - ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in) : [i4b(:)] indices defining the domain of the state (iname_veg, iname_snow, iname_soil) - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in) : [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in) : [i4b(:)] [state subset] list of indices of the full state vector in the state subset - ! water fluxes - scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in) : [dp] rainfall rate (kg m-2 s-1) - scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(in) : [dp] rain reaches ground without touching the canopy (kg m-2 s-1) - scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(in) : [dp] canopy evaporation/condensation (kg m-2 s-1) - scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(in) : [dp] canopy transpiration (kg m-2 s-1) - scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(in) : [dp] drainage liquid water from vegetation canopy (kg m-2 s-1) - iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(in) : [dp(0:)] vertical liquid water flux at soil layer interfaces (-) - mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(in) : [dp(:)] transpiration loss from each soil layer (m s-1) - mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(in) : [dp(:)] baseflow from each soil layer (m s-1) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in) : [dp(:)] change in storage associated with compression of the soil matrix (-) - scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the vegetation canopy (kg m-2 s-1) - scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the snow surface (kg m-2 s-1) - ! energy fluxes - scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ,& ! intent(in) : [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ,& ! intent(in) : [dp] sensible heat flux from the canopy to the canopy air space (W m-2) - ! domain depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in) : [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in) : [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! model state variables (vegetation canopy) - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout) : [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout) : [dp] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout) : [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout) : [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout) : [dp(:)] matric head (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(inout) : [dp(:)] matric potential of liquid water (m) - ! model state variables (aquifer) - scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(inout) : [dp(:)] storage of water in the aquifer (m) - ! error tolerance - absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1) & ! intent(in) : [dp] absolute convergence tolerance for vol frac liq water (-) - ) ! associating flux variables in the data structure - ! ------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='updateProg/' - - ! initialize water balance error - waterBalanceError=.false. - - ! get storage at the start of the step - canopyBalance0 = merge(scalarCanopyWat, realMissing, computeVegFlux) - soilBalance0 = sum( (mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) - - ! ----- - ! * update states... - ! ------------------ - - ! initialize to state variable from the last update - scalarCanairTempTrial = scalarCanairTemp - scalarCanopyTempTrial = scalarCanopyTemp - scalarCanopyWatTrial = scalarCanopyWat - scalarCanopyLiqTrial = scalarCanopyLiq - scalarCanopyIceTrial = scalarCanopyIce - mLayerTempTrial = mLayerTemp - mLayerVolFracWatTrial = mLayerVolFracWat - mLayerVolFracLiqTrial = mLayerVolFracLiq - mLayerVolFracIceTrial = mLayerVolFracIce - mLayerMatricHeadTrial = mLayerMatricHead - mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq - scalarAquiferStorageTrial = scalarAquiferStorage - - ! extract states from the state vector - call varExtract(& - ! input - stateVecTrial, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(inout): trial value of storage of water in the aquifer (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - !print*, 'after varExtract: scalarCanopyTempTrial =', scalarCanopyTempTrial ! trial value of canopy temperature (K) - !print*, 'after varExtract: scalarCanopyWatTrial =', scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - !print*, 'after varExtract: scalarCanopyLiqTrial =', scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) - !print*, 'after varExtract: scalarCanopyIceTrial =', scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - - ! update diagnostic variables - call updateVars(& +end subroutine varSubstep + + +! ********************************************************************************************************** +! private subroutine updateProg: update prognostic variables +! ********************************************************************************************************** +subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control + model_decisions,lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures + waterBalanceError,nrgFluxModified,err,message) ! output: flags and error control + USE getVectorz_module,only:varExtract ! extract variables from the state vector + USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables + USE updateVars_module,only:updateVars ! update prognostic variables + USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy + implicit none + ! model control + real(rkind) ,intent(in) :: dt ! time step (s) + integer(i4b) ,intent(in) :: nSnow ! number of snow layers + integer(i4b) ,intent(in) :: nSoil ! number of soil layers + integer(i4b) ,intent(in) :: nLayers ! total number of layers + logical(lgt) ,intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature + logical(lgt) ,intent(in) :: computeVegFlux ! flag to compute the vegetation flux + real(rkind) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) + real(rkind) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) + real(rkind) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) + logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance + logical(lgt) ,intent(in) :: checkNrgBalance ! flag to check the energy balance + ! data structures + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! indices for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + ! flags and error control + logical(lgt) ,intent(out) :: waterBalanceError ! flag to denote that there is a water balance error + logical(lgt) ,intent(out) :: nrgFluxModified ! flag to denote that the energy fluxes were modified + integer(i4b) ,intent(out) :: err ! error code + character(*) ,intent(out) :: message ! error message + ! ================================================================================================================== + ! general + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: ixSubset ! index within the state subset + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixControlIndex ! index within a given domain + real(rkind) :: volMelt ! volumetric melt (kg m-3) + real(rkind),parameter :: verySmall=epsilon(1._rkind)*2._rkind ! a very small number (deal with precision issues) + ! mass balance + real(rkind) :: canopyBalance0,canopyBalance1 ! canopy storage at start/end of time step + real(rkind) :: soilBalance0,soilBalance1 ! soil storage at start/end of time step + real(rkind) :: vertFlux ! change in storage due to vertical fluxes + real(rkind) :: tranSink,baseSink,compSink ! change in storage due to sink terms + real(rkind) :: liqError ! water balance error + real(rkind) :: fluxNet ! net water fluxes (kg m-2 s-1) + real(rkind) :: superflousWat ! superflous water used for evaporation (kg m-2 s-1) + real(rkind) :: superflousNrg ! superflous energy that cannot be used for evaporation (W m-2 [J m-2 s-1]) + character(LEN=256) :: cmessage ! error message of downwind routine + ! trial state variables + real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial vector for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector for liquid water matric potential (m) + real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) + ! diagnostic variables + real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric fraction of ice (-) + ! derivative of state variables + real(rkind) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatPrime ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempPrime ! trial vector for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! trial vector for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! trial vector for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! trial vector for liquid water matric potential (m) + real(rkind) :: scalarAquiferStoragePrime ! trial value for storage of water in the aquifer (m) + ! diagnostic variables + real(rkind) :: scalarCanopyLiqPrime ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector for volumetric fraction of ice (-) + real(rkind) :: scalarCanairEnthalpyTrial ! enthalpy of the canopy air space (J m-3) + real(rkind) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! enthalpy of snow + soil (J m-3) + ! enthalpy derivatives + real(rkind) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature + real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state + real(rkind),dimension(nLayers) :: dEnthalpy_dTk ! derivatives in layer enthalpy w.r.t. temperature + real(rkind),dimension(nLayers) :: dEnthalpy_dWat ! derivatives in layer enthalpy w.r.t. water state + ! ------------------------------------------------------------------------------------------------------------------- + + ! ------------------------------------------------------------------------------------------------------------------- + ! point to flux variables in the data structure + associate(& + ! model decisions + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical method, backward Euler or SUNDIALS/IDA + ! get indices for mass balance + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in) : [i4b] index of canopy hydrology state variable (mass) + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the soil domain + ! get indices for the un-tapped melt + ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in) : [i4b(:)] list of indices for all energy states + ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in) : [i4b(:)] indices defining the domain of the state (iname_veg, iname_snow, iname_soil) + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in) : [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in) : [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ! water fluxes + scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in) : [dp] rainfall rate (kg m-2 s-1) + scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(in) : [dp] rain reaches ground without touching the canopy (kg m-2 s-1) + scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(in) : [dp] canopy evaporation/condensation (kg m-2 s-1) + scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(in) : [dp] canopy transpiration (kg m-2 s-1) + scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(in) : [dp] drainage liquid water from vegetation canopy (kg m-2 s-1) + iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(in) : [dp(0:)] vertical liquid water flux at soil layer interfaces (-) + iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(in) : + mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) + mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(in) : [dp(:)] transpiration loss from each soil layer (m s-1) + mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(in) : [dp(:)] baseflow from each soil layer (m s-1) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in) : [dp(:)] change in storage associated with compression of the soil matrix (-) + scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the vegetation canopy (kg m-2 s-1) + scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the snow surface (kg m-2 s-1) + ! energy fluxes + scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ,& ! intent(in) : [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ,& ! intent(in) : [dp] sensible heat flux from the canopy to the canopy air space (W m-2) + ! domain depth + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in) : [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in) : [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! model state variables (vegetation canopy) + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the vegetation canopy (K) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout) : [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout) : [dp] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout) : [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variables (snow and soil domains) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout) : [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout) : [dp(:)] matric head (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(inout) : [dp(:)] matric potential of liquid water (m) + ! enthalpy + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(inout): [dp(:)] enthalpy of the snow+soil layers (J m-3) + ! derivatives, diagnositic for enthalpy + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + ! model state variables (aquifer) + scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(inout) : [dp(:)] storage of water in the aquifer (m) + ! error tolerance + absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1) & ! intent(in) : [dp] absolute convergence tolerance for vol frac liq water (-) + ) ! associating flux variables in the data structure + ! ------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='updateProg/' + + ! initialize water balancmLayerVolFracWatTrial error + waterBalanceError=.false. + + ! get storage at the start of the step + canopyBalance0 = merge(scalarCanopyLiq + scalarCanopyIce, realMissing, computeVegFlux) + soilBalance0 = sum( (mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) + + ! ----- + ! * update states... + ! ------------------ + + ! initialize to state variable from the last update + scalarCanairTempTrial = scalarCanairTemp + scalarCanopyTempTrial = scalarCanopyTemp + scalarCanopyWatTrial = scalarCanopyWat + scalarCanopyLiqTrial = scalarCanopyLiq + scalarCanopyIceTrial = scalarCanopyIce + mLayerTempTrial = mLayerTemp + mLayerVolFracWatTrial = mLayerVolFracWat + mLayerVolFracLiqTrial = mLayerVolFracLiq + mLayerVolFracIceTrial = mLayerVolFracIce + mLayerMatricHeadTrial = mLayerMatricHead + mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq + scalarAquiferStorageTrial = scalarAquiferStorage + + ! extract states from the state vector + call varExtract(& + ! input + stateVecTrial, & ! intent(in): model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + ! output: variables for the aquifer + scalarAquiferStorageTrial,& ! intent(inout): trial value of storage of water in the aquifer (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! initialize to state variable from the last update + ! should all be set to previous values if splits, but for now operator splitting is not hooked up + scalarCanairTempPrime = realMissing + scalarCanopyTempPrime = realMissing + scalarCanopyWatPrime = realMissing + scalarCanopyLiqPrime = realMissing + scalarCanopyIcePrime = realMissing + mLayerTempPrime = realMissing + mLayerVolFracWatPrime = realMissing + mLayerVolFracLiqPrime = realMissing + mLayerVolFracIcePrime = realMissing + mLayerMatricHeadPrime = realMissing + mLayerMatricHeadLiqPrime = realMissing + scalarAquiferStoragePrime = realMissing + + select case(ixNumericalMethod) + case(sundials) + call varExtract(& + ! input + stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy + scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K) + scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): derivative of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K) + mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (-) + mLayerMatricHeadPrime, & ! intent(inout): derivative of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): derivative of liquid water matric potential (m) + ! output: variables for the aquifer + scalarAquiferStoragePrime,& ! intent(inout): derivative of storage of water in the aquifer (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! update diagnostic variables + call updateVarsSundials(& + ! input + .false., & ! intent(in): logical flag if computing Jacobian for Sundials solver + doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output: variables for the vegetation canopy + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempPrime, & ! + mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) + mLayerVolFracIcePrime, & ! + mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) + ! output: error control + err,cmessage) ! intent(out): error control + case(bEuler) + ! update diagnostic variables + call updateVars(& ! input doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze lookup_data, & ! intent(in): lookup tables for a local HRU @@ -732,279 +911,323 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) ! output: error control err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - !print*, 'after updateVars: scalarCanopyTempTrial =', scalarCanopyTempTrial ! trial value of canopy temperature (K) - !print*, 'after updateVars: scalarCanopyWatTrial =', scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - !print*, 'after updateVars: scalarCanopyLiqTrial =', scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) - !print*, 'after updateVars: scalarCanopyIceTrial =', scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - - ! ----- - ! * check mass balance... - ! ----------------------- - - ! NOTE: should not need to do this, since mass balance is checked in the solver - if(checkMassBalance)then - - ! check mass balance for the canopy - if(ixVegHyd/=integerMissing)then - - ! handle cases where fluxes empty the canopy - fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage - if(-fluxNet*dt > canopyBalance0)then - - ! --> first add water - canopyBalance1 = canopyBalance0 + (scalarRainfall - scalarThroughfallRain)*dt - - ! --> next, remove canopy evaporation -- put the unsatisfied evap into sensible heat - canopyBalance1 = canopyBalance1 + scalarCanopyEvaporation*dt - if(canopyBalance1 < 0._rkind)then - ! * get superfluous water and energy - superflousWat = -canopyBalance1/dt ! kg m-2 s-1 - superflousNrg = superflousWat*LH_vap ! W m-2 (J m-2 s-1) - ! * update fluxes and states - canopyBalance1 = 0._rkind - scalarCanopyEvaporation = scalarCanopyEvaporation + superflousWat - scalarLatHeatCanopyEvap = scalarLatHeatCanopyEvap + superflousNrg - scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg - endif + case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return + end select + + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! ---- + ! * check energy balance + !------------------------ + if(checkNrgBalance)then + ! compute enthalpy at t_{n+1} + call t2enthalpy(& + .true., & ! intent(in): logical flag to include phase change in enthalpy + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) + ! input: pre-computed derivatives + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + ! output: enthalpy + scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! --> next, remove canopy drainage - canopyBalance1 = canopyBalance1 - scalarCanopyLiqDrainage*dt - if(canopyBalance1 < 0._rkind)then - superflousWat = -canopyBalance1/dt ! kg m-2 s-1 - canopyBalance1 = 0._rkind - scalarCanopyLiqDrainage = scalarCanopyLiqDrainage + superflousWat endif - ! update the trial state - scalarCanopyWatTrial = canopyBalance1 - - ! set the modification flag - nrgFluxModified = .true. - - else - canopyBalance1 = canopyBalance0 + fluxNet*dt - nrgFluxModified = .false. - endif ! cases where fluxes empty the canopy - - ! check the mass balance - fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage - liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial - !write(*,'(a,1x,f20.10)') 'dt = ', dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - !write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 - !write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 - !write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt - !write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - !write(*,'(a,1x,f20.10)') 'liqError = ', liqError - if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues - waterBalanceError = .true. - return - endif ! if there is a water balance error - endif ! if veg canopy - - ! check mass balance for soil - ! NOTE: fatal errors, though possible to recover using negative error codes - if(count(ixSoilOnlyHyd/=integerMissing)==nSoil)then - soilBalance1 = sum( (mLayerVolFracLiqTrial(nSnow+1:nLayers) + mLayerVolFracIceTrial(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) - vertFlux = -(iLayerLiqFluxSoil(nSoil) - iLayerLiqFluxSoil(0))*dt ! m s-1 --> m - tranSink = sum(mLayerTranspire)*dt ! m s-1 --> m - baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m - compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m - liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) - if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues - !write(*,'(a,1x,f20.10)') 'dt = ', dt - !write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 - !write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 - !write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux - !write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink - !write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink - !write(*,'(a,1x,f20.10)') 'compSink = ', compSink - !write(*,'(a,1x,f20.10)') 'liqError = ', liqError - !write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid - waterBalanceError = .true. - return - endif ! if there is a water balance error - endif ! if hydrology states exist in the soil domain - - endif ! if checking the mass balance - - ! ----- - ! * remove untapped melt energy... - ! -------------------------------- - - ! only work with energy state variables - if(size(ixNrgOnly)>0)then ! energy state variables exist - - ! loop through energy state variables - do iState=1,size(ixNrgOnly) - - ! get index of the control volume within the domain - ixSubset = ixNrgOnly(iState) ! index within the state subset - ixFullVector = ixMapSubset2Full(ixSubset) ! index within full state vector - ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain - - ! compute volumetric melt (kg m-3) - volMelt = dt*untappedMelt(ixSubset)/LH_fus ! (kg m-3) - - ! update ice content - select case( ixDomainType(ixFullVector) ) - case(iname_cas); cycle ! do nothing, since there is no snow stored in the canopy air space - case(iname_veg); scalarCanopyIceTrial = scalarCanopyIceTrial - volMelt*canopyDepth ! (kg m-2) - case(iname_snow); mLayerVolFracIceTrial(ixControlIndex) = mLayerVolFracIceTrial(ixControlIndex) - volMelt/iden_ice ! (-) - case(iname_soil); mLayerVolFracIceTrial(ixControlIndex+nSnow) = mLayerVolFracIceTrial(ixControlIndex+nSnow) - volMelt/iden_water ! (-) - case default; err=20; message=trim(message)//'unable to identify domain type [remove untapped melt energy]'; return - end select - - ! update liquid water content - select case( ixDomainType(ixFullVector) ) - case(iname_cas); cycle ! do nothing, since there is no snow stored in the canopy air space - case(iname_veg); scalarCanopyLiqTrial = scalarCanopyLiqTrial + volMelt*canopyDepth ! (kg m-2) - case(iname_snow); mLayerVolFracLiqTrial(ixControlIndex) = mLayerVolFracLiqTrial(ixControlIndex) + volMelt/iden_water ! (-) - case(iname_soil); mLayerVolFracLiqTrial(ixControlIndex+nSnow) = mLayerVolFracLiqTrial(ixControlIndex+nSnow) + volMelt/iden_water ! (-) - case default; err=20; message=trim(message)//'unable to identify domain type [remove untapped melt energy]'; return - end select - - end do ! looping through energy variables - - ! ======================================================================================================== - - ! *** ice - - ! --> check if we removed too much water - if(scalarCanopyIceTrial < 0._rkind .or. any(mLayerVolFracIceTrial < 0._rkind) )then - - ! ** - ! canopy within numerical precision - if(scalarCanopyIceTrial < 0._rkind)then - - if(scalarCanopyIceTrial > -verySmall)then - scalarCanopyLiqTrial = scalarCanopyLiqTrial - scalarCanopyIceTrial - scalarCanopyIceTrial = 0._rkind - - ! encountered an inconsistency: spit the dummy - else - print*, 'dt = ', dt - print*, 'untappedMelt = ', untappedMelt - print*, 'untappedMelt*dt = ', untappedMelt*dt - print*, 'scalarCanopyiceTrial = ', scalarCanopyIceTrial - message=trim(message)//'melted more than the available water' - err=20; return - endif ! (inconsistency) - - endif ! if checking the canopy - - ! ** - ! snow+soil within numerical precision - do iState=1,size(mLayerVolFracIceTrial) - - ! snow layer within numerical precision - if(mLayerVolFracIceTrial(iState) < 0._rkind)then - - if(mLayerVolFracIceTrial(iState) > -verySmall)then - mLayerVolFracLiqTrial(iState) = mLayerVolFracLiqTrial(iState) - mLayerVolFracIceTrial(iState) - mLayerVolFracIceTrial(iState) = 0._rkind - - ! encountered an inconsistency: spit the dummy - else - print*, 'dt = ', dt - print*, 'untappedMelt = ', untappedMelt - print*, 'untappedMelt*dt = ', untappedMelt*dt - print*, 'mLayerVolFracIceTrial = ', mLayerVolFracIceTrial - message=trim(message)//'melted more than the available water' - err=20; return - endif ! (inconsistency) - - endif ! if checking a snow layer - - end do ! (looping through state variables) - - endif ! (if we removed too much water) - - ! ======================================================================================================== - - ! *** liquid water - - ! --> check if we removed too much water - if(scalarCanopyLiqTrial < 0._rkind .or. any(mLayerVolFracLiqTrial < 0._rkind) )then - - ! ** - ! canopy within numerical precision - if(scalarCanopyLiqTrial < 0._rkind)then - - if(scalarCanopyLiqTrial > -verySmall)then - scalarCanopyIceTrial = scalarCanopyIceTrial - scalarCanopyLiqTrial - scalarCanopyLiqTrial = 0._rkind - - ! encountered an inconsistency: spit the dummy - else - print*, 'dt = ', dt - print*, 'untappedMelt = ', untappedMelt - print*, 'untappedMelt*dt = ', untappedMelt*dt - print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial - message=trim(message)//'frozen more than the available water' - err=20; return - endif ! (inconsistency) - - endif ! checking the canopy - - ! ** - ! snow+soil within numerical precision - do iState=1,size(mLayerVolFracLiqTrial) - - ! snow layer within numerical precision - if(mLayerVolFracLiqTrial(iState) < 0._rkind)then - - if(mLayerVolFracLiqTrial(iState) > -verySmall)then - mLayerVolFracIceTrial(iState) = mLayerVolFracIceTrial(iState) - mLayerVolFracLiqTrial(iState) - mLayerVolFracLiqTrial(iState) = 0._rkind - - ! encountered an inconsistency: spit the dummy - else - print*, 'dt = ', dt - print*, 'untappedMelt = ', untappedMelt - print*, 'untappedMelt*dt = ', untappedMelt*dt - print*, 'mLayerVolFracLiqTrial = ', mLayerVolFracLiqTrial - message=trim(message)//'frozen more than the available water' - err=20; return - endif ! (inconsistency) - - endif ! checking a snow layer - - end do ! (looping through state variables) - - endif ! (if we removed too much water) - - endif ! (if energy state variables exist) - - ! ----- - ! * update prognostic variables... - ! -------------------------------- - - ! update state variables for the vegetation canopy - scalarCanairTemp = scalarCanairTempTrial ! trial value of canopy air temperature (K) - scalarCanopyTemp = scalarCanopyTempTrial ! trial value of canopy temperature (K) - scalarCanopyWat = scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - scalarCanopyLiq = scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) - scalarCanopyIce = scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - - ! update state variables for the snow+soil domain - mLayerTemp = mLayerTempTrial ! trial vector of layer temperature (K) - mLayerVolFracWat = mLayerVolFracWatTrial ! trial vector of volumetric total water content (-) - mLayerVolFracLiq = mLayerVolFracLiqTrial ! trial vector of volumetric liquid water content (-) - mLayerVolFracIce = mLayerVolFracIceTrial ! trial vector of volumetric ice water content (-) - mLayerMatricHead = mLayerMatricHeadTrial ! trial vector of matric head (m) - mLayerMatricHeadLiq = mLayerMatricHeadLiqTrial ! trial vector of matric head (m) - - ! update state variables for the aquifer - scalarAquiferStorage = scalarAquiferStorageTrial - - ! end associations to info in the data structures - end associate - - end subroutine updateProg + ! ----- + ! * check mass balance... + ! ----------------------- + + ! NOTE: should not need to do this, since mass balance is checked in the solver + ! for sundials will not work since fluxes are averaged over data window for output but canopyBalance0 only off last step + ! so not currently checked in sundials, could cause problems if should modify nrgFlux + if(checkMassBalance)then + + ! check mass balance for the canopy + if(ixVegHyd/=integerMissing)then + + ! handle cases where fluxes empty the canopy + fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + if(-fluxNet*dt > canopyBalance0)then + + ! --> first add water + canopyBalance1 = canopyBalance0 + (scalarRainfall - scalarThroughfallRain)*dt + + ! --> next, remove canopy evaporation -- put the unsatisfied evap into sensible heat + canopyBalance1 = canopyBalance1 + scalarCanopyEvaporation*dt + if(canopyBalance1 < 0._rkind)then + ! * get superfluous water and energy + superflousWat = -canopyBalance1/dt ! kg m-2 s-1 + superflousNrg = superflousWat*LH_vap ! W m-2 (J m-2 s-1) + ! * update fluxes and states + canopyBalance1 = 0._rkind + scalarCanopyEvaporation = scalarCanopyEvaporation + superflousWat + scalarLatHeatCanopyEvap = scalarLatHeatCanopyEvap + superflousNrg + scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg + endif + + ! --> next, remove canopy drainage + canopyBalance1 = canopyBalance1 -scalarCanopyLiqDrainage*dt + if(canopyBalance1 < 0._rkind)then + superflousWat = -canopyBalance1/dt ! kg m-2 s-1 + canopyBalance1 = 0._rkind + scalarCanopyLiqDrainage = scalarCanopyLiqDrainage + superflousWat + endif + + ! update the trial state + scalarCanopyWatTrial = canopyBalance1 + + ! set the modification flag + nrgFluxModified = .true. + + else + canopyBalance1 = canopyBalance0 + fluxNet*dt + nrgFluxModified = .false. + endif ! cases where fluxes empty the canopy + + ! check the mass balance + fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial + if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues + !write(*,'(a,1x,f20.10)') 'dt = ', dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + !write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 + !write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 + !write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt + !write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt + !write(*,'(a,1x,f20.10)') 'liqError = ', liqError + waterBalanceError = .true. + return + endif ! if there is a water balance error + endif ! if veg canopy + + ! check mass balance for soil, again not checked for sundials + ! NOTE: fatal errors, though possible to recover using negative error codes + if(count(ixSoilOnlyHyd/=integerMissing)==nSoil)then + soilBalance1 = sum( (mLayerVolFracLiqTrial(nSnow+1:nLayers) + mLayerVolFracIceTrial(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) + vertFlux = -(iLayerLiqFluxSoil(nSoil) - iLayerLiqFluxSoil(0))*dt ! m s-1 --> m + tranSink = sum(mLayerTranspire)*dt ! m s-1 --> m + baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m + compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m + liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) + if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues + !write(*,'(a,1x,f20.10)') 'dt = ', dt + !write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 + !write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 + !write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux + !write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink + !write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink + !write(*,'(a,1x,f20.10)') 'compSink = ', compSink + !write(*,'(a,1x,f20.10)') 'liqError = ', liqError + waterBalanceError = .true. + return + endif ! if there is a water balance error + endif ! if hydrology states exist in the soil domain + endif ! if checking the mass balance + + ! ----- + ! * remove untapped melt energy... always 0 at the moment but if use should be in solved as affects state + ! -------------------------------- + + ! only work with energy state variables + if(size(ixNrgOnly)>0)then ! energy state variables exist + + ! loop through energy state variables + do iState=1,size(ixNrgOnly) + + ! get index of the control volume within the domain + ixSubset = ixNrgOnly(iState) ! index within the state subset + ixFullVector = ixMapSubset2Full(ixSubset) ! index within full state vector + ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain + + ! compute volumetric melt (kg m-3) + volMelt = dt*untappedMelt(ixSubset)/LH_fus ! (kg m-3) + + ! update ice content + select case( ixDomainType(ixFullVector) ) + case(iname_cas); cycle ! do nothing, since there is no snow stored in the canopy air space + case(iname_veg); scalarCanopyIceTrial = scalarCanopyIceTrial - volMelt*canopyDepth ! (kg m-2) + case(iname_snow); mLayerVolFracIceTrial(ixControlIndex) = mLayerVolFracIceTrial(ixControlIndex) - volMelt/iden_ice ! (-) + case(iname_soil); mLayerVolFracIceTrial(ixControlIndex+nSnow) = mLayerVolFracIceTrial(ixControlIndex+nSnow) - volMelt/iden_water ! (-) + case default; err=20; message=trim(message)//'unable to identify domain type [remove untapped melt energy]'; return + end select + + ! update liquid water content + select case( ixDomainType(ixFullVector) ) + case(iname_cas); cycle ! do nothing, since there is no snow stored in the canopy air space + case(iname_veg); scalarCanopyLiqTrial = scalarCanopyLiqTrial + volMelt*canopyDepth ! (kg m-2) + case(iname_snow); mLayerVolFracLiqTrial(ixControlIndex) = mLayerVolFracLiqTrial(ixControlIndex) + volMelt/iden_water ! (-) + case(iname_soil); mLayerVolFracLiqTrial(ixControlIndex+nSnow) = mLayerVolFracLiqTrial(ixControlIndex+nSnow) + volMelt/iden_water ! (-) + case default; err=20; message=trim(message)//'unable to identify domain type [remove untapped melt energy]'; return + end select + + end do ! looping through energy variables + + ! ======================================================================================================== + + ! *** ice + + ! --> check if we removed too much water + if(scalarCanopyIceTrial < 0._rkind .or. any(mLayerVolFracIceTrial < 0._rkind) )then + + ! ** + ! canopy within numerical precision + if(scalarCanopyIceTrial < 0._rkind)then + + if(scalarCanopyIceTrial > -verySmall)then + scalarCanopyLiqTrial = scalarCanopyLiqTrial - scalarCanopyIceTrial + scalarCanopyIceTrial = 0._rkind + + ! encountered an inconsistency: spit the dummy + else + print*, 'dt = ', dt + print*, 'untappedMelt = ', untappedMelt + print*, 'untappedMelt*dt = ', untappedMelt*dt + print*, 'scalarCanopyiceTrial = ', scalarCanopyIceTrial + message=trim(message)//'melted more than the available water' + err=20; return + endif ! (inconsistency) + + endif ! if checking the canopy + ! ** + ! snow+soil within numerical precision + do iState=1,size(mLayerVolFracIceTrial) + + ! snow layer within numerical precision + if(mLayerVolFracIceTrial(iState) < 0._rkind)then + + if(mLayerVolFracIceTrial(iState) > -verySmall)then + mLayerVolFracLiqTrial(iState) = mLayerVolFracLiqTrial(iState) - mLayerVolFracIceTrial(iState) + mLayerVolFracIceTrial(iState) = 0._rkind + + ! encountered an inconsistency: spit the dummy + else + print*, 'dt = ', dt + print*, 'untappedMelt = ', untappedMelt + print*, 'untappedMelt*dt = ', untappedMelt*dt + print*, 'mLayerVolFracIceTrial = ', mLayerVolFracIceTrial + message=trim(message)//'melted more than the available water' + err=20; return + endif ! (inconsistency) + + endif ! if checking a snow layer + + end do ! (looping through state variables) + + endif ! (if we removed too much water) + + ! ======================================================================================================== + + ! *** liquid water + + ! --> check if we removed too much water + if(scalarCanopyLiqTrial < 0._rkind .or. any(mLayerVolFracLiqTrial < 0._rkind) )then + + ! ** + ! canopy within numerical precision + if(scalarCanopyLiqTrial < 0._rkind)then + + if(scalarCanopyLiqTrial > -verySmall)then + scalarCanopyIceTrial = scalarCanopyIceTrial - scalarCanopyLiqTrial + scalarCanopyLiqTrial = 0._rkind + + ! encountered an inconsistency: spit the dummy + else + print*, 'dt = ', dt + print*, 'untappedMelt = ', untappedMelt + print*, 'untappedMelt*dt = ', untappedMelt*dt + print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial + message=trim(message)//'frozen more than the available water' + err=20; return + endif ! (inconsistency) + endif ! checking the canopy + + ! ** + ! snow+soil within numerical precision + do iState=1,size(mLayerVolFracLiqTrial) + + ! snow layer within numerical precision + if(mLayerVolFracLiqTrial(iState) < 0._rkind)then + + if(mLayerVolFracLiqTrial(iState) > -verySmall)then + mLayerVolFracIceTrial(iState) = mLayerVolFracIceTrial(iState) - mLayerVolFracLiqTrial(iState) + mLayerVolFracLiqTrial(iState) = 0._rkind + + ! encountered an inconsistency: spit the dummy + else + print*, 'dt = ', dt + print*, 'untappedMelt = ', untappedMelt + print*, 'untappedMelt*dt = ', untappedMelt*dt + print*, 'mLayerVolFracLiqTrial = ', mLayerVolFracLiqTrial + message=trim(message)//'frozen more than the available water' + err=20; return + endif ! (inconsistency) + + endif ! checking a snow layer + + end do ! (looping through state variables) + + endif ! (if we removed too much water) + + endif ! (if energy state variables exist) + + ! ----- + ! * update enthalpy as a diagnostic variable... + ! -------------------------------- + scalarCanairEnthalpy = scalarCanairEnthalpyTrial + scalarCanopyEnthalpy = scalarCanopyEnthalpyTrial + mLayerEnthalpy = mLayerEnthalpyTrial + + ! ----- + ! * update prognostic variables... + ! -------------------------------- + ! update state variables for the vegetation canopy + scalarCanairTemp = scalarCanairTempTrial ! trial value of canopy air temperature (K) + scalarCanopyTemp = scalarCanopyTempTrial ! trial value of canopy temperature (K) + scalarCanopyWat = scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + scalarCanopyLiq = scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) + scalarCanopyIce = scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + + ! update state variables for the snow+soil domain + mLayerTemp = mLayerTempTrial ! trial vector of layer temperature (K) + mLayerVolFracWat = mLayerVolFracWatTrial ! trial vector of volumetric total water content (-) + mLayerVolFracLiq = mLayerVolFracLiqTrial ! trial vector of volumetric liquid water content (-) + mLayerVolFracIce = mLayerVolFracIceTrial ! trial vector of volumetric ice water content (-) + mLayerMatricHead = mLayerMatricHeadTrial ! trial vector of matric head (m) + mLayerMatricHeadLiq = mLayerMatricHeadLiqTrial ! trial vector of matric head (m) + + ! update state variables for the aquifer + scalarAquiferStorage = scalarAquiferStorageTrial + + ! end associations to info in the data structures + end associate + +end subroutine updateProg end module varSubstep_module diff --git a/build/source/engine/varSubstepSundials.f90 b/build/source/engine/varSubstepSundials.f90 deleted file mode 100644 index 47b7fc3a7..000000000 --- a/build/source/engine/varSubstepSundials.f90 +++ /dev/null @@ -1,1126 +0,0 @@ -! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2015 NCAR/RAL -! -! This file is part of SUMMA -! -! For more information see: http://www.ral.ucar.edu/projects/summa -! -! This program is free software: you can redistribute it and/or modify -! it under the terms of the GNU General Public License as published by -! the Free Software Foundation, either version 3 of the License, or -! (at your option) any later version. -! -! This program is distributed in the hope that it will be useful, -! but WITHOUT ANY WARRANTY; without even the implied warranty of -! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -! GNU General Public License for more details. -! -! You should have received a copy of the GNU General Public License -! along with this program. If not, see . - -module varSubstepSundials_module - -! data types -USE nrtype - -! access missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing double precision number -USE globalData,only:quadMissing ! missing quadruple precision number - -! access the global print flag -USE globalData,only:globalPrintFlag - -! domain types -USE globalData,only:iname_cas ! named variables for the canopy air space -USE globalData,only:iname_veg ! named variables for vegetation -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil - -! global metadata -USE globalData,only:flux_meta ! metadata on the model fluxes - -! derived types to define the data structures -USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_flagVec, & ! data vector with variable length dimension (i4b) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! data vector with variable length dimension (rkind) - model_options ! defines the model decisions - -! provide access to indices that define elements of the data structures -USE var_lookup,only:iLookFLUX ! named variables for structure elements -USE var_lookup,only:iLookPROG ! named variables for structure elements -USE var_lookup,only:iLookDIAG ! named variables for structure elements -USE var_lookup,only:iLookPARAM ! named variables for structure elements -USE var_lookup,only:iLookINDEX ! named variables for structure elements -USE var_lookup,only:iLookDERIV ! named variables for structure elements - -! look up structure for variable types -USE var_lookup,only:iLookVarType - -! constants -USE multiconst,only:& - Tfreeze, & ! freezing temperature (K) - LH_fus, & ! latent heat of fusion (J kg-1) - LH_vap, & ! latent heat of vaporization (J kg-1) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water, & ! intrinsic density of liquid water (kg m-3) - ! specific heat - Cp_air, & ! specific heat of air (J kg-1 K-1) - Cp_ice, & ! specific heat of ice (J kg-1 K-1) - Cp_soil, & ! specific heat of soil (J kg-1 K-1) - Cp_water ! specific heat of liquid water (J kg-1 K-1) - -! safety: set private unless specified otherwise -implicit none -private -public::varSubstepSundials - -! algorithmic parameters -real(rkind),parameter :: verySmall=1.e-6_rkind ! used as an additive constant to check if substantial difference among real numbers - -contains - - -! ********************************************************************************************************** -! public subroutine varSubstepSundials: run the model for a collection of substeps for a given state subset -! ********************************************************************************************************** -subroutine varSubstepSundials(& - ! input: model control - dt, & ! intent(in) : time step (s) - dtInit, & ! intent(in) : initial time step (seconds) - dt_min, & ! intent(in) : minimum time step (seconds) - nState, & ! intent(in) : total number of state variables - doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature - firstSubStep, & ! intent(in) : flag to denote first sub-step - firstFluxCall, & ! intent(inout) : flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in) : flag to denote if computing energy flux over vegetation - scalarSolution, & ! intent(in) : flag to denote implementing the scalar solution - iStateSplit, & ! intent(in) : index of the state in the splitting operation - fluxMask, & ! intent(in) : mask for the fluxes used in this given state subset - fluxCount, & ! intent(inout) : number of times that fluxes are updated (should equal nSubsteps) - ! input/output: data structures - model_decisions, & ! intent(in) : model decisions - lookup_data, & ! intent(in) : lookup tables - type_data, & ! intent(in) : type of vegetation and soil - attr_data, & ! intent(in) : spatial attributes - forc_data, & ! intent(in) : model forcing data - mpar_data, & ! intent(in) : model parameters - indx_data, & ! intent(inout) : index data - prog_data, & ! intent(inout) : model prognostic variables for a local HRU - diag_data, & ! intent(inout) : model diagnostic variables for a local HRU - flux_data, & ! intent(inout) : model fluxes for a local HRU - deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables - bvar_data, & ! intent(in) : model variables for the local basin - ! output: model control - ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) - dtMultiplier, & ! intent(out) : substep multiplier (-) - nSubsteps, & ! intent(out) : number of substeps taken for a given split - failedMinimumStep, & ! intent(out) : flag to denote success of substepping for a given split - reduceCoupledStep, & ! intent(out) : flag to denote need to reduce the length of the coupled step - tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt - dt_out, & ! intent(out) - err,message) ! intent(out) : error code and error message - ! --------------------------------------------------------------------------------------- - ! structure allocations - USE allocspace_module,only:allocLocal ! allocate local data structures - ! simulation of fluxes and residuals given a trial state vector - USE systemSolv_module,only:systemSolv ! solve the system of equations for one time step - USE getVectorz_module,only:popStateVec ! populate the state vector - USE getVectorz_module,only:varExtract ! extract variables from the state vector - USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables - ! identify name of variable type (for error message) - USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages - USE systemSolvSundials_module,only:systemSolvSundials - implicit none - ! --------------------------------------------------------------------------------------- - ! * dummy variables - ! --------------------------------------------------------------------------------------- - ! input: model control - real(rkind),intent(in) :: dt ! time step (seconds) - real(rkind),intent(in) :: dtInit ! initial time step (seconds) - real(rkind),intent(in) :: dt_min ! minimum time step (seconds) - integer(i4b),intent(in) :: nState ! total number of state variables - logical(lgt),intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) - logical(lgt),intent(in) :: scalarSolution ! flag to denote implementing the scalar solution - integer(i4b),intent(in) :: iStateSplit ! index of the state in the splitting operation - type(var_flagVec),intent(in) :: fluxMask ! flags to denote if the flux is calculated in the given state subset - type(var_ilength),intent(inout) :: fluxCount ! number of times that the flux is updated (should equal nSubsteps) - ! input/output: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(zLookup),intent(in) :: lookup_data ! lookup tables - type(var_i),intent(in) :: type_data ! type of vegetation and soil - type(var_d),intent(in) :: attr_data ! spatial attributes - type(var_d),intent(in) :: forc_data ! model forcing data - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU - type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin - ! output: model control - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: dtMultiplier ! substep multiplier (-) - integer(i4b),intent(out) :: nSubsteps ! number of substeps taken for a given split - logical(lgt),intent(out) :: failedMinimumStep ! flag to denote success of substepping for a given split - logical(lgt),intent(out) :: reduceCoupledStep ! flag to denote need to reduce the length of the coupled step - logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt - real(qp),intent(out) :: dt_out - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! --------------------------------------------------------------------------------------- - ! * general local variables - ! --------------------------------------------------------------------------------------- - ! error control - character(LEN=256) :: cmessage ! error message of downwind routine - ! general local variables - integer(i4b) :: iVar ! index of variables in data structures - integer(i4b) :: iSoil ! index of soil layers - integer(i4b) :: ixLayer ! index in a given domain - integer(i4b), dimension(1) :: ixMin,ixMax ! bounds of a given flux vector - ! time stepping - real(rkind) :: dtSum ! sum of time from successful steps (seconds) - real(rkind) :: dt_wght ! weight given to a given flux calculation - real(rkind) :: dtSubstep ! length of a substep (s) - ! adaptive sub-stepping for the explicit solution - logical(lgt) :: failedSubstep ! flag to denote success of substepping for a given split - real(rkind),parameter :: safety=0.85_rkind ! safety factor in adaptive sub-stepping - real(rkind),parameter :: reduceMin=0.1_rkind ! mimimum factor that time step is reduced - real(rkind),parameter :: increaseMax=4.0_rkind ! maximum factor that time step is increased - ! adaptive sub-stepping for the implicit solution - integer(i4b),parameter :: n_inc=5 ! minimum number of iterations to increase time step - integer(i4b),parameter :: n_dec=15 ! maximum number of iterations to decrease time step - real(rkind),parameter :: F_inc = 1.25_rkind ! factor used to increase time step - real(rkind),parameter :: F_dec = 0.90_rkind ! factor used to decrease time step - ! state and flux vectors - real(rkind) :: untappedMelt(nState) ! un-tapped melt energy (J m-3 s-1) - real(rkind) :: stateVecInit(nState) ! initial state vector (mixed units) - real(rkind) :: stateVecTrial(nState) ! trial state vector (mixed units) - real(rkind) :: stateVecPrime(nState) ! trial state vector (mixed units) - type(var_dlength) :: flux_temp ! temporary model fluxes - ! flags - logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt) :: checkMassBalance ! flag to check the mass balance - logical(lgt) :: checkNrgBalance - logical(lgt) :: waterBalanceError ! flag to denote that there is a water balance error - logical(lgt) :: nrgFluxModified ! flag to denote that the energy fluxes were modified - ! energy fluxes - real(rkind) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) - real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) - real(rkind) :: sumSoilCompress - real(rkind),allocatable :: sumLayerCompress(:) - ! --------------------------------------------------------------------------------------- - ! point to variables in the data structures - ! --------------------------------------------------------------------------------------- - globalVars: associate(& - ! number of layers - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! mapping between state vectors and control volumes - ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat ,& ! intent(in): [i4b(:)] list of indices for all active layers (inactive=integerMissing) - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) - ! model state variables (vegetation canopy) - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout): [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout): [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout): [dp] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat & ! intent(inout): [dp(:)] matric potential of liquid water (m) - ) ! end association with variables in the data structures - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - ! Procedure starts here - - ! initialize error control - err=0; message='varSubstepSundials/' - - ! initialize flag for the success of the substepping - failedMinimumStep=.false. - - ! initialize the length of the substep - dtSubstep = dtInit - - ! initalize flag for checking if energy fluxes had been modified - nrgFluxModified = .false. - - ! allocate space for the temporary model flux structure - call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! initialize the model fluxes (some model fluxes are not computed in the iterations) - do iVar=1,size(flux_data%var) - flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) - end do - - ! initialize the total energy fluxes (modified in updateProgSundials) - sumCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) - sumLatHeatCanopyEvap = 0._rkind ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - sumSenHeatCanopy = 0._rkind ! sensible heat flux from the canopy to the canopy air space (W m-2) - sumSoilCompress = 0._rkind ! total soil compression - allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._rkind ! soil compression by layer - - ! define the first flux call in a splitting operation - firstSplitOper = (.not.scalarSolution .or. iStateSplit==1) - - ! initialize subStep - dtSum = 0._rkind ! keep track of the portion of the time step that is completed - nSubsteps = 0 - - ! loop through substeps - ! NOTE: continuous do statement with exit clause - substeps: do - - ! initialize error control - err=0; message='varSubstepSundials/' - - ! ----- - ! * populate state vectors... - ! --------------------------- - - ! initialize state vectors - call popStateVec(& - ! input - nState, & ! intent(in): number of desired state variables - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output - stateVecInit, & ! intent(out): initial model state vector (mixed units) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - ! ----- - ! * iterative solution... - ! ----------------------- - ! solve the system of equations for a given state subset - call systemSolvSundials(& - ! input: model control - dtSubstep, & ! intent(in): time step (s) - nState, & ! intent(in): total number of state variables - firstSubStep, & ! intent(in): flag to denote first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation - scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution - ! input/output: data structures - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): index data - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_temp, & ! intent(inout): model fluxes for a local HRU - bvar_data, & ! intent(in): model variables for the local basin - model_decisions, & ! intent(in): model decisions - stateVecInit, & ! intent(in): initial state vector - ! output: model control - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - stateVecTrial, & ! intent(out): updated state vector - stateVecPrime, & ! intent(out): updated state vector - reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step - tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt - dt_out, & ! intent(out): time step (s) - err,cmessage) ! intent(out): error code and error message - - if(err/=0)then - message=trim(message)//trim(cmessage) - if(err>0) return - endif - - ! set untapped melt energy to zero - untappedMelt(:) = 0._rkind - - ! if too much melt or need to reduce length of the coupled step then return - ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry - if(tooMuchMelt .or. reduceCoupledStep) return - - ! identify failure - failedSubstep = (err<0) - - ! reduce step based on failure - if(failedSubstep)then - err=0; message='varSubstepSundials/' ! recover from failed convergence - dtMultiplier = 0.5_rkind ! system failure: step halving - else - - endif ! switch between failure and success - - ! check if we failed the substep - if(failedSubstep)then - - ! check that the substep is greater than the minimum step - if(dtSubstep*dtMultiplier exit, and either (1) try another solution method; or (2) reduce coupled step - failedMinimumStep=.true. - exit subSteps - - else ! step is still OK - dtSubstep = dtSubstep*dtMultiplier - cycle subSteps - endif ! if step is less than the minimum - - endif ! if failed the substep - - ! ----- - ! * update model fluxes... - ! ------------------------ - - ! NOTE: if we get to here then we are accepting the step - - ! NOTE: we get to here if iterations are successful - if(err/=0)then - message=trim(message)//'expect err=0 if updating fluxes' - return - endif - - ! identify the need to check the mass balance - checkMassBalance = .false. !do not check for Sundials - checkNrgBalance = .false. !do not check for Sundials, also only check if ixHowHeatCap == enthalpyFD - - ! update prognostic variables - call updateProgSundials(dt_out,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control - lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures - waterBalanceError,nrgFluxModified,err,cmessage) ! output: flags and error control - if(err/=0)then - message=trim(message)//trim(cmessage) - if(err>0) return - endif - - ! if water balance error then reduce the length of the coupled step - if(waterBalanceError)then - message=trim(message)//'water balance error' - reduceCoupledStep=.true. - err=-20; return - endif - - if(globalPrintFlag)& - print*, trim(cmessage)//': dt = ', dtSubstep - - ! recover from errors in prognostic update - if(err<0)then - - ! modify step - err=0 ! error recovery - dtSubstep = dtSubstep/2._rkind - - ! check minimum: fail minimum step if there is an error in the update - if(dtSubstep0) then - ! scalar compression - if(.not.scalarSolution .or. iStateSplit==nSoil)& - sumSoilCompress = sumSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression - ! vector compression - do iSoil=1,nSoil - if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& - sumLayerCompress(iSoil) = sumLayerCompress(iSoil) + diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) ! soil compression in layers - end do - endif - - ! print progress - if(globalPrintFlag)& - write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt - - ! increment fluxes - dt_wght = 1._qp !dt_out/dt ! (define weight applied to each splitting operation) - do iVar=1,size(flux_meta) - if(count(fluxMask%var(iVar)%dat)>0) then - - ! ** no domain splitting - if(count(ixLayerActive/=integerMissing)==nLayers)then - flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght - fluxCount%var(iVar)%dat(:) = fluxCount%var(iVar)%dat(:) + 1 - - ! ** domain splitting - else - ixMin=lbound(flux_data%var(iVar)%dat) - ixMax=ubound(flux_data%var(iVar)%dat) - do ixLayer=ixMin(1),ixMax(1) - if(fluxMask%var(iVar)%dat(ixLayer)) then - flux_data%var(iVar)%dat(ixLayer) = flux_data%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght - fluxCount%var(iVar)%dat(ixLayer) = fluxCount%var(iVar)%dat(ixLayer) + 1 - endif - end do - endif ! (domain splitting) - - endif ! (if the flux is desired) - end do ! (loop through fluxes) - - ! increment the number of substeps - nSubsteps = nSubsteps+1 - - ! increment the sub-step legth - dtSum = dtSum + dtSubstep - - ! check that we have completed the sub-step - if(dtSum >= dt-verySmall)then - failedMinimumStep=.false. - exit subSteps - endif - - ! adjust length of the sub-step (make sure that we don't exceed the step) - dtSubstep = min(dt - dtSum, max(dtSubstep*dtMultiplier, dt_min) ) - - end do substeps ! time steps for variable-dependent sub-stepping - - ! save the energy fluxes - flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt_out ! canopy evaporation/condensation (kg m-2 s-1) - flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt_out ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt_out ! sensible heat flux from the canopy to the canopy air space (W m-2) - - ! save the soil compression diagnostics - diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress - do iSoil=1,nSoil - if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& - diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) = sumLayerCompress(iSoil) - end do - deallocate(sumLayerCompress) - - ! end associate statements - end associate globalVars - - ! update error codes - if(failedMinimumStep)then - err=-20 ! negative = recoverable error - message=trim(message)//'failed minimum step' - endif -end subroutine varSubstepSundials - - -! ********************************************************************************************************** -! private subroutine updateProgSundials: update prognostic variables -! ********************************************************************************************************** -subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control - lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures - waterBalanceError,nrgFluxModified,err,message) ! output: flags and error control - USE getVectorz_module,only:varExtract ! extract variables from the state vector - USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables - USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy - implicit none - ! model control - real(rkind) ,intent(in) :: dt ! time step (s) - integer(i4b) ,intent(in) :: nSnow ! number of snow layers - integer(i4b) ,intent(in) :: nSoil ! number of soil layers - integer(i4b) ,intent(in) :: nLayers ! total number of layers - logical(lgt) ,intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature - logical(lgt) ,intent(in) :: computeVegFlux ! flag to compute the vegetation flux - real(rkind) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) - real(rkind) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) - real(rkind) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) - logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance - logical(lgt) ,intent(in) :: checkNrgBalance ! flag to check the energy balance - ! data structures - type(zLookup),intent(in) :: lookup_data ! lookup tables - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! indices for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! flags and error control - logical(lgt) ,intent(out) :: waterBalanceError ! flag to denote that there is a water balance error - logical(lgt) ,intent(out) :: nrgFluxModified ! flag to denote that the energy fluxes were modified - integer(i4b) ,intent(out) :: err ! error code - character(*) ,intent(out) :: message ! error message - ! ================================================================================================================== - ! general - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: ixSubset ! index within the state subset - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixControlIndex ! index within a given domain - real(rkind) :: volMelt ! volumetric melt (kg m-3) - real(rkind),parameter :: verySmall=epsilon(1._rkind)*2._rkind ! a very small number (deal with precision issues) - ! mass balance - real(rkind) :: canopyBalance0,canopyBalance1 ! canopy storage at start/end of time step - real(rkind) :: soilBalance0,soilBalance1 ! soil storage at start/end of time step - real(rkind) :: vertFlux ! change in storage due to vertical fluxes - real(rkind) :: tranSink,baseSink,compSink ! change in storage due to sink terms - real(rkind) :: liqError ! water balance error - real(rkind) :: fluxNet ! net water fluxes (kg m-2 s-1) - real(rkind) :: superflousWat ! superflous water used for evaporation (kg m-2 s-1) - real(rkind) :: superflousNrg ! superflous energy that cannot be used for evaporation (W m-2 [J m-2 s-1]) - character(LEN=256) :: cmessage ! error message of downwind routine - ! trial state variables - real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial vector for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector for liquid water matric potential (m) - real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) - ! diagnostic variables - real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric fraction of ice (-) - ! derivative of state variables - real(rkind) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatPrime ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempPrime ! trial vector for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! trial vector for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! trial vector for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! trial vector for liquid water matric potential (m) - real(rkind) :: scalarAquiferStoragePrime ! trial value for storage of water in the aquifer (m) - ! diagnostic variables - real(rkind) :: scalarCanopyLiqPrime ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector for volumetric fraction of ice (-) - real(rkind) :: scalarCanairEnthalpyTrial ! enthalpy of the canopy air space (J m-3) - real(rkind) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! enthalpy of snow + soil (J m-3) - ! enthalpy derivatives - real(rkind) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature - real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state - real(rkind),dimension(nLayers) :: dEnthalpy_dTk ! derivatives in layer enthalpy w.r.t. temperature - real(rkind),dimension(nLayers) :: dEnthalpy_dWat ! derivatives in layer enthalpy w.r.t. water state - ! ------------------------------------------------------------------------------------------------------------------- - - ! ------------------------------------------------------------------------------------------------------------------- - ! point to flux variables in the data structure - associate(& - ! get indices for mass balance - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in) : [i4b] index of canopy hydrology state variable (mass) - ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the soil domain - ! get indices for the un-tapped melt - ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in) : [i4b(:)] list of indices for all energy states - ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in) : [i4b(:)] indices defining the domain of the state (iname_veg, iname_snow, iname_soil) - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in) : [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in) : [i4b(:)] [state subset] list of indices of the full state vector in the state subset - ! water fluxes - scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in) : [dp] rainfall rate (kg m-2 s-1) - scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(in) : [dp] rain reaches ground without touching the canopy (kg m-2 s-1) - scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(in) : [dp] canopy evaporation/condensation (kg m-2 s-1) - scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(in) : [dp] canopy transpiration (kg m-2 s-1) - scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(in) : [dp] drainage liquid water from vegetation canopy (kg m-2 s-1) - iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(in) : [dp(0:)] vertical liquid water flux at soil layer interfaces (-) - iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(in) : - mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) - mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(in) : [dp(:)] transpiration loss from each soil layer (m s-1) - mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(in) : [dp(:)] baseflow from each soil layer (m s-1) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in) : [dp(:)] change in storage associated with compression of the soil matrix (-) - scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the vegetation canopy (kg m-2 s-1) - scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the snow surface (kg m-2 s-1) - ! energy fluxes - scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ,& ! intent(in) : [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ,& ! intent(in) : [dp] sensible heat flux from the canopy to the canopy air space (W m-2) - ! domain depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in) : [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in) : [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! model state variables (vegetation canopy) - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout) : [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout) : [dp] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout) : [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout) : [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout) : [dp(:)] matric head (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(inout) : [dp(:)] matric potential of liquid water (m) - ! enthalpy - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(inout): [dp(:)] enthalpy of the snow+soil layers (J m-3) - ! derivatives, diagnositic for enthalpy - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) - ! model state variables (aquifer) - scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(inout) : [dp(:)] storage of water in the aquifer (m) - ! error tolerance - absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1) & ! intent(in) : [dp] absolute convergence tolerance for vol frac liq water (-) - ) ! associating flux variables in the data structure - ! ------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='updateProgSundials/' - - ! initialize water balancmLayerVolFracWatTriale error - waterBalanceError=.false. - - ! get storage at the start of the step - canopyBalance0 = merge(scalarCanopyWat, realMissing, computeVegFlux) - soilBalance0 = sum( (mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) - - ! ----- - ! * update states... - ! ------------------ - - ! initialize to state variable from the last update - scalarCanairTempTrial = scalarCanairTemp - scalarCanopyTempTrial = scalarCanopyTemp - scalarCanopyWatTrial = scalarCanopyWat - scalarCanopyLiqTrial = scalarCanopyLiq - scalarCanopyIceTrial = scalarCanopyIce - mLayerTempTrial = mLayerTemp - mLayerVolFracWatTrial = mLayerVolFracWat - mLayerVolFracLiqTrial = mLayerVolFracLiq - mLayerVolFracIceTrial = mLayerVolFracIce - mLayerMatricHeadTrial = mLayerMatricHead - mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq - scalarAquiferStorageTrial = scalarAquiferStorage - - ! extract states from the state vector - call varExtract(& - ! input - stateVecTrial, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(inout): trial value of storage of water in the aquifer (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! initialize to state variable from the last update - ! should all be set to previous values if splits, but for now operator splitting is not hooked up - scalarCanairTempPrime = realMissing - scalarCanopyTempPrime = realMissing - scalarCanopyWatPrime = realMissing - scalarCanopyLiqPrime = realMissing - scalarCanopyIcePrime = realMissing - mLayerTempPrime = realMissing - mLayerVolFracWatPrime = realMissing - mLayerVolFracLiqPrime = realMissing - mLayerVolFracIcePrime = realMissing - mLayerMatricHeadPrime = realMissing - mLayerMatricHeadLiqPrime = realMissing - scalarAquiferStoragePrime = realMissing - - call varExtract(& - ! input - stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy - scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K) - scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): derivative of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K) - mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (-) - mLayerMatricHeadPrime, & ! intent(inout): derivative of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): derivative of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStoragePrime,& ! intent(inout): derivative of storage of water in the aquifer (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! update diagnostic variables - call updateVarsSundials(& - ! input - dt, & - .false., & ! intent(in): logical flag if computing Jacobian for Sundials solver - doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - mLayerVolFracWatTrial, & ! intent(in): use current vector for prev vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): use current vector for prev vector of total water matric potential (m) - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! - mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) - mLayerVolFracIcePrime, & ! - mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! ---- - ! * check energy balance - !------------------------ - ! NOTE: for now, we just compute enthalpy with phase change, should check, and should also mirror in varSubstep non-Sundials - if(checkNrgBalance)then - ! compute enthalpy at t_{n+1} - call t2enthalpy(& - .true., & ! intent(in): logical flag to include phase change in enthalpy - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) - ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - ! output: enthalpy - scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) - dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature - dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state - dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature - dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - endif - - ! ----- - ! * check mass balance... - ! ----------------------- - - ! NOTE: should not need to do this, since mass balance is checked in the solver - if(checkMassBalance)then - - ! check mass balance for the canopy - if(ixVegHyd/=integerMissing)then - - ! handle cases where fluxes empty the canopy - fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage - if(-fluxNet*dt > canopyBalance0)then - - ! --> first add water - canopyBalance1 = canopyBalance0 + (scalarRainfall - scalarThroughfallRain)*dt - - ! --> next, remove canopy evaporation -- put the unsatisfied evap into sensible heat - canopyBalance1 = canopyBalance1 + scalarCanopyEvaporation*dt - if(canopyBalance1 < 0._rkind)then - ! * get superfluous water and energy - superflousWat = -canopyBalance1/dt ! kg m-2 s-1 - superflousNrg = superflousWat*LH_vap ! W m-2 (J m-2 s-1) - ! * update fluxes and states - canopyBalance1 = 0._rkind - scalarCanopyEvaporation = scalarCanopyEvaporation + superflousWat - scalarLatHeatCanopyEvap = scalarLatHeatCanopyEvap + superflousNrg - scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg - endif - - ! --> next, remove canopy drainage - canopyBalance1 = canopyBalance1 - scalarCanopyLiqDrainage*dt - if(canopyBalance1 < 0._rkind)then - superflousWat = -canopyBalance1/dt ! kg m-2 s-1 - canopyBalance1 = 0._rkind - scalarCanopyLiqDrainage = scalarCanopyLiqDrainage + superflousWat - endif - - ! update the trial state - scalarCanopyWatTrial = canopyBalance1 - - ! set the modification flag - nrgFluxModified = .true. - - else - canopyBalance1 = canopyBalance0 + fluxNet*dt - nrgFluxModified = .false. - endif ! cases where fluxes empty the canopy - - ! check the mass balance - fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage - liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial - if(abs(liqError) > absConvTol_liquid*10._rkind*iden_water)then ! *10 because of precision issues - !write(*,'(a,1x,f20.10)') 'dt = ', dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - !write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 - !write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 - !write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt - !write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - !write(*,'(a,1x,f20.10)') 'liqError = ', liqError - waterBalanceError = .true. - return - endif ! if there is a water balance error - endif ! if veg canopy - - ! check mass balance for soil - ! NOTE: fatal errors, though possible to recover using negative error codes - if(count(ixSoilOnlyHyd/=integerMissing)==nSoil)then - soilBalance1 = sum( (mLayerVolFracLiqTrial(nSnow+1:nLayers) + mLayerVolFracIceTrial(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) - vertFlux = -(iLayerLiqFluxSoil(nSoil) - iLayerLiqFluxSoil(0))*dt ! m s-1 --> m - tranSink = sum(mLayerTranspire)*dt ! m s-1 --> m - baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m - compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m - liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) - if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues - !write(*,'(a,1x,f20.10)') 'dt = ', dt - !write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 - !write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 - !write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux - !write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink - !write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink - !write(*,'(a,1x,f20.10)') 'compSink = ', compSink - !write(*,'(a,1x,f20.10)') 'liqError = ', liqError - !write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid - waterBalanceError = .true. - return - endif ! if there is a water balance error - endif ! if hydrology states exist in the soil domain - endif ! if checking the mass balance - - ! ----- - ! * remove untapped melt energy... - ! -------------------------------- - - ! only work with energy state variables - if(size(ixNrgOnly)>0)then ! energy state variables exist - - ! loop through energy state variables - do iState=1,size(ixNrgOnly) - - ! get index of the control volume within the domain - ixSubset = ixNrgOnly(iState) ! index within the state subset - ixFullVector = ixMapSubset2Full(ixSubset) ! index within full state vector - ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain - - ! compute volumetric melt (kg m-3) - volMelt = dt*untappedMelt(ixSubset)/LH_fus ! (kg m-3) - - ! update ice content - select case( ixDomainType(ixFullVector) ) - case(iname_cas); cycle ! do nothing, since there is no snow stored in the canopy air space - case(iname_veg); scalarCanopyIceTrial = scalarCanopyIceTrial - volMelt*canopyDepth ! (kg m-2) - case(iname_snow); mLayerVolFracIceTrial(ixControlIndex) = mLayerVolFracIceTrial(ixControlIndex) - volMelt/iden_ice ! (-) - case(iname_soil); mLayerVolFracIceTrial(ixControlIndex+nSnow) = mLayerVolFracIceTrial(ixControlIndex+nSnow) - volMelt/iden_water ! (-) - case default; err=20; message=trim(message)//'unable to identify domain type [remove untapped melt energy]'; return - end select - - ! update liquid water content - select case( ixDomainType(ixFullVector) ) - case(iname_cas); cycle ! do nothing, since there is no snow stored in the canopy air space - case(iname_veg); scalarCanopyLiqTrial = scalarCanopyLiqTrial + volMelt*canopyDepth ! (kg m-2) - case(iname_snow); mLayerVolFracLiqTrial(ixControlIndex) = mLayerVolFracLiqTrial(ixControlIndex) + volMelt/iden_water ! (-) - case(iname_soil); mLayerVolFracLiqTrial(ixControlIndex+nSnow) = mLayerVolFracLiqTrial(ixControlIndex+nSnow) + volMelt/iden_water ! (-) - case default; err=20; message=trim(message)//'unable to identify domain type [remove untapped melt energy]'; return - end select - - end do ! looping through energy variables - - ! ======================================================================================================== - - ! *** ice - - ! --> check if we removed too much water - if(scalarCanopyIceTrial < 0._rkind .or. any(mLayerVolFracIceTrial < 0._rkind) )then - - ! ** - ! canopy within numerical precision - if(scalarCanopyIceTrial < 0._rkind)then - - if(scalarCanopyIceTrial > -verySmall)then - scalarCanopyLiqTrial = scalarCanopyLiqTrial - scalarCanopyIceTrial - scalarCanopyIceTrial = 0._rkind - - ! encountered an inconsistency: spit the dummy - else - print*, 'dt = ', dt - print*, 'untappedMelt = ', untappedMelt - print*, 'untappedMelt*dt = ', untappedMelt*dt - print*, 'scalarCanopyiceTrial = ', scalarCanopyIceTrial - message=trim(message)//'melted more than the available water' - err=20; return - endif ! (inconsistency) - - endif ! if checking the canopy - ! ** - ! snow+soil within numerical precision - do iState=1,size(mLayerVolFracIceTrial) - - ! snow layer within numerical precision - if(mLayerVolFracIceTrial(iState) < 0._rkind)then - - if(mLayerVolFracIceTrial(iState) > -verySmall)then - mLayerVolFracLiqTrial(iState) = mLayerVolFracLiqTrial(iState) - mLayerVolFracIceTrial(iState) - mLayerVolFracIceTrial(iState) = 0._rkind - - ! encountered an inconsistency: spit the dummy - else - print*, 'dt = ', dt - print*, 'untappedMelt = ', untappedMelt - print*, 'untappedMelt*dt = ', untappedMelt*dt - print*, 'mLayerVolFracIceTrial = ', mLayerVolFracIceTrial - message=trim(message)//'melted more than the available water' - err=20; return - endif ! (inconsistency) - - endif ! if checking a snow layer - - end do ! (looping through state variables) - - endif ! (if we removed too much water) - - ! ======================================================================================================== - - ! *** liquid water - - ! --> check if we removed too much water - if(scalarCanopyLiqTrial < 0._rkind .or. any(mLayerVolFracLiqTrial < 0._rkind) )then - - ! ** - ! canopy within numerical precision - if(scalarCanopyLiqTrial < 0._rkind)then - - if(scalarCanopyLiqTrial > -verySmall)then - scalarCanopyIceTrial = scalarCanopyIceTrial - scalarCanopyLiqTrial - scalarCanopyLiqTrial = 0._rkind - - - ! encountered an inconsistency: spit the dummy - else - print*, 'dt = ', dt - print*, 'untappedMelt = ', untappedMelt - print*, 'untappedMelt*dt = ', untappedMelt*dt - print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial - message=trim(message)//'frozen more than the available water' - err=20; return - endif ! (inconsistency) - endif ! checking the canopy - - ! ** - ! snow+soil within numerical precision - do iState=1,size(mLayerVolFracLiqTrial) - - ! snow layer within numerical precision - if(mLayerVolFracLiqTrial(iState) < 0._rkind)then - - if(mLayerVolFracLiqTrial(iState) > -verySmall)then - mLayerVolFracIceTrial(iState) = mLayerVolFracIceTrial(iState) - mLayerVolFracLiqTrial(iState) - mLayerVolFracLiqTrial(iState) = 0._rkind - - ! encountered an inconsistency: spit the dummy - else - print*, 'dt = ', dt - print*, 'untappedMelt = ', untappedMelt - print*, 'untappedMelt*dt = ', untappedMelt*dt - print*, 'mLayerVolFracLiqTrial = ', mLayerVolFracLiqTrial - message=trim(message)//'frozen more than the available water' - err=20; return - endif ! (inconsistency) - - endif ! checking a snow layer - - end do ! (looping through state variables) - - endif ! (if we removed too much water) - - endif ! (if energy state variables exist) - - ! ----- - ! * update enthalpy as a diagnostic variable... - ! -------------------------------- - scalarCanairEnthalpy = scalarCanairEnthalpyTrial - scalarCanopyEnthalpy = scalarCanopyEnthalpyTrial - mLayerEnthalpy = mLayerEnthalpyTrial - - ! ----- - ! * update prognostic variables... - ! -------------------------------- - ! update state variables for the vegetation canopy - scalarCanairTemp = scalarCanairTempTrial ! trial value of canopy air temperature (K) - scalarCanopyTemp = scalarCanopyTempTrial ! trial value of canopy temperature (K) - scalarCanopyWat = scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - scalarCanopyLiq = scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) - scalarCanopyIce = scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - - ! update state variables for the snow+soil domain - mLayerTemp = mLayerTempTrial ! trial vector of layer temperature (K) - mLayerVolFracWat = mLayerVolFracWatTrial ! trial vector of volumetric total water content (-) - mLayerVolFracLiq = mLayerVolFracLiqTrial ! trial vector of volumetric liquid water content (-) - mLayerVolFracIce = mLayerVolFracIceTrial ! trial vector of volumetric ice water content (-) - mLayerMatricHead = mLayerMatricHeadTrial ! trial vector of matric head (m) - mLayerMatricHeadLiq = mLayerMatricHeadLiqTrial ! trial vector of matric head (m) - - ! update state variables for the aquifer - scalarAquiferStorage = scalarAquiferStorageTrial - - ! end associations to info in the data structures - end associate - -end subroutine updateProgSundials - -end module varSubstepSundials_module diff --git a/build/source/engine/volicePack.f90 b/build/source/engine/volicePack.f90 index 218dfbba0..5006c888c 100644 --- a/build/source/engine/volicePack.f90 +++ b/build/source/engine/volicePack.f90 @@ -97,20 +97,22 @@ subroutine volicePack(& ! initialize error control err=0; message='volicePack/' - ! divide snow layers if too thick - call layerDivide(& - ! input/output: model data structures - model_decisions, & ! intent(in): model decisions - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): type of each layer - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output - divideLayer, & ! intent(out): flag to denote that layers were modified - err,cmessage) ! intent(out): error control - if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if - + ! divide snow layers if too thick, don't do it if need to merge + if (.not.tooMuchMelt)then + call layerDivide(& + ! input/output: model data structures + model_decisions, & ! intent(in): model decisions + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): type of each layer + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output + divideLayer, & ! intent(out): flag to denote that layers were modified + err,cmessage) ! intent(out): error control + if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if + endif + ! print *, 'in coupled_em divideLayer = ', divideLayer ! merge snow layers if they are too thin @@ -127,7 +129,7 @@ subroutine volicePack(& mergedLayers, & ! intent(out): flag to denote that layers were modified err,cmessage) ! intent(out): error control if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if - + ! print *, 'in coupled_em mergeLayers = ', mergedLayers ! update the number of layers @@ -157,10 +159,10 @@ subroutine newsnwfall(& ! input/output: state variables scalarSWE, & ! SWE (kg m-2) scalarSnowDepth, & ! total snow depth (m) - surfaceLayerTemp, & ! temperature of each layer (K) - surfaceLayerDepth, & ! depth of each layer (m) - surfaceLayerVolFracIce, & ! volumetric fraction of ice in each layer (-) - surfaceLayerVolFracLiq, & ! volumetric fraction of liquid water in each layer (-) + surfaceLayerTemp, & ! temperature of surface layer (K) + surfaceLayerDepth, & ! depth of surface layer (m) + surfaceLayerVolFracIce, & ! volumetric fraction of ice in surface layer (-) + surfaceLayerVolFracLiq, & ! volumetric fraction of liquid water in surface layer (-) ! output: error control err,message ) ! error control ! computational modules @@ -179,10 +181,10 @@ subroutine newsnwfall(& ! input/output: state variables real(rkind),intent(inout) :: scalarSWE ! SWE (kg m-2) real(rkind),intent(inout) :: scalarSnowDepth ! total snow depth (m) - real(rkind),intent(inout) :: surfaceLayerTemp ! temperature of each layer (K) + real(rkind),intent(inout) :: surfaceLayerTemp ! temperature of surface layer (K) real(rkind),intent(inout) :: surfaceLayerDepth ! depth of each layer (m) - real(rkind),intent(inout) :: surfaceLayerVolFracIce ! volumetric fraction of ice in each layer (-) - real(rkind),intent(inout) :: surfaceLayerVolFracLiq ! volumetric fraction of liquid water in each layer (-) + real(rkind),intent(inout) :: surfaceLayerVolFracIce ! volumetric fraction of ice in surface layer (-) + real(rkind),intent(inout) :: surfaceLayerVolFracLiq ! volumetric fraction of liquid water in surface layer (-) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message From dc7080373d33c4dc79798f0628b438386ea07729 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 22 Mar 2023 23:42:12 +0900 Subject: [PATCH 0597/1472] update makefiles to be more generic --- build/{build_cmakeBMI_cop => build_cmakeBMI} | 4 +-- build/build_cmakeBMI_mac | 5 --- build/build_cmakeNGen | 5 +++ build/build_cmakeNGen_cop | 9 ----- build/build_cmakeNGen_mac | 5 --- ..._cmakeSundials_cop => build_cmakeSundials} | 4 +-- build/build_cmakeSundials_mac | 8 ----- build/{build_summa_cop => build_summa_sup} | 2 +- build/my_makefile_mac | 2 +- build/{my_makefile_cop => my_makefile_sup} | 4 +-- sundials_bmi/installation.txt | 34 +++++++------------ 11 files changed, 25 insertions(+), 57 deletions(-) rename build/{build_cmakeBMI_cop => build_cmakeBMI} (56%) delete mode 100755 build/build_cmakeBMI_mac create mode 100755 build/build_cmakeNGen delete mode 100755 build/build_cmakeNGen_cop delete mode 100755 build/build_cmakeNGen_mac rename build/{build_cmakeSundials_cop => build_cmakeSundials} (59%) delete mode 100755 build/build_cmakeSundials_mac rename build/{build_summa_cop => build_summa_sup} (81%) rename build/{my_makefile_cop => my_makefile_sup} (99%) diff --git a/build/build_cmakeBMI_cop b/build/build_cmakeBMI similarity index 56% rename from build/build_cmakeBMI_cop rename to build/build_cmakeBMI index d08d728eb..7c859f223 100755 --- a/build/build_cmakeBMI_cop +++ b/build/build_cmakeBMI @@ -1,5 +1,5 @@ # from ../../bmi/builddir, run -# cp ../../summa/build/build_cmakeBMI_cop build_cmake +# cp ../../summa/build/build_cmakeBMI build_cmake # run script from the builddir directory with ./build_cmake -cmake ../../bmi-fortran/ -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/globalhome/gwu479/HPC/SummaSundials/bmi/instdir \ No newline at end of file +cmake ../../bmi-fortran/ -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=../../bmi/instdir \ No newline at end of file diff --git a/build/build_cmakeBMI_mac b/build/build_cmakeBMI_mac deleted file mode 100755 index 1069760ce..000000000 --- a/build/build_cmakeBMI_mac +++ /dev/null @@ -1,5 +0,0 @@ -# from ../../bmi/builddir, run -# cp ../../summa/build/build_cmakeBMI_mac build_cmake -# run script from the builddir directory with ./build_cmake - -cmake ../../bmi-fortran/ -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/Users/amedin/Research/SummaSundials/bmi/instdir \ No newline at end of file diff --git a/build/build_cmakeNGen b/build/build_cmakeNGen new file mode 100755 index 000000000..ffc6932d8 --- /dev/null +++ b/build/build_cmakeNGen @@ -0,0 +1,5 @@ +# from ../../ngen, run +# cp ../../summa/build/build_cmakeNGen build_cmake +# run script from the ngen directory with ./build_cmake + +cmake ../../bmi-fortran/ -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/Users/amedin/Research/SummaSundials/bmi/instdir \ No newline at end of file diff --git a/build/build_cmakeNGen_cop b/build/build_cmakeNGen_cop deleted file mode 100755 index 4877bb748..000000000 --- a/build/build_cmakeNGen_cop +++ /dev/null @@ -1,9 +0,0 @@ -# from ../../ngen/builddir, run -# cp ../../summa/build/build_cmakeBMI_mac build_cmake -# run script from the builddir directory with ./build_cmake - - -cmake -B ngen/ -S . -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ - -cmake -B /ngen -S . && -cmake --build . --target ngen \ No newline at end of file diff --git a/build/build_cmakeNGen_mac b/build/build_cmakeNGen_mac deleted file mode 100755 index 1069760ce..000000000 --- a/build/build_cmakeNGen_mac +++ /dev/null @@ -1,5 +0,0 @@ -# from ../../bmi/builddir, run -# cp ../../summa/build/build_cmakeBMI_mac build_cmake -# run script from the builddir directory with ./build_cmake - -cmake ../../bmi-fortran/ -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/Users/amedin/Research/SummaSundials/bmi/instdir \ No newline at end of file diff --git a/build/build_cmakeSundials_cop b/build/build_cmakeSundials similarity index 59% rename from build/build_cmakeSundials_cop rename to build/build_cmakeSundials index fccfbfb26..e015363c6 100755 --- a/build/build_cmakeSundials_cop +++ b/build/build_cmakeSundials @@ -1,8 +1,8 @@ # from ../../sundials/builddir, run -# cp ../../summa/build/build_cmakeSundials_cop build_cmake +# cp ../../summa/build/build_cmakeSundials build_cmake # run script from the builddir directory with ./build_cmake # Note, using -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF because modified constraint code and did not modify examples -cmake ../../sundials-6.3.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir/examples +cmake ../../sundials-6.3.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=../../sundials/instdir -DEXAMPLES_INSTALL_PATH=../../sundials/instdir/examples diff --git a/build/build_cmakeSundials_mac b/build/build_cmakeSundials_mac deleted file mode 100755 index 83c6fcf75..000000000 --- a/build/build_cmakeSundials_mac +++ /dev/null @@ -1,8 +0,0 @@ -# from ../../sundials/builddir, run -# cp ../../summa/build/build_cmakeSundialsmac_ build_cmake -# run script from the builddir directory with ./build_cmake -# Note, using -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF because modified constraint code and did not modify examples - -cmake ../../sundials-6.3.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/Users/amedin/Research/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/Users/amedin/Research/SummaSundials/sundials/instdir/examples - - diff --git a/build/build_summa_cop b/build/build_summa_sup similarity index 81% rename from build/build_summa_cop rename to build/build_summa_sup index ad69c96b7..073175b00 100755 --- a/build/build_summa_cop +++ b/build/build_summa_sup @@ -3,4 +3,4 @@ module load gcc/9.3.0 module load openblas/0.3.17 module load netcdf-fortran/4.5.2 -make -f my_makefile_cop +make -f my_makefile_sup diff --git a/build/my_makefile_mac b/build/my_makefile_mac index f35600792..c2e8728af 100644 --- a/build/my_makefile_mac +++ b/build/my_makefile_mac @@ -39,7 +39,7 @@ # Define core directory below which everything resides. This is the # parent directory of the 'build' directory -F_MASTER_TOP = /Users/amedin/Research/SummaSundials +F_MASTER_TOP =../../ F_MASTER = $(F_MASTER_TOP)/summa # Define the Fortran Compiler. If you are using gfortran, then this needs diff --git a/build/my_makefile_cop b/build/my_makefile_sup similarity index 99% rename from build/my_makefile_cop rename to build/my_makefile_sup index 2419b0941..cd2884467 100644 --- a/build/my_makefile_cop +++ b/build/my_makefile_sup @@ -19,7 +19,7 @@ # * FC - compiler suite # * FC_EXE - compiler executable # * INCLUDES - path to include files -# * ILDFLAGS - path to libraries to include +# * LDFLAGS - path to libraries to include # * LIBRARIES - libraries to include # # Some further options can be specified for OpenMP, etc. See in Part 0 of @@ -39,7 +39,7 @@ # Define core directory below which everything resides. This is the # parent directory of the 'build' directory -F_MASTER_TOP = /globalhome/gwu479/HPC/SummaSundials +F_MASTER_TOP =../../ F_MASTER = $(F_MASTER_TOP)/summa # Define the Fortran Compiler. If you are using gfortran, then this needs diff --git a/sundials_bmi/installation.txt b/sundials_bmi/installation.txt index c27ce4ecd..135957a53 100644 --- a/sundials_bmi/installation.txt +++ b/sundials_bmi/installation.txt @@ -11,26 +11,18 @@ % cd /buildir NOTE if you need to recompile after a system upgrade, delete the contents of these directories before running cmake in the next step! -3. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, the following setting needs to be enabled while running the cmake inside the builddir, using your home directory as $(YOUR_HOME) -% cmake ../../bmi-fortran/ -DCMAKE_INSTALL_PREFIX=$(YOUR_HOME)/bmi/instdir +3. The install directory needs to be set while running the cmake inside the builddir, using home directory as $(YOUR_HOME) +% cmake ../../bmi-fortran/ -DCMAKE_INSTALL_PREFIX=$(YOUR_HOME)/bmi/instdir --DCMAKE_BUILD_WITH_INSTALL_RPATH=ON -DCMAKE_INSTALL_RPATH=/Users/amedin/Research/SummaSundials/bmi/instdir/lib -DCMAKE_SKIP_BUILD_RPATH=OFF -DCMAKE_BUILD_RPATH=/Users/amedin/Research/SummaSundials/bmi/instdir/lib - - -INSTALL_RPATH to a specific folder and set BUILD_WITH_INSTALL_RPATH to TRUE -You can do this by running from inside buildir, where the star is the system specific file with hardcoded paths: -cp ../../summa/build/build_cmakeBMI_* build_cmake -./build_cmake - -4. The default compiler is gfortan. To change it (it should be the same as the netcdf build and the later summa build), you could also add the following option to cmake with your $(YOUR_GFORTRAN) +4. The default compiler is gfortran. To change it (it should be the same as the netcdf build and the later summa build), you could also add the following option to cmake with your $(YOUR_GFORTRAN) -DCMAKE_Fortran_COMPILER=$(YOUR_GFORTRAN) -5. If the above went well, staying in the buildir directory run +You can do 3 and 4 by running from inside buildir: +cp ../../summa/build/build_cmakeBMI build_cmake +./build_cmake -#!/usr/bin/env bash -# Updates runtime paths for executables on macOS. -install_name_tool -change @rpath/libbmif.2.0.dylib $(YOUR_HOME)/bmi/instdir/lib/libbmif.2.0.dylib +5. If the above went well, staying in the buildir directory run: % make % make install @@ -49,15 +41,16 @@ Make a directory outside the sundials-6.3.0 folder and enter it, and make the in % cd /buildir NOTE if you need to recompile after a system upgrade, delete the contents of these directories before running cmake in the next step! -9. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, the following setting needs to be enabled while running the cmake inside the builddir, using your home directory as $(YOUR_HOME) +9. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, the following setting needs to be enabled, as well as the setting the install directory, while running the cmake inside the builddir, using your home directory as $(YOUR_HOME) % cmake ../sundials-5.8.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON DCMAKE_INSTALL_PREFIX=$(YOUR_HOME)/sundials/instdir -DEXAMPLES_INSTALL_PATH=$(YOUR_HOME)/sundials/instdir/examples -You can do this by running from inside buildir, where the star is the system specific file with hardcoded paths: -cp ../../summa/build/build_cmakeSundials_* build_cmake -./build_cmake 10. The default compiler is gfortan. To change it (it should be the same as the netcdf build and the later summa build), you could also add the following option to cmake with your $(YOUR_GFORTRAN) -DCMAKE_Fortran_COMPILER=$(YOUR_GFORTRAN) +You can do 9 and 10 by running from inside buildir: +cp ../../summa/build/build_cmakeSundials build_cmake +./build_cmake + 11. If the above went well, staying in the buildir directory run % make % make install @@ -78,7 +71,4 @@ or % source build_summa_* if you are using a specific * compiler export LD_LIBRARY_PATH=$(YOUR_HOME)/sundials/instdir/lib64 export LD_LIBRARY_PATH=$(YOUR_HOME)/bmi/instdir/lib64 -This does not work on macOS, sundials libraries should be found as complied but the BMI libraries will need to use the Xlinker commands -LIB_BMI=-L$(DIR_BMI)/lib -lbmif -Xlinker -rpath -Xlinker $(DIR_BMI)/lib - From 0d3371b67404fe0d3f9b5f93f574fa0c6744bff6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 22 Mar 2023 23:43:42 +0900 Subject: [PATCH 0598/1472] update makefiles to be more generic --- ..._cmakeSundials_ric => build_cmakeSundials} | 4 +- build/build_cmakeSundials_cop | 8 - build/build_cmakeSundials_gra | 8 - build/build_cmakeSundials_mac | 8 - build/build_summa_gra | 6 - build/{build_summa_cop => build_summa_sup} | 2 +- build/my_makefile_cop | 412 ------------------ build/my_makefile_mac | 10 +- build/my_makefile_ric | 10 +- build/{my_makefile_gra => my_makefile_sup} | 10 +- sundials/installation.txt | 13 +- 11 files changed, 25 insertions(+), 466 deletions(-) rename build/{build_cmakeSundials_ric => build_cmakeSundials} (61%) delete mode 100755 build/build_cmakeSundials_cop delete mode 100755 build/build_cmakeSundials_gra delete mode 100755 build/build_cmakeSundials_mac delete mode 100755 build/build_summa_gra rename build/{build_summa_cop => build_summa_sup} (81%) delete mode 100644 build/my_makefile_cop rename build/{my_makefile_gra => my_makefile_sup} (97%) diff --git a/build/build_cmakeSundials_ric b/build/build_cmakeSundials similarity index 61% rename from build/build_cmakeSundials_ric rename to build/build_cmakeSundials index a563ce505..e015363c6 100755 --- a/build/build_cmakeSundials_ric +++ b/build/build_cmakeSundials @@ -1,8 +1,8 @@ # from ../../sundials/builddir, run -# cp ../../summa/build/build_cmakeSundials_ric build_cmake +# cp ../../summa/build/build_cmakeSundials build_cmake # run script from the builddir directory with ./build_cmake # Note, using -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF because modified constraint code and did not modify examples -cmake ../../sundials-6.3.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/u1/gwu479/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/u1/gwu479/SummaSundials/sundials/instdir/examples +cmake ../../sundials-6.3.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=../../sundials/instdir -DEXAMPLES_INSTALL_PATH=../../sundials/instdir/examples diff --git a/build/build_cmakeSundials_cop b/build/build_cmakeSundials_cop deleted file mode 100755 index fccfbfb26..000000000 --- a/build/build_cmakeSundials_cop +++ /dev/null @@ -1,8 +0,0 @@ -# from ../../sundials/builddir, run -# cp ../../summa/build/build_cmakeSundials_cop build_cmake -# run script from the builddir directory with ./build_cmake -# Note, using -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF because modified constraint code and did not modify examples - -cmake ../../sundials-6.3.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir/examples - - diff --git a/build/build_cmakeSundials_gra b/build/build_cmakeSundials_gra deleted file mode 100755 index af7e35adb..000000000 --- a/build/build_cmakeSundials_gra +++ /dev/null @@ -1,8 +0,0 @@ -# from ../../sundials/builddir, run -# cp ../../summa/build/build_cmakeSundials_gra build_cmake -# run script from the builddir directory with ./build_cmake -# Note, using -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF because modified constraint code and did not modify examples - -cmake ../../sundials-6.3.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/home/avanb/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/home/avanb/SummaSundials/sundials/instdir/examples - - diff --git a/build/build_cmakeSundials_mac b/build/build_cmakeSundials_mac deleted file mode 100755 index 83c6fcf75..000000000 --- a/build/build_cmakeSundials_mac +++ /dev/null @@ -1,8 +0,0 @@ -# from ../../sundials/builddir, run -# cp ../../summa/build/build_cmakeSundialsmac_ build_cmake -# run script from the builddir directory with ./build_cmake -# Note, using -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF because modified constraint code and did not modify examples - -cmake ../../sundials-6.3.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/Users/amedin/Research/SummaSundials/sundials/instdir -DEXAMPLES_INSTALL_PATH=/Users/amedin/Research/SummaSundials/sundials/instdir/examples - - diff --git a/build/build_summa_gra b/build/build_summa_gra deleted file mode 100755 index c428dfdd5..000000000 --- a/build/build_summa_gra +++ /dev/null @@ -1,6 +0,0 @@ -module load StdEnv/2020 -module load gcc/9.3.0 -module load openblas/0.3.17 -module load netcdf-fortran/4.5.2 - -make -f my_makefile_gra diff --git a/build/build_summa_cop b/build/build_summa_sup similarity index 81% rename from build/build_summa_cop rename to build/build_summa_sup index ad69c96b7..073175b00 100755 --- a/build/build_summa_cop +++ b/build/build_summa_sup @@ -3,4 +3,4 @@ module load gcc/9.3.0 module load openblas/0.3.17 module load netcdf-fortran/4.5.2 -make -f my_makefile_cop +make -f my_makefile_sup diff --git a/build/my_makefile_cop b/build/my_makefile_cop deleted file mode 100644 index 52eb7e2a1..000000000 --- a/build/my_makefile_cop +++ /dev/null @@ -1,412 +0,0 @@ -#======================================================================== -# Makefile to compile SUMMA SUNDIALS -#======================================================================== -# -# Recommended use: Copy this file to Makefile.local, edit it to your -# heart's content, and then run `make -f build/Makefile.local` from -# your top level SUMMA directory. Don't include the Makefile.local in -# any pull requests you make. -# -# Note that Makefile configurations that we commonly use can be found on -# the SUMMA wiki at: -# https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration -# feel free to add yours to that page. -# -# To troubleshoot your paths and setup, type 'make check' -# -# At a minimum you will need to set the following: -# * F_MASTER - top level summa directory -# * FC - compiler suite -# * FC_EXE - compiler executable -# * INCLUDES - path to include files -# * ILDFLAGS - path to libraries to include -# * LIBRARIES - libraries to include -# * DIR_SUNDIALS - installation sundials directory -# -# Some further options can be specified for OpenMP, etc. See in Part 0 of -# the Makefile. You don't need to make any changes in PART 1 and -# following unless you are doing SUMMA development and changed what -# needs to be compiled - -#======================================================================== -# PART 0: User-configurable part -#======================================================================== - -# The variables can be specified in one of two ways: -# * delete the '##' in front of the variable, fill out the entry, -# save the file and run make -# * make no changes to this file, but specify the variables in your -# environment before you run make - -# Define core directory below which everything resides. This is the -# parent directory of the 'build' directory -F_MASTER =/globalhome/gwu479/HPC/SummaSundials/summa - -# Define the Fortran Compiler. If you are using gfortran, then this needs -# to be version 6 or higher. This variable is simply used to select the right -# compiler flags in the ifeq statements in this Makefile. The compiler -# executable is set separately as FC_EXE -# Currently this is either gfortran or ifort -FC = gfortran - -# Define the path for the compiler executable. This is the actual executable -# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin/gfortran-mp-11 -# FC and FC_EXE have to be consistent -FC_EXE = gfortran - -# Define the NetCDF and LAPACK libraries and path to include files. -# INCLUDES needs to be of the form (no quotes around the string): -# INCLUDES = -I -I -I<...> -I -# LIBRARIES needs to be of the form ( no quotes around the string): -# LIBRARIES = '-L -lnetcdff -L -lblas -L -l' -# If none of this makes sense, please talk to your system -# administrator. -INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include -LDFLAGS = -L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib -LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas - -DIR_SUNDIALS=/globalhome/gwu479/HPC/SummaSundials/sundials/instdir -INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran -LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod - -# Eventually we plan move to a real configure script, but for now we like -# to keep track of successful compilations of SUMMA on different platforms -# and with different compilers. If you are successful compiling SUMMA, -# please add your configuration (operating system and compiler plus -# part 0 of the Makefile) to the SUMMA wiki on github. - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# ------------ define compiler flags ---------------------------------------- - -# define open MP flags -isOpenMP = -FLAGS_OMP = -LIBOPENMP = - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# gfortran compiler flags -ifeq "$(FC)" "gfortran" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -fopenmp - endif - -# Production runs -FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) - -# Debug runs -#FLAGS_NOAH = -p -g -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -#FLAGS_COMM = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -#FLAGS_SUMMA = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds - -endif - -# ifort compiler flags -ifeq "$(FC)" "ifort" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -qopenmp - endif - -# Production runs -FLAGS_NOAH = -O3 -noerror_limit -FR -auto -fltconsistency $(FLAGS_OMP) -FLAGS_COMM = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) - -# Debug runs -#FLAGS_NOAH = -O0 -p -g -warn nounused -noerror_limit -FR -auto -WB -traceback -fltconsistency -#FLAGS_COMM = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -#FLAGS_SUMMA = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -endif - -#======================================================================== -# PART 1: Define directory paths -#======================================================================== - -# Core directory that contains source code -F_KORE_DIR = $(F_MASTER)/build/source - -# Location of the compiled modules -MOD_PATH = $(F_MASTER)/build - -# Define the directory for the executables -EXE_PATH = $(F_MASTER)/bin - -#======================================================================== -# PART 2: Assemble all of the SUMMA sub-routines -#======================================================================== - -# Define directories -DRIVER_DIR = $(F_KORE_DIR)/driver -HOOKUP_DIR = $(F_KORE_DIR)/hookup -NETCDF_DIR = $(F_KORE_DIR)/netcdf -DSHARE_DIR = $(F_KORE_DIR)/dshare -NUMREC_DIR = $(F_KORE_DIR)/numrec -NOAHMP_DIR = $(F_KORE_DIR)/noah-mp -ENGINE_DIR = $(F_KORE_DIR)/engine - -# utilities -SUMMA_NRUTIL= \ - nrtype.f90 \ - f2008funcs.f90 \ - nr_utility.f90 -NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) - -# -# Numerical recipes procedures -# NOTE: all numerical recipes procedures are now replaced with free versions -SUMMA_NRPROC= \ - expIntegral.f90 \ - spline_int.f90 -NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) - -# Hook-up modules (set files and directory paths) -SUMMA_HOOKUP= \ - ascii_util.f90 \ - summaFileManager.f90 - -HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) - -# Data modules -SUMMA_DATAMS= \ - multiconst.f90 \ - var_lookup.f90 \ - data_types.f90 \ - globalData.f90 \ - flxMapping.f90 \ - get_ixname.f90 \ - popMetadat.f90 \ - outpt_stat.f90 -DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) - -# utility modules -SUMMA_UTILMS= \ - time_utils.f90 \ - mDecisions.f90 \ - snow_utils.f90 \ - soil_utils.f90 \ - soil_utilsAddSundials.f90 \ - updatState.f90 \ - updatStateSundials.f90 \ - matrixOper.f90 -UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) - -# Model guts -SUMMA_MODGUT= \ - MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) - -# Solver -SUMMA_SOLVER= \ - vegPhenlgy.f90 \ - diagn_evar.f90 \ - stomResist.f90 \ - groundwatr.f90 \ - vegSWavRad.f90 \ - vegNrgFlux.f90 \ - ssdNrgFlux.f90 \ - vegLiqFlux.f90 \ - snowLiqFlx.f90 \ - soilLiqFlx.f90 \ - bigAquifer.f90 \ - computFlux.f90 \ - type4IDA.f90 \ - tol4IDA.f90 \ - computEnthalpy.f90 \ - computHeatCap.f90 \ - computThermConduct.f90 \ - computResid.f90 \ - computJacob.f90 \ - eval8summa.f90 \ - summaSolve.f90 \ - systemSolv.f90 \ - computResidSundials.f90 \ - eval8summaSundials.f90 \ - computJacobSundials.f90 \ - computSnowDepth.f90 \ - summaSolveSundialsIDA.f90 \ - systemSolvSundials.f90 \ - varSubstep.f90 \ - opSplittin.f90 \ - coupled_em.f90 \ - run_oneHRU.f90 \ - run_oneGRU.f90 -SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) - -# Define routines for SUMMA preliminaries -SUMMA_PRELIM= \ - conv_funcs.f90 \ - sunGeomtry.f90 \ - convE2Temp.f90 \ - allocspace.f90 \ - checkStruc.f90 \ - childStruc.f90 \ - ffile_info.f90 \ - read_attrb.f90 \ - read_pinit.f90 \ - pOverwrite.f90 \ - read_param.f90 \ - paramCheck.f90 \ - check_icond.f90 -PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) - -SUMMA_NOAHMP= \ - module_model_constants.F \ - module_sf_noahutl.F \ - module_sf_noahlsm.F \ - module_sf_noahmplsm.F -NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) - -# Define routines for the SUMMA model runs -SUMMA_MODRUN = \ - indexState.f90 \ - getVectorz.f90 \ - t2enthalpy.f90 \ - updateVars.f90 \ - updateVarsSundials.f90 \ - var_derive.f90 \ - read_force.f90 \ - derivforce.f90 \ - snowAlbedo.f90 \ - canopySnow.f90 \ - tempAdjust.f90 \ - snwCompact.f90 \ - layerMerge.f90 \ - layerDivide.f90 \ - volicePack.f90 \ - qTimeDelay.f90 -MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) - -# Define routines for the solver -SUMMA_MSOLVE = \ - -# Define NetCDF routines -SUMMA_NETCDF = \ - netcdf_util.f90 \ - def_output.f90 \ - modelwrite.f90 \ - read_icond.f90 -NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) - -# ... stitch together common programs -COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) - -# ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) - -# Define the driver routine -SUMMA_DRIVER= \ - summa_type.f90 \ - summa_util.f90 \ - summa_alarms.f90 \ - summa_globalData.f90 \ - summa_defineOutput.f90 \ - summa_init.f90 \ - summa_setup.f90 \ - summa_restart.f90 \ - summa_forcing.f90 \ - summa_modelRun.f90 \ - summa_writeOutput.f90 \ - summa_driver.f90 -DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) - -# Define the executable -DRIVER__EX = summa_sundials.exe - -# Define version number -VERSIONFILE = $(DRIVER_DIR)/summaversion.inc -VERSION = $(shell git tag | tail -n 1) -BULTTIM = $(shell date) -GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') -GITHASH = $(shell git rev-parse HEAD) - -#======================================================================== -# PART 3: Checks -#====================================================================== -# make sure that the paths are defined. These are just some high level checks -ifndef F_MASTER - $(error F_MASTER is undefined) -endif -ifndef FC - $(error FC is undefined: Specify your compiler) -endif -ifndef FC_EXE - $(error FC_EXE is undefined: Specify your compiler executable) -endif -ifndef FLAGS_SUMMA - $(error Specify flags for your compiler: $(FC)) -endif -ifndef INCLUDES - $(error INCLUDES is undefined) -endif -ifndef LIBRARIES - $(error LIBRARIES is undefined) -endif - -#======================================================================== -# PART 4: compilation -#====================================================================== - -# Compile -all: compile_noah compile_comm compile_summa link clean install -part: compile_noah compile_comm compile_summa - -check: - $(info) - $(info Displaying make variables:) - $(info F_MASTER : $(F_MASTER)) - $(info EXE_PATH : $(EXE_PATH)) - $(info FC : $(FC)) - $(info INCLUDES : $(INCLUDES)) - $(info LIBRARIES : $(LIBRARIES)) - $(info FLAGS_NOAH : $(FLAGS_NOAH)) - $(info FLAGS_COMM : $(FLAGS_COMM)) - $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) - $(info SUNDIALSDIR: $(DIR_SUNDIALS)) - $(info INC_SUNDIALS: $(INC_SUNDIALS)) - $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) - $(info) - -# update version information -update_version: - echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) - echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) - -# compile Noah-MP routines -compile_noah: - $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) - -# compile common routines - -compile_comm: - $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) - -# compile SUMMA routines -compile_summa: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ - $(INCLUDES) $(INC_SUNDIALS) - -# link routines -link: - $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) - -# Remove object files -clean: - rm -f *.o - rm -f *.mod - rm -f soil_veg_gen_parm__genmod.f90 - -# Copy the executable to the bin directory -install: - @mkdir -p $(EXE_PATH) - @mv $(DRIVER__EX) $(EXE_PATH) - $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) diff --git a/build/my_makefile_mac b/build/my_makefile_mac index ee33bee1f..80be18085 100644 --- a/build/my_makefile_mac +++ b/build/my_makefile_mac @@ -15,13 +15,12 @@ # To troubleshoot your paths and setup, type 'make check' # # At a minimum you will need to set the following: -# * F_MASTER - top level summa directory +# * F_MASTER_TOP - top level directory # * FC - compiler suite # * FC_EXE - compiler executable # * INCLUDES - path to include files -# * LDFLAGS - path to libraries to include +# * LDFLAGS - path to libraries to include # * LIBRARIES - libraries to include -# * DIR_SUNDIALS - installation sundials directory # # Some further options can be specified for OpenMP, etc. See in Part 0 of # the Makefile. You don't need to make any changes in PART 1 and @@ -40,7 +39,8 @@ # Define core directory below which everything resides. This is the # parent directory of the 'build' directory -F_MASTER = /Users/amedin/Research/SummaSundials/summa +F_MASTER_TOP =../../ +F_MASTER = $(F_MASTER_TOP)/summa # Define the Fortran Compiler. If you are using gfortran, then this needs # to be version 6 or higher. This variable is simply used to select the right @@ -65,7 +65,7 @@ INCLUDES = -I/opt/local/include -I/opt/local/lib LDFLAGS = -L/opt/local/lib LIBRARIES = $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf -DIR_SUNDIALS=/Users/amedin/Research/SummaSundials/sundials/instdir +DIR_SUNDIALS= $(F_MASTER_TOP)/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod diff --git a/build/my_makefile_ric b/build/my_makefile_ric index e9265a43d..0e373d4c3 100644 --- a/build/my_makefile_ric +++ b/build/my_makefile_ric @@ -15,13 +15,12 @@ # To troubleshoot your paths and setup, type 'make check' # # At a minimum you will need to set the following: -# * F_MASTER - top level summa directory +# * F_MASTER_TOP - top level directory # * FC - compiler suite # * FC_EXE - compiler executable # * INCLUDES - path to include files -# * ILDFLAGS - path to libraries to include +# * LDFLAGS - path to libraries to include # * LIBRARIES - libraries to include -# * DIR_SUNDIALS - installation sundials directory # # Some further options can be specified for OpenMP, etc. See in Part 0 of # the Makefile. You don't need to make any changes in PART 1 and @@ -40,7 +39,8 @@ # Define core directory below which everything resides. This is the # parent directory of the 'build' directory -F_MASTER =/u1/gwu479/SummaSundials/summa +F_MASTER_TOP =../../ +F_MASTER = $(F_MASTER_TOP)/summa # Define the Fortran Compiler. If you are using gfortran, then this needs # to be version 6 or higher. This variable is simply used to select the right @@ -65,7 +65,7 @@ INCLUDES = -I/usr/include -I/usr/local/include LDFLAGS = -L/usr/local/lib LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas -lnetcdf -DIR_SUNDIALS=/u1/gwu479/SummaSundials/sundials/instdir +DIR_SUNDIALS= $(F_MASTER_TOP)/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod diff --git a/build/my_makefile_gra b/build/my_makefile_sup similarity index 97% rename from build/my_makefile_gra rename to build/my_makefile_sup index feb5ebecc..02d4be047 100644 --- a/build/my_makefile_gra +++ b/build/my_makefile_sup @@ -15,13 +15,12 @@ # To troubleshoot your paths and setup, type 'make check' # # At a minimum you will need to set the following: -# * F_MASTER - top level summa directory +# * F_MASTER_TOP - top level directory # * FC - compiler suite # * FC_EXE - compiler executable # * INCLUDES - path to include files -# * ILDFLAGS - path to libraries to include +# * LDFLAGS - path to libraries to include # * LIBRARIES - libraries to include -# * DIR_SUNDIALS - installation sundials directory # # Some further options can be specified for OpenMP, etc. See in Part 0 of # the Makefile. You don't need to make any changes in PART 1 and @@ -40,7 +39,8 @@ # Define core directory below which everything resides. This is the # parent directory of the 'build' directory -F_MASTER =/home/avanb/SummaSundials/summa +F_MASTER_TOP =.. +F_MASTER = $(F_MASTER_TOP)/summa # Define the Fortran Compiler. If you are using gfortran, then this needs # to be version 6 or higher. This variable is simply used to select the right @@ -65,7 +65,7 @@ INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include LDFLAGS = -L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas -DIR_SUNDIALS=/home/avanb/SummaSundials/sundials/instdir +DIR_SUNDIALS=../../sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod diff --git a/sundials/installation.txt b/sundials/installation.txt index 89188b73c..ba0ddc20a 100644 --- a/sundials/installation.txt +++ b/sundials/installation.txt @@ -14,16 +14,17 @@ Make a directory outside the sundials-6.3.0 folder and enter it, and make the in % cd /buildir NOTE if you need to recompile after a system upgrade, delete the contents of these directories before running cmake in the next step! -4. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, the following setting needs to be enabled while running the cmake inside the builddir, using your home directory as $(YOUR_HOME) +4. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, the following setting needs to be enabled, as well as the setting the install directory, while running the cmake inside the builddir, using your home directory as $(YOUR_HOME) % cmake ../sundials-5.8.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON DCMAKE_INSTALL_PREFIX=$(YOUR_HOME)/sundials/instdir -DEXAMPLES_INSTALL_PATH=$(YOUR_HOME)/sundials/instdir/examples -You can do this by running from inside buildir, where the star is the system specific file with hardcoded paths: -cp ../../summa/build/build_cmakeSundials_* build_cmake -./build_cmake - -5. The default compiler is gfortan. To change it (it should be the same as the netcdf build and the later summa build), you could also add the following option to cmake with your $(YOUR_GFORTRAN) +5. The default compiler is gfortran. To change it (it should be the same as the netcdf build and the later summa build), you could also add the following option to cmake with your $(YOUR_GFORTRAN) -DCMAKE_Fortran_COMPILER=$(YOUR_GFORTRAN) +You can do 4 and 5 by running from inside buildir: +cp ../../summa/build/build_cmakeSundials build_cmake +./build_cmake + + 6. If the above went well, staying in the buildir directory run % make % make install From f1640b8cbeee990a3cfd5bec03b0ea83066c09f1 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 22 Mar 2023 23:51:40 +0900 Subject: [PATCH 0599/1472] update makefiles --- build/my_makefile_mac | 2 +- build/my_makefile_ric | 2 +- build/my_makefile_sup | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/my_makefile_mac b/build/my_makefile_mac index 80be18085..90e98d189 100644 --- a/build/my_makefile_mac +++ b/build/my_makefile_mac @@ -39,7 +39,7 @@ # Define core directory below which everything resides. This is the # parent directory of the 'build' directory -F_MASTER_TOP =../../ +F_MASTER_TOP =../.. F_MASTER = $(F_MASTER_TOP)/summa # Define the Fortran Compiler. If you are using gfortran, then this needs diff --git a/build/my_makefile_ric b/build/my_makefile_ric index 0e373d4c3..5ea5827ca 100644 --- a/build/my_makefile_ric +++ b/build/my_makefile_ric @@ -39,7 +39,7 @@ # Define core directory below which everything resides. This is the # parent directory of the 'build' directory -F_MASTER_TOP =../../ +F_MASTER_TOP =../.. F_MASTER = $(F_MASTER_TOP)/summa # Define the Fortran Compiler. If you are using gfortran, then this needs diff --git a/build/my_makefile_sup b/build/my_makefile_sup index 02d4be047..7a84b6020 100644 --- a/build/my_makefile_sup +++ b/build/my_makefile_sup @@ -39,7 +39,7 @@ # Define core directory below which everything resides. This is the # parent directory of the 'build' directory -F_MASTER_TOP =.. +F_MASTER_TOP =../.. F_MASTER = $(F_MASTER_TOP)/summa # Define the Fortran Compiler. If you are using gfortran, then this needs @@ -65,7 +65,7 @@ INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include LDFLAGS = -L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas -DIR_SUNDIALS=../../sundials/instdir +DIR_SUNDIALS= $(F_MASTER_TOP)/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod From 03ee28c8b8cb59c9171f53ae786a99ca6997aa43 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 22 Mar 2023 23:51:54 +0900 Subject: [PATCH 0600/1472] update makefiles --- build/my_makefile_mac | 2 +- build/my_makefile_sup | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/my_makefile_mac b/build/my_makefile_mac index c2e8728af..cec9cd917 100644 --- a/build/my_makefile_mac +++ b/build/my_makefile_mac @@ -39,7 +39,7 @@ # Define core directory below which everything resides. This is the # parent directory of the 'build' directory -F_MASTER_TOP =../../ +F_MASTER_TOP =../.. F_MASTER = $(F_MASTER_TOP)/summa # Define the Fortran Compiler. If you are using gfortran, then this needs diff --git a/build/my_makefile_sup b/build/my_makefile_sup index cd2884467..c72ea61de 100644 --- a/build/my_makefile_sup +++ b/build/my_makefile_sup @@ -39,7 +39,7 @@ # Define core directory below which everything resides. This is the # parent directory of the 'build' directory -F_MASTER_TOP =../../ +F_MASTER_TOP =../.. F_MASTER = $(F_MASTER_TOP)/summa # Define the Fortran Compiler. If you are using gfortran, then this needs From 490f4ea7c05272b55cdb8c9bde5012b8cf4d0fdb Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 23 Mar 2023 09:34:08 +0900 Subject: [PATCH 0601/1472] setting up like Kyle's for cmake --- build/build_summa_mac | 1 - build/build_summa_ric | 1 - build/build_summa_sup | 6 - build/cmake/ida.cmake | 296 +++++++++++++ build/cmake/load_modules.sh | 7 + build/{ => makefiles}/build_cmakeSundials | 0 build/makefiles/compile_copernicus | 17 + build/makefiles/compile_graham | 17 + build/makefiles/compile_mac | 11 + build/makefiles/compile_richardson | 11 + .../makefile_cluster} | 133 +----- .../makefile_mac} | 133 +----- build/my_makefile_ric | 412 ------------------ 13 files changed, 391 insertions(+), 654 deletions(-) delete mode 100755 build/build_summa_mac delete mode 100755 build/build_summa_ric delete mode 100755 build/build_summa_sup create mode 100644 build/cmake/ida.cmake create mode 100755 build/cmake/load_modules.sh rename build/{ => makefiles}/build_cmakeSundials (100%) create mode 100755 build/makefiles/compile_copernicus create mode 100755 build/makefiles/compile_graham create mode 100755 build/makefiles/compile_mac create mode 100755 build/makefiles/compile_richardson rename build/{my_makefile_sup => makefiles/makefile_cluster} (61%) rename build/{my_makefile_mac => makefiles/makefile_mac} (61%) delete mode 100644 build/my_makefile_ric diff --git a/build/build_summa_mac b/build/build_summa_mac deleted file mode 100755 index cebfebe94..000000000 --- a/build/build_summa_mac +++ /dev/null @@ -1 +0,0 @@ -make -f my_makefile_mac diff --git a/build/build_summa_ric b/build/build_summa_ric deleted file mode 100755 index b37eca78e..000000000 --- a/build/build_summa_ric +++ /dev/null @@ -1 +0,0 @@ -make -f my_makefile_ric diff --git a/build/build_summa_sup b/build/build_summa_sup deleted file mode 100755 index 073175b00..000000000 --- a/build/build_summa_sup +++ /dev/null @@ -1,6 +0,0 @@ -module load StdEnv/2020 -module load gcc/9.3.0 -module load openblas/0.3.17 -module load netcdf-fortran/4.5.2 - -make -f my_makefile_sup diff --git a/build/cmake/ida.cmake b/build/cmake/ida.cmake new file mode 100644 index 000000000..fd8747c88 --- /dev/null +++ b/build/cmake/ida.cmake @@ -0,0 +1,296 @@ +function(compile_with_ida PARENT_DIR, DIR_SUNDIALS) + find_package(LAPACK REQUIRED) + set(EXEC_NAME summa_sundials) + + link_directories(${DIR_SUNDIALS}/lib64) + set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib64") + set(SUMMA_INCLUDES + "$ENV{EBROOTNETCDFMINFORTRAN}/include" + "${DIR_SUNDIALS}/include" + "${DIR_SUNDIALS}/fortran" + ${netCDF_INCLUDES} + ${LAPACK_INCLUDES}) + + set(SUMMA_LIBS + -lsundials_fnvecmanyvector_mod + -lsundials_fida_mod + -lsundials_fnvecserial_mod + -lsundials_fsunlinsoldense_mod + -lsundials_fsunmatrixdense_mod + -lnetcdff + -lopenblas + ${netCDF_LIBRARIES} + ${LAPACK_LIBRARIES} + SUMMA_NOAHMP) + + set(SUMMA_ACTORS_INCLUDES + ${CAF_INCLUDES} + "$ENV{EBROOTNETCDFMINFORTRAN}/include" + ${LAPACK_INCLUDES} + "${DIR_SUNDIALS}/include" + "${PARENT_DIR}/build/includes/global" + "${PARENT_DIR}/build/includes/summa_actor" + "${PARENT_DIR}/build/includes/gru_actor" + "${PARENT_DIR}/build/includes/job_actor" + "${PARENT_DIR}/build/includes/file_access_actor" + "${PARENT_DIR}/build/includes/hru_actor") + + set(SUMMA_ACTORS_LIBS + ${CAF_LIBRARIES} + ${netCDF_LIBRARIES} + ${LAPACK_LIBRARIES} + -lopenblas + -lcaf_core + -lcaf_io + summa + -lnetcdff + -lsundials_fnvecmanyvector_mod + -lsundials_fida_mod + -lsundials_fnvecserial_mod + -lsundials_fsunlinsoldense_mod + -lsundials_fsunmatrixdense_mod) + + + set(ACTORS_DIR ${PARENT_DIR}/build/source/actors) + set(DRIVER_DIR ${PARENT_DIR}/build/source/driver) + set(DSHARE_DIR ${PARENT_DIR}/build/source/dshare) + set(ENGINE_DIR ${PARENT_DIR}/build/source/engine) + set(HOOKUP_DIR ${PARENT_DIR}/build/source/hookup) + set(NETCDF_DIR ${PARENT_DIR}/build/source/netcdf) + set(NOAHMP_DIR ${PARENT_DIR}/build/source/noah-mp) + set(FILE_ACCESS_DIR ${ACTORS_DIR}/file_access_actor) + set(JOB_ACTOR_DIR ${ACTORS_DIR}/job_actor) + set(HRU_ACTOR_DIR ${ACTORS_DIR}/hru_actor) + set(GRU_ACTOR_DIR ${ACTORS_DIR}/gru_actor) + set(SUMMA_DSHARE_DIR ${PARENT_DIR}/build/summa-sundials/build/source/dshare) + set(SUMMA_ENGINE_DIR ${PARENT_DIR}/build/summa-sundials/build/source/engine) + set(SUMMA_NOAHMP_DIR ${PARENT_DIR}/build/summa-sundials/build/source/noah-mp) + + set(NRUTIL + ${SUMMA_ENGINE_DIR}/nrtype.f90 + ${SUMMA_ENGINE_DIR}/f2008funcs.f90 + ${SUMMA_ENGINE_DIR}/nr_utility.f90) + + set(NRPROC + ${SUMMA_ENGINE_DIR}/expIntegral.f90 + ${SUMMA_ENGINE_DIR}/spline_int.f90) + + SET(HOOKUP + ${HOOKUP_DIR}/ascii_util.f90 + ${HOOKUP_DIR}/summaActors_FileManager.f90) + + SET(DATAMS + ${SUMMA_DSHARE_DIR}/multiconst.f90 + ${SUMMA_DSHARE_DIR}/var_lookup.f90 + ${DSHARE_DIR}/data_types.f90 + ${DSHARE_DIR}/globalData.f90 + ${SUMMA_DSHARE_DIR}/flxMapping.f90) + + SET(DEPENDS_ON_FILEMANAGER + ${SUMMA_DSHARE_DIR}/get_ixname.f90 + ${SUMMA_DSHARE_DIR}/popMetadat.f90 + ${SUMMA_DSHARE_DIR}/outpt_stat.f90) + + SET(UTILMS + ${SUMMA_ENGINE_DIR}/time_utils.f90 + ${ENGINE_DIR}/sundials/mDecisions.f90 + ${SUMMA_ENGINE_DIR}/snow_utils.f90 + ${SUMMA_ENGINE_DIR}/soil_utils.f90 + ${SUMMA_ENGINE_DIR}/soil_utilsAddSundials.f90 + ${SUMMA_ENGINE_DIR}/updatState.f90 + ${SUMMA_ENGINE_DIR}/updatStateSundials.f90 + ${SUMMA_ENGINE_DIR}/matrixOper.f90) + + set(SOLVER + ${ENGINE_DIR}/vegPhenlgy.f90 + ${SUMMA_ENGINE_DIR}/diagn_evar.f90 + ${SUMMA_ENGINE_DIR}/stomResist.f90 + ${SUMMA_ENGINE_DIR}/groundwatr.f90 + ${SUMMA_ENGINE_DIR}/vegSWavRad.f90 + ${SUMMA_ENGINE_DIR}/vegNrgFlux.f90 + ${SUMMA_ENGINE_DIR}/ssdNrgFlux.f90 + ${SUMMA_ENGINE_DIR}/vegLiqFlux.f90 + ${SUMMA_ENGINE_DIR}/snowLiqFlx.f90 + ${SUMMA_ENGINE_DIR}/soilLiqFlx.f90 + ${SUMMA_ENGINE_DIR}/bigAquifer.f90 + ${SUMMA_ENGINE_DIR}/computFlux.f90 + ${SUMMA_ENGINE_DIR}/type4IDA.f90 + ${SUMMA_ENGINE_DIR}/tol4IDA.f90 + ${SUMMA_ENGINE_DIR}/computEnthalpy.f90 + ${SUMMA_ENGINE_DIR}/computHeatCap.f90 + ${SUMMA_ENGINE_DIR}/computThermConduct.f90 + ${SUMMA_ENGINE_DIR}/computResid.f90 + ${SUMMA_ENGINE_DIR}/computJacob.f90 + ${SUMMA_ENGINE_DIR}/eval8summa.f90 + ${SUMMA_ENGINE_DIR}/summaSolve.f90 + ${SUMMA_ENGINE_DIR}/systemSolv.f90 + ${SUMMA_ENGINE_DIR}/computResidSundials.f90 + ${SUMMA_ENGINE_DIR}/eval8summaSundials.f90 + ${SUMMA_ENGINE_DIR}/computJacobSundials.f90 + ${SUMMA_ENGINE_DIR}/computSnowDepth.f90 + ${SUMMA_ENGINE_DIR}/summaSolveSundialsIDA.f90 + ${SUMMA_ENGINE_DIR}/systemSolvSundials.f90 + ${SUMMA_ENGINE_DIR}/varSubstep.f90 + ${SUMMA_ENGINE_DIR}/varSubstepSundials.f90 + ${SUMMA_ENGINE_DIR}/opSplittin.f90 + ${ENGINE_DIR}/sundials/coupled_em.f90) + + set(INTERFACE + ${ACTORS_DIR}/global/cppwrap_datatypes.f90 + ${ACTORS_DIR}/global/cppwrap_auxiliary.f90 + ${ACTORS_DIR}/global/cppwrap_metadata.f90) + + set(FILE_ACCESS_INTERFACE + ${FILE_ACCESS_DIR}/fortran_code/output_structure.f90 + ${FILE_ACCESS_DIR}/fortran_code/cppwrap_fileAccess.f90 + ${FILE_ACCESS_DIR}/fortran_code/read_attribute.f90 + ${FILE_ACCESS_DIR}/fortran_code/read_forcing.f90 + ${FILE_ACCESS_DIR}/fortran_code/read_param.f90 + ${FILE_ACCESS_DIR}/fortran_code/read_initcond.f90 + ${FILE_ACCESS_DIR}/fortran_code/writeOutputFromOutputStructure.f90 + ${FILE_ACCESS_DIR}/fortran_code/write_to_netcdf.f90) + + set(JOB_INTERFACE + ${JOB_ACTOR_DIR}/job_actor.f90) + + set(HRU_INTERFACE + ${HRU_ACTOR_DIR}/fortran_code/model_run.f90 + ${HRU_ACTOR_DIR}/fortran_code/setup_hru.f90 + ${HRU_ACTOR_DIR}/fortran_code/restart.f90 + ${HRU_ACTOR_DIR}/fortran_code/hru_actor.f90 + ${HRU_ACTOR_DIR}/fortran_code/init_hru_actor.f90 + ${HRU_ACTOR_DIR}/fortran_code/outputStrucWrite.f90 + ${HRU_ACTOR_DIR}/fortran_code/hru_writeOutput.f90) + + set(PRELIM + ${SUMMA_ENGINE_DIR}/conv_funcs.f90 + ${SUMMA_ENGINE_DIR}/sunGeomtry.f90 + ${SUMMA_ENGINE_DIR}/convE2Temp.f90 + ${ENGINE_DIR}/allocspaceActors.f90 + ${ENGINE_DIR}/alloc_fileAccess.f90 + ${SUMMA_ENGINE_DIR}/checkStruc.f90 + ${SUMMA_ENGINE_DIR}/childStruc.f90 + ${ENGINE_DIR}/ffile_info.f90 + ${ENGINE_DIR}/read_dimension.f90 + ${ENGINE_DIR}/read_pinit.f90 + ${SUMMA_ENGINE_DIR}/pOverwrite.f90 + ${SUMMA_ENGINE_DIR}/paramCheck.f90 + ${ENGINE_DIR}/check_icondActors.f90) + + set(NOAHMP + ${SUMMA_NOAHMP_DIR}/module_model_constants.F + ${SUMMA_NOAHMP_DIR}/module_sf_noahutl.F + ${SUMMA_NOAHMP_DIR}/module_sf_noahlsm.F + ${SUMMA_NOAHMP_DIR}/module_sf_noahmplsm.F) + + set(MODRUN + ${SUMMA_ENGINE_DIR}/indexState.f90 + ${SUMMA_ENGINE_DIR}/getVectorz.f90 + ${SUMMA_ENGINE_DIR}/t2enthalpy.f90 + ${SUMMA_ENGINE_DIR}/updateVars.f90 + ${SUMMA_ENGINE_DIR}/updateVarsSundials.f90 + ${SUMMA_ENGINE_DIR}/var_derive.f90 + ${ENGINE_DIR}/derivforce.f90 + ${SUMMA_ENGINE_DIR}/snowAlbedo.f90 + ${SUMMA_ENGINE_DIR}/canopySnow.f90 + ${SUMMA_ENGINE_DIR}/tempAdjust.f90 + ${SUMMA_ENGINE_DIR}/snwCompact.f90 + ${SUMMA_ENGINE_DIR}/layerMerge.f90 + ${SUMMA_ENGINE_DIR}/layerDivide.f90 + ${SUMMA_ENGINE_DIR}/volicePack.f90 + ${SUMMA_ENGINE_DIR}/qTimeDelay.f90) + + set(NETCDF + ${NETCDF_DIR}/netcdf_util.f90 + ${NETCDF_DIR}/def_output.f90 + ${NETCDF_DIR}/writeOutput.f90 + ${NETCDF_DIR}/read_icondActors.f90) + + set(DRIVER + ${DRIVER_DIR}/summaActors_type.f90 + ${DRIVER_DIR}/summaActors_util.f90 + ${DRIVER_DIR}/summaActors_globalData.f90 + ${DRIVER_DIR}/summaActors_alarms.f90) + + set(COMM_ALL + ${NRPROC} + ${DATAMS} + ${INTERFACE} + ${HOOKUP} + ${DEPENDS_ON_FILEMANAGER} + ${UTILMS}) + + set(SUMMA_ALL + ${NETCDF} + ${PRELIM} + ${MODRUN} + ${SOLVER} + ${DRIVER} + ${JOB_INTERFACE} + ${FILE_ACCESS_INTERFACE} + ${HRU_INTERFACE} + ${GRU_INTERFACE}) + + set(ACTORS_GLOBAL + ${ACTORS_DIR}/global/global.cpp + ${ACTORS_DIR}/global/timing_info.cpp + ${ACTORS_DIR}/global/message_atoms.cpp + ${ACTORS_DIR}/global/settings_functions.cpp + ${ACTORS_DIR}/global/auxiliary.cpp) + + set(SUMMA_ACTOR + ${ACTORS_DIR}/summa_actor/summa_actor.cpp + ${ACTORS_DIR}/summa_actor/summa_client.cpp + ${ACTORS_DIR}/summa_actor/summa_server.cpp + ${ACTORS_DIR}/summa_actor/summa_backup_server.cpp + ${ACTORS_DIR}/summa_actor/batch/batch.cpp + ${ACTORS_DIR}/summa_actor/batch/batch_container.cpp + ${ACTORS_DIR}/summa_actor/client/client.cpp + ${ACTORS_DIR}/summa_actor/client/client_container.cpp) + + set(HRU_ACTOR + ${ACTORS_DIR}/hru_actor/cpp_code/hru_actor.cpp) + + set(FILE_ACCESS_ACTOR + ${ACTORS_DIR}/file_access_actor/cpp_code/file_access_actor.cpp + ${ACTORS_DIR}/file_access_actor/cpp_code/forcing_file_info.cpp + ${ACTORS_DIR}/file_access_actor/cpp_code/output_container.cpp) + + set(JOB_ACTOR + ${ACTORS_DIR}/job_actor/job_actor.cpp + ${ACTORS_DIR}/job_actor/GRUinfo.cpp) + + set(MAIN + ${ACTORS_DIR}/main.cpp) + + add_library(SUMMA_NOAHMP OBJECT + ${NOAHMP} + ${NRUTIL}) + target_compile_options(SUMMA_NOAHMP PRIVATE ${SUMMA_NOAHMP_OPTIONS}) + add_library(SUMMA_COMM OBJECT + ${COMM_ALL}) + target_compile_options(SUMMA_COMM PRIVATE ${SUMMA_ALL_OPTIONS}) + target_include_directories(SUMMA_COMM PRIVATE ${SUMMA_INCLUDES}) + target_link_libraries(SUMMA_COMM PUBLIC ${SUMMA_LIBS}) + + # Build SUMMA Shared Library + add_library(summa SHARED + ${SUMMA_ALL}) + target_compile_options(summa PRIVATE ${SUMMA_ALL_OPTIONS}) + target_include_directories(summa PUBLIC ${SUMMA_INCLUDES}) + target_link_libraries(summa PUBLIC ${SUMMA_LIBS} SUMMA_COMM) + + # Build Summa-Actors executable + add_executable(${EXEC_NAME} + ${ACTORS_GLOBAL} + ${HRU_ACTOR} + ${FILE_ACCESS_ACTOR} + ${JOB_ACTOR} + ${SUMMA_ACTOR} + ${SUMMA_CLIENT} + ${SUMMA_SERVER} + ${MAIN}) + set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) + target_include_directories(${EXEC_NAME} PUBLIC ${SUMMA_ACTORS_INCLUDES}) + target_link_libraries( ${EXEC_NAME} ${SUMMA_ACTORS_LIBS}) +endfunction() \ No newline at end of file diff --git a/build/cmake/load_modules.sh b/build/cmake/load_modules.sh new file mode 100755 index 000000000..245cd9837 --- /dev/null +++ b/build/cmake/load_modules.sh @@ -0,0 +1,7 @@ +#! /bin/bash + +#### load modules if using Compute Canada or Copernicus #### +module load gcc/9.3.0 +module load netcdf-fortran +module load openblas +module load caf \ No newline at end of file diff --git a/build/build_cmakeSundials b/build/makefiles/build_cmakeSundials similarity index 100% rename from build/build_cmakeSundials rename to build/makefiles/build_cmakeSundials diff --git a/build/makefiles/compile_copernicus b/build/makefiles/compile_copernicus new file mode 100755 index 000000000..08a90f71c --- /dev/null +++ b/build/makefiles/compile_copernicus @@ -0,0 +1,17 @@ +#! /bin/bash + +#### load modules if using Compute Canada or Copernicus #### +module load StdEnv/2020 +module load gcc/9.3.0 +module load openblas/0.3.17 +module load netcdf-fortran/4.5.2 + +#### parent directory of the 'build' directory #### +export ROOT_DIR = /globalhome/gwu479/HPC/SummaSundials + +export INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include +export LDFLAGS = -L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib +export LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas + +make -f ${ROOT_DIR}/build/makefiles/makefile_cluster +export LD_LIBRARY_PATH=${ROOT_DIR}/bin diff --git a/build/makefiles/compile_graham b/build/makefiles/compile_graham new file mode 100755 index 000000000..f29c0e105 --- /dev/null +++ b/build/makefiles/compile_graham @@ -0,0 +1,17 @@ +#! /bin/bash + +#### load modules if using Compute Canada or Copernicus #### +module load StdEnv/2020 +module load gcc/9.3.0 +module load openblas/0.3.17 +module load netcdf-fortran/4.5.2 + +#### parent directory of the 'build' directory #### +export ROOT_DIR = /home/avanb/SummaSundials + +export INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include +export LDFLAGS = -L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib +export LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas + +make -f ${ROOT_DIR}/build/makefiles/makefile_cluster +export LD_LIBRARY_PATH=${ROOT_DIR}/bin diff --git a/build/makefiles/compile_mac b/build/makefiles/compile_mac new file mode 100755 index 000000000..1d8599da7 --- /dev/null +++ b/build/makefiles/compile_mac @@ -0,0 +1,11 @@ +#! /bin/bash + +#### parent directory of the 'build' directory #### +export ROOT_DIR = /Users/amedin/Research/SummaSundials + +export INCLUDES = -I/opt/local/include -I/opt/local/lib +export LDFLAGS = -L/opt/local/lib +export LIBRARIES = $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf + +make -f ${ROOT_DIR}/build/makefiles/makefile_mac +export LD_LIBRARY_PATH=${ROOT_DIR}/bin diff --git a/build/makefiles/compile_richardson b/build/makefiles/compile_richardson new file mode 100755 index 000000000..726f16ede --- /dev/null +++ b/build/makefiles/compile_richardson @@ -0,0 +1,11 @@ +#! /bin/bash + +#### parent directory of the 'build' directory #### +export ROOT_DIR = /u1/gwu479/SummaSundials + +export INCLUDES = -I/usr/include -I/usr/local/include +export LDFLAGS = -L/usr/local/lib +export LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas -lnetcdf + +make -f ${ROOT_DIR}/build/makefiles/makefile_cluster +export LD_LIBRARY_PATH=${ROOT_DIR}/bin diff --git a/build/my_makefile_sup b/build/makefiles/makefile_cluster similarity index 61% rename from build/my_makefile_sup rename to build/makefiles/makefile_cluster index 7a84b6020..1f387fca1 100644 --- a/build/my_makefile_sup +++ b/build/makefiles/makefile_cluster @@ -1,131 +1,30 @@ -#======================================================================== -# Makefile to compile SUMMA SUNDIALS -#======================================================================== -# -# Recommended use: Copy this file to Makefile.local, edit it to your -# heart's content, and then run `make -f build/Makefile.local` from -# your top level SUMMA directory. Don't include the Makefile.local in -# any pull requests you make. -# -# Note that Makefile configurations that we commonly use can be found on -# the SUMMA wiki at: -# https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration -# feel free to add yours to that page. -# -# To troubleshoot your paths and setup, type 'make check' -# -# At a minimum you will need to set the following: -# * F_MASTER_TOP - top level directory -# * FC - compiler suite -# * FC_EXE - compiler executable -# * INCLUDES - path to include files -# * LDFLAGS - path to libraries to include -# * LIBRARIES - libraries to include -# -# Some further options can be specified for OpenMP, etc. See in Part 0 of -# the Makefile. You don't need to make any changes in PART 1 and -# following unless you are doing SUMMA development and changed what -# needs to be compiled - -#======================================================================== -# PART 0: User-configurable part -#======================================================================== - -# The variables can be specified in one of two ways: -# * delete the '##' in front of the variable, fill out the entry, -# save the file and run make -# * make no changes to this file, but specify the variables in your -# environment before you run make - # Define core directory below which everything resides. This is the # parent directory of the 'build' directory -F_MASTER_TOP =../.. -F_MASTER = $(F_MASTER_TOP)/summa - -# Define the Fortran Compiler. If you are using gfortran, then this needs -# to be version 6 or higher. This variable is simply used to select the right -# compiler flags in the ifeq statements in this Makefile. The compiler -# executable is set separately as FC_EXE -# Currently this is either gfortran or ifort -FC = gfortran +# ROOT_DIR = +F_MASTER = $(ROOT_DIR)/summa -# Define the path for the compiler executable. This is the actual executable -# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin/gfortran-mp-11 -# FC and FC_EXE have to be consistent +#### Compilers #### +FC = gfortran FC_EXE = gfortran -# Define the NetCDF and LAPACK libraries and path to include files. -# INCLUDES needs to be of the form (no quotes around the string): -# INCLUDES = -I -I -I<...> -I -# LIBRARIES needs to be of the form ( no quotes around the string): -# LIBRARIES = '-L -lnetcdff -L -lblas -L -l' -# If none of this makes sense, please talk to your system -# administrator. -INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include -LDFLAGS = -L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib -LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas - -DIR_SUNDIALS= $(F_MASTER_TOP)/sundials/instdir +#### Includes AND Libraries #### +# INCLUDES = +# LDFLAGS = +# LIBRARIES = + +DIR_SUNDIALS= $(ROOT_DIR)/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod -# Eventually we plan move to a real configure script, but for now we like -# to keep track of successful compilations of SUMMA on different platforms -# and with different compilers. If you are successful compiling SUMMA, -# please add your configuration (operating system and compiler plus -# part 0 of the Makefile) to the SUMMA wiki on github. - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# ------------ define compiler flags ---------------------------------------- - -# define open MP flags -isOpenMP = -FLAGS_OMP = -LIBOPENMP = - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# gfortran compiler flags -ifeq "$(FC)" "gfortran" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -fopenmp - endif - # Production runs -FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors # Debug runs -#FLAGS_NOAH = -p -g -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -#FLAGS_COMM = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -#FLAGS_SUMMA = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds - -endif - -# ifort compiler flags -ifeq "$(FC)" "ifort" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -qopenmp - endif - -# Production runs -FLAGS_NOAH = -O3 -noerror_limit -FR -auto -fltconsistency $(FLAGS_OMP) -FLAGS_COMM = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) - -# Debug runs -#FLAGS_NOAH = -O0 -p -g -warn nounused -noerror_limit -FR -auto -WB -traceback -fltconsistency -#FLAGS_COMM = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -#FLAGS_SUMMA = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -endif +# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC +# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC +# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC #======================================================================== # PART 1: Define directory paths diff --git a/build/my_makefile_mac b/build/makefiles/makefile_mac similarity index 61% rename from build/my_makefile_mac rename to build/makefiles/makefile_mac index 90e98d189..00df7f3b2 100644 --- a/build/my_makefile_mac +++ b/build/makefiles/makefile_mac @@ -1,131 +1,30 @@ -#======================================================================== -# Makefile to compile SUMMA SUNDIALS -#======================================================================== -# -# Recommended use: Copy this file to Makefile.local, edit it to your -# heart's content, and then run `make -f build/Makefile.local` from -# your top level SUMMA directory. Don't include the Makefile.local in -# any pull requests you make. -# -# Note that Makefile configurations that we commonly use can be found on -# the SUMMA wiki at: -# https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration -# feel free to add yours to that page. -# -# To troubleshoot your paths and setup, type 'make check' -# -# At a minimum you will need to set the following: -# * F_MASTER_TOP - top level directory -# * FC - compiler suite -# * FC_EXE - compiler executable -# * INCLUDES - path to include files -# * LDFLAGS - path to libraries to include -# * LIBRARIES - libraries to include -# -# Some further options can be specified for OpenMP, etc. See in Part 0 of -# the Makefile. You don't need to make any changes in PART 1 and -# following unless you are doing SUMMA development and changed what -# needs to be compiled - -#======================================================================== -# PART 0: User-configurable part -#======================================================================== - -# The variables can be specified in one of two ways: -# * delete the '##' in front of the variable, fill out the entry, -# save the file and run make -# * make no changes to this file, but specify the variables in your -# environment before you run make - # Define core directory below which everything resides. This is the # parent directory of the 'build' directory -F_MASTER_TOP =../.. -F_MASTER = $(F_MASTER_TOP)/summa - -# Define the Fortran Compiler. If you are using gfortran, then this needs -# to be version 6 or higher. This variable is simply used to select the right -# compiler flags in the ifeq statements in this Makefile. The compiler -# executable is set separately as FC_EXE -# Currently this is either gfortran or ifort -FC = gfortran +# ROOT_DIR = +F_MASTER = $(ROOT_DIR)/summa -# Define the path for the compiler executable. This is the actual executable -# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin/gfortran-mp-11 -# FC and FC_EXE have to be consistent +#### Compilers #### +FC = gfortran FC_EXE =/opt/local/bin/gfortran -# Define the NetCDF and LAPACK libraries and path to include files. -# INCLUDES needs to be of the form (no quotes around the string): -# INCLUDES = -I -I -I<...> -I -# LIBRARIES needs to be of the form ( no quotes around the string): -# LIBRARIES = '-L -lnetcdff -L -lblas -L -l' -# If none of this makes sense, please talk to your system -# administrator. -INCLUDES = -I/opt/local/include -I/opt/local/lib -LDFLAGS = -L/opt/local/lib -LIBRARIES = $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf - -DIR_SUNDIALS= $(F_MASTER_TOP)/sundials/instdir +#### Includes AND Libraries #### +# INCLUDES = +# LDFLAGS = +# LIBRARIES = + +DIR_SUNDIALS= $(ROOT_DIR)/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod -# Eventually we plan move to a real configure script, but for now we like -# to keep track of successful compilations of SUMMA on different platforms -# and with different compilers. If you are successful compiling SUMMA, -# please add your configuration (operating system and compiler plus -# part 0 of the Makefile) to the SUMMA wiki on github. - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# ------------ define compiler flags ---------------------------------------- - -# define open MP flags -isOpenMP = -FLAGS_OMP = -LIBOPENMP = - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# gfortran compiler flags -ifeq "$(FC)" "gfortran" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -fopenmp - endif - # Production runs -FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors # Debug runs -#FLAGS_NOAH = -p -g -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -#FLAGS_COMM = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -#FLAGS_SUMMA = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds - -endif - -# ifort compiler flags -ifeq "$(FC)" "ifort" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -qopenmp - endif - -# Production runs -FLAGS_NOAH = -O3 -noerror_limit -FR -auto -fltconsistency $(FLAGS_OMP) -FLAGS_COMM = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) - -# Debug runs -#FLAGS_NOAH = -O0 -p -g -warn nounused -noerror_limit -FR -auto -WB -traceback -fltconsistency -#FLAGS_COMM = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -#FLAGS_SUMMA = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -endif +# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC +# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC +# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC #======================================================================== # PART 1: Define directory paths diff --git a/build/my_makefile_ric b/build/my_makefile_ric deleted file mode 100644 index 5ea5827ca..000000000 --- a/build/my_makefile_ric +++ /dev/null @@ -1,412 +0,0 @@ -#======================================================================== -# Makefile to compile SUMMA SUNDIALS -#======================================================================== -# -# Recommended use: Copy this file to Makefile.local, edit it to your -# heart's content, and then run `make -f build/Makefile.local` from -# your top level SUMMA directory. Don't include the Makefile.local in -# any pull requests you make. -# -# Note that Makefile configurations that we commonly use can be found on -# the SUMMA wiki at: -# https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration -# feel free to add yours to that page. -# -# To troubleshoot your paths and setup, type 'make check' -# -# At a minimum you will need to set the following: -# * F_MASTER_TOP - top level directory -# * FC - compiler suite -# * FC_EXE - compiler executable -# * INCLUDES - path to include files -# * LDFLAGS - path to libraries to include -# * LIBRARIES - libraries to include -# -# Some further options can be specified for OpenMP, etc. See in Part 0 of -# the Makefile. You don't need to make any changes in PART 1 and -# following unless you are doing SUMMA development and changed what -# needs to be compiled - -#======================================================================== -# PART 0: User-configurable part -#======================================================================== - -# The variables can be specified in one of two ways: -# * delete the '##' in front of the variable, fill out the entry, -# save the file and run make -# * make no changes to this file, but specify the variables in your -# environment before you run make - -# Define core directory below which everything resides. This is the -# parent directory of the 'build' directory -F_MASTER_TOP =../.. -F_MASTER = $(F_MASTER_TOP)/summa - -# Define the Fortran Compiler. If you are using gfortran, then this needs -# to be version 6 or higher. This variable is simply used to select the right -# compiler flags in the ifeq statements in this Makefile. The compiler -# executable is set separately as FC_EXE -# Currently this is either gfortran or ifort -FC = gfortran - -# Define the path for the compiler executable. This is the actual executable -# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin/gfortran-mp-11 -# FC and FC_EXE have to be consistent -FC_EXE = gfortran - -# Define the NetCDF and LAPACK libraries and path to include files. -# INCLUDES needs to be of the form (no quotes around the string): -# INCLUDES = -I -I -I<...> -I -# LIBRARIES needs to be of the form ( no quotes around the string): -# LIBRARIES = '-L -lnetcdff -L -lblas -L -l' -# If none of this makes sense, please talk to your system -# administrator. -INCLUDES = -I/usr/include -I/usr/local/include -LDFLAGS = -L/usr/local/lib -LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas -lnetcdf - -DIR_SUNDIALS= $(F_MASTER_TOP)/sundials/instdir -INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran -LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod - -# Eventually we plan move to a real configure script, but for now we like -# to keep track of successful compilations of SUMMA on different platforms -# and with different compilers. If you are successful compiling SUMMA, -# please add your configuration (operating system and compiler plus -# part 0 of the Makefile) to the SUMMA wiki on github. - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# ------------ define compiler flags ---------------------------------------- - -# define open MP flags -isOpenMP = -FLAGS_OMP = -LIBOPENMP = - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# gfortran compiler flags -ifeq "$(FC)" "gfortran" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -fopenmp - endif - -# Production runs -FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) - -# Debug runs -#FLAGS_NOAH = -p -g -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -#FLAGS_COMM = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -#FLAGS_SUMMA = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds - -endif - -# ifort compiler flags -ifeq "$(FC)" "ifort" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -qopenmp - endif - -# Production runs -FLAGS_NOAH = -O3 -noerror_limit -FR -auto -fltconsistency $(FLAGS_OMP) -FLAGS_COMM = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) - -# Debug runs -#FLAGS_NOAH = -O0 -p -g -warn nounused -noerror_limit -FR -auto -WB -traceback -fltconsistency -#FLAGS_COMM = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -#FLAGS_SUMMA = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -endif - -#======================================================================== -# PART 1: Define directory paths -#======================================================================== - -# Core directory that contains source code -F_KORE_DIR = $(F_MASTER)/build/source - -# Location of the compiled modules -MOD_PATH = $(F_MASTER)/build - -# Define the directory for the executables -EXE_PATH = $(F_MASTER)/bin - -#======================================================================== -# PART 2: Assemble all of the SUMMA sub-routines -#======================================================================== - -# Define directories -DRIVER_DIR = $(F_KORE_DIR)/driver -HOOKUP_DIR = $(F_KORE_DIR)/hookup -NETCDF_DIR = $(F_KORE_DIR)/netcdf -DSHARE_DIR = $(F_KORE_DIR)/dshare -NUMREC_DIR = $(F_KORE_DIR)/numrec -NOAHMP_DIR = $(F_KORE_DIR)/noah-mp -ENGINE_DIR = $(F_KORE_DIR)/engine - -# utilities -SUMMA_NRUTIL= \ - nrtype.f90 \ - f2008funcs.f90 \ - nr_utility.f90 -NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) - -# -# Numerical recipes procedures -# NOTE: all numerical recipes procedures are now replaced with free versions -SUMMA_NRPROC= \ - expIntegral.f90 \ - spline_int.f90 -NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) - -# Hook-up modules (set files and directory paths) -SUMMA_HOOKUP= \ - ascii_util.f90 \ - summaFileManager.f90 - -HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) - -# Data modules -SUMMA_DATAMS= \ - multiconst.f90 \ - var_lookup.f90 \ - data_types.f90 \ - globalData.f90 \ - flxMapping.f90 \ - get_ixname.f90 \ - popMetadat.f90 \ - outpt_stat.f90 -DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) - -# utility modules -SUMMA_UTILMS= \ - time_utils.f90 \ - mDecisions.f90 \ - snow_utils.f90 \ - soil_utils.f90 \ - soil_utilsAddSundials.f90 \ - updatState.f90 \ - updatStateSundials.f90 \ - matrixOper.f90 -UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) - -# Model guts -SUMMA_MODGUT= \ - MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) - -# Solver -SUMMA_SOLVER= \ - vegPhenlgy.f90 \ - diagn_evar.f90 \ - stomResist.f90 \ - groundwatr.f90 \ - vegSWavRad.f90 \ - vegNrgFlux.f90 \ - ssdNrgFlux.f90 \ - vegLiqFlux.f90 \ - snowLiqFlx.f90 \ - soilLiqFlx.f90 \ - bigAquifer.f90 \ - computFlux.f90 \ - type4IDA.f90 \ - tol4IDA.f90 \ - computEnthalpy.f90 \ - computHeatCap.f90 \ - computThermConduct.f90 \ - computResid.f90 \ - computJacob.f90 \ - eval8summa.f90 \ - summaSolve.f90 \ - systemSolv.f90 \ - computResidSundials.f90 \ - eval8summaSundials.f90 \ - computJacobSundials.f90 \ - computSnowDepth.f90 \ - summaSolveSundialsIDA.f90 \ - systemSolvSundials.f90 \ - varSubstep.f90 \ - opSplittin.f90 \ - coupled_em.f90 \ - run_oneHRU.f90 \ - run_oneGRU.f90 -SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) - -# Define routines for SUMMA preliminaries -SUMMA_PRELIM= \ - conv_funcs.f90 \ - sunGeomtry.f90 \ - convE2Temp.f90 \ - allocspace.f90 \ - checkStruc.f90 \ - childStruc.f90 \ - ffile_info.f90 \ - read_attrb.f90 \ - read_pinit.f90 \ - pOverwrite.f90 \ - read_param.f90 \ - paramCheck.f90 \ - check_icond.f90 -PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) - -SUMMA_NOAHMP= \ - module_model_constants.F \ - module_sf_noahutl.F \ - module_sf_noahlsm.F \ - module_sf_noahmplsm.F -NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) - -# Define routines for the SUMMA model runs -SUMMA_MODRUN = \ - indexState.f90 \ - getVectorz.f90 \ - t2enthalpy.f90 \ - updateVars.f90 \ - updateVarsSundials.f90 \ - var_derive.f90 \ - read_force.f90 \ - derivforce.f90 \ - snowAlbedo.f90 \ - canopySnow.f90 \ - tempAdjust.f90 \ - snwCompact.f90 \ - layerMerge.f90 \ - layerDivide.f90 \ - volicePack.f90 \ - qTimeDelay.f90 -MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) - -# Define routines for the solver -SUMMA_MSOLVE = \ - -# Define NetCDF routines -SUMMA_NETCDF = \ - netcdf_util.f90 \ - def_output.f90 \ - modelwrite.f90 \ - read_icond.f90 -NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) - -# ... stitch together common programs -COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) - -# ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) - -# Define the driver routine -SUMMA_DRIVER= \ - summa_type.f90 \ - summa_util.f90 \ - summa_alarms.f90 \ - summa_globalData.f90 \ - summa_defineOutput.f90 \ - summa_init.f90 \ - summa_setup.f90 \ - summa_restart.f90 \ - summa_forcing.f90 \ - summa_modelRun.f90 \ - summa_writeOutput.f90 \ - summa_driver.f90 -DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) - -# Define the executable -DRIVER__EX = summa_sundials.exe - -# Define version number -VERSIONFILE = $(DRIVER_DIR)/summaversion.inc -VERSION = $(shell git tag | tail -n 1) -BULTTIM = $(shell date) -GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') -GITHASH = $(shell git rev-parse HEAD) - -#======================================================================== -# PART 3: Checks -#====================================================================== -# make sure that the paths are defined. These are just some high level checks -ifndef F_MASTER - $(error F_MASTER is undefined) -endif -ifndef FC - $(error FC is undefined: Specify your compiler) -endif -ifndef FC_EXE - $(error FC_EXE is undefined: Specify your compiler executable) -endif -ifndef FLAGS_SUMMA - $(error Specify flags for your compiler: $(FC)) -endif -ifndef INCLUDES - $(error INCLUDES is undefined) -endif -ifndef LIBRARIES - $(error LIBRARIES is undefined) -endif - -#======================================================================== -# PART 4: compilation -#====================================================================== - -# Compile -all: compile_noah compile_comm compile_summa link clean install -part: compile_noah compile_comm compile_summa - -check: - $(info) - $(info Displaying make variables:) - $(info F_MASTER : $(F_MASTER)) - $(info EXE_PATH : $(EXE_PATH)) - $(info FC : $(FC)) - $(info INCLUDES : $(INCLUDES)) - $(info LIBRARIES : $(LIBRARIES)) - $(info FLAGS_NOAH : $(FLAGS_NOAH)) - $(info FLAGS_COMM : $(FLAGS_COMM)) - $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) - $(info SUNDIALSDIR: $(DIR_SUNDIALS)) - $(info INC_SUNDIALS: $(INC_SUNDIALS)) - $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) - $(info) - -# update version information -update_version: - echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) - echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) - -# compile Noah-MP routines -compile_noah: - $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) - -# compile common routines - -compile_comm: - $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) - -# compile SUMMA routines -compile_summa: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ - $(INCLUDES) $(INC_SUNDIALS) - -# link routines -link: - $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) - -# Remove object files -clean: - rm -f *.o - rm -f *.mod - rm -f soil_veg_gen_parm__genmod.f90 - -# Copy the executable to the bin directory -install: - @mkdir -p $(EXE_PATH) - @mv $(DRIVER__EX) $(EXE_PATH) - $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) From 05bb0c79aa04f766793ee4e40dcda5810a5c4a2d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 23 Mar 2023 09:34:27 +0900 Subject: [PATCH 0602/1472] setting up like Kyle's for cmake --- build/build_summa_mac | 1 - build/build_summa_sup | 6 - build/cmake/ida.cmake | 296 ++++++++++++++++++ build/cmake/load_modules.sh | 7 + build/{ => makefiles}/build_cmakeBMI | 0 build/{ => makefiles}/build_cmakeNGen | 0 build/{ => makefiles}/build_cmakeSundials | 0 build/makefiles/compile_copernicus | 17 + build/makefiles/compile_mac | 12 + .../makefile_cluster} | 135 +------- .../makefile_mac} | 133 +------- 11 files changed, 365 insertions(+), 242 deletions(-) delete mode 100755 build/build_summa_mac delete mode 100755 build/build_summa_sup create mode 100644 build/cmake/ida.cmake create mode 100755 build/cmake/load_modules.sh rename build/{ => makefiles}/build_cmakeBMI (100%) rename build/{ => makefiles}/build_cmakeNGen (100%) rename build/{ => makefiles}/build_cmakeSundials (100%) create mode 100755 build/makefiles/compile_copernicus create mode 100755 build/makefiles/compile_mac rename build/{my_makefile_sup => makefiles/makefile_cluster} (64%) rename build/{my_makefile_mac => makefiles/makefile_mac} (65%) diff --git a/build/build_summa_mac b/build/build_summa_mac deleted file mode 100755 index cebfebe94..000000000 --- a/build/build_summa_mac +++ /dev/null @@ -1 +0,0 @@ -make -f my_makefile_mac diff --git a/build/build_summa_sup b/build/build_summa_sup deleted file mode 100755 index 073175b00..000000000 --- a/build/build_summa_sup +++ /dev/null @@ -1,6 +0,0 @@ -module load StdEnv/2020 -module load gcc/9.3.0 -module load openblas/0.3.17 -module load netcdf-fortran/4.5.2 - -make -f my_makefile_sup diff --git a/build/cmake/ida.cmake b/build/cmake/ida.cmake new file mode 100644 index 000000000..fd8747c88 --- /dev/null +++ b/build/cmake/ida.cmake @@ -0,0 +1,296 @@ +function(compile_with_ida PARENT_DIR, DIR_SUNDIALS) + find_package(LAPACK REQUIRED) + set(EXEC_NAME summa_sundials) + + link_directories(${DIR_SUNDIALS}/lib64) + set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib64") + set(SUMMA_INCLUDES + "$ENV{EBROOTNETCDFMINFORTRAN}/include" + "${DIR_SUNDIALS}/include" + "${DIR_SUNDIALS}/fortran" + ${netCDF_INCLUDES} + ${LAPACK_INCLUDES}) + + set(SUMMA_LIBS + -lsundials_fnvecmanyvector_mod + -lsundials_fida_mod + -lsundials_fnvecserial_mod + -lsundials_fsunlinsoldense_mod + -lsundials_fsunmatrixdense_mod + -lnetcdff + -lopenblas + ${netCDF_LIBRARIES} + ${LAPACK_LIBRARIES} + SUMMA_NOAHMP) + + set(SUMMA_ACTORS_INCLUDES + ${CAF_INCLUDES} + "$ENV{EBROOTNETCDFMINFORTRAN}/include" + ${LAPACK_INCLUDES} + "${DIR_SUNDIALS}/include" + "${PARENT_DIR}/build/includes/global" + "${PARENT_DIR}/build/includes/summa_actor" + "${PARENT_DIR}/build/includes/gru_actor" + "${PARENT_DIR}/build/includes/job_actor" + "${PARENT_DIR}/build/includes/file_access_actor" + "${PARENT_DIR}/build/includes/hru_actor") + + set(SUMMA_ACTORS_LIBS + ${CAF_LIBRARIES} + ${netCDF_LIBRARIES} + ${LAPACK_LIBRARIES} + -lopenblas + -lcaf_core + -lcaf_io + summa + -lnetcdff + -lsundials_fnvecmanyvector_mod + -lsundials_fida_mod + -lsundials_fnvecserial_mod + -lsundials_fsunlinsoldense_mod + -lsundials_fsunmatrixdense_mod) + + + set(ACTORS_DIR ${PARENT_DIR}/build/source/actors) + set(DRIVER_DIR ${PARENT_DIR}/build/source/driver) + set(DSHARE_DIR ${PARENT_DIR}/build/source/dshare) + set(ENGINE_DIR ${PARENT_DIR}/build/source/engine) + set(HOOKUP_DIR ${PARENT_DIR}/build/source/hookup) + set(NETCDF_DIR ${PARENT_DIR}/build/source/netcdf) + set(NOAHMP_DIR ${PARENT_DIR}/build/source/noah-mp) + set(FILE_ACCESS_DIR ${ACTORS_DIR}/file_access_actor) + set(JOB_ACTOR_DIR ${ACTORS_DIR}/job_actor) + set(HRU_ACTOR_DIR ${ACTORS_DIR}/hru_actor) + set(GRU_ACTOR_DIR ${ACTORS_DIR}/gru_actor) + set(SUMMA_DSHARE_DIR ${PARENT_DIR}/build/summa-sundials/build/source/dshare) + set(SUMMA_ENGINE_DIR ${PARENT_DIR}/build/summa-sundials/build/source/engine) + set(SUMMA_NOAHMP_DIR ${PARENT_DIR}/build/summa-sundials/build/source/noah-mp) + + set(NRUTIL + ${SUMMA_ENGINE_DIR}/nrtype.f90 + ${SUMMA_ENGINE_DIR}/f2008funcs.f90 + ${SUMMA_ENGINE_DIR}/nr_utility.f90) + + set(NRPROC + ${SUMMA_ENGINE_DIR}/expIntegral.f90 + ${SUMMA_ENGINE_DIR}/spline_int.f90) + + SET(HOOKUP + ${HOOKUP_DIR}/ascii_util.f90 + ${HOOKUP_DIR}/summaActors_FileManager.f90) + + SET(DATAMS + ${SUMMA_DSHARE_DIR}/multiconst.f90 + ${SUMMA_DSHARE_DIR}/var_lookup.f90 + ${DSHARE_DIR}/data_types.f90 + ${DSHARE_DIR}/globalData.f90 + ${SUMMA_DSHARE_DIR}/flxMapping.f90) + + SET(DEPENDS_ON_FILEMANAGER + ${SUMMA_DSHARE_DIR}/get_ixname.f90 + ${SUMMA_DSHARE_DIR}/popMetadat.f90 + ${SUMMA_DSHARE_DIR}/outpt_stat.f90) + + SET(UTILMS + ${SUMMA_ENGINE_DIR}/time_utils.f90 + ${ENGINE_DIR}/sundials/mDecisions.f90 + ${SUMMA_ENGINE_DIR}/snow_utils.f90 + ${SUMMA_ENGINE_DIR}/soil_utils.f90 + ${SUMMA_ENGINE_DIR}/soil_utilsAddSundials.f90 + ${SUMMA_ENGINE_DIR}/updatState.f90 + ${SUMMA_ENGINE_DIR}/updatStateSundials.f90 + ${SUMMA_ENGINE_DIR}/matrixOper.f90) + + set(SOLVER + ${ENGINE_DIR}/vegPhenlgy.f90 + ${SUMMA_ENGINE_DIR}/diagn_evar.f90 + ${SUMMA_ENGINE_DIR}/stomResist.f90 + ${SUMMA_ENGINE_DIR}/groundwatr.f90 + ${SUMMA_ENGINE_DIR}/vegSWavRad.f90 + ${SUMMA_ENGINE_DIR}/vegNrgFlux.f90 + ${SUMMA_ENGINE_DIR}/ssdNrgFlux.f90 + ${SUMMA_ENGINE_DIR}/vegLiqFlux.f90 + ${SUMMA_ENGINE_DIR}/snowLiqFlx.f90 + ${SUMMA_ENGINE_DIR}/soilLiqFlx.f90 + ${SUMMA_ENGINE_DIR}/bigAquifer.f90 + ${SUMMA_ENGINE_DIR}/computFlux.f90 + ${SUMMA_ENGINE_DIR}/type4IDA.f90 + ${SUMMA_ENGINE_DIR}/tol4IDA.f90 + ${SUMMA_ENGINE_DIR}/computEnthalpy.f90 + ${SUMMA_ENGINE_DIR}/computHeatCap.f90 + ${SUMMA_ENGINE_DIR}/computThermConduct.f90 + ${SUMMA_ENGINE_DIR}/computResid.f90 + ${SUMMA_ENGINE_DIR}/computJacob.f90 + ${SUMMA_ENGINE_DIR}/eval8summa.f90 + ${SUMMA_ENGINE_DIR}/summaSolve.f90 + ${SUMMA_ENGINE_DIR}/systemSolv.f90 + ${SUMMA_ENGINE_DIR}/computResidSundials.f90 + ${SUMMA_ENGINE_DIR}/eval8summaSundials.f90 + ${SUMMA_ENGINE_DIR}/computJacobSundials.f90 + ${SUMMA_ENGINE_DIR}/computSnowDepth.f90 + ${SUMMA_ENGINE_DIR}/summaSolveSundialsIDA.f90 + ${SUMMA_ENGINE_DIR}/systemSolvSundials.f90 + ${SUMMA_ENGINE_DIR}/varSubstep.f90 + ${SUMMA_ENGINE_DIR}/varSubstepSundials.f90 + ${SUMMA_ENGINE_DIR}/opSplittin.f90 + ${ENGINE_DIR}/sundials/coupled_em.f90) + + set(INTERFACE + ${ACTORS_DIR}/global/cppwrap_datatypes.f90 + ${ACTORS_DIR}/global/cppwrap_auxiliary.f90 + ${ACTORS_DIR}/global/cppwrap_metadata.f90) + + set(FILE_ACCESS_INTERFACE + ${FILE_ACCESS_DIR}/fortran_code/output_structure.f90 + ${FILE_ACCESS_DIR}/fortran_code/cppwrap_fileAccess.f90 + ${FILE_ACCESS_DIR}/fortran_code/read_attribute.f90 + ${FILE_ACCESS_DIR}/fortran_code/read_forcing.f90 + ${FILE_ACCESS_DIR}/fortran_code/read_param.f90 + ${FILE_ACCESS_DIR}/fortran_code/read_initcond.f90 + ${FILE_ACCESS_DIR}/fortran_code/writeOutputFromOutputStructure.f90 + ${FILE_ACCESS_DIR}/fortran_code/write_to_netcdf.f90) + + set(JOB_INTERFACE + ${JOB_ACTOR_DIR}/job_actor.f90) + + set(HRU_INTERFACE + ${HRU_ACTOR_DIR}/fortran_code/model_run.f90 + ${HRU_ACTOR_DIR}/fortran_code/setup_hru.f90 + ${HRU_ACTOR_DIR}/fortran_code/restart.f90 + ${HRU_ACTOR_DIR}/fortran_code/hru_actor.f90 + ${HRU_ACTOR_DIR}/fortran_code/init_hru_actor.f90 + ${HRU_ACTOR_DIR}/fortran_code/outputStrucWrite.f90 + ${HRU_ACTOR_DIR}/fortran_code/hru_writeOutput.f90) + + set(PRELIM + ${SUMMA_ENGINE_DIR}/conv_funcs.f90 + ${SUMMA_ENGINE_DIR}/sunGeomtry.f90 + ${SUMMA_ENGINE_DIR}/convE2Temp.f90 + ${ENGINE_DIR}/allocspaceActors.f90 + ${ENGINE_DIR}/alloc_fileAccess.f90 + ${SUMMA_ENGINE_DIR}/checkStruc.f90 + ${SUMMA_ENGINE_DIR}/childStruc.f90 + ${ENGINE_DIR}/ffile_info.f90 + ${ENGINE_DIR}/read_dimension.f90 + ${ENGINE_DIR}/read_pinit.f90 + ${SUMMA_ENGINE_DIR}/pOverwrite.f90 + ${SUMMA_ENGINE_DIR}/paramCheck.f90 + ${ENGINE_DIR}/check_icondActors.f90) + + set(NOAHMP + ${SUMMA_NOAHMP_DIR}/module_model_constants.F + ${SUMMA_NOAHMP_DIR}/module_sf_noahutl.F + ${SUMMA_NOAHMP_DIR}/module_sf_noahlsm.F + ${SUMMA_NOAHMP_DIR}/module_sf_noahmplsm.F) + + set(MODRUN + ${SUMMA_ENGINE_DIR}/indexState.f90 + ${SUMMA_ENGINE_DIR}/getVectorz.f90 + ${SUMMA_ENGINE_DIR}/t2enthalpy.f90 + ${SUMMA_ENGINE_DIR}/updateVars.f90 + ${SUMMA_ENGINE_DIR}/updateVarsSundials.f90 + ${SUMMA_ENGINE_DIR}/var_derive.f90 + ${ENGINE_DIR}/derivforce.f90 + ${SUMMA_ENGINE_DIR}/snowAlbedo.f90 + ${SUMMA_ENGINE_DIR}/canopySnow.f90 + ${SUMMA_ENGINE_DIR}/tempAdjust.f90 + ${SUMMA_ENGINE_DIR}/snwCompact.f90 + ${SUMMA_ENGINE_DIR}/layerMerge.f90 + ${SUMMA_ENGINE_DIR}/layerDivide.f90 + ${SUMMA_ENGINE_DIR}/volicePack.f90 + ${SUMMA_ENGINE_DIR}/qTimeDelay.f90) + + set(NETCDF + ${NETCDF_DIR}/netcdf_util.f90 + ${NETCDF_DIR}/def_output.f90 + ${NETCDF_DIR}/writeOutput.f90 + ${NETCDF_DIR}/read_icondActors.f90) + + set(DRIVER + ${DRIVER_DIR}/summaActors_type.f90 + ${DRIVER_DIR}/summaActors_util.f90 + ${DRIVER_DIR}/summaActors_globalData.f90 + ${DRIVER_DIR}/summaActors_alarms.f90) + + set(COMM_ALL + ${NRPROC} + ${DATAMS} + ${INTERFACE} + ${HOOKUP} + ${DEPENDS_ON_FILEMANAGER} + ${UTILMS}) + + set(SUMMA_ALL + ${NETCDF} + ${PRELIM} + ${MODRUN} + ${SOLVER} + ${DRIVER} + ${JOB_INTERFACE} + ${FILE_ACCESS_INTERFACE} + ${HRU_INTERFACE} + ${GRU_INTERFACE}) + + set(ACTORS_GLOBAL + ${ACTORS_DIR}/global/global.cpp + ${ACTORS_DIR}/global/timing_info.cpp + ${ACTORS_DIR}/global/message_atoms.cpp + ${ACTORS_DIR}/global/settings_functions.cpp + ${ACTORS_DIR}/global/auxiliary.cpp) + + set(SUMMA_ACTOR + ${ACTORS_DIR}/summa_actor/summa_actor.cpp + ${ACTORS_DIR}/summa_actor/summa_client.cpp + ${ACTORS_DIR}/summa_actor/summa_server.cpp + ${ACTORS_DIR}/summa_actor/summa_backup_server.cpp + ${ACTORS_DIR}/summa_actor/batch/batch.cpp + ${ACTORS_DIR}/summa_actor/batch/batch_container.cpp + ${ACTORS_DIR}/summa_actor/client/client.cpp + ${ACTORS_DIR}/summa_actor/client/client_container.cpp) + + set(HRU_ACTOR + ${ACTORS_DIR}/hru_actor/cpp_code/hru_actor.cpp) + + set(FILE_ACCESS_ACTOR + ${ACTORS_DIR}/file_access_actor/cpp_code/file_access_actor.cpp + ${ACTORS_DIR}/file_access_actor/cpp_code/forcing_file_info.cpp + ${ACTORS_DIR}/file_access_actor/cpp_code/output_container.cpp) + + set(JOB_ACTOR + ${ACTORS_DIR}/job_actor/job_actor.cpp + ${ACTORS_DIR}/job_actor/GRUinfo.cpp) + + set(MAIN + ${ACTORS_DIR}/main.cpp) + + add_library(SUMMA_NOAHMP OBJECT + ${NOAHMP} + ${NRUTIL}) + target_compile_options(SUMMA_NOAHMP PRIVATE ${SUMMA_NOAHMP_OPTIONS}) + add_library(SUMMA_COMM OBJECT + ${COMM_ALL}) + target_compile_options(SUMMA_COMM PRIVATE ${SUMMA_ALL_OPTIONS}) + target_include_directories(SUMMA_COMM PRIVATE ${SUMMA_INCLUDES}) + target_link_libraries(SUMMA_COMM PUBLIC ${SUMMA_LIBS}) + + # Build SUMMA Shared Library + add_library(summa SHARED + ${SUMMA_ALL}) + target_compile_options(summa PRIVATE ${SUMMA_ALL_OPTIONS}) + target_include_directories(summa PUBLIC ${SUMMA_INCLUDES}) + target_link_libraries(summa PUBLIC ${SUMMA_LIBS} SUMMA_COMM) + + # Build Summa-Actors executable + add_executable(${EXEC_NAME} + ${ACTORS_GLOBAL} + ${HRU_ACTOR} + ${FILE_ACCESS_ACTOR} + ${JOB_ACTOR} + ${SUMMA_ACTOR} + ${SUMMA_CLIENT} + ${SUMMA_SERVER} + ${MAIN}) + set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) + target_include_directories(${EXEC_NAME} PUBLIC ${SUMMA_ACTORS_INCLUDES}) + target_link_libraries( ${EXEC_NAME} ${SUMMA_ACTORS_LIBS}) +endfunction() \ No newline at end of file diff --git a/build/cmake/load_modules.sh b/build/cmake/load_modules.sh new file mode 100755 index 000000000..245cd9837 --- /dev/null +++ b/build/cmake/load_modules.sh @@ -0,0 +1,7 @@ +#! /bin/bash + +#### load modules if using Compute Canada or Copernicus #### +module load gcc/9.3.0 +module load netcdf-fortran +module load openblas +module load caf \ No newline at end of file diff --git a/build/build_cmakeBMI b/build/makefiles/build_cmakeBMI similarity index 100% rename from build/build_cmakeBMI rename to build/makefiles/build_cmakeBMI diff --git a/build/build_cmakeNGen b/build/makefiles/build_cmakeNGen similarity index 100% rename from build/build_cmakeNGen rename to build/makefiles/build_cmakeNGen diff --git a/build/build_cmakeSundials b/build/makefiles/build_cmakeSundials similarity index 100% rename from build/build_cmakeSundials rename to build/makefiles/build_cmakeSundials diff --git a/build/makefiles/compile_copernicus b/build/makefiles/compile_copernicus new file mode 100755 index 000000000..08a90f71c --- /dev/null +++ b/build/makefiles/compile_copernicus @@ -0,0 +1,17 @@ +#! /bin/bash + +#### load modules if using Compute Canada or Copernicus #### +module load StdEnv/2020 +module load gcc/9.3.0 +module load openblas/0.3.17 +module load netcdf-fortran/4.5.2 + +#### parent directory of the 'build' directory #### +export ROOT_DIR = /globalhome/gwu479/HPC/SummaSundials + +export INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include +export LDFLAGS = -L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib +export LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas + +make -f ${ROOT_DIR}/build/makefiles/makefile_cluster +export LD_LIBRARY_PATH=${ROOT_DIR}/bin diff --git a/build/makefiles/compile_mac b/build/makefiles/compile_mac new file mode 100755 index 000000000..dd06a6991 --- /dev/null +++ b/build/makefiles/compile_mac @@ -0,0 +1,12 @@ +#! /bin/bash + +#### parent directory of the 'build' directory #### +export ROOT_DIR = /Users/amedin/Research/SummaSundials + + +export INCLUDES = -I/opt/local/include -I/opt/local/lib +export LDFLAGS = -L/opt/local/lib +export LIBRARIES = $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf + +make -f ${ROOT_DIR}/build/makefiles/makefile_mac +export LD_LIBRARY_PATH=${ROOT_DIR}/bin diff --git a/build/my_makefile_sup b/build/makefiles/makefile_cluster similarity index 64% rename from build/my_makefile_sup rename to build/makefiles/makefile_cluster index c72ea61de..0e88ddf12 100644 --- a/build/my_makefile_sup +++ b/build/makefiles/makefile_cluster @@ -1,135 +1,34 @@ -#======================================================================== -# Makefile to compile SUMMA SUNDIALS -#======================================================================== -# -# Recommended use: Copy this file to Makefile.local, edit it to your -# heart's content, and then run `make -f build/Makefile.local` from -# your top level SUMMA directory. Don't include the Makefile.local in -# any pull requests you make. -# -# Note that Makefile configurations that we commonly use can be found on -# the SUMMA wiki at: -# https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration -# feel free to add yours to that page. -# -# To troubleshoot your paths and setup, type 'make check' -# -# At a minimum you will need to set the following: -# * F_MASTER_TOP - top level directory -# * FC - compiler suite -# * FC_EXE - compiler executable -# * INCLUDES - path to include files -# * LDFLAGS - path to libraries to include -# * LIBRARIES - libraries to include -# -# Some further options can be specified for OpenMP, etc. See in Part 0 of -# the Makefile. You don't need to make any changes in PART 1 and -# following unless you are doing SUMMA development and changed what -# needs to be compiled - -#======================================================================== -# PART 0: User-configurable part -#======================================================================== - -# The variables can be specified in one of two ways: -# * delete the '##' in front of the variable, fill out the entry, -# save the file and run make -# * make no changes to this file, but specify the variables in your -# environment before you run make - # Define core directory below which everything resides. This is the # parent directory of the 'build' directory -F_MASTER_TOP =../.. -F_MASTER = $(F_MASTER_TOP)/summa - -# Define the Fortran Compiler. If you are using gfortran, then this needs -# to be version 6 or higher. This variable is simply used to select the right -# compiler flags in the ifeq statements in this Makefile. The compiler -# executable is set separately as FC_EXE -# Currently this is either gfortran or ifort -FC = gfortran +# ROOT_DIR = +F_MASTER = $(ROOT_DIR)/summa -# Define the path for the compiler executable. This is the actual executable -# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin/gfortran-mp-11 -# FC and FC_EXE have to be consistent +#### Compilers #### +FC = gfortran FC_EXE = gfortran -# Define the NetCDF and LAPACK libraries and path to include files. -# INCLUDES needs to be of the form (no quotes around the string): -# INCLUDES = -I -I -I<...> -I -# LIBRARIES needs to be of the form ( no quotes around the string): -# LIBRARIES = '-L -lnetcdff -L -lblas -L -l' -# If none of this makes sense, please talk to your system -# administrator. -INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include -LDFLAGS = -L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib -LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas - -DIR_SUNDIALS= $(F_MASTER_TOP)/sundials/instdir +#### Includes AND Libraries #### +# INCLUDES = +# LDFLAGS = +# LIBRARIES = + +DIR_SUNDIALS= $(ROOT_DIR)/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod -DIR_BMI= $(F_MASTER_TOP)/bmi/instdir +DIR_BMI= $(ROOT_DIR)/bmi/instdir INC_BMI=-I$(DIR_BMI)/include LIB_BMI=-L$(DIR_BMI)/lib -lbmif -# Eventually we plan move to a real configure script, but for now we like -# to keep track of successful compilations of SUMMA on different platforms -# and with different compilers. If you are successful compiling SUMMA, -# please add your configuration (operating system and compiler plus -# part 0 of the Makefile) to the SUMMA wiki on github. - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# ------------ define compiler flags ---------------------------------------- - -# define open MP flags -isOpenMP = -FLAGS_OMP = -LIBOPENMP = - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# gfortran compiler flags -ifeq "$(FC)" "gfortran" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -fopenmp - endif - # Production runs -FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors # Debug runs -#FLAGS_NOAH = -p -g -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -#FLAGS_COMM = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -#FLAGS_SUMMA = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds - -endif - -# ifort compiler flags -ifeq "$(FC)" "ifort" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -qopenmp - endif - -# Production runs -FLAGS_NOAH = -O3 -noerror_limit -FR -auto -fltconsistency $(FLAGS_OMP) -FLAGS_COMM = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) - -# Debug runs -#FLAGS_NOAH = -O0 -p -g -warn nounused -noerror_limit -FR -auto -WB -traceback -fltconsistency -#FLAGS_COMM = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -#FLAGS_SUMMA = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -endif +# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC +# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC +# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC #======================================================================== # PART 1: Define directory paths diff --git a/build/my_makefile_mac b/build/makefiles/makefile_mac similarity index 65% rename from build/my_makefile_mac rename to build/makefiles/makefile_mac index cec9cd917..298ea6d21 100644 --- a/build/my_makefile_mac +++ b/build/makefiles/makefile_mac @@ -1,71 +1,18 @@ -#======================================================================== -# Makefile to compile SUMMA SUNDIALS -#======================================================================== -# -# Recommended use: Copy this file to Makefile.local, edit it to your -# heart's content, and then run `make -f build/Makefile.local` from -# your top level SUMMA directory. Don't include the Makefile.local in -# any pull requests you make. -# -# Note that Makefile configurations that we commonly use can be found on -# the SUMMA wiki at: -# https://github.com/NCAR/summa/wiki/SUMMA-Makefile-Part-0-configuration -# feel free to add yours to that page. -# -# To troubleshoot your paths and setup, type 'make check' -# -# At a minimum you will need to set the following: -# * F_MASTER_TOP - top level directory -# * FC - compiler suite -# * FC_EXE - compiler executable -# * INCLUDES - path to include files -# * LDFLAGS - path to libraries to include -# * LIBRARIES - libraries to include -# -# Some further options can be specified for OpenMP, etc. See in Part 0 of -# the Makefile. You don't need to make any changes in PART 1 and -# following unless you are doing SUMMA development and changed what -# needs to be compiled - -#======================================================================== -# PART 0: User-configurable part -#======================================================================== - -# The variables can be specified in one of two ways: -# * delete the '##' in front of the variable, fill out the entry, -# save the file and run make -# * make no changes to this file, but specify the variables in your -# environment before you run make - # Define core directory below which everything resides. This is the # parent directory of the 'build' directory -F_MASTER_TOP =../.. -F_MASTER = $(F_MASTER_TOP)/summa - -# Define the Fortran Compiler. If you are using gfortran, then this needs -# to be version 6 or higher. This variable is simply used to select the right -# compiler flags in the ifeq statements in this Makefile. The compiler -# executable is set separately as FC_EXE -# Currently this is either gfortran or ifort -FC = gfortran +# ROOT_DIR = +F_MASTER = $(ROOT_DIR)/summa -# Define the path for the compiler executable. This is the actual executable -# that is invoked. For example, FC=gfortran and FC_EXE=/usr/bin/gfortran-mp-11 -# FC and FC_EXE have to be consistent +#### Compilers #### +FC = gfortran FC_EXE =/opt/local/bin/gfortran -# Define the NetCDF and LAPACK libraries and path to include files. -# INCLUDES needs to be of the form (no quotes around the string): -# INCLUDES = -I -I -I<...> -I -# LIBRARIES needs to be of the form ( no quotes around the string): -# LIBRARIES = '-L -lnetcdff -L -lblas -L -l' -# If none of this makes sense, please talk to your system -# administrator. -INCLUDES = -I/opt/local/include -I/opt/local/lib -LDFLAGS = -L/opt/local/lib -LIBRARIES = $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf - -DIR_SUNDIALS= $(F_MASTER_TOP)/sundials/instdir +#### Includes AND Libraries #### +# INCLUDES = +# LDFLAGS = +# LIBRARIES = + +DIR_SUNDIALS= $(ROOT_DIR)/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod @@ -73,63 +20,15 @@ DIR_BMI= $(F_MASTER_TOP)/bmi/instdir INC_BMI=-I$(DIR_BMI)/include LIB_BMI=-L$(DIR_BMI)/lib -lbmif -Xlinker -rpath -Xlinker $(DIR_BMI)/lib -# Eventually we plan move to a real configure script, but for now we like -# to keep track of successful compilations of SUMMA on different platforms -# and with different compilers. If you are successful compiling SUMMA, -# please add your configuration (operating system and compiler plus -# part 0 of the Makefile) to the SUMMA wiki on github. - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# ------------ define compiler flags ---------------------------------------- - -# define open MP flags -isOpenMP = -FLAGS_OMP = -LIBOPENMP = - -# Define compiler flags. If you use a different compiler, -# you will need to figure out what the equivalent flags are -# and may need to update this section - -# gfortran compiler flags -ifeq "$(FC)" "gfortran" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -fopenmp - endif - # Production runs -FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 $(FLAGS_OMP) +FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors # Debug runs -#FLAGS_NOAH = -p -g -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -#FLAGS_COMM = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -#FLAGS_SUMMA = -p -g -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds - -endif - -# ifort compiler flags -ifeq "$(FC)" "ifort" - - ifeq "$(isOpenMP)" "yes" - FLAGS_OMP = -qopenmp - endif - -# Production runs -FLAGS_NOAH = -O3 -noerror_limit -FR -auto -fltconsistency $(FLAGS_OMP) -FLAGS_COMM = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) -FLAGS_SUMMA = -O3 -FR -auto -fltconsistency -fpe0 $(FLAGS_OMP) - -# Debug runs -#FLAGS_NOAH = -O0 -p -g -warn nounused -noerror_limit -FR -auto -WB -traceback -fltconsistency -#FLAGS_COMM = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -#FLAGS_SUMMA = -O0 -p -g -debug -warn all -check all -FR -auto -WB -traceback -fltconsistency -fpe0 -endif +# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC +# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC +# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC #======================================================================== # PART 1: Define directory paths From dd2aa9e3d22ac54fa6036f344d9a4750904788b0 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Mar 2023 00:27:03 +0900 Subject: [PATCH 0603/1472] cmake files --- build/cmake/bmi.cmake | 202 +++++++++++++++++++ build/cmake/build_ngen.cluster.bash | 49 +++++ build/cmake/build_ngen.mac.bash | 21 ++ build/cmake/ida.cmake | 296 ---------------------------- build/cmake/load_modules.sh | 6 +- build/makefiles/build_cmakeNGen | 5 - build/makefiles/compile_mac | 1 - build/makefiles/makefile_mac | 2 +- 8 files changed, 276 insertions(+), 306 deletions(-) create mode 100644 build/cmake/bmi.cmake create mode 100644 build/cmake/build_ngen.cluster.bash create mode 100644 build/cmake/build_ngen.mac.bash delete mode 100644 build/cmake/ida.cmake delete mode 100755 build/makefiles/build_cmakeNGen diff --git a/build/cmake/bmi.cmake b/build/cmake/bmi.cmake new file mode 100644 index 000000000..2fe4b869f --- /dev/null +++ b/build/cmake/bmi.cmake @@ -0,0 +1,202 @@ +function(compile_with_ida ROOT_DIR, F_MASTER, DIR_SUNDIALS, INCLUDES, LIBRARIES, INC_SUNDIALS, LIB_SUNDIALS, FLAGS_ALL, FLAGS_NOAH) + +#======================================================================== +# PART 1: Define directory paths +#======================================================================== + +# Core directory that contains source code + set(F_KORE_DIR $(F_MASTER)/build/source) + +# Define directories + set(DRIVER_DIR $(F_KORE_DIR)/driver) + set(HOOKUP_DIR $(F_KORE_DIR)/hookup) + set(NETCDF_DIR $(F_KORE_DIR)/netcdf) + set(DSHARE_DIR $(F_KORE_DIR)/dshare) + set(NUMREC_DIR $(F_KORE_DIR)/numrec) + set(NOAHMP_DIR $(F_KORE_DIR)/noah-mp) + set(ENGINE_DIR $(F_KORE_DIR)/engine) + +#======================================================================== +# PART 2: Assemble all of the SUMMA sub-routines +#======================================================================== + +# utilities + set(NRUTIL + ${ENGINE_DIR}/nrtype.f90 + ${ENGINE_DIR}/f2008funcs.f90 + ${ENGINE_DIR}/nr_utility.f90) + +# Numerical recipes procedures +# NOTE: all numerical recipes procedures are now replaced with free versions + set(NRPROC + ${ENGINE_DIR}/expIntegral.f90 + ${ENGINE_DIR}/spline_int.f90) + +# Hook-up modules + set(HOOKUP + ${HOOKUP_DIR}/ascii_util.f90 + ${HOOKUP_DIR}/summaFileManager.f90) + +# Data modules + set(DATAMS + ${DSHARE_DIR}/multiconst.f90 + ${DSHARE_DIR}/var_lookup.f90 + ${DSHARE_DIR}/data_types.f90 + ${DSHARE_DIR}/globalData.f90 + ${DSHARE_DIR}/flxMapping.f90) + ${DSHARE_DIR}/get_ixname.f90 + ${DSHARE_DIR}/popMetadat.f90 + ${DSHARE_DIR}/outpt_stat.f90) + +# utility modules + set(UTILMS + ${ENGINE_DIR}/time_utils.f90 + ${ENGINE_DIR}/mDecisions.f90 + ${ENGINE_DIR}/snow_utils.f90 + ${ENGINE_DIR}/soil_utils.f90 + ${ENGINE_DIR}/soil_utilsAddSundials.f90 + ${ENGINE_DIR}/updatState.f90 + ${ENGINE_DIR}/updatStateSundials.f90 + ${ENGINE_DIR}/matrixOper.f90) + +# Solver + set(SOLVER + ${ENGINE_DIR}/vegPhenlgy.f90 + ${ENGINE_DIR}/diagn_evar.f90 + ${ENGINE_DIR}/stomResist.f90 + ${ENGINE_DIR}/groundwatr.f90 + ${ENGINE_DIR}/vegSWavRad.f90 + ${ENGINE_DIR}/vegNrgFlux.f90 + ${ENGINE_DIR}/ssdNrgFlux.f90 + ${ENGINE_DIR}/vegLiqFlux.f90 + ${ENGINE_DIR}/snowLiqFlx.f90 + ${ENGINE_DIR}/soilLiqFlx.f90 + ${ENGINE_DIR}/bigAquifer.f90 + ${ENGINE_DIR}/computFlux.f90 + ${ENGINE_DIR}/type4IDA.f90 + ${ENGINE_DIR}/tol4IDA.f90 + ${ENGINE_DIR}/computEnthalpy.f90 + ${ENGINE_DIR}/computHeatCap.f90 + ${ENGINE_DIR}/computThermConduct.f90 + ${ENGINE_DIR}/computResid.f90 + ${ENGINE_DIR}/computJacob.f90 + ${ENGINE_DIR}/eval8summa.f90 + ${ENGINE_DIR}/summaSolve.f90 + ${ENGINE_DIR}/systemSolv.f90 + ${ENGINE_DIR}/computResidSundials.f90 + ${ENGINE_DIR}/eval8summaSundials.f90 + ${ENGINE_DIR}/computJacobSundials.f90 + ${ENGINE_DIR}/computSnowDepth.f90 + ${ENGINE_DIR}/summaSolveSundialsIDA.f90 + ${ENGINE_DIR}/systemSolvSundials.f90 + ${ENGINE_DIR}/varSubstep.f90 + ${ENGINE_DIR}/opSplittin.f90 + ${ENGINE_DIR}/coupled_em.f90 + ${ENGINE_DIR}/run_oneHRU.f90 + ${ENGINE_DIR}/run_oneGRU.f90) + +# Define routines for SUMMA preliminaries + set(PRELIM + ${ENGINE_DIR}/conv_funcs.f90 + ${ENGINE_DIR}/sunGeomtry.f90 + ${ENGINE_DIR}/convE2Temp.f90 + ${ENGINE_DIR}/allocspace.f90 + ${ENGINE_DIR}/checkStruc.f90 + ${ENGINE_DIR}/childStruc.f90 + ${ENGINE_DIR}/ffile_info.f90 + ${ENGINE_DIR}/read_attrb.f90 + ${ENGINE_DIR}/read_pinit.f90 + ${ENGINE_DIR}/pOverwrite.f90 + ${ENGINE_DIR}/read_param.f90 + ${ENGINE_DIR}/paramCheck.f90 + ${ENGINE_DIR}/check_icond.f90) + + set(NOAHMP + ${NOAHMP_DIR}/module_model_constants.F + ${NOAHMP_DIR}/module_sf_noahutl.F + ${NOAHMP_DIR}/module_sf_noahlsm.F + ${NOAHMP_DIR}/module_sf_noahmplsm.F) + +# Define routines for the SUMMA model runs + set(MODRUN + ${ENGINE_DIR}/indexState.f90 + ${ENGINE_DIR}/getVectorz.f90 + ${ENGINE_DIR}/t2enthalpy.f90 + ${ENGINE_DIR}/updateVars.f90 + ${ENGINE_DIR}/updateVarsSundials.f90 + ${ENGINE_DIR}/var_derive.f90 + ${ENGINE_DIR}/read_force.f90 + ${ENGINE_DIR}/derivforce.f90 + ${ENGINE_DIR}/snowAlbedo.f90 + ${ENGINE_DIR}/canopySnow.f90 + ${ENGINE_DIR}/tempAdjust.f90 + ${ENGINE_DIR}/snwCompact.f90 + ${ENGINE_DIR}/layerMerge.f90 + ${ENGINE_DIR}/layerDivide.f90 + ${ENGINE_DIR}/volicePack.f90 + ${ENGINE_DIR}/qTimeDelay.f90) + +# Define NetCDF routines + set(NETCDF + ${NETCDF_DIR}/netcdf_util.f90 + ${NETCDF_DIR}/def_output.f90 + ${NETCDF_DIR}/modelwrite.f90 + ${NETCDF_DIR}/read_icond.f90) + +# Define the driver routine + set(DRIVER + ${DRIVER_DIR}/summa_type.f90 + ${DRIVER_DIR}/summa_util.f90 + ${DRIVER_DIR}/summa_alarms.f90 + ${DRIVER_DIR}/summa_globalData.f90 + ${DRIVER_DIR}/summa_defineOutput.f90 + ${DRIVER_DIR}/summa_init.f90 + ${DRIVER_DIR}/summa_setup.f90 + ${DRIVER_DIR}/summa_restart.f90 + ${DRIVER_DIR}/summa_forcing.f90 + ${DRIVER_DIR}/summa_modelRun.f90 + ${DRIVER_DIR}/summa_writeOutput.f90 + ${DRIVER_DIR}/summa_driver.f90) + + # run program files, do not use in ngen + # set(SUMMA ${DRIVER_DIR}/summa_run.f90) + # set(BMI ${DRIVER_DIR}/summa_runBMI.f90) + + set(COMM_ALL + ${NRUTIL} + ${NRPROC} + ${HOOKUP} + ${DATAMS} + ${UTILMS}) + + set(SUMMA_ALL + ${NETCDF} + ${PRELIM} + ${MODRUN} + ${SOLVER} + ${DRIVER}) + +#======================================================================== +# PART 4: compilation +#====================================================================== + + add_library(SUMMA_NOAHMP OBJECT ${NOAHMP} ${NRUTIL}) + target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) + + add_library(SUMMA_COMM OBJECT ${COMM_ALL}) + target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) + target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES}) + target_link_libraries(SUMMA_COMM PUBLIC ${LIBRARIES}) + + # Build SUMMA Shared Library, don't need BMI libraries in ngen + if(WIN32) + add_library(summabmi ${SUMMA_ALL}) + else + add_library(summabmi SHARED ${SUMMA_ALL}) + endif + target_compile_options(summabmi PRIVATE ${FLAGS_ALL}) + target_include_directories(summabmi PUBLIC ${INCLUDES} ${INC_SUNDIALS}) + target_link_libraries(summabmi PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_COMM) + + +endfunction() \ No newline at end of file diff --git a/build/cmake/build_ngen.cluster.bash b/build/cmake/build_ngen.cluster.bash new file mode 100644 index 000000000..5f29d3699 --- /dev/null +++ b/build/cmake/build_ngen.cluster.bash @@ -0,0 +1,49 @@ +#!/bin/bash + +# build nextgen on Copernicus + +module load gnu +module load boost +module load udunits/2.2.28 +module load python/3.7.5 +module load StdEnv/2020 +module load gcc/9.3.0 +module load openblas/0.3.17 +module load netcdf-fortran/4.5.2 + + +cmake -B extern/cfe/cmake_build -S extern/cfe +cmake --build extern/cfe/cmake_build --target all + +cmake -B extern/topmodel/cmake_build -S extern/topmodel +cmake --build extern/topmodel/cmake_build --target all + +cmake -B extern/sloth/cmake_build -S extern/sloth +cmake --build extern/sloth/cmake_build --target all + +cmake -B extern/noah-owp-modular/cmake_build -S extern/noah-owp-modular +cmake --build extern/noah-owp-modular/cmake_build --target all + +cmake -B extern/iso_c_fortran_bmi/cmake_build -S extern/iso_c_fortran_bmi +cmake --build extern/iso_c_fortran_bmi/cmake_build --target all + +cmake -B extern/snow17/cmake_build -S extern/snow17 +cmake --build extern/snow17/cmake_build --target all + +cmake -B extern/summa/cmake_build -S extern/summa +cmake --build extern/summa/cmake_build --target all + +cmake -B extern/evapotranspiration/cmake_build -S extern/evapotranspiration +cmake --build extern/evapotranspiration/cmake_build --target all + +cmake -B cmake_build -S . -DMPI_ACTIVE:BOOL=ON -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ +#cmake -B cmake_build -S . -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ +# -DUDUNITS2_LIBRARY=/glade/u/apps/ch/opt/udunits/2.2.28/gnu/10.1.0/lib/libudunits2.so \ +# -DUDUNITS2_INCLUDE=/glade/u/apps/ch/opt/udunits/2.2.28/gnu/10.1.0/include # \ +# -DCMAKE_BUILD_TYPE=Debug + + # can also add -DCMAKE_BUILD_TYPE=Debug to be able to run in gdb + +#make -j 8 -C cmake_build # build w/ 8 parallel jobs +make -C cmake_build + diff --git a/build/cmake/build_ngen.mac.bash b/build/cmake/build_ngen.mac.bash new file mode 100644 index 000000000..1e30bfd4a --- /dev/null +++ b/build/cmake/build_ngen.mac.bash @@ -0,0 +1,21 @@ +#!/bin/bash + +# build nextgen on Mac + +cmake -B extern/iso_c_fortran_bmi/cmake_build -S extern/iso_c_fortran_bmi +cmake --build extern/iso_c_fortran_bmi/cmake_build --target all + +cmake -B extern/summa/cmake_build -S extern/summa +cmake --build extern/summa/cmake_build --target all + +cmake -B cmake_build -S . -DMPI_ACTIVE:BOOL=ON -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ +#cmake -B cmake_build -S . -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ +# -DUDUNITS2_LIBRARY=/glade/u/apps/ch/opt/udunits/2.2.28/gnu/10.1.0/lib/libudunits2.so \ +# -DUDUNITS2_INCLUDE=/glade/u/apps/ch/opt/udunits/2.2.28/gnu/10.1.0/include # \ +# -DCMAKE_BUILD_TYPE=Debug + + # can also add -DCMAKE_BUILD_TYPE=Debug to be able to run in gdb + +#make -j 8 -C cmake_build # build w/ 8 parallel jobs +make -C cmake_build + diff --git a/build/cmake/ida.cmake b/build/cmake/ida.cmake deleted file mode 100644 index fd8747c88..000000000 --- a/build/cmake/ida.cmake +++ /dev/null @@ -1,296 +0,0 @@ -function(compile_with_ida PARENT_DIR, DIR_SUNDIALS) - find_package(LAPACK REQUIRED) - set(EXEC_NAME summa_sundials) - - link_directories(${DIR_SUNDIALS}/lib64) - set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib64") - set(SUMMA_INCLUDES - "$ENV{EBROOTNETCDFMINFORTRAN}/include" - "${DIR_SUNDIALS}/include" - "${DIR_SUNDIALS}/fortran" - ${netCDF_INCLUDES} - ${LAPACK_INCLUDES}) - - set(SUMMA_LIBS - -lsundials_fnvecmanyvector_mod - -lsundials_fida_mod - -lsundials_fnvecserial_mod - -lsundials_fsunlinsoldense_mod - -lsundials_fsunmatrixdense_mod - -lnetcdff - -lopenblas - ${netCDF_LIBRARIES} - ${LAPACK_LIBRARIES} - SUMMA_NOAHMP) - - set(SUMMA_ACTORS_INCLUDES - ${CAF_INCLUDES} - "$ENV{EBROOTNETCDFMINFORTRAN}/include" - ${LAPACK_INCLUDES} - "${DIR_SUNDIALS}/include" - "${PARENT_DIR}/build/includes/global" - "${PARENT_DIR}/build/includes/summa_actor" - "${PARENT_DIR}/build/includes/gru_actor" - "${PARENT_DIR}/build/includes/job_actor" - "${PARENT_DIR}/build/includes/file_access_actor" - "${PARENT_DIR}/build/includes/hru_actor") - - set(SUMMA_ACTORS_LIBS - ${CAF_LIBRARIES} - ${netCDF_LIBRARIES} - ${LAPACK_LIBRARIES} - -lopenblas - -lcaf_core - -lcaf_io - summa - -lnetcdff - -lsundials_fnvecmanyvector_mod - -lsundials_fida_mod - -lsundials_fnvecserial_mod - -lsundials_fsunlinsoldense_mod - -lsundials_fsunmatrixdense_mod) - - - set(ACTORS_DIR ${PARENT_DIR}/build/source/actors) - set(DRIVER_DIR ${PARENT_DIR}/build/source/driver) - set(DSHARE_DIR ${PARENT_DIR}/build/source/dshare) - set(ENGINE_DIR ${PARENT_DIR}/build/source/engine) - set(HOOKUP_DIR ${PARENT_DIR}/build/source/hookup) - set(NETCDF_DIR ${PARENT_DIR}/build/source/netcdf) - set(NOAHMP_DIR ${PARENT_DIR}/build/source/noah-mp) - set(FILE_ACCESS_DIR ${ACTORS_DIR}/file_access_actor) - set(JOB_ACTOR_DIR ${ACTORS_DIR}/job_actor) - set(HRU_ACTOR_DIR ${ACTORS_DIR}/hru_actor) - set(GRU_ACTOR_DIR ${ACTORS_DIR}/gru_actor) - set(SUMMA_DSHARE_DIR ${PARENT_DIR}/build/summa-sundials/build/source/dshare) - set(SUMMA_ENGINE_DIR ${PARENT_DIR}/build/summa-sundials/build/source/engine) - set(SUMMA_NOAHMP_DIR ${PARENT_DIR}/build/summa-sundials/build/source/noah-mp) - - set(NRUTIL - ${SUMMA_ENGINE_DIR}/nrtype.f90 - ${SUMMA_ENGINE_DIR}/f2008funcs.f90 - ${SUMMA_ENGINE_DIR}/nr_utility.f90) - - set(NRPROC - ${SUMMA_ENGINE_DIR}/expIntegral.f90 - ${SUMMA_ENGINE_DIR}/spline_int.f90) - - SET(HOOKUP - ${HOOKUP_DIR}/ascii_util.f90 - ${HOOKUP_DIR}/summaActors_FileManager.f90) - - SET(DATAMS - ${SUMMA_DSHARE_DIR}/multiconst.f90 - ${SUMMA_DSHARE_DIR}/var_lookup.f90 - ${DSHARE_DIR}/data_types.f90 - ${DSHARE_DIR}/globalData.f90 - ${SUMMA_DSHARE_DIR}/flxMapping.f90) - - SET(DEPENDS_ON_FILEMANAGER - ${SUMMA_DSHARE_DIR}/get_ixname.f90 - ${SUMMA_DSHARE_DIR}/popMetadat.f90 - ${SUMMA_DSHARE_DIR}/outpt_stat.f90) - - SET(UTILMS - ${SUMMA_ENGINE_DIR}/time_utils.f90 - ${ENGINE_DIR}/sundials/mDecisions.f90 - ${SUMMA_ENGINE_DIR}/snow_utils.f90 - ${SUMMA_ENGINE_DIR}/soil_utils.f90 - ${SUMMA_ENGINE_DIR}/soil_utilsAddSundials.f90 - ${SUMMA_ENGINE_DIR}/updatState.f90 - ${SUMMA_ENGINE_DIR}/updatStateSundials.f90 - ${SUMMA_ENGINE_DIR}/matrixOper.f90) - - set(SOLVER - ${ENGINE_DIR}/vegPhenlgy.f90 - ${SUMMA_ENGINE_DIR}/diagn_evar.f90 - ${SUMMA_ENGINE_DIR}/stomResist.f90 - ${SUMMA_ENGINE_DIR}/groundwatr.f90 - ${SUMMA_ENGINE_DIR}/vegSWavRad.f90 - ${SUMMA_ENGINE_DIR}/vegNrgFlux.f90 - ${SUMMA_ENGINE_DIR}/ssdNrgFlux.f90 - ${SUMMA_ENGINE_DIR}/vegLiqFlux.f90 - ${SUMMA_ENGINE_DIR}/snowLiqFlx.f90 - ${SUMMA_ENGINE_DIR}/soilLiqFlx.f90 - ${SUMMA_ENGINE_DIR}/bigAquifer.f90 - ${SUMMA_ENGINE_DIR}/computFlux.f90 - ${SUMMA_ENGINE_DIR}/type4IDA.f90 - ${SUMMA_ENGINE_DIR}/tol4IDA.f90 - ${SUMMA_ENGINE_DIR}/computEnthalpy.f90 - ${SUMMA_ENGINE_DIR}/computHeatCap.f90 - ${SUMMA_ENGINE_DIR}/computThermConduct.f90 - ${SUMMA_ENGINE_DIR}/computResid.f90 - ${SUMMA_ENGINE_DIR}/computJacob.f90 - ${SUMMA_ENGINE_DIR}/eval8summa.f90 - ${SUMMA_ENGINE_DIR}/summaSolve.f90 - ${SUMMA_ENGINE_DIR}/systemSolv.f90 - ${SUMMA_ENGINE_DIR}/computResidSundials.f90 - ${SUMMA_ENGINE_DIR}/eval8summaSundials.f90 - ${SUMMA_ENGINE_DIR}/computJacobSundials.f90 - ${SUMMA_ENGINE_DIR}/computSnowDepth.f90 - ${SUMMA_ENGINE_DIR}/summaSolveSundialsIDA.f90 - ${SUMMA_ENGINE_DIR}/systemSolvSundials.f90 - ${SUMMA_ENGINE_DIR}/varSubstep.f90 - ${SUMMA_ENGINE_DIR}/varSubstepSundials.f90 - ${SUMMA_ENGINE_DIR}/opSplittin.f90 - ${ENGINE_DIR}/sundials/coupled_em.f90) - - set(INTERFACE - ${ACTORS_DIR}/global/cppwrap_datatypes.f90 - ${ACTORS_DIR}/global/cppwrap_auxiliary.f90 - ${ACTORS_DIR}/global/cppwrap_metadata.f90) - - set(FILE_ACCESS_INTERFACE - ${FILE_ACCESS_DIR}/fortran_code/output_structure.f90 - ${FILE_ACCESS_DIR}/fortran_code/cppwrap_fileAccess.f90 - ${FILE_ACCESS_DIR}/fortran_code/read_attribute.f90 - ${FILE_ACCESS_DIR}/fortran_code/read_forcing.f90 - ${FILE_ACCESS_DIR}/fortran_code/read_param.f90 - ${FILE_ACCESS_DIR}/fortran_code/read_initcond.f90 - ${FILE_ACCESS_DIR}/fortran_code/writeOutputFromOutputStructure.f90 - ${FILE_ACCESS_DIR}/fortran_code/write_to_netcdf.f90) - - set(JOB_INTERFACE - ${JOB_ACTOR_DIR}/job_actor.f90) - - set(HRU_INTERFACE - ${HRU_ACTOR_DIR}/fortran_code/model_run.f90 - ${HRU_ACTOR_DIR}/fortran_code/setup_hru.f90 - ${HRU_ACTOR_DIR}/fortran_code/restart.f90 - ${HRU_ACTOR_DIR}/fortran_code/hru_actor.f90 - ${HRU_ACTOR_DIR}/fortran_code/init_hru_actor.f90 - ${HRU_ACTOR_DIR}/fortran_code/outputStrucWrite.f90 - ${HRU_ACTOR_DIR}/fortran_code/hru_writeOutput.f90) - - set(PRELIM - ${SUMMA_ENGINE_DIR}/conv_funcs.f90 - ${SUMMA_ENGINE_DIR}/sunGeomtry.f90 - ${SUMMA_ENGINE_DIR}/convE2Temp.f90 - ${ENGINE_DIR}/allocspaceActors.f90 - ${ENGINE_DIR}/alloc_fileAccess.f90 - ${SUMMA_ENGINE_DIR}/checkStruc.f90 - ${SUMMA_ENGINE_DIR}/childStruc.f90 - ${ENGINE_DIR}/ffile_info.f90 - ${ENGINE_DIR}/read_dimension.f90 - ${ENGINE_DIR}/read_pinit.f90 - ${SUMMA_ENGINE_DIR}/pOverwrite.f90 - ${SUMMA_ENGINE_DIR}/paramCheck.f90 - ${ENGINE_DIR}/check_icondActors.f90) - - set(NOAHMP - ${SUMMA_NOAHMP_DIR}/module_model_constants.F - ${SUMMA_NOAHMP_DIR}/module_sf_noahutl.F - ${SUMMA_NOAHMP_DIR}/module_sf_noahlsm.F - ${SUMMA_NOAHMP_DIR}/module_sf_noahmplsm.F) - - set(MODRUN - ${SUMMA_ENGINE_DIR}/indexState.f90 - ${SUMMA_ENGINE_DIR}/getVectorz.f90 - ${SUMMA_ENGINE_DIR}/t2enthalpy.f90 - ${SUMMA_ENGINE_DIR}/updateVars.f90 - ${SUMMA_ENGINE_DIR}/updateVarsSundials.f90 - ${SUMMA_ENGINE_DIR}/var_derive.f90 - ${ENGINE_DIR}/derivforce.f90 - ${SUMMA_ENGINE_DIR}/snowAlbedo.f90 - ${SUMMA_ENGINE_DIR}/canopySnow.f90 - ${SUMMA_ENGINE_DIR}/tempAdjust.f90 - ${SUMMA_ENGINE_DIR}/snwCompact.f90 - ${SUMMA_ENGINE_DIR}/layerMerge.f90 - ${SUMMA_ENGINE_DIR}/layerDivide.f90 - ${SUMMA_ENGINE_DIR}/volicePack.f90 - ${SUMMA_ENGINE_DIR}/qTimeDelay.f90) - - set(NETCDF - ${NETCDF_DIR}/netcdf_util.f90 - ${NETCDF_DIR}/def_output.f90 - ${NETCDF_DIR}/writeOutput.f90 - ${NETCDF_DIR}/read_icondActors.f90) - - set(DRIVER - ${DRIVER_DIR}/summaActors_type.f90 - ${DRIVER_DIR}/summaActors_util.f90 - ${DRIVER_DIR}/summaActors_globalData.f90 - ${DRIVER_DIR}/summaActors_alarms.f90) - - set(COMM_ALL - ${NRPROC} - ${DATAMS} - ${INTERFACE} - ${HOOKUP} - ${DEPENDS_ON_FILEMANAGER} - ${UTILMS}) - - set(SUMMA_ALL - ${NETCDF} - ${PRELIM} - ${MODRUN} - ${SOLVER} - ${DRIVER} - ${JOB_INTERFACE} - ${FILE_ACCESS_INTERFACE} - ${HRU_INTERFACE} - ${GRU_INTERFACE}) - - set(ACTORS_GLOBAL - ${ACTORS_DIR}/global/global.cpp - ${ACTORS_DIR}/global/timing_info.cpp - ${ACTORS_DIR}/global/message_atoms.cpp - ${ACTORS_DIR}/global/settings_functions.cpp - ${ACTORS_DIR}/global/auxiliary.cpp) - - set(SUMMA_ACTOR - ${ACTORS_DIR}/summa_actor/summa_actor.cpp - ${ACTORS_DIR}/summa_actor/summa_client.cpp - ${ACTORS_DIR}/summa_actor/summa_server.cpp - ${ACTORS_DIR}/summa_actor/summa_backup_server.cpp - ${ACTORS_DIR}/summa_actor/batch/batch.cpp - ${ACTORS_DIR}/summa_actor/batch/batch_container.cpp - ${ACTORS_DIR}/summa_actor/client/client.cpp - ${ACTORS_DIR}/summa_actor/client/client_container.cpp) - - set(HRU_ACTOR - ${ACTORS_DIR}/hru_actor/cpp_code/hru_actor.cpp) - - set(FILE_ACCESS_ACTOR - ${ACTORS_DIR}/file_access_actor/cpp_code/file_access_actor.cpp - ${ACTORS_DIR}/file_access_actor/cpp_code/forcing_file_info.cpp - ${ACTORS_DIR}/file_access_actor/cpp_code/output_container.cpp) - - set(JOB_ACTOR - ${ACTORS_DIR}/job_actor/job_actor.cpp - ${ACTORS_DIR}/job_actor/GRUinfo.cpp) - - set(MAIN - ${ACTORS_DIR}/main.cpp) - - add_library(SUMMA_NOAHMP OBJECT - ${NOAHMP} - ${NRUTIL}) - target_compile_options(SUMMA_NOAHMP PRIVATE ${SUMMA_NOAHMP_OPTIONS}) - add_library(SUMMA_COMM OBJECT - ${COMM_ALL}) - target_compile_options(SUMMA_COMM PRIVATE ${SUMMA_ALL_OPTIONS}) - target_include_directories(SUMMA_COMM PRIVATE ${SUMMA_INCLUDES}) - target_link_libraries(SUMMA_COMM PUBLIC ${SUMMA_LIBS}) - - # Build SUMMA Shared Library - add_library(summa SHARED - ${SUMMA_ALL}) - target_compile_options(summa PRIVATE ${SUMMA_ALL_OPTIONS}) - target_include_directories(summa PUBLIC ${SUMMA_INCLUDES}) - target_link_libraries(summa PUBLIC ${SUMMA_LIBS} SUMMA_COMM) - - # Build Summa-Actors executable - add_executable(${EXEC_NAME} - ${ACTORS_GLOBAL} - ${HRU_ACTOR} - ${FILE_ACCESS_ACTOR} - ${JOB_ACTOR} - ${SUMMA_ACTOR} - ${SUMMA_CLIENT} - ${SUMMA_SERVER} - ${MAIN}) - set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) - target_include_directories(${EXEC_NAME} PUBLIC ${SUMMA_ACTORS_INCLUDES}) - target_link_libraries( ${EXEC_NAME} ${SUMMA_ACTORS_LIBS}) -endfunction() \ No newline at end of file diff --git a/build/cmake/load_modules.sh b/build/cmake/load_modules.sh index 245cd9837..a477a811d 100755 --- a/build/cmake/load_modules.sh +++ b/build/cmake/load_modules.sh @@ -1,7 +1,7 @@ #! /bin/bash #### load modules if using Compute Canada or Copernicus #### +module load StdEnv/2020 module load gcc/9.3.0 -module load netcdf-fortran -module load openblas -module load caf \ No newline at end of file +module load openblas/0.3.17 +module load netcdf-fortran/4.5.2 \ No newline at end of file diff --git a/build/makefiles/build_cmakeNGen b/build/makefiles/build_cmakeNGen deleted file mode 100755 index ffc6932d8..000000000 --- a/build/makefiles/build_cmakeNGen +++ /dev/null @@ -1,5 +0,0 @@ -# from ../../ngen, run -# cp ../../summa/build/build_cmakeNGen build_cmake -# run script from the ngen directory with ./build_cmake - -cmake ../../bmi-fortran/ -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=/Users/amedin/Research/SummaSundials/bmi/instdir \ No newline at end of file diff --git a/build/makefiles/compile_mac b/build/makefiles/compile_mac index dd06a6991..1d8599da7 100755 --- a/build/makefiles/compile_mac +++ b/build/makefiles/compile_mac @@ -3,7 +3,6 @@ #### parent directory of the 'build' directory #### export ROOT_DIR = /Users/amedin/Research/SummaSundials - export INCLUDES = -I/opt/local/include -I/opt/local/lib export LDFLAGS = -L/opt/local/lib export LIBRARIES = $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf diff --git a/build/makefiles/makefile_mac b/build/makefiles/makefile_mac index 298ea6d21..bb4c48732 100644 --- a/build/makefiles/makefile_mac +++ b/build/makefiles/makefile_mac @@ -16,7 +16,7 @@ DIR_SUNDIALS= $(ROOT_DIR)/sundials/instdir INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod -DIR_BMI= $(F_MASTER_TOP)/bmi/instdir +DIR_BMI= $(ROOT_DIR)/bmi/instdir INC_BMI=-I$(DIR_BMI)/include LIB_BMI=-L$(DIR_BMI)/lib -lbmif -Xlinker -rpath -Xlinker $(DIR_BMI)/lib From 22b292f3bbc0db8ede962f4162ef960b19a7a889 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Mar 2023 09:03:51 +0900 Subject: [PATCH 0604/1472] adding files to repository --- build/cmake/CMakeLists.txt | 97 ++++++++++++++++++++++++++++++++++++++ build/cmake/README.md | 64 +++++++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 build/cmake/CMakeLists.txt create mode 100644 build/cmake/README.md diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt new file mode 100644 index 000000000..26d9a5883 --- /dev/null +++ b/build/cmake/CMakeLists.txt @@ -0,0 +1,97 @@ +ccmake_minimum_required(VERSION 3.10) +enable_language( Fortran ) +#add_subdirectory(../iso_c_fortran_bmi ${CMAKE_BINARY_DIR}/iso_c_bmi) + + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../../cmake/") + +project(summabmi VERSION 1.0.0 DESCRIPTION "Summa-Sundials BMI Module Shared Library") + +set( CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran) + +#### Add variables for individual libraries that are used within the *.pc.in files +set(SUMMA_LIB_NAME_CMAKE summabmi) +set(SUMMA_LIB_DESC_CMAKE "Summa-Sundials BMI Module Shared Library") + +# Make sure these are compiled with this directive UNCLEAR IF WE NEED THESE +add_compile_definitions(BMI_ACTIVE NGEN_FORCING_ACTIVE NGEN_OUTPUT_ACTIVE NGEN_ACTIVE) + + +set(ROOT_DIR ../../../) +set(F_MASTER $(ROOT_DIR)/summa) + +SET (CMAKE_Fortran_COMPILER gfortran) + +include($(F_MASTER)/build/cmake/bmi.cmake) +# -------------------------------------------------------------------------------------------- +# When compiling Summa-Sundials it can be compiled in a Cluster or Debug mode, which is set by +# the CMAKE_BUILD_TYPES variables. The default is Release, non-cluster mode. + +######### SET THE PATHS TO THE SUNDIALS LIBRARIES AND INCLUDE FILES ######### +############################################################################# + +# Add options for build type +set(CMAKE_BUILD_TYPES Debug Release Cluster_Debug Cluster) + +# Set default configuration type to Release +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +# Set compiler flags +if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) + message("Debug build.") + add_definitions(-DDEBUG) + set(FLAGS_NOAH -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) + set(FLAGS_ALL -g -O0 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) +else() + message("Release build.") + set(FLAGS_NOAH -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) + set(FLAGS_ALL -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) +endif() + + +# Packages that are required +find_package(netCDF REQUIRED) +find_package(LAPACK REQUIRED) + +set(DIR_SUNDIALS= $(ROOT_DIR)/sundials/instdir) +set(INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran) + +# Set include directories +if(CMAKE_BUILD_TYPE MATCHES Cluster OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) + set(INCLUDES $(EBROOTNETCDFMINFORTRAN)/include) + set(LDFLAGS $(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib) + set(LIBRARIES $(LDFLAGS) -lnetcdff -lopenblas) + + message("\nBuilding IDA\n") + + link_directories(${DIR_SUNDIALS}/lib64) + set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib64") + set(LIB_SUNDIALS $(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) + +else + set(INCLUDES /opt/local/include -I/opt/local/lib) + set(LDFLAGS /opt/local/lib) + set(LIBRARIES $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf) + + message("\nBuilding IDA\n") + + link_directories(${DIR_SUNDIALS}/lib) + set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib") + set(LIB_SUNDIALS $(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) + + +endif() + + +message("\nBuilding SUMMA\n") +compile_with_bmi($(ROOT_DIR), $(F_MASTER), $(DIR_SUNDIALS), $(INCLUDES), $(LIBRARIES), $(INC_SUNDIALS), $(LIB_SUNDIALS), $(FLAGS_ALL), $(FLAGS_NOAH)) + +install(TARGETS summabmi + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + +configure_file(summabmi.pc.in summabmi.pc @ONLY) + +install(FILES ${CMAKE_BINARY_DIR}/summabmi.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) diff --git a/build/cmake/README.md b/build/cmake/README.md new file mode 100644 index 000000000..6dffc1f00 --- /dev/null +++ b/build/cmake/README.md @@ -0,0 +1,64 @@ +# SummaSundials Submodule + +## About + +This directory wraps the *summa* Git submodule repo, which contains a clone of the repo for the OWP SummaSundials module library that implements BMI. From here, the shared library file for the SummaSundials module can be built for use in NGen. This is configured with the [CMakeLists.txt](CMakeLists.txt) and other files in this outer directory. + +#### Extra Outer Directory + +Currently there are two directory layers beneath the top-level *extern/* directory. This was done so that certain things used by NGen (i.e., a *CMakeLists.txt* file for building shared library files) can be placed alongside, but not within, the submodule. + +## Working with the Submodule + +Some simple explanations of several command actions are included below. To better understand what these things are doing, consult the [Git Submodule documentation](https://git-scm.com/book/en/v2/Git-Tools-Submodules). + +### Getting the Latest Changes + +There are two steps to getting upstream submodule changes fully + 1. fetching and locally checking out the changes from the remote + 2. committing the new checkout revision for the submodule + +To fetch and check out the latest revision (for the [currently used branch](#viewing-the-current-branch)): + + git submodule update --init -- extern/summa/summa + +To commit the current submodule checkout revision to the NGen repo: + + git add extern/summa/summa + git commit + +### Viewing the Commit Hash + +Git submodule configurations include the specific commit to be checked out (or an implicit default). The current commit can be view with `git submodule status`: + + git submodule status -- extern/summa/summa/ + +This will show the **commit**, **submodule local path**, and the git description for the **commit**. The specific configuration, including the configured branch, is set in the _.gitmodules_ file in the NGen project root. + +### Changing the Commit Branch + +The latest commit in the configured branch can be brought in as described here. If it is ever necessary to change to a different branch, the following will do so: + + git config -f .gitmodules "submodule.extern/summa/summa.branch" + +Note that this will be done in the NGen repo configuration, so it can then be committed and push to remotes. It is also possible to do something similar in just the local clone of a repo, by configuring `.git/config` instead of `.gitmodules`. See the Git documentation for more on how that works if needed. + +# Usage + +## Building Libraries + +First, cd into the outer directory containing the submodule: + + cd extern/summa + +Before library files can be built, a CMake build system must be generated. E.g.: + + cmake -B cmake_build -S . + +Note that when there is an existing directory, it may sometimes be necessary to clear it and regenerate, especially if any changes were made to the [CMakeLists.txt](CMakeLists.txt) file. + +After there is build system directory, the shared library can be built using the `topmodelbmi` CMake target. For example, the SummaSundials shared library file (i.e., the build config's `topmodelbmi` target) can be built using: + + cmake --build cmake_build --target summabmi -- -j 2 + +This will build a `cmake_build/libsummabmi..` file, where the version is configured within the CMake config, and the extension depends on the local machine's operating system. From ece808dabd94aa3ddce781a08d7f3f2cc8cf6ee6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Mar 2023 13:47:32 +0900 Subject: [PATCH 0605/1472] cut cmake, edit install notes --- build/cmake/ida.cmake | 296 ------------------------------------ build/cmake/load_modules.sh | 7 - sundials/installation.txt | 2 +- 3 files changed, 1 insertion(+), 304 deletions(-) delete mode 100644 build/cmake/ida.cmake delete mode 100755 build/cmake/load_modules.sh diff --git a/build/cmake/ida.cmake b/build/cmake/ida.cmake deleted file mode 100644 index fd8747c88..000000000 --- a/build/cmake/ida.cmake +++ /dev/null @@ -1,296 +0,0 @@ -function(compile_with_ida PARENT_DIR, DIR_SUNDIALS) - find_package(LAPACK REQUIRED) - set(EXEC_NAME summa_sundials) - - link_directories(${DIR_SUNDIALS}/lib64) - set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib64") - set(SUMMA_INCLUDES - "$ENV{EBROOTNETCDFMINFORTRAN}/include" - "${DIR_SUNDIALS}/include" - "${DIR_SUNDIALS}/fortran" - ${netCDF_INCLUDES} - ${LAPACK_INCLUDES}) - - set(SUMMA_LIBS - -lsundials_fnvecmanyvector_mod - -lsundials_fida_mod - -lsundials_fnvecserial_mod - -lsundials_fsunlinsoldense_mod - -lsundials_fsunmatrixdense_mod - -lnetcdff - -lopenblas - ${netCDF_LIBRARIES} - ${LAPACK_LIBRARIES} - SUMMA_NOAHMP) - - set(SUMMA_ACTORS_INCLUDES - ${CAF_INCLUDES} - "$ENV{EBROOTNETCDFMINFORTRAN}/include" - ${LAPACK_INCLUDES} - "${DIR_SUNDIALS}/include" - "${PARENT_DIR}/build/includes/global" - "${PARENT_DIR}/build/includes/summa_actor" - "${PARENT_DIR}/build/includes/gru_actor" - "${PARENT_DIR}/build/includes/job_actor" - "${PARENT_DIR}/build/includes/file_access_actor" - "${PARENT_DIR}/build/includes/hru_actor") - - set(SUMMA_ACTORS_LIBS - ${CAF_LIBRARIES} - ${netCDF_LIBRARIES} - ${LAPACK_LIBRARIES} - -lopenblas - -lcaf_core - -lcaf_io - summa - -lnetcdff - -lsundials_fnvecmanyvector_mod - -lsundials_fida_mod - -lsundials_fnvecserial_mod - -lsundials_fsunlinsoldense_mod - -lsundials_fsunmatrixdense_mod) - - - set(ACTORS_DIR ${PARENT_DIR}/build/source/actors) - set(DRIVER_DIR ${PARENT_DIR}/build/source/driver) - set(DSHARE_DIR ${PARENT_DIR}/build/source/dshare) - set(ENGINE_DIR ${PARENT_DIR}/build/source/engine) - set(HOOKUP_DIR ${PARENT_DIR}/build/source/hookup) - set(NETCDF_DIR ${PARENT_DIR}/build/source/netcdf) - set(NOAHMP_DIR ${PARENT_DIR}/build/source/noah-mp) - set(FILE_ACCESS_DIR ${ACTORS_DIR}/file_access_actor) - set(JOB_ACTOR_DIR ${ACTORS_DIR}/job_actor) - set(HRU_ACTOR_DIR ${ACTORS_DIR}/hru_actor) - set(GRU_ACTOR_DIR ${ACTORS_DIR}/gru_actor) - set(SUMMA_DSHARE_DIR ${PARENT_DIR}/build/summa-sundials/build/source/dshare) - set(SUMMA_ENGINE_DIR ${PARENT_DIR}/build/summa-sundials/build/source/engine) - set(SUMMA_NOAHMP_DIR ${PARENT_DIR}/build/summa-sundials/build/source/noah-mp) - - set(NRUTIL - ${SUMMA_ENGINE_DIR}/nrtype.f90 - ${SUMMA_ENGINE_DIR}/f2008funcs.f90 - ${SUMMA_ENGINE_DIR}/nr_utility.f90) - - set(NRPROC - ${SUMMA_ENGINE_DIR}/expIntegral.f90 - ${SUMMA_ENGINE_DIR}/spline_int.f90) - - SET(HOOKUP - ${HOOKUP_DIR}/ascii_util.f90 - ${HOOKUP_DIR}/summaActors_FileManager.f90) - - SET(DATAMS - ${SUMMA_DSHARE_DIR}/multiconst.f90 - ${SUMMA_DSHARE_DIR}/var_lookup.f90 - ${DSHARE_DIR}/data_types.f90 - ${DSHARE_DIR}/globalData.f90 - ${SUMMA_DSHARE_DIR}/flxMapping.f90) - - SET(DEPENDS_ON_FILEMANAGER - ${SUMMA_DSHARE_DIR}/get_ixname.f90 - ${SUMMA_DSHARE_DIR}/popMetadat.f90 - ${SUMMA_DSHARE_DIR}/outpt_stat.f90) - - SET(UTILMS - ${SUMMA_ENGINE_DIR}/time_utils.f90 - ${ENGINE_DIR}/sundials/mDecisions.f90 - ${SUMMA_ENGINE_DIR}/snow_utils.f90 - ${SUMMA_ENGINE_DIR}/soil_utils.f90 - ${SUMMA_ENGINE_DIR}/soil_utilsAddSundials.f90 - ${SUMMA_ENGINE_DIR}/updatState.f90 - ${SUMMA_ENGINE_DIR}/updatStateSundials.f90 - ${SUMMA_ENGINE_DIR}/matrixOper.f90) - - set(SOLVER - ${ENGINE_DIR}/vegPhenlgy.f90 - ${SUMMA_ENGINE_DIR}/diagn_evar.f90 - ${SUMMA_ENGINE_DIR}/stomResist.f90 - ${SUMMA_ENGINE_DIR}/groundwatr.f90 - ${SUMMA_ENGINE_DIR}/vegSWavRad.f90 - ${SUMMA_ENGINE_DIR}/vegNrgFlux.f90 - ${SUMMA_ENGINE_DIR}/ssdNrgFlux.f90 - ${SUMMA_ENGINE_DIR}/vegLiqFlux.f90 - ${SUMMA_ENGINE_DIR}/snowLiqFlx.f90 - ${SUMMA_ENGINE_DIR}/soilLiqFlx.f90 - ${SUMMA_ENGINE_DIR}/bigAquifer.f90 - ${SUMMA_ENGINE_DIR}/computFlux.f90 - ${SUMMA_ENGINE_DIR}/type4IDA.f90 - ${SUMMA_ENGINE_DIR}/tol4IDA.f90 - ${SUMMA_ENGINE_DIR}/computEnthalpy.f90 - ${SUMMA_ENGINE_DIR}/computHeatCap.f90 - ${SUMMA_ENGINE_DIR}/computThermConduct.f90 - ${SUMMA_ENGINE_DIR}/computResid.f90 - ${SUMMA_ENGINE_DIR}/computJacob.f90 - ${SUMMA_ENGINE_DIR}/eval8summa.f90 - ${SUMMA_ENGINE_DIR}/summaSolve.f90 - ${SUMMA_ENGINE_DIR}/systemSolv.f90 - ${SUMMA_ENGINE_DIR}/computResidSundials.f90 - ${SUMMA_ENGINE_DIR}/eval8summaSundials.f90 - ${SUMMA_ENGINE_DIR}/computJacobSundials.f90 - ${SUMMA_ENGINE_DIR}/computSnowDepth.f90 - ${SUMMA_ENGINE_DIR}/summaSolveSundialsIDA.f90 - ${SUMMA_ENGINE_DIR}/systemSolvSundials.f90 - ${SUMMA_ENGINE_DIR}/varSubstep.f90 - ${SUMMA_ENGINE_DIR}/varSubstepSundials.f90 - ${SUMMA_ENGINE_DIR}/opSplittin.f90 - ${ENGINE_DIR}/sundials/coupled_em.f90) - - set(INTERFACE - ${ACTORS_DIR}/global/cppwrap_datatypes.f90 - ${ACTORS_DIR}/global/cppwrap_auxiliary.f90 - ${ACTORS_DIR}/global/cppwrap_metadata.f90) - - set(FILE_ACCESS_INTERFACE - ${FILE_ACCESS_DIR}/fortran_code/output_structure.f90 - ${FILE_ACCESS_DIR}/fortran_code/cppwrap_fileAccess.f90 - ${FILE_ACCESS_DIR}/fortran_code/read_attribute.f90 - ${FILE_ACCESS_DIR}/fortran_code/read_forcing.f90 - ${FILE_ACCESS_DIR}/fortran_code/read_param.f90 - ${FILE_ACCESS_DIR}/fortran_code/read_initcond.f90 - ${FILE_ACCESS_DIR}/fortran_code/writeOutputFromOutputStructure.f90 - ${FILE_ACCESS_DIR}/fortran_code/write_to_netcdf.f90) - - set(JOB_INTERFACE - ${JOB_ACTOR_DIR}/job_actor.f90) - - set(HRU_INTERFACE - ${HRU_ACTOR_DIR}/fortran_code/model_run.f90 - ${HRU_ACTOR_DIR}/fortran_code/setup_hru.f90 - ${HRU_ACTOR_DIR}/fortran_code/restart.f90 - ${HRU_ACTOR_DIR}/fortran_code/hru_actor.f90 - ${HRU_ACTOR_DIR}/fortran_code/init_hru_actor.f90 - ${HRU_ACTOR_DIR}/fortran_code/outputStrucWrite.f90 - ${HRU_ACTOR_DIR}/fortran_code/hru_writeOutput.f90) - - set(PRELIM - ${SUMMA_ENGINE_DIR}/conv_funcs.f90 - ${SUMMA_ENGINE_DIR}/sunGeomtry.f90 - ${SUMMA_ENGINE_DIR}/convE2Temp.f90 - ${ENGINE_DIR}/allocspaceActors.f90 - ${ENGINE_DIR}/alloc_fileAccess.f90 - ${SUMMA_ENGINE_DIR}/checkStruc.f90 - ${SUMMA_ENGINE_DIR}/childStruc.f90 - ${ENGINE_DIR}/ffile_info.f90 - ${ENGINE_DIR}/read_dimension.f90 - ${ENGINE_DIR}/read_pinit.f90 - ${SUMMA_ENGINE_DIR}/pOverwrite.f90 - ${SUMMA_ENGINE_DIR}/paramCheck.f90 - ${ENGINE_DIR}/check_icondActors.f90) - - set(NOAHMP - ${SUMMA_NOAHMP_DIR}/module_model_constants.F - ${SUMMA_NOAHMP_DIR}/module_sf_noahutl.F - ${SUMMA_NOAHMP_DIR}/module_sf_noahlsm.F - ${SUMMA_NOAHMP_DIR}/module_sf_noahmplsm.F) - - set(MODRUN - ${SUMMA_ENGINE_DIR}/indexState.f90 - ${SUMMA_ENGINE_DIR}/getVectorz.f90 - ${SUMMA_ENGINE_DIR}/t2enthalpy.f90 - ${SUMMA_ENGINE_DIR}/updateVars.f90 - ${SUMMA_ENGINE_DIR}/updateVarsSundials.f90 - ${SUMMA_ENGINE_DIR}/var_derive.f90 - ${ENGINE_DIR}/derivforce.f90 - ${SUMMA_ENGINE_DIR}/snowAlbedo.f90 - ${SUMMA_ENGINE_DIR}/canopySnow.f90 - ${SUMMA_ENGINE_DIR}/tempAdjust.f90 - ${SUMMA_ENGINE_DIR}/snwCompact.f90 - ${SUMMA_ENGINE_DIR}/layerMerge.f90 - ${SUMMA_ENGINE_DIR}/layerDivide.f90 - ${SUMMA_ENGINE_DIR}/volicePack.f90 - ${SUMMA_ENGINE_DIR}/qTimeDelay.f90) - - set(NETCDF - ${NETCDF_DIR}/netcdf_util.f90 - ${NETCDF_DIR}/def_output.f90 - ${NETCDF_DIR}/writeOutput.f90 - ${NETCDF_DIR}/read_icondActors.f90) - - set(DRIVER - ${DRIVER_DIR}/summaActors_type.f90 - ${DRIVER_DIR}/summaActors_util.f90 - ${DRIVER_DIR}/summaActors_globalData.f90 - ${DRIVER_DIR}/summaActors_alarms.f90) - - set(COMM_ALL - ${NRPROC} - ${DATAMS} - ${INTERFACE} - ${HOOKUP} - ${DEPENDS_ON_FILEMANAGER} - ${UTILMS}) - - set(SUMMA_ALL - ${NETCDF} - ${PRELIM} - ${MODRUN} - ${SOLVER} - ${DRIVER} - ${JOB_INTERFACE} - ${FILE_ACCESS_INTERFACE} - ${HRU_INTERFACE} - ${GRU_INTERFACE}) - - set(ACTORS_GLOBAL - ${ACTORS_DIR}/global/global.cpp - ${ACTORS_DIR}/global/timing_info.cpp - ${ACTORS_DIR}/global/message_atoms.cpp - ${ACTORS_DIR}/global/settings_functions.cpp - ${ACTORS_DIR}/global/auxiliary.cpp) - - set(SUMMA_ACTOR - ${ACTORS_DIR}/summa_actor/summa_actor.cpp - ${ACTORS_DIR}/summa_actor/summa_client.cpp - ${ACTORS_DIR}/summa_actor/summa_server.cpp - ${ACTORS_DIR}/summa_actor/summa_backup_server.cpp - ${ACTORS_DIR}/summa_actor/batch/batch.cpp - ${ACTORS_DIR}/summa_actor/batch/batch_container.cpp - ${ACTORS_DIR}/summa_actor/client/client.cpp - ${ACTORS_DIR}/summa_actor/client/client_container.cpp) - - set(HRU_ACTOR - ${ACTORS_DIR}/hru_actor/cpp_code/hru_actor.cpp) - - set(FILE_ACCESS_ACTOR - ${ACTORS_DIR}/file_access_actor/cpp_code/file_access_actor.cpp - ${ACTORS_DIR}/file_access_actor/cpp_code/forcing_file_info.cpp - ${ACTORS_DIR}/file_access_actor/cpp_code/output_container.cpp) - - set(JOB_ACTOR - ${ACTORS_DIR}/job_actor/job_actor.cpp - ${ACTORS_DIR}/job_actor/GRUinfo.cpp) - - set(MAIN - ${ACTORS_DIR}/main.cpp) - - add_library(SUMMA_NOAHMP OBJECT - ${NOAHMP} - ${NRUTIL}) - target_compile_options(SUMMA_NOAHMP PRIVATE ${SUMMA_NOAHMP_OPTIONS}) - add_library(SUMMA_COMM OBJECT - ${COMM_ALL}) - target_compile_options(SUMMA_COMM PRIVATE ${SUMMA_ALL_OPTIONS}) - target_include_directories(SUMMA_COMM PRIVATE ${SUMMA_INCLUDES}) - target_link_libraries(SUMMA_COMM PUBLIC ${SUMMA_LIBS}) - - # Build SUMMA Shared Library - add_library(summa SHARED - ${SUMMA_ALL}) - target_compile_options(summa PRIVATE ${SUMMA_ALL_OPTIONS}) - target_include_directories(summa PUBLIC ${SUMMA_INCLUDES}) - target_link_libraries(summa PUBLIC ${SUMMA_LIBS} SUMMA_COMM) - - # Build Summa-Actors executable - add_executable(${EXEC_NAME} - ${ACTORS_GLOBAL} - ${HRU_ACTOR} - ${FILE_ACCESS_ACTOR} - ${JOB_ACTOR} - ${SUMMA_ACTOR} - ${SUMMA_CLIENT} - ${SUMMA_SERVER} - ${MAIN}) - set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) - target_include_directories(${EXEC_NAME} PUBLIC ${SUMMA_ACTORS_INCLUDES}) - target_link_libraries( ${EXEC_NAME} ${SUMMA_ACTORS_LIBS}) -endfunction() \ No newline at end of file diff --git a/build/cmake/load_modules.sh b/build/cmake/load_modules.sh deleted file mode 100755 index 245cd9837..000000000 --- a/build/cmake/load_modules.sh +++ /dev/null @@ -1,7 +0,0 @@ -#! /bin/bash - -#### load modules if using Compute Canada or Copernicus #### -module load gcc/9.3.0 -module load netcdf-fortran -module load openblas -module load caf \ No newline at end of file diff --git a/sundials/installation.txt b/sundials/installation.txt index ba0ddc20a..d573cd19e 100644 --- a/sundials/installation.txt +++ b/sundials/installation.txt @@ -21,7 +21,7 @@ NOTE if you need to recompile after a system upgrade, delete the contents of the -DCMAKE_Fortran_COMPILER=$(YOUR_GFORTRAN) You can do 4 and 5 by running from inside buildir: -cp ../../summa/build/build_cmakeSundials build_cmake +cp ../../summa/build/makefiles/build_cmakeSundials build_cmake ./build_cmake From 0759578e855e8304fd11a8537b5ab9e64ffca485 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Mar 2023 14:05:47 +0900 Subject: [PATCH 0606/1472] setting up celia test for ngen --- build/cmake/CMakeLists.txt | 12 +- build/cmake/bmi.cmake | 2 +- build/source/driver/summa_driver.f90 | 8 +- sundials_bmi/installation.txt | 4 +- .../input_data/celia1990/celia1990_forcing.nc | Bin 0 -> 11340 bytes .../output/non_actors/celia1990/.gitkeep | 1 + .../celia_test/settings/meta/GENPARM.TBL | 36 +++ .../celia_test/settings/meta/MPTABLE.TBL | 284 ++++++++++++++++++ .../celia_test/settings/meta/SOILPARM.TBL | 59 ++++ .../celia_test/settings/meta/VEGPARM.TBL | 119 ++++++++ .../common/summa_zBasinParamInfo.txt | 35 +++ .../celia1990/common/summa_zDecisions.txt | 168 +++++++++++ .../common/summa_zForcingFileList.txt | 8 + .../celia1990/common/summa_zInitialCond.nc | Bin 0 -> 20783 bytes .../common/summa_zLocalAttributes.nc | Bin 0 -> 2156 bytes .../common/summa_zLocalParamInfo.txt | 226 ++++++++++++++ .../celia1990/common/summa_zParamTrial.nc | Bin 0 -> 23995 bytes .../summa_fileManager_celia1990.txt | 20 ++ ...ample_realization _config_w_summa_bmi.json | 27 ++ ..._realization _config_w_summa_bmi__mac.json | 26 ++ 20 files changed, 1030 insertions(+), 5 deletions(-) create mode 100644 test_ngen/celia_test/input_data/celia1990/celia1990_forcing.nc create mode 100644 test_ngen/celia_test/output/non_actors/celia1990/.gitkeep create mode 100644 test_ngen/celia_test/settings/meta/GENPARM.TBL create mode 100644 test_ngen/celia_test/settings/meta/MPTABLE.TBL create mode 100644 test_ngen/celia_test/settings/meta/SOILPARM.TBL create mode 100644 test_ngen/celia_test/settings/meta/VEGPARM.TBL create mode 100644 test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zBasinParamInfo.txt create mode 100644 test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zDecisions.txt create mode 100644 test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zForcingFileList.txt create mode 100644 test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zInitialCond.nc create mode 100644 test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zLocalAttributes.nc create mode 100644 test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zLocalParamInfo.txt create mode 100644 test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zParamTrial.nc create mode 100644 test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt create mode 100644 test_ngen/example_realization _config_w_summa_bmi.json create mode 100644 test_ngen/example_realization _config_w_summa_bmi__mac.json diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 26d9a5883..d0c2025c5 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -1,6 +1,8 @@ ccmake_minimum_required(VERSION 3.10) enable_language( Fortran ) -#add_subdirectory(../iso_c_fortran_bmi ${CMAKE_BINARY_DIR}/iso_c_bmi) + +#Get the iso_c_fortran binding module to build as part of this build +add_subdirectory(../iso_c_fortran_bmi ${CMAKE_BINARY_DIR}/iso_c_bmi) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../../cmake/") @@ -88,6 +90,14 @@ endif() message("\nBuilding SUMMA\n") compile_with_bmi($(ROOT_DIR), $(F_MASTER), $(DIR_SUNDIALS), $(INCLUDES), $(LIBRARIES), $(INC_SUNDIALS), $(LIB_SUNDIALS), $(FLAGS_ALL), $(FLAGS_NOAH)) + +target_link_libraries(summabmi PUBLIC iso_c_bmi) +target_compile_options(summabmi PUBLIC -cpp -DNGEN_ACTIVE) + +set_target_properties(summabmi PROPERTIES VERSION ${PROJECT_VERSION}) + +include(GNUInstallDirs) + install(TARGETS summabmi LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) diff --git a/build/cmake/bmi.cmake b/build/cmake/bmi.cmake index 2fe4b869f..029e41a0c 100644 --- a/build/cmake/bmi.cmake +++ b/build/cmake/bmi.cmake @@ -188,7 +188,7 @@ function(compile_with_ida ROOT_DIR, F_MASTER, DIR_SUNDIALS, INCLUDES, LIBRARIES, target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES}) target_link_libraries(SUMMA_COMM PUBLIC ${LIBRARIES}) - # Build SUMMA Shared Library, don't need BMI libraries in ngen + # Build SUMMA Shared Library, add BMI libraries outside this function if(WIN32) add_library(summabmi ${SUMMA_ALL}) else diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index dfcc2fd8e..ac926a5df 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -26,7 +26,13 @@ module summa_driver ! data types USE,intrinsic :: iso_c_binding, only: c_ptr, c_loc, c_f_pointer USE nrtype ! variable types, etc. - use bmif_2_0 ! BMI libraries +! NGEN_ACTIVE is to be set when running in the Nextgen framework +! https://github.com/NOAA-OWP/ngen +#ifdef NGEN_ACTIVE + use bmif_2_0_iso ! BMI libraries NexGen +#else + use bmif_2_0 ! BMI libraries standard +#endif USE summa_type, only: summa1_type_dec ! master summa data type ! subroutines and functions: model setup USE summa_init, only: summa_initialize ! used to allocate/initialize summa data structures diff --git a/sundials_bmi/installation.txt b/sundials_bmi/installation.txt index 135957a53..a6a1cb4d4 100644 --- a/sundials_bmi/installation.txt +++ b/sundials_bmi/installation.txt @@ -18,7 +18,7 @@ NOTE if you need to recompile after a system upgrade, delete the contents of the -DCMAKE_Fortran_COMPILER=$(YOUR_GFORTRAN) You can do 3 and 4 by running from inside buildir: -cp ../../summa/build/build_cmakeBMI build_cmake +cp ../../summa/build/makefiles/build_cmakeBMI build_cmake ./build_cmake @@ -48,7 +48,7 @@ NOTE if you need to recompile after a system upgrade, delete the contents of the -DCMAKE_Fortran_COMPILER=$(YOUR_GFORTRAN) You can do 9 and 10 by running from inside buildir: -cp ../../summa/build/build_cmakeSundials build_cmake +cp ../../summa/build/makefiles/build_cmakeSundials build_cmake ./build_cmake 11. If the above went well, staying in the buildir directory run diff --git a/test_ngen/celia_test/input_data/celia1990/celia1990_forcing.nc b/test_ngen/celia_test/input_data/celia1990/celia1990_forcing.nc new file mode 100644 index 0000000000000000000000000000000000000000..c57e86d96e831e474f8f0166db7b8c16fa6b3719 GIT binary patch literal 11340 zcmb`}Pi)&%7y$6@PuCDQ3`9?$>S-VZ5p9!h8LUFWQ^$ZZ*w|Wz*rddbUlWJKG4^w- zPEd*j60sA;B?tZ-df8!N;((~&z#$TxdSOE9iAzw05ak3?1PBhi&#r^tlxcbWV#SG{ z?f2gAdwzcTW=@^nH#9W#A>ZSC-_Q301sh&rfPold3_N%c?PHJLR$(e9uvv&G1V0Bnyu+(9EQ6m<~GyNA`-r3Y6(N zzm%gTT}_=hk(~6~9x3QHt5_>xKZQAmk_*%%Q%{ho)O7mjbm}OXN~Vra6C-C)LoHJ> zt`Xz8^Yf4MaL7a-9d*0RELNiGim>kq;Ya}E5Ami;%bAOF4={O)_st;fI* zjZsTwcMf47bE*U}m?q;r`jfAp7~-$z;UyvVc*+l^*L!l`Ig93WQ)j`O5jH;%Gd#f& z+oZXI1FyCJ{@-cd4ChIHm)mpf6VRT%sOJc8RM&L2a@z^y$2^Gn^CrZxtn*dagsIeD z9Odq?_l)CVXpitgZ^QFXpIdnGhu^RN8telU0fO9l^i) z)!5fB|Mp)0^{c?|uAj==N>J`Q{%TzC%lAJj_@(2o5&(zmzj9^uF#m`7Ln8i!;Fo^> z)wJN3o_|$E@JrAC>a5_ul>G9-@E7a$DKv`IUlqByIG{ai1a&1{~FToBK-}de;w&>BK;dtzkL4h zwxWLd{NLS-`g#6;{hwb-p2YjVCHR-x#nr!md`O~xTkuQg|L%_9m(G9Pj^LNhzdhi0 z*H8KU--`==`Tj=*zjXW_5&Y8mzn2jF()qua7W~rrzo!U(>HOcD75viqzsLEQ_20`z z{csFh|L>1ijQWGxQuQ6Af6e#q`Ty5*k^TnKzmD`bk^T*&zlHQ~BK=!Pe;euFLHav5 ze+~HE_1`Z*x$peH7RUK(qewpi^WO9SS_0`$BmD}}Ka2EpK4$%|Ws&|O((fSsYe>I~ z^f!?Hb)>(E^lwD{^7((Q74^&K|FzAiUq1h@Z3%wq{J+)~{L=Y%i}>pYr*CJudj=`yUnj((%`c;Fr$->j}Xxo&VRzt2S|LfVPAC6&@@E4>0pte+f2kBq)y?f68>n_sYK>F8_{wC7Df%LbK{!OHR a3+Zno{X0m12j~9)_}%s2FF?7^{Qp1cXHjYZ literal 0 HcmV?d00001 diff --git a/test_ngen/celia_test/output/non_actors/celia1990/.gitkeep b/test_ngen/celia_test/output/non_actors/celia1990/.gitkeep new file mode 100644 index 000000000..8d1c8b69c --- /dev/null +++ b/test_ngen/celia_test/output/non_actors/celia1990/.gitkeep @@ -0,0 +1 @@ + diff --git a/test_ngen/celia_test/settings/meta/GENPARM.TBL b/test_ngen/celia_test/settings/meta/GENPARM.TBL new file mode 100644 index 000000000..17fc9172b --- /dev/null +++ b/test_ngen/celia_test/settings/meta/GENPARM.TBL @@ -0,0 +1,36 @@ +General Parameters +SLOPE_DATA +9 +0.1 +0.6 +1.0 +0.35 +0.55 +0.8 +0.63 +0.0 +0.0 +SBETA_DATA +-2.0 +FXEXP_DATA +2.0 +CSOIL_DATA +2.00E+6 +SALP_DATA +2.6 +REFDK_DATA +2.0E-6 +REFKDT_DATA +3.0 +FRZK_DATA +0.15 +ZBOT_DATA +-8.0 +CZIL_DATA +0.1 +SMLOW_DATA +0.5 +SMHIGH_DATA +3.0 +LVCOEF_DATA +0.5 diff --git a/test_ngen/celia_test/settings/meta/MPTABLE.TBL b/test_ngen/celia_test/settings/meta/MPTABLE.TBL new file mode 100644 index 000000000..0f0c59c02 --- /dev/null +++ b/test_ngen/celia_test/settings/meta/MPTABLE.TBL @@ -0,0 +1,284 @@ +&noah_mp_usgs_veg_categories + VEG_DATASET_DESCRIPTION = "USGS" + NVEG = 27 +/ +&noah_mp_usgs_parameters + ! NVEG = 27 + ! 1: Urban and Built-Up Land + ! 2: Dryland Cropland and Pasture + ! 3: Irrigated Cropland and Pasture + ! 4: Mixed Dryland/Irrigated Cropland and Pasture + ! 5: Cropland/Grassland Mosaic + ! 6: Cropland/Woodland Mosaic + ! 7: Grassland + ! 8: Shrubland + ! 9: Mixed Shrubland/Grassland + ! 10: Savanna + ! 11: Deciduous Broadleaf Forest + ! 12: Deciduous Needleleaf Forest + ! 13: Evergreen Broadleaf Forest + ! 14: Evergreen Needleleaf Forest + ! 15: Mixed Forest + ! 16: Water Bodies + ! 17: Herbaceous Wetland + ! 18: Wooded Wetland + ! 19: Barren or Sparsely Vegetated + ! 20: Herbaceous Tundra + ! 21: Wooded Tundra + ! 22: Mixed Tundra + ! 23: Bare Ground Tundra + ! 24: Snow or Ice + ! 25: Playa + ! 26: Lava + ! 27: White Sand + + ISURBAN = 1 + ISWATER = 16 + ISBARREN = 19 + ISSNOW = 24 + EBLFOREST = 13 + + !--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + ! 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 + !--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + CH2OP = 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, + DLEAF = 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, + Z0MVT = 1.00, 0.06, 0.06, 0.06, 0.06, 0.15, 0.06, 0.06, 0.06, 0.86, 0.80, 0.85, 1.10, 1.09, 0.80, 0.00, 0.06, 0.05, 0.00, 0.04, 0.06, 0.06, 0.03, 0.00, 0.01, 0.00, 0.00, + HVT = 15.0, 0.50, 0.50, 0.50, 0.50, 1.25, 0.50, 0.50, 0.50, 16.0, 16.0, 18.0, 20.0, 20.0, 16.0, 0.00, 0.50, 0.80, 0.00, 0.50, 0.80, 0.80, 0.50, 0.00, 0.10, 0.00, 0.00, + HVB = 1.00, 0.10, 0.10, 0.10, 0.10, 0.15, 0.05, 0.10, 0.10, 3.00, 3.50, 3.00, 4.00, 3.50, 3.00, 0.00, 0.05, 0.10, 0.00, 0.10, 0.10, 0.10, 0.10, 0.00, 0.10, 0.00, 0.00, + DEN = 0.01, 25.0, 25.0, 25.0, 25.0, 25.0, 100., 10.0, 10.0, 0.02, 0.10, 0.28, 0.02, 0.28, 0.10, 0.01, 10.0, 0.10, 0.01, 1.00, 1.00, 1.00, 1.00, 0.00, 0.01, 0.01, 0.01, + RC = 1.00, 0.08, 0.08, 0.08, 0.08, 0.08, 0.03, 0.12, 0.12, 3.00, 1.40, 1.20, 3.60, 1.20, 1.40, 0.01, 0.10, 1.40, 0.01, 0.30, 0.30, 0.30, 0.30, 0.00, 0.01, 0.01, 0.01, + + ! Row 1: Vis + ! Row 2: Near IR + RHOL = 0.00, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.07, 0.10, 0.10, 0.10, 0.07, 0.10, 0.07, 0.10, 0.00, 0.11, 0.10, 0.00, 0.10, 0.10, 0.10, 0.10, 0.00, 0.10, 0.00, 0.00, + 0.00, 0.58, 0.58, 0.58, 0.58, 0.58, 0.58, 0.35, 0.45, 0.45, 0.45, 0.35, 0.45, 0.35, 0.45, 0.00, 0.58, 0.45, 0.00, 0.45, 0.45, 0.45, 0.45, 0.00, 0.45, 0.00, 0.00, + + ! Row 1: Vis + ! Row 2: Near IR + RHOS = 0.00, 0.36, 0.36, 0.36, 0.36, 0.36, 0.36, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.00, 0.36, 0.16, 0.00, 0.16, 0.16, 0.16, 0.16, 0.00, 0.16, 0.00, 0.00, + 0.00, 0.58, 0.58, 0.58, 0.58, 0.58, 0.58, 0.39, 0.39, 0.39, 0.39, 0.39, 0.39, 0.39, 0.39, 0.00, 0.58, 0.39, 0.00, 0.39, 0.39, 0.39, 0.39, 0.00, 0.39, 0.00, 0.00, + + ! Row 1: Vis + ! Row 2: Near IR + TAUL = 0.00, 0.07, 0.07, 0.07, 0.07, 0.07, 0.07, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.00, 0.07, 0.05, 0.00, 0.05, 0.05, 0.05, 0.05, 0.00, 0.05, 0.00, 0.00, + 0.00, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.10, 0.10, 0.25, 0.25, 0.10, 0.25, 0.10, 0.25, 0.00, 0.25, 0.25, 0.00, 0.25, 0.25, 0.25, 0.25, 0.00, 0.25, 0.00, 0.00, + + ! Row 1: Vis + ! Row 2: Near IR + TAUS = 0.000, 0.220, 0.220, 0.220, 0.220, 0.220, 0.220, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.000, 0.220, 0.001, 0.000, 0.220, 0.001, 0.001, 0.001, 0.000, 0.001, 0.000, 0.000, + 0.000, 0.380, 0.380, 0.380, 0.380, 0.380, 0.380, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.000, 0.380, 0.001, 0.000, 0.380, 0.001, 0.001, 0.001, 0.000, 0.001, 0.000, 0.000, + + XL = 0.000, -0.30, -0.30, -0.30, -0.30, -0.30, -0.30, 0.010, 0.250, 0.010, 0.250, 0.010, 0.010, 0.010, 0.250, 0.000, -0.30, 0.250, 0.000, -0.30, 0.250, 0.250, 0.250, 0.000, 0.250, 0.000, 0.000, + CWPVT = 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, + C3PSN = 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + KC25 = 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, + AKC = 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, + KO25 = 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, + AKO = 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, + AVCMX = 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, + AQE = 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + + LTOVRC= 0.0, 1.6, 1.8, 1.2, 1.2, 1.30, 0.50, 0.65, 0.70, 0.65, 0.55, 0.2, 0.55, 0.5, 0.5, 0.0, 1.4, 1.4, 0.0, 1.2, 1.3, 1.4, 1.0, 0.0, 1.0, 0.0, 0.0, + DILEFC= 0.00, 0.50, 0.50, 0.50, 0.35, 0.20, 0.20, 0.20, 0.50, 0.50, 0.60, 1.80, 0.50, 1.20, 0.80, 0.00, 0.40, 0.40, 0.00, 0.40, 0.30, 0.40, 0.30, 0.00, 0.30, 0.00, 0.00, + DILEFW= 0.00, 0.20, 0.20, 0.20, 0.20, 0.20, 0.10, 0.20, 0.20, 0.50, 0.20, 0.20, 4.00, 0.20, 0.20, 0.00, 0.20, 0.20, 0.00, 0.20, 0.20, 0.20, 0.20, 0.00, 0.20, 0.00, 0.00, + RMF25 = 0.00, 1.00, 1.40, 1.45, 1.45, 1.45, 1.80, 0.26, 0.26, 0.80, 3.00, 4.00, 0.65, 3.00, 3.00, 0.00, 3.20, 3.20, 0.00, 3.20, 3.00, 3.00, 3.00, 0.00, 3.00, 0.00, 0.00, + SLA = 60, 80, 80, 80, 80, 80, 60, 60, 60, 50, 80, 80, 80, 80, 80, 0, 80, 80, 0, 80, 80, 80, 80, 0, 80, 0, 0, + FRAGR = 0.00, 0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.10, 0.20, 0.10, 0.10, 0.00, 0.10, 0.10, 0.10, 0.10, 0.10, 0.10, 0.10, 0.00, 0.10, 0.00, 0.00, + TMIN = 0, 273, 273, 273, 273, 273, 273, 273, 273, 273, 273, 268, 273, 265, 268, 0, 268, 268, 0, 268, 268, 268, 268, 0, 268, 0, 0, + VCMX25= 0.00, 80.0, 80.0, 80.0, 60.0, 70.0, 40.0, 40.0, 40.0, 40.0, 60.0, 60.0, 60.0, 50.0, 55.0, 0.00, 50.0, 50.0, 0.00, 50.0, 50.0, 50.0, 50.0, 0.00, 50.0, 0.00, 0.00, + TDLEF = 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 268, 278, 278, 268, 0, 268, 268, 0, 268, 268, 268, 268, 0, 268, 0, 0, + BP = 1.E15, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 1.E15, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 1.E15, 2.E3, 1.E15, 1.E15, + MP = 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 6., 9., 6., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., + QE25 = 0., 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.00, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.00, 0.06, 0.00, 0.00, + RMS25 = 0.00, 0.10, 0.10, 0.10, 0.10, 0.10, 0.10, 0.10, 0.10, 0.32, 0.10, 0.64, 0.30, 0.90, 0.80, 0.00, 0.10, 0.10, 0.00, 0.10, 0.10, 0.10, 0.00, 0.00, 0.00, 0.00, 0.00, + RMR25 = 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 1.20, 0.00, 0.00, 0.01, 0.01, 0.05, 0.05, 0.36, 0.03, 0.00, 0.00, 0.00, 0.00, 2.11, 2.11, 2.11, 0.00, 0.00, 0.00, 0.00, 0.00, + ARM = 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + FOLNMX= 0.00, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 0.00, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 0.00, 1.5, 0.00, 0.00, + WDPOOL= 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 0.00, 0.00, 1.00, 0.00, 0.00, 1.00, 1.00, 0.00, 0.00, 0.00, 0.00, 0.00, + WRRAT = 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 3.00, 3.00, 3.00, 30.0, 30.0, 30.0, 30.0, 30.0, 0.00, 0.00, 30.0, 0.00, 0.00, 3.00, 3.00, 0.00, 0.00, 0.00, 0.00, 0.00, + MRP = 0.00, 0.23, 0.23, 0.23, 0.23, 0.23, 0.17, 0.19, 0.19, 0.40, 0.40, 0.37, 0.23, 0.37, 0.30, 0.00, 0.17, 0.40, 0.00, 0.17, 0.23, 0.20, 0.00, 0.00, 0.20, 0.00, 0.00, + +! Monthly values, one row for each month: + SAIM = 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.3, 0.1, 0.2, 0.1, 0.4, 0.3, 0.5, 0.4, 0.2, 0.0, 0.1, 0.1, 0.0, 0.1, 0.1, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.3, 0.1, 0.2, 0.1, 0.4, 0.3, 0.5, 0.4, 0.2, 0.0, 0.1, 0.1, 0.0, 0.1, 0.1, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.3, 0.1, 0.2, 0.1, 0.4, 0.3, 0.5, 0.4, 0.2, 0.0, 0.1, 0.1, 0.0, 0.1, 0.1, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.3, 0.1, 0.2, 0.1, 0.4, 0.4, 0.5, 0.3, 0.2, 0.0, 0.1, 0.1, 0.0, 0.1, 0.1, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.1, 0.2, 0.3, 0.1, 0.2, 0.1, 0.4, 0.4, 0.5, 0.4, 0.2, 0.0, 0.1, 0.1, 0.0, 0.1, 0.1, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.2, 0.2, 0.4, 0.2, 0.3, 0.1, 0.4, 0.7, 0.5, 0.5, 0.4, 0.0, 0.2, 0.2, 0.0, 0.2, 0.2, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.3, 0.3, 0.8, 0.2, 0.5, 0.1, 0.9, 1.3, 0.5, 0.5, 0.4, 0.0, 0.4, 0.4, 0.0, 0.2, 0.2, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.5, 0.2, 1.3, 0.1, 0.8, 0.1, 1.2, 1.2, 0.5, 0.6, 0.5, 0.0, 0.6, 0.6, 0.0, 0.3, 0.3, 0.3, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.4, 0.1, 1.1, 0.1, 0.5, 0.1, 1.6, 1.0, 0.5, 0.6, 0.5, 0.0, 0.5, 0.5, 0.0, 0.3, 0.3, 0.3, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.1, 0.1, 0.4, 0.1, 0.2, 0.1, 1.4, 0.8, 0.5, 0.7, 0.6, 0.0, 0.2, 0.2, 0.0, 0.2, 0.2, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.4, 0.1, 0.2, 0.1, 0.6, 0.6, 0.5, 0.6, 0.5, 0.0, 0.2, 0.2, 0.0, 0.2, 0.2, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.4, 0.1, 0.2, 0.1, 0.4, 0.5, 0.5, 0.5, 0.3, 0.0, 0.1, 0.1, 0.0, 0.1, 0.1, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, + + LAIM = 0.0, 0.0, 0.4, 0.4, 0.4, 0.0, 0.4, 1.0, 1.0, 1.0, 0.0, 0.0, 3.5, 1.6, 1.0, 0.0, 0.4, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.5, 0.5, 0.0, 0.5, 1.0, 1.0, 1.0, 0.0, 0.0, 3.5, 1.6, 1.0, 0.0, 0.5, 0.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.6, 0.6, 0.6, 0.0, 0.6, 1.0, 1.0, 1.0, 0.3, 0.0, 3.5, 1.6, 1.0, 0.0, 0.6, 0.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.7, 0.7, 0.7, 0.5, 0.7, 1.0, 1.5, 1.0, 1.2, 0.6, 3.5, 1.6, 1.0, 0.0, 0.7, 0.4, 0.0, 0.2, 0.2, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 1.2, 1.2, 1.2, 1.5, 1.2, 1.0, 2.0, 1.0, 3.0, 1.2, 3.5, 5.3, 2.3, 0.0, 1.2, 0.5, 0.0, 0.5, 0.5, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 2.0, 3.0, 3.0, 3.0, 2.5, 3.0, 1.0, 2.5, 1.0, 4.7, 2.0, 3.5, 5.5, 3.5, 0.0, 3.0, 0.7, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 3.0, 3.5, 3.5, 3.5, 3.5, 3.5, 1.0, 3.0, 1.0, 4.5, 2.6, 3.5, 5.3, 4.3, 0.0, 3.5, 1.7, 0.0, 2.0, 2.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 3.0, 1.5, 1.5, 1.5, 3.5, 1.5, 1.0, 2.5, 1.0, 3.4, 1.7, 3.5, 5.3, 3.3, 0.0, 1.5, 3.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 1.5, 0.7, 0.7, 0.7, 2.0, 0.7, 1.0, 1.5, 1.0, 1.2, 1.0, 3.5, 4.2, 2.2, 0.0, 0.7, 2.5, 0.0, 0.5, 0.5, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.6, 0.6, 0.6, 1.0, 0.6, 1.0, 1.0, 1.0, 0.3, 0.5, 3.5, 2.2, 1.2, 0.0, 0.6, 1.6, 0.0, 0.2, 0.2, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.5, 0.5, 0.0, 0.5, 1.0, 1.0, 1.0, 0.0, 0.2, 3.5, 2.2, 1.2, 0.0, 0.5, 0.8, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.4, 0.4, 0.4, 0.0, 0.4, 1.0, 1.0, 1.0, 0.0, 0.0, 3.5, 2.2, 1.2, 0.0, 0.4, 0.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + + SLAREA=0.0228,0.0200,0.0200,0.0295,0.0223,0.0277,0.0060,0.0227,0.0188,0.0236,0.0258,0.0200,0.0200,0.0090,0.0223,0.0422,0.0390, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, + +! Five types, one row for each type. + EPS = 41.87, 0.00, 0.00, 2.52, 0.04, 17.11, 0.02, 21.62, 0.11, 22.80, 46.86, 0.00, 0.00, 0.46, 30.98, 2.31, 1.63, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.98, 0.00, 0.00, 0.16, 0.09, 0.28, 0.05, 0.92, 0.22, 0.59, 0.38, 0.00, 0.00, 3.34, 0.96, 1.47, 1.07, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 1.82, 0.00, 0.00, 0.23, 0.05, 0.81, 0.03, 1.73, 1.26, 1.37, 1.84, 0.00, 0.00, 1.85, 1.84, 1.70, 1.21, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, +/ + + +&noah_mp_modis_veg_categories + VEG_DATASET_DESCRIPTION = "modified igbp modis noah" + NVEG = 20 +/ + +&noah_mp_modis_parameters +! 1 'Evergreen Needleleaf Forest' -> USGS 14 +! 2, 'Evergreen Broadleaf Forest' -> USGS 13 +! 3, 'Deciduous Needleleaf Forest' -> USGS 12 +! 4, 'Deciduous Broadleaf Forest' -> USGS 11 +! 5, 'Mixed Forests' -> USGS 15 +! 6, 'Closed Shrublands' -> USGS 8 "shrubland" +! 7, 'Open Shrublands' -> USGS 9 "shrubland/grassland" +! 8, 'Woody Savannas' -> USGS 8 "shrubland" +! 9, 'Savannas' -> USGS 10 +! 10, 'Grasslands' -> USGS 7 +! 11 'Permanent wetlands' -> avg of USGS 17 and 18 (herb. wooded wetland) +! 12, 'Croplands' -> USGS 2 "dryland cropland" +! 13, 'Urban and Built-Up' -> USGS 1 +! 14 'cropland/natural vegetation mosaic' -> USGS 5 "cropland/grassland" +! 15, 'Snow and Ice' -> USGS 24 +! 16, 'Barren or Sparsely Vegetated' -> USGS 19 +! 17, 'Water' -> USGS 16 +! 18, 'Wooded Tundra' -> USGS 21 +! 19, 'Mixed Tundra' -> USGS 22 +! 20, 'Barren Tundra' -> USGS 23 + + ISURBAN = 13 + ISWATER = 17 + ISBARREN = 16 + ISSNOW = 15 + EBLFOREST = 2 + + !--------------------------------------------------------------------------------------------------------------------------------------------------------------------- + ! 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 + !--------------------------------------------------------------------------------------------------------------------------------------------------------------------- + CH2OP = 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, + DLEAF = 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, + Z0MVT = 1.09, 1.10, 0.85, 0.80, 0.80, 0.06, 0.06, 0.06, 0.86, 0.06, 0.055, 0.06, 1.00, 0.06, 0.00, 0.00, 0.00, 0.06, 0.06, 0.03, +! Z0MVT = 0.50, 0.50, 0.50, 0.50, 0.50, 0.05, 0.06, 0.05, 0.15, 0.12, 0.30, 0.15, 0.80, 0.14, 0.00, 0.01, 0.00, 0.30, 0.15, 0.10, + HVT = 20.0, 20.0, 18.0, 16.0, 16.0, 0.50, 0.50, 0.50, 16.0, 0.50, 0.65, 0.50, 15.0, 0.50, 0.00, 0.00, 0.00, 0.80, 0.80, 0.50, + HVB = 8.50, 8.00, 7.00, 11.5, 10.0, 0.10, 0.10, 0.10, 5.00, 0.05, 0.075, 0.10, 1.00, 0.10, 0.00, 0.00, 0.00, 0.10, 0.10, 0.10, + DEN = 0.28, 0.02, 0.28, 0.10, 0.10, 10.0, 10.0, 10.0, 0.02, 100., 5.05, 25.0, 0.01, 25.0, 0.00, 0.01, 0.01, 1.00, 1.00, 1.00, + RC = 1.20, 3.60, 1.20, 1.40, 1.40, 0.12, 0.12, 0.12, 3.00, 0.03, 0.75, 0.08, 1.00, 0.08, 0.00, 0.01, 0.01, 0.30, 0.30, 0.30, + + ! Row 1: Vis + ! Row 2: Near IR + RHOL = 0.07, 0.10, 0.07, 0.10, 0.10, 0.07, 0.10, 0.07, 0.10, 0.11, 0.105, 0.11, 0.00, 0.11, 0.00, 0.00, 0.00, 0.10, 0.10, 0.10, + 0.35, 0.45, 0.35, 0.45, 0.45, 0.35, 0.45, 0.35, 0.45, 0.58, 0.515, 0.58, 0.00, 0.58, 0.00, 0.00, 0.00, 0.45, 0.45, 0.45, + + ! Row 1: Vis + ! Row 2: Near IR + RHOS = 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.36, 0.26, 0.36, 0.00, 0.36, 0.00, 0.00, 0.00, 0.16, 0.16, 0.16, + 0.39, 0.39, 0.39, 0.39, 0.39, 0.39, 0.39, 0.39, 0.39, 0.58, 0.485, 0.58, 0.00, 0.58, 0.00, 0.00, 0.00, 0.39, 0.39, 0.39, + + ! Row 1: Vis + ! Row 2: Near IR + TAUL = 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.07, 0.06, 0.07, 0.00, 0.07, 0.00, 0.00, 0.00, 0.05, 0.05, 0.05, + 0.10, 0.25, 0.10, 0.25, 0.25, 0.10, 0.10, 0.10, 0.25, 0.25, 0.25, 0.25, 0.00, 0.25, 0.00, 0.00, 0.00, 0.25, 0.25, 0.25, + + ! Row 1: Vis + ! Row 2: Near IR + TAUS = 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.220, 0.1105, 0.220, 0.000, 0.220, 0.000, 0.000, 0.000, 0.001, 0.001, 0.001, + 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.380, 0.1905, 0.380, 0.000, 0.380, 0.000, 0.000, 0.000, 0.001, 0.001, 0.001, + + XL = 0.010, 0.010, 0.010, 0.250, 0.250, 0.010, 0.250, 0.010, 0.010, -0.30, -0.025, -0.30, 0.000, -0.30, 0.000, 0.000, 0.000, 0.250, 0.250, 0.250, + CWPVT = 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, +! CWPVT = 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, + C3PSN = 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + KC25 = 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, + AKC = 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, + KO25 = 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, + AKO = 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, + AVCMX = 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, + AQE = 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + + LTOVRC= 0.5, 0.55, 0.2, 0.55, 0.5, 0.65, 0.70, 0.65, 0.65, 0.50, 1.4, 1.6, 0.0, 1.2, 0.0, 0.0, 0.0, 1.3, 1.4, 1.0, + DILEFC= 1.20, 0.50, 1.80, 0.60, 0.80, 0.20, 0.50, 0.20, 0.50, 0.20, 0.4, 0.50, 0.00, 0.35, 0.00, 0.00, 0.00, 0.30, 0.40, 0.30, + DILEFW= 0.20, 4.00, 0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.50, 0.10, 0.2, 0.20, 0.00, 0.20, 0.00, 0.00, 0.00, 0.20, 0.20, 0.20, + RMF25 = 3.00, 0.65, 4.00, 3.00, 3.00, 0.26, 0.26, 0.26, 0.80, 1.80, 3.2, 1.00, 0.00, 1.45, 0.00, 0.00, 0.00, 3.00, 3.00, 3.00, + SLA = 80, 80, 80, 80, 80, 60, 60, 60, 50, 60, 80, 80, 60, 80, 0, 0, 0, 80, 80, 80, + FRAGR = 0.10, 0.20, 0.10, 0.20, 0.10, 0.20, 0.20, 0.20, 0.20, 0.20, 0.1, 0.20, 0.00, 0.20, 0.00, 0.10, 0.00, 0.10, 0.10, 0.10, + TMIN = 265, 273, 268, 273, 268, 273, 273, 273, 273, 273, 268, 273, 0, 273, 0, 0, 0, 268, 268, 268, + VCMX25= 50.0, 60.0, 60.0, 60.0, 55.0, 40.0, 40.0, 40.0, 40.0, 40.0, 50.0, 80.0, 0.00, 60.0, 0.00, 0.00, 0.00, 50.0, 50.0, 50.0, + TDLEF = 278, 278, 268, 278, 268, 278, 278, 278, 278, 278, 268, 278, 278, 278, 0, 0, 0, 268, 268, 268, + BP = 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 1.E15, 2.E3, 1.E15, 2.E3, 1.E15, 2.E3, 2.E3, 2.E3, + MP = 6., 9., 6., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., + QE25 = 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.00, 0.06, 0.00, 0.06, 0.00, 0.06, 0.06, 0.06, + RMS25 = 0.90, 0.30, 0.64, 0.10, 0.80, 0.10, 0.10, 0.10, 0.32, 0.10, 0.10, 0.10, 0.00, 0.10, 0.00, 0.00, 0.00, 0.10, 0.10, 0.00, + RMR25 = 0.36, 0.05, 0.05, 0.01, 0.03, 0.00, 0.00, 0.00, 0.01, 1.20, 0.0, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 2.11, 2.11, 0.00, + ARM = 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + FOLNMX= 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 0.00, 1.5, 0.00, 1.5, 0.00, 1.5, 1.5, 1.5, + WDPOOL= 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 0.00, 0.5, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 1.00, 1.00, 0.00, + WRRAT = 30.0, 30.0, 30.0, 30.0, 30.0, 3.00, 3.00, 3.00, 3.00, 0.00, 15.0, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 3.00, 3.00, 0.00, + MRP = 0.37, 0.23, 0.37, 0.40, 0.30, 0.19, 0.19, 0.19, 0.40, 0.17, 0.285, 0.23, 0.00, 0.23, 0.00, 0.00, 0.00, 0.23, 0.20, 0.00, + +! Monthly values, one row for each month: + SAIM = 0.4, 0.5, 0.3, 0.4, 0.2, 0.1, 0.2, 0.1, 0.1, 0.3, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.1, 0.0, + 0.4, 0.5, 0.3, 0.4, 0.2, 0.1, 0.2, 0.1, 0.1, 0.3, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.1, 0.0, + 0.4, 0.5, 0.3, 0.4, 0.2, 0.1, 0.2, 0.1, 0.1, 0.3, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.1, 0.0, + 0.3, 0.5, 0.4, 0.4, 0.2, 0.1, 0.2, 0.1, 0.1, 0.3, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.1, 0.0, + 0.4, 0.5, 0.4, 0.4, 0.2, 0.1, 0.2, 0.1, 0.1, 0.3, 0.1, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0.1, 0.1, 0.0, + 0.5, 0.5, 0.7, 0.4, 0.4, 0.2, 0.3, 0.2, 0.1, 0.4, 0.2, 0.0, 0.0, 0.2, 0.0, 0.0, 0.0, 0.2, 0.2, 0.0, + 0.5, 0.5, 1.3, 0.9, 0.4, 0.2, 0.5, 0.2, 0.1, 0.8, 0.4, 0.0, 0.0, 0.3, 0.0, 0.0, 0.0, 0.2, 0.2, 0.0, + 0.6, 0.5, 1.2, 1.2, 0.5, 0.1, 0.8, 0.1, 0.1, 1.3, 0.6, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.3, 0.3, 0.0, + 0.6, 0.5, 1.0, 1.6, 0.5, 0.1, 0.5, 0.1, 0.1, 1.1, 0.5, 0.0, 0.0, 0.4, 0.0, 0.0, 0.0, 0.3, 0.3, 0.0, + 0.7, 0.5, 0.8, 1.4, 0.6, 0.1, 0.2, 0.1, 0.1, 0.4, 0.2, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0.2, 0.2, 0.0, + 0.6, 0.5, 0.6, 0.6, 0.5, 0.1, 0.2, 0.1, 0.1, 0.4, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.2, 0.0, + 0.5, 0.5, 0.5, 0.4, 0.3, 0.1, 0.2, 0.1, 0.1, 0.4, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.1, 0.0, + + LAIM = 1.6, 4.5, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.4, 0.3, 0.0, 0.0, 0.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 1.6, 4.5, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5, 0.45, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 1.6, 4.5, 0.0, 0.3, 1.0, 1.0, 1.0, 1.0, 1.0, 0.6, 0.5, 0.0, 0.0, 0.6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 1.6, 4.5, 0.6, 1.2, 1.0, 1.0, 1.5, 1.0, 1.0, 0.7, 0.55, 0.0, 0.0, 0.7, 0.0, 0.0, 0.0, 0.2, 0.2, 0.0, + 5.3, 4.5, 1.2, 3.0, 2.3, 1.0, 2.0, 1.0, 1.0, 1.2, 0.85, 1.0, 0.0, 1.2, 0.0, 0.0, 0.0, 0.5, 0.5, 0.0, + 5.5, 4.5, 2.0, 4.7, 3.5, 1.0, 2.5, 1.0, 1.0, 3.0, 1.85, 2.0, 0.0, 3.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, + 5.3, 4.5, 2.6, 4.5, 4.3, 1.0, 3.0, 1.0, 1.0, 3.5, 2.6, 3.0, 0.0, 3.5, 0.0, 0.0, 0.0, 2.0, 2.0, 0.0, + 5.3, 4.5, 1.7, 3.4, 3.3, 1.0, 2.5, 1.0, 1.0, 1.5, 2.25, 3.0, 0.0, 1.5, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, + 4.2, 4.5, 1.0, 1.2, 2.2, 1.0, 1.5, 1.0, 1.0, 0.7, 1.6, 1.5, 0.0, 0.7, 0.0, 0.0, 0.0, 0.5, 0.5, 0.0, + 2.2, 4.5, 0.5, 0.3, 1.2, 1.0, 1.0, 1.0, 1.0, 0.6, 1.1, 0.0, 0.0, 0.6, 0.0, 0.0, 0.0, 0.2, 0.2, 0.0, + 2.2, 4.5, 0.2, 0.0, 1.2, 1.0, 1.0, 1.0, 1.0, 0.5, 0.65, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 2.2, 4.5, 0.0, 0.0, 1.2, 1.0, 1.0, 1.0, 1.0, 0.4, 0.4, 0.0, 0.0, 0.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + +! LAIM = 5.1, 3.3, 0.0, 1.9, 3.0, 1.0, 0.8, 0.5, 0.5, 0.7, 0.3, 1.8, 0.0, 2.4, 0.0, 0.0, 0.0, 0.6, 0.7, 0.0, +! 5.0, 3.6, 0.0, 1.9, 2.9, 1.0, 0.6, 1.0, 1.0, 0.7, 0.45, 1.9, 0.0, 2.6, 0.0, 0.0, 0.0, 0.4, 0.4, 0.0, +! 5.1, 4.4, 0.0, 2.1, 3.3, 1.0, 0.8, 1.8, 1.7, 1.1, 0.5, 2.6, 0.0, 2.9, 0.0, 0.0, 0.0, 0.4, 0.4, 0.0, +! 5.3, 5.4, 0.6, 2.5, 4.0, 1.0, 0.9, 2.6, 2.9, 1.7, 0.55, 3.9, 0.0, 3.4, 0.0, 0.0, 0.0, 0.4, 0.4, 0.0, +! 5.9, 6.2, 1.2, 3.1, 5.0, 1.0, 1.5, 3.4, 3.6, 2.5, 0.85, 5.2, 0.0, 4.0, 0.0, 0.0, 0.0, 0.8, 1.0, 0.0, +! 6.3, 6.4, 2.0, 3.3, 5.4, 1.0, 2.1, 3.6, 3.5, 2.7, 1.85, 5.6, 0.0, 4.2, 0.0, 0.0, 0.0, 2.0, 2.3, 0.0, +! 6.4, 5.9, 2.6, 3.3, 5.4, 1.0, 2.6, 3.4, 2.9, 2.8, 2.6, 5.3, 0.0, 4.1, 0.0, 0.0, 0.0, 3.3, 3.3, 0.0, +! 6.1, 5.6, 1.7, 3.1, 5.0, 1.0, 2.4, 3.2, 2.7, 2.4, 2.25, 4.5, 0.0, 3.8, 0.0, 0.0, 0.0, 3.3, 3.0, 0.0, +! 6.0, 5.3, 1.0, 2.9, 4.8, 1.0, 2.2, 2.9, 2.4, 2.1, 1.6, 4.1, 0.0, 3.7, 0.0, 0.0, 0.0, 2.8, 3.0, 0.0, +! 5.5, 4.7, 0.5, 2.6, 4.1, 1.0, 1.6, 2.3, 1.8, 1.7, 1.1, 3.2, 0.0, 3.2, 0.0, 0.0, 0.0, 1.4, 1.4, 0.0, +! 5.2, 4.0, 0.2, 2.2, 3.4, 1.0, 1.0, 1.5, 1.4, 1.3, 0.65, 2.3, 0.0, 2.7, 0.0, 0.0, 0.0, 0.5, 0.7, 0.0, +! 5.1, 3.2, 0.0, 1.9, 3.0, 1.0, 0.9, 0.7, 0.7, 0.8, 0.4, 1.7, 0.0, 2.4, 0.0, 0.0, 0.0, 0.8, 0.7, 0.0, + + SLAREA=0.0090, 0.0200, 0.0200, 0.0258, 0.0223, 0.0227, 0.0188, 0.0227, 0.0236, 0.0060, 0.0295, 0.0200, 0.0228, 0.0223, 0.02, 0.02, 0.0422, 0.02, 0.02, 0.02, + +! Five types, one row for each type. + EPS = 0.46, 0.00, 0.00, 46.86, 30.98, 21.62, 0.11, 21.62, 22.80, 0.02, 0.815, 0.00, 41.87, 0.04, 0.0, 0.0, 2.31, 0.0, 0.0, 0.0, + 3.34, 0.00, 0.00, 0.38, 0.96, 0.92, 0.22, 0.92, 0.59, 0.05, 0.535, 0.00, 0.98, 0.09, 0.0, 0.0, 1.47, 0.0, 0.0, 0.0, + 1.85, 0.00, 0.00, 1.84, 1.84, 1.73, 1.26, 1.73, 1.37, 0.03, 0.605, 0.00, 1.82, 0.05, 0.0, 0.0, 1.70, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, +/ diff --git a/test_ngen/celia_test/settings/meta/SOILPARM.TBL b/test_ngen/celia_test/settings/meta/SOILPARM.TBL new file mode 100644 index 000000000..b87d1bae5 --- /dev/null +++ b/test_ngen/celia_test/settings/meta/SOILPARM.TBL @@ -0,0 +1,59 @@ +Soil Parameters +STAS +19,1 'BB DRYSMC F11 MAXSMC REFSMC SATPSI SATDK SATDW WLTSMC QTZ ' +1, 2.79, 0.010, -0.472, 0.339, 0.236, 0.069, 1.07E-6, 0.608E-6, 0.010, 0.92, 'SAND' +2, 4.26, 0.028, -1.044, 0.421, 0.383, 0.036, 1.41E-5, 0.514E-5, 0.028, 0.82, 'LOAMY SAND' +3, 4.74, 0.047, -0.569, 0.434, 0.383, 0.141, 5.23E-6, 0.805E-5, 0.047, 0.60, 'SANDY LOAM' +4, 5.33, 0.084, 0.162, 0.476, 0.360, 0.759, 2.81E-6, 0.239E-4, 0.084, 0.25, 'SILT LOAM' +5, 5.33, 0.084, 0.162, 0.476, 0.383, 0.759, 2.81E-6, 0.239E-4, 0.084, 0.10, 'SILT' +6, 5.25, 0.066, -0.327, 0.439, 0.329, 0.355, 3.38E-6, 0.143E-4, 0.066, 0.40, 'LOAM' +7, 6.66, 0.067, -1.491, 0.404, 0.314, 0.135, 4.45E-6, 0.990E-5, 0.067, 0.60, 'SANDY CLAY LOAM' +8, 8.72, 0.120, -1.118, 0.464, 0.387, 0.617, 2.04E-6, 0.237E-4, 0.120, 0.10, 'SILTY CLAY LOAM' +9, 8.17, 0.103, -1.297, 0.465, 0.382, 0.263, 2.45E-6, 0.113E-4, 0.103, 0.35, 'CLAY LOAM' +10, 10.73, 0.100, -3.209, 0.406, 0.338, 0.098, 7.22E-6, 0.187E-4, 0.100, 0.52, 'SANDY CLAY' +11, 10.39, 0.126, -1.916, 0.468, 0.404, 0.324, 1.34E-6, 0.964E-5, 0.126, 0.10, 'SILTY CLAY' +12, 11.55, 0.138, -2.138, 0.468, 0.412, 0.468, 9.74E-7, 0.112E-4, 0.138, 0.25, 'CLAY' +13, 5.25, 0.066, -0.327, 0.439, 0.329, 0.355, 3.38E-6, 0.143E-4, 0.066, 0.05, 'ORGANIC MATERIAL' +14, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.60, 'WATER' +15, 2.79, 0.006, -1.111, 0.20, 0.17, 0.069, 1.41E-4, 0.136E-3, 0.006, 0.07, 'BEDROCK' +16, 4.26, 0.028, -1.044, 0.421, 0.283, 0.036, 1.41E-5, 0.514E-5, 0.028, 0.25, 'OTHER(land-ice)' +17, 11.55, 0.030, -10.472, 0.468, 0.454, 0.468, 9.74E-7, 0.112E-4, 0.030, 0.60, 'PLAYA' +18, 2.79, 0.006, -0.472, 0.200, 0.17, 0.069, 1.41E-4, 0.136E-3, 0.006, 0.52, 'LAVA' +19, 2.79, 0.01, -0.472, 0.339, 0.236, 0.069, 1.07E-6, 0.608E-6, 0.01, 0.92, 'WHITE SAND' +Soil Parameters +STAS-RUC +19,1 'BB DRYSMC HC MAXSMC REFSMC SATPSI SATDK SATDW WLTSMC QTZ ' +1, 4.05, 0.045, 1.47, 0.395, 0.236, 0.121, 1.76E-4, 0.608E-6, 0.068, 0.92, 'SAND' +2, 4.38, 0.057, 1.41, 0.410, 0.383, 0.090, 1.56E-4, 0.514E-5, 0.075, 0.82, 'LOAMY SAND' +3, 4.90, 0.065, 1.34, 0.435, 0.383, 0.218, 3.47E-5, 0.805E-5, 0.114, 0.60, 'SANDY LOAM' +4, 5.30, 0.067, 1.27, 0.485, 0.360, 0.786, 7.20E-6, 0.239E-4, 0.179, 0.25, 'SILT LOAM' +5, 5.30, 0.034, 1.27, 0.485, 0.383, 0.786, 7.20E-6, 0.239E-4, 0.179, 0.10, 'SILT' +6, 5.39, 0.078, 1.21, 0.451, 0.329, 0.478, 6.95E-6, 0.143E-4, 0.155, 0.40, 'LOAM' +7, 7.12, 0.100, 1.18, 0.420, 0.314, 0.299, 6.30E-6, 0.990E-5, 0.175, 0.60, 'SANDY CLAY LOAM' +8, 7.75, 0.089, 1.32, 0.477, 0.387, 0.356, 1.70E-6, 0.237E-4, 0.218, 0.10, 'SILTY CLAY LOAM' +9, 8.52, 0.095, 1.23, 0.476, 0.382, 0.630, 2.45E-6, 0.113E-4, 0.250, 0.35, 'CLAY LOAM' +10, 10.40, 0.100, 1.18, 0.426, 0.338, 0.153, 2.17E-6, 0.187E-4, 0.219, 0.52, 'SANDY CLAY' +11, 10.40, 0.070, 1.15, 0.492, 0.404, 0.490, 1.03E-6, 0.964E-5, 0.283, 0.10, 'SILTY CLAY' +12, 11.40, 0.068, 1.09, 0.482, 0.412, 0.405, 1.28E-6, 0.112E-4, 0.286, 0.25, 'CLAY' +13, 5.39, 0.078, 1.21, 0.451, 0.329, 0.478, 6.95E-6, 0.143E-4, 0.155, 0.05, 'ORGANIC MATERIAL' +14, 0.0, 0.0, 4.18, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.00, 'WATER' +15, 4.05, 0.004, 2.03, 0.200, 0.17, 0.121, 1.41E-4, 0.136E-3, 0.006, 0.60, 'BEDROCK' +16, 4.90, 0.065, 2.10, 0.435, 0.283, 0.218, 3.47E-5, 0.514E-5, 0.114, 0.05, 'OTHER(land-ice)' +17, 11.40, 0.030, 1.41, 0.468, 0.454, 0.468, 9.74E-7, 0.112E-4, 0.030, 0.60, 'PLAYA' +18, 4.05, 0.006, 1.41, 0.200, 0.17, 0.069, 1.41E-4, 0.136E-3, 0.060, 0.52, 'LAVA' +19, 4.05, 0.01, 1.47, 0.339, 0.236, 0.069, 1.76E-4, 0.608E-6, 0.060, 0.92, 'WHITE SAND' +Soil Parameters +ROSETTA +12,1 'theta_res theta_sat vGn_alpha vGn_n k_soil BB DRYSMC HC MAXSMC REFSMC SATPSI SATDK SATDW WLTSMC QTZ ' +1 0.098 0.459 -1.496 1.253 1.70799e-06 1.40 0.068 1.09 0.482 0.412 0.405 1.28E-6 0.112E-4 0.286 0.25 'CLAY' +2 0.079 0.442 -1.581 1.416 9.47297e-07 8.52 0.095 1.23 0.476 0.382 0.630 2.45E-6 0.113E-4 0.250 0.35 'CLAY LOAM' +3 0.061 0.399 -1.112 1.472 1.39472e-06 5.39 0.078 1.21 0.451 0.329 0.478 6.95E-6 0.143E-4 0.155 0.40 'LOAM' +4 0.049 0.390 -3.475 1.746 1.21755e-05 4.38 0.057 1.41 0.410 0.383 0.090 1.56E-4 0.514E-5 0.075 0.82 'LOAMY SAND' +5 0.053 0.375 -3.524 3.177 7.43852e-05 4.05 0.045 1.47 0.395 0.236 0.121 1.76E-4 0.608E-6 0.068 0.92 'SAND' +6 0.117 0.385 -3.342 1.208 1.31367e-06 0.40 0.100 1.18 0.426 0.338 0.153 2.17E-6 0.187E-4 0.219 0.52 'SANDY CLAY' +7 0.063 0.384 -2.109 1.330 1.52576e-06 7.12 0.100 1.18 0.420 0.314 0.299 6.30E-6 0.990E-5 0.175 0.60 'SANDY CLAY LOAM' +8 0.039 0.387 -2.667 1.449 4.43084e-06 4.90 0.065 1.34 0.435 0.383 0.218 3.47E-5 0.805E-5 0.114 0.60 'SANDY LOAM' +9 0.050 0.489 -0.658 1.679 5.06391e-06 5.30 0.034 1.27 0.485 0.383 0.786 7.20E-6 0.239E-4 0.179 0.10 'SILT' +10 0.111 0.481 -1.622 1.321 1.11298e-06 0.40 0.070 1.15 0.492 0.404 0.490 1.03E-6 0.964E-5 0.283 0.10 'SILTY CLAY' +11 0.090 0.482 -0.839 1.521 1.28673e-06 7.75 0.089 1.32 0.477 0.387 0.356 1.70E-6 0.237E-4 0.218 0.10 'SILTY CLAY LOAM' +12 0.065 0.439 -0.506 1.663 2.11099e-06 5.30 0.067 1.27 0.485 0.360 0.786 7.20E-6 0.239E-4 0.179 0.25 'SILT LOAM' diff --git a/test_ngen/celia_test/settings/meta/VEGPARM.TBL b/test_ngen/celia_test/settings/meta/VEGPARM.TBL new file mode 100644 index 000000000..be03224ef --- /dev/null +++ b/test_ngen/celia_test/settings/meta/VEGPARM.TBL @@ -0,0 +1,119 @@ +Vegetation Parameters +USGS +27,1, 'SHDFAC NROOT RS RGL HS SNUP MAXALB LAIMIN LAIMAX EMISSMIN EMISSMAX ALBEDOMIN ALBEDOMAX Z0MIN Z0MAX ' +1, .10, 1, 200., 999., 999.0, 0.04, 46., 1.00, 1.00, .880, .880, .15, .15, .50, .50, 'Urban and Built-Up Land' +2, .80, 3, 40., 100., 36.25, 0.04, 66., 1.56, 5.68, .920, .985, .17, .23, .05, .15, 'Dryland Cropland and Pasture' +3, .80, 3, 40., 100., 36.25, 0.04, 66., 1.56, 5.68, .930, .985, .20, .25, .02, .10, 'Irrigated Cropland and Pasture' +4, .80, 3, 40., 100., 36.25, 0.04, 66., 1.00, 4.50, .920, .985, .18, .23, .05, .15, 'Mixed Dryland/Irrigated Cropland and Pasture' +5, .80, 3, 40., 100., 36.25, 0.04, 68., 2.29, 4.29, .920, .980, .18, .23, .05, .14, 'Cropland/Grassland Mosaic' +6, .80, 3, 70., 65., 44.14, 0.04, 60., 2.00, 4.00, .930, .985, .16, .20, .20, .20, 'Cropland/Woodland Mosaic' +7, .80, 3, 40., 100., 36.35, 0.04, 70., 0.52, 2.90, .920, .960, .19, .23, .10, .12, 'Grassland' +8, .70, 3, 300., 100., 42.00, 0.03, 60., 0.50, 3.66, .930, .930, .25, .30, .01, .05, 'Shrubland' +9, .70, 3, 170., 100., 39.18, 0.035, 65., 0.60, 2.60, .930, .950, .22, .30, .01, .06, 'Mixed Shrubland/Grassland' +10, .50, 3, 70., 65., 54.53, 0.04, 50., 0.50, 3.66, .920, .920, .20, .20, .15, .15, 'Savanna' +11, .80, 4, 100., 30., 54.53, 0.08, 58., 1.85, 3.31, .930, .930, .16, .17, .50, .50, 'Deciduous Broadleaf Forest' +12, .70, 4, 150., 30., 47.35, 0.08, 54., 1.00, 5.16, .930, .940, .14, .15, .50, .50, 'Deciduous Needleleaf Forest' +13, .95, 4, 150., 30., 41.69, 0.08, 35., 3.08, 6.48, .950, .950, .12, .12, .50, .50, 'Evergreen Broadleaf Forest' +14, .70, 4, 125., 30., 47.35, 0.08, 52., 5.00, 6.40, .950, .950, .12, .12, .50, .50, 'Evergreen Needleleaf Forest' +15, .80, 4, 125., 30., 51.93, 0.08, 53., 2.80, 5.50, .930, .970, .17, .25, .20, .50, 'Mixed Forest' +16, .00, 0, 100., 30., 51.75, 0.01, 70., 0.01, 0.01, .980, .980, .08, .08, 0.0001, 0.0001, 'Water Bodies' +17, .60, 2, 40., 100., 60.00, 0.01, 68., 1.50, 5.65, .950, .950, .14, .14, .20, .20, 'Herbaceous Wetland' +18, .60, 2, 100., 30., 51.93, 0.02, 50., 2.00, 5.80, .950, .950, .14, .14, .40, .40, 'Wooded Wetland' +19, .01, 1, 999., 999., 999.0, 0.02, 75., 0.10, 0.75, .900, .900, .38, .38, .01, .01, 'Barren or Sparsely Vegetated' +20, .60, 3, 150., 100., 42.00, 0.025, 68., 0.41, 3.35, .920, .920, .15, .20, .10, .10, 'Herbaceous Tundra' +21, .60, 3, 150., 100., 42.00, 0.025, 55., 0.41, 3.35, .930, .930, .15, .20, .30, .30, 'Wooded Tundra' +22, .60, 3, 150., 100., 42.00, 0.025, 60., 0.41, 3.35, .920, .920, .15, .20, .15, .15, 'Mixed Tundra' +23, .30, 2, 200., 100., 42.00, 0.02, 75., 0.41, 3.35, .900, .900, .25, .25, .05, .10, 'Bare Ground Tundra' +24, .00, 1, 999., 999., 999.0, 0.02, 82., 0.01, 0.01, .950, .950, .55, .70, 0.001, 0.001, 'Snow or Ice' +25, .50, 1, 40., 100., 36.25, 0.02, 75., 0.01, 0.01, .890, .890, .30, .30, .01, .01, 'Playa' +26, .00, 0, 999., 999., 999.0, 0.02, 75., 0.01, 0.01, .880, .880, .16, .16, .15, .15, 'Lava' +27, .00, 0, 999., 999., 999.0, 0.02, 75., 0.01, 0.01, .830, .830, .60, .60, .01, .01, 'White Sand' +TOPT_DATA +298.0 +CMCMAX_DATA +0.5E-3 +CFACTR_DATA +0.5 +RSMAX_DATA +5000.0 +BARE +19 +NATURAL +5 +Vegetation Parameters +MODIFIED_IGBP_MODIS_NOAH +20,1, 'SHDFAC NROOT RS RGL HS SNUP MAXALB LAIMIN LAIMAX EMISSMIN EMISSMAX ALBEDOMIN ALBEDOMAX Z0MIN Z0MAX' +1 .70, 4, 125., 30., 47.35, 0.08, 52., 5.00, 6.40, .950, .950, .12, .12, .50, .50, 'Evergreen Needleleaf Forest' +2, .95, 4, 150., 30., 41.69, 0.08, 35., 3.08, 6.48, .950, .950, .12, .12, .50, .50, 'Evergreen Broadleaf Forest' +3, .70, 4, 150., 30., 47.35, 0.08, 54., 1.00, 5.16, .930, .940, .14, .15, .50, .50, 'Deciduous Needleleaf Forest' +4, .80, 4, 100., 30., 54.53, 0.08, 58., 1.85, 3.31, .930, .930, .16, .17, .50, .50, 'Deciduous Broadleaf Forest' +5, .80, 4, 125., 30., 51.93, 0.08, 53., 2.80, 5.50, .930, .970, .17, .25, .20, .50, 'Mixed Forests' +6, .70, 3, 300., 100., 42.00, 0.03, 60., 0.50, 3.66, .930, .930, .25, .30, .01, .05, 'Closed Shrublands' +7, .70, 3, 170., 100., 39.18, 0.035, 65., 0.60, 2.60, .930, .950, .22, .30, .01, .06, 'Open Shrublands' +8, .70, 3, 300., 100., 42.00, 0.03, 60., 0.50, 3.66, .930, .930, .25, .30, .01, .05, 'Woody Savannas' +9, .50, 3, 70., 65., 54.53, 0.04, 50., 0.50, 3.66, .920, .920, .20, .20, .15, .15, 'Savannas' +10, .80, 3, 40., 100., 36.35, 0.04, 70., 0.52, 2.90, .920, .960, .19, .23, .10, .12, 'Grasslands' +11 .60, 2, 70., 65., 55.97 0.015 59., 1.75, 5.72, .950, .950, .14, .14, .30, .30, 'Permanent wetlands' +12, .80, 3, 40., 100., 36.25, 0.04, 66., 1.56, 5.68, .920, .985, .17, .23, .05, .15, 'Croplands' +13, .10, 1, 200., 999., 999.0, 0.04, 46., 1.00, 1.00, .880, .880, .15, .15, .50, .50, 'Urban and Built-Up' +14 .80, 3, 40., 100., 36.25, 0.04, 68., 2.29, 4.29, .920, .980, .18, .23, .05, .14, 'cropland/natural vegetation mosaic' +15, .00, 1, 999., 999., 999.0, 0.02, 82., 0.01, 0.01, .950, .950, .55, .70, 0.001, 0.001, 'Snow and Ice' +16, .01, 1, 999., 999., 999.0, 0.02, 75., 0.10, 0.75, .900, .900, .38, .38, .01, .01, 'Barren or Sparsely Vegetated' +17, .00, 0, 100., 30., 51.75, 0.01, 70., 0.01, 0.01, .980, .980, .08, .08, 0.0001, 0.0001, 'Water' +18, .60, 3, 150., 100., 42.00, 0.025, 55., 0.41, 3.35, .930, .930, .15, .20, .30, .30, 'Wooded Tundra' +19, .60, 3, 150., 100., 42.00, 0.025, 60., 0.41, 3.35, .920, .920, .15, .20, .15, .15, 'Mixed Tundra' +20, .30, 2, 200., 100., 42.00, 0.02, 75., 0.41, 3.35, .900, .900, .25, .25, .05, .10, 'Barren Tundra' +TOPT_DATA +298.0 +CMCMAX_DATA +0.5E-3 +CFACTR_DATA +0.5 +RSMAX_DATA +5000.0 +BARE +16 +NATURAL +14 +Vegetation Parameters +USGS-RUC +27,1, 'ALBEDO Z0 LEMI PC SHDFAC NROOT RS RGL HS SNUP LAI MAXALB' +1, .18, .50, .88, .40, .10, 1, 200., 999., 999.0, 0.04, 4.0, 40., 'Urban and Built-Up Land' +2, .17, .06, .92, .30, .80, 3, 40., 100., 36.25, 0.04, 4.0, 64., 'Dryland Cropland and Pasture' +3, .18, .075, .92, .40, .80, 3, 40., 100., 36.25, 0.04, 4.0, 64., 'Irrigated Cropland and Pasture' +4, .18, .065, .92, .40, .80, 3, 40., 100., 36.25, 0.04, 4.0, 64., 'Mixed Dryland/Irrigated Cropland and Pasture' +5, .18, .05, .92, .40, .80, 3, 40., 100., 36.25, 0.04, 4.0, 64., 'Cropland/Grassland Mosaic' +6, .16, .20, .93, .40, .80, 3, 70., 65., 44.14, 0.04, 4.0, 60., 'Cropland/Woodland Mosaic' +7, .19, .075 .92, .40, .80, 3, 40., 100., 36.35, 0.04, 4.0, 64., 'Grassland' +8, .22, .10, .88, .40, .70, 3, 300., 100., 42.00, 0.03, 4.0, 69., 'Shrubland' +9, .20, .11, .90, .40, .70, 3, 170., 100., 39.18, 0.035, 4.0, 67., 'Mixed Shrubland/Grassland' +10, .20, .15, .92, .40, .50, 3, 70., 65., 54.53, 0.04, 4.0, 45., 'Savanna' +11, .16, .50, .93, .55, .80, 4, 100., 30., 54.53, 0.08, 4.0, 58., 'Deciduous Broadleaf Forest' +12, .14, .50, .94, .55, .70, 4, 150., 30., 47.35, 0.08, 4.0, 54., 'Deciduous Needleleaf Forest' +13, .12, .50, .95, .55, .95, 4, 150., 30., 41.69, 0.08, 4.0, 32., 'Evergreen Broadleaf Forest' +14, .12, .50, .95, .55, .70, 4, 125., 30., 47.35, 0.08, 4.0, 52., 'Evergreen Needleleaf Forest' +15, .13, .50, .94, .55, .80, 4, 125., 30., 51.93, 0.08, 4.0, 53., 'Mixed Forest' +16, .08, .0001, .98, .00, .00, 0, 100., 30., 51.75, 0.01, 4.0, 70., 'Water Bodies' +17, .14, .20, .95, .55, .60, 2, 40., 100., 60.00, 0.01, 4.0, 35., 'Herbaceous Wetland' +18, .14, .40, .95, .55, .60, 2, 100., 30., 51.93, 0.02, 4.0, 30., 'Wooded Wetland' +19, .25, .05, .85, .30, .01, 1, 999., 999., 999.0, 0.02, 4.0, 69., 'Barren or Sparsely Vegetated' +20, .15, .10, .92, .30, .60, 3, 150., 100., 42.00, 0.025, 4.0, 58., 'Herbaceous Tundra' +21, .15, .15, .93, .40, .60, 3, 150., 100., 42.00, 0.025, 4.0, 55., 'Wooded Tundra' +22, .15, .10, .92, .40, .60, 3, 150., 100., 42.00, 0.025, 4.0, 55., 'Mixed Tundra' +23, .25, .065 .85, .30, .30, 2, 200., 100., 42.00, 0.02, 4.0, 65., 'Bare Ground Tundra' +24, .55, .05, .95, .00, .00, 1, 999., 999., 999.0, 0.02, 4.0, 75., 'Snow or Ice' +25, .30, .01, .85, .30, .50, 1, 40., 100., 36.25, 0.02, 4.0, 69., 'Playa' +26, .16, .15, .85, .00, .00, 0, 999., 999., 999.0, 0.02, 4.0, 69., 'Lava' +27, .60, .01, .90, .00, .00, 0, 999., 999., 999.0, 0.02, 4.0, 69., 'White Sand' +TOPT_DATA +298.0 +CMCMAX_DATA +0.5E-3 +CFACTR_DATA +0.5 +RSMAX_DATA +5000.0 +BARE +19 +NATURAL +5 diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zBasinParamInfo.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zBasinParamInfo.txt new file mode 100644 index 000000000..1926fd71f --- /dev/null +++ b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zBasinParamInfo.txt @@ -0,0 +1,35 @@ +! *********************************************************************************************************************** +! *********************************************************************************************************************** +! ***** DEFINITION OF BASIN PARAMETERS ********************************************************************************** +! *********************************************************************************************************************** +! *********************************************************************************************************************** +! Note: lines starting with "!" are treated as comment lines -- there is no limit on the number of comment lines. +! +! *********************************************************************************************************************** +! DEFINE BASIN MODEL PARAMETERS +! ------------------------------------ +! the format definition defines the format of the file, which can be changed +! the delimiters "| " must be present (format a1), as these are used to check the integrety of the file +! columns are: +! 1: parameter name +! 2: default parameter value +! 3: lower parameter limit +! 4: upper parameter limit +! *********************************************************************************************************************** +! +! ******************************************************************** +! define format string for parameter descriptions +! ******************************************************************** +'(a25,1x,a1,1x,3(f12.4,1x,a1,1x))' ! format string for parameter descriptions (must be in single quotes) +! ******************************************************************** +! baseflow +! ******************************************************************** +basin__aquiferHydCond | 0.0100 | 0.0001 | 10.0000 +basin__aquiferScaleFactor | 3.5000 | 0.1000 | 100.0000 +basin__aquiferBaseflowExp | 5.0000 | 1.0000 | 10.0000 +! ******************************************************************** +! within-grid routing +! ******************************************************************** +routingGammaShape | 2.5000 | 2.0000 | 3.0000 +routingGammaScale | 20000.0000 | 1.0000 | 5000000.0000 +! ******************************************************************** diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zDecisions.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zDecisions.txt new file mode 100644 index 000000000..0cb7c13a2 --- /dev/null +++ b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zDecisions.txt @@ -0,0 +1,168 @@ +! *********************************************************************************************************************** +! DEFINITION OF THE MODEL DECISIONS +! *********************************************************************************************************************** +! This file defines the modeling decisions used. +! NOTES: +! (1) lines starting with ! are treated as comment lines -- there is no limit on the number of comment lines +! (2) the name of the decision is followed by the character string defining the decision +! (3) the simulation start/end times must be within single quotes +! *********************************************************************************************************************** +! *********************************************************************************************************************** +soilCatTbl ROSETTA ! (03) soil-category dateset +vegeParTbl USGS ! (04) vegetation category dataset +soilStress NoahType ! (05) choice of function for the soil moisture control on stomatal resistance +stomResist BallBerry ! (06) choice of function for stomatal resistance +! *********************************************************************************************************************** +num_method bEuler ! (07) choice of numerical method +fDerivMeth analytic ! (08) method used to calculate flux derivatives +LAI_method monTable ! (09) method used to determine LAI and SAI +f_Richards mixdform ! (10) form of Richard's equation +groundwatr noXplict ! (11) choice of groundwater parameterization +hc_profile constant ! (12) choice of hydraulic conductivity profile +bcUpprTdyn zeroFlux ! (13) type of upper boundary condition for thermodynamics +bcLowrTdyn zeroFlux ! (14) type of lower boundary condition for thermodynamics +bcUpprSoiH presHead ! (15) type of upper boundary condition for soil hydrology +bcLowrSoiH presHead ! (16) type of lower boundary condition for soil hydrology +veg_traits CM_QJRMS1988 ! (17) choice of parameterization for vegetation roughness length and displacement height +canopyEmis difTrans ! (18) choice of parameterization for canopy emissivity +snowIncept lightSnow ! (19) choice of parameterization for snow interception +windPrfile logBelowCanopy ! (20) choice of wind profile through the canopy +astability louisinv ! (21) choice of stability function +canopySrad CLM_2stream ! (22) choice of canopy shortwave radiation method +alb_method varDecay ! (23) choice of albedo representation +compaction anderson ! (24) choice of compaction routine +snowLayers CLM_2010 ! (25) choice of method to combine and sub-divide snow layers +thCondSnow jrdn1991 ! (26) choice of thermal conductivity representation for snow +thCondSoil mixConstit ! (27) choice of thermal conductivity representation for soil +spatial_gw localColumn ! (28) choice of method for the spatial representation of groundwater +subRouting timeDlay ! (29) choice of method for sub-grid routing +howHeatCap closedForm ! (30) +! *********************************************************************************************** +! ***** description of the options available -- nothing below this point is read **************** +! *********************************************************************************************** +! ----------------------------------------------------------------------------------------------- +! (01) simulation start time +! (02) simulation end time +! ----------------------------------------------------------------------------------------------- +! (03) soil-category dateset +! STAS ! STATSGO dataset +! STAS-RUC ! ?? +! ROSETTA ! merged Rosetta table with STAS-RUC +! ----------------------------------------------------------------------------------------------- +! (04) vegetation category dataset +! USGS ! USGS 24/27 category dataset +! MODIFIED_IGBP_MODIS_NOAH ! MODIS 20-category dataset +! ----------------------------------------------------------------------------------------------- +! (05) choice of function for the soil moisture control on stomatal resistance +! NoahType ! thresholded linear function of volumetric liquid water content +! CLM_Type ! thresholded linear function of matric head +! SiB_Type ! exponential of the log of matric head +! ----------------------------------------------------------------------------------------------- +! (06) choice of function for stomatal resistance +! BallBerry ! Ball-Berry +! Jarvis ! Jarvis +! ----------------------------------------------------------------------------------------------- +! (07) choice of numerical method +! bEuler ! home-grown backward Euler +! sundials ! SUNDIALS solver +! ----------------------------------------------------------------------------------------------- +! (08) method used to calculate flux derivatives +! numericl ! numerical derivatives +! analytic ! analytical derivatives +! ----------------------------------------------------------------------------------------------- +! (09) method used to determine LAI and SAI +! monTable ! LAI/SAI taken directly from a monthly table for different vegetation classes +! specified ! LAI/SAI computed from green vegetation fraction and winterSAI and summerLAI parameters +! ----------------------------------------------------------------------------------------------- +! (10) form of Richards' equation +! moisture ! moisture-based form of Richards' equation +! mixdform ! mixed form of Richards' equation +! ----------------------------------------------------------------------------------------------- +! (11) choice of groundwater parameterization +! qTopmodl ! topmodel parameterization +! bigBuckt ! a big bucket (lumped aquifer model) +! noXplict ! no explicit groundwater parameterization +! ----------------------------------------------------------------------------------------------- +! (12) choice of hydraulic conductivity profile +! constant ! constant hydraulic conductivity with depth +! pow_prof ! power-law profile +! ----------------------------------------------------------------------------------------------- +! (13) choice of upper boundary conditions for thermodynamics +! presTemp ! prescribed temperature +! nrg_flux ! energy flux +! ----------------------------------------------------------------------------------------------- +! (14) choice of lower boundary conditions for thermodynamics +! presTemp ! prescribed temperature +! zeroFlux ! zero flux +! ----------------------------------------------------------------------------------------------- +! (15) choice of upper boundary conditions for soil hydrology +! presHead ! prescribed head (volumetric liquid water content for mixed form of Richards' eqn) +! liq_flux ! liquid water flux +! ----------------------------------------------------------------------------------------------- +! (16) choice of lower boundary conditions for soil hydrology +! drainage ! free draining +! presHead ! prescribed head (volumetric liquid water content for mixed form of Richards' eqn) +! bottmPsi ! function of matric head in the lower-most layer +! zeroFlux ! zero flux +! ----------------------------------------------------------------------------------------------- +! (17) choice of parameterization for vegetation roughness length and displacement height +! Raupach_BLM1994 ! Raupach (BLM 1994) "Simplified expressions..." +! CM_QJRMS1988 ! Choudhury and Monteith (QJRMS 1998) "A four layer model for the heat budget..." +! vegTypeTable ! constant parameters dependent on the vegetation type +! ----------------------------------------------------------------------------------------------- +! (18) choice of parameterization for canopy emissivity +! simplExp ! simple exponential function +! difTrans ! parameterized as a function of diffuse transmissivity +! ----------------------------------------------------------------------------------------------- +! (19) choice of parameterization for snow interception +! stickySnow ! maximum interception capacity an increasing function of temperature +! lightSnow ! maximum interception capacity an inverse function of new snow density +! ----------------------------------------------------------------------------------------------- +! (20) choice of wind profile +! exponential ! exponential wind profile extends to the surface +! logBelowCanopy ! logarithmic profile below the vegetation canopy +! ----------------------------------------------------------------------------------------------- +! (21) choice of stability function +! standard ! standard MO similarity, a la Anderson (1979) +! louisinv ! Louis (1979) inverse power function +! mahrtexp ! Mahrt (1987) exponential function +! ----------------------------------------------------------------------------------------------- +! (22) choice of canopy shortwave radiation method +! noah_mp ! full Noah-MP implementation (including albedo) +! CLM_2stream ! CLM 2-stream model (see CLM documentation) +! UEB_2stream ! UEB 2-stream model (Mahat and Tarboton, WRR 2011) +! NL_scatter ! Simplified method Nijssen and Lettenmaier (JGR 1999) +! BeersLaw ! Beer's Law (as implemented in VIC) +! ----------------------------------------------------------------------------------------------- +! (23) choice of albedo representation +! conDecay ! constant decay rate (e.g., VIC, CLASS) +! varDecay ! variable decay rate (e.g., BATS approach, with destructive metamorphism + soot content) +! ----------------------------------------------------------------------------------------------- +! (24) choice of compaction routine +! consettl ! constant settlement rate +! anderson ! semi-empirical method of Anderson (1976) +! ----------------------------------------------------------------------------------------------- +! (25) choice of method to combine and sub-divide snow layers +! CLM_2010 ! CLM option: combination/sub-dividion rules depend on layer index +! jrdn1991 ! SNTHERM option: same combination/sub-dividion rules applied to all layers +! ----------------------------------------------------------------------------------------------- +! (26) choice of thermal conductivity representation for snow +! tyen1965 ! Yen (1965) +! melr1977 ! Mellor (1977) +! jrdn1991 ! Jordan (1991) +! smnv2000 ! Smirnova et al. (2000) +! ----------------------------------------------------------------------------------------------- +! (27) choice of thermal conductivity representation for soil +! funcSoilWet ! function of soil wetness +! mixConstit ! mixture of constituents +! hanssonVZJ ! test case for the mizoguchi lab experiment, Hansson et al. VZJ 2004 +! ----------------------------------------------------------------------------------------------- +! (28) choice of method for the spatial representation of groundwater +! localColumn ! separate groundwater representation in each local soil column +! singleBasin ! single groundwater store over the entire basin +! ----------------------------------------------------------------------------------------------- +! (29) choice of method for sub-grid routing +! timeDlay ! time-delay histogram +! qInstant ! instantaneous routing +! *********************************************************************************************** +! history Mon Jul 20 16:08:18 2020: /pool0/home/andrbenn/data/summa_3/utils/convert_summa_config_v2_v3.py ./syntheticTestCases/celia1990/summa_fileManager_celia1990.txt diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zForcingFileList.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zForcingFileList.txt new file mode 100644 index 000000000..d124336ff --- /dev/null +++ b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zForcingFileList.txt @@ -0,0 +1,8 @@ +! **************************************************************************************************** +! List of forcing data files used +! +! This file includes one "word" per line: +! (1) The name of a forcing file +! --> filename must be in single quotes +! **************************************************************************************************** + 'celia1990_forcing.nc' diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zInitialCond.nc b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zInitialCond.nc new file mode 100644 index 0000000000000000000000000000000000000000..0dbc2e3ec3eeaa5b94ccde92cc300f96a04dba50 GIT binary patch literal 20783 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 literal 0 HcmV?d00001 diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zLocalAttributes.nc b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zLocalAttributes.nc new file mode 100644 index 0000000000000000000000000000000000000000..6d6d398ea65ee01cae90ce82c2e6f33dd8e845cf GIT binary patch literal 2156 zcmb7FUr!T35C4#HZEp99_~fHs zLE?jteipxk&pes<37om>@uwm^H=XX@+|2xbvpY9i+j%-Tkd85$u8|b{C}s`o3*Om! zoS#X?Tw#ULapWL9r9QWoKGwA(f^BOf*^DikrQ}`Fm&WSoD6_K%F~S>cL**e<*0vtd zuMCy)+Qt?rCqgAcgcTPQQCKEP)`TWHivqYcM_5rn-Ck z%Eb?$Tk!1lTd*1qed+i>3}KHHO=KUF+7HD_+U}a;4X&j6>ok#!Ow&B>>O)1IlEbo1 zYH}rq#ZwOKYy#a*!57N#7@ERJ1v<6?+Ql?jKOQq=HkpJuIBH`OJaNp!Wh&+3VoNHi zJV?TjcRx%Zn+c<3a1k_$z6a(xeXOz6kwcoS5nNK&*`vx515J~MqTT7LkT>sp67@vEAw6WXmA9?C* zwx0R?AO39@9T|!MT=XJDpsGojX8TbmJ|h8bMwd<=E>Q2gWxf6GzH6YRBk+&h zNC(=Jbh$xb5!JMdX3>2&UPovs8K0cZMz3}A6_u8k=ufA5l YHB-H6C8N6gg~xIA=dVPc#wwir0eO&_C;$Ke literal 0 HcmV?d00001 diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zLocalParamInfo.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zLocalParamInfo.txt new file mode 100644 index 000000000..92bbfe8d8 --- /dev/null +++ b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zLocalParamInfo.txt @@ -0,0 +1,226 @@ +! ======================================================================================================================= +! ======================================================================================================================= +! ===== DEFINITION OF MODEL PARAMETERS ================================================================================== +! ======================================================================================================================= +! ======================================================================================================================= +! Note: lines starting with "!" are treated as comment lines -- there is no limit on the number of comment lines. +! +! ======================================================================================================================= +! DEFINE SITE MODEL PARAMETERS +! ------------------------------------ +! the format definition defines the format of the file, which can be changed +! the delimiters "| " must be present (format a2), as these are used to check the integrety of the file +! columns are: +! 1: parameter name +! 2: default parameter value +! 3: lower parameter limit +! 4: upper parameter limit +! ======================================================================================================================= +! +! ==================================================================== +! define format string for parameter descriptions +! ==================================================================== +'(a25,1x,3(a1,1x,f12.4,1x))' ! format string (must be in single quotes) +! ==================================================================== +! boundary conditions +! ==================================================================== +upperBoundHead | -0.7500 | -100.0000 | -0.0100 +lowerBoundHead | -10.0000 | -100.0000 | -0.0100 +upperBoundTheta | 0.2004 | 0.1020 | 0.3680 +lowerBoundTheta | 0.1100 | 0.1020 | 0.3680 +upperBoundTemp | 272.1600 | 270.1600 | 280.1600 +lowerBoundTemp | 274.1600 | 270.1600 | 280.1600 +! ==================================================================== +! precipitation partitioning +! ==================================================================== +tempCritRain | 273.1600 | 272.1600 | 274.1600 +tempRangeTimestep | 2.0000 | 0.5000 | 5.0000 +frozenPrecipMultip | 1.0000 | 0.5000 | 1.5000 +! ==================================================================== +! snow properties +! ==================================================================== +snowfrz_scale | 50.0000 | 10.0000 | 1000.0000 +fixedThermalCond_snow | 0.3500 | 0.1000 | 1.0000 +! ==================================================================== +! snow albedo +! ==================================================================== +albedoMax | 0.8400 | 0.7000 | 0.9500 +albedoMinWinter | 0.5500 | 0.6000 | 1.0000 +albedoMinSpring | 0.5500 | 0.3000 | 1.0000 +albedoMaxVisible | 0.9500 | 0.7000 | 0.9500 +albedoMinVisible | 0.7500 | 0.5000 | 0.7500 +albedoMaxNearIR | 0.6500 | 0.5000 | 0.7500 +albedoMinNearIR | 0.3000 | 0.1500 | 0.4500 +albedoDecayRate | 1.0d+6 | 0.1d+6 | 5.0d+6 +albedoSootLoad | 0.3000 | 0.1000 | 0.5000 +albedoRefresh | 1.0000 | 1.0000 | 10.0000 +! ==================================================================== +! radiation transfer within snow +! ==================================================================== +radExt_snow | 20.0000 | 20.0000 | 20.0000 +directScale | 0.0900 | 0.0000 | 0.5000 +Frad_direct | 0.7000 | 0.0000 | 1.0000 +Frad_vis | 0.5000 | 0.0000 | 1.0000 +! ==================================================================== +! new snow density +! ==================================================================== +newSnowDenMin | 100.0000 | 50.0000 | 100.0000 +newSnowDenMult | 100.0000 | 25.0000 | 75.0000 +newSnowDenScal | 5.0000 | 1.0000 | 5.0000 +constSnowDen | 100.0000 | 50.0000 | 250.0000 +newSnowDenAdd | 109.0000 | 80.0000 | 120.0000 +newSnowDenMultTemp | 6.0000 | 1.0000 | 12.0000 +newSnowDenMultWind | 26.0000 | 16.0000 | 36.0000 +newSnowDenMultAnd | 1.0000 | 1.0000 | 3.0000 +newSnowDenBase | 0.0000 | 0.0000 | 0.0000 +! ==================================================================== +! snow compaction +! ==================================================================== +densScalGrowth | 0.0460 | 0.0230 | 0.0920 +tempScalGrowth | 0.0400 | 0.0200 | 0.0600 +grainGrowthRate | 2.7d-6 | 1.0d-6 | 5.0d-6 +densScalOvrbdn | 0.0230 | 0.0115 | 0.0460 +tempScalOvrbdn | 0.0800 | 0.6000 | 1.0000 +baseViscosity | 9.0d+5 | 5.0d+5 | 1.5d+6 +! ==================================================================== +! water flow through snow +! ==================================================================== +Fcapil | 0.0600 | 0.0100 | 0.1000 +k_snow | 0.0150 | 0.0050 | 0.0500 +mw_exp | 3.0000 | 1.0000 | 5.0000 +! ==================================================================== +! turbulent heat fluxes +! ==================================================================== +z0Snow | 0.0010 | 0.0010 | 10.0000 +z0Soil | 0.0100 | 0.0010 | 10.0000 +z0Canopy | 0.1000 | 0.0010 | 10.0000 +zpdFraction | 0.6500 | 0.5000 | 0.8500 +critRichNumber | 0.2000 | 0.1000 | 1.0000 +Louis79_bparam | 9.4000 | 9.2000 | 9.6000 +Louis79_cStar | 5.3000 | 5.1000 | 5.5000 +Mahrt87_eScale | 1.0000 | 0.5000 | 2.0000 +leafExchangeCoeff | 0.0100 | 0.0010 | 0.1000 +windReductionParam | 0.2800 | 0.0000 | 1.0000 +! ==================================================================== +! stomatal conductance +! ==================================================================== +Kc25 | 296.0770 | 296.0770 | 296.0770 +Ko25 | 0.2961 | 0.2961 | 0.2961 +Kc_qFac | 2.1000 | 2.1000 | 2.1000 +Ko_qFac | 1.2000 | 1.2000 | 1.2000 +kc_Ha | 79430.0000 | 79430.0000 | 79430.0000 +ko_Ha | 36380.0000 | 36380.0000 | 36380.0000 +vcmax25_canopyTop | 40.0000 | 20.0000 | 100.0000 +vcmax_qFac | 2.4000 | 2.4000 | 2.4000 +vcmax_Ha | 65330.0000 | 65330.0000 | 65330.0000 +vcmax_Hd | 220000.0000 | 149250.0000 | 149250.0000 +vcmax_Sv | 710.0000 | 485.0000 | 485.0000 +vcmax_Kn | 0.6000 | 0.0000 | 1.2000 +jmax25_scale | 2.0000 | 2.0000 | 2.0000 +jmax_Ha | 43540.0000 | 43540.0000 | 43540.0000 +jmax_Hd | 152040.0000 | 152040.0000 | 152040.0000 +jmax_Sv | 495.0000 | 495.0000 | 495.0000 +fractionJ | 0.1500 | 0.1500 | 0.1500 +quantamYield | 0.0500 | 0.0500 | 0.0500 +vpScaleFactor | 1500.0000 | 1500.0000 | 1500.0000 +cond2photo_slope | 9.0000 | 1.0000 | 10.0000 +minStomatalConductance | 2000.0000 | 2000.0000 | 2000.0000 +! ==================================================================== +! vegetation properties +! ==================================================================== +winterSAI | 1.0000 | 0.0100 | 3.0000 +summerLAI | 3.0000 | 0.0100 | 10.0000 +rootScaleFactor1 | 2.0000 | 1.0000 | 10.0000 +rootScaleFactor2 | 5.0000 | 1.0000 | 10.0000 +rootingDepth | 0.5000 | 0.0100 | 10.0000 +rootDistExp | 1.0000 | 0.0100 | 1.0000 +plantWiltPsi | -150.0000 | -500.0000 | 0.0000 +soilStressParam | 5.8000 | 4.3600 | 6.3700 +critSoilWilting | 0.0750 | 0.0000 | 1.0000 +critSoilTranspire | 0.1750 | 0.0000 | 1.0000 +critAquiferTranspire | 0.2000 | 0.1000 | 10.0000 +minStomatalResistance | 50.0000 | 10.0000 | 200.0000 +leafDimension | 0.0400 | 0.0100 | 0.1000 +heightCanopyTop | 20.0000 | 0.0500 | 100.0000 +heightCanopyBottom | 2.0000 | 0.0000 | 5.0000 +specificHeatVeg | 874.0000 | 500.0000 | 1500.0000 +maxMassVegetation | 25.0000 | 1.0000 | 50.0000 +throughfallScaleSnow | 0.5000 | 0.1000 | 0.9000 +throughfallScaleRain | 0.5000 | 0.1000 | 0.9000 +refInterceptCapSnow | 6.6000 | 1.0000 | 10.0000 +refInterceptCapRain | 1.0000 | 0.0100 | 1.0000 +snowUnloadingCoeff | 0.0000 | 0.0000 | 1.5d-6 +canopyDrainageCoeff | 0.0050 | 0.0010 | 0.0100 +ratioDrip2Unloading | 0.4000 | 0.0000 | 1.0000 +canopyWettingFactor | 0.7000 | 0.0000 | 1.0000 +canopyWettingExp | 1.0000 | 0.0000 | 1.0000 +! ==================================================================== +! soil properties +! ==================================================================== +soil_dens_intr | 2700.0000 | 500.0000 | 4000.0000 +thCond_soil | 5.5000 | 2.9000 | 8.4000 +frac_sand | 0.1600 | 0.0000 | 1.0000 +frac_silt | 0.2800 | 0.0000 | 1.0000 +frac_clay | 0.5600 | 0.0000 | 1.0000 +fieldCapacity | 0.2000 | 0.0000 | 1.0000 +wettingFrontSuction | 0.3000 | 0.1000 | 1.5000 +theta_mp | 0.4010 | 0.3000 | 0.6000 +theta_sat | 0.5500 | 0.3000 | 0.6000 +theta_res | 0.1390 | 0.0010 | 0.1000 +vGn_alpha | -0.8400 | -1.0000 | -0.0100 +vGn_n | 1.3000 | 1.0000 | 3.0000 +mpExp | 5.0000 | 1.0000 | 10.0000 +k_soil | 7.5d-06 | 1.d-07 | 100.d-07 +k_macropore | 1.0d-03 | 1.d-07 | 100.d-07 +kAnisotropic | 1.0000 | 0.0001 | 10.0000 +zScale_TOPMODEL | 2.5000 | 0.1000 | 100.0000 +compactedDepth | 1.0000 | 0.0000 | 1.0000 +aquiferScaleFactor | 0.3500 | 0.1000 | 100.0000 +aquiferBaseflowExp | 2.0000 | 1.0000 | 10.0000 +aquiferBaseflowRate | 2.0000 | 1.0000 | 10.0000 +qSurfScale | 50.0000 | 1.0000 | 100.0000 +specificYield | 0.2000 | 0.1000 | 0.3000 +specificStorage | 1.d-09 | 1.d-05 | 1.d-07 +f_impede | 2.0000 | 1.0000 | 10.0000 +soilIceScale | 0.1300 | 0.0001 | 1.0000 +soilIceCV | 0.4500 | 0.1000 | 5.0000 +! ==================================================================== +! algorithmic control parameters +! ==================================================================== +minwind | 0.1000 | 0.0010 | 1.0000 +minstep | 1.0000 | 1.0000 | 1800.0000 +maxstep | 3600.0000 | 60.0000 | 1800.0000 +wimplicit | 0.0000 | 0.0000 | 1.0000 +maxiter | 100.0000 | 1.0000 | 100.0000 +relConvTol_liquid | 1.0d-3 | 1.0d-5 | 1.0d-1 +absConvTol_liquid | 1.0d-5 | 1.0d-8 | 1.0d-3 +relConvTol_matric | 1.0d-6 | 1.0d-5 | 1.0d-1 +absConvTol_matric | 1.0d-6 | 1.0d-8 | 1.0d-3 +relConvTol_energy | 1.0d-2 | 1.0d-5 | 1.0d-1 +absConvTol_energy | 1.0d-0 | 1.0d-2 | 1.0d+1 +relConvTol_aquifr | 1.0d-0 | 1.0d-2 | 1.0d+1 +absConvTol_aquifr | 1.0d-5 | 1.0d-5 | 1.0d-1 +zmin | 0.0100 | 0.0050 | 0.1000 +zmax | 0.0500 | 0.0100 | 0.5000 +! --- +zminLayer1 | 0.0075 | 0.0075 | 0.0075 +zminLayer2 | 0.0100 | 0.0100 | 0.0100 +zminLayer3 | 0.0500 | 0.0500 | 0.0500 +zminLayer4 | 0.1000 | 0.1000 | 0.1000 +zminLayer5 | 0.2500 | 0.2500 | 0.2500 +! --- +zmaxLayer1_lower | 0.0500 | 0.0500 | 0.0500 +zmaxLayer2_lower | 0.2000 | 0.2000 | 0.2000 +zmaxLayer3_lower | 0.5000 | 0.5000 | 0.5000 +zmaxLayer4_lower | 1.0000 | 1.0000 | 1.0000 +! --- +zmaxLayer1_upper | 0.0300 | 0.0300 | 0.0300 +zmaxLayer2_upper | 0.1500 | 0.1500 | 0.1500 +zmaxLayer3_upper | 0.3000 | 0.3000 | 0.3000 +zmaxLayer4_upper | 0.7500 | 0.7500 | 0.7500 +! ==================================================================== +minTempUnloading | 270.16 | 260.16 | 273.16 +minWindUnloading | 0.0000 | 0.0000 | 10.000 +rateTempUnloading | 1.87d+5 | 1.0d+5 | 3.0d+5 +rateWindUnloading | 1.56d+5 | 1.0d+5 | 3.0d+5 +! history Mon Jul 20 16:08:18 2020: /pool0/home/andrbenn/data/summa_3/utils/convert_summa_config_v2_v3.py ./syntheticTestCases/celia1990/summa_fileManager_celia1990.txt \ No newline at end of file diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zParamTrial.nc b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zParamTrial.nc new file mode 100644 index 0000000000000000000000000000000000000000..8c8f7337f8fc90652058803b2880fa642da87f87 GIT binary patch literal 23995 zcmeHP4{%&X8Q+&&!X-_b^nYkF?Q7a9(#fS)lKx3+^XHOFV$vksr3Gs1@pAXtJbQWX zxO*wBwpNr1I`YR-)OHj{gkr&A92lGd2L+SMK`FP&e?FnPVq!zy`}UdCkl=~khkYbw8% zGFfDQS9_?SeD9)yT5eECtuJa56&{xIk5ZNvFbzv8x#1a6iv@)-E4xgGoW`azFZ_%2 z1qPB%!c4lBnF9SRtOEwT0m$w&lPSxwKk46Nn{G6=Ep82ws=?Rb^Y0msZEHMF>G!Sn z`Byhapb$_9Cy)5Xc(@%HG9T0FB2Ac-gR(a-HOE9wqQR^pTTT<@sg!Bgw?nYOkh7~kX1GTEK<~y4Z5<(!5w3I{C!VTL7)fJf zFlia_h>?m~R>XB8cGQ>Ht=H8Fg=Ooh(UB1&^0|R?wox-~8U8hEmiydYE|}WUh%@S5 z)XW;0iSngj{w|sToiZ!}_Ws`qZ(-o^rSuu`-$p4`6%wiFU!8EN3^_nJbpFkj4 zI2&=9v1lO#f?(3wJTIHK4{}9bEGZ}`;HKW0SX!u2IEb)jY)%cYSU7`GNv14Yjx{yt z9)@rPdRxO`xSj=n7`?$82ie}ep`$O{-QO1p2U>eOWH`v$#ZUyv7D8(0Y!R17s4~Ap zX);bc^~$~F{ei9?>S;mNp0c^)DyJ29e7brawo-O2_E}>^{BHVMH*}y0-c1YqQHKVB zdpi6#bO=O2e#NX2)5h2oeF#R_cHDDCI}IStP~7zCl>F>VEA{*Sr}5L9p+A^fSi>~d zFjhzbHSaCqYUT>TJZ0EPVT-D>`(R{2Q>Znp6ed(QF{wd={6Apyb&DK3ZP`wc~tD}4i7SRDKw2#odcOS0IL z9ZBkENfvt=vrPMGS?tTQ=*zR{r)SYuWYJ4CyO}DILP>>yLO>y)5Kssx1QY@a0fm4< zKp~(IPzZbk2;}n20Y7`c&=Y*HWM`)xG2)3~qhFqq6pJH351j%Op@n^4r?}z2eBJ{e z-|1w6PD}`l8^-?rE1vJVtz~m;?O*$EdA^zC>5uunKdO1G`NXd=l|CWsvVv!a}TFR-Xm5Bell|HP{-3Qp=}XsBw@zPicg7--}BDEgTZiDJ7YESj%m+GQmVlt zJivA_9WF)5NUtcFFAYe({bs|-U^v{t*0+cfZ49blor>dCe`1+iQ9@9*!rqVlLV8Jq zAI{5t0gvQ`9fH}tr0sb^&B=FJqO0-sgAe^A80s2eNP>d%e;IK_Fcb+Mzo!cG*pmPL=5nj+)Jx38vkLwG{sX8x^xFJ{SkiLGo8BqQ8(d7@tT^&;F4r`4Lmvr!Z+|Y|6J6Y2`0>o6 zAY`h&vt~=Kc{Dg>d4!=08=hG55{_xvSqAY!7?9yYKt`w|*b(aJ3v@(8K*n5{Gxu&J`5+K|vNICwfl(wnel@ua^WUCnO!y zkTwu1dnVVs+A?K%g@<0XOZVPAIh$D8^bzZ_?-b&Bf@o$um8b8m1%hc?PWir=Yo5jO z1eM9(KjD^X@uT5)CWmL&z&0Dx#5{BJ1kp^M9oYGew?DYKhrQz~xW`+t@VdL(27807 zXz1`~%Gqn6x|FSLH5&QPepRz&FKp^z254*8^-Z^3!w&K6`pxUMvJ;T4W{=)-f0UWf zR1GU#eOHX#z_VXldCFl6AzRBjzrQ)f=78;7W=Af+kuBrZ&c67cyV%dcU@6=2)ay^O zR>&@4r~dZ9OYFP6+S=n!zsx^>EMV_Gu>UmM%ip+k_U&ibBap3PUunAaeaNWveI- z*7>TS10AyvIsV;6IUy6}$7Th)+S?fYk|L@f`SN?0U-&rZBEis_hh~;{+~JgmW?6|S zN*OL~Uo{U+Ia!u8Q?!-UJT(8tdmptden-tiv(lS+)TDW6h8FLLq#P?wU}zqitJ8Kw zMvN$YLBmO!Mvs6h&wfoOkQ5jACieb~*KI{ez+ez#mgdeQjL>9Cj z%sk(s&2e34V|tj9Jv}<<+W*{ Date: Fri, 24 Mar 2023 14:27:35 +0900 Subject: [PATCH 0607/1472] renamed folders --- build/cmake/CMakeLists.txt | 107 ------------------ build/{cmake => cmake_ngen}/README.md | 26 ++++- build/{cmake => cmake_ngen}/bmi.cmake | 0 .../build_ngen.cluster.bash | 0 .../{cmake => cmake_ngen}/build_ngen.mac.bash | 0 build/{cmake => cmake_ngen}/load_modules.sh | 0 6 files changed, 23 insertions(+), 110 deletions(-) delete mode 100644 build/cmake/CMakeLists.txt rename build/{cmake => cmake_ngen}/README.md (70%) rename build/{cmake => cmake_ngen}/bmi.cmake (100%) rename build/{cmake => cmake_ngen}/build_ngen.cluster.bash (100%) rename build/{cmake => cmake_ngen}/build_ngen.mac.bash (100%) rename build/{cmake => cmake_ngen}/load_modules.sh (100%) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt deleted file mode 100644 index d0c2025c5..000000000 --- a/build/cmake/CMakeLists.txt +++ /dev/null @@ -1,107 +0,0 @@ -ccmake_minimum_required(VERSION 3.10) -enable_language( Fortran ) - -#Get the iso_c_fortran binding module to build as part of this build -add_subdirectory(../iso_c_fortran_bmi ${CMAKE_BINARY_DIR}/iso_c_bmi) - - -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../../cmake/") - -project(summabmi VERSION 1.0.0 DESCRIPTION "Summa-Sundials BMI Module Shared Library") - -set( CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran) - -#### Add variables for individual libraries that are used within the *.pc.in files -set(SUMMA_LIB_NAME_CMAKE summabmi) -set(SUMMA_LIB_DESC_CMAKE "Summa-Sundials BMI Module Shared Library") - -# Make sure these are compiled with this directive UNCLEAR IF WE NEED THESE -add_compile_definitions(BMI_ACTIVE NGEN_FORCING_ACTIVE NGEN_OUTPUT_ACTIVE NGEN_ACTIVE) - - -set(ROOT_DIR ../../../) -set(F_MASTER $(ROOT_DIR)/summa) - -SET (CMAKE_Fortran_COMPILER gfortran) - -include($(F_MASTER)/build/cmake/bmi.cmake) -# -------------------------------------------------------------------------------------------- -# When compiling Summa-Sundials it can be compiled in a Cluster or Debug mode, which is set by -# the CMAKE_BUILD_TYPES variables. The default is Release, non-cluster mode. - -######### SET THE PATHS TO THE SUNDIALS LIBRARIES AND INCLUDE FILES ######### -############################################################################# - -# Add options for build type -set(CMAKE_BUILD_TYPES Debug Release Cluster_Debug Cluster) - -# Set default configuration type to Release -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release) -endif() - -# Set compiler flags -if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) - message("Debug build.") - add_definitions(-DDEBUG) - set(FLAGS_NOAH -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) - set(FLAGS_ALL -g -O0 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) -else() - message("Release build.") - set(FLAGS_NOAH -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) - set(FLAGS_ALL -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) -endif() - - -# Packages that are required -find_package(netCDF REQUIRED) -find_package(LAPACK REQUIRED) - -set(DIR_SUNDIALS= $(ROOT_DIR)/sundials/instdir) -set(INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran) - -# Set include directories -if(CMAKE_BUILD_TYPE MATCHES Cluster OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) - set(INCLUDES $(EBROOTNETCDFMINFORTRAN)/include) - set(LDFLAGS $(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib) - set(LIBRARIES $(LDFLAGS) -lnetcdff -lopenblas) - - message("\nBuilding IDA\n") - - link_directories(${DIR_SUNDIALS}/lib64) - set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib64") - set(LIB_SUNDIALS $(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) - -else - set(INCLUDES /opt/local/include -I/opt/local/lib) - set(LDFLAGS /opt/local/lib) - set(LIBRARIES $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf) - - message("\nBuilding IDA\n") - - link_directories(${DIR_SUNDIALS}/lib) - set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib") - set(LIB_SUNDIALS $(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) - - -endif() - - -message("\nBuilding SUMMA\n") -compile_with_bmi($(ROOT_DIR), $(F_MASTER), $(DIR_SUNDIALS), $(INCLUDES), $(LIBRARIES), $(INC_SUNDIALS), $(LIB_SUNDIALS), $(FLAGS_ALL), $(FLAGS_NOAH)) - - -target_link_libraries(summabmi PUBLIC iso_c_bmi) -target_compile_options(summabmi PUBLIC -cpp -DNGEN_ACTIVE) - -set_target_properties(summabmi PROPERTIES VERSION ${PROJECT_VERSION}) - -include(GNUInstallDirs) - -install(TARGETS summabmi - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - -configure_file(summabmi.pc.in summabmi.pc @ONLY) - -install(FILES ${CMAKE_BINARY_DIR}/summabmi.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) diff --git a/build/cmake/README.md b/build/cmake_ngen/README.md similarity index 70% rename from build/cmake/README.md rename to build/cmake_ngen/README.md index 6dffc1f00..15eefaf5c 100644 --- a/build/cmake/README.md +++ b/build/cmake_ngen/README.md @@ -51,14 +51,34 @@ First, cd into the outer directory containing the submodule: cd extern/summa -Before library files can be built, a CMake build system must be generated. E.g.: +Before summa can be built, Sundials needs to be installed. +Download the latest release of IDA solver from SUNDIALS package in https://computing.llnl.gov/projects/sundials/sundials-software + + wget "https://github.com/LLNL/sundials/releases/download/v6.3.0/sundials-6.3.0.tar.gz" + +Extract the corresponding compressed file + + tar -xzf sundials-6.3.0.tar.gz + +Enter the buildir and run + + cd sundials/buildir + ./build_cmake + make + make install + +Note if you need to recompile after a system upgrade, delete the contents of sundials/instdir and sundials/buildir EXCEPT sundials/buildir/build_cmake before building and installing. + +Before ngen library files can be built, a CMake build system must be generated. E.g.: cmake -B cmake_build -S . -Note that when there is an existing directory, it may sometimes be necessary to clear it and regenerate, especially if any changes were made to the [CMakeLists.txt](CMakeLists.txt) file. +Note (similar to above) that when there is an existing directory, it may sometimes be necessary to clear it and regenerate, especially if any changes were made to the [CMakeLists.txt](CMakeLists.txt) file. -After there is build system directory, the shared library can be built using the `topmodelbmi` CMake target. For example, the SummaSundials shared library file (i.e., the build config's `topmodelbmi` target) can be built using: +After there is build system directory, the shared library can be built using the `summabmi` CMake target. For example, the SummaSundials shared library file (i.e., the build config's `summabmi` target) can be built using: cmake --build cmake_build --target summabmi -- -j 2 This will build a `cmake_build/libsummabmi..` file, where the version is configured within the CMake config, and the extension depends on the local machine's operating system. + +There is an example of a bash script to build the ngen libraries at ngen/extern/summa/summa/build/cmake_ngen/build_ngen.[system_type].bash. diff --git a/build/cmake/bmi.cmake b/build/cmake_ngen/bmi.cmake similarity index 100% rename from build/cmake/bmi.cmake rename to build/cmake_ngen/bmi.cmake diff --git a/build/cmake/build_ngen.cluster.bash b/build/cmake_ngen/build_ngen.cluster.bash similarity index 100% rename from build/cmake/build_ngen.cluster.bash rename to build/cmake_ngen/build_ngen.cluster.bash diff --git a/build/cmake/build_ngen.mac.bash b/build/cmake_ngen/build_ngen.mac.bash similarity index 100% rename from build/cmake/build_ngen.mac.bash rename to build/cmake_ngen/build_ngen.mac.bash diff --git a/build/cmake/load_modules.sh b/build/cmake_ngen/load_modules.sh similarity index 100% rename from build/cmake/load_modules.sh rename to build/cmake_ngen/load_modules.sh From 3e742106fb37c94eb46c65fbc3a63aa9796e7118 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Mar 2023 14:40:34 +0900 Subject: [PATCH 0608/1472] add example files for building and running --- build/cmake_ngen/README.md | 10 +++++++++- ...en.cluster.bash => example_build_ngen.cluster.bash} | 0 ...build_ngen.mac.bash => example_build_ngen.mac.bash} | 0 test_ngen/example_run_celia.sh | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) rename build/cmake_ngen/{build_ngen.cluster.bash => example_build_ngen.cluster.bash} (100%) rename build/cmake_ngen/{build_ngen.mac.bash => example_build_ngen.mac.bash} (100%) create mode 100644 test_ngen/example_run_celia.sh diff --git a/build/cmake_ngen/README.md b/build/cmake_ngen/README.md index 15eefaf5c..2fcad9378 100644 --- a/build/cmake_ngen/README.md +++ b/build/cmake_ngen/README.md @@ -81,4 +81,12 @@ After there is build system directory, the shared library can be built using the This will build a `cmake_build/libsummabmi..` file, where the version is configured within the CMake config, and the extension depends on the local machine's operating system. -There is an example of a bash script to build the ngen libraries at ngen/extern/summa/summa/build/cmake_ngen/build_ngen.[system_type].bash. +There is an example of a bash script to build the ngen libraries at ngen/extern/summa/summa/build/cmake_ngen/build_ngen.[system_type].bash. These needs to be run at a level above the ngen directory. + +To run a test basin, at a level above the ngen directory, run + + cmake_build/ngen data/catchment_data.geojson "cat-27" ./data/nexus_data.geojson "nex-26" ./data/example_realization_config_w_summa.json + +This command can be copied from ngen/extern/summa/summa/test_ngen/example_run.sh + +This test is currently the Celia Laugh test, not the ngen example catchment cat-27. The forcing and input data for that basin could be used once it is in the correct form. diff --git a/build/cmake_ngen/build_ngen.cluster.bash b/build/cmake_ngen/example_build_ngen.cluster.bash similarity index 100% rename from build/cmake_ngen/build_ngen.cluster.bash rename to build/cmake_ngen/example_build_ngen.cluster.bash diff --git a/build/cmake_ngen/build_ngen.mac.bash b/build/cmake_ngen/example_build_ngen.mac.bash similarity index 100% rename from build/cmake_ngen/build_ngen.mac.bash rename to build/cmake_ngen/example_build_ngen.mac.bash diff --git a/test_ngen/example_run_celia.sh b/test_ngen/example_run_celia.sh new file mode 100644 index 000000000..b2bda13bb --- /dev/null +++ b/test_ngen/example_run_celia.sh @@ -0,0 +1 @@ +cmake_build/ngen data/catchment_data.geojson "cat-27" ./data/nexus_data.geojson "nex-26" ./data/example_bmi_multi_realization_config_w_summa.json \ No newline at end of file From 876eb104d290e83e3efb1a4142fdcff2a8e50344 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Mar 2023 14:44:27 +0900 Subject: [PATCH 0609/1472] name was messed up --- ...summa_bmi.json => example_realization_config_w_summa_bmi.json} | 0 ..._mac.json => example_realization_config_w_summa_bmi__mac.json} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename test_ngen/{example_realization _config_w_summa_bmi.json => example_realization_config_w_summa_bmi.json} (100%) rename test_ngen/{example_realization _config_w_summa_bmi__mac.json => example_realization_config_w_summa_bmi__mac.json} (100%) diff --git a/test_ngen/example_realization _config_w_summa_bmi.json b/test_ngen/example_realization_config_w_summa_bmi.json similarity index 100% rename from test_ngen/example_realization _config_w_summa_bmi.json rename to test_ngen/example_realization_config_w_summa_bmi.json diff --git a/test_ngen/example_realization _config_w_summa_bmi__mac.json b/test_ngen/example_realization_config_w_summa_bmi__mac.json similarity index 100% rename from test_ngen/example_realization _config_w_summa_bmi__mac.json rename to test_ngen/example_realization_config_w_summa_bmi__mac.json From fd938118a769c586f271cb6300dbd9634b00ca4a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Mar 2023 14:51:49 +0900 Subject: [PATCH 0610/1472] add example CMakeList --- build/cmake_ngen/example_CMakeLists.txt | 107 ++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 build/cmake_ngen/example_CMakeLists.txt diff --git a/build/cmake_ngen/example_CMakeLists.txt b/build/cmake_ngen/example_CMakeLists.txt new file mode 100644 index 000000000..7ed7b7538 --- /dev/null +++ b/build/cmake_ngen/example_CMakeLists.txt @@ -0,0 +1,107 @@ +ccmake_minimum_required(VERSION 3.10) +enable_language( Fortran ) + +#Get the iso_c_fortran binding module to build as part of this build +add_subdirectory(../iso_c_fortran_bmi ${CMAKE_BINARY_DIR}/iso_c_bmi) + + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../../cmake/") + +project(summabmi VERSION 1.0.0 DESCRIPTION "Summa-Sundials BMI Module Shared Library") + +set( CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran) + +#### Add variables for individual libraries that are used within the *.pc.in files +set(SUMMA_LIB_NAME_CMAKE summabmi) +set(SUMMA_LIB_DESC_CMAKE "Summa-Sundials BMI Module Shared Library") + +# Make sure these are compiled with this directive UNCLEAR IF WE NEED THESE +add_compile_definitions(BMI_ACTIVE NGEN_FORCING_ACTIVE NGEN_OUTPUT_ACTIVE NGEN_ACTIVE) + + +set(ROOT_DIR ../../../) +set(F_MASTER $(ROOT_DIR)/summa) + +SET (CMAKE_Fortran_COMPILER gfortran) + +include($(F_MASTER)/build/cmake_ngen/bmi.cmake) +# -------------------------------------------------------------------------------------------- +# When compiling Summa-Sundials it can be compiled in a Cluster or Debug mode, which is set by +# the CMAKE_BUILD_TYPES variables. The default is Release, non-cluster mode. + +######### SET THE PATHS TO THE SUNDIALS LIBRARIES AND INCLUDE FILES ######### +############################################################################# + +# Add options for build type +set(CMAKE_BUILD_TYPES Debug Release Cluster_Debug Cluster) + +# Set default configuration type to Release +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +# Set compiler flags +if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) + message("Debug build.") + add_definitions(-DDEBUG) + set(FLAGS_NOAH -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) + set(FLAGS_ALL -g -O0 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) +else() + message("Release build.") + set(FLAGS_NOAH -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) + set(FLAGS_ALL -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) +endif() + + +# Packages that are required +find_package(netCDF REQUIRED) +find_package(LAPACK REQUIRED) + +set(DIR_SUNDIALS= $(ROOT_DIR)/sundials/instdir) +set(INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran) + +# Set include directories +if(CMAKE_BUILD_TYPE MATCHES Cluster OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) + set(INCLUDES $(EBROOTNETCDFMINFORTRAN)/include) + set(LDFLAGS $(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib) + set(LIBRARIES $(LDFLAGS) -lnetcdff -lopenblas) + + message("\nBuilding IDA\n") + + link_directories(${DIR_SUNDIALS}/lib64) + set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib64") + set(LIB_SUNDIALS $(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) + +else + set(INCLUDES /opt/local/include -I/opt/local/lib) + set(LDFLAGS /opt/local/lib) + set(LIBRARIES $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf) + + message("\nBuilding IDA\n") + + link_directories(${DIR_SUNDIALS}/lib) + set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib") + set(LIB_SUNDIALS $(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) + + +endif() + + +message("\nBuilding SUMMA\n") +compile_with_bmi($(ROOT_DIR), $(F_MASTER), $(DIR_SUNDIALS), $(INCLUDES), $(LIBRARIES), $(INC_SUNDIALS), $(LIB_SUNDIALS), $(FLAGS_ALL), $(FLAGS_NOAH)) + + +target_link_libraries(summabmi PUBLIC iso_c_bmi) +target_compile_options(summabmi PUBLIC -cpp -DNGEN_ACTIVE) + +set_target_properties(summabmi PROPERTIES VERSION ${PROJECT_VERSION}) + +include(GNUInstallDirs) + +install(TARGETS summabmi + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + +configure_file(summabmi.pc.in summabmi.pc @ONLY) + +install(FILES ${CMAKE_BINARY_DIR}/summabmi.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) From 00d9cd7e8cb3c11c6d2fcca7f17c6aa99d1a3a33 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Mar 2023 15:06:38 +0900 Subject: [PATCH 0611/1472] update bash files --- .../example_build_ngen.cluster.bash | 36 +++++++++---------- build/cmake_ngen/example_build_ngen.mac.bash | 12 +++---- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/build/cmake_ngen/example_build_ngen.cluster.bash b/build/cmake_ngen/example_build_ngen.cluster.bash index 5f29d3699..b46825ac2 100644 --- a/build/cmake_ngen/example_build_ngen.cluster.bash +++ b/build/cmake_ngen/example_build_ngen.cluster.bash @@ -2,39 +2,39 @@ # build nextgen on Copernicus -module load gnu +#module load gnu module load boost module load udunits/2.2.28 -module load python/3.7.5 +#module load python/3.7.5 module load StdEnv/2020 module load gcc/9.3.0 module load openblas/0.3.17 module load netcdf-fortran/4.5.2 -cmake -B extern/cfe/cmake_build -S extern/cfe -cmake --build extern/cfe/cmake_build --target all +cmake -B /ngen/extern/cfe/cmake_build -S /ngen/extern/cfe +cmake --build /ngen/extern/cfe/cmake_build --target all -cmake -B extern/topmodel/cmake_build -S extern/topmodel -cmake --build extern/topmodel/cmake_build --target all +cmake -B /ngen/extern/topmodel/cmake_build -S /ngen/extern/topmodel +cmake --build /ngen/extern/topmodel/cmake_build --target all -cmake -B extern/sloth/cmake_build -S extern/sloth -cmake --build extern/sloth/cmake_build --target all +cmake -B /ngen/extern/sloth/cmake_build -S /ngen/extern/sloth +cmake --build /ngen/extern/sloth/cmake_build --target all -cmake -B extern/noah-owp-modular/cmake_build -S extern/noah-owp-modular -cmake --build extern/noah-owp-modular/cmake_build --target all +cmake -B /ngen/extern/noah-owp-modular/cmake_build -S /ngen/extern/noah-owp-modular +cmake --build /ngen/extern/noah-owp-modular/cmake_build --target all -cmake -B extern/iso_c_fortran_bmi/cmake_build -S extern/iso_c_fortran_bmi -cmake --build extern/iso_c_fortran_bmi/cmake_build --target all +cmake -B /ngen/extern/iso_c_fortran_bmi/cmake_build -S /ngen/extern/iso_c_fortran_bmi +cmake --build /ngen/extern/iso_c_fortran_bmi/cmake_build --target all -cmake -B extern/snow17/cmake_build -S extern/snow17 -cmake --build extern/snow17/cmake_build --target all +cmake -B /ngen/extern/snow17/cmake_build -S /ngen/extern/snow17 +cmake --build /ngen/extern/snow17/cmake_build --target all -cmake -B extern/summa/cmake_build -S extern/summa -cmake --build extern/summa/cmake_build --target all +cmake -B /ngen/extern/summa/cmake_build -S /ngen/extern/summa +cmake --build /ngen/extern/summa/cmake_build --target all -cmake -B extern/evapotranspiration/cmake_build -S extern/evapotranspiration -cmake --build extern/evapotranspiration/cmake_build --target all +cmake -B /ngen/extern/evapotranspiration/cmake_build -S /ngen/extern/evapotranspiration +cmake --build /ngen/extern/evapotranspiration/cmake_build --target all cmake -B cmake_build -S . -DMPI_ACTIVE:BOOL=ON -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ #cmake -B cmake_build -S . -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ diff --git a/build/cmake_ngen/example_build_ngen.mac.bash b/build/cmake_ngen/example_build_ngen.mac.bash index 1e30bfd4a..bac6b43c6 100644 --- a/build/cmake_ngen/example_build_ngen.mac.bash +++ b/build/cmake_ngen/example_build_ngen.mac.bash @@ -2,14 +2,14 @@ # build nextgen on Mac -cmake -B extern/iso_c_fortran_bmi/cmake_build -S extern/iso_c_fortran_bmi -cmake --build extern/iso_c_fortran_bmi/cmake_build --target all +cmake -B /ngen/extern/iso_c_fortran_bmi/cmake_build -S /ngen/extern/iso_c_fortran_bmi +cmake --build /ngen/extern/iso_c_fortran_bmi/cmake_build --target all -cmake -B extern/summa/cmake_build -S extern/summa -cmake --build extern/summa/cmake_build --target all +cmake -B /ngen/extern/summa/cmake_build -S /ngen/extern/summa +cmake --build /ngen/extern/summa/cmake_build --target all -cmake -B cmake_build -S . -DMPI_ACTIVE:BOOL=ON -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ -#cmake -B cmake_build -S . -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ +cmake -B /ngen/cmake_build -S . -DMPI_ACTIVE:BOOL=ON -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ +#cmake -B /ngen/cmake_build -S . -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ # -DUDUNITS2_LIBRARY=/glade/u/apps/ch/opt/udunits/2.2.28/gnu/10.1.0/lib/libudunits2.so \ # -DUDUNITS2_INCLUDE=/glade/u/apps/ch/opt/udunits/2.2.28/gnu/10.1.0/include # \ # -DCMAKE_BUILD_TYPE=Debug From cf4318498214ad05a08423ea15f638c9fca54357 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Mar 2023 15:17:34 +0900 Subject: [PATCH 0612/1472] more bash files --- build/cmake_ngen/README.md | 6 ++-- .../example_build_ngen.cluster.bash | 34 +++++++++---------- build/cmake_ngen/example_build_ngen.mac.bash | 10 +++--- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/build/cmake_ngen/README.md b/build/cmake_ngen/README.md index 2fcad9378..e6d63ca47 100644 --- a/build/cmake_ngen/README.md +++ b/build/cmake_ngen/README.md @@ -81,12 +81,12 @@ After there is build system directory, the shared library can be built using the This will build a `cmake_build/libsummabmi..` file, where the version is configured within the CMake config, and the extension depends on the local machine's operating system. -There is an example of a bash script to build the ngen libraries at ngen/extern/summa/summa/build/cmake_ngen/build_ngen.[system_type].bash. These needs to be run at a level above the ngen directory. +There is an example of a bash script to build the ngen libraries at ngen/extern/summa/summa/build/cmake_ngen/build_ngen.[system_type].bash. These needs to be put a level above the ngen directory but run from the ngen directory: -To run a test basin, at a level above the ngen directory, run +To run a test basin, in the ngen directory, run cmake_build/ngen data/catchment_data.geojson "cat-27" ./data/nexus_data.geojson "nex-26" ./data/example_realization_config_w_summa.json -This command can be copied from ngen/extern/summa/summa/test_ngen/example_run.sh +This command can be copied from ngen/extern/summa/summa/test_ngen/example_run.sh and put a level above the ngen directory but run from the ngen directory. This test is currently the Celia Laugh test, not the ngen example catchment cat-27. The forcing and input data for that basin could be used once it is in the correct form. diff --git a/build/cmake_ngen/example_build_ngen.cluster.bash b/build/cmake_ngen/example_build_ngen.cluster.bash index b46825ac2..698ac1288 100644 --- a/build/cmake_ngen/example_build_ngen.cluster.bash +++ b/build/cmake_ngen/example_build_ngen.cluster.bash @@ -1,6 +1,6 @@ #!/bin/bash -# build nextgen on Copernicus +# build nextgen on Copernicus, from ngen directory put this one directory up and run this as ../example_build_ngen.bash #module load gnu module load boost @@ -12,29 +12,29 @@ module load openblas/0.3.17 module load netcdf-fortran/4.5.2 -cmake -B /ngen/extern/cfe/cmake_build -S /ngen/extern/cfe -cmake --build /ngen/extern/cfe/cmake_build --target all +cmake -B /extern/cfe/cmake_build -S /extern/cfe +cmake --build /extern/cfe/cmake_build --target all -cmake -B /ngen/extern/topmodel/cmake_build -S /ngen/extern/topmodel -cmake --build /ngen/extern/topmodel/cmake_build --target all +cmake -B /extern/topmodel/cmake_build -S /extern/topmodel +cmake --build /extern/topmodel/cmake_build --target all -cmake -B /ngen/extern/sloth/cmake_build -S /ngen/extern/sloth -cmake --build /ngen/extern/sloth/cmake_build --target all +cmake -B /extern/sloth/cmake_build -S /extern/sloth +cmake --build /extern/sloth/cmake_build --target all -cmake -B /ngen/extern/noah-owp-modular/cmake_build -S /ngen/extern/noah-owp-modular -cmake --build /ngen/extern/noah-owp-modular/cmake_build --target all +cmake -B /extern/noah-owp-modular/cmake_build -S /extern/noah-owp-modular +cmake --build /extern/noah-owp-modular/cmake_build --target all -cmake -B /ngen/extern/iso_c_fortran_bmi/cmake_build -S /ngen/extern/iso_c_fortran_bmi -cmake --build /ngen/extern/iso_c_fortran_bmi/cmake_build --target all +cmake -B /extern/iso_c_fortran_bmi/cmake_build -S /extern/iso_c_fortran_bmi +cmake --build /extern/iso_c_fortran_bmi/cmake_build --target all -cmake -B /ngen/extern/snow17/cmake_build -S /ngen/extern/snow17 -cmake --build /ngen/extern/snow17/cmake_build --target all +cmake -B /extern/snow17/cmake_build -S /extern/snow17 +cmake --build /extern/snow17/cmake_build --target all -cmake -B /ngen/extern/summa/cmake_build -S /ngen/extern/summa -cmake --build /ngen/extern/summa/cmake_build --target all +cmake -B /extern/summa/cmake_build -S /extern/summa +cmake --build /extern/summa/cmake_build --target all -cmake -B /ngen/extern/evapotranspiration/cmake_build -S /ngen/extern/evapotranspiration -cmake --build /ngen/extern/evapotranspiration/cmake_build --target all +cmake -B /extern/evapotranspiration/cmake_build -S /extern/evapotranspiration +cmake --build /extern/evapotranspiration/cmake_build --target all cmake -B cmake_build -S . -DMPI_ACTIVE:BOOL=ON -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ #cmake -B cmake_build -S . -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ diff --git a/build/cmake_ngen/example_build_ngen.mac.bash b/build/cmake_ngen/example_build_ngen.mac.bash index bac6b43c6..4e4332537 100644 --- a/build/cmake_ngen/example_build_ngen.mac.bash +++ b/build/cmake_ngen/example_build_ngen.mac.bash @@ -1,12 +1,12 @@ #!/bin/bash -# build nextgen on Mac +# build nextgen on Mac, from ngen directory put this one directory up and run this as ../example_build_ngen.mac.bash -cmake -B /ngen/extern/iso_c_fortran_bmi/cmake_build -S /ngen/extern/iso_c_fortran_bmi -cmake --build /ngen/extern/iso_c_fortran_bmi/cmake_build --target all +cmake -B /extern/iso_c_fortran_bmi/cmake_build -S /extern/iso_c_fortran_bmi +cmake --build /extern/iso_c_fortran_bmi/cmake_build --target all -cmake -B /ngen/extern/summa/cmake_build -S /ngen/extern/summa -cmake --build /ngen/extern/summa/cmake_build --target all +cmake -B /extern/summa/cmake_build -S /extern/summa +cmake --build /extern/summa/cmake_build --target all cmake -B /ngen/cmake_build -S . -DMPI_ACTIVE:BOOL=ON -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ #cmake -B /ngen/cmake_build -S . -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ From b8d5992bbfe8fbe4a627377ea10fbd83b70cb58d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Mar 2023 15:18:36 +0900 Subject: [PATCH 0613/1472] more bash files --- .../example_build_ngen.cluster.bash | 32 +++++++++---------- build/cmake_ngen/example_build_ngen.mac.bash | 8 ++--- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/build/cmake_ngen/example_build_ngen.cluster.bash b/build/cmake_ngen/example_build_ngen.cluster.bash index 698ac1288..4e68560f9 100644 --- a/build/cmake_ngen/example_build_ngen.cluster.bash +++ b/build/cmake_ngen/example_build_ngen.cluster.bash @@ -12,29 +12,29 @@ module load openblas/0.3.17 module load netcdf-fortran/4.5.2 -cmake -B /extern/cfe/cmake_build -S /extern/cfe -cmake --build /extern/cfe/cmake_build --target all +cmake -B extern/cfe/cmake_build -S extern/cfe +cmake --build extern/cfe/cmake_build --target all -cmake -B /extern/topmodel/cmake_build -S /extern/topmodel -cmake --build /extern/topmodel/cmake_build --target all +cmake -B extern/topmodel/cmake_build -S extern/topmodel +cmake --build extern/topmodel/cmake_build --target all -cmake -B /extern/sloth/cmake_build -S /extern/sloth -cmake --build /extern/sloth/cmake_build --target all +cmake -B extern/sloth/cmake_build -S extern/sloth +cmake --build extern/sloth/cmake_build --target all -cmake -B /extern/noah-owp-modular/cmake_build -S /extern/noah-owp-modular -cmake --build /extern/noah-owp-modular/cmake_build --target all +cmake -B extern/noah-owp-modular/cmake_build -S extern/noah-owp-modular +cmake --build extern/noah-owp-modular/cmake_build --target all -cmake -B /extern/iso_c_fortran_bmi/cmake_build -S /extern/iso_c_fortran_bmi -cmake --build /extern/iso_c_fortran_bmi/cmake_build --target all +cmake -B extern/iso_c_fortran_bmi/cmake_build -S extern/iso_c_fortran_bmi +cmake --build extern/iso_c_fortran_bmi/cmake_build --target all -cmake -B /extern/snow17/cmake_build -S /extern/snow17 -cmake --build /extern/snow17/cmake_build --target all +cmake -B extern/snow17/cmake_build -S extern/snow17 +cmake --build extern/snow17/cmake_build --target all -cmake -B /extern/summa/cmake_build -S /extern/summa -cmake --build /extern/summa/cmake_build --target all +cmake -B extern/summa/cmake_build -S extern/summa +cmake --build extern/summa/cmake_build --target all -cmake -B /extern/evapotranspiration/cmake_build -S /extern/evapotranspiration -cmake --build /extern/evapotranspiration/cmake_build --target all +cmake -B extern/evapotranspiration/cmake_build -S extern/evapotranspiration +cmake --build extern/evapotranspiration/cmake_build --target all cmake -B cmake_build -S . -DMPI_ACTIVE:BOOL=ON -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ #cmake -B cmake_build -S . -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ diff --git a/build/cmake_ngen/example_build_ngen.mac.bash b/build/cmake_ngen/example_build_ngen.mac.bash index 4e4332537..379a61c19 100644 --- a/build/cmake_ngen/example_build_ngen.mac.bash +++ b/build/cmake_ngen/example_build_ngen.mac.bash @@ -2,11 +2,11 @@ # build nextgen on Mac, from ngen directory put this one directory up and run this as ../example_build_ngen.mac.bash -cmake -B /extern/iso_c_fortran_bmi/cmake_build -S /extern/iso_c_fortran_bmi -cmake --build /extern/iso_c_fortran_bmi/cmake_build --target all +cmake -B extern/iso_c_fortran_bmi/cmake_build -S extern/iso_c_fortran_bmi +cmake --build extern/iso_c_fortran_bmi/cmake_build --target all -cmake -B /extern/summa/cmake_build -S /extern/summa -cmake --build /extern/summa/cmake_build --target all +cmake -B extern/summa/cmake_build -S extern/summa +cmake --build extern/summa/cmake_build --target all cmake -B /ngen/cmake_build -S . -DMPI_ACTIVE:BOOL=ON -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ #cmake -B /ngen/cmake_build -S . -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ From 72b89868f5a579ac5bbd22386ad679fdd61c92b2 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Mar 2023 19:11:38 +0900 Subject: [PATCH 0614/1472] missing () and evaportranspiration not building correctly so comment out --- build/cmake_ngen/bmi.cmake | 24 +++++++++---------- build/cmake_ngen/example_CMakeLists.txt | 4 ++-- .../example_build_ngen.cluster.bash | 5 ++-- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/build/cmake_ngen/bmi.cmake b/build/cmake_ngen/bmi.cmake index 029e41a0c..14d0f7dd4 100644 --- a/build/cmake_ngen/bmi.cmake +++ b/build/cmake_ngen/bmi.cmake @@ -19,25 +19,25 @@ function(compile_with_ida ROOT_DIR, F_MASTER, DIR_SUNDIALS, INCLUDES, LIBRARIES, #======================================================================== # PART 2: Assemble all of the SUMMA sub-routines #======================================================================== - + # utilities set(NRUTIL ${ENGINE_DIR}/nrtype.f90 ${ENGINE_DIR}/f2008funcs.f90 ${ENGINE_DIR}/nr_utility.f90) - + # Numerical recipes procedures # NOTE: all numerical recipes procedures are now replaced with free versions set(NRPROC ${ENGINE_DIR}/expIntegral.f90 ${ENGINE_DIR}/spline_int.f90) -# Hook-up modules +# Hook-up modules set(HOOKUP ${HOOKUP_DIR}/ascii_util.f90 ${HOOKUP_DIR}/summaFileManager.f90) -# Data modules +# Data modules set(DATAMS ${DSHARE_DIR}/multiconst.f90 ${DSHARE_DIR}/var_lookup.f90 @@ -74,7 +74,7 @@ function(compile_with_ida ROOT_DIR, F_MASTER, DIR_SUNDIALS, INCLUDES, LIBRARIES, ${ENGINE_DIR}/bigAquifer.f90 ${ENGINE_DIR}/computFlux.f90 ${ENGINE_DIR}/type4IDA.f90 - ${ENGINE_DIR}/tol4IDA.f90 + ${ENGINE_DIR}/tol4IDA.f90 ${ENGINE_DIR}/computEnthalpy.f90 ${ENGINE_DIR}/computHeatCap.f90 ${ENGINE_DIR}/computThermConduct.f90 @@ -94,7 +94,7 @@ function(compile_with_ida ROOT_DIR, F_MASTER, DIR_SUNDIALS, INCLUDES, LIBRARIES, ${ENGINE_DIR}/coupled_em.f90 ${ENGINE_DIR}/run_oneHRU.f90 ${ENGINE_DIR}/run_oneGRU.f90) - + # Define routines for SUMMA preliminaries set(PRELIM ${ENGINE_DIR}/conv_funcs.f90 @@ -110,14 +110,14 @@ function(compile_with_ida ROOT_DIR, F_MASTER, DIR_SUNDIALS, INCLUDES, LIBRARIES, ${ENGINE_DIR}/read_param.f90 ${ENGINE_DIR}/paramCheck.f90 ${ENGINE_DIR}/check_icond.f90) - + set(NOAHMP ${NOAHMP_DIR}/module_model_constants.F ${NOAHMP_DIR}/module_sf_noahutl.F ${NOAHMP_DIR}/module_sf_noahlsm.F ${NOAHMP_DIR}/module_sf_noahmplsm.F) -# Define routines for the SUMMA model runs +# Define routines for the SUMMA model runs set(MODRUN ${ENGINE_DIR}/indexState.f90 ${ENGINE_DIR}/getVectorz.f90 @@ -160,7 +160,7 @@ function(compile_with_ida ROOT_DIR, F_MASTER, DIR_SUNDIALS, INCLUDES, LIBRARIES, # run program files, do not use in ngen # set(SUMMA ${DRIVER_DIR}/summa_run.f90) - # set(BMI ${DRIVER_DIR}/summa_runBMI.f90) + # set(BMI ${DRIVER_DIR}/summa_runBMI.f90) set(COMM_ALL ${NRUTIL} @@ -179,7 +179,7 @@ function(compile_with_ida ROOT_DIR, F_MASTER, DIR_SUNDIALS, INCLUDES, LIBRARIES, #======================================================================== # PART 4: compilation #====================================================================== - + add_library(SUMMA_NOAHMP OBJECT ${NOAHMP} ${NRUTIL}) target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) @@ -191,9 +191,9 @@ function(compile_with_ida ROOT_DIR, F_MASTER, DIR_SUNDIALS, INCLUDES, LIBRARIES, # Build SUMMA Shared Library, add BMI libraries outside this function if(WIN32) add_library(summabmi ${SUMMA_ALL}) - else + else() add_library(summabmi SHARED ${SUMMA_ALL}) - endif + endif() target_compile_options(summabmi PRIVATE ${FLAGS_ALL}) target_include_directories(summabmi PUBLIC ${INCLUDES} ${INC_SUNDIALS}) target_link_libraries(summabmi PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_COMM) diff --git a/build/cmake_ngen/example_CMakeLists.txt b/build/cmake_ngen/example_CMakeLists.txt index 7ed7b7538..156b6384c 100644 --- a/build/cmake_ngen/example_CMakeLists.txt +++ b/build/cmake_ngen/example_CMakeLists.txt @@ -26,7 +26,7 @@ SET (CMAKE_Fortran_COMPILER gfortran) include($(F_MASTER)/build/cmake_ngen/bmi.cmake) # -------------------------------------------------------------------------------------------- -# When compiling Summa-Sundials it can be compiled in a Cluster or Debug mode, which is set by +# When compiling Summa-Sundials it can be compiled in a Cluster or Debug mode, which is set by # the CMAKE_BUILD_TYPES variables. The default is Release, non-cluster mode. ######### SET THE PATHS TO THE SUNDIALS LIBRARIES AND INCLUDE FILES ######### @@ -72,7 +72,7 @@ if(CMAKE_BUILD_TYPE MATCHES Cluster OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib64") set(LIB_SUNDIALS $(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) -else +else() set(INCLUDES /opt/local/include -I/opt/local/lib) set(LDFLAGS /opt/local/lib) set(LIBRARIES $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf) diff --git a/build/cmake_ngen/example_build_ngen.cluster.bash b/build/cmake_ngen/example_build_ngen.cluster.bash index 4e68560f9..0c96d5bcd 100644 --- a/build/cmake_ngen/example_build_ngen.cluster.bash +++ b/build/cmake_ngen/example_build_ngen.cluster.bash @@ -33,8 +33,9 @@ cmake --build extern/snow17/cmake_build --target all cmake -B extern/summa/cmake_build -S extern/summa cmake --build extern/summa/cmake_build --target all -cmake -B extern/evapotranspiration/cmake_build -S extern/evapotranspiration -cmake --build extern/evapotranspiration/cmake_build --target all +# does not work currently, can't find files +#cmake -B extern/evapotranspiration/cmake_build -S extern/evapotranspiration +#cmake --build extern/evapotranspiration/cmake_build --target all cmake -B cmake_build -S . -DMPI_ACTIVE:BOOL=ON -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ #cmake -B cmake_build -S . -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ From a1b94a0118e4b96212bc6f6a59a57a8757e982be Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Mar 2023 19:16:42 +0900 Subject: [PATCH 0615/1472] errors in names --- build/cmake_ngen/bmi.cmake | 2 +- build/cmake_ngen/example_CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/cmake_ngen/bmi.cmake b/build/cmake_ngen/bmi.cmake index 14d0f7dd4..a359ff56e 100644 --- a/build/cmake_ngen/bmi.cmake +++ b/build/cmake_ngen/bmi.cmake @@ -1,4 +1,4 @@ -function(compile_with_ida ROOT_DIR, F_MASTER, DIR_SUNDIALS, INCLUDES, LIBRARIES, INC_SUNDIALS, LIB_SUNDIALS, FLAGS_ALL, FLAGS_NOAH) +function(compile_with_bmi ROOT_DIR, F_MASTER, DIR_SUNDIALS, INCLUDES, LIBRARIES, INC_SUNDIALS, LIB_SUNDIALS, FLAGS_ALL, FLAGS_NOAH) #======================================================================== # PART 1: Define directory paths diff --git a/build/cmake_ngen/example_CMakeLists.txt b/build/cmake_ngen/example_CMakeLists.txt index 156b6384c..f54fd37a5 100644 --- a/build/cmake_ngen/example_CMakeLists.txt +++ b/build/cmake_ngen/example_CMakeLists.txt @@ -1,4 +1,4 @@ -ccmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.10) enable_language( Fortran ) #Get the iso_c_fortran binding module to build as part of this build From 09de3c8c739e93017916d991ea9a8789858b9904 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Mar 2023 20:06:23 +0900 Subject: [PATCH 0616/1472] typos and update file names --- build/cmake_ngen/bmi.cmake | 2 +- ...n.cluster.bash => build_ngen.cluster.bash} | 0 ...uild_ngen.mac.bash => build_ngen.mac.bash} | 0 build/cmake_ngen/example_CMakeLists.txt | 107 ------------------ build/cmake_ngen/load_modules.sh | 7 -- build/cmake_ngen/summabmi.pc.in | 12 ++ test_ngen/example_run_celia.sh | 2 +- 7 files changed, 14 insertions(+), 116 deletions(-) rename build/cmake_ngen/{example_build_ngen.cluster.bash => build_ngen.cluster.bash} (100%) rename build/cmake_ngen/{example_build_ngen.mac.bash => build_ngen.mac.bash} (100%) delete mode 100644 build/cmake_ngen/example_CMakeLists.txt delete mode 100755 build/cmake_ngen/load_modules.sh create mode 100644 build/cmake_ngen/summabmi.pc.in diff --git a/build/cmake_ngen/bmi.cmake b/build/cmake_ngen/bmi.cmake index a359ff56e..fa885a4fa 100644 --- a/build/cmake_ngen/bmi.cmake +++ b/build/cmake_ngen/bmi.cmake @@ -43,7 +43,7 @@ function(compile_with_bmi ROOT_DIR, F_MASTER, DIR_SUNDIALS, INCLUDES, LIBRARIES, ${DSHARE_DIR}/var_lookup.f90 ${DSHARE_DIR}/data_types.f90 ${DSHARE_DIR}/globalData.f90 - ${DSHARE_DIR}/flxMapping.f90) + ${DSHARE_DIR}/flxMapping.f90 ${DSHARE_DIR}/get_ixname.f90 ${DSHARE_DIR}/popMetadat.f90 ${DSHARE_DIR}/outpt_stat.f90) diff --git a/build/cmake_ngen/example_build_ngen.cluster.bash b/build/cmake_ngen/build_ngen.cluster.bash similarity index 100% rename from build/cmake_ngen/example_build_ngen.cluster.bash rename to build/cmake_ngen/build_ngen.cluster.bash diff --git a/build/cmake_ngen/example_build_ngen.mac.bash b/build/cmake_ngen/build_ngen.mac.bash similarity index 100% rename from build/cmake_ngen/example_build_ngen.mac.bash rename to build/cmake_ngen/build_ngen.mac.bash diff --git a/build/cmake_ngen/example_CMakeLists.txt b/build/cmake_ngen/example_CMakeLists.txt deleted file mode 100644 index f54fd37a5..000000000 --- a/build/cmake_ngen/example_CMakeLists.txt +++ /dev/null @@ -1,107 +0,0 @@ -cmake_minimum_required(VERSION 3.10) -enable_language( Fortran ) - -#Get the iso_c_fortran binding module to build as part of this build -add_subdirectory(../iso_c_fortran_bmi ${CMAKE_BINARY_DIR}/iso_c_bmi) - - -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../../cmake/") - -project(summabmi VERSION 1.0.0 DESCRIPTION "Summa-Sundials BMI Module Shared Library") - -set( CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran) - -#### Add variables for individual libraries that are used within the *.pc.in files -set(SUMMA_LIB_NAME_CMAKE summabmi) -set(SUMMA_LIB_DESC_CMAKE "Summa-Sundials BMI Module Shared Library") - -# Make sure these are compiled with this directive UNCLEAR IF WE NEED THESE -add_compile_definitions(BMI_ACTIVE NGEN_FORCING_ACTIVE NGEN_OUTPUT_ACTIVE NGEN_ACTIVE) - - -set(ROOT_DIR ../../../) -set(F_MASTER $(ROOT_DIR)/summa) - -SET (CMAKE_Fortran_COMPILER gfortran) - -include($(F_MASTER)/build/cmake_ngen/bmi.cmake) -# -------------------------------------------------------------------------------------------- -# When compiling Summa-Sundials it can be compiled in a Cluster or Debug mode, which is set by -# the CMAKE_BUILD_TYPES variables. The default is Release, non-cluster mode. - -######### SET THE PATHS TO THE SUNDIALS LIBRARIES AND INCLUDE FILES ######### -############################################################################# - -# Add options for build type -set(CMAKE_BUILD_TYPES Debug Release Cluster_Debug Cluster) - -# Set default configuration type to Release -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release) -endif() - -# Set compiler flags -if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) - message("Debug build.") - add_definitions(-DDEBUG) - set(FLAGS_NOAH -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) - set(FLAGS_ALL -g -O0 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) -else() - message("Release build.") - set(FLAGS_NOAH -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) - set(FLAGS_ALL -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) -endif() - - -# Packages that are required -find_package(netCDF REQUIRED) -find_package(LAPACK REQUIRED) - -set(DIR_SUNDIALS= $(ROOT_DIR)/sundials/instdir) -set(INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran) - -# Set include directories -if(CMAKE_BUILD_TYPE MATCHES Cluster OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) - set(INCLUDES $(EBROOTNETCDFMINFORTRAN)/include) - set(LDFLAGS $(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib) - set(LIBRARIES $(LDFLAGS) -lnetcdff -lopenblas) - - message("\nBuilding IDA\n") - - link_directories(${DIR_SUNDIALS}/lib64) - set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib64") - set(LIB_SUNDIALS $(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) - -else() - set(INCLUDES /opt/local/include -I/opt/local/lib) - set(LDFLAGS /opt/local/lib) - set(LIBRARIES $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf) - - message("\nBuilding IDA\n") - - link_directories(${DIR_SUNDIALS}/lib) - set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib") - set(LIB_SUNDIALS $(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) - - -endif() - - -message("\nBuilding SUMMA\n") -compile_with_bmi($(ROOT_DIR), $(F_MASTER), $(DIR_SUNDIALS), $(INCLUDES), $(LIBRARIES), $(INC_SUNDIALS), $(LIB_SUNDIALS), $(FLAGS_ALL), $(FLAGS_NOAH)) - - -target_link_libraries(summabmi PUBLIC iso_c_bmi) -target_compile_options(summabmi PUBLIC -cpp -DNGEN_ACTIVE) - -set_target_properties(summabmi PROPERTIES VERSION ${PROJECT_VERSION}) - -include(GNUInstallDirs) - -install(TARGETS summabmi - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - -configure_file(summabmi.pc.in summabmi.pc @ONLY) - -install(FILES ${CMAKE_BINARY_DIR}/summabmi.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) diff --git a/build/cmake_ngen/load_modules.sh b/build/cmake_ngen/load_modules.sh deleted file mode 100755 index a477a811d..000000000 --- a/build/cmake_ngen/load_modules.sh +++ /dev/null @@ -1,7 +0,0 @@ -#! /bin/bash - -#### load modules if using Compute Canada or Copernicus #### -module load StdEnv/2020 -module load gcc/9.3.0 -module load openblas/0.3.17 -module load netcdf-fortran/4.5.2 \ No newline at end of file diff --git a/build/cmake_ngen/summabmi.pc.in b/build/cmake_ngen/summabmi.pc.in new file mode 100644 index 000000000..f8810d6ff --- /dev/null +++ b/build/cmake_ngen/summabmi.pc.in @@ -0,0 +1,12 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=@CMAKE_INSTALL_PREFIX@ +libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ + +Name: @SUMMA_LIB_NAME_CMAKE@ +Description: @SUMMA_DESC_CMAKE@ +Version: @PROJECT_VERSION@ + +Requires: +Libs: -L${libdir} -lmylib +Cflags: -I${includedir} \ No newline at end of file diff --git a/test_ngen/example_run_celia.sh b/test_ngen/example_run_celia.sh index b2bda13bb..787d9f05a 100644 --- a/test_ngen/example_run_celia.sh +++ b/test_ngen/example_run_celia.sh @@ -1 +1 @@ -cmake_build/ngen data/catchment_data.geojson "cat-27" ./data/nexus_data.geojson "nex-26" ./data/example_bmi_multi_realization_config_w_summa.json \ No newline at end of file +cmake_build/ngen data/catchment_data.geojson "cat-27" ./data/nexus_data.geojson "nex-26" ./data/example_realization_config_w_summa_bmi.json \ No newline at end of file From 1634772577d28ed5f7266d679d3ee8a48468b282 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Mar 2023 20:08:14 +0900 Subject: [PATCH 0617/1472] add file --- build/cmake_ngen/CMakeLists.txt | 107 ++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 build/cmake_ngen/CMakeLists.txt diff --git a/build/cmake_ngen/CMakeLists.txt b/build/cmake_ngen/CMakeLists.txt new file mode 100644 index 000000000..112fb4731 --- /dev/null +++ b/build/cmake_ngen/CMakeLists.txt @@ -0,0 +1,107 @@ +cmake_minimum_required(VERSION 3.10) +enable_language( Fortran ) + +#Get the iso_c_fortran binding module to build as part of this build +add_subdirectory(../iso_c_fortran_bmi ${CMAKE_BINARY_DIR}/iso_c_bmi) + + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../../cmake/") + +project(summabmi VERSION 1.0.0 DESCRIPTION "Summa-Sundials BMI Module Shared Library") + +set( CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran) + +#### Add variables for individual libraries that are used within the *.pc.in files +set(SUMMA_LIB_NAME_CMAKE summabmi) +set(SUMMA_LIB_DESC_CMAKE "Summa-Sundials BMI Module Shared Library") + +# Make sure these are compiled with this directive UNCLEAR IF WE NEED THESE +add_compile_definitions(BMI_ACTIVE NGEN_FORCING_ACTIVE NGEN_OUTPUT_ACTIVE NGEN_ACTIVE) + + +set(ROOT_DIR ../../../) +set(F_MASTER $(ROOT_DIR)/summa) + +SET (CMAKE_Fortran_COMPILER gfortran) + +include(bmi.cmake) +# -------------------------------------------------------------------------------------------- +# When compiling Summa-Sundials it can be compiled in a Cluster or Debug mode, which is set by +# the CMAKE_BUILD_TYPES variables. The default is Release, non-cluster mode. + +######### SET THE PATHS TO THE SUNDIALS LIBRARIES AND INCLUDE FILES ######### +############################################################################# + +# Add options for build type +set(CMAKE_BUILD_TYPES Debug Release Cluster_Debug Cluster) + +# Set default configuration type to Release +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +# Set compiler flags +if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) + message("Debug build.") + add_definitions(-DDEBUG) + set(FLAGS_NOAH -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) + set(FLAGS_ALL -g -O0 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) +else() + message("Release build.") + set(FLAGS_NOAH -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) + set(FLAGS_ALL -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) +endif() + + +# Packages that are required +find_package(netCDF REQUIRED) +find_package(LAPACK REQUIRED) + +set(DIR_SUNDIALS= $(ROOT_DIR)/sundials/instdir) +set(INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran) + +# Set include directories +if(CMAKE_BUILD_TYPE MATCHES Cluster OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) + set(INCLUDES $(EBROOTNETCDFMINFORTRAN)/include) + set(LDFLAGS $(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib) + set(LIBRARIES $(LDFLAGS) -lnetcdff -lopenblas) + + message("\nBuilding IDA\n") + + link_directories(${DIR_SUNDIALS}/lib64) + set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib64") + set(LIB_SUNDIALS $(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) + +else() + set(INCLUDES /opt/local/include -I/opt/local/lib) + set(LDFLAGS /opt/local/lib) + set(LIBRARIES $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf) + + message("\nBuilding IDA\n") + + link_directories(${DIR_SUNDIALS}/lib) + set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib") + set(LIB_SUNDIALS $(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) + + +endif() + + +message("\nBuilding SUMMA\n") +compile_with_bmi($(ROOT_DIR), $(F_MASTER), $(DIR_SUNDIALS), $(INCLUDES), $(LIBRARIES), $(INC_SUNDIALS), $(LIB_SUNDIALS), $(FLAGS_ALL), $(FLAGS_NOAH)) + + +target_link_libraries(summabmi PUBLIC iso_c_bmi) +target_compile_options(summabmi PUBLIC -cpp -DNGEN_ACTIVE) + +set_target_properties(summabmi PROPERTIES VERSION ${PROJECT_VERSION}) + +include(GNUInstallDirs) + +install(TARGETS summabmi + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + +configure_file(summabmi.pc.in summabmi.pc @ONLY) + +install(FILES ${CMAKE_BINARY_DIR}/summabmi.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) From 0f99bfe816abdf971bb2a4b6fe40098693d3578e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Mar 2023 20:28:30 +0900 Subject: [PATCH 0618/1472] fixed brackets --- build/cmake_ngen/CMakeLists.txt | 16 ++++++++-------- build/cmake_ngen/bmi.cmake | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/build/cmake_ngen/CMakeLists.txt b/build/cmake_ngen/CMakeLists.txt index 112fb4731..947e1ca41 100644 --- a/build/cmake_ngen/CMakeLists.txt +++ b/build/cmake_ngen/CMakeLists.txt @@ -20,7 +20,7 @@ add_compile_definitions(BMI_ACTIVE NGEN_FORCING_ACTIVE NGEN_OUTPUT_ACTIVE NGEN_A set(ROOT_DIR ../../../) -set(F_MASTER $(ROOT_DIR)/summa) +set(F_MASTER ${ROOT_DIR}/summa) SET (CMAKE_Fortran_COMPILER gfortran) @@ -57,20 +57,20 @@ endif() find_package(netCDF REQUIRED) find_package(LAPACK REQUIRED) -set(DIR_SUNDIALS= $(ROOT_DIR)/sundials/instdir) -set(INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran) +set(DIR_SUNDIALS= ${ROOT_DIR}/sundials/instdir) +set(INC_SUNDIALS=-I${DIR_SUNDIALS}/include -I${DIR_SUNDIALS}/fortran) # Set include directories if(CMAKE_BUILD_TYPE MATCHES Cluster OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) - set(INCLUDES $(EBROOTNETCDFMINFORTRAN)/include) - set(LDFLAGS $(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib) + set(INCLUDES ${EBROOTNETCDFMINFORTRAN}/include) + set(LDFLAGS ${EBROOTNETCDFMINFORTRAN}/lib64 -L${EBROOTOPENBLAS}/lib) set(LIBRARIES $(LDFLAGS) -lnetcdff -lopenblas) message("\nBuilding IDA\n") link_directories(${DIR_SUNDIALS}/lib64) set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib64") - set(LIB_SUNDIALS $(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) + set(LIB_SUNDIALS ${DIR_SUNDIALS}/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) else() set(INCLUDES /opt/local/include -I/opt/local/lib) @@ -81,14 +81,14 @@ else() link_directories(${DIR_SUNDIALS}/lib) set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib") - set(LIB_SUNDIALS $(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) + set(LIB_SUNDIALS ${DIR_SUNDIALS}/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) endif() message("\nBuilding SUMMA\n") -compile_with_bmi($(ROOT_DIR), $(F_MASTER), $(DIR_SUNDIALS), $(INCLUDES), $(LIBRARIES), $(INC_SUNDIALS), $(LIB_SUNDIALS), $(FLAGS_ALL), $(FLAGS_NOAH)) +compile_with_bmi(${ROOT_DIR}, ${F_MASTER}, ${DIR_SUNDIALS}, ${INCLUDES}, ${LIBRARIES}, ${INC_SUNDIALS}, ${LIB_SUNDIALS}, ${FLAGS_ALL}, ${FLAGS_NOAH}) target_link_libraries(summabmi PUBLIC iso_c_bmi) diff --git a/build/cmake_ngen/bmi.cmake b/build/cmake_ngen/bmi.cmake index fa885a4fa..906aceae1 100644 --- a/build/cmake_ngen/bmi.cmake +++ b/build/cmake_ngen/bmi.cmake @@ -5,16 +5,16 @@ function(compile_with_bmi ROOT_DIR, F_MASTER, DIR_SUNDIALS, INCLUDES, LIBRARIES, #======================================================================== # Core directory that contains source code - set(F_KORE_DIR $(F_MASTER)/build/source) + set(F_KORE_DIR ${F_MASTER}/build/source) # Define directories - set(DRIVER_DIR $(F_KORE_DIR)/driver) - set(HOOKUP_DIR $(F_KORE_DIR)/hookup) - set(NETCDF_DIR $(F_KORE_DIR)/netcdf) - set(DSHARE_DIR $(F_KORE_DIR)/dshare) - set(NUMREC_DIR $(F_KORE_DIR)/numrec) - set(NOAHMP_DIR $(F_KORE_DIR)/noah-mp) - set(ENGINE_DIR $(F_KORE_DIR)/engine) + set(DRIVER_DIR ${F_KORE_DIR}/driver) + set(HOOKUP_DIR ${F_KORE_DIR}/hookup) + set(NETCDF_DIR ${F_KORE_DIR}/netcdf) + set(DSHARE_DIR ${F_KORE_DIR}/dshare) + set(NUMREC_DIR ${F_KORE_DIR}/numrec) + set(NOAHMP_DIR ${F_KORE_DIR}/noah-mp) + set(ENGINE_DIR ${F_KORE_DIR}/engine) #======================================================================== # PART 2: Assemble all of the SUMMA sub-routines From fc85823960cb97088affddc50e1de8adc735a8b6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Mar 2023 21:51:14 +0900 Subject: [PATCH 0619/1472] libraries set incorrectly --- build/cmake_ngen/CMakeLists.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/build/cmake_ngen/CMakeLists.txt b/build/cmake_ngen/CMakeLists.txt index 947e1ca41..b421a99ee 100644 --- a/build/cmake_ngen/CMakeLists.txt +++ b/build/cmake_ngen/CMakeLists.txt @@ -19,7 +19,7 @@ set(SUMMA_LIB_DESC_CMAKE "Summa-Sundials BMI Module Shared Library") add_compile_definitions(BMI_ACTIVE NGEN_FORCING_ACTIVE NGEN_OUTPUT_ACTIVE NGEN_ACTIVE) -set(ROOT_DIR ../../../) +set(ROOT_DIR ./) set(F_MASTER ${ROOT_DIR}/summa) SET (CMAKE_Fortran_COMPILER gfortran) @@ -62,25 +62,25 @@ set(INC_SUNDIALS=-I${DIR_SUNDIALS}/include -I${DIR_SUNDIALS}/fortran) # Set include directories if(CMAKE_BUILD_TYPE MATCHES Cluster OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) - set(INCLUDES ${EBROOTNETCDFMINFORTRAN}/include) - set(LDFLAGS ${EBROOTNETCDFMINFORTRAN}/lib64 -L${EBROOTOPENBLAS}/lib) - set(LIBRARIES $(LDFLAGS) -lnetcdff -lopenblas) + set(INCLUDES $ENV{EBROOTNETCDFMINFORTRAN}/include ${netCDF_INCLUDES} ${LAPACK_INCLUDES}) + set(LIBRARIES ${netCDF_LIBRARIES} ${LAPACK_LIBRARIES} -lnetcdff -lopenblas) message("\nBuilding IDA\n") link_directories(${DIR_SUNDIALS}/lib64) set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib64") + set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran set(LIB_SUNDIALS ${DIR_SUNDIALS}/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) else() - set(INCLUDES /opt/local/include -I/opt/local/lib) - set(LDFLAGS /opt/local/lib) - set(LIBRARIES $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf) + set(INCLUDES /opt/local/include ${netCDF_INCLUDES} ${LAPACK_INCLUDES}) + set(LIBRARIES ${netCDF_LIBRARIES} ${LAPACK_LIBRARIES} -llapack -lgfortran -lnetcdff -lnetcdf) message("\nBuilding IDA\n") link_directories(${DIR_SUNDIALS}/lib) set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib") + set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran set(LIB_SUNDIALS ${DIR_SUNDIALS}/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) From d2fdf9f35bcc860d9dbc757adf501e3f231f77d6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Mar 2023 22:11:40 +0900 Subject: [PATCH 0620/1472] fixing linking --- build/cmake_ngen/CMakeLists.txt | 16 ++++++++-------- build/cmake_ngen/bmi.cmake | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/build/cmake_ngen/CMakeLists.txt b/build/cmake_ngen/CMakeLists.txt index b421a99ee..9fe581f57 100644 --- a/build/cmake_ngen/CMakeLists.txt +++ b/build/cmake_ngen/CMakeLists.txt @@ -27,7 +27,7 @@ SET (CMAKE_Fortran_COMPILER gfortran) include(bmi.cmake) # -------------------------------------------------------------------------------------------- # When compiling Summa-Sundials it can be compiled in a Cluster or Debug mode, which is set by -# the CMAKE_BUILD_TYPES variables. The default is Release, non-cluster mode. +# the CMAKE_BUILD_TYPES variables. The default is Release, cluster mode. ######### SET THE PATHS TO THE SUNDIALS LIBRARIES AND INCLUDE FILES ######### ############################################################################# @@ -37,7 +37,7 @@ set(CMAKE_BUILD_TYPES Debug Release Cluster_Debug Cluster) # Set default configuration type to Release if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release) + set(CMAKE_BUILD_TYPE Cluster) endif() # Set compiler flags @@ -63,25 +63,25 @@ set(INC_SUNDIALS=-I${DIR_SUNDIALS}/include -I${DIR_SUNDIALS}/fortran) # Set include directories if(CMAKE_BUILD_TYPE MATCHES Cluster OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) set(INCLUDES $ENV{EBROOTNETCDFMINFORTRAN}/include ${netCDF_INCLUDES} ${LAPACK_INCLUDES}) - set(LIBRARIES ${netCDF_LIBRARIES} ${LAPACK_LIBRARIES} -lnetcdff -lopenblas) + set(LIBRARIES NOAHMP ${netCDF_LIBRARIES} ${LAPACK_LIBRARIES} -lnetcdff -lopenblas) message("\nBuilding IDA\n") link_directories(${DIR_SUNDIALS}/lib64) set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib64") - set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran - set(LIB_SUNDIALS ${DIR_SUNDIALS}/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) + set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran) + set(LIB_SUNDIALS -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) else() set(INCLUDES /opt/local/include ${netCDF_INCLUDES} ${LAPACK_INCLUDES}) - set(LIBRARIES ${netCDF_LIBRARIES} ${LAPACK_LIBRARIES} -llapack -lgfortran -lnetcdff -lnetcdf) + set(LIBRARIES NOAHMP ${netCDF_LIBRARIES} ${LAPACK_LIBRARIES} -llapack -lgfortran -lnetcdff -lnetcdf) message("\nBuilding IDA\n") link_directories(${DIR_SUNDIALS}/lib) set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib") - set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran - set(LIB_SUNDIALS ${DIR_SUNDIALS}/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) + set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran) + set(LIB_SUNDIALS -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) endif() diff --git a/build/cmake_ngen/bmi.cmake b/build/cmake_ngen/bmi.cmake index 906aceae1..d5c24ae08 100644 --- a/build/cmake_ngen/bmi.cmake +++ b/build/cmake_ngen/bmi.cmake @@ -196,7 +196,7 @@ function(compile_with_bmi ROOT_DIR, F_MASTER, DIR_SUNDIALS, INCLUDES, LIBRARIES, endif() target_compile_options(summabmi PRIVATE ${FLAGS_ALL}) target_include_directories(summabmi PUBLIC ${INCLUDES} ${INC_SUNDIALS}) - target_link_libraries(summabmi PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_COMM) + target_link_libraries(summabmi PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_COMM SUMMA_NOAHMP) endfunction() \ No newline at end of file From b6af2a922bcdcb03bad55a3b72214ff200c9652b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Mar 2023 23:03:24 +0900 Subject: [PATCH 0621/1472] noah mp object --- build/cmake_ngen/CMakeLists.txt | 4 ++-- build/cmake_ngen/bmi.cmake | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/cmake_ngen/CMakeLists.txt b/build/cmake_ngen/CMakeLists.txt index 9fe581f57..66258b0ab 100644 --- a/build/cmake_ngen/CMakeLists.txt +++ b/build/cmake_ngen/CMakeLists.txt @@ -63,7 +63,7 @@ set(INC_SUNDIALS=-I${DIR_SUNDIALS}/include -I${DIR_SUNDIALS}/fortran) # Set include directories if(CMAKE_BUILD_TYPE MATCHES Cluster OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) set(INCLUDES $ENV{EBROOTNETCDFMINFORTRAN}/include ${netCDF_INCLUDES} ${LAPACK_INCLUDES}) - set(LIBRARIES NOAHMP ${netCDF_LIBRARIES} ${LAPACK_LIBRARIES} -lnetcdff -lopenblas) + set(LIBRARIES SUMMA_NOAHMP ${netCDF_LIBRARIES} ${LAPACK_LIBRARIES} -lnetcdff -lopenblas) message("\nBuilding IDA\n") @@ -74,7 +74,7 @@ if(CMAKE_BUILD_TYPE MATCHES Cluster OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) else() set(INCLUDES /opt/local/include ${netCDF_INCLUDES} ${LAPACK_INCLUDES}) - set(LIBRARIES NOAHMP ${netCDF_LIBRARIES} ${LAPACK_LIBRARIES} -llapack -lgfortran -lnetcdff -lnetcdf) + set(LIBRARIES SUMMA_NOAHMP ${netCDF_LIBRARIES} ${LAPACK_LIBRARIES} -llapack -lgfortran -lnetcdff -lnetcdf) message("\nBuilding IDA\n") diff --git a/build/cmake_ngen/bmi.cmake b/build/cmake_ngen/bmi.cmake index d5c24ae08..906aceae1 100644 --- a/build/cmake_ngen/bmi.cmake +++ b/build/cmake_ngen/bmi.cmake @@ -196,7 +196,7 @@ function(compile_with_bmi ROOT_DIR, F_MASTER, DIR_SUNDIALS, INCLUDES, LIBRARIES, endif() target_compile_options(summabmi PRIVATE ${FLAGS_ALL}) target_include_directories(summabmi PUBLIC ${INCLUDES} ${INC_SUNDIALS}) - target_link_libraries(summabmi PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_COMM SUMMA_NOAHMP) + target_link_libraries(summabmi PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_COMM) endfunction() \ No newline at end of file From f823e2a8ad3ba8a65ebdffa25ab6c58a44c92888 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Mar 2023 23:30:04 +0900 Subject: [PATCH 0622/1472] put bmi.cmake inside CMakeLists because not working --- build/cmake_ngen/CMakeLists.txt | 205 +++++++++++++++++++++++++++++++- build/cmake_ngen/bmi.cmake | 2 +- 2 files changed, 205 insertions(+), 2 deletions(-) diff --git a/build/cmake_ngen/CMakeLists.txt b/build/cmake_ngen/CMakeLists.txt index 66258b0ab..e5c5e96d9 100644 --- a/build/cmake_ngen/CMakeLists.txt +++ b/build/cmake_ngen/CMakeLists.txt @@ -88,7 +88,210 @@ endif() message("\nBuilding SUMMA\n") -compile_with_bmi(${ROOT_DIR}, ${F_MASTER}, ${DIR_SUNDIALS}, ${INCLUDES}, ${LIBRARIES}, ${INC_SUNDIALS}, ${LIB_SUNDIALS}, ${FLAGS_ALL}, ${FLAGS_NOAH}) + + +#compile_with_bmi(${F_MASTER}, ${DIR_SUNDIALS}, ${INCLUDES}, ${LIBRARIES}, ${INC_SUNDIALS}, ${LIB_SUNDIALS}, ${FLAGS_ALL}, ${FLAGS_NOAH}) +#function(compile_with_bmi F_MASTER, DIR_SUNDIALS, INCLUDES, LIBRARIES, INC_SUNDIALS, LIB_SUNDIALS, FLAGS_ALL, FLAGS_NOAH) + +#======================================================================== +# PART 1: Define directory paths +#======================================================================== + +# Core directory that contains source code + set(F_KORE_DIR ${F_MASTER}/build/source) + +# Define directories + set(DRIVER_DIR ${F_KORE_DIR}/driver) + set(HOOKUP_DIR ${F_KORE_DIR}/hookup) + set(NETCDF_DIR ${F_KORE_DIR}/netcdf) + set(DSHARE_DIR ${F_KORE_DIR}/dshare) + set(NUMREC_DIR ${F_KORE_DIR}/numrec) + set(NOAHMP_DIR ${F_KORE_DIR}/noah-mp) + set(ENGINE_DIR ${F_KORE_DIR}/engine) + +#======================================================================== +# PART 2: Assemble all of the SUMMA sub-routines +#======================================================================== + +# utilities + set(NRUTIL + ${ENGINE_DIR}/nrtype.f90 + ${ENGINE_DIR}/f2008funcs.f90 + ${ENGINE_DIR}/nr_utility.f90) + +# Numerical recipes procedures +# NOTE: all numerical recipes procedures are now replaced with free versions + set(NRPROC + ${ENGINE_DIR}/expIntegral.f90 + ${ENGINE_DIR}/spline_int.f90) + +# Hook-up modules + set(HOOKUP + ${HOOKUP_DIR}/ascii_util.f90 + ${HOOKUP_DIR}/summaFileManager.f90) + +# Data modules + set(DATAMS + ${DSHARE_DIR}/multiconst.f90 + ${DSHARE_DIR}/var_lookup.f90 + ${DSHARE_DIR}/data_types.f90 + ${DSHARE_DIR}/globalData.f90 + ${DSHARE_DIR}/flxMapping.f90 + ${DSHARE_DIR}/get_ixname.f90 + ${DSHARE_DIR}/popMetadat.f90 + ${DSHARE_DIR}/outpt_stat.f90) + +# utility modules + set(UTILMS + ${ENGINE_DIR}/time_utils.f90 + ${ENGINE_DIR}/mDecisions.f90 + ${ENGINE_DIR}/snow_utils.f90 + ${ENGINE_DIR}/soil_utils.f90 + ${ENGINE_DIR}/soil_utilsAddSundials.f90 + ${ENGINE_DIR}/updatState.f90 + ${ENGINE_DIR}/updatStateSundials.f90 + ${ENGINE_DIR}/matrixOper.f90) + +# Solver + set(SOLVER + ${ENGINE_DIR}/vegPhenlgy.f90 + ${ENGINE_DIR}/diagn_evar.f90 + ${ENGINE_DIR}/stomResist.f90 + ${ENGINE_DIR}/groundwatr.f90 + ${ENGINE_DIR}/vegSWavRad.f90 + ${ENGINE_DIR}/vegNrgFlux.f90 + ${ENGINE_DIR}/ssdNrgFlux.f90 + ${ENGINE_DIR}/vegLiqFlux.f90 + ${ENGINE_DIR}/snowLiqFlx.f90 + ${ENGINE_DIR}/soilLiqFlx.f90 + ${ENGINE_DIR}/bigAquifer.f90 + ${ENGINE_DIR}/computFlux.f90 + ${ENGINE_DIR}/type4IDA.f90 + ${ENGINE_DIR}/tol4IDA.f90 + ${ENGINE_DIR}/computEnthalpy.f90 + ${ENGINE_DIR}/computHeatCap.f90 + ${ENGINE_DIR}/computThermConduct.f90 + ${ENGINE_DIR}/computResid.f90 + ${ENGINE_DIR}/computJacob.f90 + ${ENGINE_DIR}/eval8summa.f90 + ${ENGINE_DIR}/summaSolve.f90 + ${ENGINE_DIR}/systemSolv.f90 + ${ENGINE_DIR}/computResidSundials.f90 + ${ENGINE_DIR}/eval8summaSundials.f90 + ${ENGINE_DIR}/computJacobSundials.f90 + ${ENGINE_DIR}/computSnowDepth.f90 + ${ENGINE_DIR}/summaSolveSundialsIDA.f90 + ${ENGINE_DIR}/systemSolvSundials.f90 + ${ENGINE_DIR}/varSubstep.f90 + ${ENGINE_DIR}/opSplittin.f90 + ${ENGINE_DIR}/coupled_em.f90 + ${ENGINE_DIR}/run_oneHRU.f90 + ${ENGINE_DIR}/run_oneGRU.f90) + +# Define routines for SUMMA preliminaries + set(PRELIM + ${ENGINE_DIR}/conv_funcs.f90 + ${ENGINE_DIR}/sunGeomtry.f90 + ${ENGINE_DIR}/convE2Temp.f90 + ${ENGINE_DIR}/allocspace.f90 + ${ENGINE_DIR}/checkStruc.f90 + ${ENGINE_DIR}/childStruc.f90 + ${ENGINE_DIR}/ffile_info.f90 + ${ENGINE_DIR}/read_attrb.f90 + ${ENGINE_DIR}/read_pinit.f90 + ${ENGINE_DIR}/pOverwrite.f90 + ${ENGINE_DIR}/read_param.f90 + ${ENGINE_DIR}/paramCheck.f90 + ${ENGINE_DIR}/check_icond.f90) + + set(NOAHMP + ${NOAHMP_DIR}/module_model_constants.F + ${NOAHMP_DIR}/module_sf_noahutl.F + ${NOAHMP_DIR}/module_sf_noahlsm.F + ${NOAHMP_DIR}/module_sf_noahmplsm.F) + +# Define routines for the SUMMA model runs + set(MODRUN + ${ENGINE_DIR}/indexState.f90 + ${ENGINE_DIR}/getVectorz.f90 + ${ENGINE_DIR}/t2enthalpy.f90 + ${ENGINE_DIR}/updateVars.f90 + ${ENGINE_DIR}/updateVarsSundials.f90 + ${ENGINE_DIR}/var_derive.f90 + ${ENGINE_DIR}/read_force.f90 + ${ENGINE_DIR}/derivforce.f90 + ${ENGINE_DIR}/snowAlbedo.f90 + ${ENGINE_DIR}/canopySnow.f90 + ${ENGINE_DIR}/tempAdjust.f90 + ${ENGINE_DIR}/snwCompact.f90 + ${ENGINE_DIR}/layerMerge.f90 + ${ENGINE_DIR}/layerDivide.f90 + ${ENGINE_DIR}/volicePack.f90 + ${ENGINE_DIR}/qTimeDelay.f90) + +# Define NetCDF routines + set(NETCDF + ${NETCDF_DIR}/netcdf_util.f90 + ${NETCDF_DIR}/def_output.f90 + ${NETCDF_DIR}/modelwrite.f90 + ${NETCDF_DIR}/read_icond.f90) + +# Define the driver routine + set(DRIVER + ${DRIVER_DIR}/summa_type.f90 + ${DRIVER_DIR}/summa_util.f90 + ${DRIVER_DIR}/summa_alarms.f90 + ${DRIVER_DIR}/summa_globalData.f90 + ${DRIVER_DIR}/summa_defineOutput.f90 + ${DRIVER_DIR}/summa_init.f90 + ${DRIVER_DIR}/summa_setup.f90 + ${DRIVER_DIR}/summa_restart.f90 + ${DRIVER_DIR}/summa_forcing.f90 + ${DRIVER_DIR}/summa_modelRun.f90 + ${DRIVER_DIR}/summa_writeOutput.f90 + ${DRIVER_DIR}/summa_driver.f90) + + # run program files, do not use in ngen + # set(SUMMA ${DRIVER_DIR}/summa_run.f90) + # set(BMI ${DRIVER_DIR}/summa_runBMI.f90) + + set(COMM_ALL + ${NRUTIL} + ${NRPROC} + ${HOOKUP} + ${DATAMS} + ${UTILMS}) + + set(SUMMA_ALL + ${NETCDF} + ${PRELIM} + ${MODRUN} + ${SOLVER} + ${DRIVER}) + +#======================================================================== +# PART 4: compilation +#====================================================================== + + add_library(SUMMA_NOAHMP OBJECT ${NOAHMP} ${NRUTIL}) + target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) + + add_library(SUMMA_COMM OBJECT ${COMM_ALL}) + target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) + target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES}) + target_link_libraries(SUMMA_COMM PUBLIC ${LIBRARIES}) + + # Build SUMMA Shared Library, add BMI libraries outside this function + if(WIN32) + add_library(summabmi ${SUMMA_ALL}) + else() + add_library(summabmi SHARED ${SUMMA_ALL}) + endif() + target_compile_options(summabmi PRIVATE ${FLAGS_ALL}) + target_include_directories(summabmi PUBLIC ${INCLUDES} ${INC_SUNDIALS}) + target_link_libraries(summabmi PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_COMM) + +#endfunction() target_link_libraries(summabmi PUBLIC iso_c_bmi) diff --git a/build/cmake_ngen/bmi.cmake b/build/cmake_ngen/bmi.cmake index 906aceae1..2b32006a6 100644 --- a/build/cmake_ngen/bmi.cmake +++ b/build/cmake_ngen/bmi.cmake @@ -1,4 +1,4 @@ -function(compile_with_bmi ROOT_DIR, F_MASTER, DIR_SUNDIALS, INCLUDES, LIBRARIES, INC_SUNDIALS, LIB_SUNDIALS, FLAGS_ALL, FLAGS_NOAH) +function(compile_with_bmi F_MASTER, DIR_SUNDIALS, INCLUDES, LIBRARIES, INC_SUNDIALS, LIB_SUNDIALS, FLAGS_ALL, FLAGS_NOAH) #======================================================================== # PART 1: Define directory paths From ccddd33b736abbe2db7976d7fd2f7ba977651012 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 25 Mar 2023 00:13:35 +0900 Subject: [PATCH 0623/1472] update version --- build/cmake_ngen/CMakeLists.txt | 7 ++ build/cmake_ngen/bmi.cmake | 202 -------------------------------- 2 files changed, 7 insertions(+), 202 deletions(-) delete mode 100644 build/cmake_ngen/bmi.cmake diff --git a/build/cmake_ngen/CMakeLists.txt b/build/cmake_ngen/CMakeLists.txt index e5c5e96d9..7bcc2c0ec 100644 --- a/build/cmake_ngen/CMakeLists.txt +++ b/build/cmake_ngen/CMakeLists.txt @@ -255,6 +255,13 @@ message("\nBuilding SUMMA\n") # set(SUMMA ${DRIVER_DIR}/summa_run.f90) # set(BMI ${DRIVER_DIR}/summa_runBMI.f90) +# Define version number + set(VERSIONFILE ${DRIVER_DIR}/summaversion.inc) + set(VERSION ${shell git tag | tail -n 1} + set(BULTTIM ${shell date} + set(GITBRCH ${shell git describe --long --all --always | sed -e's/heads\///'} + set(GITHASH ${shell git rev-parse HEAD} + set(COMM_ALL ${NRUTIL} ${NRPROC} diff --git a/build/cmake_ngen/bmi.cmake b/build/cmake_ngen/bmi.cmake deleted file mode 100644 index 2b32006a6..000000000 --- a/build/cmake_ngen/bmi.cmake +++ /dev/null @@ -1,202 +0,0 @@ -function(compile_with_bmi F_MASTER, DIR_SUNDIALS, INCLUDES, LIBRARIES, INC_SUNDIALS, LIB_SUNDIALS, FLAGS_ALL, FLAGS_NOAH) - -#======================================================================== -# PART 1: Define directory paths -#======================================================================== - -# Core directory that contains source code - set(F_KORE_DIR ${F_MASTER}/build/source) - -# Define directories - set(DRIVER_DIR ${F_KORE_DIR}/driver) - set(HOOKUP_DIR ${F_KORE_DIR}/hookup) - set(NETCDF_DIR ${F_KORE_DIR}/netcdf) - set(DSHARE_DIR ${F_KORE_DIR}/dshare) - set(NUMREC_DIR ${F_KORE_DIR}/numrec) - set(NOAHMP_DIR ${F_KORE_DIR}/noah-mp) - set(ENGINE_DIR ${F_KORE_DIR}/engine) - -#======================================================================== -# PART 2: Assemble all of the SUMMA sub-routines -#======================================================================== - -# utilities - set(NRUTIL - ${ENGINE_DIR}/nrtype.f90 - ${ENGINE_DIR}/f2008funcs.f90 - ${ENGINE_DIR}/nr_utility.f90) - -# Numerical recipes procedures -# NOTE: all numerical recipes procedures are now replaced with free versions - set(NRPROC - ${ENGINE_DIR}/expIntegral.f90 - ${ENGINE_DIR}/spline_int.f90) - -# Hook-up modules - set(HOOKUP - ${HOOKUP_DIR}/ascii_util.f90 - ${HOOKUP_DIR}/summaFileManager.f90) - -# Data modules - set(DATAMS - ${DSHARE_DIR}/multiconst.f90 - ${DSHARE_DIR}/var_lookup.f90 - ${DSHARE_DIR}/data_types.f90 - ${DSHARE_DIR}/globalData.f90 - ${DSHARE_DIR}/flxMapping.f90 - ${DSHARE_DIR}/get_ixname.f90 - ${DSHARE_DIR}/popMetadat.f90 - ${DSHARE_DIR}/outpt_stat.f90) - -# utility modules - set(UTILMS - ${ENGINE_DIR}/time_utils.f90 - ${ENGINE_DIR}/mDecisions.f90 - ${ENGINE_DIR}/snow_utils.f90 - ${ENGINE_DIR}/soil_utils.f90 - ${ENGINE_DIR}/soil_utilsAddSundials.f90 - ${ENGINE_DIR}/updatState.f90 - ${ENGINE_DIR}/updatStateSundials.f90 - ${ENGINE_DIR}/matrixOper.f90) - -# Solver - set(SOLVER - ${ENGINE_DIR}/vegPhenlgy.f90 - ${ENGINE_DIR}/diagn_evar.f90 - ${ENGINE_DIR}/stomResist.f90 - ${ENGINE_DIR}/groundwatr.f90 - ${ENGINE_DIR}/vegSWavRad.f90 - ${ENGINE_DIR}/vegNrgFlux.f90 - ${ENGINE_DIR}/ssdNrgFlux.f90 - ${ENGINE_DIR}/vegLiqFlux.f90 - ${ENGINE_DIR}/snowLiqFlx.f90 - ${ENGINE_DIR}/soilLiqFlx.f90 - ${ENGINE_DIR}/bigAquifer.f90 - ${ENGINE_DIR}/computFlux.f90 - ${ENGINE_DIR}/type4IDA.f90 - ${ENGINE_DIR}/tol4IDA.f90 - ${ENGINE_DIR}/computEnthalpy.f90 - ${ENGINE_DIR}/computHeatCap.f90 - ${ENGINE_DIR}/computThermConduct.f90 - ${ENGINE_DIR}/computResid.f90 - ${ENGINE_DIR}/computJacob.f90 - ${ENGINE_DIR}/eval8summa.f90 - ${ENGINE_DIR}/summaSolve.f90 - ${ENGINE_DIR}/systemSolv.f90 - ${ENGINE_DIR}/computResidSundials.f90 - ${ENGINE_DIR}/eval8summaSundials.f90 - ${ENGINE_DIR}/computJacobSundials.f90 - ${ENGINE_DIR}/computSnowDepth.f90 - ${ENGINE_DIR}/summaSolveSundialsIDA.f90 - ${ENGINE_DIR}/systemSolvSundials.f90 - ${ENGINE_DIR}/varSubstep.f90 - ${ENGINE_DIR}/opSplittin.f90 - ${ENGINE_DIR}/coupled_em.f90 - ${ENGINE_DIR}/run_oneHRU.f90 - ${ENGINE_DIR}/run_oneGRU.f90) - -# Define routines for SUMMA preliminaries - set(PRELIM - ${ENGINE_DIR}/conv_funcs.f90 - ${ENGINE_DIR}/sunGeomtry.f90 - ${ENGINE_DIR}/convE2Temp.f90 - ${ENGINE_DIR}/allocspace.f90 - ${ENGINE_DIR}/checkStruc.f90 - ${ENGINE_DIR}/childStruc.f90 - ${ENGINE_DIR}/ffile_info.f90 - ${ENGINE_DIR}/read_attrb.f90 - ${ENGINE_DIR}/read_pinit.f90 - ${ENGINE_DIR}/pOverwrite.f90 - ${ENGINE_DIR}/read_param.f90 - ${ENGINE_DIR}/paramCheck.f90 - ${ENGINE_DIR}/check_icond.f90) - - set(NOAHMP - ${NOAHMP_DIR}/module_model_constants.F - ${NOAHMP_DIR}/module_sf_noahutl.F - ${NOAHMP_DIR}/module_sf_noahlsm.F - ${NOAHMP_DIR}/module_sf_noahmplsm.F) - -# Define routines for the SUMMA model runs - set(MODRUN - ${ENGINE_DIR}/indexState.f90 - ${ENGINE_DIR}/getVectorz.f90 - ${ENGINE_DIR}/t2enthalpy.f90 - ${ENGINE_DIR}/updateVars.f90 - ${ENGINE_DIR}/updateVarsSundials.f90 - ${ENGINE_DIR}/var_derive.f90 - ${ENGINE_DIR}/read_force.f90 - ${ENGINE_DIR}/derivforce.f90 - ${ENGINE_DIR}/snowAlbedo.f90 - ${ENGINE_DIR}/canopySnow.f90 - ${ENGINE_DIR}/tempAdjust.f90 - ${ENGINE_DIR}/snwCompact.f90 - ${ENGINE_DIR}/layerMerge.f90 - ${ENGINE_DIR}/layerDivide.f90 - ${ENGINE_DIR}/volicePack.f90 - ${ENGINE_DIR}/qTimeDelay.f90) - -# Define NetCDF routines - set(NETCDF - ${NETCDF_DIR}/netcdf_util.f90 - ${NETCDF_DIR}/def_output.f90 - ${NETCDF_DIR}/modelwrite.f90 - ${NETCDF_DIR}/read_icond.f90) - -# Define the driver routine - set(DRIVER - ${DRIVER_DIR}/summa_type.f90 - ${DRIVER_DIR}/summa_util.f90 - ${DRIVER_DIR}/summa_alarms.f90 - ${DRIVER_DIR}/summa_globalData.f90 - ${DRIVER_DIR}/summa_defineOutput.f90 - ${DRIVER_DIR}/summa_init.f90 - ${DRIVER_DIR}/summa_setup.f90 - ${DRIVER_DIR}/summa_restart.f90 - ${DRIVER_DIR}/summa_forcing.f90 - ${DRIVER_DIR}/summa_modelRun.f90 - ${DRIVER_DIR}/summa_writeOutput.f90 - ${DRIVER_DIR}/summa_driver.f90) - - # run program files, do not use in ngen - # set(SUMMA ${DRIVER_DIR}/summa_run.f90) - # set(BMI ${DRIVER_DIR}/summa_runBMI.f90) - - set(COMM_ALL - ${NRUTIL} - ${NRPROC} - ${HOOKUP} - ${DATAMS} - ${UTILMS}) - - set(SUMMA_ALL - ${NETCDF} - ${PRELIM} - ${MODRUN} - ${SOLVER} - ${DRIVER}) - -#======================================================================== -# PART 4: compilation -#====================================================================== - - add_library(SUMMA_NOAHMP OBJECT ${NOAHMP} ${NRUTIL}) - target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) - - add_library(SUMMA_COMM OBJECT ${COMM_ALL}) - target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) - target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES}) - target_link_libraries(SUMMA_COMM PUBLIC ${LIBRARIES}) - - # Build SUMMA Shared Library, add BMI libraries outside this function - if(WIN32) - add_library(summabmi ${SUMMA_ALL}) - else() - add_library(summabmi SHARED ${SUMMA_ALL}) - endif() - target_compile_options(summabmi PRIVATE ${FLAGS_ALL}) - target_include_directories(summabmi PUBLIC ${INCLUDES} ${INC_SUNDIALS}) - target_link_libraries(summabmi PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_COMM) - - -endfunction() \ No newline at end of file From 34dcef05b1fe8a94e2c3f16222d6337dbfab17d4 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 25 Mar 2023 00:40:59 +0900 Subject: [PATCH 0624/1472] set and write version --- build/cmake_ngen/CMakeLists.txt | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/build/cmake_ngen/CMakeLists.txt b/build/cmake_ngen/CMakeLists.txt index 7bcc2c0ec..e843c0c22 100644 --- a/build/cmake_ngen/CMakeLists.txt +++ b/build/cmake_ngen/CMakeLists.txt @@ -24,7 +24,7 @@ set(F_MASTER ${ROOT_DIR}/summa) SET (CMAKE_Fortran_COMPILER gfortran) -include(bmi.cmake) +#include(bmi.cmake) # -------------------------------------------------------------------------------------------- # When compiling Summa-Sundials it can be compiled in a Cluster or Debug mode, which is set by # the CMAKE_BUILD_TYPES variables. The default is Release, cluster mode. @@ -257,10 +257,10 @@ message("\nBuilding SUMMA\n") # Define version number set(VERSIONFILE ${DRIVER_DIR}/summaversion.inc) - set(VERSION ${shell git tag | tail -n 1} - set(BULTTIM ${shell date} - set(GITBRCH ${shell git describe --long --all --always | sed -e's/heads\///'} - set(GITHASH ${shell git rev-parse HEAD} + execute_process(COMMAND bash -c "git tag | tail -n 1" OUTPUT_VARIABLE VERSION) + execute_process(COMMAND bash -c "date" OUTPUT_VARIABLE BULTTIM) + execute_process(COMMAND bash -c "git describe --long --all --always | sed -e's/heads\///'" OUTPUT_VARIABLE GITBRCH) + execute_process(COMMAND bash -c "git rev-parse HEAD" OUTPUT_VARIABLE GITHASH) set(COMM_ALL ${NRUTIL} @@ -280,6 +280,13 @@ message("\nBuilding SUMMA\n") # PART 4: compilation #====================================================================== +# update version information + file({WRITE} ${VERSIONFILE} "character(len=64), parameter :: summaVersion = '${VERSION}'") + file({APPEND} ${VERSIONFILE} "character(len=64), parameter :: buildTime = '${BULTTIM}'") + file({APPEND} ${VERSIONFILE} "character(len=64), parameter :: gitBranch = '${GITBRCH}'") + file({APPEND} ${VERSIONFILE} "character(len=64), parameter :: gitHash = '${GITHASH}'") + + add_library(SUMMA_NOAHMP OBJECT ${NOAHMP} ${NRUTIL}) target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) From a35e2996b462330e5066e65dc6a57fc292025ff5 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 25 Mar 2023 00:53:52 +0900 Subject: [PATCH 0625/1472] fix dates --- build/cmake_ngen/CMakeLists.txt | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/build/cmake_ngen/CMakeLists.txt b/build/cmake_ngen/CMakeLists.txt index e843c0c22..030c8447d 100644 --- a/build/cmake_ngen/CMakeLists.txt +++ b/build/cmake_ngen/CMakeLists.txt @@ -257,10 +257,10 @@ message("\nBuilding SUMMA\n") # Define version number set(VERSIONFILE ${DRIVER_DIR}/summaversion.inc) - execute_process(COMMAND bash -c "git tag | tail -n 1" OUTPUT_VARIABLE VERSION) - execute_process(COMMAND bash -c "date" OUTPUT_VARIABLE BULTTIM) - execute_process(COMMAND bash -c "git describe --long --all --always | sed -e's/heads\///'" OUTPUT_VARIABLE GITBRCH) - execute_process(COMMAND bash -c "git rev-parse HEAD" OUTPUT_VARIABLE GITHASH) + execute_process(COMMAND "git tag | tail -n 1" OUTPUT_VARIABLE VERSION) + execute_process(COMMAND "date" OUTPUT_VARIABLE BULTTIM) + execute_process(COMMAND "git describe --long --all --always | sed -e's/heads\///'" OUTPUT_VARIABLE GITBRCH) + execute_process(COMMAND "git rev-parse HEAD" OUTPUT_VARIABLE GITHASH) set(COMM_ALL ${NRUTIL} @@ -281,10 +281,11 @@ message("\nBuilding SUMMA\n") #====================================================================== # update version information - file({WRITE} ${VERSIONFILE} "character(len=64), parameter :: summaVersion = '${VERSION}'") - file({APPEND} ${VERSIONFILE} "character(len=64), parameter :: buildTime = '${BULTTIM}'") - file({APPEND} ${VERSIONFILE} "character(len=64), parameter :: gitBranch = '${GITBRCH}'") - file({APPEND} ${VERSIONFILE} "character(len=64), parameter :: gitHash = '${GITHASH}'") + file(WRITE ${VERSIONFILE} +"character(len=64), parameter :: summaVersion = '${VERSION}'\n) +character(len=64), parameter :: buildTime = '${BULTTIM}'\n) +character(len=64), parameter :: gitBranch = '${GITBRCH}'\n) +character(len=64), parameter :: gitHash = '${GITHASH}'") add_library(SUMMA_NOAHMP OBJECT ${NOAHMP} ${NRUTIL}) From 855b871bba899dd993d6834108c5bf63d3edd990 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 25 Mar 2023 01:01:41 +0900 Subject: [PATCH 0626/1472] fix git hash --- build/cmake_ngen/CMakeLists.txt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/build/cmake_ngen/CMakeLists.txt b/build/cmake_ngen/CMakeLists.txt index 030c8447d..07c4459d9 100644 --- a/build/cmake_ngen/CMakeLists.txt +++ b/build/cmake_ngen/CMakeLists.txt @@ -257,10 +257,10 @@ message("\nBuilding SUMMA\n") # Define version number set(VERSIONFILE ${DRIVER_DIR}/summaversion.inc) - execute_process(COMMAND "git tag | tail -n 1" OUTPUT_VARIABLE VERSION) + execute_process(COMMAND "${GIT_EXECUTABLE} tag | tail -n 1" OUTPUT_VARIABLE VERSION) execute_process(COMMAND "date" OUTPUT_VARIABLE BULTTIM) - execute_process(COMMAND "git describe --long --all --always | sed -e's/heads\///'" OUTPUT_VARIABLE GITBRCH) - execute_process(COMMAND "git rev-parse HEAD" OUTPUT_VARIABLE GITHASH) + execute_process(COMMAND "${GIT_EXECUTABLE} describe --long --all --always | sed -e's/heads\///'" OUTPUT_VARIABLE GITBRCH) + execute_process(COMMAND "${GIT_EXECUTABLE} rev-parse HEAD" OUTPUT_VARIABLE GITHASH) set(COMM_ALL ${NRUTIL} @@ -282,12 +282,11 @@ message("\nBuilding SUMMA\n") # update version information file(WRITE ${VERSIONFILE} -"character(len=64), parameter :: summaVersion = '${VERSION}'\n) -character(len=64), parameter :: buildTime = '${BULTTIM}'\n) -character(len=64), parameter :: gitBranch = '${GITBRCH}'\n) +"character(len=64), parameter :: summaVersion = '${VERSION}'\n +character(len=64), parameter :: buildTime = '${BULTTIM}'\n +character(len=64), parameter :: gitBranch = '${GITBRCH}'\n character(len=64), parameter :: gitHash = '${GITHASH}'") - add_library(SUMMA_NOAHMP OBJECT ${NOAHMP} ${NRUTIL}) target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) From cd845a0a718bb038ca8de0e445a9cdb9e2532ad6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 25 Mar 2023 01:25:31 +0900 Subject: [PATCH 0627/1472] git hash --- build/cmake_ngen/CMakeLists.txt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/build/cmake_ngen/CMakeLists.txt b/build/cmake_ngen/CMakeLists.txt index 07c4459d9..ba63d744c 100644 --- a/build/cmake_ngen/CMakeLists.txt +++ b/build/cmake_ngen/CMakeLists.txt @@ -255,7 +255,7 @@ message("\nBuilding SUMMA\n") # set(SUMMA ${DRIVER_DIR}/summa_run.f90) # set(BMI ${DRIVER_DIR}/summa_runBMI.f90) -# Define version number +# Define version number, not working correctly set(VERSIONFILE ${DRIVER_DIR}/summaversion.inc) execute_process(COMMAND "${GIT_EXECUTABLE} tag | tail -n 1" OUTPUT_VARIABLE VERSION) execute_process(COMMAND "date" OUTPUT_VARIABLE BULTTIM) @@ -280,12 +280,11 @@ message("\nBuilding SUMMA\n") # PART 4: compilation #====================================================================== -# update version information - file(WRITE ${VERSIONFILE} -"character(len=64), parameter :: summaVersion = '${VERSION}'\n -character(len=64), parameter :: buildTime = '${BULTTIM}'\n -character(len=64), parameter :: gitBranch = '${GITBRCH}'\n -character(len=64), parameter :: gitHash = '${GITHASH}'") +# update version information, not working correctly + file(WRITE ${VERSIONFILE} "character(len=64), parameter :: summaVersion = '${VERSION}'\n") + file(APPEND ${VERSIONFILE} "character(len=64), parameter :: buildTime = ''\n") + file(APPEND ${VERSIONFILE} "character(len=64), parameter :: gitBranch = '${GITBRCH}'\n") + file(APPEND ${VERSIONFILE} "character(len=64), parameter :: gitHash = '${GITHASH}'") add_library(SUMMA_NOAHMP OBJECT ${NOAHMP} ${NRUTIL}) target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) From dde9890e4e3d7a071a9825ee07415cfc4b578b2d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 25 Mar 2023 01:28:06 +0900 Subject: [PATCH 0628/1472] directory name screwed up --- build/cmake_ngen/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build/cmake_ngen/CMakeLists.txt b/build/cmake_ngen/CMakeLists.txt index ba63d744c..4d9cd9069 100644 --- a/build/cmake_ngen/CMakeLists.txt +++ b/build/cmake_ngen/CMakeLists.txt @@ -57,8 +57,7 @@ endif() find_package(netCDF REQUIRED) find_package(LAPACK REQUIRED) -set(DIR_SUNDIALS= ${ROOT_DIR}/sundials/instdir) -set(INC_SUNDIALS=-I${DIR_SUNDIALS}/include -I${DIR_SUNDIALS}/fortran) +set(DIR_SUNDIALS ${ROOT_DIR}/sundials/instdir) # Set include directories if(CMAKE_BUILD_TYPE MATCHES Cluster OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) From 4a78d9f911517df12ed83c7dc0995db0cfe8d781 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 25 Mar 2023 01:40:56 +0900 Subject: [PATCH 0629/1472] compiles now --- build/cmake_ngen/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/build/cmake_ngen/CMakeLists.txt b/build/cmake_ngen/CMakeLists.txt index 4d9cd9069..80684e0a0 100644 --- a/build/cmake_ngen/CMakeLists.txt +++ b/build/cmake_ngen/CMakeLists.txt @@ -262,7 +262,6 @@ message("\nBuilding SUMMA\n") execute_process(COMMAND "${GIT_EXECUTABLE} rev-parse HEAD" OUTPUT_VARIABLE GITHASH) set(COMM_ALL - ${NRUTIL} ${NRPROC} ${HOOKUP} ${DATAMS} From 6fa59a56d96fec30e0c47a19f1f9f6d6ca45d163 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 27 Mar 2023 00:14:52 +0900 Subject: [PATCH 0630/1472] things missing in inputs --- build/cmake_ngen/build_ngen.cluster.bash | 12 +++--------- build/cmake_ngen/build_ngen.mac.bash | 12 +++--------- build/source/driver/summa_driver.f90 | 4 ++-- .../example_realization_config_w_summa_bmi.json | 10 ++++++---- ...xample_realization_config_w_summa_bmi__mac.json | 14 +++++++++----- test_ngen/summa-init-cat-27.namelist.input | 3 +++ 6 files changed, 26 insertions(+), 29 deletions(-) create mode 100644 test_ngen/summa-init-cat-27.namelist.input diff --git a/build/cmake_ngen/build_ngen.cluster.bash b/build/cmake_ngen/build_ngen.cluster.bash index 0c96d5bcd..77e02250d 100644 --- a/build/cmake_ngen/build_ngen.cluster.bash +++ b/build/cmake_ngen/build_ngen.cluster.bash @@ -37,14 +37,8 @@ cmake --build extern/summa/cmake_build --target all #cmake -B extern/evapotranspiration/cmake_build -S extern/evapotranspiration #cmake --build extern/evapotranspiration/cmake_build --target all -cmake -B cmake_build -S . -DMPI_ACTIVE:BOOL=ON -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ -#cmake -B cmake_build -S . -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ -# -DUDUNITS2_LIBRARY=/glade/u/apps/ch/opt/udunits/2.2.28/gnu/10.1.0/lib/libudunits2.so \ -# -DUDUNITS2_INCLUDE=/glade/u/apps/ch/opt/udunits/2.2.28/gnu/10.1.0/include # \ -# -DCMAKE_BUILD_TYPE=Debug - - # can also add -DCMAKE_BUILD_TYPE=Debug to be able to run in gdb - -#make -j 8 -C cmake_build # build w/ 8 parallel jobs +cmake -B /ngen/cmake_build -S . -DMPI_ACTIVE:BOOL=OFF -DMPI_ACTIVE:BOOL=ON -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON +# can also add -DCMAKE_BUILD_TYPE=Debug to be able to run in gdb +# make -j 8 -C cmake_build # build w/ 8 parallel jobs, also turn MPI on make -C cmake_build diff --git a/build/cmake_ngen/build_ngen.mac.bash b/build/cmake_ngen/build_ngen.mac.bash index 379a61c19..c0d2b0fa6 100644 --- a/build/cmake_ngen/build_ngen.mac.bash +++ b/build/cmake_ngen/build_ngen.mac.bash @@ -8,14 +8,8 @@ cmake --build extern/iso_c_fortran_bmi/cmake_build --target all cmake -B extern/summa/cmake_build -S extern/summa cmake --build extern/summa/cmake_build --target all -cmake -B /ngen/cmake_build -S . -DMPI_ACTIVE:BOOL=ON -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ -#cmake -B /ngen/cmake_build -S . -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON \ -# -DUDUNITS2_LIBRARY=/glade/u/apps/ch/opt/udunits/2.2.28/gnu/10.1.0/lib/libudunits2.so \ -# -DUDUNITS2_INCLUDE=/glade/u/apps/ch/opt/udunits/2.2.28/gnu/10.1.0/include # \ -# -DCMAKE_BUILD_TYPE=Debug - - # can also add -DCMAKE_BUILD_TYPE=Debug to be able to run in gdb - -#make -j 8 -C cmake_build # build w/ 8 parallel jobs +cmake -B /ngen/cmake_build -S . -DMPI_ACTIVE:BOOL=OFF -DMPI_ACTIVE:BOOL=ON -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON +# can also add -DCMAKE_BUILD_TYPE=Debug to be able to run in gdb +# make -j 8 -C cmake_build # build w/ 8 parallel jobs, also turn MPI on make -C cmake_build diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index ac926a5df..07f25b2c3 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -305,13 +305,13 @@ function summa_output_item_count(this, count) result (bmi_status) bmi_status = BMI_SUCCESS end function summa_output_item_count - ! List input variables (none) + ! List input variables function summa_input_var_names(this, names) result (bmi_status) class (summa_bmi), intent(in) :: this character (*), pointer, intent(out) :: names(:) integer :: bmi_status - input_items(1) = '' + input_items(1) = 'file_manager' names => input_items bmi_status = BMI_SUCCESS end function summa_input_var_names diff --git a/test_ngen/example_realization_config_w_summa_bmi.json b/test_ngen/example_realization_config_w_summa_bmi.json index 2e8172121..d7d471fc5 100644 --- a/test_ngen/example_realization_config_w_summa_bmi.json +++ b/test_ngen/example_realization_config_w_summa_bmi.json @@ -9,15 +9,17 @@ "forcing_file": "", "init_config": "./extern/summa/data_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt", "allow_exceed_end_time": true, + "main_output_variable": "land_surface_water__runoff_volume_flux", "uses_forcing_file": false } } ], "forcing": { - "path": "./data/forcing/cats-27_52_67-2015_12_01-2015_12_30.nc", - "provider": "NetCDF" - } - }, + "file_pattern": ".*{{id}}.*..csv", + "path": "./data/forcing/", + "provider": "CsvPerFeature" + } + }, "time": { "start_time": "2015-12-01 00:00:00", "end_time": "2015-12-30 23:00:00", diff --git a/test_ngen/example_realization_config_w_summa_bmi__mac.json b/test_ngen/example_realization_config_w_summa_bmi__mac.json index 14740b0b1..7908cd527 100644 --- a/test_ngen/example_realization_config_w_summa_bmi__mac.json +++ b/test_ngen/example_realization_config_w_summa_bmi__mac.json @@ -7,17 +7,21 @@ "model_type_name": "bmi_fortran_summa", "library_file": "./extern/summa/cmake_build/libsummabmi.dylib", "forcing_file": "", - "init_config": "./extern/summa/data_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt", + "init_config": "./data/bmi/fortran/summa-init-{{id}}.namelist.input", + + ./extern/summa/data_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt", "allow_exceed_end_time": true, + "main_output_variable": "land_surface_water__runoff_volume_flux", "uses_forcing_file": false } } ], "forcing": { - "path": "./data/forcing/cats-27_52_67-2015_12_01-2015_12_30.nc", - "provider": "NetCDF" - } - }, + "file_pattern": ".*{{id}}.*..csv", + "path": "./data/forcing/", + "provider": "CsvPerFeature" + } + }, "time": { "start_time": "2015-12-01 00:00:00", "end_time": "2015-12-30 23:00:00", diff --git a/test_ngen/summa-init-cat-27.namelist.input b/test_ngen/summa-init-cat-27.namelist.input new file mode 100644 index 000000000..681ddadef --- /dev/null +++ b/test_ngen/summa-init-cat-27.namelist.input @@ -0,0 +1,3 @@ +¶meters ! all inclusive stub for now + file_manager = "extern/summa/data_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt" +/ From ec7d3b506ca9af3fa492ac2876bc94a59608de6d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 27 Mar 2023 00:25:15 +0900 Subject: [PATCH 0631/1472] change input --- test_ngen/example_realization_config_w_summa_bmi.json | 3 +-- test_ngen/example_realization_config_w_summa_bmi__mac.json | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/test_ngen/example_realization_config_w_summa_bmi.json b/test_ngen/example_realization_config_w_summa_bmi.json index d7d471fc5..fd43b0ed6 100644 --- a/test_ngen/example_realization_config_w_summa_bmi.json +++ b/test_ngen/example_realization_config_w_summa_bmi.json @@ -7,8 +7,7 @@ "model_type_name": "bmi_fortran_summa", "library_file": "./extern/summa/cmake_build/libsummabmi.so", "forcing_file": "", - "init_config": "./extern/summa/data_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt", - "allow_exceed_end_time": true, + "init_config": "./data/bmi/fortran/summa-init-{{id}}.namelist.input", "allow_exceed_end_time": true, "main_output_variable": "land_surface_water__runoff_volume_flux", "uses_forcing_file": false } diff --git a/test_ngen/example_realization_config_w_summa_bmi__mac.json b/test_ngen/example_realization_config_w_summa_bmi__mac.json index 7908cd527..9bb8b90fe 100644 --- a/test_ngen/example_realization_config_w_summa_bmi__mac.json +++ b/test_ngen/example_realization_config_w_summa_bmi__mac.json @@ -8,8 +8,6 @@ "library_file": "./extern/summa/cmake_build/libsummabmi.dylib", "forcing_file": "", "init_config": "./data/bmi/fortran/summa-init-{{id}}.namelist.input", - - ./extern/summa/data_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt", "allow_exceed_end_time": true, "main_output_variable": "land_surface_water__runoff_volume_flux", "uses_forcing_file": false From 221a61a5e6a5c01f370a7ef3a142025f5fecab21 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 27 Mar 2023 00:43:40 +0900 Subject: [PATCH 0632/1472] add register_bmi for ngen --- build/source/driver/summa_driver.f90 | 29 ++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 07f25b2c3..beb56ca03 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -993,6 +993,35 @@ function summa_set_at_indices_double(this, name, inds, src) result (bmi_status) end select end function summa_set_at_indices_double +#ifdef NGEN_ACTIVE + function register_bmi(this) result(bmi_status) bind(C, name="register_bmi") + use, intrinsic:: iso_c_binding, only: c_ptr, c_loc, c_int + use iso_c_bmif_2_0 + implicit none + type(c_ptr) :: this ! If not value, then from the C perspective `this` is a void** + integer(kind=c_int) :: bmi_status + !Create the model instance to use + type(summa_bmi), pointer :: bmi_model + !Create a simple pointer wrapper + type(box), pointer :: bmi_box + + !allocate model + allocate(summa_bmi::bmi_model) + !allocate the pointer box + allocate(bmi_box) + + !associate the wrapper pointer the created model instance + bmi_box%ptr => bmi_model + + if( .not. associated( bmi_box ) .or. .not. associated( bmi_box%ptr ) ) then + bmi_status = BMI_FAILURE + else + !Return the pointer to box + this = c_loc(bmi_box) + bmi_status = BMI_SUCCESS + end function register_bmi +#endif + ! non-BMI helper function to get fields, only get first do_nHRU of them subroutine get_basin_field(this, name, do_nHRU, targetarr) implicit none From 8a313d4b3680469d5d20d9dd8ef46b4510cb1dc1 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 27 Mar 2023 21:17:24 +0900 Subject: [PATCH 0633/1472] missing if statement --- build/source/driver/summa_driver.f90 | 51 ++++++++++++++-------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index beb56ca03..50e409507 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -995,31 +995,32 @@ end function summa_set_at_indices_double #ifdef NGEN_ACTIVE function register_bmi(this) result(bmi_status) bind(C, name="register_bmi") - use, intrinsic:: iso_c_binding, only: c_ptr, c_loc, c_int - use iso_c_bmif_2_0 - implicit none - type(c_ptr) :: this ! If not value, then from the C perspective `this` is a void** - integer(kind=c_int) :: bmi_status - !Create the model instance to use - type(summa_bmi), pointer :: bmi_model - !Create a simple pointer wrapper - type(box), pointer :: bmi_box - - !allocate model - allocate(summa_bmi::bmi_model) - !allocate the pointer box - allocate(bmi_box) - - !associate the wrapper pointer the created model instance - bmi_box%ptr => bmi_model - - if( .not. associated( bmi_box ) .or. .not. associated( bmi_box%ptr ) ) then - bmi_status = BMI_FAILURE - else - !Return the pointer to box - this = c_loc(bmi_box) - bmi_status = BMI_SUCCESS - end function register_bmi + use, intrinsic:: iso_c_binding, only: c_ptr, c_loc, c_int + use iso_c_bmif_2_0 + implicit none + type(c_ptr) :: this ! If not value, then from the C perspective `this` is a void** + integer(kind=c_int) :: bmi_status + !Create the model instance to use + type(summa_bmi), pointer :: bmi_model + !Create a simple pointer wrapper + type(box), pointer :: bmi_box + + !allocate model + allocate(summa_bmi::bmi_model) + !allocate the pointer box + allocate(bmi_box) + + !associate the wrapper pointer the created model instance + bmi_box%ptr => bmi_model + + if( .not. associated( bmi_box ) .or. .not. associated( bmi_box%ptr ) ) then + bmi_status = BMI_FAILURE + else + !Return the pointer to box + this = c_loc(bmi_box) + bmi_status = BMI_SUCCESS + endif + end function register_bmi #endif ! non-BMI helper function to get fields, only get first do_nHRU of them From f1a5fcab2b4c1d44751783162f2200a477f69a6a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 27 Mar 2023 22:24:32 +0900 Subject: [PATCH 0634/1472] make file_manager send in as a namelist input like ngen wants --- build/source/driver/summa_driver.f90 | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 50e409507..e8a46d535 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -173,7 +173,8 @@ function summa_bmi_initialize(this, config_file) result (bmi_status) ! error control integer(i4b) :: err=0 ! error code character(len=1024) :: message='' ! error message - integer :: bmi_status, i + integer :: bmi_status, i,fu,rc + namelist :: /parameters/ file_manager ! initialize time steps this%model%timeStep = 0 @@ -184,12 +185,23 @@ function summa_bmi_initialize(this, config_file) result (bmi_status) ! if using the BMI interface, there is an argument pointing to the file manager file ! then make sure summaFileManagerFile is set before executing initialization - ! Note, if this is more than 80 characters the pre-built BMI libraries will fail if (len(config_file) > 0)then +#ifdef NGEN_ACTIVE + ! with NGEN the argument gives the file manager file as an input parameter in a namelist + open (action='read', file=config_file, iostat=rc, newunit=fu) + read (nml=parameters, iostat=rc, unit=fu) + if (rc /= 0) write (stderr, '("Error: invalid Namelist format")') + this%model%summa1_struc(n)%summaFileManagerFile=trim(file_manager) + print "(A)", "file_master is '"//trim(file_manager)//"'." +#else + ! without NGEN the argument gives the file manager file directly + ! Note, if this is more than 80 characters the pre-built BMI libraries will fail this%model%summa1_struc(n)%summaFileManagerFile=trim(config_file) print "(A)", "file_master is '"//trim(config_file)//"'." +#endif endif + ! declare and allocate summa data structures and initialize model state to known values call summa_initialize(this%model%summa1_struc(n), err, message) call handle_err(err, message) @@ -305,13 +317,13 @@ function summa_output_item_count(this, count) result (bmi_status) bmi_status = BMI_SUCCESS end function summa_output_item_count - ! List input variables + ! List input variables (none) function summa_input_var_names(this, names) result (bmi_status) class (summa_bmi), intent(in) :: this character (*), pointer, intent(out) :: names(:) integer :: bmi_status - input_items(1) = 'file_manager' + input_items(1) = '' names => input_items bmi_status = BMI_SUCCESS end function summa_input_var_names From 0d3cc5ff3f27becf4fdf3445e880b08affda9cfc Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 27 Mar 2023 22:34:48 +0900 Subject: [PATCH 0635/1472] namelist def --- build/source/driver/summa_driver.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index e8a46d535..df4b57899 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -174,7 +174,8 @@ function summa_bmi_initialize(this, config_file) result (bmi_status) integer(i4b) :: err=0 ! error code character(len=1024) :: message='' ! error message integer :: bmi_status, i,fu,rc - namelist :: /parameters/ file_manager + ! namelist definition + namelist /parameters/ file_manager ! initialize time steps this%model%timeStep = 0 @@ -201,7 +202,6 @@ function summa_bmi_initialize(this, config_file) result (bmi_status) #endif endif - ! declare and allocate summa data structures and initialize model state to known values call summa_initialize(this%model%summa1_struc(n), err, message) call handle_err(err, message) From 525961053a57e37f6e41e71e26dea2b0408ebb8a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 27 Mar 2023 22:37:19 +0900 Subject: [PATCH 0636/1472] namelist def --- build/source/driver/summa_driver.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index df4b57899..8e65c16fb 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -173,6 +173,7 @@ function summa_bmi_initialize(this, config_file) result (bmi_status) ! error control integer(i4b) :: err=0 ! error code character(len=1024) :: message='' ! error message + character(len=1024) :: file_manager integer :: bmi_status, i,fu,rc ! namelist definition namelist /parameters/ file_manager From 6a205691ab6fbe9b3262985b34c8cc5928f47c28 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 27 Mar 2023 22:46:12 +0900 Subject: [PATCH 0637/1472] namelist def --- build/source/driver/summa_driver.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 8e65c16fb..f5fa77b3b 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -192,7 +192,6 @@ function summa_bmi_initialize(this, config_file) result (bmi_status) ! with NGEN the argument gives the file manager file as an input parameter in a namelist open (action='read', file=config_file, iostat=rc, newunit=fu) read (nml=parameters, iostat=rc, unit=fu) - if (rc /= 0) write (stderr, '("Error: invalid Namelist format")') this%model%summa1_struc(n)%summaFileManagerFile=trim(file_manager) print "(A)", "file_master is '"//trim(file_manager)//"'." #else From 93c4462ef00b43d4b8c96ffbe3a2ce9b6d32d111 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 27 Mar 2023 23:08:34 +0900 Subject: [PATCH 0638/1472] no command arguments with ngen --- build/source/driver/summa_init.f90 | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/build/source/driver/summa_init.f90 b/build/source/driver/summa_init.f90 index 5777e18e2..e51fd6f4b 100644 --- a/build/source/driver/summa_init.f90 +++ b/build/source/driver/summa_init.f90 @@ -34,7 +34,7 @@ module summa_init USE globalData,only:mpar_meta,indx_meta ! metadata structures USE globalData,only:bpar_meta,bvar_meta ! metadata structures USE globalData,only:averageFlux_meta ! metadata for time-step average fluxes -USE globalData,only:lookup_meta +USE globalData,only:lookup_meta ! statistics metadata structures USE globalData,only:statForc_meta ! child metadata for stats @@ -180,11 +180,13 @@ subroutine summa_initialize(summa1_struc, err, message) elapsedRead=0._rkind elapsedWrite=0._rkind elapsedPhysics=0._rkind - +#ifdef NGEN_ACTIVE + !no command arguments with NGen +#else ! get the command line arguments call getCommandArguments(summa1_struc,err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - +#endif ! set directories and files -- summaFileManager used as command-line argument call summa_SetTimesDirsAndFiles(summaFileManagerFile,err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif From 0754c15b7afd927a13fa51b9210154b125a6d1bc Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 28 Mar 2023 00:08:54 +0900 Subject: [PATCH 0639/1472] fix settings paths --- .../celia1990/non_actors/summa_fileManager_celia1990.txt | 4 ++-- test_ngen/summa-init-cat-27.namelist.input | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt index b74922505..ea8dffb37 100644 --- a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt +++ b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt @@ -2,8 +2,8 @@ controlVersion 'SUMMA_FILE_MANAGER_V3.0.0' simStartTime '2000-01-01 00:30' simEndTime '2000-01-03 12:00' tmZoneInfo 'localTime' -settingsPath './extern/summa/data_ngen/celia_test/settings/' -forcingPath './extern/summa/data_ngen/celia_test/input_data/celia1990/' +settingsPath './extern/summa/summa/data_ngen/celia_test/settings/' +forcingPath './extern/summa/summa/data_ngen/celia_test/input_data/celia1990/' outputPath './extern/summa/data_ngen/celia_test/output/non_actors/celia1990/' decisionsFile 'syntheticTestCases/celia1990/common/summa_zDecisions.txt' outputControlFile 'meta/Model_Output.txt' diff --git a/test_ngen/summa-init-cat-27.namelist.input b/test_ngen/summa-init-cat-27.namelist.input index 681ddadef..7b33f256e 100644 --- a/test_ngen/summa-init-cat-27.namelist.input +++ b/test_ngen/summa-init-cat-27.namelist.input @@ -1,3 +1,3 @@ ¶meters ! all inclusive stub for now - file_manager = "extern/summa/data_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt" + file_manager = "./extern/summa/summa/test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt" / From f26e5fd5badd67aa183497953ff658b8bd52610c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 28 Mar 2023 00:09:56 +0900 Subject: [PATCH 0640/1472] fix settings paths --- .../celia1990/non_actors/summa_fileManager_celia1990.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt index ea8dffb37..d8d49395d 100644 --- a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt +++ b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt @@ -4,7 +4,7 @@ simEndTime '2000-01-03 12:00' tmZoneInfo 'localTime' settingsPath './extern/summa/summa/data_ngen/celia_test/settings/' forcingPath './extern/summa/summa/data_ngen/celia_test/input_data/celia1990/' -outputPath './extern/summa/data_ngen/celia_test/output/non_actors/celia1990/' +outputPath './extern/summa/summa/data_ngen/celia_test/output/non_actors/celia1990/' decisionsFile 'syntheticTestCases/celia1990/common/summa_zDecisions.txt' outputControlFile 'meta/Model_Output.txt' globalHruParamFile 'syntheticTestCases/celia1990/common/summa_zLocalParamInfo.txt' From cb72b7f509e7078afefe9fc05048200e6099f336 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 28 Mar 2023 00:12:23 +0900 Subject: [PATCH 0641/1472] fix settings paths --- .../celia1990/non_actors/summa_fileManager_celia1990.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt index d8d49395d..7c50cb173 100644 --- a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt +++ b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt @@ -2,9 +2,9 @@ controlVersion 'SUMMA_FILE_MANAGER_V3.0.0' simStartTime '2000-01-01 00:30' simEndTime '2000-01-03 12:00' tmZoneInfo 'localTime' -settingsPath './extern/summa/summa/data_ngen/celia_test/settings/' -forcingPath './extern/summa/summa/data_ngen/celia_test/input_data/celia1990/' -outputPath './extern/summa/summa/data_ngen/celia_test/output/non_actors/celia1990/' +settingsPath './extern/summa/summa/test_ngen/celia_test/settings/' +forcingPath './extern/summa/summa/test_ngen/celia_test/input_data/celia1990/' +outputPath './extern/summa/summa/test_ngen/celia_test/output/non_actors/celia1990/' decisionsFile 'syntheticTestCases/celia1990/common/summa_zDecisions.txt' outputControlFile 'meta/Model_Output.txt' globalHruParamFile 'syntheticTestCases/celia1990/common/summa_zLocalParamInfo.txt' From 37703f96134985c6cffbccff0b8ab3927cc33473 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 28 Mar 2023 00:15:03 +0900 Subject: [PATCH 0642/1472] missed txt file --- .../celia_test/settings/meta/Model_Output.txt | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 test_ngen/celia_test/settings/meta/Model_Output.txt diff --git a/test_ngen/celia_test/settings/meta/Model_Output.txt b/test_ngen/celia_test/settings/meta/Model_Output.txt new file mode 100644 index 000000000..b2a732d2b --- /dev/null +++ b/test_ngen/celia_test/settings/meta/Model_Output.txt @@ -0,0 +1,40 @@ +! --------- +! model variables +! --------- +nSnow | 1 +nSoil | 1 +pptrate | 1 +airtemp | 1 +scalarRainPlusMelt | 1 +scalarSWE | 1 +scalarThroughfallSnow | 1 +scalarThroughfallRain | 1 +scalarSnowSublimation | 1 +scalarInfiltration | 1 +scalarExfiltration | 1 +scalarSurfaceRunoff | 1 +scalarSurfaceTemp | 1 +scalarSenHeatTotal | 1 +scalarLatHeatTotal | 1 +iLayerHeight | 1 +iLayerLiqFluxSoil | 1 +mLayerTemp | 1 +mLayerDepth | 1 +mLayerHeight | 1 +mLayerLiqFluxSoil | 1 +mLayerVolFracIce | 1 +mLayerVolFracLiq | 1 +mLayerVolFracWat | 1 +mLayerMatricHead | 1 +basin__SurfaceRunoff | 1 +basin__ColumnOutflow | 1 +basin__AquiferStorage | 1 +basin__AquiferRecharge | 1 +basin__AquiferBaseflow | 1 +basin__AquiferTranspire | 1 +averageInstantRunoff | 1 +averageRoutedRunoff | 1 +scalarLAI | 1 +scalarSAI | 1 + + From 54882c114efc4034de394a3bd66f3946c73c3eb4 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 28 Mar 2023 23:02:15 +0900 Subject: [PATCH 0643/1472] need to call getCommandArgs even if no arguments --- build/source/driver/summa_init.f90 | 6 ++---- build/source/driver/summa_util.f90 | 4 ++++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/build/source/driver/summa_init.f90 b/build/source/driver/summa_init.f90 index e51fd6f4b..51252525f 100644 --- a/build/source/driver/summa_init.f90 +++ b/build/source/driver/summa_init.f90 @@ -180,13 +180,11 @@ subroutine summa_initialize(summa1_struc, err, message) elapsedRead=0._rkind elapsedWrite=0._rkind elapsedPhysics=0._rkind -#ifdef NGEN_ACTIVE - !no command arguments with NGen -#else + ! get the command line arguments call getCommandArguments(summa1_struc,err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif -#endif + ! set directories and files -- summaFileManager used as command-line argument call summa_SetTimesDirsAndFiles(summaFileManagerFile,err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif diff --git a/build/source/driver/summa_util.f90 b/build/source/driver/summa_util.f90 index 35c2e58d2..8d1ae9426 100644 --- a/build/source/driver/summa_util.f90 +++ b/build/source/driver/summa_util.f90 @@ -86,9 +86,13 @@ subroutine getCommandArguments(summa1_struc,err,message) ! check number of command-line arguments nArgument = command_argument_count() +#ifdef NGEN_ACTIVE + !no command arguments with NGen +#else if (nArgument < 1) then call printCommandHelp() end if +#endif ! read command line arguments allocate(argString(nArgument)) From ea82b7500cf40a64186019bf2564c5a48f46cc40 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 29 Mar 2023 00:19:37 +0900 Subject: [PATCH 0644/1472] add complete stub for input of none in BMI --- build/source/driver/summa_driver.f90 | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index f5fa77b3b..18e9b1788 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -317,13 +317,13 @@ function summa_output_item_count(this, count) result (bmi_status) bmi_status = BMI_SUCCESS end function summa_output_item_count - ! List input variables (none) + ! List input variables (stub) function summa_input_var_names(this, names) result (bmi_status) class (summa_bmi), intent(in) :: this character (*), pointer, intent(out) :: names(:) integer :: bmi_status - input_items(1) = '' + input_items() = 'none' names => input_items bmi_status = BMI_SUCCESS end function summa_input_var_names @@ -698,6 +698,7 @@ function summa_var_units(this, name, units) result (bmi_status) integer :: bmi_status select case (name) + case('none') ; units = 'none' ; bmi_status = BMI_SUCCESS !stub for inputs case('land_surface_water__runoff_volume_flux') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS case('land_surface_water__evaporation_mass_flux') ; units = 'kg m-2 s-1'; bmi_status = BMI_SUCCESS case('atmosphere_water__precipitation_mass_flux') ; units = 'kg m-2 s-1'; bmi_status = BMI_SUCCESS @@ -727,6 +728,9 @@ function summa_var_itemsize(this, name, size) result (bmi_status) integer :: bmi_status select case(name) + case('none') + size = 0 + bmi_status = BMI_SUCCESS case default call get_basin_field(this, name, 1, targetarr) ! See near bottom of file size = sizeof(targetarr(1)) ! 'sizeof' in gcc & ifort @@ -791,6 +795,9 @@ function summa_get_float(this, name, dest) result (bmi_status) integer :: bmi_status select case(name) + case('none') + dest = 0.0 + bmi_status = BMI_SUCCESS case default call get_basin_field(this, name, sum(gru_struc(:)%hruCount), targetarr) dest = targetarr @@ -936,6 +943,9 @@ function summa_set_float(this, name, src) result (bmi_status) integer :: bmi_status select case(name) + case('none') + !this%model%input = src(1) + bmi_status = BMI_SUCCESS case default bmi_status = BMI_FAILURE end select From 30e291b9f3b40f7463422e2ab8796e0a63787152 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 29 Mar 2023 00:21:54 +0900 Subject: [PATCH 0645/1472] missed index --- build/source/driver/summa_driver.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 18e9b1788..661904f0c 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -323,7 +323,7 @@ function summa_input_var_names(this, names) result (bmi_status) character (*), pointer, intent(out) :: names(:) integer :: bmi_status - input_items() = 'none' + input_items(1) = 'none' names => input_items bmi_status = BMI_SUCCESS end function summa_input_var_names From 89ed612d66e5dde31a339d30fd8452fa7e0f16d3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 4 Apr 2023 13:23:16 +0900 Subject: [PATCH 0646/1472] precip is input not output --- build/source/driver/summa_driver.f90 | 83 ++++++++++++++++++---------- 1 file changed, 53 insertions(+), 30 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 661904f0c..b343e019e 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -156,8 +156,8 @@ module summa_driver ! define parameters for the model simulation integer(i4b), parameter :: n=1 ! number of instantiations ! Exchange items - integer, parameter :: input_item_count = 1 - integer, parameter :: output_item_count = 16 + integer, parameter :: input_item_count = 7 + integer, parameter :: output_item_count = 15 character (len=BMI_MAX_VAR_NAME), target,dimension(input_item_count) :: input_items character (len=BMI_MAX_VAR_NAME), target,dimension(output_item_count) :: output_items ! --------------------------------------------------------------------------------------- @@ -317,13 +317,20 @@ function summa_output_item_count(this, count) result (bmi_status) bmi_status = BMI_SUCCESS end function summa_output_item_count - ! List input variables (stub) + ! List output variables standardized as "https://csdms.colorado.edu/wiki/CSDMS_Standard_Names" function summa_input_var_names(this, names) result (bmi_status) class (summa_bmi), intent(in) :: this character (*), pointer, intent(out) :: names(:) integer :: bmi_status - input_items(1) = 'none' + input_items(1) = 'atmosphere_water__precipitation_mass_flux' + input_items(2) = 'land_surface_air__temperature' + input_items(3) = 'land_surface__specific_humidity' + input_items(4) = 'land_surface_wind__speed' + input_items(5) = 'land_surface_radiation~incoming~shortwave__energy_flux' + input_items(6) = 'land_surface_radiation~incoming~longwave__energy_flux' + input_items(7) = 'land_surface_air__pressure' + names => input_items bmi_status = BMI_SUCCESS end function summa_input_var_names @@ -336,20 +343,19 @@ function summa_output_var_names(this, names) result (bmi_status) output_items(1) = 'land_surface_water__runoff_volume_flux' output_items(2) = 'land_surface_water__evaporation_mass_flux' - output_items(3) = 'atmosphere_water__precipitation_mass_flux' - output_items(4) = 'land_vegetation_water__evaporation_mass_flux' - output_items(5) = 'land_vegetation_water__transpiration_mass_flux' - output_items(6) = 'snowpack__sublimation_mass_flux' - output_items(7) = 'land_vegetation_water__sublimation_mass_flux' - output_items(8) = 'snowpack_mass' - output_items(9) = 'soil_water__mass' - output_items(10)= 'land_vegetation_water__mass' - output_items(11)= 'land_surface_radiation~net~total__energy_flux' - output_items(12)= 'land_atmosphere_heat~net~latent__energy_flux' !(incoming to the *atmosphere*, since atmosphere is last) - output_items(13)= 'land_atmosphere_heat~net~sensible__energy_flux' !(incoming to the *atmosphere*, since atmosphere is last) - output_items(14)= 'atmosphere_energy~net~total__energy_flux' - output_items(15)= 'land_vegetation_energy~net~total__energy_flux' - output_items(16)= 'land_surface_energy~net~total__energy_flux' + output_items(3) = 'land_vegetation_water__evaporation_mass_flux' + output_items(4) = 'land_vegetation_water__transpiration_mass_flux' + output_items(5) = 'snowpack__sublimation_mass_flux' + output_items(6) = 'land_vegetation_water__sublimation_mass_flux' + output_items(7) = 'snowpack_mass' + output_items(8) = 'soil_water__mass' + output_items(9) = 'land_vegetation_water__mass' + output_items(10)= 'land_surface_radiation~net~total__energy_flux' + output_items(11)= 'land_atmosphere_heat~net~latent__energy_flux' !(incoming to the *atmosphere*, since atmosphere is last) + output_items(12)= 'land_atmosphere_heat~net~sensible__energy_flux' !(incoming to the *atmosphere*, since atmosphere is last) + output_items(13)= 'atmosphere_energy~net~total__energy_flux' + output_items(14)= 'land_vegetation_energy~net~total__energy_flux' + output_items(15)= 'land_surface_energy~net~total__energy_flux' names => output_items bmi_status = BMI_SUCCESS end function summa_output_var_names @@ -698,10 +704,19 @@ function summa_var_units(this, name, units) result (bmi_status) integer :: bmi_status select case (name) - case('none') ; units = 'none' ; bmi_status = BMI_SUCCESS !stub for inputs + + ! input + case('atmosphere_water__precipitation_mass_flux') ; units = 'kg m-2 s-1'; bmi_status = BMI_SUCCESS + case('land_surface_air__temperature') ; units = 'K' ; bmi_status = BMI_SUCCESS + case('land_surface__specific_humidity') ; units = 'kg kg-1' ; bmi_status = BMI_SUCCESS + case('land_surface_wind__speed') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS + case('land_surface_radiation~incoming~shortwave__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS + case('land_surface_radiation~incoming~longwave__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS + case('land_surface_air__pressure') ; units = 'kg m−1 s−2'; bmi_status = BMI_SUCCESS + + ! output case('land_surface_water__runoff_volume_flux') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS case('land_surface_water__evaporation_mass_flux') ; units = 'kg m-2 s-1'; bmi_status = BMI_SUCCESS - case('atmosphere_water__precipitation_mass_flux') ; units = 'kg m-2 s-1'; bmi_status = BMI_SUCCESS case('land_vegetation_water__evaporation_mass_flux') ; units = 'kg m-2 s-1'; bmi_status = BMI_SUCCESS case('land_vegetation_water__transpiration_mass_flux'); units = 'kg m-2 s-1'; bmi_status = BMI_SUCCESS case('snowpack__sublimation_mass_flux') ; units = 'kg m-2 s-1'; bmi_status = BMI_SUCCESS @@ -728,9 +743,6 @@ function summa_var_itemsize(this, name, size) result (bmi_status) integer :: bmi_status select case(name) - case('none') - size = 0 - bmi_status = BMI_SUCCESS case default call get_basin_field(this, name, 1, targetarr) ! See near bottom of file size = sizeof(targetarr(1)) ! 'sizeof' in gcc & ifort @@ -795,9 +807,6 @@ function summa_get_float(this, name, dest) result (bmi_status) integer :: bmi_status select case(name) - case('none') - dest = 0.0 - bmi_status = BMI_SUCCESS case default call get_basin_field(this, name, sum(gru_struc(:)%hruCount), targetarr) dest = targetarr @@ -943,8 +952,8 @@ function summa_set_float(this, name, src) result (bmi_status) integer :: bmi_status select case(name) - case('none') - !this%model%input = src(1) + case('land_surface_air__temperature') + this%model%forcing%land_surface_air__temperature(1) = src(1) bmi_status = BMI_SUCCESS case default bmi_status = BMI_FAILURE @@ -1066,12 +1075,26 @@ subroutine get_basin_field(this, name, do_nHRU, targetarr) i = (iGRU-1) * gru_struc(iGRU)%hruCount + jHRU if (i > do_nHRU) return select case (name) + + ! input + case('atmosphere_water__precipitation_mass_flux') + targetarr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%pptrate) + case('land_surface__specific_humidity') + targetarr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airtemp) + case('land_surface_wind__speed') + targetarr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%windspd) + case('land_surface_radiation~incoming~shortwave__energy_flux') + targetarr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%SWRadAtm) + case('land_surface_radiation~incoming~longwave__energy_flux') + targetarr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%LWRadAtm) + case('land_surface_air__pressure') + targetarr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airpres) + + ! output case('land_surface_water__runoff_volume_flux') targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarTotalRunoff)%dat(1) case('land_surface_water__evaporation_mass_flux') targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarGroundEvaporation)%dat(1) - case('atmosphere_water__precipitation_mass_flux') - targetarr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%pptrate) case('land_vegetation_water__evaporation_mass_flux') targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) case('land_vegetation_water__transpiration_mass_flux') From 16cfff8492ef33b962907bcf5e7bd754a22db1d6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 4 Apr 2023 17:37:11 +0900 Subject: [PATCH 0647/1472] add assign_basin_field --- build/source/driver/summa_driver.f90 | 93 +++++++++++++++++++--------- 1 file changed, 64 insertions(+), 29 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index b343e019e..e3b964975 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -952,8 +952,8 @@ function summa_set_float(this, name, src) result (bmi_status) integer :: bmi_status select case(name) - case('land_surface_air__temperature') - this%model%forcing%land_surface_air__temperature(1) = src(1) + case default + call assign_basin_field(this, name, src) ! See near bottom of file bmi_status = BMI_SUCCESS case default bmi_status = BMI_FAILURE @@ -1025,35 +1025,70 @@ function summa_set_at_indices_double(this, name, inds, src) result (bmi_status) end function summa_set_at_indices_double #ifdef NGEN_ACTIVE - function register_bmi(this) result(bmi_status) bind(C, name="register_bmi") - use, intrinsic:: iso_c_binding, only: c_ptr, c_loc, c_int - use iso_c_bmif_2_0 - implicit none - type(c_ptr) :: this ! If not value, then from the C perspective `this` is a void** - integer(kind=c_int) :: bmi_status - !Create the model instance to use - type(summa_bmi), pointer :: bmi_model - !Create a simple pointer wrapper - type(box), pointer :: bmi_box - - !allocate model - allocate(summa_bmi::bmi_model) - !allocate the pointer box - allocate(bmi_box) - - !associate the wrapper pointer the created model instance - bmi_box%ptr => bmi_model - - if( .not. associated( bmi_box ) .or. .not. associated( bmi_box%ptr ) ) then - bmi_status = BMI_FAILURE - else - !Return the pointer to box - this = c_loc(bmi_box) - bmi_status = BMI_SUCCESS - endif - end function register_bmi + function register_bmi(this) result(bmi_status) bind(C, name="register_bmi") + use, intrinsic:: iso_c_binding, only: c_ptr, c_loc, c_int + use iso_c_bmif_2_0 + implicit none + type(c_ptr) :: this ! If not value, then from the C perspective `this` is a void** + integer(kind=c_int) :: bmi_status + !Create the model instance to use + type(summa_bmi), pointer :: bmi_model + !Create a simple pointer wrapper + type(box), pointer :: bmi_box + + !allocate model + allocate(summa_bmi::bmi_model) + !allocate the pointer box + allocate(bmi_box) + + !associate the wrapper pointer the created model instance + bmi_box%ptr => bmi_model + + if( .not. associated( bmi_box ) .or. .not. associated( bmi_box%ptr ) ) then + bmi_status = BMI_FAILURE + else + ! Return the pointer to box + this = c_loc(bmi_box) + bmi_status = BMI_SUCCESS + endif + end function register_bmi #endif + ! non-BMI helper function to assign input fields + subroutine assign_basin_field(this, name, inputarr) + implicit none + class (summa_bmi), intent(inout) :: this + character (len=*), intent(in) :: name + real, intent(in) :: inputarr(sum(gru_struc(:)%hruCount)) + integer :: iGRU, jHRU, i + + summaVars: associate(& + forcStruct => this%model%summa1_struc(n)%forcStruct , & ! x%gru(:)%hru(:)%var(:) -- model forcing data + ) + targetarr = -999.0 + do iGRU = 1, this%model%summa1_struc(n)%nGRU + do jHRU = 1, gru_struc(iGRU)%hruCount + i = (iGRU-1) * gru_struc(iGRU)%hruCount + jHRU + select case (name) + ! input + case('atmosphere_water__precipitation_mass_flux') + forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%pptrate) = inputarr(i) + case('land_surface__specific_humidity') + forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airtemp) = inputarr(i) + case('land_surface_wind__speed') + forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%windspd) = inputarr(i) + case('land_surface_radiation~incoming~shortwave__energy_flux') + forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%SWRadAtm) = inputarr(i) + case('land_surface_radiation~incoming~longwave__energy_flux') + forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%LWRadAtm) = inputarr(i) + case('land_surface_air__pressure') + forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airpres) = inputarr(i) + end select + end do + end do + end associate summaVars + end subroutine assign_basin_field + ! non-BMI helper function to get fields, only get first do_nHRU of them subroutine get_basin_field(this, name, do_nHRU, targetarr) implicit none From c1004357896cbd320dbaff79f0346f72bc262fa6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 4 Apr 2023 17:38:43 +0900 Subject: [PATCH 0648/1472] syntax error --- build/source/driver/summa_driver.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index e3b964975..c4418b24c 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -1063,7 +1063,7 @@ subroutine assign_basin_field(this, name, inputarr) integer :: iGRU, jHRU, i summaVars: associate(& - forcStruct => this%model%summa1_struc(n)%forcStruct , & ! x%gru(:)%hru(:)%var(:) -- model forcing data + forcStruct => this%model%summa1_struc(n)%forcStruct & ! x%gru(:)%hru(:)%var(:) -- model forcing data ) targetarr = -999.0 do iGRU = 1, this%model%summa1_struc(n)%nGRU From f6b3acd8d3c00a2a4fb2155fe7ff1a6435bc9f44 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 4 Apr 2023 17:39:50 +0900 Subject: [PATCH 0649/1472] syntax error --- build/source/driver/summa_driver.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index c4418b24c..037477ad5 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -1065,7 +1065,6 @@ subroutine assign_basin_field(this, name, inputarr) summaVars: associate(& forcStruct => this%model%summa1_struc(n)%forcStruct & ! x%gru(:)%hru(:)%var(:) -- model forcing data ) - targetarr = -999.0 do iGRU = 1, this%model%summa1_struc(n)%nGRU do jHRU = 1, gru_struc(iGRU)%hruCount i = (iGRU-1) * gru_struc(iGRU)%hruCount + jHRU From e26d714c7c53890a04c3a160ce56ad388e2edb11 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 4 Apr 2023 17:40:38 +0900 Subject: [PATCH 0650/1472] syntax error --- build/source/driver/summa_driver.f90 | 2 -- 1 file changed, 2 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 037477ad5..026c37a30 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -955,8 +955,6 @@ function summa_set_float(this, name, src) result (bmi_status) case default call assign_basin_field(this, name, src) ! See near bottom of file bmi_status = BMI_SUCCESS - case default - bmi_status = BMI_FAILURE end select end function summa_set_float From 47e4e8c0e107b5d4b3e39580b1d24ab69734e5a0 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 4 Apr 2023 23:19:11 +0900 Subject: [PATCH 0651/1472] add in all input forcing variables, except time which will have to be assumed in ngen --- build/source/driver/summa_driver.f90 | 47 ++++++++++++++++--- ...xample_realization_config_w_summa_bmi.json | 13 ++++- ...e_realization_config_w_summa_bmi__mac.json | 9 ++++ 3 files changed, 60 insertions(+), 9 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 026c37a30..13a799e53 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -156,7 +156,11 @@ module summa_driver ! define parameters for the model simulation integer(i4b), parameter :: n=1 ! number of instantiations ! Exchange items +#ifdef NGEN_ACTIVE + integer, parameter :: input_item_count = 8 +#else integer, parameter :: input_item_count = 7 +#endif integer, parameter :: output_item_count = 15 character (len=BMI_MAX_VAR_NAME), target,dimension(input_item_count) :: input_items character (len=BMI_MAX_VAR_NAME), target,dimension(output_item_count) :: output_items @@ -325,8 +329,13 @@ function summa_input_var_names(this, names) result (bmi_status) input_items(1) = 'atmosphere_water__precipitation_mass_flux' input_items(2) = 'land_surface_air__temperature' - input_items(3) = 'land_surface__specific_humidity' + input_items(3) = 'atmosphere_air_water~vapor__relative_saturation' +#ifdef NGEN_ACTIVE + input_items(4) = 'land_surface_wind__x_component_of_velocity' + input_items(8) = 'land_surface_wind__y_component_of_velocity' +#else input_items(4) = 'land_surface_wind__speed' +#endif input_items(5) = 'land_surface_radiation~incoming~shortwave__energy_flux' input_items(6) = 'land_surface_radiation~incoming~longwave__energy_flux' input_items(7) = 'land_surface_air__pressure' @@ -704,12 +713,16 @@ function summa_var_units(this, name, units) result (bmi_status) integer :: bmi_status select case (name) - ! input case('atmosphere_water__precipitation_mass_flux') ; units = 'kg m-2 s-1'; bmi_status = BMI_SUCCESS case('land_surface_air__temperature') ; units = 'K' ; bmi_status = BMI_SUCCESS - case('land_surface__specific_humidity') ; units = 'kg kg-1' ; bmi_status = BMI_SUCCESS + case('atmosphere_air_water~vapor__relative_saturation') ; units = 'kg kg-1' ; bmi_status = BMI_SUCCESS +#ifdef NGEN_ACTIVE + case('land_surface_wind__x_component_of_velocity') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS + case('land_surface_wind__y_component_of_velocity') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS +#else case('land_surface_wind__speed') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS +#endif case('land_surface_radiation~incoming~shortwave__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS case('land_surface_radiation~incoming~longwave__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS case('land_surface_air__pressure') ; units = 'kg m−1 s−2'; bmi_status = BMI_SUCCESS @@ -1061,7 +1074,8 @@ subroutine assign_basin_field(this, name, inputarr) integer :: iGRU, jHRU, i summaVars: associate(& - forcStruct => this%model%summa1_struc(n)%forcStruct & ! x%gru(:)%hru(:)%var(:) -- model forcing data + forcStruct => this%model%summa1_struc(n)%forcStruct , & ! x%gru(:)%hru(:)%var(:) -- model forcing data + diagStruct => this%model%summa1_struc(n)%diagStruct & ! x%gru(:)%hru(:)%var(:)%dat -- model diagnostic variables ) do iGRU = 1, this%model%summa1_struc(n)%nGRU do jHRU = 1, gru_struc(iGRU)%hruCount @@ -1070,10 +1084,19 @@ subroutine assign_basin_field(this, name, inputarr) ! input case('atmosphere_water__precipitation_mass_flux') forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%pptrate) = inputarr(i) - case('land_surface__specific_humidity') + case('land_surface_air__temperature') forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airtemp) = inputarr(i) + case('atmosphere_air_water~vapor__relative_saturation') + forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%spechum) = inputarr(i) +#ifdef NGEN_ACTIVE + case('land_surface_wind__x_component_of_velocity') + diagStruct%gru(iGRU)%hru(jHRU)%var(windspd_x)%dat(1) = inputarr(i) + case('land_surface_wind__y_component_of_velocity') + diagStruct%gru(iGRU)%hru(jHRU)%var(windspd_y)%dat(1) = inputarr(i) +#else case('land_surface_wind__speed') forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%windspd) = inputarr(i) +#endif case('land_surface_radiation~incoming~shortwave__energy_flux') forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%SWRadAtm) = inputarr(i) case('land_surface_radiation~incoming~longwave__energy_flux') @@ -1107,14 +1130,24 @@ subroutine get_basin_field(this, name, do_nHRU, targetarr) i = (iGRU-1) * gru_struc(iGRU)%hruCount + jHRU if (i > do_nHRU) return select case (name) - ! input case('atmosphere_water__precipitation_mass_flux') targetarr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%pptrate) - case('land_surface__specific_humidity') + case('land_surface_air__temperature') targetarr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airtemp) + case('atmosphere_air_water~vapor__relative_saturation') + targetarr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%spechum) case('land_surface_wind__speed') targetarr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%windspd) +#ifdef NGEN_ACTIVE + case('land_surface_wind__x_component_of_velocity') + targetarr(i) = diagStruct%gru(iGRU)%hru(jHRU)%var(windspd_x)%dat(1) + case('land_surface_wind__y_component_of_velocity') + targetarr(i) = diagStruct%gru(iGRU)%hru(jHRU)%var(windspd_y)%dat(1) +#else + case('land_surface_wind__speed') + targetarr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%windspd) +#endif case('land_surface_radiation~incoming~shortwave__energy_flux') targetarr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%SWRadAtm) case('land_surface_radiation~incoming~longwave__energy_flux') diff --git a/test_ngen/example_realization_config_w_summa_bmi.json b/test_ngen/example_realization_config_w_summa_bmi.json index fd43b0ed6..bc0eb911e 100644 --- a/test_ngen/example_realization_config_w_summa_bmi.json +++ b/test_ngen/example_realization_config_w_summa_bmi.json @@ -7,8 +7,18 @@ "model_type_name": "bmi_fortran_summa", "library_file": "./extern/summa/cmake_build/libsummabmi.so", "forcing_file": "", - "init_config": "./data/bmi/fortran/summa-init-{{id}}.namelist.input", "allow_exceed_end_time": true, + "init_config": "./data/bmi/fortran/summa-init-{{id}}.namelist.input", + "allow_exceed_end_time": true, "main_output_variable": "land_surface_water__runoff_volume_flux", + "variables_names_map": { + "atmosphere_water__precipitation_mass_flux": "precip_rate", + "land_surface_air__temperature": "TMP_2maboveground", + "atmosphere_air_water~vapor__relative_saturation": "SPFH_2maboveground", + "land_surface_wind__x_component_of_velocity": "UGRD_10maboveground", + "land_surface_wind__y_component_of_velocity": "VGRD_10maboveground", + "land_surface_radiation~incoming~shortwave__energy_flux": "DSWRF_surface", + "land_surface_radiation~incoming~longwave__energy_flux": "DLWRF_surface", + "land_surface_air__pressure": "PRES_surface", "uses_forcing_file": false } } @@ -25,4 +35,3 @@ "output_interval": 3600 } } - diff --git a/test_ngen/example_realization_config_w_summa_bmi__mac.json b/test_ngen/example_realization_config_w_summa_bmi__mac.json index 9bb8b90fe..80aca3bef 100644 --- a/test_ngen/example_realization_config_w_summa_bmi__mac.json +++ b/test_ngen/example_realization_config_w_summa_bmi__mac.json @@ -10,6 +10,15 @@ "init_config": "./data/bmi/fortran/summa-init-{{id}}.namelist.input", "allow_exceed_end_time": true, "main_output_variable": "land_surface_water__runoff_volume_flux", + "variables_names_map": { + "atmosphere_water__precipitation_mass_flux": "precip_rate", + "land_surface_air__temperature": "TMP_2maboveground", + "atmosphere_air_water~vapor__relative_saturation": "SPFH_2maboveground", + "land_surface_wind__x_component_of_velocity": "UGRD_10maboveground", + "land_surface_wind__y_component_of_velocity": "VGRD_10maboveground", + "land_surface_radiation~incoming~shortwave__energy_flux": "DSWRF_surface", + "land_surface_radiation~incoming~longwave__energy_flux": "DLWRF_surface", + "land_surface_air__pressure": "PRES_surface", "uses_forcing_file": false } } From a773fa1074097a4e5c5bfbd867179515d7154bd3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 5 Apr 2023 00:55:37 +0900 Subject: [PATCH 0652/1472] time as an input if not NGEN --- build/source/driver/summa_driver.f90 | 339 ++++++++++++++++----------- 1 file changed, 207 insertions(+), 132 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 13a799e53..a21841609 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -159,7 +159,7 @@ module summa_driver #ifdef NGEN_ACTIVE integer, parameter :: input_item_count = 8 #else - integer, parameter :: input_item_count = 7 + integer, parameter :: input_item_count = 12 #endif integer, parameter :: output_item_count = 15 character (len=BMI_MAX_VAR_NAME), target,dimension(input_item_count) :: input_items @@ -322,6 +322,8 @@ function summa_output_item_count(this, count) result (bmi_status) end function summa_output_item_count ! List output variables standardized as "https://csdms.colorado.edu/wiki/CSDMS_Standard_Names" + ! NGEN uses two component wind and a time vector that is not currently separable + ! (compute wind speed from the two components and time from start time and hourly step assumption) function summa_input_var_names(this, names) result (bmi_status) class (summa_bmi), intent(in) :: this character (*), pointer, intent(out) :: names(:) @@ -335,6 +337,11 @@ function summa_input_var_names(this, names) result (bmi_status) input_items(8) = 'land_surface_wind__y_component_of_velocity' #else input_items(4) = 'land_surface_wind__speed' + input_items(8) = 'model__time_year' + input_items(9) = 'model__time_month' + input_items(10)= 'model__time_day' + input_items(11)= 'model__time_hour' + input_items(12)= 'model__time_min' #endif input_items(5) = 'land_surface_radiation~incoming~shortwave__energy_flux' input_items(6) = 'land_surface_radiation~incoming~longwave__energy_flux' @@ -607,7 +614,7 @@ function summa_grid_node_count(this, grid, count) result(bmi_status) end select end function summa_grid_node_count - ! Get the number of edges in an unstructured grid, points is 0 BUT COULD BE FOR GRUs + ! Get the number of edges in an unstructured grid, points is 0 BUT COULD BE USED FOR GRUs function summa_grid_edge_count(this, grid, count) result(bmi_status) class(summa_bmi), intent(in) :: this integer, intent(in) :: grid @@ -621,7 +628,7 @@ function summa_grid_edge_count(this, grid, count) result(bmi_status) end select end function summa_grid_edge_count - ! Get the number of faces in an unstructured grid, points is 0 BUT COULD BE FOR GRUs + ! Get the number of faces in an unstructured grid, points is 0 BUT COULD BE USED FOR GRUs function summa_grid_face_count(this, grid, count) result(bmi_status) class(summa_bmi), intent(in) :: this integer, intent(in) :: grid @@ -635,7 +642,7 @@ function summa_grid_face_count(this, grid, count) result(bmi_status) end select end function summa_grid_face_count - ! Get the edge-node connectivity, points is 0 BUT COULD BE FOR GRUs + ! Get the edge-node connectivity, points is 0 BUT COULD BE USED FOR GRUs function summa_grid_edge_nodes(this, grid, edge_nodes) result(bmi_status) class(summa_bmi), intent(in) :: this integer, intent(in) :: grid @@ -649,7 +656,7 @@ function summa_grid_edge_nodes(this, grid, edge_nodes) result(bmi_status) end select end function summa_grid_edge_nodes - ! Get the face-edge connectivity, points is 0 BUT COULD BE FOR GRUs + ! Get the face-edge connectivity, points is 0 BUT COULD BE USED FOR GRUs function summa_grid_face_edges(this, grid, face_edges) result(bmi_status) class(summa_bmi), intent(in) :: this integer, intent(in) :: grid @@ -663,7 +670,7 @@ function summa_grid_face_edges(this, grid, face_edges) result(bmi_status) end select end function summa_grid_face_edges - ! Get the face-node connectivity, points is 0 BUT COULD BE FOR GRUs + ! Get the face-node connectivity, points is 0 BUT COULD BE USED FOR GRUs function summa_grid_face_nodes(this, grid, face_nodes) result(bmi_status) class(summa_bmi), intent(in) :: this integer, intent(in) :: grid @@ -677,7 +684,7 @@ function summa_grid_face_nodes(this, grid, face_nodes) result(bmi_status) end select end function summa_grid_face_nodes - ! Get the number of nodes for each face, points is 0 BUT COULD BE FOR GRUs + ! Get the number of nodes for each face, points is 0 BUT COULD BE USED FOR GRUs function summa_grid_nodes_per_face(this, grid, nodes_per_face) result(bmi_status) class(summa_bmi), intent(in) :: this integer, intent(in) :: grid @@ -698,11 +705,12 @@ function summa_var_type(this, name, type) result (bmi_status) character (len=*), intent(out) :: type integer :: bmi_status - select case(name) - case default + if(name(1:5)=='model')then + type = "integer" + else type = "real" - bmi_status = BMI_SUCCESS - end select + endif + bmi_status = BMI_SUCCESS end function summa_var_type ! The units of the given variable @@ -722,6 +730,11 @@ function summa_var_units(this, name, units) result (bmi_status) case('land_surface_wind__y_component_of_velocity') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS #else case('land_surface_wind__speed') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS + case('model_time_year') ; units = '' ; bmi_status = BMI_SUCCESS + case('model_time_month') ; units = '' ; bmi_status = BMI_SUCCESS + case('model_time_day') ; units = '' ; bmi_status = BMI_SUCCESS + case('model_time_hour') ; units = '' ; bmi_status = BMI_SUCCESS + case('model_time_min') ; units = '' ; bmi_status = BMI_SUCCESS #endif case('land_surface_radiation~incoming~shortwave__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS case('land_surface_radiation~incoming~longwave__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS @@ -752,15 +765,18 @@ function summa_var_itemsize(this, name, size) result (bmi_status) class (summa_bmi), intent(in) :: this character (len=*), intent(in) :: name integer, intent(out) :: size - real ,target :: targetarr(sum(gru_struc(:)%hruCount)) + real, target :: target_arr(sum(gru_struc(:)%hruCount)) + integer ,target :: itarget_arr integer :: bmi_status - select case(name) - case default - call get_basin_field(this, name, 1, targetarr) ! See near bottom of file - size = sizeof(targetarr(1)) ! 'sizeof' in gcc & ifort - bmi_status = BMI_SUCCESS - end select + call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file + ! use the real or integer target + if(name(1:5)=='model')then + size = sizeof(itarget_arr) ! 'sizeof' in gcc & ifort + else + size = sizeof(target_arr(1)) ! 'sizeof' in gcc & ifort + endif + bmi_status = BMI_SUCCESS end function summa_var_itemsize ! The size of the given variable @@ -802,12 +818,16 @@ function summa_get_int(this, name, dest) result (bmi_status) class (summa_bmi), intent(in) :: this character (len=*), intent(in) :: name integer, intent(inout) :: dest(:) + real, target :: target_arr(sum(gru_struc(:)%hruCount)) + integer ,target :: itarget_arr integer :: bmi_status select case(name) case default - dest(:) = -1 - bmi_status = BMI_FAILURE + call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file + ! use the integer target + dest = itarget_arr + bmi_status = BMI_SUCCESS end select end function summa_get_int @@ -816,13 +836,15 @@ function summa_get_float(this, name, dest) result (bmi_status) class (summa_bmi), intent(in) :: this character (len=*), intent(in) :: name real, intent(inout) :: dest(:) - real, target :: targetarr(sum(gru_struc(:)%hruCount)) + real, target :: target_arr(sum(gru_struc(:)%hruCount)) + integer ,target :: itarget_arr integer :: bmi_status select case(name) case default - call get_basin_field(this, name, sum(gru_struc(:)%hruCount), targetarr) - dest = targetarr + call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file + ! use the real target + dest = target_arr bmi_status = BMI_SUCCESS end select end function summa_get_float @@ -847,11 +869,18 @@ function summa_get_ptr_int(this, name, dest_ptr) result (bmi_status) character (len=*), intent(in) :: name integer, pointer, intent(inout) :: dest_ptr(:) integer :: bmi_status, n_elements + real, target :: target_arr(sum(gru_struc(:)%hruCount)) + integer ,target :: itarget_arr type (c_ptr) :: src select case(name) case default - bmi_status = BMI_FAILURE + call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file + ! use the integer target + src = c_loc(itarget_arr) + n_elements = sum(gru_struc(:)%hruCount) + call c_f_pointer(src, dest_ptr, [n_elements]) + bmi_status = BMI_SUCCESS end select end function summa_get_ptr_int @@ -861,13 +890,15 @@ function summa_get_ptr_float(this, name, dest_ptr) result (bmi_status) character (len=*), intent(in) :: name real, pointer, intent(inout) :: dest_ptr(:) integer :: bmi_status, n_elements - real, target :: targetarr(sum(gru_struc(:)%hruCount)) + real, target :: target_arr(sum(gru_struc(:)%hruCount)) + integer ,target :: itarget_arr type (c_ptr) :: src select case(name) case default - call get_basin_field(this, name, 1, targetarr) ! See near bottom of file - src = c_loc(targetarr(1)) + call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file + ! use the real target + src = c_loc(target_arr(1)) n_elements = sum(gru_struc(:)%hruCount) call c_f_pointer(src, dest_ptr, [n_elements]) bmi_status = BMI_SUCCESS @@ -895,13 +926,19 @@ function summa_get_at_indices_int(this, name, dest, inds) result (bmi_status) integer, intent(inout) :: dest(:) integer, intent(in) :: inds(:) integer :: bmi_status, i, n_elements + real, target :: target_arr(sum(gru_struc(:)%hruCount)) + integer ,target :: itarget_arr type (c_ptr) src integer, pointer :: src_flattened(:) select case(name) case default - bmi_status = BMI_FAILURE - end select + call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file + ! use the integer target + src = c_loc(itarget_arr) + n_elements = sum(gru_struc(:)%hruCount) + call c_f_pointer(src, dest_ptr, [n_elements]) + bmi_status = BMI_SUCCESS end select end function summa_get_at_indices_int ! Get values of a real variable at the given locations @@ -911,14 +948,16 @@ function summa_get_at_indices_float(this, name, dest, inds) result (bmi_status) real, intent(inout) :: dest(:) integer, intent(in) :: inds(:) integer :: bmi_status, i, n_elements - real, target :: targetarr(sum(gru_struc(:)%hruCount)) + real, target :: target_arr(sum(gru_struc(:)%hruCount)) + integer ,target :: itarget_arr type (c_ptr) src real, pointer :: src_flattened(:) select case(name) case default - call get_basin_field(this, name, 1, targetarr) ! See near bottom of file - src = c_loc(targetarr(1)) + call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file + ! use the real target + src = c_loc(target_arr(1)) call c_f_pointer(src, src_flattened, [n_elements]) n_elements = size(inds) do i = 1, n_elements @@ -944,16 +983,19 @@ function summa_get_at_indices_double(this, name, dest, inds) result (bmi_status) end select end function summa_get_at_indices_double - ! Set new integer values + ! Set new integer values, ONLY FOR INPUT VARIABLES function summa_set_int(this, name, src) result (bmi_status) class (summa_bmi), intent(inout) :: this character (len=*), intent(in) :: name integer, intent(in) :: src(:) + real :: rsrc(sum(gru_struc(:)%hruCount)) integer :: bmi_status select case(name) case default - bmi_status = BMI_FAILURE + rsrc = -999.0 + call assign_basin_field(this, name, rsrc, src) ! See near bottom of file + bmi_status = BMI_SUCCESS end select end function summa_set_int @@ -962,11 +1004,12 @@ function summa_set_float(this, name, src) result (bmi_status) class (summa_bmi), intent(inout) :: this character (len=*), intent(in) :: name real, intent(in) :: src(:) - integer :: bmi_status + integer :: bmi_status, isrc select case(name) case default - call assign_basin_field(this, name, src) ! See near bottom of file + isrc = -999 + call assign_basin_field(this, name, src, isrc) ! See near bottom of file bmi_status = BMI_SUCCESS end select end function summa_set_float @@ -1066,129 +1109,161 @@ end function register_bmi #endif ! non-BMI helper function to assign input fields - subroutine assign_basin_field(this, name, inputarr) + subroutine assign_basin_field(this, name, src_arr, isrc_arr) implicit none class (summa_bmi), intent(inout) :: this character (len=*), intent(in) :: name - real, intent(in) :: inputarr(sum(gru_struc(:)%hruCount)) + real, intent(in) :: src_arr(sum(gru_struc(:)%hruCount)) + integer, intent(in) :: isrc_arr integer :: iGRU, jHRU, i summaVars: associate(& + timeStruct => this%model%summa1_struc(n)%timeStruct , & ! x%var(:) -- model time data forcStruct => this%model%summa1_struc(n)%forcStruct , & ! x%gru(:)%hru(:)%var(:) -- model forcing data diagStruct => this%model%summa1_struc(n)%diagStruct & ! x%gru(:)%hru(:)%var(:)%dat -- model diagnostic variables ) - do iGRU = 1, this%model%summa1_struc(n)%nGRU - do jHRU = 1, gru_struc(iGRU)%hruCount - i = (iGRU-1) * gru_struc(iGRU)%hruCount + jHRU - select case (name) - ! input - case('atmosphere_water__precipitation_mass_flux') - forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%pptrate) = inputarr(i) - case('land_surface_air__temperature') - forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airtemp) = inputarr(i) - case('atmosphere_air_water~vapor__relative_saturation') - forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%spechum) = inputarr(i) -#ifdef NGEN_ACTIVE - case('land_surface_wind__x_component_of_velocity') - diagStruct%gru(iGRU)%hru(jHRU)%var(windspd_x)%dat(1) = inputarr(i) - case('land_surface_wind__y_component_of_velocity') - diagStruct%gru(iGRU)%hru(jHRU)%var(windspd_y)%dat(1) = inputarr(i) -#else - case('land_surface_wind__speed') - forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%windspd) = inputarr(i) -#endif - case('land_surface_radiation~incoming~shortwave__energy_flux') - forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%SWRadAtm) = inputarr(i) - case('land_surface_radiation~incoming~longwave__energy_flux') - forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%LWRadAtm) = inputarr(i) - case('land_surface_air__pressure') - forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airpres) = inputarr(i) - end select + + if(name(1:5)=='model')then + select case (name) + ! input + case('model_time_year') + timeStruct%var(iLookTIME%iyyy) = isrc_arr + case('model_time_month') + timeStruct%var(iLookTIME%im) = isrc_arr + case('model_time_day') + timeStruct%var(iLookTIME%id) = isrc_arr + case('model_time_hour') + timeStruct%var(iLookTIME%ih) = isrc_arr + case('model_time_hour') + timeStruct%var(iLookTIME%imin) = isrc_arr + end select + else + do iGRU = 1, this%model%summa1_struc(n)%nGRU + do jHRU = 1, gru_struc(iGRU)%hruCount + i = (iGRU-1) * gru_struc(iGRU)%hruCount + jHRU + select case (name) + ! input + case('atmosphere_water__precipitation_mass_flux') + forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%pptrate) = src_arr(i) + case('land_surface_air__temperature') + forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airtemp) = src_arr(i) + case('atmosphere_air_water~vapor__relative_saturation') + forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%spechum) = src_arr(i) + case('land_surface_wind__x_component_of_velocity') + diagStruct%gru(iGRU)%hru(jHRU)%var(iLookDIAG%windspd_x)%dat(1) = src_arr(i) + case('land_surface_wind__y_component_of_velocity') + diagStruct%gru(iGRU)%hru(jHRU)%var(iLookDIAG%windspd_y)%dat(1) = src_arr(i) + case('land_surface_wind__speed') + forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%windspd) = src_arr(i) + case('land_surface_radiation~incoming~shortwave__energy_flux') + forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%SWRadAtm) = src_arr(i) + case('land_surface_radiation~incoming~longwave__energy_flux') + forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%LWRadAtm) = src_arr(i) + case('land_surface_air__pressure') + forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airpres) = src_arr(i) + end select + end do end do - end do + endif end associate summaVars end subroutine assign_basin_field ! non-BMI helper function to get fields, only get first do_nHRU of them - subroutine get_basin_field(this, name, do_nHRU, targetarr) + subroutine get_basin_field(this, name, do_nHRU, target_arr, itarget_arr) implicit none class (summa_bmi), intent(in) :: this integer, intent(in) :: do_nHRU character (len=*), intent(in) :: name - real, target, intent(out) :: targetarr(sum(gru_struc(:)%hruCount)) + real, target, intent(out) :: target_arr(sum(gru_struc(:)%hruCount)) + integer, target, intent(out) :: itarget_arr integer :: iGRU, jHRU, i summaVars: associate(& + timeStruct => this%model%summa1_struc(n)%timeStruct , & ! x%var(:) -- model time data forcStruct => this%model%summa1_struc(n)%forcStruct , & ! x%gru(:)%hru(:)%var(:) -- model forcing data progStruct => this%model%summa1_struc(n)%progStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model prognostic (state) variables diagStruct => this%model%summa1_struc(n)%diagStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model diagnostic variables fluxStruct => this%model%summa1_struc(n)%fluxStruct & ! x%gru(:)%hru(:)%var(:)%dat -- model fluxes ) - targetarr = -999.0 - do iGRU = 1, this%model%summa1_struc(n)%nGRU - do jHRU = 1, gru_struc(iGRU)%hruCount - i = (iGRU-1) * gru_struc(iGRU)%hruCount + jHRU - if (i > do_nHRU) return - select case (name) - ! input - case('atmosphere_water__precipitation_mass_flux') - targetarr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%pptrate) - case('land_surface_air__temperature') - targetarr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airtemp) - case('atmosphere_air_water~vapor__relative_saturation') - targetarr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%spechum) - case('land_surface_wind__speed') - targetarr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%windspd) -#ifdef NGEN_ACTIVE - case('land_surface_wind__x_component_of_velocity') - targetarr(i) = diagStruct%gru(iGRU)%hru(jHRU)%var(windspd_x)%dat(1) - case('land_surface_wind__y_component_of_velocity') - targetarr(i) = diagStruct%gru(iGRU)%hru(jHRU)%var(windspd_y)%dat(1) -#else - case('land_surface_wind__speed') - targetarr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%windspd) -#endif - case('land_surface_radiation~incoming~shortwave__energy_flux') - targetarr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%SWRadAtm) - case('land_surface_radiation~incoming~longwave__energy_flux') - targetarr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%LWRadAtm) - case('land_surface_air__pressure') - targetarr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airpres) - - ! output - case('land_surface_water__runoff_volume_flux') - targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarTotalRunoff)%dat(1) - case('land_surface_water__evaporation_mass_flux') - targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarGroundEvaporation)%dat(1) - case('land_vegetation_water__evaporation_mass_flux') - targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) - case('land_vegetation_water__transpiration_mass_flux') - targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) - case('snowpack__sublimation_mass_flux') - targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarSnowSublimation)%dat(1) - case('land_vegetation_water__sublimation_mass_flux') - targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopySublimation)%dat(1) - case('snowpack_mass') - targetarr(i) = progStruct%gru(iGRU)%hru(jHRU)%var(iLookPROG%scalarSWE)%dat(1) - case('soil_water__mass') - targetarr(i) = diagStruct%gru(iGRU)%hru(jHRU)%var(iLookDIAG%scalarTotalSoilWat)%dat(1) - case('land_vegetation_water__mass') - targetarr(i) = progStruct%gru(iGRU)%hru(jHRU)%var(iLookPROG%scalarCanopyWat)%dat(1) - case('land_surface_radiation~net~total__energy_flux') - targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarNetRadiation)%dat(1) - case('land_atmosphere_heat~net~latent__energy_flux') - targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarLatHeatTotal)%dat(1) - case('land_atmosphere_heat~net~sensible__energy_flux') - targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarSenHeatTotal)%dat(1) - case('atmosphere_energy~net~total__energy_flux') - targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1) - case('land_vegetation_energy~net~total__energy_flux') - targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1) - case('land_surface_energy~net~total__energy_flux') - targetarr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1) - end select + target_arr = -999.0 + itarget_arr = -999 + if(name(1:5)=='model')then + select case (name) + ! input + case('model_time_year') + itarget_arr = timeStruct%var(iLookTIME%iyyy) + case('model_time_month') + itarget_arr = timeStruct%var(iLookTIME%im) + case('model_time_day') + itarget_arr = timeStruct%var(iLookTIME%id) + case('model_time_hour') + itarget_arr = timeStruct%var(iLookTIME%ih) + case('model_time_hour') + itarget_arr = timeStruct%var(iLookTIME%imin) + end select + else + do iGRU = 1, this%model%summa1_struc(n)%nGRU + do jHRU = 1, gru_struc(iGRU)%hruCount + i = (iGRU-1) * gru_struc(iGRU)%hruCount + jHRU + if (i > do_nHRU) return + select case (name) + ! input + case('atmosphere_water__precipitation_mass_flux') + target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%pptrate) + case('land_surface_air__temperature') + target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airtemp) + case('atmosphere_air_water~vapor__relative_saturation') + target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%spechum) + case('land_surface_wind__speed') + target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%windspd) + case('land_surface_wind__x_component_of_velocity') + target_arr(i) = diagStruct%gru(iGRU)%hru(jHRU)%var(iLookDIAG%windspd_x)%dat(1) + case('land_surface_wind__y_component_of_velocity') + target_arr(i) = diagStruct%gru(iGRU)%hru(jHRU)%var(iLookDIAG%windspd_y)%dat(1) + case('land_surface_wind__speed') + target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%windspd) + case('land_surface_radiation~incoming~shortwave__energy_flux') + target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%SWRadAtm) + case('land_surface_radiation~incoming~longwave__energy_flux') + target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%LWRadAtm) + case('land_surface_air__pressure') + target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airpres) + + ! output + case('land_surface_water__runoff_volume_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarTotalRunoff)%dat(1) + case('land_surface_water__evaporation_mass_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarGroundEvaporation)%dat(1) + case('land_vegetation_water__evaporation_mass_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) + case('land_vegetation_water__transpiration_mass_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) + case('snowpack__sublimation_mass_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarSnowSublimation)%dat(1) + case('land_vegetation_water__sublimation_mass_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopySublimation)%dat(1) + case('snowpack_mass') + target_arr(i) = progStruct%gru(iGRU)%hru(jHRU)%var(iLookPROG%scalarSWE)%dat(1) + case('soil_water__mass') + target_arr(i) = diagStruct%gru(iGRU)%hru(jHRU)%var(iLookDIAG%scalarTotalSoilWat)%dat(1) + case('land_vegetation_water__mass') + target_arr(i) = progStruct%gru(iGRU)%hru(jHRU)%var(iLookPROG%scalarCanopyWat)%dat(1) + case('land_surface_radiation~net~total__energy_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarNetRadiation)%dat(1) + case('land_atmosphere_heat~net~latent__energy_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarLatHeatTotal)%dat(1) + case('land_atmosphere_heat~net~sensible__energy_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarSenHeatTotal)%dat(1) + case('atmosphere_energy~net~total__energy_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1) + case('land_vegetation_energy~net~total__energy_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1) + case('land_surface_energy~net~total__energy_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1) + end select + end do end do - end do + endif end associate summaVars end subroutine get_basin_field From 871a3eb0dec1af7dea321335c8453fa9ef7ffda1 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 5 Apr 2023 09:49:18 +0900 Subject: [PATCH 0653/1472] didn't have diag structure set right --- build/source/driver/summa_driver.f90 | 11 ++++++++--- build/source/dshare/get_ixname.f90 | 6 ++++-- build/source/dshare/popMetadat.f90 | 6 ++++-- build/source/dshare/var_lookup.f90 | 8 +++++--- build/source/engine/derivforce.f90 | 10 ++++++++++ 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index a21841609..319a81abb 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -54,6 +54,7 @@ module summa_driver USE globalData, only: gru_struc ! gru-hru mapping structures USE multiconst, only: secprday ! number of seconds in a day ! provide access to the named variables that describe elements of parent model structures + USE var_lookup, only: iLookTIME ! named variables for time data structure USE var_lookup, only: iLookATTR ! named variables for real valued attribute data structure USE var_lookup, only: iLookFORCE ! named variables for forcing data structure USE var_lookup, only: iLookFLUX ! named variables for local flux variables @@ -936,9 +937,13 @@ function summa_get_at_indices_int(this, name, dest, inds) result (bmi_status) call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file ! use the integer target src = c_loc(itarget_arr) - n_elements = sum(gru_struc(:)%hruCount) - call c_f_pointer(src, dest_ptr, [n_elements]) - bmi_status = BMI_SUCCESS end select + call c_f_pointer(src, src_flattened, [n_elements]) + n_elements = size(inds) + do i = 1, n_elements + dest(i) = src_flattened(inds(i)) + end do + bmi_status = BMI_SUCCESS + end select end function summa_get_at_indices_int ! Get values of a real variable at the given locations diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 482300649..12064e32d 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -512,6 +512,8 @@ function get_ixdiag(varName) case('scalarNewSnowDensity' ); get_ixdiag = iLookDIAG%scalarNewSnowDensity ! density of fresh snow, should snow be falling in this time step (kg m-3) case('scalarO2air' ); get_ixdiag = iLookDIAG%scalarO2air ! atmospheric o2 concentration (Pa) case('scalarCO2air' ); get_ixdiag = iLookDIAG%scalarCO2air ! atmospheric co2 concentration (Pa) + case('windspd_x' ); get_ixdiag = iLookDIAG%windspd_x ! wind speed at 10 meter height in x-direction (m s-1) + case('windspd_y' ); get_ixdiag = iLookDIAG%windspd_y ! wind speed at 10 meter height in y-direction (m s-1) ! shortwave radiation case('scalarCosZenith' ); get_ixdiag = iLookDIAG%scalarCosZenith ! cosine of the solar zenith angle (0-1) case('scalarFractionDirect' ); get_ixdiag = iLookDIAG%scalarFractionDirect ! fraction of direct radiation (0-1) @@ -560,8 +562,8 @@ function get_ixdiag(varName) case('scalarSoilControl' ); get_ixdiag = iLookDIAG%scalarSoilControl ! soil control on infiltration: 1=controlling; 0=not (-) case('mLayerVolFracAir' ); get_ixdiag = iLookDIAG%mLayerVolFracAir ! volumetric fraction of air in each layer (-) case('mLayerTcrit' ); get_ixdiag = iLookDIAG%mLayerTcrit ! critical soil temperature above which all water is unfrozen (K) - case('mLayerCompress' ); get_ixdiag = iLookDIAG%mLayerCompress ! change in volumetric water content due to compression of soil (-) - case('scalarSoilCompress' ); get_ixdiag = iLookDIAG%scalarSoilCompress ! change in total soil storage due to compression of the soil matrix (kg m-2) + case('mLayerCompress' ); get_ixdiag = iLookDIAG%mLayerCompress ! change in volumetric water content due to compression of soil (s-1) + case('scalarSoilCompress' ); get_ixdiag = iLookDIAG%scalarSoilCompress ! change in total soil storage due to compression of the soil matrix (kg m-2 s-1) case('mLayerMatricHeadLiq' ); get_ixdiag = iLookDIAG%mLayerMatricHeadLiq ! matric potential of liquid water (m) ! mass balance check case('scalarSoilWatBalError' ); get_ixdiag = iLookDIAG%scalarSoilWatBalError ! error in the total soil water balance (kg m-2) diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index ca684c256..e1b142ad3 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -375,6 +375,8 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%scalarNewSnowDensity) = var_info('scalarNewSnowDensity' , 'density of fresh snow' , 'kg m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarO2air) = var_info('scalarO2air' , 'atmospheric o2 concentration' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarCO2air) = var_info('scalarCO2air' , 'atmospheric co2 concentration' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%windspd_x) = var_info('windspd_x' , 'wind speed at 10 meter height in x-direction' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%windspd_y) = var_info('windspd_y' , 'wind speed at 10 meter height in y-direction' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! shortwave radiation diag_meta(iLookDIAG%scalarCosZenith) = var_info('scalarCosZenith' , 'cosine of the solar zenith angle' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarFractionDirect) = var_info('scalarFractionDirect' , 'fraction of direct radiation (0-1)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) @@ -423,8 +425,8 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%scalarSoilControl) = var_info('scalarSoilControl' , 'soil control on infiltration (1=controlling; 0=not)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%mLayerVolFracAir) = var_info('mLayerVolFracAir' , 'volumetric fraction of air in each layer' , '-' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%mLayerTcrit) = var_info('mLayerTcrit' , 'critical soil temperature above which all water is unfrozen' , 'K' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%mLayerCompress) = var_info('mLayerCompress' , 'change in volumetric water content due to compression of soil' , '-' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarSoilCompress) = var_info('scalarSoilCompress' , 'change in total soil storage due to compression of soil matrix' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%mLayerCompress) = var_info('mLayerCompress' , 'change in volumetric water content due to compression of soil' , 's-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarSoilCompress) = var_info('scalarSoilCompress' , 'change in total soil storage due to compression of soil matrix' , 'kg m-2 s-1 ' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%mLayerMatricHeadLiq) = var_info('mLayerMatricHeadLiq' , 'matric potential of liquid water' , 'm' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) ! mass balance check diag_meta(iLookDIAG%scalarSoilWatBalError) = var_info('scalarSoilWatBalError' , 'error in the total soil water balance' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 7a31ebd2f..49fb99706 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -389,6 +389,8 @@ MODULE var_lookup integer(i4b) :: scalarNewSnowDensity = integerMissing ! density of fresh snow (kg m-3) integer(i4b) :: scalarO2air = integerMissing ! atmospheric o2 concentration (Pa) integer(i4b) :: scalarCO2air = integerMissing ! atmospheric co2 concentration (Pa) + integer(i4b) :: windspd_x = integerMissing ! wind speed at 10 meter height in x-direction (m s-1) + integer(i4b) :: windspd_y = integerMissing ! wind speed at 10 meter height in y-direction (m s-1) ! shortwave radiation integer(i4b) :: scalarCosZenith = integerMissing ! cosine of the solar zenith angle (0-1) integer(i4b) :: scalarFractionDirect = integerMissing ! fraction of direct radiation (0-1) @@ -437,8 +439,8 @@ MODULE var_lookup integer(i4b) :: scalarSoilControl = integerMissing ! soil control on infiltration: 1=controlling; 0=not (-) integer(i4b) :: mLayerVolFracAir = integerMissing ! volumetric fraction of air in each layer (-) integer(i4b) :: mLayerTcrit = integerMissing ! critical soil temperature above which all water is unfrozen (K) - integer(i4b) :: mLayerCompress = integerMissing ! change in volumetric water content due to compression of soil (-) - integer(i4b) :: scalarSoilCompress = integerMissing ! change in total soil storage due to compression of the soil matrix (kg m-2) + integer(i4b) :: mLayerCompress = integerMissing ! change in volumetric water content due to compression of soil (s-1) + integer(i4b) :: scalarSoilCompress = integerMissing ! change in total soil storage due to compression of the soil matrix (kg m-2 s-1) integer(i4b) :: mLayerMatricHeadLiq = integerMissing ! matric potential of liquid water (m) ! mass balance check integer(i4b) :: scalarSoilWatBalError = integerMissing ! error in the total soil water balance (kg m-2) @@ -867,7 +869,7 @@ MODULE var_lookup 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,& 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,& 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,& - 91, 92, 93, 94, 95, 96) + 91, 92, 93, 94, 95, 96, 97, 98) ! named variables: model fluxes type(iLook_flux), public,parameter :: iLookFLUX =iLook_flux ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& diff --git a/build/source/engine/derivforce.f90 b/build/source/engine/derivforce.f90 index e3ed9dc48..abdefec42 100644 --- a/build/source/engine/derivforce.f90 +++ b/build/source/engine/derivforce.f90 @@ -162,6 +162,8 @@ subroutine derivforce(time_data,forc_data,attr_data,mpar_data,prog_data,diag_dat ! derived model forcing data scalarO2air => diag_data%var(iLookDIAG%scalarO2air)%dat(1) , & ! atmospheric o2 concentration (Pa) scalarCO2air => diag_data%var(iLookDIAG%scalarCO2air)%dat(1) , & ! atmospheric co2 concentration (Pa) + windspd_x => diag_data%var(iLookDIAG%windspd_x)%dat(1) , & ! wind speed at 10 meter height in x-direction (m s-1) + windspd_y => diag_data%var(iLookDIAG%windspd_y)%dat(1) , & ! wind speed at 10 meter height in y-direction (m s-1) ! radiation variables scalarFractionDirect => diag_data%var(iLookDIAG%scalarFractionDirect)%dat(1) , & ! fraction of direct radiation (0-1) spectralIncomingDirect => flux_data%var(iLookFLUX%spectralIncomingDirect)%dat , & ! downwelling direct shortwave radiation for each waveband (W m-2) @@ -178,6 +180,14 @@ subroutine derivforce(time_data,forc_data,attr_data,mpar_data,prog_data,diag_dat ! initialize error control err=0; message="derivforce/" + ! NGEN wants the wind inputted as two components, if not inputting NGEN forcing let the y direction be 0 +#ifndef NGEN_FORCING_ACTIVE + windspd = sqrt(windspd_x**2._rkind + windspd_y**2._rkind) +#else + windspd_x = windspd + windspd_y = 0._rkind +#endif + ! check spectral dimension if(size(spectralIncomingDirect) /= nBands .or. size(spectralIncomingDiffuse) /= nBands)then write(message,'(a,i0,a)') trim(message)//'expect ', nBands, 'spectral classes for radiation' From f11b0c0a22b543936835c797a857c8f1498b2fb0 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 5 Apr 2023 10:32:49 +0900 Subject: [PATCH 0654/1472] variable name --- build/source/driver/summa_driver.f90 | 32 ++++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 319a81abb..e58e85b7d 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -260,7 +260,7 @@ end function summa_update ! **************************************************************************** function summa_update_until(this, time) result (bmi_status) class (summa_bmi), intent(inout) :: this - double precision, intent(in) :: time ! unit days (julian days, same as get_model_time) + double precision, intent(in) :: time ! unit days (julian days, same as get_model__time) integer :: bmi_status, istat, n_steps, i double precision :: current @@ -731,11 +731,11 @@ function summa_var_units(this, name, units) result (bmi_status) case('land_surface_wind__y_component_of_velocity') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS #else case('land_surface_wind__speed') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS - case('model_time_year') ; units = '' ; bmi_status = BMI_SUCCESS - case('model_time_month') ; units = '' ; bmi_status = BMI_SUCCESS - case('model_time_day') ; units = '' ; bmi_status = BMI_SUCCESS - case('model_time_hour') ; units = '' ; bmi_status = BMI_SUCCESS - case('model_time_min') ; units = '' ; bmi_status = BMI_SUCCESS + case('model__time_year') ; units = '' ; bmi_status = BMI_SUCCESS + case('model__time_month') ; units = '' ; bmi_status = BMI_SUCCESS + case('model__time_day') ; units = '' ; bmi_status = BMI_SUCCESS + case('model__time_hour') ; units = '' ; bmi_status = BMI_SUCCESS + case('model__time_min') ; units = '' ; bmi_status = BMI_SUCCESS #endif case('land_surface_radiation~incoming~shortwave__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS case('land_surface_radiation~incoming~longwave__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS @@ -1131,15 +1131,15 @@ subroutine assign_basin_field(this, name, src_arr, isrc_arr) if(name(1:5)=='model')then select case (name) ! input - case('model_time_year') + case('model__time_year') timeStruct%var(iLookTIME%iyyy) = isrc_arr - case('model_time_month') + case('model__time_month') timeStruct%var(iLookTIME%im) = isrc_arr - case('model_time_day') + case('model__time_day') timeStruct%var(iLookTIME%id) = isrc_arr - case('model_time_hour') + case('model__time_hour') timeStruct%var(iLookTIME%ih) = isrc_arr - case('model_time_hour') + case('model__time_min') timeStruct%var(iLookTIME%imin) = isrc_arr end select else @@ -1195,15 +1195,15 @@ subroutine get_basin_field(this, name, do_nHRU, target_arr, itarget_arr) if(name(1:5)=='model')then select case (name) ! input - case('model_time_year') + case('model__time_year') itarget_arr = timeStruct%var(iLookTIME%iyyy) - case('model_time_month') + case('model__time_month') itarget_arr = timeStruct%var(iLookTIME%im) - case('model_time_day') + case('model__time_day') itarget_arr = timeStruct%var(iLookTIME%id) - case('model_time_hour') + case('model__time_hour') itarget_arr = timeStruct%var(iLookTIME%ih) - case('model_time_hour') + case('model__time_min') itarget_arr = timeStruct%var(iLookTIME%imin) end select else From bbf58f237a917e64286d3ac3285b115c1ccf534a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 5 Apr 2023 14:11:41 +0900 Subject: [PATCH 0655/1472] fix linking and semantics errors --- build/cmake_ngen/CMakeLists.txt | 7 +++++++ build/cmake_ngen/build_ngen.cluster.bash | 2 -- build/source/driver/summa_driver.f90 | 20 +++++++++---------- ...xample_realization_config_w_summa_bmi.json | 3 ++- ...e_realization_config_w_summa_bmi__mac.json | 3 ++- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/build/cmake_ngen/CMakeLists.txt b/build/cmake_ngen/CMakeLists.txt index 80684e0a0..3b23a3df6 100644 --- a/build/cmake_ngen/CMakeLists.txt +++ b/build/cmake_ngen/CMakeLists.txt @@ -53,6 +53,13 @@ else() endif() +# MKL needs BLA_VENDOR set with cmake +if(CMAKE_BUILD_TYPE MATCHES Cluster OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) + set(BLA_VENDOR OpenBLAS) +else() + set(BLA_VENDOR OpenBLAS) +endif() + # Packages that are required find_package(netCDF REQUIRED) find_package(LAPACK REQUIRED) diff --git a/build/cmake_ngen/build_ngen.cluster.bash b/build/cmake_ngen/build_ngen.cluster.bash index 77e02250d..513939845 100644 --- a/build/cmake_ngen/build_ngen.cluster.bash +++ b/build/cmake_ngen/build_ngen.cluster.bash @@ -2,10 +2,8 @@ # build nextgen on Copernicus, from ngen directory put this one directory up and run this as ../example_build_ngen.bash -#module load gnu module load boost module load udunits/2.2.28 -#module load python/3.7.5 module load StdEnv/2020 module load gcc/9.3.0 module load openblas/0.3.17 diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index e58e85b7d..641cdd10e 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -722,8 +722,8 @@ function summa_var_units(this, name, units) result (bmi_status) integer :: bmi_status select case (name) - ! input - case('atmosphere_water__precipitation_mass_flux') ; units = 'kg m-2 s-1'; bmi_status = BMI_SUCCESS + ! input, note using the NGEN preferred unit definitions, equivalent to standard SUMMA definitions as noted + case('atmosphere_water__precipitation_mass_flux') ; units = 'mm s-1'; ; bmi_status = BMI_SUCCESS !equivalent kg m-2 s-1 case('land_surface_air__temperature') ; units = 'K' ; bmi_status = BMI_SUCCESS case('atmosphere_air_water~vapor__relative_saturation') ; units = 'kg kg-1' ; bmi_status = BMI_SUCCESS #ifdef NGEN_ACTIVE @@ -739,15 +739,15 @@ function summa_var_units(this, name, units) result (bmi_status) #endif case('land_surface_radiation~incoming~shortwave__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS case('land_surface_radiation~incoming~longwave__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS - case('land_surface_air__pressure') ; units = 'kg m−1 s−2'; bmi_status = BMI_SUCCESS + case('land_surface_air__pressure') ; units = 'kg m-1 s-2'; bmi_status = BMI_SUCCESS ! output case('land_surface_water__runoff_volume_flux') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS - case('land_surface_water__evaporation_mass_flux') ; units = 'kg m-2 s-1'; bmi_status = BMI_SUCCESS - case('land_vegetation_water__evaporation_mass_flux') ; units = 'kg m-2 s-1'; bmi_status = BMI_SUCCESS - case('land_vegetation_water__transpiration_mass_flux'); units = 'kg m-2 s-1'; bmi_status = BMI_SUCCESS - case('snowpack__sublimation_mass_flux') ; units = 'kg m-2 s-1'; bmi_status = BMI_SUCCESS - case('land_vegetation_water__sublimation_mass_flux') ; units = 'kg m-2 s-1'; bmi_status = BMI_SUCCESS + case('land_surface_water__evaporation_mass_flux') ; units = 'mm s-1' ; bmi_status = BMI_SUCCESS !equivalent kg m-2 s-1 + case('land_vegetation_water__evaporation_mass_flux') ; units = 'mm s-1' ; bmi_status = BMI_SUCCESS !equivalent kg m-2 s-1 + case('land_vegetation_water__transpiration_mass_flux'); units = 'mm s-1' ; bmi_status = BMI_SUCCESS !equivalent kg m-2 s-1 + case('snowpack__sublimation_mass_flux') ; units = 'mm s-1' ; bmi_status = BMI_SUCCESS !equivalent kg m-2 s-1 + case('land_vegetation_water__sublimation_mass_flux') ; units = 'mm s-1' ; bmi_status = BMI_SUCCESS !equivalent kg m-2 s-1 case('snowpack_mass') ; units = 'kg m-2' ; bmi_status = BMI_SUCCESS case('soil_water__mass') ; units = 'kg m-2' ; bmi_status = BMI_SUCCESS case('land_vegetation_water__mass') ; units = 'kg m-2' ; bmi_status = BMI_SUCCESS @@ -999,7 +999,7 @@ function summa_set_int(this, name, src) result (bmi_status) select case(name) case default rsrc = -999.0 - call assign_basin_field(this, name, rsrc, src) ! See near bottom of file + call assign_basin_field(this, name, rsrc, src(1)) ! See near bottom of file bmi_status = BMI_SUCCESS end select end function summa_set_int @@ -1219,8 +1219,6 @@ subroutine get_basin_field(this, name, do_nHRU, target_arr, itarget_arr) target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airtemp) case('atmosphere_air_water~vapor__relative_saturation') target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%spechum) - case('land_surface_wind__speed') - target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%windspd) case('land_surface_wind__x_component_of_velocity') target_arr(i) = diagStruct%gru(iGRU)%hru(jHRU)%var(iLookDIAG%windspd_x)%dat(1) case('land_surface_wind__y_component_of_velocity') diff --git a/test_ngen/example_realization_config_w_summa_bmi.json b/test_ngen/example_realization_config_w_summa_bmi.json index bc0eb911e..5e28da0ec 100644 --- a/test_ngen/example_realization_config_w_summa_bmi.json +++ b/test_ngen/example_realization_config_w_summa_bmi.json @@ -18,7 +18,8 @@ "land_surface_wind__y_component_of_velocity": "VGRD_10maboveground", "land_surface_radiation~incoming~shortwave__energy_flux": "DSWRF_surface", "land_surface_radiation~incoming~longwave__energy_flux": "DLWRF_surface", - "land_surface_air__pressure": "PRES_surface", + "land_surface_air__pressure": "PRES_surface" + }, "uses_forcing_file": false } } diff --git a/test_ngen/example_realization_config_w_summa_bmi__mac.json b/test_ngen/example_realization_config_w_summa_bmi__mac.json index 80aca3bef..c00aaa692 100644 --- a/test_ngen/example_realization_config_w_summa_bmi__mac.json +++ b/test_ngen/example_realization_config_w_summa_bmi__mac.json @@ -18,7 +18,8 @@ "land_surface_wind__y_component_of_velocity": "VGRD_10maboveground", "land_surface_radiation~incoming~shortwave__energy_flux": "DSWRF_surface", "land_surface_radiation~incoming~longwave__energy_flux": "DLWRF_surface", - "land_surface_air__pressure": "PRES_surface", + "land_surface_air__pressure": "PRES_surface" + }, "uses_forcing_file": false } } From 2173c60760cd9eb32d786fbe8ab0539a86682c77 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 5 Apr 2023 15:31:24 +0900 Subject: [PATCH 0656/1472] add test files for forcing --- build/source/driver/summa_driver.f90 | 1 + .../input_data/celia1990/celia_ngen.csv | 721 ++++++++++++++++++ ...xample_realization_config_w_summa_bmi.json | 8 +- ...e_realization_config_w_summa_bmi__mac.json | 8 +- test_ngen/example_run_celia.sh | 2 +- ....input => summa-init-ceiia.namelist.input} | 0 6 files changed, 731 insertions(+), 9 deletions(-) create mode 100644 test_ngen/celia_test/input_data/celia1990/celia_ngen.csv rename test_ngen/{summa-init-cat-27.namelist.input => summa-init-ceiia.namelist.input} (100%) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 641cdd10e..276b106cb 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -323,6 +323,7 @@ function summa_output_item_count(this, count) result (bmi_status) end function summa_output_item_count ! List output variables standardized as "https://csdms.colorado.edu/wiki/CSDMS_Standard_Names" + ! These are the inputs we will need if we do not want to call read_force inside summa_forcing.f90 ! NGEN uses two component wind and a time vector that is not currently separable ! (compute wind speed from the two components and time from start time and hourly step assumption) function summa_input_var_names(this, names) result (bmi_status) diff --git a/test_ngen/celia_test/input_data/celia1990/celia_ngen.csv b/test_ngen/celia_test/input_data/celia1990/celia_ngen.csv new file mode 100644 index 000000000..5617c6ae8 --- /dev/null +++ b/test_ngen/celia_test/input_data/celia1990/celia_ngen.csv @@ -0,0 +1,721 @@ +time,APCP_surface,DLWRF_surface,DSWRF_surface,PRES_surface,SPFH_2maboveground,TMP_2maboveground,UGRD_10maboveground,VGRD_10maboveground,precip_rate +2000-01-01 00:00:00,0.0,361.3000183105469,0.0,100310.0,0.00930000003427267,285.8000183105469,-2.299999952316284,0.10000000149011612,0.0 +2000-01-01 01:00:00,0.0,361.3000183105469,0.0,100320.0,0.008799999952316284,285.3000183105469,-2.299999952316284,-0.30000001192092896,0.0 +2000-01-01 02:00:00,0.0,361.3000183105469,0.0,100340.0,0.008299999870359898,284.8000183105469,-2.4000000953674316,-0.6000000238418579,0.0 +2000-01-01 03:00:00,0.0,356.0,0.0,100390.0,0.007999999448657036,283.8999938964844,-2.4000000953674316,-0.9000000357627869,0.0 +2000-01-01 04:00:00,0.0,356.0,0.0,100370.0,0.007799999788403511,283.20001220703125,-2.200000047683716,-0.20000000298023224,0.0 +2000-01-01 05:00:00,0.0,356.0,0.0,100280.0,0.007499999832361937,282.8000183105469,-2.0,0.4000000059604645,0.0 +2000-01-01 06:00:00,0.0,349.1000061035156,0.0,100320.0,0.007199999876320362,282.6000061035156,-1.8000000715255737,1.100000023841858,0.0 +2000-01-01 07:00:00,0.0,349.1000061035156,0.0,100230.0,0.0071000000461936,282.20001220703125,-2.0,0.800000011920929,0.0 +2000-01-01 08:00:00,0.0,349.1000061035156,0.0,100230.0,0.006899999920278788,282.0,-2.1000001430511475,0.5,0.0 +2000-01-01 09:00:00,0.0,341.8999938964844,0.0,100250.0,0.00699999975040555,281.8999938964844,-2.299999952316284,0.30000001192092896,0.0 +2000-01-01 10:00:00,0.0,341.8999938964844,0.0,100200.0,0.006899999920278788,281.8000183105469,-2.299999952316284,0.6000000238418579,0.0 +2000-01-01 11:00:00,0.0,341.8999938964844,0.0,100200.0,0.006799999624490738,281.8000183105469,-2.299999952316284,1.0,0.0 +2000-01-01 12:00:00,0.0,353.70001220703125,0.0,100240.0,0.006899999920278788,281.70001220703125,-2.299999952316284,1.3000000715255737,0.0 +2000-01-01 13:00:00,0.0,353.70001220703125,81.30000305175781,100280.0,0.006899999920278788,281.8999938964844,-2.4000000953674316,1.3000000715255737,0.0 +2000-01-01 14:00:00,0.0,353.70001220703125,184.8000030517578,100330.0,0.00699999975040555,282.1000061035156,-2.5,1.3000000715255737,0.0 +2000-01-01 15:00:00,0.0,391.20001220703125,227.1999969482422,100350.0,0.0071000000461936,282.1000061035156,-2.6000001430511475,1.2000000476837158,0.0 +2000-01-01 16:00:00,0.0,391.20001220703125,275.6000061035156,100270.0,0.007199999876320362,282.70001220703125,-1.899999976158142,1.899999976158142,0.0 +2000-01-01 17:00:00,0.0,391.20001220703125,287.8000183105469,100240.0,0.007499999832361937,282.70001220703125,-1.3000000715255737,2.6000001430511475,0.0 +2000-01-01 18:00:00,0.0,392.3999938964844,342.8999938964844,100130.0,0.007799999788403511,283.3000183105469,-0.6000000238418579,3.200000047683716,0.0 +2000-01-01 19:00:00,0.0,392.3999938964844,309.3000183105469,100030.0,0.008299999870359898,284.20001220703125,-0.699999988079071,3.200000047683716,0.0 +2000-01-01 20:00:00,0.0,392.3999938964844,232.90000915527344,100040.0,0.00839999970048666,284.8999938964844,-0.800000011920929,3.1000001430511475,0.0 +2000-01-01 21:00:00,0.0,390.1000061035156,109.0999984741211,99930.0,0.008599999360740185,284.8000183105469,-0.9000000357627869,3.0,0.0 +2000-01-01 22:00:00,0.0,390.1000061035156,16.80000114440918,99940.0,0.008799999952316284,285.0,-1.2000000476837158,2.6000001430511475,0.0 +2000-01-01 23:00:00,0.0,390.1000061035156,0.0,100000.0,0.008899999782443047,285.0,-1.5,2.299999952316284,0.0 +2000-01-02 00:00:00,0.0,387.6000061035156,0.0,99990.0,0.008899999782443047,285.0,-1.8000000715255737,1.899999976158142,0.0 +2000-01-02 01:00:00,0.0,387.6000061035156,0.0,99940.0,0.009099999442696571,285.3999938964844,-1.7000000476837158,1.7000000476837158,0.0 +2000-01-02 02:00:00,0.0,387.6000061035156,0.0,99900.0,0.008899999782443047,285.5,-1.600000023841858,1.5,0.0 +2000-01-02 03:00:00,0.0,369.20001220703125,0.0,99880.0,0.008999999612569809,285.6000061035156,-1.600000023841858,1.3000000715255737,0.0 +2000-01-02 04:00:00,0.0,369.20001220703125,0.0,99800.0,0.009200000204145908,285.6000061035156,-1.600000023841858,1.899999976158142,0.0 +2000-01-02 05:00:00,0.0,369.20001220703125,0.0,99810.0,0.008999999612569809,285.20001220703125,-1.600000023841858,2.6000001430511475,0.0 +2000-01-02 06:00:00,0.0,366.3999938964844,0.0,99770.0,0.009099999442696571,285.3999938964844,-1.600000023841858,3.200000047683716,0.0 +2000-01-02 07:00:00,0.0,366.3999938964844,0.0,99600.0,0.00930000003427267,286.0,-1.600000023841858,3.4000000953674316,0.0 +2000-01-02 08:00:00,0.0,366.3999938964844,0.0,99550.0,0.009800000116229057,286.8999938964844,-1.7000000476837158,3.700000047683716,0.0 +2000-01-02 09:00:00,0.0,369.70001220703125,0.0,99500.0,0.00989999994635582,287.0,-1.8000000715255737,3.9000000953674316,0.0 +2000-01-02 10:00:00,0.0,369.70001220703125,0.0,99440.0,0.010499999858438969,287.6000061035156,-1.600000023841858,4.0,0.0 +2000-01-02 11:00:00,0.0,369.70001220703125,0.0,99430.0,0.010900000110268593,288.20001220703125,-1.5,4.0,0.0 +2000-01-02 12:00:00,0.0,376.5,0.0,99440.0,0.01119999960064888,288.70001220703125,-1.399999976158142,4.099999904632568,0.0 +2000-01-02 13:00:00,0.0,376.5,76.30000305175781,99440.0,0.011399999260902405,289.3999938964844,-0.800000011920929,4.0,0.0 +2000-01-02 14:00:00,0.0,376.5,175.60000610351562,99400.0,0.011899999342858791,290.0,-0.20000000298023224,4.0,2.780978003916007e-08 +2000-01-02 15:00:00,0.10000000149011612,398.0,117.0999984741211,99550.0,0.012299999594688416,290.6000061035156,0.30000001192092896,3.9000000953674316,1.9468860507876914e-07 +2000-01-02 16:00:00,0.699999988079071,398.0,142.3000030517578,99530.0,0.012199999764561653,290.3999938964844,1.0,4.0,1.1124674758866803e-07 +2000-01-02 17:00:00,0.4000000059604645,397.8999938964844,148.6999969482422,99360.0,0.012299999594688416,290.1000061035156,1.8000000715255737,4.200000286102295,2.781025218983742e-08 +2000-01-02 18:00:00,0.10000000149011612,398.8999938964844,132.10000610351562,99260.0,0.012000000104308128,290.8000183105469,2.5,4.300000190734863,0.0 +2000-01-02 19:00:00,0.0,398.8999938964844,119.20000457763672,99210.0,0.011699999682605267,289.8999938964844,3.0,4.200000286102295,0.0 +2000-01-02 20:00:00,0.0,398.8999938964844,89.70000457763672,99070.0,0.011899999342858791,290.1000061035156,3.5,4.099999904632568,0.0 +2000-01-02 21:00:00,0.0,377.8000183105469,56.900001525878906,99120.0,0.011799999512732029,289.8000183105469,4.099999904632568,4.0,0.0 +2000-01-02 22:00:00,0.0,377.8000183105469,8.699999809265137,99130.0,0.011500000022351742,289.5,4.5,4.099999904632568,0.0 +2000-01-02 23:00:00,0.0,377.8000183105469,0.0,99180.0,0.010900000110268593,289.3999938964844,5.0,4.300000190734863,0.0 +2000-01-03 00:00:00,0.0,352.0,0.0,99280.0,0.008700000122189522,288.0,5.400000095367432,4.400000095367432,0.0 +2000-01-03 01:00:00,0.0,352.0,0.0,99240.0,0.007499999832361937,286.3999938964844,5.200000286102295,4.0,0.0 +2000-01-03 02:00:00,0.0,352.0,0.0,99320.0,0.0071000000461936,285.20001220703125,4.900000095367432,3.6000001430511475,0.0 +2000-01-03 03:00:00,0.0,297.5,0.0,99430.0,0.006599999964237213,283.8999938964844,4.599999904632568,3.299999952316284,0.0 +2000-01-03 04:00:00,0.0,297.5,0.0,99440.0,0.0064999996684491634,283.0,4.5,2.4000000953674316,0.0 +2000-01-03 05:00:00,0.0,297.5,0.0,99570.0,0.006599999964237213,282.8999938964844,4.400000095367432,1.5,0.0 +2000-01-03 06:00:00,0.0,279.70001220703125,0.0,99560.0,0.0064999996684491634,282.3999938964844,4.300000190734863,0.699999988079071,0.0 +2000-01-03 07:00:00,0.0,279.70001220703125,0.0,99600.0,0.006599999964237213,281.70001220703125,3.9000000953674316,-0.20000000298023224,0.0 +2000-01-03 08:00:00,0.0,279.70001220703125,0.0,99700.0,0.0064999996684491634,281.3999938964844,3.4000000953674316,-1.0,0.0 +2000-01-03 09:00:00,0.0,256.70001220703125,0.0,99760.0,0.006300000008195639,281.1000061035156,2.9000000953674316,-1.899999976158142,0.0 +2000-01-03 10:00:00,0.0,256.70001220703125,0.0,99830.0,0.006199999712407589,280.70001220703125,2.299999952316284,-2.299999952316284,0.0 +2000-01-03 11:00:00,0.0,256.70001220703125,0.0,99940.0,0.0058999997563660145,280.6000061035156,1.600000023841858,-2.799999952316284,0.0 +2000-01-03 12:00:00,0.0,252.3000030517578,0.0,100080.0,0.0050999997183680534,278.70001220703125,0.9000000357627869,-3.299999952316284,0.0 +2000-01-03 13:00:00,0.0,252.3000030517578,110.5999984741211,100110.0,0.0050999997183680534,278.8999938964844,0.20000000298023224,-4.200000286102295,0.0 +2000-01-03 14:00:00,0.0,252.3000030517578,258.1000061035156,100240.0,0.004399999976158142,280.8999938964844,-0.5,-5.099999904632568,0.0 +2000-01-03 15:00:00,0.0,262.1000061035156,384.0,100410.0,0.0038999998942017555,282.20001220703125,-1.2000000476837158,-5.900000095367432,0.0 +2000-01-03 16:00:00,0.0,262.1000061035156,467.3999938964844,100430.0,0.003599999938160181,282.8999938964844,-1.399999976158142,-5.599999904632568,0.0 +2000-01-03 17:00:00,0.0,262.1000061035156,489.0,100410.0,0.003700000001117587,283.5,-1.7000000476837158,-5.300000190734863,0.0 +2000-01-03 18:00:00,0.0,265.3999938964844,483.20001220703125,100380.0,0.003499999875202775,283.5,-2.0,-4.900000095367432,0.0 +2000-01-03 19:00:00,0.0,265.3999938964844,436.1000061035156,100350.0,0.003499999875202775,283.8999938964844,-1.7000000476837158,-4.700000286102295,0.0 +2000-01-03 20:00:00,0.0,265.3999938964844,328.3999938964844,100450.0,0.003599999938160181,283.3000183105469,-1.399999976158142,-4.400000095367432,0.0 +2000-01-03 21:00:00,0.0,251.8000030517578,175.40000915527344,100500.0,0.0037999998312443495,282.70001220703125,-1.100000023841858,-4.200000286102295,0.0 +2000-01-03 22:00:00,0.0,251.8000030517578,26.399999618530273,100580.0,0.004100000020116568,281.1000061035156,-1.2000000476837158,-3.700000047683716,0.0 +2000-01-03 23:00:00,0.0,251.8000030517578,0.0,100660.0,0.00419999985024333,279.8999938964844,-1.2000000476837158,-3.299999952316284,0.0 +2000-01-04 00:00:00,0.0,233.5,0.0,100760.0,0.00419999985024333,279.6000061035156,-1.2000000476837158,-2.9000000953674316,0.0 +2000-01-04 01:00:00,0.0,233.5,0.0,100780.0,0.00419999985024333,278.20001220703125,-1.399999976158142,-2.9000000953674316,0.0 +2000-01-04 02:00:00,0.0,233.5,0.0,100870.0,0.00419999985024333,278.1000061035156,-1.600000023841858,-2.9000000953674316,0.0 +2000-01-04 03:00:00,0.0,227.60000610351562,0.0,101010.0,0.003999999724328518,278.3999938964844,-1.7000000476837158,-2.799999952316284,0.0 +2000-01-04 04:00:00,0.0,227.60000610351562,0.0,101020.0,0.003999999724328518,277.8999938964844,-1.7000000476837158,-2.799999952316284,0.0 +2000-01-04 05:00:00,0.0,227.60000610351562,0.0,101000.0,0.003700000001117587,277.8000183105469,-1.7000000476837158,-2.799999952316284,0.0 +2000-01-04 06:00:00,0.0,220.90000915527344,0.0,101060.0,0.003499999875202775,277.6000061035156,-1.8000000715255737,-2.799999952316284,0.0 +2000-01-04 07:00:00,0.0,220.90000915527344,0.0,101060.0,0.003499999875202775,277.70001220703125,-1.399999976158142,-2.9000000953674316,0.0 +2000-01-04 08:00:00,0.0,220.90000915527344,0.0,101160.0,0.003499999875202775,276.3999938964844,-1.100000023841858,-3.0,0.0 +2000-01-04 09:00:00,0.0,216.3000030517578,0.0,101210.0,0.003399999812245369,275.8000183105469,-0.699999988079071,-3.1000001430511475,0.0 +2000-01-04 10:00:00,0.0,216.3000030517578,0.0,101210.0,0.0032999999821186066,274.70001220703125,-0.9000000357627869,-3.0,0.0 +2000-01-04 11:00:00,0.0,216.3000030517578,0.0,101300.0,0.0032999999821186066,274.3000183105469,-1.100000023841858,-2.799999952316284,0.0 +2000-01-04 12:00:00,0.0,219.3000030517578,0.0,101410.0,0.0032999999821186066,273.8999938964844,-1.3000000715255737,-2.700000047683716,0.0 +2000-01-04 13:00:00,0.0,219.3000030517578,109.5999984741211,101410.0,0.003399999812245369,274.6000061035156,-1.5,-3.0,0.0 +2000-01-04 14:00:00,0.0,219.1999969482422,259.1000061035156,101470.0,0.003599999938160181,277.6000061035156,-1.8000000715255737,-3.4000000953674316,0.0 +2000-01-04 15:00:00,0.0,237.6999969482422,413.8000183105469,101550.0,0.003599999938160181,280.0,-2.0,-3.700000047683716,0.0 +2000-01-04 16:00:00,0.0,237.6999969482422,504.3000183105469,101540.0,0.003700000001117587,281.70001220703125,-2.5,-3.799999952316284,0.0 +2000-01-04 17:00:00,0.0,237.6999969482422,528.1000366210938,101480.0,0.003499999875202775,283.3999938964844,-3.0,-3.9000000953674316,0.0 +2000-01-04 18:00:00,0.0,246.0,508.20001220703125,101470.0,0.003499999875202775,284.20001220703125,-3.5,-3.9000000953674316,0.0 +2000-01-04 19:00:00,0.0,246.0,458.8999938964844,101360.0,0.003499999875202775,285.0,-3.299999952316284,-3.799999952316284,0.0 +2000-01-04 20:00:00,0.0,246.0,345.6000061035156,101310.0,0.0032999999821186066,285.1000061035156,-3.1000001430511475,-3.6000001430511475,0.0 +2000-01-04 21:00:00,0.0,237.1999969482422,148.5,101360.0,0.0031999999191612005,284.8999938964844,-2.9000000953674316,-3.4000000953674316,0.0 +2000-01-04 22:00:00,0.0,237.1999969482422,22.200000762939453,101390.0,0.003499999875202775,282.8000183105469,-2.0,-3.5,0.0 +2000-01-04 23:00:00,0.0,237.1999969482422,0.0,101420.0,0.003700000001117587,279.5,-1.2000000476837158,-3.5,0.0 +2000-01-05 00:00:00,0.0,225.6999969482422,0.0,101480.0,0.003700000001117587,278.1000061035156,-0.30000001192092896,-3.5,0.0 +2000-01-05 01:00:00,0.0,225.6999969482422,0.0,101440.0,0.003599999938160181,276.70001220703125,-0.699999988079071,-3.4000000953674316,0.0 +2000-01-05 02:00:00,0.0,225.6999969482422,0.0,101550.0,0.003599999938160181,275.8999938964844,-1.0,-3.200000047683716,0.0 +2000-01-05 03:00:00,0.0,222.1999969482422,0.0,101630.0,0.003599999938160181,275.70001220703125,-1.399999976158142,-3.1000001430511475,0.0 +2000-01-05 04:00:00,0.0,222.1999969482422,0.0,101590.0,0.003499999875202775,275.0,-1.399999976158142,-3.1000001430511475,0.0 +2000-01-05 05:00:00,0.0,222.1999969482422,0.0,101630.0,0.003499999875202775,274.3000183105469,-1.399999976158142,-3.1000001430511475,0.0 +2000-01-05 06:00:00,0.0,219.60000610351562,0.0,101590.0,0.003499999875202775,274.3000183105469,-1.3000000715255737,-3.200000047683716,0.0 +2000-01-05 07:00:00,0.0,219.60000610351562,0.0,101600.0,0.003499999875202775,274.20001220703125,-1.3000000715255737,-3.1000001430511475,0.0 +2000-01-05 08:00:00,0.0,219.60000610351562,0.0,101700.0,0.003399999812245369,273.3000183105469,-1.2000000476837158,-3.1000001430511475,0.0 +2000-01-05 09:00:00,0.0,220.0,0.0,101720.0,0.003499999875202775,273.5,-1.2000000476837158,-3.0,0.0 +2000-01-05 10:00:00,0.0,220.0,0.0,101760.0,0.003399999812245369,273.6000061035156,-1.3000000715255737,-2.9000000953674316,0.0 +2000-01-05 11:00:00,0.0,220.0,0.0,101780.0,0.003499999875202775,273.8999938964844,-1.399999976158142,-2.700000047683716,0.0 +2000-01-05 12:00:00,0.0,225.6999969482422,0.0,101840.0,0.003399999812245369,273.3999938964844,-1.399999976158142,-2.5,0.0 +2000-01-05 13:00:00,0.0,225.6999969482422,105.80000305175781,101840.0,0.003599999938160181,274.20001220703125,-1.7000000476837158,-2.9000000953674316,0.0 +2000-01-05 14:00:00,0.0,225.6999969482422,253.5,101890.0,0.003700000001117587,278.20001220703125,-2.0,-3.299999952316284,0.0 +2000-01-05 15:00:00,0.0,251.10000610351562,404.3999938964844,101910.0,0.003700000001117587,280.8000183105469,-2.299999952316284,-3.700000047683716,0.0 +2000-01-05 16:00:00,0.0,251.10000610351562,493.70001220703125,101920.0,0.003700000001117587,282.8999938964844,-2.5,-3.700000047683716,0.0 +2000-01-05 17:00:00,0.0,251.10000610351562,517.4000244140625,101830.0,0.003700000001117587,284.6000061035156,-2.799999952316284,-3.700000047683716,0.0 +2000-01-05 18:00:00,0.0,266.8000183105469,495.1000061035156,101720.0,0.003499999875202775,285.70001220703125,-3.0,-3.799999952316284,0.0 +2000-01-05 19:00:00,0.0,266.8000183105469,447.20001220703125,101570.0,0.003599999938160181,286.6000061035156,-2.700000047683716,-3.700000047683716,0.0 +2000-01-05 20:00:00,0.0,266.8000183105469,336.8999938964844,101580.0,0.003700000001117587,286.70001220703125,-2.5,-3.700000047683716,0.0 +2000-01-05 21:00:00,0.0,260.0,142.10000610351562,101670.0,0.003599999938160181,286.3000183105469,-2.200000047683716,-3.700000047683716,0.0 +2000-01-05 22:00:00,0.0,260.0,21.100000381469727,101670.0,0.0037999998312443495,284.70001220703125,-2.200000047683716,-3.299999952316284,0.0 +2000-01-05 23:00:00,0.0,260.0,0.0,101700.0,0.003999999724328518,281.6000061035156,-2.1000001430511475,-3.0,0.0 +2000-01-06 00:00:00,0.0,249.5,0.0,101750.0,0.0038999998942017555,280.5,-2.1000001430511475,-2.700000047683716,0.0 +2000-01-06 01:00:00,0.0,249.5,0.0,101730.0,0.0037999998312443495,280.0,-2.1000001430511475,-2.6000001430511475,0.0 +2000-01-06 02:00:00,0.0,249.5,0.0,101760.0,0.0037999998312443495,279.3000183105469,-2.200000047683716,-2.6000001430511475,0.0 +2000-01-06 03:00:00,0.0,250.90000915527344,0.0,101760.0,0.003999999724328518,279.1000061035156,-2.200000047683716,-2.6000001430511475,0.0 +2000-01-06 04:00:00,0.0,250.90000915527344,0.0,101730.0,0.0037999998312443495,278.3000183105469,-2.200000047683716,-2.4000000953674316,0.0 +2000-01-06 05:00:00,0.0,250.90000915527344,0.0,101740.0,0.0037999998312443495,277.8999938964844,-2.1000001430511475,-2.200000047683716,0.0 +2000-01-06 06:00:00,0.0,268.20001220703125,0.0,101720.0,0.0038999998942017555,277.3999938964844,-2.0,-2.0,0.0 +2000-01-06 07:00:00,0.0,268.20001220703125,0.0,101620.0,0.0037999998312443495,277.3999938964844,-1.899999976158142,-2.200000047683716,0.0 +2000-01-06 08:00:00,0.0,268.20001220703125,0.0,101610.0,0.003700000001117587,276.8000183105469,-1.8000000715255737,-2.5,0.0 +2000-01-06 09:00:00,0.0,303.1000061035156,0.0,101600.0,0.0038999998942017555,276.3000183105469,-1.8000000715255737,-2.700000047683716,0.0 +2000-01-06 10:00:00,0.0,303.1000061035156,0.0,101600.0,0.003999999724328518,276.5,-1.600000023841858,-2.4000000953674316,0.0 +2000-01-06 11:00:00,0.0,303.1000061035156,0.0,101600.0,0.0037999998312443495,275.3000183105469,-1.5,-2.1000001430511475,0.0 +2000-01-06 12:00:00,0.0,272.20001220703125,0.0,101650.0,0.003700000001117587,275.1000061035156,-1.3000000715255737,-1.8000000715255737,0.0 +2000-01-06 13:00:00,0.0,272.20001220703125,101.0,101620.0,0.003999999724328518,276.0,-1.399999976158142,-2.0,0.0 +2000-01-06 14:00:00,0.0,272.20001220703125,245.10000610351562,101590.0,0.004299999680370092,279.3000183105469,-1.600000023841858,-2.299999952316284,0.0 +2000-01-06 15:00:00,0.0,276.8000183105469,393.3999938964844,101590.0,0.004399999976158142,282.20001220703125,-1.7000000476837158,-2.5,0.0 +2000-01-06 16:00:00,0.0,276.8000183105469,481.0,101520.0,0.004299999680370092,284.3999938964844,-1.899999976158142,-2.0,0.0 +2000-01-06 17:00:00,0.0,276.8000183105469,504.5,101420.0,0.00419999985024333,285.70001220703125,-2.0,-1.5,0.0 +2000-01-06 18:00:00,0.0,294.3999938964844,484.3000183105469,101330.0,0.0038999998942017555,287.70001220703125,-2.200000047683716,-1.100000023841858,0.0 +2000-01-06 19:00:00,0.0,294.3999938964844,437.6000061035156,101090.0,0.004600000102072954,288.5,-2.0,-1.100000023841858,0.0 +2000-01-06 20:00:00,0.0,294.3999938964844,329.70001220703125,100990.0,0.004699999932199717,289.3000183105469,-1.7000000476837158,-1.100000023841858,0.0 +2000-01-06 21:00:00,0.0,286.20001220703125,145.60000610351562,101010.0,0.004999999888241291,289.5,-1.5,-1.100000023841858,0.0 +2000-01-06 22:00:00,0.0,286.1000061035156,21.600000381469727,100930.0,0.00559999980032444,286.3000183105469,-1.5,-0.6000000238418579,0.0 +2000-01-06 23:00:00,0.0,286.20001220703125,0.0,100950.0,0.005499999970197678,283.5,-1.399999976158142,-0.10000000149011612,0.0 +2000-01-07 00:00:00,0.0,275.5,0.0,101000.0,0.005499999970197678,282.20001220703125,-1.399999976158142,0.4000000059604645,0.0 +2000-01-07 01:00:00,0.0,275.5,0.0,100940.0,0.005499999970197678,280.8999938964844,-1.2000000476837158,0.0,0.0 +2000-01-07 02:00:00,0.0,275.5,0.0,100920.0,0.005200000014156103,279.70001220703125,-0.9000000357627869,-0.30000001192092896,0.0 +2000-01-07 03:00:00,0.0,275.8999938964844,0.0,100930.0,0.0050999997183680534,279.0,-0.699999988079071,-0.6000000238418579,0.0 +2000-01-07 04:00:00,0.0,275.8999938964844,0.0,100820.0,0.004999999888241291,277.70001220703125,-0.30000001192092896,-1.3000000715255737,0.0 +2000-01-07 05:00:00,0.0,275.8999938964844,0.0,100830.0,0.0052999998442828655,278.3999938964844,0.0,-1.899999976158142,0.0 +2000-01-07 06:00:00,0.0,270.8999938964844,0.0,100810.0,0.0052999998442828655,277.8999938964844,0.30000001192092896,-2.5,0.0 +2000-01-07 07:00:00,0.0,270.8999938964844,0.0,100700.0,0.005399999674409628,278.0,0.10000000149011612,-2.4000000953674316,0.0 +2000-01-07 08:00:00,0.0,270.8999938964844,0.0,100640.0,0.0044999998062849045,276.1000061035156,-0.10000000149011612,-2.200000047683716,0.0 +2000-01-07 09:00:00,0.0,273.0,0.0,100610.0,0.004600000102072954,276.1000061035156,-0.20000000298023224,-2.1000001430511475,0.0 +2000-01-07 10:00:00,0.0,273.0,0.0,100530.0,0.004399999976158142,274.8000183105469,0.10000000149011612,-2.0,0.0 +2000-01-07 11:00:00,0.0,273.0,0.0,100540.0,0.0044999998062849045,275.5,0.4000000059604645,-1.899999976158142,0.0 +2000-01-07 12:00:00,0.0,314.3999938964844,0.0,100580.0,0.004799999762326479,276.20001220703125,0.699999988079071,-1.899999976158142,0.0 +2000-01-07 13:00:00,0.0,314.3999938964844,70.5999984741211,100530.0,0.004900000058114529,276.3999938964844,0.9000000357627869,-1.899999976158142,0.0 +2000-01-07 14:00:00,0.0,314.3999938964844,173.6999969482422,100510.0,0.005699999630451202,279.20001220703125,1.0,-1.899999976158142,0.0 +2000-01-07 15:00:00,0.0,342.3999938964844,287.6000061035156,100530.0,0.006099999882280827,284.0,1.2000000476837158,-2.0,0.0 +2000-01-07 16:00:00,0.0,342.3999938964844,352.1000061035156,100430.0,0.0052999998442828655,287.3000183105469,1.0,-2.1000001430511475,0.0 +2000-01-07 17:00:00,0.0,342.3999938964844,369.6000061035156,100280.0,0.004999999888241291,289.6000061035156,0.9000000357627869,-2.299999952316284,0.0 +2000-01-07 18:00:00,0.0,338.20001220703125,409.6000061035156,100210.0,0.0050999997183680534,290.5,0.800000011920929,-2.4000000953674316,0.0 +2000-01-07 19:00:00,0.0,338.20001220703125,370.3000183105469,100030.0,0.005499999970197678,291.1000061035156,0.9000000357627869,-2.299999952316284,0.0 +2000-01-07 20:00:00,0.0,338.20001220703125,279.1000061035156,99960.0,0.00559999980032444,291.3000183105469,1.100000023841858,-2.200000047683716,0.0 +2000-01-07 21:00:00,0.0,343.8000183105469,130.0,100050.0,0.00559999980032444,291.3999938964844,1.2000000476837158,-2.0,0.0 +2000-01-07 22:00:00,0.0,343.8000183105469,19.200000762939453,100030.0,0.005699999630451202,288.6000061035156,0.9000000357627869,-1.7000000476837158,0.0 +2000-01-07 23:00:00,0.0,343.8000183105469,0.0,100120.0,0.00559999980032444,283.3000183105469,0.699999988079071,-1.3000000715255737,0.0 +2000-01-08 00:00:00,0.0,311.6000061035156,0.0,100170.0,0.005799999926239252,284.0,0.4000000059604645,-1.0,0.0 +2000-01-08 01:00:00,0.0,311.6000061035156,0.0,100130.0,0.005699999630451202,282.70001220703125,0.5,-1.0,0.0 +2000-01-08 02:00:00,0.0,311.6000061035156,0.0,100170.0,0.005499999970197678,281.8999938964844,0.5,-1.0,0.0 +2000-01-08 03:00:00,0.0,281.3000183105469,0.0,100250.0,0.00559999980032444,282.3000183105469,0.6000000238418579,-1.0,0.0 +2000-01-08 04:00:00,0.0,281.3000183105469,0.0,100250.0,0.0052999998442828655,280.5,0.6000000238418579,-0.699999988079071,0.0 +2000-01-08 05:00:00,0.0,281.3000183105469,0.0,100230.0,0.0050999997183680534,280.3000183105469,0.699999988079071,-0.4000000059604645,0.0 +2000-01-08 06:00:00,0.0,279.5,0.0,100190.0,0.0050999997183680534,279.8000183105469,0.699999988079071,0.0,0.0 +2000-01-08 07:00:00,0.0,279.5,0.0,100150.0,0.005200000014156103,279.0,0.800000011920929,-0.30000001192092896,0.0 +2000-01-08 08:00:00,0.0,279.5,0.0,100220.0,0.005399999674409628,278.3000183105469,0.800000011920929,-0.6000000238418579,0.0 +2000-01-08 09:00:00,0.0,267.20001220703125,0.0,100210.0,0.004900000058114529,277.70001220703125,0.9000000357627869,-0.9000000357627869,0.0 +2000-01-08 10:00:00,0.0,267.20001220703125,0.0,100160.0,0.004799999762326479,277.0,0.5,-0.800000011920929,0.0 +2000-01-08 11:00:00,0.0,267.20001220703125,0.0,100230.0,0.0044999998062849045,275.3999938964844,0.10000000149011612,-0.800000011920929,0.0 +2000-01-08 12:00:00,0.0,258.20001220703125,0.0,100330.0,0.0044999998062849045,275.5,-0.30000001192092896,-0.800000011920929,0.0 +2000-01-08 13:00:00,0.0,258.20001220703125,96.80000305175781,100300.0,0.0044999998062849045,275.20001220703125,-0.4000000059604645,-0.6000000238418579,0.0 +2000-01-08 14:00:00,0.0,258.20001220703125,241.5,100380.0,0.006000000052154064,280.1000061035156,-0.6000000238418579,-0.4000000059604645,0.0 +2000-01-08 15:00:00,0.0,283.6000061035156,393.6000061035156,100460.0,0.006099999882280827,284.1000061035156,-0.699999988079071,-0.30000001192092896,0.0 +2000-01-08 16:00:00,0.0,283.6000061035156,482.70001220703125,100430.0,0.00559999980032444,286.5,-0.699999988079071,0.5,0.0 +2000-01-08 17:00:00,0.0,283.6000061035156,507.20001220703125,100320.0,0.0052999998442828655,288.5,-0.800000011920929,1.3000000715255737,0.0 +2000-01-08 18:00:00,0.0,289.5,489.5,100250.0,0.005499999970197678,289.3000183105469,-0.800000011920929,2.1000001430511475,0.0 +2000-01-08 19:00:00,0.0,289.5,442.70001220703125,100120.0,0.005699999630451202,290.0,-0.4000000059604645,2.5,0.0 +2000-01-08 20:00:00,0.0,289.5,333.8000183105469,100130.0,0.005799999926239252,290.20001220703125,0.0,2.9000000953674316,0.0 +2000-01-08 21:00:00,0.0,278.70001220703125,141.40000915527344,100100.0,0.006000000052154064,289.5,0.5,3.200000047683716,0.0 +2000-01-08 22:00:00,0.0,278.70001220703125,21.0,100100.0,0.006000000052154064,287.5,0.10000000149011612,2.9000000953674316,0.0 +2000-01-08 23:00:00,0.0,278.70001220703125,0.0,100120.0,0.006000000052154064,283.5,-0.20000000298023224,2.6000001430511475,0.0 +2000-01-09 00:00:00,0.0,263.3000183105469,0.0,100180.0,0.006099999882280827,281.8000183105469,-0.5,2.299999952316284,0.0 +2000-01-09 01:00:00,0.0,263.3000183105469,0.0,100190.0,0.006099999882280827,281.20001220703125,-0.800000011920929,2.299999952316284,0.0 +2000-01-09 02:00:00,0.0,263.3000183105469,0.0,100220.0,0.006399999838322401,281.0,-1.2000000476837158,2.200000047683716,0.0 +2000-01-09 03:00:00,0.0,258.1000061035156,0.0,100240.0,0.006099999882280827,280.1000061035156,-1.600000023841858,2.200000047683716,0.0 +2000-01-09 04:00:00,0.0,258.1000061035156,0.0,100170.0,0.005799999926239252,278.8999938964844,-1.100000023841858,2.0,0.0 +2000-01-09 05:00:00,0.0,258.1000061035156,0.0,100160.0,0.005699999630451202,279.0,-0.6000000238418579,1.8000000715255737,0.0 +2000-01-09 06:00:00,0.0,254.5,0.0,100160.0,0.005699999630451202,278.5,0.0,1.600000023841858,0.0 +2000-01-09 07:00:00,0.0,254.5,0.0,100130.0,0.005499999970197678,278.6000061035156,-0.20000000298023224,1.5,0.0 +2000-01-09 08:00:00,0.0,254.5,0.0,100160.0,0.005499999970197678,278.5,-0.4000000059604645,1.399999976158142,0.0 +2000-01-09 09:00:00,0.0,255.3000030517578,0.0,100110.0,0.005399999674409628,277.70001220703125,-0.6000000238418579,1.399999976158142,0.0 +2000-01-09 10:00:00,0.0,255.3000030517578,0.0,100090.0,0.004999999888241291,276.8999938964844,-0.800000011920929,1.7000000476837158,0.0 +2000-01-09 11:00:00,0.0,255.3000030517578,0.0,100130.0,0.004900000058114529,277.1000061035156,-1.0,2.1000001430511475,0.0 +2000-01-09 12:00:00,0.0,266.6000061035156,0.0,100190.0,0.004999999888241291,276.6000061035156,-1.2000000476837158,2.5,0.0 +2000-01-09 13:00:00,0.0,266.6000061035156,93.4000015258789,100160.0,0.004699999932199717,276.0,-1.0,1.8000000715255737,0.0 +2000-01-09 14:00:00,0.0,266.6000061035156,236.10000610351562,100240.0,0.0058999997563660145,279.3000183105469,-0.800000011920929,1.0,0.0 +2000-01-09 15:00:00,0.0,294.6000061035156,362.0,100290.0,0.0066999997943639755,283.0,-0.699999988079071,0.30000001192092896,0.0 +2000-01-09 16:00:00,0.0,294.6000061035156,444.6000061035156,100160.0,0.006000000052154064,286.5,-0.4000000059604645,1.0,0.0 +2000-01-09 17:00:00,0.0,294.6000061035156,467.70001220703125,100020.0,0.0052999998442828655,288.8999938964844,-0.20000000298023224,1.600000023841858,0.0 +2000-01-09 18:00:00,0.0,304.1000061035156,478.0,99930.0,0.004799999762326479,290.1000061035156,0.0,2.299999952316284,0.0 +2000-01-09 19:00:00,0.0,304.1000061035156,432.5,99750.0,0.004999999888241291,291.0,0.4000000059604645,2.0,0.0 +2000-01-09 20:00:00,0.0,304.1000061035156,326.3000183105469,99770.0,0.004999999888241291,291.1000061035156,0.800000011920929,1.8000000715255737,0.0 +2000-01-09 21:00:00,0.0,291.8000183105469,138.1999969482422,99740.0,0.004999999888241291,290.70001220703125,1.2000000476837158,1.5,0.0 +2000-01-09 22:00:00,0.0,291.8000183105469,20.600000381469727,99790.0,0.005200000014156103,288.0,1.600000023841858,0.6000000238418579,0.0 +2000-01-09 23:00:00,0.0,291.8000183105469,0.0,99830.0,0.0050999997183680534,284.0,2.0,-0.30000001192092896,0.0 +2000-01-10 00:00:00,0.0,274.6000061035156,0.0,99830.0,0.005200000014156103,284.20001220703125,2.4000000953674316,-1.2000000476837158,0.0 +2000-01-10 01:00:00,0.0,274.6000061035156,0.0,99830.0,0.0052999998442828655,282.3000183105469,2.1000001430511475,-1.600000023841858,0.0 +2000-01-10 02:00:00,0.0,274.6000061035156,0.0,99860.0,0.004999999888241291,281.1000061035156,1.8000000715255737,-2.0,0.0 +2000-01-10 03:00:00,0.0,271.8000183105469,0.0,99830.0,0.004900000058114529,280.5,1.5,-2.4000000953674316,0.0 +2000-01-10 04:00:00,0.0,271.8000183105469,0.0,99790.0,0.005200000014156103,280.8999938964844,1.5,-2.1000001430511475,0.0 +2000-01-10 05:00:00,0.0,271.8000183105469,0.0,99820.0,0.0050999997183680534,279.8999938964844,1.399999976158142,-1.7000000476837158,0.0 +2000-01-10 06:00:00,0.0,277.3999938964844,0.0,99860.0,0.0050999997183680534,279.20001220703125,1.399999976158142,-1.399999976158142,0.0 +2000-01-10 07:00:00,0.0,277.3999938964844,0.0,99780.0,0.004999999888241291,280.3999938964844,1.399999976158142,-1.3000000715255737,0.0 +2000-01-10 08:00:00,0.0,277.3999938964844,0.0,99780.0,0.0050999997183680534,279.5,1.399999976158142,-1.2000000476837158,0.0 +2000-01-10 09:00:00,0.0,306.3999938964844,0.0,99780.0,0.005200000014156103,279.8000183105469,1.3000000715255737,-1.100000023841858,0.0 +2000-01-10 10:00:00,0.0,306.3999938964844,0.0,99780.0,0.004999999888241291,279.5,0.800000011920929,-0.9000000357627869,0.0 +2000-01-10 11:00:00,0.0,306.3999938964844,0.0,99820.0,0.0052999998442828655,279.3999938964844,0.30000001192092896,-0.699999988079071,0.0 +2000-01-10 12:00:00,0.0,289.1000061035156,0.0,99830.0,0.0050999997183680534,279.3000183105469,-0.20000000298023224,-0.6000000238418579,0.0 +2000-01-10 13:00:00,0.0,289.1000061035156,91.0,99860.0,0.005399999674409628,278.8000183105469,0.20000000298023224,-0.6000000238418579,0.0 +2000-01-10 14:00:00,0.0,289.1000061035156,233.3000030517578,99880.0,0.006099999882280827,282.70001220703125,0.699999988079071,-0.6000000238418579,0.0 +2000-01-10 15:00:00,0.0,301.1000061035156,382.1000061035156,100000.0,0.006300000008195639,286.1000061035156,1.2000000476837158,-0.6000000238418579,0.0 +2000-01-10 16:00:00,0.0,301.1000061035156,469.8999938964844,99920.0,0.007199999876320362,288.8999938964844,1.5,0.20000000298023224,0.0 +2000-01-10 17:00:00,0.0,301.1000061035156,494.70001220703125,99860.0,0.007199999876320362,291.8000183105469,1.8000000715255737,1.100000023841858,0.0 +2000-01-10 18:00:00,0.0,328.3999938964844,440.8999938964844,99730.0,0.007899999618530273,293.0,2.1000001430511475,1.899999976158142,0.0 +2000-01-10 19:00:00,0.0,328.3999938964844,399.20001220703125,99650.0,0.008100000210106373,292.8999938964844,1.899999976158142,2.4000000953674316,0.0 +2000-01-10 20:00:00,0.0,328.3999938964844,301.3999938964844,99630.0,0.008200000040233135,293.0,1.7000000476837158,3.0,0.0 +2000-01-10 21:00:00,0.0,351.3000183105469,126.5999984741211,99670.0,0.007899999618530273,292.1000061035156,1.399999976158142,3.6000001430511475,0.0 +2000-01-10 22:00:00,0.0,351.3000183105469,19.0,99670.0,0.007799999788403511,290.6000061035156,1.100000023841858,3.4000000953674316,0.0 +2000-01-10 23:00:00,0.0,351.3999938964844,0.0,99650.0,0.007799999788403511,287.8999938964844,0.699999988079071,3.299999952316284,0.0 +2000-01-11 00:00:00,0.0,351.20001220703125,0.0,99710.0,0.007899999618530273,286.1000061035156,0.4000000059604645,3.1000001430511475,0.0 +2000-01-11 01:00:00,0.0,351.20001220703125,0.0,99690.0,0.007999999448657036,286.1000061035156,0.10000000149011612,2.799999952316284,0.0 +2000-01-11 02:00:00,0.0,351.20001220703125,0.0,99770.0,0.007999999448657036,285.8000183105469,-0.20000000298023224,2.4000000953674316,0.0 +2000-01-11 03:00:00,0.0,363.8999938964844,0.0,99790.0,0.007799999788403511,284.6000061035156,-0.6000000238418579,2.1000001430511475,0.0 +2000-01-11 04:00:00,0.0,363.8999938964844,0.0,99750.0,0.007599999662488699,284.5,-0.20000000298023224,2.200000047683716,0.0 +2000-01-11 05:00:00,0.0,363.8999938964844,0.0,99740.0,0.007400000002235174,284.3999938964844,0.10000000149011612,2.299999952316284,0.0 +2000-01-11 06:00:00,0.0,369.5,0.0,99750.0,0.007400000002235174,284.6000061035156,0.4000000059604645,2.4000000953674316,0.0 +2000-01-11 07:00:00,0.0,369.5,0.0,99650.0,0.007599999662488699,283.8999938964844,0.800000011920929,2.299999952316284,0.0 +2000-01-11 08:00:00,0.0,369.5,0.0,99720.0,0.007599999662488699,283.8000183105469,1.3000000715255737,2.200000047683716,0.0 +2000-01-11 09:00:00,0.0,311.70001220703125,0.0,99710.0,0.007400000002235174,283.8000183105469,1.7000000476837158,2.0,0.0 +2000-01-11 10:00:00,0.0,311.70001220703125,0.0,99730.0,0.0071000000461936,282.3000183105469,1.3000000715255737,2.1000001430511475,0.0 +2000-01-11 11:00:00,0.0,311.8000183105469,0.0,99840.0,0.00699999975040555,281.8999938964844,0.9000000357627869,2.200000047683716,0.0 +2000-01-11 12:00:00,0.0,297.1000061035156,0.0,99900.0,0.007199999876320362,281.8999938964844,0.5,2.299999952316284,0.0 +2000-01-11 13:00:00,0.0,297.1000061035156,86.9000015258789,99910.0,0.007499999832361937,283.1000061035156,0.800000011920929,2.1000001430511475,0.0 +2000-01-11 14:00:00,0.0,297.1000061035156,226.1999969482422,100010.0,0.008799999952316284,285.3000183105469,1.2000000476837158,1.8000000715255737,0.0 +2000-01-11 15:00:00,0.0,327.0,373.70001220703125,100090.0,0.010099999606609344,287.5,1.5,1.600000023841858,0.0 +2000-01-11 16:00:00,0.0,327.0,460.3000183105469,100110.0,0.010099999606609344,287.8000183105469,1.8000000715255737,2.299999952316284,0.0 +2000-01-11 17:00:00,0.0,326.8999938964844,485.0,100090.0,0.010199999436736107,288.3999938964844,2.0,3.0,0.0 +2000-01-11 18:00:00,0.0,348.8999938964844,463.8999938964844,100080.0,0.011500000022351742,290.8999938964844,2.299999952316284,3.700000047683716,0.0 +2000-01-11 19:00:00,0.0,348.8999938964844,420.3000183105469,99970.0,0.010599999688565731,293.8999938964844,2.200000047683716,3.799999952316284,0.0 +2000-01-11 20:00:00,0.0,348.8999938964844,317.5,99910.0,0.010599999688565731,295.20001220703125,2.1000001430511475,3.9000000953674316,0.0 +2000-01-11 21:00:00,0.0,344.8000183105469,131.3000030517578,99990.0,0.010799999348819256,294.3000183105469,2.0,4.0,0.0 +2000-01-11 22:00:00,0.0,344.8000183105469,20.0,99970.0,0.010900000110268593,292.8000183105469,1.399999976158142,3.700000047683716,0.0 +2000-01-11 23:00:00,0.0,344.8000183105469,0.0,100000.0,0.010699999518692493,289.8999938964844,0.9000000357627869,3.5,0.0 +2000-01-12 00:00:00,0.0,329.8999938964844,0.0,100080.0,0.010599999688565731,288.6000061035156,0.30000001192092896,3.200000047683716,0.0 +2000-01-12 01:00:00,0.0,329.8999938964844,0.0,100030.0,0.010400000028312206,287.6000061035156,0.30000001192092896,3.1000001430511475,0.0 +2000-01-12 02:00:00,0.0,330.0,0.0,100080.0,0.010099999606609344,287.1000061035156,0.20000000298023224,3.0,0.0 +2000-01-12 03:00:00,0.0,327.0,0.0,100210.0,0.010199999436736107,287.5,0.20000000298023224,3.0,0.0 +2000-01-12 04:00:00,0.0,327.0,0.0,100250.0,0.009999999776482582,287.5,0.9000000357627869,3.1000001430511475,0.0 +2000-01-12 05:00:00,0.0,327.0,0.0,100290.0,0.009200000204145908,285.8999938964844,1.600000023841858,3.299999952316284,0.0 +2000-01-12 06:00:00,0.0,327.0,0.0,100320.0,0.009399999864399433,286.1000061035156,2.299999952316284,3.5,0.0 +2000-01-12 07:00:00,0.0,327.0,0.0,100330.0,0.009099999442696571,285.8999938964844,2.1000001430511475,3.200000047683716,0.0 +2000-01-12 08:00:00,0.0,327.0,0.0,100350.0,0.009499999694526196,287.1000061035156,1.899999976158142,2.9000000953674316,0.0 +2000-01-12 09:00:00,0.0,320.20001220703125,0.0,100410.0,0.00969999935477972,287.70001220703125,1.7000000476837158,2.5,0.0 +2000-01-12 10:00:00,0.0,320.20001220703125,0.0,100430.0,0.009800000116229057,287.3000183105469,1.3000000715255737,2.5,0.0 +2000-01-12 11:00:00,0.0,320.20001220703125,0.0,100430.0,0.010300000198185444,287.70001220703125,0.9000000357627869,2.6000001430511475,0.0 +2000-01-12 12:00:00,0.0,316.3000183105469,0.0,100590.0,0.00930000003427267,286.70001220703125,0.5,2.5,0.0 +2000-01-12 13:00:00,0.0,316.3000183105469,83.5999984741211,100520.0,0.009599999524652958,286.3000183105469,0.9000000357627869,2.4000000953674316,0.0 +2000-01-12 14:00:00,0.0,316.3000183105469,220.5,100610.0,0.010499999858438969,288.3999938964844,1.399999976158142,2.299999952316284,0.0 +2000-01-12 15:00:00,0.0,341.5,367.5,100690.0,0.011399999260902405,290.70001220703125,1.8000000715255737,2.200000047683716,0.0 +2000-01-12 16:00:00,0.0,341.5,453.3000183105469,100610.0,0.011599999852478504,293.3000183105469,2.299999952316284,2.0,0.0 +2000-01-12 17:00:00,0.0,341.5,478.0,100550.0,0.010499999858438969,295.5,2.9000000953674316,1.8000000715255737,0.0 +2000-01-12 18:00:00,0.0,354.20001220703125,463.6000061035156,100500.0,0.010199999436736107,295.8999938964844,3.5,1.600000023841858,0.0 +2000-01-12 19:00:00,0.0,354.20001220703125,420.20001220703125,100390.0,0.010199999436736107,296.8000183105469,3.200000047683716,1.899999976158142,0.0 +2000-01-12 20:00:00,0.0,354.20001220703125,317.70001220703125,100390.0,0.010900000110268593,296.70001220703125,2.9000000953674316,2.200000047683716,0.0 +2000-01-12 21:00:00,0.0,339.8999938964844,149.60000610351562,100420.0,0.010499999858438969,296.3000183105469,2.6000001430511475,2.4000000953674316,0.0 +2000-01-12 22:00:00,0.0,339.8999938964844,23.100000381469727,100460.0,0.010799999348819256,293.6000061035156,2.4000000953674316,2.4000000953674316,0.0 +2000-01-12 23:00:00,0.0,339.8999938964844,0.0,100490.0,0.010499999858438969,290.20001220703125,2.1000001430511475,2.4000000953674316,0.0 +2000-01-13 00:00:00,0.0,319.20001220703125,0.0,100510.0,0.010699999518692493,289.1000061035156,1.8000000715255737,2.4000000953674316,0.0 +2000-01-13 01:00:00,0.0,319.20001220703125,0.0,100510.0,0.010699999518692493,288.6000061035156,1.2000000476837158,2.200000047683716,0.0 +2000-01-13 02:00:00,0.0,319.20001220703125,0.0,100560.0,0.010099999606609344,287.0,0.699999988079071,2.0,0.0 +2000-01-13 03:00:00,0.0,310.70001220703125,0.0,100580.0,0.009499999694526196,286.3999938964844,0.10000000149011612,1.8000000715255737,0.0 +2000-01-13 04:00:00,0.0,310.70001220703125,0.0,100580.0,0.008899999782443047,285.3999938964844,0.5,1.8000000715255737,0.0 +2000-01-13 05:00:00,0.0,310.70001220703125,0.0,100580.0,0.00839999970048666,284.3000183105469,0.9000000357627869,1.8000000715255737,0.0 +2000-01-13 06:00:00,0.0,303.20001220703125,0.0,100600.0,0.008200000040233135,284.1000061035156,1.3000000715255737,1.8000000715255737,0.0 +2000-01-13 07:00:00,0.0,303.20001220703125,0.0,100490.0,0.007799999788403511,283.8999938964844,1.0,1.8000000715255737,0.0 +2000-01-13 08:00:00,0.0,303.20001220703125,0.0,100500.0,0.008100000210106373,284.3000183105469,0.6000000238418579,1.7000000476837158,0.0 +2000-01-13 09:00:00,0.0,295.3000183105469,0.0,100490.0,0.007899999618530273,283.6000061035156,0.20000000298023224,1.600000023841858,0.0 +2000-01-13 10:00:00,0.0,295.3000183105469,0.0,100440.0,0.0071000000461936,281.8999938964844,-0.20000000298023224,1.3000000715255737,0.0 +2000-01-13 11:00:00,0.0,295.3000183105469,0.0,100490.0,0.006599999964237213,281.1000061035156,-0.6000000238418579,0.9000000357627869,0.0 +2000-01-13 12:00:00,0.0,299.8999938964844,0.0,100530.0,0.007199999876320362,282.3000183105469,-1.0,0.6000000238418579,0.0 +2000-01-13 13:00:00,0.0,299.8999938964844,83.0,100450.0,0.006799999624490738,281.3999938964844,-0.800000011920929,0.10000000149011612,0.0 +2000-01-13 14:00:00,0.0,299.8999938964844,222.10000610351562,100570.0,0.00839999970048666,284.3999938964844,-0.6000000238418579,-0.4000000059604645,0.0 +2000-01-13 15:00:00,0.0,334.20001220703125,356.8999938964844,100570.0,0.009399999864399433,288.0,-0.4000000059604645,-0.9000000357627869,0.0 +2000-01-13 16:00:00,0.0,334.20001220703125,440.8999938964844,100450.0,0.00930000003427267,291.70001220703125,-0.6000000238418579,0.30000001192092896,0.0 +2000-01-13 17:00:00,0.0,334.20001220703125,465.3999938964844,100430.0,0.008700000122189522,293.0,-0.699999988079071,1.5,0.0 +2000-01-13 18:00:00,0.0,342.8000183105469,447.20001220703125,100350.0,0.00989999994635582,294.8000183105469,-0.9000000357627869,2.700000047683716,0.0 +2000-01-13 19:00:00,0.0,342.8000183105469,405.6000061035156,100200.0,0.009599999524652958,294.8000183105469,-1.3000000715255737,2.9000000953674316,0.0 +2000-01-13 20:00:00,0.0,342.8000183105469,306.8999938964844,100170.0,0.009999999776482582,295.1000061035156,-1.7000000476837158,3.200000047683716,0.0 +2000-01-13 21:00:00,0.0,322.70001220703125,142.8000030517578,100140.0,0.010599999688565731,294.6000061035156,-2.1000001430511475,3.4000000953674316,0.0 +2000-01-13 22:00:00,0.0,322.70001220703125,22.30000114440918,100110.0,0.010599999688565731,292.0,-2.200000047683716,2.9000000953674316,0.0 +2000-01-13 23:00:00,0.0,322.70001220703125,0.0,100140.0,0.010599999688565731,289.3999938964844,-2.4000000953674316,2.4000000953674316,0.0 +2000-01-14 00:00:00,0.0,319.6000061035156,0.0,100120.0,0.010799999348819256,288.20001220703125,-2.5,1.899999976158142,0.0 +2000-01-14 01:00:00,0.0,319.6000061035156,0.0,100060.0,0.010799999348819256,288.70001220703125,-2.700000047683716,1.7000000476837158,0.0 +2000-01-14 02:00:00,0.0,319.6000061035156,0.0,100130.0,0.010900000110268593,288.0,-2.799999952316284,1.5,0.0 +2000-01-14 03:00:00,0.0,314.70001220703125,0.0,100080.0,0.011500000022351742,289.6000061035156,-3.0,1.399999976158142,0.0 +2000-01-14 04:00:00,0.0,314.70001220703125,0.0,100050.0,0.010900000110268593,288.8999938964844,-2.700000047683716,1.899999976158142,0.0 +2000-01-14 05:00:00,0.0,314.70001220703125,0.0,99960.0,0.010699999518692493,288.3000183105469,-2.5,2.4000000953674316,0.0 +2000-01-14 06:00:00,0.0,323.3000183105469,0.0,99970.0,0.011299999430775642,288.6000061035156,-2.200000047683716,3.0,0.0 +2000-01-14 07:00:00,0.0,323.3000183105469,0.0,99810.0,0.010900000110268593,288.5,-1.8000000715255737,3.1000001430511475,0.0 +2000-01-14 08:00:00,0.0,323.3000183105469,0.0,99750.0,0.011399999260902405,289.3000183105469,-1.399999976158142,3.200000047683716,0.0 +2000-01-14 09:00:00,0.0,345.5,0.0,99710.0,0.011500000022351742,289.5,-1.0,3.299999952316284,0.0 +2000-01-14 10:00:00,0.0,345.5,0.0,99590.0,0.011699999682605267,289.3999938964844,-1.399999976158142,3.299999952316284,0.0 +2000-01-14 11:00:00,0.0,345.5,0.0,99510.0,0.011799999512732029,289.70001220703125,-1.899999976158142,3.200000047683716,0.0 +2000-01-14 12:00:00,0.0,377.70001220703125,0.0,99470.0,0.011799999512732029,289.70001220703125,-2.299999952316284,3.200000047683716,0.0 +2000-01-14 13:00:00,0.0,377.70001220703125,38.400001525878906,99380.0,0.01249999925494194,290.20001220703125,-1.7000000476837158,4.0,0.0 +2000-01-14 14:00:00,0.0,377.70001220703125,104.20000457763672,99390.0,0.012999999336898327,291.3000183105469,-1.100000023841858,4.700000286102295,0.0 +2000-01-14 15:00:00,0.0,389.5,173.5,99330.0,0.01269999984651804,291.8000183105469,-0.5,5.5,2.781874398823169e-07 +2000-01-14 16:00:00,1.0,389.5,214.60000610351562,99150.0,0.013199999928474426,292.1000061035156,-0.30000001192092896,5.700000286102295,0.0 +2000-01-14 17:00:00,0.0,389.5,226.8000030517578,99080.0,0.013899999670684338,293.0,-0.10000000149011612,5.800000190734863,0.0 +2000-01-14 18:00:00,0.0,405.5,149.90000915527344,99000.0,0.014499999582767487,293.3000183105469,0.10000000149011612,6.0,0.0 +2000-01-14 19:00:00,0.0,405.5,136.10000610351562,98920.0,0.015199999324977398,294.1000061035156,0.699999988079071,5.599999904632568,0.0 +2000-01-14 20:00:00,0.0,405.5,103.0999984741211,98870.0,0.014599999412894249,293.8000183105469,1.3000000715255737,5.300000190734863,1.1410234794070803e-06 +2000-01-14 21:00:00,4.099999904632568,398.0,43.400001525878906,98930.0,0.013499999418854713,292.0,1.899999976158142,4.900000095367432,2.5037821260041673e-07 +2000-01-14 22:00:00,0.9000000357627869,397.8999938964844,6.900000095367432,98880.0,0.012000000104308128,290.20001220703125,2.4000000953674316,4.599999904632568,0.0 +2000-01-14 23:00:00,0.0,397.8999938964844,0.0,98920.0,0.011899999342858791,289.5,2.799999952316284,4.300000190734863,0.0 +2000-01-15 00:00:00,0.0,343.70001220703125,0.0,98900.0,0.01209999993443489,289.8999938964844,3.299999952316284,3.9000000953674316,0.0 +2000-01-15 01:00:00,0.0,343.70001220703125,0.0,98860.0,0.01209999993443489,289.8999938964844,3.299999952316284,3.799999952316284,0.0 +2000-01-15 02:00:00,0.0,343.70001220703125,0.0,98910.0,0.011799999512732029,289.3000183105469,3.299999952316284,3.700000047683716,0.0 +2000-01-15 03:00:00,0.0,305.5,0.0,98940.0,0.011399999260902405,288.8999938964844,3.4000000953674316,3.6000001430511475,0.0 +2000-01-15 04:00:00,0.0,305.5,0.0,98920.0,0.011099999770522118,288.70001220703125,3.6000001430511475,3.0,0.0 +2000-01-15 05:00:00,0.0,305.5,0.0,99020.0,0.010400000028312206,287.8999938964844,3.9000000953674316,2.4000000953674316,0.0 +2000-01-15 06:00:00,0.0,287.8000183105469,0.0,99050.0,0.009399999864399433,286.8000183105469,4.099999904632568,1.8000000715255737,0.0 +2000-01-15 07:00:00,0.0,287.8000183105469,0.0,99030.0,0.008899999782443047,286.6000061035156,4.200000286102295,1.600000023841858,0.0 +2000-01-15 08:00:00,0.0,287.8000183105469,0.0,99020.0,0.007899999618530273,285.70001220703125,4.200000286102295,1.3000000715255737,0.0 +2000-01-15 09:00:00,0.0,279.8000183105469,0.0,99080.0,0.007400000002235174,284.8000183105469,4.200000286102295,1.100000023841858,0.0 +2000-01-15 10:00:00,0.0,279.8000183105469,0.0,99060.0,0.0071000000461936,283.6000061035156,3.799999952316284,0.9000000357627869,0.0 +2000-01-15 11:00:00,0.0,279.8000183105469,0.0,99080.0,0.00699999975040555,283.3000183105469,3.299999952316284,0.800000011920929,0.0 +2000-01-15 12:00:00,0.0,271.6000061035156,0.0,99210.0,0.0066999997943639755,282.6000061035156,2.799999952316284,0.6000000238418579,0.0 +2000-01-15 13:00:00,0.0,271.6000061035156,81.5999984741211,99240.0,0.00699999975040555,282.1000061035156,2.9000000953674316,0.20000000298023224,0.0 +2000-01-15 14:00:00,0.0,271.6000061035156,224.60000610351562,99300.0,0.007899999618530273,284.6000061035156,3.0,-0.30000001192092896,0.0 +2000-01-15 15:00:00,0.0,301.6000061035156,376.8000183105469,99400.0,0.007400000002235174,287.20001220703125,3.1000001430511475,-0.699999988079071,0.0 +2000-01-15 16:00:00,0.0,301.6000061035156,466.70001220703125,99350.0,0.007499999832361937,290.20001220703125,2.799999952316284,-0.20000000298023224,0.0 +2000-01-15 17:00:00,0.0,301.6000061035156,493.6000061035156,99260.0,0.007799999788403511,292.6000061035156,2.5,0.20000000298023224,0.0 +2000-01-15 18:00:00,0.0,314.5,478.3000183105469,99280.0,0.007400000002235174,294.3999938964844,2.200000047683716,0.699999988079071,0.0 +2000-01-15 19:00:00,0.0,314.5,434.3999938964844,99170.0,0.007400000002235174,295.8999938964844,1.899999976158142,0.30000001192092896,0.0 +2000-01-15 20:00:00,0.0,314.5,329.20001220703125,99210.0,0.0072999997064471245,296.20001220703125,1.7000000476837158,0.0,0.0 +2000-01-15 21:00:00,0.0,303.8000183105469,142.0,99270.0,0.007599999662488699,295.8999938964844,1.5,-0.4000000059604645,0.0 +2000-01-15 22:00:00,0.0,303.8000183105469,23.100000381469727,99320.0,0.008100000210106373,292.3999938964844,1.2000000476837158,0.0,0.0 +2000-01-15 23:00:00,0.0,303.8000183105469,0.0,99320.0,0.008100000210106373,288.20001220703125,0.9000000357627869,0.30000001192092896,0.0 +2000-01-16 00:00:00,0.0,287.0,0.0,99380.0,0.008499999530613422,287.1000061035156,0.6000000238418579,0.6000000238418579,0.0 +2000-01-16 01:00:00,0.0,287.0,0.0,99370.0,0.008100000210106373,285.20001220703125,0.30000001192092896,-0.20000000298023224,0.0 +2000-01-16 02:00:00,0.0,287.0,0.0,99410.0,0.008200000040233135,285.0,0.0,-1.0,0.0 +2000-01-16 03:00:00,0.0,280.6000061035156,0.0,99510.0,0.008299999870359898,284.0,-0.20000000298023224,-1.8000000715255737,0.0 +2000-01-16 04:00:00,0.0,280.6000061035156,0.0,99530.0,0.007799999788403511,283.3999938964844,-0.4000000059604645,-1.5,0.0 +2000-01-16 05:00:00,0.0,280.6000061035156,0.0,99540.0,0.007199999876320362,282.1000061035156,-0.5,-1.2000000476837158,0.0 +2000-01-16 06:00:00,0.0,275.70001220703125,0.0,99570.0,0.0066999997943639755,280.8999938964844,-0.6000000238418579,-0.800000011920929,0.0 +2000-01-16 07:00:00,0.0,275.70001220703125,0.0,99590.0,0.006799999624490738,280.8999938964844,-0.800000011920929,-0.800000011920929,0.0 +2000-01-16 08:00:00,0.0,275.70001220703125,0.0,99590.0,0.0066999997943639755,280.6000061035156,-1.0,-0.800000011920929,0.0 +2000-01-16 09:00:00,0.0,272.1000061035156,0.0,99630.0,0.006300000008195639,279.8999938964844,-1.2000000476837158,-0.699999988079071,0.0 +2000-01-16 10:00:00,0.0,272.1000061035156,0.0,99600.0,0.006099999882280827,279.3000183105469,-1.600000023841858,-0.699999988079071,0.0 +2000-01-16 11:00:00,0.0,272.1000061035156,0.0,99650.0,0.006799999624490738,281.70001220703125,-1.899999976158142,-0.699999988079071,0.0 +2000-01-16 12:00:00,0.0,267.0,0.0,99760.0,0.00699999975040555,280.8999938964844,-2.299999952316284,-0.6000000238418579,0.0 +2000-01-16 13:00:00,0.0,267.0,80.5,99770.0,0.0066999997943639755,280.5,-2.200000047683716,-0.699999988079071,0.0 +2000-01-16 14:00:00,0.0,267.0,224.6999969482422,99840.0,0.007799999788403511,284.20001220703125,-2.1000001430511475,-0.800000011920929,0.0 +2000-01-16 15:00:00,0.0,291.20001220703125,378.70001220703125,99910.0,0.00699999975040555,287.3000183105469,-2.1000001430511475,-0.9000000357627869,0.0 +2000-01-16 16:00:00,0.0,291.20001220703125,469.70001220703125,99880.0,0.007199999876320362,290.0,-2.4000000953674316,-0.30000001192092896,0.0 +2000-01-16 17:00:00,0.0,291.20001220703125,497.20001220703125,99780.0,0.007699999958276749,292.3000183105469,-2.700000047683716,0.30000001192092896,0.0 +2000-01-16 18:00:00,0.0,305.6000061035156,479.8999938964844,99740.0,0.007599999662488699,294.0,-3.0,0.800000011920929,0.0 +2000-01-16 19:00:00,0.0,305.6000061035156,436.20001220703125,99650.0,0.007599999662488699,294.6000061035156,-2.9000000953674316,0.9000000357627869,0.0 +2000-01-16 20:00:00,0.0,305.6000061035156,330.8999938964844,99620.0,0.007699999958276749,294.8000183105469,-2.700000047683716,1.100000023841858,0.0 +2000-01-16 21:00:00,0.0,300.8000183105469,142.3000030517578,99580.0,0.007599999662488699,294.0,-2.5,1.2000000476837158,0.0 +2000-01-16 22:00:00,0.0,300.8000183105469,23.600000381469727,99590.0,0.007799999788403511,291.6000061035156,-2.4000000953674316,1.0,0.0 +2000-01-16 23:00:00,0.0,300.8000183105469,0.0,99670.0,0.008299999870359898,287.8000183105469,-2.4000000953674316,0.800000011920929,0.0 +2000-01-17 00:00:00,0.0,301.0,0.0,99640.0,0.008200000040233135,286.8000183105469,-2.299999952316284,0.5,0.0 +2000-01-17 01:00:00,0.0,301.0,0.0,99720.0,0.00839999970048666,286.1000061035156,-2.1000001430511475,0.6000000238418579,0.0 +2000-01-17 02:00:00,0.0,301.1000061035156,0.0,99720.0,0.008299999870359898,286.3000183105469,-2.0,0.6000000238418579,0.0 +2000-01-17 03:00:00,0.0,329.70001220703125,0.0,99700.0,0.008299999870359898,285.8999938964844,-1.899999976158142,0.6000000238418579,5.558624344337431e-08 +2000-01-17 04:00:00,0.20000000298023224,329.70001220703125,0.0,99620.0,0.008299999870359898,285.8999938964844,-1.399999976158142,0.6000000238418579,2.779312130753737e-07 +2000-01-17 05:00:00,1.0,329.70001220703125,0.0,99610.0,0.008899999782443047,286.0,-1.0,0.699999988079071,4.446954255654342e-07 +2000-01-17 06:00:00,1.600000023841858,380.1000061035156,0.0,99570.0,0.009099999442696571,286.0,-0.5,0.699999988079071,4.724888958755971e-07 +2000-01-17 07:00:00,1.7000000476837158,380.1000061035156,0.0,99480.0,0.009599999524652958,286.6000061035156,-0.9000000357627869,0.699999988079071,6.115029072671384e-07 +2000-01-17 08:00:00,2.200000047683716,380.1000061035156,0.0,99460.0,0.00930000003427267,286.20001220703125,-1.399999976158142,0.699999988079071,0.0 +2000-01-17 09:00:00,0.0,383.8000183105469,0.0,99450.0,0.008999999612569809,285.6000061035156,-1.8000000715255737,0.699999988079071,8.3376344949306e-08 +2000-01-17 10:00:00,0.30000001192092896,383.8000183105469,0.0,99340.0,0.009099999442696571,285.8000183105469,-2.1000001430511475,0.6000000238418579,0.0 +2000-01-17 11:00:00,0.0,383.8000183105469,0.0,99250.0,0.009200000204145908,286.1000061035156,-2.299999952316284,0.6000000238418579,1.945566619149497e-07 +2000-01-17 12:00:00,0.699999988079071,379.8999938964844,0.0,99260.0,0.009599999524652958,286.3000183105469,-2.6000001430511475,0.5,7.22657308704565e-07 +2000-01-17 13:00:00,2.6000001430511475,379.8999938964844,43.29999923706055,99220.0,0.009399999864399433,286.3000183105469,-2.700000047683716,1.399999976158142,1.41752000103748e-06 +2000-01-17 14:00:00,5.099999904632568,379.8999938964844,122.4000015258789,99230.0,0.009800000116229057,287.20001220703125,-2.9000000953674316,2.4000000953674316,2.2238260147358027e-07 +2000-01-17 15:00:00,0.800000011920929,400.8999938964844,146.8000030517578,99220.0,0.009800000116229057,287.3000183105469,-3.0,3.299999952316284,1.3343140915244274e-06 +2000-01-17 16:00:00,4.800000190734863,400.8999938964844,182.3000030517578,99200.0,0.00989999994635582,287.20001220703125,-1.100000023841858,4.599999904632568,1.1119129907991068e-06 +2000-01-17 17:00:00,4.0,400.8999938964844,193.1999969482422,99150.0,0.00989999994635582,286.70001220703125,0.9000000357627869,5.900000095367432,0.0 +2000-01-17 18:00:00,0.0,378.70001220703125,254.1999969482422,99020.0,0.010499999858438969,287.8000183105469,2.799999952316284,7.200000286102295,0.0 +2000-01-17 19:00:00,0.0,378.70001220703125,231.3000030517578,99020.0,0.010799999348819256,288.3999938964844,3.5,6.200000286102295,0.0 +2000-01-17 20:00:00,0.0,378.70001220703125,175.60000610351562,98990.0,0.01119999960064888,288.8000183105469,4.200000286102295,5.200000286102295,0.0 +2000-01-17 21:00:00,0.0,362.3000183105469,74.0,99000.0,0.010999999940395355,288.70001220703125,4.800000190734863,4.300000190734863,0.0 +2000-01-17 22:00:00,0.0,362.3000183105469,12.5,99020.0,0.01119999960064888,288.70001220703125,4.099999904632568,4.0,0.0 +2000-01-17 23:00:00,0.0,362.3000183105469,0.0,99060.0,0.010999999940395355,288.3000183105469,3.4000000953674316,3.700000047683716,0.0 +2000-01-18 00:00:00,0.0,360.20001220703125,0.0,99090.0,0.011099999770522118,288.8000183105469,2.700000047683716,3.4000000953674316,0.0 +2000-01-18 01:00:00,0.0,360.20001220703125,0.0,99090.0,0.010900000110268593,288.6000061035156,2.200000047683716,3.299999952316284,0.0 +2000-01-18 02:00:00,0.0,360.20001220703125,0.0,99120.0,0.010900000110268593,288.5,1.600000023841858,3.200000047683716,0.0 +2000-01-18 03:00:00,0.0,363.6000061035156,0.0,99170.0,0.010599999688565731,287.8999938964844,1.100000023841858,3.1000001430511475,0.0 +2000-01-18 04:00:00,0.0,363.6000061035156,0.0,99190.0,0.010699999518692493,287.8999938964844,1.2000000476837158,3.1000001430511475,0.0 +2000-01-18 05:00:00,0.0,363.6000061035156,0.0,99080.0,0.010599999688565731,287.8000183105469,1.399999976158142,3.0,0.0 +2000-01-18 06:00:00,0.0,346.1000061035156,0.0,99090.0,0.010699999518692493,287.5,1.600000023841858,2.9000000953674316,0.0 +2000-01-18 07:00:00,0.0,346.20001220703125,0.0,99050.0,0.010300000198185444,287.8999938964844,1.899999976158142,2.6000001430511475,0.0 +2000-01-18 08:00:00,0.0,346.20001220703125,0.0,99100.0,0.010099999606609344,287.70001220703125,2.1000001430511475,2.299999952316284,0.0 +2000-01-18 09:00:00,0.0,329.3999938964844,0.0,99070.0,0.00989999994635582,287.0,2.4000000953674316,2.0,0.0 +2000-01-18 10:00:00,0.0,329.3999938964844,0.0,99090.0,0.00969999935477972,286.5,3.0,2.0,0.0 +2000-01-18 11:00:00,0.0,329.3999938964844,0.0,99120.0,0.008799999952316284,285.70001220703125,3.6000001430511475,1.899999976158142,0.0 +2000-01-18 12:00:00,0.0,272.0,0.0,99240.0,0.008200000040233135,285.0,4.300000190734863,1.899999976158142,0.0 +2000-01-18 13:00:00,0.0,272.0,81.30000305175781,99240.0,0.007999999448657036,284.0,5.0,1.5,0.0 +2000-01-18 14:00:00,0.0,272.0,233.0,99370.0,0.007799999788403511,283.5,5.800000190734863,1.2000000476837158,0.0 +2000-01-18 15:00:00,0.0,267.0,378.8000183105469,99520.0,0.006199999712407589,283.70001220703125,6.5,0.800000011920929,0.0 +2000-01-18 16:00:00,0.0,267.0,471.20001220703125,99610.0,0.0052999998442828655,284.3000183105469,7.099999904632568,0.6000000238418579,0.0 +2000-01-18 17:00:00,0.0,267.0,499.6000061035156,99570.0,0.004600000102072954,285.0,7.599999904632568,0.30000001192092896,0.0 +2000-01-18 18:00:00,0.0,260.6000061035156,490.20001220703125,99580.0,0.003499999875202775,285.8999938964844,8.100000381469727,0.10000000149011612,0.0 +2000-01-18 19:00:00,0.0,260.6000061035156,446.3000183105469,99450.0,0.003700000001117587,287.0,7.5,-1.2000000476837158,0.0 +2000-01-18 20:00:00,0.0,260.70001220703125,339.3000183105469,99510.0,0.003499999875202775,287.3999938964844,6.800000190734863,-2.5,0.0 +2000-01-18 21:00:00,0.0,241.90000915527344,152.10000610351562,99640.0,0.002899999963119626,286.70001220703125,6.099999904632568,-3.799999952316284,0.0 +2000-01-18 22:00:00,0.0,241.90000915527344,26.399999618530273,99730.0,0.0030999998562037945,285.20001220703125,5.5,-3.700000047683716,0.0 +2000-01-18 23:00:00,0.0,241.90000915527344,0.0,99970.0,0.0030999998562037945,281.20001220703125,4.800000190734863,-3.5,0.0 +2000-01-19 00:00:00,0.0,223.8000030517578,0.0,100130.0,0.003000000026077032,279.20001220703125,4.200000286102295,-3.4000000953674316,0.0 +2000-01-19 01:00:00,0.0,223.8000030517578,0.0,100270.0,0.00279999990016222,277.8999938964844,4.0,-3.299999952316284,0.0 +2000-01-19 02:00:00,0.0,223.8000030517578,0.0,100480.0,0.0026000000070780516,276.3000183105469,3.799999952316284,-3.1000001430511475,0.0 +2000-01-19 03:00:00,0.0,214.5,0.0,100610.0,0.0024999999441206455,275.3000183105469,3.700000047683716,-2.9000000953674316,0.0 +2000-01-19 04:00:00,0.0,214.5,0.0,100640.0,0.0026000000070780516,274.70001220703125,3.1000001430511475,-2.700000047683716,0.0 +2000-01-19 05:00:00,0.0,214.5,0.0,100730.0,0.0024999999441206455,273.70001220703125,2.5,-2.4000000953674316,0.0 +2000-01-19 06:00:00,0.0,205.6999969482422,0.0,100780.0,0.002300000051036477,272.70001220703125,2.0,-2.1000001430511475,0.0 +2000-01-19 07:00:00,0.0,205.6999969482422,0.0,100750.0,0.0023999998811632395,272.0,2.0,-2.0,0.0 +2000-01-19 08:00:00,0.0,205.6999969482422,0.0,100840.0,0.0024999999441206455,271.3999938964844,1.899999976158142,-1.8000000715255737,0.0 +2000-01-19 09:00:00,0.0,203.60000610351562,0.0,100870.0,0.0024999999441206455,270.8999938964844,1.899999976158142,-1.7000000476837158,0.0 +2000-01-19 10:00:00,0.0,203.60000610351562,0.0,100910.0,0.0026000000070780516,271.1000061035156,1.899999976158142,-0.800000011920929,0.0 +2000-01-19 11:00:00,0.0,203.60000610351562,0.0,101050.0,0.002699999837204814,270.1000061035156,2.0,0.10000000149011612,0.0 +2000-01-19 12:00:00,0.0,207.40000915527344,0.0,101170.0,0.002699999837204814,270.0,2.0,0.9000000357627869,0.0 +2000-01-19 13:00:00,0.0,207.40000915527344,78.5,101180.0,0.002899999963119626,270.3000183105469,2.0,0.699999988079071,0.0 +2000-01-19 14:00:00,0.0,207.40000915527344,228.10000610351562,101260.0,0.00279999990016222,273.3999938964844,2.1000001430511475,0.4000000059604645,0.0 +2000-01-19 15:00:00,0.0,226.8000030517578,384.8999938964844,101430.0,0.002699999837204814,276.3000183105469,2.1000001430511475,0.20000000298023224,0.0 +2000-01-19 16:00:00,0.0,226.8000030517578,479.5,101340.0,0.002099999925121665,279.3999938964844,2.799999952316284,0.5,0.0 +2000-01-19 17:00:00,0.0,226.8000030517578,508.8999938964844,101250.0,0.0017999999690800905,280.8999938964844,3.4000000953674316,0.9000000357627869,0.0 +2000-01-19 18:00:00,0.0,237.1999969482422,496.3000183105469,101260.0,0.0016999999061226845,281.8999938964844,4.099999904632568,1.2000000476837158,0.0 +2000-01-19 19:00:00,0.0,237.1999969482422,452.20001220703125,101130.0,0.0017999999690800905,282.6000061035156,4.0,0.9000000357627869,0.0 +2000-01-19 20:00:00,0.0,237.1999969482422,344.1000061035156,101120.0,0.0017999999690800905,283.3999938964844,3.799999952316284,0.6000000238418579,0.0 +2000-01-19 21:00:00,0.0,231.0,155.40000915527344,101180.0,0.002099999925121665,283.0,3.6000001430511475,0.30000001192092896,0.0 +2000-01-19 22:00:00,0.0,231.0,27.700000762939453,101130.0,0.002199999988079071,281.3000183105469,3.1000001430511475,0.6000000238418579,0.0 +2000-01-19 23:00:00,0.0,231.0,0.0,101210.0,0.0024999999441206455,278.1000061035156,2.700000047683716,0.800000011920929,0.0 +2000-01-20 00:00:00,0.0,222.10000610351562,0.0,101330.0,0.002699999837204814,275.8999938964844,2.200000047683716,1.100000023841858,0.0 +2000-01-20 01:00:00,0.0,222.10000610351562,0.0,101290.0,0.002899999963119626,275.20001220703125,2.0,0.699999988079071,0.0 +2000-01-20 02:00:00,0.0,222.10000610351562,0.0,101290.0,0.00279999990016222,273.3999938964844,1.7000000476837158,0.30000001192092896,0.0 +2000-01-20 03:00:00,0.0,218.90000915527344,0.0,101320.0,0.00279999990016222,273.1000061035156,1.399999976158142,-0.10000000149011612,0.0 +2000-01-20 04:00:00,0.0,218.90000915527344,0.0,101340.0,0.00279999990016222,272.6000061035156,1.100000023841858,-0.699999988079071,0.0 +2000-01-20 05:00:00,0.0,218.90000915527344,0.0,101400.0,0.00279999990016222,272.1000061035156,0.699999988079071,-1.399999976158142,0.0 +2000-01-20 06:00:00,0.0,217.1999969482422,0.0,101400.0,0.00279999990016222,271.6000061035156,0.30000001192092896,-2.0,0.0 +2000-01-20 07:00:00,0.0,217.1999969482422,0.0,101350.0,0.003000000026077032,271.0,0.20000000298023224,-2.299999952316284,0.0 +2000-01-20 08:00:00,0.0,217.3000030517578,0.0,101360.0,0.003000000026077032,272.0,0.10000000149011612,-2.6000001430511475,0.0 +2000-01-20 09:00:00,0.0,215.0,0.0,101430.0,0.00279999990016222,271.70001220703125,0.10000000149011612,-2.9000000953674316,0.0 +2000-01-20 10:00:00,0.0,215.0,0.0,101440.0,0.002699999837204814,271.0,-0.30000001192092896,-2.200000047683716,0.0 +2000-01-20 11:00:00,0.0,215.0,0.0,101460.0,0.00279999990016222,270.6000061035156,-0.699999988079071,-1.5,0.0 +2000-01-20 12:00:00,0.0,219.0,0.0,101510.0,0.00279999990016222,270.70001220703125,-1.100000023841858,-0.9000000357627869,0.0 +2000-01-20 13:00:00,0.0,219.0,76.4000015258789,101550.0,0.003000000026077032,271.3999938964844,-1.399999976158142,-0.699999988079071,0.0 +2000-01-20 14:00:00,0.0,219.0,224.90000915527344,101660.0,0.003399999812245369,275.3999938964844,-1.7000000476837158,-0.6000000238418579,0.0 +2000-01-20 15:00:00,0.0,239.8000030517578,381.3999938964844,101710.0,0.0031999999191612005,279.3000183105469,-2.1000001430511475,-0.4000000059604645,0.0 +2000-01-20 16:00:00,0.0,239.8000030517578,475.6000061035156,101660.0,0.002699999837204814,282.3000183105469,-2.4000000953674316,0.0,0.0 +2000-01-20 17:00:00,0.0,239.8000030517578,505.20001220703125,101530.0,0.0024999999441206455,284.20001220703125,-2.700000047683716,0.30000001192092896,0.0 +2000-01-20 18:00:00,0.0,251.0,489.6000061035156,101580.0,0.002300000051036477,285.3000183105469,-3.0,0.699999988079071,0.0 +2000-01-20 19:00:00,0.0,251.0,446.3999938964844,101410.0,0.0024999999441206455,286.20001220703125,-2.700000047683716,0.699999988079071,0.0 +2000-01-20 20:00:00,0.0,251.0,340.20001220703125,101410.0,0.0024999999441206455,286.3000183105469,-2.5,0.800000011920929,0.0 +2000-01-20 21:00:00,0.0,250.1999969482422,167.5,101350.0,0.0026000000070780516,285.20001220703125,-2.299999952316284,0.9000000357627869,0.0 +2000-01-20 22:00:00,0.0,250.1999969482422,30.600000381469727,101360.0,0.0026000000070780516,283.3000183105469,-2.6000001430511475,0.9000000357627869,0.0 +2000-01-20 23:00:00,0.0,250.1999969482422,0.0,101390.0,0.0032999999821186066,280.1000061035156,-3.0,1.0,0.0 +2000-01-21 00:00:00,0.0,247.90000915527344,0.0,101390.0,0.003399999812245369,278.70001220703125,-3.4000000953674316,1.100000023841858,0.0 +2000-01-21 01:00:00,0.0,247.90000915527344,0.0,101330.0,0.003399999812245369,277.70001220703125,-3.200000047683716,0.6000000238418579,0.0 +2000-01-21 02:00:00,0.0,247.90000915527344,0.0,101350.0,0.003399999812245369,276.5,-2.9000000953674316,0.20000000298023224,0.0 +2000-01-21 03:00:00,0.0,263.5,0.0,101370.0,0.003599999938160181,276.70001220703125,-2.700000047683716,-0.30000001192092896,0.0 +2000-01-21 04:00:00,0.0,263.5,0.0,101280.0,0.003599999938160181,277.20001220703125,-2.799999952316284,-0.6000000238418579,0.0 +2000-01-21 05:00:00,0.0,263.5,0.0,101250.0,0.003599999938160181,276.6000061035156,-2.799999952316284,-0.800000011920929,0.0 +2000-01-21 06:00:00,0.0,282.6000061035156,0.0,101280.0,0.0037999998312443495,276.8000183105469,-2.799999952316284,-1.100000023841858,0.0 +2000-01-21 07:00:00,0.0,282.6000061035156,0.0,101170.0,0.004100000020116568,278.5,-2.700000047683716,-1.0,0.0 +2000-01-21 08:00:00,0.0,282.6000061035156,0.0,101220.0,0.003999999724328518,278.6000061035156,-2.5,-0.9000000357627869,0.0 +2000-01-21 09:00:00,0.0,330.3000183105469,0.0,101140.0,0.004100000020116568,279.20001220703125,-2.4000000953674316,-0.800000011920929,0.0 +2000-01-21 10:00:00,0.0,330.20001220703125,0.0,101010.0,0.00419999985024333,278.8999938964844,-2.299999952316284,-0.800000011920929,0.0 +2000-01-21 11:00:00,0.0,330.20001220703125,0.0,101030.0,0.004100000020116568,278.6000061035156,-2.200000047683716,-0.699999988079071,0.0 +2000-01-21 12:00:00,0.0,351.8000183105469,0.0,101090.0,0.004299999680370092,279.0,-2.1000001430511475,-0.6000000238418579,0.0 +2000-01-21 13:00:00,0.0,351.8000183105469,33.5,101070.0,0.004399999976158142,279.3000183105469,-2.1000001430511475,-0.5,0.0 +2000-01-21 14:00:00,0.0,351.70001220703125,99.80000305175781,101030.0,0.0044999998062849045,280.6000061035156,-2.0,-0.4000000059604645,0.0 +2000-01-21 15:00:00,0.0,371.20001220703125,153.90000915527344,101060.0,0.004399999976158142,281.70001220703125,-2.0,-0.20000000298023224,0.0 +2000-01-21 16:00:00,0.0,371.20001220703125,192.1999969482422,101010.0,0.004900000058114529,283.70001220703125,-1.899999976158142,0.10000000149011612,0.0 +2000-01-21 17:00:00,0.0,371.20001220703125,204.40000915527344,101000.0,0.00559999980032444,285.1000061035156,-1.8000000715255737,0.4000000059604645,0.0 +2000-01-21 18:00:00,0.0,374.8999938964844,271.5,100920.0,0.00559999980032444,284.3999938964844,-1.7000000476837158,0.699999988079071,0.0 +2000-01-21 19:00:00,0.0,374.8999938964844,247.8000030517578,100740.0,0.006099999882280827,285.70001220703125,-1.8000000715255737,0.699999988079071,0.0 +2000-01-21 20:00:00,0.0,374.8999938964844,189.0,100660.0,0.0064999996684491634,286.5,-1.899999976158142,0.699999988079071,0.0 +2000-01-21 21:00:00,0.0,361.3999938964844,124.20000457763672,100640.0,0.0064999996684491634,287.0,-2.0,0.800000011920929,0.0 +2000-01-21 22:00:00,0.0,361.3999938964844,23.30000114440918,100580.0,0.006599999964237213,285.70001220703125,-2.1000001430511475,1.0,0.0 +2000-01-21 23:00:00,0.0,361.3999938964844,0.0,100570.0,0.0064999996684491634,283.8000183105469,-2.299999952316284,1.100000023841858,0.0 +2000-01-22 00:00:00,0.0,354.8000183105469,0.0,100500.0,0.006599999964237213,283.1000061035156,-2.4000000953674316,1.3000000715255737,0.0 +2000-01-22 01:00:00,0.0,354.8000183105469,0.0,100450.0,0.006899999920278788,282.70001220703125,-2.299999952316284,1.399999976158142,0.0 +2000-01-22 02:00:00,0.0,354.8000183105469,0.0,100430.0,0.006899999920278788,283.0,-2.299999952316284,1.399999976158142,0.0 +2000-01-22 03:00:00,0.0,367.8000183105469,0.0,100370.0,0.00699999975040555,282.8000183105469,-2.200000047683716,1.399999976158142,0.0 +2000-01-22 04:00:00,0.0,367.8000183105469,0.0,100350.0,0.0072999997064471245,283.20001220703125,-2.4000000953674316,1.600000023841858,0.0 +2000-01-22 05:00:00,0.0,367.8000183105469,0.0,100260.0,0.0072999997064471245,283.3000183105469,-2.6000001430511475,1.7000000476837158,8.335614187810817e-08 +2000-01-22 06:00:00,0.30000001192092896,387.20001220703125,0.0,100190.0,0.007799999788403511,283.3000183105469,-2.799999952316284,1.8000000715255737,5.279222042923904e-07 +2000-01-22 07:00:00,1.899999976158142,387.20001220703125,0.0,100020.0,0.007899999618530273,283.3000183105469,-1.600000023841858,2.0,2.52846964248974e-06 +2000-01-22 08:00:00,9.100000381469727,387.20001220703125,0.0,99900.0,0.008200000040233135,284.0,-0.4000000059604645,2.1000001430511475,1.4171493158587097e-06 +2000-01-22 09:00:00,5.099999904632568,387.8999938964844,0.0,99850.0,0.008899999782443047,285.20001220703125,0.800000011920929,2.200000047683716,1.0477138035473381e-05 +2000-01-22 10:00:00,37.70000076293945,387.8999938964844,0.0,99830.0,0.009099999442696571,285.8000183105469,0.800000011920929,2.700000047683716,1.7787380935536381e-06 +2000-01-22 11:00:00,6.400000095367432,387.8999938964844,0.0,99800.0,0.009200000204145908,285.8999938964844,0.800000011920929,3.1000001430511475,1.361862970574917e-06 +2000-01-22 12:00:00,4.900000095367432,380.1000061035156,0.0,99870.0,0.009599999524652958,286.0,0.800000011920929,3.5,2.223477127827171e-07 +2000-01-22 13:00:00,0.800000011920929,380.1000061035156,25.0,99870.0,0.010300000198185444,287.3999938964844,0.6000000238418579,3.700000047683716,5.5597193987443644e-08 +2000-01-22 14:00:00,0.20000000298023224,380.1000061035156,75.4000015258789,99940.0,0.010499999858438969,288.1000061035156,0.4000000059604645,3.9000000953674316,0.0 +2000-01-22 15:00:00,0.0,383.3000183105469,231.8000030517578,99970.0,0.010799999348819256,288.1000061035156,0.30000001192092896,4.0,0.0 +2000-01-22 16:00:00,0.0,383.3000183105469,289.8000183105469,99980.0,0.011099999770522118,288.70001220703125,0.30000001192092896,3.799999952316284,0.0 +2000-01-22 17:00:00,0.0,383.3000183105469,308.3999938964844,99910.0,0.010999999940395355,288.5,0.30000001192092896,3.5,0.0 +2000-01-22 18:00:00,0.0,395.20001220703125,297.70001220703125,99830.0,0.010799999348819256,288.6000061035156,0.30000001192092896,3.299999952316284,0.0 +2000-01-22 19:00:00,0.0,395.20001220703125,272.0,99680.0,0.012000000104308128,290.3999938964844,-0.20000000298023224,3.5,0.0 +2000-01-22 20:00:00,0.0,395.20001220703125,207.8000030517578,99720.0,0.01209999993443489,290.6000061035156,-0.6000000238418579,3.799999952316284,0.0 +2000-01-22 21:00:00,0.0,386.5,122.5,99820.0,0.01209999993443489,290.1000061035156,-1.100000023841858,4.0,0.0 +2000-01-22 22:00:00,0.0,386.5,23.700000762939453,99740.0,0.011799999512732029,289.5,-1.3000000715255737,3.5,0.0 +2000-01-22 23:00:00,0.0,386.5,0.0,99720.0,0.011299999430775642,288.5,-1.600000023841858,3.0,0.0 +2000-01-23 00:00:00,0.0,387.3999938964844,0.0,99830.0,0.011299999430775642,288.8000183105469,-1.899999976158142,2.5,0.0 +2000-01-23 01:00:00,0.0,387.3999938964844,0.0,99800.0,0.011399999260902405,289.0,-1.899999976158142,2.200000047683716,0.0 +2000-01-23 02:00:00,0.0,387.3999938964844,0.0,99870.0,0.011500000022351742,289.20001220703125,-2.0,1.899999976158142,0.0 +2000-01-23 03:00:00,0.0,377.6000061035156,0.0,99870.0,0.011699999682605267,289.70001220703125,-2.0,1.600000023841858,0.0 +2000-01-23 04:00:00,0.0,377.6000061035156,0.0,99770.0,0.012000000104308128,289.8000183105469,-1.600000023841858,1.7000000476837158,0.0 +2000-01-23 05:00:00,0.0,377.6000061035156,0.0,99750.0,0.012000000104308128,289.8000183105469,-1.2000000476837158,1.8000000715255737,0.0 +2000-01-23 06:00:00,0.0,387.3999938964844,0.0,99750.0,0.01249999925494194,290.20001220703125,-0.699999988079071,1.899999976158142,2.7810727410571993e-08 +2000-01-23 07:00:00,0.10000000149011612,387.3999938964844,0.0,99680.0,0.01249999925494194,290.5,-1.100000023841858,2.0,0.0 +2000-01-23 08:00:00,0.0,387.3999938964844,0.0,99670.0,0.01269999984651804,290.3000183105469,-1.399999976158142,2.1000001430511475,0.0 +2000-01-23 09:00:00,0.0,387.0,0.0,99730.0,0.01269999984651804,290.5,-1.8000000715255737,2.1000001430511475,0.0 +2000-01-23 10:00:00,0.0,387.0,0.0,99650.0,0.01249999925494194,290.3000183105469,-1.8000000715255737,2.200000047683716,0.0 +2000-01-23 11:00:00,0.0,387.0,0.0,99710.0,0.012299999594688416,290.0,-1.899999976158142,2.200000047683716,0.0 +2000-01-23 12:00:00,0.0,387.3999938964844,0.0,99650.0,0.012399999424815178,290.3000183105469,-2.0,2.200000047683716,3.893568673245631e-07 +2000-01-23 13:00:00,1.399999976158142,387.3999938964844,27.30000114440918,99620.0,0.012399999424815178,290.5,-2.1000001430511475,2.799999952316284,3.893703857757011e-07 +2000-01-23 14:00:00,1.399999976158142,387.3999938964844,83.4000015258789,99640.0,0.01269999984651804,290.3999938964844,-2.299999952316284,3.4000000953674316,1.1124674593096233e-06 +2000-01-23 15:00:00,4.0,390.5,159.1999969482422,99710.0,0.012799999676644802,291.0,-2.4000000953674316,4.099999904632568,1.001327023947647e-06 +2000-01-23 16:00:00,3.6000001430511475,390.5,199.3000030517578,99610.0,0.013199999928474426,291.8999938964844,-1.7000000476837158,4.300000190734863,7.511203185325428e-07 +2000-01-23 17:00:00,2.700000047683716,390.5,212.1999969482422,99480.0,0.013700000010430813,292.20001220703125,-1.0,4.599999904632568,0.0 +2000-01-23 18:00:00,0.0,403.8999938964844,175.0,99390.0,0.014299999922513962,293.1000061035156,-0.30000001192092896,4.800000190734863,3.617357214924627e-07 +2000-01-23 19:00:00,1.3000000715255737,403.8999938964844,160.0,99270.0,0.0142000000923872,292.3999938964844,0.0,4.800000190734863,2.6152633535538457e-06 +2000-01-23 20:00:00,9.40000057220459,403.8999938964844,122.4000015258789,99380.0,0.012899999506771564,291.3000183105469,0.30000001192092896,4.800000190734863,0.0 +2000-01-23 21:00:00,0.0,392.6000061035156,82.5999984741211,99520.0,0.013199999928474426,291.3000183105469,0.6000000238418579,4.800000190734863,3.059777168752196e-07 +2000-01-23 22:00:00,1.100000023841858,392.6000061035156,16.399999618530273,99480.0,0.012799999676644802,290.8000183105469,0.4000000059604645,4.0,5.562728561571695e-08 +2000-01-23 23:00:00,0.20000000298023224,392.6000061035156,0.0,99500.0,0.012399999424815178,290.3000183105469,0.10000000149011612,3.200000047683716,3.337344766516485e-07 +2000-01-24 00:00:00,1.2000000476837158,396.8999938964844,0.0,99600.0,0.012399999424815178,290.3999938964844,-0.10000000149011612,2.299999952316284,3.893636041275454e-07 +2000-01-24 01:00:00,1.399999976158142,396.8000183105469,0.0,99590.0,0.011799999512732029,289.8000183105469,-0.800000011920929,2.6000001430511475,5.283680425381081e-07 +2000-01-24 02:00:00,1.899999976158142,396.8000183105469,0.0,99720.0,0.012399999424815178,290.3999938964844,-1.399999976158142,2.9000000953674316,5.840454559224889e-07 +2000-01-24 03:00:00,2.1000001430511475,395.20001220703125,0.0,99860.0,0.01209999993443489,290.20001220703125,-2.1000001430511475,3.200000047683716,8.34321809884796e-07 +2000-01-24 04:00:00,3.0,395.20001220703125,0.0,99710.0,0.012199999764561653,290.5,-1.5,4.099999904632568,0.0 +2000-01-24 05:00:00,0.0,395.20001220703125,0.0,99680.0,0.012199999764561653,290.20001220703125,-0.9000000357627869,5.099999904632568,2.2248581928457594e-07 +2000-01-24 06:00:00,0.800000011920929,394.20001220703125,0.0,99670.0,0.012299999594688416,290.5,-0.30000001192092896,6.0,0.0 +2000-01-24 07:00:00,0.0,394.20001220703125,0.0,99650.0,0.012600000016391277,291.0,0.0,6.0,0.0 +2000-01-24 08:00:00,0.0,394.20001220703125,0.0,99660.0,0.013599999248981476,291.8000183105469,0.30000001192092896,6.0,0.0 +2000-01-24 09:00:00,0.0,383.70001220703125,0.0,99700.0,0.0142000000923872,292.8999938964844,0.6000000238418579,5.900000095367432,0.0 +2000-01-24 10:00:00,0.0,383.70001220703125,0.0,99740.0,0.014599999412894249,292.8000183105469,0.30000001192092896,5.0,0.0 +2000-01-24 11:00:00,0.0,383.70001220703125,0.0,99820.0,0.014699999243021011,293.3999938964844,0.0,4.0,0.0 +2000-01-24 12:00:00,0.0,376.3000183105469,0.0,99870.0,0.014399999752640724,293.5,-0.30000001192092896,3.0,0.0 +2000-01-24 13:00:00,0.0,376.3000183105469,46.79999923706055,99920.0,0.014699999243021011,293.8000183105469,0.30000001192092896,3.299999952316284,0.0 +2000-01-24 14:00:00,0.0,376.3000183105469,144.60000610351562,100040.0,0.014999999664723873,294.3000183105469,0.9000000357627869,3.6000001430511475,0.0 +2000-01-24 15:00:00,0.0,395.6000061035156,284.70001220703125,100150.0,0.01529999915510416,294.6000061035156,1.5,3.9000000953674316,0.0 +2000-01-24 16:00:00,0.0,395.6000061035156,356.70001220703125,100100.0,0.015399999916553497,295.5,2.700000047683716,3.799999952316284,0.0 +2000-01-24 17:00:00,0.0,395.6000061035156,380.1000061035156,100110.0,0.01599999889731407,295.5,3.9000000953674316,3.700000047683716,0.0 +2000-01-24 18:00:00,0.0,396.1000061035156,334.20001220703125,100100.0,0.01589999906718731,295.3000183105469,5.099999904632568,3.6000001430511475,0.0 +2000-01-24 19:00:00,0.0,396.1000061035156,305.8000183105469,100040.0,0.01549999974668026,295.5,4.5,3.200000047683716,0.0 +2000-01-24 20:00:00,0.0,396.1000061035156,234.3000030517578,100040.0,0.01599999889731407,295.0,3.9000000953674316,2.799999952316284,0.0 +2000-01-24 21:00:00,0.0,400.3000183105469,112.4000015258789,100090.0,0.015699999406933784,295.3999938964844,3.299999952316284,2.4000000953674316,0.0 +2000-01-24 22:00:00,0.0,400.3000183105469,23.0,100110.0,0.015799999237060547,294.5,2.1000001430511475,2.4000000953674316,0.0 +2000-01-24 23:00:00,0.0,400.3000183105469,0.0,100210.0,0.01529999915510416,294.3999938964844,0.9000000357627869,2.4000000953674316,8.350020135867138e-08 +2000-01-25 00:00:00,0.30000001192092896,385.5,0.0,100270.0,0.015599999576807022,294.6000061035156,-0.4000000059604645,2.4000000953674316,0.0 +2000-01-25 01:00:00,0.0,385.5,0.0,100300.0,0.013899999670684338,293.5,0.4000000059604645,2.200000047683716,5.5656204442663486e-08 +2000-01-25 02:00:00,0.20000000298023224,385.5,0.0,100370.0,0.013499999418854713,292.70001220703125,1.2000000476837158,2.1000001430511475,0.0 +2000-01-25 03:00:00,0.0,370.70001220703125,0.0,100450.0,0.012899999506771564,291.8999938964844,1.899999976158142,1.899999976158142,0.0 +2000-01-25 04:00:00,0.0,370.70001220703125,0.0,100510.0,0.012600000016391277,291.3999938964844,1.8000000715255737,1.5,2.7816667451248293e-08 +2000-01-25 05:00:00,0.10000000149011612,370.70001220703125,0.0,100490.0,0.012399999424815178,291.20001220703125,1.7000000476837158,1.100000023841858,0.0 +2000-01-25 06:00:00,0.0,383.0,0.0,100450.0,0.01269999984651804,291.3999938964844,1.5,0.699999988079071,0.0 +2000-01-25 07:00:00,0.0,383.0,0.0,100450.0,0.012899999506771564,291.5,1.3000000715255737,0.6000000238418579,0.0 +2000-01-25 08:00:00,0.0,383.0,0.0,100520.0,0.012299999594688416,291.1000061035156,1.100000023841858,0.4000000059604645,2.7815141514545572e-08 +2000-01-25 09:00:00,0.10000000149011612,391.5,0.0,100570.0,0.012399999424815178,290.8000183105469,1.0,0.30000001192092896,0.0 +2000-01-25 10:00:00,0.0,391.5,0.0,100490.0,0.012000000104308128,290.5,0.10000000149011612,0.5,0.0 +2000-01-25 11:00:00,0.0,391.5,0.0,100510.0,0.011799999512732029,290.20001220703125,-0.800000011920929,0.699999988079071,0.0 +2000-01-25 12:00:00,0.0,381.5,0.0,100620.0,0.011599999852478504,289.8999938964844,-1.7000000476837158,0.9000000357627869,0.0 +2000-01-25 13:00:00,0.0,381.5,53.5,100550.0,0.011799999512732029,290.1000061035156,-1.3000000715255737,1.0,0.0 +2000-01-25 14:00:00,0.0,381.5,167.3000030517578,100590.0,0.012600000016391277,291.1000061035156,-0.800000011920929,1.2000000476837158,0.0 +2000-01-25 15:00:00,0.0,388.8999938964844,312.20001220703125,100630.0,0.013700000010430813,292.8999938964844,-0.4000000059604645,1.399999976158142,0.0 +2000-01-25 16:00:00,0.0,388.8999938964844,391.6000061035156,100620.0,0.013899999670684338,293.70001220703125,0.0,1.899999976158142,0.0 +2000-01-25 17:00:00,0.0,388.8999938964844,417.70001220703125,100560.0,0.0142000000923872,294.1000061035156,0.30000001192092896,2.4000000953674316,0.0 +2000-01-25 18:00:00,0.0,402.3000183105469,391.20001220703125,100450.0,0.01489999983459711,294.20001220703125,0.699999988079071,2.9000000953674316,0.0 +2000-01-25 19:00:00,0.0,402.3000183105469,358.3999938964844,100380.0,0.015099999494850636,294.8000183105469,1.2000000476837158,2.799999952316284,0.0 +2000-01-25 20:00:00,0.0,402.3000183105469,274.8999938964844,100410.0,0.01549999974668026,295.8000183105469,1.600000023841858,2.799999952316284,0.0 +2000-01-25 21:00:00,0.0,398.0,125.5,100440.0,0.015599999576807022,296.20001220703125,2.1000001430511475,2.700000047683716,0.0 +2000-01-25 22:00:00,0.0,398.0,26.399999618530273,100440.0,0.014999999664723873,294.8999938964844,1.3000000715255737,2.5,0.0 +2000-01-25 23:00:00,0.0,398.1000061035156,0.0,100540.0,0.014800000004470348,294.1000061035156,0.5,2.299999952316284,3.3397930247683724e-07 +2000-01-26 00:00:00,1.2000000476837158,396.6000061035156,0.0,100590.0,0.014399999752640724,293.70001220703125,-0.30000001192092896,2.200000047683716,1.029682587294264e-06 +2000-01-26 01:00:00,3.700000047683716,396.6000061035156,0.0,100590.0,0.013700000010430813,292.8999938964844,-0.6000000238418579,1.600000023841858,5.84318769949322e-07 +2000-01-26 02:00:00,2.1000001430511475,396.6000061035156,0.0,100610.0,0.013399999588727951,291.8999938964844,-0.9000000357627869,1.0,0.0 +2000-01-26 03:00:00,0.0,391.6000061035156,0.0,100620.0,0.012600000016391277,291.70001220703125,-1.2000000476837158,0.4000000059604645,0.0 +2000-01-26 04:00:00,0.0,391.6000061035156,0.0,100610.0,0.01209999993443489,291.20001220703125,-1.0,0.20000000298023224,0.0 +2000-01-26 05:00:00,0.0,391.6000061035156,0.0,100600.0,0.011500000022351742,290.0,-0.699999988079071,-0.10000000149011612,0.0 +2000-01-26 06:00:00,0.0,388.8999938964844,0.0,100670.0,0.011899999342858791,290.3000183105469,-0.5,-0.4000000059604645,0.0 +2000-01-26 07:00:00,0.0,388.8999938964844,0.0,100600.0,0.011799999512732029,290.0,-0.9000000357627869,-0.30000001192092896,0.0 +2000-01-26 08:00:00,0.0,388.8999938964844,0.0,100670.0,0.011399999260902405,289.8000183105469,-1.399999976158142,-0.30000001192092896,0.0 +2000-01-26 09:00:00,0.0,382.6000061035156,0.0,100600.0,0.01119999960064888,289.70001220703125,-1.8000000715255737,-0.20000000298023224,0.0 +2000-01-26 10:00:00,0.0,382.6000061035156,0.0,100500.0,0.011299999430775642,289.8000183105469,-1.5,-0.30000001192092896,0.0 +2000-01-26 11:00:00,0.0,382.6000061035156,0.0,100490.0,0.011399999260902405,289.8000183105469,-1.100000023841858,-0.5,0.0 +2000-01-26 12:00:00,0.0,360.8000183105469,0.0,100570.0,0.011500000022351742,289.8999938964844,-0.800000011920929,-0.6000000238418579,0.0 +2000-01-26 13:00:00,0.0,360.8000183105469,62.400001525878906,100530.0,0.011799999512732029,290.1000061035156,-1.0,-0.6000000238418579,0.0 +2000-01-26 14:00:00,0.0,360.8000183105469,197.3000030517578,100700.0,0.011599999852478504,290.5,-1.2000000476837158,-0.5,0.0 +2000-01-26 15:00:00,0.0,373.6000061035156,345.3999938964844,100840.0,0.012999999336898327,292.3999938964844,-1.399999976158142,-0.5,0.0 +2000-01-26 16:00:00,0.0,373.6000061035156,433.8000183105469,100860.0,0.013299999758601189,293.20001220703125,-1.100000023841858,0.4000000059604645,0.0 +2000-01-26 17:00:00,0.0,373.6000061035156,463.1000061035156,100690.0,0.0139999995008111,293.8999938964844,-0.9000000357627869,1.3000000715255737,0.0 +2000-01-26 18:00:00,0.0,384.20001220703125,459.1000061035156,100660.0,0.014299999922513962,295.5,-0.699999988079071,2.200000047683716,0.0 +2000-01-26 19:00:00,0.0,384.20001220703125,420.8999938964844,100510.0,0.01489999983459711,296.8000183105469,-0.699999988079071,2.1000001430511475,0.0 +2000-01-26 20:00:00,0.0,384.20001220703125,323.3999938964844,100540.0,0.013700000010430813,297.3000183105469,-0.699999988079071,2.1000001430511475,0.0 +2000-01-26 21:00:00,0.0,371.8999938964844,146.5,100510.0,0.013899999670684338,296.3999938964844,-0.699999988079071,2.0,0.0 +2000-01-26 22:00:00,0.0,371.8999938964844,31.80000114440918,100560.0,0.014299999922513962,294.8999938964844,-1.100000023841858,2.1000001430511475,0.0 +2000-01-26 23:00:00,0.0,371.8999938964844,0.0,100600.0,0.0139999995008111,293.70001220703125,-1.600000023841858,2.200000047683716,0.0 +2000-01-27 00:00:00,0.0,361.6000061035156,0.0,100640.0,0.013599999248981476,292.6000061035156,-2.0,2.299999952316284,0.0 +2000-01-27 01:00:00,0.0,361.6000061035156,0.0,100570.0,0.013299999758601189,291.8000183105469,-2.1000001430511475,2.1000001430511475,0.0 +2000-01-27 02:00:00,0.0,361.6000061035156,0.0,100620.0,0.013299999758601189,292.1000061035156,-2.200000047683716,1.899999976158142,0.0 +2000-01-27 03:00:00,0.0,351.3999938964844,0.0,100610.0,0.013499999418854713,291.8999938964844,-2.200000047683716,1.7000000476837158,0.0 +2000-01-27 04:00:00,0.0,351.3999938964844,0.0,100580.0,0.013100000098347664,291.70001220703125,-2.1000001430511475,1.899999976158142,0.0 +2000-01-27 05:00:00,0.0,351.3999938964844,0.0,100530.0,0.013199999928474426,292.0,-1.899999976158142,2.1000001430511475,0.0 +2000-01-27 06:00:00,0.0,362.20001220703125,0.0,100470.0,0.013299999758601189,292.1000061035156,-1.8000000715255737,2.299999952316284,0.0 +2000-01-27 07:00:00,0.0,362.20001220703125,0.0,100350.0,0.013399999588727951,292.20001220703125,-1.7000000476837158,2.1000001430511475,0.0 +2000-01-27 08:00:00,0.0,362.20001220703125,0.0,100410.0,0.013399999588727951,292.0,-1.7000000476837158,1.899999976158142,0.0 +2000-01-27 09:00:00,0.0,380.3000183105469,0.0,100420.0,0.013499999418854713,292.5,-1.600000023841858,1.7000000476837158,0.0 +2000-01-27 10:00:00,0.0,380.3000183105469,0.0,100380.0,0.013499999418854713,292.3000183105469,-1.7000000476837158,1.7000000476837158,0.0 +2000-01-27 11:00:00,0.0,380.3000183105469,0.0,100410.0,0.013499999418854713,292.6000061035156,-1.8000000715255737,1.7000000476837158,0.0 +2000-01-27 12:00:00,0.0,366.3999938964844,0.0,100430.0,0.013299999758601189,292.20001220703125,-1.8000000715255737,1.7000000476837158,0.0 +2000-01-27 13:00:00,0.0,366.3999938964844,36.29999923706055,100320.0,0.012999999336898327,292.3000183105469,-1.2000000476837158,1.2000000476837158,0.0 +2000-01-27 14:00:00,0.0,366.3999938964844,115.80000305175781,100360.0,0.013499999418854713,292.3000183105469,-0.6000000238418579,0.699999988079071,2.7821407388965253e-07 +2000-01-27 15:00:00,1.0,388.70001220703125,184.6999969482422,100370.0,0.013399999588727951,292.3999938964844,0.0,0.20000000298023224,1.0572340440260603e-06 +2000-01-27 16:00:00,3.799999952316284,388.70001220703125,232.1999969482422,100310.0,0.013499999418854713,292.5,0.4000000059604645,0.5,0.0 +2000-01-27 17:00:00,0.0,388.70001220703125,248.10000610351562,100190.0,0.014099999330937862,293.6000061035156,0.699999988079071,0.9000000357627869,0.0 +2000-01-27 18:00:00,0.0,383.5,333.3000183105469,100120.0,0.01489999983459711,294.0,1.100000023841858,1.2000000476837158,0.0 +2000-01-27 19:00:00,0.0,383.5,305.8999938964844,99960.0,0.014999999664723873,294.8999938964844,1.2000000476837158,1.3000000715255737,0.0 +2000-01-27 20:00:00,0.0,383.5,235.40000915527344,99970.0,0.014999999664723873,295.5,1.2000000476837158,1.5,5.568038368734538e-08 +2000-01-27 21:00:00,0.20000000298023224,375.20001220703125,143.0,100000.0,0.015199999324977398,294.8000183105469,1.3000000715255737,1.7000000476837158,0.0 +2000-01-27 22:00:00,0.0,375.20001220703125,32.0,100010.0,0.015099999494850636,294.3999938964844,0.699999988079071,1.899999976158142,0.0 +2000-01-27 23:00:00,0.0,375.20001220703125,0.0,99990.0,0.014699999243021011,293.3000183105469,0.10000000149011612,2.1000001430511475,0.0 +2000-01-28 00:00:00,0.0,368.1000061035156,0.0,100000.0,0.014800000004470348,293.20001220703125,-0.4000000059604645,2.299999952316284,0.0 +2000-01-28 01:00:00,0.0,368.20001220703125,0.0,99980.0,0.013599999248981476,292.3000183105469,-0.699999988079071,2.200000047683716,0.0 +2000-01-28 02:00:00,0.0,368.20001220703125,0.0,100020.0,0.012999999336898327,291.3000183105469,-1.100000023841858,2.1000001430511475,0.0 +2000-01-28 03:00:00,0.0,338.3000183105469,0.0,100090.0,0.013100000098347664,291.0,-1.399999976158142,2.0,0.0 +2000-01-28 04:00:00,0.0,338.3000183105469,0.0,100110.0,0.012999999336898327,291.70001220703125,-1.399999976158142,2.200000047683716,0.0 +2000-01-28 05:00:00,0.0,338.3000183105469,0.0,100090.0,0.013199999928474426,291.8000183105469,-1.399999976158142,2.299999952316284,0.0 +2000-01-28 06:00:00,0.0,341.3000183105469,0.0,100050.0,0.013499999418854713,292.3999938964844,-1.5,2.4000000953674316,0.0 +2000-01-28 07:00:00,0.0,341.3000183105469,0.0,100040.0,0.013100000098347664,292.1000061035156,-1.8000000715255737,1.899999976158142,0.0 +2000-01-28 08:00:00,0.0,341.3000183105469,0.0,100030.0,0.013399999588727951,291.8999938964844,-2.1000001430511475,1.5,0.0 +2000-01-28 09:00:00,0.0,341.1000061035156,0.0,100040.0,0.013100000098347664,291.8999938964844,-2.4000000953674316,1.0,0.0 +2000-01-28 10:00:00,0.0,341.1000061035156,0.0,100000.0,0.012899999506771564,291.6000061035156,-2.5,1.0,0.0 +2000-01-28 11:00:00,0.0,341.1000061035156,0.0,100000.0,0.012999999336898327,291.70001220703125,-2.5,0.9000000357627869,0.0 +2000-01-28 12:00:00,0.0,346.5,0.0,100070.0,0.013100000098347664,291.3999938964844,-2.6000001430511475,0.800000011920929,0.0 +2000-01-28 13:00:00,0.0,346.5,54.400001525878906,100060.0,0.012899999506771564,291.8000183105469,-2.5,0.4000000059604645,0.0 +2000-01-28 14:00:00,0.0,346.5,175.5,100080.0,0.013100000098347664,291.70001220703125,-2.4000000953674316,0.0,0.0 +2000-01-28 15:00:00,0.0,378.70001220703125,257.8000183105469,100090.0,0.013799999840557575,292.8000183105469,-2.299999952316284,-0.4000000059604645,0.0 +2000-01-28 16:00:00,0.0,378.70001220703125,324.5,100100.0,0.014399999752640724,293.3999938964844,-3.299999952316284,0.10000000149011612,0.0 +2000-01-28 17:00:00,0.0,378.70001220703125,346.8999938964844,100020.0,0.014800000004470348,293.8999938964844,-4.300000190734863,0.6000000238418579,0.0 +2000-01-28 18:00:00,0.0,403.6000061035156,251.8000030517578,99920.0,0.015399999916553497,294.8999938964844,-5.300000190734863,1.100000023841858,0.0 +2000-01-28 19:00:00,0.0,403.6000061035156,231.3000030517578,99800.0,0.01549999974668026,296.6000061035156,-5.599999904632568,1.5,0.0 +2000-01-28 20:00:00,0.0,403.6000061035156,178.3000030517578,99710.0,0.01489999983459711,297.0,-5.800000190734863,2.0,0.0 +2000-01-28 21:00:00,0.0,388.5,106.80000305175781,99730.0,0.014800000004470348,296.0,-6.099999904632568,2.4000000953674316,0.0 +2000-01-28 22:00:00,0.0,388.5,24.600000381469727,99660.0,0.01589999906718731,295.1000061035156,-6.200000286102295,2.5,2.2270145581262254e-07 +2000-01-28 23:00:00,0.800000011920929,388.5,0.0,99710.0,0.015399999916553497,294.3999938964844,-6.400000095367432,2.700000047683716,0.0 +2000-01-29 00:00:00,0.0,377.70001220703125,0.0,99760.0,0.015099999494850636,294.1000061035156,-6.599999904632568,2.799999952316284,0.0 +2000-01-29 01:00:00,0.0,377.70001220703125,0.0,99740.0,0.01549999974668026,294.8000183105469,-5.900000095367432,2.799999952316284,0.0 +2000-01-29 02:00:00,0.0,377.70001220703125,0.0,99680.0,0.015699999406933784,294.8999938964844,-5.099999904632568,2.799999952316284,0.0 +2000-01-29 03:00:00,0.0,382.1000061035156,0.0,99740.0,0.0142000000923872,293.70001220703125,-4.300000190734863,2.799999952316284,0.0 +2000-01-29 04:00:00,0.0,382.1000061035156,0.0,99670.0,0.014499999582767487,293.6000061035156,-3.5,3.4000000953674316,0.0 +2000-01-29 05:00:00,0.0,382.1000061035156,0.0,99710.0,0.013899999670684338,293.3000183105469,-2.700000047683716,3.9000000953674316,2.226156571250332e-07 +2000-01-29 06:00:00,0.800000011920929,397.3000183105469,0.0,99650.0,0.013899999670684338,293.3000183105469,-1.8000000715255737,4.5,6.400199914285021e-07 +2000-01-29 07:00:00,2.299999952316284,397.3000183105469,0.0,99510.0,0.014999999664723873,294.20001220703125,-0.800000011920929,5.0,9.462948913342166e-07 +2000-01-29 08:00:00,3.4000000953674316,397.3000183105469,0.0,99600.0,0.014499999582767487,293.70001220703125,0.20000000298023224,5.5,1.6975847576620752e-06 +2000-01-29 09:00:00,6.099999904632568,378.8999938964844,0.0,99620.0,0.014399999752640724,293.8999938964844,1.2000000476837158,6.0,4.7311727553386857e-07 +2000-01-29 10:00:00,1.7000000476837158,378.8999938964844,0.0,99620.0,0.014499999582767487,293.3000183105469,2.200000047683716,5.5,2.226156571250332e-07 +2000-01-29 11:00:00,0.800000011920929,379.0,0.0,99670.0,0.014800000004470348,293.3999938964844,3.200000047683716,5.0,0.0 +2000-01-29 12:00:00,0.0,379.5,0.0,99710.0,0.014699999243021011,293.6000061035156,4.300000190734863,4.5,0.0 +2000-01-29 13:00:00,0.0,379.5,39.20000076293945,99770.0,0.014800000004470348,293.8999938964844,3.9000000953674316,4.099999904632568,0.0 +2000-01-29 14:00:00,0.0,379.5,127.4000015258789,99850.0,0.014999999664723873,293.70001220703125,3.6000001430511475,3.700000047683716,0.0 +2000-01-29 15:00:00,0.0,368.70001220703125,257.20001220703125,99950.0,0.015799999237060547,295.20001220703125,3.299999952316284,3.4000000953674316,0.0 +2000-01-29 16:00:00,0.0,368.70001220703125,324.0,99990.0,0.014699999243021011,294.70001220703125,3.4000000953674316,3.5,0.0 +2000-01-29 17:00:00,0.0,368.70001220703125,346.70001220703125,99900.0,0.0142000000923872,294.8000183105469,3.4000000953674316,3.6000001430511475,0.0 +2000-01-29 18:00:00,0.0,367.3999938964844,399.3999938964844,99890.0,0.013700000010430813,295.0,3.5,3.700000047683716,0.0 +2000-01-29 19:00:00,0.0,367.3999938964844,367.20001220703125,99790.0,0.013899999670684338,296.6000061035156,3.1000001430511475,3.299999952316284,0.0 +2000-01-29 20:00:00,0.0,367.3999938964844,283.5,99810.0,0.014299999922513962,296.5,2.700000047683716,2.9000000953674316,0.0 +2000-01-29 21:00:00,0.0,366.1000061035156,140.10000610351562,99950.0,0.013599999248981476,296.1000061035156,2.299999952316284,2.6000001430511475,0.0 +2000-01-29 22:00:00,0.0,366.1000061035156,33.20000076293945,99950.0,0.013399999588727951,295.20001220703125,2.1000001430511475,2.200000047683716,0.0 +2000-01-29 23:00:00,0.0,366.1000061035156,0.0,100020.0,0.013899999670684338,293.8000183105469,1.899999976158142,1.8000000715255737,0.0 +2000-01-30 00:00:00,0.0,334.8999938964844,0.0,100130.0,0.013100000098347664,292.5,1.600000023841858,1.5,0.0 +2000-01-30 01:00:00,0.0,334.8999938964844,0.0,100070.0,0.013799999840557575,293.1000061035156,1.2000000476837158,1.399999976158142,0.0 +2000-01-30 02:00:00,0.0,334.8999938964844,0.0,100220.0,0.013100000098347664,292.3999938964844,0.699999988079071,1.3000000715255737,0.0 +2000-01-30 03:00:00,0.0,334.0,0.0,100290.0,0.013100000098347664,291.5,0.20000000298023224,1.100000023841858,0.0 +2000-01-30 04:00:00,0.0,334.0,0.0,100270.0,0.012399999424815178,291.1000061035156,0.10000000149011612,1.2000000476837158,0.0 +2000-01-30 05:00:00,0.0,334.0,0.0,100310.0,0.012399999424815178,290.8000183105469,-0.10000000149011612,1.3000000715255737,0.0 +2000-01-30 06:00:00,0.0,346.8999938964844,0.0,100360.0,0.012299999594688416,290.6000061035156,-0.20000000298023224,1.399999976158142,0.0 +2000-01-30 07:00:00,0.0,346.8999938964844,0.0,100330.0,0.012000000104308128,290.6000061035156,-0.6000000238418579,0.699999988079071,0.0 +2000-01-30 08:00:00,0.0,346.8999938964844,0.0,100330.0,0.012399999424815178,291.0,-1.0,0.0,0.0 +2000-01-30 09:00:00,0.0,381.3999938964844,0.0,100340.0,0.012399999424815178,290.8000183105469,-1.5,-0.699999988079071,1.390682119670145e-07 +2000-01-30 10:00:00,0.5,381.3999938964844,0.0,100300.0,0.012199999764561653,290.70001220703125,-1.600000023841858,-0.5,0.0 +2000-01-30 11:00:00,0.0,381.3999938964844,0.0,100300.0,0.012299999594688416,291.1000061035156,-1.7000000476837158,-0.20000000298023224,2.781514110006767e-07 +2000-01-30 12:00:00,1.0,397.3999938964844,0.0,100310.0,0.012999999336898327,291.5,-1.899999976158142,0.10000000149011612,0.0 +2000-01-30 13:00:00,0.0,397.3999938964844,28.200000762939453,100300.0,0.012799999676644802,292.0,-1.2000000476837158,0.30000001192092896,0.0 +2000-01-30 14:00:00,0.0,397.3999938964844,92.4000015258789,100270.0,0.013299999758601189,292.3999938964844,-0.5,0.6000000238418579,0.0 +2000-01-30 15:00:00,0.0,399.0,137.8000030517578,100340.0,0.014299999922513962,293.3999938964844,0.20000000298023224,0.800000011920929,1.1409286097882968e-06 +2000-01-30 16:00:00,4.099999904632568,399.0,173.6999969482422,100440.0,0.0139999995008111,292.8999938964844,0.4000000059604645,1.399999976158142,5.731888601858435e-06 +2000-01-30 17:00:00,20.600000381469727,399.0,186.0,100250.0,0.013799999840557575,292.6000061035156,0.6000000238418579,2.1000001430511475,3.950871781250009e-06 +2000-01-30 18:00:00,14.199999809265137,401.0,181.8000030517578,100330.0,0.012799999676644802,291.70001220703125,0.800000011920929,2.799999952316284,7.510919598994889e-07 +2000-01-30 19:00:00,2.700000047683716,401.0,167.40000915527344,100240.0,0.012399999424815178,291.6000061035156,0.699999988079071,2.299999952316284,6.119894023402888e-07 +2000-01-30 20:00:00,2.200000047683716,401.0,129.40000915527344,100220.0,0.012799999676644802,291.70001220703125,0.699999988079071,1.899999976158142,3.6163688309164025e-07 +2000-01-30 21:00:00,1.3000000715255737,396.70001220703125,75.30000305175781,100190.0,0.012799999676644802,291.3000183105469,0.6000000238418579,1.5,4.45058494258552e-07 +2000-01-30 22:00:00,1.600000023841858,396.70001220703125,18.399999618530273,100230.0,0.01269999984651804,291.0,1.0,1.100000023841858,1.1404001498766157e-06 +2000-01-30 23:00:00,4.099999904632568,396.70001220703125,0.0,100260.0,0.012600000016391277,290.8000183105469,1.399999976158142,0.699999988079071,4.172046359010436e-07 diff --git a/test_ngen/example_realization_config_w_summa_bmi.json b/test_ngen/example_realization_config_w_summa_bmi.json index 5e28da0ec..92791c606 100644 --- a/test_ngen/example_realization_config_w_summa_bmi.json +++ b/test_ngen/example_realization_config_w_summa_bmi.json @@ -7,7 +7,7 @@ "model_type_name": "bmi_fortran_summa", "library_file": "./extern/summa/cmake_build/libsummabmi.so", "forcing_file": "", - "init_config": "./data/bmi/fortran/summa-init-{{id}}.namelist.input", + "init_config": ".//extern/summa/summa/test_ngen/summa-init-{{id}}.namelist.input", "allow_exceed_end_time": true, "main_output_variable": "land_surface_water__runoff_volume_flux", "variables_names_map": { @@ -26,13 +26,13 @@ ], "forcing": { "file_pattern": ".*{{id}}.*..csv", - "path": "./data/forcing/", + "path": "./extern/summa/summa/test_ngen/celia_test/input_data/celia1990/", "provider": "CsvPerFeature" } }, "time": { - "start_time": "2015-12-01 00:00:00", - "end_time": "2015-12-30 23:00:00", + "start_time": "2000-01-01 00:00:00", + "end_time": "2000-01-03 12:00:00", "output_interval": 3600 } } diff --git a/test_ngen/example_realization_config_w_summa_bmi__mac.json b/test_ngen/example_realization_config_w_summa_bmi__mac.json index c00aaa692..acdb7f818 100644 --- a/test_ngen/example_realization_config_w_summa_bmi__mac.json +++ b/test_ngen/example_realization_config_w_summa_bmi__mac.json @@ -7,7 +7,7 @@ "model_type_name": "bmi_fortran_summa", "library_file": "./extern/summa/cmake_build/libsummabmi.dylib", "forcing_file": "", - "init_config": "./data/bmi/fortran/summa-init-{{id}}.namelist.input", + "init_config": ".//extern/summa/summa/test_ngen/summa-init-{{id}}.namelist.input", "allow_exceed_end_time": true, "main_output_variable": "land_surface_water__runoff_volume_flux", "variables_names_map": { @@ -26,13 +26,13 @@ ], "forcing": { "file_pattern": ".*{{id}}.*..csv", - "path": "./data/forcing/", + "path": "./extern/summa/summa/test_ngen/celia_test/input_data/celia1990/", "provider": "CsvPerFeature" } }, "time": { - "start_time": "2015-12-01 00:00:00", - "end_time": "2015-12-30 23:00:00", + "start_time": "2000-01-01 00:00:00", + "end_time": "2000-01-03 12:00:00", "output_interval": 3600 } } diff --git a/test_ngen/example_run_celia.sh b/test_ngen/example_run_celia.sh index 787d9f05a..be91cbcf3 100644 --- a/test_ngen/example_run_celia.sh +++ b/test_ngen/example_run_celia.sh @@ -1 +1 @@ -cmake_build/ngen data/catchment_data.geojson "cat-27" ./data/nexus_data.geojson "nex-26" ./data/example_realization_config_w_summa_bmi.json \ No newline at end of file +cmake_build/ngen data/catchment_data.geojson "celia" ./data/nexus_data.geojson "nex-26" ./data/example_realization_config_w_summa_bmi.json \ No newline at end of file diff --git a/test_ngen/summa-init-cat-27.namelist.input b/test_ngen/summa-init-ceiia.namelist.input similarity index 100% rename from test_ngen/summa-init-cat-27.namelist.input rename to test_ngen/summa-init-ceiia.namelist.input From b2d9c8e3f552de25f9ba5c21fa803e1c544b73f5 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 5 Apr 2023 15:57:46 +0900 Subject: [PATCH 0657/1472] file moving --- build/cmake_ngen/README.md | 14 ++++++++------ build/cmake_ngen/build_ngen.cluster.bash | 4 ---- build/cmake_ngen/build_ngen.mac.bash | 3 +++ .../example_realization_config_w_summa_bmi.json | 2 +- ...xample_realization_config_w_summa_bmi__mac.json | 2 +- test_ngen/example_run_celia.cluster.sh | 1 + test_ngen/example_run_celia.mac.sh | 1 + test_ngen/example_run_celia.sh | 1 - 8 files changed, 15 insertions(+), 13 deletions(-) create mode 100644 test_ngen/example_run_celia.cluster.sh create mode 100644 test_ngen/example_run_celia.mac.sh delete mode 100644 test_ngen/example_run_celia.sh diff --git a/build/cmake_ngen/README.md b/build/cmake_ngen/README.md index e6d63ca47..5bbf110f4 100644 --- a/build/cmake_ngen/README.md +++ b/build/cmake_ngen/README.md @@ -81,12 +81,14 @@ After there is build system directory, the shared library can be built using the This will build a `cmake_build/libsummabmi..` file, where the version is configured within the CMake config, and the extension depends on the local machine's operating system. -There is an example of a bash script to build the ngen libraries at ngen/extern/summa/summa/build/cmake_ngen/build_ngen.[system_type].bash. These needs to be put a level above the ngen directory but run from the ngen directory: +There is an example of a bash script to build the ngen libraries at ngen/extern/summa/summa/build/cmake_ngen/build_ngen.[system_type].bash. These need to be run in the main ngen directory. -To run a test basin, in the ngen directory, run +To run a test basin, in the main ngen directory, run - cmake_build/ngen data/catchment_data.geojson "cat-27" ./data/nexus_data.geojson "nex-26" ./data/example_realization_config_w_summa.json - -This command can be copied from ngen/extern/summa/summa/test_ngen/example_run.sh and put a level above the ngen directory but run from the ngen directory. + cmake_build/ngen data/catchment_data.geojson "celia" ./data/nexus_data.geojson "nex-26" ./extern/summa/summa/test_ngen/example_realization_config_w_summa_bmi.json +or + cmake_build/ngen data/catchment_data.geojson "celia" ./data/nexus_data.geojson "nex-26" ./extern/summa/summa/test_ngen/example_realization_config_w_summa_bmi__mac.json + +This command can be run as ./extern/summa/summa/test_ngen/example_run_celia.[system_type].sh also. -This test is currently the Celia Laugh test, not the ngen example catchment cat-27. The forcing and input data for that basin could be used once it is in the correct form. +This test is currently the Celia Laugh test, not a ngen example catchment. diff --git a/build/cmake_ngen/build_ngen.cluster.bash b/build/cmake_ngen/build_ngen.cluster.bash index 513939845..1c0b24b54 100644 --- a/build/cmake_ngen/build_ngen.cluster.bash +++ b/build/cmake_ngen/build_ngen.cluster.bash @@ -31,10 +31,6 @@ cmake --build extern/snow17/cmake_build --target all cmake -B extern/summa/cmake_build -S extern/summa cmake --build extern/summa/cmake_build --target all -# does not work currently, can't find files -#cmake -B extern/evapotranspiration/cmake_build -S extern/evapotranspiration -#cmake --build extern/evapotranspiration/cmake_build --target all - cmake -B /ngen/cmake_build -S . -DMPI_ACTIVE:BOOL=OFF -DMPI_ACTIVE:BOOL=ON -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON # can also add -DCMAKE_BUILD_TYPE=Debug to be able to run in gdb # make -j 8 -C cmake_build # build w/ 8 parallel jobs, also turn MPI on diff --git a/build/cmake_ngen/build_ngen.mac.bash b/build/cmake_ngen/build_ngen.mac.bash index c0d2b0fa6..70c313fe2 100644 --- a/build/cmake_ngen/build_ngen.mac.bash +++ b/build/cmake_ngen/build_ngen.mac.bash @@ -5,6 +5,9 @@ cmake -B extern/iso_c_fortran_bmi/cmake_build -S extern/iso_c_fortran_bmi cmake --build extern/iso_c_fortran_bmi/cmake_build --target all +cmake -B extern/noah-owp-modular/cmake_build -S extern/noah-owp-modular +cmake --build extern/noah-owp-modular/cmake_build --target all + cmake -B extern/summa/cmake_build -S extern/summa cmake --build extern/summa/cmake_build --target all diff --git a/test_ngen/example_realization_config_w_summa_bmi.json b/test_ngen/example_realization_config_w_summa_bmi.json index 92791c606..ae78bb671 100644 --- a/test_ngen/example_realization_config_w_summa_bmi.json +++ b/test_ngen/example_realization_config_w_summa_bmi.json @@ -7,7 +7,7 @@ "model_type_name": "bmi_fortran_summa", "library_file": "./extern/summa/cmake_build/libsummabmi.so", "forcing_file": "", - "init_config": ".//extern/summa/summa/test_ngen/summa-init-{{id}}.namelist.input", + "init_config": "./extern/summa/summa/test_ngen/summa-init-{{id}}.namelist.input", "allow_exceed_end_time": true, "main_output_variable": "land_surface_water__runoff_volume_flux", "variables_names_map": { diff --git a/test_ngen/example_realization_config_w_summa_bmi__mac.json b/test_ngen/example_realization_config_w_summa_bmi__mac.json index acdb7f818..3a71c8b6b 100644 --- a/test_ngen/example_realization_config_w_summa_bmi__mac.json +++ b/test_ngen/example_realization_config_w_summa_bmi__mac.json @@ -7,7 +7,7 @@ "model_type_name": "bmi_fortran_summa", "library_file": "./extern/summa/cmake_build/libsummabmi.dylib", "forcing_file": "", - "init_config": ".//extern/summa/summa/test_ngen/summa-init-{{id}}.namelist.input", + "init_config": "./extern/summa/summa/test_ngen/summa-init-{{id}}.namelist.input", "allow_exceed_end_time": true, "main_output_variable": "land_surface_water__runoff_volume_flux", "variables_names_map": { diff --git a/test_ngen/example_run_celia.cluster.sh b/test_ngen/example_run_celia.cluster.sh new file mode 100644 index 000000000..6f17da1c7 --- /dev/null +++ b/test_ngen/example_run_celia.cluster.sh @@ -0,0 +1 @@ +cmake_build/ngen data/catchment_data.geojson "celia" ./data/nexus_data.geojson "nex-26" ./extern/summa/summa/test_ngen/example_realization_config_w_summa_bmi.json \ No newline at end of file diff --git a/test_ngen/example_run_celia.mac.sh b/test_ngen/example_run_celia.mac.sh new file mode 100644 index 000000000..da500be69 --- /dev/null +++ b/test_ngen/example_run_celia.mac.sh @@ -0,0 +1 @@ +cmake_build/ngen data/catchment_data.geojson "celia" ./data/nexus_data.geojson "nex-26" ./extern/summa/summa/test_ngen/example_realization_config_w_summa_bmi__mac.json \ No newline at end of file diff --git a/test_ngen/example_run_celia.sh b/test_ngen/example_run_celia.sh deleted file mode 100644 index be91cbcf3..000000000 --- a/test_ngen/example_run_celia.sh +++ /dev/null @@ -1 +0,0 @@ -cmake_build/ngen data/catchment_data.geojson "celia" ./data/nexus_data.geojson "nex-26" ./data/example_realization_config_w_summa_bmi.json \ No newline at end of file From 848a2f9bb4985b13fac5abd2403d54f1d17a6510 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 5 Apr 2023 16:15:18 +0900 Subject: [PATCH 0658/1472] names were wrong --- test_ngen/example_realization_config_w_summa_bmi.json | 4 ++-- test_ngen/example_realization_config_w_summa_bmi__mac.json | 4 ++-- ...t-ceiia.namelist.input => summa-init-celia.namelist.input} | 0 3 files changed, 4 insertions(+), 4 deletions(-) rename test_ngen/{summa-init-ceiia.namelist.input => summa-init-celia.namelist.input} (100%) diff --git a/test_ngen/example_realization_config_w_summa_bmi.json b/test_ngen/example_realization_config_w_summa_bmi.json index ae78bb671..9ab4a40c9 100644 --- a/test_ngen/example_realization_config_w_summa_bmi.json +++ b/test_ngen/example_realization_config_w_summa_bmi.json @@ -31,8 +31,8 @@ } }, "time": { - "start_time": "2000-01-01 00:00:00", + "start_time": "2000-01-01 00:30:00", "end_time": "2000-01-03 12:00:00", - "output_interval": 3600 + "output_interval": 1800 } } diff --git a/test_ngen/example_realization_config_w_summa_bmi__mac.json b/test_ngen/example_realization_config_w_summa_bmi__mac.json index 3a71c8b6b..577bcf74d 100644 --- a/test_ngen/example_realization_config_w_summa_bmi__mac.json +++ b/test_ngen/example_realization_config_w_summa_bmi__mac.json @@ -31,8 +31,8 @@ } }, "time": { - "start_time": "2000-01-01 00:00:00", + "start_time": "2000-01-01 00:30:00", "end_time": "2000-01-03 12:00:00", - "output_interval": 3600 + "output_interval": 1800 } } diff --git a/test_ngen/summa-init-ceiia.namelist.input b/test_ngen/summa-init-celia.namelist.input similarity index 100% rename from test_ngen/summa-init-ceiia.namelist.input rename to test_ngen/summa-init-celia.namelist.input From ee4b7e5f7dc72fabc2d390b93bbf9f4f98fc1d45 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 5 Apr 2023 17:31:51 +0900 Subject: [PATCH 0659/1472] try making bmi.o file in folder since not progressing time correctly --- build/cmake_ngen/CMakeLists.txt | 3 +- build/cmake_ngen/README.md | 4 +- build/source/driver/bmi.f90 | 564 ++++++++++++++++++ ...xample_realization_config_w_summa_bmi.json | 4 +- ...e_realization_config_w_summa_bmi__mac.json | 4 +- test_ngen/example_run_celia.cluster.sh | 2 +- test_ngen/example_run_celia.mac.sh | 2 +- 7 files changed, 574 insertions(+), 9 deletions(-) create mode 100644 build/source/driver/bmi.f90 diff --git a/build/cmake_ngen/CMakeLists.txt b/build/cmake_ngen/CMakeLists.txt index 3b23a3df6..f4ac3565f 100644 --- a/build/cmake_ngen/CMakeLists.txt +++ b/build/cmake_ngen/CMakeLists.txt @@ -255,7 +255,8 @@ message("\nBuilding SUMMA\n") ${DRIVER_DIR}/summa_forcing.f90 ${DRIVER_DIR}/summa_modelRun.f90 ${DRIVER_DIR}/summa_writeOutput.f90 - ${DRIVER_DIR}/summa_driver.f90) + ${DRIVER_DIR}/summa_driver.f90 + ${DRIVER_DIR}/bmi.f90) # run program files, do not use in ngen # set(SUMMA ${DRIVER_DIR}/summa_run.f90) diff --git a/build/cmake_ngen/README.md b/build/cmake_ngen/README.md index 5bbf110f4..d16c4c5a5 100644 --- a/build/cmake_ngen/README.md +++ b/build/cmake_ngen/README.md @@ -87,8 +87,8 @@ To run a test basin, in the main ngen directory, run cmake_build/ngen data/catchment_data.geojson "celia" ./data/nexus_data.geojson "nex-26" ./extern/summa/summa/test_ngen/example_realization_config_w_summa_bmi.json or - cmake_build/ngen data/catchment_data.geojson "celia" ./data/nexus_data.geojson "nex-26" ./extern/summa/summa/test_ngen/example_realization_config_w_summa_bmi__mac.json + cmake_build/ngen data/catchment_data.geojson "cat-27" ./data/nexus_data.geojson "nex-26" ./extern/summa/summa/test_ngen/example_realization_config_w_summa_bmi__mac.json This command can be run as ./extern/summa/summa/test_ngen/example_run_celia.[system_type].sh also. -This test is currently the Celia Laugh test, not a ngen example catchment. +This test is currently the Celia Laugh test, not the cat-27 ngen example catchment. diff --git a/build/source/driver/bmi.f90 b/build/source/driver/bmi.f90 new file mode 100644 index 000000000..0c3afc4b7 --- /dev/null +++ b/build/source/driver/bmi.f90 @@ -0,0 +1,564 @@ +! The Basic Model Interface (BMI) Fortran specification. +! +! This language specification is derived from the Scientific +! Interface Definition Language (SIDL) file bmi.sidl located at +! https://github.com/csdms/bmi. + +module bmif_2_0 + + implicit none + + integer, parameter :: BMI_MAX_COMPONENT_NAME = 2048 + integer, parameter :: BMI_MAX_VAR_NAME = 2048 + integer, parameter :: BMI_MAX_TYPE_NAME = 2048 + integer, parameter :: BMI_MAX_UNITS_NAME = 2048 + + integer, parameter :: BMI_FAILURE = 1 + integer, parameter :: BMI_SUCCESS = 0 + + type, abstract :: bmi + contains + + ! Initialize, run, finalize (IRF) + procedure(bmif_initialize), deferred :: initialize + procedure(bmif_update), deferred :: update + procedure(bmif_update_until), deferred :: update_until + procedure(bmif_finalize), deferred :: finalize + + ! Exchange items + procedure(bmif_get_component_name), deferred :: get_component_name + procedure(bmif_get_input_item_count), deferred :: get_input_item_count + procedure(bmif_get_output_item_count), deferred :: get_output_item_count + procedure(bmif_get_input_var_names), deferred :: get_input_var_names + procedure(bmif_get_output_var_names), deferred :: get_output_var_names + + ! Variable information + procedure(bmif_get_var_grid), deferred :: get_var_grid + procedure(bmif_get_var_type), deferred :: get_var_type + procedure(bmif_get_var_units), deferred :: get_var_units + procedure(bmif_get_var_itemsize), deferred :: get_var_itemsize + procedure(bmif_get_var_nbytes), deferred :: get_var_nbytes + procedure(bmif_get_var_location), deferred :: get_var_location + + ! Time information + procedure(bmif_get_current_time), deferred :: get_current_time + procedure(bmif_get_start_time), deferred :: get_start_time + procedure(bmif_get_end_time), deferred :: get_end_time + procedure(bmif_get_time_units), deferred :: get_time_units + procedure(bmif_get_time_step), deferred :: get_time_step +! + ! Getters, by type + procedure(bmif_get_value_int), deferred :: get_value_int + procedure(bmif_get_value_float), deferred :: get_value_float + procedure(bmif_get_value_double), deferred :: get_value_double + procedure(bmif_get_value_ptr_int), deferred :: get_value_ptr_int + procedure(bmif_get_value_ptr_float), deferred :: get_value_ptr_float + procedure(bmif_get_value_ptr_double), deferred :: get_value_ptr_double + procedure(bmif_get_value_at_indices_int), deferred :: & + get_value_at_indices_int + procedure(bmif_get_value_at_indices_float), deferred :: & + get_value_at_indices_float + procedure(bmif_get_value_at_indices_double), deferred :: & + get_value_at_indices_double + + ! Setters, by type + procedure(bmif_set_value_int), deferred :: set_value_int + procedure(bmif_set_value_float), deferred :: set_value_float + procedure(bmif_set_value_double), deferred :: set_value_double + procedure(bmif_set_value_at_indices_int), deferred :: & + set_value_at_indices_int + procedure(bmif_set_value_at_indices_float), deferred :: & + set_value_at_indices_float + procedure(bmif_set_value_at_indices_double), deferred :: & + set_value_at_indices_double + + ! Grid information + procedure(bmif_get_grid_rank), deferred :: get_grid_rank + procedure(bmif_get_grid_size), deferred :: get_grid_size + procedure(bmif_get_grid_type), deferred :: get_grid_type + + ! Uniform rectilinear + procedure(bmif_get_grid_shape), deferred :: get_grid_shape + procedure(bmif_get_grid_spacing), deferred :: get_grid_spacing + procedure(bmif_get_grid_origin), deferred :: get_grid_origin + + ! Non-uniform rectilinear, curvilinear + procedure(bmif_get_grid_x), deferred :: get_grid_x + procedure(bmif_get_grid_y), deferred :: get_grid_y + procedure(bmif_get_grid_z), deferred :: get_grid_z + + ! Unstructured + procedure(bmif_get_grid_node_count), deferred :: get_grid_node_count + procedure(bmif_get_grid_edge_count), deferred :: get_grid_edge_count + procedure(bmif_get_grid_face_count), deferred :: get_grid_face_count + procedure(bmif_get_grid_edge_nodes), deferred :: get_grid_edge_nodes + procedure(bmif_get_grid_face_edges), deferred :: get_grid_face_edges + procedure(bmif_get_grid_face_nodes), deferred :: get_grid_face_nodes + procedure(bmif_get_grid_nodes_per_face), deferred :: & + get_grid_nodes_per_face + end type bmi + + abstract interface + + ! Perform startup tasks for the model. + function bmif_initialize(this, config_file) result(bmi_status) + import :: bmi + class(bmi), intent(out) :: this + character(len=*), intent(in) :: config_file + integer :: bmi_status + end function bmif_initialize + + ! Advance the model one time step. + function bmif_update(this) result(bmi_status) + import :: bmi + class(bmi), intent(inout) :: this + integer :: bmi_status + end function bmif_update + + ! Advance the model until the given time. + function bmif_update_until(this, time) result(bmi_status) + import :: bmi + class(bmi), intent(inout) :: this + double precision, intent(in) :: time + integer :: bmi_status + end function bmif_update_until + + ! Perform teardown tasks for the model. + function bmif_finalize(this) result(bmi_status) + import :: bmi + class(bmi), intent(inout) :: this + integer :: bmi_status + end function bmif_finalize + + ! Get the name of the model. + function bmif_get_component_name(this, name) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + character(len=*), pointer, intent(out) :: name + integer :: bmi_status + end function bmif_get_component_name + + ! Count a model's input variables. + function bmif_get_input_item_count(this, count) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + integer, intent(out) :: count + integer :: bmi_status + end function bmif_get_input_item_count + + ! Count a model's output variables. + function bmif_get_output_item_count(this, count) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + integer, intent(out) :: count + integer :: bmi_status + end function bmif_get_output_item_count + + ! List a model's input variables. + function bmif_get_input_var_names(this, names) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + character(len=*), pointer, intent(out) :: names(:) + integer :: bmi_status + end function bmif_get_input_var_names + + ! List a model's output variables. + function bmif_get_output_var_names(this, names) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + character(len=*), pointer, intent(out) :: names(:) + integer :: bmi_status + end function bmif_get_output_var_names + + ! Get the grid identifier for the given variable. + function bmif_get_var_grid(this, name, grid) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + character(len=*), intent(in) :: name + integer, intent(out) :: grid + integer :: bmi_status + end function bmif_get_var_grid + + ! Get the data type of the given variable as a string. + function bmif_get_var_type(this, name, type) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + character(len=*), intent(in) :: name + character(len=*), intent(out) :: type + integer :: bmi_status + end function bmif_get_var_type + + ! Get the units of the given variable. + function bmif_get_var_units(this, name, units) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + character(len=*), intent(in) :: name + character(len=*), intent(out) :: units + integer :: bmi_status + end function bmif_get_var_units + + ! Get memory use per array element, in bytes. + function bmif_get_var_itemsize(this, name, size) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + character(len=*), intent(in) :: name + integer, intent(out) :: size + integer :: bmi_status + end function bmif_get_var_itemsize + + ! Get size of the given variable, in bytes. + function bmif_get_var_nbytes(this, name, nbytes) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + character(len=*), intent(in) :: name + integer, intent(out) :: nbytes + integer :: bmi_status + end function bmif_get_var_nbytes + + ! Describe where a variable is located: node, edge, or face. + function bmif_get_var_location(this, name, location) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + character(len=*), intent(in) :: name + character(len=*), intent(out) :: location + integer :: bmi_status + end function bmif_get_var_location + + ! Current time of the model. + function bmif_get_current_time(this, time) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + double precision, intent(out) :: time + integer :: bmi_status + end function bmif_get_current_time + + ! Start time of the model. + function bmif_get_start_time(this, time) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + double precision, intent(out) :: time + integer :: bmi_status + end function bmif_get_start_time + + ! End time of the model. + function bmif_get_end_time(this, time) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + double precision, intent(out) :: time + integer :: bmi_status + end function bmif_get_end_time + + ! Time units of the model. + function bmif_get_time_units(this, units) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + character(len=*), intent(out) :: units + integer :: bmi_status + end function bmif_get_time_units + + ! Time step of the model. + function bmif_get_time_step(this, time_step) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + double precision, intent(out) :: time_step + integer :: bmi_status + end function bmif_get_time_step + + ! Get a copy of values (flattened!) of the given integer variable. + function bmif_get_value_int(this, name, dest) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + character(len=*), intent(in) :: name + integer, intent(inout) :: dest(:) + integer :: bmi_status + end function bmif_get_value_int + + ! Get a copy of values (flattened!) of the given real variable. + function bmif_get_value_float(this, name, dest) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + character(len=*), intent(in) :: name + real, intent(inout) :: dest(:) + integer :: bmi_status + end function bmif_get_value_float + + ! Get a copy of values (flattened!) of the given double variable. + function bmif_get_value_double(this, name, dest) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + character(len=*), intent(in) :: name + double precision, intent(inout) :: dest(:) + integer :: bmi_status + end function bmif_get_value_double + + ! Get a reference to the given integer variable. + function bmif_get_value_ptr_int(this, name, dest_ptr) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + character(len=*), intent(in) :: name + integer, pointer, intent(inout) :: dest_ptr(:) + integer :: bmi_status + end function bmif_get_value_ptr_int + + ! Get a reference to the given real variable. + function bmif_get_value_ptr_float(this, name, dest_ptr) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + character(len=*), intent(in) :: name + real, pointer, intent(inout) :: dest_ptr(:) + integer :: bmi_status + end function bmif_get_value_ptr_float + + ! Get a reference to the given double variable. + function bmif_get_value_ptr_double(this, name, dest_ptr) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + character(len=*), intent(in) :: name + double precision, pointer, intent(inout) :: dest_ptr(:) + integer :: bmi_status + end function bmif_get_value_ptr_double + + ! Get integer values at particular (one-dimensional) indices. + function bmif_get_value_at_indices_int(this, name, dest, inds) & + result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + character(len=*), intent(in) :: name + integer, intent(inout) :: dest(:) + integer, intent(in) :: inds(:) + integer :: bmi_status + end function bmif_get_value_at_indices_int + + ! Get real values at particular (one-dimensional) indices. + function bmif_get_value_at_indices_float(this, name, dest, inds) & + result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + character(len=*), intent(in) :: name + real, intent(inout) :: dest(:) + integer, intent(in) :: inds(:) + integer :: bmi_status + end function bmif_get_value_at_indices_float + + ! Get double values at particular (one-dimensional) indices. + function bmif_get_value_at_indices_double(this, name, dest, inds) & + result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + character(len=*), intent(in) :: name + double precision, intent(inout) :: dest(:) + integer, intent(in) :: inds(:) + integer :: bmi_status + end function bmif_get_value_at_indices_double + + ! Set new values for an integer model variable. + function bmif_set_value_int(this, name, src) result(bmi_status) + import :: bmi + class(bmi), intent(inout) :: this + character(len=*), intent(in) :: name + integer, intent(in) :: src(:) + integer :: bmi_status + end function bmif_set_value_int + + ! Set new values for a real model variable. + function bmif_set_value_float(this, name, src) result(bmi_status) + import :: bmi + class(bmi), intent(inout) :: this + character(len=*), intent(in) :: name + real, intent(in) :: src(:) + integer :: bmi_status + end function bmif_set_value_float + + ! Set new values for a double model variable. + function bmif_set_value_double(this, name, src) result(bmi_status) + import :: bmi + class(bmi), intent(inout) :: this + character(len=*), intent(in) :: name + double precision, intent(in) :: src(:) + integer :: bmi_status + end function bmif_set_value_double + + ! Set integer values at particular (one-dimensional) indices. + function bmif_set_value_at_indices_int(this, name, inds, src) & + result(bmi_status) + import :: bmi + class(bmi), intent(inout) :: this + character(len=*), intent(in) :: name + integer, intent(in) :: inds(:) + integer, intent(in) :: src(:) + integer :: bmi_status + end function bmif_set_value_at_indices_int + + ! Set real values at particular (one-dimensional) indices. + function bmif_set_value_at_indices_float(this, name, inds, src) & + result(bmi_status) + import :: bmi + class(bmi), intent(inout) :: this + character(len=*), intent(in) :: name + integer, intent(in) :: inds(:) + real, intent(in) :: src(:) + integer :: bmi_status + end function bmif_set_value_at_indices_float + + ! Set double values at particular (one-dimensional) indices. + function bmif_set_value_at_indices_double(this, name, inds, src) & + result(bmi_status) + import :: bmi + class(bmi), intent(inout) :: this + character(len=*), intent(in) :: name + integer, intent(in) :: inds(:) + double precision, intent(in) :: src(:) + integer :: bmi_status + end function bmif_set_value_at_indices_double + + ! Get number of dimensions of the computational grid. + function bmif_get_grid_rank(this, grid, rank) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + integer, intent(in) :: grid + integer, intent(out) :: rank + integer :: bmi_status + end function bmif_get_grid_rank + + ! Get the total number of elements in the computational grid. + function bmif_get_grid_size(this, grid, size) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + integer, intent(in) :: grid + integer, intent(out) :: size + integer :: bmi_status + end function bmif_get_grid_size + + ! Get the grid type as a string. + function bmif_get_grid_type(this, grid, type) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + integer, intent(in) :: grid + character(len=*), intent(out) :: type + integer :: bmi_status + end function bmif_get_grid_type + + ! Get the dimensions of the computational grid. + function bmif_get_grid_shape(this, grid, shape) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + integer, intent(in) :: grid + integer, dimension(:), intent(out) :: shape + integer :: bmi_status + end function bmif_get_grid_shape + + ! Get distance between nodes of the computational grid. + function bmif_get_grid_spacing(this, grid, spacing) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + integer, intent(in) :: grid + double precision, dimension(:), intent(out) :: spacing + integer :: bmi_status + end function bmif_get_grid_spacing + + ! Get coordinates of the origin of the computational grid. + function bmif_get_grid_origin(this, grid, origin) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + integer, intent(in) :: grid + double precision, dimension(:), intent(out) :: origin + integer :: bmi_status + end function bmif_get_grid_origin + + ! Get the x-coordinates of the nodes of a computational grid. + function bmif_get_grid_x(this, grid, x) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + integer, intent(in) :: grid + double precision, dimension(:), intent(out) :: x + integer :: bmi_status + end function bmif_get_grid_x + + ! Get the y-coordinates of the nodes of a computational grid. + function bmif_get_grid_y(this, grid, y) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + integer, intent(in) :: grid + double precision, dimension(:), intent(out) :: y + integer :: bmi_status + end function bmif_get_grid_y + + ! Get the z-coordinates of the nodes of a computational grid. + function bmif_get_grid_z(this, grid, z) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + integer, intent(in) :: grid + double precision, dimension(:), intent(out) :: z + integer :: bmi_status + end function bmif_get_grid_z + + ! Get the number of nodes in an unstructured grid. + function bmif_get_grid_node_count(this, grid, count) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + integer, intent(in) :: grid + integer, intent(out) :: count + integer :: bmi_status + end function bmif_get_grid_node_count + + ! Get the number of edges in an unstructured grid. + function bmif_get_grid_edge_count(this, grid, count) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + integer, intent(in) :: grid + integer, intent(out) :: count + integer :: bmi_status + end function bmif_get_grid_edge_count + + ! Get the number of faces in an unstructured grid. + function bmif_get_grid_face_count(this, grid, count) result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + integer, intent(in) :: grid + integer, intent(out) :: count + integer :: bmi_status + end function bmif_get_grid_face_count + + ! Get the edge-node connectivity. + function bmif_get_grid_edge_nodes(this, grid, edge_nodes) & + result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + integer, intent(in) :: grid + integer, dimension(:), intent(out) :: edge_nodes + integer :: bmi_status + end function bmif_get_grid_edge_nodes + + ! Get the face-edge connectivity. + function bmif_get_grid_face_edges(this, grid, face_edges) & + result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + integer, intent(in) :: grid + integer, dimension(:), intent(out) :: face_edges + integer :: bmi_status + end function bmif_get_grid_face_edges + + ! Get the face-node connectivity. + function bmif_get_grid_face_nodes(this, grid, face_nodes) & + result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + integer, intent(in) :: grid + integer, dimension(:), intent(out) :: face_nodes + integer :: bmi_status + end function bmif_get_grid_face_nodes + + ! Get the number of nodes for each face. + function bmif_get_grid_nodes_per_face(this, grid, nodes_per_face) & + result(bmi_status) + import :: bmi + class(bmi), intent(in) :: this + integer, intent(in) :: grid + integer, dimension(:), intent(out) :: nodes_per_face + integer :: bmi_status + end function bmif_get_grid_nodes_per_face + + end interface + +end module bmif_2_0 diff --git a/test_ngen/example_realization_config_w_summa_bmi.json b/test_ngen/example_realization_config_w_summa_bmi.json index 9ab4a40c9..caeff8514 100644 --- a/test_ngen/example_realization_config_w_summa_bmi.json +++ b/test_ngen/example_realization_config_w_summa_bmi.json @@ -7,7 +7,7 @@ "model_type_name": "bmi_fortran_summa", "library_file": "./extern/summa/cmake_build/libsummabmi.so", "forcing_file": "", - "init_config": "./extern/summa/summa/test_ngen/summa-init-{{id}}.namelist.input", + "init_config": "./extern/summa/summa/test_ngen/summa-init-celia.namelist.input", "allow_exceed_end_time": true, "main_output_variable": "land_surface_water__runoff_volume_flux", "variables_names_map": { @@ -25,7 +25,7 @@ } ], "forcing": { - "file_pattern": ".*{{id}}.*..csv", + "file_pattern": ".*celia.*..csv", "path": "./extern/summa/summa/test_ngen/celia_test/input_data/celia1990/", "provider": "CsvPerFeature" } diff --git a/test_ngen/example_realization_config_w_summa_bmi__mac.json b/test_ngen/example_realization_config_w_summa_bmi__mac.json index 577bcf74d..40d5a83cf 100644 --- a/test_ngen/example_realization_config_w_summa_bmi__mac.json +++ b/test_ngen/example_realization_config_w_summa_bmi__mac.json @@ -7,7 +7,7 @@ "model_type_name": "bmi_fortran_summa", "library_file": "./extern/summa/cmake_build/libsummabmi.dylib", "forcing_file": "", - "init_config": "./extern/summa/summa/test_ngen/summa-init-{{id}}.namelist.input", + "init_config": "./extern/summa/summa/test_ngen/summa-init-celia.namelist.input", "allow_exceed_end_time": true, "main_output_variable": "land_surface_water__runoff_volume_flux", "variables_names_map": { @@ -25,7 +25,7 @@ } ], "forcing": { - "file_pattern": ".*{{id}}.*..csv", + "file_pattern": ".*celia.*..csv", "path": "./extern/summa/summa/test_ngen/celia_test/input_data/celia1990/", "provider": "CsvPerFeature" } diff --git a/test_ngen/example_run_celia.cluster.sh b/test_ngen/example_run_celia.cluster.sh index 6f17da1c7..beebd3084 100644 --- a/test_ngen/example_run_celia.cluster.sh +++ b/test_ngen/example_run_celia.cluster.sh @@ -1 +1 @@ -cmake_build/ngen data/catchment_data.geojson "celia" ./data/nexus_data.geojson "nex-26" ./extern/summa/summa/test_ngen/example_realization_config_w_summa_bmi.json \ No newline at end of file +cmake_build/ngen data/catchment_data.geojson "cat-27" ./data/nexus_data.geojson "nex-26" ./extern/summa/summa/test_ngen/example_realization_config_w_summa_bmi.json \ No newline at end of file diff --git a/test_ngen/example_run_celia.mac.sh b/test_ngen/example_run_celia.mac.sh index da500be69..b4dd861c4 100644 --- a/test_ngen/example_run_celia.mac.sh +++ b/test_ngen/example_run_celia.mac.sh @@ -1 +1 @@ -cmake_build/ngen data/catchment_data.geojson "celia" ./data/nexus_data.geojson "nex-26" ./extern/summa/summa/test_ngen/example_realization_config_w_summa_bmi__mac.json \ No newline at end of file +cmake_build/ngen data/catchment_data.geojson "cat-27" ./data/nexus_data.geojson "nex-26" ./extern/summa/summa/test_ngen/example_realization_config_w_summa_bmi__mac.json \ No newline at end of file From 2510c6284ab59ed65eb35563e36d96f6c37a650a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 5 Apr 2023 17:37:41 +0900 Subject: [PATCH 0660/1472] undo last changes --- build/cmake_ngen/CMakeLists.txt | 3 +- build/source/driver/bmi.f90 | 564 -------------------------------- 2 files changed, 1 insertion(+), 566 deletions(-) delete mode 100644 build/source/driver/bmi.f90 diff --git a/build/cmake_ngen/CMakeLists.txt b/build/cmake_ngen/CMakeLists.txt index f4ac3565f..3b23a3df6 100644 --- a/build/cmake_ngen/CMakeLists.txt +++ b/build/cmake_ngen/CMakeLists.txt @@ -255,8 +255,7 @@ message("\nBuilding SUMMA\n") ${DRIVER_DIR}/summa_forcing.f90 ${DRIVER_DIR}/summa_modelRun.f90 ${DRIVER_DIR}/summa_writeOutput.f90 - ${DRIVER_DIR}/summa_driver.f90 - ${DRIVER_DIR}/bmi.f90) + ${DRIVER_DIR}/summa_driver.f90) # run program files, do not use in ngen # set(SUMMA ${DRIVER_DIR}/summa_run.f90) diff --git a/build/source/driver/bmi.f90 b/build/source/driver/bmi.f90 deleted file mode 100644 index 0c3afc4b7..000000000 --- a/build/source/driver/bmi.f90 +++ /dev/null @@ -1,564 +0,0 @@ -! The Basic Model Interface (BMI) Fortran specification. -! -! This language specification is derived from the Scientific -! Interface Definition Language (SIDL) file bmi.sidl located at -! https://github.com/csdms/bmi. - -module bmif_2_0 - - implicit none - - integer, parameter :: BMI_MAX_COMPONENT_NAME = 2048 - integer, parameter :: BMI_MAX_VAR_NAME = 2048 - integer, parameter :: BMI_MAX_TYPE_NAME = 2048 - integer, parameter :: BMI_MAX_UNITS_NAME = 2048 - - integer, parameter :: BMI_FAILURE = 1 - integer, parameter :: BMI_SUCCESS = 0 - - type, abstract :: bmi - contains - - ! Initialize, run, finalize (IRF) - procedure(bmif_initialize), deferred :: initialize - procedure(bmif_update), deferred :: update - procedure(bmif_update_until), deferred :: update_until - procedure(bmif_finalize), deferred :: finalize - - ! Exchange items - procedure(bmif_get_component_name), deferred :: get_component_name - procedure(bmif_get_input_item_count), deferred :: get_input_item_count - procedure(bmif_get_output_item_count), deferred :: get_output_item_count - procedure(bmif_get_input_var_names), deferred :: get_input_var_names - procedure(bmif_get_output_var_names), deferred :: get_output_var_names - - ! Variable information - procedure(bmif_get_var_grid), deferred :: get_var_grid - procedure(bmif_get_var_type), deferred :: get_var_type - procedure(bmif_get_var_units), deferred :: get_var_units - procedure(bmif_get_var_itemsize), deferred :: get_var_itemsize - procedure(bmif_get_var_nbytes), deferred :: get_var_nbytes - procedure(bmif_get_var_location), deferred :: get_var_location - - ! Time information - procedure(bmif_get_current_time), deferred :: get_current_time - procedure(bmif_get_start_time), deferred :: get_start_time - procedure(bmif_get_end_time), deferred :: get_end_time - procedure(bmif_get_time_units), deferred :: get_time_units - procedure(bmif_get_time_step), deferred :: get_time_step -! - ! Getters, by type - procedure(bmif_get_value_int), deferred :: get_value_int - procedure(bmif_get_value_float), deferred :: get_value_float - procedure(bmif_get_value_double), deferred :: get_value_double - procedure(bmif_get_value_ptr_int), deferred :: get_value_ptr_int - procedure(bmif_get_value_ptr_float), deferred :: get_value_ptr_float - procedure(bmif_get_value_ptr_double), deferred :: get_value_ptr_double - procedure(bmif_get_value_at_indices_int), deferred :: & - get_value_at_indices_int - procedure(bmif_get_value_at_indices_float), deferred :: & - get_value_at_indices_float - procedure(bmif_get_value_at_indices_double), deferred :: & - get_value_at_indices_double - - ! Setters, by type - procedure(bmif_set_value_int), deferred :: set_value_int - procedure(bmif_set_value_float), deferred :: set_value_float - procedure(bmif_set_value_double), deferred :: set_value_double - procedure(bmif_set_value_at_indices_int), deferred :: & - set_value_at_indices_int - procedure(bmif_set_value_at_indices_float), deferred :: & - set_value_at_indices_float - procedure(bmif_set_value_at_indices_double), deferred :: & - set_value_at_indices_double - - ! Grid information - procedure(bmif_get_grid_rank), deferred :: get_grid_rank - procedure(bmif_get_grid_size), deferred :: get_grid_size - procedure(bmif_get_grid_type), deferred :: get_grid_type - - ! Uniform rectilinear - procedure(bmif_get_grid_shape), deferred :: get_grid_shape - procedure(bmif_get_grid_spacing), deferred :: get_grid_spacing - procedure(bmif_get_grid_origin), deferred :: get_grid_origin - - ! Non-uniform rectilinear, curvilinear - procedure(bmif_get_grid_x), deferred :: get_grid_x - procedure(bmif_get_grid_y), deferred :: get_grid_y - procedure(bmif_get_grid_z), deferred :: get_grid_z - - ! Unstructured - procedure(bmif_get_grid_node_count), deferred :: get_grid_node_count - procedure(bmif_get_grid_edge_count), deferred :: get_grid_edge_count - procedure(bmif_get_grid_face_count), deferred :: get_grid_face_count - procedure(bmif_get_grid_edge_nodes), deferred :: get_grid_edge_nodes - procedure(bmif_get_grid_face_edges), deferred :: get_grid_face_edges - procedure(bmif_get_grid_face_nodes), deferred :: get_grid_face_nodes - procedure(bmif_get_grid_nodes_per_face), deferred :: & - get_grid_nodes_per_face - end type bmi - - abstract interface - - ! Perform startup tasks for the model. - function bmif_initialize(this, config_file) result(bmi_status) - import :: bmi - class(bmi), intent(out) :: this - character(len=*), intent(in) :: config_file - integer :: bmi_status - end function bmif_initialize - - ! Advance the model one time step. - function bmif_update(this) result(bmi_status) - import :: bmi - class(bmi), intent(inout) :: this - integer :: bmi_status - end function bmif_update - - ! Advance the model until the given time. - function bmif_update_until(this, time) result(bmi_status) - import :: bmi - class(bmi), intent(inout) :: this - double precision, intent(in) :: time - integer :: bmi_status - end function bmif_update_until - - ! Perform teardown tasks for the model. - function bmif_finalize(this) result(bmi_status) - import :: bmi - class(bmi), intent(inout) :: this - integer :: bmi_status - end function bmif_finalize - - ! Get the name of the model. - function bmif_get_component_name(this, name) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - character(len=*), pointer, intent(out) :: name - integer :: bmi_status - end function bmif_get_component_name - - ! Count a model's input variables. - function bmif_get_input_item_count(this, count) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - integer, intent(out) :: count - integer :: bmi_status - end function bmif_get_input_item_count - - ! Count a model's output variables. - function bmif_get_output_item_count(this, count) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - integer, intent(out) :: count - integer :: bmi_status - end function bmif_get_output_item_count - - ! List a model's input variables. - function bmif_get_input_var_names(this, names) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - character(len=*), pointer, intent(out) :: names(:) - integer :: bmi_status - end function bmif_get_input_var_names - - ! List a model's output variables. - function bmif_get_output_var_names(this, names) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - character(len=*), pointer, intent(out) :: names(:) - integer :: bmi_status - end function bmif_get_output_var_names - - ! Get the grid identifier for the given variable. - function bmif_get_var_grid(this, name, grid) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - character(len=*), intent(in) :: name - integer, intent(out) :: grid - integer :: bmi_status - end function bmif_get_var_grid - - ! Get the data type of the given variable as a string. - function bmif_get_var_type(this, name, type) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - character(len=*), intent(in) :: name - character(len=*), intent(out) :: type - integer :: bmi_status - end function bmif_get_var_type - - ! Get the units of the given variable. - function bmif_get_var_units(this, name, units) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - character(len=*), intent(in) :: name - character(len=*), intent(out) :: units - integer :: bmi_status - end function bmif_get_var_units - - ! Get memory use per array element, in bytes. - function bmif_get_var_itemsize(this, name, size) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - character(len=*), intent(in) :: name - integer, intent(out) :: size - integer :: bmi_status - end function bmif_get_var_itemsize - - ! Get size of the given variable, in bytes. - function bmif_get_var_nbytes(this, name, nbytes) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - character(len=*), intent(in) :: name - integer, intent(out) :: nbytes - integer :: bmi_status - end function bmif_get_var_nbytes - - ! Describe where a variable is located: node, edge, or face. - function bmif_get_var_location(this, name, location) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - character(len=*), intent(in) :: name - character(len=*), intent(out) :: location - integer :: bmi_status - end function bmif_get_var_location - - ! Current time of the model. - function bmif_get_current_time(this, time) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - double precision, intent(out) :: time - integer :: bmi_status - end function bmif_get_current_time - - ! Start time of the model. - function bmif_get_start_time(this, time) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - double precision, intent(out) :: time - integer :: bmi_status - end function bmif_get_start_time - - ! End time of the model. - function bmif_get_end_time(this, time) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - double precision, intent(out) :: time - integer :: bmi_status - end function bmif_get_end_time - - ! Time units of the model. - function bmif_get_time_units(this, units) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - character(len=*), intent(out) :: units - integer :: bmi_status - end function bmif_get_time_units - - ! Time step of the model. - function bmif_get_time_step(this, time_step) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - double precision, intent(out) :: time_step - integer :: bmi_status - end function bmif_get_time_step - - ! Get a copy of values (flattened!) of the given integer variable. - function bmif_get_value_int(this, name, dest) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - character(len=*), intent(in) :: name - integer, intent(inout) :: dest(:) - integer :: bmi_status - end function bmif_get_value_int - - ! Get a copy of values (flattened!) of the given real variable. - function bmif_get_value_float(this, name, dest) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - character(len=*), intent(in) :: name - real, intent(inout) :: dest(:) - integer :: bmi_status - end function bmif_get_value_float - - ! Get a copy of values (flattened!) of the given double variable. - function bmif_get_value_double(this, name, dest) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - character(len=*), intent(in) :: name - double precision, intent(inout) :: dest(:) - integer :: bmi_status - end function bmif_get_value_double - - ! Get a reference to the given integer variable. - function bmif_get_value_ptr_int(this, name, dest_ptr) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - character(len=*), intent(in) :: name - integer, pointer, intent(inout) :: dest_ptr(:) - integer :: bmi_status - end function bmif_get_value_ptr_int - - ! Get a reference to the given real variable. - function bmif_get_value_ptr_float(this, name, dest_ptr) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - character(len=*), intent(in) :: name - real, pointer, intent(inout) :: dest_ptr(:) - integer :: bmi_status - end function bmif_get_value_ptr_float - - ! Get a reference to the given double variable. - function bmif_get_value_ptr_double(this, name, dest_ptr) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - character(len=*), intent(in) :: name - double precision, pointer, intent(inout) :: dest_ptr(:) - integer :: bmi_status - end function bmif_get_value_ptr_double - - ! Get integer values at particular (one-dimensional) indices. - function bmif_get_value_at_indices_int(this, name, dest, inds) & - result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - character(len=*), intent(in) :: name - integer, intent(inout) :: dest(:) - integer, intent(in) :: inds(:) - integer :: bmi_status - end function bmif_get_value_at_indices_int - - ! Get real values at particular (one-dimensional) indices. - function bmif_get_value_at_indices_float(this, name, dest, inds) & - result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - character(len=*), intent(in) :: name - real, intent(inout) :: dest(:) - integer, intent(in) :: inds(:) - integer :: bmi_status - end function bmif_get_value_at_indices_float - - ! Get double values at particular (one-dimensional) indices. - function bmif_get_value_at_indices_double(this, name, dest, inds) & - result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - character(len=*), intent(in) :: name - double precision, intent(inout) :: dest(:) - integer, intent(in) :: inds(:) - integer :: bmi_status - end function bmif_get_value_at_indices_double - - ! Set new values for an integer model variable. - function bmif_set_value_int(this, name, src) result(bmi_status) - import :: bmi - class(bmi), intent(inout) :: this - character(len=*), intent(in) :: name - integer, intent(in) :: src(:) - integer :: bmi_status - end function bmif_set_value_int - - ! Set new values for a real model variable. - function bmif_set_value_float(this, name, src) result(bmi_status) - import :: bmi - class(bmi), intent(inout) :: this - character(len=*), intent(in) :: name - real, intent(in) :: src(:) - integer :: bmi_status - end function bmif_set_value_float - - ! Set new values for a double model variable. - function bmif_set_value_double(this, name, src) result(bmi_status) - import :: bmi - class(bmi), intent(inout) :: this - character(len=*), intent(in) :: name - double precision, intent(in) :: src(:) - integer :: bmi_status - end function bmif_set_value_double - - ! Set integer values at particular (one-dimensional) indices. - function bmif_set_value_at_indices_int(this, name, inds, src) & - result(bmi_status) - import :: bmi - class(bmi), intent(inout) :: this - character(len=*), intent(in) :: name - integer, intent(in) :: inds(:) - integer, intent(in) :: src(:) - integer :: bmi_status - end function bmif_set_value_at_indices_int - - ! Set real values at particular (one-dimensional) indices. - function bmif_set_value_at_indices_float(this, name, inds, src) & - result(bmi_status) - import :: bmi - class(bmi), intent(inout) :: this - character(len=*), intent(in) :: name - integer, intent(in) :: inds(:) - real, intent(in) :: src(:) - integer :: bmi_status - end function bmif_set_value_at_indices_float - - ! Set double values at particular (one-dimensional) indices. - function bmif_set_value_at_indices_double(this, name, inds, src) & - result(bmi_status) - import :: bmi - class(bmi), intent(inout) :: this - character(len=*), intent(in) :: name - integer, intent(in) :: inds(:) - double precision, intent(in) :: src(:) - integer :: bmi_status - end function bmif_set_value_at_indices_double - - ! Get number of dimensions of the computational grid. - function bmif_get_grid_rank(this, grid, rank) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - integer, intent(in) :: grid - integer, intent(out) :: rank - integer :: bmi_status - end function bmif_get_grid_rank - - ! Get the total number of elements in the computational grid. - function bmif_get_grid_size(this, grid, size) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - integer, intent(in) :: grid - integer, intent(out) :: size - integer :: bmi_status - end function bmif_get_grid_size - - ! Get the grid type as a string. - function bmif_get_grid_type(this, grid, type) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - integer, intent(in) :: grid - character(len=*), intent(out) :: type - integer :: bmi_status - end function bmif_get_grid_type - - ! Get the dimensions of the computational grid. - function bmif_get_grid_shape(this, grid, shape) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - integer, intent(in) :: grid - integer, dimension(:), intent(out) :: shape - integer :: bmi_status - end function bmif_get_grid_shape - - ! Get distance between nodes of the computational grid. - function bmif_get_grid_spacing(this, grid, spacing) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - integer, intent(in) :: grid - double precision, dimension(:), intent(out) :: spacing - integer :: bmi_status - end function bmif_get_grid_spacing - - ! Get coordinates of the origin of the computational grid. - function bmif_get_grid_origin(this, grid, origin) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - integer, intent(in) :: grid - double precision, dimension(:), intent(out) :: origin - integer :: bmi_status - end function bmif_get_grid_origin - - ! Get the x-coordinates of the nodes of a computational grid. - function bmif_get_grid_x(this, grid, x) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - integer, intent(in) :: grid - double precision, dimension(:), intent(out) :: x - integer :: bmi_status - end function bmif_get_grid_x - - ! Get the y-coordinates of the nodes of a computational grid. - function bmif_get_grid_y(this, grid, y) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - integer, intent(in) :: grid - double precision, dimension(:), intent(out) :: y - integer :: bmi_status - end function bmif_get_grid_y - - ! Get the z-coordinates of the nodes of a computational grid. - function bmif_get_grid_z(this, grid, z) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - integer, intent(in) :: grid - double precision, dimension(:), intent(out) :: z - integer :: bmi_status - end function bmif_get_grid_z - - ! Get the number of nodes in an unstructured grid. - function bmif_get_grid_node_count(this, grid, count) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - integer, intent(in) :: grid - integer, intent(out) :: count - integer :: bmi_status - end function bmif_get_grid_node_count - - ! Get the number of edges in an unstructured grid. - function bmif_get_grid_edge_count(this, grid, count) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - integer, intent(in) :: grid - integer, intent(out) :: count - integer :: bmi_status - end function bmif_get_grid_edge_count - - ! Get the number of faces in an unstructured grid. - function bmif_get_grid_face_count(this, grid, count) result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - integer, intent(in) :: grid - integer, intent(out) :: count - integer :: bmi_status - end function bmif_get_grid_face_count - - ! Get the edge-node connectivity. - function bmif_get_grid_edge_nodes(this, grid, edge_nodes) & - result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - integer, intent(in) :: grid - integer, dimension(:), intent(out) :: edge_nodes - integer :: bmi_status - end function bmif_get_grid_edge_nodes - - ! Get the face-edge connectivity. - function bmif_get_grid_face_edges(this, grid, face_edges) & - result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - integer, intent(in) :: grid - integer, dimension(:), intent(out) :: face_edges - integer :: bmi_status - end function bmif_get_grid_face_edges - - ! Get the face-node connectivity. - function bmif_get_grid_face_nodes(this, grid, face_nodes) & - result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - integer, intent(in) :: grid - integer, dimension(:), intent(out) :: face_nodes - integer :: bmi_status - end function bmif_get_grid_face_nodes - - ! Get the number of nodes for each face. - function bmif_get_grid_nodes_per_face(this, grid, nodes_per_face) & - result(bmi_status) - import :: bmi - class(bmi), intent(in) :: this - integer, intent(in) :: grid - integer, dimension(:), intent(out) :: nodes_per_face - integer :: bmi_status - end function bmif_get_grid_nodes_per_face - - end interface - -end module bmif_2_0 From dcb847b3ba0340ea068f7f12c185949255389c99 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 5 Apr 2023 22:37:40 +0900 Subject: [PATCH 0661/1472] some time was in days, should all be in seconds (or at least consistent) --- build/source/driver/summa_driver.f90 | 12 ++++++------ .../example_realization_config_w_summa_bmi.json | 2 +- .../example_realization_config_w_summa_bmi__mac.json | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 276b106cb..61f3a322c 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -260,7 +260,7 @@ end function summa_update ! **************************************************************************** function summa_update_until(this, time) result (bmi_status) class (summa_bmi), intent(inout) :: this - double precision, intent(in) :: time ! unit days (julian days, same as get_model__time) + double precision, intent(in) :: time ! unit seconds (julian_days*secprday, same as get_model__time) integer :: bmi_status, istat, n_steps, i double precision :: current @@ -270,7 +270,7 @@ function summa_update_until(this, time) result (bmi_status) return end if - n_steps = nint( (time - current)*secprday/data_step ) + 1 ! model can only do a full data_step + n_steps = nint( (time - current)/data_step ) + 1 ! model can only do a full data_step do i = 1, n_steps istat = this%update() end do @@ -384,7 +384,7 @@ function summa_start_time(this, time) result (bmi_status) double precision, intent(out) :: time integer :: bmi_status - time = dJulianStart ! unit days + time = dJulianStart*secprday ! unit seconds bmi_status = BMI_SUCCESS end function summa_start_time @@ -394,7 +394,7 @@ function summa_end_time(this, time) result (bmi_status) double precision, intent(out) :: time integer :: bmi_status - time = dJulianFinsh ! unit days + time = dJulianFinsh*secprday ! unit seconds bmi_status = BMI_SUCCESS end function summa_end_time @@ -405,9 +405,9 @@ function summa_current_time(this, time) result (bmi_status) integer :: bmi_status if(this%model%timeStep==1)then - time = dJulianStart ! unit days + time = dJulianStart*secprday ! unit seconds else - time = dJulianStart + (data_step*real(this%model%timeStep-1,dp))/secprday ! unit days + time = dJulianStart*secprday + (data_step*real(this%model%timeStep-1,dp)) ! unit seconds end if bmi_status = BMI_SUCCESS end function summa_current_time diff --git a/test_ngen/example_realization_config_w_summa_bmi.json b/test_ngen/example_realization_config_w_summa_bmi.json index caeff8514..92b88de18 100644 --- a/test_ngen/example_realization_config_w_summa_bmi.json +++ b/test_ngen/example_realization_config_w_summa_bmi.json @@ -8,7 +8,7 @@ "library_file": "./extern/summa/cmake_build/libsummabmi.so", "forcing_file": "", "init_config": "./extern/summa/summa/test_ngen/summa-init-celia.namelist.input", - "allow_exceed_end_time": true, + "allow_exceed_end_time": false, "main_output_variable": "land_surface_water__runoff_volume_flux", "variables_names_map": { "atmosphere_water__precipitation_mass_flux": "precip_rate", diff --git a/test_ngen/example_realization_config_w_summa_bmi__mac.json b/test_ngen/example_realization_config_w_summa_bmi__mac.json index 40d5a83cf..7cb931c59 100644 --- a/test_ngen/example_realization_config_w_summa_bmi__mac.json +++ b/test_ngen/example_realization_config_w_summa_bmi__mac.json @@ -8,7 +8,7 @@ "library_file": "./extern/summa/cmake_build/libsummabmi.dylib", "forcing_file": "", "init_config": "./extern/summa/summa/test_ngen/summa-init-celia.namelist.input", - "allow_exceed_end_time": true, + "allow_exceed_end_time": false, "main_output_variable": "land_surface_water__runoff_volume_flux", "variables_names_map": { "atmosphere_water__precipitation_mass_flux": "precip_rate", From 5571e621871c2cfbef4704e45c90c564a2f9a6fc Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 6 Apr 2023 19:40:49 +0900 Subject: [PATCH 0662/1472] BMI wants epoch time --- build/source/driver/summa_driver.f90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 61f3a322c..336cf5af1 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -384,7 +384,7 @@ function summa_start_time(this, time) result (bmi_status) double precision, intent(out) :: time integer :: bmi_status - time = dJulianStart*secprday ! unit seconds + time = dJulianStart*secprday - 2440587.5*3600*24 ! unit seconds epoch time bmi_status = BMI_SUCCESS end function summa_start_time @@ -394,7 +394,7 @@ function summa_end_time(this, time) result (bmi_status) double precision, intent(out) :: time integer :: bmi_status - time = dJulianFinsh*secprday ! unit seconds + time = dJulianFinsh*secprday - 2440587.5*3600*24 ! unit seconds epoch time bmi_status = BMI_SUCCESS end function summa_end_time @@ -405,9 +405,9 @@ function summa_current_time(this, time) result (bmi_status) integer :: bmi_status if(this%model%timeStep==1)then - time = dJulianStart*secprday ! unit seconds + time = dJulianStart*secprday - 2440587.5*3600*24 ! unit seconds epoch time else - time = dJulianStart*secprday + (data_step*real(this%model%timeStep-1,dp)) ! unit seconds + time = dJulianStart*secprday- 2440587.5*3600*24 + (data_step*real(this%model%timeStep-1,dp)) ! unit seconds epoch time end if bmi_status = BMI_SUCCESS end function summa_current_time From e17e574035682b4a40731b3ea327be5a1df7d7a2 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 6 Apr 2023 20:29:59 +0900 Subject: [PATCH 0663/1472] epoch was wrong --- build/source/driver/summa_driver.f90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 336cf5af1..81b0d2146 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -384,7 +384,7 @@ function summa_start_time(this, time) result (bmi_status) double precision, intent(out) :: time integer :: bmi_status - time = dJulianStart*secprday - 2440587.5*3600*24 ! unit seconds epoch time + time = (dJulianStart - 2440588.0)*secprday ! unit seconds epoch time bmi_status = BMI_SUCCESS end function summa_start_time @@ -394,7 +394,7 @@ function summa_end_time(this, time) result (bmi_status) double precision, intent(out) :: time integer :: bmi_status - time = dJulianFinsh*secprday - 2440587.5*3600*24 ! unit seconds epoch time + time = (dJulianFinsh - 2440588.0)*secprday ! unit seconds epoch time bmi_status = BMI_SUCCESS end function summa_end_time @@ -405,9 +405,9 @@ function summa_current_time(this, time) result (bmi_status) integer :: bmi_status if(this%model%timeStep==1)then - time = dJulianStart*secprday - 2440587.5*3600*24 ! unit seconds epoch time + time = (dJulianStart - 2440588.0)*secprday ! unit seconds epoch time else - time = dJulianStart*secprday- 2440587.5*3600*24 + (data_step*real(this%model%timeStep-1,dp)) ! unit seconds epoch time + time = dJulianStart - 2440588.0)*secprday + (data_step*real(this%model%timeStep-1,dp)) ! unit seconds epoch time end if bmi_status = BMI_SUCCESS end function summa_current_time From 7d29b1132231ec211619bfcdf0b00844ee0dd91b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 6 Apr 2023 21:46:46 +0900 Subject: [PATCH 0664/1472] unix epoch --- build/source/driver/summa_driver.f90 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 81b0d2146..bfa2d0c65 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -385,6 +385,7 @@ function summa_start_time(this, time) result (bmi_status) integer :: bmi_status time = (dJulianStart - 2440588.0)*secprday ! unit seconds epoch time +print*,time,'s', dJulianStart bmi_status = BMI_SUCCESS end function summa_start_time @@ -395,6 +396,7 @@ function summa_end_time(this, time) result (bmi_status) integer :: bmi_status time = (dJulianFinsh - 2440588.0)*secprday ! unit seconds epoch time +print*,time,'e',dJulianFinsh bmi_status = BMI_SUCCESS end function summa_end_time @@ -407,8 +409,9 @@ function summa_current_time(this, time) result (bmi_status) if(this%model%timeStep==1)then time = (dJulianStart - 2440588.0)*secprday ! unit seconds epoch time else - time = dJulianStart - 2440588.0)*secprday + (data_step*real(this%model%timeStep-1,dp)) ! unit seconds epoch time + time = (dJulianStart - 2440588.0)*secprday + (data_step*real(this%model%timeStep-1,dp)) ! unit seconds epoch time end if +print*,time,this%model%timeStep BMI starts at time step 0!?? bmi_status = BMI_SUCCESS end function summa_current_time From 9c4d132daaa2a552c889808e985178c5e18774c2 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 7 Apr 2023 00:04:55 +0900 Subject: [PATCH 0665/1472] epoch time or seconds not necessary, (can have any time and also put in "day"). Since datastep is in seconds, put all in seconds starting at time 0 so doesn't get too big --- build/source/driver/summa_driver.f90 | 18 ++++++++---------- build/source/driver/summa_run.f90 | 1 + ...example_realization_config_w_summa_bmi.json | 2 +- ...le_realization_config_w_summa_bmi__mac.json | 2 +- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index bfa2d0c65..0f171902f 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -260,17 +260,18 @@ end function summa_update ! **************************************************************************** function summa_update_until(this, time) result (bmi_status) class (summa_bmi), intent(inout) :: this - double precision, intent(in) :: time ! unit seconds (julian_days*secprday, same as get_model__time) + double precision, intent(in) :: time integer :: bmi_status, istat, n_steps, i double precision :: current - istat = this%get_current_time(current) + istat = this%get_current_time(current) ! unit seconds if (time < current) then bmi_status = BMI_FAILURE return end if n_steps = nint( (time - current)/data_step ) + 1 ! model can only do a full data_step + ! SUMMA runs the ending step (so start=end would still run a step) do i = 1, n_steps istat = this%update() end do @@ -384,8 +385,7 @@ function summa_start_time(this, time) result (bmi_status) double precision, intent(out) :: time integer :: bmi_status - time = (dJulianStart - 2440588.0)*secprday ! unit seconds epoch time -print*,time,'s', dJulianStart + time = 0.0 ! unit seconds bmi_status = BMI_SUCCESS end function summa_start_time @@ -395,8 +395,7 @@ function summa_end_time(this, time) result (bmi_status) double precision, intent(out) :: time integer :: bmi_status - time = (dJulianFinsh - 2440588.0)*secprday ! unit seconds epoch time -print*,time,'e',dJulianFinsh + time = (dJulianFinsh - dJulianStart)*secprday ! unit seconds bmi_status = BMI_SUCCESS end function summa_end_time @@ -406,12 +405,11 @@ function summa_current_time(this, time) result (bmi_status) double precision, intent(out) :: time integer :: bmi_status - if(this%model%timeStep==1)then - time = (dJulianStart - 2440588.0)*secprday ! unit seconds epoch time + if(this%model%timeStep==0)then + time = 0.0 ! unit seconds else - time = (dJulianStart - 2440588.0)*secprday + (data_step*real(this%model%timeStep-1,dp)) ! unit seconds epoch time + time = (data_step*real(this%model%timeStep-1,dp)) ! unit seconds end if -print*,time,this%model%timeStep BMI starts at time step 0!?? bmi_status = BMI_SUCCESS end function summa_current_time diff --git a/build/source/driver/summa_run.f90 b/build/source/driver/summa_run.f90 index 27f5ba8fe..2bda95ee9 100644 --- a/build/source/driver/summa_run.f90 +++ b/build/source/driver/summa_run.f90 @@ -47,6 +47,7 @@ program summa_run ! loop through time where numtim has been already computed as ! numtim = nint( (dJulianFinsh - dJulianStart)*secprday/data_step ) + 1 + ! SUMMA runs the ending step (so start=end would still run a step) do modelTimeStep=1,numtim istat = model%update() end do ! (looping through time) diff --git a/test_ngen/example_realization_config_w_summa_bmi.json b/test_ngen/example_realization_config_w_summa_bmi.json index 92b88de18..9295a12e9 100644 --- a/test_ngen/example_realization_config_w_summa_bmi.json +++ b/test_ngen/example_realization_config_w_summa_bmi.json @@ -33,6 +33,6 @@ "time": { "start_time": "2000-01-01 00:30:00", "end_time": "2000-01-03 12:00:00", - "output_interval": 1800 + "output_interval": 5400 } } diff --git a/test_ngen/example_realization_config_w_summa_bmi__mac.json b/test_ngen/example_realization_config_w_summa_bmi__mac.json index 7cb931c59..fe533e91b 100644 --- a/test_ngen/example_realization_config_w_summa_bmi__mac.json +++ b/test_ngen/example_realization_config_w_summa_bmi__mac.json @@ -33,6 +33,6 @@ "time": { "start_time": "2000-01-01 00:30:00", "end_time": "2000-01-03 12:00:00", - "output_interval": 1800 + "output_interval": 5400 } } From 2c8c890ea6a93a73529d5f64b82a36a6487f09ce Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 7 Apr 2023 14:10:01 +0900 Subject: [PATCH 0666/1472] fixing makefiles --- build/makefiles/compile_copernicus | 12 ++++++------ build/makefiles/compile_mac | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/build/makefiles/compile_copernicus b/build/makefiles/compile_copernicus index 08a90f71c..49aff777c 100755 --- a/build/makefiles/compile_copernicus +++ b/build/makefiles/compile_copernicus @@ -7,11 +7,11 @@ module load openblas/0.3.17 module load netcdf-fortran/4.5.2 #### parent directory of the 'build' directory #### -export ROOT_DIR = /globalhome/gwu479/HPC/SummaSundials +export ROOT_DIR=/project/gwf/gwf_cmt/ngen/ngen_code/ngen/extern/summa -export INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include -export LDFLAGS = -L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib -export LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas +export INCLUDES=-"I$(EBROOTNETCDFMINFORTRAN)/include" +export LDFLAGS="-L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib" +export LIBRARIES="$(LDFLAGS) -lnetcdff -lopenblas" -make -f ${ROOT_DIR}/build/makefiles/makefile_cluster -export LD_LIBRARY_PATH=${ROOT_DIR}/bin +make -f ${ROOT_DIR}/summa/build/makefiles/makefile_cluster +export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin diff --git a/build/makefiles/compile_mac b/build/makefiles/compile_mac index 1d8599da7..74717be19 100755 --- a/build/makefiles/compile_mac +++ b/build/makefiles/compile_mac @@ -1,11 +1,11 @@ #! /bin/bash #### parent directory of the 'build' directory #### -export ROOT_DIR = /Users/amedin/Research/SummaSundials +export ROOT_DIR=/Users/amedin/Research/SummaBMI/ngen/extern/summa -export INCLUDES = -I/opt/local/include -I/opt/local/lib -export LDFLAGS = -L/opt/local/lib -export LIBRARIES = $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf +export INCLUDES="-I/opt/local/include -I/opt/local/lib" +export LDFLAGS="-L/opt/local/lib" +export LIBRARIES="$(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf" -make -f ${ROOT_DIR}/build/makefiles/makefile_mac -export LD_LIBRARY_PATH=${ROOT_DIR}/bin +make -f ${ROOT_DIR}/summa/build/makefiles/makefile_mac +export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin From 94f93610203830d1be16f0211cbec941a2a625d6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 7 Apr 2023 14:10:36 +0900 Subject: [PATCH 0667/1472] fixing makefiles --- build/makefiles/compile_copernicus | 12 ++++++------ build/makefiles/compile_graham | 12 ++++++------ build/makefiles/compile_mac | 12 ++++++------ build/makefiles/compile_richardson | 12 ++++++------ 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/build/makefiles/compile_copernicus b/build/makefiles/compile_copernicus index 08a90f71c..fd0b2369a 100755 --- a/build/makefiles/compile_copernicus +++ b/build/makefiles/compile_copernicus @@ -7,11 +7,11 @@ module load openblas/0.3.17 module load netcdf-fortran/4.5.2 #### parent directory of the 'build' directory #### -export ROOT_DIR = /globalhome/gwu479/HPC/SummaSundials +export ROOT_DIR=/globalhome/gwu479/HPC/SummaSundials -export INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include -export LDFLAGS = -L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib -export LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas +export INCLUDES=-"I$(EBROOTNETCDFMINFORTRAN)/include" +export LDFLAGS="-L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib" +export LIBRARIES="$(LDFLAGS) -lnetcdff -lopenblas" -make -f ${ROOT_DIR}/build/makefiles/makefile_cluster -export LD_LIBRARY_PATH=${ROOT_DIR}/bin +make -f ${ROOT_DIR}/summa/build/makefiles/makefile_cluster +export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin diff --git a/build/makefiles/compile_graham b/build/makefiles/compile_graham index f29c0e105..b85ffd771 100755 --- a/build/makefiles/compile_graham +++ b/build/makefiles/compile_graham @@ -7,11 +7,11 @@ module load openblas/0.3.17 module load netcdf-fortran/4.5.2 #### parent directory of the 'build' directory #### -export ROOT_DIR = /home/avanb/SummaSundials +export ROOT_DIR=/home/avanb/SummaSundials -export INCLUDES = -I$(EBROOTNETCDFMINFORTRAN)/include -export LDFLAGS = -L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib -export LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas +export INCLUDES=-"I$(EBROOTNETCDFMINFORTRAN)/include" +export LDFLAGS="-L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib" +export LIBRARIES="$(LDFLAGS) -lnetcdff -lopenblas" -make -f ${ROOT_DIR}/build/makefiles/makefile_cluster -export LD_LIBRARY_PATH=${ROOT_DIR}/bin +make -f ${ROOT_DIR}/summa/build/makefiles/makefile_cluster +export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin diff --git a/build/makefiles/compile_mac b/build/makefiles/compile_mac index 1d8599da7..bf071d9b7 100755 --- a/build/makefiles/compile_mac +++ b/build/makefiles/compile_mac @@ -1,11 +1,11 @@ #! /bin/bash #### parent directory of the 'build' directory #### -export ROOT_DIR = /Users/amedin/Research/SummaSundials +export ROOT_DIR=/Users/amedin/Research/SummaSundials -export INCLUDES = -I/opt/local/include -I/opt/local/lib -export LDFLAGS = -L/opt/local/lib -export LIBRARIES = $(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf +export INCLUDES="-I/opt/local/include -I/opt/local/lib" +export LDFLAGS="-L/opt/local/lib" +export LIBRARIES="$(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf" -make -f ${ROOT_DIR}/build/makefiles/makefile_mac -export LD_LIBRARY_PATH=${ROOT_DIR}/bin +make -f ${ROOT_DIR}/summa/build/makefiles/makefile_mac +export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin diff --git a/build/makefiles/compile_richardson b/build/makefiles/compile_richardson index 726f16ede..a3682a69e 100755 --- a/build/makefiles/compile_richardson +++ b/build/makefiles/compile_richardson @@ -1,11 +1,11 @@ #! /bin/bash #### parent directory of the 'build' directory #### -export ROOT_DIR = /u1/gwu479/SummaSundials +export ROOT_DIR=/u1/gwu479/SummaSundials -export INCLUDES = -I/usr/include -I/usr/local/include -export LDFLAGS = -L/usr/local/lib -export LIBRARIES = $(LDFLAGS) -lnetcdff -lopenblas -lnetcdf +export INCLUDES="-I/usr/include -I/usr/local/include" +export LDFLAGS="-L/usr/local/lib" +export LIBRARIES="$(LDFLAGS) -lnetcdff -lopenblas -lnetcdf" -make -f ${ROOT_DIR}/build/makefiles/makefile_cluster -export LD_LIBRARY_PATH=${ROOT_DIR}/bin +make -f ${ROOT_DIR}/summa/build/makefiles/makefile_cluster +export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin From 1627fd41e993caa112b8b926b89f0db38424e82b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 7 Apr 2023 14:35:43 +0900 Subject: [PATCH 0668/1472] fixing makefiles --- build/makefiles/compile_copernicus | 6 +++--- build/makefiles/compile_graham | 6 +++--- build/makefiles/compile_mac | 2 +- build/makefiles/compile_richardson | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build/makefiles/compile_copernicus b/build/makefiles/compile_copernicus index fd0b2369a..d936d10fe 100755 --- a/build/makefiles/compile_copernicus +++ b/build/makefiles/compile_copernicus @@ -9,9 +9,9 @@ module load netcdf-fortran/4.5.2 #### parent directory of the 'build' directory #### export ROOT_DIR=/globalhome/gwu479/HPC/SummaSundials -export INCLUDES=-"I$(EBROOTNETCDFMINFORTRAN)/include" -export LDFLAGS="-L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib" -export LIBRARIES="$(LDFLAGS) -lnetcdff -lopenblas" +export INCLUDES=-"I${EBROOTNETCDFMINFORTRAN}/include" +export LDFLAGS="-L${EBROOTNETCDFMINFORTRAN}/lib64 -L${EBROOTOPENBLAS}/lib" +export LIBRARIES="${LDFLAGS} -lnetcdff -lopenblas" make -f ${ROOT_DIR}/summa/build/makefiles/makefile_cluster export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin diff --git a/build/makefiles/compile_graham b/build/makefiles/compile_graham index b85ffd771..d5fc9b7c0 100755 --- a/build/makefiles/compile_graham +++ b/build/makefiles/compile_graham @@ -9,9 +9,9 @@ module load netcdf-fortran/4.5.2 #### parent directory of the 'build' directory #### export ROOT_DIR=/home/avanb/SummaSundials -export INCLUDES=-"I$(EBROOTNETCDFMINFORTRAN)/include" -export LDFLAGS="-L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib" -export LIBRARIES="$(LDFLAGS) -lnetcdff -lopenblas" +export INCLUDES=-"I${EBROOTNETCDFMINFORTRAN}/include" +export LDFLAGS="-L${EBROOTNETCDFMINFORTRAN}/lib64 -L${EBROOTOPENBLAS}/lib" +export LIBRARIES="${LDFLAGS} -lnetcdff -lopenblas" make -f ${ROOT_DIR}/summa/build/makefiles/makefile_cluster export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin diff --git a/build/makefiles/compile_mac b/build/makefiles/compile_mac index bf071d9b7..f1a58cd8c 100755 --- a/build/makefiles/compile_mac +++ b/build/makefiles/compile_mac @@ -5,7 +5,7 @@ export ROOT_DIR=/Users/amedin/Research/SummaSundials export INCLUDES="-I/opt/local/include -I/opt/local/lib" export LDFLAGS="-L/opt/local/lib" -export LIBRARIES="$(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf" +export LIBRARIES="${LDFLAGS} -llapack -lgfortran -lnetcdff -lnetcdf" make -f ${ROOT_DIR}/summa/build/makefiles/makefile_mac export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin diff --git a/build/makefiles/compile_richardson b/build/makefiles/compile_richardson index a3682a69e..34bf5778f 100755 --- a/build/makefiles/compile_richardson +++ b/build/makefiles/compile_richardson @@ -5,7 +5,7 @@ export ROOT_DIR=/u1/gwu479/SummaSundials export INCLUDES="-I/usr/include -I/usr/local/include" export LDFLAGS="-L/usr/local/lib" -export LIBRARIES="$(LDFLAGS) -lnetcdff -lopenblas -lnetcdf" +export LIBRARIES="${LDFLAGS} -lnetcdff -lopenblas -lnetcdf" make -f ${ROOT_DIR}/summa/build/makefiles/makefile_cluster export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin From 1b51de165c3261326bf6e0b385b9855127dc73f5 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 7 Apr 2023 14:37:49 +0900 Subject: [PATCH 0669/1472] fixing makefiles --- build/makefiles/compile_copernicus | 6 +++--- build/makefiles/compile_mac | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/makefiles/compile_copernicus b/build/makefiles/compile_copernicus index 49aff777c..5f15709af 100755 --- a/build/makefiles/compile_copernicus +++ b/build/makefiles/compile_copernicus @@ -9,9 +9,9 @@ module load netcdf-fortran/4.5.2 #### parent directory of the 'build' directory #### export ROOT_DIR=/project/gwf/gwf_cmt/ngen/ngen_code/ngen/extern/summa -export INCLUDES=-"I$(EBROOTNETCDFMINFORTRAN)/include" -export LDFLAGS="-L$(EBROOTNETCDFMINFORTRAN)/lib64 -L$(EBROOTOPENBLAS)/lib" -export LIBRARIES="$(LDFLAGS) -lnetcdff -lopenblas" +export INCLUDES=-"I${EBROOTNETCDFMINFORTRAN}/include" +export LDFLAGS="-L${EBROOTNETCDFMINFORTRAN}/lib64 -L${EBROOTOPENBLAS}/lib" +export LIBRARIES="${LDFLAGS} -lnetcdff -lopenblas" make -f ${ROOT_DIR}/summa/build/makefiles/makefile_cluster export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin diff --git a/build/makefiles/compile_mac b/build/makefiles/compile_mac index 74717be19..4777dd2a3 100755 --- a/build/makefiles/compile_mac +++ b/build/makefiles/compile_mac @@ -5,7 +5,7 @@ export ROOT_DIR=/Users/amedin/Research/SummaBMI/ngen/extern/summa export INCLUDES="-I/opt/local/include -I/opt/local/lib" export LDFLAGS="-L/opt/local/lib" -export LIBRARIES="$(LDFLAGS) -llapack -lgfortran -lnetcdff -lnetcdf" +export LIBRARIES="${LDFLAGS} -llapack -lgfortran -lnetcdff -lnetcdf" make -f ${ROOT_DIR}/summa/build/makefiles/makefile_mac export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin From 14129e46c14ddfd484f899d7cd041c5c58ebeb62 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 11 Apr 2023 11:15:06 +0900 Subject: [PATCH 0670/1472] dt_last over data window does not sum to dt, so make sums reflect that for better average flux calculation --- build/source/engine/coupled_em.f90 | 4 ++-- build/source/engine/eval8summaSundials.f90 | 4 ++-- build/source/engine/summaSolveSundialsIDA.f90 | 17 +++++++++++------ build/source/engine/varSubstep.f90 | 2 +- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 8290f3bb2..c3b3b4c53 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1025,7 +1025,7 @@ subroutine coupled_em(& flux_inner%var(iVar)%dat(:) = flux_inner%var(iVar)%dat(:) + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:)*dt_wght end do innerSoilCompress = innerSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1)*dt_wght - if (nSnow>0)innerEffRainfall = innerEffRainfall + ( flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) + flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) )*dt_wght + if (nSnow>0) innerEffRainfall = innerEffRainfall + ( flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) + flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) )*dt_wght ! increment sub-step accepted step dt_solvInner = dt_solvInner + dt_sub @@ -1219,7 +1219,7 @@ subroutine coupled_em(& ! check SWE if(nSnow>0)then effSnowfall = averageThroughfallSnow + averageCanopySnowUnloading - !effRainfall is averageThroughfallRain + averageCanopyLiqDrainage only over snow + ! effRainfall is averageThroughfallRain + averageCanopyLiqDrainage only over snow newSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) delSWE = newSWE - (oldSWE - sfcMeltPond) massBalance = delSWE - (effSnowfall + effRainfall + averageSnowSublimation - averageSnowDrainage*iden_water)*data_step diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index 796fdc13a..4b0be5b95 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -79,7 +79,7 @@ module eval8summaSundials_module ! ********************************************************************************************************** subroutine eval8summaSundials(& ! input: model control - dt_cur, & ! intent(in): current stepsize + dt_cur, & ! intent(in): current stepsize (difference from last ending time) dt, & ! intent(in): entire time step for drainage pond rate nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -584,7 +584,7 @@ subroutine eval8summaSundials(& ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! Below commented out because it seems more correct for Sundials to use the analytical form + ! Below commented out because it seems more correct for Sundials to use the analytical form, ! to conserve energy compute finite difference approximation of (theta_ice)' !if(dt_cur > 1e-14_rkind) then ! scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 7b826aa9f..dbf61be2e 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -212,12 +212,14 @@ subroutine summaSolveSundialsIDA( & logical(lgt) :: feasible ! feasibility flag real(qp) :: t0 ! staring time real(qp) :: dt_last(1) ! last time step + real(qp) :: dt_diff ! difference from previous timestep integer(kind = 8) :: mu, lu ! in banded matrix mode in Sundials type integer(c_long) :: nState ! total number of state variables in Sundials type real(rkind) :: rVec(nStat) ! residual vector integer(i4b) :: iVar, i ! indices integer(i4b) :: nRoot ! total number of roots (events) to find real(qp) :: tret(1) ! time in data window + real(qp) :: tretPrev ! previous time in data window integer(i4b),allocatable :: rootsfound(:) ! crossing direction of discontinuities integer(i4b),allocatable :: rootdir(:) ! forced crossing direction of discontinuities logical(lgt) :: tinystep ! if step goes below small size @@ -443,6 +445,7 @@ subroutine summaSolveSundialsIDA( & tret(1) = t0 ! initial time do while(tret(1) < dt) + tretPrev = tret(1) ! call this at beginning of step to reduce root bouncing (only looking in one direction) if(detect_events .and. .not.tinystep)then call find_rootdir(eqns_data, rootdir) @@ -467,13 +470,14 @@ subroutine summaSolveSundialsIDA( & enddo if(tooMuchMelt)exit - ! get the last stepsize + ! get the last stepsize and difference from previous end time, not necessarily the same retval = FIDAGetLastStep(ida_mem, dt_last) + dt_diff = tret(1) - tretPrev ! compute the flux and the residual vector for a given state vector call eval8summaSundials(& ! input: model control - dt_last(1), & ! intent(in): current stepsize + dt_diff, & ! intent(in): current stepsize eqns_data%dt, & ! intent(in): total data step eqns_data%nSnow, & ! intent(in): number of snow layers eqns_data%nSoil, & ! intent(in): number of soil layers @@ -537,14 +541,15 @@ subroutine summaSolveSundialsIDA( & rVec, & ! intent(out): residual vector eqns_data%err,eqns_data%message) ! intent(out): error control - ! sum of fluxes + ! sum of fluxes smoothed over timestep do iVar=1,size(flux_meta) - flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * dt_last(1) + flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * dt_diff end do - ! sum of mLayerCmpress over the time step, since using prev value not * dt_last(1) + ! sum of mLayerCmpress over the time step, since using prev value + ! Note: correct for difference between dt_diff and dt_last(1), e.g. if dt_last is longer than dt_diff should be adding less than the whole mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & - * ( eqns_data%mLayerMatricHeadTrial(:) - eqns_data%mLayerMatricHeadPrev(:) ) + * ( eqns_data%mLayerMatricHeadTrial(:) - eqns_data%mLayerMatricHeadPrev(:) )*dt_diff/dt_last(1) ! save required quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 7cc7b5bb0..1652295b3 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -114,7 +114,7 @@ subroutine varSubstep(& indx_data, & ! intent(inout) : index data prog_data, & ! intent(inout) : model prognostic variables for a local HRU diag_data, & ! intent(inout) : model diagnostic variables for a local HRU - flux_data, & ! intent(inout) : model fluxes for a local HRU + flux_data, & ! intent(inout) : model fluxes for a local HRU flux_mean, & ! intent(inout) : mean model fluxes for a local HRU deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables bvar_data, & ! intent(in) : model variables for the local basin From 32b88793709d4194069bf19719b5391960e2648f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 11 Apr 2023 11:22:58 +0900 Subject: [PATCH 0671/1472] massBalance checks default leave at just for bEuler --- build/source/engine/coupled_em.f90 | 10 +++++++++- build/source/engine/varSubstep.f90 | 7 +++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index c3b3b4c53..bc9a09457 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1131,6 +1131,8 @@ subroutine coupled_em(& ! associate local variables with information in the data structures associate(& + ! model decisions + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! choice of numerical method, backward Euler or SUNDIALS/IDA ! model forcing scalarSnowfall => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSnowfall) )%dat(1) ,& ! computed snowfall rate (kg m-2 s-1) scalarRainfall => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarRainfall) )%dat(1) ,& ! computed rainfall rate (kg m-2 s-1) @@ -1164,10 +1166,16 @@ subroutine coupled_em(& scalarTotalSoilLiq => diag_data%var(iLookDIAG%scalarTotalSoilLiq)%dat(1) & ! total liquid water in the soil column (kg m-2) ) ! (association of local variables with information in the data structures + ! identify the need to check the mass balance + select case(ixNumericalMethod) + case(sundials); checkMassBalance = .false. ! currently only for bEuler because of how store variables + case(bEuler); checkMassBalance = .true. + case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return + end select + ! ----- ! * balance checks for the canopy... ! ---------------------------------- - checkMassBalance = .true. ! if computing the vegetation flux if(computeVegFlux)then diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 1652295b3..60e9b04dc 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -453,9 +453,9 @@ subroutine varSubstep(& return endif - ! identify the need to check the mass balance, only for bEuler because of how store variables + ! identify the need to check the mass balance select case(ixNumericalMethod) - case(sundials); checkMassBalance = .false. + case(sundials); checkMassBalance = .false. ! currently only for bEuler because of how store variables case(bEuler); checkMassBalance = .true. ! (.not.scalarSolution) case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return end select @@ -963,8 +963,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! ----------------------- ! NOTE: should not need to do this, since mass balance is checked in the solver - ! for sundials will not work since fluxes are averaged over data window for output but canopyBalance0 only off last step - ! so not currently checked in sundials, could cause problems if should modify nrgFlux + ! if do not check could cause problems if should modify nrgFlux if(checkMassBalance)then ! check mass balance for the canopy From 1d728d37a8e7e9d224c2a1bf6c78c41fc4f7c205 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 11 Apr 2023 11:32:11 +0900 Subject: [PATCH 0672/1472] printing things and turning on all checks --- build/source/engine/coupled_em.f90 | 10 +++--- build/source/engine/summaSolveSundialsIDA.f90 | 2 +- build/source/engine/varSubstep.f90 | 36 +++++++++---------- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index bc9a09457..2a9db1829 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1168,7 +1168,7 @@ subroutine coupled_em(& ! identify the need to check the mass balance select case(ixNumericalMethod) - case(sundials); checkMassBalance = .false. ! currently only for bEuler because of how store variables + case(sundials); checkMassBalance = .true. ! currently only for bEuler because of how store variables case(bEuler); checkMassBalance = .true. case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return end select @@ -1186,8 +1186,6 @@ subroutine coupled_em(& ! NOTE: need to put the balance checks in the sub-step loop so that we can re-compute if necessary scalarCanopyWatBalError = balanceCanopyWater1 - (balanceCanopyWater0 + (scalarSnowfall - averageThroughfallSnow)*data_step + (scalarRainfall - averageThroughfallRain)*data_step & - averageCanopySnowUnloading*data_step - averageCanopyLiqDrainage*data_step + averageCanopySublimation*data_step + averageCanopyEvaporation*data_step) - if(abs(scalarCanopyWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then - print*, '** canopy water balance error:' write(*,'(a,1x,f20.10)') 'data_step = ', data_step write(*,'(a,1x,f20.10)') 'balanceCanopyWater0 = ', balanceCanopyWater0 write(*,'(a,1x,f20.10)') 'balanceCanopyWater1 = ', balanceCanopyWater1 @@ -1200,6 +1198,7 @@ subroutine coupled_em(& write(*,'(a,1x,f20.10)') 'averageCanopySublimation = ', averageCanopySublimation!*data_step write(*,'(a,1x,f20.10)') 'averageCanopyEvaporation = ', averageCanopyEvaporation!*data_step write(*,'(a,1x,f20.10)') 'scalarCanopyWatBalError = ', scalarCanopyWatBalError + if(abs(scalarCanopyWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then message=trim(message)//'canopy hydrology does not balance' err=20; return end if @@ -1231,7 +1230,6 @@ subroutine coupled_em(& newSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) delSWE = newSWE - (oldSWE - sfcMeltPond) massBalance = delSWE - (effSnowfall + effRainfall + averageSnowSublimation - averageSnowDrainage*iden_water)*data_step - if(abs(massBalance) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then print*, 'nSnow = ', nSnow print*, 'nSub = ', nSub write(*,'(a,1x,f20.10)') 'data_step = ', data_step @@ -1244,6 +1242,7 @@ subroutine coupled_em(& write(*,'(a,1x,f20.10)') 'snwDrainage = ', averageSnowDrainage*iden_water*data_step write(*,'(a,1x,f20.10)') 'sfcMeltPond = ', sfcMeltPond write(*,'(a,1x,f20.10)') 'massBalance = ', massBalance + if(abs(massBalance) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then message=trim(message)//'SWE does not balance' err=20; return endif ! if failed mass balance check @@ -1286,7 +1285,6 @@ subroutine coupled_em(& ! check the soil water balance scalarSoilWatBalError = balanceSoilWater1 - (balanceSoilWater0 + (balanceSoilInflux + balanceSoilET - balanceSoilBaseflow - balanceSoilDrainage - balanceSoilCompress) ) - if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues write(*,*) 'solution method = ', ixSolution write(*,'(a,1x,f20.10)') 'data_step = ', data_step write(*,'(a,1x,f20.10)') 'balanceSoilCompress = ', balanceSoilCompress @@ -1299,7 +1297,7 @@ subroutine coupled_em(& write(*,'(a,1x,f20.10)') 'balanceSoilDrainage = ', balanceSoilDrainage write(*,'(a,1x,f20.10)') 'balanceSoilET = ', balanceSoilET write(*,'(a,1x,f20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError - ! error control + if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues message=trim(message)//'soil hydrology does not balance' err=20; return end if diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index dbf61be2e..969233f90 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -550,7 +550,7 @@ subroutine summaSolveSundialsIDA( & ! Note: correct for difference between dt_diff and dt_last(1), e.g. if dt_last is longer than dt_diff should be adding less than the whole mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & * ( eqns_data%mLayerMatricHeadTrial(:) - eqns_data%mLayerMatricHeadPrev(:) )*dt_diff/dt_last(1) - +print*,dt_diff,dt_last(1),tret(1),tretPrev ! save required quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 60e9b04dc..2274ab92e 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -455,7 +455,7 @@ subroutine varSubstep(& ! identify the need to check the mass balance select case(ixNumericalMethod) - case(sundials); checkMassBalance = .false. ! currently only for bEuler because of how store variables + case(sundials); checkMassBalance = .true. ! currently only for bEuler because of how store variables case(bEuler); checkMassBalance = .true. ! (.not.scalarSolution) case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return end select @@ -1012,15 +1012,15 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues - !write(*,'(a,1x,f20.10)') 'dt = ', dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - !write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 - !write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 - !write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt - !write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - !write(*,'(a,1x,f20.10)') 'liqError = ', liqError + write(*,'(a,1x,f20.10)') 'dt = ', dt + write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 + write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 + write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt + write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt + write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt + write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt + write(*,'(a,1x,f20.10)') 'liqError = ', liqError waterBalanceError = .true. return endif ! if there is a water balance error @@ -1036,14 +1036,14 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues - !write(*,'(a,1x,f20.10)') 'dt = ', dt - !write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 - !write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 - !write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux - !write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink - !write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink - !write(*,'(a,1x,f20.10)') 'compSink = ', compSink - !write(*,'(a,1x,f20.10)') 'liqError = ', liqError + write(*,'(a,1x,f20.10)') 'dt = ', dt + write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 + write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 + write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux + write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink + write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink + write(*,'(a,1x,f20.10)') 'compSink = ', compSink + write(*,'(a,1x,f20.10)') 'liqError = ', liqError waterBalanceError = .true. return endif ! if there is a water balance error From 1f7ab603ce1ca2d97f2fc6bf4a67b97e3eccfb23 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 11 Apr 2023 15:58:55 +0900 Subject: [PATCH 0673/1472] correcting average flux calculation to be off smoothed values and printing some things in eval8summaSundials --- build/source/engine/eval8summaSundials.f90 | 45 +++++++++------- build/source/engine/summaSolveSundialsIDA.f90 | 51 +++++++++++++------ build/source/engine/systemSolvSundials.f90 | 7 ++- build/source/engine/type4IDA.f90 | 9 ++-- 4 files changed, 75 insertions(+), 37 deletions(-) diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index 4b0be5b95..d515288b4 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -79,7 +79,7 @@ module eval8summaSundials_module ! ********************************************************************************************************** subroutine eval8summaSundials(& ! input: model control - dt_cur, & ! intent(in): current stepsize (difference from last ending time) + dt_cur, & ! intent(in): step size to last time solution dt, & ! intent(in): entire time step for drainage pond rate nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -133,6 +133,7 @@ subroutine eval8summaSundials(& scalarAquiferStoragePrev,& ! intent(in): value of storage of water in the aquifer (m) mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) + mLayerMatricHeadPrime, & ! intent(out): trial value for prime total water matric potential (m s-1) ! input-output: baseflow ixSaturation, & ! intent(inout): index of the lowest saturated layer dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) @@ -159,7 +160,7 @@ subroutine eval8summaSundials(& ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control - real(rkind),intent(in) :: dt_cur ! current stepsize + real(rkind),intent(in) :: dt_cur ! step size to last time solution real(rkind),intent(in) :: dt ! entire time step for drainage pond rate integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers @@ -213,6 +214,7 @@ subroutine eval8summaSundials(& real(rkind),intent(in) :: scalarAquiferStoragePrev ! previous value of storage of water in the aquifer (m) real(rkind),intent(in) :: mLayerEnthalpyPrev(:) ! previous vector of enthalpy for snow+soil layers (J m-3) real(rkind),intent(out) :: mLayerEnthalpyTrial(:) ! trial vector of enthalpy for snow+soil layers (J m-3) + real(rkind),intent(out) :: mLayerMatricHeadPrime(:) ! deriviative value for prime total water matric potential (m s-1) ! input-output: baseflow integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) @@ -237,7 +239,6 @@ subroutine eval8summaSundials(& real(rkind) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) real(rkind),dimension(nLayers) :: mLayerTempPrime ! derivative value for temperature of layers in the snow and soil domains (K) real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! derivative value for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! derivative value for total water matric potential (m) real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! derivative value for liquid water matric potential (m) real(rkind) :: scalarAquiferStoragePrime ! derivative value of storage of water in the aquifer (m) ! derivative of diagnostic variables @@ -264,6 +265,8 @@ subroutine eval8summaSundials(& real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m + real(rkind) :: liqError ! water balance error + real(rkind) :: fluxNet ! net water fluxes (kg m-2 s-1) ! -------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures @@ -397,7 +400,9 @@ subroutine eval8summaSundials(& endif ! initialize to state variable from the last update + scalarCanairTempTrial = realMissing ! should be set to previous values if splits, but for now operator splitting is not hooked up scalarCanopyTempTrial = scalarCanopyTempPrev + scalarCanopyWatTrial = scalarCanopyLiqPrev + scalarCanopyIcePrev scalarCanopyLiqTrial = scalarCanopyLiqPrev scalarCanopyIceTrial = scalarCanopyIcePrev mLayerTempTrial = mLayerTempPrev @@ -567,31 +572,23 @@ subroutine eval8summaSundials(& mLayerTempPrev, & ! intent(in): previous temperature mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil - mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) + mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) ! input: pre-computed derivatives dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - dCanEnthalpy_dTk, & ! intent(in): derivatives in canopy enthalpy w.r.t. temperature - dCanEnthalpy_dWat, & ! intent(in): derivatives in canopy enthalpy w.r.t. water state - dEnthalpy_dTk, & ! intent(in): derivatives in layer enthalpy w.r.t. temperature - dEnthalpy_dWat, & ! intent(in): derivatives in layer enthalpy w.r.t. water state + dCanEnthalpy_dTk, & ! intent(in): derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat, & ! intent(in): derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk, & ! intent(in): derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat, & ! intent(in): derivatives in layer enthalpy w.r.t. water state ! output heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! Below commented out because it seems more correct for Sundials to use the analytical form, - ! to conserve energy compute finite difference approximation of (theta_ice)' - !if(dt_cur > 1e-14_rkind) then - ! scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur - ! do concurrent (iLayer=1:nLayers) - ! mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur - ! end do - !endif ! if dt_cur is not too small else if(ixHowHeatCap == closedForm)then call computHeatCapAnalytic(& ! input: control variables @@ -797,6 +794,19 @@ subroutine eval8summaSundials(& dt1 = 1._qp endif + fluxNet = flux_data%var(iLookFLUX%scalarRainfall)%dat(1) + flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) & + - flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) - flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) + liqError = (scalarCanopyLiqPrev + scalarCanopyIcePrev + fluxNet*dt_cur) - scalarCanopyWatTrial + write(*,'(a,1x,f20.10)') 'dt = ', dt_cur + write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', scalarCanopyLiqPrev + scalarCanopyIcePrev + write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', scalarCanopyLiqPrev + scalarCanopyIcePrev + fluxNet*dt_cur + write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', flux_data%var(iLookFLUX%scalarRainfall)%dat(1)*dt_cur + write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1)*dt_cur + write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1)*dt_cur + write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1)*dt_cur + write(*,'(a,1x,f20.10)') 'liqError = ', liqError + ! end association with the information in the data structures end associate @@ -884,7 +894,7 @@ integer(c_int) function eval8summa4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%bvar_data, & ! intent(in): average model variables for the entire basin eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU ! input-output: data structures - eqns_data%indx_data, & ! intent(inou): index data + eqns_data%indx_data, & ! intent(inout): index data eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables @@ -912,6 +922,7 @@ integer(c_int) function eval8summa4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) + eqns_data%mLayerMatricHeadPrime, & ! intent(out): derivative value for total water matric potential (m s-1) ! input-output: baseflow eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 969233f90..35db502b3 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -223,9 +223,12 @@ subroutine summaSolveSundialsIDA( & integer(i4b),allocatable :: rootsfound(:) ! crossing direction of discontinuities integer(i4b),allocatable :: rootdir(:) ! forced crossing direction of discontinuities logical(lgt) :: tinystep ! if step goes below small size - logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true - logical(lgt),parameter :: detect_events = .true. ! flag to do event detection and restarting, default true - logical(lgt),parameter :: use_fdJac = .false. ! flag to use finite difference Jacobian, default false + type(var_dlength) :: flux_prev ! previous model fluxes for a local HRU + real(rkind),allocatable :: mLayerMatricHeadPrimePrev(:) ! previous derivative value for total water matric potential (m s-1) + real(rkind),allocatable :: dCompress_dPsiPrev(:) ! previous derivative value soil compression + logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true + logical(lgt),parameter :: detect_events = .true. ! flag to do event detection and restarting, default true + logical(lgt),parameter :: use_fdJac = .false. ! flag to use finite difference Jacobian, default false ! ----------------------------------------------------------------------------------------------------- @@ -267,10 +270,13 @@ subroutine summaSolveSundialsIDA( & if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif eqns_data%diag_data = diag_data - ! allocate space for the temporary flux variable structure + ! allocate space for the temporary and previousflux variable structure call allocLocal(flux_meta(:),eqns_data%flux_data,nSnow,nSoil,err,message) if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif eqns_data%flux_data = flux_data + call allocLocal(flux_meta(:),flux_prev,nSnow,nSoil,err,message) + if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif + flux_prev = flux_data ! allocate space for the derivative structure call allocLocal(deriv_meta(:),eqns_data%deriv_data,nSnow,nSoil,err,message) @@ -304,6 +310,9 @@ subroutine summaSolveSundialsIDA( & allocate( eqns_data%mLayerVolFracLiqPrev(nLayers) ) allocate( eqns_data%mLayerEnthalpyTrial(nLayers) ) allocate( eqns_data%mLayerEnthalpyPrev(nLayers) ) + allocate( eqns_data%mLayerMatricHeadPrime(nSoil) ) + allocate( mLayerMatricHeadPrimePrev(nSoil) ) + allocate( dCompress_dPsiPrev(nSoil) ) allocate( eqns_data%fluxVec(nState) ) allocate( eqns_data%resSink(nState) ) @@ -433,8 +442,9 @@ subroutine summaSolveSundialsIDA( & eqns_data%mLayerVolFracLiqPrev(:) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(:) eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) eqns_data%scalarAquiferStoragePrev = prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) + mLayerMatricHeadPrimePrev(:) = 0._rkind + dCompress_dPsiPrev(:) = 0._rkind eqns_data%ixSaturation = ixSaturation - tinystep = .false. !********************************************************************************** @@ -443,9 +453,10 @@ subroutine summaSolveSundialsIDA( & !********************************************************************************** tret(1) = t0 ! initial time + tretPrev = tret(1) do while(tret(1) < dt) - tretPrev = tret(1) + ! call this at beginning of step to reduce root bouncing (only looking in one direction) if(detect_events .and. .not.tinystep)then call find_rootdir(eqns_data, rootdir) @@ -477,7 +488,7 @@ subroutine summaSolveSundialsIDA( & ! compute the flux and the residual vector for a given state vector call eval8summaSundials(& ! input: model control - dt_diff, & ! intent(in): current stepsize + dt_diff, & ! intent(in): step size to last time solution eqns_data%dt, & ! intent(in): total data step eqns_data%nSnow, & ! intent(in): number of snow layers eqns_data%nSoil, & ! intent(in): number of soil layers @@ -507,7 +518,7 @@ subroutine summaSolveSundialsIDA( & eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: here we need to pass some extra variables that do not get updated in in the Sundials loops + ! input-output: here we need to pass some extra variables that do not get updated in in the Sundials loops (without operator splitting could cut prev values) eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) @@ -531,6 +542,7 @@ subroutine summaSolveSundialsIDA( & eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) + eqns_data%mLayerMatricHeadPrime, & ! intent(out): derivative value for total water matric potential (m s-1) ! input-output: baseflow eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) @@ -541,16 +553,18 @@ subroutine summaSolveSundialsIDA( & rVec, & ! intent(out): residual vector eqns_data%err,eqns_data%message) ! intent(out): error control - ! sum of fluxes smoothed over timestep + ! sum of fluxes smoothed over the time step do iVar=1,size(flux_meta) - flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * dt_diff + flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * (dt_diff*3. - dt_last(1))/2. & + - flux_prev%var(iVar)%dat(:) * (dt_diff - dt_last(1))/2. end do - ! sum of mLayerCmpress over the time step, since using prev value - ! Note: correct for difference between dt_diff and dt_last(1), e.g. if dt_last is longer than dt_diff should be adding less than the whole - mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & - * ( eqns_data%mLayerMatricHeadTrial(:) - eqns_data%mLayerMatricHeadPrev(:) )*dt_diff/dt_last(1) -print*,dt_diff,dt_last(1),tret(1),tretPrev + ! sum of mLayerCmpress smoothed over the time step + mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) * eqns_data%mLayerMatricHeadPrime(:) * (dt_diff*3. - dt_last(1))/2. & + - dCompress_dPsiPrev(:) * mLayerMatricHeadPrimePrev(:) * (dt_diff - dt_last(1))/2. + +print*,dt_diff,dt_last(1),tret(1) + ! save required quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial @@ -563,6 +577,10 @@ subroutine summaSolveSundialsIDA( & eqns_data%mLayerVolFracLiqPrev(:) = eqns_data%mLayerVolFracLiqTrial(:) eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial + mLayerMatricHeadPrimePrev(:) = eqns_data%mLayerMatricHeadPrime(:) + dCompress_dPsiPrev(:) = eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) + tretPrev = tret(1) + flux_prev = flux_data ! Restart for where vegetation and layers cross freezing point if(detect_events)then @@ -618,6 +636,9 @@ subroutine summaSolveSundialsIDA( & deallocate( eqns_data%mLayerVolFracLiqPrev ) deallocate( eqns_data%mLayerEnthalpyTrial ) deallocate( eqns_data%mLayerEnthalpyPrev ) + deallocate( eqns_data%mLayerMatricHeadPrime) + deallocate( mLayerMatricHeadPrimePrev ) + deallocate( dCompress_dPsiPrev ) deallocate( eqns_data%fluxVec ) deallocate( eqns_data%resSink ) deallocate( rootsfound ) diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index c11965597..0b5f3f6d8 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -211,6 +211,7 @@ subroutine systemSolvSundials(& real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt_out (=dt) real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix + real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! derivative value for total water matric potential (m s-1) logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step real(rkind) :: fOld ! function values (-); NOTE: dimensionless because scaled ! enthalpy derivatives @@ -308,8 +309,9 @@ subroutine systemSolvSundials(& call allocLocal(flux_meta(:),flux_init,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - ! allocate space for mLayerCmpress_sum at the start of the time step + ! allocate space for mLayerCmpress_sum and mLayerMatricHeadPrime at the start of the time step allocate( mLayerCmpress_sum(nSoil) ) + allocate( mLayerMatricHeadPrime(nSoil) ) ! allocate space for the baseflow derivatives ! NOTE: needs allocation because only used when baseflow sinks are active @@ -459,6 +461,7 @@ subroutine systemSolvSundials(& scalarAquiferStorage, & ! intent(in): value of storage of water in the aquifer (m) mLayerEnthalpy, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) mLayerEnthalpy, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) + mLayerMatricHeadPrime, & ! intent(out): derivative value for total water matric potential (m s-1) ! input-output: baseflow ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) @@ -581,6 +584,8 @@ subroutine systemSolvSundials(& stateVecTrial = stateVecNew ! free memory + deallocate( mLayerCmpress_sum) + deallocate( mLayerMatricHeadPrime) deallocate(dBaseflow_dMatric) ! end associate statements diff --git a/build/source/engine/type4IDA.f90 b/build/source/engine/type4IDA.f90 index 334b808eb..14cf5e10d 100644 --- a/build/source/engine/type4IDA.f90 +++ b/build/source/engine/type4IDA.f90 @@ -52,6 +52,8 @@ module type4IDA real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation real(rkind), allocatable :: atol(:) ! vector of absolute tolerances real(rkind), allocatable :: rtol(:) ! vector of relative tolerances + real(rkind), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind), allocatable :: mLayerTempPrev(:) ! vector of layer temperature (K) at previous step real(rkind), allocatable :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) real(rkind), allocatable :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) real(rkind), allocatable :: mLayerMatricHeadPrev(:) ! vector of total water matric potential (m) at previous step @@ -61,12 +63,11 @@ module type4IDA real(rkind), allocatable :: mLayerVolFracIcePrev(:) ! value for volumetric fraction of ice (-) at previous step real(rkind), allocatable :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) real(rkind), allocatable :: mLayerVolFracLiqPrev(:) ! value for volumetric fraction of liquid water (-) at previous step - real(rkind) :: scalarAquiferStoragePrev - real(rkind) :: scalarAquiferStorageTrial + real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + real(rkind) :: scalarAquiferStoragePrev ! value of storage of water in the aquifer (m) at previous step real(rkind), allocatable :: mLayerEnthalpyTrial(:) ! trial enthalpy of snow and soil (J m-3) real(rkind), allocatable :: mLayerEnthalpyPrev(:) ! enthalpy of snow and soil (J m-3) at previous step - real(rkind), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind), allocatable :: mLayerTempPrev(:) ! vector of layer temperature (K) at previous step + real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! derivative value for total water matric potential (m s-1) real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) integer(i4b) :: ixSaturation integer(i4b) :: err ! error code From 928508c96b1074333a1f8f3275d156fabd3150e4 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 11 Apr 2023 22:48:52 +0900 Subject: [PATCH 0674/1472] changing flux sum addition --- build/source/engine/summaSolveSundialsIDA.f90 | 14 +++++++++----- build/source/engine/varSubstep.f90 | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 35db502b3..8fabcd205 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -456,7 +456,6 @@ subroutine summaSolveSundialsIDA( & tretPrev = tret(1) do while(tret(1) < dt) - ! call this at beginning of step to reduce root bouncing (only looking in one direction) if(detect_events .and. .not.tinystep)then call find_rootdir(eqns_data, rootdir) @@ -555,13 +554,18 @@ subroutine summaSolveSundialsIDA( & ! sum of fluxes smoothed over the time step do iVar=1,size(flux_meta) - flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * (dt_diff*3. - dt_last(1))/2. & - - flux_prev%var(iVar)%dat(:) * (dt_diff - dt_last(1))/2. +! flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * (dt_diff*3. - dt_last(1))/2. & +! - flux_prev%var(iVar)%dat(:) * (dt_diff - dt_last(1))/2. + flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + ( eqns_data%flux_data%var(iVar)%dat(:) & + + flux_prev%var(iVar)%dat(:) ) *dt_diff/2._rkind end do ! sum of mLayerCmpress smoothed over the time step - mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) * eqns_data%mLayerMatricHeadPrime(:) * (dt_diff*3. - dt_last(1))/2. & - - dCompress_dPsiPrev(:) * mLayerMatricHeadPrimePrev(:) * (dt_diff - dt_last(1))/2. +! mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) * eqns_data%mLayerMatricHeadPrime(:) * (dt_diff*3. - dt_last(1))/2. & +! - dCompress_dPsiPrev(:) * mLayerMatricHeadPrimePrev(:) * (dt_diff - dt_last(1))/2. + mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + ( eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) * eqns_data%mLayerMatricHeadPrime(:) & + + dCompress_dPsiPrev(:) * mLayerMatricHeadPrimePrev(:) ) * dt_diff/2._rkind + print*,dt_diff,dt_last(1),tret(1) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 2274ab92e..781974d35 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -327,7 +327,7 @@ subroutine varSubstep(& call systemSolvSundials(& ! input: model control dtSubstep, & ! intent(in): time step (s) - whole_step, & ! intent(in): entire time step (s), right now same as dtSubstep but might change with operator splitting + whole_step, & ! intent(in): entire time step (s), right now same as dtSubstep but might change with operator splitting nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to denote first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call From 3599b4a943209dc91a0cfd72c136bc9d3951a041 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 12 Apr 2023 13:07:46 +0900 Subject: [PATCH 0675/1472] instantaneous fluxes in sundials, can't check MB in varSubstep --- build/source/engine/summaSolveSundialsIDA.f90 | 5 ----- build/source/engine/varSubstep.f90 | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 8fabcd205..fdbde2209 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -554,15 +554,10 @@ subroutine summaSolveSundialsIDA( & ! sum of fluxes smoothed over the time step do iVar=1,size(flux_meta) -! flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * (dt_diff*3. - dt_last(1))/2. & -! - flux_prev%var(iVar)%dat(:) * (dt_diff - dt_last(1))/2. flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + ( eqns_data%flux_data%var(iVar)%dat(:) & + flux_prev%var(iVar)%dat(:) ) *dt_diff/2._rkind end do - ! sum of mLayerCmpress smoothed over the time step -! mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) * eqns_data%mLayerMatricHeadPrime(:) * (dt_diff*3. - dt_last(1))/2. & -! - dCompress_dPsiPrev(:) * mLayerMatricHeadPrimePrev(:) * (dt_diff - dt_last(1))/2. mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + ( eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) * eqns_data%mLayerMatricHeadPrime(:) & + dCompress_dPsiPrev(:) * mLayerMatricHeadPrimePrev(:) ) * dt_diff/2._rkind diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 781974d35..03a6bf7d7 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -455,7 +455,7 @@ subroutine varSubstep(& ! identify the need to check the mass balance select case(ixNumericalMethod) - case(sundials); checkMassBalance = .true. ! currently only for bEuler because of how store variables + case(sundials); checkMassBalance = .false. ! only for bEuler because sundials has instantaneous fluxes only case(bEuler); checkMassBalance = .true. ! (.not.scalarSolution) case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return end select @@ -962,7 +962,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! * check mass balance... ! ----------------------- - ! NOTE: should not need to do this, since mass balance is checked in the solver + ! NOTE: should not need to do this, since mass balance is checked in the solver, and cannot do for Sundials ! if do not check could cause problems if should modify nrgFlux if(checkMassBalance)then From c9fd17b145617702b2e75deeaf41baf0c578ace6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 12 Apr 2023 13:48:48 +0900 Subject: [PATCH 0676/1472] print evap --- build/source/engine/summaSolveSundialsIDA.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index fdbde2209..40c3d59c0 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -557,12 +557,13 @@ subroutine summaSolveSundialsIDA( & flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + ( eqns_data%flux_data%var(iVar)%dat(:) & + flux_prev%var(iVar)%dat(:) ) *dt_diff/2._rkind end do - mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + ( eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) * eqns_data%mLayerMatricHeadPrime(:) & + dCompress_dPsiPrev(:) * mLayerMatricHeadPrimePrev(:) ) * dt_diff/2._rkind print*,dt_diff,dt_last(1),tret(1) +print*,( eqns_data%flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) + flux_prev%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ) *dt_diff/2._rkind, & + eqns_data%flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,flux_prev%var(iLookFLUX%scalarCanopyEvaporation)%dat(1),flux_sum%var(iLookFLUX%scalarCanopyEvaporation)%dat(1)/tret(1) ! save required quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial From 22d7c51ca52557528ef652b0e3860994d6119202 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 12 Apr 2023 13:57:44 +0900 Subject: [PATCH 0677/1472] error in assignment --- build/source/engine/summaSolveSundialsIDA.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 40c3d59c0..a14997a26 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -276,7 +276,7 @@ subroutine summaSolveSundialsIDA( & eqns_data%flux_data = flux_data call allocLocal(flux_meta(:),flux_prev,nSnow,nSoil,err,message) if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif - flux_prev = flux_data + flux_prev = eqns_data%flux_data ! allocate space for the derivative structure call allocLocal(deriv_meta(:),eqns_data%deriv_data,nSnow,nSoil,err,message) @@ -579,8 +579,8 @@ subroutine summaSolveSundialsIDA( & eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial mLayerMatricHeadPrimePrev(:) = eqns_data%mLayerMatricHeadPrime(:) dCompress_dPsiPrev(:) = eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) - tretPrev = tret(1) - flux_prev = flux_data + tretPrev = tret(1) + flux_prev = eqns_data%flux_data ! Restart for where vegetation and layers cross freezing point if(detect_events)then From 95b8b2d2c1ee9eb6a65f4e4dbbf8a4263e3a3431 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 12 Apr 2023 14:03:39 +0900 Subject: [PATCH 0678/1472] turning off some printing --- build/source/engine/eval8summaSundials.f90 | 15 --------------- build/source/engine/summaSolveSundialsIDA.f90 | 5 ----- 2 files changed, 20 deletions(-) diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index d515288b4..8f8038e74 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -265,8 +265,6 @@ subroutine eval8summaSundials(& real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m - real(rkind) :: liqError ! water balance error - real(rkind) :: fluxNet ! net water fluxes (kg m-2 s-1) ! -------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures @@ -794,19 +792,6 @@ subroutine eval8summaSundials(& dt1 = 1._qp endif - fluxNet = flux_data%var(iLookFLUX%scalarRainfall)%dat(1) + flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) & - - flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) - flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) - liqError = (scalarCanopyLiqPrev + scalarCanopyIcePrev + fluxNet*dt_cur) - scalarCanopyWatTrial - write(*,'(a,1x,f20.10)') 'dt = ', dt_cur - write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', scalarCanopyLiqPrev + scalarCanopyIcePrev - write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', scalarCanopyLiqPrev + scalarCanopyIcePrev + fluxNet*dt_cur - write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', flux_data%var(iLookFLUX%scalarRainfall)%dat(1)*dt_cur - write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1)*dt_cur - write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1)*dt_cur - write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1)*dt_cur - write(*,'(a,1x,f20.10)') 'liqError = ', liqError - ! end association with the information in the data structures end associate diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index a14997a26..b09262d0d 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -560,11 +560,6 @@ subroutine summaSolveSundialsIDA( & mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + ( eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) * eqns_data%mLayerMatricHeadPrime(:) & + dCompress_dPsiPrev(:) * mLayerMatricHeadPrimePrev(:) ) * dt_diff/2._rkind - -print*,dt_diff,dt_last(1),tret(1) -print*,( eqns_data%flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) + flux_prev%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ) *dt_diff/2._rkind, & - eqns_data%flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,flux_prev%var(iLookFLUX%scalarCanopyEvaporation)%dat(1),flux_sum%var(iLookFLUX%scalarCanopyEvaporation)%dat(1)/tret(1) - ! save required quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial From 6ba024db1a373d7885ef97b0023df6b86ee0439d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 12 Apr 2023 14:05:25 +0900 Subject: [PATCH 0679/1472] turning off more printing --- build/source/engine/varSubstep.f90 | 34 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 03a6bf7d7..68e90316a 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -1012,15 +1012,15 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues - write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 - write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 - write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt - write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt - write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt - write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - write(*,'(a,1x,f20.10)') 'liqError = ', liqError + !write(*,'(a,1x,f20.10)') 'dt = ', dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + !write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 + !write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 + !write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt + !write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt + !write(*,'(a,1x,f20.10)') 'liqError = ', liqError waterBalanceError = .true. return endif ! if there is a water balance error @@ -1036,14 +1036,14 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues - write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 - write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 - write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux - write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink - write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink - write(*,'(a,1x,f20.10)') 'compSink = ', compSink - write(*,'(a,1x,f20.10)') 'liqError = ', liqError + !write(*,'(a,1x,f20.10)') 'dt = ', dt + !write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 + !write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 + !write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux + !write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink + !write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink + !write(*,'(a,1x,f20.10)') 'compSink = ', compSink + !write(*,'(a,1x,f20.10)') 'liqError = ', liqError waterBalanceError = .true. return endif ! if there is a water balance error From b82919cc2b2705b7b5490631e7e5756aeb564bb8 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 12 Apr 2023 14:21:43 +0900 Subject: [PATCH 0680/1472] turn off more printing and add comments --- build/source/engine/coupled_em.f90 | 12 ++++++------ build/source/engine/summaSolveSundialsIDA.f90 | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 2a9db1829..1798b3ba1 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1166,10 +1166,10 @@ subroutine coupled_em(& scalarTotalSoilLiq => diag_data%var(iLookDIAG%scalarTotalSoilLiq)%dat(1) & ! total liquid water in the soil column (kg m-2) ) ! (association of local variables with information in the data structures - ! identify the need to check the mass balance + ! identify the need to check the mass balance, both methods should work if tolerance coarse enough select case(ixNumericalMethod) - case(sundials); checkMassBalance = .true. ! currently only for bEuler because of how store variables - case(bEuler); checkMassBalance = .true. + case(sundials); checkMassBalance = .true. ! sundials gives instantaneous fluxes and were summed for an average flux for checks + case(bEuler); checkMassBalance = .true. ! bEuler gives finite difference dt_sub fluxes and were summed for an average flux for checks case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return end select @@ -1186,6 +1186,7 @@ subroutine coupled_em(& ! NOTE: need to put the balance checks in the sub-step loop so that we can re-compute if necessary scalarCanopyWatBalError = balanceCanopyWater1 - (balanceCanopyWater0 + (scalarSnowfall - averageThroughfallSnow)*data_step + (scalarRainfall - averageThroughfallRain)*data_step & - averageCanopySnowUnloading*data_step - averageCanopyLiqDrainage*data_step + averageCanopySublimation*data_step + averageCanopyEvaporation*data_step) + if(abs(scalarCanopyWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then write(*,'(a,1x,f20.10)') 'data_step = ', data_step write(*,'(a,1x,f20.10)') 'balanceCanopyWater0 = ', balanceCanopyWater0 write(*,'(a,1x,f20.10)') 'balanceCanopyWater1 = ', balanceCanopyWater1 @@ -1198,7 +1199,6 @@ subroutine coupled_em(& write(*,'(a,1x,f20.10)') 'averageCanopySublimation = ', averageCanopySublimation!*data_step write(*,'(a,1x,f20.10)') 'averageCanopyEvaporation = ', averageCanopyEvaporation!*data_step write(*,'(a,1x,f20.10)') 'scalarCanopyWatBalError = ', scalarCanopyWatBalError - if(abs(scalarCanopyWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then message=trim(message)//'canopy hydrology does not balance' err=20; return end if @@ -1230,6 +1230,7 @@ subroutine coupled_em(& newSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) delSWE = newSWE - (oldSWE - sfcMeltPond) massBalance = delSWE - (effSnowfall + effRainfall + averageSnowSublimation - averageSnowDrainage*iden_water)*data_step + if(abs(massBalance) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then print*, 'nSnow = ', nSnow print*, 'nSub = ', nSub write(*,'(a,1x,f20.10)') 'data_step = ', data_step @@ -1242,7 +1243,6 @@ subroutine coupled_em(& write(*,'(a,1x,f20.10)') 'snwDrainage = ', averageSnowDrainage*iden_water*data_step write(*,'(a,1x,f20.10)') 'sfcMeltPond = ', sfcMeltPond write(*,'(a,1x,f20.10)') 'massBalance = ', massBalance - if(abs(massBalance) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then message=trim(message)//'SWE does not balance' err=20; return endif ! if failed mass balance check @@ -1285,6 +1285,7 @@ subroutine coupled_em(& ! check the soil water balance scalarSoilWatBalError = balanceSoilWater1 - (balanceSoilWater0 + (balanceSoilInflux + balanceSoilET - balanceSoilBaseflow - balanceSoilDrainage - balanceSoilCompress) ) + if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues write(*,*) 'solution method = ', ixSolution write(*,'(a,1x,f20.10)') 'data_step = ', data_step write(*,'(a,1x,f20.10)') 'balanceSoilCompress = ', balanceSoilCompress @@ -1297,7 +1298,6 @@ subroutine coupled_em(& write(*,'(a,1x,f20.10)') 'balanceSoilDrainage = ', balanceSoilDrainage write(*,'(a,1x,f20.10)') 'balanceSoilET = ', balanceSoilET write(*,'(a,1x,f20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError - if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues message=trim(message)//'soil hydrology does not balance' err=20; return end if diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index b09262d0d..8cf907056 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -552,7 +552,7 @@ subroutine summaSolveSundialsIDA( & rVec, & ! intent(out): residual vector eqns_data%err,eqns_data%message) ! intent(out): error control - ! sum of fluxes smoothed over the time step + ! sum of fluxes smoothed over the time step, average from instantaneous values do iVar=1,size(flux_meta) flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + ( eqns_data%flux_data%var(iVar)%dat(:) & + flux_prev%var(iVar)%dat(:) ) *dt_diff/2._rkind From ff8f8129d7083fd6d71d22a6321faae9d0af670e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 12 Apr 2023 16:33:59 +0900 Subject: [PATCH 0681/1472] applying checkMassBalance fixes --- build/source/engine/coupled_em.f90 | 10 +++-- build/source/engine/eval8summaSundials.f90 | 30 ++++++------- build/source/engine/summaSolveSundialsIDA.f90 | 44 +++++++++++++------ build/source/engine/systemSolvSundials.f90 | 7 ++- build/source/engine/type4IDA.f90 | 9 ++-- build/source/engine/varSubstep.f90 | 9 ++-- 6 files changed, 66 insertions(+), 43 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 8290f3bb2..0a1dd7e78 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1164,10 +1164,16 @@ subroutine coupled_em(& scalarTotalSoilLiq => diag_data%var(iLookDIAG%scalarTotalSoilLiq)%dat(1) & ! total liquid water in the soil column (kg m-2) ) ! (association of local variables with information in the data structures + ! identify the need to check the mass balance, both methods should work if tolerance coarse enough + select case(ixNumericalMethod) + case(sundials); checkMassBalance = .true. ! sundials gives instantaneous fluxes and were summed for an average flux for checks + case(bEuler); checkMassBalance = .true. ! bEuler gives finite difference dt_sub fluxes and were summed for an average flux for checks + case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return + end select + ! ----- ! * balance checks for the canopy... ! ---------------------------------- - checkMassBalance = .true. ! if computing the vegetation flux if(computeVegFlux)then @@ -1179,7 +1185,6 @@ subroutine coupled_em(& scalarCanopyWatBalError = balanceCanopyWater1 - (balanceCanopyWater0 + (scalarSnowfall - averageThroughfallSnow)*data_step + (scalarRainfall - averageThroughfallRain)*data_step & - averageCanopySnowUnloading*data_step - averageCanopyLiqDrainage*data_step + averageCanopySublimation*data_step + averageCanopyEvaporation*data_step) if(abs(scalarCanopyWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then - print*, '** canopy water balance error:' write(*,'(a,1x,f20.10)') 'data_step = ', data_step write(*,'(a,1x,f20.10)') 'balanceCanopyWater0 = ', balanceCanopyWater0 write(*,'(a,1x,f20.10)') 'balanceCanopyWater1 = ', balanceCanopyWater1 @@ -1291,7 +1296,6 @@ subroutine coupled_em(& write(*,'(a,1x,f20.10)') 'balanceSoilDrainage = ', balanceSoilDrainage write(*,'(a,1x,f20.10)') 'balanceSoilET = ', balanceSoilET write(*,'(a,1x,f20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError - ! error control message=trim(message)//'soil hydrology does not balance' err=20; return end if diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index 796fdc13a..8f8038e74 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -79,7 +79,7 @@ module eval8summaSundials_module ! ********************************************************************************************************** subroutine eval8summaSundials(& ! input: model control - dt_cur, & ! intent(in): current stepsize + dt_cur, & ! intent(in): step size to last time solution dt, & ! intent(in): entire time step for drainage pond rate nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -133,6 +133,7 @@ subroutine eval8summaSundials(& scalarAquiferStoragePrev,& ! intent(in): value of storage of water in the aquifer (m) mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) + mLayerMatricHeadPrime, & ! intent(out): trial value for prime total water matric potential (m s-1) ! input-output: baseflow ixSaturation, & ! intent(inout): index of the lowest saturated layer dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) @@ -159,7 +160,7 @@ subroutine eval8summaSundials(& ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control - real(rkind),intent(in) :: dt_cur ! current stepsize + real(rkind),intent(in) :: dt_cur ! step size to last time solution real(rkind),intent(in) :: dt ! entire time step for drainage pond rate integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers @@ -213,6 +214,7 @@ subroutine eval8summaSundials(& real(rkind),intent(in) :: scalarAquiferStoragePrev ! previous value of storage of water in the aquifer (m) real(rkind),intent(in) :: mLayerEnthalpyPrev(:) ! previous vector of enthalpy for snow+soil layers (J m-3) real(rkind),intent(out) :: mLayerEnthalpyTrial(:) ! trial vector of enthalpy for snow+soil layers (J m-3) + real(rkind),intent(out) :: mLayerMatricHeadPrime(:) ! deriviative value for prime total water matric potential (m s-1) ! input-output: baseflow integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) @@ -237,7 +239,6 @@ subroutine eval8summaSundials(& real(rkind) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) real(rkind),dimension(nLayers) :: mLayerTempPrime ! derivative value for temperature of layers in the snow and soil domains (K) real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! derivative value for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! derivative value for total water matric potential (m) real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! derivative value for liquid water matric potential (m) real(rkind) :: scalarAquiferStoragePrime ! derivative value of storage of water in the aquifer (m) ! derivative of diagnostic variables @@ -397,7 +398,9 @@ subroutine eval8summaSundials(& endif ! initialize to state variable from the last update + scalarCanairTempTrial = realMissing ! should be set to previous values if splits, but for now operator splitting is not hooked up scalarCanopyTempTrial = scalarCanopyTempPrev + scalarCanopyWatTrial = scalarCanopyLiqPrev + scalarCanopyIcePrev scalarCanopyLiqTrial = scalarCanopyLiqPrev scalarCanopyIceTrial = scalarCanopyIcePrev mLayerTempTrial = mLayerTempPrev @@ -567,31 +570,23 @@ subroutine eval8summaSundials(& mLayerTempPrev, & ! intent(in): previous temperature mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil - mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) + mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) ! input: pre-computed derivatives dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - dCanEnthalpy_dTk, & ! intent(in): derivatives in canopy enthalpy w.r.t. temperature - dCanEnthalpy_dWat, & ! intent(in): derivatives in canopy enthalpy w.r.t. water state - dEnthalpy_dTk, & ! intent(in): derivatives in layer enthalpy w.r.t. temperature - dEnthalpy_dWat, & ! intent(in): derivatives in layer enthalpy w.r.t. water state + dCanEnthalpy_dTk, & ! intent(in): derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat, & ! intent(in): derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk, & ! intent(in): derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat, & ! intent(in): derivatives in layer enthalpy w.r.t. water state ! output heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! Below commented out because it seems more correct for Sundials to use the analytical form - ! to conserve energy compute finite difference approximation of (theta_ice)' - !if(dt_cur > 1e-14_rkind) then - ! scalarCanopyIcePrime = ( scalarCanopyIceTrial - scalarCanopyIcePrev ) / dt_cur - ! do concurrent (iLayer=1:nLayers) - ! mLayerVolFracIcePrime(iLayer) = ( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIcePrev(iLayer) ) / dt_cur - ! end do - !endif ! if dt_cur is not too small else if(ixHowHeatCap == closedForm)then call computHeatCapAnalytic(& ! input: control variables @@ -884,7 +879,7 @@ integer(c_int) function eval8summa4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%bvar_data, & ! intent(in): average model variables for the entire basin eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU ! input-output: data structures - eqns_data%indx_data, & ! intent(inou): index data + eqns_data%indx_data, & ! intent(inout): index data eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables @@ -912,6 +907,7 @@ integer(c_int) function eval8summa4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) + eqns_data%mLayerMatricHeadPrime, & ! intent(out): derivative value for total water matric potential (m s-1) ! input-output: baseflow eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 7b826aa9f..8502c21d7 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -221,9 +221,12 @@ subroutine summaSolveSundialsIDA( & integer(i4b),allocatable :: rootsfound(:) ! crossing direction of discontinuities integer(i4b),allocatable :: rootdir(:) ! forced crossing direction of discontinuities logical(lgt) :: tinystep ! if step goes below small size - logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true - logical(lgt),parameter :: detect_events = .true. ! flag to do event detection and restarting, default true - logical(lgt),parameter :: use_fdJac = .false. ! flag to use finite difference Jacobian, default false + type(var_dlength) :: flux_prev ! previous model fluxes for a local HRU + real(rkind),allocatable :: mLayerMatricHeadPrimePrev(:) ! previous derivative value for total water matric potential (m s-1) + real(rkind),allocatable :: dCompress_dPsiPrev(:) ! previous derivative value soil compression + logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true + logical(lgt),parameter :: detect_events = .true. ! flag to do event detection and restarting, default true + logical(lgt),parameter :: use_fdJac = .false. ! flag to use finite difference Jacobian, default false ! ----------------------------------------------------------------------------------------------------- @@ -265,10 +268,13 @@ subroutine summaSolveSundialsIDA( & if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif eqns_data%diag_data = diag_data - ! allocate space for the temporary flux variable structure + ! allocate space for the temporary and previousflux variable structure call allocLocal(flux_meta(:),eqns_data%flux_data,nSnow,nSoil,err,message) if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif eqns_data%flux_data = flux_data + call allocLocal(flux_meta(:),flux_prev,nSnow,nSoil,err,message) + if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif + flux_prev = eqns_data%flux_data ! allocate space for the derivative structure call allocLocal(deriv_meta(:),eqns_data%deriv_data,nSnow,nSoil,err,message) @@ -302,6 +308,9 @@ subroutine summaSolveSundialsIDA( & allocate( eqns_data%mLayerVolFracLiqPrev(nLayers) ) allocate( eqns_data%mLayerEnthalpyTrial(nLayers) ) allocate( eqns_data%mLayerEnthalpyPrev(nLayers) ) + allocate( eqns_data%mLayerMatricHeadPrime(nSoil) ) + allocate( mLayerMatricHeadPrimePrev(nSoil) ) + allocate( dCompress_dPsiPrev(nSoil) ) allocate( eqns_data%fluxVec(nState) ) allocate( eqns_data%resSink(nState) ) @@ -431,8 +440,9 @@ subroutine summaSolveSundialsIDA( & eqns_data%mLayerVolFracLiqPrev(:) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(:) eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) eqns_data%scalarAquiferStoragePrev = prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) + mLayerMatricHeadPrimePrev(:) = 0._rkind + dCompress_dPsiPrev(:) = 0._rkind eqns_data%ixSaturation = ixSaturation - tinystep = .false. !********************************************************************************** @@ -441,6 +451,7 @@ subroutine summaSolveSundialsIDA( & !********************************************************************************** tret(1) = t0 ! initial time + tretPrev = tret(1) do while(tret(1) < dt) ! call this at beginning of step to reduce root bouncing (only looking in one direction) @@ -473,7 +484,7 @@ subroutine summaSolveSundialsIDA( & ! compute the flux and the residual vector for a given state vector call eval8summaSundials(& ! input: model control - dt_last(1), & ! intent(in): current stepsize + dt_diff, & ! intent(in): step size to last time solution eqns_data%dt, & ! intent(in): total data step eqns_data%nSnow, & ! intent(in): number of snow layers eqns_data%nSoil, & ! intent(in): number of soil layers @@ -503,7 +514,7 @@ subroutine summaSolveSundialsIDA( & eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: here we need to pass some extra variables that do not get updated in in the Sundials loops + ! input-output: here we need to pass some extra variables that do not get updated in in the Sundials loops (without operator splitting could cut prev values) eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) @@ -527,6 +538,7 @@ subroutine summaSolveSundialsIDA( & eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) + eqns_data%mLayerMatricHeadPrime, & ! intent(out): derivative value for total water matric potential (m s-1) ! input-output: baseflow eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) @@ -537,14 +549,13 @@ subroutine summaSolveSundialsIDA( & rVec, & ! intent(out): residual vector eqns_data%err,eqns_data%message) ! intent(out): error control - ! sum of fluxes + ! sum of fluxes smoothed over the time step, average from instantaneous values do iVar=1,size(flux_meta) - flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + eqns_data%flux_data%var(iVar)%dat(:) * dt_last(1) + flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + ( eqns_data%flux_data%var(iVar)%dat(:) & + + flux_prev%var(iVar)%dat(:) ) *dt_diff/2._rkind end do - - ! sum of mLayerCmpress over the time step, since using prev value not * dt_last(1) - mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) & - * ( eqns_data%mLayerMatricHeadTrial(:) - eqns_data%mLayerMatricHeadPrev(:) ) + mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + ( eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) * eqns_data%mLayerMatricHeadPrime(:) & + + dCompress_dPsiPrev(:) * mLayerMatricHeadPrimePrev(:) ) * dt_diff/2._rkind ! save required quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial @@ -558,6 +569,10 @@ subroutine summaSolveSundialsIDA( & eqns_data%mLayerVolFracLiqPrev(:) = eqns_data%mLayerVolFracLiqTrial(:) eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial + mLayerMatricHeadPrimePrev(:) = eqns_data%mLayerMatricHeadPrime(:) + dCompress_dPsiPrev(:) = eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) + tretPrev = tret(1) + flux_prev = eqns_data%flux_data ! Restart for where vegetation and layers cross freezing point if(detect_events)then @@ -613,6 +628,9 @@ subroutine summaSolveSundialsIDA( & deallocate( eqns_data%mLayerVolFracLiqPrev ) deallocate( eqns_data%mLayerEnthalpyTrial ) deallocate( eqns_data%mLayerEnthalpyPrev ) + deallocate( eqns_data%mLayerMatricHeadPrime) + deallocate( mLayerMatricHeadPrimePrev ) + deallocate( dCompress_dPsiPrev ) deallocate( eqns_data%fluxVec ) deallocate( eqns_data%resSink ) deallocate( rootsfound ) diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index c11965597..0b5f3f6d8 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -211,6 +211,7 @@ subroutine systemSolvSundials(& real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt_out (=dt) real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix + real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! derivative value for total water matric potential (m s-1) logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step real(rkind) :: fOld ! function values (-); NOTE: dimensionless because scaled ! enthalpy derivatives @@ -308,8 +309,9 @@ subroutine systemSolvSundials(& call allocLocal(flux_meta(:),flux_init,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - ! allocate space for mLayerCmpress_sum at the start of the time step + ! allocate space for mLayerCmpress_sum and mLayerMatricHeadPrime at the start of the time step allocate( mLayerCmpress_sum(nSoil) ) + allocate( mLayerMatricHeadPrime(nSoil) ) ! allocate space for the baseflow derivatives ! NOTE: needs allocation because only used when baseflow sinks are active @@ -459,6 +461,7 @@ subroutine systemSolvSundials(& scalarAquiferStorage, & ! intent(in): value of storage of water in the aquifer (m) mLayerEnthalpy, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) mLayerEnthalpy, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) + mLayerMatricHeadPrime, & ! intent(out): derivative value for total water matric potential (m s-1) ! input-output: baseflow ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) @@ -581,6 +584,8 @@ subroutine systemSolvSundials(& stateVecTrial = stateVecNew ! free memory + deallocate( mLayerCmpress_sum) + deallocate( mLayerMatricHeadPrime) deallocate(dBaseflow_dMatric) ! end associate statements diff --git a/build/source/engine/type4IDA.f90 b/build/source/engine/type4IDA.f90 index 334b808eb..14cf5e10d 100644 --- a/build/source/engine/type4IDA.f90 +++ b/build/source/engine/type4IDA.f90 @@ -52,6 +52,8 @@ module type4IDA real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation real(rkind), allocatable :: atol(:) ! vector of absolute tolerances real(rkind), allocatable :: rtol(:) ! vector of relative tolerances + real(rkind), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind), allocatable :: mLayerTempPrev(:) ! vector of layer temperature (K) at previous step real(rkind), allocatable :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) real(rkind), allocatable :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) real(rkind), allocatable :: mLayerMatricHeadPrev(:) ! vector of total water matric potential (m) at previous step @@ -61,12 +63,11 @@ module type4IDA real(rkind), allocatable :: mLayerVolFracIcePrev(:) ! value for volumetric fraction of ice (-) at previous step real(rkind), allocatable :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) real(rkind), allocatable :: mLayerVolFracLiqPrev(:) ! value for volumetric fraction of liquid water (-) at previous step - real(rkind) :: scalarAquiferStoragePrev - real(rkind) :: scalarAquiferStorageTrial + real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + real(rkind) :: scalarAquiferStoragePrev ! value of storage of water in the aquifer (m) at previous step real(rkind), allocatable :: mLayerEnthalpyTrial(:) ! trial enthalpy of snow and soil (J m-3) real(rkind), allocatable :: mLayerEnthalpyPrev(:) ! enthalpy of snow and soil (J m-3) at previous step - real(rkind), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind), allocatable :: mLayerTempPrev(:) ! vector of layer temperature (K) at previous step + real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! derivative value for total water matric potential (m s-1) real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) integer(i4b) :: ixSaturation integer(i4b) :: err ! error code diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 7cc7b5bb0..54d53e972 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -327,7 +327,7 @@ subroutine varSubstep(& call systemSolvSundials(& ! input: model control dtSubstep, & ! intent(in): time step (s) - whole_step, & ! intent(in): entire time step (s), right now same as dtSubstep but might change with operator splitting + whole_step, & ! intent(in): entire time step (s), right now same as dtSubstep but might change with operator splitting nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to denote first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call @@ -455,7 +455,7 @@ subroutine varSubstep(& ! identify the need to check the mass balance, only for bEuler because of how store variables select case(ixNumericalMethod) - case(sundials); checkMassBalance = .false. + case(sundials); checkMassBalance = .false. ! only for bEuler because sundials has instantaneous fluxes only case(bEuler); checkMassBalance = .true. ! (.not.scalarSolution) case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return end select @@ -962,9 +962,8 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! * check mass balance... ! ----------------------- - ! NOTE: should not need to do this, since mass balance is checked in the solver - ! for sundials will not work since fluxes are averaged over data window for output but canopyBalance0 only off last step - ! so not currently checked in sundials, could cause problems if should modify nrgFlux + ! NOTE: should not need to do this, since mass balance is checked in the solver, and cannot do for Sundials + ! if do not check could cause problems if should modify nrgFlux if(checkMassBalance)then ! check mass balance for the canopy From ce5f55d771d2f87a7c92f18d0f13e604e8a1cdbb Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 13 Apr 2023 11:34:27 +0900 Subject: [PATCH 0682/1472] sleep at end to prevent exiting before HDF5 has closed in batch runs --- build/source/driver/summa_driver.f90 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 6e84a7733..db67ae4cc 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -103,4 +103,8 @@ program summa_driver ! successful end call stop_program(0, 'finished simulation successfully.') + + ! to prevent exiting before HDF5 has closed + call sleep(2) + end program summa_driver From 2d556b0256c83c960179f1b6286e31257ba0671b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 13 Apr 2023 11:35:02 +0900 Subject: [PATCH 0683/1472] sleep at end to prevent exiting before HDF5 has closed in batch runs --- build/source/driver/summa_driver.f90 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 0f171902f..b818cc624 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -286,6 +286,8 @@ function summa_finalize(this) result (bmi_status) integer :: bmi_status call stop_program(0, 'finished simulation successfully.') + ! to prevent exiting before HDF5 has closed + call sleep(2) bmi_status = BMI_SUCCESS end function summa_finalize From 12954961d17826ea34ee8bf6dff01c48d36f4f06 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 13 Apr 2023 11:38:37 +0900 Subject: [PATCH 0684/1472] Revert "sleep at end to prevent exiting before HDF5 has closed in batch runs" This reverts commit 357e3744b837f1a3d2af8319aedd58693519292f. --- build/source/driver/summa_driver.f90 | 2 -- 1 file changed, 2 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index b818cc624..0f171902f 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -286,8 +286,6 @@ function summa_finalize(this) result (bmi_status) integer :: bmi_status call stop_program(0, 'finished simulation successfully.') - ! to prevent exiting before HDF5 has closed - call sleep(2) bmi_status = BMI_SUCCESS end function summa_finalize From e017583526cf30f43d57b6e718c84d7e96bc5beb Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 13 Apr 2023 11:42:00 +0900 Subject: [PATCH 0685/1472] to prevent exiting before HDF5 has closed --- build/source/driver/summa_driver.f90 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 0f171902f..b818cc624 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -286,6 +286,8 @@ function summa_finalize(this) result (bmi_status) integer :: bmi_status call stop_program(0, 'finished simulation successfully.') + ! to prevent exiting before HDF5 has closed + call sleep(2) bmi_status = BMI_SUCCESS end function summa_finalize From 36dff38d76b7d16cbaf95d2ccbc8f6220156f22d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 17 Apr 2023 10:51:12 +0900 Subject: [PATCH 0686/1472] raise buffer on rootfinding so things complete --- build/source/engine/summaSolveSundialsIDA.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 8cf907056..030f3c7fe 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -587,7 +587,7 @@ subroutine summaSolveSundialsIDA( & ! Reininitialize solver for running after discontinuity and restart retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAReInit'; return; endif - if(dt_last(1) < 0.01_rkind)then ! don't keep calling if step is small + if(dt_last(1) < 0.1_rkind)then ! don't keep calling if step is small (more accurate with this tiny but getting hung up) retval = FIDARootInit(ida_mem, 0, c_funloc(layerDisCont4IDA)) tinystep = .true. else From 1e1a6c08a5ddd6dcfc3091a68ea6901d91197764 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 17 Apr 2023 15:13:19 +0900 Subject: [PATCH 0687/1472] file had dt_diff wrong --- build/source/engine/summaSolveSundialsIDA.f90 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 8502c21d7..030f3c7fe 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -212,12 +212,14 @@ subroutine summaSolveSundialsIDA( & logical(lgt) :: feasible ! feasibility flag real(qp) :: t0 ! staring time real(qp) :: dt_last(1) ! last time step + real(qp) :: dt_diff ! difference from previous timestep integer(kind = 8) :: mu, lu ! in banded matrix mode in Sundials type integer(c_long) :: nState ! total number of state variables in Sundials type real(rkind) :: rVec(nStat) ! residual vector integer(i4b) :: iVar, i ! indices integer(i4b) :: nRoot ! total number of roots (events) to find real(qp) :: tret(1) ! time in data window + real(qp) :: tretPrev ! previous time in data window integer(i4b),allocatable :: rootsfound(:) ! crossing direction of discontinuities integer(i4b),allocatable :: rootdir(:) ! forced crossing direction of discontinuities logical(lgt) :: tinystep ! if step goes below small size @@ -478,8 +480,9 @@ subroutine summaSolveSundialsIDA( & enddo if(tooMuchMelt)exit - ! get the last stepsize + ! get the last stepsize and difference from previous end time, not necessarily the same retval = FIDAGetLastStep(ida_mem, dt_last) + dt_diff = tret(1) - tretPrev ! compute the flux and the residual vector for a given state vector call eval8summaSundials(& @@ -584,7 +587,7 @@ subroutine summaSolveSundialsIDA( & ! Reininitialize solver for running after discontinuity and restart retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAReInit'; return; endif - if(dt_last(1) < 0.01_rkind)then ! don't keep calling if step is small + if(dt_last(1) < 0.1_rkind)then ! don't keep calling if step is small (more accurate with this tiny but getting hung up) retval = FIDARootInit(ida_mem, 0, c_funloc(layerDisCont4IDA)) tinystep = .true. else From 33e46eb38b56347b2c2ba4fe26a40d2d0dc7677f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 17 Apr 2023 15:17:20 +0900 Subject: [PATCH 0688/1472] more errors in merge --- build/source/engine/coupled_em.f90 | 6 ++++-- build/source/engine/varSubstep.f90 | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 0a1dd7e78..1798b3ba1 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1025,7 +1025,7 @@ subroutine coupled_em(& flux_inner%var(iVar)%dat(:) = flux_inner%var(iVar)%dat(:) + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:)*dt_wght end do innerSoilCompress = innerSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1)*dt_wght - if (nSnow>0)innerEffRainfall = innerEffRainfall + ( flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) + flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) )*dt_wght + if (nSnow>0) innerEffRainfall = innerEffRainfall + ( flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) + flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) )*dt_wght ! increment sub-step accepted step dt_solvInner = dt_solvInner + dt_sub @@ -1131,6 +1131,8 @@ subroutine coupled_em(& ! associate local variables with information in the data structures associate(& + ! model decisions + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! choice of numerical method, backward Euler or SUNDIALS/IDA ! model forcing scalarSnowfall => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSnowfall) )%dat(1) ,& ! computed snowfall rate (kg m-2 s-1) scalarRainfall => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarRainfall) )%dat(1) ,& ! computed rainfall rate (kg m-2 s-1) @@ -1224,7 +1226,7 @@ subroutine coupled_em(& ! check SWE if(nSnow>0)then effSnowfall = averageThroughfallSnow + averageCanopySnowUnloading - !effRainfall is averageThroughfallRain + averageCanopyLiqDrainage only over snow + ! effRainfall is averageThroughfallRain + averageCanopyLiqDrainage only over snow newSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) delSWE = newSWE - (oldSWE - sfcMeltPond) massBalance = delSWE - (effSnowfall + effRainfall + averageSnowSublimation - averageSnowDrainage*iden_water)*data_step diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 54d53e972..68e90316a 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -114,7 +114,7 @@ subroutine varSubstep(& indx_data, & ! intent(inout) : index data prog_data, & ! intent(inout) : model prognostic variables for a local HRU diag_data, & ! intent(inout) : model diagnostic variables for a local HRU - flux_data, & ! intent(inout) : model fluxes for a local HRU + flux_data, & ! intent(inout) : model fluxes for a local HRU flux_mean, & ! intent(inout) : mean model fluxes for a local HRU deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables bvar_data, & ! intent(in) : model variables for the local basin @@ -453,7 +453,7 @@ subroutine varSubstep(& return endif - ! identify the need to check the mass balance, only for bEuler because of how store variables + ! identify the need to check the mass balance select case(ixNumericalMethod) case(sundials); checkMassBalance = .false. ! only for bEuler because sundials has instantaneous fluxes only case(bEuler); checkMassBalance = .true. ! (.not.scalarSolution) From f1366f81e606b14b95cc338491da766b8c473cd3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 18 Apr 2023 20:01:35 +0900 Subject: [PATCH 0689/1472] cmake works on mac now --- build/cmake_ngen/CMakeLists.txt | 26 ++++++++++++------------ build/cmake_ngen/build_ngen.cluster.bash | 2 +- build/cmake_ngen/build_ngen.mac.bash | 4 ++-- build/makefiles/compile_copernicus | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/build/cmake_ngen/CMakeLists.txt b/build/cmake_ngen/CMakeLists.txt index 3b23a3df6..4e360e015 100644 --- a/build/cmake_ngen/CMakeLists.txt +++ b/build/cmake_ngen/CMakeLists.txt @@ -52,22 +52,18 @@ else() set(FLAGS_ALL -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) endif() +set(DIR_SUNDIALS ${ROOT_DIR}/sundials/instdir) -# MKL needs BLA_VENDOR set with cmake if(CMAKE_BUILD_TYPE MATCHES Cluster OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) - set(BLA_VENDOR OpenBLAS) -else() - set(BLA_VENDOR OpenBLAS) -endif() -# Packages that are required -find_package(netCDF REQUIRED) -find_package(LAPACK REQUIRED) + # MKL needs BLA_VENDOR set with cmake + set(BLA_VENDOR OpenBLAS) -set(DIR_SUNDIALS ${ROOT_DIR}/sundials/instdir) + # Packages that are required + find_package(NetCDF REQUIRED) + find_package(LAPACK REQUIRED) -# Set include directories -if(CMAKE_BUILD_TYPE MATCHES Cluster OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) + # Set include directories set(INCLUDES $ENV{EBROOTNETCDFMINFORTRAN}/include ${netCDF_INCLUDES} ${LAPACK_INCLUDES}) set(LIBRARIES SUMMA_NOAHMP ${netCDF_LIBRARIES} ${LAPACK_LIBRARIES} -lnetcdff -lopenblas) @@ -79,8 +75,12 @@ if(CMAKE_BUILD_TYPE MATCHES Cluster OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) set(LIB_SUNDIALS -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) else() - set(INCLUDES /opt/local/include ${netCDF_INCLUDES} ${LAPACK_INCLUDES}) - set(LIBRARIES SUMMA_NOAHMP ${netCDF_LIBRARIES} ${LAPACK_LIBRARIES} -llapack -lgfortran -lnetcdff -lnetcdf) + + set(SDKROOT "$(xcrun --show-sdk-path)") + + link_directories(/opt/local/lib) + set(INCLUDES /opt/local/include /opt/local/lib) + set(LIBRARIES SUMMA_NOAHMP -llapack -lgfortran -lnetcdff -lnetcdf) message("\nBuilding IDA\n") diff --git a/build/cmake_ngen/build_ngen.cluster.bash b/build/cmake_ngen/build_ngen.cluster.bash index 1c0b24b54..038a2bffc 100644 --- a/build/cmake_ngen/build_ngen.cluster.bash +++ b/build/cmake_ngen/build_ngen.cluster.bash @@ -31,7 +31,7 @@ cmake --build extern/snow17/cmake_build --target all cmake -B extern/summa/cmake_build -S extern/summa cmake --build extern/summa/cmake_build --target all -cmake -B /ngen/cmake_build -S . -DMPI_ACTIVE:BOOL=OFF -DMPI_ACTIVE:BOOL=ON -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON +cmake -B cmake_build -S . -DMPI_ACTIVE:BOOL=OFF -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON # can also add -DCMAKE_BUILD_TYPE=Debug to be able to run in gdb # make -j 8 -C cmake_build # build w/ 8 parallel jobs, also turn MPI on make -C cmake_build diff --git a/build/cmake_ngen/build_ngen.mac.bash b/build/cmake_ngen/build_ngen.mac.bash index 70c313fe2..3e7636d11 100644 --- a/build/cmake_ngen/build_ngen.mac.bash +++ b/build/cmake_ngen/build_ngen.mac.bash @@ -8,10 +8,10 @@ cmake --build extern/iso_c_fortran_bmi/cmake_build --target all cmake -B extern/noah-owp-modular/cmake_build -S extern/noah-owp-modular cmake --build extern/noah-owp-modular/cmake_build --target all -cmake -B extern/summa/cmake_build -S extern/summa +cmake -B extern/summa/cmake_build -S extern/summa -DCMAKE_BUILD_TYPE=Release cmake --build extern/summa/cmake_build --target all -cmake -B /ngen/cmake_build -S . -DMPI_ACTIVE:BOOL=OFF -DMPI_ACTIVE:BOOL=ON -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON +cmake -B cmake_build -S . -DMPI_ACTIVE:BOOL=OFF -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON # can also add -DCMAKE_BUILD_TYPE=Debug to be able to run in gdb # make -j 8 -C cmake_build # build w/ 8 parallel jobs, also turn MPI on make -C cmake_build diff --git a/build/makefiles/compile_copernicus b/build/makefiles/compile_copernicus index 5f15709af..f818b6ced 100755 --- a/build/makefiles/compile_copernicus +++ b/build/makefiles/compile_copernicus @@ -9,7 +9,7 @@ module load netcdf-fortran/4.5.2 #### parent directory of the 'build' directory #### export ROOT_DIR=/project/gwf/gwf_cmt/ngen/ngen_code/ngen/extern/summa -export INCLUDES=-"I${EBROOTNETCDFMINFORTRAN}/include" +export INCLUDES="-I${EBROOTNETCDFMINFORTRAN}/include" export LDFLAGS="-L${EBROOTNETCDFMINFORTRAN}/lib64 -L${EBROOTOPENBLAS}/lib" export LIBRARIES="${LDFLAGS} -lnetcdff -lopenblas" From a5aa7e0cce6ac9f64be5e57bd663524160929b82 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 18 Apr 2023 20:02:13 +0900 Subject: [PATCH 0690/1472] makefile syntax --- build/makefiles/compile_copernicus | 2 +- build/makefiles/compile_graham | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/makefiles/compile_copernicus b/build/makefiles/compile_copernicus index d936d10fe..a54c13f40 100755 --- a/build/makefiles/compile_copernicus +++ b/build/makefiles/compile_copernicus @@ -9,7 +9,7 @@ module load netcdf-fortran/4.5.2 #### parent directory of the 'build' directory #### export ROOT_DIR=/globalhome/gwu479/HPC/SummaSundials -export INCLUDES=-"I${EBROOTNETCDFMINFORTRAN}/include" +export INCLUDES="-I${EBROOTNETCDFMINFORTRAN}/include" export LDFLAGS="-L${EBROOTNETCDFMINFORTRAN}/lib64 -L${EBROOTOPENBLAS}/lib" export LIBRARIES="${LDFLAGS} -lnetcdff -lopenblas" diff --git a/build/makefiles/compile_graham b/build/makefiles/compile_graham index d5fc9b7c0..545353682 100755 --- a/build/makefiles/compile_graham +++ b/build/makefiles/compile_graham @@ -9,7 +9,7 @@ module load netcdf-fortran/4.5.2 #### parent directory of the 'build' directory #### export ROOT_DIR=/home/avanb/SummaSundials -export INCLUDES=-"I${EBROOTNETCDFMINFORTRAN}/include" +export INCLUDES="-I${EBROOTNETCDFMINFORTRAN}/include" export LDFLAGS="-L${EBROOTNETCDFMINFORTRAN}/lib64 -L${EBROOTOPENBLAS}/lib" export LIBRARIES="${LDFLAGS} -lnetcdff -lopenblas" From 9fcc71426e8d8974423b8ea35f96ac24ae580d36 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 19 Apr 2023 12:58:22 +0900 Subject: [PATCH 0691/1472] Adding compiler directives so that we only need to download the sundials libraries and compile them if -DSUNDIALS_ACTIVE:BOOL=ON in the build. This only changes varSubstep with a compiler directive-- so if we update that file in the master, we will need to merge the changes here. --- build/cmake_ngen/CMakeLists.txt | 69 +++++++++++++++--------- build/cmake_ngen/README.md | 4 +- build/cmake_ngen/build_ngen.cluster.bash | 2 +- build/cmake_ngen/build_ngen.mac.bash | 2 +- build/source/engine/varSubstep.f90 | 6 +++ 5 files changed, 54 insertions(+), 29 deletions(-) diff --git a/build/cmake_ngen/CMakeLists.txt b/build/cmake_ngen/CMakeLists.txt index 4e360e015..e1e1fa6b7 100644 --- a/build/cmake_ngen/CMakeLists.txt +++ b/build/cmake_ngen/CMakeLists.txt @@ -153,10 +153,10 @@ message("\nBuilding SUMMA\n") ${ENGINE_DIR}/mDecisions.f90 ${ENGINE_DIR}/snow_utils.f90 ${ENGINE_DIR}/soil_utils.f90 - ${ENGINE_DIR}/soil_utilsAddSundials.f90 ${ENGINE_DIR}/updatState.f90 - ${ENGINE_DIR}/updatStateSundials.f90 - ${ENGINE_DIR}/matrixOper.f90) + ${ENGINE_DIR}/matrixOper.f90 + ${ENGINE_DIR}/soil_utilsAddSundials.f90 + ${ENGINE_DIR}/updatStateSundials.f90) # Solver set(SOLVER @@ -172,8 +172,6 @@ message("\nBuilding SUMMA\n") ${ENGINE_DIR}/soilLiqFlx.f90 ${ENGINE_DIR}/bigAquifer.f90 ${ENGINE_DIR}/computFlux.f90 - ${ENGINE_DIR}/type4IDA.f90 - ${ENGINE_DIR}/tol4IDA.f90 ${ENGINE_DIR}/computEnthalpy.f90 ${ENGINE_DIR}/computHeatCap.f90 ${ENGINE_DIR}/computThermConduct.f90 @@ -182,17 +180,21 @@ message("\nBuilding SUMMA\n") ${ENGINE_DIR}/eval8summa.f90 ${ENGINE_DIR}/summaSolve.f90 ${ENGINE_DIR}/systemSolv.f90 - ${ENGINE_DIR}/computResidSundials.f90 - ${ENGINE_DIR}/eval8summaSundials.f90 - ${ENGINE_DIR}/computJacobSundials.f90 ${ENGINE_DIR}/computSnowDepth.f90 - ${ENGINE_DIR}/summaSolveSundialsIDA.f90 - ${ENGINE_DIR}/systemSolvSundials.f90 ${ENGINE_DIR}/varSubstep.f90 ${ENGINE_DIR}/opSplittin.f90 ${ENGINE_DIR}/coupled_em.f90 ${ENGINE_DIR}/run_oneHRU.f90 ${ENGINE_DIR}/run_oneGRU.f90) + set(SOLVER_SUN + ${ENGINE_DIR}/type4IDA.f90 + ${ENGINE_DIR}/tol4IDA.f90 + ${ENGINE_DIR}/computResidSundials.f90 + ${ENGINE_DIR}/eval8summaSundials.f90 + ${ENGINE_DIR}/computJacobSundials.f90 + ${ENGINE_DIR}/summaSolveSundialsIDA.f90 + ${ENGINE_DIR}/systemSolvSundials.f90) + # Define routines for SUMMA preliminaries set(PRELIM @@ -222,7 +224,6 @@ message("\nBuilding SUMMA\n") ${ENGINE_DIR}/getVectorz.f90 ${ENGINE_DIR}/t2enthalpy.f90 ${ENGINE_DIR}/updateVars.f90 - ${ENGINE_DIR}/updateVarsSundials.f90 ${ENGINE_DIR}/var_derive.f90 ${ENGINE_DIR}/read_force.f90 ${ENGINE_DIR}/derivforce.f90 @@ -233,7 +234,9 @@ message("\nBuilding SUMMA\n") ${ENGINE_DIR}/layerMerge.f90 ${ENGINE_DIR}/layerDivide.f90 ${ENGINE_DIR}/volicePack.f90 - ${ENGINE_DIR}/qTimeDelay.f90) + ${ENGINE_DIR}/qTimeDelay.f90 + ${ENGINE_DIR}/updateVarsSundials.f90) + # Define NetCDF routines set(NETCDF @@ -274,12 +277,23 @@ message("\nBuilding SUMMA\n") ${DATAMS} ${UTILMS}) - set(SUMMA_ALL - ${NETCDF} - ${PRELIM} - ${MODRUN} - ${SOLVER} - ${DRIVER}) + if(SUNDIALS_ACTIVE) + add_compile_definitions(NGEN_SUNDIALS_ACTIVE) + set(SUMMA_ALL + ${NETCDF} + ${PRELIM} + ${MODRUN} + ${SOLVER} + ${DRIVER} + ${SOLVER_SUN}) + else() + set(SUMMA_ALL + ${NETCDF} + ${PRELIM} + ${MODRUN} + ${SOLVER} + ${DRIVER}) + endif() #======================================================================== # PART 4: compilation @@ -292,22 +306,27 @@ message("\nBuilding SUMMA\n") file(APPEND ${VERSIONFILE} "character(len=64), parameter :: gitHash = '${GITHASH}'") add_library(SUMMA_NOAHMP OBJECT ${NOAHMP} ${NRUTIL}) - target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) - + target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) add_library(SUMMA_COMM OBJECT ${COMM_ALL}) - target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) - target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES}) - target_link_libraries(SUMMA_COMM PUBLIC ${LIBRARIES}) + target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) + target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES}) + target_link_libraries(SUMMA_COMM PUBLIC ${LIBRARIES}) - # Build SUMMA Shared Library, add BMI libraries outside this function +# Build SUMMA Shared Library, add BMI libraries outside this function if(WIN32) add_library(summabmi ${SUMMA_ALL}) else() add_library(summabmi SHARED ${SUMMA_ALL}) endif() - target_compile_options(summabmi PRIVATE ${FLAGS_ALL}) + target_compile_options(summabmi PRIVATE ${FLAGS_ALL}) + + if(SUNDIALS_ACTIVE) target_include_directories(summabmi PUBLIC ${INCLUDES} ${INC_SUNDIALS}) target_link_libraries(summabmi PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_COMM) + else() + target_include_directories(summabmi PUBLIC ${INCLUDES}) + target_link_libraries(summabmi PUBLIC ${LIBRARIES} SUMMA_COMM) + endif() #endfunction() diff --git a/build/cmake_ngen/README.md b/build/cmake_ngen/README.md index d16c4c5a5..7f5c6a175 100644 --- a/build/cmake_ngen/README.md +++ b/build/cmake_ngen/README.md @@ -51,7 +51,7 @@ First, cd into the outer directory containing the submodule: cd extern/summa -Before summa can be built, Sundials needs to be installed. +If you want to use Sundials, set -DSUNDIALS_ACTIVE:BOOL=ON in the build script. Then, before summa can be built, Sundials needs to be installed. Download the latest release of IDA solver from SUNDIALS package in https://computing.llnl.gov/projects/sundials/sundials-software wget "https://github.com/LLNL/sundials/releases/download/v6.3.0/sundials-6.3.0.tar.gz" @@ -81,7 +81,7 @@ After there is build system directory, the shared library can be built using the This will build a `cmake_build/libsummabmi..` file, where the version is configured within the CMake config, and the extension depends on the local machine's operating system. -There is an example of a bash script to build the ngen libraries at ngen/extern/summa/summa/build/cmake_ngen/build_ngen.[system_type].bash. These need to be run in the main ngen directory. +There is an example of a bash script to build the ngen libraries at ngen/extern/summa/summa/build/cmake_ngen/build_ngen.[system_type].bash. Sundials is turned off here. These need to be run in the main ngen directory. To run a test basin, in the main ngen directory, run diff --git a/build/cmake_ngen/build_ngen.cluster.bash b/build/cmake_ngen/build_ngen.cluster.bash index 038a2bffc..15df2c864 100644 --- a/build/cmake_ngen/build_ngen.cluster.bash +++ b/build/cmake_ngen/build_ngen.cluster.bash @@ -28,7 +28,7 @@ cmake --build extern/iso_c_fortran_bmi/cmake_build --target all cmake -B extern/snow17/cmake_build -S extern/snow17 cmake --build extern/snow17/cmake_build --target all -cmake -B extern/summa/cmake_build -S extern/summa +cmake -B extern/summa/cmake_build -S extern/summa -DCMAKE_BUILD_TYPE=Cluster -DSUNDIALS_ACTIVE:BOOL=OFF cmake --build extern/summa/cmake_build --target all cmake -B cmake_build -S . -DMPI_ACTIVE:BOOL=OFF -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON diff --git a/build/cmake_ngen/build_ngen.mac.bash b/build/cmake_ngen/build_ngen.mac.bash index 3e7636d11..4098f9a7e 100644 --- a/build/cmake_ngen/build_ngen.mac.bash +++ b/build/cmake_ngen/build_ngen.mac.bash @@ -8,7 +8,7 @@ cmake --build extern/iso_c_fortran_bmi/cmake_build --target all cmake -B extern/noah-owp-modular/cmake_build -S extern/noah-owp-modular cmake --build extern/noah-owp-modular/cmake_build --target all -cmake -B extern/summa/cmake_build -S extern/summa -DCMAKE_BUILD_TYPE=Release +cmake -B extern/summa/cmake_build -S extern/summa -DCMAKE_BUILD_TYPE=Release -DSUNDIALS_ACTIVE:BOOL=OFF cmake --build extern/summa/cmake_build --target all cmake -B cmake_build -S . -DMPI_ACTIVE:BOOL=OFF -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 68e90316a..fb8a17897 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -132,7 +132,9 @@ subroutine varSubstep(& ! simulation of fluxes and residuals given a trial state vector USE getVectorz_module,only:popStateVec ! populate the state vector USE getVectorz_module,only:varExtract ! extract variables from the state vector +#ifdef NGEN_SUNDIALS_ACTIVE USE systemSolvSundials_module,only:systemSolvSundials ! solve the system of equations for one time step +#endif USE systemSolv_module,only:systemSolv ! solve the system of equations for one time step ! identify name of variable type (for error message) USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages @@ -324,6 +326,7 @@ subroutine varSubstep(& ! solve the system of equations for a given state subset select case(ixNumericalMethod) case(sundials) +#ifdef NGEN_SUNDIALS_ACTIVE call systemSolvSundials(& ! input: model control dtSubstep, & ! intent(in): time step (s) @@ -357,6 +360,9 @@ subroutine varSubstep(& err,cmessage) ! intent(out): error code and error message untappedMelt(:) = 0._rkind ! set untapped melt energy to zero niter = 0 ! will not use +#else + err=20; message=trim(message)//'cannot use num_method as sundials if did not compile with -DSUNDIALS_ACTIVE:BOOL=ON'; return +#endif case(bEuler) call systemSolv(& ! input: model control From 2fb03f6b7c4c1f3e086ff78961afb2bbc3e59044 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 19 Apr 2023 13:08:04 +0900 Subject: [PATCH 0692/1472] one more if statement in CMakeLists for Sundials --- build/cmake_ngen/CMakeLists.txt | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/build/cmake_ngen/CMakeLists.txt b/build/cmake_ngen/CMakeLists.txt index e1e1fa6b7..e0f6408bf 100644 --- a/build/cmake_ngen/CMakeLists.txt +++ b/build/cmake_ngen/CMakeLists.txt @@ -67,12 +67,14 @@ if(CMAKE_BUILD_TYPE MATCHES Cluster OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) set(INCLUDES $ENV{EBROOTNETCDFMINFORTRAN}/include ${netCDF_INCLUDES} ${LAPACK_INCLUDES}) set(LIBRARIES SUMMA_NOAHMP ${netCDF_LIBRARIES} ${LAPACK_LIBRARIES} -lnetcdff -lopenblas) - message("\nBuilding IDA\n") + if(SUNDIALS_ACTIVE) + message("\nBuilding IDA\n") - link_directories(${DIR_SUNDIALS}/lib64) - set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib64") - set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran) - set(LIB_SUNDIALS -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) + link_directories(${DIR_SUNDIALS}/lib64) + set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib64") + set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran) + set(LIB_SUNDIALS -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) + endif() else() @@ -82,13 +84,14 @@ else() set(INCLUDES /opt/local/include /opt/local/lib) set(LIBRARIES SUMMA_NOAHMP -llapack -lgfortran -lnetcdff -lnetcdf) - message("\nBuilding IDA\n") - - link_directories(${DIR_SUNDIALS}/lib) - set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib") - set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran) - set(LIB_SUNDIALS -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) + if(SUNDIALS_ACTIVE) + message("\nBuilding IDA\n") + link_directories(${DIR_SUNDIALS}/lib) + set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib") + set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran) + set(LIB_SUNDIALS -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) + endif() endif() From ea9faf52f5ac4a31d71a0e33118487181ea6786c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 19 Apr 2023 14:01:52 +0900 Subject: [PATCH 0693/1472] add aquifer output to bmi --- build/source/driver/summa_driver.f90 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index b818cc624..77a0dd711 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -162,7 +162,7 @@ module summa_driver #else integer, parameter :: input_item_count = 12 #endif - integer, parameter :: output_item_count = 15 + integer, parameter :: output_item_count = 16 character (len=BMI_MAX_VAR_NAME), target,dimension(input_item_count) :: input_items character (len=BMI_MAX_VAR_NAME), target,dimension(output_item_count) :: output_items ! --------------------------------------------------------------------------------------- @@ -377,6 +377,7 @@ function summa_output_var_names(this, names) result (bmi_status) output_items(13)= 'atmosphere_energy~net~total__energy_flux' output_items(14)= 'land_vegetation_energy~net~total__energy_flux' output_items(15)= 'land_surface_energy~net~total__energy_flux' + output_items(16)= 'land_surface_water__baseflow_volume_flux' names => output_items bmi_status = BMI_SUCCESS end function summa_output_var_names @@ -761,6 +762,7 @@ function summa_var_units(this, name, units) result (bmi_status) case('atmosphere_energy~net~total__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS case('land_vegetation_energy~net~total__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS case('land_surface_energy~net~total__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS + case('land_surface_water__baseflow_volume_flux') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS case default; units = "-"; bmi_status = BMI_FAILURE end select end function summa_var_units @@ -1238,7 +1240,7 @@ subroutine get_basin_field(this, name, do_nHRU, target_arr, itarget_arr) ! output case('land_surface_water__runoff_volume_flux') - target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarTotalRunoff)%dat(1) + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarSurfaceRunoff)%dat(1) case('land_surface_water__evaporation_mass_flux') target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarGroundEvaporation)%dat(1) case('land_vegetation_water__evaporation_mass_flux') @@ -1267,6 +1269,8 @@ subroutine get_basin_field(this, name, do_nHRU, target_arr, itarget_arr) target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1) case('land_surface_energy~net~total__energy_flux') target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1) + case('land_surface_water__baseflow_volume_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarAquiferBaseflow)%dat(1) end select end do end do From bd6aacb50cb940b0fe4de880af5fa19b7b756c5f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 21 Apr 2023 21:35:12 +0900 Subject: [PATCH 0694/1472] simplify build --- build/cmake_ngen/build_ngen.cluster.bash | 16 ---------------- build/cmake_ngen/build_ngen.mac.bash | 3 --- 2 files changed, 19 deletions(-) mode change 100644 => 100755 build/cmake_ngen/build_ngen.mac.bash diff --git a/build/cmake_ngen/build_ngen.cluster.bash b/build/cmake_ngen/build_ngen.cluster.bash index 15df2c864..2da4021c2 100644 --- a/build/cmake_ngen/build_ngen.cluster.bash +++ b/build/cmake_ngen/build_ngen.cluster.bash @@ -9,25 +9,9 @@ module load gcc/9.3.0 module load openblas/0.3.17 module load netcdf-fortran/4.5.2 - -cmake -B extern/cfe/cmake_build -S extern/cfe -cmake --build extern/cfe/cmake_build --target all - -cmake -B extern/topmodel/cmake_build -S extern/topmodel -cmake --build extern/topmodel/cmake_build --target all - -cmake -B extern/sloth/cmake_build -S extern/sloth -cmake --build extern/sloth/cmake_build --target all - -cmake -B extern/noah-owp-modular/cmake_build -S extern/noah-owp-modular -cmake --build extern/noah-owp-modular/cmake_build --target all - cmake -B extern/iso_c_fortran_bmi/cmake_build -S extern/iso_c_fortran_bmi cmake --build extern/iso_c_fortran_bmi/cmake_build --target all -cmake -B extern/snow17/cmake_build -S extern/snow17 -cmake --build extern/snow17/cmake_build --target all - cmake -B extern/summa/cmake_build -S extern/summa -DCMAKE_BUILD_TYPE=Cluster -DSUNDIALS_ACTIVE:BOOL=OFF cmake --build extern/summa/cmake_build --target all diff --git a/build/cmake_ngen/build_ngen.mac.bash b/build/cmake_ngen/build_ngen.mac.bash old mode 100644 new mode 100755 index 4098f9a7e..0074da6b0 --- a/build/cmake_ngen/build_ngen.mac.bash +++ b/build/cmake_ngen/build_ngen.mac.bash @@ -5,9 +5,6 @@ cmake -B extern/iso_c_fortran_bmi/cmake_build -S extern/iso_c_fortran_bmi cmake --build extern/iso_c_fortran_bmi/cmake_build --target all -cmake -B extern/noah-owp-modular/cmake_build -S extern/noah-owp-modular -cmake --build extern/noah-owp-modular/cmake_build --target all - cmake -B extern/summa/cmake_build -S extern/summa -DCMAKE_BUILD_TYPE=Release -DSUNDIALS_ACTIVE:BOOL=OFF cmake --build extern/summa/cmake_build --target all From 645627408b09dc75000f6c9bddf6e89a22cfb1a1 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 25 Apr 2023 19:29:03 +0900 Subject: [PATCH 0695/1472] Can read ngen forcing now, all forcing at 3600 s then. Step 0 needs to be init step. Still only reading start and end dates for code parts off file_manager, not off json (json used for forcing). --- build/cmake_ngen/CMakeLists.txt | 2 +- build/source/driver/summa_driver.f90 | 42 +++--------- build/source/driver/summa_setup.f90 | 44 ++++++++----- build/source/engine/derivforce.f90 | 2 +- build/source/engine/read_force.f90 | 64 ++++++++++++++++--- build/source/engine/time_utils.f90 | 2 +- .../summa_fileManager_celia1990.txt | 2 +- ...xample_realization_config_w_summa_bmi.json | 6 +- ...e_realization_config_w_summa_bmi__mac.json | 6 +- 9 files changed, 100 insertions(+), 70 deletions(-) diff --git a/build/cmake_ngen/CMakeLists.txt b/build/cmake_ngen/CMakeLists.txt index e0f6408bf..ac4a0b795 100644 --- a/build/cmake_ngen/CMakeLists.txt +++ b/build/cmake_ngen/CMakeLists.txt @@ -15,7 +15,7 @@ set( CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran) set(SUMMA_LIB_NAME_CMAKE summabmi) set(SUMMA_LIB_DESC_CMAKE "Summa-Sundials BMI Module Shared Library") -# Make sure these are compiled with this directive UNCLEAR IF WE NEED THESE +# Make sure these are compiled with this directive add_compile_definitions(BMI_ACTIVE NGEN_FORCING_ACTIVE NGEN_OUTPUT_ACTIVE NGEN_ACTIVE) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 77a0dd711..66374042d 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -160,7 +160,7 @@ module summa_driver #ifdef NGEN_ACTIVE integer, parameter :: input_item_count = 8 #else - integer, parameter :: input_item_count = 12 + integer, parameter :: input_item_count = 7 #endif integer, parameter :: output_item_count = 16 character (len=BMI_MAX_VAR_NAME), target,dimension(input_item_count) :: input_items @@ -220,6 +220,7 @@ function summa_bmi_initialize(this, config_file) result (bmi_status) call handle_err(err, message) ! done with initialization + this%model%timeStep = 1 bmi_status = BMI_SUCCESS end function summa_bmi_initialize @@ -233,8 +234,6 @@ function summa_update(this) result (bmi_status) character(len=1024) :: message='' ! error message integer :: bmi_status - ! start, advance time, as model uses this time step throughout - this%model%timeStep = this%model%timeStep + 1 ! read model forcing data call summa_readForcing(this%model%timeStep, this%model%summa1_struc(n), err, message) @@ -251,6 +250,9 @@ function summa_update(this) result (bmi_status) call summa_writeOutputFiles(this%model%timeStep, this%model%summa1_struc(n), err, message) call handle_err(err, message) + ! start, advance time, as model uses this time step throughout + this%model%timeStep = this%model%timeStep + 1 + ! done with step bmi_status = BMI_SUCCESS end function summa_update @@ -342,11 +344,6 @@ function summa_input_var_names(this, names) result (bmi_status) input_items(8) = 'land_surface_wind__y_component_of_velocity' #else input_items(4) = 'land_surface_wind__speed' - input_items(8) = 'model__time_year' - input_items(9) = 'model__time_month' - input_items(10)= 'model__time_day' - input_items(11)= 'model__time_hour' - input_items(12)= 'model__time_min' #endif input_items(5) = 'land_surface_radiation~incoming~shortwave__energy_flux' input_items(6) = 'land_surface_radiation~incoming~longwave__energy_flux' @@ -711,7 +708,7 @@ function summa_var_type(this, name, type) result (bmi_status) character (len=*), intent(out) :: type integer :: bmi_status - if(name(1:5)=='model')then + if(name(1:5)=='model')then ! not currently used, left in for future integer type needs type = "integer" else type = "real" @@ -736,11 +733,6 @@ function summa_var_units(this, name, units) result (bmi_status) case('land_surface_wind__y_component_of_velocity') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS #else case('land_surface_wind__speed') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS - case('model__time_year') ; units = '' ; bmi_status = BMI_SUCCESS - case('model__time_month') ; units = '' ; bmi_status = BMI_SUCCESS - case('model__time_day') ; units = '' ; bmi_status = BMI_SUCCESS - case('model__time_hour') ; units = '' ; bmi_status = BMI_SUCCESS - case('model__time_min') ; units = '' ; bmi_status = BMI_SUCCESS #endif case('land_surface_radiation~incoming~shortwave__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS case('land_surface_radiation~incoming~longwave__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS @@ -778,7 +770,7 @@ function summa_var_itemsize(this, name, size) result (bmi_status) call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file ! use the real or integer target - if(name(1:5)=='model')then + if(name(1:5)=='model')then ! not currently used, left in for future integer type needs size = sizeof(itarget_arr) ! 'sizeof' in gcc & ifort else size = sizeof(target_arr(1)) ! 'sizeof' in gcc & ifort @@ -1134,19 +1126,11 @@ subroutine assign_basin_field(this, name, src_arr, isrc_arr) diagStruct => this%model%summa1_struc(n)%diagStruct & ! x%gru(:)%hru(:)%var(:)%dat -- model diagnostic variables ) - if(name(1:5)=='model')then + if(name(1:5)=='model')then ! not currently used, left in for future integer type needs select case (name) ! input case('model__time_year') timeStruct%var(iLookTIME%iyyy) = isrc_arr - case('model__time_month') - timeStruct%var(iLookTIME%im) = isrc_arr - case('model__time_day') - timeStruct%var(iLookTIME%id) = isrc_arr - case('model__time_hour') - timeStruct%var(iLookTIME%ih) = isrc_arr - case('model__time_min') - timeStruct%var(iLookTIME%imin) = isrc_arr end select else do iGRU = 1, this%model%summa1_struc(n)%nGRU @@ -1198,19 +1182,11 @@ subroutine get_basin_field(this, name, do_nHRU, target_arr, itarget_arr) ) target_arr = -999.0 itarget_arr = -999 - if(name(1:5)=='model')then + if(name(1:5)=='model')then ! not currently used, left in for future integer type needs select case (name) ! input case('model__time_year') itarget_arr = timeStruct%var(iLookTIME%iyyy) - case('model__time_month') - itarget_arr = timeStruct%var(iLookTIME%im) - case('model__time_day') - itarget_arr = timeStruct%var(iLookTIME%id) - case('model__time_hour') - itarget_arr = timeStruct%var(iLookTIME%ih) - case('model__time_min') - itarget_arr = timeStruct%var(iLookTIME%imin) end select else do iGRU = 1, this%model%summa1_struc(n)%nGRU diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index 1b95adc24..65e56a7d6 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -22,36 +22,39 @@ module summa_setup ! initializes parameter data structures (e.g. vegetation and soil parameters). ! access missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing double precision number +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing double precision number + +! global data on the forcing file +USE globalData,only:data_step ! length of the data step (s) ! named variables -USE var_lookup,only:iLookATTR ! look-up values for local attributes -USE var_lookup,only:iLookTYPE ! look-up values for classification of veg, soils etc. -USE var_lookup,only:iLookPARAM ! look-up values for local column model parameters -USE var_lookup,only:iLookINDEX ! look-up values for local column model indices -USE var_lookup,only:iLookLOOKUP ! look-up values for local column lookup tables -USE var_lookup,only:iLookID ! look-up values for local column model ids -USE var_lookup,only:iLookBVAR ! look-up values for basin-average model variables -USE var_lookup,only:iLookDECISIONS ! look-up values for model decisions -USE globalData,only:urbanVegCategory ! vegetation category for urban areas +USE var_lookup,only:iLookATTR ! look-up values for local attributes +USE var_lookup,only:iLookTYPE ! look-up values for classification of veg, soils etc. +USE var_lookup,only:iLookPARAM ! look-up values for local column model parameters +USE var_lookup,only:iLookINDEX ! look-up values for local column model indices +USE var_lookup,only:iLookLOOKUP ! look-up values for local column lookup tables +USE var_lookup,only:iLookID ! look-up values for local column model ids +USE var_lookup,only:iLookBVAR ! look-up values for basin-average model variables +USE var_lookup,only:iLookDECISIONS ! look-up values for model decisions +USE globalData,only:urbanVegCategory ! vegetation category for urban areas ! metadata structures -USE globalData,only:mpar_meta,bpar_meta ! parameter metadata structures +USE globalData,only:mpar_meta,bpar_meta ! parameter metadata structures ! look-up values for the choice of heat capacity computation USE mDecisions_module,only: & - enthalpyFD ! heat capacity using enthalpy + enthalpyFD ! heat capacity using enthalpy ! named variables to define the decisions for snow layers USE mDecisions_module,only:& - sameRulesAllLayers, & ! SNTHERM option: same combination/sub-dividion rules applied to all layers - rulesDependLayerIndex ! CLM option: combination/sub-dividion rules depend on layer index + sameRulesAllLayers, & ! SNTHERM option: same combination/sub-dividion rules applied to all layers + rulesDependLayerIndex ! CLM option: combination/sub-dividion rules depend on layer index ! named variables to define LAI decisions USE mDecisions_module,only:& - monthlyTable,& ! LAI/SAI taken directly from a monthly table for different vegetation classes - specified ! LAI/SAI computed from green vegetation fraction and winterSAI and summerLAI parameters + monthlyTable,& ! LAI/SAI taken directly from a monthly table for different vegetation classes + specified ! LAI/SAI computed from green vegetation fraction and winterSAI and summerLAI parameters ! safety: set private unless specified otherwise implicit none @@ -150,11 +153,18 @@ subroutine summa_paramSetup(summa1_struc, err, message) ! initialize the start of the initialization call date_and_time(values=startSetup) +#ifdef NGEN_FORCING_ACTIVE + ! ***************************************************************************** + ! if using NGEN forcing only need to set the hourly data_step (fixed) + ! ***************************************************************************** + data_step = 3600._rkind +#else ! ***************************************************************************** ! *** read description of model forcing datafile used in each HRU ! ***************************************************************************** call ffile_info(nGRU,err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif +#endif ! ***************************************************************************** ! *** read model decisions diff --git a/build/source/engine/derivforce.f90 b/build/source/engine/derivforce.f90 index abdefec42..e0f702e6c 100644 --- a/build/source/engine/derivforce.f90 +++ b/build/source/engine/derivforce.f90 @@ -181,7 +181,7 @@ subroutine derivforce(time_data,forc_data,attr_data,mpar_data,prog_data,diag_dat err=0; message="derivforce/" ! NGEN wants the wind inputted as two components, if not inputting NGEN forcing let the y direction be 0 -#ifndef NGEN_FORCING_ACTIVE +#ifdef NGEN_FORCING_ACTIVE windspd = sqrt(windspd_x**2._rkind + windspd_y**2._rkind) #else windspd_x = windspd diff --git a/build/source/engine/read_force.f90 b/build/source/engine/read_force.f90 index f519c8c85..def10a691 100644 --- a/build/source/engine/read_force.f90 +++ b/build/source/engine/read_force.f90 @@ -111,6 +111,15 @@ subroutine read_force(istep,iFile,iRead,ncid,time_data,forcStruct,err,message) currentJulDay = dJulianStart + (data_step*real(iStep-1,dp))/secprday end if +#ifdef NGEN_FORCING_ACTIVE + ! ********************************************************************************************** + ! ***** part 0-1: if using NGEN forcing will be using forcing read with BMI and only need time + ! ********************************************************************************************** + ! get forcing time data + call createForcingTimeData(currentJulday,time_data,err,message) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + +#else ! ********************************************************************************************** ! ***** part 0: if initial step, then open first file and find initial model time step ! ***** loop through as many forcing files as necessary to find the initial model step @@ -167,6 +176,8 @@ subroutine read_force(istep,iFile,iRead,ncid,time_data,forcStruct,err,message) err=20; return end if ! end ncid open check +#endif + ! ********************************************************************************************** ! ***** part 2: compute time ! ********************************************************************************************** @@ -385,7 +396,7 @@ subroutine openForcingFile(iFile,infile,ncId,err,message) ! convert the reference time to days since the beginning of time - call compjulday(iyyy,im,id,ih,imin,dsec, & ! output = year, month, day, hour, minute, second + call compjulday(iyyy,im,id,ih,imin,dsec, & ! input = year, month, day, hour, minute, second refJulday_data,err,cmessage) ! output = julian day (fraction of day) + error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if @@ -409,7 +420,7 @@ subroutine readForcingData(currentJulday,ncId,iFile,iRead,nHRUlocal,time_data,fo USE time_utils_module,only:compJulday ! convert calendar date to julian day USE get_ixname_module,only:get_ixforce ! identify index of named variable ! dummy variables - real(rkind),intent(in) :: currentJulday ! Julian day of current time step + real(rkind),intent(in) :: currentJulday ! Julian day of current time step integer(i4b) ,intent(in) :: ncId ! NetCDF ID integer(i4b) ,intent(in) :: iFile ! index of forcing file integer(i4b) ,intent(in) :: iRead ! index in data file @@ -422,7 +433,7 @@ subroutine readForcingData(currentJulday,ncId,iFile,iRead,nHRUlocal,time_data,fo character(len=256) :: cmessage ! error message for downwind routine integer(i4b) :: varId ! variable identifier character(len = nf90_max_name) :: varName ! dimenison name - real(rkind) :: varTime(1) ! time variable of current forcing data step being read + real(rkind) :: varTime(1) ! time variable of current forcing data step being read ! other local variables integer(i4b) :: iGRU,iHRU ! index of GRU and HRU integer(i4b) :: iHRU_global ! index of HRU in the NetCDF file @@ -430,12 +441,11 @@ subroutine readForcingData(currentJulday,ncId,iFile,iRead,nHRUlocal,time_data,fo integer(i4b) :: iline ! loop through lines in the file integer(i4b) :: iNC ! loop through variables in forcing file integer(i4b) :: iVar ! index of forcing variable in forcing data vector - logical(lgt),parameter :: checkTime=.false. ! flag to check the time - real(rkind) :: dsec ! double precision seconds (not used) - real(rkind) :: dataJulDay ! julian day of current forcing data step being read - real(rkind),dimension(nHRUlocal) :: dataVec ! vector of data - real(rkind),dimension(1) :: dataVal ! single data value - real(rkind),parameter :: dataMin=-1._rkind ! minimum allowable data value (all forcing variables should be positive) + real(rkind) :: dsec ! double precision seconds (not used) + real(rkind) :: dataJulDay ! julian day of current forcing data step being read + real(rkind),dimension(nHRUlocal) :: dataVec ! vector of data + real(rkind),dimension(1) :: dataVal ! single data value + real(rkind),parameter :: dataMin=-1._rkind ! minimum allowable data value (all forcing variables should be positive) logical(lgt),dimension(size(forc_meta)) :: checkForce ! flags to check forcing data variables exist logical(lgt),parameter :: simultaneousRead=.true. ! flag to denote reading all HRUs at once ! Start procedure here @@ -486,7 +496,7 @@ subroutine readForcingData(currentJulday,ncId,iFile,iRead,nHRUlocal,time_data,fo ! get index in forcing structure iVar = forcFileInfo(iFile)%var_ix(iNC) checkForce(iVar) = .true. - + ! get variable name for error reporting err=nf90_inquire_variable(ncid,iNC,name=varName) if(err/=nf90_noerr)then; message=trim(message)//'problem reading forcing variable name from netCDF: '//trim(nf90_strerror(err)); return; endif @@ -548,5 +558,39 @@ subroutine readForcingData(currentJulday,ncId,iFile,iRead,nHRUlocal,time_data,fo end subroutine readForcingData + ! ************************************************************************* + ! * create forcing time data if using NGEN (uses BMI) + ! ************************************************************************* + subroutine createForcingTimeData(currentJulday,time_data,err,message) + USE time_utils_module,only:compcalday ! convert julian day to calendar date + ! dummy variables + real(rkind),intent(in) :: currentJulday ! Julian day of current time step + integer(i4b),intent(out) :: time_data(:) ! vector of time data for a given time step + integer(i4b) ,intent(out) :: err ! error code + character(*) ,intent(out) :: message ! error message + ! local variables + character(len=256) :: cmessage ! error message for downwind routine + ! other local variables + real(rkind) :: dsec ! double precision seconds (not used) + + ! Start procedure here + err=0; message="createForcingTimeData/" + + ! initialize time and forcing data structures + time_data(:) = integerMissing + + ! convert julian day to time vector + ! NOTE: use small offset to force ih=0 at the start of the day + call compcalday(currentJulday+smallOffset, & ! input = julian day + time_data(iLookTIME%iyyy), & ! output = year + time_data(iLookTIME%im), & ! output = month + time_data(iLookTIME%id), & ! output = day + time_data(iLookTIME%ih), & ! output = hour + time_data(iLookTIME%imin),dsec, & ! output = minute/second + err,cmessage) ! output = error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + end subroutine createForcingTimeData + end module read_force_module diff --git a/build/source/engine/time_utils.f90 b/build/source/engine/time_utils.f90 index 451dc037f..2d56110cf 100644 --- a/build/source/engine/time_utils.f90 +++ b/build/source/engine/time_utils.f90 @@ -328,7 +328,7 @@ subroutine compcalday(julday, & !input integer(i4b), intent(out) :: id ! day integer(i4b), intent(out) :: ih ! hour integer(i4b), intent(out) :: imin ! minute - real(rkind), intent(out) :: dsec ! seconds + real(rkind), intent(out) :: dsec ! seconds integer(i4b), intent(out) :: err ! error code character(*), intent(out) :: message ! error message diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt index 7c50cb173..42d36d557 100644 --- a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt +++ b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt @@ -1,5 +1,5 @@ controlVersion 'SUMMA_FILE_MANAGER_V3.0.0' -simStartTime '2000-01-01 00:30' +simStartTime '2000-01-01 01:00' simEndTime '2000-01-03 12:00' tmZoneInfo 'localTime' settingsPath './extern/summa/summa/test_ngen/celia_test/settings/' diff --git a/test_ngen/example_realization_config_w_summa_bmi.json b/test_ngen/example_realization_config_w_summa_bmi.json index 9295a12e9..231dcb307 100644 --- a/test_ngen/example_realization_config_w_summa_bmi.json +++ b/test_ngen/example_realization_config_w_summa_bmi.json @@ -8,7 +8,7 @@ "library_file": "./extern/summa/cmake_build/libsummabmi.so", "forcing_file": "", "init_config": "./extern/summa/summa/test_ngen/summa-init-celia.namelist.input", - "allow_exceed_end_time": false, + "allow_exceed_end_time": true, "main_output_variable": "land_surface_water__runoff_volume_flux", "variables_names_map": { "atmosphere_water__precipitation_mass_flux": "precip_rate", @@ -31,8 +31,8 @@ } }, "time": { - "start_time": "2000-01-01 00:30:00", + "start_time": "2000-01-01 01:00:00", "end_time": "2000-01-03 12:00:00", - "output_interval": 5400 + "output_interval": 3600 } } diff --git a/test_ngen/example_realization_config_w_summa_bmi__mac.json b/test_ngen/example_realization_config_w_summa_bmi__mac.json index fe533e91b..0d6106cd3 100644 --- a/test_ngen/example_realization_config_w_summa_bmi__mac.json +++ b/test_ngen/example_realization_config_w_summa_bmi__mac.json @@ -8,7 +8,7 @@ "library_file": "./extern/summa/cmake_build/libsummabmi.dylib", "forcing_file": "", "init_config": "./extern/summa/summa/test_ngen/summa-init-celia.namelist.input", - "allow_exceed_end_time": false, + "allow_exceed_end_time": true, "main_output_variable": "land_surface_water__runoff_volume_flux", "variables_names_map": { "atmosphere_water__precipitation_mass_flux": "precip_rate", @@ -31,8 +31,8 @@ } }, "time": { - "start_time": "2000-01-01 00:30:00", + "start_time": "2000-01-01 01:00:00", "end_time": "2000-01-03 12:00:00", - "output_interval": 5400 + "output_interval": 3600 } } From 391cf611a56713e7f6602c013c9baf8243fe9990 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 26 Apr 2023 16:24:10 +0900 Subject: [PATCH 0696/1472] turn off rootfinding for paper --- build/source/engine/summaSolveSundialsIDA.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 030f3c7fe..fe357d851 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -227,7 +227,7 @@ subroutine summaSolveSundialsIDA( & real(rkind),allocatable :: mLayerMatricHeadPrimePrev(:) ! previous derivative value for total water matric potential (m s-1) real(rkind),allocatable :: dCompress_dPsiPrev(:) ! previous derivative value soil compression logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true - logical(lgt),parameter :: detect_events = .true. ! flag to do event detection and restarting, default true + logical(lgt),parameter :: detect_events = .false. ! flag to do event detection and restarting, default true logical(lgt),parameter :: use_fdJac = .false. ! flag to use finite difference Jacobian, default false ! ----------------------------------------------------------------------------------------------------- From f598f6c3f0b96f84d0a23c8c583c0523403536b2 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 28 Apr 2023 14:26:03 +0900 Subject: [PATCH 0697/1472] flux of canopy sublimation not gathered correctly in BE>1 with step failures in canopy sub --- build/source/engine/coupled_em.f90 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 1798b3ba1..47bb05544 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -804,6 +804,7 @@ subroutine coupled_em(& ! correct increments (if need to redo inner step) and reset increment dt_solv = dt_solv - dt_solvInner dt_solvInner = 0._rkind + lastInnerStep = .false. ! initialize sublimation sums to average over whole_step sumCanopySublimation = 0._rkind @@ -1044,9 +1045,9 @@ subroutine coupled_em(& meanSoilCompress = meanSoilCompress + innerSoilCompress*dt_wght effRainfall = effRainfall + innerEffRainfall*dt_wght flux_mean%var(childFLUX_MEAN(iLookDIAG%scalarSoilCompress))%dat(1) = meanSoilCompress - flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySublimation))%dat(1) = sumCanopySublimation/data_step ! these two will be equal unless insufficient canopy water for sublim - flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarLatHeatCanopyEvap))%dat(1) = sumLatHeatCanopyEvap/data_step ! these two will be equal unless insufficient canopy water for sublim - flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSenHeatCanopy))%dat(1) = sumSenHeatCanopy/data_step ! these two will be equal unless insufficient canopy water for sublim + flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySublimation))%dat(1) = flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySublimation))%dat(1) + sumCanopySublimation/data_step + flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarLatHeatCanopyEvap))%dat(1) = flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarLatHeatCanopyEvap))%dat(1) + sumLatHeatCanopyEvap/data_step + flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSenHeatCanopy))%dat(1) = flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSenHeatCanopy))%dat(1) + sumSenHeatCanopy/data_step endif ! save the time step to initialize the subsequent step From 21ad25a8cf41983787d8506626cd6c6aab174c10 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 28 Apr 2023 14:47:36 +0900 Subject: [PATCH 0698/1472] need to collect canopy sublims outside of flux --- build/source/engine/coupled_em.f90 | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 47bb05544..c6adeb671 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -223,12 +223,14 @@ subroutine coupled_em(& type(var_dlength) :: flux_inner ! inner step average model fluxes for a local HRU real(rkind) :: meanSoilCompress ! timestep-average soil compression real(rkind) :: innerSoilCompress ! inner step average soil compression - ! sublimation sums over substep and means over data_step real(rkind) :: sumCanopySublimation ! sum of sublimation from the vegetation canopy (kg m-2 s-1) over substep real(rkind) :: sumSnowSublimation ! sum of sublimation from the snow surface (kg m-2 s-1) over substep real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) over substep real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) over substep + real(rkind) :: meanCanopySublimation ! timestep-average sublimation from the vegetation canopy (kg m-2 s-1) + real(rkind) :: meanLatHeatCanopyEvap ! timestep-average latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + real(rkind) :: meanSenHeatCanopy ! timestep-average sensible heat flux from the canopy to the canopy air space (W m-2) ! balance checks integer(i4b) :: iVar ! loop through model variables real(rkind) :: balanceSoilCompress ! total soil compression (kg m-2) @@ -340,8 +342,12 @@ subroutine coupled_em(& flux_mean%var(iVar)%dat(:) = 0._rkind end do meanSoilCompress = 0._rkind ! mean total soil compression + meanCanopySublimation = 0._rkind ! mean canopy sublimation + meanLatHeatCanopyEvap = 0._rkind ! mean latent heat flux for evaporation from the canopy + meanSenHeatCanopy = 0._rkind ! mean sensible heat flux from the canopy effRainfall = 0._rkind ! mean total effective rainfall over snow + ! associate local variables with information in the data structures associate(& ! state variables in the vegetation canopy @@ -1043,11 +1049,14 @@ subroutine coupled_em(& flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_inner%var(iVar)%dat(:)*dt_wght end do meanSoilCompress = meanSoilCompress + innerSoilCompress*dt_wght + meanCanopySublimation = meanCanopySublimation + sumCanopySublimation/data_step + meanLatHeatCanopyEvap = meanLatHeatCanopyEvap + sumLatHeatCanopyEvap/data_step + meanSenHeatCanopy = meanSenHeatCanopy + sumSenHeatCanopy/data_step effRainfall = effRainfall + innerEffRainfall*dt_wght flux_mean%var(childFLUX_MEAN(iLookDIAG%scalarSoilCompress))%dat(1) = meanSoilCompress - flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySublimation))%dat(1) = flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySublimation))%dat(1) + sumCanopySublimation/data_step - flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarLatHeatCanopyEvap))%dat(1) = flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarLatHeatCanopyEvap))%dat(1) + sumLatHeatCanopyEvap/data_step - flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSenHeatCanopy))%dat(1) = flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSenHeatCanopy))%dat(1) + sumSenHeatCanopy/data_step + flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySublimation))%dat(1) = meanCanopySublimation + flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarLatHeatCanopyEvap))%dat(1) = meanLatHeatCanopyEvap + flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSenHeatCanopy))%dat(1) = meanSenHeatCanopy endif ! save the time step to initialize the subsequent step From 1e25f74c40745f4b680067a77b47a45bb2b31d85 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 1 May 2023 13:16:18 +0900 Subject: [PATCH 0699/1472] add latest BE32 changes --- build/source/engine/coupled_em.f90 | 18 ++++++++++++++---- build/source/engine/summaSolveSundialsIDA.f90 | 2 +- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 1798b3ba1..c6adeb671 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -223,12 +223,14 @@ subroutine coupled_em(& type(var_dlength) :: flux_inner ! inner step average model fluxes for a local HRU real(rkind) :: meanSoilCompress ! timestep-average soil compression real(rkind) :: innerSoilCompress ! inner step average soil compression - ! sublimation sums over substep and means over data_step real(rkind) :: sumCanopySublimation ! sum of sublimation from the vegetation canopy (kg m-2 s-1) over substep real(rkind) :: sumSnowSublimation ! sum of sublimation from the snow surface (kg m-2 s-1) over substep real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) over substep real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) over substep + real(rkind) :: meanCanopySublimation ! timestep-average sublimation from the vegetation canopy (kg m-2 s-1) + real(rkind) :: meanLatHeatCanopyEvap ! timestep-average latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + real(rkind) :: meanSenHeatCanopy ! timestep-average sensible heat flux from the canopy to the canopy air space (W m-2) ! balance checks integer(i4b) :: iVar ! loop through model variables real(rkind) :: balanceSoilCompress ! total soil compression (kg m-2) @@ -340,8 +342,12 @@ subroutine coupled_em(& flux_mean%var(iVar)%dat(:) = 0._rkind end do meanSoilCompress = 0._rkind ! mean total soil compression + meanCanopySublimation = 0._rkind ! mean canopy sublimation + meanLatHeatCanopyEvap = 0._rkind ! mean latent heat flux for evaporation from the canopy + meanSenHeatCanopy = 0._rkind ! mean sensible heat flux from the canopy effRainfall = 0._rkind ! mean total effective rainfall over snow + ! associate local variables with information in the data structures associate(& ! state variables in the vegetation canopy @@ -804,6 +810,7 @@ subroutine coupled_em(& ! correct increments (if need to redo inner step) and reset increment dt_solv = dt_solv - dt_solvInner dt_solvInner = 0._rkind + lastInnerStep = .false. ! initialize sublimation sums to average over whole_step sumCanopySublimation = 0._rkind @@ -1042,11 +1049,14 @@ subroutine coupled_em(& flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_inner%var(iVar)%dat(:)*dt_wght end do meanSoilCompress = meanSoilCompress + innerSoilCompress*dt_wght + meanCanopySublimation = meanCanopySublimation + sumCanopySublimation/data_step + meanLatHeatCanopyEvap = meanLatHeatCanopyEvap + sumLatHeatCanopyEvap/data_step + meanSenHeatCanopy = meanSenHeatCanopy + sumSenHeatCanopy/data_step effRainfall = effRainfall + innerEffRainfall*dt_wght flux_mean%var(childFLUX_MEAN(iLookDIAG%scalarSoilCompress))%dat(1) = meanSoilCompress - flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySublimation))%dat(1) = sumCanopySublimation/data_step ! these two will be equal unless insufficient canopy water for sublim - flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarLatHeatCanopyEvap))%dat(1) = sumLatHeatCanopyEvap/data_step ! these two will be equal unless insufficient canopy water for sublim - flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSenHeatCanopy))%dat(1) = sumSenHeatCanopy/data_step ! these two will be equal unless insufficient canopy water for sublim + flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySublimation))%dat(1) = meanCanopySublimation + flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarLatHeatCanopyEvap))%dat(1) = meanLatHeatCanopyEvap + flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSenHeatCanopy))%dat(1) = meanSenHeatCanopy endif ! save the time step to initialize the subsequent step diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index 030f3c7fe..fe357d851 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -227,7 +227,7 @@ subroutine summaSolveSundialsIDA( & real(rkind),allocatable :: mLayerMatricHeadPrimePrev(:) ! previous derivative value for total water matric potential (m s-1) real(rkind),allocatable :: dCompress_dPsiPrev(:) ! previous derivative value soil compression logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true - logical(lgt),parameter :: detect_events = .true. ! flag to do event detection and restarting, default true + logical(lgt),parameter :: detect_events = .false. ! flag to do event detection and restarting, default true logical(lgt),parameter :: use_fdJac = .false. ! flag to use finite difference Jacobian, default false ! ----------------------------------------------------------------------------------------------------- From bd29ae87c92661d80fe52d3396d9f8558e5a79c7 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 3 May 2023 12:13:18 +0900 Subject: [PATCH 0700/1472] should be #ifdef SUNDIALS_ACTIVE --- build/source/engine/varSubstep.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index fb8a17897..32c889fe1 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -132,7 +132,7 @@ subroutine varSubstep(& ! simulation of fluxes and residuals given a trial state vector USE getVectorz_module,only:popStateVec ! populate the state vector USE getVectorz_module,only:varExtract ! extract variables from the state vector -#ifdef NGEN_SUNDIALS_ACTIVE +#ifdef SUNDIALS_ACTIVE USE systemSolvSundials_module,only:systemSolvSundials ! solve the system of equations for one time step #endif USE systemSolv_module,only:systemSolv ! solve the system of equations for one time step From c236a2192175345d4cfac5231b50cd2eb43b6ec3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 3 May 2023 23:21:25 +0900 Subject: [PATCH 0701/1472] fixing docs --- docs/sundials/flags_params.txt | 24 ++++++++++++++++++ {sundials => docs/sundials}/installation.txt | 0 sundials/flags_params.txt | 26 -------------------- 3 files changed, 24 insertions(+), 26 deletions(-) create mode 100644 docs/sundials/flags_params.txt rename {sundials => docs/sundials}/installation.txt (100%) delete mode 100644 sundials/flags_params.txt diff --git a/docs/sundials/flags_params.txt b/docs/sundials/flags_params.txt new file mode 100644 index 000000000..216ddc8bf --- /dev/null +++ b/docs/sundials/flags_params.txt @@ -0,0 +1,24 @@ + +To switch between SUMMA-BE and SUMMA-SUNDIALS, the "num_method" variable is used in the var_lookup module. +A user should add this variable to the model_decision file with one of the values "bEuler" (backwards compatible with "itertive" or "sundials". + +In energy equation, the heat capacity is computed using either the analytical (closed) formula or the finite differece formula (dH_T/dT). The +"howHeatCap" varialbe has been added to the var_lookup module to handle such decision. A user should add this variable to the model_decision +file with one of the values "closedForm" or "enthalpyFD". For now, this value is by default "colsedForm" in mDecisions.f90. + +All SUMMA-SUNDIALS files are in build/source/engine. The important ones are as following: + +systemSolvSundials.f90: contains public subroutine systemSolvSundials which runs the coupled energy-mass model for one timestep + +summaSolveSundialsIDA.f90: contains public subroutine summaSolveSundialsIDA which solves the differential equation system F(y,y') = 0 by IDA + (y is the state vector) and private subroutines setInitialCondition and setSolverParams. + setSolverParams can be used to to set parameters (maximum order, number of nonlinear iteration , etc) in IDA solver. + +eval8summaSundials.f90: contains public subroutine eval8summaSundials which computes the residual vector F(t,y,y') + mainly by calling varExtract, updateVarsSundials, computFlux, and computResidSundials. We also switch between different forms + of the energy equation in this subroutine. It also contains public function eval8summa4IDA which is the interface wrapper for + computing the residual vector required for the IDA solver. + +computJacobSundials.f90: contains public subroutine computJacobSundials which computes the Jacobian matrix dF/dy + c dF/dy'. It also contains + the public function computJacob4IDA which is the interface wrapper for computing the Jacobian matrix required for the IDA solver. + diff --git a/sundials/installation.txt b/docs/sundials/installation.txt similarity index 100% rename from sundials/installation.txt rename to docs/sundials/installation.txt diff --git a/sundials/flags_params.txt b/sundials/flags_params.txt deleted file mode 100644 index 50242d808..000000000 --- a/sundials/flags_params.txt +++ /dev/null @@ -1,26 +0,0 @@ - -To switch between SUMMA-BE and SUMMA-SUNDIALS, the "diffEqSolv" variabe has been added to the var_lookup module. -A user should add this variable to the model_decision file with one of the values "backwEuler" or "sundialIDA". - For now, this value is by default "sundialIDA" in mDecisions.f90. - -In energy equation, the heat capacity is computed using either the analytical (closed) formula or the finite differece formula (dH_T/dT). The -"howHeatCap" varialbe has been added to the var_lookup module to handle such decision. A user should add this variable to the model_decision -file with one of the values "closedForm" or "enthalpyFD". For now, this value is by default "colsedForm" in mDecisions.f90. - -All SUMMA-SUNDIALS files are in build/source/engine. The important ones are as following: - -systemSolvSundials.f90: contains public subroutine systemSolvSundials which runs the coupled energy-mass model for one timestep - -solveByIDA.f90: contains public subroutine solveByIDA which solves the differential equation system F(y,y') = 0 by IDA (y is the state vector) - and private subroutines setInitialCondition and setSolverParams. - setSolverParams can be used to to set paprmeters (maximum order, number of nonlinear iteration , etc) in IDA solver - -evalDAE4IDA.f90: contains public function evalDAE4IDA which is the interface wrapper for computing the residual vector F(t,y,y') required for IDA solver - -eval8DAE.f90: contains public subroutine eval8DAE which computes the residual vector mainly by calling varExtractSundials, updateVarsSundials, computFlux, - and computResidDAE. We also switch between different forms of the energy equation in this subroutine. - -evalJac4IDA.f90: contains public function evalJac4IDA which is the interface wrapper for computing the Jacobian matrix dF/dy + c dF/dy' for IDA solver - -eval8JacDAE.f90: contains public subroutine eval8JacDAE which computes the residual vector and the Jacobian matrix mainly by calling computJacDAE - From b72383c06c9f4f77b7ad754e7f353d2ae54f2a5d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 3 May 2023 23:22:12 +0900 Subject: [PATCH 0702/1472] fixing docs --- {sundials_bmi => docs/sundials_bmi}/bmi_interface.txt | 0 {sundials_bmi => docs/sundials_bmi}/flags_params_sundials.txt | 0 {sundials_bmi => docs/sundials_bmi}/installation.txt | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {sundials_bmi => docs/sundials_bmi}/bmi_interface.txt (100%) rename {sundials_bmi => docs/sundials_bmi}/flags_params_sundials.txt (100%) rename {sundials_bmi => docs/sundials_bmi}/installation.txt (100%) diff --git a/sundials_bmi/bmi_interface.txt b/docs/sundials_bmi/bmi_interface.txt similarity index 100% rename from sundials_bmi/bmi_interface.txt rename to docs/sundials_bmi/bmi_interface.txt diff --git a/sundials_bmi/flags_params_sundials.txt b/docs/sundials_bmi/flags_params_sundials.txt similarity index 100% rename from sundials_bmi/flags_params_sundials.txt rename to docs/sundials_bmi/flags_params_sundials.txt diff --git a/sundials_bmi/installation.txt b/docs/sundials_bmi/installation.txt similarity index 100% rename from sundials_bmi/installation.txt rename to docs/sundials_bmi/installation.txt From faaffbe2d3b5a5a86417df96044d8f3475478823 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 3 May 2023 23:55:21 +0900 Subject: [PATCH 0703/1472] moving actors changes into code and setting up cmake for initial commit --- build/cmake/build.cluster.bash | 17 + build/cmake/build.mac.bash | 8 + build/cmake_ngen/build_ngen.cluster.bash | 2 +- build/cmake_ngen/build_ngen.mac.bash | 2 +- build/source/dshare/globalData.f90 | 628 +++++++++++------------ build/source/dshare/var_lookup.f90 | 11 +- build/source/engine/coupled_em.f90 | 18 +- build/source/engine/derivforce.f90 | 118 ++--- build/source/engine/mDecisions.f90 | 28 +- build/source/engine/run_oneHRU.f90 | 37 +- build/source/engine/vegPhenlgy.f90 | 16 +- 11 files changed, 466 insertions(+), 419 deletions(-) create mode 100644 build/cmake/build.cluster.bash create mode 100755 build/cmake/build.mac.bash mode change 100644 => 100755 build/source/dshare/globalData.f90 mode change 100644 => 100755 build/source/engine/derivforce.f90 mode change 100644 => 100755 build/source/engine/mDecisions.f90 mode change 100644 => 100755 build/source/engine/vegPhenlgy.f90 diff --git a/build/cmake/build.cluster.bash b/build/cmake/build.cluster.bash new file mode 100644 index 000000000..5c8c7cc94 --- /dev/null +++ b/build/cmake/build.cluster.bash @@ -0,0 +1,17 @@ +#!/bin/bash + +# build on Copernicus or Graham, from summa directory put this one directory up and run this as ../build.cluster.bash + +module load boost +module load udunits/2.2.28 +module load StdEnv/2020 +module load gcc/9.3.0 +module load openblas/0.3.17 +module load netcdf-fortran/4.5.2 + + +cmake -B cmake_build -S summa -DCMAKE_BUILD_TYPE=Cluster -DSUNDIALS_ACTIVE:BOOL=ON -DACTORS_ACTIVE:BOOL=OFF +cmake --build summa/cmake_build --target all + +make -C cmake_build + diff --git a/build/cmake/build.mac.bash b/build/cmake/build.mac.bash new file mode 100755 index 000000000..b32c4163a --- /dev/null +++ b/build/cmake/build.mac.bash @@ -0,0 +1,8 @@ +#!/bin/bash + +# build on Mac, from summa directory put this one directory up and run this as ../build.mac.bash + +cmake -B cmake_build -S summa -DCMAKE_BUILD_TYPE=Release -DSUNDIALS_ACTIVE:BOOL=ON -DACTORS_ACTIVE:BOOL=OFF +cmake --build summa/cmake_build --target all + +make -C cmake_build diff --git a/build/cmake_ngen/build_ngen.cluster.bash b/build/cmake_ngen/build_ngen.cluster.bash index 2da4021c2..03910b579 100644 --- a/build/cmake_ngen/build_ngen.cluster.bash +++ b/build/cmake_ngen/build_ngen.cluster.bash @@ -1,6 +1,6 @@ #!/bin/bash -# build nextgen on Copernicus, from ngen directory put this one directory up and run this as ../example_build_ngen.bash +# build nextgen on Copernicus, from ngen directory put this one directory up and run this as ../build_ngen.cluster.bash module load boost module load udunits/2.2.28 diff --git a/build/cmake_ngen/build_ngen.mac.bash b/build/cmake_ngen/build_ngen.mac.bash index 0074da6b0..33250bd82 100755 --- a/build/cmake_ngen/build_ngen.mac.bash +++ b/build/cmake_ngen/build_ngen.mac.bash @@ -1,6 +1,6 @@ #!/bin/bash -# build nextgen on Mac, from ngen directory put this one directory up and run this as ../example_build_ngen.mac.bash +# build nextgen on Mac, from ngen directory put this one directory up and run this as ../build_ngen.mac.bash cmake -B extern/iso_c_fortran_bmi/cmake_build -S extern/iso_c_fortran_bmi cmake --build extern/iso_c_fortran_bmi/cmake_build --target all diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 old mode 100644 new mode 100755 index 8fe721277..fc3724362 --- a/build/source/dshare/globalData.f90 +++ b/build/source/dshare/globalData.f90 @@ -23,329 +23,315 @@ ! ---------------------------------------------------------------------------------------------------------------- MODULE globalData - ! data types - USE nrtype - USE netcdf - USE,intrinsic :: ieee_arithmetic ! IEEE arithmetic - USE data_types,only:gru2hru_map ! mapping between the GRUs and HRUs - USE data_types,only:hru2gru_map ! mapping between the GRUs and HRUs - USE data_types,only:model_options ! the model decision structure - USE data_types,only:file_info ! metadata for model forcing datafile - USE data_types,only:par_info ! default parameter values and parameter bounds - USE data_types,only:var_info ! metadata for variables in each model structure - USE data_types,only:flux2state ! extended metadata to define flux-to-state mapping - USE data_types,only:extended_info ! extended metadata for variables in each model structure - USE data_types,only:struct_info ! summary information on all data structures - USE data_types,only:var_i ! vector of integers - ! number of variables in each data structure - USE var_lookup,only:maxvarTime ! time: maximum number variables - USE var_lookup,only:maxvarForc ! forcing data: maximum number variables - USE var_lookup,only:maxvarAttr ! attributes: maximum number variables - USE var_lookup,only:maxvarType ! type index: maximum number variables - USE var_lookup,only:maxvarId ! IDs index: maximum number variables - USE var_lookup,only:maxvarProg ! prognostic variables: maximum number variables - USE var_lookup,only:maxvarDiag ! diagnostic variables: maximum number variables - USE var_lookup,only:maxvarFlux ! model fluxes: maximum number variables - USE var_lookup,only:maxvarDeriv ! model derivatives: maximum number variables - USE var_lookup,only:maxvarIndx ! model indices: maximum number variables - USE var_lookup,only:maxvarMpar ! model parameters: maximum number variables - USE var_lookup,only:maxvarBvar ! basin-average variables: maximum number variables - USE var_lookup,only:maxvarBpar ! basin-average parameters: maximum number variables - USE var_lookup,only:maxvarDecisions ! maximum number of decisions - USE var_lookup,only:maxvarFreq ! maximum number of output files - USE var_lookup,only:maxvarLookup ! maximum number of variables in the lookup tables - implicit none - private - - ! ---------------------------------------------------------------------------------------------------------------- - ! * part 1: parameters that are fixed across multiple instantiations - ! ---------------------------------------------------------------------------------------------------------------- - - ! define missing values - real(rkind),parameter,public :: quadMissing = nr_quadMissing ! (from nrtype) missing quadruple precision number - real(rkind),parameter,public :: realMissing = nr_realMissing ! (from nrtype) missing double precision number - integer(i4b),parameter,public :: integerMissing = nr_integerMissing ! (from nrtype) missing integer - - ! define run modes - integer(i4b),parameter,public :: iRunModeFull=1 ! named variable defining running mode as full run (all GRUs) - integer(i4b),parameter,public :: iRunModeGRU=2 ! named variable defining running mode as GRU-parallelization run (GRU subset) - integer(i4b),parameter,public :: iRunModeHRU=3 ! named variable defining running mode as single-HRU run (ONE HRU) - - ! define progress modes - integer(i4b),parameter,public :: ixProgress_im=1000 ! named variable to print progress once per month - integer(i4b),parameter,public :: ixProgress_id=1001 ! named variable to print progress once per day - integer(i4b),parameter,public :: ixProgress_ih=1002 ! named variable to print progress once per hour - integer(i4b),parameter,public :: ixProgress_never=1003 ! named variable to print progress never - integer(i4b),parameter,public :: ixProgress_it=1004 ! named variable to print progress every timestep - - ! define restart frequency - integer(i4b),parameter,public :: ixRestart_iy=1000 ! named variable to print a re-start file once per year - integer(i4b),parameter,public :: ixRestart_im=1001 ! named variable to print a re-start file once per month - integer(i4b),parameter,public :: ixRestart_id=1002 ! named variable to print a re-start file once per day - integer(i4b),parameter,public :: ixRestart_end=1003 ! named variable to print a re-start file at the end of a run - integer(i4b),parameter,public :: ixRestart_never=1004 ! named variable to print a re-start file never - - ! define output file frequency - integer(i4b),parameter,public :: noNewFiles=1001 ! no new output files - integer(i4b),parameter,public :: newFileEveryOct1=1002 ! create a new file on Oct 1 every year (start of the USA water year) -! =======------- - ! define vectors of metadata - !type(var_info),save,public :: time_meta(maxvarTime) ! model time information - !type(var_info),save,public :: forc_meta(maxvarForc) ! model forcing data - !type(var_info),save,public :: attr_meta(maxvarAttr) ! local attributes - !type(var_info),save,public :: type_meta(maxvarType) ! local classification of veg, soil, etc. - !type(var_info),save,public :: id_meta(maxvarId) ! local labels of hru and gru IDs - !type(var_info),save,public :: mpar_meta(maxvarMpar) ! local model parameters for each HRU - !type(var_info),save,public :: indx_meta(maxvarIndx) ! local model indices for each HRU - !type(var_info),save,public :: prog_meta(maxvarProg) ! local state variables for each HRU - !type(var_info),save,public :: diag_meta(maxvarDiag) ! local diagnostic variables for each HRU - !type(var_info),save,public :: flux_meta(maxvarFlux) ! local model fluxes for each HRU - !type(var_info),save,public :: deriv_meta(maxvarDeriv) ! local model derivatives for each HRU - !type(var_info),save,public :: bpar_meta(maxvarBpar) ! basin parameters for aggregated processes - !type(var_info),save,public :: bvar_meta(maxvarBvar) ! basin variables for aggregated processes - - ! ancillary metadata structures - !type(flux2state), save,public :: flux2state_orig(maxvarFlux) ! named variables for the states affected by each flux (original) - !type(flux2state), save,public :: flux2state_liq(maxvarFlux) ! named variables for the states affected by each flux (liquid water) - !type(extended_info),save,public,allocatable :: averageFlux_meta(:) ! timestep-average model fluxes - - ! define summary information on all data structures - !integer(i4b),parameter :: nStruct=13 ! number of data structures - !type(struct_info),parameter,public,dimension(nStruct) :: structInfo=(/& - ! struct_info('time', 'TIME' , maxvarTime ), & ! the time data structure - ! struct_info('forc', 'FORCE', maxvarForc ), & ! the forcing data structure - ! struct_info('attr', 'ATTR' , maxvarAttr ), & ! the attribute data structure - ! struct_info('type', 'TYPE' , maxvarType ), & ! the type data structure - ! struct_info('id', 'ID' , maxvarId ), & ! the IDs data structure - ! struct_info('mpar', 'PARAM', maxvarMpar ), & ! the model parameter data structure - ! struct_info('bpar', 'BPAR' , maxvarBpar ), & ! the basin parameter data structure - ! struct_info('bvar', 'BVAR' , maxvarBvar ), & ! the basin variable data structure - ! struct_info('indx', 'INDEX', maxvarIndx ), & ! the model index data structure - ! struct_info('prog', 'PROG', maxvarProg ), & ! the prognostic (state) variable data structure - ! struct_info('diag', 'DIAG' , maxvarDiag ), & ! the diagnostic variable data structure - ! struct_info('flux', 'FLUX' , maxvarFlux ), & ! the flux data structure - ! struct_info('deriv', 'DERIV', maxvarDeriv) /) ! the model derivative data structure - - ! define named variables for "yes" and "no" - integer(i4b),parameter,public :: no=0 ! .false. - integer(i4b),parameter,public :: yes=1 ! .true. - - ! define named variables to describe the domain type - integer(i4b),parameter,public :: iname_cas =1000 ! named variable to denote a canopy air space state variable - integer(i4b),parameter,public :: iname_veg =1001 ! named variable to denote a vegetation state variable - integer(i4b),parameter,public :: iname_soil=1002 ! named variable to denote a soil layer - integer(i4b),parameter,public :: iname_snow=1003 ! named variable to denote a snow layer - integer(i4b),parameter,public :: iname_aquifer=1004 ! named variable to denote a snow layer - - ! define named variables to describe the state variable type - integer(i4b),parameter,public :: iname_nrgCanair=2001 ! named variable defining the energy of the canopy air space - integer(i4b),parameter,public :: iname_nrgCanopy=2002 ! named variable defining the energy of the vegetation canopy - integer(i4b),parameter,public :: iname_watCanopy=2003 ! named variable defining the mass of total water on the vegetation canopy - integer(i4b),parameter,public :: iname_liqCanopy=2004 ! named variable defining the mass of liquid water on the vegetation canopy - integer(i4b),parameter,public :: iname_nrgLayer=3001 ! named variable defining the energy state variable for snow+soil layers - integer(i4b),parameter,public :: iname_watLayer=3002 ! named variable defining the total water state variable for snow+soil layers - integer(i4b),parameter,public :: iname_liqLayer=3003 ! named variable defining the liquid water state variable for snow+soil layers - integer(i4b),parameter,public :: iname_matLayer=3004 ! named variable defining the matric head state variable for soil layers - integer(i4b),parameter,public :: iname_lmpLayer=3005 ! named variable defining the liquid matric potential state variable for soil layers - integer(i4b),parameter,public :: iname_watAquifer=3006 ! named variable defining the water storage in the aquifer - - ! define named variables to describe the form and structure of the band-diagonal matrices used in the numerical solver - ! NOTE: This indexing scheme provides the matrix structure expected by lapack and sundials. Specifically, they require kl extra rows for additional storage. - ! Consequently, all indices are offset by kl and the total number of bands for storage is 2*kl+ku+1 instead of kl+ku+1. - integer(i4b),parameter,public :: nRHS=1 ! number of unknown variables on the RHS of the linear system A.X=B - integer(i4b),parameter,public :: ku=3 ! number of super-diagonal bands, ku>=3 to accommodate coupled layer above - integer(i4b),parameter,public :: kl=4 ! number of sub-diagonal bands, kl>=4 to accommodate vegetation - integer(i4b),parameter,public :: ixDiag=kl+ku+1 ! index for the diagonal band - integer(i4b),parameter,public :: nBands=2*kl+ku+1 ! length of the leading dimension of the band diagonal matrix - - ! define named variables for the type of matrix used in the numerical solution. - integer(i4b),parameter,public :: ixFullMatrix=1001 ! named variable for the full Jacobian matrix - integer(i4b),parameter,public :: ixBandMatrix=1002 ! named variable for the band diagonal matrix - - ! define indices describing the first and last layers of the Jacobian to print (for debugging) - integer(i4b),parameter,public :: iJac1=16 ! first layer of the Jacobian to print - integer(i4b),parameter,public :: iJac2=20 ! last layer of the Jacobian to print - - ! define limit checks - real(rkind),parameter,public :: verySmall=tiny(1.0_rkind) ! a very small number - real(rkind),parameter,public :: veryBig=1.e+20_rkind ! a very big number - - ! define algorithmic control parameters - real(rkind),parameter,public :: dx = 1.e-8_rkind ! finite difference increment - - ! define summary information on all data structures - integer(i4b),parameter :: nStruct=14 ! number of data structures - type(struct_info),parameter,public,dimension(nStruct) :: structInfo=(/& - struct_info('time', 'TIME' , maxvarTime ), & ! the time data structure - struct_info('forc', 'FORCE', maxvarForc ), & ! the forcing data structure - struct_info('attr', 'ATTR' , maxvarAttr ), & ! the attribute data structure - struct_info('type', 'TYPE' , maxvarType ), & ! the type data structure - struct_info('id' , 'ID' , maxvarId ), & ! the type data structure - struct_info('mpar', 'PARAM', maxvarMpar ), & ! the model parameter data structure - struct_info('bpar', 'BPAR' , maxvarBpar ), & ! the basin parameter data structure - struct_info('bvar', 'BVAR' , maxvarBvar ), & ! the basin variable data structure - struct_info('indx', 'INDEX', maxvarIndx ), & ! the model index data structure - struct_info('prog', 'PROG', maxvarProg ), & ! the prognostic (state) variable data structure - struct_info('diag', 'DIAG' , maxvarDiag ), & ! the diagnostic variable data structure - struct_info('flux', 'FLUX' , maxvarFlux ), & ! the flux data structure - struct_info('deriv', 'DERIV', maxvarDeriv ), & ! the model derivative data structure - struct_info('lookup', 'LOOKUP', maxvarLookup) /) ! the lookup table data structure - - ! fixed model decisions - logical(lgt) , parameter, public :: overwriteRSMIN=.false. ! flag to overwrite RSMIN - integer(i4b) , parameter, public :: maxSoilLayers=10000 ! Maximum Number of Soil Layers - - ! ---------------------------------------------------------------------------------------------------------------- - ! * part 2: globally constant variables/structures that require initialization - ! ---------------------------------------------------------------------------------------------------------------- + ! data types + USE nrtype + USE netcdf + USE,intrinsic :: ieee_arithmetic ! IEEE arithmetic + USE data_types,only:gru2hru_map ! mapping between the GRUs and HRUs + USE data_types,only:hru2gru_map ! mapping between the GRUs and HRUs + USE data_types,only:model_options ! the model decision structure + USE data_types,only:file_info ! metadata for model forcing datafile + USE data_types,only:par_info ! default parameter values and parameter bounds + USE data_types,only:var_info ! metadata for variables in each model structure + USE data_types,only:flux2state ! extended metadata to define flux-to-state mapping + USE data_types,only:extended_info ! extended metadata for variables in each model structure + USE data_types,only:struct_info ! summary information on all data structures + USE data_types,only:var_i ! vector of integers +#ifdef ACTORS_ACTIVE + USE data_types,only:var_forc ! for Actors + USE data_types,only:dlength ! for Actors + USE data_types,only:ilength ! for Actors + USE data_types,only:init_cond ! for Actors +#endif + ! number of variables in each data structure + USE var_lookup,only:maxvarTime ! time: maximum number variables + USE var_lookup,only:maxvarForc ! forcing data: maximum number variables + USE var_lookup,only:maxvarAttr ! attributes: maximum number variables + USE var_lookup,only:maxvarType ! type index: maximum number variables + USE var_lookup,only:maxvarId ! IDs index: maximum number variables + USE var_lookup,only:maxvarProg ! prognostic variables: maximum number variables + USE var_lookup,only:maxvarDiag ! diagnostic variables: maximum number variables + USE var_lookup,only:maxvarFlux ! model fluxes: maximum number variables + USE var_lookup,only:maxvarDeriv ! model derivatives: maximum number variables + USE var_lookup,only:maxvarIndx ! model indices: maximum number variables + USE var_lookup,only:maxvarMpar ! model parameters: maximum number variables + USE var_lookup,only:maxvarBvar ! basin-average variables: maximum number variables + USE var_lookup,only:maxvarBpar ! basin-average parameters: maximum number variables + USE var_lookup,only:maxvarDecisions ! maximum number of decisions + USE var_lookup,only:maxvarFreq ! maximum number of output files + USE var_lookup,only:maxvarLookup ! maximum number of variables in the lookup + implicit none + private + + ! ---------------------------------------------------------------------------------------------------------------- + ! * part 1: parameters that are fixed across multiple instantiations + ! ---------------------------------------------------------------------------------------------------------------- + + ! define missing values + real(rkind),parameter,public :: quadMissing = nr_quadMissing ! (from nrtype) missing quadruple precision number + real(rkind),parameter,public :: realMissing = nr_realMissing ! (from nrtype) missing double precision number + integer(i4b),parameter,public :: integerMissing = nr_integerMissing ! (from nrtype) missing integer + + ! define run modes + integer(i4b),parameter,public :: iRunModeFull=1 ! named variable defining running mode as full run (all GRUs) + integer(i4b),parameter,public :: iRunModeGRU=2 ! named variable defining running mode as GRU-parallelization run (GRU subset) + integer(i4b),parameter,public :: iRunModeHRU=3 ! named variable defining running mode as single-HRU run (ONE HRU) + + ! define progress modes + integer(i4b),parameter,public :: ixProgress_im=1000 ! named variable to print progress once per month + integer(i4b),parameter,public :: ixProgress_id=1001 ! named variable to print progress once per day + integer(i4b),parameter,public :: ixProgress_ih=1002 ! named variable to print progress once per hour + integer(i4b),parameter,public :: ixProgress_never=1003 ! named variable to print progress never + integer(i4b),parameter,public :: ixProgress_it=1004 ! named variable to print progress every timestep + + ! define restart frequency + integer(i4b),parameter,public :: ixRestart_iy=1000 ! named variable to print a re-start file once per year + integer(i4b),parameter,public :: ixRestart_im=1001 ! named variable to print a re-start file once per month + integer(i4b),parameter,public :: ixRestart_id=1002 ! named variable to print a re-start file once per day + integer(i4b),parameter,public :: ixRestart_end=1003 ! named variable to print a re-start file at the end of a run + integer(i4b),parameter,public :: ixRestart_never=1004 ! named variable to print a re-start file never + + ! define output file frequency + integer(i4b),parameter,public :: noNewFiles=1001 ! no new output files + integer(i4b),parameter,public :: newFileEveryOct1=1002 ! create a new file on Oct 1 every year (start of the USA water year) + + ! define named variables for "yes" and "no" + integer(i4b),parameter,public :: no=0 ! .false. + integer(i4b),parameter,public :: yes=1 ! .true. + + ! define named variables to describe the domain type + integer(i4b),parameter,public :: iname_cas =1000 ! named variable to denote a canopy air space state variable + integer(i4b),parameter,public :: iname_veg =1001 ! named variable to denote a vegetation state variable + integer(i4b),parameter,public :: iname_soil=1002 ! named variable to denote a soil layer + integer(i4b),parameter,public :: iname_snow=1003 ! named variable to denote a snow layer + integer(i4b),parameter,public :: iname_aquifer=1004 ! named variable to denote a snow layer + + ! define named variables to describe the state variable type + integer(i4b),parameter,public :: iname_nrgCanair=2001 ! named variable defining the energy of the canopy air space + integer(i4b),parameter,public :: iname_nrgCanopy=2002 ! named variable defining the energy of the vegetation canopy + integer(i4b),parameter,public :: iname_watCanopy=2003 ! named variable defining the mass of total water on the vegetation canopy + integer(i4b),parameter,public :: iname_liqCanopy=2004 ! named variable defining the mass of liquid water on the vegetation canopy + integer(i4b),parameter,public :: iname_nrgLayer=3001 ! named variable defining the energy state variable for snow+soil layers + integer(i4b),parameter,public :: iname_watLayer=3002 ! named variable defining the total water state variable for snow+soil layers + integer(i4b),parameter,public :: iname_liqLayer=3003 ! named variable defining the liquid water state variable for snow+soil layers + integer(i4b),parameter,public :: iname_matLayer=3004 ! named variable defining the matric head state variable for soil layers + integer(i4b),parameter,public :: iname_lmpLayer=3005 ! named variable defining the liquid matric potential state variable for soil layers + integer(i4b),parameter,public :: iname_watAquifer=3006 ! named variable defining the water storage in the aquifer + + ! define named variables to describe the form and structure of the band-diagonal matrices used in the numerical solver + ! NOTE: This indexing scheme provides the matrix structure expected by lapack and sundials. Specifically, they require kl extra rows for additional storage. + ! Consequently, all indices are offset by kl and the total number of bands for storage is 2*kl+ku+1 instead of kl+ku+1. + integer(i4b),parameter,public :: nRHS=1 ! number of unknown variables on the RHS of the linear system A.X=B + integer(i4b),parameter,public :: ku=3 ! number of super-diagonal bands, ku>=3 to accommodate coupled layer above + integer(i4b),parameter,public :: kl=4 ! number of sub-diagonal bands, kl>=4 to accommodate vegetation + integer(i4b),parameter,public :: ixDiag=kl+ku+1 ! index for the diagonal band + integer(i4b),parameter,public :: nBands=2*kl+ku+1 ! length of the leading dimension of the band diagonal matrix + + ! define named variables for the type of matrix used in the numerical solution. + integer(i4b),parameter,public :: ixFullMatrix=1001 ! named variable for the full Jacobian matrix + integer(i4b),parameter,public :: ixBandMatrix=1002 ! named variable for the band diagonal matrix + + ! define indices describing the first and last layers of the Jacobian to print (for debugging) + integer(i4b),parameter,public :: iJac1=16 ! first layer of the Jacobian to print + integer(i4b),parameter,public :: iJac2=20 ! last layer of the Jacobian to print + + ! define limit checks + real(rkind),parameter,public :: verySmall=tiny(1.0_rkind) ! a very small number + real(rkind),parameter,public :: veryBig=1.e+20_rkind ! a very big number + + ! define algorithmic control parameters + real(rkind),parameter,public :: dx = 1.e-8_rkind ! finite difference increment + + ! define summary information on all data structures + integer(i4b),parameter :: nStruct=14 ! number of data structures + type(struct_info),parameter,public,dimension(nStruct) :: structInfo=(/& + struct_info('time', 'TIME' , maxvarTime ), & ! the time data structure + struct_info('forc', 'FORCE', maxvarForc ), & ! the forcing data structure + struct_info('attr', 'ATTR' , maxvarAttr ), & ! the attribute data structure + struct_info('type', 'TYPE' , maxvarType ), & ! the type data structure + struct_info('id' , 'ID' , maxvarId ), & ! the type data structure + struct_info('mpar', 'PARAM', maxvarMpar ), & ! the model parameter data structure + struct_info('bpar', 'BPAR' , maxvarBpar ), & ! the basin parameter data structure + struct_info('bvar', 'BVAR' , maxvarBvar ), & ! the basin variable data structure + struct_info('indx', 'INDEX', maxvarIndx ), & ! the model index data structure + struct_info('prog', 'PROG', maxvarProg ), & ! the prognostic (state) variable data structure + struct_info('diag', 'DIAG' , maxvarDiag ), & ! the diagnostic variable data structure + struct_info('flux', 'FLUX' , maxvarFlux ), & ! the flux data structure + struct_info('deriv', 'DERIV', maxvarDeriv), & ! the model derivative data structure + struct_info('lookup','LOOKUP',maxvarLookup) /) ! the lookup table data structure + ! fixed model decisions + logical(lgt) , parameter, public :: overwriteRSMIN=.false. ! flag to overwrite RSMIN + integer(i4b) , parameter, public :: maxSoilLayers=10000 ! Maximum Number of Soil Layers + + ! ---------------------------------------------------------------------------------------------------------------- + ! * part 2: globally constant variables/structures that require initialization + ! ---------------------------------------------------------------------------------------------------------------- ! define Not-a-Number (NaN) - real(rkind),save,public :: dNaN - - ! define default parameter values and parameter bounds - type(par_info),save,public :: localParFallback(maxvarMpar) ! local column default parameters - type(par_info),save,public :: basinParFallback(maxvarBpar) ! basin-average default parameters - - ! define vectors of metadata - type(var_info),save,public :: time_meta(maxvarTime) ! model time information - type(var_info),save,public :: forc_meta(maxvarForc) ! model forcing data - type(var_info),save,public :: attr_meta(maxvarAttr) ! local attributes - type(var_info),save,public :: type_meta(maxvarType) ! local classification of veg, soil, etc. - type(var_info),save,public :: id_meta(maxvarId) ! local classification of veg, soil, etc. - type(var_info),save,public :: mpar_meta(maxvarMpar) ! local model parameters for each HRU - type(var_info),save,public :: indx_meta(maxvarIndx) ! local model indices for each HRU - type(var_info),save,public :: prog_meta(maxvarProg) ! local state variables for each HRU - type(var_info),save,public :: diag_meta(maxvarDiag) ! local diagnostic variables for each HRU - type(var_info),save,public :: flux_meta(maxvarFlux) ! local model fluxes for each HRU - type(var_info),save,public :: deriv_meta(maxvarDeriv) ! local model derivatives for each HRU - type(var_info),save,public :: lookup_meta(maxvarLookup) ! local lookup tables for each HRU - type(var_info),save,public :: bpar_meta(maxvarBpar) ! basin parameters for aggregated processes - type(var_info),save,public :: bvar_meta(maxvarBvar) ! basin variables for aggregated processes - - ! ancillary metadata structures - type(flux2state), save,public :: flux2state_orig(maxvarFlux) ! named variables for the states affected by each flux (original) - type(flux2state), save,public :: flux2state_liq(maxvarFlux) ! named variables for the states affected by each flux (liquid water) - type(extended_info),save,public,allocatable :: averageFlux_meta(:) ! timestep-average model fluxes - - ! mapping from original to child structures - integer(i4b),save,public,allocatable :: forcChild_map(:) ! index of the child data structure: stats forc - integer(i4b),save,public,allocatable :: progChild_map(:) ! index of the child data structure: stats prog - integer(i4b),save,public,allocatable :: diagChild_map(:) ! index of the child data structure: stats diag - integer(i4b),save,public,allocatable :: fluxChild_map(:) ! index of the child data structure: stats flux - integer(i4b),save,public,allocatable :: indxChild_map(:) ! index of the child data structure: stats indx - integer(i4b),save,public,allocatable :: bvarChild_map(:) ! index of the child data structure: stats bvar - - ! child metadata structures - type(extended_info),save,public,allocatable :: statForc_meta(:) ! child metadata for stats - type(extended_info),save,public,allocatable :: statProg_meta(:) ! child metadata for stats - type(extended_info),save,public,allocatable :: statDiag_meta(:) ! child metadata for stats - type(extended_info),save,public,allocatable :: statFlux_meta(:) ! child metadata for stats - type(extended_info),save,public,allocatable :: statIndx_meta(:) ! child metadata for stats - type(extended_info),save,public,allocatable :: statBvar_meta(:) ! child metadata for stats - - ! ---------------------------------------------------------------------------------------------------------------- - ! * part 3: run time variables - ! ---------------------------------------------------------------------------------------------------------------- - - ! define the model decisions - type(model_options),save,public :: model_decisions(maxvarDecisions) ! the model decision structure - - ! define metadata for model forcing datafile - type(file_info),save,public,allocatable :: forcFileInfo(:) ! file info for model forcing data - - ! define index variables describing the indices of the first and last HRUs in the forcing file - integer(i4b),save,public :: ixHRUfile_min ! minimum index - integer(i4b),save,public :: ixHRUfile_max ! maximum index - - ! define indices in the forcing data files - integer(i4b),save,public :: iFile=1 ! index of current forcing file from forcing file list - integer(i4b),save,public :: forcingStep=integerMissing ! index of current time step in current forcing file - integer(i4b),save,public :: forcNcid=integerMissing ! netcdf id for current netcdf forcing file - - ! define mapping structures - type(gru2hru_map),allocatable,save,public :: gru_struc(:) ! gru2hru map - type(hru2gru_map),allocatable,save,public :: index_map(:) ! hru2gru map - - ! define variables used for the vegetation phenology - real(rkind),dimension(12), save , public :: greenVegFrac_monthly ! fraction of green vegetation in each month (0-1) - - ! define the model output file - character(len=256),save,public :: fileout='' ! output filename - character(len=256),save,public :: output_fileSuffix='' ! suffix for the output file - - ! define controls on model output - integer(i4b),dimension(maxvarFreq),save,public :: statCounter=0 ! time counter for stats - integer(i4b),dimension(maxvarFreq),save,public :: outputTimeStep=0 ! timestep in output files - logical(lgt),dimension(maxvarFreq),save,public :: resetStats=.true. ! flags to reset statistics - logical(lgt),dimension(maxvarFreq),save,public :: finalizeStats=.false. ! flags to reset statistics - integer(i4b),save,public :: maxLayers ! maximum number of layers - integer(i4b),save,public :: maxSnowLayers ! maximum number of snow layers - - ! define control variables - integer(i4b),save,public :: startGRU ! index of the starting GRU for parallelization run - integer(i4b),save,public :: checkHRU ! index of the HRU for a single HRU run - integer(i4b),save,public :: iRunMode ! define the current running mode - integer(i4b),save,public :: nThreads=1 ! number of threads - integer(i4b),save,public :: ixProgress=ixProgress_id ! define frequency to write progress - integer(i4b),save,public :: ixRestart=ixRestart_never ! define frequency to write restart files - integer(i4b),save,public :: newOutputFile=noNewFiles ! define option for new output files - - ! define common variables - integer(i4b),save,public :: numtim ! number of time steps - integer(i4b),save,public :: nHRUrun ! number of HRUs in the run domain - integer(i4b),save,public :: nGRUrun ! number of GRUs in the run domain - real(rkind),save,public :: data_step ! time step of the data - real(rkind),save,public :: refJulday ! reference time in fractional julian days - real(rkind),save,public :: refJulday_data ! reference time in fractional julian days (data files) - real(rkind),save,public :: fracJulday ! fractional julian days since the start of year - real(rkind),save,public :: dJulianStart ! julian day of start time of simulation - real(rkind),save,public :: dJulianFinsh ! julian day of end time of simulation - real(rkind),save,public :: tmZoneOffsetFracDay ! time zone offset in fractional days - integer(i4b),save,public :: nHRUfile ! number of HRUs in the file - integer(i4b),save,public :: yearLength ! number of days in the current year - integer(i4b),save,public :: urbanVegCategory ! vegetation category for urban areas - logical(lgt),save,public :: doJacobian=.false. ! flag to compute the Jacobian - logical(lgt),save,public :: globalPrintFlag=.false. ! flag to compute the Jacobian - integer(i4b),save,public :: chunksize=1024 ! chunk size for the netcdf read/write - integer(i4b),save,public :: outputPrecision=nf90_double ! variable type - integer(i4b),save,public :: outputCompressionLevel=4 ! output netcdf file deflate level: 0-9. 0 is no compression. - - ! define result from the time calls - integer(i4b),dimension(8),save,public :: startInit,endInit ! date/time for the start and end of the initialization - integer(i4b),dimension(8),save,public :: startSetup,endSetup ! date/time for the start and end of the parameter setup - integer(i4b),dimension(8),save,public :: startRestart,endRestart ! date/time for the start and end to read restart data - integer(i4b),dimension(8),save,public :: startRead,endRead ! date/time for the start and end of the data read - integer(i4b),dimension(8),save,public :: startWrite,endWrite ! date/time for the start and end of the stats/write - integer(i4b),dimension(8),save,public :: startPhysics,endPhysics ! date/time for the start and end of the physics + real(rkind),save,public :: dNaN + + ! define default parameter values and parameter bounds + type(par_info),save,public :: localParFallback(maxvarMpar) ! local column default parameters + type(par_info),save,public :: basinParFallback(maxvarBpar) ! basin-average default parameters + + ! define vectors of metadata + type(var_info),save,public :: time_meta(maxvarTime) ! model time information + type(var_info),save,public :: forc_meta(maxvarForc) ! model forcing data + type(var_info),save,public :: attr_meta(maxvarAttr) ! local attributes + type(var_info),save,public :: type_meta(maxvarType) ! local classification of veg, soil, etc. + type(var_info),save,public :: id_meta(maxvarId) ! local classification of veg, soil, etc. + type(var_info),save,public :: mpar_meta(maxvarMpar) ! local model parameters for each HRU + type(var_info),save,public :: indx_meta(maxvarIndx) ! local model indices for each HRU + type(var_info),save,public :: prog_meta(maxvarProg) ! local state variables for each HRU + type(var_info),save,public :: diag_meta(maxvarDiag) ! local diagnostic variables for each HRU + type(var_info),save,public :: flux_meta(maxvarFlux) ! local model fluxes for each HRU + type(var_info),save,public :: deriv_meta(maxvarDeriv) ! local model derivatives for each HRU + type(var_info),save,public :: lookup_meta(maxvarLookup) ! local lookup tables for each HRU + type(var_info),save,public :: bpar_meta(maxvarBpar) ! basin parameters for aggregated processes + type(var_info),save,public :: bvar_meta(maxvarBvar) ! basin variables for aggregated processes + + ! ancillary metadata structures + type(flux2state), save,public :: flux2state_orig(maxvarFlux) ! named variables for the states affected by each flux (original) + type(flux2state), save,public :: flux2state_liq(maxvarFlux) ! named variables for the states affected by each flux (liquid water) + type(extended_info),save,public,allocatable :: averageFlux_meta(:) ! timestep-average model fluxes + + ! mapping from original to child structures + integer(i4b),save,public,allocatable :: forcChild_map(:) ! index of the child data structure: stats forc + integer(i4b),save,public,allocatable :: progChild_map(:) ! index of the child data structure: stats prog + integer(i4b),save,public,allocatable :: diagChild_map(:) ! index of the child data structure: stats diag + integer(i4b),save,public,allocatable :: fluxChild_map(:) ! index of the child data structure: stats flux + integer(i4b),save,public,allocatable :: indxChild_map(:) ! index of the child data structure: stats indx + integer(i4b),save,public,allocatable :: bvarChild_map(:) ! index of the child data structure: stats bvar + + ! child metadata structures + type(extended_info),save,public,allocatable :: statForc_meta(:) ! child metadata for stats + type(extended_info),save,public,allocatable :: statProg_meta(:) ! child metadata for stats + type(extended_info),save,public,allocatable :: statDiag_meta(:) ! child metadata for stats + type(extended_info),save,public,allocatable :: statFlux_meta(:) ! child metadata for stats + type(extended_info),save,public,allocatable :: statIndx_meta(:) ! child metadata for stats + type(extended_info),save,public,allocatable :: statBvar_meta(:) ! child metadata for stats + + ! ---------------------------------------------------------------------------------------------------------------- + ! * part 3: run time variables + ! ---------------------------------------------------------------------------------------------------------------- + + ! define the model decisions + type(model_options),save,public :: model_decisions(maxvarDecisions) ! the model decision structure + + ! define index variables describing the indices of the first and last HRUs in the forcing file + integer(i4b),save,public :: ixHRUfile_min ! minimum index + integer(i4b),save,public :: ixHRUfile_max ! maximum index + + ! define mapping structures + type(gru2hru_map),allocatable,save,public :: gru_struc(:) ! gru2hru map + type(hru2gru_map),allocatable,save,public :: index_map(:) ! hru2gru map + + ! define variables used for the vegetation phenology + real(rkind),dimension(12),save,public :: greenVegFrac_monthly ! fraction of green vegetation in each month (0-1) + + ! define the model output file + character(len=256),save,public :: fileout='' ! output filename + character(len=256),save,public :: output_fileSuffix='' ! suffix for the output file + + ! define controls on model output + logical(lgt),dimension(maxvarFreq),save,public :: finalizeStats=.false. ! flags to reset statistics + integer(i4b),save,public :: maxLayers ! maximum number of layers + integer(i4b),save,public :: maxSnowLayers ! maximum number of snow layers + + ! define control variables + integer(i4b),save,public :: startGRU ! index of the starting GRU for parallelization run + integer(i4b),save,public :: checkHRU ! index of the HRU for a single HRU run + integer(i4b),save,public :: iRunMode ! define the current running mode + integer(i4b),save,public :: nThreads=1 ! number of threads + integer(i4b),save,public :: ixProgress=ixProgress_id ! define frequency to write progress + integer(i4b),save,public :: ixRestart=ixRestart_never ! define frequency to write restart files + integer(i4b),save,public :: newOutputFile=noNewFiles ! define option for new output files + + ! define common variables + integer(i4b),save,public :: numtim ! number of time steps + integer(i4b),save,public :: nHRUrun ! number of HRUs in the run domain + integer(i4b),save,public :: nGRUrun ! number of GRUs in the run domain + real(rkind),save,public :: data_step ! length of the time_step + real(rkind),save,public :: refJulday ! reference time in fractional julian days + real(rkind),save,public :: refJulday_data ! reference time in fractional julian days (data files) + real(rkind),save,public :: dJulianStart ! julian day of start time of simulation + real(rkind),save,public :: dJulianFinsh ! julian day of end time of simulation + integer(i4b),save,public :: nHRUfile ! number of HRUs in the file + integer(i4b),save,public :: urbanVegCategory ! vegetation category for urban areas + logical(lgt),save,public :: doJacobian=.false. ! flag to compute the Jacobian + logical(lgt),save,public :: globalPrintFlag=.false. ! flag to compute the Jacobian + integer(i4b),save,public :: chunksize=1024 ! chunk size for the netcdf read/write + integer(i4b),save,public :: outputPrecision=nf90_double ! variable type + integer(i4b),save,public :: outputCompressionLevel=4 ! output netcdf file deflate level: 0-9. 0 is no compression. + + ! define result from the time calls + integer(i4b),dimension(8),save,public :: startInit,endInit ! date/time for the start and end of the initialization + integer(i4b),dimension(8),save,public :: startSetup,endSetup ! date/time for the start and end of the parameter setup + integer(i4b),dimension(8),save,public :: startRestart,endRestart ! date/time for the start and end to read restart data + integer(i4b),dimension(8),save,public :: startRead,endRead ! date/time for the start and end of the data read + integer(i4b),dimension(8),save,public :: startWrite,endWrite ! date/time for the start and end of the stats/write + integer(i4b),dimension(8),save,public :: startPhysics,endPhysics ! date/time for the start and end of the physics ! define elapsed time - real(rkind),save,public :: elapsedInit ! elapsed time for the initialization - real(rkind),save,public :: elapsedSetup ! elapsed time for the parameter setup - real(rkind),save,public :: elapsedRestart ! elapsed time to read restart data - real(rkind),save,public :: elapsedRead ! elapsed time for the data read - real(rkind),save,public :: elapsedWrite ! elapsed time for the stats/write - real(rkind),save,public :: elapsedPhysics ! elapsed time for the physics - - ! define ancillary data structures - type(var_i),save,public :: startTime ! start time for the model simulation - type(var_i),save,public :: finshTime ! end time for the model simulation - type(var_i),save,public :: refTime ! reference time for the model simulation - type(var_i),save,public :: oldTime ! time for the previous model time step - - ! output file information - logical(lgt),dimension(maxvarFreq),save,public :: outFreq ! true if the output frequency is desired - integer(i4b),dimension(maxvarFreq),save,public :: ncid ! netcdf output file id - - ! look-up values for the choice of the time zone information (formerly in modelDecisions module) - integer(i4b),parameter,public :: ncTime=1 ! time zone information from NetCDF file (timeOffset = longitude/15. - ncTimeOffset) - integer(i4b),parameter,public :: utcTime=2 ! all times in UTC (timeOffset = longitude/15. hours) - integer(i4b),parameter,public :: localTime=3 ! all times local (timeOffset = 0) - - ! define fixed dimensions - integer(i4b),parameter,public :: nBand=2 ! number of spectral bands - integer(i4b),parameter,public :: nTimeDelay=2000 ! number of time steps in the time delay histogram (default: ~1 season = 24*365/4) - - - ! printing step frequency - integer(i4b),parameter,public :: print_step_freq = 1000 + real(rkind),save,public :: elapsedInit ! elapsed time for the initialization + real(rkind),save,public :: elapsedSetup ! elapsed time for the parameter setup + real(rkind),save,public :: elapsedRestart ! elapsed time to read restart data + real(rkind),save,public :: elapsedRead ! elapsed time for the data read + real(rkind),save,public :: elapsedWrite ! elapsed time for the stats/write + real(rkind),save,public :: elapsedPhysics ! elapsed time for the physics + + ! define ancillary data structures + type(var_i),save,public :: startTime ! start time for the model simulation + type(var_i),save,public :: finshTime ! end time for the model simulation + type(var_i),save,public :: refTime ! reference time for the model simulation + type(var_i),save,public :: oldTime ! time for the previous model time step + + ! output file information + logical(lgt),dimension(maxvarFreq),save,public :: outFreq ! true if the output frequency is desired + integer(i4b),dimension(maxvarFreq),save,public :: ncid ! netcdf output file id + + ! look-up values for the choice of the time zone information (formerly in modelDecisions module) + integer(i4b),parameter,public :: ncTime=1 ! time zone information from NetCDF file (timeOffset = longitude/15. - ncTimeOffset) + integer(i4b),parameter,public :: utcTime=2 ! all times in UTC (timeOffset = longitude/15. hours) + integer(i4b),parameter,public :: localTime=3 ! all times local (timeOffset = 0) + +#ifdef ACTORS_ACTIVE + ! global data structures are managed by FileAccessActor + type(var_forc),allocatable,save,public :: forcingDataStruct(:) ! forcingDataStruct(:)%var(:)%dataFromFile(:,:) + type(dlength),allocatable,save,public :: vecTime(:) + logical(lgt),allocatable,save,public :: failedHRUs(:) ! list of true and false values to indicate if an HRU has failed + type(ilength),allocatable,save,public :: outputTimeStep(:) ! timestep in output files + + ! inital conditions for Actors + type(init_cond),allocatable,save,public :: init_cond_prog(:) ! variable data for initial conditions + type(init_cond),allocatable,save,public :: init_cond_bvar(:) ! variable data for initial conditions +#else + ! define metadata for model forcing datafile non-Actors + type(file_info),save,public,allocatable :: forcFileInfo(:) ! file info for model forcing data + + ! define indices in the forcing data files non-Actors + integer(i4b),save,public :: iFile=1 ! index of current forcing file from forcing file list + integer(i4b),save,public :: forcingStep=integerMissing ! index of current time step in current forcing file + integer(i4b),save,public :: forcNcid=integerMissing ! netcdf id for current netcdf forcing file + + ! define controls on model output non-Actors + integer(i4b),dimension(maxvarFreq),save,public :: statCounter=0 ! time counter for stats + integer(i4b),dimension(maxvarFreq),save,public :: outputTimeStep=0 ! timestep in output files + logical(lgt),dimension(maxvarFreq),save,public :: resetStats=.true. ! flags to reset statistics + + ! define common variables non-Actors + real(rkind),save,public :: fracJulday ! fractional julian days since the start of year + real(rkind),save,public :: tmZoneOffsetFracDay ! time zone offset in fractional days + integer(i4b),save,public :: yearLength ! number of days in the current year +#endif + + ! define fixed dimensions + integer(i4b),parameter,public :: nBand=2 ! number of spectral bands + integer(i4b),parameter,public :: nTimeDelay=2000 ! number of time steps in the time delay histogram (default: ~1 season = 24*365/4) + + ! printing step frequency + integer(i4b),parameter,public :: print_step_freq = 1000 + + character(len=1024),public,save :: fname ! temporary filename + END MODULE globalData diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 49fb99706..3e77c9a9e 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -20,6 +20,9 @@ MODULE var_lookup ! defines named variables used to index array elements +#ifdef ACTORS_ACTIVE + USE, intrinsic :: iso_c_binding +#endif USE nrtype, integerMissing=>nr_integerMissing implicit none private @@ -389,8 +392,6 @@ MODULE var_lookup integer(i4b) :: scalarNewSnowDensity = integerMissing ! density of fresh snow (kg m-3) integer(i4b) :: scalarO2air = integerMissing ! atmospheric o2 concentration (Pa) integer(i4b) :: scalarCO2air = integerMissing ! atmospheric co2 concentration (Pa) - integer(i4b) :: windspd_x = integerMissing ! wind speed at 10 meter height in x-direction (m s-1) - integer(i4b) :: windspd_y = integerMissing ! wind speed at 10 meter height in y-direction (m s-1) ! shortwave radiation integer(i4b) :: scalarCosZenith = integerMissing ! cosine of the solar zenith angle (0-1) integer(i4b) :: scalarFractionDirect = integerMissing ! fraction of direct radiation (0-1) @@ -452,7 +453,7 @@ MODULE var_lookup integer(i4b) :: scalarVGn_m = integerMissing ! van Genuchten "m" parameter (-) integer(i4b) :: scalarKappa = integerMissing ! constant in the freezing curve function (m K-1) integer(i4b) :: scalarVolLatHt_fus = integerMissing ! volumetric latent heat of fusion (J m-3) - ! timing information + ! number of function evaluations integer(i4b) :: numFluxCalls = integerMissing ! number of flux calls (-) integer(i4b) :: wallClockTime = integerMissing ! wall clock time (s) endtype iLook_diag @@ -765,7 +766,11 @@ MODULE var_lookup ! (13) structure for looking up the type of a model variable (this is only needed for backward ! compatability, and should be removed eventually) ! *********************************************************************************************************** +#ifdef ACTORS_ACTIVE + type, public, bind(C) :: iLook_varType +#else type, public :: iLook_varType +#endif integer(i4b) :: scalarv = integerMissing ! scalar variables integer(i4b) :: wLength = integerMissing ! # spectral bands integer(i4b) :: midSnow = integerMissing ! mid-layer snow variables diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index c6adeb671..52c07e1b1 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -104,7 +104,10 @@ subroutine coupled_em(& ! model control hruId, & ! intent(in): hruId dt_init, & ! intent(inout): used to initialize the size of the sub-step + dt_init_factor, & ! intent(in): Used to adjust the length of the timestep in the event of a failure computeVegFlux, & ! intent(inout): flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) + fracJulDay, & ! intent(in): fractional julian days since the start of year + yearLength, & ! intent(in): number of days in the current year ! data structures (input) type_data, & ! intent(in): local classification of soil veg etc. for each HRU attr_data, & ! intent(in): local attributes for each HRU @@ -144,8 +147,13 @@ subroutine coupled_em(& implicit none ! model control +#ifdef ACTORS_ACTIVE + integer(4),intent(in) :: hruId ! hruId +#else integer(8),intent(in) :: hruId ! hruId +#endif real(rkind),intent(inout) :: dt_init ! used to initialize the size of the sub-step + integer(i4b),intent(in) :: dt_init_factor ! Used to adjust the length of the timestep in the event of a failure logical(lgt),intent(inout) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) ! data structures (input) type(var_i),intent(in) :: type_data ! type of vegetation and soil @@ -159,6 +167,8 @@ subroutine coupled_em(& type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + real(rkind),intent(in) :: fracJulday ! fractional julian days since the start of year + integer(i4b),intent(in) :: yearLength ! number of days in the current year ! error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -424,6 +434,9 @@ subroutine coupled_em(& ! compute the exposed LAI and SAI and whether veg is buried by snow call vegPhenlgy(& + ! model control + fracJulDay, & ! intent(in): fractional julian days since the start of year + yearLength, & ! intent(in): number of days in the current year ! input/output: data structures model_decisions, & ! intent(in): model decisions type_data, & ! intent(in): type of vegetation and soil @@ -586,7 +599,7 @@ subroutine coupled_em(& whole_step = maxstep dt_solv = 0._rkind ! length of time step that has been completed (s) dt_solvInner = 0._rkind ! length of time step that has been completed (s) in whole_step subStep - dt_init = min(data_step,whole_step,maxstep_op) ! initial substep length (s) + dt_init = min(data_step,whole_step,maxstep_op) / dt_init_factor ! initial substep length (s) dt_sub = dt_init dtSave = whole_step ! length of whole substep @@ -875,6 +888,7 @@ subroutine coupled_em(& ! handle special case of the step failure ! NOTE: need to revert back to the previous state vector that we were happy with and reduce the time step + ! TODO: ask isn't this what the actors program does without the code block below if(stepFailure)then ! halve whole_step, for more frequent outer loop updates whole_step = dtSave/2._rkind @@ -1332,7 +1346,7 @@ subroutine coupled_em(& iLayer = nSnow+1 if(nsub>50000)then - write(message,'(a,i0)') trim(cmessage)//'number of sub-steps > 50000 for HRU ', hruID + write(message,'(a,i0)') trim(cmessage)//'number of sub-steps > 50000 for HRU ', hruId err=20; return end if diff --git a/build/source/engine/derivforce.f90 b/build/source/engine/derivforce.f90 old mode 100644 new mode 100755 index e0f702e6c..d8a488ed5 --- a/build/source/engine/derivforce.f90 +++ b/build/source/engine/derivforce.f90 @@ -33,7 +33,6 @@ module derivforce_module ! global time information USE globalData,only:refJulday ! reference time (fractional julian days) USE globalData,only:data_step ! length of the data step (s) -USE globalData,only:tmZoneOffsetFracDay ! time zone offset in fractional days ! model decisions USE globalData,only:model_decisions ! model decision structure @@ -49,10 +48,10 @@ module derivforce_module ! look-up values for the choice of snow albedo options USE mDecisions_module,only: & - constDens, & ! Constant new snow density - anderson, & ! Anderson 1976 - hedAndPom, & ! Hedstrom and Pomeroy (1998), expoential increase - pahaut_76 ! Pahaut 1976, wind speed dependent (derived from Col de Porte, French Alps) + constDens, & ! Constant new snow density + anderson, & ! Anderson 1976 + hedAndPom, & ! Hedstrom and Pomeroy (1998), expoential increase + pahaut_76 ! Pahaut 1976, wind speed dependent (derived from Col de Porte, French Alps) ! privacy implicit none @@ -63,56 +62,57 @@ module derivforce_module ! ************************************************************************************************ ! public subroutine derivforce: compute derived forcing data ! ************************************************************************************************ - subroutine derivforce(time_data,forc_data,attr_data,mpar_data,prog_data,diag_data,flux_data,err,message) - USE sunGeomtry_module,only:clrsky_rad ! compute cosine of the solar zenith angle - USE conv_funcs_module,only:vapPress ! compute vapor pressure of air (Pa) - USE conv_funcs_module,only:SPHM2RELHM,RELHM2SPHM,WETBULBTMP ! conversion functions - USE snow_utils_module,only:fracliquid,templiquid ! functions to compute temperature/liquid water - USE time_utils_module,only:compcalday ! convert julian day to calendar date - USE summaFileManager,only: NC_TIME_ZONE ! time zone option from control file + subroutine derivforce(time_data,forc_data,attr_data,mpar_data,prog_data,diag_data,flux_data,tmZoneOffsetFracDay,err,message) + USE sunGeomtry_module,only:clrsky_rad ! compute cosine of the solar zenith angle + USE conv_funcs_module,only:vapPress ! compute vapor pressure of air (Pa) + USE conv_funcs_module,only:SPHM2RELHM,RELHM2SPHM,WETBULBTMP ! conversion functions + USE snow_utils_module,only:fracliquid,templiquid ! functions to compute temperature/liquid water + USE time_utils_module,only:compcalday ! convert julian day to calendar date + USE summaFileManager,only: NC_TIME_ZONE ! time zone option from control file ! compute derived forcing data variables implicit none - ! input variables - integer(i4b), intent(in) :: time_data(:) ! vector of time data for a given time step - real(rkind), intent(inout) :: forc_data(:) ! vector of forcing data for a given time step - real(rkind), intent(in) :: attr_data(:) ! vector of model attributes - type(var_dlength),intent(in) :: mpar_data ! vector of model parameters - type(var_dlength),intent(in) :: prog_data ! data structure of model prognostic variables for a local HRU + ! input variable + integer(i4b),intent(in) :: time_data(:) ! vector of time data for a given time step + real(rkind),intent(inout) :: forc_data(:) ! vector of forcing data for a given time step + real(rkind),intent(in) :: attr_data(:) ! vector of model attributes + type(var_dlength),intent(in) :: mpar_data ! vector of model parameters + type(var_dlength),intent(in) :: prog_data ! data structure of model prognostic variables for a local HRU ! output variables - type(var_dlength),intent(inout) :: diag_data ! data structure of model diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! data structure of model fluxes for a local HRU - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + type(var_dlength),intent(inout) :: diag_data ! data structure of model diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! data structure of model fluxes for a local HRU + real(rkind),intent(inout) :: tmZoneOffsetFracDay + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! local time - integer(i4b) :: jyyy,jm,jd ! year, month, day - integer(i4b) :: jh,jmin ! hour, minute - real(rkind) :: dsec ! double precision seconds (not used) - real(rkind) :: timeOffset ! time offset from Grenwich (days) - real(rkind) :: julianTime ! local julian time + integer(i4b) :: jyyy,jm,jd ! year, month, day + integer(i4b) :: jh,jmin ! hour, minute + real(rkind) :: dsec ! double precision seconds (not used) + real(rkind) :: timeOffset ! time offset from Grenwich (days) + real(rkind) :: julianTime ! local julian time ! cosine of the solar zenith angle - real(rkind) :: ahour ! hour at start of time step - real(rkind) :: dataStep ! data step (hours) - real(rkind) :: slope ! HRU terrain slope (degrees) - real(rkind) :: azimuth ! HRU terrain azimuth (degrees) - real(rkind) :: hri ! average radiation index over time step DT + real(rkind) :: ahour ! hour at start of time step + real(rkind) :: dataStep ! data step (hours) + real(rkind),parameter :: slope ! terrain slope (assume flat) + real(rkind),parameter :: azimuth ! terrain azimuth (assume zero) + real(rkind) :: hri ! average radiation index over time step DT ! general local variables - character(len=256) :: cmessage ! error message for downwind routine - integer(i4b),parameter :: nBands=2 ! number of spectral bands - real(rkind),parameter :: valueMissing=-9999._rkind ! missing value - real(rkind),parameter :: co2Factor=355.e-6_rkind ! empirical factor to obtain partial pressure of co2 - real(rkind),parameter :: o2Factor=0.209_rkind ! empirical factor to obtain partial pressure of o2 - real(rkind),parameter :: minMeasHeight=1._rkind ! minimum measurement height (m) - real(rkind) :: relhum ! relative humidity (-) - real(rkind) :: fracrain ! fraction of precipitation that falls as rain - real(rkind) :: maxFrozenSnowTemp ! maximum temperature of snow when the snow is predominantely frozen (K) - real(rkind),parameter :: unfrozenLiq=0.01_rkind ! unfrozen liquid water used to compute maxFrozenSnowTemp (-) - real(rkind),parameter :: eps=epsilon(fracrain) ! a number that is almost negligible - real(rkind) :: Tmin,Tmax ! minimum and maximum wet bulb temperature in the time step (K) - real(rkind),parameter :: pomNewSnowDenMax=150._rkind ! Upper limit for new snow density limit in Hedstrom and Pomeroy 1998. 150 was used because at was the highest observed density at air temperatures used in this study. See Figure 4 of Hedstrom and Pomeroy (1998). - real(rkind),parameter :: andersonWarmDenLimit=2._rkind ! Upper air temperature limit in Anderson (1976) new snow density (C) - real(rkind),parameter :: andersonColdDenLimit=15._rkind! Lower air temperature limit in Anderson (1976) new snow density (C) - real(rkind),parameter :: andersonDenScal=1.5_rkind ! Scalar parameter in Anderson (1976) new snow density function (-) - real(rkind),parameter :: pahautDenWindScal=0.5_rkind ! Scalar parameter for wind impacts on density using Pahaut (1976) function (-) + character(len=256) :: cmessage ! error message for downwind routine + integer(i4b),parameter :: nBands=2 ! number of spectral bands + real(rkind),parameter :: valueMissing=-9999._rkind ! missing value + real(rkind),parameter :: co2Factor=355.e-6_rkind ! empirical factor to obtain partial pressure of co2 + real(rkind),parameter :: o2Factor=0.209_rkind ! empirical factor to obtain partial pressure of o2 + real(rkind),parameter :: minMeasHeight=1._rkind ! minimum measurement height (m) + real(rkind) :: relhum ! relative humidity (-) + real(rkind) :: fracrain ! fraction of precipitation that falls as rain + real(rkind) :: maxFrozenSnowTemp ! maximum temperature of snow when the snow is predominantely frozen (K) + real(rkind),parameter :: unfrozenLiq=0.01_rkind ! unfrozen liquid water used to compute maxFrozenSnowTemp (-) + real(rkind),parameter :: eps=epsilon(fracrain) ! a number that is almost negligible + real(rkind) :: Tmin,Tmax ! minimum and maximum wet bulb temperature in the time step (K) + real(rkind),parameter :: pomNewSnowDenMax=150._rkind ! Upper limit for new snow density limit in Hedstrom and Pomeroy 1998. 150 was used because at was the highest observed density at air temperatures used in this study. See Figure 4 of Hedstrom and Pomeroy (1998). + real(rkind),parameter :: andersonWarmDenLimit=2._rkind ! Upper air temperature limit in Anderson (1976) new snow density (C) + real(rkind),parameter :: andersonColdDenLimit=15._rkind! Lower air temperature limit in Anderson (1976) new snow density (C) + real(rkind),parameter :: andersonDenScal=1.5_rkind ! Scalar parameter in Anderson (1976) new snow density function (-) + real(rkind),parameter :: pahautDenWindScal=0.5_rkind ! Scalar parameter for wind impacts on density using Pahaut (1976) function (-) ! ************************************************************************************************ ! associate local variables with the information in the data structures associate(& @@ -134,7 +134,6 @@ subroutine derivforce(time_data,forc_data,attr_data,mpar_data,prog_data,diag_dat newSnowDenMultWind => mpar_data%var(iLookPARAM%newSnowDenMultWind)%dat(1) , & ! Pahaut 1976, multiplier for new snow density applied to wind speed (kg m-7/2 s-1/2) newSnowDenMultAnd => mpar_data%var(iLookPARAM%newSnowDenMultAnd)%dat(1) , & ! Anderson 1976, multiplier for new snow density for Anderson function (K-1) newSnowDenBase => mpar_data%var(iLookPARAM%newSnowDenBase)%dat(1) , & ! Anderson 1976, base value that is rasied to the (3/2) power (K) - heightCanopyTop => mpar_data%var(iLookPARAM%heightCanopyTop)%dat(1) , & ! height of the top of the canopy layer (m) ! radiation geometry variables iyyy => time_data(iLookTIME%iyyy) , & ! year im => time_data(iLookTIME%im) , & ! month @@ -150,6 +149,7 @@ subroutine derivforce(time_data,forc_data,attr_data,mpar_data,prog_data,diag_dat mHeight => attr_data(iLookATTR%mHeight) , & ! latitude (degrees north) adjMeasHeight => diag_data%var(iLookDIAG%scalarAdjMeasHeight)%dat(1) , & ! adjusted measurement height (m) scalarSnowDepth => prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) , & ! snow depth on the ground surface (m) + heightCanopyTop => mpar_data%var(iLookPARAM%heightCanopyTop)%dat(1) , & ! height of the top of the canopy layer (m) ! model time secondsSinceRefTime => forc_data(iLookFORCE%time) , & ! time = seconds since reference time ! model forcing data @@ -162,8 +162,6 @@ subroutine derivforce(time_data,forc_data,attr_data,mpar_data,prog_data,diag_dat ! derived model forcing data scalarO2air => diag_data%var(iLookDIAG%scalarO2air)%dat(1) , & ! atmospheric o2 concentration (Pa) scalarCO2air => diag_data%var(iLookDIAG%scalarCO2air)%dat(1) , & ! atmospheric co2 concentration (Pa) - windspd_x => diag_data%var(iLookDIAG%windspd_x)%dat(1) , & ! wind speed at 10 meter height in x-direction (m s-1) - windspd_y => diag_data%var(iLookDIAG%windspd_y)%dat(1) , & ! wind speed at 10 meter height in y-direction (m s-1) ! radiation variables scalarFractionDirect => diag_data%var(iLookDIAG%scalarFractionDirect)%dat(1) , & ! fraction of direct radiation (0-1) spectralIncomingDirect => flux_data%var(iLookFLUX%spectralIncomingDirect)%dat , & ! downwelling direct shortwave radiation for each waveband (W m-2) @@ -227,10 +225,10 @@ subroutine derivforce(time_data,forc_data,attr_data,mpar_data,prog_data,diag_dat end select ! identifying option tmZoneInfo ! constrain timeOffset so that it is in the [-0.5, 0.5] range - if(timeOffset < -0.5)then - timeOffset = timeOffset + 1 - else if(timeOffset > 0.5)then - timeOffset = timeOffset - 1 + if(timeOffset<-0.5)then + timeOffset = timeOffset+1 + else if(timeOffset>0.5)then + timeOffset = timeOffset-1 endif ! compute the local time @@ -251,7 +249,7 @@ subroutine derivforce(time_data,forc_data,attr_data,mpar_data,prog_data,diag_dat azimuth = 0._rkind ! if aspect is not an input attribute, slope & azimuth = zero (flat Earth) slope = 0._rkind else - azimuth = aspect ! in degrees + azimuth = aspect ! in degrees slope = atan(abs(tan_slope))*180._rkind/PI_D ! convert from m/m to degrees endif @@ -259,8 +257,6 @@ subroutine derivforce(time_data,forc_data,attr_data,mpar_data,prog_data,diag_dat call clrsky_rad(jm,jd,ahour,dataStep, & ! intent(in): time variables slope,azimuth,latitude, & ! intent(in): location variables hri,cosZenith) ! intent(out): cosine of the solar zenith angle - !write(*,'(a,1x,4(i2,1x),5(f9.3,1x))') 'im,id,ih,imin,ahour,dataStep,azimuth,slope,cosZenith = ', & - ! im,id,ih,imin,ahour,dataStep,azimuth,slope,cosZenith ! ensure solar radiation is non-negative if(SWRadAtm < 0._rkind) SWRadAtm = 0._rkind @@ -277,12 +273,8 @@ subroutine derivforce(time_data,forc_data,attr_data,mpar_data,prog_data,diag_dat spectralIncomingDiffuse(1) = SWRadAtm*(1._rkind - scalarFractionDirect)*Frad_vis ! (diffuse vis) spectralIncomingDiffuse(2) = SWRadAtm*(1._rkind - scalarFractionDirect)*(1._rkind - Frad_vis) ! (diffuse nir) - !print*,'Frad_direct,scalarFractionDirect,directScale,SWRadAtm,Frad_vis,spectralIncomingDirect: ', & - ! frad_direct,scalarFractionDirect,directScale,SWRadAtm,Frad_vis,spectralIncomingDirect - ! ensure wind speed is above a prescribed minimum value if(windspd < minwind) windspd=minwind - ! compute relative humidity (-) relhum = SPHM2RELHM(spechum, airpres, airtemp) ! if relative humidity exceeds saturation, then set relative and specific humidity to saturation diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 old mode 100644 new mode 100755 index 60f8c6b0e..233b80205 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -19,6 +19,9 @@ ! along with this program. If not, see . module mDecisions_module +#ifdef ACTORS_ACTIVE +USE, intrinsic :: iso_c_binding +#endif USE nrtype USE var_lookup, only: maxvarDecisions ! maximum number of decisions implicit none @@ -147,8 +150,8 @@ module mDecisions_module integer(i4b),parameter,public :: meltDripUnload = 321 ! Hedstrom and Pomeroy (1998), Storck et al 2002 (snowUnloadingCoeff & ratioDrip2Unloading) integer(i4b),parameter,public :: windUnload = 322 ! Roesch et al 2001, formulate unloading based on wind and temperature ! look-up values for the choice of energy equation -integer(i4b),parameter,public :: enthalpyFD = 323 ! enthalpyFD -integer(i4b),parameter,public :: closedForm = 324 ! closedForm +integer(i4b),parameter,public :: enthalpyFD = 323 ! enthalpyFD +integer(i4b),parameter,public :: closedForm = 324 ! closedForm ! ----------------------------------------------------------------------------------------------------------- contains @@ -156,7 +159,11 @@ module mDecisions_module ! ************************************************************************************************ ! public subroutine mDecisions: save model decisions as named integers ! ************************************************************************************************ +#ifdef ACTORS_ACTIVE +subroutine mDecisions(num_steps,err) bind(C, name='mDecisions') +#else subroutine mDecisions(err,message) +#endif ! model time structures USE multiconst,only:secprday ! number of seconds in a day USE var_lookup,only:iLookTIME ! named variables that identify indices in the time structures @@ -184,12 +191,19 @@ subroutine mDecisions(err,message) USE summaFileManager,only: SIM_START_TM, SIM_END_TM ! time info from control file module implicit none - ! define output + ! define output, depends on if using Actors +#ifdef ACTORS_ACTIVE + integer(c_int),intent(out) :: num_steps ! number of time steps in the simulation + integer(c_int),intent(out) :: err ! error code + character(*) :: message ! error message +#else + integer(i4b) :: num_steps ! number of time steps in the simulation integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message +#endif ! define local variables character(len=256) :: cmessage ! error message for downwind routine - real(rkind) :: dsec,dsec_tz ! second + real(rkind) :: dsec,dsec_tz ! second ! initialize error control err=0; message='mDecisions/' @@ -287,9 +301,11 @@ subroutine mDecisions(err,message) oldTime%var(:) = startTime%var(:) ! compute the number of time steps - numtim = nint( (dJulianFinsh - dJulianStart)*secprday/data_step ) + 1 + num_steps = nint( (dJulianFinsh - dJulianStart)*secprday/data_step ) + 1 + numtim = num_steps +#ifndef ACTORS_ACTIVE write(*,'(a,1x,i10)') 'number of time steps = ', numtim - +#endif ! set Noah-MP options DVEG=3 ! option for dynamic vegetation diff --git a/build/source/engine/run_oneHRU.f90 b/build/source/engine/run_oneHRU.f90 index a8119e4b9..4c9c4d5cd 100644 --- a/build/source/engine/run_oneHRU.f90 +++ b/build/source/engine/run_oneHRU.f90 @@ -53,16 +53,17 @@ module run_oneHRU_module USE globalData,only:model_decisions ! model decision structure USE var_lookup,only:iLookDECISIONS ! look-up values for model decisions +! these are needed because we cannot access them in modules locally if we might use those modules with Actors +USE globalData,only:fracJulday ! fractional julian days since the start of year +USE globalData,only:yearLength ! number of days in the current year +USE globalData,only:tmZoneOffsetFracDay ! time zone offset in fractional days + ! provide access to the named variables that describe model decisions -USE mDecisions_module,only:& ! look-up values for LAI decisions - monthlyTable,& ! LAI/SAI taken directly from a monthly table for different vegetation classes - specified ! LAI/SAI computed from green vegetation fraction and winterSAI and summerLAI parameters +USE mDecisions_module,only: & ! look-up values for LAI decisions + monthlyTable,& ! LAI/SAI taken directly from a monthly table for different vegetation classes + specified ! LAI/SAI computed from green vegetation fraction and winterSAI and summerLAI parameters -! ----------------------------------------------------------------------------------------------------------------------------------- -! ----------------------------------------------------------------------------------------------------------------------------------- ! ----- global variables that are modified ------------------------------------------------------------------------------------------ -! ----------------------------------------------------------------------------------------------------------------------------------- -! ----------------------------------------------------------------------------------------------------------------------------------- ! Noah-MP parameters USE NOAHMP_VEG_PARAMETERS,only:SAIM,LAIM ! 2-d tables for stem area index and leaf area index (vegType,month) @@ -193,14 +194,15 @@ subroutine run_oneHRU(& ! ----- hru forcing ---------------------------------------------------------------------------------------------------- ! compute derived forcing variables - call derivforce(timeVec, & ! vector of time information - forcData%var, & ! vector of model forcing data - attrData%var, & ! vector of model attributes - mparData, & ! data structure of model parameters - progData, & ! data structure of model prognostic variables - diagData, & ! data structure of model diagnostic variables - fluxData, & ! data structure of model fluxes - err,cmessage) ! error control + call derivforce(timeVec, & ! vector of time information + forcData%var, & ! vector of model forcing data + attrData%var, & ! vector of model attributes + mparData, & ! data structure of model parameters + progData, & ! data structure of model prognostic variables + diagData, & ! data structure of model diagnostic variables + fluxData, & ! data structure of model fluxes + tmZoneOffsetFracDay & ! time zone offset in fractional days + err,cmessage) ! error control if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif ! ----- run the model -------------------------------------------------------------------------------------------------- @@ -213,7 +215,10 @@ subroutine run_oneHRU(& ! model control hruId, & ! intent(in): hruId dt_init, & ! intent(inout): initial time step + 1._rkind, & ! intent(in): used to adjust the length of the timestep with failure in Actors (non-Actors here, always 1) computeVegFlux, & ! intent(inout): flag to indicate if we are computing fluxes over vegetation + fracJulDay, & ! intent(in): fractional julian days since the start of year + yearLength, & ! intent(in): number of days in the current year ! data structures (input) typeData, & ! intent(in): local classification of soil veg etc. for each HRU attrData, & ! intent(in): local attributes for each HRU @@ -227,7 +232,7 @@ subroutine run_oneHRU(& diagData, & ! intent(inout): model diagnostic variables for a local HRU fluxData, & ! intent(inout): model fluxes for a local HRU ! error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif ! update the number of layers diff --git a/build/source/engine/vegPhenlgy.f90 b/build/source/engine/vegPhenlgy.f90 old mode 100644 new mode 100755 index 7d3cd3dc2..fe74b8a10 --- a/build/source/engine/vegPhenlgy.f90 +++ b/build/source/engine/vegPhenlgy.f90 @@ -25,8 +25,6 @@ module vegPhenlgy_module ! global variables USE globalData,only:urbanVegCategory ! vegetation category for urban areas -USE globalData,only:fracJulday ! fractional julian days since the start of year -USE globalData,only:yearLength ! number of days in the current year ! provide access to the derived types to define the data structures USE data_types,only:& @@ -67,6 +65,9 @@ module vegPhenlgy_module ! public subroutine vegPhenlgy: compute vegetation phenology ! ************************************************************************************************ subroutine vegPhenlgy(& + ! model control + fracJulDay, & ! intent(in): fractional julian days since the start of year + yearLength, & ! intent(in): number of days in the current year ! input/output: data structures model_decisions, & ! intent(in): model decisions type_data, & ! intent(in): type of vegetation and soil @@ -79,6 +80,7 @@ subroutine vegPhenlgy(& canopyDepth, & ! intent(out): canopy depth (m) exposedVAI, & ! intent(out): exposed vegetation area index (LAI + SAI) err,message) ! intent(out): error control + ! ------------------------------------------------------------------------------------------------- ! modules USE NOAHMP_ROUTINES,only:phenology ! determine vegetation phenology @@ -93,14 +95,16 @@ subroutine vegPhenlgy(& type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU ! output logical(lgt),intent(out) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) - real(rkind),intent(out) :: canopyDepth ! canopy depth (m) - real(rkind),intent(out) :: exposedVAI ! exposed vegetation area index (LAI + SAI) + real(rkind),intent(out) :: canopyDepth ! canopy depth (m) + real(rkind),intent(out) :: exposedVAI ! exposed vegetation area index (LAI + SAI) + real(rkind),intent(in) :: fracJulday ! fractional julian days since the start of year + integer(i4b),intent(in) :: yearLength ! number of days in the current year integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------- ! local - real(rkind) :: notUsed_heightCanopyTop ! height of the top of the canopy layer (m) - real(rkind) :: heightAboveSnow ! height top of canopy is above the snow surface (m) + real(rkind) :: notUsed_heightCanopyTop ! height of the top of the canopy layer (m) + real(rkind) :: heightAboveSnow ! height top of canopy is above the snow surface (m) ! initialize error control err=0; message="vegPhenlgy/" ! ---------------------------------------------------------------------------------------------------------------------------------- From 5db7aa21db570d79481675800f33ff0ec8b6545d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 4 May 2023 00:04:28 +0900 Subject: [PATCH 0704/1472] fixing docs --- docs/sundials_bmi/flags_params_sundials.txt | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/docs/sundials_bmi/flags_params_sundials.txt b/docs/sundials_bmi/flags_params_sundials.txt index 30dbebb11..65c79b396 100644 --- a/docs/sundials_bmi/flags_params_sundials.txt +++ b/docs/sundials_bmi/flags_params_sundials.txt @@ -5,15 +5,11 @@ In energy equation, the heat capacity is computed using either the analytical (c All SUMMA-SUNDIALS files are in build/source/engine. The important ones are as following: -systemSolvSundials.f90: contains public subroutine systemSolvSundials which runs the coupled energy-mass model for one timestep +systemSolvSundials.f90: contains public subroutine systemSolvSundials which runs the coupled energy-mass model for one timestep. -summaSolveSundialsIDA.f90: contains public subroutine summaSolveSundialsIDA which solves the differential equation system F(y,y') = 0 by IDA (y is the state vector) and private subroutines setInitialCondition and setSolverParams. Subroutine setSolverParams can be used to to set parameters (maximum order, number of nonlinear iteration , etc) in IDA solver +summaSolveSundialsIDA.f90: contains public subroutine summaSolveSundialsIDA which solves the differential equation system F(y,y') = 0 by IDA (y is the state vector) and private subroutines setInitialCondition and setSolverParams. Subroutine setSolverParams can be used to to set parameters (maximum order, number of nonlinear iteration , etc) in IDA solver. -computResidSundials.f90: contains public function computResidSundials which is the interface wrapper for computing the residual vector F(t,y,y') required for IDA solver +eval8summaSundials.f90: contains public subroutine eval8summaSundials which computes the residual vector F(t,y,y') mainly by calling varExtract, updateVarsSundials, computFlux, and computResidSundials. We also switch between different forms of the energy equation in this subroutine. It also contains public function eval8summa4IDA which is the interface wrapper for computing the residual vector required for the IDA solver. -eval8summaSundials.f90: contains public subroutine eval8summaSundials which computes the residual vector mainly by calling varExtract, updateVarsSundials, computFlux,and computResidSundials. We also switch between different forms of the energy equation in this subroutine. - -computJacobSundials.f90: contains public function computJacobSundials which is the interface wrapper for computing the Jacobian matrix dF/dy + c dF/dy' for IDA solver - -eval8JacDAE.f90: contains public subroutine eval8JacDAE which computes the residual vector and the Jacobian matrix mainly by calling computJacobSundials +computJacobSundials.f90: contains public subroutine computJacobSundials which computes the Jacobian matrix dF/dy + c dF/dy'. It also contains the public function computJacob4IDA which is the interface wrapper for computing the Jacobian matrix required for the IDA solver. From ac3989d4335f05f2906bb6aff23ca71f443a6f94 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 4 May 2023 16:00:58 +0900 Subject: [PATCH 0705/1472] start of CMakeList --- build/cmake/CMakeLists.txt | 459 +++++++++++++++++++++++++++++ build/cmake/build.cluster.bash | 13 +- build/cmake/build.mac.bash | 2 +- build/source/engine/derivforce.f90 | 13 +- build/source/engine/varSubstep.f90 | 2 +- 5 files changed, 480 insertions(+), 9 deletions(-) create mode 100644 build/cmake/CMakeLists.txt diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt new file mode 100644 index 000000000..515f779e9 --- /dev/null +++ b/build/cmake/CMakeLists.txt @@ -0,0 +1,459 @@ +cmake_minimum_required(VERSION 3.10 FATAL_ERROR) +enable_language(C Fortran) +SET (CMAKE_Fortran_COMPILER gfortran) + + +# -------------------------------------------------------------------------------------------- +# When compiling Summa each configuration can be compiled in a Cluster or Debug mode, which is +# set by the CMAKE_BUILD_TYPES variables. The default is BE, release mode no cluster no Sundials +# no Actors no NexGen. +# Other options include how to drive the code, NexGen (with BMI) or Actors (without BMI) framework +# or no extra framework (without BMI). +# Note, Sundials, NexGen, and Actors will all require extra libraries to be downloaded and +# compiled. + +# Set default configuration type to Release +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE BE) +endif() + +# Add options for build type +set(CMAKE_CONFIGURATION_TYPES BE BE_Debug BE_Cluster BE_Cluster_Debug + BE_NexGen BE_NexGen_Debug BE_NexGen_Cluster BE_NexGen_Cluster_Debug + BE_Actors BE_Actors_Debug BE_Actors_Cluster BE_Actors_Cluster_Debug + Kinsol Kinsol_Debug Kinsol_Cluster Kinsol_Cluster_Debug + Kinsol_NexGen Kinsol_NexGen_Debug Kinsol_NexGen_Cluster Kinsol_NexGen_Cluster_Debug + Kinsol_Actors Kinsol_Actors_Debug Kinsol_Actors_Cluster Kinsol_Actors_Cluster_Debug + IDA IDA_Debug IDA_Cluster IDA_Cluster_Debug + IDA_NexGen IDA_NexGen_Debug IDA_NexGen_Cluster IDA_NexGen_Cluster_Debug + IDA_Actors IDA_Actors_Debug IDA_Actors_Cluster IDA_Actors_Cluster_Debug) +message("\nSelected Bulid Type: ${CMAKE_BUILD_TYPE}\n") + +if(CMAKE_BUILD_TYPE MATCHES Kinsol OR CMAKE_BUILD_TYPE MATCHES IDA) + add_compile_definitions(SUNDIALS_ACTIVE) +endif() + +if(CMAKE_BUILD_TYPE MATCHES NexGen) + message("\nUsing NexGen Framework, should have been installed previously\n") + add_compile_definitions(NGEN_ACTIVE BMI_ACTIVE NGEN_FORCING_ACTIVE NGEN_OUTPUT_ACTIVE) + + project(summabmi VERSION 1.0.0 DESCRIPTION "Summa-Sundials-BE BMI Module Shared Library") + + # NexGen needs to have CMakeLists one directory above summa submodule GIT, at ${NexGen GIT directory}/extern/summa + set(F_MASTER ./summa/) # directory of summa source code + set(PARENT_DIR ${F_MASTER}) # directory of summa source code + + # Get the iso_c_fortran binding module to build as part of this build + add_subdirectory(../iso_c_fortran_bmi ${CMAKE_BINARY_DIR}/iso_c_bmi) + + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../../cmake/") + set( CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran) + + #### Add variables for individual libraries that are used within the *.pc.in files + set(SUMMA_LIB_NAME_CMAKE summabmi) + set(SUMMA_LIB_DESC_CMAKE "Summa-Sundials BMI Module Shared Library") + +else() + set(F_MASTER ../../) # directory of summa source code + + if(CMAKE_BUILD_TYPE MATCHES Actors) + message("\nUsing Actors Framework, should have been installed previously\n") + add_compile_definitions(ACTORS_ACTIVE) + + project(summaactors DESCRIPTION "Summa-Sundials-BE Actors") + + include(FortranCInterface) + FortranCInterface_VERIFY(CXX) + + # CMakeLists in ${summa-actors GIT directory}/build/summa/build/cmake + set(PARENT_DIR ${F_MASTER}../../) # directory of summa actors source code + set(EXEC_DIR ${PARENT_DIR}../bin) + + else() + project(summa DESCRIPTION "Summa-Sundials-BE") + + # CMakeLists in ${summa-actors GIT directory}/build/cmake + set(PARENT_DIR ${F_MASTER}) # directory of summa source code + set(EXEC_DIR ${F_MASTER}/bin) + + endif() + + # set the output directory for executables + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXEC_DIR}) + +endif() + +######### SET THE PATHS TO THE LIBRARIES AND INCLUDE FILES ######### +############################################################################# + +# Set compiler flags +if(CMAKE_BUILD_TYPE MATCHES Debug) + message("\nSetting Debug Options\n") + add_definitions(-DDEBUG) + set(FLAGS_NOAH -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) + set(FLAGS_ALL -g -O0 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) + set(FLAGS_CXX -g -O0 -Wfatal-errors -std=c++17) +else() + message("\nSetting Release Options\n") + set(FLAGS_NOAH -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) + set(FLAGS_ALL -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) + set(FLAGS_CXX -O3 -Wfatal-errors -std=c++17) +endif() + +# Find Sundials if exists +set(DIR_SUNDIALS "") +foreach(dir IN ITEMS + "${F_MASTER}../sundials/instdir" + "${PARENT_DIR}../sundials/instdir" + "../../../../SummaSundials/sundials/instdir" + if(EXISTS ${dir}) + set(DIR_SUNDIALS ${dir}) + break() + endif() +endforeach() + +# Set libraries for all builds +if(CMAKE_BUILD_TYPE MATCHES Cluster) + + # MKL needs BLA_VENDOR set with cmake + set(BLA_VENDOR OpenBLAS) + + # Packages that are required + find_package(NetCDF REQUIRED) + find_package(LAPACK REQUIRED) + + # Set include directories + set(INCLUDES $ENV{EBROOTNETCDFMINFORTRAN}/include ${netCDF_INCLUDES} ${LAPACK_INCLUDES}) + set(LIBRARIES SUMMA_NOAHMP ${netCDF_LIBRARIES} ${LAPACK_LIBRARIES} -lnetcdff -lopenblas) + + if(SUNDIALS_ACTIVE) + link_directories(${DIR_SUNDIALS}/lib64) + set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib64") + set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran) + endif() + +else() + + set(SDKROOT "$(xcrun --show-sdk-path)") + + link_directories(/opt/local/lib) + set(INCLUDES /opt/local/include /opt/local/lib) + set(LIBRARIES SUMMA_NOAHMP -llapack -lgfortran -lnetcdff -lnetcdf) + + if(SUNDIALS_ACTIVE) + link_directories(${DIR_SUNDIALS}/lib) + set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib") + set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran) + endif() + +endif() + +if(CMAKE_BUILD_TYPE MATCHES IDA) + message("\nUsing SUNDIALS IDA libraries, should have been installed previously\n") + set(LIB_SUNDIALS -lsundials_fida_mod -lsundials_fnvecmanyvector_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) + +elseif(CMAKE_BUILD_TYPE MATCHES Kinsol) + message("\nUsing SUNDIALS Kinsol libraries, should have been installed previously\n") + set(LIB_SUNDIALS -lsundials_fkinsol_mod -lsundials_fnvecserial_mod -lsundials_fsunnonlinsolnewton_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) +endif() + +# Actors is on Cluster, if using on personal computer might need to link directory-- not working on Mac right now +if(ACTORS_ACTIVE) + find_package(CAF REQUIRED) + set(INC_ACTORS ${CAF_INCLUDES} ${PARENT_DIR}/build/includes/global ${PARENT_DIR}/build/includes/summa_actor ${PARENT_DIR}/build/includes/gru_actor ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) + set(LIB_ACTORS ${CAF_LIBRARIES} -lcaf_core -lcaf_io) +endif() + + +message("\nBuilding SUMMA\n") + + +#compile_with_bmi(${F_MASTER}, ${DIR_SUNDIALS}, ${INCLUDES}, ${LIBRARIES}, ${INC_SUNDIALS}, ${LIB_SUNDIALS}, ${FLAGS_ALL}, ${FLAGS_NOAH}) +#function(compile_with_bmi F_MASTER, DIR_SUNDIALS, INCLUDES, LIBRARIES, INC_SUNDIALS, LIB_SUNDIALS, FLAGS_ALL, FLAGS_NOAH) + +#======================================================================== +# PART 1: Define directory paths +#======================================================================== + +# Define directories that contains source code + set(DRIVER_DIR ${F_MASTER}/build/source/driver) + set(HOOKUP_DIR ${F_MASTER}/build/source/hookup) + set(NETCDF_DIR ${F_MASTER}/build/source/netcdf) + set(DSHARE_DIR ${F_MASTER}/build/source/dshare) + set(NUMREC_DIR ${F_MASTER}/build/source/numrec) + set(NOAHMP_DIR ${F_MASTER}/build/source/noah-mp) + set(ENGINE_DIR ${F_MASTER}/build/source/engine) + +# Define directories for source files that might be replaced by actors (identical if not using actors) + set(SUB_DRIVER_DIR ${PARENT_DIR}/build/source/driver) + set(SUB_DSHARE_DIR ${PARENT_DIR}/build/source/dshare) + set(SUB_ENGINE_DIR ${PARENT_DIR}/build/source/engine) + set(SUB_HOOKUP_DIR ${PARENT_DIR}/build/source/hookup) + set(SUB_NETCDF_DIR ${PARENT_DIR}/build/source/netcdf) + +# Define Actors specific directories + set(ACTORS_DIR ${PARENT_DIR}/build/source/actors) + set(FILE_ACCESS_DIR ${ACTORS_DIR}/file_access_actor) + set(JOB_ACTOR_DIR ${ACTORS_DIR}/job_actor) + set(HRU_ACTOR_DIR ${ACTORS_DIR}/hru_actor) + set(GRU_ACTOR_DIR ${ACTORS_DIR}/gru_actor) + + +#======================================================================== +# PART 2: Assemble all of the SUMMA sub-routines +#======================================================================== + +# utilities + set(NRUTIL + ${ENGINE_DIR}/nrtype.f90 + ${ENGINE_DIR}/f2008funcs.f90 + ${ENGINE_DIR}/nr_utility.f90) + +# Numerical recipes procedures replaced with free versions + set(NRPROC + ${ENGINE_DIR}/expIntegral.f90 + ${ENGINE_DIR}/spline_int.f90) + +# Hook-up modules + set(HOOKUP + ${SUB_HOOKUP_DIR}/ascii_util.f90 + ${SUB_HOOKUP_DIR}/summaFileManager.f90) + +# Data modules + set(DATAMS + ${DSHARE_DIR}/multiconst.f90 + ${DSHARE_DIR}/var_lookup.f90 + ${SUB_DSHARE_DIR}/data_types.f90 + ${DSHARE_DIR}/globalData.f90 + ${DSHARE_DIR}/flxMapping.f90 + ${DSHARE_DIR}/get_ixname.f90 + ${DSHARE_DIR}/popMetadat.f90 + ${DSHARE_DIR}/outpt_stat.f90) + +# utility modules + set(UTILMS + ${ENGINE_DIR}/time_utils.f90 + ${ENGINE_DIR}/mDecisions.f90 + ${ENGINE_DIR}/snow_utils.f90 + ${ENGINE_DIR}/soil_utils.f90 + ${ENGINE_DIR}/updatState.f90 + ${ENGINE_DIR}/matrixOper.f90 + ${ENGINE_DIR}/soil_utilsAddSundials.f90 + ${ENGINE_DIR}/updatStateSundials.f90) + +# Solver + set(SOLVER + ${ENGINE_DIR}/vegPhenlgy.f90 + ${ENGINE_DIR}/diagn_evar.f90 + ${ENGINE_DIR}/stomResist.f90 + ${ENGINE_DIR}/groundwatr.f90 + ${ENGINE_DIR}/vegSWavRad.f90 + ${ENGINE_DIR}/vegNrgFlux.f90 + ${ENGINE_DIR}/ssdNrgFlux.f90 + ${ENGINE_DIR}/vegLiqFlux.f90 + ${ENGINE_DIR}/snowLiqFlx.f90 + ${ENGINE_DIR}/soilLiqFlx.f90 + ${ENGINE_DIR}/bigAquifer.f90 + ${ENGINE_DIR}/computFlux.f90 + ${ENGINE_DIR}/computEnthalpy.f90 + ${ENGINE_DIR}/computHeatCap.f90 + ${ENGINE_DIR}/computThermConduct.f90 + ${ENGINE_DIR}/computResid.f90 + ${ENGINE_DIR}/computJacob.f90 + ${ENGINE_DIR}/eval8summa.f90 + ${ENGINE_DIR}/summaSolve.f90 + ${ENGINE_DIR}/systemSolv.f90 + ${ENGINE_DIR}/computSnowDepth.f90 + ${ENGINE_DIR}/varSubstep.f90 + ${ENGINE_DIR}/opSplittin.f90 + ${ENGINE_DIR}/coupled_em.f90 + ${ENGINE_DIR}/run_oneHRU.f90 + ${ENGINE_DIR}/run_oneGRU.f90) + set(SOLVER_SUN + ${ENGINE_DIR}/type4IDA.f90 + ${ENGINE_DIR}/tol4IDA.f90 + ${ENGINE_DIR}/computResidSundials.f90 + ${ENGINE_DIR}/eval8summaSundials.f90 + ${ENGINE_DIR}/computJacobSundials.f90 + ${ENGINE_DIR}/summaSolveSundialsIDA.f90 + ${ENGINE_DIR}/systemSolvSundials.f90) + +# Actors + set(INTERFACE + ${ACTORS_DIR}/global/cppwrap_datatypes.f90 + ${ACTORS_DIR}/global/cppwrap_auxiliary.f90 + ${ACTORS_DIR}/global/cppwrap_metadata.f90) + set(FILE_ACCESS_INTERFACE + ${FILE_ACCESS_DIR}/fortran_code/output_structure.f90 + ${FILE_ACCESS_DIR}/fortran_code/cppwrap_fileAccess.f90 + ${FILE_ACCESS_DIR}/fortran_code/read_attribute.f90 + ${FILE_ACCESS_DIR}/fortran_code/read_forcing.f90 + ${FILE_ACCESS_DIR}/fortran_code/read_param.f90 + ${FILE_ACCESS_DIR}/fortran_code/read_initcond.f90 + ${FILE_ACCESS_DIR}/fortran_code/writeOutputFromOutputStructure.f90 + ${FILE_ACCESS_DIR}/fortran_code/write_to_netcdf.f90) + set(JOB_INTERFACE + ${JOB_ACTOR_DIR}/job_actor.f90) + set(HRU_INTERFACE + ${HRU_ACTOR_DIR}/fortran_code/model_run.f90 + ${HRU_ACTOR_DIR}/fortran_code/setup_hru.f90 + ${HRU_ACTOR_DIR}/fortran_code/restart.f90 + ${HRU_ACTOR_DIR}/fortran_code/hru_actor.f90 + ${HRU_ACTOR_DIR}/fortran_code/init_hru_actor.f90 + ${HRU_ACTOR_DIR}/fortran_code/outputStrucWrite.f90 + ${HRU_ACTOR_DIR}/fortran_code/hru_writeOutput.f90) + + +# Define routines for SUMMA preliminaries + set(PRELIM + ${ENGINE_DIR}/conv_funcs.f90 + ${ENGINE_DIR}/sunGeomtry.f90 + ${ENGINE_DIR}/convE2Temp.f90 + ${SUB_ENGINE_DIR}/allocspace.f90 + ${ENGINE_DIR}/checkStruc.f90 + ${ENGINE_DIR}/childStruc.f90 + ${SUB_ENGINE_DIR}/ffile_info.f90 + ${ENGINE_DIR}/read_attrb.f90 + ${ENGINE_DIR}/read_pinit.f90 + ${ENGINE_DIR}/pOverwrite.f90 + ${ENGINE_DIR}/read_param.f90 + ${ENGINE_DIR}/paramCheck.f90 + ${ENGINE_DIR}/check_icond.f90) + set(PRELIM_ACT + ${SUB_ENGINE_DIR}/alloc_fileAccess.f90 + ${SUB_ENGINE_DIR}/check_icondActors.f90) + + set(NOAHMP + ${NOAHMP_DIR}/module_model_constants.F + ${NOAHMP_DIR}/module_sf_noahutl.F + ${NOAHMP_DIR}/module_sf_noahlsm.F + ${NOAHMP_DIR}/module_sf_noahmplsm.F) + +# Define routines for the SUMMA model runs + set(MODRUN + ${ENGINE_DIR}/indexState.f90 + ${ENGINE_DIR}/getVectorz.f90 + ${ENGINE_DIR}/t2enthalpy.f90 + ${ENGINE_DIR}/updateVars.f90 + ${ENGINE_DIR}/var_derive.f90 + ${ENGINE_DIR}/read_force.f90 + ${ENGINE_DIR}/derivforce.f90 + ${ENGINE_DIR}/snowAlbedo.f90 + ${ENGINE_DIR}/canopySnow.f90 + ${ENGINE_DIR}/tempAdjust.f90 + ${ENGINE_DIR}/snwCompact.f90 + ${ENGINE_DIR}/layerMerge.f90 + ${ENGINE_DIR}/layerDivide.f90 + ${ENGINE_DIR}/volicePack.f90 + ${ENGINE_DIR}/qTimeDelay.f90 + ${ENGINE_DIR}/updateVarsSundials.f90) + +# Define NetCDF routines + set(NETCDF + ${NETCDF_DIR}/netcdf_util.f90 + ${SUB_NETCDF_DIR}/def_output.f90 + ${SUB_NETCDF_DIR}/modelwrite.f90 + ${NETCDF_DIR}/read_icond.f90) + set(NETCDF_ACT + ${SUB_NETCDF_DIR}/read_icondActors.f90) + +# Define the driver routine + set(DRIVER + ${DRIVER_DIR}/summa_type.f90 + ${DRIVER_DIR}/summa_util.f90 + ${DRIVER_DIR}/summa_alarms.f90 + ${DRIVER_DIR}/summa_globalData.f90 + ${DRIVER_DIR}/summa_defineOutput.f90 + ${DRIVER_DIR}/summa_init.f90 + ${DRIVER_DIR}/summa_setup.f90 + ${DRIVER_DIR}/summa_restart.f90 + ${DRIVER_DIR}/summa_forcing.f90 + ${DRIVER_DIR}/summa_modelRun.f90 + ${DRIVER_DIR}/summa_writeOutput.f90 + ${DRIVER_DIR}/summa_driver.f90) + set(DRIVER + ${SUB_DRIVER_DIR}/summaActors_type.f90 + ${SUB_DRIVER_DIR}/summaActors_util.f90 + ${SUB_DRIVER_DIR}/summaActors_globalData.f90 + ${SUB_DRIVER_DIR}/summaActors_alarms.f90) + +# Run program files + set(SUMMA ${DRIVER_DIR}/summa_run.f90) + set(BMI ${DRIVER_DIR}/summa_runBMI.f90) + +# Define version number, not working correctly + set(VERSIONFILE ${DRIVER_DIR}/summaversion.inc) + execute_process(COMMAND "${GIT_EXECUTABLE} tag | tail -n 1" OUTPUT_VARIABLE VERSION) + execute_process(COMMAND "date" OUTPUT_VARIABLE BULTTIM) + execute_process(COMMAND "${GIT_EXECUTABLE} describe --long --all --always | sed -e's/heads\///'" OUTPUT_VARIABLE GITBRCH) + execute_process(COMMAND "${GIT_EXECUTABLE} rev-parse HEAD" OUTPUT_VARIABLE GITHASH) + + set(COMM_ALL + ${NRPROC} + ${HOOKUP} + ${DATAMS} + ${UTILMS}) + + set(SUMMA_ALL + ${NETCDF} + ${PRELIM} + ${MODRUN} + ${SOLVER} + ${DRIVER}) + + if(CMAKE_BUILD_TYPE MATCHES IDA) + set(SUMMA_ALL ${SUMMA_ALL} ${SOLVER_SUN}) + elseif(CMAKE_BUILD_TYPE MATCHES Kinsol) + set(SUMMA_ALL ${SUMMA_ALL} ${SOLVER_SUN}) #stub + endif() + +#======================================================================== +# PART 4: compilation +#====================================================================== + +# update version information, not working correctly + file(WRITE ${VERSIONFILE} "character(len=64), parameter :: summaVersion = '${VERSION}'\n") + file(APPEND ${VERSIONFILE} "character(len=64), parameter :: buildTime = ''\n") + file(APPEND ${VERSIONFILE} "character(len=64), parameter :: gitBranch = '${GITBRCH}'\n") + file(APPEND ${VERSIONFILE} "character(len=64), parameter :: gitHash = '${GITHASH}'") + + add_library(SUMMA_NOAHMP OBJECT ${NOAHMP} ${NRUTIL}) + target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) + add_library(SUMMA_COMM OBJECT ${COMM_ALL}) + target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) + target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES}) + target_link_libraries(SUMMA_COMM PUBLIC ${LIBRARIES}) + +# Build SUMMA Shared Library, add BMI libraries outside this function + if(WIN32) + add_library(summabmi ${SUMMA_ALL}) + else() + add_library(summabmi SHARED ${SUMMA_ALL}) + endif() + target_compile_options(summabmi PRIVATE ${FLAGS_ALL}) + + if(SUNDIALS_ACTIVE) + target_include_directories(summabmi PUBLIC ${INCLUDES} ${INC_SUNDIALS}) + target_link_libraries(summabmi PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_COMM) + else() + target_include_directories(summabmi PUBLIC ${INCLUDES}) + target_link_libraries(summabmi PUBLIC ${LIBRARIES} SUMMA_COMM) + endif() + +#endfunction() + + +target_link_libraries(summabmi PUBLIC iso_c_bmi) +target_compile_options(summabmi PUBLIC -cpp -DNGEN_ACTIVE) + +set_target_properties(summabmi PROPERTIES VERSION ${PROJECT_VERSION}) + +include(GNUInstallDirs) + +install(TARGETS summabmi + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + +configure_file(summabmi.pc.in summabmi.pc @ONLY) + +install(FILES ${CMAKE_BINARY_DIR}/summabmi.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) diff --git a/build/cmake/build.cluster.bash b/build/cmake/build.cluster.bash index 5c8c7cc94..7dd492992 100644 --- a/build/cmake/build.cluster.bash +++ b/build/cmake/build.cluster.bash @@ -2,16 +2,21 @@ # build on Copernicus or Graham, from summa directory put this one directory up and run this as ../build.cluster.bash -module load boost -module load udunits/2.2.28 module load StdEnv/2020 module load gcc/9.3.0 module load openblas/0.3.17 module load netcdf-fortran/4.5.2 +# for NexGen +module load boost +module load udunits/2.2.28 + +# for Actors +module load caf + -cmake -B cmake_build -S summa -DCMAKE_BUILD_TYPE=Cluster -DSUNDIALS_ACTIVE:BOOL=ON -DACTORS_ACTIVE:BOOL=OFF -cmake --build summa/cmake_build --target all +cmake -B ../cmake_build -S ../../ -DCMAKE_BUILD_TYPE=IDA_Cluster +cmake --build ../cmake_build --target all make -C cmake_build diff --git a/build/cmake/build.mac.bash b/build/cmake/build.mac.bash index b32c4163a..68a0f4e0e 100755 --- a/build/cmake/build.mac.bash +++ b/build/cmake/build.mac.bash @@ -2,7 +2,7 @@ # build on Mac, from summa directory put this one directory up and run this as ../build.mac.bash -cmake -B cmake_build -S summa -DCMAKE_BUILD_TYPE=Release -DSUNDIALS_ACTIVE:BOOL=ON -DACTORS_ACTIVE:BOOL=OFF +cmake -B cmake_build -S summa -DCMAKE_BUILD_TYPE=IDA cmake --build summa/cmake_build --target all make -C cmake_build diff --git a/build/source/engine/derivforce.f90 b/build/source/engine/derivforce.f90 index d8a488ed5..3e12e48ae 100755 --- a/build/source/engine/derivforce.f90 +++ b/build/source/engine/derivforce.f90 @@ -71,7 +71,7 @@ subroutine derivforce(time_data,forc_data,attr_data,mpar_data,prog_data,diag_dat USE summaFileManager,only: NC_TIME_ZONE ! time zone option from control file ! compute derived forcing data variables implicit none - ! input variable + ! input variables integer(i4b),intent(in) :: time_data(:) ! vector of time data for a given time step real(rkind),intent(inout) :: forc_data(:) ! vector of forcing data for a given time step real(rkind),intent(in) :: attr_data(:) ! vector of model attributes @@ -92,8 +92,8 @@ subroutine derivforce(time_data,forc_data,attr_data,mpar_data,prog_data,diag_dat ! cosine of the solar zenith angle real(rkind) :: ahour ! hour at start of time step real(rkind) :: dataStep ! data step (hours) - real(rkind),parameter :: slope ! terrain slope (assume flat) - real(rkind),parameter :: azimuth ! terrain azimuth (assume zero) + real(rkind) :: slope ! terrain slope (assume flat) + real(rkind) :: azimuth ! terrain azimuth (assume zero) real(rkind) :: hri ! average radiation index over time step DT ! general local variables character(len=256) :: cmessage ! error message for downwind routine @@ -162,6 +162,8 @@ subroutine derivforce(time_data,forc_data,attr_data,mpar_data,prog_data,diag_dat ! derived model forcing data scalarO2air => diag_data%var(iLookDIAG%scalarO2air)%dat(1) , & ! atmospheric o2 concentration (Pa) scalarCO2air => diag_data%var(iLookDIAG%scalarCO2air)%dat(1) , & ! atmospheric co2 concentration (Pa) + windspd_x => diag_data%var(iLookDIAG%windspd_x)%dat(1) , & ! wind speed at 10 meter height in x-direction (m s-1) + windspd_y => diag_data%var(iLookDIAG%windspd_y)%dat(1) , & ! wind speed at 10 meter height in y-direction (m s-1) ! radiation variables scalarFractionDirect => diag_data%var(iLookDIAG%scalarFractionDirect)%dat(1) , & ! fraction of direct radiation (0-1) spectralIncomingDirect => flux_data%var(iLookFLUX%spectralIncomingDirect)%dat , & ! downwelling direct shortwave radiation for each waveband (W m-2) @@ -244,6 +246,10 @@ subroutine derivforce(time_data,forc_data,attr_data,mpar_data,prog_data,diag_dat dataStep = data_step/secprhour ! time step (hours) ahour = real(jh,kind(rkind)) + real(jmin,kind(rkind))/minprhour - data_step/secprhour ! decimal hour (start of the step) +#ifdef ACTORS_ACTIVE + azimuth = 0._rkind ! if aspect is not an input attribute, slope & azimuth = zero (flat Earth) + slope = 0._rkind ! Actors doesn't look for this attribute for some reason, should probably FIX +#else ! check slope/aspect intent for radiation calculation if(aspect == nr_realMissing)then azimuth = 0._rkind ! if aspect is not an input attribute, slope & azimuth = zero (flat Earth) @@ -252,6 +258,7 @@ subroutine derivforce(time_data,forc_data,attr_data,mpar_data,prog_data,diag_dat azimuth = aspect ! in degrees slope = atan(abs(tan_slope))*180._rkind/PI_D ! convert from m/m to degrees endif +#endif ! compute the cosine of the solar zenith angle call clrsky_rad(jm,jd,ahour,dataStep, & ! intent(in): time variables diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 32c889fe1..633a5b864 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -326,7 +326,7 @@ subroutine varSubstep(& ! solve the system of equations for a given state subset select case(ixNumericalMethod) case(sundials) -#ifdef NGEN_SUNDIALS_ACTIVE +#ifdef SUNDIALS_ACTIVE call systemSolvSundials(& ! input: model control dtSubstep, & ! intent(in): time step (s) From 7b2576369549c06bfb7bacbd1a010d19c4fd3df9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 4 May 2023 16:26:08 +0900 Subject: [PATCH 0706/1472] spaces --- build/source/driver/summa_alarms.f90 | 226 +++++++++++++-------------- 1 file changed, 113 insertions(+), 113 deletions(-) diff --git a/build/source/driver/summa_alarms.f90 b/build/source/driver/summa_alarms.f90 index 0d9c27864..572a2a1f6 100644 --- a/build/source/driver/summa_alarms.f90 +++ b/build/source/driver/summa_alarms.f90 @@ -52,116 +52,116 @@ module summa_alarms public::summa_setWriteAlarms contains - ! used to set alarms to write model output - subroutine summa_setWriteAlarms(oldTime, newTime, endTime, & ! time vectors - newOutputFile, defNewOutputFile, & ! flag to define new output file - ixRestart, printRestart, & ! flag to print the restart file - ixProgress, printProgress, & ! flag to print simulation progress - resetStats, finalizeStats, & ! flags to reset and finalize stats - statCounter, & ! statistics counter - err,message) ! error control - ! --------------------------------------------------------------------------------------- - ! data types - USE nrtype ! variable types, etc. - ! --------------------------------------------------------------------------------------- - implicit none - ! dummy variables: time vectors - integer(i4b),intent(in) :: oldTime(:) ! time vector from the previous time step - integer(i4b),intent(in) :: newTime(:) ! time vector from the current time step - integer(i4b),intent(in) :: endTime(:) ! time vector at the end of the simulation - ! dummy variables: model decisions - integer(i4b),intent(in) :: newOutputFile ! option for the new output file - integer(i4b),intent(in) :: ixRestart ! option to write the restart file - integer(i4b),intent(in) :: ixProgress ! option to print simulation progress - logical(lgt),intent(in) :: resetStats(:) ! flags to reset statistics - ! dummy variables: alarms - logical(lgt),intent(out) :: defNewOutputFile ! flag to define new output file - logical(lgt),intent(out) :: printRestart ! flag to write the restart file - logical(lgt),intent(out) :: printProgress ! flag to print simulation progress - ! dummy variables: controls on statistics output - logical(lgt),intent(out) :: finalizeStats(:) ! flags to finalize statistics - integer(i4b),intent(out) :: statCounter(:) ! index in model output for different output frequencies - ! dummy variables: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! --------------------------------------------------------------------------------------- - ! local variables - integer(i4b) :: iFreq ! loop through frequencies - ! --------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='summa_setWriteAlarms/' - - ! ***************************************************************************** - ! *** define the need to create the model output file - ! ***************************************************************************** - - ! define the need to create a new output file - select case(newOutputFile) - - ! (don't create a new output files) - case(noNewFiles); defNewOutputFile=.false. - - ! (check for the start of the USA water year) - case(newFileEveryOct1) - defNewOutputFile = (newTime(iLookTIME%im) == 10 .and. & ! month = October - newTime(iLookTIME%im) /= oldTime(iLookTIME%im)) ! first timestep in October - - ! (check that we found the option) - case default; err=20; message=trim(message)//'unable to identify the option to define new output files'; return - - end select - - ! ***************************************************************************** - ! *** define the need to create a restart file - ! ***************************************************************************** - select case(ixRestart) - case(ixRestart_iy); printRestart = (newTime(iLookTIME%im) == 1 .and. newTime(iLookTIME%id) == 1 .and. & - newTime(iLookTIME%ih) == 0 .and. newTime(iLookTIME%imin) == 0) - case(ixRestart_im); printRestart = (newTime(iLookTIME%id) == 1 .and. newTime(iLookTIME%ih) == 0 .and. & - newTime(iLookTIME%imin) == 0) - case(ixRestart_id); printRestart = (newTime(iLookTIME%ih) == 0 .and. newTime(iLookTIME%imin) == 0) - case(ixRestart_end); printRestart = (newTime(iLookTIME%im) == endTime(iLookTIME%im) .and. & - newTime(iLookTIME%id) == endTime(iLookTIME%id) .and. & - newTime(iLookTIME%ih) == endTime(iLookTIME%ih) .and. & - newTime(iLookTIME%imin) == endTime(iLookTIME%imin)) ! newTime does not have a '24h', won't write ending state if end_h=24 - case(ixRestart_never); printRestart = .false. - case default; err=20; message=trim(message)//'unable to identify option for the restart file'; return - end select - - ! ***************************************************************************** - ! *** define the need to print progress - ! ***************************************************************************** - select case(ixProgress) - case(ixProgress_im); printProgress = (newTime(iLookTIME%im) /= oldTime(iLookTIME%im)) ! start month missed - case(ixProgress_id); printProgress = (newTime(iLookTIME%id) /= oldTime(iLookTIME%id)) ! start day missed - case(ixProgress_ih); printProgress = (newTime(iLookTIME%imin) == 0) - case(ixProgress_it); printProgress = .true. - case(ixProgress_never); printProgress = .false. - case default; err=20; message=trim(message)//'unable to identify option to print progress'; return - end select - - ! ***************************************************************************** - ! *** reset counters/flags for model statistics - ! ***************************************************************************** - - ! reset output counters/flags - do iFreq=1,maxVarFreq ! loop through output frequencies - - ! define the need to finalize statistics - ! NOTE: time vector is configured so that ih=0 at the start of the day, hence day in oldTime and timeStruct%var differ - select case(iFreq) - case(iLookFreq%day ); finalizeStats(iFreq)=(oldTime(iLookTIME%id )/=newTime(iLookTIME%id )) ! daily aggregation - case(iLookFreq%month ); finalizeStats(iFreq)=(oldTime(iLookTIME%im )/=newTime(iLookTIME%im )) ! monthly aggregation - case(iLookFreq%annual ); finalizeStats(iFreq)=(oldTime(iLookTIME%iyyy)/=newTime(iLookTIME%iyyy)) ! yearly (annual) aggregation - case(iLookFreq%timestep); finalizeStats(iFreq)=.true. ! timestep-level output (no temporal aggregation) - case default; err=20; message=trim(message)//'unable to identify output frequency'; return - end select - - ! reset ouput timestep - if(resetStats(iFreq)) statCounter(iFreq)=1 - - end do ! looping through output frequencies - - end subroutine summa_setWriteAlarms - -end module summa_alarms +! used to set alarms to write model output +subroutine summa_setWriteAlarms(oldTime, newTime, endTime, & ! time vectors + newOutputFile, defNewOutputFile, & ! flag to define new output file + ixRestart, printRestart, & ! flag to print the restart file + ixProgress, printProgress, & ! flag to print simulation progress + resetStats, finalizeStats, & ! flags to reset and finalize stats + statCounter, & ! statistics counter + err,message) ! error control + ! --------------------------------------------------------------------------------------- + ! data types + USE nrtype ! variable types, etc. + ! --------------------------------------------------------------------------------------- + implicit none + ! dummy variables: time vectors + integer(i4b),intent(in) :: oldTime(:) ! time vector from the previous time step + integer(i4b),intent(in) :: newTime(:) ! time vector from the current time step + integer(i4b),intent(in) :: endTime(:) ! time vector at the end of the simulation + ! dummy variables: model decisions + integer(i4b),intent(in) :: newOutputFile ! option for the new output file + integer(i4b),intent(in) :: ixRestart ! option to write the restart file + integer(i4b),intent(in) :: ixProgress ! option to print simulation progress + logical(lgt),intent(in) :: resetStats(:) ! flags to reset statistics + ! dummy variables: alarms + logical(lgt),intent(out) :: defNewOutputFile ! flag to define new output file + logical(lgt),intent(out) :: printRestart ! flag to write the restart file + logical(lgt),intent(out) :: printProgress ! flag to print simulation progress + ! dummy variables: controls on statistics output + logical(lgt),intent(out) :: finalizeStats(:) ! flags to finalize statistics + integer(i4b),intent(out) :: statCounter(:) ! index in model output for different output frequencies + ! dummy variables: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! --------------------------------------------------------------------------------------- + ! local variables + integer(i4b) :: iFreq ! loop through frequencies + ! --------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='summa_setWriteAlarms/' + + ! ***************************************************************************** + ! *** define the need to create the model output file + ! ***************************************************************************** + + ! define the need to create a new output file + select case(newOutputFile) + + ! (don't create a new output files) + case(noNewFiles); defNewOutputFile=.false. + + ! (check for the start of the USA water year) + case(newFileEveryOct1) + defNewOutputFile = (newTime(iLookTIME%im) == 10 .and. & ! month = October + newTime(iLookTIME%im) /= oldTime(iLookTIME%im)) ! first timestep in October + + ! (check that we found the option) + case default; err=20; message=trim(message)//'unable to identify the option to define new output files'; return + + end select + + ! ***************************************************************************** + ! *** define the need to create a restart file + ! ***************************************************************************** + select case(ixRestart) + case(ixRestart_iy); printRestart = (newTime(iLookTIME%im) == 1 .and. newTime(iLookTIME%id) == 1 .and. & + newTime(iLookTIME%ih) == 0 .and. newTime(iLookTIME%imin) == 0) + case(ixRestart_im); printRestart = (newTime(iLookTIME%id) == 1 .and. newTime(iLookTIME%ih) == 0 .and. & + newTime(iLookTIME%imin) == 0) + case(ixRestart_id); printRestart = (newTime(iLookTIME%ih) == 0 .and. newTime(iLookTIME%imin) == 0) + case(ixRestart_end); printRestart = (newTime(iLookTIME%im) == endTime(iLookTIME%im) .and. & + newTime(iLookTIME%id) == endTime(iLookTIME%id) .and. & + newTime(iLookTIME%ih) == endTime(iLookTIME%ih) .and. & + newTime(iLookTIME%imin) == endTime(iLookTIME%imin)) ! newTime does not have a '24h', won't write ending state if end_h=24 + case(ixRestart_never); printRestart = .false. + case default; err=20; message=trim(message)//'unable to identify option for the restart file'; return + end select + + ! ***************************************************************************** + ! *** define the need to print progress + ! ***************************************************************************** + select case(ixProgress) + case(ixProgress_im); printProgress = (newTime(iLookTIME%im) /= oldTime(iLookTIME%im)) ! start month missed + case(ixProgress_id); printProgress = (newTime(iLookTIME%id) /= oldTime(iLookTIME%id)) ! start day missed + case(ixProgress_ih); printProgress = (newTime(iLookTIME%imin) == 0) + case(ixProgress_it); printProgress = .true. + case(ixProgress_never); printProgress = .false. + case default; err=20; message=trim(message)//'unable to identify option to print progress'; return + end select + + ! ***************************************************************************** + ! *** reset counters/flags for model statistics + ! ***************************************************************************** + + ! reset output counters/flags + do iFreq=1,maxVarFreq ! loop through output frequencies + + ! define the need to finalize statistics + ! NOTE: time vector is configured so that ih=0 at the start of the day, hence day in oldTime and timeStruct%var differ + select case(iFreq) + case(iLookFreq%day ); finalizeStats(iFreq)=(oldTime(iLookTIME%id )/=newTime(iLookTIME%id )) ! daily aggregation + case(iLookFreq%month ); finalizeStats(iFreq)=(oldTime(iLookTIME%im )/=newTime(iLookTIME%im )) ! monthly aggregation + case(iLookFreq%annual ); finalizeStats(iFreq)=(oldTime(iLookTIME%iyyy)/=newTime(iLookTIME%iyyy)) ! yearly (annual) aggregation + case(iLookFreq%timestep); finalizeStats(iFreq)=.true. ! timestep-level output (no temporal aggregation) + case default; err=20; message=trim(message)//'unable to identify output frequency'; return + end select + + ! reset ouput timestep + if(resetStats(iFreq)) statCounter(iFreq)=1 + + end do ! looping through output frequencies + +end subroutine summa_setWriteAlarms + +end module summa_alarms \ No newline at end of file From 44933953f6bb764a3e18a6afc57bec6dff3c50a8 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 9 May 2023 21:15:12 +0900 Subject: [PATCH 0707/1472] moving cmake files to one folder and fixing up CMakeList. A few files changed names. Adding Kyle's asci_util which might fix the memory leak. --- build/cmake/CMakeLists.txt | 687 +++++---- .../README.md => cmake/README_ngen.md} | 6 +- build/cmake/build.cluster.bash | 7 +- build/cmake/build.mac.bash | 7 +- .../build_ngen.cluster.bash | 2 +- .../{cmake_ngen => cmake}/build_ngen.mac.bash | 2 +- build/cmake_ngen/CMakeLists.txt | 350 ----- build/cmake_ngen/summabmi.pc.in | 12 - build/makefiles/compile_copernicus | 17 - build/makefiles/compile_mac | 11 - build/makefiles/makefile_cluster | 352 ----- build/makefiles/makefile_mac | 352 ----- build/source/driver/summa_bmi.f90 | 1257 ++++++++++++++++ build/source/driver/summa_driver.f90 | 1325 ++--------------- .../{summa_run.f90 => summa_driverBmi.f90} | 6 +- build/source/driver/summa_runBMI.f90 | 90 -- build/source/engine/varSubstep.f90 | 6 +- build/source/hookup/ascii_util.f90 | 145 +- 18 files changed, 1853 insertions(+), 2781 deletions(-) rename build/{cmake_ngen/README.md => cmake/README_ngen.md} (94%) rename build/{cmake_ngen => cmake}/build_ngen.cluster.bash (95%) rename build/{cmake_ngen => cmake}/build_ngen.mac.bash (94%) delete mode 100644 build/cmake_ngen/CMakeLists.txt delete mode 100644 build/cmake_ngen/summabmi.pc.in delete mode 100755 build/makefiles/compile_copernicus delete mode 100755 build/makefiles/compile_mac delete mode 100644 build/makefiles/makefile_cluster delete mode 100644 build/makefiles/makefile_mac create mode 100644 build/source/driver/summa_bmi.f90 mode change 100644 => 100755 build/source/driver/summa_driver.f90 rename build/source/driver/{summa_run.f90 => summa_driverBmi.f90} (97%) delete mode 100644 build/source/driver/summa_runBMI.f90 diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 515f779e9..e1280ecd1 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -3,14 +3,15 @@ enable_language(C Fortran) SET (CMAKE_Fortran_COMPILER gfortran) -# -------------------------------------------------------------------------------------------- -# When compiling Summa each configuration can be compiled in a Cluster or Debug mode, which is -# set by the CMAKE_BUILD_TYPES variables. The default is BE, release mode no cluster no Sundials -# no Actors no NexGen. -# Other options include how to drive the code, NexGen (with BMI) or Actors (without BMI) framework -# or no extra framework (without BMI). -# Note, Sundials, NexGen, and Actors will all require extra libraries to be downloaded and -# compiled. +#========================================================================================= +# When compiling Summa each configuration can be compiled in a Cluster or Debug mode, +# which is set by the CMAKE_BUILD_TYPES variables. The default is BE, release mode no +# cluster no Sundials no Actors no NexGen. +# Other options include how to drive the code, NexGen (with BMI) or Actors (without BMI) +# framework or no extra framework (without BMI). +# Note, Sundials, NexGen, and Actors will all require extra libraries to be downloaded +# and compiled. +#========================================================================================= # Set default configuration type to Release if(NOT CMAKE_BUILD_TYPE) @@ -31,6 +32,11 @@ message("\nSelected Bulid Type: ${CMAKE_BUILD_TYPE}\n") if(CMAKE_BUILD_TYPE MATCHES Kinsol OR CMAKE_BUILD_TYPE MATCHES IDA) add_compile_definitions(SUNDIALS_ACTIVE) + if(CMAKE_BUILD_TYPE MATCHES Kinsol) + add_compile_definitions(KINSOL_ACTIVE) + elseif(CMAKE_BUILD_TYPE MATCHES IDA) + add_compile_definitions(IDA_ACTIVE) + endif() endif() if(CMAKE_BUILD_TYPE MATCHES NexGen) @@ -39,7 +45,8 @@ if(CMAKE_BUILD_TYPE MATCHES NexGen) project(summabmi VERSION 1.0.0 DESCRIPTION "Summa-Sundials-BE BMI Module Shared Library") - # NexGen needs to have CMakeLists one directory above summa submodule GIT, at ${NexGen GIT directory}/extern/summa + # NexGen needs to have CMakeLists one directory above summa submodule GIT, + # at ${NexGen GIT directory}/extern/summa set(F_MASTER ./summa/) # directory of summa source code set(PARENT_DIR ${F_MASTER}) # directory of summa source code @@ -83,8 +90,9 @@ else() endif() -######### SET THE PATHS TO THE LIBRARIES AND INCLUDE FILES ######### -############################################################################# +#========================================================================================= +# SET THE PATHS TO THE LIBRARIES AND INCLUDE FILES +#========================================================================================= # Set compiler flags if(CMAKE_BUILD_TYPE MATCHES Debug) @@ -102,7 +110,7 @@ endif() # Find Sundials if exists set(DIR_SUNDIALS "") -foreach(dir IN ITEMS +foreach(dir IN ITEMS "${F_MASTER}../sundials/instdir" "${PARENT_DIR}../sundials/instdir" "../../../../SummaSundials/sundials/instdir" @@ -114,6 +122,7 @@ endforeach() # Set libraries for all builds if(CMAKE_BUILD_TYPE MATCHES Cluster) + message("\Building for cluster\n") # MKL needs BLA_VENDOR set with cmake set(BLA_VENDOR OpenBLAS) @@ -132,7 +141,15 @@ if(CMAKE_BUILD_TYPE MATCHES Cluster) set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran) endif() + if(CMAKE_BUILD_TYPE MATCHES Actors) + find_package(CAF REQUIRED) + set(INC_ACTORS ${CAF_INCLUDES} ${PARENT_DIR}/build/includes/global ${PARENT_DIR}/build/includes/summa_actor ${PARENT_DIR}/build/includes/gru_actor ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) + set(LIB_ACTORS ${CAF_LIBRARIES} -lcaf_core -lcaf_io) + endif() + + else() + message("\Building for personal computer\n") set(SDKROOT "$(xcrun --show-sdk-path)") @@ -146,285 +163,372 @@ else() set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran) endif() + # Actors is on Cluster, if using on personal computer might need to link directory-- not working on Mac right now + if(CMAKE_BUILD_TYPE MATCHES Actors) + #link_directories(${DIR_ACTORS}/lib) + find_package(CAF REQUIRED) + set(INC_ACTORS ${CAF_INCLUDES} ${PARENT_DIR}/build/includes/global ${PARENT_DIR}/build/includes/summa_actor ${PARENT_DIR}/build/includes/gru_actor ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) + set(LIB_ACTORS ${CAF_LIBRARIES} -lcaf_core -lcaf_io) + endif() + endif() + if(CMAKE_BUILD_TYPE MATCHES IDA) message("\nUsing SUNDIALS IDA libraries, should have been installed previously\n") + set(LIB_SUNDIALS -lsundials_fida_mod -lsundials_fnvecmanyvector_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) elseif(CMAKE_BUILD_TYPE MATCHES Kinsol) message("\nUsing SUNDIALS Kinsol libraries, should have been installed previously\n") + set(LIB_SUNDIALS -lsundials_fkinsol_mod -lsundials_fnvecserial_mod -lsundials_fsunnonlinsolnewton_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) -endif() +elseif(CMAKE_BUILD_TYPE MATCHES BE) + message("\nUsing Home-grown Backward Euler\n") -# Actors is on Cluster, if using on personal computer might need to link directory-- not working on Mac right now -if(ACTORS_ACTIVE) - find_package(CAF REQUIRED) - set(INC_ACTORS ${CAF_INCLUDES} ${PARENT_DIR}/build/includes/global ${PARENT_DIR}/build/includes/summa_actor ${PARENT_DIR}/build/includes/gru_actor ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) - set(LIB_ACTORS ${CAF_LIBRARIES} -lcaf_core -lcaf_io) +else() + message("Valid build types are: ${CMAKE_CONFIGURATION_TYPES}") + message(FATAL_ERROR "Unknown build type: ${CMAKE_BUILD_TYPE}") endif() - message("\nBuilding SUMMA\n") -#compile_with_bmi(${F_MASTER}, ${DIR_SUNDIALS}, ${INCLUDES}, ${LIBRARIES}, ${INC_SUNDIALS}, ${LIB_SUNDIALS}, ${FLAGS_ALL}, ${FLAGS_NOAH}) -#function(compile_with_bmi F_MASTER, DIR_SUNDIALS, INCLUDES, LIBRARIES, INC_SUNDIALS, LIB_SUNDIALS, FLAGS_ALL, FLAGS_NOAH) - -#======================================================================== -# PART 1: Define directory paths -#======================================================================== +#========================================================================================= +# COMPILE PART 1: Define directory paths +#========================================================================================= # Define directories that contains source code - set(DRIVER_DIR ${F_MASTER}/build/source/driver) - set(HOOKUP_DIR ${F_MASTER}/build/source/hookup) - set(NETCDF_DIR ${F_MASTER}/build/source/netcdf) - set(DSHARE_DIR ${F_MASTER}/build/source/dshare) - set(NUMREC_DIR ${F_MASTER}/build/source/numrec) - set(NOAHMP_DIR ${F_MASTER}/build/source/noah-mp) - set(ENGINE_DIR ${F_MASTER}/build/source/engine) +set(DRIVER_DIR ${F_MASTER}/build/source/driver) +set(DSHARE_DIR ${F_MASTER}/build/source/dshare) +set(ENGINE_DIR ${F_MASTER}/build/source/engine) +set(HOOKUP_DIR ${F_MASTER}/build/source/hookup) +set(NETCDF_DIR ${F_MASTER}/build/source/netcdf) +set(NOAHMP_DIR ${F_MASTER}/build/source/noah-mp) # Define directories for source files that might be replaced by actors (identical if not using actors) - set(SUB_DRIVER_DIR ${PARENT_DIR}/build/source/driver) - set(SUB_DSHARE_DIR ${PARENT_DIR}/build/source/dshare) - set(SUB_ENGINE_DIR ${PARENT_DIR}/build/source/engine) - set(SUB_HOOKUP_DIR ${PARENT_DIR}/build/source/hookup) - set(SUB_NETCDF_DIR ${PARENT_DIR}/build/source/netcdf) +set(SUB_DRIVER_DIR ${PARENT_DIR}/build/source/driver) +set(SUB_DSHARE_DIR ${PARENT_DIR}/build/source/dshare) +set(SUB_ENGINE_DIR ${PARENT_DIR}/build/source/engine) +set(SUB_HOOKUP_DIR ${PARENT_DIR}/build/source/hookup) +set(SUB_NETCDF_DIR ${PARENT_DIR}/build/source/netcdf) # Define Actors specific directories - set(ACTORS_DIR ${PARENT_DIR}/build/source/actors) - set(FILE_ACCESS_DIR ${ACTORS_DIR}/file_access_actor) - set(JOB_ACTOR_DIR ${ACTORS_DIR}/job_actor) - set(HRU_ACTOR_DIR ${ACTORS_DIR}/hru_actor) - set(GRU_ACTOR_DIR ${ACTORS_DIR}/gru_actor) - - -#======================================================================== -# PART 2: Assemble all of the SUMMA sub-routines -#======================================================================== - -# utilities - set(NRUTIL - ${ENGINE_DIR}/nrtype.f90 - ${ENGINE_DIR}/f2008funcs.f90 - ${ENGINE_DIR}/nr_utility.f90) - -# Numerical recipes procedures replaced with free versions - set(NRPROC - ${ENGINE_DIR}/expIntegral.f90 - ${ENGINE_DIR}/spline_int.f90) +set(ACTORS_DIR ${PARENT_DIR}/build/source/actors) +set(FILE_ACCESS_DIR ${ACTORS_DIR}/file_access_actor) +set(JOB_ACTOR_DIR ${ACTORS_DIR}/job_actor) +set(HRU_ACTOR_DIR ${ACTORS_DIR}/hru_actor) +set(GRU_ACTOR_DIR ${ACTORS_DIR}/gru_actor) + + +#========================================================================================= +# COMPILE PART 2: Assemble all of the SUMMA sub-routines +#========================================================================================= + +# NOAHMP modules +set(NOAHMP + ${NOAHMP_DIR}/module_model_constants.F + ${NOAHMP_DIR}/module_sf_noahutl.F + ${NOAHMP_DIR}/module_sf_noahlsm.F + ${NOAHMP_DIR}/module_sf_noahmplsm.F) + +# Free versions of numerical recipes utilities for NOAH-MP modules +set(NRUTIL + ${ENGINE_DIR}/f2008funcs.f90 + ${ENGINE_DIR}/nr_utility.f90 + ${ENGINE_DIR}/nrtype.f90) + +# Free versions of numerical recipes procedures for SUMMA modules +set(NRPROC + ${ENGINE_DIR}/expIntegral.f90 + ${ENGINE_DIR}/spline_int.f90) # Hook-up modules - set(HOOKUP - ${SUB_HOOKUP_DIR}/ascii_util.f90 - ${SUB_HOOKUP_DIR}/summaFileManager.f90) +set(HOOKUP + ${HOOKUP_DIR}/ascii_util.f90 + ${SUB_HOOKUP_DIR}/summaFileManager.f90) # Data modules - set(DATAMS - ${DSHARE_DIR}/multiconst.f90 - ${DSHARE_DIR}/var_lookup.f90 - ${SUB_DSHARE_DIR}/data_types.f90 - ${DSHARE_DIR}/globalData.f90 - ${DSHARE_DIR}/flxMapping.f90 - ${DSHARE_DIR}/get_ixname.f90 - ${DSHARE_DIR}/popMetadat.f90 - ${DSHARE_DIR}/outpt_stat.f90) - -# utility modules - set(UTILMS - ${ENGINE_DIR}/time_utils.f90 - ${ENGINE_DIR}/mDecisions.f90 - ${ENGINE_DIR}/snow_utils.f90 - ${ENGINE_DIR}/soil_utils.f90 - ${ENGINE_DIR}/updatState.f90 - ${ENGINE_DIR}/matrixOper.f90 - ${ENGINE_DIR}/soil_utilsAddSundials.f90 - ${ENGINE_DIR}/updatStateSundials.f90) - -# Solver - set(SOLVER - ${ENGINE_DIR}/vegPhenlgy.f90 - ${ENGINE_DIR}/diagn_evar.f90 - ${ENGINE_DIR}/stomResist.f90 - ${ENGINE_DIR}/groundwatr.f90 - ${ENGINE_DIR}/vegSWavRad.f90 - ${ENGINE_DIR}/vegNrgFlux.f90 - ${ENGINE_DIR}/ssdNrgFlux.f90 - ${ENGINE_DIR}/vegLiqFlux.f90 - ${ENGINE_DIR}/snowLiqFlx.f90 - ${ENGINE_DIR}/soilLiqFlx.f90 - ${ENGINE_DIR}/bigAquifer.f90 - ${ENGINE_DIR}/computFlux.f90 - ${ENGINE_DIR}/computEnthalpy.f90 - ${ENGINE_DIR}/computHeatCap.f90 - ${ENGINE_DIR}/computThermConduct.f90 - ${ENGINE_DIR}/computResid.f90 - ${ENGINE_DIR}/computJacob.f90 - ${ENGINE_DIR}/eval8summa.f90 - ${ENGINE_DIR}/summaSolve.f90 - ${ENGINE_DIR}/systemSolv.f90 - ${ENGINE_DIR}/computSnowDepth.f90 - ${ENGINE_DIR}/varSubstep.f90 - ${ENGINE_DIR}/opSplittin.f90 - ${ENGINE_DIR}/coupled_em.f90 - ${ENGINE_DIR}/run_oneHRU.f90 - ${ENGINE_DIR}/run_oneGRU.f90) - set(SOLVER_SUN - ${ENGINE_DIR}/type4IDA.f90 - ${ENGINE_DIR}/tol4IDA.f90 - ${ENGINE_DIR}/computResidSundials.f90 - ${ENGINE_DIR}/eval8summaSundials.f90 - ${ENGINE_DIR}/computJacobSundials.f90 - ${ENGINE_DIR}/summaSolveSundialsIDA.f90 - ${ENGINE_DIR}/systemSolvSundials.f90) - -# Actors - set(INTERFACE - ${ACTORS_DIR}/global/cppwrap_datatypes.f90 - ${ACTORS_DIR}/global/cppwrap_auxiliary.f90 - ${ACTORS_DIR}/global/cppwrap_metadata.f90) - set(FILE_ACCESS_INTERFACE - ${FILE_ACCESS_DIR}/fortran_code/output_structure.f90 - ${FILE_ACCESS_DIR}/fortran_code/cppwrap_fileAccess.f90 - ${FILE_ACCESS_DIR}/fortran_code/read_attribute.f90 - ${FILE_ACCESS_DIR}/fortran_code/read_forcing.f90 - ${FILE_ACCESS_DIR}/fortran_code/read_param.f90 - ${FILE_ACCESS_DIR}/fortran_code/read_initcond.f90 - ${FILE_ACCESS_DIR}/fortran_code/writeOutputFromOutputStructure.f90 - ${FILE_ACCESS_DIR}/fortran_code/write_to_netcdf.f90) - set(JOB_INTERFACE - ${JOB_ACTOR_DIR}/job_actor.f90) - set(HRU_INTERFACE - ${HRU_ACTOR_DIR}/fortran_code/model_run.f90 - ${HRU_ACTOR_DIR}/fortran_code/setup_hru.f90 - ${HRU_ACTOR_DIR}/fortran_code/restart.f90 - ${HRU_ACTOR_DIR}/fortran_code/hru_actor.f90 - ${HRU_ACTOR_DIR}/fortran_code/init_hru_actor.f90 - ${HRU_ACTOR_DIR}/fortran_code/outputStrucWrite.f90 - ${HRU_ACTOR_DIR}/fortran_code/hru_writeOutput.f90) - - -# Define routines for SUMMA preliminaries - set(PRELIM - ${ENGINE_DIR}/conv_funcs.f90 - ${ENGINE_DIR}/sunGeomtry.f90 - ${ENGINE_DIR}/convE2Temp.f90 - ${SUB_ENGINE_DIR}/allocspace.f90 - ${ENGINE_DIR}/checkStruc.f90 - ${ENGINE_DIR}/childStruc.f90 - ${SUB_ENGINE_DIR}/ffile_info.f90 - ${ENGINE_DIR}/read_attrb.f90 - ${ENGINE_DIR}/read_pinit.f90 - ${ENGINE_DIR}/pOverwrite.f90 - ${ENGINE_DIR}/read_param.f90 - ${ENGINE_DIR}/paramCheck.f90 - ${ENGINE_DIR}/check_icond.f90) - set(PRELIM_ACT - ${SUB_ENGINE_DIR}/alloc_fileAccess.f90 - ${SUB_ENGINE_DIR}/check_icondActors.f90) - - set(NOAHMP - ${NOAHMP_DIR}/module_model_constants.F - ${NOAHMP_DIR}/module_sf_noahutl.F - ${NOAHMP_DIR}/module_sf_noahlsm.F - ${NOAHMP_DIR}/module_sf_noahmplsm.F) - -# Define routines for the SUMMA model runs - set(MODRUN - ${ENGINE_DIR}/indexState.f90 - ${ENGINE_DIR}/getVectorz.f90 - ${ENGINE_DIR}/t2enthalpy.f90 - ${ENGINE_DIR}/updateVars.f90 - ${ENGINE_DIR}/var_derive.f90 - ${ENGINE_DIR}/read_force.f90 - ${ENGINE_DIR}/derivforce.f90 - ${ENGINE_DIR}/snowAlbedo.f90 - ${ENGINE_DIR}/canopySnow.f90 - ${ENGINE_DIR}/tempAdjust.f90 - ${ENGINE_DIR}/snwCompact.f90 - ${ENGINE_DIR}/layerMerge.f90 - ${ENGINE_DIR}/layerDivide.f90 - ${ENGINE_DIR}/volicePack.f90 - ${ENGINE_DIR}/qTimeDelay.f90 - ${ENGINE_DIR}/updateVarsSundials.f90) - -# Define NetCDF routines - set(NETCDF - ${NETCDF_DIR}/netcdf_util.f90 - ${SUB_NETCDF_DIR}/def_output.f90 - ${SUB_NETCDF_DIR}/modelwrite.f90 - ${NETCDF_DIR}/read_icond.f90) - set(NETCDF_ACT - ${SUB_NETCDF_DIR}/read_icondActors.f90) - -# Define the driver routine - set(DRIVER - ${DRIVER_DIR}/summa_type.f90 - ${DRIVER_DIR}/summa_util.f90 - ${DRIVER_DIR}/summa_alarms.f90 - ${DRIVER_DIR}/summa_globalData.f90 - ${DRIVER_DIR}/summa_defineOutput.f90 - ${DRIVER_DIR}/summa_init.f90 - ${DRIVER_DIR}/summa_setup.f90 - ${DRIVER_DIR}/summa_restart.f90 - ${DRIVER_DIR}/summa_forcing.f90 - ${DRIVER_DIR}/summa_modelRun.f90 - ${DRIVER_DIR}/summa_writeOutput.f90 - ${DRIVER_DIR}/summa_driver.f90) - set(DRIVER - ${SUB_DRIVER_DIR}/summaActors_type.f90 - ${SUB_DRIVER_DIR}/summaActors_util.f90 - ${SUB_DRIVER_DIR}/summaActors_globalData.f90 - ${SUB_DRIVER_DIR}/summaActors_alarms.f90) +set(DATAMS + ${SUB_DSHARE_DIR}/data_types.f90 + ${DSHARE_DIR}/flxMapping.f90 + ${DSHARE_DIR}/get_ixname.f90 + ${DSHARE_DIR}/globalData.f90 + ${DSHARE_DIR}/multiconst.f90 + ${DSHARE_DIR}/outpt_stat.f90 + ${DSHARE_DIR}/popMetadat.f90 + ${DSHARE_DIR}/var_lookup.f90) +set(DATAMS_KINSOL + ${DSHARE_DIR}/csv_file.f90 + ${SUMMA_DSHARE_DIR}/kinsol_user_data_type.f90) + +# Utility modules +set(UTILMS + ${ENGINE_DIR}/matrixOper.f90 + ${ENGINE_DIR}/mDecisions.f90 + ${ENGINE_DIR}/snow_utils.f90 + ${ENGINE_DIR}/soil_utils.f90 + ${ENGINE_DIR}/time_utils.f90 + ${ENGINE_DIR}/updatState.f90) +set(UTILMS_IDA + ${ENGINE_DIR}/soil_utilsAddSundials.f90 + ${ENGINE_DIR}/updatStateSundials.f90) + +# NetCDF routines +set(NETCDF + ${SUB_NETCDF_DIR}/def_output.f90 + ${SUB_NETCDF_DIR}/modelwrite.f90 + ${NETCDF_DIR}/netcdf_util.f90 + ${SUB_NETCDF_DIR}/read_icond.f90) + +# Preliminary modules +set(PRELIM + ${SUB_ENGINE_DIR}/allocspace.f90 + ${SUB_ENGINE_DIR}/check_icond.f90 + ${ENGINE_DIR}/checkStruc.f90 + ${ENGINE_DIR}/childStruc.f90 + ${ENGINE_DIR}/conv_funcs.f90 + ${ENGINE_DIR}/convE2Temp.f90 + ${SUB_ENGINE_DIR}/ffile_info.f90 + ${ENGINE_DIR}/read_attrb.f90 + ${ENGINE_DIR}/read_pinit.f90 + ${ENGINE_DIR}/paramCheck.f90 + ${ENGINE_DIR}/pOverwrite.f90 + ${ENGINE_DIR}/read_param.f90 + ${ENGINE_DIR}/sunGeomtry.f90) +set(PRELIM_ACTOR + ${SUB_ENGINE_DIR}/alloc_fileAccess.f90) + +# Model run support modules +set(MODRUN + ${ENGINE_DIR}/canopySnow.f90 + ${ENGINE_DIR}/derivforce.f90 + ${ENGINE_DIR}/getVectorz.f90 + ${ENGINE_DIR}/indexState.f90 + ${ENGINE_DIR}/layerMerge.f90 + ${ENGINE_DIR}/layerDivide.f90 + ${ENGINE_DIR}/qTimeDelay.f90 + ${ENGINE_DIR}/read_force.f90 + ${ENGINE_DIR}/snowAlbedo.f90 + ${ENGINE_DIR}/snwCompact.f90 + ${ENGINE_DIR}/tempAdjust.f90 + ${ENGINE_DIR}/updateVars.f90 + ${ENGINE_DIR}/var_derive.f90 + ${ENGINE_DIR}/volicePack.f90) +# Note, eventually these should be added to Kinsol +set(MODRUN_NOT_KINSOL + ${ENGINE_DIR}/t2enthalpy.f90 +set(MODRUN_IDA + ${ENGINE_DIR}/updateVarsSundials.f90) + +# Solver main modules +set(SOLVER + ${ENGINE_DIR}/bigAquifer.f90 + ${ENGINE_DIR}/computFlux.f90 + ${ENGINE_DIR}/computJacob.f90 + ${ENGINE_DIR}/computResid.f90 + ${ENGINE_DIR}/coupled_em.f90 + ${ENGINE_DIR}/diagn_evar.f90 + ${ENGINE_DIR}/eval8summa.f90 + ${ENGINE_DIR}/groundwatr.f90 + ${ENGINE_DIR}/opSplittin.f90 + ${ENGINE_DIR}/snowLiqFlx.f90 + ${ENGINE_DIR}/soilLiqFlx.f90 + ${ENGINE_DIR}/ssdNrgFlux.f90 + ${ENGINE_DIR}/stomResist.f90 + ${ENGINE_DIR}/summaSolve.f90 + ${ENGINE_DIR}/systemSolv.f90 + ${ENGINE_DIR}/varSubstep.f90 + ${ENGINE_DIR}/vegLiqFlux.f90 + ${ENGINE_DIR}/vegNrgFlux.f90 + ${ENGINE_DIR}/vegPhenlgy.f90 + ${ENGINE_DIR}/vegSWavRad.f90) +# Note, eventually these should be added to Kinsol +set(SOLVER_NOT_KINSOL + ${ENGINE_DIR}/computSnowDepth.f90 + ${ENGINE_DIR}/computEnthalpy.f90 + ${ENGINE_DIR}/computHeatCap.f90 + ${ENGINE_DIR}/computThermConduct.f90) +set(SOLVER_IDA + ${ENGINE_DIR}/computJacobSundials.f90 + ${ENGINE_DIR}/computResidSundials.f90 + ${ENGINE_DIR}/eval8summaSundials.f90 + ${ENGINE_DIR}/summaSolveSundialsIDA.f90 + ${ENGINE_DIR}/systemSolvSundials.f90 + ${ENGINE_DIR}/tol4IDA.f90 + ${ENGINE_DIR}/type4IDA.f90) +set(SOLVER_NOT_ACTOR + ${ENGINE_DIR}/run_oneGRU.f90 + ${ENGINE_DIR}/run_oneHRU.f90s) + +# Driver support modules +set(DRIVER + ${SUB_DRIVER_DIR}/summa_type.f90 + ${SUB_DRIVER_DIR}/summa_util.f90 + ${DRIVER_DIR}/summa_alarms.f90 + ${SUB_DRIVER_DIR}/summa_globalData.f90) +set(DRIVER_NOT_ACTOR + ${DRIVER_DIR}/summa_defineOutput.f90 + ${DRIVER_DIR}/summa_init.f90 + ${DRIVER_DIR}/summa_setup.f90 + ${DRIVER_DIR}/summa_restart.f90 + ${DRIVER_DIR}/summa_forcing.f90 + ${DRIVER_DIR}/summa_modelRun.f90 + ${DRIVER_DIR}/summa_writeOutput.f90) +set(DRIVER_NEXGEN + ${DRIVER_DIR}/summa_bmi.f90) + +# Actors interface modules +set(INTERFACE + ${ACTORS_DIR}/global/cppwrap_datatypes.f90 + ${ACTORS_DIR}/global/cppwrap_auxiliary.f90 + ${ACTORS_DIR}/global/cppwrap_metadata.f90) +set(FILE_ACCESS_INTERFACE + ${FILE_ACCESS_DIR}/fortran_code/output_structure.f90 + ${FILE_ACCESS_DIR}/fortran_code/cppwrap_fileAccess.f90 + ${FILE_ACCESS_DIR}/fortran_code/read_attribute.f90 + ${FILE_ACCESS_DIR}/fortran_code/read_forcing.f90 + ${FILE_ACCESS_DIR}/fortran_code/read_param.f90 + ${FILE_ACCESS_DIR}/fortran_code/read_initcond.f90 + ${FILE_ACCESS_DIR}/fortran_code/writeOutputFromOutputStructure.f90 + ${FILE_ACCESS_DIR}/fortran_code/write_to_netcdf.f90) +set(JOB_INTERFACE + ${JOB_ACTOR_DIR}/job_actor.f90) +set(HRU_INTERFACE + ${HRU_ACTOR_DIR}/fortran_code/model_run.f90 + ${HRU_ACTOR_DIR}/fortran_code/setup_hru.f90 + ${HRU_ACTOR_DIR}/fortran_code/restart.f90 + ${HRU_ACTOR_DIR}/fortran_code/hru_actor.f90 + ${HRU_ACTOR_DIR}/fortran_code/init_hru_actor.f90 + ${HRU_ACTOR_DIR}/fortran_code/outputStrucWrite.f90 + ${HRU_ACTOR_DIR}/fortran_code/hru_writeOutput.f90) + +# Actors actual actor modules +set(ACTORS_GLOBAL + ${ACTORS_DIR}/global/global.cpp + ${ACTORS_DIR}/global/timing_info.cpp + ${ACTORS_DIR}/global/message_atoms.cpp + ${ACTORS_DIR}/global/settings_functions.cpp + ${ACTORS_DIR}/global/auxiliary.cpp) +set(SUMMA_ACTOR + ${ACTORS_DIR}/summa_actor/summa_actor.cpp + ${ACTORS_DIR}/summa_actor/summa_client.cpp + ${ACTORS_DIR}/summa_actor/summa_server.cpp + ${ACTORS_DIR}/summa_actor/summa_backup_server.cpp + ${ACTORS_DIR}/summa_actor/batch/batch.cpp + ${ACTORS_DIR}/summa_actor/batch/batch_container.cpp + ${ACTORS_DIR}/summa_actor/client/client.cpp + ${ACTORS_DIR}/summa_actor/client/client_container.cpp) +set(HRU_ACTOR + ${ACTORS_DIR}/hru_actor/cpp_code/hru_actor.cpp) +set(GRU_ACTOR + ${ACTORS_DIR}/gru_actor/gru_actor.cpp) +set(FILE_ACCESS_ACTOR + ${ACTORS_DIR}/file_access_actor/cpp_code/file_access_actor.cpp + ${ACTORS_DIR}/file_access_actor/cpp_code/forcing_file_info.cpp + ${ACTORS_DIR}/file_access_actor/cpp_code/output_container.cpp) +set(JOB_ACTOR + ${ACTORS_DIR}/job_actor/job_actor.cpp + ${ACTORS_DIR}/job_actor/GRU.cpp) + + +#========================================================================================= +# COMPILE PART 3: Collect the subroutines into build groups depending on build type +#========================================================================================= + +set(COMM_ALL + ${NRPROC} + ${HOOKUP} + ${DATAMS} + ${UTILMS}) +if(CMAKE_BUILD_TYPE MATCHES Actors) + set(COMM_ALL ${COMM_ALL} + ${INTERFACE}) +endif() +if(CMAKE_BUILD_TYPE MATCHES Kinsol) + set(COMM_ALL ${COMM_ALL} + ${DATAMS_KINSOL}) +endif() +if(CMAKE_BUILD_TYPE MATCHES IDA) + set(COMM_ALL ${COMM_ALL} + ${UTILMS_IDA}) +endif() + +set(SUMMA_ALL + ${NETCDF} + ${PRELIM} + ${MODRUN} + ${SOLVER} + ${DRIVER}) + +if(CMAKE_BUILD_TYPE MATCHES Actors) + set(SUMMA_ALL ${SUMMA_ALL} + ${PRELIM_ACTOR} + ${JOB_INTERFACE} + ${FILE_ACCESS_INTERFACE} + ${HRU_INTERFACE} + ${GRU_INTERFACE}) +else() + set(SUMMA_ALL ${SUMMA_ALL} + ${SOLVER_NOT_ACTOR} + ${DRIVER_NOT_ACTOR}) +endif() +if(CMAKE_BUILD_TYPE MATCHES NexGen) + set(SUMMA_ALL ${SUMMA_ALL} + ${DRIVER_NEXGEN}) +endif() +if(CMAKE_BUILD_TYPE MATCHES IDA) + set(SUMMA_ALL ${SUMMA_ALL} + ${MODRUN_IDA} + ${SOLVER_IDA}) +endif() + +if(NOT CMAKE_BUILD_TYPE MATCHES Kinsol) + set(SUMMA_ALL ${SUMMA_ALL} + ${MODRUN_NOT_KINSOL} + ${SOLVER_NOT_KINSOL}) +endif() # Run program files - set(SUMMA ${DRIVER_DIR}/summa_run.f90) - set(BMI ${DRIVER_DIR}/summa_runBMI.f90) +set(MAIN_ACTOR ${ACTORS_DIR}/main.cpp) +set(MAIN_BMI ${DRIVER_DIR}/summa_driverBMI.f90) # do not need a driver in NexGen so do not use +set(MAIN_SUMMA ${DRIVER_DIR}/summa_driver.f90) + +# Define the executable +set(EXEC_NAME summa_sundials) # Define version number, not working correctly - set(VERSIONFILE ${DRIVER_DIR}/summaversion.inc) - execute_process(COMMAND "${GIT_EXECUTABLE} tag | tail -n 1" OUTPUT_VARIABLE VERSION) - execute_process(COMMAND "date" OUTPUT_VARIABLE BULTTIM) - execute_process(COMMAND "${GIT_EXECUTABLE} describe --long --all --always | sed -e's/heads\///'" OUTPUT_VARIABLE GITBRCH) - execute_process(COMMAND "${GIT_EXECUTABLE} rev-parse HEAD" OUTPUT_VARIABLE GITHASH) - - set(COMM_ALL - ${NRPROC} - ${HOOKUP} - ${DATAMS} - ${UTILMS}) - - set(SUMMA_ALL - ${NETCDF} - ${PRELIM} - ${MODRUN} - ${SOLVER} - ${DRIVER}) - - if(CMAKE_BUILD_TYPE MATCHES IDA) - set(SUMMA_ALL ${SUMMA_ALL} ${SOLVER_SUN}) - elseif(CMAKE_BUILD_TYPE MATCHES Kinsol) - set(SUMMA_ALL ${SUMMA_ALL} ${SOLVER_SUN}) #stub - endif() +set(VERSIONFILE ${DRIVER_DIR}/summaversion.inc) +execute_process(COMMAND " ${GIT_EXECUTABLE} tag | tail -n 1" OUTPUT_VARIABLE VERSION) +execute_process(COMMAND "date" OUTPUT_VARIABLE BULTTIM) +execute_process(COMMAND " ${GIT_EXECUTABLE} describe --long --all --always | sed -e's/heads\///'" OUTPUT_VARIABLE GITBRCH) +execute_process(COMMAND " ${GIT_EXECUTABLE} rev-parse HEAD" OUTPUT_VARIABLE GITHASH) + -#======================================================================== -# PART 4: compilation -#====================================================================== +#========================================================================================= +# COMPILE PART 4: Do the compilation +#========================================================================================= # update version information, not working correctly - file(WRITE ${VERSIONFILE} "character(len=64), parameter :: summaVersion = '${VERSION}'\n") - file(APPEND ${VERSIONFILE} "character(len=64), parameter :: buildTime = ''\n") - file(APPEND ${VERSIONFILE} "character(len=64), parameter :: gitBranch = '${GITBRCH}'\n") - file(APPEND ${VERSIONFILE} "character(len=64), parameter :: gitHash = '${GITHASH}'") - - add_library(SUMMA_NOAHMP OBJECT ${NOAHMP} ${NRUTIL}) - target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) - add_library(SUMMA_COMM OBJECT ${COMM_ALL}) - target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) - target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES}) - target_link_libraries(SUMMA_COMM PUBLIC ${LIBRARIES}) - -# Build SUMMA Shared Library, add BMI libraries outside this function +file(WRITE ${VERSIONFILE} "character(len=64), parameter :: summaVersion = '${VERSION}'\n") +file(APPEND ${VERSIONFILE} "character(len=64), parameter :: buildTime = ''\n") +file(APPEND ${VERSIONFILE} "character(len=64), parameter :: gitBranch = '${GITBRCH}'\n") +file(APPEND ${VERSIONFILE} "character(len=64), parameter :: gitHash = '${GITHASH}'") + +# Build NOAH_MP Object +add_library(SUMMA_NOAHMP OBJECT ${NOAHMP} ${NRUTIL}) +target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) +#target_link_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) + +# Build SUMMA_COMM Object +add_library(SUMMA_COMM OBJECT ${COMM_ALL}) +target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) +target_link_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) +target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES}) +#target_link_libraries(SUMMA_COMM PUBLIC ${LIBRARIES}) + +# For NexGen, build SUMMA Shared Library and add the outside BMI libraries +if(CMAKE_BUILD_TYPE MATCHES NexGen) if(WIN32) add_library(summabmi ${SUMMA_ALL}) else() @@ -439,21 +543,58 @@ message("\nBuilding SUMMA\n") target_include_directories(summabmi PUBLIC ${INCLUDES}) target_link_libraries(summabmi PUBLIC ${LIBRARIES} SUMMA_COMM) endif() + target_link_libraries(summabmi PUBLIC iso_c_bmi) + target_compile_options(summabmi PUBLIC -cpp -DNGEN_ACTIVE) + set_target_properties(summabmi PROPERTIES VERSION ${PROJECT_VERSION}) + include(GNUInstallDirs) + + install(TARGETS summabmi + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + configure_file(summabmi.pc.in summabmi.pc @ONLY) + install(FILES ${CMAKE_BINARY_DIR}/summabmi.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) + +# For Actors, build SUMMA Shared Library add the actors libraries and add the executable +elseif(CMAKE_BUILD_TYPE MATCHES Actors) + add_library(summaactors SHARED ${SUMMA_ALL}) + target_compile_options(summaactors PRIVATE ${FLAGS_ALL}) -#endfunction() - - -target_link_libraries(summabmi PUBLIC iso_c_bmi) -target_compile_options(summabmi PUBLIC -cpp -DNGEN_ACTIVE) - -set_target_properties(summabmi PROPERTIES VERSION ${PROJECT_VERSION}) - -include(GNUInstallDirs) + if(SUNDIALS_ACTIVE) + target_include_directories(summaactors PUBLIC ${INCLUDES} ${INC_SUNDIALS}) + target_link_libraries(summaactors PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_COMM) + else() + target_include_directories(summaactors PUBLIC ${INCLUDES}) + target_link_libraries(summaactors PUBLIC ${LIBRARIES} SUMMA_COMM) + endif() -install(TARGETS summabmi - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + set_target_properties(summaactors PROPERTIES VERSION ${PROJECT_VERSION}) + add_executable(${EXEC_NAME} ${MAIN_ACTOR} + ${ACTORS_GLOBAL} + ${HRU_ACTOR} + ${GRU_ACTOR} + ${FILE_ACCESS_ACTOR} + ${JOB_ACTOR} + ${SUMMA_ACTOR} + ${SUMMA_CLIENT} + ${SUMMA_SERVER}) + set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) + target_include_directories(${EXEC_NAME} PUBLIC ${INC_ACTORS}) + target_link_libraries( ${EXEC_NAME} ${LIB_ACTORS}) + +# For other cases, build SUMMA Shared Library and add the executable +else() + add_library(summa SHARED ${SUMMA_ALL}) + target_compile_options(summa PRIVATE ${FLAGS_ALL}) -configure_file(summabmi.pc.in summabmi.pc @ONLY) + if(SUNDIALS_ACTIVE) + target_include_directories(summa PUBLIC ${INCLUDES} ${INC_SUNDIALS}) + target_link_libraries(summa PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_COMM) + else() + target_include_directories(summa PUBLIC ${INCLUDES}) + target_link_libraries(summa PUBLIC ${LIBRARIES} SUMMA_COMM) + endif() -install(FILES ${CMAKE_BINARY_DIR}/summabmi.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) + set_target_properties(summa PROPERTIES VERSION ${PROJECT_VERSION}) + add_executable(${EXEC_NAME} ${MAIN_SUMMA}) + set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) +endif() diff --git a/build/cmake_ngen/README.md b/build/cmake/README_ngen.md similarity index 94% rename from build/cmake_ngen/README.md rename to build/cmake/README_ngen.md index 7f5c6a175..96942c04e 100644 --- a/build/cmake_ngen/README.md +++ b/build/cmake/README_ngen.md @@ -51,7 +51,7 @@ First, cd into the outer directory containing the submodule: cd extern/summa -If you want to use Sundials, set -DSUNDIALS_ACTIVE:BOOL=ON in the build script. Then, before summa can be built, Sundials needs to be installed. +If you want to use Sundials_IDA, set -DCMAKE_BUILD_TYPE=IDA_NexGen in the build script. Then, before summa can be built, Sundials needs to be installed. Download the latest release of IDA solver from SUNDIALS package in https://computing.llnl.gov/projects/sundials/sundials-software wget "https://github.com/LLNL/sundials/releases/download/v6.3.0/sundials-6.3.0.tar.gz" @@ -73,11 +73,11 @@ Before ngen library files can be built, a CMake build system must be generated. cmake -B cmake_build -S . -Note (similar to above) that when there is an existing directory, it may sometimes be necessary to clear it and regenerate, especially if any changes were made to the [CMakeLists.txt](CMakeLists.txt) file. +Note (similar to above) that when there is an existing directory, it may sometimes be necessary to clear it and regenerate, especially if any changes were made to the CMakeLists.txt file. After there is build system directory, the shared library can be built using the `summabmi` CMake target. For example, the SummaSundials shared library file (i.e., the build config's `summabmi` target) can be built using: - cmake --build cmake_build --target summabmi -- -j 2 + cmake --build cmake_build --target all This will build a `cmake_build/libsummabmi..` file, where the version is configured within the CMake config, and the extension depends on the local machine's operating system. diff --git a/build/cmake/build.cluster.bash b/build/cmake/build.cluster.bash index 7dd492992..ad8163825 100644 --- a/build/cmake/build.cluster.bash +++ b/build/cmake/build.cluster.bash @@ -1,6 +1,6 @@ #!/bin/bash -# build on Copernicus or Graham, from summa directory put this one directory up and run this as ../build.cluster.bash +# build on Copernicus or Graham, from cmake directory run this as ./build.cluster.bash ./build.cluster.bash module load StdEnv/2020 module load gcc/9.3.0 @@ -14,9 +14,6 @@ module load udunits/2.2.28 # for Actors module load caf - cmake -B ../cmake_build -S ../../ -DCMAKE_BUILD_TYPE=IDA_Cluster -cmake --build ../cmake_build --target all - -make -C cmake_build +cmake -j ../cmake_build --target all diff --git a/build/cmake/build.mac.bash b/build/cmake/build.mac.bash index 68a0f4e0e..ad24267d8 100755 --- a/build/cmake/build.mac.bash +++ b/build/cmake/build.mac.bash @@ -1,8 +1,7 @@ #!/bin/bash -# build on Mac, from summa directory put this one directory up and run this as ../build.mac.bash +# build on Mac, from cmake directory run this as ./build.mac.bash -cmake -B cmake_build -S summa -DCMAKE_BUILD_TYPE=IDA -cmake --build summa/cmake_build --target all +cmake -B ../cmake_build -S summa -DCMAKE_BUILD_TYPE=IDA +cmake -j ../cmake_build --target all -make -C cmake_build diff --git a/build/cmake_ngen/build_ngen.cluster.bash b/build/cmake/build_ngen.cluster.bash similarity index 95% rename from build/cmake_ngen/build_ngen.cluster.bash rename to build/cmake/build_ngen.cluster.bash index 03910b579..21b981358 100644 --- a/build/cmake_ngen/build_ngen.cluster.bash +++ b/build/cmake/build_ngen.cluster.bash @@ -12,7 +12,7 @@ module load netcdf-fortran/4.5.2 cmake -B extern/iso_c_fortran_bmi/cmake_build -S extern/iso_c_fortran_bmi cmake --build extern/iso_c_fortran_bmi/cmake_build --target all -cmake -B extern/summa/cmake_build -S extern/summa -DCMAKE_BUILD_TYPE=Cluster -DSUNDIALS_ACTIVE:BOOL=OFF +cmake -B extern/summa/cmake_build -S extern/summa -DCMAKE_BUILD_TYPE=BE_NexGen_Cluster cmake --build extern/summa/cmake_build --target all cmake -B cmake_build -S . -DMPI_ACTIVE:BOOL=OFF -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON diff --git a/build/cmake_ngen/build_ngen.mac.bash b/build/cmake/build_ngen.mac.bash similarity index 94% rename from build/cmake_ngen/build_ngen.mac.bash rename to build/cmake/build_ngen.mac.bash index 33250bd82..87cc77414 100755 --- a/build/cmake_ngen/build_ngen.mac.bash +++ b/build/cmake/build_ngen.mac.bash @@ -5,7 +5,7 @@ cmake -B extern/iso_c_fortran_bmi/cmake_build -S extern/iso_c_fortran_bmi cmake --build extern/iso_c_fortran_bmi/cmake_build --target all -cmake -B extern/summa/cmake_build -S extern/summa -DCMAKE_BUILD_TYPE=Release -DSUNDIALS_ACTIVE:BOOL=OFF +cmake -B extern/summa/cmake_build -S extern/summa -DCMAKE_BUILD_TYPE=BE_NexGen cmake --build extern/summa/cmake_build --target all cmake -B cmake_build -S . -DMPI_ACTIVE:BOOL=OFF -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON diff --git a/build/cmake_ngen/CMakeLists.txt b/build/cmake_ngen/CMakeLists.txt deleted file mode 100644 index ac4a0b795..000000000 --- a/build/cmake_ngen/CMakeLists.txt +++ /dev/null @@ -1,350 +0,0 @@ -cmake_minimum_required(VERSION 3.10) -enable_language( Fortran ) - -#Get the iso_c_fortran binding module to build as part of this build -add_subdirectory(../iso_c_fortran_bmi ${CMAKE_BINARY_DIR}/iso_c_bmi) - - -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../../cmake/") - -project(summabmi VERSION 1.0.0 DESCRIPTION "Summa-Sundials BMI Module Shared Library") - -set( CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran) - -#### Add variables for individual libraries that are used within the *.pc.in files -set(SUMMA_LIB_NAME_CMAKE summabmi) -set(SUMMA_LIB_DESC_CMAKE "Summa-Sundials BMI Module Shared Library") - -# Make sure these are compiled with this directive -add_compile_definitions(BMI_ACTIVE NGEN_FORCING_ACTIVE NGEN_OUTPUT_ACTIVE NGEN_ACTIVE) - - -set(ROOT_DIR ./) -set(F_MASTER ${ROOT_DIR}/summa) - -SET (CMAKE_Fortran_COMPILER gfortran) - -#include(bmi.cmake) -# -------------------------------------------------------------------------------------------- -# When compiling Summa-Sundials it can be compiled in a Cluster or Debug mode, which is set by -# the CMAKE_BUILD_TYPES variables. The default is Release, cluster mode. - -######### SET THE PATHS TO THE SUNDIALS LIBRARIES AND INCLUDE FILES ######### -############################################################################# - -# Add options for build type -set(CMAKE_BUILD_TYPES Debug Release Cluster_Debug Cluster) - -# Set default configuration type to Release -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Cluster) -endif() - -# Set compiler flags -if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) - message("Debug build.") - add_definitions(-DDEBUG) - set(FLAGS_NOAH -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) - set(FLAGS_ALL -g -O0 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) -else() - message("Release build.") - set(FLAGS_NOAH -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) - set(FLAGS_ALL -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) -endif() - -set(DIR_SUNDIALS ${ROOT_DIR}/sundials/instdir) - -if(CMAKE_BUILD_TYPE MATCHES Cluster OR CMAKE_BUILD_TYPE MATCHES Cluster_Debug) - - # MKL needs BLA_VENDOR set with cmake - set(BLA_VENDOR OpenBLAS) - - # Packages that are required - find_package(NetCDF REQUIRED) - find_package(LAPACK REQUIRED) - - # Set include directories - set(INCLUDES $ENV{EBROOTNETCDFMINFORTRAN}/include ${netCDF_INCLUDES} ${LAPACK_INCLUDES}) - set(LIBRARIES SUMMA_NOAHMP ${netCDF_LIBRARIES} ${LAPACK_LIBRARIES} -lnetcdff -lopenblas) - - if(SUNDIALS_ACTIVE) - message("\nBuilding IDA\n") - - link_directories(${DIR_SUNDIALS}/lib64) - set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib64") - set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran) - set(LIB_SUNDIALS -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) - endif() - -else() - - set(SDKROOT "$(xcrun --show-sdk-path)") - - link_directories(/opt/local/lib) - set(INCLUDES /opt/local/include /opt/local/lib) - set(LIBRARIES SUMMA_NOAHMP -llapack -lgfortran -lnetcdff -lnetcdf) - - if(SUNDIALS_ACTIVE) - message("\nBuilding IDA\n") - - link_directories(${DIR_SUNDIALS}/lib) - set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib") - set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran) - set(LIB_SUNDIALS -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) - endif() - -endif() - - -message("\nBuilding SUMMA\n") - - -#compile_with_bmi(${F_MASTER}, ${DIR_SUNDIALS}, ${INCLUDES}, ${LIBRARIES}, ${INC_SUNDIALS}, ${LIB_SUNDIALS}, ${FLAGS_ALL}, ${FLAGS_NOAH}) -#function(compile_with_bmi F_MASTER, DIR_SUNDIALS, INCLUDES, LIBRARIES, INC_SUNDIALS, LIB_SUNDIALS, FLAGS_ALL, FLAGS_NOAH) - -#======================================================================== -# PART 1: Define directory paths -#======================================================================== - -# Core directory that contains source code - set(F_KORE_DIR ${F_MASTER}/build/source) - -# Define directories - set(DRIVER_DIR ${F_KORE_DIR}/driver) - set(HOOKUP_DIR ${F_KORE_DIR}/hookup) - set(NETCDF_DIR ${F_KORE_DIR}/netcdf) - set(DSHARE_DIR ${F_KORE_DIR}/dshare) - set(NUMREC_DIR ${F_KORE_DIR}/numrec) - set(NOAHMP_DIR ${F_KORE_DIR}/noah-mp) - set(ENGINE_DIR ${F_KORE_DIR}/engine) - -#======================================================================== -# PART 2: Assemble all of the SUMMA sub-routines -#======================================================================== - -# utilities - set(NRUTIL - ${ENGINE_DIR}/nrtype.f90 - ${ENGINE_DIR}/f2008funcs.f90 - ${ENGINE_DIR}/nr_utility.f90) - -# Numerical recipes procedures -# NOTE: all numerical recipes procedures are now replaced with free versions - set(NRPROC - ${ENGINE_DIR}/expIntegral.f90 - ${ENGINE_DIR}/spline_int.f90) - -# Hook-up modules - set(HOOKUP - ${HOOKUP_DIR}/ascii_util.f90 - ${HOOKUP_DIR}/summaFileManager.f90) - -# Data modules - set(DATAMS - ${DSHARE_DIR}/multiconst.f90 - ${DSHARE_DIR}/var_lookup.f90 - ${DSHARE_DIR}/data_types.f90 - ${DSHARE_DIR}/globalData.f90 - ${DSHARE_DIR}/flxMapping.f90 - ${DSHARE_DIR}/get_ixname.f90 - ${DSHARE_DIR}/popMetadat.f90 - ${DSHARE_DIR}/outpt_stat.f90) - -# utility modules - set(UTILMS - ${ENGINE_DIR}/time_utils.f90 - ${ENGINE_DIR}/mDecisions.f90 - ${ENGINE_DIR}/snow_utils.f90 - ${ENGINE_DIR}/soil_utils.f90 - ${ENGINE_DIR}/updatState.f90 - ${ENGINE_DIR}/matrixOper.f90 - ${ENGINE_DIR}/soil_utilsAddSundials.f90 - ${ENGINE_DIR}/updatStateSundials.f90) - -# Solver - set(SOLVER - ${ENGINE_DIR}/vegPhenlgy.f90 - ${ENGINE_DIR}/diagn_evar.f90 - ${ENGINE_DIR}/stomResist.f90 - ${ENGINE_DIR}/groundwatr.f90 - ${ENGINE_DIR}/vegSWavRad.f90 - ${ENGINE_DIR}/vegNrgFlux.f90 - ${ENGINE_DIR}/ssdNrgFlux.f90 - ${ENGINE_DIR}/vegLiqFlux.f90 - ${ENGINE_DIR}/snowLiqFlx.f90 - ${ENGINE_DIR}/soilLiqFlx.f90 - ${ENGINE_DIR}/bigAquifer.f90 - ${ENGINE_DIR}/computFlux.f90 - ${ENGINE_DIR}/computEnthalpy.f90 - ${ENGINE_DIR}/computHeatCap.f90 - ${ENGINE_DIR}/computThermConduct.f90 - ${ENGINE_DIR}/computResid.f90 - ${ENGINE_DIR}/computJacob.f90 - ${ENGINE_DIR}/eval8summa.f90 - ${ENGINE_DIR}/summaSolve.f90 - ${ENGINE_DIR}/systemSolv.f90 - ${ENGINE_DIR}/computSnowDepth.f90 - ${ENGINE_DIR}/varSubstep.f90 - ${ENGINE_DIR}/opSplittin.f90 - ${ENGINE_DIR}/coupled_em.f90 - ${ENGINE_DIR}/run_oneHRU.f90 - ${ENGINE_DIR}/run_oneGRU.f90) - set(SOLVER_SUN - ${ENGINE_DIR}/type4IDA.f90 - ${ENGINE_DIR}/tol4IDA.f90 - ${ENGINE_DIR}/computResidSundials.f90 - ${ENGINE_DIR}/eval8summaSundials.f90 - ${ENGINE_DIR}/computJacobSundials.f90 - ${ENGINE_DIR}/summaSolveSundialsIDA.f90 - ${ENGINE_DIR}/systemSolvSundials.f90) - - -# Define routines for SUMMA preliminaries - set(PRELIM - ${ENGINE_DIR}/conv_funcs.f90 - ${ENGINE_DIR}/sunGeomtry.f90 - ${ENGINE_DIR}/convE2Temp.f90 - ${ENGINE_DIR}/allocspace.f90 - ${ENGINE_DIR}/checkStruc.f90 - ${ENGINE_DIR}/childStruc.f90 - ${ENGINE_DIR}/ffile_info.f90 - ${ENGINE_DIR}/read_attrb.f90 - ${ENGINE_DIR}/read_pinit.f90 - ${ENGINE_DIR}/pOverwrite.f90 - ${ENGINE_DIR}/read_param.f90 - ${ENGINE_DIR}/paramCheck.f90 - ${ENGINE_DIR}/check_icond.f90) - - set(NOAHMP - ${NOAHMP_DIR}/module_model_constants.F - ${NOAHMP_DIR}/module_sf_noahutl.F - ${NOAHMP_DIR}/module_sf_noahlsm.F - ${NOAHMP_DIR}/module_sf_noahmplsm.F) - -# Define routines for the SUMMA model runs - set(MODRUN - ${ENGINE_DIR}/indexState.f90 - ${ENGINE_DIR}/getVectorz.f90 - ${ENGINE_DIR}/t2enthalpy.f90 - ${ENGINE_DIR}/updateVars.f90 - ${ENGINE_DIR}/var_derive.f90 - ${ENGINE_DIR}/read_force.f90 - ${ENGINE_DIR}/derivforce.f90 - ${ENGINE_DIR}/snowAlbedo.f90 - ${ENGINE_DIR}/canopySnow.f90 - ${ENGINE_DIR}/tempAdjust.f90 - ${ENGINE_DIR}/snwCompact.f90 - ${ENGINE_DIR}/layerMerge.f90 - ${ENGINE_DIR}/layerDivide.f90 - ${ENGINE_DIR}/volicePack.f90 - ${ENGINE_DIR}/qTimeDelay.f90 - ${ENGINE_DIR}/updateVarsSundials.f90) - - -# Define NetCDF routines - set(NETCDF - ${NETCDF_DIR}/netcdf_util.f90 - ${NETCDF_DIR}/def_output.f90 - ${NETCDF_DIR}/modelwrite.f90 - ${NETCDF_DIR}/read_icond.f90) - -# Define the driver routine - set(DRIVER - ${DRIVER_DIR}/summa_type.f90 - ${DRIVER_DIR}/summa_util.f90 - ${DRIVER_DIR}/summa_alarms.f90 - ${DRIVER_DIR}/summa_globalData.f90 - ${DRIVER_DIR}/summa_defineOutput.f90 - ${DRIVER_DIR}/summa_init.f90 - ${DRIVER_DIR}/summa_setup.f90 - ${DRIVER_DIR}/summa_restart.f90 - ${DRIVER_DIR}/summa_forcing.f90 - ${DRIVER_DIR}/summa_modelRun.f90 - ${DRIVER_DIR}/summa_writeOutput.f90 - ${DRIVER_DIR}/summa_driver.f90) - - # run program files, do not use in ngen - # set(SUMMA ${DRIVER_DIR}/summa_run.f90) - # set(BMI ${DRIVER_DIR}/summa_runBMI.f90) - -# Define version number, not working correctly - set(VERSIONFILE ${DRIVER_DIR}/summaversion.inc) - execute_process(COMMAND "${GIT_EXECUTABLE} tag | tail -n 1" OUTPUT_VARIABLE VERSION) - execute_process(COMMAND "date" OUTPUT_VARIABLE BULTTIM) - execute_process(COMMAND "${GIT_EXECUTABLE} describe --long --all --always | sed -e's/heads\///'" OUTPUT_VARIABLE GITBRCH) - execute_process(COMMAND "${GIT_EXECUTABLE} rev-parse HEAD" OUTPUT_VARIABLE GITHASH) - - set(COMM_ALL - ${NRPROC} - ${HOOKUP} - ${DATAMS} - ${UTILMS}) - - if(SUNDIALS_ACTIVE) - add_compile_definitions(NGEN_SUNDIALS_ACTIVE) - set(SUMMA_ALL - ${NETCDF} - ${PRELIM} - ${MODRUN} - ${SOLVER} - ${DRIVER} - ${SOLVER_SUN}) - else() - set(SUMMA_ALL - ${NETCDF} - ${PRELIM} - ${MODRUN} - ${SOLVER} - ${DRIVER}) - endif() - -#======================================================================== -# PART 4: compilation -#====================================================================== - -# update version information, not working correctly - file(WRITE ${VERSIONFILE} "character(len=64), parameter :: summaVersion = '${VERSION}'\n") - file(APPEND ${VERSIONFILE} "character(len=64), parameter :: buildTime = ''\n") - file(APPEND ${VERSIONFILE} "character(len=64), parameter :: gitBranch = '${GITBRCH}'\n") - file(APPEND ${VERSIONFILE} "character(len=64), parameter :: gitHash = '${GITHASH}'") - - add_library(SUMMA_NOAHMP OBJECT ${NOAHMP} ${NRUTIL}) - target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) - add_library(SUMMA_COMM OBJECT ${COMM_ALL}) - target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) - target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES}) - target_link_libraries(SUMMA_COMM PUBLIC ${LIBRARIES}) - -# Build SUMMA Shared Library, add BMI libraries outside this function - if(WIN32) - add_library(summabmi ${SUMMA_ALL}) - else() - add_library(summabmi SHARED ${SUMMA_ALL}) - endif() - target_compile_options(summabmi PRIVATE ${FLAGS_ALL}) - - if(SUNDIALS_ACTIVE) - target_include_directories(summabmi PUBLIC ${INCLUDES} ${INC_SUNDIALS}) - target_link_libraries(summabmi PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_COMM) - else() - target_include_directories(summabmi PUBLIC ${INCLUDES}) - target_link_libraries(summabmi PUBLIC ${LIBRARIES} SUMMA_COMM) - endif() - -#endfunction() - - -target_link_libraries(summabmi PUBLIC iso_c_bmi) -target_compile_options(summabmi PUBLIC -cpp -DNGEN_ACTIVE) - -set_target_properties(summabmi PROPERTIES VERSION ${PROJECT_VERSION}) - -include(GNUInstallDirs) - -install(TARGETS summabmi - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - -configure_file(summabmi.pc.in summabmi.pc @ONLY) - -install(FILES ${CMAKE_BINARY_DIR}/summabmi.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) diff --git a/build/cmake_ngen/summabmi.pc.in b/build/cmake_ngen/summabmi.pc.in deleted file mode 100644 index f8810d6ff..000000000 --- a/build/cmake_ngen/summabmi.pc.in +++ /dev/null @@ -1,12 +0,0 @@ -prefix=@CMAKE_INSTALL_PREFIX@ -exec_prefix=@CMAKE_INSTALL_PREFIX@ -libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@ -includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ - -Name: @SUMMA_LIB_NAME_CMAKE@ -Description: @SUMMA_DESC_CMAKE@ -Version: @PROJECT_VERSION@ - -Requires: -Libs: -L${libdir} -lmylib -Cflags: -I${includedir} \ No newline at end of file diff --git a/build/makefiles/compile_copernicus b/build/makefiles/compile_copernicus deleted file mode 100755 index f818b6ced..000000000 --- a/build/makefiles/compile_copernicus +++ /dev/null @@ -1,17 +0,0 @@ -#! /bin/bash - -#### load modules if using Compute Canada or Copernicus #### -module load StdEnv/2020 -module load gcc/9.3.0 -module load openblas/0.3.17 -module load netcdf-fortran/4.5.2 - -#### parent directory of the 'build' directory #### -export ROOT_DIR=/project/gwf/gwf_cmt/ngen/ngen_code/ngen/extern/summa - -export INCLUDES="-I${EBROOTNETCDFMINFORTRAN}/include" -export LDFLAGS="-L${EBROOTNETCDFMINFORTRAN}/lib64 -L${EBROOTOPENBLAS}/lib" -export LIBRARIES="${LDFLAGS} -lnetcdff -lopenblas" - -make -f ${ROOT_DIR}/summa/build/makefiles/makefile_cluster -export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin diff --git a/build/makefiles/compile_mac b/build/makefiles/compile_mac deleted file mode 100755 index 4777dd2a3..000000000 --- a/build/makefiles/compile_mac +++ /dev/null @@ -1,11 +0,0 @@ -#! /bin/bash - -#### parent directory of the 'build' directory #### -export ROOT_DIR=/Users/amedin/Research/SummaBMI/ngen/extern/summa - -export INCLUDES="-I/opt/local/include -I/opt/local/lib" -export LDFLAGS="-L/opt/local/lib" -export LIBRARIES="${LDFLAGS} -llapack -lgfortran -lnetcdff -lnetcdf" - -make -f ${ROOT_DIR}/summa/build/makefiles/makefile_mac -export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin diff --git a/build/makefiles/makefile_cluster b/build/makefiles/makefile_cluster deleted file mode 100644 index 0e88ddf12..000000000 --- a/build/makefiles/makefile_cluster +++ /dev/null @@ -1,352 +0,0 @@ -# Define core directory below which everything resides. This is the -# parent directory of the 'build' directory -# ROOT_DIR = -F_MASTER = $(ROOT_DIR)/summa - -#### Compilers #### -FC = gfortran -FC_EXE = gfortran - -#### Includes AND Libraries #### -# INCLUDES = -# LDFLAGS = -# LIBRARIES = - -DIR_SUNDIALS= $(ROOT_DIR)/sundials/instdir -INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran -LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod - -DIR_BMI= $(ROOT_DIR)/bmi/instdir -INC_BMI=-I$(DIR_BMI)/include -LIB_BMI=-L$(DIR_BMI)/lib -lbmif - -# Production runs -FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors - -# Debug runs -# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC -# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC -# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC - -#======================================================================== -# PART 1: Define directory paths -#======================================================================== - -# Core directory that contains source code -F_KORE_DIR = $(F_MASTER)/build/source - -# Location of the compiled modules -MOD_PATH = $(F_MASTER)/build - -# Define the directory for the executables -EXE_PATH = $(F_MASTER)/bin - -#======================================================================== -# PART 2: Assemble all of the SUMMA sub-routines -#======================================================================== - -# Define directories -DRIVER_DIR = $(F_KORE_DIR)/driver -HOOKUP_DIR = $(F_KORE_DIR)/hookup -NETCDF_DIR = $(F_KORE_DIR)/netcdf -DSHARE_DIR = $(F_KORE_DIR)/dshare -NUMREC_DIR = $(F_KORE_DIR)/numrec -NOAHMP_DIR = $(F_KORE_DIR)/noah-mp -ENGINE_DIR = $(F_KORE_DIR)/engine - -# utilities -SUMMA_NRUTIL= \ - nrtype.f90 \ - f2008funcs.f90 \ - nr_utility.f90 -NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) - -# -# Numerical recipes procedures -# NOTE: all numerical recipes procedures are now replaced with free versions -SUMMA_NRPROC= \ - expIntegral.f90 \ - spline_int.f90 -NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) - -# Hook-up modules (set files and directory paths) -SUMMA_HOOKUP= \ - ascii_util.f90 \ - summaFileManager.f90 - -HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) - -# Data modules -SUMMA_DATAMS= \ - multiconst.f90 \ - var_lookup.f90 \ - data_types.f90 \ - globalData.f90 \ - flxMapping.f90 \ - get_ixname.f90 \ - popMetadat.f90 \ - outpt_stat.f90 -DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) - -# utility modules -SUMMA_UTILMS= \ - time_utils.f90 \ - mDecisions.f90 \ - snow_utils.f90 \ - soil_utils.f90 \ - soil_utilsAddSundials.f90 \ - updatState.f90 \ - updatStateSundials.f90 \ - matrixOper.f90 -UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) - -# Model guts -SUMMA_MODGUT= \ - MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) - -# Solver -SUMMA_SOLVER= \ - vegPhenlgy.f90 \ - diagn_evar.f90 \ - stomResist.f90 \ - groundwatr.f90 \ - vegSWavRad.f90 \ - vegNrgFlux.f90 \ - ssdNrgFlux.f90 \ - vegLiqFlux.f90 \ - snowLiqFlx.f90 \ - soilLiqFlx.f90 \ - bigAquifer.f90 \ - computFlux.f90 \ - type4IDA.f90 \ - tol4IDA.f90 \ - computEnthalpy.f90 \ - computHeatCap.f90 \ - computThermConduct.f90 \ - computResid.f90 \ - computJacob.f90 \ - eval8summa.f90 \ - summaSolve.f90 \ - systemSolv.f90 \ - computResidSundials.f90 \ - eval8summaSundials.f90 \ - computJacobSundials.f90 \ - computSnowDepth.f90 \ - summaSolveSundialsIDA.f90 \ - systemSolvSundials.f90 \ - varSubstep.f90 \ - opSplittin.f90 \ - coupled_em.f90 \ - run_oneHRU.f90 \ - run_oneGRU.f90 -SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) - -# Define routines for SUMMA preliminaries -SUMMA_PRELIM= \ - conv_funcs.f90 \ - sunGeomtry.f90 \ - convE2Temp.f90 \ - allocspace.f90 \ - checkStruc.f90 \ - childStruc.f90 \ - ffile_info.f90 \ - read_attrb.f90 \ - read_pinit.f90 \ - pOverwrite.f90 \ - read_param.f90 \ - paramCheck.f90 \ - check_icond.f90 -PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) - -SUMMA_NOAHMP= \ - module_model_constants.F \ - module_sf_noahutl.F \ - module_sf_noahlsm.F \ - module_sf_noahmplsm.F -NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) - -# Define routines for the SUMMA model runs -SUMMA_MODRUN = \ - indexState.f90 \ - getVectorz.f90 \ - t2enthalpy.f90 \ - updateVars.f90 \ - updateVarsSundials.f90 \ - var_derive.f90 \ - read_force.f90 \ - derivforce.f90 \ - snowAlbedo.f90 \ - canopySnow.f90 \ - tempAdjust.f90 \ - snwCompact.f90 \ - layerMerge.f90 \ - layerDivide.f90 \ - volicePack.f90 \ - qTimeDelay.f90 -MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) - -# Define routines for the solver -SUMMA_MSOLVE = \ - -# Define NetCDF routines -SUMMA_NETCDF = \ - netcdf_util.f90 \ - def_output.f90 \ - modelwrite.f90 \ - read_icond.f90 -NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) - -# ... stitch together common programs -COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) - -# ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) - -# Define the driver routines -SUMMA_DRIVER= \ - summa_type.f90 \ - summa_util.f90 \ - summa_alarms.f90 \ - summa_globalData.f90 \ - summa_defineOutput.f90 \ - summa_init.f90 \ - summa_setup.f90 \ - summa_restart.f90 \ - summa_forcing.f90 \ - summa_modelRun.f90 \ - summa_writeOutput.f90 \ - summa_driver.f90 -DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) - -# Define SUMMA running infrastructure -SUMMA_RUN = \ - summa_run.f90 -SUMMA = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_RUN)) - -# Define BMI testing infrastructure -BMI_TEST = \ - summa_runBMI.f90 -BMI = $(patsubst %, $(DRIVER_DIR)/%, $(BMI_TEST)) - -# Define the executable -DRIVER__EX = summa_sundials.exe -BMI__EX = summa_bmi.exe - -# Define version number -VERSIONFILE = $(DRIVER_DIR)/summaversion.inc -VERSION = $(shell git tag | tail -n 1) -BULTTIM = $(shell date) -GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') -GITHASH = $(shell git rev-parse HEAD) - -#======================================================================== -# PART 3: Checks -#====================================================================== -# make sure that the paths are defined. These are just some high level checks -ifndef F_MASTER - $(error F_MASTER is undefined) -endif -ifndef FC - $(error FC is undefined: Specify your compiler) -endif -ifndef FC_EXE - $(error FC_EXE is undefined: Specify your compiler executable) -endif -ifndef FLAGS_SUMMA - $(error Specify flags for your compiler: $(FC)) -endif -ifndef INCLUDES - $(error INCLUDES is undefined) -endif -ifndef LIBRARIES - $(error LIBRARIES is undefined) -endif - -#======================================================================== -# PART 4: compilation -#====================================================================== - -# Compile -all: compile_noah compile_comm compile_rout compile_test link_test clean_test compile_summa link clean install install_test -part: compile_noah compile_comm compile_rout -summa: compile_noah compile_comm compile_rout compile_summa link clean install -bmi: compile_noah compile_comm compile_rout compile_test link_test clean install_test - - -check: - $(info) - $(info Displaying make variables:) - $(info F_MASTER : $(F_MASTER)) - $(info EXE_PATH : $(EXE_PATH)) - $(info FC : $(FC)) - $(info INCLUDES : $(INCLUDES)) - $(info LIBRARIES : $(LIBRARIES)) - $(info FLAGS_NOAH : $(FLAGS_NOAH)) - $(info FLAGS_COMM : $(FLAGS_COMM)) - $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) - $(info SUNDIALSDIR: $(DIR_SUNDIALS)) - $(info INC_SUNDIALS: $(INC_SUNDIALS)) - $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) - $(info BMIDIR: $(DIR_BMI)) - $(info INC_BMI: $(INC_BMI)) - $(info LIB_BMI: $(LIB_BMI)) - $(info) - -# update version information -update_version: - echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) - echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) - -# compile common routines -compile_comm: - $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) - -# compile Noah-MP routines -compile_noah: - $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) - -# compile SUMMA routines -compile_rout: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) - -# compile run scripts -compile_summa: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA) - -# compile test scripts -compile_test: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(BMI) - -# link run routines -link: - $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(DRIVER__EX) - -# link test routines -link_test: - $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(BMI__EX) - -# remove test script objects -clean_test: - rm -f summa_runBMI.o - -# Remove object files -clean: - rm -f *.o - rm -f *.mod - rm -f soil_veg_gen_parm__genmod.f90 - -# Copy the executable to the bin directory -install: - @mkdir -p $(EXE_PATH) - @mv $(DRIVER__EX) $(EXE_PATH) - $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) - -# Copy the executable to the bin directory -install_test: - @mkdir -p $(EXE_PATH) - @mv $(BMI__EX) $(EXE_PATH) - $(info $(BMI__EX) successfully installed in $(EXE_PATH)) diff --git a/build/makefiles/makefile_mac b/build/makefiles/makefile_mac deleted file mode 100644 index bb4c48732..000000000 --- a/build/makefiles/makefile_mac +++ /dev/null @@ -1,352 +0,0 @@ -# Define core directory below which everything resides. This is the -# parent directory of the 'build' directory -# ROOT_DIR = -F_MASTER = $(ROOT_DIR)/summa - -#### Compilers #### -FC = gfortran -FC_EXE =/opt/local/bin/gfortran - -#### Includes AND Libraries #### -# INCLUDES = -# LDFLAGS = -# LIBRARIES = - -DIR_SUNDIALS= $(ROOT_DIR)/sundials/instdir -INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran -LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod - -DIR_BMI= $(ROOT_DIR)/bmi/instdir -INC_BMI=-I$(DIR_BMI)/include -LIB_BMI=-L$(DIR_BMI)/lib -lbmif -Xlinker -rpath -Xlinker $(DIR_BMI)/lib - -# Production runs -FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors - -# Debug runs -# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC -# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC -# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC - -#======================================================================== -# PART 1: Define directory paths -#======================================================================== - -# Core directory that contains source code -F_KORE_DIR = $(F_MASTER)/build/source - -# Location of the compiled modules -MOD_PATH = $(F_MASTER)/build - -# Define the directory for the executables -EXE_PATH = $(F_MASTER)/bin - -#======================================================================== -# PART 2: Assemble all of the SUMMA sub-routines -#======================================================================== - -# Define directories -DRIVER_DIR = $(F_KORE_DIR)/driver -HOOKUP_DIR = $(F_KORE_DIR)/hookup -NETCDF_DIR = $(F_KORE_DIR)/netcdf -DSHARE_DIR = $(F_KORE_DIR)/dshare -NUMREC_DIR = $(F_KORE_DIR)/numrec -NOAHMP_DIR = $(F_KORE_DIR)/noah-mp -ENGINE_DIR = $(F_KORE_DIR)/engine - -# utilities -SUMMA_NRUTIL= \ - nrtype.f90 \ - f2008funcs.f90 \ - nr_utility.f90 -NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) - -# -# Numerical recipes procedures -# NOTE: all numerical recipes procedures are now replaced with free versions -SUMMA_NRPROC= \ - expIntegral.f90 \ - spline_int.f90 -NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) - -# Hook-up modules (set files and directory paths) -SUMMA_HOOKUP= \ - ascii_util.f90 \ - summaFileManager.f90 - -HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) - -# Data modules -SUMMA_DATAMS= \ - multiconst.f90 \ - var_lookup.f90 \ - data_types.f90 \ - globalData.f90 \ - flxMapping.f90 \ - get_ixname.f90 \ - popMetadat.f90 \ - outpt_stat.f90 -DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) - -# utility modules -SUMMA_UTILMS= \ - time_utils.f90 \ - mDecisions.f90 \ - snow_utils.f90 \ - soil_utils.f90 \ - soil_utilsAddSundials.f90 \ - updatState.f90 \ - updatStateSundials.f90 \ - matrixOper.f90 -UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) - -# Model guts -SUMMA_MODGUT= \ - MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) - -# Solver -SUMMA_SOLVER= \ - vegPhenlgy.f90 \ - diagn_evar.f90 \ - stomResist.f90 \ - groundwatr.f90 \ - vegSWavRad.f90 \ - vegNrgFlux.f90 \ - ssdNrgFlux.f90 \ - vegLiqFlux.f90 \ - snowLiqFlx.f90 \ - soilLiqFlx.f90 \ - bigAquifer.f90 \ - computFlux.f90 \ - type4IDA.f90 \ - tol4IDA.f90 \ - computEnthalpy.f90 \ - computHeatCap.f90 \ - computThermConduct.f90 \ - computResid.f90 \ - computJacob.f90 \ - eval8summa.f90 \ - summaSolve.f90 \ - systemSolv.f90 \ - computResidSundials.f90 \ - eval8summaSundials.f90 \ - computJacobSundials.f90 \ - computSnowDepth.f90 \ - summaSolveSundialsIDA.f90 \ - systemSolvSundials.f90 \ - varSubstep.f90 \ - opSplittin.f90 \ - coupled_em.f90 \ - run_oneHRU.f90 \ - run_oneGRU.f90 -SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) - -# Define routines for SUMMA preliminaries -SUMMA_PRELIM= \ - conv_funcs.f90 \ - sunGeomtry.f90 \ - convE2Temp.f90 \ - allocspace.f90 \ - checkStruc.f90 \ - childStruc.f90 \ - ffile_info.f90 \ - read_attrb.f90 \ - read_pinit.f90 \ - pOverwrite.f90 \ - read_param.f90 \ - paramCheck.f90 \ - check_icond.f90 -PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) - -SUMMA_NOAHMP= \ - module_model_constants.F \ - module_sf_noahutl.F \ - module_sf_noahlsm.F \ - module_sf_noahmplsm.F -NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) - -# Define routines for the SUMMA model runs -SUMMA_MODRUN = \ - indexState.f90 \ - getVectorz.f90 \ - t2enthalpy.f90 \ - updateVars.f90 \ - updateVarsSundials.f90 \ - var_derive.f90 \ - read_force.f90 \ - derivforce.f90 \ - snowAlbedo.f90 \ - canopySnow.f90 \ - tempAdjust.f90 \ - snwCompact.f90 \ - layerMerge.f90 \ - layerDivide.f90 \ - volicePack.f90 \ - qTimeDelay.f90 -MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) - -# Define routines for the solver -SUMMA_MSOLVE = \ - -# Define NetCDF routines -SUMMA_NETCDF = \ - netcdf_util.f90 \ - def_output.f90 \ - modelwrite.f90 \ - read_icond.f90 -NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) - -# ... stitch together common programs -COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) - -# ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) - -# Define the driver routines -SUMMA_DRIVER= \ - summa_type.f90 \ - summa_util.f90 \ - summa_alarms.f90 \ - summa_globalData.f90 \ - summa_defineOutput.f90 \ - summa_init.f90 \ - summa_setup.f90 \ - summa_restart.f90 \ - summa_forcing.f90 \ - summa_modelRun.f90 \ - summa_writeOutput.f90 \ - summa_driver.f90 -DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) - -# Define SUMMA running infrastructure -SUMMA_RUN = \ - summa_run.f90 -SUMMA = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_RUN)) - -# Define BMI testing infrastructure -BMI_TEST = \ - summa_runBMI.f90 -BMI = $(patsubst %, $(DRIVER_DIR)/%, $(BMI_TEST)) - -# Define the executable -DRIVER__EX = summa_sundials.exe -BMI__EX = summa_bmi.exe - -# Define version number -VERSIONFILE = $(DRIVER_DIR)/summaversion.inc -VERSION = $(shell git tag | tail -n 1) -BULTTIM = $(shell date) -GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') -GITHASH = $(shell git rev-parse HEAD) - -#======================================================================== -# PART 3: Checks -#====================================================================== -# make sure that the paths are defined. These are just some high level checks -ifndef F_MASTER - $(error F_MASTER is undefined) -endif -ifndef FC - $(error FC is undefined: Specify your compiler) -endif -ifndef FC_EXE - $(error FC_EXE is undefined: Specify your compiler executable) -endif -ifndef FLAGS_SUMMA - $(error Specify flags for your compiler: $(FC)) -endif -ifndef INCLUDES - $(error INCLUDES is undefined) -endif -ifndef LIBRARIES - $(error LIBRARIES is undefined) -endif - -#======================================================================== -# PART 4: compilation -#====================================================================== - -# Compile -all: compile_noah compile_comm compile_rout compile_test link_test clean_test compile_summa link clean install install_test -part: compile_noah compile_comm compile_rout -summa: compile_noah compile_comm compile_rout compile_summa link clean install -bmi: compile_noah compile_comm compile_rout compile_test link_test clean install_test - - -check: - $(info) - $(info Displaying make variables:) - $(info F_MASTER : $(F_MASTER)) - $(info EXE_PATH : $(EXE_PATH)) - $(info FC : $(FC)) - $(info INCLUDES : $(INCLUDES)) - $(info LIBRARIES : $(LIBRARIES)) - $(info FLAGS_NOAH : $(FLAGS_NOAH)) - $(info FLAGS_COMM : $(FLAGS_COMM)) - $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) - $(info SUNDIALSDIR: $(DIR_SUNDIALS)) - $(info INC_SUNDIALS: $(INC_SUNDIALS)) - $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) - $(info BMIDIR: $(DIR_BMI)) - $(info INC_BMI: $(INC_BMI)) - $(info LIB_BMI: $(LIB_BMI)) - $(info) - -# update version information -update_version: - echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) - echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) - -# compile common routines -compile_comm: - $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) - -# compile Noah-MP routines -compile_noah: - $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) - -# compile SUMMA routines -compile_rout: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) - -# compile run scripts -compile_summa: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA) - -# compile test scripts -compile_test: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(BMI) - -# link run routines -link: - $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(DRIVER__EX) - -# link test routines -link_test: - $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(BMI__EX) - -# remove test script objects -clean_test: - rm -f summa_runBMI.o - -# Remove object files -clean: - rm -f *.o - rm -f *.mod - rm -f soil_veg_gen_parm__genmod.f90 - -# Copy the executable to the bin directory -install: - @mkdir -p $(EXE_PATH) - @mv $(DRIVER__EX) $(EXE_PATH) - $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) - -# Copy the executable to the bin directory -install_test: - @mkdir -p $(EXE_PATH) - @mv $(BMI__EX) $(EXE_PATH) - $(info $(BMI__EX) successfully installed in $(EXE_PATH)) diff --git a/build/source/driver/summa_bmi.f90 b/build/source/driver/summa_bmi.f90 new file mode 100644 index 000000000..5d89744b7 --- /dev/null +++ b/build/source/driver/summa_bmi.f90 @@ -0,0 +1,1257 @@ +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . + +module summa_bmi + ! provides functions needed for summa driver routines adding BMI functions + ! ***************************************************************************** + ! * use desired modules + ! ***************************************************************************** + ! data types + USE,intrinsic :: iso_c_binding, only: c_ptr, c_loc, c_f_pointer + USE nrtype ! variable types, etc. +! NGEN_ACTIVE is to be set when running in the Nextgen framework +! https://github.com/NOAA-OWP/ngen +#ifdef NGEN_ACTIVE + use bmif_2_0_iso ! BMI libraries NexGen +#else + use bmif_2_0 ! BMI libraries standard +#endif + USE summa_type, only: summa1_type_dec ! master summa data type + ! subroutines and functions: model setup + USE summa_init, only: summa_initialize ! used to allocate/initialize summa data structures + USE summa_setup, only: summa_paramSetup ! used to initialize parameter data structures (e.g. vegetation and soil parameters) + USE summa_restart, only: summa_readRestart ! used to read restart data and reset the model state + ! subroutines and functions: model simulation + USE summa_forcing, only: summa_readForcing ! used to read forcing data + USE summa_modelRun, only: summa_runPhysics ! used to run the summa physics for one time step + USE summa_writeOutput, only: summa_writeOutputFiles ! used to write the summa output files + ! utility functions + USE summa_util, only: stop_program ! used to stop the summa program (with errors) + USE summa_util, only: handle_err ! used to process errors + ! global data + USE globalData, only: numtim ! number of model time steps + USE globalData, only: print_step_freq + USE globalData, only: dJulianStart ! julian day of start time of simulation + USE globalData, only: dJulianFinsh ! julian day of end time of simulation + USE globalData, only: data_step ! length of time steps for the outermost timeloop + USE globalData, only: gru_struc ! gru-hru mapping structures + USE multiconst, only: secprday ! number of seconds in a day + ! provide access to the named variables that describe elements of parent model structures + USE var_lookup, only: iLookTIME ! named variables for time data structure + USE var_lookup, only: iLookATTR ! named variables for real valued attribute data structure + USE var_lookup, only: iLookFORCE ! named variables for forcing data structure + USE var_lookup, only: iLookFLUX ! named variables for local flux variables + USE var_lookup, only: iLookDIAG ! named variables for local diagnostic variables + USE var_lookup, only: iLookPROG ! named variables for local prognostic variables + + implicit none + + ! Define the attributes of the model. + type :: summa_model + integer(i4b) :: timeStep ! index of model time step + type(summa1_type_dec), allocatable :: summa1_struc(:) + end type summa_model + + type, extends (bmi) :: summa_bmi + private + type (summa_model) :: model + contains + procedure :: get_component_name => summa_component_name + procedure :: get_input_item_count => summa_input_item_count + procedure :: get_output_item_count => summa_output_item_count + procedure :: get_input_var_names => summa_input_var_names + procedure :: get_output_var_names => summa_output_var_names + procedure :: initialize => summa_bmi_initialize + procedure :: finalize => summa_finalize + procedure :: get_start_time => summa_start_time + procedure :: get_end_time => summa_end_time + procedure :: get_current_time => summa_current_time + procedure :: get_time_step => summa_time_step + procedure :: get_time_units => summa_time_units + procedure :: update => summa_update + procedure :: update_until => summa_update_until + procedure :: get_var_grid => summa_var_grid + procedure :: get_grid_type => summa_grid_type + procedure :: get_grid_rank => summa_grid_rank + procedure :: get_grid_shape => summa_grid_shape + procedure :: get_grid_size => summa_grid_size + procedure :: get_grid_spacing => summa_grid_spacing + procedure :: get_grid_origin => summa_grid_origin + procedure :: get_grid_x => summa_grid_x + procedure :: get_grid_y => summa_grid_y + procedure :: get_grid_z => summa_grid_z + procedure :: get_grid_node_count => summa_grid_node_count + procedure :: get_grid_edge_count => summa_grid_edge_count + procedure :: get_grid_face_count => summa_grid_face_count + procedure :: get_grid_edge_nodes => summa_grid_edge_nodes + procedure :: get_grid_face_edges => summa_grid_face_edges + procedure :: get_grid_face_nodes => summa_grid_face_nodes + procedure :: get_grid_nodes_per_face => summa_grid_nodes_per_face + procedure :: get_var_type => summa_var_type + procedure :: get_var_units => summa_var_units + procedure :: get_var_itemsize => summa_var_itemsize + procedure :: get_var_nbytes => summa_var_nbytes + procedure :: get_var_location => summa_var_location + procedure :: get_value_int => summa_get_int + procedure :: get_value_float => summa_get_float + procedure :: get_value_double => summa_get_double + generic :: get_value => & + get_value_int, & + get_value_float, & + get_value_double + procedure :: get_value_ptr_int => summa_get_ptr_int + procedure :: get_value_ptr_float => summa_get_ptr_float + procedure :: get_value_ptr_double => summa_get_ptr_double + generic :: get_value_ptr => & + get_value_ptr_int, & + get_value_ptr_float, & + get_value_ptr_double + procedure :: get_value_at_indices_int => summa_get_at_indices_int + procedure :: get_value_at_indices_float => summa_get_at_indices_float + procedure :: get_value_at_indices_double => summa_get_at_indices_double + generic :: get_value_at_indices => & + get_value_at_indices_int, & + get_value_at_indices_float, & + get_value_at_indices_double + procedure :: set_value_int => summa_set_int + procedure :: set_value_float => summa_set_float + procedure :: set_value_double => summa_set_double + generic :: set_value => & + set_value_int, & + set_value_float, & + set_value_double + procedure :: set_value_at_indices_int => summa_set_at_indices_int + procedure :: set_value_at_indices_float => summa_set_at_indices_float + procedure :: set_value_at_indices_double => summa_set_at_indices_double + generic :: set_value_at_indices => & + set_value_at_indices_int, & + set_value_at_indices_float, & + set_value_at_indices_double + end type summa_bmi + + private + public :: summa_bmi + + ! ***************************************************************************** + ! * variable definitions + ! ***************************************************************************** + character (len=BMI_MAX_COMPONENT_NAME), target :: & + component_name = "Structure for Unifying Multiple Modeling Alternatives: SUMMA" + ! define parameters for the model simulation + integer(i4b), parameter :: n=1 ! number of instantiations + ! Exchange items +#ifdef NGEN_ACTIVE + integer, parameter :: input_item_count = 8 +#else + integer, parameter :: input_item_count = 7 +#endif + integer, parameter :: output_item_count = 16 + character (len=BMI_MAX_VAR_NAME), target,dimension(input_item_count) :: input_items + character (len=BMI_MAX_VAR_NAME), target,dimension(output_item_count) :: output_items + ! --------------------------------------------------------------------------------------- + + contains + + ! ***************************************************************************** + ! * model setup/initialization + ! ***************************************************************************** + function summa_bmi_initialize(this, config_file) result (bmi_status) + class (summa_bmi), intent(out) :: this + character (len=*), intent(in) :: config_file + ! error control + integer(i4b) :: err=0 ! error code + character(len=1024) :: message='' ! error message + character(len=1024) :: file_manager + integer :: bmi_status, i,fu,rc + ! namelist definition + namelist /parameters/ file_manager + + ! initialize time steps + this%model%timeStep = 0 + + ! allocate space for the master summa structure, could happen outside of BMI function + allocate(this%model%summa1_struc(n), stat=err) + if(err/=0) call stop_program(1, 'problem allocating master summa structure') + + ! if using the BMI interface, there is an argument pointing to the file manager file + ! then make sure summaFileManagerFile is set before executing initialization + if (len(config_file) > 0)then +#ifdef NGEN_ACTIVE + ! with NGEN the argument gives the file manager file as an input parameter in a namelist + open (action='read', file=config_file, iostat=rc, newunit=fu) + read (nml=parameters, iostat=rc, unit=fu) + this%model%summa1_struc(n)%summaFileManagerFile=trim(file_manager) + print "(A)", "file_master is '"//trim(file_manager)//"'." +#else + ! without NGEN the argument gives the file manager file directly + ! Note, if this is more than 80 characters the pre-built BMI libraries will fail + this%model%summa1_struc(n)%summaFileManagerFile=trim(config_file) + print "(A)", "file_master is '"//trim(config_file)//"'." +#endif + endif + + ! declare and allocate summa data structures and initialize model state to known values + call summa_initialize(this%model%summa1_struc(n), err, message) + call handle_err(err, message) + + ! initialize parameter data structures (e.g. vegetation and soil parameters) + call summa_paramSetup(this%model%summa1_struc(n), err, message) + call handle_err(err, message) + + ! read restart data and reset the model state + call summa_readRestart(this%model%summa1_struc(n), err, message) + call handle_err(err, message) + + ! done with initialization + this%model%timeStep = 1 + bmi_status = BMI_SUCCESS + end function summa_bmi_initialize + + ! ***************************************************************************** + ! * advance model by one time step. + ! ***************************************************************************** + function summa_update(this) result (bmi_status) + class (summa_bmi), intent(inout) :: this + ! error control + integer(i4b) :: err=0 ! error code + character(len=1024) :: message='' ! error message + integer :: bmi_status + + + ! read model forcing data + call summa_readForcing(this%model%timeStep, this%model%summa1_struc(n), err, message) + call handle_err(err, message) + + if (mod(this%model%timeStep, print_step_freq) == 0)then + print *, 'step ---> ', this%model%timeStep + endif + ! run the summa physics for one time step + call summa_runPhysics(this%model%timeStep, this%model%summa1_struc(n), err, message) + call handle_err(err, message) + + ! write the model output + call summa_writeOutputFiles(this%model%timeStep, this%model%summa1_struc(n), err, message) + call handle_err(err, message) + + ! start, advance time, as model uses this time step throughout + this%model%timeStep = this%model%timeStep + 1 + + ! done with step + bmi_status = BMI_SUCCESS + end function summa_update + + ! **************************************************************************** + ! * advance the model until the given time + ! **************************************************************************** + function summa_update_until(this, time) result (bmi_status) + class (summa_bmi), intent(inout) :: this + double precision, intent(in) :: time + integer :: bmi_status, istat, n_steps, i + double precision :: current + + istat = this%get_current_time(current) ! unit seconds + if (time < current) then + bmi_status = BMI_FAILURE + return + end if + + n_steps = nint( (time - current)/data_step ) + 1 ! model can only do a full data_step + ! SUMMA runs the ending step (so start=end would still run a step) + do i = 1, n_steps + istat = this%update() + end do + bmi_status = BMI_SUCCESS + end function summa_update_until + + ! ***************************************************************************** + ! * successful end + ! ***************************************************************************** + function summa_finalize(this) result (bmi_status) + class (summa_bmi), intent(inout) :: this + integer :: bmi_status + + call stop_program(0, 'finished simulation successfully.') + ! to prevent exiting before HDF5 has closed + call sleep(2) + bmi_status = BMI_SUCCESS + end function summa_finalize + + ! ***************************************************************************** + ! * extra BMI functions + ! ***************************************************************************** + + ! Get the name of the model + function summa_component_name(this, name) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), pointer, intent(out) :: name + integer :: bmi_status + + name => component_name + bmi_status = BMI_SUCCESS + end function summa_component_name + + ! Count the input variables + function summa_input_item_count(this, count) result (bmi_status) + class (summa_bmi), intent(in) :: this + integer, intent(out) :: count + integer :: bmi_status + + count = input_item_count + bmi_status = BMI_SUCCESS + end function summa_input_item_count + + ! Count the output variables + function summa_output_item_count(this, count) result (bmi_status) + class (summa_bmi), intent(in) :: this + integer, intent(out) :: count + integer :: bmi_status + + count = output_item_count + bmi_status = BMI_SUCCESS + end function summa_output_item_count + + ! List output variables standardized as "https://csdms.colorado.edu/wiki/CSDMS_Standard_Names" + ! These are the inputs we will need if we do not want to call read_force inside summa_forcing.f90 + ! NGEN uses two component wind and a time vector that is not currently separable + ! (compute wind speed from the two components and time from start time and hourly step assumption) + function summa_input_var_names(this, names) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (*), pointer, intent(out) :: names(:) + integer :: bmi_status + + input_items(1) = 'atmosphere_water__precipitation_mass_flux' + input_items(2) = 'land_surface_air__temperature' + input_items(3) = 'atmosphere_air_water~vapor__relative_saturation' +#ifdef NGEN_ACTIVE + input_items(4) = 'land_surface_wind__x_component_of_velocity' + input_items(8) = 'land_surface_wind__y_component_of_velocity' +#else + input_items(4) = 'land_surface_wind__speed' +#endif + input_items(5) = 'land_surface_radiation~incoming~shortwave__energy_flux' + input_items(6) = 'land_surface_radiation~incoming~longwave__energy_flux' + input_items(7) = 'land_surface_air__pressure' + + names => input_items + bmi_status = BMI_SUCCESS + end function summa_input_var_names + + ! List output variables standardized as "https://csdms.colorado.edu/wiki/CSDMS_Standard_Names" + function summa_output_var_names(this, names) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (*), pointer, intent(out) :: names(:) + integer :: bmi_status, i + + output_items(1) = 'land_surface_water__runoff_volume_flux' + output_items(2) = 'land_surface_water__evaporation_mass_flux' + output_items(3) = 'land_vegetation_water__evaporation_mass_flux' + output_items(4) = 'land_vegetation_water__transpiration_mass_flux' + output_items(5) = 'snowpack__sublimation_mass_flux' + output_items(6) = 'land_vegetation_water__sublimation_mass_flux' + output_items(7) = 'snowpack_mass' + output_items(8) = 'soil_water__mass' + output_items(9) = 'land_vegetation_water__mass' + output_items(10)= 'land_surface_radiation~net~total__energy_flux' + output_items(11)= 'land_atmosphere_heat~net~latent__energy_flux' !(incoming to the *atmosphere*, since atmosphere is last) + output_items(12)= 'land_atmosphere_heat~net~sensible__energy_flux' !(incoming to the *atmosphere*, since atmosphere is last) + output_items(13)= 'atmosphere_energy~net~total__energy_flux' + output_items(14)= 'land_vegetation_energy~net~total__energy_flux' + output_items(15)= 'land_surface_energy~net~total__energy_flux' + output_items(16)= 'land_surface_water__baseflow_volume_flux' + names => output_items + bmi_status = BMI_SUCCESS + end function summa_output_var_names + + ! Model start time + function summa_start_time(this, time) result (bmi_status) + class (summa_bmi), intent(in) :: this + double precision, intent(out) :: time + integer :: bmi_status + + time = 0.0 ! unit seconds + bmi_status = BMI_SUCCESS + end function summa_start_time + + ! Model end time + function summa_end_time(this, time) result (bmi_status) + class (summa_bmi), intent(in) :: this + double precision, intent(out) :: time + integer :: bmi_status + + time = (dJulianFinsh - dJulianStart)*secprday ! unit seconds + bmi_status = BMI_SUCCESS + end function summa_end_time + + ! Model current time + function summa_current_time(this, time) result (bmi_status) + class (summa_bmi), intent(in) :: this + double precision, intent(out) :: time + integer :: bmi_status + + if(this%model%timeStep==0)then + time = 0.0 ! unit seconds + else + time = (data_step*real(this%model%timeStep-1,dp)) ! unit seconds + end if + bmi_status = BMI_SUCCESS + end function summa_current_time + + ! Model time step + function summa_time_step(this, time_step) result (bmi_status) + class (summa_bmi), intent(in) :: this + double precision, intent(out) :: time_step + integer :: bmi_status + + time_step = data_step ! unit seconds + bmi_status = BMI_SUCCESS + end function summa_time_step + + ! Model time units + function summa_time_units(this, units) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(out) :: units + integer :: bmi_status + + units = "s" + bmi_status = BMI_SUCCESS + end function summa_time_units + + ! Get the grid id for a particular variable + function summa_var_grid(this, name, grid) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + integer, intent(out) :: grid + integer :: bmi_status + + select case(name) + case default + grid = 0 + bmi_status = BMI_SUCCESS + end select + end function summa_var_grid + + ! The type of a variable's grid + function summa_grid_type(this, grid, type) result (bmi_status) + class (summa_bmi), intent(in) :: this + integer, intent(in) :: grid + character (len=*), intent(out) :: type + integer :: bmi_status + + select case(grid) + case(0) + type = 'points' + bmi_status = BMI_SUCCESS + case default + type = "-" + bmi_status = BMI_FAILURE + end select + end function summa_grid_type + + ! The number of dimensions of a grid, latitude and longitude and elevation + function summa_grid_rank(this, grid, rank) result (bmi_status) + class (summa_bmi), intent(in) :: this + integer, intent(in) :: grid + integer, intent(out) :: rank + integer :: bmi_status + + select case(grid) + case(0) + rank = 3 + bmi_status = BMI_SUCCESS + case default + rank = -1 + bmi_status = BMI_FAILURE + end select + end function summa_grid_rank + + ! The dimensions of a grid, not applicable to unstructured + function summa_grid_shape(this, grid, shape) result (bmi_status) + class (summa_bmi), intent(in) :: this + integer, intent(in) :: grid + integer, dimension(:), intent(out) :: shape + integer :: bmi_status + + select case(grid) + case default + shape(:) = -1 + bmi_status = BMI_FAILURE + end select + end function summa_grid_shape + + ! The total number of elements in a grid + function summa_grid_size(this, grid, size) result (bmi_status) + class (summa_bmi), intent(in) :: this + integer, intent(in) :: grid + integer, intent(out) :: size + integer :: bmi_status + + select case(grid) + case(0) + size = sum(gru_struc(:)%hruCount) + bmi_status = BMI_SUCCESS + case default + size = -1 + bmi_status = BMI_FAILURE + end select + end function summa_grid_size + + ! The distance between nodes of a grid, not applicable to unstructured + function summa_grid_spacing(this, grid, spacing) result (bmi_status) + class (summa_bmi), intent(in) :: this + integer, intent(in) :: grid + double precision, dimension(:), intent(out) :: spacing + integer :: bmi_status + + select case(grid) + case default + spacing(:) = -1.d0 + bmi_status = BMI_FAILURE + end select + end function summa_grid_spacing + + ! Coordinates of grid origin, not applicable to unstructured + function summa_grid_origin(this, grid, origin) result (bmi_status) + class (summa_bmi), intent(in) :: this + integer, intent(in) :: grid + double precision, dimension(:), intent(out) :: origin + integer :: bmi_status + + select case(grid) + case default + origin(:) = -1.d0 + bmi_status = BMI_FAILURE + end select + end function summa_grid_origin + + ! X-coordinates of grid nodes, longitude (degrees east) + function summa_grid_x(this, grid, x) result (bmi_status) + class (summa_bmi), intent(in) :: this + integer, intent(in) :: grid + double precision, dimension(:), intent(out) :: x + integer :: bmi_status, iGRU, jHRU + + summaVars: associate(attrStruct => this%model%summa1_struc(n)%attrStruct & ! x%gru(:)%hru(:)%var(:) -- local attributes for each HRU + ) + select case(grid) + case default + do iGRU = 1, this%model%summa1_struc(n)%nGRU + do jHRU = 1, gru_struc(iGRU)%hruCount + x((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = attrStruct%gru(iGRU)%hru(jHRU)%var(iLookATTR%longitude) + end do + end do + bmi_status = BMI_SUCCESS + end select + end associate summaVars + end function summa_grid_x + + ! Y-coordinates of grid nodes, latitude (degrees north) + function summa_grid_y(this, grid, y) result (bmi_status) + class (summa_bmi), intent(in) :: this + integer, intent(in) :: grid + double precision, dimension(:), intent(out) :: y + integer :: bmi_status, iGRU, jHRU + + summaVars: associate(attrStruct => this%model%summa1_struc(n)%attrStruct & ! x%gru(:)%hru(:)%var(:) -- local attributes for each HRU + ) + select case(grid) + case default + do iGRU = 1, this%model%summa1_struc(n)%nGRU + do jHRU = 1, gru_struc(iGRU)%hruCount + y((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = attrStruct%gru(iGRU)%hru(jHRU)%var(iLookATTR%latitude) + end do + end do + bmi_status = BMI_SUCCESS + end select + end associate summaVars + end function summa_grid_y + + ! Z-coordinates of grid nodes, elevation (m) + function summa_grid_z(this, grid, z) result (bmi_status) + class (summa_bmi), intent(in) :: this + integer, intent(in) :: grid + double precision, dimension(:), intent(out) :: z + integer :: bmi_status, iGRU, jHRU + + summaVars: associate(attrStruct => this%model%summa1_struc(n)%attrStruct & ! x%gru(:)%hru(:)%var(:) -- local attributes for each HRU + ) + select case(grid) + case default + do iGRU = 1, this%model%summa1_struc(n)%nGRU + do jHRU = 1, gru_struc(iGRU)%hruCount + z((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = attrStruct%gru(iGRU)%hru(jHRU)%var(iLookATTR%elevation) + end do + end do + bmi_status = BMI_SUCCESS + end select + end associate summaVars + end function summa_grid_z + + ! Get the number of nodes in an unstructured grid + function summa_grid_node_count(this, grid, count) result(bmi_status) + class(summa_bmi), intent(in) :: this + integer, intent(in) :: grid + integer, intent(out) :: count + integer :: bmi_status + + select case(grid) + case default + count = sum(gru_struc(:)%hruCount) + bmi_status = BMI_SUCCESS + end select + end function summa_grid_node_count + + ! Get the number of edges in an unstructured grid, points is 0 BUT COULD BE USED FOR GRUs + function summa_grid_edge_count(this, grid, count) result(bmi_status) + class(summa_bmi), intent(in) :: this + integer, intent(in) :: grid + integer, intent(out) :: count + integer :: bmi_status + + select case(grid) + case default + count = 0 + bmi_status = BMI_SUCCESS + end select + end function summa_grid_edge_count + + ! Get the number of faces in an unstructured grid, points is 0 BUT COULD BE USED FOR GRUs + function summa_grid_face_count(this, grid, count) result(bmi_status) + class(summa_bmi), intent(in) :: this + integer, intent(in) :: grid + integer, intent(out) :: count + integer :: bmi_status + + select case(grid) + case default + count = 0 + bmi_status = BMI_SUCCESS + end select + end function summa_grid_face_count + + ! Get the edge-node connectivity, points is 0 BUT COULD BE USED FOR GRUs + function summa_grid_edge_nodes(this, grid, edge_nodes) result(bmi_status) + class(summa_bmi), intent(in) :: this + integer, intent(in) :: grid + integer, dimension(:), intent(out) :: edge_nodes + integer :: bmi_status + + select case(grid) + case default + edge_nodes(:) = -1 + bmi_status = BMI_FAILURE + end select + end function summa_grid_edge_nodes + + ! Get the face-edge connectivity, points is 0 BUT COULD BE USED FOR GRUs + function summa_grid_face_edges(this, grid, face_edges) result(bmi_status) + class(summa_bmi), intent(in) :: this + integer, intent(in) :: grid + integer, dimension(:), intent(out) :: face_edges + integer :: bmi_status + + select case(grid) + case default + face_edges(:) = -1 + bmi_status = BMI_FAILURE + end select + end function summa_grid_face_edges + + ! Get the face-node connectivity, points is 0 BUT COULD BE USED FOR GRUs + function summa_grid_face_nodes(this, grid, face_nodes) result(bmi_status) + class(summa_bmi), intent(in) :: this + integer, intent(in) :: grid + integer, dimension(:), intent(out) :: face_nodes + integer :: bmi_status + + select case(grid) + case default + face_nodes(:) = -1 + bmi_status = BMI_FAILURE + end select + end function summa_grid_face_nodes + + ! Get the number of nodes for each face, points is 0 BUT COULD BE USED FOR GRUs + function summa_grid_nodes_per_face(this, grid, nodes_per_face) result(bmi_status) + class(summa_bmi), intent(in) :: this + integer, intent(in) :: grid + integer, dimension(:), intent(out) :: nodes_per_face + integer :: bmi_status + + select case(grid) + case default + nodes_per_face(:) = -1 + bmi_status = BMI_SUCCESS + end select + end function summa_grid_nodes_per_face + + ! The data type of the variable, as a string + function summa_var_type(this, name, type) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + character (len=*), intent(out) :: type + integer :: bmi_status + + if(name(1:5)=='model')then ! not currently used, left in for future integer type needs + type = "integer" + else + type = "real" + endif + bmi_status = BMI_SUCCESS + end function summa_var_type + + ! The units of the given variable + function summa_var_units(this, name, units) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + character (len=*), intent(out) :: units + integer :: bmi_status + + select case (name) + ! input, note using the NGEN preferred unit definitions, equivalent to standard SUMMA definitions as noted + case('atmosphere_water__precipitation_mass_flux') ; units = 'mm s-1'; ; bmi_status = BMI_SUCCESS !equivalent kg m-2 s-1 + case('land_surface_air__temperature') ; units = 'K' ; bmi_status = BMI_SUCCESS + case('atmosphere_air_water~vapor__relative_saturation') ; units = 'kg kg-1' ; bmi_status = BMI_SUCCESS +#ifdef NGEN_ACTIVE + case('land_surface_wind__x_component_of_velocity') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS + case('land_surface_wind__y_component_of_velocity') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS +#else + case('land_surface_wind__speed') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS +#endif + case('land_surface_radiation~incoming~shortwave__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS + case('land_surface_radiation~incoming~longwave__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS + case('land_surface_air__pressure') ; units = 'kg m-1 s-2'; bmi_status = BMI_SUCCESS + + ! output + case('land_surface_water__runoff_volume_flux') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS + case('land_surface_water__evaporation_mass_flux') ; units = 'mm s-1' ; bmi_status = BMI_SUCCESS !equivalent kg m-2 s-1 + case('land_vegetation_water__evaporation_mass_flux') ; units = 'mm s-1' ; bmi_status = BMI_SUCCESS !equivalent kg m-2 s-1 + case('land_vegetation_water__transpiration_mass_flux'); units = 'mm s-1' ; bmi_status = BMI_SUCCESS !equivalent kg m-2 s-1 + case('snowpack__sublimation_mass_flux') ; units = 'mm s-1' ; bmi_status = BMI_SUCCESS !equivalent kg m-2 s-1 + case('land_vegetation_water__sublimation_mass_flux') ; units = 'mm s-1' ; bmi_status = BMI_SUCCESS !equivalent kg m-2 s-1 + case('snowpack_mass') ; units = 'kg m-2' ; bmi_status = BMI_SUCCESS + case('soil_water__mass') ; units = 'kg m-2' ; bmi_status = BMI_SUCCESS + case('land_vegetation_water__mass') ; units = 'kg m-2' ; bmi_status = BMI_SUCCESS + case('land_surface_radiation~net~total__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS + case('land_atmosphere_heat~net~latent__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS + case('land_atmosphere_heat~net~sensible__energy_flux'); units = 'W m-2' ; bmi_status = BMI_SUCCESS + case('atmosphere_energy~net~total__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS + case('land_vegetation_energy~net~total__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS + case('land_surface_energy~net~total__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS + case('land_surface_water__baseflow_volume_flux') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS + case default; units = "-"; bmi_status = BMI_FAILURE + end select + end function summa_var_units + + ! Memory use per array element + function summa_var_itemsize(this, name, size) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + integer, intent(out) :: size + real, target :: target_arr(sum(gru_struc(:)%hruCount)) + integer ,target :: itarget_arr + integer :: bmi_status + + call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file + ! use the real or integer target + if(name(1:5)=='model')then ! not currently used, left in for future integer type needs + size = sizeof(itarget_arr) ! 'sizeof' in gcc & ifort + else + size = sizeof(target_arr(1)) ! 'sizeof' in gcc & ifort + endif + bmi_status = BMI_SUCCESS + end function summa_var_itemsize + + ! The size of the given variable + function summa_var_nbytes(this, name, nbytes) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + integer, intent(out) :: nbytes + integer :: bmi_status + integer :: s1, s2, s3, grid, grid_size, item_size + + s1 = this%get_var_grid(name, grid) + s2 = this%get_grid_size(grid, grid_size) + s3 = this%get_var_itemsize(name, item_size) + if ((s1 == BMI_SUCCESS).and.(s2 == BMI_SUCCESS).and.(s3 == BMI_SUCCESS)) then + nbytes = item_size * grid_size + bmi_status = BMI_SUCCESS + else + nbytes = -1 + bmi_status = BMI_FAILURE + end if + end function summa_var_nbytes + + ! The location (node, face, edge) of the given variable + function summa_var_location(this, name, location) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + character (len=*), intent(out) :: location + integer :: bmi_status + + select case(name) + case default + location = "node" + bmi_status = BMI_SUCCESS + end select + end function summa_var_location + + ! Get a copy of a integer variable's values, flattened + function summa_get_int(this, name, dest) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + integer, intent(inout) :: dest(:) + real, target :: target_arr(sum(gru_struc(:)%hruCount)) + integer ,target :: itarget_arr + integer :: bmi_status + + select case(name) + case default + call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file + ! use the integer target + dest = itarget_arr + bmi_status = BMI_SUCCESS + end select + end function summa_get_int + + ! Get a copy of a real variable's values, flattened + function summa_get_float(this, name, dest) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + real, intent(inout) :: dest(:) + real, target :: target_arr(sum(gru_struc(:)%hruCount)) + integer ,target :: itarget_arr + integer :: bmi_status + + select case(name) + case default + call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file + ! use the real target + dest = target_arr + bmi_status = BMI_SUCCESS + end select + end function summa_get_float + + ! Get a copy of a double variable's values, flattened + function summa_get_double(this, name, dest) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + double precision, intent(inout) :: dest(:) + integer :: bmi_status + + select case(name) + case default + dest(:) = -1.d0 + bmi_status = BMI_FAILURE + end select + end function summa_get_double + + ! Get a reference to an integer-valued variable, flattened + function summa_get_ptr_int(this, name, dest_ptr) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + integer, pointer, intent(inout) :: dest_ptr(:) + integer :: bmi_status, n_elements + real, target :: target_arr(sum(gru_struc(:)%hruCount)) + integer ,target :: itarget_arr + type (c_ptr) :: src + + select case(name) + case default + call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file + ! use the integer target + src = c_loc(itarget_arr) + n_elements = sum(gru_struc(:)%hruCount) + call c_f_pointer(src, dest_ptr, [n_elements]) + bmi_status = BMI_SUCCESS + end select + end function summa_get_ptr_int + + ! Get a reference to a real-valued variable, flattened + function summa_get_ptr_float(this, name, dest_ptr) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + real, pointer, intent(inout) :: dest_ptr(:) + integer :: bmi_status, n_elements + real, target :: target_arr(sum(gru_struc(:)%hruCount)) + integer ,target :: itarget_arr + type (c_ptr) :: src + + select case(name) + case default + call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file + ! use the real target + src = c_loc(target_arr(1)) + n_elements = sum(gru_struc(:)%hruCount) + call c_f_pointer(src, dest_ptr, [n_elements]) + bmi_status = BMI_SUCCESS + end select + end function summa_get_ptr_float + + ! Get a reference to an double-valued variable, flattened + function summa_get_ptr_double(this, name, dest_ptr) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + double precision, pointer, intent(inout) :: dest_ptr(:) + integer :: bmi_status, n_elements + type (c_ptr) :: src + + select case(name) + case default + bmi_status = BMI_FAILURE + end select + end function summa_get_ptr_double + + ! Get values of an integer variable at the given locations + function summa_get_at_indices_int(this, name, dest, inds) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + integer, intent(inout) :: dest(:) + integer, intent(in) :: inds(:) + integer :: bmi_status, i, n_elements + real, target :: target_arr(sum(gru_struc(:)%hruCount)) + integer ,target :: itarget_arr + type (c_ptr) src + integer, pointer :: src_flattened(:) + + select case(name) + case default + call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file + ! use the integer target + src = c_loc(itarget_arr) + call c_f_pointer(src, src_flattened, [n_elements]) + n_elements = size(inds) + do i = 1, n_elements + dest(i) = src_flattened(inds(i)) + end do + bmi_status = BMI_SUCCESS + end select + end function summa_get_at_indices_int + + ! Get values of a real variable at the given locations + function summa_get_at_indices_float(this, name, dest, inds) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + real, intent(inout) :: dest(:) + integer, intent(in) :: inds(:) + integer :: bmi_status, i, n_elements + real, target :: target_arr(sum(gru_struc(:)%hruCount)) + integer ,target :: itarget_arr + type (c_ptr) src + real, pointer :: src_flattened(:) + + select case(name) + case default + call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file + ! use the real target + src = c_loc(target_arr(1)) + call c_f_pointer(src, src_flattened, [n_elements]) + n_elements = size(inds) + do i = 1, n_elements + dest(i) = src_flattened(inds(i)) + end do + bmi_status = BMI_SUCCESS + end select + end function summa_get_at_indices_float + + ! Get values of a double variable at the given locations + function summa_get_at_indices_double(this, name, dest, inds) result (bmi_status) + class (summa_bmi), intent(in) :: this + character (len=*), intent(in) :: name + double precision, intent(inout) :: dest(:) + integer, intent(in) :: inds(:) + integer :: bmi_status, i, n_elements + type (c_ptr) src + double precision, pointer :: src_flattened(:) + + select case(name) + case default + bmi_status = BMI_FAILURE + end select + end function summa_get_at_indices_double + + ! Set new integer values, ONLY FOR INPUT VARIABLES + function summa_set_int(this, name, src) result (bmi_status) + class (summa_bmi), intent(inout) :: this + character (len=*), intent(in) :: name + integer, intent(in) :: src(:) + real :: rsrc(sum(gru_struc(:)%hruCount)) + integer :: bmi_status + + select case(name) + case default + rsrc = -999.0 + call assign_basin_field(this, name, rsrc, src(1)) ! See near bottom of file + bmi_status = BMI_SUCCESS + end select + end function summa_set_int + + ! Set new real values, ONLY FOR INPUT VARIABLES + function summa_set_float(this, name, src) result (bmi_status) + class (summa_bmi), intent(inout) :: this + character (len=*), intent(in) :: name + real, intent(in) :: src(:) + integer :: bmi_status, isrc + + select case(name) + case default + isrc = -999 + call assign_basin_field(this, name, src, isrc) ! See near bottom of file + bmi_status = BMI_SUCCESS + end select + end function summa_set_float + + ! Set new double values + function summa_set_double(this, name, src) result (bmi_status) + class (summa_bmi), intent(inout) :: this + character (len=*), intent(in) :: name + double precision, intent(in) :: src(:) + integer :: bmi_status + + select case(name) + case default + bmi_status = BMI_FAILURE + end select + end function summa_set_double + + ! Set integer values at particular locations + function summa_set_at_indices_int(this, name, inds, src) result (bmi_status) + class (summa_bmi), intent(inout) :: this + character (len=*), intent(in) :: name + integer, intent(in) :: inds(:) + integer, intent(in) :: src(:) + integer :: bmi_status + type (c_ptr) dest + integer, pointer :: dest_flattened(:) + integer :: i + + select case(name) + case default + bmi_status = BMI_FAILURE + end select + end function summa_set_at_indices_int + + ! Set real values at particular locations, ONLY FOR INPUT VARIABLES + function summa_set_at_indices_float(this, name, inds, src) result (bmi_status) + class (summa_bmi), intent(inout) :: this + character (len=*), intent(in) :: name + integer, intent(in) :: inds(:) + real, intent(in) :: src(:) + integer :: bmi_status + type (c_ptr) dest + real, pointer :: dest_flattened(:) + integer :: i + + select case(name) + case default + bmi_status = BMI_FAILURE + end select + end function summa_set_at_indices_float + + ! Set double values at particular locations + function summa_set_at_indices_double(this, name, inds, src) result (bmi_status) + class (summa_bmi), intent(inout) :: this + character (len=*), intent(in) :: name + integer, intent(in) :: inds(:) + double precision, intent(in) :: src(:) + integer :: bmi_status + type (c_ptr) dest + double precision, pointer :: dest_flattened(:) + integer :: i + + select case(name) + case default + bmi_status = BMI_FAILURE + end select + end function summa_set_at_indices_double + +#ifdef NGEN_ACTIVE + function register_bmi(this) result(bmi_status) bind(C, name="register_bmi") + use, intrinsic:: iso_c_binding, only: c_ptr, c_loc, c_int + use iso_c_bmif_2_0 + implicit none + type(c_ptr) :: this ! If not value, then from the C perspective `this` is a void** + integer(kind=c_int) :: bmi_status + !Create the model instance to use + type(summa_bmi), pointer :: bmi_model + !Create a simple pointer wrapper + type(box), pointer :: bmi_box + + !allocate model + allocate(summa_bmi::bmi_model) + !allocate the pointer box + allocate(bmi_box) + + !associate the wrapper pointer the created model instance + bmi_box%ptr => bmi_model + + if( .not. associated( bmi_box ) .or. .not. associated( bmi_box%ptr ) ) then + bmi_status = BMI_FAILURE + else + ! Return the pointer to box + this = c_loc(bmi_box) + bmi_status = BMI_SUCCESS + endif + end function register_bmi +#endif + + ! non-BMI helper function to assign input fields + subroutine assign_basin_field(this, name, src_arr, isrc_arr) + implicit none + class (summa_bmi), intent(inout) :: this + character (len=*), intent(in) :: name + real, intent(in) :: src_arr(sum(gru_struc(:)%hruCount)) + integer, intent(in) :: isrc_arr + integer :: iGRU, jHRU, i + + summaVars: associate(& + timeStruct => this%model%summa1_struc(n)%timeStruct , & ! x%var(:) -- model time data + forcStruct => this%model%summa1_struc(n)%forcStruct , & ! x%gru(:)%hru(:)%var(:) -- model forcing data + diagStruct => this%model%summa1_struc(n)%diagStruct & ! x%gru(:)%hru(:)%var(:)%dat -- model diagnostic variables + ) + + if(name(1:5)=='model')then ! not currently used, left in for future integer type needs + select case (name) + ! input + case('model__time_year') + timeStruct%var(iLookTIME%iyyy) = isrc_arr + end select + else + do iGRU = 1, this%model%summa1_struc(n)%nGRU + do jHRU = 1, gru_struc(iGRU)%hruCount + i = (iGRU-1) * gru_struc(iGRU)%hruCount + jHRU + select case (name) + ! input + case('atmosphere_water__precipitation_mass_flux') + forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%pptrate) = src_arr(i) + case('land_surface_air__temperature') + forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airtemp) = src_arr(i) + case('atmosphere_air_water~vapor__relative_saturation') + forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%spechum) = src_arr(i) + case('land_surface_wind__x_component_of_velocity') + diagStruct%gru(iGRU)%hru(jHRU)%var(iLookDIAG%windspd_x)%dat(1) = src_arr(i) + case('land_surface_wind__y_component_of_velocity') + diagStruct%gru(iGRU)%hru(jHRU)%var(iLookDIAG%windspd_y)%dat(1) = src_arr(i) + case('land_surface_wind__speed') + forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%windspd) = src_arr(i) + case('land_surface_radiation~incoming~shortwave__energy_flux') + forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%SWRadAtm) = src_arr(i) + case('land_surface_radiation~incoming~longwave__energy_flux') + forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%LWRadAtm) = src_arr(i) + case('land_surface_air__pressure') + forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airpres) = src_arr(i) + end select + end do + end do + endif + end associate summaVars + end subroutine assign_basin_field + + ! non-BMI helper function to get fields, only get first do_nHRU of them + subroutine get_basin_field(this, name, do_nHRU, target_arr, itarget_arr) + implicit none + class (summa_bmi), intent(in) :: this + integer, intent(in) :: do_nHRU + character (len=*), intent(in) :: name + real, target, intent(out) :: target_arr(sum(gru_struc(:)%hruCount)) + integer, target, intent(out) :: itarget_arr + integer :: iGRU, jHRU, i + + summaVars: associate(& + timeStruct => this%model%summa1_struc(n)%timeStruct , & ! x%var(:) -- model time data + forcStruct => this%model%summa1_struc(n)%forcStruct , & ! x%gru(:)%hru(:)%var(:) -- model forcing data + progStruct => this%model%summa1_struc(n)%progStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model prognostic (state) variables + diagStruct => this%model%summa1_struc(n)%diagStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model diagnostic variables + fluxStruct => this%model%summa1_struc(n)%fluxStruct & ! x%gru(:)%hru(:)%var(:)%dat -- model fluxes + ) + target_arr = -999.0 + itarget_arr = -999 + if(name(1:5)=='model')then ! not currently used, left in for future integer type needs + select case (name) + ! input + case('model__time_year') + itarget_arr = timeStruct%var(iLookTIME%iyyy) + end select + else + do iGRU = 1, this%model%summa1_struc(n)%nGRU + do jHRU = 1, gru_struc(iGRU)%hruCount + i = (iGRU-1) * gru_struc(iGRU)%hruCount + jHRU + if (i > do_nHRU) return + select case (name) + ! input + case('atmosphere_water__precipitation_mass_flux') + target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%pptrate) + case('land_surface_air__temperature') + target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airtemp) + case('atmosphere_air_water~vapor__relative_saturation') + target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%spechum) + case('land_surface_wind__x_component_of_velocity') + target_arr(i) = diagStruct%gru(iGRU)%hru(jHRU)%var(iLookDIAG%windspd_x)%dat(1) + case('land_surface_wind__y_component_of_velocity') + target_arr(i) = diagStruct%gru(iGRU)%hru(jHRU)%var(iLookDIAG%windspd_y)%dat(1) + case('land_surface_wind__speed') + target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%windspd) + case('land_surface_radiation~incoming~shortwave__energy_flux') + target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%SWRadAtm) + case('land_surface_radiation~incoming~longwave__energy_flux') + target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%LWRadAtm) + case('land_surface_air__pressure') + target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airpres) + + ! output + case('land_surface_water__runoff_volume_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarSurfaceRunoff)%dat(1) + case('land_surface_water__evaporation_mass_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarGroundEvaporation)%dat(1) + case('land_vegetation_water__evaporation_mass_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) + case('land_vegetation_water__transpiration_mass_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) + case('snowpack__sublimation_mass_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarSnowSublimation)%dat(1) + case('land_vegetation_water__sublimation_mass_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopySublimation)%dat(1) + case('snowpack_mass') + target_arr(i) = progStruct%gru(iGRU)%hru(jHRU)%var(iLookPROG%scalarSWE)%dat(1) + case('soil_water__mass') + target_arr(i) = diagStruct%gru(iGRU)%hru(jHRU)%var(iLookDIAG%scalarTotalSoilWat)%dat(1) + case('land_vegetation_water__mass') + target_arr(i) = progStruct%gru(iGRU)%hru(jHRU)%var(iLookPROG%scalarCanopyWat)%dat(1) + case('land_surface_radiation~net~total__energy_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarNetRadiation)%dat(1) + case('land_atmosphere_heat~net~latent__energy_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarLatHeatTotal)%dat(1) + case('land_atmosphere_heat~net~sensible__energy_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarSenHeatTotal)%dat(1) + case('atmosphere_energy~net~total__energy_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1) + case('land_vegetation_energy~net~total__energy_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1) + case('land_surface_energy~net~total__energy_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1) + case('land_surface_water__baseflow_volume_flux') + target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarAquiferBaseflow)%dat(1) + end select + end do + end do + endif + end associate summaVars + end subroutine get_basin_field + +end module summa_bmi diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 old mode 100644 new mode 100755 index 66374042d..708c95f8b --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -18,1240 +18,91 @@ ! You should have received a copy of the GNU General Public License ! along with this program. If not, see . -module summa_driver - ! provides functions needed for summa driver routines adding BMI functions - ! ***************************************************************************** - ! * use desired modules - ! ***************************************************************************** - ! data types - USE,intrinsic :: iso_c_binding, only: c_ptr, c_loc, c_f_pointer - USE nrtype ! variable types, etc. -! NGEN_ACTIVE is to be set when running in the Nextgen framework -! https://github.com/NOAA-OWP/ngen -#ifdef NGEN_ACTIVE - use bmif_2_0_iso ! BMI libraries NexGen -#else - use bmif_2_0 ! BMI libraries standard -#endif - USE summa_type, only: summa1_type_dec ! master summa data type - ! subroutines and functions: model setup - USE summa_init, only: summa_initialize ! used to allocate/initialize summa data structures - USE summa_setup, only: summa_paramSetup ! used to initialize parameter data structures (e.g. vegetation and soil parameters) - USE summa_restart, only: summa_readRestart ! used to read restart data and reset the model state - ! subroutines and functions: model simulation - USE summa_forcing, only: summa_readForcing ! used to read forcing data - USE summa_modelRun, only: summa_runPhysics ! used to run the summa physics for one time step - USE summa_writeOutput, only: summa_writeOutputFiles ! used to write the summa output files - ! utility functions - USE summa_util, only: stop_program ! used to stop the summa program (with errors) - USE summa_util, only: handle_err ! used to process errors - ! global data - USE globalData, only: numtim ! number of model time steps - USE globalData, only: print_step_freq - USE globalData, only: dJulianStart ! julian day of start time of simulation - USE globalData, only: dJulianFinsh ! julian day of end time of simulation - USE globalData, only: data_step ! length of time steps for the outermost timeloop - USE globalData, only: gru_struc ! gru-hru mapping structures - USE multiconst, only: secprday ! number of seconds in a day - ! provide access to the named variables that describe elements of parent model structures - USE var_lookup, only: iLookTIME ! named variables for time data structure - USE var_lookup, only: iLookATTR ! named variables for real valued attribute data structure - USE var_lookup, only: iLookFORCE ! named variables for forcing data structure - USE var_lookup, only: iLookFLUX ! named variables for local flux variables - USE var_lookup, only: iLookDIAG ! named variables for local diagnostic variables - USE var_lookup, only: iLookPROG ! named variables for local prognostic variables - - implicit none - - ! Define the attributes of the model. - type :: summa_model - integer(i4b) :: timeStep ! index of model time step - type(summa1_type_dec), allocatable :: summa1_struc(:) - end type summa_model - - type, extends (bmi) :: summa_bmi - private - type (summa_model) :: model - contains - procedure :: get_component_name => summa_component_name - procedure :: get_input_item_count => summa_input_item_count - procedure :: get_output_item_count => summa_output_item_count - procedure :: get_input_var_names => summa_input_var_names - procedure :: get_output_var_names => summa_output_var_names - procedure :: initialize => summa_bmi_initialize - procedure :: finalize => summa_finalize - procedure :: get_start_time => summa_start_time - procedure :: get_end_time => summa_end_time - procedure :: get_current_time => summa_current_time - procedure :: get_time_step => summa_time_step - procedure :: get_time_units => summa_time_units - procedure :: update => summa_update - procedure :: update_until => summa_update_until - procedure :: get_var_grid => summa_var_grid - procedure :: get_grid_type => summa_grid_type - procedure :: get_grid_rank => summa_grid_rank - procedure :: get_grid_shape => summa_grid_shape - procedure :: get_grid_size => summa_grid_size - procedure :: get_grid_spacing => summa_grid_spacing - procedure :: get_grid_origin => summa_grid_origin - procedure :: get_grid_x => summa_grid_x - procedure :: get_grid_y => summa_grid_y - procedure :: get_grid_z => summa_grid_z - procedure :: get_grid_node_count => summa_grid_node_count - procedure :: get_grid_edge_count => summa_grid_edge_count - procedure :: get_grid_face_count => summa_grid_face_count - procedure :: get_grid_edge_nodes => summa_grid_edge_nodes - procedure :: get_grid_face_edges => summa_grid_face_edges - procedure :: get_grid_face_nodes => summa_grid_face_nodes - procedure :: get_grid_nodes_per_face => summa_grid_nodes_per_face - procedure :: get_var_type => summa_var_type - procedure :: get_var_units => summa_var_units - procedure :: get_var_itemsize => summa_var_itemsize - procedure :: get_var_nbytes => summa_var_nbytes - procedure :: get_var_location => summa_var_location - procedure :: get_value_int => summa_get_int - procedure :: get_value_float => summa_get_float - procedure :: get_value_double => summa_get_double - generic :: get_value => & - get_value_int, & - get_value_float, & - get_value_double - procedure :: get_value_ptr_int => summa_get_ptr_int - procedure :: get_value_ptr_float => summa_get_ptr_float - procedure :: get_value_ptr_double => summa_get_ptr_double - generic :: get_value_ptr => & - get_value_ptr_int, & - get_value_ptr_float, & - get_value_ptr_double - procedure :: get_value_at_indices_int => summa_get_at_indices_int - procedure :: get_value_at_indices_float => summa_get_at_indices_float - procedure :: get_value_at_indices_double => summa_get_at_indices_double - generic :: get_value_at_indices => & - get_value_at_indices_int, & - get_value_at_indices_float, & - get_value_at_indices_double - procedure :: set_value_int => summa_set_int - procedure :: set_value_float => summa_set_float - procedure :: set_value_double => summa_set_double - generic :: set_value => & - set_value_int, & - set_value_float, & - set_value_double - procedure :: set_value_at_indices_int => summa_set_at_indices_int - procedure :: set_value_at_indices_float => summa_set_at_indices_float - procedure :: set_value_at_indices_double => summa_set_at_indices_double - generic :: set_value_at_indices => & - set_value_at_indices_int, & - set_value_at_indices_float, & - set_value_at_indices_double - end type summa_bmi - - private - public :: summa_bmi - - ! ***************************************************************************** - ! * variable definitions - ! ***************************************************************************** - character (len=BMI_MAX_COMPONENT_NAME), target :: & - component_name = "Structure for Unifying Multiple Modeling Alternatives: SUMMA" - ! define parameters for the model simulation - integer(i4b), parameter :: n=1 ! number of instantiations - ! Exchange items -#ifdef NGEN_ACTIVE - integer, parameter :: input_item_count = 8 -#else - integer, parameter :: input_item_count = 7 -#endif - integer, parameter :: output_item_count = 16 - character (len=BMI_MAX_VAR_NAME), target,dimension(input_item_count) :: input_items - character (len=BMI_MAX_VAR_NAME), target,dimension(output_item_count) :: output_items - ! --------------------------------------------------------------------------------------- - - contains - - ! ***************************************************************************** - ! * model setup/initialization - ! ***************************************************************************** - function summa_bmi_initialize(this, config_file) result (bmi_status) - class (summa_bmi), intent(out) :: this - character (len=*), intent(in) :: config_file - ! error control - integer(i4b) :: err=0 ! error code - character(len=1024) :: message='' ! error message - character(len=1024) :: file_manager - integer :: bmi_status, i,fu,rc - ! namelist definition - namelist /parameters/ file_manager - - ! initialize time steps - this%model%timeStep = 0 - - ! allocate space for the master summa structure, could happen outside of BMI function - allocate(this%model%summa1_struc(n), stat=err) - if(err/=0) call stop_program(1, 'problem allocating master summa structure') - - ! if using the BMI interface, there is an argument pointing to the file manager file - ! then make sure summaFileManagerFile is set before executing initialization - if (len(config_file) > 0)then -#ifdef NGEN_ACTIVE - ! with NGEN the argument gives the file manager file as an input parameter in a namelist - open (action='read', file=config_file, iostat=rc, newunit=fu) - read (nml=parameters, iostat=rc, unit=fu) - this%model%summa1_struc(n)%summaFileManagerFile=trim(file_manager) - print "(A)", "file_master is '"//trim(file_manager)//"'." -#else - ! without NGEN the argument gives the file manager file directly - ! Note, if this is more than 80 characters the pre-built BMI libraries will fail - this%model%summa1_struc(n)%summaFileManagerFile=trim(config_file) - print "(A)", "file_master is '"//trim(config_file)//"'." -#endif - endif - - ! declare and allocate summa data structures and initialize model state to known values - call summa_initialize(this%model%summa1_struc(n), err, message) - call handle_err(err, message) - - ! initialize parameter data structures (e.g. vegetation and soil parameters) - call summa_paramSetup(this%model%summa1_struc(n), err, message) - call handle_err(err, message) - - ! read restart data and reset the model state - call summa_readRestart(this%model%summa1_struc(n), err, message) - call handle_err(err, message) - - ! done with initialization - this%model%timeStep = 1 - bmi_status = BMI_SUCCESS - end function summa_bmi_initialize - - ! ***************************************************************************** - ! * advance model by one time step. - ! ***************************************************************************** - function summa_update(this) result (bmi_status) - class (summa_bmi), intent(inout) :: this - ! error control - integer(i4b) :: err=0 ! error code - character(len=1024) :: message='' ! error message - integer :: bmi_status - - - ! read model forcing data - call summa_readForcing(this%model%timeStep, this%model%summa1_struc(n), err, message) - call handle_err(err, message) - - if (mod(this%model%timeStep, print_step_freq) == 0)then - print *, 'step ---> ', this%model%timeStep - endif - ! run the summa physics for one time step - call summa_runPhysics(this%model%timeStep, this%model%summa1_struc(n), err, message) - call handle_err(err, message) - - ! write the model output - call summa_writeOutputFiles(this%model%timeStep, this%model%summa1_struc(n), err, message) - call handle_err(err, message) - - ! start, advance time, as model uses this time step throughout - this%model%timeStep = this%model%timeStep + 1 - - ! done with step - bmi_status = BMI_SUCCESS - end function summa_update - - ! **************************************************************************** - ! * advance the model until the given time - ! **************************************************************************** - function summa_update_until(this, time) result (bmi_status) - class (summa_bmi), intent(inout) :: this - double precision, intent(in) :: time - integer :: bmi_status, istat, n_steps, i - double precision :: current - - istat = this%get_current_time(current) ! unit seconds - if (time < current) then - bmi_status = BMI_FAILURE - return - end if - - n_steps = nint( (time - current)/data_step ) + 1 ! model can only do a full data_step - ! SUMMA runs the ending step (so start=end would still run a step) - do i = 1, n_steps - istat = this%update() - end do - bmi_status = BMI_SUCCESS - end function summa_update_until - - ! ***************************************************************************** - ! * successful end - ! ***************************************************************************** - function summa_finalize(this) result (bmi_status) - class (summa_bmi), intent(inout) :: this - integer :: bmi_status - - call stop_program(0, 'finished simulation successfully.') - ! to prevent exiting before HDF5 has closed - call sleep(2) - bmi_status = BMI_SUCCESS - end function summa_finalize - - ! ***************************************************************************** - ! * extra BMI functions - ! ***************************************************************************** - - ! Get the name of the model - function summa_component_name(this, name) result (bmi_status) - class (summa_bmi), intent(in) :: this - character (len=*), pointer, intent(out) :: name - integer :: bmi_status - - name => component_name - bmi_status = BMI_SUCCESS - end function summa_component_name - - ! Count the input variables - function summa_input_item_count(this, count) result (bmi_status) - class (summa_bmi), intent(in) :: this - integer, intent(out) :: count - integer :: bmi_status - - count = input_item_count - bmi_status = BMI_SUCCESS - end function summa_input_item_count - - ! Count the output variables - function summa_output_item_count(this, count) result (bmi_status) - class (summa_bmi), intent(in) :: this - integer, intent(out) :: count - integer :: bmi_status - - count = output_item_count - bmi_status = BMI_SUCCESS - end function summa_output_item_count - - ! List output variables standardized as "https://csdms.colorado.edu/wiki/CSDMS_Standard_Names" - ! These are the inputs we will need if we do not want to call read_force inside summa_forcing.f90 - ! NGEN uses two component wind and a time vector that is not currently separable - ! (compute wind speed from the two components and time from start time and hourly step assumption) - function summa_input_var_names(this, names) result (bmi_status) - class (summa_bmi), intent(in) :: this - character (*), pointer, intent(out) :: names(:) - integer :: bmi_status - - input_items(1) = 'atmosphere_water__precipitation_mass_flux' - input_items(2) = 'land_surface_air__temperature' - input_items(3) = 'atmosphere_air_water~vapor__relative_saturation' -#ifdef NGEN_ACTIVE - input_items(4) = 'land_surface_wind__x_component_of_velocity' - input_items(8) = 'land_surface_wind__y_component_of_velocity' -#else - input_items(4) = 'land_surface_wind__speed' -#endif - input_items(5) = 'land_surface_radiation~incoming~shortwave__energy_flux' - input_items(6) = 'land_surface_radiation~incoming~longwave__energy_flux' - input_items(7) = 'land_surface_air__pressure' - - names => input_items - bmi_status = BMI_SUCCESS - end function summa_input_var_names - - ! List output variables standardized as "https://csdms.colorado.edu/wiki/CSDMS_Standard_Names" - function summa_output_var_names(this, names) result (bmi_status) - class (summa_bmi), intent(in) :: this - character (*), pointer, intent(out) :: names(:) - integer :: bmi_status, i - - output_items(1) = 'land_surface_water__runoff_volume_flux' - output_items(2) = 'land_surface_water__evaporation_mass_flux' - output_items(3) = 'land_vegetation_water__evaporation_mass_flux' - output_items(4) = 'land_vegetation_water__transpiration_mass_flux' - output_items(5) = 'snowpack__sublimation_mass_flux' - output_items(6) = 'land_vegetation_water__sublimation_mass_flux' - output_items(7) = 'snowpack_mass' - output_items(8) = 'soil_water__mass' - output_items(9) = 'land_vegetation_water__mass' - output_items(10)= 'land_surface_radiation~net~total__energy_flux' - output_items(11)= 'land_atmosphere_heat~net~latent__energy_flux' !(incoming to the *atmosphere*, since atmosphere is last) - output_items(12)= 'land_atmosphere_heat~net~sensible__energy_flux' !(incoming to the *atmosphere*, since atmosphere is last) - output_items(13)= 'atmosphere_energy~net~total__energy_flux' - output_items(14)= 'land_vegetation_energy~net~total__energy_flux' - output_items(15)= 'land_surface_energy~net~total__energy_flux' - output_items(16)= 'land_surface_water__baseflow_volume_flux' - names => output_items - bmi_status = BMI_SUCCESS - end function summa_output_var_names - - ! Model start time - function summa_start_time(this, time) result (bmi_status) - class (summa_bmi), intent(in) :: this - double precision, intent(out) :: time - integer :: bmi_status - - time = 0.0 ! unit seconds - bmi_status = BMI_SUCCESS - end function summa_start_time - - ! Model end time - function summa_end_time(this, time) result (bmi_status) - class (summa_bmi), intent(in) :: this - double precision, intent(out) :: time - integer :: bmi_status - - time = (dJulianFinsh - dJulianStart)*secprday ! unit seconds - bmi_status = BMI_SUCCESS - end function summa_end_time - - ! Model current time - function summa_current_time(this, time) result (bmi_status) - class (summa_bmi), intent(in) :: this - double precision, intent(out) :: time - integer :: bmi_status - - if(this%model%timeStep==0)then - time = 0.0 ! unit seconds - else - time = (data_step*real(this%model%timeStep-1,dp)) ! unit seconds - end if - bmi_status = BMI_SUCCESS - end function summa_current_time - - ! Model time step - function summa_time_step(this, time_step) result (bmi_status) - class (summa_bmi), intent(in) :: this - double precision, intent(out) :: time_step - integer :: bmi_status - - time_step = data_step ! unit seconds - bmi_status = BMI_SUCCESS - end function summa_time_step - - ! Model time units - function summa_time_units(this, units) result (bmi_status) - class (summa_bmi), intent(in) :: this - character (len=*), intent(out) :: units - integer :: bmi_status - - units = "s" - bmi_status = BMI_SUCCESS - end function summa_time_units - - ! Get the grid id for a particular variable - function summa_var_grid(this, name, grid) result (bmi_status) - class (summa_bmi), intent(in) :: this - character (len=*), intent(in) :: name - integer, intent(out) :: grid - integer :: bmi_status - - select case(name) - case default - grid = 0 - bmi_status = BMI_SUCCESS - end select - end function summa_var_grid - - ! The type of a variable's grid - function summa_grid_type(this, grid, type) result (bmi_status) - class (summa_bmi), intent(in) :: this - integer, intent(in) :: grid - character (len=*), intent(out) :: type - integer :: bmi_status - - select case(grid) - case(0) - type = 'points' - bmi_status = BMI_SUCCESS - case default - type = "-" - bmi_status = BMI_FAILURE - end select - end function summa_grid_type - - ! The number of dimensions of a grid, latitude and longitude and elevation - function summa_grid_rank(this, grid, rank) result (bmi_status) - class (summa_bmi), intent(in) :: this - integer, intent(in) :: grid - integer, intent(out) :: rank - integer :: bmi_status - - select case(grid) - case(0) - rank = 3 - bmi_status = BMI_SUCCESS - case default - rank = -1 - bmi_status = BMI_FAILURE - end select - end function summa_grid_rank - - ! The dimensions of a grid, not applicable to unstructured - function summa_grid_shape(this, grid, shape) result (bmi_status) - class (summa_bmi), intent(in) :: this - integer, intent(in) :: grid - integer, dimension(:), intent(out) :: shape - integer :: bmi_status - - select case(grid) - case default - shape(:) = -1 - bmi_status = BMI_FAILURE - end select - end function summa_grid_shape - - ! The total number of elements in a grid - function summa_grid_size(this, grid, size) result (bmi_status) - class (summa_bmi), intent(in) :: this - integer, intent(in) :: grid - integer, intent(out) :: size - integer :: bmi_status - - select case(grid) - case(0) - size = sum(gru_struc(:)%hruCount) - bmi_status = BMI_SUCCESS - case default - size = -1 - bmi_status = BMI_FAILURE - end select - end function summa_grid_size - - ! The distance between nodes of a grid, not applicable to unstructured - function summa_grid_spacing(this, grid, spacing) result (bmi_status) - class (summa_bmi), intent(in) :: this - integer, intent(in) :: grid - double precision, dimension(:), intent(out) :: spacing - integer :: bmi_status - - select case(grid) - case default - spacing(:) = -1.d0 - bmi_status = BMI_FAILURE - end select - end function summa_grid_spacing - - ! Coordinates of grid origin, not applicable to unstructured - function summa_grid_origin(this, grid, origin) result (bmi_status) - class (summa_bmi), intent(in) :: this - integer, intent(in) :: grid - double precision, dimension(:), intent(out) :: origin - integer :: bmi_status - - select case(grid) - case default - origin(:) = -1.d0 - bmi_status = BMI_FAILURE - end select - end function summa_grid_origin - - ! X-coordinates of grid nodes, longitude (degrees east) - function summa_grid_x(this, grid, x) result (bmi_status) - class (summa_bmi), intent(in) :: this - integer, intent(in) :: grid - double precision, dimension(:), intent(out) :: x - integer :: bmi_status, iGRU, jHRU - - summaVars: associate(attrStruct => this%model%summa1_struc(n)%attrStruct & ! x%gru(:)%hru(:)%var(:) -- local attributes for each HRU - ) - select case(grid) - case default - do iGRU = 1, this%model%summa1_struc(n)%nGRU - do jHRU = 1, gru_struc(iGRU)%hruCount - x((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = attrStruct%gru(iGRU)%hru(jHRU)%var(iLookATTR%longitude) - end do - end do - bmi_status = BMI_SUCCESS - end select - end associate summaVars - end function summa_grid_x - - ! Y-coordinates of grid nodes, latitude (degrees north) - function summa_grid_y(this, grid, y) result (bmi_status) - class (summa_bmi), intent(in) :: this - integer, intent(in) :: grid - double precision, dimension(:), intent(out) :: y - integer :: bmi_status, iGRU, jHRU - - summaVars: associate(attrStruct => this%model%summa1_struc(n)%attrStruct & ! x%gru(:)%hru(:)%var(:) -- local attributes for each HRU - ) - select case(grid) - case default - do iGRU = 1, this%model%summa1_struc(n)%nGRU - do jHRU = 1, gru_struc(iGRU)%hruCount - y((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = attrStruct%gru(iGRU)%hru(jHRU)%var(iLookATTR%latitude) - end do - end do - bmi_status = BMI_SUCCESS - end select - end associate summaVars - end function summa_grid_y - - ! Z-coordinates of grid nodes, elevation (m) - function summa_grid_z(this, grid, z) result (bmi_status) - class (summa_bmi), intent(in) :: this - integer, intent(in) :: grid - double precision, dimension(:), intent(out) :: z - integer :: bmi_status, iGRU, jHRU - - summaVars: associate(attrStruct => this%model%summa1_struc(n)%attrStruct & ! x%gru(:)%hru(:)%var(:) -- local attributes for each HRU - ) - select case(grid) - case default - do iGRU = 1, this%model%summa1_struc(n)%nGRU - do jHRU = 1, gru_struc(iGRU)%hruCount - z((iGRU-1) * gru_struc(iGRU)%hruCount + jHRU) = attrStruct%gru(iGRU)%hru(jHRU)%var(iLookATTR%elevation) - end do - end do - bmi_status = BMI_SUCCESS - end select - end associate summaVars - end function summa_grid_z - - ! Get the number of nodes in an unstructured grid - function summa_grid_node_count(this, grid, count) result(bmi_status) - class(summa_bmi), intent(in) :: this - integer, intent(in) :: grid - integer, intent(out) :: count - integer :: bmi_status - - select case(grid) - case default - count = sum(gru_struc(:)%hruCount) - bmi_status = BMI_SUCCESS - end select - end function summa_grid_node_count - - ! Get the number of edges in an unstructured grid, points is 0 BUT COULD BE USED FOR GRUs - function summa_grid_edge_count(this, grid, count) result(bmi_status) - class(summa_bmi), intent(in) :: this - integer, intent(in) :: grid - integer, intent(out) :: count - integer :: bmi_status - - select case(grid) - case default - count = 0 - bmi_status = BMI_SUCCESS - end select - end function summa_grid_edge_count - - ! Get the number of faces in an unstructured grid, points is 0 BUT COULD BE USED FOR GRUs - function summa_grid_face_count(this, grid, count) result(bmi_status) - class(summa_bmi), intent(in) :: this - integer, intent(in) :: grid - integer, intent(out) :: count - integer :: bmi_status - - select case(grid) - case default - count = 0 - bmi_status = BMI_SUCCESS - end select - end function summa_grid_face_count - - ! Get the edge-node connectivity, points is 0 BUT COULD BE USED FOR GRUs - function summa_grid_edge_nodes(this, grid, edge_nodes) result(bmi_status) - class(summa_bmi), intent(in) :: this - integer, intent(in) :: grid - integer, dimension(:), intent(out) :: edge_nodes - integer :: bmi_status - - select case(grid) - case default - edge_nodes(:) = -1 - bmi_status = BMI_FAILURE - end select - end function summa_grid_edge_nodes - - ! Get the face-edge connectivity, points is 0 BUT COULD BE USED FOR GRUs - function summa_grid_face_edges(this, grid, face_edges) result(bmi_status) - class(summa_bmi), intent(in) :: this - integer, intent(in) :: grid - integer, dimension(:), intent(out) :: face_edges - integer :: bmi_status - - select case(grid) - case default - face_edges(:) = -1 - bmi_status = BMI_FAILURE - end select - end function summa_grid_face_edges - - ! Get the face-node connectivity, points is 0 BUT COULD BE USED FOR GRUs - function summa_grid_face_nodes(this, grid, face_nodes) result(bmi_status) - class(summa_bmi), intent(in) :: this - integer, intent(in) :: grid - integer, dimension(:), intent(out) :: face_nodes - integer :: bmi_status - - select case(grid) - case default - face_nodes(:) = -1 - bmi_status = BMI_FAILURE - end select - end function summa_grid_face_nodes - - ! Get the number of nodes for each face, points is 0 BUT COULD BE USED FOR GRUs - function summa_grid_nodes_per_face(this, grid, nodes_per_face) result(bmi_status) - class(summa_bmi), intent(in) :: this - integer, intent(in) :: grid - integer, dimension(:), intent(out) :: nodes_per_face - integer :: bmi_status - - select case(grid) - case default - nodes_per_face(:) = -1 - bmi_status = BMI_SUCCESS - end select - end function summa_grid_nodes_per_face - - ! The data type of the variable, as a string - function summa_var_type(this, name, type) result (bmi_status) - class (summa_bmi), intent(in) :: this - character (len=*), intent(in) :: name - character (len=*), intent(out) :: type - integer :: bmi_status - - if(name(1:5)=='model')then ! not currently used, left in for future integer type needs - type = "integer" - else - type = "real" - endif - bmi_status = BMI_SUCCESS - end function summa_var_type - - ! The units of the given variable - function summa_var_units(this, name, units) result (bmi_status) - class (summa_bmi), intent(in) :: this - character (len=*), intent(in) :: name - character (len=*), intent(out) :: units - integer :: bmi_status - - select case (name) - ! input, note using the NGEN preferred unit definitions, equivalent to standard SUMMA definitions as noted - case('atmosphere_water__precipitation_mass_flux') ; units = 'mm s-1'; ; bmi_status = BMI_SUCCESS !equivalent kg m-2 s-1 - case('land_surface_air__temperature') ; units = 'K' ; bmi_status = BMI_SUCCESS - case('atmosphere_air_water~vapor__relative_saturation') ; units = 'kg kg-1' ; bmi_status = BMI_SUCCESS -#ifdef NGEN_ACTIVE - case('land_surface_wind__x_component_of_velocity') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS - case('land_surface_wind__y_component_of_velocity') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS -#else - case('land_surface_wind__speed') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS -#endif - case('land_surface_radiation~incoming~shortwave__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS - case('land_surface_radiation~incoming~longwave__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS - case('land_surface_air__pressure') ; units = 'kg m-1 s-2'; bmi_status = BMI_SUCCESS - - ! output - case('land_surface_water__runoff_volume_flux') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS - case('land_surface_water__evaporation_mass_flux') ; units = 'mm s-1' ; bmi_status = BMI_SUCCESS !equivalent kg m-2 s-1 - case('land_vegetation_water__evaporation_mass_flux') ; units = 'mm s-1' ; bmi_status = BMI_SUCCESS !equivalent kg m-2 s-1 - case('land_vegetation_water__transpiration_mass_flux'); units = 'mm s-1' ; bmi_status = BMI_SUCCESS !equivalent kg m-2 s-1 - case('snowpack__sublimation_mass_flux') ; units = 'mm s-1' ; bmi_status = BMI_SUCCESS !equivalent kg m-2 s-1 - case('land_vegetation_water__sublimation_mass_flux') ; units = 'mm s-1' ; bmi_status = BMI_SUCCESS !equivalent kg m-2 s-1 - case('snowpack_mass') ; units = 'kg m-2' ; bmi_status = BMI_SUCCESS - case('soil_water__mass') ; units = 'kg m-2' ; bmi_status = BMI_SUCCESS - case('land_vegetation_water__mass') ; units = 'kg m-2' ; bmi_status = BMI_SUCCESS - case('land_surface_radiation~net~total__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS - case('land_atmosphere_heat~net~latent__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS - case('land_atmosphere_heat~net~sensible__energy_flux'); units = 'W m-2' ; bmi_status = BMI_SUCCESS - case('atmosphere_energy~net~total__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS - case('land_vegetation_energy~net~total__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS - case('land_surface_energy~net~total__energy_flux') ; units = 'W m-2' ; bmi_status = BMI_SUCCESS - case('land_surface_water__baseflow_volume_flux') ; units = 'm s-1' ; bmi_status = BMI_SUCCESS - case default; units = "-"; bmi_status = BMI_FAILURE - end select - end function summa_var_units - - ! Memory use per array element - function summa_var_itemsize(this, name, size) result (bmi_status) - class (summa_bmi), intent(in) :: this - character (len=*), intent(in) :: name - integer, intent(out) :: size - real, target :: target_arr(sum(gru_struc(:)%hruCount)) - integer ,target :: itarget_arr - integer :: bmi_status - - call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file - ! use the real or integer target - if(name(1:5)=='model')then ! not currently used, left in for future integer type needs - size = sizeof(itarget_arr) ! 'sizeof' in gcc & ifort - else - size = sizeof(target_arr(1)) ! 'sizeof' in gcc & ifort - endif - bmi_status = BMI_SUCCESS - end function summa_var_itemsize - - ! The size of the given variable - function summa_var_nbytes(this, name, nbytes) result (bmi_status) - class (summa_bmi), intent(in) :: this - character (len=*), intent(in) :: name - integer, intent(out) :: nbytes - integer :: bmi_status - integer :: s1, s2, s3, grid, grid_size, item_size - - s1 = this%get_var_grid(name, grid) - s2 = this%get_grid_size(grid, grid_size) - s3 = this%get_var_itemsize(name, item_size) - if ((s1 == BMI_SUCCESS).and.(s2 == BMI_SUCCESS).and.(s3 == BMI_SUCCESS)) then - nbytes = item_size * grid_size - bmi_status = BMI_SUCCESS - else - nbytes = -1 - bmi_status = BMI_FAILURE - end if - end function summa_var_nbytes - - ! The location (node, face, edge) of the given variable - function summa_var_location(this, name, location) result (bmi_status) - class (summa_bmi), intent(in) :: this - character (len=*), intent(in) :: name - character (len=*), intent(out) :: location - integer :: bmi_status - - select case(name) - case default - location = "node" - bmi_status = BMI_SUCCESS - end select - end function summa_var_location - - ! Get a copy of a integer variable's values, flattened - function summa_get_int(this, name, dest) result (bmi_status) - class (summa_bmi), intent(in) :: this - character (len=*), intent(in) :: name - integer, intent(inout) :: dest(:) - real, target :: target_arr(sum(gru_struc(:)%hruCount)) - integer ,target :: itarget_arr - integer :: bmi_status - - select case(name) - case default - call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file - ! use the integer target - dest = itarget_arr - bmi_status = BMI_SUCCESS - end select - end function summa_get_int - - ! Get a copy of a real variable's values, flattened - function summa_get_float(this, name, dest) result (bmi_status) - class (summa_bmi), intent(in) :: this - character (len=*), intent(in) :: name - real, intent(inout) :: dest(:) - real, target :: target_arr(sum(gru_struc(:)%hruCount)) - integer ,target :: itarget_arr - integer :: bmi_status - - select case(name) - case default - call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file - ! use the real target - dest = target_arr - bmi_status = BMI_SUCCESS - end select - end function summa_get_float - - ! Get a copy of a double variable's values, flattened - function summa_get_double(this, name, dest) result (bmi_status) - class (summa_bmi), intent(in) :: this - character (len=*), intent(in) :: name - double precision, intent(inout) :: dest(:) - integer :: bmi_status - - select case(name) - case default - dest(:) = -1.d0 - bmi_status = BMI_FAILURE - end select - end function summa_get_double - - ! Get a reference to an integer-valued variable, flattened - function summa_get_ptr_int(this, name, dest_ptr) result (bmi_status) - class (summa_bmi), intent(in) :: this - character (len=*), intent(in) :: name - integer, pointer, intent(inout) :: dest_ptr(:) - integer :: bmi_status, n_elements - real, target :: target_arr(sum(gru_struc(:)%hruCount)) - integer ,target :: itarget_arr - type (c_ptr) :: src - - select case(name) - case default - call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file - ! use the integer target - src = c_loc(itarget_arr) - n_elements = sum(gru_struc(:)%hruCount) - call c_f_pointer(src, dest_ptr, [n_elements]) - bmi_status = BMI_SUCCESS - end select - end function summa_get_ptr_int - - ! Get a reference to a real-valued variable, flattened - function summa_get_ptr_float(this, name, dest_ptr) result (bmi_status) - class (summa_bmi), intent(in) :: this - character (len=*), intent(in) :: name - real, pointer, intent(inout) :: dest_ptr(:) - integer :: bmi_status, n_elements - real, target :: target_arr(sum(gru_struc(:)%hruCount)) - integer ,target :: itarget_arr - type (c_ptr) :: src - - select case(name) - case default - call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file - ! use the real target - src = c_loc(target_arr(1)) - n_elements = sum(gru_struc(:)%hruCount) - call c_f_pointer(src, dest_ptr, [n_elements]) - bmi_status = BMI_SUCCESS - end select - end function summa_get_ptr_float - - ! Get a reference to an double-valued variable, flattened - function summa_get_ptr_double(this, name, dest_ptr) result (bmi_status) - class (summa_bmi), intent(in) :: this - character (len=*), intent(in) :: name - double precision, pointer, intent(inout) :: dest_ptr(:) - integer :: bmi_status, n_elements - type (c_ptr) :: src - - select case(name) - case default - bmi_status = BMI_FAILURE - end select - end function summa_get_ptr_double - - ! Get values of an integer variable at the given locations - function summa_get_at_indices_int(this, name, dest, inds) result (bmi_status) - class (summa_bmi), intent(in) :: this - character (len=*), intent(in) :: name - integer, intent(inout) :: dest(:) - integer, intent(in) :: inds(:) - integer :: bmi_status, i, n_elements - real, target :: target_arr(sum(gru_struc(:)%hruCount)) - integer ,target :: itarget_arr - type (c_ptr) src - integer, pointer :: src_flattened(:) - - select case(name) - case default - call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file - ! use the integer target - src = c_loc(itarget_arr) - call c_f_pointer(src, src_flattened, [n_elements]) - n_elements = size(inds) - do i = 1, n_elements - dest(i) = src_flattened(inds(i)) - end do - bmi_status = BMI_SUCCESS - end select - end function summa_get_at_indices_int - - ! Get values of a real variable at the given locations - function summa_get_at_indices_float(this, name, dest, inds) result (bmi_status) - class (summa_bmi), intent(in) :: this - character (len=*), intent(in) :: name - real, intent(inout) :: dest(:) - integer, intent(in) :: inds(:) - integer :: bmi_status, i, n_elements - real, target :: target_arr(sum(gru_struc(:)%hruCount)) - integer ,target :: itarget_arr - type (c_ptr) src - real, pointer :: src_flattened(:) - - select case(name) - case default - call get_basin_field(this, name, 1, target_arr, itarget_arr) ! See near bottom of file - ! use the real target - src = c_loc(target_arr(1)) - call c_f_pointer(src, src_flattened, [n_elements]) - n_elements = size(inds) - do i = 1, n_elements - dest(i) = src_flattened(inds(i)) - end do - bmi_status = BMI_SUCCESS - end select - end function summa_get_at_indices_float - - ! Get values of a double variable at the given locations - function summa_get_at_indices_double(this, name, dest, inds) result (bmi_status) - class (summa_bmi), intent(in) :: this - character (len=*), intent(in) :: name - double precision, intent(inout) :: dest(:) - integer, intent(in) :: inds(:) - integer :: bmi_status, i, n_elements - type (c_ptr) src - double precision, pointer :: src_flattened(:) - - select case(name) - case default - bmi_status = BMI_FAILURE - end select - end function summa_get_at_indices_double - - ! Set new integer values, ONLY FOR INPUT VARIABLES - function summa_set_int(this, name, src) result (bmi_status) - class (summa_bmi), intent(inout) :: this - character (len=*), intent(in) :: name - integer, intent(in) :: src(:) - real :: rsrc(sum(gru_struc(:)%hruCount)) - integer :: bmi_status - - select case(name) - case default - rsrc = -999.0 - call assign_basin_field(this, name, rsrc, src(1)) ! See near bottom of file - bmi_status = BMI_SUCCESS - end select - end function summa_set_int - - ! Set new real values, ONLY FOR INPUT VARIABLES - function summa_set_float(this, name, src) result (bmi_status) - class (summa_bmi), intent(inout) :: this - character (len=*), intent(in) :: name - real, intent(in) :: src(:) - integer :: bmi_status, isrc - - select case(name) - case default - isrc = -999 - call assign_basin_field(this, name, src, isrc) ! See near bottom of file - bmi_status = BMI_SUCCESS - end select - end function summa_set_float - - ! Set new double values - function summa_set_double(this, name, src) result (bmi_status) - class (summa_bmi), intent(inout) :: this - character (len=*), intent(in) :: name - double precision, intent(in) :: src(:) - integer :: bmi_status - - select case(name) - case default - bmi_status = BMI_FAILURE - end select - end function summa_set_double - - ! Set integer values at particular locations - function summa_set_at_indices_int(this, name, inds, src) result (bmi_status) - class (summa_bmi), intent(inout) :: this - character (len=*), intent(in) :: name - integer, intent(in) :: inds(:) - integer, intent(in) :: src(:) - integer :: bmi_status - type (c_ptr) dest - integer, pointer :: dest_flattened(:) - integer :: i - - select case(name) - case default - bmi_status = BMI_FAILURE - end select - end function summa_set_at_indices_int - - ! Set real values at particular locations, ONLY FOR INPUT VARIABLES - function summa_set_at_indices_float(this, name, inds, src) result (bmi_status) - class (summa_bmi), intent(inout) :: this - character (len=*), intent(in) :: name - integer, intent(in) :: inds(:) - real, intent(in) :: src(:) - integer :: bmi_status - type (c_ptr) dest - real, pointer :: dest_flattened(:) - integer :: i - - select case(name) - case default - bmi_status = BMI_FAILURE - end select - end function summa_set_at_indices_float - - ! Set double values at particular locations - function summa_set_at_indices_double(this, name, inds, src) result (bmi_status) - class (summa_bmi), intent(inout) :: this - character (len=*), intent(in) :: name - integer, intent(in) :: inds(:) - double precision, intent(in) :: src(:) - integer :: bmi_status - type (c_ptr) dest - double precision, pointer :: dest_flattened(:) - integer :: i - - select case(name) - case default - bmi_status = BMI_FAILURE - end select - end function summa_set_at_indices_double - -#ifdef NGEN_ACTIVE - function register_bmi(this) result(bmi_status) bind(C, name="register_bmi") - use, intrinsic:: iso_c_binding, only: c_ptr, c_loc, c_int - use iso_c_bmif_2_0 - implicit none - type(c_ptr) :: this ! If not value, then from the C perspective `this` is a void** - integer(kind=c_int) :: bmi_status - !Create the model instance to use - type(summa_bmi), pointer :: bmi_model - !Create a simple pointer wrapper - type(box), pointer :: bmi_box - - !allocate model - allocate(summa_bmi::bmi_model) - !allocate the pointer box - allocate(bmi_box) - - !associate the wrapper pointer the created model instance - bmi_box%ptr => bmi_model - - if( .not. associated( bmi_box ) .or. .not. associated( bmi_box%ptr ) ) then - bmi_status = BMI_FAILURE - else - ! Return the pointer to box - this = c_loc(bmi_box) - bmi_status = BMI_SUCCESS - endif - end function register_bmi -#endif - - ! non-BMI helper function to assign input fields - subroutine assign_basin_field(this, name, src_arr, isrc_arr) - implicit none - class (summa_bmi), intent(inout) :: this - character (len=*), intent(in) :: name - real, intent(in) :: src_arr(sum(gru_struc(:)%hruCount)) - integer, intent(in) :: isrc_arr - integer :: iGRU, jHRU, i - - summaVars: associate(& - timeStruct => this%model%summa1_struc(n)%timeStruct , & ! x%var(:) -- model time data - forcStruct => this%model%summa1_struc(n)%forcStruct , & ! x%gru(:)%hru(:)%var(:) -- model forcing data - diagStruct => this%model%summa1_struc(n)%diagStruct & ! x%gru(:)%hru(:)%var(:)%dat -- model diagnostic variables - ) - - if(name(1:5)=='model')then ! not currently used, left in for future integer type needs - select case (name) - ! input - case('model__time_year') - timeStruct%var(iLookTIME%iyyy) = isrc_arr - end select - else - do iGRU = 1, this%model%summa1_struc(n)%nGRU - do jHRU = 1, gru_struc(iGRU)%hruCount - i = (iGRU-1) * gru_struc(iGRU)%hruCount + jHRU - select case (name) - ! input - case('atmosphere_water__precipitation_mass_flux') - forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%pptrate) = src_arr(i) - case('land_surface_air__temperature') - forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airtemp) = src_arr(i) - case('atmosphere_air_water~vapor__relative_saturation') - forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%spechum) = src_arr(i) - case('land_surface_wind__x_component_of_velocity') - diagStruct%gru(iGRU)%hru(jHRU)%var(iLookDIAG%windspd_x)%dat(1) = src_arr(i) - case('land_surface_wind__y_component_of_velocity') - diagStruct%gru(iGRU)%hru(jHRU)%var(iLookDIAG%windspd_y)%dat(1) = src_arr(i) - case('land_surface_wind__speed') - forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%windspd) = src_arr(i) - case('land_surface_radiation~incoming~shortwave__energy_flux') - forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%SWRadAtm) = src_arr(i) - case('land_surface_radiation~incoming~longwave__energy_flux') - forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%LWRadAtm) = src_arr(i) - case('land_surface_air__pressure') - forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airpres) = src_arr(i) - end select - end do - end do - endif - end associate summaVars - end subroutine assign_basin_field - - ! non-BMI helper function to get fields, only get first do_nHRU of them - subroutine get_basin_field(this, name, do_nHRU, target_arr, itarget_arr) - implicit none - class (summa_bmi), intent(in) :: this - integer, intent(in) :: do_nHRU - character (len=*), intent(in) :: name - real, target, intent(out) :: target_arr(sum(gru_struc(:)%hruCount)) - integer, target, intent(out) :: itarget_arr - integer :: iGRU, jHRU, i - - summaVars: associate(& - timeStruct => this%model%summa1_struc(n)%timeStruct , & ! x%var(:) -- model time data - forcStruct => this%model%summa1_struc(n)%forcStruct , & ! x%gru(:)%hru(:)%var(:) -- model forcing data - progStruct => this%model%summa1_struc(n)%progStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model prognostic (state) variables - diagStruct => this%model%summa1_struc(n)%diagStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model diagnostic variables - fluxStruct => this%model%summa1_struc(n)%fluxStruct & ! x%gru(:)%hru(:)%var(:)%dat -- model fluxes - ) - target_arr = -999.0 - itarget_arr = -999 - if(name(1:5)=='model')then ! not currently used, left in for future integer type needs - select case (name) - ! input - case('model__time_year') - itarget_arr = timeStruct%var(iLookTIME%iyyy) - end select - else - do iGRU = 1, this%model%summa1_struc(n)%nGRU - do jHRU = 1, gru_struc(iGRU)%hruCount - i = (iGRU-1) * gru_struc(iGRU)%hruCount + jHRU - if (i > do_nHRU) return - select case (name) - ! input - case('atmosphere_water__precipitation_mass_flux') - target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%pptrate) - case('land_surface_air__temperature') - target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airtemp) - case('atmosphere_air_water~vapor__relative_saturation') - target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%spechum) - case('land_surface_wind__x_component_of_velocity') - target_arr(i) = diagStruct%gru(iGRU)%hru(jHRU)%var(iLookDIAG%windspd_x)%dat(1) - case('land_surface_wind__y_component_of_velocity') - target_arr(i) = diagStruct%gru(iGRU)%hru(jHRU)%var(iLookDIAG%windspd_y)%dat(1) - case('land_surface_wind__speed') - target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%windspd) - case('land_surface_radiation~incoming~shortwave__energy_flux') - target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%SWRadAtm) - case('land_surface_radiation~incoming~longwave__energy_flux') - target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%LWRadAtm) - case('land_surface_air__pressure') - target_arr(i) = forcStruct%gru(iGRU)%hru(jHRU)%var(iLookFORCE%airpres) - - ! output - case('land_surface_water__runoff_volume_flux') - target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarSurfaceRunoff)%dat(1) - case('land_surface_water__evaporation_mass_flux') - target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarGroundEvaporation)%dat(1) - case('land_vegetation_water__evaporation_mass_flux') - target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) - case('land_vegetation_water__transpiration_mass_flux') - target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) - case('snowpack__sublimation_mass_flux') - target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarSnowSublimation)%dat(1) - case('land_vegetation_water__sublimation_mass_flux') - target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopySublimation)%dat(1) - case('snowpack_mass') - target_arr(i) = progStruct%gru(iGRU)%hru(jHRU)%var(iLookPROG%scalarSWE)%dat(1) - case('soil_water__mass') - target_arr(i) = diagStruct%gru(iGRU)%hru(jHRU)%var(iLookDIAG%scalarTotalSoilWat)%dat(1) - case('land_vegetation_water__mass') - target_arr(i) = progStruct%gru(iGRU)%hru(jHRU)%var(iLookPROG%scalarCanopyWat)%dat(1) - case('land_surface_radiation~net~total__energy_flux') - target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarNetRadiation)%dat(1) - case('land_atmosphere_heat~net~latent__energy_flux') - target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarLatHeatTotal)%dat(1) - case('land_atmosphere_heat~net~sensible__energy_flux') - target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarSenHeatTotal)%dat(1) - case('atmosphere_energy~net~total__energy_flux') - target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1) - case('land_vegetation_energy~net~total__energy_flux') - target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1) - case('land_surface_energy~net~total__energy_flux') - target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1) - case('land_surface_water__baseflow_volume_flux') - target_arr(i) = fluxStruct%gru(iGRU)%hru(jHRU)%var(iLookFLUX%scalarAquiferBaseflow)%dat(1) - end select - end do - end do - endif - end associate summaVars - end subroutine get_basin_field - -end module summa_driver +program summa_driver +! driver program for summa simulations +! ***************************************************************************** +! * use desired modules +! ***************************************************************************** +! data types +USE nrtype ! variable types, etc. +USE summa_type, only: summa1_type_dec ! master summa data type +! subroutines and functions: model setup +USE summa_init, only: summa_initialize ! used to allocate/initialize summa data structures +USE summa_setup, only: summa_paramSetup ! used to initialize parameter data structures (e.g. vegetation and soil parameters) +USE summa_restart, only: summa_readRestart ! used to read restart data and reset the model state +! subroutines and functions: model simulation +USE summa_forcing, only: summa_readForcing ! used to read forcing data +USE summa_modelRun, only: summa_runPhysics ! used to run the summa physics for one time step +USE summa_writeOutput, only: summa_writeOutputFiles ! used to write the summa output files +! utility functions +USE summa_util, only: stop_program ! used to stop the summa program (with errors) +USE summa_util, only: handle_err ! used to process errors +! global data +USE globalData, only: numtim ! number of model time steps +implicit none + +! ***************************************************************************** +! * variable definitions +! ***************************************************************************** +! define the master summa data structure +type(summa1_type_dec), allocatable :: summa1_struc(:) +! define parameters for the model simulation +integer(i4b), parameter :: n=1 ! number of instantiations +! define timing information +integer(i4b) :: modelTimeStep ! index of model time step +! error control +integer(i4b) :: err=0 ! error code +character(len=1024) :: message='' ! error message +integer(i4b) :: iStep + +! ***************************************************************************** +! * preliminaries +! ***************************************************************************** + +! allocate space for the master summa structure +allocate(summa1_struc(n), stat=err) +if(err/=0) call stop_program(1, 'problem allocating master summa structure') + +! ***************************************************************************** +! * model setup/initialization +! ***************************************************************************** + +! declare and allocate summa data structures and initialize model state to known values +call summa_initialize(summa1_struc(n), err, message) +call handle_err(err, message) + +! initialize parameter data structures (e.g. vegetation and soil parameters) +call summa_paramSetup(summa1_struc(n), err, message) +call handle_err(err, message) + +! read restart data and reset the model state +call summa_readRestart(summa1_struc(n), err, message) +call handle_err(err, message) + +! ***************************************************************************** +! * model simulation +! ***************************************************************************** + +iStep = 1 +! loop through time +do modelTimeStep=1,numtim + + ! read model forcing data + call summa_readForcing(modelTimeStep, summa1_struc(n), err, message) + call handle_err(err, message) + + print *, 'step ---> ', iStep + ! run the summa physics for one time step + call summa_runPhysics(modelTimeStep, summa1_struc(n), err, message) + call handle_err(err, message) + + ! write the model output + call summa_writeOutputFiles(modelTimeStep, summa1_struc(n), err, message) + call handle_err(err, message) + + iStep = iStep + 1 +end do ! looping through time + +! successful end +call stop_program(0, 'finished simulation successfully.') +end program summa_driver diff --git a/build/source/driver/summa_run.f90 b/build/source/driver/summa_driverBmi.f90 similarity index 97% rename from build/source/driver/summa_run.f90 rename to build/source/driver/summa_driverBmi.f90 index 2bda95ee9..c2c819f44 100644 --- a/build/source/driver/summa_run.f90 +++ b/build/source/driver/summa_driverBmi.f90 @@ -18,14 +18,14 @@ ! You should have received a copy of the GNU General Public License ! along with this program. If not, see . -program summa_run +program summa_driverBmi ! driver program for summa simulations ! ***************************************************************************** ! * use desired modules ! ***************************************************************************** USE nrtype ! variable types, etc. ! subroutines and functions: model simulation - USE summa_driver + USE summa_bmi ! global data USE globalData,only:numtim ! number of time steps @@ -53,4 +53,4 @@ program summa_run end do ! (looping through time) istat = model%finalize() -end program summa_run +end program summa_driverBmi diff --git a/build/source/driver/summa_runBMI.f90 b/build/source/driver/summa_runBMI.f90 deleted file mode 100644 index 60578a56f..000000000 --- a/build/source/driver/summa_runBMI.f90 +++ /dev/null @@ -1,90 +0,0 @@ -! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington -! -! This file is part of SUMMA -! -! For more information see: http://www.ral.ucar.edu/projects/summa -! -! This program is free software: you can redistribute it and/or modify -! it under the terms of the GNU General Public License as published by -! the Free Software Foundation, either version 3 of the License, or -! (at your option) any later version. -! -! This program is distributed in the hope that it will be useful, -! but WITHOUT ANY WARRANTY; without even the implied warranty of -! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -! GNU General Public License for more details. -! -! You should have received a copy of the GNU General Public License -! along with this program. If not, see . - -program summa_runBMI - ! driver program for summa simulations - ! ***************************************************************************** - ! * use desired modules - ! ***************************************************************************** - ! subroutines and functions: model simulation - use summa_driver - use, intrinsic :: iso_fortran_env, only : file_unit=>input_unit - - implicit none - - ! ***************************************************************************** - ! * variable definitions - ! ***************************************************************************** - character (len=*), parameter :: output_file = "summa_bmi.out" - character (len=*), parameter :: var_name = "land_surface_water__runoff_volume_flux" - type (summa_bmi) :: model - integer :: arg_count = 0 - character (len=80) :: arg - integer :: i, j, istat, grid_id, grid_size - double precision :: current_time, end_time - real, allocatable :: runoff(:) - - ! ***************************************************************************** - ! * model simulation - ! ***************************************************************************** - do while (arg_count <= 1) - call get_command_argument(arg_count, arg) - arg_count = arg_count + 1 - end do - - if (len_trim(arg) == 0) then - write(*,"(a)") "Usage: summa_bmi.exe CONFIGURATION_FILE<80char" - write(*,"(a)") - write(*,"(a)") "Run the summa model through its BMI with a configuration file." - write(*,"(a)") "Output is written to the file `summa_bmi.out`." - stop - end if - - open(file_unit,file=output_file) - - write(file_unit,"(a)") "Initialize model." - istat = model%initialize(arg) - - istat = model%get_current_time(current_time) - istat = model%get_end_time(end_time) -! variable will need to be on a grid that is constant through simulation and set in initialization - istat = model%get_var_grid(var_name, grid_id) - istat = model%get_grid_size(grid_id, grid_size) - - allocate(runoff(grid_size)) - - do while (current_time <= end_time) - write(file_unit,"(a, f10.2)") "Model values at time = ", current_time - istat = model%get_value(var_name, runoff) - do j = 1, grid_size - write (file_unit,"(e13.5)", advance="no") runoff(j) - end do - write (file_unit,*) - istat = model%update() - istat = model%get_current_time(current_time) - end do - - deallocate(runoff) - istat = model%finalize() - write(file_unit,"(a)") "Finalize model." - - close(file_unit) - -end program summa_runBMI diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 633a5b864..88c55d876 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -132,7 +132,7 @@ subroutine varSubstep(& ! simulation of fluxes and residuals given a trial state vector USE getVectorz_module,only:popStateVec ! populate the state vector USE getVectorz_module,only:varExtract ! extract variables from the state vector -#ifdef SUNDIALS_ACTIVE +#ifdef IDA_ACTIVE USE systemSolvSundials_module,only:systemSolvSundials ! solve the system of equations for one time step #endif USE systemSolv_module,only:systemSolv ! solve the system of equations for one time step @@ -326,7 +326,7 @@ subroutine varSubstep(& ! solve the system of equations for a given state subset select case(ixNumericalMethod) case(sundials) -#ifdef SUNDIALS_ACTIVE +#ifdef IDA_ACTIVE call systemSolvSundials(& ! input: model control dtSubstep, & ! intent(in): time step (s) @@ -361,7 +361,7 @@ subroutine varSubstep(& untappedMelt(:) = 0._rkind ! set untapped melt energy to zero niter = 0 ! will not use #else - err=20; message=trim(message)//'cannot use num_method as sundials if did not compile with -DSUNDIALS_ACTIVE:BOOL=ON'; return + err=20; message=trim(message)//'cannot use num_method as sundials if did not compile with -DCMAKE_BUILD_TYPE=IDA'; return #endif case(bEuler) call systemSolv(& diff --git a/build/source/hookup/ascii_util.f90 b/build/source/hookup/ascii_util.f90 index da8a3ea5b..bde6b9c6b 100755 --- a/build/source/hookup/ascii_util.f90 +++ b/build/source/hookup/ascii_util.f90 @@ -77,22 +77,22 @@ subroutine split_line(inline,words,err,message) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! declare local variables - integer(i4b),parameter :: cLen=8192 - character(len=cLen) :: temp ! temporary line of characters - integer(i4b) :: iword ! loop through words - integer(i4b),parameter :: maxWords=1000 ! maximum number of words in a line - integer(i4b) :: i1 ! index at the start of a given word - character(len=256) :: cword ! the current word - integer(i4b) :: nWords ! number of words in the character string + integer(i4b),parameter :: cLen=8192 + character(len=cLen) :: temp ! temporary line of characters + integer(i4b) :: iword ! loop through words + integer(i4b),parameter :: maxWords=1000 ! maximum number of words in a line + integer(i4b) :: i1 ! index at the start of a given word + character(len=256) :: cword ! the current word + integer(i4b) :: nWords ! number of words in the character string ! define pointers for linked list type node - character(len=256) :: chardat - integer(i4b) :: ix - type(node),pointer :: next=>null() + character(len=256) :: chardat + integer(i4b) :: ix + type(node),pointer :: next=>null() end type node - type(node),pointer :: list=>null() - type(node),pointer :: current=>null() - type(node),pointer :: previous=>null() + type(node),pointer :: list=>null() + type(node),pointer :: current=>null() + type(node),pointer :: previous=>null() ! start procedure here err=0; message='split_line/' temp=inline ! initialize string of characters @@ -131,60 +131,71 @@ end subroutine split_line ! ********************************************************************************************************* ! public subroutine get_vlines: get valid lines of data from file and store as a vector of charater strings ! ********************************************************************************************************* - subroutine get_vlines(unt,vlines,err,message) - ! do not know how many valid lines, so use linked lists - implicit none - ! declare dummy arguments - integer(i4b),intent(in) :: unt ! file unit - character(len=linewidth),intent(out),allocatable :: vlines(:) ! vector of character strings - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! declare local variables - integer(i4b) :: iline ! loop through lines in the file - integer(i4b),parameter :: maxLines=1000000 ! maximum number of valid lines in a file - character(len=2048) :: temp ! character data or a given line - integer(i4b) :: icount ! counter for the valid lines - integer(i4b) :: iend ! index to indicate end of the file - ! define pointers for linked list - type node - character(len=2048) :: chardat - integer(i4b) :: ix - type(node),pointer :: next=>null() - end type node - type(node),pointer :: list=>null() - type(node),pointer :: current=>null() - type(node),pointer :: previous=>null() - ! start procedure here - err=0; message='get_vlines/' - ! ***** get the valid lines of data from the file and store in linked lists ***** - icount=0 ! initialize the counter for the valid lines - do iline=1,maxLines - read(unt,'(a)',iostat=iend)temp; if(iend/=0)exit ! read line of data - if (temp(1:1)=='!' .or. temp == '')cycle ! skip comment and empty lines - icount = icount+1 - ! add the variable to the linked list - if(.not.associated(list))then - allocate(list,previous,current); list=node(temp,icount,null()) - current=>list - else - allocate(current%next) - current%next=node(temp,icount,null()) - current=>current%next - end if - if (iline==maxLines)then; err=20; message=trim(message)//"exceedMaxLines"; return; end if - end do ! looping through the lines in the file (exit clause above will kick in) - ! ***** allocate space for the valid lines ***** - allocate(vlines(icount),stat=err) - if(err/=0)then; err=30; message=trim(message)//"problemAllocateVlines"; return; end if - ! ***** save the list in a vector, and deallocate space as we go... ***** - current=>list - do while(associated(current)) - vlines(current%ix) = current%chardat - previous=>current; current=>current%next - deallocate(previous) - end do - if(associated(list)) nullify(list) - end subroutine get_vlines +subroutine get_vlines(unt,vlines,err,message) + ! do not know how many valid lines, so use linked lists + implicit none + ! declare dummy arguments + integer(i4b),intent(in) :: unt ! file unit + character(len=linewidth),intent(out),allocatable :: vlines(:) ! vector of character strings + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! declare local variables + integer(i4b) :: iline ! loop through lines in the file + integer(i4b),parameter :: maxLines=1000000 ! maximum number of valid lines in a file + character(len=2048) :: temp ! character data or a given line + integer(i4b) :: icount ! counter for the valid lines + integer(i4b) :: iend ! index to indicate end of the file + character(len=2048),dimension(:),allocatable :: tempArray + ! define pointers for linked list + type node + character(len=2048) :: chardat + integer(i4b) :: ix + type(node),pointer :: next=>null() + end type node + ! type(node),pointer :: list=>null() + ! type(node),pointer :: current=>null() + ! type(node),pointer :: previous=>null() + ! start procedure here + err=0; message='get_vlines/' + allocate(tempArray(1000)) + ! ***** get the valid lines of data from the file and store in linked lists ***** + icount=0 ! initialize the counter for the valid lines + do iline=1,maxLines + read(unt,'(a)',iostat=iend)temp; if(iend/=0)exit ! read line of data + if (temp(1:1)=='!' .or. temp == '')cycle ! skip comment and empty lines + icount = icount+1 + ! add the variable to the linked list + ! if(.not.associated(list))then + ! allocate(list) + ! allocate(current) + ! allocate(previous) + ! list=node(temp,icount,null()) + ! current=>list + ! else + ! allocate(current%next) + ! current%next=node(temp,icount,null()) + ! current=>current%next + ! end if + tempArray(icount) = temp + if (iline==maxLines)then; err=20; message=trim(message)//"exceedMaxLines"; return; end if + end do ! looping through the lines in the file (exit clause above will kick in) + ! ***** allocate space for the valid lines ***** + allocate(vlines(icount),stat=err) + if(err/=0)then; err=30; message=trim(message)//"problemAllocateVlines"; return; end if + ! ***** save the list in a vector, and deallocate space as we go... ***** + do iline=1, icount + vlines(iline) = tempArray(iline) + ! print*, vlines(iline), "index = ", iline + end do + ! current=>list + ! do while(associated(current)) + ! ! vlines(current%ix) = current%chardat + ! previous=>current; current=>current%next + ! deallocate(previous) + ! end do + ! if(associated(list)) nullify(list) + deallocate(tempArray) +end subroutine get_vlines end module ascii_util_module From f325fec89f3627d65e69e536164914f42bb63a51 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 9 May 2023 23:36:10 +0900 Subject: [PATCH 0708/1472] fixing small bugs in cmake scripts and compiler directives --- build/cmake/CMakeLists.txt | 77 +++++++++++++------------- build/cmake/build.cluster.bash | 4 +- build/cmake/build.mac.bash | 4 +- build/source/driver/summa_modelRun.f90 | 9 ++- build/source/dshare/var_lookup.f90 | 2 + build/source/engine/run_oneHRU.f90 | 4 +- 6 files changed, 55 insertions(+), 45 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index e1280ecd1..13c0e4767 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -10,7 +10,7 @@ SET (CMAKE_Fortran_COMPILER gfortran) # Other options include how to drive the code, NexGen (with BMI) or Actors (without BMI) # framework or no extra framework (without BMI). # Note, Sundials, NexGen, and Actors will all require extra libraries to be downloaded -# and compiled. +# and compiled. Currently, NexGen cannot work with Actors. #========================================================================================= # Set default configuration type to Release @@ -40,7 +40,7 @@ if(CMAKE_BUILD_TYPE MATCHES Kinsol OR CMAKE_BUILD_TYPE MATCHES IDA) endif() if(CMAKE_BUILD_TYPE MATCHES NexGen) - message("\nUsing NexGen Framework, should have been installed previously\n") + message("\nUsing NexGen Framework, should have been installed previously") add_compile_definitions(NGEN_ACTIVE BMI_ACTIVE NGEN_FORCING_ACTIVE NGEN_OUTPUT_ACTIVE) project(summabmi VERSION 1.0.0 DESCRIPTION "Summa-Sundials-BE BMI Module Shared Library") @@ -64,7 +64,7 @@ else() set(F_MASTER ../../) # directory of summa source code if(CMAKE_BUILD_TYPE MATCHES Actors) - message("\nUsing Actors Framework, should have been installed previously\n") + message("\nUsing Actors Framework, should have been installed previously") add_compile_definitions(ACTORS_ACTIVE) project(summaactors DESCRIPTION "Summa-Sundials-BE Actors") @@ -96,33 +96,37 @@ endif() # Set compiler flags if(CMAKE_BUILD_TYPE MATCHES Debug) - message("\nSetting Debug Options\n") - add_definitions(-DDEBUG) + message("\nSetting Debug Options") + add_compile_definitions(DEBUG) set(FLAGS_NOAH -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) - set(FLAGS_ALL -g -O0 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) - set(FLAGS_CXX -g -O0 -Wfatal-errors -std=c++17) + set(FLAGS_ALL -g -O0 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp) + set(FLAGS_CXX -g -O0 -Wfatal-errors -std=c++17) else() - message("\nSetting Release Options\n") + message("\nSetting Release Options") set(FLAGS_NOAH -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) - set(FLAGS_ALL -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) - set(FLAGS_CXX -O3 -Wfatal-errors -std=c++17) + set(FLAGS_ALL -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp) + set(FLAGS_CXX -O3 -Wfatal-errors -std=c++17) endif() -# Find Sundials if exists -set(DIR_SUNDIALS "") -foreach(dir IN ITEMS - "${F_MASTER}../sundials/instdir" - "${PARENT_DIR}../sundials/instdir" - "../../../../SummaSundials/sundials/instdir" - if(EXISTS ${dir}) - set(DIR_SUNDIALS ${dir}) - break() - endif() -endforeach() +# Find Sundials if exists, add path here if FATAL_ERROR "Did not find Sundials directory" +if(CMAKE_BUILD_TYPE MATCHES Kinsol OR CMAKE_BUILD_TYPE MATCHES IDA) + set(DIR_SUNDIALS "") + foreach(dir IN ITEMS + "${F_MASTER}../sundials/instdir" + "${PARENT_DIR}../sundials/instdir" + "../../../../SummaSundials/sundials/instdir") + if(EXISTS ${dir}) + set(DIR_SUNDIALS ${dir}) + message("\nFound Sundials directory at ${DIR_SUNDIALS}") + break() + endif() + message(FATAL_ERROR "\nDid not find Sundials directory, edit CMakeLists.txt to include path") + endforeach() +endif() # Set libraries for all builds if(CMAKE_BUILD_TYPE MATCHES Cluster) - message("\Building for cluster\n") + message("\nBuilding for cluster") # MKL needs BLA_VENDOR set with cmake set(BLA_VENDOR OpenBLAS) @@ -135,7 +139,7 @@ if(CMAKE_BUILD_TYPE MATCHES Cluster) set(INCLUDES $ENV{EBROOTNETCDFMINFORTRAN}/include ${netCDF_INCLUDES} ${LAPACK_INCLUDES}) set(LIBRARIES SUMMA_NOAHMP ${netCDF_LIBRARIES} ${LAPACK_LIBRARIES} -lnetcdff -lopenblas) - if(SUNDIALS_ACTIVE) + if(CMAKE_BUILD_TYPE MATCHES Kinsol OR CMAKE_BUILD_TYPE MATCHES IDA) link_directories(${DIR_SUNDIALS}/lib64) set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib64") set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran) @@ -149,7 +153,7 @@ if(CMAKE_BUILD_TYPE MATCHES Cluster) else() - message("\Building for personal computer\n") + message("\nBuilding for personal computer") set(SDKROOT "$(xcrun --show-sdk-path)") @@ -157,7 +161,7 @@ else() set(INCLUDES /opt/local/include /opt/local/lib) set(LIBRARIES SUMMA_NOAHMP -llapack -lgfortran -lnetcdff -lnetcdf) - if(SUNDIALS_ACTIVE) + if(CMAKE_BUILD_TYPE MATCHES Kinsol OR CMAKE_BUILD_TYPE MATCHES IDA) link_directories(${DIR_SUNDIALS}/lib) set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib") set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran) @@ -175,20 +179,20 @@ endif() if(CMAKE_BUILD_TYPE MATCHES IDA) - message("\nUsing SUNDIALS IDA libraries, should have been installed previously\n") + message("\nUsing SUNDIALS IDA libraries, should have been installed previously") set(LIB_SUNDIALS -lsundials_fida_mod -lsundials_fnvecmanyvector_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) elseif(CMAKE_BUILD_TYPE MATCHES Kinsol) - message("\nUsing SUNDIALS Kinsol libraries, should have been installed previously\n") + message("\nUsing SUNDIALS Kinsol libraries, should have been installed previously") set(LIB_SUNDIALS -lsundials_fkinsol_mod -lsundials_fnvecserial_mod -lsundials_fsunnonlinsolnewton_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) elseif(CMAKE_BUILD_TYPE MATCHES BE) - message("\nUsing Home-grown Backward Euler\n") + message("\nUsing Home-grown Backward Euler") else() - message("Valid build types are: ${CMAKE_CONFIGURATION_TYPES}") - message(FATAL_ERROR "Unknown build type: ${CMAKE_BUILD_TYPE}") + message("\nValid build types are: ${CMAKE_CONFIGURATION_TYPES}") + message(FATAL_ERROR "\nUnknown build type: ${CMAKE_BUILD_TYPE}") endif() message("\nBuilding SUMMA\n") @@ -317,7 +321,7 @@ set(MODRUN ${ENGINE_DIR}/volicePack.f90) # Note, eventually these should be added to Kinsol set(MODRUN_NOT_KINSOL - ${ENGINE_DIR}/t2enthalpy.f90 + ${ENGINE_DIR}/t2enthalpy.f90) set(MODRUN_IDA ${ENGINE_DIR}/updateVarsSundials.f90) @@ -359,7 +363,7 @@ set(SOLVER_IDA ${ENGINE_DIR}/type4IDA.f90) set(SOLVER_NOT_ACTOR ${ENGINE_DIR}/run_oneGRU.f90 - ${ENGINE_DIR}/run_oneHRU.f90s) + ${ENGINE_DIR}/run_oneHRU.f90) # Driver support modules set(DRIVER @@ -482,7 +486,6 @@ if(CMAKE_BUILD_TYPE MATCHES IDA) ${MODRUN_IDA} ${SOLVER_IDA}) endif() - if(NOT CMAKE_BUILD_TYPE MATCHES Kinsol) set(SUMMA_ALL ${SUMMA_ALL} ${MODRUN_NOT_KINSOL} @@ -536,7 +539,7 @@ if(CMAKE_BUILD_TYPE MATCHES NexGen) endif() target_compile_options(summabmi PRIVATE ${FLAGS_ALL}) - if(SUNDIALS_ACTIVE) + if(CMAKE_BUILD_TYPE MATCHES Kinsol OR CMAKE_BUILD_TYPE MATCHES IDA) target_include_directories(summabmi PUBLIC ${INCLUDES} ${INC_SUNDIALS}) target_link_libraries(summabmi PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_COMM) else() @@ -559,7 +562,7 @@ elseif(CMAKE_BUILD_TYPE MATCHES Actors) add_library(summaactors SHARED ${SUMMA_ALL}) target_compile_options(summaactors PRIVATE ${FLAGS_ALL}) - if(SUNDIALS_ACTIVE) + if(CMAKE_BUILD_TYPE MATCHES Kinsol OR CMAKE_BUILD_TYPE MATCHES IDA) target_include_directories(summaactors PUBLIC ${INCLUDES} ${INC_SUNDIALS}) target_link_libraries(summaactors PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_COMM) else() @@ -567,7 +570,6 @@ elseif(CMAKE_BUILD_TYPE MATCHES Actors) target_link_libraries(summaactors PUBLIC ${LIBRARIES} SUMMA_COMM) endif() - set_target_properties(summaactors PROPERTIES VERSION ${PROJECT_VERSION}) add_executable(${EXEC_NAME} ${MAIN_ACTOR} ${ACTORS_GLOBAL} ${HRU_ACTOR} @@ -586,7 +588,7 @@ else() add_library(summa SHARED ${SUMMA_ALL}) target_compile_options(summa PRIVATE ${FLAGS_ALL}) - if(SUNDIALS_ACTIVE) + if(CMAKE_BUILD_TYPE MATCHES Kinsol OR CMAKE_BUILD_TYPE MATCHES IDA) target_include_directories(summa PUBLIC ${INCLUDES} ${INC_SUNDIALS}) target_link_libraries(summa PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_COMM) else() @@ -594,7 +596,6 @@ else() target_link_libraries(summa PUBLIC ${LIBRARIES} SUMMA_COMM) endif() - set_target_properties(summa PROPERTIES VERSION ${PROJECT_VERSION}) add_executable(${EXEC_NAME} ${MAIN_SUMMA}) set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) endif() diff --git a/build/cmake/build.cluster.bash b/build/cmake/build.cluster.bash index ad8163825..72183e40c 100644 --- a/build/cmake/build.cluster.bash +++ b/build/cmake/build.cluster.bash @@ -14,6 +14,6 @@ module load udunits/2.2.28 # for Actors module load caf -cmake -B ../cmake_build -S ../../ -DCMAKE_BUILD_TYPE=IDA_Cluster -cmake -j ../cmake_build --target all +cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=IDA_Cluster +cmake --build ../cmake_build --target all diff --git a/build/cmake/build.mac.bash b/build/cmake/build.mac.bash index ad24267d8..706f8f689 100755 --- a/build/cmake/build.mac.bash +++ b/build/cmake/build.mac.bash @@ -2,6 +2,6 @@ # build on Mac, from cmake directory run this as ./build.mac.bash -cmake -B ../cmake_build -S summa -DCMAKE_BUILD_TYPE=IDA -cmake -j ../cmake_build --target all +cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=IDA +cmake --build ../cmake_build --target all diff --git a/build/source/driver/summa_modelRun.f90 b/build/source/driver/summa_modelRun.f90 index fda10a006..9aa6e0886 100644 --- a/build/source/driver/summa_modelRun.f90 +++ b/build/source/driver/summa_modelRun.f90 @@ -32,6 +32,10 @@ module summa_modelRun USE var_lookup,only:iLookINDEX ! look-up values for local column index variables USE summa_util,only:handle_err +! these are needed because we cannot access them in modules locally if we might use those modules with Actors +USE globalData,only:fracJulday ! fractional julian days since the start of year +USE globalData,only:yearLength ! number of days in the current year + ! safety: set private unless specified otherwise implicit none private @@ -127,6 +131,9 @@ subroutine summa_runPhysics(modelTimeStep, summa1_struc, err, message) ! get vegetation phenology ! (compute the exposed LAI and SAI and whether veg is buried by snow) call vegPhenlgy(& + ! model control + fracJulDay, & ! intent(in): fractional julian days since the start of year + yearLength, & ! intent(in): number of days in the current year ! input/output: data structures model_decisions, & ! intent(in): model decisions typeStruct%gru(iGRU)%hru(iHRU), & ! intent(in): type of vegetation and soil @@ -221,7 +228,7 @@ subroutine summa_runPhysics(modelTimeStep, summa1_struc, err, message) ! lookup table structure lookupStruct => summa1_struc%lookupStruct , & ! x%gru(:)%hru(:)%z(:)%var(:)%lookup -- lookup-tables - + ! run time variables greenVegFrac_monthly => summa1_struc%greenVegFrac_monthly, & ! fraction of green vegetation in each month (0-1) computeVegFlux => summa1_struc%computeVegFlux , & ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 3e77c9a9e..22da2bd21 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -392,6 +392,8 @@ MODULE var_lookup integer(i4b) :: scalarNewSnowDensity = integerMissing ! density of fresh snow (kg m-3) integer(i4b) :: scalarO2air = integerMissing ! atmospheric o2 concentration (Pa) integer(i4b) :: scalarCO2air = integerMissing ! atmospheric co2 concentration (Pa) + integer(i4b) :: windspd_x = integerMissing ! wind speed at 10 meter height in x-direction (m s-1) + integer(i4b) :: windspd_y = integerMissing ! wind speed at 10 meter height in y-direction (m s-1) ! shortwave radiation integer(i4b) :: scalarCosZenith = integerMissing ! cosine of the solar zenith angle (0-1) integer(i4b) :: scalarFractionDirect = integerMissing ! fraction of direct radiation (0-1) diff --git a/build/source/engine/run_oneHRU.f90 b/build/source/engine/run_oneHRU.f90 index 4c9c4d5cd..71b343481 100644 --- a/build/source/engine/run_oneHRU.f90 +++ b/build/source/engine/run_oneHRU.f90 @@ -201,7 +201,7 @@ subroutine run_oneHRU(& progData, & ! data structure of model prognostic variables diagData, & ! data structure of model diagnostic variables fluxData, & ! data structure of model fluxes - tmZoneOffsetFracDay & ! time zone offset in fractional days + tmZoneOffsetFracDay,& ! time zone offset in fractional days err,cmessage) ! error control if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif @@ -215,7 +215,7 @@ subroutine run_oneHRU(& ! model control hruId, & ! intent(in): hruId dt_init, & ! intent(inout): initial time step - 1._rkind, & ! intent(in): used to adjust the length of the timestep with failure in Actors (non-Actors here, always 1) + 1, & ! intent(in): used to adjust the length of the timestep with failure in Actors (non-Actors here, always 1) computeVegFlux, & ! intent(inout): flag to indicate if we are computing fluxes over vegetation fracJulDay, & ! intent(in): fractional julian days since the start of year yearLength, & ! intent(in): number of days in the current year From 484b06b19dac60789c023f7e27b0433399552f9a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 9 May 2023 23:39:08 +0900 Subject: [PATCH 0709/1472] correct driver file --- build/source/driver/summa_driver.f90 | 146 ++++++++++++++------------- 1 file changed, 74 insertions(+), 72 deletions(-) mode change 100755 => 100644 build/source/driver/summa_driver.f90 diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 old mode 100755 new mode 100644 index 708c95f8b..db67ae4cc --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -19,90 +19,92 @@ ! along with this program. If not, see . program summa_driver -! driver program for summa simulations -! ***************************************************************************** -! * use desired modules -! ***************************************************************************** -! data types -USE nrtype ! variable types, etc. -USE summa_type, only: summa1_type_dec ! master summa data type -! subroutines and functions: model setup -USE summa_init, only: summa_initialize ! used to allocate/initialize summa data structures -USE summa_setup, only: summa_paramSetup ! used to initialize parameter data structures (e.g. vegetation and soil parameters) -USE summa_restart, only: summa_readRestart ! used to read restart data and reset the model state -! subroutines and functions: model simulation -USE summa_forcing, only: summa_readForcing ! used to read forcing data -USE summa_modelRun, only: summa_runPhysics ! used to run the summa physics for one time step -USE summa_writeOutput, only: summa_writeOutputFiles ! used to write the summa output files -! utility functions -USE summa_util, only: stop_program ! used to stop the summa program (with errors) -USE summa_util, only: handle_err ! used to process errors -! global data -USE globalData, only: numtim ! number of model time steps -implicit none + ! driver program for summa simulations + ! ***************************************************************************** + ! * use desired modules + ! ***************************************************************************** + ! data types + USE nrtype ! variable types, etc. + USE summa_type, only: summa1_type_dec ! master summa data type + ! subroutines and functions: model setup + USE summa_init, only: summa_initialize ! used to allocate/initialize summa data structures + USE summa_setup, only: summa_paramSetup ! used to initialize parameter data structures (e.g. vegetation and soil parameters) + USE summa_restart, only: summa_readRestart ! used to read restart data and reset the model state + ! subroutines and functions: model simulation + USE summa_forcing, only: summa_readForcing ! used to read forcing data + USE summa_modelRun, only: summa_runPhysics ! used to run the summa physics for one time step + USE summa_writeOutput, only: summa_writeOutputFiles ! used to write the summa output files + ! utility functions + USE summa_util, only: stop_program ! used to stop the summa program (with errors) + USE summa_util, only: handle_err ! used to process errors + ! global data + USE globalData, only: numtim ! number of model time steps + USE globalData, only: print_step_freq + implicit none -! ***************************************************************************** -! * variable definitions -! ***************************************************************************** -! define the master summa data structure -type(summa1_type_dec), allocatable :: summa1_struc(:) -! define parameters for the model simulation -integer(i4b), parameter :: n=1 ! number of instantiations -! define timing information -integer(i4b) :: modelTimeStep ! index of model time step -! error control -integer(i4b) :: err=0 ! error code -character(len=1024) :: message='' ! error message -integer(i4b) :: iStep + ! ***************************************************************************** + ! * variable definitions + ! ***************************************************************************** + ! define the master summa data structure + type(summa1_type_dec), allocatable :: summa1_struc(:) + ! define parameters for the model simulation + integer(i4b), parameter :: n=1 ! number of instantiations + ! define timing information + integer(i4b) :: modelTimeStep ! index of model time step + ! error control + integer(i4b) :: err=0 ! error code + character(len=1024) :: message='' ! error message -! ***************************************************************************** -! * preliminaries -! ***************************************************************************** + ! ***************************************************************************** + ! * preliminaries + ! ***************************************************************************** -! allocate space for the master summa structure -allocate(summa1_struc(n), stat=err) -if(err/=0) call stop_program(1, 'problem allocating master summa structure') + ! allocate space for the master summa structure + allocate(summa1_struc(n), stat=err) + if(err/=0) call stop_program(1, 'problem allocating master summa structure') -! ***************************************************************************** -! * model setup/initialization -! ***************************************************************************** + ! ***************************************************************************** + ! * model setup/initialization + ! ***************************************************************************** -! declare and allocate summa data structures and initialize model state to known values -call summa_initialize(summa1_struc(n), err, message) -call handle_err(err, message) + ! declare and allocate summa data structures and initialize model state to known values + call summa_initialize(summa1_struc(n), err, message) + call handle_err(err, message) -! initialize parameter data structures (e.g. vegetation and soil parameters) -call summa_paramSetup(summa1_struc(n), err, message) -call handle_err(err, message) + ! initialize parameter data structures (e.g. vegetation and soil parameters) + call summa_paramSetup(summa1_struc(n), err, message) + call handle_err(err, message) -! read restart data and reset the model state -call summa_readRestart(summa1_struc(n), err, message) -call handle_err(err, message) + ! read restart data and reset the model state + call summa_readRestart(summa1_struc(n), err, message) + call handle_err(err, message) -! ***************************************************************************** -! * model simulation -! ***************************************************************************** + ! ***************************************************************************** + ! * model simulation + ! ***************************************************************************** + ! loop through time + do modelTimeStep=1,numtim -iStep = 1 -! loop through time -do modelTimeStep=1,numtim + ! read model forcing data + call summa_readForcing(modelTimeStep, summa1_struc(n), err, message) + call handle_err(err, message) - ! read model forcing data - call summa_readForcing(modelTimeStep, summa1_struc(n), err, message) - call handle_err(err, message) + if (mod(modelTimeStep, print_step_freq) == 0)then + print *, 'step ---> ', modelTimeStep + endif + ! run the summa physics for one time step + call summa_runPhysics(modelTimeStep, summa1_struc(n), err, message) + call handle_err(err, message) - print *, 'step ---> ', iStep - ! run the summa physics for one time step - call summa_runPhysics(modelTimeStep, summa1_struc(n), err, message) - call handle_err(err, message) + ! write the model output + call summa_writeOutputFiles(modelTimeStep, summa1_struc(n), err, message) + call handle_err(err, message) + end do ! looping through time - ! write the model output - call summa_writeOutputFiles(modelTimeStep, summa1_struc(n), err, message) - call handle_err(err, message) + ! successful end + call stop_program(0, 'finished simulation successfully.') - iStep = iStep + 1 -end do ! looping through time + ! to prevent exiting before HDF5 has closed + call sleep(2) -! successful end -call stop_program(0, 'finished simulation successfully.') end program summa_driver From 927ab89375cbe17c284db7c2c0170104900ee84e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 10 May 2023 00:14:33 +0900 Subject: [PATCH 0710/1472] compiles Sundials IDA version now --- build/cmake/CMakeLists.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 13c0e4767..a310305c3 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -30,13 +30,18 @@ set(CMAKE_CONFIGURATION_TYPES BE BE_Debug BE_Cluster BE_Cluster_Debug IDA_Actors IDA_Actors_Debug IDA_Actors_Cluster IDA_Actors_Cluster_Debug) message("\nSelected Bulid Type: ${CMAKE_BUILD_TYPE}\n") +# Give executable names to be backwards compatible with old versions if(CMAKE_BUILD_TYPE MATCHES Kinsol OR CMAKE_BUILD_TYPE MATCHES IDA) add_compile_definitions(SUNDIALS_ACTIVE) if(CMAKE_BUILD_TYPE MATCHES Kinsol) add_compile_definitions(KINSOL_ACTIVE) + set(EXEC_NAME summa_kinsol.exe) elseif(CMAKE_BUILD_TYPE MATCHES IDA) add_compile_definitions(IDA_ACTIVE) + set(EXEC_NAME summa_sundials.exe) endif() +else() + set(EXEC_NAME summa.exe) endif() if(CMAKE_BUILD_TYPE MATCHES NexGen) @@ -497,9 +502,6 @@ set(MAIN_ACTOR ${ACTORS_DIR}/main.cpp) set(MAIN_BMI ${DRIVER_DIR}/summa_driverBMI.f90) # do not need a driver in NexGen so do not use set(MAIN_SUMMA ${DRIVER_DIR}/summa_driver.f90) -# Define the executable -set(EXEC_NAME summa_sundials) - # Define version number, not working correctly set(VERSIONFILE ${DRIVER_DIR}/summaversion.inc) execute_process(COMMAND " ${GIT_EXECUTABLE} tag | tail -n 1" OUTPUT_VARIABLE VERSION) @@ -547,7 +549,6 @@ if(CMAKE_BUILD_TYPE MATCHES NexGen) target_link_libraries(summabmi PUBLIC ${LIBRARIES} SUMMA_COMM) endif() target_link_libraries(summabmi PUBLIC iso_c_bmi) - target_compile_options(summabmi PUBLIC -cpp -DNGEN_ACTIVE) set_target_properties(summabmi PROPERTIES VERSION ${PROJECT_VERSION}) include(GNUInstallDirs) @@ -598,4 +599,5 @@ else() add_executable(${EXEC_NAME} ${MAIN_SUMMA}) set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) + target_link_libraries(${EXEC_NAME} summa) endif() From 0e4d218def567dcee789fd9cbc7e0e7bc452d966 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 10 May 2023 10:17:41 +0900 Subject: [PATCH 0711/1472] file clean up --- build/source/engine/derivforce.f90 | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/build/source/engine/derivforce.f90 b/build/source/engine/derivforce.f90 index 3e12e48ae..aff470750 100755 --- a/build/source/engine/derivforce.f90 +++ b/build/source/engine/derivforce.f90 @@ -273,10 +273,10 @@ subroutine derivforce(time_data,forc_data,attr_data,mpar_data,prog_data,diag_dat else scalarFractionDirect = 0._rkind end if - ! compute direct shortwave radiation, in the visible and near-infra-red part of the spectrum - spectralIncomingDirect(1) = SWRadAtm*scalarFractionDirect*Frad_vis ! (direct vis) - spectralIncomingDirect(2) = SWRadAtm*scalarFractionDirect*(1._rkind - Frad_vis) ! (direct nir) - ! compute diffuse shortwave radiation, in the visible and near-infra-red part of the spectrum + + ! compute direct shortwave and diffuse radiation, in the visible and near-infra-red part of the spectrum + spectralIncomingDirect(1) = SWRadAtm*scalarFractionDirect*Frad_vis ! (direct vis) + spectralIncomingDirect(2) = SWRadAtm*scalarFractionDirect*(1._rkind - Frad_vis) ! (direct nir) spectralIncomingDiffuse(1) = SWRadAtm*(1._rkind - scalarFractionDirect)*Frad_vis ! (diffuse vis) spectralIncomingDiffuse(2) = SWRadAtm*(1._rkind - scalarFractionDirect)*(1._rkind - Frad_vis) ! (diffuse nir) @@ -292,7 +292,6 @@ subroutine derivforce(time_data,forc_data,attr_data,mpar_data,prog_data,diag_dat ! compute vapor pressure of the air above the vegetation canopy (Pa) VPair = vapPress(spechum,airpres) - !print*, 'VPair = ', VPair ! compute wet bulb temperature (K) twetbulb = WETBULBTMP(airtemp, relhum, airpres) From 19e8a59613cf011250c85da93153c1dedcc6d961 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 10 May 2023 16:16:53 +0900 Subject: [PATCH 0712/1472] First steps of adding kinsol solver, putting in decision stub and moving files around --- build/cmake/CMakeLists.txt | 115 +++++++----------- build/cmake/build.cluster.bash | 2 +- build/cmake/build.mac.bash | 2 +- build/source/driver/summa_driverBmi.f90 | 4 +- build/source/dshare/get_ixname.f90 | 24 ++-- build/source/dshare/popMetadat.f90 | 24 ++-- build/source/{engine => dshare}/tol4IDA.f90 | 18 +-- build/source/{engine => dshare}/type4IDA.f90 | 0 build/source/dshare/type4kinsol.f90 | 52 ++++++++ build/source/engine/coupled_em.f90 | 11 +- build/source/engine/mDecisions.f90 | 12 +- build/source/engine/opSplittin.f90 | 11 +- build/source/engine/summaSolveSundialsIDA.f90 | 2 +- build/source/engine/varSubstep.f90 | 33 +++-- docs/sundials_bmi/flags_params_sundials.txt | 4 +- .../celia1990/common/summa_zDecisions.txt | 7 +- 16 files changed, 181 insertions(+), 140 deletions(-) rename build/source/{engine => dshare}/tol4IDA.f90 (97%) rename build/source/{engine => dshare}/type4IDA.f90 (100%) create mode 100644 build/source/dshare/type4kinsol.f90 diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index a310305c3..1ed6adfa3 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -6,11 +6,17 @@ SET (CMAKE_Fortran_COMPILER gfortran) #========================================================================================= # When compiling Summa each configuration can be compiled in a Cluster or Debug mode, # which is set by the CMAKE_BUILD_TYPES variables. The default is BE, release mode no -# cluster no Sundials no Actors no NexGen. -# Other options include how to drive the code, NexGen (with BMI) or Actors (without BMI) -# framework or no extra framework (without BMI). +# cluster no Sundials no Actors no NexGen which runs the code with BE based off Numerical +# Recipes only. # Note, Sundials, NexGen, and Actors will all require extra libraries to be downloaded # and compiled. Currently, NexGen cannot work with Actors. +# If you install Sundials, you can compile Summa with Sundials, which allows you to run +# Summa with a solver of BE based off Numerical Recipes, Kinsol, or IDA. The solver choice +# is then set in the code decisions file. +# Other options include how to drive the code, NexGen (with BMI) or Actors (without BMI) +# framework or no extra framework (without BMI). +# If you change the build type, you will need to delete the CMakeCache.txt file and +# rerun cmake. #========================================================================================= # Set default configuration type to Release @@ -22,25 +28,16 @@ endif() set(CMAKE_CONFIGURATION_TYPES BE BE_Debug BE_Cluster BE_Cluster_Debug BE_NexGen BE_NexGen_Debug BE_NexGen_Cluster BE_NexGen_Cluster_Debug BE_Actors BE_Actors_Debug BE_Actors_Cluster BE_Actors_Cluster_Debug - Kinsol Kinsol_Debug Kinsol_Cluster Kinsol_Cluster_Debug - Kinsol_NexGen Kinsol_NexGen_Debug Kinsol_NexGen_Cluster Kinsol_NexGen_Cluster_Debug - Kinsol_Actors Kinsol_Actors_Debug Kinsol_Actors_Cluster Kinsol_Actors_Cluster_Debug - IDA IDA_Debug IDA_Cluster IDA_Cluster_Debug - IDA_NexGen IDA_NexGen_Debug IDA_NexGen_Cluster IDA_NexGen_Cluster_Debug - IDA_Actors IDA_Actors_Debug IDA_Actors_Cluster IDA_Actors_Cluster_Debug) + Sundials Sundials_Debug Sundials_Cluster Sundials_Cluster_Debug + Sundials_NexGen Sundials_NexGen_Debug Sundials_NexGen_Cluster Sundials_NexGen_Cluster_Debug + Sundials_Actors Sundials_Actors_Debug Sundials_Actors_Cluster Sundials_Actors_Cluster_Debug) message("\nSelected Bulid Type: ${CMAKE_BUILD_TYPE}\n") # Give executable names to be backwards compatible with old versions -if(CMAKE_BUILD_TYPE MATCHES Kinsol OR CMAKE_BUILD_TYPE MATCHES IDA) +if(CMAKE_BUILD_TYPE MATCHES Sundials) add_compile_definitions(SUNDIALS_ACTIVE) - if(CMAKE_BUILD_TYPE MATCHES Kinsol) - add_compile_definitions(KINSOL_ACTIVE) - set(EXEC_NAME summa_kinsol.exe) - elseif(CMAKE_BUILD_TYPE MATCHES IDA) - add_compile_definitions(IDA_ACTIVE) - set(EXEC_NAME summa_sundials.exe) - endif() -else() + set(EXEC_NAME summa_sundials.exe) +elseif(CMAKE_BUILD_TYPE MATCHES BE) set(EXEC_NAME summa.exe) endif() @@ -114,7 +111,7 @@ else() endif() # Find Sundials if exists, add path here if FATAL_ERROR "Did not find Sundials directory" -if(CMAKE_BUILD_TYPE MATCHES Kinsol OR CMAKE_BUILD_TYPE MATCHES IDA) +if(CMAKE_BUILD_TYPE MATCHES Sundials) set(DIR_SUNDIALS "") foreach(dir IN ITEMS "${F_MASTER}../sundials/instdir" @@ -144,7 +141,7 @@ if(CMAKE_BUILD_TYPE MATCHES Cluster) set(INCLUDES $ENV{EBROOTNETCDFMINFORTRAN}/include ${netCDF_INCLUDES} ${LAPACK_INCLUDES}) set(LIBRARIES SUMMA_NOAHMP ${netCDF_LIBRARIES} ${LAPACK_LIBRARIES} -lnetcdff -lopenblas) - if(CMAKE_BUILD_TYPE MATCHES Kinsol OR CMAKE_BUILD_TYPE MATCHES IDA) + if(CMAKE_BUILD_TYPE MATCHES Sundials) link_directories(${DIR_SUNDIALS}/lib64) set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib64") set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran) @@ -166,7 +163,7 @@ else() set(INCLUDES /opt/local/include /opt/local/lib) set(LIBRARIES SUMMA_NOAHMP -llapack -lgfortran -lnetcdff -lnetcdf) - if(CMAKE_BUILD_TYPE MATCHES Kinsol OR CMAKE_BUILD_TYPE MATCHES IDA) + if(CMAKE_BUILD_TYPE MATCHES Sundials) link_directories(${DIR_SUNDIALS}/lib) set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib") set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran) @@ -183,18 +180,11 @@ else() endif() -if(CMAKE_BUILD_TYPE MATCHES IDA) - message("\nUsing SUNDIALS IDA libraries, should have been installed previously") - - set(LIB_SUNDIALS -lsundials_fida_mod -lsundials_fnvecmanyvector_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) - -elseif(CMAKE_BUILD_TYPE MATCHES Kinsol) - message("\nUsing SUNDIALS Kinsol libraries, should have been installed previously") - - set(LIB_SUNDIALS -lsundials_fkinsol_mod -lsundials_fnvecserial_mod -lsundials_fsunnonlinsolnewton_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod) +if(CMAKE_BUILD_TYPE MATCHES Sundials) + message("\nUsing SUNDIALS libraries for IDA and Kinsol, should have been installed previously") + set(LIB_SUNDIALS -lsundials_fida_mod -lsundials_fnvecmanyvector_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod -lsundials_fkinsol_mod -lsundials_fsunnonlinsolnewton_mod) elseif(CMAKE_BUILD_TYPE MATCHES BE) - message("\nUsing Home-grown Backward Euler") - + message("\nUsing Backward Euler based off free versions of Numerical Recipes only") else() message("\nValid build types are: ${CMAKE_CONFIGURATION_TYPES}") message(FATAL_ERROR "\nUnknown build type: ${CMAKE_BUILD_TYPE}") @@ -267,9 +257,10 @@ set(DATAMS ${DSHARE_DIR}/outpt_stat.f90 ${DSHARE_DIR}/popMetadat.f90 ${DSHARE_DIR}/var_lookup.f90) -set(DATAMS_KINSOL - ${DSHARE_DIR}/csv_file.f90 - ${SUMMA_DSHARE_DIR}/kinsol_user_data_type.f90) +set(DATAMS_SUNDIALS + ${DSHARE_DIR}/tol4IDA.f90 + ${DSHARE_DIR}/type4IDA.f90 + ${DSHARE_DIR}/type4kinsol.f90) # Utility modules set(UTILMS @@ -279,7 +270,7 @@ set(UTILMS ${ENGINE_DIR}/soil_utils.f90 ${ENGINE_DIR}/time_utils.f90 ${ENGINE_DIR}/updatState.f90) -set(UTILMS_IDA +set(UTILMS_SUNDIALS ${ENGINE_DIR}/soil_utilsAddSundials.f90 ${ENGINE_DIR}/updatStateSundials.f90) @@ -320,22 +311,24 @@ set(MODRUN ${ENGINE_DIR}/read_force.f90 ${ENGINE_DIR}/snowAlbedo.f90 ${ENGINE_DIR}/snwCompact.f90 + ${ENGINE_DIR}/t2enthalpy.f90 ${ENGINE_DIR}/tempAdjust.f90 ${ENGINE_DIR}/updateVars.f90 ${ENGINE_DIR}/var_derive.f90 ${ENGINE_DIR}/volicePack.f90) -# Note, eventually these should be added to Kinsol -set(MODRUN_NOT_KINSOL - ${ENGINE_DIR}/t2enthalpy.f90) -set(MODRUN_IDA +set(MODRUN_SUNDIALS ${ENGINE_DIR}/updateVarsSundials.f90) # Solver main modules set(SOLVER ${ENGINE_DIR}/bigAquifer.f90 + ${ENGINE_DIR}/computEnthalpy.f90 ${ENGINE_DIR}/computFlux.f90 + ${ENGINE_DIR}/computHeatCap.f90 ${ENGINE_DIR}/computJacob.f90 ${ENGINE_DIR}/computResid.f90 + ${ENGINE_DIR}/computSnowDepth.f90 + ${ENGINE_DIR}/computThermConduct.f90 ${ENGINE_DIR}/coupled_em.f90 ${ENGINE_DIR}/diagn_evar.f90 ${ENGINE_DIR}/eval8summa.f90 @@ -352,21 +345,13 @@ set(SOLVER ${ENGINE_DIR}/vegNrgFlux.f90 ${ENGINE_DIR}/vegPhenlgy.f90 ${ENGINE_DIR}/vegSWavRad.f90) -# Note, eventually these should be added to Kinsol -set(SOLVER_NOT_KINSOL - ${ENGINE_DIR}/computSnowDepth.f90 - ${ENGINE_DIR}/computEnthalpy.f90 - ${ENGINE_DIR}/computHeatCap.f90 - ${ENGINE_DIR}/computThermConduct.f90) -set(SOLVER_IDA +set(SOLVER_SUNDIALS ${ENGINE_DIR}/computJacobSundials.f90 ${ENGINE_DIR}/computResidSundials.f90 ${ENGINE_DIR}/eval8summaSundials.f90 ${ENGINE_DIR}/summaSolveSundialsIDA.f90 - ${ENGINE_DIR}/systemSolvSundials.f90 - ${ENGINE_DIR}/tol4IDA.f90 - ${ENGINE_DIR}/type4IDA.f90) -set(SOLVER_NOT_ACTOR + ${ENGINE_DIR}/systemSolvSundials.f90) +set(SOLVER_NOT_ACTORS ${ENGINE_DIR}/run_oneGRU.f90 ${ENGINE_DIR}/run_oneHRU.f90) @@ -376,7 +361,7 @@ set(DRIVER ${SUB_DRIVER_DIR}/summa_util.f90 ${DRIVER_DIR}/summa_alarms.f90 ${SUB_DRIVER_DIR}/summa_globalData.f90) -set(DRIVER_NOT_ACTOR +set(DRIVER_NOT_ACTORS ${DRIVER_DIR}/summa_defineOutput.f90 ${DRIVER_DIR}/summa_init.f90 ${DRIVER_DIR}/summa_setup.f90 @@ -454,13 +439,10 @@ if(CMAKE_BUILD_TYPE MATCHES Actors) set(COMM_ALL ${COMM_ALL} ${INTERFACE}) endif() -if(CMAKE_BUILD_TYPE MATCHES Kinsol) +if(CMAKE_BUILD_TYPE MATCHES Sundials) set(COMM_ALL ${COMM_ALL} - ${DATAMS_KINSOL}) -endif() -if(CMAKE_BUILD_TYPE MATCHES IDA) - set(COMM_ALL ${COMM_ALL} - ${UTILMS_IDA}) + ${DATAMS_SUNDIALS} + ${UTILMS_SUNDIALS}) endif() set(SUMMA_ALL @@ -486,20 +468,15 @@ if(CMAKE_BUILD_TYPE MATCHES NexGen) set(SUMMA_ALL ${SUMMA_ALL} ${DRIVER_NEXGEN}) endif() -if(CMAKE_BUILD_TYPE MATCHES IDA) - set(SUMMA_ALL ${SUMMA_ALL} - ${MODRUN_IDA} - ${SOLVER_IDA}) -endif() -if(NOT CMAKE_BUILD_TYPE MATCHES Kinsol) +if(CMAKE_BUILD_TYPE MATCHES Sundials) set(SUMMA_ALL ${SUMMA_ALL} - ${MODRUN_NOT_KINSOL} - ${SOLVER_NOT_KINSOL}) + ${MODRUN_SUNDIALS} + ${SOLVER_SUNDIALS}) endif() # Run program files set(MAIN_ACTOR ${ACTORS_DIR}/main.cpp) -set(MAIN_BMI ${DRIVER_DIR}/summa_driverBMI.f90) # do not need a driver in NexGen so do not use +#set(MAIN_BMI ${DRIVER_DIR}/summa_driver4BMI.f90) # do not need a driver in NexGen so do not use set(MAIN_SUMMA ${DRIVER_DIR}/summa_driver.f90) # Define version number, not working correctly @@ -563,7 +540,7 @@ elseif(CMAKE_BUILD_TYPE MATCHES Actors) add_library(summaactors SHARED ${SUMMA_ALL}) target_compile_options(summaactors PRIVATE ${FLAGS_ALL}) - if(CMAKE_BUILD_TYPE MATCHES Kinsol OR CMAKE_BUILD_TYPE MATCHES IDA) + if(CMAKE_BUILD_TYPE MATCHES Sundials) target_include_directories(summaactors PUBLIC ${INCLUDES} ${INC_SUNDIALS}) target_link_libraries(summaactors PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_COMM) else() @@ -589,7 +566,7 @@ else() add_library(summa SHARED ${SUMMA_ALL}) target_compile_options(summa PRIVATE ${FLAGS_ALL}) - if(CMAKE_BUILD_TYPE MATCHES Kinsol OR CMAKE_BUILD_TYPE MATCHES IDA) + if(CMAKE_BUILD_TYPE MATCHES Sundials) target_include_directories(summa PUBLIC ${INCLUDES} ${INC_SUNDIALS}) target_link_libraries(summa PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_COMM) else() diff --git a/build/cmake/build.cluster.bash b/build/cmake/build.cluster.bash index 72183e40c..da86203da 100644 --- a/build/cmake/build.cluster.bash +++ b/build/cmake/build.cluster.bash @@ -14,6 +14,6 @@ module load udunits/2.2.28 # for Actors module load caf -cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=IDA_Cluster +cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials_Cluster cmake --build ../cmake_build --target all diff --git a/build/cmake/build.mac.bash b/build/cmake/build.mac.bash index 706f8f689..dc929bf1f 100755 --- a/build/cmake/build.mac.bash +++ b/build/cmake/build.mac.bash @@ -2,6 +2,6 @@ # build on Mac, from cmake directory run this as ./build.mac.bash -cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=IDA +cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials cmake --build ../cmake_build --target all diff --git a/build/source/driver/summa_driverBmi.f90 b/build/source/driver/summa_driverBmi.f90 index c2c819f44..9ce4748c2 100644 --- a/build/source/driver/summa_driverBmi.f90 +++ b/build/source/driver/summa_driverBmi.f90 @@ -18,7 +18,7 @@ ! You should have received a copy of the GNU General Public License ! along with this program. If not, see . -program summa_driverBmi +program summa_driver4BMI ! driver program for summa simulations ! ***************************************************************************** ! * use desired modules @@ -53,4 +53,4 @@ program summa_driverBmi end do ! (looping through time) istat = model%finalize() -end program summa_driverBmi +end program summa_driver4BMI diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 12064e32d..111e9bade 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -377,18 +377,18 @@ function get_ixparam(varName) case('soilIceCV' ); get_ixparam = iLookPARAM%soilIceCV ! CV of depth of soil ice, used to get frozen fraction (-) ! algorithmic control parameters case('minwind' ); get_ixparam = iLookPARAM%minwind ! minimum wind speed (m s-1) - case('minstep' ); get_ixparam = iLookPARAM%minstep ! minimum length of the time step bEuler, not currently used - case('maxstep' ); get_ixparam = iLookPARAM%maxstep ! maximum length of the time step bEuler - case('wimplicit' ); get_ixparam = iLookPARAM%wimplicit ! weight assigned to start-of-step fluxes bEuler, not currently used - case('maxiter' ); get_ixparam = iLookPARAM%maxiter ! maximum number of iterations bEuler or nonlinear iterations Sundials - case('relConvTol_liquid' ); get_ixparam = iLookPARAM%relConvTol_liquid ! relative convergence tolerance for vol frac liq water (-) bEuler - case('absConvTol_liquid' ); get_ixparam = iLookPARAM%absConvTol_liquid ! absolute convergence tolerance for vol frac liq water (-) bEuler - case('relConvTol_matric' ); get_ixparam = iLookPARAM%relConvTol_matric ! relative convergence tolerance for matric head (-) bEuler - case('absConvTol_matric' ); get_ixparam = iLookPARAM%absConvTol_matric ! absolute convergence tolerance for matric head (m) bEuler - case('relConvTol_energy' ); get_ixparam = iLookPARAM%relConvTol_energy ! relative convergence tolerance for energy (-) bEuler - case('absConvTol_energy' ); get_ixparam = iLookPARAM%absConvTol_energy ! absolute convergence tolerance for energy (J m-3) bEuler - case('relConvTol_aquifr' ); get_ixparam = iLookPARAM%relConvTol_aquifr ! relative convergence tolerance for aquifer storage (-) bEuler - case('absConvTol_aquifr' ); get_ixparam = iLookPARAM%absConvTol_aquifr ! absolute convergence tolerance for aquifer storage (m) bEuler + case('minstep' ); get_ixparam = iLookPARAM%minstep ! minimum length of the time step be_numrec, not currently used + case('maxstep' ); get_ixparam = iLookPARAM%maxstep ! maximum length of the time step be_numrec + case('wimplicit' ); get_ixparam = iLookPARAM%wimplicit ! weight assigned to start-of-step fluxes be_numrec, not currently used + case('maxiter' ); get_ixparam = iLookPARAM%maxiter ! maximum number of iterations be_numrec or nonlinear iterations Sundials + case('relConvTol_liquid' ); get_ixparam = iLookPARAM%relConvTol_liquid ! relative convergence tolerance for vol frac liq water (-) be_numrec + case('absConvTol_liquid' ); get_ixparam = iLookPARAM%absConvTol_liquid ! absolute convergence tolerance for vol frac liq water (-) be_numrec + case('relConvTol_matric' ); get_ixparam = iLookPARAM%relConvTol_matric ! relative convergence tolerance for matric head (-) be_numrec + case('absConvTol_matric' ); get_ixparam = iLookPARAM%absConvTol_matric ! absolute convergence tolerance for matric head (m) be_numrec + case('relConvTol_energy' ); get_ixparam = iLookPARAM%relConvTol_energy ! relative convergence tolerance for energy (-) be_numrec + case('absConvTol_energy' ); get_ixparam = iLookPARAM%absConvTol_energy ! absolute convergence tolerance for energy (J m-3) be_numrec + case('relConvTol_aquifr' ); get_ixparam = iLookPARAM%relConvTol_aquifr ! relative convergence tolerance for aquifer storage (-) be_numrec + case('absConvTol_aquifr' ); get_ixparam = iLookPARAM%absConvTol_aquifr ! absolute convergence tolerance for aquifer storage (m) be_numrec case('zmin' ); get_ixparam = iLookPARAM%zmin ! minimum layer depth (m) case('zmax' ); get_ixparam = iLookPARAM%zmax ! maximum layer depth (m) case('zminLayer1' ); get_ixparam = iLookPARAM%zminLayer1 ! minimum layer depth for the 1st (top) layer (m) diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index e1b142ad3..5648c597f 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -259,18 +259,18 @@ subroutine popMetadat(err,message) mpar_meta(iLookPARAM%soilIceCV) = var_info('soilIceCV' , 'CV of depth of soil ice, used to get frozen fraction' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! algorithmic control parameters mpar_meta(iLookPARAM%minwind) = var_info('minwind' , 'minimum wind speed' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%minstep) = var_info('minstep' , 'minimum length of the time step bEuler, not currently used' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%maxstep) = var_info('maxstep' , 'maximum length of the time step bEuler' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%wimplicit) = var_info('wimplicit' , 'weight assigned to the start-of-step fluxes ,bEuler, not currently used', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%maxiter) = var_info('maxiter' , 'maximum number of iterations bEuler or nonlinear iterations Sundials', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_liquid) = var_info('relConvTol_liquid' , 'relative convergence tolerance for vol frac liq water bEuler' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_liquid) = var_info('absConvTol_liquid' , 'absolute convergence tolerance for vol frac liq water bEuler' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_matric) = var_info('relConvTol_matric' , 'relative convergence tolerance for matric head bEuler' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_matric) = var_info('absConvTol_matric' , 'absolute convergence tolerance for matric head bEuler' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_energy) = var_info('relConvTol_energy' , 'relative convergence tolerance for energy bEuler' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_energy) = var_info('absConvTol_energy' , 'absolute convergence tolerance for energy bEuler' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_aquifr) = var_info('relConvTol_aquifr' , 'relative convergence tolerance for aquifer storage bEuler' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_aquifr) = var_info('absConvTol_aquifr' , 'absolute convergence tolerance for aquifer storage bEuler' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%minstep) = var_info('minstep' , 'minimum length of the time step be_numrec, not currently used' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%maxstep) = var_info('maxstep' , 'maximum length of the time step be_numrec' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%wimplicit) = var_info('wimplicit' , 'weight assigned to the start-of-step fluxes ,be_numrec, not currently used', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%maxiter) = var_info('maxiter' , 'maximum number of iterations be_numrec or nonlinear iterations Sundials', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_liquid) = var_info('relConvTol_liquid' , 'relative convergence tolerance for vol frac liq water be_numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_liquid) = var_info('absConvTol_liquid' , 'absolute convergence tolerance for vol frac liq water be_numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_matric) = var_info('relConvTol_matric' , 'relative convergence tolerance for matric head be_numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_matric) = var_info('absConvTol_matric' , 'absolute convergence tolerance for matric head be_numrec' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_energy) = var_info('relConvTol_energy' , 'relative convergence tolerance for energy be_numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_energy) = var_info('absConvTol_energy' , 'absolute convergence tolerance for energy be_numrec' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_aquifr) = var_info('relConvTol_aquifr' , 'relative convergence tolerance for aquifer storage be_numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_aquifr) = var_info('absConvTol_aquifr' , 'absolute convergence tolerance for aquifer storage be_numrec' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%zmin) = var_info('zmin' , 'minimum layer depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%zmax) = var_info('zmax' , 'maximum layer depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%zminLayer1) = var_info('zminLayer1' , 'minimum layer depth for the 1st (top) layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/engine/tol4IDA.f90 b/build/source/dshare/tol4IDA.f90 similarity index 97% rename from build/source/engine/tol4IDA.f90 rename to build/source/dshare/tol4IDA.f90 index c33481089..7e0c25763 100644 --- a/build/source/engine/tol4IDA.f90 +++ b/build/source/dshare/tol4IDA.f90 @@ -155,20 +155,20 @@ subroutine popTol4IDA(& integer(i4b) :: iLayer ! index of layer within the snow+soil domain integer(i4b) :: ixStateSubset ! index within the state subset logical(lgt),dimension(nState) :: tolFlag ! flag to denote that the state is populated - real(rkind) :: absTolTempCas = 1e-6 ! could use absConvTol_energy 1e-6 = 1e-0 bEuler - real(rkind) :: relTolTempCas = 1e-6 ! could use relConvTol_energy 1e-6 = 1e-2 bEuler + real(rkind) :: absTolTempCas = 1e-6 ! could use absConvTol_energy 1e-6 = 1e-0 be_numrec + real(rkind) :: relTolTempCas = 1e-6 ! could use relConvTol_energy 1e-6 = 1e-2 be_numrec real(rkind) :: absTolTempVeg = 1e-6 ! could use absConvTol_energy real(rkind) :: relTolTempVeg = 1e-6 ! could use relConvTol_energy - real(rkind) :: absTolWatVeg = 1e-6 ! could use absConvTol_liquid 1e-6 = 1e-5 bEuler - real(rkind) :: relTolWatVeg = 1e-6 ! could use relConvTol_liquid 1e-6 = 1e-3 bEuler + real(rkind) :: absTolWatVeg = 1e-6 ! could use absConvTol_liquid 1e-6 = 1e-5 be_numrec + real(rkind) :: relTolWatVeg = 1e-6 ! could use relConvTol_liquid 1e-6 = 1e-3 be_numrec real(rkind) :: absTolTempSoilSnow = 1e-6 ! could use absConvTol_energy real(rkind) :: relTolTempSoilSnow = 1e-6 ! could use relConvTol_energy real(rkind) :: absTolWatSnow = 1e-6 ! could use absConvTol_liquid real(rkind) :: relTolWatSnow = 1e-6 ! could use relConvTol_liquid - real(rkind) :: absTolMatric = 1e-6 ! could use absConvTol_matric 1e-6 = 1e-6 bEuler - real(rkind) :: relTolMatric = 1e-6 ! could use relConvTol_matric 1e-6 = 1e-6 bEuler - real(rkind) :: absTolAquifr = 1e-6 ! could use absConvTol_aquifr 1e-6 = 1e-5 bEuler - real(rkind) :: relTolAquifr = 1e-6 ! could use relConvTol_aquifr 1e-6 = 1e-0 bEuler + real(rkind) :: absTolMatric = 1e-6 ! could use absConvTol_matric 1e-6 = 1e-6 be_numrec + real(rkind) :: relTolMatric = 1e-6 ! could use relConvTol_matric 1e-6 = 1e-6 be_numrec + real(rkind) :: absTolAquifr = 1e-6 ! could use absConvTol_aquifr 1e-6 = 1e-5 be_numrec + real(rkind) :: relTolAquifr = 1e-6 ! could use relConvTol_aquifr 1e-6 = 1e-0 be_numrec ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- @@ -239,7 +239,7 @@ subroutine popTol4IDA(& end select end do - ! tolerance for tempreture of the snow and soil domain + ! tolerance for temperature of the snow and soil domain if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) ixStateSubset = ixSnowSoilNrg(iLayer) ! index within the state vector diff --git a/build/source/engine/type4IDA.f90 b/build/source/dshare/type4IDA.f90 similarity index 100% rename from build/source/engine/type4IDA.f90 rename to build/source/dshare/type4IDA.f90 diff --git a/build/source/dshare/type4kinsol.f90 b/build/source/dshare/type4kinsol.f90 new file mode 100644 index 000000000..448700734 --- /dev/null +++ b/build/source/dshare/type4kinsol.f90 @@ -0,0 +1,52 @@ +module type4kinsol + ! data types + USE nrtype + USE, intrinsic :: iso_c_binding + + USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (dp) + model_options ! defines the model decisions + implicit none + +type kinsol_data + real(rkind) :: dt + integer(i4b) :: nSnow + integer(i4b) :: nSoil + integer(i4b) :: nLayers + integer(i4b) :: nState + logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt) :: computeBaseflow + integer(i4b) :: ixMatrix + logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution + type(var_i) :: type_data ! type of vegetation and soil + type(var_d) :: attr_data ! spatial attributes + type(var_dlength) :: mpar_data ! model parameters + type(var_d) :: forc_data ! model forcing data + type(var_dlength) :: bvar_data ! model variables for the local basin + type(var_dlength) :: flux_data ! model fluxes for a local HRU + integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + type(var_ilength) :: indx_data + type(var_dlength) :: prog_data + type(var_dlength) :: diag_data + type(var_dlength) :: deriv_data + logical(lgt) :: feasible ! flag to denote the feasibility of the solution + real(rkind) :: fEval ! function evaluation + real(rkind),allocatable :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + real(rkind),allocatable :: fScale(:) ! function scaling vector + real(rkind),allocatable :: fluxVec(:) ! flux vector + real(rkind),allocatable :: resSink(:) ! sink terms on the RHS of the flux equation + type(model_options),allocatable :: model_decisions(:) ! model decisions + real(rkind), allocatable :: dBaseflow_dMatric(:,:) + real(rkind), allocatable :: dMat(:) + real(rkind), allocatable :: stateVecPrev(:) ! state vector from the previous iteration to help with infeasibility + logical(lgt) :: firstStateiteration ! flag to denote if we computed an iteration so we know to save the state +end type kinsol_data + + +end module type4kinsol \ No newline at end of file diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 52c07e1b1..63168b426 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -82,8 +82,9 @@ module coupled_em_module ! look-up values for the numerical method USE mDecisions_module,only: & - sundials ,& ! SUNDIALS/IDA solution - bEuler ! home-grown backward Euler solution with long time step + be_numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes + be_kinsol ,& ! SUNDIALS backward Euler solution using Kinsol + sundials ! SUNDIALS solution using IDA ! privacy implicit none @@ -1192,9 +1193,9 @@ subroutine coupled_em(& ! identify the need to check the mass balance, both methods should work if tolerance coarse enough select case(ixNumericalMethod) - case(sundials); checkMassBalance = .true. ! sundials gives instantaneous fluxes and were summed for an average flux for checks - case(bEuler); checkMassBalance = .true. ! bEuler gives finite difference dt_sub fluxes and were summed for an average flux for checks - case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return + case(sundials); checkMassBalance = .true. ! sundials gives instantaneous fluxes and were summed for an average flux for checks + case(be_numrec); checkMassBalance = .true. ! be_numrec gives finite difference dt_sub fluxes and were summed for an average flux for checks + case default; err=20; message=trim(message)//'expect num_method to be sundials, be_kinsol, or be_numrec (or itertive, which is be_numrec)'; return end select ! ----- diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index 233b80205..11f678222 100755 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -60,8 +60,9 @@ module mDecisions_module integer(i4b),parameter,public :: constantScaling = 71 ! constant scaling factor integer(i4b),parameter,public :: laiScaling = 72 ! exponential function of LAI (Leuning, Plant Cell Env 1995: "Scaling from..." [eq 9]) ! look-up values for the choice of numerical method -integer(i4b),parameter,public :: bEuler = 81 ! home-grown backward Euler solution with long time steps -integer(i4b),parameter,public :: sundials = 82 ! SUNDIALS/IDA solution +integer(i4b),parameter,public :: be_numrec = 81 ! home-grown backward Euler solution using free versions of Numerical recipes +integer(i4b),parameter,public :: be_kinsol = 82 ! SUNDIALS backward Euler solution using Kinsol +integer(i4b),parameter,public :: sundials = 83 ! SUNDIALS solution using IDA ! look-up values for method used to compute derivative integer(i4b),parameter,public :: numerical = 91 ! numerical solution integer(i4b),parameter,public :: analytical = 92 ! analytical solution @@ -410,9 +411,10 @@ subroutine mDecisions(err,message) ! identify the numerical method select case(trim(model_decisions(iLookDECISIONS%num_method)%cDecision)) - case('bEuler' ); model_decisions(iLookDECISIONS%num_method)%iDecision = bEuler ! home-grown backward Euler solution with long time steps - case('itertive' ); model_decisions(iLookDECISIONS%num_method)%iDecision = bEuler ! home-grown backward Euler solution (included for backwards compatibility) - case('sundials' ); model_decisions(iLookDECISIONS%num_method)%iDecision = sundials ! SUNDIALS/IDA solution + case('be_numrec'); model_decisions(iLookDECISIONS%num_method)%iDecision = be_numrec ! home-grown backward Euler solution using free versions of Numerical recipes + case('itertive' ); model_decisions(iLookDECISIONS%num_method)%iDecision = be_numrec ! home-grown backward Euler solution (included for backwards compatibility) + case('be_kinsol'); model_decisions(iLookDECISIONS%num_method)%iDecision = be_kinsol ! SUNDIALS backward Euler solution using Kinsol + case('sundials' ); model_decisions(iLookDECISIONS%num_method)%iDecision = sundials ! SUNDIALS solution using IDA case default err=10; message=trim(message)//"unknown numerical method [option="//trim(model_decisions(iLookDECISIONS%num_method)%cDecision)//"]"; return end select diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index aa1f70765..63cc20df6 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -98,9 +98,10 @@ module opSplittin_module ! look-up values for the numerical method -USE mDecisions_module,only: & - sundials, & ! SUNDIALS/IDA solution - bEuler ! home-grown backward Euler solution with long time step +USE mDecisions_module,only: & + be_numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes + be_kinsol ,& ! SUNDIALS backward Euler solution using Kinsol + sundials ! SUNDIALS solution using IDA ! safety: set private unless specified otherwise implicit none @@ -318,8 +319,8 @@ subroutine opSplittin(& ! we just solve the fully coupled problem by with Sundials for now select case(ixNumericalMethod) case(sundials); nCoupling = 1 - case(bEuler); nCoupling = 2 - case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return + case(be_numrec); nCoupling = 2 + case default; err=20; message=trim(message)//'expect num_method to be sundials, be_kinsol, or be_numrec (or itertive, which is be_numrec)'; return end select ! ----- diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundialsIDA.f90 index fe357d851..ac4b66474 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundialsIDA.f90 @@ -145,7 +145,7 @@ subroutine summaSolveSundialsIDA( & USE eval8summaSundials_module,only:eval8summa4IDA ! DAE/ODE functions USE eval8summaSundials_module,only:eval8summaSundials ! residual of DAE USE computJacobSundials_module,only:computJacob4IDA ! system Jacobian - USE tol4IDA_module,only:computWeight4IDA ! weigth required for tolerances + USE tol4IDA_module,only:computWeight4IDA ! weight required for tolerances USE var_derive_module,only:calcHeight ! height at layer interfaces and layer mid-point !======= Declarations ========= diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 88c55d876..5ba481dca 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -71,9 +71,10 @@ module varSubstep_module iden_water ! intrinsic density of liquid water (kg m-3) ! look-up values for the numerical method -USE mDecisions_module,only: & - sundials, & ! SUNDIALS/IDA solution - bEuler ! home-grown backward Euler solution with long time step +USE mDecisions_module,only: & + be_numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes + be_kinsol ,& ! SUNDIALS backward Euler solution using Kinsol + sundials ! SUNDIALS solution using IDA ! safety: set private unless specified otherwise implicit none @@ -132,7 +133,7 @@ subroutine varSubstep(& ! simulation of fluxes and residuals given a trial state vector USE getVectorz_module,only:popStateVec ! populate the state vector USE getVectorz_module,only:varExtract ! extract variables from the state vector -#ifdef IDA_ACTIVE +#ifdef SUNDIALS_ACTIVE USE systemSolvSundials_module,only:systemSolvSundials ! solve the system of equations for one time step #endif USE systemSolv_module,only:systemSolv ! solve the system of equations for one time step @@ -266,7 +267,7 @@ subroutine varSubstep(& ! initialize the length of the substep dtSubstep = dtInit - ! change maxstep with hard code here to make only the newton step loop in systemSolv* happen more frequently for num_method = bEuler or sundials + ! change maxstep with hard code here to make only the newton step loop in systemSolv* happen more frequently ! NOTE: this may just be amplifying the splitting error if maxstep is smaller than the full possible step maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) @@ -326,7 +327,7 @@ subroutine varSubstep(& ! solve the system of equations for a given state subset select case(ixNumericalMethod) case(sundials) -#ifdef IDA_ACTIVE +#ifdef SUNDIALS_ACTIVE call systemSolvSundials(& ! input: model control dtSubstep, & ! intent(in): time step (s) @@ -363,7 +364,7 @@ subroutine varSubstep(& #else err=20; message=trim(message)//'cannot use num_method as sundials if did not compile with -DCMAKE_BUILD_TYPE=IDA'; return #endif - case(bEuler) + case(be_numrec) call systemSolv(& ! input: model control dtSubstep, & ! intent(in): time step (s) @@ -397,7 +398,7 @@ subroutine varSubstep(& niter, & ! intent(out): number of iterations taken err,cmessage) ! intent(out): error code and error message stateVecPrime = stateVecTrial ! will not use, dummy - case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return + case default; err=20; message=trim(message)//'expect num_method to be sundials, be_kinsol, or be_numrec (or itertive, which is be_numrec)'; return end select if(err/=0)then @@ -461,9 +462,9 @@ subroutine varSubstep(& ! identify the need to check the mass balance select case(ixNumericalMethod) - case(sundials); checkMassBalance = .false. ! only for bEuler because sundials has instantaneous fluxes only - case(bEuler); checkMassBalance = .true. ! (.not.scalarSolution) - case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return + case(sundials); checkMassBalance = .false. ! only for be_numrec because sundials has instantaneous fluxes only + case(be_numrec); checkMassBalance = .true. ! (.not.scalarSolution) + case default; err=20; message=trim(message)//'expect num_method to be sundials, be_kinsol, or be_numrec (or itertive, which is be_numrec)'; return end select ! identify the need to check the energy balance, DOES NOT WORK YET and only check if ixHowHeatCap == enthalpyFD checkNrgBalance = .false. @@ -618,7 +619,9 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe model_decisions,lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures waterBalanceError,nrgFluxModified,err,message) ! output: flags and error control USE getVectorz_module,only:varExtract ! extract variables from the state vector +#ifdef SUNDIALS_ACTIVE USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables +#endif USE updateVars_module,only:updateVars ! update prognostic variables USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy implicit none @@ -835,6 +838,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe select case(ixNumericalMethod) case(sundials) +#ifdef SUNDIALS_ACTIVE call varExtract(& ! input stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) @@ -892,7 +896,10 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) ! output: error control err,cmessage) ! intent(out): error control - case(bEuler) +#else + err=20; message=trim(message)//'cannot use num_method as sundials if did not compile with -DCMAKE_BUILD_TYPE=IDA'; return +#endif + case(be_numrec) ! update diagnostic variables call updateVars(& ! input @@ -917,7 +924,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) ! output: error control err,cmessage) ! intent(out): error control - case default; err=20; message=trim(message)//'expect num_method to be sundials or bEuler (or itertive, which is bEuler)'; return + case default; err=20; message=trim(message)//'expect num_method to be sundials, be_kinsol, or be_numrec (or itertive, which is be_numrec)'; return end select if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) diff --git a/docs/sundials_bmi/flags_params_sundials.txt b/docs/sundials_bmi/flags_params_sundials.txt index 65c79b396..28515657a 100644 --- a/docs/sundials_bmi/flags_params_sundials.txt +++ b/docs/sundials_bmi/flags_params_sundials.txt @@ -1,7 +1,7 @@ -To switch between SUMMA-BE and SUMMA-SUNDIALS, the num_method in the model_decision file can be either one of the values "bEuler" (choice "itertive" is backward compatible) or "sundials". +To switch between SUMMA-BE and SUMMA-SUNDIALS, the num_method in the model_decision file can be either one of the values "be_numrec" (choice "itertive" is backward compatible), "be_kinsol", or "sundials". -In energy equation, the heat capacity is computed using either the analytical (closed) formula or the finite difference formula (dH_T/dT). The "howHeatCap" variable has been added to the var_lookup module to handle such decision. A user should add this variable to the model_decision file with one of the values "closedForm" or "enthalpyFD". Choice of num_method as "itertive" will set num_method=bEuler and howHeatCap=closedForm. +In energy equation, the heat capacity is computed using either the analytical (closed) formula or the finite difference formula (dH_T/dT). The "howHeatCap" variable has been added to the var_lookup module to handle such decision. A user should add this variable to the model_decision file with one of the values "closedForm" or "enthalpyFD". Choice of num_method as "itertive" will set num_method=be_numrec and howHeatCap=closedForm. All SUMMA-SUNDIALS files are in build/source/engine. The important ones are as following: diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zDecisions.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zDecisions.txt index 0cb7c13a2..dfadcd9d1 100644 --- a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zDecisions.txt +++ b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zDecisions.txt @@ -13,7 +13,7 @@ vegeParTbl USGS ! (04) vegetation category datas soilStress NoahType ! (05) choice of function for the soil moisture control on stomatal resistance stomResist BallBerry ! (06) choice of function for stomatal resistance ! *********************************************************************************************************************** -num_method bEuler ! (07) choice of numerical method +num_method be_numrec ! (07) choice of numerical method fDerivMeth analytic ! (08) method used to calculate flux derivatives LAI_method monTable ! (09) method used to determine LAI and SAI f_Richards mixdform ! (10) form of Richard's equation @@ -63,8 +63,9 @@ howHeatCap closedForm ! (30) ! Jarvis ! Jarvis ! ----------------------------------------------------------------------------------------------- ! (07) choice of numerical method -! bEuler ! home-grown backward Euler -! sundials ! SUNDIALS solver +! be_numrec ! home-grown backward Euler +! be_kinsol ! SUNDIALS backward Euler solution using Kinsol +! sundials ! SUNDIALS solution using IDA ! ----------------------------------------------------------------------------------------------- ! (08) method used to calculate flux derivatives ! numericl ! numerical derivatives From 75dfd1e20ceeccae681d1b0526a3f9c030dcf624 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 10 May 2023 16:46:05 +0900 Subject: [PATCH 0713/1472] had to move more stuff around and rename --- build/cmake/CMakeLists.txt | 12 +-- ...mma_driverBmi.f90 => summa_driver4bmi.f90} | 4 +- build/source/dshare/type4IDA.f90 | 4 +- build/source/engine/computJacobSundials.f90 | 12 +-- build/source/engine/eval8summaSundials.f90 | 12 +-- ...ialsIDA.f90 => summaSolveSundials4ida.f90} | 78 +++++++++---------- build/source/engine/systemSolvSundials.f90 | 8 +- .../tol4IDA.f90 => engine/tol4ida.f90} | 28 +++---- docs/sundials_bmi/flags_params_sundials.txt | 6 +- 9 files changed, 82 insertions(+), 82 deletions(-) rename build/source/driver/{summa_driverBmi.f90 => summa_driver4bmi.f90} (97%) rename build/source/engine/{summaSolveSundialsIDA.f90 => summaSolveSundials4ida.f90} (95%) rename build/source/{dshare/tol4IDA.f90 => engine/tol4ida.f90} (97%) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 1ed6adfa3..116bd5cea 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -258,8 +258,7 @@ set(DATAMS ${DSHARE_DIR}/popMetadat.f90 ${DSHARE_DIR}/var_lookup.f90) set(DATAMS_SUNDIALS - ${DSHARE_DIR}/tol4IDA.f90 - ${DSHARE_DIR}/type4IDA.f90 + ${DSHARE_DIR}/type4ida.f90 ${DSHARE_DIR}/type4kinsol.f90) # Utility modules @@ -317,6 +316,7 @@ set(MODRUN ${ENGINE_DIR}/var_derive.f90 ${ENGINE_DIR}/volicePack.f90) set(MODRUN_SUNDIALS + ${ENGINE_DIR}/tol4ida.f90 ${ENGINE_DIR}/updateVarsSundials.f90) # Solver main modules @@ -349,7 +349,7 @@ set(SOLVER_SUNDIALS ${ENGINE_DIR}/computJacobSundials.f90 ${ENGINE_DIR}/computResidSundials.f90 ${ENGINE_DIR}/eval8summaSundials.f90 - ${ENGINE_DIR}/summaSolveSundialsIDA.f90 + ${ENGINE_DIR}/summaSolveSundials4ida.f90 ${ENGINE_DIR}/systemSolvSundials.f90) set(SOLVER_NOT_ACTORS ${ENGINE_DIR}/run_oneGRU.f90 @@ -461,8 +461,8 @@ if(CMAKE_BUILD_TYPE MATCHES Actors) ${GRU_INTERFACE}) else() set(SUMMA_ALL ${SUMMA_ALL} - ${SOLVER_NOT_ACTOR} - ${DRIVER_NOT_ACTOR}) + ${SOLVER_NOT_ACTORS} + ${DRIVER_NOT_ACTORS}) endif() if(CMAKE_BUILD_TYPE MATCHES NexGen) set(SUMMA_ALL ${SUMMA_ALL} @@ -476,7 +476,7 @@ endif() # Run program files set(MAIN_ACTOR ${ACTORS_DIR}/main.cpp) -#set(MAIN_BMI ${DRIVER_DIR}/summa_driver4BMI.f90) # do not need a driver in NexGen so do not use +#set(MAIN_BMI ${DRIVER_DIR}/summa_driver4bmi.f90) # do not need a driver in NexGen so do not use set(MAIN_SUMMA ${DRIVER_DIR}/summa_driver.f90) # Define version number, not working correctly diff --git a/build/source/driver/summa_driverBmi.f90 b/build/source/driver/summa_driver4bmi.f90 similarity index 97% rename from build/source/driver/summa_driverBmi.f90 rename to build/source/driver/summa_driver4bmi.f90 index 9ce4748c2..9154f2154 100644 --- a/build/source/driver/summa_driverBmi.f90 +++ b/build/source/driver/summa_driver4bmi.f90 @@ -18,7 +18,7 @@ ! You should have received a copy of the GNU General Public License ! along with this program. If not, see . -program summa_driver4BMI +program summa_driver4bmi ! driver program for summa simulations ! ***************************************************************************** ! * use desired modules @@ -53,4 +53,4 @@ program summa_driver4BMI end do ! (looping through time) istat = model%finalize() -end program summa_driver4BMI +end program summa_driver4bmi diff --git a/build/source/dshare/type4IDA.f90 b/build/source/dshare/type4IDA.f90 index 14cf5e10d..5d60c11e7 100644 --- a/build/source/dshare/type4IDA.f90 +++ b/build/source/dshare/type4IDA.f90 @@ -1,4 +1,4 @@ -module type4IDA +module type4ida ! data types USE nrtype @@ -75,7 +75,7 @@ module type4IDA end type eqnsData -end module type4IDA +end module type4ida diff --git a/build/source/engine/computJacobSundials.f90 b/build/source/engine/computJacobSundials.f90 index 2c31cab01..9ecd367cd 100644 --- a/build/source/engine/computJacobSundials.f90 +++ b/build/source/engine/computJacobSundials.f90 @@ -107,7 +107,7 @@ module computJacobSundials_module private public::computJacobSundials public::computJacobSetup -public::computJacob4IDA +public::computJacob4ida contains @@ -1367,16 +1367,16 @@ end subroutine computJacobSetup ! ********************************************************************************************************** -! public function computJacob4IDA: the interface to compute the Jacobian matrix dF/dy + c dF/dy' for IDA solver +! public function computJacob4ida: the interface to compute the Jacobian matrix dF/dy + c dF/dy' for IDA solver ! ********************************************************************************************************** ! Return values: ! 0 = success, ! 1 = recoverable error, ! -1 = non-recoverable error ! ---------------------------------------------------------------- -integer(c_int) function computJacob4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & +integer(c_int) function computJacob4ida(t, cj, sunvec_y, sunvec_yp, sunvec_r, & sunmat_J, user_data, sunvec_temp1, sunvec_temp2, sunvec_temp3) & - result(ierr) bind(C,name='computJacob4IDA') + result(ierr) bind(C,name='computJacob4ida') !======= Inclusions =========== use, intrinsic :: iso_c_binding @@ -1385,7 +1385,7 @@ integer(c_int) function computJacob4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & use fnvector_serial_mod use fsunmatrix_band_mod use fsunmatrix_dense_mod - use type4IDA + use type4ida !======= Declarations ========= implicit none @@ -1458,7 +1458,7 @@ integer(c_int) function computJacob4IDA(t, cj, sunvec_y, sunvec_yp, sunvec_r, & ierr = 0 return -end function computJacob4IDA +end function computJacob4ida ! ********************************************************************************************************** diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index 8f8038e74..2479cde76 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -69,7 +69,7 @@ module eval8summaSundials_module implicit none private public::eval8summaSundials -public::eval8summa4IDA +public::eval8summa4ida contains @@ -799,22 +799,22 @@ end subroutine eval8summaSundials ! ********************************************************************************************************** -! public function eval8summa4IDA: compute the residual vector F(t,y,y') required for IDA solver +! public function eval8summa4ida: compute the residual vector F(t,y,y') required for IDA solver ! ********************************************************************************************************** ! Return values: ! 0 = success, ! 1 = recoverable error, ! -1 = non-recoverable error ! ---------------------------------------------------------------- -integer(c_int) function eval8summa4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user_data) & - result(ierr) bind(C,name='eval8summa4IDA') +integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user_data) & + result(ierr) bind(C,name='eval8summa4ida') !======= Inclusions =========== use, intrinsic :: iso_c_binding use fida_mod use fsundials_nvector_mod use fnvector_serial_mod - use type4IDA + use type4ida !======= Declarations ========= implicit none @@ -926,7 +926,7 @@ integer(c_int) function eval8summa4IDA(tres, sunvec_y, sunvec_yp, sunvec_r, user ierr = 0 return -end function eval8summa4IDA +end function eval8summa4ida end module eval8summaSundials_module diff --git a/build/source/engine/summaSolveSundialsIDA.f90 b/build/source/engine/summaSolveSundials4ida.f90 similarity index 95% rename from build/source/engine/summaSolveSundialsIDA.f90 rename to build/source/engine/summaSolveSundials4ida.f90 index ac4b66474..18e997960 100644 --- a/build/source/engine/summaSolveSundialsIDA.f90 +++ b/build/source/engine/summaSolveSundials4ida.f90 @@ -1,13 +1,13 @@ -module summaSolveSundialsIDA_module +module summaSolveSundials4ida_module !======= Inclusions =========== USE, intrinsic :: iso_c_binding USE nrtype -USE type4IDA +USE type4ida ! access the global print flag USE globalData,only:globalPrintFlag @@ -79,15 +79,15 @@ module summaSolveSundialsIDA_module private::setInitialCondition private::setSolverParams private::find_rootdir - public::layerDisCont4IDA - public::summaSolveSundialsIDA + public::layerDisCont4ida + public::summaSolveSundials4ida contains !------------------- -! * public subroutine summaSolveSundialsIDA: solve F(y,y') = 0 by IDA (y is the state vector) +! * public subroutine summaSolveSundials4ida: solve F(y,y') = 0 by IDA (y is the state vector) ! ------------------ -subroutine summaSolveSundialsIDA( & +subroutine summaSolveSundials4ida( & dt, & ! intent(in): data time step atol, & ! intent(in): absolute telerance rtol, & ! intent(in): relative tolerance @@ -142,10 +142,10 @@ subroutine summaSolveSundialsIDA( & USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver USE allocspace_module,only:allocLocal ! allocate local data structures - USE eval8summaSundials_module,only:eval8summa4IDA ! DAE/ODE functions + USE eval8summaSundials_module,only:eval8summa4ida ! DAE/ODE functions USE eval8summaSundials_module,only:eval8summaSundials ! residual of DAE - USE computJacobSundials_module,only:computJacob4IDA ! system Jacobian - USE tol4IDA_module,only:computWeight4IDA ! weight required for tolerances + USE computJacobSundials_module,only:computJacob4ida ! system Jacobian + USE tol4ida_module,only:computWeight4ida ! weight required for tolerances USE var_derive_module,only:calcHeight ! height at layer interfaces and layer mid-point !======= Declarations ========= @@ -233,7 +233,7 @@ subroutine summaSolveSundialsIDA( & ! ----------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="summaSolveSundialsIDA/" + err=0; message="summaSolveSundials4ida/" nState = nStat idaSucceeds = .true. @@ -320,31 +320,31 @@ subroutine summaSolveSundialsIDA( & ! create serial vectors sunvec_y => FN_VMake_Serial(nState, stateVec, sunctx) - if (.not. associated(sunvec_y)) then; err=20; message='summaSolveSundialsIDA: sunvec = NULL'; return; endif + if (.not. associated(sunvec_y)) then; err=20; message='summaSolveSundials4ida: sunvec = NULL'; return; endif sunvec_yp => FN_VMake_Serial(nState, stateVecPrime, sunctx) - if (.not. associated(sunvec_yp)) then; err=20; message='summaSolveSundialsIDA: sunvec = NULL'; return; endif + if (.not. associated(sunvec_yp)) then; err=20; message='summaSolveSundials4ida: sunvec = NULL'; return; endif ! Initialize solution vectors call setInitialCondition(nState, stateVecInit, sunvec_y, sunvec_yp) ! Create memory ida_mem = FIDACreate(sunctx) - if (.not. c_associated(ida_mem)) then; err=20; message='summaSolveSundialsIDA: ida_mem = NULL'; return; endif + if (.not. c_associated(ida_mem)) then; err=20; message='summaSolveSundials4ida: ida_mem = NULL'; return; endif ! Attach user data to memory eqns_data%ida_mem = ida_mem retval = FIDASetUserData(ida_mem, c_loc(eqns_data)) - if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetUserData'; return; endif + if (retval /= 0) then; err=20; message='summaSolveSundials4ida: error in FIDASetUserData'; return; endif ! Initialize memory t0 = 0._rkind - retval = FIDAInit(ida_mem, c_funloc(eval8summa4IDA), t0, sunvec_y, sunvec_yp) - if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDAInit'; return; endif + retval = FIDAInit(ida_mem, c_funloc(eval8summa4ida), t0, sunvec_y, sunvec_yp) + if (retval /= 0) then; err=20; message='summaSolveSundials4ida: error in FIDAInit'; return; endif ! set tolerances - retval = FIDAWFtolerances(ida_mem, c_funloc(computWeight4IDA)) - if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDAWFtolerances'; return; endif + retval = FIDAWFtolerances(ida_mem, c_funloc(computWeight4ida)) + if (retval /= 0) then; err=20; message='summaSolveSundials4ida: error in FIDAWFtolerances'; return; endif ! initialize rootfinding problem and allocate space, counting roots if(detect_events)then @@ -364,7 +364,7 @@ subroutine summaSolveSundialsIDA( & allocate( rootsfound(nRoot) ) allocate( rootdir(nRoot) ) rootdir = 0 - retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4IDA)) + retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4ida)) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif else ! will not use, allocate at something nRoot = 1 @@ -378,51 +378,51 @@ subroutine summaSolveSundialsIDA( & mu = ku; lu = kl; ! Create banded SUNMatrix for use in linear solves sunmat_A => FSUNBandMatrix(nState, mu, lu, sunctx) - if (.not. associated(sunmat_A)) then; err=20; message='summaSolveSundialsIDA: sunmat = NULL'; return; endif + if (.not. associated(sunmat_A)) then; err=20; message='summaSolveSundials4ida: sunmat = NULL'; return; endif ! Create banded SUNLinearSolver object sunlinsol_LS => FSUNLinSol_Band(sunvec_y, sunmat_A, sunctx) - if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolveSundialsIDA: sunlinsol = NULL'; return; endif + if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolveSundials4ida: sunlinsol = NULL'; return; endif case(ixFullMatrix) ! Create dense SUNMatrix for use in linear solves sunmat_A => FSUNDenseMatrix(nState, nState, sunctx) - if (.not. associated(sunmat_A)) then; err=20; message='summaSolveSundialsIDA: sunmat = NULL'; return; endif + if (.not. associated(sunmat_A)) then; err=20; message='summaSolveSundials4ida: sunmat = NULL'; return; endif ! Create dense SUNLinearSolver object sunlinsol_LS => FSUNLinSol_Dense(sunvec_y, sunmat_A, sunctx) - if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolveSundialsIDA: sunlinsol = NULL'; return; endif + if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolveSundials4ida: sunlinsol = NULL'; return; endif ! check - case default; err=20; message='summaSolveSundialsIDA: error in type of matrix'; return + case default; err=20; message='summaSolveSundials4ida: error in type of matrix'; return end select ! form of matrix ! Attach the matrix and linear solver retval = FIDASetLinearSolver(ida_mem, sunlinsol_LS, sunmat_A); - if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetLinearSolver'; return; endif + if (retval /= 0) then; err=20; message='summaSolveSundials4ida: error in FIDASetLinearSolver'; return; endif ! Set the user-supplied Jacobian routine if(.not.use_fdJac)then - retval = FIDASetJacFn(ida_mem, c_funloc(computJacob4IDA)) - if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetJacFn'; return; endif + retval = FIDASetJacFn(ida_mem, c_funloc(computJacob4ida)) + if (retval /= 0) then; err=20; message='summaSolveSundials4ida: error in FIDASetJacFn'; return; endif endif ! Create Newton SUNNonlinearSolver object sunnonlin_NLS => FSUNNonlinSol_Newton(sunvec_y, sunctx) - if (.not. associated(sunnonlin_NLS)) then; err=20; message='summaSolveSundialsIDA: sunnonlinsol = NULL'; return; endif + if (.not. associated(sunnonlin_NLS)) then; err=20; message='summaSolveSundials4ida: sunnonlinsol = NULL'; return; endif ! Attach the nonlinear solver retval = FIDASetNonlinearSolver(ida_mem, sunnonlin_NLS) - if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetNonlinearSolver'; return; endif + if (retval /= 0) then; err=20; message='summaSolveSundials4ida: error in FIDASetNonlinearSolver'; return; endif ! Enforce the solver to stop at end of the time step retval = FIDASetStopTime(ida_mem, dt) - if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetStopTime'; return; endif + if (retval /= 0) then; err=20; message='summaSolveSundials4ida: error in FIDASetStopTime'; return; endif ! Set solver parameters such as maximum order, number of iterations, ... call setSolverParams(dt, nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)), ida_mem, retval) - if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in setSolverParams'; return; endif + if (retval /= 0) then; err=20; message='summaSolveSundials4ida: error in setSolverParams'; return; endif ! Disable error messages and warnings if(offErrWarnMessage) then @@ -588,10 +588,10 @@ subroutine summaSolveSundialsIDA( & retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAReInit'; return; endif if(dt_last(1) < 0.1_rkind)then ! don't keep calling if step is small (more accurate with this tiny but getting hung up) - retval = FIDARootInit(ida_mem, 0, c_funloc(layerDisCont4IDA)) + retval = FIDARootInit(ida_mem, 0, c_funloc(layerDisCont4ida)) tinystep = .true. else - retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4IDA)) + retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4ida)) tinystep = .false. endif if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif @@ -647,7 +647,7 @@ subroutine summaSolveSundialsIDA( & call FN_VDestroy(sunvec_yp) retval = FSUNContext_Free(sunctx) -end subroutine summaSolveSundialsIDA +end subroutine summaSolveSundials4ida ! ---------------------------------------------------------------- ! SetInitialCondition: routine to initialize u and up vectors. @@ -825,7 +825,7 @@ end subroutine find_rootdir ! ---------------------------------------------------------------------------------------- -! layerDisCont4IDA: The root function routine to find soil matrix potential = 0, +! layerDisCont4ida: The root function routine to find soil matrix potential = 0, ! soil temp = critical frozen point, and snow and veg temp = Tfreeze ! ---------------------------------------------------------------------------------------- ! Return values: @@ -833,8 +833,8 @@ end subroutine find_rootdir ! 1 = recoverable error, ! -1 = non-recoverable error ! ---------------------------------------------------------------------------------------- - integer(c_int) function layerDisCont4IDA(t, sunvec_u, sunvec_up, gout, user_data) & - result(ierr) bind(C,name='layerDisCont4IDA') + integer(c_int) function layerDisCont4ida(t, sunvec_u, sunvec_up, gout, user_data) & + result(ierr) bind(C,name='layerDisCont4ida') !======= Inclusions =========== use, intrinsic :: iso_c_binding @@ -919,6 +919,6 @@ integer(c_int) function layerDisCont4IDA(t, sunvec_u, sunvec_up, gout, user_data ierr = 0 return - end function layerDisCont4IDA + end function layerDisCont4ida -end module summaSolveSundialsIDA_module +end module summaSolveSundials4ida_module diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 0b5f3f6d8..70520e019 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -135,8 +135,8 @@ subroutine systemSolvSundials(& USE eval8summaSundials_module,only:eval8summaSundials ! simulation of fluxes and residuals given a trial state vector USE getVectorz_module,only:getScaling ! get the scaling vectors USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy - USE tol4IDA_module,only:popTol4IDA ! pop tolerances - USE summaSolveSundialsIDA_module,only:summaSolveSundialsIDA ! solve DAE by IDA + USE tol4ida_module,only:popTol4ida ! pop tolerances + USE summaSolveSundials4ida_module,only:summaSolveSundials4ida ! solve DAE by IDA USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy use, intrinsic :: iso_c_binding implicit none @@ -497,7 +497,7 @@ subroutine systemSolvSundials(& endif ! get tolerance vectors - call popTol4IDA(& + call popTol4ida(& ! input nState, & ! intent(in): number of desired state variables prog_data, & ! intent(in): model prognostic variables for a local HRU @@ -521,7 +521,7 @@ subroutine systemSolvSundials(& ! initialize sum of compression of the soil matrix mLayerCmpress_sum(:) = 0._rkind - call summaSolveSundialsIDA(& + call summaSolveSundials4ida(& dt_cur, & ! intent(in): data time step atol, & ! intent(in): absolute tolerance rtol, & ! intent(in): relative tolerance diff --git a/build/source/dshare/tol4IDA.f90 b/build/source/engine/tol4ida.f90 similarity index 97% rename from build/source/dshare/tol4IDA.f90 rename to build/source/engine/tol4ida.f90 index 7e0c25763..ce7756683 100644 --- a/build/source/dshare/tol4IDA.f90 +++ b/build/source/engine/tol4ida.f90 @@ -1,9 +1,9 @@ -module tol4IDA_module +module tol4ida_module !======= Inclusions =========== use, intrinsic :: iso_c_binding use nrtype -use type4IDA +use type4ida ! missing values USE globalData,only:integerMissing ! missing integer @@ -61,28 +61,28 @@ module tol4IDA_module ! privacy implicit none private -public::computWeight4IDA -public::popTol4IDA +public::computWeight4ida +public::popTol4ida contains ! ********************************************************************************************************** -! public function computWeight4IDA: compute w_i = 1 / ( rtol_i * y_i + atol_i ) +! public function computWeight4ida: compute w_i = 1 / ( rtol_i * y_i + atol_i ) ! ********************************************************************************************************** ! Return values: ! 0 = success, ! -1 = non-recoverable error, NaN or negative values ! ---------------------------------------------------------------- -integer(c_int) function computWeight4IDA(sunvec_y, sunvec_ewt, user_data) & - result(ierr) bind(C,name='computWeight4IDA') +integer(c_int) function computWeight4ida(sunvec_y, sunvec_ewt, user_data) & + result(ierr) bind(C,name='computWeight4ida') !======= Inclusions =========== use, intrinsic :: iso_c_binding use fsundials_nvector_mod use fnvector_serial_mod use nrtype - use type4IDA + use type4ida !======= Declarations ========= implicit none @@ -118,13 +118,13 @@ integer(c_int) function computWeight4IDA(sunvec_y, sunvec_ewt, user_data) & ierr = 0 return -end function computWeight4IDA +end function computWeight4ida ! ********************************************************************************************************** -! public subroutine popTol4IDA: populate tolerances for state vectors +! public subroutine popTol4ida: populate tolerances for state vectors ! ********************************************************************************************************** -subroutine popTol4IDA(& +subroutine popTol4ida(& ! input: data structures nState, & ! intent(in): number of desired state variables prog_data, & ! intent(in): model prognostic variables for a local HRU @@ -206,7 +206,7 @@ subroutine popTol4IDA(& ) ! end association with variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message='popTol4IDA/' + err=0; message='popTol4ida/' ! ----- ! * initialize state vectors... @@ -280,7 +280,7 @@ subroutine popTol4IDA(& endif end associate fixedLength ! end association to variables in the data structure where vector length does not change -end subroutine popTol4IDA +end subroutine popTol4ida -end module tol4IDA_module +end module tol4ida_module diff --git a/docs/sundials_bmi/flags_params_sundials.txt b/docs/sundials_bmi/flags_params_sundials.txt index 28515657a..8204606be 100644 --- a/docs/sundials_bmi/flags_params_sundials.txt +++ b/docs/sundials_bmi/flags_params_sundials.txt @@ -7,9 +7,9 @@ All SUMMA-SUNDIALS files are in build/source/engine. The important ones are as f systemSolvSundials.f90: contains public subroutine systemSolvSundials which runs the coupled energy-mass model for one timestep. -summaSolveSundialsIDA.f90: contains public subroutine summaSolveSundialsIDA which solves the differential equation system F(y,y') = 0 by IDA (y is the state vector) and private subroutines setInitialCondition and setSolverParams. Subroutine setSolverParams can be used to to set parameters (maximum order, number of nonlinear iteration , etc) in IDA solver. +summaSolveSundials4ida.f90: contains public subroutine summaSolveSundials4ida which solves the differential equation system F(y,y') = 0 by IDA (y is the state vector) and private subroutines setInitialCondition and setSolverParams. Subroutine setSolverParams can be used to to set parameters (maximum order, number of nonlinear iteration , etc) in IDA solver. -eval8summaSundials.f90: contains public subroutine eval8summaSundials which computes the residual vector F(t,y,y') mainly by calling varExtract, updateVarsSundials, computFlux, and computResidSundials. We also switch between different forms of the energy equation in this subroutine. It also contains public function eval8summa4IDA which is the interface wrapper for computing the residual vector required for the IDA solver. +eval8summaSundials.f90: contains public subroutine eval8summaSundials which computes the residual vector F(t,y,y') mainly by calling varExtract, updateVarsSundials, computFlux, and computResidSundials. We also switch between different forms of the energy equation in this subroutine. It also contains public function eval8summa4ida which is the interface wrapper for computing the residual vector required for the IDA solver. -computJacobSundials.f90: contains public subroutine computJacobSundials which computes the Jacobian matrix dF/dy + c dF/dy'. It also contains the public function computJacob4IDA which is the interface wrapper for computing the Jacobian matrix required for the IDA solver. +computJacobSundials.f90: contains public subroutine computJacobSundials which computes the Jacobian matrix dF/dy + c dF/dy'. It also contains the public function computJacob4ida which is the interface wrapper for computing the Jacobian matrix required for the IDA solver. From 5b4d1e4797e40ab88cc4d2bf015f58ae16e942e5 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 10 May 2023 16:52:54 +0900 Subject: [PATCH 0714/1472] fix a readme --- build/cmake/README_ngen.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/cmake/README_ngen.md b/build/cmake/README_ngen.md index 96942c04e..3fa495e4d 100644 --- a/build/cmake/README_ngen.md +++ b/build/cmake/README_ngen.md @@ -51,7 +51,7 @@ First, cd into the outer directory containing the submodule: cd extern/summa -If you want to use Sundials_IDA, set -DCMAKE_BUILD_TYPE=IDA_NexGen in the build script. Then, before summa can be built, Sundials needs to be installed. +If you want to use Sundials IDA or BE Kinsol, set -DCMAKE_BUILD_TYPE=Sundials_NexGen in the build script. Then, before summa can be built, Sundials needs to be installed. Download the latest release of IDA solver from SUNDIALS package in https://computing.llnl.gov/projects/sundials/sundials-software wget "https://github.com/LLNL/sundials/releases/download/v6.3.0/sundials-6.3.0.tar.gz" From 2f345445d369d0b54938350b8ab6b0bc1294b0a7 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 11 May 2023 11:01:22 +0900 Subject: [PATCH 0715/1472] fixing BMI cmake --- build/cmake/CMakeLists.txt | 5 +++-- build/source/driver/summa_bmi.f90 | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 116bd5cea..982f047ac 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -1,6 +1,7 @@ cmake_minimum_required(VERSION 3.10 FATAL_ERROR) enable_language(C Fortran) -SET (CMAKE_Fortran_COMPILER gfortran) +set (CMAKE_Fortran_COMPILER $ENV{FC}) #NexGen +#set (CMAKE_Fortran_COMPILER gfortran) #others #========================================================================================= @@ -10,7 +11,7 @@ SET (CMAKE_Fortran_COMPILER gfortran) # Recipes only. # Note, Sundials, NexGen, and Actors will all require extra libraries to be downloaded # and compiled. Currently, NexGen cannot work with Actors. -# If you install Sundials, you can compile Summa with Sundials, which allows you to run +# If you install Sundials, you can compile Summa with Sundials, which allows you to run # Summa with a solver of BE based off Numerical Recipes, Kinsol, or IDA. The solver choice # is then set in the code decisions file. # Other options include how to drive the code, NexGen (with BMI) or Actors (without BMI) diff --git a/build/source/driver/summa_bmi.f90 b/build/source/driver/summa_bmi.f90 index 5d89744b7..cd7ae5c24 100644 --- a/build/source/driver/summa_bmi.f90 +++ b/build/source/driver/summa_bmi.f90 @@ -18,7 +18,7 @@ ! You should have received a copy of the GNU General Public License ! along with this program. If not, see . -module summa_bmi +module summabmi ! provides functions needed for summa driver routines adding BMI functions ! ***************************************************************************** ! * use desired modules @@ -1254,4 +1254,4 @@ subroutine get_basin_field(this, name, do_nHRU, target_arr, itarget_arr) end associate summaVars end subroutine get_basin_field -end module summa_bmi +end module summabmi From 72ac933a5fc717952373d537b4f99b5d05359c9c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 11 May 2023 11:02:29 +0900 Subject: [PATCH 0716/1472] replacing sundials decision with ida --- build/source/engine/computJacobSundials.f90 | 2 +- build/source/engine/coupled_em.f90 | 4 ++-- build/source/engine/mDecisions.f90 | 5 +++-- build/source/engine/opSplittin.f90 | 6 +++--- build/source/engine/varSubstep.f90 | 18 +++++++++--------- docs/sundials_bmi/flags_params_sundials.txt | 2 +- 6 files changed, 19 insertions(+), 18 deletions(-) diff --git a/build/source/engine/computJacobSundials.f90 b/build/source/engine/computJacobSundials.f90 index 9ecd367cd..85c5ddd34 100644 --- a/build/source/engine/computJacobSundials.f90 +++ b/build/source/engine/computJacobSundials.f90 @@ -1283,7 +1283,7 @@ subroutine computJacobSetup(& call updateVarsSundials(& ! input - .true., & ! intent(in): logical flag if computing Jacobian for sundials solver + .true., & ! intent(in): logical flag if computing Jacobian for Sundials solver .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 63168b426..45d0ad94b 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -84,7 +84,7 @@ module coupled_em_module USE mDecisions_module,only: & be_numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes be_kinsol ,& ! SUNDIALS backward Euler solution using Kinsol - sundials ! SUNDIALS solution using IDA + ida ! SUNDIALS solution using IDA ! privacy implicit none @@ -1193,7 +1193,7 @@ subroutine coupled_em(& ! identify the need to check the mass balance, both methods should work if tolerance coarse enough select case(ixNumericalMethod) - case(sundials); checkMassBalance = .true. ! sundials gives instantaneous fluxes and were summed for an average flux for checks + case(ida); checkMassBalance = .true. ! Sundials gives instantaneous fluxes and were summed for an average flux for checks case(be_numrec); checkMassBalance = .true. ! be_numrec gives finite difference dt_sub fluxes and were summed for an average flux for checks case default; err=20; message=trim(message)//'expect num_method to be sundials, be_kinsol, or be_numrec (or itertive, which is be_numrec)'; return end select diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index 11f678222..5d3224c9f 100755 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -62,7 +62,7 @@ module mDecisions_module ! look-up values for the choice of numerical method integer(i4b),parameter,public :: be_numrec = 81 ! home-grown backward Euler solution using free versions of Numerical recipes integer(i4b),parameter,public :: be_kinsol = 82 ! SUNDIALS backward Euler solution using Kinsol -integer(i4b),parameter,public :: sundials = 83 ! SUNDIALS solution using IDA +integer(i4b),parameter,public :: ida = 83 ! SUNDIALS solution using IDA ! look-up values for method used to compute derivative integer(i4b),parameter,public :: numerical = 91 ! numerical solution integer(i4b),parameter,public :: analytical = 92 ! analytical solution @@ -414,7 +414,8 @@ subroutine mDecisions(err,message) case('be_numrec'); model_decisions(iLookDECISIONS%num_method)%iDecision = be_numrec ! home-grown backward Euler solution using free versions of Numerical recipes case('itertive' ); model_decisions(iLookDECISIONS%num_method)%iDecision = be_numrec ! home-grown backward Euler solution (included for backwards compatibility) case('be_kinsol'); model_decisions(iLookDECISIONS%num_method)%iDecision = be_kinsol ! SUNDIALS backward Euler solution using Kinsol - case('sundials' ); model_decisions(iLookDECISIONS%num_method)%iDecision = sundials ! SUNDIALS solution using IDA + case('sundials' ); model_decisions(iLookDECISIONS%num_method)%iDecision = ida ! SUNDIALS solution using IDA + case('ida ' ); model_decisions(iLookDECISIONS%num_method)%iDecision = ida ! SUNDIALS solution using IDA case default err=10; message=trim(message)//"unknown numerical method [option="//trim(model_decisions(iLookDECISIONS%num_method)%cDecision)//"]"; return end select diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 63cc20df6..cd5bba796 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -101,7 +101,7 @@ module opSplittin_module USE mDecisions_module,only: & be_numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes be_kinsol ,& ! SUNDIALS backward Euler solution using Kinsol - sundials ! SUNDIALS solution using IDA + ida ! SUNDIALS solution using IDA ! safety: set private unless specified otherwise implicit none @@ -318,9 +318,9 @@ subroutine opSplittin(& ! we just solve the fully coupled problem by with Sundials for now select case(ixNumericalMethod) - case(sundials); nCoupling = 1 + case(ida); nCoupling = 1 case(be_numrec); nCoupling = 2 - case default; err=20; message=trim(message)//'expect num_method to be sundials, be_kinsol, or be_numrec (or itertive, which is be_numrec)'; return + case default; err=20; message=trim(message)//'expect num_method to be ida, be_kinsol, or be_numrec (or itertive, which is be_numrec)'; return end select ! ----- diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 5ba481dca..68f94b509 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -74,7 +74,7 @@ module varSubstep_module USE mDecisions_module,only: & be_numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes be_kinsol ,& ! SUNDIALS backward Euler solution using Kinsol - sundials ! SUNDIALS solution using IDA + ida ! SUNDIALS solution using IDA ! safety: set private unless specified otherwise implicit none @@ -326,7 +326,7 @@ subroutine varSubstep(& ! ----------------------- ! solve the system of equations for a given state subset select case(ixNumericalMethod) - case(sundials) + case(ida) #ifdef SUNDIALS_ACTIVE call systemSolvSundials(& ! input: model control @@ -362,7 +362,7 @@ subroutine varSubstep(& untappedMelt(:) = 0._rkind ! set untapped melt energy to zero niter = 0 ! will not use #else - err=20; message=trim(message)//'cannot use num_method as sundials if did not compile with -DCMAKE_BUILD_TYPE=IDA'; return + err=20; message=trim(message)//'cannot use num_method as uda if did not compile with -DCMAKE_BUILD_TYPE=Sundials'; return #endif case(be_numrec) call systemSolv(& @@ -398,7 +398,7 @@ subroutine varSubstep(& niter, & ! intent(out): number of iterations taken err,cmessage) ! intent(out): error code and error message stateVecPrime = stateVecTrial ! will not use, dummy - case default; err=20; message=trim(message)//'expect num_method to be sundials, be_kinsol, or be_numrec (or itertive, which is be_numrec)'; return + case default; err=20; message=trim(message)//'expect num_method to be ida, be_kinsol, or be_numrec (or itertive, which is be_numrec)'; return end select if(err/=0)then @@ -462,9 +462,9 @@ subroutine varSubstep(& ! identify the need to check the mass balance select case(ixNumericalMethod) - case(sundials); checkMassBalance = .false. ! only for be_numrec because sundials has instantaneous fluxes only + case(ida); checkMassBalance = .false. ! only for be_numrec because sundials has instantaneous fluxes only case(be_numrec); checkMassBalance = .true. ! (.not.scalarSolution) - case default; err=20; message=trim(message)//'expect num_method to be sundials, be_kinsol, or be_numrec (or itertive, which is be_numrec)'; return + case default; err=20; message=trim(message)//'expect num_method to be ida, be_kinsol, or be_numrec (or itertive, which is be_numrec)'; return end select ! identify the need to check the energy balance, DOES NOT WORK YET and only check if ixHowHeatCap == enthalpyFD checkNrgBalance = .false. @@ -837,7 +837,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe scalarAquiferStoragePrime = realMissing select case(ixNumericalMethod) - case(sundials) + case(ida) #ifdef SUNDIALS_ACTIVE call varExtract(& ! input @@ -897,7 +897,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! output: error control err,cmessage) ! intent(out): error control #else - err=20; message=trim(message)//'cannot use num_method as sundials if did not compile with -DCMAKE_BUILD_TYPE=IDA'; return + err=20; message=trim(message)//'cannot use num_method as ida if did not compile with -DCMAKE_BUILD_TYPE=Sundials'; return #endif case(be_numrec) ! update diagnostic variables @@ -924,7 +924,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) ! output: error control err,cmessage) ! intent(out): error control - case default; err=20; message=trim(message)//'expect num_method to be sundials, be_kinsol, or be_numrec (or itertive, which is be_numrec)'; return + case default; err=20; message=trim(message)//'expect num_method to be ida, be_kinsol, or be_numrec (or itertive, which is be_numrec)'; return end select if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) diff --git a/docs/sundials_bmi/flags_params_sundials.txt b/docs/sundials_bmi/flags_params_sundials.txt index 8204606be..d5fb2039b 100644 --- a/docs/sundials_bmi/flags_params_sundials.txt +++ b/docs/sundials_bmi/flags_params_sundials.txt @@ -1,5 +1,5 @@ -To switch between SUMMA-BE and SUMMA-SUNDIALS, the num_method in the model_decision file can be either one of the values "be_numrec" (choice "itertive" is backward compatible), "be_kinsol", or "sundials". +To switch between SUMMA-BE and SUMMA-SUNDIALS, the num_method in the model_decision file can be either one of the values "be_numrec" (choice "itertive" is backward compatible), "be_kinsol", or "ida". In energy equation, the heat capacity is computed using either the analytical (closed) formula or the finite difference formula (dH_T/dT). The "howHeatCap" variable has been added to the var_lookup module to handle such decision. A user should add this variable to the model_decision file with one of the values "closedForm" or "enthalpyFD". Choice of num_method as "itertive" will set num_method=be_numrec and howHeatCap=closedForm. From 9d7e378009fb07953e54e5f8a56faad2a3656d10 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 11 May 2023 23:14:46 +0900 Subject: [PATCH 0717/1472] cmake comment --- build/cmake/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 982f047ac..9df66da50 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.10 FATAL_ERROR) enable_language(C Fortran) -set (CMAKE_Fortran_COMPILER $ENV{FC}) #NexGen -#set (CMAKE_Fortran_COMPILER gfortran) #others +set (CMAKE_Fortran_COMPILER $ENV{FC}) #for NexGen need, works for others +#set (CMAKE_Fortran_COMPILER gfortran) # for others may work #========================================================================================= From ad38fedecccb2bbf9c042026981dba7dfdef442a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 11 May 2023 23:56:56 +0900 Subject: [PATCH 0718/1472] cmake actors executable name --- build/cmake/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 9df66da50..bb125e3e8 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -41,6 +41,9 @@ if(CMAKE_BUILD_TYPE MATCHES Sundials) elseif(CMAKE_BUILD_TYPE MATCHES BE) set(EXEC_NAME summa.exe) endif() +if(CMAKE_BUILD_TYPE MATCHES Actors) + set(EXEC_NAME summa_actors) +endif() if(CMAKE_BUILD_TYPE MATCHES NexGen) message("\nUsing NexGen Framework, should have been installed previously") From d01a3bae4f5db41803799125c0bd303ae76b06db Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 12 May 2023 00:34:18 +0900 Subject: [PATCH 0719/1472] mods for cluster makefile and actors --- build/cmake/CMakeLists.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index bb125e3e8..77c1db2af 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -139,7 +139,7 @@ if(CMAKE_BUILD_TYPE MATCHES Cluster) # Packages that are required find_package(NetCDF REQUIRED) - find_package(LAPACK REQUIRED) + #find_package(LAPACK REQUIRED) # Set include directories set(INCLUDES $ENV{EBROOTNETCDFMINFORTRAN}/include ${netCDF_INCLUDES} ${LAPACK_INCLUDES}) @@ -504,14 +504,11 @@ file(APPEND ${VERSIONFILE} "character(len=64), parameter :: gitHash = '${GIT # Build NOAH_MP Object add_library(SUMMA_NOAHMP OBJECT ${NOAHMP} ${NRUTIL}) target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) -#target_link_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) # Build SUMMA_COMM Object add_library(SUMMA_COMM OBJECT ${COMM_ALL}) target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) -target_link_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES}) -#target_link_libraries(SUMMA_COMM PUBLIC ${LIBRARIES}) # For NexGen, build SUMMA Shared Library and add the outside BMI libraries if(CMAKE_BUILD_TYPE MATCHES NexGen) @@ -562,6 +559,7 @@ elseif(CMAKE_BUILD_TYPE MATCHES Actors) ${SUMMA_CLIENT} ${SUMMA_SERVER}) set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) + target_compile_options(${EXEC_NAME} PUBLIC ${FLAGS_CXX}) target_include_directories(${EXEC_NAME} PUBLIC ${INC_ACTORS}) target_link_libraries( ${EXEC_NAME} ${LIB_ACTORS}) From d6e0708649f709027e5f4601a9001fa6395bfda3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 12 May 2023 12:43:53 +0900 Subject: [PATCH 0720/1472] JulDay model name agreement, fixing actors part of cmake --- build/cmake/CMakeLists.txt | 11 ++++-- build/source/driver/summa_modelRun.f90 | 2 +- build/source/dshare/globalData.f90 | 6 +-- build/source/engine/coupled_em.f90 | 2 +- build/source/engine/derivforce.f90 | 4 +- build/source/engine/mDecisions.f90 | 6 +-- build/source/engine/read_force.f90 | 52 +++++++++++++------------- build/source/engine/run_oneHRU.f90 | 2 +- build/source/engine/vegPhenlgy.f90 | 4 +- 9 files changed, 46 insertions(+), 43 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 77c1db2af..e2599906c 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -120,7 +120,9 @@ if(CMAKE_BUILD_TYPE MATCHES Sundials) foreach(dir IN ITEMS "${F_MASTER}../sundials/instdir" "${PARENT_DIR}../sundials/instdir" - "../../../../SummaSundials/sundials/instdir") + "${F_MASTER}../../SummaSundials/sundials/instdir" + "${PARENT_DIR}../../SummaSundials/sundials/instdir" + "../../../../SummaSundials/sundials/instdir") #look in same head directory as summa or up to another installation if(EXISTS ${dir}) set(DIR_SUNDIALS ${dir}) message("\nFound Sundials directory at ${DIR_SUNDIALS}") @@ -138,8 +140,8 @@ if(CMAKE_BUILD_TYPE MATCHES Cluster) set(BLA_VENDOR OpenBLAS) # Packages that are required - find_package(NetCDF REQUIRED) - #find_package(LAPACK REQUIRED) + #find_package(NetCDF REQUIRED) + find_package(LAPACK REQUIRED) # Set include directories set(INCLUDES $ENV{EBROOTNETCDFMINFORTRAN}/include ${netCDF_INCLUDES} ${LAPACK_INCLUDES}) @@ -508,6 +510,7 @@ target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) # Build SUMMA_COMM Object add_library(SUMMA_COMM OBJECT ${COMM_ALL}) target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) +target_link_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES}) # For NexGen, build SUMMA Shared Library and add the outside BMI libraries @@ -561,7 +564,7 @@ elseif(CMAKE_BUILD_TYPE MATCHES Actors) set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) target_compile_options(${EXEC_NAME} PUBLIC ${FLAGS_CXX}) target_include_directories(${EXEC_NAME} PUBLIC ${INC_ACTORS}) - target_link_libraries( ${EXEC_NAME} ${LIB_ACTORS}) + target_link_libraries( ${EXEC_NAME} ${LIB_ACTORS} summaactors) # For other cases, build SUMMA Shared Library and add the executable else() diff --git a/build/source/driver/summa_modelRun.f90 b/build/source/driver/summa_modelRun.f90 index 9aa6e0886..cfe271458 100644 --- a/build/source/driver/summa_modelRun.f90 +++ b/build/source/driver/summa_modelRun.f90 @@ -33,7 +33,7 @@ module summa_modelRun USE summa_util,only:handle_err ! these are needed because we cannot access them in modules locally if we might use those modules with Actors -USE globalData,only:fracJulday ! fractional julian days since the start of year +USE globalData,only:fracJulDay ! fractional julian days since the start of year USE globalData,only:yearLength ! number of days in the current year ! safety: set private unless specified otherwise diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 index fc3724362..c701aae94 100755 --- a/build/source/dshare/globalData.f90 +++ b/build/source/dshare/globalData.f90 @@ -252,8 +252,8 @@ MODULE globalData integer(i4b),save,public :: nHRUrun ! number of HRUs in the run domain integer(i4b),save,public :: nGRUrun ! number of GRUs in the run domain real(rkind),save,public :: data_step ! length of the time_step - real(rkind),save,public :: refJulday ! reference time in fractional julian days - real(rkind),save,public :: refJulday_data ! reference time in fractional julian days (data files) + real(rkind),save,public :: refJulDay ! reference time in fractional julian days + real(rkind),save,public :: refJulDay_data ! reference time in fractional julian days (data files) real(rkind),save,public :: dJulianStart ! julian day of start time of simulation real(rkind),save,public :: dJulianFinsh ! julian day of end time of simulation integer(i4b),save,public :: nHRUfile ! number of HRUs in the file @@ -320,7 +320,7 @@ MODULE globalData logical(lgt),dimension(maxvarFreq),save,public :: resetStats=.true. ! flags to reset statistics ! define common variables non-Actors - real(rkind),save,public :: fracJulday ! fractional julian days since the start of year + real(rkind),save,public :: fracJulDay ! fractional julian days since the start of year real(rkind),save,public :: tmZoneOffsetFracDay ! time zone offset in fractional days integer(i4b),save,public :: yearLength ! number of days in the current year #endif diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 45d0ad94b..7b4bb2c23 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -168,7 +168,7 @@ subroutine coupled_em(& type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - real(rkind),intent(in) :: fracJulday ! fractional julian days since the start of year + real(rkind),intent(in) :: fracJulDay ! fractional julian days since the start of year integer(i4b),intent(in) :: yearLength ! number of days in the current year ! error control integer(i4b),intent(out) :: err ! error code diff --git a/build/source/engine/derivforce.f90 b/build/source/engine/derivforce.f90 index aff470750..0f08584c9 100755 --- a/build/source/engine/derivforce.f90 +++ b/build/source/engine/derivforce.f90 @@ -31,7 +31,7 @@ module derivforce_module USE multiconst,only:minprhour ! number of minutes in an hour ! global time information -USE globalData,only:refJulday ! reference time (fractional julian days) +USE globalData,only:refJulDay ! reference time (fractional julian days) USE globalData,only:data_step ! length of the data step (s) ! model decisions @@ -234,7 +234,7 @@ subroutine derivforce(time_data,forc_data,attr_data,mpar_data,prog_data,diag_dat endif ! compute the local time - julianTime = secondsSinceRefTime/secprday + refJulday ! julian time (days) + julianTime = secondsSinceRefTime/secprday + refJulDay ! julian time (days) ! convert julian day to year/month/day/hour/minute call compcalday(julianTime+timeOffset, & ! input = julian day diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index 5d3224c9f..00a033e27 100755 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -168,7 +168,7 @@ subroutine mDecisions(err,message) ! model time structures USE multiconst,only:secprday ! number of seconds in a day USE var_lookup,only:iLookTIME ! named variables that identify indices in the time structures - USE globalData,only:refTime,refJulday ! reference time + USE globalData,only:refTime,refJulDay ! reference time USE globalData,only:oldTime ! time from the previous time step USE globalData,only:startTime,finshTime ! start/end time of simulation USE globalData,only:dJulianStart ! julian day of start time of simulation @@ -196,7 +196,7 @@ subroutine mDecisions(err,message) #ifdef ACTORS_ACTIVE integer(c_int),intent(out) :: num_steps ! number of time steps in the simulation integer(c_int),intent(out) :: err ! error code - character(*) :: message ! error message + character(256) :: message ! error message #else integer(i4b) :: num_steps ! number of time steps in the simulation integer(i4b),intent(out) :: err ! error code @@ -235,7 +235,7 @@ subroutine mDecisions(err,message) refTime%var(iLookTIME%ih), & ! hour refTime%var(iLookTIME%imin), & ! minute 0._rkind, & ! second - refJulday, & ! julian date for the start of the simulation + refJulDay, & ! julian date for the start of the simulation err, cmessage) ! error control if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if diff --git a/build/source/engine/read_force.f90 b/build/source/engine/read_force.f90 index def10a691..40cc70b84 100644 --- a/build/source/engine/read_force.f90 +++ b/build/source/engine/read_force.f90 @@ -43,8 +43,8 @@ module read_force_module USE globalData,only:data_step ! length of the data step (s) USE globalData,only:forcFileInfo ! forcing file info USE globalData,only:dJulianStart ! julian day of start time of simulation -USE globalData,only:refJulday ! reference time (fractional julian days) -USE globalData,only:refJulday_data ! reference time for data files (fractional julian days) +USE globalData,only:refJulDay ! reference time (fractional julian days) +USE globalData,only:refJulDay_data ! reference time for data files (fractional julian days) USE globalData,only:fracJulDay ! fractional julian days since the start of year USE globalData,only:yearLength ! number of days in the current year USE globalData,only:nHRUfile ! number of days in the data file @@ -75,7 +75,7 @@ module read_force_module subroutine read_force(istep,iFile,iRead,ncid,time_data,forcStruct,err,message) ! provide access to subroutines USE netcdf ! netcdf capability - USE time_utils_module,only:compJulday ! convert calendar date to julian day + USE time_utils_module,only:compJulDay ! convert calendar date to julian day USE time_utils_module,only:compcalday ! convert julian day to calendar date USE time_utils_module,only:elapsedSec ! calculate the elapsed time implicit none @@ -96,7 +96,7 @@ subroutine read_force(istep,iFile,iRead,ncid,time_data,forcStruct,err,message) character(len=256),save :: infile ! filename character(len=256) :: cmessage ! error message for downwind routine real(rkind) :: startJulDay ! julian day at the start of the year - real(rkind) :: currentJulday ! Julian day of current time step + real(rkind) :: currentJulDay ! Julian day of current time step logical(lgt),parameter :: checkTime=.false. ! flag to check the time ! Start procedure here err=0; message="read_force/" @@ -116,7 +116,7 @@ subroutine read_force(istep,iFile,iRead,ncid,time_data,forcStruct,err,message) ! ***** part 0-1: if using NGEN forcing will be using forcing read with BMI and only need time ! ********************************************************************************************** ! get forcing time data - call createForcingTimeData(currentJulday,time_data,err,message) + call createForcingTimeData(currentJulDay,time_data,err,message) if(err/=0)then; message=trim(message)//trim(cmessage); return; end if #else @@ -128,7 +128,7 @@ subroutine read_force(istep,iFile,iRead,ncid,time_data,forcStruct,err,message) if(ncid==integerMissing)then ! file is closed if ncid==integerMissing ! identify the first time step - call getFirstTimestep(currentJulday,iFile,iRead,ncid,err,cmessage) + call getFirstTimestep(currentJulDay,iFile,iRead,ncid,err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; end if end if ! if the file is not yet open @@ -167,7 +167,7 @@ subroutine read_force(istep,iFile,iRead,ncid,time_data,forcStruct,err,message) end if ! if we've passed the end of the NetCDF file ! read forcing data - call readForcingData(currentJulday,ncId,iFile,iRead,nHRUlocal,time_data,forcStruct,err,message) + call readForcingData(currentJulDay,ncId,iFile,iRead,nHRUlocal,time_data,forcStruct,err,message) if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! check that the file was in fact open @@ -194,15 +194,15 @@ subroutine read_force(istep,iFile,iRead,ncid,time_data,forcStruct,err,message) time_data(iLookTIME%id), & ! input = day time_data(iLookTIME%ih), & ! input = hour time_data(iLookTIME%imin),0._rkind, & ! input = minute/second - currentJulday,err,cmessage) ! output = julian day (fraction of day) + error control + currentJulDay,err,cmessage) ! output = julian day (fraction of day) + error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! compute the time since the start of the year (in fractional days) - fracJulday = currentJulday - startJulDay + fracJulDay = currentJulDay - startJulDay ! set timing of current forcing vector (in seconds since reference day) ! NOTE: It is a bit silly to have time information for each HRU and GRU do iGRU=1,size(gru_struc) do iHRU=1,gru_struc(iGRU)%hruCount - forcStruct%gru(iGRU)%hru(iHRU)%var(iLookFORCE%time) = (currentJulday-refJulday)*secprday + forcStruct%gru(iGRU)%hru(iHRU)%var(iLookFORCE%time) = (currentJulDay-refJulDay)*secprday end do ! looping through HRUs end do ! looping through GRUs @@ -225,7 +225,7 @@ subroutine read_force(istep,iFile,iRead,ncid,time_data,forcStruct,err,message) time_data(iLookTIME%id), & ! day time_data(iLookTIME%ih), & ! hour time_data(iLookTIME%imin), & ! minute - fracJulday, & ! fractional julian day for the current time step + fracJulDay, & ! fractional julian day for the current time step yearLength ! number of days in the current year !pause ' checking time' end if @@ -241,12 +241,12 @@ end subroutine read_force ! ************************************************************************* ! * private subroutine: find first timestep in any of the forcing files... ! ************************************************************************* - subroutine getFirstTimestep(currentJulday,iFile,iRead,ncid,err,message) + subroutine getFirstTimestep(currentJulDay,iFile,iRead,ncid,err,message) USE netcdf ! netcdf capability USE nr_utility_module,only:arth ! get a sequence of numbers implicit none ! define input - real(rkind),intent(in) :: currentJulday ! Julian day of current time step + real(rkind),intent(in) :: currentJulDay ! Julian day of current time step ! define input-output variables integer(i4b),intent(inout) :: iFile ! index of current forcing file in forcing file list integer(i4b),intent(inout) :: iRead ! index of read position in time dimension in current netcdf file @@ -308,11 +308,11 @@ subroutine getFirstTimestep(currentJulday,iFile,iRead,ncid,err,message) if(err/=nf90_noerr)then; message=trim(message)//'trouble reading time vector/'//trim(nf90_strerror(err)); return; endif ! get time vector & convert units based on offset and data step - fileTime = arth(0,1,dimLen) * data_step/secprday + refJulday_data & + fileTime = arth(0,1,dimLen) * data_step/secprday + refJulDay_data & + timeVal(1)/forcFileInfo(iFile)%convTime2Days - ! find difference of fileTime from currentJulday - diffTime=abs(fileTime-currentJulday) + ! find difference of fileTime from currentJulDay + diffTime=abs(fileTime-currentJulDay) ! start time is in the current file if(any(diffTime < verySmall))then @@ -343,7 +343,7 @@ subroutine openForcingFile(iFile,infile,ncId,err,message) USE netcdf_util_module,only:nc_file_open ! open netcdf file USE time_utils_module,only:fracDay ! compute fractional day USE time_utils_module,only:extractTime ! extract time info from units string - USE time_utils_module,only:compJulday ! convert calendar date to julian day + USE time_utils_module,only:compJulDay ! convert calendar date to julian day USE globalData,only:tmZoneOffsetFracDay ! time zone offset in fractional days USE globalData,only:ncTime ! time zone information from NetCDF file (timeOffset = longitude/15. - ncTimeOffset) USE globalData,only:utcTime ! all times in UTC (timeOffset = longitude/15. hours) @@ -397,7 +397,7 @@ subroutine openForcingFile(iFile,infile,ncId,err,message) ! convert the reference time to days since the beginning of time call compjulday(iyyy,im,id,ih,imin,dsec, & ! input = year, month, day, hour, minute, second - refJulday_data,err,cmessage) ! output = julian day (fraction of day) + error control + refJulDay_data,err,cmessage) ! output = julian day (fraction of day) + error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! get the time multiplier needed to convert time to units of days @@ -414,13 +414,13 @@ end subroutine openForcingFile ! ************************************************************************* ! * read the NetCDF forcing data ! ************************************************************************* - subroutine readForcingData(currentJulday,ncId,iFile,iRead,nHRUlocal,time_data,forcStruct,err,message) + subroutine readForcingData(currentJulDay,ncId,iFile,iRead,nHRUlocal,time_data,forcStruct,err,message) USE netcdf ! netcdf capability USE time_utils_module,only:compcalday ! convert julian day to calendar date - USE time_utils_module,only:compJulday ! convert calendar date to julian day + USE time_utils_module,only:compJulDay ! convert calendar date to julian day USE get_ixname_module,only:get_ixforce ! identify index of named variable ! dummy variables - real(rkind),intent(in) :: currentJulday ! Julian day of current time step + real(rkind),intent(in) :: currentJulDay ! Julian day of current time step integer(i4b) ,intent(in) :: ncId ! NetCDF ID integer(i4b) ,intent(in) :: iFile ! index of forcing file integer(i4b) ,intent(in) :: iRead ! index in data file @@ -459,8 +459,8 @@ subroutine readForcingData(currentJulday,ncId,iFile,iRead,nHRUlocal,time_data,fo err = nf90_get_var(ncid,varId,varTime,start=(/iRead/)); if(err/=nf90_noerr)then; message=trim(message)//'trouble reading time variable/'//trim(nf90_strerror(err)); return; endif ! check that the computed julian day matches the time information in the NetCDF file - dataJulDay = varTime(1)/forcFileInfo(iFile)%convTime2Days + refJulday_data - if(abs(currentJulday - dataJulDay) > verySmall)then + dataJulDay = varTime(1)/forcFileInfo(iFile)%convTime2Days + refJulDay_data + if(abs(currentJulDay - dataJulDay) > verySmall)then write(message,'(a,f18.8,a,f18.8)') trim(message)//'date for time step: ',dataJulDay,' differs from the expected date: ',currentJulDay err=40; return end if @@ -561,10 +561,10 @@ end subroutine readForcingData ! ************************************************************************* ! * create forcing time data if using NGEN (uses BMI) ! ************************************************************************* - subroutine createForcingTimeData(currentJulday,time_data,err,message) + subroutine createForcingTimeData(currentJulDay,time_data,err,message) USE time_utils_module,only:compcalday ! convert julian day to calendar date ! dummy variables - real(rkind),intent(in) :: currentJulday ! Julian day of current time step + real(rkind),intent(in) :: currentJulDay ! Julian day of current time step integer(i4b),intent(out) :: time_data(:) ! vector of time data for a given time step integer(i4b) ,intent(out) :: err ! error code character(*) ,intent(out) :: message ! error message @@ -581,7 +581,7 @@ subroutine createForcingTimeData(currentJulday,time_data,err,message) ! convert julian day to time vector ! NOTE: use small offset to force ih=0 at the start of the day - call compcalday(currentJulday+smallOffset, & ! input = julian day + call compcalday(currentJulDay+smallOffset, & ! input = julian day time_data(iLookTIME%iyyy), & ! output = year time_data(iLookTIME%im), & ! output = month time_data(iLookTIME%id), & ! output = day diff --git a/build/source/engine/run_oneHRU.f90 b/build/source/engine/run_oneHRU.f90 index 71b343481..ebf6b9b51 100644 --- a/build/source/engine/run_oneHRU.f90 +++ b/build/source/engine/run_oneHRU.f90 @@ -54,7 +54,7 @@ module run_oneHRU_module USE var_lookup,only:iLookDECISIONS ! look-up values for model decisions ! these are needed because we cannot access them in modules locally if we might use those modules with Actors -USE globalData,only:fracJulday ! fractional julian days since the start of year +USE globalData,only:fracJulDay ! fractional julian days since the start of year USE globalData,only:yearLength ! number of days in the current year USE globalData,only:tmZoneOffsetFracDay ! time zone offset in fractional days diff --git a/build/source/engine/vegPhenlgy.f90 b/build/source/engine/vegPhenlgy.f90 index fe74b8a10..2b96b6f48 100755 --- a/build/source/engine/vegPhenlgy.f90 +++ b/build/source/engine/vegPhenlgy.f90 @@ -97,7 +97,7 @@ subroutine vegPhenlgy(& logical(lgt),intent(out) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) real(rkind),intent(out) :: canopyDepth ! canopy depth (m) real(rkind),intent(out) :: exposedVAI ! exposed vegetation area index (LAI + SAI) - real(rkind),intent(in) :: fracJulday ! fractional julian days since the start of year + real(rkind),intent(in) :: fracJulDay ! fractional julian days since the start of year integer(i4b),intent(in) :: yearLength ! number of days in the current year integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -169,7 +169,7 @@ subroutine vegPhenlgy(& scalarCanopyTemp, & ! intent(in): temperature of the vegetation canopy at the start of the sub-step (K) latitude, & ! intent(in): latitude yearLength, & ! intent(in): number of days in the current year - fracJulday, & ! intent(in): fractional julian days since the start of year + fracJulDay, & ! intent(in): fractional julian days since the start of year scalarLAI, & ! intent(inout): one-sided leaf area index (m2 m-2) scalarSAI, & ! intent(inout): one-sided stem area index (m2 m-2) scalarRootZoneTemp, & ! intent(in): root zone temperature (K) From 52b19e69d998ddef7ef6ee630d9505b917bc268d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 12 May 2023 12:56:40 +0900 Subject: [PATCH 0721/1472] adding back in makefiles folder, a lot of these probably don't work --- build/makefiles/Actors/compile_copernicus.sh | 30 ++ build/makefiles/Actors/compile_graham.sh | 30 ++ build/makefiles/Actors/makefile_apptainer | 420 +++++++++++++++++ build/makefiles/Actors/makefile_cluster | 419 +++++++++++++++++ build/makefiles/Actors/makefile_richardson | 425 ++++++++++++++++++ build/makefiles/BE/makefile_old_summa | 421 +++++++++++++++++ build/makefiles/{ => Ngen}/build_cmakeBMI | 0 build/makefiles/Ngen/compile_copernicus.sh | 17 + build/makefiles/Ngen/compile_mac.sh | 11 + build/makefiles/Ngen/makefile_cluster | 352 +++++++++++++++ build/makefiles/Ngen/makefile_mac | 352 +++++++++++++++ .../{ => Sundials}/build_cmakeSundials | 0 12 files changed, 2477 insertions(+) create mode 100644 build/makefiles/Actors/compile_copernicus.sh create mode 100644 build/makefiles/Actors/compile_graham.sh create mode 100644 build/makefiles/Actors/makefile_apptainer create mode 100644 build/makefiles/Actors/makefile_cluster create mode 100644 build/makefiles/Actors/makefile_richardson create mode 100644 build/makefiles/BE/makefile_old_summa rename build/makefiles/{ => Ngen}/build_cmakeBMI (100%) create mode 100755 build/makefiles/Ngen/compile_copernicus.sh create mode 100755 build/makefiles/Ngen/compile_mac.sh create mode 100644 build/makefiles/Ngen/makefile_cluster create mode 100644 build/makefiles/Ngen/makefile_mac rename build/makefiles/{ => Sundials}/build_cmakeSundials (100%) diff --git a/build/makefiles/Actors/compile_copernicus.sh b/build/makefiles/Actors/compile_copernicus.sh new file mode 100644 index 000000000..defa4b69d --- /dev/null +++ b/build/makefiles/Actors/compile_copernicus.sh @@ -0,0 +1,30 @@ +#! /bin/bash + +#### load modules if using Compute Canada or Copernicus #### +module load gcc/9.3.0 +module load netcdf-fortran +module load openblas +module load caf + + +#### parent directory of the 'build' directory #### +export ROOT_DIR=/globalhome/kck540/HPC/Summa-Projects/Summa-Actors + +export INCLUDES="-I$EBROOTNETCDFMINFORTRAN/include" +export LIBRARIES="-L$EBROOTNETCDFMINFORTRAN/lib64\ + -L$EBROOTOPENBLAS/lib\ + -lnetcdff -lopenblas" + +# INCLUDES FOR Actors Component +export ACTORS_INCLUDES="-I$EBROOTCAF/include\ + -I$EBROOTNETCDFMINFORTRAN/include" + +export ACTORS_LIBRARIES="-L$EBROOTCAF/lib\ + -L$EBROOTCAF/lib64\ + -L$EBROOTNETCDFMINFORTRAN/lib64\ + -L$EBROOTOPENBLAS/lib\ + -L$ROOT_DIR/bin\ + -lcaf_core -lcaf_io -lsumma -lopenblas -lnetcdff" + +make -f ${ROOT_DIR}/build/makefiles/makefile_cluster +export LD_LIBRARY_PATH=${ROOT_DIR}/bin \ No newline at end of file diff --git a/build/makefiles/Actors/compile_graham.sh b/build/makefiles/Actors/compile_graham.sh new file mode 100644 index 000000000..13c2881fc --- /dev/null +++ b/build/makefiles/Actors/compile_graham.sh @@ -0,0 +1,30 @@ +#! /bin/bash + +#### load modules if using Compute Canada or Copernicus #### +module load gcc/9.3.0 +module load netcdf-fortran +module load openblas +module load caf + + +#### parent directory of the 'build' directory #### +# export ROOT_DIR=/home/kklenk/Summa-Projects/Summa-Actors + +# export INCLUDES="-I$EBROOTNETCDFMINFORTRAN/include" +# export LIBRARIES="-L$EBROOTNETCDFMINFORTRAN/lib64\ +# -L$EBROOTOPENBLAS/lib\ +# -lnetcdff -lopenblas" + +# # INCLUDES FOR Actors Component +# export ACTORS_INCLUDES="-I$EBROOTCAF/include\ +# -I$EBROOTNETCDFMINFORTRAN/include" + +# export ACTORS_LIBRARIES="-L$EBROOTCAF/lib\ +# -L$EBROOTCAF/lib64\ +# -L$EBROOTNETCDFMINFORTRAN/lib64\ +# -L$EBROOTOPENBLAS/lib\ +# -L$ROOT_DIR/bin\ +# -lcaf_core -lcaf_io -lsumma -lopenblas -lnetcdff" + +# make -f ${ROOT_DIR}/build/makefiles/makefile_cluster cpp +# export LD_LIBRARY_PATH=${ROOT_DIR}/bin \ No newline at end of file diff --git a/build/makefiles/Actors/makefile_apptainer b/build/makefiles/Actors/makefile_apptainer new file mode 100644 index 000000000..ee3b1ba70 --- /dev/null +++ b/build/makefiles/Actors/makefile_apptainer @@ -0,0 +1,420 @@ +#### parent directory of the 'build' directory #### +ROOT_DIR = /Summa-Actors + +#### Compilers #### +FC = gfortran # Fortran +CC = g++ # C++ + +DIR_SUNDIALS=/usr/local/sundials +INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran +LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod + + +#### Includes AND Libraries #### +INCLUDES = -I/usr/include -I/usr/local/include $(INC_SUNDIALS) +LIBRARIES = -L/usr/lib -L/usr/local/lib -lnetcdff -lopenblas $(LIB_SUNDIALS) + +ACTORS_INCLUDES = -I/usr/include -I/usr/local/include $(INC_SUNDIALS) +ACTORS_LIBRARIES = -L/usr/lib -L/usr/local/lib -L/Summa-Actors/bin -lcaf_core -lcaf_io -lsumma -lopenblas -lnetcdff $(LIB_SUNDIALS) + + +# Production runs +FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_COMM = -g -O0 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_SUMMA = -g -O0 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_ACTORS = -g -O0 -Wfatal-errors -std=c++17 + +# Debug runs +# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC +# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC +# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC +# FLAGS_ACTORS = -g -O0 -Wall -std=c++17 + + + +#======================================================================== +# PART 1: Define directory paths +#======================================================================== + +# Core directory that contains source code +F_KORE_DIR = $(ROOT_DIR)/build/source + +# Location of the compiled modules +MOD_PATH = $(ROOT_DIR)/build + +# Define the directory for the executables +EXE_PATH = $(ROOT_DIR)/bin + +#################################################################################################### +###################################### Assemble Fortran Files ###################################### +#################################################################################################### +# Define directories +DRIVER_DIR = $(F_KORE_DIR)/driver +HOOKUP_DIR = $(F_KORE_DIR)/hookup +NETCDF_DIR = $(F_KORE_DIR)/netcdf +DSHARE_DIR = $(F_KORE_DIR)/dshare +NUMREC_DIR = $(F_KORE_DIR)/numrec +NOAHMP_DIR = $(F_KORE_DIR)/noah-mp +ENGINE_DIR = $(F_KORE_DIR)/engine +ACTORS_DIR = $(F_KORE_DIR)/actors +JOB_ACTOR_DIR = $(ACTORS_DIR)/job_actor +FILE_ACCESS_DIR = $(ACTORS_DIR)/file_access_actor/fortran_code +HRU_ACTOR_DIR = $(ACTORS_DIR)/hru_actor/fortran_code +GRU_ACTOR_DIR = $(ACTORS_DIR)/gru_actor + +# utilities +SUMMA_NRUTIL= \ + nrtype.f90 \ + f2008funcs.f90 \ + nr_utility.f90 \ + +NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) + +# Numerical recipes procedures +# NOTE: all numerical recipes procedures are now replaced with free versions +SUMMA_NRPROC= \ + expIntegral.f90 \ + spline_int.f90 +NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) + +# Hook-up modules (set files and directory paths) +SUMMA_HOOKUP= \ + ascii_util.f90 \ + summaActors_FileManager.f90 +HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) + +# Data modules +SUMMA_DATAMS= \ + multiconst.f90 \ + var_lookup.f90 \ + data_types.f90 \ + globalData.f90 \ + flxMapping.f90 \ + get_ixname.f90 +DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) + +SUMMA_DEPEND_ON_FILEMANAGER= \ + popMetadat.f90 \ + outpt_stat.f90 +DEPEND_ON_FILEMANAGER = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DEPEND_ON_FILEMANAGER)) + +# utility modules +SUMMA_UTILMS= \ + time_utils.f90 \ + mDecisions.f90 \ + snow_utils.f90 \ + soil_utils.f90 \ + sundials/soil_utilsAddSundials.f90 \ + updatState.f90 \ + sundials/updatStateSundials.f90 \ + matrixOper.f90 +UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) + +# Model guts +SUMMA_MODGUT= \ + MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) + +# Solver +SUMMA_SOLVER= \ + vegPhenlgy.f90 \ + diagn_evar.f90 \ + stomResist.f90 \ + groundwatr.f90 \ + vegSWavRad.f90 \ + vegNrgFlux.f90 \ + ssdNrgFlux.f90 \ + vegLiqFlux.f90 \ + snowLiqFlx.f90 \ + soilLiqFlx.f90 \ + bigAquifer.f90 \ + computFlux.f90 \ + computResid.f90 \ + computJacob.f90 \ + eval8summa.f90 \ + summaSolve.f90 \ + systemSolv.f90 \ + sundials/type4IDA.f90 \ + sundials/tol4IDA.f90 \ + sundials/computEnthalpy.f90 \ + sundials/computHeatCap.f90 \ + sundials/computThermConduct.f90 \ + sundials/computResidSundials.f90 \ + sundials/eval8summaSundials.f90 \ + sundials/computJacobSundials.f90 \ + sundials/computSnowDepth.f90 \ + sundials/summaSolveSundialsIDA.f90 \ + sundials/systemSolvSundials.f90 \ + varSubstep.f90 \ + sundials/varSubstepSundials.f90 \ + opSplittin.f90 \ + coupled_em.f90 + +SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) + +# Interface code for Fortran-C++ +SUMMA_INTERFACE= \ + cppwrap_datatypes.f90 \ + cppwrap_auxiliary.f90 \ + cppwrap_metadata.f90 \ + +INTERFACE = $(patsubst %, $(ACTORS_DIR)/global/%, $(SUMMA_INTERFACE)) + + +SUMMA_FILEACCESS_INTERFACE = \ + cppwrap_fileAccess.f90 \ + read_attribute.f90 \ + read_forcing.f90 \ + read_param.f90 \ + write_to_netcdf.f90 + +FILEACCESS_INTERFACE = $(patsubst %, $(FILE_ACCESS_DIR)/%, $(SUMMA_FILEACCESS_INTERFACE)) + +SUMMA_JOB_INTERFACE = \ + job_actor.f90 + +JOB_INTERFACE = $(patsubst %, $(JOB_ACTOR_DIR)/%, $(SUMMA_JOB_INTERFACE)) + +SUMMA_HRU_INTERFACE = \ + cppwrap_hru.f90 \ + hru_actor.f90 \ + init_hru_actor.f90 \ + + +HRU_INTERFACE = $(patsubst %, $(HRU_ACTOR_DIR)/%, $(SUMMA_HRU_INTERFACE)) + +SUMMA_GRU_INTERFACE = \ + gru_actor.f90 \ + +GRU_INTERFACE = $(patsubst %, $(GRU_ACTOR_DIR)/%, $(SUMMA_GRU_INTERFACE)) + + + +# Define routines for SUMMA preliminaries +SUMMA_PRELIM= \ + conv_funcs.f90 \ + sunGeomtry.f90 \ + convE2Temp.f90 \ + allocspaceActors.f90 \ + checkStruc.f90 \ + childStruc.f90 \ + ffile_info.f90 \ + read_dimension.f90 \ + read_pinit.f90 \ + pOverwrite.f90 \ + paramCheck.f90 \ + check_icondActors.f90 \ + # allocspace.f90 +PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) + +SUMMA_NOAHMP= \ + module_model_constants.F \ + module_sf_noahutl.F \ + module_sf_noahlsm.F \ + module_sf_noahmplsm.F + +NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) + +# Define routines for the SUMMA model runs +# sundials - t2enthalpy.f90 +SUMMA_MODRUN = \ + indexState.f90 \ + getVectorz.f90 \ + sundials/t2enthalpy.f90 \ + updateVars.f90 \ + sundials/updateVarsSundials.f90 \ + var_derive.f90 \ + derivforce.f90 \ + snowAlbedo.f90 \ + canopySnow.f90 \ + tempAdjust.f90 \ + snwCompact.f90 \ + layerMerge.f90 \ + layerDivide.f90 \ + volicePack.f90 \ + qTimeDelay.f90 +MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) + +# Define NetCDF routines +# OutputStrucWrite is not a netcdf subroutine and should be +# moved +SUMMA_NETCDF = \ + netcdf_util.f90 \ + def_output.f90 \ + writeOutput.f90 \ + read_icondActors.f90 +NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) + +# ... stitch together common programs +COMM_ALL = $(NRUTIL) $(NRPROC) $(DATAMS) $(INTERFACE) $(HOOKUP) $(DEPEND_ON_FILEMANAGER) $(UTILMS) + +# ... stitch together SUMMA programs +SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) + +# Define the driver routine +SUMMA_DRIVER= \ + summaActors_type.f90 \ + summaActors_util.f90 \ + summaActors_globalData.f90 \ + SummaActors_setup.f90 \ + summaActors_restart.f90 \ + SummaActors_modelRun.f90 \ + summaActors_alarms.f90 + + +DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) + +#################################################################################################### +###################################### Assemble Fortran Files ###################################### +#################################################################################################### + +#################################################################################################### +######################################## Assemble C++ Files ######################################## +#################################################################################################### + +INCLUDE_DIR = /Summa-Actors/build/includes +SOURCE_DIR = /Summa-Actors/build/source/actors + + +GLOBAL_INCLUDES = -I$(INCLUDE_DIR)/global +GLOBAL = $(SOURCE_DIR)/global/global.cpp +TIMEINFO = $(SOURCE_DIR)/global/timing_info.cpp +SETTINGS_FILES = $(SOURCE_DIR)/global/settings_functions.cpp +AUXILARY = $(SOURCE_DIR)/global/auxiliary.cpp + +SUMMA_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/summa_actor +SUMMA_ACTOR = $(SOURCE_DIR)/summa_actor/summa_actor.cpp +SUMMA_CLIENT = $(SOURCE_DIR)/summa_actor/summa_client.cpp +SUMMA_SERVER = $(SOURCE_DIR)/summa_actor/summa_server.cpp +SUMMA_BACKUP_SERVER = $(SOURCE_DIR)/summa_actor/summa_backup_server.cpp + +BATCH = $(SOURCE_DIR)/summa_actor/batch/batch.cpp +BATCH_CONTAINER = $(SOURCE_DIR)/summa_actor/batch/batch_container.cpp + +CLIENT = $(SOURCE_DIR)/summa_actor/client/client.cpp +CLIENT_CONTAINER = $(SOURCE_DIR)/summa_actor/client/client_container.cpp + +CLIENT_BATCH = $(SOURCE_DIR)/summa_actor/batch_client.cpp +CLIENT_BATCH_CONTAINERS = $(SOURCE_DIR)/summa_actor/batch_client_containers.cpp + +GRU_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/gru_actor +GRU_ACTOR = $(SOURCE_DIR)/gru_actor/gru_actor.cpp + +JOB_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/job_actor +JOB_ACTOR = $(SOURCE_DIR)/job_actor/job_actor.cpp +GRUinfo = $(SOURCE_DIR)/job_actor/GRUinfo.cpp + +FILE_ACCESS_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/file_access_actor +FILE_ACCESS_ACTOR = $(SOURCE_DIR)/file_access_actor/cpp_code/file_access_actor.cpp +FORCING_FILE_INFO = $(SOURCE_DIR)/file_access_actor/cpp_code/forcing_file_info.cpp +OUTPUT_MANAGER = $(SOURCE_DIR)/file_access_actor/cpp_code/output_manager.cpp + +HRU_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/hru_actor +HRU_ACTOR = $(SOURCE_DIR)/hru_actor/cpp_code/hru_actor.cpp + +MAIN = $(F_KORE_DIR)/actors/main.cpp + +ACTOR_TEST = $(F_KORE_DIR)/testing/testing_main.cc +#################################################################################################### +######################################## Assemble C++ Files ######################################## +#################################################################################################### + + +#======================================================================== +# PART 3: compilation +#====================================================================== +all: fortran cpp + +fortran: compile_noah compile_comm compile_summa link clean_fortran + +cpp: compile_globals compile_hru_actor compile_gru_actor compile_file_access_actor compile_job_actor compile_summa_actor \ + compile_summa_client compile_summa_server compile_main link_cpp clean_cpp + +test: actors_test actors_testLink actorsClean + +################################################################################################################### +############################################## COMPILE SUMMA-Fortran ############################################## +################################################################################################################### +compile_noah: + $(FC) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) + +# compile common routines +compile_comm: + $(FC) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) + +# compile SUMMA routines +compile_summa: + $(FC) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(JOB_INTERFACE) $(FILEACCESS_INTERFACE) $(HRU_INTERFACE) $(GRU_INTERFACE) $(INCLUDES) + +# generate library +link: + $(FC) -shared *.o -o libsumma.so + mv libsumma.so $(ROOT_DIR)/bin + +# Remove object files +clean_fortran: + rm -f *.o *.mod soil_veg_gen_parm__genmod.f90 +################################################################################################################### +############################################## COMPILE SUMMA-Fortran ############################################## +################################################################################################################### + + +################################################################################################################### +################################################ COMPILE SUMMA-C++ ################################################ +################################################################################################################### +compile_globals: + $(CC) $(FLAGS_ACTORS) -c $(GLOBAL) $(TIMEINFO) $(AUXILARY) $(SETTINGS_FILES) $(GLOBAL_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_gru_actor: + $(CC) $(FLAGS_ACTORS) -c $(GRU_ACTOR) $(HRU_ACTOR_INCLUDES) $(GRU_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_hru_actor: + $(CC) $(FLAGS_ACTORS) -c $(HRU_ACTOR) $(HRU_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_file_access_actor: + $(CC) $(FLAGS_ACTORS) -c $(FILE_ACCESS_ACTOR) $(FORCING_FILE_INFO) $(OUTPUT_MANAGER) \ + $(FILE_ACCESS_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_job_actor: + $(CC) $(FLAGS_ACTORS) -c $(JOB_ACTOR) $(GRUinfo) $(JOB_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) \ + $(FILE_ACCESS_ACTOR_INCLUDES) $(HRU_ACTOR_INCLUDES) $(GRU_ACTOR_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_summa_actor: + $(CC) $(FLAGS_ACTORS) -c $(SUMMA_ACTOR) $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) \ + $(JOB_ACTOR_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_summa_client: + $(CC) $(FLAGS_ACTORS) -c $(SUMMA_CLIENT) $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) + +compile_summa_server: + $(CC) $(FLAGS_ACTORS) -c $(SUMMA_SERVER) $(SUMMA_BACKUP_SERVER) $(BATCH) $(CLIENT) $(BATCH_CONTAINER) $(CLIENT_CONTAINER) \ + $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) + +compile_main: + $(CC) $(FLAGS_ACTORS) -c $(MAIN) $(GLOBAL_INCLUDES) $(SUMMA_ACTOR_INCLUDES) $(JOB_ACTOR_INCLUDES) + +link_cpp: + $(CC) $(FLAGS_ACTORS) -Wl,-rpath='/Summa-Actors/bin:/usr/local/lib:/code/sundials/instdir/lib' -o summaMain *.o $(ACTORS_LIBRARIES) + mv summaMain $(ROOT_DIR)/bin + +clean_cpp: + rm *.o +################################################################################################################### +################################################ COMPILE SUMMA-C++ ################################################ +################################################################################################################### + + +################################################################################################################### +################################################## COMPILE TESTS ################################################## +################################################################################################################### +actors_test: + $(CC) $(FLAGS_ACTORS) -c $(ACTOR_TEST) -std=c++17 $(ACTORS_INCLUDES) + +actors_testLink: + $(CC) -o summaTest *.o $(ACTORS_LIBRARIES) + +clean_lib: + rm *.so + + + + diff --git a/build/makefiles/Actors/makefile_cluster b/build/makefiles/Actors/makefile_cluster new file mode 100644 index 000000000..18e025f3e --- /dev/null +++ b/build/makefiles/Actors/makefile_cluster @@ -0,0 +1,419 @@ +#### parent directory of the 'build' directory #### +# ROOT_DIR = + +#### Compilers #### +FC = gfortran # Fortran +CC = g++ # C++ + +#### Includes AND Libraries #### +# INCLUDES = +# LIBRARIES = + +# ACTORS_INCLUDES = +# ACTORS_LIBRARIES = + + +# Production runs +FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_ACTORS = -O3 -Wfatal-errors -std=c++17 + +# Debug runs +# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC +# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC +# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC +# FLAGS_ACTORS = -g -O0 -Wall -std=c++17 + +#======================================================================== +# PART 1: Define directory paths +#======================================================================== + +# Core directory that contains source code +F_KORE_DIR = $(ROOT_DIR)/build/source + +# Location of the compiled modules +MOD_PATH = $(ROOT_DIR)/build + +# Define the directory for the executables +EXE_PATH = $(ROOT_DIR)/bin + +#################################################################################################### +###################################### Assemble Fortran Files ###################################### +#################################################################################################### +# Define directories +DRIVER_DIR = $(F_KORE_DIR)/driver +HOOKUP_DIR = $(F_KORE_DIR)/hookup +NETCDF_DIR = $(F_KORE_DIR)/netcdf +DSHARE_DIR = $(F_KORE_DIR)/dshare +NUMREC_DIR = $(F_KORE_DIR)/numrec +NOAHMP_DIR = $(F_KORE_DIR)/noah-mp +ENGINE_DIR = $(F_KORE_DIR)/engine +ACTORS_DIR = $(F_KORE_DIR)/actors + +SUMMA_DSHARE_DIR = $(MOD_PATH)/summa/build/source/dshare +SUMMA_ENGINE_DIR = $(MOD_PATH)/summa/build/source/engine + + +JOB_ACTOR_DIR = $(ACTORS_DIR)/job_actor +FILE_ACCESS_DIR = $(ACTORS_DIR)/file_access_actor/fortran_code +HRU_ACTOR_DIR = $(ACTORS_DIR)/hru_actor/fortran_code +GRU_ACTOR_DIR = $(ACTORS_DIR)/gru_actor + +# utilities +SUMMA_NRUTIL= \ + nrtype.f90 \ + f2008funcs.f90 \ + nr_utility.f90 \ + +NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) + +# Numerical recipes procedures +# NOTE: all numerical recipes procedures are now replaced with free versions +SUMMA_NRPROC= \ + expIntegral.f90 \ + spline_int.f90 +NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) + +# Hook-up modules (set files and directory paths) +SUMMA_HOOKUP= \ + ascii_util.f90 \ + summaActors_FileManager.f90 +HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) + +# Data modules +SUMMA_DATAMS= \ + var_lookup.f90 \ + multiconst.f90 \ + data_types.f90 \ + globalData.f90 \ + flxMapping.f90 +DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) + +IXNAME_METADAT = \ + get_ixname.f90 \ + popMetadat.f90 +IXNAME_METADAT_DATAMS = $(patsubst %, $(SUMMA_DSHARE_DIR)/%, $(IXNAME_METADAT)) + +SUMMA_DEPEND_ON_FILEMANAGER= \ + outpt_stat.f90 +DEPEND_ON_FILEMANAGER = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DEPEND_ON_FILEMANAGER)) + + +# utility modules +SUMMA_UTILMS= \ + time_utils.f90 \ + mDecisions.f90 \ + snow_utils.f90 \ + soil_utils.f90 \ + updatState.f90 \ + matrixOper.f90 +UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) + +# Model guts +SUMMA_MODGUT= \ + MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) + + + +# Solver - Split up to compiling with old summa files vs new summa files +VEG_PHENLGY= \ + vegPhenlgy.f90 +VEG_PHENLGY_SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(VEG_PHENLGY)) +SNOW_DEPTH= \ + sundials/computSnowDepth.f90 +SNOW_DEPTH_SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SNOW_DEPTH)) +SUMMA_SOLVER= \ + diagn_evar.f90 \ + stomResist.f90 \ + groundwatr.f90 \ + vegSWavRad.f90 \ + vegNrgFlux.f90 \ + ssdNrgFlux.f90 \ + vegLiqFlux.f90 \ + snowLiqFlx.f90 \ + soilLiqFlx.f90 \ + bigAquifer.f90 \ + computFlux.f90 \ + computResid.f90 \ + computJacob.f90 \ + eval8summa.f90 \ + summaSolve.f90 \ + systemSolv.f90 \ + varSubstep.f90 \ + opSplittin.f90 +SOLVER = $(patsubst %, $(SUMMA_ENGINE_DIR)/%, $(SUMMA_SOLVER)) +SUMMA_ACTORS_SOLVER = \ + coupled_em.f90 +COUPLED_EM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_ACTORS_SOLVER)) + + + +# Interface code for Fortran-C++ +SUMMA_INTERFACE= \ + cppwrap_datatypes.f90 \ + cppwrap_auxiliary.f90 \ + cppwrap_metadata.f90 \ + +INTERFACE = $(patsubst %, $(ACTORS_DIR)/global/%, $(SUMMA_INTERFACE)) + + +SUMMA_FILEACCESS_INTERFACE = \ + output_structure.f90 \ + cppwrap_fileAccess.f90 \ + read_attribute.f90 \ + read_forcing.f90 \ + read_param.f90 \ + read_initcond.f90 \ + writeOutputFromOutputStructure.f90 \ + write_to_netcdf.f90 + +FILEACCESS_INTERFACE = $(patsubst %, $(FILE_ACCESS_DIR)/%, $(SUMMA_FILEACCESS_INTERFACE)) + +SUMMA_JOB_INTERFACE = \ + job_actor.f90 + +JOB_INTERFACE = $(patsubst %, $(JOB_ACTOR_DIR)/%, $(SUMMA_JOB_INTERFACE)) + +SUMMA_HRU_INTERFACE = \ + cppwrap_hru.f90 \ + hru_actor.f90 \ + init_hru_actor.f90 \ + outputStrucWrite.f90 \ + hru_writeOutput.f90 + + +HRU_INTERFACE = $(patsubst %, $(HRU_ACTOR_DIR)/%, $(SUMMA_HRU_INTERFACE)) + +SUMMA_GRU_INTERFACE = \ + gru_actor.f90 \ + +GRU_INTERFACE = $(patsubst %, $(GRU_ACTOR_DIR)/%, $(SUMMA_GRU_INTERFACE)) + + + +# Define routines for SUMMA preliminaries +SUMMA_PRELIM= \ + conv_funcs.f90 \ + sunGeomtry.f90 \ + convE2Temp.f90 \ + allocspaceActors.f90 \ + alloc_fileAccess.f90 \ + checkStruc.f90 \ + childStruc.f90 \ + ffile_info.f90 \ + read_dimension.f90 \ + read_pinit.f90 \ + pOverwrite.f90 \ + paramCheck.f90 \ + check_icondActors.f90 +PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) + +SUMMA_NOAHMP= \ + module_model_constants.F \ + module_sf_noahutl.F \ + module_sf_noahlsm.F \ + module_sf_noahmplsm.F + +NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) + +# Define routines for the SUMMA model runs +DERIV_FORCE = \ + derivforce.f90 \ + sundials/t2enthalpy.f90 +DERIV_FORCE_MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(DERIV_FORCE)) + +SUMMA_MODRUN = \ + indexState.f90 \ + getVectorz.f90 \ + updateVars.f90 \ + var_derive.f90 \ + snowAlbedo.f90 \ + canopySnow.f90 \ + tempAdjust.f90 \ + snwCompact.f90 \ + layerMerge.f90 \ + layerDivide.f90 \ + volicePack.f90 \ + qTimeDelay.f90 +MODRUN = $(patsubst %, $(SUMMA_ENGINE_DIR)/%, $(SUMMA_MODRUN)) + +# Define NetCDF routines +# OutputStrucWrite is not a netcdf subroutine and should be +# moved +SUMMA_NETCDF = \ + netcdf_util.f90 \ + def_output.f90 \ + writeOutput.f90 \ + read_icondActors.f90 +NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) + +# ... stitch together common programs +COMM_ALL = $(NRUTIL) $(NRPROC) $(DATAMS) \ + $(INTERFACE) $(HOOKUP) $(IXNAME_METADAT_DATAMS) $(DEPEND_ON_FILEMANAGER) \ + $(TIME_UTILS_UTILMS) $(M_DECISIONS_UTILMS) $(UTILMS) +# ... stitch together SUMMA programs +SUMMA_ALL = $(NETCDF) $(PRELIM) $(DERIV_FORCE_MODRUN) $(MODRUN) \ + $(VEG_PHENLGY_SOLVER) $(SOLVER) $(SNOW_DEPTH_SOLVER) $(COUPLED_EM) + +# Define the driver routine +SUMMA_DRIVER= \ + summaActors_type.f90 \ + summaActors_util.f90 \ + summaActors_globalData.f90 \ + SummaActors_setup.f90 \ + summaActors_restart.f90 \ + SummaActors_modelRun.f90 \ + summaActors_alarms.f90 + + +DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) + +#################################################################################################### +###################################### Assemble Fortran Files ###################################### +#################################################################################################### + +#################################################################################################### +######################################## Assemble C++ Files ######################################## +#################################################################################################### + +INCLUDE_DIR = $(ROOT_DIR)/build/includes +SOURCE_DIR = $(ROOT_DIR)/build/source/actors + + +GLOBAL_INCLUDES = -I$(INCLUDE_DIR)/global +GLOBAL = $(SOURCE_DIR)/global/global.cpp +TIMEINFO = $(SOURCE_DIR)/global/timing_info.cpp +SETTINGS_FILES = $(SOURCE_DIR)/global/settings_functions.cpp +AUXILARY = $(SOURCE_DIR)/global/auxiliary.cpp + +SUMMA_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/summa_actor +SUMMA_ACTOR = $(SOURCE_DIR)/summa_actor/summa_actor.cpp +SUMMA_CLIENT = $(SOURCE_DIR)/summa_actor/summa_client.cpp +SUMMA_SERVER = $(SOURCE_DIR)/summa_actor/summa_server.cpp +SUMMA_BACKUP_SERVER = $(SOURCE_DIR)/summa_actor/summa_backup_server.cpp + +BATCH = $(SOURCE_DIR)/summa_actor/batch/batch.cpp +BATCH_CONTAINER = $(SOURCE_DIR)/summa_actor/batch/batch_container.cpp + +CLIENT = $(SOURCE_DIR)/summa_actor/client/client.cpp +CLIENT_CONTAINER = $(SOURCE_DIR)/summa_actor/client/client_container.cpp + +GRU_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/gru_actor +GRU_ACTOR = $(SOURCE_DIR)/gru_actor/gru_actor.cpp + +JOB_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/job_actor +JOB_ACTOR = $(SOURCE_DIR)/job_actor/job_actor.cpp +GRUinfo = $(SOURCE_DIR)/job_actor/GRUinfo.cpp + +FILE_ACCESS_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/file_access_actor +FILE_ACCESS_ACTOR = $(SOURCE_DIR)/file_access_actor/cpp_code/file_access_actor.cpp +FORCING_FILE_INFO = $(SOURCE_DIR)/file_access_actor/cpp_code/forcing_file_info.cpp +OUTPUT_CONTAINER = $(SOURCE_DIR)/file_access_actor/cpp_code/output_container.cpp + +HRU_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/hru_actor +HRU_ACTOR = $(SOURCE_DIR)/hru_actor/cpp_code/hru_actor.cpp + +MAIN = $(F_KORE_DIR)/actors/main.cpp + +ACTOR_TEST = $(F_KORE_DIR)/testing/testing_main.cc +#################################################################################################### +######################################## Assemble C++ Files ######################################## +#################################################################################################### + + +#======================================================================== +# PART 3: compilation +#====================================================================== +all: fortran cpp + +fortran: compile_noah compile_comm compile_summa link clean_fortran + +cpp: compile_globals compile_hru_actor compile_gru_actor compile_file_access_actor compile_job_actor compile_summa_actor \ + compile_summa_client compile_summa_server compile_main link_cpp clean_cpp + +test: actors_test actors_testLink actorsClean + +################################################################################################################### +############################################## COMPILE SUMMA-Fortran ############################################## +################################################################################################################### +compile_noah: + $(FC) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) + +# compile common routines +compile_comm: + $(FC) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) + +# compile SUMMA routines +compile_summa: + $(FC) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(JOB_INTERFACE) $(FILEACCESS_INTERFACE) $(HRU_INTERFACE) $(GRU_INTERFACE) $(INCLUDES) + +# generate library +link: + $(FC) -shared *.o -o libsumma.so + mv libsumma.so $(ROOT_DIR)/bin + +# Remove object files +clean_fortran: + rm -f *.o *.mod soil_veg_gen_parm__genmod.f90 +################################################################################################################### +############################################## COMPILE SUMMA-Fortran ############################################## +################################################################################################################### + + +################################################################################################################### +################################################ COMPILE SUMMA-C++ ################################################ +################################################################################################################### +compile_globals: + $(CC) $(FLAGS_ACTORS) -c $(GLOBAL) $(TIMEINFO) $(AUXILARY) $(SETTINGS_FILES) $(GLOBAL_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_gru_actor: + $(CC) $(FLAGS_ACTORS) -c $(GRU_ACTOR) $(HRU_ACTOR_INCLUDES) $(GRU_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_hru_actor: + $(CC) $(FLAGS_ACTORS) -c $(HRU_ACTOR) $(HRU_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_file_access_actor: + $(CC) $(FLAGS_ACTORS) -c $(OUTPUT_CONTAINER) $(FILE_ACCESS_ACTOR) $(FORCING_FILE_INFO) $(OUTPUT_MANAGER) \ + $(FILE_ACCESS_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_job_actor: + $(CC) $(FLAGS_ACTORS) -c $(JOB_ACTOR) $(GRUinfo) $(JOB_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) \ + $(FILE_ACCESS_ACTOR_INCLUDES) $(HRU_ACTOR_INCLUDES) $(GRU_ACTOR_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_summa_actor: + $(CC) $(FLAGS_ACTORS) -c $(SUMMA_ACTOR) $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) \ + $(JOB_ACTOR_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_summa_client: + $(CC) $(FLAGS_ACTORS) -c $(SUMMA_CLIENT) $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) + +compile_summa_server: + $(CC) $(FLAGS_ACTORS) -c $(SUMMA_SERVER) $(SUMMA_BACKUP_SERVER) $(BATCH) $(CLIENT) $(BATCH_CONTAINER) $(CLIENT_CONTAINER) \ + $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) + +compile_main: + $(CC) $(FLAGS_ACTORS) -c $(MAIN) $(GLOBAL_INCLUDES) $(SUMMA_ACTOR_INCLUDES) $(JOB_ACTOR_INCLUDES) + +link_cpp: + $(CC) $(FLAGS_ACTORS) -Wl,-rpath='$(ROOT_DIR)/bin:/usr/local/lib' -o summaMain *.o $(ACTORS_LIBRARIES) + mv summaMain $(ROOT_DIR)/bin + +clean_cpp: + rm *.o +################################################################################################################### +################################################ COMPILE SUMMA-C++ ################################################ +################################################################################################################### + + +################################################################################################################### +################################################## COMPILE TESTS ################################################## +################################################################################################################### +actors_test: + $(CC) $(FLAGS_ACTORS) -c $(ACTOR_TEST) -std=c++17 $(ACTORS_INCLUDES) + +actors_testLink: + $(CC) -o summaTest *.o $(ACTORS_LIBRARIES) + +clean_lib: + rm *.so \ No newline at end of file diff --git a/build/makefiles/Actors/makefile_richardson b/build/makefiles/Actors/makefile_richardson new file mode 100644 index 000000000..92853c168 --- /dev/null +++ b/build/makefiles/Actors/makefile_richardson @@ -0,0 +1,425 @@ +#### parent directory of the 'build' directory #### +ROOT_DIR = /u1/kck540/Summa-Projects/Summa-Actors-Main + +#### Compilers #### +FC = gfortran # Fortran +CC = g++ # C++ + +#### Includes AND Libraries #### +INCLUDES = -I/usr/include -I/usr/local/include +LIBRARIES = -L/usr/lib -L/usr/local/lib -lnetcdff -lopenblas + +ACTORS_INCLUDES = -I/usr/include -I/usr/local/include +ACTORS_LIBRARIES = -L/usr/lib -L/usr/local/lib -L$(ROOT_DIR)/bin -lcaf_core -lcaf_io -lsumma -lopenblas -lnetcdff + + +# Production runs +FLAGS_NOAH = -g -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_COMM = -g -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_SUMMA = -g -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_ACTORS = -g -O3 -Wfatal-errors -std=c++17 + +# Debug runs +# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC +# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC +# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC +# FLAGS_ACTORS = -g -O0 -Wall -std=c++17 + + + +#======================================================================== +# PART 1: Define directory paths +#======================================================================== + +# Core directory that contains source code +F_KORE_DIR = $(ROOT_DIR)/build/source + +# Location of the compiled modules +MOD_PATH = $(ROOT_DIR)/build + +# Define the directory for the executables +EXE_PATH = $(ROOT_DIR)/bin + +#################################################################################################### +###################################### Assemble Fortran Files ###################################### +#################################################################################################### +# Define directories +DRIVER_DIR = $(F_KORE_DIR)/driver +HOOKUP_DIR = $(F_KORE_DIR)/hookup +NETCDF_DIR = $(F_KORE_DIR)/netcdf +DSHARE_DIR = $(F_KORE_DIR)/dshare +NUMREC_DIR = $(F_KORE_DIR)/numrec +NOAHMP_DIR = $(F_KORE_DIR)/noah-mp +ENGINE_DIR = $(F_KORE_DIR)/engine +ACTORS_DIR = $(F_KORE_DIR)/actors + +SUMMA_DSHARE_DIR = $(MOD_PATH)/summa/build/source/dshare +SUMMA_ENGINE_DIR = $(MOD_PATH)/summa/build/source/engine + + +JOB_ACTOR_DIR = $(ACTORS_DIR)/job_actor +FILE_ACCESS_DIR = $(ACTORS_DIR)/file_access_actor/fortran_code +HRU_ACTOR_DIR = $(ACTORS_DIR)/hru_actor/fortran_code +GRU_ACTOR_DIR = $(ACTORS_DIR)/gru_actor + +# utilities +SUMMA_NRUTIL= \ + nrtype.f90 \ + f2008funcs.f90 \ + nr_utility.f90 \ + +NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) + +# Numerical recipes procedures +# NOTE: all numerical recipes procedures are now replaced with free versions +SUMMA_NRPROC= \ + expIntegral.f90 \ + spline_int.f90 +NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) + +# Hook-up modules (set files and directory paths) +SUMMA_HOOKUP= \ + ascii_util.f90 \ + summaActors_FileManager.f90 +HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) + +# Data modules +SUMMA_DATAMS= \ + var_lookup.f90 \ + multiconst.f90 \ + data_types.f90 \ + globalData.f90 \ + flxMapping.f90 +DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) + +IXNAME_METADAT = \ + get_ixname.f90 \ + popMetadat.f90 +IXNAME_METADAT_DATAMS = $(patsubst %, $(SUMMA_DSHARE_DIR)/%, $(IXNAME_METADAT)) + +SUMMA_DEPEND_ON_FILEMANAGER= \ + outpt_stat.f90 +DEPEND_ON_FILEMANAGER = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DEPEND_ON_FILEMANAGER)) + + +# utility modules +SUMMA_UTILMS= \ + time_utils.f90 \ + mDecisions.f90 \ + snow_utils.f90 \ + soil_utils.f90 \ + updatState.f90 \ + matrixOper.f90 +UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) + +# Model guts +SUMMA_MODGUT= \ + MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) + + + +# Solver - Split up to compiling with old summa files vs new summa files +VEG_PHENLGY= \ + vegPhenlgy.f90 +VEG_PHENLGY_SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(VEG_PHENLGY)) +SNOW_DEPTH= \ + sundials/computSnowDepth.f90 +SNOW_DEPTH_SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SNOW_DEPTH)) +SUMMA_SOLVER= \ + diagn_evar.f90 \ + stomResist.f90 \ + groundwatr.f90 \ + vegSWavRad.f90 \ + vegNrgFlux.f90 \ + ssdNrgFlux.f90 \ + vegLiqFlux.f90 \ + snowLiqFlx.f90 \ + soilLiqFlx.f90 \ + bigAquifer.f90 \ + computFlux.f90 \ + computResid.f90 \ + computJacob.f90 \ + eval8summa.f90 \ + summaSolve.f90 \ + systemSolv.f90 \ + varSubstep.f90 \ + opSplittin.f90 +SOLVER = $(patsubst %, $(SUMMA_ENGINE_DIR)/%, $(SUMMA_SOLVER)) +SUMMA_ACTORS_SOLVER = \ + coupled_em.f90 +COUPLED_EM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_ACTORS_SOLVER)) + + + +# Interface code for Fortran-C++ +SUMMA_INTERFACE= \ + cppwrap_datatypes.f90 \ + cppwrap_auxiliary.f90 \ + cppwrap_metadata.f90 \ + +INTERFACE = $(patsubst %, $(ACTORS_DIR)/global/%, $(SUMMA_INTERFACE)) + + +SUMMA_FILEACCESS_INTERFACE = \ + output_structure.f90 \ + cppwrap_fileAccess.f90 \ + read_attribute.f90 \ + read_forcing.f90 \ + read_param.f90 \ + read_initcond.f90 \ + writeOutputFromOutputStructure.f90 \ + write_to_netcdf.f90 + +FILEACCESS_INTERFACE = $(patsubst %, $(FILE_ACCESS_DIR)/%, $(SUMMA_FILEACCESS_INTERFACE)) + +SUMMA_JOB_INTERFACE = \ + job_actor.f90 + +JOB_INTERFACE = $(patsubst %, $(JOB_ACTOR_DIR)/%, $(SUMMA_JOB_INTERFACE)) + +SUMMA_HRU_INTERFACE = \ + cppwrap_hru.f90 \ + hru_actor.f90 \ + init_hru_actor.f90 \ + outputStrucWrite.f90 \ + hru_writeOutput.f90 + + +HRU_INTERFACE = $(patsubst %, $(HRU_ACTOR_DIR)/%, $(SUMMA_HRU_INTERFACE)) + +SUMMA_GRU_INTERFACE = \ + gru_actor.f90 \ + +GRU_INTERFACE = $(patsubst %, $(GRU_ACTOR_DIR)/%, $(SUMMA_GRU_INTERFACE)) + + + +# Define routines for SUMMA preliminaries +SUMMA_PRELIM= \ + conv_funcs.f90 \ + sunGeomtry.f90 \ + convE2Temp.f90 \ + allocspaceActors.f90 \ + alloc_fileAccess.f90 \ + checkStruc.f90 \ + childStruc.f90 \ + ffile_info.f90 \ + read_dimension.f90 \ + read_pinit.f90 \ + pOverwrite.f90 \ + paramCheck.f90 \ + check_icondActors.f90 +PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) + +SUMMA_NOAHMP= \ + module_model_constants.F \ + module_sf_noahutl.F \ + module_sf_noahlsm.F \ + module_sf_noahmplsm.F + +NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) + +# Define routines for the SUMMA model runs +DERIV_FORCE = \ + derivforce.f90 \ + sundials/t2enthalpy.f90 +DERIV_FORCE_MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(DERIV_FORCE)) + +SUMMA_MODRUN = \ + indexState.f90 \ + getVectorz.f90 \ + updateVars.f90 \ + var_derive.f90 \ + snowAlbedo.f90 \ + canopySnow.f90 \ + tempAdjust.f90 \ + snwCompact.f90 \ + layerMerge.f90 \ + layerDivide.f90 \ + volicePack.f90 \ + qTimeDelay.f90 +MODRUN = $(patsubst %, $(SUMMA_ENGINE_DIR)/%, $(SUMMA_MODRUN)) + +# Define NetCDF routines +# OutputStrucWrite is not a netcdf subroutine and should be +# moved +SUMMA_NETCDF = \ + netcdf_util.f90 \ + def_output.f90 \ + writeOutput.f90 \ + read_icondActors.f90 +NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) + +# ... stitch together common programs +COMM_ALL = $(NRUTIL) $(NRPROC) $(DATAMS) \ + $(INTERFACE) $(HOOKUP) $(IXNAME_METADAT_DATAMS) $(DEPEND_ON_FILEMANAGER) \ + $(TIME_UTILS_UTILMS) $(M_DECISIONS_UTILMS) $(UTILMS) +# ... stitch together SUMMA programs +SUMMA_ALL = $(NETCDF) $(PRELIM) $(DERIV_FORCE_MODRUN) $(MODRUN) \ + $(VEG_PHENLGY_SOLVER) $(SOLVER) $(SNOW_DEPTH_SOLVER) $(COUPLED_EM) + +# Define the driver routine +SUMMA_DRIVER= \ + summaActors_type.f90 \ + summaActors_util.f90 \ + summaActors_globalData.f90 \ + SummaActors_setup.f90 \ + summaActors_restart.f90 \ + SummaActors_modelRun.f90 \ + summaActors_alarms.f90 + + +DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) + +#################################################################################################### +###################################### Assemble Fortran Files ###################################### +#################################################################################################### + +#################################################################################################### +######################################## Assemble C++ Files ######################################## +#################################################################################################### + +INCLUDE_DIR = $(ROOT_DIR)/build/includes +SOURCE_DIR = $(ROOT_DIR)/build/source/actors + + +GLOBAL_INCLUDES = -I$(INCLUDE_DIR)/global +GLOBAL = $(SOURCE_DIR)/global/global.cpp +TIMEINFO = $(SOURCE_DIR)/global/timing_info.cpp +SETTINGS_FILES = $(SOURCE_DIR)/global/settings_functions.cpp +AUXILARY = $(SOURCE_DIR)/global/auxiliary.cpp + +SUMMA_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/summa_actor +SUMMA_ACTOR = $(SOURCE_DIR)/summa_actor/summa_actor.cpp +SUMMA_CLIENT = $(SOURCE_DIR)/summa_actor/summa_client.cpp +SUMMA_SERVER = $(SOURCE_DIR)/summa_actor/summa_server.cpp +SUMMA_BACKUP_SERVER = $(SOURCE_DIR)/summa_actor/summa_backup_server.cpp + +BATCH = $(SOURCE_DIR)/summa_actor/batch/batch.cpp +BATCH_CONTAINER = $(SOURCE_DIR)/summa_actor/batch/batch_container.cpp + +CLIENT = $(SOURCE_DIR)/summa_actor/client/client.cpp +CLIENT_CONTAINER = $(SOURCE_DIR)/summa_actor/client/client_container.cpp + +GRU_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/gru_actor +GRU_ACTOR = $(SOURCE_DIR)/gru_actor/gru_actor.cpp + +JOB_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/job_actor +JOB_ACTOR = $(SOURCE_DIR)/job_actor/job_actor.cpp +GRUinfo = $(SOURCE_DIR)/job_actor/GRUinfo.cpp + +FILE_ACCESS_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/file_access_actor +FILE_ACCESS_ACTOR = $(SOURCE_DIR)/file_access_actor/cpp_code/file_access_actor.cpp +FORCING_FILE_INFO = $(SOURCE_DIR)/file_access_actor/cpp_code/forcing_file_info.cpp +OUTPUT_CONTAINER = $(SOURCE_DIR)/file_access_actor/cpp_code/output_container.cpp + +HRU_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/hru_actor +HRU_ACTOR = $(SOURCE_DIR)/hru_actor/cpp_code/hru_actor.cpp + +MAIN = $(F_KORE_DIR)/actors/main.cpp + +ACTOR_TEST = $(F_KORE_DIR)/testing/testing_main.cc +#################################################################################################### +######################################## Assemble C++ Files ######################################## +#################################################################################################### + + +#======================================================================== +# PART 3: compilation +#====================================================================== +all: fortran cpp + +fortran: compile_noah compile_comm compile_summa link clean_fortran + +cpp: compile_globals compile_hru_actor compile_gru_actor compile_file_access_actor compile_job_actor compile_summa_actor \ + compile_summa_client compile_summa_server compile_main link_cpp clean_cpp + +test: actors_test actors_testLink actorsClean + +################################################################################################################### +############################################## COMPILE SUMMA-Fortran ############################################## +################################################################################################################### +compile_noah: + $(FC) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) + +# compile common routines +compile_comm: + $(FC) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) + +# compile SUMMA routines +compile_summa: + $(FC) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(JOB_INTERFACE) $(FILEACCESS_INTERFACE) $(HRU_INTERFACE) $(GRU_INTERFACE) $(INCLUDES) + +# generate library +link: + $(FC) -shared *.o -o libsumma.so + mv libsumma.so $(ROOT_DIR)/bin + +# Remove object files +clean_fortran: + rm -f *.o *.mod soil_veg_gen_parm__genmod.f90 +################################################################################################################### +############################################## COMPILE SUMMA-Fortran ############################################## +################################################################################################################### + + +################################################################################################################### +################################################ COMPILE SUMMA-C++ ################################################ +################################################################################################################### +compile_globals: + $(CC) $(FLAGS_ACTORS) -c $(GLOBAL) $(TIMEINFO) $(AUXILARY) $(SETTINGS_FILES) $(GLOBAL_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_gru_actor: + $(CC) $(FLAGS_ACTORS) -c $(GRU_ACTOR) $(HRU_ACTOR_INCLUDES) $(GRU_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_hru_actor: + $(CC) $(FLAGS_ACTORS) -c $(HRU_ACTOR) $(HRU_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_file_access_actor: + $(CC) $(FLAGS_ACTORS) -c $(OUTPUT_CONTAINER) $(FILE_ACCESS_ACTOR) $(FORCING_FILE_INFO) $(OUTPUT_MANAGER) \ + $(FILE_ACCESS_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_job_actor: + $(CC) $(FLAGS_ACTORS) -c $(JOB_ACTOR) $(GRUinfo) $(JOB_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) \ + $(FILE_ACCESS_ACTOR_INCLUDES) $(HRU_ACTOR_INCLUDES) $(GRU_ACTOR_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_summa_actor: + $(CC) $(FLAGS_ACTORS) -c $(SUMMA_ACTOR) $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) \ + $(JOB_ACTOR_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_summa_client: + $(CC) $(FLAGS_ACTORS) -c $(SUMMA_CLIENT) $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) + +compile_summa_server: + $(CC) $(FLAGS_ACTORS) -c $(SUMMA_SERVER) $(SUMMA_BACKUP_SERVER) $(BATCH) $(CLIENT) $(BATCH_CONTAINER) $(CLIENT_CONTAINER) \ + $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) + +compile_main: + $(CC) $(FLAGS_ACTORS) -c $(MAIN) $(GLOBAL_INCLUDES) $(SUMMA_ACTOR_INCLUDES) $(JOB_ACTOR_INCLUDES) + +link_cpp: + $(CC) $(FLAGS_ACTORS) -Wl,-rpath='$(ROOT_DIR)/bin:/usr/local/lib' -o summaMain *.o $(ACTORS_LIBRARIES) + mv summaMain $(ROOT_DIR)/bin + +clean_cpp: + rm *.o +################################################################################################################### +################################################ COMPILE SUMMA-C++ ################################################ +################################################################################################################### + + +################################################################################################################### +################################################## COMPILE TESTS ################################################## +################################################################################################################### +actors_test: + $(CC) $(FLAGS_ACTORS) -c $(ACTOR_TEST) -std=c++17 $(ACTORS_INCLUDES) + +actors_testLink: + $(CC) -o summaTest *.o $(ACTORS_LIBRARIES) + +clean_lib: + rm *.so + + + + diff --git a/build/makefiles/BE/makefile_old_summa b/build/makefiles/BE/makefile_old_summa new file mode 100644 index 000000000..74aafdddf --- /dev/null +++ b/build/makefiles/BE/makefile_old_summa @@ -0,0 +1,421 @@ +#### parent directory of the 'build' directory #### +ROOT_DIR = /Summa-Actors + +#### Compilers #### +FC = gfortran # Fortran +CC = g++ # C++ + +#### Includes AND Libraries #### +INCLUDES = -I/usr/include -I/usr/local/include +LIBRARIES = -L/usr/lib -L/usr/local/lib -lnetcdff -lopenblas + +ACTORS_INCLUDES = -I/usr/include -I/usr/local/include +ACTORS_LIBRARIES = -L/usr/lib -L/usr/local/lib -L/Summa-Actors/bin -lcaf_core -lcaf_io -lsumma -lopenblas -lnetcdff + +# Production runs +FLAGS_NOAH = -g -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_COMM = -g -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_SUMMA = -g -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_ACTORS = -g -O3 -Wfatal-errors -std=c++17 + +# Debug runs +# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC +# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC +# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC +# FLAGS_ACTORS = -g -O0 -Wall -std=c++17 + + + +#======================================================================== +# PART 1: Define directory paths +#======================================================================== + +# Core directory that contains source code +F_KORE_DIR = $(ROOT_DIR)/build/source + +# Location of the compiled modules +MOD_PATH = $(ROOT_DIR)/build + +# Define the directory for the executables +EXE_PATH = $(ROOT_DIR)/bin + +#################################################################################################### +###################################### Assemble Fortran Files ###################################### +#################################################################################################### +# Define directories +DRIVER_DIR = $(F_KORE_DIR)/driver +HOOKUP_DIR = $(F_KORE_DIR)/hookup +NETCDF_DIR = $(F_KORE_DIR)/netcdf +DSHARE_DIR = $(F_KORE_DIR)/dshare +NUMREC_DIR = $(F_KORE_DIR)/numrec +NOAHMP_DIR = $(F_KORE_DIR)/noah-mp +ENGINE_DIR = $(F_KORE_DIR)/engine +ACTORS_DIR = $(F_KORE_DIR)/actors + +SUMMA_DSHARE_DIR = $(MOD_PATH)/summa/build/source/dshare +SUMMA_ENGINE_DIR = $(MOD_PATH)/summa/build/source/engine + + +JOB_ACTOR_DIR = $(ACTORS_DIR)/job_actor +FILE_ACCESS_DIR = $(ACTORS_DIR)/file_access_actor/fortran_code +HRU_ACTOR_DIR = $(ACTORS_DIR)/hru_actor/fortran_code +GRU_ACTOR_DIR = $(ACTORS_DIR)/gru_actor + +# utilities +SUMMA_NRUTIL= \ + nrtype.f90 \ + f2008funcs.f90 \ + nr_utility.f90 \ + +NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) + +# Numerical recipes procedures +# NOTE: all numerical recipes procedures are now replaced with free versions +SUMMA_NRPROC= \ + expIntegral.f90 \ + spline_int.f90 +NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) + +# Hook-up modules (set files and directory paths) +SUMMA_HOOKUP= \ + ascii_util.f90 \ + summaActors_FileManager.f90 +HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) + +# Data modules +SUMMA_DATAMS= \ + var_lookup.f90 \ + multiconst.f90 \ + data_types.f90 \ + globalData.f90 \ + flxMapping.f90 +DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) + +IXNAME_METADAT = \ + get_ixname.f90 \ + popMetadat.f90 +IXNAME_METADAT_DATAMS = $(patsubst %, $(SUMMA_DSHARE_DIR)/%, $(IXNAME_METADAT)) + +SUMMA_DEPEND_ON_FILEMANAGER= \ + outpt_stat.f90 +DEPEND_ON_FILEMANAGER = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DEPEND_ON_FILEMANAGER)) + + +# utility modules +SUMMA_UTILMS= \ + time_utils.f90 \ + mDecisions.f90 \ + snow_utils.f90 \ + soil_utils.f90 \ + updatState.f90 \ + matrixOper.f90 +UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) + +# Model guts +SUMMA_MODGUT= \ + MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) + + + +# Solver - Split up to compiling with old summa files vs new summa files +VEG_PHENLGY= \ + vegPhenlgy.f90 +VEG_PHENLGY_SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(VEG_PHENLGY)) +SNOW_DEPTH= \ + sundials/computSnowDepth.f90 +SNOW_DEPTH_SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SNOW_DEPTH)) +SUMMA_SOLVER= \ + diagn_evar.f90 \ + stomResist.f90 \ + groundwatr.f90 \ + vegSWavRad.f90 \ + vegNrgFlux.f90 \ + ssdNrgFlux.f90 \ + vegLiqFlux.f90 \ + snowLiqFlx.f90 \ + soilLiqFlx.f90 \ + bigAquifer.f90 \ + computFlux.f90 \ + computResid.f90 \ + computJacob.f90 \ + eval8summa.f90 \ + summaSolve.f90 \ + systemSolv.f90 \ + varSubstep.f90 \ + opSplittin.f90 +SOLVER = $(patsubst %, $(SUMMA_ENGINE_DIR)/%, $(SUMMA_SOLVER)) +SUMMA_ACTORS_SOLVER = \ + coupled_em.f90 +COUPLED_EM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_ACTORS_SOLVER)) + + + +# Interface code for Fortran-C++ +SUMMA_INTERFACE= \ + cppwrap_datatypes.f90 \ + cppwrap_auxiliary.f90 \ + cppwrap_metadata.f90 \ + +INTERFACE = $(patsubst %, $(ACTORS_DIR)/global/%, $(SUMMA_INTERFACE)) + + +SUMMA_FILEACCESS_INTERFACE = \ + output_structure.f90 \ + cppwrap_fileAccess.f90 \ + read_attribute.f90 \ + read_forcing.f90 \ + read_param.f90 \ + read_initcond.f90 \ + writeOutputFromOutputStructure.f90 \ + write_to_netcdf.f90 + +FILEACCESS_INTERFACE = $(patsubst %, $(FILE_ACCESS_DIR)/%, $(SUMMA_FILEACCESS_INTERFACE)) + +SUMMA_JOB_INTERFACE = \ + job_actor.f90 + +JOB_INTERFACE = $(patsubst %, $(JOB_ACTOR_DIR)/%, $(SUMMA_JOB_INTERFACE)) + +SUMMA_HRU_INTERFACE = \ + cppwrap_hru.f90 \ + hru_actor.f90 \ + init_hru_actor.f90 \ + outputStrucWrite.f90 \ + hru_writeOutput.f90 + + +HRU_INTERFACE = $(patsubst %, $(HRU_ACTOR_DIR)/%, $(SUMMA_HRU_INTERFACE)) + +SUMMA_GRU_INTERFACE = \ + gru_actor.f90 \ + +GRU_INTERFACE = $(patsubst %, $(GRU_ACTOR_DIR)/%, $(SUMMA_GRU_INTERFACE)) + + + +# Define routines for SUMMA preliminaries +SUMMA_PRELIM= \ + conv_funcs.f90 \ + sunGeomtry.f90 \ + convE2Temp.f90 \ + allocspaceActors.f90 \ + alloc_fileAccess.f90 \ + checkStruc.f90 \ + childStruc.f90 \ + ffile_info.f90 \ + read_dimension.f90 \ + read_pinit.f90 \ + pOverwrite.f90 \ + paramCheck.f90 \ + check_icondActors.f90 +PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) + +SUMMA_NOAHMP= \ + module_model_constants.F \ + module_sf_noahutl.F \ + module_sf_noahlsm.F \ + module_sf_noahmplsm.F + +NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) + +# Define routines for the SUMMA model runs +DERIV_FORCE = \ + derivforce.f90 \ + sundials/t2enthalpy.f90 +DERIV_FORCE_MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(DERIV_FORCE)) + +SUMMA_MODRUN = \ + indexState.f90 \ + getVectorz.f90 \ + updateVars.f90 \ + var_derive.f90 \ + snowAlbedo.f90 \ + canopySnow.f90 \ + tempAdjust.f90 \ + snwCompact.f90 \ + layerMerge.f90 \ + layerDivide.f90 \ + volicePack.f90 \ + qTimeDelay.f90 +MODRUN = $(patsubst %, $(SUMMA_ENGINE_DIR)/%, $(SUMMA_MODRUN)) + +# Define NetCDF routines +# OutputStrucWrite is not a netcdf subroutine and should be +# moved +SUMMA_NETCDF = \ + netcdf_util.f90 \ + def_output.f90 \ + writeOutput.f90 \ + read_icondActors.f90 +NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) + +# ... stitch together common programs +COMM_ALL = $(NRUTIL) $(NRPROC) $(DATAMS) \ + $(INTERFACE) $(HOOKUP) $(IXNAME_METADAT_DATAMS) $(DEPEND_ON_FILEMANAGER) \ + $(TIME_UTILS_UTILMS) $(M_DECISIONS_UTILMS) $(UTILMS) +# ... stitch together SUMMA programs +SUMMA_ALL = $(NETCDF) $(PRELIM) $(DERIV_FORCE_MODRUN) $(MODRUN) \ + $(VEG_PHENLGY_SOLVER) $(SOLVER) $(SNOW_DEPTH_SOLVER) $(COUPLED_EM) + +# Define the driver routine +SUMMA_DRIVER= \ + summaActors_type.f90 \ + summaActors_util.f90 \ + summaActors_globalData.f90 \ + SummaActors_setup.f90 \ + summaActors_restart.f90 \ + SummaActors_modelRun.f90 \ + summaActors_alarms.f90 + + +DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) + +#################################################################################################### +###################################### Assemble Fortran Files ###################################### +#################################################################################################### + +#################################################################################################### +######################################## Assemble C++ Files ######################################## +#################################################################################################### + +INCLUDE_DIR = $(ROOT_DIR)/build/includes +SOURCE_DIR = $(ROOT_DIR)/build/source/actors + + +GLOBAL_INCLUDES = -I$(INCLUDE_DIR)/global +GLOBAL = $(SOURCE_DIR)/global/global.cpp +TIMEINFO = $(SOURCE_DIR)/global/timing_info.cpp +SETTINGS_FILES = $(SOURCE_DIR)/global/settings_functions.cpp +AUXILARY = $(SOURCE_DIR)/global/auxiliary.cpp + +SUMMA_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/summa_actor +SUMMA_ACTOR = $(SOURCE_DIR)/summa_actor/summa_actor.cpp +SUMMA_CLIENT = $(SOURCE_DIR)/summa_actor/summa_client.cpp +SUMMA_SERVER = $(SOURCE_DIR)/summa_actor/summa_server.cpp +SUMMA_BACKUP_SERVER = $(SOURCE_DIR)/summa_actor/summa_backup_server.cpp + +BATCH = $(SOURCE_DIR)/summa_actor/batch/batch.cpp +BATCH_CONTAINER = $(SOURCE_DIR)/summa_actor/batch/batch_container.cpp + +CLIENT = $(SOURCE_DIR)/summa_actor/client/client.cpp +CLIENT_CONTAINER = $(SOURCE_DIR)/summa_actor/client/client_container.cpp + +GRU_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/gru_actor +GRU_ACTOR = $(SOURCE_DIR)/gru_actor/gru_actor.cpp + +JOB_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/job_actor +JOB_ACTOR = $(SOURCE_DIR)/job_actor/job_actor.cpp +GRUinfo = $(SOURCE_DIR)/job_actor/GRUinfo.cpp + +FILE_ACCESS_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/file_access_actor +FILE_ACCESS_ACTOR = $(SOURCE_DIR)/file_access_actor/cpp_code/file_access_actor.cpp +FORCING_FILE_INFO = $(SOURCE_DIR)/file_access_actor/cpp_code/forcing_file_info.cpp +OUTPUT_CONTAINER = $(SOURCE_DIR)/file_access_actor/cpp_code/output_container.cpp + +HRU_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/hru_actor +HRU_ACTOR = $(SOURCE_DIR)/hru_actor/cpp_code/hru_actor.cpp + +MAIN = $(F_KORE_DIR)/actors/main.cpp + +ACTOR_TEST = $(F_KORE_DIR)/testing/testing_main.cc +#################################################################################################### +######################################## Assemble C++ Files ######################################## +#################################################################################################### + + +#======================================================================== +# PART 3: compilation +#====================================================================== +all: fortran cpp + +fortran: compile_noah compile_comm compile_summa link clean_fortran + +cpp: compile_globals compile_hru_actor compile_gru_actor compile_file_access_actor compile_job_actor compile_summa_actor \ + compile_summa_client compile_summa_server compile_main link_cpp clean_cpp + +test: actors_test actors_testLink actorsClean + +################################################################################################################### +############################################## COMPILE SUMMA-Fortran ############################################## +################################################################################################################### +compile_noah: + $(FC) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) + +# compile common routines +compile_comm: + $(FC) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) + +# compile SUMMA routines +compile_summa: + $(FC) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(JOB_INTERFACE) $(FILEACCESS_INTERFACE) $(HRU_INTERFACE) $(GRU_INTERFACE) $(INCLUDES) + +# generate library +link: + $(FC) -shared *.o -o libsumma.so + mv libsumma.so $(ROOT_DIR)/bin + +# Remove object files +clean_fortran: + rm -f *.o *.mod soil_veg_gen_parm__genmod.f90 +################################################################################################################### +############################################## COMPILE SUMMA-Fortran ############################################## +################################################################################################################### + + +################################################################################################################### +################################################ COMPILE SUMMA-C++ ################################################ +################################################################################################################### +compile_globals: + $(CC) $(FLAGS_ACTORS) -c $(GLOBAL) $(TIMEINFO) $(AUXILARY) $(SETTINGS_FILES) $(GLOBAL_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_gru_actor: + $(CC) $(FLAGS_ACTORS) -c $(GRU_ACTOR) $(HRU_ACTOR_INCLUDES) $(GRU_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_hru_actor: + $(CC) $(FLAGS_ACTORS) -c $(HRU_ACTOR) $(HRU_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_file_access_actor: + $(CC) $(FLAGS_ACTORS) -c $(OUTPUT_CONTAINER) $(FILE_ACCESS_ACTOR) $(FORCING_FILE_INFO) $(OUTPUT_MANAGER) \ + $(FILE_ACCESS_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_job_actor: + $(CC) $(FLAGS_ACTORS) -c $(JOB_ACTOR) $(GRUinfo) $(JOB_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) \ + $(FILE_ACCESS_ACTOR_INCLUDES) $(HRU_ACTOR_INCLUDES) $(GRU_ACTOR_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_summa_actor: + $(CC) $(FLAGS_ACTORS) -c $(SUMMA_ACTOR) $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) \ + $(JOB_ACTOR_INCLUDES) $(SUMMA_ACTOR_INCLUDES) + +compile_summa_client: + $(CC) $(FLAGS_ACTORS) -c $(SUMMA_CLIENT) $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) + +compile_summa_server: + $(CC) $(FLAGS_ACTORS) -c $(SUMMA_SERVER) $(SUMMA_BACKUP_SERVER) $(BATCH) $(CLIENT) $(BATCH_CONTAINER) $(CLIENT_CONTAINER) \ + $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) + +compile_main: + $(CC) $(FLAGS_ACTORS) -c $(MAIN) $(GLOBAL_INCLUDES) $(SUMMA_ACTOR_INCLUDES) $(JOB_ACTOR_INCLUDES) + +link_cpp: + $(CC) $(FLAGS_ACTORS) -Wl,-rpath='$(ROOT_DIR)/bin:/usr/local/lib' -o summaMain *.o $(ACTORS_LIBRARIES) + mv summaMain $(ROOT_DIR)/bin + +clean_cpp: + rm *.o +################################################################################################################### +################################################ COMPILE SUMMA-C++ ################################################ +################################################################################################################### + + +################################################################################################################### +################################################## COMPILE TESTS ################################################## +################################################################################################################### +actors_test: + $(CC) $(FLAGS_ACTORS) -c $(ACTOR_TEST) -std=c++17 $(ACTORS_INCLUDES) + +actors_testLink: + $(CC) -o summaTest *.o $(ACTORS_LIBRARIES) + +clean_lib: + rm *.so + diff --git a/build/makefiles/build_cmakeBMI b/build/makefiles/Ngen/build_cmakeBMI similarity index 100% rename from build/makefiles/build_cmakeBMI rename to build/makefiles/Ngen/build_cmakeBMI diff --git a/build/makefiles/Ngen/compile_copernicus.sh b/build/makefiles/Ngen/compile_copernicus.sh new file mode 100755 index 000000000..f818b6ced --- /dev/null +++ b/build/makefiles/Ngen/compile_copernicus.sh @@ -0,0 +1,17 @@ +#! /bin/bash + +#### load modules if using Compute Canada or Copernicus #### +module load StdEnv/2020 +module load gcc/9.3.0 +module load openblas/0.3.17 +module load netcdf-fortran/4.5.2 + +#### parent directory of the 'build' directory #### +export ROOT_DIR=/project/gwf/gwf_cmt/ngen/ngen_code/ngen/extern/summa + +export INCLUDES="-I${EBROOTNETCDFMINFORTRAN}/include" +export LDFLAGS="-L${EBROOTNETCDFMINFORTRAN}/lib64 -L${EBROOTOPENBLAS}/lib" +export LIBRARIES="${LDFLAGS} -lnetcdff -lopenblas" + +make -f ${ROOT_DIR}/summa/build/makefiles/makefile_cluster +export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin diff --git a/build/makefiles/Ngen/compile_mac.sh b/build/makefiles/Ngen/compile_mac.sh new file mode 100755 index 000000000..4777dd2a3 --- /dev/null +++ b/build/makefiles/Ngen/compile_mac.sh @@ -0,0 +1,11 @@ +#! /bin/bash + +#### parent directory of the 'build' directory #### +export ROOT_DIR=/Users/amedin/Research/SummaBMI/ngen/extern/summa + +export INCLUDES="-I/opt/local/include -I/opt/local/lib" +export LDFLAGS="-L/opt/local/lib" +export LIBRARIES="${LDFLAGS} -llapack -lgfortran -lnetcdff -lnetcdf" + +make -f ${ROOT_DIR}/summa/build/makefiles/makefile_mac +export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin diff --git a/build/makefiles/Ngen/makefile_cluster b/build/makefiles/Ngen/makefile_cluster new file mode 100644 index 000000000..0e88ddf12 --- /dev/null +++ b/build/makefiles/Ngen/makefile_cluster @@ -0,0 +1,352 @@ +# Define core directory below which everything resides. This is the +# parent directory of the 'build' directory +# ROOT_DIR = +F_MASTER = $(ROOT_DIR)/summa + +#### Compilers #### +FC = gfortran +FC_EXE = gfortran + +#### Includes AND Libraries #### +# INCLUDES = +# LDFLAGS = +# LIBRARIES = + +DIR_SUNDIALS= $(ROOT_DIR)/sundials/instdir +INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran +LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod + +DIR_BMI= $(ROOT_DIR)/bmi/instdir +INC_BMI=-I$(DIR_BMI)/include +LIB_BMI=-L$(DIR_BMI)/lib -lbmif + +# Production runs +FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors + +# Debug runs +# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC +# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC +# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC + +#======================================================================== +# PART 1: Define directory paths +#======================================================================== + +# Core directory that contains source code +F_KORE_DIR = $(F_MASTER)/build/source + +# Location of the compiled modules +MOD_PATH = $(F_MASTER)/build + +# Define the directory for the executables +EXE_PATH = $(F_MASTER)/bin + +#======================================================================== +# PART 2: Assemble all of the SUMMA sub-routines +#======================================================================== + +# Define directories +DRIVER_DIR = $(F_KORE_DIR)/driver +HOOKUP_DIR = $(F_KORE_DIR)/hookup +NETCDF_DIR = $(F_KORE_DIR)/netcdf +DSHARE_DIR = $(F_KORE_DIR)/dshare +NUMREC_DIR = $(F_KORE_DIR)/numrec +NOAHMP_DIR = $(F_KORE_DIR)/noah-mp +ENGINE_DIR = $(F_KORE_DIR)/engine + +# utilities +SUMMA_NRUTIL= \ + nrtype.f90 \ + f2008funcs.f90 \ + nr_utility.f90 +NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) + +# +# Numerical recipes procedures +# NOTE: all numerical recipes procedures are now replaced with free versions +SUMMA_NRPROC= \ + expIntegral.f90 \ + spline_int.f90 +NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) + +# Hook-up modules (set files and directory paths) +SUMMA_HOOKUP= \ + ascii_util.f90 \ + summaFileManager.f90 + +HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) + +# Data modules +SUMMA_DATAMS= \ + multiconst.f90 \ + var_lookup.f90 \ + data_types.f90 \ + globalData.f90 \ + flxMapping.f90 \ + get_ixname.f90 \ + popMetadat.f90 \ + outpt_stat.f90 +DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) + +# utility modules +SUMMA_UTILMS= \ + time_utils.f90 \ + mDecisions.f90 \ + snow_utils.f90 \ + soil_utils.f90 \ + soil_utilsAddSundials.f90 \ + updatState.f90 \ + updatStateSundials.f90 \ + matrixOper.f90 +UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) + +# Model guts +SUMMA_MODGUT= \ + MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) + +# Solver +SUMMA_SOLVER= \ + vegPhenlgy.f90 \ + diagn_evar.f90 \ + stomResist.f90 \ + groundwatr.f90 \ + vegSWavRad.f90 \ + vegNrgFlux.f90 \ + ssdNrgFlux.f90 \ + vegLiqFlux.f90 \ + snowLiqFlx.f90 \ + soilLiqFlx.f90 \ + bigAquifer.f90 \ + computFlux.f90 \ + type4IDA.f90 \ + tol4IDA.f90 \ + computEnthalpy.f90 \ + computHeatCap.f90 \ + computThermConduct.f90 \ + computResid.f90 \ + computJacob.f90 \ + eval8summa.f90 \ + summaSolve.f90 \ + systemSolv.f90 \ + computResidSundials.f90 \ + eval8summaSundials.f90 \ + computJacobSundials.f90 \ + computSnowDepth.f90 \ + summaSolveSundialsIDA.f90 \ + systemSolvSundials.f90 \ + varSubstep.f90 \ + opSplittin.f90 \ + coupled_em.f90 \ + run_oneHRU.f90 \ + run_oneGRU.f90 +SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) + +# Define routines for SUMMA preliminaries +SUMMA_PRELIM= \ + conv_funcs.f90 \ + sunGeomtry.f90 \ + convE2Temp.f90 \ + allocspace.f90 \ + checkStruc.f90 \ + childStruc.f90 \ + ffile_info.f90 \ + read_attrb.f90 \ + read_pinit.f90 \ + pOverwrite.f90 \ + read_param.f90 \ + paramCheck.f90 \ + check_icond.f90 +PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) + +SUMMA_NOAHMP= \ + module_model_constants.F \ + module_sf_noahutl.F \ + module_sf_noahlsm.F \ + module_sf_noahmplsm.F +NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) + +# Define routines for the SUMMA model runs +SUMMA_MODRUN = \ + indexState.f90 \ + getVectorz.f90 \ + t2enthalpy.f90 \ + updateVars.f90 \ + updateVarsSundials.f90 \ + var_derive.f90 \ + read_force.f90 \ + derivforce.f90 \ + snowAlbedo.f90 \ + canopySnow.f90 \ + tempAdjust.f90 \ + snwCompact.f90 \ + layerMerge.f90 \ + layerDivide.f90 \ + volicePack.f90 \ + qTimeDelay.f90 +MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) + +# Define routines for the solver +SUMMA_MSOLVE = \ + +# Define NetCDF routines +SUMMA_NETCDF = \ + netcdf_util.f90 \ + def_output.f90 \ + modelwrite.f90 \ + read_icond.f90 +NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) + +# ... stitch together common programs +COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) + +# ... stitch together SUMMA programs +SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) + +# Define the driver routines +SUMMA_DRIVER= \ + summa_type.f90 \ + summa_util.f90 \ + summa_alarms.f90 \ + summa_globalData.f90 \ + summa_defineOutput.f90 \ + summa_init.f90 \ + summa_setup.f90 \ + summa_restart.f90 \ + summa_forcing.f90 \ + summa_modelRun.f90 \ + summa_writeOutput.f90 \ + summa_driver.f90 +DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) + +# Define SUMMA running infrastructure +SUMMA_RUN = \ + summa_run.f90 +SUMMA = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_RUN)) + +# Define BMI testing infrastructure +BMI_TEST = \ + summa_runBMI.f90 +BMI = $(patsubst %, $(DRIVER_DIR)/%, $(BMI_TEST)) + +# Define the executable +DRIVER__EX = summa_sundials.exe +BMI__EX = summa_bmi.exe + +# Define version number +VERSIONFILE = $(DRIVER_DIR)/summaversion.inc +VERSION = $(shell git tag | tail -n 1) +BULTTIM = $(shell date) +GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') +GITHASH = $(shell git rev-parse HEAD) + +#======================================================================== +# PART 3: Checks +#====================================================================== +# make sure that the paths are defined. These are just some high level checks +ifndef F_MASTER + $(error F_MASTER is undefined) +endif +ifndef FC + $(error FC is undefined: Specify your compiler) +endif +ifndef FC_EXE + $(error FC_EXE is undefined: Specify your compiler executable) +endif +ifndef FLAGS_SUMMA + $(error Specify flags for your compiler: $(FC)) +endif +ifndef INCLUDES + $(error INCLUDES is undefined) +endif +ifndef LIBRARIES + $(error LIBRARIES is undefined) +endif + +#======================================================================== +# PART 4: compilation +#====================================================================== + +# Compile +all: compile_noah compile_comm compile_rout compile_test link_test clean_test compile_summa link clean install install_test +part: compile_noah compile_comm compile_rout +summa: compile_noah compile_comm compile_rout compile_summa link clean install +bmi: compile_noah compile_comm compile_rout compile_test link_test clean install_test + + +check: + $(info) + $(info Displaying make variables:) + $(info F_MASTER : $(F_MASTER)) + $(info EXE_PATH : $(EXE_PATH)) + $(info FC : $(FC)) + $(info INCLUDES : $(INCLUDES)) + $(info LIBRARIES : $(LIBRARIES)) + $(info FLAGS_NOAH : $(FLAGS_NOAH)) + $(info FLAGS_COMM : $(FLAGS_COMM)) + $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) + $(info SUNDIALSDIR: $(DIR_SUNDIALS)) + $(info INC_SUNDIALS: $(INC_SUNDIALS)) + $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) + $(info BMIDIR: $(DIR_BMI)) + $(info INC_BMI: $(INC_BMI)) + $(info LIB_BMI: $(LIB_BMI)) + $(info) + +# update version information +update_version: + echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) + echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) + +# compile common routines +compile_comm: + $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) + +# compile Noah-MP routines +compile_noah: + $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) + +# compile SUMMA routines +compile_rout: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) + +# compile run scripts +compile_summa: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA) + +# compile test scripts +compile_test: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(BMI) + +# link run routines +link: + $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(DRIVER__EX) + +# link test routines +link_test: + $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(BMI__EX) + +# remove test script objects +clean_test: + rm -f summa_runBMI.o + +# Remove object files +clean: + rm -f *.o + rm -f *.mod + rm -f soil_veg_gen_parm__genmod.f90 + +# Copy the executable to the bin directory +install: + @mkdir -p $(EXE_PATH) + @mv $(DRIVER__EX) $(EXE_PATH) + $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) + +# Copy the executable to the bin directory +install_test: + @mkdir -p $(EXE_PATH) + @mv $(BMI__EX) $(EXE_PATH) + $(info $(BMI__EX) successfully installed in $(EXE_PATH)) diff --git a/build/makefiles/Ngen/makefile_mac b/build/makefiles/Ngen/makefile_mac new file mode 100644 index 000000000..bb4c48732 --- /dev/null +++ b/build/makefiles/Ngen/makefile_mac @@ -0,0 +1,352 @@ +# Define core directory below which everything resides. This is the +# parent directory of the 'build' directory +# ROOT_DIR = +F_MASTER = $(ROOT_DIR)/summa + +#### Compilers #### +FC = gfortran +FC_EXE =/opt/local/bin/gfortran + +#### Includes AND Libraries #### +# INCLUDES = +# LDFLAGS = +# LIBRARIES = + +DIR_SUNDIALS= $(ROOT_DIR)/sundials/instdir +INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran +LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod + +DIR_BMI= $(ROOT_DIR)/bmi/instdir +INC_BMI=-I$(DIR_BMI)/include +LIB_BMI=-L$(DIR_BMI)/lib -lbmif -Xlinker -rpath -Xlinker $(DIR_BMI)/lib + +# Production runs +FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors + +# Debug runs +# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC +# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC +# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC + +#======================================================================== +# PART 1: Define directory paths +#======================================================================== + +# Core directory that contains source code +F_KORE_DIR = $(F_MASTER)/build/source + +# Location of the compiled modules +MOD_PATH = $(F_MASTER)/build + +# Define the directory for the executables +EXE_PATH = $(F_MASTER)/bin + +#======================================================================== +# PART 2: Assemble all of the SUMMA sub-routines +#======================================================================== + +# Define directories +DRIVER_DIR = $(F_KORE_DIR)/driver +HOOKUP_DIR = $(F_KORE_DIR)/hookup +NETCDF_DIR = $(F_KORE_DIR)/netcdf +DSHARE_DIR = $(F_KORE_DIR)/dshare +NUMREC_DIR = $(F_KORE_DIR)/numrec +NOAHMP_DIR = $(F_KORE_DIR)/noah-mp +ENGINE_DIR = $(F_KORE_DIR)/engine + +# utilities +SUMMA_NRUTIL= \ + nrtype.f90 \ + f2008funcs.f90 \ + nr_utility.f90 +NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) + +# +# Numerical recipes procedures +# NOTE: all numerical recipes procedures are now replaced with free versions +SUMMA_NRPROC= \ + expIntegral.f90 \ + spline_int.f90 +NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) + +# Hook-up modules (set files and directory paths) +SUMMA_HOOKUP= \ + ascii_util.f90 \ + summaFileManager.f90 + +HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) + +# Data modules +SUMMA_DATAMS= \ + multiconst.f90 \ + var_lookup.f90 \ + data_types.f90 \ + globalData.f90 \ + flxMapping.f90 \ + get_ixname.f90 \ + popMetadat.f90 \ + outpt_stat.f90 +DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) + +# utility modules +SUMMA_UTILMS= \ + time_utils.f90 \ + mDecisions.f90 \ + snow_utils.f90 \ + soil_utils.f90 \ + soil_utilsAddSundials.f90 \ + updatState.f90 \ + updatStateSundials.f90 \ + matrixOper.f90 +UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) + +# Model guts +SUMMA_MODGUT= \ + MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) + +# Solver +SUMMA_SOLVER= \ + vegPhenlgy.f90 \ + diagn_evar.f90 \ + stomResist.f90 \ + groundwatr.f90 \ + vegSWavRad.f90 \ + vegNrgFlux.f90 \ + ssdNrgFlux.f90 \ + vegLiqFlux.f90 \ + snowLiqFlx.f90 \ + soilLiqFlx.f90 \ + bigAquifer.f90 \ + computFlux.f90 \ + type4IDA.f90 \ + tol4IDA.f90 \ + computEnthalpy.f90 \ + computHeatCap.f90 \ + computThermConduct.f90 \ + computResid.f90 \ + computJacob.f90 \ + eval8summa.f90 \ + summaSolve.f90 \ + systemSolv.f90 \ + computResidSundials.f90 \ + eval8summaSundials.f90 \ + computJacobSundials.f90 \ + computSnowDepth.f90 \ + summaSolveSundialsIDA.f90 \ + systemSolvSundials.f90 \ + varSubstep.f90 \ + opSplittin.f90 \ + coupled_em.f90 \ + run_oneHRU.f90 \ + run_oneGRU.f90 +SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) + +# Define routines for SUMMA preliminaries +SUMMA_PRELIM= \ + conv_funcs.f90 \ + sunGeomtry.f90 \ + convE2Temp.f90 \ + allocspace.f90 \ + checkStruc.f90 \ + childStruc.f90 \ + ffile_info.f90 \ + read_attrb.f90 \ + read_pinit.f90 \ + pOverwrite.f90 \ + read_param.f90 \ + paramCheck.f90 \ + check_icond.f90 +PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) + +SUMMA_NOAHMP= \ + module_model_constants.F \ + module_sf_noahutl.F \ + module_sf_noahlsm.F \ + module_sf_noahmplsm.F +NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) + +# Define routines for the SUMMA model runs +SUMMA_MODRUN = \ + indexState.f90 \ + getVectorz.f90 \ + t2enthalpy.f90 \ + updateVars.f90 \ + updateVarsSundials.f90 \ + var_derive.f90 \ + read_force.f90 \ + derivforce.f90 \ + snowAlbedo.f90 \ + canopySnow.f90 \ + tempAdjust.f90 \ + snwCompact.f90 \ + layerMerge.f90 \ + layerDivide.f90 \ + volicePack.f90 \ + qTimeDelay.f90 +MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) + +# Define routines for the solver +SUMMA_MSOLVE = \ + +# Define NetCDF routines +SUMMA_NETCDF = \ + netcdf_util.f90 \ + def_output.f90 \ + modelwrite.f90 \ + read_icond.f90 +NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) + +# ... stitch together common programs +COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) + +# ... stitch together SUMMA programs +SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) + +# Define the driver routines +SUMMA_DRIVER= \ + summa_type.f90 \ + summa_util.f90 \ + summa_alarms.f90 \ + summa_globalData.f90 \ + summa_defineOutput.f90 \ + summa_init.f90 \ + summa_setup.f90 \ + summa_restart.f90 \ + summa_forcing.f90 \ + summa_modelRun.f90 \ + summa_writeOutput.f90 \ + summa_driver.f90 +DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) + +# Define SUMMA running infrastructure +SUMMA_RUN = \ + summa_run.f90 +SUMMA = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_RUN)) + +# Define BMI testing infrastructure +BMI_TEST = \ + summa_runBMI.f90 +BMI = $(patsubst %, $(DRIVER_DIR)/%, $(BMI_TEST)) + +# Define the executable +DRIVER__EX = summa_sundials.exe +BMI__EX = summa_bmi.exe + +# Define version number +VERSIONFILE = $(DRIVER_DIR)/summaversion.inc +VERSION = $(shell git tag | tail -n 1) +BULTTIM = $(shell date) +GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') +GITHASH = $(shell git rev-parse HEAD) + +#======================================================================== +# PART 3: Checks +#====================================================================== +# make sure that the paths are defined. These are just some high level checks +ifndef F_MASTER + $(error F_MASTER is undefined) +endif +ifndef FC + $(error FC is undefined: Specify your compiler) +endif +ifndef FC_EXE + $(error FC_EXE is undefined: Specify your compiler executable) +endif +ifndef FLAGS_SUMMA + $(error Specify flags for your compiler: $(FC)) +endif +ifndef INCLUDES + $(error INCLUDES is undefined) +endif +ifndef LIBRARIES + $(error LIBRARIES is undefined) +endif + +#======================================================================== +# PART 4: compilation +#====================================================================== + +# Compile +all: compile_noah compile_comm compile_rout compile_test link_test clean_test compile_summa link clean install install_test +part: compile_noah compile_comm compile_rout +summa: compile_noah compile_comm compile_rout compile_summa link clean install +bmi: compile_noah compile_comm compile_rout compile_test link_test clean install_test + + +check: + $(info) + $(info Displaying make variables:) + $(info F_MASTER : $(F_MASTER)) + $(info EXE_PATH : $(EXE_PATH)) + $(info FC : $(FC)) + $(info INCLUDES : $(INCLUDES)) + $(info LIBRARIES : $(LIBRARIES)) + $(info FLAGS_NOAH : $(FLAGS_NOAH)) + $(info FLAGS_COMM : $(FLAGS_COMM)) + $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) + $(info SUNDIALSDIR: $(DIR_SUNDIALS)) + $(info INC_SUNDIALS: $(INC_SUNDIALS)) + $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) + $(info BMIDIR: $(DIR_BMI)) + $(info INC_BMI: $(INC_BMI)) + $(info LIB_BMI: $(LIB_BMI)) + $(info) + +# update version information +update_version: + echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) + echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) + +# compile common routines +compile_comm: + $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) + +# compile Noah-MP routines +compile_noah: + $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) + +# compile SUMMA routines +compile_rout: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) + +# compile run scripts +compile_summa: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA) + +# compile test scripts +compile_test: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(BMI) + +# link run routines +link: + $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(DRIVER__EX) + +# link test routines +link_test: + $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(BMI__EX) + +# remove test script objects +clean_test: + rm -f summa_runBMI.o + +# Remove object files +clean: + rm -f *.o + rm -f *.mod + rm -f soil_veg_gen_parm__genmod.f90 + +# Copy the executable to the bin directory +install: + @mkdir -p $(EXE_PATH) + @mv $(DRIVER__EX) $(EXE_PATH) + $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) + +# Copy the executable to the bin directory +install_test: + @mkdir -p $(EXE_PATH) + @mv $(BMI__EX) $(EXE_PATH) + $(info $(BMI__EX) successfully installed in $(EXE_PATH)) diff --git a/build/makefiles/build_cmakeSundials b/build/makefiles/Sundials/build_cmakeSundials similarity index 100% rename from build/makefiles/build_cmakeSundials rename to build/makefiles/Sundials/build_cmakeSundials From 8b34561c6ea296f4a407f0653e71214a968c564b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 12 May 2023 12:59:07 +0900 Subject: [PATCH 0722/1472] more makefiles --- .../makefiles/Sundials/compile_copernicus.sh | 17 + build/makefiles/Sundials/compile_graham.sh | 17 + build/makefiles/Sundials/compile_mac.sh | 11 + .../makefiles/Sundials/compile_richardson.sh | 11 + build/makefiles/Sundials/makefile_cluster | 311 ++++++++++++++++++ build/makefiles/Sundials/makefile_mac | 310 +++++++++++++++++ 6 files changed, 677 insertions(+) create mode 100755 build/makefiles/Sundials/compile_copernicus.sh create mode 100755 build/makefiles/Sundials/compile_graham.sh create mode 100755 build/makefiles/Sundials/compile_mac.sh create mode 100755 build/makefiles/Sundials/compile_richardson.sh create mode 100644 build/makefiles/Sundials/makefile_cluster create mode 100644 build/makefiles/Sundials/makefile_mac diff --git a/build/makefiles/Sundials/compile_copernicus.sh b/build/makefiles/Sundials/compile_copernicus.sh new file mode 100755 index 000000000..a54c13f40 --- /dev/null +++ b/build/makefiles/Sundials/compile_copernicus.sh @@ -0,0 +1,17 @@ +#! /bin/bash + +#### load modules if using Compute Canada or Copernicus #### +module load StdEnv/2020 +module load gcc/9.3.0 +module load openblas/0.3.17 +module load netcdf-fortran/4.5.2 + +#### parent directory of the 'build' directory #### +export ROOT_DIR=/globalhome/gwu479/HPC/SummaSundials + +export INCLUDES="-I${EBROOTNETCDFMINFORTRAN}/include" +export LDFLAGS="-L${EBROOTNETCDFMINFORTRAN}/lib64 -L${EBROOTOPENBLAS}/lib" +export LIBRARIES="${LDFLAGS} -lnetcdff -lopenblas" + +make -f ${ROOT_DIR}/summa/build/makefiles/makefile_cluster +export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin diff --git a/build/makefiles/Sundials/compile_graham.sh b/build/makefiles/Sundials/compile_graham.sh new file mode 100755 index 000000000..545353682 --- /dev/null +++ b/build/makefiles/Sundials/compile_graham.sh @@ -0,0 +1,17 @@ +#! /bin/bash + +#### load modules if using Compute Canada or Copernicus #### +module load StdEnv/2020 +module load gcc/9.3.0 +module load openblas/0.3.17 +module load netcdf-fortran/4.5.2 + +#### parent directory of the 'build' directory #### +export ROOT_DIR=/home/avanb/SummaSundials + +export INCLUDES="-I${EBROOTNETCDFMINFORTRAN}/include" +export LDFLAGS="-L${EBROOTNETCDFMINFORTRAN}/lib64 -L${EBROOTOPENBLAS}/lib" +export LIBRARIES="${LDFLAGS} -lnetcdff -lopenblas" + +make -f ${ROOT_DIR}/summa/build/makefiles/makefile_cluster +export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin diff --git a/build/makefiles/Sundials/compile_mac.sh b/build/makefiles/Sundials/compile_mac.sh new file mode 100755 index 000000000..f1a58cd8c --- /dev/null +++ b/build/makefiles/Sundials/compile_mac.sh @@ -0,0 +1,11 @@ +#! /bin/bash + +#### parent directory of the 'build' directory #### +export ROOT_DIR=/Users/amedin/Research/SummaSundials + +export INCLUDES="-I/opt/local/include -I/opt/local/lib" +export LDFLAGS="-L/opt/local/lib" +export LIBRARIES="${LDFLAGS} -llapack -lgfortran -lnetcdff -lnetcdf" + +make -f ${ROOT_DIR}/summa/build/makefiles/makefile_mac +export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin diff --git a/build/makefiles/Sundials/compile_richardson.sh b/build/makefiles/Sundials/compile_richardson.sh new file mode 100755 index 000000000..34bf5778f --- /dev/null +++ b/build/makefiles/Sundials/compile_richardson.sh @@ -0,0 +1,11 @@ +#! /bin/bash + +#### parent directory of the 'build' directory #### +export ROOT_DIR=/u1/gwu479/SummaSundials + +export INCLUDES="-I/usr/include -I/usr/local/include" +export LDFLAGS="-L/usr/local/lib" +export LIBRARIES="${LDFLAGS} -lnetcdff -lopenblas -lnetcdf" + +make -f ${ROOT_DIR}/summa/build/makefiles/makefile_cluster +export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin diff --git a/build/makefiles/Sundials/makefile_cluster b/build/makefiles/Sundials/makefile_cluster new file mode 100644 index 000000000..1f387fca1 --- /dev/null +++ b/build/makefiles/Sundials/makefile_cluster @@ -0,0 +1,311 @@ +# Define core directory below which everything resides. This is the +# parent directory of the 'build' directory +# ROOT_DIR = +F_MASTER = $(ROOT_DIR)/summa + +#### Compilers #### +FC = gfortran +FC_EXE = gfortran + +#### Includes AND Libraries #### +# INCLUDES = +# LDFLAGS = +# LIBRARIES = + +DIR_SUNDIALS= $(ROOT_DIR)/sundials/instdir +INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran +LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod + +# Production runs +FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors + +# Debug runs +# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC +# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC +# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC + +#======================================================================== +# PART 1: Define directory paths +#======================================================================== + +# Core directory that contains source code +F_KORE_DIR = $(F_MASTER)/build/source + +# Location of the compiled modules +MOD_PATH = $(F_MASTER)/build + +# Define the directory for the executables +EXE_PATH = $(F_MASTER)/bin + +#======================================================================== +# PART 2: Assemble all of the SUMMA sub-routines +#======================================================================== + +# Define directories +DRIVER_DIR = $(F_KORE_DIR)/driver +HOOKUP_DIR = $(F_KORE_DIR)/hookup +NETCDF_DIR = $(F_KORE_DIR)/netcdf +DSHARE_DIR = $(F_KORE_DIR)/dshare +NUMREC_DIR = $(F_KORE_DIR)/numrec +NOAHMP_DIR = $(F_KORE_DIR)/noah-mp +ENGINE_DIR = $(F_KORE_DIR)/engine + +# utilities +SUMMA_NRUTIL= \ + nrtype.f90 \ + f2008funcs.f90 \ + nr_utility.f90 +NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) + +# +# Numerical recipes procedures +# NOTE: all numerical recipes procedures are now replaced with free versions +SUMMA_NRPROC= \ + expIntegral.f90 \ + spline_int.f90 +NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) + +# Hook-up modules (set files and directory paths) +SUMMA_HOOKUP= \ + ascii_util.f90 \ + summaFileManager.f90 + +HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) + +# Data modules +SUMMA_DATAMS= \ + multiconst.f90 \ + var_lookup.f90 \ + data_types.f90 \ + globalData.f90 \ + flxMapping.f90 \ + get_ixname.f90 \ + popMetadat.f90 \ + outpt_stat.f90 +DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) + +# utility modules +SUMMA_UTILMS= \ + time_utils.f90 \ + mDecisions.f90 \ + snow_utils.f90 \ + soil_utils.f90 \ + soil_utilsAddSundials.f90 \ + updatState.f90 \ + updatStateSundials.f90 \ + matrixOper.f90 +UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) + +# Model guts +SUMMA_MODGUT= \ + MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) + +# Solver +SUMMA_SOLVER= \ + vegPhenlgy.f90 \ + diagn_evar.f90 \ + stomResist.f90 \ + groundwatr.f90 \ + vegSWavRad.f90 \ + vegNrgFlux.f90 \ + ssdNrgFlux.f90 \ + vegLiqFlux.f90 \ + snowLiqFlx.f90 \ + soilLiqFlx.f90 \ + bigAquifer.f90 \ + computFlux.f90 \ + type4IDA.f90 \ + tol4IDA.f90 \ + computEnthalpy.f90 \ + computHeatCap.f90 \ + computThermConduct.f90 \ + computResid.f90 \ + computJacob.f90 \ + eval8summa.f90 \ + summaSolve.f90 \ + systemSolv.f90 \ + computResidSundials.f90 \ + eval8summaSundials.f90 \ + computJacobSundials.f90 \ + computSnowDepth.f90 \ + summaSolveSundialsIDA.f90 \ + systemSolvSundials.f90 \ + varSubstep.f90 \ + opSplittin.f90 \ + coupled_em.f90 \ + run_oneHRU.f90 \ + run_oneGRU.f90 +SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) + +# Define routines for SUMMA preliminaries +SUMMA_PRELIM= \ + conv_funcs.f90 \ + sunGeomtry.f90 \ + convE2Temp.f90 \ + allocspace.f90 \ + checkStruc.f90 \ + childStruc.f90 \ + ffile_info.f90 \ + read_attrb.f90 \ + read_pinit.f90 \ + pOverwrite.f90 \ + read_param.f90 \ + paramCheck.f90 \ + check_icond.f90 +PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) + +SUMMA_NOAHMP= \ + module_model_constants.F \ + module_sf_noahutl.F \ + module_sf_noahlsm.F \ + module_sf_noahmplsm.F +NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) + +# Define routines for the SUMMA model runs +SUMMA_MODRUN = \ + indexState.f90 \ + getVectorz.f90 \ + t2enthalpy.f90 \ + updateVars.f90 \ + updateVarsSundials.f90 \ + var_derive.f90 \ + read_force.f90 \ + derivforce.f90 \ + snowAlbedo.f90 \ + canopySnow.f90 \ + tempAdjust.f90 \ + snwCompact.f90 \ + layerMerge.f90 \ + layerDivide.f90 \ + volicePack.f90 \ + qTimeDelay.f90 +MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) + +# Define routines for the solver +SUMMA_MSOLVE = \ + +# Define NetCDF routines +SUMMA_NETCDF = \ + netcdf_util.f90 \ + def_output.f90 \ + modelwrite.f90 \ + read_icond.f90 +NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) + +# ... stitch together common programs +COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) + +# ... stitch together SUMMA programs +SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) + +# Define the driver routine +SUMMA_DRIVER= \ + summa_type.f90 \ + summa_util.f90 \ + summa_alarms.f90 \ + summa_globalData.f90 \ + summa_defineOutput.f90 \ + summa_init.f90 \ + summa_setup.f90 \ + summa_restart.f90 \ + summa_forcing.f90 \ + summa_modelRun.f90 \ + summa_writeOutput.f90 \ + summa_driver.f90 +DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) + +# Define the executable +DRIVER__EX = summa_sundials.exe + +# Define version number +VERSIONFILE = $(DRIVER_DIR)/summaversion.inc +VERSION = $(shell git tag | tail -n 1) +BULTTIM = $(shell date) +GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') +GITHASH = $(shell git rev-parse HEAD) + +#======================================================================== +# PART 3: Checks +#====================================================================== +# make sure that the paths are defined. These are just some high level checks +ifndef F_MASTER + $(error F_MASTER is undefined) +endif +ifndef FC + $(error FC is undefined: Specify your compiler) +endif +ifndef FC_EXE + $(error FC_EXE is undefined: Specify your compiler executable) +endif +ifndef FLAGS_SUMMA + $(error Specify flags for your compiler: $(FC)) +endif +ifndef INCLUDES + $(error INCLUDES is undefined) +endif +ifndef LIBRARIES + $(error LIBRARIES is undefined) +endif + +#======================================================================== +# PART 4: compilation +#====================================================================== + +# Compile +all: compile_noah compile_comm compile_summa link clean install +part: compile_noah compile_comm compile_summa + +check: + $(info) + $(info Displaying make variables:) + $(info F_MASTER : $(F_MASTER)) + $(info EXE_PATH : $(EXE_PATH)) + $(info FC : $(FC)) + $(info INCLUDES : $(INCLUDES)) + $(info LIBRARIES : $(LIBRARIES)) + $(info FLAGS_NOAH : $(FLAGS_NOAH)) + $(info FLAGS_COMM : $(FLAGS_COMM)) + $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) + $(info SUNDIALSDIR: $(DIR_SUNDIALS)) + $(info INC_SUNDIALS: $(INC_SUNDIALS)) + $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) + $(info) + +# update version information +update_version: + echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) + echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) + +# compile Noah-MP routines +compile_noah: + $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) + +# compile common routines + +compile_comm: + $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) + +# compile SUMMA routines +compile_summa: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ + $(INCLUDES) $(INC_SUNDIALS) + +# link routines +link: + $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) + +# Remove object files +clean: + rm -f *.o + rm -f *.mod + rm -f soil_veg_gen_parm__genmod.f90 + +# Copy the executable to the bin directory +install: + @mkdir -p $(EXE_PATH) + @mv $(DRIVER__EX) $(EXE_PATH) + $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) diff --git a/build/makefiles/Sundials/makefile_mac b/build/makefiles/Sundials/makefile_mac new file mode 100644 index 000000000..00df7f3b2 --- /dev/null +++ b/build/makefiles/Sundials/makefile_mac @@ -0,0 +1,310 @@ +# Define core directory below which everything resides. This is the +# parent directory of the 'build' directory +# ROOT_DIR = +F_MASTER = $(ROOT_DIR)/summa + +#### Compilers #### +FC = gfortran +FC_EXE =/opt/local/bin/gfortran + +#### Includes AND Libraries #### +# INCLUDES = +# LDFLAGS = +# LIBRARIES = + +DIR_SUNDIALS= $(ROOT_DIR)/sundials/instdir +INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran +LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod + +# Production runs +FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors +FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors + +# Debug runs +# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC +# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC +# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC + +#======================================================================== +# PART 1: Define directory paths +#======================================================================== + +# Core directory that contains source code +F_KORE_DIR = $(F_MASTER)/build/source + +# Location of the compiled modules +MOD_PATH = $(F_MASTER)/build + +# Define the directory for the executables +EXE_PATH = $(F_MASTER)/bin + +#======================================================================== +# PART 2: Assemble all of the SUMMA sub-routines +#======================================================================== + +# Define directories +DRIVER_DIR = $(F_KORE_DIR)/driver +HOOKUP_DIR = $(F_KORE_DIR)/hookup +NETCDF_DIR = $(F_KORE_DIR)/netcdf +DSHARE_DIR = $(F_KORE_DIR)/dshare +NUMREC_DIR = $(F_KORE_DIR)/numrec +NOAHMP_DIR = $(F_KORE_DIR)/noah-mp +ENGINE_DIR = $(F_KORE_DIR)/engine + +# utilities +SUMMA_NRUTIL= \ + nrtype.f90 \ + f2008funcs.f90 \ + nr_utility.f90 +NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) + +# +# Numerical recipes procedures +# NOTE: all numerical recipes procedures are now replaced with free versions +SUMMA_NRPROC= \ + expIntegral.f90 \ + spline_int.f90 +NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) + +# Hook-up modules (set files and directory paths) +SUMMA_HOOKUP= \ + ascii_util.f90 \ + summaFileManager.f90 + +HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) + +# Data modules +SUMMA_DATAMS= \ + multiconst.f90 \ + var_lookup.f90 \ + data_types.f90 \ + globalData.f90 \ + flxMapping.f90 \ + get_ixname.f90 \ + popMetadat.f90 \ + outpt_stat.f90 +DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) + +# utility modules +SUMMA_UTILMS= \ + time_utils.f90 \ + mDecisions.f90 \ + snow_utils.f90 \ + soil_utils.f90 \ + soil_utilsAddSundials.f90 \ + updatState.f90 \ + updatStateSundials.f90 \ + matrixOper.f90 +UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) + +# Model guts +SUMMA_MODGUT= \ + MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) + +# Solver +SUMMA_SOLVER= \ + vegPhenlgy.f90 \ + diagn_evar.f90 \ + stomResist.f90 \ + groundwatr.f90 \ + vegSWavRad.f90 \ + vegNrgFlux.f90 \ + ssdNrgFlux.f90 \ + vegLiqFlux.f90 \ + snowLiqFlx.f90 \ + soilLiqFlx.f90 \ + bigAquifer.f90 \ + computFlux.f90 \ + type4IDA.f90 \ + tol4IDA.f90 \ + computEnthalpy.f90 \ + computHeatCap.f90 \ + computThermConduct.f90 \ + computResid.f90 \ + computJacob.f90 \ + eval8summa.f90 \ + summaSolve.f90 \ + systemSolv.f90 \ + computResidSundials.f90 \ + eval8summaSundials.f90 \ + computJacobSundials.f90 \ + computSnowDepth.f90 \ + summaSolveSundialsIDA.f90 \ + systemSolvSundials.f90 \ + varSubstep.f90 \ + opSplittin.f90 \ + coupled_em.f90 \ + run_oneHRU.f90 \ + run_oneGRU.f90 +SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) + +# Define routines for SUMMA preliminaries +SUMMA_PRELIM= \ + conv_funcs.f90 \ + sunGeomtry.f90 \ + convE2Temp.f90 \ + allocspace.f90 \ + checkStruc.f90 \ + childStruc.f90 \ + ffile_info.f90 \ + read_attrb.f90 \ + read_pinit.f90 \ + pOverwrite.f90 \ + read_param.f90 \ + paramCheck.f90 \ + check_icond.f90 +PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) + +SUMMA_NOAHMP= \ + module_model_constants.F \ + module_sf_noahutl.F \ + module_sf_noahlsm.F \ + module_sf_noahmplsm.F +NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) + +# Define routines for the SUMMA model runs +SUMMA_MODRUN = \ + indexState.f90 \ + getVectorz.f90 \ + t2enthalpy.f90 \ + updateVars.f90 \ + updateVarsSundials.f90 \ + var_derive.f90 \ + read_force.f90 \ + derivforce.f90 \ + snowAlbedo.f90 \ + canopySnow.f90 \ + tempAdjust.f90 \ + snwCompact.f90 \ + layerMerge.f90 \ + layerDivide.f90 \ + volicePack.f90 \ + qTimeDelay.f90 +MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) + +# Define routines for the solver +SUMMA_MSOLVE = \ + +# Define NetCDF routines +SUMMA_NETCDF = \ + netcdf_util.f90 \ + def_output.f90 \ + modelwrite.f90 \ + read_icond.f90 +NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) + +# ... stitch together common programs +COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) + +# ... stitch together SUMMA programs +SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) + +# Define the driver routine +SUMMA_DRIVER= \ + summa_type.f90 \ + summa_util.f90 \ + summa_alarms.f90 \ + summa_globalData.f90 \ + summa_defineOutput.f90 \ + summa_init.f90 \ + summa_setup.f90 \ + summa_restart.f90 \ + summa_forcing.f90 \ + summa_modelRun.f90 \ + summa_writeOutput.f90 \ + summa_driver.f90 +DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) + +# Define the executable +DRIVER__EX = summa_sundials.exe + +# Define version number +VERSIONFILE = $(DRIVER_DIR)/summaversion.inc +VERSION = $(shell git tag | tail -n 1) +BULTTIM = $(shell date) +GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') +GITHASH = $(shell git rev-parse HEAD) + +#======================================================================== +# PART 3: Checks +#====================================================================== +# make sure that the paths are defined. These are just some high level checks +ifndef F_MASTER + $(error F_MASTER is undefined) +endif +ifndef FC + $(error FC is undefined: Specify your compiler) +endif +ifndef FC_EXE + $(error FC_EXE is undefined: Specify your compiler executable) +endif +ifndef FLAGS_SUMMA + $(error Specify flags for your compiler: $(FC)) +endif +ifndef INCLUDES + $(error INCLUDES is undefined) +endif +ifndef LIBRARIES + $(error LIBRARIES is undefined) +endif + +#======================================================================== +# PART 4: compilation +#====================================================================== + +# Compile +all: compile_noah compile_comm compile_summa link clean install +part: compile_noah compile_comm compile_summa + +check: + $(info) + $(info Displaying make variables:) + $(info F_MASTER : $(F_MASTER)) + $(info EXE_PATH : $(EXE_PATH)) + $(info FC : $(FC)) + $(info INCLUDES : $(INCLUDES)) + $(info LIBRARIES : $(LIBRARIES)) + $(info FLAGS_NOAH : $(FLAGS_NOAH)) + $(info FLAGS_COMM : $(FLAGS_COMM)) + $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) + $(info SUNDIALSDIR: $(DIR_SUNDIALS)) + $(info INC_SUNDIALS: $(INC_SUNDIALS)) + $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) + $(info) + +# update version information +update_version: + echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) + echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) + echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) + +# compile Noah-MP routines +compile_noah: + $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) + +# compile common routines +compile_comm: + $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) + +# compile SUMMA routines +compile_summa: update_version + $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ + $(INCLUDES) $(INC_SUNDIALS) + +# link routines +link: + $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) + +# Remove object files +clean: + rm -f *.o + rm -f *.mod + rm -f soil_veg_gen_parm__genmod.f90 + +# Copy the executable to the bin directory +install: + @mkdir -p $(EXE_PATH) + @mv $(DRIVER__EX) $(EXE_PATH) + $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) From 998dfb5b5ebe9fcfe66c73273071fb3f3eb14e83 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 12 May 2023 17:34:27 +0900 Subject: [PATCH 0723/1472] name changing and removing some things from actors build as they are repeats --- build/cmake/CMakeLists.txt | 40 ++++++++++++++++------ build/cmake/build.cluster.bash | 11 ++---- build/cmake/build_actors.cluster.bash | 15 ++++++++ build/cmake/build_actors.mac.bash | 7 ++++ build/cmake/build_ngen.cluster.bash | 8 +++-- build/makefiles/Actors/makefile_apptainer | 10 +++--- build/makefiles/Actors/makefile_cluster | 4 +-- build/makefiles/Actors/makefile_richardson | 4 +-- build/makefiles/BE/makefile_old_summa | 4 +-- build/makefiles/Ngen/makefile_cluster | 6 ++-- build/makefiles/Ngen/makefile_mac | 6 ++-- build/makefiles/Sundials/makefile_cluster | 6 ++-- build/makefiles/Sundials/makefile_mac | 6 ++-- build/source/engine/read_attrb.f90 | 17 ++++----- 14 files changed, 91 insertions(+), 53 deletions(-) create mode 100644 build/cmake/build_actors.cluster.bash create mode 100755 build/cmake/build_actors.mac.bash diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index e2599906c..7e45548d7 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -177,6 +177,22 @@ else() # Actors is on Cluster, if using on personal computer might need to link directory-- not working on Mac right now if(CMAKE_BUILD_TYPE MATCHES Actors) + set(DIR_ACTORS "") + foreach(dir IN ITEMS + "${F_MASTER}../actor-framework/install" + "${PARENT_DIR}../actor-framework/install" + "${F_MASTER}../../SummaActors/actor-framework/install" + "${PARENT_DIR}../../SummaActors/actor-framework/install" + "../../../../SummaActors/actor-framework/install") #look in same head directory as summa or up to another installation + if(EXISTS ${dir}) + set(DIR_SUNDIALS ${dir}) + message("\nFound Actors directory at ${DIR_ACTORS}") + break() + endif() + message(FATAL_ERROR "\nDid not find Actors directory, edit CMakeLists.txt to include path") + endforeach() + + #link_directories(${DIR_ACTORS}/lib) find_package(CAF REQUIRED) set(INC_ACTORS ${CAF_INCLUDES} ${PARENT_DIR}/build/includes/global ${PARENT_DIR}/build/includes/summa_actor ${PARENT_DIR}/build/includes/gru_actor ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) @@ -295,13 +311,14 @@ set(PRELIM ${ENGINE_DIR}/conv_funcs.f90 ${ENGINE_DIR}/convE2Temp.f90 ${SUB_ENGINE_DIR}/ffile_info.f90 - ${ENGINE_DIR}/read_attrb.f90 ${ENGINE_DIR}/read_pinit.f90 ${ENGINE_DIR}/paramCheck.f90 ${ENGINE_DIR}/pOverwrite.f90 - ${ENGINE_DIR}/read_param.f90 ${ENGINE_DIR}/sunGeomtry.f90) -set(PRELIM_ACTOR +set(PRELIM_NOT_ACTORS + ${ENGINE_DIR}/read_attrb.f90 + ${ENGINE_DIR}/read_param.f90) +set(PRELIM_ACTORS ${SUB_ENGINE_DIR}/alloc_fileAccess.f90) # Model run support modules @@ -313,7 +330,6 @@ set(MODRUN ${ENGINE_DIR}/layerMerge.f90 ${ENGINE_DIR}/layerDivide.f90 ${ENGINE_DIR}/qTimeDelay.f90 - ${ENGINE_DIR}/read_force.f90 ${ENGINE_DIR}/snowAlbedo.f90 ${ENGINE_DIR}/snwCompact.f90 ${ENGINE_DIR}/t2enthalpy.f90 @@ -321,6 +337,8 @@ set(MODRUN ${ENGINE_DIR}/updateVars.f90 ${ENGINE_DIR}/var_derive.f90 ${ENGINE_DIR}/volicePack.f90) +set(MODRUN_NOT_ACTORS + ${ENGINE_DIR}/read_force.f90) set(MODRUN_SUNDIALS ${ENGINE_DIR}/tol4ida.f90 ${ENGINE_DIR}/updateVarsSundials.f90) @@ -351,15 +369,15 @@ set(SOLVER ${ENGINE_DIR}/vegNrgFlux.f90 ${ENGINE_DIR}/vegPhenlgy.f90 ${ENGINE_DIR}/vegSWavRad.f90) +set(SOLVER_NOT_ACTORS + ${ENGINE_DIR}/run_oneGRU.f90 + ${ENGINE_DIR}/run_oneHRU.f90) set(SOLVER_SUNDIALS ${ENGINE_DIR}/computJacobSundials.f90 ${ENGINE_DIR}/computResidSundials.f90 ${ENGINE_DIR}/eval8summaSundials.f90 ${ENGINE_DIR}/summaSolveSundials4ida.f90 ${ENGINE_DIR}/systemSolvSundials.f90) -set(SOLVER_NOT_ACTORS - ${ENGINE_DIR}/run_oneGRU.f90 - ${ENGINE_DIR}/run_oneHRU.f90) # Driver support modules set(DRIVER @@ -386,8 +404,8 @@ set(INTERFACE set(FILE_ACCESS_INTERFACE ${FILE_ACCESS_DIR}/fortran_code/output_structure.f90 ${FILE_ACCESS_DIR}/fortran_code/cppwrap_fileAccess.f90 - ${FILE_ACCESS_DIR}/fortran_code/read_attribute.f90 - ${FILE_ACCESS_DIR}/fortran_code/read_forcing.f90 + ${FILE_ACCESS_DIR}/fortran_code/read_attrb.f90 + ${FILE_ACCESS_DIR}/fortran_code/read_force.f90 ${FILE_ACCESS_DIR}/fortran_code/read_param.f90 ${FILE_ACCESS_DIR}/fortran_code/read_initcond.f90 ${FILE_ACCESS_DIR}/fortran_code/writeOutputFromOutputStructure.f90 @@ -460,13 +478,15 @@ set(SUMMA_ALL if(CMAKE_BUILD_TYPE MATCHES Actors) set(SUMMA_ALL ${SUMMA_ALL} - ${PRELIM_ACTOR} + ${PRELIM_ACTORS} ${JOB_INTERFACE} ${FILE_ACCESS_INTERFACE} ${HRU_INTERFACE} ${GRU_INTERFACE}) else() set(SUMMA_ALL ${SUMMA_ALL} + ${PRELIM_NOT_ACTORS} + ${MODRUN_NOT_ACTORS} ${SOLVER_NOT_ACTORS} ${DRIVER_NOT_ACTORS}) endif() diff --git a/build/cmake/build.cluster.bash b/build/cmake/build.cluster.bash index da86203da..744804b6c 100644 --- a/build/cmake/build.cluster.bash +++ b/build/cmake/build.cluster.bash @@ -1,19 +1,12 @@ #!/bin/bash -# build on Copernicus or Graham, from cmake directory run this as ./build.cluster.bash ./build.cluster.bash - +# build on Copernicus or Graham, from cmake directory run this as ./build.cluster.bash +# for Summa module load StdEnv/2020 module load gcc/9.3.0 module load openblas/0.3.17 module load netcdf-fortran/4.5.2 -# for NexGen -module load boost -module load udunits/2.2.28 - -# for Actors -module load caf - cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials_Cluster cmake --build ../cmake_build --target all diff --git a/build/cmake/build_actors.cluster.bash b/build/cmake/build_actors.cluster.bash new file mode 100644 index 000000000..d4046af8e --- /dev/null +++ b/build/cmake/build_actors.cluster.bash @@ -0,0 +1,15 @@ +#!/bin/bash + +# build on Copernicus or Graham, from cmake directory run this as ./build_actors.cluster.bash +# for Summa +module load StdEnv/2020 +module load gcc/9.3.0 +module load openblas/0.3.17 +module load netcdf-fortran/4.5.2 + +# for Actors +module load caf + +cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials_Actors_Cluster +cmake --build ../cmake_build --target all + diff --git a/build/cmake/build_actors.mac.bash b/build/cmake/build_actors.mac.bash new file mode 100755 index 000000000..bc43513be --- /dev/null +++ b/build/cmake/build_actors.mac.bash @@ -0,0 +1,7 @@ +#!/bin/bash + +# build on Mac, from cmake directory run this as ./build_actors.mac.bash + +cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials_Actors +cmake --build ../cmake_build --target all + diff --git a/build/cmake/build_ngen.cluster.bash b/build/cmake/build_ngen.cluster.bash index 21b981358..672a29e30 100644 --- a/build/cmake/build_ngen.cluster.bash +++ b/build/cmake/build_ngen.cluster.bash @@ -1,14 +1,16 @@ #!/bin/bash # build nextgen on Copernicus, from ngen directory put this one directory up and run this as ../build_ngen.cluster.bash - -module load boost -module load udunits/2.2.28 +# for Summa module load StdEnv/2020 module load gcc/9.3.0 module load openblas/0.3.17 module load netcdf-fortran/4.5.2 +# for NexGen +module load boost +module load udunits/2.2.28 + cmake -B extern/iso_c_fortran_bmi/cmake_build -S extern/iso_c_fortran_bmi cmake --build extern/iso_c_fortran_bmi/cmake_build --target all diff --git a/build/makefiles/Actors/makefile_apptainer b/build/makefiles/Actors/makefile_apptainer index ee3b1ba70..a1fdf9e37 100644 --- a/build/makefiles/Actors/makefile_apptainer +++ b/build/makefiles/Actors/makefile_apptainer @@ -133,8 +133,8 @@ SUMMA_SOLVER= \ eval8summa.f90 \ summaSolve.f90 \ systemSolv.f90 \ - sundials/type4IDA.f90 \ - sundials/tol4IDA.f90 \ + sundials/type4ida.f90 \ + sundials/tol4ida.f90 \ sundials/computEnthalpy.f90 \ sundials/computHeatCap.f90 \ sundials/computThermConduct.f90 \ @@ -142,7 +142,7 @@ SUMMA_SOLVER= \ sundials/eval8summaSundials.f90 \ sundials/computJacobSundials.f90 \ sundials/computSnowDepth.f90 \ - sundials/summaSolveSundialsIDA.f90 \ + sundials/summaSolveSundials4ida.f90 \ sundials/systemSolvSundials.f90 \ varSubstep.f90 \ sundials/varSubstepSundials.f90 \ @@ -162,8 +162,8 @@ INTERFACE = $(patsubst %, $(ACTORS_DIR)/global/%, $(SUMMA_INTERFACE)) SUMMA_FILEACCESS_INTERFACE = \ cppwrap_fileAccess.f90 \ - read_attribute.f90 \ - read_forcing.f90 \ + read_attrb.f90 \ + read_force.f90 \ read_param.f90 \ write_to_netcdf.f90 diff --git a/build/makefiles/Actors/makefile_cluster b/build/makefiles/Actors/makefile_cluster index 18e025f3e..780e755f4 100644 --- a/build/makefiles/Actors/makefile_cluster +++ b/build/makefiles/Actors/makefile_cluster @@ -161,8 +161,8 @@ INTERFACE = $(patsubst %, $(ACTORS_DIR)/global/%, $(SUMMA_INTERFACE)) SUMMA_FILEACCESS_INTERFACE = \ output_structure.f90 \ cppwrap_fileAccess.f90 \ - read_attribute.f90 \ - read_forcing.f90 \ + read_attrb.f90 \ + read_force.f90 \ read_param.f90 \ read_initcond.f90 \ writeOutputFromOutputStructure.f90 \ diff --git a/build/makefiles/Actors/makefile_richardson b/build/makefiles/Actors/makefile_richardson index 92853c168..e2a1f2a12 100644 --- a/build/makefiles/Actors/makefile_richardson +++ b/build/makefiles/Actors/makefile_richardson @@ -163,8 +163,8 @@ INTERFACE = $(patsubst %, $(ACTORS_DIR)/global/%, $(SUMMA_INTERFACE)) SUMMA_FILEACCESS_INTERFACE = \ output_structure.f90 \ cppwrap_fileAccess.f90 \ - read_attribute.f90 \ - read_forcing.f90 \ + read_attrb.f90 \ + read_force.f90 \ read_param.f90 \ read_initcond.f90 \ writeOutputFromOutputStructure.f90 \ diff --git a/build/makefiles/BE/makefile_old_summa b/build/makefiles/BE/makefile_old_summa index 74aafdddf..d61c29a49 100644 --- a/build/makefiles/BE/makefile_old_summa +++ b/build/makefiles/BE/makefile_old_summa @@ -162,8 +162,8 @@ INTERFACE = $(patsubst %, $(ACTORS_DIR)/global/%, $(SUMMA_INTERFACE)) SUMMA_FILEACCESS_INTERFACE = \ output_structure.f90 \ cppwrap_fileAccess.f90 \ - read_attribute.f90 \ - read_forcing.f90 \ + read_attrb.f90 \ + read_force.f90 \ read_param.f90 \ read_initcond.f90 \ writeOutputFromOutputStructure.f90 \ diff --git a/build/makefiles/Ngen/makefile_cluster b/build/makefiles/Ngen/makefile_cluster index 0e88ddf12..c5b2da265 100644 --- a/build/makefiles/Ngen/makefile_cluster +++ b/build/makefiles/Ngen/makefile_cluster @@ -120,8 +120,8 @@ SUMMA_SOLVER= \ soilLiqFlx.f90 \ bigAquifer.f90 \ computFlux.f90 \ - type4IDA.f90 \ - tol4IDA.f90 \ + type4ida.f90 \ + tol4ida.f90 \ computEnthalpy.f90 \ computHeatCap.f90 \ computThermConduct.f90 \ @@ -134,7 +134,7 @@ SUMMA_SOLVER= \ eval8summaSundials.f90 \ computJacobSundials.f90 \ computSnowDepth.f90 \ - summaSolveSundialsIDA.f90 \ + summaSolveSundials4ida.f90 \ systemSolvSundials.f90 \ varSubstep.f90 \ opSplittin.f90 \ diff --git a/build/makefiles/Ngen/makefile_mac b/build/makefiles/Ngen/makefile_mac index bb4c48732..31789c9fd 100644 --- a/build/makefiles/Ngen/makefile_mac +++ b/build/makefiles/Ngen/makefile_mac @@ -120,8 +120,8 @@ SUMMA_SOLVER= \ soilLiqFlx.f90 \ bigAquifer.f90 \ computFlux.f90 \ - type4IDA.f90 \ - tol4IDA.f90 \ + type4ida.f90 \ + tol4ida.f90 \ computEnthalpy.f90 \ computHeatCap.f90 \ computThermConduct.f90 \ @@ -134,7 +134,7 @@ SUMMA_SOLVER= \ eval8summaSundials.f90 \ computJacobSundials.f90 \ computSnowDepth.f90 \ - summaSolveSundialsIDA.f90 \ + summaSolveSundials4ida.f90 \ systemSolvSundials.f90 \ varSubstep.f90 \ opSplittin.f90 \ diff --git a/build/makefiles/Sundials/makefile_cluster b/build/makefiles/Sundials/makefile_cluster index 1f387fca1..bc3d0238d 100644 --- a/build/makefiles/Sundials/makefile_cluster +++ b/build/makefiles/Sundials/makefile_cluster @@ -116,8 +116,8 @@ SUMMA_SOLVER= \ soilLiqFlx.f90 \ bigAquifer.f90 \ computFlux.f90 \ - type4IDA.f90 \ - tol4IDA.f90 \ + type4ida.f90 \ + tol4ida.f90 \ computEnthalpy.f90 \ computHeatCap.f90 \ computThermConduct.f90 \ @@ -130,7 +130,7 @@ SUMMA_SOLVER= \ eval8summaSundials.f90 \ computJacobSundials.f90 \ computSnowDepth.f90 \ - summaSolveSundialsIDA.f90 \ + summaSolveSundials4ida.f90 \ systemSolvSundials.f90 \ varSubstep.f90 \ opSplittin.f90 \ diff --git a/build/makefiles/Sundials/makefile_mac b/build/makefiles/Sundials/makefile_mac index 00df7f3b2..51cd8c416 100644 --- a/build/makefiles/Sundials/makefile_mac +++ b/build/makefiles/Sundials/makefile_mac @@ -116,8 +116,8 @@ SUMMA_SOLVER= \ soilLiqFlx.f90 \ bigAquifer.f90 \ computFlux.f90 \ - type4IDA.f90 \ - tol4IDA.f90 \ + type4ida.f90 \ + tol4ida.f90 \ computEnthalpy.f90 \ computHeatCap.f90 \ computThermConduct.f90 \ @@ -130,7 +130,7 @@ SUMMA_SOLVER= \ eval8summaSundials.f90 \ computJacobSundials.f90 \ computSnowDepth.f90 \ - summaSolveSundialsIDA.f90 \ + summaSolveSundials4ida.f90 \ systemSolvSundials.f90 \ varSubstep.f90 \ opSplittin.f90 \ diff --git a/build/source/engine/read_attrb.f90 b/build/source/engine/read_attrb.f90 index 0eba82dd2..44d260608 100644 --- a/build/source/engine/read_attrb.f90 +++ b/build/source/engine/read_attrb.f90 @@ -123,10 +123,6 @@ subroutine read_dimension(attrFile,fileGRU,fileHRU,nGRU,nHRU,err,message,startGR err = nf90_inq_varid(ncID,"hru2gruId",varID); if (err/=0) then; message=trim(message)//'problem finding hru2gruId'; return; end if err = nf90_get_var(ncID,varID,hru2gru_id); if (err/=0) then; message=trim(message)//'problem reading hru2gruId'; return; end if - ! close netcdf file - call nc_file_close(ncID,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - ! array from 1 to total # of HRUs in attributes file hru_ix=arth(1,1,fileHRU) @@ -187,6 +183,11 @@ subroutine read_dimension(attrFile,fileGRU,fileHRU,nGRU,nHRU,err,message,startGR end if ! not checkHRU +deallocate(gru_id, hru_ix, hru_id, hru2gru_id) +! close netcdf file +call nc_file_close(ncID,err,cmessage) +if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + end subroutine read_dimension ! ************************************************************************************************ @@ -393,14 +394,14 @@ subroutine read_attrb(attrFile,nGRU,attrStruct,typeStruct,idStruct,err,message) ! ********************************************************************************************** ! (5) close netcdf file ! ********************************************************************************************** - call nc_file_close(ncID,err,cmessage) - if (err/=0)then; message=trim(message)//trim(cmessage); return; end if - - ! free memory +! free memory deallocate(checkType) deallocate(checkId) deallocate(checkAttr) + call nc_file_close(ncID,err,cmessage) + if (err/=0)then; message=trim(message)//trim(cmessage); return; end if + end subroutine read_attrb end module read_attrb_module From 2674e73c768a37cb4abb6b060dca819d39b4242e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 12 May 2023 22:13:35 +0900 Subject: [PATCH 0724/1472] trying to rename things to make sense --- build/cmake/CMakeLists.txt | 34 +- build/makefiles/Actors/makefile_apptainer | 2 +- build/makefiles/Actors/makefile_cluster | 6 +- build/makefiles/Actors/makefile_richardson | 425 --------------------- build/makefiles/BE/makefile_old_summa | 6 +- 5 files changed, 22 insertions(+), 451 deletions(-) delete mode 100644 build/makefiles/Actors/makefile_richardson diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 7e45548d7..8f80892d7 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -159,7 +159,6 @@ if(CMAKE_BUILD_TYPE MATCHES Cluster) set(LIB_ACTORS ${CAF_LIBRARIES} -lcaf_core -lcaf_io) endif() - else() message("\nBuilding for personal computer") @@ -191,10 +190,7 @@ else() endif() message(FATAL_ERROR "\nDid not find Actors directory, edit CMakeLists.txt to include path") endforeach() - - - #link_directories(${DIR_ACTORS}/lib) - find_package(CAF REQUIRED) + link_directories(${DIR_ACTORS}/lib) set(INC_ACTORS ${CAF_INCLUDES} ${PARENT_DIR}/build/includes/global ${PARENT_DIR}/build/includes/summa_actor ${PARENT_DIR}/build/includes/gru_actor ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) set(LIB_ACTORS ${CAF_LIBRARIES} -lcaf_core -lcaf_io) endif() @@ -381,44 +377,44 @@ set(SOLVER_SUNDIALS # Driver support modules set(DRIVER - ${SUB_DRIVER_DIR}/summa_type.f90 - ${SUB_DRIVER_DIR}/summa_util.f90 ${DRIVER_DIR}/summa_alarms.f90 ${SUB_DRIVER_DIR}/summa_globalData.f90) + ${SUB_DRIVER_DIR}/summa_type.f90 + ${SUB_DRIVER_DIR}/summa_util.f90 set(DRIVER_NOT_ACTORS ${DRIVER_DIR}/summa_defineOutput.f90 ${DRIVER_DIR}/summa_init.f90 - ${DRIVER_DIR}/summa_setup.f90 - ${DRIVER_DIR}/summa_restart.f90 ${DRIVER_DIR}/summa_forcing.f90 ${DRIVER_DIR}/summa_modelRun.f90 + ${DRIVER_DIR}/summa_restart.f90 + ${DRIVER_DIR}/summa_setup.f90 ${DRIVER_DIR}/summa_writeOutput.f90) set(DRIVER_NEXGEN ${DRIVER_DIR}/summa_bmi.f90) # Actors interface modules set(INTERFACE - ${ACTORS_DIR}/global/cppwrap_datatypes.f90 ${ACTORS_DIR}/global/cppwrap_auxiliary.f90 + ${ACTORS_DIR}/global/cppwrap_datatypes.f90 ${ACTORS_DIR}/global/cppwrap_metadata.f90) set(FILE_ACCESS_INTERFACE - ${FILE_ACCESS_DIR}/fortran_code/output_structure.f90 ${FILE_ACCESS_DIR}/fortran_code/cppwrap_fileAccess.f90 + ${FILE_ACCESS_DIR}/fortran_code/output_structure.f90 ${FILE_ACCESS_DIR}/fortran_code/read_attrb.f90 ${FILE_ACCESS_DIR}/fortran_code/read_force.f90 + ${FILE_ACCESS_DIR}/fortran_code/read_icondFromStructure.f90 ${FILE_ACCESS_DIR}/fortran_code/read_param.f90 - ${FILE_ACCESS_DIR}/fortran_code/read_initcond.f90 - ${FILE_ACCESS_DIR}/fortran_code/writeOutputFromOutputStructure.f90 - ${FILE_ACCESS_DIR}/fortran_code/write_to_netcdf.f90) + ${FILE_ACCESS_DIR}/fortran_code/write_to_netcdf.f90 + ${FILE_ACCESS_DIR}/fortran_code/writeOutputFromOutputStructure.f90) set(JOB_INTERFACE ${JOB_ACTOR_DIR}/job_actor.f90) set(HRU_INTERFACE - ${HRU_ACTOR_DIR}/fortran_code/model_run.f90 - ${HRU_ACTOR_DIR}/fortran_code/setup_hru.f90 - ${HRU_ACTOR_DIR}/fortran_code/restart.f90 ${HRU_ACTOR_DIR}/fortran_code/hru_actor.f90 - ${HRU_ACTOR_DIR}/fortran_code/init_hru_actor.f90 - ${HRU_ACTOR_DIR}/fortran_code/outputStrucWrite.f90 + ${HRU_ACTOR_DIR}/fortran_code/hru_init.f90 + ${HRU_ACTOR_DIR}/fortran_code/hru_modelRun.f90 + ${HRU_ACTOR_DIR}/fortran_code/hru_restart.f90 + ${HRU_ACTOR_DIR}/fortran_code/hru_setup.f90 + ${HRU_ACTOR_DIR}/fortran_code/hru_modelwrite.f90 ${HRU_ACTOR_DIR}/fortran_code/hru_writeOutput.f90) # Actors actual actor modules diff --git a/build/makefiles/Actors/makefile_apptainer b/build/makefiles/Actors/makefile_apptainer index a1fdf9e37..fe701e0c3 100644 --- a/build/makefiles/Actors/makefile_apptainer +++ b/build/makefiles/Actors/makefile_apptainer @@ -177,7 +177,7 @@ JOB_INTERFACE = $(patsubst %, $(JOB_ACTOR_DIR)/%, $(SUMMA_JOB_INTERFACE)) SUMMA_HRU_INTERFACE = \ cppwrap_hru.f90 \ hru_actor.f90 \ - init_hru_actor.f90 \ + hru_init.f90 \ HRU_INTERFACE = $(patsubst %, $(HRU_ACTOR_DIR)/%, $(SUMMA_HRU_INTERFACE)) diff --git a/build/makefiles/Actors/makefile_cluster b/build/makefiles/Actors/makefile_cluster index 780e755f4..c3c899e59 100644 --- a/build/makefiles/Actors/makefile_cluster +++ b/build/makefiles/Actors/makefile_cluster @@ -164,7 +164,7 @@ SUMMA_FILEACCESS_INTERFACE = \ read_attrb.f90 \ read_force.f90 \ read_param.f90 \ - read_initcond.f90 \ + read_icondFromStructure.f90 \ writeOutputFromOutputStructure.f90 \ write_to_netcdf.f90 @@ -178,8 +178,8 @@ JOB_INTERFACE = $(patsubst %, $(JOB_ACTOR_DIR)/%, $(SUMMA_JOB_INTERFACE)) SUMMA_HRU_INTERFACE = \ cppwrap_hru.f90 \ hru_actor.f90 \ - init_hru_actor.f90 \ - outputStrucWrite.f90 \ + hru_init.f90 \ + hru_modelwrite.f90 \ hru_writeOutput.f90 diff --git a/build/makefiles/Actors/makefile_richardson b/build/makefiles/Actors/makefile_richardson deleted file mode 100644 index e2a1f2a12..000000000 --- a/build/makefiles/Actors/makefile_richardson +++ /dev/null @@ -1,425 +0,0 @@ -#### parent directory of the 'build' directory #### -ROOT_DIR = /u1/kck540/Summa-Projects/Summa-Actors-Main - -#### Compilers #### -FC = gfortran # Fortran -CC = g++ # C++ - -#### Includes AND Libraries #### -INCLUDES = -I/usr/include -I/usr/local/include -LIBRARIES = -L/usr/lib -L/usr/local/lib -lnetcdff -lopenblas - -ACTORS_INCLUDES = -I/usr/include -I/usr/local/include -ACTORS_LIBRARIES = -L/usr/lib -L/usr/local/lib -L$(ROOT_DIR)/bin -lcaf_core -lcaf_io -lsumma -lopenblas -lnetcdff - - -# Production runs -FLAGS_NOAH = -g -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_COMM = -g -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_SUMMA = -g -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_ACTORS = -g -O3 -Wfatal-errors -std=c++17 - -# Debug runs -# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC -# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC -# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC -# FLAGS_ACTORS = -g -O0 -Wall -std=c++17 - - - -#======================================================================== -# PART 1: Define directory paths -#======================================================================== - -# Core directory that contains source code -F_KORE_DIR = $(ROOT_DIR)/build/source - -# Location of the compiled modules -MOD_PATH = $(ROOT_DIR)/build - -# Define the directory for the executables -EXE_PATH = $(ROOT_DIR)/bin - -#################################################################################################### -###################################### Assemble Fortran Files ###################################### -#################################################################################################### -# Define directories -DRIVER_DIR = $(F_KORE_DIR)/driver -HOOKUP_DIR = $(F_KORE_DIR)/hookup -NETCDF_DIR = $(F_KORE_DIR)/netcdf -DSHARE_DIR = $(F_KORE_DIR)/dshare -NUMREC_DIR = $(F_KORE_DIR)/numrec -NOAHMP_DIR = $(F_KORE_DIR)/noah-mp -ENGINE_DIR = $(F_KORE_DIR)/engine -ACTORS_DIR = $(F_KORE_DIR)/actors - -SUMMA_DSHARE_DIR = $(MOD_PATH)/summa/build/source/dshare -SUMMA_ENGINE_DIR = $(MOD_PATH)/summa/build/source/engine - - -JOB_ACTOR_DIR = $(ACTORS_DIR)/job_actor -FILE_ACCESS_DIR = $(ACTORS_DIR)/file_access_actor/fortran_code -HRU_ACTOR_DIR = $(ACTORS_DIR)/hru_actor/fortran_code -GRU_ACTOR_DIR = $(ACTORS_DIR)/gru_actor - -# utilities -SUMMA_NRUTIL= \ - nrtype.f90 \ - f2008funcs.f90 \ - nr_utility.f90 \ - -NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) - -# Numerical recipes procedures -# NOTE: all numerical recipes procedures are now replaced with free versions -SUMMA_NRPROC= \ - expIntegral.f90 \ - spline_int.f90 -NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) - -# Hook-up modules (set files and directory paths) -SUMMA_HOOKUP= \ - ascii_util.f90 \ - summaActors_FileManager.f90 -HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) - -# Data modules -SUMMA_DATAMS= \ - var_lookup.f90 \ - multiconst.f90 \ - data_types.f90 \ - globalData.f90 \ - flxMapping.f90 -DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) - -IXNAME_METADAT = \ - get_ixname.f90 \ - popMetadat.f90 -IXNAME_METADAT_DATAMS = $(patsubst %, $(SUMMA_DSHARE_DIR)/%, $(IXNAME_METADAT)) - -SUMMA_DEPEND_ON_FILEMANAGER= \ - outpt_stat.f90 -DEPEND_ON_FILEMANAGER = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DEPEND_ON_FILEMANAGER)) - - -# utility modules -SUMMA_UTILMS= \ - time_utils.f90 \ - mDecisions.f90 \ - snow_utils.f90 \ - soil_utils.f90 \ - updatState.f90 \ - matrixOper.f90 -UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) - -# Model guts -SUMMA_MODGUT= \ - MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) - - - -# Solver - Split up to compiling with old summa files vs new summa files -VEG_PHENLGY= \ - vegPhenlgy.f90 -VEG_PHENLGY_SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(VEG_PHENLGY)) -SNOW_DEPTH= \ - sundials/computSnowDepth.f90 -SNOW_DEPTH_SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SNOW_DEPTH)) -SUMMA_SOLVER= \ - diagn_evar.f90 \ - stomResist.f90 \ - groundwatr.f90 \ - vegSWavRad.f90 \ - vegNrgFlux.f90 \ - ssdNrgFlux.f90 \ - vegLiqFlux.f90 \ - snowLiqFlx.f90 \ - soilLiqFlx.f90 \ - bigAquifer.f90 \ - computFlux.f90 \ - computResid.f90 \ - computJacob.f90 \ - eval8summa.f90 \ - summaSolve.f90 \ - systemSolv.f90 \ - varSubstep.f90 \ - opSplittin.f90 -SOLVER = $(patsubst %, $(SUMMA_ENGINE_DIR)/%, $(SUMMA_SOLVER)) -SUMMA_ACTORS_SOLVER = \ - coupled_em.f90 -COUPLED_EM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_ACTORS_SOLVER)) - - - -# Interface code for Fortran-C++ -SUMMA_INTERFACE= \ - cppwrap_datatypes.f90 \ - cppwrap_auxiliary.f90 \ - cppwrap_metadata.f90 \ - -INTERFACE = $(patsubst %, $(ACTORS_DIR)/global/%, $(SUMMA_INTERFACE)) - - -SUMMA_FILEACCESS_INTERFACE = \ - output_structure.f90 \ - cppwrap_fileAccess.f90 \ - read_attrb.f90 \ - read_force.f90 \ - read_param.f90 \ - read_initcond.f90 \ - writeOutputFromOutputStructure.f90 \ - write_to_netcdf.f90 - -FILEACCESS_INTERFACE = $(patsubst %, $(FILE_ACCESS_DIR)/%, $(SUMMA_FILEACCESS_INTERFACE)) - -SUMMA_JOB_INTERFACE = \ - job_actor.f90 - -JOB_INTERFACE = $(patsubst %, $(JOB_ACTOR_DIR)/%, $(SUMMA_JOB_INTERFACE)) - -SUMMA_HRU_INTERFACE = \ - cppwrap_hru.f90 \ - hru_actor.f90 \ - init_hru_actor.f90 \ - outputStrucWrite.f90 \ - hru_writeOutput.f90 - - -HRU_INTERFACE = $(patsubst %, $(HRU_ACTOR_DIR)/%, $(SUMMA_HRU_INTERFACE)) - -SUMMA_GRU_INTERFACE = \ - gru_actor.f90 \ - -GRU_INTERFACE = $(patsubst %, $(GRU_ACTOR_DIR)/%, $(SUMMA_GRU_INTERFACE)) - - - -# Define routines for SUMMA preliminaries -SUMMA_PRELIM= \ - conv_funcs.f90 \ - sunGeomtry.f90 \ - convE2Temp.f90 \ - allocspaceActors.f90 \ - alloc_fileAccess.f90 \ - checkStruc.f90 \ - childStruc.f90 \ - ffile_info.f90 \ - read_dimension.f90 \ - read_pinit.f90 \ - pOverwrite.f90 \ - paramCheck.f90 \ - check_icondActors.f90 -PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) - -SUMMA_NOAHMP= \ - module_model_constants.F \ - module_sf_noahutl.F \ - module_sf_noahlsm.F \ - module_sf_noahmplsm.F - -NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) - -# Define routines for the SUMMA model runs -DERIV_FORCE = \ - derivforce.f90 \ - sundials/t2enthalpy.f90 -DERIV_FORCE_MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(DERIV_FORCE)) - -SUMMA_MODRUN = \ - indexState.f90 \ - getVectorz.f90 \ - updateVars.f90 \ - var_derive.f90 \ - snowAlbedo.f90 \ - canopySnow.f90 \ - tempAdjust.f90 \ - snwCompact.f90 \ - layerMerge.f90 \ - layerDivide.f90 \ - volicePack.f90 \ - qTimeDelay.f90 -MODRUN = $(patsubst %, $(SUMMA_ENGINE_DIR)/%, $(SUMMA_MODRUN)) - -# Define NetCDF routines -# OutputStrucWrite is not a netcdf subroutine and should be -# moved -SUMMA_NETCDF = \ - netcdf_util.f90 \ - def_output.f90 \ - writeOutput.f90 \ - read_icondActors.f90 -NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) - -# ... stitch together common programs -COMM_ALL = $(NRUTIL) $(NRPROC) $(DATAMS) \ - $(INTERFACE) $(HOOKUP) $(IXNAME_METADAT_DATAMS) $(DEPEND_ON_FILEMANAGER) \ - $(TIME_UTILS_UTILMS) $(M_DECISIONS_UTILMS) $(UTILMS) -# ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(DERIV_FORCE_MODRUN) $(MODRUN) \ - $(VEG_PHENLGY_SOLVER) $(SOLVER) $(SNOW_DEPTH_SOLVER) $(COUPLED_EM) - -# Define the driver routine -SUMMA_DRIVER= \ - summaActors_type.f90 \ - summaActors_util.f90 \ - summaActors_globalData.f90 \ - SummaActors_setup.f90 \ - summaActors_restart.f90 \ - SummaActors_modelRun.f90 \ - summaActors_alarms.f90 - - -DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) - -#################################################################################################### -###################################### Assemble Fortran Files ###################################### -#################################################################################################### - -#################################################################################################### -######################################## Assemble C++ Files ######################################## -#################################################################################################### - -INCLUDE_DIR = $(ROOT_DIR)/build/includes -SOURCE_DIR = $(ROOT_DIR)/build/source/actors - - -GLOBAL_INCLUDES = -I$(INCLUDE_DIR)/global -GLOBAL = $(SOURCE_DIR)/global/global.cpp -TIMEINFO = $(SOURCE_DIR)/global/timing_info.cpp -SETTINGS_FILES = $(SOURCE_DIR)/global/settings_functions.cpp -AUXILARY = $(SOURCE_DIR)/global/auxiliary.cpp - -SUMMA_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/summa_actor -SUMMA_ACTOR = $(SOURCE_DIR)/summa_actor/summa_actor.cpp -SUMMA_CLIENT = $(SOURCE_DIR)/summa_actor/summa_client.cpp -SUMMA_SERVER = $(SOURCE_DIR)/summa_actor/summa_server.cpp -SUMMA_BACKUP_SERVER = $(SOURCE_DIR)/summa_actor/summa_backup_server.cpp - -BATCH = $(SOURCE_DIR)/summa_actor/batch/batch.cpp -BATCH_CONTAINER = $(SOURCE_DIR)/summa_actor/batch/batch_container.cpp - -CLIENT = $(SOURCE_DIR)/summa_actor/client/client.cpp -CLIENT_CONTAINER = $(SOURCE_DIR)/summa_actor/client/client_container.cpp - -GRU_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/gru_actor -GRU_ACTOR = $(SOURCE_DIR)/gru_actor/gru_actor.cpp - -JOB_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/job_actor -JOB_ACTOR = $(SOURCE_DIR)/job_actor/job_actor.cpp -GRUinfo = $(SOURCE_DIR)/job_actor/GRUinfo.cpp - -FILE_ACCESS_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/file_access_actor -FILE_ACCESS_ACTOR = $(SOURCE_DIR)/file_access_actor/cpp_code/file_access_actor.cpp -FORCING_FILE_INFO = $(SOURCE_DIR)/file_access_actor/cpp_code/forcing_file_info.cpp -OUTPUT_CONTAINER = $(SOURCE_DIR)/file_access_actor/cpp_code/output_container.cpp - -HRU_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/hru_actor -HRU_ACTOR = $(SOURCE_DIR)/hru_actor/cpp_code/hru_actor.cpp - -MAIN = $(F_KORE_DIR)/actors/main.cpp - -ACTOR_TEST = $(F_KORE_DIR)/testing/testing_main.cc -#################################################################################################### -######################################## Assemble C++ Files ######################################## -#################################################################################################### - - -#======================================================================== -# PART 3: compilation -#====================================================================== -all: fortran cpp - -fortran: compile_noah compile_comm compile_summa link clean_fortran - -cpp: compile_globals compile_hru_actor compile_gru_actor compile_file_access_actor compile_job_actor compile_summa_actor \ - compile_summa_client compile_summa_server compile_main link_cpp clean_cpp - -test: actors_test actors_testLink actorsClean - -################################################################################################################### -############################################## COMPILE SUMMA-Fortran ############################################## -################################################################################################################### -compile_noah: - $(FC) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) - -# compile common routines -compile_comm: - $(FC) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) - -# compile SUMMA routines -compile_summa: - $(FC) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(JOB_INTERFACE) $(FILEACCESS_INTERFACE) $(HRU_INTERFACE) $(GRU_INTERFACE) $(INCLUDES) - -# generate library -link: - $(FC) -shared *.o -o libsumma.so - mv libsumma.so $(ROOT_DIR)/bin - -# Remove object files -clean_fortran: - rm -f *.o *.mod soil_veg_gen_parm__genmod.f90 -################################################################################################################### -############################################## COMPILE SUMMA-Fortran ############################################## -################################################################################################################### - - -################################################################################################################### -################################################ COMPILE SUMMA-C++ ################################################ -################################################################################################################### -compile_globals: - $(CC) $(FLAGS_ACTORS) -c $(GLOBAL) $(TIMEINFO) $(AUXILARY) $(SETTINGS_FILES) $(GLOBAL_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_gru_actor: - $(CC) $(FLAGS_ACTORS) -c $(GRU_ACTOR) $(HRU_ACTOR_INCLUDES) $(GRU_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_hru_actor: - $(CC) $(FLAGS_ACTORS) -c $(HRU_ACTOR) $(HRU_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_file_access_actor: - $(CC) $(FLAGS_ACTORS) -c $(OUTPUT_CONTAINER) $(FILE_ACCESS_ACTOR) $(FORCING_FILE_INFO) $(OUTPUT_MANAGER) \ - $(FILE_ACCESS_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_job_actor: - $(CC) $(FLAGS_ACTORS) -c $(JOB_ACTOR) $(GRUinfo) $(JOB_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) \ - $(FILE_ACCESS_ACTOR_INCLUDES) $(HRU_ACTOR_INCLUDES) $(GRU_ACTOR_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_summa_actor: - $(CC) $(FLAGS_ACTORS) -c $(SUMMA_ACTOR) $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) \ - $(JOB_ACTOR_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_summa_client: - $(CC) $(FLAGS_ACTORS) -c $(SUMMA_CLIENT) $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) - -compile_summa_server: - $(CC) $(FLAGS_ACTORS) -c $(SUMMA_SERVER) $(SUMMA_BACKUP_SERVER) $(BATCH) $(CLIENT) $(BATCH_CONTAINER) $(CLIENT_CONTAINER) \ - $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) - -compile_main: - $(CC) $(FLAGS_ACTORS) -c $(MAIN) $(GLOBAL_INCLUDES) $(SUMMA_ACTOR_INCLUDES) $(JOB_ACTOR_INCLUDES) - -link_cpp: - $(CC) $(FLAGS_ACTORS) -Wl,-rpath='$(ROOT_DIR)/bin:/usr/local/lib' -o summaMain *.o $(ACTORS_LIBRARIES) - mv summaMain $(ROOT_DIR)/bin - -clean_cpp: - rm *.o -################################################################################################################### -################################################ COMPILE SUMMA-C++ ################################################ -################################################################################################################### - - -################################################################################################################### -################################################## COMPILE TESTS ################################################## -################################################################################################################### -actors_test: - $(CC) $(FLAGS_ACTORS) -c $(ACTOR_TEST) -std=c++17 $(ACTORS_INCLUDES) - -actors_testLink: - $(CC) -o summaTest *.o $(ACTORS_LIBRARIES) - -clean_lib: - rm *.so - - - - diff --git a/build/makefiles/BE/makefile_old_summa b/build/makefiles/BE/makefile_old_summa index d61c29a49..46c912fb7 100644 --- a/build/makefiles/BE/makefile_old_summa +++ b/build/makefiles/BE/makefile_old_summa @@ -165,7 +165,7 @@ SUMMA_FILEACCESS_INTERFACE = \ read_attrb.f90 \ read_force.f90 \ read_param.f90 \ - read_initcond.f90 \ + read_icondFromStructure.f90 \ writeOutputFromOutputStructure.f90 \ write_to_netcdf.f90 @@ -179,8 +179,8 @@ JOB_INTERFACE = $(patsubst %, $(JOB_ACTOR_DIR)/%, $(SUMMA_JOB_INTERFACE)) SUMMA_HRU_INTERFACE = \ cppwrap_hru.f90 \ hru_actor.f90 \ - init_hru_actor.f90 \ - outputStrucWrite.f90 \ + hru_init.f90 \ + hru_modelwrite.f90 \ hru_writeOutput.f90 From fc1af42ec7a31f9d5c829241350c465af5a857d2 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 12 May 2023 22:16:35 +0900 Subject: [PATCH 0725/1472] organizing --- build/cmake/CMakeLists.txt | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 8f80892d7..1f1c3f230 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -412,27 +412,27 @@ set(HRU_INTERFACE ${HRU_ACTOR_DIR}/fortran_code/hru_actor.f90 ${HRU_ACTOR_DIR}/fortran_code/hru_init.f90 ${HRU_ACTOR_DIR}/fortran_code/hru_modelRun.f90 + ${HRU_ACTOR_DIR}/fortran_code/hru_modelwrite.f90 ${HRU_ACTOR_DIR}/fortran_code/hru_restart.f90 ${HRU_ACTOR_DIR}/fortran_code/hru_setup.f90 - ${HRU_ACTOR_DIR}/fortran_code/hru_modelwrite.f90 ${HRU_ACTOR_DIR}/fortran_code/hru_writeOutput.f90) # Actors actual actor modules set(ACTORS_GLOBAL + ${ACTORS_DIR}/global/auxiliary.cpp ${ACTORS_DIR}/global/global.cpp - ${ACTORS_DIR}/global/timing_info.cpp ${ACTORS_DIR}/global/message_atoms.cpp ${ACTORS_DIR}/global/settings_functions.cpp - ${ACTORS_DIR}/global/auxiliary.cpp) + ${ACTORS_DIR}/global/timing_info.cpp) set(SUMMA_ACTOR - ${ACTORS_DIR}/summa_actor/summa_actor.cpp - ${ACTORS_DIR}/summa_actor/summa_client.cpp - ${ACTORS_DIR}/summa_actor/summa_server.cpp - ${ACTORS_DIR}/summa_actor/summa_backup_server.cpp ${ACTORS_DIR}/summa_actor/batch/batch.cpp ${ACTORS_DIR}/summa_actor/batch/batch_container.cpp ${ACTORS_DIR}/summa_actor/client/client.cpp - ${ACTORS_DIR}/summa_actor/client/client_container.cpp) + ${ACTORS_DIR}/summa_actor/client/client_container.cpp + ${ACTORS_DIR}/summa_actor/summa_actor.cpp + ${ACTORS_DIR}/summa_actor/summa_backup_server.cpp + ${ACTORS_DIR}/summa_actor/summa_client.cpp + ${ACTORS_DIR}/summa_actor/summa_server.cpp) set(HRU_ACTOR ${ACTORS_DIR}/hru_actor/cpp_code/hru_actor.cpp) set(GRU_ACTOR @@ -442,8 +442,8 @@ set(FILE_ACCESS_ACTOR ${ACTORS_DIR}/file_access_actor/cpp_code/forcing_file_info.cpp ${ACTORS_DIR}/file_access_actor/cpp_code/output_container.cpp) set(JOB_ACTOR - ${ACTORS_DIR}/job_actor/job_actor.cpp - ${ACTORS_DIR}/job_actor/GRU.cpp) + ${ACTORS_DIR}/job_actor/GRU.cpp + ${ACTORS_DIR}/job_actor/job_actor.cpp) #========================================================================================= From 726931c9f27be12ebf9e7f703b9eaebebb13f853 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 13 May 2023 12:46:35 +0900 Subject: [PATCH 0726/1472] names --- build/cmake/CMakeLists.txt | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 1f1c3f230..34504d8b5 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -308,11 +308,11 @@ set(PRELIM ${ENGINE_DIR}/convE2Temp.f90 ${SUB_ENGINE_DIR}/ffile_info.f90 ${ENGINE_DIR}/read_pinit.f90 + ${ENGINE_DIR}/read_attrb.f90 ${ENGINE_DIR}/paramCheck.f90 ${ENGINE_DIR}/pOverwrite.f90 ${ENGINE_DIR}/sunGeomtry.f90) set(PRELIM_NOT_ACTORS - ${ENGINE_DIR}/read_attrb.f90 ${ENGINE_DIR}/read_param.f90) set(PRELIM_ACTORS ${SUB_ENGINE_DIR}/alloc_fileAccess.f90) @@ -378,9 +378,9 @@ set(SOLVER_SUNDIALS # Driver support modules set(DRIVER ${DRIVER_DIR}/summa_alarms.f90 - ${SUB_DRIVER_DIR}/summa_globalData.f90) + ${SUB_DRIVER_DIR}/summa_globalData.f90 ${SUB_DRIVER_DIR}/summa_type.f90 - ${SUB_DRIVER_DIR}/summa_util.f90 + ${SUB_DRIVER_DIR}/summa_util.f90) set(DRIVER_NOT_ACTORS ${DRIVER_DIR}/summa_defineOutput.f90 ${DRIVER_DIR}/summa_init.f90 @@ -408,6 +408,8 @@ set(FILE_ACCESS_INTERFACE ${FILE_ACCESS_DIR}/fortran_code/writeOutputFromOutputStructure.f90) set(JOB_INTERFACE ${JOB_ACTOR_DIR}/job_actor.f90) +set(GRU_INTERFACE + ${GRU_ACTOR_DIR}/gru_actor.f90) set(HRU_INTERFACE ${HRU_ACTOR_DIR}/fortran_code/hru_actor.f90 ${HRU_ACTOR_DIR}/fortran_code/hru_init.f90 @@ -433,10 +435,6 @@ set(SUMMA_ACTOR ${ACTORS_DIR}/summa_actor/summa_backup_server.cpp ${ACTORS_DIR}/summa_actor/summa_client.cpp ${ACTORS_DIR}/summa_actor/summa_server.cpp) -set(HRU_ACTOR - ${ACTORS_DIR}/hru_actor/cpp_code/hru_actor.cpp) -set(GRU_ACTOR - ${ACTORS_DIR}/gru_actor/gru_actor.cpp) set(FILE_ACCESS_ACTOR ${ACTORS_DIR}/file_access_actor/cpp_code/file_access_actor.cpp ${ACTORS_DIR}/file_access_actor/cpp_code/forcing_file_info.cpp @@ -444,6 +442,10 @@ set(FILE_ACCESS_ACTOR set(JOB_ACTOR ${ACTORS_DIR}/job_actor/GRU.cpp ${ACTORS_DIR}/job_actor/job_actor.cpp) +set(GRU_ACTOR + ${ACTORS_DIR}/gru_actor/gru_actor.cpp) +set(HRU_ACTOR + ${ACTORS_DIR}/hru_actor/cpp_code/hru_actor.cpp) #========================================================================================= @@ -475,10 +477,10 @@ set(SUMMA_ALL if(CMAKE_BUILD_TYPE MATCHES Actors) set(SUMMA_ALL ${SUMMA_ALL} ${PRELIM_ACTORS} - ${JOB_INTERFACE} ${FILE_ACCESS_INTERFACE} - ${HRU_INTERFACE} - ${GRU_INTERFACE}) + ${JOB_INTERFACE} + ${GRU_INTERFACE} + ${HRU_INTERFACE}) else() set(SUMMA_ALL ${SUMMA_ALL} ${PRELIM_NOT_ACTORS} @@ -570,10 +572,10 @@ elseif(CMAKE_BUILD_TYPE MATCHES Actors) add_executable(${EXEC_NAME} ${MAIN_ACTOR} ${ACTORS_GLOBAL} - ${HRU_ACTOR} - ${GRU_ACTOR} ${FILE_ACCESS_ACTOR} ${JOB_ACTOR} + ${GRU_ACTOR} + ${HRU_ACTOR} ${SUMMA_ACTOR} ${SUMMA_CLIENT} ${SUMMA_SERVER}) From 7f930e0d7bbf2cfbb4e87b4072142f59fc44edd3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 13 May 2023 12:54:34 +0900 Subject: [PATCH 0727/1472] missed sub directory --- build/cmake/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 34504d8b5..122be0fc6 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -308,7 +308,7 @@ set(PRELIM ${ENGINE_DIR}/convE2Temp.f90 ${SUB_ENGINE_DIR}/ffile_info.f90 ${ENGINE_DIR}/read_pinit.f90 - ${ENGINE_DIR}/read_attrb.f90 + ${SUB_ENGINE_DIR}/read_attrb.f90 ${ENGINE_DIR}/paramCheck.f90 ${ENGINE_DIR}/pOverwrite.f90 ${ENGINE_DIR}/sunGeomtry.f90) From 0ef5a5c35a40b7b5c938406b8d4d8f22c02c6956 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 13 May 2023 13:14:29 +0900 Subject: [PATCH 0728/1472] rename and error in directory search --- build/cmake/CMakeLists.txt | 8 ++++++-- build/source/dshare/{type4IDA.f90 => type4ida.f90} | 0 2 files changed, 6 insertions(+), 2 deletions(-) rename build/source/dshare/{type4IDA.f90 => type4ida.f90} (100%) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 122be0fc6..915e626d7 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -128,8 +128,10 @@ if(CMAKE_BUILD_TYPE MATCHES Sundials) message("\nFound Sundials directory at ${DIR_SUNDIALS}") break() endif() - message(FATAL_ERROR "\nDid not find Sundials directory, edit CMakeLists.txt to include path") endforeach() + if(NOT DIR_SUNDIALS) + message(FATAL_ERROR "Did not find Sundials directory, edit CMakeLists.txt to add path") + endif() endif() # Set libraries for all builds @@ -188,8 +190,10 @@ else() message("\nFound Actors directory at ${DIR_ACTORS}") break() endif() - message(FATAL_ERROR "\nDid not find Actors directory, edit CMakeLists.txt to include path") endforeach() + if(NOT DIR_ACTORS) + message(FATAL_ERROR "Did not find Actors directory, edit CMakeLists.txt to add path") + endif() link_directories(${DIR_ACTORS}/lib) set(INC_ACTORS ${CAF_INCLUDES} ${PARENT_DIR}/build/includes/global ${PARENT_DIR}/build/includes/summa_actor ${PARENT_DIR}/build/includes/gru_actor ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) set(LIB_ACTORS ${CAF_LIBRARIES} -lcaf_core -lcaf_io) diff --git a/build/source/dshare/type4IDA.f90 b/build/source/dshare/type4ida.f90 similarity index 100% rename from build/source/dshare/type4IDA.f90 rename to build/source/dshare/type4ida.f90 From 89070ab03704520c97bf6e9f41c0ef547b7df745 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 13 May 2023 13:49:14 +0900 Subject: [PATCH 0729/1472] fix root part got screwed up in merge --- .../source/engine/summaSolveSundials4ida.f90 | 35 +++---------------- build/source/engine/tol4ida.f90 | 28 +++++++-------- 2 files changed, 19 insertions(+), 44 deletions(-) diff --git a/build/source/engine/summaSolveSundials4ida.f90 b/build/source/engine/summaSolveSundials4ida.f90 index f7a7594dd..e703c13ae 100644 --- a/build/source/engine/summaSolveSundials4ida.f90 +++ b/build/source/engine/summaSolveSundials4ida.f90 @@ -79,13 +79,8 @@ module summaSolveSundials4ida_module private::setInitialCondition private::setSolverParams private::find_rootdir -<<<<<<< HEAD:build/source/engine/summaSolveSundialsIDA.f90 - public::layerDisCont4IDA - public::summaSolveSundialsIDA -======= public::layerDisCont4ida public::summaSolveSundials4ida ->>>>>>> actors_ngen:build/source/engine/summaSolveSundials4ida.f90 contains @@ -149,13 +144,8 @@ subroutine summaSolveSundials4ida( & USE allocspace_module,only:allocLocal ! allocate local data structures USE eval8summaSundials_module,only:eval8summa4ida ! DAE/ODE functions USE eval8summaSundials_module,only:eval8summaSundials ! residual of DAE -<<<<<<< HEAD:build/source/engine/summaSolveSundialsIDA.f90 - USE computJacobSundials_module,only:computJacob4IDA ! system Jacobian - USE tol4IDA_module,only:computWeight4IDA ! weigth required for tolerances -======= USE computJacobSundials_module,only:computJacob4ida ! system Jacobian USE tol4ida_module,only:computWeight4ida ! weight required for tolerances ->>>>>>> actors_ngen:build/source/engine/summaSolveSundials4ida.f90 USE var_derive_module,only:calcHeight ! height at layer interfaces and layer mid-point !======= Declarations ========= @@ -400,7 +390,7 @@ subroutine summaSolveSundials4ida( & allocate( rootsfound(nRoot) ) allocate( rootdir(nRoot) ) rootdir = 0 - retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4IDA)) + retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4ida)) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif else ! will not use, allocate at something nRoot = 1 @@ -440,13 +430,8 @@ subroutine summaSolveSundials4ida( & ! Set the user-supplied Jacobian routine if(.not.use_fdJac)then -<<<<<<< HEAD:build/source/engine/summaSolveSundialsIDA.f90 - retval = FIDASetJacFn(ida_mem, c_funloc(computJacob4IDA)) - if (retval /= 0) then; err=20; message='summaSolveSundialsIDA: error in FIDASetJacFn'; return; endif -======= retval = FIDASetJacFn(ida_mem, c_funloc(computJacob4ida)) if (retval /= 0) then; err=20; message='summaSolveSundials4ida: error in FIDASetJacFn'; return; endif ->>>>>>> actors_ngen:build/source/engine/summaSolveSundials4ida.f90 endif ! Create Newton SUNNonlinearSolver object @@ -629,17 +614,10 @@ subroutine summaSolveSundials4ida( & retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAReInit'; return; endif if(dt_last(1) < 0.1_rkind)then ! don't keep calling if step is small (more accurate with this tiny but getting hung up) -<<<<<<< HEAD:build/source/engine/summaSolveSundialsIDA.f90 - retval = FIDARootInit(ida_mem, 0, c_funloc(layerDisCont4IDA)) - tinystep = .true. - else - retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4IDA)) -======= retval = FIDARootInit(ida_mem, 0, c_funloc(layerDisCont4ida)) tinystep = .true. else retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4ida)) ->>>>>>> actors_ngen:build/source/engine/summaSolveSundials4ida.f90 tinystep = .false. endif if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif @@ -808,7 +786,6 @@ subroutine find_rootdir(eqns_data,rootdir) use globalData,only:integerMissing ! missing integer use var_lookup,only:iLookINDEX ! named variables for structure elements use multiconst,only:Tfreeze ! freezing point of pure water (K) -<<<<<<< HEAD:build/source/engine/summaSolveSundialsIDA.f90 !======= Declarations ========= implicit none @@ -874,7 +851,7 @@ end subroutine find_rootdir ! ---------------------------------------------------------------------------------------- -! layerDisCont4IDA: The root function routine to find soil matrix potential = 0, +! layerDisCont4ida: The root function routine to find soil matrix potential = 0, ! soil temp = critical frozen point, and snow and veg temp = Tfreeze ! ---------------------------------------------------------------------------------------- ! Return values: @@ -882,8 +859,8 @@ end subroutine find_rootdir ! 1 = recoverable error, ! -1 = non-recoverable error ! ---------------------------------------------------------------------------------------- - integer(c_int) function layerDisCont4IDA(t, sunvec_u, sunvec_up, gout, user_data) & - result(ierr) bind(C,name='layerDisCont4IDA') + integer(c_int) function layerDisCont4ida(t, sunvec_u, sunvec_up, gout, user_data) & + result(ierr) bind(C,name='layerDisCont4ida') !======= Inclusions =========== use, intrinsic :: iso_c_binding @@ -968,9 +945,7 @@ integer(c_int) function layerDisCont4IDA(t, sunvec_u, sunvec_up, gout, user_data ierr = 0 return - end function layerDisCont4IDA -======= ->>>>>>> actors_ngen:build/source/engine/summaSolveSundials4ida.f90 + end function layerDisCont4ida !======= Declarations ========= implicit none diff --git a/build/source/engine/tol4ida.f90 b/build/source/engine/tol4ida.f90 index c33481089..59b41cf41 100644 --- a/build/source/engine/tol4ida.f90 +++ b/build/source/engine/tol4ida.f90 @@ -1,9 +1,9 @@ -module tol4IDA_module +module tol4ida_module !======= Inclusions =========== use, intrinsic :: iso_c_binding use nrtype -use type4IDA +use type4ida ! missing values USE globalData,only:integerMissing ! missing integer @@ -61,28 +61,28 @@ module tol4IDA_module ! privacy implicit none private -public::computWeight4IDA -public::popTol4IDA +public::computWeight4ida +public::popTol4ida contains ! ********************************************************************************************************** -! public function computWeight4IDA: compute w_i = 1 / ( rtol_i * y_i + atol_i ) +! public function computWeight4ida: compute w_i = 1 / ( rtol_i * y_i + atol_i ) ! ********************************************************************************************************** ! Return values: ! 0 = success, ! -1 = non-recoverable error, NaN or negative values ! ---------------------------------------------------------------- -integer(c_int) function computWeight4IDA(sunvec_y, sunvec_ewt, user_data) & - result(ierr) bind(C,name='computWeight4IDA') +integer(c_int) function computWeight4ida(sunvec_y, sunvec_ewt, user_data) & + result(ierr) bind(C,name='computWeight4ida') !======= Inclusions =========== use, intrinsic :: iso_c_binding use fsundials_nvector_mod use fnvector_serial_mod use nrtype - use type4IDA + use type4ida !======= Declarations ========= implicit none @@ -118,13 +118,13 @@ integer(c_int) function computWeight4IDA(sunvec_y, sunvec_ewt, user_data) & ierr = 0 return -end function computWeight4IDA +end function computWeight4ida ! ********************************************************************************************************** -! public subroutine popTol4IDA: populate tolerances for state vectors +! public subroutine popTol4ida: populate tolerances for state vectors ! ********************************************************************************************************** -subroutine popTol4IDA(& +subroutine popTol4ida(& ! input: data structures nState, & ! intent(in): number of desired state variables prog_data, & ! intent(in): model prognostic variables for a local HRU @@ -206,7 +206,7 @@ subroutine popTol4IDA(& ) ! end association with variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message='popTol4IDA/' + err=0; message='popTol4ida/' ! ----- ! * initialize state vectors... @@ -280,7 +280,7 @@ subroutine popTol4IDA(& endif end associate fixedLength ! end association to variables in the data structure where vector length does not change -end subroutine popTol4IDA +end subroutine popTol4ida -end module tol4IDA_module +end module tol4ida_module From edcc3e66d13589098f6fd03070b21700aee20141 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 13 May 2023 13:54:12 +0900 Subject: [PATCH 0730/1472] still fixing --- .../source/engine/summaSolveSundials4ida.f90 | 160 ------------------ 1 file changed, 160 deletions(-) diff --git a/build/source/engine/summaSolveSundials4ida.f90 b/build/source/engine/summaSolveSundials4ida.f90 index e703c13ae..99c4a03c7 100644 --- a/build/source/engine/summaSolveSundials4ida.f90 +++ b/build/source/engine/summaSolveSundials4ida.f90 @@ -850,166 +850,6 @@ subroutine find_rootdir(eqns_data,rootdir) end subroutine find_rootdir -! ---------------------------------------------------------------------------------------- -! layerDisCont4ida: The root function routine to find soil matrix potential = 0, -! soil temp = critical frozen point, and snow and veg temp = Tfreeze -! ---------------------------------------------------------------------------------------- -! Return values: -! 0 = success, -! 1 = recoverable error, -! -1 = non-recoverable error -! ---------------------------------------------------------------------------------------- - integer(c_int) function layerDisCont4ida(t, sunvec_u, sunvec_up, gout, user_data) & - result(ierr) bind(C,name='layerDisCont4ida') - - !======= Inclusions =========== - use, intrinsic :: iso_c_binding - use fsundials_nvector_mod - use fnvector_serial_mod - use soil_utils_module,only:crit_soilT ! compute the critical temperature below which ice exists - use globalData,only:integerMissing ! missing integer - use var_lookup,only:iLookINDEX ! named variables for structure elements - use multiconst,only:Tfreeze ! freezing point of pure water (K) - - !======= Declarations ========= - implicit none - - ! calling variables - real(c_double), value :: t ! current time - type(N_Vector) :: sunvec_u ! solution N_Vector - type(N_Vector) :: sunvec_up ! derivative N_Vector - real(c_double) :: gout(999) ! root function values, if (nVeg + nSnow + 2*nSoil)>999, problem - type(c_ptr), value :: user_data ! user-defined data - - ! local variables - integer(i4b) :: i,ind ! indices - integer(i4b) :: nState ! number of states - integer(i4b) :: nSnow ! number of snow layers - integer(i4b) :: nSoil ! number of soil layers - real(rkind) :: xPsi ! matric head at layer (m) - real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) - - ! pointers to data in SUNDIALS vectors - real(c_double), pointer :: uu(:) - type(eqnsData), pointer :: eqns_data ! equations data - - !======= Internals ============ - ! get equations data from user-defined data - call c_f_pointer(user_data, eqns_data) - nState = eqns_data%nState - nSnow = eqns_data%nSnow - nSoil = eqns_data%nSoil - - ! get data array from SUNDIALS vector - uu(1:nState) => FN_VGetArrayPointer(sunvec_u) - - ! initialize - ind = 0 - - ! identify the critical point when vegetation begins to freeze - if(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then - ind = ind+1 - gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)) - Tfreeze - endif - - if(nSnow>0)then - do i = 1,nSnow - ! identify the critical point when the snow layer begins to freeze - if(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing)then - ind = ind+1 - gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)) - Tfreeze - endif - end do - endif - - if(nSoil>0)then - do i = 1,nSoil - ! identify the critical point when soil matrix potential goes below 0 and Tfreeze depends only on temp - if (eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing)then - ind = ind+1 - xPsi = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)) - gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)) - else - xPsi = eqns_data%prog_data%var(iLookPROG%mLayerMatricHead)%dat(i) - endif - ! identify the critical point when the soil layer begins to freeze - if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing)then - ind = ind+1 - TcSoil = crit_soilT(xPsi) - gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) - TcSoil - endif - end do - endif - - ! return success - ierr = 0 - return - - end function layerDisCont4ida - - !======= Declarations ========= - implicit none - - ! calling variables - type(eqnsData),intent(in) :: eqns_data ! equations data - integer(i4b),intent(inout) :: rootdir(:) ! root function directions to search - - ! local variables - integer(i4b) :: i,ind ! indices - integer(i4b) :: nState ! number of states - integer(i4b) :: nSnow ! number of snow layers - integer(i4b) :: nSoil ! number of soil layers - real(rkind) :: xPsi ! matric head at layer (m) - real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) - - ! get equations data variables - nState = eqns_data%nState - nSnow = eqns_data%nSnow - nSoil = eqns_data%nSoil - - ! initialize - ind = 0 - - ! identify the critical point when vegetation begins to freeze - if(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then - ind = ind+1 - rootdir(ind) = 1 - if(eqns_data%scalarCanopyTempPrev > Tfreeze) rootdir(ind) = -1 - endif - - if(nSnow>0)then - do i = 1,nSnow - ! identify the critical point when the snow layer begins to freeze - if(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing)then - ind = ind+1 - rootdir(ind) = 1 - if(eqns_data%mLayerTempPrev(i) > Tfreeze) rootdir(ind) = -1 - endif - end do - endif - - if(nSoil>0)then - do i = 1,nSoil - xPsi = eqns_data%mLayerMatricHeadPrev(i) - ! identify the critical point when soil matrix potential goes below 0 and Tfreeze depends only on temp - if (eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing)then - ind = ind+1 - rootdir(ind) = 1 - if(xPsi > 0._rkind ) rootdir(ind) = -1 - endif - ! identify the critical point when the soil layer begins to freeze - if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing)then - ind = ind+1 - TcSoil = crit_soilT(xPsi) - rootdir(ind) = 1 - if(eqns_data%mLayerTempPrev(i+nSnow) > TcSoil) rootdir(ind) = -1 - endif - end do - endif - - end subroutine find_rootdir - - ! ---------------------------------------------------------------------------------------- ! layerDisCont4ida: The root function routine to find soil matrix potential = 0, ! soil temp = critical frozen point, and snow and veg temp = Tfreeze From 59ead8f22c30dc22df6d70b5e9bfd281c44a3299 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 23 May 2023 21:01:11 +0900 Subject: [PATCH 0731/1472] make executables +x --- build/cmake/build.cluster.bash | 0 build/cmake/build_actors.cluster.bash | 0 build/cmake/build_ngen.cluster.bash | 0 test_ngen/example_run_celia.cluster.sh | 0 test_ngen/example_run_celia.mac.sh | 0 5 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 build/cmake/build.cluster.bash mode change 100644 => 100755 build/cmake/build_actors.cluster.bash mode change 100644 => 100755 build/cmake/build_ngen.cluster.bash mode change 100644 => 100755 test_ngen/example_run_celia.cluster.sh mode change 100644 => 100755 test_ngen/example_run_celia.mac.sh diff --git a/build/cmake/build.cluster.bash b/build/cmake/build.cluster.bash old mode 100644 new mode 100755 diff --git a/build/cmake/build_actors.cluster.bash b/build/cmake/build_actors.cluster.bash old mode 100644 new mode 100755 diff --git a/build/cmake/build_ngen.cluster.bash b/build/cmake/build_ngen.cluster.bash old mode 100644 new mode 100755 diff --git a/test_ngen/example_run_celia.cluster.sh b/test_ngen/example_run_celia.cluster.sh old mode 100644 new mode 100755 diff --git a/test_ngen/example_run_celia.mac.sh b/test_ngen/example_run_celia.mac.sh old mode 100644 new mode 100755 From 66d788bd505fb5b41ef1a9e7435c52505d8bbe65 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 23 May 2023 21:20:56 +0900 Subject: [PATCH 0732/1472] sections got repeated in the merge in some files --- .../source/engine/summaSolveSundials4ida.f90 | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/build/source/engine/summaSolveSundials4ida.f90 b/build/source/engine/summaSolveSundials4ida.f90 index 99c4a03c7..18e997960 100644 --- a/build/source/engine/summaSolveSundials4ida.f90 +++ b/build/source/engine/summaSolveSundials4ida.f90 @@ -372,32 +372,6 @@ subroutine summaSolveSundials4ida( & allocate( rootdir(nRoot) ) endif - ! initialize rootfinding problem and allocate space, counting roots - if(detect_events)then - nRoot = 0 - if(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing) nRoot = nRoot+1 - if(nSnow>0)then - do i = 1,nSnow - if(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing) nRoot = nRoot+1 - enddo - endif - if(nSoil>0)then - do i = 1,nSoil - if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing) nRoot = nRoot+1 - if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing) nRoot = nRoot+1 - enddo - endif - allocate( rootsfound(nRoot) ) - allocate( rootdir(nRoot) ) - rootdir = 0 - retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4ida)) - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif - else ! will not use, allocate at something - nRoot = 1 - allocate( rootsfound(nRoot) ) - allocate( rootdir(nRoot) ) - endif - ! define the form of the matrix select case(ixMatrix) case(ixBandMatrix) From 46adb9e87b4c5918b990af6990c60c754e7466eb Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 26 May 2023 13:57:56 +0900 Subject: [PATCH 0733/1472] turn off Mass checking in Sundials, fixing some utilities --- build/source/engine/coupled_em.f90 | 4 +-- utils/timeseries_to_statistics.py | 50 ++++++++++++++++++++---------- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 7b4bb2c23..2299c6298 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1193,8 +1193,8 @@ subroutine coupled_em(& ! identify the need to check the mass balance, both methods should work if tolerance coarse enough select case(ixNumericalMethod) - case(ida); checkMassBalance = .true. ! Sundials gives instantaneous fluxes and were summed for an average flux for checks - case(be_numrec); checkMassBalance = .true. ! be_numrec gives finite difference dt_sub fluxes and were summed for an average flux for checks + case(ida); checkMassBalance = .false. ! Sundials gives instantaneous fluxes and were summed for an average flux, but if large time step, then average is not accurate enough to pass the check + case(be_numrec); checkMassBalance = .true. ! be_numrec gives finite difference dt_sub fluxes and were summed for an average flux case default; err=20; message=trim(message)//'expect num_method to be sundials, be_kinsol, or be_numrec (or itertive, which is be_numrec)'; return end select diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index a4b56c331..91c72ca44 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -7,10 +7,10 @@ # written originally by W. Knoben, modified by A. Van Beusekom (2023) # Best to comment out parallel processing lines and run that way on Graham or for full dataset -# Uses modified KGEm calculation avoids the amplified values when mean is small +# Uses modified KGEm calculation avoids the amplified values when mean is small # and avoids the KGE value dependence on the units of measurement (as discussed by Santos et al. 2018; Clark et al. 2021). -# The KGEm values range from -∞ to 1, with 1 being a perfect match with the benchmark results. -# Similar to Beck et al.(2020), we scaled KGEm values to avoid the heavy influence of large negative values. +# The KGEm values range from -∞ to 1, with 1 being a perfect match with the benchmark results. +# Similar to Beck et al.(2020), we scaled KGEm values to avoid the heavy influence of large negative values. # This results in KGE values that range between -1 and 1, with lower KGE values indicating larger differences from bench. # Run: @@ -21,20 +21,19 @@ import xarray as xr from pathlib import Path import numpy as np +import sys + +import warnings +warnings.simplefilter("ignore") #deal with correlation warnings from variance 0 in kgem, both have no snow # Settings bench_name = 'sundials_1en8' -top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' +top_fold = '/home/avanb/scratch/' testing = False -if testing: - top_fold = '/Users/amedin/Research/USask/test_py/' - method_name = 'be1' -else: - import multiprocessing as mp - import sys - # The first input argument specifies the run where the files are - method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) + +# The first input argument specifies the run where the files are +method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) des_dir = top_fold + 'statistics' src_dir = top_fold + 'summa-' + method_name @@ -70,6 +69,16 @@ def correlation(x,y,dims=None): assert len(ben_files) == len(src_files), \ 'Found {} files but need {}!'.format(len(src_files), len(ben_files)) +# -- test for corruption +#for (file, bench) in zip(src_files,ben_files): +# # open file +# try: +# with xr.open_dataset(file), xr.open_dataset(bench) as ds: +# # Do nothing if the file is successfully opened +# pass +# except: +# # Log the file name or take other appropriate action if the file is corrupted +# print('Error opening file:', file, bench) # -- functions def run_loop(file,bench): @@ -77,8 +86,15 @@ def run_loop(file,bench): # extract the subset IDs subset = file.split('/')[-1].split('_')[1] - # open file - dat,ben = xr.open_dataset(file), xr.open_dataset(bench) + # acquire the lock before opening the file + if testing: + dat, ben = xr.open_dataset(file), xr.open_dataset(bench) + else: + import multiprocessing as mp + lock = mp.Lock() + with lock: + dat, ben = xr.open_dataset(file), xr.open_dataset(bench) + # sometimes gives -9999 the whole run (non-compute), make these nan and plot as lowest value 0 in geographic dat = dat.where(dat!=-9999) ben = ben.where(ben!=-9999) @@ -125,7 +141,7 @@ def run_loop(file,bench): kgem = 1 - np.sqrt( np.square(r-1) + np.square( dat[var].std(dim='time')/ben[var].std(dim='time') - 1) + np.square( (dat[var].mean(dim='time')-ben[var].mean(dim='time'))/ben[var].std(dim='time') ) ) - + #if constant and identical, want this as 1.0 -- correlation with a constant = 0 and std dev = 0\n", for h in the_hru: ss = dat[var].sel(hru=h) @@ -133,7 +149,7 @@ def run_loop(file,bench): kgem.loc[h] =kgem.sel(hru=h).where(np.allclose(ss,tt, atol = 1e-10)==False, other=1.0) kgem = kgem/(2.0-kgem) kgem = kgem.expand_dims("stat").assign_coords(stat=("stat",["kgem"])) - + new = xr.merge([mean,amax,rmse,maxe,kgem]) new.to_netcdf(des_dir / des_fil.format(var,subset)) @@ -167,7 +183,7 @@ def merge_subsets_into_one(src,pattern,des,name): ncpus = int(os.environ.get('SLURM_CPUS_PER_TASK',default=1)) if __name__ == "__main__": pool = mp.Pool(processes=ncpus) - results = [pool.apply_async(run_loop, args=(file, bench)) for (file, bench) in zip(src_files,ben_files)] + results = [pool.apply_async(run_loop, args=(file, bench, lock)) for (file, bench) in zip(src_files, ben_files)] dojob = [p.get() for p in results] pool.close() # -- end parallel processing From f93fc3c4e7724af6b6c653e3cd50ac17878bc9e9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 26 May 2023 14:23:12 +0900 Subject: [PATCH 0734/1472] fix cmakelist for actors --- build/cmake/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 915e626d7..83262a285 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -80,7 +80,7 @@ else() # CMakeLists in ${summa-actors GIT directory}/build/summa/build/cmake set(PARENT_DIR ${F_MASTER}../../) # directory of summa actors source code - set(EXEC_DIR ${PARENT_DIR}../bin) + set(EXEC_DIR ${PARENT_DIR}/bin) else() project(summa DESCRIPTION "Summa-Sundials-BE") From 8bc9f5a339dd9a3066c45ec65234017eb2d61885 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 26 May 2023 15:10:50 +0900 Subject: [PATCH 0735/1472] add and fix readmes --- build/cmake/README_ngen.md | 4 +-- build/cmake/README_not_ngen.md | 62 ++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 build/cmake/README_not_ngen.md diff --git a/build/cmake/README_ngen.md b/build/cmake/README_ngen.md index 3fa495e4d..e0cb04ef3 100644 --- a/build/cmake/README_ngen.md +++ b/build/cmake/README_ngen.md @@ -77,11 +77,11 @@ Note (similar to above) that when there is an existing directory, it may sometim After there is build system directory, the shared library can be built using the `summabmi` CMake target. For example, the SummaSundials shared library file (i.e., the build config's `summabmi` target) can be built using: - cmake --build cmake_build --target all + cmake --build extern/summa/cmake_build --target all This will build a `cmake_build/libsummabmi..` file, where the version is configured within the CMake config, and the extension depends on the local machine's operating system. -There is an example of a bash script to build the ngen libraries at ngen/extern/summa/summa/build/cmake_ngen/build_ngen.[system_type].bash. Sundials is turned off here. These need to be run in the main ngen directory. +There is an example of a bash script to build the ngen libraries at ngen/extern/summa/summa/build/cmake/build_ngen.[system_type].bash. Sundials is turned off here. These need to be run in the main ngen directory. To run a test basin, in the main ngen directory, run diff --git a/build/cmake/README_not_ngen.md b/build/cmake/README_not_ngen.md new file mode 100644 index 000000000..3ad0e0f78 --- /dev/null +++ b/build/cmake/README_not_ngen.md @@ -0,0 +1,62 @@ +# SummaSundials + +## About + +This is configured with the [CMakeLists.txt](CMakeLists.txt) and other files in this directory. + +#### Extra Outer Directory for Actors + +Actors needs one + + +### Getting the Latest Changes + +There are two steps to getting upstream submodule changes fully + 1. fetching and locally checking out the changes from the remote + 2. committing the new checkout revision for the submodule + +To fetch and check out the latest revision (for the [currently used branch](#viewing-the-current-branch)): + + git pull + +# Usage + +## Building Libraries + +First, cd into the cmake directory in summa, without Actors: + + cd summa/build/cmake + +or with Actors: + + cd summa/build/summa/build/cmake + +If you want to use Sundials IDA or BE Kinsol, set -DCMAKE_BUILD_TYPE=Sundials* in the build script instead of BE*. Then, before summa can be built, Sundials needs to be installed. +Download the latest release of IDA solver from SUNDIALS package in https://computing.llnl.gov/projects/sundials/sundials-software + + wget "https://github.com/LLNL/sundials/releases/download/v6.3.0/sundials-6.3.0.tar.gz" + +Extract the corresponding compressed file + + tar -xzf sundials-6.3.0.tar.gz + +Enter the buildir and run + + cd sundials/buildir + ./build_cmake + make + make install + +Note if you need to recompile after a system upgrade, delete the contents of sundials/instdir and sundials/buildir EXCEPT sundials/buildir/build_cmake before building and installing. + + +Note that when there is an existing directory, it may sometimes be necessary to clear it and regenerate, especially if any changes were made to the CMakeLists.txt file. + +After there is build system directory, the shared library can be built using the `summabmi` CMake target. For example, the SummaSundials shared library file (i.e., the build config's `summabmi` target) can be built using: + + cmake --build ../cmake_build --target all + +This will build a `cmake_build/libsummabmi..` file, where the version is configured within the CMake config, and the extension depends on the local machine's operating system. + +There is an example of a bash script to build the summa libraries at /build/cmake/build[_actors].[system_type].bash. Sundials is turned on here. These need to be run in the cmake directory. + From cbb6131db9ad0a7cdd03bbb03e2f4abd741b8129 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 30 May 2023 23:46:23 +0900 Subject: [PATCH 0736/1472] Added utils file for RMSE attribute checking. Added parameters be_steps and relConvTol_ida and absConvTol_ida. These have a default for backwards compatibility, but should be added to the localParamInfo.txt as be_steps | 1.0000 | 1.0000 | 512.0000 relConvTol_ida | 1.0d-6 | 1.0d-10| 1.0d-3 absConvTol_ida | 1.0d-6 | 1.0d-10| 1.0d-3 --- build/source/dshare/get_ixname.f90 | 3 + build/source/dshare/popMetadat.f90 | 3 + build/source/dshare/var_lookup.f90 | 6 +- build/source/engine/coupled_em.f90 | 6 +- build/source/engine/read_pinit.f90 | 13 ++ build/source/engine/tol4ida.f90 | 43 ++++-- build/source/engine/vegPhenlgy.f90 | 2 +- utils/largest_error_attrib.py | 72 +++++++++ utils/plot_control_NorthAmerica.txt | 231 ---------------------------- utils/timeseries_to_statistics.py | 8 +- 10 files changed, 134 insertions(+), 253 deletions(-) create mode 100644 utils/largest_error_attrib.py delete mode 100644 utils/plot_control_NorthAmerica.txt diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 111e9bade..d94104d10 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -379,6 +379,7 @@ function get_ixparam(varName) case('minwind' ); get_ixparam = iLookPARAM%minwind ! minimum wind speed (m s-1) case('minstep' ); get_ixparam = iLookPARAM%minstep ! minimum length of the time step be_numrec, not currently used case('maxstep' ); get_ixparam = iLookPARAM%maxstep ! maximum length of the time step be_numrec + case('be_steps' ); get_ixparam = iLookPARAM%be_steps ! minimum number of substeps to take in a maxstep be_numrec case('wimplicit' ); get_ixparam = iLookPARAM%wimplicit ! weight assigned to start-of-step fluxes be_numrec, not currently used case('maxiter' ); get_ixparam = iLookPARAM%maxiter ! maximum number of iterations be_numrec or nonlinear iterations Sundials case('relConvTol_liquid' ); get_ixparam = iLookPARAM%relConvTol_liquid ! relative convergence tolerance for vol frac liq water (-) be_numrec @@ -389,6 +390,8 @@ function get_ixparam(varName) case('absConvTol_energy' ); get_ixparam = iLookPARAM%absConvTol_energy ! absolute convergence tolerance for energy (J m-3) be_numrec case('relConvTol_aquifr' ); get_ixparam = iLookPARAM%relConvTol_aquifr ! relative convergence tolerance for aquifer storage (-) be_numrec case('absConvTol_aquifr' ); get_ixparam = iLookPARAM%absConvTol_aquifr ! absolute convergence tolerance for aquifer storage (m) be_numrec + case('relConvTol_ida' ); get_ixparam = iLookPARAM%relConvTol_ida ! relative convergence tolerance for sundials ida + case('absConvTol_ida' ); get_ixparam = iLookPARAM%absConvTol_ida ! absolute convergence tolerance for sundials ida case('zmin' ); get_ixparam = iLookPARAM%zmin ! minimum layer depth (m) case('zmax' ); get_ixparam = iLookPARAM%zmax ! maximum layer depth (m) case('zminLayer1' ); get_ixparam = iLookPARAM%zminLayer1 ! minimum layer depth for the 1st (top) layer (m) diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 5648c597f..fa6f3ca69 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -261,6 +261,7 @@ subroutine popMetadat(err,message) mpar_meta(iLookPARAM%minwind) = var_info('minwind' , 'minimum wind speed' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%minstep) = var_info('minstep' , 'minimum length of the time step be_numrec, not currently used' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%maxstep) = var_info('maxstep' , 'maximum length of the time step be_numrec' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%be_steps) = var_info('be_steps' , 'minimum number of substeps to take in a maxstep be_numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%wimplicit) = var_info('wimplicit' , 'weight assigned to the start-of-step fluxes ,be_numrec, not currently used', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%maxiter) = var_info('maxiter' , 'maximum number of iterations be_numrec or nonlinear iterations Sundials', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%relConvTol_liquid) = var_info('relConvTol_liquid' , 'relative convergence tolerance for vol frac liq water be_numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) @@ -271,6 +272,8 @@ subroutine popMetadat(err,message) mpar_meta(iLookPARAM%absConvTol_energy) = var_info('absConvTol_energy' , 'absolute convergence tolerance for energy be_numrec' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%relConvTol_aquifr) = var_info('relConvTol_aquifr' , 'relative convergence tolerance for aquifer storage be_numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%absConvTol_aquifr) = var_info('absConvTol_aquifr' , 'absolute convergence tolerance for aquifer storage be_numrec' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_ida) = var_info('relConvTol_ida' , 'relative convergence tolerance for sundials ida' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_ida) = var_info('absConvTol_ida' , 'absolute convergence tolerance for sundials ida' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%zmin) = var_info('zmin' , 'minimum layer depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%zmax) = var_info('zmax' , 'maximum layer depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%zminLayer1) = var_info('zminLayer1' , 'minimum layer depth for the 1st (top) layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 22da2bd21..fb5bfdb6a 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -283,6 +283,7 @@ MODULE var_lookup integer(i4b) :: minwind = integerMissing ! minimum wind speed (m s-1) integer(i4b) :: minstep = integerMissing ! minimum length of the time step integer(i4b) :: maxstep = integerMissing ! maximum length of the time step + integer(i4b) :: be_steps = integerMissing ! minimum number of substeps to take in a maxstep be_numrec integer(i4b) :: wimplicit = integerMissing ! weight assigned to the start-of-step fluxes integer(i4b) :: maxiter = integerMissing ! maximum number of iteration integer(i4b) :: relConvTol_liquid = integerMissing ! relative convergence tolerance for vol frac liq water (-) @@ -293,6 +294,8 @@ MODULE var_lookup integer(i4b) :: absConvTol_energy = integerMissing ! absolute convergence tolerance for energy (J m-3) integer(i4b) :: relConvTol_aquifr = integerMissing ! relative convergence tolerance for aquifer storage (-) integer(i4b) :: absConvTol_aquifr = integerMissing ! absolute convergence tolerance for aquifer storage (J m-3) + integer(i4b) :: relConvTol_ida = integerMissing ! relative convergence tolerance for sundials ida' + integer(i4b) :: absConvTol_ida = integerMissing ! absolute convergence tolerance for sundials ida' integer(i4b) :: zmin = integerMissing ! minimum layer depth (m) integer(i4b) :: zmax = integerMissing ! maximum layer depth (m) integer(i4b) :: zminLayer1 = integerMissing ! minimum layer depth for the 1st (top) layer (m) @@ -859,7 +862,8 @@ MODULE var_lookup 121,122,123,124,125,126,127,128,129,130,& 131,132,133,134,135,136,137,138,139,140,& 141,142,143,144,145,146,147,148,149,150,& - 151,152,153,154,155,156,157,158,159) + 151,152,153,154,155,156,157,158,159,160,& + 161,162) ! named variables: model prognostic (state) variables type(iLook_prog), public,parameter :: iLookPROG =iLook_prog ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 2299c6298..25fc2900e 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -405,11 +405,11 @@ subroutine coupled_em(& ! short-cut to the algorithmic control parameters ! NOTE - temporary assignment of minstep to foce something reasonable - ! change maxstep with hard code here to make the outer and inner loop computations here in coupled_em happen more frequently - ! change maxstep_op with hard code here to make the inner loop computations in opSplittin happen more frequently + ! changing the maxstep parameter will make the outer and inner loop computations here in coupled_em happen more frequently + ! changing the be_steps parameter will make the inner loop computations in opSplittin happen more frequently (e.g. be_steps = 32.0 give BE32) minstep = 10._rkind ! mpar_data%var(iLookPARAM%minstep)%dat(1) ! minimum time step (s) maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) - maxstep_op = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) to run opSplittin over + maxstep_op = mpar_data%var(iLookPARAM%maxstep)%dat(1)/NINT(mpar_data%var(iLookPARAM%be_steps)%dat(1)) ! maximum time step (s) to run opSplittin over ! compute the number of layers with roots nLayersRoots = count(prog_data%var(iLookPROG%iLayerHeight)%dat(nSnow:nLayers-1) < mpar_data%var(iLookPARAM%rootingDepth)%dat(1)-verySmall) diff --git a/build/source/engine/read_pinit.f90 b/build/source/engine/read_pinit.f90 index d0103d615..6b4b9968a 100644 --- a/build/source/engine/read_pinit.f90 +++ b/build/source/engine/read_pinit.f90 @@ -129,6 +129,18 @@ subroutine read_pinit(filenm,isLocal,mpar_meta,parFallback,err,message) err=40; message=trim(message)//"variable in parameter file not present in data structure [var="//trim(varname)//"]"; return end if end do ! (looping through lines in the file) + + ! add these defaults for backwards compatibility pre Sundials + if (parFallback(iLookPARAM%be_steps)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%be_steps)%default_val = 1._rkind + end if + if (parFallback(iLookPARAM%relConvTol_ida)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%relConvTol_ida)%default_val = 1.e-6_rkind + end if + if (parFallback(iLookPARAM%absConvTol_ida)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%absConvTol_ida)%default_val = 1.e-6_rkind + end if + ! check we have populated all variables ! NOTE: ultimately need a need a parameter dictionary to ensure that the parameters used are populated if(.not.backwardsCompatible)then ! if we add new variables in future versions of the code, then some may be missing in the input file @@ -148,6 +160,7 @@ subroutine read_pinit(filenm,isLocal,mpar_meta,parFallback,err,message) end if end if end if + ! close file unit close(unt) end subroutine read_pinit diff --git a/build/source/engine/tol4ida.f90 b/build/source/engine/tol4ida.f90 index 59b41cf41..a75a7c9ca 100644 --- a/build/source/engine/tol4ida.f90 +++ b/build/source/engine/tol4ida.f90 @@ -155,20 +155,20 @@ subroutine popTol4ida(& integer(i4b) :: iLayer ! index of layer within the snow+soil domain integer(i4b) :: ixStateSubset ! index within the state subset logical(lgt),dimension(nState) :: tolFlag ! flag to denote that the state is populated - real(rkind) :: absTolTempCas = 1e-6 ! could use absConvTol_energy 1e-6 = 1e-0 bEuler - real(rkind) :: relTolTempCas = 1e-6 ! could use relConvTol_energy 1e-6 = 1e-2 bEuler - real(rkind) :: absTolTempVeg = 1e-6 ! could use absConvTol_energy - real(rkind) :: relTolTempVeg = 1e-6 ! could use relConvTol_energy - real(rkind) :: absTolWatVeg = 1e-6 ! could use absConvTol_liquid 1e-6 = 1e-5 bEuler - real(rkind) :: relTolWatVeg = 1e-6 ! could use relConvTol_liquid 1e-6 = 1e-3 bEuler - real(rkind) :: absTolTempSoilSnow = 1e-6 ! could use absConvTol_energy - real(rkind) :: relTolTempSoilSnow = 1e-6 ! could use relConvTol_energy - real(rkind) :: absTolWatSnow = 1e-6 ! could use absConvTol_liquid - real(rkind) :: relTolWatSnow = 1e-6 ! could use relConvTol_liquid - real(rkind) :: absTolMatric = 1e-6 ! could use absConvTol_matric 1e-6 = 1e-6 bEuler - real(rkind) :: relTolMatric = 1e-6 ! could use relConvTol_matric 1e-6 = 1e-6 bEuler - real(rkind) :: absTolAquifr = 1e-6 ! could use absConvTol_aquifr 1e-6 = 1e-5 bEuler - real(rkind) :: relTolAquifr = 1e-6 ! could use relConvTol_aquifr 1e-6 = 1e-0 bEuler + real(rkind) :: absTolTempCas + real(rkind) :: relTolTempCas + real(rkind) :: absTolTempVeg + real(rkind) :: relTolTempVeg + real(rkind) :: absTolWatVeg + real(rkind) :: relTolWatVeg + real(rkind) :: absTolTempSoilSnow + real(rkind) :: relTolTempSoilSnow + real(rkind) :: absTolWatSnow + real(rkind) :: relTolWatSnow + real(rkind) :: absTolMatric + real(rkind) :: relTolMatric + real(rkind) :: absTolAquifr + real(rkind) :: relTolAquifr ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- @@ -208,6 +208,21 @@ subroutine popTol4ida(& ! initialize error control err=0; message='popTol4ida/' + absTolTempCas = mpar_data%var(iLookPARAM%absConvTol_ida)%dat(1) + relTolTempCas = mpar_data%var(iLookPARAM%relConvTol_ida)%dat(1) + absTolTempVeg = mpar_data%var(iLookPARAM%absConvTol_ida)%dat(1) + relTolTempVeg = mpar_data%var(iLookPARAM%relConvTol_ida)%dat(1) + absTolWatVeg = mpar_data%var(iLookPARAM%absConvTol_ida)%dat(1) + relTolWatVeg = mpar_data%var(iLookPARAM%relConvTol_ida)%dat(1) + absTolTempSoilSnow = mpar_data%var(iLookPARAM%absConvTol_ida)%dat(1) + relTolTempSoilSnow = mpar_data%var(iLookPARAM%relConvTol_ida)%dat(1) + absTolWatSnow = mpar_data%var(iLookPARAM%absConvTol_ida)%dat(1) + relTolWatSnow = mpar_data%var(iLookPARAM%relConvTol_ida)%dat(1) + absTolMatric = mpar_data%var(iLookPARAM%absConvTol_ida)%dat(1) + relTolMatric = mpar_data%var(iLookPARAM%relConvTol_ida)%dat(1) + absTolAquifr = mpar_data%var(iLookPARAM%absConvTol_ida)%dat(1) + relTolAquifr = mpar_data%var(iLookPARAM%relConvTol_ida)%dat(1) + ! ----- ! * initialize state vectors... ! ----------------------------- diff --git a/build/source/engine/vegPhenlgy.f90 b/build/source/engine/vegPhenlgy.f90 index 2b96b6f48..51fec4552 100755 --- a/build/source/engine/vegPhenlgy.f90 +++ b/build/source/engine/vegPhenlgy.f90 @@ -185,7 +185,7 @@ subroutine vegPhenlgy(& heightAboveSnow = heightCanopyTop - scalarSnowDepth ! height top of canopy is above the snow surface (m) ! determine if need to include vegetation in the energy flux routines - computeVegFlux = (exposedVAI > 0.05_rkind .and. heightAboveSnow > 0.05_rkind) + computeVegFlux = (exposedVAI > 0.05_rkind .and. heightAboveSnow > 0.05_rkind) !write(*,'(a,1x,i2,1x,L1,1x,10(f12.5,1x))') 'vegTypeIndex, computeVegFlux, heightCanopyTop, heightAboveSnow, scalarSnowDepth = ', & ! vegTypeIndex, computeVegFlux, heightCanopyTop, heightAboveSnow, scalarSnowDepth diff --git a/utils/largest_error_attrib.py b/utils/largest_error_attrib.py new file mode 100644 index 000000000..43ab91cee --- /dev/null +++ b/utils/largest_error_attrib.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python + +# python largest_error_attrib.py [method_name] + +import sys +import numpy as np +import xarray as xr +from pathlib import Path + +top_fold = '/home/avanb/scratch/' +attr_fold = '/home/avanb/TestScripts/settings/' +nBig = 10 + +testing = False +if testing: + method_name= 'be1' #sys.argv[1] + top_fold = '/Users/amedin/Research/USask/test_py/' + attr_fold = '/Users/amedin/Research/USask/test_py/settings/' + nBig = 10 +else: + method_name = sys.argv[1] + +des_dir = top_fold + 'statistics' +des_dir = Path(des_dir) + +settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] +viz_fil = method_name + '_hrly_diff_stats_{}.nc' +viz_fil = viz_fil.format(','.join(settings)) +src_file = des_dir / viz_fil + +attr_fil = Path(attr_fold) / 'attributes.nc' + +# Open the netCDF file with RMSE data +summa = xr.open_dataset(src_file) + +# Get the RMSE variable from the netCDF file +rmse_var = summa['scalarTotalSoilWat'].sel(stat='rmse') +#rmse_var = summa['scalarTotalET'].sel(stat='rmse') +#rmse_var = summa['averageRoutedRunoff'].sel(stat='rmse') + +# Mask the finite values of the RMSE variable +mask = np.isfinite(rmse_var) +rmse_var = rmse_var[mask] + +# Get the indices of the largest nBig RMSE values +max_rmse_indices = rmse_var.argsort()[-nBig:] + +# Get the largest RMSE values +rmse_of_max_rmse = rmse_var[max_rmse_indices.values] + +# Get the hru coordinate of the largest RMSE values +hru_of_max_rmse = rmse_var[max_rmse_indices.values].hru.values + +# Print all the values of the the biggest rmse hru +rmse_values = summa.sel(hru=hru_of_max_rmse[nBig-1],stat='rmse') +print(settings[0:5]) +print(rmse_values[settings[0]].values,rmse_values[settings[1]].values,rmse_values[settings[2]].values,rmse_values[settings[3]].values,rmse_values[settings[4]].values) + +# Open the netCDF file with local attributes +attr = xr.open_dataset(attr_fil) + +# Mask the HRU variable from the netCDF file +mask = attr['hruId'].isin(hru_of_max_rmse) + +# Get the vegTypeIndex variable from the netCDF file +veg_type_of_max_rmse = attr['vegTypeIndex'][mask] + +# Print the HRU, vegTypeIndex, and RMSE values of the largest RMSE values +print("HRU values of the largest RMSE values:", hru_of_max_rmse) +print("GRU values of the largest RMSE values:", gru_of_max_rmse) +print("vegTypeIndex values of the largest RMSE values:", veg_type_of_max_rmse.values) +print("RMSE values of the largest RMSE values:", rmse_of_max_rmse.values) \ No newline at end of file diff --git a/utils/plot_control_NorthAmerica.txt b/utils/plot_control_NorthAmerica.txt deleted file mode 100644 index 7d8708711..000000000 --- a/utils/plot_control_NorthAmerica.txt +++ /dev/null @@ -1,231 +0,0 @@ -# SUMMA workflow setting file. -# a lot of this is unneeded for Sundials paper plots so not updating values -# Characters '|' and '#' are used as separators to find the actual setting values. Any text behind '|' is assumed to be part of the setting value, unless preceded by '#'. - -# Note on path specification -# If deviating from default paths, a full path must be specified. E.g. '/home/user/non-default/path' - - -# Modeling domain settings -root_path | /home/avanb/projects/rpp-kshook/wknoben/CWARHM_data # Root folder where data will be stored. -domain_name | NorthAmerica # Used as part of the root folder name for the prepared data. - - -# Shapefile settings - SUMMA catchment file -catchment_shp_path | default # If 'default', uses 'root_path/domain_[name]/shapefiles/catchment'. -catchment_shp_name | merit_hydro_basins_and_coastal_hillslopes_merged_NA.shp # Name of the catchment shapefile. Requires extension '.shp'. -catchment_shp_gruid | COMID # Name of the GRU ID column (can be any numeric value, HRU's within a single GRU have the same GRU ID). -catchment_shp_hruid | COMID # Name of the HRU ID column (consecutive from 1 to total number of HRUs, must be unique). -catchment_shp_area | HRU_area # Name of the catchment area column. Area must be in units [m^2] -catchment_shp_lat | center_lat # Name of the latitude column. Should be a value representative for the HRU. Typically the centroid. -catchment_shp_lon | center_lon # Name of the longitude column. Should be a value representative for the HRU. Typically the centroid. - - -# Shapefile settings - mizuRoute river network file -river_network_shp_path | default # If 'default', uses 'root_path/domain_[name]/shapefiles/river_network'. -river_network_shp_name | merit_river_na_full.shp # Name of the river network shapefile. Requires extension '.shp'. -river_network_shp_segid | COMID # Name of the segment ID column. -river_network_shp_downsegid | NextDownID # Name of the downstream segment ID column. -river_network_shp_slope | slope # Name of the slope column. Slope must be in in units [length/length]. -river_network_shp_length | length # Name of the segment length column. Length must be in units [m]. - - -# Shapefile settings - mizuRoute catchment file -river_basin_shp_path | /home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/catchment/ # Same file as the SUMMA catchments -river_basin_shp_name | merit_hydro_basins_and_coastal_hillslopes_merged_NA.shp # Name of the routing subbasins shapefile needed for remapping. Requires extension '.shp'. -river_basin_shp_rm_hruid | COMID # Name of the routing basin ID column. -river_basin_shp_area | HRU_area # Name of the catchment area column. Area must be in units [m^2] -river_basin_shp_hru_to_seg | COMID # Name of the column that shows which river segment each HRU connects to. HRUs and river segments have the same COMID so this works. - - -# Shapefile settings - SUMMA-to-mizuRoute -river_basin_needs_remap | no # 'no' if routing basins map 1:1 onto model GRUs. 'yes' if river segments span multiple GRUs or if multiple segments are inside a single GRU. - - -# Install settings -github_summa | https://github.com/ncar/summa # Replace this with the path to your own fork if you forked the repo. -github_mizuroute | https://github.com/ncar/mizuroute # Replace this with the path to your own fork if you forked the repo. -install_path_summa | default # If 'default', clones source code into 'root_path/installs/summa'. -install_path_mizuroute | default # If 'default', clones source code into 'root_path/installs/mizuRoute'. -exe_name_summa | summa.exe # Name of the compiled executable. -exe_name_mizuroute | mizuroute.exe # Name of the compiled executable. - - -# Forcing settings -forcing_raw_time | 1979,1984 # Years to download: Jan-[from],Dec-[to]. -forcing_raw_space | 85/-179.5/5/-50 # Bounding box of the shapefile: lat_max/lon_min/lat_min/lon_max. Will be converted to ERA5 download coordinates in script. Order and use of '/' to separate values is mandatory. -forcing_time_step_size | 3600 # Size of the forcing time step in [s]. Must be constant. -forcing_measurement_height | 3 # Reference height for forcing measurements [m]. -forcing_shape_path | default # If 'default', uses 'root_path/domain_[name]/shapefiles/forcing'. -forcing_shape_name | era5_grid.shp # Name of the forcing shapefile. Requires extension '.shp'. -forcing_shape_lat_name | lat # Name of the latitude field that contains the latitude of ERA5 data points. -forcing_shape_lon_name | lon # Name of the longitude field that contains the latitude of ERA5 data points. -forcing_geo_path | default # If 'default', uses 'root_path/domain_[name]/forcing/0_geopotential'. -forcing_raw_path | default # If 'default', uses 'root_path/domain_[name]/forcing/1_raw_data'. -forcing_merged_path | default # If 'default', uses 'root_path/domain_[name]/forcing/2_merged_data'. -forcing_easymore_path | default # If 'default', uses 'root_path/domain_[name]/forcing/3_temp_easymore'. -forcing_basin_avg_path | default # If 'default', uses 'root_path/domain_[name]/forcing/3_basin_averaged_data'. -forcing_summa_path | default # If 'default', uses 'root_path/domain_[name]/forcing/4_SUMMA_input'. - - -# Parameter settings - DEM -parameter_dem_main_url | http://hydro.iis.u-tokyo.ac.jp/~yamadai/MERIT_Hydro/distribute/v1.0.1/ # Primary download URL for MERIT Hydro adjusted elevation data. Needs to be appended with filenames. -parameter_dem_file_template | elv_{}{}.tar # Template for download file names. -parameter_dem_raw_path | default # If 'default', uses 'root_path/domain_[name]/parameters/dem/1_MERIT_hydro_raw_data'. -parameter_dem_unpack_path | default # If 'default', uses 'root_path/domain_[name]/parameters/dem/2_MERIT_hydro_unpacked_data'. -parameter_dem_vrt1_path | default # If 'default', uses 'root_path/domain_[name]/parameters/dem/3_vrt'. -parameter_dem_vrt2_path | default # If 'default', uses 'root_path/domain_[name]/parameters/dem/4_domain_vrt'. -parameter_dem_tif_path | default # If 'default', uses 'root_path/domain_[name]/parameters/dem/5_elevation'. -parameter_dem_tif_name | elevation.tif # Name of the final DEM for the domain. Must be in .tif format. - - -# Parameter settings - soil -parameter_soil_hydro_ID | 1361509511e44adfba814f6950c6e742 # ID of the Hydroshare resource to download. -parameter_soil_raw_path | default # If 'default', uses 'root_path/domain_[name]/parameters/soilclass/1_soil_classes_global'. -parameter_soil_domain_path | default # If 'default', uses 'root_path/domain_[name]/parameters/soilclass/2_soil_classes_domain'. -parameter_soil_tif_name | soil_classes.tif # Name of the final soil class overview for the domain. Must be in .tif format. - - -# Parameter settings - land -parameter_land_list_path | default # If 'default', uses 'summaWorkflow_public/3b_parameters/MODIS_MCD12Q1_V6/1_download/'. Location of file with data download links. -parameter_land_list_name | daac_mcd12q1_data_links.txt # Name of file that contains list of MODIS download urls. -parameter_land_raw_path | /home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/parameters/landclass/1_MODIS_raw_data # If 'default', uses 'root_path/domain_[name]/parameters/landclass/1_MODIS_raw_data'. -parameter_land_vrt1_path | default # If 'default', uses 'root_path/domain_[name]/parameters/landclass/2_vrt_native_crs'. Virtual dataset composed of .hdf files. -parameter_land_vrt2_path | default # If 'default', uses 'root_path/domain_[name]/parameters/landclass/3_vrt_epsg_4326'. Virtual dataset projected in EPSG:4326. -parameter_land_vrt3_path | default # If 'default', uses 'root_path/domain_[name]/parameters/landclass/4_domain_vrt_epsg_4326'. Virtual dataset cropped to model domain. -parameter_land_vrt4_path | default # If 'default', uses 'root_path/domain_[name]/parameters/landclass/5_multiband_domain_vrt_epsg_4326'. Multiband cropped virtual dataset. -parameter_land_tif_path | default # If 'default', uses 'root_path/domain_[name]/parameters/landclass/6_tif_multiband'. -parameter_land_mode_path | default # If 'default', uses 'root_path/domain_[name]/parameters/landclass/7_mode_land_class'. -parameter_land_tif_name | land_classes.tif # Name of the final landclass overview for the domain. Must be in .tif format. - - -# Intersection settings -intersect_dem_path | default # If 'default', uses 'root_path/domain_[name]/shapefiles/catchment_intersection/with_dem'. -intersect_dem_name | catchment_with_merit_dem.shp # Name of the shapefile with intersection between catchment and MERIT Hydro DEM, stored in column 'elev_mean'. -intersect_soil_path | default # If 'default', uses 'root_path/domain_[name]/shapefiles/catchment_intersection/with_soilgrids'. -intersect_soil_name | catchment_with_soilgrids.shp # Name of the shapefile with intersection between catchment and SOILGRIDS-derived USDA soil classes, stored in columns 'USDA_{1,...n}' -intersect_land_path | default # If 'default', uses 'root_path/domain_[name]/shapefiles/catchment_intersection/with_modis'. -intersect_land_name | catchment_with_modis.shp # Name of the shapefile with intersection between catchment and MODIS-derived IGBP land classes, stored in columns 'IGBP_{1,...n}' -intersect_forcing_path | default # If 'default', uses 'root_path/domain_[name]/shapefiles/catchment_intersection/with_forcing'. -intersect_routing_path | default # If 'default', uses 'root_path/domain_[name]/shapefiles/catchment_intersection/with_routing'. -intersect_routing_name | catchment_with_routing_basins.shp # Name of the shapefile with intersection between hydrologic model catchments and routing model catchments. - - -# Experiment settings - general -experiment_id | run1 # Descriptor of the modelling experiment; used as output folder name. -experiment_time_start | default # Simulation start. If 'default', constructs this from 'forcing_raw_time' setting and uses all downloaded forcing data; e.g. '1979-01-01 00:00'. -experiment_time_end | default # Simulation end. If 'default', constructs this from 'forcing_raw_time' setting and uses all downloaded forcing data; e.g. '1979-12-31 23:00'. -experiment_output_summa | default # If 'default', uses 'root_path/domain_[name]/simulations/[experiment_id]/SUMMA'. -experiment_output_mizuRoute | default # If 'default', uses 'root_path/domain_[name]/simulations/[experiment_id]/mizuRoute'. -experiment_log_summa | default # If 'default', uses 'root_path/domain_[name]/simulations/[experiment_id]/SUMMA/SUMMA_logs'. -experiment_log_mizuroute | default # If 'default', uses 'root_path/domain_[name]/simulations/[experiment_id]/mizuRoute/mizuRoute_logs'. -experiment_backup_settings | yes # Flag to (not) create a copy of the model settings in the output folder; "no" or "yes". Copying settings may be undesirable if files are large. - - -# Experiment settings - SUMMA -settings_summa_path | default # If 'default', uses 'root_path/domain_[name]/settings/SUMMA'. -settings_summa_filemanager | fileManager.txt # Name of the file with the SUMMA inputs. -settings_summa_coldstate | coldState.nc # Name of the file with intial states. -settings_summa_trialParams | trialParams.nc # Name of the file that can contain trial parameter values (note, can be empty of any actual parameter values but must be provided and must contain an 'hruId' variable). -settings_summa_forcing_list | forcingFileList.txt # Name of the file that has the list of forcing files. -settings_summa_attributes | attributes.nc # Name of the attributes file. -settings_summa_connect_HRUs | no # Attribute setting: "no" or "yes". Tricky concept, see README in ./5_model_input/SUMMA/3f_attributes. If no; all HRUs modeled as independent columns (downHRUindex = 0). If yes; HRUs within each GRU are connected based on relative HRU elevation (highest = upstream, lowest = outlet). -settings_summa_trialParam_n | 1 # Number of trial parameter specifications. Specify 0 if none are wanted (they can still be included in this file but won't be read). -settings_summa_trialParam_1 | maxstep,900 # Name of trial parameter and value to assign. Value assumed to be float. - - -# Experiment settings - mizuRoute -settings_mizu_path | default # If 'default', uses 'root_path/domain_[name]/settings/mizuRoute'. -settings_mizu_parameters | param.nml.default # Name of the routing parameters file. -settings_mizu_topology | topology.nc # Name of the river network topology file. -settings_mizu_remap | routing_remap.nc # Name of the optional catchment remapping file, for cases when SUMMA uses different catchments than mizuRoute. -settings_mizu_control_file | mizuroute.control # Name of the control file. -settings_mizu_routing_var | averageRoutedRunoff # Name of SUMMA output variable to use for routing. -settings_mizu_routing_units | m/s # Units of the variable to be routed. -settings_mizu_routing_dt | 3600 # Size of the routing time step [s]. -settings_mizu_output_freq | annual # Frequency with which mizuRoute generates new output files. Must be one of 'single', 'day', 'month', 'annual'. -settings_mizu_output_vars | 0 # Routing output. '0' for both KWT and IRF; '1' IRF only; '2' KWT only. -settings_mizu_within_basin | 0 # '0' (no) or '1' (IRF routing). Flag to enable within-basin routing by mizuRoute. Should be set to 0 if SUMMA is run with "subRouting" decision "timeDlay". -settings_mizu_make_outlet | n/a # Segment ID or IDs that should be set as network outlet. Specify multiple IDs separated by commas: X,Y,Z. Specify no IDs as: n/a. Note that this can also be done in the network shapefile. - - -# Postprocessing settings -visualization_folder | default # If 'default', uses 'root_path/domain_[name]/visualization'. - - -# Default folder structure -# Example of the resulting folder structure in "root_path". -# New domains will go into their own folder. - -- summWorkflow_data - | - |_ domain_BowAtBanff - | | - | |_ forcing - | | |_ 0_geopotential - | | |_ 1_raw_data - | | |_ 2_merged_data - | | |_ 3_basin_averaged_data - | | |_ 4_SUMMA_input - | | - | |_ parameters - | | |_ soilclass - | | | |_ 1_soil_classes_global - | | | |_ 2_soil_classes_domain - | | | - | | |_ landclass - | | | |_ 1_MODIS_raw_data - | | | |_ 2_vrt_native_crs - | | | |_ 3_vrt_epsg_4326 - | | | |_ 4_domain_vrt_epsg_4326 - | | | |_ 5_multiband_domain_vrt_epsg_4326 - | | | |_ 6_tif_multiband - | | | |_ 7_mode_land_class - | | | - | | |_ dem - | | |_ 1_MERIT_hydro_raw_data - | | |_ 2_MERIT_hydro_unpacked_data - | | |_ 3_vrt - | | |_ 4_domain_vrt - | | |_ 5_elevation - | | - | |_ settings - | | |_ mizuRoute - | | |_ SUMMA - | | - | |_ shapefiles - | | |_ catchment - | | |_ catchment_intersection - | | | |_ with_dem - | | | |_ with_forcing - | | | |_ with_soil - | | | |_ with_veg - | | |_ forcing - | | |_ river_basins - | | |_ river_network - | | - | |_ simulations - | | |_run1 - | | | |_ 0_settings_backup - | | | | |_ summa - | | | | |_ mizuRoute - | | | |_ summa - | | | | |_run_settings - | | | | |_SUMMA_logs - | | | |_ mizuRoute - | | | | |_run_settings - | | | | |_mizuRoute_logs - | | |_run2 - | | |_ ... - | | - | |_ visualization - | - |_ domain_global - | |_ ... - | - |_ domain_northAmerica - | |_ ... - | - |_ installs - |_ mizuRoute - |_ SUMMA \ No newline at end of file diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 91c72ca44..34116a26e 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -161,11 +161,13 @@ def merge_subsets_into_one(src,pattern,des,name): '''Merges all files in {src} that match {pattern} into one file stored in /{des}/{name.nc}''' + # this runs out of memory sometimes # Find all files - src_files = glob.glob(str( src / pattern )) - + #src_files = glob.glob(str( src / pattern )) # Merge into one - out = xr.merge([xr.open_dataset(file) for file in src_files]) + #out = xr.merge([xr.open_dataset(file) for file in src_files]) + + out = xr.open_mfdataset(str( src / pattern )) # save to file out.to_netcdf(des / name) From 0619a403cd1bb57af4e7f6cbad2c6c3e51b1f2af Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 31 May 2023 11:38:12 +0900 Subject: [PATCH 0737/1472] Changed to params be_steps | 1.0000 | 1.0000 | 512.0000 relErrTol_ida | 1.0d-6 | 1.0d-10| 1.0d-3 absErrTol_ida | 1.0d-6 | 1.0d-10| 1.0d-3 --- build/source/dshare/get_ixname.f90 | 4 ++-- build/source/dshare/popMetadat.f90 | 4 ++-- build/source/dshare/var_lookup.f90 | 4 ++-- build/source/engine/read_pinit.f90 | 8 ++++---- build/source/engine/tol4ida.f90 | 28 ++++++++++++++-------------- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index d94104d10..bcc0721a8 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -390,8 +390,8 @@ function get_ixparam(varName) case('absConvTol_energy' ); get_ixparam = iLookPARAM%absConvTol_energy ! absolute convergence tolerance for energy (J m-3) be_numrec case('relConvTol_aquifr' ); get_ixparam = iLookPARAM%relConvTol_aquifr ! relative convergence tolerance for aquifer storage (-) be_numrec case('absConvTol_aquifr' ); get_ixparam = iLookPARAM%absConvTol_aquifr ! absolute convergence tolerance for aquifer storage (m) be_numrec - case('relConvTol_ida' ); get_ixparam = iLookPARAM%relConvTol_ida ! relative convergence tolerance for sundials ida - case('absConvTol_ida' ); get_ixparam = iLookPARAM%absConvTol_ida ! absolute convergence tolerance for sundials ida + case('relErrTol_ida' ); get_ixparam = iLookPARAM%relErrTol_ida ! relative error tolerance for sundials ida + case('absErrTol_ida' ); get_ixparam = iLookPARAM%absErrTol_ida ! absolute error tolerance for sundials ida case('zmin' ); get_ixparam = iLookPARAM%zmin ! minimum layer depth (m) case('zmax' ); get_ixparam = iLookPARAM%zmax ! maximum layer depth (m) case('zminLayer1' ); get_ixparam = iLookPARAM%zminLayer1 ! minimum layer depth for the 1st (top) layer (m) diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index fa6f3ca69..d6c6ca90f 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -272,8 +272,8 @@ subroutine popMetadat(err,message) mpar_meta(iLookPARAM%absConvTol_energy) = var_info('absConvTol_energy' , 'absolute convergence tolerance for energy be_numrec' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%relConvTol_aquifr) = var_info('relConvTol_aquifr' , 'relative convergence tolerance for aquifer storage be_numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%absConvTol_aquifr) = var_info('absConvTol_aquifr' , 'absolute convergence tolerance for aquifer storage be_numrec' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_ida) = var_info('relConvTol_ida' , 'relative convergence tolerance for sundials ida' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_ida) = var_info('absConvTol_ida' , 'absolute convergence tolerance for sundials ida' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relErrTol_ida) = var_info('relErrTol_ida' , 'relative error tolerance for sundials ida' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absErrTol_ida) = var_info('absErrTol_ida' , 'absolute error tolerance for sundials ida' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%zmin) = var_info('zmin' , 'minimum layer depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%zmax) = var_info('zmax' , 'maximum layer depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%zminLayer1) = var_info('zminLayer1' , 'minimum layer depth for the 1st (top) layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index fb5bfdb6a..b6ef359da 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -294,8 +294,8 @@ MODULE var_lookup integer(i4b) :: absConvTol_energy = integerMissing ! absolute convergence tolerance for energy (J m-3) integer(i4b) :: relConvTol_aquifr = integerMissing ! relative convergence tolerance for aquifer storage (-) integer(i4b) :: absConvTol_aquifr = integerMissing ! absolute convergence tolerance for aquifer storage (J m-3) - integer(i4b) :: relConvTol_ida = integerMissing ! relative convergence tolerance for sundials ida' - integer(i4b) :: absConvTol_ida = integerMissing ! absolute convergence tolerance for sundials ida' + integer(i4b) :: relErrTol_ida = integerMissing ! relative error tolerance for sundials ida' + integer(i4b) :: absErrTol_ida = integerMissing ! absolute error tolerance for sundials ida' integer(i4b) :: zmin = integerMissing ! minimum layer depth (m) integer(i4b) :: zmax = integerMissing ! maximum layer depth (m) integer(i4b) :: zminLayer1 = integerMissing ! minimum layer depth for the 1st (top) layer (m) diff --git a/build/source/engine/read_pinit.f90 b/build/source/engine/read_pinit.f90 index 6b4b9968a..be6a3f156 100644 --- a/build/source/engine/read_pinit.f90 +++ b/build/source/engine/read_pinit.f90 @@ -134,11 +134,11 @@ subroutine read_pinit(filenm,isLocal,mpar_meta,parFallback,err,message) if (parFallback(iLookPARAM%be_steps)%default_val < 0.99_rkind*realMissing) then parFallback(iLookPARAM%be_steps)%default_val = 1._rkind end if - if (parFallback(iLookPARAM%relConvTol_ida)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%relConvTol_ida)%default_val = 1.e-6_rkind + if (parFallback(iLookPARAM%relErrTol_ida)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%relErrTol_ida)%default_val = 1.e-6_rkind end if - if (parFallback(iLookPARAM%absConvTol_ida)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%absConvTol_ida)%default_val = 1.e-6_rkind + if (parFallback(iLookPARAM%absErrTol_ida)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%absErrTol_ida)%default_val = 1.e-6_rkind end if ! check we have populated all variables diff --git a/build/source/engine/tol4ida.f90 b/build/source/engine/tol4ida.f90 index a75a7c9ca..97ec45f83 100644 --- a/build/source/engine/tol4ida.f90 +++ b/build/source/engine/tol4ida.f90 @@ -208,20 +208,20 @@ subroutine popTol4ida(& ! initialize error control err=0; message='popTol4ida/' - absTolTempCas = mpar_data%var(iLookPARAM%absConvTol_ida)%dat(1) - relTolTempCas = mpar_data%var(iLookPARAM%relConvTol_ida)%dat(1) - absTolTempVeg = mpar_data%var(iLookPARAM%absConvTol_ida)%dat(1) - relTolTempVeg = mpar_data%var(iLookPARAM%relConvTol_ida)%dat(1) - absTolWatVeg = mpar_data%var(iLookPARAM%absConvTol_ida)%dat(1) - relTolWatVeg = mpar_data%var(iLookPARAM%relConvTol_ida)%dat(1) - absTolTempSoilSnow = mpar_data%var(iLookPARAM%absConvTol_ida)%dat(1) - relTolTempSoilSnow = mpar_data%var(iLookPARAM%relConvTol_ida)%dat(1) - absTolWatSnow = mpar_data%var(iLookPARAM%absConvTol_ida)%dat(1) - relTolWatSnow = mpar_data%var(iLookPARAM%relConvTol_ida)%dat(1) - absTolMatric = mpar_data%var(iLookPARAM%absConvTol_ida)%dat(1) - relTolMatric = mpar_data%var(iLookPARAM%relConvTol_ida)%dat(1) - absTolAquifr = mpar_data%var(iLookPARAM%absConvTol_ida)%dat(1) - relTolAquifr = mpar_data%var(iLookPARAM%relConvTol_ida)%dat(1) + absTolTempCas = mpar_data%var(iLookPARAM%absErrTol_ida)%dat(1) + relTolTempCas = mpar_data%var(iLookPARAM%relErrTol_ida)%dat(1) + absTolTempVeg = mpar_data%var(iLookPARAM%absErrTol_ida)%dat(1) + relTolTempVeg = mpar_data%var(iLookPARAM%relErrTol_ida)%dat(1) + absTolWatVeg = mpar_data%var(iLookPARAM%absErrTol_ida)%dat(1) + relTolWatVeg = mpar_data%var(iLookPARAM%relErrTol_ida)%dat(1) + absTolTempSoilSnow = mpar_data%var(iLookPARAM%absErrTol_ida)%dat(1) + relTolTempSoilSnow = mpar_data%var(iLookPARAM%relErrTol_ida)%dat(1) + absTolWatSnow = mpar_data%var(iLookPARAM%absErrTol_ida)%dat(1) + relTolWatSnow = mpar_data%var(iLookPARAM%relErrTol_ida)%dat(1) + absTolMatric = mpar_data%var(iLookPARAM%absErrTol_ida)%dat(1) + relTolMatric = mpar_data%var(iLookPARAM%relErrTol_ida)%dat(1) + absTolAquifr = mpar_data%var(iLookPARAM%absErrTol_ida)%dat(1) + relTolAquifr = mpar_data%var(iLookPARAM%relErrTol_ida)%dat(1) ! ----- ! * initialize state vectors... From b5eaae113898da3cef48702430570e5f73a8923d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 5 Jun 2023 17:01:27 +0900 Subject: [PATCH 0738/1472] just comment typos --- build/source/engine/coupled_em.f90 | 5 ++--- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/systemSolv.f90 | 1 - build/source/engine/varSubstep.f90 | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 25fc2900e..95913d4a9 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -586,11 +586,10 @@ subroutine coupled_em(& ! NOTE 2: this initialization needs to be done AFTER the call to canopySnow, since canopySnow uses canopy drip drom the previous time step if(.not.computeVegFlux)then flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) = flux_data%var(iLookFLUX%scalarRainfall)%dat(1) - flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) = 0._rkind else flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) = 0._rkind - flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) = 0._rkind end if + flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) = 0._rkind ! **************************************************************************************************** ! *** MAIN SOLVER ************************************************************************************ @@ -854,7 +853,7 @@ subroutine coupled_em(& nState, & ! intent(in): total number of layers dt_sub, & ! intent(in): length of the model sub-step whole_step, & ! intent(in): length of whole step for surface drainage and average flux - (nsub==1), & ! intent(in): logical flag to denote the first substep + (dt_solv Date: Mon, 5 Jun 2023 17:58:13 +0900 Subject: [PATCH 0739/1472] scalarSoilResistance should have been mapped to iname_nrgLayer --- build/source/dshare/flxMapping.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/dshare/flxMapping.f90 b/build/source/dshare/flxMapping.f90 index 1195a53a4..56d486ec5 100644 --- a/build/source/dshare/flxMapping.f90 +++ b/build/source/dshare/flxMapping.f90 @@ -97,7 +97,7 @@ subroutine flxMapping(err,message) flux2state_orig(iLookFLUX%scalarGroundResistance) = flux2state(state1=iname_nrgCanopy, state2=iname_nrgLayer) flux2state_orig(iLookFLUX%scalarCanopyResistance) = flux2state(state1=iname_nrgCanopy, state2=integerMissing) flux2state_orig(iLookFLUX%scalarLeafResistance) = flux2state(state1=iname_nrgCanopy, state2=integerMissing) - flux2state_orig(iLookFLUX%scalarSoilResistance) = flux2state(state1=iname_nrgCanopy, state2=integerMissing) + flux2state_orig(iLookFLUX%scalarSoilResistance) = flux2state(state1=iname_nrgCanopy, state2=iname_nrgLayer) flux2state_orig(iLookFLUX%scalarSenHeatTotal) = flux2state(state1=iname_nrgCanopy, state2=iname_nrgLayer) flux2state_orig(iLookFLUX%scalarSenHeatCanopy) = flux2state(state1=iname_nrgCanopy, state2=integerMissing) flux2state_orig(iLookFLUX%scalarSenHeatGround) = flux2state(state1=iname_nrgCanopy, state2=iname_nrgLayer) From bf739d2df987b33da9b3f55de5179cf50d7b7e63 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 5 Jun 2023 18:41:20 +0900 Subject: [PATCH 0740/1472] Detect events should probably be on ... --- build/source/engine/summaSolveSundials4ida.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/summaSolveSundials4ida.f90 b/build/source/engine/summaSolveSundials4ida.f90 index 18e997960..937f67303 100644 --- a/build/source/engine/summaSolveSundials4ida.f90 +++ b/build/source/engine/summaSolveSundials4ida.f90 @@ -227,7 +227,7 @@ subroutine summaSolveSundials4ida( & real(rkind),allocatable :: mLayerMatricHeadPrimePrev(:) ! previous derivative value for total water matric potential (m s-1) real(rkind),allocatable :: dCompress_dPsiPrev(:) ! previous derivative value soil compression logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true - logical(lgt),parameter :: detect_events = .false. ! flag to do event detection and restarting, default true + logical(lgt),parameter :: detect_events = .true. ! flag to do event detection and restarting, default true logical(lgt),parameter :: use_fdJac = .false. ! flag to use finite difference Jacobian, default false ! ----------------------------------------------------------------------------------------------------- From 19fca2ba6faa180fd623ab5eef1f526f4a5967e7 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 7 Jun 2023 12:13:20 +0900 Subject: [PATCH 0741/1472] cleaning up stuff so files more similar between sundials and BE, plus removing some unneeded arguments --- build/source/dshare/type4ida.f90 | 6 +- build/source/dshare/type4kinsol.f90 | 86 +- build/source/engine/computJacob.f90 | 12 +- build/source/engine/computJacobSundials.f90 | 180 ++-- build/source/engine/computResid.f90 | 4 +- build/source/engine/eval8summa.f90 | 32 +- build/source/engine/eval8summaSundials.f90 | 6 +- build/source/engine/getVectorz.f90 | 2 +- build/source/engine/summaSolve.f90 | 148 ++-- build/source/engine/systemSolv.f90 | 916 ++++++++++---------- build/source/engine/systemSolvSundials.f90 | 33 +- build/source/engine/updateVars.f90 | 60 +- build/source/engine/updateVarsSundials.f90 | 27 +- build/source/engine/varSubstep.f90 | 5 +- 14 files changed, 722 insertions(+), 795 deletions(-) diff --git a/build/source/dshare/type4ida.f90 b/build/source/dshare/type4ida.f90 index 5d60c11e7..8fe05f04a 100644 --- a/build/source/dshare/type4ida.f90 +++ b/build/source/dshare/type4ida.f90 @@ -23,8 +23,8 @@ module type4ida integer(i4b) :: nState ! total number of state variables integer(i4b) :: ixMatrix ! form of matrix (dense or banded) logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt) :: firstFluxCall - logical(lgt) :: firstSplitOper + logical(lgt) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution type(zLookup) :: lookup_data ! lookup tables @@ -69,7 +69,7 @@ module type4ida real(rkind), allocatable :: mLayerEnthalpyPrev(:) ! enthalpy of snow and soil (J m-3) at previous step real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! derivative value for total water matric potential (m s-1) real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - integer(i4b) :: ixSaturation + integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) integer(i4b) :: err ! error code character(len = 50) :: message ! error message end type eqnsData diff --git a/build/source/dshare/type4kinsol.f90 b/build/source/dshare/type4kinsol.f90 index 448700734..715a95ca7 100644 --- a/build/source/dshare/type4kinsol.f90 +++ b/build/source/dshare/type4kinsol.f90 @@ -1,52 +1,54 @@ module type4kinsol - ! data types - USE nrtype - USE, intrinsic :: iso_c_binding - USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (dp) - model_options ! defines the model decisions - implicit none +! data types +USE nrtype +USE, intrinsic :: iso_c_binding -type kinsol_data - real(rkind) :: dt - integer(i4b) :: nSnow - integer(i4b) :: nSoil - integer(i4b) :: nLayers - integer(i4b) :: nState - logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt) :: computeBaseflow - integer(i4b) :: ixMatrix - logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution - type(var_i) :: type_data ! type of vegetation and soil - type(var_d) :: attr_data ! spatial attributes - type(var_dlength) :: mpar_data ! model parameters - type(var_d) :: forc_data ! model forcing data - type(var_dlength) :: bvar_data ! model variables for the local basin - type(var_dlength) :: flux_data ! model fluxes for a local HRU - integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - type(var_ilength) :: indx_data - type(var_dlength) :: prog_data - type(var_dlength) :: diag_data - type(var_dlength) :: deriv_data +USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (dp) + model_options ! defines the model decisions +implicit none + +type eqnsData + real(rkind) :: dt ! data step + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers + integer(i4b) :: nLayers ! total number of layers + integer(i4b) :: nState ! total number of state variables + integer(i4b) :: ixMatrix ! form of matrix (dense or banded) + logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution + type(zLookup) :: lookup_data ! lookup tables + type(var_i) :: type_data ! type of vegetation and soil + type(var_d) :: attr_data ! spatial attributes + type(var_dlength) :: mpar_data ! model parameters + type(var_d) :: forc_data ! model forcing data + type(var_dlength) :: bvar_data ! model variables for the local basin + type(var_dlength) :: prog_data ! prognostic variables for a local HRU + type(var_ilength) :: indx_data ! indices defining model states and layers + type(var_dlength) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength) :: flux_data ! model fluxes for a local HRU + type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + real(rkind), allocatable :: stateVecPrev(:) ! state vector from the previous iteration to help with infeasibility + real(qp), allocatable :: sMul(:) ! state vector multiplier (used in the residual calculations) + real(rkind), allocatable :: dMat(:) ! diagonal of the Jacobian matrix + real(rkind), allocatable :: fluxVec(:) ! flux vector + real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation + real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + integer(i4b) :: ixSaturation ! index of the lowest saturated layer + integer(i4b) :: err ! error code + character(len = 50) :: message ! error message logical(lgt) :: feasible ! flag to denote the feasibility of the solution real(rkind) :: fEval ! function evaluation - real(rkind),allocatable :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(rkind),allocatable :: fScale(:) ! function scaling vector - real(rkind),allocatable :: fluxVec(:) ! flux vector - real(rkind),allocatable :: resSink(:) ! sink terms on the RHS of the flux equation - type(model_options),allocatable :: model_decisions(:) ! model decisions - real(rkind), allocatable :: dBaseflow_dMatric(:,:) - real(rkind), allocatable :: dMat(:) - real(rkind), allocatable :: stateVecPrev(:) ! state vector from the previous iteration to help with infeasibility logical(lgt) :: firstStateiteration ! flag to denote if we computed an iteration so we know to save the state -end type kinsol_data +end type eqnsData end module type4kinsol \ No newline at end of file diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 5c5c401c9..eec627a6f 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -68,8 +68,8 @@ module computJacob_module ! constants USE multiconst,only:& - LH_fus, & ! latent heat of fusion (J kg-1) - iden_water ! intrinsic density of liquid water (kg m-3) + LH_fus, & ! latent heat of fusion (J kg-1) + iden_water ! intrinsic density of liquid water (kg m-3) implicit none ! define constants @@ -83,7 +83,7 @@ module computJacob_module ! public subroutine computJacob: compute the Jacobian matrix ! ********************************************************************************************************** subroutine computJacob(& - ! input: model control + ! input: model control dt, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -117,10 +117,10 @@ subroutine computJacob(& type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! input-output: Jacobian and its diagonal - real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix - real(rkind),intent(out) :: aJac(:,:) ! Jacobian matrix + real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix + real(rkind),intent(out) :: aJac(:,:) ! Jacobian matrix ! output variables integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message diff --git a/build/source/engine/computJacobSundials.f90 b/build/source/engine/computJacobSundials.f90 index 85c5ddd34..8237bcfd4 100644 --- a/build/source/engine/computJacobSundials.f90 +++ b/build/source/engine/computJacobSundials.f90 @@ -106,7 +106,7 @@ module computJacobSundials_module private public::computJacobSundials -public::computJacobSetup +public::computJacob4idaSetup public::computJacob4ida contains @@ -134,14 +134,12 @@ subroutine computJacobSundials(& deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) ! input: state variables - mLayerTemp, & ! intent(in): vector of layer temperature (K) mLayerTempPrime, & ! intent(in): vector of derivative value for layer temperature (K) - mLayerMatricHeadPrime, & ! intent(in) - mLayerMatricHeadLiqPrime, & ! intent(in) - mLayerVolFracWatPrime, & ! intent(in) - scalarCanopyTemp, & ! intent(in): temperature of the vegetation canopy (K) + mLayerMatricHeadPrime, & ! intent(in): vector of derivative value for layer matric head + mLayerMatricHeadLiqPrime, & ! intent(in): vector of derivative value for layer liquid matric head + mLayerVolFracWatPrime, & ! intent(in): vector of derivative value for layer water volume fraction scalarCanopyTempPrime, & ! intent(in): derivative value for temperature of the vegetation canopy (K) - scalarCanopyWatPrime, & ! intent(in) + scalarCanopyWatPrime, & ! intent(in): derivative value for water content of the vegetation canopy ! input-output: Jacobian and its diagonal dMat, & ! intent(inout): diagonal of the Jacobian matrix aJac, & ! intent(out): Jacobian matrix @@ -168,14 +166,14 @@ subroutine computJacobSundials(& type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! input: state variables - real(rkind),intent(in) :: mLayerTemp(:) - real(rkind),intent(in) :: mLayerTempPrime(:) - real(rkind),intent(in) :: mLayerMatricHeadPrime(:) - real(rkind),intent(in) :: mLayerMatricHeadLiqPrime(:) - real(rkind),intent(in) :: mLayerVolFracWatPrime(:) - real(rkind),intent(in) :: scalarCanopyTemp - real(rkind),intent(in) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) - real(rkind),intent(in) :: scalarCanopyWatPrime + real(rkind),intent(in) :: mLayerTemp(:) ! vector of layer temperature (K) + real(rkind),intent(in) :: mLayerTempPrime(:) ! vector of derivative value for layer temperature + real(rkind),intent(in) :: mLayerMatricHeadPrime(:) ! vector of derivative value for layer matric head + real(rkind),intent(in) :: mLayerMatricHeadLiqPrime(:)! vector of derivative value for layer liquid matric head + real(rkind),intent(in) :: mLayerVolFracWatPrime(:) ! vector of derivative value for layer water volume fraction + real(rkind),intent(in) :: scalarCanopyTemp ! temperature of the vegetation canopy (K) + real(rkind),intent(in) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) + real(rkind),intent(in) :: scalarCanopyWatPrime ! derivative value for water content of the vegetation canopy ! input-output: Jacobian and its diagonal real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix real(rkind),intent(out) :: aJac(:,:) ! Jacobian matrix @@ -1083,9 +1081,9 @@ end subroutine computJacobSundials ! ********************************************************************************************************** -! public subroutine computJacobSetup: compute the Jacobian matrix +! public subroutine computJacob4idaSetup: compute the Jacobian matrix ! ********************************************************************************************************** -subroutine computJacobSetup(& +subroutine computJacob4idaSetup(& ! input: model control cj, & ! intent(in): this scalar changes whenever the step size or method order changes dt, & ! intent(in): time step @@ -1094,8 +1092,7 @@ subroutine computJacobSetup(& nLayers, & ! intent(in): total number of layers ixMatrix, & ! intent(in): form of the Jacobian matrix computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors + ! input: state vectors stateVec, & ! intent(in): model state vector stateVecPrime, & ! intent(in): derivative of model state vector sMul, & ! intent(in): state vector multiplier (used in the residual calculations) @@ -1116,7 +1113,7 @@ subroutine computJacobSetup(& ! -------------------------------------------------------------------------------------------------------------------------------- ! provide access to subroutines USE getVectorz_module, only:varExtract ! extract variables from the state vector - USE updateVarsSundials_module, only:updateVarsSundials ! update prognostic variables + USE updateVarsSundials_module, only:updateVarsSundials ! update prognostic variables implicit none ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- @@ -1128,8 +1125,7 @@ subroutine computJacobSetup(& integer(i4b),intent(in) :: nLayers ! total number of layers integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - ! input: state vectors + ! input: state vectors real(rkind),intent(in) :: stateVec(:) ! model state vector real(rkind),intent(in) :: stateVecPrime(:) ! model state vector real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) @@ -1181,7 +1177,7 @@ subroutine computJacobSetup(& real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! derivative value for volumetric fraction of liquid water (-) real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! derivative value for volumetric fraction of ice (-) ! other local variables - character(LEN=256) :: cmessage ! error message of downwind routine + character(LEN=256) :: cmessage ! error message of downwind routine real(rkind) :: dt1 ! -------------------------------------------------------------------------------------------------------------------------------- @@ -1201,7 +1197,7 @@ subroutine computJacobSetup(& ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="computJacobSetup/" + err=0; message="computJacob4idaSetup/" ! initialize to state variable from the last update ! should all be set to previous values if splits, but for now operator splitting is not hooked up @@ -1281,39 +1277,40 @@ subroutine computJacobSetup(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + ! update diagnostic variables and derivatives call updateVarsSundials(& - ! input - .true., & ! intent(in): logical flag if computing Jacobian for Sundials solver - .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2 - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! - mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) - mLayerVolFracIcePrime, & ! - mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) - ! output: error control - err,cmessage) ! intent(out): error control + ! input + .true., & ! intent(in): logical flag if computing for Jacobian update + .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output: variables for the vegetation canopy + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2 + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempPrime, & ! + mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) + mLayerVolFracIcePrime, & ! + mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) + ! output: error control + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! ----- @@ -1326,39 +1323,37 @@ subroutine computJacobSetup(& ! or in the call to eval8summaSundials in the previous iteration dt1 = 1._qp call computJacobSundials(& - ! input: model control - cj, & ! intent(in): this scalar changes whenever the step size or method order changes - dt1, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - (ixGroundwater==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow - ixMatrix, & ! intent(in): form of the Jacobian matrix - specificStorage, & ! intent(in): specific storage coefficient (m-1) - theta_sat, & ! intent(in): soil porosity (-) - ixRichards, & ! intent(in): choice of option for Richards' equation - ! input: data structures - indx_data, & ! intent(in): index data - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! input: state variables - mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) - mLayerTempPrime, & ! intent(in) - mLayerMatricHeadPrime, & ! intent(in) - mLayerMatricHeadLiqPrime, & ! intent(in) - mLayerVolFracWatPrime, & ! intent(in) - scalarCanopyTempTrial, & ! intent(in) - scalarCanopyTempPrime, & ! intent(in) derivative value for temperature of the vegetation canopy (K) - scalarCanopyWatPrime, & ! intetn(in) - ! input-output: Jacobian and its diagonal - dMat, & ! intent(inout): diagonal of the Jacobian matrix - Jac, & ! intent(out): Jacobian matrix - ! output: error control - err,cmessage) ! intent(out): error code and error message - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + ! input: model control + cj, & ! intent(in): this scalar changes whenever the step size or method order changes + dt1, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + (ixGroundwater==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow + ixMatrix, & ! intent(in): form of the Jacobian matrix + specificStorage, & ! intent(in): specific storage coefficient (m-1) + theta_sat, & ! intent(in): soil porosity (-) + ixRichards, & ! intent(in): choice of option for Richards' equation + ! input: data structures + indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables + dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! input: state variables + mLayerTempPrime, & ! intent(in): derivative value for temperature of each snow and soil layer (K) + mLayerMatricHeadPrime, & ! intent(in): derivative value for matric head of each snow and soil layer (m) + mLayerMatricHeadLiqPrime, & ! intent(in): derivative value for liquid water matric head of each snow and soil layer (m) + mLayerVolFracWatPrime, & ! intent(in): derivative value for volumetric total water content of each snow and soil layer (-) + scalarCanopyTempPrime, & ! intent(in): derivative value for temperature of the vegetation canopy (K) + scalarCanopyWatPrime, & ! intent(in): derivative value for total water content of the vegetation canopy (kg m-2) + ! input-output: Jacobian and its diagonal + dMat, & ! intent(inout): diagonal of the Jacobian matrix + Jac, & ! intent(out): Jacobian matrix + ! output: error control + err,cmessage) ! intent(out): error code and error message + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! end association with the information in the data structures end associate @@ -1405,7 +1400,6 @@ integer(c_int) function computJacob4ida(t, cj, sunvec_y, sunvec_yp, sunvec_r, & ! pointers to data in SUNDIALS vectors real(rkind), pointer :: stateVec(:) ! state vector real(rkind), pointer :: stateVecPrime(:)! derivative of the state vector - real(rkind), pointer :: rVec(:) ! residual vector real(rkind), pointer :: Jac(:,:) ! Jacobian matrix type(eqnsData), pointer :: eqns_data ! equations data @@ -1417,12 +1411,11 @@ integer(c_int) function computJacob4ida(t, cj, sunvec_y, sunvec_yp, sunvec_r, & ! get data arrays from SUNDIALS vectors stateVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_y) stateVecPrime(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_yp) - rVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_r) if (eqns_data%ixMatrix==ixBandMatrix) Jac(1:nBands, 1:eqns_data%nState) => FSUNBandMatrix_Data(sunmat_J) if (eqns_data%ixMatrix==ixFullMatrix) Jac(1:eqns_data%nState, 1:eqns_data%nState) => FSUNDenseMatrix_Data(sunmat_J) ! compute Jacobian matrix - call computJacobSetup(& + call computJacob4idaSetup(& ! input: model control cj, & ! intent(in): this scalar changes whenever the step size or method order changes eqns_data%dt, & ! intent(in): data step @@ -1431,8 +1424,7 @@ integer(c_int) function computJacob4ida(t, cj, sunvec_y, sunvec_yp, sunvec_r, & eqns_data%nLayers, & ! intent(in): number of layers eqns_data%ixMatrix, & ! intent(in): type of matrix (dense or banded) eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors + ! input: state vectors stateVec, & ! intent(in): model state vector stateVecPrime, & ! intent(in): model state vector eqns_data%sMul, & ! intent(in): state vector multiplier (used in the residual calculations) diff --git a/build/source/engine/computResid.f90 b/build/source/engine/computResid.f90 index 5bc6805d7..66d2010cd 100644 --- a/build/source/engine/computResid.f90 +++ b/build/source/engine/computResid.f90 @@ -114,7 +114,7 @@ subroutine computResid(& integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain ! input: flux vectors - real(rkind),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(rkind),intent(in) :: fVec(:) ! flux vector ! input: state variables (already disaggregated into scalars and vectors) real(rkind),intent(in) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) @@ -137,7 +137,7 @@ subroutine computResid(& type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers ! output real(rkind),intent(out) :: rAdd(:) ! additional (sink) terms on the RHS of the state equation - real(rkind),intent(out) :: rVec(:) ! NOTE: qp ! residual vector + real(qp),intent(out) :: rVec(:) ! NOTE: qp ! residual vector integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 12cad502f..02a369aca 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -108,7 +108,7 @@ subroutine eval8summa(& computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors - stateVecTrial, & ! intent(in): model state vector + stateVec, & ! intent(in): model state vector fScale, & ! intent(in): function scaling vector sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures @@ -164,9 +164,9 @@ subroutine eval8summa(& logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors - real(rkind),intent(in) :: stateVecTrial(:) ! model state vector + real(rkind),intent(in) :: stateVec(:) ! model state vector real(rkind),intent(in) :: fScale(:) ! function scaling vector - real(rkind),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) ! input: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions type(zLookup), intent(in) :: lookup_data ! lookup tables @@ -188,7 +188,7 @@ subroutine eval8summa(& logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution real(rkind),intent(out) :: fluxVec(:) ! flux vector real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation - real(rkind),intent(out) :: resVec(:) ! NOTE: qp ! residual vector + real(qp),intent(out) :: resVec(:) ! NOTE: qp ! residual vector real(rkind),intent(out) :: fEval ! function evaluation ! output: error control integer(i4b),intent(out) :: err ! error code @@ -302,27 +302,28 @@ subroutine eval8summa(& ! check that the canopy air space temperature is reasonable if(ixCasNrg/=integerMissing)then - if(stateVecTrial(ixCasNrg) > canopyTempMax) feasible=.false. - !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVecTrial( ixCasNrg )', feasible, canopyTempMax, stateVecTrial(ixCasNrg) + if(stateVec(ixCasNrg) > canopyTempMax) feasible=.false. + if(stateVec(ixCasNrg) > canopyTempMax) message=trim(message)//'canopy air space temp high,' + !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixCasNrg )', feasible, canopyTempMax, stateVec(ixCasNrg) endif ! check that the canopy air space temperature is reasonable if(ixVegNrg/=integerMissing)then - if(stateVecTrial(ixVegNrg) > canopyTempMax) feasible=.false. - !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVecTrial( ixVegNrg )', feasible, canopyTempMax, stateVecTrial(ixVegNrg) + if(stateVec(ixVegNrg) > canopyTempMax) feasible=.false. + !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixVegNrg )', feasible, canopyTempMax, stateVec(ixVegNrg) endif ! check canopy liquid water is not negative if(ixVegHyd/=integerMissing)then - if(stateVecTrial(ixVegHyd) < 0._rkind) feasible=.false. - !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVecTrial( ixVegHyd )', feasible, 0._rkind, stateVecTrial(ixVegHyd) + if(stateVec(ixVegHyd) < 0._rkind) feasible=.false. + !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVec( ixVegHyd )', feasible, 0._rkind, stateVec(ixVegHyd) end if ! check snow temperature is below freezing if(count(ixSnowOnlyNrg/=integerMissing)>0)then - if(any(stateVecTrial( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. + if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. !do iLayer=1,nSnow - ! if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVecTrial( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVecTrial( ixSnowOnlyNrg(iLayer) ) + ! if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVec( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVec( ixSnowOnlyNrg(iLayer) ) !enddo endif @@ -346,8 +347,8 @@ subroutine eval8summa(& end select ! --> check - if(stateVecTrial( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVecTrial( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. - !if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax + if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. + !if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax endif ! if water states @@ -389,7 +390,7 @@ subroutine eval8summa(& ! extract variables from the model state vector call varExtract(& ! input - stateVecTrial, & ! intent(in): model state vector (mixed units) + stateVec, & ! intent(in): model state vector (mixed units) diag_data, & ! intent(in): model diagnostic variables for a local HRU prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers @@ -414,7 +415,6 @@ subroutine eval8summa(& call updateVars(& ! input .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - lookup_data, & ! intent(in): lookup tables for a local HRU mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaSundials.f90 index 2479cde76..176dff31d 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaSundials.f90 @@ -308,8 +308,8 @@ subroutine eval8summaSundials(& ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) - heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & ! intent(out): heat capacity for snow and soil + heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(out): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & ! intent(out): heat capacity for snow and soil ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -475,7 +475,7 @@ subroutine eval8summaSundials(& ! update diagnostic variables and derivatives call updateVarsSundials(& ! input - .false., & ! intent(in): logical flag if computing Jacobian for Sundials solver + .false., & ! intent(in): logical flag if computing for Jacobian update .false., & ! intent(in): logical flag to adjust temperature to account for the energy mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index f6a320134..e47239c46 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -266,7 +266,7 @@ subroutine getScaling(& ! output: state vectors real(rkind),intent(out) :: fScale(:) ! function scaling vector (mixed units) real(rkind),intent(out) :: xScale(:) ! variable scaling vector (mixed units) - real(rkind),intent(out) :: sMul(:) ! NOTE: qp ! multiplier for state vector (used in the residual calculations) + real(qp),intent(out) :: sMul(:) ! NOTE: qp ! multiplier for state vector (used in the residual calculations) real(rkind),intent(out) :: dMat(:) ! diagonal of the Jacobian matrix (excludes fluxes) ! output: error control integer(i4b),intent(out) :: err ! error code diff --git a/build/source/engine/summaSolve.f90 b/build/source/engine/summaSolve.f90 index 1e7329584..b5b8d893e 100644 --- a/build/source/engine/summaSolve.f90 +++ b/build/source/engine/summaSolve.f90 @@ -153,14 +153,14 @@ subroutine summaSolve(& logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors - real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector - real(rkind),intent(inout) :: xMin,xMax ! brackets of the root - real(rkind),intent(in) :: fScale(:) ! function scaling vector - real(rkind),intent(in) :: xScale(:) ! "variable" scaling vector, i.e., for state variables - real(rkind),intent(in) :: rVec(:) ! NOTE: qp ! residual vector - real(rkind),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) - real(rkind),intent(inout) :: dMat(:) ! diagonal matrix (excludes flux derivatives) - real(rkind),intent(in) :: fOld ! old function evaluation + real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector + real(rkind),intent(inout) :: xMin,xMax ! brackets of the root + real(rkind),intent(in) :: fScale(:) ! function scaling vector + real(rkind),intent(in) :: xScale(:) ! "variable" scaling vector, i.e., for state variables + real(qp),intent(in) :: rVec(:) ! NOTE: qp ! residual vector + real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + real(rkind),intent(inout) :: dMat(:) ! diagonal matrix (excludes flux derivatives) + real(rkind),intent(in) :: fOld ! old function evaluation ! input: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions type(zLookup), intent(in) :: lookup_data ! lookup tables @@ -177,13 +177,13 @@ subroutine summaSolve(& type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(inout) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + real(rkind),intent(inout) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors - real(rkind),intent(out) :: stateVecNew(:) ! new state vector - real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector - real(rkind),intent(out) :: resSinkNew(:) ! sink terms on the RHS of the flux equation - real(rkind),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector - real(rkind),intent(out) :: fNew ! new function evaluation + real(rkind),intent(out) :: stateVecNew(:) ! new state vector + real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector + real(rkind),intent(out) :: resSinkNew(:) ! sink terms on the RHS of the flux equation + real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector + real(rkind),intent(out) :: fNew ! new function evaluation logical(lgt),intent(out) :: converged ! convergence flag ! output: error control integer(i4b),intent(out) :: err ! error code @@ -194,13 +194,13 @@ subroutine summaSolve(& ! Jacobian matrix logical(lgt),parameter :: doNumJacobian=.false. ! flag to compute the numerical Jacobian matrix logical(lgt),parameter :: testBandDiagonal=.false. ! flag to test the band diagonal Jacobian matrix - real(rkind) :: nJac(nState,nState) ! numerical Jacobian matrix - real(rkind) :: aJac(nLeadDim,nState) ! Jacobian matrix - real(rkind) :: aJacScaled(nLeadDim,nState) ! Jacobian matrix (scaled) - real(rkind) :: aJacScaledTemp(nLeadDim,nState) ! Jacobian matrix (scaled) -- temporary copy since decomposed in lapack + real(rkind) :: nJac(nState,nState) ! numerical Jacobian matrix + real(rkind) :: aJac(nLeadDim,nState) ! Jacobian matrix + real(rkind) :: aJacScaled(nLeadDim,nState) ! Jacobian matrix (scaled) + real(rkind) :: aJacScaledTemp(nLeadDim,nState) ! Jacobian matrix (scaled) -- temporary copy since decomposed in lapack ! solution/step vectors - real(rkind),dimension(nState) :: rVecScaled ! residual vector (scaled) - real(rkind),dimension(nState) :: newtStepScaled ! full newton step (scaled) + real(rkind),dimension(nState) :: rVecScaled ! residual vector (scaled) + real(rkind),dimension(nState) :: newtStepScaled ! full newton step (scaled) ! step size refinement logical(lgt) :: doRefine ! flag for step refinement integer(i4b),parameter :: ixLineSearch=1001 ! step refinement = line search @@ -347,36 +347,36 @@ subroutine lineSearchRefinement(doLineSearch,stateVecTrial,newtStepScaled,aJacSc implicit none ! input logical(lgt),intent(in) :: doLineSearch ! flag to do the line search - real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector - real(rkind),intent(in) :: newtStepScaled(:) ! scaled newton step - real(rkind),intent(in) :: aJacScaled(:,:) ! scaled jacobian matrix - real(rkind),intent(in) :: rVecScaled(:) ! scaled residual vector - real(rkind),intent(in) :: fOld ! old function value + real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector + real(rkind),intent(in) :: newtStepScaled(:) ! scaled newton step + real(rkind),intent(in) :: aJacScaled(:,:) ! scaled jacobian matrix + real(rkind),intent(in) :: rVecScaled(:) ! scaled residual vector + real(rkind),intent(in) :: fOld ! old function value ! output - real(rkind),intent(out) :: stateVecNew(:) ! new state vector - real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector - real(rkind),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector - real(rkind),intent(out) :: fNew ! new function evaluation + real(rkind),intent(out) :: stateVecNew(:) ! new state vector + real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector + real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector + real(rkind),intent(out) :: fNew ! new function evaluation logical(lgt),intent(out) :: converged ! convergence flag integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------- ! local character(len=256) :: cmessage ! error message of downwind routine - real(rkind) :: gradScaled(nState) ! scaled gradient - real(rkind) :: xInc(nState) ! iteration increment (re-scaled to original units of the state vector) + real(rkind) :: gradScaled(nState) ! scaled gradient + real(rkind) :: xInc(nState) ! iteration increment (re-scaled to original units of the state vector) logical(lgt) :: feasible ! flag to denote the feasibility of the solution integer(i4b) :: iLine ! line search index integer(i4b),parameter :: maxLineSearch=5 ! maximum number of backtracks - real(rkind),parameter :: alpha=1.e-4_rkind ! check on gradient - real(rkind) :: xLambda ! backtrack magnitude - real(rkind) :: xLambdaTemp ! temporary backtrack magnitude - real(rkind) :: slopeInit ! initial slope - real(rkind) :: rhs1,rhs2 ! rhs used to compute the cubic - real(rkind) :: aCoef,bCoef ! coefficients in the cubic - real(rkind) :: disc ! temporary variable used in cubic - real(rkind) :: xLambdaPrev ! previous lambda value (used in the cubic) - real(rkind) :: fPrev ! previous function evaluation (used in the cubic) + real(rkind),parameter :: alpha=1.e-4_rkind ! check on gradient + real(rkind) :: xLambda ! backtrack magnitude + real(rkind) :: xLambdaTemp ! temporary backtrack magnitude + real(rkind) :: slopeInit ! initial slope + real(rkind) :: rhs1,rhs2 ! rhs used to compute the cubic + real(rkind) :: aCoef,bCoef ! coefficients in the cubic + real(rkind) :: disc ! temporary variable used in cubic + real(rkind) :: xLambdaPrev ! previous lambda value (used in the cubic) + real(rkind) :: fPrev ! previous function evaluation (used in the cubic) ! -------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='lineSearchRefinement/' @@ -515,16 +515,16 @@ subroutine trustRegionRefinement(doTrustRefinement,stateVecTrial,newtStepScaled, implicit none ! input logical(lgt),intent(in) :: doTrustRefinement ! flag to refine using trust regions - real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector - real(rkind),intent(in) :: newtStepScaled(:) ! scaled newton step - real(rkind),intent(in) :: aJacScaled(:,:) ! scaled jacobian matrix - real(rkind),intent(in) :: rVecScaled(:) ! scaled residual vector - real(rkind),intent(in) :: fOld ! old function value + real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector + real(rkind),intent(in) :: newtStepScaled(:) ! scaled newton step + real(rkind),intent(in) :: aJacScaled(:,:) ! scaled jacobian matrix + real(rkind),intent(in) :: rVecScaled(:) ! scaled residual vector + real(rkind),intent(in) :: fOld ! old function value ! output - real(rkind),intent(out) :: stateVecNew(:) ! new state vector - real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector - real(rkind),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector - real(rkind),intent(out) :: fNew ! new function evaluation + real(rkind),intent(out) :: stateVecNew(:) ! new state vector + real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector + real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector + real(rkind),intent(out) :: fNew ! new function evaluation logical(lgt),intent(out) :: converged ! convergence flag integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -581,31 +581,31 @@ subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fl USE globalData,only:dNaN ! double precision NaN implicit none ! input - real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector - real(rkind),intent(in) :: rVecScaled(:) ! scaled residual vector - real(rkind),intent(in) :: newtStepScaled(:) ! scaled newton step + real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector + real(rkind),intent(in) :: rVecScaled(:) ! scaled residual vector + real(rkind),intent(in) :: newtStepScaled(:) ! scaled newton step ! output - real(rkind),intent(out) :: stateVecNew(:) ! new state vector - real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector - real(rkind),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector - real(rkind),intent(out) :: fNew ! new function evaluation + real(rkind),intent(out) :: stateVecNew(:) ! new state vector + real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector + real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector + real(rkind),intent(out) :: fNew ! new function evaluation logical(lgt),intent(out) :: converged ! convergence flag integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------- ! local variables character(len=256) :: cmessage ! error message of downwind routine - real(rkind),parameter :: relTolerance=0.005_rkind ! force bi-section if trial is slightly larger than (smaller than) xmin (xmax) - real(rkind) :: xTolerance ! relTolerance*(xmax-xmin) - real(rkind) :: xInc(nState) ! iteration increment (re-scaled to original units of the state vector) - real(rkind) :: rVec(nState) ! residual vector (re-scaled to original units of the state equation) + real(rkind),parameter :: relTolerance=0.005_rkind ! force bi-section if trial is slightly larger than (smaller than) xmin (xmax) + real(rkind) :: xTolerance ! relTolerance*(xmax-xmin) + real(rkind) :: xInc(nState) ! iteration increment (re-scaled to original units of the state vector) + real(rkind) :: rVec(nState) ! residual vector (re-scaled to original units of the state equation) logical(lgt) :: feasible ! feasibility of the solution logical(lgt) :: doBisection ! flag to do the bi-section logical(lgt) :: bracketsDefined ! flag to define if the brackets are defined !integer(i4b) :: iCheck ! check the model state variables (not used) integer(i4b),parameter :: nCheck=100 ! number of times to check the model state variables - real(rkind),parameter :: delX=1._rkind ! trial increment - !real(rkind) :: xIncrement(nState) ! trial increment (not used) + real(rkind),parameter :: delX=1._rkind ! trial increment + !real(rkind) :: xIncrement(nState) ! trial increment (not used) ! -------------------------------------------------------------------------------------------------------- err=0; message='safeRootfinder/' @@ -759,20 +759,20 @@ end subroutine getBrackets subroutine numJacobian(stateVec,dMat,nJac,err,message) implicit none ! dummies - real(rkind),intent(in) :: stateVec(:) ! trial state vector - real(rkind),intent(in) :: dMat(:) ! diagonal matrix + real(rkind),intent(in) :: stateVec(:) ! trial state vector + real(rkind),intent(in) :: dMat(:) ! diagonal matrix ! output - real(rkind),intent(out) :: nJac(:,:) ! numerical Jacobian + real(rkind),intent(out) :: nJac(:,:) ! numerical Jacobian integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! ---------------------------------------------------------------------------------------------------------- ! local character(len=256) :: cmessage ! error message of downwind routine - real(rkind),parameter :: dx=1.e-8_rkind ! finite difference increment - real(rkind),dimension(nState) :: stateVecPerturbed ! perturbed state vector - real(rkind),dimension(nState) :: fluxVecInit,fluxVecJac ! flux vector (mized units) - real(rkind),dimension(nState) :: resVecInit,resVecJac ! qp ! residual vector (mixed units) - real(rkind) :: func ! function value + real(rkind),parameter :: dx=1.e-8_rkind ! finite difference increment + real(rkind),dimension(nState) :: stateVecPerturbed ! perturbed state vector + real(rkind),dimension(nState) :: fluxVecInit,fluxVecJac ! flux vector (mized units) + real(qp),dimension(nState) :: resVecInit,resVecJac ! qp ! residual vector (mixed units) + real(rkind) :: func ! function value logical(lgt) :: feasible ! flag to denote the feasibility of the solution integer(i4b) :: iJac ! index of row of the Jacobian matrix integer(i4b),parameter :: ixNumFlux=1001 ! named variable for the flux-based form of the numerical Jacobian @@ -911,11 +911,11 @@ subroutine eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err USE eval8summa_module,only:eval8summa ! simulation of fluxes and residuals given a trial state vector implicit none ! input - real(rkind),intent(in) :: stateVecNew(:) ! updated state vector + real(rkind),intent(in) :: stateVecNew(:) ! updated state vector ! output - real(rkind),intent(out) :: fluxVecNew(:) ! updated flux vector - real(rkind),intent(out) :: resVecNew(:) ! NOTE: qp ! updated residual vector - real(rkind),intent(out) :: fNew ! new function value + real(rkind),intent(out) :: fluxVecNew(:) ! updated flux vector + real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! updated residual vector + real(rkind),intent(out) :: fNew ! new function value logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 162e626ce..d43527f61 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -147,478 +147,452 @@ subroutine systemSolv(& tooMuchMelt, & ! intent(out): flag to denote that there was too much melt niter, & ! intent(out): number of iterations taken err,message) ! intent(out): error code and error message - ! --------------------------------------------------------------------------------------- - ! structure allocations - USE allocspace_module,only:allocLocal ! allocate local data structures - ! simulation of fluxes and residuals given a trial state vector - USE eval8summa_module,only:eval8summa ! simulation of fluxes and residuals given a trial state vector - USE summaSolve_module,only:summaSolve ! calculate the iteration increment, evaluate the new state, and refine if necessary - USE getVectorz_module,only:getScaling ! get the scaling vectors - USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy - USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy - implicit none - ! --------------------------------------------------------------------------------------- - ! * dummy variables - ! --------------------------------------------------------------------------------------- - ! input: model control - real(rkind),intent(in) :: dt_cur ! current stepsize - real(rkind),intent(in) :: dt ! entire time step for drainage pond rate - integer(i4b),intent(in) :: nState ! total number of state variables - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call - logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - ! input/output: data structures - type(zLookup),intent(in) :: lookup_data ! lookup tables - type(var_i),intent(in) :: type_data ! type of vegetation and soil - type(var_d),intent(in) :: attr_data ! spatial attributes - type(var_d),intent(in) :: forc_data ! model forcing data - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU - type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_temp ! model fluxes for a local HRU - type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin - type(model_options),intent(in) :: model_decisions(:) ! model decisions - real(rkind),intent(in) :: stateVecInit(:) ! initial state vector (mixed units) - ! output: model control - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) - real(rkind),intent(out) :: stateVecTrial(:) ! trial state vector (mixed units) - logical(lgt),intent(out) :: reduceCoupledStep ! flag to reduce the length of the coupled step - logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that there was too much melt - integer(i4b),intent(out) :: niter ! number of iterations taken - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - ! --------------------------------------------------------------------------------------- - ! * general local variables - ! --------------------------------------------------------------------------------------- - character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b) :: iter ! iteration index - integer(i4b) :: iVar ! index of variable - integer(i4b) :: iLayer ! index of layer in the snow+soil domain - integer(i4b) :: iState ! index of model state - integer(i4b) :: nLeadDim ! length of the leading dimension of the Jacobian matrix (nBands or nState) - integer(i4b) :: local_ixGroundwater ! local index for groundwater representation - real(rkind) :: bulkDensity ! bulk density of a given layer (kg m-3) - real(rkind) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) - real(rkind),parameter :: tempAccelerate=0.00_rkind ! factor to force initial canopy temperatures to be close to air temperature - real(rkind),parameter :: xMinCanopyWater=0.0001_rkind ! minimum value to initialize canopy water (kg m-2) - real(rkind),parameter :: tinyStep=0.000001_rkind ! stupidly small time step (s) - ! ------------------------------------------------------------------------------------------------------ - ! * model solver - ! ------------------------------------------------------------------------------------------------------ - logical(lgt),parameter :: forceFullMatrix=.false. ! flag to force the use of the full Jacobian matrix - integer(i4b) :: maxiter ! maximum number of iterations - integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) - integer(i4b) :: localMaxIter ! maximum number of iterations (depends on solution type) - integer(i4b), parameter :: scalarMaxIter=100 ! maximum number of iterations for the scalar solution - type(var_dlength) :: flux_init ! model fluxes at the start of the time step - real(rkind),allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! NOTE: allocatable, since not always needed - real(rkind) :: stateVecNew(nState) ! new state vector (mixed units) - real(rkind) :: fluxVec0(nState) ! flux vector (mixed units) - real(rkind) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) - real(rkind) :: xScale(nState) ! characteristic scale of the state vector (mixed units) - real(rkind) :: dMat(nState) ! diagonal matrix (excludes flux derivatives) - real(rkind) :: sMul(nState) ! NOTE: qp ! multiplier for state vector for the residual calculations - real(rkind) :: rVec(nState) ! NOTE: qp ! residual vector - real(rkind) :: rAdd(nState) ! additional terms in the residual vector - real(rkind) :: fOld,fNew ! function values (-); NOTE: dimensionless because scaled - real(rkind) :: xMin,xMax ! state minimum and maximum (mixed units) - logical(lgt) :: converged ! convergence flag - logical(lgt) :: feasible ! feasibility flag - real(rkind) :: resSinkNew(nState) ! additional terms in the residual vector - real(rkind) :: fluxVecNew(nState) ! new flux vector - real(rkind) :: resVecNew(nState) ! NOTE: qp ! new residual vector - ! enthalpy derivatives - real(rkind) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature - real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state - real(rkind) :: dEnthalpy_dTk(nState) ! derivatives in layer enthalpy w.r.t. temperature - real(rkind) :: dEnthalpy_dWat(nState) ! derivatives in layer enthalpy w.r.t. water state - ! --------------------------------------------------------------------------------------- - ! point to variables in the data structures - ! --------------------------------------------------------------------------------------- - globalVars: associate(& - ! model decisions - ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy - ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization - ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) - ! enthalpy - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) - ! derivatives, diagnositic for enthalpy - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) - ! model state variables - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) - ! check the need to merge snow layers - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ! accelerate solution for temperature - airtemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ! vector of energy and hydrology indices for the snow and soil domains - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the soil domain - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology state variables in the snow+soil domain - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology state variables in the soil domain - ! mapping from full domain to the sub-domain - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b] mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b] index of control volume for different domains (veg, snow, soil) - ! type of state and domain for a given variable - ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] type of desired model state variables - ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] domain for desired model state variables - ! layer geometry - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in): [i4b] total number of layers - ) - ! --------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="systemSolv/" - - ! ***** - ! (0) PRELIMINARIES... - ! ******************** - - ! ----- - ! * initialize... - ! --------------- - - ! check - if(dt_cur < tinyStep)then - message=trim(message)//'dt is tiny' - err=20; return - endif - - ! initialize the flags - tooMuchMelt = .false. ! too much melt - reduceCoupledStep = .false. ! need to reduce the length of the coupled step - - ! define maximum number of iterations - maxiter = nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)) - - ! modify the groundwater representation for this single-column implementation - select case(ixSpatialGroundwater) - case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale - case(localColumn); local_ixGroundwater = ixGroundwater ! go with the specified decision - case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return - end select ! (modify the groundwater representation for this single-column implementation) - - ! allocate space for the model fluxes at the start of the time step - call allocLocal(flux_meta(:),flux_init,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! allocate space for the baseflow derivatives - ! NOTE: needs allocation because only used when baseflow sinks are active - if(ixGroundwater==qbaseTopmodel)then - allocate(dBaseflow_dMatric(nSoil,nSoil),stat=err) ! baseflow depends on total storage in the soil column, hence on matric head in every soil layer - else - allocate(dBaseflow_dMatric(0,0),stat=err) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix - end if - if(err/=0)then; err=20; message=trim(message)//'unable to allocate space for the baseflow derivatives'; return; end if - - ! identify the matrix solution method - ! (the type of matrix used to solve the linear system A.X=B) - if(local_ixGroundwater==qbaseTopmodel .or. scalarSolution .or. forceFullMatrix)then - nLeadDim=nState ! length of the leading dimension - ixMatrix=ixFullMatrix ! named variable to denote the full Jacobian matrix - else - nLeadDim=nBands ! length of the leading dimension - ixMatrix=ixBandMatrix ! named variable to denote the band-diagonal matrix - endif - - ! initialize the model fluxes (some model fluxes are not computed in the iterations) - do iVar=1,size(flux_temp%var) - flux_init%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) - end do - - ! ************************************************************************************************************************** - ! ************************************************************************************************************************** - ! ************************************************************************************************************************** - ! *** NUMERICAL SOLUTION FOR A GIVEN SUBSTEP AND SPLIT ********************************************************************* - ! ************************************************************************************************************************** - ! ************************************************************************************************************************** - ! ************************************************************************************************************************** - - ! ----- - ! * get scaling vectors... - ! ------------------------ - - ! initialize state vectors - call getScaling(& - ! input - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output - fScale, & ! intent(out): function scaling vector (mixed units) - xScale, & ! intent(out): variable scaling vector (mixed units) - sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) - dMat, & ! intent(out): diagonal of the Jacobian matrix (excludes fluxes) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - ! ----- - ! * compute the initial function evaluation... - ! -------------------------------------------- - - ! initialize the trial state vectors - stateVecTrial = stateVecInit - - ! Changing in read_icond, not here - ! need to intialize canopy water at a positive value - !if(ixVegHyd/=integerMissing)then - ! if(stateVecTrial(ixVegHyd) < xMinCanopyWater) stateVecTrial(ixVegHyd) = stateVecTrial(ixVegHyd) + xMinCanopyWater - !endif - ! try to accelerate solution for energy - !if(ixCasNrg/=integerMissing) stateVecTrial(ixCasNrg) = stateVecInit(ixCasNrg) + (airtemp - stateVecInit(ixCasNrg))*tempAccelerate - !if(ixVegNrg/=integerMissing) stateVecTrial(ixVegNrg) = stateVecInit(ixVegNrg) + (airtemp - stateVecInit(ixVegNrg))*tempAccelerate - - if(ixHowHeatCap == enthalpyFD)then - ! compute H_T at the beginning of the data step without phase change - call t2enthalpy(& - .false., & ! intent(in): logical flag to not include phase change in enthalpy + ! --------------------------------------------------------------------------------------- + ! structure allocations + USE allocspace_module,only:allocLocal ! allocate local data structures + ! simulation of fluxes and residuals given a trial state vector + USE eval8summa_module,only:eval8summa ! simulation of fluxes and residuals given a trial state vector + USE summaSolve_module,only:summaSolve ! calculate the iteration increment, evaluate the new state, and refine if necessary + USE getVectorz_module,only:getScaling ! get the scaling vectors + USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy + USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy + implicit none + ! --------------------------------------------------------------------------------------- + ! * dummy variables + ! --------------------------------------------------------------------------------------- + ! input: model control + real(rkind),intent(in) :: dt_cur ! current stepsize + real(rkind),intent(in) :: dt ! entire time step for drainage pond rate + integer(i4b),intent(in) :: nState ! total number of state variables + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call + logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + ! input/output: data structures + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_i),intent(in) :: type_data ! type of vegetation and soil + type(var_d),intent(in) :: attr_data ! spatial attributes + type(var_d),intent(in) :: forc_data ! model forcing data + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU + type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_temp ! model fluxes for a local HRU + type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin + type(model_options),intent(in) :: model_decisions(:) ! model decisions + real(rkind),intent(in) :: stateVecInit(:) ! initial state vector (mixed units) + ! output: model control + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + real(rkind),intent(out) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) + real(rkind),intent(out) :: stateVecTrial(:) ! trial state vector (mixed units) + logical(lgt),intent(out) :: reduceCoupledStep ! flag to reduce the length of the coupled step + logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that there was too much melt + integer(i4b),intent(out) :: niter ! number of iterations taken + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! --------------------------------------------------------------------------------------- + ! * general local variables + ! --------------------------------------------------------------------------------------- + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iter ! iteration index + integer(i4b) :: iVar ! index of variable + integer(i4b) :: iLayer ! index of layer in the snow+soil domain + integer(i4b) :: iState ! index of model state + integer(i4b) :: nLeadDim ! length of the leading dimension of the Jacobian matrix (nBands or nState) + integer(i4b) :: local_ixGroundwater ! local index for groundwater representation + real(rkind) :: bulkDensity ! bulk density of a given layer (kg m-3) + real(rkind) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) + real(rkind),parameter :: tinyStep=0.000001_rkind ! stupidly small time step (s) + ! ------------------------------------------------------------------------------------------------------ + ! * model solver + ! ------------------------------------------------------------------------------------------------------ + logical(lgt),parameter :: forceFullMatrix=.false. ! flag to force the use of the full Jacobian matrix + integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) + type(var_dlength) :: flux_init ! model fluxes at the start of the time step + real(rkind),allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! NOTE: allocatable, since not always needed + real(rkind) :: stateVecNew(nState) ! new state vector (mixed units) + real(rkind) :: fluxVec0(nState) ! flux vector (mixed units) + real(rkind) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) + real(rkind) :: xScale(nState) ! characteristic scale of the state vector (mixed units) + real(rkind) :: dMat(nState) ! diagonal matrix (excludes flux derivatives) + real(qp) :: sMul(nState) ! NOTE: qp ! multiplier for state vector for the residual calculations + real(qp) :: rVec(nState) ! NOTE: qp ! residual vector + real(rkind) :: rAdd(nState) ! additional terms in the residual vector + real(rkind) :: fOld,fNew ! function values (-); NOTE: dimensionless because scaled + real(rkind) :: xMin,xMax ! state minimum and maximum (mixed units) + integer(i4b) :: maxiter ! maximum number of iterations + integer(i4b) :: localMaxIter ! maximum number of iterations (depends on solution type) + integer(i4b), parameter :: scalarMaxIter=100 ! maximum number of iterations for the scalar solution + logical(lgt) :: converged ! convergence flag + logical(lgt) :: feasible ! feasibility flag + real(rkind) :: resSinkNew(nState) ! additional terms in the residual vector + real(rkind) :: fluxVecNew(nState) ! new flux vector + real(qp) :: resVecNew(nState) ! NOTE: qp ! new residual vector + ! enthalpy derivatives + real(rkind) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature + real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state + real(rkind) :: dEnthalpy_dTk(nState) ! derivatives in layer enthalpy w.r.t. temperature + real(rkind) :: dEnthalpy_dWat(nState) ! derivatives in layer enthalpy w.r.t. water state + ! --------------------------------------------------------------------------------------- + ! point to variables in the data structures + ! --------------------------------------------------------------------------------------- + globalVars: associate(& + ! model decisions + ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy + ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization + ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) + ! enthalpy + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) + ! derivatives, diagnositic for enthalpy + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + ! model state variables + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variables (snow and soil domains) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) + ! check the need to merge snow layers + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ! accelerate solution for temperature + airtemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ! vector of energy and hydrology indices for the snow and soil domains + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the soil domain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology state variables in the snow+soil domain + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology state variables in the soil domain + ! mapping from full domain to the sub-domain + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b] mapping of full state vector to the state subset + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b] index of control volume for different domains (veg, snow, soil) + ! type of state and domain for a given variable + ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] type of desired model state variables + ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] domain for desired model state variables + ! layer geometry + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in): [i4b] total number of layers + ) + ! --------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="systemSolv/" + + ! ***** + ! (0) PRELIMINARIES... + ! ******************** + + ! ----- + ! * initialize... + ! --------------- + + ! check + if(dt_cur < tinyStep)then + message=trim(message)//'dt is tiny' + err=20; return + endif + + ! initialize the flags + tooMuchMelt = .false. ! too much melt + reduceCoupledStep = .false. ! need to reduce the length of the coupled step + + ! define maximum number of iterations + maxiter = nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)) + + ! modify the groundwater representation for this single-column implementation + select case(ixSpatialGroundwater) + case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale + case(localColumn); local_ixGroundwater = ixGroundwater ! go with the specified decision + case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return + end select ! (modify the groundwater representation for this single-column implementation) + + ! allocate space for the model fluxes at the start of the time step + call allocLocal(flux_meta(:),flux_init,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! allocate space for the baseflow derivatives + ! NOTE: needs allocation because only used when baseflow sinks are active + if(ixGroundwater==qbaseTopmodel)then + allocate(dBaseflow_dMatric(nSoil,nSoil),stat=err) ! baseflow depends on total storage in the soil column, hence on matric head in every soil layer + else + allocate(dBaseflow_dMatric(0,0),stat=err) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix + end if + if(err/=0)then; err=20; message=trim(message)//'unable to allocate space for the baseflow derivatives'; return; end if + + ! identify the matrix solution method + ! (the type of matrix used to solve the linear system A.X=B) + if(local_ixGroundwater==qbaseTopmodel .or. scalarSolution .or. forceFullMatrix)then + nLeadDim=nState ! length of the leading dimension + ixMatrix=ixFullMatrix ! named variable to denote the full Jacobian matrix + else + nLeadDim=nBands ! length of the leading dimension + ixMatrix=ixBandMatrix ! named variable to denote the band-diagonal matrix + endif + + ! initialize the model fluxes (some model fluxes are not computed in the iterations) + do iVar=1,size(flux_temp%var) + flux_init%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) + end do + + ! ----- + ! * get scaling vectors... + ! ------------------------ + + ! initialize state vectors + call getScaling(& + ! input + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output + fScale, & ! intent(out): function scaling vector (mixed units) + xScale, & ! intent(out): variable scaling vector (mixed units) + sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) + dMat, & ! intent(out): diagonal of the Jacobian matrix (excludes fluxes) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + + ! ----- + ! * compute the initial function evaluation... + ! -------------------------------------------- + + ! initialize the trial state vectors + stateVecTrial = stateVecInit + + ! compute the initial function evaluation + if(ixHowHeatCap == enthalpyFD)then + ! compute H_T at the beginning of the data step without phase change + call t2enthalpy(& + .false., & ! intent(in): logical flag to not include phase change in enthalpy + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTemp, & ! intent(in): value of canopy air temperature (K) + scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) + scalarCanopyWat, & ! intent(in): value of canopy total water (kg m-2) + scalarCanopyIce, & ! intent(in): value for canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTemp, & ! intent(in): vector of layer temperature (K) + mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) + mLayerMatricHead, & ! intent(in): vector of total water matric potential (m) + mLayerVolFracIce, & ! intent(in): vector of volumetric fraction of ice (-) + ! input: pre-computed derivatives + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + ! output: enthalpy + scalarCanairEnthalpy, & ! intent(out): temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) + dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + endif + + ! compute the flux and the residual vector for a given state vector + ! NOTE 1: The derivatives computed in eval8summa are used to calculate the Jacobian matrix for the first iteration + ! NOTE 2: The Jacobian matrix together with the residual vector is used to calculate the first iteration increment + call eval8summa(& + ! input: model control + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): number of layers + nState, & ! intent(in): number of state variables in the current subset + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVecTrial, & ! intent(in): model state vector + fScale, & ! intent(in): function scaling vector + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTemp, & ! intent(in): value of canopy air temperature (K) - scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) - scalarCanopyWat, & ! intent(in): value of canopy total water (kg m-2) - scalarCanopyIce, & ! intent(in): value for canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - mLayerTemp, & ! intent(in): vector of layer temperature (K) - mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) - mLayerMatricHead, & ! intent(in): vector of total water matric potential (m) - mLayerVolFracIce, & ! intent(in): vector of volumetric fraction of ice (-) - ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) - dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature - dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state - dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature - dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - endif - - ! compute the flux and the residual vector for a given state vector - ! NOTE 1: The derivatives computed in eval8summa are used to calculate the Jacobian matrix for the first iteration - ! NOTE 2: The Jacobian matrix together with the residual vector is used to calculate the first iteration increment - call eval8summa(& - ! input: model control - dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): number of layers - nState, & ! intent(in): number of state variables in the current subset - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVecTrial, & ! intent(in): model state vector - fScale, & ! intent(in): function scaling vector - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): index data - ! input-output: data structures - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - ! output - feasible, & ! intent(out): flag to denote the feasibility of the solution - fluxVec0, & ! intent(out): flux vector - rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector - fOld, & ! intent(out): function evaluation - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif - - ! copy over the initial flux structure since some model fluxes are not computed in the iterations - do concurrent ( iVar=1:size(flux_meta) ) - flux_temp%var(iVar)%dat(:) = flux_init%var(iVar)%dat(:) - end do - - ! check the need to merge snow layers - if(nSnow>0)then - ! compute the energy required to melt the top snow layer (J m-2) - bulkDensity = mLayerVolFracIce(1)*iden_ice + mLayerVolFracLiq(1)*iden_water - volEnthalpy = temp2ethpy(mLayerTemp(1),bulkDensity,snowfrz_scale) - ! set flag and error codes for too much melt - if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_cur)then - tooMuchMelt=.true. - message=trim(message)//'net flux in the top snow layer can melt all the snow in the top layer' - err=-20; return ! negative error code to denote a warning - endif - endif - - ! ========================================================================================================================================== - ! ========================================================================================================================================== - ! ========================================================================================================================================== - ! ========================================================================================================================================== - - ! ************************** - ! *** MAIN ITERATION LOOP... - ! ************************** - - ! correct the number of iterations - localMaxIter = merge(scalarMaxIter, maxIter, scalarSolution) - - ! iterate - do iter=1,localMaxIter - - ! keep track of the number of iterations - niter = iter+1 ! +1 because xFluxResid was moved outside the iteration loop (for backwards compatibility) - - ! compute the next trial state vector - ! 1) Computes the Jacobian matrix based on derivatives from the last flux evaluation - ! 2) Computes the iteration increment based on Jacobian and residuals from the last flux evaluation - ! 3) Computes new fluxes and derivatives, new residuals, and (if necessary) refines the state vector - call summaSolve(& - ! input: model control - dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate - iter, & ! intent(in): iteration index - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - nLeadDim, & ! intent(in): length of the leading dimension of the Jacobian matrix (either nBands or nState) - nState, & ! intent(in): total number of state variables - ixMatrix, & ! intent(in): type of matrix (full or band diagonal) - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVecTrial, & ! intent(in): trial state vector - xMin,xMax, & ! intent(inout): state maximum and minimum - fScale, & ! intent(in): function scaling vector - xScale, & ! intent(in): "variable" scaling vector, i.e., for state variables - rVec, & ! intent(in): residual vector - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - dMat, & ! intent(inout): diagonal matrix (excludes flux derivatives) - fOld, & ! intent(in): old function evaluation - ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): index data - ! input-output: data structures - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_temp, & ! intent(inout): model fluxes for a local HRU (temporary structure) - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(inout): derivative in baseflow w.r.t. matric head (s-1) - ! output - stateVecNew, & ! intent(out): new state vector - fluxVecNew, & ! intent(out): new flux vector - resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equa - resVecNew, & ! intent(out): new residual vector - fNew, & ! intent(out): new function evaluation - converged, & ! intent(out): convergence flag - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - - ! update function evaluation, residual vector, and states - ! NOTE 1: The derivatives computed in summaSolve are used to calculate the Jacobian matrix at the next iteration - ! NOTE 2: The Jacobian matrix together with the residual vector is used to calculate the new iteration increment - - ! save functions and residuals - fOld = fNew - rVec = resVecNew - stateVecTrial = stateVecNew - - ! exit iteration loop if converged - if(converged) exit - - ! check convergence - if(iter==localMaxiter)then - message=trim(message)//'failed to converge' - err=-20; return - endif - !print*, 'PAUSE: iterating'; read(*,*) - - end do ! iterating - !print*, 'PAUSE: after iterations'; read(*,*) - - ! ----- - ! * update states... - ! ------------------ - - ! set untapped melt energy to zero - untappedMelt(:) = 0._rkind - - ! update temperatures (ensure new temperature is consistent with the fluxes) - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - iState = ixSnowSoilNrg(iLayer) - stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState))/real(sMul(iState), rkind) - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! update volumetric water content in the snow (ensure change in state is consistent with the fluxes) - ! NOTE: for soil water balance is constrained within the iteration loop - if(nSnowSoilHyd>0)then - do concurrent (iLayer=1:nSnow,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing water state variables in the snow domain) - iState = ixSnowSoilHyd(iLayer) - stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState)) - end do ! looping through non-missing water state variables in the soil domain - endif - - if ( allocated(dBaseflow_dMatric) ) deallocate(dBaseflow_dMatric) + model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): index data + ! input-output: data structures + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + ! output + feasible, & ! intent(out): flag to denote the feasibility of the solution + fluxVec0, & ! intent(out): flux vector + rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec, & ! intent(out): residual vector + fOld, & ! intent(out): function evaluation + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif + + ! copy over the initial flux structure since some model fluxes are not computed in the iterations + do concurrent ( iVar=1:size(flux_meta) ) + flux_temp%var(iVar)%dat(:) = flux_init%var(iVar)%dat(:) + end do + + ! check the need to merge snow layers + if(nSnow>0)then + ! compute the energy required to melt the top snow layer (J m-2) + bulkDensity = mLayerVolFracIce(1)*iden_ice + mLayerVolFracLiq(1)*iden_water + volEnthalpy = temp2ethpy(mLayerTemp(1),bulkDensity,snowfrz_scale) + ! set flag and error codes for too much melt + if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_cur)then + tooMuchMelt=.true. + message=trim(message)//'net flux in the top snow layer can melt all the snow in the top layer' + err=-20; return ! negative error code to denote a warning + endif + endif + + ! ************************** + ! * solving F(y) = 0 by Backward Euler. Here, y is the state vector + ! ************************** + + ! correct the number of iterations + localMaxIter = merge(scalarMaxIter, maxIter, scalarSolution) + + ! iterate + do iter=1,localMaxIter + + ! keep track of the number of iterations + niter = iter+1 ! +1 because xFluxResid was moved outside the iteration loop (for backwards compatibility) + + ! compute the next trial state vector + ! 1) Computes the Jacobian matrix based on derivatives from the last flux evaluation + ! 2) Computes the iteration increment based on Jacobian and residuals from the last flux evaluation + ! 3) Computes new fluxes and derivatives, new residuals, and (if necessary) refines the state vector + call summaSolve(& + ! input: model control + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate + iter, & ! intent(in): iteration index + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + nLeadDim, & ! intent(in): length of the leading dimension of the Jacobian matrix (either nBands or nState) + nState, & ! intent(in): total number of state variables + ixMatrix, & ! intent(in): type of matrix (full or band diagonal) + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVecTrial, & ! intent(in): trial state vector + xMin,xMax, & ! intent(inout): state maximum and minimum + fScale, & ! intent(in): function scaling vector + xScale, & ! intent(in): "variable" scaling vector, i.e., for state variables + rVec, & ! intent(in): residual vector + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + dMat, & ! intent(inout): diagonal matrix (excludes flux derivatives) + fOld, & ! intent(in): old function evaluation + ! input: data structures + model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): index data + ! input-output: data structures + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_temp, & ! intent(inout): model fluxes for a local HRU (temporary structure) + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(inout): derivative in baseflow w.r.t. matric head (s-1) + ! output + stateVecNew, & ! intent(out): new state vector + fluxVecNew, & ! intent(out): new flux vector + resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equa + resVecNew, & ! intent(out): new residual vector + fNew, & ! intent(out): new function evaluation + converged, & ! intent(out): convergence flag + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + + + ! update function evaluation, residual vector, and states + ! NOTE 1: The derivatives computed in summaSolve are used to calculate the Jacobian matrix at the next iteration + ! NOTE 2: The Jacobian matrix together with the residual vector is used to calculate the new iteration increment + + ! save functions and residuals + fOld = fNew + rVec = resVecNew + stateVecTrial = stateVecNew + + ! exit iteration loop if converged + if(converged) exit + + ! check convergence + if(iter==localMaxiter)then + message=trim(message)//'failed to converge' + err=-20; return + endif + + end do ! iterating + + ! ----- + ! * update states... + ! ------------------ + + ! set untapped melt energy to zero + untappedMelt(:) = 0._rkind + + ! update temperatures (ensure new temperature is consistent with the fluxes) + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + iState = ixSnowSoilNrg(iLayer) + stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState))/real(sMul(iState), rkind) + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! update volumetric water content in the snow (ensure change in state is consistent with the fluxes) + ! NOTE: for soil water balance is constrained within the iteration loop + if(nSnowSoilHyd>0)then + do concurrent (iLayer=1:nSnow,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing water state variables in the snow domain) + iState = ixSnowSoilHyd(iLayer) + stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState)) + end do ! looping through non-missing water state variables in the soil domain + endif + + ! free memory + deallocate(dBaseflow_dMatric) ! end associate statements end associate globalVars diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 index 70520e019..6a617546e 100644 --- a/build/source/engine/systemSolvSundials.f90 +++ b/build/source/engine/systemSolvSundials.f90 @@ -174,7 +174,6 @@ subroutine systemSolvSundials(& logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that there was too much melt integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message - ! --------------------------------------------------------------------------------------- ! * general local variables ! --------------------------------------------------------------------------------------- @@ -184,10 +183,7 @@ subroutine systemSolvSundials(& integer(i4b) :: local_ixGroundwater ! local index for groundwater representation real(rkind) :: bulkDensity ! bulk density of a given layer (kg m-3) real(rkind) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) - real(rkind),parameter :: tempAccelerate=0.00_rkind ! factor to force initial canopy temperatures to be close to air temperature - real(rkind),parameter :: xMinCanopyWater=0.0001_rkind ! minimum value to initialize canopy water (kg m-2) real(rkind),parameter :: tinyStep=0.000001_rkind ! stupidly small time step (s) - ! ------------------------------------------------------------------------------------------------------ ! * model solver ! ------------------------------------------------------------------------------------------------------ @@ -207,19 +203,15 @@ subroutine systemSolvSundials(& real(rkind) :: atol(nState) ! absolute telerance real(rkind) :: rtol(nState) ! relative tolerance integer(i4b) :: iLayer ! index of model layer in the snow+soil domain - real(rkind) :: xMin,xMax ! minimum and maximum values for water content - real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt_out (=dt) real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! derivative value for total water matric potential (m s-1) logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step - real(rkind) :: fOld ! function values (-); NOTE: dimensionless because scaled - ! enthalpy derivatives + ! enthalpy derivatives real(rkind) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state real(rkind) :: dEnthalpy_dTk(nState) ! derivatives in layer enthalpy w.r.t. temperature real(rkind) :: dEnthalpy_dWat(nState) ! derivatives in layer enthalpy w.r.t. water state - ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- @@ -297,7 +289,6 @@ subroutine systemSolvSundials(& tooMuchMelt = .false. ! too much melt reduceCoupledStep = .false. ! need to reduce the length of the coupled step - ! modify the groundwater representation for this single-column implementation select case(ixSpatialGroundwater) case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale @@ -356,15 +347,7 @@ subroutine systemSolvSundials(& ! initialize the trial state vectors stateVecTrial = stateVecInit - ! Changing in read_icond, not here - ! need to intialize canopy water at a positive value - !if(ixVegHyd/=integerMissing)then - ! if(stateVecTrial(ixVegHyd) < xMinCanopyWater) stateVecTrial(ixVegHyd) = stateVecTrial(ixVegHyd) + xMinCanopyWater - !endif - ! try to accelerate solution for energy - !if(ixCasNrg/=integerMissing) stateVecTrial(ixCasNrg) = stateVecInit(ixCasNrg) + (airtemp - stateVecInit(ixCasNrg))*tempAccelerate - !if(ixVegNrg/=integerMissing) stateVecTrial(ixVegNrg) = stateVecInit(ixVegNrg) + (airtemp - stateVecInit(ixVegNrg))*tempAccelerate - + ! compute the initial function evaluation if(ixHowHeatCap == enthalpyFD)then ! compute H_T at the beginning of the data step without phase change call t2enthalpy(& @@ -510,9 +493,9 @@ subroutine systemSolvSundials(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - !------------------- + ! ************************** ! * solving F(y,y') = 0 by IDA. Here, y is the state vector - ! ------------------ + ! ************************** ! initialize flux_sum do concurrent ( iVar=1:size(flux_meta) ) @@ -571,6 +554,10 @@ subroutine systemSolvSundials(& if (tooMuchMelt) return !exit to start same step over after merge endif + ! ----- + ! * update states... + ! ------------------ + ! compute average flux do iVar=1,size(flux_meta) flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / dt_out @@ -584,8 +571,8 @@ subroutine systemSolvSundials(& stateVecTrial = stateVecNew ! free memory - deallocate( mLayerCmpress_sum) - deallocate( mLayerMatricHeadPrime) + deallocate(mLayerCmpress_sum) + deallocate(mLayerMatricHeadPrime) deallocate(dBaseflow_dMatric) ! end associate statements diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 index 3795c4caf..f6d121280 100644 --- a/build/source/engine/updateVars.f90 +++ b/build/source/engine/updateVars.f90 @@ -103,7 +103,6 @@ module updateVars_module subroutine updateVars(& ! input do_adjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - lookup_data, & ! intent(in): lookup tables for a local HRU mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU @@ -127,13 +126,12 @@ subroutine updateVars(& ! -------------------------------------------------------------------------------------------------------------------------------- implicit none ! input - logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze - type(zLookup), intent(in) :: lookup_data ! lookup tables for a local HRU - type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze + type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy real(rkind),intent(inout) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) real(rkind),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) @@ -147,19 +145,19 @@ subroutine updateVars(& real(rkind),intent(inout) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) real(rkind),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- ! general local variables - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixDomainType ! name of a given model domain - integer(i4b) :: ixControlIndex ! index within a given model domain - integer(i4b) :: ixOther,ixOtherLocal ! index of the coupled state variable within the (full, local) vector - logical(lgt) :: isCoupled ! .true. if a given variable shared another state variable in the same control volume - logical(lgt) :: isNrgState ! .true. if a given variable is an energy state - logical(lgt),allocatable :: computedCoupling(:) ! .true. if computed the coupling for a given state variable + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixDomainType ! name of a given model domain + integer(i4b) :: ixControlIndex ! index within a given model domain + integer(i4b) :: ixOther,ixOtherLocal ! index of the coupled state variable within the (full, local) vector + logical(lgt) :: isCoupled ! .true. if a given variable shared another state variable in the same control volume + logical(lgt) :: isNrgState ! .true. if a given variable is an energy state + logical(lgt),allocatable :: computedCoupling(:) ! .true. if computed the coupling for a given state variable real(rkind) :: scalarVolFracLiq ! volumetric fraction of liquid water (-) real(rkind) :: scalarVolFracIce ! volumetric fraction of ice (-) real(rkind) :: Tcrit ! critical soil temperature below which ice exists (K) @@ -167,22 +165,22 @@ subroutine updateVars(& real(rkind) :: fLiq ! fraction of liquid water (-) real(rkind) :: effSat ! effective saturation (-) real(rkind) :: avPore ! available pore space (-) - character(len=256) :: cMessage ! error message of downwind routine - logical(lgt),parameter :: printFlag=.false. ! flag to turn on printing + character(len=256) :: cMessage ! error message of downwind routine + logical(lgt),parameter :: printFlag=.false. ! flag to turn on printing ! iterative solution for temperature real(rkind) :: meltNrg ! energy for melt+freeze (J m-3) real(rkind) :: residual ! residual in the energy equation (J m-3) real(rkind) :: derivative ! derivative in the energy equation (J m-3 K-1) real(rkind) :: tempInc ! iteration increment (K) - integer(i4b) :: iter ! iteration index - integer(i4b) :: niter ! number of iterations - integer(i4b),parameter :: maxiter=100 ! maximum number of iterations + integer(i4b) :: iter ! iteration index + integer(i4b) :: niter ! number of iterations + integer(i4b),parameter :: maxiter=100 ! maximum number of iterations real(rkind),parameter :: nrgConvTol=1.e-4_rkind ! convergence tolerance for energy (J m-3) real(rkind),parameter :: tempConvTol=1.e-6_rkind ! convergence tolerance for temperature (K) real(rkind) :: critDiff ! temperature difference from critical (K) real(rkind) :: tempMin ! minimum bracket for temperature (K) real(rkind) :: tempMax ! maximum bracket for temperature (K) - logical(lgt) :: bFlag ! flag to denote that iteration increment was constrained using bi-section + logical(lgt) :: bFlag ! flag to denote that iteration increment was constrained using bi-section real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical temperature (K) ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures @@ -223,19 +221,11 @@ subroutine updateVars(& ! model diagnostic variables (fraction of liquid water) scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) - ! model states for the vegetation canopy - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) + ! model states from a previous solution scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variable vectors for the snow-soil layers mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] total water matric potential (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) ! model diagnostic variables from a previous solution - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) ! derivatives dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential @@ -406,7 +396,7 @@ subroutine updateVars(& ! --> partially frozen: dependence of liquid water on temperature if(xTemp diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) - ! model states for the vegetation canopy - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) + ! model states from a previous solution scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variable vectors for the snow-soil layers mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] total water matric potential (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) ! model diagnostic variables from a previous solution - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) ! derivatives dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential @@ -339,18 +331,9 @@ subroutine updateVarsSundials(& print*, 'isNrgState = ', isNrgState endif - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! update hydrology state variables for the uncoupled solution if(.not.isNrgState .and. .not.isCoupled)then - if(.not.computJac) stop 1 ! this does not work yet? FIX - ! update the total water from volumetric liquid water if(ixStateType(ixFullVector)==iname_liqCanopy .or. ixStateType(ixFullVector)==iname_liqLayer)then select case(ixDomainType) @@ -596,8 +579,8 @@ subroutine updateVarsSundials(& ! ------------------------ ! check the need to adjust temperature (will always be false if inside solver) - ! can be true if inside varSubstep, outside solver, but currently will not work so turn off always and not sure should ever do this - if(do_adjustTemp .and. computJac)then + ! can be true if inside varSubstep, outside solver, if in a splitting case + if(do_adjustTemp)then ! get the melt energy meltNrg = merge(LH_fus*iden_ice, LH_fus*iden_water, ixDomainType==iname_snow) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 25c8b9ecc..ac24a3a6b 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -865,7 +865,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! update diagnostic variables call updateVarsSundials(& ! input - .false., & ! intent(in): logical flag if computing Jacobian for Sundials solver + .false., & ! intent(in): logical flag if computing for Jacobian update doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers @@ -900,11 +900,10 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe err=20; message=trim(message)//'cannot use num_method as ida if did not compile with -DCMAKE_BUILD_TYPE=Sundials'; return #endif case(be_numrec) - ! update diagnostic variables + ! update diagnostic variables call updateVars(& ! input doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - lookup_data, & ! intent(in): lookup tables for a local HRU mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU From 3278929278d114c7a536c8d4b673137d5c55ac5b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 7 Jun 2023 18:05:48 +0900 Subject: [PATCH 0742/1472] name changes to reflect what routines do and set up for Kinsol option plus merge of systemSolv.f90. options are ida, numrec, kinsol --- build/cmake/CMakeLists.txt | 17 +- build/makefiles/Actors/makefile_apptainer | 18 +- build/makefiles/Actors/makefile_cluster | 2 +- build/makefiles/BE/makefile_old_summa | 2 +- build/makefiles/Ngen/makefile_cluster | 17 +- build/makefiles/Ngen/makefile_mac | 17 +- build/makefiles/Sundials/makefile_cluster | 19 +- build/makefiles/Sundials/makefile_mac | 19 +- build/source/dshare/get_ixname.f90 | 30 +- build/source/dshare/popMetadat.f90 | 30 +- build/source/dshare/var_lookup.f90 | 6 +- ...bSundials.f90 => computJacobWithPrime.f90} | 24 +- ...dSundials.f90 => computResidWithPrime.f90} | 14 +- build/source/engine/coupled_em.f90 | 8 +- ...maSundials.f90 => eval8summaWithPrime.f90} | 26 +- build/source/engine/mDecisions.f90 | 14 +- build/source/engine/opSplittin.f90 | 8 +- ...AddSundials.f90 => soil_utilsAddPrime.f90} | 4 +- ...lveSundials4ida.f90 => summaSolve4ida.f90} | 58 +- .../{summaSolve.f90 => summaSolve4numrec.f90} | 14 +- build/source/engine/systemSolv.f90 | 797 +++++++++++------- build/source/engine/systemSolvSundials.f90 | 583 ------------- ...teSundials.f90 => updatStateWithPrime.f90} | 4 +- ...rsSundials.f90 => updateVarsWithPrime.f90} | 24 +- build/source/engine/varSubstep.f90 | 76 +- docs/sundials_bmi/flags_params_sundials.txt | 12 +- .../celia1990/common/summa_zDecisions.txt | 6 +- 27 files changed, 701 insertions(+), 1148 deletions(-) rename build/source/engine/{computJacobSundials.f90 => computJacobWithPrime.f90} (99%) rename build/source/engine/{computResidSundials.f90 => computResidWithPrime.f90} (98%) rename build/source/engine/{eval8summaSundials.f90 => eval8summaWithPrime.f90} (99%) rename build/source/engine/{soil_utilsAddSundials.f90 => soil_utilsAddPrime.f90} (99%) rename build/source/engine/{summaSolveSundials4ida.f90 => summaSolve4ida.f90} (96%) rename build/source/engine/{summaSolve.f90 => summaSolve4numrec.f90} (99%) delete mode 100644 build/source/engine/systemSolvSundials.f90 rename build/source/engine/{updatStateSundials.f90 => updatStateWithPrime.f90} (99%) rename build/source/engine/{updateVarsSundials.f90 => updateVarsWithPrime.f90} (98%) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 83262a285..f220c3ad2 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -292,8 +292,8 @@ set(UTILMS ${ENGINE_DIR}/time_utils.f90 ${ENGINE_DIR}/updatState.f90) set(UTILMS_SUNDIALS - ${ENGINE_DIR}/soil_utilsAddSundials.f90 - ${ENGINE_DIR}/updatStateSundials.f90) + ${ENGINE_DIR}/soil_utilsAddPrime.f90 + ${ENGINE_DIR}/updatStateWithPrime.f90) # NetCDF routines set(NETCDF @@ -341,7 +341,7 @@ set(MODRUN_NOT_ACTORS ${ENGINE_DIR}/read_force.f90) set(MODRUN_SUNDIALS ${ENGINE_DIR}/tol4ida.f90 - ${ENGINE_DIR}/updateVarsSundials.f90) + ${ENGINE_DIR}/updateVarsWithPrime.f90) # Solver main modules set(SOLVER @@ -362,7 +362,7 @@ set(SOLVER ${ENGINE_DIR}/soilLiqFlx.f90 ${ENGINE_DIR}/ssdNrgFlux.f90 ${ENGINE_DIR}/stomResist.f90 - ${ENGINE_DIR}/summaSolve.f90 + ${ENGINE_DIR}/summaSolve4numrec.f90 ${ENGINE_DIR}/systemSolv.f90 ${ENGINE_DIR}/varSubstep.f90 ${ENGINE_DIR}/vegLiqFlux.f90 @@ -373,11 +373,10 @@ set(SOLVER_NOT_ACTORS ${ENGINE_DIR}/run_oneGRU.f90 ${ENGINE_DIR}/run_oneHRU.f90) set(SOLVER_SUNDIALS - ${ENGINE_DIR}/computJacobSundials.f90 - ${ENGINE_DIR}/computResidSundials.f90 - ${ENGINE_DIR}/eval8summaSundials.f90 - ${ENGINE_DIR}/summaSolveSundials4ida.f90 - ${ENGINE_DIR}/systemSolvSundials.f90) + ${ENGINE_DIR}/computJacobWithPrime.f90 + ${ENGINE_DIR}/computResidWithPrime.f90 + ${ENGINE_DIR}/eval8summaWithPrime.f90 + ${ENGINE_DIR}/summaSolve4ida.f90 # Driver support modules set(DRIVER diff --git a/build/makefiles/Actors/makefile_apptainer b/build/makefiles/Actors/makefile_apptainer index fe701e0c3..d9dff9069 100644 --- a/build/makefiles/Actors/makefile_apptainer +++ b/build/makefiles/Actors/makefile_apptainer @@ -104,9 +104,9 @@ SUMMA_UTILMS= \ mDecisions.f90 \ snow_utils.f90 \ soil_utils.f90 \ - sundials/soil_utilsAddSundials.f90 \ + sundials/soil_utilsAddPrime.f90 \ updatState.f90 \ - sundials/updatStateSundials.f90 \ + sundials/updatStateWithPrime.f90 \ matrixOper.f90 UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) @@ -131,21 +131,19 @@ SUMMA_SOLVER= \ computResid.f90 \ computJacob.f90 \ eval8summa.f90 \ - summaSolve.f90 \ + summaSolve4numrec.f90 \ systemSolv.f90 \ sundials/type4ida.f90 \ sundials/tol4ida.f90 \ sundials/computEnthalpy.f90 \ sundials/computHeatCap.f90 \ sundials/computThermConduct.f90 \ - sundials/computResidSundials.f90 \ - sundials/eval8summaSundials.f90 \ - sundials/computJacobSundials.f90 \ + sundials/computResidWithPrime.f90 \ + sundials/eval8summaWithPrime.f90 \ + sundials/computJacobWithPrime.f90 \ sundials/computSnowDepth.f90 \ - sundials/summaSolveSundials4ida.f90 \ - sundials/systemSolvSundials.f90 \ + sundials/summaSolve4ida.f90 \ varSubstep.f90 \ - sundials/varSubstepSundials.f90 \ opSplittin.f90 \ coupled_em.f90 @@ -221,7 +219,7 @@ SUMMA_MODRUN = \ getVectorz.f90 \ sundials/t2enthalpy.f90 \ updateVars.f90 \ - sundials/updateVarsSundials.f90 \ + sundials/updateVarsWithPrime.f90 \ var_derive.f90 \ derivforce.f90 \ snowAlbedo.f90 \ diff --git a/build/makefiles/Actors/makefile_cluster b/build/makefiles/Actors/makefile_cluster index c3c899e59..8ce4062cf 100644 --- a/build/makefiles/Actors/makefile_cluster +++ b/build/makefiles/Actors/makefile_cluster @@ -138,7 +138,7 @@ SUMMA_SOLVER= \ computResid.f90 \ computJacob.f90 \ eval8summa.f90 \ - summaSolve.f90 \ + summaSolve4numrec.f90 \ systemSolv.f90 \ varSubstep.f90 \ opSplittin.f90 diff --git a/build/makefiles/BE/makefile_old_summa b/build/makefiles/BE/makefile_old_summa index 46c912fb7..c426eb001 100644 --- a/build/makefiles/BE/makefile_old_summa +++ b/build/makefiles/BE/makefile_old_summa @@ -139,7 +139,7 @@ SUMMA_SOLVER= \ computResid.f90 \ computJacob.f90 \ eval8summa.f90 \ - summaSolve.f90 \ + summaSolve4numrec.f90 \ systemSolv.f90 \ varSubstep.f90 \ opSplittin.f90 diff --git a/build/makefiles/Ngen/makefile_cluster b/build/makefiles/Ngen/makefile_cluster index c5b2da265..fd8116566 100644 --- a/build/makefiles/Ngen/makefile_cluster +++ b/build/makefiles/Ngen/makefile_cluster @@ -96,9 +96,9 @@ SUMMA_UTILMS= \ mDecisions.f90 \ snow_utils.f90 \ soil_utils.f90 \ - soil_utilsAddSundials.f90 \ + soil_utilsAddPrime.f90 \ updatState.f90 \ - updatStateSundials.f90 \ + updatStateWithPrime.f90 \ matrixOper.f90 UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) @@ -128,14 +128,13 @@ SUMMA_SOLVER= \ computResid.f90 \ computJacob.f90 \ eval8summa.f90 \ - summaSolve.f90 \ + summaSolve4numrec.f90 \ systemSolv.f90 \ - computResidSundials.f90 \ - eval8summaSundials.f90 \ - computJacobSundials.f90 \ + computResidWithPrime.f90 \ + eval8summaWithPrime.f90 \ + computJacobWithPrime.f90 \ computSnowDepth.f90 \ - summaSolveSundials4ida.f90 \ - systemSolvSundials.f90 \ + summaSolve4ida.f90 \ varSubstep.f90 \ opSplittin.f90 \ coupled_em.f90 \ @@ -173,7 +172,7 @@ SUMMA_MODRUN = \ getVectorz.f90 \ t2enthalpy.f90 \ updateVars.f90 \ - updateVarsSundials.f90 \ + updateVarsWithPrime.f90 \ var_derive.f90 \ read_force.f90 \ derivforce.f90 \ diff --git a/build/makefiles/Ngen/makefile_mac b/build/makefiles/Ngen/makefile_mac index 31789c9fd..7b3d88730 100644 --- a/build/makefiles/Ngen/makefile_mac +++ b/build/makefiles/Ngen/makefile_mac @@ -96,9 +96,9 @@ SUMMA_UTILMS= \ mDecisions.f90 \ snow_utils.f90 \ soil_utils.f90 \ - soil_utilsAddSundials.f90 \ + soil_utilsAddPrime.f90 \ updatState.f90 \ - updatStateSundials.f90 \ + updatStateWithPrime.f90 \ matrixOper.f90 UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) @@ -128,14 +128,13 @@ SUMMA_SOLVER= \ computResid.f90 \ computJacob.f90 \ eval8summa.f90 \ - summaSolve.f90 \ + summaSolve4numrec.f90 \ systemSolv.f90 \ - computResidSundials.f90 \ - eval8summaSundials.f90 \ - computJacobSundials.f90 \ + computResidWithPrime.f90 \ + eval8summaWithPrime.f90 \ + computJacobWithPrime.f90 \ computSnowDepth.f90 \ - summaSolveSundials4ida.f90 \ - systemSolvSundials.f90 \ + summaSolve4ida.f90 \ varSubstep.f90 \ opSplittin.f90 \ coupled_em.f90 \ @@ -173,7 +172,7 @@ SUMMA_MODRUN = \ getVectorz.f90 \ t2enthalpy.f90 \ updateVars.f90 \ - updateVarsSundials.f90 \ + updateVarsWithPrime.f90 \ var_derive.f90 \ read_force.f90 \ derivforce.f90 \ diff --git a/build/makefiles/Sundials/makefile_cluster b/build/makefiles/Sundials/makefile_cluster index bc3d0238d..a074f0b6d 100644 --- a/build/makefiles/Sundials/makefile_cluster +++ b/build/makefiles/Sundials/makefile_cluster @@ -92,9 +92,9 @@ SUMMA_UTILMS= \ mDecisions.f90 \ snow_utils.f90 \ soil_utils.f90 \ - soil_utilsAddSundials.f90 \ + soil_utilsAddPrime.f90 \ updatState.f90 \ - updatStateSundials.f90 \ + updatStateWithPrime.f90 \ matrixOper.f90 UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) @@ -124,15 +124,14 @@ SUMMA_SOLVER= \ computResid.f90 \ computJacob.f90 \ eval8summa.f90 \ - summaSolve.f90 \ + summaSolve4numrec.f90 \ systemSolv.f90 \ - computResidSundials.f90 \ - eval8summaSundials.f90 \ - computJacobSundials.f90 \ + computResidWithPrime.f90 \ + eval8summaWithPrime.f90 \ + computJacobWithPrime.f90 \ computSnowDepth.f90 \ - summaSolveSundials4ida.f90 \ - systemSolvSundials.f90 \ - varSubstep.f90 \ + summaSolve4ida.f90 \ + varSubstep.f90 \ opSplittin.f90 \ coupled_em.f90 \ run_oneHRU.f90 \ @@ -169,7 +168,7 @@ SUMMA_MODRUN = \ getVectorz.f90 \ t2enthalpy.f90 \ updateVars.f90 \ - updateVarsSundials.f90 \ + updateVarsWithPrime.f90 \ var_derive.f90 \ read_force.f90 \ derivforce.f90 \ diff --git a/build/makefiles/Sundials/makefile_mac b/build/makefiles/Sundials/makefile_mac index 51cd8c416..f72cfc68c 100644 --- a/build/makefiles/Sundials/makefile_mac +++ b/build/makefiles/Sundials/makefile_mac @@ -92,9 +92,9 @@ SUMMA_UTILMS= \ mDecisions.f90 \ snow_utils.f90 \ soil_utils.f90 \ - soil_utilsAddSundials.f90 \ + soil_utilsAddPrime.f90 \ updatState.f90 \ - updatStateSundials.f90 \ + updatStateWithPrime.f90 \ matrixOper.f90 UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) @@ -124,15 +124,14 @@ SUMMA_SOLVER= \ computResid.f90 \ computJacob.f90 \ eval8summa.f90 \ - summaSolve.f90 \ + summaSolve4numrec.f90 \ systemSolv.f90 \ - computResidSundials.f90 \ - eval8summaSundials.f90 \ - computJacobSundials.f90 \ + computResidWithPrime.f90 \ + eval8summaWithPrime.f90 \ + computJacobWithPrime.f90 \ computSnowDepth.f90 \ - summaSolveSundials4ida.f90 \ - systemSolvSundials.f90 \ - varSubstep.f90 \ + summaSolve4ida.f90 \ + varSubstep.f90 \ opSplittin.f90 \ coupled_em.f90 \ run_oneHRU.f90 \ @@ -169,7 +168,7 @@ SUMMA_MODRUN = \ getVectorz.f90 \ t2enthalpy.f90 \ updateVars.f90 \ - updateVarsSundials.f90 \ + updateVarsWithPrime.f90 \ var_derive.f90 \ read_force.f90 \ derivforce.f90 \ diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index bcc0721a8..feef3dfdb 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -377,21 +377,21 @@ function get_ixparam(varName) case('soilIceCV' ); get_ixparam = iLookPARAM%soilIceCV ! CV of depth of soil ice, used to get frozen fraction (-) ! algorithmic control parameters case('minwind' ); get_ixparam = iLookPARAM%minwind ! minimum wind speed (m s-1) - case('minstep' ); get_ixparam = iLookPARAM%minstep ! minimum length of the time step be_numrec, not currently used - case('maxstep' ); get_ixparam = iLookPARAM%maxstep ! maximum length of the time step be_numrec - case('be_steps' ); get_ixparam = iLookPARAM%be_steps ! minimum number of substeps to take in a maxstep be_numrec - case('wimplicit' ); get_ixparam = iLookPARAM%wimplicit ! weight assigned to start-of-step fluxes be_numrec, not currently used - case('maxiter' ); get_ixparam = iLookPARAM%maxiter ! maximum number of iterations be_numrec or nonlinear iterations Sundials - case('relConvTol_liquid' ); get_ixparam = iLookPARAM%relConvTol_liquid ! relative convergence tolerance for vol frac liq water (-) be_numrec - case('absConvTol_liquid' ); get_ixparam = iLookPARAM%absConvTol_liquid ! absolute convergence tolerance for vol frac liq water (-) be_numrec - case('relConvTol_matric' ); get_ixparam = iLookPARAM%relConvTol_matric ! relative convergence tolerance for matric head (-) be_numrec - case('absConvTol_matric' ); get_ixparam = iLookPARAM%absConvTol_matric ! absolute convergence tolerance for matric head (m) be_numrec - case('relConvTol_energy' ); get_ixparam = iLookPARAM%relConvTol_energy ! relative convergence tolerance for energy (-) be_numrec - case('absConvTol_energy' ); get_ixparam = iLookPARAM%absConvTol_energy ! absolute convergence tolerance for energy (J m-3) be_numrec - case('relConvTol_aquifr' ); get_ixparam = iLookPARAM%relConvTol_aquifr ! relative convergence tolerance for aquifer storage (-) be_numrec - case('absConvTol_aquifr' ); get_ixparam = iLookPARAM%absConvTol_aquifr ! absolute convergence tolerance for aquifer storage (m) be_numrec - case('relErrTol_ida' ); get_ixparam = iLookPARAM%relErrTol_ida ! relative error tolerance for sundials ida - case('absErrTol_ida' ); get_ixparam = iLookPARAM%absErrTol_ida ! absolute error tolerance for sundials ida + case('minstep' ); get_ixparam = iLookPARAM%minstep ! minimum length of the time step numrec, not currently used + case('maxstep' ); get_ixparam = iLookPARAM%maxstep ! maximum length of the time step numrec + case('be_steps' ); get_ixparam = iLookPARAM%be_steps ! minimum number of substeps to take in a maxstep numrec + case('wimplicit' ); get_ixparam = iLookPARAM%wimplicit ! weight assigned to start-of-step fluxes numrec, not currently used + case('maxiter' ); get_ixparam = iLookPARAM%maxiter ! maximum number of iterations numrec or nonlinear iterations Sundials + case('relConvTol_liquid' ); get_ixparam = iLookPARAM%relConvTol_liquid ! relative convergence tolerance for vol frac liq water (-) numrec + case('absConvTol_liquid' ); get_ixparam = iLookPARAM%absConvTol_liquid ! absolute convergence tolerance for vol frac liq water (-) numrec + case('relConvTol_matric' ); get_ixparam = iLookPARAM%relConvTol_matric ! relative convergence tolerance for matric head (-) numrec + case('absConvTol_matric' ); get_ixparam = iLookPARAM%absConvTol_matric ! absolute convergence tolerance for matric head (m) numrec + case('relConvTol_energy' ); get_ixparam = iLookPARAM%relConvTol_energy ! relative convergence tolerance for energy (-) numrec + case('absConvTol_energy' ); get_ixparam = iLookPARAM%absConvTol_energy ! absolute convergence tolerance for energy (J m-3) numrec + case('relConvTol_aquifr' ); get_ixparam = iLookPARAM%relConvTol_aquifr ! relative convergence tolerance for aquifer storage (-) numrec + case('absConvTol_aquifr' ); get_ixparam = iLookPARAM%absConvTol_aquifr ! absolute convergence tolerance for aquifer storage (m) numrec + case('relErrTol_ida' ); get_ixparam = iLookPARAM%relErrTol_ida ! relative error tolerance for ida + case('absErrTol_ida' ); get_ixparam = iLookPARAM%absErrTol_ida ! absolute error tolerance for ida case('zmin' ); get_ixparam = iLookPARAM%zmin ! minimum layer depth (m) case('zmax' ); get_ixparam = iLookPARAM%zmax ! maximum layer depth (m) case('zminLayer1' ); get_ixparam = iLookPARAM%zminLayer1 ! minimum layer depth for the 1st (top) layer (m) diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index d6c6ca90f..62f19e0b5 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -259,21 +259,21 @@ subroutine popMetadat(err,message) mpar_meta(iLookPARAM%soilIceCV) = var_info('soilIceCV' , 'CV of depth of soil ice, used to get frozen fraction' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! algorithmic control parameters mpar_meta(iLookPARAM%minwind) = var_info('minwind' , 'minimum wind speed' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%minstep) = var_info('minstep' , 'minimum length of the time step be_numrec, not currently used' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%maxstep) = var_info('maxstep' , 'maximum length of the time step be_numrec' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%be_steps) = var_info('be_steps' , 'minimum number of substeps to take in a maxstep be_numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%wimplicit) = var_info('wimplicit' , 'weight assigned to the start-of-step fluxes ,be_numrec, not currently used', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%maxiter) = var_info('maxiter' , 'maximum number of iterations be_numrec or nonlinear iterations Sundials', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_liquid) = var_info('relConvTol_liquid' , 'relative convergence tolerance for vol frac liq water be_numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_liquid) = var_info('absConvTol_liquid' , 'absolute convergence tolerance for vol frac liq water be_numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_matric) = var_info('relConvTol_matric' , 'relative convergence tolerance for matric head be_numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_matric) = var_info('absConvTol_matric' , 'absolute convergence tolerance for matric head be_numrec' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_energy) = var_info('relConvTol_energy' , 'relative convergence tolerance for energy be_numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_energy) = var_info('absConvTol_energy' , 'absolute convergence tolerance for energy be_numrec' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_aquifr) = var_info('relConvTol_aquifr' , 'relative convergence tolerance for aquifer storage be_numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_aquifr) = var_info('absConvTol_aquifr' , 'absolute convergence tolerance for aquifer storage be_numrec' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relErrTol_ida) = var_info('relErrTol_ida' , 'relative error tolerance for sundials ida' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absErrTol_ida) = var_info('absErrTol_ida' , 'absolute error tolerance for sundials ida' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%minstep) = var_info('minstep' , 'minimum length of the time step numrec, not currently used' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%maxstep) = var_info('maxstep' , 'maximum length of the time step numrec' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%be_steps) = var_info('be_steps' , 'minimum number of substeps to take in a maxstep numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%wimplicit) = var_info('wimplicit' , 'weight assigned to the start-of-step fluxes ,numrec, not currently used', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%maxiter) = var_info('maxiter' , 'maximum number of iterations numrec or nonlinear iterations Sundials', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_liquid) = var_info('relConvTol_liquid' , 'relative convergence tolerance for vol frac liq water numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_liquid) = var_info('absConvTol_liquid' , 'absolute convergence tolerance for vol frac liq water numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_matric) = var_info('relConvTol_matric' , 'relative convergence tolerance for matric head numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_matric) = var_info('absConvTol_matric' , 'absolute convergence tolerance for matric head numrec' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_energy) = var_info('relConvTol_energy' , 'relative convergence tolerance for energy numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_energy) = var_info('absConvTol_energy' , 'absolute convergence tolerance for energy numrec' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_aquifr) = var_info('relConvTol_aquifr' , 'relative convergence tolerance for aquifer storage numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_aquifr) = var_info('absConvTol_aquifr' , 'absolute convergence tolerance for aquifer storage numrec' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relErrTol_ida) = var_info('relErrTol_ida' , 'relative error tolerance for ida' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absErrTol_ida) = var_info('absErrTol_ida' , 'absolute error tolerance for ida' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%zmin) = var_info('zmin' , 'minimum layer depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%zmax) = var_info('zmax' , 'maximum layer depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%zminLayer1) = var_info('zminLayer1' , 'minimum layer depth for the 1st (top) layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index b6ef359da..60e14f6de 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -283,7 +283,7 @@ MODULE var_lookup integer(i4b) :: minwind = integerMissing ! minimum wind speed (m s-1) integer(i4b) :: minstep = integerMissing ! minimum length of the time step integer(i4b) :: maxstep = integerMissing ! maximum length of the time step - integer(i4b) :: be_steps = integerMissing ! minimum number of substeps to take in a maxstep be_numrec + integer(i4b) :: be_steps = integerMissing ! minimum number of substeps to take in a maxstep numrec integer(i4b) :: wimplicit = integerMissing ! weight assigned to the start-of-step fluxes integer(i4b) :: maxiter = integerMissing ! maximum number of iteration integer(i4b) :: relConvTol_liquid = integerMissing ! relative convergence tolerance for vol frac liq water (-) @@ -294,8 +294,8 @@ MODULE var_lookup integer(i4b) :: absConvTol_energy = integerMissing ! absolute convergence tolerance for energy (J m-3) integer(i4b) :: relConvTol_aquifr = integerMissing ! relative convergence tolerance for aquifer storage (-) integer(i4b) :: absConvTol_aquifr = integerMissing ! absolute convergence tolerance for aquifer storage (J m-3) - integer(i4b) :: relErrTol_ida = integerMissing ! relative error tolerance for sundials ida' - integer(i4b) :: absErrTol_ida = integerMissing ! absolute error tolerance for sundials ida' + integer(i4b) :: relErrTol_ida = integerMissing ! relative error tolerance for ida' + integer(i4b) :: absErrTol_ida = integerMissing ! absolute error tolerance for ida' integer(i4b) :: zmin = integerMissing ! minimum layer depth (m) integer(i4b) :: zmax = integerMissing ! maximum layer depth (m) integer(i4b) :: zminLayer1 = integerMissing ! minimum layer depth for the 1st (top) layer (m) diff --git a/build/source/engine/computJacobSundials.f90 b/build/source/engine/computJacobWithPrime.f90 similarity index 99% rename from build/source/engine/computJacobSundials.f90 rename to build/source/engine/computJacobWithPrime.f90 index 8237bcfd4..a9155e55a 100644 --- a/build/source/engine/computJacobSundials.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -18,7 +18,7 @@ ! You should have received a copy of the GNU General Public License ! along with this program. If not, see . -module computJacobSundials_module +module computJacobWithPrime_module ! data types USE nrtype @@ -105,16 +105,16 @@ module computJacobSundials_module real(rkind),parameter :: verySmall=tiny(1.0_rkind) ! a very small number private -public::computJacobSundials +public::computJacobWithPrime public::computJacob4idaSetup public::computJacob4ida contains ! ********************************************************************************************************** -! public subroutine computJacobSundials: compute the Jacobian matrix +! public subroutine computJacobWithPrime: compute the Jacobian matrix ! ********************************************************************************************************** -subroutine computJacobSundials(& +subroutine computJacobWithPrime(& ! input: model control cj, & ! intent(in): this scalar changes whenever the step size or method order changes dt, & ! intent(in): length of the time step (seconds) @@ -313,7 +313,7 @@ subroutine computJacobSundials(& ) ! making association with data in structures ! -------------------------------------------------------------- ! initialize error control - err=0; message='computJacobSundials/' + err=0; message='computJacobWithPrime/' ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* @@ -1077,7 +1077,7 @@ subroutine computJacobSundials(& ! end association to variables in the data structures end associate -end subroutine computJacobSundials +end subroutine computJacobWithPrime ! ********************************************************************************************************** @@ -1113,7 +1113,7 @@ subroutine computJacob4idaSetup(& ! -------------------------------------------------------------------------------------------------------------------------------- ! provide access to subroutines USE getVectorz_module, only:varExtract ! extract variables from the state vector - USE updateVarsSundials_module, only:updateVarsSundials ! update prognostic variables + USE updateVarsWithPrime_module, only:updateVarsWithPrime ! update prognostic variables implicit none ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- @@ -1278,7 +1278,7 @@ subroutine computJacob4idaSetup(& if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! update diagnostic variables and derivatives - call updateVarsSundials(& + call updateVarsWithPrime(& ! input .true., & ! intent(in): logical flag if computing for Jacobian update .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze @@ -1319,10 +1319,10 @@ subroutine computJacob4idaSetup(& ! compute the analytical Jacobian matrix ! NOTE: The derivatives were computed in the previous call to computFlux - ! This occurred either at the call to eval8summaSundials at the start of sysSolveSundials - ! or in the call to eval8summaSundials in the previous iteration + ! This occurred either at the call to eval8summaWithPrime at the start of sysSolveSundials + ! or in the call to eval8summaWithPrime in the previous iteration dt1 = 1._qp - call computJacobSundials(& + call computJacobWithPrime(& ! input: model control cj, & ! intent(in): this scalar changes whenever the step size or method order changes dt1, & ! intent(in): length of the time step (seconds) @@ -1465,4 +1465,4 @@ function ixOffDiag(jState,iState) ixOffDiag = ixDiag + jState - iState end function ixOffDiag -end module computJacobSundials_module +end module computJacobWithPrime_module diff --git a/build/source/engine/computResidSundials.f90 b/build/source/engine/computResidWithPrime.f90 similarity index 98% rename from build/source/engine/computResidSundials.f90 rename to build/source/engine/computResidWithPrime.f90 index 31aa7f35c..ec2f4b500 100644 --- a/build/source/engine/computResidSundials.f90 +++ b/build/source/engine/computResidWithPrime.f90 @@ -1,6 +1,6 @@ -module computResidSundials_module +module computResidWithPrime_module ! data types USE nrtype @@ -50,13 +50,13 @@ module computResidSundials_module ! privacy implicit none private::printResidDAE -public::computResidSundials +public::computResidWithPrime contains ! ********************************************************************************************************** -! public subroutine computResidSundials: compute the residual vector +! public subroutine computResidWithPrime: compute the residual vector ! ********************************************************************************************************** -subroutine computResidSundials(& +subroutine computResidWithPrime(& ! input: model control dt, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers @@ -163,7 +163,7 @@ subroutine computResidSundials(& ) ! association to necessary variables for the residual computations ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="computResidSundials/" + err=0; message="computResidWithPrime/" ! --- ! * compute sink terms... @@ -248,7 +248,7 @@ subroutine computResidSundials(& ! end association with the necessary variabiles for the residual calculations end associate -end subroutine computResidSundials +end subroutine computResidWithPrime ! ********************************************************************************************************** ! private subroutine printResidDAE: print the residual vector mainly for debugging @@ -341,4 +341,4 @@ subroutine printResidDAE( & end subroutine printResidDAE -end module computResidSundials_module +end module computResidWithPrime_module diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 95913d4a9..df201c64f 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -82,8 +82,8 @@ module coupled_em_module ! look-up values for the numerical method USE mDecisions_module,only: & - be_numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes - be_kinsol ,& ! SUNDIALS backward Euler solution using Kinsol + numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes + kinsol ,& ! SUNDIALS backward Euler solution using Kinsol ida ! SUNDIALS solution using IDA ! privacy @@ -1193,8 +1193,8 @@ subroutine coupled_em(& ! identify the need to check the mass balance, both methods should work if tolerance coarse enough select case(ixNumericalMethod) case(ida); checkMassBalance = .false. ! Sundials gives instantaneous fluxes and were summed for an average flux, but if large time step, then average is not accurate enough to pass the check - case(be_numrec); checkMassBalance = .true. ! be_numrec gives finite difference dt_sub fluxes and were summed for an average flux - case default; err=20; message=trim(message)//'expect num_method to be sundials, be_kinsol, or be_numrec (or itertive, which is be_numrec)'; return + case(numrec); checkMassBalance = .true. ! numrec gives finite difference dt_sub fluxes and were summed for an average flux + case default; err=20; message=trim(message)//'expect num_method to be sundials, kinsol, or numrec (or itertive, which is numrec)'; return end select ! ----- diff --git a/build/source/engine/eval8summaSundials.f90 b/build/source/engine/eval8summaWithPrime.f90 similarity index 99% rename from build/source/engine/eval8summaSundials.f90 rename to build/source/engine/eval8summaWithPrime.f90 index 176dff31d..0f892dcde 100644 --- a/build/source/engine/eval8summaSundials.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -1,5 +1,5 @@ -module eval8summaSundials_module +module eval8summaWithPrime_module ! data types USE nrtype @@ -68,16 +68,16 @@ module eval8summaSundials_module implicit none private -public::eval8summaSundials +public::eval8summaWithPrime public::eval8summa4ida contains ! ********************************************************************************************************** -! public subroutine eval8summaSundials: compute the residual vector +! public subroutine eval8summaWithPrime: compute the residual vector ! ********************************************************************************************************** -subroutine eval8summaSundials(& +subroutine eval8summaWithPrime(& ! input: model control dt_cur, & ! intent(in): step size to last time solution dt, & ! intent(in): entire time step for drainage pond rate @@ -146,7 +146,7 @@ subroutine eval8summaSundials(& ! -------------------------------------------------------------------------------------------------------------------------------- ! provide access to subroutines USE getVectorz_module, only:varExtract ! extract variables from the state vector - USE updateVarsSundials_module, only:updateVarsSundials ! update variables + USE updateVarsWithPrime_module, only:updateVarsWithPrime ! update variables USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy USE computFlux_module, only:soilCmpresSundials ! compute soil compression USE computFlux_module, only:computFlux ! compute fluxes given a state vector @@ -154,7 +154,7 @@ subroutine eval8summaSundials(& USE computHeatCap_module,only:computHeatCapAnalytic ! recompute heat capacity and derivatives USE computHeatCap_module,only:computCm USE computHeatCap_module, only:computStatMult ! recompute state multiplier - USE computResidSundials_module,only:computResidSundials ! compute residuals given a state vector + USE computResidWithPrime_module,only:computResidWithPrime ! compute residuals given a state vector USE computThermConduct_module,only:computThermConduct ! recompute thermal conductivity and derivatives implicit none ! -------------------------------------------------------------------------------------------------------------------------------- @@ -313,7 +313,7 @@ subroutine eval8summaSundials(& ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="eval8summaSundials/" + err=0; message="eval8summaWithPrime/" feasible=.true. ! check the feasibility of the solution only if not inside Sundials solver @@ -473,7 +473,7 @@ subroutine eval8summaSundials(& if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! update diagnostic variables and derivatives - call updateVarsSundials(& + call updateVarsWithPrime(& ! input .false., & ! intent(in): logical flag if computing for Jacobian update .false., & ! intent(in): logical flag to adjust temperature to account for the energy @@ -720,7 +720,7 @@ subroutine eval8summaSundials(& deriv_data, & ! intent(out): derivatives in model fluxes w.r.t. relevant state variables ! input-output: flux vector and baseflow derivatives ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later in computJacobSundials + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1), we will use it later in computJacobWithPrime fluxVec, & ! intent(out): flux vector (mixed units) ! output: error control err,cmessage) ! intent(out): error code and error message @@ -752,7 +752,7 @@ subroutine eval8summaSundials(& if (insideIDA)then dt1 = 1._qp ! always 1 for sundials since using Prime derivatives - call computResidSundials(& + call computResidWithPrime(& ! input: model control dt1, & ! intent(in): length of the residual time step (seconds) nSnow, & ! intent(in): number of snow layers @@ -795,7 +795,7 @@ subroutine eval8summaSundials(& ! end association with the information in the data structures end associate -end subroutine eval8summaSundials +end subroutine eval8summaWithPrime ! ********************************************************************************************************** @@ -851,7 +851,7 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user end if ! compute the flux and the residual vector for a given state vector - call eval8summaSundials(& + call eval8summaWithPrime(& ! input: model control stepsize_next(1), & ! intent(in): current stepsize eqns_data%dt, & ! intent(in): data step @@ -929,4 +929,4 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user end function eval8summa4ida -end module eval8summaSundials_module +end module eval8summaWithPrime_module diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index 00a033e27..8b905b860 100755 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -60,8 +60,8 @@ module mDecisions_module integer(i4b),parameter,public :: constantScaling = 71 ! constant scaling factor integer(i4b),parameter,public :: laiScaling = 72 ! exponential function of LAI (Leuning, Plant Cell Env 1995: "Scaling from..." [eq 9]) ! look-up values for the choice of numerical method -integer(i4b),parameter,public :: be_numrec = 81 ! home-grown backward Euler solution using free versions of Numerical recipes -integer(i4b),parameter,public :: be_kinsol = 82 ! SUNDIALS backward Euler solution using Kinsol +integer(i4b),parameter,public :: numrec = 81 ! home-grown backward Euler solution using free versions of Numerical recipes +integer(i4b),parameter,public :: kinsol = 82 ! SUNDIALS backward Euler solution using Kinsol integer(i4b),parameter,public :: ida = 83 ! SUNDIALS solution using IDA ! look-up values for method used to compute derivative integer(i4b),parameter,public :: numerical = 91 ! numerical solution @@ -411,11 +411,11 @@ subroutine mDecisions(err,message) ! identify the numerical method select case(trim(model_decisions(iLookDECISIONS%num_method)%cDecision)) - case('be_numrec'); model_decisions(iLookDECISIONS%num_method)%iDecision = be_numrec ! home-grown backward Euler solution using free versions of Numerical recipes - case('itertive' ); model_decisions(iLookDECISIONS%num_method)%iDecision = be_numrec ! home-grown backward Euler solution (included for backwards compatibility) - case('be_kinsol'); model_decisions(iLookDECISIONS%num_method)%iDecision = be_kinsol ! SUNDIALS backward Euler solution using Kinsol - case('sundials' ); model_decisions(iLookDECISIONS%num_method)%iDecision = ida ! SUNDIALS solution using IDA - case('ida ' ); model_decisions(iLookDECISIONS%num_method)%iDecision = ida ! SUNDIALS solution using IDA + case('numrec' ); model_decisions(iLookDECISIONS%num_method)%iDecision = numrec ! home-grown backward Euler solution using free versions of Numerical recipes + case('itertive'); model_decisions(iLookDECISIONS%num_method)%iDecision = numrec ! home-grown backward Euler solution (included for backwards compatibility) + case('kinsol' ); model_decisions(iLookDECISIONS%num_method)%iDecision = kinsol ! SUNDIALS backward Euler solution using Kinsol + case('sundials'); model_decisions(iLookDECISIONS%num_method)%iDecision = ida ! SUNDIALS solution using IDA + case('ida' ); model_decisions(iLookDECISIONS%num_method)%iDecision = ida ! SUNDIALS solution using IDA case default err=10; message=trim(message)//"unknown numerical method [option="//trim(model_decisions(iLookDECISIONS%num_method)%cDecision)//"]"; return end select diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index cd5bba796..22cde3b44 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -99,8 +99,8 @@ module opSplittin_module ! look-up values for the numerical method USE mDecisions_module,only: & - be_numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes - be_kinsol ,& ! SUNDIALS backward Euler solution using Kinsol + numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes + kinsol ,& ! SUNDIALS backward Euler solution using Kinsol ida ! SUNDIALS solution using IDA ! safety: set private unless specified otherwise @@ -319,8 +319,8 @@ subroutine opSplittin(& ! we just solve the fully coupled problem by with Sundials for now select case(ixNumericalMethod) case(ida); nCoupling = 1 - case(be_numrec); nCoupling = 2 - case default; err=20; message=trim(message)//'expect num_method to be ida, be_kinsol, or be_numrec (or itertive, which is be_numrec)'; return + case(numrec); nCoupling = 2 + case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol , or numrec (or itertive, which is numrec)'; return end select ! ----- diff --git a/build/source/engine/soil_utilsAddSundials.f90 b/build/source/engine/soil_utilsAddPrime.f90 similarity index 99% rename from build/source/engine/soil_utilsAddSundials.f90 rename to build/source/engine/soil_utilsAddPrime.f90 index af0f4f191..d76f626a5 100644 --- a/build/source/engine/soil_utilsAddSundials.f90 +++ b/build/source/engine/soil_utilsAddPrime.f90 @@ -18,7 +18,7 @@ ! You should have received a copy of the GNU General Public License ! along with this program. If not, see . -module soil_utilsAddSundials_module +module soil_utilsAddPrime_module ! data types USE nrtype @@ -223,4 +223,4 @@ function d2Theta_dTk2(Tk,theta_res,theta_sat,alpha,n,m) + n*(-m-1)*xtemp**(2*n - 2._rkind) * (1._rkind + xtemp**n)**(-m - 2._rkind) ) end function d2Theta_dTk2 -end module soil_utilsAddSundials_module +end module soil_utilsAddPrime_module diff --git a/build/source/engine/summaSolveSundials4ida.f90 b/build/source/engine/summaSolve4ida.f90 similarity index 96% rename from build/source/engine/summaSolveSundials4ida.f90 rename to build/source/engine/summaSolve4ida.f90 index 937f67303..fc692e571 100644 --- a/build/source/engine/summaSolveSundials4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -1,7 +1,7 @@ -module summaSolveSundials4ida_module +module summaSolve4ida_module !======= Inclusions =========== @@ -80,14 +80,14 @@ module summaSolveSundials4ida_module private::setSolverParams private::find_rootdir public::layerDisCont4ida - public::summaSolveSundials4ida + public::summaSolve4ida contains !------------------- -! * public subroutine summaSolveSundials4ida: solve F(y,y') = 0 by IDA (y is the state vector) +! * public subroutine summaSolve4ida: solve F(y,y') = 0 by IDA (y is the state vector) ! ------------------ -subroutine summaSolveSundials4ida( & +subroutine summaSolve4ida( & dt, & ! intent(in): data time step atol, & ! intent(in): absolute telerance rtol, & ! intent(in): relative tolerance @@ -142,9 +142,9 @@ subroutine summaSolveSundials4ida( & USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver USE allocspace_module,only:allocLocal ! allocate local data structures - USE eval8summaSundials_module,only:eval8summa4ida ! DAE/ODE functions - USE eval8summaSundials_module,only:eval8summaSundials ! residual of DAE - USE computJacobSundials_module,only:computJacob4ida ! system Jacobian + USE eval8summaWithPrime_module,only:eval8summa4ida ! DAE/ODE functions + USE eval8summaWithPrime_module,only:eval8summaWithPrime ! residual of DAE + USE computJacobWithPrime_module,only:computJacob4ida ! system Jacobian USE tol4ida_module,only:computWeight4ida ! weight required for tolerances USE var_derive_module,only:calcHeight ! height at layer interfaces and layer mid-point @@ -233,11 +233,11 @@ subroutine summaSolveSundials4ida( & ! ----------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="summaSolveSundials4ida/" + err=0; message="summaSolve4ida/" nState = nStat idaSucceeds = .true. - ! fill eqns_data which will be required later to call eval8summaSundials + ! fill eqns_data which will be required later to call eval8summaWithPrime eqns_data%dt = dt eqns_data%nSnow = nSnow eqns_data%nSoil = nSoil @@ -320,31 +320,31 @@ subroutine summaSolveSundials4ida( & ! create serial vectors sunvec_y => FN_VMake_Serial(nState, stateVec, sunctx) - if (.not. associated(sunvec_y)) then; err=20; message='summaSolveSundials4ida: sunvec = NULL'; return; endif + if (.not. associated(sunvec_y)) then; err=20; message='summaSolve4ida: sunvec = NULL'; return; endif sunvec_yp => FN_VMake_Serial(nState, stateVecPrime, sunctx) - if (.not. associated(sunvec_yp)) then; err=20; message='summaSolveSundials4ida: sunvec = NULL'; return; endif + if (.not. associated(sunvec_yp)) then; err=20; message='summaSolve4ida: sunvec = NULL'; return; endif ! Initialize solution vectors call setInitialCondition(nState, stateVecInit, sunvec_y, sunvec_yp) ! Create memory ida_mem = FIDACreate(sunctx) - if (.not. c_associated(ida_mem)) then; err=20; message='summaSolveSundials4ida: ida_mem = NULL'; return; endif + if (.not. c_associated(ida_mem)) then; err=20; message='summaSolve4ida: ida_mem = NULL'; return; endif ! Attach user data to memory eqns_data%ida_mem = ida_mem retval = FIDASetUserData(ida_mem, c_loc(eqns_data)) - if (retval /= 0) then; err=20; message='summaSolveSundials4ida: error in FIDASetUserData'; return; endif + if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetUserData'; return; endif ! Initialize memory t0 = 0._rkind retval = FIDAInit(ida_mem, c_funloc(eval8summa4ida), t0, sunvec_y, sunvec_yp) - if (retval /= 0) then; err=20; message='summaSolveSundials4ida: error in FIDAInit'; return; endif + if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDAInit'; return; endif ! set tolerances retval = FIDAWFtolerances(ida_mem, c_funloc(computWeight4ida)) - if (retval /= 0) then; err=20; message='summaSolveSundials4ida: error in FIDAWFtolerances'; return; endif + if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDAWFtolerances'; return; endif ! initialize rootfinding problem and allocate space, counting roots if(detect_events)then @@ -378,51 +378,51 @@ subroutine summaSolveSundials4ida( & mu = ku; lu = kl; ! Create banded SUNMatrix for use in linear solves sunmat_A => FSUNBandMatrix(nState, mu, lu, sunctx) - if (.not. associated(sunmat_A)) then; err=20; message='summaSolveSundials4ida: sunmat = NULL'; return; endif + if (.not. associated(sunmat_A)) then; err=20; message='summaSolve4ida: sunmat = NULL'; return; endif ! Create banded SUNLinearSolver object sunlinsol_LS => FSUNLinSol_Band(sunvec_y, sunmat_A, sunctx) - if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolveSundials4ida: sunlinsol = NULL'; return; endif + if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolve4ida: sunlinsol = NULL'; return; endif case(ixFullMatrix) ! Create dense SUNMatrix for use in linear solves sunmat_A => FSUNDenseMatrix(nState, nState, sunctx) - if (.not. associated(sunmat_A)) then; err=20; message='summaSolveSundials4ida: sunmat = NULL'; return; endif + if (.not. associated(sunmat_A)) then; err=20; message='summaSolve4ida: sunmat = NULL'; return; endif ! Create dense SUNLinearSolver object sunlinsol_LS => FSUNLinSol_Dense(sunvec_y, sunmat_A, sunctx) - if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolveSundials4ida: sunlinsol = NULL'; return; endif + if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolve4ida: sunlinsol = NULL'; return; endif ! check - case default; err=20; message='summaSolveSundials4ida: error in type of matrix'; return + case default; err=20; message='summaSolve4ida: error in type of matrix'; return end select ! form of matrix ! Attach the matrix and linear solver retval = FIDASetLinearSolver(ida_mem, sunlinsol_LS, sunmat_A); - if (retval /= 0) then; err=20; message='summaSolveSundials4ida: error in FIDASetLinearSolver'; return; endif + if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetLinearSolver'; return; endif ! Set the user-supplied Jacobian routine if(.not.use_fdJac)then retval = FIDASetJacFn(ida_mem, c_funloc(computJacob4ida)) - if (retval /= 0) then; err=20; message='summaSolveSundials4ida: error in FIDASetJacFn'; return; endif + if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetJacFn'; return; endif endif ! Create Newton SUNNonlinearSolver object sunnonlin_NLS => FSUNNonlinSol_Newton(sunvec_y, sunctx) - if (.not. associated(sunnonlin_NLS)) then; err=20; message='summaSolveSundials4ida: sunnonlinsol = NULL'; return; endif + if (.not. associated(sunnonlin_NLS)) then; err=20; message='summaSolve4ida: sunnonlinsol = NULL'; return; endif ! Attach the nonlinear solver retval = FIDASetNonlinearSolver(ida_mem, sunnonlin_NLS) - if (retval /= 0) then; err=20; message='summaSolveSundials4ida: error in FIDASetNonlinearSolver'; return; endif + if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetNonlinearSolver'; return; endif ! Enforce the solver to stop at end of the time step retval = FIDASetStopTime(ida_mem, dt) - if (retval /= 0) then; err=20; message='summaSolveSundials4ida: error in FIDASetStopTime'; return; endif + if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetStopTime'; return; endif ! Set solver parameters such as maximum order, number of iterations, ... call setSolverParams(dt, nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)), ida_mem, retval) - if (retval /= 0) then; err=20; message='summaSolveSundials4ida: error in setSolverParams'; return; endif + if (retval /= 0) then; err=20; message='summaSolve4ida: error in setSolverParams'; return; endif ! Disable error messages and warnings if(offErrWarnMessage) then @@ -485,7 +485,7 @@ subroutine summaSolveSundials4ida( & dt_diff = tret(1) - tretPrev ! compute the flux and the residual vector for a given state vector - call eval8summaSundials(& + call eval8summaWithPrime(& ! input: model control dt_diff, & ! intent(in): step size to last time solution eqns_data%dt, & ! intent(in): total data step @@ -647,7 +647,7 @@ subroutine summaSolveSundials4ida( & call FN_VDestroy(sunvec_yp) retval = FSUNContext_Free(sunctx) -end subroutine summaSolveSundials4ida +end subroutine summaSolve4ida ! ---------------------------------------------------------------- ! SetInitialCondition: routine to initialize u and up vectors. @@ -921,4 +921,4 @@ integer(c_int) function layerDisCont4ida(t, sunvec_u, sunvec_up, gout, user_data end function layerDisCont4ida -end module summaSolveSundials4ida_module +end module summaSolve4ida_module diff --git a/build/source/engine/summaSolve.f90 b/build/source/engine/summaSolve4numrec.f90 similarity index 99% rename from build/source/engine/summaSolve.f90 rename to build/source/engine/summaSolve4numrec.f90 index b5b8d893e..1db69bc04 100644 --- a/build/source/engine/summaSolve.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -18,7 +18,7 @@ ! You should have received a copy of the GNU General Public License ! along with this program. If not, see . -module summaSolve_module +module summaSolve4numrec_module ! data types USE nrtype @@ -78,13 +78,13 @@ module summaSolve_module implicit none private -public::summaSolve +public::summaSolve4numrec contains ! ********************************************************************************************************* - ! public subroutine summaSolve: calculate the iteration increment, evaluate the new state, and refine if necessary + ! public subroutine summaSolve4numrec: calculate the iteration increment, evaluate the new state, and refine if necessary ! ********************************************************************************************************* - subroutine summaSolve(& + subroutine summaSolve4numrec(& ! input: model control dt_cur, & ! intent(in): current stepsize dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate @@ -217,7 +217,7 @@ subroutine summaSolve(& associate(ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision) ! intent(in): [i4b] groundwater parameterization ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message='summaSolve/' + err=0; message='summaSolve4numrec/' ! get the number of soil layers in the solution vector mSoil = size(indx_data%var(iLookINDEX%ixMatOnly)%dat) @@ -1353,9 +1353,9 @@ subroutine imposeConstraints(stateVecTrial,xInc,err,message) end subroutine imposeConstraints - end subroutine summaSolve + end subroutine summaSolve4numrec -end module summaSolve_module +end module summaSolve4numrec_module diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index d43527f61..0304a1a36 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -84,78 +84,84 @@ module systemSolv_module model_options ! defines the model decisions ! look-up values for the choice of heat capacity computation -USE mDecisions_module,only: & - enthalpyFD ! heat capacity using enthalpy +USE mDecisions_module,only:& + enthalpyFD ! heat capacity using enthalpy ! look-up values for the choice of groundwater representation (local-column, or single-basin) -USE mDecisions_module,only: & - localColumn, & ! separate groundwater representation in each local soil column - singleBasin ! single groundwater store over the entire basin +USE mDecisions_module,only:& + localColumn, & ! separate groundwater representation in each local soil column + singleBasin ! single groundwater store over the entire basin ! look-up values for the choice of groundwater parameterization -USE mDecisions_module,only: & - qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization - bigBucket, & ! a big bucket (lumped aquifer model) - noExplicit ! no explicit groundwater parameterization +USE mDecisions_module,only: & + qbaseTopmodel,& ! TOPMODEL-ish baseflow parameterization + bigBucket, & ! a big bucket (lumped aquifer model) + noExplicit ! no explicit groundwater parameterization + + ! look-up values for the numerical method +USE mDecisions_module,only:& + numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes + kinsol ,& ! SUNDIALS backward Euler solution using Kinsol + ida ! SUNDIALS solution using IDA ! safety: set private unless specified otherwise implicit none private public::systemSolv -! control parameters -real(rkind),parameter :: valueMissing=-9999._rkind ! missing value -real(rkind),parameter :: verySmall=1.e-12_rkind ! a very small number (used to check consistency) -real(rkind),parameter :: veryBig=1.e+20_rkind ! a very big number -real(rkind),parameter :: dx = 1.e-8_rkind ! finite difference increment - contains - ! ********************************************************************************************************** - ! public subroutine systemSolv: run the coupled energy-mass model for one timestep - ! ********************************************************************************************************** - subroutine systemSolv(& - ! input: model control - dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): time step (s) - nState, & ! intent(in): total number of state variables - firstSubStep, & ! intent(in): flag to denote first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation - scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution - ! input/output: data structures - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): index data - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_temp, & ! intent(inout): model fluxes for a local HRU - bvar_data, & ! intent(in): model variables for the local basin - model_decisions, & ! intent(in): model decisions - stateVecInit, & ! intent(in): initial state vector - ! output - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - untappedMelt, & ! intent(out): un-tapped melt energy (J m-3 s-1) - stateVecTrial, & ! intent(out): updated state vector - reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step - tooMuchMelt, & ! intent(out): flag to denote that there was too much melt - niter, & ! intent(out): number of iterations taken - err,message) ! intent(out): error code and error message +! ********************************************************************************************************** +! public subroutine systemSolv: run the coupled energy-mass model for one timestep +! ********************************************************************************************************** +subroutine systemSolv(& + ! input: model control + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): entire time step (s) + nState, & ! intent(in): total number of state variables + firstSubStep, & ! intent(in): flag to denote first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution + ! input/output: data structures + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): index data + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_temp, & ! intent(inout): model fluxes for a local HRU + bvar_data, & ! intent(in): model variables for the local basin + model_decisions, & ! intent(in): model decisions + stateVecInit, & ! intent(in): initial state vector + ! output + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + stateVecTrial, & ! intent(out): updated state vector + stateVecPrime, & ! intent(out): updated state vector if need the prime space (ida) + niter, & ! intent(out): number of iterations taken (numrec) + reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step + tooMuchMelt, & ! intent(out): flag to denote that there was too much melt + err,message) ! intent(out): error code and error message ! --------------------------------------------------------------------------------------- ! structure allocations USE allocspace_module,only:allocLocal ! allocate local data structures - ! simulation of fluxes and residuals given a trial state vector - USE eval8summa_module,only:eval8summa ! simulation of fluxes and residuals given a trial state vector - USE summaSolve_module,only:summaSolve ! calculate the iteration increment, evaluate the new state, and refine if necessary + ! state vector and solver USE getVectorz_module,only:getScaling ! get the scaling vectors USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy +#ifdef SUNDIALS_ACTIVE + USE tol4ida_module,only:popTol4ida ! populate tolerances + USE eval8summaWithPrime_module,only:eval8summaWithPrime ! get the fluxes and residuals + USE summaSolve4ida_module,only:summaSolve4ida ! solve DAE by IDA +#endif + USE eval8summa_module,only:eval8summa ! get the fluxes and residuals + USE summaSolve4numrec_module,only:summaSolve4numrec ! solve by numerical recipes + implicit none ! --------------------------------------------------------------------------------------- ! * dummy variables @@ -166,7 +172,7 @@ subroutine systemSolv(& integer(i4b),intent(in) :: nState ! total number of state variables logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call - logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt),intent(inout) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input/output: data structures @@ -185,8 +191,8 @@ subroutine systemSolv(& ! output: model control type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) real(rkind),intent(out) :: stateVecTrial(:) ! trial state vector (mixed units) + real(rkind),intent(out) :: stateVecPrime(:) ! trial state vector (mixed units) logical(lgt),intent(out) :: reduceCoupledStep ! flag to reduce the length of the coupled step logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that there was too much melt integer(i4b),intent(out) :: niter ! number of iterations taken @@ -195,6 +201,7 @@ subroutine systemSolv(& ! --------------------------------------------------------------------------------------- ! * general local variables ! --------------------------------------------------------------------------------------- + real(qp) :: dt_out ! time step sum for data window at termination of sundials character(LEN=256) :: cmessage ! error message of downwind routine integer(i4b) :: iter ! iteration index integer(i4b) :: iVar ! index of variable @@ -204,42 +211,51 @@ subroutine systemSolv(& integer(i4b) :: local_ixGroundwater ! local index for groundwater representation real(rkind) :: bulkDensity ! bulk density of a given layer (kg m-3) real(rkind) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) - real(rkind),parameter :: tinyStep=0.000001_rkind ! stupidly small time step (s) + real(rkind),parameter :: tinyStep=0.000001_rkind ! stupidly small time step (s) ! ------------------------------------------------------------------------------------------------------ ! * model solver ! ------------------------------------------------------------------------------------------------------ - logical(lgt),parameter :: forceFullMatrix=.false. ! flag to force the use of the full Jacobian matrix - integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) - type(var_dlength) :: flux_init ! model fluxes at the start of the time step - real(rkind),allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! NOTE: allocatable, since not always needed - real(rkind) :: stateVecNew(nState) ! new state vector (mixed units) - real(rkind) :: fluxVec0(nState) ! flux vector (mixed units) - real(rkind) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) - real(rkind) :: xScale(nState) ! characteristic scale of the state vector (mixed units) - real(rkind) :: dMat(nState) ! diagonal matrix (excludes flux derivatives) - real(qp) :: sMul(nState) ! NOTE: qp ! multiplier for state vector for the residual calculations - real(qp) :: rVec(nState) ! NOTE: qp ! residual vector - real(rkind) :: rAdd(nState) ! additional terms in the residual vector - real(rkind) :: fOld,fNew ! function values (-); NOTE: dimensionless because scaled - real(rkind) :: xMin,xMax ! state minimum and maximum (mixed units) - integer(i4b) :: maxiter ! maximum number of iterations - integer(i4b) :: localMaxIter ! maximum number of iterations (depends on solution type) - integer(i4b), parameter :: scalarMaxIter=100 ! maximum number of iterations for the scalar solution - logical(lgt) :: converged ! convergence flag - logical(lgt) :: feasible ! feasibility flag - real(rkind) :: resSinkNew(nState) ! additional terms in the residual vector - real(rkind) :: fluxVecNew(nState) ! new flux vector - real(qp) :: resVecNew(nState) ! NOTE: qp ! new residual vector + logical(lgt),parameter :: forceFullMatrix=.false. ! flag to force the use of the full Jacobian matrix + integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) + type(var_dlength) :: flux_init ! model fluxes at the start of the time step + real(rkind),allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! NOTE: allocatable, since not always needed + real(rkind) :: stateVecNew(nState) ! new state vector (mixed units) + real(rkind) :: fluxVec0(nState) ! flux vector (mixed units) + real(rkind) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) + real(rkind) :: xScale(nState) ! characteristic scale of the state vector (mixed units) + real(rkind) :: dMat(nState) ! diagonal matrix (excludes flux derivatives) + real(qp) :: sMul(nState) ! NOTE: qp ! multiplier for state vector for the residual calculations + real(qp) :: rVec(nState) ! NOTE: qp ! residual vector + real(rkind) :: rAdd(nState) ! additional terms in the residual vector + logical(lgt) :: feasible ! feasibility flag + ! ida variables + real(rkind) :: atol(nState) ! absolute tolerance ida + real(rkind) :: rtol(nState) ! relative tolerance ida + type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt_out (=dt) + real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix + real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! derivative value for total water matric potential (m s-1) + logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step + ! numrec variables + real(rkind) :: fOld,fNew ! function values (-); NOTE: dimensionless because scaled numrec + real(rkind) :: xMin,xMax ! state minimum and maximum (mixed units) numrec + integer(i4b) :: maxiter ! maximum number of iterations numrec + integer(i4b) :: localMaxIter ! maximum number of iterations (depends on solution type) numrec + integer(i4b), parameter :: scalarMaxIter=100 ! maximum number of iterations for the scalar solution numrec + logical(lgt) :: converged ! convergence flag numrec + real(rkind) :: resSinkNew(nState) ! additional terms in the residual vector numrec + real(rkind) :: fluxVecNew(nState) ! new flux vector numrec + real(qp) :: resVecNew(nState) ! NOTE: qp ! new residual vector numrec ! enthalpy derivatives - real(rkind) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature - real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state - real(rkind) :: dEnthalpy_dTk(nState) ! derivatives in layer enthalpy w.r.t. temperature - real(rkind) :: dEnthalpy_dWat(nState) ! derivatives in layer enthalpy w.r.t. water state + real(rkind) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature + real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state + real(rkind) :: dEnthalpy_dTk(nState) ! derivatives in layer enthalpy w.r.t. temperature + real(rkind) :: dEnthalpy_dWat(nState) ! derivatives in layer enthalpy w.r.t. water state ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- globalVars: associate(& ! model decisions + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical method, backward Euler or SUNDIALS/IDA ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) @@ -247,38 +263,38 @@ subroutine systemSolv(& scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) - ! derivatives, diagnositic for enthalpy - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + ! derivatives, diagnostic for enthalpy + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + ! soil parameters + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) ! model state variables scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp] mass of liquid water on the vegetation canopy (kg m-2) scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) ! model state variables (snow and soil domains) mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) + ! model state variables (aquifer) + scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in): [dp] storage of water in the aquifer (m) ! check the need to merge snow layers mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ! accelerate solution for temperature - airtemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) ! vector of energy and hydrology indices for the snow and soil domains ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the soil domain nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology state variables in the snow+soil domain - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology state variables in the soil domain ! mapping from full domain to the sub-domain ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b] mapping of full state vector to the state subset ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b] index of control volume for different domains (veg, snow, soil) @@ -311,10 +327,7 @@ subroutine systemSolv(& ! initialize the flags tooMuchMelt = .false. ! too much melt reduceCoupledStep = .false. ! need to reduce the length of the coupled step - - ! define maximum number of iterations - maxiter = nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)) - + ! modify the groundwater representation for this single-column implementation select case(ixSpatialGroundwater) case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale @@ -326,6 +339,15 @@ subroutine systemSolv(& call allocLocal(flux_meta(:),flux_init,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + ! allocate space for mLayerCmpress_sum and mLayerMatricHeadPrime at the start of the time step + if(num_method==ida)then + allocate( mLayerCmpress_sum(nSoil) ) + allocate( mLayerMatricHeadPrime(nSoil) ) + else + allocate( mLayerCmpress_sum(0) ) + allocate( mLayerMatricHeadPrime(0) ) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix + end if + ! allocate space for the baseflow derivatives ! NOTE: needs allocation because only used when baseflow sinks are active if(ixGroundwater==qbaseTopmodel)then @@ -357,14 +379,14 @@ subroutine systemSolv(& ! initialize state vectors call getScaling(& ! input - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers ! output - fScale, & ! intent(out): function scaling vector (mixed units) - xScale, & ! intent(out): variable scaling vector (mixed units) - sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) - dMat, & ! intent(out): diagonal of the Jacobian matrix (excludes fluxes) - err,cmessage) ! intent(out): error control + fScale, & ! intent(out): function scaling vector (mixed units) + xScale, & ! intent(out): variable scaling vector (mixed units) + sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) + dMat, & ! intent(out): diagonal of the Jacobian matrix (excludes fluxes) + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) ! ----- @@ -374,90 +396,166 @@ subroutine systemSolv(& ! initialize the trial state vectors stateVecTrial = stateVecInit - ! compute the initial function evaluation if(ixHowHeatCap == enthalpyFD)then ! compute H_T at the beginning of the data step without phase change call t2enthalpy(& - .false., & ! intent(in): logical flag to not include phase change in enthalpy - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTemp, & ! intent(in): value of canopy air temperature (K) - scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) - scalarCanopyWat, & ! intent(in): value of canopy total water (kg m-2) - scalarCanopyIce, & ! intent(in): value for canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - mLayerTemp, & ! intent(in): vector of layer temperature (K) - mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) - mLayerMatricHead, & ! intent(in): vector of total water matric potential (m) - mLayerVolFracIce, & ! intent(in): vector of volumetric fraction of ice (-) - ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) - dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature - dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state - dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature - dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state - ! output: error control - err,cmessage) ! intent(out): error control + .false., & ! intent(in): logical flag to not include phase change in enthalpy + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTemp, & ! intent(in): value of canopy air temperature (K) + scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) + scalarCanopyWat, & ! intent(in): value of canopy total water (kg m-2) + scalarCanopyIce, & ! intent(in): value for canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTemp, & ! intent(in): vector of layer temperature (K) + mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) + mLayerMatricHead, & ! intent(in): vector of total water matric potential (m) + mLayerVolFracIce, & ! intent(in): vector of volumetric fraction of ice (-) + ! input: pre-computed derivatives + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + ! output: enthalpy + scalarCanairEnthalpy, & ! intent(out): temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) + dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state + ! output: error control + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif endif - ! compute the flux and the residual vector for a given state vector - ! NOTE 1: The derivatives computed in eval8summa are used to calculate the Jacobian matrix for the first iteration - ! NOTE 2: The Jacobian matrix together with the residual vector is used to calculate the first iteration increment - call eval8summa(& - ! input: model control - dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): number of layers - nState, & ! intent(in): number of state variables in the current subset - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVecTrial, & ! intent(in): model state vector - fScale, & ! intent(in): function scaling vector - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): index data - ! input-output: data structures - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - ! output - feasible, & ! intent(out): flag to denote the feasibility of the solution - fluxVec0, & ! intent(out): flux vector - rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector - fOld, & ! intent(out): function evaluation - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + ! compute the initial flux and the residual vector, also gets values needed for the Jacobian matrix + select case(ixNumericalMethod) + case(ida) +#ifdef SUNDIALS_ACTIVE + call eval8summaWithPrime(& + ! input: model control + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): number of layers + nState, & ! intent(in): number of state variables in the current subset + .false., & ! intent(in): we are outside Sundials solver loop + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVecTrial, & ! intent(in): model state vector + fScale, & ! intent(in): function scaling vector + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + indx_data, & ! intent(inout): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: here we need to pass some extra variables that do not get updated in in the Sundials loops + scalarCanopyTemp, & ! intent(out): trial value of canopy temperature (K) + scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) + scalarCanopyIce, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyIce, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiq, & ! intent(out): trial value of canopy liquid water (kg m-2) + scalarCanopyLiq, & ! intent(in): value of canopy liquid water (kg m-2) + scalarCanopyEnthalpy, & ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpy, & ! intent(in): value for enthalpy of the vegetation canopy (J m-3) + mLayerTemp, & ! intent(out): trial vector of layer temperature (K) + mLayerTemp, & ! intent(in): vector of layer temperature (K) + mLayerMatricHeadLiq, & ! intent(out): trial value for liquid water matric potential (m) + mLayerMatricHead, & ! intent(out): trial value for total water matric potential (m) + mLayerMatricHead, & ! intent(in): value for total water matric potential (m) + mLayerVolFracWat, & ! intent(out): trial vector of volumetric total water content (-) + mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) + mLayerVolFracIce, & ! intent(out): trial vector of volumetric ice water content (-) + mLayerVolFracIce, & ! intent(in): vector of volumetric ice water content (-) + mLayerVolFracLiq, & ! intent(out): trial vector of volumetric liquid water content (-) + mLayerVolFracLiq, & ! intent(in): vector of volumetric liquid water content (-) + scalarAquiferStorage, & ! intent(out): trial value of storage of water in the aquifer (m) + scalarAquiferStorage, & ! intent(in): value of storage of water in the aquifer (m) + mLayerEnthalpy, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) + mLayerEnthalpy, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) + mLayerMatricHeadPrime, & ! intent(out): derivative value for total water matric potential (m s-1) + ! input-output: baseflow + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + ! output: flux and residual vectors + feasible, & ! intent(out): flag to denote the feasibility of the solution + fluxVec0, & ! intent(out): flux vector + rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec, & ! intent(out): residual vector + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + +#else + err=20; message=trim(message)//'cannot use num_method as ida if did not compile with -DCMAKE_BUILD_TYPE=Sundials'; return +#endif + case(numrec) + call eval8summa(& + ! input: model control + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): number of layers + nState, & ! intent(in): number of state variables in the current subset + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVecTrial, & ! intent(in): model state vector + fScale, & ! intent(in): function scaling vector + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): index data + ! input-output: data structures + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + ! output + feasible, & ! intent(out): flag to denote the feasibility of the solution + fluxVec0, & ! intent(out): flux vector + rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec, & ! intent(out): residual vector + fOld, & ! intent(out): function evaluation + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + + case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or numrec (or itertive, which is numrec)'; return + end select + if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif ! copy over the initial flux structure since some model fluxes are not computed in the iterations @@ -472,132 +570,229 @@ subroutine systemSolv(& volEnthalpy = temp2ethpy(mLayerTemp(1),bulkDensity,snowfrz_scale) ! set flag and error codes for too much melt if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_cur)then - tooMuchMelt=.true. - message=trim(message)//'net flux in the top snow layer can melt all the snow in the top layer' - err=-20; return ! negative error code to denote a warning + tooMuchMelt=.true. + message=trim(message)//'net flux in the top snow layer can melt all the snow in the top layer' + err=-20; return ! negative error code to denote a warning endif endif ! ************************** - ! * solving F(y) = 0 by Backward Euler. Here, y is the state vector + ! * Solving the System ! ************************** + select case(ixNumericalMethod) + case(ida) +#ifdef SUNDIALS_ACTIVE + ! get tolerance vectors + call popTol4ida(& + ! input + nState, & ! intent(in): number of desired state variables + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + mpar_data, & ! intent(in): model parameters + ! output + atol, & ! intent(out): absolute tolerances vector (mixed units) + rtol, & ! intent(out): relative tolerances vector (mixed units) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + + ! allocate space for the temporary flux_sum structure + call allocLocal(flux_meta(:),flux_sum,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! initialize flux_sum + do concurrent ( iVar=1:size(flux_meta) ) + flux_sum%var(iVar)%dat(:) = 0._rkind + end do + ! initialize sum of compression of the soil matrix + mLayerCmpress_sum(:) = 0._rkind + + !--------------------------- + ! * solving F(y,y') = 0 by IDA, y is the state vector and y' is the time derivative vector dy/dt + !--------------------------- + ! iterations and updates to trial state vector, fluxes, and derivatives are done inside IDA solver + call summaSolve4ida(& + dt_cur, & ! intent(in): data time step + atol, & ! intent(in): absolute tolerance + rtol, & ! intent(in): relative tolerance + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): number of snow+soil layers + nState, & ! intent(in): number of state variables in the current subset + ixMatrix, & ! intent(in): type of matrix (dense or banded) + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vector + stateVecTrial, & ! intent(in): model state vector at the beginning of the data time step + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + dMat, & ! intent(inout) diagonal of the Jacobian matrix (excludes fluxes) + ! input: data structures + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): index data + ! input-output: data structures + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_temp, & ! intent(inout): model fluxes for a local HRU + flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + mLayerCmpress_sum, & ! intent(inout): sum of compression of the soil matrix + ! output + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step + tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt + dt_out, & ! intent(out): time step sum for entire data window at termination of sundials + stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step + stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step + err,cmessage) ! intent(out): error control + ! check if IDA is successful + if( .not.idaSucceeds )then + err = 20 + message=trim(message)//trim(cmessage) + ! reduceCoupledStep = .true. + return + else + if (tooMuchMelt) return !exit to start same step over after merge + endif + niter = 0 ! iterations inside IDA solver + + ! ----- + ! * update states... + ! ------------------ + + ! compute average flux + do iVar=1,size(flux_meta) + flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / dt_out + end do + + ! compute the total change in storage associated with compression of the soil matrix (kg m-2) + diag_data%var(iLookDIAG%mLayerCompress)%dat(:) = mLayerCmpress_sum(:) / dt_out + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sum(diag_data%var(iLookDIAG%mLayerCompress)%dat(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water + + ! save the computed solution + stateVecTrial = stateVecNew +#endif + case(numrec) + ! define maximum number of iterations + maxiter = nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)) + + ! correct the number of iterations + localMaxIter = merge(scalarMaxIter, maxIter, scalarSolution) + + !--------------------------- + ! * solving F(y) = 0 by Backward Euler with free numerical recipes routines, y is the state vector + !--------------------------- + ! iterate and update trial state vector, fluxes, and derivatives + do iter=1,localMaxIter + ! keep track of the number of iterations + niter = iter+1 ! +1 because xFluxResid was moved outside the iteration loop (for backwards compatibility) + call summaSolve4numrec(& + ! input: model control + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate + iter, & ! intent(in): iteration index + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + nLeadDim, & ! intent(in): length of the leading dimension of the Jacobian matrix (either nBands or nState) + nState, & ! intent(in): total number of state variables + ixMatrix, & ! intent(in): type of matrix (full or band diagonal) + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVecTrial, & ! intent(in): trial state vector + xMin,xMax, & ! intent(inout): state maximum and minimum + fScale, & ! intent(in): function scaling vector + xScale, & ! intent(in): "variable" scaling vector, i.e., for state variables + rVec, & ! intent(in): residual vector + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + dMat, & ! intent(inout): diagonal matrix (excludes flux derivatives) + fOld, & ! intent(in): old function evaluation + ! input: data structures + model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): index data + ! input-output: data structures + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_temp, & ! intent(inout): model fluxes for a local HRU (temporary structure) + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(inout): derivative in baseflow w.r.t. matric head (s-1) + ! output + stateVecNew, & ! intent(out): new state vector + fluxVecNew, & ! intent(out): new flux vector + resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equa + resVecNew, & ! intent(out): new residual vector + fNew, & ! intent(out): new function evaluation + converged, & ! intent(out): convergence flag + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + + ! save the computed functions, residuals, and solution + fOld = fNew + rVec = resVecNew + stateVecTrial = stateVecNew + ! NOTE 1: The derivatives computed in summaSolve4numrec are used to calculate the Jacobian matrix at the next iteration + ! NOTE 2: The Jacobian matrix together with the residual vector is used to calculate the new iteration increment + + ! exit iteration loop if converged + if(converged) exit + + ! check convergence + if(iter==localMaxiter)then + message=trim(message)//'failed to converge' + err=-20; return + endif + + end do ! iterating + + ! ----- + ! * update states... + ! ------------------ + stateVecPrime = stateVecTrial ! prime values not used here, dummy + + ! update temperatures (ensure new temperature is consistent with the fluxes) + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + iState = ixSnowSoilNrg(iLayer) + stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState))/real(sMul(iState), rkind) + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! update volumetric water content in the snow (ensure change in state is consistent with the fluxes) + ! NOTE: for soil water balance is constrained within the iteration loop + if(nSnowSoilHyd>0)then + do concurrent (iLayer=1:nSnow,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing water state variables in the snow domain) + iState = ixSnowSoilHyd(iLayer) + stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState)) + end do ! looping through non-missing water state variables in the soil domain + endif + + end select - ! correct the number of iterations - localMaxIter = merge(scalarMaxIter, maxIter, scalarSolution) - - ! iterate - do iter=1,localMaxIter - - ! keep track of the number of iterations - niter = iter+1 ! +1 because xFluxResid was moved outside the iteration loop (for backwards compatibility) - - ! compute the next trial state vector - ! 1) Computes the Jacobian matrix based on derivatives from the last flux evaluation - ! 2) Computes the iteration increment based on Jacobian and residuals from the last flux evaluation - ! 3) Computes new fluxes and derivatives, new residuals, and (if necessary) refines the state vector - call summaSolve(& - ! input: model control - dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate - iter, & ! intent(in): iteration index - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - nLeadDim, & ! intent(in): length of the leading dimension of the Jacobian matrix (either nBands or nState) - nState, & ! intent(in): total number of state variables - ixMatrix, & ! intent(in): type of matrix (full or band diagonal) - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVecTrial, & ! intent(in): trial state vector - xMin,xMax, & ! intent(inout): state maximum and minimum - fScale, & ! intent(in): function scaling vector - xScale, & ! intent(in): "variable" scaling vector, i.e., for state variables - rVec, & ! intent(in): residual vector - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - dMat, & ! intent(inout): diagonal matrix (excludes flux derivatives) - fOld, & ! intent(in): old function evaluation - ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): index data - ! input-output: data structures - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_temp, & ! intent(inout): model fluxes for a local HRU (temporary structure) - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(inout): derivative in baseflow w.r.t. matric head (s-1) - ! output - stateVecNew, & ! intent(out): new state vector - fluxVecNew, & ! intent(out): new flux vector - resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equa - resVecNew, & ! intent(out): new residual vector - fNew, & ! intent(out): new function evaluation - converged, & ! intent(out): convergence flag - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - - ! update function evaluation, residual vector, and states - ! NOTE 1: The derivatives computed in summaSolve are used to calculate the Jacobian matrix at the next iteration - ! NOTE 2: The Jacobian matrix together with the residual vector is used to calculate the new iteration increment - - ! save functions and residuals - fOld = fNew - rVec = resVecNew - stateVecTrial = stateVecNew - - ! exit iteration loop if converged - if(converged) exit - - ! check convergence - if(iter==localMaxiter)then - message=trim(message)//'failed to converge' - err=-20; return - endif - - end do ! iterating - - ! ----- - ! * update states... - ! ------------------ - - ! set untapped melt energy to zero - untappedMelt(:) = 0._rkind - - ! update temperatures (ensure new temperature is consistent with the fluxes) - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - iState = ixSnowSoilNrg(iLayer) - stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState))/real(sMul(iState), rkind) - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! update volumetric water content in the snow (ensure change in state is consistent with the fluxes) - ! NOTE: for soil water balance is constrained within the iteration loop - if(nSnowSoilHyd>0)then - do concurrent (iLayer=1:nSnow,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing water state variables in the snow domain) - iState = ixSnowSoilHyd(iLayer) - stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState)) - end do ! looping through non-missing water state variables in the soil domain - endif - + ! ************************** ! free memory + deallocate(mLayerCmpress_sum) + deallocate(mLayerMatricHeadPrime) deallocate(dBaseflow_dMatric) - ! end associate statements - end associate globalVars - + ! end associate statements + end associate globalVars - end subroutine systemSolv +end subroutine systemSolv end module systemSolv_module diff --git a/build/source/engine/systemSolvSundials.f90 b/build/source/engine/systemSolvSundials.f90 deleted file mode 100644 index 6a617546e..000000000 --- a/build/source/engine/systemSolvSundials.f90 +++ /dev/null @@ -1,583 +0,0 @@ - - -module systemSolvSundials_module - -! data types -USE nrtype - -! access the global print flag -USE globalData,only:globalPrintFlag - -! access missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing double precision number -USE globalData,only:quadMissing ! missing quadruple precision number - -! access matrix information -USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix -USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix -USE globalData,only: iJac1 ! first layer of the Jacobian to print -USE globalData,only: iJac2 ! last layer of the Jacobian to print - -! domain types -USE globalData,only:iname_veg ! named variables for vegetation -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil - -! state variable type -USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space -USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy -USE globalData,only:iname_watCanopy ! named variable defining the mass of total water on the vegetation canopy -USE globalData,only:iname_liqCanopy ! named variable defining the mass of liquid water on the vegetation canopy -USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers -USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers -USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers -USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers -USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers - -! global metadata -USE globalData,only:flux_meta ! metadata on the model fluxes - -! constants -USE multiconst,only:& - LH_fus, & ! latent heat of fusion (J K-1) - Tfreeze, & ! temperature at freezing (K) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water ! intrinsic density of liquid water (kg m-3) - -! provide access to indices that define elements of the data structures -USE var_lookup,only:iLookPROG ! named variables for structure elements -USE var_lookup,only:iLookDIAG ! named variables for structure elements -USE var_lookup,only:iLookFLUX ! named variables for structure elements -USE var_lookup,only:iLookFORCE ! named variables for structure elements -USE var_lookup,only:iLookPARAM ! named variables for structure elements -USE var_lookup,only:iLookINDEX ! named variables for structure elements -USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure -USE var_lookup,only:iLookDERIV ! named variables for structure elements - -! provide access to the derived types to define the data structures -USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! data vector with variable length dimension (rkind) - model_options ! defines the model decisions - -! look-up values for the choice of heat capacity computation -USE mDecisions_module,only: & - enthalpyFD ! heat capacity using enthalpy - -! look-up values for the choice of groundwater representation (local-column, or single-basin) -USE mDecisions_module,only: & - localColumn, & ! separate groundwater representation in each local soil column - singleBasin ! single groundwater store over the entire basin - -! look-up values for the choice of groundwater parameterization -USE mDecisions_module,only: & - qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization - bigBucket, & ! a big bucket (lumped aquifer model) - noExplicit ! no explicit groundwater parameterization - - -! safety: set private unless specified otherwise -implicit none -private -public::systemSolvSundials - -! control parameters -real(rkind),parameter :: valueMissing=-9999._rkind ! missing value -real(rkind),parameter :: verySmall=1.e-12_rkind ! a very small number (used to check consistency) -real(rkind),parameter :: veryBig=1.e+20_rkind ! a very big number -real(rkind),parameter :: dx = 1.e-8_rkind ! finite difference increment - -contains - - -! ********************************************************************************************************** -! public subroutine systemSolvSundials: run the coupled energy-mass model for one timestep -! ********************************************************************************************************** -subroutine systemSolvSundials(& - ! input: model control - dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): time step (s) - nState, & ! intent(in): total number of state variables - firstSubStep, & ! intent(in): flag to denote first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation - scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution - ! input/output: data structures - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): index data - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_temp, & ! intent(inout): model fluxes for a local HRU - bvar_data, & ! intent(in): model variables for the local basin - model_decisions, & ! intent(in): model decisions - stateVecInit, & ! intent(in): initial state vector - ! output - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - stateVecTrial, & ! intent(out): updated state vector - stateVecPrime, & ! intent(out): updated state vector - reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step - tooMuchMelt, & ! intent(out): flag to denote that there was too much melt - err,message) ! intent(out): error code and error message - ! --------------------------------------------------------------------------------------- - ! structure allocations - USE allocspace_module,only:allocLocal ! allocate local data structures - ! simulation of fluxes and residuals given a trial state vector - USE eval8summaSundials_module,only:eval8summaSundials ! simulation of fluxes and residuals given a trial state vector - USE getVectorz_module,only:getScaling ! get the scaling vectors - USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy - USE tol4ida_module,only:popTol4ida ! pop tolerances - USE summaSolveSundials4ida_module,only:summaSolveSundials4ida ! solve DAE by IDA - USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy - use, intrinsic :: iso_c_binding - implicit none - ! --------------------------------------------------------------------------------------- - ! * dummy variables - ! --------------------------------------------------------------------------------------- - ! input: model control - real(rkind),intent(in) :: dt_cur ! current stepsize - real(rkind),intent(in) :: dt ! time step (seconds) - integer(i4b),intent(in) :: nState ! total number of state variables - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call - logical(lgt),intent(inout) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - ! input/output: data structures - type(zLookup),intent(in) :: lookup_data ! lookup tables - type(var_i),intent(in) :: type_data ! type of vegetation and soil - type(var_d),intent(in) :: attr_data ! spatial attributes - type(var_d),intent(in) :: forc_data ! model forcing data - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU - type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_temp ! model fluxes for a local HRU - type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin - type(model_options),intent(in) :: model_decisions(:) ! model decisions - real(rkind),intent(in) :: stateVecInit(:) ! initial state vector (mixed units) - ! output: model control - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: stateVecTrial(:) ! trial state vector (mixed units) - real(rkind),intent(out) :: stateVecPrime(:) ! trial state vector (mixed units) - logical(lgt),intent(out) :: reduceCoupledStep ! flag to reduce the length of the coupled step - logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that there was too much melt - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! --------------------------------------------------------------------------------------- - ! * general local variables - ! --------------------------------------------------------------------------------------- - real(qp) :: dt_out ! time step sum for data window at termination of sundials - character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b) :: iVar ! index of variable - integer(i4b) :: local_ixGroundwater ! local index for groundwater representation - real(rkind) :: bulkDensity ! bulk density of a given layer (kg m-3) - real(rkind) :: volEnthalpy ! volumetric enthalpy of a given layer (J m-3) - real(rkind),parameter :: tinyStep=0.000001_rkind ! stupidly small time step (s) - ! ------------------------------------------------------------------------------------------------------ - ! * model solver - ! ------------------------------------------------------------------------------------------------------ - logical(lgt),parameter :: forceFullMatrix=.false. ! flag to force the use of the full Jacobian matrix - integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) - type(var_dlength) :: flux_init ! model fluxes at the start of the time step - real(rkind),allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! NOTE: allocatable, since not always needed - real(rkind) :: stateVecNew(nState) ! new state vector (mixed units) - real(rkind) :: fluxVec0(nState) ! flux vector (mixed units) - real(rkind) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) - real(rkind) :: xScale(nState) ! characteristic scale of the state vector (mixed units) - real(rkind) :: dMat(nState) ! diagonal matrix (excludes flux derivatives) - real(qp) :: sMul(nState) ! NOTE: qp ! multiplier for state vector for the residual calculations - real(qp) :: rVec(nState) ! NOTE: qp ! residual vector - real(rkind) :: rAdd(nState) ! additional terms in the residual vector - logical(lgt) :: feasible ! feasibility flag - real(rkind) :: atol(nState) ! absolute telerance - real(rkind) :: rtol(nState) ! relative tolerance - integer(i4b) :: iLayer ! index of model layer in the snow+soil domain - type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt_out (=dt) - real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix - real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! derivative value for total water matric potential (m s-1) - logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step - ! enthalpy derivatives - real(rkind) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature - real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state - real(rkind) :: dEnthalpy_dTk(nState) ! derivatives in layer enthalpy w.r.t. temperature - real(rkind) :: dEnthalpy_dWat(nState) ! derivatives in layer enthalpy w.r.t. water state - ! --------------------------------------------------------------------------------------- - ! point to variables in the data structures - ! --------------------------------------------------------------------------------------- - globalVars: associate(& - ! model decisions - ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy - ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization - ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) - ! enthalpy - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) - ! derivatives, diagnostic for enthalpy - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) - ! soil parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) - ! model state variables - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) - ! model state variables (aquifer) - scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in): [dp] storage of water in the aquifer (m) - ! check the need to merge snow layers - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ! accelerate solution for temperature - airtemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ! vector of energy and hydrology indices for the snow and soil domains - ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) - ! layer geometry - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in): [i4b] total number of layers - ) - ! --------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="systemSolvSundials/" - - ! ***** - ! (0) PRELIMINARIES... - ! ******************** - - ! ----- - ! * initialize... - ! --------------- - - ! check - if(dt_cur < tinyStep)then - message=trim(message)//'dt is tiny' - err=20; return - endif - - ! initialize the flags - tooMuchMelt = .false. ! too much melt - reduceCoupledStep = .false. ! need to reduce the length of the coupled step - - ! modify the groundwater representation for this single-column implementation - select case(ixSpatialGroundwater) - case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale - case(localColumn); local_ixGroundwater = ixGroundwater ! go with the specified decision - case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return - end select ! (modify the groundwater representation for this single-column implementation) - - ! allocate space for the model fluxes at the start of the time step - call allocLocal(flux_meta(:),flux_init,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! allocate space for mLayerCmpress_sum and mLayerMatricHeadPrime at the start of the time step - allocate( mLayerCmpress_sum(nSoil) ) - allocate( mLayerMatricHeadPrime(nSoil) ) - - ! allocate space for the baseflow derivatives - ! NOTE: needs allocation because only used when baseflow sinks are active - if(ixGroundwater==qbaseTopmodel)then - allocate(dBaseflow_dMatric(nSoil,nSoil),stat=err) ! baseflow depends on total storage in the soil column, hence on matric head in every soil layer - else - allocate(dBaseflow_dMatric(0,0),stat=err) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix - end if - if(err/=0)then; err=20; message=trim(message)//'unable to allocate space for the baseflow derivatives'; return; end if - - - ! identify the matrix solution method - ! (the type of matrix used to solve the linear system A.X=B) - if(local_ixGroundwater==qbaseTopmodel .or. scalarSolution .or. forceFullMatrix)then - ixMatrix=ixFullMatrix ! named variable to denote the full Jacobian matrix - else - ixMatrix=ixBandMatrix ! named variable to denote the band-diagonal matrix - endif - - ! initialize the model fluxes (some model fluxes are not computed in the iterations) - do iVar=1,size(flux_temp%var) - flux_init%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) - end do - - ! ----- - ! * get scaling vectors... - ! ------------------------ - - ! initialize state vectors - call getScaling(& - ! input - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output - fScale, & ! intent(out): function scaling vector (mixed units) - xScale, & ! intent(out): variable scaling vector (mixed units) - sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) - dMat, & ! intent(out): diagonal of the Jacobian matrix (excludes fluxes) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - ! initialize the trial state vectors - stateVecTrial = stateVecInit - - ! compute the initial function evaluation - if(ixHowHeatCap == enthalpyFD)then - ! compute H_T at the beginning of the data step without phase change - call t2enthalpy(& - .false., & ! intent(in): logical flag to not include phase change in enthalpy - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTemp, & ! intent(in): value of canopy air temperature (K) - scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) - scalarCanopyWat, & ! intent(in): value of canopy total water (kg m-2) - scalarCanopyIce, & ! intent(in): value for canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - mLayerTemp, & ! intent(in): vector of layer temperature (K) - mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) - mLayerMatricHead, & ! intent(in): vector of total water matric potential (m) - mLayerVolFracIce, & ! intent(in): vector of volumetric fraction of ice (-) - ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) - dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature - dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state - dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature - dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - endif - - ! compute the flux and the residual vector for a given state vector - ! NOTE: The values calculated in eval8summaSundials are used to calculate the initial flux - call eval8summaSundials(& - ! input: model control - dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): number of layers - nState, & ! intent(in): number of state variables in the current subset - .false., & ! intent(in): we are outside Sundials solver loop - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVecTrial, & ! intent(in): model state vector - fScale, & ! intent(in): function scaling vector - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: here we need to pass some extra variables that do not get updated in in the Sundials loops - scalarCanopyTemp, & ! intent(out): trial value of canopy temperature (K) - scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) - scalarCanopyIce, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyIce, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq, & ! intent(out): trial value of canopy liquid water (kg m-2) - scalarCanopyLiq, & ! intent(in): value of canopy liquid water (kg m-2) - scalarCanopyEnthalpy, & ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) - scalarCanopyEnthalpy, & ! intent(in): value for enthalpy of the vegetation canopy (J m-3) - mLayerTemp, & ! intent(out): trial vector of layer temperature (K) - mLayerTemp, & ! intent(in): vector of layer temperature (K) - mLayerMatricHeadLiq, & ! intent(out): trial value for liquid water matric potential (m) - mLayerMatricHead, & ! intent(out): trial value for total water matric potential (m) - mLayerMatricHead, & ! intent(in): value for total water matric potential (m) - mLayerVolFracWat, & ! intent(out): trial vector of volumetric total water content (-) - mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) - mLayerVolFracIce, & ! intent(out): trial vector of volumetric ice water content (-) - mLayerVolFracIce, & ! intent(in): vector of volumetric ice water content (-) - mLayerVolFracLiq, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerVolFracLiq, & ! intent(in): vector of volumetric liquid water content (-) - scalarAquiferStorage, & ! intent(out): trial value of storage of water in the aquifer (m) - scalarAquiferStorage, & ! intent(in): value of storage of water in the aquifer (m) - mLayerEnthalpy, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) - mLayerEnthalpy, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) - mLayerMatricHeadPrime, & ! intent(out): derivative value for total water matric potential (m s-1) - ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - ! output: flux and residual vectors - feasible, & ! intent(out): flag to denote the feasibility of the solution - fluxVec0, & ! intent(out): flux vector - rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif - - ! copy over the initial flux structure since some model fluxes are not computed in the iterations - do concurrent ( iVar=1:size(flux_meta) ) - flux_temp%var(iVar)%dat(:) = flux_init%var(iVar)%dat(:) - end do - - ! allocate space for the temporary flux_sum structure - call allocLocal(flux_meta(:),flux_sum,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! check the need to merge snow layers - if(nSnow>0)then - ! compute the energy required to melt the top snow layer (J m-2) - bulkDensity = mLayerVolFracIce(1)*iden_ice + mLayerVolFracLiq(1)*iden_water - volEnthalpy = temp2ethpy(mLayerTemp(1),bulkDensity,snowfrz_scale) - ! set flag and error codes for too much melt - if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_cur)then - tooMuchMelt=.true. - message=trim(message)//'net flux in the top snow layer can melt all the snow in the top layer' - err=-20; return ! negative error code to denote a warning - endif - endif - - ! get tolerance vectors - call popTol4ida(& - ! input - nState, & ! intent(in): number of desired state variables - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - mpar_data, & ! intent(in): model parameters - ! output - atol, & ! intent(out): absolute tolerances vector (mixed units) - rtol, & ! intent(out): relative tolerances vector (mixed units) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - ! ************************** - ! * solving F(y,y') = 0 by IDA. Here, y is the state vector - ! ************************** - - ! initialize flux_sum - do concurrent ( iVar=1:size(flux_meta) ) - flux_sum%var(iVar)%dat(:) = 0._rkind - end do - ! initialize sum of compression of the soil matrix - mLayerCmpress_sum(:) = 0._rkind - - call summaSolveSundials4ida(& - dt_cur, & ! intent(in): data time step - atol, & ! intent(in): absolute tolerance - rtol, & ! intent(in): relative tolerance - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): number of snow+soil layers - nState, & ! intent(in): number of state variables in the current subset - ixMatrix, & ! intent(in): type of matrix (dense or banded) - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vector - stateVecTrial, & ! intent(in): model state vector at the beginning of the data time step - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - dMat, & ! intent(inout) diagonal of the Jacobian matrix (excludes fluxes) - ! input: data structures - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): index data - ! input-output: data structures - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_temp, & ! intent(inout): model fluxes for a local HRU - flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - mLayerCmpress_sum, & ! intent(inout): sum of compression of the soil matrix - ! output - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step - tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt - dt_out, & ! intent(out): time step sum for entire data window at termination of sundials - stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step - stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step - err,cmessage) ! intent(out): error control - - ! check if IDA is successful - if( .not.idaSucceeds )then - err = 20 - message=trim(message)//trim(cmessage) - ! reduceCoupledStep = .true. - return - else - if (tooMuchMelt) return !exit to start same step over after merge - endif - - ! ----- - ! * update states... - ! ------------------ - - ! compute average flux - do iVar=1,size(flux_meta) - flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / dt_out - end do - - ! compute the total change in storage associated with compression of the soil matrix (kg m-2) - diag_data%var(iLookDIAG%mLayerCompress)%dat(:) = mLayerCmpress_sum(:) / dt_out - diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sum(diag_data%var(iLookDIAG%mLayerCompress)%dat(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water - - ! save the computed solution - stateVecTrial = stateVecNew - - ! free memory - deallocate(mLayerCmpress_sum) - deallocate(mLayerMatricHeadPrime) - deallocate(dBaseflow_dMatric) - - ! end associate statements - end associate globalVars - -end subroutine systemSolvSundials - -end module systemSolvSundials_module diff --git a/build/source/engine/updatStateSundials.f90 b/build/source/engine/updatStateWithPrime.f90 similarity index 99% rename from build/source/engine/updatStateSundials.f90 rename to build/source/engine/updatStateWithPrime.f90 index 843a805a1..d97acdf9a 100644 --- a/build/source/engine/updatStateSundials.f90 +++ b/build/source/engine/updatStateWithPrime.f90 @@ -1,4 +1,4 @@ -module updatStateSundials_module +module updatStateWithPrime_module USE nrtype ! physical constants USE multiconst,only:& @@ -152,4 +152,4 @@ subroutine updateSoilSundials(& end subroutine updateSoilSundials -end module updatStateSundials_module +end module updatStateWithPrime_module diff --git a/build/source/engine/updateVarsSundials.f90 b/build/source/engine/updateVarsWithPrime.f90 similarity index 98% rename from build/source/engine/updateVarsSundials.f90 rename to build/source/engine/updateVarsWithPrime.f90 index 09c35ea90..47c05a2d0 100644 --- a/build/source/engine/updateVarsSundials.f90 +++ b/build/source/engine/updateVarsWithPrime.f90 @@ -18,7 +18,7 @@ ! You should have received a copy of the GNU General Public License ! along with this program. If not, see . -module updateVarsSundials_module +module updateVarsWithPrime_module ! data types USE nrtype @@ -73,8 +73,8 @@ module updateVarsSundials_module USE var_lookup,only:iLookINDEX ! named variables for structure elements ! provide access to routines to update states -USE updatStateSundials_module,only:updateSnowSundials ! update snow states -USE updatStateSundials_module,only:updateSoilSundials ! update soil states +USE updatStateWithPrime_module,only:updateSnowSundials ! update snow states +USE updatStateWithPrime_module,only:updateSoilSundials ! update soil states ! provide access to functions for the constitutive functions and derivatives USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) @@ -85,23 +85,23 @@ module updateVarsSundials_module USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists -USE soil_utilsAddSundials_module,only:liquidHeadSundials ! compute the liquid water matric potential -USE soil_utilsAddSundials_module,only:d2Theta_dPsi2 -USE soil_utilsAddSundials_module,only:d2Theta_dTk2 +USE soil_utilsAddPrime_module,only:liquidHeadSundials ! compute the liquid water matric potential +USE soil_utilsAddPrime_module,only:d2Theta_dPsi2 +USE soil_utilsAddPrime_module,only:d2Theta_dTk2 ! IEEE checks USE, intrinsic :: ieee_arithmetic ! check values (NaN, etc.) implicit none private -public::updateVarsSundials +public::updateVarsWithPrime contains ! ********************************************************************************************************** -! public subroutine updateVarsSundials: compute diagnostic variables and derivatives for Sundials Jacobian +! public subroutine updateVarsWithPrime: compute diagnostic variables and derivatives for Sundials Jacobian ! ********************************************************************************************************** -subroutine updateVarsSundials(& +subroutine updateVarsWithPrime(& ! input computJac, & ! intent(in): logical flag if computing for Jacobian update do_adjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze @@ -270,7 +270,7 @@ subroutine updateVarsSundials(& ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message='updateVarsSundials/' + err=0; message='updateVarsWithPrime/' ! allocate space and assign values to the flag vector allocate(computedCoupling(size(ixMapSubset2Full)),stat=err) ! .true. if computed the coupling for a given state variable @@ -744,7 +744,7 @@ subroutine updateVarsSundials(& ! end association to the variables in the data structures end associate -end subroutine updateVarsSundials +end subroutine updateVarsWithPrime ! ********************************************************************************************************** @@ -781,4 +781,4 @@ subroutine xTempSolve(& derivative = heatCap + LH_fus*iden_water*dLiq_dT ! J m-3 K-1 end subroutine xTempSolve -end module updateVarsSundials_module +end module updateVarsWithPrime_module diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index ac24a3a6b..ec425b19e 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -72,8 +72,8 @@ module varSubstep_module ! look-up values for the numerical method USE mDecisions_module,only: & - be_numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes - be_kinsol ,& ! SUNDIALS backward Euler solution using Kinsol + numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes + kinsol ,& ! SUNDIALS backward Euler solution using Kinsol ida ! SUNDIALS solution using IDA ! safety: set private unless specified otherwise @@ -133,9 +133,6 @@ subroutine varSubstep(& ! simulation of fluxes and residuals given a trial state vector USE getVectorz_module,only:popStateVec ! populate the state vector USE getVectorz_module,only:varExtract ! extract variables from the state vector -#ifdef SUNDIALS_ACTIVE - USE systemSolvSundials_module,only:systemSolvSundials ! solve the system of equations for one time step -#endif USE systemSolv_module,only:systemSolv ! solve the system of equations for one time step ! identify name of variable type (for error message) USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages @@ -325,10 +322,7 @@ subroutine varSubstep(& ! * iterative solution... ! ----------------------- ! solve the system of equations for a given state subset - select case(ixNumericalMethod) - case(ida) -#ifdef SUNDIALS_ACTIVE - call systemSolvSundials(& + call systemSolv(& ! input: model control dtSubstep, & ! intent(in): time step (s) whole_step, & ! intent(in): entire time step (s), right now same as dtSubstep but might change with operator splitting @@ -355,56 +349,12 @@ subroutine varSubstep(& deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) stateVecTrial, & ! intent(out): updated state vector - stateVecPrime, & ! intent(out): updated state vector + stateVecPrime, & ! intent(out): updated state vector if need the prime space (ida) + niter, & ! intent(out): number of iterations taken (numrec) reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt err,cmessage) ! intent(out): error code and error message - untappedMelt(:) = 0._rkind ! set untapped melt energy to zero - niter = 0 ! will not use -#else - err=20; message=trim(message)//'cannot use num_method as uda if did not compile with -DCMAKE_BUILD_TYPE=Sundials'; return -#endif - case(be_numrec) - call systemSolv(& - ! input: model control - dtSubstep, & ! intent(in): time step (s) - whole_step, & ! intent(in): entire time step (s) - nState, & ! intent(in): total number of state variables - firstSubStep, & ! intent(in): flag to denote first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation - scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution - ! input/output: data structures - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(inout): index data - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_temp, & ! intent(inout): model fluxes for a local HRU - bvar_data, & ! intent(in): model variables for the local basin - model_decisions, & ! intent(in): model decisions - stateVecInit, & ! intent(in): initial state vector - ! output: model control - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - untappedMelt, & ! intent(out): un-tapped melt energy (J m-3 s-1) - stateVecTrial, & ! intent(out): updated state vector - reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step - tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt - niter, & ! intent(out): number of iterations taken - err,cmessage) ! intent(out): error code and error message - stateVecPrime = stateVecTrial ! will not use, dummy - case default; err=20; message=trim(message)//'expect num_method to be ida, be_kinsol, or be_numrec (or itertive, which is be_numrec)'; return - end select - - if(err/=0)then - message=trim(message)//trim(cmessage) - if(err>0) return - endif + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) ! if too much melt or need to reduce length of the coupled step then return ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry @@ -462,9 +412,9 @@ subroutine varSubstep(& ! identify the need to check the mass balance select case(ixNumericalMethod) - case(ida); checkMassBalance = .false. ! only for be_numrec because sundials has instantaneous fluxes only - case(be_numrec); checkMassBalance = .true. ! (.not.scalarSolution) - case default; err=20; message=trim(message)//'expect num_method to be ida, be_kinsol, or be_numrec (or itertive, which is be_numrec)'; return + case(ida); checkMassBalance = .false. ! only for numrec because sundials has instantaneous fluxes only + case(numrec); checkMassBalance = .true. ! (.not.scalarSolution) + case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or numrec (or itertive, which is numrec)'; return end select ! identify the need to check the energy balance, DOES NOT WORK YET and only check if ixHowHeatCap == enthalpyFD checkNrgBalance = .false. @@ -620,7 +570,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe waterBalanceError,nrgFluxModified,err,message) ! output: flags and error control USE getVectorz_module,only:varExtract ! extract variables from the state vector #ifdef SUNDIALS_ACTIVE - USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables + USE updateVarsWithPrime_module,only:updateVarsWithPrime ! update prognostic variables #endif USE updateVars_module,only:updateVars ! update prognostic variables USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy @@ -863,7 +813,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! update diagnostic variables - call updateVarsSundials(& + call updateVarsWithPrime(& ! input .false., & ! intent(in): logical flag if computing for Jacobian update doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze @@ -899,7 +849,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe #else err=20; message=trim(message)//'cannot use num_method as ida if did not compile with -DCMAKE_BUILD_TYPE=Sundials'; return #endif - case(be_numrec) + case(numrec) ! update diagnostic variables call updateVars(& ! input @@ -923,7 +873,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) ! output: error control err,cmessage) ! intent(out): error control - case default; err=20; message=trim(message)//'expect num_method to be ida, be_kinsol, or be_numrec (or itertive, which is be_numrec)'; return + case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or numrec (or itertive, which is numrec)'; return end select if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) diff --git a/docs/sundials_bmi/flags_params_sundials.txt b/docs/sundials_bmi/flags_params_sundials.txt index d5fb2039b..61f73d9c7 100644 --- a/docs/sundials_bmi/flags_params_sundials.txt +++ b/docs/sundials_bmi/flags_params_sundials.txt @@ -1,15 +1,13 @@ -To switch between SUMMA-BE and SUMMA-SUNDIALS, the num_method in the model_decision file can be either one of the values "be_numrec" (choice "itertive" is backward compatible), "be_kinsol", or "ida". +To switch between SUMMA-BE and SUMMA-SUNDIALS, the num_method in the model_decision file can be either one of the values "numrec" (choice "itertive" is backward compatible), "kinsol", or "ida". -In energy equation, the heat capacity is computed using either the analytical (closed) formula or the finite difference formula (dH_T/dT). The "howHeatCap" variable has been added to the var_lookup module to handle such decision. A user should add this variable to the model_decision file with one of the values "closedForm" or "enthalpyFD". Choice of num_method as "itertive" will set num_method=be_numrec and howHeatCap=closedForm. +In energy equation, the heat capacity is computed using either the analytical (closed) formula or the finite difference formula (dH_T/dT). The "howHeatCap" variable has been added to the var_lookup module to handle such decision. A user should add this variable to the model_decision file with one of the values "closedForm" or "enthalpyFD". Choice of num_method as "itertive" will set num_method=numrec and howHeatCap=closedForm. All SUMMA-SUNDIALS files are in build/source/engine. The important ones are as following: -systemSolvSundials.f90: contains public subroutine systemSolvSundials which runs the coupled energy-mass model for one timestep. +summaSolve4ida.f90: contains public subroutine summaSolve4ida which solves the differential equation system F(y,y') = 0 by IDA (y is the state vector) and private subroutines setInitialCondition and setSolverParams. Subroutine setSolverParams can be used to to set parameters (maximum order, number of nonlinear iteration , etc) in IDA solver. -summaSolveSundials4ida.f90: contains public subroutine summaSolveSundials4ida which solves the differential equation system F(y,y') = 0 by IDA (y is the state vector) and private subroutines setInitialCondition and setSolverParams. Subroutine setSolverParams can be used to to set parameters (maximum order, number of nonlinear iteration , etc) in IDA solver. +eval8summaWithPrime.f90: contains public subroutine eval8summaWithPrime which computes the residual vector F(t,y,y') mainly by calling varExtract, updateVarsWithPrime, computFlux, and computResidWithPrime. We also switch between different forms of the energy equation in this subroutine. It also contains public function eval8summa4ida which is the interface wrapper for computing the residual vector required for the IDA solver. -eval8summaSundials.f90: contains public subroutine eval8summaSundials which computes the residual vector F(t,y,y') mainly by calling varExtract, updateVarsSundials, computFlux, and computResidSundials. We also switch between different forms of the energy equation in this subroutine. It also contains public function eval8summa4ida which is the interface wrapper for computing the residual vector required for the IDA solver. - -computJacobSundials.f90: contains public subroutine computJacobSundials which computes the Jacobian matrix dF/dy + c dF/dy'. It also contains the public function computJacob4ida which is the interface wrapper for computing the Jacobian matrix required for the IDA solver. +computJacobWithPrime.f90: contains public subroutine computJacobWithPrime which computes the Jacobian matrix dF/dy + c dF/dy'. It also contains the public function computJacob4ida which is the interface wrapper for computing the Jacobian matrix required for the IDA solver. diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zDecisions.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zDecisions.txt index dfadcd9d1..eb2eae897 100644 --- a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zDecisions.txt +++ b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zDecisions.txt @@ -13,7 +13,7 @@ vegeParTbl USGS ! (04) vegetation category datas soilStress NoahType ! (05) choice of function for the soil moisture control on stomatal resistance stomResist BallBerry ! (06) choice of function for stomatal resistance ! *********************************************************************************************************************** -num_method be_numrec ! (07) choice of numerical method +num_method numrec ! (07) choice of numerical method fDerivMeth analytic ! (08) method used to calculate flux derivatives LAI_method monTable ! (09) method used to determine LAI and SAI f_Richards mixdform ! (10) form of Richard's equation @@ -63,8 +63,8 @@ howHeatCap closedForm ! (30) ! Jarvis ! Jarvis ! ----------------------------------------------------------------------------------------------- ! (07) choice of numerical method -! be_numrec ! home-grown backward Euler -! be_kinsol ! SUNDIALS backward Euler solution using Kinsol +! numrec ! home-grown backward Euler +! kinsol ! SUNDIALS backward Euler solution using Kinsol ! sundials ! SUNDIALS solution using IDA ! ----------------------------------------------------------------------------------------------- ! (08) method used to calculate flux derivatives From 5f15b66cf89c1b45979935d24703c874e4355305 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 13 Jun 2023 23:40:26 +0900 Subject: [PATCH 0743/1472] move checking feasibility to own solver --- build/source/dshare/get_ixname.f90 | 2 +- build/source/dshare/popMetadat.f90 | 2 +- build/source/dshare/type4kinsol.f90 | 8 +- build/source/engine/computJacob.f90 | 16 +- build/source/engine/computJacobWithPrime.f90 | 21 -- build/source/engine/eval8summa.f90 | 107 +------ build/source/engine/eval8summaWithPrime.f90 | 126 +------- build/source/engine/getVectorz.f90 | 121 ++++++++ build/source/engine/summaSolve4ida.f90 | 291 +++++++------------ build/source/engine/summaSolve4numrec.f90 | 41 ++- build/source/engine/systemSolv.f90 | 8 +- build/source/engine/tol4ida.f90 | 5 +- 12 files changed, 287 insertions(+), 461 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index feef3dfdb..f0eac12f7 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -381,7 +381,7 @@ function get_ixparam(varName) case('maxstep' ); get_ixparam = iLookPARAM%maxstep ! maximum length of the time step numrec case('be_steps' ); get_ixparam = iLookPARAM%be_steps ! minimum number of substeps to take in a maxstep numrec case('wimplicit' ); get_ixparam = iLookPARAM%wimplicit ! weight assigned to start-of-step fluxes numrec, not currently used - case('maxiter' ); get_ixparam = iLookPARAM%maxiter ! maximum number of iterations numrec or nonlinear iterations Sundials + case('maxiter' ); get_ixparam = iLookPARAM%maxiter ! maximum number of iterations numrec, kinsol, or nonlinear iterations Sundials case('relConvTol_liquid' ); get_ixparam = iLookPARAM%relConvTol_liquid ! relative convergence tolerance for vol frac liq water (-) numrec case('absConvTol_liquid' ); get_ixparam = iLookPARAM%absConvTol_liquid ! absolute convergence tolerance for vol frac liq water (-) numrec case('relConvTol_matric' ); get_ixparam = iLookPARAM%relConvTol_matric ! relative convergence tolerance for matric head (-) numrec diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 62f19e0b5..db174da8c 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -263,7 +263,7 @@ subroutine popMetadat(err,message) mpar_meta(iLookPARAM%maxstep) = var_info('maxstep' , 'maximum length of the time step numrec' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%be_steps) = var_info('be_steps' , 'minimum number of substeps to take in a maxstep numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%wimplicit) = var_info('wimplicit' , 'weight assigned to the start-of-step fluxes ,numrec, not currently used', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%maxiter) = var_info('maxiter' , 'maximum number of iterations numrec or nonlinear iterations Sundials', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%maxiter) = var_info('maxiter' , 'maximum number of iterations numrec, kinsol, or nonlinear iterations Sundials', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%relConvTol_liquid) = var_info('relConvTol_liquid' , 'relative convergence tolerance for vol frac liq water numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%absConvTol_liquid) = var_info('absConvTol_liquid' , 'absolute convergence tolerance for vol frac liq water numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%relConvTol_matric) = var_info('relConvTol_matric' , 'relative convergence tolerance for matric head numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/type4kinsol.f90 b/build/source/dshare/type4kinsol.f90 index 715a95ca7..19ef5e33d 100644 --- a/build/source/dshare/type4kinsol.f90 +++ b/build/source/dshare/type4kinsol.f90 @@ -13,6 +13,8 @@ module type4kinsol implicit none type eqnsData + type(c_ptr) :: kinsol_mem ! KINSOL memory + real(rkind) :: dt_cur ! current stepsize real(rkind) :: dt ! data step integer(i4b) :: nSnow ! number of snow layers integer(i4b) :: nSoil ! number of soil layers @@ -40,14 +42,14 @@ module type4kinsol real(rkind), allocatable :: dMat(:) ! diagonal of the Jacobian matrix real(rkind), allocatable :: fluxVec(:) ! flux vector real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation + real(rkind),allocatable :: fScale(:) ! function scaling vector + real(rkind),allocatable :: xScale(:) ! state scaling vector real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) integer(i4b) :: ixSaturation ! index of the lowest saturated layer integer(i4b) :: err ! error code character(len = 50) :: message ! error message - logical(lgt) :: feasible ! flag to denote the feasibility of the solution real(rkind) :: fEval ! function evaluation - real(rkind),allocatable :: fScale(:) ! function scaling vector - logical(lgt) :: firstStateiteration ! flag to denote if we computed an iteration so we know to save the state + logical(lgt) :: firstStateiteration ! flag to denote if we computed an iteration so we know to save the state end type eqnsData diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index eec627a6f..93363125a 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -41,20 +41,9 @@ module computJacob_module USE globalData,only:integerMissing ! missing integer USE globalData,only:realMissing ! missing real number -! domain types -USE globalData,only:iname_veg ! named variables for vegetation -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil - ! named variables to describe the state variable type -USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space -USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy -USE globalData,only:iname_watCanopy ! named variable defining the mass of water on the vegetation canopy -USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers -USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers -USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers -USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers +USE globalData,only:model_decisions ! model decision structure ! access named variables to describe the form and structure of the matrices used in the numerical solver USE globalData,only: ku ! number of super-diagonal bands, assume ku>=3 @@ -151,7 +140,6 @@ subroutine computJacob(& ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer ! vectors of indices for specfic state types within specific sub-domains IN THE FULL STATE VECTOR ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain - ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain ! vector of energy indices for the snow and soil domains ! NOTE: states not in the subset are equal to integerMissing ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain @@ -171,8 +159,6 @@ subroutine computJacob(& nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain ! type and index of model control volume ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain - ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the domain (iname_veg, iname_snow, iname_soil) - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for specific model domains ! mapping between states and model layers ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] list of indices in the full state vector that are in the state subset ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset in each element of the full state vector diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index a9155e55a..c7703f290 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -47,20 +47,8 @@ module computJacobWithPrime_module USE globalData,only:integerMissing ! missing integer USE globalData,only:realMissing ! missing real number -! domain types -USE globalData,only:iname_veg ! named variables for vegetation -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil - ! named variables to describe the state variable type -USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space -USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy -USE globalData,only:iname_watCanopy ! named variable defining the mass of water on the vegetation canopy -USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers -USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers -USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers -USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers USE globalData,only:model_decisions ! model decision structure ! access named variables to describe the form and structure of the matrices used in the numerical solver @@ -75,13 +63,7 @@ module computJacobWithPrime_module ! constants USE multiconst,only:& - Tfreeze, & ! temperature at freezing (K) LH_fus, & ! latent heat of fusion (J kg-1) - LH_vap, & ! latent heat of vaporization (J kg-1) - LH_sub, & ! latent heat of sublimation (J kg-1) - Cp_air, & ! specific heat of air (J kg-1 K-1) - iden_air, & ! intrinsic density of air (kg m-3) - iden_ice, & ! intrinsic density of ice (kg m-3) iden_water ! intrinsic density of liquid water (kg m-3) ! look-up values for the choice of groundwater representation (local-column, or single-basin) @@ -207,7 +189,6 @@ subroutine computJacobWithPrime(& ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer ! vectors of indices for specfic state types within specific sub-domains IN THE FULL STATE VECTOR ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain - ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain ! vector of energy indices for the snow and soil domains ! NOTE: states not in the subset are equal to integerMissing ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain @@ -227,8 +208,6 @@ subroutine computJacobWithPrime(& nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain ! type and index of model control volume ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain - ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the domain (iname_veg, iname_snow, iname_soil) - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for specific model domains ! mapping between states and model layers ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] list of indices in the full state vector that are in the state subset ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset in each element of the full state vector diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 02a369aca..ad2764511 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -35,29 +35,10 @@ module eval8summa_module USE globalData,only: iJac1 ! first layer of the Jacobian to print USE globalData,only: iJac2 ! last layer of the Jacobian to print -! domain types -USE globalData,only:iname_veg ! named variables for vegetation -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil - -! named variables to describe the state variable type -USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space -USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy -USE globalData,only:iname_watCanopy ! named variable defining the mass of water on the vegetation canopy -USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers -USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers -USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers -USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers -USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers ! constants USE multiconst,only:& Tfreeze, & ! temperature at freezing (K) - LH_fus, & ! latent heat of fusion (J kg-1) - LH_vap, & ! latent heat of vaporization (J kg-1) - LH_sub, & ! latent heat of sublimation (J kg-1) - Cp_air, & ! specific heat of air (J kg-1 K-1) - iden_air, & ! intrinsic density of air (kg m-3) iden_ice, & ! intrinsic density of ice (kg m-3) iden_water ! intrinsic density of liquid water (kg m-3) @@ -138,6 +119,7 @@ subroutine eval8summa(& ! -------------------------------------------------------------------------------------------------------------------------------- ! provide access to subroutines USE getVectorz_module, only:varExtract ! extract variables from the state vector + USE getVectorz_module, only:checkFeas ! check feasibility of state vector USE updateVars_module, only:updateVars ! update prognostic variables USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy USE computFlux_module, only:soilCmpres ! compute soil compression, use non-sundials version because sundials version needs mLayerMatricHeadPrime @@ -158,6 +140,7 @@ subroutine eval8summa(& integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers integer(i4b),intent(in) :: nState ! total number of state variables + logical(lgt),intent(in) :: insideSUN ! flag to indicate if we are inside Sundials solver logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation @@ -221,9 +204,6 @@ subroutine eval8summa(& integer(i4b) :: iLayer ! index of model layer in the snow+soil domain integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine - integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) - real(rkind) :: xMin,xMax ! minimum and maximum values for water content - real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) real(rkind),dimension(nState) :: rVecScaled ! scaled residual vector character(LEN=256) :: cmessage ! error message of downwind routine real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy @@ -242,12 +222,11 @@ subroutine eval8summa(& snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) ! soil parameters theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) ! canopy and layer depth canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! model state variables + ! model state variables from the previous solution scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) @@ -256,7 +235,7 @@ subroutine eval8summa(& mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] total water matric potential (m) mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in): [dp] storage of water in the aquifer (m) - ! model diagnostic variables from a previous solution + ! model diagnostic variables from the previous solution scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) @@ -264,7 +243,7 @@ subroutine eval8summa(& mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) - ! enthalpy + ! enthalpy from the previous solution scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) @@ -280,15 +259,6 @@ subroutine eval8summa(& ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) ! indices - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable (nrg) - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable (nrg) - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(out): volumetric heat capacity of vegetation canopy mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & ! intent(out): heat capacity for snow and soil ) ! association to variables in the data structures @@ -296,63 +266,18 @@ subroutine eval8summa(& ! initialize error control err=0; message="eval8summa/" - ! check the feasibility of the solution always with SUMMA BE - ! NOTE: we will not print infeasibilities since it does not indicate a failure, just a need to iterate until maxiter + ! check the feasibility of the solution always with BE numrec but not inside Sundials solver feasible=.true. - - ! check that the canopy air space temperature is reasonable - if(ixCasNrg/=integerMissing)then - if(stateVec(ixCasNrg) > canopyTempMax) feasible=.false. - if(stateVec(ixCasNrg) > canopyTempMax) message=trim(message)//'canopy air space temp high,' - !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixCasNrg )', feasible, canopyTempMax, stateVec(ixCasNrg) - endif - - ! check that the canopy air space temperature is reasonable - if(ixVegNrg/=integerMissing)then - if(stateVec(ixVegNrg) > canopyTempMax) feasible=.false. - !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixVegNrg )', feasible, canopyTempMax, stateVec(ixVegNrg) - endif - - ! check canopy liquid water is not negative - if(ixVegHyd/=integerMissing)then - if(stateVec(ixVegHyd) < 0._rkind) feasible=.false. - !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVec( ixVegHyd )', feasible, 0._rkind, stateVec(ixVegHyd) - end if - - ! check snow temperature is below freezing - if(count(ixSnowOnlyNrg/=integerMissing)>0)then - if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. - !do iLayer=1,nSnow - ! if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVec( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVec( ixSnowOnlyNrg(iLayer) ) - !enddo - endif - - ! loop through non-missing hydrology state variables in the snow+soil domain - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) - - ! check the minimum and maximum water constraints - if(ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_liqLayer)then - - ! --> minimum - if (layerType(iLayer) == iname_soil) then - xMin = theta_res(iLayer-nSnow) - else - xMin = 0._rkind - endif - - ! --> maximum - select case( layerType(iLayer) ) - case(iname_snow); xMax = merge(iden_ice, 1._rkind - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) - case(iname_soil); xMax = merge(theta_sat(iLayer-nSnow), theta_sat(iLayer-nSnow) - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) - end select - - ! --> check - if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. - !if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax - - endif ! if water states - - end do ! loop through non-missing hydrology state variables in the snow+soil domain + if (.not.insideSUN) then + call checkFeas(& + ! input + stateVec, & ! intent(in): model state vector (mixed units) + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: feasibility + feasible, & ! intent(inout): flag to denote the feasibility of the solution + ! output: error control + err,cmessage) ! intent(out): error control ! early return for non-feasible solutions if(.not.feasible)then diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 0f892dcde..f1c959b52 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -16,30 +16,9 @@ module eval8summaWithPrime_module USE globalData,only: iJac1 ! first layer of the Jacobian to print USE globalData,only: iJac2 ! last layer of the Jacobian to print -! domain types -USE globalData,only:iname_veg ! named variables for vegetation -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil - -! named variables to describe the state variable type -USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space -USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy -USE globalData,only:iname_watCanopy ! named variable defining the mass of water on the vegetation canopy -USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers -USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers -USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers -USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers -USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers -USE globalData,only:model_decisions ! model decision structure - ! constants USE multiconst,only:& Tfreeze, & ! temperature at freezing (K) - LH_fus, & ! latent heat of fusion (J kg-1) - LH_vap, & ! latent heat of vaporization (J kg-1) - LH_sub, & ! latent heat of sublimation (J kg-1) - Cp_air, & ! specific heat of air (J kg-1 K-1) - iden_air, & ! intrinsic density of air (kg m-3) iden_ice, & ! intrinsic density of ice (kg m-3) iden_water ! intrinsic density of liquid water (kg m-3) @@ -79,13 +58,12 @@ module eval8summaWithPrime_module ! ********************************************************************************************************** subroutine eval8summaWithPrime(& ! input: model control - dt_cur, & ! intent(in): step size to last time solution dt, & ! intent(in): entire time step for drainage pond rate nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers nState, & ! intent(in): total number of state variables - insideIDA, & ! intent(in): flag to indicate if we are inside Sundials solver + insideSUN, & ! intent(in): flag to indicate if we are inside Sundials solver firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation @@ -146,6 +124,7 @@ subroutine eval8summaWithPrime(& ! -------------------------------------------------------------------------------------------------------------------------------- ! provide access to subroutines USE getVectorz_module, only:varExtract ! extract variables from the state vector + USE getVectorz_module, only:checkFeas ! check feasibility of state vector USE updateVarsWithPrime_module, only:updateVarsWithPrime ! update variables USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy USE computFlux_module, only:soilCmpresSundials ! compute soil compression @@ -160,13 +139,12 @@ subroutine eval8summaWithPrime(& ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control - real(rkind),intent(in) :: dt_cur ! step size to last time solution real(rkind),intent(in) :: dt ! entire time step for drainage pond rate integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers integer,intent(in) :: nState ! total number of state variables - logical(lgt),intent(in) :: insideIDA ! flag to indicate if we are inside Sundials solver + logical(lgt),intent(in) :: insideSUN ! flag to indicate if we are inside Sundials solver logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call logical(lgt),intent(inout) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation @@ -257,9 +235,6 @@ subroutine eval8summaWithPrime(& integer(i4b) :: iLayer ! index of model layer in the snow+soil domain integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine - integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) - real(rkind) :: xMin,xMax ! minimum and maximum values for water content - real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) character(LEN=256) :: cmessage ! error message of downwind routine real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil @@ -278,14 +253,12 @@ subroutine eval8summaWithPrime(& ! soil parameters theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) - ! canopy and layer depth + ! canopy and layer depth canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) ! model diagnostic variables from a previous solution scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) ! soil compression scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2 s-1) @@ -299,83 +272,25 @@ subroutine eval8summaWithPrime(& ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) ! indices - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable (nrg) - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable (nrg) - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(out): volumetric heat capacity of vegetation canopy mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & ! intent(out): heat capacity for snow and soil ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="eval8summaWithPrime/" - feasible=.true. ! check the feasibility of the solution only if not inside Sundials solver - ! NOTE: we will not print infeasibilities since it does not indicate a failure, just a need to iterate until maxiter - if (.not.insideIDA) then - ! check that the canopy air space temperature is reasonable - if(ixCasNrg/=integerMissing)then - if(stateVec(ixCasNrg) > canopyTempMax) feasible=.false. - if(stateVec(ixCasNrg) > canopyTempMax) message=trim(message)//'canopy air space temp high,' - !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixCasNrg )', feasible, canopyTempMax, stateVec(ixCasNrg) - endif - - ! check that the canopy temperature is reasonable - if(ixVegNrg/=integerMissing)then - if(stateVec(ixVegNrg) > canopyTempMax) feasible=.false. - if(stateVec(ixVegNrg) > canopyTempMax) message=trim(message)//'canopy temp high,' - !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixVegNrg )', feasible, canopyTempMax, stateVec(ixVegNrg) - endif - - ! check canopy liquid water is not negative - if(ixVegHyd/=integerMissing)then - if(stateVec(ixVegHyd) < 0._rkind) feasible=.false. - if(stateVec(ixVegHyd) < 0._rkind) message=trim(message)//'canopy water negative,' - !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVec( ixVegHyd )', feasible, 0._rkind, stateVec(ixVegHyd) - end if - - ! check snow temperature is below freezing - if(count(ixSnowOnlyNrg/=integerMissing)>0)then - if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. - if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) message=trim(message)//'snow temp above freezing,' - !do iLayer=1,nSnow - ! if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVec( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVec( ixSnowOnlyNrg(iLayer) ) - !enddo - endif - - ! loop through non-missing hydrology state variables in the snow+soil domain - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) - - ! check the minimum and maximum water constraints - if(ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_liqLayer)then - - ! --> minimum - if (layerType(iLayer) == iname_soil) then - xMin = theta_res(iLayer-nSnow) - else - xMin = 0._rkind - endif - - ! --> maximum - select case( layerType(iLayer) ) - case(iname_snow); xMax = merge(iden_ice, 1._rkind - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) - case(iname_soil); xMax = merge(theta_sat(iLayer-nSnow), theta_sat(iLayer-nSnow) - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) - end select - - ! --> check - if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. - if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) message=trim(message)//'layer water outside bounds,' - !if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax - - endif ! if water states - - end do ! loop through non-missing hydrology state variables in the snow+soil domain + feasible=.true. + if (.not.insideSUN) then + call checkFeas(& + ! input + stateVec, & ! intent(in): model state vector (mixed units) + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: feasibility + feasible, & ! intent(inout): flag to denote the feasibility of the solution + ! output: error control + err,cmessage) ! intent(out): error control ! early return for non-feasible solutions if(.not.feasible)then @@ -676,7 +591,6 @@ subroutine eval8summaWithPrime(& mLayerCmTrial = 0._qp endif ! needCm - ! save the number of flux calls per time step indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) + 1 @@ -749,7 +663,7 @@ subroutine eval8summaWithPrime(& scalarSoilCompress = sum(mLayerCompress(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water ! compute the residual vector - if (insideIDA)then + if (insideSUN)then dt1 = 1._qp ! always 1 for sundials since using Prime derivatives call computResidWithPrime(& @@ -833,7 +747,6 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user real(rkind), pointer :: rVec(:) logical(lgt) :: feasible integer(i4b) :: retval - real(c_double) :: stepsize_next(1) !======= Internals ============ ! get equations data from user-defined data @@ -844,16 +757,9 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user stateVecPrime(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_yp) rVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_r) - retval = FIDAGetCurrentStep(eqns_data%ida_mem, stepsize_next) - if (retval /= 0) then - print *, 'Error in FIDAGetCurrentStep, retval = ', retval, '; halting' - stop 1 - end if - ! compute the flux and the residual vector for a given state vector call eval8summaWithPrime(& ! input: model control - stepsize_next(1), & ! intent(in): current stepsize eqns_data%dt, & ! intent(in): data step eqns_data%nSnow, & ! intent(in): number of snow layers eqns_data%nSoil, & ! intent(in): number of soil layers diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index e47239c46..94649676d 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -93,6 +93,7 @@ module getVectorz_module public::popStateVec public::getScaling public::varExtract +public::checkFeas ! common variables real(rkind),parameter :: valueMissing=-9999._rkind ! missing value @@ -395,6 +396,126 @@ subroutine getScaling(& end subroutine getScaling +! ********************************************************************************************************** +! public subroutine checkFeas: check feasibility of the state vector +! ********************************************************************************************************** +subroutine checkFeas(& + ! input + stateVec, & ! intent(in): model state vector (mixed units) + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: feasibility + feasible, & ! intent(inout): flag to denote the feasibility of the solution + ! output: error control + err,message) ! intent(out): error control +! -------------------------------------------------------------------------------------------------------------------------------- +! -------------------------------------------------------------------------------------------------------------------------------- +implicit none +! input +real(rkind),intent(in) :: stateVec(:) ! model state vector (mixed units) +type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU +type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers +! output: feasibility +logical(lgt),intent(inout) :: feasible ! flag to denote the feasibility of the solution +! output: error control +integer(i4b),intent(out) :: err ! error code +character(*),intent(out) :: message ! error message +! -------------------------------------------------------------------------------------------------------------------------------- +! local variables +integer(i4b) :: iLayer ! index of layer within the snow+soil domain +real(rkind) :: xMin,xMax ! minimum and maximum values for water content +real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) +! -------------------------------------------------------------------------------------------------------------------------------- +! make association with variables in the data structures +associate(& + ! soil parameters + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) + ! model diagnostic variables from the previous solution + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + ! number of model layers, and layer type + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + ! indices defining model states and layers + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) + )! association with variables in the data structures + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message="checkFeas/" + + ! check the feasibility of the solution always with BE numrec but not inside Sundials solver + ! NOTE: we will not print infeasibilities since it does not indicate a failure, just a need to iterate until maxiter + feasible=.true. + if (.not.insideSUN) then + ! check that the canopy air space temperature is reasonable + if(ixCasNrg/=integerMissing)then + if(stateVec(ixCasNrg) > canopyTempMax) feasible=.false. + if(stateVec(ixCasNrg) > canopyTempMax) message=trim(message)//'canopy air space temp high,' + !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixCasNrg )', feasible, canopyTempMax, stateVec(ixCasNrg) + endif + + ! check that the canopy air space temperature is reasonable + if(ixVegNrg/=integerMissing)then + if(stateVec(ixVegNrg) > canopyTempMax) feasible=.false. + !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixVegNrg )', feasible, canopyTempMax, stateVec(ixVegNrg) + endif + + ! check canopy liquid water is not negative + if(ixVegHyd/=integerMissing)then + if(stateVec(ixVegHyd) < 0._rkind) feasible=.false. + !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVec( ixVegHyd )', feasible, 0._rkind, stateVec(ixVegHyd) + end if + + ! check snow temperature is below freezing + if(count(ixSnowOnlyNrg/=integerMissing)>0)then + if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. + !do iLayer=1,nSnow + ! if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVec( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVec( ixSnowOnlyNrg(iLayer) ) + !enddo + endif + + ! loop through non-missing hydrology state variables in the snow+soil domain + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) + + ! check the minimum and maximum water constraints + if(ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_liqLayer)then + + ! --> minimum + if (layerType(iLayer) == iname_soil) then + xMin = theta_res(iLayer-nSnow) + else + xMin = 0._rkind + endif + + ! --> maximum + select case( layerType(iLayer) ) + case(iname_snow); xMax = merge(iden_ice, 1._rkind - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) + case(iname_soil); xMax = merge(theta_sat(iLayer-nSnow), theta_sat(iLayer-nSnow) - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) + end select + + ! --> check + if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. + !if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax + + endif ! if water states + + end do ! loop through non-missing hydrology state variables in the snow+soil domain + + end associate ! end association to variables in the data structure +end subroutine checkFeas + + ! ********************************************************************************************************** ! public subroutine varExtract: extract variables from the state vector and compute diagnostic variables ! This routine does not initialize any of the variables diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index fc692e571..1435ee728 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -1,5 +1,22 @@ - - +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . module summaSolve4ida_module @@ -14,8 +31,6 @@ module summaSolve4ida_module ! access missing values USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing double precision number -USE globalData,only:quadMissing ! missing quadruple precision number ! access matrix information USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix @@ -23,36 +38,14 @@ module summaSolve4ida_module USE globalData,only: ku ! number of super-diagonal bands USE globalData,only: kl ! number of sub-diagonal bands -! domain types -USE globalData,only:iname_veg ! named variables for vegetation -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil - ! state variable type -USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space -USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy -USE globalData,only:iname_watCanopy ! named variable defining the mass of total water on the vegetation canopy -USE globalData,only:iname_liqCanopy ! named variable defining the mass of liquid water on the vegetation canopy -USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers -USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers -USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers -USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers -USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers USE globalData,only:model_decisions ! model decision structure ! global metadata USE globalData,only:flux_meta ! metadata on the model fluxes -USE globalData,only:diag_meta ! metadata on the model diagnostic variables -USE globalData,only:prog_meta ! metadata on the model prognostic variables -USE globalData,only:deriv_meta ! metadata on the model derivatives ! constants -USE multiconst,only:& - LH_fus, & ! latent heat of fusion (J K-1) - LH_sub, & ! latent heat of sublimation (J kg-1) - Tfreeze, & ! temperature at freezing (K) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water ! intrinsic density of liquid water (kg m-3) +USE multiconst,only: Tfreeze ! temperature at freezing (K) ! provide access to indices that define elements of the data structures USE var_lookup,only:iLookPROG ! named variables for structure elements @@ -89,7 +82,7 @@ module summaSolve4ida_module ! ------------------ subroutine summaSolve4ida( & dt, & ! intent(in): data time step - atol, & ! intent(in): absolute telerance + atol, & ! intent(in): absolute tolerance rtol, & ! intent(in): relative tolerance nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -120,7 +113,7 @@ subroutine summaSolve4ida( & mLayerCmpress_sum, & ! intent(inout): sum of compression of the soil matrix ! output ixSaturation, & ! intent(inout) index of the lowest saturated layer (NOTE: only computed on the first iteration) - idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step + idaSucceeds, & ! intent(out): flag to indicate if IDA successfully solved the problem in current data step tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt dt_out, & ! intent(out): time step sum for entire data window at termination of sundials stateVec, & ! intent(out): model state vector @@ -136,18 +129,15 @@ subroutine summaSolve4ida( & USE fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver USE fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix USE fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver - USE fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver USE fsundials_matrix_mod ! Fortran interface to generic SUNMatrix USE fsundials_nvector_mod ! Fortran interface to generic N_Vector USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver - USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver USE allocspace_module,only:allocLocal ! allocate local data structures USE eval8summaWithPrime_module,only:eval8summa4ida ! DAE/ODE functions USE eval8summaWithPrime_module,only:eval8summaWithPrime ! residual of DAE USE computJacobWithPrime_module,only:computJacob4ida ! system Jacobian USE tol4ida_module,only:computWeight4ida ! weight required for tolerances - USE var_derive_module,only:calcHeight ! height at layer interfaces and layer mid-point - + !======= Declarations ========= implicit none @@ -210,7 +200,7 @@ subroutine summaSolve4ida( & type(eqnsData), target :: eqns_data ! IDA type integer(i4b) :: retval, retvalr ! return value logical(lgt) :: feasible ! feasibility flag - real(qp) :: t0 ! staring time + real(qp) :: t0 ! starting time real(qp) :: dt_last(1) ! last time step real(qp) :: dt_diff ! difference from previous timestep integer(kind = 8) :: mu, lu ! in banded matrix mode in Sundials type @@ -235,8 +225,9 @@ subroutine summaSolve4ida( & ! initialize error control err=0; message="summaSolve4ida/" - nState = nStat + nState = nStat ! total number of state variables in Sundials type idaSucceeds = .true. + ! fill eqns_data which will be required later to call eval8summaWithPrime eqns_data%dt = dt eqns_data%nSnow = nSnow @@ -247,51 +238,31 @@ subroutine summaSolve4ida( & eqns_data%firstSubStep = firstSubStep eqns_data%computeVegFlux = computeVegFlux eqns_data%scalarSolution = scalarSolution - - allocate( eqns_data%atol(nState) ) - eqns_data%atol = atol - - allocate( eqns_data%rtol(nState) ) - eqns_data%rtol = rtol - - allocate( eqns_data%sMul(nState) ) - eqns_data%sMul = sMul - - allocate( eqns_data%dMat(nState) ) - eqns_data%dMat = dMat - - ! allocate space for the temporary prognostic variable structure - call allocLocal(prog_meta(:),eqns_data%prog_data,nSnow,nSoil,err,message) - if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif - eqns_data%prog_data = prog_data - - ! allocate space for the temporary diagnostic variable structure - call allocLocal(diag_meta(:),eqns_data%diag_data,nSnow,nSoil,err,message) - if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif - eqns_data%diag_data = diag_data - - ! allocate space for the temporary and previousflux variable structure - call allocLocal(flux_meta(:),eqns_data%flux_data,nSnow,nSoil,err,message) - if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif - eqns_data%flux_data = flux_data - call allocLocal(flux_meta(:),flux_prev,nSnow,nSoil,err,message) - if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif - flux_prev = eqns_data%flux_data - - ! allocate space for the derivative structure - call allocLocal(deriv_meta(:),eqns_data%deriv_data,nSnow,nSoil,err,message) - if(err/=0)then; err=20; message=trim(message)//trim(message); return; end if - eqns_data%deriv_data = deriv_data - eqns_data%lookup_data = lookup_data eqns_data%type_data = type_data eqns_data%attr_data = attr_data eqns_data%mpar_data = mpar_data eqns_data%forc_data = forc_data eqns_data%bvar_data = bvar_data + eqns_data%prog_data = prog_data eqns_data%indx_data = indx_data + eqns_data%diag_data = diag_data + eqns_data%flux_data = flux_data + eqns_data%deriv_data = deriv_data + eqns_data%ixSaturation = ixSaturation + + ! allocate space and fill + allocate( eqns_data%atol(nState) ); eqns_data%atol = atol + allocate( eqns_data%rtol(nState) ); eqns_data%rtol = rtol + allocate( eqns_data%sMul(nState) ); eqns_data%sMul = sMul + allocate( eqns_data%dMat(nState) ); eqns_data%dMat = dMat + + ! allocate space for the to save previous fluxes + call allocLocal(flux_meta(:),flux_prev,nSnow,nSoil,err,message) + if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif + flux_prev = eqns_data%flux_data - ! allocate space + ! allocate space for other variables if(model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel)then allocate(eqns_data%dBaseflow_dMatric(nSoil,nSoil),stat=err) else @@ -316,28 +287,45 @@ subroutine summaSolve4ida( & allocate( eqns_data%fluxVec(nState) ) allocate( eqns_data%resSink(nState) ) + ! need the following values for the first substep + eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) + eqns_data%scalarCanopyIcePrev = prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) + eqns_data%scalarCanopyLiqPrev = prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) + eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) + eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) + eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) + eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) + eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) + eqns_data%mLayerVolFracLiqPrev(:) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(:) + eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) + eqns_data%scalarAquiferStoragePrev = prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) + mLayerMatricHeadPrimePrev(:) = 0._rkind + dCompress_dPsiPrev(:) = 0._rkind + retval = FSUNContext_Create(c_null_ptr, sunctx) ! create serial vectors sunvec_y => FN_VMake_Serial(nState, stateVec, sunctx) if (.not. associated(sunvec_y)) then; err=20; message='summaSolve4ida: sunvec = NULL'; return; endif - sunvec_yp => FN_VMake_Serial(nState, stateVecPrime, sunctx) if (.not. associated(sunvec_yp)) then; err=20; message='summaSolve4ida: sunvec = NULL'; return; endif - ! Initialize solution vectors + ! initialize solution vectors call setInitialCondition(nState, stateVecInit, sunvec_y, sunvec_yp) - ! Create memory + ! create memory ida_mem = FIDACreate(sunctx) if (.not. c_associated(ida_mem)) then; err=20; message='summaSolve4ida: ida_mem = NULL'; return; endif ! Attach user data to memory - eqns_data%ida_mem = ida_mem retval = FIDASetUserData(ida_mem, c_loc(eqns_data)) if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetUserData'; return; endif - ! Initialize memory + ! Set solver parameters before calling FIDAInit + call setSolverParams(dt, nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)), ida_mem, retval) + if (retval /= 0) then; err=20; message='summaSolve4ida: error in setSolverParams'; return; endif + + ! Set the function IDA will use to advance the state t0 = 0._rkind retval = FIDAInit(ida_mem, c_funloc(eval8summa4ida), t0, sunvec_y, sunvec_yp) if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDAInit'; return; endif @@ -365,7 +353,7 @@ subroutine summaSolve4ida( & allocate( rootdir(nRoot) ) rootdir = 0 retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4ida)) - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif + if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDARootInit'; return; endif else ! will not use, allocate at something nRoot = 1 allocate( rootsfound(nRoot) ) @@ -399,6 +387,7 @@ subroutine summaSolve4ida( & end select ! form of matrix ! Attach the matrix and linear solver + ! For the nonlinear solver, IDA uses a Newton SUNNonlinearSolver-- it is not necessary to create and attach it retval = FIDASetLinearSolver(ida_mem, sunlinsol_LS, sunmat_A); if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetLinearSolver'; return; endif @@ -408,50 +397,19 @@ subroutine summaSolve4ida( & if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetJacFn'; return; endif endif - ! Create Newton SUNNonlinearSolver object - sunnonlin_NLS => FSUNNonlinSol_Newton(sunvec_y, sunctx) - if (.not. associated(sunnonlin_NLS)) then; err=20; message='summaSolve4ida: sunnonlinsol = NULL'; return; endif - - ! Attach the nonlinear solver - retval = FIDASetNonlinearSolver(ida_mem, sunnonlin_NLS) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetNonlinearSolver'; return; endif - ! Enforce the solver to stop at end of the time step retval = FIDASetStopTime(ida_mem, dt) if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetStopTime'; return; endif - ! Set solver parameters such as maximum order, number of iterations, ... - call setSolverParams(dt, nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)), ida_mem, retval) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in setSolverParams'; return; endif - ! Disable error messages and warnings if(offErrWarnMessage) then retval = FIDASetErrFile(ida_mem, c_null_ptr) retval = FIDASetNoInactiveRootWarn(ida_mem) endif - ! need the following values for the first substep - eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) - eqns_data%scalarCanopyIcePrev = prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) - eqns_data%scalarCanopyLiqPrev = prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) - eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) - eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) - eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) - eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) - eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) - eqns_data%mLayerVolFracLiqPrev(:) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(:) - eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) - eqns_data%scalarAquiferStoragePrev = prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) - mLayerMatricHeadPrimePrev(:) = 0._rkind - dCompress_dPsiPrev(:) = 0._rkind - eqns_data%ixSaturation = ixSaturation - tinystep = .false. - - !********************************************************************************** - !****************************** Main Solver *************************************** - !************************* loop on one_step mode ********************************** - !********************************************************************************** + !*********************** Main Solver * loop on one_step mode ***************************** + tinystep = .false. tret(1) = t0 ! initial time tretPrev = tret(1) do while(tret(1) < dt) @@ -460,7 +418,7 @@ subroutine summaSolve4ida( & if(detect_events .and. .not.tinystep)then call find_rootdir(eqns_data, rootdir) retval = FIDASetRootDirection(ida_mem, rootdir) - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDASetRootDirection'; return; endif + if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetRootDirection'; return; endif endif eqns_data%firstFluxCall = .false. @@ -470,10 +428,9 @@ subroutine summaSolve4ida( & if( retvalr < 0 )then idaSucceeds = .false. exit - endif + end if tooMuchMelt = .false. - feasible = .true. ! loop through non-missing energy state variables in the snow domain to see if need to merge do concurrent (i=1:nSnow,indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing) if (stateVec(indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)) > Tfreeze) tooMuchMelt = .true. !need to merge @@ -484,73 +441,24 @@ subroutine summaSolve4ida( & retval = FIDAGetLastStep(ida_mem, dt_last) dt_diff = tret(1) - tretPrev - ! compute the flux and the residual vector for a given state vector - call eval8summaWithPrime(& - ! input: model control - dt_diff, & ! intent(in): step size to last time solution - eqns_data%dt, & ! intent(in): total data step - eqns_data%nSnow, & ! intent(in): number of snow layers - eqns_data%nSoil, & ! intent(in): number of soil layers - eqns_data%nLayers, & ! intent(in): number of layers - eqns_data%nState, & ! intent(in): number of state variables in the current subset - .false., & ! intent(in): check for feasibility once outside Sundials loop - eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation - eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVec, & ! intent(in): model state vector - stateVecPrime, & ! intent(in): model state vector - eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - eqns_data%lookup_data, & ! intent(in): lookup data - eqns_data%type_data, & ! intent(in): type of vegetation and soil - eqns_data%attr_data, & ! intent(in): spatial attributes - eqns_data%mpar_data, & ! intent(in): model parameters - eqns_data%forc_data, & ! intent(in): model forcing data - eqns_data%bvar_data, & ! intent(in): average model variables for the entire basin - eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - eqns_data%indx_data, & ! intent(inout): index data - eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU - eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) - eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: here we need to pass some extra variables that do not get updated in in the Sundials loops (without operator splitting could cut prev values) - eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) - eqns_data%scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) - eqns_data%scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - eqns_data%scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) - eqns_data%scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) - eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): value for enthalpy of the vegetation canopy (J m-3) - eqns_data%mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - eqns_data%mLayerTempPrev, & ! intent(in): vector of layer temperature (K) - eqns_data%mLayerMatricHeadLiqTrial, & ! intent(out): trial value for liquid water matric potential (m) - eqns_data%mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) - eqns_data%mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) - eqns_data%mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - eqns_data%mLayerVolFracWatPrev, & ! intent(in): vector of volumetric total water content (-) - eqns_data%mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) - eqns_data%mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) - eqns_data%mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - eqns_data%mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) - eqns_data%scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) - eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) - eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) - eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) - eqns_data%mLayerMatricHeadPrime, & ! intent(out): derivative value for total water matric potential (m s-1) - ! input-output: baseflow - eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer - eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - ! output: flux and residual vectors - feasible, & ! intent(out): flag to denote the feasibility of the solution - eqns_data%fluxVec, & ! intent(out): flux vector - eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector - eqns_data%err,eqns_data%message) ! intent(out): error control + ! check the feasibility of the solution + feasible=.true. + call checkFeas(& + ! input + stateVec, & ! intent(in): model state vector (mixed units) + eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU + eqns_data%indx_data, & ! intent(in): indices defining model states and layers + ! output: feasibility + feasible, & ! intent(inout): flag to denote the feasibility of the solution + ! output: error control + err,cmessage) ! intent(out): error control + + ! early return for non-feasible solutions, will fail in current Sundials formulation + if(.not.feasible)then + eqns_data%fluxVec(:) = realMissing + message=trim(message)//'non-feasible' + return + end if ! sum of fluxes smoothed over the time step, average from instantaneous values do iVar=1,size(flux_meta) @@ -582,11 +490,11 @@ subroutine summaSolve4ida( & if (retvalr .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) ! rootsfound[i]= +1 indicates that gi is increasing, -1 g[i] decreasing, 0 no root !retval = FIDAGetRootInfo(ida_mem, rootsfound) - !if (retval < 0) then; err=20; message='solveByIDA: error in FIDAGetRootInfo'; return; endif + !if (retval < 0) then; err=20; message='summaSolve4ida: error in FIDAGetRootInfo'; return; endif !print '(a,f15.7,2x,17(i2,2x))', "time, rootsfound[] = ", tret(1), rootsfound ! Reininitialize solver for running after discontinuity and restart retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDAReInit'; return; endif + if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDAReInit'; return; endif if(dt_last(1) < 0.1_rkind)then ! don't keep calling if step is small (more accurate with this tiny but getting hung up) retval = FIDARootInit(ida_mem, 0, c_funloc(layerDisCont4ida)) tinystep = .true. @@ -594,7 +502,7 @@ subroutine summaSolve4ida( & retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4ida)) tinystep = .false. endif - if (retval /= 0) then; err=20; message='solveByIDA: error in FIDARootInit'; return; endif + if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDARootInit'; return; endif endif endif @@ -641,14 +549,18 @@ subroutine summaSolve4ida( & call FIDAFree(ida_mem) retval = FSUNNonlinSolFree(sunnonlin_NLS) + if(retval /= 0)then; err=20; message='summaSolve4ida: unable to free the nonlinear solver'; return; endif retval = FSUNLinSolFree(sunlinsol_LS) + if(retval /= 0)then; err=20; message='summaSolve4ida: unable to free the linear solver'; return; endif call FSUNMatDestroy(sunmat_A) call FN_VDestroy(sunvec_y) call FN_VDestroy(sunvec_yp) retval = FSUNContext_Free(sunctx) + if(retval /= 0)then; err=20; message='summaSolve4ida: unable to free the SUNDIALS context'; return; endif end subroutine summaSolve4ida + ! ---------------------------------------------------------------- ! SetInitialCondition: routine to initialize u and up vectors. ! ---------------------------------------------------------------- @@ -666,7 +578,7 @@ subroutine setInitialCondition(neq, y, sunvec_u, sunvec_up) type(N_Vector) :: sunvec_u ! solution N_Vector type(N_Vector) :: sunvec_up ! derivative N_Vector integer(c_long) :: neq - real(rkind) :: y(neq) + real(rkind) :: y(neq) ! pointers to data in SUNDIALS vectors real(c_double), pointer :: uu(:) @@ -682,7 +594,7 @@ subroutine setInitialCondition(neq, y, sunvec_u, sunvec_up) end subroutine setInitialCondition ! ---------------------------------------------------------------- -! setSolverParams: private routine to set parameters in ida solver +! setSolverParams: private routine to set parameters in IDA solver ! ---------------------------------------------------------------- subroutine setSolverParams(dt,nonlin_iter,ida_mem,retval) @@ -699,16 +611,15 @@ subroutine setSolverParams(dt,nonlin_iter,ida_mem,retval) type(c_ptr),intent(inout) :: ida_mem ! IDA memory integer(i4b),intent(out) :: retval ! return value - !======= Internals ============ - real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 integer,parameter :: max_order = 5 ! maximum BDF order, default = 5 + real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 integer(kind = 8),parameter :: max_step = 999999 ! maximum number of steps, default = 500 - real(qp),parameter :: h_init = 0 ! initial stepsize real(qp) :: h_max ! maximum stepsize, default = infinity - + real(qp),parameter :: h_init = 0 ! initial stepsize + ! Set the maximum BDF order retval = FIDASetMaxOrd(ida_mem, max_order) if (retval /= 0) return diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 1db69bc04..d2e833a39 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -533,7 +533,6 @@ subroutine trustRegionRefinement(doTrustRefinement,stateVecTrial,newtStepScaled, ! .. needed .. - ! -------------------------------------------------------------------------------------------------------- err=0; message='trustRegionRefinement/' @@ -568,8 +567,6 @@ subroutine trustRegionRefinement(doTrustRefinement,stateVecTrial,newtStepScaled, message=trim(message)//'routine not implemented yet' err=20; return - - end subroutine trustRegionRefinement @@ -691,17 +688,17 @@ subroutine getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,message) USE,intrinsic :: ieee_arithmetic,only:ieee_is_nan ! IEEE arithmetic (check NaN) implicit none ! dummies - real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector - real(rkind),intent(out) :: stateVecNew(:) ! new state vector - real(rkind),intent(out) :: xMin,xMax ! constraints + real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector + real(rkind),intent(out) :: stateVecNew(:) ! new state vector + real(rkind),intent(out) :: xMin,xMax ! constraints integer(i4b),intent(inout) :: err ! error code character(*),intent(out) :: message ! error message ! locals integer(i4b) :: iCheck ! check the model state variables integer(i4b),parameter :: nCheck=100 ! number of times to check the model state variables logical(lgt) :: feasible ! feasibility of the solution - real(rkind),parameter :: delX=1._rkind ! trial increment - real(rkind) :: xIncrement(nState) ! trial increment + real(rkind),parameter :: delX=1._rkind ! trial increment + real(rkind) :: xIncrement(nState) ! trial increment ! initialize err=0; message='getBrackets/' @@ -901,7 +898,6 @@ subroutine testBandMat(check,err,message) end subroutine testBandMat - ! ********************************************************************************************************* ! * internal subroutine eval8summa_wrapper: compute the right-hand-side vector ! ********************************************************************************************************* @@ -935,9 +931,10 @@ subroutine eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers nState, & ! intent(in): total number of state variables + .false., & ! intent(in): not inside Sundials solver firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - .false., & ! intent(in): flag to indicate if we are processing the first iteration in a splitting operation + .false., & ! intent(in): not processing the first iteration in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors @@ -953,8 +950,8 @@ subroutine eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err forc_data, & ! intent(in): model forcing data bvar_data, & ! intent(in): average model variables for the entire basin prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): index data ! input-output: data structures + indx_data, & ! intent(inout): index data diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables @@ -1106,25 +1103,25 @@ subroutine imposeConstraints(stateVecTrial,xInc,err,message) USE soil_utils_module,only:crit_soilT ! compute the critical temperature below which ice exists implicit none ! dummies - real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector - real(rkind),intent(inout) :: xInc(:) ! iteration increment + real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector + real(rkind),intent(inout) :: xInc(:) ! iteration increment integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! ----------------------------------------------------------------------------------------------------- ! temporary variables for model constraints - real(rkind) :: cInc ! constrained temperature increment (K) -- simplified bi-section - real(rkind) :: xIncFactor ! scaling factor for the iteration increment (-) + real(rkind) :: cInc ! constrained temperature increment (K) -- simplified bi-section + real(rkind) :: xIncFactor ! scaling factor for the iteration increment (-) integer(i4b) :: iMax(1) ! index of maximum temperature - real(rkind) :: scalarTemp ! temperature of an individual snow layer (K) - real(rkind) :: volFracLiq ! volumetric liquid water content of an individual snow layer (-) + real(rkind) :: scalarTemp ! temperature of an individual snow layer (K) + real(rkind) :: volFracLiq ! volumetric liquid water content of an individual snow layer (-) logical(lgt),dimension(nSnow) :: drainFlag ! flag to denote when drainage exceeds available capacity logical(lgt),dimension(nSoil) :: crosFlag ! flag to denote temperature crossing from unfrozen to frozen (or vice-versa) logical(lgt) :: crosTempVeg ! flag to denoote where temperature crosses the freezing point - real(rkind) :: xPsi00 ! matric head after applying the iteration increment (m) - real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) - real(rkind) :: critDiff ! temperature difference from critical (K) - real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical (K) - real(rkind),parameter :: zMaxTempIncrement=1._rkind ! maximum temperature increment (K) + real(rkind) :: xPsi00 ! matric head after applying the iteration increment (m) + real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) + real(rkind) :: critDiff ! temperature difference from critical (K) + real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical (K) + real(rkind),parameter :: zMaxTempIncrement=1._rkind ! maximum temperature increment (K) ! indices of model state variables integer(i4b) :: iState ! index of state within a specific variable type integer(i4b) :: ixNrg,ixLiq ! index of energy and mass state variables in full state vector diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 0304a1a36..1fd87b07c 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -440,7 +440,6 @@ subroutine systemSolv(& #ifdef SUNDIALS_ACTIVE call eval8summaWithPrime(& ! input: model control - dt_cur, & ! intent(in): current stepsize dt, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers @@ -518,6 +517,7 @@ subroutine systemSolv(& nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): number of layers nState, & ! intent(in): number of state variables in the current subset + .false., & ! intent(in): not inside Sundials solver firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation @@ -536,8 +536,8 @@ subroutine systemSolv(& forc_data, & ! intent(in): model forcing data bvar_data, & ! intent(in): average model variables for the entire basin prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): index data ! input-output: data structures + indx_data, & ! intent(inout): index data diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables @@ -570,7 +570,7 @@ subroutine systemSolv(& volEnthalpy = temp2ethpy(mLayerTemp(1),bulkDensity,snowfrz_scale) ! set flag and error codes for too much melt if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_cur)then - tooMuchMelt=.true. + tooMuchMelt = .true. message=trim(message)//'net flux in the top snow layer can melt all the snow in the top layer' err=-20; return ! negative error code to denote a warning endif @@ -659,7 +659,7 @@ subroutine systemSolv(& else if (tooMuchMelt) return !exit to start same step over after merge endif - niter = 0 ! iterations inside IDA solver + niter = 0 ! iterations are counted inside IDA solver ! ----- ! * update states... diff --git a/build/source/engine/tol4ida.f90 b/build/source/engine/tol4ida.f90 index 97ec45f83..ce630eb25 100644 --- a/build/source/engine/tol4ida.f90 +++ b/build/source/engine/tol4ida.f90 @@ -95,8 +95,8 @@ integer(c_int) function computWeight4ida(sunvec_y, sunvec_ewt, user_data) & ! pointers to data in SUNDIALS vectors type(eqnsData), pointer :: tol_data ! equations data - real(rkind), pointer :: stateVec(:) - real(rkind), pointer :: weightVec(:) + real(rkind), pointer :: stateVec(:) + real(rkind), pointer :: weightVec(:) integer(c_int) :: iState !======= Internals ============ @@ -104,7 +104,6 @@ integer(c_int) function computWeight4ida(sunvec_y, sunvec_ewt, user_data) & ! get equations data from user-defined data call c_f_pointer(user_data, tol_data) - ! get data arrays from SUNDIALS vectors stateVec(1:tol_data%nState) => FN_VGetArrayPointer(sunvec_y) weightVec(1:tol_data%nState) => FN_VGetArrayPointer(sunvec_ewt) From b7b0a0593b4fce822afc1184ed19782461f3b1ad Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 13 Jun 2023 23:50:39 +0900 Subject: [PATCH 0744/1472] put in kinsol --- build/source/engine/computJacob.f90 | 286 ++++++++++- build/source/engine/computJacobWithPrime.f90 | 2 +- build/source/engine/eval8summa.f90 | 114 ++++- build/source/engine/eval8summaWithPrime.f90 | 3 +- build/source/engine/summaSolve4ida.f90 | 6 +- build/source/engine/summaSolve4kinsol.f90 | 505 +++++++++++++++++++ 6 files changed, 900 insertions(+), 16 deletions(-) create mode 100644 build/source/engine/summaSolve4kinsol.f90 diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 93363125a..0faefc003 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -25,8 +25,8 @@ module computJacob_module ! derived types to define the data structures USE data_types,only:& - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength ! data vector with variable length dimension (rkind) ! named variables for structure elements USE var_lookup,only:iLookPROG ! named variables for structure elements @@ -66,6 +66,9 @@ module computJacob_module private public::computJacob +public::computJacob4kinsolSetup +public::computJacob4kinsol + contains ! ********************************************************************************************************** @@ -993,6 +996,285 @@ subroutine computJacob(& end subroutine computJacob +! ********************************************************************************************************** +! public subroutine computJacob4kinsolSetup: compute the Jacobian matrix +! ********************************************************************************************************** +subroutine computJacob4kinsolSetup(& + ! input: model control + dt, & ! intent(in): time step + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + ixMatrix, & ! intent(in): form of the Jacobian matrix + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + ! input: state vectors + stateVec, & ! intent(in): model state vector + sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + indx_data, & ! intent(inout): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input: baseflow + dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! output: flux and residual vectors + dMat, & ! intent(inout): diagonal of Jacobian Matrix + Jac, & ! intent(out): jacobian matrix + err,message) ! intent(out): error control +! -------------------------------------------------------------------------------------------------------------------------------- +! provide access to subroutines +USE getVectorz_module, only:varExtract ! extract variables from the state vector +USE updateVars_module, only:updateVars ! update prognostic variables +implicit none +! -------------------------------------------------------------------------------------------------------------------------------- +! -------------------------------------------------------------------------------------------------------------------------------- +! input: model control +real(rkind),intent(in) :: dt ! time step +integer(i4b),intent(in) :: nSnow ! number of snow layers +integer(i4b),intent(in) :: nSoil ! number of soil layers +integer(i4b),intent(in) :: nLayers ! total number of layers +integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) +logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation +! input: state vectors +real(rkind),intent(in) :: stateVec(:) ! model state vector +real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) +! input: data structures +type(model_options),intent(in) :: model_decisions(:) ! model decisions +type(var_dlength), intent(in) :: mpar_data ! model parameters +type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU +! output: data structures +type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers +type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU +type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables +! input-output: baseflow +real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) +! output: Jacobian +real(rkind), intent(inout) :: dMat(:) +real(rkind), intent(out) :: Jac(:,:) ! jacobian matrix +! output: error control +integer(i4b),intent(out) :: err ! error code +character(*),intent(out) :: message ! error message +! -------------------------------------------------------------------------------------------------------------------------------- +! local variables +! -------------------------------------------------------------------------------------------------------------------------------- +! state variables +real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) +real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) +real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) +real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial value for temperature of layers in the snow and soil domains (K) +real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial value for volumetric fraction of total water (-) +real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial value for total water matric potential (m) +real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) +real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) +! diagnostic variables +real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) +real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) +real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) +real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) +! other local variables +character(LEN=256) :: cmessage ! error message of downwind routine +real(rkind) :: dt1 + +! -------------------------------------------------------------------------------------------------------------------------------- +! association to variables in the data structures +! -------------------------------------------------------------------------------------------------------------------------------- +associate(& + ! model state variables + ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision & + ) ! association to variables in the data structures + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="computJacob4kinsolSetup/" + + ! initialize to state variable from the last update + ! should all be set to previous values if splits, but for now operator splitting is not hooked up + scalarCanairTempTrial = realMissing + scalarCanopyTempTrial = realMissing + scalarCanopyWatTrial = realMissing + scalarCanopyLiqTrial = realMissing + scalarCanopyIceTrial = realMissing + mLayerTempTrial = realMissing + mLayerVolFracWatTrial = realMissing + mLayerVolFracLiqTrial = realMissing + mLayerVolFracIceTrial = realMissing + mLayerMatricHeadTrial = realMissing + mLayerMatricHeadLiqTrial = realMissing + scalarAquiferStorageTrial = realMissing + + ! extract variables from the model state vector + call varExtract(& + ! input + stateVec, & ! intent(in): model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + ! output: variables for the aquifer + scalarAquiferStorageTrial,& ! intent(inout): trial value of storage of water in the aquifer (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! update diagnostic variables and derivatives + call updateVars(& + ! input + .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output: variables for the vegetation canopy + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! ----- + ! * compute the Jacobian matrix... + ! -------------------------------- + + ! compute the analytical Jacobian matrix + ! NOTE: The derivatives were computed in the previous call to computFlux + ! This occurred either at the call to eval8summaWithPrime at the start of sysSolveSundials + ! or in the call to eval8summaWithPrime in the previous iteration + dt1 = 1._qp + call computJacob(& + ! input: model control + dt1, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + (ixGroundwater==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow + ixMatrix, & ! intent(in): form of the Jacobian matrix + ! input: data structures + indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables + dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! input-output: Jacobian and its diagonal + dMat, & ! intent(inout): diagonal of the Jacobian matrix + Jac, & ! intent(out): Jacobian matrix + ! output: error control + err,cmessage) ! intent(out): error code and error message +if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + +! end association with the information in the data structures +end associate + +end subroutine computJacob4kinsolSetup + + +! ********************************************************************************************************** +! public function computJacob4kinsol: the interface to compute the Jacobian matrix dF/dy + c dF/dy' for IDA solver +! ********************************************************************************************************** +! Return values: +! 0 = success, +! 1 = recoverable error, +! -1 = non-recoverable error +! ---------------------------------------------------------------- +integer(c_int) function computJacob4kinsol(sunvec_y, sunvec_r, sunmat_J, & + user_data, sunvec_temp1, sunvec_temp2 & + ) result(ierr) bind(C, name='computJacob4kinsol') + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use fsundials_nvector_mod + use fsundials_matrix_mod + use fnvector_serial_mod + use fsunmatrix_band_mod + use fsunmatrix_dense_mod + use type4kinsol + + !======= Declarations ========= + implicit none + + ! calling variables + type(N_Vector) :: sunvec_y ! solution N_Vector + type(N_Vector) :: sunvec_r ! residual N_Vector + type(SUNMatrix) :: sunmat_J ! Jacobian SUNMatrix + type(c_ptr), value :: user_data ! user-defined data + type(N_Vector) :: sunvec_temp1 ! temporary N_Vector + type(N_Vector) :: sunvec_temp2 ! temporary N_Vector + + ! pointers to data in SUNDIALS vectors + real(c_double), pointer :: stateVec(:) ! state vector + real(c_double), pointer :: Jac(:,:) ! Jacobian matrix + character(len=256) :: message ! error message + type(data4kinsol), pointer :: eqns_data ! equations data + + !======= Internals ============ + + ! get equations data from user-defined data + call c_f_pointer(user_data,eqns_data) + + ! get data arrays from SUNDIALS vectors + stateVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_y) + if (eqns_data%ixMatrix==ixBandMatrix) Jac(1:nBands, 1:eqns_data%nState) => FSUNBandMatrix_Data(sunmat_J) + if (eqns_data%ixMatrix==ixFullMatrix) Jac(1:eqns_data%nState, 1:eqns_data%nState) => FSUNDenseMatrix_Data(sunmat_J) + + ! compute Jacobian matrix + call computJacob4kinsolSetup(& + ! input: model control + eqns_data%dt, & ! intent(in): data step + eqns_data%nSnow, & ! intent(in): number of snow layers + eqns_data%nSoil, & ! intent(in): number of soil layers + eqns_data%nLayers, & ! intent(in): number of layers + eqns_data%ixMatrix, & ! intent(in): type of matrix (dense or banded) + eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + ! input: state vectors + stateVec, & ! intent(in): model state vector + eqns_data%sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + eqns_data%mpar_data, & ! intent(in): model parameters + eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + eqns_data%indx_data, & ! intent(inout): index data + eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU + eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input: baseflow + eqns_data%dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! output + eqns_data%dMat, & ! intetn(inout): diagonal of the Jacobian matrix + Jac, & ! intent(out): Jacobian matrix + eqns_data%err,eqns_data%message) ! intent(out): error control + + if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif + if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif + + ! return success + ierr = 0 + return + +end function computJacob4kinsol + + ! ********************************************************************************************************** ! private function: get the off-diagonal index in the band-diagonal matrix ! ********************************************************************************************************** diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index c7703f290..2844b2cf5 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -1380,7 +1380,7 @@ integer(c_int) function computJacob4ida(t, cj, sunvec_y, sunvec_yp, sunvec_r, & real(rkind), pointer :: stateVec(:) ! state vector real(rkind), pointer :: stateVecPrime(:)! derivative of the state vector real(rkind), pointer :: Jac(:,:) ! Jacobian matrix - type(eqnsData), pointer :: eqns_data ! equations data + type(data4ida), pointer :: eqns_data ! equations data !======= Internals ============ diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index ad2764511..e7476fe88 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -68,6 +68,7 @@ module eval8summa_module implicit none private public::eval8summa +public::eval8summa4kinsol contains @@ -279,14 +280,15 @@ subroutine eval8summa(& ! output: error control err,cmessage) ! intent(out): error control - ! early return for non-feasible solutions - if(.not.feasible)then - fluxVec(:) = realMissing - resVec(:) = quadMissing - fEval = realMissing - message=trim(message)//'non-feasible' - return - endif + ! early return for non-feasible solutions + if(.not.feasible)then + fluxVec(:) = realMissing + resVec(:) = quadMissing + fEval = realMissing + message=trim(message)//'non-feasible' + return + end if + end if ! ( feasibility check ) ! get the start and end indices for the soil compression calculations if(scalarSolution)then @@ -649,4 +651,100 @@ subroutine eval8summa(& end subroutine eval8summa + +! ********************************************************************************************************** +! public function eval8summa4kinsol: compute the residual vector F(t,y) required for IDA solver +! ********************************************************************************************************** +! Return values: +! 0 = success, +! 1 = recoverable error, +! -1 = non-recoverable error +! ---------------------------------------------------------------- +integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & + result(ierr) bind(C,name='eval8summa4kinsol') + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use fsundials_nvector_mod + use fnvector_serial_mod + use type4kinsol + + !======= Declarations ========= + implicit none + + ! calling variables + type(N_Vector) :: sunvec_y ! solution N_Vector y + type(N_Vector) :: sunvec_r ! residual N_Vector F(t,y) + type(c_ptr), value :: user_data ! user-defined data + + ! pointers to data in SUNDIALS vectors + type(data4kinsol), pointer :: eqns_data ! equations data + real(rkind), pointer :: stateVec(:) + real(rkind), pointer :: rVec(:) + logical(lgt) :: feasible + real(rkind) :: fNew ! function values, not needed here + !======= Internals ============ + + ! get equations data from user-defined data + call c_f_pointer(user_data, eqns_data) + + ! get data arrays from SUNDIALS vectors + stateVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_y) + rVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_r) + + ! compute the flux and the residual vector for a given state vector + call eval8summa(& + ! input: model control + eqns_data%dt_cur, & ! intent(in): current stepsize + eqns_data%dt, & ! intent(in): data step + eqns_data%nSnow, & ! intent(in): number of snow layers + eqns_data%nSoil, & ! intent(in): number of soil layers + eqns_data%nLayers, & ! intent(in): number of layers + eqns_data%nState, & ! intent(in): number of state variables in the current subset + .true., & ! intent(in): inside Sundials solver + eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation + eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVec, & ! intent(in): model state vector + eqns_data%fScale, & ! intent(in): function scaling vector + eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + eqns_data%lookup_data, & ! intent(in): lookup data + eqns_data%type_data, & ! intent(in): type of vegetation and soil + eqns_data%attr_data, & ! intent(in): spatial attributes + eqns_data%mpar_data, & ! intent(in): model parameters + eqns_data%forc_data, & ! intent(in): model forcing data + eqns_data%bvar_data, & ! intent(in): average model variables for the entire basin + eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + eqns_data%indx_data, & ! intent(inout): index data + eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU + eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) + eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer + eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + ! output: flux and residual vectors + feasible, & ! intent(out): flag to denote the feasibility of the solution + eqns_data%fluxVec, & ! intent(out): flux vector + eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec, & ! intent(out): residual vector + fNew, & ! intent(out): new function evaluation + eqns_data%err,eqns_data%message) ! intent(out): error control + + if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif + if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif + if(.not.feasible)then; eqns_data%message=trim(eqns_data%message)//'state vector not feasible'; ierr = 1; return; endif + + ! return success + ierr = 0 + return + +end function eval8summa4kinsol + + end module eval8summa_module diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index f1c959b52..cd1956d95 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -299,7 +299,6 @@ subroutine eval8summaWithPrime(& message=trim(message)//'non-feasible' return end if - end if ! ( feasibility check ) ! get the start and end indices for the soil compression calculations @@ -741,7 +740,7 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user type(c_ptr), value :: user_data ! user-defined data ! pointers to data in SUNDIALS vectors - type(eqnsData), pointer :: eqns_data ! equations data + type(data4ida), pointer :: eqns_data ! equations data real(rkind), pointer :: stateVec(:) real(rkind), pointer :: stateVecPrime(:) real(rkind), pointer :: rVec(:) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 1435ee728..7df95a5b4 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -197,7 +197,7 @@ subroutine summaSolve4ida( & type(SUNNonLinearSolver), pointer :: sunnonlin_NLS ! sundials nonlinear solver type(c_ptr) :: ida_mem ! IDA memory type(c_ptr) :: sunctx ! SUNDIALS simulation context - type(eqnsData), target :: eqns_data ! IDA type + type(data4ida), target :: eqns_data ! IDA type integer(i4b) :: retval, retvalr ! return value logical(lgt) :: feasible ! feasibility flag real(qp) :: t0 ! starting time @@ -676,7 +676,7 @@ subroutine find_rootdir(eqns_data,rootdir) implicit none ! calling variables - type(eqnsData),intent(in) :: eqns_data ! equations data + type(data4ida),intent(in) :: eqns_data ! equations data integer(i4b),intent(inout) :: rootdir(:) ! root function directions to search ! local variables @@ -776,7 +776,7 @@ integer(c_int) function layerDisCont4ida(t, sunvec_u, sunvec_up, gout, user_data ! pointers to data in SUNDIALS vectors real(c_double), pointer :: uu(:) - type(eqnsData), pointer :: eqns_data ! equations data + type(data4ida), pointer :: eqns_data ! equations data !======= Internals ============ ! get equations data from user-defined data diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 new file mode 100644 index 000000000..36350f8d5 --- /dev/null +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -0,0 +1,505 @@ +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . + +module summaSolve4kinsol_module + + !======= Inclusions =========== +USE, intrinsic :: iso_c_binding +USE nrtype +USE type4kinsol + +! access the global print flag +USE globalData,only:globalPrintFlag + +! access missing values +USE globalData,only:integerMissing ! missing integer + +! access matrix information +USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix +USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix +USE globalData,only: ku ! number of super-diagonal bands +USE globalData,only: kl ! number of sub-diagonal bands + +! state variable type +USE globalData,only:model_decisions ! model decision structure + +! global metadata +USE globalData,only:flux_meta ! metadata on the model fluxes + +! constants +USE multiconst,only: Tfreeze ! temperature at freezing (K) + +! provide access to indices that define elements of the data structures +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure +USE var_lookup,only:iLookDERIV ! named variables for structure elements +USE var_lookup,only:iLookFLUX ! named variables for structure elements +USE var_lookup,only:iLookPARAM ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements + +! provide access to the derived types to define the data structures +USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup ! data vector + +! look-up values for the choice of groundwater parameterization +USE mDecisions_module,only: qbaseTopmodel ! TOPMODEL-ish baseflow parameterization + +! privacy + implicit none + private::setInitialCondition + private::setSolverParams + public::summaSolve4kinsol + +contains + +!------------------- +! * public subroutine summaSolve4kinsol: solve F(y) = 0 by KINSOL (y is the state vector) +! ------------------ +subroutine summaSolve4kinsol(& + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): data time step + fScale, & ! intent(in): characteristic scale of the function evaluations (mixed units) + xScale, & ! intent(in): characteristic scale of the state vector (mixed units) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + nStat, & ! intent(in): total number of state variables + ixMatrix, & ! intent(in): type of matrix (dense or banded) + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVecInit, & ! intent(in): initial state vector + sMul, & ! intent(inout): state vector multiplier (USEd in the residual calculations) + dMat, & ! intent(inout): diagonal of the Jacobian matrix (excludes fluxes) + ! input: data structures + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + indx_data, & ! intent(in): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output + ixSaturation, & ! intent(inout) index of the lowest saturated layer (NOTE: only computed on the first iteration) + kinsolSucceeds, & ! intent(out): flag to indicate if KINSOL successfully solved the problem in current data step + tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt + stateVec, & ! intent(out): model state vector + err,message & ! intent(out): error control + ) + + !======= Inclusions =========== + + USE fkinsol_mod ! Fortran interface to KINSOL + USE fsundials_context_mod ! Fortran interface to SUNContext + USE fnvector_serial_mod ! Fortran interface to serial N_Vector + USE fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix + USE fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver + USE fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix + USE fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver + USE fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver + USE fsundials_matrix_mod ! Fortran interface to generic SUNMatrix + USE fsundials_nvector_mod ! Fortran interface to generic N_Vector + USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver + USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver + USE allocspace_module,only:allocLocal ! allocate local data structures + USE eval8summa_module,only:eval8summa4kinsol ! DAE/ODE functions + USE eval8summa_module,only:eval8summa ! residual of DAE + USE computJacob_module,only:computJacob4kinsol ! system Jacobian + !USE tol4kinsol_module,only:computWeight4kinsol ! weight required for tolerances + + !======= Declarations ========= + implicit none + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! calling variables + ! -------------------------------------------------------------------------------------------------------------------------------- + ! input: model control + real(rkind),intent(in) :: dt_cur ! current stepsize + real(rkind),intent(in) :: dt ! data time step + real(rkind),intent(inout) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) + real(rkind),intent(inout) :: xScale(nState) ! characteristic scale of the state vector (mixed units) + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + integer(i4b),intent(in) :: nStat ! total number of state variables + integer(i4b),intent(in) :: ixMatrix ! form of matrix (dense or banded) + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + ! input: state vectors + real(rkind),intent(in) :: stateVecInit(:) ! model state vector + real(qp),intent(in) :: sMul(:) ! state vector multiplier (used in the residual calculations) + real(rkind), intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix (excludes fluxes) + ! input: data structures + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_i), intent(in) :: type_data ! type of vegetation and soil + type(var_d), intent(in) :: attr_data ! spatial attributes + type(var_dlength), intent(in) :: mpar_data ! model parameters + type(var_d), intent(in) :: forc_data ! model forcing data + type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin + type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_ilength), intent(in) :: indx_data ! indices defining model states and layers + ! input-output: data structures + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + ! output: state vectors + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer + real(rkind),intent(inout) :: stateVec(:) ! model state vector (y) + logical(lgt),intent(out) :: kinsolSucceeds ! flag to indicate if KINSOL is successful + logical(lgt),intent(inout) :: tooMuchMelt ! flag to denote that there was too much melt + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + ! -------------------------------------------------------------------------------------------------------------------------------- + type(N_Vector), pointer :: sunvec_y ! sundials solution vector + type(SUNMatrix), pointer :: sunmat_A ! sundials matrix + type(N_Vector), pointer :: sunvec_fscale ! vector containing diagonal elements of function scaling matrix + type(N_Vector), pointer :: sunvec_xscale ! vector containing diagonal elements of state scaling matrix + type(SUNLinearSolver), pointer :: sunlinsol_LS ! sundials linear solver + type(c_ptr) :: kinsol_mem ! KINSOL memory + type(c_ptr) :: sunctx ! SUNDIALS simulation context + type(data4kinsol), target :: eqns_data ! KINSOL type + integer(i4b) :: retval, retvalr ! return value + logical(lgt) :: feasible ! feasibility flag + integer(kind = 8) :: mu, lu ! in banded matrix mode in Sundials type + integer(c_long) :: nState ! total number of state variables in Sundials type + real(rkind) :: rVec(nStat) ! residual vector + integer(i4b) :: iVar, i ! indices + type(var_dlength) :: flux_prev ! previous model fluxes for a local HRU + real(rkind),allocatable :: mLayerMatricHeadPrimePrev(:) ! previous derivative value for total water matric potential (m s-1) + real(rkind),allocatable :: dCompress_dPsiPrev(:) ! previous derivative value soil compression + logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn KINSOL warnings off, default true + logical(lgt),parameter :: use_fdJac = .false. ! flag to use finite difference Jacobian, default false + + + + + ! local variables + integer(c_long) :: mset + integer(c_long) :: maxIter = 200 + + ! ----------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message="summaSolve4kinsol/" + + nState = nStat ! total number of state variables in Sundials type + kinsolSucceeds = .true. + + ! fill eqns_data which will be required later to call eval8summa + eqns_data%dt_cur = dt_cur + eqns_data%dt = dt + eqns_data%nSnow = nSnow + eqns_data%nSoil = nSoil + eqns_data%nLayers = nLayers + eqns_data%nState = nState + eqns_data%ixMatrix = ixMatrix + eqns_data%firstSubStep = firstSubStep + eqns_data%computeVegFlux = computeVegFlux + eqns_data%scalarSolution = scalarSolution + eqns_data%deriv_data = deriv_data + eqns_data%lookup_data = lookup_data + eqns_data%type_data = type_data + eqns_data%attr_data = attr_data + eqns_data%mpar_data = mpar_data + eqns_data%forc_data = forc_data + eqns_data%bvar_data = bvar_data + eqns_data%prog_data = prog_data + eqns_data%indx_data = indx_data + eqns_data%diag_data = diag_data + eqns_data%flux_data = flux_data + eqns_data%deriv_data = deriv_data + eqns_data%ixSaturation = ixSaturation + + ! allocate space and fill + allocate( eqns_data%fScale(nState) ); eqns_data%fScale = fScale + allocate( eqns_data%xScale(nState) ); eqns_data%xScale = xScale + allocate( eqns_data%sMul(nState) ); eqns_data%sMul = sMul + allocate( eqns_data%dMat(nState) ); eqns_data%dMat = dMat + + ! allocate space for the to save previous fluxes + call allocLocal(flux_meta(:),flux_prev,nSnow,nSoil,err,message) + if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif + flux_prev = eqns_data%flux_data + + ! allocate space for other variables + if(model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel)then + allocate(eqns_data%dBaseflow_dMatric(nSoil,nSoil),stat=err) + else + allocate(eqns_data%dBaseflow_dMatric(0,0),stat=err) + end if + allocate( eqns_data%mLayerMatricHeadLiqTrial(nSoil) ) + allocate( eqns_data%mLayerMatricHeadTrial(nSoil) ) + allocate( eqns_data%mLayerMatricHeadPrev(nSoil) ) + allocate( eqns_data%mLayerVolFracWatTrial(nLayers) ) + allocate( eqns_data%mLayerVolFracWatPrev(nLayers) ) + allocate( eqns_data%mLayerTempTrial(nLayers) ) + allocate( eqns_data%mLayerTempPrev(nLayers) ) + allocate( eqns_data%mLayerVolFracIceTrial(nLayers) ) + allocate( eqns_data%mLayerVolFracIcePrev(nLayers) ) + allocate( eqns_data%mLayerVolFracLiqTrial(nLayers) ) + allocate( eqns_data%mLayerVolFracLiqPrev(nLayers) ) + allocate( eqns_data%mLayerEnthalpyTrial(nLayers) ) + allocate( eqns_data%mLayerEnthalpyPrev(nLayers) ) + allocate( eqns_data%mLayerMatricHeadPrime(nSoil) ) + allocate( mLayerMatricHeadPrimePrev(nSoil) ) + allocate( dCompress_dPsiPrev(nSoil) ) + allocate( eqns_data%fluxVec(nState) ) + allocate( eqns_data%resSink(nState) ) + + retval = FSUNContext_Create(c_null_ptr, sunctx) + + ! create serial vectors + sunvec_y => FN_VMake_Serial(nState, stateVec, sunctx) + if (.not. associated(sunvec_y)) then; err=20; message='summaSolve4kinsol: sunvec = NULL'; return; endif + + ! create the scaling vectors + sunvec_fscale => FN_VMake_Serial(nState, fscale, sunctx) + if (.not. associated(sunvec_fscale)) then; err=20; message='summaSolve4kinsol: sunvec = NULL'; return; endif + sunvec_xscale => FN_VMake_Serial(nState, xscale, sunctx) + if (.not. associated(sunvec_xscale)) then; err=20; message='summaSolve4kinsol: sunvec = NULL'; return; endif + + ! initialize solution vectors + call setInitialCondition(nState, stateVecInit, sunvec_y) + + ! create memory + kinsol_mem = FKINCreate(sunctx) + if (.not. c_associated(kinsol_mem)) then; err=20; message='summaSolve4kinsol: kinsol_mem = NULL'; return; endif + + ! Attach user data to memory + retval = FKINSetUserData(kinsol_mem, c_loc(eqns_data)) + if (retval /= 0) then; err=20; message='summaSolve4kinsol: error in FKINSetUserData'; return; endif + + ! Set solver parameters before calling FKINInit + call setSolverParams(nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)), kinsol_mem, retval) + if (retval /= 0) then; err=20; message='summaSolve4kinsol: error in setSolverParams'; return; endif + + ! Set the function Kinsol will use to advance the state + retval = FKINInit(kinsol_mem, c_funloc(eval8summa4kinsol), sunvec_y) + if (retval /= 0) then; err=20; message='summaSolve4kinsol: error in FKINInit'; return; endif + + ! define the form of the matrix + select case(ixMatrix) + case(ixBandMatrix) + mu = ku; lu = kl; + ! Create banded SUNMatrix for use in linear solves + sunmat_A => FSUNBandMatrix(nState, mu, lu, sunctx) + if (.not. associated(sunmat_A)) then; err=20; message='summaSolve4kinsol: sunmat = NULL'; return; endif + + ! Create banded SUNLinearSolver object + sunlinsol_LS => FSUNLinSol_Band(sunvec_y, sunmat_A, sunctx) + if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolve4kinsol: sunlinsol = NULL'; return; endif + + case(ixFullMatrix) + ! Create dense SUNMatrix for use in linear solves + sunmat_A => FSUNDenseMatrix(nState, nState, sunctx) + if (.not. associated(sunmat_A)) then; err=20; message='summaSolve4kinsol: sunmat = NULL'; return; endif + + ! Create dense SUNLinearSolver object + sunlinsol_LS => FSUNLinSol_Dense(sunvec_y, sunmat_A, sunctx) + if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolve4kinsol: sunlinsol = NULL'; return; endif + + ! check + case default; err=20; message='summaSolve4kinsol: error in type of matrix'; return + + end select ! form of matrix + + ! Attach the matrix and linear solver + retval = FKINSetLinearSolver(kinsol_mem, sunlinsol_LS, sunmat_A); + if (retval /= 0) then; err=20; message='summaSolve4kinsol: error in FKINSetLinearSolver'; return; endif + + ! Set the user-supplied Jacobian routine + if(.not.use_fdJac)then + retval = FKINSetJacFn(kinsol_mem, c_funloc(computJacob4kinsol)) + if (retval /= 0) then; err=20; message='summaSolve4kinsol: error in FKINSetJacFn'; return; endif + endif + + ! Disable error messages and warnings + if(offErrWarnMessage) then + retval = FKINSetErrFile(kinsol_mem, c_null_ptr) + endif + + !*********************** Main Solver * loop on one_step mode ***************************** + + ! Call KINSol to solve problem with choice of solver, linesearch or Picard + !retval = FKINSol(kinsol_mem, sunvec_y, KIN_LINESEARCH, sunvec_xscale, sunvec_fscale) + retval = FKINSol(kinsol_mem, sunvec_y, KIN_PICARD, sunvec_xscale, sunvec_fscale) + if( retval < 0 )then + kinsolSucceeds = .false. + exit + end if + + ! check the feasibility of the solution + feasible=.true. + call checkFeas(& + ! input + stateVec, & ! intent(in): model state vector (mixed units) + eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU + eqns_data%indx_data, & ! intent(in): indices defining model states and layers + ! output: feasibility + feasible, & ! intent(inout): flag to denote the feasibility of the solution + ! output: error control + err,cmessage) ! intent(out): error control + + ! early return for non-feasible solutions, will fail in current Sundials formulation + if(.not.feasible)then + eqns_data%fluxVec(:) = realMissing + message=trim(message)//'non-feasible' + return + end if + + !****************************** End of Main Solver *************************************** + + err = eqns_data%err + message = eqns_data%message + if( .not. feasible) kinsolSucceeds = .false. + + if(kinsolSucceeds)then + ! copy to output data + diag_data = eqns_data%diag_data + flux_data = eqns_data%flux_data + deriv_data = eqns_data%deriv_data + ixSaturation = eqns_data%ixSaturation + endif + + ! free memory + deallocate( eqns_data%fScale ) + deallocate( eqns_data%xScale ) + deallocate( eqns_data%sMul ) + deallocate( eqns_data%dMat ) + deallocate( eqns_data%dBaseflow_dMatric ) + deallocate( eqns_data%fluxVec ) + deallocate( eqns_data%resSink ) + + + call FKINFree(kinsol_mem) + retval = FSUNLinSolFree(sunlinsol_LS) + if(retval /= 0)then; err=20; message='summaSolve4kinsol: unable to free the linear solver'; return; endif + call FSUNMatDestroy(sunmat_A) + call FN_VDestroy(sunvec_y) + call FN_VDestroy(sunvec_xscale) + call FN_VDestroy(sunvec_fscale) + retval = FSUNContext_Free(sunctx) + if(retval /= 0)then; err=20; message='summaSolve4kinsol: unable to free the SUNDIALS context'; return; endif + +end subroutine summaSolve4kinsol + + +! ---------------------------------------------------------------- +! SetInitialCondition: routine to initialize u vector. +! ---------------------------------------------------------------- + subroutine setInitialCondition(neq, y, sunvec_u, sunvec_up) + + !======= Inclusions =========== + USE, intrinsic :: iso_c_binding + USE fsundials_nvector_mod + USE fnvector_serial_mod + + !======= Declarations ========= + implicit none + + ! calling variables + type(N_Vector) :: sunvec_u ! solution N_Vector + integer(c_long) :: neq + real(rkind) :: y(neq) + + ! pointers to data in SUNDIALS vectors + real(c_double), pointer :: uu(:) + + ! get data arrays from SUNDIALS vectors + uu(1:neq) => FN_VGetArrayPointer(sunvec_u) + + uu = y + + end subroutine setInitialCondition + + ! ---------------------------------------------------------------- + ! setSolverParams: private routine to set parameters in KINSOL solver + ! ---------------------------------------------------------------- + subroutine setSolverParams(lin_iter,kinsol_mem,retval) + + !======= Inclusions =========== + USE, intrinsic :: iso_c_binding + USE fkinsol_mod ! Fortran interface to KINSOL + + !======= Declarations ========= + implicit none + + ! calling variables + integer,intent(in) :: nonlin_iter ! maximum number of nonlinear iterations, default = 200, set in parameters + type(c_ptr),intent(inout) :: kinsol_mem ! KINSOL memory + integer(i4b),intent(out) :: retval ! return value + + !======= Internals ============ + integer,parameter :: mset = 1 ! maximum number of times the solver is called without Jacobian update, pass 0 to give default of 10 times + integer,parameter :: msubset = 1 ! maximum number of nonlinear iterations between checks by the residual monitoring algorithm, default=5 + integer,parameter :: maa = 0 ! maximum number of prior residuals to use acceleration, default = 0 + integer,parameter :: beta_fail = 50 ! maximum number of beta condition failures, default = 10 + real(qp),parameter :: fnormtol = 0.0 ! stopping tolerance on the scaled maximum norm of the system function, pass 0 to give default of unit_roundoff**(1/3) + real(qp),parameter :: scsteptol = 0.0 ! stopping tolerance on the minimum scaled step length, pass 0 to give default of unit_roundoff**(2/3) + + ! Set maximum number of times the linear solver is called without a Jacobian update + retval = FKINSetMaxSetupCalls(kinsol_mem, mset) + if (retval /= 0) return + + ! Every msubset iterations, test if a Jacobian evaluation is necessary + ierr = FKINSetMaxSubSetupCalls(kinsol_mem, msubset) + if (retval /= 0) return + + ! Set maximum number of iterations + retval = FKINSetNumMaxIters(kinsol_mem, nonlin_iter) + if (retval /= 0) return + + ! Set maximum number of prior residuals to use for Anderson acceleration + ! ONLY in conjunction with Picard or fixed-point iteration + retval = FKINSetMAA(kinsol_mem, maa); + if (retval /= 0) return + + ! Set maximum number of beta condition failures in the linesearch + retval = FKINSetMaxBetaFails(kinsol_mem, beta_fail) + if (retval /= 0) return + + ! Set tolerances for stopping criteria: scaled maximum norm of the system function + retval = FKINSetFuncNormTol(kinsol_mem, fnormtol) + if (retval /= 0) return + +\ ! Set stopping tolerance on the scaled maximum norm of the system function + retval = FKINSetScaledStepTol(kinsol_mem, scsteptol) + if (retval /= 0) return + + + end subroutine setSolverParams + + \ No newline at end of file From d4eeb42f2012fa7b64c47942dc03970e235f83d0 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 13 Jun 2023 23:51:00 +0900 Subject: [PATCH 0745/1472] types --- build/source/dshare/type4ida.f90 | 6 +++--- build/source/dshare/type4kinsol.f90 | 7 ++----- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/build/source/dshare/type4ida.f90 b/build/source/dshare/type4ida.f90 index 8fe05f04a..8d8623c58 100644 --- a/build/source/dshare/type4ida.f90 +++ b/build/source/dshare/type4ida.f90 @@ -14,7 +14,7 @@ module type4ida implicit none -type eqnsData +type data4ida type(c_ptr) :: ida_mem ! IDA memory real(rkind) :: dt ! data step integer(i4b) :: nSnow ! number of snow layers @@ -69,10 +69,10 @@ module type4ida real(rkind), allocatable :: mLayerEnthalpyPrev(:) ! enthalpy of snow and soil (J m-3) at previous step real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! derivative value for total water matric potential (m s-1) real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + integer(i4b) :: ixSaturation ! index of the lowest saturated layer integer(i4b) :: err ! error code character(len = 50) :: message ! error message -end type eqnsData +end type data4ida end module type4ida diff --git a/build/source/dshare/type4kinsol.f90 b/build/source/dshare/type4kinsol.f90 index 19ef5e33d..dbcd145c2 100644 --- a/build/source/dshare/type4kinsol.f90 +++ b/build/source/dshare/type4kinsol.f90 @@ -12,7 +12,7 @@ module type4kinsol model_options ! defines the model decisions implicit none -type eqnsData +type data4kinsol type(c_ptr) :: kinsol_mem ! KINSOL memory real(rkind) :: dt_cur ! current stepsize real(rkind) :: dt ! data step @@ -37,7 +37,6 @@ module type4kinsol type(var_dlength) :: diag_data ! diagnostic variables for a local HRU type(var_dlength) :: flux_data ! model fluxes for a local HRU type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(rkind), allocatable :: stateVecPrev(:) ! state vector from the previous iteration to help with infeasibility real(qp), allocatable :: sMul(:) ! state vector multiplier (used in the residual calculations) real(rkind), allocatable :: dMat(:) ! diagonal of the Jacobian matrix real(rkind), allocatable :: fluxVec(:) ! flux vector @@ -48,9 +47,7 @@ module type4kinsol integer(i4b) :: ixSaturation ! index of the lowest saturated layer integer(i4b) :: err ! error code character(len = 50) :: message ! error message - real(rkind) :: fEval ! function evaluation - logical(lgt) :: firstStateiteration ! flag to denote if we computed an iteration so we know to save the state -end type eqnsData +end type data4kinsol end module type4kinsol \ No newline at end of file From 31662daaf585d1487a9c6b741064e3e10dae7fc0 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sun, 18 Jun 2023 23:04:51 +0900 Subject: [PATCH 0746/1472] changing some comments and a few syntax bugs --- build/source/engine/computJacob.f90 | 15 +-- build/source/engine/computJacobWithPrime.f90 | 3 +- build/source/engine/eval8summa.f90 | 12 +- build/source/engine/eval8summaWithPrime.f90 | 6 +- build/source/engine/getVectorz.f90 | 111 +++++++++---------- build/source/engine/mDecisions.f90 | 7 ++ build/source/engine/matrixOper.f90 | 4 +- build/source/engine/summaSolve4ida.f90 | 3 +- build/source/engine/summaSolve4kinsol.f90 | 10 +- build/source/engine/summaSolve4numrec.f90 | 10 +- build/source/engine/systemSolv.f90 | 34 +++--- build/source/engine/varSubstep.f90 | 9 +- 12 files changed, 115 insertions(+), 109 deletions(-) diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 0faefc003..a5248dc1c 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -66,11 +66,14 @@ module computJacob_module private public::computJacob +#ifdef SUNDIALS_ACTIVE public::computJacob4kinsolSetup public::computJacob4kinsol +#endif contains + ! ********************************************************************************************************** ! public subroutine computJacob: compute the Jacobian matrix ! ********************************************************************************************************** @@ -995,7 +998,7 @@ subroutine computJacob(& end subroutine computJacob - +#ifdef SUNDIALS_ACTIVE ! ********************************************************************************************************** ! public subroutine computJacob4kinsolSetup: compute the Jacobian matrix ! ********************************************************************************************************** @@ -1076,7 +1079,6 @@ subroutine computJacob4kinsolSetup(& real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) ! other local variables character(LEN=256) :: cmessage ! error message of downwind routine -real(rkind) :: dt1 ! -------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures @@ -1159,12 +1161,11 @@ subroutine computJacob4kinsolSetup(& ! compute the analytical Jacobian matrix ! NOTE: The derivatives were computed in the previous call to computFlux - ! This occurred either at the call to eval8summaWithPrime at the start of sysSolveSundials - ! or in the call to eval8summaWithPrime in the previous iteration - dt1 = 1._qp + ! This occurred either at the call to eval8summa at the start of systemSolv + ! or in the call to eval8summa in the previous iteration call computJacob(& ! input: model control - dt1, & ! intent(in): length of the time step (seconds) + dt, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers @@ -1273,7 +1274,7 @@ integer(c_int) function computJacob4kinsol(sunvec_y, sunvec_r, sunmat_J, & return end function computJacob4kinsol - +#endif ! ********************************************************************************************************** ! private function: get the off-diagonal index in the band-diagonal matrix diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index 2844b2cf5..82b8a66f6 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -93,6 +93,7 @@ module computJacobWithPrime_module contains + ! ********************************************************************************************************** ! public subroutine computJacobWithPrime: compute the Jacobian matrix ! ********************************************************************************************************** @@ -1298,7 +1299,7 @@ subroutine computJacob4idaSetup(& ! compute the analytical Jacobian matrix ! NOTE: The derivatives were computed in the previous call to computFlux - ! This occurred either at the call to eval8summaWithPrime at the start of sysSolveSundials + ! This occurred either at the call to eval8summaWithPrime at the start of systemSolv ! or in the call to eval8summaWithPrime in the previous iteration dt1 = 1._qp call computJacobWithPrime(& diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index e7476fe88..1413a8a6f 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -68,7 +68,9 @@ module eval8summa_module implicit none private public::eval8summa +#ifdef SUNDIALS_ACTIVE public::eval8summa4kinsol +#endif contains @@ -91,7 +93,7 @@ subroutine eval8summa(& scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVec, & ! intent(in): model state vector - fScale, & ! intent(in): function scaling vector + fScale, & ! intent(in): characteristic scale of the function evaluations sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures model_decisions, & ! intent(in): model decisions @@ -149,7 +151,7 @@ subroutine eval8summa(& logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors real(rkind),intent(in) :: stateVec(:) ! model state vector - real(rkind),intent(in) :: fScale(:) ! function scaling vector + real(rkind),intent(in) :: fScale(:) ! characteristic scale of the function evaluations real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) ! input: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions @@ -651,7 +653,7 @@ subroutine eval8summa(& end subroutine eval8summa - +#ifdef SUNDIALS_ACTIVE ! ********************************************************************************************************** ! public function eval8summa4kinsol: compute the residual vector F(t,y) required for IDA solver ! ********************************************************************************************************** @@ -709,7 +711,7 @@ integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVec, & ! intent(in): model state vector - eqns_data%fScale, & ! intent(in): function scaling vector + eqns_data%fScale, & ! intent(in): characteristic scale of the function evaluations eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures model_decisions, & ! intent(in): model decisions @@ -745,6 +747,6 @@ integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & return end function eval8summa4kinsol - +#endif end module eval8summa_module diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index cd1956d95..049b45fac 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -288,9 +288,9 @@ subroutine eval8summaWithPrime(& prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: feasibility - feasible, & ! intent(inout): flag to denote the feasibility of the solution + feasible, & ! intent(inout): flag to denote the feasibility of the solution ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control ! early return for non-feasible solutions if(.not.feasible)then @@ -733,7 +733,7 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user implicit none ! calling variables - real(rkind), value :: tres ! current time t + real(rkind), value :: tres ! current time t type(N_Vector) :: sunvec_y ! solution N_Vector y type(N_Vector) :: sunvec_yp ! derivative N_Vector y' type(N_Vector) :: sunvec_r ! residual N_Vector F(t,y,y') diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index 94649676d..922959179 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -252,7 +252,7 @@ subroutine getScaling(& diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output - fScale, & ! intent(out): function scaling vector (mixed units) + fScale, & ! intent(out): characteristic scale of the function evaluations (mixed units) xScale, & ! intent(out): variable scaling vector (mixed units) sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) dMat, & ! intent(out): diagonal of the Jacobian matrix (excludes fluxes) @@ -265,7 +265,7 @@ subroutine getScaling(& type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers ! output: state vectors - real(rkind),intent(out) :: fScale(:) ! function scaling vector (mixed units) + real(rkind),intent(out) :: fScale(:) ! characteristic scale of the function evaluations (mixed units) real(rkind),intent(out) :: xScale(:) ! variable scaling vector (mixed units) real(qp),intent(out) :: sMul(:) ! NOTE: qp ! multiplier for state vector (used in the residual calculations) real(rkind),intent(out) :: dMat(:) ! diagonal of the Jacobian matrix (excludes fluxes) @@ -400,64 +400,62 @@ end subroutine getScaling ! public subroutine checkFeas: check feasibility of the state vector ! ********************************************************************************************************** subroutine checkFeas(& - ! input - stateVec, & ! intent(in): model state vector (mixed units) - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: feasibility - feasible, & ! intent(inout): flag to denote the feasibility of the solution - ! output: error control - err,message) ! intent(out): error control -! -------------------------------------------------------------------------------------------------------------------------------- -! -------------------------------------------------------------------------------------------------------------------------------- -implicit none -! input -real(rkind),intent(in) :: stateVec(:) ! model state vector (mixed units) -type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU -type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers -! output: feasibility -logical(lgt),intent(inout) :: feasible ! flag to denote the feasibility of the solution -! output: error control -integer(i4b),intent(out) :: err ! error code -character(*),intent(out) :: message ! error message -! -------------------------------------------------------------------------------------------------------------------------------- -! local variables -integer(i4b) :: iLayer ! index of layer within the snow+soil domain -real(rkind) :: xMin,xMax ! minimum and maximum values for water content -real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) -! -------------------------------------------------------------------------------------------------------------------------------- -! make association with variables in the data structures -associate(& - ! soil parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) - ! model diagnostic variables from the previous solution - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers - ! indices defining model states and layers - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) - )! association with variables in the data structures + ! input + stateVec, & ! intent(in): model state vector (mixed units) + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: feasibility + feasible, & ! intent(inout): flag to denote the feasibility of the solution + ! output: error control + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input + real(rkind),intent(in) :: stateVec(:) ! model state vector (mixed units) + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + ! output: feasibility + logical(lgt),intent(inout) :: feasible ! flag to denote the feasibility of the solution + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + real(rkind) :: xMin,xMax ! minimum and maximum values for water content + real(rkind),parameter :: canopyTempMax=500._rkind ! expected maximum value for the canopy temperature (K) + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + associate(& + ! soil parameters + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) + ! model diagnostic variables from the previous solution + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + ! number of model layers, and layer type + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + ! indices defining model states and layers + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) + )! association with variables in the data structures + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="checkFeas/" + ! initialize error control + err=0; message="checkFeas/" - ! check the feasibility of the solution always with BE numrec but not inside Sundials solver - ! NOTE: we will not print infeasibilities since it does not indicate a failure, just a need to iterate until maxiter - feasible=.true. - if (.not.insideSUN) then + ! NOTE: we will not print infeasibilities since it does not indicate a failure, just a need to iterate until maxiter + feasible=.true. ! check that the canopy air space temperature is reasonable if(ixCasNrg/=integerMissing)then if(stateVec(ixCasNrg) > canopyTempMax) feasible=.false. @@ -644,7 +642,6 @@ subroutine varExtract(& if(ixAqWat/=integerMissing) scalarAquiferStorageTrial = stateVec(ixAqWat) end associate - end subroutine varExtract end module getVectorz_module diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index 8b905b860..d49f7a705 100755 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -420,6 +420,13 @@ subroutine mDecisions(err,message) err=10; message=trim(message)//"unknown numerical method [option="//trim(model_decisions(iLookDECISIONS%num_method)%cDecision)//"]"; return end select + ! make sure compiled with SUNDIALS if want to use it +#ifndef SUNDIALS_ACTIVE + if(model_decisions(iLookDECISIONS%num_method)%iDecision==ida .or. model_decisions(iLookDECISIONS%num_method)%iDecision==kinsol)then + err=20; message=trim(message)//'cannot use num_method as ida or kinsol if did not compile with -DCMAKE_BUILD_TYPE=Sundials'; return + endif +#endif + ! how to compute heat capacity in energy equation, choice enthalpyFD has better coincidence of energy conservation with sundials tolerance. select case(trim(model_decisions(iLookDECISIONS%howHeatCap)%cDecision)) case('enthalpyFD'); model_decisions(iLookDECISIONS%howHeatCap)%iDecision = enthalpyFD ! heat capacity using enthalpy diff --git a/build/source/engine/matrixOper.f90 b/build/source/engine/matrixOper.f90 index a5fed26ad..27939b484 100644 --- a/build/source/engine/matrixOper.f90 +++ b/build/source/engine/matrixOper.f90 @@ -52,8 +52,8 @@ subroutine scaleMatrices(ixMatrix,nState,aJac,fScale,xScale,aJacScaled,err,messa integer(i4b),intent(in) :: ixMatrix ! type of matrix (full Jacobian or band diagonal) integer(i4b),intent(in) :: nState ! number of state variables real(rkind),intent(in) :: aJac(:,:) ! original Jacobian matrix - real(rkind),intent(in) :: fScale(:) ! function scaling vector - real(rkind),intent(in) :: xScale(:) ! "variable" scaling vector, i.e., for state variables + real(rkind),intent(in) :: fScale(:) ! characteristic scale of the function evaluations + real(rkind),intent(in) :: xScale(:) ! characteristic scale of the state vector ! output variables real(rkind),intent(out) :: aJacScaled(:,:) ! scaled Jacobian matrix integer(i4b),intent(out) :: err ! error code diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 7df95a5b4..d794593f0 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -118,8 +118,7 @@ subroutine summaSolve4ida( & dt_out, & ! intent(out): time step sum for entire data window at termination of sundials stateVec, & ! intent(out): model state vector stateVecPrime, & ! intent(out): derivative of model state vector - err,message & ! intent(out): error control - ) + err,message) ! intent(out): error control !======= Inclusions =========== USE fida_mod ! Fortran interface to IDA diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index 36350f8d5..9c5b5049c 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -74,6 +74,7 @@ module summaSolve4kinsol_module contains + !------------------- ! * public subroutine summaSolve4kinsol: solve F(y) = 0 by KINSOL (y is the state vector) ! ------------------ @@ -110,10 +111,8 @@ subroutine summaSolve4kinsol(& ! output ixSaturation, & ! intent(inout) index of the lowest saturated layer (NOTE: only computed on the first iteration) kinsolSucceeds, & ! intent(out): flag to indicate if KINSOL successfully solved the problem in current data step - tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt stateVec, & ! intent(out): model state vector - err,message & ! intent(out): error control - ) + err,message) ! intent(out): error control !======= Inclusions =========== @@ -133,7 +132,6 @@ subroutine summaSolve4kinsol(& USE eval8summa_module,only:eval8summa4kinsol ! DAE/ODE functions USE eval8summa_module,only:eval8summa ! residual of DAE USE computJacob_module,only:computJacob4kinsol ! system Jacobian - !USE tol4kinsol_module,only:computWeight4kinsol ! weight required for tolerances !======= Declarations ========= implicit none @@ -175,7 +173,6 @@ subroutine summaSolve4kinsol(& integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer real(rkind),intent(inout) :: stateVec(:) ! model state vector (y) logical(lgt),intent(out) :: kinsolSucceeds ! flag to indicate if KINSOL is successful - logical(lgt),intent(inout) :: tooMuchMelt ! flag to denote that there was too much melt ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -353,6 +350,9 @@ subroutine summaSolve4kinsol(& !*********************** Main Solver * loop on one_step mode ***************************** + eqns_data%firstFluxCall = .false. + eqns_data%firstSplitOper = .true. + ! Call KINSol to solve problem with choice of solver, linesearch or Picard !retval = FKINSol(kinsol_mem, sunvec_y, KIN_LINESEARCH, sunvec_xscale, sunvec_fscale) retval = FKINSol(kinsol_mem, sunvec_y, KIN_PICARD, sunvec_xscale, sunvec_fscale) diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index d2e833a39..bdbb8a28a 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -102,8 +102,8 @@ subroutine summaSolve4numrec(& ! input: state vectors stateVecTrial, & ! intent(in): trial state vector xMin,xMax, & ! intent(inout): brackets of the root - fScale, & ! intent(in): function scaling vector - xScale, & ! intent(in): "variable" scaling vector, i.e., for state variables + fScale, & ! intent(in): characteristic scale of the function evaluations + xScale, & ! intent(in): characteristic scale of the state vector rVec, & ! intent(in): residual vector sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout): diagonal matrix (excludes flux derivatives) @@ -155,8 +155,8 @@ subroutine summaSolve4numrec(& ! input: state vectors real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector real(rkind),intent(inout) :: xMin,xMax ! brackets of the root - real(rkind),intent(in) :: fScale(:) ! function scaling vector - real(rkind),intent(in) :: xScale(:) ! "variable" scaling vector, i.e., for state variables + real(rkind),intent(in) :: fScale(:) ! characteristic scale of the function evaluations + real(rkind),intent(in) :: xScale(:) ! characteristic scale of the state vector real(qp),intent(in) :: rVec(:) ! NOTE: qp ! residual vector real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(rkind),intent(inout) :: dMat(:) ! diagonal matrix (excludes flux derivatives) @@ -939,7 +939,7 @@ subroutine eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVecNew, & ! intent(in): updated model state vector - fScale, & ! intent(in): function scaling vector + fScale, & ! intent(in): characteristic scale of the function evaluations sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures model_decisions, & ! intent(in): model decisions diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 1fd87b07c..276c15383 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -158,9 +158,10 @@ subroutine systemSolv(& USE tol4ida_module,only:popTol4ida ! populate tolerances USE eval8summaWithPrime_module,only:eval8summaWithPrime ! get the fluxes and residuals USE summaSolve4ida_module,only:summaSolve4ida ! solve DAE by IDA + USE summaSolve4kinsol_module,only:summaSolve4kinsol ! solve DAE by KINSOL #endif USE eval8summa_module,only:eval8summa ! get the fluxes and residuals - USE summaSolve4numrec_module,only:summaSolve4numrec ! solve by numerical recipes + USE summaSolve4numrec_module,only:summaSolve4numrec ! solve DAE by numerical recipes implicit none ! --------------------------------------------------------------------------------------- @@ -228,13 +229,16 @@ subroutine systemSolv(& real(qp) :: rVec(nState) ! NOTE: qp ! residual vector real(rkind) :: rAdd(nState) ! additional terms in the residual vector logical(lgt) :: feasible ! feasibility flag + logical(lgt) :: sunSucceeds ! flag to indicate if SUNDIALS successfully solved the problem in current data step ! ida variables real(rkind) :: atol(nState) ! absolute tolerance ida real(rkind) :: rtol(nState) ! relative tolerance ida type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt_out (=dt) real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! derivative value for total water matric potential (m s-1) - logical(lgt) :: idaSucceeds ! flag to indicate if ida successfully solved the problem in current data step + ! kinsol variables + real(rkind) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) + real(rkind) :: xScale(nState) ! characteristic scale of the state vector (mixed units) ! numrec variables real(rkind) :: fOld,fNew ! function values (-); NOTE: dimensionless because scaled numrec real(rkind) :: xMin,xMax ! state minimum and maximum (mixed units) numrec @@ -382,7 +386,7 @@ subroutine systemSolv(& diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output - fScale, & ! intent(out): function scaling vector (mixed units) + fScale, & ! intent(out): characteristic scale of the function evaluations (mixed units) xScale, & ! intent(out): variable scaling vector (mixed units) sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) dMat, & ! intent(out): diagonal of the Jacobian matrix (excludes fluxes) @@ -453,7 +457,7 @@ subroutine systemSolv(& scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVecTrial, & ! intent(in): model state vector - fScale, & ! intent(in): function scaling vector + fScale, & ! intent(in): characteristic scale of the function evaluations sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures model_decisions, & ! intent(in): model decisions @@ -504,11 +508,8 @@ subroutine systemSolv(& rVec, & ! intent(out): residual vector err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - -#else - err=20; message=trim(message)//'cannot use num_method as ida if did not compile with -DCMAKE_BUILD_TYPE=Sundials'; return #endif - case(numrec) + case(kinsol .or. numrec) call eval8summa(& ! input: model control dt_cur, & ! intent(in): current stepsize @@ -525,7 +526,7 @@ subroutine systemSolv(& scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVecTrial, & ! intent(in): model state vector - fScale, & ! intent(in): function scaling vector + fScale, & ! intent(in): characteristic scale of the function evaluations sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures model_decisions, & ! intent(in): model decisions @@ -580,8 +581,8 @@ subroutine systemSolv(& ! * Solving the System ! ************************** select case(ixNumericalMethod) - case(ida) #ifdef SUNDIALS_ACTIVE + case(ida) ! get tolerance vectors call popTol4ida(& ! input @@ -644,14 +645,14 @@ subroutine systemSolv(& mLayerCmpress_sum, & ! intent(inout): sum of compression of the soil matrix ! output ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - idaSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step + sunSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt dt_out, & ! intent(out): time step sum for entire data window at termination of sundials stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step err,cmessage) ! intent(out): error control ! check if IDA is successful - if( .not.idaSucceeds )then + if( .not.sunSucceeds )then err = 20 message=trim(message)//trim(cmessage) ! reduceCoupledStep = .true. @@ -661,9 +662,8 @@ subroutine systemSolv(& endif niter = 0 ! iterations are counted inside IDA solver - ! ----- - ! * update states... - ! ------------------ + ! save the computed solution + stateVecTrial = stateVecNew ! compute average flux do iVar=1,size(flux_meta) @@ -709,8 +709,8 @@ subroutine systemSolv(& ! input: state vectors stateVecTrial, & ! intent(in): trial state vector xMin,xMax, & ! intent(inout): state maximum and minimum - fScale, & ! intent(in): function scaling vector - xScale, & ! intent(in): "variable" scaling vector, i.e., for state variables + fScale, & ! intent(in): characteristic scale of the function evaluations + xScale, & ! intent(in): characteristic scale of the state vector rVec, & ! intent(in): residual vector sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout): diagonal matrix (excludes flux derivatives) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index ec425b19e..e957facf5 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -412,10 +412,11 @@ subroutine varSubstep(& ! identify the need to check the mass balance select case(ixNumericalMethod) - case(ida); checkMassBalance = .false. ! only for numrec because sundials has instantaneous fluxes only - case(numrec); checkMassBalance = .true. ! (.not.scalarSolution) + case(ida); checkMassBalance = .false. ! IDA has instantaneous fluxes only so average will not balance over data window + case(kinsol .or. numrec); checkMassBalance = .true. ! (.not.scalarSolution) case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or numrec (or itertive, which is numrec)'; return end select + ! identify the need to check the energy balance, DOES NOT WORK YET and only check if ixHowHeatCap == enthalpyFD checkNrgBalance = .false. @@ -846,10 +847,8 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) ! output: error control err,cmessage) ! intent(out): error control -#else - err=20; message=trim(message)//'cannot use num_method as ida if did not compile with -DCMAKE_BUILD_TYPE=Sundials'; return #endif - case(numrec) + case(kinsol .or. numrec) ! update diagnostic variables call updateVars(& ! input From 276fc9ba8e885b4fb7cc7eaac2187b565df31539 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sun, 18 Jun 2023 23:06:16 +0900 Subject: [PATCH 0747/1472] indx_data should be inout, and making it so summa BE (numrec) does not post-process state vectors since Sundials does not --- build/cmake/CMakeLists.txt | 1 + build/source/dshare/type4kinsol.f90 | 4 +- build/source/engine/summaSolve4ida.f90 | 8 +- build/source/engine/summaSolve4kinsol.f90 | 18 ++--- build/source/engine/systemSolv.f90 | 97 ++++++++++++++++++----- 5 files changed, 90 insertions(+), 38 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index f220c3ad2..9b90d4910 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -377,6 +377,7 @@ set(SOLVER_SUNDIALS ${ENGINE_DIR}/computResidWithPrime.f90 ${ENGINE_DIR}/eval8summaWithPrime.f90 ${ENGINE_DIR}/summaSolve4ida.f90 + ${ENGINE_DIR}/summaSolve4kinsol.f90) # Driver support modules set(DRIVER diff --git a/build/source/dshare/type4kinsol.f90 b/build/source/dshare/type4kinsol.f90 index dbcd145c2..0f536266f 100644 --- a/build/source/dshare/type4kinsol.f90 +++ b/build/source/dshare/type4kinsol.f90 @@ -41,8 +41,8 @@ module type4kinsol real(rkind), allocatable :: dMat(:) ! diagonal of the Jacobian matrix real(rkind), allocatable :: fluxVec(:) ! flux vector real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation - real(rkind),allocatable :: fScale(:) ! function scaling vector - real(rkind),allocatable :: xScale(:) ! state scaling vector + real(rkind),allocatable :: fScale(:) ! characteristic scale of the function evaluations + real(rkind),allocatable :: xScale(:) ! characteristic scale of the state vector real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) integer(i4b) :: ixSaturation ! index of the lowest saturated layer integer(i4b) :: err ! error code diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index d794593f0..5f24e0576 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -77,6 +77,7 @@ module summaSolve4ida_module contains + !------------------- ! * public subroutine summaSolve4ida: solve F(y,y') = 0 by IDA (y is the state vector) ! ------------------ @@ -105,7 +106,7 @@ subroutine summaSolve4ida( & bvar_data, & ! intent(in): average model variables for the entire basin prog_data, & ! intent(in): model prognostic variables for a local HRU ! input-output: data structures - indx_data, & ! intent(in): index data + indx_data, & ! intent(inout): index data diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a dt @@ -167,8 +168,8 @@ subroutine summaSolve4ida( & type(var_d), intent(in) :: forc_data ! model forcing data type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_ilength), intent(in) :: indx_data ! indices defining model states and layers - ! input-output: data structures + ! input-output: data structures + type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt @@ -515,6 +516,7 @@ subroutine summaSolve4ida( & if(idaSucceeds)then ! copy to output data + indx_data = eqns_data%indx_data diag_data = eqns_data%diag_data flux_data = eqns_data%flux_data deriv_data = eqns_data%deriv_data diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index 9c5b5049c..f285a3090 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -104,7 +104,7 @@ subroutine summaSolve4kinsol(& bvar_data, & ! intent(in): average model variables for the entire basin prog_data, & ! intent(in): model prognostic variables for a local HRU ! input-output: data structures - indx_data, & ! intent(in): index data + indx_data, & ! intent(inout): index data diag_data, & ! intent(inout): model diagnostic variables for a local HRU diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables @@ -132,7 +132,7 @@ subroutine summaSolve4kinsol(& USE eval8summa_module,only:eval8summa4kinsol ! DAE/ODE functions USE eval8summa_module,only:eval8summa ! residual of DAE USE computJacob_module,only:computJacob4kinsol ! system Jacobian - + !======= Declarations ========= implicit none @@ -164,12 +164,12 @@ subroutine summaSolve4kinsol(& type(var_d), intent(in) :: forc_data ! model forcing data type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_ilength), intent(in) :: indx_data ! indices defining model states and layers ! input-output: data structures + type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! output: state vectors + ! output: state vectors integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer real(rkind),intent(inout) :: stateVec(:) ! model state vector (y) logical(lgt),intent(out) :: kinsolSucceeds ! flag to indicate if KINSOL is successful @@ -199,14 +199,7 @@ subroutine summaSolve4kinsol(& real(rkind),allocatable :: dCompress_dPsiPrev(:) ! previous derivative value soil compression logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn KINSOL warnings off, default true logical(lgt),parameter :: use_fdJac = .false. ! flag to use finite difference Jacobian, default false - - - - - ! local variables - integer(c_long) :: mset - integer(c_long) :: maxIter = 200 - + ! ----------------------------------------------------------------------------------------------------- ! initialize error control @@ -388,6 +381,7 @@ subroutine summaSolve4kinsol(& if(kinsolSucceeds)then ! copy to output data + indx_data = eqns_data%indx_data diag_data = eqns_data%diag_data flux_data = eqns_data%flux_data deriv_data = eqns_data%deriv_data diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 276c15383..ccee3b349 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -230,7 +230,7 @@ subroutine systemSolv(& real(rkind) :: rAdd(nState) ! additional terms in the residual vector logical(lgt) :: feasible ! feasibility flag logical(lgt) :: sunSucceeds ! flag to indicate if SUNDIALS successfully solved the problem in current data step - ! ida variables + ! ida variables real(rkind) :: atol(nState) ! absolute tolerance ida real(rkind) :: rtol(nState) ! relative tolerance ida type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt_out (=dt) @@ -239,7 +239,7 @@ subroutine systemSolv(& ! kinsol variables real(rkind) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) real(rkind) :: xScale(nState) ! characteristic scale of the state vector (mixed units) - ! numrec variables + ! numrec variables real(rkind) :: fOld,fNew ! function values (-); NOTE: dimensionless because scaled numrec real(rkind) :: xMin,xMax ! state minimum and maximum (mixed units) numrec integer(i4b) :: maxiter ! maximum number of iterations numrec @@ -590,10 +590,10 @@ subroutine systemSolv(& prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers - mpar_data, & ! intent(in): model parameters + mpar_data, & ! intent(in): model parameters ! output atol, & ! intent(out): absolute tolerances vector (mixed units) - rtol, & ! intent(out): relative tolerances vector (mixed units) + rtol, & ! intent(out): relative tolerances vector (mixed units) err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) @@ -636,8 +636,8 @@ subroutine systemSolv(& forc_data, & ! intent(in): model forcing data bvar_data, & ! intent(in): average model variables for the entire basin prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): index data ! input-output: data structures + indx_data, & ! intent(inout): index data diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_temp, & ! intent(inout): model fluxes for a local HRU flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step @@ -674,8 +674,59 @@ subroutine systemSolv(& diag_data%var(iLookDIAG%mLayerCompress)%dat(:) = mLayerCmpress_sum(:) / dt_out diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sum(diag_data%var(iLookDIAG%mLayerCompress)%dat(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water + case(kinsol) + !--------------------------- + ! * solving F(y) = 0 by Backward Euler with KINSOL, y is the state vector + !--------------------------- + ! iterations and updates to trial state vector, fluxes, and derivatives are done inside IDA solver + call summaSolve4kinsol(& + dt_cur, & ! intent(in): data time step + dt, & ! intent(in): data time step + fScale, & ! intent(in): characteristic scale of the function evaluations + xScale, & ! intent(in): characteristic scale of the state vector + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): number of snow+soil layers + nState, & ! intent(in): number of state variables in the current subset + ixMatrix, & ! intent(in): type of matrix (dense or banded) + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vector + stateVecTrial, & ! intent(in): model state vector at the beginning of the data time step + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + dMat, & ! intent(inout) diagonal of the Jacobian matrix (excludes fluxes) + ! input: data structures + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + indx_data, & ! intent(inout): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_temp, & ! intent(inout): model fluxes for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + sunSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step + stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step + err,cmessage) ! intent(out): error control + ! check if KINSOL is successful + if( .not.sunSucceeds )then + err = 20 + message=trim(message)//trim(cmessage) + ! reduceCoupledStep = .true. + return + endif + niter = 0 ! iterations are counted inside KINSOL solver + ! save the computed solution stateVecTrial = stateVecNew + stateVecPrime = stateVecTrial ! prime values not used here, dummy + #endif case(numrec) ! define maximum number of iterations @@ -724,8 +775,8 @@ subroutine systemSolv(& forc_data, & ! intent(in): model forcing data bvar_data, & ! intent(in): average model variables for the entire basin prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): index data ! input-output: data structures + indx_data, & ! intent(inout): index data diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_temp, & ! intent(inout): model fluxes for a local HRU (temporary structure) deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables @@ -760,27 +811,31 @@ subroutine systemSolv(& end do ! iterating + ! prime values not used here, dummy + stateVecPrime = stateVecTrial + ! ----- ! * update states... + ! Post processing step to “perfectly” conserve mass by pushing the errors into the state variables + ! Turn off for now to agree with SUNDIALS ! ------------------ - stateVecPrime = stateVecTrial ! prime values not used here, dummy - + ! ! update temperatures (ensure new temperature is consistent with the fluxes) - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - iState = ixSnowSoilNrg(iLayer) - stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState))/real(sMul(iState), rkind) - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - + !if(nSnowSoilNrg>0)then + ! do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + ! iState = ixSnowSoilNrg(iLayer) + ! stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState))/real(sMul(iState), rkind) + ! end do ! looping through non-missing energy state variables in the snow+soil domain + !endif + ! ! update volumetric water content in the snow (ensure change in state is consistent with the fluxes) ! NOTE: for soil water balance is constrained within the iteration loop - if(nSnowSoilHyd>0)then - do concurrent (iLayer=1:nSnow,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing water state variables in the snow domain) - iState = ixSnowSoilHyd(iLayer) - stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState)) - end do ! looping through non-missing water state variables in the soil domain - endif + !if(nSnowSoilHyd>0)then + ! do concurrent (iLayer=1:nSnow,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing water state variables in the snow domain) + ! iState = ixSnowSoilHyd(iLayer) + ! stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState)) + ! end do ! looping through non-missing water state variables in the soil domain + !endif end select From 4f2cbbfb03472dc2be449733dc0075b92bb5ecbf Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 19 Jun 2023 18:02:48 +0900 Subject: [PATCH 0748/1472] fix one input bug and comment changes --- build/source/dshare/type4kinsol.f90 | 2 +- build/source/engine/eval8summa.f90 | 10 +++----- build/source/engine/eval8summaWithPrime.f90 | 12 +++------ build/source/engine/getVectorz.f90 | 28 +++++++++++---------- build/source/engine/opSplittin.f90 | 2 +- build/source/engine/summaSolve4ida.f90 | 3 ++- build/source/engine/summaSolve4kinsol.f90 | 3 ++- build/source/engine/summaSolve4numrec.f90 | 2 +- build/source/engine/systemSolv.f90 | 2 +- build/source/engine/updateVars.f90 | 4 +-- build/source/engine/varSubstep.f90 | 2 +- 11 files changed, 33 insertions(+), 37 deletions(-) diff --git a/build/source/dshare/type4kinsol.f90 b/build/source/dshare/type4kinsol.f90 index 0f536266f..4cffb170a 100644 --- a/build/source/dshare/type4kinsol.f90 +++ b/build/source/dshare/type4kinsol.f90 @@ -9,7 +9,7 @@ module type4kinsol var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (dp) - model_options ! defines the model decisions + zLookup ! data vector with variable length dimension (rkind) implicit none type data4kinsol diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 1413a8a6f..d700424e5 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -31,10 +31,6 @@ module eval8summa_module ! access the global print flag USE globalData,only:globalPrintFlag -! define access to state variables to print -USE globalData,only: iJac1 ! first layer of the Jacobian to print -USE globalData,only: iJac2 ! last layer of the Jacobian to print - ! constants USE multiconst,only:& @@ -275,6 +271,7 @@ subroutine eval8summa(& call checkFeas(& ! input stateVec, & ! intent(in): model state vector (mixed units) + mpar_data, & ! intent(in): model parameters prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: feasibility @@ -731,7 +728,7 @@ integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors - feasible, & ! intent(out): flag to denote the feasibility of the solution + feasible, & ! intent(out): flag to denote the feasibility of the solution always true inside SUNDIALS eqns_data%fluxVec, & ! intent(out): flux vector eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation rVec, & ! intent(out): residual vector @@ -740,8 +737,7 @@ integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif - if(.not.feasible)then; eqns_data%message=trim(eqns_data%message)//'state vector not feasible'; ierr = 1; return; endif - + ! return success ierr = 0 return diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 049b45fac..655e2fbbe 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -12,9 +12,6 @@ module eval8summaWithPrime_module ! access the global print flag USE globalData,only:globalPrintFlag -! define access to state variables to print -USE globalData,only: iJac1 ! first layer of the Jacobian to print -USE globalData,only: iJac2 ! last layer of the Jacobian to print ! constants USE multiconst,only:& @@ -28,7 +25,7 @@ module eval8summaWithPrime_module var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (rkind) - zlookup, & + zlookup, & ! lookup tables model_options ! defines the model decisions ! indices that define elements of the data structures @@ -285,6 +282,7 @@ subroutine eval8summaWithPrime(& call checkFeas(& ! input stateVec, & ! intent(in): model state vector (mixed units) + mpar_data, & ! intent(in): model parameters prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: feasibility @@ -724,7 +722,6 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user !======= Inclusions =========== use, intrinsic :: iso_c_binding - use fida_mod use fsundials_nvector_mod use fnvector_serial_mod use type4ida @@ -817,7 +814,7 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors - feasible, & ! intent(out): flag to denote the feasibility of the solution + feasible, & ! intent(out): flag to denote the feasibility of the solution always true inside SUNDIALS eqns_data%fluxVec, & ! intent(out): flux vector eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation rVec, & ! intent(out): residual vector @@ -825,8 +822,7 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif - if(.not.feasible)then; eqns_data%message=trim(eqns_data%message)//'state vector not feasible'; ierr = 1; return; endif - + ! return success ierr = 0 return diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index 922959179..695fb4d29 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -402,6 +402,7 @@ end subroutine getScaling subroutine checkFeas(& ! input stateVec, & ! intent(in): model state vector (mixed units) + mpar_data, & ! intent(in): model parameters prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: feasibility @@ -413,6 +414,7 @@ subroutine checkFeas(& implicit none ! input real(rkind),intent(in) :: stateVec(:) ! model state vector (mixed units) + type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers ! output: feasibility @@ -446,7 +448,7 @@ subroutine checkFeas(& ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) + layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) )! association with variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- @@ -548,24 +550,24 @@ subroutine varExtract(& type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers ! output: variables for the vegetation canopy - real(rkind),intent(inout) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) - real(rkind),intent(inout) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - real(rkind),intent(inout) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) + real(rkind),intent(inout) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) + real(rkind),intent(inout) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + real(rkind),intent(inout) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - real(rkind),intent(inout) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(inout) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(rkind),intent(inout) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(inout) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) + real(rkind),intent(inout) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(inout) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(rkind),intent(inout) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(inout) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) ! output: variables for the aquifer - real(rkind),intent(inout) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + real(rkind),intent(inout) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables - integer(i4b) :: iLayer ! index of layer within the snow+soil domain + integer(i4b) :: iLayer ! index of layer within the snow+soil domain ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures associate(& @@ -584,7 +586,7 @@ subroutine varExtract(& nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain ! indices defining type of model state variables ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] type of desired model state variables - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat & ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat & ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain )! association with variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 22cde3b44..1ec220cab 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -93,7 +93,7 @@ module opSplittin_module var_flagVec, & ! data vector with variable length dimension (i4b) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! data vector with variable length dimension (rkind) + zLookup, & ! lookup tables model_options ! defines the model decisions diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 5f24e0576..d4c061b86 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -62,7 +62,7 @@ module summaSolve4ida_module var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (rkind) - zLookup ! data vector + zLookup ! lookup tables ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: qbaseTopmodel ! TOPMODEL-ish baseflow parameterization @@ -446,6 +446,7 @@ subroutine summaSolve4ida( & call checkFeas(& ! input stateVec, & ! intent(in): model state vector (mixed units) + eqns_data%mpar_data, & ! intent(in): model parameters eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU eqns_data%indx_data, & ! intent(in): indices defining model states and layers ! output: feasibility diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index f285a3090..2f1972050 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -61,7 +61,7 @@ module summaSolve4kinsol_module var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (rkind) - zLookup ! data vector + zLookup ! lookup tables ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: qbaseTopmodel ! TOPMODEL-ish baseflow parameterization @@ -359,6 +359,7 @@ subroutine summaSolve4kinsol(& call checkFeas(& ! input stateVec, & ! intent(in): model state vector (mixed units) + eqns_data%mpar_data, & ! intent(in): model parameters eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU eqns_data%indx_data, & ! intent(in): indices defining model states and layers ! output: feasibility diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index bdbb8a28a..7c65ecc50 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -67,7 +67,7 @@ module summaSolve4numrec_module var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! data vector with variable length dimension (rkind) + zLookup, & ! lookup tables model_options ! defines the model decisions ! look-up values for the choice of groundwater parameterization diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index ccee3b349..92307e2d3 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -80,7 +80,7 @@ module systemSolv_module var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! data vector with variable length dimension (rkind) + zLookup, & ! lookup tables model_options ! defines the model decisions ! look-up values for the choice of heat capacity computation diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 index f6d121280..bc86790c1 100644 --- a/build/source/engine/updateVars.f90 +++ b/build/source/engine/updateVars.f90 @@ -63,8 +63,8 @@ module updateVars_module var_i, & ! data vector (i4b) var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - zLookup, & ! data vector with variable length dimension (rkind) - var_dlength ! data vector with variable length dimension (rkind) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup & ! lookup tables ! provide access to indices that define elements of the data structures USE var_lookup,only:iLookDIAG ! named variables for structure elements diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index e957facf5..83691f30a 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -47,7 +47,7 @@ module varSubstep_module var_flagVec, & ! data vector with variable length dimension (i4b) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! data vector with variable length dimension (rkind) + zLookup, & ! lookup tables model_options ! defines the model decisions ! provide access to indices that define elements of the data structures From 09e2093b87bd159d116ace3f345c688d3dd7b537 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 19 Jun 2023 18:21:30 +0900 Subject: [PATCH 0749/1472] comments only --- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/eval8summaWithPrime.f90 | 9 ++++----- build/source/engine/updateVars.f90 | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index d700424e5..f7d9c55a3 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -700,7 +700,7 @@ integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & eqns_data%nSoil, & ! intent(in): number of soil layers eqns_data%nLayers, & ! intent(in): number of layers eqns_data%nState, & ! intent(in): number of state variables in the current subset - .true., & ! intent(in): inside Sundials solver + .true., & ! intent(in): inside SUNDIALS solver eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 655e2fbbe..67be138f1 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -153,7 +153,7 @@ subroutine eval8summaWithPrime(& real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) ! input: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(zLookup),intent(in) :: lookup_data ! lookup tables + type(zLookup), intent(in) :: lookup_data ! lookup tables type(var_i), intent(in) :: type_data ! type of vegetation and soil type(var_d), intent(in) :: attr_data ! spatial attributes type(var_dlength), intent(in) :: mpar_data ! model parameters @@ -742,7 +742,6 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user real(rkind), pointer :: stateVecPrime(:) real(rkind), pointer :: rVec(:) logical(lgt) :: feasible - integer(i4b) :: retval !======= Internals ============ ! get equations data from user-defined data @@ -761,10 +760,10 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%nSoil, & ! intent(in): number of soil layers eqns_data%nLayers, & ! intent(in): number of layers eqns_data%nState, & ! intent(in): number of state variables in the current subset - .true., & ! intent(in): inside Sundials solver - eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + .true., & ! intent(in): inside SUNDIALS solver + eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation + eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 index bc86790c1..0403b1dab 100644 --- a/build/source/engine/updateVars.f90 +++ b/build/source/engine/updateVars.f90 @@ -64,7 +64,7 @@ module updateVars_module var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (rkind) - zLookup & ! lookup tables + zLookup ! lookup tables ! provide access to indices that define elements of the data structures USE var_lookup,only:iLookDIAG ! named variables for structure elements From 99db98900e0a55775e4bc5df76db30cf09e05f4d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 19 Jun 2023 19:57:47 +0900 Subject: [PATCH 0750/1472] add model_decisions in as an eqns_data type and and input to computJacob* routines --- build/source/dshare/type4ida.f90 | 118 ++++++++++--------- build/source/dshare/type4kinsol.f90 | 72 +++++------ build/source/engine/computJacob.f90 | 6 +- build/source/engine/computJacobWithPrime.f90 | 10 +- build/source/engine/eval8summa.f90 | 6 +- build/source/engine/eval8summaWithPrime.f90 | 6 +- build/source/engine/summaSolve4ida.f90 | 12 +- build/source/engine/summaSolve4kinsol.f90 | 13 +- build/source/engine/summaSolve4numrec.f90 | 8 +- build/source/engine/systemSolv.f90 | 4 +- build/source/engine/tol4ida.f90 | 8 +- 11 files changed, 133 insertions(+), 130 deletions(-) diff --git a/build/source/dshare/type4ida.f90 b/build/source/dshare/type4ida.f90 index 8d8623c58..a09bd162b 100644 --- a/build/source/dshare/type4ida.f90 +++ b/build/source/dshare/type4ida.f90 @@ -10,68 +10,70 @@ module type4ida var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (rkind) - zLookup ! data vector with variable length dimension (rkind) + zLookup, & ! data vector with variable length dimension (rkind) + model_options ! defines the model decisions implicit none type data4ida - type(c_ptr) :: ida_mem ! IDA memory - real(rkind) :: dt ! data step - integer(i4b) :: nSnow ! number of snow layers - integer(i4b) :: nSoil ! number of soil layers - integer(i4b) :: nLayers ! total number of layers - integer(i4b) :: nState ! total number of state variables - integer(i4b) :: ixMatrix ! form of matrix (dense or banded) - logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution - type(zLookup) :: lookup_data ! lookup tables - type(var_i) :: type_data ! type of vegetation and soil - type(var_d) :: attr_data ! spatial attributes - type(var_dlength) :: mpar_data ! model parameters - type(var_d) :: forc_data ! model forcing data - type(var_dlength) :: bvar_data ! model variables for the local basin - type(var_dlength) :: prog_data ! prognostic variables for a local HRU - type(var_ilength) :: indx_data ! indices defining model states and layers - type(var_dlength) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength) :: flux_data ! model fluxes for a local HRU - type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(rkind) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind) :: scalarCanopyTempPrev ! previous value of canopy temperature (K) - real(rkind) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - real(rkind) :: scalarCanopyIcePrev ! value of canopy ice content (kg m-2) at previous step - real(rkind) :: scalarCanopyLiqTrial ! trial value of canopy ice content (kg m-2) - real(rkind) :: scalarCanopyLiqPrev ! value of canopy ice content (kg m-2) at previous step - real(rkind) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) - real(rkind) :: scalarCanopyEnthalpyPrev ! previous enthalpy of the vegetation canopy (J m-3) - real(qp), allocatable :: sMul(:) ! state vector multiplier (used in the residual calculations) - real(rkind), allocatable :: dMat(:) ! diagonal of the Jacobian matrix - real(rkind), allocatable :: fluxVec(:) ! flux vector - real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation - real(rkind), allocatable :: atol(:) ! vector of absolute tolerances - real(rkind), allocatable :: rtol(:) ! vector of relative tolerances - real(rkind), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind), allocatable :: mLayerTempPrev(:) ! vector of layer temperature (K) at previous step - real(rkind), allocatable :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) - real(rkind), allocatable :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind), allocatable :: mLayerMatricHeadPrev(:) ! vector of total water matric potential (m) at previous step - real(rkind), allocatable :: mLayerVolFracWatTrial(:) ! trial value for volumetric fraction of total water (-) - real(rkind), allocatable :: mLayerVolFracWatPrev(:) ! value for volumetric fraction of total water (-) at previous step - real(rkind), allocatable :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) - real(rkind), allocatable :: mLayerVolFracIcePrev(:) ! value for volumetric fraction of ice (-) at previous step - real(rkind), allocatable :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) - real(rkind), allocatable :: mLayerVolFracLiqPrev(:) ! value for volumetric fraction of liquid water (-) at previous step - real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) - real(rkind) :: scalarAquiferStoragePrev ! value of storage of water in the aquifer (m) at previous step - real(rkind), allocatable :: mLayerEnthalpyTrial(:) ! trial enthalpy of snow and soil (J m-3) - real(rkind), allocatable :: mLayerEnthalpyPrev(:) ! enthalpy of snow and soil (J m-3) at previous step - real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! derivative value for total water matric potential (m s-1) - real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - integer(i4b) :: ixSaturation ! index of the lowest saturated layer - integer(i4b) :: err ! error code - character(len = 50) :: message ! error message + type(c_ptr) :: ida_mem ! IDA memory + real(rkind) :: dt ! data step + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers + integer(i4b) :: nLayers ! total number of layers + integer(i4b) :: nState ! total number of state variables + integer(i4b) :: ixMatrix ! form of matrix (dense or banded) + logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution + type(model_options),allocatable :: model_decisions(:) ! model decisions + type(zLookup) :: lookup_data ! lookup tables + type(var_i) :: type_data ! type of vegetation and soil + type(var_d) :: attr_data ! spatial attributes + type(var_dlength) :: mpar_data ! model parameters + type(var_d) :: forc_data ! model forcing data + type(var_dlength) :: bvar_data ! model variables for the local basin + type(var_dlength) :: prog_data ! prognostic variables for a local HRU + type(var_ilength) :: indx_data ! indices defining model states and layers + type(var_dlength) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength) :: flux_data ! model fluxes for a local HRU + type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + real(rkind) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind) :: scalarCanopyTempPrev ! previous value of canopy temperature (K) + real(rkind) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + real(rkind) :: scalarCanopyIcePrev ! value of canopy ice content (kg m-2) at previous step + real(rkind) :: scalarCanopyLiqTrial ! trial value of canopy ice content (kg m-2) + real(rkind) :: scalarCanopyLiqPrev ! value of canopy ice content (kg m-2) at previous step + real(rkind) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) + real(rkind) :: scalarCanopyEnthalpyPrev ! previous enthalpy of the vegetation canopy (J m-3) + real(qp), allocatable :: sMul(:) ! state vector multiplier (used in the residual calculations) + real(rkind), allocatable :: dMat(:) ! diagonal of the Jacobian matrix + real(rkind), allocatable :: fluxVec(:) ! flux vector + real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation + real(rkind), allocatable :: atol(:) ! vector of absolute tolerances + real(rkind), allocatable :: rtol(:) ! vector of relative tolerances + real(rkind), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind), allocatable :: mLayerTempPrev(:) ! vector of layer temperature (K) at previous step + real(rkind), allocatable :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) + real(rkind), allocatable :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind), allocatable :: mLayerMatricHeadPrev(:) ! vector of total water matric potential (m) at previous step + real(rkind), allocatable :: mLayerVolFracWatTrial(:) ! trial value for volumetric fraction of total water (-) + real(rkind), allocatable :: mLayerVolFracWatPrev(:) ! value for volumetric fraction of total water (-) at previous step + real(rkind), allocatable :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) + real(rkind), allocatable :: mLayerVolFracIcePrev(:) ! value for volumetric fraction of ice (-) at previous step + real(rkind), allocatable :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) + real(rkind), allocatable :: mLayerVolFracLiqPrev(:) ! value for volumetric fraction of liquid water (-) at previous step + real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + real(rkind) :: scalarAquiferStoragePrev ! value of storage of water in the aquifer (m) at previous step + real(rkind), allocatable :: mLayerEnthalpyTrial(:) ! trial enthalpy of snow and soil (J m-3) + real(rkind), allocatable :: mLayerEnthalpyPrev(:) ! enthalpy of snow and soil (J m-3) at previous step + real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! derivative value for total water matric potential (m s-1) + real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + integer(i4b) :: ixSaturation ! index of the lowest saturated layer + integer(i4b) :: err ! error code + character(len = 50) :: message ! error message end type data4ida diff --git a/build/source/dshare/type4kinsol.f90 b/build/source/dshare/type4kinsol.f90 index 4cffb170a..3797c4e58 100644 --- a/build/source/dshare/type4kinsol.f90 +++ b/build/source/dshare/type4kinsol.f90 @@ -9,44 +9,46 @@ module type4kinsol var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (dp) - zLookup ! data vector with variable length dimension (rkind) + zLookup, & ! data vector with variable length dimension (rkind) + model_options ! defines the model decisions implicit none type data4kinsol - type(c_ptr) :: kinsol_mem ! KINSOL memory - real(rkind) :: dt_cur ! current stepsize - real(rkind) :: dt ! data step - integer(i4b) :: nSnow ! number of snow layers - integer(i4b) :: nSoil ! number of soil layers - integer(i4b) :: nLayers ! total number of layers - integer(i4b) :: nState ! total number of state variables - integer(i4b) :: ixMatrix ! form of matrix (dense or banded) - logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution - type(zLookup) :: lookup_data ! lookup tables - type(var_i) :: type_data ! type of vegetation and soil - type(var_d) :: attr_data ! spatial attributes - type(var_dlength) :: mpar_data ! model parameters - type(var_d) :: forc_data ! model forcing data - type(var_dlength) :: bvar_data ! model variables for the local basin - type(var_dlength) :: prog_data ! prognostic variables for a local HRU - type(var_ilength) :: indx_data ! indices defining model states and layers - type(var_dlength) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength) :: flux_data ! model fluxes for a local HRU - type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(qp), allocatable :: sMul(:) ! state vector multiplier (used in the residual calculations) - real(rkind), allocatable :: dMat(:) ! diagonal of the Jacobian matrix - real(rkind), allocatable :: fluxVec(:) ! flux vector - real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation - real(rkind),allocatable :: fScale(:) ! characteristic scale of the function evaluations - real(rkind),allocatable :: xScale(:) ! characteristic scale of the state vector - real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - integer(i4b) :: ixSaturation ! index of the lowest saturated layer - integer(i4b) :: err ! error code - character(len = 50) :: message ! error message + type(c_ptr) :: kinsol_mem ! KINSOL memory + real(rkind) :: dt_cur ! current stepsize + real(rkind) :: dt ! data step + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers + integer(i4b) :: nLayers ! total number of layers + integer(i4b) :: nState ! total number of state variables + integer(i4b) :: ixMatrix ! form of matrix (dense or banded) + logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution + type(model_options),allocatable :: model_decisions(:) ! model decisions + type(zLookup) :: lookup_data ! lookup tables + type(var_i) :: type_data ! type of vegetation and soil + type(var_d) :: attr_data ! spatial attributes + type(var_dlength) :: mpar_data ! model parameters + type(var_d) :: forc_data ! model forcing data + type(var_dlength) :: bvar_data ! model variables for the local basin + type(var_dlength) :: prog_data ! prognostic variables for a local HRU + type(var_ilength) :: indx_data ! indices defining model states and layers + type(var_dlength) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength) :: flux_data ! model fluxes for a local HRU + type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + real(qp), allocatable :: sMul(:) ! state vector multiplier (used in the residual calculations) + real(rkind), allocatable :: dMat(:) ! diagonal of the Jacobian matrix + real(rkind), allocatable :: fluxVec(:) ! flux vector + real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation + real(rkind),allocatable :: fScale(:) ! characteristic scale of the function evaluations + real(rkind),allocatable :: xScale(:) ! characteristic scale of the state vector + real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + integer(i4b) :: ixSaturation ! index of the lowest saturated layer + integer(i4b) :: err ! error code + character(len = 50) :: message ! error message end type data4kinsol diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index a5248dc1c..f4ef32146 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -26,7 +26,8 @@ module computJacob_module ! derived types to define the data structures USE data_types,only:& var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (rkind) + var_dlength, & ! data vector with variable length dimension (rkind) + model_options ! defines the model decisions ! named variables for structure elements USE var_lookup,only:iLookPROG ! named variables for structure elements @@ -43,7 +44,6 @@ module computJacob_module ! named variables to describe the state variable type USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers -USE globalData,only:model_decisions ! model decision structure ! access named variables to describe the form and structure of the matrices used in the numerical solver USE globalData,only: ku ! number of super-diagonal bands, assume ku>=3 @@ -1252,7 +1252,7 @@ integer(c_int) function computJacob4kinsol(sunvec_y, sunvec_r, sunmat_J, & stateVec, & ! intent(in): model state vector eqns_data%sMul, & ! intent(in): state vector multiplier (used in the residual calculations) ! input: data structures - model_decisions, & ! intent(in): model decisions + eqns_data%model_decisions, & ! intent(in): model decisions eqns_data%mpar_data, & ! intent(in): model parameters eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU ! input-output: data structures diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index 82b8a66f6..3d014dd2b 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -49,7 +49,6 @@ module computJacobWithPrime_module ! named variables to describe the state variable type USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers -USE globalData,only:model_decisions ! model decision structure ! access named variables to describe the form and structure of the matrices used in the numerical solver USE globalData,only: ku ! number of super-diagonal bands, assume ku>=3 @@ -111,6 +110,7 @@ subroutine computJacobWithPrime(& theta_sat, & ! intent(in): soil porosity (-) ixRichards, & ! intent(in): choice of option for Richards' equation ! input: data structures + model_decisions, & ! intent(in): model decisions indx_data, & ! intent(in): index data prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -143,18 +143,17 @@ subroutine computJacobWithPrime(& real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation ! input: data structures + type(model_options),intent(in) :: model_decisions(:) ! model decisions type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! input: state variables - real(rkind),intent(in) :: mLayerTemp(:) ! vector of layer temperature (K) real(rkind),intent(in) :: mLayerTempPrime(:) ! vector of derivative value for layer temperature real(rkind),intent(in) :: mLayerMatricHeadPrime(:) ! vector of derivative value for layer matric head real(rkind),intent(in) :: mLayerMatricHeadLiqPrime(:)! vector of derivative value for layer liquid matric head real(rkind),intent(in) :: mLayerVolFracWatPrime(:) ! vector of derivative value for layer water volume fraction - real(rkind),intent(in) :: scalarCanopyTemp ! temperature of the vegetation canopy (K) real(rkind),intent(in) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) real(rkind),intent(in) :: scalarCanopyWatPrime ! derivative value for water content of the vegetation canopy ! input-output: Jacobian and its diagonal @@ -1316,6 +1315,7 @@ subroutine computJacob4idaSetup(& theta_sat, & ! intent(in): soil porosity (-) ixRichards, & ! intent(in): choice of option for Richards' equation ! input: data structures + model_decisions, & ! intent(in): model decisions indx_data, & ! intent(in): index data prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -1338,7 +1338,7 @@ subroutine computJacob4idaSetup(& ! end association with the information in the data structures end associate -end subroutine computJacobSetup +end subroutine computJacob4idaSetup ! ********************************************************************************************************** @@ -1409,7 +1409,7 @@ integer(c_int) function computJacob4ida(t, cj, sunvec_y, sunvec_yp, sunvec_r, & stateVecPrime, & ! intent(in): model state vector eqns_data%sMul, & ! intent(in): state vector multiplier (used in the residual calculations) ! input: data structures - model_decisions, & ! intent(in): model decisions + eqns_data%model_decisions, & ! intent(in): model decisions eqns_data%mpar_data, & ! intent(in): model parameters eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU ! input-output: data structures diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index f7d9c55a3..7957d9a4d 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -28,9 +28,6 @@ module eval8summa_module USE globalData,only:realMissing ! missing double precision number USE globalData,only:quadMissing ! missing quadruple precision number -! access the global print flag -USE globalData,only:globalPrintFlag - ! constants USE multiconst,only:& @@ -82,6 +79,7 @@ subroutine eval8summa(& nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers nState, & ! intent(in): total number of state variables + insideSUN, & ! intent(in): flag to indicate if we are inside Sundials solver firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation @@ -711,7 +709,7 @@ integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & eqns_data%fScale, & ! intent(in): characteristic scale of the function evaluations eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures - model_decisions, & ! intent(in): model decisions + eqns_data%model_decisions, & ! intent(in): model decisions eqns_data%lookup_data, & ! intent(in): lookup data eqns_data%type_data, & ! intent(in): type of vegetation and soil eqns_data%attr_data, & ! intent(in): spatial attributes diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 67be138f1..7e0e0ed43 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -9,10 +9,6 @@ module eval8summaWithPrime_module USE globalData,only:realMissing ! missing double precision number USE globalData,only:quadMissing ! missing quadruple precision number -! access the global print flag -USE globalData,only:globalPrintFlag - - ! constants USE multiconst,only:& Tfreeze, & ! temperature at freezing (K) @@ -771,7 +767,7 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user stateVecPrime, & ! intent(in): model state vector eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures - model_decisions, & ! intent(in): model decisions + eqns_data%model_decisions, & ! intent(in): model decisions eqns_data%lookup_data, & ! intent(in): lookup data eqns_data%type_data, & ! intent(in): type of vegetation and soil eqns_data%attr_data, & ! intent(in): spatial attributes diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index d4c061b86..a2be90f9b 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -38,9 +38,6 @@ module summaSolve4ida_module USE globalData,only: ku ! number of super-diagonal bands USE globalData,only: kl ! number of sub-diagonal bands -! state variable type -USE globalData,only:model_decisions ! model decision structure - ! global metadata USE globalData,only:flux_meta ! metadata on the model fluxes @@ -62,7 +59,8 @@ module summaSolve4ida_module var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (rkind) - zLookup ! lookup tables + zLookup, & ! lookup tables + model_options ! defines the model decisions ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: qbaseTopmodel ! TOPMODEL-ish baseflow parameterization @@ -98,6 +96,7 @@ subroutine summaSolve4ida( & sMul, & ! intent(inout): state vector multiplier (USEd in the residual calculations) dMat, & ! intent(inout): diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures + model_decisions, & ! intent(in): model decisions lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes @@ -161,7 +160,8 @@ subroutine summaSolve4ida( & real(qp),intent(in) :: sMul(:) ! state vector multiplier (used in the residual calculations) real(rkind), intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures - type(zLookup),intent(in) :: lookup_data ! lookup tables + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup), intent(in) :: lookup_data ! lookup tables type(var_i), intent(in) :: type_data ! type of vegetation and soil type(var_d), intent(in) :: attr_data ! spatial attributes type(var_dlength), intent(in) :: mpar_data ! model parameters @@ -238,6 +238,7 @@ subroutine summaSolve4ida( & eqns_data%firstSubStep = firstSubStep eqns_data%computeVegFlux = computeVegFlux eqns_data%scalarSolution = scalarSolution + eqns_data%model_decisions = model_decisions eqns_data%lookup_data = lookup_data eqns_data%type_data = type_data eqns_data%attr_data = attr_data @@ -526,6 +527,7 @@ subroutine summaSolve4ida( & endif ! free memory + deallocate( eqns_data%model_decisions) deallocate( eqns_data%sMul ) deallocate( eqns_data%dMat ) deallocate( eqns_data%dBaseflow_dMatric ) diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index 2f1972050..af74f6061 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -37,9 +37,6 @@ module summaSolve4kinsol_module USE globalData,only: ku ! number of super-diagonal bands USE globalData,only: kl ! number of sub-diagonal bands -! state variable type -USE globalData,only:model_decisions ! model decision structure - ! global metadata USE globalData,only:flux_meta ! metadata on the model fluxes @@ -61,7 +58,8 @@ module summaSolve4kinsol_module var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (rkind) - zLookup ! lookup tables + zLookup, & ! lookup tables + model_options ! defines the model decisions ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: qbaseTopmodel ! TOPMODEL-ish baseflow parameterization @@ -96,6 +94,7 @@ subroutine summaSolve4kinsol(& sMul, & ! intent(inout): state vector multiplier (USEd in the residual calculations) dMat, & ! intent(inout): diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures + model_decisions, & ! intent(in): model decisions lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes @@ -157,7 +156,8 @@ subroutine summaSolve4kinsol(& real(qp),intent(in) :: sMul(:) ! state vector multiplier (used in the residual calculations) real(rkind), intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures - type(zLookup),intent(in) :: lookup_data ! lookup tables + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup), intent(in) :: lookup_data ! lookup tables type(var_i), intent(in) :: type_data ! type of vegetation and soil type(var_d), intent(in) :: attr_data ! spatial attributes type(var_dlength), intent(in) :: mpar_data ! model parameters @@ -219,6 +219,7 @@ subroutine summaSolve4kinsol(& eqns_data%firstSubStep = firstSubStep eqns_data%computeVegFlux = computeVegFlux eqns_data%scalarSolution = scalarSolution + eqns_data%model_decisions = model_decisions eqns_data%deriv_data = deriv_data eqns_data%lookup_data = lookup_data eqns_data%type_data = type_data @@ -390,6 +391,7 @@ subroutine summaSolve4kinsol(& endif ! free memory + deallocate( eqns_data%model_decisions) deallocate( eqns_data%fScale ) deallocate( eqns_data%xScale ) deallocate( eqns_data%sMul ) @@ -398,7 +400,6 @@ subroutine summaSolve4kinsol(& deallocate( eqns_data%fluxVec ) deallocate( eqns_data%resSink ) - call FKINFree(kinsol_mem) retval = FSUNLinSolFree(sunlinsol_LS) if(retval /= 0)then; err=20; message='summaSolve4kinsol: unable to free the linear solver'; return; endif diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 7c65ecc50..c101390d1 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -36,11 +36,11 @@ module summaSolve4numrec_module USE globalData,only:quadMissing ! missing quadruple precision number ! access named variables to describe the form and structure of the matrices used in the numerical solver +USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix +USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix USE globalData,only: ku ! number of super-diagonal bands USE globalData,only: kl ! number of sub-diagonal bands USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix -USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix -USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix USE globalData,only: iJac1 ! first layer of the Jacobian to print USE globalData,only: iJac2 ! last layer of the Jacobian to print @@ -235,7 +235,7 @@ subroutine summaSolve4numrec(& ! or in the call to eval8summa in the previous iteration (within lineSearchRefinement or trustRegionRefinement) call computJacob(& ! input: model control - dt_cur, & ! intent(in): length of the time step (seconds) + dt_cur, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers @@ -931,7 +931,7 @@ subroutine eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers nState, & ! intent(in): total number of state variables - .false., & ! intent(in): not inside Sundials solver + .false., & ! intent(in): not inside Sundials solver firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call .false., & ! intent(in): not processing the first iteration in a splitting operation diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 92307e2d3..8980e9483 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -55,7 +55,7 @@ module systemSolv_module USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers ! global metadata -USE globalData,only:flux_meta ! metadata on the model fluxes +USE globalData,only:flux_meta ! metadata on the model fluxes ! constants USE multiconst,only:& @@ -629,6 +629,7 @@ subroutine systemSolv(& sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout) diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures + model_decisions, & ! intent(in): model decisions lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes @@ -697,6 +698,7 @@ subroutine systemSolv(& sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout) diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures + model_decisions, & ! intent(in): model decisions lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes diff --git a/build/source/engine/tol4ida.f90 b/build/source/engine/tol4ida.f90 index ce630eb25..d18ba5956 100644 --- a/build/source/engine/tol4ida.f90 +++ b/build/source/engine/tol4ida.f90 @@ -94,10 +94,10 @@ integer(c_int) function computWeight4ida(sunvec_y, sunvec_ewt, user_data) & ! pointers to data in SUNDIALS vectors - type(eqnsData), pointer :: tol_data ! equations data - real(rkind), pointer :: stateVec(:) - real(rkind), pointer :: weightVec(:) - integer(c_int) :: iState + type(eqnsData), pointer :: tol_data ! equations data + real(rkind), pointer :: stateVec(:) + real(rkind), pointer :: weightVec(:) + integer(c_int) :: iState !======= Internals ============ From 575f24c5f5b6ad9899809f5e4e92f54b83be7ec8 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 19 Jun 2023 20:03:55 +0900 Subject: [PATCH 0751/1472] wrong data type --- build/source/engine/tol4ida.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/tol4ida.f90 b/build/source/engine/tol4ida.f90 index d18ba5956..6399a3cfb 100644 --- a/build/source/engine/tol4ida.f90 +++ b/build/source/engine/tol4ida.f90 @@ -94,7 +94,7 @@ integer(c_int) function computWeight4ida(sunvec_y, sunvec_ewt, user_data) & ! pointers to data in SUNDIALS vectors - type(eqnsData), pointer :: tol_data ! equations data + type(data4ida), pointer :: tol_data ! equations data real(rkind), pointer :: stateVec(:) real(rkind), pointer :: weightVec(:) integer(c_int) :: iState From fa946861a2823a64e9c9639a5a51e587677600c3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 19 Jun 2023 23:05:51 +0900 Subject: [PATCH 0752/1472] compiles now --- build/source/engine/computJacob.f90 | 7 ++ build/source/engine/computJacobWithPrime.f90 | 19 ++--- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/eval8summaWithPrime.f90 | 2 +- build/source/engine/summaSolve4ida.f90 | 33 +++++---- build/source/engine/summaSolve4kinsol.f90 | 77 +++++++------------- build/source/engine/systemSolv.f90 | 8 +- build/source/engine/varSubstep.f90 | 4 +- 8 files changed, 64 insertions(+), 88 deletions(-) diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index f4ef32146..954deac6b 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -30,6 +30,7 @@ module computJacob_module model_options ! defines the model decisions ! named variables for structure elements +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure USE var_lookup,only:iLookPROG ! named variables for structure elements USE var_lookup,only:iLookDIAG ! named variables for structure elements USE var_lookup,only:iLookINDEX ! named variables for structure elements @@ -60,6 +61,12 @@ module computJacob_module LH_fus, & ! latent heat of fusion (J kg-1) iden_water ! intrinsic density of liquid water (kg m-3) +! look-up values for the choice of groundwater parameterization +USE mDecisions_module,only: & + qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization + bigBucket, & ! a big bucket (lumped aquifer model) + noExplicit ! no explicit groundwater parameterization + implicit none ! define constants real(rkind),parameter :: verySmall=tiny(1.0_rkind) ! a very small number diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index 3d014dd2b..1e4bd4a35 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -65,21 +65,16 @@ module computJacobWithPrime_module LH_fus, & ! latent heat of fusion (J kg-1) iden_water ! intrinsic density of liquid water (kg m-3) -! look-up values for the choice of groundwater representation (local-column, or single-basin) -USE mDecisions_module,only: & - localColumn, & ! separate groundwater representation in each local soil column - singleBasin ! single groundwater store over the entire basin - ! look-up values for the choice of groundwater parameterization -USE mDecisions_module,only: & - qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization - bigBucket, & ! a big bucket (lumped aquifer model) - noExplicit ! no explicit groundwater parameterization +USE mDecisions_module,only: & + qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization + bigBucket, & ! a big bucket (lumped aquifer model) + noExplicit ! no explicit groundwater parameterization ! look-up values for the form of Richards' equation -USE mDecisions_module,only: & - moisture, & ! moisture-based form of Richards' equation - mixdform ! mixed form of Richards' equation +USE mDecisions_module,only: & + moisture, & ! moisture-based form of Richards' equation + mixdform ! mixed form of Richards' equation implicit none ! define constants diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 7957d9a4d..c48e237ae 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -282,7 +282,7 @@ subroutine eval8summa(& fluxVec(:) = realMissing resVec(:) = quadMissing fEval = realMissing - message=trim(message)//'non-feasible' + message=trim(message)//trim(cmessage)//'non-feasible' return end if end if ! ( feasibility check ) diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 7e0e0ed43..23a1ccf3a 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -290,7 +290,7 @@ subroutine eval8summaWithPrime(& if(.not.feasible)then fluxVec(:) = realMissing resVec(:) = quadMissing - message=trim(message)//'non-feasible' + message=trim(message)//trim(cmessage)//'non-feasible' return end if end if ! ( feasibility check ) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index a2be90f9b..c72c01b1d 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -31,6 +31,7 @@ module summaSolve4ida_module ! access missing values USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing double precision number ! access matrix information USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix @@ -124,19 +125,22 @@ subroutine summaSolve4ida( & USE fida_mod ! Fortran interface to IDA USE fsundials_context_mod ! Fortran interface to SUNContext USE fnvector_serial_mod ! Fortran interface to serial N_Vector + USE fsundials_nvector_mod ! Fortran interface to generic N_Vector USE fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix - USE fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver USE fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix - USE fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver USE fsundials_matrix_mod ! Fortran interface to generic SUNMatrix - USE fsundials_nvector_mod ! Fortran interface to generic N_Vector + USE fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver + USE fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver + USE fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver + USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver USE allocspace_module,only:allocLocal ! allocate local data structures - USE eval8summaWithPrime_module,only:eval8summa4ida ! DAE/ODE functions - USE eval8summaWithPrime_module,only:eval8summaWithPrime ! residual of DAE - USE computJacobWithPrime_module,only:computJacob4ida ! system Jacobian + USE getVectorz_module, only:checkFeas ! check feasibility of state vector + USE eval8summaWithPrime_module,only:eval8summa4ida ! DAE/ODE functions + USE eval8summaWithPrime_module,only:eval8summaWithPrime ! residual of DAE + USE computJacobWithPrime_module,only:computJacob4ida ! system Jacobian USE tol4ida_module,only:computWeight4ida ! weight required for tolerances - + !======= Declarations ========= implicit none @@ -203,8 +207,8 @@ subroutine summaSolve4ida( & real(qp) :: t0 ! starting time real(qp) :: dt_last(1) ! last time step real(qp) :: dt_diff ! difference from previous timestep - integer(kind = 8) :: mu, lu ! in banded matrix mode in Sundials type - integer(c_long) :: nState ! total number of state variables in Sundials type + integer(c_long) :: mu, lu ! in banded matrix mode in SUNDIALS type + integer(c_long) :: nState ! total number of state variables in SUNDIALS type real(rkind) :: rVec(nStat) ! residual vector integer(i4b) :: iVar, i ! indices integer(i4b) :: nRoot ! total number of roots (events) to find @@ -214,6 +218,7 @@ subroutine summaSolve4ida( & integer(i4b),allocatable :: rootdir(:) ! forced crossing direction of discontinuities logical(lgt) :: tinystep ! if step goes below small size type(var_dlength) :: flux_prev ! previous model fluxes for a local HRU + character(LEN=256) :: cmessage ! error message of downwind routine real(rkind),allocatable :: mLayerMatricHeadPrimePrev(:) ! previous derivative value for total water matric potential (m s-1) real(rkind),allocatable :: dCompress_dPsiPrev(:) ! previous derivative value soil compression logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true @@ -225,7 +230,7 @@ subroutine summaSolve4ida( & ! initialize error control err=0; message="summaSolve4ida/" - nState = nStat ! total number of state variables in Sundials type + nState = nStat ! total number of state variables in SUNDIALS type idaSucceeds = .true. ! fill eqns_data which will be required later to call eval8summaWithPrime @@ -409,7 +414,6 @@ subroutine summaSolve4ida( & endif !*********************** Main Solver * loop on one_step mode ***************************** - tinystep = .false. tret(1) = t0 ! initial time tretPrev = tret(1) @@ -458,7 +462,7 @@ subroutine summaSolve4ida( & ! early return for non-feasible solutions, will fail in current Sundials formulation if(.not.feasible)then eqns_data%fluxVec(:) = realMissing - message=trim(message)//'non-feasible' + message=trim(message)//trim(cmessage)//'non-feasible' return end if @@ -509,7 +513,6 @@ subroutine summaSolve4ida( & endif enddo ! while loop on one_step mode until time dt - !****************************** End of Main Solver *************************************** err = eqns_data%err @@ -620,7 +623,7 @@ subroutine setSolverParams(dt,nonlin_iter,ida_mem,retval) real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 - integer(kind = 8),parameter :: max_step = 999999 ! maximum number of steps, default = 500 + integer(c_long),parameter :: max_step = 999999 ! maximum number of steps, default = 500 real(qp) :: h_max ! maximum stepsize, default = infinity real(qp),parameter :: h_init = 0 ! initial stepsize @@ -633,7 +636,7 @@ subroutine setSolverParams(dt,nonlin_iter,ida_mem,retval) if (retval /= 0) return ! Set maximun number of nonliear iterations - retval = FIDASetMaxNonlinIters(ida_mem, nonlin_iter) + retval = FIDASetMaxNonlinIters(ida_mem, nonlin_iter) if (retval /= 0) return ! Set maximum number of convergence test failures diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index af74f6061..3d06bf337 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -30,6 +30,7 @@ module summaSolve4kinsol_module ! access missing values USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing double precision number ! access matrix information USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix @@ -118,16 +119,15 @@ subroutine summaSolve4kinsol(& USE fkinsol_mod ! Fortran interface to KINSOL USE fsundials_context_mod ! Fortran interface to SUNContext USE fnvector_serial_mod ! Fortran interface to serial N_Vector + USE fsundials_nvector_mod ! Fortran interface to generic N_Vector USE fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix - USE fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver USE fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix - USE fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver - USE fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver USE fsundials_matrix_mod ! Fortran interface to generic SUNMatrix - USE fsundials_nvector_mod ! Fortran interface to generic N_Vector + USE fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver + USE fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver - USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver USE allocspace_module,only:allocLocal ! allocate local data structures + USE getVectorz_module, only:checkFeas ! check feasibility of state vector USE eval8summa_module,only:eval8summa4kinsol ! DAE/ODE functions USE eval8summa_module,only:eval8summa ! residual of DAE USE computJacob_module,only:computJacob4kinsol ! system Jacobian @@ -141,8 +141,8 @@ subroutine summaSolve4kinsol(& ! input: model control real(rkind),intent(in) :: dt_cur ! current stepsize real(rkind),intent(in) :: dt ! data time step - real(rkind),intent(inout) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) - real(rkind),intent(inout) :: xScale(nState) ! characteristic scale of the state vector (mixed units) + real(rkind),intent(inout) :: fScale(:) ! characteristic scale of the function evaluations (mixed units) + real(rkind),intent(inout) :: xScale(:) ! characteristic scale of the state vector (mixed units) integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers @@ -190,13 +190,11 @@ subroutine summaSolve4kinsol(& type(data4kinsol), target :: eqns_data ! KINSOL type integer(i4b) :: retval, retvalr ! return value logical(lgt) :: feasible ! feasibility flag - integer(kind = 8) :: mu, lu ! in banded matrix mode in Sundials type - integer(c_long) :: nState ! total number of state variables in Sundials type + integer(c_long) :: mu, lu ! in banded matrix mode in SUNDIALS type + integer(c_long) :: nState ! total number of state variables in SUNDIALS type real(rkind) :: rVec(nStat) ! residual vector integer(i4b) :: iVar, i ! indices - type(var_dlength) :: flux_prev ! previous model fluxes for a local HRU - real(rkind),allocatable :: mLayerMatricHeadPrimePrev(:) ! previous derivative value for total water matric potential (m s-1) - real(rkind),allocatable :: dCompress_dPsiPrev(:) ! previous derivative value soil compression + character(LEN=256) :: cmessage ! error message of downwind routine logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn KINSOL warnings off, default true logical(lgt),parameter :: use_fdJac = .false. ! flag to use finite difference Jacobian, default false @@ -205,7 +203,7 @@ subroutine summaSolve4kinsol(& ! initialize error control err=0; message="summaSolve4kinsol/" - nState = nStat ! total number of state variables in Sundials type + nState = nStat ! total number of state variables in SUNDIALS type kinsolSucceeds = .true. ! fill eqns_data which will be required later to call eval8summa @@ -240,33 +238,12 @@ subroutine summaSolve4kinsol(& allocate( eqns_data%sMul(nState) ); eqns_data%sMul = sMul allocate( eqns_data%dMat(nState) ); eqns_data%dMat = dMat - ! allocate space for the to save previous fluxes - call allocLocal(flux_meta(:),flux_prev,nSnow,nSoil,err,message) - if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif - flux_prev = eqns_data%flux_data - ! allocate space for other variables if(model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel)then allocate(eqns_data%dBaseflow_dMatric(nSoil,nSoil),stat=err) else allocate(eqns_data%dBaseflow_dMatric(0,0),stat=err) end if - allocate( eqns_data%mLayerMatricHeadLiqTrial(nSoil) ) - allocate( eqns_data%mLayerMatricHeadTrial(nSoil) ) - allocate( eqns_data%mLayerMatricHeadPrev(nSoil) ) - allocate( eqns_data%mLayerVolFracWatTrial(nLayers) ) - allocate( eqns_data%mLayerVolFracWatPrev(nLayers) ) - allocate( eqns_data%mLayerTempTrial(nLayers) ) - allocate( eqns_data%mLayerTempPrev(nLayers) ) - allocate( eqns_data%mLayerVolFracIceTrial(nLayers) ) - allocate( eqns_data%mLayerVolFracIcePrev(nLayers) ) - allocate( eqns_data%mLayerVolFracLiqTrial(nLayers) ) - allocate( eqns_data%mLayerVolFracLiqPrev(nLayers) ) - allocate( eqns_data%mLayerEnthalpyTrial(nLayers) ) - allocate( eqns_data%mLayerEnthalpyPrev(nLayers) ) - allocate( eqns_data%mLayerMatricHeadPrime(nSoil) ) - allocate( mLayerMatricHeadPrimePrev(nSoil) ) - allocate( dCompress_dPsiPrev(nSoil) ) allocate( eqns_data%fluxVec(nState) ) allocate( eqns_data%resSink(nState) ) @@ -343,17 +320,13 @@ subroutine summaSolve4kinsol(& endif !*********************** Main Solver * loop on one_step mode ***************************** - eqns_data%firstFluxCall = .false. eqns_data%firstSplitOper = .true. ! Call KINSol to solve problem with choice of solver, linesearch or Picard !retval = FKINSol(kinsol_mem, sunvec_y, KIN_LINESEARCH, sunvec_xscale, sunvec_fscale) retval = FKINSol(kinsol_mem, sunvec_y, KIN_PICARD, sunvec_xscale, sunvec_fscale) - if( retval < 0 )then - kinsolSucceeds = .false. - exit - end if + if( retval < 0 ) kinsolSucceeds = .false. ! check the feasibility of the solution feasible=.true. @@ -371,10 +344,9 @@ subroutine summaSolve4kinsol(& ! early return for non-feasible solutions, will fail in current Sundials formulation if(.not.feasible)then eqns_data%fluxVec(:) = realMissing - message=trim(message)//'non-feasible' + message=trim(message)//trim(cmessage)//'non-feasible' return end if - !****************************** End of Main Solver *************************************** err = eqns_data%err @@ -416,7 +388,7 @@ end subroutine summaSolve4kinsol ! ---------------------------------------------------------------- ! SetInitialCondition: routine to initialize u vector. ! ---------------------------------------------------------------- - subroutine setInitialCondition(neq, y, sunvec_u, sunvec_up) + subroutine setInitialCondition(neq, y, sunvec_u) !======= Inclusions =========== USE, intrinsic :: iso_c_binding @@ -444,7 +416,7 @@ end subroutine setInitialCondition ! ---------------------------------------------------------------- ! setSolverParams: private routine to set parameters in KINSOL solver ! ---------------------------------------------------------------- - subroutine setSolverParams(lin_iter,kinsol_mem,retval) + subroutine setSolverParams(nonlin_iter,kinsol_mem,retval) !======= Inclusions =========== USE, intrinsic :: iso_c_binding @@ -459,10 +431,11 @@ subroutine setSolverParams(lin_iter,kinsol_mem,retval) integer(i4b),intent(out) :: retval ! return value !======= Internals ============ - integer,parameter :: mset = 1 ! maximum number of times the solver is called without Jacobian update, pass 0 to give default of 10 times - integer,parameter :: msubset = 1 ! maximum number of nonlinear iterations between checks by the residual monitoring algorithm, default=5 - integer,parameter :: maa = 0 ! maximum number of prior residuals to use acceleration, default = 0 - integer,parameter :: beta_fail = 50 ! maximum number of beta condition failures, default = 10 + integer(c_long) :: nonlin_itr ! maximum number of nonlinear iterations in SUNDIALS type + integer(c_long),parameter :: mset = 1 ! maximum number of times the solver is called without Jacobian update, pass 0 to give default of 10 times + integer(c_long),parameter :: msubset = 1 ! maximum number of nonlinear iterations between checks by the residual monitoring algorithm, default=5 + integer(c_long),parameter :: maa = 0 ! maximum number of prior residuals to use acceleration, default = 0 + integer(c_long),parameter :: beta_fail = 50 ! maximum number of beta condition failures, default = 10 real(qp),parameter :: fnormtol = 0.0 ! stopping tolerance on the scaled maximum norm of the system function, pass 0 to give default of unit_roundoff**(1/3) real(qp),parameter :: scsteptol = 0.0 ! stopping tolerance on the minimum scaled step length, pass 0 to give default of unit_roundoff**(2/3) @@ -471,11 +444,12 @@ subroutine setSolverParams(lin_iter,kinsol_mem,retval) if (retval /= 0) return ! Every msubset iterations, test if a Jacobian evaluation is necessary - ierr = FKINSetMaxSubSetupCalls(kinsol_mem, msubset) + retval = FKINSetMaxSubSetupCalls(kinsol_mem, msubset) if (retval /= 0) return ! Set maximum number of iterations - retval = FKINSetNumMaxIters(kinsol_mem, nonlin_iter) + nonlin_itr = nonlin_iter ! maximum number of nonlinear iterations in SUNDIALS type + retval = FKINSetNumMaxIters(kinsol_mem, nonlin_itr) if (retval /= 0) return ! Set maximum number of prior residuals to use for Anderson acceleration @@ -491,11 +465,10 @@ subroutine setSolverParams(lin_iter,kinsol_mem,retval) retval = FKINSetFuncNormTol(kinsol_mem, fnormtol) if (retval /= 0) return -\ ! Set stopping tolerance on the scaled maximum norm of the system function + ! Set stopping tolerance on the scaled maximum norm of the system function retval = FKINSetScaledStepTol(kinsol_mem, scsteptol) if (retval /= 0) return - end subroutine setSolverParams - \ No newline at end of file +end module summaSolve4kinsol_module \ No newline at end of file diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 8980e9483..b7503b994 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -222,8 +222,6 @@ subroutine systemSolv(& real(rkind),allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! NOTE: allocatable, since not always needed real(rkind) :: stateVecNew(nState) ! new state vector (mixed units) real(rkind) :: fluxVec0(nState) ! flux vector (mixed units) - real(rkind) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) - real(rkind) :: xScale(nState) ! characteristic scale of the state vector (mixed units) real(rkind) :: dMat(nState) ! diagonal matrix (excludes flux derivatives) real(qp) :: sMul(nState) ! NOTE: qp ! multiplier for state vector for the residual calculations real(qp) :: rVec(nState) ! NOTE: qp ! residual vector @@ -236,7 +234,7 @@ subroutine systemSolv(& type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt_out (=dt) real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! derivative value for total water matric potential (m s-1) - ! kinsol variables + ! kinsol and numrec variables real(rkind) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) real(rkind) :: xScale(nState) ! characteristic scale of the state vector (mixed units) ! numrec variables @@ -344,7 +342,7 @@ subroutine systemSolv(& if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif ! allocate space for mLayerCmpress_sum and mLayerMatricHeadPrime at the start of the time step - if(num_method==ida)then + if(ixNumericalMethod==ida)then allocate( mLayerCmpress_sum(nSoil) ) allocate( mLayerMatricHeadPrime(nSoil) ) else @@ -509,7 +507,7 @@ subroutine systemSolv(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) #endif - case(kinsol .or. numrec) + case(kinsol, numrec) call eval8summa(& ! input: model control dt_cur, & ! intent(in): current stepsize diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 83691f30a..76255ede9 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -413,7 +413,7 @@ subroutine varSubstep(& ! identify the need to check the mass balance select case(ixNumericalMethod) case(ida); checkMassBalance = .false. ! IDA has instantaneous fluxes only so average will not balance over data window - case(kinsol .or. numrec); checkMassBalance = .true. ! (.not.scalarSolution) + case(kinsol, numrec); checkMassBalance = .true. ! (.not.scalarSolution) case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or numrec (or itertive, which is numrec)'; return end select @@ -848,7 +848,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! output: error control err,cmessage) ! intent(out): error control #endif - case(kinsol .or. numrec) + case(kinsol, numrec) ! update diagnostic variables call updateVars(& ! input From f0ad6faa86cbcee61c06a4829855964e5a11ed1e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 20 Jun 2023 12:46:06 +0900 Subject: [PATCH 0753/1472] set initial params after later in ida --- build/source/engine/summaSolve4ida.f90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index c72c01b1d..959601e4c 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -327,10 +327,6 @@ subroutine summaSolve4ida( & retval = FIDASetUserData(ida_mem, c_loc(eqns_data)) if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetUserData'; return; endif - ! Set solver parameters before calling FIDAInit - call setSolverParams(dt, nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)), ida_mem, retval) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in setSolverParams'; return; endif - ! Set the function IDA will use to advance the state t0 = 0._rkind retval = FIDAInit(ida_mem, c_funloc(eval8summa4ida), t0, sunvec_y, sunvec_yp) @@ -407,6 +403,10 @@ subroutine summaSolve4ida( & retval = FIDASetStopTime(ida_mem, dt) if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetStopTime'; return; endif + ! Set solver parameters at end of setup + call setSolverParams(dt, nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)), ida_mem, retval) + if (retval /= 0) then; err=20; message='summaSolve4ida: error in setSolverParams'; return; endif + ! Disable error messages and warnings if(offErrWarnMessage) then retval = FIDASetErrFile(ida_mem, c_null_ptr) From a1aafc01fa84b84f2c7c6059107f3af5bac48d3a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 20 Jun 2023 16:18:08 +0900 Subject: [PATCH 0754/1472] make 0 out no veg water --- build/cmake/build.mac.bash | 2 +- build/source/driver/summa_modelRun.f90 | 2 +- build/source/engine/coupled_em.f90 | 2 +- build/source/engine/updateVarsWithPrime.f90 | 40 ++++++++++----------- build/source/engine/vegPhenlgy.f90 | 17 ++++----- 5 files changed, 29 insertions(+), 34 deletions(-) diff --git a/build/cmake/build.mac.bash b/build/cmake/build.mac.bash index dc929bf1f..87b371c00 100755 --- a/build/cmake/build.mac.bash +++ b/build/cmake/build.mac.bash @@ -2,6 +2,6 @@ # build on Mac, from cmake directory run this as ./build.mac.bash -cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials +cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials_Debug cmake --build ../cmake_build --target all diff --git a/build/source/driver/summa_modelRun.f90 b/build/source/driver/summa_modelRun.f90 index cfe271458..dae3e4b1b 100644 --- a/build/source/driver/summa_modelRun.f90 +++ b/build/source/driver/summa_modelRun.f90 @@ -139,7 +139,7 @@ subroutine summa_runPhysics(modelTimeStep, summa1_struc, err, message) typeStruct%gru(iGRU)%hru(iHRU), & ! intent(in): type of vegetation and soil attrStruct%gru(iGRU)%hru(iHRU), & ! intent(in): spatial attributes mparStruct%gru(iGRU)%hru(iHRU), & ! intent(in): model parameters - progStruct%gru(iGRU)%hru(iHRU), & ! intent(in): model prognostic variables for a local HRU + progStruct%gru(iGRU)%hru(iHRU), & ! intent(inout): model prognostic variables for a local HRU diagStruct%gru(iGRU)%hru(iHRU), & ! intent(inout): model diagnostic variables for a local HRU ! output computeVegFluxFlag, & ! intent(out): flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index df201c64f..47b60ef55 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -443,7 +443,7 @@ subroutine coupled_em(& type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU + prog_data, & ! intent(inout): model prognostic variables for a local HRU diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! output computeVegFlux, & ! intent(out): flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) diff --git a/build/source/engine/updateVarsWithPrime.f90 b/build/source/engine/updateVarsWithPrime.f90 index 47c05a2d0..0bd5b76d8 100644 --- a/build/source/engine/updateVarsWithPrime.f90 +++ b/build/source/engine/updateVarsWithPrime.f90 @@ -168,19 +168,19 @@ subroutine updateVarsWithPrime(& real(rkind),intent(inout) :: mLayerMatricHeadPrime(:) ! trial value of time derivative total water matric potential (m) real(rkind),intent(inout) :: mLayerMatricHeadLiqPrime(:) ! trial value of time derivative liquid water matric potential (m) ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- ! general local variables - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixDomainType ! name of a given model domain - integer(i4b) :: ixControlIndex ! index within a given model domain - integer(i4b) :: ixOther,ixOtherLocal ! index of the coupled state variable within the (full, local) vector - logical(lgt) :: isCoupled ! .true. if a given variable shared another state variable in the same control volume - logical(lgt) :: isNrgState ! .true. if a given variable is an energy state - logical(lgt),allocatable :: computedCoupling(:) ! .true. if computed the coupling for a given state variable + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixDomainType ! name of a given model domain + integer(i4b) :: ixControlIndex ! index within a given model domain + integer(i4b) :: ixOther,ixOtherLocal ! index of the coupled state variable within the (full, local) vector + logical(lgt) :: isCoupled ! .true. if a given variable shared another state variable in the same control volume + logical(lgt) :: isNrgState ! .true. if a given variable is an energy state + logical(lgt),allocatable :: computedCoupling(:) ! .true. if computed the coupling for a given state variable real(rkind) :: scalarVolFracLiq ! volumetric fraction of liquid water (-) real(rkind) :: scalarVolFracIce ! volumetric fraction of ice (-) real(rkind) :: scalarVolFracLiqPrime ! time derivative volumetric fraction of liquid water (-) @@ -190,23 +190,23 @@ subroutine updateVarsWithPrime(& real(rkind) :: fLiq ! fraction of liquid water (-) real(rkind) :: effSat ! effective saturation (-) real(rkind) :: avPore ! available pore space (-) - character(len=256) :: cMessage ! error message of downwind routine - logical(lgt),parameter :: printFlag=.false. ! flag to turn on printing + character(len=256) :: cMessage ! error message of downwind routine + logical(lgt),parameter :: printFlag=.false. ! flag to turn on printing ! iterative solution for temperature real(rkind) :: meltNrg ! energy for melt+freeze (J m-3) real(rkind) :: residual ! residual in the energy equation (J m-3) real(rkind) :: derivative ! derivative in the energy equation (J m-3 K-1) real(rkind) :: tempInc ! iteration increment (K) - integer(i4b) :: iter ! iteration index - integer(i4b) :: niter ! number of iterations - integer(i4b),parameter :: maxiter=100 ! maximum number of iterations - real(rkind),parameter :: nrgConvTol=1.e-4_rkind ! convergence tolerance for energy (J m-3) - real(rkind),parameter :: tempConvTol=1.e-6_rkind ! convergence tolerance for temperature (K) + integer(i4b) :: iter ! iteration index + integer(i4b) :: niter ! number of iterations + integer(i4b),parameter :: maxiter=100 ! maximum number of iterations + real(rkind),parameter :: nrgConvTol=1.e-4_rkind ! convergence tolerance for energy (J m-3) + real(rkind),parameter :: tempConvTol=1.e-6_rkind ! convergence tolerance for temperature (K) real(rkind) :: critDiff ! temperature difference from critical (K) real(rkind) :: tempMin ! minimum bracket for temperature (K) real(rkind) :: tempMax ! maximum bracket for temperature (K) - logical(lgt) :: bFlag ! flag to denote that iteration increment was constrained using bi-section - real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical temperature (K) + logical(lgt) :: bFlag ! flag to denote that iteration increment was constrained using bi-section + real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical temperature (K) ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures associate(& diff --git a/build/source/engine/vegPhenlgy.f90 b/build/source/engine/vegPhenlgy.f90 index 51fec4552..6e6d5c1c5 100755 --- a/build/source/engine/vegPhenlgy.f90 +++ b/build/source/engine/vegPhenlgy.f90 @@ -73,7 +73,7 @@ subroutine vegPhenlgy(& type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): prognostic variables for a local HRU + prog_data, & ! intent(inout): prognostic variables for a local HRU diag_data, & ! intent(inout): diagnostic variables for a local HRU ! output computeVegFlux, & ! intent(out): flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) @@ -91,7 +91,7 @@ subroutine vegPhenlgy(& type(var_i),intent(in) :: type_data ! type of vegetation and soil type(var_d),intent(in) :: attr_data ! spatial attributes type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU ! output logical(lgt),intent(out) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) @@ -110,33 +110,27 @@ subroutine vegPhenlgy(& ! ---------------------------------------------------------------------------------------------------------------------------------- ! associate variables in the data structure associate(& - ! input: model decisions ix_bcUpprTdyn => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision, & ! intent(in): [i4b] choice of upper boundary condition for thermodynamics ix_bcUpprSoiH => model_decisions(iLookDECISIONS%bcUpprSoiH)%iDecision, & ! intent(in): [i4b] index of method used for the upper boundary condition for soil hydrology - ! local attributes vegTypeIndex => type_data%var(iLookTYPE%vegTypeIndex), & ! intent(in): [i4b] vegetation type index latitude => attr_data%var(iLookATTR%latitude), & ! intent(in): [dp] latitude - ! model state variables scalarSnowDepth => prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(in): [dp] snow depth on the ground surface (m) scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1), & ! intent(in): [dp] temperature of the vegetation canopy at the start of the sub-step (K) - + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1), & ! intent(in): [dp] liquid water in the vegetation canopy at the start of the sub-step ! diagnostic variables and parameters (input) heightCanopyTop => mpar_data%var(iLookPARAM%heightCanopyTop)%dat(1), & ! intent(in): [dp] height of the top of the canopy layer (m) heightCanopyBottom => mpar_data%var(iLookPARAM%heightCanopyBottom)%dat(1), & ! intent(in): [dp] height of the bottom of the canopy layer (m) scalarRootZoneTemp => diag_data%var(iLookDIAG%scalarRootZoneTemp)%dat(1), & ! intent(in): [dp] root zone temperature (K) - ! diagnostic variables and parameters (input/output) scalarLAI => diag_data%var(iLookDIAG%scalarLAI)%dat(1), & ! intent(inout): [dp] one-sided leaf area index (m2 m-2) scalarSAI => diag_data%var(iLookDIAG%scalarSAI)%dat(1), & ! intent(inout): [dp] one-sided stem area index (m2 m-2) - ! diagnostic variables and parameters (output) scalarExposedLAI => diag_data%var(iLookDIAG%scalarExposedLAI)%dat(1), & ! intent(out): [dp] exposed leaf area index after burial by snow (m2 m-2) scalarExposedSAI => diag_data%var(iLookDIAG%scalarExposedSAI)%dat(1), & ! intent(out): [dp] exposed stem area index after burial by snow (m2 m-2) scalarGrowingSeasonIndex => diag_data%var(iLookDIAG%scalarGrowingSeasonIndex)%dat(1) & ! intent(out): [dp] growing season index (0=off, 1=on) - ) ! associate variables in data structure ! ---------------------------------------------------------------------------------------------------------------------------------- @@ -186,8 +180,9 @@ subroutine vegPhenlgy(& ! determine if need to include vegetation in the energy flux routines computeVegFlux = (exposedVAI > 0.05_rkind .and. heightAboveSnow > 0.05_rkind) - !write(*,'(a,1x,i2,1x,L1,1x,10(f12.5,1x))') 'vegTypeIndex, computeVegFlux, heightCanopyTop, heightAboveSnow, scalarSnowDepth = ', & - ! vegTypeIndex, computeVegFlux, heightCanopyTop, heightAboveSnow, scalarSnowDepth + + ! if no vegetation ever, should not have initialized scalarCanopyLiq to 0.0001 in read_icond.f90 + if((scalarLAI + scalarSAI) == 0.0_rkind) scalarCanopyLiq = 0.0_rkind end if ! (check if the snow-soil column is isolated) From 3ec5509a743569243286fdeb62e9c244c05e7807 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 20 Jun 2023 16:42:44 +0900 Subject: [PATCH 0755/1472] don't free nonlinear solver since didn't have to attach it --- build/source/engine/summaSolve4ida.f90 | 2 -- 1 file changed, 2 deletions(-) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 959601e4c..e45b6310c 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -555,8 +555,6 @@ subroutine summaSolve4ida( & deallocate( rootdir ) call FIDAFree(ida_mem) - retval = FSUNNonlinSolFree(sunnonlin_NLS) - if(retval /= 0)then; err=20; message='summaSolve4ida: unable to free the nonlinear solver'; return; endif retval = FSUNLinSolFree(sunlinsol_LS) if(retval /= 0)then; err=20; message='summaSolve4ida: unable to free the linear solver'; return; endif call FSUNMatDestroy(sunmat_A) From 9345c119cf83b42e1922b2fc80d5d60e5f1a3e5a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 20 Jun 2023 18:18:58 +0900 Subject: [PATCH 0756/1472] changing indx_data in solver so need to recompute layer variables --- build/source/engine/systemSolv.f90 | 63 +++++++++++++++++++----------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index b7503b994..57ae92d11 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -290,13 +290,7 @@ subroutine systemSolv(& ! model state variables (aquifer) scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in): [dp] storage of water in the aquifer (m) ! check the need to merge snow layers - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ! vector of energy and hydrology indices for the snow and soil domains - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology state variables in the snow+soil domain + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) ! mapping from full domain to the sub-domain ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b] mapping of full state vector to the state subset ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b] index of control volume for different domains (veg, snow, soil) @@ -670,8 +664,19 @@ subroutine systemSolv(& end do ! compute the total change in storage associated with compression of the soil matrix (kg m-2) - diag_data%var(iLookDIAG%mLayerCompress)%dat(:) = mLayerCmpress_sum(:) / dt_out - diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sum(diag_data%var(iLookDIAG%mLayerCompress)%dat(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water + ! need to recompute layer geometry because indx_data is part of the SUNDIALS memory + layerVars: associate(& + ! layer geometry + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! total number of layers + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! depth of each layer in the snow-soil sub-domain (m) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! change in storage associated with compression of the soil matrix (-) + scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) & ! total change in storage associated with compression of the soil matrix (kg m-2 s-1) + ) + mLayerCompress = mLayerCmpress_sum / dt_out + scalarSoilCompress = sum(mLayerCompress(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water + end associate layerVars case(kinsol) !--------------------------- @@ -819,23 +824,35 @@ subroutine systemSolv(& ! Post processing step to “perfectly” conserve mass by pushing the errors into the state variables ! Turn off for now to agree with SUNDIALS ! ------------------ + !layerVars: associate(& + ! vector of energy and hydrology indices for the snow and soil domains + ! ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ! ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + ! nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + ! nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology state variables in the snow+soil domain + ! layer geometry + ! nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + ! nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers + ! nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in): [i4b] total number of layers + ! ) ! ! update temperatures (ensure new temperature is consistent with the fluxes) !if(nSnowSoilNrg>0)then - ! do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - ! iState = ixSnowSoilNrg(iLayer) - ! stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState))/real(sMul(iState), rkind) - ! end do ! looping through non-missing energy state variables in the snow+soil domain - !endif - ! - ! update volumetric water content in the snow (ensure change in state is consistent with the fluxes) - ! NOTE: for soil water balance is constrained within the iteration loop - !if(nSnowSoilHyd>0)then - ! do concurrent (iLayer=1:nSnow,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing water state variables in the snow domain) - ! iState = ixSnowSoilHyd(iLayer) - ! stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState)) - ! end do ! looping through non-missing water state variables in the soil domain - !endif + ! do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + ! iState = ixSnowSoilNrg(iLayer) + ! stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState))/real(sMul(iState), rkind) + ! end do ! looping through non-missing energy state variables in the snow+soil domain + !endif + ! + ! update volumetric water content in the snow (ensure change in state is consistent with the fluxes) + ! NOTE: for soil water balance is constrained within the iteration loop + !if(nSnowSoilHyd>0)then + ! do concurrent (iLayer=1:nSnow,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing water state variables in the snow domain) + ! iState = ixSnowSoilHyd(iLayer) + ! stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState)) + ! end do ! looping through non-missing water state variables in the soil domain + !endif + !end associate layerVars end select From 7e102f225d1f48d3c568c4b2b25fd74468391b06 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 20 Jun 2023 19:38:12 +0900 Subject: [PATCH 0757/1472] only change flux calculation number in indx_data inside solver --- build/source/engine/coupled_em.f90 | 6 +++--- build/source/engine/opSplittin.f90 | 8 ++++---- build/source/engine/summaSolve4ida.f90 | 2 +- build/source/engine/summaSolve4kinsol.f90 | 2 +- build/source/engine/systemSolv.f90 | 10 +--------- build/source/engine/varSubstep.f90 | 2 +- 6 files changed, 11 insertions(+), 19 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 47b60ef55..d65338472 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1192,9 +1192,9 @@ subroutine coupled_em(& ! identify the need to check the mass balance, both methods should work if tolerance coarse enough select case(ixNumericalMethod) - case(ida); checkMassBalance = .false. ! Sundials gives instantaneous fluxes and were summed for an average flux, but if large time step, then average is not accurate enough to pass the check - case(numrec); checkMassBalance = .true. ! numrec gives finite difference dt_sub fluxes and were summed for an average flux - case default; err=20; message=trim(message)//'expect num_method to be sundials, kinsol, or numrec (or itertive, which is numrec)'; return + case(ida); checkMassBalance = .false. ! IDA gives instantaneous fluxes and were summed for an average flux, but if large time step, then average is not accurate enough to pass the check + case(kinsol, numrec); checkMassBalance = .true. ! KINSOL or numrec give finite difference dt_sub fluxes and were summed for an average flux + case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or numrec (or itertive, which is numrec)'; return end select ! ----- diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 1ec220cab..493d64a72 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -316,11 +316,11 @@ subroutine opSplittin(& ! initialize error control err=0; message="opSplittin/" - ! we just solve the fully coupled problem by with Sundials for now + ! we just solve the fully coupled problem if IDA for now, splitting can happen otherwise select case(ixNumericalMethod) - case(ida); nCoupling = 1 - case(numrec); nCoupling = 2 - case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol , or numrec (or itertive, which is numrec)'; return + case(ida); nCoupling = 1 + case(kinsol, numrec); nCoupling = 2 + case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or numrec (or itertive, which is numrec)'; return end select ! ----- diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index e45b6310c..1ab9b3834 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -521,12 +521,12 @@ subroutine summaSolve4ida( & if(idaSucceeds)then ! copy to output data - indx_data = eqns_data%indx_data diag_data = eqns_data%diag_data flux_data = eqns_data%flux_data deriv_data = eqns_data%deriv_data ixSaturation = eqns_data%ixSaturation dt_out = tret(1) ! should be dt, probably do not need to keep this + indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = eqns_data%indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) !only number of Flux Calculations changes endif ! free memory diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index 3d06bf337..dbb0699ba 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -355,11 +355,11 @@ subroutine summaSolve4kinsol(& if(kinsolSucceeds)then ! copy to output data - indx_data = eqns_data%indx_data diag_data = eqns_data%diag_data flux_data = eqns_data%flux_data deriv_data = eqns_data%deriv_data ixSaturation = eqns_data%ixSaturation + indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = eqns_data%indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) !only number of Flux Calculations changes endif ! free memory diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 57ae92d11..3662e4a24 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -664,12 +664,8 @@ subroutine systemSolv(& end do ! compute the total change in storage associated with compression of the soil matrix (kg m-2) - ! need to recompute layer geometry because indx_data is part of the SUNDIALS memory layerVars: associate(& - ! layer geometry - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! total number of layers + ! layer geometry mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! depth of each layer in the snow-soil sub-domain (m) mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! change in storage associated with compression of the soil matrix (-) scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) & ! total change in storage associated with compression of the soil matrix (kg m-2 s-1) @@ -830,10 +826,6 @@ subroutine systemSolv(& ! ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain ! nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain ! nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology state variables in the snow+soil domain - ! layer geometry - ! nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - ! nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers - ! nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in): [i4b] total number of layers ! ) ! ! update temperatures (ensure new temperature is consistent with the fluxes) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 76255ede9..f85ee6d0d 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -412,7 +412,7 @@ subroutine varSubstep(& ! identify the need to check the mass balance select case(ixNumericalMethod) - case(ida); checkMassBalance = .false. ! IDA has instantaneous fluxes only so average will not balance over data window + case(ida); checkMassBalance = .false. ! IDA has instantaneous fluxes only so average will not balance over data window case(kinsol, numrec); checkMassBalance = .true. ! (.not.scalarSolution) case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or numrec (or itertive, which is numrec)'; return end select From cd6f07f1b5daf998bd6f59e7cb3a6eec5393237f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 20 Jun 2023 23:53:18 +0900 Subject: [PATCH 0758/1472] clarifying firstSplitOper (seems to be working as expected) and bug in run_oneGRU Aquifer recharge --- build/source/driver/summa_restart.f90 | 5 +++- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/eval8summaWithPrime.f90 | 2 +- build/source/engine/run_oneGRU.f90 | 2 +- build/source/engine/soilLiqFlx.f90 | 26 ++++++++++----------- build/source/engine/summaSolve4ida.f90 | 5 ++-- build/source/engine/summaSolve4kinsol.f90 | 7 +++--- 7 files changed, 26 insertions(+), 23 deletions(-) diff --git a/build/source/driver/summa_restart.f90 b/build/source/driver/summa_restart.f90 index 1bef46d47..e58e97e7d 100644 --- a/build/source/driver/summa_restart.f90 +++ b/build/source/driver/summa_restart.f90 @@ -202,11 +202,14 @@ subroutine summa_readRestart(summa1_struc, err, message) ! the basin-average aquifer storage is not used if the groundwater is included in the local column case(localColumn) bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 0._rkind ! set to zero to be clear that there is no basin-average aquifer storage in this configuration + do iHRU=1,gru_struc(iGRU)%hruCount + progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarAquiferStorage)%dat(1) = 0._rkind ! 1.0 was default but gives runoff pulse at beginning of simulation so now 0.0 + end do ! the local column aquifer storage is not used if the groundwater is basin-average ! (i.e., where multiple HRUs drain to a basin-average aquifer) case(singleBasin) - bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 1._rkind + bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 0._rkind ! 1.0 was default but gives runoff pulse at beginning of simulation so now 0.0 do iHRU=1,gru_struc(iGRU)%hruCount progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarAquiferStorage)%dat(1) = 0._rkind ! set to zero to be clear that there is no local aquifer storage in this configuration end do diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index c48e237ae..f370b60bd 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -701,7 +701,7 @@ integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & .true., & ! intent(in): inside SUNDIALS solver eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation + eqns_data%firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 23a1ccf3a..2a5664e47 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -633,7 +633,7 @@ subroutine eval8summaWithPrime(& err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - firstSplitOper = .true. + firstSplitOper = .false. ! after call computeFlux once in dt, no longer firstSplitOper ! compute soil compressibility (-) and its derivative w.r.t. matric head (m) ! NOTE: we already extracted trial matrix head and volumetric liquid water as part of the flux calculations diff --git a/build/source/engine/run_oneGRU.f90 b/build/source/engine/run_oneGRU.f90 index ee2a03889..5002878a8 100644 --- a/build/source/engine/run_oneGRU.f90 +++ b/build/source/engine/run_oneGRU.f90 @@ -252,7 +252,7 @@ subroutine run_oneGRU(& ! NOTE: groundwater computed later for singleBasin if(model_decisions(iLookDECISIONS%spatial_gw)%iDecision == localColumn .and. model_decisions(iLookDECISIONS%groundwatr)%iDecision == bigBucket) then - bvarData%var(iLookBVAR%basin__AquiferRecharge)%dat(1) = bvarData%var(iLookBVAR%basin__AquiferRecharge)%dat(1) + fluxHRU%hru(iHRU)%var(iLookFLUX%scalarSoilDrainage)%dat(1) * fracHRU + bvarData%var(iLookBVAR%basin__AquiferRecharge)%dat(1) = bvarData%var(iLookBVAR%basin__AquiferRecharge)%dat(1) + fluxHRU%hru(iHRU)%var(iLookFLUX%scalarAquiferRecharge)%dat(1) * fracHRU bvarData%var(iLookBVAR%basin__AquiferTranspire)%dat(1) = bvarData%var(iLookBVAR%basin__AquiferTranspire)%dat(1) + fluxHRU%hru(iHRU)%var(iLookFLUX%scalarAquiferTranspire)%dat(1) * fracHRU bvarData%var(iLookBVAR%basin__AquiferBaseflow)%dat(1) = bvarData%var(iLookBVAR%basin__AquiferBaseflow)%dat(1) & + fluxHRU%hru(iHRU)%var(iLookFLUX%scalarAquiferBaseflow)%dat(1) * fracHRU diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index df51f376c..63f39655a 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -91,7 +91,7 @@ module soilLiqFlx_module subroutine soilLiqFlx(& ! input: model control nSoil, & ! intent(in): number of soil layers - doInfiltrate, & ! intent(in): flag to compute infiltration + firstSplitOper, & ! intent(in): flag to compute infiltration, if firstSplitOper scalarSolution, & ! intent(in): flag to indicate the scalar solution deriv_desired, & ! intent(in): flag indicating if derivatives are desired ! input: trial state variables @@ -161,7 +161,7 @@ subroutine soilLiqFlx(& implicit none ! input: model control integer(i4b),intent(in) :: nSoil ! number of soil layers - logical(lgt),intent(in) :: doInfiltrate ! flag to compute infiltration + logical(lgt),intent(in) :: firstSplitOper ! flag to compute infiltration logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution logical(lgt),intent(in) :: deriv_desired ! flag indicating if derivatives are desired ! input: trial model state variables @@ -508,7 +508,7 @@ subroutine soilLiqFlx(& call surfaceFlx(& ! input: model control - doInfiltrate, & ! intent(in): flag indicating if desire to compute infiltration + firstSplitOper, & ! intent(in): flag indicating if desire to compute infiltration desireAnal, & ! intent(in): flag indicating if derivatives are desired ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) ixBcUpperSoilHydrology, & ! intent(in): index defining the type of boundary conditions (neumann or diriclet) @@ -1097,7 +1097,7 @@ end subroutine diagv_node ! *************************************************************************************************************** subroutine surfaceFlx(& ! input: model control - doInfiltration, & ! intent(in): flag indicating if desire to compute infiltration + firstSplitOper, & ! intent(in): flag indicating if desire to compute infiltration deriv_desired, & ! intent(in): flag indicating if derivatives are desired ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) bc_upper, & ! intent(in): index defining the type of boundary conditions (neumann or diriclet) @@ -1166,7 +1166,7 @@ subroutine surfaceFlx(& implicit none ! ----------------------------------------------------------------------------------------------------------------------------- ! input: model control - logical(lgt),intent(in) :: doInfiltration ! flag indicating if desire to compute infiltration + logical(lgt),intent(in) :: firstSplitOper ! flag indicating if desire to compute infiltration logical(lgt),intent(in) :: deriv_desired ! flag to indicate if derivatives are desired integer(i4b),intent(in) :: bc_upper ! index defining the type of boundary conditions integer(i4b),intent(in) :: ixRichards ! index defining the option for Richards' equation (moisture or mixdform) @@ -1265,8 +1265,8 @@ subroutine surfaceFlx(& real(rkind) :: dRootZoneIce_dTk(1:nSoil) ! derivative in vol fraction of scalar root zone ice w.r.t. temperature in root layers real(rkind) :: dDepthWettingFront_dWat(1:nSoil) ! derivative in scalar depth of wetting front w.r.t. water state variable in root layers real(rkind) :: dDepthWettingFront_dTk(1:nSoil) ! derivative in scalar depth of wetting front w.r.t. temperature in root layers - real(rkind) :: dXMaxInfilRate_dWat(1:nSoil) ! derivative in scalar max infiltration rate w.r.t. water state variable in root layers - real(rkind) :: dXMaxInfilRate_dTk(1:nSoil) ! derivative in scalar max infiltration rate w.r.t. temperature in root layers + real(rkind) :: dxMaxInfilRate_dWat(1:nSoil) ! derivative in scalar max infiltration rate w.r.t. water state variable in root layers + real(rkind) :: dxMaxInfilRate_dTk(1:nSoil) ! derivative in scalar max infiltration rate w.r.t. temperature in root layers real(rkind) :: dInfilArea_dWat(0:nSoil) ! derivative in scalar infiltration rate w.r.t. water state variable in canopy or snow and root layers real(rkind) :: dInfilArea_dTk(0:nSoil) ! derivative in scalar infiltration rate w.r.t. temperature in canopy or snow and root layers real(rkind) :: dFrozenArea_dWat(0:nSoil) ! derivative in scalar frozen area w.r.t. water state variable in canopy or snow and root layers @@ -1328,7 +1328,7 @@ subroutine surfaceFlx(& case(liquidFlux) ! force infiltration to be constant over the iterations - if(doInfiltration)then + if(firstSplitOper)then ! (process root layers only liquid and ice derivatives) dVolFracLiq_dWat(:) = 0._rkind @@ -1405,10 +1405,10 @@ subroutine surfaceFlx(& fPart2 = (wettingFrontSuction + depthWettingFront)/depthWettingFront dPart1(:) = surfaceSatHydCond*(zScale_TOPMODEL - 1._rkind) * ( (1._rkind - depthWettingFront/sum(mLayerDepth))**(zScale_TOPMODEL - 2._rkind) ) * (-dDepthWettingFront_dWat(:))/sum(mLayerDepth) dPart2(:) = -dDepthWettingFront_dWat(:)*wettingFrontSuction / (depthWettingFront**2._rkind) - dXMaxInfilRate_dWat(:) = fPart1*dpart2(:) + fPart2*dPart1(:) + dxMaxInfilRate_dWat(:) = fPart1*dpart2(:) + fPart2*dPart1(:) dPart1(:) = surfaceSatHydCond*(zScale_TOPMODEL - 1._rkind) * ( (1._rkind - depthWettingFront/sum(mLayerDepth))**(zScale_TOPMODEL - 2._rkind) ) * (-dDepthWettingFront_dTk(:))/sum(mLayerDepth) dPart2(:) = -dDepthWettingFront_dTk(:)*wettingFrontSuction / (depthWettingFront**2._rkind) - dXMaxInfilRate_dTk(:) = fPart1*dpart2(:) + fPart2*dPart1(:) + dxMaxInfilRate_dTk(:) = fPart1*dpart2(:) + fPart2*dPart1(:) ! define the infiltrating area and derivatives for the non-frozen part of the cell/basin if(qSurfScale < qSurfScaleMax)then @@ -1457,11 +1457,11 @@ subroutine surfaceFlx(& dFrozenArea_dTk(0) = 0._rkind - if (xMaxInfilRate < scalarRainPlusMelt) then ! = dXMaxInfilRate_d, dependent on layers not at surface + if (xMaxInfilRate < scalarRainPlusMelt) then ! = dxMaxInfilRate_d, dependent on layers not at surface dInfilRate_dWat(0) = 0._rkind dInfilRate_dTk(0) = 0._rkind - dInfilRate_dWat(1:nSoil) = dXMaxInfilRate_dWat(:) - dInfilRate_dTk(1:nSoil) = dXMaxInfilRate_dTk(:) + dInfilRate_dWat(1:nSoil) = dxMaxInfilRate_dWat(:) + dInfilRate_dTk(1:nSoil) = dxMaxInfilRate_dTk(:) else ! = dRainPlusMelt_d, dependent on above layer (canopy or snow) water and temp dInfilRate_dWat(0) = above_soilLiqFluxDeriv*above_soilFracLiq dInfilRate_dTk(0) = above_soilLiqFluxDeriv*above_soildLiq_dTk diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 1ab9b3834..810429f64 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -426,8 +426,9 @@ subroutine summaSolve4ida( & if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetRootDirection'; return; endif endif - eqns_data%firstFluxCall = .false. - eqns_data%firstSplitOper = .true. + eqns_data%firstFluxCall = .false. ! already called for initial + eqns_data%firstSplitOper = .true. ! always true at start of dt since no splitting + ! call IDASolve, advance solver just one internal step retvalr = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) if( retvalr < 0 )then diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index dbb0699ba..ff3e1a71a 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -214,6 +214,8 @@ subroutine summaSolve4kinsol(& eqns_data%nLayers = nLayers eqns_data%nState = nState eqns_data%ixMatrix = ixMatrix + eqns_data%firstFluxCall = .false. ! already called for initial + eqns_data%firstSplitOper = .false. ! already called for initial and false inside solver ??? eqns_data%firstSubStep = firstSubStep eqns_data%computeVegFlux = computeVegFlux eqns_data%scalarSolution = scalarSolution @@ -319,10 +321,7 @@ subroutine summaSolve4kinsol(& retval = FKINSetErrFile(kinsol_mem, c_null_ptr) endif - !*********************** Main Solver * loop on one_step mode ***************************** - eqns_data%firstFluxCall = .false. - eqns_data%firstSplitOper = .true. - + !****************************** Main Solver ********************************************** ! Call KINSol to solve problem with choice of solver, linesearch or Picard !retval = FKINSol(kinsol_mem, sunvec_y, KIN_LINESEARCH, sunvec_xscale, sunvec_fscale) retval = FKINSol(kinsol_mem, sunvec_y, KIN_PICARD, sunvec_xscale, sunvec_fscale) From 758781cc867ef86814c702a8087783344f15ca60 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 21 Jun 2023 17:10:18 +0900 Subject: [PATCH 0759/1472] comments --- build/source/engine/opSplittin.f90 | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 493d64a72..d44cec2a7 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -525,10 +525,10 @@ subroutine opSplittin(& ! identify the type of state for the states in the subset stateSubset: associate(ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] indices of state types - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) - ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat ,& ! intent(in): [i4b(:)] list of indices for all active layers (inactive=integerM - ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ) ! intent(in): [i4b(:)] indices defining the type of the domain (iname_veg, iname_snow, iname_soil) + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) + ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat ,& ! intent(in): [i4b(:)] list of indices for all active layers (inactive=integerM + ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ) ! intent(in): [i4b(:)] indices defining the type of the domain (iname_veg, iname_snow, iname_soil) ! loop through flux variables do iVar=1,size(flux_meta) @@ -650,7 +650,7 @@ subroutine opSplittin(& ! reset the flag for the first flux call if(.not.firstSuccess) firstFluxCall=.true. - ! update variables, also updated inside Sundials (if fail a split will need these) + ! update variables, also updated inside SUNDIALS (if fail a split will need these) ! save/recover copies of prognostic variables do iVar=1,size(prog_data%var) select case(failure) @@ -828,6 +828,7 @@ subroutine opSplittin(& ! check that all state variables were updated if(any(stateCheck==0))then + print*,stateCheck message=trim(message)//'some state variables were not updated!' err=20; return endif From 33ba6e9feb6491450ee4ab26ea39eed9f80ef586 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 21 Jun 2023 18:15:15 +0900 Subject: [PATCH 0760/1472] just some flags --- build/source/engine/opSplittin.f90 | 10 +++--- build/source/engine/systemSolv.f90 | 58 +++++++++++++++--------------- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index d44cec2a7..796fb27a2 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -524,11 +524,11 @@ subroutine opSplittin(& ! --------------------------------------- ! identify the type of state for the states in the subset - stateSubset: associate(ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] indices of state types - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) - ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat ,& ! intent(in): [i4b(:)] list of indices for all active layers (inactive=integerM - ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ) ! intent(in): [i4b(:)] indices defining the type of the domain (iname_veg, iname_snow, iname_soil) + stateSubset: associate(ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] indices of state types + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) + ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat ,& ! intent(in): [i4b(:)] list of indices for all active layers (inactive=integerM + ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ) ! intent(in): [i4b(:)] indices defining the type of the domain (iname_veg, iname_snow, iname_soil) ! loop through flux variables do iVar=1,size(flux_meta) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 3662e4a24..f3511ef04 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -247,6 +247,7 @@ subroutine systemSolv(& real(rkind) :: resSinkNew(nState) ! additional terms in the residual vector numrec real(rkind) :: fluxVecNew(nState) ! new flux vector numrec real(qp) :: resVecNew(nState) ! NOTE: qp ! new residual vector numrec + logical(lgt), parameter :: post_massCons=.false. ! “perfectly” conserve mass by pushing the errors into the states, turn off for now to agree with SUNDIALS ! enthalpy derivatives real(rkind) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state @@ -664,7 +665,7 @@ subroutine systemSolv(& end do ! compute the total change in storage associated with compression of the soil matrix (kg m-2) - layerVars: associate(& + soilVars: associate(& ! layer geometry mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! depth of each layer in the snow-soil sub-domain (m) mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! change in storage associated with compression of the soil matrix (-) @@ -672,7 +673,7 @@ subroutine systemSolv(& ) mLayerCompress = mLayerCmpress_sum / dt_out scalarSoilCompress = sum(mLayerCompress(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water - end associate layerVars + end associate soilVars case(kinsol) !--------------------------- @@ -818,33 +819,34 @@ subroutine systemSolv(& ! ----- ! * update states... ! Post processing step to “perfectly” conserve mass by pushing the errors into the state variables - ! Turn off for now to agree with SUNDIALS ! ------------------ - !layerVars: associate(& - ! vector of energy and hydrology indices for the snow and soil domains - ! ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain - ! ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - ! nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - ! nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology state variables in the snow+soil domain - ! ) - ! - ! update temperatures (ensure new temperature is consistent with the fluxes) - !if(nSnowSoilNrg>0)then - ! do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - ! iState = ixSnowSoilNrg(iLayer) - ! stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState))/real(sMul(iState), rkind) - ! end do ! looping through non-missing energy state variables in the snow+soil domain - !endif - ! - ! update volumetric water content in the snow (ensure change in state is consistent with the fluxes) - ! NOTE: for soil water balance is constrained within the iteration loop - !if(nSnowSoilHyd>0)then - ! do concurrent (iLayer=1:nSnow,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing water state variables in the snow domain) - ! iState = ixSnowSoilHyd(iLayer) - ! stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState)) - ! end do ! looping through non-missing water state variables in the soil domain - !endif - !end associate layerVars + if (post_massCons)then + layerVars: associate(& + ! vector of energy and hydrology indices for the snow and soil domains + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) & ! intent(in): [i4b] number of hydrology state variables in the snow+soil domain + ) + + ! update temperatures (ensure new temperature is consistent with the fluxes) + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + iState = ixSnowSoilNrg(iLayer) + stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState))/real(sMul(iState), rkind) + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! update volumetric water content in the snow (ensure change in state is consistent with the fluxes) + ! NOTE: for soil water balance is constrained within the iteration loop + if(nSnowSoilHyd>0)then + do concurrent (iLayer=1:nSnow,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing water state variables in the snow domain) + iState = ixSnowSoilHyd(iLayer) + stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState)) + end do ! looping through non-missing water state variables in the soil domain + endif + end associate layerVars + endif end select From eb3208d7ded8f8ba276b4f20add60a992c8451a4 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 22 Jun 2023 11:14:06 +0900 Subject: [PATCH 0761/1472] removing lookup --- build/source/engine/updateVars.f90 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 index 0403b1dab..03cd43729 100644 --- a/build/source/engine/updateVars.f90 +++ b/build/source/engine/updateVars.f90 @@ -63,8 +63,7 @@ module updateVars_module var_i, & ! data vector (i4b) var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - zLookup ! lookup tables + var_dlength ! data vector with variable length dimension (rkind) ! provide access to indices that define elements of the data structures USE var_lookup,only:iLookDIAG ! named variables for structure elements From 1d9ecb32b700fc1f32b3c37215861040667e628d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 22 Jun 2023 11:59:03 +0900 Subject: [PATCH 0762/1472] should have fixed bug --- build/source/engine/opSplittin.f90 | 1 - build/source/engine/varSubstep.f90 | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 796fb27a2..d7d21a96e 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -828,7 +828,6 @@ subroutine opSplittin(& ! check that all state variables were updated if(any(stateCheck==0))then - print*,stateCheck message=trim(message)//'some state variables were not updated!' err=20; return endif diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index f85ee6d0d..b9d3d72ab 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -354,8 +354,11 @@ subroutine varSubstep(& reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt err,cmessage) ! intent(out): error code and error message - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - + if(err/=0)then ! (check for errors, but do not fail yet) + message=trim(message)//trim(cmessage) + if(err>0) return + endif + ! if too much melt or need to reduce length of the coupled step then return ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry if(tooMuchMelt .or. reduceCoupledStep) return From 800b6c31dc6e0341a8cceeb1df0df9b142a4c934 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 22 Jun 2023 12:06:31 +0900 Subject: [PATCH 0763/1472] remove debug compile --- build/cmake/build.mac.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/cmake/build.mac.bash b/build/cmake/build.mac.bash index 87b371c00..dc929bf1f 100755 --- a/build/cmake/build.mac.bash +++ b/build/cmake/build.mac.bash @@ -2,6 +2,6 @@ # build on Mac, from cmake directory run this as ./build.mac.bash -cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials_Debug +cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials cmake --build ../cmake_build --target all From 54813f34cf91f7020a4925a1dcad4d29f61d37a6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 22 Jun 2023 12:28:11 +0900 Subject: [PATCH 0764/1472] put untapped melt back in --- build/source/engine/systemSolv.f90 | 5 +++++ build/source/engine/varSubstep.f90 | 1 + 2 files changed, 6 insertions(+) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index f3511ef04..81f828294 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -141,6 +141,7 @@ subroutine systemSolv(& ! output deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + untappedMelt, & ! intent(out): un-tapped melt energy (J m-3 s-1) stateVecTrial, & ! intent(out): updated state vector stateVecPrime, & ! intent(out): updated state vector if need the prime space (ida) niter, & ! intent(out): number of iterations taken (numrec) @@ -192,6 +193,7 @@ subroutine systemSolv(& ! output: model control type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + real(rkind),intent(out) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) real(rkind),intent(out) :: stateVecTrial(:) ! trial state vector (mixed units) real(rkind),intent(out) :: stateVecPrime(:) ! trial state vector (mixed units) logical(lgt),intent(out) :: reduceCoupledStep ! flag to reduce the length of the coupled step @@ -850,6 +852,9 @@ subroutine systemSolv(& end select + ! set untapped melt energy to zero + untappedMelt(:) = 0._rkind + ! ************************** ! free memory deallocate(mLayerCmpress_sum) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index b9d3d72ab..3aaec077e 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -350,6 +350,7 @@ subroutine varSubstep(& ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) stateVecTrial, & ! intent(out): updated state vector stateVecPrime, & ! intent(out): updated state vector if need the prime space (ida) + untappedMelt, & ! intent(out): un-tapped melt energy (J m-3 s-1) niter, & ! intent(out): number of iterations taken (numrec) reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt From ade744a0983651131d15f74684c5a4e082f02d82 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 22 Jun 2023 12:36:42 +0900 Subject: [PATCH 0765/1472] wrong order on untapped melt --- build/source/engine/systemSolv.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 81f828294..7bb89a0dd 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -141,9 +141,9 @@ subroutine systemSolv(& ! output deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - untappedMelt, & ! intent(out): un-tapped melt energy (J m-3 s-1) stateVecTrial, & ! intent(out): updated state vector stateVecPrime, & ! intent(out): updated state vector if need the prime space (ida) + untappedMelt, & ! intent(out): un-tapped melt energy (J m-3 s-1) niter, & ! intent(out): number of iterations taken (numrec) reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out): flag to denote that there was too much melt From 761d10f8f5e85a8f5e130c30bad1911f90f47f31 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 23 Jun 2023 11:01:43 +0900 Subject: [PATCH 0766/1472] Sean's changes --- build/source/engine/bigAquifer.f90 | 2 +- build/source/engine/computFlux.f90 | 46 +-- build/source/engine/computThermConduct.f90 | 8 +- build/source/engine/convE2Temp.f90 | 2 +- build/source/engine/conv_funcs.f90 | 2 +- build/source/engine/derivforce.f90 | 2 +- build/source/engine/groundwatr.f90 | 83 +---- build/source/engine/snowLiqFlx.f90 | 20 +- build/source/engine/snow_utils.f90 | 12 +- build/source/engine/soilLiqFlx.f90 | 112 ++---- build/source/engine/soil_utils.f90 | 20 +- build/source/engine/soil_utilsAddPrime.f90 | 6 +- build/source/engine/ssdNrgFlux.f90 | 41 +-- build/source/engine/t2enthalpy.f90 | 6 +- build/source/engine/updateVarsWithPrime.f90 | 4 +- build/source/engine/vegLiqFlux.f90 | 9 +- build/source/engine/vegNrgFlux.f90 | 372 ++++++-------------- build/source/engine/vegSWavRad.f90 | 12 +- 18 files changed, 201 insertions(+), 558 deletions(-) diff --git a/build/source/engine/bigAquifer.f90 b/build/source/engine/bigAquifer.f90 index 35e60b27c..064ae2524 100644 --- a/build/source/engine/bigAquifer.f90 +++ b/build/source/engine/bigAquifer.f90 @@ -140,7 +140,7 @@ subroutine bigAquifer(& ! compute the derivative in the net aquifer flux dBaseflow_dAquifer = -(aquiferBaseflowExp*aquiferBaseflowRate*(xTemp**(aquiferBaseflowExp - 1._rkind)))/aquiferScaleFactor - ! end association to data in structures + ! end association to data in structures end associate end subroutine bigAquifer diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index c4a3a35f1..9e557207a 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -224,19 +224,15 @@ subroutine computFlux(& ! get the necessary variables for the flux computations associate(& - ! model decisions ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) - ! domain boundary conditions upperBoundTemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in): [dp] rainfall rate (kg m-2 s-1) - ! canopy and layer depth canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! indices of model state variables for the vegetation subdomain ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable @@ -244,12 +240,10 @@ subroutine computFlux(& ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer - ! indices of model state variables for the snow+soil domain ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] type of layer (iname_soil or iname_snow) - ! number of state variables of a specific type nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain @@ -257,36 +251,29 @@ subroutine computFlux(& nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain - ! snow parameters snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ! derivatives dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(in): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature - ! number of flux calls numFluxCalls => diag_data%var(iLookDIAG%numFluxCalls)%dat(1) ,& ! intent(out): [dp] number of flux calls (-) - ! net fluxes over the vegetation domain scalarCanairNetNrgFlux => flux_data%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the canopy air space (W m-2) scalarCanopyNetNrgFlux => flux_data%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the vegetation canopy (W m-2) scalarGroundNetNrgFlux => flux_data%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the ground surface (W m-2) scalarCanopyNetLiqFlux => flux_data%var(iLookFLUX%scalarCanopyNetLiqFlux)%dat(1) ,& ! intent(out): [dp] net liquid water flux for the vegetation canopy (kg m-2 s-1) - ! net fluxes over the snow+soil domain mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) mLayerLiqFluxSnow => flux_data%var(iLookFLUX%mLayerLiqFluxSnow)%dat ,& ! intent(out): [dp] net liquid water flux for each snow layer (s-1) mLayerLiqFluxSoil => flux_data%var(iLookFLUX%mLayerLiqFluxSoil)%dat ,& ! intent(out): [dp] net liquid water flux for each soil layer (s-1) - ! evaporative fluxes scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(out): [dp] canopy transpiration (kg m-2 s-1) scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(out): [dp] canopy evaporation/condensation (kg m-2 s-1) scalarGroundEvaporation => flux_data%var(iLookFLUX%scalarGroundEvaporation)%dat(1) ,& ! intent(out): [dp] ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(out): [dp(:)] transpiration loss from each soil layer (m s-1) - ! fluxes for the snow+soil domain iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(out): [dp(0:)] vertical energy flux at the interface of snow and soil layers iLayerLiqFluxSnow => flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at snow layer interfaces (-) @@ -296,7 +283,6 @@ subroutine computFlux(& scalarSnowDrainage => flux_data%var(iLookFLUX%scalarSnowDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the snow profile (m s-1) scalarSoilDrainage => flux_data%var(iLookFLUX%scalarSoilDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the soil profile (m s-1) scalarSoilBaseflow => flux_data%var(iLookFLUX%scalarSoilBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the soil profile (m s-1) - ! infiltration scalarInfilArea => diag_data%var(iLookDIAG%scalarInfilArea )%dat(1) ,& ! intent(out): [dp] fraction of unfrozen area where water can infiltrate (-) scalarFrozenArea => diag_data%var(iLookDIAG%scalarFrozenArea )%dat(1) ,& ! intent(out): [dp] fraction of area that is considered impermeable due to soil ice (-) @@ -305,7 +291,6 @@ subroutine computFlux(& scalarInfiltration => flux_data%var(iLookFLUX%scalarInfiltration)%dat(1) ,& ! intent(out): [dp] infiltration of water into the soil profile (m s-1) scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(inout): [dp] fraction of liquid water on vegetation (-) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(inout): [dp(:)] fraction of liquid water in each snow layer (-) - ! boundary fluxes in the soil domain scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(out): [dp] rain that reaches the ground without ever touching the canopy (kg m-2 s-1) scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(out): [dp] drainage of liquid water from the vegetation canopy (kg m-2 s-1) @@ -313,15 +298,12 @@ subroutine computFlux(& scalarSurfaceRunoff => flux_data%var(iLookFLUX%scalarSurfaceRunoff)%dat(1) ,& ! intent(out): [dp] surface runoff (m s-1) scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1) ,& ! intent(out): [dp] exfiltration from the soil profile (m s-1) mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat ,& ! intent(out): [dp(:)] column outflow from each soil layer (m3 s-1) - ! fluxes for the aquifer scalarAquiferTranspire => flux_data%var(iLookFLUX%scalarAquiferTranspire)%dat(1) ,& ! intent(out): [dp] transpiration loss from the aquifer (m s-1 scalarAquiferRecharge => flux_data%var(iLookFLUX%scalarAquiferRecharge)%dat(1) ,& ! intent(out): [dp] recharge to the aquifer (m s-1) scalarAquiferBaseflow => flux_data%var(iLookFLUX%scalarAquiferBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the aquifer (m s-1) - ! total runoff scalarTotalRunoff => flux_data%var(iLookFLUX%scalarTotalRunoff)%dat(1) ,& ! intent(out): [dp] total runoff (m s-1) - ! derivatives in net vegetation energy fluxes w.r.t. relevant state variables dCanairNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy air temperature dCanairNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy temperature @@ -334,7 +316,6 @@ subroutine computFlux(& dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy temperature dGroundNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. ground temperature dGroundNetFlux_dCanWat => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in net ground fluxes w.r.t. canopy total water content - ! derivatives in evaporative fluxes w.r.t. relevant state variables dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy air temperature dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy temperature @@ -344,32 +325,25 @@ subroutine computFlux(& dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy temperature dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. ground temperature dGroundEvaporation_dCanWat => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy total water content - ! derivatives in transpiration dCanopyTrans_dTCanair => deriv_data%var(iLookDERIV%dCanopyTrans_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) dCanopyTrans_dTCanopy => deriv_data%var(iLookDERIV%dCanopyTrans_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) dCanopyTrans_dTGround => deriv_data%var(iLookDERIV%dCanopyTrans_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) dCanopyTrans_dCanWat => deriv_data%var(iLookDERIV%dCanopyTrans_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy total water content (s-1) - ! derivatives in canopy water w.r.t canopy temperature dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(out): [dp] derivative of canopy liquid storage w.r.t. temperature - ! derivatives in canopy liquid fluxes w.r.t. canopy water scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ,& ! intent(out): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water scalarThroughfallRainDeriv => deriv_data%var(iLookDERIV%scalarThroughfallRainDeriv )%dat(1) ,& ! intent(out): [dp] derivative in throughfall w.r.t. canopy liquid water scalarCanopyLiqDrainageDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDrainageDeriv)%dat(1) ,& ! intent(out): [dp] derivative in canopy drainage w.r.t. canopy liquid water - ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below - ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below dNrgFlux_dWatAbove => deriv_data%var(iLookDERIV%dNrgFlux_dWatAbove )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. water state in the layer above dNrgFlux_dWatBelow => deriv_data%var(iLookDERIV%dNrgFlux_dWatBelow )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. water state in the layer below - ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(out): [dp(:)] derivative in vertical liquid water flux at layer interfaces - ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above @@ -378,27 +352,22 @@ subroutine computFlux(& mLayerdTheta_dPsi => deriv_data%var(iLookDERIV%mLayerdTheta_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. psi mLayerdPsi_dTheta => deriv_data%var(iLookDERIV%mLayerdPsi_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. theta dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in compressibility w.r.t matric head - ! derivative in baseflow flux w.r.t. aquifer storage dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] erivative in baseflow flux w.r.t. aquifer storage (s-1) - ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below dq_dNrgStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dNrgStateLayerSurfVec )%dat ,& ! intent(out): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers - ! derivatives in soil transpiration w.r.t. canopy state variables mLayerdTrans_dTCanair => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanair )%dat ,& !intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature mLayerdTrans_dTCanopy => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanopy )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy temperature mLayerdTrans_dTGround => deriv_data%var(iLookDERIV%mLayerdTrans_dTGround )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. ground temperature mLayerdTrans_dCanWat => deriv_data%var(iLookDERIV%mLayerdTrans_dCanWat )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy total water - ! derivatives in aquifer transpiration w.r.t. canopy state variables dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair )%dat(1) ,& !intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature dAquiferTrans_dTCanopy => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanopy )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy temperature dAquiferTrans_dTGround => deriv_data%var(iLookDERIV%dAquiferTrans_dTGround )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. ground temperature dAquiferTrans_dCanWat => deriv_data%var(iLookDERIV%dAquiferTrans_dCanWat )%dat(1) & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy total water - ) ! association to data in structures ! ***** @@ -429,13 +398,10 @@ subroutine computFlux(& ! identify the need to calculate the energy flux over vegetation doVegNrgFlux = (ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing .or. ixTopNrg/=integerMissing) - - ! check if there is a need to calculate the energy fluxes over vegetation - if(doVegNrgFlux)then - - ! derivative in canopy liquid storage w.r.t. canopy temperature - dCanLiq_dTcanopy = dTheta_dTkCanopy*iden_water*canopyDepth ! kg m-2 K-1 - + + if(doVegNrgFlux)then ! check if there is a need to calculate the energy fluxes over vegetation + dCanLiq_dTcanopy = dTheta_dTkCanopy*iden_water*canopyDepth ! derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) + ! calculate the energy fluxes over vegetation call vegNrgFlux(& ! input: model control @@ -846,9 +812,7 @@ subroutine computFlux(& ! check if computing aquifer fluxes if(ixAqWat/=integerMissing)then - - ! identify modeling decision - if(local_ixGroundwater==bigBucket)then + if(local_ixGroundwater==bigBucket)then ! identify modeling decision ! compute fluxes for the big bucket call bigAquifer(& diff --git a/build/source/engine/computThermConduct.f90 b/build/source/engine/computThermConduct.f90 index a3306b544..a63db3ab2 100644 --- a/build/source/engine/computThermConduct.f90 +++ b/build/source/engine/computThermConduct.f90 @@ -330,11 +330,11 @@ subroutine computThermConduct(& if(err/=0)then; message=trim(message)//trim(cmessage); return; end if select case(ixThCondSnow) case(Yen1965) - dThermalC_dWat(iLayer) = 2._rkind * 3.217d-6 * mLayerVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dWat - dThermalC_dNrg(iLayer) = 2._rkind * 3.217d-6 * mLayerVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dTk + dThermalC_dWat(iLayer) = 2._rkind * 3.217d-6 * mLayerVolFracIce(iLayer) * iden_ice**2_i4b * dVolFracIce_dWat + dThermalC_dNrg(iLayer) = 2._rkind * 3.217d-6 * mLayerVolFracIce(iLayer) * iden_ice**2_i4b * dVolFracIce_dTk case(Mellor1977) - dThermalC_dWat(iLayer) = 2._rkind * 2.576d-6 * mLayerVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dWat - dThermalC_dNrg(iLayer) = 2._rkind * 2.576d-6 * mLayerVolFracIce(iLayer) * iden_ice**2._rkind * dVolFracIce_dTk + dThermalC_dWat(iLayer) = 2._rkind * 2.576d-6 * mLayerVolFracIce(iLayer) * iden_ice**2_i4b * dVolFracIce_dWat + dThermalC_dNrg(iLayer) = 2._rkind * 2.576d-6 * mLayerVolFracIce(iLayer) * iden_ice**2_i4b * dVolFracIce_dTk case(Jordan1991) dThermalC_dWat(iLayer) = ( 7.75d-5 + 2._rkind * 1.105d-6 * mLayerVolFracIce(iLayer) * iden_ice ) * (lambda_ice-lambda_air) * iden_ice * dVolFracIce_dWat dThermalC_dNrg(iLayer) = ( 7.75d-5 + 2._rkind * 1.105d-6 * mLayerVolFracIce(iLayer) * iden_ice ) * (lambda_ice-lambda_air) * iden_ice * dVolFracIce_dTk diff --git a/build/source/engine/convE2Temp.f90 b/build/source/engine/convE2Temp.f90 index 3ee8e8ba3..fe80dd9c6 100644 --- a/build/source/engine/convE2Temp.f90 +++ b/build/source/engine/convE2Temp.f90 @@ -212,7 +212,7 @@ function temp2ethpy(Tk,BulkDenWater,fc_param) real(rkind) :: enthMass ! mass component of specific enthalpy (J kg-1) ! NOTE: this function assumes the freezing curve for snow ... it needs modification to use vanGenuchten functions for soil ! compute the fraction of liquid water in the given layer - frac_liq = 1._rkind / ( 1._rkind + ( fc_param*( Tfreeze - min(Tk,Tfreeze) ) )**2._rkind ) + frac_liq = 1._rkind / ( 1._rkind + ( fc_param*( Tfreeze - min(Tk,Tfreeze) ) )**2_i4b ) ! compute the temperature component of enthalpy for the soil constituent (J kg-1) !enthTempSoil = Cp_soil*(Tk - Tfreeze) ! compute the temperature component of enthalpy for total water (J kg-1) diff --git a/build/source/engine/conv_funcs.f90 b/build/source/engine/conv_funcs.f90 index 10ac07f69..4ff4fb22a 100644 --- a/build/source/engine/conv_funcs.f90 +++ b/build/source/engine/conv_funcs.f90 @@ -92,7 +92,7 @@ subroutine satVapPress(TC, SVP, dSVP_dT) dSVP_dT = 0._rkind else SVP = SATVPFRZ * EXP( (X1*TC)/(X2 + TC) ) ! Saturated Vapour Press (Pa) - dSVP_dT = SVP * (X1/(X2 + TC) - X1*TC/(X2 + TC)**2._rkind) + dSVP_dT = SVP * (X1/(X2 + TC) - X1*TC/(X2 + TC)**2_i4b) end if if(testDeriv) print*, 'dSVP_dT check... ', SVP, dSVP_dT, (SATVPRESS(TC+dx) - SVP)/dx END SUBROUTINE satVapPress diff --git a/build/source/engine/derivforce.f90 b/build/source/engine/derivforce.f90 index 0f08584c9..ff2bdff51 100755 --- a/build/source/engine/derivforce.f90 +++ b/build/source/engine/derivforce.f90 @@ -182,7 +182,7 @@ subroutine derivforce(time_data,forc_data,attr_data,mpar_data,prog_data,diag_dat ! NGEN wants the wind inputted as two components, if not inputting NGEN forcing let the y direction be 0 #ifdef NGEN_FORCING_ACTIVE - windspd = sqrt(windspd_x**2._rkind + windspd_y**2._rkind) + windspd = sqrt(windspd_x**2_i4b + windspd_y**2_i4b) #else windspd_x = windspd windspd_y = 0._rkind diff --git a/build/source/engine/groundwatr.f90 b/build/source/engine/groundwatr.f90 index 6db3ed497..bc5ca3972 100644 --- a/build/source/engine/groundwatr.f90 +++ b/build/source/engine/groundwatr.f90 @@ -62,11 +62,11 @@ module groundwatr_module ! Method ! ------ ! -! Here we assume that water avaialble for shallow groundwater flow includes is all water above +! Here we assume that water available for shallow groundwater flow includes is all water above ! "field capacity" below the depth zCrit, where zCrit is defined as the lowest point in the soil ! profile where the volumetric liquid water content is less than field capacity. ! -! We further assume that transmssivity (m2 s-1) for each layer is defined asuming that the water +! We further assume that transmssivity (m2 s-1) for each layer is defined assuming that the water ! available for saturated flow is located at the bottom of the soil profile. Specifically: ! trTotal(iLayer) = tran0*(zActive(iLayer)/soilDepth)**zScale_TOPMODEL ! trSoil(iLayer) = trTotal(iLayer) - trTotal(iLayer+1) @@ -79,31 +79,26 @@ module groundwatr_module ! ! ************************************************************************************************ subroutine groundwatr(& - ! input: model control nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers getSatDepth, & ! intent(in): logical flag to compute index of the lowest saturated layer - ! input: state and diagnostic variables mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) mLayerMatricHeadLiq, & ! intent(in): liquid water matric potential (m) mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water (-) mLayerVolFracIce, & ! intent(in): volumetric fraction of ice (-) - ! input/output: data structures attr_data, & ! intent(in): spatial attributes mpar_data, & ! intent(in): model parameters prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU - ! output: baseflow ixSaturation, & ! intent(inout) index of lowest saturated layer (NOTE: only computed on the first iteration) mLayerBaseflow, & ! intent(out): baseflow from each soil layer (m s-1) dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - ! output: error control err,message) ! intent(out): error control ! --------------------------------------------------------------------------------------- @@ -143,12 +138,6 @@ subroutine groundwatr(& ! general local variables integer(i4b) :: iLayer ! index of soil layer real(rkind),dimension(nSoil,nSoil) :: dBaseflow_dVolLiq ! derivative in the baseflow flux w.r.t. volumetric liquid water content (m s-1) - ! local variables to compute the numerical Jacobian - logical(lgt),parameter :: doNumericalJacobian=.false. ! flag to compute the numerical Jacobian - real(rkind),dimension(nSoil) :: mLayerMatricHeadPerturbed ! perturbed matric head (m) - real(rkind),dimension(nSoil) :: mLayerVolFracLiqPerturbed ! perturbed volumetric fraction of liquid water (-) - real(rkind),dimension(nSoil) :: mLayerBaseflowPerturbed ! perturbed baseflow (m s-1) - real(rkind),dimension(nSoil,nSoil) :: nJac ! numerical Jacobian (s-1) ! *************************************************************************************** ! *************************************************************************************** ! initialize error control @@ -157,21 +146,17 @@ subroutine groundwatr(& ! --------------------------------------------------------------------------------------- ! associate variables in data structures associate(& - ! input: baseflow parameters fieldCapacity => mpar_data%var(iLookPARAM%fieldCapacity)%dat(1), & ! intent(in): [dp] field capacity (-) theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): [dp] soil porosity (-) theta_res => mpar_data%var(iLookPARAM%theta_res)%dat, & ! intent(in): [dp] residual volumetric water content (-) - ! input: van Genuchten soil parametrers vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat, & ! intent(in): [dp] van Genutchen "alpha" parameter (m-1) vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat, & ! intent(in): [dp] van Genutchen "n" parameter (-) vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat, & ! intent(in): [dp] van Genutchen "m" parameter (-) - ! output: diagnostic variables scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1), & ! intent(out):[dp] exfiltration from the soil profile (m s-1) mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat & ! intent(out):[dp(:)] column outflow from each soil layer (m3 s-1) - ) ! end association to variables in data structures ! ************************************************************************************************ @@ -225,68 +210,6 @@ subroutine groundwatr(& if(iLayer mLayerThetaResid(iLayer))then + if (mLayerVolFracLiqTrial(iLayer) > mLayerThetaResid(iLayer)) then ! check that flow occurs ! compute the relative saturation (-) availCap = mLayerPoreSpace(iLayer) - mLayerThetaResid(iLayer) ! available capacity relSaturn = (mLayerVolFracLiqTrial(iLayer) - mLayerThetaResid(iLayer)) / availCap ! relative saturation diff --git a/build/source/engine/snow_utils.f90 b/build/source/engine/snow_utils.f90 index 2008a9ada..672d8c67d 100644 --- a/build/source/engine/snow_utils.f90 +++ b/build/source/engine/snow_utils.f90 @@ -51,7 +51,7 @@ function fracliquid(Tk,fc_param) real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) real(rkind) :: fracliquid ! fraction of liquid water (-) ! compute fraction of liquid water (-) - fracliquid = 1._rkind / ( 1._rkind + (fc_param*( Tfreeze - min(Tk,Tfreeze) ))**2._rkind ) + fracliquid = 1._rkind / ( 1._rkind + (fc_param*( Tfreeze - min(Tk,Tfreeze) ))**2_i4b ) end function fracliquid @@ -64,7 +64,7 @@ function templiquid(fracliquid,fc_param) real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) real(rkind) :: templiquid ! temperature (K) ! compute temperature based on the fraction of liquid water (K) - templiquid = Tfreeze - ((1._rkind/fracliquid - 1._rkind)/fc_param**2._rkind)**(0.5_rkind) + templiquid = Tfreeze - ((1._rkind/fracliquid - 1._rkind)/fc_param**2_i4b)**(0.5_rkind) end function templiquid @@ -84,7 +84,7 @@ function dFracLiq_dTk(Tk,fc_param) Tdep = Tfreeze - min(Tk,Tfreeze) Tdim = fc_param*Tdep ! differentiate the freezing curve w.r.t temperature - dFracLiq_dTk = (fc_param*2._rkind*Tdim) / ( ( 1._rkind + Tdim**2._rkind)**2._rkind ) + dFracLiq_dTk = (fc_param*2._rkind*Tdim) / ( ( 1._rkind + Tdim**2_i4b)**2_i4b ) end function dFracLiq_dTk @@ -101,9 +101,9 @@ subroutine tcond_snow(BulkDenIce,thermlcond,err,message) err=0; message="tcond_snow/" ! compute thermal conductivity of snow select case(model_decisions(iLookDECISIONS%thCondSnow)%iDecision) - case(Yen1965); thermlcond = 3.217d-6 * BulkDenIce**2._rkind ! Yen (1965) - case(Mellor1977); thermlcond = 2.576d-6 * BulkDenIce**2._rkind + 7.4d-2 ! Mellor (1977) - case(Jordan1991); thermlcond = lambda_air + (7.75d-5*BulkDenIce + 1.105d-6*(BulkDenIce**2._rkind)) & + case(Yen1965); thermlcond = 3.217d-6 * BulkDenIce**2_i4b ! Yen (1965) + case(Mellor1977); thermlcond = 2.576d-6 * BulkDenIce**2_i4b + 7.4d-2 ! Mellor (1977) + case(Jordan1991); thermlcond = lambda_air + (7.75d-5*BulkDenIce + 1.105d-6*(BulkDenIce**2_i4b)) & * (lambda_ice-lambda_air) ! Jordan (1991) case default err=10; message=trim(message)//"unknownOption"; return diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index 63f39655a..adae6b002 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -730,16 +730,13 @@ subroutine soilLiqFlx(& ! ============================ ! identify the type of perturbation select case(itry) - ! skip undesired perturbations case(perturbStateBelow); cycle ! only perturb soil state at this time (perhaps perturb aquifer state later) case(perturbState); cycle ! here pertubing the state above the flux at the interface - ! un-perturbed case case(unperturbed) scalarVolFracLiqTrial = mLayerVolFracLiqTrial(nSoil) scalarMatricHeadLiqTrial = mLayerMatricHeadLiqTrial(nSoil) - ! perturb soil state (one-sided finite differences) case(perturbStateAbove) select case(ixRichards) ! (perturbation depends on the form of Richards' equation) @@ -751,31 +748,26 @@ subroutine soilLiqFlx(& scalarMatricHeadLiqTrial = mLayerMatricHeadLiqTrial(nSoil) + dx case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return end select ! (form of Richards' equation) - end select ! (type of perturbation) ! ===== ! get hydraulic conductivty... ! ============================ select case(itry) - ! compute perturbed value of hydraulic conductivity case(perturbStateAbove) select case(ixRichards) case(moisture); scalarHydCondTrial = hydCond_liq(scalarVolFracLiqTrial,mLayerSatHydCond(nSoil),theta_res(nSoil),theta_sat(nSoil),vGn_m(nSoil)) * iceImpedeFac(nSoil) case(mixdform); scalarHydCondTrial = hydCond_psi(scalarMatricHeadLiqTrial,mLayerSatHydCond(nSoil),vGn_alpha(nSoil),vGn_n(nSoil),vGn_m(nSoil)) * iceImpedeFac(nSoil) end select - ! (use un-perturbed value) case default scalarHydCondTrial = mLayerHydCond(nSoil) ! hydraulic conductivity at the mid-point of the lowest unsaturated soil layer (m s-1) - end select ! (re-computing hydraulic conductivity) ! ===== ! compute drainage flux and its derivative... ! =========================================== - call qDrainFlux(& ! input: model control desireAnal, & ! intent(in): flag indicating if derivatives are desired @@ -957,18 +949,6 @@ subroutine diagv_node(& real(rkind) :: dK_dLiq__noIce ! derivative in hydraulic conductivity w.r.t volumetric liquid water content, in the absence of ice (m s-1) real(rkind) :: dK_dPsi__noIce ! derivative in hydraulic conductivity w.r.t matric head, in the absence of ice (s-1) real(rkind) :: relSatMP ! relative saturation of macropores (-) - ! local variables to test the derivative - logical(lgt),parameter :: testDeriv=.false. ! local flag to test the derivative - real(rkind) :: xConst ! LH_fus/(gravity*Tfreeze), used in freezing point depression equation (m K-1) - real(rkind) :: vTheta ! volumetric fraction of total water (-) - real(rkind) :: volLiq ! volumetric fraction of liquid water (-) - real(rkind) :: volIce ! volumetric fraction of ice (-) - real(rkind) :: volFracLiq1,volFracLiq2 ! different trial values of volumetric liquid water content (-) - real(rkind) :: effSat ! effective saturation (-) - real(rkind) :: psiLiq ! liquid water matric potential (m) - real(rkind) :: hydCon ! hydraulic conductivity (m s-1) - real(rkind) :: hydIce ! hydraulic conductivity after accounting for ice impedance (-) - real(rkind),parameter :: dx = 1.e-8_rkind ! finite difference increment (m) ! initialize error control err=0; message="diagv_node/" @@ -981,10 +961,6 @@ subroutine diagv_node(& case(mixdform) scalardTheta_dPsi = dTheta_dPsi(scalarMatricHeadLiqTrial,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) scalardPsi_dTheta = dPsi_dTheta(scalarVolFracLiqTrial,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if(testDeriv)then - volFracLiq1 = volFracLiq(scalarMatricHeadLiqTrial, vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - volFracLiq2 = volFracLiq(scalarMatricHeadLiqTrial+dx,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - end if ! (testing the derivative) case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return end select @@ -1028,7 +1004,7 @@ subroutine diagv_node(& scalarHydCond = hydCond_noIce*iceImpedeFac + scalarHydCondMP ! compute derivative in hydraulic conductivity (m s-1) - if(deriv_desired)then + if(deriv_desired)then ! (compute derivative for macropores) if(localVolFracLiq > theta_mp)then relSatMP = (localVolFracLiq - theta_mp)/(theta_sat - theta_mp) @@ -1057,30 +1033,12 @@ subroutine diagv_node(& dIceImpede_dT ) ! intent(out): derivative in ice impedance factor w.r.t. temperature (K-1) ! (compute derivative in hydraulic conductivity w.r.t. temperature) dHydCond_dTemp = hydCond_noIce*dIceImpede_dT + dHydCondMicro_dTemp*iceImpedeFac - ! (test derivative) - if(testDeriv)then - xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) - vTheta = scalarVolFracIceTrial + scalarVolFracLiqTrial - volLiq = volFracLiq(xConst*(scalarTempTrial+dx - Tfreeze),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - volIce = vTheta - volLiq - effSat = (volLiq - theta_res)/(theta_sat - volIce - theta_res) - psiLiq = matricHead(effSat,vGn_alpha,0._rkind,1._rkind,vGn_n,vGn_m) ! use effective saturation, so theta_res=0 and theta_sat=1 - hydCon = hydCond_psi(psiLiq,scalarSatHydCond,vGn_alpha,vGn_n,vGn_m) - call iceImpede(volIce,f_impede,iceImpedeFac,dIceImpede_dLiq) - hydIce = hydCon*iceImpedeFac - print*, 'test derivative: ', (psiLiq - scalarMatricHeadLiqTrial)/dx, dPsiLiq_dTemp - print*, 'test derivative: ', (hydCon - hydCond_noIce)/dx, dHydCondMicro_dTemp - print*, 'test derivative: ', (hydIce - scalarHydCond)/dx, dHydCond_dTemp - print*, 'press any key to continue'; read(*,*) ! (alternative to the deprecated 'pause' statement) - end if ! testing the derivative ! (set values that are not used to missing) dHydCond_dVolLiq = realMissing ! not used, so cause problems dDiffuse_dVolLiq = realMissing ! not used, so cause problems end if - case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return - - end select + end select ! select form of Richards' equation ! if derivatives are not desired, then set values to missing if(.not.deriv_desired)then @@ -1230,31 +1188,31 @@ subroutine surfaceFlx(& character(*),intent(out) :: message ! error message ! ----------------------------------------------------------------------------------------------------------------------------- ! local variables - ! (general) + ! general integer(i4b) :: iLayer ! index of soil layer real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) real(rkind) :: fpart1,fpart2 ! different parts of a function real(rkind) :: dpart1(1:nSoil),dpart2(1:nSoil) ! derivatives for different parts of a function real(rkind) :: dfracCap(1:nSoil),dfInfRaw(1:nSoil) ! derivatives for different parts of a function - ! (head boundary condition) + ! head boundary condition real(rkind) :: cFlux ! capillary flux (m s-1) real(rkind) :: dNum ! numerical derivative - ! (simplified Green-Ampt infiltration) + ! simplified Green-Ampt infiltration real(rkind) :: rootZoneLiq ! depth of liquid water in the root zone (m) real(rkind) :: rootZoneIce ! depth of ice in the root zone (m) real(rkind) :: availCapacity ! available storage capacity in the root zone (m) real(rkind) :: depthWettingFront ! depth to the wetting front (m) real(rkind) :: hydCondWettingFront ! hydraulic conductivity at the wetting front (m s-1) - ! (saturated area associated with variable storage capacity) + ! saturated area associated with variable storage capacity real(rkind) :: fracCap ! fraction of pore space filled with liquid water and ice (-) real(rkind) :: fInfRaw ! infiltrating area before imposing solution constraints (-) real(rkind),parameter :: maxFracCap=0.995_rkind ! maximum fraction capacity -- used to avoid numerical problems associated with an enormous derivative real(rkind),parameter :: scaleFactor=0.000001_rkind ! scale factor for the smoothing function (-) real(rkind),parameter :: qSurfScaleMax=1000._rkind ! maximum surface runoff scaling factor (-) - ! (fraction of impermeable area associated with frozen ground) + ! fraction of impermeable area associated with frozen ground real(rkind) :: alpha ! shape parameter in the Gamma distribution real(rkind) :: xLimg ! upper limit of the integral - ! (derivatives) + ! derivatives real(rkind) :: dVolFracLiq_dWat(1:nSoil) ! derivative in vol fraction of liquid w.r.t. water state variable in root layers real(rkind) :: dVolFracIce_dWat(1:nSoil) ! derivative in vol fraction of ice w.r.t. water state variable in root layers real(rkind) :: dVolFracLiq_dTk(1:nSoil) ! derivative in vol fraction of liquid w.r.t. temperature in root layers @@ -1288,10 +1246,8 @@ subroutine surfaceFlx(& ! ***** ! head condition case(prescribedHead) - ! surface runoff iz zero for the head condition scalarSurfaceRunoff = 0._rkind - ! compute transmission and the capillary flux select case(ixRichards) ! (form of Richards' equation) case(moisture) @@ -1324,19 +1280,18 @@ subroutine surfaceFlx(& dNum = 0._rkind end if + ! ***** ! flux condition case(liquidFlux) - ! force infiltration to be constant over the iterations if(firstSplitOper)then - - ! (process root layers only liquid and ice derivatives) + ! process root layers only liquid and ice derivatives dVolFracLiq_dWat(:) = 0._rkind dVolFracIce_dWat(:) = 0._rkind dVolFracLiq_dTk(:) = 0._rkind dVolFracIce_dTk(:) = 0._rkind if(deriv_desired .and. nRoots > 0)then - select case(ixRichards) ! (form of Richards' equation) + select case(ixRichards) ! form of Richards' equation case(moisture) dVolFracLiq_dWat(:) = 1._rkind dVolFracIce_dWat(:) = mLayerdPsi_dTheta(:) - 1._rkind @@ -1364,7 +1319,7 @@ subroutine surfaceFlx(& dRootZoneLiq_dTk(:) = 0._rkind dRootZoneIce_dTk(:) = 0._rkind - ! (process layers where the roots extend to the bottom of the layer) + ! process layers where the roots extend to the bottom of the layer if(nRoots > 1)then do iLayer=1,nRoots-1 rootZoneLiq = rootZoneLiq + mLayerVolFracLiq(iLayer)*mLayerDepth(iLayer) @@ -1375,7 +1330,7 @@ subroutine surfaceFlx(& dRootZoneIce_dTk(iLayer) = dVolFracIce_dTk(iLayer) *mLayerDepth(iLayer) end do end if - ! (process layers where the roots end in the current layer) + ! process layers where the roots end in the current layer rootZoneLiq = rootZoneLiq + mLayerVolFracLiq(nRoots)*(rootingDepth - iLayerHeight(nRoots-1)) rootZoneIce = rootZoneIce + mLayerVolFracIce(nRoots)*(rootingDepth - iLayerHeight(nRoots-1)) dRootZoneLiq_dWat(nRoots) = dVolFracLiq_dWat(nRoots)*(rootingDepth - iLayerHeight(nRoots-1)) @@ -1404,24 +1359,24 @@ subroutine surfaceFlx(& fPart1 = hydCondWettingFront fPart2 = (wettingFrontSuction + depthWettingFront)/depthWettingFront dPart1(:) = surfaceSatHydCond*(zScale_TOPMODEL - 1._rkind) * ( (1._rkind - depthWettingFront/sum(mLayerDepth))**(zScale_TOPMODEL - 2._rkind) ) * (-dDepthWettingFront_dWat(:))/sum(mLayerDepth) - dPart2(:) = -dDepthWettingFront_dWat(:)*wettingFrontSuction / (depthWettingFront**2._rkind) + dPart2(:) = -dDepthWettingFront_dWat(:)*wettingFrontSuction / (depthWettingFront**2_i4b) dxMaxInfilRate_dWat(:) = fPart1*dpart2(:) + fPart2*dPart1(:) dPart1(:) = surfaceSatHydCond*(zScale_TOPMODEL - 1._rkind) * ( (1._rkind - depthWettingFront/sum(mLayerDepth))**(zScale_TOPMODEL - 2._rkind) ) * (-dDepthWettingFront_dTk(:))/sum(mLayerDepth) - dPart2(:) = -dDepthWettingFront_dTk(:)*wettingFrontSuction / (depthWettingFront**2._rkind) + dPart2(:) = -dDepthWettingFront_dTk(:)*wettingFrontSuction / (depthWettingFront**2_i4b) dxMaxInfilRate_dTk(:) = fPart1*dpart2(:) + fPart2*dPart1(:) ! define the infiltrating area and derivatives for the non-frozen part of the cell/basin if(qSurfScale < qSurfScaleMax)then fracCap = rootZoneLiq/(maxFracCap*availCapacity) ! fraction of available root zone filled with water fInfRaw = 1._rkind - exp(-qSurfScale*(1._rkind - fracCap)) ! infiltrating area -- allowed to violate solution constraints - scalarInfilArea = min(0.5_rkind*(fInfRaw + sqrt(fInfRaw**2._rkind + scaleFactor)), 1._rkind) ! infiltrating area -- constrained - if (0.5_rkind*(fInfRaw + sqrt(fInfRaw**2._rkind + scaleFactor))< 1._rkind) then + scalarInfilArea = min(0.5_rkind*(fInfRaw + sqrt(fInfRaw**2_i4b + scaleFactor)), 1._rkind) ! infiltrating area -- constrained + if (0.5_rkind*(fInfRaw + sqrt(fInfRaw**2_i4b + scaleFactor))< 1._rkind) then dfracCap(:) = ( dRootZoneLiq_dWat(:)/maxFracCap + dRootZoneIce_dWat(:)*fracCap )/availCapacity dfInfRaw(:) = -qSurfScale*dfracCap(:) * exp(-qSurfScale*(1._rkind - fracCap)) - dInfilArea_dWat(1:nSoil) = 0.5_rkind*dfInfRaw(:) * (1._rkind + fInfRaw/sqrt(fInfRaw**2._rkind + scaleFactor)) + dInfilArea_dWat(1:nSoil) = 0.5_rkind*dfInfRaw(:) * (1._rkind + fInfRaw/sqrt(fInfRaw**2_i4b + scaleFactor)) dfracCap(:) = ( dRootZoneLiq_dTk(:)/maxFracCap + dRootZoneIce_dTk(:)*fracCap )/availCapacity dfInfRaw(:) = -qSurfScale*dfracCap(:) * exp(-qSurfScale*(1._rkind - fracCap)) - dInfilArea_dTk(1:nSoil) = 0.5_rkind*dfInfRaw(:) * (1._rkind + fInfRaw/sqrt(fInfRaw**2._rkind + scaleFactor)) + dInfilArea_dTk(1:nSoil) = 0.5_rkind*dfInfRaw(:) * (1._rkind + fInfRaw/sqrt(fInfRaw**2_i4b + scaleFactor)) else ! scalarInfilArea = 1._rkind dInfilArea_dWat(1:nSoil) = 0._rkind dInfilArea_dTk(1:nSoil) = 0._rkind @@ -1441,7 +1396,7 @@ subroutine surfaceFlx(& ! define the impermeable area and derivatives due to frozen ground if(rootZoneIce > tiny(rootZoneIce))then ! (avoid divide by zero) - alpha = 1._rkind/(soilIceCV**2._rkind) ! shape parameter in the Gamma distribution + alpha = 1._rkind/(soilIceCV**2_i4b) ! shape parameter in the Gamma distribution xLimg = alpha*soilIceScale/rootZoneIce ! upper limit of the integral !if we use this, we will have a derivative of scalarFrozenArea w.r.t. water and temperature in each layer (through mLayerVolFracIce) @@ -1491,7 +1446,6 @@ subroutine surfaceFlx(& surfaceHydCond = realMissing surfaceDiffuse = realMissing - ! ***** error check case default; err=20; message=trim(message)//'unknown upper boundary condition for soil hydrology'; return end select ! (type of upper boundary condition) @@ -1592,17 +1546,16 @@ subroutine iLayerFlux(& ! compute the vertical flux of liquid water ! compute the hydraulic conductivity at the interface if(useGeometric)then - iLayerHydCond = (nodeHydCondTrial(ixLower) * nodeHydCondTrial(ixUpper))**0.5_rkind + iLayerHydCond = sqrt(nodeHydCondTrial(ixLower) * nodeHydCondTrial(ixUpper)) else iLayerHydCond = (nodeHydCondTrial(ixLower) + nodeHydCondTrial(ixUpper))*0.5_rkind end if - !write(*,'(a,1x,5(e20.10,1x))') 'in iLayerFlux: iLayerHydCond, iLayerHydCondMP = ', iLayerHydCond, iLayerHydCondMP - ! compute the height difference between nodes + dz = nodeHeight(ixLower) - nodeHeight(ixUpper) ! compute the capillary flux select case(ixRichards) ! (form of Richards' equation) case(moisture) - iLayerDiffuse = (nodeDiffuseTrial(ixLower) * nodeDiffuseTrial(ixUpper))**0.5_rkind + iLayerDiffuse = sqrt(nodeDiffuseTrial(ixLower) * nodeDiffuseTrial(ixUpper)) dLiq = nodeVolFracLiqTrial(ixLower) - nodeVolFracLiqTrial(ixUpper) cflux = -iLayerDiffuse * dLiq/dz case(mixdform) @@ -1767,14 +1720,10 @@ subroutine qDrainFlux(& ! determine lower boundary condition select case(bc_lower) - ! --------------------------------------------------------------------------------------------- - ! * prescribed head - ! --------------------------------------------------------------------------------------------- case(prescribedHead) - - ! compute fluxes - select case(ixRichards) ! (moisture-based form of Richards' equation) - case(moisture) + ! compute flux + select case(ixRichards) + case(moisture) ! (moisture-based form of Richards' equation) ! compute the hydraulic conductivity and diffusivity at the boundary bottomHydCond = hydCond_liq(lowerBoundTheta,bottomSatHydCond,theta_res,theta_sat,vGn_m) * iceImpedeFac bottomDiffuse = dPsi_dTheta(lowerBoundTheta,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * bottomHydCond @@ -1805,12 +1754,8 @@ subroutine qDrainFlux(& dq_dNrgStateUnsat = realMissing end if - ! --------------------------------------------------------------------------------------------- - ! * function of matric head in the bottom layer - ! --------------------------------------------------------------------------------------------- - case(funcBottomHead) - - ! compute fluxes + case(funcBottomHead) !function of matric head in the bottom layer + ! compute flux select case(ixRichards) case(moisture); nodePsi = matricHead(nodeVolFracLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) case(mixdform); nodePsi = nodeMatricHeadLiq @@ -1834,7 +1779,6 @@ subroutine qDrainFlux(& end if case(freeDrainage) - ! compute flux scalarDrainage = nodeHydCond*kAnisotropic diff --git a/build/source/engine/soil_utils.f90 b/build/source/engine/soil_utils.f90 index 00436d0ea..4849ef5aa 100644 --- a/build/source/engine/soil_utils.f90 +++ b/build/source/engine/soil_utils.f90 @@ -172,7 +172,7 @@ subroutine liquidHead(& endif ! (compute derivative in the liquid water matric potential w.r.t. the total water matric potential) - dPsiLiq_dPsi0 = dVolTot_dPsi0*dPsiLiq_dEffSat*xNum/(xDen**2._rkind) + dPsiLiq_dPsi0 = dVolTot_dPsi0*dPsiLiq_dEffSat*xNum/(xDen**2_i4b) endif ! if dPsiLiq_dTemp is desired @@ -190,7 +190,7 @@ subroutine liquidHead(& endif ! (compute the derivative in the liquid water matric potential w.r.t. temperature) - dEffSat_dTemp = -dTheta_dT*xNum/(xDen**2._rkind) + dTheta_dT/xDen + dEffSat_dTemp = -dTheta_dT*xNum/(xDen**2_i4b) + dTheta_dT/xDen dPsiLiq_dTemp = dPsiLiq_dEffSat*dEffSat_dTemp endif ! if dPsiLiq_dTemp is desired @@ -245,7 +245,7 @@ function hydCond_psi(psi,k_sat,alpha,n,m) real(rkind) :: hydCond_psi ! hydraulic conductivity (m s-1) if(psi<0._rkind)then hydCond_psi = k_sat * & - ( ( (1._rkind - (psi*alpha)**(n-1._rkind) * (1._rkind + (psi*alpha)**n)**(-m))**2._rkind ) & + ( ( (1._rkind - (psi*alpha)**(n-1._rkind) * (1._rkind + (psi*alpha)**n)**(-m))**2_i4b ) & / ( (1._rkind + (psi*alpha)**n)**(m/2._rkind) ) ) else hydCond_psi = k_sat @@ -270,7 +270,7 @@ function hydCond_liq(volFracLiq,k_sat,theta_res,theta_sat,m) real(rkind) :: theta_e ! effective soil moisture if(volFracLiq < theta_sat)then theta_e = (volFracLiq - theta_res) / (theta_sat - theta_res) - hydCond_liq = k_sat*theta_e**(1._rkind/2._rkind) * (1._rkind - (1._rkind - theta_e**(1._rkind/m) )**m)**2._rkind + hydCond_liq = k_sat*theta_e**(1._rkind/2._rkind) * (1._rkind - (1._rkind - theta_e**(1._rkind/m) )**m)**2_i4b else hydCond_liq = k_sat end if @@ -412,11 +412,11 @@ function dPsi_dTheta2(volFracLiq,alpha,theta_res,theta_sat,n,m,lTangent) theta_e = (volFracLiq - theta_res) / (theta_sat - theta_res) ! get the first function and derivative y1 = (-1._rkind/m)*theta_e**(-1._rkind/m - 1._rkind) / (theta_sat - theta_res) - d1 = ( (m + 1._rkind) / (m**2._rkind * (theta_sat - theta_res)**2._rkind) ) * theta_e**(-1._rkind/m - 2._rkind) + d1 = ( (m + 1._rkind) / (m**2_i4b * (theta_sat - theta_res)**2_i4b) ) * theta_e**(-1._rkind/m - 2._rkind) ! get the second function and derivative xx = theta_e**(-1._rkind/m) - 1._rkind y2 = (1._rkind/n)*xx**(1._rkind/n - 1._rkind) - d2 = ( -(1._rkind - n)/((theta_sat - theta_res)*m*n**2._rkind) ) * xx**(1._rkind/n - 2._rkind) * theta_e**(-1._rkind/m - 1._rkind) + d2 = ( -(1._rkind - n)/((theta_sat - theta_res)*m*n**2_i4b) ) * xx**(1._rkind/n - 2._rkind) * theta_e**(-1._rkind/m - 1._rkind) ! return the derivative dPsi_dTheta2 = (d1*y2 + y1*d2)/alpha ! ***** compute numerical derivatives @@ -468,13 +468,13 @@ function dHydCond_dPsi(psi,k_sat,alpha,n,m,lTangent) f_x2 = (1._rkind + (psi*alpha)**n)**(-m) d_x1 = alpha * (n - 1._rkind)*(psi*alpha)**(n - 2._rkind) d_x2 = alpha * n*(psi*alpha)**(n - 1._rkind) * (-m)*(1._rkind + (psi*alpha)**n)**(-m - 1._rkind) - f_nm = (1._rkind - f_x1*f_x2)**2._rkind + f_nm = (1._rkind - f_x1*f_x2)**2_i4b d_nm = (-d_x1*f_x2 - f_x1*d_x2) * 2._rkind*(1._rkind - f_x1*f_x2) ! compute the derivative for the denominator f_dm = (1._rkind + (psi*alpha)**n)**(m/2._rkind) d_dm = alpha * n*(psi*alpha)**(n - 1._rkind) * (m/2._rkind)*(1._rkind + (psi*alpha)**n)**(m/2._rkind - 1._rkind) ! and combine - dHydCond_dPsi = k_sat*(d_nm*f_dm - d_dm*f_nm) / (f_dm**2._rkind) + dHydCond_dPsi = k_sat*(d_nm*f_dm - d_dm*f_nm) / (f_dm**2_i4b) else ! ***** compute numerical derivatives hydcond0 = hydCond_psi(psi, k_sat,alpha,n,m) @@ -521,7 +521,7 @@ function dHydCond_dLiq(volFracLiq,k_sat,theta_res,theta_sat,m,lTangent) ! compute the effective saturation theta_e = (volFracLiq - theta_res) / (theta_sat - theta_res) ! compute the function and derivative of the first fuction - f1 = k_sat*theta_e**0.5_rkind + f1 = k_sat*sqrt(theta_e) d1 = k_sat*0.5_rkind*theta_e**(-0.5_rkind) / (theta_sat - theta_res) ! compute the function and derivative of the second function ! (first part) @@ -531,7 +531,7 @@ function dHydCond_dLiq(volFracLiq,k_sat,theta_res,theta_sat,m,lTangent) x2 = x1**m p2 = m*x1**(m - 1._rkind) ! (final) - f2 = (1._rkind - x2)**2._rkind + f2 = (1._rkind - x2)**2_i4b p3 = -2._rkind*(1._rkind - x2) ! (combine) d2 = p1*p2*p3 diff --git a/build/source/engine/soil_utilsAddPrime.f90 b/build/source/engine/soil_utilsAddPrime.f90 index d76f626a5..9e2a20a1a 100644 --- a/build/source/engine/soil_utilsAddPrime.f90 +++ b/build/source/engine/soil_utilsAddPrime.f90 @@ -118,7 +118,7 @@ subroutine liquidHeadSundials(& ! - matric head associated with liquid water matricHeadLiq = matricHead(effSat,vGn_alpha,0._rkind,1._rkind,vGn_n,vGn_m) ! argument is effective saturation, so theta_res=0 and theta_sat=1 if (effSat < 1._rkind .and. effSat > 0._rkind)then - effSatPrime = (volFracLiqPrime * xDen + volFracIcePrime * xNum) / xDen**2._rkind + effSatPrime = (volFracLiqPrime * xDen + volFracIcePrime * xNum) / xDen**2_i4b matricHeadLiqPrime = -( 1._rkind/(vGn_alpha*vGn_n*vGn_m) ) * effSat**(-1._rkind-1._rkind/vGn_m) * ( effSat**(-1._rkind/vGn_m) - 1._rkind )**(-1._rkind+1._rkind/vGn_n) * effSatPrime else matricHeadLiqPrime = 0._rkind @@ -143,7 +143,7 @@ subroutine liquidHeadSundials(& endif ! (compute derivative in the liquid water matric potential w.r.t. the total water matric potential) - dPsiLiq_dPsi0 = dVolTot_dPsi0*dPsiLiq_dEffSat*xNum/(xDen**2._rkind) + dPsiLiq_dPsi0 = dVolTot_dPsi0*dPsiLiq_dEffSat*xNum/(xDen**2_i4b) endif ! if dPsiLiq_dTemp is desired @@ -160,7 +160,7 @@ subroutine liquidHeadSundials(& err=20; return endif ! (compute the derivative in the liquid water matric potential w.r.t. temperature) - dEffSat_dTemp = -dTheta_dT*xNum/(xDen**2._rkind) + dTheta_dT/xDen + dEffSat_dTemp = -dTheta_dT*xNum/(xDen**2_i4b) + dTheta_dT/xDen dPsiLiq_dTemp = dPsiLiq_dEffSat*dEffSat_dTemp endif ! if dPsiLiq_dTemp is desired diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index 8864da0f4..7e4e60c87 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -185,19 +185,15 @@ subroutine ssdNrgFlux(& ! ***** compute the conductive fluxes at layer interfaces ***** ! ------------------------------------------------------------------------------------------------------------------------- do iLayer=ixTop,ixBot - if(iLayer==nLayers)then ! (lower boundary fluxes -- positive downwards) ! flux depends on the type of lower boundary condition select case(ix_bcLowrTdyn) ! (identify the lower boundary condition for thermodynamics case(prescribedTemp); iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*(lowerBoundTemp - mLayerTempTrial(iLayer))/(mLayerDepth(iLayer)*0.5_rkind) case(zeroFlux); iLayerConductiveFlux(iLayer) = 0._rkind end select ! (identifying the lower boundary condition for thermodynamics) - else ! (domain boundary fluxes -- positive downwards) iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) / & (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) - - !write(*,'(a,i4,1x,2(f9.3,1x))') 'iLayer, iLayerConductiveFlux(iLayer), iLayerThermalC(iLayer) = ', iLayer, iLayerConductiveFlux(iLayer), iLayerThermalC(iLayer) end if ! (the type of layer) end do ! looping through layers @@ -205,7 +201,6 @@ subroutine ssdNrgFlux(& ! ***** compute the advective fluxes at layer interfaces ***** ! ------------------------------------------------------------------------------------------------------------------------- do iLayer=ixTop,ixBot - ! get the liquid flux at layer interfaces select case(layerType(iLayer)) case(iname_snow); qFlux = iLayerLiqFluxSnow(iLayer) @@ -274,36 +269,28 @@ subroutine ssdNrgFlux(& ! loop through INTERFACES... do iLayer=ixTop,ixBot - ! ***** the lower boundary if(iLayer==nLayers)then ! (lower boundary) ! identify the lower boundary condition - select case(ix_bcLowrTdyn) - - ! * prescribed temperature at the lower boundary + select case(ix_bcLowrTdyn) !prescribed temperature at the lower boundary case(prescribedTemp) - dz = mLayerDepth(iLayer)*0.5_rkind - dFlux_dWatAbove(iLayer) = -dThermalC_dWatAbove(iLayer) * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz - if(ixDerivMethod==analytical)then ! ** analytical derivatives - dFlux_dTempAbove(iLayer) = -dThermalC_dTempAbove(iLayer) * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + iLayerThermalC(iLayer)/dz - else ! ** numerical derivatives for constant step iLayerThermalC - flux0 = -iLayerThermalC(iLayer)*(lowerBoundTemp - (mLayerTempTrial(iLayer) ))/dz - flux1 = -iLayerThermalC(iLayer)*(lowerBoundTemp - (mLayerTempTrial(iLayer)+dx))/dz - dFlux_dTempAbove(iLayer) = (flux1 - flux0)/dx - end if - - ! * zero flux at the lower boundary - case(zeroFlux) - dFlux_dWatAbove(iLayer) = 0._rkind - dFlux_dTempAbove(iLayer) = 0._rkind - + dz = mLayerDepth(iLayer)*0.5_rkind + dFlux_dWatAbove(iLayer) = -dThermalC_dWatAbove(iLayer) * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + if(ixDerivMethod==analytical)then ! ** analytical derivatives + dFlux_dTempAbove(iLayer) = -dThermalC_dTempAbove(iLayer) * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + iLayerThermalC(iLayer)/dz + else ! ** numerical derivatives for constant step iLayerThermalC + flux0 = -iLayerThermalC(iLayer)*(lowerBoundTemp - (mLayerTempTrial(iLayer) ))/dz + flux1 = -iLayerThermalC(iLayer)*(lowerBoundTemp - (mLayerTempTrial(iLayer)+dx))/dz + dFlux_dTempAbove(iLayer) = (flux1 - flux0)/dx + end if + case(zeroFlux) ! zero flux at the lower boundary + dFlux_dWatAbove(iLayer) = 0._rkind + dFlux_dTempAbove(iLayer) = 0._rkind case default; err=20; message=trim(message)//'unable to identify lower boundary condition for thermodynamics'; return - end select ! (identifying the lower boundary condition for thermodynamics) - ! ***** internal layers - + ! ***** internal layers else dz = (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) dFlux_dWatAbove(iLayer) = -dThermalC_dWatAbove(iLayer) * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index 142adbb33..344bb335b 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -111,7 +111,7 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number of ! get the values of temperature for the lookup table xIncr = 1._rkind/real(nLook-1, kind(rkind)) - xTemp = T_lower + (Tfreeze - T_lower)*arth(0._rkind,xIncr,nLook)**0.25_rkind ! use **0.25 to give more values near freezing + xTemp = T_lower + (Tfreeze - T_lower)*sqrt(sqrt(arth(0._rkind,xIncr,nLook))) ! use sqrt(sqrt()) to give more values near freezing ! ----- ! * allocate space for the lookup table... @@ -459,7 +459,7 @@ subroutine t2enthalpy(& enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth enthPhase = LH_fus * scalarCanopyIceTrial / canopyDepth ! derivatives - d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2._rkind) + d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) ! enthalpy derivatives dEnthLiq_dTk = Cp_water * scalarCanopyWatTrial * d_integral_dTk / canopyDepth dEnthIce_dTk = Cp_ice * scalarCanopyWatTrial * ( 1._rkind - d_integral_dTk ) / canopyDepth @@ -505,7 +505,7 @@ subroutine t2enthalpy(& enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) enthPhase = iden_ice * LH_fus * mLayerVolFracIceTrial(iLayer) ! derivatives - d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2._rkind) + d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) ! enthalpy derivatives dEnthLiq_dTk = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * d_integral_dTk dEnthIce_dTk = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( 1._rkind - d_integral_dTk ) diff --git a/build/source/engine/updateVarsWithPrime.f90 b/build/source/engine/updateVarsWithPrime.f90 index 0bd5b76d8..0cdb6a3a3 100644 --- a/build/source/engine/updateVarsWithPrime.f90 +++ b/build/source/engine/updateVarsWithPrime.f90 @@ -437,14 +437,14 @@ subroutine updateVarsWithPrime(& dTheta_dTkCanopy = dFracLiqVeg_dTkCanopy * scalarCanopyWatTrial/(iden_water*canopyDepth) if(computJac)then fLiq = fracLiquid(xTemp,snowfrz_scale) - d2Theta_dTkCanopy2 = 2._rkind * snowfrz_scale**2._rkind * ( (Tfreeze - xTemp) * 2._rkind * fLiq * dFracLiqVeg_dTkCanopy - fLiq**2._rkind ) * scalarCanopyWatTrial/(iden_water*canopyDepth) + d2Theta_dTkCanopy2 = 2._rkind * snowfrz_scale**2_i4b * ( (Tfreeze - xTemp) * 2._rkind * fLiq * dFracLiqVeg_dTkCanopy - fLiq**2_i4b ) * scalarCanopyWatTrial/(iden_water*canopyDepth) endif case(iname_snow) dFracLiqSnow_dTk(iLayer) = dFracLiq_dTk(xTemp,snowfrz_scale) mLayerdTheta_dTk(iLayer) = dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatTrial(iLayer) if(computJac)then fLiq = fracLiquid(xTemp,snowfrz_scale) - mLayerd2Theta_dTk2(iLayer) = 2._rkind * snowfrz_scale**2._rkind * ( (Tfreeze - xTemp) * 2._rkind * fLiq * dFracLiqSnow_dTk(iLayer) - fLiq**2._rkind ) * mLayerVolFracWatTrial(iLayer) + mLayerd2Theta_dTk2(iLayer) = 2._rkind * snowfrz_scale**2_i4b * ( (Tfreeze - xTemp) * 2._rkind * fLiq * dFracLiqSnow_dTk(iLayer) - fLiq**2_i4b ) * mLayerVolFracWatTrial(iLayer) endif case(iname_soil) dFracLiqSnow_dTk(iLayer) = 0._rkind !dTheta_dTk(xTemp,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex))/ mLayerVolFracWatTrial(iLayer) diff --git a/build/source/engine/vegLiqFlux.f90 b/build/source/engine/vegLiqFlux.f90 index 78e0ab758..72a348223 100644 --- a/build/source/engine/vegLiqFlux.f90 +++ b/build/source/engine/vegLiqFlux.f90 @@ -86,7 +86,7 @@ subroutine vegLiqFlux(& scalarCanopyLiqMax => diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1), & ! intent(in): maximum storage before canopy drainage begins (kg m-2 s-1) scalarThroughfallScaleRain => mpar_data%var(iLookPARAM%throughfallScaleRain)%dat(1),& ! intent(in): fraction of rain that hits the ground without touching the canopy (-) scalarCanopyDrainageCoeff => mpar_data%var(iLookPARAM%canopyDrainageCoeff)%dat(1) & ! intent(in): canopy drainage coefficient (s-1) - ) ! associating local variables with information in the data structures + ) ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! initialize error control err=0; message="vegLiqFlux/" @@ -102,34 +102,27 @@ subroutine vegLiqFlux(& ! compute throughfall select case(ixCanopyInterception) - ! original model (no flexibility in canopy interception): 100% of rainfall is intercepted by the vegetation canopy ! NOTE: this could be done with scalarThroughfallScaleRain=0, though requires setting scalarThroughfallScaleRain in all test cases case(unDefined) scalarThroughfallRain = 0._rkind scalarThroughfallRainDeriv = 0._rkind - ! fraction of rainfall hits the ground without ever touching the canopy case(sparseCanopy) scalarThroughfallRain = scalarThroughfallScaleRain*scalarRainfall scalarThroughfallRainDeriv = 0._rkind - ! throughfall a function of canopy storage case(storageFunc) - ! throughfall during wetting-up phase if(scalarCanopyLiqTrial < scalarCanopyLiqMax)then scalarThroughfallRain = scalarRainfall*(scalarCanopyLiqTrial/scalarCanopyLiqMax) scalarThroughfallRainDeriv = scalarRainfall/scalarCanopyLiqMax - ! all rain falls through the canopy when the canopy is at capacity else scalarThroughfallRain = scalarRainfall scalarThroughfallRainDeriv = 0._rkind end if - case default; err=20; message=trim(message)//'unable to identify option for canopy interception'; return - end select ! (option for canopy interception) ! compute canopy drainage diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index e0aa76713..2f9fb415f 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -119,8 +119,7 @@ module vegNrgFlux_module real(rkind),parameter :: tinyVal=epsilon(1._rkind) ! used as an additive constant to check if substantial difference among real numbers real(rkind),parameter :: mpe=1.e-6_rkind ! prevents overflow error if division by zero real(rkind),parameter :: dx=1.e-11_rkind ! finite difference increment -! control -logical(lgt) :: printflag ! flag to turn on printing + contains ! ******************************************************************************************************* @@ -132,7 +131,6 @@ subroutine vegNrgFlux(& firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation checkLWBalance, & ! intent(in): flag to check longwave balance - ! input: model state variables upperBoundTemp, & ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature canairTempTrial, & ! intent(in): trial value of the canopy air space temperature (K) @@ -140,10 +138,8 @@ subroutine vegNrgFlux(& groundTempTrial, & ! intent(in): trial value of ground temperature (K) canopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) canopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) - ! input: model derivatives dCanLiq_dTcanopy, & ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) - ! input/output: data structures type_data, & ! intent(in): type of vegetation and soil forc_data, & ! intent(in): model forcing data @@ -154,17 +150,14 @@ subroutine vegNrgFlux(& flux_data, & ! intent(inout): model fluxes for a local HRU bvar_data, & ! intent(in): model variables for the local basin model_decisions, & ! intent(in): model decisions - ! output: liquid water fluxes associated with evaporation/transpiration (needed for coupling) returnCanopyTranspiration, & ! intent(out): canopy transpiration (kg m-2 s-1) returnCanopyEvaporation, & ! intent(out): canopy evaporation/condensation (kg m-2 s-1) returnGroundEvaporation, & ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - ! output: fluxes canairNetFlux, & ! intent(out): net energy flux for the canopy air space (W m-2) canopyNetFlux, & ! intent(out): net energy flux for the vegetation canopy (W m-2) groundNetFlux, & ! intent(out): net energy flux for the ground surface (W m-2) - ! output: energy flux derivatives dCanairNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) dCanairNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) @@ -175,29 +168,24 @@ subroutine vegNrgFlux(& dGroundNetFlux_dCanairTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) dGroundNetFlux_dCanopyTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) dGroundNetFlux_dGroundTemp, & ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - ! output liquid water flux derivarives (canopy evap) dCanopyEvaporation_dCanWat, & ! intent(out): derivative in canopy evaporation w.r.t. canopy total water content (s-1) dCanopyEvaporation_dTCanair, & ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) dCanopyEvaporation_dTCanopy, & ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) dCanopyEvaporation_dTGround, & ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - ! output: liquid water flux derivarives (ground evap) dGroundEvaporation_dCanWat, & ! intent(out): derivative in ground evaporation w.r.t. canopy total water content (s-1) dGroundEvaporation_dTCanair, & ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) dGroundEvaporation_dTCanopy, & ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) dGroundEvaporation_dTGround, & ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - ! output: transpiration derivatives dCanopyTrans_dCanWat, & ! intent(out): derivative in canopy transpiration w.r.t. canopy total water content (s-1) dCanopyTrans_dTCanair, & ! intent(out): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) dCanopyTrans_dTCanopy, & ! intent(out): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) dCanopyTrans_dTGround, & ! intent(out): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - ! output: cross derivative terms dCanopyNetFlux_dCanWat, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) dGroundNetFlux_dCanWat, & ! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) - ! output: error control err,message) ! intent(out): error control @@ -229,10 +217,8 @@ subroutine vegNrgFlux(& real(rkind),intent(in) :: groundTempTrial ! trial value of ground temperature (K) real(rkind),intent(in) :: canopyIceTrial ! trial value of mass of ice on the vegetation canopy (kg m-2) real(rkind),intent(in) :: canopyLiqTrial ! trial value of mass of liquid water on the vegetation canopy (kg m-2) - ! input: model derivatives real(rkind),intent(in) :: dCanLiq_dTcanopy ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) - ! input/output: data structures type(var_i),intent(in) :: type_data ! type of vegetation and soil type(var_d),intent(in) :: forc_data ! model forcing data @@ -243,17 +229,14 @@ subroutine vegNrgFlux(& type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin type(model_options),intent(in) :: model_decisions(:) ! model decisions - ! output: liquid water fluxes associated with evaporation/transpiration (needed for coupling) real(rkind),intent(out) :: returnCanopyTranspiration ! canopy transpiration (kg m-2 s-1) real(rkind),intent(out) :: returnCanopyEvaporation ! canopy evaporation/condensation (kg m-2 s-1) real(rkind),intent(out) :: returnGroundEvaporation ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - ! output: fluxes real(rkind),intent(out) :: canairNetFlux ! net energy flux for the canopy air space (W m-2) real(rkind),intent(out) :: canopyNetFlux ! net energy flux for the vegetation canopy (W m-2) real(rkind),intent(out) :: groundNetFlux ! net energy flux for the ground surface (W m-2) - ! output: energy flux derivatives real(rkind),intent(out) :: dCanairNetFlux_dCanairTemp ! derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) real(rkind),intent(out) :: dCanairNetFlux_dCanopyTemp ! derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) @@ -264,29 +247,24 @@ subroutine vegNrgFlux(& real(rkind),intent(out) :: dGroundNetFlux_dCanairTemp ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) real(rkind),intent(out) :: dGroundNetFlux_dCanopyTemp ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) real(rkind),intent(out) :: dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - ! output: liquid flux derivatives (canopy evap) real(rkind),intent(out) :: dCanopyEvaporation_dCanWat ! derivative in canopy evaporation w.r.t. canopy total water content (s-1) real(rkind),intent(out) :: dCanopyEvaporation_dTCanair ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) real(rkind),intent(out) :: dCanopyEvaporation_dTCanopy ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) real(rkind),intent(out) :: dCanopyEvaporation_dTGround ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - ! output: liquid flux derivatives (ground evap) real(rkind),intent(out) :: dGroundEvaporation_dCanWat ! derivative in ground evaporation w.r.t. canopy total water content (s-1) real(rkind),intent(out) :: dGroundEvaporation_dTCanair ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) real(rkind),intent(out) :: dGroundEvaporation_dTCanopy ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) real(rkind),intent(out) :: dGroundEvaporation_dTGround ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - ! output: transpiration derivatives real(rkind),intent(out) :: dCanopyTrans_dCanWat ! intent(out): derivative in canopy transpiration w.r.t. canopy total water content (s-1) real(rkind),intent(out) :: dCanopyTrans_dTCanair ! intent(out): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) real(rkind),intent(out) :: dCanopyTrans_dTCanopy ! intent(out): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) real(rkind),intent(out) :: dCanopyTrans_dTGround ! intent(out): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - ! output: cross derivative terms real(rkind),intent(out) :: dCanopyNetFlux_dCanWat ! derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) real(rkind),intent(out) :: dGroundNetFlux_dCanWat ! derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) - ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -294,14 +272,13 @@ subroutine vegNrgFlux(& ! --------------------------------------------------------------------------------------- ! * local variables ! --------------------------------------------------------------------------------------- - ! local (general) + ! general) character(LEN=256) :: cmessage ! error message of downwind routine real(rkind) :: VAI ! vegetation area index (m2 m-2) real(rkind) :: exposedVAI ! exposed vegetation area index (m2 m-2) real(rkind) :: totalCanopyWater ! total water on the vegetation canopy (kg m-2) real(rkind) :: scalarAquiferStorage ! aquifer storage (m) - - ! local (compute numerical derivatives) + ! compute numerical derivatives integer(i4b),parameter :: unperturbed=1 ! named variable to identify the case of unperturbed state variables integer(i4b),parameter :: perturbStateGround=2 ! named variable to identify the case where we perturb the ground temperature integer(i4b),parameter :: perturbStateCanopy=3 ! named variable to identify the case where we perturb the canopy temperature @@ -316,20 +293,17 @@ subroutine vegNrgFlux(& real(rkind) :: canopyLiq ! value of canopy liquid water used in flux calculations (may be perturbed) real(rkind) :: canopyIce ! value of canopy ice used in flux calculations (may be perturbed) real(rkind) :: try0,try1 ! trial values to evaluate specific derivatives (testing only) - - ! local (saturation vapor pressure of veg) + ! saturation vapor pressure of veg real(rkind) :: TV_celcius ! vegetaion temperature (C) real(rkind) :: TG_celcius ! ground temperature (C) real(rkind) :: dSVPCanopy_dCanopyTemp ! derivative in canopy saturated vapor pressure w.r.t. vegetation temperature (Pa/K) real(rkind) :: dSVPGround_dGroundTemp ! derivative in ground saturated vapor pressure w.r.t. ground temperature (Pa/K) - - ! local (wetted canopy area) + ! wetted canopy area real(rkind) :: fracLiquidCanopy ! fraction of liquid water in the canopy (-) real(rkind) :: canopyWetFraction ! trial value of the canopy wetted fraction (-) real(rkind) :: dCanopyWetFraction_dWat ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) real(rkind) :: dCanopyWetFraction_dT ! derivative in wetted fraction w.r.t. canopy temperature (K-1) - - ! local (longwave radiation) + ! longwave radiation real(rkind) :: expi ! exponential integral real(rkind) :: scaleLAI ! scaled LAI (computing diffuse transmissivity) real(rkind) :: diffuseTrans ! diffuse transmissivity (-) @@ -341,12 +315,10 @@ subroutine vegNrgFlux(& real(rkind) :: dLWNetGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) real(rkind) :: dLWNetCanopy_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) real(rkind) :: dLWNetGround_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) - - ! local (aerodynamic resistance) + ! aerodynamic resistance real(rkind) :: scalarCanopyStabilityCorrection_old ! stability correction for the canopy (-) real(rkind) :: scalarGroundStabilityCorrection_old ! stability correction for the ground surface (-) - - ! local (turbulent heat transfer) + ! turbulent heat transfer real(rkind) :: z0Ground ! roughness length of the ground (ground below the canopy or non-vegetated surface) (m) real(rkind) :: soilEvapFactor ! soil water control on evaporation from non-vegetated surfaces real(rkind) :: soilRelHumidity_noSnow ! relative humidity in the soil pores [0-1] @@ -366,9 +338,8 @@ subroutine vegNrgFlux(& real(rkind) :: turbFluxCanair ! total turbulent heat fluxes exchanged at the canopy air space (W m-2) real(rkind) :: turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) real(rkind) :: turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) - - ! local (turbulent heat transfer -- compute numerical derivatives) - ! (temporary scalar resistances when states are perturbed) + ! turbulent heat transfer -- compute numerical derivatives + ! temporary scalar resistances when states are perturbed real(rkind) :: trialLeafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) real(rkind) :: trialGroundResistance ! below canopy aerodynamic resistance (s m-1) real(rkind) :: trialCanopyResistance ! above canopy aerodynamic resistance (s m-1) @@ -388,61 +359,52 @@ subroutine vegNrgFlux(& real(rkind) :: notUsed_dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) real(rkind) :: notUsed_dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) real(rkind) :: notUsed_dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - - ! (fluxes after perturbations in model states -- canopy air space) + ! fluxes after perturbations in model states -- canopy air space real(rkind) :: turbFluxCanair_dStateCanair ! turbulent exchange from the canopy air space to the atmosphere, after canopy air temperature is perturbed (W m-2) real(rkind) :: turbFluxCanair_dStateCanopy ! turbulent exchange from the canopy air space to the atmosphere, after canopy temperature is perturbed (W m-2) real(rkind) :: turbFluxCanair_dStateGround ! turbulent exchange from the canopy air space to the atmosphere, after ground temperature is perturbed (W m-2) real(rkind) :: turbFluxCanair_dStateCanWat ! turbulent exchange from the canopy air space to the atmosphere, after canopy total water content is perturbed (W m-2) - ! (fluxes after perturbations in model states -- vegetation canopy) + ! fluxes after perturbations in model states -- vegetation canopy real(rkind) :: turbFluxCanopy_dStateCanair ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy air temperature is perturbed (W m-2) real(rkind) :: turbFluxCanopy_dStateCanopy ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy temperature is perturbed (W m-2) real(rkind) :: turbFluxCanopy_dStateGround ! total turbulent heat fluxes from the canopy to the canopy air space, after ground temperature is perturbed (W m-2) real(rkind) :: turbFluxCanopy_dStateCanWat ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy total water content is perturbed (W m-2) - - ! (fluxes after perturbations in model states -- ground surface) + ! fluxes after perturbations in model states -- ground surface real(rkind) :: turbFluxGround_dStateCanair ! total turbulent heat fluxes from the ground to the canopy air space, after canopy air temperature is perturbed (W m-2) real(rkind) :: turbFluxGround_dStateCanopy ! total turbulent heat fluxes from the ground to the canopy air space, after canopy temperature is perturbed (W m-2) real(rkind) :: turbFluxGround_dStateGround ! total turbulent heat fluxes from the ground to the canopy air space, after ground temperature is perturbed (W m-2) real(rkind) :: turbFluxGround_dStateCanWat ! total turbulent heat fluxes from the ground to the canopy air space, after canopy total water content is perturbed (W m-2) - - ! (fluxes after perturbations in model states -- canopy evaporation) + ! fluxes after perturbations in model states -- canopy evaporation real(rkind) :: latHeatCanEvap_dStateCanair ! canopy evaporation after canopy air temperature is perturbed (W m-2) real(rkind) :: latHeatCanEvap_dStateCanopy ! canopy evaporation after canopy temperature is perturbed (W m-2) real(rkind) :: latHeatCanEvap_dStateGround ! canopy evaporation after ground temperature is perturbed (W m-2) real(rkind) :: latHeatCanEvap_dStateCanWat ! canopy evaporation after canopy total water content is perturbed (W m-2) - - ! (flux derivatives -- canopy air space) + ! flux derivatives -- canopy air space real(rkind) :: dTurbFluxCanair_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) real(rkind) :: dTurbFluxCanair_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) real(rkind) :: dTurbFluxCanair_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) real(rkind) :: dTurbFluxCanair_dCanWat ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) - - ! (flux derivatives -- vegetation canopy) + ! flux derivatives -- vegetation canopy real(rkind) :: dTurbFluxCanopy_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) real(rkind) :: dTurbFluxCanopy_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) real(rkind) :: dTurbFluxCanopy_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) real(rkind) :: dTurbFluxCanopy_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - - ! (flux derivatives -- ground surface) + ! flux derivatives -- ground surface real(rkind) :: dTurbFluxGround_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) real(rkind) :: dTurbFluxGround_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) real(rkind) :: dTurbFluxGround_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) real(rkind) :: dTurbFluxGround_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - - ! (liquid water flux derivatives -- canopy evap) + ! liquid water flux derivatives -- canopy evap real(rkind) :: dLatHeatCanopyEvap_dCanWat ! derivative in latent heat of canopy evaporation w.r.t. canopy total water content (W kg-1) real(rkind) :: dLatHeatCanopyEvap_dTCanair ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) real(rkind) :: dLatHeatCanopyEvap_dTCanopy ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) real(rkind) :: dLatHeatCanopyEvap_dTGround ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) - - ! (liquid water flux derivatives -- ground evap) + ! liquid water flux derivatives -- ground evap real(rkind) :: dLatHeatGroundEvap_dCanWat ! derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) real(rkind) :: dLatHeatGroundEvap_dTCanair ! derivative in latent heat of ground evaporation w.r.t. canopy air temperature (W m-2 K-1) real(rkind) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) real(rkind) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) - - ! output: latent heat flux derivatives (canopy trans) + ! latent heat flux derivatives -- canopy trans real(rkind) :: dLatHeatCanopyTrans_dCanWat ! derivative in the latent heat of canopy transpiration w.r.t. canopy total water (J kg-1 s-1) real(rkind) :: dLatHeatCanopyTrans_dTCanair ! derivative in the latent heat of canopy transpiration w.r.t. canopy air temperature real(rkind) :: dLatHeatCanopyTrans_dTCanopy ! derivative in the latent heat of canopy transpiration w.r.t. canopy temperature @@ -452,7 +414,6 @@ subroutine vegNrgFlux(& ! point to variables in the data structure ! --------------------------------------------------------------------------------------- associate(& - ! input: model decisions ix_bcUpprTdyn => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision, & ! intent(in): [i4b] choice of upper boundary condition for thermodynamics ixDerivMethod => model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): [i4b] choice of method to compute derivatives @@ -464,16 +425,13 @@ subroutine vegNrgFlux(& ix_groundwatr => model_decisions(iLookDECISIONS%groundwatr)%iDecision, & ! intent(in): [i4b] choice of groundwater parameterization ix_stomResist => model_decisions(iLookDECISIONS%stomResist)%iDecision, & ! intent(in): [i4b] choice of function for stomatal resistance ix_spatial_gw => model_decisions(iLookDECISIONS%spatial_gw)%iDecision, & ! intent(in): [i4b] choice of groundwater representation (local, basin) - ! input: layer geometry nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): [i4b] number of snow layers nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1), & ! intent(in): [i4b] number of soil layers nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): [i4b] total number of layers - ! input: physical attributes vegTypeIndex => type_data%var(iLookTYPE%vegTypeIndex), & ! intent(in): [i4b] vegetation type index soilTypeIndex => type_data%var(iLookTYPE%soilTypeIndex), & ! intent(in): [i4b] soil type index - ! input: vegetation parameters heightCanopyTop => mpar_data%var(iLookPARAM%heightCanopyTop)%dat(1), & ! intent(in): [dp] height at the top of the vegetation canopy (m) heightCanopyBottom => mpar_data%var(iLookPARAM%heightCanopyBottom)%dat(1), & ! intent(in): [dp] height at the bottom of the vegetation canopy (m) @@ -481,7 +439,6 @@ subroutine vegNrgFlux(& canopyWettingExp => mpar_data%var(iLookPARAM%canopyWettingExp)%dat(1), & ! intent(in): [dp] exponent in canopy wetting function (-) scalarCanopyIceMax => diag_data%var(iLookDIAG%scalarCanopyIceMax)%dat(1), & ! intent(in): [dp] maximum interception storage capacity for ice (kg m-2) scalarCanopyLiqMax => diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1), & ! intent(in): [dp] maximum interception storage capacity for liquid water (kg m-2) - ! input: vegetation phenology scalarLAI => diag_data%var(iLookDIAG%scalarLAI)%dat(1), & ! intent(in): [dp] one-sided leaf area index (m2 m-2) scalarSAI => diag_data%var(iLookDIAG%scalarSAI)%dat(1), & ! intent(in): [dp] one-sided stem area index (m2 m-2) @@ -489,7 +446,6 @@ subroutine vegNrgFlux(& scalarExposedSAI => diag_data%var(iLookDIAG%scalarExposedSAI)%dat(1), & ! intent(in): [dp] exposed stem area index after burial by snow (m2 m-2) scalarGrowingSeasonIndex => diag_data%var(iLookDIAG%scalarGrowingSeasonIndex)%dat(1), & ! intent(in): [dp] growing season index (0=off, 1=on) scalarFoliageNitrogenFactor => diag_data%var(iLookDIAG%scalarFoliageNitrogenFactor)%dat(1), & ! intent(in): [dp] foliage nitrogen concentration (1.0 = saturated) - ! input: aerodynamic resistance parameters z0Snow => mpar_data%var(iLookPARAM%z0Snow)%dat(1), & ! intent(in): [dp] roughness length of snow (m) z0Soil => mpar_data%var(iLookPARAM%z0Soil)%dat(1), & ! intent(in): [dp] roughness length of soil (m) @@ -502,7 +458,6 @@ subroutine vegNrgFlux(& windReductionParam => mpar_data%var(iLookPARAM%windReductionParam)%dat(1), & ! intent(in): [dp] canopy wind reduction parameter (-) leafExchangeCoeff => mpar_data%var(iLookPARAM%leafExchangeCoeff)%dat(1), & ! intent(in): [dp] turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) leafDimension => mpar_data%var(iLookPARAM%leafDimension)%dat(1), & ! intent(in): [dp] characteristic leaf dimension (m) - ! input: soil stress parameters theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(1), & ! intent(in): [dp] soil porosity (-) theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(1), & ! intent(in): [dp] residual volumetric liquid water content (-) @@ -512,10 +467,8 @@ subroutine vegNrgFlux(& critSoilTranspire => mpar_data%var(iLookPARAM%critSoilTranspire)%dat(1), & ! intent(in): [dp] critical vol. liq. water content when transpiration is limited (-) critAquiferTranspire => mpar_data%var(iLookPARAM%critAquiferTranspire)%dat(1), & ! intent(in): [dp] critical aquifer storage value when transpiration is limited (m) minStomatalResistance => mpar_data%var(iLookPARAM%minStomatalResistance)%dat(1), & ! intent(in): [dp] mimimum stomatal resistance (s m-1) - ! snow parameters snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ! input: forcing at the upper boundary mHeight => diag_data%var(iLookDIAG%scalarAdjMeasHeight)%dat(1), & ! intent(in): [dp] measurement height (m) airtemp => forc_data%var(iLookFORCE%airtemp), & ! intent(in): [dp] air temperature at some height above the surface (K) @@ -530,7 +483,6 @@ subroutine vegNrgFlux(& scalarSnowfall => flux_data%var(iLookFLUX%scalarSnowfall)%dat(1), & ! intent(in): [dp] computed snowfall rate (kg m-2 s-1) scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1), & ! intent(in): [dp] rainfall through the vegetation canopy (kg m-2 s-1) scalarThroughfallSnow => flux_data%var(iLookFLUX%scalarThroughfallSnow)%dat(1), & ! intent(in): [dp] snowfall through the vegetation canopy (kg m-2 s-1) - ! input: water storage ! NOTE: soil stress only computed at the start of the substep (firstFluxCall=.true.) scalarSWE => prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(in): [dp] snow water equivalent on the ground (kg m-2) @@ -539,7 +491,6 @@ subroutine vegNrgFlux(& mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat, & ! intent(in): [dp(:)] matric head in each soil layer (m) localAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1), & ! intent(in): [dp] aquifer storage for the local column (m) basinAquiferStorage => bvar_data%var(iLookBVAR%basin__AquiferStorage)%dat(1), & ! intent(in): [dp] aquifer storage for the single basin (m) - ! input: shortwave radiation fluxes scalarCanopySunlitLAI => diag_data%var(iLookDIAG%scalarCanopySunlitLAI)%dat(1), & ! intent(in): [dp] sunlit leaf area (-) scalarCanopyShadedLAI => diag_data%var(iLookDIAG%scalarCanopyShadedLAI)%dat(1), & ! intent(in): [dp] shaded leaf area (-) @@ -547,11 +498,9 @@ subroutine vegNrgFlux(& scalarCanopyShadedPAR => flux_data%var(iLookFLUX%scalarCanopyShadedPAR)%dat(1), & ! intent(in): [dp] average absorbed par for shaded leaves (w m-2) scalarCanopyAbsorbedSolar => flux_data%var(iLookFLUX%scalarCanopyAbsorbedSolar)%dat(1), & ! intent(in): [dp] solar radiation absorbed by canopy (W m-2) scalarGroundAbsorbedSolar => flux_data%var(iLookFLUX%scalarGroundAbsorbedSolar)%dat(1), & ! intent(in): [dp] solar radiation absorbed by ground (W m-2) - ! output: fraction of wetted canopy area and fraction of snow on the ground scalarCanopyWetFraction => diag_data%var(iLookDIAG%scalarCanopyWetFraction)%dat(1), & ! intent(out): [dp] fraction of canopy that is wet scalarGroundSnowFraction => diag_data%var(iLookDIAG%scalarGroundSnowFraction)%dat(1), & ! intent(out): [dp] fraction of ground covered with snow (-) - ! output: longwave radiation fluxes scalarCanopyEmissivity => diag_data%var(iLookDIAG%scalarCanopyEmissivity)%dat(1), & ! intent(out): [dp] effective emissivity of the canopy (-) scalarLWRadCanopy => flux_data%var(iLookFLUX%scalarLWRadCanopy)%dat(1), & ! intent(out): [dp] longwave radiation emitted from the canopy (W m-2) @@ -567,7 +516,6 @@ subroutine vegNrgFlux(& scalarLWNetCanopy => flux_data%var(iLookFLUX%scalarLWNetCanopy)%dat(1), & ! intent(out): [dp] net longwave radiation at the canopy (W m-2) scalarLWNetGround => flux_data%var(iLookFLUX%scalarLWNetGround)%dat(1), & ! intent(out): [dp] net longwave radiation at the ground surface (W m-2) scalarLWNetUbound => flux_data%var(iLookFLUX%scalarLWNetUbound)%dat(1), & ! intent(out): [dp] net longwave radiation at the upper boundary (W m-2) - ! output: aerodynamic resistance scalarZ0Canopy => diag_data%var(iLookDIAG%scalarZ0Canopy)%dat(1), & ! intent(out): [dp] roughness length of the canopy (m) scalarWindReductionFactor => diag_data%var(iLookDIAG%scalarWindReductionFactor)%dat(1), & ! intent(out): [dp] canopy wind reduction factor (-) @@ -581,7 +529,6 @@ subroutine vegNrgFlux(& scalarLeafResistance => flux_data%var(iLookFLUX%scalarLeafResistance)%dat(1), & ! intent(out): [dp] mean leaf boundary layer resistance per unit leaf area (s m-1) scalarGroundResistance => flux_data%var(iLookFLUX%scalarGroundResistance)%dat(1), & ! intent(out): [dp] below canopy aerodynamic resistance (s m-1) scalarCanopyResistance => flux_data%var(iLookFLUX%scalarCanopyResistance)%dat(1), & ! intent(out): [dp] above canopy aerodynamic resistance (s m-1) - ! input/output: soil resistance -- intent(in) and intent(inout) because only called at the first flux call mLayerRootDensity => diag_data%var(iLookDIAG%mLayerRootDensity)%dat, & ! intent(in): [dp] root density in each layer (-) scalarAquiferRootFrac => diag_data%var(iLookDIAG%scalarAquiferRootFrac)%dat(1), & ! intent(in): [dp] fraction of roots below the lowest soil layer (-) @@ -590,13 +537,11 @@ subroutine vegNrgFlux(& scalarTranspireLimAqfr => diag_data%var(iLookDIAG%scalarTranspireLimAqfr)%dat(1), & ! intent(inout): [dp] transpiration limiting factor for the aquifer (-) scalarSoilRelHumidity => diag_data%var(iLookDIAG%scalarSoilRelHumidity)%dat(1), & ! intent(inout): [dp] relative humidity in the soil pores [0-1] scalarSoilResistance => flux_data%var(iLookFLUX%scalarSoilResistance)%dat(1), & ! intent(inout): [dp] resistance from the soil (s m-1) - ! input/output: stomatal resistance -- intent(inout) because only called at the first flux call scalarStomResistSunlit => flux_data%var(iLookFLUX%scalarStomResistSunlit)%dat(1), & ! intent(inout): [dp] stomatal resistance for sunlit leaves (s m-1) scalarStomResistShaded => flux_data%var(iLookFLUX%scalarStomResistShaded)%dat(1), & ! intent(inout): [dp] stomatal resistance for shaded leaves (s m-1) scalarPhotosynthesisSunlit => flux_data%var(iLookFLUX%scalarPhotosynthesisSunlit)%dat(1), & ! intent(inout): [dp] sunlit photosynthesis (umolco2 m-2 s-1) scalarPhotosynthesisShaded => flux_data%var(iLookFLUX%scalarPhotosynthesisShaded)%dat(1), & ! intent(inout): [dp] shaded photosynthesis (umolco2 m-2 s-1) - ! output: turbulent heat fluxes scalarLatHeatSubVapCanopy => diag_data%var(iLookDIAG%scalarLatHeatSubVapCanopy)%dat(1), & ! intent(inout): [dp] latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) scalarLatHeatSubVapGround => diag_data%var(iLookDIAG%scalarLatHeatSubVapGround)%dat(1), & ! intent(inout): [dp] latent heat of sublimation/vaporization for the ground surface (J kg-1) @@ -609,25 +554,20 @@ subroutine vegNrgFlux(& scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1), & ! intent(out): [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) scalarLatHeatCanopyTrans => flux_data%var(iLookFLUX%scalarLatHeatCanopyTrans)%dat(1), & ! intent(out): [dp] latent heat flux for transpiration from the canopy to the canopy air space (W m-2) scalarLatHeatGround => flux_data%var(iLookFLUX%scalarLatHeatGround)%dat(1), & ! intent(out): [dp] latent heat flux from ground surface below vegetation (W m-2) - ! output: advective heat fluxes scalarCanopyAdvectiveHeatFlux => flux_data%var(iLookFLUX%scalarCanopyAdvectiveHeatFlux)%dat(1), & ! intent(out): [dp] heat advected to the canopy surface with rain + snow (W m-2) scalarGroundAdvectiveHeatFlux => flux_data%var(iLookFLUX%scalarGroundAdvectiveHeatFlux)%dat(1), & ! intent(out): [dp] heat advected to the ground surface with throughfall (W m-2) - ! output: mass fluxes scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1), & ! intent(out): [dp] canopy sublimation/frost (kg m-2 s-1) scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1), & ! intent(out): [dp] snow sublimation/frost -- below canopy or non-vegetated (kg m-2 s-1) - ! input/output: canopy air space variables scalarVP_CanopyAir => diag_data%var(iLookDIAG%scalarVP_CanopyAir)%dat(1), & ! intent(inout): [dp] vapor pressure of the canopy air space (Pa) scalarCanopyStabilityCorrection => diag_data%var(iLookDIAG%scalarCanopyStabilityCorrection)%dat(1),& ! intent(inout): [dp] stability correction for the canopy (-) scalarGroundStabilityCorrection => diag_data%var(iLookDIAG%scalarGroundStabilityCorrection)%dat(1),& ! intent(inout): [dp] stability correction for the ground surface (-) - ! output: liquid water fluxes scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1), & ! intent(out): [dp] canopy transpiration (kg m-2 s-1) scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1), & ! intent(out): [dp] canopy evaporation/condensation (kg m-2 s-1) scalarGroundEvaporation => flux_data%var(iLookFLUX%scalarGroundEvaporation)%dat(1), & ! intent(out): [dp] ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - ! output: derived fluxes scalarTotalET => flux_data%var(iLookFLUX%scalarTotalET)%dat(1), & ! intent(out): [dp] total ET (kg m-2 s-1) scalarNetRadiation => flux_data%var(iLookFLUX%scalarNetRadiation)%dat(1) & ! intent(out): [dp] net radiation (W m-2) @@ -636,20 +576,15 @@ subroutine vegNrgFlux(& ! initialize error control err=0; message="vegNrgFlux/" - ! initialize printflag - printflag = .false. - ! identify the type of boundary condition for thermodynamics select case(ix_bcUpprTdyn) ! ***** ! (1) DIRICHLET OR ZERO FLUX BOUNDARY CONDITION... - ! ************************************************ - - ! NOTE: Vegetation fluxes are not computed in this case - ! ** prescribed temperature or zero flux at the upper boundary of the snow-soil system - case(prescribedTemp,zeroFlux) + ! NOTE: Vegetation fluxes are not computed in this case + ! ************************************************ + case(prescribedTemp,zeroFlux) ! derived fluxes scalarTotalET = 0._rkind ! total ET (kg m-2 s-1) @@ -702,22 +637,15 @@ subroutine vegNrgFlux(& err=20; message=trim(message)//'unable to identify upper boundary condition for thermodynamics: expect the case to be prescribedTemp or zeroFlux'; return end if - ! ***** - ! (2) NEUMANN BOUNDARY CONDITION... - ! ********************************* - - ! NOTE 1: This is the main routine for calculating vegetation fluxes - ! NOTE 2: This routine also calculates surface fluxes for the case where vegetation is buried with snow (or bare soil) - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! ***** PRELIMINARIES ********************************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - - ! * flux boundary condition + ! ***** + ! (2) NEUMANN BOUNDARY CONDITION... + ! ** flux boundary condition + ! NOTE 1: This is the main routine for calculating vegetation fluxes + ! NOTE 2: This routine also calculates surface fluxes for the case where vegetation is buried with snow (or bare soil) + ! ************************************************ case(energyFlux) + ! ***** PRELIMINARIES ***************************************************************************************************************************************** ! identify the appropriate groundwater variable select case(ix_spatial_gw) case(singleBasin); scalarAquiferStorage = basinAquiferStorage @@ -764,19 +692,13 @@ subroutine vegNrgFlux(& ! compute emissivity of the canopy (-) if(computeVegFlux)then select case(ix_canopyEmis) - ! *** simple exponential function - case(simplExp) - scalarCanopyEmissivity = 1._rkind - exp(-exposedVAI) ! effective emissivity of the canopy (-) - ! *** canopy emissivity parameterized as a function of diffuse transmissivity - case(difTrans) - ! compute the exponential integral + case(simplExp) ! *** simple exponential function + scalarCanopyEmissivity = 1._rkind - exp(-exposedVAI) ! effective emissivity of the canopy (-) + case(difTrans) ! *** canopy emissivity parameterized as a function of diffuse transmissivity scaleLAI = 0.5_rkind*exposedVAI - expi = expInt(scaleLAI) - ! compute diffuse transmissivity (-) - diffuseTrans = (1._rkind - scaleLAI)*exp(-scaleLAI) + (scaleLAI**2._rkind)*expi - ! compute the canopy emissivity - scalarCanopyEmissivity = (1._rkind - diffuseTrans)*vegEmissivity - ! *** check we found the correct option + expi = expInt(scaleLAI) ! compute the exponential integral + diffuseTrans = (1._rkind - scaleLAI)*exp(-scaleLAI) + (scaleLAI**2_i4b)*expi ! compute diffuse transmissivity (-) + scalarCanopyEmissivity = (1._rkind - diffuseTrans)*vegEmissivity ! compute the canopy emissivity case default err=20; message=trim(message)//'unable to identify option for canopy emissivity'; return end select @@ -827,15 +749,8 @@ subroutine vegNrgFlux(& dCanopyWetFraction_dT = 0._rkind ! derivative in wetted fraction w.r.t. canopy temperature (K-1) end if - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* ! ***** AERODYNAMIC RESISTANCE ***************************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! NOTE: compute for all iterations - - ! compute aerodynamic resistances ! Refs: Choudhury and Monteith (4-layer model for heat budget of homogenous surfaces; QJRMS, 1988) ! Niu and Yang (Canopy effects on snow processes; JGR, 2004) ! Mahat et al. (Below-canopy turbulence in a snowmelt model, WRR, 2012) @@ -894,15 +809,10 @@ subroutine vegNrgFlux(& err,cmessage ) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* ! ***** STOMATAL RESISTANCE ***************************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! stomatal resistance is constant over the SUBSTEP ! NOTE: This is a simplification, as stomatal resistance does depend on canopy temperature - ! This "short-cut" made because: + ! This is a "short-cut" made because: ! (1) computations are expensive; ! (2) derivative calculations are rather complex (iterations within the Ball-Berry routine); and ! (3) stomatal resistance does not change rapidly @@ -957,13 +867,7 @@ subroutine vegNrgFlux(& end if ! (if the first flux call in a given sub-step) - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* ! ***** LONGWAVE RADIATION ***************************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! compute canopy longwave radiation balance call longwaveBal(& ! input: model control @@ -1003,13 +907,7 @@ subroutine vegNrgFlux(& err,cmessage ) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* ! ***** TURBULENT HEAT FLUXES ************************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - ! check the need to compute numerical derivatives if(ixDerivMethod == numerical)then nFlux=5 ! compute the derivatives using one-sided finite differences @@ -1024,48 +922,41 @@ subroutine vegNrgFlux(& ! state perturbations for numerical deriavtives with one-sided finite differences ! note: no perturbations performed using analytical derivatives (nFlux=1) ! ------------------------------------------------------------------------------------- - ! initialize for unperturbed state canopyWetFraction = scalarCanopyWetFraction ! identify the type of perturbation select case(itry) - ! un-perturbed case case(unperturbed) groundTemp = groundTempTrial canopyTemp = canopyTempTrial canairTemp = canairTempTrial canopyWat = totalCanopyWater - ! perturb ground temperature case(perturbStateGround) groundTemp = groundTempTrial + dx canopyTemp = canopyTempTrial canairTemp = canairTempTrial canopyWat = totalCanopyWater - ! perturb canopy temperature case(perturbStateCanopy) groundTemp = groundTempTrial canopyTemp = canopyTempTrial + dx canairTemp = canairTempTrial canopyWat = totalCanopyWater - ! perturb canopy air temperature case(perturbStateCanair) groundTemp = groundTempTrial canopyTemp = canopyTempTrial canairTemp = canairTempTrial + dx canopyWat = totalCanopyWater - ! perturb canopy total water content case(perturbStateCanWat) groundTemp = groundTempTrial canopyTemp = canopyTempTrial canairTemp = canairTempTrial canopyWat = totalCanopyWater + dx - ! check for an unknown perturbation case default; err=10; message=trim(message)//"unknown perturbation"; return @@ -1119,7 +1010,6 @@ subroutine vegNrgFlux(& ! ------------------------------------------------------------------------------------- ! calculation block (unperturbed fluxes returned [computed last]) ! ------------------------------------------------------------------------------------- - ! re-compute aerodynamic resistances for perturbed cases ! NOTE: unperturbed fluxes computed earlier, and not over-written if(itry /= unperturbed)then @@ -1177,26 +1067,23 @@ subroutine vegNrgFlux(& ! output: error control err,cmessage ) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - ! assign scalar resistances for un-perturbed cases else trialLeafResistance = scalarLeafResistance trialGroundResistance = scalarGroundResistance trialCanopyResistance = scalarCanopyResistance - end if ! (re-computing resistances for perturbed cases) ! compute the relative humidity in the top soil layer and the resistance at the ground surface ! NOTE: computations are based on start-of-step values, so only compute for the first flux call if(firstFluxCall)then - ! (soil water evaporation factor [0-1]) + ! soil water evaporation factor [0-1] soilEvapFactor = mLayerVolFracLiq(nSnow+1)/(theta_sat - theta_res) - ! (resistance from the soil [s m-1]) + ! resistance from the soil [s m-1] scalarSoilResistance = scalarGroundSnowFraction*1._rkind + (1._rkind - scalarGroundSnowFraction)*EXP(8.25_rkind - 4.225_rkind*soilEvapFactor) ! Sellers (1992) !scalarSoilResistance = scalarGroundSnowFraction*0._rkind + (1._rkind - scalarGroundSnowFraction)*exp(8.25_rkind - 6.0_rkind*soilEvapFactor) ! Niu adjustment to decrease resitance for wet soil - ! (relative humidity in the soil pores [0-1]) + ! relative humidity in the soil pores [0-1] if(mLayerMatricHead(1) > -1.e+6_rkind)then ! avoid problems with numerical precision when soil is very dry soilRelHumidity_noSnow = exp( (mLayerMatricHead(1)*gravity) / (groundTemp*R_wv) ) else @@ -1303,17 +1190,6 @@ subroutine vegNrgFlux(& err,cmessage ) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - !notUsed_scalarCanopyStabilityCorrection ! stability correction for the canopy (-) - !notUsed_scalarGroundStabilityCorrection ! stability correction for the ground surface (-) - !notUsed_EddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) - !notUsed_FrictionVelocity ! friction velocity (m s-1) - !notUsed_WindspdCanopyTop ! windspeed at the top of the canopy (m s-1) - !notUsed_WindspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) - !trialLeafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) - !trialGroundResistance ! below canopy aerodynamic resistance (s m-1) - !trialCanopyResistance ! above canopy aerodynamic resistance (s m-1) - ! save perturbed fluxes if(ixDerivMethod == numerical)then select case(itry) ! (select type of perturbation) @@ -1347,7 +1223,6 @@ subroutine vegNrgFlux(& end do ! (looping through different flux perturbations) - ! compute numerical derivatives if(ixDerivMethod == numerical)then ! derivatives w.r.t. canopy air temperature @@ -1372,7 +1247,6 @@ subroutine vegNrgFlux(& dLatHeatCanopyEvap_dCanWat = (latHeatCanEvap_dStateCanWat - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy total water content (J kg-1 s-1) end if - ! compute the heat advected with precipitation (W m-2) ! NOTE: fluxes are in kg m-2 s-1, so no need to use density of water/ice here scalarCanopyAdvectiveHeatFlux = -Cp_water*(scalarRainfall - scalarThroughfallRain)*(canopyTempTrial - scalarTwetbulb) + & @@ -1380,12 +1254,9 @@ subroutine vegNrgFlux(& scalarGroundAdvectiveHeatFlux = -Cp_water*scalarThroughfallRain*(groundTempTrial - scalarTwetbulb) + & (-Cp_ice)*scalarThroughfallSnow*(groundTempTrial - scalarTwetbulb) - ! compute the mass flux associated with transpiration and evaporation/sublimation (J m-2 s-1 --> kg m-2 s-1) ! NOTE: remove water from the snow on the ground in preference to removing water from the water in soil pores - - ! (canopy transpiration/sublimation) - if(scalarLatHeatSubVapCanopy > LH_vap+verySmall)then ! sublimation + if(scalarLatHeatSubVapCanopy > LH_vap+verySmall)then ! canopy sublimation scalarCanopyEvaporation = 0._rkind scalarCanopySublimation = scalarLatHeatCanopyEvap/LH_sub if(scalarLatHeatCanopyTrans > 0._rkind)then ! flux directed towards the veg @@ -1394,8 +1265,7 @@ subroutine vegNrgFlux(& else scalarCanopyTranspiration = scalarLatHeatCanopyTrans/LH_vap ! transpiration is always vapor end if - ! (canopy transpiration/evaporation) - else ! evaporation + else ! canopy evaporation scalarCanopyEvaporation = scalarLatHeatCanopyEvap/LH_vap scalarCanopySublimation = 0._rkind if(scalarLatHeatCanopyTrans > 0._rkind)then ! flux directed towards the veg @@ -1405,8 +1275,7 @@ subroutine vegNrgFlux(& scalarCanopyTranspiration = scalarLatHeatCanopyTrans/LH_vap end if end if - ! (ground evaporation/sublimation) - if(scalarLatHeatSubVapGround > LH_vap+verySmall)then ! sublimation + if(scalarLatHeatSubVapGround > LH_vap+verySmall)then ! ground sublimation ! NOTE: this should only occur when we have formed snow layers, so check if(nSnow == 0)then; err=20; message=trim(message)//'only expect snow sublimation when we have formed some snow layers'; return; end if scalarGroundEvaporation = 0._rkind ! ground evaporation is zero once the snowpack has formed @@ -1418,13 +1287,8 @@ subroutine vegNrgFlux(& scalarSnowSublimation = 0._rkind ! no sublimation from snow if no snow layers have formed end if - - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* ! ***** AND STITCH EVERYTHING TOGETHER ***************************************************************************************************************************** - ! ******************************************************************************************************************************************************************* - ! ******************************************************************************************************************************************************************* - + ! compute derived fluxes scalarTotalET = scalarGroundEvaporation + scalarCanopyEvaporation + scalarCanopyTranspiration scalarNetRadiation = scalarCanopyAbsorbedSolar + scalarLWNetCanopy + scalarGroundAbsorbedSolar + scalarLWNetGround @@ -1447,15 +1311,12 @@ subroutine vegNrgFlux(& ! check if evaporation or sublimation if(scalarLatHeatSubVapCanopy < LH_vap+verySmall)then ! evaporation - ! compute the liquid water derivarives dCanopyEvaporation_dCanWat = dLatHeatCanopyEvap_dCanWat/LH_vap ! (s-1) dCanopyEvaporation_dTCanair = dLatHeatCanopyEvap_dTCanair/LH_vap ! (kg m-2 s-1 K-1) dCanopyEvaporation_dTCanopy = dLatHeatCanopyEvap_dTCanopy/LH_vap ! (kg m-2 s-1 K-1) dCanopyEvaporation_dTGround = dLatHeatCanopyEvap_dTGround/LH_vap ! (kg m-2 s-1 K-1) - - ! sublimation - else + else ! sublimation dCanopyEvaporation_dCanWat = 0._rkind ! (s-1) dCanopyEvaporation_dTCanair = 0._rkind ! (kg m-2 s-1 K-1) dCanopyEvaporation_dTCanopy = 0._rkind ! (kg m-2 s-1 K-1) @@ -1475,7 +1336,6 @@ subroutine vegNrgFlux(& dCanopyTrans_dTGround= dLatHeatCanopyTrans_dTGround/LH_vap end if - ! compute the liquid water derivarives (ground evap) dGroundEvaporation_dCanWat = dLatHeatGroundEvap_dCanWat/LH_vap ! (s-1) dGroundEvaporation_dTCanair = dLatHeatGroundEvap_dTCanair/LH_vap ! (kg m-2 s-1 K-1) @@ -1486,7 +1346,7 @@ subroutine vegNrgFlux(& dCanopyNetFlux_dCanWat = dTurbFluxCanopy_dCanWat ! derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) dGroundNetFlux_dCanWat = dTurbFluxGround_dCanWat ! derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) - ! * check + ! * check upper boundary condition case default; err=10; message=trim(message)//'unable to identify upper boundary condition for thermodynamics'; return ! end case statement @@ -1670,10 +1530,10 @@ subroutine logisticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) ! only compute smoothing function for small exponents if(xArg > -xLimit .and. xArg < xLimit)then ! avoid huge exponents - expX = exp(-xarg) ! (also used in the derivative) - smoothFunc = 1._rkind / (1._rkind + expX) ! (logistic smoother) + expX = exp(-xarg) ! also used in the derivative + smoothFunc = 1._rkind / (1._rkind + expX) ! logistic smoother if(derDesire)then - smoothFuncDeriv = expX / (smoothScale * (1._rkind + expX)**2._rkind) ! (derivative in the smoothing function) + smoothFuncDeriv = expX / (smoothScale * (1._rkind + expX)**2_i4b) ! derivative in the smoothing function else smoothFuncDeriv = 0._rkind end if @@ -1687,7 +1547,6 @@ subroutine logisticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) end if ! check for huge exponents end subroutine logisticSmoother - ! -------------------------------------------------------------------------------------------------------------- ! ******************************************************************************************************* @@ -1797,7 +1656,6 @@ subroutine longwaveBal(& ! either one or multiple flux calls, depending on if using analytical or numerical derivatives do itry=nFlux,1,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) - ! ------------------------------------------------------------------------------------- ! state perturbations for numerical deriavtives with one-sided finite differences ! note: no perturbations performed using analytical derivatives (nFlux=1) @@ -1805,25 +1663,20 @@ subroutine longwaveBal(& ! identify the type of perturbation select case(itry) - ! un-perturbed case case(unperturbed) TCan = canopyTemp TGnd = groundTemp - ! perturb canopy temperature case(perturbStateCanopy) TCan = canopyTemp + dx TGnd = groundTemp - ! perturb ground temperature case(perturbStateGround) TCan = canopyTemp TGnd = groundTemp + dx - ! check for an unknown perturbation case default; err=10; message=trim(message)//"unknown perturbation"; return - end select ! (type of perturbation) ! ------------------------------------------------------------------------------------- @@ -1833,11 +1686,11 @@ subroutine longwaveBal(& ! compute longwave fluxes from canopy and the ground if(computeVegFlux)then - LWRadCanopy = emc*sb*TCan**4._rkind ! longwave radiation emitted from the canopy (W m-2) + LWRadCanopy = emc*sb*TCan**4_i4b ! longwave radiation emitted from the canopy (W m-2) else LWRadCanopy = 0._rkind end if - LWRadGround = emg*sb*TGnd**4._rkind ! longwave radiation emitted at the ground surface (W m-2) + LWRadGround = emg*sb*TGnd**4_i4b ! longwave radiation emitted at the ground surface (W m-2) ! compute fluxes originating from the atmosphere LWRadUbound2Canopy = (emc + (1._rkind - emc)*(1._rkind - emg)*emc)*LWRadUbound ! downward atmospheric longwave radiation absorbed by the canopy (W m-2) @@ -1904,29 +1757,22 @@ subroutine longwaveBal(& ! compute derivatives ! ------------------------------------------------------------------------------------- select case(ixDerivMethod) - - ! ***** analytical derivatives case(analytical) ! compute initial derivatives - dLWRadCanopy_dTCanopy = 4._rkind*emc*sb*TCan**3._rkind - dLWRadGround_dTGround = 4._rkind*emg*sb*TGnd**3._rkind + dLWRadCanopy_dTCanopy = 4._rkind*emc*sb*TCan**3_i4b + dLWRadGround_dTGround = 4._rkind*emg*sb*TGnd**3_i4b ! compute analytical derivatives dLWNetCanopy_dTCanopy = (emc*(1._rkind - emg) - 2._rkind)*dLWRadCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) dLWNetGround_dTGround = -dLWRadGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) dLWNetCanopy_dTGround = emc*dLWRadGround_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) dLWNetGround_dTCanopy = emg*dLWRadCanopy_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) - - ! ***** numerical derivatives case(numerical) ! compute numerical derivatives (one-sided finite differences) dLWNetCanopy_dTCanopy = (LWNetCanopy_dStateCanopy - LWNetCanopy)/dx ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) dLWNetGround_dTGround = (LWNetGround_dStateGround - LWNetGround)/dx ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) dLWNetCanopy_dTGround = (LWNetCanopy_dStateGround - LWNetCanopy)/dx ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) dLWNetGround_dTCanopy = (LWNetGround_dStateCanopy - LWNetGround)/dx ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) - - ! ***** error check case default; err=10; message=trim(message)//"unknown method to calculate derivatives"; return - end select ! (type of method to calculate derivatives) end subroutine longwaveBal @@ -2053,7 +1899,7 @@ subroutine aeroResist(& ! local variables: vegetation roughness and dispalcement height real(rkind),parameter :: oneThird=1._rkind/3._rkind ! 1/3 real(rkind),parameter :: twoThirds=2._rkind/3._rkind ! 2/3 - real(rkind),parameter :: C_r = 0.3 ! roughness element drag coefficient (-) from Raupach (BLM, 1994) + real(rkind),parameter :: C_r = 0.3_rkind ! roughness element drag coefficient (-) from Raupach (BLM, 1994) real(rkind),parameter :: C_s = 0.003_rkind ! substrate surface drag coefficient (-) from Raupach (BLM, 1994) real(rkind),parameter :: approxDragCoef_max = 0.3_rkind ! maximum value of the approximate drag coefficient (-) from Raupach (BLM, 1994) real(rkind),parameter :: psi_h = 0.193_rkind ! roughness sub-layer influence function (-) from Raupach (BLM, 1994) @@ -2123,9 +1969,9 @@ subroutine aeroResist(& ! Choudhury and Monteith (QJRMS 1988) "A four layer model for the heat budget..." case(CM_QJRMS1988) funcLAI = cd_CM*exposedVAI - zeroPlaneDisplacement = 1.1_rkind*heightCanopyTopAboveSnow*log(1._rkind + funcLAI**0.25_rkind) + zeroPlaneDisplacement = 1.1_rkind*heightCanopyTopAboveSnow*log(1._rkind + sqrt(sqrt(funcLAI))) if(funcLAI < 0.2_rkind)then - z0Canopy = z0Ground + 0.3_rkind*heightCanopyTopAboveSnow*funcLAI**0.5_rkind + z0Canopy = z0Ground + 0.3_rkind*heightCanopyTopAboveSnow*sqrt(funcLAI) else z0Canopy = 0.3_rkind*heightCanopyTopAboveSnow*(1._rkind - zeroPlaneDisplacement/heightCanopyTopAboveSnow) end if @@ -2180,7 +2026,7 @@ subroutine aeroResist(& if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! compute turbulent exchange coefficient (-) - canopyExNeut = (vkc**2._rkind) / ( log((mHeight - zeroPlaneDisplacement)/z0Canopy))**2._rkind ! coefficient under conditions of neutral stability + canopyExNeut = (vkc**2_i4b) / ( log((mHeight - zeroPlaneDisplacement)/z0Canopy))**2_i4b ! coefficient under conditions of neutral stability sfc2AtmExchangeCoeff_canopy = canopyExNeut*canopyStabilityCorrection ! after stability corrections ! compute the friction velocity (m s-1) @@ -2240,7 +2086,7 @@ subroutine aeroResist(& tmp2 = exp(-windReductionFactor*(z0Canopy+zeroPlaneDisplacement)/heightCanopyTopAboveSnow) groundResistanceNeutral = ( heightCanopyTopAboveSnow*exp(windReductionFactor) / (windReductionFactor*eddyDiffusCanopyTop) ) * (tmp1 - tmp2) ! (add log-below-canopy component) - groundResistanceNeutral = groundResistanceNeutral + (1._rkind/(max(0.1_rkind,windspdCanopyBottom)*vkc**2._rkind))*(log(heightCanopyBottomAboveSnow/z0Ground))**2._rkind + groundResistanceNeutral = groundResistanceNeutral + (1._rkind/(max(0.1_rkind,windspdCanopyBottom)*vkc**2_i4b))*(log(heightCanopyBottomAboveSnow/z0Ground))**2_i4b endif ! switch between exponential profile and log-below-canopy @@ -2285,7 +2131,7 @@ subroutine aeroResist(& if(mHeight < snowDepth+z0Ground)then; err=20; message=trim(message)//'measurement height < snow depth + roughness length'; return; end if ! compute the resistance between the surface and canopy air UNDER NEUTRAL CONDITIONS (s m-1) - groundExNeut = (vkc**2._rkind) / ( log((mHeight - snowDepth)/z0Ground)**2._rkind) ! turbulent transfer coefficient under conditions of neutral stability (-) + groundExNeut = (vkc**2_i4b) / ( log((mHeight - snowDepth)/z0Ground)**2_i4b) ! turbulent transfer coefficient under conditions of neutral stability (-) groundResistanceNeutral = 1._rkind / (groundExNeut*windspd) ! define height above the snow surface @@ -2293,12 +2139,12 @@ subroutine aeroResist(& ! check that measurement height above the ground surface is above the roughness length if(heightAboveGround < z0Ground)then - print*, 'z0Ground = ', z0Ground - print*, 'mHeight = ', mHeight - print*, 'snowDepth = ', snowDepth - print*, 'heightAboveGround = ', heightAboveGround - message=trim(message)//'height above ground < roughness length [likely due to snow accumulation]' - err=20; return + print*, 'z0Ground = ', z0Ground + print*, 'mHeight = ', mHeight + print*, 'snowDepth = ', snowDepth + print*, 'heightAboveGround = ', heightAboveGround + message=trim(message)//'height above ground < roughness length [likely due to snow accumulation]' + err=20; return end if ! compute ground stability correction @@ -2350,36 +2196,29 @@ subroutine aeroResist(& ! derivatives for the vegetation canopy if(computeVegFlux) then ! (if vegetation is exposed) - ! ***** compute derivatives w.r.t. canopy temperature ! NOTE: derivatives are zero because using canopy air space temperature dCanopyResistance_dTCanopy = 0._rkind ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) dGroundResistance_dTCanopy = 0._rkind ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - ! ***** compute derivatives w.r.t. ground temperature (s m-1 K-1) - dGroundResistance_dTGround = -(groundResistanceNeutral*dGroundStabilityCorrection_dSfcTemp)/(groundStabilityCorrection**2._rkind) - + dGroundResistance_dTGround = -(groundResistanceNeutral*dGroundStabilityCorrection_dSfcTemp)/(groundStabilityCorrection**2_i4b) ! ***** compute derivatives w.r.t. temperature of the canopy air space (s m-1 K-1) ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - dCanopyResistance_dTCanair = -dCanopyStabilityCorrection_dCasTemp/(windspd*canopyExNeut*canopyStabilityCorrection**2._rkind) + dCanopyResistance_dTCanair = -dCanopyStabilityCorrection_dCasTemp/(windspd*canopyExNeut*canopyStabilityCorrection**2_i4b) ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) ! (compute derivative in NEUTRAL ground resistance w.r.t. canopy air temperature (s m-1 K-1)) dFV_dT = windspd*canopyExNeut*dCanopyStabilityCorrection_dCasTemp/(sqrt(sfc2AtmExchangeCoeff_canopy)*2._rkind) ! d(frictionVelocity)/d(canopy air temperature) dED_dT = dFV_dT*vkc*(heightCanopyTopAboveSnow - zeroPlaneDisplacement) ! d(eddyDiffusCanopyTop)d(canopy air temperature) - dGR_dT = -dED_dT*(tmp1 - tmp2)*heightCanopyTopAboveSnow*exp(windReductionFactor) / (windReductionFactor*eddyDiffusCanopyTop**2._rkind) ! d(groundResistanceNeutral)/d(canopy air temperature) + dGR_dT = -dED_dT*(tmp1 - tmp2)*heightCanopyTopAboveSnow*exp(windReductionFactor) / (windReductionFactor*eddyDiffusCanopyTop**2_i4b) ! d(groundResistanceNeutral)/d(canopy air temperature) ! (stitch everything together -- product rule) - dGroundResistance_dTCanair = dGR_dT/groundStabilityCorrection - groundResistanceNeutral*dGroundStabilityCorrection_dCasTemp/(groundStabilityCorrection**2._rkind) - + dGroundResistance_dTCanair = dGR_dT/groundStabilityCorrection - groundResistanceNeutral*dGroundStabilityCorrection_dCasTemp/(groundStabilityCorrection**2_i4b) ! ***** compute resistances for non-vegetated surfaces (e.g., snow) else - ! set canopy derivatives to zero (non-vegetated, remember) dCanopyResistance_dTCanopy = 0._rkind dGroundResistance_dTCanopy = 0._rkind - ! compute derivatives for ground resistance - dGroundResistance_dTGround = -dGroundStabilityCorrection_dSfcTemp/(windspd*groundExNeut*groundStabilityCorrection**2._rkind) - + dGroundResistance_dTGround = -dGroundStabilityCorrection_dSfcTemp/(windspd*groundExNeut*groundStabilityCorrection**2_i4b) end if ! (switch between vegetated and non-vegetated surfaces) ! * analytical derivatives not desired @@ -2792,11 +2631,11 @@ subroutine turbFluxes(& if(computeVegFlux)then dEvapCond_dCanopyTemp = dCanopyWetFraction_dT*leafConductance ! derivative in evap conductance w.r.t. canopy temperature dTransCond_dCanopyTemp = -dCanopyWetFraction_dT*leafConductanceTr ! derivative in trans conductance w.r.t. canopy temperature - dCanopyCond_dCanairTemp = -dCanopyResistance_dTCanair/canopyResistance**2._rkind ! derivative in canopy conductance w.r.t. canopy air emperature - dCanopyCond_dCanopyTemp = -dCanopyResistance_dTCanopy/canopyResistance**2._rkind ! derivative in canopy conductance w.r.t. canopy temperature - dGroundCondSH_dCanairTemp = -dGroundResistance_dTCanair/groundResistance**2._rkind ! derivative in ground conductance w.r.t. canopy air temperature - dGroundCondSH_dCanopyTemp = -dGroundResistance_dTCanopy/groundResistance**2._rkind ! derivative in ground conductance w.r.t. canopy temperature - dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2._rkind ! derivative in ground conductance w.r.t. ground temperature + dCanopyCond_dCanairTemp = -dCanopyResistance_dTCanair/canopyResistance**2_i4b ! derivative in canopy conductance w.r.t. canopy air emperature + dCanopyCond_dCanopyTemp = -dCanopyResistance_dTCanopy/canopyResistance**2_i4b ! derivative in canopy conductance w.r.t. canopy temperature + dGroundCondSH_dCanairTemp = -dGroundResistance_dTCanair/groundResistance**2_i4b ! derivative in ground conductance w.r.t. canopy air temperature + dGroundCondSH_dCanopyTemp = -dGroundResistance_dTCanopy/groundResistance**2_i4b ! derivative in ground conductance w.r.t. canopy temperature + dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2_i4b ! derivative in ground conductance w.r.t. ground temperature else dEvapCond_dCanopyTemp = 0._rkind ! derivative in evap conductance w.r.t. canopy temperature dTransCond_dCanopyTemp = 0._rkind ! derivative in trans conductance w.r.t. canopy temperature @@ -2804,21 +2643,21 @@ subroutine turbFluxes(& dCanopyCond_dCanopyTemp = 0._rkind ! derivative in canopy conductance w.r.t. canopy temperature dGroundCondSH_dCanairTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy air temperature dGroundCondSH_dCanopyTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy temperature - dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2._rkind ! derivative in ground conductance w.r.t. ground temperature - end if + dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2_i4b ! derivative in ground conductance w.r.t. ground temperature + endif ! compute derivatives in individual conductances for latent heat w.r.t. canopy temperature (m s-1 K-1) if(computeVegFlux)then - dGroundCondLH_dCanairTemp = -dGroundResistance_dTCanair/(groundResistance+soilResistance)**2._rkind ! derivative in ground conductance w.r.t. canopy air temperature - dGroundCondLH_dCanopyTemp = -dGroundResistance_dTCanopy/(groundResistance+soilResistance)**2._rkind ! derivative in ground conductance w.r.t. canopy temperature - dGroundCondLH_dGroundTemp = -dGroundResistance_dTGround/(groundResistance+soilResistance)**2._rkind ! derivative in ground conductance w.r.t. ground temperature + dGroundCondLH_dCanairTemp = -dGroundResistance_dTCanair/(groundResistance+soilResistance)**2_i4b ! derivative in ground conductance w.r.t. canopy air temperature + dGroundCondLH_dCanopyTemp = -dGroundResistance_dTCanopy/(groundResistance+soilResistance)**2_i4b ! derivative in ground conductance w.r.t. canopy temperature + dGroundCondLH_dGroundTemp = -dGroundResistance_dTGround/(groundResistance+soilResistance)**2_i4b ! derivative in ground conductance w.r.t. ground temperature else dGroundCondLH_dCanairTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy air temperature dGroundCondLH_dCanopyTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy temperature - dGroundCondLH_dGroundTemp = -dGroundResistance_dTGround/(groundResistance+soilResistance)**2._rkind ! derivative in ground conductance w.r.t. ground temperature + dGroundCondLH_dGroundTemp = -dGroundResistance_dTGround/(groundResistance+soilResistance)**2_i4b ! derivative in ground conductance w.r.t. ground temperature end if - end if ! (if computing analytical derivatives) + endif ! (if computing analytical derivatives) ! ***** ! * compute sensible and latent heat fluxes, and derivatives... @@ -2840,13 +2679,12 @@ subroutine turbFluxes(& latHeatCanopyEvap = -latHeatSubVapCanopy*latentHeatConstant*evapConductance*(satVP_CanopyTemp - VP_CanopyAir) ! (positive downwards) latHeatCanopyTrans = -LH_vap*latentHeatConstant*transConductance*(satVP_CanopyTemp - VP_CanopyAir) ! (positive downwards) - ! * no vegetation, so fluxes are zero else senHeatCanopy = 0._rkind latHeatCanopyEvap = 0._rkind latHeatCanopyTrans = 0._rkind - end if + endif ! compute sensible and latent heat fluxes from the ground to the canopy air space (W m-2) if(computeVegFlux)then @@ -2856,8 +2694,7 @@ subroutine turbFluxes(& senHeatGround = -volHeatCapacityAir*groundConductanceSH*(groundTemp - airtemp) ! (positive downwards) latHeatGround = -latHeatSubVapGround*latentHeatConstant*groundConductanceLH*(satVP_GroundTemp*soilRelHumidity - VPair) ! (positive downwards) senHeatTotal = senHeatGround - end if - + endif ! compute latent heat flux from the canopy air space to the atmosphere ! NOTE: VP_CanopyAir is a diagnostic variable @@ -2872,20 +2709,20 @@ subroutine turbFluxes(& ! compute derivatives of vapor pressure in the canopy air space w.r.t. all state variables ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the canopy air space) dPart1 = dCanopyCond_dCanairTemp*VPair + dGroundCondLH_dCanairTemp*satVP_GroundTemp*soilRelHumidity - dPart2 = -(dCanopyCond_dCanairTemp + dGroundCondLH_dCanairTemp)/(totalConductanceLH**2._rkind) + dPart2 = -(dCanopyCond_dCanairTemp + dGroundCondLH_dCanairTemp)/(totalConductanceLH**2_i4b) dVPCanopyAir_dTCanair = dPart1/totalConductanceLH + fPart_VP*dPart2 ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the canopy) dPart0 = (evapConductance + transConductance)*dSVPCanopy_dCanopyTemp + (dEvapCond_dCanopyTemp + dTransCond_dCanopyTemp)*satVP_CanopyTemp dPart1 = dCanopyCond_dCanopyTemp*VPair + dPart0 + dGroundCondLH_dCanopyTemp*satVP_GroundTemp*soilRelHumidity - dPart2 = -(dCanopyCond_dCanopyTemp + dEvapCond_dCanopyTemp + dTransCond_dCanopyTemp + dGroundCondLH_dCanopyTemp)/(totalConductanceLH**2._rkind) + dPart2 = -(dCanopyCond_dCanopyTemp + dEvapCond_dCanopyTemp + dTransCond_dCanopyTemp + dGroundCondLH_dCanopyTemp)/(totalConductanceLH**2_i4b) dVPCanopyAir_dTCanopy = dPart1/totalConductanceLH + fPart_VP*dPart2 ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the ground) dPart1 = dGroundCondLH_dGroundTemp*satVP_GroundTemp*soilRelHumidity + groundConductanceLH*dSVPGround_dGroundTemp*soilRelHumidity - dPart2 = -dGroundCondLH_dGroundTemp/(totalConductanceLH**2._rkind) + dPart2 = -dGroundCondLH_dGroundTemp/(totalConductanceLH**2_i4b) dVPCanopyAir_dTGround = dPart1/totalConductanceLH + fPart_VP*dPart2 ! (derivative of vapor pressure in the canopy air space w.r.t. wetted fraction of the canopy) dPart1 = (leafConductance - leafConductanceTr)*satVP_CanopyTemp - dPart2 = -(leafConductance - leafConductanceTr)/(totalConductanceLH**2._rkind) + dPart2 = -(leafConductance - leafConductanceTr)/(totalConductanceLH**2_i4b) dVPCanopyAir_dWetFrac = dPart1/totalConductanceLH + fPart_VP*dPart2 dVPCanopyAir_dCanWat = dVPCanopyAir_dWetFrac*dCanopyWetFraction_dWat @@ -2944,7 +2781,6 @@ subroutine turbFluxes(& ! latent heat associated with canopy transpiration w.r.t. canopy total water dLatHeatCanopyTrans_dCanWat = dLatHeatCanopyTrans_dWetFrac*dCanopyWetFraction_dWat ! (J s-1 kg-1) - else ! canopy is undefined ! set derivatives for canopy fluxes to zero (no canopy, so fluxes are undefined) @@ -3112,7 +2948,7 @@ subroutine aStability(& ! ***** process unstable cases if(RiBulk<0._rkind)then ! compute surface-atmosphere exchange coefficient (-) - stabilityCorrection = (1._rkind - 16._rkind*RiBulk)**0.5_rkind + stabilityCorrection = sqrt(1._rkind - 16._rkind*RiBulk) ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) if(computeDerivative)then dStabilityCorrection_dRich = (-16._rkind) * 0.5_rkind*(1._rkind - 16._rkind*RiBulk)**(-0.5_rkind) @@ -3128,11 +2964,11 @@ subroutine aStability(& ! ("standard" stability correction, a la Anderson 1976) case(standard) ! compute surface-atmosphere exchange coefficient (-) - if(RiBulk < critRichNumber) stabilityCorrection = (1._rkind - 5._rkind*RiBulk)**2._rkind + if(RiBulk < critRichNumber) stabilityCorrection = (1._rkind - 5._rkind*RiBulk)**2_i4b if(RiBulk >= critRichNumber) stabilityCorrection = verySmall ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) if(computeDerivative)then - if(RiBulk < critRichNumber) dStabilityCorrection_dRich = (-5._rkind) * 2._rkind*(1._rkind - 5._rkind*RiBulk) + if(RiBulk < critRichNumber) dStabilityCorrection_dRich = -10._rkind*(1._rkind - 5._rkind*RiBulk) if(RiBulk >= critRichNumber) dStabilityCorrection_dRich = verySmall end if @@ -3141,7 +2977,7 @@ subroutine aStability(& ! scale the "b" parameter for stable conditions bprime = Louis79_bparam/2._rkind ! compute surface-atmosphere exchange coefficient (-) - stabilityCorrection = 1._rkind / ( (1._rkind + bprime*RiBulk)**2._rkind ) + stabilityCorrection = 1._rkind / ( (1._rkind + bprime*RiBulk)**2_i4b ) if(stabilityCorrection < epsilon(stabilityCorrection)) stabilityCorrection = epsilon(stabilityCorrection) ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) if(computeDerivative)then @@ -3191,21 +3027,21 @@ subroutine bulkRichardson(& err,message) ! output: error control implicit none ! input -real(rkind),intent(in) :: airtemp ! air temperature (K) -real(rkind),intent(in) :: sfcTemp ! surface temperature (K) -real(rkind),intent(in) :: windspd ! wind speed (m s-1) -real(rkind),intent(in) :: mHeight ! measurement height (m) +real(rkind),intent(in) :: airtemp ! air temperature (K) +real(rkind),intent(in) :: sfcTemp ! surface temperature (K) +real(rkind),intent(in) :: windspd ! wind speed (m s-1) +real(rkind),intent(in) :: mHeight ! measurement height (m) logical(lgt),intent(in) :: computeDerivative ! flag to compute the derivative ! output -real(rkind),intent(inout) :: RiBulk ! bulk Richardson number (-) -real(rkind),intent(out) :: dRiBulk_dAirTemp ! derivative in the bulk Richardson number w.r.t. air temperature (K-1) -real(rkind),intent(out) :: dRiBulk_dSfcTemp ! derivative in the bulk Richardson number w.r.t. surface temperature (K-1) +real(rkind),intent(inout) :: RiBulk ! bulk Richardson number (-) +real(rkind),intent(out) :: dRiBulk_dAirTemp ! derivative in the bulk Richardson number w.r.t. air temperature (K-1) +real(rkind),intent(out) :: dRiBulk_dSfcTemp ! derivative in the bulk Richardson number w.r.t. surface temperature (K-1) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local variables -real(rkind) :: T_grad ! gradient in temperature between the atmosphere and surface (K) -real(rkind) :: T_mean ! mean of the atmosphere and surface temperature (K) -real(rkind) :: RiMult ! dimensionless scaling factor (-) +real(rkind) :: T_grad ! gradient in temperature between the atmosphere and surface (K) +real(rkind) :: T_mean ! mean of the atmosphere and surface temperature (K) +real(rkind) :: RiMult ! dimensionless scaling factor (-) ! initialize error control err=0; message='bulkRichardson/' ! compute local variables @@ -3216,8 +3052,8 @@ subroutine bulkRichardson(& RiBulk = (T_grad/T_mean) * RiMult ! compute the derivative in the Richardson number if(computeDerivative)then - dRiBulk_dAirTemp = RiMult/T_mean - RiMult*T_grad/(0.5_rkind*((airtemp + sfcTemp)**2._rkind)) - dRiBulk_dSfcTemp = -RiMult/T_mean - RiMult*T_grad/(0.5_rkind*((airtemp + sfcTemp)**2._rkind)) + dRiBulk_dAirTemp = RiMult/T_mean - RiMult*T_grad/(0.5_rkind*((airtemp + sfcTemp)**2_i4b)) + dRiBulk_dSfcTemp = -RiMult/T_mean - RiMult*T_grad/(0.5_rkind*((airtemp + sfcTemp)**2_i4b)) else dRiBulk_dAirTemp = 1._rkind dRiBulk_dSfcTemp = 1._rkind diff --git a/build/source/engine/vegSWavRad.f90 b/build/source/engine/vegSWavRad.f90 index c438af531..44d2363db 100644 --- a/build/source/engine/vegSWavRad.f90 +++ b/build/source/engine/vegSWavRad.f90 @@ -587,7 +587,7 @@ subroutine canopy_SW(& ! compute transmission of diffuse radiation (-) vFactor = scalarGproj*scalarExposedVAI expi = expInt(vFactor) - taudFinite = (1._rkind - vFactor)*exp(-vFactor) + (vFactor**2._rkind)*expi + taudFinite = (1._rkind - vFactor)*exp(-vFactor) + (vFactor**2_i4b)*expi ! compute ground albedo (-) groundAlbedoDirect = Frad_vis*spectralAlbGndDirect(ixVisible) + (1._rkind - Frad_vis)*spectralAlbGndDirect(ixNearIR) @@ -690,19 +690,19 @@ subroutine canopy_SW(& betaInfinite = (1._rkind - transCoefPrime)/(1._rkind + transCoefPrime) ! compute transmission for a finite canopy (-) - tauFinite = tauInfinite*(1._rkind - betaInfinite**2._rkind)/(1._rkind - (betaInfinite**2._rkind)*tauInfinite**2._rkind) + tauFinite = tauInfinite*(1._rkind - betaInfinite**2_i4b)/(1._rkind - (betaInfinite**2_i4b)*tauInfinite**2_i4b) ! compute reflectance for a finite canopy (-) - betaFinite = betaInfinite*(1._rkind - tauInfinite**2._rkind) / (1._rkind - (betaInfinite**2._rkind)*(tauInfinite**2._rkind)) + betaFinite = betaInfinite*(1._rkind - tauInfinite**2_i4b) / (1._rkind - (betaInfinite**2_i4b)*(tauInfinite**2_i4b)) ! compute transmission of diffuse radiation (-) vFactor = transCoefPrime*scalarGproj*scalarExposedVAI expi = expInt(vFactor) - taudInfinite = (1._rkind - vFactor)*exp(-vFactor) + (vFactor**2._rkind)*expi - taudFinite = taudInfinite*(1._rkind - betaInfinite**2._rkind)/(1._rkind - (betaInfinite**2._rkind)*taudInfinite**2._rkind) + taudInfinite = (1._rkind - vFactor)*exp(-vFactor) + (vFactor**2_i4b)*expi + taudFinite = taudInfinite*(1._rkind - betaInfinite**2_i4b)/(1._rkind - (betaInfinite**2_i4b)*taudInfinite**2_i4b) ! compute reflectance of diffuse radiation (-) - betadFinite = betaInfinite*(1._rkind - taudInfinite**2._rkind) / (1._rkind - (betaInfinite**2._rkind)*(taudInfinite**2._rkind)) + betadFinite = betaInfinite*(1._rkind - taudInfinite**2_i4b) / (1._rkind - (betaInfinite**2_i4b)*(taudInfinite**2_i4b)) ! compute total transmission of direct and diffuse radiation, accounting for multiple reflections (-) refMult = 1._rkind / (1._rkind - groundAlbedoDiffuse*betadFinite*(1._rkind - taudFinite) ) From b3b3171495e12f9db9e6bf360561ecbd259edbb0 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 23 Jun 2023 19:12:03 +0900 Subject: [PATCH 0767/1472] This should fix the issue of low ET. diag variable soil compression was overwriting the ET. --- build/source/dshare/var_lookup.f90 | 1 - build/source/engine/coupled_em.f90 | 27 ++++++++++++++++----------- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/varSubstep.f90 | 4 ++-- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 60e14f6de..b70e00b43 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -955,5 +955,4 @@ MODULE var_lookup integer(i4b),allocatable,save,public :: childFLUX_MEAN(:) ! index of the child data structure: mean flux - END MODULE var_lookup diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index d65338472..cb70c48ec 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -232,8 +232,8 @@ subroutine coupled_em(& integer(i4b) :: iSoil ! index of soil layers type(var_dlength) :: flux_mean ! timestep-average model fluxes for a local HRU type(var_dlength) :: flux_inner ! inner step average model fluxes for a local HRU - real(rkind) :: meanSoilCompress ! timestep-average soil compression - real(rkind) :: innerSoilCompress ! inner step average soil compression + real(rkind),allocatable :: meanSoilCompress(:) ! timestep-average soil compression by layer + real(rkind),allocatable :: innerSoilCompress(:) ! inner step average soil compression by layer ! sublimation sums over substep and means over data_step real(rkind) :: sumCanopySublimation ! sum of sublimation from the vegetation canopy (kg m-2 s-1) over substep real(rkind) :: sumSnowSublimation ! sum of sublimation from the snow surface (kg m-2 s-1) over substep @@ -352,7 +352,7 @@ subroutine coupled_em(& do iVar=1,size(averageFlux_meta) flux_mean%var(iVar)%dat(:) = 0._rkind end do - meanSoilCompress = 0._rkind ! mean total soil compression + allocate(meanSoilCompress(nSoil)); meanSoilCompress = 0._rkind ! mean total soil compression meanCanopySublimation = 0._rkind ! mean canopy sublimation meanLatHeatCanopyEvap = 0._rkind ! mean latent heat flux for evaporation from the canopy meanSenHeatCanopy = 0._rkind ! mean sensible heat flux from the canopy @@ -834,8 +834,8 @@ subroutine coupled_em(& do iVar=1,size(averageFlux_meta) flux_inner%var(iVar)%dat(:) = 0._rkind end do - innerSoilCompress = 0._rkind ! mean total soil compression - innerEffRainfall = 0._rkind ! mean total effective rainfall over snow + allocate(innerSoilCompress(nSoil)); innerSoilCompress = 0._rkind ! mean total soil compression + innerEffRainfall = 0._rkind ! mean total effective rainfall over snow endif ! (do_outer loop) @@ -888,7 +888,7 @@ subroutine coupled_em(& ! handle special case of the step failure ! NOTE: need to revert back to the previous state vector that we were happy with and reduce the time step - ! TODO: ask isn't this what the actors program does without the code block below + ! TODO: ask isn't this what the actors program does without the code block below if(stepFailure)then ! halve whole_step, for more frequent outer loop updates whole_step = dtSave/2._rkind @@ -901,6 +901,7 @@ subroutine coupled_em(& endif ! try again, restart step deallocate(mLayerVolFracIceInit) + deallocate(innerSoilCompress) cycle substeps endif @@ -1045,7 +1046,7 @@ subroutine coupled_em(& do iVar=1,size(averageFlux_meta) flux_inner%var(iVar)%dat(:) = flux_inner%var(iVar)%dat(:) + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:)*dt_wght end do - innerSoilCompress = innerSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1)*dt_wght + innerSoilCompress(:) = innerSoilCompress(:) + diag_data%var(iLookDIAG%mLayerCompress)%dat(:)*dt_wght if (nSnow>0) innerEffRainfall = innerEffRainfall + ( flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) + flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) )*dt_wght ! increment sub-step accepted step @@ -1062,12 +1063,12 @@ subroutine coupled_em(& do iVar=1,size(averageFlux_meta) flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_inner%var(iVar)%dat(:)*dt_wght end do - meanSoilCompress = meanSoilCompress + innerSoilCompress*dt_wght + meanSoilCompress(:) = meanSoilCompress(:) + innerSoilCompress(:)*dt_wght ! not in flux structure so handle differently + deallocate(innerSoilCompress) meanCanopySublimation = meanCanopySublimation + sumCanopySublimation/data_step meanLatHeatCanopyEvap = meanLatHeatCanopyEvap + sumLatHeatCanopyEvap/data_step meanSenHeatCanopy = meanSenHeatCanopy + sumSenHeatCanopy/data_step effRainfall = effRainfall + innerEffRainfall*dt_wght - flux_mean%var(childFLUX_MEAN(iLookDIAG%scalarSoilCompress))%dat(1) = meanSoilCompress flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySublimation))%dat(1) = meanCanopySublimation flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarLatHeatCanopyEvap))%dat(1) = meanLatHeatCanopyEvap flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSenHeatCanopy))%dat(1) = meanSenHeatCanopy @@ -1143,7 +1144,11 @@ subroutine coupled_em(& do iVar=1,size(averageFlux_meta) flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:) = flux_mean%var(iVar)%dat(:) end do - diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = meanSoilCompress + ! keep soil compression as an average like the fluxes + diag_data%var(iLookDIAG%mLayerCompress)%dat(:) = meanSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sum( meanSoilCompress(1:nSoil)*iden_water & + * prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers) ) + deallocate(meanSoilCompress) ! *********************************************************************************************************************************** ! --- @@ -1174,7 +1179,7 @@ subroutine coupled_em(& averageSoilInflux => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarInfiltration) )%dat(1) ,& ! influx of water at the top of the soil profile (m s-1) averageSoilDrainage => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSoilDrainage) )%dat(1) ,& ! drainage from the bottom of the soil profile (m s-1) averageSoilBaseflow => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSoilBaseflow) )%dat(1) ,& ! total baseflow from throughout the soil profile (m s-1) - averageSoilCompress => flux_mean%var(childFLUX_MEAN(iLookDIAG%scalarSoilCompress) )%dat(1) ,& ! soil compression (kg m-2 s-1) + averageSoilCompress => diag_data%var( iLookDIAG%scalarSoilCompress) %dat(1) ,& ! soil compression (kg m-2 s-1) averageGroundEvaporation => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarGroundEvaporation) )%dat(1) ,& ! soil evaporation (kg m-2 s-1) averageCanopyTranspiration => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopyTranspiration))%dat(1) ,& ! canopy transpiration (kg m-2 s-1) ! state variables in the vegetation canopy diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index f370b60bd..9fd4bbafe 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -583,7 +583,7 @@ subroutine eval8summa(& ! compute soil compressibility (-) and its derivative w.r.t. matric head (m) ! NOTE: we already extracted trial matrix head and volumetric liquid water as part of the flux calculations - ! use non-sundials version because sundials version needs mLayerMatricHeadPrime + ! use non-prime version call soilCmpres(& ! input: dt_cur, & ! intent(in): length of the time step (seconds) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 3aaec077e..46091a7b9 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -927,7 +927,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! * check mass balance... ! ----------------------- - ! NOTE: should not need to do this, since mass balance is checked in the solver, and cannot do for Sundials + ! NOTE: should not need to do this, since mass balance is checked in the solver, and cannot do for IDA ! if do not check could cause problems if should modify nrgFlux if(checkMassBalance)then @@ -991,7 +991,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe endif ! if there is a water balance error endif ! if veg canopy - ! check mass balance for soil, again not checked for sundials + ! check mass balance for soil, again not checked for IDA ! NOTE: fatal errors, though possible to recover using negative error codes if(count(ixSoilOnlyHyd/=integerMissing)==nSoil)then soilBalance1 = sum( (mLayerVolFracLiqTrial(nSnow+1:nLayers) + mLayerVolFracIceTrial(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) From 71f4fc04fedf0a81a09c0ad41d1935731dae5389 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 24 Jun 2023 19:07:51 +0900 Subject: [PATCH 0768/1472] needs to be deallocated if fail --- build/source/engine/coupled_em.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index cb70c48ec..ad841699f 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1010,6 +1010,7 @@ subroutine coupled_em(& err=20; return endif ! try again, restart step (at end inner step) + deallocate(innerSoilCompress) cycle substeps endif From 4320f24026054a09fc717d05ddbb5657200e9969 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 24 Jun 2023 20:18:51 +0900 Subject: [PATCH 0769/1472] just moving stuff around and adding comments --- build/source/engine/coupled_em.f90 | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index ad841699f..34376e811 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -352,12 +352,11 @@ subroutine coupled_em(& do iVar=1,size(averageFlux_meta) flux_mean%var(iVar)%dat(:) = 0._rkind end do - allocate(meanSoilCompress(nSoil)); meanSoilCompress = 0._rkind ! mean total soil compression meanCanopySublimation = 0._rkind ! mean canopy sublimation meanLatHeatCanopyEvap = 0._rkind ! mean latent heat flux for evaporation from the canopy - meanSenHeatCanopy = 0._rkind ! mean sensible heat flux from the canopy - effRainfall = 0._rkind ! mean total effective rainfall over snow - + meanSenHeatCanopy = 0._rkind ! mean sensible heat flux from the canopy + effRainfall = 0._rkind ! mean total effective rainfall over snow + allocate(meanSoilCompress(nSoil)); meanSoilCompress = 0._rkind ! mean total soil compression ! associate local variables with information in the data structures associate(& @@ -585,9 +584,9 @@ subroutine coupled_em(& ! NOTE 1: this needs to be done before solving the energy and liquid water equations, to account for the heat advected with precipitation ! NOTE 2: this initialization needs to be done AFTER the call to canopySnow, since canopySnow uses canopy drip drom the previous time step if(.not.computeVegFlux)then - flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) = flux_data%var(iLookFLUX%scalarRainfall)%dat(1) + flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) = flux_data%var(iLookFLUX%scalarRainfall)%dat(1) else - flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) = 0._rkind + flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) = 0._rkind end if flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) = 0._rkind @@ -874,7 +873,6 @@ subroutine coupled_em(& stepFailure, & ! intent(out): flag to denote that the coupled step failed ixSolution, & ! intent(out): solution method used in this iteration err,cmessage) ! intent(out): error code and error message - ! check for all errors (error recovery within opSplittin) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if @@ -1064,8 +1062,6 @@ subroutine coupled_em(& do iVar=1,size(averageFlux_meta) flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_inner%var(iVar)%dat(:)*dt_wght end do - meanSoilCompress(:) = meanSoilCompress(:) + innerSoilCompress(:)*dt_wght ! not in flux structure so handle differently - deallocate(innerSoilCompress) meanCanopySublimation = meanCanopySublimation + sumCanopySublimation/data_step meanLatHeatCanopyEvap = meanLatHeatCanopyEvap + sumLatHeatCanopyEvap/data_step meanSenHeatCanopy = meanSenHeatCanopy + sumSenHeatCanopy/data_step @@ -1073,6 +1069,10 @@ subroutine coupled_em(& flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySublimation))%dat(1) = meanCanopySublimation flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarLatHeatCanopyEvap))%dat(1) = meanLatHeatCanopyEvap flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSenHeatCanopy))%dat(1) = meanSenHeatCanopy + ! Soil compression is not in flux structure so handle differently + ! This will be a problem if nSoil changes (currently not possible) + meanSoilCompress(:) = meanSoilCompress(:) + innerSoilCompress(:)*dt_wght + deallocate(innerSoilCompress) endif ! save the time step to initialize the subsequent step From 1b789e1941b1b6b3328aa785e96fec2d90abed3f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 24 Jun 2023 20:40:59 +0900 Subject: [PATCH 0770/1472] just allocate soil compression once-- will need to not save soil comp as a flux-like variable (which are all averaged over data window) if nSoil can change. --- build/source/engine/coupled_em.f90 | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 34376e811..d976f06c8 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -356,7 +356,12 @@ subroutine coupled_em(& meanLatHeatCanopyEvap = 0._rkind ! mean latent heat flux for evaporation from the canopy meanSenHeatCanopy = 0._rkind ! mean sensible heat flux from the canopy effRainfall = 0._rkind ! mean total effective rainfall over snow - allocate(meanSoilCompress(nSoil)); meanSoilCompress = 0._rkind ! mean total soil compression + + ! Need mean soil compression for balance checks but it is not in flux structure so handle differently + ! This will be a problem if nSoil changes (currently not possible)-- then might need to not keep the average + allocate(meanSoilCompress(nSoil)) + allocate(innerSoilCompress(nSoil)) + meanSoilCompress = 0._rkind ! mean total soil compression ! associate local variables with information in the data structures associate(& @@ -833,8 +838,8 @@ subroutine coupled_em(& do iVar=1,size(averageFlux_meta) flux_inner%var(iVar)%dat(:) = 0._rkind end do - allocate(innerSoilCompress(nSoil)); innerSoilCompress = 0._rkind ! mean total soil compression innerEffRainfall = 0._rkind ! mean total effective rainfall over snow + innerSoilCompress = 0._rkind ! mean total soil compression endif ! (do_outer loop) @@ -899,7 +904,6 @@ subroutine coupled_em(& endif ! try again, restart step deallocate(mLayerVolFracIceInit) - deallocate(innerSoilCompress) cycle substeps endif @@ -1008,7 +1012,6 @@ subroutine coupled_em(& err=20; return endif ! try again, restart step (at end inner step) - deallocate(innerSoilCompress) cycle substeps endif @@ -1040,7 +1043,7 @@ subroutine coupled_em(& ! *** END MAIN SOLVER ******************************************************************************** ! **************************************************************************************************** - ! increment mean fluxes, soil compression, and canopy sublimation, reset on whole_step + ! increment mean fluxes, soil compression, and effective rainfall, reset on whole_step dt_wght = dt_sub/whole_step ! define weight applied to each sub-step do iVar=1,size(averageFlux_meta) flux_inner%var(iVar)%dat(:) = flux_inner%var(iVar)%dat(:) + flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:)*dt_wght @@ -1052,7 +1055,7 @@ subroutine coupled_em(& dt_solvInner = dt_solvInner + dt_sub dt_solv = dt_solv + dt_sub - ! update first and last inner steps if did successful lastInnerStep, increment fluxes over data_step + ! update first and last inner steps if did successful lastInnerStep, increment fluxes and flux variables over data_step if (lastInnerStep)then firstInnerStep = .true. lastInnerStep = .false. @@ -1065,14 +1068,11 @@ subroutine coupled_em(& meanCanopySublimation = meanCanopySublimation + sumCanopySublimation/data_step meanLatHeatCanopyEvap = meanLatHeatCanopyEvap + sumLatHeatCanopyEvap/data_step meanSenHeatCanopy = meanSenHeatCanopy + sumSenHeatCanopy/data_step + meanSoilCompress(:) = meanSoilCompress(:) + innerSoilCompress(:)*dt_wght effRainfall = effRainfall + innerEffRainfall*dt_wght flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySublimation))%dat(1) = meanCanopySublimation flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarLatHeatCanopyEvap))%dat(1) = meanLatHeatCanopyEvap flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSenHeatCanopy))%dat(1) = meanSenHeatCanopy - ! Soil compression is not in flux structure so handle differently - ! This will be a problem if nSoil changes (currently not possible) - meanSoilCompress(:) = meanSoilCompress(:) + innerSoilCompress(:)*dt_wght - deallocate(innerSoilCompress) endif ! save the time step to initialize the subsequent step @@ -1145,10 +1145,11 @@ subroutine coupled_em(& do iVar=1,size(averageFlux_meta) flux_data%var(averageFlux_meta(iVar)%ixParent)%dat(:) = flux_mean%var(iVar)%dat(:) end do - ! keep soil compression as an average like the fluxes + ! keep soil compression as an average like the fluxes, will not want to do this if nSoil can change diag_data%var(iLookDIAG%mLayerCompress)%dat(:) = meanSoilCompress diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sum( meanSoilCompress(1:nSoil)*iden_water & * prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers) ) + deallocate(innerSoilCompress) deallocate(meanSoilCompress) ! *********************************************************************************************************************************** From 1a7252e607346ee75692cd92772c54bb8b5c0aa7 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 26 Jun 2023 14:30:07 +0900 Subject: [PATCH 0771/1472] CMakeLists had errrors --- build/cmake/CMakeLists.txt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 9b90d4910..2adfc4c42 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -525,15 +525,15 @@ file(APPEND ${VERSIONFILE} "character(len=64), parameter :: buildTime = ''\n file(APPEND ${VERSIONFILE} "character(len=64), parameter :: gitBranch = '${GITBRCH}'\n") file(APPEND ${VERSIONFILE} "character(len=64), parameter :: gitHash = '${GITHASH}'") -# Build NOAH_MP Object +# Build SUMMA_NOAHMP Object add_library(SUMMA_NOAHMP OBJECT ${NOAHMP} ${NRUTIL}) target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) # Build SUMMA_COMM Object add_library(SUMMA_COMM OBJECT ${COMM_ALL}) target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) -target_link_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES}) +target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP) # For NexGen, build SUMMA Shared Library and add the outside BMI libraries if(CMAKE_BUILD_TYPE MATCHES NexGen) @@ -544,12 +544,12 @@ if(CMAKE_BUILD_TYPE MATCHES NexGen) endif() target_compile_options(summabmi PRIVATE ${FLAGS_ALL}) - if(CMAKE_BUILD_TYPE MATCHES Kinsol OR CMAKE_BUILD_TYPE MATCHES IDA) + if(CMAKE_BUILD_TYPE MATCHES Sundials) target_include_directories(summabmi PUBLIC ${INCLUDES} ${INC_SUNDIALS}) - target_link_libraries(summabmi PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_COMM) + target_link_libraries(summabmi PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_NOAHMP SUMMA_COMM) else() target_include_directories(summabmi PUBLIC ${INCLUDES}) - target_link_libraries(summabmi PUBLIC ${LIBRARIES} SUMMA_COMM) + target_link_libraries(summabmi PUBLIC ${LIBRARIES} SUMMA_NOAHMP SUMMA_COMM) endif() target_link_libraries(summabmi PUBLIC iso_c_bmi) set_target_properties(summabmi PROPERTIES VERSION ${PROJECT_VERSION}) @@ -568,10 +568,10 @@ elseif(CMAKE_BUILD_TYPE MATCHES Actors) if(CMAKE_BUILD_TYPE MATCHES Sundials) target_include_directories(summaactors PUBLIC ${INCLUDES} ${INC_SUNDIALS}) - target_link_libraries(summaactors PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_COMM) + target_link_libraries(summaactors PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_NOAHMP SUMMA_COMM) else() target_include_directories(summaactors PUBLIC ${INCLUDES}) - target_link_libraries(summaactors PUBLIC ${LIBRARIES} SUMMA_COMM) + target_link_libraries(summaactors PUBLIC ${LIBRARIES} SUMMA_NOAHMP SUMMA_COMM) endif() add_executable(${EXEC_NAME} ${MAIN_ACTOR} @@ -595,10 +595,10 @@ else() if(CMAKE_BUILD_TYPE MATCHES Sundials) target_include_directories(summa PUBLIC ${INCLUDES} ${INC_SUNDIALS}) - target_link_libraries(summa PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_COMM) + target_link_libraries(summa PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_NOAHMP SUMMA_COMM) else() target_include_directories(summa PUBLIC ${INCLUDES}) - target_link_libraries(summa PUBLIC ${LIBRARIES} SUMMA_COMM) + target_link_libraries(summa PUBLIC ${LIBRARIES} SUMMA_NOAHMP SUMMA_COMM) endif() add_executable(${EXEC_NAME} ${MAIN_SUMMA}) From 5738852ef90f9338afcdc5987fd338a180dfa35c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 26 Jun 2023 18:12:52 +0900 Subject: [PATCH 0772/1472] adding in error codes as messages in IDA and KINSOL --- build/source/engine/summaSolve4ida.f90 | 362 ++++++++++++---------- build/source/engine/summaSolve4kinsol.f90 | 247 ++++++++------- build/source/engine/summaSolve4numrec.f90 | 2 +- 3 files changed, 344 insertions(+), 267 deletions(-) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 810429f64..bb5cbb5da 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -72,6 +72,7 @@ module summaSolve4ida_module private::setSolverParams private::find_rootdir public::layerDisCont4ida + private::getErrMessage public::summaSolve4ida contains @@ -433,6 +434,8 @@ subroutine summaSolve4ida( & retvalr = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) if( retvalr < 0 )then idaSucceeds = .false. + call getErrMessage(retvalr,cmessage) + message=trim(message)//trim(cmessage) exit end if @@ -518,7 +521,10 @@ subroutine summaSolve4ida( & err = eqns_data%err message = eqns_data%message - if( .not. feasible) idaSucceeds = .false. + if( .not. feasible)then + idaSucceeds = .false. + message=trim(message)//trim(cmessage)//'non-feasible' + endif if(idaSucceeds)then ! copy to output data @@ -566,7 +572,6 @@ subroutine summaSolve4ida( & end subroutine summaSolve4ida - ! ---------------------------------------------------------------- ! SetInitialCondition: routine to initialize u and up vectors. ! ---------------------------------------------------------------- @@ -661,85 +666,83 @@ subroutine setSolverParams(dt,nonlin_iter,ida_mem,retval) end subroutine setSolverParams - ! ---------------------------------------------------------------------------------------- ! find_rootdir: private routine to determine which direction to look for the root, by ! determining if the variable is greater or less than the root. Need to do this to prevent ! bouncing around solution ! ---------------------------------------------------------------------------------------- - subroutine find_rootdir(eqns_data,rootdir) - - !======= Inclusions =========== - use, intrinsic :: iso_c_binding - use fsundials_nvector_mod - use fnvector_serial_mod - use soil_utils_module,only:crit_soilT ! compute the critical temperature below which ice exists - use globalData,only:integerMissing ! missing integer - use var_lookup,only:iLookINDEX ! named variables for structure elements - use multiconst,only:Tfreeze ! freezing point of pure water (K) - - !======= Declarations ========= - implicit none +subroutine find_rootdir(eqns_data,rootdir) + + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use fsundials_nvector_mod + use fnvector_serial_mod + use soil_utils_module,only:crit_soilT ! compute the critical temperature below which ice exists + use globalData,only:integerMissing ! missing integer + use var_lookup,only:iLookINDEX ! named variables for structure elements + use multiconst,only:Tfreeze ! freezing point of pure water (K) + + !======= Declarations ========= + implicit none - ! calling variables - type(data4ida),intent(in) :: eqns_data ! equations data - integer(i4b),intent(inout) :: rootdir(:) ! root function directions to search - - ! local variables - integer(i4b) :: i,ind ! indices - integer(i4b) :: nState ! number of states - integer(i4b) :: nSnow ! number of snow layers - integer(i4b) :: nSoil ! number of soil layers - real(rkind) :: xPsi ! matric head at layer (m) - real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) - - ! get equations data variables - nState = eqns_data%nState - nSnow = eqns_data%nSnow - nSoil = eqns_data%nSoil - - ! initialize - ind = 0 - - ! identify the critical point when vegetation begins to freeze - if(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then - ind = ind+1 - rootdir(ind) = 1 - if(eqns_data%scalarCanopyTempPrev > Tfreeze) rootdir(ind) = -1 - endif - - if(nSnow>0)then - do i = 1,nSnow - ! identify the critical point when the snow layer begins to freeze - if(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing)then - ind = ind+1 - rootdir(ind) = 1 - if(eqns_data%mLayerTempPrev(i) > Tfreeze) rootdir(ind) = -1 - endif - end do - endif - - if(nSoil>0)then - do i = 1,nSoil - xPsi = eqns_data%mLayerMatricHeadPrev(i) - ! identify the critical point when soil matrix potential goes below 0 and Tfreeze depends only on temp - if (eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing)then - ind = ind+1 - rootdir(ind) = 1 - if(xPsi > 0._rkind ) rootdir(ind) = -1 - endif - ! identify the critical point when the soil layer begins to freeze - if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing)then - ind = ind+1 - TcSoil = crit_soilT(xPsi) - rootdir(ind) = 1 - if(eqns_data%mLayerTempPrev(i+nSnow) > TcSoil) rootdir(ind) = -1 - endif - end do - endif - - end subroutine find_rootdir + ! calling variables + type(data4ida),intent(in) :: eqns_data ! equations data + integer(i4b),intent(inout) :: rootdir(:) ! root function directions to search + ! local variables + integer(i4b) :: i,ind ! indices + integer(i4b) :: nState ! number of states + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers + real(rkind) :: xPsi ! matric head at layer (m) + real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) + + ! get equations data variables + nState = eqns_data%nState + nSnow = eqns_data%nSnow + nSoil = eqns_data%nSoil + + ! initialize + ind = 0 + + ! identify the critical point when vegetation begins to freeze + if(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then + ind = ind+1 + rootdir(ind) = 1 + if(eqns_data%scalarCanopyTempPrev > Tfreeze) rootdir(ind) = -1 + endif + + if(nSnow>0)then + do i = 1,nSnow + ! identify the critical point when the snow layer begins to freeze + if(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing)then + ind = ind+1 + rootdir(ind) = 1 + if(eqns_data%mLayerTempPrev(i) > Tfreeze) rootdir(ind) = -1 + endif + end do + endif + + if(nSoil>0)then + do i = 1,nSoil + xPsi = eqns_data%mLayerMatricHeadPrev(i) + ! identify the critical point when soil matrix potential goes below 0 and Tfreeze depends only on temp + if (eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing)then + ind = ind+1 + rootdir(ind) = 1 + if(xPsi > 0._rkind ) rootdir(ind) = -1 + endif + ! identify the critical point when the soil layer begins to freeze + if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing)then + ind = ind+1 + TcSoil = crit_soilT(xPsi) + rootdir(ind) = 1 + if(eqns_data%mLayerTempPrev(i+nSnow) > TcSoil) rootdir(ind) = -1 + endif + end do + endif + +end subroutine find_rootdir ! ---------------------------------------------------------------------------------------- ! layerDisCont4ida: The root function routine to find soil matrix potential = 0, @@ -750,92 +753,133 @@ end subroutine find_rootdir ! 1 = recoverable error, ! -1 = non-recoverable error ! ---------------------------------------------------------------------------------------- - integer(c_int) function layerDisCont4ida(t, sunvec_u, sunvec_up, gout, user_data) & +integer(c_int) function layerDisCont4ida(t, sunvec_u, sunvec_up, gout, user_data) & result(ierr) bind(C,name='layerDisCont4ida') - !======= Inclusions =========== - use, intrinsic :: iso_c_binding - use fsundials_nvector_mod - use fnvector_serial_mod - use soil_utils_module,only:crit_soilT ! compute the critical temperature below which ice exists - use globalData,only:integerMissing ! missing integer - use var_lookup,only:iLookINDEX ! named variables for structure elements - use multiconst,only:Tfreeze ! freezing point of pure water (K) + !======= Inclusions =========== + use, intrinsic :: iso_c_binding + use fsundials_nvector_mod + use fnvector_serial_mod + use soil_utils_module,only:crit_soilT ! compute the critical temperature below which ice exists + use globalData,only:integerMissing ! missing integer + use var_lookup,only:iLookINDEX ! named variables for structure elements + use multiconst,only:Tfreeze ! freezing point of pure water (K) - !======= Declarations ========= - implicit none + !======= Declarations ========= + implicit none + + ! calling variables + real(c_double), value :: t ! current time + type(N_Vector) :: sunvec_u ! solution N_Vector + type(N_Vector) :: sunvec_up ! derivative N_Vector + real(c_double) :: gout(999) ! root function values, if (nVeg + nSnow + 2*nSoil)>999, problem + type(c_ptr), value :: user_data ! user-defined data + + ! local variables + integer(i4b) :: i,ind ! indices + integer(i4b) :: nState ! number of states + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers + real(rkind) :: xPsi ! matric head at layer (m) + real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) + + ! pointers to data in SUNDIALS vectors + real(c_double), pointer :: uu(:) + type(data4ida), pointer :: eqns_data ! equations data + + !======= Internals ============ + ! get equations data from user-defined data + call c_f_pointer(user_data, eqns_data) + nState = eqns_data%nState + nSnow = eqns_data%nSnow + nSoil = eqns_data%nSoil + + ! get data array from SUNDIALS vector + uu(1:nState) => FN_VGetArrayPointer(sunvec_u) + + ! initialize + ind = 0 + + ! identify the critical point when vegetation begins to freeze + if(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then + ind = ind+1 + gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)) - Tfreeze + endif - ! calling variables - real(c_double), value :: t ! current time - type(N_Vector) :: sunvec_u ! solution N_Vector - type(N_Vector) :: sunvec_up ! derivative N_Vector - real(c_double) :: gout(999) ! root function values, if (nVeg + nSnow + 2*nSoil)>999, problem - type(c_ptr), value :: user_data ! user-defined data - - ! local variables - integer(i4b) :: i,ind ! indices - integer(i4b) :: nState ! number of states - integer(i4b) :: nSnow ! number of snow layers - integer(i4b) :: nSoil ! number of soil layers - real(rkind) :: xPsi ! matric head at layer (m) - real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) - - ! pointers to data in SUNDIALS vectors - real(c_double), pointer :: uu(:) - type(data4ida), pointer :: eqns_data ! equations data - - !======= Internals ============ - ! get equations data from user-defined data - call c_f_pointer(user_data, eqns_data) - nState = eqns_data%nState - nSnow = eqns_data%nSnow - nSoil = eqns_data%nSoil - - ! get data array from SUNDIALS vector - uu(1:nState) => FN_VGetArrayPointer(sunvec_u) - - ! initialize - ind = 0 - - ! identify the critical point when vegetation begins to freeze - if(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then - ind = ind+1 - gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)) - Tfreeze - endif - - if(nSnow>0)then - do i = 1,nSnow - ! identify the critical point when the snow layer begins to freeze - if(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing)then - ind = ind+1 - gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)) - Tfreeze - endif - end do - endif - - if(nSoil>0)then - do i = 1,nSoil - ! identify the critical point when soil matrix potential goes below 0 and Tfreeze depends only on temp - if (eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing)then - ind = ind+1 - xPsi = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)) - gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)) - else - xPsi = eqns_data%prog_data%var(iLookPROG%mLayerMatricHead)%dat(i) - endif - ! identify the critical point when the soil layer begins to freeze - if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing)then - ind = ind+1 - TcSoil = crit_soilT(xPsi) - gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) - TcSoil - endif - end do - endif - - ! return success - ierr = 0 - return - - end function layerDisCont4ida - -end module summaSolve4ida_module + if(nSnow>0)then + do i = 1,nSnow + ! identify the critical point when the snow layer begins to freeze + if(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing)then + ind = ind+1 + gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)) - Tfreeze + endif + end do + endif + + if(nSoil>0)then + do i = 1,nSoil + ! identify the critical point when soil matrix potential goes below 0 and Tfreeze depends only on temp + if (eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing)then + ind = ind+1 + xPsi = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)) + gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)) + else + xPsi = eqns_data%prog_data%var(iLookPROG%mLayerMatricHead)%dat(i) + endif + ! identify the critical point when the soil layer begins to freeze + if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing)then + ind = ind+1 + TcSoil = crit_soilT(xPsi) + gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) - TcSoil + endif + end do + endif + + ! return success + ierr = 0 + return + +end function layerDisCont4ida + +! ---------------------------------------------------------------- +! getErrMessage: private routine to get error message for IDA solver +! ---------------------------------------------------------------- +subroutine getErrMessage(retval,message) + + !======= Declarations ========= + implicit none + + ! calling variables + integer(i4b),intent(in) :: retval ! return value from IDA + character(*),intent(out) :: message ! error message + + ! get message + if( retval==-1 ) message = 'IDA_TOO_MUCH_WORK' ! The solver took mxstep internal steps but could not reach tout. + if( retval==-2 ) message = 'IDA_TOO_MUCH_ACC' ! The solver could not satisfy the accuracy demanded by the user for some internal step. + if( retval==-3 ) message = 'IDA_ERR_FAIL' ! Error test failures occurred too many times during one internal timestep or minimum step size was reached. + if( retval==-4 ) message = 'IDA_CONV_FAIL' ! Convergence test failures occurred too many times during one internal time step or minimum step size was reached. + if( retval==-5 ) message = 'IDA_LINIT_FAIL' ! The linear solver’s initialization function failed. + if( retval==-6 ) message = 'IDA_LSETUP_FAIL' ! The linear solver’s setup function failed in an unrecoverable manner. + if( retval==-7 ) message = 'IDA_LSOLVE_FAIL' ! The linear solver’s solve function failed in an unrecoverable manner. + if( retval==-8 ) message = 'IDA_RES_FAIL' ! The user-provided residual function failed in an unrecoverable manner. + if( retval==-9 ) message = 'IDA_REP_RES_FAIL' ! The user-provided residual function repeatedly returned a recoverable error flag, but the solver was unable to recover. + if( retval==-10) message = 'IDA_RTFUNC_FAIL' ! The rootfinding function failed in an unrecoverable manner. + if( retval==-11) message = 'IDA_CONSTR_FAIL' ! The inequality constraints were violated and the solver was unable to recover. + if( retval==-12) message = 'IDA_FIRST_RES_FAIL' ! The user-provided residual function failed recoverably on the first call. + if( retval==-13) message = 'IDA_LINESEARCH_FAIL' ! The line search failed. + if( retval==-14) message = 'IDA_NO_RECOVERY' ! The residual function, linear solver setup function, or linear solver solve function had a recoverable failure, but IDACalcIC could not recover. + if( retval==-15) message = 'IDA_NLS_INIT_FAIL' ! The nonlinear solver’s init routine failed. + if( retval==-16) message = 'IDA_NLS_SETUP_FAIL' ! The nonlinear solver’s setup routine failed. + if( retval==-20) message = 'IDA_MEM_NULL' ! The ida_mem argument was NULL. + if( retval==-21) message = 'IDA_MEM_FAIL' ! A memory allocation failed. + if( retval==-22) message = 'IDA_ILL_INPUT' ! One of the function inputs is illegal. + if( retval==-23) message = 'IDA_NO_MALLOC' ! The IDA memory was not allocated by a call to IDAInit. + if( retval==-24) message = 'IDA_BAD_EWT' ! Zero value of some error weight component. + if( retval==-25) message = 'IDA_BAD_K' ! The k-th derivative is not available. + if( retval==-26) message = 'IDA_BAD_T' ! The time t is outside the last step taken. + if( retval==-27) message = 'IDA_BAD_DKY' ! The vector argument where derivative should be stored is NULL. + +end subroutine getErrMessage + + +end module summaSolve4ida_module \ No newline at end of file diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index ff3e1a71a..d4e8f9d1d 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -69,6 +69,7 @@ module summaSolve4kinsol_module implicit none private::setInitialCondition private::setSolverParams + private::getErrMessage public::summaSolve4kinsol contains @@ -323,34 +324,35 @@ subroutine summaSolve4kinsol(& !****************************** Main Solver ********************************************** ! Call KINSol to solve problem with choice of solver, linesearch or Picard - !retval = FKINSol(kinsol_mem, sunvec_y, KIN_LINESEARCH, sunvec_xscale, sunvec_fscale) - retval = FKINSol(kinsol_mem, sunvec_y, KIN_PICARD, sunvec_xscale, sunvec_fscale) - if( retval < 0 ) kinsolSucceeds = .false. - - ! check the feasibility of the solution - feasible=.true. - call checkFeas(& - ! input - stateVec, & ! intent(in): model state vector (mixed units) - eqns_data%mpar_data, & ! intent(in): model parameters - eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU - eqns_data%indx_data, & ! intent(in): indices defining model states and layers - ! output: feasibility - feasible, & ! intent(inout): flag to denote the feasibility of the solution - ! output: error control - err,cmessage) ! intent(out): error control - - ! early return for non-feasible solutions, will fail in current Sundials formulation - if(.not.feasible)then - eqns_data%fluxVec(:) = realMissing - message=trim(message)//trim(cmessage)//'non-feasible' - return - end if + retval = FKINSol(kinsol_mem, sunvec_y, KIN_LINESEARCH, sunvec_xscale, sunvec_fscale) + !retval = FKINSol(kinsol_mem, sunvec_y, KIN_PICARD, sunvec_xscale, sunvec_fscale) + + if( retvalr < 0 )then + kinsolSucceeds = .false. + call getErrMessage(retval,cmessage) + message=trim(message)//trim(cmessage) + else + ! check the feasibility of the solution + feasible=.true. + call checkFeas(& + ! input + stateVec, & ! intent(in): model state vector (mixed units) + eqns_data%mpar_data, & ! intent(in): model parameters + eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU + eqns_data%indx_data, & ! intent(in): indices defining model states and layers + ! output: feasibility + feasible, & ! intent(inout): flag to denote the feasibility of the solution + ! output: error control + err,cmessage) ! intent(out): error control + endif !****************************** End of Main Solver *************************************** err = eqns_data%err message = eqns_data%message - if( .not. feasible) kinsolSucceeds = .false. + if( .not. feasible) then + kinsolSucceeds = .false. + message=trim(message)//trim(cmessage)//'non-feasible' + endif if(kinsolSucceeds)then ! copy to output data @@ -383,91 +385,122 @@ subroutine summaSolve4kinsol(& end subroutine summaSolve4kinsol - ! ---------------------------------------------------------------- ! SetInitialCondition: routine to initialize u vector. ! ---------------------------------------------------------------- - subroutine setInitialCondition(neq, y, sunvec_u) +subroutine setInitialCondition(neq, y, sunvec_u) + + !======= Inclusions =========== + USE, intrinsic :: iso_c_binding + USE fsundials_nvector_mod + USE fnvector_serial_mod + + !======= Declarations ========= + implicit none + + ! calling variables + type(N_Vector) :: sunvec_u ! solution N_Vector + integer(c_long) :: neq + real(rkind) :: y(neq) + + ! pointers to data in SUNDIALS vectors + real(c_double), pointer :: uu(:) + + ! get data arrays from SUNDIALS vectors + uu(1:neq) => FN_VGetArrayPointer(sunvec_u) + + uu = y + +end subroutine setInitialCondition + +! ---------------------------------------------------------------- +! setSolverParams: private routine to set parameters in KINSOL solver +! ---------------------------------------------------------------- +subroutine setSolverParams(nonlin_iter,kinsol_mem,retval) + + !======= Inclusions =========== + USE, intrinsic :: iso_c_binding + USE fkinsol_mod ! Fortran interface to KINSOL + + !======= Declarations ========= + implicit none + + ! calling variables + integer,intent(in) :: nonlin_iter ! maximum number of nonlinear iterations, default = 200, set in parameters + type(c_ptr),intent(inout) :: kinsol_mem ! KINSOL memory + integer(i4b),intent(out) :: retval ! return value + + !======= Internals ============ + integer(c_long) :: nonlin_itr ! maximum number of nonlinear iterations in SUNDIALS type + integer(c_long),parameter :: mset = 1 ! maximum number of times the solver is called without Jacobian update, pass 0 to give default of 10 times + integer(c_long),parameter :: msubset = 1 ! maximum number of nonlinear iterations between checks by the residual monitoring algorithm, default=5 + integer(c_long),parameter :: maa = 0 ! maximum number of prior residuals to use acceleration, default = 0 + integer(c_long),parameter :: beta_fail = 50 ! maximum number of beta condition failures, default = 10 + real(qp),parameter :: fnormtol = 0.0 ! stopping tolerance on the scaled maximum norm of the system function, pass 0 to give default of unit_roundoff**(1/3) + real(qp),parameter :: scsteptol = 0.0 ! stopping tolerance on the minimum scaled step length, pass 0 to give default of unit_roundoff**(2/3) + + ! Set maximum number of times the linear solver is called without a Jacobian update + retval = FKINSetMaxSetupCalls(kinsol_mem, mset) + if (retval /= 0) return + + ! Every msubset iterations, test if a Jacobian evaluation is necessary + retval = FKINSetMaxSubSetupCalls(kinsol_mem, msubset) + if (retval /= 0) return + + ! Set maximum number of iterations + nonlin_itr = nonlin_iter ! maximum number of nonlinear iterations in SUNDIALS type + retval = FKINSetNumMaxIters(kinsol_mem, nonlin_itr) + if (retval /= 0) return + + ! Set maximum number of prior residuals to use for Anderson acceleration + ! ONLY in conjunction with Picard or fixed-point iteration + retval = FKINSetMAA(kinsol_mem, maa); + if (retval /= 0) return + + ! Set maximum number of beta condition failures in the linesearch + retval = FKINSetMaxBetaFails(kinsol_mem, beta_fail) + if (retval /= 0) return + + ! Set tolerances for stopping criteria: scaled maximum norm of the system function + retval = FKINSetFuncNormTol(kinsol_mem, fnormtol) + if (retval /= 0) return + + ! Set stopping tolerance on the scaled maximum norm of the system function + retval = FKINSetScaledStepTol(kinsol_mem, scsteptol) + if (retval /= 0) return + +end subroutine setSolverParams + +! ---------------------------------------------------------------- +! getErrMessage: private routine to get error message for KINSOL solver +! ---------------------------------------------------------------- +subroutine getErrMessage(retval,message) + + !======= Declarations ========= + implicit none + + ! calling variables + integer(i4b),intent(in) :: retval ! return value from KINSOL + character(*),intent(out) :: message ! error message + + ! get message + if( retval==-1 ) message = 'KIN_MEM_NULL' ! The kin_mem argument was NULL. + if( retval==-2 ) message = 'KIN_ILL_INPUT' ! One of the function inputs is illegal. + if( retval==-3 ) message = 'KIN_NO_MALLOC' ! The KINSOL memory was not allocated by a call to KINMalloc. + if( retval==-4 ) message = 'KIN_MEM_FAIL' ! A memory allocation failed. + if( retval==-5 ) message = 'KIN_LINESEARCH_NONCONV ' ! The linesearch algorithm was unable to find an iterate sufficiently distinct from the current iterate. + if( retval==-6 ) message = 'KIN_MAXITER_REACHED' ! The maximum number of nonlinear iterations has been reached. + if( retval==-7 ) message = 'KIN_MXNEWT_5X_EXCEEDED' ! Five consecutive steps have been taken that satisfy a scaled step length test. + if( retval==-8 ) message = 'KIN_LINESEARCH_BCFAIL' ! The linesearch algorithm was unable to satisfy the 𝛽-condition for nbcfails iterations. + if( retval==-9 ) message = 'KIN_LINSOLV_NO_RECOVERY' ! The user-supplied routine preconditioner slve function failed recoverably, but the preconditioner is already current. + if( retval==-10) message = 'KIN_LINIT_FAIL' ! The linear solver’s initialization function failed. + if( retval==-11) message = 'KIN_LSETUP_FAIL' ! The linear solver’s setup function failed in an unrecoverable manner. + if( retval==-12) message = 'KIN_LSOLVE_FAIL' ! The linear solver’s solve function failed in an unrecoverable manner. + if( retval==-13) message = 'KIN_SYSFUNC_FAIL' ! The system function failed in an unrecoverable manner. + if( retval==-14) message = 'KIN_FIRST_SYSFUNC_ERR' ! The system function failed with a recoverable error at the first call. + if( retval==-15) message = 'KIN_REPTD_SYSFUNC_ERR' ! The system function had repeated recoverable errors. + +end subroutine getErrMessage + - !======= Inclusions =========== - USE, intrinsic :: iso_c_binding - USE fsundials_nvector_mod - USE fnvector_serial_mod - - !======= Declarations ========= - implicit none - - ! calling variables - type(N_Vector) :: sunvec_u ! solution N_Vector - integer(c_long) :: neq - real(rkind) :: y(neq) - - ! pointers to data in SUNDIALS vectors - real(c_double), pointer :: uu(:) - - ! get data arrays from SUNDIALS vectors - uu(1:neq) => FN_VGetArrayPointer(sunvec_u) - - uu = y - - end subroutine setInitialCondition - - ! ---------------------------------------------------------------- - ! setSolverParams: private routine to set parameters in KINSOL solver - ! ---------------------------------------------------------------- - subroutine setSolverParams(nonlin_iter,kinsol_mem,retval) - - !======= Inclusions =========== - USE, intrinsic :: iso_c_binding - USE fkinsol_mod ! Fortran interface to KINSOL - - !======= Declarations ========= - implicit none - - ! calling variables - integer,intent(in) :: nonlin_iter ! maximum number of nonlinear iterations, default = 200, set in parameters - type(c_ptr),intent(inout) :: kinsol_mem ! KINSOL memory - integer(i4b),intent(out) :: retval ! return value - - !======= Internals ============ - integer(c_long) :: nonlin_itr ! maximum number of nonlinear iterations in SUNDIALS type - integer(c_long),parameter :: mset = 1 ! maximum number of times the solver is called without Jacobian update, pass 0 to give default of 10 times - integer(c_long),parameter :: msubset = 1 ! maximum number of nonlinear iterations between checks by the residual monitoring algorithm, default=5 - integer(c_long),parameter :: maa = 0 ! maximum number of prior residuals to use acceleration, default = 0 - integer(c_long),parameter :: beta_fail = 50 ! maximum number of beta condition failures, default = 10 - real(qp),parameter :: fnormtol = 0.0 ! stopping tolerance on the scaled maximum norm of the system function, pass 0 to give default of unit_roundoff**(1/3) - real(qp),parameter :: scsteptol = 0.0 ! stopping tolerance on the minimum scaled step length, pass 0 to give default of unit_roundoff**(2/3) - - ! Set maximum number of times the linear solver is called without a Jacobian update - retval = FKINSetMaxSetupCalls(kinsol_mem, mset) - if (retval /= 0) return - - ! Every msubset iterations, test if a Jacobian evaluation is necessary - retval = FKINSetMaxSubSetupCalls(kinsol_mem, msubset) - if (retval /= 0) return - - ! Set maximum number of iterations - nonlin_itr = nonlin_iter ! maximum number of nonlinear iterations in SUNDIALS type - retval = FKINSetNumMaxIters(kinsol_mem, nonlin_itr) - if (retval /= 0) return - - ! Set maximum number of prior residuals to use for Anderson acceleration - ! ONLY in conjunction with Picard or fixed-point iteration - retval = FKINSetMAA(kinsol_mem, maa); - if (retval /= 0) return - - ! Set maximum number of beta condition failures in the linesearch - retval = FKINSetMaxBetaFails(kinsol_mem, beta_fail) - if (retval /= 0) return - - ! Set tolerances for stopping criteria: scaled maximum norm of the system function - retval = FKINSetFuncNormTol(kinsol_mem, fnormtol) - if (retval /= 0) return - - ! Set stopping tolerance on the scaled maximum norm of the system function - retval = FKINSetScaledStepTol(kinsol_mem, scsteptol) - if (retval /= 0) return - - end subroutine setSolverParams - end module summaSolve4kinsol_module \ No newline at end of file diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index c101390d1..aca1509ba 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -141,7 +141,7 @@ subroutine summaSolve4numrec(& ! input: model control real(rkind),intent(in) :: dt_cur ! current stepsize real(rkind),intent(in) :: dt ! entire time step for drainage pond rate - integer(i4b),intent(in) :: iter ! interation index + integer(i4b),intent(in) :: iter ! iteration index integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers From df8347bfc5af7133d14f820f7c7241477ca2d72c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 26 Jun 2023 20:44:37 +0900 Subject: [PATCH 0773/1472] summaSolve4ida should be called with dt_cur, won't affect paper solution because it was the same as dt --- build/source/engine/systemSolv.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 7bb89a0dd..ba63cc72b 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -439,7 +439,7 @@ subroutine systemSolv(& #ifdef SUNDIALS_ACTIVE call eval8summaWithPrime(& ! input: model control - dt, & ! intent(in): length of the time step (seconds) + dt, & ! intent(in): entire time step for drainage pond rate nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): number of layers @@ -608,7 +608,7 @@ subroutine systemSolv(& !--------------------------- ! iterations and updates to trial state vector, fluxes, and derivatives are done inside IDA solver call summaSolve4ida(& - dt_cur, & ! intent(in): data time step + dt, & ! intent(in): entire time step for drainage pond rate atol, & ! intent(in): absolute tolerance rtol, & ! intent(in): relative tolerance nSnow, & ! intent(in): number of snow layers @@ -684,7 +684,7 @@ subroutine systemSolv(& ! iterations and updates to trial state vector, fluxes, and derivatives are done inside IDA solver call summaSolve4kinsol(& dt_cur, & ! intent(in): data time step - dt, & ! intent(in): data time step + dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate fScale, & ! intent(in): characteristic scale of the function evaluations xScale, & ! intent(in): characteristic scale of the state vector nSnow, & ! intent(in): number of snow layers From 1a389d26718e87b2fd60dd242c2e9adc155f4ea8 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 26 Jun 2023 21:35:23 +0900 Subject: [PATCH 0774/1472] we can start the run by running eval8summa even if call ida because prime vectors are 0 to start --- build/source/engine/systemSolv.f90 | 182 ++++++++--------------------- 1 file changed, 49 insertions(+), 133 deletions(-) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index ba63cc72b..290775c9d 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -235,7 +235,6 @@ subroutine systemSolv(& real(rkind) :: rtol(nState) ! relative tolerance ida type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt_out (=dt) real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix - real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! derivative value for total water matric potential (m s-1) ! kinsol and numrec variables real(rkind) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) real(rkind) :: xScale(nState) ! characteristic scale of the state vector (mixed units) @@ -274,26 +273,19 @@ subroutine systemSolv(& mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) - ! soil parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) ! model state variables scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp] mass of liquid water on the vegetation canopy (kg m-2) scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) ! model state variables (snow and soil domains) mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) - ! model state variables (aquifer) - scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in): [dp] storage of water in the aquifer (m) ! check the need to merge snow layers - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) ! mapping from full domain to the sub-domain ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b] mapping of full state vector to the state subset ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b] index of control volume for different domains (veg, snow, soil) @@ -338,13 +330,11 @@ subroutine systemSolv(& call allocLocal(flux_meta(:),flux_init,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - ! allocate space for mLayerCmpress_sum and mLayerMatricHeadPrime at the start of the time step + ! allocate space for mLayerCmpress_sum at the start of the time step if(ixNumericalMethod==ida)then allocate( mLayerCmpress_sum(nSoil) ) - allocate( mLayerMatricHeadPrime(nSoil) ) else - allocate( mLayerCmpress_sum(0) ) - allocate( mLayerMatricHeadPrime(0) ) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix + allocate( mLayerCmpress_sum(0) ) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix end if ! allocate space for the baseflow derivatives @@ -433,124 +423,51 @@ subroutine systemSolv(& if(err/=0)then; message=trim(message)//trim(cmessage); return; endif endif - ! compute the initial flux and the residual vector, also gets values needed for the Jacobian matrix - select case(ixNumericalMethod) - case(ida) -#ifdef SUNDIALS_ACTIVE - call eval8summaWithPrime(& - ! input: model control - dt, & ! intent(in): entire time step for drainage pond rate - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): number of layers - nState, & ! intent(in): number of state variables in the current subset - .false., & ! intent(in): we are outside Sundials solver loop - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVecTrial, & ! intent(in): model state vector - fScale, & ! intent(in): characteristic scale of the function evaluations - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: here we need to pass some extra variables that do not get updated in in the Sundials loops - scalarCanopyTemp, & ! intent(out): trial value of canopy temperature (K) - scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) - scalarCanopyIce, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyIce, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq, & ! intent(out): trial value of canopy liquid water (kg m-2) - scalarCanopyLiq, & ! intent(in): value of canopy liquid water (kg m-2) - scalarCanopyEnthalpy, & ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) - scalarCanopyEnthalpy, & ! intent(in): value for enthalpy of the vegetation canopy (J m-3) - mLayerTemp, & ! intent(out): trial vector of layer temperature (K) - mLayerTemp, & ! intent(in): vector of layer temperature (K) - mLayerMatricHeadLiq, & ! intent(out): trial value for liquid water matric potential (m) - mLayerMatricHead, & ! intent(out): trial value for total water matric potential (m) - mLayerMatricHead, & ! intent(in): value for total water matric potential (m) - mLayerVolFracWat, & ! intent(out): trial vector of volumetric total water content (-) - mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) - mLayerVolFracIce, & ! intent(out): trial vector of volumetric ice water content (-) - mLayerVolFracIce, & ! intent(in): vector of volumetric ice water content (-) - mLayerVolFracLiq, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerVolFracLiq, & ! intent(in): vector of volumetric liquid water content (-) - scalarAquiferStorage, & ! intent(out): trial value of storage of water in the aquifer (m) - scalarAquiferStorage, & ! intent(in): value of storage of water in the aquifer (m) - mLayerEnthalpy, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) - mLayerEnthalpy, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) - mLayerMatricHeadPrime, & ! intent(out): derivative value for total water matric potential (m s-1) - ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - ! output: flux and residual vectors - feasible, & ! intent(out): flag to denote the feasibility of the solution - fluxVec0, & ! intent(out): flux vector - rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) -#endif - case(kinsol, numrec) - call eval8summa(& - ! input: model control - dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): number of layers - nState, & ! intent(in): number of state variables in the current subset - .false., & ! intent(in): not inside Sundials solver - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVecTrial, & ! intent(in): model state vector - fScale, & ! intent(in): characteristic scale of the function evaluations - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - ! output - feasible, & ! intent(out): flag to denote the feasibility of the solution - fluxVec0, & ! intent(out): flux vector - rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector - fOld, & ! intent(out): function evaluation - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or numrec (or itertive, which is numrec)'; return - end select + ! compute the initial flux and the residual vector, also gets values needed for the Jacobian matrix + ! (prime initial values are 0 so it's fine to run the regular eval8summa with every solver choice) + call eval8summa(& + ! input: model control + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): number of layers + nState, & ! intent(in): number of state variables in the current subset + .false., & ! intent(in): not inside Sundials solver + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVecTrial, & ! intent(in): model state vector + fScale, & ! intent(in): characteristic scale of the function evaluations + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + indx_data, & ! intent(inout): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + ! output + feasible, & ! intent(out): flag to denote the feasibility of the solution + fluxVec0, & ! intent(out): flux vector + rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec, & ! intent(out): residual vector + fOld, & ! intent(out): function evaluation + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif @@ -849,7 +766,7 @@ subroutine systemSolv(& endif end associate layerVars endif - + case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or numrec (or itertive, which is numrec)'; return end select ! set untapped melt energy to zero @@ -858,7 +775,6 @@ subroutine systemSolv(& ! ************************** ! free memory deallocate(mLayerCmpress_sum) - deallocate(mLayerMatricHeadPrime) deallocate(dBaseflow_dMatric) ! end associate statements From 6d8d4568bc2c687fa5f4d19aee4ae5e58ac81fbe Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 26 Jun 2023 22:26:21 +0900 Subject: [PATCH 0775/1472] turning off check longwave balance with kinsol --- build/source/engine/coupled_em.f90 | 2 +- build/source/engine/eval8summa.f90 | 30 +++++++++++++++++++++--------- build/source/engine/opSplittin.f90 | 5 ++--- build/source/engine/systemSolv.f90 | 3 +-- build/source/engine/varSubstep.f90 | 8 +++----- 5 files changed, 28 insertions(+), 20 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index d976f06c8..5f30443ff 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1163,7 +1163,7 @@ subroutine coupled_em(& ! associate local variables with information in the data structures associate(& ! model decisions - ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! choice of numerical method, backward Euler or SUNDIALS/IDA + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! choice of numerical solver ! model forcing scalarSnowfall => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSnowfall) )%dat(1) ,& ! computed snowfall rate (kg m-2 s-1) scalarRainfall => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarRainfall) )%dat(1) ,& ! computed rainfall rate (kg m-2 s-1) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 9fd4bbafe..f0f92c18f 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -58,6 +58,12 @@ module eval8summa_module closedForm, & ! heat capacity using closed form, not using enthalpy enthalpyFD ! heat capacity using enthalpy +! look-up values for the numerical method +USE mDecisions_module,only: & +numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes +kinsol ,& ! SUNDIALS backward Euler solution using Kinsol +ida ! SUNDIALS solution using IDA + implicit none private public::eval8summa @@ -198,6 +204,7 @@ subroutine eval8summa(& real(rkind),dimension(nLayers) :: dEnthalpy_dTk ! derivatives in layer enthalpy w.r.t. temperature real(rkind),dimension(nLayers) :: dEnthalpy_dWat ! derivatives in layer enthalpy w.r.t. water state ! other local variables + logical(lgt) :: checkLWBalance ! flag to check longwave balance integer(i4b) :: iLayer ! index of model layer in the snow+soil domain integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine @@ -213,16 +220,17 @@ subroutine eval8summa(& ! -------------------------------------------------------------------------------------------------------------------------------- associate(& ! model decisions - ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy - ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver + ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy + ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) ! soil parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) ! model state variables from the previous solution scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) @@ -535,6 +543,10 @@ subroutine eval8summa(& ! save the number of flux calls per time step indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) + 1 + ! only need to check longwave balance with numerical recipes solver + checkLWBalance = .false. + if(ixNumericalMethod==numrec) checkLWBalance = .true. + ! compute the fluxes for a given state vector call computFlux(& ! input-output: model control @@ -546,7 +558,7 @@ subroutine eval8summa(& firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution - .true., & ! intent(in): check longwave balane + checkLWBalance, & ! intent(in): flag to check longwave balance scalarSfcMeltPond/dt, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) ! input: state variables scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) @@ -650,7 +662,7 @@ end subroutine eval8summa #ifdef SUNDIALS_ACTIVE ! ********************************************************************************************************** -! public function eval8summa4kinsol: compute the residual vector F(t,y) required for IDA solver +! public function eval8summa4kinsol: compute the residual vector F(t,y) required for KINSOL solver ! ********************************************************************************************************** ! Return values: ! 0 = success, diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index d7d21a96e..d81fc4c12 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -289,7 +289,7 @@ subroutine opSplittin(& globalVars: associate(& ! model decisions - ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical method, backward Euler or SUNDIALS/IDA + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver ! vector of energy and hydrology indices for the snow and soil domains ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain @@ -320,8 +320,7 @@ subroutine opSplittin(& select case(ixNumericalMethod) case(ida); nCoupling = 1 case(kinsol, numrec); nCoupling = 2 - case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or numrec (or itertive, which is numrec)'; return - end select + end select ! ----- ! * initialize... diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 290775c9d..27d8864b4 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -259,7 +259,7 @@ subroutine systemSolv(& ! --------------------------------------------------------------------------------------- globalVars: associate(& ! model decisions - ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical method, backward Euler or SUNDIALS/IDA + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) @@ -766,7 +766,6 @@ subroutine systemSolv(& endif end associate layerVars endif - case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or numrec (or itertive, which is numrec)'; return end select ! set untapped melt energy to zero diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 46091a7b9..e6b762ae1 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -226,7 +226,7 @@ subroutine varSubstep(& ! --------------------------------------------------------------------------------------- globalVars: associate(& ! model decisions - ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical method, backward Euler or SUNDIALS/IDA + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver ! number of layers nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers @@ -325,7 +325,7 @@ subroutine varSubstep(& call systemSolv(& ! input: model control dtSubstep, & ! intent(in): time step (s) - whole_step, & ! intent(in): entire time step (s), right now same as dtSubstep but might change with operator splitting + whole_step, & ! intent(in): entire time step (s) nState, & ! intent(in): total number of state variables firstSubStep, & ! intent(in): flag to denote first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call @@ -418,7 +418,6 @@ subroutine varSubstep(& select case(ixNumericalMethod) case(ida); checkMassBalance = .false. ! IDA has instantaneous fluxes only so average will not balance over data window case(kinsol, numrec); checkMassBalance = .true. ! (.not.scalarSolution) - case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or numrec (or itertive, which is numrec)'; return end select ! identify the need to check the energy balance, DOES NOT WORK YET and only check if ixHowHeatCap == enthalpyFD @@ -666,7 +665,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! point to flux variables in the data structure associate(& ! model decisions - ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical method, backward Euler or SUNDIALS/IDA + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver ! get indices for mass balance ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in) : [i4b] index of canopy hydrology state variable (mass) ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the soil domain @@ -876,7 +875,6 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) ! output: error control err,cmessage) ! intent(out): error control - case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or numrec (or itertive, which is numrec)'; return end select if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) From 570b3d9b979baae34d22b7e0708b7b7d4f97d4ba Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 26 Jun 2023 22:28:43 +0900 Subject: [PATCH 0776/1472] spaces --- build/source/engine/eval8summa.f90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index f0f92c18f..481ad627e 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -59,10 +59,10 @@ module eval8summa_module enthalpyFD ! heat capacity using enthalpy ! look-up values for the numerical method -USE mDecisions_module,only: & -numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes -kinsol ,& ! SUNDIALS backward Euler solution using Kinsol -ida ! SUNDIALS solution using IDA +USE mDecisions_module,only: & + numrec, & ! home-grown backward Euler solution using free versions of Numerical recipes + kinsol, & ! SUNDIALS backward Euler solution using Kinsol + ida ! SUNDIALS solution using IDA implicit none private From 07879d0de5d36ce57140b7f7d101495bda9c2ed2 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 27 Jun 2023 16:53:55 +0900 Subject: [PATCH 0777/1472] changing Sundials name to Prime --- build/source/engine/computFlux.f90 | 8 ++++---- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/eval8summaWithPrime.f90 | 12 ++++++------ build/source/engine/getVectorz.f90 | 8 ++++---- build/source/engine/mDecisions.f90 | 2 +- build/source/engine/opSplittin.f90 | 3 +-- build/source/engine/soil_utilsAddPrime.f90 | 8 ++++---- build/source/engine/summaSolve4ida.f90 | 4 ++-- build/source/engine/tol4ida.f90 | 2 -- build/source/engine/updatStateWithPrime.f90 | 20 ++++++++++---------- build/source/engine/varSubstep.f90 | 2 +- 11 files changed, 34 insertions(+), 37 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 9e557207a..215ada9ca 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -97,7 +97,7 @@ module computFlux_module private public::computFlux public::soilCmpres -public::soilCmpresSundials +public::soilCmpresPrime contains @@ -957,7 +957,7 @@ end subroutine soilCmpres ! ********************************************************************************************************** ! public subroutine soilCmpres: compute soil compressibility (-) and its derivative w.r.t matric head (m-1) ! ********************************************************************************************************** -subroutine soilCmpresSundials(& +subroutine soilCmpresPrime(& ! input: ixRichards, & ! intent(in): choice of option for Richards' equation ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers @@ -988,7 +988,7 @@ subroutine soilCmpresSundials(& integer(i4b) :: iLayer ! index of soil layer ! -------------------------------------------------------------- ! initialize error control - err=0; message='soilCmpresSundials/' + err=0; message='soilCmpresPrime/' ! (only compute for the mixed form of Richards' equation) if(ixRichards==mixdform)then do iLayer=1,size(mLayerMatricHeadPrime) @@ -1003,6 +1003,6 @@ subroutine soilCmpresSundials(& compress(:) = 0._rkind dCompress_dPsi(:) = 0._rkind end if -end subroutine soilCmpresSundials +end subroutine soilCmpresPrime end module computFlux_module diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 481ad627e..2389c9fd7 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -125,7 +125,7 @@ subroutine eval8summa(& USE getVectorz_module, only:checkFeas ! check feasibility of state vector USE updateVars_module, only:updateVars ! update prognostic variables USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy - USE computFlux_module, only:soilCmpres ! compute soil compression, use non-sundials version because sundials version needs mLayerMatricHeadPrime + USE computFlux_module, only:soilCmpres ! compute soil compression USE computFlux_module, only:computFlux ! compute fluxes given a state vector USE computHeatCap_module,only:computHeatCap ! recompute heat capacity and derivatives USE computHeatCap_module,only:computHeatCapAnalytic ! recompute heat capacity and derivatives diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 2a5664e47..b4bc44b1d 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -80,7 +80,7 @@ subroutine eval8summaWithPrime(& diag_data, & ! intent(inout) model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: here we need to pass some extra variables that do not get updated in in the Sundials loops + ! input-output: here we need to pass some extra variables that do not get updated in in the IDA loops scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) scalarCanopyTempPrev, & ! intent(in): value of canopy temperature (K) scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) @@ -120,7 +120,7 @@ subroutine eval8summaWithPrime(& USE getVectorz_module, only:checkFeas ! check feasibility of state vector USE updateVarsWithPrime_module, only:updateVarsWithPrime ! update variables USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy - USE computFlux_module, only:soilCmpresSundials ! compute soil compression + USE computFlux_module, only:soilCmpresPrime ! compute soil compression USE computFlux_module, only:computFlux ! compute fluxes given a state vector USE computHeatCap_module,only:computHeatCap ! recompute heat capacity and derivatives USE computHeatCap_module,only:computHeatCapAnalytic ! recompute heat capacity and derivatives @@ -161,7 +161,7 @@ subroutine eval8summaWithPrime(& type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! input-output: here we need to pass some extra variables that do not get updated in in the Sundials loops + ! input-output: here we need to pass some extra variables that do not get updated in in the IDA loops real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) real(rkind),intent(in) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) real(rkind),intent(out) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) @@ -637,7 +637,7 @@ subroutine eval8summaWithPrime(& ! compute soil compressibility (-) and its derivative w.r.t. matric head (m) ! NOTE: we already extracted trial matrix head and volumetric liquid water as part of the flux calculations - call soilCmpresSundials(& + call soilCmpresPrime(& ! input: ixRichards, & ! intent(in): choice of option for Richards' equation ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers @@ -657,7 +657,7 @@ subroutine eval8summaWithPrime(& ! compute the residual vector if (insideSUN)then - dt1 = 1._qp ! always 1 for sundials since using Prime derivatives + dt1 = 1._qp ! always 1 for IDA since using Prime derivatives call computResidWithPrime(& ! input: model control @@ -780,7 +780,7 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: here we need to pass some extra variables that do not get updated in in the Sundials loops + ! input-output: here we need to pass some extra variables that do not get updated in in the IDA loops eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index 695fb4d29..ffab3a646 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -265,10 +265,10 @@ subroutine getScaling(& type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers ! output: state vectors - real(rkind),intent(out) :: fScale(:) ! characteristic scale of the function evaluations (mixed units) - real(rkind),intent(out) :: xScale(:) ! variable scaling vector (mixed units) - real(qp),intent(out) :: sMul(:) ! NOTE: qp ! multiplier for state vector (used in the residual calculations) - real(rkind),intent(out) :: dMat(:) ! diagonal of the Jacobian matrix (excludes fluxes) + real(rkind),intent(out) :: fScale(:) ! characteristic scale of the function evaluations (mixed units) + real(rkind),intent(out) :: xScale(:) ! variable scaling vector (mixed units) + real(qp),intent(out) :: sMul(:) ! NOTE: qp ! multiplier for state vector (used in the residual calculations) + real(rkind),intent(out) :: dMat(:) ! diagonal of the Jacobian matrix (excludes fluxes) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index d49f7a705..9d8a9f1e2 100755 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -427,7 +427,7 @@ subroutine mDecisions(err,message) endif #endif - ! how to compute heat capacity in energy equation, choice enthalpyFD has better coincidence of energy conservation with sundials tolerance. + ! how to compute heat capacity in energy equation, choice enthalpyFD has better coincidence of energy conservation with IDA tolerance. select case(trim(model_decisions(iLookDECISIONS%howHeatCap)%cDecision)) case('enthalpyFD'); model_decisions(iLookDECISIONS%howHeatCap)%iDecision = enthalpyFD ! heat capacity using enthalpy case('closedForm'); model_decisions(iLookDECISIONS%howHeatCap)%iDecision = closedForm ! heat capacity using closed form, not using enthalpy diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index d81fc4c12..ff61b0dd8 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -99,7 +99,7 @@ module opSplittin_module ! look-up values for the numerical method USE mDecisions_module,only: & - numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes + numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes kinsol ,& ! SUNDIALS backward Euler solution using Kinsol ida ! SUNDIALS solution using IDA @@ -649,7 +649,6 @@ subroutine opSplittin(& ! reset the flag for the first flux call if(.not.firstSuccess) firstFluxCall=.true. - ! update variables, also updated inside SUNDIALS (if fail a split will need these) ! save/recover copies of prognostic variables do iVar=1,size(prog_data%var) select case(failure) diff --git a/build/source/engine/soil_utilsAddPrime.f90 b/build/source/engine/soil_utilsAddPrime.f90 index 9e2a20a1a..49aca0dc8 100644 --- a/build/source/engine/soil_utilsAddPrime.f90 +++ b/build/source/engine/soil_utilsAddPrime.f90 @@ -38,7 +38,7 @@ module soil_utilsAddPrime_module ! routines to make public -public::liquidHeadSundials +public::liquidHeadPrime public::d2Theta_dPsi2 public::d2Theta_dTk2 @@ -50,7 +50,7 @@ module soil_utilsAddPrime_module ! ****************************************************************************************************************************** ! public subroutine: compute the liquid water matric potential (and the derivatives w.r.t. total matric potential and temperature) ! ****************************************************************************************************************************** -subroutine liquidHeadSundials(& +subroutine liquidHeadPrime(& ! input matricHeadTotal ,& ! intent(in) : total water matric potential (m) matricHeadTotalPrime ,& ! intent(in) @@ -98,7 +98,7 @@ subroutine liquidHeadSundials(& real(rkind) :: effSatPrime ! ------------------------------------------------------------------------------------------------------------------------------ ! initialize error control - err=0; message='liquidHeadSundials/' + err=0; message='liquidHeadPrime/' ! ** partially frozen soil if(volFracIce > verySmall .and. matricHeadTotal < 0._rkind)then ! check that ice exists and that the soil is unsaturated @@ -172,7 +172,7 @@ subroutine liquidHeadSundials(& if(present(dPsiLiq_dTemp)) dPsiLiq_dTemp = 0._rkind ! derivative=0 because no impact of temperature for unfrozen conditions end if ! (if ice exists) -end subroutine liquidHeadSundials +end subroutine liquidHeadPrime ! ****************************************************************************************************************************** ! public function d2Theta_dPsi2: compute the second derivative of the soil water characteristic (m-1) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index bb5cbb5da..c53b20988 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -186,7 +186,7 @@ subroutine summaSolve4ida( & real(rkind),intent(inout) :: stateVecPrime(:) ! model state vector (y') logical(lgt),intent(out) :: idaSucceeds ! flag to indicate if IDA is successful logical(lgt),intent(inout) :: tooMuchMelt ! flag to denote that there was too much melt - real(qp),intent(out) :: dt_out ! time step sum for entire data window at termination of sundials + real(qp),intent(out) :: dt_out ! time step sum for entire data window at termination of IDA loops ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -463,7 +463,7 @@ subroutine summaSolve4ida( & ! output: error control err,cmessage) ! intent(out): error control - ! early return for non-feasible solutions, will fail in current Sundials formulation + ! early return for non-feasible solutions, will fail in current IDA formulation if(.not.feasible)then eqns_data%fluxVec(:) = realMissing message=trim(message)//trim(cmessage)//'non-feasible' diff --git a/build/source/engine/tol4ida.f90 b/build/source/engine/tol4ida.f90 index 6399a3cfb..fb1752001 100644 --- a/build/source/engine/tol4ida.f90 +++ b/build/source/engine/tol4ida.f90 @@ -92,7 +92,6 @@ integer(c_int) function computWeight4ida(sunvec_y, sunvec_ewt, user_data) & type(N_Vector) :: sunvec_ewt ! derivative N_Vector W type(c_ptr), value :: user_data ! user-defined data - ! pointers to data in SUNDIALS vectors type(data4ida), pointer :: tol_data ! equations data real(rkind), pointer :: stateVec(:) @@ -108,7 +107,6 @@ integer(c_int) function computWeight4ida(sunvec_y, sunvec_ewt, user_data) & stateVec(1:tol_data%nState) => FN_VGetArrayPointer(sunvec_y) weightVec(1:tol_data%nState) => FN_VGetArrayPointer(sunvec_ewt) - do iState = 1,tol_data%nState weightVec(iState) = tol_data%rtol(iState) * abs( stateVec(iState) ) + tol_data%atol(iState) weightVec(iState) = 1._rkind / weightVec(iState) diff --git a/build/source/engine/updatStateWithPrime.f90 b/build/source/engine/updatStateWithPrime.f90 index d97acdf9a..c60fe4371 100644 --- a/build/source/engine/updatStateWithPrime.f90 +++ b/build/source/engine/updatStateWithPrime.f90 @@ -10,8 +10,8 @@ module updatStateWithPrime_module LH_fus ! latent heat of fusion (J kg-1) implicit none private -public::updateSnowSundials -public::updateSoilSundials +public::updateSnowPrime +public::updateSoilPrime real(rkind),parameter :: verySmall=1e-14_rkind ! a very small number (used to avoid divide by zero) @@ -19,9 +19,9 @@ module updatStateWithPrime_module ! ************************************************************************************************************* -! public subroutine updateSnowSundials: compute phase change impacts on volumetric liquid water and ice +! public subroutine updateSnowPrime: compute phase change impacts on volumetric liquid water and ice ! ************************************************************************************************************* -subroutine updateSnowSundials(& +subroutine updateSnowPrime(& ! input mLayerTemp ,& ! intent(in): temperature (K) mLayerTheta ,& ! intent(in): volume fraction of total water (-) @@ -55,7 +55,7 @@ subroutine updateSnowSundials(& integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! initialize error control - err=0; message="updateSnowSundials/" + err=0; message="updateSnowPrime/" ! compute the volumetric fraction of liquid water and ice (-) fLiq = fracliquid(mLayerTemp,snowfrz_scale) @@ -64,12 +64,12 @@ subroutine updateSnowSundials(& mLayerVolFracLiqPrime = fLiq * mLayerThetaPrime + dFracLiq_dTk(mLayerTemp,snowfrz_scale) * mLayerTheta * mLayerTempPrime mLayerVolFracIcePrime = ( mLayerThetaPrime - mLayerVolFracLiqPrime ) * (iden_water/iden_ice) -end subroutine updateSnowSundials +end subroutine updateSnowPrime ! *********************************************************************************************************************************** -! public subroutine updateSoilSundials: compute phase change impacts on matric head and volumetric liquid water and ice (veg or soil) +! public subroutine updateSoilPrime: compute phase change impacts on matric head and volumetric liquid water and ice (veg or soil) ! *********************************************************************************************************************************** -subroutine updateSoilSundials(& +subroutine updateSoilPrime(& ! input mLayerTemp ,& ! intent(in): temperature (K) mLayerMatricHead ,& ! intent(in): total water matric potential (m) @@ -118,7 +118,7 @@ subroutine updateSoilSundials(& real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) real(rkind),parameter :: tinyVal=epsilon(1._rkind) ! used in balance check ! initialize error control - err=0; message="updateSoilSundials/" + err=0; message="updateSoilPrime/" ! compute fractional **volume** of total water (liquid plus ice) mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) @@ -150,6 +150,6 @@ subroutine updateSoilSundials(& mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime -end subroutine updateSoilSundials +end subroutine updateSoilPrime end module updatStateWithPrime_module diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index e6b762ae1..598617a70 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -364,7 +364,7 @@ subroutine varSubstep(& ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry if(tooMuchMelt .or. reduceCoupledStep) return - ! identify failure, should not happen in sundials + ! identify failure, should not happen in SUNDIALS failedSubstep = (err<0) ! check From d9d9ea50403cee8d7c7135cf040ad3b5b889ec4e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 27 Jun 2023 16:55:15 +0900 Subject: [PATCH 0778/1472] sundials name to Prime --- build/source/engine/updateVarsWithPrime.f90 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/build/source/engine/updateVarsWithPrime.f90 b/build/source/engine/updateVarsWithPrime.f90 index 0cdb6a3a3..b10443c8a 100644 --- a/build/source/engine/updateVarsWithPrime.f90 +++ b/build/source/engine/updateVarsWithPrime.f90 @@ -73,8 +73,8 @@ module updateVarsWithPrime_module USE var_lookup,only:iLookINDEX ! named variables for structure elements ! provide access to routines to update states -USE updatStateWithPrime_module,only:updateSnowSundials ! update snow states -USE updatStateWithPrime_module,only:updateSoilSundials ! update soil states +USE updatStateWithPrime_module,only:updateSnowPrime ! update snow states +USE updatStateWithPrime_module,only:updateSoilPrime ! update soil states ! provide access to functions for the constitutive functions and derivatives USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) @@ -85,7 +85,7 @@ module updateVarsWithPrime_module USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists -USE soil_utilsAddPrime_module,only:liquidHeadSundials ! compute the liquid water matric potential +USE soil_utilsAddPrime_module,only:liquidHeadPrime ! compute the liquid water matric potential USE soil_utilsAddPrime_module,only:d2Theta_dPsi2 USE soil_utilsAddPrime_module,only:d2Theta_dTk2 @@ -99,7 +99,7 @@ module updateVarsWithPrime_module contains ! ********************************************************************************************************** -! public subroutine updateVarsWithPrime: compute diagnostic variables and derivatives for Sundials Jacobian +! public subroutine updateVarsWithPrime: compute diagnostic variables and derivatives for Prime Jacobian ! ********************************************************************************************************** subroutine updateVarsWithPrime(& ! input @@ -494,7 +494,7 @@ subroutine updateVarsWithPrime(& case(iname_veg) ! compute volumetric fraction of liquid water and ice - call updateSnowSundials(& + call updateSnowPrime(& xTemp, & ! intent(in) : temperature (K) scalarCanopyWatTrial/(iden_water*canopyDepth),& ! intent(in) : volumetric fraction of total water (-) snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) @@ -519,7 +519,7 @@ subroutine updateVarsWithPrime(& case(iname_snow) ! compute volumetric fraction of liquid water and ice - call updateSnowSundials(& + call updateSnowPrime(& xTemp, & ! intent(in) : temperature (K) mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) @@ -537,7 +537,7 @@ subroutine updateVarsWithPrime(& case(iname_soil) ! compute volumetric fraction of liquid water and ice - call updateSoilSundials(& + call updateSoilPrime(& xTemp, & ! intent(in) : temperature (K) mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) mLayerTempPrime(iLayer), & ! intent(in) : temperature time derivative (K/s) @@ -711,7 +711,7 @@ subroutine updateVarsWithPrime(& ! case of energy state or coupled solution else ! compute the liquid matric potential (and the derivatives w.r.t. total matric potential and temperature) - call liquidHeadSundials(& + call liquidHeadPrime(& ! input mLayerMatricHeadTrial(ixControlIndex) ,& ! intent(in) : total water matric potential (m) mLayerMatricHeadPrime(ixControlIndex) ,& ! From 328caeb9bc161d9868b55dc69db02184fc03c772 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 27 Jun 2023 21:58:01 +0900 Subject: [PATCH 0779/1472] don't need to update things before Jacobian that did in eval8 according to documentation --- build/source/dshare/get_ixname.f90 | 2 +- build/source/dshare/popMetadat.f90 | 2 +- build/source/dshare/type4ida.f90 | 7 +- build/source/engine/computJacob.f90 | 230 +----------- build/source/engine/computJacobWithPrime.f90 | 353 ++----------------- build/source/engine/eval8summaWithPrime.f90 | 28 +- build/source/engine/summaSolve4ida.f90 | 9 +- 7 files changed, 85 insertions(+), 546 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index f0eac12f7..16864eb5a 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -381,7 +381,7 @@ function get_ixparam(varName) case('maxstep' ); get_ixparam = iLookPARAM%maxstep ! maximum length of the time step numrec case('be_steps' ); get_ixparam = iLookPARAM%be_steps ! minimum number of substeps to take in a maxstep numrec case('wimplicit' ); get_ixparam = iLookPARAM%wimplicit ! weight assigned to start-of-step fluxes numrec, not currently used - case('maxiter' ); get_ixparam = iLookPARAM%maxiter ! maximum number of iterations numrec, kinsol, or nonlinear iterations Sundials + case('maxiter' ); get_ixparam = iLookPARAM%maxiter ! maximum number of iterations numrec, kinsol, or nonlinear iterations ida case('relConvTol_liquid' ); get_ixparam = iLookPARAM%relConvTol_liquid ! relative convergence tolerance for vol frac liq water (-) numrec case('absConvTol_liquid' ); get_ixparam = iLookPARAM%absConvTol_liquid ! absolute convergence tolerance for vol frac liq water (-) numrec case('relConvTol_matric' ); get_ixparam = iLookPARAM%relConvTol_matric ! relative convergence tolerance for matric head (-) numrec diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index db174da8c..f7d8d4427 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -263,7 +263,7 @@ subroutine popMetadat(err,message) mpar_meta(iLookPARAM%maxstep) = var_info('maxstep' , 'maximum length of the time step numrec' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%be_steps) = var_info('be_steps' , 'minimum number of substeps to take in a maxstep numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%wimplicit) = var_info('wimplicit' , 'weight assigned to the start-of-step fluxes ,numrec, not currently used', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%maxiter) = var_info('maxiter' , 'maximum number of iterations numrec, kinsol, or nonlinear iterations Sundials', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%maxiter) = var_info('maxiter' , 'maximum number of iterations numrec, kinsol, or nonlinear iterations ida', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%relConvTol_liquid) = var_info('relConvTol_liquid' , 'relative convergence tolerance for vol frac liq water numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%absConvTol_liquid) = var_info('absConvTol_liquid' , 'absolute convergence tolerance for vol frac liq water numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%relConvTol_matric) = var_info('relConvTol_matric' , 'relative convergence tolerance for matric head numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/type4ida.f90 b/build/source/dshare/type4ida.f90 index a09bd162b..3d8ee6c63 100644 --- a/build/source/dshare/type4ida.f90 +++ b/build/source/dshare/type4ida.f90 @@ -69,7 +69,12 @@ module type4ida real(rkind) :: scalarAquiferStoragePrev ! value of storage of water in the aquifer (m) at previous step real(rkind), allocatable :: mLayerEnthalpyTrial(:) ! trial enthalpy of snow and soil (J m-3) real(rkind), allocatable :: mLayerEnthalpyPrev(:) ! enthalpy of snow and soil (J m-3) at previous step - real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! derivative value for total water matric potential (m s-1) + real(rkind), allocatable :: mLayerTempPrime(:) ! derivative value for temperature of each snow and soil layer (K) + real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! derivative value for matric head of each snow and soil layer (m) + real(rkind), allocatable :: mLayerMatricHeadLiqPrime(:) ! derivative value for liquid water matric head of each snow and soil layer (m) + real(rkind), allocatable :: mLayerVolFracWatPrime(:) ! derivative value for volumetric total water content of each snow and soil layer (-) + real(rkind) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatPrime ! derivative value for total water content of the vegetation canopy (kg m-2) real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) integer(i4b) :: ixSaturation ! index of the lowest saturated layer integer(i4b) :: err ! error code diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 954deac6b..05f4d7a59 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -74,7 +74,6 @@ module computJacob_module private public::computJacob #ifdef SUNDIALS_ACTIVE -public::computJacob4kinsolSetup public::computJacob4kinsol #endif @@ -1006,198 +1005,6 @@ subroutine computJacob(& end subroutine computJacob #ifdef SUNDIALS_ACTIVE -! ********************************************************************************************************** -! public subroutine computJacob4kinsolSetup: compute the Jacobian matrix -! ********************************************************************************************************** -subroutine computJacob4kinsolSetup(& - ! input: model control - dt, & ! intent(in): time step - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - ixMatrix, & ! intent(in): form of the Jacobian matrix - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - ! input: state vectors - stateVec, & ! intent(in): model state vector - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input: baseflow - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! output: flux and residual vectors - dMat, & ! intent(inout): diagonal of Jacobian Matrix - Jac, & ! intent(out): jacobian matrix - err,message) ! intent(out): error control -! -------------------------------------------------------------------------------------------------------------------------------- -! provide access to subroutines -USE getVectorz_module, only:varExtract ! extract variables from the state vector -USE updateVars_module, only:updateVars ! update prognostic variables -implicit none -! -------------------------------------------------------------------------------------------------------------------------------- -! -------------------------------------------------------------------------------------------------------------------------------- -! input: model control -real(rkind),intent(in) :: dt ! time step -integer(i4b),intent(in) :: nSnow ! number of snow layers -integer(i4b),intent(in) :: nSoil ! number of soil layers -integer(i4b),intent(in) :: nLayers ! total number of layers -integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) -logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation -! input: state vectors -real(rkind),intent(in) :: stateVec(:) ! model state vector -real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) -! input: data structures -type(model_options),intent(in) :: model_decisions(:) ! model decisions -type(var_dlength), intent(in) :: mpar_data ! model parameters -type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU -! output: data structures -type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers -type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU -type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables -! input-output: baseflow -real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) -! output: Jacobian -real(rkind), intent(inout) :: dMat(:) -real(rkind), intent(out) :: Jac(:,:) ! jacobian matrix -! output: error control -integer(i4b),intent(out) :: err ! error code -character(*),intent(out) :: message ! error message -! -------------------------------------------------------------------------------------------------------------------------------- -! local variables -! -------------------------------------------------------------------------------------------------------------------------------- -! state variables -real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) -real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) -real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) -real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial value for temperature of layers in the snow and soil domains (K) -real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial value for volumetric fraction of total water (-) -real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial value for total water matric potential (m) -real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) -real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) -! diagnostic variables -real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) -real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) -real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) -real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) -! other local variables -character(LEN=256) :: cmessage ! error message of downwind routine - -! -------------------------------------------------------------------------------------------------------------------------------- -! association to variables in the data structures -! -------------------------------------------------------------------------------------------------------------------------------- -associate(& - ! model state variables - ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision & - ) ! association to variables in the data structures - ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="computJacob4kinsolSetup/" - - ! initialize to state variable from the last update - ! should all be set to previous values if splits, but for now operator splitting is not hooked up - scalarCanairTempTrial = realMissing - scalarCanopyTempTrial = realMissing - scalarCanopyWatTrial = realMissing - scalarCanopyLiqTrial = realMissing - scalarCanopyIceTrial = realMissing - mLayerTempTrial = realMissing - mLayerVolFracWatTrial = realMissing - mLayerVolFracLiqTrial = realMissing - mLayerVolFracIceTrial = realMissing - mLayerMatricHeadTrial = realMissing - mLayerMatricHeadLiqTrial = realMissing - scalarAquiferStorageTrial = realMissing - - ! extract variables from the model state vector - call varExtract(& - ! input - stateVec, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(inout): trial value of storage of water in the aquifer (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! update diagnostic variables and derivatives - call updateVars(& - ! input - .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! ----- - ! * compute the Jacobian matrix... - ! -------------------------------- - - ! compute the analytical Jacobian matrix - ! NOTE: The derivatives were computed in the previous call to computFlux - ! This occurred either at the call to eval8summa at the start of systemSolv - ! or in the call to eval8summa in the previous iteration - call computJacob(& - ! input: model control - dt, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - (ixGroundwater==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow - ixMatrix, & ! intent(in): form of the Jacobian matrix - ! input: data structures - indx_data, & ! intent(in): index data - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! input-output: Jacobian and its diagonal - dMat, & ! intent(inout): diagonal of the Jacobian matrix - Jac, & ! intent(out): Jacobian matrix - ! output: error control - err,cmessage) ! intent(out): error code and error message -if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - -! end association with the information in the data structures -end associate - -end subroutine computJacob4kinsolSetup - - ! ********************************************************************************************************** ! public function computJacob4kinsol: the interface to compute the Jacobian matrix dF/dy + c dF/dy' for IDA solver ! ********************************************************************************************************** @@ -1231,48 +1038,41 @@ integer(c_int) function computJacob4kinsol(sunvec_y, sunvec_r, sunmat_J, & type(N_Vector) :: sunvec_temp2 ! temporary N_Vector ! pointers to data in SUNDIALS vectors - real(c_double), pointer :: stateVec(:) ! state vector real(c_double), pointer :: Jac(:,:) ! Jacobian matrix - character(len=256) :: message ! error message type(data4kinsol), pointer :: eqns_data ! equations data - - !======= Internals ============ +! ---------------------------------------------------------------- ! get equations data from user-defined data call c_f_pointer(user_data,eqns_data) ! get data arrays from SUNDIALS vectors - stateVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_y) if (eqns_data%ixMatrix==ixBandMatrix) Jac(1:nBands, 1:eqns_data%nState) => FSUNBandMatrix_Data(sunmat_J) if (eqns_data%ixMatrix==ixFullMatrix) Jac(1:eqns_data%nState, 1:eqns_data%nState) => FSUNDenseMatrix_Data(sunmat_J) - ! compute Jacobian matrix - call computJacob4kinsolSetup(& + ! compute the analytical Jacobian matrix + ! NOTE: The derivatives were computed in the previous call to computFlux + ! This occurred either at the call to eval8summa at the start of systemSolv + ! or in the call to eval8summa in the previous iteration + call computJacob(& ! input: model control eqns_data%dt, & ! intent(in): data step eqns_data%nSnow, & ! intent(in): number of snow layers eqns_data%nSoil, & ! intent(in): number of soil layers eqns_data%nLayers, & ! intent(in): number of layers - eqns_data%ixMatrix, & ! intent(in): type of matrix (dense or banded) eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - ! input: state vectors - stateVec, & ! intent(in): model state vector - eqns_data%sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + (eqns_data%model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow + eqns_data%ixMatrix, & ! intent(in): type of matrix (dense or banded) ! input: data structures - eqns_data%model_decisions, & ! intent(in): model decisions - eqns_data%mpar_data, & ! intent(in): model parameters + eqns_data%indx_data, & ! intent(in): index data eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - eqns_data%indx_data, & ! intent(inout): index data - eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU - eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input: baseflow + eqns_data%diag_data, & ! intent(in): model diagnostic variables for a local HRU + eqns_data%deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables eqns_data%dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! output - eqns_data%dMat, & ! intetn(inout): diagonal of the Jacobian matrix + ! input-output: Jacobian and its diagonal + eqns_data%dMat, & ! intent(inout): diagonal of the Jacobian matrix Jac, & ! intent(out): Jacobian matrix - eqns_data%err,eqns_data%message) ! intent(out): error control - + ! output: error control + eqns_data%err,eqns_data%message) ! intent(out): error code and error message if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index 1e4bd4a35..c3998c165 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -82,7 +82,6 @@ module computJacobWithPrime_module private public::computJacobWithPrime -public::computJacob4idaSetup public::computJacob4ida contains @@ -1053,289 +1052,6 @@ subroutine computJacobWithPrime(& end subroutine computJacobWithPrime - -! ********************************************************************************************************** -! public subroutine computJacob4idaSetup: compute the Jacobian matrix -! ********************************************************************************************************** -subroutine computJacob4idaSetup(& - ! input: model control - cj, & ! intent(in): this scalar changes whenever the step size or method order changes - dt, & ! intent(in): time step - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - ixMatrix, & ! intent(in): form of the Jacobian matrix - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - ! input: state vectors - stateVec, & ! intent(in): model state vector - stateVecPrime, & ! intent(in): derivative of model state vector - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input: baseflow - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! output: flux and residual vectors - dMat, & ! intent(inout): diagonal of Jacobian Matrix - Jac, & ! intent(out): jacobian matrix - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - ! provide access to subroutines - USE getVectorz_module, only:varExtract ! extract variables from the state vector - USE updateVarsWithPrime_module, only:updateVarsWithPrime ! update prognostic variables - implicit none - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - ! input: model control - real(rkind),intent(in) :: cj ! this scalar changes whenever the step size or method order changes - real(rkind),intent(in) :: dt ! time step - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - ! input: state vectors - real(rkind),intent(in) :: stateVec(:) ! model state vector - real(rkind),intent(in) :: stateVecPrime(:) ! model state vector - real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) - ! input: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(var_dlength), intent(in) :: mpar_data ! model parameters - type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU - ! output: data structures - type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - ! output: Jacobian - real(rkind), intent(inout) :: dMat(:) - real(rkind), intent(out) :: Jac(:,:) ! jacobian matrix - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - ! -------------------------------------------------------------------------------------------------------------------------------- - ! state variables - real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial value for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial value for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial value for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) - real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) - ! diagnostic variables - real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) - ! derivative of state variables - real(rkind) :: scalarCanairTempPrime ! derivative value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempPrime ! derivative value for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! derivative value for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! derivative value for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! derivative value for liquid water matric potential (m) - real(rkind) :: scalarAquiferStoragePrime ! derivative value of storage of water in the aquifer (m) - ! derivative of diagnostic variables - real(rkind) :: scalarCanopyLiqPrime ! derivative value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIcePrime ! derivative value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! derivative value for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! derivative value for volumetric fraction of ice (-) - ! other local variables - character(LEN=256) :: cmessage ! error message of downwind routine - real(rkind) :: dt1 - - ! -------------------------------------------------------------------------------------------------------------------------------- - ! association to variables in the data structures - ! -------------------------------------------------------------------------------------------------------------------------------- - associate(& - ! model decisions - ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation - ! soil parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) - ! model state variables - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) - ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision & - ) ! association to variables in the data structures - ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="computJacob4idaSetup/" - - ! initialize to state variable from the last update - ! should all be set to previous values if splits, but for now operator splitting is not hooked up - scalarCanairTempTrial = realMissing - scalarCanopyTempTrial = realMissing - scalarCanopyWatTrial = realMissing - scalarCanopyLiqTrial = realMissing - scalarCanopyIceTrial = realMissing - mLayerTempTrial = realMissing - mLayerVolFracWatTrial = realMissing - mLayerVolFracLiqTrial = realMissing - mLayerVolFracIceTrial = realMissing - mLayerMatricHeadTrial = realMissing - mLayerMatricHeadLiqTrial = realMissing - scalarAquiferStorageTrial = realMissing - - ! extract variables from the model state vector - call varExtract(& - ! input - stateVec, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(inout): trial value of storage of water in the aquifer (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! initialize to state variable from the last update - ! should all be set to previous values if splits, but for now operator splitting is not hooked up - scalarCanairTempPrime = realMissing - scalarCanopyTempPrime = realMissing - scalarCanopyWatPrime = realMissing - scalarCanopyLiqPrime = realMissing - scalarCanopyIcePrime = realMissing - mLayerTempPrime = realMissing - mLayerVolFracWatPrime = realMissing - mLayerVolFracLiqPrime = realMissing - mLayerVolFracIcePrime = realMissing - mLayerMatricHeadPrime = realMissing - mLayerMatricHeadLiqPrime = realMissing - scalarAquiferStoragePrime = realMissing - - ! extract derivative of variables from derivative of the model state vector - call varExtract(& - ! input - stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy - scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K) - scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): derivative of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K) - mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (-) - mLayerMatricHeadPrime, & ! intent(inout): derivative of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): derivative of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStoragePrime,& ! intent(inout): derivative of storage of water in the aquifer (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! update diagnostic variables and derivatives - call updateVarsWithPrime(& - ! input - .true., & ! intent(in): logical flag if computing for Jacobian update - .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2 - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! - mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) - mLayerVolFracIcePrime, & ! - mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! ----- - ! * compute the Jacobian matrix... - ! -------------------------------- - - ! compute the analytical Jacobian matrix - ! NOTE: The derivatives were computed in the previous call to computFlux - ! This occurred either at the call to eval8summaWithPrime at the start of systemSolv - ! or in the call to eval8summaWithPrime in the previous iteration - dt1 = 1._qp - call computJacobWithPrime(& - ! input: model control - cj, & ! intent(in): this scalar changes whenever the step size or method order changes - dt1, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - (ixGroundwater==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow - ixMatrix, & ! intent(in): form of the Jacobian matrix - specificStorage, & ! intent(in): specific storage coefficient (m-1) - theta_sat, & ! intent(in): soil porosity (-) - ixRichards, & ! intent(in): choice of option for Richards' equation - ! input: data structures - model_decisions, & ! intent(in): model decisions - indx_data, & ! intent(in): index data - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! input: state variables - mLayerTempPrime, & ! intent(in): derivative value for temperature of each snow and soil layer (K) - mLayerMatricHeadPrime, & ! intent(in): derivative value for matric head of each snow and soil layer (m) - mLayerMatricHeadLiqPrime, & ! intent(in): derivative value for liquid water matric head of each snow and soil layer (m) - mLayerVolFracWatPrime, & ! intent(in): derivative value for volumetric total water content of each snow and soil layer (-) - scalarCanopyTempPrime, & ! intent(in): derivative value for temperature of the vegetation canopy (K) - scalarCanopyWatPrime, & ! intent(in): derivative value for total water content of the vegetation canopy (kg m-2) - ! input-output: Jacobian and its diagonal - dMat, & ! intent(inout): diagonal of the Jacobian matrix - Jac, & ! intent(out): Jacobian matrix - ! output: error control - err,cmessage) ! intent(out): error code and error message - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! end association with the information in the data structures - end associate - -end subroutine computJacob4idaSetup - - ! ********************************************************************************************************** ! public function computJacob4ida: the interface to compute the Jacobian matrix dF/dy + c dF/dy' for IDA solver ! ********************************************************************************************************** @@ -1373,51 +1089,53 @@ integer(c_int) function computJacob4ida(t, cj, sunvec_y, sunvec_yp, sunvec_r, & type(N_Vector) :: sunvec_temp3 ! temporary N_Vector ! pointers to data in SUNDIALS vectors - real(rkind), pointer :: stateVec(:) ! state vector - real(rkind), pointer :: stateVecPrime(:)! derivative of the state vector real(rkind), pointer :: Jac(:,:) ! Jacobian matrix type(data4ida), pointer :: eqns_data ! equations data - - !======= Internals ============ + ! ---------------------------------------------------------------- ! get equations data from user-defined data call c_f_pointer(user_data, eqns_data) ! get data arrays from SUNDIALS vectors - stateVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_y) - stateVecPrime(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_yp) if (eqns_data%ixMatrix==ixBandMatrix) Jac(1:nBands, 1:eqns_data%nState) => FSUNBandMatrix_Data(sunmat_J) if (eqns_data%ixMatrix==ixFullMatrix) Jac(1:eqns_data%nState, 1:eqns_data%nState) => FSUNDenseMatrix_Data(sunmat_J) - ! compute Jacobian matrix - call computJacob4idaSetup(& + ! compute the analytical Jacobian matrix + ! NOTE: The derivatives were computed in the previous call to computFlux + ! This occurred either at the call to eval8summaWithPrime at the start of systemSolv + ! or in the call to eval8summaWithPrime in the previous iteration + call computJacobWithPrime(& ! input: model control - cj, & ! intent(in): this scalar changes whenever the step size or method order changes - eqns_data%dt, & ! intent(in): data step - eqns_data%nSnow, & ! intent(in): number of snow layers - eqns_data%nSoil, & ! intent(in): number of soil layers - eqns_data%nLayers, & ! intent(in): number of layers - eqns_data%ixMatrix, & ! intent(in): type of matrix (dense or banded) - eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - ! input: state vectors - stateVec, & ! intent(in): model state vector - stateVecPrime, & ! intent(in): model state vector - eqns_data%sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + cj, & ! intent(in): this scalar changes whenever the step size or method order changes + 1._qp, & ! intent(in): length of the time step (seconds) + eqns_data%nSnow, & ! intent(in): number of snow layers + eqns_data%nSoil, & ! intent(in): number of soil layers + eqns_data%nLayers, & ! intent(in): total number of layers + eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + (eqns_data%model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow + eqns_data%ixMatrix, & ! intent(in): form of the Jacobian matrix + eqns_data%mpar_data%var(iLookPARAM%specificStorage)%dat(1) , & ! intent(in): specific storage coefficient (m-1) + eqns_data%mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): soil porosity (-) + eqns_data%model_decisions(iLookDECISIONS%f_Richards)%iDecision, & ! intent(in): choice of option for Richards' equation ! input: data structures - eqns_data%model_decisions, & ! intent(in): model decisions - eqns_data%mpar_data, & ! intent(in): model parameters - eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - eqns_data%indx_data, & ! intent(inou): index data - eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU - eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input: baseflow - eqns_data%dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! output - eqns_data%dMat, & ! intetn(inout): diagonal of the Jacobian matrix - Jac, & ! intent(out): Jacobian matrix - eqns_data%err,eqns_data%message) ! intent(out): error control - + eqns_data%model_decisions, & ! intent(in): model decisions + eqns_data%indx_data, & ! intent(in): index data + eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU + eqns_data%diag_data, & ! intent(in): model diagnostic variables for a local HRU + eqns_data%deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables + eqns_data%dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! input: state variables + eqns_data%mLayerTempPrime, & ! intent(in): derivative value for temperature of each snow and soil layer (K) + eqns_data%mLayerMatricHeadPrime, & ! intent(in): derivative value for matric head of each snow and soil layer (m) + eqns_data%mLayerMatricHeadLiqPrime, & ! intent(in): derivative value for liquid water matric head of each snow and soil layer (m) + eqns_data%mLayerVolFracWatPrime, & ! intent(in): derivative value for volumetric total water content of each snow and soil layer (-) + eqns_data%scalarCanopyTempPrime, & ! intent(in): derivative value for temperature of the vegetation canopy (K) + eqns_data%scalarCanopyWatPrime, & ! intent(in): derivative value for total water content of the vegetation canopy (kg m-2) + ! input-output: Jacobian and its diagonal + eqns_data%dMat, & ! intent(inout): diagonal of the Jacobian matrix + Jac, & ! intent(out): Jacobian matrix + ! output: error control + eqns_data%err,eqns_data%message) ! intent(out): error code and error message if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif @@ -1427,7 +1145,6 @@ integer(c_int) function computJacob4ida(t, cj, sunvec_y, sunvec_yp, sunvec_r, & end function computJacob4ida - ! ********************************************************************************************************** ! private function: get the off-diagonal index in the band-diagonal matrix ! ********************************************************************************************************** diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index b4bc44b1d..01f3ec2a1 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -104,8 +104,13 @@ subroutine eval8summaWithPrime(& scalarAquiferStoragePrev,& ! intent(in): value of storage of water in the aquifer (m) mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) - mLayerMatricHeadPrime, & ! intent(out): trial value for prime total water matric potential (m s-1) - ! input-output: baseflow + mLayerTempPrime, & ! intent(out): derivative value for temperature of each snow and soil layer (K) + mLayerMatricHeadPrime, & ! intent(out): derivative value for matric head of each snow and soil layer (m) + mLayerMatricHeadLiqPrime,& ! intent(out): derivative value for liquid water matric head of each snow and soil layer (m) + mLayerVolFracWatPrime, & ! intent(out): derivative value for volumetric total water content of each snow and soil layer (-) + scalarCanopyTempPrime, & ! intent(out): derivative value for temperature of the vegetation canopy (K) + scalarCanopyWatPrime, & ! intent(out): derivative value for total water content of the vegetation canopy (kg m-2) + ! input-output: baseflow ixSaturation, & ! intent(inout): index of the lowest saturated layer dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors @@ -185,7 +190,12 @@ subroutine eval8summaWithPrime(& real(rkind),intent(in) :: scalarAquiferStoragePrev ! previous value of storage of water in the aquifer (m) real(rkind),intent(in) :: mLayerEnthalpyPrev(:) ! previous vector of enthalpy for snow+soil layers (J m-3) real(rkind),intent(out) :: mLayerEnthalpyTrial(:) ! trial vector of enthalpy for snow+soil layers (J m-3) - real(rkind),intent(out) :: mLayerMatricHeadPrime(:) ! deriviative value for prime total water matric potential (m s-1) + real(rkind),intent(out) :: mLayerTempPrime(:) ! derivative value for temperature of each snow and soil layer (K) + real(rkind),intent(out) :: mLayerMatricHeadPrime(:) ! derivative value for matric head of each snow and soil layer (m) + real(rkind),intent(out) :: mLayerMatricHeadLiqPrime(:) ! derivative value for liquid water matric head of each snow and soil layer (m) + real(rkind),intent(out) :: mLayerVolFracWatPrime(:) ! derivative value for volumetric total water content of each snow and soil layer (-) + real(rkind),intent(out) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) + real(rkind),intent(out) :: scalarCanopyWatPrime ! derivative value for total water content of the vegetation canopy (kg m-2) ! input-output: baseflow integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) @@ -206,11 +216,6 @@ subroutine eval8summaWithPrime(& real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) ! derivative of state variables real(rkind) :: scalarCanairTempPrime ! derivative value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatPrime ! derivative value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempPrime ! derivative value for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! derivative value for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! derivative value for liquid water matric potential (m) real(rkind) :: scalarAquiferStoragePrime ! derivative value of storage of water in the aquifer (m) ! derivative of diagnostic variables real(rkind) :: scalarCanopyLiqPrime ! derivative value for mass of liquid water on the vegetation canopy (kg m-2) @@ -804,7 +809,12 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) - eqns_data%mLayerMatricHeadPrime, & ! intent(out): derivative value for total water matric potential (m s-1) + eqns_data%mLayerTempPrime, & ! intent(out): derivative value for temperature of each snow and soil layer (K) + eqns_data%mLayerMatricHeadPrime, & ! intent(out): derivative value for matric head of each snow and soil layer (m) + eqns_data%mLayerMatricHeadLiqPrime,& ! intent(out): derivative value for liquid water matric head of each snow and soil layer (m) + eqns_data%mLayerVolFracWatPrime, & ! intent(out): derivative value for volumetric total water content of each snow and soil layer (-) + eqns_data%scalarCanopyTempPrime, & ! intent(out): derivative value for temperature of the vegetation canopy (K) + eqns_data%scalarCanopyWatPrime, & ! intent(out): derivative value for total water content of the vegetation canopy (kg m-2) ! input-output: baseflow eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index c53b20988..b45769402 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -288,7 +288,10 @@ subroutine summaSolve4ida( & allocate( eqns_data%mLayerVolFracLiqPrev(nLayers) ) allocate( eqns_data%mLayerEnthalpyTrial(nLayers) ) allocate( eqns_data%mLayerEnthalpyPrev(nLayers) ) + allocate( eqns_data%mLayerTempPrime(nLayers) ) allocate( eqns_data%mLayerMatricHeadPrime(nSoil) ) + allocate( eqns_data%mLayerMatricHeadLiqPrime(nSoil) ) + allocate( eqns_data%mLayerVolFracWatPrime(nLayers) ) allocate( mLayerMatricHeadPrimePrev(nSoil) ) allocate( dCompress_dPsiPrev(nSoil) ) allocate( eqns_data%fluxVec(nState) ) @@ -553,7 +556,11 @@ subroutine summaSolve4ida( & deallocate( eqns_data%mLayerVolFracLiqPrev ) deallocate( eqns_data%mLayerEnthalpyTrial ) deallocate( eqns_data%mLayerEnthalpyPrev ) - deallocate( eqns_data%mLayerMatricHeadPrime) + deallocate( eqns_data%mLayerTempPrime ) + deallocate( eqns_data%mLayerMatricHeadPrime ) + deallocate( eqns_data%mLayerMatricHeadLiqPrime ) + deallocate( eqns_data%mLayerVolFracWatPrime ) + deallocate( mLayerMatricHeadPrimePrev ) deallocate( dCompress_dPsiPrev ) deallocate( eqns_data%fluxVec ) From 79b66880107dadd2ec0ed377e16402c969e3248b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 27 Jun 2023 23:55:50 +0900 Subject: [PATCH 0780/1472] some issues with not passing correct subtimestep when doing inner step more --- build/source/engine/computJacob.f90 | 2 +- build/source/engine/eval8summa.f90 | 1 - build/source/engine/eval8summaWithPrime.f90 | 1 - build/source/engine/summaSolve4ida.f90 | 31 ++++++++++----------- build/source/engine/summaSolve4kinsol.f90 | 4 +-- build/source/engine/systemSolv.f90 | 11 ++++---- 6 files changed, 23 insertions(+), 27 deletions(-) diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 05f4d7a59..87fe4fc01 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -1055,7 +1055,7 @@ integer(c_int) function computJacob4kinsol(sunvec_y, sunvec_r, sunmat_J, & ! or in the call to eval8summa in the previous iteration call computJacob(& ! input: model control - eqns_data%dt, & ! intent(in): data step + eqns_data%dt_cur, & ! intent(in): length of the time step (seconds) eqns_data%nSnow, & ! intent(in): number of snow layers eqns_data%nSoil, & ! intent(in): number of soil layers eqns_data%nLayers, & ! intent(in): number of layers diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 2389c9fd7..28c0ac10d 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -744,7 +744,6 @@ integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & rVec, & ! intent(out): residual vector fNew, & ! intent(out): new function evaluation eqns_data%err,eqns_data%message) ! intent(out): error control - if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 01f3ec2a1..e762c15dc 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -824,7 +824,6 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation rVec, & ! intent(out): residual vector eqns_data%err,eqns_data%message) ! intent(out): error control - if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index b45769402..a29c5c3e5 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -82,6 +82,7 @@ module summaSolve4ida_module ! * public subroutine summaSolve4ida: solve F(y,y') = 0 by IDA (y is the state vector) ! ------------------ subroutine summaSolve4ida( & + dt_cur, & ! intent(in): current stepsize dt, & ! intent(in): data time step atol, & ! intent(in): absolute tolerance rtol, & ! intent(in): relative tolerance @@ -110,14 +111,13 @@ subroutine summaSolve4ida( & indx_data, & ! intent(inout): index data diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU - flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a dt + flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a dt_cur deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables mLayerCmpress_sum, & ! intent(inout): sum of compression of the soil matrix ! output - ixSaturation, & ! intent(inout) index of the lowest saturated layer (NOTE: only computed on the first iteration) + ixSaturation, & ! intent(inout) index of the lowest saturated layer (NOTE: only computed on the first iteration) idaSucceeds, & ! intent(out): flag to indicate if IDA successfully solved the problem in current data step - tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt - dt_out, & ! intent(out): time step sum for entire data window at termination of sundials + tooMuchMelt, & ! intent(inout): lag to denote that there was too much melt stateVec, & ! intent(out): model state vector stateVecPrime, & ! intent(out): derivative of model state vector err,message) ! intent(out): error control @@ -149,6 +149,7 @@ subroutine summaSolve4ida( & ! calling variables ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control + real(rkind),intent(in) :: dt_cur ! current stepsize real(qp),intent(in) :: dt ! data time step real(qp),intent(inout) :: atol(:) ! vector of absolute tolerances real(qp),intent(inout) :: rtol(:) ! vector of relative tolerances @@ -177,7 +178,7 @@ subroutine summaSolve4ida( & type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt + type(var_dlength),intent(inout) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt_cur type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables real(rkind),intent(inout) :: mLayerCmpress_sum(:) ! sum of soil compress ! output: state vectors @@ -186,7 +187,6 @@ subroutine summaSolve4ida( & real(rkind),intent(inout) :: stateVecPrime(:) ! model state vector (y') logical(lgt),intent(out) :: idaSucceeds ! flag to indicate if IDA is successful logical(lgt),intent(inout) :: tooMuchMelt ! flag to denote that there was too much melt - real(qp),intent(out) :: dt_out ! time step sum for entire data window at termination of IDA loops ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -404,11 +404,11 @@ subroutine summaSolve4ida( & endif ! Enforce the solver to stop at end of the time step - retval = FIDASetStopTime(ida_mem, dt) + retval = FIDASetStopTime(ida_mem, dt_cur) if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetStopTime'; return; endif ! Set solver parameters at end of setup - call setSolverParams(dt, nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)), ida_mem, retval) + call setSolverParams(dt_cur, nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)), ida_mem, retval) if (retval /= 0) then; err=20; message='summaSolve4ida: error in setSolverParams'; return; endif ! Disable error messages and warnings @@ -421,7 +421,7 @@ subroutine summaSolve4ida( & tinystep = .false. tret(1) = t0 ! initial time tretPrev = tret(1) - do while(tret(1) < dt) + do while(tret(1) < dt_cur) ! call this at beginning of step to reduce root bouncing (only looking in one direction) if(detect_events .and. .not.tinystep)then @@ -431,10 +431,10 @@ subroutine summaSolve4ida( & endif eqns_data%firstFluxCall = .false. ! already called for initial - eqns_data%firstSplitOper = .true. ! always true at start of dt since no splitting + eqns_data%firstSplitOper = .true. ! always true at start of dt_cur since no splitting ! call IDASolve, advance solver just one internal step - retvalr = FIDASolve(ida_mem, dt, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) + retvalr = FIDASolve(ida_mem, dt_cur, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) if( retvalr < 0 )then idaSucceeds = .false. call getErrMessage(retvalr,cmessage) @@ -519,7 +519,7 @@ subroutine summaSolve4ida( & endif endif - enddo ! while loop on one_step mode until time dt + enddo ! while loop on one_step mode until time dt_cur !****************************** End of Main Solver *************************************** err = eqns_data%err @@ -535,7 +535,6 @@ subroutine summaSolve4ida( & flux_data = eqns_data%flux_data deriv_data = eqns_data%deriv_data ixSaturation = eqns_data%ixSaturation - dt_out = tret(1) ! should be dt, probably do not need to keep this indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = eqns_data%indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) !only number of Flux Calculations changes endif @@ -614,7 +613,7 @@ end subroutine setInitialCondition ! ---------------------------------------------------------------- ! setSolverParams: private routine to set parameters in IDA solver ! ---------------------------------------------------------------- -subroutine setSolverParams(dt,nonlin_iter,ida_mem,retval) +subroutine setSolverParams(dt_cur,nonlin_iter,ida_mem,retval) !======= Inclusions =========== USE, intrinsic :: iso_c_binding @@ -624,7 +623,7 @@ subroutine setSolverParams(dt,nonlin_iter,ida_mem,retval) implicit none ! calling variables - real(rkind),intent(in) :: dt ! time step + real(rkind),intent(in) :: dt_cur ! current whole time step integer,intent(in) :: nonlin_iter ! maximum number of nonlinear iterations, default = 4, set in parameters type(c_ptr),intent(inout) :: ida_mem ! IDA memory integer(i4b),intent(out) :: retval ! return value @@ -663,7 +662,7 @@ subroutine setSolverParams(dt,nonlin_iter,ida_mem,retval) if (retval /= 0) return ! Set maximum stepsize - h_max = dt + h_max = dt_cur retval = FIDASetMaxStep(ida_mem, h_max) if (retval /= 0) return diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index d4e8f9d1d..180a74505 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -216,7 +216,7 @@ subroutine summaSolve4kinsol(& eqns_data%nState = nState eqns_data%ixMatrix = ixMatrix eqns_data%firstFluxCall = .false. ! already called for initial - eqns_data%firstSplitOper = .false. ! already called for initial and false inside solver ??? + eqns_data%firstSplitOper = .false. ! already called for initial and false inside solver eqns_data%firstSubStep = firstSubStep eqns_data%computeVegFlux = computeVegFlux eqns_data%scalarSolution = scalarSolution @@ -349,7 +349,7 @@ subroutine summaSolve4kinsol(& err = eqns_data%err message = eqns_data%message - if( .not. feasible) then + if( .not. feasible)then kinsolSucceeds = .false. message=trim(message)//trim(cmessage)//'non-feasible' endif diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 27d8864b4..d85954972 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -204,7 +204,6 @@ subroutine systemSolv(& ! --------------------------------------------------------------------------------------- ! * general local variables ! --------------------------------------------------------------------------------------- - real(qp) :: dt_out ! time step sum for data window at termination of sundials character(LEN=256) :: cmessage ! error message of downwind routine integer(i4b) :: iter ! iteration index integer(i4b) :: iVar ! index of variable @@ -218,7 +217,7 @@ subroutine systemSolv(& ! ------------------------------------------------------------------------------------------------------ ! * model solver ! ------------------------------------------------------------------------------------------------------ - logical(lgt),parameter :: forceFullMatrix=.false. ! flag to force the use of the full Jacobian matrix + logical(lgt),parameter :: forceFullMatrix=.true. ! flag to force the use of the full Jacobian matrix integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) type(var_dlength) :: flux_init ! model fluxes at the start of the time step real(rkind),allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! NOTE: allocatable, since not always needed @@ -233,7 +232,7 @@ subroutine systemSolv(& ! ida variables real(rkind) :: atol(nState) ! absolute tolerance ida real(rkind) :: rtol(nState) ! relative tolerance ida - type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt_out (=dt) + type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt_cur real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix ! kinsol and numrec variables real(rkind) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) @@ -525,6 +524,7 @@ subroutine systemSolv(& !--------------------------- ! iterations and updates to trial state vector, fluxes, and derivatives are done inside IDA solver call summaSolve4ida(& + dt_cur, & ! intent(in): current stepsize dt, & ! intent(in): entire time step for drainage pond rate atol, & ! intent(in): absolute tolerance rtol, & ! intent(in): relative tolerance @@ -560,7 +560,6 @@ subroutine systemSolv(& ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) sunSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt - dt_out, & ! intent(out): time step sum for entire data window at termination of sundials stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step err,cmessage) ! intent(out): error control @@ -580,7 +579,7 @@ subroutine systemSolv(& ! compute average flux do iVar=1,size(flux_meta) - flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / dt_out + flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / dt_cur end do ! compute the total change in storage associated with compression of the soil matrix (kg m-2) @@ -590,7 +589,7 @@ subroutine systemSolv(& mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! change in storage associated with compression of the soil matrix (-) scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) & ! total change in storage associated with compression of the soil matrix (kg m-2 s-1) ) - mLayerCompress = mLayerCmpress_sum / dt_out + mLayerCompress = mLayerCmpress_sum / dt_cur scalarSoilCompress = sum(mLayerCompress(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water end associate soilVars From e4aecbe60cfa21b00b9a9bad30d22d344b5c768d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 28 Jun 2023 01:28:22 +0900 Subject: [PATCH 0781/1472] forcefullMatrix should be false --- build/source/engine/summaSolve4kinsol.f90 | 2 +- build/source/engine/systemSolv.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index 180a74505..a6115df80 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -435,7 +435,7 @@ subroutine setSolverParams(nonlin_iter,kinsol_mem,retval) integer(c_long),parameter :: mset = 1 ! maximum number of times the solver is called without Jacobian update, pass 0 to give default of 10 times integer(c_long),parameter :: msubset = 1 ! maximum number of nonlinear iterations between checks by the residual monitoring algorithm, default=5 integer(c_long),parameter :: maa = 0 ! maximum number of prior residuals to use acceleration, default = 0 - integer(c_long),parameter :: beta_fail = 50 ! maximum number of beta condition failures, default = 10 + integer(c_long),parameter :: beta_fail = 10 ! maximum number of beta condition failures, default = 10 real(qp),parameter :: fnormtol = 0.0 ! stopping tolerance on the scaled maximum norm of the system function, pass 0 to give default of unit_roundoff**(1/3) real(qp),parameter :: scsteptol = 0.0 ! stopping tolerance on the minimum scaled step length, pass 0 to give default of unit_roundoff**(2/3) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index d85954972..06b848f96 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -217,7 +217,7 @@ subroutine systemSolv(& ! ------------------------------------------------------------------------------------------------------ ! * model solver ! ------------------------------------------------------------------------------------------------------ - logical(lgt),parameter :: forceFullMatrix=.true. ! flag to force the use of the full Jacobian matrix + logical(lgt),parameter :: forceFullMatrix=.false. ! flag to force the use of the full Jacobian matrix integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) type(var_dlength) :: flux_init ! model fluxes at the start of the time step real(rkind),allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! NOTE: allocatable, since not always needed From 9e5b703eac92942f5ced2878980760d50a30b727 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 28 Jun 2023 12:38:23 +0900 Subject: [PATCH 0782/1472] getting rid of numerical derivative calculations since wrong anyhow -- now use decision to choose if SUNDIALS makes finite difference Jacobian or analytical --- build/source/engine/coupled_em.f90 | 1 - build/source/engine/soilLiqFlx.f90 | 575 +++------- build/source/engine/ssdNrgFlux.f90 | 31 +- build/source/engine/summaSolve4ida.f90 | 19 +- build/source/engine/summaSolve4kinsol.f90 | 24 +- build/source/engine/summaSolve4numrec.f90 | 20 +- build/source/engine/vegNrgFlux.f90 | 1270 +++++++-------------- 7 files changed, 629 insertions(+), 1311 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 5f30443ff..e4823750d 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -491,7 +491,6 @@ subroutine coupled_em(& call wettedFrac(& ! input .false., & ! flag to denote if derivatives are required - .false., & ! flag to denote if derivatives are calculated numerically (prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) < Tfreeze), & ! flag to denote if the canopy is frozen varNotUsed1, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) varNotUsed2, & ! fraction of liquid water on the canopy diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index adae6b002..f2703e925 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -160,10 +160,10 @@ subroutine soilLiqFlx(& ! ------------------------------------------------------------------------------------------------------------------------------------------------- implicit none ! input: model control - integer(i4b),intent(in) :: nSoil ! number of soil layers - logical(lgt),intent(in) :: firstSplitOper ! flag to compute infiltration - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - logical(lgt),intent(in) :: deriv_desired ! flag indicating if derivatives are desired + integer(i4b),intent(in) :: nSoil ! number of soil layers + logical(lgt),intent(in) :: firstSplitOper ! flag to compute infiltration + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + logical(lgt),intent(in) :: deriv_desired ! flag indicating if derivatives are desired ! input: trial model state variables real(rkind),intent(in) :: mLayerTempTrial(:) ! temperature in each layer at the current iteration (m) real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! matric head in each layer at the current iteration (m) @@ -224,48 +224,26 @@ subroutine soilLiqFlx(& ! local variables: general character(LEN=256) :: cmessage ! error message of downwind routine integer(i4b) :: ibeg,iend ! start and end indices of the soil layers in concatanated snow-soil vector - logical(lgt) :: desireAnal ! flag to identify if analytical derivatives are desired integer(i4b) :: iLayer,iSoil ! index of soil layer integer(i4b) :: ixLayerDesired(1) ! layer desired (scalar solution) integer(i4b) :: ixTop ! top layer in subroutine call integer(i4b) :: ixBot ! bottom layer in subroutine call - ! additional variables to compute numerical derivatives - integer(i4b) :: nFlux ! number of flux calculations required (>1 = numerical derivatives with one-sided finite differences) - integer(i4b) :: itry ! index of different flux calculations - integer(i4b),parameter :: unperturbed=0 ! named variable to identify the case of unperturbed state variables - integer(i4b),parameter :: perturbState=1 ! named variable to identify the case where we perturb the state in the current layer - integer(i4b),parameter :: perturbStateAbove=2 ! named variable to identify the case where we perturb the state layer above - integer(i4b),parameter :: perturbStateBelow=3 ! named variable to identify the case where we perturb the state layer below - integer(i4b) :: ixPerturb ! index of element in 2-element vector to perturb - integer(i4b) :: ixOriginal ! index of perturbed element in the original vector - real(rkind) :: scalarVolFracLiqTrial ! trial value of volumetric liquid water content (-) - real(rkind) :: scalarMatricHeadLiqTrial ! trial value of liquid matric head (m) - real(rkind) :: scalarHydCondTrial ! trial value of hydraulic conductivity (m s-1) - real(rkind) :: scalarHydCondMicro ! trial value of hydraulic conductivity of micropores (m s-1) - real(rkind) :: scalarHydCondMacro ! trial value of hydraulic conductivity of macropores (m s-1) - real(rkind) :: scalarFlux ! vertical flux (m s-1) - real(rkind) :: scalarFlux_dStateAbove ! vertical flux with perturbation to the state above (m s-1) - real(rkind) :: scalarFlux_dStateBelow ! vertical flux with perturbation to the state below (m s-1) ! transpiration sink term - real(rkind),dimension(nSoil) :: mLayerTranspireFrac ! fraction of transpiration allocated to each soil layer (-) + real(rkind),dimension(nSoil) :: mLayerTranspireFrac ! fraction of transpiration allocated to each soil layer (-) ! diagnostic variables - real(rkind),dimension(nSoil) :: iceImpedeFac ! ice impedence factor at layer mid-points (-) - real(rkind),dimension(nSoil) :: mLayerDiffuse ! diffusivity at layer mid-point (m2 s-1) - real(rkind),dimension(nSoil) :: dHydCond_dVolLiq ! derivative in hydraulic conductivity w.r.t volumetric liquid water content (m s-1) - real(rkind),dimension(nSoil) :: dDiffuse_dVolLiq ! derivative in hydraulic diffusivity w.r.t volumetric liquid water content (m2 s-1) - real(rkind),dimension(nSoil) :: dHydCond_dTemp ! derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) - real(rkind),dimension(0:nSoil) :: iLayerHydCond ! hydraulic conductivity at layer interface (m s-1) - real(rkind),dimension(0:nSoil) :: iLayerDiffuse ! diffusivity at layer interface (m2 s-1) + real(rkind),dimension(nSoil) :: iceImpedeFac ! ice impedence factor at layer mid-points (-) + real(rkind),dimension(nSoil) :: mLayerDiffuse ! diffusivity at layer mid-point (m2 s-1) + real(rkind),dimension(nSoil) :: dHydCond_dVolLiq ! derivative in hydraulic conductivity w.r.t volumetric liquid water content (m s-1) + real(rkind),dimension(nSoil) :: dDiffuse_dVolLiq ! derivative in hydraulic diffusivity w.r.t volumetric liquid water content (m2 s-1) + real(rkind),dimension(nSoil) :: dHydCond_dTemp ! derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) + real(rkind),dimension(0:nSoil) :: iLayerHydCond ! hydraulic conductivity at layer interface (m s-1) + real(rkind),dimension(0:nSoil) :: iLayerDiffuse ! diffusivity at layer interface (m2 s-1) ! compute surface flux - integer(i4b) :: nRoots ! number of soil layers with roots - integer(i4b) :: ixIce ! index of the lowest soil layer that contains ice - real(rkind),dimension(0:nSoil) :: iLayerHeight ! height of the layer interfaces (m) + integer(i4b) :: nRoots ! number of soil layers with roots + integer(i4b) :: ixIce ! index of the lowest soil layer that contains ice + real(rkind),dimension(0:nSoil) :: iLayerHeight ! height of the layer interfaces (m) ! compute fluxes and derivatives at layer interfaces - real(rkind),dimension(2) :: vectorVolFracLiqTrial ! trial value of volumetric liquid water content (-) - real(rkind),dimension(2) :: vectorMatricHeadLiqTrial ! trial value of liquid matric head (m) - real(rkind),dimension(2) :: vectorHydCondTrial ! trial value of hydraulic conductivity (m s-1) - real(rkind),dimension(2) :: vectorDiffuseTrial ! trial value of hydraulic diffusivity (m2 s-1) - real(rkind) :: scalardPsi_dTheta ! derivative in soil water characteristix, used for perturbations when computing numerical derivatives + real(rkind) :: scalardPsi_dTheta ! derivative in soil water characteristix, used for perturbations when computing numerical derivatives ! ------------------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='soilLiqFlx/' @@ -281,7 +259,6 @@ subroutine soilLiqFlx(& ! make association between local variables and the information in the data structures associate(& ! input: model control - ixDerivMethod => model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): index of the method used to calculate flux derivatives ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision, & ! intent(in): index of the form of Richards' equation ixBcUpperSoilHydrology => model_decisions(iLookDECISIONS%bcUpprSoiH)%iDecision, & ! intent(in): index of the upper boundary conditions for soil hydrology ixBcLowerSoilHydrology => model_decisions(iLookDECISIONS%bcLowrSoiH)%iDecision, & ! intent(in): index of the lower boundary conditions for soil hydrology @@ -328,26 +305,6 @@ subroutine soilLiqFlx(& ! preliminaries ! ------------------------------------------------------------------------------------------------------------------------------------------------- - ! numerical derivatives are not implemented yet - if(ixDerivMethod==numerical)then - message=trim(message)//'numerical derivates do not account for the cross derivatives between hydrology and thermodynamics' - err=20; return - end if - - ! check the need to compute analytical derivatives - if(deriv_desired .and. ixDerivMethod==analytical)then - desireAnal = .true. - else - desireAnal = .false. - end if - - ! check the need to compute numerical derivatives - if(deriv_desired .and. ixDerivMethod==numerical)then - nFlux=3 ! compute the derivatives using one-sided finite differences - else - nFlux=0 ! compute analytical derivatives - end if - ! get the indices for the soil layers if(scalarSolution)then ixLayerDesired = pack(ixMatricHead, ixSoilOnlyHyd/=integerMissing) @@ -371,6 +328,7 @@ subroutine soilLiqFlx(& do iLayer=1,nSoil ! (loop through soil layers) if(mLayerVolFracIceTrial(iLayer) > verySmall) ixIce = iLayer end do + ! ------------------------------------------------------------------------------------------------------------------------------------------------- ! compute the transpiration sink term ! ------------------------------------------------------------------------------------------------------------------------------------------------- @@ -415,10 +373,9 @@ subroutine soilLiqFlx(& ! compute diagnostic variables at the nodes throughout the soil profile ! ------------------------------------------------------------------------------------------------------------------------------------------------- do iSoil=ixTop,min(ixBot+1,nSoil) ! (loop through soil layers) - call diagv_node(& ! input: model control - desireAnal, & ! intent(in): flag indicating if derivatives are desired + deriv_desired, & ! intent(in): flag indicating if derivatives are desired ixRichards, & ! intent(in): index defining the option for Richards' equation (moisture or mixdform) ! input: state variables mLayerTempTrial(iSoil), & ! intent(in): temperature (K) @@ -455,264 +412,125 @@ subroutine soilLiqFlx(& ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - end do ! (looping through soil layers) ! ------------------------------------------------------------------------------------------------------------------------------------------------- ! compute infiltraton at the surface and its derivative w.r.t. mass in the upper soil layer ! ------------------------------------------------------------------------------------------------------------------------------------------------- - + ! set derivative w.r.t. state above to zero (does not exist) dq_dHydStateAbove(0) = 0._rkind dq_dNrgStateAbove(0) = 0._rkind - ! either one or multiple flux calls, depending on if using analytical or numerical derivatives - do itry=nFlux,0,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) - - ! ===== - ! get input state variables... - ! ============================ - ! identify the type of perturbation - ! Currently we are ignoring the perturbations in the non-surface layers and with temperature - select case(itry) - - ! skip undesired perturbations - case(perturbStateAbove); cycle ! cannot perturb state above (does not exist) -- so keep cycling - case(perturbState); cycle ! perturbing the layer below the flux at the top interface - - ! un-perturbed case - case(unperturbed) - scalarVolFracLiqTrial = mLayerVolFracLiqTrial(1) - scalarMatricHeadLiqTrial = mLayerMatricHeadLiqTrial(1) - - ! perturb soil state (one-sided finite differences) - case(perturbStateBelow) - ! (perturbation depends on the form of Richards' equation) - select case(ixRichards) - case(moisture) - scalarVolFracLiqTrial = mLayerVolFracLiqTrial(1) + dx - scalarMatricHeadLiqTrial = mLayerMatricHeadLiqTrial(1) - case(mixdform) - scalarVolFracLiqTrial = mLayerVolFracLiqTrial(1) - scalarMatricHeadLiqTrial = mLayerMatricHeadLiqTrial(1) + dx - case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return - end select ! (form of Richards' equation - ! check for an unknown perturbation - case default; err=10; message=trim(message)//"unknown perturbation"; return - - end select ! (type of perturbation) - - ! ===== - ! compute surface flux and its derivative... - ! ========================================== - - call surfaceFlx(& - ! input: model control - firstSplitOper, & ! intent(in): flag indicating if desire to compute infiltration - desireAnal, & ! intent(in): flag indicating if derivatives are desired - ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) - ixBcUpperSoilHydrology, & ! intent(in): index defining the type of boundary conditions (neumann or diriclet) - nRoots, & ! intent(in): number of layers that contain roots - ixIce, & ! intent(in): index of lowest ice layer - nSoil, & ! intent(in): number of soil layers - ! input: state variables - mLayerTempTrial, & ! intent(in): temperature (K) - scalarMatricHeadLiqTrial, & ! intent(in): liquid matric head in the upper-most soil layer (m) - mLayerMatricHeadTrial, & ! intent(in): matric head in each soil layer (m) - scalarVolFracLiqTrial, & ! intent(in): volumetric liquid water content the upper-most soil layer (-) - mLayerVolFracLiqTrial, & ! intent(in): volumetric liquid water content in each soil layer (-) - mLayerVolFracIceTrial, & ! intent(in): volumetric ice content in each soil layer (-) - ! input: pre-computed deriavatives - mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) - mLayerdPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) - above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water - above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature - above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) - ! input: depth of upper-most soil layer (m) - mLayerDepth, & ! intent(in): depth of each soil layer (m) - iLayerHeight, & ! intent(in): height at the interface of each layer (m) - ! input: boundary conditions - upperBoundHead, & ! intent(in): upper boundary condition (m) - upperBoundTheta, & ! intent(in): upper boundary condition (-) - ! input: flux at the upper boundary - scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) - ! input: transmittance - iLayerSatHydCond(0), & ! intent(in): saturated hydraulic conductivity at the surface (m s-1) - dHydCond_dTemp(1), & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) - iceImpedeFac(1), & ! intent(in): ice impedence factor in the upper-most soil layer (-) - ! input: soil parameters - vGn_alpha(1), & ! intent(in): van Genutchen "alpha" parameter (m-1) - vGn_n(1), & ! intent(in): van Genutchen "n" parameter (-) - vGn_m(1), & ! intent(in): van Genutchen "m" parameter (-) - theta_sat(1), & ! intent(in): soil porosity (-) - theta_res(1), & ! intent(in): soil residual volumetric water content (-) - qSurfScale, & ! intent(in): scaling factor in the surface runoff parameterization (-) - zScale_TOPMODEL, & ! intent(in): scaling factor used to describe decrease in hydraulic conductivity with depth (m) - rootingDepth, & ! intent(in): rooting depth (m) - wettingFrontSuction, & ! intent(in): Green-Ampt wetting front suction (m) - soilIceScale, & ! intent(in): soil ice scaling factor in Gamma distribution used to define frozen area (m) - soilIceCV, & ! intent(in): soil ice CV in Gamma distribution used to define frozen area (-) - ! input-output: hydraulic conductivity and diffusivity at the surface - iLayerHydCond(0), & ! intent(inout): hydraulic conductivity at the surface (m s-1) - iLayerDiffuse(0), & ! intent(inout): hydraulic diffusivity at the surface (m2 s-1) - ! input-output: fluxes at layer interfaces and surface runoff - xMaxInfilRate, & ! intent(inout): maximum infiltration rate (m s-1) - scalarInfilArea, & ! intent(inout): fraction of unfrozen area where water can infiltrate (-) - scalarFrozenArea, & ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) - scalarSurfaceRunoff, & ! intent(out): surface runoff (m s-1) - scalarSurfaceInfiltration, & ! intent(out): surface infiltration (m s-1) - ! input-output: deriavtives in surface infiltration w.r.t. volumetric liquid water (m s-1) and matric head (s-1) in the upper-most soil layer - dq_dHydStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) - dq_dNrgStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - ! include base soil evaporation as the upper boundary flux - iLayerLiqFluxSoil(0) = scalarGroundEvaporation/iden_water + scalarSurfaceInfiltration - - ! get copies of surface flux to compute numerical derivatives - if(deriv_desired .and. ixDerivMethod==numerical)then - select case(itry) - case(unperturbed); scalarFlux = iLayerLiqFluxSoil(0) - case(perturbStateBelow); scalarFlux_dStateBelow = iLayerLiqFluxSoil(0) - case default; err=10; message=trim(message)//"unknown perturbation"; return - end select - end if - end do ! (looping through different flux calculations -- one or multiple calls depending if desire for numerical or analytical derivatives) + ! compute surface flux and its derivative... + call surfaceFlx(& + ! input: model control + firstSplitOper, & ! intent(in): flag indicating if desire to compute infiltration + deriv_desired, & ! intent(in): flag indicating if derivatives are desired + ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) + ixBcUpperSoilHydrology, & ! intent(in): index defining the type of boundary conditions (neumann or diriclet) + nRoots, & ! intent(in): number of layers that contain roots + ixIce, & ! intent(in): index of lowest ice layer + nSoil, & ! intent(in): number of soil layers + ! input: state variables + mLayerTempTrial, & ! intent(in): temperature (K) + mLayerMatricHeadLiqTrial(1), & ! intent(in): liquid matric head in the upper-most soil layer (m) + mLayerMatricHeadTrial, & ! intent(in): matric head in each soil layer (m) + mLayerVolFracLiqTrial(1), & ! intent(in): volumetric liquid water content the upper-most soil layer (-) + mLayerVolFracLiqTrial, & ! intent(in): volumetric liquid water content in each soil layer (-) + mLayerVolFracIceTrial, & ! intent(in): volumetric ice content in each soil layer (-) + ! input: pre-computed deriavatives + mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) + mLayerdPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) + above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) + ! input: depth of upper-most soil layer (m) + mLayerDepth, & ! intent(in): depth of each soil layer (m) + iLayerHeight, & ! intent(in): height at the interface of each layer (m) + ! input: boundary conditions + upperBoundHead, & ! intent(in): upper boundary condition (m) + upperBoundTheta, & ! intent(in): upper boundary condition (-) + ! input: flux at the upper boundary + scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) + ! input: transmittance + iLayerSatHydCond(0), & ! intent(in): saturated hydraulic conductivity at the surface (m s-1) + dHydCond_dTemp(1), & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) + iceImpedeFac(1), & ! intent(in): ice impedence factor in the upper-most soil layer (-) + ! input: soil parameters + vGn_alpha(1), & ! intent(in): van Genutchen "alpha" parameter (m-1) + vGn_n(1), & ! intent(in): van Genutchen "n" parameter (-) + vGn_m(1), & ! intent(in): van Genutchen "m" parameter (-) + theta_sat(1), & ! intent(in): soil porosity (-) + theta_res(1), & ! intent(in): soil residual volumetric water content (-) + qSurfScale, & ! intent(in): scaling factor in the surface runoff parameterization (-) + zScale_TOPMODEL, & ! intent(in): scaling factor used to describe decrease in hydraulic conductivity with depth (m) + rootingDepth, & ! intent(in): rooting depth (m) + wettingFrontSuction, & ! intent(in): Green-Ampt wetting front suction (m) + soilIceScale, & ! intent(in): soil ice scaling factor in Gamma distribution used to define frozen area (m) + soilIceCV, & ! intent(in): soil ice CV in Gamma distribution used to define frozen area (-) + ! input-output: hydraulic conductivity and diffusivity at the surface + iLayerHydCond(0), & ! intent(inout): hydraulic conductivity at the surface (m s-1) + iLayerDiffuse(0), & ! intent(inout): hydraulic diffusivity at the surface (m2 s-1) + ! input-output: fluxes at layer interfaces and surface runoff + xMaxInfilRate, & ! intent(inout): maximum infiltration rate (m s-1) + scalarInfilArea, & ! intent(inout): fraction of unfrozen area where water can infiltrate (-) + scalarFrozenArea, & ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) + scalarSurfaceRunoff, & ! intent(out): surface runoff (m s-1) + scalarSurfaceInfiltration, & ! intent(out): surface infiltration (m s-1) + ! input-output: deriavtives in surface infiltration w.r.t. volumetric liquid water (m s-1) and matric head (s-1) in the upper-most soil layer + dq_dHydStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) + dq_dNrgStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + + ! include base soil evaporation as the upper boundary flux + iLayerLiqFluxSoil(0) = scalarGroundEvaporation/iden_water + scalarSurfaceInfiltration dq_dHydStateBelow(0) = 0._rkind ! contribution will be in dq_dHydStateLayerSurfVec(1) dq_dNrgStateBelow(0) = 0._rkind ! contribution will be in dq_dNrgStateLayerSurfVec(1) - ! compute numerical derivatives - if(deriv_desired .and. ixDerivMethod==numerical)then - dq_dHydStateLayerSurfVec(1) = (scalarFlux_dStateBelow - scalarFlux)/dx ! change in surface flux w.r.t. change in the soil moisture in the top soil layer (m s-1) - dq_dHydStateLayerSurfVec(1) = 0._rkind ! Did not yet compute this perturbation - end if - ! ------------------------------------------------------------------------------------------------------------------------------------------------- ! * compute fluxes and derivatives at layer interfaces... ! ------------------------------------------------------------------------------------------------------------------------------------------------- - ! NOTE: computing flux at the bottom of the layer - - ! loop through soil layers + ! computing flux at the bottom of the layer do iLayer=ixTop,min(ixBot,nSoil-1) - - ! either one or multiple flux calls, depending on if using analytical or numerical derivatives - do itry=nFlux,0,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) - - ! ===== - ! determine layer to perturb - ! ============================ - select case(itry) - ! skip undesired perturbations - case(perturbState); cycle ! perturbing the layers above and below the flux at the interface - ! identify the index for the perturbation - case(unperturbed); ixPerturb = 0 - case(perturbStateAbove); ixPerturb = 1 - case(perturbStateBelow); ixPerturb = 2 - case default; err=10; message=trim(message)//"unknown perturbation"; return - end select ! (identifying layer to of perturbation) - ! determine the index in the original vector - ixOriginal = iLayer + (ixPerturb-1) - - ! ===== - ! get input state variables... - ! ============================ - ! start with the un-perturbed case - vectorVolFracLiqTrial(1:2) = mLayerVolFracLiqTrial(iLayer:iLayer+1) - vectorMatricHeadLiqTrial(1:2) = mLayerMatricHeadLiqTrial(iLayer:iLayer+1) - ! make appropriate perturbations - if(ixPerturb > 0)then - select case(ixRichards) - case(moisture); vectorVolFracLiqTrial(ixPerturb) = vectorVolFracLiqTrial(ixPerturb) + dx - case(mixdform); vectorMatricHeadLiqTrial(ixPerturb) = vectorMatricHeadLiqTrial(ixPerturb) + dx - case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return - end select ! (form of Richards' equation) - end if - - ! ===== - ! get hydraulic conductivty... - ! ============================ - ! start with the un-perturbed case - vectorHydCondTrial(1:2) = mLayerHydCond(iLayer:iLayer+1) - vectorDiffuseTrial(1:2) = mLayerDiffuse(iLayer:iLayer+1) - ! make appropriate perturbations - if(ixPerturb > 0)then ! only recompute these if perturbed - select case(ixRichards) - case(moisture) - scalardPsi_dTheta = dPsi_dTheta(vectorVolFracLiqTrial(ixPerturb),vGn_alpha(ixPerturb),theta_res(ixPerturb),theta_sat(ixPerturb),vGn_n(ixPerturb),vGn_m(ixPerturb)) - vectorHydCondTrial(ixPerturb) = hydCond_liq(vectorVolFracLiqTrial(ixPerturb),mLayerSatHydCond(ixOriginal),theta_res(ixPerturb),theta_sat(ixPerturb),vGn_m(ixPerturb)) * iceImpedeFac(ixOriginal) - vectorDiffuseTrial(ixPerturb) = scalardPsi_dTheta * vectorHydCondTrial(ixPerturb) - case(mixdform) - scalarVolFracLiqTrial = volFracLiq(vectorMatricHeadLiqTrial(ixPerturb),vGn_alpha(ixPerturb),theta_res(ixPerturb),theta_sat(ixPerturb),vGn_n(ixPerturb),vGn_m(ixPerturb)) - scalarHydCondMicro = hydCond_psi(vectorMatricHeadLiqTrial(ixPerturb),mLayerSatHydCond(ixOriginal),vGn_alpha(ixPerturb),vGn_n(ixPerturb),vGn_m(ixPerturb)) * iceImpedeFac(ixOriginal) - scalarHydCondMacro = hydCondMP_liq(scalarVolFracLiqTrial,theta_sat(ixPerturb),theta_mp,mpExp,mLayerSatHydCondMP(ixOriginal),mLayerSatHydCond(ixOriginal)) - vectorHydCondTrial(ixPerturb) = scalarHydCondMicro + scalarHydCondMacro - case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return - end select ! (form of Richards' equation) - endif ! (recompute if perturbed) - - ! ===== - ! compute vertical flux at layer interface and its derivative w.r.t. the state above and the state below... - ! ========================================================================================================= - - ! NOTE: computing flux at the bottom of the layer - - call iLayerFlux(& - ! input: model control - desireAnal, & ! intent(in): flag indicating if derivatives are desired - ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) - ! input: state variables (adjacent layers) - vectorMatricHeadLiqTrial, & ! intent(in): liquid matric head at the soil nodes (m) - vectorVolFracLiqTrial, & ! intent(in): volumetric liquid water content at the soil nodes (-) - ! input: model coordinate variables (adjacent layers) - mLayerHeight(iLayer:iLayer+1), & ! intent(in): height of the soil nodes (m) - ! input: temperature derivatives - dPsiLiq_dTemp(iLayer:iLayer+1), & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) - dHydCond_dTemp(iLayer:iLayer+1), & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) - ! input: transmittance (adjacent layers) - vectorHydCondTrial, & ! intent(in): hydraulic conductivity at the soil nodes (m s-1) - vectorDiffuseTrial, & ! intent(in): hydraulic diffusivity at the soil nodes (m2 s-1) - ! input: transmittance derivatives (adjacent layers) - dHydCond_dVolLiq(iLayer:iLayer+1), & ! intent(in): change in hydraulic conductivity w.r.t. change in volumetric liquid water content (m s-1) - dDiffuse_dVolLiq(iLayer:iLayer+1), & ! intent(in): change in hydraulic diffusivity w.r.t. change in volumetric liquid water content (m2 s-1) - dHydCond_dMatric(iLayer:iLayer+1), & ! intent(in): change in hydraulic conductivity w.r.t. change in matric head (s-1) - ! output: tranmsmittance at the layer interface (scalars) - iLayerHydCond(iLayer), & ! intent(out): hydraulic conductivity at the interface between layers (m s-1) - iLayerDiffuse(iLayer), & ! intent(out): hydraulic diffusivity at the interface between layers (m2 s-1) - ! output: vertical flux at the layer interface (scalars) - iLayerLiqFluxSoil(iLayer), & ! intent(out): vertical flux of liquid water at the layer interface (m s-1) - ! output: derivatives in fluxes w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below (m s-1 or s-1) - dq_dHydStateAbove(iLayer), & ! intent(out): derivatives in the flux w.r.t. matric head or volumetric lquid water in the layer above (m s-1 or s-1) - dq_dHydStateBelow(iLayer), & ! intent(out): derivatives in the flux w.r.t. matric head or volumetric lquid water in the layer below (m s-1 or s-1) - ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) - dq_dNrgStateAbove(iLayer), & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) - dq_dNrgStateBelow(iLayer), & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - ! compute total vertical flux, to compute derivatives - if(deriv_desired .and. ixDerivMethod==numerical)then - select case(itry) - case(unperturbed); scalarFlux = iLayerLiqFluxSoil(iLayer) - case(perturbStateAbove); scalarFlux_dStateAbove = iLayerLiqFluxSoil(iLayer) - case(perturbStateBelow); scalarFlux_dStateBelow = iLayerLiqFluxSoil(iLayer) - case default; err=10; message=trim(message)//"unknown perturbation"; return - end select - end if - - end do ! (looping through different flux calculations -- one or multiple calls depending if desire for numerical or analytical derivatives) - - ! compute numerical derivatives - if(deriv_desired .and. ixDerivMethod==numerical)then - dq_dHydStateAbove(iLayer) = (scalarFlux_dStateAbove - scalarFlux)/dx ! change in drainage flux w.r.t. change in the state in the layer below (m s-1 or s-1) - dq_dHydStateBelow(iLayer) = (scalarFlux_dStateBelow - scalarFlux)/dx ! change in drainage flux w.r.t. change in the state in the layer below (m s-1 or s-1) - end if - + call iLayerFlux(& + ! input: model control + deriv_desired, & ! intent(in): flag indicating if derivatives are desired + ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) + ! input: state variables (adjacent layers) + mLayerMatricHeadLiqTrial(iLayer:iLayer+1), & ! intent(in): liquid matric head at the soil nodes (m) + mLayerVolFracLiqTrial(iLayer:iLayer+1), & ! intent(in): volumetric liquid water content at the soil nodes (-) + ! input: model coordinate variables (adjacent layers) + mLayerHeight(iLayer:iLayer+1), & ! intent(in): height of the soil nodes (m) + ! input: temperature derivatives + dPsiLiq_dTemp(iLayer:iLayer+1), & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) + dHydCond_dTemp(iLayer:iLayer+1), & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) + ! input: transmittance (adjacent layers) + mLayerHydCond(iLayer:iLayer+1), & ! intent(in): hydraulic conductivity at the soil nodes (m s-1) + mLayerDiffuse(iLayer:iLayer+1), & ! intent(in): hydraulic diffusivity at the soil nodes (m2 s-1) + ! input: transmittance derivatives (adjacent layers) + dHydCond_dVolLiq(iLayer:iLayer+1), & ! intent(in): change in hydraulic conductivity w.r.t. change in volumetric liquid water content (m s-1) + dDiffuse_dVolLiq(iLayer:iLayer+1), & ! intent(in): change in hydraulic diffusivity w.r.t. change in volumetric liquid water content (m2 s-1) + dHydCond_dMatric(iLayer:iLayer+1), & ! intent(in): change in hydraulic conductivity w.r.t. change in matric head (s-1) + ! output: tranmsmittance at the layer interface (scalars) + iLayerHydCond(iLayer), & ! intent(out): hydraulic conductivity at the interface between layers (m s-1) + iLayerDiffuse(iLayer), & ! intent(out): hydraulic diffusivity at the interface between layers (m2 s-1) + ! output: vertical flux at the layer interface (scalars) + iLayerLiqFluxSoil(iLayer), & ! intent(out): vertical flux of liquid water at the layer interface (m s-1) + ! output: derivatives in fluxes w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below (m s-1 or s-1) + dq_dHydStateAbove(iLayer), & ! intent(out): derivatives in the flux w.r.t. matric head or volumetric lquid water in the layer above (m s-1 or s-1) + dq_dHydStateBelow(iLayer), & ! intent(out): derivatives in the flux w.r.t. matric head or volumetric lquid water in the layer below (m s-1 or s-1) + ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) + dq_dNrgStateAbove(iLayer), & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) + dq_dNrgStateBelow(iLayer), & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if end do ! (looping through soil layers) ! ------------------------------------------------------------------------------------------------------------------------------------------------- @@ -721,116 +539,51 @@ subroutine soilLiqFlx(& ! define the need to compute drainage if( .not. (scalarSolution .and. ixTop model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): method used to calculate flux derivatives ix_bcUpprTdyn => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision, & ! intent(in): method used to calculate the upper boundary condition for thermodynamics ix_bcLowrTdyn => model_decisions(iLookDECISIONS%bcLowrTdyn)%iDecision, & ! intent(in): method used to calculate the lower boundary condition for thermodynamics ! input: coordinate variables @@ -243,13 +241,7 @@ subroutine ssdNrgFlux(& case(prescribedTemp) dz = mLayerHeight(1)*0.5_rkind dFlux_dWatBelow(0) = -dThermalC_dWatBelow(0) * ( mLayerTempTrial(1) - upperBoundTemp )/dz - if(ixDerivMethod==analytical)then ! ** analytical derivatives for temperature - dFlux_dTempBelow(0) = -dThermalC_dTempBelow(0) * ( mLayerTempTrial(1) - upperBoundTemp )/dz - iLayerThermalC(0)/dz - else ! ** numerical derivatives for constant step iLayerThermalC - flux0 = -iLayerThermalC(0) * ( mLayerTempTrial(1) - upperBoundTemp ) / dz - flux2 = -iLayerThermalC(0) * ((mLayerTempTrial(1)+dx) - upperBoundTemp ) / dz - dFlux_dTempBelow(0) = (flux2 - flux0)/dx - end if + dFlux_dTempBelow(0) = -dThermalC_dTempBelow(0) * ( mLayerTempTrial(1) - upperBoundTemp )/dz - iLayerThermalC(0)/dz ! * zero flux at the upper boundary case(zeroFlux) @@ -277,13 +269,7 @@ subroutine ssdNrgFlux(& case(prescribedTemp) dz = mLayerDepth(iLayer)*0.5_rkind dFlux_dWatAbove(iLayer) = -dThermalC_dWatAbove(iLayer) * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz - if(ixDerivMethod==analytical)then ! ** analytical derivatives - dFlux_dTempAbove(iLayer) = -dThermalC_dTempAbove(iLayer) * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + iLayerThermalC(iLayer)/dz - else ! ** numerical derivatives for constant step iLayerThermalC - flux0 = -iLayerThermalC(iLayer)*(lowerBoundTemp - (mLayerTempTrial(iLayer) ))/dz - flux1 = -iLayerThermalC(iLayer)*(lowerBoundTemp - (mLayerTempTrial(iLayer)+dx))/dz - dFlux_dTempAbove(iLayer) = (flux1 - flux0)/dx - end if + dFlux_dTempAbove(iLayer) = -dThermalC_dTempAbove(iLayer) * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz + iLayerThermalC(iLayer)/dz case(zeroFlux) ! zero flux at the lower boundary dFlux_dWatAbove(iLayer) = 0._rkind dFlux_dTempAbove(iLayer) = 0._rkind @@ -295,17 +281,8 @@ subroutine ssdNrgFlux(& dz = (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) dFlux_dWatAbove(iLayer) = -dThermalC_dWatAbove(iLayer) * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz dFlux_dWatBelow(iLayer) = -dThermalC_dWatBelow(iLayer) * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz - if(ixDerivMethod==analytical)then ! ** analytical derivatives - dFlux_dTempAbove(iLayer) = -dThermalC_dTempAbove(iLayer) * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz + iLayerThermalC(iLayer)/dz - dFlux_dTempBelow(iLayer) = -dThermalC_dTempBelow(iLayer) * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz - iLayerThermalC(iLayer)/dz - else ! ** numerical derivatives for constant step iLayerThermalC - flux0 = -iLayerThermalC(iLayer)*( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) ) / dz - flux1 = -iLayerThermalC(iLayer)*( mLayerTempTrial(iLayer+1) - (mLayerTempTrial(iLayer)+dx)) / dz - flux2 = -iLayerThermalC(iLayer)*((mLayerTempTrial(iLayer+1)+dx) - mLayerTempTrial(iLayer) ) / dz - dFlux_dTempAbove(iLayer) = (flux1 - flux0)/dx - dFlux_dTempBelow(iLayer) = (flux2 - flux0)/dx - end if - + dFlux_dTempAbove(iLayer) = -dThermalC_dTempAbove(iLayer) * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz + iLayerThermalC(iLayer)/dz + dFlux_dTempBelow(iLayer) = -dThermalC_dTempBelow(iLayer) * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz - iLayerThermalC(iLayer)/dz end if ! type of layer (upper, internal, or lower) end do ! (looping through layers) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index a29c5c3e5..8634910f8 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -64,7 +64,15 @@ module summaSolve4ida_module model_options ! defines the model decisions ! look-up values for the choice of groundwater parameterization -USE mDecisions_module,only: qbaseTopmodel ! TOPMODEL-ish baseflow parameterization +USE mDecisions_module,only: & + qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization + bigBucket, & ! a big bucket (lumped aquifer model) + noExplicit ! no explicit groundwater parameterization + +! look-up values for method used to compute derivative +USE mDecisions_module,only: & + numerical, & ! numerical solution + analytical ! analytical solution ! privacy implicit none @@ -222,15 +230,22 @@ subroutine summaSolve4ida( & character(LEN=256) :: cmessage ! error message of downwind routine real(rkind),allocatable :: mLayerMatricHeadPrimePrev(:) ! previous derivative value for total water matric potential (m s-1) real(rkind),allocatable :: dCompress_dPsiPrev(:) ! previous derivative value soil compression + logical(lgt) :: use_fdJac ! flag to use finite difference Jacobian, default false logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true logical(lgt),parameter :: detect_events = .true. ! flag to do event detection and restarting, default true - logical(lgt),parameter :: use_fdJac = .false. ! flag to use finite difference Jacobian, default false ! ----------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="summaSolve4ida/" + ! choose Jacobian type + select case(model_decisions(iLookDECISIONS%fDerivMeth)%iDecision) + case(numerical); use_fdJac =.true. + case(analytical); use_fdJac =.false. + case default; err=20; message=trim(message)//'expect choice numericl or analytic to calculate derivatives for Jacobian'; return + end select + nState = nStat ! total number of state variables in SUNDIALS type idaSucceeds = .true. diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index a6115df80..93f13e785 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -63,7 +63,15 @@ module summaSolve4kinsol_module model_options ! defines the model decisions ! look-up values for the choice of groundwater parameterization -USE mDecisions_module,only: qbaseTopmodel ! TOPMODEL-ish baseflow parameterization +USE mDecisions_module,only: & + qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization + bigBucket, & ! a big bucket (lumped aquifer model) + noExplicit ! no explicit groundwater parameterization + +! look-up values for method used to compute derivative +USE mDecisions_module,only: & + numerical, & ! numerical solution + analytical ! analytical solution ! privacy implicit none @@ -196,14 +204,20 @@ subroutine summaSolve4kinsol(& real(rkind) :: rVec(nStat) ! residual vector integer(i4b) :: iVar, i ! indices character(LEN=256) :: cmessage ! error message of downwind routine - logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn KINSOL warnings off, default true - logical(lgt),parameter :: use_fdJac = .false. ! flag to use finite difference Jacobian, default false - - ! ----------------------------------------------------------------------------------------------------- + logical(lgt) :: use_fdJac ! flag to use finite difference Jacobian, default false + logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true + ! ----------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="summaSolve4kinsol/" + ! choose Jacobian type + select case(model_decisions(iLookDECISIONS%fDerivMeth)%iDecision) + case(numerical); use_fdJac =.true. + case(analytical); use_fdJac =.false. + case default; err=20; message=trim(message)//'expect choice numericl or analytic to calculate derivatives for Jacobian'; return + end select + nState = nStat ! total number of state variables in SUNDIALS type kinsolSucceeds = .true. diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index aca1509ba..35a0a87b3 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -72,9 +72,14 @@ module summaSolve4numrec_module ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: & - qbaseTopmodel,& ! TOPMODEL-ish baseflow parameterization - bigBucket, & ! a big bucket (lumped aquifer model) - noExplicit ! no explicit groundwater parameterization + qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization + bigBucket, & ! a big bucket (lumped aquifer model) + noExplicit ! no explicit groundwater parameterization + +! look-up values for method used to compute derivative +USE mDecisions_module,only: & + numerical, & ! numerical solution + analytical ! analytical solution implicit none private @@ -219,6 +224,13 @@ subroutine summaSolve4numrec(& ! initialize error control err=0; message='summaSolve4numrec/' +! choose Jacobian type + select case(model_decisions(iLookDECISIONS%fDerivMeth)%iDecision) + case(numerical); err=20; message=trim(message)//'numerical derivatives are not implemented for BE numerical Recipes solver'; return + case(analytical); ! this is fine + case default; err=20; message=trim(message)//'expect choice numericl or analytic to calculate derivatives for Jacobian'; return + end select + ! get the number of soil layers in the solution vector mSoil = size(indx_data%var(iLookINDEX%ixMatOnly)%dat) @@ -854,7 +866,7 @@ subroutine testBandMat(check,err,message) ! compute the full Jacobian matrix call computJacob(& ! input: model control - dt_cur, & ! intent(in): length of the time step (seconds) + dt_cur, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 2f9fb415f..552b36c62 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -278,21 +278,6 @@ subroutine vegNrgFlux(& real(rkind) :: exposedVAI ! exposed vegetation area index (m2 m-2) real(rkind) :: totalCanopyWater ! total water on the vegetation canopy (kg m-2) real(rkind) :: scalarAquiferStorage ! aquifer storage (m) - ! compute numerical derivatives - integer(i4b),parameter :: unperturbed=1 ! named variable to identify the case of unperturbed state variables - integer(i4b),parameter :: perturbStateGround=2 ! named variable to identify the case where we perturb the ground temperature - integer(i4b),parameter :: perturbStateCanopy=3 ! named variable to identify the case where we perturb the canopy temperature - integer(i4b),parameter :: perturbStateCanair=4 ! named variable to identify the case where we perturb the canopy air temperature - integer(i4b),parameter :: perturbStateCanWat=5 ! named variable to identify the case where we perturb the canopy total water content - integer(i4b) :: itry ! index of flux evaluation - integer(i4b) :: nFlux ! number of flux evaluations - real(rkind) :: groundTemp ! value of ground temperature used in flux calculations (may be perturbed) - real(rkind) :: canopyTemp ! value of canopy temperature used in flux calculations (may be perturbed) - real(rkind) :: canairTemp ! value of canopy air temperature used in flux calculations (may be perturbed) - real(rkind) :: canopyWat ! value of canopy total water used in flux calculations (may be perturbed) - real(rkind) :: canopyLiq ! value of canopy liquid water used in flux calculations (may be perturbed) - real(rkind) :: canopyIce ! value of canopy ice used in flux calculations (may be perturbed) - real(rkind) :: try0,try1 ! trial values to evaluate specific derivatives (testing only) ! saturation vapor pressure of veg real(rkind) :: TV_celcius ! vegetaion temperature (C) real(rkind) :: TG_celcius ! ground temperature (C) @@ -338,47 +323,6 @@ subroutine vegNrgFlux(& real(rkind) :: turbFluxCanair ! total turbulent heat fluxes exchanged at the canopy air space (W m-2) real(rkind) :: turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) real(rkind) :: turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) - ! turbulent heat transfer -- compute numerical derivatives - ! temporary scalar resistances when states are perturbed - real(rkind) :: trialLeafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) - real(rkind) :: trialGroundResistance ! below canopy aerodynamic resistance (s m-1) - real(rkind) :: trialCanopyResistance ! above canopy aerodynamic resistance (s m-1) - real(rkind) :: notUsed_RiBulkCanopy ! bulk Richardson number for the canopy (-) - real(rkind) :: notUsed_RiBulkGround ! bulk Richardson number for the ground surface (-) - real(rkind) :: notUsed_z0Canopy ! roughness length of the vegetation canopy (m) - real(rkind) :: notUsed_WindReductionFactor ! canopy wind reduction factor (-) - real(rkind) :: notUsed_ZeroPlaneDisplacement ! zero plane displacement (m) - real(rkind) :: notUsed_scalarCanopyStabilityCorrection ! stability correction for the canopy (-) - real(rkind) :: notUsed_scalarGroundStabilityCorrection ! stability correction for the ground surface (-) - real(rkind) :: notUsed_EddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) - real(rkind) :: notUsed_FrictionVelocity ! friction velocity (m s-1) - real(rkind) :: notUsed_WindspdCanopyTop ! windspeed at the top of the canopy (m s-1) - real(rkind) :: notUsed_WindspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) - real(rkind) :: notUsed_dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - real(rkind) :: notUsed_dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind) :: notUsed_dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - real(rkind) :: notUsed_dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind) :: notUsed_dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - ! fluxes after perturbations in model states -- canopy air space - real(rkind) :: turbFluxCanair_dStateCanair ! turbulent exchange from the canopy air space to the atmosphere, after canopy air temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanair_dStateCanopy ! turbulent exchange from the canopy air space to the atmosphere, after canopy temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanair_dStateGround ! turbulent exchange from the canopy air space to the atmosphere, after ground temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanair_dStateCanWat ! turbulent exchange from the canopy air space to the atmosphere, after canopy total water content is perturbed (W m-2) - ! fluxes after perturbations in model states -- vegetation canopy - real(rkind) :: turbFluxCanopy_dStateCanair ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy air temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanopy_dStateCanopy ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanopy_dStateGround ! total turbulent heat fluxes from the canopy to the canopy air space, after ground temperature is perturbed (W m-2) - real(rkind) :: turbFluxCanopy_dStateCanWat ! total turbulent heat fluxes from the canopy to the canopy air space, after canopy total water content is perturbed (W m-2) - ! fluxes after perturbations in model states -- ground surface - real(rkind) :: turbFluxGround_dStateCanair ! total turbulent heat fluxes from the ground to the canopy air space, after canopy air temperature is perturbed (W m-2) - real(rkind) :: turbFluxGround_dStateCanopy ! total turbulent heat fluxes from the ground to the canopy air space, after canopy temperature is perturbed (W m-2) - real(rkind) :: turbFluxGround_dStateGround ! total turbulent heat fluxes from the ground to the canopy air space, after ground temperature is perturbed (W m-2) - real(rkind) :: turbFluxGround_dStateCanWat ! total turbulent heat fluxes from the ground to the canopy air space, after canopy total water content is perturbed (W m-2) - ! fluxes after perturbations in model states -- canopy evaporation - real(rkind) :: latHeatCanEvap_dStateCanair ! canopy evaporation after canopy air temperature is perturbed (W m-2) - real(rkind) :: latHeatCanEvap_dStateCanopy ! canopy evaporation after canopy temperature is perturbed (W m-2) - real(rkind) :: latHeatCanEvap_dStateGround ! canopy evaporation after ground temperature is perturbed (W m-2) - real(rkind) :: latHeatCanEvap_dStateCanWat ! canopy evaporation after canopy total water content is perturbed (W m-2) ! flux derivatives -- canopy air space real(rkind) :: dTurbFluxCanair_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) real(rkind) :: dTurbFluxCanair_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) @@ -416,7 +360,6 @@ subroutine vegNrgFlux(& associate(& ! input: model decisions ix_bcUpprTdyn => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision, & ! intent(in): [i4b] choice of upper boundary condition for thermodynamics - ixDerivMethod => model_decisions(iLookDECISIONS%fDerivMeth)%iDecision, & ! intent(in): [i4b] choice of method to compute derivatives ix_veg_traits => model_decisions(iLookDECISIONS%veg_traits)%iDecision, & ! intent(in): [i4b] choice of parameterization for vegetation roughness length and displacement height ix_canopyEmis => model_decisions(iLookDECISIONS%canopyEmis)%iDecision, & ! intent(in): [i4b] choice of parameterization for canopy emissivity ix_windPrfile => model_decisions(iLookDECISIONS%windPrfile)%iDecision, & ! intent(in): [i4b] choice of canopy wind profile @@ -726,7 +669,6 @@ subroutine vegNrgFlux(& call wettedFrac(& ! input .true., & ! flag to denote if derivative is desired - (ixDerivMethod == numerical), & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) (scalarLatHeatSubVapCanopy > LH_vap+verySmall), & ! flag to denote if the canopy is frozen dCanLiq_dTcanopy, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) fracLiquidCanopy, & ! fraction of liquid water on the canopy (-) @@ -757,7 +699,6 @@ subroutine vegNrgFlux(& call aeroResist(& ! input: model control computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) - (ixDerivMethod == analytical), & ! intent(in): logical flag if would like to compute analytical derivaties ix_veg_traits, & ! intent(in): choice of parameterization for vegetation roughness length and displacement height ix_windPrfile, & ! intent(in): choice of canopy wind profile ix_astability, & ! intent(in): choice of stability function @@ -871,8 +812,7 @@ subroutine vegNrgFlux(& ! compute canopy longwave radiation balance call longwaveBal(& ! input: model control - ixDerivMethod, & ! intent(in): method used to calculate flux derivatives - computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation + computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation checkLWBalance, & ! intent(in): flag to check longwave balance ! input: canopy and ground temperature canopyTempTrial, & ! intent(in): temperature of the vegetation canopy (K) @@ -908,344 +848,129 @@ subroutine vegNrgFlux(& if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! ***** TURBULENT HEAT FLUXES ************************************************************************************************************************************** - ! check the need to compute numerical derivatives - if(ixDerivMethod == numerical)then - nFlux=5 ! compute the derivatives using one-sided finite differences - else - nFlux=1 ! compute analytical derivatives - end if - - ! either one or multiple flux calls, depending on if using analytical or numerical derivatives - do itry=nFlux,1,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) - - ! ------------------------------------------------------------------------------------- - ! state perturbations for numerical deriavtives with one-sided finite differences - ! note: no perturbations performed using analytical derivatives (nFlux=1) - ! ------------------------------------------------------------------------------------- - ! initialize for unperturbed state - canopyWetFraction = scalarCanopyWetFraction - - ! identify the type of perturbation - select case(itry) - ! un-perturbed case - case(unperturbed) - groundTemp = groundTempTrial - canopyTemp = canopyTempTrial - canairTemp = canairTempTrial - canopyWat = totalCanopyWater - ! perturb ground temperature - case(perturbStateGround) - groundTemp = groundTempTrial + dx - canopyTemp = canopyTempTrial - canairTemp = canairTempTrial - canopyWat = totalCanopyWater - ! perturb canopy temperature - case(perturbStateCanopy) - groundTemp = groundTempTrial - canopyTemp = canopyTempTrial + dx - canairTemp = canairTempTrial - canopyWat = totalCanopyWater - ! perturb canopy air temperature - case(perturbStateCanair) - groundTemp = groundTempTrial - canopyTemp = canopyTempTrial - canairTemp = canairTempTrial + dx - canopyWat = totalCanopyWater - ! perturb canopy total water content - case(perturbStateCanWat) - groundTemp = groundTempTrial - canopyTemp = canopyTempTrial - canairTemp = canairTempTrial - canopyWat = totalCanopyWater + dx - ! check for an unknown perturbation - case default; err=10; message=trim(message)//"unknown perturbation"; return - - end select ! (type of perturbation) - - ! perturbations in canopy total water content affect canopy wetted fraction - if(itry == perturbStateCanopy .OR. itry == perturbStateCanWat)then - - ! recalculate liquid and ice from total water - fracLiquidCanopy = fracliquid(canopyTemp,snowfrz_scale) - canopyLiq = fracLiquidCanopy*canopyWat - canopyIce = (1._rkind - fracLiquidCanopy)*canopyWat - - if(computeVegFlux)then - call wettedFrac(& - ! input - .false., & ! flag to denote if derivative is desired - (ixDerivMethod == numerical), & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) - (scalarLatHeatSubVapCanopy > LH_vap+verySmall), & ! flag to denote if the canopy is frozen - dCanLiq_dTcanopy, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) - fracLiquidCanopy, & ! fraction of liquid water on the canopy (-) - canopyLiq, & ! canopy liquid water (kg m-2) - canopyIce, & ! canopy ice (kg m-2) - scalarCanopyLiqMax, & ! maximum canopy liquid water (kg m-2) - scalarCanopyIceMax, & ! maximum canopy ice content (kg m-2) - canopyWettingFactor, & ! maximum wetted fraction of the canopy (-) - canopyWettingExp, & ! exponent in canopy wetting function (-) - ! output - canopyWetFraction, & ! canopy wetted fraction (-) - dCanopyWetFraction_dWat, & ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) - dCanopyWetFraction_dT, & ! derivative in wetted fraction w.r.t. canopy temperature (K-1) - err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - - else - canopyWetFraction = 0._rkind - end if ! (desired computing vegetation flux) - - end if ! (re-computing wetted fraction for perturbed cases) - - ! compute the saturation vapor pressure for vegetation temperature - ! NOTE: saturated vapor pressure derivatives don't seem that accurate.... - TV_celcius = canopyTemp - Tfreeze - call satVapPress(TV_celcius, scalarSatVP_CanopyTemp, dSVPCanopy_dCanopyTemp) - - ! compute the saturation vapor pressure for ground temperature - ! NOTE: saturated vapor pressure derivatives don't seem that accurate.... - TG_celcius = groundTemp - Tfreeze - call satVapPress(TG_celcius, scalarSatVP_GroundTemp, dSVPGround_dGroundTemp) - - ! ------------------------------------------------------------------------------------- - ! calculation block (unperturbed fluxes returned [computed last]) - ! ------------------------------------------------------------------------------------- - ! re-compute aerodynamic resistances for perturbed cases - ! NOTE: unperturbed fluxes computed earlier, and not over-written - if(itry /= unperturbed)then - call aeroResist(& - ! input: model control - computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) - .false., & ! intent(in): logical flag if would like to compute analytical derivaties - ix_veg_traits, & ! intent(in): choice of parameterization for vegetation roughness length and displacement height - ix_windPrfile, & ! intent(in): choice of canopy wind profile - ix_astability, & ! intent(in): choice of stability function - ! input: above-canopy forcing data - mHeight, & ! intent(in): measurement height (m) - airtemp, & ! intent(in): air temperature at some height above the surface (K) - windspd, & ! intent(in): wind speed at some height above the surface (m s-1) - ! input: temperature (canopy, ground, canopy air space) - canairTemp, & ! intent(in): temperature of the canopy air space (K) - groundTemp, & ! intent(in): ground temperature (K) - ! input: diagnostic variables - exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) - scalarSnowDepth, & ! intent(in): snow depth (m) - ! input: parameters - z0Ground, & ! intent(in): roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) - z0CanopyParam, & ! intent(in): roughness length of the canopy (m) - zpdFraction, & ! intent(in): zero plane displacement / canopy height (-) - critRichNumber, & ! intent(in): critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam, & ! intent(in): parameter in Louis (1979) stability function - Mahrt87_eScale, & ! intent(in): exponential scaling factor in the Mahrt (1987) stability function - windReductionParam, & ! intent(in): canopy wind reduction parameter (-) - leafExchangeCoeff, & ! intent(in): turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) - leafDimension, & ! intent(in): characteristic leaf dimension (m) - heightCanopyTop, & ! intent(in): height at the top of the vegetation canopy (m) - heightCanopyBottom, & ! intent(in): height at the bottom of the vegetation canopy (m) - ! output: stability corrections - notUsed_RiBulkCanopy, & ! intent(out): bulk Richardson number for the canopy (-) - notUsed_RiBulkGround, & ! intent(out): bulk Richardson number for the ground surface (-) - notUsed_scalarCanopyStabilityCorrection, & ! intent(out): stability correction for the canopy (-) - notUsed_scalarGroundStabilityCorrection, & ! intent(out): stability correction for the ground surface (-) - ! output: scalar resistances - notUsed_z0Canopy, & ! intent(out): roughness length of the canopy (m) - notUsed_WindReductionFactor, & ! intent(out): canopy wind reduction factor (-) - notUsed_ZeroPlaneDisplacement, & ! intent(out): zero plane displacement (m) - notUsed_EddyDiffusCanopyTop, & ! intent(out): eddy diffusivity for heat at the top of the canopy (m2 s-1) - notUsed_FrictionVelocity, & ! intent(out): friction velocity (m s-1) - notUsed_WindspdCanopyTop, & ! intent(out): windspeed at the top of the canopy (m s-1) - notUsed_WindspdCanopyBottom, & ! intent(out): windspeed at the height of the bottom of the canopy (m s-1) - trialLeafResistance, & ! intent(out): mean leaf boundary layer resistance per unit leaf area (s m-1) - trialGroundResistance, & ! intent(out): below canopy aerodynamic resistance (s m-1) - trialCanopyResistance, & ! intent(out): above canopy aerodynamic resistance (s m-1) - ! output: derivatives in scalar resistances - notUsed_dGroundResistance_dTGround, & ! intent(out): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - notUsed_dGroundResistance_dTCanopy, & ! intent(out): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - notUsed_dGroundResistance_dTCanair, & ! intent(out): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - notUsed_dCanopyResistance_dTCanopy, & ! intent(out): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - notUsed_dCanopyResistance_dTCanair, & ! intent(out): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - ! output: error control - err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - ! assign scalar resistances for un-perturbed cases + ! compute the saturation vapor pressure for vegetation temperature + ! NOTE: saturated vapor pressure derivatives don't seem that accurate.... + TV_celcius = canopyTempTrial - Tfreeze + call satVapPress(TV_celcius, scalarSatVP_CanopyTemp, dSVPCanopy_dCanopyTemp) + + ! compute the saturation vapor pressure for ground temperature + ! NOTE: saturated vapor pressure derivatives don't seem that accurate.... + TG_celcius = groundTempTrial - Tfreeze + call satVapPress(TG_celcius, scalarSatVP_GroundTemp, dSVPGround_dGroundTemp) + + ! compute the relative humidity in the top soil layer and the resistance at the ground surface + ! NOTE: computations are based on start-of-step values, so only compute for the first flux call + if(firstFluxCall)then + ! soil water evaporation factor [0-1] + soilEvapFactor = mLayerVolFracLiq(nSnow+1)/(theta_sat - theta_res) + ! resistance from the soil [s m-1] + scalarSoilResistance = scalarGroundSnowFraction*1._rkind + (1._rkind - scalarGroundSnowFraction)*EXP(8.25_rkind - 4.225_rkind*soilEvapFactor) ! Sellers (1992) + !scalarSoilResistance = scalarGroundSnowFraction*0._rkind + (1._rkind - scalarGroundSnowFraction)*exp(8.25_rkind - 6.0_rkind*soilEvapFactor) ! Niu adjustment to decrease resitance for wet soil + ! relative humidity in the soil pores [0-1] + if(mLayerMatricHead(1) > -1.e+6_rkind)then ! avoid problems with numerical precision when soil is very dry + soilRelHumidity_noSnow = exp( (mLayerMatricHead(1)*gravity) / (groundTempTrial*R_wv) ) else - trialLeafResistance = scalarLeafResistance - trialGroundResistance = scalarGroundResistance - trialCanopyResistance = scalarCanopyResistance - end if ! (re-computing resistances for perturbed cases) - - - ! compute the relative humidity in the top soil layer and the resistance at the ground surface - ! NOTE: computations are based on start-of-step values, so only compute for the first flux call - if(firstFluxCall)then - ! soil water evaporation factor [0-1] - soilEvapFactor = mLayerVolFracLiq(nSnow+1)/(theta_sat - theta_res) - ! resistance from the soil [s m-1] - scalarSoilResistance = scalarGroundSnowFraction*1._rkind + (1._rkind - scalarGroundSnowFraction)*EXP(8.25_rkind - 4.225_rkind*soilEvapFactor) ! Sellers (1992) - !scalarSoilResistance = scalarGroundSnowFraction*0._rkind + (1._rkind - scalarGroundSnowFraction)*exp(8.25_rkind - 6.0_rkind*soilEvapFactor) ! Niu adjustment to decrease resitance for wet soil - ! relative humidity in the soil pores [0-1] - if(mLayerMatricHead(1) > -1.e+6_rkind)then ! avoid problems with numerical precision when soil is very dry - soilRelHumidity_noSnow = exp( (mLayerMatricHead(1)*gravity) / (groundTemp*R_wv) ) - else - soilRelHumidity_noSnow = 0._rkind - end if ! (if matric head is very low) - scalarSoilRelHumidity = scalarGroundSnowFraction*1._rkind + (1._rkind - scalarGroundSnowFraction)*soilRelHumidity_noSnow - end if ! (if the first flux call) - - ! compute turbulent heat fluxes - call turbFluxes(& - ! input: model control - computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) - ixDerivMethod, & ! intent(in): method used to calculate flux derivatives - ! input: above-canopy forcing data - airtemp, & ! intent(in): air temperature at some height above the surface (K) - airpres, & ! intent(in): air pressure of the air above the vegetation canopy (Pa) - scalarVPair, & ! intent(in): vapor pressure of the air above the vegetation canopy (Pa) - ! input: latent heat of sublimation/vaporization - scalarLatHeatSubVapCanopy, & ! intent(in): latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) - scalarLatHeatSubVapGround, & ! intent(in): latent heat of sublimation/vaporization for the ground surface (J kg-1) - ! input: canopy/ground temperature and saturated vapor pressure - canairTemp, & ! intent(in): temperature of the canopy air space (K) - canopyTemp, & ! intent(in): canopy temperature (K) - groundTemp, & ! intent(in): ground temperature (K) - scalarSatVP_CanopyTemp, & ! intent(in): saturation vapor pressure at the temperature of the veg canopy (Pa) - scalarSatVP_GroundTemp, & ! intent(in): saturation vapor pressure at the temperature of the ground (Pa) - dSVPCanopy_dCanopyTemp, & ! intent(in): derivative in canopy saturation vapor pressure w.r.t. canopy temperature (Pa K-1) - dSVPGround_dGroundTemp, & ! intent(in): derivative in ground saturation vapor pressure w.r.t. ground temperature (Pa K-1) - ! input: diagnostic variables - exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) - canopyWetFraction, & ! intent(in): trial value for the fraction of canopy that is wet [0-1] - dCanopyWetFraction_dWat, & ! intent(in): derivative in the canopy wetted fraction w.r.t. total water content (kg-1 m-2) - dCanopyWetFraction_dT, & ! intent(in): derivative in wetted fraction w.r.t. canopy temperature (K-1) - scalarCanopySunlitLAI, & ! intent(in): sunlit leaf area (-) - scalarCanopyShadedLAI, & ! intent(in): shaded leaf area (-) - scalarSoilRelHumidity, & ! intent(in): relative humidity in the soil pores [0-1] - scalarSoilResistance, & ! intent(in): resistance from the soil (s m-1) - trialLeafResistance, & ! intent(in): mean leaf boundary layer resistance per unit leaf area (s m-1) - trialGroundResistance, & ! intent(in): below canopy aerodynamic resistance (s m-1) - trialCanopyResistance, & ! intent(in): above canopy aerodynamic resistance (s m-1) - scalarStomResistSunlit, & ! intent(in): stomatal resistance for sunlit leaves (s m-1) - scalarStomResistShaded, & ! intent(in): stomatal resistance for shaded leaves (s m-1) - ! input: derivatives in scalar resistances - dGroundResistance_dTGround, & ! intent(in): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - dGroundResistance_dTCanopy, & ! intent(in): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - dGroundResistance_dTCanair, & ! intent(in): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - dCanopyResistance_dTCanopy, & ! intent(in): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - dCanopyResistance_dTCanair, & ! intent(in): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - ! output: conductances (used to check derivative calculations) - scalarLeafConductance, & ! intent(out): leaf conductance (m s-1) - scalarCanopyConductance, & ! intent(out): canopy conductance (m s-1) - scalarGroundConductanceSH, & ! intent(out): ground conductance for sensible heat (m s-1) - scalarGroundConductanceLH, & ! intent(out): ground conductance for latent heat -- includes soil resistance (m s-1) - scalarEvapConductance, & ! intent(out): conductance for evaporation (m s-1) - scalarTransConductance, & ! intent(out): conductance for transpiration (m s-1) - scalarTotalConductanceSH, & ! intent(out): total conductance for sensible heat (m s-1) - scalarTotalConductanceLH, & ! intent(out): total conductance for latent heat (m s-1) - ! output: canopy air space variables - scalarVP_CanopyAir, & ! intent(out): vapor pressure of the canopy air space (Pa) - ! output: fluxes from the vegetation canopy - scalarSenHeatCanopy, & ! intent(out): sensible heat flux from the canopy to the canopy air space (W m-2) - scalarLatHeatCanopyEvap, & ! intent(out): latent heat flux associated with evaporation from the canopy to the canopy air space (W m-2) - scalarLatHeatCanopyTrans, & ! intent(out): latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) - ! output: fluxes from non-vegetated surfaces (ground surface below vegetation, bare ground, or snow covered vegetation) - scalarSenHeatGround, & ! intent(out): sensible heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) - scalarLatHeatGround, & ! intent(out): latent heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) - ! output: total heat fluxes to the atmosphere - scalarSenHeatTotal, & ! intent(out): total sensible heat flux to the atmosphere (W m-2) - scalarLatHeatTotal, & ! intent(out): total latent heat flux to the atmosphere (W m-2) - ! output: net fluxes - turbFluxCanair, & ! intent(out): net turbulent heat fluxes at the canopy air space (W m-2) - turbFluxCanopy, & ! intent(out): net turbulent heat fluxes at the canopy (W m-2) - turbFluxGround, & ! intent(out): net turbulent heat fluxes at the ground surface (W m-2) - ! output: energy flux derivatives - dTurbFluxCanair_dTCanair, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxCanair_dTCanopy, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxCanair_dTGround, & ! intent(out): derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanair, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanopy, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxCanopy_dTGround, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxGround_dTCanair, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxGround_dTCanopy, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxGround_dTGround, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - ! output: liquid flux derivatives (canopy evap) - dLatHeatCanopyEvap_dCanWat, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy total water content (W kg-1) - dLatHeatCanopyEvap_dTCanair, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTCanopy, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTGround, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) - ! output: liquid flux derivatives (ground evap) - dLatHeatGroundEvap_dCanWat, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) - dLatHeatGroundEvap_dTCanair, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy air temperature - dLatHeatGroundEvap_dTCanopy, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy temperature - dLatHeatGroundEvap_dTGround, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. ground temperature - ! output: latent heat flux derivatives (canopy trans) - dLatHeatCanopyTrans_dCanWat, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy total water (J kg-1 s-1) - dLatHeatCanopyTrans_dTCanair, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy air temperature - dLatHeatCanopyTrans_dTCanopy, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy temperature - dLatHeatCanopyTrans_dTGround, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. ground temperature - ! output: cross derivatives - dTurbFluxCanair_dCanWat, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) - dTurbFluxCanopy_dCanWat, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - dTurbFluxGround_dCanWat, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - ! output: error control - err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + soilRelHumidity_noSnow = 0._rkind + end if ! (if matric head is very low) + scalarSoilRelHumidity = scalarGroundSnowFraction*1._rkind + (1._rkind - scalarGroundSnowFraction)*soilRelHumidity_noSnow + end if ! (if the first flux call) - ! save perturbed fluxes - if(ixDerivMethod == numerical)then - select case(itry) ! (select type of perturbation) - case(unperturbed) - try0 = turbFluxGround - exit - case(perturbStateCanair) - turbFluxCanair_dStateCanair = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) - turbFluxCanopy_dStateCanair = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) - turbFluxGround_dStateCanair = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) - latHeatCanEvap_dStateCanair = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) - case(perturbStateCanopy) - turbFluxCanair_dStateCanopy = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) - turbFluxCanopy_dStateCanopy = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) - turbFluxGround_dStateCanopy = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) - latHeatCanEvap_dStateCanopy = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) - case(perturbStateGround) - try1 = turbFluxGround - turbFluxCanair_dStateGround = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) - turbFluxCanopy_dStateGround = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) - turbFluxGround_dStateGround = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) - latHeatCanEvap_dStateGround = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) - case(perturbStateCanWat) - turbFluxCanair_dStateCanWat = turbFluxCanair ! turbulent exchange from the canopy air space to the atmosphere (W m-2) - turbFluxCanopy_dStateCanWat = turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) - turbFluxGround_dStateCanWat = turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) - latHeatCanEvap_dStateCanWat = scalarLatHeatCanopyEvap ! perturbed value for the latent heat associated with canopy evaporation (W m-2) - case default; err=10; message=trim(message)//"unknown perturbation"; return - end select ! (type of perturbation) - end if ! (if numerical) - - end do ! (looping through different flux perturbations) - - ! compute numerical derivatives - if(ixDerivMethod == numerical)then - ! derivatives w.r.t. canopy air temperature - dTurbFluxCanair_dTCanair = (turbFluxCanair_dStateCanair - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanair = (turbFluxCanopy_dStateCanair - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxGround_dTCanair = (turbFluxGround_dStateCanair - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTCanair = (latHeatCanEvap_dStateCanair - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) - ! derivatives w.r.t. canopy temperature - dTurbFluxCanair_dTCanopy = (turbFluxCanair_dStateCanopy - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanopy = (turbFluxCanopy_dStateCanopy - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxGround_dTCanopy = (turbFluxGround_dStateCanopy - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTCanopy = (latHeatCanEvap_dStateCanopy - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) - ! derivatives w.r.t. ground temperature - dTurbFluxCanair_dTGround = (turbFluxCanair_dStateGround - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxCanopy_dTGround = (turbFluxCanopy_dStateGround - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxGround_dTGround = (turbFluxGround_dStateGround - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - dLatHeatCanopyEvap_dTGround = (latHeatCanEvap_dStateGround - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) - ! derivatives w.r.t. canopy total water content - dTurbFluxCanair_dCanWat = (turbFluxCanair_dStateCanWat - turbFluxCanair) / dx ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) - dTurbFluxCanopy_dCanWat = (turbFluxCanopy_dStateCanWat - turbFluxCanopy) / dx ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - dTurbFluxGround_dCanWat = (turbFluxGround_dStateCanWat - turbFluxGround) / dx ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - dLatHeatCanopyEvap_dCanWat = (latHeatCanEvap_dStateCanWat - scalarLatHeatCanopyEvap) / dx ! derivative in latent heat of canopy evaporation w.r.t. canopy total water content (J kg-1 s-1) - end if + ! compute turbulent heat fluxes + call turbFluxes(& + ! input: model control + computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) + ! input: above-canopy forcing data + airtemp, & ! intent(in): air temperature at some height above the surface (K) + airpres, & ! intent(in): air pressure of the air above the vegetation canopy (Pa) + scalarVPair, & ! intent(in): vapor pressure of the air above the vegetation canopy (Pa) + ! input: latent heat of sublimation/vaporization + scalarLatHeatSubVapCanopy, & ! intent(in): latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) + scalarLatHeatSubVapGround, & ! intent(in): latent heat of sublimation/vaporization for the ground surface (J kg-1) + ! input: canopy/ground temperature and saturated vapor pressure + canairTempTrial, & ! intent(in): temperature of the canopy air space (K) + canopyTempTrial, & ! intent(in): canopy temperature (K) + groundTempTrial, & ! intent(in): ground temperature (K) + scalarSatVP_CanopyTemp, & ! intent(in): saturation vapor pressure at the temperature of the veg canopy (Pa) + scalarSatVP_GroundTemp, & ! intent(in): saturation vapor pressure at the temperature of the ground (Pa) + dSVPCanopy_dCanopyTemp, & ! intent(in): derivative in canopy saturation vapor pressure w.r.t. canopy temperature (Pa K-1) + dSVPGround_dGroundTemp, & ! intent(in): derivative in ground saturation vapor pressure w.r.t. ground temperature (Pa K-1) + ! input: diagnostic variables + exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) + scalarCanopyWetFraction, & ! intent(in): trial value for the fraction of canopy that is wet [0-1] + dCanopyWetFraction_dWat, & ! intent(in): derivative in the canopy wetted fraction w.r.t. total water content (kg-1 m-2) + dCanopyWetFraction_dT, & ! intent(in): derivative in wetted fraction w.r.t. canopy temperature (K-1) + scalarCanopySunlitLAI, & ! intent(in): sunlit leaf area (-) + scalarCanopyShadedLAI, & ! intent(in): shaded leaf area (-) + scalarSoilRelHumidity, & ! intent(in): relative humidity in the soil pores [0-1] + scalarSoilResistance, & ! intent(in): resistance from the soil (s m-1) + scalarLeafResistance, & ! intent(in): mean leaf boundary layer resistance per unit leaf area (s m-1) + scalarGroundResistance, & ! intent(in): below canopy aerodynamic resistance (s m-1) + scalarCanopyResistance, & ! intent(in): above canopy aerodynamic resistance (s m-1) + scalarStomResistSunlit, & ! intent(in): stomatal resistance for sunlit leaves (s m-1) + scalarStomResistShaded, & ! intent(in): stomatal resistance for shaded leaves (s m-1) + ! input: derivatives in scalar resistances + dGroundResistance_dTGround, & ! intent(in): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + dGroundResistance_dTCanopy, & ! intent(in): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + dGroundResistance_dTCanair, & ! intent(in): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + dCanopyResistance_dTCanopy, & ! intent(in): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + dCanopyResistance_dTCanair, & ! intent(in): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + ! output: conductances (used to check derivative calculations) + scalarLeafConductance, & ! intent(out): leaf conductance (m s-1) + scalarCanopyConductance, & ! intent(out): canopy conductance (m s-1) + scalarGroundConductanceSH, & ! intent(out): ground conductance for sensible heat (m s-1) + scalarGroundConductanceLH, & ! intent(out): ground conductance for latent heat -- includes soil resistance (m s-1) + scalarEvapConductance, & ! intent(out): conductance for evaporation (m s-1) + scalarTransConductance, & ! intent(out): conductance for transpiration (m s-1) + scalarTotalConductanceSH, & ! intent(out): total conductance for sensible heat (m s-1) + scalarTotalConductanceLH, & ! intent(out): total conductance for latent heat (m s-1) + ! output: canopy air space variables + scalarVP_CanopyAir, & ! intent(out): vapor pressure of the canopy air space (Pa) + ! output: fluxes from the vegetation canopy + scalarSenHeatCanopy, & ! intent(out): sensible heat flux from the canopy to the canopy air space (W m-2) + scalarLatHeatCanopyEvap, & ! intent(out): latent heat flux associated with evaporation from the canopy to the canopy air space (W m-2) + scalarLatHeatCanopyTrans, & ! intent(out): latent heat flux associated with transpiration from the canopy to the canopy air space (W m-2) + ! output: fluxes from non-vegetated surfaces (ground surface below vegetation, bare ground, or snow covered vegetation) + scalarSenHeatGround, & ! intent(out): sensible heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) + scalarLatHeatGround, & ! intent(out): latent heat flux from ground surface below vegetation, bare ground, or snow covered vegetation (W m-2) + ! output: total heat fluxes to the atmosphere + scalarSenHeatTotal, & ! intent(out): total sensible heat flux to the atmosphere (W m-2) + scalarLatHeatTotal, & ! intent(out): total latent heat flux to the atmosphere (W m-2) + ! output: net fluxes + turbFluxCanair, & ! intent(out): net turbulent heat fluxes at the canopy air space (W m-2) + turbFluxCanopy, & ! intent(out): net turbulent heat fluxes at the canopy (W m-2) + turbFluxGround, & ! intent(out): net turbulent heat fluxes at the ground surface (W m-2) + ! output: energy flux derivatives + dTurbFluxCanair_dTCanair, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxCanair_dTCanopy, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxCanair_dTGround, & ! intent(out): derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanair, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanopy, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxCanopy_dTGround, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxGround_dTCanair, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxGround_dTCanopy, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxGround_dTGround, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + ! output: liquid flux derivatives (canopy evap) + dLatHeatCanopyEvap_dCanWat, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy total water content (W kg-1) + dLatHeatCanopyEvap_dTCanair, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) + dLatHeatCanopyEvap_dTCanopy, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) + dLatHeatCanopyEvap_dTGround, & ! intent(out): derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) + ! output: liquid flux derivatives (ground evap) + dLatHeatGroundEvap_dCanWat, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) + dLatHeatGroundEvap_dTCanair, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy air temperature + dLatHeatGroundEvap_dTCanopy, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. canopy temperature + dLatHeatGroundEvap_dTGround, & ! intent(out): derivative in latent heat of ground evaporation w.r.t. ground temperature + ! output: latent heat flux derivatives (canopy trans) + dLatHeatCanopyTrans_dCanWat, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy total water (J kg-1 s-1) + dLatHeatCanopyTrans_dTCanair, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy air temperature + dLatHeatCanopyTrans_dTCanopy, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. canopy temperature + dLatHeatCanopyTrans_dTGround, & ! intent(out): derivative in the latent heat of canopy transpiration w.r.t. ground temperature + ! output: cross derivatives + dTurbFluxCanair_dCanWat, & ! intent(out): derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxCanopy_dCanWat, & ! intent(out): derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxGround_dCanWat, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + ! output: error control + err,cmessage ) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! compute the heat advected with precipitation (W m-2) ! NOTE: fluxes are in kg m-2 s-1, so no need to use density of water/ice here @@ -1362,14 +1087,12 @@ subroutine vegNrgFlux(& end subroutine vegNrgFlux - ! ******************************************************************************************************* ! public subroutine wettedFrac: compute wetted fraction of the canopy ! ******************************************************************************************************* subroutine wettedFrac(& ! input deriv, & ! flag to denote if derivative is desired - derNum, & ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) frozen, & ! flag to denote if the canopy is frozen dLiq_dT, & ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) fracLiq, & ! fraction of liquid water on the canopy (-) @@ -1387,27 +1110,25 @@ subroutine wettedFrac(& implicit none ! input logical(lgt),intent(in) :: deriv ! flag to denote if derivative is desired - logical(lgt),intent(in) :: derNum ! flag to denote that numerical derivatives are required (otherwise, analytical derivatives are calculated) logical(lgt),intent(in) :: frozen ! flag to denote if the canopy is frozen - real(rkind),intent(in) :: dLiq_dT ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) - real(rkind),intent(in) :: fracLiq ! fraction of liquid water on the canopy (-) - real(rkind),intent(in) :: canopyLiq ! canopy liquid water (kg m-2) - real(rkind),intent(in) :: canopyIce ! canopy ice (kg m-2) - real(rkind),intent(in) :: canopyLiqMax ! maximum canopy liquid water (kg m-2) - real(rkind),intent(in) :: canopyIceMax ! maximum canopy ice content (kg m-2) - real(rkind),intent(in) :: canopyWettingFactor ! maximum wetted fraction of the canopy (-) - real(rkind),intent(in) :: canopyWettingExp ! exponent in canopy wetting function (-) + real(rkind),intent(in) :: dLiq_dT ! derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) + real(rkind),intent(in) :: fracLiq ! fraction of liquid water on the canopy (-) + real(rkind),intent(in) :: canopyLiq ! canopy liquid water (kg m-2) + real(rkind),intent(in) :: canopyIce ! canopy ice (kg m-2) + real(rkind),intent(in) :: canopyLiqMax ! maximum canopy liquid water (kg m-2) + real(rkind),intent(in) :: canopyIceMax ! maximum canopy ice content (kg m-2) + real(rkind),intent(in) :: canopyWettingFactor ! maximum wetted fraction of the canopy (-) + real(rkind),intent(in) :: canopyWettingExp ! exponent in canopy wetting function (-) ! output - real(rkind),intent(out) :: canopyWetFraction ! canopy wetted fraction (-) - real(rkind),intent(out) :: dCanopyWetFraction_dWat ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) - real(rkind),intent(out) :: dCanopyWetFraction_dT ! derivative in wetted fraction w.r.t. canopy temperature (K-1) + real(rkind),intent(out) :: canopyWetFraction ! canopy wetted fraction (-) + real(rkind),intent(out) :: dCanopyWetFraction_dWat ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) + real(rkind),intent(out) :: dCanopyWetFraction_dT ! derivative in wetted fraction w.r.t. canopy temperature (K-1) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local variables logical(lgt),parameter :: smoothing=.true. ! flag to denote that smoothing is required - real(rkind) :: canopyWetFractionPert ! canopy wetted fraction after state perturbations (-) - real(rkind) :: canopyWetFractionDeriv ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) + real(rkind) :: canopyWetFractionDeriv ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) ! ----------------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='wettedFrac/' @@ -1415,12 +1136,8 @@ subroutine wettedFrac(& ! compute case where the canopy is frozen if(frozen)then ! compute fraction of liquid water on the canopy - call wetFraction((deriv .and. .not.derNum),smoothing,canopyIce,canopyIceMax,canopyWettingFactor,canopyWettingExp,canopyWetFraction,canopyWetFractionDeriv) - ! compute numerical derivative, if derivative is desired - if(deriv.and.derNum)then - call wetFraction((deriv .and. .not.derNum),smoothing,canopyIce+dx,canopyIceMax,canopyWettingFactor,canopyWettingExp,canopyWetFractionPert,canopyWetFractionDeriv) - canopyWetFractionDeriv = (canopyWetFractionPert - canopyWetFraction)/dx - end if + call wetFraction(deriv,smoothing,canopyIce,canopyIceMax,canopyWettingFactor,canopyWettingExp,canopyWetFraction,canopyWetFractionDeriv) + ! scale derivative by the fraction of water ! NOTE: dIce/dWat = (1._rkind - fracLiq), hence dWet/dWat = dIce/dWat . dWet/dLiq dCanopyWetFraction_dWat = canopyWetFractionDeriv*(1._rkind - fracLiq) @@ -1430,13 +1147,7 @@ subroutine wettedFrac(& ! compute fraction of liquid water on the canopy ! NOTE: if(.not.deriv) canopyWetFractionDeriv = 0._rkind - call wetFraction((deriv .and. .not.derNum),smoothing,canopyLiq,canopyLiqMax,canopyWettingFactor,canopyWettingExp,canopyWetFraction,canopyWetFractionDeriv) - - ! compute numerical derivative - if(deriv.and.derNum)then - call wetFraction((deriv .and. .not.derNum),smoothing,canopyLiq+dx,canopyLiqMax,canopyWettingFactor,canopyWettingExp,canopyWetFractionPert,canopyWetFractionDeriv) - canopyWetFractionDeriv = (canopyWetFractionPert - canopyWetFraction)/dx - end if + call wetFraction(deriv,smoothing,canopyLiq,canopyLiqMax,canopyWettingFactor,canopyWettingExp,canopyWetFraction,canopyWetFractionDeriv) ! scale derivative by the fraction of water ! NOTE: dLiq/dWat = fracLiq, hence dWet/dWat = dLiq/dWat . dWet/dLiq @@ -1445,15 +1156,14 @@ subroutine wettedFrac(& end subroutine wettedFrac - ! ******************************************************************************************************* ! private subroutine wetFraction: compute fraction of canopy covered with liquid water ! ******************************************************************************************************* subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFactor,canopyWettingExp,canopyWetFraction,canopyWetFractionDeriv) implicit none ! dummy variables - logical(lgt),intent(in) :: derDesire ! flag to denote if analytical derivatives are desired - logical(lgt),intent(in) :: smoothing ! flag to denote if smoothing is required + logical(lgt),intent(in) :: derDesire ! flag to denote if analytical derivatives are desired + logical(lgt),intent(in) :: smoothing ! flag to denote if smoothing is required real(rkind),intent(in) :: canopyLiq ! liquid water content (kg m-2) real(rkind),intent(in) :: canopyMax ! liquid water content (kg m-2) real(rkind),intent(in) :: canopyWettingFactor ! maximum wetted fraction of the canopy (-) @@ -1507,7 +1217,6 @@ subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFact end subroutine wetFraction - ! ******************************************************************************************************* ! private subroutine logisticSmoother: compute the smoothing function ! ******************************************************************************************************* @@ -1548,13 +1257,11 @@ subroutine logisticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) end subroutine logisticSmoother - ! ******************************************************************************************************* ! private subroutine longwaveBal: compute longwave radiation balance at the canopy and ground surface ! ******************************************************************************************************* subroutine longwaveBal(& ! input: model control - ixDerivMethod, & ! intent(in): choice of method used to compute derivative (analytical or numerical) computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation checkLWBalance, & ! intent(in): flag to check longwave balance ! input: canopy and ground temperature @@ -1591,7 +1298,6 @@ subroutine longwaveBal(& ! ----------------------------------------------------------------------------------------------------------------------------------------------- implicit none ! input: model control - integer(i4b),intent(in) :: ixDerivMethod ! choice of method used to compute derivative (analytical or numerical) logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: checkLWBalance ! flag to check longwave balance ! input: canopy and ground temperature @@ -1624,167 +1330,90 @@ subroutine longwaveBal(& real(rkind),intent(out) :: dLWNetCanopy_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) real(rkind),intent(out) :: dLWNetGround_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! ----------------------------------------------------------------------------------------------------------------------------------------------- ! local variables - integer(i4b),parameter :: unperturbed=1 ! named variable to identify the case of unperturbed state variables - integer(i4b),parameter :: perturbStateCanopy=2 ! named variable to identify the case where we perturb the canopy temperature - integer(i4b),parameter :: perturbStateGround=3 ! named variable to identify the case where we perturb the ground temperature - integer(i4b) :: itry ! index of flux evaluation - integer(i4b) :: nFlux ! number of flux evaluations - real(rkind) :: TCan ! value of canopy temperature used in flux calculations (may be perturbed) - real(rkind) :: TGnd ! value of ground temperature used in flux calculations (may be perturbed) real(rkind) :: fluxBalance ! check energy closure (W m-2) real(rkind),parameter :: fluxTolerance=1.e-10_rkind ! tolerance for energy closure (W m-2) real(rkind) :: dLWRadCanopy_dTCanopy ! derivative in emitted radiation at the canopy w.r.t. canopy temperature real(rkind) :: dLWRadGround_dTGround ! derivative in emitted radiation at the ground w.r.t. ground temperature - real(rkind) :: LWNetCanopy_dStateCanopy ! net lw canopy flux after perturbation in canopy temperature - real(rkind) :: LWNetGround_dStateCanopy ! net lw ground flux after perturbation in canopy temperature - real(rkind) :: LWNetCanopy_dStateGround ! net lw canopy flux after perturbation in ground temperature - real(rkind) :: LWNetGround_dStateGround ! net lw ground flux after perturbation in ground temperature ! ----------------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='longwaveBal/' - - ! check the need to compute numerical derivatives - if(ixDerivMethod==numerical)then - nFlux=3 ! compute the derivatives using one-sided finite differences + + ! NOTE: emc should be set to zero when not computing canopy fluxes + ! compute longwave fluxes from canopy and the ground + if(computeVegFlux)then + LWRadCanopy = emc*sb*canopyTemp**4_i4b ! longwave radiation emitted from the canopy (W m-2) else - nFlux=1 ! compute analytical derivatives + LWRadCanopy = 0._rkind + end if + LWRadGround = emg*sb*groundTemp**4_i4b ! longwave radiation emitted at the ground surface (W m-2) + + ! compute fluxes originating from the atmosphere + LWRadUbound2Canopy = (emc + (1._rkind - emc)*(1._rkind - emg)*emc)*LWRadUbound ! downward atmospheric longwave radiation absorbed by the canopy (W m-2) + LWRadUbound2Ground = (1._rkind - emc)*emg*LWRadUbound ! downward atmospheric longwave radiation absorbed by the ground (W m-2) + LWRadUbound2Ubound = (1._rkind - emc)*(1._rkind - emg)*(1._rkind - emc)*LWRadUbound ! atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) + + ! compute fluxes originating from the canopy + LWRadCanopy2Ubound = (1._rkind + (1._rkind - emc)*(1._rkind - emg))*LWRadCanopy ! longwave radiation emitted from canopy lost thru upper boundary (W m-2) + LWRadCanopy2Ground = emg*LWRadCanopy ! longwave radiation emitted from canopy absorbed by the ground (W m-2) + LWRadCanopy2Canopy = emc*(1._rkind - emg)*LWRadCanopy ! canopy longwave reflected from ground and absorbed by the canopy (W m-2) + + ! compute fluxes originating from the ground surface + LWRadGround2Ubound = (1._rkind - emc)*LWRadGround ! longwave radiation emitted from ground lost thru upper boundary (W m-2) + LWRadGround2Canopy = emc*LWRadGround ! longwave radiation emitted from ground and absorbed by the canopy (W m-2) + + ! compute net longwave radiation (W m-2) + LWNetCanopy = LWRadUbound2Canopy + LWRadGround2Canopy + LWRadCanopy2Canopy - 2._rkind*LWRadCanopy ! canopy + LWNetGround = LWRadUbound2Ground + LWRadCanopy2Ground - LWRadGround ! ground surface + LWNetUbound = LWRadUbound - LWRadUbound2Ubound - LWRadCanopy2Ubound - LWRadGround2Ubound ! upper boundary + + ! check the flux balance + fluxBalance = LWNetUbound - (LWNetCanopy + LWNetGround) + if(abs(fluxBalance) > fluxTolerance .and. checkLWBalance)then + print*, 'fluxBalance = ', fluxBalance + print*, 'emg, emc = ', emg, emc + print*, 'canopyTemp, groundTemp = ', canopyTemp, groundTemp + print*, 'LWRadUbound = ', LWRadUbound + print*, 'LWRadCanopy = ', LWRadCanopy + print*, 'LWRadGround = ', LWRadGround + print*, 'LWRadUbound2Canopy = ', LWRadUbound2Canopy + print*, 'LWRadUbound2Ground = ', LWRadUbound2Ground + print*, 'LWRadUbound2Ubound = ', LWRadUbound2Ubound + print*, 'LWRadCanopy2Ubound = ', LWRadCanopy2Ubound + print*, 'LWRadCanopy2Ground = ', LWRadCanopy2Ground + print*, 'LWRadCanopy2Canopy = ', LWRadCanopy2Canopy + print*, 'LWRadGround2Ubound = ', LWRadGround2Ubound + print*, 'LWRadGround2Canopy = ', LWRadGround2Canopy + print*, 'LWNetCanopy = ', LWNetCanopy + print*, 'LWNetGround = ', LWNetGround + print*, 'LWNetUbound = ', LWNetUbound + message=trim(message)//'flux imbalance' + err=20; return end if - - ! either one or multiple flux calls, depending on if using analytical or numerical derivatives - do itry=nFlux,1,-1 ! (work backwards to ensure all computed fluxes come from the un-perturbed case) - ! ------------------------------------------------------------------------------------- - ! state perturbations for numerical deriavtives with one-sided finite differences - ! note: no perturbations performed using analytical derivatives (nFlux=1) - ! ------------------------------------------------------------------------------------- - - ! identify the type of perturbation - select case(itry) - ! un-perturbed case - case(unperturbed) - TCan = canopyTemp - TGnd = groundTemp - ! perturb canopy temperature - case(perturbStateCanopy) - TCan = canopyTemp + dx - TGnd = groundTemp - ! perturb ground temperature - case(perturbStateGround) - TCan = canopyTemp - TGnd = groundTemp + dx - ! check for an unknown perturbation - case default; err=10; message=trim(message)//"unknown perturbation"; return - end select ! (type of perturbation) - - ! ------------------------------------------------------------------------------------- - ! calculation block (unperturbed fluxes returned [computed last]) - ! ------------------------------------------------------------------------------------- - ! NOTE: emc should be set to zero when not computing canopy fluxes - - ! compute longwave fluxes from canopy and the ground - if(computeVegFlux)then - LWRadCanopy = emc*sb*TCan**4_i4b ! longwave radiation emitted from the canopy (W m-2) - else - LWRadCanopy = 0._rkind - end if - LWRadGround = emg*sb*TGnd**4_i4b ! longwave radiation emitted at the ground surface (W m-2) - - ! compute fluxes originating from the atmosphere - LWRadUbound2Canopy = (emc + (1._rkind - emc)*(1._rkind - emg)*emc)*LWRadUbound ! downward atmospheric longwave radiation absorbed by the canopy (W m-2) - LWRadUbound2Ground = (1._rkind - emc)*emg*LWRadUbound ! downward atmospheric longwave radiation absorbed by the ground (W m-2) - LWRadUbound2Ubound = (1._rkind - emc)*(1._rkind - emg)*(1._rkind - emc)*LWRadUbound ! atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) - - ! compute fluxes originating from the canopy - LWRadCanopy2Ubound = (1._rkind + (1._rkind - emc)*(1._rkind - emg))*LWRadCanopy ! longwave radiation emitted from canopy lost thru upper boundary (W m-2) - LWRadCanopy2Ground = emg*LWRadCanopy ! longwave radiation emitted from canopy absorbed by the ground (W m-2) - LWRadCanopy2Canopy = emc*(1._rkind - emg)*LWRadCanopy ! canopy longwave reflected from ground and absorbed by the canopy (W m-2) - - ! compute fluxes originating from the ground surface - LWRadGround2Ubound = (1._rkind - emc)*LWRadGround ! longwave radiation emitted from ground lost thru upper boundary (W m-2) - LWRadGround2Canopy = emc*LWRadGround ! longwave radiation emitted from ground and absorbed by the canopy (W m-2) - - ! compute net longwave radiation (W m-2) - LWNetCanopy = LWRadUbound2Canopy + LWRadGround2Canopy + LWRadCanopy2Canopy - 2._rkind*LWRadCanopy ! canopy - LWNetGround = LWRadUbound2Ground + LWRadCanopy2Ground - LWRadGround ! ground surface - LWNetUbound = LWRadUbound - LWRadUbound2Ubound - LWRadCanopy2Ubound - LWRadGround2Ubound ! upper boundary - - ! check the flux balance - fluxBalance = LWNetUbound - (LWNetCanopy + LWNetGround) - if(abs(fluxBalance) > fluxTolerance .and. checkLWBalance)then - print*, 'fluxBalance = ', fluxBalance - print*, 'emg, emc = ', emg, emc - print*, 'TCan, TGnd = ', TCan, TGnd - print*, 'LWRadUbound = ', LWRadUbound - print*, 'LWRadCanopy = ', LWRadCanopy - print*, 'LWRadGround = ', LWRadGround - print*, 'LWRadUbound2Canopy = ', LWRadUbound2Canopy - print*, 'LWRadUbound2Ground = ', LWRadUbound2Ground - print*, 'LWRadUbound2Ubound = ', LWRadUbound2Ubound - print*, 'LWRadCanopy2Ubound = ', LWRadCanopy2Ubound - print*, 'LWRadCanopy2Ground = ', LWRadCanopy2Ground - print*, 'LWRadCanopy2Canopy = ', LWRadCanopy2Canopy - print*, 'LWRadGround2Ubound = ', LWRadGround2Ubound - print*, 'LWRadGround2Canopy = ', LWRadGround2Canopy - print*, 'LWNetCanopy = ', LWNetCanopy - print*, 'LWNetGround = ', LWNetGround - print*, 'LWNetUbound = ', LWNetUbound - message=trim(message)//'flux imbalance' - err=20; return - end if - - ! -------------------------------------------------------------------------------------- - ! save perturbed fluxes to calculate numerical derivatives (one-sided finite difference) - ! -------------------------------------------------------------------------------------- - if(ixDerivMethod==numerical)then - select case(itry) ! (select type of perturbation) - case(unperturbed); exit - case(perturbStateCanopy) - LWNetCanopy_dStateCanopy = LWNetCanopy - LWNetGround_dStateCanopy = LWNetGround - case(perturbStateGround) - LWNetCanopy_dStateGround = LWNetCanopy - LWNetGround_dStateGround = LWNetGround - case default; err=10; message=trim(message)//"unknown perturbation"; return - end select ! (type of perturbation) - end if ! (if numerical) - - end do ! looping through different perturbations ! ------------------------------------------------------------------------------------- ! compute derivatives ! ------------------------------------------------------------------------------------- - select case(ixDerivMethod) - case(analytical) - ! compute initial derivatives - dLWRadCanopy_dTCanopy = 4._rkind*emc*sb*TCan**3_i4b - dLWRadGround_dTGround = 4._rkind*emg*sb*TGnd**3_i4b - ! compute analytical derivatives - dLWNetCanopy_dTCanopy = (emc*(1._rkind - emg) - 2._rkind)*dLWRadCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) - dLWNetGround_dTGround = -dLWRadGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetCanopy_dTGround = emc*dLWRadGround_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetGround_dTCanopy = emg*dLWRadCanopy_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) - case(numerical) - ! compute numerical derivatives (one-sided finite differences) - dLWNetCanopy_dTCanopy = (LWNetCanopy_dStateCanopy - LWNetCanopy)/dx ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) - dLWNetGround_dTGround = (LWNetGround_dStateGround - LWNetGround)/dx ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetCanopy_dTGround = (LWNetCanopy_dStateGround - LWNetCanopy)/dx ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetGround_dTCanopy = (LWNetGround_dStateCanopy - LWNetGround)/dx ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) - case default; err=10; message=trim(message)//"unknown method to calculate derivatives"; return - end select ! (type of method to calculate derivatives) + ! compute initial derivatives + dLWRadCanopy_dTCanopy = 4._rkind*emc*sb*canopyTemp**3_i4b + dLWRadGround_dTGround = 4._rkind*emg*sb*groundTemp**3_i4b + ! compute analytical derivatives + dLWNetCanopy_dTCanopy = (emc*(1._rkind - emg) - 2._rkind)*dLWRadCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) + dLWNetGround_dTGround = -dLWRadGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) + dLWNetCanopy_dTGround = emc*dLWRadGround_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) + dLWNetGround_dTCanopy = emg*dLWRadCanopy_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) end subroutine longwaveBal - ! ******************************************************************************************************* ! private subroutine aeroResist: compute aerodynamic resistances ! ******************************************************************************************************* subroutine aeroResist(& ! input: model control computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) - derivDesired, & ! intent(in): flag to indicate if analytical derivatives are desired ixVegTraits, & ! intent(in): choice of parameterization for vegetation roughness length and displacement height ixWindProfile, & ! intent(in): choice of canopy wind profile ixStability, & ! intent(in): choice of stability function @@ -1842,7 +1471,6 @@ subroutine aeroResist(& implicit none ! input: model control logical(lgt),intent(in) :: computeVegFlux ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) - logical(lgt),intent(in) :: derivDesired ! logical flag to indicate if analytical derivatives are desired integer(i4b),intent(in) :: ixVegTraits ! choice of parameterization for vegetation roughness length and displacement height integer(i4b),intent(in) :: ixWindProfile ! choice of canopy wind profile integer(i4b),intent(in) :: ixStability ! choice of stability function @@ -2005,7 +1633,6 @@ subroutine aeroResist(& ! compute the stability correction for resistance from canopy air space to air above the canopy (-) call aStability(& ! input - derivDesired, & ! input: logical flag to compute analytical derivatives ixStability, & ! input: choice of stability function ! input: forcing data, diagnostic and state variables mHeight, & ! input: measurement height (m) @@ -2094,7 +1721,6 @@ subroutine aeroResist(& ! NOTE: here we are interested in the windspeed at height z0Canopy+zeroPlaneDisplacement call aStability(& ! input - derivDesired, & ! input: logical flag to compute analytical derivatives ixStability, & ! input: choice of stability function ! input: forcing data, diagnostic and state variables referenceHeight, & ! input: height of the canopy air space temperature/wind (m) @@ -2150,7 +1776,6 @@ subroutine aeroResist(& ! compute ground stability correction call aStability(& ! input - derivDesired, & ! input: logical flag to compute analytical derivatives ixStability, & ! input: choice of stability function ! input: forcing data, diagnostic and state variables heightAboveGround, & ! input: measurement height above the ground surface (m) @@ -2186,51 +1811,36 @@ subroutine aeroResist(& windspdCanopyBottom = missingValue ! windspeed at the height of the bottom of the canopy (m s-1) end if ! (if no canopy) - - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! ----------------------------------------------------------------------------------------------------------------------------------------- - ! * compute derivatives - if(derivDesired)then ! if analytical derivatives are desired - - ! derivatives for the vegetation canopy - if(computeVegFlux) then ! (if vegetation is exposed) - ! ***** compute derivatives w.r.t. canopy temperature - ! NOTE: derivatives are zero because using canopy air space temperature - dCanopyResistance_dTCanopy = 0._rkind ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - dGroundResistance_dTCanopy = 0._rkind ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - ! ***** compute derivatives w.r.t. ground temperature (s m-1 K-1) - dGroundResistance_dTGround = -(groundResistanceNeutral*dGroundStabilityCorrection_dSfcTemp)/(groundStabilityCorrection**2_i4b) - ! ***** compute derivatives w.r.t. temperature of the canopy air space (s m-1 K-1) - ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - dCanopyResistance_dTCanair = -dCanopyStabilityCorrection_dCasTemp/(windspd*canopyExNeut*canopyStabilityCorrection**2_i4b) - ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - ! (compute derivative in NEUTRAL ground resistance w.r.t. canopy air temperature (s m-1 K-1)) - dFV_dT = windspd*canopyExNeut*dCanopyStabilityCorrection_dCasTemp/(sqrt(sfc2AtmExchangeCoeff_canopy)*2._rkind) ! d(frictionVelocity)/d(canopy air temperature) - dED_dT = dFV_dT*vkc*(heightCanopyTopAboveSnow - zeroPlaneDisplacement) ! d(eddyDiffusCanopyTop)d(canopy air temperature) - dGR_dT = -dED_dT*(tmp1 - tmp2)*heightCanopyTopAboveSnow*exp(windReductionFactor) / (windReductionFactor*eddyDiffusCanopyTop**2_i4b) ! d(groundResistanceNeutral)/d(canopy air temperature) - ! (stitch everything together -- product rule) - dGroundResistance_dTCanair = dGR_dT/groundStabilityCorrection - groundResistanceNeutral*dGroundStabilityCorrection_dCasTemp/(groundStabilityCorrection**2_i4b) - ! ***** compute resistances for non-vegetated surfaces (e.g., snow) - else - ! set canopy derivatives to zero (non-vegetated, remember) - dCanopyResistance_dTCanopy = 0._rkind - dGroundResistance_dTCanopy = 0._rkind - ! compute derivatives for ground resistance - dGroundResistance_dTGround = -dGroundStabilityCorrection_dSfcTemp/(windspd*groundExNeut*groundStabilityCorrection**2_i4b) - end if ! (switch between vegetated and non-vegetated surfaces) - - ! * analytical derivatives not desired + + ! derivatives for the vegetation canopy + if(computeVegFlux) then ! (if vegetation is exposed) + ! ***** compute derivatives w.r.t. canopy temperature + ! NOTE: derivatives are zero because using canopy air space temperature + dCanopyResistance_dTCanopy = 0._rkind ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + dGroundResistance_dTCanopy = 0._rkind ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + ! ***** compute derivatives w.r.t. ground temperature (s m-1 K-1) + dGroundResistance_dTGround = -(groundResistanceNeutral*dGroundStabilityCorrection_dSfcTemp)/(groundStabilityCorrection**2_i4b) + ! ***** compute derivatives w.r.t. temperature of the canopy air space (s m-1 K-1) + ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + dCanopyResistance_dTCanair = -dCanopyStabilityCorrection_dCasTemp/(windspd*canopyExNeut*canopyStabilityCorrection**2_i4b) + ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + ! (compute derivative in NEUTRAL ground resistance w.r.t. canopy air temperature (s m-1 K-1)) + dFV_dT = windspd*canopyExNeut*dCanopyStabilityCorrection_dCasTemp/(sqrt(sfc2AtmExchangeCoeff_canopy)*2._rkind) ! d(frictionVelocity)/d(canopy air temperature) + dED_dT = dFV_dT*vkc*(heightCanopyTopAboveSnow - zeroPlaneDisplacement) ! d(eddyDiffusCanopyTop)d(canopy air temperature) + dGR_dT = -dED_dT*(tmp1 - tmp2)*heightCanopyTopAboveSnow*exp(windReductionFactor) / (windReductionFactor*eddyDiffusCanopyTop**2_i4b) ! d(groundResistanceNeutral)/d(canopy air temperature) + ! (stitch everything together -- product rule) + dGroundResistance_dTCanair = dGR_dT/groundStabilityCorrection - groundResistanceNeutral*dGroundStabilityCorrection_dCasTemp/(groundStabilityCorrection**2_i4b) + ! ***** compute resistances for non-vegetated surfaces (e.g., snow) else - dGroundResistance_dTGround = missingValue - dGroundResistance_dTCanopy = missingValue - dCanopyResistance_dTCanopy = missingValue - end if + ! set canopy derivatives to zero (non-vegetated, remember) + dCanopyResistance_dTCanopy = 0._rkind + dGroundResistance_dTCanopy = 0._rkind + ! compute derivatives for ground resistance + dGroundResistance_dTGround = -dGroundStabilityCorrection_dSfcTemp/(windspd*groundExNeut*groundStabilityCorrection**2_i4b) + end if ! (switch between vegetated and non-vegetated surfaces) end subroutine aeroResist - ! ******************************************************************************************************* ! private subroutine soilResist: compute soil moisture factor controlling stomatal resistance ! ******************************************************************************************************* @@ -2261,8 +1871,8 @@ subroutine soilResist(& USE mDecisions_module, only: bigBucket ! named variable that defines the "bigBucket" groundwater parameterization implicit none ! input (model decisions) - integer(i4b),intent(in) :: ixSoilResist ! choice of function for the soil moisture control on stomatal resistance - integer(i4b),intent(in) :: ixGroundwater ! choice of groundwater representation + integer(i4b),intent(in) :: ixSoilResist ! choice of function for the soil moisture control on stomatal resistance + integer(i4b),intent(in) :: ixGroundwater ! choice of groundwater representation ! input (variables) real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head in each layer (m) real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid water in each layer (-) @@ -2280,12 +1890,12 @@ subroutine soilResist(& real(rkind),intent(out) :: wAvgTranspireLimitFac ! intent(out): weighted average of the transpiration limiting factor (-) real(rkind),intent(out) :: mLayerTranspireLimitFac(:) ! intent(out): transpiration limiting factor in each layer (-) real(rkind),intent(out) :: aquiferTranspireLimitFac ! intent(out): transpiration limiting factor for the aquifer (-) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! local variables real(rkind) :: gx ! stress function for the soil layers real(rkind),parameter :: verySmall=epsilon(gx) ! a very small number - integer(i4b) :: iLayer ! index of soil layer + integer(i4b) :: iLayer ! index of soil layer ! initialize error control err=0; message='soilResist/' @@ -2335,14 +1945,12 @@ subroutine soilResist(& end subroutine soilResist - ! ******************************************************************************** ! private subroutine turbFluxes: compute turbulent heat fluxes ! ******************************************************************************** subroutine turbFluxes(& ! input: model control computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) - ixDerivMethod, & ! intent(in): choice of method used to compute derivative (analytical or numerical) ! input: above-canopy forcing data airtemp, & ! intent(in): air temperature at some height above the surface (K) airpres, & ! intent(in): air pressure of the air above the vegetation canopy (Pa) @@ -2437,9 +2045,8 @@ subroutine turbFluxes(& ! ----------------------------------------------------------------------------------------------------------------------------------------- implicit none ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) - integer(i4b),intent(in) :: ixDerivMethod ! choice of method used to compute derivative (analytical or numerical) - ! input: above-canopy forcing data + logical(lgt),intent(in) :: computeVegFlux ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) + ! input: above-canopy forcing data real(rkind),intent(in) :: airtemp ! air temperature at some height above the surface (K) real(rkind),intent(in) :: airpres ! air pressure of the air above the vegetation canopy (Pa) real(rkind),intent(in) :: VPair ! vapor pressure of the air above the vegetation canopy (Pa) @@ -2469,11 +2076,11 @@ subroutine turbFluxes(& real(rkind),intent(in) :: stomResistSunlit ! stomatal resistance for sunlit leaves (s m-1) real(rkind),intent(in) :: stomResistShaded ! stomatal resistance for shaded leaves (s m-1) ! input: derivatives in scalar resistances - real(rkind),intent(in) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - real(rkind),intent(in) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind),intent(in) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - real(rkind),intent(in) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind),intent(in) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + real(rkind),intent(in) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + real(rkind),intent(in) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind),intent(in) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + real(rkind),intent(in) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind),intent(in) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) ! --------------------------------------------------------------------------------------------------------------------------------------------------------------- ! output: conductances -- used to test derivatives real(rkind),intent(out) :: leafConductance ! leaf conductance (m s-1) @@ -2623,41 +2230,36 @@ subroutine turbFluxes(& err=20; return endif - ! * compute derivatives + ! compute derivatives in individual conductances for sensible heat w.r.t. canopy temperature (m s-1 K-1) ! NOTE: it may be more efficient to compute these derivatives when computing resistances - if(ixDerivMethod == analytical)then - - ! compute derivatives in individual conductances for sensible heat w.r.t. canopy temperature (m s-1 K-1) - if(computeVegFlux)then - dEvapCond_dCanopyTemp = dCanopyWetFraction_dT*leafConductance ! derivative in evap conductance w.r.t. canopy temperature - dTransCond_dCanopyTemp = -dCanopyWetFraction_dT*leafConductanceTr ! derivative in trans conductance w.r.t. canopy temperature - dCanopyCond_dCanairTemp = -dCanopyResistance_dTCanair/canopyResistance**2_i4b ! derivative in canopy conductance w.r.t. canopy air emperature - dCanopyCond_dCanopyTemp = -dCanopyResistance_dTCanopy/canopyResistance**2_i4b ! derivative in canopy conductance w.r.t. canopy temperature - dGroundCondSH_dCanairTemp = -dGroundResistance_dTCanair/groundResistance**2_i4b ! derivative in ground conductance w.r.t. canopy air temperature - dGroundCondSH_dCanopyTemp = -dGroundResistance_dTCanopy/groundResistance**2_i4b ! derivative in ground conductance w.r.t. canopy temperature - dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2_i4b ! derivative in ground conductance w.r.t. ground temperature - else - dEvapCond_dCanopyTemp = 0._rkind ! derivative in evap conductance w.r.t. canopy temperature - dTransCond_dCanopyTemp = 0._rkind ! derivative in trans conductance w.r.t. canopy temperature - dCanopyCond_dCanairTemp = 0._rkind ! derivative in canopy conductance w.r.t. canopy air emperature - dCanopyCond_dCanopyTemp = 0._rkind ! derivative in canopy conductance w.r.t. canopy temperature - dGroundCondSH_dCanairTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy air temperature - dGroundCondSH_dCanopyTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy temperature - dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2_i4b ! derivative in ground conductance w.r.t. ground temperature - endif - - ! compute derivatives in individual conductances for latent heat w.r.t. canopy temperature (m s-1 K-1) - if(computeVegFlux)then - dGroundCondLH_dCanairTemp = -dGroundResistance_dTCanair/(groundResistance+soilResistance)**2_i4b ! derivative in ground conductance w.r.t. canopy air temperature - dGroundCondLH_dCanopyTemp = -dGroundResistance_dTCanopy/(groundResistance+soilResistance)**2_i4b ! derivative in ground conductance w.r.t. canopy temperature - dGroundCondLH_dGroundTemp = -dGroundResistance_dTGround/(groundResistance+soilResistance)**2_i4b ! derivative in ground conductance w.r.t. ground temperature - else - dGroundCondLH_dCanairTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy air temperature - dGroundCondLH_dCanopyTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy temperature - dGroundCondLH_dGroundTemp = -dGroundResistance_dTGround/(groundResistance+soilResistance)**2_i4b ! derivative in ground conductance w.r.t. ground temperature - end if + if(computeVegFlux)then + dEvapCond_dCanopyTemp = dCanopyWetFraction_dT*leafConductance ! derivative in evap conductance w.r.t. canopy temperature + dTransCond_dCanopyTemp = -dCanopyWetFraction_dT*leafConductanceTr ! derivative in trans conductance w.r.t. canopy temperature + dCanopyCond_dCanairTemp = -dCanopyResistance_dTCanair/canopyResistance**2_i4b ! derivative in canopy conductance w.r.t. canopy air emperature + dCanopyCond_dCanopyTemp = -dCanopyResistance_dTCanopy/canopyResistance**2_i4b ! derivative in canopy conductance w.r.t. canopy temperature + dGroundCondSH_dCanairTemp = -dGroundResistance_dTCanair/groundResistance**2_i4b ! derivative in ground conductance w.r.t. canopy air temperature + dGroundCondSH_dCanopyTemp = -dGroundResistance_dTCanopy/groundResistance**2_i4b ! derivative in ground conductance w.r.t. canopy temperature + dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2_i4b ! derivative in ground conductance w.r.t. ground temperature + else + dEvapCond_dCanopyTemp = 0._rkind ! derivative in evap conductance w.r.t. canopy temperature + dTransCond_dCanopyTemp = 0._rkind ! derivative in trans conductance w.r.t. canopy temperature + dCanopyCond_dCanairTemp = 0._rkind ! derivative in canopy conductance w.r.t. canopy air emperature + dCanopyCond_dCanopyTemp = 0._rkind ! derivative in canopy conductance w.r.t. canopy temperature + dGroundCondSH_dCanairTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy air temperature + dGroundCondSH_dCanopyTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy temperature + dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2_i4b ! derivative in ground conductance w.r.t. ground temperature + endif - endif ! (if computing analytical derivatives) + ! compute derivatives in individual conductances for latent heat w.r.t. canopy temperature (m s-1 K-1) + if(computeVegFlux)then + dGroundCondLH_dCanairTemp = -dGroundResistance_dTCanair/(groundResistance+soilResistance)**2_i4b ! derivative in ground conductance w.r.t. canopy air temperature + dGroundCondLH_dCanopyTemp = -dGroundResistance_dTCanopy/(groundResistance+soilResistance)**2_i4b ! derivative in ground conductance w.r.t. canopy temperature + dGroundCondLH_dGroundTemp = -dGroundResistance_dTGround/(groundResistance+soilResistance)**2_i4b ! derivative in ground conductance w.r.t. ground temperature + else + dGroundCondLH_dCanairTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy air temperature + dGroundCondLH_dCanopyTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy temperature + dGroundCondLH_dGroundTemp = -dGroundResistance_dTGround/(groundResistance+soilResistance)**2_i4b ! derivative in ground conductance w.r.t. ground temperature + end if ! ***** ! * compute sensible and latent heat fluxes, and derivatives... @@ -2701,124 +2303,118 @@ subroutine turbFluxes(& latHeatTotal = latHeatCanopyEvap + latHeatCanopyTrans + latHeatGround ! * compute derivatives - if(ixDerivMethod == analytical)then - - ! differentiate CANOPY fluxes - if(computeVegFlux)then - - ! compute derivatives of vapor pressure in the canopy air space w.r.t. all state variables - ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the canopy air space) - dPart1 = dCanopyCond_dCanairTemp*VPair + dGroundCondLH_dCanairTemp*satVP_GroundTemp*soilRelHumidity - dPart2 = -(dCanopyCond_dCanairTemp + dGroundCondLH_dCanairTemp)/(totalConductanceLH**2_i4b) - dVPCanopyAir_dTCanair = dPart1/totalConductanceLH + fPart_VP*dPart2 - ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the canopy) - dPart0 = (evapConductance + transConductance)*dSVPCanopy_dCanopyTemp + (dEvapCond_dCanopyTemp + dTransCond_dCanopyTemp)*satVP_CanopyTemp - dPart1 = dCanopyCond_dCanopyTemp*VPair + dPart0 + dGroundCondLH_dCanopyTemp*satVP_GroundTemp*soilRelHumidity - dPart2 = -(dCanopyCond_dCanopyTemp + dEvapCond_dCanopyTemp + dTransCond_dCanopyTemp + dGroundCondLH_dCanopyTemp)/(totalConductanceLH**2_i4b) - dVPCanopyAir_dTCanopy = dPart1/totalConductanceLH + fPart_VP*dPart2 - ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the ground) - dPart1 = dGroundCondLH_dGroundTemp*satVP_GroundTemp*soilRelHumidity + groundConductanceLH*dSVPGround_dGroundTemp*soilRelHumidity - dPart2 = -dGroundCondLH_dGroundTemp/(totalConductanceLH**2_i4b) - dVPCanopyAir_dTGround = dPart1/totalConductanceLH + fPart_VP*dPart2 - ! (derivative of vapor pressure in the canopy air space w.r.t. wetted fraction of the canopy) - dPart1 = (leafConductance - leafConductanceTr)*satVP_CanopyTemp - dPart2 = -(leafConductance - leafConductanceTr)/(totalConductanceLH**2_i4b) - dVPCanopyAir_dWetFrac = dPart1/totalConductanceLH + fPart_VP*dPart2 - dVPCanopyAir_dCanWat = dVPCanopyAir_dWetFrac*dCanopyWetFraction_dWat - - ! sensible heat from the canopy to the atmosphere - dSenHeatTotal_dTCanair = -volHeatCapacityAir*canopyConductance - volHeatCapacityAir*dCanopyCond_dCanairTemp*(canairTemp - airtemp) - dSenHeatTotal_dTCanopy = -volHeatCapacityAir*dCanopyCond_dCanopyTemp*(canairTemp - airtemp) - dSenHeatTotal_dTGround = 0._rkind - - ! sensible heat from the canopy to the canopy air space - dSenHeatCanopy_dTCanair = volHeatCapacityAir*leafConductance - dSenHeatCanopy_dTCanopy = -volHeatCapacityAir*leafConductance - dSenHeatCanopy_dTGround = 0._rkind - - ! sensible heat from the ground to the canopy air space - dSenHeatGround_dTCanair = -volHeatCapacityAir*dGroundCondSH_dCanairTemp*(groundTemp - canairTemp) + volHeatCapacityAir*groundConductanceSH - dSenHeatGround_dTCanopy = -volHeatCapacityAir*dGroundCondSH_dCanopyTemp*(groundTemp - canairTemp) - dSenHeatGround_dTGround = -volHeatCapacityAir*dGroundCondSH_dGroundTemp*(groundTemp - canairTemp) - volHeatCapacityAir*groundConductanceSH - - ! latent heat associated with canopy evaporation - ! (initial calculations) - fPart1 = -latHeatSubVapCanopy*latentHeatConstant*evapConductance - dPart1 = -latHeatSubVapCanopy*latentHeatConstant*dEvapCond_dCanopyTemp - fPart2 = satVP_CanopyTemp - VP_CanopyAir - dPart2 = dSVPCanopy_dCanopyTemp - dVPCanopyAir_dTCanopy - ! (derivatives) - dLatHeatCanopyEvap_dTCanair = fPart1*(-dVPCanopyAir_dTCanair) - dLatHeatCanopyEvap_dTCanopy = fPart1*dpart2 + fPart2*dPart1 - dLatHeatCanopyEvap_dTGround = fPart1*(-dVPCanopyAir_dTGround) - - ! latent heat associated with canopy transpiration - ! (initial calculations) - fPart1 = -LH_vap*latentHeatConstant*transConductance - dPart1 = -LH_vap*latentHeatConstant*dTransCond_dCanopyTemp - ! (derivatives) - dLatHeatCanopyTrans_dTCanair = fPart1*(-dVPCanopyAir_dTCanair) - dLatHeatCanopyTrans_dTCanopy = fPart1*dPart2 + fPart2*dPart1 - dLatHeatCanopyTrans_dTGround = fPart1*(-dVPCanopyAir_dTGround) - - ! latent heat flux from the ground - fPart1 = -latHeatSubVapGround*latentHeatConstant*groundConductanceLH ! function of the first part - fPart2 = (satVP_GroundTemp*soilRelHumidity - VP_CanopyAir) ! function of the second part - dLatHeatGroundEvap_dTCanair = -latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dCanairTemp*fPart2 - dVPCanopyAir_dTCanair*fPart1 - dLatHeatGroundEvap_dTCanopy = -latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dCanopyTemp*fPart2 - dVPCanopyAir_dTCanopy*fPart1 - dLatHeatGroundEvap_dTGround = -latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dGroundTemp*fPart2 + (dSVPGround_dGroundTemp*soilRelHumidity - dVPCanopyAir_dTGround)*fPart1 - - ! latent heat associated with canopy evaporation w.r.t. wetted fraction of the canopy - dPart1 = -latHeatSubVapCanopy*latentHeatConstant*leafConductance - fPart1 = dPart1*canopyWetFraction - dLatHeatCanopyEvap_dWetFrac = dPart1*(satVP_CanopyTemp - VP_CanopyAir) + fPart1*(-dVPCanopyAir_dWetFrac) - - ! latent heat associated with canopy transpiration w.r.t. wetted fraction of the canopy - dPart1 = LH_vap*latentHeatConstant*leafConductanceTr ! NOTE: positive, since (1 - wetFrac) - fPart1 = -dPart1*(1._rkind - canopyWetFraction) - dLatHeatCanopyTrans_dWetFrac = dPart1*(satVP_CanopyTemp - VP_CanopyAir) + fPart1*(-dVPCanopyAir_dWetFrac) - - ! latent heat associated with canopy transpiration w.r.t. canopy total water - dLatHeatCanopyTrans_dCanWat = dLatHeatCanopyTrans_dWetFrac*dCanopyWetFraction_dWat ! (J s-1 kg-1) - - else ! canopy is undefined - - ! set derivatives for canopy fluxes to zero (no canopy, so fluxes are undefined) - dSenHeatTotal_dTCanair = 0._rkind - dSenHeatTotal_dTCanopy = 0._rkind - dSenHeatTotal_dTGround = 0._rkind - dSenHeatCanopy_dTCanair = 0._rkind - dSenHeatCanopy_dTCanopy = 0._rkind - dSenHeatCanopy_dTGround = 0._rkind - dLatHeatCanopyEvap_dTCanair = 0._rkind - dLatHeatCanopyEvap_dTCanopy = 0._rkind - dLatHeatCanopyEvap_dTGround = 0._rkind - dLatHeatCanopyTrans_dTCanair = 0._rkind - dLatHeatCanopyTrans_dTCanopy = 0._rkind - dLatHeatCanopyTrans_dTGround = 0._rkind - - ! set derivatives for wetted area and canopy transpiration to zero (no canopy, so fluxes are undefined) - dLatHeatCanopyEvap_dWetFrac = 0._rkind - dLatHeatCanopyEvap_dCanWat = 0._rkind - dLatHeatCanopyTrans_dCanWat = 0._rkind - dVPCanopyAir_dCanWat = 0._rkind - - ! set derivatives for ground fluxes w.r.t canopy temperature to zero (no canopy, so fluxes are undefined) - dSenHeatGround_dTCanair = 0._rkind - dSenHeatGround_dTCanopy = 0._rkind - dLatHeatGroundEvap_dTCanair = 0._rkind - dLatHeatGroundEvap_dTCanopy = 0._rkind - - ! compute derivatives for the ground fluxes w.r.t. ground temperature - dSenHeatGround_dTGround = (-volHeatCapacityAir*dGroundCondSH_dGroundTemp)*(groundTemp - airtemp) + & ! d(ground sensible heat flux)/d(ground temp) - (-volHeatCapacityAir*groundConductanceSH) - dLatHeatGroundEvap_dTGround = (-latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dGroundTemp)*(satVP_GroundTemp*soilRelHumidity - VPair) + & ! d(ground latent heat flux)/d(ground temp) - (-latHeatSubVapGround*latentHeatConstant*groundConductanceLH)*dSVPGround_dGroundTemp*soilRelHumidity - - end if ! (if canopy is defined) - - end if ! (if computing analytical derivatives) + ! differentiate CANOPY fluxes + if(computeVegFlux)then + ! compute derivatives of vapor pressure in the canopy air space w.r.t. all state variables + ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the canopy air space) + dPart1 = dCanopyCond_dCanairTemp*VPair + dGroundCondLH_dCanairTemp*satVP_GroundTemp*soilRelHumidity + dPart2 = -(dCanopyCond_dCanairTemp + dGroundCondLH_dCanairTemp)/(totalConductanceLH**2_i4b) + dVPCanopyAir_dTCanair = dPart1/totalConductanceLH + fPart_VP*dPart2 + ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the canopy) + dPart0 = (evapConductance + transConductance)*dSVPCanopy_dCanopyTemp + (dEvapCond_dCanopyTemp + dTransCond_dCanopyTemp)*satVP_CanopyTemp + dPart1 = dCanopyCond_dCanopyTemp*VPair + dPart0 + dGroundCondLH_dCanopyTemp*satVP_GroundTemp*soilRelHumidity + dPart2 = -(dCanopyCond_dCanopyTemp + dEvapCond_dCanopyTemp + dTransCond_dCanopyTemp + dGroundCondLH_dCanopyTemp)/(totalConductanceLH**2_i4b) + dVPCanopyAir_dTCanopy = dPart1/totalConductanceLH + fPart_VP*dPart2 + ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the ground) + dPart1 = dGroundCondLH_dGroundTemp*satVP_GroundTemp*soilRelHumidity + groundConductanceLH*dSVPGround_dGroundTemp*soilRelHumidity + dPart2 = -dGroundCondLH_dGroundTemp/(totalConductanceLH**2_i4b) + dVPCanopyAir_dTGround = dPart1/totalConductanceLH + fPart_VP*dPart2 + ! (derivative of vapor pressure in the canopy air space w.r.t. wetted fraction of the canopy) + dPart1 = (leafConductance - leafConductanceTr)*satVP_CanopyTemp + dPart2 = -(leafConductance - leafConductanceTr)/(totalConductanceLH**2_i4b) + dVPCanopyAir_dWetFrac = dPart1/totalConductanceLH + fPart_VP*dPart2 + dVPCanopyAir_dCanWat = dVPCanopyAir_dWetFrac*dCanopyWetFraction_dWat + + ! sensible heat from the canopy to the atmosphere + dSenHeatTotal_dTCanair = -volHeatCapacityAir*canopyConductance - volHeatCapacityAir*dCanopyCond_dCanairTemp*(canairTemp - airtemp) + dSenHeatTotal_dTCanopy = -volHeatCapacityAir*dCanopyCond_dCanopyTemp*(canairTemp - airtemp) + dSenHeatTotal_dTGround = 0._rkind + + ! sensible heat from the canopy to the canopy air space + dSenHeatCanopy_dTCanair = volHeatCapacityAir*leafConductance + dSenHeatCanopy_dTCanopy = -volHeatCapacityAir*leafConductance + dSenHeatCanopy_dTGround = 0._rkind + + ! sensible heat from the ground to the canopy air space + dSenHeatGround_dTCanair = -volHeatCapacityAir*dGroundCondSH_dCanairTemp*(groundTemp - canairTemp) + volHeatCapacityAir*groundConductanceSH + dSenHeatGround_dTCanopy = -volHeatCapacityAir*dGroundCondSH_dCanopyTemp*(groundTemp - canairTemp) + dSenHeatGround_dTGround = -volHeatCapacityAir*dGroundCondSH_dGroundTemp*(groundTemp - canairTemp) - volHeatCapacityAir*groundConductanceSH + + ! latent heat associated with canopy evaporation + ! (initial calculations) + fPart1 = -latHeatSubVapCanopy*latentHeatConstant*evapConductance + dPart1 = -latHeatSubVapCanopy*latentHeatConstant*dEvapCond_dCanopyTemp + fPart2 = satVP_CanopyTemp - VP_CanopyAir + dPart2 = dSVPCanopy_dCanopyTemp - dVPCanopyAir_dTCanopy + ! (derivatives) + dLatHeatCanopyEvap_dTCanair = fPart1*(-dVPCanopyAir_dTCanair) + dLatHeatCanopyEvap_dTCanopy = fPart1*dpart2 + fPart2*dPart1 + dLatHeatCanopyEvap_dTGround = fPart1*(-dVPCanopyAir_dTGround) + + ! latent heat associated with canopy transpiration + ! (initial calculations) + fPart1 = -LH_vap*latentHeatConstant*transConductance + dPart1 = -LH_vap*latentHeatConstant*dTransCond_dCanopyTemp + ! (derivatives) + dLatHeatCanopyTrans_dTCanair = fPart1*(-dVPCanopyAir_dTCanair) + dLatHeatCanopyTrans_dTCanopy = fPart1*dPart2 + fPart2*dPart1 + dLatHeatCanopyTrans_dTGround = fPart1*(-dVPCanopyAir_dTGround) + + ! latent heat flux from the ground + fPart1 = -latHeatSubVapGround*latentHeatConstant*groundConductanceLH ! function of the first part + fPart2 = (satVP_GroundTemp*soilRelHumidity - VP_CanopyAir) ! function of the second part + dLatHeatGroundEvap_dTCanair = -latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dCanairTemp*fPart2 - dVPCanopyAir_dTCanair*fPart1 + dLatHeatGroundEvap_dTCanopy = -latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dCanopyTemp*fPart2 - dVPCanopyAir_dTCanopy*fPart1 + dLatHeatGroundEvap_dTGround = -latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dGroundTemp*fPart2 + (dSVPGround_dGroundTemp*soilRelHumidity - dVPCanopyAir_dTGround)*fPart1 + + ! latent heat associated with canopy evaporation w.r.t. wetted fraction of the canopy + dPart1 = -latHeatSubVapCanopy*latentHeatConstant*leafConductance + fPart1 = dPart1*canopyWetFraction + dLatHeatCanopyEvap_dWetFrac = dPart1*(satVP_CanopyTemp - VP_CanopyAir) + fPart1*(-dVPCanopyAir_dWetFrac) + + ! latent heat associated with canopy transpiration w.r.t. wetted fraction of the canopy + dPart1 = LH_vap*latentHeatConstant*leafConductanceTr ! NOTE: positive, since (1 - wetFrac) + fPart1 = -dPart1*(1._rkind - canopyWetFraction) + dLatHeatCanopyTrans_dWetFrac = dPart1*(satVP_CanopyTemp - VP_CanopyAir) + fPart1*(-dVPCanopyAir_dWetFrac) + + ! latent heat associated with canopy transpiration w.r.t. canopy total water + dLatHeatCanopyTrans_dCanWat = dLatHeatCanopyTrans_dWetFrac*dCanopyWetFraction_dWat ! (J s-1 kg-1) + + else ! canopy is undefined + + ! set derivatives for canopy fluxes to zero (no canopy, so fluxes are undefined) + dSenHeatTotal_dTCanair = 0._rkind + dSenHeatTotal_dTCanopy = 0._rkind + dSenHeatTotal_dTGround = 0._rkind + dSenHeatCanopy_dTCanair = 0._rkind + dSenHeatCanopy_dTCanopy = 0._rkind + dSenHeatCanopy_dTGround = 0._rkind + dLatHeatCanopyEvap_dTCanair = 0._rkind + dLatHeatCanopyEvap_dTCanopy = 0._rkind + dLatHeatCanopyEvap_dTGround = 0._rkind + dLatHeatCanopyTrans_dTCanair = 0._rkind + dLatHeatCanopyTrans_dTCanopy = 0._rkind + dLatHeatCanopyTrans_dTGround = 0._rkind + + ! set derivatives for wetted area and canopy transpiration to zero (no canopy, so fluxes are undefined) + dLatHeatCanopyEvap_dWetFrac = 0._rkind + dLatHeatCanopyEvap_dCanWat = 0._rkind + dLatHeatCanopyTrans_dCanWat = 0._rkind + dVPCanopyAir_dCanWat = 0._rkind + + ! set derivatives for ground fluxes w.r.t canopy temperature to zero (no canopy, so fluxes are undefined) + dSenHeatGround_dTCanair = 0._rkind + dSenHeatGround_dTCanopy = 0._rkind + dLatHeatGroundEvap_dTCanair = 0._rkind + dLatHeatGroundEvap_dTCanopy = 0._rkind + + ! compute derivatives for the ground fluxes w.r.t. ground temperature + dSenHeatGround_dTGround = (-volHeatCapacityAir*dGroundCondSH_dGroundTemp)*(groundTemp - airtemp) + & ! d(ground sensible heat flux)/d(ground temp) + (-volHeatCapacityAir*groundConductanceSH) + dLatHeatGroundEvap_dTGround = (-latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dGroundTemp)*(satVP_GroundTemp*soilRelHumidity - VPair) + & ! d(ground latent heat flux)/d(ground temp) + (-latHeatSubVapGround*latentHeatConstant*groundConductanceLH)*dSVPGround_dGroundTemp*soilRelHumidity + end if ! (if canopy is defined) ! ***** ! * compute net turbulent fluxes, and derivatives... @@ -2829,55 +2425,33 @@ subroutine turbFluxes(& turbFluxCanopy = senHeatCanopy + latHeatCanopyEvap + latHeatCanopyTrans ! net turbulent flux at the canopy (W m-2) turbFluxGround = senHeatGround + latHeatGround ! net turbulent flux at the ground surface (W m-2) - ! * compute derivatives - if(ixDerivMethod == analytical)then - ! (energy derivatives) - dTurbFluxCanair_dTCanair = dSenHeatTotal_dTCanair - dSenHeatCanopy_dTCanair - dSenHeatGround_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxCanair_dTCanopy = dSenHeatTotal_dTCanopy - dSenHeatCanopy_dTCanopy - dSenHeatGround_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxCanair_dTGround = dSenHeatTotal_dTGround - dSenHeatCanopy_dTGround - dSenHeatGround_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanair = dSenHeatCanopy_dTCanair + dLatHeatCanopyEvap_dTCanair + dLatHeatCanopyTrans_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxCanopy_dTCanopy = dSenHeatCanopy_dTCanopy + dLatHeatCanopyEvap_dTCanopy + dLatHeatCanopyTrans_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxCanopy_dTGround = dSenHeatCanopy_dTGround + dLatHeatCanopyEvap_dTGround + dLatHeatCanopyTrans_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - dTurbFluxGround_dTCanair = dSenHeatGround_dTCanair + dLatHeatGroundEvap_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - dTurbFluxGround_dTCanopy = dSenHeatGround_dTCanopy + dLatHeatGroundEvap_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - dTurbFluxGround_dTGround = dSenHeatGround_dTGround + dLatHeatGroundEvap_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - ! (liquid water derivatives) - dLatHeatCanopyEvap_dCanWat = dLatHeatCanopyEvap_dWetFrac*dCanopyWetFraction_dWat ! derivative in latent heat of canopy evaporation w.r.t. canopy total water (W kg-1) - dLatHeatGroundEvap_dCanWat = latHeatSubVapGround*latentHeatConstant*groundConductanceLH*dVPCanopyAir_dCanWat ! derivative in latent heat of ground evaporation w.r.t. canopy total water (J kg-1 s-1) - ! (cross deriavtives) - dTurbFluxCanair_dCanWat = 0._rkind ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) - dTurbFluxCanopy_dCanWat = dLatHeatCanopyEvap_dCanWat + dLatHeatCanopyTrans_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - dTurbFluxGround_dCanWat = dLatHeatGroundEvap_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) - else ! (just make sure we return something) - ! (energy derivatives) - dTurbFluxCanair_dTCanair = 0._rkind - dTurbFluxCanair_dTCanopy = 0._rkind - dTurbFluxCanair_dTGround = 0._rkind - dTurbFluxCanopy_dTCanair = 0._rkind - dTurbFluxCanopy_dTCanopy = 0._rkind - dTurbFluxCanopy_dTGround = 0._rkind - dTurbFluxGround_dTCanair = 0._rkind - dTurbFluxGround_dTCanopy = 0._rkind - dTurbFluxGround_dTGround = 0._rkind - ! (liquid water derivatives) - dLatHeatCanopyEvap_dCanWat = 0._rkind - dLatHeatGroundEvap_dCanWat = 0._rkind - ! (cross deriavtives) - dTurbFluxCanair_dCanWat = 0._rkind - dTurbFluxCanopy_dCanWat = 0._rkind - dTurbFluxGround_dCanWat = 0._rkind - end if + ! * compute derivatives + ! (energy derivatives) + dTurbFluxCanair_dTCanair = dSenHeatTotal_dTCanair - dSenHeatCanopy_dTCanair - dSenHeatGround_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxCanair_dTCanopy = dSenHeatTotal_dTCanopy - dSenHeatCanopy_dTCanopy - dSenHeatGround_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxCanair_dTGround = dSenHeatTotal_dTGround - dSenHeatCanopy_dTGround - dSenHeatGround_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanair = dSenHeatCanopy_dTCanair + dLatHeatCanopyEvap_dTCanair + dLatHeatCanopyTrans_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxCanopy_dTCanopy = dSenHeatCanopy_dTCanopy + dLatHeatCanopyEvap_dTCanopy + dLatHeatCanopyTrans_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxCanopy_dTGround = dSenHeatCanopy_dTGround + dLatHeatCanopyEvap_dTGround + dLatHeatCanopyTrans_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + dTurbFluxGround_dTCanair = dSenHeatGround_dTCanair + dLatHeatGroundEvap_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + dTurbFluxGround_dTCanopy = dSenHeatGround_dTCanopy + dLatHeatGroundEvap_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + dTurbFluxGround_dTGround = dSenHeatGround_dTGround + dLatHeatGroundEvap_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + ! (liquid water derivatives) + dLatHeatCanopyEvap_dCanWat = dLatHeatCanopyEvap_dWetFrac*dCanopyWetFraction_dWat ! derivative in latent heat of canopy evaporation w.r.t. canopy total water (W kg-1) + dLatHeatGroundEvap_dCanWat = latHeatSubVapGround*latentHeatConstant*groundConductanceLH*dVPCanopyAir_dCanWat ! derivative in latent heat of ground evaporation w.r.t. canopy total water (J kg-1 s-1) + ! (cross deriavtives) + dTurbFluxCanair_dCanWat = 0._rkind ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxCanopy_dCanWat = dLatHeatCanopyEvap_dCanWat + dLatHeatCanopyTrans_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + dTurbFluxGround_dCanWat = dLatHeatGroundEvap_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) end subroutine turbFluxes - ! ******************************************************************************************************* ! private subroutine aStability: compute stability corrections for turbulent heat fluxes (-) ! ******************************************************************************************************* subroutine aStability(& ! input: control - computeDerivative, & ! input: logical flag to compute analytical derivatives - ixStability, & ! input: choice of stability function + ixStability, & ! input: choice of stability function ! input: forcing data, diagnostic and state variables mHeight, & ! input: measurement height (m) airTemp, & ! input: air temperature (K) @@ -2896,7 +2470,6 @@ subroutine aStability(& err, message ) ! output: error control implicit none ! input: control - logical(lgt),intent(in) :: computeDerivative ! flag to compute the derivative integer(i4b),intent(in) :: ixStability ! choice of stability function ! input: forcing data, diagnostic and state variables real(rkind),intent(in) :: mHeight ! measurement height (m) @@ -2931,30 +2504,20 @@ subroutine aStability(& sfcTemp, & ! input: surface temperature (K) windspd, & ! input: wind speed (m s-1) mHeight, & ! input: measurement height (m) - computeDerivative, & ! input: flag to compute the derivative ! output RiBulk, & ! output: bulk Richardson number (-) dRiBulk_dAirTemp, & ! output: derivative in the bulk Richardson number w.r.t. air temperature (K-1) dRiBulk_dSfcTemp, & ! output: derivative in the bulk Richardson number w.r.t. surface temperature (K-1) err,message) ! output: error control - ! set derivative to one if not computing it - if(.not.computeDerivative)then - dStabilityCorrection_dRich = 1._rkind - dStabilityCorrection_dAirTemp = 1._rkind - dStabilityCorrection_dSfcTemp = 1._rkind - end if - ! ***** process unstable cases if(RiBulk<0._rkind)then ! compute surface-atmosphere exchange coefficient (-) stabilityCorrection = sqrt(1._rkind - 16._rkind*RiBulk) ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) - if(computeDerivative)then - dStabilityCorrection_dRich = (-16._rkind) * 0.5_rkind*(1._rkind - 16._rkind*RiBulk)**(-0.5_rkind) - dStabilityCorrection_dAirTemp = dRiBulk_dAirTemp * dStabilityCorrection_dRich - dStabilityCorrection_dSfcTemp = dRiBulk_dSfcTemp * dStabilityCorrection_dRich - end if + dStabilityCorrection_dRich = (-16._rkind) * 0.5_rkind*(1._rkind - 16._rkind*RiBulk)**(-0.5_rkind) + dStabilityCorrection_dAirTemp = dRiBulk_dAirTemp * dStabilityCorrection_dRich + dStabilityCorrection_dSfcTemp = dRiBulk_dSfcTemp * dStabilityCorrection_dRich return end if @@ -2967,10 +2530,8 @@ subroutine aStability(& if(RiBulk < critRichNumber) stabilityCorrection = (1._rkind - 5._rkind*RiBulk)**2_i4b if(RiBulk >= critRichNumber) stabilityCorrection = verySmall ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) - if(computeDerivative)then - if(RiBulk < critRichNumber) dStabilityCorrection_dRich = -10._rkind*(1._rkind - 5._rkind*RiBulk) - if(RiBulk >= critRichNumber) dStabilityCorrection_dRich = verySmall - end if + if(RiBulk < critRichNumber) dStabilityCorrection_dRich = -10._rkind*(1._rkind - 5._rkind*RiBulk) + if(RiBulk >= critRichNumber) dStabilityCorrection_dRich = verySmall ! (Louis 1979) case(louisInversePower) @@ -2980,9 +2541,7 @@ subroutine aStability(& stabilityCorrection = 1._rkind / ( (1._rkind + bprime*RiBulk)**2_i4b ) if(stabilityCorrection < epsilon(stabilityCorrection)) stabilityCorrection = epsilon(stabilityCorrection) ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) - if(computeDerivative)then - dStabilityCorrection_dRich = bprime * (-2._rkind)*(1._rkind + bprime*RiBulk)**(-3._rkind) - end if + dStabilityCorrection_dRich = bprime * (-2._rkind)*(1._rkind + bprime*RiBulk)**(-3._rkind) ! (Mahrt 1987) case(mahrtExponential) @@ -2990,9 +2549,7 @@ subroutine aStability(& stabilityCorrection = exp(-Mahrt87_eScale * RiBulk) if(stabilityCorrection < epsilon(stabilityCorrection)) stabilityCorrection = epsilon(stabilityCorrection) ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) - if(computeDerivative)then - dStabilityCorrection_dRich = (-Mahrt87_eScale) * exp(-Mahrt87_eScale * RiBulk) - end if + dStabilityCorrection_dRich = (-Mahrt87_eScale) * exp(-Mahrt87_eScale * RiBulk) ! (return error if the stability correction method is not found) case default @@ -3002,14 +2559,11 @@ subroutine aStability(& ! get the stability correction with respect to air temperature and surface temperature ! NOTE: air temperature is used for canopy air temperature, which is a model state variable - if(computeDerivative)then - dStabilityCorrection_dAirTemp = dRiBulk_dAirTemp * dStabilityCorrection_dRich - dStabilityCorrection_dSfcTemp = dRiBulk_dSfcTemp * dStabilityCorrection_dRich - end if + dStabilityCorrection_dAirTemp = dRiBulk_dAirTemp * dStabilityCorrection_dRich + dStabilityCorrection_dSfcTemp = dRiBulk_dSfcTemp * dStabilityCorrection_dRich end subroutine aStability - ! ******************************************************************************************************* ! private subroutine bulkRichardson: compute bulk Richardson number ! ******************************************************************************************************* @@ -3019,8 +2573,7 @@ subroutine bulkRichardson(& sfcTemp, & ! input: surface temperature (K) windspd, & ! input: wind speed (m s-1) mHeight, & ! input: measurement height (m) - computeDerivative, & ! input: flag to compute the derivative - ! output + ! output RiBulk, & ! output: bulk Richardson number (-) dRiBulk_dAirTemp, & ! output: derivative in the bulk Richardson number w.r.t. air temperature (K-1) dRiBulk_dSfcTemp, & ! output: derivative in the bulk Richardson number w.r.t. surface temperature (K-1) @@ -3031,7 +2584,6 @@ subroutine bulkRichardson(& real(rkind),intent(in) :: sfcTemp ! surface temperature (K) real(rkind),intent(in) :: windspd ! wind speed (m s-1) real(rkind),intent(in) :: mHeight ! measurement height (m) -logical(lgt),intent(in) :: computeDerivative ! flag to compute the derivative ! output real(rkind),intent(inout) :: RiBulk ! bulk Richardson number (-) real(rkind),intent(out) :: dRiBulk_dAirTemp ! derivative in the bulk Richardson number w.r.t. air temperature (K-1) @@ -3051,14 +2603,10 @@ subroutine bulkRichardson(& ! compute the Richardson number RiBulk = (T_grad/T_mean) * RiMult ! compute the derivative in the Richardson number - if(computeDerivative)then - dRiBulk_dAirTemp = RiMult/T_mean - RiMult*T_grad/(0.5_rkind*((airtemp + sfcTemp)**2_i4b)) - dRiBulk_dSfcTemp = -RiMult/T_mean - RiMult*T_grad/(0.5_rkind*((airtemp + sfcTemp)**2_i4b)) - else - dRiBulk_dAirTemp = 1._rkind - dRiBulk_dSfcTemp = 1._rkind - end if + dRiBulk_dAirTemp = RiMult/T_mean - RiMult*T_grad/(0.5_rkind*((airtemp + sfcTemp)**2_i4b)) + dRiBulk_dSfcTemp = -RiMult/T_mean - RiMult*T_grad/(0.5_rkind*((airtemp + sfcTemp)**2_i4b)) + end subroutine bulkRichardson -end module vegNrgFlux_module +end module vegNrgFlux_module \ No newline at end of file From 0ee96f6c16b6e88799517d593c5ce7ad9cbca7fb Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 29 Jun 2023 17:41:35 +0900 Subject: [PATCH 0783/1472] error in vegNrgFlux wettedFrac deriv without smoothing (wasn't being used) --- build/source/dshare/globalData.f90 | 4 ++-- build/source/engine/vegLiqFlux.f90 | 12 ++++++------ build/source/engine/vegNrgFlux.f90 | 19 +++++++++---------- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 index c701aae94..320b93d6d 100755 --- a/build/source/dshare/globalData.f90 +++ b/build/source/dshare/globalData.f90 @@ -132,8 +132,8 @@ MODULE globalData integer(i4b),parameter,public :: ixBandMatrix=1002 ! named variable for the band diagonal matrix ! define indices describing the first and last layers of the Jacobian to print (for debugging) - integer(i4b),parameter,public :: iJac1=16 ! first layer of the Jacobian to print - integer(i4b),parameter,public :: iJac2=20 ! last layer of the Jacobian to print + integer(i4b),parameter,public :: iJac1=1 ! first layer of the Jacobian to print + integer(i4b),parameter,public :: iJac2=100 ! last layer of the Jacobian to print ! define limit checks real(rkind),parameter,public :: verySmall=tiny(1.0_rkind) ! a very small number diff --git a/build/source/engine/vegLiqFlux.f90 b/build/source/engine/vegLiqFlux.f90 index 72a348223..46de9f949 100644 --- a/build/source/engine/vegLiqFlux.f90 +++ b/build/source/engine/vegLiqFlux.f90 @@ -67,16 +67,16 @@ subroutine vegLiqFlux(& implicit none ! input logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) - real(rkind),intent(in) :: scalarCanopyLiqTrial ! trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) - real(rkind),intent(in) :: scalarRainfall ! rainfall (kg m-2 s-1) + real(rkind),intent(in) :: scalarCanopyLiqTrial ! trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) + real(rkind),intent(in) :: scalarRainfall ! rainfall (kg m-2 s-1) ! input-output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for the local basin ! output - real(rkind),intent(out) :: scalarThroughfallRain ! rain that reaches the ground without ever touching the canopy (kg m-2 s-1) - real(rkind),intent(out) :: scalarCanopyLiqDrainage ! drainage of liquid water from the vegetation canopy (kg m-2 s-1) - real(rkind),intent(out) :: scalarThroughfallRainDeriv ! derivative in throughfall w.r.t. canopy liquid water (s-1) - real(rkind),intent(out) :: scalarCanopyLiqDrainageDeriv ! derivative in canopy drainage w.r.t. canopy liquid water (s-1) + real(rkind),intent(out) :: scalarThroughfallRain ! rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + real(rkind),intent(out) :: scalarCanopyLiqDrainage ! drainage of liquid water from the vegetation canopy (kg m-2 s-1) + real(rkind),intent(out) :: scalarThroughfallRainDeriv ! derivative in throughfall w.r.t. canopy liquid water (s-1) + real(rkind),intent(out) :: scalarCanopyLiqDrainageDeriv ! derivative in canopy drainage w.r.t. canopy liquid water (s-1) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------------------------------------------------------------ diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 552b36c62..5ff57be6e 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -1179,7 +1179,6 @@ subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFact real(rkind) :: smoothFuncDeriv ! derivative in the smoothing function w.r.t.canopy storage (kg-1 m2) real(rkind) :: verySmall=epsilon(1._rkind) ! a very small number ! -------------------------------------------------------------------------------------------------------------- - ! compute relative canopy water relativeCanopyWater = canopyLiq/canopyMax @@ -1192,7 +1191,6 @@ subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFact else rawWetFractionDeriv = 0._rkind end if - ! - canopy is at capacity (canopyWettingFactor) else rawCanopyWetFraction = canopyWettingFactor @@ -1204,13 +1202,16 @@ subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFact call logisticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) canopyWetFraction = rawCanopyWetFraction*smoothFunc ! logistic smoother else - canopyWetFraction = rawCanopyWetFraction - canopyWetFractionDeriv = rawWetFractionDeriv + canopyWetFraction = rawCanopyWetFraction end if ! compute derivative (product rule) - if(derDesire .and. smoothing)then ! NOTE: raw derivative is used if not smoothing - canopyWetFractionDeriv = rawWetFractionDeriv*smoothFunc + rawCanopyWetFraction*smoothFuncDeriv + if(derDesire)then + if(smoothing)then + canopyWetFractionDeriv = rawWetFractionDeriv*smoothFunc + rawCanopyWetFraction*smoothFuncDeriv + else ! raw derivative is used if not smoothing + canopyWetFractionDeriv = rawWetFractionDeriv + endif else canopyWetFractionDeriv = 0._rkind end if @@ -1223,7 +1224,7 @@ end subroutine wetFraction subroutine logisticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) implicit none ! dummy variables - logical(lgt),intent(in) :: derDesire ! flag to denote if analytical derivatives are desired + logical(lgt),intent(in) :: derDesire ! flag to denote if analytical derivatives are desired real(rkind),intent(in) :: canopyLiq ! liquid water content (kg m-2) real(rkind),intent(out) :: smoothFunc ! smoothing function (-) real(rkind),intent(out) :: smoothFuncDeriv ! derivative in smoothing function (kg-1 m-2) @@ -2209,8 +2210,6 @@ subroutine turbFluxes(& if(computeVegFlux)then evapConductance = canopyWetFraction*leafConductance transConductance = (1._rkind - canopyWetFraction) * leafConductanceTr - !write(*,'(a,10(f14.8,1x))') 'canopySunlitLAI, canopyShadedLAI, stomResistSunlit, stomResistShaded, leafResistance, canopyWetFraction = ', & - ! canopySunlitLAI, canopyShadedLAI, stomResistSunlit, stomResistShaded, leafResistance, canopyWetFraction else evapConductance = 0._rkind transConductance = 0._rkind @@ -2439,7 +2438,7 @@ subroutine turbFluxes(& ! (liquid water derivatives) dLatHeatCanopyEvap_dCanWat = dLatHeatCanopyEvap_dWetFrac*dCanopyWetFraction_dWat ! derivative in latent heat of canopy evaporation w.r.t. canopy total water (W kg-1) dLatHeatGroundEvap_dCanWat = latHeatSubVapGround*latentHeatConstant*groundConductanceLH*dVPCanopyAir_dCanWat ! derivative in latent heat of ground evaporation w.r.t. canopy total water (J kg-1 s-1) - ! (cross deriavtives) + ! (cross derivatives) dTurbFluxCanair_dCanWat = 0._rkind ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) dTurbFluxCanopy_dCanWat = dLatHeatCanopyEvap_dCanWat + dLatHeatCanopyTrans_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) dTurbFluxGround_dCanWat = dLatHeatGroundEvap_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) From 58ba2eca6da415944dfab06a4344827b5db698df Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 29 Jun 2023 23:05:41 +0900 Subject: [PATCH 0784/1472] just spaces --- build/source/dshare/globalData.f90 | 2 +- build/source/engine/computHeatCap.f90 | 6 ++--- build/source/engine/computJacob.f90 | 9 +------- build/source/engine/computJacobWithPrime.f90 | 7 ------ build/source/engine/getVectorz.f90 | 24 ++++++++++---------- build/source/engine/summaSolve4numrec.f90 | 1 - 6 files changed, 17 insertions(+), 32 deletions(-) diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 index 320b93d6d..d2b05e3c6 100755 --- a/build/source/dshare/globalData.f90 +++ b/build/source/dshare/globalData.f90 @@ -133,7 +133,7 @@ MODULE globalData ! define indices describing the first and last layers of the Jacobian to print (for debugging) integer(i4b),parameter,public :: iJac1=1 ! first layer of the Jacobian to print - integer(i4b),parameter,public :: iJac2=100 ! last layer of the Jacobian to print + integer(i4b),parameter,public :: iJac2=100 ! last layer of the Jacobian to print ! define limit checks real(rkind),parameter,public :: verySmall=tiny(1.0_rkind) ! a very small number diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index 2e8616bc1..49cca758c 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -466,9 +466,9 @@ subroutine computHeatCapAnalytic(& ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) if(computeVegFlux)then - heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component - Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component - Cp_ice*scalarCanopyIce/canopyDepth ! ice component + heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component + Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component + Cp_ice*scalarCanopyIce/canopyDepth ! ice component ! derivatives fLiq = scalarFracLiqVeg diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 87fe4fc01..512418e20 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -254,12 +254,10 @@ subroutine computJacob(& ! initialize error control err=0; message='computJacob/' - ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* ! * PART 0: PRELIMINARIES (INITIALIZE JACOBIAN AND COMPUTE TIME-VARIABLE DIAGONAL TERMS) ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - + ! get the number of state variables nState = size(dMat) @@ -292,12 +290,9 @@ subroutine computJacob(& ! define the form of the matrix select case(ixMatrix) - - ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* ! * PART 1: BAND MATRIX ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* case(ixBandMatrix) ! check @@ -649,11 +644,9 @@ subroutine computJacob(& end do endif - ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* ! * PART 2: FULL MATRIX ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* case(ixFullMatrix) ! check diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index c3998c165..5933fb583 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -288,11 +288,9 @@ subroutine computJacobWithPrime(& ! initialize error control err=0; message='computJacobWithPrime/' - ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* ! * PART 0: PRELIMINARIES (INITIALIZE JACOBIAN AND COMPUTE TIME-VARIABLE DIAGONAL TERMS) ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* ! get the number of state variables nState = size(dMat) @@ -335,12 +333,9 @@ subroutine computJacobWithPrime(& ! define the form of the matrix select case(ixMatrix) - - ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* ! * PART 1: BAND MATRIX ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* case(ixBandMatrix) ! check if(size(aJac,1)/=nBands .or. size(aJac,2)/=size(dMat))then @@ -694,11 +689,9 @@ subroutine computJacobWithPrime(& end do endif - ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* ! * PART 2: FULL MATRIX ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* case(ixFullMatrix) ! check diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index ffab3a646..a72e363e7 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -276,12 +276,12 @@ subroutine getScaling(& ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- ! scaling parameters - real(rkind),parameter :: fScaleLiq=0.01_rkind ! func eval: characteristic scale for volumetric liquid water content (-) - real(rkind),parameter :: fScaleMat=10._rkind ! func eval: characteristic scale for matric head (m) - real(rkind),parameter :: fScaleNrg=1000000._rkind ! func eval: characteristic scale for energy (J m-3) - real(rkind),parameter :: xScaleLiq=0.1_rkind ! state var: characteristic scale for volumetric liquid water content (-) - real(rkind),parameter :: xScaleMat=10._rkind ! state var: characteristic scale for matric head (m) - real(rkind),parameter :: xScaleTemp=1._rkind ! state var: characteristic scale for temperature (K) + real(rkind),parameter :: fScaleLiq=0.01_rkind ! func eval: characteristic scale for volumetric liquid water content (-) + real(rkind),parameter :: fScaleMat=10._rkind ! func eval: characteristic scale for matric head (m) + real(rkind),parameter :: fScaleNrg=1000000._rkind ! func eval: characteristic scale for energy (J m-3) + real(rkind),parameter :: xScaleLiq=0.1_rkind ! state var: characteristic scale for volumetric liquid water content (-) + real(rkind),parameter :: xScaleMat=10._rkind ! state var: characteristic scale for matric head (m) + real(rkind),parameter :: xScaleTemp=1._rkind ! state var: characteristic scale for temperature (K) ! state subsets integer(i4b) :: iLayer ! index of layer within the snow+soil domain integer(i4b) :: ixStateSubset ! index within the state subset @@ -356,8 +356,8 @@ subroutine getScaling(& where(ixStateType_subset==iname_nrgCanair) sMul = Cp_air*iden_air ! volumetric heat capacity of air (J m-3 K-1) where(ixStateType_subset==iname_nrgCanopy) sMul = volHeatCapVeg ! volumetric heat capacity of the vegetation (J m-3 K-1) - where(ixStateType_subset==iname_watCanopy) sMul = 1._rkind ! nothing else on the left hand side - where(ixStateType_subset==iname_liqCanopy) sMul = 1._rkind ! nothing else on the left hand side + where(ixStateType_subset==iname_watCanopy) sMul = 1._rkind ! nothing else on the left hand side + where(ixStateType_subset==iname_liqCanopy) sMul = 1._rkind ! nothing else on the left hand side ! compute terms in the Jacobian for vegetation (excluding fluxes) ! NOTE: This is computed outside the iteration loop because it does not depend on state variables @@ -365,8 +365,8 @@ subroutine getScaling(& ! NOTE: Use the "where" statement to generalize to multiple canopy layers (currently one canopy layer) where(ixStateType_subset==iname_nrgCanair) dMat = Cp_air*iden_air ! volumetric heat capacity of air (J m-3 K-1) where(ixStateType_subset==iname_nrgCanopy) dMat = realMissing ! populated within the iteration loop - where(ixStateType_subset==iname_watCanopy) dMat = 1._rkind ! nothing else on the left hand side - where(ixStateType_subset==iname_liqCanopy) dMat = 1._rkind ! nothing else on the left hand side + where(ixStateType_subset==iname_watCanopy) dMat = 1._rkind ! nothing else on the left hand side + where(ixStateType_subset==iname_liqCanopy) dMat = 1._rkind ! nothing else on the left hand side ! define the energy multiplier and diagonal elements for the state vector for residual calculations (snow-soil domain) if(nSnowSoilNrg>0)then @@ -381,8 +381,8 @@ subroutine getScaling(& if(nSnowSoilHyd>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) ixStateSubset = ixSnowSoilHyd(iLayer) ! index within the state vector - sMul(ixStateSubset) = 1._rkind ! state multiplier = 1 (nothing else on the left-hand-side) - dMat(ixStateSubset) = 1._rkind ! diagonal element = 1 (nothing else on the left-hand-side) + sMul(ixStateSubset) = 1._rkind ! state multiplier = 1 (nothing else on the left-hand-side) + dMat(ixStateSubset) = 1._rkind ! diagonal element = 1 (nothing else on the left-hand-side) end do ! looping through non-missing energy state variables in the snow+soil domain endif diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 35a0a87b3..0489fcbf1 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -309,7 +309,6 @@ subroutine summaSolve4numrec(& if(globalPrintFlag)& write(*,'(a,1x,10(e17.10,1x))') 'newtStepScaled = ', newtStepScaled(min(iJac1,nState):min(iJac2,nState)) - !print*, 'PAUSE'; read(*,*) ! ----- ! * update, evaluate, and refine the state vector... From ce81dae9e9d0d5a178423c2edd7e389756febbe4 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 30 Jun 2023 14:31:26 +0900 Subject: [PATCH 0785/1472] derivative for bulk heat capacity needs to be computed in tempAdjust since changes that value. --- build/source/engine/coupled_em.f90 | 2 +- build/source/engine/eval8summa.f90 | 30 ++++++------- build/source/engine/tempAdjust.f90 | 68 +++++++++++++----------------- build/source/engine/varSubstep.f90 | 2 +- 4 files changed, 46 insertions(+), 56 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index e4823750d..bf89fa247 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1198,7 +1198,7 @@ subroutine coupled_em(& ! identify the need to check the mass balance, both methods should work if tolerance coarse enough select case(ixNumericalMethod) - case(ida); checkMassBalance = .false. ! IDA gives instantaneous fluxes and were summed for an average flux, but if large time step, then average is not accurate enough to pass the check + case(ida); checkMassBalance = .false. ! IDA balance agreement levels are controlled by set tolerances case(kinsol, numrec); checkMassBalance = .true. ! KINSOL or numrec give finite difference dt_sub fluxes and were summed for an average flux case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or numrec (or itertive, which is numrec)'; return end select diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 28c0ac10d..e4f1ce4e1 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -244,28 +244,28 @@ subroutine eval8summa(& scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) - scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) + scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) ! enthalpy from the previous solution - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) ! soil compression - scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2 s-1) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in volumetric water content due to compression of soil (s-1) + scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2 s-1) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in volumetric water content due to compression of soil (s-1) ! derivatives - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential - dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature ! mapping - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) ! indices - heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & ! intent(out): heat capacity for snow and soil + heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),&! intent(out): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & ! intent(out): heat capacity for snow and soil ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control diff --git a/build/source/engine/tempAdjust.f90 b/build/source/engine/tempAdjust.f90 index 87da3cbc4..ac42fda60 100644 --- a/build/source/engine/tempAdjust.f90 +++ b/build/source/engine/tempAdjust.f90 @@ -65,7 +65,7 @@ subroutine tempAdjust(& implicit none ! ------------------------------------------------------------------------------------------------ ! input: derived parameters - real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) + real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) ! input/output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_dlength),intent(inout) :: prog_data ! model prognostic variables for a local HRU @@ -75,37 +75,36 @@ subroutine tempAdjust(& character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------ ! local variables for canopy thermodynamics - integer(i4b) :: iTry ! trial index - integer(i4b) :: iter ! iteration index - integer(i4b),parameter :: maxiter=100 ! maximum number of iterations - real(rkind) :: fLiq ! fraction of liquid water (-) - real(rkind) :: tempMin,tempMax ! solution constraints for temperature (K) - real(rkind) :: nrgMeltFreeze ! energy required to melt-freeze the water to the current canopy temperature (J m-3) - real(rkind) :: scalarCanopyWat ! total canopy water (kg m-2) - real(rkind) :: scalarCanopyIceOld ! canopy ice content after melt-freeze to the initial temperature (kg m-2) - real(rkind),parameter :: resNrgToler=0.1_rkind ! tolerance for the energy residual (J m-3) - real(rkind) :: f1,f2,x1,x2,fTry,xTry,fDer,xInc ! iteration variables - logical(lgt) :: fBis ! .true. if bisection + integer(i4b) :: iTry ! trial index + integer(i4b) :: iter ! iteration index + integer(i4b),parameter :: maxiter=100 ! maximum number of iterations + real(rkind) :: fLiq ! fraction of liquid water (-) + real(rkind) :: tempMin,tempMax ! solution constraints for temperature (K) + real(rkind) :: nrgMeltFreeze ! energy required to melt-freeze the water to the current canopy temperature (J m-3) + real(rkind) :: scalarCanopyWat ! total canopy water (kg m-2) + real(rkind) :: scalarCanopyIceOld ! canopy ice content after melt-freeze to the initial temperature (kg m-2) + real(rkind),parameter :: resNrgToler=0.1_rkind ! tolerance for the energy residual (J m-3) + real(rkind) :: f1,f2,x1,x2,fTry,xTry,fDer,xInc ! iteration variables + logical(lgt) :: fBis ! .true. if bisection ! ------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='tempAdjust/' ! ------------------------------------------------------------------------------------------------ ! associate variables in the data structure associate(& - ! model parameters for canopy thermodynamics (input) - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! intent(in): [dp] scaling factor for snow freezing curve (K) - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): [dp] specific heat of vegetation mass (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): [dp] maximum mass of vegetation (full foliage) (kg m-2) - + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! intent(in): [dp] scaling factor for snow freezing curve (K) + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): [dp] specific heat of vegetation mass (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): [dp] maximum mass of vegetation (full foliage) (kg m-2) ! state variables (input/output) scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1), & ! intent(inout): [dp] mass of liquid water on the vegetation canopy (kg m-2) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! intent(inout): [dp] mass of ice on the vegetation canopy (kg m-2) scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1), & ! intent(inout): [dp] temperature of the vegetation canopy (K) - ! diagnostic variables (output) - scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) & ! intent(out): [dp] volumetric heat capacity of the vegetation (J m-3 K-1) - + scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1), & ! intent(out): [dp] volumetric heat capacity of the vegetation (J m-3 K-1) + ! output: derivatives + dVolHtCapBulk_dCanWat => diag_data%var(iLookDIAG%dVolHtCapBulk_dCanWat)%dat(1), & ! intent(out): [dp] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTkCanopy => diag_data%var(iLookDIAG%dVolHtCapBulk_dTkCanopy)%dat(1) & ! intent(out): [dp] derivative in bulk heat capacity w.r.t. temperature ) ! associate variables in the data structures ! ----------------------------------------------------------------------------------------------------------------------------------------------------- @@ -127,6 +126,14 @@ subroutine tempAdjust(& Cp_water*scalarCanopyLiq/canopyDepth + & ! liquid water component Cp_ice*scalarCanopyIce/canopyDepth ! ice component + ! derivatives, off starting values since do not recompute scalarBulkVolHeatCapVeg + dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) + if(scalarCanopyTemp < Tfreeze)then !use dTheta_dTkCanopy = fLiq * scalarCanopyWatTrial/(iden_water*canopyDepth) + dVolHtCapBulk_dTkCanopy = (-Cp_ice + Cp_water) * scalarCanopyLiq/canopyDepth + else + dVolHtCapBulk_dTkCanopy = 0._rkind + endif + ! compute the energy required to melt-freeze the water to the current canopy temperature (J m-3) nrgMeltFreeze = LH_fus*(scalarCanopyIceOld - scalarCanopyIce)/canopyDepth @@ -142,8 +149,6 @@ subroutine tempAdjust(& ! compute new function based on newton step from the first function x2 = x1 + f1 / fDer f2 = resNrgFunc(x2,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) - !print*, 'x1, x2 = ', x1, x2 - !print*, 'f1, f2 = ', f1, f2 ! ensure that we bracket the root if(f1*f2 > 0._rkind)then @@ -154,16 +159,13 @@ subroutine tempAdjust(& x2 = x1 + sign(x2,xInc)*2._rkind f2 = resNrgFunc(x2,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) if(f1*f2 < 0._rkind)exit - ! check that we bracketed the root - ! (should get here in just a couple of expansions) + ! check that we bracketed the root (should get here in just a couple of expansions) if(iter==maxiter)then message=trim(message)//'unable to bracket the root' err=20; return end if end do ! trying to bracket the root end if ! first check that we bracketed the root - !print*, 'x1, x2 = ', x1, x2 - !print*, 'f1, f2 = ', f1, f2 ! define initial constraints if(x1 < x2)then @@ -180,14 +182,10 @@ subroutine tempAdjust(& xTry = 0.5_rkind*(x1 + x2) fTry = resNrgFunc(xTry,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) fDer = resNrgDer(xTry,scalarBulkVolHeatCapVeg,snowfrz_scale) - !print*, 'xTry = ', xTry - !print*, 'fTry = ', fTry ! check the functions at the limits (should be of opposing sign) !f1 = resNrgFunc(tempMax,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) !f2 = resNrgFunc(tempMin,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) - !print*, 'f1, f2 = ', f1, f2 - ! ----------------------------------------------------------------------------------------------------------------------------------------------------- ! iterate do iter=1,maxiter @@ -196,19 +194,16 @@ subroutine tempAdjust(& if(xTry <= tempMin .or. xTry >= tempMax)then xTry = 0.5_rkind*(tempMin + tempMax) ! new value fBis = .true. - ! value in range; use the newton step else xInc = fTry/fDer xTry = xTry + xInc fBis = .false. - end if ! (switch between bi-section and newton) ! compute new function and derivative fTry = resNrgFunc(xTry,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) fDer = resNrgDer(xTry,scalarBulkVolHeatCapVeg,snowfrz_scale) - !print*, 'tempMin, tempMax = ', tempMin, tempMax ! update limits if(fTry < 0._rkind)then @@ -220,11 +215,7 @@ subroutine tempAdjust(& ! check the functions at the limits (should be of opposing sign) !f1 = resNrgFunc(tempMax,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) !f2 = resNrgFunc(tempMin,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) - !print*, 'f1, f2 = ', f1, f2 - - ! print progress - !write(*,'(a,1x,i4,1x,l1,1x,e20.10,1x,4(f20.10,1x))') 'iter, fBis, fTry, xTry, xInc, tempMin, tempMax = ', iter, fBis, fTry, xTry, xInc, tempMin, tempMax - + ! check convergence if(abs(fTry) < resNrgToler) exit @@ -240,7 +231,6 @@ subroutine tempAdjust(& message=trim(message)//'unable to converge' err=20; return end if - end do ! iterating ! ----------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 598617a70..45455bfaa 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -416,7 +416,7 @@ subroutine varSubstep(& ! identify the need to check the mass balance select case(ixNumericalMethod) - case(ida); checkMassBalance = .false. ! IDA has instantaneous fluxes only so average will not balance over data window + case(ida); checkMassBalance = .false. ! IDA balance agreement levels are controlled by set tolerances case(kinsol, numrec); checkMassBalance = .true. ! (.not.scalarSolution) end select From 4877ef981eada2eaed928a63a60e0627a2dc4a96 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 3 Jul 2023 23:30:48 +0900 Subject: [PATCH 0786/1472] move bulkheat capacity to derivatives (and thermal conductivity), properly. should not change code results. --- build/source/dshare/get_ixname.f90 | 21 ++- build/source/dshare/multiconst.f90 | 2 +- build/source/dshare/popMetadat.f90 | 22 +-- build/source/dshare/var_lookup.f90 | 45 ++---- build/source/engine/computFlux.f90 | 37 +++-- build/source/engine/computHeatCap.f90 | 110 +++++++------ build/source/engine/computJacob.f90 | 39 ++--- build/source/engine/computJacobWithPrime.f90 | 41 ++--- build/source/engine/diagn_evar.f90 | 73 +++------ build/source/engine/eval8summa.f90 | 159 +++++++++++-------- build/source/engine/eval8summaWithPrime.f90 | 40 ++++- build/source/engine/opSplittin.f90 | 2 +- build/source/engine/ssdNrgFlux.f90 | 46 +++--- 13 files changed, 350 insertions(+), 287 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 16864eb5a..69eb740e3 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -493,16 +493,6 @@ function get_ixdiag(varName) case('scalarLambda_wetsoil' ); get_ixdiag = iLookDIAG%scalarLambda_wetsoil ! thermal conductivity of wet soil (W m-1) case('mLayerThermalC' ); get_ixdiag = iLookDIAG%mLayerThermalC ! thermal conductivity at the mid-point of each layer (W m-1 K-1) case('iLayerThermalC' ); get_ixdiag = iLookDIAG%iLayerThermalC ! thermal conductivity at the interface of each layer (W m-1 K-1) - ! energy derivatives that might be treated as constant if heat capacity and thermal conductivity not updated - case('dVolHtCapBulk_dPsi0' ); get_ixdiag = iLookDIAG%dVolHtCapBulk_dPsi0 ! derivative in bulk heat capacity w.r.t. matric potential - case('dVolHtCapBulk_dTheta' ); get_ixdiag = iLookDIAG%dVolHtCapBulk_dTheta ! derivative in bulk heat capacity w.r.t. volumetric water content - case('dVolHtCapBulk_dCanWat' ); get_ixdiag = iLookDIAG%dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. volumetric water content - case('dVolHtCapBulk_dTk' ); get_ixdiag = iLookDIAG%dVolHtCapBulk_dTk ! derivative in bulk heat capacity w.r.t. temperature - case('dVolHtCapBulk_dTkCanopy' ); get_ixdiag = iLookDIAG%dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature - case('dThermalC_dTempAbove' ); get_ixdiag = iLookDIAG%dThermalC_dTempAbove ! derivative in the thermal conductivity w.r.t. energy state in the layer above - case('dThermalC_dTempBelow' ); get_ixdiag = iLookDIAG%dThermalC_dTempBelow ! derivative in the thermal conductivity w.r.t. energy state in the layer above - case('dThermalC_dWatAbove' ); get_ixdiag = iLookDIAG%dThermalC_dWatAbove ! derivative in the thermal conductivity w.r.t. water state in the layer above - case('dThermalC_dWatBelow' ); get_ixdiag = iLookDIAG%dThermalC_dWatBelow ! derivative in the thermal conductivity w.r.t. water state in the layer above ! enthalpy case('scalarCanairEnthalpy' ); get_ixdiag = iLookDIAG%scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) case('scalarCanopyEnthalpy' ); get_ixdiag = iLookDIAG%scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) @@ -752,7 +742,16 @@ function get_ixderiv(varName) case('scalarCanopyLiqDeriv' ); get_ixderiv = iLookDERIV%scalarCanopyLiqDeriv ! derivative in (throughfall + canopy drainage) w.r.t. canopy liquid water (s-1) case('scalarThroughfallRainDeriv' ); get_ixderiv = iLookDERIV%scalarThroughfallRainDeriv ! derivative in throughfall w.r.t. canopy liquid water (s-1) case('scalarCanopyLiqDrainageDeriv' ); get_ixderiv = iLookDERIV%scalarCanopyLiqDrainageDeriv ! derivative in canopy drainage w.r.t. canopy liquid water (s-1) - ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below + ! energy derivatives that might be treated as constant if heat capacity and thermal conductivity not updated + case('dVolHtCapBulk_dPsi0' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dPsi0 ! derivative in bulk heat capacity w.r.t. matric potential + case('dVolHtCapBulk_dTheta' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTheta ! derivative in bulk heat capacity w.r.t. volumetric water content + case('dVolHtCapBulk_dCanWat' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. volumetric water content + case('dVolHtCapBulk_dTk' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTk ! derivative in bulk heat capacity w.r.t. temperature + case('dVolHtCapBulk_dTkCanopy' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature + case('dThermalC_dTempAbove' ); get_ixderiv = iLookDERIV%dThermalC_dTempAbove ! derivative in the thermal conductivity w.r.t. energy state in the layer above + case('dThermalC_dTempBelow' ); get_ixderiv = iLookDERIV%dThermalC_dTempBelow ! derivative in the thermal conductivity w.r.t. energy state in the layer above + case('dThermalC_dWatAbove' ); get_ixderiv = iLookDERIV%dThermalC_dWatAbove ! derivative in the thermal conductivity w.r.t. water state in the layer above + case('dThermalC_dWatBelow' ); get_ixderiv = iLookDERIV%dThermalC_dWatBelow ! derivative in the thermal conductivity w.r.t. water state in the layer above ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below case('dNrgFlux_dTempAbove' ); get_ixderiv = iLookDERIV%dNrgFlux_dTempAbove ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) case('dNrgFlux_dTempBelow' ); get_ixderiv = iLookDERIV%dNrgFlux_dTempBelow ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below diff --git a/build/source/dshare/multiconst.f90 b/build/source/dshare/multiconst.f90 index b1f007a1a..7d1a48519 100644 --- a/build/source/dshare/multiconst.f90 +++ b/build/source/dshare/multiconst.f90 @@ -38,7 +38,7 @@ MODULE multiconst real(rkind), PARAMETER :: LH_fus = 333700.0_rkind ! latent heat of fusion (J kg-1) real(rkind), PARAMETER :: LH_vap = 2501000.0_rkind ! latent heat of vaporization (J kg-1) real(rkind), PARAMETER :: LH_sub = 2834700.0_rkind ! latent heat of sublimation (J kg-1) - real(rkind), PARAMETER :: sb = 5.6705d-8 ! Stefan Boltzman constant (W m-2 K-4) + real(rkind), PARAMETER :: sb = 5.6705d-8 ! Stefan Boltzman constant (W m-2 K-4) real(rkind), PARAMETER :: em_sno = 0.99_rkind ! emissivity of snow (-) real(rkind), PARAMETER :: lambda_air = 0.026_rkind ! thermal conductivity of air (W m-1 K-1) real(rkind), PARAMETER :: lambda_ice = 2.50_rkind ! thermal conductivity of ice (W m-1 K-1) diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index f7d8d4427..dea09d0b3 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -356,16 +356,6 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%scalarLambda_wetsoil) = var_info('scalarLambda_wetsoil' , 'thermal conductivity of wet soil' , 'W m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%mLayerThermalC) = var_info('mLayerThermalC' , 'thermal conductivity at the mid-point of each layer' , 'W m-1 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%iLayerThermalC) = var_info('iLayerThermalC' , 'thermal conductivity at the interface of each layer' , 'W m-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) - ! energy derivatives that might be treated as constant if heat capacity and thermal conductivity not updated - diag_meta(iLookDIAG%dVolHtCapBulk_dPsi0) = var_info('dVolHtCapBulk_dPsi0' , 'derivative in bulk heat capacity w.r.t. matric potential' , 'J m-4 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%dVolHtCapBulk_dTheta) = var_info('dVolHtCapBulk_dTheta' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%dVolHtCapBulk_dCanWat) = var_info('dVolHtCapBulk_dCanWat' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%dVolHtCapBulk_dTk) = var_info('dVolHtCapBulk_dTk' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%dVolHtCapBulk_dTkCanopy) = var_info('dVolHtCapBulk_dTkCanopy' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%dThermalC_dTempAbove) = var_info('dThermalC_dTempAbove' , 'derivative in the thermal conductivity w.r.t. energy in the layer above','J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%dThermalC_dTempBelow) = var_info('dThermalC_dTempBelow' , 'derivative in the thermal conductivity w.r.t. energy in the layer above','J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%dThermalC_dWatAbove) = var_info('dThermalC_dWatAbove' , 'derivative in the thermal conductivity w.r.t. water in the layer above', 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%dThermalC_dWatBelow) = var_info('dThermalC_dWatBelow' , 'derivative in the thermal conductivity w.r.t. water in the layer above', 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) ! enthalpy diag_meta(iLookDIAG%scalarCanairEnthalpy) = var_info('scalarCanairEnthalpy' , 'enthalpy of the canopy air space' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarCanopyEnthalpy) = var_info('scalarCanopyEnthalpy' , 'enthalpy of the vegetation canopy' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) @@ -582,11 +572,21 @@ subroutine popMetadat(err,message) deriv_meta(iLookDERIV%dTheta_dTkCanopy) = var_info('dTheta_dTkCanopy' , 'derivative of volumetric liquid water content w.r.t. temperature' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%d2Theta_dTkCanopy2) = var_info('d2Theta_dTkCanopy2' , 'second derivative of volumetric liquid water content w.r.t. temperature', 'K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dCanLiq_dTcanopy) = var_info('dCanLiq_dTcanopy' , 'derivative of canopy liquid storage w.r.t. temperature' , 'kg m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dFracLiqVeg_dTkCanopy) = var_info('dFracLiqVeg_dTkCanopy' , 'derivative in fraction of (throughfall + drainage) w.r.t. temperature', 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dFracLiqVeg_dTkCanopy) = var_info('dFracLiqVeg_dTkCanopy' , 'derivative in fraction of (throughfall + drainage) w.r.t. temperature', 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! derivatives in canopy liquid fluxes w.r.t. canopy water deriv_meta(iLookDERIV%scalarCanopyLiqDeriv) = var_info('scalarCanopyLiqDeriv' , 'derivative in (throughfall + drainage) w.r.t. canopy liquid water' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%scalarThroughfallRainDeriv) = var_info('scalarThroughfallRainDeriv' , 'derivative in throughfall w.r.t. canopy liquid water' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%scalarCanopyLiqDrainageDeriv) = var_info('scalarCanopyLiqDrainageDeriv' , 'derivative in canopy drainage w.r.t. canopy liquid water' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! energy derivatives that might be treated as constant if heat capacity and thermal conductivity not updated + deriv_meta(iLookDERIV%dVolHtCapBulk_dPsi0) = var_info('dVolHtCapBulk_dPsi0' , 'derivative in bulk heat capacity w.r.t. matric potential' , 'J m-4 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dTheta) = var_info('dVolHtCapBulk_dTheta' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dCanWat) = var_info('dVolHtCapBulk_dCanWat' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dTk) = var_info('dVolHtCapBulk_dTk' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dTkCanopy) = var_info('dVolHtCapBulk_dTkCanopy' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dThermalC_dTempAbove) = var_info('dThermalC_dTempAbove' , 'derivative in the thermal conductivity w.r.t. energy in the layer above','J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dThermalC_dTempBelow) = var_info('dThermalC_dTempBelow' , 'derivative in the thermal conductivity w.r.t. energy in the layer above','J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dThermalC_dWatAbove) = var_info('dThermalC_dWatAbove' , 'derivative in the thermal conductivity w.r.t. water in the layer above', 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dThermalC_dWatBelow) = var_info('dThermalC_dWatBelow' , 'derivative in the thermal conductivity w.r.t. water in the layer above', 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below deriv_meta(iLookDERIV%dNrgFlux_dTempAbove) = var_info('dNrgFlux_dTempAbove' , 'derivatives in the flux w.r.t. temperature in the layer above' , 'J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dNrgFlux_dTempBelow) = var_info('dNrgFlux_dTempBelow' , 'derivatives in the flux w.r.t. temperature in the layer below' , 'J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index b70e00b43..fbd1366e7 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -373,16 +373,6 @@ MODULE var_lookup integer(i4b) :: scalarLambda_wetsoil = integerMissing ! thermal conductivity of wet soil (W m-1 K-1) integer(i4b) :: mLayerThermalC = integerMissing ! thermal conductivity at the mid-point of each layer (W m-1 K-1) integer(i4b) :: iLayerThermalC = integerMissing ! thermal conductivity at the interface of each layer (W m-1 K-1) - ! energy derivatives that might be treated as constant if heat capacity and thermal conductivity not updated - integer(i4b) :: dVolHtCapBulk_dPsi0 = integerMissing ! derivative in bulk heat capacity w.r.t. matric potential - integer(i4b) :: dVolHtCapBulk_dTheta = integerMissing ! derivative in bulk heat capacity w.r.t. volumetric water content - integer(i4b) :: dVolHtCapBulk_dCanWat = integerMissing ! derivative in bulk heat capacity w.r.t. volumetric water content - integer(i4b) :: dVolHtCapBulk_dTk = integerMissing ! derivative in bulk heat capacity w.r.t. temperature - integer(i4b) :: dVolHtCapBulk_dTkCanopy = integerMissing ! derivative in bulk heat capacity w.r.t. temperature - integer(i4b) :: dThermalC_dTempAbove = integerMissing ! derivative in the thermal conductivity w.r.t. energy state in the layer above - integer(i4b) :: dThermalC_dTempBelow = integerMissing ! derivative in the thermal conductivity w.r.t. energy state in the layer above - integer(i4b) :: dThermalC_dWatAbove = integerMissing ! derivative in the thermal conductivity w.r.t. water state in the layer above - integer(i4b) :: dThermalC_dWatBelow = integerMissing ! derivative in the thermal conductivity w.r.t. water state in the layer above ! enthalpy integer(i4b) :: scalarCanairEnthalpy = integerMissing ! enthalpy of the canopy air space (J m-3) integer(i4b) :: scalarCanopyEnthalpy = integerMissing ! enthalpy of the vegetation canopy (J m-3) @@ -608,6 +598,16 @@ MODULE var_lookup integer(i4b) :: scalarCanopyLiqDeriv = integerMissing ! derivative in (throughfall + canopy drainage) w.r.t. canopy liquid water (s-1) integer(i4b) :: scalarThroughfallRainDeriv = integerMissing ! derivative in throughfall w.r.t. canopy liquid water (s-1) integer(i4b) :: scalarCanopyLiqDrainageDeriv = integerMissing ! derivative in canopy drainage w.r.t. canopy liquid water (s-1) + ! energy derivatives that might be treated as constant if heat capacity and thermal conductivity not updated + integer(i4b) :: dVolHtCapBulk_dPsi0 = integerMissing ! derivative in bulk heat capacity w.r.t. matric potential + integer(i4b) :: dVolHtCapBulk_dTheta = integerMissing ! derivative in bulk heat capacity w.r.t. volumetric water content + integer(i4b) :: dVolHtCapBulk_dCanWat = integerMissing ! derivative in bulk heat capacity w.r.t. volumetric water content + integer(i4b) :: dVolHtCapBulk_dTk = integerMissing ! derivative in bulk heat capacity w.r.t. temperature + integer(i4b) :: dVolHtCapBulk_dTkCanopy = integerMissing ! derivative in bulk heat capacity w.r.t. temperature + integer(i4b) :: dThermalC_dTempAbove = integerMissing ! derivative in the thermal conductivity w.r.t. energy state in the layer above + integer(i4b) :: dThermalC_dTempBelow = integerMissing ! derivative in the thermal conductivity w.r.t. energy state in the layer above + integer(i4b) :: dThermalC_dWatAbove = integerMissing ! derivative in the thermal conductivity w.r.t. water state in the layer above + integer(i4b) :: dThermalC_dWatBelow = integerMissing ! derivative in the thermal conductivity w.r.t. water state in the layer above ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below integer(i4b) :: dNrgFlux_dTempAbove = integerMissing ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) integer(i4b) :: dNrgFlux_dTempBelow = integerMissing ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) @@ -648,8 +648,8 @@ MODULE var_lookup integer(i4b) :: mLayerdTheta_dTk = integerMissing ! derivative of volumetric liquid water content w.r.t. temperature (K-1) integer(i4b) :: mLayerd2Theta_dTk2 = integerMissing ! second derivative of volumetric liquid water content w.r.t. temperature ! derivatives in time - integer(i4b) :: mLayerdTemp_dt = integerMissing ! timestep change in layer temperature - integer(i4b) :: scalarCanopydTemp_dt = integerMissing ! timestep change in canopy temperature + integer(i4b) : mLayerdTemp_dt = integerMissing ! timestep change in layer temperature + integer(i4b) :: scalarCanopydTemp_dt = integerMissing ! timestep change in canopy temperature endtype iLook_deriv @@ -833,19 +833,14 @@ MODULE var_lookup 31, 32, 33, 34, 35, 36, 37, 38, 39) ! named variables: model time type(iLook_time), public,parameter :: iLookTIME =iLook_time ( 1, 2, 3, 4, 5, 6, 7) - ! named variables: model forcing data type(iLook_force), public,parameter :: iLookFORCE =iLook_force ( 1, 2, 3, 4, 5, 6, 7, 8) - ! named variables: model attributes type(iLook_attr), public,parameter :: iLookATTR =iLook_attr ( 1, 2, 3, 4, 5, 6, 7, 8) - ! named variables: soil and vegetation types type(iLook_type), public,parameter :: iLookTYPE =iLook_type ( 1, 2, 3, 4) - ! named variables: hru and gru IDs and associated information type(iLook_id), public,parameter :: iLookID =iLook_id ( 1) - ! named variables: model parameters type(iLook_param), public,parameter :: iLookPARAM =iLook_param ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& @@ -864,12 +859,10 @@ MODULE var_lookup 141,142,143,144,145,146,147,148,149,150,& 151,152,153,154,155,156,157,158,159,160,& 161,162) - ! named variables: model prognostic (state) variables type(iLook_prog), public,parameter :: iLookPROG =iLook_prog ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& 21) - ! named variables: model diagnostic variables type(iLook_diag), public,parameter :: iLookDIAG =iLook_diag ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& @@ -880,7 +873,7 @@ MODULE var_lookup 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,& 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,& 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,& - 91, 92, 93, 94, 95, 96, 97, 98) + 91) ! named variables: model fluxes type(iLook_flux), public,parameter :: iLookFLUX =iLook_flux ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& @@ -891,7 +884,6 @@ MODULE var_lookup 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,& 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,& 81, 82, 83, 84, 85, 86, 87, 88, 89) - ! named variables: derivatives in model fluxes w.r.t. relevant state variables type(iLook_deriv), public,parameter :: iLookDERIV =iLook_deriv ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& @@ -899,8 +891,8 @@ MODULE var_lookup 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,& 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,& 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,& - 61, 62) - + 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,& + 71) ! named variables: model indices type(iLook_index), public,parameter :: iLookINDEX =ilook_index ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& @@ -908,27 +900,20 @@ MODULE var_lookup 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,& 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,& 51, 52, 53, 54, 55, 56, 57, 58, 59, 60) - ! named variables: basin-average parameters type(iLook_bpar), public,parameter :: iLookBPAR =ilook_bpar ( 1, 2, 3, 4, 5) - ! named variables: basin-average variables type(iLook_bvar), public,parameter :: iLookBVAR =ilook_bvar ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13) - ! named variables in varibale type structure type(iLook_varType), public,parameter :: iLookVarType =ilook_varType ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12) - ! number of possible output statistics type(iLook_stat), public,parameter :: iLookStat =ilook_stat ( 1, 2, 3, 4, 5, 6, 7) - ! number of possible output frequencies type(iLook_freq), public,parameter :: iLookFreq =ilook_freq ( 1, 2, 3, 4) - ! named variables in the lookup table structure type(iLook_vLookup), public,parameter :: iLookLOOKUP =ilook_vLookup ( 1, 2, 3) - ! define maximum number of variables of each type integer(i4b),parameter,public :: maxvarDecisions = storage_size(iLookDECISIONS)/iLength integer(i4b),parameter,public :: maxvarTime = storage_size(iLookTIME)/iLength diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 215ada9ca..7d4172ca5 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -116,7 +116,7 @@ subroutine computFlux(& scalarSolution, & ! intent(in): flag to indicate the scalar solution checkLWBalance, & ! intent(in): flag to check longwave balance drainageMeltPond, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) - ! input: state variables + ! input: state variables scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) @@ -339,9 +339,13 @@ subroutine computFlux(& ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below + dThermalC_dWatAbove => deriv_data%var(iLookDERIV%dThermalC_dWatAbove )%dat ,& ! intent(in): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow )%dat ,& ! intent(in): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below - dNrgFlux_dWatAbove => deriv_data%var(iLookDERIV%dNrgFlux_dWatAbove )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. water state in the layer above - dNrgFlux_dWatBelow => deriv_data%var(iLookDERIV%dNrgFlux_dWatBelow )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. water state in the layer below + dNrgFlux_dWatAbove => deriv_data%var(iLookDERIV%dNrgFlux_dWatAbove )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. water state in the layer above + dNrgFlux_dWatBelow => deriv_data%var(iLookDERIV%dNrgFlux_dWatBelow )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. water state in the layer below + dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove )%dat ,& ! intent(in): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow )%dat ,& ! intent(in): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(out): [dp(:)] derivative in vertical liquid water flux at layer interfaces ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables @@ -353,7 +357,7 @@ subroutine computFlux(& mLayerdPsi_dTheta => deriv_data%var(iLookDERIV%mLayerdPsi_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. theta dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in compressibility w.r.t matric head ! derivative in baseflow flux w.r.t. aquifer storage - dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] erivative in baseflow flux w.r.t. aquifer storage (s-1) + dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] derivative in baseflow flux w.r.t. aquifer storage (s-1) ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below @@ -496,13 +500,18 @@ subroutine computFlux(& ! input: model control (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution ! input: fluxes and derivatives at the upper boundary - scalarGroundNetNrgFlux, & ! intent(in): total flux at the ground surface (W m-2) - dGroundNetFlux_dGroundTemp, & ! intent(in): derivative in total ground surface flux w.r.t. ground temperature (W m-2 K-1) + scalarGroundNetNrgFlux, & ! intent(in): total flux at the ground surface (W m-2) + dGroundNetFlux_dGroundTemp, & ! intent(in): derivative in total ground surface flux w.r.t. ground temperature (W m-2 K-1) ! input: liquid water fluxes throughout the snow and soil domains - iLayerLiqFluxSnow, & ! intent(in): liquid flux at the interface of each snow layer (m s-1) - iLayerLiqFluxSoil, & ! intent(in): liquid flux at the interface of each soil layer (m s-1) + iLayerLiqFluxSnow, & ! intent(in): liquid flux at the interface of each snow layer (m s-1) + iLayerLiqFluxSoil, & ! intent(in): liquid flux at the interface of each soil layer (m s-1) ! input: trial value of model state variables - mLayerTempTrial, & ! intent(in): trial temperature at the current iteration (K) + mLayerTempTrial, & ! intent(in): trial temperature at the current iteration (K) + ! input: derivatives + dThermalC_dWatAbove, & ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow, & ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dTempAbove, & ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow, & ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above ! input-output: data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model indices @@ -510,11 +519,11 @@ subroutine computFlux(& diag_data, & ! intent(in): model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU ! output: fluxes and derivatives at all layer interfaces - iLayerNrgFlux, & ! intent(out): energy flux at the layer interfaces (W m-2) - dNrgFlux_dTempAbove, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (W m-2 K-1) - dNrgFlux_dTempBelow, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (W m-2 K-1) - dNrgFlux_dWatAbove, & ! intent(out): derivatives in the flux w.r.t. water state in the layer above - dNrgFlux_dWatBelow, & ! intent(out): derivatives in the flux w.r.t. water state in the layer below + iLayerNrgFlux, & ! intent(out):  energy flux at the layer interfaces (W m-2) + dNrgFlux_dTempAbove, & ! intent(out):  derivatives in the flux w.r.t. temperature in the layer above (W m-2 K-1) + dNrgFlux_dTempBelow, & ! intent(out):  derivatives in the flux w.r.t. temperature in the layer below (W m-2 K-1) + dNrgFlux_dWatAbove, & ! intent(out):  derivatives in the flux w.r.t. water state in the layer above + dNrgFlux_dWatBelow, & ! intent(out):  derivatives in the flux w.r.t. water state in the layer below ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index 49cca758c..f45e721b3 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -87,6 +87,7 @@ subroutine computHeatCap(& mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! input: state variables scalarCanopyIce, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) scalarCanopyLiquid, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) @@ -114,6 +115,11 @@ subroutine computHeatCap(& ! output heatCapVeg, & ! intent(out): heat capacity for canopy mLayerHeatCap, & ! intent(out): heat capacity for snow and soil + dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature ! output: error control err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------------- @@ -128,6 +134,7 @@ subroutine computHeatCap(& type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model layer indices type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! input: state variables real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) real(rkind),intent(in) :: scalarCanopyLiquid ! trial value for the liquid water on the vegetation canopy (kg m-2) @@ -155,16 +162,22 @@ subroutine computHeatCap(& ! output: real(qp),intent(out) :: heatCapVeg ! heat capacity for canopy real(qp),intent(out) :: mLayerHeatCap(:) ! heat capacity for snow and soil + real(rkind),intent(out) :: dVolHtCapBulk_dPsi0 ! derivative in bulk heat capacity w.r.t. matric potential + real(rkind),intent(out) :: dVolHtCapBulk_dTheta ! derivative in bulk heat capacity w.r.t. volumetric water content + real(rkind),intent(out) :: dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. volumetric water content + real(rkind),intent(out) :: dVolHtCapBulk_dTk ! derivative in bulk heat capacity w.r.t. temperature + real(rkind),intent(out) :: dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature + ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: iSoil ! index of soil layer + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: iSoil ! index of soil layer real(rkind) :: delT real(rkind) :: delEnt - real(rkind) :: fLiq ! fraction of liquid water - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind) :: fLiq ! fraction of liquid water + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) ! -------------------------------------------------------------------------------------------------------------------------------- ! associate variables in data structure associate(& @@ -176,13 +189,7 @@ subroutine computHeatCap(& maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) ! input: depth varying soil parameters iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): soil porosity (-) - ! output: derivatives - dVolHtCapBulk_dPsi0 => diag_data%var(iLookDIAG%dVolHtCapBulk_dPsi0)%dat, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta => diag_data%var(iLookDIAG%dVolHtCapBulk_dTheta)%dat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat => diag_data%var(iLookDIAG%dVolHtCapBulk_dCanWat)%dat(1), & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk => diag_data%var(iLookDIAG%dVolHtCapBulk_dTk)%dat, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy => diag_data%var(iLookDIAG%dVolHtCapBulk_dTkCanopy)%dat(1) & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) ) ! end associate statemen ! initialize error control err=0; message="computHeatCap/" @@ -399,6 +406,11 @@ subroutine computHeatCapAnalytic(& ! output heatCapVeg, & ! intent(out): heat capacity for canopy mLayerHeatCap, & ! intent(out): heat capacity for snow and soil + dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature ! output: error control err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------------- @@ -406,37 +418,43 @@ subroutine computHeatCapAnalytic(& USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) - real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) - real(rkind),intent(in) :: scalarCanopyLiquid - real(rkind),intent(in) :: scalarCanopyTemp ! value of canopy temperature (kg m-2) - real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) - real(rkind),intent(in) :: mLayerTemp(:) ! vector of temperature (-) - real(rkind),intent(in) :: mLayerMatricHead(:) ! vector of total water matric potential (m) - ! input: pre-computed derivatives - real(rkind),intent(in) :: dTheta_dTkCanopy ! derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: scalarFracLiqVeg ! fraction of canopy liquid water (-) - real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative of volumetric liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) - real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) - ! input/output: data structures - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model layer indices - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - ! output: error control - real(qp),intent(out) :: heatCapVeg ! heat capacity for canopy - real(qp),intent(out) :: mLayerHeatCap(:) ! heat capacity for snow and soil - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: iSoil ! index of soil layer - real(rkind) :: fLiq ! fraction of liquid water - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) + real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) + real(rkind),intent(in) :: scalarCanopyLiquid + real(rkind),intent(in) :: scalarCanopyTemp ! value of canopy temperature (kg m-2) + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) + real(rkind),intent(in) :: mLayerTemp(:) ! vector of temperature (-) + real(rkind),intent(in) :: mLayerMatricHead(:) ! vector of total water matric potential (m) + ! input: pre-computed derivatives + real(rkind),intent(in) :: dTheta_dTkCanopy ! derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: scalarFracLiqVeg ! fraction of canopy liquid water (-) + real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative of volumetric liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) + real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) + ! input/output: data structures + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model layer indices + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + ! output + real(qp),intent(out) :: heatCapVeg ! heat capacity for canopy + real(qp),intent(out) :: mLayerHeatCap(:) ! heat capacity for snow and soil + real(rkind),intent(out) :: dVolHtCapBulk_dPsi0 ! derivative in bulk heat capacity w.r.t. matric potential + real(rkind),intent(out) :: dVolHtCapBulk_dTheta ! derivative in bulk heat capacity w.r.t. volumetric water content + real(rkind),intent(out) :: dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. volumetric water content + real(rkind),intent(out) :: dVolHtCapBulk_dTk ! derivative in bulk heat capacity w.r.t. temperature + real(rkind),intent(out) :: dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------- ------------------------------------------------------------------------ + ! local variables + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: iSoil ! index of soil layer + real(rkind) :: fLiq ! fraction of liquid water + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) ! -------------------------------------------------------------------------------------------------------------------------------- ! associate variables in data structure associate(& @@ -449,13 +467,7 @@ subroutine computHeatCapAnalytic(& maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) ! input: depth varying soil parameters iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): soil porosity (-) - ! output: derivatives - dVolHtCapBulk_dPsi0 => diag_data%var(iLookDIAG%dVolHtCapBulk_dPsi0)%dat, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta => diag_data%var(iLookDIAG%dVolHtCapBulk_dTheta)%dat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat => diag_data%var(iLookDIAG%dVolHtCapBulk_dCanWat)%dat(1), & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk => diag_data%var(iLookDIAG%dVolHtCapBulk_dTk)%dat, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy => diag_data%var(iLookDIAG%dVolHtCapBulk_dTkCanopy)%dat(1) & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) ) ! end associate statement ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 512418e20..d682a0f50 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -232,11 +232,11 @@ subroutine computJacob(& ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk )%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature ! derivative in bulk heat capacity w.r.t. relevant state variables - dVolHtCapBulk_dPsi0 => diag_data%var(iLookDIAG%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta => diag_data%var(iLookDIAG%dVolHtCapBulk_dTheta )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat => diag_data%var(iLookDIAG%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk => diag_data%var(iLookDIAG%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy => diag_data%var(iLookDIAG%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. temperature ! derivatives in time mLayerdTemp_dt => deriv_data%var(iLookDERIV%mLayerdTemp_dt )%dat ,& ! intent(in): [dp(:)] timestep change in layer temperature scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt )%dat(1) ,& ! intent(in): [dp ] timestep change in canopy temperature @@ -636,14 +636,6 @@ subroutine computJacob(& endif ! (if there are state variables for both water and energy in the soil domain) - if(globalPrintFlag)then - print*, '** banded analytical Jacobian:' - write(*,'(a4,1x,100(i17,1x))') 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) - do iLayer=kl+1,nBands - write(*,'(i4,1x,100(e17.10,1x))') iLayer, (aJac(iLayer,jLayer),jLayer=min(iJac1,nState),min(iJac2,nState)) - end do - endif - ! ********************************************************************************************************************************************************* ! * PART 2: FULL MATRIX ! ********************************************************************************************************************************************************* @@ -971,20 +963,29 @@ subroutine computJacob(& endif ! (if there are state variables for both water and energy in the soil domain) + ! check + case default; err=20; message=trim(message)//'unable to identify option for the type of matrix'; return + + end select ! type of matrix + ! ********************************************************************************************************************************************************* ! print the Jacobian if(globalPrintFlag)then - print*, '** analytical Jacobian (full):' + select case(ixMatrix) + case(ixBandMatrix) + print*, '** banded analytical Jacobian:' + write(*,'(a4,1x,100(i17,1x))') 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) + do iLayer=kl+1,nBands + write(*,'(i4,1x,100(e17.10,1x))') iLayer, (aJac(iLayer,jLayer),jLayer=min(iJac1,nState),min(iJac2,nState)) + end do + case(ixFullMatrix) + print*, '** full analytical Jacobian:' write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) do iLayer=min(iJac1,nState),min(iJac2,nState) write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(min(iJac1,nState):min(iJac2,nState),iLayer) end do + end select endif - ! check - case default; err=20; message=trim(message)//'unable to identify option for the type of matrix'; return - - end select ! type of matrix - if(any(isNan(aJac)))then print *, '******************************* WE FOUND NAN IN JACOBIAN ************************************' stop 1 diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index 5933fb583..b193565d9 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -267,12 +267,12 @@ subroutine computJacobWithPrime(& dFracLiqSnow_dTk => deriv_data%var(iLookDERIV%dFracLiqSnow_dTk )%dat ,& ! intent(in): [dp(:)] derivative in fraction of liquid snow w.r.t. temperature mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk )%dat ,& ! intent(in): [dp(:)] derivative in volumetric liquid water content w.r.t. temperature mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2 )%dat ,& ! intent(in): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature - ! derivate in bulk heat capacity w.r.t. relevant state variables - dVolHtCapBulk_dPsi0 => diag_data%var(iLookDIAG%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta => diag_data%var(iLookDIAG%dVolHtCapBulk_dTheta )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat => diag_data%var(iLookDIAG%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk => diag_data%var(iLookDIAG%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy => diag_data%var(iLookDIAG%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. temperature + ! derivative in bulk heat capacity w.r.t. relevant state variables + dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. temperature ! diagnostic variables scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg )%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg )%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) @@ -681,14 +681,6 @@ subroutine computJacobWithPrime(& endif ! (if there are state variables for both water and energy in the soil domain) - if(globalPrintFlag)then - print*, '** banded analytical Jacobian:' - write(*,'(a4,1x,100(i17,1x))') 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) - do iLayer=kl+1,nBands - write(*,'(i4,1x,100(e17.10,1x))') iLayer, (aJac(iLayer,jLayer),jLayer=min(iJac1,nState),min(iJac2,nState)) - end do - endif - ! ********************************************************************************************************************************************************* ! * PART 2: FULL MATRIX ! ********************************************************************************************************************************************************* @@ -1019,20 +1011,29 @@ subroutine computJacobWithPrime(& endif ! (if there are state variables for both water and energy in the soil domain) + ! check + case default; err=20; message=trim(message)//'unable to identify option for the type of matrix'; return + + end select ! type of matrix + ! ********************************************************************************************************************************************************* ! print the Jacobian if(globalPrintFlag)then - print*, '** analytical Jacobian (full):' + select case(ixMatrix) + case(ixBandMatrix) + print*, '** banded analytical Jacobian:' + write(*,'(a4,1x,100(i17,1x))') 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) + do iLayer=kl+1,nBands + write(*,'(i4,1x,100(e17.10,1x))') iLayer, (aJac(iLayer,jLayer),jLayer=min(iJac1,nState),min(iJac2,nState)) + end do + case(ixFullMatrix) + print*, '** full analytical Jacobian:' write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) do iLayer=min(iJac1,nState),min(iJac2,nState) write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(min(iJac1,nState):min(iJac2,nState),iLayer) end do + end select endif - ! check - case default; err=20; message=trim(message)//'unable to identify option for the type of matrix'; return - - end select ! type of matrix - if(any(isNan(aJac)))then print *, '******************************* WE FOUND NAN IN JACOBIAN ************************************' stop 1 diff --git a/build/source/engine/diagn_evar.f90 b/build/source/engine/diagn_evar.f90 index d42e9e878..0a17a26dd 100644 --- a/build/source/engine/diagn_evar.f90 +++ b/build/source/engine/diagn_evar.f90 @@ -85,14 +85,14 @@ module diagn_evar_module ! ********************************************************************************************************** subroutine diagn_evar(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) ! input/output: data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output: error control + ! output: error control err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------------- ! provide access to external subroutines @@ -100,7 +100,7 @@ subroutine diagn_evar(& ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: model control logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) + real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) ! input/output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model layer indices @@ -111,29 +111,29 @@ subroutine diagn_evar(& character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables - character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: iSoil ! index of soil layer - real(rkind) :: TCn ! thermal conductivity below the layer interface (W m-1 K-1) - real(rkind) :: TCp ! thermal conductivity above the layer interface (W m-1 K-1) - real(rkind) :: zdn ! height difference between interface and lower value (m) - real(rkind) :: zdp ! height difference between interface and upper value (m) - real(rkind) :: bulkden_soil ! bulk density of soil (kg m-3) - real(rkind) :: lambda_drysoil ! thermal conductivity of dry soil (W m-1) - real(rkind) :: lambda_wetsoil ! thermal conductivity of wet soil (W m-1) - real(rkind) :: lambda_wet ! thermal conductivity of the wet material - real(rkind) :: relativeSat ! relative saturation (-) - real(rkind) :: kerstenNum ! the Kersten number (-), defining weight applied to conductivity of the wet medium - real(rkind) :: den ! denominator in the thermal conductivity calculations + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: iSoil ! index of soil layer + real(rkind) :: TCn ! thermal conductivity below the layer interface (W m-1 K-1) + real(rkind) :: TCp ! thermal conductivity above the layer interface (W m-1 K-1) + real(rkind) :: zdn ! height difference between interface and lower value (m) + real(rkind) :: zdp ! height difference between interface and upper value (m) + real(rkind) :: bulkden_soil ! bulk density of soil (kg m-3) + real(rkind) :: lambda_drysoil ! thermal conductivity of dry soil (W m-1) + real(rkind) :: lambda_wetsoil ! thermal conductivity of wet soil (W m-1) + real(rkind) :: lambda_wet ! thermal conductivity of the wet material + real(rkind) :: relativeSat ! relative saturation (-) + real(rkind) :: kerstenNum ! the Kersten number (-), defining weight applied to conductivity of the wet medium + real(rkind) :: den ! denominator in the thermal conductivity calculations ! local variables to reproduce the thermal conductivity of Hansson et al. VZJ 2005 - real(rkind),parameter :: c1=0.55_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c2=0.8_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c3=3.07_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: c4=0.13_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c5=4._rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: f1=13.05_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: f2=1.06_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind) :: fArg,xArg ! temporary variables (see Hansson et al. VZJ 2005 for details) + real(rkind),parameter :: c1=0.55_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c2=0.8_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c3=3.07_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: c4=0.13_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c5=4._rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: f1=13.05_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: f2=1.06_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind) :: fArg,xArg ! temporary variables (see Hansson et al. VZJ 2005 for details) ! -------------------------------------------------------------------------------------------------------------------------------- ! associate variables in data structure associate(& @@ -169,16 +169,6 @@ subroutine diagn_evar(& mLayerThermalC => diag_data%var(iLookDIAG%mLayerThermalC)%dat, & ! intent(out): thermal conductivity at the mid-point of each layer (W m-1 K-1) iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(out): thermal conductivity at the interface of each layer (W m-1 K-1) mLayerVolFracAir => diag_data%var(iLookDIAG%mLayerVolFracAir)%dat, & ! intent(out): volumetric fraction of air in each layer (-) - ! energy derivatives initialized as constant - dVolHtCapBulk_dPsi0 => diag_data%var(iLookDIAG%dVolHtCapBulk_dPsi0)%dat, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta => diag_data%var(iLookDIAG%dVolHtCapBulk_dTheta)%dat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat => diag_data%var(iLookDIAG%dVolHtCapBulk_dCanWat)%dat(1), & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk => diag_data%var(iLookDIAG%dVolHtCapBulk_dTk)%dat, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy => diag_data%var(iLookDIAG%dVolHtCapBulk_dTkCanopy)%dat(1), & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dThermalC_dWatAbove => diag_data%var(iLookDIAG%dThermalC_dWatAbove)%dat, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dWatBelow => diag_data%var(iLookDIAG%dThermalC_dWatBelow)%dat, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dTempAbove => diag_data%var(iLookDIAG%dThermalC_dTempAbove)%dat, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dTempBelow => diag_data%var(iLookDIAG%dThermalC_dTempBelow)%dat & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above ) ! end associate statement ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -332,17 +322,6 @@ subroutine diagn_evar(& ! assume the thermal conductivity at the domain boundaries is equal to the thermal conductivity of the layer iLayerThermalC(nLayers) = mLayerThermalC(nLayers) - ! set derivatives to 0 for constant through step - dVolHtCapBulk_dPsi0 = 0._rkind - dVolHtCapBulk_dTheta = 0._rkind - dVolHtCapBulk_dCanWat = 0._rkind - dVolHtCapBulk_dTk = 0._rkind - dVolHtCapBulk_dTkCanopy = 0._rkind - dThermalC_dWatAbove = 0._rkind - dThermalC_dWatBelow = 0._rkind - dThermalC_dTempAbove = 0._rkind - dThermalC_dTempBelow = 0._rkind - ! end association to variables in the data structure end associate diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index e4f1ce4e1..87107bb8d 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -244,23 +244,32 @@ subroutine eval8summa(& scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) - scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) + scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) ! enthalpy from the previous solution scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) ! soil compression - scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2 s-1) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in volumetric water content due to compression of soil (s-1) + scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2 s-1) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in volumetric water content due to compression of soil (s-1) ! derivatives dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential - dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - ! mapping + dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1),& ! intent(out): [dp] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1),& !intent(out):[dp] derivative in bulk heat capacity w.r.t. temperature + dThermalC_dWatAbove => deriv_data%var(iLookDERIV%dThermalC_dWatAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + ! mapping ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) ! indices @@ -411,42 +420,47 @@ subroutine eval8summa(& ! *** compute volumetric heat capacity C_p = dH_T/dT call computHeatCap(& ! input: control variables - nLayers, & ! intent(in): number of layers (soil+snow) - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) - ! input output data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices + nLayers, & ! intent(in): number of layers (soil+snow) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) + ! input output data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyTemp, & ! intent(in): previous value of canopy temperature (K) - scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) - scalarCanopyEnthalpy, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial temperature - mLayerTemp, & ! intent(in): previous temperature - mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil - mLayerEnthalpy, & ! intent(in): previous enthalpy for snow and soil - mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) - ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - dCanEnthalpy_dTk, & ! intent(in): derivatives in canopy enthalpy w.r.t. temperature - dCanEnthalpy_dWat, & ! intent(in): derivatives in canopy enthalpy w.r.t. water state - dEnthalpy_dTk, & ! intent(in): derivatives in layer enthalpy w.r.t. temperature - dEnthalpy_dWat, & ! intent(in): derivatives in layer enthalpy w.r.t. water state + scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyTemp, & ! intent(in): previous value of canopy temperature (K) + scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpy, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial temperature + mLayerTemp, & ! intent(in): previous temperature + mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil + mLayerEnthalpy, & ! intent(in): previous enthalpy for snow and soil + mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) + ! input: pre-computed derivatives + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + dCanEnthalpy_dTk, & ! intent(in): derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat, & ! intent(in): derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk, & ! intent(in): derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat, & ! intent(in): derivatives in layer enthalpy w.r.t. water state ! output - heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil + heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil + dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! update values mLayerEnthalpy = mLayerEnthalpyTrial @@ -454,32 +468,37 @@ subroutine eval8summa(& else if(ixHowHeatCap == closedForm)then call computHeatCapAnalytic(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) - ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) - ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) + ! input: state variables + scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) + ! input: pre-computed derivatives + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! input output data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! output - heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow + heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow + dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature ! output: error control - err,cmessage) ! intent(out): error control - endif + err,cmessage) ! intent(out): error control + endif !(choice of how compute heat capacity) ! compute multiplier of state vector call computStatMult(& @@ -514,9 +533,25 @@ subroutine eval8summa(& indx_data, & ! intent(in): model layer indices prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(inout): model diagnostic variables for a local HRU + ! output: derivatives + dThermalC_dWatAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dTempAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if - + else + ! set heat capacity derivatives to 0 for constant through step + dVolHtCapBulk_dPsi0 = 0._rkind + dVolHtCapBulk_dTheta = 0._rkind + dVolHtCapBulk_dCanWat = 0._rkind + dVolHtCapBulk_dTk = 0._rkind + dVolHtCapBulk_dTkCanopy = 0._rkind + dThermalC_dWatAbove = 0._rkind + dThermalC_dWatBelow = 0._rkind + dThermalC_dTempAbove = 0._rkind + dThermalC_dTempBelow = 0._rkind endif ! updateCp if(needCm)then diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index e762c15dc..4dd75dd38 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -266,6 +266,15 @@ subroutine eval8summaWithPrime(& dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1),& ! intent(out): [dp] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1),& !intent(out):[dp] derivative in bulk heat capacity w.r.t. temperature + dThermalC_dWatAbove => deriv_data%var(iLookDERIV%dThermalC_dWatAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above ! mapping ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) @@ -497,6 +506,11 @@ subroutine eval8summaWithPrime(& ! output heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil + dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -526,9 +540,14 @@ subroutine eval8summaWithPrime(& ! output heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow + dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature ! output: error control err,cmessage) ! intent(out): error control - endif + endif !(choice of how compute heat capacity) ! compute multiplier of state vector call computStatMult(& @@ -563,9 +582,26 @@ subroutine eval8summaWithPrime(& indx_data, & ! intent(in): model layer indices prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(inout): model diagnostic variables for a local HRU + ! output: derivatives + dThermalC_dWatAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dTempAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + ! output: error control + ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if - + else + ! set heat capacity derivatives to 0 for constant through step + dVolHtCapBulk_dPsi0 = 0._rkind + dVolHtCapBulk_dTheta = 0._rkind + dVolHtCapBulk_dCanWat = 0._rkind + dVolHtCapBulk_dTk = 0._rkind + dVolHtCapBulk_dTkCanopy = 0._rkind + dThermalC_dWatAbove = 0._rkind + dThermalC_dWatBelow = 0._rkind + dThermalC_dTempAbove = 0._rkind + dThermalC_dTempBelow = 0._rkind endif ! updateCp if(needCm)then diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index ff61b0dd8..e1d23ca8b 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -164,7 +164,7 @@ subroutine opSplittin(& firstSubStep, & ! intent(in): flag to denote first sub-step firstInnerStep, & ! intent(in): flag to denote if the last time step in maxstep subStep computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation - ! input/output: data structures + ! input/output: data structures type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes forc_data, & ! intent(in): model forcing data diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index 1e3832309..30f2e57be 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -85,6 +85,11 @@ subroutine ssdNrgFlux(& iLayerLiqFluxSoil, & ! intent(in): liquid flux at the interface of each soil layer (m s-1) ! input: trial value of model state variables mLayerTempTrial, & ! intent(in): trial temperature at the current iteration (K) + ! input: derivatives + dThermalC_dWatAbove, & ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow, & ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dTempAbove, & ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow, & ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above ! input-output: data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model indices @@ -97,21 +102,26 @@ subroutine ssdNrgFlux(& dFlux_dTempBelow, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (W m-2 K-1) dFlux_dWatAbove, & ! intent(out): derivatives in the flux w.r.t. water state in the layer above (W m-2 K-1) dFlux_dWatBelow, & ! intent(out): derivatives in the flux w.r.t. water state in the layer below (W m-2 K-1) - ! output: error control - err,message) ! intent(out): error control + ! output: error control + err,message) ! intent(out): error control ! ------------------------------------------------------------------------------------------------------------------------------------------------- implicit none ! input: model control - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: fluxes and derivatives at the upper boundary - real(rkind),intent(in) :: groundNetFlux ! net energy flux for the ground surface (W m-2) - real(rkind),intent(inout) :: dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + real(rkind),intent(in) :: groundNetFlux ! net energy flux for the ground surface (W m-2) + real(rkind),intent(inout) :: dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) ! input: liquid water fluxes - real(rkind),intent(in) :: iLayerLiqFluxSnow(0:) ! liquid flux at the interface of each snow layer (m s-1) - real(rkind),intent(in) :: iLayerLiqFluxSoil(0:) ! liquid flux at the interface of each soil layer (m s-1) + real(rkind),intent(in) :: iLayerLiqFluxSnow(0:) ! liquid flux at the interface of each snow layer (m s-1) + real(rkind),intent(in) :: iLayerLiqFluxSoil(0:) ! liquid flux at the interface of each soil layer (m s-1) ! input: trial model state variables real(rkind),intent(in) :: mLayerTempTrial(:) ! temperature in each layer at the current iteration (m) + ! input: derivatives + real(rkind),intent(in) :: dThermalC_dWatAbove(:) ! derivative in the thermal conductivity w.r.t. water state in the layer above + real(rkind),intent(in) :: dThermalC_dWatBelow(:) ! derivative in the thermal conductivity w.r.t. water state in the layer above + real(rkind),intent(in) :: dThermalC_dTempAbove(:) ! derivative in the thermal conductivity w.r.t. energy state in the layer above + real(rkind),intent(in) :: dThermalC_dTempBelow(:) ! derivative in the thermal conductivity w.r.t. energy state in the layer above ! input-output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! state vector geometry @@ -124,18 +134,18 @@ subroutine ssdNrgFlux(& real(rkind),intent(out) :: dFlux_dTempBelow(0:) ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) real(rkind),intent(out) :: dFlux_dWatAbove(0:) ! derivatives in the flux w.r.t. water state in the layer above (J m-2 s-1 K-1) real(rkind),intent(out) :: dFlux_dWatBelow(0:) ! derivatives in the flux w.r.t. water state in the layer below (J m-2 s-1 K-1) - ! output: error control + ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! local variables - character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b) :: iLayer ! index of model layers - integer(i4b) :: ixLayerDesired(1) ! layer desired (scalar solution) - integer(i4b) :: ixTop ! top layer in subroutine call - integer(i4b) :: ixBot ! bottom layer in subroutine call - real(rkind) :: qFlux ! liquid flux at layer interfaces (m s-1) - real(rkind) :: dz ! height difference (m) + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iLayer ! index of model layers + integer(i4b) :: ixLayerDesired(1) ! layer desired (scalar solution) + integer(i4b) :: ixTop ! top layer in subroutine call + integer(i4b) :: ixBot ! bottom layer in subroutine call + real(rkind) :: qFlux ! liquid flux at layer interfaces (m s-1) + real(rkind) :: dz ! height difference (m) ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! make association of local variables with information in the data structures associate(& @@ -153,11 +163,7 @@ subroutine ssdNrgFlux(& upperBoundTemp => mpar_data%var(iLookPARAM%upperBoundTemp)%dat(1), & ! intent(in): temperature of the upper boundary (K) lowerBoundTemp => mpar_data%var(iLookPARAM%lowerBoundTemp)%dat(1), & ! intent(in): temperature of the lower boundary (K) iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(in): thermal conductivity at the interface of each layer (W m-1 K-1) - dThermalC_dWatAbove => diag_data%var(iLookDIAG%dThermalC_dWatAbove)%dat, & ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dWatBelow => diag_data%var(iLookDIAG%dThermalC_dWatBelow)%dat, & ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dTempAbove => diag_data%var(iLookDIAG%dThermalC_dTempAbove)%dat, & ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dTempBelow => diag_data%var(iLookDIAG%dThermalC_dTempBelow)%dat, & ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above - ! output: diagnostic fluxes + ! output: diagnostic fluxes iLayerConductiveFlux => flux_data%var(iLookFLUX%iLayerConductiveFlux)%dat, & ! intent(out): conductive energy flux at layer interfaces at end of time step (W m-2) iLayerAdvectiveFlux => flux_data%var(iLookFLUX%iLayerAdvectiveFlux)%dat & ! intent(out): advective energy flux at layer interfaces at end of time step (W m-2) ) ! association of local variables with information in the data structures From a23aa3be488eff9d4f3058bd2b66d5c4d3e0f23e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 3 Jul 2023 23:31:39 +0900 Subject: [PATCH 0787/1472] part of last commit --- build/source/engine/computThermConduct.f90 | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/build/source/engine/computThermConduct.f90 b/build/source/engine/computThermConduct.f90 index a63db3ab2..62ad3e13c 100644 --- a/build/source/engine/computThermConduct.f90 +++ b/build/source/engine/computThermConduct.f90 @@ -89,6 +89,11 @@ subroutine computThermConduct(& indx_data, & ! intent(in): model layer indices prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(inout): model diagnostic variables for a local HRU + ! output: derivatives + dThermalC_dWatAbove, & ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow, & ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dTempAbove, & ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow, & ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above ! output: error control err,message) ! intent(out): error control @@ -119,6 +124,11 @@ subroutine computThermConduct(& type(var_ilength),intent(in) :: indx_data ! model layer indices type(var_dlength),intent(in) :: prog_data ! model prognostic variables for a local HRU type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for a local HRU + ! output: derivatives + real(rkind),intent(out) :: dThermalC_dWatAbove(:) ! derivative in the thermal conductivity w.r.t. water state in the layer above + real(rkind),intent(out) :: dThermalC_dWatBelow(:) ! derivative in the thermal conductivity w.r.t. water state in the layer above + real(rkind),intent(out) :: dThermalC_dTempAbove(:)! derivative in the thermal conductivity w.r.t. energy state in the layer above + real(rkind),intent(out) :: dThermalC_dTempBelow(:)! derivative in the thermal conductivity w.r.t. energy state in the layer above ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -192,11 +202,7 @@ subroutine computThermConduct(& ! output: diagnostic variables and derivatives (diagnostic as may be treated as constant) mLayerThermalC => diag_data%var(iLookDIAG%mLayerThermalC)%dat, & ! intent(out): thermal conductivity at the mid-point of each layer (W m-1 K-1) iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(out): thermal conductivity at the interface of each layer (W m-1 K-1) - mLayerVolFracAir => diag_data%var(iLookDIAG%mLayerVolFracAir)%dat, & ! intent(out): volumetric fraction of air in each layer (-) - dThermalC_dWatAbove => diag_data%var(iLookDIAG%dThermalC_dWatAbove)%dat, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dWatBelow => diag_data%var(iLookDIAG%dThermalC_dWatBelow)%dat, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dTempAbove => diag_data%var(iLookDIAG%dThermalC_dTempAbove)%dat, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dTempBelow => diag_data%var(iLookDIAG%dThermalC_dTempBelow)%dat & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + mLayerVolFracAir => diag_data%var(iLookDIAG%mLayerVolFracAir)%dat & ! intent(out): volumetric fraction of air in each layer (-) ) ! association of local variables with information in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control From 74e8279bdad9d6f48fccc6b7f59d76b13877cfd9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 3 Jul 2023 23:35:24 +0900 Subject: [PATCH 0788/1472] put in adjusted temp derivates, first iteration only so far (this solves the Jacobian problem, the last Jacobian "fix" was incorrect. --- build/source/dshare/get_ixname.f90 | 3 + build/source/dshare/popMetadat.f90 | 3 + build/source/dshare/var_lookup.f90 | 3 + build/source/engine/computJacob.f90 | 36 ++++--- build/source/engine/computJacobWithPrime.f90 | 20 +++- build/source/engine/coupled_em.f90 | 6 +- build/source/engine/tempAdjust.f90 | 98 +++++++++++++------- build/source/engine/varSubstep.f90 | 8 +- 8 files changed, 124 insertions(+), 53 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 69eb740e3..a7122612b 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -541,6 +541,9 @@ function get_ixdiag(varName) ! canopy hydrology case('scalarFracLiqVeg' ); get_ixdiag = iLookDIAG%scalarFracLiqVeg ! fraction of liquid water on vegetation (-) case('scalarCanopyWetFraction' ); get_ixdiag = iLookDIAG%scalarCanopyWetFraction ! fraction of canopy that is wet + ! canopy derivatives from adjusting the canopy temperature to account for new snow + case('dTkCanopyAdj_dTkCanopy' ); get_ixdiag = iLookDIAG%dTkCanopyAdj_dTkCanopy ! derivative in the adjusted temperature w.r.t. original temperature + case('dTkCanopyAdj_dCanWat' ); get_ixdiag = iLookDIAG%dTkCanopyAdj_dCanWat ! derivative in the adjusted temperature w.r.t. volumetric water content ! snow hydrology case('scalarSnowAge' ); get_ixdiag = iLookDIAG%scalarSnowAge ! non-dimensional snow age (-) case('scalarGroundSnowFraction' ); get_ixdiag = iLookDIAG%scalarGroundSnowFraction ! fraction of ground that is covered with snow (-) diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index dea09d0b3..0dfaf3a60 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -404,6 +404,9 @@ subroutine popMetadat(err,message) ! canopy hydrology diag_meta(iLookDIAG%scalarFracLiqVeg) = var_info('scalarFracLiqVeg' , 'fraction of liquid water on vegetation' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarCanopyWetFraction) = var_info('scalarCanopyWetFraction' , 'fraction canopy that is wet' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! canopy derivatives from adjusting the canopy temperature to account for new snow + diag_meta(iLookDIAG%dTkCanopyAdj_dTkCanopy) = var_info('dTkCanopyAdj_dTkCanopy' , 'derivative in the adjusted temperature w.r.t. original temperature' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%dTkCanopyAdj_dCanWat) = var_info('dTkCanopyAdj_dCanWat', , 'derivative in the adjusted temperature w.r.t. volumetric water content', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! snow hydrology diag_meta(iLookDIAG%scalarSnowAge) = var_info('scalarSnowAge' , 'non-dimensional snow age' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarGroundSnowFraction) = var_info('scalarGroundSnowFraction' , 'fraction ground that is covered with snow' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index fbd1366e7..43e7a5804 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -421,6 +421,9 @@ MODULE var_lookup ! canopy hydrology integer(i4b) :: scalarFracLiqVeg = integerMissing ! fraction of liquid water on vegetation (-) integer(i4b) :: scalarCanopyWetFraction = integerMissing ! fraction of canopy that is wet + ! canopy derivatives from adjusting the canopy temperature to account for new snow + integer(i4b) :: dTkCanopyAdj_dTkCanopy = integerMissing ! derivative in the adjusted temperature w.r.t. original temperature + integer(i4b) :: dTkCanopyAdj_dCanWat = integerMissing ! derivative in the adjusted temperature w.r.t. volumetric water content ! snow hydrology integer(i4b) :: scalarSnowAge = integerMissing ! non-dimensional snow age (-) integer(i4b) :: scalarGroundSnowFraction = integerMissing ! fraction of ground that is covered with snow (-) diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index d682a0f50..6f3814a41 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -237,18 +237,21 @@ subroutine computJacob(& dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. temperature + ! canopy derivatives from adjusting the canopy temperature to account for new snow + dTkCanopyAdj_dTkCanopy => diag_data%var(iLookDIAG%dTkCanopyAdj_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in the adjusted temperature w.r.t. original temperature + dTkCanopyAdj_dCanWat => diag_data%var(iLookDIAG%dTkCanopyAdj_dCanWat )%dat(1) ,& ! intent(in): [dp ] derivative in the adjusted temperature w.r.t. canopy water ! derivatives in time mLayerdTemp_dt => deriv_data%var(iLookDERIV%mLayerdTemp_dt )%dat ,& ! intent(in): [dp(:)] timestep change in layer temperature scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt )%dat(1) ,& ! intent(in): [dp ] timestep change in canopy temperature ! diagnostic variables - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) - scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) - mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] bulk volumetric heat capacity in each snow and soil layer (J m-3 K-1) - scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl)%dat(1) ,& ! intent(in): [dp] soil control on infiltration, zero or one + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg )%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg )%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow )%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk )%dat ,& ! intent(in): [dp(:)] bulk volumetric heat capacity in each snow and soil layer (J m-3 K-1) + scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1) ,& ! intent(in): [dp] soil control on infiltration, zero or one ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat & ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth )%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth )%dat & ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) ) ! making association with data in structures ! -------------------------------------------------------------- ! initialize error control @@ -961,15 +964,26 @@ subroutine computJacob(& if(ixVegNrg/=integerMissing .and. ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegNrg) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(0) + aJac(ixTopHyd,ixVegNrg) endif - endif ! (if there are state variables for both water and energy in the soil domain) + endif ! (if there are state variables for both water and energy in the soil domain) ! check case default; err=20; message=trim(message)//'unable to identify option for the type of matrix'; return end select ! type of matrix ! ********************************************************************************************************************************************************* - ! print the Jacobian - if(globalPrintFlag)then + + ! Add in the new-snow adjusted canopy temperature derivatives (for either type of matrix) + if(computeVegFlux)then + if(ixVegNrg/=integerMissing)then + if(ixVegHyd/=integerMissing)then + aJac(:,ixVegHyd) = aJac(:,ixVegHyd)+aJac(:,ixVegNrg)*dTkCanopyAdj_dCanWat + endif + aJac(:,ixVegNrg) = aJac(:,ixVegNrg)*dTkCanopyAdj_dTkCanopy + endif + endif + + ! print the Jacobian + if(globalPrintFlag)then select case(ixMatrix) case(ixBandMatrix) print*, '** banded analytical Jacobian:' @@ -984,7 +998,7 @@ subroutine computJacob(& write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(min(iJac1,nState):min(iJac2,nState),iLayer) end do end select - endif + endif if(any(isNan(aJac)))then print *, '******************************* WE FOUND NAN IN JACOBIAN ************************************' diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index b193565d9..7d25f3f8d 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -273,6 +273,9 @@ subroutine computJacobWithPrime(& dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. temperature + ! canopy derivatives from adjusting the canopy temperature to account for new snow + dTkCanopyAdj_dTkCanopy => deriv_data%var(iLookDERIV%dTkCanopyAdj_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in the adjusted temperature w.r.t. original temperature + dTkCanopyAdj_dCanWat => deriv_data%var(iLookDERIV%dTkCanopyAdj_dCanWat )%dat(1) ,& ! intent(in): [dp ] derivative in the adjusted temperature w.r.t. canopy water ! diagnostic variables scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg )%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg )%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) @@ -1016,8 +1019,19 @@ subroutine computJacobWithPrime(& end select ! type of matrix ! ********************************************************************************************************************************************************* - ! print the Jacobian - if(globalPrintFlag)then + + ! Add in the new-snow adjusted canopy temperature derivatives (for either type of matrix) + if(computeVegFlux)then + if(ixVegNrg/=integerMissing)then + if(ixVegHyd/=integerMissing)then + aJac(:,ixVegHyd) = aJac(:,ixVegHyd)+aJac(:,ixVegNrg)*dTkCanopyAdj_dCanWat + endif + aJac(:,ixVegNrg) = aJac(:,ixVegNrg)*dTkCanopyAdj_dTkCanopy + endif + endif + + ! print the Jacobian + if(globalPrintFlag)then select case(ixMatrix) case(ixBandMatrix) print*, '** banded analytical Jacobian:' @@ -1032,7 +1046,7 @@ subroutine computJacobWithPrime(& write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(min(iJac1,nState):min(iJac2,nState),iLayer) end do end select - endif + endif if(any(isNan(aJac)))then print *, '******************************* WE FOUND NAN IN JACOBIAN ************************************' diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index bf89fa247..10496ecdb 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -578,10 +578,14 @@ subroutine coupled_em(& ! input/output: data structures mpar_data, & ! intent(in): model parameters prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(out): model diagnostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + else + ! no dependency of adjusted temperature since did not adjust temperature + diag_data%var(iLookDIAG%dTkCanopyAdj_dTkCanopy)%dat(1) = 1._rkind + diag_data%var(iLookDIAG%dTkCanopyAdj_dCanWat)%dat(1) = 0._rkind endif ! if computing fluxes over vegetation ! initialize drainage and throughfall diff --git a/build/source/engine/tempAdjust.f90 b/build/source/engine/tempAdjust.f90 index ac42fda60..7485b92cf 100644 --- a/build/source/engine/tempAdjust.f90 +++ b/build/source/engine/tempAdjust.f90 @@ -55,7 +55,7 @@ subroutine tempAdjust(& ! input/output: data structures mpar_data, & ! intent(in): model parameters prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(out): model diagnostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! output: error control err,message) ! intent(out): error control ! ------------------------------------------------------------------------------------------------ @@ -65,27 +65,35 @@ subroutine tempAdjust(& implicit none ! ------------------------------------------------------------------------------------------------ ! input: derived parameters - real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) - ! input/output: data structures - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(inout) :: prog_data ! model prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for a local HRU - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) + ! input/output: data structures + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(inout) :: prog_data ! model prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for a local HRU + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------ ! local variables for canopy thermodynamics - integer(i4b) :: iTry ! trial index - integer(i4b) :: iter ! iteration index - integer(i4b),parameter :: maxiter=100 ! maximum number of iterations - real(rkind) :: fLiq ! fraction of liquid water (-) - real(rkind) :: tempMin,tempMax ! solution constraints for temperature (K) - real(rkind) :: nrgMeltFreeze ! energy required to melt-freeze the water to the current canopy temperature (J m-3) - real(rkind) :: scalarCanopyWat ! total canopy water (kg m-2) - real(rkind) :: scalarCanopyIceOld ! canopy ice content after melt-freeze to the initial temperature (kg m-2) - real(rkind),parameter :: resNrgToler=0.1_rkind ! tolerance for the energy residual (J m-3) + integer(i4b) :: iTry ! trial index + integer(i4b) :: iter ! iteration index + integer(i4b),parameter :: maxiter=100 ! maximum number of iterations + real(rkind) :: fLiq ! fraction of liquid water (-) + real(rkind) :: tempMin,tempMax ! solution constraints for temperature (K) + real(rkind) :: nrgMeltFreeze ! energy required to melt-freeze the water to the current canopy temperature (J m-3) + real(rkind) :: scalarCanopyWat ! total canopy water (kg m-2) + real(rkind) :: scalarCanopyIceOld ! canopy ice content after melt-freeze to the initial temperature (kg m-2) + real(rkind),parameter :: resNrgToler=0.1_rkind ! tolerance for the energy residual (J m-3) real(rkind) :: f1,f2,x1,x2,fTry,xTry,fDer,xInc ! iteration variables - logical(lgt) :: fBis ! .true. if bisection + logical(lgt) :: fBis ! .true. if bisection + ! local variables for computing derivatives + real(rkind) :: dCp_dWat ! derivative of heat capacity with canopy water + real(rkind) :: dCp_dTk ! derivative of heat capacity with canopy temperature + real(rkind) :: dIceOld_dWat ! derivative of canopy ice content after melt-freeze to the initial temp with canopy water + real(rkind) :: dIceOld_dTk ! derivative of canopy ice content after melt-freeze to the initial temp canopy temperature + real(rkind) :: dfTry_dWat, dfDer_dWat ! derivative of iteration variables with canopy water + real(rkind) :: dfTry_dTk, dfDer_dTk ! derivative of iteration variables with canopy temperature + ! ------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='tempAdjust/' @@ -102,18 +110,17 @@ subroutine tempAdjust(& scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1), & ! intent(inout): [dp] temperature of the vegetation canopy (K) ! diagnostic variables (output) scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1), & ! intent(out): [dp] volumetric heat capacity of the vegetation (J m-3 K-1) + ! canopy derivatives from adjusting the canopy temperature + dTkCanopyAdj_dTkCanopy => diag_data%var(iLookDIAG%dTkCanopyAdj_dTkCanopy)%dat(1), & ! intent(in): [dp ] derivative in the adjusted temperature w.r.t. original temperature + dTkCanopyAdj_dCanWat => diag_data%var(iLookDIAG%dTkCanopyAdj_dCanWat)%dat(1) & ! intent(in): [dp ] derivative in the adjusted temperature w.r.t. canopy water ! output: derivatives - dVolHtCapBulk_dCanWat => diag_data%var(iLookDIAG%dVolHtCapBulk_dCanWat)%dat(1), & ! intent(out): [dp] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTkCanopy => diag_data%var(iLookDIAG%dVolHtCapBulk_dTkCanopy)%dat(1) & ! intent(out): [dp] derivative in bulk heat capacity w.r.t. temperature ) ! associate variables in the data structures ! ----------------------------------------------------------------------------------------------------------------------------------------------------- - ! ** preliminaries ! compute the total canopy water (state variable: will not change) scalarCanopyWat = scalarCanopyLiq + scalarCanopyIce - !write(*,'(a,1x,3(f20.10,1x))') 'scalarCanopyWat, scalarCanopyLiq, scalarCanopyIce = ', scalarCanopyWat, scalarCanopyLiq, scalarCanopyIce - + ! compute the fraction of liquid water associated with the canopy temperature fLiq = fracliquid(scalarCanopyTemp,snowfrz_scale) @@ -126,14 +133,6 @@ subroutine tempAdjust(& Cp_water*scalarCanopyLiq/canopyDepth + & ! liquid water component Cp_ice*scalarCanopyIce/canopyDepth ! ice component - ! derivatives, off starting values since do not recompute scalarBulkVolHeatCapVeg - dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) - if(scalarCanopyTemp < Tfreeze)then !use dTheta_dTkCanopy = fLiq * scalarCanopyWatTrial/(iden_water*canopyDepth) - dVolHtCapBulk_dTkCanopy = (-Cp_ice + Cp_water) * scalarCanopyLiq/canopyDepth - else - dVolHtCapBulk_dTkCanopy = 0._rkind - endif - ! compute the energy required to melt-freeze the water to the current canopy temperature (J m-3) nrgMeltFreeze = LH_fus*(scalarCanopyIceOld - scalarCanopyIce)/canopyDepth @@ -234,11 +233,44 @@ subroutine tempAdjust(& end do ! iterating ! ----------------------------------------------------------------------------------------------------------------------------------------------------- - ! update state variables + ! using a one iteration simplication, compute derivatives of tNew = xTry + fTry/fDer with respect to original Tk and Wat + xIce = (1._rkind - fracliquid(xTry,snowfrz_scale))*scalarCanopyWat + fTry = -bulkVolHeatCapVeg*(xTry - scalarCanopyTemp) + LH_fus*(xIce - scalarCanopyIceOld)/canopyDepth + nrgMeltFreeze + fDer = bulkVolHeatCapVeg + scalarCanopyWat*dFracLiq_dTk(xTry,snowfrz_scale)*LH_fus/canopyDepth + + ! heat capacity derivatives + dCp_dWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth + if(scalarCanopyTemp < Tfreeze)then + dCp_dTk = (-Cp_ice + Cp_water) * fLiq * scalarCanopyLiq/canopyDepth ! no derivative in air + else + dCp_dTk = 0._rkind + endif + ! canopy ice content after melt-freeze to the initial temp derivatives + dIceOld_dWat = 1._rkind - fracliquid(scalarCanopyTemp,snowfrz_scale) + dIceOld_dTk = -scalarCanopyWat*dFracLiq_dTk(scalarCanopyTemp,snowfrz_scale) + ! iteration variable derivatives + dfTry_dWat = -dCp_dWat*(xTry - scalarCanopyTemp) & + - LH_fus*(1._rkind - fracliquid(xTry,snowfrz_scale) - dIceOld_dWat)/canopyDepth & + + LH_fus*(dIceOld_dWat)/canopyDepth !maybe (dIceOld_dWat - 1) + dfTry_dTk = -dCp_dTk*(xTry - scalarCanopyTemp) + dCp_dTk & + - LH_fus*dIceOld_dTk/canopyDepth + LH_fus*dIceOld_dTk/canopyDepth + dfDer_dWat = dCp_dWat + dFracLiq_dTk(xTry,snowfrz_scale)*LH_fus/canopyDepth + dfDer_dTk = dCp_dTk + + dTkCanopyAdj_dCanWat = dfTry_dWat/fDer - fTry/(dfDer_dWat**2_i4b) + dTkCanopyAdj_dTkCanopy = dfTry_dTk/fDer - fTry/(dfDer_dTk**2_i4b) + +! update state variables scalarCanopyTemp = xTry scalarCanopyIce = (1._rkind - fracliquid(xTry,snowfrz_scale))*scalarCanopyWat scalarCanopyLiq = scalarCanopyWat - scalarCanopyIce + ! update bulk heat capacity + scalarBulkVolHeatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component + Cp_water*scalarCanopyLiq/canopyDepth + & ! liquid water component + Cp_ice*scalarCanopyIce/canopyDepth ! ice component + + ! end association to variables in the data structure end associate diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 45455bfaa..b5c6dea58 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -105,7 +105,7 @@ subroutine varSubstep(& iStateSplit, & ! intent(in) : index of the state in the splitting operation fluxMask, & ! intent(in) : mask for the fluxes used in this given state subset fluxCount, & ! intent(inout) : number of times that fluxes are updated (should equal nSubsteps) - ! input/output: data structures + ! input/output: data structures model_decisions, & ! intent(in) : model decisions lookup_data, & ! intent(in) : lookup tables type_data, & ! intent(in) : type of vegetation and soil @@ -252,9 +252,7 @@ subroutine varSubstep(& mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat & ! intent(inout): [dp(:)] matric potential of liquid water (m) ) ! end association with variables in the data structures ! ********************************************************************************************************************************************************* - ! ********************************************************************************************************************************************************* - ! Procedure starts here - + ! initialize error control err=0; message='varSubstep/' @@ -974,7 +972,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! check the mass balance fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial - if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues + if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues !write(*,'(a,1x,f20.10)') 'dt = ', dt !write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial !write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 From df1756053d731ed842b11d61e36e08e83adf6cbb Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 6 Jul 2023 10:03:38 +0900 Subject: [PATCH 0789/1472] putting in dcanwat_dTk and dWat in canopySnow iteration and fixing some spaces --- build/source/dshare/popMetadat.f90 | 2 +- build/source/dshare/var_lookup.f90 | 2 +- build/source/engine/canopySnow.f90 | 279 +++++++----- build/source/engine/computHeatCap.f90 | 50 +- build/source/engine/computJacobWithPrime.f90 | 4 +- build/source/engine/computThermConduct.f90 | 166 +++---- build/source/engine/coupled_em.f90 | 11 +- build/source/engine/diagn_evar.f90 | 2 +- build/source/engine/eval8summa.f90 | 264 +++++------ build/source/engine/eval8summaWithPrime.f90 | 452 +++++++++---------- build/source/engine/snow_utils.f90 | 27 +- build/source/engine/tempAdjust.f90 | 262 ++++++----- build/source/engine/varSubstep.f90 | 18 +- build/source/engine/vegNrgFlux.f90 | 8 +- 14 files changed, 833 insertions(+), 714 deletions(-) diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 0dfaf3a60..7d346758b 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -406,7 +406,7 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%scalarCanopyWetFraction) = var_info('scalarCanopyWetFraction' , 'fraction canopy that is wet' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! canopy derivatives from adjusting the canopy temperature to account for new snow diag_meta(iLookDIAG%dTkCanopyAdj_dTkCanopy) = var_info('dTkCanopyAdj_dTkCanopy' , 'derivative in the adjusted temperature w.r.t. original temperature' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%dTkCanopyAdj_dCanWat) = var_info('dTkCanopyAdj_dCanWat', , 'derivative in the adjusted temperature w.r.t. volumetric water content', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%dTkCanopyAdj_dCanWat) = var_info('dTkCanopyAdj_dCanWat' , 'derivative in the adjusted temperature w.r.t. volumetric water content', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! snow hydrology diag_meta(iLookDIAG%scalarSnowAge) = var_info('scalarSnowAge' , 'non-dimensional snow age' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarGroundSnowFraction) = var_info('scalarGroundSnowFraction' , 'fraction ground that is covered with snow' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 43e7a5804..54eb29f49 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -651,7 +651,7 @@ MODULE var_lookup integer(i4b) :: mLayerdTheta_dTk = integerMissing ! derivative of volumetric liquid water content w.r.t. temperature (K-1) integer(i4b) :: mLayerd2Theta_dTk2 = integerMissing ! second derivative of volumetric liquid water content w.r.t. temperature ! derivatives in time - integer(i4b) : mLayerdTemp_dt = integerMissing ! timestep change in layer temperature + integer(i4b) :: mLayerdTemp_dt = integerMissing ! timestep change in layer temperature integer(i4b) :: scalarCanopydTemp_dt = integerMissing ! timestep change in canopy temperature endtype iLook_deriv diff --git a/build/source/engine/canopySnow.f90 b/build/source/engine/canopySnow.f90 index d3ea510fc..64bad8514 100644 --- a/build/source/engine/canopySnow.f90 +++ b/build/source/engine/canopySnow.f90 @@ -67,174 +67,223 @@ subroutine canopySnow(& diag_data, & ! intent(in): model diagnostic variables for a local HRU prog_data, & ! intent(inout): model prognostic variables for a local HRU flux_data, & ! intent(inout): model flux variables + ! output: derivatives + dCanopyIce_dWat, & ! intent(out): derivative of canopy ice with canopy water + dCanopyIce_dTk, & ! intent(out): derivative of canopy ice with canopy temperature ! output: error control err,message) ! intent(out): error control ! ------------------------------------------------------------------------------------------------ + ! utility routines + USE snow_utils_module,only:fracliquid ! compute fraction of liquid water + USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) implicit none ! ------------------------------------------------------------------------------------------------ ! input: model control - real(rkind),intent(in) :: dt ! time step (seconds) - real(rkind),intent(in) :: exposedVAI ! exposed vegetation area index -- leaf + stem -- after burial by snow (m2 m-2) - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) + real(rkind),intent(in) :: dt ! time step (seconds) + real(rkind),intent(in) :: exposedVAI ! exposed vegetation area index -- leaf + stem -- after burial by snow (m2 m-2) + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) ! input/output: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(var_d),intent(in) :: forc_data ! model forcing data - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(in) :: diag_data ! model diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: prog_data ! model prognostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model flux variables + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(var_d),intent(in) :: forc_data ! model forcing data + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(in) :: diag_data ! model diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: prog_data ! model prognostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model flux variables + ! output: derivatives + real(rkind),intent(out) :: dCanopyIce_dWat ! derivative of canopy ice with canopy water + real(rkind),intent(out) :: dCanopyIce_dTk ! derivative of canopy ice with canopy temperature ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! local variables - real(rkind),parameter :: valueMissing=-9999._rkind ! missing value - integer(i4b) :: iter ! iteration index - integer(i4b),parameter :: maxiter=50 ! maximum number of iterations - real(rkind) :: unloading_melt ! unloading associated with canopy drip (kg m-2 s-1) - real(rkind) :: airtemp_degC ! value of air temperature in degrees Celcius - real(rkind) :: leafScaleFactor ! scaling factor for interception based on temperature (-) - real(rkind) :: leafInterceptCapSnow ! storage capacity for snow per unit leaf area (kg m-2) - real(rkind) :: canopyIceScaleFactor ! capacity scaling factor for throughfall (kg m-2) - real(rkind) :: throughfallDeriv ! derivative in throughfall flux w.r.t. canopy storage (s-1) - real(rkind) :: unloadingDeriv ! derivative in unloading flux w.r.t. canopy storage (s-1) - real(rkind) :: scalarCanopyIceIter ! trial value for mass of ice on the vegetation canopy (kg m-2) (kg m-2) - real(rkind) :: flux ! net flux (kg m-2 s-1) - real(rkind) :: delS ! change in storage (kg m-2) - real(rkind) :: resMass ! residual in mass equation (kg m-2) - real(rkind) :: tempUnloadingFun ! temperature unloading functions, Eq. 14 in Roesch et al. 2001 - real(rkind) :: windUnloadingFun ! temperature unloading functions, Eq. 15 in Roesch et al. 2001 - real(rkind),parameter :: convTolerMass=0.0001_rkind ! convergence tolerance for mass (kg m-2) + real(rkind),parameter :: valueMissing=-9999._rkind ! missing value + integer(i4b) :: iter ! iteration index + integer(i4b),parameter :: maxiter=50 ! maximum number of iterations + logical :: use_drip ! branch decision used in computing for first canopy ice derivative (code shortcut) + real(rkind) :: scalarCanopyWat ! total canopy water (kg m-2) + real(rkind) :: unloading_melt ! unloading associated with canopy drip (kg m-2 s-1) + real(rkind) :: airtemp_degC ! value of air temperature in degrees Celcius + real(rkind) :: leafScaleFactor ! scaling factor for interception based on temperature (-) + real(rkind) :: leafInterceptCapSnow ! storage capacity for snow per unit leaf area (kg m-2) + real(rkind) :: canopyIceScaleFactor ! capacity scaling factor for throughfall (kg m-2) + real(rkind) :: throughfallDeriv ! derivative in throughfall flux w.r.t. canopy storage (s-1) + real(rkind) :: unloadingDeriv ! derivative in unloading flux w.r.t. canopy storage (s-1) + real(rkind) :: scalarCanopyIceIter ! trial value for mass of ice on the vegetation canopy (kg m-2) (kg m-2) + real(rkind) :: fLiq ! fraction of liquid water (-) + real(rkind) :: canopyLiqDrainageDeriv ! derivative in canopy drainage w.r.t. canopy liquid water (s-1) (recomputed from vegLiqFlus) + real(rkind) :: dCanopyIceIter_dWat,dCanopyIceIter_dTk ! derivates of canopy ice iteration w.r.t canopy water and canopy temperature + real(rkind) :: flux ! net flux (kg m-2 s-1) + real(rkind) :: delS ! change in storage (kg m-2) + real(rkind) :: resMass ! residual in mass equation (kg m-2) + real(rkind) :: tempUnloadingFun ! temperature unloading functions, Eq. 14 in Roesch et al. 2001 + real(rkind) :: windUnloadingFun ! temperature unloading functions, Eq. 15 in Roesch et al. 2001 + real(rkind),parameter :: convTolerMass=0.0001_rkind ! convergence tolerance for mass (kg m-2) + real(rkind) :: ddelS_dWat, ddelS_dTk ! derivates of iteration increment w.r.t canopy water and canopy temperature ! ------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='canopySnow/' ! ------------------------------------------------------------------------------------------------ ! associate variables in the data structure associate(& - ! model decisions - ixSnowInterception => model_decisions(iLookDECISIONS%snowIncept)%iDecision, & ! intent(in): [i4b] choice of option to determine maximum snow interception capacity - ixSnowUnload => model_decisions(iLookDECISIONS%snowUnload)%iDecision, & ! intent(in): [i4b] choice of option to determing how snow unloads from canopy - + ixSnowInterception => model_decisions(iLookDECISIONS%snowIncept)%iDecision, & ! intent(in): [i4b] choice of option to determine maximum snow interception capacity + ixSnowUnload => model_decisions(iLookDECISIONS%snowUnload)%iDecision, & ! intent(in): [i4b] choice of option to determing how snow unloads from canopy ! model forcing data - scalarAirtemp => forc_data%var(iLookFORCE%airtemp), & ! intent(in): [dp] air temperature (K) - + scalarAirtemp => forc_data%var(iLookFORCE%airtemp), & ! intent(in): [dp] air temperature (K) ! model parameters - refInterceptCapSnow => mpar_data%var(iLookPARAM%refInterceptCapSnow)%dat(1), & ! intent(in): [dp] reference canopy interception capacity for snow per unit leaf area (kg m-2) - ratioDrip2Unloading => mpar_data%var(iLookPARAM%ratioDrip2Unloading)%dat(1), & ! intent(in): [dp] ratio of canopy drip to snow unloading (-) - snowUnloadingCoeff => mpar_data%var(iLookPARAM%snowUnloadingCoeff)%dat(1), & ! intent(in): [dp] time constant for unloading of snow from the forest canopy (s-1) - minTempUnloading => mpar_data%var(iLookPARAM%minTempUnloading)%dat(1), & ! constant describing the minimum temperature for snow unloading in windySnow parameterization (K) - minWindUnloading => mpar_data%var(iLookPARAM%minWindUnloading)%dat(1), & ! constant describing the minimum temperature for snow unloading in windySnow parameterization (K) - rateTempUnloading => mpar_data%var(iLookPARAM%rateTempUnloading)%dat(1), & ! constant describing how quickly snow will unload due to temperature in windySnow parameterization (K s) - rateWindUnloading => mpar_data%var(iLookPARAM%rateWindUnloading)%dat(1), & ! constant describing how quickly snow will unload due to wind in windySnow parameterization (K s) - + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! intent(in): [dp] scaling factor for snow freezing curve (K) + refInterceptCapSnow => mpar_data%var(iLookPARAM%refInterceptCapSnow)%dat(1), & ! intent(in): [dp] reference canopy interception capacity for snow per unit leaf area (kg m-2) + ratioDrip2Unloading => mpar_data%var(iLookPARAM%ratioDrip2Unloading)%dat(1), & ! intent(in): [dp] ratio of canopy drip to snow unloading (-) + snowUnloadingCoeff => mpar_data%var(iLookPARAM%snowUnloadingCoeff)%dat(1), & ! intent(in): [dp] time constant for unloading of snow from the forest canopy (s-1) + minTempUnloading => mpar_data%var(iLookPARAM%minTempUnloading)%dat(1), & ! intent(in): [dp] constant describing the minimum temperature for snow unloading in windySnow parameterization (K) + minWindUnloading => mpar_data%var(iLookPARAM%minWindUnloading)%dat(1), & ! intent(in): [dp] constant describing the minimum temperature for snow unloading in windySnow parameterization (K) + rateTempUnloading => mpar_data%var(iLookPARAM%rateTempUnloading)%dat(1), & ! intent(in): [dp] constant describing how quickly snow will unload due to temperature in windySnow parameterization (K s) + rateWindUnloading => mpar_data%var(iLookPARAM%rateWindUnloading)%dat(1), & ! intent(in): [dp] constant describing how quickly snow will unload due to wind in windySnow parameterization (K s) + scalarCanopyLiqMax => diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1), & ! intent(in): [dp] maximum storage before canopy drainage begins (kg m-2 s-1) + scalarCanopyDrainageCoeff => mpar_data%var(iLookPARAM%canopyDrainageCoeff)%dat(1), & ! intent(in): [dp] canopy drainage coefficient (s-1) ! model diagnostic variables - scalarNewSnowDensity => diag_data%var(iLookDIAG%scalarNewSnowDensity)%dat(1), & ! intent(in): [dp] density of new snow (kg m-3) - + scalarNewSnowDensity => diag_data%var(iLookDIAG%scalarNewSnowDensity)%dat(1), & ! intent(in): [dp] density of new snow (kg m-3) ! model prognostic variables (input/output) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1), & ! intent(in): [dp] mass of liquid water on the vegetation canopy (kg m-2) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! intent(inout): [dp] mass of ice on the vegetation canopy (kg m-2) - + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1), & ! intent(in): [dp] temperature of the vegetation canopy (K) ! model fluxes (input) - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1), & ! intent(in): [dp] temperature of the canopy air space (k) - scalarSnowfall => flux_data%var(iLookFLUX%scalarSnowfall)%dat(1), & ! intent(in): [dp] computed snowfall rate (kg m-2 s-1) - scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1), & ! intent(in): [dp] liquid drainage from the vegetation canopy (kg m-2 s-1) - scalarWindspdCanopyTop => flux_data%var(iLookFLUX%scalarWindspdCanopyTop)%dat(1), & ! intent(in): [dp] windspeed at the top of the canopy (m s-1) + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1), & ! intent(in): [dp] temperature of the canopy air space (k) + scalarSnowfall => flux_data%var(iLookFLUX%scalarSnowfall)%dat(1), & ! intent(in): [dp] computed snowfall rate (kg m-2 s-1) + scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1), & ! intent(in): [dp] liquid drainage from the vegetation canopy (kg m-2 s-1) + scalarWindspdCanopyTop => flux_data%var(iLookFLUX%scalarWindspdCanopyTop)%dat(1), & ! intent(in): [dp] windspeed at the top of the canopy (m s-1) ! model variables (output) - scalarThroughfallSnow => flux_data%var(iLookFLUX%scalarThroughfallSnow)%dat(1), & ! intent(out): [dp] snow that reaches the ground without ever touching the canopy (kg m-2 s-1) - scalarCanopySnowUnloading => flux_data%var(iLookFLUX%scalarCanopySnowUnloading)%dat(1) & ! intent(out): [dp] unloading of snow from the vegetion canopy (kg m-2 s-1) - + scalarThroughfallSnow => flux_data%var(iLookFLUX%scalarThroughfallSnow)%dat(1), & ! intent(out): [dp] snow that reaches the ground without ever touching the canopy (kg m-2 s-1) + scalarCanopySnowUnloading => flux_data%var(iLookFLUX%scalarCanopySnowUnloading)%dat(1) & ! intent(out): [dp] unloading of snow from the vegetion canopy (kg m-2 s-1) ) ! associate variables in the data structures ! ----------------------------------------------------------------------------------------------------------------------------------------------------- + ! compute the total canopy water (state variable: will not change) + scalarCanopyWat = scalarCanopyLiq + scalarCanopyIce + + ! compute the initial derivatives + fLiq = fracliquid(scalarCanopyTemp,snowfrz_scale) + dCanopyIce_dWat = 1._rkind - fLiq + dCanopyIce_dTk = -dFracLiq_dTk(scalarCanopyTemp,snowfrz_scale)*scalarCanopyWat + ! compute unloading due to melt drip... ! ************************************* if(computeVegFlux)then - unloading_melt = min(ratioDrip2Unloading*scalarCanopyLiqDrainage, scalarCanopyIce/dt) ! kg m-2 s-1 + unloading_melt = min(ratioDrip2Unloading*scalarCanopyLiqDrainage, scalarCanopyIce/dt) ! kg m-2 s-1 + if(scalarCanopyIce/dt < ratioDrip2Unloading*scalarCanopyLiqDrainage)then + unloadingDeriv = 1._rkind/dt + else ! unloadingDeriv = ratioDrip2Unloading*canopyLiqDrainageDeriv*(fLiq*scalarCanopyWat) + if(scalarCanopyLiq > scalarCanopyLiqMax)then !from vegLiqFlux last step + canopyLiqDrainageDeriv = scalarCanopyDrainageCoeff + else + canopyLiqDrainageDeriv = 0._rkind + endif + unloadingDeriv = -ratioDrip2Unloading*canopyLiqDrainageDeriv + dCanopyIce_dWat = dCanopyIce_dWat - ratioDrip2Unloading*canopyLiqDrainageDeriv*fLiq*dt + use_drip = .true. + endif else - unloading_melt = 0._rkind + unloading_melt = 0._rkind + unloadingDeriv = 0._rkind end if scalarCanopyIce = scalarCanopyIce - unloading_melt*dt + if(.not.use_drip) dCanopyIce_dWat = (1._rkind - unloadingDeriv*dt)*dCanopyIce_dWat + dCanopyIce_dTk = (1._rkind - unloadingDeriv*dt)*dCanopyIce_dTk + + ! initialize + dCanopyIceIter_dWat = dCanopyIce_dWat + dCanopyIceIter_dTk = dCanopyIce_dTk ! ***** ! compute the ice balance due to snowfall and unloading... ! ******************************************************** ! check for early returns if(.not.computeVegFlux .or. (scalarSnowfall= minWindUnloading) then - windUnloadingFun = abs(scalarWindspdCanopyTop) / rateWindUnloading ! (s-1) + ! ** compute unloading + if(ixSnowUnload==meltDripUnload)then + scalarCanopySnowUnloading = snowUnloadingCoeff*scalarCanopyIceIter + unloadingDeriv = snowUnloadingCoeff + else if (ixSnowUnload==windUnload) then + tempUnloadingFun = max(scalarCanairTemp - minTempUnloading, 0._rkind) / rateTempUnloading ! (s-1) + if(scalarWindspdCanopyTop >= minWindUnloading)then + windUnloadingFun = abs(scalarWindspdCanopyTop) / rateWindUnloading ! (s-1) + else + windUnloadingFun = 0._rkind ! (s-1) + end if + ! implement the "windySnow" Roesch et al. 2001 parameterization, Eq. 13 in Roesch et al. 2001 + scalarCanopySnowUnloading = scalarCanopyIceIter * (tempUnloadingFun + windUnloadingFun) + unloadingDeriv = tempUnloadingFun + windUnloadingFun + end if + ! no snowfall + if(scalarSnowfall -1._rkind)then + leafScaleFactor = 4.0_rkind + elseif(airtemp_degC > -3._rkind)then + leafScaleFactor = 1.5_rkind*airtemp_degC + 5.5_rkind else - windUnloadingFun = 0._rkind ! (s-1) + leafScaleFactor = 1.0_rkind end if - ! implement the "windySnow" Roesch et al. 2001 parameterization, Eq. 13 in Roesch et al. 2001 - scalarCanopySnowUnloading = scalarCanopyIceIter * (tempUnloadingFun + windUnloadingFun) - unloadingDeriv = tempUnloadingFun + windUnloadingFun - end if - ! no snowfall - if(scalarSnowfall -1._rkind) then - leafScaleFactor = 4.0_rkind - elseif(airtemp_degC > -3._rkind) then - leafScaleFactor = 1.5_rkind*airtemp_degC + 5.5_rkind - else - leafScaleFactor = 1.0_rkind - end if - leafInterceptCapSnow = refInterceptCapSnow*leafScaleFactor - case default - message=trim(message)//'unable to identify option for maximum branch interception capacity' - err=20; return - end select - ! compute maximum interception capacity for the canopy - canopyIceScaleFactor = leafInterceptCapSnow*exposedVAI - ! (compute throughfall) - scalarThroughfallSnow = scalarSnowfall*(scalarCanopyIceIter/canopyIceScaleFactor) - throughfallDeriv = scalarSnowfall/canopyIceScaleFactor - end if ! (if snow is falling) - ! ** compute iteration increment - flux = scalarSnowfall - scalarThroughfallSnow - scalarCanopySnowUnloading ! net flux (kg m-2 s-1) - delS = (flux*dt - (scalarCanopyIceIter - scalarCanopyIce))/(1._rkind + (throughfallDeriv + unloadingDeriv)*dt) - ! ** check for convergence - resMass = scalarCanopyIceIter - (scalarCanopyIce + flux*dt) - if(abs(resMass) < convTolerMass)exit - ! ** check for non-convengence - if(iter==maxiter)then; err=20; message=trim(message)//'failed to converge [mass]'; return; end if - ! ** update value - scalarCanopyIceIter = scalarCanopyIceIter + delS + leafInterceptCapSnow = refInterceptCapSnow*leafScaleFactor + case default + message=trim(message)//'unable to identify option for maximum branch interception capacity' + err=20; return + end select + ! compute maximum interception capacity for the canopy + canopyIceScaleFactor = leafInterceptCapSnow*exposedVAI + ! compute throughfall + scalarThroughfallSnow = scalarSnowfall*(scalarCanopyIceIter/canopyIceScaleFactor) + throughfallDeriv = scalarSnowfall/canopyIceScaleFactor + end if ! (if snow is falling) + ! ** compute iteration increment + flux = scalarSnowfall - scalarThroughfallSnow - scalarCanopySnowUnloading ! net flux (kg m-2 s-1) + delS = (flux*dt - (scalarCanopyIceIter - scalarCanopyIce))/(1._rkind + (throughfallDeriv + unloadingDeriv)*dt) + ! ** compute derivatives of delS + ddelS_dWat = (((-throughfallDeriv - unloadingDeriv)*dt - 1._rkind)*dCanopyIceIter_dWat + dCanopyIce_dWat) & + /(1._rkind + (throughfallDeriv + unloadingDeriv)*dt) + ddelS_dTk = (((-throughfallDeriv - unloadingDeriv)*dt - 1._rkind)*dCanopyIceIter_dTk + dCanopyIce_dTk) & + /(1._rkind + (throughfallDeriv + unloadingDeriv)*dt) + ! ** check for convergence + resMass = scalarCanopyIceIter - (scalarCanopyIce + flux*dt) + if(abs(resMass) < convTolerMass)exit + ! ** check for non-convengence + if(iter==maxiter)then; err=20; message=trim(message)//'failed to converge [mass]'; return; end if + ! ** update value + scalarCanopyIceIter = scalarCanopyIceIter + delS + dCanopyIceIter_dWat = dCanopyIceIter_dWat + ddelS_dWat + dCanopyIceIter_dTk = dCanopyIceIter_dTk + ddelS_dTk end do ! iterating ! add the unloading associated with melt drip (kg m-2 s-1) scalarCanopySnowUnloading = scalarCanopySnowUnloading + unloading_melt ! ***** - ! update mass of ice on the canopy (kg m-2) + ! update mass of ice on the canopy (kg m-2) and derivatives scalarCanopyIce = scalarCanopyIceIter + dCanopyIce_dWat = dCanopyIceIter_dWat + dCanopyIce_dTk = dCanopyIceIter_dTk ! end association to variables in the data structure end associate diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index f45e721b3..b0c48ad67 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -87,7 +87,6 @@ subroutine computHeatCap(& mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! input: state variables scalarCanopyIce, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) scalarCanopyLiquid, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) @@ -134,7 +133,6 @@ subroutine computHeatCap(& type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model layer indices type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! input: state variables real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) real(rkind),intent(in) :: scalarCanopyLiquid ! trial value for the liquid water on the vegetation canopy (kg m-2) @@ -148,7 +146,7 @@ subroutine computHeatCap(& real(rkind),intent(in) :: mLayerTempPrev(:) ! previous temperature real(rkind),intent(in) :: mLayerEnthalpyTrial(:) ! trial enthalpy for snow and soil real(rkind),intent(in) :: mLayerEnthalpyPrev(:) ! previous enthalpy for snow and soil - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! vector of total water matric potential (m) + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! vector of total water matric potential (m) ! input: pre-computed derivatives real(rkind),intent(in) :: dTheta_dTkCanopy ! derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) real(rkind),intent(in) :: scalarFracLiqVeg ! fraction of canopy liquid water (-) @@ -162,10 +160,10 @@ subroutine computHeatCap(& ! output: real(qp),intent(out) :: heatCapVeg ! heat capacity for canopy real(qp),intent(out) :: mLayerHeatCap(:) ! heat capacity for snow and soil - real(rkind),intent(out) :: dVolHtCapBulk_dPsi0 ! derivative in bulk heat capacity w.r.t. matric potential - real(rkind),intent(out) :: dVolHtCapBulk_dTheta ! derivative in bulk heat capacity w.r.t. volumetric water content + real(rkind),intent(out) :: dVolHtCapBulk_dPsi0(:) ! derivative in bulk heat capacity w.r.t. matric potential + real(rkind),intent(out) :: dVolHtCapBulk_dTheta(:) ! derivative in bulk heat capacity w.r.t. volumetric water content real(rkind),intent(out) :: dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. volumetric water content - real(rkind),intent(out) :: dVolHtCapBulk_dTk ! derivative in bulk heat capacity w.r.t. temperature + real(rkind),intent(out) :: dVolHtCapBulk_dTk(:) ! derivative in bulk heat capacity w.r.t. temperature real(rkind),intent(out) :: dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature ! output: error control integer(i4b),intent(out) :: err ! error code @@ -174,22 +172,22 @@ subroutine computHeatCap(& ! local variables integer(i4b) :: iLayer ! index of model layer integer(i4b) :: iSoil ! index of soil layer - real(rkind) :: delT - real(rkind) :: delEnt + real(rkind) :: delT ! temperature change + real(rkind) :: delEnt ! enthalpy change real(rkind) :: fLiq ! fraction of liquid water real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) ! -------------------------------------------------------------------------------------------------------------------------------- ! associate variables in data structure associate(& ! input: coordinate variables - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers + layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) ! input: heat capacity and thermal conductivity - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) ! input: depth varying soil parameters - iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) + iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) ) ! end associate statemen ! initialize error control err=0; message="computHeatCap/" @@ -233,8 +231,8 @@ subroutine computHeatCap(& ! * soil case(iname_soil) mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component - iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component ! derivatives dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use @@ -440,10 +438,10 @@ subroutine computHeatCapAnalytic(& ! output real(qp),intent(out) :: heatCapVeg ! heat capacity for canopy real(qp),intent(out) :: mLayerHeatCap(:) ! heat capacity for snow and soil - real(rkind),intent(out) :: dVolHtCapBulk_dPsi0 ! derivative in bulk heat capacity w.r.t. matric potential - real(rkind),intent(out) :: dVolHtCapBulk_dTheta ! derivative in bulk heat capacity w.r.t. volumetric water content + real(rkind),intent(out) :: dVolHtCapBulk_dPsi0(:) ! derivative in bulk heat capacity w.r.t. matric potential + real(rkind),intent(out) :: dVolHtCapBulk_dTheta(:) ! derivative in bulk heat capacity w.r.t. volumetric water content real(rkind),intent(out) :: dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. volumetric water content - real(rkind),intent(out) :: dVolHtCapBulk_dTk ! derivative in bulk heat capacity w.r.t. temperature + real(rkind),intent(out) :: dVolHtCapBulk_dTk(:) ! derivative in bulk heat capacity w.r.t. temperature real(rkind),intent(out) :: dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature ! output: error control integer(i4b),intent(out) :: err ! error code @@ -459,15 +457,15 @@ subroutine computHeatCapAnalytic(& ! associate variables in data structure associate(& ! input: coordinate variables - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers - layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers + layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) ! input: heat capacity and thermal conductivity - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) ! input: depth varying soil parameters - iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) + iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) ) ! end associate statement ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index 7d25f3f8d..3febecc11 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -274,8 +274,8 @@ subroutine computJacobWithPrime(& dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. temperature ! canopy derivatives from adjusting the canopy temperature to account for new snow - dTkCanopyAdj_dTkCanopy => deriv_data%var(iLookDERIV%dTkCanopyAdj_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in the adjusted temperature w.r.t. original temperature - dTkCanopyAdj_dCanWat => deriv_data%var(iLookDERIV%dTkCanopyAdj_dCanWat )%dat(1) ,& ! intent(in): [dp ] derivative in the adjusted temperature w.r.t. canopy water + dTkCanopyAdj_dTkCanopy => diag_data%var(iLookDIAG%dTkCanopyAdj_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in the adjusted temperature w.r.t. original temperature + dTkCanopyAdj_dCanWat => diag_data%var(iLookDIAG%dTkCanopyAdj_dCanWat )%dat(1) ,& ! intent(in): [dp ] derivative in the adjusted temperature w.r.t. canopy water ! diagnostic variables scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg )%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg )%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) diff --git a/build/source/engine/computThermConduct.f90 b/build/source/engine/computThermConduct.f90 index 62ad3e13c..92ea99c27 100644 --- a/build/source/engine/computThermConduct.f90 +++ b/build/source/engine/computThermConduct.f90 @@ -71,16 +71,16 @@ module computThermConduct_module ! ********************************************************************************************************** subroutine computThermConduct(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux nLayers, & ! intent(in): total number of layers - canopyDepth, & ! intent(in): canopy depth (m) + canopyDepth, & ! intent(in): canopy depth (m) ! input: state variables - scalarCanopyIce, & ! intent(in): canopy ice content (kg m-2) - scalarCanopyLiquid, & ! intent(in): canopy liquid water content (kg m-2) - mLayerTemp, & ! intent(in): temperature at the current iteration (K) - mLayerMatricHead, & ! intent(in): matric head at the current iteration(m) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + scalarCanopyIce, & ! intent(in): canopy ice content (kg m-2) + scalarCanopyLiquid, & ! intent(in): canopy liquid water content (kg m-2) + mLayerTemp, & ! intent(in): temperature at the current iteration (K) + mLayerMatricHead, & ! intent(in): matric head at the current iteration(m) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) ! input: pre-computed derivatives mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) @@ -95,7 +95,7 @@ subroutine computThermConduct(& dThermalC_dTempAbove, & ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above dThermalC_dTempBelow, & ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above ! output: error control - err,message) ! intent(out): error control + err,message) ! intent(out): error control ! utility modules USE snow_utils_module,only:tcond_snow ! compute thermal conductivity of snow @@ -106,103 +106,103 @@ subroutine computThermConduct(& implicit none ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain - real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) + logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain + real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) ! input: trial model state variables - real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) + real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) real(rkind),intent(in) :: scalarCanopyLiquid - real(rkind),intent(in) :: mLayerTemp(:) ! temperature in each layer at the current iteration (m) - real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head in each layer at the current iteration (m) - real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice at the current iteration (-) - real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid at the current iteration (-) + real(rkind),intent(in) :: mLayerTemp(:) ! temperature in each layer at the current iteration (m) + real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head in each layer at the current iteration (m) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice at the current iteration (-) + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid at the current iteration (-) ! input: pre-computed derivatives - real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative in volumetric liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) + real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative in volumetric liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) ! input/output: data structures - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model layer indices - type(var_dlength),intent(in) :: prog_data ! model prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for a local HRU + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model layer indices + type(var_dlength),intent(in) :: prog_data ! model prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for a local HRU ! output: derivatives - real(rkind),intent(out) :: dThermalC_dWatAbove(:) ! derivative in the thermal conductivity w.r.t. water state in the layer above - real(rkind),intent(out) :: dThermalC_dWatBelow(:) ! derivative in the thermal conductivity w.r.t. water state in the layer above - real(rkind),intent(out) :: dThermalC_dTempAbove(:)! derivative in the thermal conductivity w.r.t. energy state in the layer above - real(rkind),intent(out) :: dThermalC_dTempBelow(:)! derivative in the thermal conductivity w.r.t. energy state in the layer above + real(rkind),intent(out) :: dThermalC_dWatAbove(0:) ! derivative in the thermal conductivity w.r.t. water state in the layer above + real(rkind),intent(out) :: dThermalC_dWatBelow(0:) ! derivative in the thermal conductivity w.r.t. water state in the layer above + real(rkind),intent(out) :: dThermalC_dTempAbove(0:) ! derivative in the thermal conductivity w.r.t. energy state in the layer above + real(rkind),intent(out) :: dThermalC_dTempBelow(0:) ! derivative in the thermal conductivity w.r.t. energy state in the layer above ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables - character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: iSoil ! index of soil layer - real(rkind) :: TCn ! thermal conductivity below the layer interface (W m-1 K-1) - real(rkind) :: TCp ! thermal conductivity above the layer interface (W m-1 K-1) - real(rkind) :: zdn ! height difference between interface and lower value (m) - real(rkind) :: zdp ! height difference between interface and upper value (m) - real(rkind) :: bulkden_soil ! bulk density of soil (kg m-3) - real(rkind) :: lambda_drysoil ! thermal conductivity of dry soil (W m-1) - real(rkind) :: lambda_wetsoil ! thermal conductivity of wet soil (W m-1) - real(rkind) :: lambda_wet ! thermal conductivity of the wet material - real(rkind) :: relativeSat ! relative saturation (-) - real(rkind) :: kerstenNum ! the Kersten number (-), defining weight applied to conductivity of the wet medium - real(rkind) :: den ! denominator in the thermal conductivity calculations - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - real(rkind),dimension(nLayers) :: dThermalC_dWat ! derivative in thermal conductivity w.r.t. matric head or volumetric liquid water - real(rkind),dimension(nLayers) :: dThermalC_dNrg ! derivative in thermal conductivity w.r.t. temperature - real(rkind) :: dlambda_wet_dWat ! derivative in thermal conductivity of wet material w.r.t.soil water state variable - real(rkind) :: dlambda_wet_dTk ! derivative in thermal conductivity of wet material w.r.t. temperature - real(rkind) :: dkerstenNum_dWat ! derivative in Kersten number w.r.t. soil water state variable - real(rkind) :: dVolFracLiq_dWat ! derivative in vol fraction of liquid w.r.t. water state variable - real(rkind) :: dVolFracIce_dWat ! derivative in vol fraction of ice w.r.t. water state variable - real(rkind) :: dVolFracLiq_dTk ! derivative in vol fraction of liquid w.r.t. temperature - real(rkind) :: dVolFracIce_dTk ! derivative in vol fraction of ice w.r.t. temperature + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: iSoil ! index of soil layer + real(rkind) :: TCn ! thermal conductivity below the layer interface (W m-1 K-1) + real(rkind) :: TCp ! thermal conductivity above the layer interface (W m-1 K-1) + real(rkind) :: zdn ! height difference between interface and lower value (m) + real(rkind) :: zdp ! height difference between interface and upper value (m) + real(rkind) :: bulkden_soil ! bulk density of soil (kg m-3) + real(rkind) :: lambda_drysoil ! thermal conductivity of dry soil (W m-1) + real(rkind) :: lambda_wetsoil ! thermal conductivity of wet soil (W m-1) + real(rkind) :: lambda_wet ! thermal conductivity of the wet material + real(rkind) :: relativeSat ! relative saturation (-) + real(rkind) :: kerstenNum ! the Kersten number (-), defining weight applied to conductivity of the wet medium + real(rkind) :: den ! denominator in the thermal conductivity calculations + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind),dimension(nLayers) :: dThermalC_dWat ! derivative in thermal conductivity w.r.t. matric head or volumetric liquid water + real(rkind),dimension(nLayers) :: dThermalC_dNrg ! derivative in thermal conductivity w.r.t. temperature + real(rkind) :: dlambda_wet_dWat ! derivative in thermal conductivity of wet material w.r.t.soil water state variable + real(rkind) :: dlambda_wet_dTk ! derivative in thermal conductivity of wet material w.r.t. temperature + real(rkind) :: dkerstenNum_dWat ! derivative in Kersten number w.r.t. soil water state variable + real(rkind) :: dVolFracLiq_dWat ! derivative in vol fraction of liquid w.r.t. water state variable + real(rkind) :: dVolFracIce_dWat ! derivative in vol fraction of ice w.r.t. water state variable + real(rkind) :: dVolFracLiq_dTk ! derivative in vol fraction of liquid w.r.t. temperature + real(rkind) :: dVolFracIce_dTk ! derivative in vol fraction of ice w.r.t. temperature ! local variables to reproduce the thermal conductivity of Hansson et al. VZJ 2005 - real(rkind),parameter :: c1=0.55_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c2=0.8_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c3=3.07_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: c4=0.13_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c5=4._rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: f1=13.05_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: f2=1.06_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind) :: fArg,xArg ! temporary variables (see Hansson et al. VZJ 2005 for details) - real(rkind) :: dxArg_dWat,dxArg_dTk ! derivates of the temporary variables with respect to soil water state variable and temperature + real(rkind),parameter :: c1=0.55_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c2=0.8_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c3=3.07_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: c4=0.13_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c5=4._rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: f1=13.05_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: f2=1.06_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind) :: fArg,xArg ! temporary variables (see Hansson et al. VZJ 2005 for details) + real(rkind) :: dxArg_dWat,dxArg_dTk ! derivates of the temporary variables with respect to soil water state variable and temperature ! -------------------------------------------------------------------------------------------------------------------------------- ! associate variables in data structure associate(& ! input: model decisions - ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision, & ! intent(in): index of the form of Richards' equation - ixThCondSnow => model_decisions(iLookDECISIONS%thCondSnow)%iDecision, & ! intent(in): choice of method for thermal conductivity of snow - ixThCondSoil => model_decisions(iLookDECISIONS%thCondSoil)%iDecision, & ! intent(in): choice of method for thermal conductivity of soil + ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision, & ! intent(in): [i4b] index of the form of Richards' equation + ixThCondSnow => model_decisions(iLookDECISIONS%thCondSnow)%iDecision, & ! intent(in): [i4b] choice of method for thermal conductivity of snow + ixThCondSoil => model_decisions(iLookDECISIONS%thCondSoil)%iDecision, & ! intent(in): [i4b] choice of method for thermal conductivity of soil ! input: coordinate variables - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1), & ! intent(in): number of soil layers - layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) - mLayerHeight => prog_data%var(iLookPROG%mLayerHeight)%dat, & ! intent(in): height at the mid-point of each layer (m) - iLayerHeight => prog_data%var(iLookPROG%iLayerHeight)%dat, & ! intent(in): height at the interface of each layer (m) + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): [dp] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1), & ! intent(in): [dp] number of soil layers + layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): [dp(:)] layer type (iname_soil or iname_snow) + mLayerHeight => prog_data%var(iLookPROG%mLayerHeight)%dat, & ! intent(in): [dp(:)] height at the mid-point of each layer (m) + iLayerHeight => prog_data%var(iLookPROG%iLayerHeight)%dat, & ! intent(in): [dp(:)] height at the interface of each layer (m) ! input: heat capacity and thermal conductivity - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) - fixedThermalCond_snow => mpar_data%var(iLookPARAM%fixedThermalCond_snow)%dat(1), & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): [dp] specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): [dp] maximum mass of vegetation (kg m-2) + fixedThermalCond_snow => mpar_data%var(iLookPARAM%fixedThermalCond_snow)%dat(1), & ! intent(in): [dp] temporally constant thermal conductivity of snow (W m-1 K-1) ! input: depth varying soil parameters - iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) - thCond_soil => mpar_data%var(iLookPARAM%thCond_soil)%dat, & ! intent(in): thermal conductivity of soil (W m-1 K-1) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): soil porosity (-) - frac_sand => mpar_data%var(iLookPARAM%frac_sand)%dat, & ! intent(in): fraction of sand (-) - frac_silt => mpar_data%var(iLookPARAM%frac_silt)%dat, & ! intent(in): fraction of silt (-) - frac_clay => mpar_data%var(iLookPARAM%frac_clay)%dat, & ! intent(in): fraction of clay (-) + iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): [dp(:)] intrinsic density of soil (kg m-3) + thCond_soil => mpar_data%var(iLookPARAM%thCond_soil)%dat, & ! intent(in): [dp(:)] thermal conductivity of soil (W m-1 K-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): [dp(:)] soil porosity (-) + frac_sand => mpar_data%var(iLookPARAM%frac_sand)%dat, & ! intent(in): [dp(:)] fraction of sand (-) + frac_silt => mpar_data%var(iLookPARAM%frac_silt)%dat, & ! intent(in): [dp(:)] fraction of silt (-) + frac_clay => mpar_data%var(iLookPARAM%frac_clay)%dat, & ! intent(in): [dp(:)] fraction of clay (-) vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat, & ! intent(in): [dp(:)] van Genutchen "m" parameter (-) vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat, & ! intent(in): [dp(:)] van Genutchen "n" parameter (-) vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat, & ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) theta_res => mpar_data%var(iLookPARAM%theta_res)%dat, & ! intent(in): [dp(:)] soil residual volumetric water content (-) ! input: snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) ! output: diagnostic variables and derivatives (diagnostic as may be treated as constant) - mLayerThermalC => diag_data%var(iLookDIAG%mLayerThermalC)%dat, & ! intent(out): thermal conductivity at the mid-point of each layer (W m-1 K-1) - iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(out): thermal conductivity at the interface of each layer (W m-1 K-1) - mLayerVolFracAir => diag_data%var(iLookDIAG%mLayerVolFracAir)%dat & ! intent(out): volumetric fraction of air in each layer (-) + mLayerThermalC => diag_data%var(iLookDIAG%mLayerThermalC)%dat, & ! intent(out): [dp(:)] thermal conductivity at the mid-point of each layer (W m-1 K-1) + iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(out): [dp(:)] thermal conductivity at the interface of each layer (W m-1 K-1) + mLayerVolFracAir => diag_data%var(iLookDIAG%mLayerVolFracAir)%dat & ! intent(out): [dp(:)] volumetric fraction of air in each layer (-) ) ! association of local variables with information in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 10496ecdb..005f593f2 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -219,6 +219,9 @@ subroutine coupled_em(& type(var_dlength) :: prog_temp ! temporary model prognostic variables type(var_dlength) :: diag_temp ! temporary model diagnostic variables real(rkind),allocatable :: mLayerVolFracIceInit(:)! initial vector for volumetric fraction of ice (-) + ! canopy ice derivatives for canopy temperature adjustment + real(rkind) :: dCanopyIce_dWat ! derivative of canopy ice with canopy water + real(rkind) :: dCanopyIce_dTk ! derivative of canopy ice with canopy temperature ! check SWE real(rkind) :: oldSWE ! SWE at the start of the substep real(rkind) :: newSWE ! SWE at the end of the substep @@ -566,6 +569,9 @@ subroutine coupled_em(& diag_data, & ! intent(in): model diagnostic variables for a local HRU prog_data, & ! intent(inout): model prognostic variables for a local HRU flux_data, & ! intent(inout): model flux variables + ! output: derivatives + dCanopyIce_dWat, & ! intent(out): derivative of canopy ice with canopy water + dCanopyIce_dTk, & ! intent(out): derivative of canopy ice with canopy temperature ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if @@ -574,7 +580,10 @@ subroutine coupled_em(& if(computeVegFlux)then ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) call tempAdjust(& ! input: derived parameters - canopyDepth, & ! intent(in): canopy depth (m) + canopyDepth, & ! intent(in): canopy depth (m) + ! input: derivatives + dCanopyIce_dWat, & ! intent(in): derivative of canopy ice with canopy water + dCanopyIce_dTk, & ! intent(in): derivative of canopy ice with canopy temperature ! input/output: data structures mpar_data, & ! intent(in): model parameters prog_data, & ! intent(inout): model prognostic variables for a local HRU diff --git a/build/source/engine/diagn_evar.f90 b/build/source/engine/diagn_evar.f90 index 0a17a26dd..eb3308e1d 100644 --- a/build/source/engine/diagn_evar.f90 +++ b/build/source/engine/diagn_evar.f90 @@ -168,7 +168,7 @@ subroutine diagn_evar(& mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat, & ! intent(out): volumetric heat capacity in each layer (J m-3 K-1) mLayerThermalC => diag_data%var(iLookDIAG%mLayerThermalC)%dat, & ! intent(out): thermal conductivity at the mid-point of each layer (W m-1 K-1) iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(out): thermal conductivity at the interface of each layer (W m-1 K-1) - mLayerVolFracAir => diag_data%var(iLookDIAG%mLayerVolFracAir)%dat, & ! intent(out): volumetric fraction of air in each layer (-) + mLayerVolFracAir => diag_data%var(iLookDIAG%mLayerVolFracAir)%dat & ! intent(out): volumetric fraction of air in each layer (-) ) ! end associate statement ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 87107bb8d..9bbcfe0fe 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -220,61 +220,61 @@ subroutine eval8summa(& ! -------------------------------------------------------------------------------------------------------------------------------- associate(& ! model decisions - ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver - ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy - ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver + ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy + ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) ! soil parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) ! model state variables from the previous solution - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] total water matric potential (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) - scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in): [dp] storage of water in the aquifer (m) + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] total water matric potential (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) + scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in): [dp] storage of water in the aquifer (m) ! model diagnostic variables from the previous solution - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) ! enthalpy from the previous solution - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(in): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(in): [dp] enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(in): [dp(:)] enthalpy of the snow+soil layers (J m-3) ! soil compression scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2 s-1) mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in volumetric water content due to compression of soil (s-1) ! derivatives - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1),& ! intent(out): [dp] derivative in bulk heat capacity w.r.t. volumetric water content dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1),& !intent(out):[dp] derivative in bulk heat capacity w.r.t. temperature - dThermalC_dWatAbove => deriv_data%var(iLookDERIV%dThermalC_dWatAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above - ! mapping - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) - ! indices - heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),&! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & ! intent(out): heat capacity for snow and soil + dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1),&!intent(out): [dp] derivative in bulk heat capacity w.r.t. temperature + dThermalC_dWatAbove => deriv_data%var(iLookDERIV%dThermalC_dWatAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + ! mapping + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)]mapping of full state vector to the state subset + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)]index of control volume for different domains (veg, snow, soil) + ! heat capacity + heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),&! intent(out): [dp] volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & ! intent(out): [dp(:)] heat capacity for snow and soil ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -290,9 +290,9 @@ subroutine eval8summa(& prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: feasibility - feasible, & ! intent(inout): flag to denote the feasibility of the solution + feasible, & ! intent(inout): flag to denote the feasibility of the solution ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control ! early return for non-feasible solutions if(.not.feasible)then @@ -336,18 +336,18 @@ subroutine eval8summa(& prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(inout): trial value of storage of water in the aquifer (m) + scalarAquiferStorageTrial,& ! intent(inout): trial value of storage of water in the aquifer (m) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) @@ -382,7 +382,7 @@ subroutine eval8summa(& if(ixHowHeatCap == enthalpyFD)then ! compute H_T without phase change call t2enthalpy(& - .false., & ! intent(in): logical flag to not include phase change in enthalpy + .false., & ! intent(in): ogical flag to not include phase change in enthalpy ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure @@ -399,19 +399,19 @@ subroutine eval8summa(& mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) - dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature - dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state - dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature - dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state + scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -454,11 +454,11 @@ subroutine eval8summa(& ! output heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil - dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -491,11 +491,11 @@ subroutine eval8summa(& ! output heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow - dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature ! output: error control err,cmessage) ! intent(out): error control endif !(choice of how compute heat capacity) @@ -503,31 +503,31 @@ subroutine eval8summa(& ! compute multiplier of state vector call computStatMult(& ! input - heatCapVegTrial, & ! intent(in): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(in): volumetric heat capacity of soil and snow - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers + heatCapVegTrial, & ! intent(in): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(in): volumetric heat capacity of soil and snow + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers ! output - sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) - err,cmessage) ! intent(out): error control + sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) ! update thermal conductivity call computThermConduct(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - nLayers, & ! intent(in): total number of layers - canopyDepth, & ! intent(in): canopy depth (m) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + nLayers, & ! intent(in): total number of layers + canopyDepth, & ! intent(in): canopy depth (m) ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value of canopy liquid water (kg m-2) - mLayerTempTrial, & ! intent(in): trial temperature of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value of canopy liquid water (kg m-2) + mLayerTempTrial, & ! intent(in): trial temperature of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) ! input: pre-computed derivatives - mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) ! input/output: data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices @@ -558,18 +558,18 @@ subroutine eval8summa(& ! compute C_m call computCm(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux ! input: state variables - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (m) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (m) ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices ! output - scalarCanopyCmTrial, & ! intent(out): Cm for vegetation - mLayerCmTrial, & ! intent(out): Cm for soil and snow - err,cmessage) ! intent(out): error control + scalarCanopyCmTrial, & ! intent(out): Cm for vegetation + mLayerCmTrial, & ! intent(out): Cm for soil and snow + err,cmessage) ! intent(out): error control else scalarCanopyCmTrial = 0._qp mLayerCmTrial = 0._qp @@ -634,18 +634,18 @@ subroutine eval8summa(& call soilCmpres(& ! input: dt_cur, & ! intent(in): length of the time step (seconds) - ixRichards, & ! intent(in): choice of option for Richards' equation - ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers - mLayerMatricHead(1:nSoil), & ! intent(in): matric head at the start of the time step (m) - mLayerMatricHeadTrial(1:nSoil), & ! intent(in): trial value of matric head (m) - mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) - mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric ice content in each soil layer (-) - specificStorage, & ! intent(in): specific storage coefficient (m-1) - theta_sat, & ! intent(in): soil porosity (-) + ixRichards, & ! intent(in): choice of option for Richards' equation + ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers + mLayerMatricHead(1:nSoil), & ! intent(in): matric head at the start of the time step (m) + mLayerMatricHeadTrial(1:nSoil), & ! intent(in): trial value of matric head (m) + mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) + mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric ice content in each soil layer (-) + specificStorage, & ! intent(in): specific storage coefficient (m-1) + theta_sat, & ! intent(in): soil porosity (-) ! output: mLayerCompress, & ! intent(inout): compressibility of the soil matrix (-) dCompress_dPsi, & ! intent(inout): derivative in compressibility w.r.t. matric head (m-1) - err,cmessage) ! intent(out): error code and error message + err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! compute the total change in storage associated with compression of the soil matrix (kg m-2 s-1) @@ -654,36 +654,36 @@ subroutine eval8summa(& ! compute the residual vector call computResid(& ! input: model control - dt_cur, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers + dt_cur, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers ! input: flux vectors - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) - fluxVec, & ! intent(in): flux vector + sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + fluxVec, & ! intent(in): flux vector ! input: state variables (already disaggregated into scalars and vectors) - scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) - scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) - scalarCanopyWatTrial, & ! intent(in): trial value for the water on the vegetation canopy (kg m-2) - mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) - scalarAquiferStorageTrial, & ! intent(in): trial value of storage of water in the aquifer (m) + scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) + scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) + scalarCanopyWatTrial, & ! intent(in): trial value for the water on the vegetation canopy (kg m-2) + mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) + scalarAquiferStorageTrial, & ! intent(in): trial value of storage of water in the aquifer (m) ! input: diagnostic variables defining the liquid water and ice content (function of state variables) - scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value for the liq on the vegetation canopy (kg m-2) - mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) - mLayerVolFracWatTrial, & ! intent(in): trial value for the volumetric water in each snow and soil layer (-) - mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liq in each snow and soil layer (-) - scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) - mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) + scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value for the liq on the vegetation canopy (kg m-2) + mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) + mLayerVolFracWatTrial, & ! intent(in): trial value for the volumetric water in each snow and soil layer (-) + mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liq in each snow and soil layer (-) + scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) + mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) ! input: data structures - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - flux_data, & ! intent(in): model fluxes for a local HRU - indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + flux_data, & ! intent(in): model fluxes for a local HRU + indx_data, & ! intent(in): index data ! output - resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - resVec, & ! intent(out): residual vector - err,cmessage) ! intent(out): error control + resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + resVec, & ! intent(out): residual vector + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! compute the function evaluation diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 4dd75dd38..c6fa29486 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -121,62 +121,62 @@ subroutine eval8summaWithPrime(& err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- ! provide access to subroutines - USE getVectorz_module, only:varExtract ! extract variables from the state vector - USE getVectorz_module, only:checkFeas ! check feasibility of state vector - USE updateVarsWithPrime_module, only:updateVarsWithPrime ! update variables - USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy - USE computFlux_module, only:soilCmpresPrime ! compute soil compression - USE computFlux_module, only:computFlux ! compute fluxes given a state vector - USE computHeatCap_module,only:computHeatCap ! recompute heat capacity and derivatives - USE computHeatCap_module,only:computHeatCapAnalytic ! recompute heat capacity and derivatives + USE getVectorz_module, only:varExtract ! extract variables from the state vector + USE getVectorz_module, only:checkFeas ! check feasibility of state vector + USE updateVarsWithPrime_module, only:updateVarsWithPrime ! update variables + USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy + USE computFlux_module, only:soilCmpresPrime ! compute soil compression + USE computFlux_module, only:computFlux ! compute fluxes given a state vector + USE computHeatCap_module,only:computHeatCap ! recompute heat capacity and derivatives + USE computHeatCap_module,only:computHeatCapAnalytic ! recompute heat capacity and derivatives USE computHeatCap_module,only:computCm - USE computHeatCap_module, only:computStatMult ! recompute state multiplier - USE computResidWithPrime_module,only:computResidWithPrime ! compute residuals given a state vector - USE computThermConduct_module,only:computThermConduct ! recompute thermal conductivity and derivatives + USE computHeatCap_module, only:computStatMult ! recompute state multiplier + USE computResidWithPrime_module,only:computResidWithPrime ! compute residuals given a state vector + USE computThermConduct_module,only:computThermConduct ! recompute thermal conductivity and derivatives implicit none ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control - real(rkind),intent(in) :: dt ! entire time step for drainage pond rate - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - integer,intent(in) :: nState ! total number of state variables - logical(lgt),intent(in) :: insideSUN ! flag to indicate if we are inside Sundials solver - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt),intent(inout) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + real(rkind),intent(in) :: dt ! entire time step for drainage pond rate + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + integer,intent(in) :: nState ! total number of state variables + logical(lgt),intent(in) :: insideSUN ! flag to indicate if we are inside Sundials solver + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt),intent(inout) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors - real(rkind),intent(in) :: stateVec(:) ! model state vector - real(rkind),intent(in) :: stateVecPrime(:) ! model state vector - real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + real(rkind),intent(in) :: stateVec(:) ! model state vector + real(rkind),intent(in) :: stateVecPrime(:) ! model state vector + real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) ! input: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(zLookup), intent(in) :: lookup_data ! lookup tables - type(var_i), intent(in) :: type_data ! type of vegetation and soil - type(var_d), intent(in) :: attr_data ! spatial attributes - type(var_dlength), intent(in) :: mpar_data ! model parameters - type(var_d), intent(in) :: forc_data ! model forcing data - type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin - type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup), intent(in) :: lookup_data ! lookup tables + type(var_i), intent(in) :: type_data ! type of vegetation and soil + type(var_d), intent(in) :: attr_data ! spatial attributes + type(var_dlength), intent(in) :: mpar_data ! model parameters + type(var_d), intent(in) :: forc_data ! model forcing data + type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin + type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU ! output: data structures - type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! input-output: here we need to pass some extra variables that do not get updated in in the IDA loops - real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind),intent(in) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) - real(rkind),intent(out) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: scalarCanopyIcePrev ! previous value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(out) :: scalarCanopyLiqTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: scalarCanopyLiqPrev ! previous value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(out) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(in) :: scalarCanopyEnthalpyPrev ! previous value of enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(in) :: mLayerTempPrev(:) ! previous vector of layer temperature (K) + real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind),intent(in) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) + real(rkind),intent(out) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: scalarCanopyIcePrev ! previous value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(out) :: scalarCanopyLiqTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: scalarCanopyLiqPrev ! previous value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(out) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(in) :: scalarCanopyEnthalpyPrev ! previous value of enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(in) :: mLayerTempPrev(:) ! previous vector of layer temperature (K) real(rkind),intent(out) :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial value for total water matric potential (m) real(rkind),intent(in) :: mLayerMatricHeadPrev(:) ! previous value for total water matric potential (m) @@ -197,16 +197,16 @@ subroutine eval8summaWithPrime(& real(rkind),intent(out) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) real(rkind),intent(out) :: scalarCanopyWatPrime ! derivative value for total water content of the vegetation canopy (kg m-2) ! input-output: baseflow - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer - real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer + real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors - logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution - real(rkind),intent(out) :: fluxVec(:) ! flux vector - real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation - real(qp),intent(out) :: resVec(:) ! NOTE: qp ! residual vector + logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution + real(rkind),intent(out) :: fluxVec(:) ! flux vector + real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation + real(qp),intent(out) :: resVec(:) ! NOTE: qp ! residual vector ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- @@ -244,43 +244,43 @@ subroutine eval8summaWithPrime(& ! -------------------------------------------------------------------------------------------------------------------------------- associate(& ! model decisions - ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy - ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation + ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy + ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) ! soil parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) ! model diagnostic variables from a previous solution - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) - scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) ! soil compression - scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2 s-1) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in volumetric water content due to compression of soil (s-1) + scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2 s-1) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in volumetric water content due to compression of soil (s-1) ! derivatives - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential - dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1),& ! intent(out): [dp] derivative in bulk heat capacity w.r.t. volumetric water content dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1),& !intent(out):[dp] derivative in bulk heat capacity w.r.t. temperature - dThermalC_dWatAbove => deriv_data%var(iLookDERIV%dThermalC_dWatAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1),&!intent(out): [dp] derivative in bulk heat capacity w.r.t. temperature + dThermalC_dWatAbove => deriv_data%var(iLookDERIV%dThermalC_dWatAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above ! mapping - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) - ! indices - heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & ! intent(out): heat capacity for snow and soil + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)]mapping of full state vector to the state subset + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)]index of control volume for different domains (veg, snow, soil) + ! heat capacity + heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(out): [dp] volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & ! intent(out): [dp(:)] heat capacity for snow and soil ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -298,7 +298,7 @@ subroutine eval8summaWithPrime(& ! output: feasibility feasible, & ! intent(inout): flag to denote the feasibility of the solution ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control ! early return for non-feasible solutions if(.not.feasible)then @@ -340,16 +340,16 @@ subroutine eval8summaWithPrime(& prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) ! output: variables for the aquifer scalarAquiferStorageTrial,& ! intent(inout): trial value of storage of water in the aquifer (m) ! output: error control @@ -378,18 +378,18 @@ subroutine eval8summaWithPrime(& prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy - scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K) - scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(niout): derivative of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2) + scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K) + scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(niout): derivative of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K) - mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (-) - mLayerMatricHeadPrime, & ! intent(inout): derivative of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): derivative of liquid water matric potential (m) + mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K) + mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (-) + mLayerMatricHeadPrime, & ! intent(inout): derivative of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): derivative of liquid water matric potential (m) ! output: variables for the aquifer - scalarAquiferStoragePrime,& ! intent(inout): derivative of storage of water in the aquifer (m) + scalarAquiferStoragePrime,& ! intent(inout): derivative of storage of water in the aquifer (m) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) @@ -452,19 +452,19 @@ subroutine eval8summaWithPrime(& mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) - dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature - dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state - dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature - dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state + scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -472,81 +472,81 @@ subroutine eval8summaWithPrime(& ! *** compute volumetric heat capacity C_p = dH_T/dT call computHeatCap(& ! input: control variables - nLayers, & ! intent(in): number of layers (soil+snow) - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) + nLayers, & ! intent(in): number of layers (soil+snow) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) ! input output data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) - scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial temperature - mLayerTempPrev, & ! intent(in): previous temperature - mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil - mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil - mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) + scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial temperature + mLayerTempPrev, & ! intent(in): previous temperature + mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil + mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil + mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - dCanEnthalpy_dTk, & ! intent(in): derivatives in canopy enthalpy w.r.t. temperature - dCanEnthalpy_dWat, & ! intent(in): derivatives in canopy enthalpy w.r.t. water state - dEnthalpy_dTk, & ! intent(in): derivatives in layer enthalpy w.r.t. temperature - dEnthalpy_dWat, & ! intent(in): derivatives in layer enthalpy w.r.t. water state + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + dCanEnthalpy_dTk, & ! intent(in): derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat, & ! intent(in): derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk, & ! intent(in): derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat, & ! intent(in): derivatives in layer enthalpy w.r.t. water state ! output - heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil - dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil + dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif else if(ixHowHeatCap == closedForm)then call computHeatCapAnalytic(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) + scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! input output data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! output - heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow - dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow + dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control endif !(choice of how compute heat capacity) ! compute multiplier of state vector @@ -564,19 +564,19 @@ subroutine eval8summaWithPrime(& ! update thermal conductivity call computThermConduct(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - nLayers, & ! intent(in): total number of layers - canopyDepth, & ! intent(in): canopy depth (m) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + nLayers, & ! intent(in): total number of layers + canopyDepth, & ! intent(in): canopy depth (m) ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value of canopy liquid water (kg m-2) - mLayerTempTrial, & ! intent(in): trial temperature of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value of canopy liquid water (kg m-2) + mLayerTempTrial, & ! intent(in): trial temperature of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) ! input: pre-computed derivatives - mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) ! input/output: data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices @@ -608,18 +608,18 @@ subroutine eval8summaWithPrime(& ! compute C_m call computCm(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux ! input: state variables - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (m) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (m) ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices ! output - scalarCanopyCmTrial, & ! intent(out): Cm for vegetation - mLayerCmTrial, & ! intent(out): Cm for soil and snow - err,cmessage) ! intent(out): error control + scalarCanopyCmTrial, & ! intent(out): Cm for vegetation + mLayerCmTrial, & ! intent(out): Cm for soil and snow + err,cmessage) ! intent(out): error control else scalarCanopyCmTrial = 0._qp mLayerCmTrial = 0._qp @@ -680,17 +680,17 @@ subroutine eval8summaWithPrime(& ! NOTE: we already extracted trial matrix head and volumetric liquid water as part of the flux calculations call soilCmpresPrime(& ! input: - ixRichards, & ! intent(in): choice of option for Richards' equation - ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers - mLayerMatricHeadPrime(1:nSoil), & ! intent(in): matric head at the start of the time step (m) - mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) - mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric ice content in each soil layer (-) - specificStorage, & ! intent(in): specific storage coefficient (m-1) - theta_sat, & ! intent(in): soil porosity (-) + ixRichards, & ! intent(in): choice of option for Richards' equation + ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers + mLayerMatricHeadPrime(1:nSoil), & ! intent(in): matric head at the start of the time step (m) + mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) + mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric ice content in each soil layer (-) + specificStorage, & ! intent(in): specific storage coefficient (m-1) + theta_sat, & ! intent(in): soil porosity (-) ! output: mLayerCompress, & ! intent(inout): compressibility of the soil matrix (-) dCompress_dPsi, & ! intent(inout): derivative in compressibility w.r.t. matric head (m-1) - err,cmessage) ! intent(out): error code and error message + err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! compute the total change in storage associated with compression of the soil matrix (kg m-2 s-1) @@ -702,38 +702,38 @@ subroutine eval8summaWithPrime(& call computResidWithPrime(& ! input: model control - dt1, & ! intent(in): length of the residual time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers + dt1, & ! intent(in): length of the residual time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers ! input: flux vectors - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) - fluxVec, & ! intent(in): flux vector + sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + fluxVec, & ! intent(in): flux vector ! input: state variables (already disaggregated into scalars and vectors) - scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) - mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) - scalarCanairTempPrime, & ! intent(in): Prime value for the temperature of the canopy air space (K s-1) - scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K s-1) - scalarCanopyWatPrime, & ! intent(in): Prime value for the water on the vegetation canopy (kg m-2 s-1) - mLayerTempPrime, & ! intent(in): Prime value for the temperature of each snow and soil layer (K s-1) - scalarAquiferStoragePrime, & ! intent(in): Prime value of storage of water in the aquifer (m s-1) + scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) + mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) + scalarCanairTempPrime, & ! intent(in): Prime value for the temperature of the canopy air space (K s-1) + scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K s-1) + scalarCanopyWatPrime, & ! intent(in): Prime value for the water on the vegetation canopy (kg m-2 s-1) + mLayerTempPrime, & ! intent(in): Prime value for the temperature of each snow and soil layer (K s-1) + scalarAquiferStoragePrime, & ! intent(in): Prime value of storage of water in the aquifer (m s-1) ! input: diagnostic variables defining the liquid water and ice content (function of state variables) - scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2 s-1) - scalarCanopyLiqPrime, & ! intent(in): Prime value for the liq on the vegetation canopy (kg m-2 s-1) - mLayerVolFracIcePrime, & ! intent(in): Prime value for the volumetric ice in each snow and soil layer (s-1) - mLayerVolFracWatPrime, & ! intent(in): Prime value for the volumetric water in each snow and soil layer (s-1) - mLayerVolFracLiqPrime, & ! intent(in): Prime value for the volumetric liq in each snow and soil layer (s-1) - scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) - mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) + scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2 s-1) + scalarCanopyLiqPrime, & ! intent(in): Prime value for the liq on the vegetation canopy (kg m-2 s-1) + mLayerVolFracIcePrime, & ! intent(in): Prime value for the volumetric ice in each snow and soil layer (s-1) + mLayerVolFracWatPrime, & ! intent(in): Prime value for the volumetric water in each snow and soil layer (s-1) + mLayerVolFracLiqPrime, & ! intent(in): Prime value for the volumetric liq in each snow and soil layer (s-1) + scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) + mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) ! input: data structures - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - flux_data, & ! intent(in): model fluxes for a local HRU - indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + flux_data, & ! intent(in): model fluxes for a local HRU + indx_data, & ! intent(in): index data ! output - resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - resVec, & ! intent(out): residual vector - err,cmessage) ! intent(out): error control + resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + resVec, & ! intent(out): residual vector + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) else !currently not using residuals outside Sundials! diff --git a/build/source/engine/snow_utils.f90 b/build/source/engine/snow_utils.f90 index 672d8c67d..18b88ac97 100644 --- a/build/source/engine/snow_utils.f90 +++ b/build/source/engine/snow_utils.f90 @@ -38,6 +38,7 @@ module snow_utils_module public::fracliquid public::templiquid public::dFracLiq_dTk +public::d2FracLiq_dTk2 public::tcond_snow contains @@ -54,7 +55,6 @@ function fracliquid(Tk,fc_param) fracliquid = 1._rkind / ( 1._rkind + (fc_param*( Tfreeze - min(Tk,Tfreeze) ))**2_i4b ) end function fracliquid - ! *********************************************************************************************************** ! public function templiquid: invert the fraction of liquid water function ! *********************************************************************************************************** @@ -67,7 +67,6 @@ function templiquid(fracliquid,fc_param) templiquid = Tfreeze - ((1._rkind/fracliquid - 1._rkind)/fc_param**2_i4b)**(0.5_rkind) end function templiquid - ! *********************************************************************************************************** ! public function dFracLiq_dTk: differentiate the freezing curve ! *********************************************************************************************************** @@ -87,6 +86,30 @@ function dFracLiq_dTk(Tk,fc_param) dFracLiq_dTk = (fc_param*2._rkind*Tdim) / ( ( 1._rkind + Tdim**2_i4b)**2_i4b ) end function dFracLiq_dTk +! *********************************************************************************************************** +! public function d2FracLiq2_dTk2: differentiate the freezing curve twice +! *********************************************************************************************************** +function d2FracLiq_dTk2(Tk,fc_param) + implicit none + ! dummies + real(rkind),intent(in) :: Tk ! temperature (K) + real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) + real(rkind) :: d2FracLiq_dTk2 ! differentiate the freezing curve twice (K-2) + ! locals + real(rkind) :: Tdep ! temperature depression (K) + real(rkind) :: Tdim ! dimensionless temperature (-) + real(rkind) :: fPart1,fPart2 ! different parts of a function + real(rkind) :: dPart1,dPart2 ! derivatives for different parts of a function + ! compute local variables (just to make things more efficient) + Tdep = Tfreeze - min(Tk,Tfreeze) + Tdim = fc_param*Tdep + fPart1 = fc_param*2._rkind*Tdim + fPart2 = 1._rkind/( ( 1._rkind + Tdim**2_i4b)**2_i4b ) + ! differentiate the freezing curve w.r.t temperature twice + dPart1 = fc_param*2._rkind*Tdim/Tk + dPart2 = (fc_param*4._rkind*Tdim) / ( ( 1._rkind + Tdim**2_i4b)**2_i4b ) + d2FracLiq_dTk2 = fPart1*dPart2 + dPart1*fPart2 +end function d2FracLiq_dTk2 ! *********************************************************************************************************** ! public subroutine tcond_snow: compute thermal conductivity of snow diff --git a/build/source/engine/tempAdjust.f90 b/build/source/engine/tempAdjust.f90 index 7485b92cf..23b007ba9 100644 --- a/build/source/engine/tempAdjust.f90 +++ b/build/source/engine/tempAdjust.f90 @@ -51,7 +51,10 @@ module tempAdjust_module ! ************************************************************************************************ subroutine tempAdjust(& ! input: derived parameters - canopyDepth, & ! intent(in): canopy depth (m) + canopyDepth, & ! intent(in): canopy depth (m) + ! input: derivatives + dCanopyIce_dWat, & ! intent(in): derivative of canopy ice with canopy water + dCanopyIce_dTk, & ! intent(in): derivative of canopy ice with canopy temperature ! input/output: data structures mpar_data, & ! intent(in): model parameters prog_data, & ! intent(inout): model prognostic variables for a local HRU @@ -62,10 +65,14 @@ subroutine tempAdjust(& ! utility routines USE snow_utils_module,only:fracliquid ! compute fraction of liquid water USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) + USE snow_utils_module,only:d2FracLiq_dTk2 ! differentiate the freezing curve twice w.r.t. temperature (snow) implicit none ! ------------------------------------------------------------------------------------------------ ! input: derived parameters real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) + ! input: derivatives + real(rkind),intent(in) :: dCanopyIce_dWat ! derivative of canopy ice with canopy water + real(rkind),intent(in) :: dCanopyIce_dTk ! derivative of canopy ice with canopy temperature ! input/output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_dlength),intent(inout) :: prog_data ! model prognostic variables for a local HRU @@ -88,12 +95,11 @@ subroutine tempAdjust(& logical(lgt) :: fBis ! .true. if bisection ! local variables for computing derivatives real(rkind) :: dCp_dWat ! derivative of heat capacity with canopy water - real(rkind) :: dCp_dTk ! derivative of heat capacity with canopy temperature - real(rkind) :: dIceOld_dWat ! derivative of canopy ice content after melt-freeze to the initial temp with canopy water - real(rkind) :: dIceOld_dTk ! derivative of canopy ice content after melt-freeze to the initial temp canopy temperature - real(rkind) :: dfTry_dWat, dfDer_dWat ! derivative of iteration variables with canopy water - real(rkind) :: dfTry_dTk, dfDer_dTk ! derivative of iteration variables with canopy temperature - + real(rkind) :: dCp_dTk ! derivative of heat capacity with canopy temperature + real(rkind) :: dxTry_dWat,dx2_dWat,dtempMin_dWat,dtempMax_dWat ! derivative of iteration temperature and brackets w.r.t. canopy water + real(rkind) :: dxTry_dTk, dx2_dTk, dtempMin_dTk, dtempMax_dTk ! derivative of iteration temperature and brackets w.r.t. canopy temperature + real(rkind) :: df1_dWat,dfTry_dWat,dfDer_dWat ! derivative of iteration variables w.r.t. canopy water + real(rkind) :: df1_dTk, dfTry_dTk, dfDer_dTk ! derivative of iteration variables w.r.t. canopy temperature ! ------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='tempAdjust/' @@ -109,10 +115,10 @@ subroutine tempAdjust(& scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! intent(inout): [dp] mass of ice on the vegetation canopy (kg m-2) scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1), & ! intent(inout): [dp] temperature of the vegetation canopy (K) ! diagnostic variables (output) - scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1), & ! intent(out): [dp] volumetric heat capacity of the vegetation (J m-3 K-1) + scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1), & ! intent(out): [dp] volumetric heat capacity of the vegetation (J m-3 K-1) ! canopy derivatives from adjusting the canopy temperature - dTkCanopyAdj_dTkCanopy => diag_data%var(iLookDIAG%dTkCanopyAdj_dTkCanopy)%dat(1), & ! intent(in): [dp ] derivative in the adjusted temperature w.r.t. original temperature - dTkCanopyAdj_dCanWat => diag_data%var(iLookDIAG%dTkCanopyAdj_dCanWat)%dat(1) & ! intent(in): [dp ] derivative in the adjusted temperature w.r.t. canopy water + dTkCanopyAdj_dTkCanopy => diag_data%var(iLookDIAG%dTkCanopyAdj_dTkCanopy)%dat(1), & ! intent(in): [dp] derivative in the adjusted temperature w.r.t. original temperature + dTkCanopyAdj_dCanWat => diag_data%var(iLookDIAG%dTkCanopyAdj_dCanWat)%dat(1) & ! intent(in): [dp] derivative in the adjusted temperature w.r.t. canopy water ! output: derivatives ) ! associate variables in the data structures ! ----------------------------------------------------------------------------------------------------------------------------------------------------- @@ -133,6 +139,14 @@ subroutine tempAdjust(& Cp_water*scalarCanopyLiq/canopyDepth + & ! liquid water component Cp_ice*scalarCanopyIce/canopyDepth ! ice component + ! heat capacity derivatives + dCp_dWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth + if(scalarCanopyTemp < Tfreeze)then + dCp_dTk = (-Cp_ice + Cp_water) * fLiq * scalarCanopyLiq/canopyDepth ! no derivative in air + else + dCp_dTk = 0._rkind + endif + ! compute the energy required to melt-freeze the water to the current canopy temperature (J m-3) nrgMeltFreeze = LH_fus*(scalarCanopyIceOld - scalarCanopyIce)/canopyDepth @@ -140,41 +154,60 @@ subroutine tempAdjust(& ! ** get ready for iterating - ! compute initial function and derivative + ! compute initial function and derivatives x1 = scalarCanopyTemp f1 = nrgMeltFreeze + df1_dWat = LH_fus*((1._rkind - fLiq) - dCanopyIce_dWat )/canopyDepth + df1_dTk = LH_fus*( -dFracLiq_dTk(scalarCanopyTemp,snowfrz_scale) - dCanopyIce_dTk )/canopyDepth fDer = resNrgDer(x1,scalarBulkVolHeatCapVeg,snowfrz_scale) + dfDer_dWat = dCp_dWat + dFracLiq_dTk(x1,snowfrz_scale)*LH_fus/canopyDepth + dfDer_dTk = dCp_dTk + scalarCanopyWat*d2FracLiq_dTk2(x1,snowfrz_scale)*LH_fus/canopyDepth ! compute new function based on newton step from the first function x2 = x1 + f1 / fDer f2 = resNrgFunc(x2,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) + ! compute derivatives + dx2_dWat = df1_dWat/fDer - f1/(dfDer_dWat**2_i4b) + dx2_dTk = 1._rkind + df1_dTk/fDer - f1/(dfDer_dTk**2_i4b) - ! ensure that we bracket the root + ! ensure that we bracket the root and recompute x2 and derivatives if not if(f1*f2 > 0._rkind)then - xInc = f1 / fDer - x2 = 1._rkind - do iter=1,maxiter - ! successively expand limit in order to bracket the root - x2 = x1 + sign(x2,xInc)*2._rkind - f2 = resNrgFunc(x2,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) - if(f1*f2 < 0._rkind)exit - ! check that we bracketed the root (should get here in just a couple of expansions) - if(iter==maxiter)then - message=trim(message)//'unable to bracket the root' - err=20; return - end if - end do ! trying to bracket the root + xInc = f1 / fDer + x2 = 1._rkind + dx2_dWat = 0._rkind + dx2_dTk = 0._rkind + do iter=1,maxiter + ! successively expand limit in order to bracket the root + x2 = x1 + sign(x2,xInc)*2._rkind + f2 = resNrgFunc(x2,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) + ! compute derivatives + dx2_dWat = 0._rkind + dx2_dTk = 1._rkind + if(f1*f2 < 0._rkind)exit + ! check that we bracketed the root (should get here in just a couple of expansions) + if(iter==maxiter)then + message=trim(message)//'unable to bracket the root' + err=20; return + end if + end do ! trying to bracket the root end if ! first check that we bracketed the root - ! define initial constraints + ! define initial constraints and derivatives if(x1 < x2)then - tempMin = x1 - tempMax = x2 + tempMin = x1 + tempMax = x2 + dtempMin_dWat = 0._rkind + dtempMin_dTk = 1._rkind + dtempMax_dWat = dx2_dWat + dtempMax_dTk = dx2_dTk else - tempMin = x2 - tempMax = x1 + tempMin = x2 + tempMax = x1 + dtempMin_dWat = dx2_dWat + dtempMin_dTk = dx2_dTk + dtempMax_dWat = 0._rkind + dtempMax_dTk = 1._rkind end if - !print*, 'tempMin, tempMax = ', tempMin, tempMax ! get starting trial xInc = huge(1._rkind) @@ -182,95 +215,102 @@ subroutine tempAdjust(& fTry = resNrgFunc(xTry,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) fDer = resNrgDer(xTry,scalarBulkVolHeatCapVeg,snowfrz_scale) - ! check the functions at the limits (should be of opposing sign) - !f1 = resNrgFunc(tempMax,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) - !f2 = resNrgFunc(tempMin,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) + ! compute derivatives + dxTry_dWat = 0.5_rkind*(dtempMin_dWat + dtempMax_dWat) + dxTry_dTk = 0.5_rkind*(dtempMin_dTk + dtempMax_dTk) + ! xIce = (1._rkind - fracliquid(xTry,snowfrz_scale))*scalarCanopyWat + ! fTry = -scalarBulkVolHeatCapVeg*(xTry - scalarCanopyTemp) + LH_fus*(xIce - scalarCanopyIce)/canopyDepth + dfTry_dWat = -dCp_dWat*(xTry - scalarCanopyTemp) - scalarBulkVolHeatCapVeg*dxTry_dWat & + + LH_fus* ( 1._rkind - fracliquid(xTry,snowfrz_scale) & + - dFracLiq_dTk(xTry,snowfrz_scale)*scalarCanopyWat*dxTry_dWat - dCanopyIce_dWat )/canopyDepth + dfTry_dTk = -dCp_dTk*(xTry - scalarCanopyTemp) + scalarBulkVolHeatCapVeg*dxTry_dTk & + + LH_fus* ( -dFracLiq_dTk(xTry,snowfrz_scale)*scalarCanopyWat*dxTry_dTk - dCanopyIce_dTk )/canopyDepth + ! fDer = scalarBulkVolHeatCapVeg + scalarCanopyWat*dFracLiq_dTk(xTry,snowfrz_scale)*LH_fus/canopyDepth + dfDer_dWat = dCp_dWat + ( dFracLiq_dTk(xTry,snowfrz_scale) & + + scalarCanopyWat*d2FracLiq_dTk2(xTry,snowfrz_scale)*dxTry_dWat )*LH_fus/canopyDepth + dfDer_dTk = dCp_dTk + scalarCanopyWat*d2FracLiq_dTk2(xTry,snowfrz_scale)*dxTry_dTk*LH_fus/canopyDepth + ! ----------------------------------------------------------------------------------------------------------------------------------------------------- ! iterate do iter=1,maxiter + if(xTry <= tempMin .or. xTry >= tempMax)then ! bisect if out of range + xTry = 0.5_rkind*(tempMin + tempMax) ! new value + fBis = .true. + ! compute derivatives + dxTry_dWat = 0.5_rkind*(dtempMin_dWat + dtempMax_dWat) + dxTry_dTk = 0.5_rkind*(dtempMin_dTk + dtempMax_dTk) + else ! value in range; use the newton step + xInc = fTry/fDer + xTry = xTry + xInc + fBis = .false. + ! compute derivatives + dxTry_dWat = dxTry_dWat+ dfTry_dWat/fDer - fTry/(dfDer_dWat**2_i4b) + dxTry_dTk = dxTry_dTk + dfTry_dTk/fDer - fTry/(dfDer_dTk**2_i4b) + end if ! (switch between bi-section and newton) + + ! compute new function and derivatives + fTry = resNrgFunc(xTry,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) + fDer = resNrgDer(xTry,scalarBulkVolHeatCapVeg,snowfrz_scale) + dfTry_dWat = -dCp_dWat*(xTry - scalarCanopyTemp) - scalarBulkVolHeatCapVeg*dxTry_dWat & + + LH_fus* ( 1._rkind - fracliquid(xTry,snowfrz_scale) & + - dFracLiq_dTk(xTry,snowfrz_scale)*scalarCanopyWat*dxTry_dWat - dCanopyIce_dWat )/canopyDepth + dfTry_dTk = -dCp_dTk*(xTry - scalarCanopyTemp) + scalarBulkVolHeatCapVeg*dxTry_dTk & + + LH_fus* ( -dFracLiq_dTk(xTry,snowfrz_scale)*scalarCanopyWat*dxTry_dTk - dCanopyIce_dTk )/canopyDepth + dfDer_dWat = dCp_dWat + ( dFracLiq_dTk(xTry,snowfrz_scale) & + + scalarCanopyWat*d2FracLiq_dTk2(xTry,snowfrz_scale)*dxTry_dWat )*LH_fus/canopyDepth + dfDer_dTk = dCp_dTk + scalarCanopyWat*d2FracLiq_dTk2(xTry,snowfrz_scale)*dxTry_dTk*LH_fus/canopyDepth + + ! update limits and derivatives + if(fTry < 0._rkind)then + tempMax = min(xTry,tempMax) + if(xTrytempMin)then + dtempMin_dWat = dxTry_dWat + dtempMin_dTk = dxTry_dTk + endif + end if - ! bisect if out of range - if(xTry <= tempMin .or. xTry >= tempMax)then - xTry = 0.5_rkind*(tempMin + tempMax) ! new value - fBis = .true. - ! value in range; use the newton step - else - xInc = fTry/fDer - xTry = xTry + xInc - fBis = .false. - end if ! (switch between bi-section and newton) - - ! compute new function and derivative - fTry = resNrgFunc(xTry,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) - fDer = resNrgDer(xTry,scalarBulkVolHeatCapVeg,snowfrz_scale) - - ! update limits - if(fTry < 0._rkind)then - tempMax = min(xTry,tempMax) - else - tempMin = max(tempMin,xTry) - end if - - ! check the functions at the limits (should be of opposing sign) - !f1 = resNrgFunc(tempMax,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) - !f2 = resNrgFunc(tempMin,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) - - ! check convergence - if(abs(fTry) < resNrgToler) exit - - ! check non-convergence - if(iter==maxiter)then - ! (print out a 1-d x-section) - do iTry=1,maxiter - xTry = 1.0_rkind*real(iTry,kind(1._rkind))/real(maxiter,kind(1._rkind)) + 272.5_rkind - fTry = resNrgFunc(xTry,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) - write(*,'(a,1x,i4,1x,e20.10,1x,4(f20.10,1x))') 'iTry, fTry, xTry = ', iTry, fTry, xTry - end do - ! (return with error) - message=trim(message)//'unable to converge' - err=20; return - end if + ! check the functions at the limits (should be of opposing sign) + !f1 = resNrgFunc(tempMax,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) + !f2 = resNrgFunc(tempMin,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) + + ! check convergence + if(abs(fTry) < resNrgToler) exit + + ! check non-convergence + if(iter==maxiter)then + ! (print out a 1-d x-section) + do iTry=1,maxiter + xTry = 1.0_rkind*real(iTry,kind(1._rkind))/real(maxiter,kind(1._rkind)) + 272.5_rkind + fTry = resNrgFunc(xTry,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) + write(*,'(a,1x,i4,1x,e20.10,1x,4(f20.10,1x))') 'iTry, fTry, xTry = ', iTry, fTry, xTry + end do + ! (return with error) + message=trim(message)//'unable to converge' + err=20; return + end if end do ! iterating ! ----------------------------------------------------------------------------------------------------------------------------------------------------- - ! using a one iteration simplication, compute derivatives of tNew = xTry + fTry/fDer with respect to original Tk and Wat - xIce = (1._rkind - fracliquid(xTry,snowfrz_scale))*scalarCanopyWat - fTry = -bulkVolHeatCapVeg*(xTry - scalarCanopyTemp) + LH_fus*(xIce - scalarCanopyIceOld)/canopyDepth + nrgMeltFreeze - fDer = bulkVolHeatCapVeg + scalarCanopyWat*dFracLiq_dTk(xTry,snowfrz_scale)*LH_fus/canopyDepth - - ! heat capacity derivatives - dCp_dWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth - if(scalarCanopyTemp < Tfreeze)then - dCp_dTk = (-Cp_ice + Cp_water) * fLiq * scalarCanopyLiq/canopyDepth ! no derivative in air - else - dCp_dTk = 0._rkind - endif - ! canopy ice content after melt-freeze to the initial temp derivatives - dIceOld_dWat = 1._rkind - fracliquid(scalarCanopyTemp,snowfrz_scale) - dIceOld_dTk = -scalarCanopyWat*dFracLiq_dTk(scalarCanopyTemp,snowfrz_scale) - ! iteration variable derivatives - dfTry_dWat = -dCp_dWat*(xTry - scalarCanopyTemp) & - - LH_fus*(1._rkind - fracliquid(xTry,snowfrz_scale) - dIceOld_dWat)/canopyDepth & - + LH_fus*(dIceOld_dWat)/canopyDepth !maybe (dIceOld_dWat - 1) - dfTry_dTk = -dCp_dTk*(xTry - scalarCanopyTemp) + dCp_dTk & - - LH_fus*dIceOld_dTk/canopyDepth + LH_fus*dIceOld_dTk/canopyDepth - dfDer_dWat = dCp_dWat + dFracLiq_dTk(xTry,snowfrz_scale)*LH_fus/canopyDepth - dfDer_dTk = dCp_dTk - - dTkCanopyAdj_dCanWat = dfTry_dWat/fDer - fTry/(dfDer_dWat**2_i4b) - dTkCanopyAdj_dTkCanopy = dfTry_dTk/fDer - fTry/(dfDer_dTk**2_i4b) - -! update state variables + ! update state variables scalarCanopyTemp = xTry scalarCanopyIce = (1._rkind - fracliquid(xTry,snowfrz_scale))*scalarCanopyWat scalarCanopyLiq = scalarCanopyWat - scalarCanopyIce + ! update derivatives + dTkCanopyAdj_dCanWat = dxTry_dWat + dTkCanopyAdj_dTkCanopy = dxTry_dTk + ! update bulk heat capacity scalarBulkVolHeatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component Cp_water*scalarCanopyLiq/canopyDepth + & ! liquid water component Cp_ice*scalarCanopyIce/canopyDepth ! ice component - ! end association to variables in the data structure end associate @@ -280,36 +320,36 @@ subroutine tempAdjust(& ! ************************************************************************************************ ! internal function resNrgFunc: calculate the residual in energy (J m-3) ! ************************************************************************************************ - function resNrgFunc(xTemp,xTemp0,bulkVolHeatCapVeg,snowfrz_scale) + function resNrgFunc(xTemp,xTemp0,scalarBulkVolHeatCapVeg,snowfrz_scale) ! implicit none real(rkind),intent(in) :: xTemp ! temperature (K) real(rkind),intent(in) :: xTemp0 ! initial temperature (K) - real(rkind),intent(in) :: bulkVolHeatCapVeg ! volumetric heat capacity of veg (J m-3 K-1) + real(rkind),intent(in) :: scalarBulkVolHeatCapVeg ! volumetric heat capacity of veg (J m-3 K-1) real(rkind),intent(in) :: snowfrz_scale ! scaling factor in freezing curve (K-1) real(rkind) :: xIce ! canopy ice content (kg m-2) real(rkind) :: resNrgFunc ! residual in energy (J m-3) xIce = (1._rkind - fracliquid(xTemp,snowfrz_scale))*scalarCanopyWat - resNrgFunc = -bulkVolHeatCapVeg*(xTemp - xTemp0) + LH_fus*(xIce - scalarCanopyIceOld)/canopyDepth + nrgMeltFreeze + resNrgFunc = -scalarBulkVolHeatCapVeg*(xTemp - xTemp0) + LH_fus*(xIce - scalarCanopyIceOld)/canopyDepth + nrgMeltFreeze return end function resNrgFunc - ! ************************************************************************************************ ! internal function resNrgDer: calculate the derivative (J m-3 K-1) ! ************************************************************************************************ - function resNrgDer(xTemp,bulkVolHeatCapVeg,snowfrz_scale) + function resNrgDer(xTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) implicit none real(rkind),intent(in) :: xTemp ! temperature (K) - real(rkind),intent(in) :: bulkVolHeatCapVeg ! volumetric heat capacity of veg (J m-3 K-1) + real(rkind),intent(in) :: scalarBulkVolHeatCapVeg ! volumetric heat capacity of veg (J m-3 K-1) real(rkind),intent(in) :: snowfrz_scale ! scaling factor in freezing curve (K-1) real(rkind) :: dW_dT ! derivative in canopy ice content w.r.t. temperature (kg m-2 K-1) real(rkind) :: resNrgDer ! derivative (J m-3 K-1) dW_dT = -scalarCanopyWat*dFracLiq_dTk(xTemp,snowfrz_scale) - resNrgDer = bulkVolHeatCapVeg - dW_dT*LH_fus/canopyDepth + resNrgDer = scalarBulkVolHeatCapVeg - dW_dT*LH_fus/canopyDepth return end function resNrgDer + end subroutine tempAdjust diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index b5c6dea58..5eca81550 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -972,16 +972,16 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! check the mass balance fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial + write(*,'(a,1x,f20.10)') 'dt = ', dt + write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 + write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 + write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt + write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt + write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt + write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt + write(*,'(a,1x,f20.10)') 'liqError = ', liqError if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues - !write(*,'(a,1x,f20.10)') 'dt = ', dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - !write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 - !write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 - !write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt - !write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - !write(*,'(a,1x,f20.10)') 'liqError = ', liqError waterBalanceError = .true. return endif ! if there is a water balance error diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 5ff57be6e..9c08c2ed9 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -2138,12 +2138,12 @@ subroutine turbFluxes(& real(rkind),intent(out) :: dTurbFluxCanopy_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) real(rkind),intent(out) :: dTurbFluxGround_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! ----------------------------------------------------------------------------------------------------------------------------------------- ! local variables -- general - real(rkind) :: fpart1,fpart2 ! different parts of a function - real(rkind) :: dPart0,dpart1,dpart2 ! derivatives for different parts of a function + real(rkind) :: fPart1,fPart2 ! different parts of a function + real(rkind) :: dPart0,dPart1,dPart2 ! derivatives for different parts of a function ! local variables -- "constants" real(rkind) :: volHeatCapacityAir ! volumetric heat capacity of air (J m-3) real(rkind) :: latentHeatConstant ! latent heat constant (kg m-3 K-1) From 52221bb176f42418d14d533af4c4dfa3e9424a83 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 6 Jul 2023 15:03:32 +0900 Subject: [PATCH 0790/1472] derivative 0 don't divide --- build/source/engine/tempAdjust.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/source/engine/tempAdjust.f90 b/build/source/engine/tempAdjust.f90 index 23b007ba9..5915f69fa 100644 --- a/build/source/engine/tempAdjust.f90 +++ b/build/source/engine/tempAdjust.f90 @@ -168,7 +168,8 @@ subroutine tempAdjust(& f2 = resNrgFunc(x2,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) ! compute derivatives dx2_dWat = df1_dWat/fDer - f1/(dfDer_dWat**2_i4b) - dx2_dTk = 1._rkind + df1_dTk/fDer - f1/(dfDer_dTk**2_i4b) + dx2_dTk = 1._rkind + df1_dTk/fDer + if(dfDer_dTk.ne.0._rkind) dx2_dTk = dx2_dTk - f1/(dfDer_dTk**2_i4b) !if dCp_dTk = scalarCanopyWat = 0 will be 0 ! ensure that we bracket the root and recompute x2 and derivatives if not if(f1*f2 > 0._rkind)then From 00d2cf289a86d4eee8a23cff4f159b7ee539c8c3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 6 Jul 2023 17:13:56 +0900 Subject: [PATCH 0791/1472] dThermalC didn't have length (0:) --- build/source/dshare/get_ixname.f90 | 30 +- build/source/dshare/popMetadat.f90 | 534 ++++++++++----------- build/source/engine/computThermConduct.f90 | 8 +- build/source/engine/snowLiqFlx.f90 | 62 +-- build/source/engine/ssdNrgFlux.f90 | 8 +- 5 files changed, 313 insertions(+), 329 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index a7122612b..2f782df09 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -754,7 +754,7 @@ function get_ixderiv(varName) case('dThermalC_dTempAbove' ); get_ixderiv = iLookDERIV%dThermalC_dTempAbove ! derivative in the thermal conductivity w.r.t. energy state in the layer above case('dThermalC_dTempBelow' ); get_ixderiv = iLookDERIV%dThermalC_dTempBelow ! derivative in the thermal conductivity w.r.t. energy state in the layer above case('dThermalC_dWatAbove' ); get_ixderiv = iLookDERIV%dThermalC_dWatAbove ! derivative in the thermal conductivity w.r.t. water state in the layer above - case('dThermalC_dWatBelow' ); get_ixderiv = iLookDERIV%dThermalC_dWatBelow ! derivative in the thermal conductivity w.r.t. water state in the layer above ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below + case('dThermalC_dWatBelow' ); get_ixderiv = iLookDERIV%dThermalC_dWatBelow ! derivative in the thermal conductivity w.r.t. water state in the layer above case('dNrgFlux_dTempAbove' ); get_ixderiv = iLookDERIV%dNrgFlux_dTempAbove ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) case('dNrgFlux_dTempBelow' ); get_ixderiv = iLookDERIV%dNrgFlux_dTempBelow ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below @@ -870,23 +870,23 @@ function get_ixindex(varName) case('ixNrgCanair' ); get_ixINDEX = iLookINDEX%ixNrgCanair ! indices IN THE FULL VECTOR for energy states in canopy air space domain (-) case('ixNrgCanopy' ); get_ixINDEX = iLookINDEX%ixNrgCanopy ! indices IN THE FULL VECTOR for energy states in the canopy domain (-) case('ixHydCanopy' ); get_ixINDEX = iLookINDEX%ixHydCanopy ! indices IN THE FULL VECTOR for hydrology states in the canopy domain (-) - case('ixNrgLayer' ); get_ixINDEX = iLookINDEX%ixNrgLayer ! indices IN THE FULL VECTOR for energy states in the snow+soil domain (-) - case('ixHydLayer' ); get_ixINDEX = iLookINDEX%ixHydLayer ! indices IN THE FULL VECTOR for hydrology states in the snow+soil domain (-) - case('ixWatAquifer' ); get_ixINDEX = iLookINDEX%ixWatAquifer ! indices IN THE FULL VECTOR for storage of water in the aquifer (-) + case('ixNrgLayer' ); get_ixINDEX = iLookINDEX%ixNrgLayer ! indices IN THE FULL VECTOR for energy states in the snow+soil domain (-) + case('ixHydLayer' ); get_ixINDEX = iLookINDEX%ixHydLayer ! indices IN THE FULL VECTOR for hydrology states in the snow+soil domain (-) + case('ixWatAquifer' ); get_ixINDEX = iLookINDEX%ixWatAquifer ! indices IN THE FULL VECTOR for storage of water in the aquifer (-) ! vectors of indices for specific state types IN SPECIFIC SUB-DOMAINS - case('ixVolFracWat' ); get_ixINDEX = iLookINDEX%ixVolFracWat ! indices IN THE SNOW+SOIL VECTOR for hyd states (-) - case('ixMatricHead' ); get_ixINDEX = iLookINDEX%ixMatricHead ! indices IN THE SOIL VECTOR for hyd states (-) + case('ixVolFracWat' ); get_ixINDEX = iLookINDEX%ixVolFracWat ! indices IN THE SNOW+SOIL VECTOR for hyd states (-) + case('ixMatricHead' ); get_ixINDEX = iLookINDEX%ixMatricHead ! indices IN THE SOIL VECTOR for hyd states (-) ! indices within state vectors - case('ixAllState' ); get_ixINDEX = iLookINDEX%ixAllState ! list of indices for all model state variables (-) - case('ixSoilState' ); get_ixINDEX = iLookINDEX%ixSoilState ! list of indices for all soil layers (-) - case('ixLayerState' ); get_ixINDEX = iLookINDEX%ixLayerState ! list of indices for all model layers (-) - case('ixLayerActive' ); get_ixINDEX = iLookINDEX%ixLayerActive ! list of indices for all active model layers (-) + case('ixAllState' ); get_ixINDEX = iLookINDEX%ixAllState ! list of indices for all model state variables (-) + case('ixSoilState' ); get_ixINDEX = iLookINDEX%ixSoilState ! list of indices for all soil layers (-) + case('ixLayerState' ); get_ixINDEX = iLookINDEX%ixLayerState ! list of indices for all model layers (-) + case('ixLayerActive' ); get_ixINDEX = iLookINDEX%ixLayerActive ! list of indices for all active model layers (-) ! number of trials - case('numberFluxCalc' ); get_ixINDEX = iLookINDEX%numberFluxCalc ! number of flux calculations (-) - case('numberStateSplit' ); get_ixINDEX = iLookINDEX%numberStateSplit ! number of state splitting solutions (-) - case('numberDomainSplitNrg' ); get_ixINDEX = iLookINDEX%numberDomainSplitNrg ! number of domain splitting solutions for energy (-) - case('numberDomainSplitMass'); get_ixINDEX = iLookINDEX%numberDomainSplitMass ! number of domain splitting solutions for mass (-) - case('numberScalarSolutions'); get_ixINDEX = iLookINDEX%numberScalarSolutions ! number of scalar solutions (-) + case('numberFluxCalc' ); get_ixINDEX = iLookINDEX%numberFluxCalc ! number of flux calculations (-) + case('numberStateSplit' ); get_ixINDEX = iLookINDEX%numberStateSplit ! number of state splitting solutions (-) + case('numberDomainSplitNrg' ); get_ixINDEX = iLookINDEX%numberDomainSplitNrg ! number of domain splitting solutions for energy (-) + case('numberDomainSplitMass'); get_ixINDEX = iLookINDEX%numberDomainSplitMass ! number of domain splitting solutions for mass (-) + case('numberScalarSolutions'); get_ixINDEX = iLookINDEX%numberScalarSolutions ! number of scalar solutions (-) ! default case default get_ixindex = integerMissing diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 7d346758b..b14a815f1 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -17,46 +17,46 @@ module popMetadat_module subroutine popMetadat(err,message) ! data structures - USE data_types, only: var_info ! data type for metadata structure - USE globalData, only: time_meta ! data structure for time metadata - USE globalData, only: forc_meta ! data structure for forcing metadata - USE globalData, only: type_meta ! data structure for categorical metadata - USE globalData, only: id_meta ! data structure for hru and gru ID metadata - USE globalData, only: attr_meta ! data structure for attribute metadata - USE globalData, only: mpar_meta ! data structure for local parameter metadata - USE globalData, only: bpar_meta ! data structure for basin parameter metadata - USE globalData, only: bvar_meta ! data structure for basin model variable metadata - USE globalData, only: indx_meta ! data structure for index metadata - USE globalData, only: prog_meta ! data structure for local prognostic (state) variables - USE globalData, only: diag_meta ! data structure for local diagnostic variables - USE globalData, only: flux_meta ! data structure for local flux variables - USE globalData, only: deriv_meta ! data structure for local flux derivatives - USE globalData, only: lookup_meta ! data structure for lookup tables + USE data_types, only: var_info ! data type for metadata structure + USE globalData, only: time_meta ! data structure for time metadata + USE globalData, only: forc_meta ! data structure for forcing metadata + USE globalData, only: type_meta ! data structure for categorical metadata + USE globalData, only: id_meta ! data structure for hru and gru ID metadata + USE globalData, only: attr_meta ! data structure for attribute metadata + USE globalData, only: mpar_meta ! data structure for local parameter metadata + USE globalData, only: bpar_meta ! data structure for basin parameter metadata + USE globalData, only: bvar_meta ! data structure for basin model variable metadata + USE globalData, only: indx_meta ! data structure for index metadata + USE globalData, only: prog_meta ! data structure for local prognostic (state) variables + USE globalData, only: diag_meta ! data structure for local diagnostic variables + USE globalData, only: flux_meta ! data structure for local flux variables + USE globalData, only: deriv_meta ! data structure for local flux derivatives + USE globalData, only: lookup_meta ! data structure for lookup tables ! structures of named variables - USE var_lookup, only: iLookTIME ! named variables for time data structure - USE var_lookup, only: iLookFORCE ! named variables for forcing data structure - USE var_lookup, only: iLookTYPE ! named variables for categorical attribute data structure - USE var_lookup, only: iLookID ! named variables for hru and gru ID metadata - USE var_lookup, only: iLookATTR ! named variables for real valued attribute data structure - USE var_lookup, only: iLookPARAM ! named variables for local parameter data structure - USE var_lookup, only: iLookBPAR ! named variables for basin parameter data structure - USE var_lookup, only: iLookBVAR ! named variables for basin model variable data structure - USE var_lookup, only: iLookINDEX ! named variables for index variable data structure - USE var_lookup, only: iLookPROG ! named variables for local state variables - USE var_lookup, only: iLookDIAG ! named variables for local diagnostic variables - USE var_lookup, only: iLookFLUX ! named variables for local flux variables - USE var_lookup, only: iLookDERIV ! named variables for local flux derivatives - USE var_lookup, only: iLookLOOKUP ! named variables for lookup tables - USE var_lookup, only: maxvarFreq ! number of output frequencies - USE var_lookup, only: maxvarStat ! number of statistics - USE get_ixName_module,only:get_ixVarType ! to turn vartype strings to integers + USE var_lookup, only: iLookTIME ! named variables for time data structure + USE var_lookup, only: iLookFORCE ! named variables for forcing data structure + USE var_lookup, only: iLookTYPE ! named variables for categorical attribute data structure + USE var_lookup, only: iLookID ! named variables for hru and gru ID metadata + USE var_lookup, only: iLookATTR ! named variables for real valued attribute data structure + USE var_lookup, only: iLookPARAM ! named variables for local parameter data structure + USE var_lookup, only: iLookBPAR ! named variables for basin parameter data structure + USE var_lookup, only: iLookBVAR ! named variables for basin model variable data structure + USE var_lookup, only: iLookINDEX ! named variables for index variable data structure + USE var_lookup, only: iLookPROG ! named variables for local state variables + USE var_lookup, only: iLookDIAG ! named variables for local diagnostic variables + USE var_lookup, only: iLookFLUX ! named variables for local flux variables + USE var_lookup, only: iLookDERIV ! named variables for local flux derivatives + USE var_lookup, only: iLookLOOKUP ! named variables for lookup tables + USE var_lookup, only: maxvarFreq ! number of output frequencies + USE var_lookup, only: maxvarStat ! number of statistics + USE get_ixName_module,only:get_ixVarType ! to turn vartype strings to integers implicit none ! dummy variables - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! internals - character(256) :: cmessage ! error message - integer,dimension(maxVarFreq) :: iMissVec ! vector of missing integers + character(256) :: cmessage ! error message + integer,dimension(maxVarFreq) :: iMissVec ! vector of missing integers ! initialize error control err=0; message='popMetadat/' @@ -65,240 +65,233 @@ subroutine popMetadat(err,message) ! ----- ! * model time structures... ! -------------------------- - time_meta(iLookTIME%iyyy) = var_info('iyyy' , 'year' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - time_meta(iLookTIME%im) = var_info('im' , 'month' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - time_meta(iLookTIME%id) = var_info('id' , 'day' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - time_meta(iLookTIME%ih) = var_info('ih' , 'hour' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - time_meta(iLookTIME%imin) = var_info('imin' , 'minute' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - time_meta(iLookTIME%ih_tz) = var_info('ih_tz' , 'hour for time zone offset' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - time_meta(iLookTIME%imin_tz) = var_info('imin_tz', 'minute for time zone offset', '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - + time_meta(iLookTIME%iyyy) = var_info('iyyy' , 'year' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + time_meta(iLookTIME%im) = var_info('im' , 'month' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + time_meta(iLookTIME%id) = var_info('id' , 'day' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + time_meta(iLookTIME%ih) = var_info('ih' , 'hour' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + time_meta(iLookTIME%imin) = var_info('imin' , 'minute' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + time_meta(iLookTIME%ih_tz) = var_info('ih_tz' , 'hour for time zone offset' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + time_meta(iLookTIME%imin_tz) = var_info('imin_tz' , 'minute for time zone offset' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! ----- ! * model forcing data... ! ----------------------- - forc_meta(iLookFORCE%time) = var_info('time' , 'time since time reference' , 'seconds since 1990-1-1 0:0:0.0 -0:00', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - forc_meta(iLookFORCE%pptrate) = var_info('pptrate' , 'precipitation rate' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - forc_meta(iLookFORCE%SWRadAtm) = var_info('SWRadAtm', 'downward shortwave radiation at the upper boundary', 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - forc_meta(iLookFORCE%LWRadAtm) = var_info('LWRadAtm', 'downward longwave radiation at the upper boundary' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - forc_meta(iLookFORCE%airtemp) = var_info('airtemp' , 'air temperature at the measurement height' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - forc_meta(iLookFORCE%windspd) = var_info('windspd' , 'wind speed at the measurement height' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - forc_meta(iLookFORCE%airpres) = var_info('airpres' , 'air pressure at the the measurement height' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - forc_meta(iLookFORCE%spechum) = var_info('spechum' , 'specific humidity at the measurement height' , 'g g-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - + forc_meta(iLookFORCE%time) = var_info('time' , 'time since time reference' , 'seconds since 1990-1-1 0:0:0.0 -0:00', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + forc_meta(iLookFORCE%pptrate) = var_info('pptrate' , 'precipitation rate' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + forc_meta(iLookFORCE%SWRadAtm) = var_info('SWRadAtm' , 'downward shortwave radiation at the upper boundary' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + forc_meta(iLookFORCE%LWRadAtm) = var_info('LWRadAtm' , 'downward longwave radiation at the upper boundary' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + forc_meta(iLookFORCE%airtemp) = var_info('airtemp' , 'air temperature at the measurement height' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + forc_meta(iLookFORCE%windspd) = var_info('windspd' , 'wind speed at the measurement height' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + forc_meta(iLookFORCE%airpres) = var_info('airpres' , 'air pressure at the the measurement height' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + forc_meta(iLookFORCE%spechum) = var_info('spechum' , 'specific humidity at the measurement height' , 'g g-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! ----- ! * categorical data... ! --------------------- - type_meta(iLookTYPE%vegTypeIndex) = var_info('vegTypeIndex' , 'index defining vegetation type' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - type_meta(iLookTYPE%soilTypeIndex) = var_info('soilTypeIndex' , 'index defining soil type' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - type_meta(iLookTYPE%slopeTypeIndex) = var_info('slopeTypeIndex', 'index defining slope' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - type_meta(iLookTYPE%downHRUindex) = var_info('downHRUindex' , 'index of downslope HRU (0 = basin outlet)' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - + type_meta(iLookTYPE%vegTypeIndex) = var_info('vegTypeIndex' , 'index defining vegetation type' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + type_meta(iLookTYPE%soilTypeIndex) = var_info('soilTypeIndex' , 'index defining soil type' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + type_meta(iLookTYPE%slopeTypeIndex) = var_info('slopeTypeIndex' , 'index defining slope' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + type_meta(iLookTYPE%downHRUindex) = var_info('downHRUindex' , 'index of downslope HRU (0 = basin outlet)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! ----- ! * hru and gru ID data... ! --------------------- - id_meta(iLookID%hruId) = var_info('hruId' , 'ID defining the hydrologic response unit' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - + id_meta(iLookID%hruId) = var_info('hruId' , 'ID defining the hydrologic response unit' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! ----- ! * site characteristics... ! ------------------------- - attr_meta(iLookATTR%latitude) = var_info('latitude' , 'latitude' , 'degrees north', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - attr_meta(iLookATTR%longitude) = var_info('longitude' , 'longitude' , 'degrees east' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - attr_meta(iLookATTR%elevation) = var_info('elevation' , 'elevation' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - attr_meta(iLookATTR%tan_slope) = var_info('tan_slope' , 'tan water table slope (tan local ground surface slope)', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - attr_meta(iLookATTR%contourLength) = var_info('contourLength' , 'length of contour at downslope edge of HRU' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - attr_meta(iLookATTR%HRUarea) = var_info('HRUarea' , 'area of each HRU' , 'm2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - attr_meta(iLookATTR%mHeight) = var_info('mHeight' , 'measurement height above bare ground' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - attr_meta(iLookATTR%aspect) = var_info('aspect' , 'mean azimuth of HRU in degrees East of North (0)' , 'degrees' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - + attr_meta(iLookATTR%latitude) = var_info('latitude' , 'latitude' , 'degrees north' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + attr_meta(iLookATTR%longitude) = var_info('longitude' , 'longitude' , 'degrees east' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + attr_meta(iLookATTR%elevation) = var_info('elevation' , 'elevation' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + attr_meta(iLookATTR%tan_slope) = var_info('tan_slope' , 'tan water table slope (tan local ground surface slope)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + attr_meta(iLookATTR%contourLength) = var_info('contourLength' , 'length of contour at downslope edge of HRU' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + attr_meta(iLookATTR%HRUarea) = var_info('HRUarea' , 'area of each HRU' , 'm2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + attr_meta(iLookATTR%mHeight) = var_info('mHeight' , 'measurement height above bare ground' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + attr_meta(iLookATTR%aspect) = var_info('aspect' , 'mean azimuth of HRU in degrees East of North (0)' , 'degrees' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! ----- ! * local parameter data... ! ------------------------- ! boundary conditions - mpar_meta(iLookPARAM%upperBoundHead) = var_info('upperBoundHead' , 'matric head at the upper boundary' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%lowerBoundHead) = var_info('lowerBoundHead' , 'matric head at the lower boundary' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%upperBoundTheta) = var_info('upperBoundTheta' , 'volumetric liquid water content at the upper boundary' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%lowerBoundTheta) = var_info('lowerBoundTheta' , 'volumetric liquid water content at the lower boundary' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%upperBoundTemp) = var_info('upperBoundTemp' , 'temperature of the upper boundary' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%lowerBoundTemp) = var_info('lowerBoundTemp' , 'temperature of the lower boundary' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%upperBoundHead) = var_info('upperBoundHead' , 'matric head at the upper boundary' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%lowerBoundHead) = var_info('lowerBoundHead' , 'matric head at the lower boundary' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%upperBoundTheta) = var_info('upperBoundTheta' , 'volumetric liquid water content at the upper boundary' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%lowerBoundTheta) = var_info('lowerBoundTheta' , 'volumetric liquid water content at the lower boundary' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%upperBoundTemp) = var_info('upperBoundTemp' , 'temperature of the upper boundary' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%lowerBoundTemp) = var_info('lowerBoundTemp' , 'temperature of the lower boundary' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! precipitation partitioning - mpar_meta(iLookPARAM%tempCritRain) = var_info('tempCritRain' , 'critical temperature where precipitation is rain' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%tempRangeTimestep) = var_info('tempRangeTimestep' , 'temperature range over the time step' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%frozenPrecipMultip) = var_info('frozenPrecipMultip' , 'frozen precipitation multiplier' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%tempCritRain) = var_info('tempCritRain' , 'critical temperature where precipitation is rain' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%tempRangeTimestep) = var_info('tempRangeTimestep' , 'temperature range over the time step' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%frozenPrecipMultip) = var_info('frozenPrecipMultip' , 'frozen precipitation multiplier' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! snow properties - mpar_meta(iLookPARAM%snowfrz_scale) = var_info('snowfrz_scale' , 'scaling parameter for the freezing curve for snow' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%fixedThermalCond_snow) = var_info('fixedThermalCond_snow' , 'temporally constant thermal conductivity for snow' , 'W m-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%snowfrz_scale) = var_info('snowfrz_scale' , 'scaling parameter for the freezing curve for snow' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%fixedThermalCond_snow) = var_info('fixedThermalCond_snow' , 'temporally constant thermal conductivity for snow' , 'W m-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! snow albedo - mpar_meta(iLookPARAM%albedoMax) = var_info('albedoMax' , 'maximum snow albedo (single spectral band)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%albedoMinWinter) = var_info('albedoMinWinter' , 'minimum snow albedo during winter (single spectral band)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%albedoMinSpring) = var_info('albedoMinSpring' , 'minimum snow albedo during spring (single spectral band)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%albedoMaxVisible) = var_info('albedoMaxVisible' , 'maximum snow albedo in the visible part of the spectrum' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%albedoMinVisible) = var_info('albedoMinVisible' , 'minimum snow albedo in the visible part of the spectrum' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%albedoMaxNearIR) = var_info('albedoMaxNearIR' , 'maximum snow albedo in the near infra-red part of the spectrum' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%albedoMinNearIR) = var_info('albedoMinNearIR' , 'minimum snow albedo in the near infra-red part of the spectrum' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%albedoDecayRate) = var_info('albedoDecayRate' , 'albedo decay rate' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%albedoSootLoad) = var_info('albedoSootLoad' , 'soot load factor' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%albedoRefresh) = var_info('albedoRefresh' , 'critical mass necessary for albedo refreshment' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%albedoMax) = var_info('albedoMax' , 'maximum snow albedo (single spectral band)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%albedoMinWinter) = var_info('albedoMinWinter' , 'minimum snow albedo during winter (single spectral band)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%albedoMinSpring) = var_info('albedoMinSpring' , 'minimum snow albedo during spring (single spectral band)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%albedoMaxVisible) = var_info('albedoMaxVisible' , 'maximum snow albedo in the visible part of the spectrum' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%albedoMinVisible) = var_info('albedoMinVisible' , 'minimum snow albedo in the visible part of the spectrum' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%albedoMaxNearIR) = var_info('albedoMaxNearIR' , 'maximum snow albedo in the near infra-red part of the spectrum' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%albedoMinNearIR) = var_info('albedoMinNearIR' , 'minimum snow albedo in the near infra-red part of the spectrum' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%albedoDecayRate) = var_info('albedoDecayRate' , 'albedo decay rate' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%albedoSootLoad) = var_info('albedoSootLoad' , 'soot load factor' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%albedoRefresh) = var_info('albedoRefresh' , 'critical mass necessary for albedo refreshment' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! radiation transfer - mpar_meta(iLookPARAM%radExt_snow) = var_info('radExt_snow' , 'extinction coefficient for radiation penetration into snowpack' , 'm-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%directScale) = var_info('directScale' , 'scaling factor for fractional driect radiaion parameterization' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%Frad_direct) = var_info('Frad_direct' , 'fraction direct solar radiation' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%Frad_vis) = var_info('Frad_vis' , 'fraction radiation in visible part of spectrum' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%radExt_snow) = var_info('radExt_snow' , 'extinction coefficient for radiation penetration into snowpack' , 'm-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%directScale) = var_info('directScale' , 'scaling factor for fractional driect radiaion parameterization' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%Frad_direct) = var_info('Frad_direct' , 'fraction direct solar radiation' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%Frad_vis) = var_info('Frad_vis' , 'fraction radiation in visible part of spectrum' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! new snow density - mpar_meta(iLookPARAM%newSnowDenMin) = var_info('newSnowDenMin' , 'minimum new snow density' , 'kg m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%newSnowDenMult) = var_info('newSnowDenMult' , 'multiplier for new snow density' , 'kg m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%newSnowDenScal) = var_info('newSnowDenScal' , 'scaling factor for new snow density' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%constSnowDen) = var_info('constSnowDen' , 'Constant new snow density' , 'kg m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%newSnowDenAdd) = var_info('newSnowDenAdd' , 'Pahaut 1976, additive factor for new snow density' , 'kg m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%newSnowDenMultTemp) = var_info('newSnowDenMultTemp' , 'Pahaut 1976, multiplier for new snow density for air temperature' , 'kg m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%newSnowDenMultWind) = var_info('newSnowDenMultWind' , 'Pahaut 1976, multiplier for new snow density for wind speed' , 'kg m-7/2 s-1/2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%newSnowDenMultAnd) = var_info('newSnowDenMultAnd' , 'Anderson 1976, multiplier for new snow density (Anderson func)' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%newSnowDenBase) = var_info('newSnowDenBase' , 'Anderson 1976, base value that is rasied to the (3/2) power' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%newSnowDenMin) = var_info('newSnowDenMin' , 'minimum new snow density' , 'kg m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%newSnowDenMult) = var_info('newSnowDenMult' , 'multiplier for new snow density' , 'kg m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%newSnowDenScal) = var_info('newSnowDenScal' , 'scaling factor for new snow density' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%constSnowDen) = var_info('constSnowDen' , 'Constant new snow density' , 'kg m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%newSnowDenAdd) = var_info('newSnowDenAdd' , 'Pahaut 1976, additive factor for new snow density' , 'kg m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%newSnowDenMultTemp) = var_info('newSnowDenMultTemp' , 'Pahaut 1976, multiplier for new snow density for air temperature' , 'kg m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%newSnowDenMultWind) = var_info('newSnowDenMultWind' , 'Pahaut 1976, multiplier for new snow density for wind speed' , 'kg m-7/2 s-1/2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%newSnowDenMultAnd) = var_info('newSnowDenMultAnd' , 'Anderson 1976, multiplier for new snow density (Anderson func)' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%newSnowDenBase) = var_info('newSnowDenBase' , 'Anderson 1976, base value that is rasied to the (3/2) power' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! snow compaction - mpar_meta(iLookPARAM%densScalGrowth) = var_info('densScalGrowth' , 'density scaling factor for grain growth' , 'kg-1 m3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%tempScalGrowth) = var_info('tempScalGrowth' , 'temperature scaling factor for grain growth' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%grainGrowthRate) = var_info('grainGrowthRate' , 'rate of grain growth' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%densScalOvrbdn) = var_info('densScalOvrbdn' , 'density scaling factor for overburden pressure' , 'kg-1 m3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%tempScalOvrbdn) = var_info('tempScalOvrbdn' , 'temperature scaling factor for overburden pressure' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%baseViscosity ) = var_info('baseViscosity ' , 'viscosity coefficient at T=T_frz and snow density=0' , 'kg s m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%densScalGrowth) = var_info('densScalGrowth' , 'density scaling factor for grain growth' , 'kg-1 m3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%tempScalGrowth) = var_info('tempScalGrowth' , 'temperature scaling factor for grain growth' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%grainGrowthRate) = var_info('grainGrowthRate' , 'rate of grain growth' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%densScalOvrbdn) = var_info('densScalOvrbdn' , 'density scaling factor for overburden pressure' , 'kg-1 m3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%tempScalOvrbdn) = var_info('tempScalOvrbdn' , 'temperature scaling factor for overburden pressure' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%baseViscosity ) = var_info('baseViscosity ' , 'viscosity coefficient at T=T_frz and snow density=0' , 'kg s m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! water flow through snow - mpar_meta(iLookPARAM%Fcapil) = var_info('Fcapil' , 'capillary retention (fraction of total pore volume)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%k_snow) = var_info('k_snow' , 'hydraulic conductivity of snow' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%mw_exp) = var_info('mw_exp' , 'exponent for meltwater flow' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%Fcapil) = var_info('Fcapil' , 'capillary retention (fraction of total pore volume)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%k_snow) = var_info('k_snow' , 'hydraulic conductivity of snow' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%mw_exp) = var_info('mw_exp' , 'exponent for meltwater flow' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! turbulent heat fluxes - mpar_meta(iLookPARAM%z0Snow) = var_info('z0Snow' , 'roughness length of snow' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%z0Soil) = var_info('z0Soil' , 'roughness length of bare soil below the canopy' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%z0Canopy) = var_info('z0Canopy' , 'roughness length of the canopy' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zpdFraction) = var_info('zpdFraction' , 'zero plane displacement / canopy height' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%critRichNumber) = var_info('critRichNumber' , 'critical value for the bulk Richardson number' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%Louis79_bparam) = var_info('Louis79_bparam' , 'parameter in Louis (1979) stability function' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%Louis79_cStar) = var_info('Louis79_cStar' , 'parameter in Louis (1979) stability function' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%Mahrt87_eScale) = var_info('Mahrt87_eScale' , 'exponential scaling factor in the Mahrt (1987) stability function', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%leafExchangeCoeff) = var_info('leafExchangeCoeff' , 'turbulent exchange coeff between canopy surface and canopy air' , 'm s-(1/2)' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%windReductionParam) = var_info('windReductionParam' , 'canopy wind reduction parameter' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%z0Snow) = var_info('z0Snow' , 'roughness length of snow' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%z0Soil) = var_info('z0Soil' , 'roughness length of bare soil below the canopy' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%z0Canopy) = var_info('z0Canopy' , 'roughness length of the canopy' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zpdFraction) = var_info('zpdFraction' , 'zero plane displacement / canopy height' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%critRichNumber) = var_info('critRichNumber' , 'critical value for the bulk Richardson number' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%Louis79_bparam) = var_info('Louis79_bparam' , 'parameter in Louis (1979) stability function' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%Louis79_cStar) = var_info('Louis79_cStar' , 'parameter in Louis (1979) stability function' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%Mahrt87_eScale) = var_info('Mahrt87_eScale' , 'exponential scaling factor in the Mahrt (1987) stability function', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%leafExchangeCoeff) = var_info('leafExchangeCoeff' , 'turbulent exchange coeff between canopy surface and canopy air' , 'm s-(1/2)' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%windReductionParam) = var_info('windReductionParam' , 'canopy wind reduction parameter' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! stomatal conductance - mpar_meta(iLookPARAM%Kc25) = var_info('Kc25' , 'Michaelis-Menten constant for CO2 at 25 degrees C' , 'umol mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%Ko25) = var_info('Ko25' , 'Michaelis-Menten constant for O2 at 25 degrees C' , 'mol mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%Kc_qFac) = var_info('Kc_qFac' , 'factor in the q10 function defining temperature controls on Kc' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%Ko_qFac) = var_info('Ko_qFac' , 'factor in the q10 function defining temperature controls on Ko' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%kc_Ha) = var_info('kc_Ha' , 'activation energy for the Michaelis-Menten constant for CO2' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%ko_Ha) = var_info('ko_Ha' , 'activation energy for the Michaelis-Menten constant for O2' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%vcmax25_canopyTop) = var_info('vcmax25_canopyTop' , 'potential carboxylation rate at 25 degrees C at the canopy top' , 'umol co2 m-2 s-1', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%vcmax_qFac) = var_info('vcmax_qFac' , 'factor in the q10 function defining temperature controls on vcmax', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%vcmax_Ha) = var_info('vcmax_Ha' , 'activation energy in the vcmax function' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%vcmax_Hd) = var_info('vcmax_Hd' , 'deactivation energy in the vcmax function' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%vcmax_Sv) = var_info('vcmax_Sv' , 'entropy term in the vcmax function' , 'J mol-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%vcmax_Kn) = var_info('vcmax_Kn' , 'foliage nitrogen decay coefficient' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%jmax25_scale) = var_info('jmax25_scale' , 'scaling factor to relate jmax25 to vcmax25' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%jmax_Ha) = var_info('jmax_Ha' , 'activation energy in the jmax function' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%jmax_Hd) = var_info('jmax_Hd' , 'deactivation energy in the jmax function' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%jmax_Sv) = var_info('jmax_Sv' , 'entropy term in the jmax function' , 'J mol-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%fractionJ) = var_info('fractionJ' , 'fraction of light lost by other than the chloroplast lamellae' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%quantamYield) = var_info('quantamYield' , 'quantam yield' , 'mol e mol-1 q' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%vpScaleFactor) = var_info('vpScaleFactor' , 'vapor pressure scaling factor in stomatal conductance function' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%cond2photo_slope) = var_info('cond2photo_slope' , 'slope of conductance-photosynthesis relationship' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%minStomatalConductance)= var_info('minStomatalConductance', 'minimum stomatal conductance' , 'umol H2O m-2 s-1', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%Kc25) = var_info('Kc25' , 'Michaelis-Menten constant for CO2 at 25 degrees C' , 'umol mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%Ko25) = var_info('Ko25' , 'Michaelis-Menten constant for O2 at 25 degrees C' , 'mol mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%Kc_qFac) = var_info('Kc_qFac' , 'factor in the q10 function defining temperature controls on Kc' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%Ko_qFac) = var_info('Ko_qFac' , 'factor in the q10 function defining temperature controls on Ko' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%kc_Ha) = var_info('kc_Ha' , 'activation energy for the Michaelis-Menten constant for CO2' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%ko_Ha) = var_info('ko_Ha' , 'activation energy for the Michaelis-Menten constant for O2' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%vcmax25_canopyTop) = var_info('vcmax25_canopyTop' , 'potential carboxylation rate at 25 degrees C at the canopy top' , 'umol co2 m-2 s-1', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%vcmax_qFac) = var_info('vcmax_qFac' , 'factor in the q10 function defining temperature controls on vcmax', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%vcmax_Ha) = var_info('vcmax_Ha' , 'activation energy in the vcmax function' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%vcmax_Hd) = var_info('vcmax_Hd' , 'deactivation energy in the vcmax function' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%vcmax_Sv) = var_info('vcmax_Sv' , 'entropy term in the vcmax function' , 'J mol-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%vcmax_Kn) = var_info('vcmax_Kn' , 'foliage nitrogen decay coefficient' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%jmax25_scale) = var_info('jmax25_scale' , 'scaling factor to relate jmax25 to vcmax25' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%jmax_Ha) = var_info('jmax_Ha' , 'activation energy in the jmax function' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%jmax_Hd) = var_info('jmax_Hd' , 'deactivation energy in the jmax function' , 'J mol-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%jmax_Sv) = var_info('jmax_Sv' , 'entropy term in the jmax function' , 'J mol-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%fractionJ) = var_info('fractionJ' , 'fraction of light lost by other than the chloroplast lamellae' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%quantamYield) = var_info('quantamYield' , 'quantam yield' , 'mol e mol-1 q' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%vpScaleFactor) = var_info('vpScaleFactor' , 'vapor pressure scaling factor in stomatal conductance function' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%cond2photo_slope) = var_info('cond2photo_slope' , 'slope of conductance-photosynthesis relationship' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%minStomatalConductance) = var_info('minStomatalConductance' , 'minimum stomatal conductance' , 'umol H2O m-2 s-1', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! vegetation properties - mpar_meta(iLookPARAM%winterSAI) = var_info('winterSAI' , 'stem area index prior to the start of the growing season' , 'm2 m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%summerLAI) = var_info('summerLAI' , 'maximum leaf area index at the peak of the growing season' , 'm2 m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%rootScaleFactor1) = var_info('rootScaleFactor1' , '1st scaling factor (a) in Y = 1 - 0.5*( exp(-aZ) + exp(-bZ) )' , 'm-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%rootScaleFactor2) = var_info('rootScaleFactor2' , '2nd scaling factor (b) in Y = 1 - 0.5*( exp(-aZ) + exp(-bZ) )' , 'm-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%rootingDepth) = var_info('rootingDepth' , 'rooting depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%rootDistExp) = var_info('rootDistExp' , 'exponent for the vertical distribution of root density' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%plantWiltPsi) = var_info('plantWiltPsi' , 'matric head at wilting point' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%soilStressParam) = var_info('soilStressParam' , 'parameter in the exponential soil stress function' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%critSoilWilting) = var_info('critSoilWilting' , 'critical vol. liq. water content when plants are wilting' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%critSoilTranspire) = var_info('critSoilTranspire' , 'critical vol. liq. water content when transpiration is limited' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%critAquiferTranspire) = var_info('critAquiferTranspire' , 'critical aquifer storage value when transpiration is limited' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%minStomatalResistance) = var_info('minStomatalResistance' , 'minimum stomatal resistance' , 's m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%leafDimension) = var_info('leafDimension' , 'characteristic leaf dimension' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%heightCanopyTop) = var_info('heightCanopyTop' , 'height of top of the vegetation canopy above ground surface' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%heightCanopyBottom) = var_info('heightCanopyBottom' , 'height of bottom of the vegetation canopy above ground surface' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%specificHeatVeg) = var_info('specificHeatVeg' , 'specific heat of vegetation' , 'J kg-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%maxMassVegetation) = var_info('maxMassVegetation' , 'maximum mass of vegetation (full foliage)' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%throughfallScaleSnow) = var_info('throughfallScaleSnow' , 'scaling factor for throughfall (snow)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%throughfallScaleRain) = var_info('throughfallScaleRain' , 'scaling factor for throughfall (rain)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%refInterceptCapSnow) = var_info('refInterceptCapSnow' , 'reference canopy interception capacity per unit leaf area (snow)' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%refInterceptCapRain) = var_info('refInterceptCapRain' , 'canopy interception capacity per unit leaf area (rain)' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%snowUnloadingCoeff) = var_info('snowUnloadingCoeff' , 'time constant for unloading of snow from the forest canopy' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%canopyDrainageCoeff) = var_info('canopyDrainageCoeff' , 'time constant for drainage of liquid water from the forest canopy', 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%ratioDrip2Unloading) = var_info('ratioDrip2Unloading' , 'ratio of canopy drip to unloading of snow from the forest canopy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%canopyWettingFactor) = var_info('canopyWettingFactor' , 'maximum wetted fraction of the canopy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%canopyWettingExp) = var_info('canopyWettingExp' , 'exponent in canopy wetting function' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%minTempUnloading) = var_info('minTempUnloading' , 'min temp for unloading in windySnow' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%rateTempUnloading) = var_info('rateTempUnloading' , 'how quickly to unload due to temperature' , 'K s' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%minWindUnloading) = var_info('minWindUnloading' , 'min wind speed for unloading in windySnow' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%rateWindUnloading) = var_info('rateWindUnloading' , 'how quickly to unload due to wind' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%winterSAI) = var_info('winterSAI' , 'stem area index prior to the start of the growing season' , 'm2 m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%summerLAI) = var_info('summerLAI' , 'maximum leaf area index at the peak of the growing season' , 'm2 m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%rootScaleFactor1) = var_info('rootScaleFactor1' , '1st scaling factor (a) in Y = 1 - 0.5*( exp(-aZ) + exp(-bZ) )' , 'm-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%rootScaleFactor2) = var_info('rootScaleFactor2' , '2nd scaling factor (b) in Y = 1 - 0.5*( exp(-aZ) + exp(-bZ) )' , 'm-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%rootingDepth) = var_info('rootingDepth' , 'rooting depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%rootDistExp) = var_info('rootDistExp' , 'exponent for the vertical distribution of root density' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%plantWiltPsi) = var_info('plantWiltPsi' , 'matric head at wilting point' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%soilStressParam) = var_info('soilStressParam' , 'parameter in the exponential soil stress function' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%critSoilWilting) = var_info('critSoilWilting' , 'critical vol. liq. water content when plants are wilting' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%critSoilTranspire) = var_info('critSoilTranspire' , 'critical vol. liq. water content when transpiration is limited' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%critAquiferTranspire) = var_info('critAquiferTranspire' , 'critical aquifer storage value when transpiration is limited' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%minStomatalResistance) = var_info('minStomatalResistance' , 'minimum stomatal resistance' , 's m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%leafDimension) = var_info('leafDimension' , 'characteristic leaf dimension' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%heightCanopyTop) = var_info('heightCanopyTop' , 'height of top of the vegetation canopy above ground surface' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%heightCanopyBottom) = var_info('heightCanopyBottom' , 'height of bottom of the vegetation canopy above ground surface' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%specificHeatVeg) = var_info('specificHeatVeg' , 'specific heat of vegetation' , 'J kg-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%maxMassVegetation) = var_info('maxMassVegetation' , 'maximum mass of vegetation (full foliage)' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%throughfallScaleSnow) = var_info('throughfallScaleSnow' , 'scaling factor for throughfall (snow)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%throughfallScaleRain) = var_info('throughfallScaleRain' , 'scaling factor for throughfall (rain)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%refInterceptCapSnow) = var_info('refInterceptCapSnow' , 'reference canopy interception capacity per unit leaf area (snow)' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%refInterceptCapRain) = var_info('refInterceptCapRain' , 'canopy interception capacity per unit leaf area (rain)' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%snowUnloadingCoeff) = var_info('snowUnloadingCoeff' , 'time constant for unloading of snow from the forest canopy' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%canopyDrainageCoeff) = var_info('canopyDrainageCoeff' , 'time constant for drainage of liquid water from the forest canopy', 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%ratioDrip2Unloading) = var_info('ratioDrip2Unloading' , 'ratio of canopy drip to unloading of snow from the forest canopy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%canopyWettingFactor) = var_info('canopyWettingFactor' , 'maximum wetted fraction of the canopy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%canopyWettingExp) = var_info('canopyWettingExp' , 'exponent in canopy wetting function' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%minTempUnloading) = var_info('minTempUnloading' , 'min temp for unloading in windySnow' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%rateTempUnloading) = var_info('rateTempUnloading' , 'how quickly to unload due to temperature' , 'K s' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%minWindUnloading) = var_info('minWindUnloading' , 'min wind speed for unloading in windySnow' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%rateWindUnloading) = var_info('rateWindUnloading' , 'how quickly to unload due to wind' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! soil properties - mpar_meta(iLookPARAM%soil_dens_intr) = var_info('soil_dens_intr' , 'intrinsic soil density' , 'kg m-3' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%thCond_soil) = var_info('thCond_soil' , 'thermal conductivity of soil (includes quartz and other minerals)', 'W m-1 K-1' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%frac_sand) = var_info('frac_sand' , 'fraction of sand' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%frac_silt) = var_info('frac_silt' , 'fraction of silt' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%frac_clay) = var_info('frac_clay' , 'fraction of clay' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%theta_sat) = var_info('theta_sat' , 'soil porosity' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%theta_res) = var_info('theta_res' , 'volumetric residual water content' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%vGn_alpha) = var_info('vGn_alpha' , 'van Genuchten "alpha" parameter' , 'm-1' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%vGn_n) = var_info('vGn_n' , 'van Genuchten "n" parameter' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%k_soil) = var_info('k_soil' , 'saturated hydraulic conductivity' , 'm s-1' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%k_macropore) = var_info('k_macropore' , 'saturated hydraulic conductivity for macropores' , 'm s-1' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%soil_dens_intr) = var_info('soil_dens_intr' , 'intrinsic soil density' , 'kg m-3' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%thCond_soil) = var_info('thCond_soil' , 'thermal conductivity of soil (includes quartz and other minerals)', 'W m-1 K-1' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%frac_sand) = var_info('frac_sand' , 'fraction of sand' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%frac_silt) = var_info('frac_silt' , 'fraction of silt' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%frac_clay) = var_info('frac_clay' , 'fraction of clay' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%theta_sat) = var_info('theta_sat' , 'soil porosity' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%theta_res) = var_info('theta_res' , 'volumetric residual water content' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%vGn_alpha) = var_info('vGn_alpha' , 'van Genuchten "alpha" parameter' , 'm-1' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%vGn_n) = var_info('vGn_n' , 'van Genuchten "n" parameter' , '-' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%k_soil) = var_info('k_soil' , 'saturated hydraulic conductivity' , 'm s-1' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%k_macropore) = var_info('k_macropore' , 'saturated hydraulic conductivity for macropores' , 'm s-1' , get_ixVarType('parSoil'), iMissVec, iMissVec, .false.) ! scalar soil properties - mpar_meta(iLookPARAM%fieldCapacity) = var_info('fieldCapacity' , 'soil field capacity (vol liq water content when baseflow begins)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%wettingFrontSuction) = var_info('wettingFrontSuction' , 'Green-Ampt wetting front suction' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%theta_mp) = var_info('theta_mp' , 'volumetric liquid water content when macropore flow begins' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%mpExp) = var_info('mpExp' , 'empirical exponent in macropore flow equation' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%kAnisotropic) = var_info('kAnisotropic' , 'anisotropy factor for lateral hydraulic conductivity' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zScale_TOPMODEL) = var_info('zScale_TOPMODEL' , 'TOPMODEL scaling factor used in lower boundary condition for soil', 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%compactedDepth) = var_info('compactedDepth' , 'depth where k_soil reaches the compacted value given by CH78' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%aquiferBaseflowRate) = var_info('aquiferBaseflowRate' , 'baseflow rate when aquifer storage = aquiferScaleFactor' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%aquiferScaleFactor) = var_info('aquiferScaleFactor' , 'scaling factor for aquifer storage in the big bucket' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%aquiferBaseflowExp) = var_info('aquiferBaseflowExp' , 'baseflow exponent' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%qSurfScale) = var_info('qSurfScale' , 'scaling factor in the surface runoff parameterization' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%specificYield) = var_info('specificYield' , 'specific yield' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%specificStorage) = var_info('specificStorage' , 'specific storage coefficient' , 'm-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%f_impede) = var_info('f_impede' , 'ice impedence factor' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%soilIceScale) = var_info('soilIceScale' , 'scaling factor for depth of soil ice, used to get frozen fraction', 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%soilIceCV) = var_info('soilIceCV' , 'CV of depth of soil ice, used to get frozen fraction' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%fieldCapacity) = var_info('fieldCapacity' , 'soil field capacity (vol liq water content when baseflow begins)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%wettingFrontSuction) = var_info('wettingFrontSuction' , 'Green-Ampt wetting front suction' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%theta_mp) = var_info('theta_mp' , 'volumetric liquid water content when macropore flow begins' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%mpExp) = var_info('mpExp' , 'empirical exponent in macropore flow equation' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%kAnisotropic) = var_info('kAnisotropic' , 'anisotropy factor for lateral hydraulic conductivity' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zScale_TOPMODEL) = var_info('zScale_TOPMODEL' , 'TOPMODEL scaling factor used in lower boundary condition for soil', 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%compactedDepth) = var_info('compactedDepth' , 'depth where k_soil reaches the compacted value given by CH78' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%aquiferBaseflowRate) = var_info('aquiferBaseflowRate' , 'baseflow rate when aquifer storage = aquiferScaleFactor' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%aquiferScaleFactor) = var_info('aquiferScaleFactor' , 'scaling factor for aquifer storage in the big bucket' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%aquiferBaseflowExp) = var_info('aquiferBaseflowExp' , 'baseflow exponent' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%qSurfScale) = var_info('qSurfScale' , 'scaling factor in the surface runoff parameterization' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%specificYield) = var_info('specificYield' , 'specific yield' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%specificStorage) = var_info('specificStorage' , 'specific storage coefficient' , 'm-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%f_impede) = var_info('f_impede' , 'ice impedence factor' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%soilIceScale) = var_info('soilIceScale' , 'scaling factor for depth of soil ice, used to get frozen fraction', 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%soilIceCV) = var_info('soilIceCV' , 'CV of depth of soil ice, used to get frozen fraction' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! algorithmic control parameters - mpar_meta(iLookPARAM%minwind) = var_info('minwind' , 'minimum wind speed' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%minstep) = var_info('minstep' , 'minimum length of the time step numrec, not currently used' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%maxstep) = var_info('maxstep' , 'maximum length of the time step numrec' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%be_steps) = var_info('be_steps' , 'minimum number of substeps to take in a maxstep numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%wimplicit) = var_info('wimplicit' , 'weight assigned to the start-of-step fluxes ,numrec, not currently used', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%maxiter) = var_info('maxiter' , 'maximum number of iterations numrec, kinsol, or nonlinear iterations ida', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_liquid) = var_info('relConvTol_liquid' , 'relative convergence tolerance for vol frac liq water numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_liquid) = var_info('absConvTol_liquid' , 'absolute convergence tolerance for vol frac liq water numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_matric) = var_info('relConvTol_matric' , 'relative convergence tolerance for matric head numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_matric) = var_info('absConvTol_matric' , 'absolute convergence tolerance for matric head numrec' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_energy) = var_info('relConvTol_energy' , 'relative convergence tolerance for energy numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_energy) = var_info('absConvTol_energy' , 'absolute convergence tolerance for energy numrec' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_aquifr) = var_info('relConvTol_aquifr' , 'relative convergence tolerance for aquifer storage numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_aquifr) = var_info('absConvTol_aquifr' , 'absolute convergence tolerance for aquifer storage numrec' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relErrTol_ida) = var_info('relErrTol_ida' , 'relative error tolerance for ida' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absErrTol_ida) = var_info('absErrTol_ida' , 'absolute error tolerance for ida' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zmin) = var_info('zmin' , 'minimum layer depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zmax) = var_info('zmax' , 'maximum layer depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zminLayer1) = var_info('zminLayer1' , 'minimum layer depth for the 1st (top) layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zminLayer2) = var_info('zminLayer2' , 'minimum layer depth for the 2nd layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zminLayer3) = var_info('zminLayer3' , 'minimum layer depth for the 3rd layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zminLayer4) = var_info('zminLayer4' , 'minimum layer depth for the 4th layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zminLayer5) = var_info('zminLayer5' , 'minimum layer depth for the 5th (bottom) layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zmaxLayer1_lower) = var_info('zmaxLayer1_lower' , 'maximum layer depth for the 1st (top) layer when only 1 layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zmaxLayer2_lower) = var_info('zmaxLayer2_lower' , 'maximum layer depth for the 2nd layer when only 2 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zmaxLayer3_lower) = var_info('zmaxLayer3_lower' , 'maximum layer depth for the 3rd layer when only 3 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zmaxLayer4_lower) = var_info('zmaxLayer4_lower' , 'maximum layer depth for the 4th layer when only 4 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zmaxLayer1_upper) = var_info('zmaxLayer1_upper' , 'maximum layer depth for the 1st (top) layer when > 1 layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zmaxLayer2_upper) = var_info('zmaxLayer2_upper' , 'maximum layer depth for the 2nd layer when > 2 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zmaxLayer3_upper) = var_info('zmaxLayer3_upper' , 'maximum layer depth for the 3rd layer when > 3 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%zmaxLayer4_upper) = var_info('zmaxLayer4_upper' , 'maximum layer depth for the 4th layer when > 4 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - + mpar_meta(iLookPARAM%minwind) = var_info('minwind' , 'minimum wind speed' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%minstep) = var_info('minstep' , 'minimum length of the time step numrec, not currently used' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%maxstep) = var_info('maxstep' , 'maximum length of the time step numrec' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%be_steps) = var_info('be_steps' , 'minimum number of substeps to take in a maxstep numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%wimplicit) = var_info('wimplicit' , 'weight assigned to the start-of-step fluxes ,numrec, not currently used', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%maxiter) = var_info('maxiter' , 'maximum number of iterations numrec, kinsol, or nonlinear iterations ida', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_liquid) = var_info('relConvTol_liquid' , 'relative convergence tolerance for vol frac liq water numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_liquid) = var_info('absConvTol_liquid' , 'absolute convergence tolerance for vol frac liq water numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_matric) = var_info('relConvTol_matric' , 'relative convergence tolerance for matric head numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_matric) = var_info('absConvTol_matric' , 'absolute convergence tolerance for matric head numrec' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_energy) = var_info('relConvTol_energy' , 'relative convergence tolerance for energy numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_energy) = var_info('absConvTol_energy' , 'absolute convergence tolerance for energy numrec' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_aquifr) = var_info('relConvTol_aquifr' , 'relative convergence tolerance for aquifer storage numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_aquifr) = var_info('absConvTol_aquifr' , 'absolute convergence tolerance for aquifer storage numrec' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relErrTol_ida) = var_info('relErrTol_ida' , 'relative error tolerance for ida' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absErrTol_ida) = var_info('absErrTol_ida' , 'absolute error tolerance for ida' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zmin) = var_info('zmin' , 'minimum layer depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zmax) = var_info('zmax' , 'maximum layer depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zminLayer1) = var_info('zminLayer1' , 'minimum layer depth for the 1st (top) layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zminLayer2) = var_info('zminLayer2' , 'minimum layer depth for the 2nd layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zminLayer3) = var_info('zminLayer3' , 'minimum layer depth for the 3rd layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zminLayer4) = var_info('zminLayer4' , 'minimum layer depth for the 4th layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zminLayer5) = var_info('zminLayer5' , 'minimum layer depth for the 5th (bottom) layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zmaxLayer1_lower) = var_info('zmaxLayer1_lower' , 'maximum layer depth for the 1st (top) layer when only 1 layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zmaxLayer2_lower) = var_info('zmaxLayer2_lower' , 'maximum layer depth for the 2nd layer when only 2 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zmaxLayer3_lower) = var_info('zmaxLayer3_lower' , 'maximum layer depth for the 3rd layer when only 3 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zmaxLayer4_lower) = var_info('zmaxLayer4_lower' , 'maximum layer depth for the 4th layer when only 4 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zmaxLayer1_upper) = var_info('zmaxLayer1_upper' , 'maximum layer depth for the 1st (top) layer when > 1 layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zmaxLayer2_upper) = var_info('zmaxLayer2_upper' , 'maximum layer depth for the 2nd layer when > 2 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zmaxLayer3_upper) = var_info('zmaxLayer3_upper' , 'maximum layer depth for the 3rd layer when > 3 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%zmaxLayer4_upper) = var_info('zmaxLayer4_upper' , 'maximum layer depth for the 4th layer when > 4 layers' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! ----- ! * basin parameter data... ! ------------------------- - bpar_meta(iLookBPAR%basin__aquiferHydCond) = var_info('basin__aquiferHydCond' , 'hydraulic conductivity of the aquifer' , 'm s-1', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - bpar_meta(iLookBPAR%basin__aquiferScaleFactor) = var_info('basin__aquiferScaleFactor', 'scaling factor for aquifer storage in the big bucket' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - bpar_meta(iLookBPAR%basin__aquiferBaseflowExp) = var_info('basin__aquiferBaseflowExp', 'baseflow exponent for the big bucket' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - bpar_meta(iLookBPAR%routingGammaShape) = var_info('routingGammaShape' , 'shape parameter in Gamma distribution used for sub-grid routing', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - bpar_meta(iLookBPAR%routingGammaScale) = var_info('routingGammaScale' , 'scale parameter in Gamma distribution used for sub-grid routing', 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - + bpar_meta(iLookBPAR%basin__aquiferHydCond) = var_info('basin__aquiferHydCond' , 'hydraulic conductivity of the aquifer' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + bpar_meta(iLookBPAR%basin__aquiferScaleFactor) = var_info('basin__aquiferScaleFactor' , 'scaling factor for aquifer storage in the big bucket' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + bpar_meta(iLookBPAR%basin__aquiferBaseflowExp) = var_info('basin__aquiferBaseflowExp' , 'baseflow exponent for the big bucket' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + bpar_meta(iLookBPAR%routingGammaShape) = var_info('routingGammaShape' , 'shape parameter in Gamma distribution used for sub-grid routing' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + bpar_meta(iLookBPAR%routingGammaScale) = var_info('routingGammaScale' , 'scale parameter in Gamma distribution used for sub-grid routing' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! ----- ! * local model prognostic (state) variables... ! --------------------------------------------- @@ -329,7 +322,6 @@ subroutine popMetadat(err,message) prog_meta(iLookPROG%mLayerDepth) = var_info('mLayerDepth' , 'depth of each layer' , 'm' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) prog_meta(iLookPROG%mLayerHeight) = var_info('mLayerHeight' , 'height of the layer mid-point (top of soil = 0)' , 'm' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) prog_meta(iLookPROG%iLayerHeight) = var_info('iLayerHeight' , 'height of the layer interface (top of soil = 0)' , 'm' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) - ! ----- ! * local model diagnostic variables... ! ------------------------------------- @@ -405,8 +397,8 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%scalarFracLiqVeg) = var_info('scalarFracLiqVeg' , 'fraction of liquid water on vegetation' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarCanopyWetFraction) = var_info('scalarCanopyWetFraction' , 'fraction canopy that is wet' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! canopy derivatives from adjusting the canopy temperature to account for new snow - diag_meta(iLookDIAG%dTkCanopyAdj_dTkCanopy) = var_info('dTkCanopyAdj_dTkCanopy' , 'derivative in the adjusted temperature w.r.t. original temperature' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%dTkCanopyAdj_dCanWat) = var_info('dTkCanopyAdj_dCanWat' , 'derivative in the adjusted temperature w.r.t. volumetric water content', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%dTkCanopyAdj_dTkCanopy) = var_info('dTkCanopyAdj_dTkCanopy' , 'derivative in the adjusted temperature w.r.t. original temperature' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%dTkCanopyAdj_dCanWat) = var_info('dTkCanopyAdj_dCanWat' , 'derivative in the adjusted temperature w.r.t. volumetric water content', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! snow hydrology diag_meta(iLookDIAG%scalarSnowAge) = var_info('scalarSnowAge' , 'non-dimensional snow age' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarGroundSnowFraction) = var_info('scalarGroundSnowFraction' , 'fraction ground that is covered with snow' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) @@ -437,7 +429,6 @@ subroutine popMetadat(err,message) ! timing information diag_meta(iLookDIAG%numFluxCalls) = var_info('numFluxCalls' , 'number of flux calls' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%wallClockTime) = var_info('wallClockTime' , 'wall clock time' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! ----- ! * local model fluxes... ! ----------------------- @@ -541,7 +532,6 @@ subroutine popMetadat(err,message) flux_meta(iLookFLUX%scalarTotalET) = var_info('scalarTotalET' , 'total ET' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) flux_meta(iLookFLUX%scalarTotalRunoff) = var_info('scalarTotalRunoff' , 'total runoff' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) flux_meta(iLookFLUX%scalarNetRadiation) = var_info('scalarNetRadiation' , 'net radiation' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! ----- ! * local flux derivatives... ! --------------------------- @@ -575,21 +565,21 @@ subroutine popMetadat(err,message) deriv_meta(iLookDERIV%dTheta_dTkCanopy) = var_info('dTheta_dTkCanopy' , 'derivative of volumetric liquid water content w.r.t. temperature' , 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%d2Theta_dTkCanopy2) = var_info('d2Theta_dTkCanopy2' , 'second derivative of volumetric liquid water content w.r.t. temperature', 'K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dCanLiq_dTcanopy) = var_info('dCanLiq_dTcanopy' , 'derivative of canopy liquid storage w.r.t. temperature' , 'kg m-2 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dFracLiqVeg_dTkCanopy) = var_info('dFracLiqVeg_dTkCanopy' , 'derivative in fraction of (throughfall + drainage) w.r.t. temperature', 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dFracLiqVeg_dTkCanopy) = var_info('dFracLiqVeg_dTkCanopy' , 'derivative in fraction of (throughfall + drainage) w.r.t. temperature', 'K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! derivatives in canopy liquid fluxes w.r.t. canopy water deriv_meta(iLookDERIV%scalarCanopyLiqDeriv) = var_info('scalarCanopyLiqDeriv' , 'derivative in (throughfall + drainage) w.r.t. canopy liquid water' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%scalarThroughfallRainDeriv) = var_info('scalarThroughfallRainDeriv' , 'derivative in throughfall w.r.t. canopy liquid water' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%scalarCanopyLiqDrainageDeriv) = var_info('scalarCanopyLiqDrainageDeriv' , 'derivative in canopy drainage w.r.t. canopy liquid water' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! energy derivatives that might be treated as constant if heat capacity and thermal conductivity not updated - deriv_meta(iLookDERIV%dVolHtCapBulk_dPsi0) = var_info('dVolHtCapBulk_dPsi0' , 'derivative in bulk heat capacity w.r.t. matric potential' , 'J m-4 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dTheta) = var_info('dVolHtCapBulk_dTheta' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dCanWat) = var_info('dVolHtCapBulk_dCanWat' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dTk) = var_info('dVolHtCapBulk_dTk' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dTkCanopy) = var_info('dVolHtCapBulk_dTkCanopy' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dThermalC_dTempAbove) = var_info('dThermalC_dTempAbove' , 'derivative in the thermal conductivity w.r.t. energy in the layer above','J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dThermalC_dTempBelow) = var_info('dThermalC_dTempBelow' , 'derivative in the thermal conductivity w.r.t. energy in the layer above','J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dThermalC_dWatAbove) = var_info('dThermalC_dWatAbove' , 'derivative in the thermal conductivity w.r.t. water in the layer above', 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dThermalC_dWatBelow) = var_info('dThermalC_dWatBelow' , 'derivative in the thermal conductivity w.r.t. water in the layer above', 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dPsi0) = var_info('dVolHtCapBulk_dPsi0' , 'derivative in bulk heat capacity w.r.t. matric potential' , 'J m-4 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dTheta) = var_info('dVolHtCapBulk_dTheta' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dCanWat) = var_info('dVolHtCapBulk_dCanWat' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dTk) = var_info('dVolHtCapBulk_dTk' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dTkCanopy) = var_info('dVolHtCapBulk_dTkCanopy' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dThermalC_dTempAbove) = var_info('dThermalC_dTempAbove' , 'derivative in the thermal conductivity w.r.t. energy in the layer above','J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dThermalC_dTempBelow) = var_info('dThermalC_dTempBelow' , 'derivative in the thermal conductivity w.r.t. energy in the layer above','J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dThermalC_dWatAbove) = var_info('dThermalC_dWatAbove' , 'derivative in the thermal conductivity w.r.t. water in the layer above', 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dThermalC_dWatBelow) = var_info('dThermalC_dWatBelow' , 'derivative in the thermal conductivity w.r.t. water in the layer above', 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below deriv_meta(iLookDERIV%dNrgFlux_dTempAbove) = var_info('dNrgFlux_dTempAbove' , 'derivatives in the flux w.r.t. temperature in the layer above' , 'J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dNrgFlux_dTempBelow) = var_info('dNrgFlux_dTempBelow' , 'derivatives in the flux w.r.t. temperature in the layer below' , 'J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) @@ -606,33 +596,32 @@ subroutine popMetadat(err,message) deriv_meta(iLookDERIV%mLayerdPsi_dTheta) = var_info('mLayerdPsi_dTheta' , 'derivative in the soil water characteristic w.r.t. theta' , 'm' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dq_dHydStateAbove) = var_info('dq_dHydStateAbove' , 'change in flux at layer interfaces w.r.t. states in the layer above' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dq_dHydStateBelow) = var_info('dq_dHydStateBelow' , 'change in flux at layer interfaces w.r.t. states in the layer below' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dq_dHydStateLayerSurfVec) = var_info('dq_dHydStateLayerSurfVec' , 'change in the flux in soil surface interface w.r.t. state variables in layers' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dq_dHydStateLayerSurfVec) = var_info('dq_dHydStateLayerSurfVec' , 'change in the flux in soil surface interface w.r.t. state variables in layers','unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) ! derivative in baseflow flux w.r.t. aquifer storage deriv_meta(iLookDERIV%dBaseflow_dAquifer) = var_info('dBaseflow_dAquifer' , 'derivative in baseflow flux w.r.t. aquifer storage' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables deriv_meta(iLookDERIV%dq_dNrgStateAbove) = var_info('dq_dNrgStateAbove' , 'change in flux at layer interfaces w.r.t. states in the layer above' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dq_dNrgStateBelow) = var_info('dq_dNrgStateBelow' , 'change in flux at layer interfaces w.r.t. states in the layer below' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dq_dNrgStateLayerSurfVec) = var_info('dq_dNrgStateLayerSurfVec' , 'change in the flux in soil surface interface w.r.t. state variables in layers' , 'unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dq_dNrgStateLayerSurfVec) = var_info('dq_dNrgStateLayerSurfVec' , 'change in the flux in soil surface interface w.r.t. state variables in layers','unknown' , get_ixVarType('ifcSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dPsiLiq_dTemp) = var_info('dPsiLiq_dTemp' , 'derivative in the liquid water matric potential w.r.t. temperature' , 'm K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dPsiLiq_dPsi0) = var_info('dPsiLiq_dPsi0' , 'derivative in liquid matric potential w.r.t. total matric potential' , '-' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) ! derivatives in soil transpiration w.r.t. canopy state variables - deriv_meta(iLookDERIV%mLayerdTrans_dTCanair) = var_info('mLayerdTrans_dTCanair' , 'derivatives in the soil layer transpiration flux w.r.t. canopy air temperature', 'm s-1 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%mLayerdTrans_dTCanopy) = var_info('mLayerdTrans_dTCanopy' , 'derivatives in the soil layer transpiration flux w.r.t. canopy temperature', 'm s-1 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%mLayerdTrans_dTGround) = var_info('mLayerdTrans_dTGround' , 'derivatives in the soil layer transpiration flux w.r.t. ground temperature', 'm s-1 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%mLayerdTrans_dCanWat) = var_info('mLayerdTrans_dCanWat' , 'derivatives in the soil layer transpiration flux w.r.t. canopy total water', 'm-1 s-1 kg-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%mLayerdTrans_dTCanair) = var_info('mLayerdTrans_dTCanair' , 'derivatives in the soil layer transpiration flux w.r.t. canopy air temperature','m s-1 K-1',get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%mLayerdTrans_dTCanopy) = var_info('mLayerdTrans_dTCanopy' , 'derivatives in the soil layer transpiration flux w.r.t. canopy temperature', 'm s-1 K-1' ,get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%mLayerdTrans_dTGround) = var_info('mLayerdTrans_dTGround' , 'derivatives in the soil layer transpiration flux w.r.t. ground temperature', 'm s-1 K-1' ,get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%mLayerdTrans_dCanWat) = var_info('mLayerdTrans_dCanWat' , 'derivatives in the soil layer transpiration flux w.r.t. canopy total water','m-1 s-1 kg-1' ,get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) ! derivatives in aquifer transpiration w.r.t. canopy state variables - deriv_meta(iLookDERIV%dAquiferTrans_dTCanair) = var_info('dAquiferTrans_dTCanair' , 'derivative in the aquifer transpiration flux w.r.t. canopy air temperature', 'm s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dAquiferTrans_dTCanopy) = var_info('dAquiferTrans_dTCanopy' , 'derivative in the aquifer transpiration flux w.r.t. canopy temperature', 'm s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dAquiferTrans_dTGround) = var_info('dAquiferTrans_dTGround' , 'derivative in the aquifer transpiration flux w.r.t. ground temperature', 'm s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dAquiferTrans_dCanWat) = var_info('dAquiferTrans_dCanWat' , 'derivative in the aquifer transpiration flux w.r.t. canopy total water', 'm-1 s-1 kg-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature + deriv_meta(iLookDERIV%dAquiferTrans_dTCanair) = var_info('dAquiferTrans_dTCanair' , 'derivative in the aquifer transpiration flux w.r.t. canopy air temperature','m s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dAquiferTrans_dTCanopy) = var_info('dAquiferTrans_dTCanopy' , 'derivative in the aquifer transpiration flux w.r.t. canopy temperature', 'm s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dAquiferTrans_dTGround) = var_info('dAquiferTrans_dTGround' , 'derivative in the aquifer transpiration flux w.r.t. ground temperature', 'm s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dAquiferTrans_dCanWat) = var_info('dAuiferTrans_dCanWat' , 'derivative in the aquifer transpiration flux w.r.t. canopy total water', 'm-1 s-1 kg-1', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! derivative in liquid water fluxes for the soil and snow domain w.rt temperatur deriv_meta(iLookDERIV%dFracLiqSnow_dTk) = var_info('dFracLiqSnow_dTk' , 'derivative in fraction of liquid snow w.r.t. temperature' , 'K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%mLayerdTheta_dTk) = var_info('mLayerdTheta_dTk' , 'derivative of volumetric liquid water content w.r.t. temperature' , 'K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%mLayerd2Theta_dTk2) = var_info('mLayerd2Theta_dTk2' , 'second derivative of volumetric liquid water content w.r.t. temperature', 'K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%mLayerd2Theta_dTk2) = var_info('mLayerd2Theta_dTk2' , 'second derivative of volumetric liquid water content w.r.t. temperature','K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) ! derivatives in time - deriv_meta(iLookDERIV%mLayerdTemp_dt) = var_info('mLayerdTemp_dt' , 'timestep change in layer temperature' , 'K' ,get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%scalarCanopydTemp_dt) = var_info('scalarCanopydTemp_dt' , 'timestep change in canopy temperature' , 'K' ,get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - + deriv_meta(iLookDERIV%mLayerdTemp_dt) = var_info('mLayerdTemp_dt' , 'timestep change in layer temperature' , 'K' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%scalarCanopydTemp_dt) = var_info('scalarCanopydTemp_dt' , 'timestep change in canopy temperature' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! ----- ! * basin-wide runoff and aquifer fluxes... ! ----------------------------------------- @@ -649,20 +638,15 @@ subroutine popMetadat(err,message) bvar_meta(iLookBVAR%routingFractionFuture) = var_info('routingFractionFuture' , 'fraction of runoff in future time steps' , '-' , get_ixVarType('routing'), iMissVec, iMissVec, .false.) bvar_meta(iLookBVAR%averageInstantRunoff) = var_info('averageInstantRunoff' , 'instantaneous runoff' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) bvar_meta(iLookBVAR%averageRoutedRunoff) = var_info('averageRoutedRunoff' , 'routed runoff' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! ----- - ! * lookup tables... - ! ------------------ - - ! temperature and enthalpy + ! * temperature and enthalpy lookup tables... + ! ------------------------------------------- lookup_meta(iLookLOOKUP%temperature) = var_info('temperature' , 'value of temperature in the lookup table' , 'K' , get_ixVarType('unknown'), iMissVec, iMissVec, .false.) lookup_meta(iLookLOOKUP%enthalpy) = var_info('enthalpy' , 'value of enthalpy in the lookup table' , 'J m-3' , get_ixVarType('unknown'), iMissVec, iMissVec, .false.) lookup_meta(iLookLOOKUP%deriv2) = var_info('deriv2' , 'second derivatives of the interpolating function' , 'mixed' , get_ixVarType('unknown'), iMissVec, iMissVec, .false.) - ! ----- ! * model indices... ! ------------------ - ! number of model layers, and layer indices indx_meta(iLookINDEX%nSnow) = var_info('nSnow' , 'number of snow layers' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) indx_meta(iLookINDEX%nSoil) = var_info('nSoil' , 'number of soil layers' , '-', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/engine/computThermConduct.f90 b/build/source/engine/computThermConduct.f90 index 92ea99c27..eb0621402 100644 --- a/build/source/engine/computThermConduct.f90 +++ b/build/source/engine/computThermConduct.f90 @@ -90,10 +90,10 @@ subroutine computThermConduct(& prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! output: derivatives - dThermalC_dWatAbove, & ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dWatBelow, & ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dTempAbove, & ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dTempBelow, & ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dWatAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dTempAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above ! output: error control err,message) ! intent(out): error control diff --git a/build/source/engine/snowLiqFlx.f90 b/build/source/engine/snowLiqFlx.f90 index f91f7f7f4..7223e84ea 100644 --- a/build/source/engine/snowLiqFlx.f90 +++ b/build/source/engine/snowLiqFlx.f90 @@ -71,48 +71,48 @@ subroutine snowLiqFlx(& err,message) ! intent(out): error control implicit none ! input: model control - integer(i4b),intent(in) :: nSnow ! number of snow layers - logical(lgt),intent(in) :: firstFluxCall ! the first flux call - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + integer(i4b),intent(in) :: nSnow ! number of snow layers + logical(lgt),intent(in) :: firstFluxCall ! the first flux call + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: forcing for the snow domain - real(rkind),intent(in) :: scalarThroughfallRain ! computed throughfall rate (kg m-2 s-1) - real(rkind),intent(in) :: scalarCanopyLiqDrainage ! computed drainage of liquid water (kg m-2 s-1) + real(rkind),intent(in) :: scalarThroughfallRain ! computed throughfall rate (kg m-2 s-1) + real(rkind),intent(in) :: scalarCanopyLiqDrainage ! computed drainage of liquid water (kg m-2 s-1) ! input: model state vector - real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value of volumetric fraction of liquid water at the current iteration (-) + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value of volumetric fraction of liquid water at the current iteration (-) ! input-output: data structures - type(var_ilength),intent(in) :: indx_data ! model indices - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_ilength),intent(in) :: indx_data ! model indices + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU ! output: fluxes and derivatives - real(rkind),intent(inout) :: iLayerLiqFluxSnow(0:) ! vertical liquid water flux at layer interfaces (m s-1) - real(rkind),intent(inout) :: iLayerLiqFluxSnowDeriv(0:) ! derivative in vertical liquid water flux at layer interfaces (m s-1) + real(rkind),intent(inout) :: iLayerLiqFluxSnow(0:) ! vertical liquid water flux at layer interfaces (m s-1) + real(rkind),intent(inout) :: iLayerLiqFluxSnowDeriv(0:) ! derivative in vertical liquid water flux at layer interfaces (m s-1) ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ------------------------------------------------------------------------------------------------------------------------------------------ + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------ ------------------------------------------------------------------------------------------------------------ ! local variables - integer(i4b) :: i ! search index for scalar solution - integer(i4b) :: iLayer ! layer index - integer(i4b) :: ixTop ! top layer in subroutine call - integer(i4b) :: ixBot ! bottom layer in subroutine call - real(rkind) :: multResid ! multiplier for the residual water content (-) - real(rkind),parameter :: residThrs=550._rkind ! ice density threshold to reduce residual liquid water content (kg m-3) - real(rkind),parameter :: residScal=10._rkind ! scaling factor for residual liquid water content reduction factor (kg m-3) - real(rkind),parameter :: maxVolIceContent=0.7_rkind ! maximum volumetric ice content to store water (-) - real(rkind) :: availCap ! available storage capacity [0,1] (-) - real(rkind) :: relSaturn ! relative saturation [0,1] (-) + integer(i4b) :: i ! search index for scalar solution + integer(i4b) :: iLayer ! layer index + integer(i4b) :: ixTop ! top layer in subroutine call + integer(i4b) :: ixBot ! bottom layer in subroutine call + real(rkind) :: multResid ! multiplier for the residual water content (-) + real(rkind),parameter :: residThrs=550._rkind ! ice density threshold to reduce residual liquid water content (kg m-3) + real(rkind),parameter :: residScal=10._rkind ! scaling factor for residual liquid water content reduction factor (kg m-3) + real(rkind),parameter :: maxVolIceContent=0.7_rkind ! maximum volumetric ice content to store water (-) + real(rkind) :: availCap ! available storage capacity [0,1] (-) + real(rkind) :: relSaturn ! relative saturation [0,1] (-) ! ------------------------------------------------------------------------------------------------------------------------------------------ ! make association of local variables with information in the data structures associate(& ! input: layer indices - ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers - ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat, & ! intent(in): index in the state subset for hydrology state variables in the snow domain + ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers + ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat, & ! intent(in): index in the state subset for hydrology state variables in the snow domain ! input: snow properties and parameters - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow), & ! intent(in): volumetric ice content at the start of the time step (-) - Fcapil => mpar_data%var(iLookPARAM%Fcapil)%dat(1), & ! intent(in): capillary retention as a fraction of the total pore volume (-) - k_snow => mpar_data%var(iLookPARAM%k_snow)%dat(1), & ! intent(in): hydraulic conductivity of snow (m s-1), 0.0055 = approx. 20 m/hr, from UEB - mw_exp => mpar_data%var(iLookPARAM%mw_exp)%dat(1), & ! intent(in): exponent for meltwater flow (-) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow), & ! intent(in): volumetric ice content at the start of the time step (-) + Fcapil => mpar_data%var(iLookPARAM%Fcapil)%dat(1), & ! intent(in): capillary retention as a fraction of the total pore volume (-) + k_snow => mpar_data%var(iLookPARAM%k_snow)%dat(1), & ! intent(in): hydraulic conductivity of snow (m s-1), 0.0055 = approx. 20 m/hr, from UEB + mw_exp => mpar_data%var(iLookPARAM%mw_exp)%dat(1), & ! intent(in): exponent for meltwater flow (-) ! input/output: diagnostic variables -- only computed for the first iteration mLayerPoreSpace => diag_data%var(iLookDIAG%mLayerPoreSpace)%dat, & ! intent(inout): pore space in each snow layer (-) mLayerThetaResid => diag_data%var(iLookDIAG%mLayerThetaResid)%dat & ! intent(inout): esidual volumetric liquid water content in each snow layer (-) diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index 30f2e57be..6f4e3bc86 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -118,10 +118,10 @@ subroutine ssdNrgFlux(& ! input: trial model state variables real(rkind),intent(in) :: mLayerTempTrial(:) ! temperature in each layer at the current iteration (m) ! input: derivatives - real(rkind),intent(in) :: dThermalC_dWatAbove(:) ! derivative in the thermal conductivity w.r.t. water state in the layer above - real(rkind),intent(in) :: dThermalC_dWatBelow(:) ! derivative in the thermal conductivity w.r.t. water state in the layer above - real(rkind),intent(in) :: dThermalC_dTempAbove(:) ! derivative in the thermal conductivity w.r.t. energy state in the layer above - real(rkind),intent(in) :: dThermalC_dTempBelow(:) ! derivative in the thermal conductivity w.r.t. energy state in the layer above + real(rkind),intent(in) :: dThermalC_dWatAbove(0:) ! derivative in the thermal conductivity w.r.t. water state in the layer above + real(rkind),intent(in) :: dThermalC_dWatBelow(0:) ! derivative in the thermal conductivity w.r.t. water state in the layer above + real(rkind),intent(in) :: dThermalC_dTempAbove(0:) ! derivative in the thermal conductivity w.r.t. energy state in the layer above + real(rkind),intent(in) :: dThermalC_dTempBelow(0:) ! derivative in the thermal conductivity w.r.t. energy state in the layer above ! input-output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! state vector geometry From 268b5374c79fb1fabdfb5d08f8d73de91ef48583 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 6 Jul 2023 19:39:43 +0900 Subject: [PATCH 0792/1472] variable name typo --- build/source/dshare/popMetadat.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index b14a815f1..5c90e336e 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -614,7 +614,7 @@ subroutine popMetadat(err,message) deriv_meta(iLookDERIV%dAquiferTrans_dTCanair) = var_info('dAquiferTrans_dTCanair' , 'derivative in the aquifer transpiration flux w.r.t. canopy air temperature','m s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dAquiferTrans_dTCanopy) = var_info('dAquiferTrans_dTCanopy' , 'derivative in the aquifer transpiration flux w.r.t. canopy temperature', 'm s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dAquiferTrans_dTGround) = var_info('dAquiferTrans_dTGround' , 'derivative in the aquifer transpiration flux w.r.t. ground temperature', 'm s-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dAquiferTrans_dCanWat) = var_info('dAuiferTrans_dCanWat' , 'derivative in the aquifer transpiration flux w.r.t. canopy total water', 'm-1 s-1 kg-1', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dAquiferTrans_dCanWat) = var_info('dAquiferTrans_dCanWat' , 'derivative in the aquifer transpiration flux w.r.t. canopy total water', 'm-1 s-1 kg-1', get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! derivative in liquid water fluxes for the soil and snow domain w.rt temperatur deriv_meta(iLookDERIV%dFracLiqSnow_dTk) = var_info('dFracLiqSnow_dTk' , 'derivative in fraction of liquid snow w.r.t. temperature' , 'K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%mLayerdTheta_dTk) = var_info('mLayerdTheta_dTk' , 'derivative of volumetric liquid water content w.r.t. temperature' , 'K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) From 9d9baeafe8890cc9d6e39aaff221dc40fbc956b9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 6 Jul 2023 21:18:24 +0900 Subject: [PATCH 0793/1472] since the temp adjust happens before the iteration, I shouldn't need that in the Jacobian. Removing everything. --- build/source/dshare/get_ixname.f90 | 3 - build/source/dshare/popMetadat.f90 | 3 - build/source/dshare/var_lookup.f90 | 6 +- build/source/engine/canopySnow.f90 | 63 +---------- build/source/engine/computJacob.f90 | 13 --- build/source/engine/computJacobWithPrime.f90 | 13 --- build/source/engine/coupled_em.f90 | 13 --- build/source/engine/snow_utils.f90 | 26 ----- build/source/engine/tempAdjust.f90 | 109 ++----------------- 9 files changed, 14 insertions(+), 235 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 2f782df09..79af0c0bf 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -541,9 +541,6 @@ function get_ixdiag(varName) ! canopy hydrology case('scalarFracLiqVeg' ); get_ixdiag = iLookDIAG%scalarFracLiqVeg ! fraction of liquid water on vegetation (-) case('scalarCanopyWetFraction' ); get_ixdiag = iLookDIAG%scalarCanopyWetFraction ! fraction of canopy that is wet - ! canopy derivatives from adjusting the canopy temperature to account for new snow - case('dTkCanopyAdj_dTkCanopy' ); get_ixdiag = iLookDIAG%dTkCanopyAdj_dTkCanopy ! derivative in the adjusted temperature w.r.t. original temperature - case('dTkCanopyAdj_dCanWat' ); get_ixdiag = iLookDIAG%dTkCanopyAdj_dCanWat ! derivative in the adjusted temperature w.r.t. volumetric water content ! snow hydrology case('scalarSnowAge' ); get_ixdiag = iLookDIAG%scalarSnowAge ! non-dimensional snow age (-) case('scalarGroundSnowFraction' ); get_ixdiag = iLookDIAG%scalarGroundSnowFraction ! fraction of ground that is covered with snow (-) diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 5c90e336e..ebbd6f245 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -396,9 +396,6 @@ subroutine popMetadat(err,message) ! canopy hydrology diag_meta(iLookDIAG%scalarFracLiqVeg) = var_info('scalarFracLiqVeg' , 'fraction of liquid water on vegetation' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarCanopyWetFraction) = var_info('scalarCanopyWetFraction' , 'fraction canopy that is wet' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! canopy derivatives from adjusting the canopy temperature to account for new snow - diag_meta(iLookDIAG%dTkCanopyAdj_dTkCanopy) = var_info('dTkCanopyAdj_dTkCanopy' , 'derivative in the adjusted temperature w.r.t. original temperature' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%dTkCanopyAdj_dCanWat) = var_info('dTkCanopyAdj_dCanWat' , 'derivative in the adjusted temperature w.r.t. volumetric water content', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! snow hydrology diag_meta(iLookDIAG%scalarSnowAge) = var_info('scalarSnowAge' , 'non-dimensional snow age' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarGroundSnowFraction) = var_info('scalarGroundSnowFraction' , 'fraction ground that is covered with snow' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 54eb29f49..d4d4bf66b 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -421,9 +421,6 @@ MODULE var_lookup ! canopy hydrology integer(i4b) :: scalarFracLiqVeg = integerMissing ! fraction of liquid water on vegetation (-) integer(i4b) :: scalarCanopyWetFraction = integerMissing ! fraction of canopy that is wet - ! canopy derivatives from adjusting the canopy temperature to account for new snow - integer(i4b) :: dTkCanopyAdj_dTkCanopy = integerMissing ! derivative in the adjusted temperature w.r.t. original temperature - integer(i4b) :: dTkCanopyAdj_dCanWat = integerMissing ! derivative in the adjusted temperature w.r.t. volumetric water content ! snow hydrology integer(i4b) :: scalarSnowAge = integerMissing ! non-dimensional snow age (-) integer(i4b) :: scalarGroundSnowFraction = integerMissing ! fraction of ground that is covered with snow (-) @@ -875,8 +872,7 @@ MODULE var_lookup 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,& 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,& 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,& - 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,& - 91) + 81, 82, 83, 84, 85, 86, 87, 88, 89) ! named variables: model fluxes type(iLook_flux), public,parameter :: iLookFLUX =iLook_flux ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& diff --git a/build/source/engine/canopySnow.f90 b/build/source/engine/canopySnow.f90 index 64bad8514..a6675e87a 100644 --- a/build/source/engine/canopySnow.f90 +++ b/build/source/engine/canopySnow.f90 @@ -67,16 +67,10 @@ subroutine canopySnow(& diag_data, & ! intent(in): model diagnostic variables for a local HRU prog_data, & ! intent(inout): model prognostic variables for a local HRU flux_data, & ! intent(inout): model flux variables - ! output: derivatives - dCanopyIce_dWat, & ! intent(out): derivative of canopy ice with canopy water - dCanopyIce_dTk, & ! intent(out): derivative of canopy ice with canopy temperature ! output: error control err,message) ! intent(out): error control ! ------------------------------------------------------------------------------------------------ - ! utility routines - USE snow_utils_module,only:fracliquid ! compute fraction of liquid water - USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) - implicit none + implicit none ! ------------------------------------------------------------------------------------------------ ! input: model control real(rkind),intent(in) :: dt ! time step (seconds) @@ -89,9 +83,6 @@ subroutine canopySnow(& type(var_dlength),intent(in) :: diag_data ! model diagnostic variables for a local HRU type(var_dlength),intent(inout) :: prog_data ! model prognostic variables for a local HRU type(var_dlength),intent(inout) :: flux_data ! model flux variables - ! output: derivatives - real(rkind),intent(out) :: dCanopyIce_dWat ! derivative of canopy ice with canopy water - real(rkind),intent(out) :: dCanopyIce_dTk ! derivative of canopy ice with canopy temperature ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -99,7 +90,6 @@ subroutine canopySnow(& real(rkind),parameter :: valueMissing=-9999._rkind ! missing value integer(i4b) :: iter ! iteration index integer(i4b),parameter :: maxiter=50 ! maximum number of iterations - logical :: use_drip ! branch decision used in computing for first canopy ice derivative (code shortcut) real(rkind) :: scalarCanopyWat ! total canopy water (kg m-2) real(rkind) :: unloading_melt ! unloading associated with canopy drip (kg m-2 s-1) real(rkind) :: airtemp_degC ! value of air temperature in degrees Celcius @@ -110,15 +100,12 @@ subroutine canopySnow(& real(rkind) :: unloadingDeriv ! derivative in unloading flux w.r.t. canopy storage (s-1) real(rkind) :: scalarCanopyIceIter ! trial value for mass of ice on the vegetation canopy (kg m-2) (kg m-2) real(rkind) :: fLiq ! fraction of liquid water (-) - real(rkind) :: canopyLiqDrainageDeriv ! derivative in canopy drainage w.r.t. canopy liquid water (s-1) (recomputed from vegLiqFlus) - real(rkind) :: dCanopyIceIter_dWat,dCanopyIceIter_dTk ! derivates of canopy ice iteration w.r.t canopy water and canopy temperature real(rkind) :: flux ! net flux (kg m-2 s-1) real(rkind) :: delS ! change in storage (kg m-2) real(rkind) :: resMass ! residual in mass equation (kg m-2) real(rkind) :: tempUnloadingFun ! temperature unloading functions, Eq. 14 in Roesch et al. 2001 real(rkind) :: windUnloadingFun ! temperature unloading functions, Eq. 15 in Roesch et al. 2001 real(rkind),parameter :: convTolerMass=0.0001_rkind ! convergence tolerance for mass (kg m-2) - real(rkind) :: ddelS_dWat, ddelS_dTk ! derivates of iteration increment w.r.t canopy water and canopy temperature ! ------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='canopySnow/' @@ -139,18 +126,14 @@ subroutine canopySnow(& minWindUnloading => mpar_data%var(iLookPARAM%minWindUnloading)%dat(1), & ! intent(in): [dp] constant describing the minimum temperature for snow unloading in windySnow parameterization (K) rateTempUnloading => mpar_data%var(iLookPARAM%rateTempUnloading)%dat(1), & ! intent(in): [dp] constant describing how quickly snow will unload due to temperature in windySnow parameterization (K s) rateWindUnloading => mpar_data%var(iLookPARAM%rateWindUnloading)%dat(1), & ! intent(in): [dp] constant describing how quickly snow will unload due to wind in windySnow parameterization (K s) - scalarCanopyLiqMax => diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1), & ! intent(in): [dp] maximum storage before canopy drainage begins (kg m-2 s-1) - scalarCanopyDrainageCoeff => mpar_data%var(iLookPARAM%canopyDrainageCoeff)%dat(1), & ! intent(in): [dp] canopy drainage coefficient (s-1) ! model diagnostic variables scalarNewSnowDensity => diag_data%var(iLookDIAG%scalarNewSnowDensity)%dat(1), & ! intent(in): [dp] density of new snow (kg m-3) ! model prognostic variables (input/output) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1), & ! intent(in): [dp] mass of liquid water on the vegetation canopy (kg m-2) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! intent(inout): [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1), & ! intent(in): [dp] temperature of the vegetation canopy (K) ! model fluxes (input) scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1), & ! intent(in): [dp] temperature of the canopy air space (k) scalarSnowfall => flux_data%var(iLookFLUX%scalarSnowfall)%dat(1), & ! intent(in): [dp] computed snowfall rate (kg m-2 s-1) - scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1), & ! intent(in): [dp] liquid drainage from the vegetation canopy (kg m-2 s-1) + scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1), & ! intent(in): [dp] liquid drainage from the vegetation canopy (kg m-2 s-1) scalarWindspdCanopyTop => flux_data%var(iLookFLUX%scalarWindspdCanopyTop)%dat(1), & ! intent(in): [dp] windspeed at the top of the canopy (m s-1) ! model variables (output) scalarThroughfallSnow => flux_data%var(iLookFLUX%scalarThroughfallSnow)%dat(1), & ! intent(out): [dp] snow that reaches the ground without ever touching the canopy (kg m-2 s-1) @@ -158,44 +141,16 @@ subroutine canopySnow(& ) ! associate variables in the data structures ! ----------------------------------------------------------------------------------------------------------------------------------------------------- - ! compute the total canopy water (state variable: will not change) - scalarCanopyWat = scalarCanopyLiq + scalarCanopyIce - - ! compute the initial derivatives - fLiq = fracliquid(scalarCanopyTemp,snowfrz_scale) - dCanopyIce_dWat = 1._rkind - fLiq - dCanopyIce_dTk = -dFracLiq_dTk(scalarCanopyTemp,snowfrz_scale)*scalarCanopyWat - ! compute unloading due to melt drip... ! ************************************* if(computeVegFlux)then unloading_melt = min(ratioDrip2Unloading*scalarCanopyLiqDrainage, scalarCanopyIce/dt) ! kg m-2 s-1 - if(scalarCanopyIce/dt < ratioDrip2Unloading*scalarCanopyLiqDrainage)then - unloadingDeriv = 1._rkind/dt - else ! unloadingDeriv = ratioDrip2Unloading*canopyLiqDrainageDeriv*(fLiq*scalarCanopyWat) - if(scalarCanopyLiq > scalarCanopyLiqMax)then !from vegLiqFlux last step - canopyLiqDrainageDeriv = scalarCanopyDrainageCoeff - else - canopyLiqDrainageDeriv = 0._rkind - endif - unloadingDeriv = -ratioDrip2Unloading*canopyLiqDrainageDeriv - dCanopyIce_dWat = dCanopyIce_dWat - ratioDrip2Unloading*canopyLiqDrainageDeriv*fLiq*dt - use_drip = .true. - endif else unloading_melt = 0._rkind - unloadingDeriv = 0._rkind end if scalarCanopyIce = scalarCanopyIce - unloading_melt*dt - if(.not.use_drip) dCanopyIce_dWat = (1._rkind - unloadingDeriv*dt)*dCanopyIce_dWat - dCanopyIce_dTk = (1._rkind - unloadingDeriv*dt)*dCanopyIce_dTk - - ! initialize - dCanopyIceIter_dWat = dCanopyIce_dWat - dCanopyIceIter_dTk = dCanopyIce_dTk - ! ***** ! compute the ice balance due to snowfall and unloading... ! ******************************************************** ! check for early returns @@ -260,11 +215,7 @@ subroutine canopySnow(& ! ** compute iteration increment flux = scalarSnowfall - scalarThroughfallSnow - scalarCanopySnowUnloading ! net flux (kg m-2 s-1) delS = (flux*dt - (scalarCanopyIceIter - scalarCanopyIce))/(1._rkind + (throughfallDeriv + unloadingDeriv)*dt) - ! ** compute derivatives of delS - ddelS_dWat = (((-throughfallDeriv - unloadingDeriv)*dt - 1._rkind)*dCanopyIceIter_dWat + dCanopyIce_dWat) & - /(1._rkind + (throughfallDeriv + unloadingDeriv)*dt) - ddelS_dTk = (((-throughfallDeriv - unloadingDeriv)*dt - 1._rkind)*dCanopyIceIter_dTk + dCanopyIce_dTk) & - /(1._rkind + (throughfallDeriv + unloadingDeriv)*dt) + ! ** check for convergence resMass = scalarCanopyIceIter - (scalarCanopyIce + flux*dt) if(abs(resMass) < convTolerMass)exit @@ -272,18 +223,14 @@ subroutine canopySnow(& if(iter==maxiter)then; err=20; message=trim(message)//'failed to converge [mass]'; return; end if ! ** update value scalarCanopyIceIter = scalarCanopyIceIter + delS - dCanopyIceIter_dWat = dCanopyIceIter_dWat + ddelS_dWat - dCanopyIceIter_dTk = dCanopyIceIter_dTk + ddelS_dTk end do ! iterating ! add the unloading associated with melt drip (kg m-2 s-1) scalarCanopySnowUnloading = scalarCanopySnowUnloading + unloading_melt - ! ***** - ! update mass of ice on the canopy (kg m-2) and derivatives + ! update mass of ice on the canopy (kg m-2) scalarCanopyIce = scalarCanopyIceIter - dCanopyIce_dWat = dCanopyIceIter_dWat - dCanopyIce_dTk = dCanopyIceIter_dTk + ! end association to variables in the data structure end associate diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 6f3814a41..43050b805 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -237,9 +237,6 @@ subroutine computJacob(& dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. temperature - ! canopy derivatives from adjusting the canopy temperature to account for new snow - dTkCanopyAdj_dTkCanopy => diag_data%var(iLookDIAG%dTkCanopyAdj_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in the adjusted temperature w.r.t. original temperature - dTkCanopyAdj_dCanWat => diag_data%var(iLookDIAG%dTkCanopyAdj_dCanWat )%dat(1) ,& ! intent(in): [dp ] derivative in the adjusted temperature w.r.t. canopy water ! derivatives in time mLayerdTemp_dt => deriv_data%var(iLookDERIV%mLayerdTemp_dt )%dat ,& ! intent(in): [dp(:)] timestep change in layer temperature scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt )%dat(1) ,& ! intent(in): [dp ] timestep change in canopy temperature @@ -972,16 +969,6 @@ subroutine computJacob(& end select ! type of matrix ! ********************************************************************************************************************************************************* - ! Add in the new-snow adjusted canopy temperature derivatives (for either type of matrix) - if(computeVegFlux)then - if(ixVegNrg/=integerMissing)then - if(ixVegHyd/=integerMissing)then - aJac(:,ixVegHyd) = aJac(:,ixVegHyd)+aJac(:,ixVegNrg)*dTkCanopyAdj_dCanWat - endif - aJac(:,ixVegNrg) = aJac(:,ixVegNrg)*dTkCanopyAdj_dTkCanopy - endif - endif - ! print the Jacobian if(globalPrintFlag)then select case(ixMatrix) diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index 3febecc11..96eec0c27 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -273,9 +273,6 @@ subroutine computJacobWithPrime(& dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. temperature - ! canopy derivatives from adjusting the canopy temperature to account for new snow - dTkCanopyAdj_dTkCanopy => diag_data%var(iLookDIAG%dTkCanopyAdj_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in the adjusted temperature w.r.t. original temperature - dTkCanopyAdj_dCanWat => diag_data%var(iLookDIAG%dTkCanopyAdj_dCanWat )%dat(1) ,& ! intent(in): [dp ] derivative in the adjusted temperature w.r.t. canopy water ! diagnostic variables scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg )%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg )%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) @@ -1020,16 +1017,6 @@ subroutine computJacobWithPrime(& end select ! type of matrix ! ********************************************************************************************************************************************************* - ! Add in the new-snow adjusted canopy temperature derivatives (for either type of matrix) - if(computeVegFlux)then - if(ixVegNrg/=integerMissing)then - if(ixVegHyd/=integerMissing)then - aJac(:,ixVegHyd) = aJac(:,ixVegHyd)+aJac(:,ixVegNrg)*dTkCanopyAdj_dCanWat - endif - aJac(:,ixVegNrg) = aJac(:,ixVegNrg)*dTkCanopyAdj_dTkCanopy - endif - endif - ! print the Jacobian if(globalPrintFlag)then select case(ixMatrix) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 005f593f2..ef4ff7c62 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -219,9 +219,6 @@ subroutine coupled_em(& type(var_dlength) :: prog_temp ! temporary model prognostic variables type(var_dlength) :: diag_temp ! temporary model diagnostic variables real(rkind),allocatable :: mLayerVolFracIceInit(:)! initial vector for volumetric fraction of ice (-) - ! canopy ice derivatives for canopy temperature adjustment - real(rkind) :: dCanopyIce_dWat ! derivative of canopy ice with canopy water - real(rkind) :: dCanopyIce_dTk ! derivative of canopy ice with canopy temperature ! check SWE real(rkind) :: oldSWE ! SWE at the start of the substep real(rkind) :: newSWE ! SWE at the end of the substep @@ -569,9 +566,6 @@ subroutine coupled_em(& diag_data, & ! intent(in): model diagnostic variables for a local HRU prog_data, & ! intent(inout): model prognostic variables for a local HRU flux_data, & ! intent(inout): model flux variables - ! output: derivatives - dCanopyIce_dWat, & ! intent(out): derivative of canopy ice with canopy water - dCanopyIce_dTk, & ! intent(out): derivative of canopy ice with canopy temperature ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if @@ -581,9 +575,6 @@ subroutine coupled_em(& call tempAdjust(& ! input: derived parameters canopyDepth, & ! intent(in): canopy depth (m) - ! input: derivatives - dCanopyIce_dWat, & ! intent(in): derivative of canopy ice with canopy water - dCanopyIce_dTk, & ! intent(in): derivative of canopy ice with canopy temperature ! input/output: data structures mpar_data, & ! intent(in): model parameters prog_data, & ! intent(inout): model prognostic variables for a local HRU @@ -591,10 +582,6 @@ subroutine coupled_em(& ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - else - ! no dependency of adjusted temperature since did not adjust temperature - diag_data%var(iLookDIAG%dTkCanopyAdj_dTkCanopy)%dat(1) = 1._rkind - diag_data%var(iLookDIAG%dTkCanopyAdj_dCanWat)%dat(1) = 0._rkind endif ! if computing fluxes over vegetation ! initialize drainage and throughfall diff --git a/build/source/engine/snow_utils.f90 b/build/source/engine/snow_utils.f90 index 18b88ac97..18c633cc2 100644 --- a/build/source/engine/snow_utils.f90 +++ b/build/source/engine/snow_utils.f90 @@ -38,7 +38,6 @@ module snow_utils_module public::fracliquid public::templiquid public::dFracLiq_dTk -public::d2FracLiq_dTk2 public::tcond_snow contains @@ -86,31 +85,6 @@ function dFracLiq_dTk(Tk,fc_param) dFracLiq_dTk = (fc_param*2._rkind*Tdim) / ( ( 1._rkind + Tdim**2_i4b)**2_i4b ) end function dFracLiq_dTk -! *********************************************************************************************************** -! public function d2FracLiq2_dTk2: differentiate the freezing curve twice -! *********************************************************************************************************** -function d2FracLiq_dTk2(Tk,fc_param) - implicit none - ! dummies - real(rkind),intent(in) :: Tk ! temperature (K) - real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) - real(rkind) :: d2FracLiq_dTk2 ! differentiate the freezing curve twice (K-2) - ! locals - real(rkind) :: Tdep ! temperature depression (K) - real(rkind) :: Tdim ! dimensionless temperature (-) - real(rkind) :: fPart1,fPart2 ! different parts of a function - real(rkind) :: dPart1,dPart2 ! derivatives for different parts of a function - ! compute local variables (just to make things more efficient) - Tdep = Tfreeze - min(Tk,Tfreeze) - Tdim = fc_param*Tdep - fPart1 = fc_param*2._rkind*Tdim - fPart2 = 1._rkind/( ( 1._rkind + Tdim**2_i4b)**2_i4b ) - ! differentiate the freezing curve w.r.t temperature twice - dPart1 = fc_param*2._rkind*Tdim/Tk - dPart2 = (fc_param*4._rkind*Tdim) / ( ( 1._rkind + Tdim**2_i4b)**2_i4b ) - d2FracLiq_dTk2 = fPart1*dPart2 + dPart1*fPart2 -end function d2FracLiq_dTk2 - ! *********************************************************************************************************** ! public subroutine tcond_snow: compute thermal conductivity of snow ! *********************************************************************************************************** diff --git a/build/source/engine/tempAdjust.f90 b/build/source/engine/tempAdjust.f90 index 5915f69fa..05567d049 100644 --- a/build/source/engine/tempAdjust.f90 +++ b/build/source/engine/tempAdjust.f90 @@ -52,9 +52,6 @@ module tempAdjust_module subroutine tempAdjust(& ! input: derived parameters canopyDepth, & ! intent(in): canopy depth (m) - ! input: derivatives - dCanopyIce_dWat, & ! intent(in): derivative of canopy ice with canopy water - dCanopyIce_dTk, & ! intent(in): derivative of canopy ice with canopy temperature ! input/output: data structures mpar_data, & ! intent(in): model parameters prog_data, & ! intent(inout): model prognostic variables for a local HRU @@ -65,14 +62,10 @@ subroutine tempAdjust(& ! utility routines USE snow_utils_module,only:fracliquid ! compute fraction of liquid water USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) - USE snow_utils_module,only:d2FracLiq_dTk2 ! differentiate the freezing curve twice w.r.t. temperature (snow) implicit none ! ------------------------------------------------------------------------------------------------ ! input: derived parameters real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) - ! input: derivatives - real(rkind),intent(in) :: dCanopyIce_dWat ! derivative of canopy ice with canopy water - real(rkind),intent(in) :: dCanopyIce_dTk ! derivative of canopy ice with canopy temperature ! input/output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_dlength),intent(inout) :: prog_data ! model prognostic variables for a local HRU @@ -93,13 +86,6 @@ subroutine tempAdjust(& real(rkind),parameter :: resNrgToler=0.1_rkind ! tolerance for the energy residual (J m-3) real(rkind) :: f1,f2,x1,x2,fTry,xTry,fDer,xInc ! iteration variables logical(lgt) :: fBis ! .true. if bisection - ! local variables for computing derivatives - real(rkind) :: dCp_dWat ! derivative of heat capacity with canopy water - real(rkind) :: dCp_dTk ! derivative of heat capacity with canopy temperature - real(rkind) :: dxTry_dWat,dx2_dWat,dtempMin_dWat,dtempMax_dWat ! derivative of iteration temperature and brackets w.r.t. canopy water - real(rkind) :: dxTry_dTk, dx2_dTk, dtempMin_dTk, dtempMax_dTk ! derivative of iteration temperature and brackets w.r.t. canopy temperature - real(rkind) :: df1_dWat,dfTry_dWat,dfDer_dWat ! derivative of iteration variables w.r.t. canopy water - real(rkind) :: df1_dTk, dfTry_dTk, dfDer_dTk ! derivative of iteration variables w.r.t. canopy temperature ! ------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='tempAdjust/' @@ -115,11 +101,7 @@ subroutine tempAdjust(& scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1), & ! intent(inout): [dp] mass of ice on the vegetation canopy (kg m-2) scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1), & ! intent(inout): [dp] temperature of the vegetation canopy (K) ! diagnostic variables (output) - scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1), & ! intent(out): [dp] volumetric heat capacity of the vegetation (J m-3 K-1) - ! canopy derivatives from adjusting the canopy temperature - dTkCanopyAdj_dTkCanopy => diag_data%var(iLookDIAG%dTkCanopyAdj_dTkCanopy)%dat(1), & ! intent(in): [dp] derivative in the adjusted temperature w.r.t. original temperature - dTkCanopyAdj_dCanWat => diag_data%var(iLookDIAG%dTkCanopyAdj_dCanWat)%dat(1) & ! intent(in): [dp] derivative in the adjusted temperature w.r.t. canopy water - ! output: derivatives + scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) & ! intent(out): [dp] volumetric heat capacity of the vegetation (J m-3 K-1) ) ! associate variables in the data structures ! ----------------------------------------------------------------------------------------------------------------------------------------------------- ! ** preliminaries @@ -139,14 +121,6 @@ subroutine tempAdjust(& Cp_water*scalarCanopyLiq/canopyDepth + & ! liquid water component Cp_ice*scalarCanopyIce/canopyDepth ! ice component - ! heat capacity derivatives - dCp_dWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth - if(scalarCanopyTemp < Tfreeze)then - dCp_dTk = (-Cp_ice + Cp_water) * fLiq * scalarCanopyLiq/canopyDepth ! no derivative in air - else - dCp_dTk = 0._rkind - endif - ! compute the energy required to melt-freeze the water to the current canopy temperature (J m-3) nrgMeltFreeze = LH_fus*(scalarCanopyIceOld - scalarCanopyIce)/canopyDepth @@ -154,36 +128,23 @@ subroutine tempAdjust(& ! ** get ready for iterating - ! compute initial function and derivatives + ! compute initial function x1 = scalarCanopyTemp f1 = nrgMeltFreeze - df1_dWat = LH_fus*((1._rkind - fLiq) - dCanopyIce_dWat )/canopyDepth - df1_dTk = LH_fus*( -dFracLiq_dTk(scalarCanopyTemp,snowfrz_scale) - dCanopyIce_dTk )/canopyDepth fDer = resNrgDer(x1,scalarBulkVolHeatCapVeg,snowfrz_scale) - dfDer_dWat = dCp_dWat + dFracLiq_dTk(x1,snowfrz_scale)*LH_fus/canopyDepth - dfDer_dTk = dCp_dTk + scalarCanopyWat*d2FracLiq_dTk2(x1,snowfrz_scale)*LH_fus/canopyDepth ! compute new function based on newton step from the first function x2 = x1 + f1 / fDer f2 = resNrgFunc(x2,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) - ! compute derivatives - dx2_dWat = df1_dWat/fDer - f1/(dfDer_dWat**2_i4b) - dx2_dTk = 1._rkind + df1_dTk/fDer - if(dfDer_dTk.ne.0._rkind) dx2_dTk = dx2_dTk - f1/(dfDer_dTk**2_i4b) !if dCp_dTk = scalarCanopyWat = 0 will be 0 - - ! ensure that we bracket the root and recompute x2 and derivatives if not + + ! ensure that we bracket the root and recompute x2 if not if(f1*f2 > 0._rkind)then xInc = f1 / fDer x2 = 1._rkind - dx2_dWat = 0._rkind - dx2_dTk = 0._rkind do iter=1,maxiter ! successively expand limit in order to bracket the root x2 = x1 + sign(x2,xInc)*2._rkind f2 = resNrgFunc(x2,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) - ! compute derivatives - dx2_dWat = 0._rkind - dx2_dTk = 1._rkind if(f1*f2 < 0._rkind)exit ! check that we bracketed the root (should get here in just a couple of expansions) if(iter==maxiter)then @@ -193,21 +154,13 @@ subroutine tempAdjust(& end do ! trying to bracket the root end if ! first check that we bracketed the root - ! define initial constraints and derivatives + ! define initial constraints if(x1 < x2)then tempMin = x1 tempMax = x2 - dtempMin_dWat = 0._rkind - dtempMin_dTk = 1._rkind - dtempMax_dWat = dx2_dWat - dtempMax_dTk = dx2_dTk else tempMin = x2 tempMax = x1 - dtempMin_dWat = dx2_dWat - dtempMin_dTk = dx2_dTk - dtempMax_dWat = 0._rkind - dtempMax_dTk = 1._rkind end if ! get starting trial @@ -216,64 +169,27 @@ subroutine tempAdjust(& fTry = resNrgFunc(xTry,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) fDer = resNrgDer(xTry,scalarBulkVolHeatCapVeg,snowfrz_scale) - ! compute derivatives - dxTry_dWat = 0.5_rkind*(dtempMin_dWat + dtempMax_dWat) - dxTry_dTk = 0.5_rkind*(dtempMin_dTk + dtempMax_dTk) - ! xIce = (1._rkind - fracliquid(xTry,snowfrz_scale))*scalarCanopyWat - ! fTry = -scalarBulkVolHeatCapVeg*(xTry - scalarCanopyTemp) + LH_fus*(xIce - scalarCanopyIce)/canopyDepth - dfTry_dWat = -dCp_dWat*(xTry - scalarCanopyTemp) - scalarBulkVolHeatCapVeg*dxTry_dWat & - + LH_fus* ( 1._rkind - fracliquid(xTry,snowfrz_scale) & - - dFracLiq_dTk(xTry,snowfrz_scale)*scalarCanopyWat*dxTry_dWat - dCanopyIce_dWat )/canopyDepth - dfTry_dTk = -dCp_dTk*(xTry - scalarCanopyTemp) + scalarBulkVolHeatCapVeg*dxTry_dTk & - + LH_fus* ( -dFracLiq_dTk(xTry,snowfrz_scale)*scalarCanopyWat*dxTry_dTk - dCanopyIce_dTk )/canopyDepth - ! fDer = scalarBulkVolHeatCapVeg + scalarCanopyWat*dFracLiq_dTk(xTry,snowfrz_scale)*LH_fus/canopyDepth - dfDer_dWat = dCp_dWat + ( dFracLiq_dTk(xTry,snowfrz_scale) & - + scalarCanopyWat*d2FracLiq_dTk2(xTry,snowfrz_scale)*dxTry_dWat )*LH_fus/canopyDepth - dfDer_dTk = dCp_dTk + scalarCanopyWat*d2FracLiq_dTk2(xTry,snowfrz_scale)*dxTry_dTk*LH_fus/canopyDepth - ! ----------------------------------------------------------------------------------------------------------------------------------------------------- ! iterate do iter=1,maxiter if(xTry <= tempMin .or. xTry >= tempMax)then ! bisect if out of range xTry = 0.5_rkind*(tempMin + tempMax) ! new value fBis = .true. - ! compute derivatives - dxTry_dWat = 0.5_rkind*(dtempMin_dWat + dtempMax_dWat) - dxTry_dTk = 0.5_rkind*(dtempMin_dTk + dtempMax_dTk) else ! value in range; use the newton step xInc = fTry/fDer xTry = xTry + xInc fBis = .false. - ! compute derivatives - dxTry_dWat = dxTry_dWat+ dfTry_dWat/fDer - fTry/(dfDer_dWat**2_i4b) - dxTry_dTk = dxTry_dTk + dfTry_dTk/fDer - fTry/(dfDer_dTk**2_i4b) end if ! (switch between bi-section and newton) - ! compute new function and derivatives + ! compute new function fTry = resNrgFunc(xTry,scalarCanopyTemp,scalarBulkVolHeatCapVeg,snowfrz_scale) fDer = resNrgDer(xTry,scalarBulkVolHeatCapVeg,snowfrz_scale) - dfTry_dWat = -dCp_dWat*(xTry - scalarCanopyTemp) - scalarBulkVolHeatCapVeg*dxTry_dWat & - + LH_fus* ( 1._rkind - fracliquid(xTry,snowfrz_scale) & - - dFracLiq_dTk(xTry,snowfrz_scale)*scalarCanopyWat*dxTry_dWat - dCanopyIce_dWat )/canopyDepth - dfTry_dTk = -dCp_dTk*(xTry - scalarCanopyTemp) + scalarBulkVolHeatCapVeg*dxTry_dTk & - + LH_fus* ( -dFracLiq_dTk(xTry,snowfrz_scale)*scalarCanopyWat*dxTry_dTk - dCanopyIce_dTk )/canopyDepth - dfDer_dWat = dCp_dWat + ( dFracLiq_dTk(xTry,snowfrz_scale) & - + scalarCanopyWat*d2FracLiq_dTk2(xTry,snowfrz_scale)*dxTry_dWat )*LH_fus/canopyDepth - dfDer_dTk = dCp_dTk + scalarCanopyWat*d2FracLiq_dTk2(xTry,snowfrz_scale)*dxTry_dTk*LH_fus/canopyDepth - - ! update limits and derivatives + + ! update limits if(fTry < 0._rkind)then tempMax = min(xTry,tempMax) - if(xTrytempMin)then - dtempMin_dWat = dxTry_dWat - dtempMin_dTk = dxTry_dTk - endif end if ! check the functions at the limits (should be of opposing sign) @@ -303,15 +219,6 @@ subroutine tempAdjust(& scalarCanopyIce = (1._rkind - fracliquid(xTry,snowfrz_scale))*scalarCanopyWat scalarCanopyLiq = scalarCanopyWat - scalarCanopyIce - ! update derivatives - dTkCanopyAdj_dCanWat = dxTry_dWat - dTkCanopyAdj_dTkCanopy = dxTry_dTk - - ! update bulk heat capacity - scalarBulkVolHeatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component - Cp_water*scalarCanopyLiq/canopyDepth + & ! liquid water component - Cp_ice*scalarCanopyIce/canopyDepth ! ice component - ! end association to variables in the data structure end associate From cd5002c7b8be020d63d31485e8fd393fb8501b43 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 6 Jul 2023 23:21:27 +0900 Subject: [PATCH 0794/1472] Jacobian was writing over a term, that was the problem. Also turned off updating heat capacity. --- build/source/engine/computJacob.f90 | 20 ++++++++++---------- build/source/engine/computJacobWithPrime.f90 | 16 ++++++++-------- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/eval8summaWithPrime.f90 | 2 +- build/source/engine/tempAdjust.f90 | 2 +- build/source/engine/varSubstep.f90 | 20 ++++++++++---------- 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 43050b805..074101387 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -577,11 +577,11 @@ subroutine computJacob(& ! - include derivatives w.r.t. ground evaporation if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer, assume here that kl>=4 if(computeVegFlux)then - if(ixCasNrg/=integerMissing) aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) - if(ixVegHyd/=integerMissing) aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 + if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) + aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) ! dVol/dT (K-1) + if(ixVegHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) + aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) ! dVol/dLiq (kg m-2)-1 endif - if(ixTopNrg/=integerMissing) aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) + if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(ixOffDiag(ixTopHyd,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) endif ! - only include banded terms for derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) in banded structure @@ -635,7 +635,7 @@ subroutine computJacob(& endif endif ! (if there are state variables for both water and energy in the soil domain) - + ! ********************************************************************************************************************************************************* ! * PART 2: FULL MATRIX ! ********************************************************************************************************************************************************* @@ -913,11 +913,11 @@ subroutine computJacob(& ! - include derivatives of energy w.r.t. ground evaporation if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer if(computeVegFlux)then - if(ixCasNrg/=integerMissing) aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) - if(ixVegNrg/=integerMissing) aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) - if(ixVegHyd/=integerMissing) aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 + if(ixCasNrg/=integerMissing) aJac(ixTopHyd,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) + if(ixVegNrg/=integerMissing) aJac(ixTopHyd,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) + aJac(ixTopHyd,ixVegNrg) ! dVol/dT (K-1) + if(ixVegHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) + aJac(ixTopHyd,ixVegHyd) ! dVol/dLiq (kg m-2)-1 endif - if(ixTopNrg/=integerMissing) aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) + if(ixTopNrg/=integerMissing) aJac(ixTopHyd,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(ixTopHyd,ixTopNrg) ! dVol/dT (K-1) endif ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) @@ -1086,7 +1086,7 @@ function ixOffDiag(jState,iState) integer(i4b),intent(in) :: jState ! off-diagonal state integer(i4b),intent(in) :: iState ! diagonal state integer(i4b),parameter :: ixDiag=kl+ku+1 ! index for the diagonal, the offset in the band Jacobian matrix - integer(i4b) :: ixOffDiag ! off-diagonal index in gthe band-diagonal matrix + integer(i4b) :: ixOffDiag ! off-diagonal index in the band-diagonal matrix ixOffDiag = ixDiag + jState - iState end function ixOffDiag diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index 96eec0c27..b893bff2b 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -621,11 +621,11 @@ subroutine computJacobWithPrime(& ! - include derivatives w.r.t. ground evaporation if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer, assume here that kl>=4 if(computeVegFlux)then - if(ixCasNrg/=integerMissing) aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) - if(ixVegHyd/=integerMissing) aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 + if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) + aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) ! dVol/dT (K-1) + if(ixVegHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) + aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) ! dVol/dLiq (kg m-2)-1 endif - if(ixTopNrg/=integerMissing) aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) + if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(ixOffDiag(ixTopHyd,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) endif ! - only include banded terms for derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) in banded structure @@ -960,11 +960,11 @@ subroutine computJacobWithPrime(& ! - include derivatives of energy w.r.t. ground evaporation if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer if(computeVegFlux)then - if(ixCasNrg/=integerMissing) aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) - if(ixVegNrg/=integerMissing) aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) - if(ixVegHyd/=integerMissing) aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) ! dVol/dLiq (kg m-2)-1 + if(ixCasNrg/=integerMissing) aJac(ixTopHyd,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) + if(ixVegNrg/=integerMissing) aJac(ixTopHyd,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) + aJac(ixTopHyd,ixVegNrg) ! dVol/dT (K-1) + if(ixVegHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanWat/iden_water) + aJac(ixTopHyd,ixVegHyd) ! dVol/dLiq (kg m-2)-1 endif - if(ixTopNrg/=integerMissing) aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) + if(ixTopNrg/=integerMissing) aJac(ixTopHyd,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(ixTopHyd,ixTopNrg) ! dVol/dT (K-1) endif ! - include derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 9bbcfe0fe..7fcb2aaf6 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -212,7 +212,7 @@ subroutine eval8summa(& character(LEN=256) :: cmessage ! error message of downwind routine real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil - logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step + logical(lgt),parameter :: updateCp=.false. ! flag to indicate if we update Cp at each step logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m ! -------------------------------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index c6fa29486..9889589bc 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -236,7 +236,7 @@ subroutine eval8summaWithPrime(& character(LEN=256) :: cmessage ! error message of downwind routine real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil - logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step + logical(lgt),parameter :: updateCp=.false. ! flag to indicate if we update Cp at each step logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m ! -------------------------------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/tempAdjust.f90 b/build/source/engine/tempAdjust.f90 index 05567d049..9fe8acd3c 100644 --- a/build/source/engine/tempAdjust.f90 +++ b/build/source/engine/tempAdjust.f90 @@ -214,7 +214,7 @@ subroutine tempAdjust(& end do ! iterating ! ----------------------------------------------------------------------------------------------------------------------------------------------------- - ! update state variables + ! update state variables, but not heat capacity since used heat capacity to get these values scalarCanopyTemp = xTry scalarCanopyIce = (1._rkind - fracliquid(xTry,snowfrz_scale))*scalarCanopyWat scalarCanopyLiq = scalarCanopyWat - scalarCanopyIce diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 5eca81550..4846a1e5a 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -972,16 +972,16 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! check the mass balance fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial - write(*,'(a,1x,f20.10)') 'dt = ', dt - write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 - write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 - write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt - write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt - write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt - write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - write(*,'(a,1x,f20.10)') 'liqError = ', liqError - if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues + if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues + !write(*,'(a,1x,f20.10)') 'dt = ', dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + !write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 + !write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 + !write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt + !write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt + !write(*,'(a,1x,f20.10)') 'liqError = ', liqError waterBalanceError = .true. return endif ! if there is a water balance error From fc1a517d8d2b0f7f0f5c290f0055d7c7a44e4cd1 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 6 Jul 2023 23:32:15 +0900 Subject: [PATCH 0795/1472] extra arguments in canopySnow --- build/source/engine/canopySnow.f90 | 3 --- 1 file changed, 3 deletions(-) diff --git a/build/source/engine/canopySnow.f90 b/build/source/engine/canopySnow.f90 index a6675e87a..50ae2b946 100644 --- a/build/source/engine/canopySnow.f90 +++ b/build/source/engine/canopySnow.f90 @@ -90,7 +90,6 @@ subroutine canopySnow(& real(rkind),parameter :: valueMissing=-9999._rkind ! missing value integer(i4b) :: iter ! iteration index integer(i4b),parameter :: maxiter=50 ! maximum number of iterations - real(rkind) :: scalarCanopyWat ! total canopy water (kg m-2) real(rkind) :: unloading_melt ! unloading associated with canopy drip (kg m-2 s-1) real(rkind) :: airtemp_degC ! value of air temperature in degrees Celcius real(rkind) :: leafScaleFactor ! scaling factor for interception based on temperature (-) @@ -99,7 +98,6 @@ subroutine canopySnow(& real(rkind) :: throughfallDeriv ! derivative in throughfall flux w.r.t. canopy storage (s-1) real(rkind) :: unloadingDeriv ! derivative in unloading flux w.r.t. canopy storage (s-1) real(rkind) :: scalarCanopyIceIter ! trial value for mass of ice on the vegetation canopy (kg m-2) (kg m-2) - real(rkind) :: fLiq ! fraction of liquid water (-) real(rkind) :: flux ! net flux (kg m-2 s-1) real(rkind) :: delS ! change in storage (kg m-2) real(rkind) :: resMass ! residual in mass equation (kg m-2) @@ -118,7 +116,6 @@ subroutine canopySnow(& ! model forcing data scalarAirtemp => forc_data%var(iLookFORCE%airtemp), & ! intent(in): [dp] air temperature (K) ! model parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! intent(in): [dp] scaling factor for snow freezing curve (K) refInterceptCapSnow => mpar_data%var(iLookPARAM%refInterceptCapSnow)%dat(1), & ! intent(in): [dp] reference canopy interception capacity for snow per unit leaf area (kg m-2) ratioDrip2Unloading => mpar_data%var(iLookPARAM%ratioDrip2Unloading)%dat(1), & ! intent(in): [dp] ratio of canopy drip to snow unloading (-) snowUnloadingCoeff => mpar_data%var(iLookPARAM%snowUnloadingCoeff)%dat(1), & ! intent(in): [dp] time constant for unloading of snow from the forest canopy (s-1) From 5586c9a879e8dc67da5f12ed9defb671db7c2e0e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 6 Jul 2023 23:44:27 +0900 Subject: [PATCH 0796/1472] add updateCp for sundials paper --- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/eval8summaWithPrime.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 7fcb2aaf6..0b99f4ecf 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -212,7 +212,7 @@ subroutine eval8summa(& character(LEN=256) :: cmessage ! error message of downwind routine real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil - logical(lgt),parameter :: updateCp=.false. ! flag to indicate if we update Cp at each step + logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m ! -------------------------------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 9889589bc..c6fa29486 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -236,7 +236,7 @@ subroutine eval8summaWithPrime(& character(LEN=256) :: cmessage ! error message of downwind routine real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil - logical(lgt),parameter :: updateCp=.false. ! flag to indicate if we update Cp at each step + logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m ! -------------------------------------------------------------------------------------------------------------------------------- From 17ddb03be2098585d67992f4fe32ba7ae7e29028 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 6 Jul 2023 23:46:22 +0900 Subject: [PATCH 0797/1472] turn off updateCp to agree with previous Summa --- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/eval8summaWithPrime.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 0b99f4ecf..7fcb2aaf6 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -212,7 +212,7 @@ subroutine eval8summa(& character(LEN=256) :: cmessage ! error message of downwind routine real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil - logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step + logical(lgt),parameter :: updateCp=.false. ! flag to indicate if we update Cp at each step logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m ! -------------------------------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index c6fa29486..9889589bc 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -236,7 +236,7 @@ subroutine eval8summaWithPrime(& character(LEN=256) :: cmessage ! error message of downwind routine real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil - logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step + logical(lgt),parameter :: updateCp=.false. ! flag to indicate if we update Cp at each step logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m ! -------------------------------------------------------------------------------------------------------------------------------- From 14cf29e06c17a545a5d2f9d9558653bbcf38ac96 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 10 Jul 2023 13:50:51 -0500 Subject: [PATCH 0798/1472] spaces --- build/source/engine/computJacobWithPrime.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index b893bff2b..28e346fcf 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -377,7 +377,7 @@ subroutine computJacobWithPrime(& ! * energy fluxes with the canopy air space (J m-3 K-1) if(ixCasNrg/=integerMissing)then - aJac(ixDiag, ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) * cj + aJac(ixDiag, ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) * cj if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixCasNrg,ixVegNrg),ixVegNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanopyTemp) if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixCasNrg,ixTopNrg),ixTopNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dGroundTemp) endif From 714b09c38b0a93c139b40ec27f45f71b2ead21a9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 10 Jul 2023 16:35:16 -0500 Subject: [PATCH 0799/1472] spaces --- build/source/engine/var_derive.f90 | 77 +++++++++++------------------- 1 file changed, 29 insertions(+), 48 deletions(-) diff --git a/build/source/engine/var_derive.f90 b/build/source/engine/var_derive.f90 index 719f9c385..d5b12023f 100644 --- a/build/source/engine/var_derive.f90 +++ b/build/source/engine/var_derive.f90 @@ -26,18 +26,14 @@ module var_derive_module ! derived types to define the data structures USE data_types,only:var_ilength ! x%var(:)%dat (i4b) USE data_types,only:var_dlength ! x%var(:)%dat (rkind) - ! named variables for snow and soil USE globalData,only:iname_snow ! named variables for snow USE globalData,only:iname_soil ! named variables for soil - ! named variables USE globalData,only:data_step ! time step of forcing data - ! named variables USE var_lookup,only:iLookPARAM,iLookINDEX,iLookPROG,iLookDIAG,iLookFLUX ! HRU: named variables for structure elements USE var_lookup,only:iLookBVAR,iLookBPAR ! GRU: named variables for structure elements - ! model decision structures USE globalData,only:model_decisions ! model decision structure USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure @@ -49,8 +45,8 @@ module var_derive_module ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: & - bigBucket, & ! a big bucket (lumped aquifer model) - noExplicit ! no explicit groundwater parameterization + bigBucket, & ! a big bucket (lumped aquifer model) + noExplicit ! no explicit groundwater parameterization ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: & @@ -58,9 +54,9 @@ module var_derive_module powerLaw_profile ! power-law profile ! look-up values for the sub-grid routing method -USE mDecisions_module,only: & - timeDelay,& ! time-delay histogram - qInstant ! instantaneous routing +USE mDecisions_module,only: & + timeDelay, & ! time-delay histogram + qInstant ! instantaneous routing ! privacy implicit none @@ -122,12 +118,6 @@ subroutine calcHeight(& iLayerHeight(iLayer) = iLayerHeight(iLayer-1) + mLayerDepth(iLayer) end do ! (looping through layers) - !print*, 'layerType = ', layerType - !print*, 'mLayerDepth = ', mLayerDepth - !print*, 'mLayerHeight = ', mLayerHeight - !print*, 'iLayerHeight = ', iLayerHeight - !print*, '************** ' - ! end association to variables in the data structure end associate @@ -149,10 +139,10 @@ subroutine rootDensty(mpar_data,indx_data,prog_data,diag_data,err,message) character(*),intent(out) :: message ! error message ! declare local variables integer(i4b) :: iLayer ! loop through layers - real(rkind) :: fracRootLower ! fraction of the rooting depth at the lower interface - real(rkind) :: fracRootUpper ! fraction of the rooting depth at the upper interface - real(rkind), parameter :: rootTolerance = 0.05_rkind ! tolerance for error in doubleExp rooting option - real(rkind) :: error ! machine precision error in rooting distribution + real(rkind) :: fracRootLower ! fraction of the rooting depth at the lower interface + real(rkind) :: fracRootUpper ! fraction of the rooting depth at the upper interface + real(rkind), parameter :: rootTolerance = 0.05_rkind ! tolerance for error in doubleExp rooting option + real(rkind) :: error ! machine precision error in rooting distribution ! initialize error control err=0; message='rootDensty/' @@ -178,9 +168,6 @@ subroutine rootDensty(mpar_data,indx_data,prog_data,diag_data,err,message) ) ! end associate ! ---------------------------------------------------------------------------------- - !print*, 'nSnow = ', nSnow - !print*, 'nLayers = ', nLayers - ! compute the fraction of roots in each soil layer do iLayer=nSnow+1,nLayers @@ -203,8 +190,6 @@ subroutine rootDensty(mpar_data,indx_data,prog_data,diag_data,err,message) else mLayerRootDensity(iLayer-nSnow) = 0._rkind end if - !write(*,'(a,10(f11.5,1x))') 'mLayerRootDensity(iLayer-nSnow), fracRootUpper, fracRootLower = ', & - ! mLayerRootDensity(iLayer-nSnow), fracRootUpper, fracRootLower ! ** option 2: double expoential profile of Zeng et al. (JHM 2001) case(doubleExp) @@ -213,9 +198,7 @@ subroutine rootDensty(mpar_data,indx_data,prog_data,diag_data,err,message) fracRootUpper = 1._rkind - 0.5_rkind*(exp(-iLayerHeight(iLayer )*rootScaleFactor1) + exp(-iLayerHeight(iLayer )*rootScaleFactor2) ) ! compute the root density mLayerRootDensity(iLayer-nSnow) = fracRootUpper - fracRootLower - !write(*,'(a,10(f11.5,1x))') 'mLayerRootDensity(iLayer-nSnow), fracRootUpper, fracRootLower = ', & - ! mLayerRootDensity(iLayer-nSnow), fracRootUpper, fracRootLower - + ! ** check case default; err=20; message=trim(message)//'unable to identify option for rooting profile'; return @@ -274,8 +257,8 @@ subroutine satHydCond(mpar_data,indx_data,prog_data,flux_data,err,message) character(*),intent(out) :: message ! error message ! declare local variables integer(i4b) :: iLayer ! loop through layers - real(rkind) :: ifcDepthScaleFactor ! depth scaling factor (layer interfaces) - real(rkind) :: midDepthScaleFactor ! depth scaling factor (layer midpoints) + real(rkind) :: ifcDepthScaleFactor ! depth scaling factor (layer interfaces) + real(rkind) :: midDepthScaleFactor ! depth scaling factor (layer midpoints) ! initialize error control err=0; message='satHydCond/' ! ---------------------------------------------------------------------------------- @@ -368,8 +351,6 @@ subroutine satHydCond(mpar_data,indx_data,prog_data,flux_data,err,message) mLayerSatHydCondMP(iLayer-nSnow) = mLayerSatHydCond(iLayer-nSnow) endif ! if mLayerSatHydCondMP < mLayerSatHydCond endif ! if iLayer>0 - !if(iLayer > nSnow)& ! avoid layer 0 - ! write(*,'(a,1x,i4,1x,2(f11.5,1x,e20.10,1x))') 'satHydCond: ', iLayer, mLayerHeight(iLayer), mLayerSatHydCond(iLayer-nSnow), iLayerHeight(iLayer), iLayerSatHydCond(iLayer-nSnow) end do ! looping through soil layers end associate @@ -382,24 +363,24 @@ end subroutine satHydCond ! ********************************************************************************************************** subroutine fracFuture(bpar_data,bvar_data,err,message) ! external functions - USE soil_utils_module,only:gammp ! compute the cumulative probabilty based on the Gamma distribution + USE soil_utils_module,only:gammp ! compute the cumulative probabilty based on the Gamma distribution implicit none ! input variables - real(rkind),intent(in) :: bpar_data(:) ! vector of basin-average model parameters + real(rkind),intent(in) :: bpar_data(:) ! vector of basin-average model parameters ! output variables - type(var_dlength),intent(inout) :: bvar_data ! data structure of basin-average model variables - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + type(var_dlength),intent(inout) :: bvar_data ! data structure of basin-average model variables + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! internal - real(rkind) :: dt ! data time step (s) - integer(i4b) :: nTDH ! number of points in the time-delay histogram - integer(i4b) :: iFuture ! index in time delay histogram - real(rkind) :: tFuture ! future time (end of step) - real(rkind) :: pSave ! cumulative probability at the start of the step - real(rkind) :: cumProb ! cumulative probability at the end of the step - real(rkind) :: sumFrac ! sum of runoff fractions in all steps - real(rkind),parameter :: tolerFrac=0.01_rkind ! tolerance for missing fractional runoff by truncating histogram + real(rkind) :: dt ! data time step (s) + integer(i4b) :: nTDH ! number of points in the time-delay histogram + integer(i4b) :: iFuture ! index in time delay histogram + real(rkind) :: tFuture ! future time (end of step) + real(rkind) :: pSave ! cumulative probability at the start of the step + real(rkind) :: cumProb ! cumulative probability at the end of the step + real(rkind) :: sumFrac ! sum of runoff fractions in all steps + real(rkind),parameter :: tolerFrac=0.01_rkind ! tolerance for missing fractional runoff by truncating histogram ! initialize error control err=0; message='fracFuture/' ! ---------------------------------------------------------------------------------- @@ -442,8 +423,8 @@ subroutine fracFuture(bpar_data,bvar_data,err,message) do iFuture = 1,nTDH ! get weight for a given bin tFuture = real(iFuture, kind(dt))*dt ! future time (end of step) - cumProb = gammp(routingGammaShape,tFuture/routingGammaScale) ! cumulative probability at the end of the step - fractionFuture(iFuture) = max(0._rkind, cumProb - pSave) ! fraction of runoff in the current step + cumProb = gammp(routingGammaShape,tFuture/routingGammaScale) ! cumulative probability at the end of the step + fractionFuture(iFuture) = max(0._rkind, cumProb - pSave) ! fraction of runoff in the current step pSave = cumProb ! save the cumulative probability for use in the next step !write(*,'(a,1x,i4,1x,3(f20.10,1x))') trim(message), iFuture, tFuture, cumProb, fractionFuture(iFuture) ! set remaining bins to zero @@ -494,8 +475,8 @@ subroutine v_shortcut(mpar_data,diag_data,err,message) ! associate variables in data structure associate(& ! associate values in the parameter structures - vGn_n =>mpar_data%var(iLookPARAM%vGn_n)%dat, & ! van Genutchen "n" parameter (-) - vGn_m =>diag_data%var(iLookDIAG%scalarVGn_m)%dat & ! van Genutchen "m" parameter (-) + vGn_n =>mpar_data%var(iLookPARAM%vGn_n)%dat, & ! van Genutchen "n" parameter (-) + vGn_m =>diag_data%var(iLookDIAG%scalarVGn_m)%dat & ! van Genutchen "m" parameter (-) ) ! end associate ! ---------------------------------------------------------------------------------- From 51079f48e3f2b69a51d939084af01faa2ac92391 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 14 Jul 2023 15:34:45 -0500 Subject: [PATCH 0800/1472] separating Jacobian if statements for non-existing indices and fixing some spaces --- build/source/engine/computFlux.f90 | 4 ++-- build/source/engine/computJacob.f90 | 22 ++++++++++++-------- build/source/engine/computJacobWithPrime.f90 | 22 ++++++++++++-------- build/source/engine/getVectorz.f90 | 1 - build/source/engine/soilLiqFlx.f90 | 3 +-- build/source/engine/summaSolve4numrec.f90 | 4 ++-- 6 files changed, 31 insertions(+), 25 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 7d4172ca5..a88aa71bf 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -363,12 +363,12 @@ subroutine computFlux(& dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below dq_dNrgStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dNrgStateLayerSurfVec )%dat ,& ! intent(out): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers ! derivatives in soil transpiration w.r.t. canopy state variables - mLayerdTrans_dTCanair => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanair )%dat ,& !intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + mLayerdTrans_dTCanair => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanair )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature mLayerdTrans_dTCanopy => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanopy )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy temperature mLayerdTrans_dTGround => deriv_data%var(iLookDERIV%mLayerdTrans_dTGround )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. ground temperature mLayerdTrans_dCanWat => deriv_data%var(iLookDERIV%mLayerdTrans_dCanWat )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy total water ! derivatives in aquifer transpiration w.r.t. canopy state variables - dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair )%dat(1) ,& !intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature dAquiferTrans_dTCanopy => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanopy )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy temperature dAquiferTrans_dTGround => deriv_data%var(iLookDERIV%dAquiferTrans_dTGround )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. ground temperature dAquiferTrans_dCanWat => deriv_data%var(iLookDERIV%dAquiferTrans_dCanWat )%dat(1) & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy total water diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 074101387..8c1dca2ef 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -496,15 +496,18 @@ subroutine computJacob(& if(computeBaseflow .and. nSoilOnlyHyd==nSoil)then do pLayer=1,nSoil qState = ixSoilOnlyHyd(pLayer) ! hydrology state index within the state subset - if(qState/=integerMissing .and. (qstate - watState <= ku) .and. (watState - qstate <= kl)) & - aJac(ixOffDiag(watState,qState),qState) = (dt/mLayerDepth(jLayer))*dBaseflow_dMatric(iLayer,pLayer) + aJac(ixOffDiag(watState,qState),qState) + if(qState/=integerMissing)then + if((qstate - watState <= ku) .and. (watState - qstate <= kl)) & + aJac(ixOffDiag(watState,qState),qState) = (dt/mLayerDepth(jLayer))*dBaseflow_dMatric(iLayer,pLayer) + aJac(ixOffDiag(watState,qState),qState) + endif end do endif ! - only include banded terms for surface infiltration below surface in banded structure; ixSoilOnlyHyd(1) - watState always <= kl - if(ixSoilOnlyHyd(1)/=integerMissing .and. (watState - ixSoilOnlyHyd(1) <= ku)) & - aJac(ixOffDiag(ixSoilOnlyHyd(1),watState),watState) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(iLayer) + aJac(ixOffDiag(ixSoilOnlyHyd(1),watState),watState) - + if(ixSoilOnlyHyd(1)/=integerMissing)then + if(watState - ixSoilOnlyHyd(1) <= ku) & + aJac(ixOffDiag(ixSoilOnlyHyd(1),watState),watState) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(iLayer) + aJac(ixOffDiag(ixSoilOnlyHyd(1),watState),watState) + endif end do ! (looping through hydrology states in the soil domain) ! - include terms for surface infiltration above surface @@ -523,7 +526,7 @@ subroutine computJacob(& aJac(ixDiag,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) if(ixSoilOnlyNrg(nSoil)/=integerMissing) aJac(ixOffDiag(ixAqWat,ixSoilOnlyNrg(nSoil)),ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt ! dAquiferRecharge_dTk = d_iLayerLiqFluxSoil(nSoil)_dTk if(ixSoilOnlyHyd(nSoil)/=integerMissing) aJac(ixOffDiag(ixAqWat,ixSoilOnlyHyd(nSoil)),ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt ! dAquiferRecharge_dWat = d_iLayerLiqFluxSoil(nSoil)_dWat - ! - only include banded derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration), would have to have few soil layers + ! - only include banded terms for derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration), would have to have few soil layers if(computeVegFlux)then if(ixCasNrg/=integerMissing)then if(ixAqWat-ixCasNrg <= kl) aJac(ixOffDiag(ixAqWat,ixCasNrg),ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) @@ -622,9 +625,10 @@ subroutine computJacob(& endif ! (if the water state for the current layer is within the state subset) ! - only include banded terms for surface infiltration below surface in banded structure; ixSoilOnlyHyd(1) - nrgState always <= kl - if(ixSoilOnlyHyd(1)/=integerMissing .and. (nrgState - ixSoilOnlyHyd(1) <= ku)) & - aJac(ixOffDiag(ixSoilOnlyHyd(1),nrgState),nrgState) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(iLayer) + aJac(ixOffDiag(ixSoilOnlyHyd(1),nrgState),nrgState) - + if(ixSoilOnlyHyd(1)/=integerMissing)then + if(nrgState - ixSoilOnlyHyd(1) <= ku) & + aJac(ixOffDiag(ixSoilOnlyHyd(1),nrgState),nrgState) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(iLayer) + aJac(ixOffDiag(ixSoilOnlyHyd(1),nrgState),nrgState) + endif end do ! (looping through energy states in the soil domain) ! - include terms for surface infiltration above surface diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index 28e346fcf..2e22fbdee 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -540,15 +540,18 @@ subroutine computJacobWithPrime(& if(computeBaseflow .and. nSoilOnlyHyd==nSoil)then do pLayer=1,nSoil qState = ixSoilOnlyHyd(pLayer) ! hydrology state index within the state subset - if(qState/=integerMissing .and. (qstate - watState <= ku) .and. (watState - qstate <= kl)) & - aJac(ixOffDiag(watState,qState),qState) = (dt/mLayerDepth(jLayer))*dBaseflow_dMatric(iLayer,pLayer) + aJac(ixOffDiag(watState,qState),qState) + if(qState/=integerMissing)then + if((qstate - watState <= ku) .and. (watState - qstate <= kl)) & + aJac(ixOffDiag(watState,qState),qState) = (dt/mLayerDepth(jLayer))*dBaseflow_dMatric(iLayer,pLayer) + aJac(ixOffDiag(watState,qState),qState) + endif end do endif ! - only include banded terms for surface infiltration below surface in banded structure; ixSoilOnlyHyd(1) - watState always <= kl - if(ixSoilOnlyHyd(1)/=integerMissing .and. (watState - ixSoilOnlyHyd(1) <= ku)) & - aJac(ixOffDiag(ixSoilOnlyHyd(1),watState),watState) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(iLayer) + aJac(ixOffDiag(ixSoilOnlyHyd(1),watState),watState) - + if(ixSoilOnlyHyd(1)/=integerMissing)then + if(watState - ixSoilOnlyHyd(1) <= ku) & + aJac(ixOffDiag(ixSoilOnlyHyd(1),watState),watState) = -(dt/mLayerDepth(1+nSnow))*dq_dHydStateLayerSurfVec(iLayer) + aJac(ixOffDiag(ixSoilOnlyHyd(1),watState),watState) + endif end do ! (looping through hydrology states in the soil domain) ! - include terms for surface infiltration above surface @@ -567,7 +570,7 @@ subroutine computJacobWithPrime(& aJac(ixDiag,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) * cj if(ixSoilOnlyNrg(nSoil)/=integerMissing) aJac(ixOffDiag(ixAqWat,ixSoilOnlyNrg(nSoil)),ixSoilOnlyNrg(nSoil)) = -dq_dNrgStateAbove(nSoil)*dt if(ixSoilOnlyHyd(nSoil)/=integerMissing) aJac(ixOffDiag(ixAqWat,ixSoilOnlyHyd(nSoil)),ixSoilOnlyHyd(nSoil)) = -dq_dHydStateAbove(nSoil)*dt - ! - only include banded derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration), would have to have few soil layers + ! - only include banded terms for derivatives of energy and water w.r.t soil transpiration (dependent on canopy transpiration), would have to have few soil layers if(computeVegFlux)then if(ixCasNrg/=integerMissing)then if(ixAqWat-ixCasNrg <= kl) aJac(ixOffDiag(ixAqWat,ixCasNrg),ixCasNrg) = -dAquiferTrans_dTCanair*dt ! dVol/dT (K-1) @@ -667,9 +670,10 @@ subroutine computJacobWithPrime(& endif ! (if the water state for the current layer is within the state subset) ! - only include banded terms for surface infiltration below surface in banded structure; ixSoilOnlyHyd(1) - nrgState always <= kl - if(ixSoilOnlyHyd(1)/=integerMissing .and. (nrgState - ixSoilOnlyHyd(1) <= ku)) & - aJac(ixOffDiag(ixSoilOnlyHyd(1),nrgState),nrgState) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(iLayer) + aJac(ixOffDiag(ixSoilOnlyHyd(1),nrgState),nrgState) - + if(ixSoilOnlyHyd(1)/=integerMissing)then + if(nrgState - ixSoilOnlyHyd(1) <= ku) & + aJac(ixOffDiag(ixSoilOnlyHyd(1),nrgState),nrgState) = -(dt/mLayerDepth(1+nSnow))*dq_dNrgStateLayerSurfVec(iLayer) + aJac(ixOffDiag(ixSoilOnlyHyd(1),nrgState),nrgState) + endif end do ! (looping through energy states in the soil domain) ! - include terms for surface infiltration above surface diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index a72e363e7..bd6b78086 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -353,7 +353,6 @@ subroutine getScaling(& ! define the multiplier for the state vector for residual calculations (vegetation canopy) ! NOTE: Use the "where" statement to generalize to multiple canopy layers (currently one canopy layer) - where(ixStateType_subset==iname_nrgCanair) sMul = Cp_air*iden_air ! volumetric heat capacity of air (J m-3 K-1) where(ixStateType_subset==iname_nrgCanopy) sMul = volHeatCapVeg ! volumetric heat capacity of the vegetation (J m-3 K-1) where(ixStateType_subset==iname_watCanopy) sMul = 1._rkind ! nothing else on the left hand side diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index f2703e925..1954a4574 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -335,14 +335,13 @@ subroutine soilLiqFlx(& ! check the need to compute transpiration (NOTE: intent=inout) if( .not. (scalarSolution .and. ixTop>1) )then - + ! compute the fraction of transpiration loss from each soil layer if(scalarTranspireLim > tiny(scalarTranspireLim))then ! (transpiration may be non-zero even if the soil moisture limiting factor is zero) mLayerTranspireFrac(:) = mLayerRootDensity(:)*mLayerTranspireLim(:)/scalarTranspireLim else ! (possible for there to be non-zero conductance and therefore transpiration in this case) mLayerTranspireFrac(:) = mLayerRootDensity(:) / sum(mLayerRootDensity) end if - ! check fractions sum to one if(abs(sum(mLayerTranspireFrac) - 1._rkind) > verySmall)then message=trim(message)//'fraction transpiration in soil layers does not sum to one' diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 0489fcbf1..c8e8f54e2 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -1131,8 +1131,8 @@ subroutine imposeConstraints(stateVecTrial,xInc,err,message) real(rkind) :: xPsi00 ! matric head after applying the iteration increment (m) real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) real(rkind) :: critDiff ! temperature difference from critical (K) - real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical (K) - real(rkind),parameter :: zMaxTempIncrement=1._rkind ! maximum temperature increment (K) + real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical (K) + real(rkind),parameter :: zMaxTempIncrement=1._rkind ! maximum temperature increment (K) ! indices of model state variables integer(i4b) :: iState ! index of state within a specific variable type integer(i4b) :: ixNrg,ixLiq ! index of energy and mass state variables in full state vector From 5a755c357acd4803f16df40ffe0e19b8f566acc4 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 19 Jul 2023 23:42:54 -0500 Subject: [PATCH 0801/1472] spaces --- build/source/engine/stomResist.f90 | 48 ++--------------------- build/source/engine/summaSolve4ida.f90 | 6 +-- build/source/engine/summaSolve4kinsol.f90 | 10 ++--- build/source/engine/summaSolve4numrec.f90 | 4 +- 4 files changed, 13 insertions(+), 55 deletions(-) diff --git a/build/source/engine/stomResist.f90 b/build/source/engine/stomResist.f90 index 1934681e3..356e67e66 100644 --- a/build/source/engine/stomResist.f90 +++ b/build/source/engine/stomResist.f90 @@ -434,7 +434,6 @@ subroutine stomResist_flex(& ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! associate variables in the data structure associate(& - ! input: model decisions ix_bbTempFunc => model_decisions(iLookDECISIONS%bbTempFunc)%iDecision, & ! intent(in): [i4b] leaf temperature controls on photosynthesis + stomatal resistance ix_bbHumdFunc => model_decisions(iLookDECISIONS%bbHumdFunc)%iDecision, & ! intent(in): [i4b] humidity controls on stomatal resistance @@ -443,7 +442,6 @@ subroutine stomResist_flex(& ix_bbNumerics => model_decisions(iLookDECISIONS%bbNumerics)%iDecision, & ! intent(in): [i4b] iterative numerical solution method used in the Ball-Berry parameterization ix_bbAssimFnc => model_decisions(iLookDECISIONS%bbAssimFnc)%iDecision, & ! intent(in): [i4b] controls on carbon assimilation (min function, or colimitation) ix_bbCanIntg8 => model_decisions(iLookDECISIONS%bbCanIntg8)%iDecision, & ! intent(in): [i4b] scaling of photosynthesis from the leaf to the canopy - ! input: model parameters Kc25 => mpar_data%var(iLookPARAM%Kc25)%dat(1), & ! intent(in): [dp] Michaelis-Menten constant for CO2 at 25 degrees C (umol mol-1) Ko25 => mpar_data%var(iLookPARAM%Ko25)%dat(1), & ! intent(in): [dp] Michaelis-Menten constant for O2 at 25 degrees C (mol mol-1) @@ -466,27 +464,22 @@ subroutine stomResist_flex(& vpScaleFactor => mpar_data%var(iLookPARAM%vpScaleFactor)%dat(1), & ! intent(in): [dp] vapor pressure scaling factor in stomatal conductance function (Pa) cond2photo_slope => mpar_data%var(iLookPARAM%cond2photo_slope)%dat(1), & ! intent(in): [dp] slope of conductance-photosynthesis relationship (-) minStomatalConductance => mpar_data%var(iLookPARAM%minStomatalConductance)%dat(1), & ! intent(in): [dp] mimimum stomatal conductance (umol H2O m-2 s-1) - ! input: forcing at the upper boundary airtemp => forc_data%var(iLookFORCE%airtemp), & ! intent(in): [dp] air temperature at some height above the surface (K) airpres => forc_data%var(iLookFORCE%airpres), & ! intent(in): [dp] air pressure at some height above the surface (Pa) scalarO2air => diag_data%var(iLookDIAG%scalarO2air)%dat(1), & ! intent(in): [dp] atmospheric o2 concentration (Pa) scalarCO2air => diag_data%var(iLookDIAG%scalarCO2air)%dat(1), & ! intent(in): [dp] atmospheric co2 concentration (Pa) - ! input: state and diagnostic variables scalarExposedLAI => diag_data%var(iLookDIAG%scalarExposedLAI)%dat(1), & ! intent(in): [dp] exposed LAI (m2 m-2) scalarGrowingSeasonIndex => diag_data%var(iLookDIAG%scalarGrowingSeasonIndex)%dat(1), & ! intent(in): [dp] growing season index (0=off, 1=on) scalarFoliageNitrogenFactor => diag_data%var(iLookDIAG%scalarFoliageNitrogenFactor)%dat(1), & ! intent(in): [dp] foliage nitrogen concentration (1.0 = saturated) scalarTranspireLim => diag_data%var(iLookDIAG%scalarTranspireLim)%dat(1), & ! intent(in): [dp] weighted average of the transpiration limiting factor (-) scalarLeafResistance => flux_data%var(iLookFLUX%scalarLeafResistance)%dat(1) & ! intent(in): [dp] mean leaf boundary layer resistance per unit leaf area (s m-1) - ) ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! initialize error control err=0; message="stomResist_flex/" - !print*, '**' - ! ***** ! * preliminaries... ! ****************** @@ -567,7 +560,6 @@ subroutine stomResist_flex(& ! linear model, as used in CLM4 and Noah-MP case(linear) Js = quantamYield*joule2umolConv*absorbedPAR - !write(*,'(a,1x,10(f20.10,1x))') 'quantamYield, joule2umolConv, absorbedPAR = ', quantamYield, joule2umolConv, absorbedPAR ! linear function of qmax, as used in Cable [Wang et al., Ag Forest Met 1998, eq D5] case(linearJmax) @@ -626,23 +618,11 @@ subroutine stomResist_flex(& else ci = 0.7_rkind*scalarCO2air ! always initialize if not NR end if - !write(*,'(a,1x,10(f20.10,1x))') 'Kc25, Kc_qFac, Ko25, Ko_qFac = ', Kc25, Kc_qFac, Ko25, Ko_qFac - !write(*,'(a,1x,10(f20.10,1x))') 'scalarCO2air, ci, co2compPt, Kc, Ko = ', scalarCO2air, ci, co2compPt, Kc, Ko - + ! initialize brackets for the solution cMin = 0._rkind cMax = scalarCO2air - ! ********************************************************************************************************************************* - ! ********************************************************************************************************************************* - ! ********************************************************************************************************************************* - ! ********************************************************************************************************************************* - ! ********************************************************************************************************************************* - ! ********************************************************************************************************************************* - - !print *, '**' - !print *, '**' - ! *** ! iterate do iter=1,maxiter @@ -679,12 +659,6 @@ subroutine stomResist_flex(& x2 = h2o_co2__stomPores * airpres ! Pa ci = max(cs - x2*psn*rs, 0._rkind) ! Pa - ! print progress - !if(ix_bbNumerics==NoahMPsolution)then - ! write(*,'(a,1x,10(f20.10,1x))') 'psn, rs, ci, cs, scalarVegetationTemp, vcmax, Js = ', & - ! psn, rs, ci, cs, scalarVegetationTemp, vcmax, Js - !end if - ! final derivative if(ci > tiny(ci))then dci_dc = -x1*dA_dc - x2*(psn*drs_dc + rs*dA_dc) @@ -731,10 +705,6 @@ subroutine stomResist_flex(& ci = 0.5_rkind * (cMin + cMax) end if - ! print progress - !write(*,'(a,1x,i4,1x,20(f12.7,1x))') 'iter, psn, rs, ci, cs, cMin, cMax, co2compPt, scalarCO2air, xInc = ', & - ! iter, psn, rs, ci, cs, cMin, cMax, co2compPt, scalarCO2air, xInc - ! check for convergence if(abs(xInc) < convToler) exit if(iter==maxIter)then @@ -743,7 +713,6 @@ subroutine stomResist_flex(& end if end do ! iterating - !pause 'iterating' ! assign output variables scalarStomResist = unitConv*umol_per_mol*rs ! umol-1 m2 s --> s/m @@ -754,8 +723,6 @@ subroutine stomResist_flex(& contains ! ****************************************************** - ! ****************************************************** - ! internal function used to test derivatives function testFunc(ci, cond2photo_slope, airpres, scalarCO2air, ix_bbHumdFunc, ix_bbCO2point, ix_bbAssimFnc) real(rkind),intent(in) :: ci, cond2photo_slope, airpres, scalarCO2air @@ -940,8 +907,8 @@ end subroutine photosynthesis subroutine quadResist(desireDeriv,ix_bbHumdFunc,rlb,fHum,gMin,g0,dg0_dc,rs,drs_dc) implicit none ! dummy variables - logical(lgt),intent(in) :: desireDeriv ! flag to denote if the derivative is desired - integer(i4b),intent(in) :: ix_bbHumdFunc ! option for humidity control on stomatal resistance + logical(lgt),intent(in) :: desireDeriv ! flag to denote if the derivative is desired + integer(i4b),intent(in) :: ix_bbHumdFunc ! option for humidity control on stomatal resistance real(rkind),intent(in) :: rlb ! leaf boundary layer resistance (umol-1 m2 s) real(rkind),intent(in) :: fHum ! scaled humidity function (-) real(rkind),intent(in) :: gMin ! scaled minimum stomatal consuctance (umol m-2 s-1) @@ -982,11 +949,6 @@ subroutine quadResist(desireDeriv,ix_bbHumdFunc,rlb,fHum,gMin,g0,dg0_dc,rs,drs_d root2 = cQuad / qQuad rs = max(root1,root2) - ! check - !write(*,'(a,1x,10(f20.5,1x))') 'root1, root2, rs = ', root1, root2, rs - !write(*,'(a,1x,10(f20.5,1x))') 'g0, fHum, aquad, bquad, cquad, qquad = ', & - ! g0, fHum, aquad, bquad, cquad, qquad - ! compute derivatives if(desireDeriv)then @@ -1082,7 +1044,6 @@ end subroutine quadSmooth ! ***** ! * temperature functions... ! ************************** - ! q10 function for temperature dependence function q10(a,T,Tmid,Tscale) implicit none @@ -1265,8 +1226,5 @@ end subroutine stomResist_NoahMP ! -- end private subroutines - ! ------------------------------------------------------------------------------------------------------------ - ! ------------------------------------------------------------------------------------------------------------ - ! ------------------------------------------------------------------------------------------------------------ end module stomResist_module diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 8634910f8..e5dfded35 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -86,9 +86,9 @@ module summaSolve4ida_module contains -!------------------- +! ************************************************************************************ ! * public subroutine summaSolve4ida: solve F(y,y') = 0 by IDA (y is the state vector) -! ------------------ +! ************************************************************************************ subroutine summaSolve4ida( & dt_cur, & ! intent(in): current stepsize dt, & ! intent(in): data time step @@ -230,7 +230,7 @@ subroutine summaSolve4ida( & character(LEN=256) :: cmessage ! error message of downwind routine real(rkind),allocatable :: mLayerMatricHeadPrimePrev(:) ! previous derivative value for total water matric potential (m s-1) real(rkind),allocatable :: dCompress_dPsiPrev(:) ! previous derivative value soil compression - logical(lgt) :: use_fdJac ! flag to use finite difference Jacobian, default false + logical(lgt) :: use_fdJac ! flag to use finite difference Jacobian, controlled by decision fDerivMeth logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true logical(lgt),parameter :: detect_events = .true. ! flag to do event detection and restarting, default true diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index 93f13e785..3218e0f0c 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -83,9 +83,9 @@ module summaSolve4kinsol_module contains -!------------------- +! *************************************************************************************** ! * public subroutine summaSolve4kinsol: solve F(y) = 0 by KINSOL (y is the state vector) -! ------------------ +! *************************************************************************************** subroutine summaSolve4kinsol(& dt_cur, & ! intent(in): current stepsize dt, & ! intent(in): data time step @@ -204,7 +204,7 @@ subroutine summaSolve4kinsol(& real(rkind) :: rVec(nStat) ! residual vector integer(i4b) :: iVar, i ! indices character(LEN=256) :: cmessage ! error message of downwind routine - logical(lgt) :: use_fdJac ! flag to use finite difference Jacobian, default false + logical(lgt) :: use_fdJac ! flag to use finite difference Jacobian, controlled by decision fDerivMeth logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true ! ----------------------------------------------------------------------------------------------------- @@ -427,9 +427,9 @@ subroutine setInitialCondition(neq, y, sunvec_u) end subroutine setInitialCondition -! ---------------------------------------------------------------- +! ------------------------------------------------------------------- ! setSolverParams: private routine to set parameters in KINSOL solver -! ---------------------------------------------------------------- +! ------------------------------------------------------------------- subroutine setSolverParams(nonlin_iter,kinsol_mem,retval) !======= Inclusions =========== diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index c8e8f54e2..5c8fbc864 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -86,9 +86,9 @@ module summaSolve4numrec_module public::summaSolve4numrec contains - ! ********************************************************************************************************* + ! *********************************************************************************************************************** ! public subroutine summaSolve4numrec: calculate the iteration increment, evaluate the new state, and refine if necessary - ! ********************************************************************************************************* + ! *********************************************************************************************************************** subroutine summaSolve4numrec(& ! input: model control dt_cur, & ! intent(in): current stepsize From 8d103b77f4cf4ebde6a53ca86b1cb5ad48ead547 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 25 Jul 2023 16:33:13 -0500 Subject: [PATCH 0802/1472] add in impose constraints, but keep all constraints turned off for now. Also switching to Picard because it seems to work better than linesearch. --- build/source/dshare/type4ida.f90 | 2 +- build/source/dshare/type4kinsol.f90 | 4 +- build/source/engine/eval8summa.f90 | 279 +++++++++++++++++++- build/source/engine/eval8summaWithPrime.f90 | 20 +- build/source/engine/summaSolve4ida.f90 | 3 +- build/source/engine/summaSolve4kinsol.f90 | 10 +- build/source/engine/summaSolve4numrec.f90 | 10 - build/source/engine/systemSolv.f90 | 2 +- 8 files changed, 294 insertions(+), 36 deletions(-) diff --git a/build/source/dshare/type4ida.f90 b/build/source/dshare/type4ida.f90 index 3d8ee6c63..9f7f8e403 100644 --- a/build/source/dshare/type4ida.f90 +++ b/build/source/dshare/type4ida.f90 @@ -78,7 +78,7 @@ module type4ida real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) integer(i4b) :: ixSaturation ! index of the lowest saturated layer integer(i4b) :: err ! error code - character(len = 50) :: message ! error message + character(len=50) :: message ! error message end type data4ida diff --git a/build/source/dshare/type4kinsol.f90 b/build/source/dshare/type4kinsol.f90 index 3797c4e58..1b8614817 100644 --- a/build/source/dshare/type4kinsol.f90 +++ b/build/source/dshare/type4kinsol.f90 @@ -47,8 +47,10 @@ module type4kinsol real(rkind),allocatable :: xScale(:) ! characteristic scale of the state vector real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) integer(i4b) :: ixSaturation ! index of the lowest saturated layer + real(rkind), allocatable :: stateVecPrev(:) ! state vector from the previous iteration to help with infeasibility + logical(lgt) :: firstStateIteration ! flag to denote if we computed an iteration so we know to save the state integer(i4b) :: err ! error code - character(len = 50) :: message ! error message + character(len=50) :: message ! error message end type data4kinsol diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 7fcb2aaf6..7201e9d52 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -28,6 +28,9 @@ module eval8summa_module USE globalData,only:realMissing ! missing double precision number USE globalData,only:quadMissing ! missing quadruple precision number +! named variables to describe the state variable type +USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers +USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers ! constants USE multiconst,only:& @@ -65,7 +68,7 @@ module eval8summa_module ida ! SUNDIALS solution using IDA implicit none -private +private::imposeConstraints public::eval8summa #ifdef SUNDIALS_ACTIVE public::eval8summa4kinsol @@ -717,16 +720,18 @@ integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & implicit none ! calling variables - type(N_Vector) :: sunvec_y ! solution N_Vector y - type(N_Vector) :: sunvec_r ! residual N_Vector F(t,y) - type(c_ptr), value :: user_data ! user-defined data + type(N_Vector) :: sunvec_y ! solution N_Vector y + type(N_Vector) :: sunvec_r ! residual N_Vector F(t,y) + type(c_ptr), value :: user_data ! user-defined data ! pointers to data in SUNDIALS vectors - type(data4kinsol), pointer :: eqns_data ! equations data - real(rkind), pointer :: stateVec(:) - real(rkind), pointer :: rVec(:) - logical(lgt) :: feasible - real(rkind) :: fNew ! function values, not needed here + type(data4kinsol), pointer :: eqns_data ! equations data + real(rkind), pointer :: stateVec(:) ! solution vector + real(rkind), pointer :: rVec(:) ! residual vector + logical(lgt) :: feasible ! feasibility of state vector + real(rkind) :: fNew ! function values, not needed here + integer(i4b) :: err ! error in imposeConstraints + character(len=256) :: message ! error message !======= Internals ============ ! get equations data from user-defined data @@ -736,6 +741,16 @@ integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & stateVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_y) rVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_r) + ! increment the proposed iteration for simple error control if needed + if (eqns_data%firstStateiteration) then + eqns_data%firstStateIteration = .false. + else + call imposeConstraints(eqns_data%indx_data,eqns_data%prog_data,eqns_data%mpar_data,stateVec(:), & + eqns_data%stateVecPrev, eqns_data%nState, eqns_data%nSoil, eqns_data%nSnow, message, err) + if(err/=0)then; ierr=1; message="Impose Constraints Failed"; print*, message; return; end if ! (check for errors) + eqns_data%stateVecPrev = stateVec(:) + endif + ! compute the flux and the residual vector for a given state vector call eval8summa(& ! input: model control @@ -789,4 +804,250 @@ integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & end function eval8summa4kinsol #endif +! *************************************************************************************************************************************** +! private subroutine imposeConstraints: impose solution constraints +! This is simple error control to reduce possible temperature increments, cross over freezing point events, and keep the state feasible +! Imposed after the internal call of KINSOL incrementing the linesearch +! *************************************************************************************************************************************** +subroutine imposeConstraints(indx_data, prog_data, mpar_data, stateVec, stateVecPrev,& + nState, nSoil, nSnow, message, err) + ! external functions + USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water at a given temperature (snow) + USE soil_utils_module,only:crit_soilT ! compute the critical temperature below which ice exists + implicit none + + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(in) :: mpar_data ! model parameters + real(rkind), intent(inout) :: stateVec(:) ! state vector + real(rkind), intent(in) :: stateVecPrev(:) ! previous state vector + integer(i4b),intent(in) :: nState ! total number of state variables + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(out) :: err ! error code + character(len=256),intent(out) :: message ! error message + ! ----------------------------------------------------------------------------------------------------- + ! temporary variables for model constraints + real(rkind) :: cInc ! constrained temperature increment (K) -- simplified bi-section + real(qp),dimension(nState) :: xInc ! iteration increment + real(rkind) :: xIncFactor ! scaling factor for the iteration increment (-) + integer(i4b) :: iMax(1) ! index of maximum temperature + real(rkind) :: scalarTemp ! temperature of an individual snow layer (K) + real(rkind) :: volFracLiq ! volumetric liquid water content of an individual snow layer (-) + logical(lgt),dimension(nSoil) :: crosFlag ! flag to denote temperature crossing from unfrozen to frozen (or vice-versa) + logical(lgt) :: crosTempVeg ! flag to denoote where temperature crosses the freezing point + real(rkind) :: xPsi00 ! matric head after applying the iteration increment (m) + real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) + real(rkind) :: critDiff ! temperature difference from critical (K) + real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical (K) + real(rkind),parameter :: zMaxTempIncrement=1._rkind ! maximum temperature increment (K) + ! indices of model state variables + integer(i4b) :: iState ! index of state within a specific variable type + integer(i4b) :: ixNrg,ixLiq ! index of energy and mass state variables in full state vector + ! indices of model layers + integer(i4b) :: iLayer ! index of model layer + ! choice of constraints to impose + logical(lgt),parameter :: small_delTemp=.false. ! flag to constain temperature change to be less than zMaxTempIncrement + logical(lgt),parameter :: detect_events=.false. ! flag to do freezing point event detection and cross-over with epsT + logical(lgt),parameter :: positive_wat=.false. ! flag to force water to not go negative + real(qp),dimension(nState) :: junk + ! ----------------------------------------------------------------------------------------------------- + ! associate variables with indices of model state variables + associate(& + ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for energy states + ixHydOnly => indx_data%var(iLookINDEX%ixHydOnly)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for hydrology states + ixMatOnly => indx_data%var(iLookINDEX%ixMatOnly)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for matric head states + ixMassOnly => indx_data%var(iLookINDEX%ixMassOnly)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for canopy storage states + ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] named variables defining the states in the subset + ! indices for specific state variables + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow-soil subdomain + ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow-soil subdomain + ! vector of energy indices for the snow and soil domains + ! NOTE: states not in the subset are equal to integerMissing + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow domain + ixSoilOnlyNrg => indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the soil domain + ! vector of hydrology indices for the snow and soil domains + ! NOTE: states not in the subset are equal to integerMissing + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow domain + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the soil domain + ! number of state variables of a specific type + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain + nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + ! state variables at the start of the time step + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat & ! intent(in): [dp(:)] matric head (m) + ) ! associating variables with indices of model state variables + ! ----------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message='imposeConstraints/' + + ! calculate proposed increment in state vector + xInc(1:nState) = stateVec(1:nState)*1._qp - stateVecPrev(1:nState)*1._qp + + ! ** limit temperature increment to zMaxTempIncrement + if(small_delTemp)then + if(any(abs(xInc(ixNrgOnly)) > zMaxTempIncrement))then + iMax = maxloc( abs(xInc(ixNrgOnly)) ) ! index of maximum temperature increment + xIncFactor = abs( zMaxTempIncrement/xInc(ixNrgOnly(iMax(1))) ) ! scaling factor for the iteration increment (-) + xInc = xIncFactor*xInc + endif + endif ! (small temperature change) + + ! ** stop just above or just below the freezing point if crossing + if(detect_events)then + + ! crossing freezing point event for vegetation + if(ixVegNrg/=integerMissing)then + ! initialize + critDiff = Tfreeze - stateVecPrev(ixVegNrg) + crosTempVeg = .false. + ! initially frozen (T < Tfreeze) + if(critDiff > 0._rkind)then + if(xInc(ixVegNrg) > critDiff)then + crosTempVeg = .true. + cInc = critDiff + epsT ! constrained temperature increment (K) + end if + ! initially unfrozen (T > Tfreeze) + else + if(xInc(ixVegNrg) < critDiff)then + crosTempVeg = .true. + cInc = critDiff - epsT ! constrained temperature increment (K) + end if + end if ! switch between frozen and unfrozen + ! scale iterations + if(crosTempVeg)then + xIncFactor = cInc/xInc(ixVegNrg) ! scaling factor for the iteration increment (-) + xInc = xIncFactor*xInc ! scale iteration increments + endif + endif ! if the state variable for canopy temperature is included within the state subset + + ! crossing freezing point event for snow + if(nSnowOnlyNrg > 0)then + do iLayer=1,nSnow + ! check if energy state is included + if(ixSnowOnlyNrg(iLayer)==integerMissing) cycle + ! check temperatures, and, if necessary, scale iteration increment + iState = ixSnowOnlyNrg(iLayer) + if(stateVecPrev(iState) + xInc(iState) > Tfreeze)then + ! scale iteration increment + cInc = 0.5_rkind*(Tfreeze - stateVecPrev(iState) ) ! constrained temperature increment (K) -- simplified bi-section + xIncFactor = cInc/xInc(iState) ! scaling factor for the iteration increment (-) + xInc = xIncFactor*xInc + endif ! (if snow temperature > freezing) + end do ! (loop through snow layers) + endif ! (if there are state variables for energy in the snow domain) + + ! crossing freezing point event for soil + if(nSoilOnlyNrg>0)then + do iLayer=1,nSoil + ! check if energy state is included + if(ixSoilOnlyNrg(iLayer)==integerMissing) cycle + ! define index of the state variables within the state subset + ixNrg = ixSoilOnlyNrg(iLayer) + ixLiq = ixSoilOnlyHyd(iLayer) + ! get the matric potential of total water + if(ixLiq/=integerMissing)then + xPsi00 = stateVecPrev(ixLiq) + xInc(ixLiq) + else + xPsi00 = mLayerMatricHead(iLayer) + endif + ! identify the critical point when soil begins to freeze (TcSoil) + TcSoil = crit_soilT(xPsi00) + ! get the difference from the current state and the crossing point (K) + critDiff = TcSoil - stateVecPrev(ixNrg) + ! initially frozen (T < TcSoil) + if(critDiff > 0._rkind)then + ! (check crossing above zero) + if(xInc(ixNrg) > critDiff)then + crosFlag(iLayer) = .true. + xInc(ixNrg) = critDiff + epsT ! set iteration increment to slightly above critical temperature + endif + ! initially unfrozen (T > TcSoil) + else + ! (check crossing below zero) + if(xInc(ixNrg) < critDiff)then + crosFlag(iLayer) = .true. + xInc(ixNrg) = critDiff - epsT ! set iteration increment to slightly below critical temperature + endif + endif ! (switch between initially frozen and initially unfrozen) + end do ! (loop through soil layers) + endif ! (if there are both energy and liquid water state variables) + + endif ! (detect events) + + ! ** ensure water is non-negative + if(positive_wat)then + + ! impose positivity for canopy liquid water + if(ixVegHyd/=integerMissing)then + ! check if new value of storage will be negative + if(stateVecPrev(ixVegHyd)+xInc(ixVegHyd) < 0._rkind)then + ! scale iteration increment + cInc = -0.5_rkind*stateVecPrev(ixVegHyd) ! constrained iteration increment (K) -- simplified bi-section + xIncFactor = cInc/xInc(ixVegHyd) ! scaling factor for the iteration increment (-) + xInc = xIncFactor*xInc ! new iteration increment + end if + endif ! (if the state variable for canopy water is included within the state subset) + + ! impose positivity for snow water + if(nSnowOnlyHyd>0)then + ! loop through snow layers + do iLayer=1,nSnow + ! check if the layer is included + if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle + if(ixSnowOnlyNrg(iLayer)/=integerMissing)then + ! get the layer temperature (from stateVecPrev if ixSnowOnlyNrg(iLayer) is within the state vector + scalarTemp = stateVecPrev( ixSnowOnlyNrg(iLayer) ) + else + ! get the layer temperature from the last update + scalarTemp = prog_data%var(iLookPROG%mLayerTemp)%dat(iLayer) + endif + ! get the volumetric fraction of liquid water + select case( ixStateType_subset( ixSnowOnlyHyd(iLayer) ) ) + case(iname_watLayer); volFracLiq = fracliquid(scalarTemp,mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1)) * stateVecPrev(ixSnowOnlyHyd(iLayer)) + case(iname_liqLayer); volFracLiq = stateVecPrev(ixSnowOnlyHyd(iLayer)) + case default; err=20; message=trim(message)//'expect ixStateType_subset to be iname_watLayer or iname_liqLayer for snow hydrology'; return + end select + ! checking if drain more than what is available (increment does not exceed volumetric liquid water content) + ! NOTE: change in total water is only due to liquid flux + if(-xInc(ixSnowOnlyHyd(iLayer)) > volFracLiq)then + xInc(ixSnowOnlyHyd(iLayer)) = -0.5_rkind*volFracLiq + endif + end do ! (looping through snow layers) + endif ! (if there are state variables for liquid water in the snow domain) + + ! impose positivity for soil water (matric head) + if(size(ixMatOnly)>0)then + ! loop through soil layers + do iState=1,size(ixMatOnly) + ! define index of the hydrology state variable within the state subset + ixLiq = ixMatOnly(iState) + ! place constraint for matric head + if(xInc(ixLiq) > 1._rkind .and. stateVecPrev(ixLiq) > 0._rkind)then + xInc(ixLiq) = 1._rkind + endif ! if constraining matric head + end do ! (loop through soil layers) + endif ! (if there are both energy and liquid water state variables) + + endif ! (water positivity) + + + ! Update the state vector with the modified iteration increment + stateVec(:) = stateVecPrev(:) + xInc(:) + + ! end association with variables with indices of model state variables + end associate + +end subroutine imposeConstraints + + end module eval8summa_module diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 9889589bc..cb4deff81 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -767,18 +767,18 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user implicit none ! calling variables - real(rkind), value :: tres ! current time t - type(N_Vector) :: sunvec_y ! solution N_Vector y - type(N_Vector) :: sunvec_yp ! derivative N_Vector y' - type(N_Vector) :: sunvec_r ! residual N_Vector F(t,y,y') - type(c_ptr), value :: user_data ! user-defined data + real(rkind), value :: tres ! current time t + type(N_Vector) :: sunvec_y ! solution N_Vector y + type(N_Vector) :: sunvec_yp ! derivative N_Vector y' + type(N_Vector) :: sunvec_r ! residual N_Vector F(t,y,y') + type(c_ptr), value :: user_data ! user-defined data ! pointers to data in SUNDIALS vectors - type(data4ida), pointer :: eqns_data ! equations data - real(rkind), pointer :: stateVec(:) - real(rkind), pointer :: stateVecPrime(:) - real(rkind), pointer :: rVec(:) - logical(lgt) :: feasible + type(data4ida), pointer :: eqns_data ! equations data + real(rkind), pointer :: stateVec(:) ! solution vector + real(rkind), pointer :: stateVecPrime(:) ! derivative vector + real(rkind), pointer :: rVec(:) ! residual vector + logical(lgt) :: feasible ! feasibility of state vector !======= Internals ============ ! get equations data from user-defined data diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index e5dfded35..8fc75649d 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -149,6 +149,7 @@ subroutine summaSolve4ida( & USE eval8summaWithPrime_module,only:eval8summaWithPrime ! residual of DAE USE computJacobWithPrime_module,only:computJacob4ida ! system Jacobian USE tol4ida_module,only:computWeight4ida ! weight required for tolerances + USE var_lookup,only:maxvarDecisions ! maximum number of decisions !======= Declarations ========= implicit none @@ -259,7 +260,6 @@ subroutine summaSolve4ida( & eqns_data%firstSubStep = firstSubStep eqns_data%computeVegFlux = computeVegFlux eqns_data%scalarSolution = scalarSolution - eqns_data%model_decisions = model_decisions eqns_data%lookup_data = lookup_data eqns_data%type_data = type_data eqns_data%attr_data = attr_data @@ -274,6 +274,7 @@ subroutine summaSolve4ida( & eqns_data%ixSaturation = ixSaturation ! allocate space and fill + allocate( eqns_data%model_decisions(maxvarDecisions) ); eqns_data%model_decisions = model_decisions allocate( eqns_data%atol(nState) ); eqns_data%atol = atol allocate( eqns_data%rtol(nState) ); eqns_data%rtol = rtol allocate( eqns_data%sMul(nState) ); eqns_data%sMul = sMul diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index 3218e0f0c..444f1d500 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -140,6 +140,7 @@ subroutine summaSolve4kinsol(& USE eval8summa_module,only:eval8summa4kinsol ! DAE/ODE functions USE eval8summa_module,only:eval8summa ! residual of DAE USE computJacob_module,only:computJacob4kinsol ! system Jacobian + USE var_lookup,only:maxvarDecisions ! maximum number of decisions !======= Declarations ========= implicit none @@ -234,7 +235,6 @@ subroutine summaSolve4kinsol(& eqns_data%firstSubStep = firstSubStep eqns_data%computeVegFlux = computeVegFlux eqns_data%scalarSolution = scalarSolution - eqns_data%model_decisions = model_decisions eqns_data%deriv_data = deriv_data eqns_data%lookup_data = lookup_data eqns_data%type_data = type_data @@ -248,12 +248,15 @@ subroutine summaSolve4kinsol(& eqns_data%flux_data = flux_data eqns_data%deriv_data = deriv_data eqns_data%ixSaturation = ixSaturation + eqns_data%firstStateIteration = .true. ! allocate space and fill + allocate( eqns_data%model_decisions(maxvarDecisions) ); eqns_data%model_decisions = model_decisions allocate( eqns_data%fScale(nState) ); eqns_data%fScale = fScale allocate( eqns_data%xScale(nState) ); eqns_data%xScale = xScale allocate( eqns_data%sMul(nState) ); eqns_data%sMul = sMul allocate( eqns_data%dMat(nState) ); eqns_data%dMat = dMat + allocate( eqns_data%stateVecPrev(nState) ); eqns_data%stateVecPrev = stateVecInit ! allocate space for other variables if(model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel)then @@ -338,8 +341,8 @@ subroutine summaSolve4kinsol(& !****************************** Main Solver ********************************************** ! Call KINSol to solve problem with choice of solver, linesearch or Picard - retval = FKINSol(kinsol_mem, sunvec_y, KIN_LINESEARCH, sunvec_xscale, sunvec_fscale) - !retval = FKINSol(kinsol_mem, sunvec_y, KIN_PICARD, sunvec_xscale, sunvec_fscale) + !retval = FKINSol(kinsol_mem, sunvec_y, KIN_LINESEARCH, sunvec_xscale, sunvec_fscale) + retval = FKINSol(kinsol_mem, sunvec_y, KIN_PICARD, sunvec_xscale, sunvec_fscale) if( retvalr < 0 )then kinsolSucceeds = .false. @@ -383,6 +386,7 @@ subroutine summaSolve4kinsol(& deallocate( eqns_data%xScale ) deallocate( eqns_data%sMul ) deallocate( eqns_data%dMat ) + deallocate( eqns_data%stateVecPrev ) deallocate( eqns_data%dBaseflow_dMatric ) deallocate( eqns_data%fluxVec ) deallocate( eqns_data%resSink ) diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 5c8fbc864..72e027f23 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -686,10 +686,6 @@ subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fl ! check convergence converged = checkConv(resVecNew,xInc,stateVecNew) - !write(*,'(a,1x,2(L1,1x),5(e20.8,1x))') 'bracketsDefined, doBisection, xMin, xMax, stateVecTrial, stateVecNew, xInc = ', & - ! bracketsDefined, doBisection, xMin, xMax, stateVecTrial, stateVecNew, xInc - !print*, 'PAUSE'; read(*,*) - end subroutine safeRootfinder ! ********************************************************************************************************* @@ -743,10 +739,6 @@ subroutine getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,message) xMax = stateVecNew(1) endif - ! print progress - !print*, 'xMin, xMax, stateVecTrial, stateVecNew, resVecNew, xIncrement = ', & - ! xMin, xMax, stateVecTrial, stateVecNew, resVecNew, xIncrement - ! check that the brackets are defined if( .not.ieee_is_nan(xMin) .and. .not.ieee_is_nan(xMax) ) exit @@ -1364,6 +1356,4 @@ end subroutine imposeConstraints end subroutine summaSolve4numrec - - end module summaSolve4numrec_module diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 06b848f96..d85954972 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -217,7 +217,7 @@ subroutine systemSolv(& ! ------------------------------------------------------------------------------------------------------ ! * model solver ! ------------------------------------------------------------------------------------------------------ - logical(lgt),parameter :: forceFullMatrix=.false. ! flag to force the use of the full Jacobian matrix + logical(lgt),parameter :: forceFullMatrix=.true. ! flag to force the use of the full Jacobian matrix integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) type(var_dlength) :: flux_init ! model fluxes at the start of the time step real(rkind),allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! NOTE: allocatable, since not always needed From 0f07c53e9f6e58b832e1bd0036201f91358f8943 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 25 Jul 2023 16:57:16 -0500 Subject: [PATCH 0803/1472] works best with detect_events on and everything else off, but still failing quite a lot --- build/source/engine/eval8summa.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 7201e9d52..36df2b31e 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -848,9 +848,9 @@ subroutine imposeConstraints(indx_data, prog_data, mpar_data, stateVec, stateVec integer(i4b) :: iLayer ! index of model layer ! choice of constraints to impose logical(lgt),parameter :: small_delTemp=.false. ! flag to constain temperature change to be less than zMaxTempIncrement - logical(lgt),parameter :: detect_events=.false. ! flag to do freezing point event detection and cross-over with epsT + logical(lgt),parameter :: detect_events=.true. ! flag to do freezing point event detection and cross-over with epsT logical(lgt),parameter :: positive_wat=.false. ! flag to force water to not go negative - real(qp),dimension(nState) :: junk + ! ----------------------------------------------------------------------------------------------------- ! associate variables with indices of model state variables associate(& From 47d33249047ad73696f93bba38ede197581d4e89 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 26 Jul 2023 09:47:22 -0500 Subject: [PATCH 0804/1472] seems to work better with a bigger step over the event --- build/source/engine/eval8summa.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 36df2b31e..efa95e0c2 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -839,7 +839,7 @@ subroutine imposeConstraints(indx_data, prog_data, mpar_data, stateVec, stateVec real(rkind) :: xPsi00 ! matric head after applying the iteration increment (m) real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) real(rkind) :: critDiff ! temperature difference from critical (K) - real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical (K) + real(rkind),parameter :: epsT=1.e-3_rkind ! small interval above/below critical (K) real(rkind),parameter :: zMaxTempIncrement=1._rkind ! maximum temperature increment (K) ! indices of model state variables integer(i4b) :: iState ! index of state within a specific variable type From 5ca363de1adec539020933924fee77688275827e Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 26 Jul 2023 02:15:36 -0600 Subject: [PATCH 0805/1472] Added summa/build/cmake_build build object directory to .gitignore file. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 8d478f773..caeff16e1 100755 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,5 @@ Makefile-* # site directory from mkdocs site/ Laugh-Tests/ +# cmake_build containing cmake build objects +build/cmake_build From 60c655c728f63cd0379b166b8ef7070c81ece176 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 26 Jul 2023 02:18:10 -0600 Subject: [PATCH 0806/1472] Added build.pc.bash for building SUMMA on a pc using Bash and generalized CMakeLists.txt to handle more pc platforms. --- build/cmake/CMakeLists.txt | 19 ++++++++++++++----- build/cmake/build.pc.bash | 20 ++++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) create mode 100755 build/cmake/build.pc.bash diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 2adfc4c42..5ca9b461b 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -164,11 +164,20 @@ if(CMAKE_BUILD_TYPE MATCHES Cluster) else() message("\nBuilding for personal computer") - set(SDKROOT "$(xcrun --show-sdk-path)") - - link_directories(/opt/local/lib) - set(INCLUDES /opt/local/include /opt/local/lib) - set(LIBRARIES SUMMA_NOAHMP -llapack -lgfortran -lnetcdff -lnetcdf) + #set(SDKROOT "$(xcrun --show-sdk-path)") # appears to be unused and specifically for MacOS so commented out + + ## Original Block -- possibly for MacOS -- attempted to generalize things for additional platforms in the next block + #link_directories(/opt/local/lib) + #set(INCLUDES /opt/local/include /opt/local/lib) + #set(LIBRARIES SUMMA_NOAHMP -llapack -lgfortran -lnetcdff -lnetcdf) + + # Set Links and Libraries using Environment Variables + set(LINK_DIRS $ENV{LINK_DIRS}) # set link directories from environment variable + set(INCLUDES_DIRS $ENV{INCLUDES_DIRS}) # set directories for INCLUDES from environment variable + set(LIBRARY_LINKS $ENV{LIBRARY_LINKS}) # set list of library links from environment variable + link_directories(${LINK_DIRS}) + set(INCLUDES ${INCLUDES_DIRS}) + set(LIBRARIES SUMMA_NOAHMP ${LIBRARY_LINKS}) if(CMAKE_BUILD_TYPE MATCHES Sundials) link_directories(${DIR_SUNDIALS}/lib) diff --git a/build/cmake/build.pc.bash b/build/cmake/build.pc.bash new file mode 100755 index 000000000..62304b585 --- /dev/null +++ b/build/cmake/build.pc.bash @@ -0,0 +1,20 @@ +#!/bin/bash + +# build SUMMA on a pc using Bash, from cmake directory run this as ./build.ubuntu.bash +# Environment variables that must be set for cmake: FC, LINK_DIRS, INCLUDES_DIRS, and LIBRARY_LINKS +# Ubuntu Example: +#export FC=gfortran # Fortran compiler family +#export LINK_DIRS=/usr/local/lib # Link directories for cmake +#export INCLUDES_DIRS='/usr/include;/usr/local/include' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) +#export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) + +# Mac Example: +#export FC=gfortran # Fortran compiler family +#export LINK_DIRS=/opt/local/lib # Link directories for cmake +#export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) +#export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) + + +cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=BE +cmake --build ../cmake_build --target all + From 27e615592aff1760194e9b573938cf5c3b95cb0a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 27 Jul 2023 13:54:27 -0500 Subject: [PATCH 0807/1472] actors build scripts --- build/makefiles/Actors/build_cmakeActors.sh | 7 +++++++ .../{build_cmakeSundials => build_cmakeSundials.sh} | 6 +++--- 2 files changed, 10 insertions(+), 3 deletions(-) create mode 100755 build/makefiles/Actors/build_cmakeActors.sh rename build/makefiles/Sundials/{build_cmakeSundials => build_cmakeSundials.sh} (70%) diff --git a/build/makefiles/Actors/build_cmakeActors.sh b/build/makefiles/Actors/build_cmakeActors.sh new file mode 100755 index 000000000..158722824 --- /dev/null +++ b/build/makefiles/Actors/build_cmakeActors.sh @@ -0,0 +1,7 @@ +# from {$maindir}/actor-framework, run +# cp ../../summa/build/summa/build/makefiles/Actors/build_cmakeActors.sh build_cmake +# mkdir install +# run script from the actor-framework directory with ./build_cmake + +setenv CXX "g++" +./configure --prefix=../actor-framework/install \ No newline at end of file diff --git a/build/makefiles/Sundials/build_cmakeSundials b/build/makefiles/Sundials/build_cmakeSundials.sh similarity index 70% rename from build/makefiles/Sundials/build_cmakeSundials rename to build/makefiles/Sundials/build_cmakeSundials.sh index e015363c6..cb359d274 100755 --- a/build/makefiles/Sundials/build_cmakeSundials +++ b/build/makefiles/Sundials/build_cmakeSundials.sh @@ -1,7 +1,7 @@ -# from ../../sundials/builddir, run -# cp ../../summa/build/build_cmakeSundials build_cmake +# from {$maindir}/sundials/builddir, run +# cp ../../summa/build/makefiles/Sundials/build_cmakeSundials.sh build_cmake # run script from the builddir directory with ./build_cmake -# Note, using -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF because modified constraint code and did not modify examples +# Note, using -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF, if want to run examples should change cmake ../../sundials-6.3.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=../../sundials/instdir -DEXAMPLES_INSTALL_PATH=../../sundials/instdir/examples From 89a674ecc07ec4b89f7f7790ce74e70acefadf58 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 28 Jul 2023 10:15:54 -0500 Subject: [PATCH 0808/1472] fixing Actors CMakeLists --- build/cmake/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 2adfc4c42..5f42a3958 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -176,7 +176,7 @@ else() set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran) endif() - # Actors is on Cluster, if using on personal computer might need to link directory-- not working on Mac right now + # If using on personal computer need to link directory-- not working on Mac right now to compile with Actors if(CMAKE_BUILD_TYPE MATCHES Actors) set(DIR_ACTORS "") foreach(dir IN ITEMS @@ -186,7 +186,7 @@ else() "${PARENT_DIR}../../SummaActors/actor-framework/install" "../../../../SummaActors/actor-framework/install") #look in same head directory as summa or up to another installation if(EXISTS ${dir}) - set(DIR_SUNDIALS ${dir}) + set(DIR_ACTORS ${dir}) message("\nFound Actors directory at ${DIR_ACTORS}") break() endif() From 32730e4ff29a9a995659858d2fb0774bb3b94204 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 28 Jul 2023 10:45:48 -0500 Subject: [PATCH 0809/1472] fixing Actors build --- build/makefiles/Actors/build_cmakeActors.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/makefiles/Actors/build_cmakeActors.sh b/build/makefiles/Actors/build_cmakeActors.sh index 158722824..67fd92152 100755 --- a/build/makefiles/Actors/build_cmakeActors.sh +++ b/build/makefiles/Actors/build_cmakeActors.sh @@ -4,4 +4,4 @@ # run script from the actor-framework directory with ./build_cmake setenv CXX "g++" -./configure --prefix=../actor-framework/install \ No newline at end of file +./configure --prefix=../install \ No newline at end of file From b9e5e407ae81161d9374d84468e7eb30d081f748 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 29 Jul 2023 04:57:57 -0600 Subject: [PATCH 0810/1472] Minor cleanup of Bash build script for PCs. --- build/cmake/build.pc.bash | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/cmake/build.pc.bash b/build/cmake/build.pc.bash index 62304b585..249998ec6 100755 --- a/build/cmake/build.pc.bash +++ b/build/cmake/build.pc.bash @@ -2,6 +2,7 @@ # build SUMMA on a pc using Bash, from cmake directory run this as ./build.ubuntu.bash # Environment variables that must be set for cmake: FC, LINK_DIRS, INCLUDES_DIRS, and LIBRARY_LINKS + # Ubuntu Example: #export FC=gfortran # Fortran compiler family #export LINK_DIRS=/usr/local/lib # Link directories for cmake @@ -13,8 +14,7 @@ #export LINK_DIRS=/opt/local/lib # Link directories for cmake #export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) #export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) - - -cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=BE + +cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=BE_Debug cmake --build ../cmake_build --target all From b4caca0b33fecafacf2e3d2e141e12dc9ec1a5c3 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 29 Jul 2023 05:06:50 -0600 Subject: [PATCH 0811/1472] Added new compiler options for Debug and Release builds. --- build/cmake/CMakeLists.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 5ca9b461b..1fe7166a8 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -104,14 +104,14 @@ endif() if(CMAKE_BUILD_TYPE MATCHES Debug) message("\nSetting Debug Options") add_compile_definitions(DEBUG) - set(FLAGS_NOAH -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) - set(FLAGS_ALL -g -O0 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp) - set(FLAGS_CXX -g -O0 -Wfatal-errors -std=c++17) + set(FLAGS_NOAH -g -O0 -fbounds-check -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) + set(FLAGS_ALL -g -O0 -fbounds-check -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp) + set(FLAGS_CXX -g -O0 -fbounds-check -Wfatal-errors -std=c++17) else() message("\nSetting Release Options") - set(FLAGS_NOAH -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) - set(FLAGS_ALL -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp) - set(FLAGS_CXX -O3 -Wfatal-errors -std=c++17) + set(FLAGS_NOAH -O3 -flto -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) + set(FLAGS_ALL -O3 -flto -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp) + set(FLAGS_CXX -O3 -flto -Wfatal-errors -std=c++17) endif() # Find Sundials if exists, add path here if FATAL_ERROR "Did not find Sundials directory" From 7d95d273f3822318268e2123243ab34b4236be82 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 29 Jul 2023 05:11:13 -0600 Subject: [PATCH 0812/1472] Fixed issue for SUNDIALS parameters in paramFallback; now only assigned for the local column parameters case. --- build/source/engine/read_pinit.f90 | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/build/source/engine/read_pinit.f90 b/build/source/engine/read_pinit.f90 index be6a3f156..34be9581c 100644 --- a/build/source/engine/read_pinit.f90 +++ b/build/source/engine/read_pinit.f90 @@ -131,14 +131,16 @@ subroutine read_pinit(filenm,isLocal,mpar_meta,parFallback,err,message) end do ! (looping through lines in the file) ! add these defaults for backwards compatibility pre Sundials - if (parFallback(iLookPARAM%be_steps)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%be_steps)%default_val = 1._rkind - end if - if (parFallback(iLookPARAM%relErrTol_ida)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%relErrTol_ida)%default_val = 1.e-6_rkind - end if - if (parFallback(iLookPARAM%absErrTol_ida)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%absErrTol_ida)%default_val = 1.e-6_rkind + if (isLocal) then ! dealing with parameters for local column -- fix this !!!! + if (parFallback(iLookPARAM%be_steps)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%be_steps)%default_val = 1._rkind + end if + if (parFallback(iLookPARAM%relErrTol_ida)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%relErrTol_ida)%default_val = 1.e-6_rkind + end if + if (parFallback(iLookPARAM%absErrTol_ida)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%absErrTol_ida)%default_val = 1.e-6_rkind + end if end if ! check we have populated all variables From ace92fbacc447128e5351bdafabf544106ee8934 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 29 Jul 2023 05:12:46 -0600 Subject: [PATCH 0813/1472] Fixed array bounds/syntax issue for certain parameter checks. --- build/source/engine/paramCheck.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/paramCheck.f90 b/build/source/engine/paramCheck.f90 index b038e2fdf..96c2905e2 100644 --- a/build/source/engine/paramCheck.f90 +++ b/build/source/engine/paramCheck.f90 @@ -132,7 +132,7 @@ subroutine paramCheck(mpar_data,err,message) endif ! check that the maximum transpiration limit is within bounds - if( any(critSoilTranspire > theta_sat) .or. any(critSoilTranspire < theta_res) )then + if( any(critSoilTranspire(1) > theta_sat) .or. any(critSoilTranspire(1) < theta_res) )then print*, 'theta_res = ', theta_res print*, 'theta_sat = ', theta_sat print*, 'critSoilTranspire = ', critSoilTranspire @@ -142,7 +142,7 @@ subroutine paramCheck(mpar_data,err,message) end if ! check that the soil wilting point is within bounds - if( any(critSoilWilting > theta_sat) .or. any(critSoilWilting < theta_res) )then + if( any(critSoilWilting(1) > theta_sat) .or. any(critSoilWilting(1) < theta_res) )then print*, 'theta_res = ', theta_res print*, 'theta_sat = ', theta_sat print*, 'critSoilWilting = ', critSoilWilting From 43add066ba471abb5c3775e0777dcb8e7dbd7866 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 31 Jul 2023 13:08:31 +0900 Subject: [PATCH 0814/1472] the aquifer storage should be initialized at 1 as in previous Summa versions --- build/source/driver/summa_restart.f90 | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/build/source/driver/summa_restart.f90 b/build/source/driver/summa_restart.f90 index e58e97e7d..ea1d657ad 100644 --- a/build/source/driver/summa_restart.f90 +++ b/build/source/driver/summa_restart.f90 @@ -192,6 +192,8 @@ subroutine summa_readRestart(summa1_struc, err, message) ! There are two options for groundwater: ! (1) where groundwater is included in the local column (i.e., the HRUs); and ! (2) where groundwater is included for the single basin (i.e., the GRUS, where multiple HRUS drain into a GRU). + ! Start with this full, since easier to spin up by draining than filling (filling we need to wait for precip). + ! There will be an initial pulse of water, but the spinup runs should be discarded. ! For water balance calculations it is important to ensure that the local aquifer storage is zero if groundwater is treated as a basin-average state variable (singleBasin); ! and ensure that basin-average aquifer storage is zero when groundwater is included in the local columns (localColumn). @@ -203,13 +205,13 @@ subroutine summa_readRestart(summa1_struc, err, message) case(localColumn) bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 0._rkind ! set to zero to be clear that there is no basin-average aquifer storage in this configuration do iHRU=1,gru_struc(iGRU)%hruCount - progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarAquiferStorage)%dat(1) = 0._rkind ! 1.0 was default but gives runoff pulse at beginning of simulation so now 0.0 - end do + progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarAquiferStorage)%dat(1) = 1._rkind + end do ! the local column aquifer storage is not used if the groundwater is basin-average ! (i.e., where multiple HRUs drain to a basin-average aquifer) case(singleBasin) - bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 0._rkind ! 1.0 was default but gives runoff pulse at beginning of simulation so now 0.0 + bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 1._rkind do iHRU=1,gru_struc(iGRU)%hruCount progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarAquiferStorage)%dat(1) = 0._rkind ! set to zero to be clear that there is no local aquifer storage in this configuration end do From 54432992284a1984736b3fa8f33355750fca0a97 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 1 Aug 2023 19:56:59 +0900 Subject: [PATCH 0815/1472] fixing actors cmake --- build/cmake/CMakeLists.txt | 4 ++-- build/makefiles/Actors/build_cmakeActors.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 5f42a3958..610596bbd 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -195,8 +195,8 @@ else() message(FATAL_ERROR "Did not find Actors directory, edit CMakeLists.txt to add path") endif() link_directories(${DIR_ACTORS}/lib) - set(INC_ACTORS ${CAF_INCLUDES} ${PARENT_DIR}/build/includes/global ${PARENT_DIR}/build/includes/summa_actor ${PARENT_DIR}/build/includes/gru_actor ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) - set(LIB_ACTORS ${CAF_LIBRARIES} -lcaf_core -lcaf_io) + set(INC_ACTORS ${DIR_ACTORS}/include ${PARENT_DIR}/build/includes/global ${PARENT_DIR}/build/includes/summa_actor ${PARENT_DIR}/build/includes/gru_actor ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) + set(LIB_ACTORS ${DIR_ACTORS}/lib -lcaf_core -lcaf_io) endif() endif() diff --git a/build/makefiles/Actors/build_cmakeActors.sh b/build/makefiles/Actors/build_cmakeActors.sh index 67fd92152..75698d166 100755 --- a/build/makefiles/Actors/build_cmakeActors.sh +++ b/build/makefiles/Actors/build_cmakeActors.sh @@ -1,7 +1,7 @@ # from {$maindir}/actor-framework, run # cp ../../summa/build/summa/build/makefiles/Actors/build_cmakeActors.sh build_cmake -# mkdir install # run script from the actor-framework directory with ./build_cmake +# run `make`, then `make install` setenv CXX "g++" ./configure --prefix=../install \ No newline at end of file From fc7186361e01309ae1baeeb82fccb624f42f57c1 Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 1 Aug 2023 06:34:00 -0600 Subject: [PATCH 0816/1472] Fix for array bounds issue for hydraulic conductivity in var_derive.f90. --- build/source/engine/var_derive.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/var_derive.f90 b/build/source/engine/var_derive.f90 index d5b12023f..78786dc6b 100644 --- a/build/source/engine/var_derive.f90 +++ b/build/source/engine/var_derive.f90 @@ -343,14 +343,14 @@ subroutine satHydCond(mpar_data,indx_data,prog_data,flux_data,err,message) end select ! check that the hydraulic conductivity for macropores is greater than for micropores - if(iLayer > 0)then + if (iLayer > nSnow) then if( mLayerSatHydCondMP(iLayer-nSnow) < mLayerSatHydCond(iLayer-nSnow) )then write(*,'(2(a,e12.6),a,i0)')trim(message)//'WARNING: hydraulic conductivity for macropores [', mLayerSatHydCondMP(iLayer-nSnow), & '] is less than the hydraulic conductivity for micropores [', mLayerSatHydCond(iLayer-nSnow), & ']: resetting macropore conductivity to equal micropore value. Layer = ', iLayer mLayerSatHydCondMP(iLayer-nSnow) = mLayerSatHydCond(iLayer-nSnow) endif ! if mLayerSatHydCondMP < mLayerSatHydCond - endif ! if iLayer>0 + end if ! if iLayer > nSnow end do ! looping through soil layers end associate From 58a742d8b5bd4310c566f6b054707cebcff96a5a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 2 Aug 2023 13:37:27 +0900 Subject: [PATCH 0817/1472] Revert "the aquifer storage should be initialized at 1 as in previous Summa versions" This reverts commit 43add066ba471abb5c3775e0777dcb8e7dbd7866. --- build/source/driver/summa_restart.f90 | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/build/source/driver/summa_restart.f90 b/build/source/driver/summa_restart.f90 index ea1d657ad..e58e97e7d 100644 --- a/build/source/driver/summa_restart.f90 +++ b/build/source/driver/summa_restart.f90 @@ -192,8 +192,6 @@ subroutine summa_readRestart(summa1_struc, err, message) ! There are two options for groundwater: ! (1) where groundwater is included in the local column (i.e., the HRUs); and ! (2) where groundwater is included for the single basin (i.e., the GRUS, where multiple HRUS drain into a GRU). - ! Start with this full, since easier to spin up by draining than filling (filling we need to wait for precip). - ! There will be an initial pulse of water, but the spinup runs should be discarded. ! For water balance calculations it is important to ensure that the local aquifer storage is zero if groundwater is treated as a basin-average state variable (singleBasin); ! and ensure that basin-average aquifer storage is zero when groundwater is included in the local columns (localColumn). @@ -205,13 +203,13 @@ subroutine summa_readRestart(summa1_struc, err, message) case(localColumn) bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 0._rkind ! set to zero to be clear that there is no basin-average aquifer storage in this configuration do iHRU=1,gru_struc(iGRU)%hruCount - progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarAquiferStorage)%dat(1) = 1._rkind - end do + progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarAquiferStorage)%dat(1) = 0._rkind ! 1.0 was default but gives runoff pulse at beginning of simulation so now 0.0 + end do ! the local column aquifer storage is not used if the groundwater is basin-average ! (i.e., where multiple HRUs drain to a basin-average aquifer) case(singleBasin) - bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 1._rkind + bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 0._rkind ! 1.0 was default but gives runoff pulse at beginning of simulation so now 0.0 do iHRU=1,gru_struc(iGRU)%hruCount progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarAquiferStorage)%dat(1) = 0._rkind ! set to zero to be clear that there is no local aquifer storage in this configuration end do From edf08de5264d2b206f310118159ef5f503fcc8a6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 2 Aug 2023 13:37:57 +0900 Subject: [PATCH 0818/1472] fix build files --- build/cmake/build.mac.bash | 11 +++++++++-- build/cmake/build.pc.bash | 20 +++++++------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/build/cmake/build.mac.bash b/build/cmake/build.mac.bash index dc929bf1f..4bbb60f85 100755 --- a/build/cmake/build.mac.bash +++ b/build/cmake/build.mac.bash @@ -1,6 +1,13 @@ #!/bin/bash - -# build on Mac, from cmake directory run this as ./build.mac.bash + +# build SUMMA on a Mac using Bash, from cmake directory run this as ./build.mac.bash +# Environment variables that must be set for cmake: FC, LINK_DIRS, INCLUDES_DIRS, and LIBRARY_LINKS + +# Mac Example using MacPorts: +export FC=gfortran # Fortran compiler family +export LINK_DIRS=/opt/local/lib # Link directories for cmake +export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) +export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials cmake --build ../cmake_build --target all diff --git a/build/cmake/build.pc.bash b/build/cmake/build.pc.bash index 249998ec6..8ad3cff03 100755 --- a/build/cmake/build.pc.bash +++ b/build/cmake/build.pc.bash @@ -1,20 +1,14 @@ #!/bin/bash - -# build SUMMA on a pc using Bash, from cmake directory run this as ./build.ubuntu.bash + +# build SUMMA on a PC using Bash, from cmake directory run this as ./build.pc.bash # Environment variables that must be set for cmake: FC, LINK_DIRS, INCLUDES_DIRS, and LIBRARY_LINKS -# Ubuntu Example: -#export FC=gfortran # Fortran compiler family -#export LINK_DIRS=/usr/local/lib # Link directories for cmake -#export INCLUDES_DIRS='/usr/include;/usr/local/include' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) -#export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) +# PC Example using Ubuntu +export FC=gfortran # Fortran compiler family +export LINK_DIRS=/usr/local/lib # Link directories for cmake +export INCLUDES_DIRS='/usr/include;/usr/local/include' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) +export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) -# Mac Example: -#export FC=gfortran # Fortran compiler family -#export LINK_DIRS=/opt/local/lib # Link directories for cmake -#export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) -#export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) - cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=BE_Debug cmake --build ../cmake_build --target all From 8de458f9788ee704939b5bcf2fe7f545ee5c2215 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 2 Aug 2023 22:14:50 +0900 Subject: [PATCH 0819/1472] first timestep weird, do not do any statistics on it --- utils/timeseries_to_statistics.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 34116a26e..4d3c7d2e6 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -104,18 +104,20 @@ def run_loop(file,bench): #dat['averageRoutedRunoff'] = dat['averageRoutedRunoff'].fillna(0) #ben['averageRoutedRunoff'] = ben['averageRoutedRunoff'].fillna(0) - # get rid of gru dimension, assuming they are same as the often are (everything now as hruId) + # get rid of gru dimension, assuming hru and gru are one to one (everything now as hruId) dat = dat.drop_vars(['hruId','gruId']) m = dat.drop_dims('hru') m = m.rename({'gru': 'hru'}) dat = dat.drop_dims('gru') dat = xr.merge([dat,m]) + dat = dat.where(dat.time!=dat.time[0],drop=True) #first timestep weird ben = ben.drop_vars(['hruId','gruId']) m = ben.drop_dims('hru') m = m.rename({'gru': 'hru'}) ben = ben.drop_dims('gru') ben = xr.merge([ben,m]) + ben = ben.where(ben.time!=ben.time[0],drop=True) #first timestep weird diff = dat - ben the_hru = np.array(ben['hru']) From 5f59b4d128dd4841e7d663d4e8c8d7afdc42610c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 2 Aug 2023 22:15:16 +0900 Subject: [PATCH 0820/1472] Revert "Revert "the aquifer storage should be initialized at 1 as in previous Summa versions"" This reverts commit 58a742d8b5bd4310c566f6b054707cebcff96a5a. --- build/source/driver/summa_restart.f90 | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/build/source/driver/summa_restart.f90 b/build/source/driver/summa_restart.f90 index e58e97e7d..ea1d657ad 100644 --- a/build/source/driver/summa_restart.f90 +++ b/build/source/driver/summa_restart.f90 @@ -192,6 +192,8 @@ subroutine summa_readRestart(summa1_struc, err, message) ! There are two options for groundwater: ! (1) where groundwater is included in the local column (i.e., the HRUs); and ! (2) where groundwater is included for the single basin (i.e., the GRUS, where multiple HRUS drain into a GRU). + ! Start with this full, since easier to spin up by draining than filling (filling we need to wait for precip). + ! There will be an initial pulse of water, but the spinup runs should be discarded. ! For water balance calculations it is important to ensure that the local aquifer storage is zero if groundwater is treated as a basin-average state variable (singleBasin); ! and ensure that basin-average aquifer storage is zero when groundwater is included in the local columns (localColumn). @@ -203,13 +205,13 @@ subroutine summa_readRestart(summa1_struc, err, message) case(localColumn) bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 0._rkind ! set to zero to be clear that there is no basin-average aquifer storage in this configuration do iHRU=1,gru_struc(iGRU)%hruCount - progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarAquiferStorage)%dat(1) = 0._rkind ! 1.0 was default but gives runoff pulse at beginning of simulation so now 0.0 - end do + progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarAquiferStorage)%dat(1) = 1._rkind + end do ! the local column aquifer storage is not used if the groundwater is basin-average ! (i.e., where multiple HRUs drain to a basin-average aquifer) case(singleBasin) - bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 0._rkind ! 1.0 was default but gives runoff pulse at beginning of simulation so now 0.0 + bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 1._rkind do iHRU=1,gru_struc(iGRU)%hruCount progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarAquiferStorage)%dat(1) = 0._rkind ! set to zero to be clear that there is no local aquifer storage in this configuration end do From be93a73707108e908091eb41300da42daca61d16 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 5 Aug 2023 00:27:28 +0900 Subject: [PATCH 0821/1472] spaces --- build/source/engine/run_oneGRU.f90 | 2 +- build/source/engine/run_oneHRU.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/run_oneGRU.f90 b/build/source/engine/run_oneGRU.f90 index 5002878a8..aa67cf5e0 100644 --- a/build/source/engine/run_oneGRU.f90 +++ b/build/source/engine/run_oneGRU.f90 @@ -136,7 +136,7 @@ subroutine run_oneGRU(& integer(i4b) :: nSnow ! number of snow layers integer(i4b) :: nSoil ! number of soil layers integer(i4b) :: nLayers ! total number of layers - real(rkind) :: fracHRU ! fractional area of a given HRU (-) + real(rkind) :: fracHRU ! fractional area of a given HRU (-) logical(lgt) :: computeVegFluxFlag ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) ! initialize error control diff --git a/build/source/engine/run_oneHRU.f90 b/build/source/engine/run_oneHRU.f90 index ebf6b9b51..6a6cbb30b 100644 --- a/build/source/engine/run_oneHRU.f90 +++ b/build/source/engine/run_oneHRU.f90 @@ -119,7 +119,7 @@ subroutine run_oneHRU(& ! model control integer(i4b) , intent(in) :: hru_nc ! hru index in netcdf integer(8) , intent(in) :: hruId ! hruId - real(rkind) , intent(inout) :: dt_init ! used to initialize the length of the sub-step for each HRU + real(rkind) , intent(inout) :: dt_init ! used to initialize the length of the sub-step for each HRU logical(lgt) , intent(inout) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (false=no, true=yes) integer(i4b) , intent(inout) :: nSnow,nSoil,nLayers ! number of snow and soil layers ! data structures (input) From 7c43917f652279394e5843221316b95941bbc456 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 5 Aug 2023 19:55:30 +0900 Subject: [PATCH 0822/1472] adding case_study files --- build/source/engine/mDecisions.f90 | 4 +- case_study/base_settings/GENPARM.TBL | 36 +++ case_study/base_settings/MPTABLE.TBL | 288 ++++++++++++++++++++ case_study/base_settings/SOILPARM.TBL | 59 ++++ case_study/base_settings/VEGPARM.TBL | 211 ++++++++++++++ case_study/base_settings/localParamInfo.txt | 233 ++++++++++++++++ 6 files changed, 829 insertions(+), 2 deletions(-) create mode 100644 case_study/base_settings/GENPARM.TBL create mode 100644 case_study/base_settings/MPTABLE.TBL create mode 100644 case_study/base_settings/SOILPARM.TBL create mode 100644 case_study/base_settings/VEGPARM.TBL create mode 100644 case_study/base_settings/localParamInfo.txt diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index 9d8a9f1e2..4826a3932 100755 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -604,8 +604,8 @@ subroutine mDecisions(err,message) ! choice of method to combine and sub-divide snow layers select case(trim(model_decisions(iLookDECISIONS%snowLayers)%cDecision)) - case('jrdn1991'); model_decisions(iLookDECISIONS%snowLayers)%iDecision = sameRulesAllLayers ! SNTHERM option: same combination/sub-dividion rules applied to all layers - case('CLM_2010'); model_decisions(iLookDECISIONS%snowLayers)%iDecision = rulesDependLayerIndex ! CLM option: combination/sub-dividion rules depend on layer index + case('jrdn1991'); model_decisions(iLookDECISIONS%snowLayers)%iDecision = sameRulesAllLayers ! SNTHERM option: same combination/sub-division rules applied to all layers + case('CLM_2010'); model_decisions(iLookDECISIONS%snowLayers)%iDecision = rulesDependLayerIndex ! CLM option: combination/sub-division rules depend on layer index case default err=10; message=trim(message)//"unknown option for combination/sub-division of snow layers [option="//trim(model_decisions(iLookDECISIONS%snowLayers)%cDecision)//"]"; return end select diff --git a/case_study/base_settings/GENPARM.TBL b/case_study/base_settings/GENPARM.TBL new file mode 100644 index 000000000..05c460196 --- /dev/null +++ b/case_study/base_settings/GENPARM.TBL @@ -0,0 +1,36 @@ +General Parameters +SLOPE_DATA +9 +0.1 +0.6 +1.0 +0.35 +0.55 +0.8 +0.63 +0.0 +0.0 +SBETA_DATA +-2.0 +FXEXP_DATA +2.0 +CSOIL_DATA +2.00E+6 +SALP_DATA +2.6 +REFDK_DATA +2.0E-6 +REFKDT_DATA +1.0 +FRZK_DATA +0.15 +ZBOT_DATA +-8.0 +CZIL_DATA +0.1 +SMLOW_DATA +0.5 +SMHIGH_DATA +3.0 +LVCOEF_DATA +0.5 diff --git a/case_study/base_settings/MPTABLE.TBL b/case_study/base_settings/MPTABLE.TBL new file mode 100644 index 000000000..97990c7e4 --- /dev/null +++ b/case_study/base_settings/MPTABLE.TBL @@ -0,0 +1,288 @@ +&noah_mp_usgs_veg_categories + VEG_DATASET_DESCRIPTION = "USGS" + NVEG = 27 +/ +&noah_mp_usgs_parameters + ! NVEG = 27 + ! 1: Urban and Built-Up Land + ! 2: Dryland Cropland and Pasture + ! 3: Irrigated Cropland and Pasture + ! 4: Mixed Dryland/Irrigated Cropland and Pasture + ! 5: Cropland/Grassland Mosaic + ! 6: Cropland/Woodland Mosaic + ! 7: Grassland + ! 8: Shrubland + ! 9: Mixed Shrubland/Grassland + ! 10: Savanna + ! 11: Deciduous Broadleaf Forest + ! 12: Deciduous Needleleaf Forest + ! 13: Evergreen Broadleaf Forest + ! 14: Evergreen Needleleaf Forest + ! 15: Mixed Forest + ! 16: Water Bodies + ! 17: Herbaceous Wetland + ! 18: Wooded Wetland + ! 19: Barren or Sparsely Vegetated + ! 20: Herbaceous Tundra + ! 21: Wooded Tundra + ! 22: Mixed Tundra + ! 23: Bare Ground Tundra + ! 24: Snow or Ice + ! 25: Playa + ! 26: Lava + ! 27: White Sand + + ISURBAN = 1 + ISWATER = 16 + ISBARREN = 19 + ISSNOW = 24 + EBLFOREST = 13 + + !--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + ! 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 + !--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + CH2OP = 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, + DLEAF = 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, + Z0MVT = 1.00, 0.15, 0.15, 0.15, 0.14, 0.50, 0.12, 0.06, 0.09, 0.50, 0.80, 0.85, 1.10, 1.09, 0.80, 0.00, 0.12, 0.50, 0.00, 0.10, 0.30, 0.20, 0.03, 0.00, 0.01, 0.00, 0.00, + HVT = 15.0, 2.00, 2.00, 2.00, 1.50, 8.00, 1.00, 1.10, 1.10, 10.0, 16.0, 18.0, 20.0, 20.0, 16.0, 0.00, 0.50, 10.0, 0.00, 0.50, 4.00, 2.00, 0.50, 0.00, 0.10, 0.00, 0.00, + HVB = 1.00, 0.10, 0.10, 0.10, 0.10, 0.15, 0.05, 0.10, 0.10, 0.10, 11.5, 7.00, 8.00, 8.50, 10.0, 0.00, 0.05, 0.10, 0.00, 0.10, 0.10, 0.10, 0.10, 0.00, 0.10, 0.00, 0.00, + DEN = 0.01, 25.0, 25.0, 25.0, 25.0, 25.0, 100., 10.0, 10.0, 0.02, 0.10, 0.28, 0.02, 0.28, 0.10, 0.01, 10.0, 0.10, 0.01, 1.00, 1.00, 1.00, 1.00, 0.00, 0.01, 0.01, 0.01, + RC = 1.00, 0.08, 0.08, 0.08, 0.08, 0.08, 0.03, 0.12, 0.12, 3.00, 1.40, 1.20, 3.60, 1.20, 1.40, 0.01, 0.10, 1.40, 0.01, 0.30, 0.30, 0.30, 0.30, 0.00, 0.01, 0.01, 0.01, + + ! Row 1: Vis + ! Row 2: Near IR + RHOL = 0.00, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.07, 0.10, 0.10, 0.10, 0.07, 0.10, 0.07, 0.10, 0.00, 0.11, 0.10, 0.00, 0.10, 0.10, 0.10, 0.10, 0.00, 0.10, 0.00, 0.00, + 0.00, 0.58, 0.58, 0.58, 0.58, 0.58, 0.58, 0.35, 0.45, 0.45, 0.45, 0.35, 0.45, 0.35, 0.45, 0.00, 0.58, 0.45, 0.00, 0.45, 0.45, 0.45, 0.45, 0.00, 0.45, 0.00, 0.00, + + ! Row 1: Vis + ! Row 2: Near IR + RHOS = 0.00, 0.36, 0.36, 0.36, 0.36, 0.36, 0.36, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.00, 0.36, 0.16, 0.00, 0.16, 0.16, 0.16, 0.16, 0.00, 0.16, 0.00, 0.00, + 0.00, 0.58, 0.58, 0.58, 0.58, 0.58, 0.58, 0.39, 0.39, 0.39, 0.39, 0.39, 0.39, 0.39, 0.39, 0.00, 0.58, 0.39, 0.00, 0.39, 0.39, 0.39, 0.39, 0.00, 0.39, 0.00, 0.00, + + ! Row 1: Vis + ! Row 2: Near IR + TAUL = 0.00, 0.07, 0.07, 0.07, 0.07, 0.07, 0.07, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.00, 0.07, 0.05, 0.00, 0.05, 0.05, 0.05, 0.05, 0.00, 0.05, 0.00, 0.00, + 0.00, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.10, 0.10, 0.25, 0.25, 0.10, 0.25, 0.10, 0.25, 0.00, 0.25, 0.25, 0.00, 0.25, 0.25, 0.25, 0.25, 0.00, 0.25, 0.00, 0.00, + + ! Row 1: Vis + ! Row 2: Near IR + TAUS = 0.000, 0.220, 0.220, 0.220, 0.220, 0.220, 0.220, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.000, 0.220, 0.001, 0.000, 0.220, 0.001, 0.001, 0.001, 0.000, 0.001, 0.000, 0.000, + 0.000, 0.380, 0.380, 0.380, 0.380, 0.380, 0.380, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.000, 0.380, 0.001, 0.000, 0.380, 0.001, 0.001, 0.001, 0.000, 0.001, 0.000, 0.000, + + XL = 0.000, -0.30, -0.30, -0.30, -0.30, -0.30, -0.30, 0.010, 0.250, 0.010, 0.250, 0.010, 0.010, 0.010, 0.250, 0.000, -0.30, 0.250, 0.000, -0.30, 0.250, 0.250, 0.250, 0.000, 0.250, 0.000, 0.000, +! CWPVT = 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, + CWPVT = 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, + C3PSN = 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + KC25 = 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, + AKC = 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, + KO25 = 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, + AKO = 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, + AVCMX = 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, + AQE = 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + + LTOVRC= 0.0, 1.2, 1.2, 1.2, 1.2, 1.30, 0.50, 0.65, 0.70, 0.65, 0.55, 0.2, 0.55, 0.5, 0.5, 0.0, 1.4, 1.4, 0.0, 1.2, 1.3, 1.4, 1.0, 0.0, 1.0, 0.0, 0.0, + DILEFC= 0.00, 0.50, 0.50, 0.50, 0.35, 0.20, 0.20, 0.20, 0.50, 0.50, 0.60, 1.80, 0.50, 1.20, 0.80, 0.00, 0.40, 0.40, 0.00, 0.40, 0.30, 0.40, 0.30, 0.00, 0.30, 0.00, 0.00, + DILEFW= 0.00, 0.20, 0.20, 0.20, 0.20, 0.20, 0.10, 0.20, 0.20, 0.50, 0.20, 0.20, 4.00, 0.20, 0.20, 0.00, 0.20, 0.20, 0.00, 0.20, 0.20, 0.20, 0.20, 0.00, 0.20, 0.00, 0.00, + RMF25 = 0.00, 1.00, 1.40, 1.45, 1.45, 1.45, 1.80, 0.26, 0.26, 0.80, 3.00, 4.00, 0.65, 3.00, 3.00, 0.00, 3.20, 3.20, 0.00, 3.20, 3.00, 3.00, 3.00, 0.00, 3.00, 0.00, 0.00, + SLA = 60, 80, 80, 80, 80, 80, 60, 60, 60, 50, 80, 80, 80, 80, 80, 0, 80, 80, 0, 80, 80, 80, 80, 0, 80, 0, 0, + FRAGR = 0.00, 0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.10, 0.20, 0.10, 0.10, 0.00, 0.10, 0.10, 0.10, 0.10, 0.10, 0.10, 0.10, 0.00, 0.10, 0.00, 0.00, + TMIN = 0, 273, 273, 273, 273, 273, 273, 273, 273, 273, 273, 268, 273, 265, 268, 0, 268, 268, 0, 268, 268, 268, 268, 0, 268, 0, 0, + VCMX25= 0.00, 80.0, 80.0, 80.0, 60.0, 70.0, 40.0, 40.0, 40.0, 40.0, 60.0, 60.0, 60.0, 50.0, 55.0, 0.00, 50.0, 50.0, 0.00, 50.0, 50.0, 50.0, 50.0, 0.00, 50.0, 0.00, 0.00, + TDLEF = 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 268, 278, 278, 268, 0, 268, 268, 0, 268, 268, 268, 268, 0, 268, 0, 0, + BP = 1.E15, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 1.E15, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 1.E15, 2.E3, 1.E15, 1.E15, + MP = 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 6., 9., 6., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., + QE25 = 0., 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.00, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.00, 0.06, 0.00, 0.00, + RMS25 = 0.00, 0.10, 0.10, 0.10, 0.10, 0.10, 0.10, 0.10, 0.10, 0.32, 0.10, 0.64, 0.30, 0.90, 0.80, 0.00, 0.10, 0.10, 0.00, 0.10, 0.10, 0.10, 0.00, 0.00, 0.00, 0.00, 0.00, + RMR25 = 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 1.20, 0.00, 0.00, 0.01, 0.01, 0.05, 0.05, 0.36, 0.03, 0.00, 0.00, 0.00, 0.00, 2.11, 2.11, 2.11, 0.00, 0.00, 0.00, 0.00, 0.00, + ARM = 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + FOLNMX= 0.00, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 0.00, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 0.00, 1.5, 0.00, 0.00, + WDPOOL= 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 0.00, 0.00, 1.00, 0.00, 0.00, 1.00, 1.00, 0.00, 0.00, 0.00, 0.00, 0.00, + WRRAT = 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 3.00, 3.00, 3.00, 30.0, 30.0, 30.0, 30.0, 30.0, 0.00, 0.00, 30.0, 0.00, 0.00, 3.00, 3.00, 0.00, 0.00, 0.00, 0.00, 0.00, + MRP = 0.00, 0.23, 0.23, 0.23, 0.23, 0.23, 0.17, 0.19, 0.19, 0.40, 0.40, 0.37, 0.23, 0.37, 0.30, 0.00, 0.17, 0.40, 0.00, 0.17, 0.23, 0.20, 0.00, 0.00, 0.20, 0.00, 0.00, + +! Monthly values, one row for each month: + SAIM = 0.0, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.2, 0.2, 0.3, 0.4, 0.3, 0.5, 0.4, 0.4, 0.0, 0.2, 0.3, 0.0, 0.1, 0.2, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.2, 0.2, 0.3, 0.4, 0.3, 0.5, 0.4, 0.4, 0.0, 0.2, 0.3, 0.0, 0.1, 0.2, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.2, 0.2, 0.3, 0.4, 0.3, 0.5, 0.4, 0.4, 0.0, 0.2, 0.3, 0.0, 0.1, 0.2, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.2, 0.2, 0.3, 0.4, 0.4, 0.5, 0.3, 0.4, 0.0, 0.2, 0.3, 0.0, 0.1, 0.2, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.2, 0.2, 0.2, 0.3, 0.3, 0.3, 0.2, 0.2, 0.3, 0.4, 0.4, 0.5, 0.4, 0.4, 0.0, 0.3, 0.3, 0.0, 0.1, 0.2, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.3, 0.3, 0.3, 0.4, 0.4, 0.4, 0.2, 0.3, 0.4, 0.4, 0.7, 0.5, 0.5, 0.4, 0.0, 0.4, 0.4, 0.0, 0.2, 0.2, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.4, 0.4, 0.4, 0.6, 0.6, 0.8, 0.4, 0.6, 0.8, 0.9, 1.3, 0.5, 0.5, 0.7, 0.0, 0.6, 0.6, 0.0, 0.4, 0.4, 0.4, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.5, 0.5, 0.5, 0.9, 0.9, 1.3, 0.6, 0.9, 1.2, 1.2, 1.2, 0.5, 0.6, 0.8, 0.0, 0.9, 0.9, 0.0, 0.6, 0.6, 0.6, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.4, 0.4, 0.4, 0.7, 1.0, 1.1, 0.8, 1.0, 1.3, 1.6, 1.0, 0.5, 0.6, 1.0, 0.0, 0.7, 1.0, 0.0, 0.7, 0.8, 0.7, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.3, 0.3, 0.3, 0.3, 0.8, 0.4, 0.7, 0.6, 0.7, 1.4, 0.8, 0.5, 0.7, 1.0, 0.0, 0.3, 0.8, 0.0, 0.5, 0.7, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.3, 0.3, 0.3, 0.3, 0.4, 0.4, 0.3, 0.3, 0.4, 0.6, 0.6, 0.5, 0.6, 0.5, 0.0, 0.3, 0.4, 0.0, 0.3, 0.3, 0.3, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.3, 0.3, 0.3, 0.3, 0.3, 0.4, 0.2, 0.3, 0.4, 0.4, 0.5, 0.5, 0.5, 0.4, 0.0, 0.3, 0.4, 0.0, 0.2, 0.2, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, + + LAIM = 0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.4, 0.0, 0.2, 0.3, 0.0, 0.0, 4.5, 4.0, 2.0, 0.0, 0.2, 0.2, 0.0, 0.2, 1.0, 0.6, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.3, 0.0, 0.5, 0.0, 0.3, 0.3, 0.0, 0.0, 4.5, 4.0, 2.0, 0.0, 0.3, 0.3, 0.0, 0.3, 1.0, 0.6, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.3, 0.2, 0.6, 0.2, 0.4, 0.5, 0.3, 0.0, 4.5, 4.0, 2.2, 0.0, 0.3, 0.3, 0.0, 0.3, 1.1, 0.7, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.4, 0.6, 0.7, 0.6, 0.7, 0.8, 1.2, 0.6, 4.5, 4.0, 2.6, 0.0, 0.4, 0.6, 0.0, 0.4, 1.3, 0.8, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 1.0, 1.0, 1.1, 2.0, 1.2, 1.5, 1.4, 1.8, 3.0, 1.2, 4.5, 4.0, 3.5, 0.0, 1.1, 2.0, 0.0, 0.6, 1.7, 1.2, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 2.0, 2.0, 2.0, 2.5, 3.3, 3.0, 2.3, 2.6, 3.6, 4.7, 2.0, 4.5, 4.0, 4.3, 0.0, 2.5, 3.3, 0.0, 1.5, 2.1, 1.8, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 3.0, 3.0, 3.0, 3.2, 3.7, 3.5, 2.3, 2.9, 3.8, 4.5, 2.6, 4.5, 4.0, 4.3, 0.0, 3.2, 3.7, 0.0, 1.7, 2.1, 1.8, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 3.0, 3.0, 3.0, 2.2, 3.2, 1.5, 1.7, 1.6, 2.1, 3.4, 1.7, 4.5, 4.0, 3.7, 0.0, 2.2, 3.2, 0.0, 0.8, 1.8, 1.3, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 1.5, 1.5, 1.5, 1.1, 1.3, 0.7, 0.6, 0.7, 0.9, 1.2, 1.0, 4.5, 4.0, 2.6, 0.0, 1.1, 1.3, 0.0, 0.4, 1.3, 0.8, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.3, 0.2, 0.6, 0.2, 0.4, 0.5, 0.3, 0.5, 4.5, 4.0, 2.2, 0.0, 0.3, 0.3, 0.0, 0.3, 1.1, 0.7, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.3, 0.0, 0.5, 0.0, 0.3, 0.3, 0.0, 0.2, 4.5, 4.0, 2.0, 0.0, 0.3, 0.3, 0.0, 0.2, 1.0, 0.6, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.4, 0.0, 0.2, 0.3, 0.0, 0.0, 4.5, 4.0, 2.0, 0.0, 0.2, 0.2, 0.0, 0.2, 1.0, 0.6, 0.0, 0.0, 0.0, 0.0, 0.0, + + SLAREA=0.0228,0.0200,0.0200,0.0295,0.0223,0.0277,0.0060,0.0227,0.0188,0.0236,0.0258,0.0200,0.0200,0.0090,0.0223,0.0422,0.0390, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, + +! Five types, one row for each type. + EPS = 41.87, 0.00, 0.00, 2.52, 0.04, 17.11, 0.02, 21.62, 0.11, 22.80, 46.86, 0.00, 0.00, 0.46, 30.98, 2.31, 1.63, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.98, 0.00, 0.00, 0.16, 0.09, 0.28, 0.05, 0.92, 0.22, 0.59, 0.38, 0.00, 0.00, 3.34, 0.96, 1.47, 1.07, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 1.82, 0.00, 0.00, 0.23, 0.05, 0.81, 0.03, 1.73, 1.26, 1.37, 1.84, 0.00, 0.00, 1.85, 1.84, 1.70, 1.21, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, +/ + +&noah_mp_modis_veg_categories + VEG_DATASET_DESCRIPTION = "modified igbp modis noah" + NVEG = 20 +/ + +&noah_mp_modis_veg_categories + VEG_DATASET_DESCRIPTION = "modified igbp modis noah" + NVEG = 20 +/ + +&noah_mp_modis_parameters +! 1 'Evergreen Needleleaf Forest' -> USGS 14 +! 2, 'Evergreen Broadleaf Forest' -> USGS 13 +! 3, 'Deciduous Needleleaf Forest' -> USGS 12 +! 4, 'Deciduous Broadleaf Forest' -> USGS 11 +! 5, 'Mixed Forests' -> USGS 15 +! 6, 'Closed Shrublands' -> USGS 8 "shrubland" +! 7, 'Open Shrublands' -> USGS 9 "shrubland/grassland" +! 8, 'Woody Savannas' -> USGS 8 "shrubland" +! 9, 'Savannas' -> USGS 10 +! 10, 'Grasslands' -> USGS 7 +! 11 'Permanent wetlands' -> avg of USGS 17 and 18 (herb. wooded wetland) +! 12, 'Croplands' -> USGS 2 "dryland cropland" +! 13, 'Urban and Built-Up' -> USGS 1 +! 14 'cropland/natural vegetation mosaic' -> USGS 5 "cropland/grassland" +! 15, 'Snow and Ice' -> USGS 24 +! 16, 'Barren or Sparsely Vegetated' -> USGS 19 +! 17, 'Water' -> USGS 16 +! 18, 'Wooded Tundra' -> USGS 21 +! 19, 'Mixed Tundra' -> USGS 22 +! 20, 'Barren Tundra' -> USGS 23 + + ISURBAN = 13 + ISWATER = 17 + ISBARREN = 16 + ISSNOW = 15 + EBLFOREST = 2 + + !--------------------------------------------------------------------------------------------------------------------------------------------------------------------- + ! 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 + !--------------------------------------------------------------------------------------------------------------------------------------------------------------------- + CH2OP = 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, + DLEAF = 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, + Z0MVT = 1.09, 1.10, 0.85, 0.80, 0.80, 0.20, 0.06, 0.60, 0.50, 0.12, 0.30, 0.15, 1.00, 0.14, 0.00, 0.00, 0.00, 0.30, 0.20, 0.03, + HVT = 20.0, 20.0, 18.0, 16.0, 16.0, 1.10, 1.10, 13.0, 10.0, 1.00, 5.00, 2.00, 15.0, 1.50, 0.00, 0.00, 0.00, 4.00, 2.00, 0.50, + HVB = 8.50, 8.00, 7.00, 11.5, 10.0, 0.10, 0.10, 0.10, 0.10, 0.05, 0.10, 0.10, 1.00, 0.10, 0.00, 0.00, 0.00, 0.30, 0.20, 0.10, + DEN = 0.28, 0.02, 0.28, 0.10, 0.10, 10.0, 10.0, 10.0, 0.02, 100., 5.05, 25.0, 0.01, 25.0, 0.00, 0.01, 0.01, 1.00, 1.00, 1.00, + RC = 1.20, 3.60, 1.20, 1.40, 1.40, 0.12, 0.12, 0.12, 3.00, 0.03, 0.75, 0.08, 1.00, 0.08, 0.00, 0.01, 0.01, 0.30, 0.30, 0.30, + + ! Row 1: Vis + ! Row 2: Near IR + RHOL = 0.07, 0.10, 0.07, 0.10, 0.10, 0.07, 0.07, 0.07, 0.10, 0.11, 0.105, 0.11, 0.00, 0.11, 0.00, 0.00, 0.00, 0.10, 0.10, 0.10, + 0.35, 0.45, 0.35, 0.45, 0.45, 0.35, 0.35, 0.35, 0.45, 0.58, 0.515, 0.58, 0.00, 0.58, 0.00, 0.00, 0.00, 0.45, 0.45, 0.45, + + ! Row 1: Vis + ! Row 2: Near IR + RHOS = 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.36, 0.26, 0.36, 0.00, 0.36, 0.00, 0.00, 0.00, 0.16, 0.16, 0.16, + 0.39, 0.39, 0.39, 0.39, 0.39, 0.39, 0.39, 0.39, 0.39, 0.58, 0.485, 0.58, 0.00, 0.58, 0.00, 0.00, 0.00, 0.39, 0.39, 0.39, + + ! Row 1: Vis + ! Row 2: Near IR + TAUL = 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.07, 0.06, 0.07, 0.00, 0.07, 0.00, 0.00, 0.00, 0.05, 0.05, 0.05, + 0.10, 0.25, 0.10, 0.25, 0.25, 0.10, 0.10, 0.10, 0.25, 0.25, 0.25, 0.25, 0.00, 0.25, 0.00, 0.00, 0.00, 0.25, 0.25, 0.25, + + ! Row 1: Vis + ! Row 2: Near IR + TAUS = 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.220, 0.1105, 0.220, 0.000, 0.220, 0.000, 0.000, 0.000, 0.001, 0.001, 0.001, + 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.380, 0.1905, 0.380, 0.000, 0.380, 0.000, 0.000, 0.000, 0.001, 0.001, 0.001, + + XL = 0.010, 0.010, 0.010, 0.250, 0.250, 0.010, 0.010, 0.010, 0.010, -0.30, -0.025, -0.30, 0.000, -0.30, 0.000, 0.000, 0.000, 0.250, 0.250, 0.250, +! CWPVT = 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, + CWPVT = 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, + C3PSN = 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + KC25 = 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, + AKC = 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, + KO25 = 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, + AKO = 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, + AVCMX = 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, + AQE = 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + + LTOVRC= 0.5, 0.55, 0.2, 0.55, 0.5, 0.65, 0.65, 0.65, 0.65, 0.50, 1.4, 1.6, 0.0, 1.2, 0.0, 0.0, 0.0, 1.3, 1.4, 1.0, + DILEFC= 1.20, 0.50, 1.80, 0.60, 0.80, 0.20, 0.20, 0.20, 0.50, 0.20, 0.4, 0.50, 0.00, 0.35, 0.00, 0.00, 0.00, 0.30, 0.40, 0.30, + DILEFW= 0.20, 4.00, 0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.50, 0.10, 0.2, 0.20, 0.00, 0.20, 0.00, 0.00, 0.00, 0.20, 0.20, 0.20, + RMF25 = 3.00, 0.65, 4.00, 3.00, 3.00, 0.26, 0.26, 0.26, 0.80, 1.80, 3.2, 1.00, 0.00, 1.45, 0.00, 0.00, 0.00, 3.00, 3.00, 3.00, + SLA = 80, 80, 80, 80, 80, 60, 60, 60, 50, 60, 80, 80, 60, 80, 0, 0, 0, 80, 80, 80, + FRAGR = 0.10, 0.20, 0.10, 0.20, 0.10, 0.20, 0.20, 0.20, 0.20, 0.20, 0.1, 0.20, 0.00, 0.20, 0.00, 0.10, 0.00, 0.10, 0.10, 0.10, + TMIN = 265, 273, 268, 273, 268, 273, 273, 273, 273, 273, 268, 273, 0, 273, 0, 0, 0, 268, 268, 268, + VCMX25= 50.0, 60.0, 60.0, 60.0, 55.0, 40.0, 40.0, 40.0, 40.0, 40.0, 50.0, 80.0, 0.00, 60.0, 0.00, 0.00, 0.00, 50.0, 50.0, 50.0, + TDLEF = 278, 278, 268, 278, 268, 278, 278, 278, 278, 278, 268, 278, 278, 278, 0, 0, 0, 268, 268, 268, + BP = 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 1.E15, 2.E3, 1.E15, 2.E3, 1.E15, 2.E3, 2.E3, 2.E3, + MP = 6., 9., 6., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., + QE25 = 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.00, 0.06, 0.00, 0.06, 0.00, 0.06, 0.06, 0.06, + RMS25 = 0.90, 0.30, 0.64, 0.10, 0.80, 0.10, 0.10, 0.10, 0.32, 0.10, 0.10, 0.10, 0.00, 0.10, 0.00, 0.00, 0.00, 0.10, 0.10, 0.00, + RMR25 = 0.36, 0.05, 0.05, 0.01, 0.03, 0.00, 0.00, 0.00, 0.01, 1.20, 0.0, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 2.11, 2.11, 0.00, + ARM = 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + FOLNMX= 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 0.00, 1.5, 0.00, 1.5, 0.00, 1.5, 1.5, 1.5, + WDPOOL= 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 0.00, 0.5, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 1.00, 1.00, 0.00, + WRRAT = 30.0, 30.0, 30.0, 30.0, 30.0, 3.00, 3.00, 3.00, 3.00, 0.00, 15.0, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 3.00, 3.00, 0.00, + MRP = 0.37, 0.23, 0.37, 0.40, 0.30, 0.19, 0.19, 0.19, 0.40, 0.17, 0.285, 0.23, 0.00, 0.23, 0.00, 0.00, 0.00, 0.23, 0.20, 0.00, + +! Monthly values, one row for each month: + SAIM = 0.4, 0.5, 0.3, 0.4, 0.4, 0.3, 0.2, 0.4, 0.3, 0.3, 0.3, 0.3, 0.0, 0.3, 0.0, 0.0, 0.0, 0.2, 0.1, 0.0, + 0.4, 0.5, 0.3, 0.4, 0.4, 0.3, 0.2, 0.4, 0.3, 0.3, 0.3, 0.3, 0.0, 0.3, 0.0, 0.0, 0.0, 0.2, 0.1, 0.0, + 0.4, 0.5, 0.3, 0.4, 0.4, 0.3, 0.2, 0.4, 0.3, 0.3, 0.3, 0.3, 0.0, 0.3, 0.0, 0.0, 0.0, 0.2, 0.1, 0.0, + 0.3, 0.5, 0.4, 0.4, 0.4, 0.3, 0.2, 0.4, 0.3, 0.3, 0.3, 0.3, 0.0, 0.3, 0.0, 0.0, 0.0, 0.2, 0.1, 0.0, + 0.4, 0.5, 0.4, 0.4, 0.4, 0.3, 0.2, 0.4, 0.3, 0.3, 0.3, 0.3, 0.0, 0.3, 0.0, 0.0, 0.0, 0.2, 0.1, 0.0, + 0.5, 0.5, 0.7, 0.4, 0.4, 0.3, 0.2, 0.4, 0.4, 0.4, 0.4, 0.3, 0.0, 0.4, 0.0, 0.0, 0.0, 0.2, 0.2, 0.0, + 0.5, 0.5, 1.3, 0.9, 0.7, 0.6, 0.4, 0.7, 0.8, 0.8, 0.6, 0.4, 0.0, 0.6, 0.0, 0.0, 0.0, 0.4, 0.4, 0.0, + 0.6, 0.5, 1.2, 1.2, 0.8, 0.9, 0.6, 1.2, 1.2, 1.3, 0.9, 0.5, 0.0, 0.9, 0.0, 0.0, 0.0, 0.6, 0.6, 0.0, + 0.6, 0.5, 1.0, 1.6, 1.0, 1.2, 0.8, 1.4, 1.3, 1.1, 0.9, 0.4, 0.0, 0.7, 0.0, 0.0, 0.0, 0.8, 0.7, 0.0, + 0.7, 0.5, 0.8, 1.4, 1.0, 0.9, 0.7, 1.1, 0.7, 0.4, 0.6, 0.3, 0.0, 0.3, 0.0, 0.0, 0.0, 0.7, 0.5, 0.0, + 0.6, 0.5, 0.6, 0.6, 0.5, 0.4, 0.3, 0.5, 0.4, 0.4, 0.4, 0.3, 0.0, 0.3, 0.0, 0.0, 0.0, 0.3, 0.3, 0.0, + 0.5, 0.5, 0.5, 0.4, 0.4, 0.3, 0.2, 0.4, 0.4, 0.4, 0.3, 0.3, 0.0, 0.3, 0.0, 0.0, 0.0, 0.2, 0.2, 0.0, + + LAIM = 4.0, 4.5, 0.0, 0.0, 2.0, 0.0, 0.0, 0.2, 0.3, 0.4, 0.2, 0.0, 0.0, 0.2, 0.0, 0.0, 0.0, 1.0, 0.6, 0.0, + 4.0, 4.5, 0.0, 0.0, 2.0, 0.0, 0.0, 0.2, 0.3, 0.5, 0.3, 0.0, 0.0, 0.3, 0.0, 0.0, 0.0, 1.0, 0.6, 0.0, + 4.0, 4.5, 0.0, 0.3, 2.2, 0.3, 0.2, 0.4, 0.5, 0.6, 0.3, 0.0, 0.0, 0.3, 0.0, 0.0, 0.0, 1.1, 0.7, 0.0, + 4.0, 4.5, 0.6, 1.2, 2.6, 0.9, 0.6, 1.0, 0.8, 0.7, 0.5, 0.0, 0.0, 0.4, 0.0, 0.0, 0.0, 1.3, 0.8, 0.0, + 4.0, 4.5, 1.2, 3.0, 3.5, 2.2, 1.5, 2.4, 1.8, 1.2, 1.5, 1.0, 0.0, 1.1, 0.0, 0.0, 0.0, 1.7, 1.2, 0.0, + 4.0, 4.5, 2.0, 4.7, 4.3, 3.5, 2.3, 4.1, 3.6, 3.0, 2.9, 2.0, 0.0, 2.5, 0.0, 0.0, 0.0, 2.1, 1.8, 0.0, + 4.0, 4.5, 2.6, 4.5, 4.3, 3.5, 2.3, 4.1, 3.8, 3.5, 3.5, 3.0, 0.0, 3.2, 0.0, 0.0, 0.0, 2.1, 1.8, 0.0, + 4.0, 4.5, 1.7, 3.4, 3.7, 2.5, 1.7, 2.7, 2.1, 1.5, 2.7, 3.0, 0.0, 2.2, 0.0, 0.0, 0.0, 1.8, 1.3, 0.0, + 4.0, 4.5, 1.0, 1.2, 2.6, 0.9, 0.6, 1.0, 0.9, 0.7, 1.2, 1.5, 0.0, 1.1, 0.0, 0.0, 0.0, 1.3, 0.8, 0.0, + 4.0, 4.5, 0.5, 0.3, 2.2, 0.3, 0.2, 0.4, 0.5, 0.6, 0.3, 0.0, 0.0, 0.3, 0.0, 0.0, 0.0, 1.1, 0.7, 0.0, + 4.0, 4.5, 0.2, 0.0, 2.0, 0.0, 0.0, 0.2, 0.3, 0.5, 0.3, 0.0, 0.0, 0.3, 0.0, 0.0, 0.0, 1.0, 0.6, 0.0, + 4.0, 4.5, 0.0, 0.0, 2.0, 0.0, 0.0, 0.2, 0.3, 0.4, 0.2, 0.0, 0.0, 0.2, 0.0, 0.0, 0.0, 1.0, 0.6, 0.0, + +! LAIM = 5.1, 3.3, 0.0, 1.9, 3.0, 1.0, 0.8, 0.5, 0.5, 0.7, 0.3, 1.8, 0.0, 2.4, 0.0, 0.0, 0.0, 0.6, 0.7, 0.0, +! 5.0, 3.6, 0.0, 1.9, 2.9, 1.0, 0.6, 1.0, 1.0, 0.7, 0.45, 1.9, 0.0, 2.6, 0.0, 0.0, 0.0, 0.4, 0.4, 0.0, +! 5.1, 4.4, 0.0, 2.1, 3.3, 1.0, 0.8, 1.8, 1.7, 1.1, 0.5, 2.6, 0.0, 2.9, 0.0, 0.0, 0.0, 0.4, 0.4, 0.0, +! 5.3, 5.4, 0.6, 2.5, 4.0, 1.0, 0.9, 2.6, 2.9, 1.7, 0.55, 3.9, 0.0, 3.4, 0.0, 0.0, 0.0, 0.4, 0.4, 0.0, +! 5.9, 6.2, 1.2, 3.1, 5.0, 1.0, 1.5, 3.4, 3.6, 2.5, 0.85, 5.2, 0.0, 4.0, 0.0, 0.0, 0.0, 0.8, 1.0, 0.0, +! 6.3, 6.4, 2.0, 3.3, 5.4, 1.0, 2.1, 3.6, 3.5, 2.7, 1.85, 5.6, 0.0, 4.2, 0.0, 0.0, 0.0, 2.0, 2.3, 0.0, +! 6.4, 5.9, 2.6, 3.3, 5.4, 1.0, 2.6, 3.4, 2.9, 2.8, 2.6, 5.3, 0.0, 4.1, 0.0, 0.0, 0.0, 3.3, 3.3, 0.0, +! 6.1, 5.6, 1.7, 3.1, 5.0, 1.0, 2.4, 3.2, 2.7, 2.4, 2.25, 4.5, 0.0, 3.8, 0.0, 0.0, 0.0, 3.3, 3.0, 0.0, +! 6.0, 5.3, 1.0, 2.9, 4.8, 1.0, 2.2, 2.9, 2.4, 2.1, 1.6, 4.1, 0.0, 3.7, 0.0, 0.0, 0.0, 2.8, 3.0, 0.0, +! 5.5, 4.7, 0.5, 2.6, 4.1, 1.0, 1.6, 2.3, 1.8, 1.7, 1.1, 3.2, 0.0, 3.2, 0.0, 0.0, 0.0, 1.4, 1.4, 0.0, +! 5.2, 4.0, 0.2, 2.2, 3.4, 1.0, 1.0, 1.5, 1.4, 1.3, 0.65, 2.3, 0.0, 2.7, 0.0, 0.0, 0.0, 0.5, 0.7, 0.0, +! 5.1, 3.2, 0.0, 1.9, 3.0, 1.0, 0.9, 0.7, 0.7, 0.8, 0.4, 1.7, 0.0, 2.4, 0.0, 0.0, 0.0, 0.8, 0.7, 0.0, + + SLAREA=0.0090, 0.0200, 0.0200, 0.0258, 0.0223, 0.0227, 0.0188, 0.0227, 0.0236, 0.0060, 0.0295, 0.0200, 0.0228, 0.0223, 0.02, 0.02, 0.0422, 0.02, 0.02, 0.02, + +! Five types, one row for each type. + EPS = 0.46, 0.00, 0.00, 46.86, 30.98, 21.62, 0.11, 21.62, 22.80, 0.02, 0.815, 0.00, 41.87, 0.04, 0.0, 0.0, 2.31, 0.0, 0.0, 0.0, + 3.34, 0.00, 0.00, 0.38, 0.96, 0.92, 0.22, 0.92, 0.59, 0.05, 0.535, 0.00, 0.98, 0.09, 0.0, 0.0, 1.47, 0.0, 0.0, 0.0, + 1.85, 0.00, 0.00, 1.84, 1.84, 1.73, 1.26, 1.73, 1.37, 0.03, 0.605, 0.00, 1.82, 0.05, 0.0, 0.0, 1.70, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, +/ diff --git a/case_study/base_settings/SOILPARM.TBL b/case_study/base_settings/SOILPARM.TBL new file mode 100644 index 000000000..cfb0d545e --- /dev/null +++ b/case_study/base_settings/SOILPARM.TBL @@ -0,0 +1,59 @@ +Soil Parameters +STAS +19,1 'BB DRYSMC F11 MAXSMC REFSMC SATPSI SATDK SATDW WLTSMC QTZ ' +1, 2.79, 0.010, -0.472, 0.339, 0.236, 0.069, 4.66E-5, 0.608E-6, 0.010, 0.92, 'SAND' +2, 4.26, 0.028, -1.044, 0.421, 0.383, 0.036, 1.41E-5, 0.514E-5, 0.028, 0.82, 'LOAMY SAND' +3, 4.74, 0.047, -0.569, 0.434, 0.383, 0.141, 5.23E-6, 0.805E-5, 0.047, 0.60, 'SANDY LOAM' +4, 5.33, 0.084, 0.162, 0.476, 0.360, 0.759, 2.81E-6, 0.239E-4, 0.084, 0.25, 'SILT LOAM' +5, 5.33, 0.084, 0.162, 0.476, 0.383, 0.759, 2.81E-6, 0.239E-4, 0.084, 0.10, 'SILT' +6, 5.25, 0.066, -0.327, 0.439, 0.329, 0.355, 3.38E-6, 0.143E-4, 0.066, 0.40, 'LOAM' +7, 6.77, 0.067, -1.491, 0.404, 0.314, 0.135, 4.45E-6, 0.990E-5, 0.067, 0.60, 'SANDY CLAY LOAM' +8, 8.72, 0.120, -1.118, 0.464, 0.387, 0.617, 2.03E-6, 0.237E-4, 0.120, 0.10, 'SILTY CLAY LOAM' +9, 8.17, 0.103, -1.297, 0.465, 0.382, 0.263, 2.45E-6, 0.113E-4, 0.103, 0.35, 'CLAY LOAM' +10, 10.73, 0.100, -3.209, 0.406, 0.338, 0.098, 7.22E-6, 0.187E-4, 0.100, 0.52, 'SANDY CLAY' +11, 10.39, 0.126, -1.916, 0.468, 0.404, 0.324, 1.34E-6, 0.964E-5, 0.126, 0.10, 'SILTY CLAY' +12, 11.55, 0.138, -2.138, 0.468, 0.412, 0.468, 9.74E-7, 0.112E-4, 0.138, 0.25, 'CLAY' +13, 5.25, 0.066, -0.327, 0.439, 0.329, 0.355, 3.38E-6, 0.143E-4, 0.066, 0.05, 'ORGANIC MATERIAL' +14, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.60, 'WATER' +15, 2.79, 0.006, -1.111, 0.20, 0.17, 0.069, 1.41E-4, 0.136E-3, 0.006, 0.07, 'BEDROCK' +16, 4.26, 0.028, -1.044, 0.421, 0.283, 0.036, 1.41E-5, 0.514E-5, 0.028, 0.25, 'OTHER(land-ice)' +17, 11.55, 0.030, -10.472, 0.468, 0.454, 0.468, 9.74E-7, 0.112E-4, 0.030, 0.60, 'PLAYA' +18, 2.79, 0.006, -0.472, 0.200, 0.17, 0.069, 1.41E-4, 0.136E-3, 0.006, 0.52, 'LAVA' +19, 2.79, 0.01, -0.472, 0.339, 0.236, 0.069, 4.66E-5, 0.608E-6, 0.01, 0.92, 'WHITE SAND' +Soil Parameters +STAS-RUC +19,1 'BB DRYSMC HC MAXSMC REFSMC SATPSI SATDK SATDW WLTSMC QTZ ' +1, 4.05, 0.045, 1.47, 0.395, 0.174, 0.121, 1.76E-4, 0.608E-6, 0.068, 0.92, 'SAND' +2, 4.38, 0.057, 1.41, 0.410, 0.179, 0.090, 1.56E-4, 0.514E-5, 0.075, 0.82, 'LOAMY SAND' +3, 4.90, 0.065, 1.34, 0.435, 0.249, 0.218, 3.47E-5, 0.805E-5, 0.114, 0.60, 'SANDY LOAM' +4, 5.30, 0.067, 1.27, 0.485, 0.369, 0.786, 7.20E-6, 0.239E-4, 0.179, 0.25, 'SILT LOAM' +5, 5.30, 0.034, 1.27, 0.485, 0.369, 0.786, 7.20E-6, 0.239E-4, 0.179, 0.10, 'SILT' +6, 5.39, 0.078, 1.21, 0.451, 0.314, 0.478, 6.95E-6, 0.143E-4, 0.155, 0.40, 'LOAM' +7, 7.12, 0.100, 1.18, 0.420, 0.299, 0.299, 6.30E-6, 0.990E-5, 0.175, 0.60, 'SANDY CLAY LOAM' +8, 7.75, 0.089, 1.32, 0.477, 0.357, 0.356, 1.70E-6, 0.237E-4, 0.218, 0.10, 'SILTY CLAY LOAM' +9, 8.52, 0.095, 1.23, 0.476, 0.391, 0.630, 2.45E-6, 0.113E-4, 0.250, 0.35, 'CLAY LOAM' +10, 10.40, 0.100, 1.18, 0.426, 0.316, 0.153, 2.17E-6, 0.187E-4, 0.219, 0.52, 'SANDY CLAY' +11, 10.40, 0.070, 1.15, 0.492, 0.409, 0.490, 1.03E-6, 0.964E-5, 0.283, 0.10, 'SILTY CLAY' +12, 11.40, 0.068, 1.09, 0.482, 0.400, 0.405, 1.28E-6, 0.112E-4, 0.286, 0.25, 'CLAY' +13, 5.39, 0.078, 1.21, 0.451, 0.314, 0.478, 6.95E-6, 0.143E-4, 0.155, 0.05, 'ORGANIC MATERIAL' +14, 0.0, 0.0, 4.18, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.00, 'WATER' +15, 4.05, 0.004, 2.03, 0.200, 0.10 , 0.121, 1.41E-4, 0.136E-3, 0.006, 0.60, 'BEDROCK' +16, 4.90, 0.065, 2.10, 0.435, 0.249, 0.218, 3.47E-5, 0.514E-5, 0.114, 0.05, 'OTHER(land-ice)' +17, 11.40, 0.030, 1.41, 0.468, 0.454, 0.468, 9.74E-7, 0.112E-4, 0.030, 0.60, 'PLAYA' +18, 4.05, 0.006, 1.41, 0.200, 0.17, 0.069, 1.41E-4, 0.136E-3, 0.060, 0.52, 'LAVA' +19, 4.05, 0.01, 1.47, 0.339, 0.236, 0.069, 1.76E-4, 0.608E-6, 0.060, 0.92, 'WHITE SAND' +Soil Parameters +ROSETTA +12,1 'theta_res theta_sat vGn_alpha vGn_n k_soil BB DRYSMC HC MAXSMC REFSMC SATPSI SATDK SATDW WLTSMC QTZ ' +1 0.098 0.459 -1.496 1.253 1.70799e-06 11.40 0.068 1.09 0.482 0.412 0.405 1.28E-6 0.112E-4 0.286 0.25 'CLAY' +2 0.079 0.442 -1.581 1.416 9.47297e-07 8.52 0.095 1.23 0.476 0.382 0.630 2.45E-6 0.113E-4 0.250 0.35 'CLAY LOAM' +3 0.061 0.399 -1.112 1.472 1.39472e-06 5.39 0.078 1.21 0.451 0.329 0.478 6.95E-6 0.143E-4 0.155 0.40 'LOAM' +4 0.049 0.390 -3.475 1.746 1.21755e-05 4.38 0.057 1.41 0.410 0.383 0.090 1.56E-4 0.514E-5 0.075 0.82 'LOAMY SAND' +5 0.053 0.375 -3.524 3.177 7.43852e-05 4.05 0.045 1.47 0.395 0.236 0.121 1.76E-4 0.608E-6 0.068 0.92 'SAND' +6 0.117 0.385 -3.342 1.208 1.31367e-06 10.40 0.100 1.18 0.426 0.338 0.153 2.17E-6 0.187E-4 0.219 0.52 'SANDY CLAY' +7 0.063 0.384 -2.109 1.330 1.52576e-06 7.12 0.100 1.18 0.420 0.314 0.299 6.30E-6 0.990E-5 0.175 0.60 'SANDY CLAY LOAM' +8 0.039 0.387 -2.667 1.449 4.43084e-06 4.90 0.065 1.34 0.435 0.383 0.218 3.47E-5 0.805E-5 0.114 0.60 'SANDY LOAM' +9 0.050 0.489 -0.658 1.679 5.06391e-06 5.30 0.034 1.27 0.485 0.383 0.786 7.20E-6 0.239E-4 0.179 0.10 'SILT' +10 0.111 0.481 -1.622 1.321 1.11298e-06 10.40 0.070 1.15 0.492 0.404 0.490 1.03E-6 0.964E-5 0.283 0.10 'SILTY CLAY' +11 0.090 0.482 -0.839 1.521 1.28673e-06 7.75 0.089 1.32 0.477 0.387 0.356 1.70E-6 0.237E-4 0.218 0.10 'SILTY CLAY LOAM' +12 0.065 0.439 -0.506 1.663 2.11099e-06 5.30 0.067 1.27 0.485 0.360 0.786 7.20E-6 0.239E-4 0.179 0.25 'SILT LOAM' diff --git a/case_study/base_settings/VEGPARM.TBL b/case_study/base_settings/VEGPARM.TBL new file mode 100644 index 000000000..2d53237ae --- /dev/null +++ b/case_study/base_settings/VEGPARM.TBL @@ -0,0 +1,211 @@ +Vegetation Parameters +USGS +27,1, 'SHDFAC NROOT RS RGL HS SNUP MAXALB LAIMIN LAIMAX EMISSMIN EMISSMAX ALBEDOMIN ALBEDOMAX Z0MIN Z0MAX ZTOPV ZBOTV' +1, .10, 1, 200., 999., 999.0, 0.04, 46., 1.00, 1.00, .880, .880, .15, .15, .50, .50, 0.00, 0.00, 'Urban and Built-Up Land' +2, .80, 3, 40., 100., 36.25, 0.04, 66., 1.56, 5.68, .920, .985, .17, .23, .05, .15, 0.50, 0.01, 'Dryland Cropland and Pasture' +3, .80, 3, 40., 100., 36.25, 0.04, 66., 1.56, 5.68, .930, .985, .20, .25, .02, .10, 0.50, 0.01, 'Irrigated Cropland and Pasture' +4, .80, 3, 40., 100., 36.25, 0.04, 66., 1.00, 4.50, .920, .985, .18, .23, .05, .15, 0.50, 0.01, 'Mixed Dryland/Irrigated Cropland and Pasture' +5, .80, 3, 40., 100., 36.25, 0.04, 68., 2.29, 4.29, .920, .980, .18, .23, .05, .14, 0.50, 0.01, 'Cropland/Grassland Mosaic' +6, .80, 3, 70., 65., 44.14, 0.04, 60., 2.00, 4.00, .930, .985, .16, .20, .20, .20, 0.50, 0.01, 'Cropland/Woodland Mosaic' +7, .80, 3, 40., 100., 36.35, 0.04, 70., 0.52, 2.90, .920, .960, .19, .23, .10, .12, 0.50, 0.01, 'Grassland' +8, .70, 3, 300., 100., 42.00, 0.03, 60., 0.50, 3.66, .930, .930, .25, .30, .01, .05, 0.50, 0.10, 'Shrubland' +9, .70, 3, 170., 100., 39.18, 0.035, 65., 0.60, 2.60, .930, .950, .22, .30, .01, .06, 0.50, 0.10, 'Mixed Shrubland/Grassland' +10, .50, 3, 70., 65., 54.53, 0.04, 50., 0.50, 3.66, .920, .920, .20, .20, .15, .15, 5.00, 0.10, 'Savanna' +11, .80, 4, 100., 30., 54.53, 0.08, 58., 1.85, 3.31, .930, .930, .16, .17, .50, .50, 20.0, 11.5, 'Deciduous Broadleaf Forest' +12, .70, 4, 150., 30., 47.35, 0.08, 54., 1.00, 5.16, .930, .940, .14, .15, .50, .50, 14.0, 7.0, 'Deciduous Needleleaf Forest' +13, .95, 4, 150., 30., 41.69, 0.08, 35., 3.08, 6.48, .950, .950, .12, .12, .50, .50, 35.0, 1.0, 'Evergreen Broadleaf Forest' +14, .70, 4, 125., 30., 47.35, 0.08, 52., 5.00, 6.40, .950, .950, .12, .12, .50, .50, 17.0, 8.5, 'Evergreen Needleleaf Forest' +15, .80, 4, 125., 30., 51.93, 0.08, 53., 2.80, 5.50, .930, .970, .17, .25, .20, .50, 18.0, 10.0, 'Mixed Forest' +16, .00, 0, 100., 30., 51.75, 0.01, 70., 0.01, 0.01, .980, .980, .08, .08, 0.0001, 0.0001, 0.00, 0.00, 'Water Bodies' +17, .60, 2, 40., 100., 60.00, 0.01, 68., 1.50, 5.65, .950, .950, .14, .14, .20, .20, 0.50, 0.01, 'Herbaceous Wetland' +18, .60, 2, 100., 30., 51.93, 0.02, 50., 2.00, 5.80, .950, .950, .14, .14, .40, .40, 20.0, 11.5, 'Wooded Wetland' +19, .01, 1, 999., 999., 999.0, 0.02, 75., 0.10, 0.75, .900, .900, .38, .38, .01, .01, 0.02, 0.01, 'Barren or Sparsely Vegetated' +20, .60, 3, 150., 100., 42.00, 0.025, 68., 0.41, 3.35, .920, .920, .15, .20, .10, .10, 0.50, 0.01, 'Herbaceous Tundra' +21, .60, 3, 150., 100., 42.00, 0.025, 55., 0.41, 3.35, .930, .930, .15, .20, .30, .30, 10.0, 0.10, 'Wooded Tundra' +22, .60, 3, 150., 100., 42.00, 0.025, 60., 0.41, 3.35, .920, .920, .15, .20, .15, .15, 5.00, 0.10, 'Mixed Tundra' +23, .30, 2, 200., 100., 42.00, 0.02, 75., 0.41, 3.35, .900, .900, .25, .25, .05, .10, 0.02, 0.01, 'Bare Ground Tundra' +24, .00, 1, 999., 999., 999.0, 0.02, 82., 0.01, 0.01, .950, .950, .55, .70, 0.001, 0.001, 0.00, 0.00, 'Snow or Ice' +25, .50, 1, 40., 100., 36.25, 0.02, 75., 0.01, 0.01, .890, .890, .30, .30, .01, .01, 0.00, 0.00, 'Playa' +26, .00, 0, 999., 999., 999.0, 0.02, 75., 0.01, 0.01, .880, .880, .16, .16, .15, .15, 0.00, 0.00, 'Lava' +27, .00, 0, 999., 999., 999.0, 0.02, 75., 0.01, 0.01, .830, .830, .60, .60, .01, .01, 0.00, 0.00, 'White Sand' +TOPT_DATA +298.0 +CMCMAX_DATA +0.5E-3 +CFACTR_DATA +0.5 +RSMAX_DATA +5000.0 +BARE +19 +NATURAL +5 +Vegetation Parameters +MODIFIED_IGBP_MODIS_NOAH +20,1, 'SHDFAC NROOT RS RGL HS SNUP MAXALB LAIMIN LAIMAX EMISSMIN EMISSMAX ALBEDOMIN ALBEDOMAX Z0MIN Z0MAX ZTOPV ZBOTV' +1 .70, 4, 125., 30., 47.35, 0.08, 52., 5.00, 6.40, .950, .950, .12, .12, .50, .50, 17.0, 8.5, 'Evergreen Needleleaf Forest' +2, .95, 4, 150., 30., 41.69, 0.08, 35., 3.08, 6.48, .950, .950, .12, .12, .50, .50, 35.0, 1.0, 'Evergreen Broadleaf Forest' +3, .70, 4, 150., 30., 47.35, 0.08, 54., 1.00, 5.16, .930, .940, .14, .15, .50, .50, 14.0, 7.0, 'Deciduous Needleleaf Forest' +4, .80, 4, 100., 30., 54.53, 0.08, 58., 1.85, 3.31, .930, .930, .16, .17, .50, .50, 20.0, 11.5, 'Deciduous Broadleaf Forest' +5, .80, 4, 125., 30., 51.93, 0.08, 53., 2.80, 5.50, .930, .970, .17, .25, .20, .50, 18.0, 10.0, 'Mixed Forests' +6, .70, 3, 300., 100., 42.00, 0.03, 60., 0.50, 3.66, .930, .930, .25, .30, .01, .05, 0.50, 0.10, 'Closed Shrublands' +7, .70, 3, 170., 100., 39.18, 0.035, 65., 0.60, 2.60, .930, .950, .22, .30, .01, .06, 0.50, 0.10, 'Open Shrublands' +8, .70, 3, 300., 100., 42.00, 0.03, 60., 0.50, 3.66, .930, .930, .25, .30, .01, .05, 0.50, 0.10, 'Woody Savannas' +9, .50, 3, 70., 65., 54.53, 0.04, 50., 0.50, 3.66, .920, .920, .20, .20, .15, .15, 0.50, 0.10, 'Savannas' +10, .80, 3, 40., 100., 36.35, 0.04, 70., 0.52, 2.90, .920, .960, .19, .23, .10, .12, 0.50, 0.01, 'Grasslands' +11 .60, 2, 70., 65., 55.97 0.015 59., 1.75, 5.72, .950, .950, .14, .14, .30, .30, 0.00, 0.00, 'Permanent wetlands' +12, .80, 3, 40., 100., 36.25, 0.04, 66., 1.56, 5.68, .920, .985, .17, .23, .05, .15, 0.50, 0.01, 'Croplands' +13, .10, 1, 200., 999., 999.0, 0.04, 46., 1.00, 1.00, .880, .880, .15, .15, .50, .50, 0.00, 0.00, 'Urban and Built-Up' +14 .80, 3, 40., 100., 36.25, 0.04, 68., 2.29, 4.29, .920, .980, .18, .23, .05, .14, 0.50, 0.01, 'cropland/natural vegetation mosaic' +15, .00, 1, 999., 999., 999.0, 0.02, 82., 0.01, 0.01, .950, .950, .55, .70, 0.001, 0.001, 0.00, 0.00, 'Snow and Ice' +16, .01, 1, 999., 999., 999.0, 0.02, 75., 0.10, 0.75, .900, .900, .38, .38, .01, .01, 0.02, 0.01, 'Barren or Sparsely Vegetated' +17, .00, 0, 100., 30., 51.75, 0.01, 70., 0.01, 0.01, .980, .980, .08, .08, 0.0001, 0.0001, 0.00, 0.00, 'Water' +18, .60, 3, 150., 100., 42.00, 0.025, 55., 0.41, 3.35, .930, .930, .15, .20, .30, .30, 10.0, 0.10, 'Wooded Tundra' +19, .60, 3, 150., 100., 42.00, 0.025, 60., 0.41, 3.35, .920, .920, .15, .20, .15, .15, 5.00, 0.10, 'Mixed Tundra' +20, .30, 2, 200., 100., 42.00, 0.02, 75., 0.41, 3.35, .900, .900, .25, .25, .05, .10, 0.02, 0.01, 'Barren Tundra' +TOPT_DATA +298.0 +CMCMAX_DATA +0.5E-3 +CFACTR_DATA +0.5 +RSMAX_DATA +5000.0 +BARE +16 +NATURAL +14 +Vegetation Parameters +USGS-RUC +28,1, 'ALBEDO Z0 LEMI PC SHDFAC IFOR RS RGL HS SNUP LAI MAXALB' +1, .18, 2.0, .88, .40, .10, 9, 200., 999., 999.0, 0.04, 1.00, 40., 'Urban and Built-Up Land' +2, .17, .06, .92, .30, .80, 7, 40., 100., 36.25, 0.04, 5.68, 64., 'Dryland Cropland and Pasture' +3, .18, .075, .92, .40, .80, 7, 40., 100., 36.25, 0.04, 5.68, 64., 'Irrigated Cropland and Pasture' +4, .18, .125, .92, .40, .80, 7, 40., 100., 36.25, 0.04, 4.50, 64., 'Mixed Dryland/Irrigated Cropland and Pasture' +5, .18, .15, .92, .40, .80, 3, 40., 100., 36.25, 0.04, 4.29, 64., 'Cropland/Grassland Mosaic' +6, .16, .20, .93, .40, .80, 3, 70., 65., 44.14, 0.04, 4.00, 60., 'Cropland/Woodland Mosaic' +7, .19, .075 .92, .40, .80, 5, 40., 100., 36.35, 0.04, 2.90, 64., 'Grassland' +8, .22, .10, .88, .40, .70, 4, 300., 100., 42.00, 0.03, 3.66, 69., 'Shrubland' +9, .20, .11, .90, .40, .70, 4, 170., 100., 39.18, 0.035, 2.60, 67., 'Mixed Shrubland/Grassland' +10, .20, .15, .92, .40, .50, 5, 70., 65., 54.53, 0.04, 3.66, 45., 'Savanna' +11, .16, .70, .93, .55, .80, 3, 100., 30., 54.53, 0.08, 3.31, 58., 'Deciduous Broadleaf Forest' +12, .14, .70, .94, .55, .70, 4, 150., 30., 47.35, 0.08, 5.16, 54., 'Deciduous Needleleaf Forest' +13, .12, .70, .95, .55, .95, 2, 150., 30., 41.69, 0.08, 6.48, 32., 'Evergreen Broadleaf Forest' +14, .12, .70, .95, .55, .70, 1, 125., 30., 47.35, 0.08, 6.40, 52., 'Evergreen Needleleaf Forest' +15, .13, .70, .94, .55, .80, 2, 125., 30., 51.93, 0.08, 5.50, 53., 'Mixed Forest' +16, .08, .0001, .98, .00, .00, 9, 100., 30., 51.75, 0.01, 0.01, 70., 'Water Bodies' +17, .14, .20, .95, .55, .60, 4, 40., 100., 60.00, 0.01, 5.65, 35., 'Herbaceous Wetland' +18, .14, .40, .95, .55, .60, 4, 100., 30., 51.93, 0.02, 5.80, 30., 'Wooded Wetland' +19, .25, .05, .85, .30, .01, 5, 999., 999., 999.0, 0.02, 0.75, 69., 'Barren or Sparsely Vegetated' +20, .15, .10, .92, .30, .60, 5, 150., 100., 42.00, 0.025, 3.35, 58., 'Herbaceous Tundra' +21, .15, .15, .93, .40, .60, 5, 150., 100., 42.00, 0.025, 3.35, 55., 'Wooded Tundra' +22, .15, .10, .92, .40, .60, 5, 150., 100., 42.00, 0.025, 3.35, 55., 'Mixed Tundra' +23, .25, .065, .85, .30, .30, 5, 200., 100., 42.00, 0.02, 3.35, 65., 'Bare Ground Tundra' +24, .55, .0024, .98, .00, .00, 9, 999., 999., 999.0, 0.02, 0.01, 75., 'Snow or Ice' +25, .30, .01, .85, .30, .50, 9, 40., 100., 36.25, 0.02, 0.01, 69., 'Playa' +26, .16, .15, .85, .00, .00, 9, 999., 999., 999.0, 0.02, 0.01, 69., 'Lava' +27, .60, .01, .90, .00, .00, 9, 999., 999., 999.0, 0.02, 0.01, 69., 'White Sand' +28, .08, .0001, .98, .00, .00, 9, 100., 30., 51.75, 0.01, 0.01, 70., 'Lakes' +TOPT_DATA +298.0 +CMCMAX_DATA +0.2E-3 +CFACTR_DATA +0.5 +RSMAX_DATA +5000.0 +BARE +19 +NATURAL +5 +Vegetation Parameters +MODI-RUC +21,1, 'ALBEDO Z0 LEMI PC SHDFAC IFOR RS RGL HS SNUP LAI MAXALB' +1 .12, .70, .950, .55, .70, 1, 125., 30., 47.35, 0.08, 6.40, 52., 'Evergreen Needleleaf Forest' +2, .12, .70, .950, .55, .95, 2, 150., 30., 41.69, 0.08, 6.48, 35., 'Evergreen Broadleaf Forest' +3, .14, .70, .940, .55, .70, 4, 150., 30., 47.35, 0.08, 5.16, 54., 'Deciduous Needleleaf Forest' +4, .16, .70, .930, .55, .80, 3, 100., 30., 54.53, 0.08, 3.31, 58., 'Deciduous Broadleaf Forest' +5, .13, .70, .940, .55, .80, 2, 125., 30., 51.93, 0.08, 5.50, 53., 'Mixed Forests' +6, .22, .10, .930, .40, .70, 4, 300., 100., 42.00, 0.03, 3.66, 60., 'Closed Shrublands' +7, .20, .10, .880, .40, .70, 4, 170., 100., 39.18, 0.035, 2.60, 65., 'Open Shrublands' +8, .20, .15, .930, .40, .70, 5, 300., 100., 42.00, 0.03, 3.66, 60., 'Woody Savannas' +9, .20, .15, .920, .40, .50, 5, 70., 65., 54.53, 0.04, 3.66, 50., 'Savannas' +10, .19, .075, .920, .40, .80, 5, 40., 100., 36.35, 0.04, 2.90, 70., 'Grasslands' +11 .14, .30, .950, .40, .60, 4, 70., 65., 55.97 0.015 5.72, 59., 'Permanent wetlands' +12, .18, .15, .935, .40, .80, 7, 40., 100., 36.25, 0.04, 5.68, 66., 'Croplands' +13, .18, 2.0, .880, .40, .10, 9, 200., 999., 999.0, 0.04, 1.00, 46., 'Urban and Built-Up' +14 .16, .14, .920, .40, .80, 7, 40., 100., 36.25, 0.04, 4.29, 68., 'cropland/natural vegetation mosaic' +15, .55, .011, .980, .00, .00, 9, 999., 999., 999.0, 0.02, 0.01, 82., 'Snow and Ice' +16, .25, .065, .850, .30, .01, 5, 999., 999., 999.0, 0.02, 0.75, 75., 'Barren or Sparsely Vegetated' +17, .08, .0001, .980, .00, .00, 9, 100., 30., 51.75, 0.01, 0.01, 70., 'Water' +18, .15, .15, .930, .40, .60, 5, 150., 100., 42.00, 0.025, 3.35, 55., 'Wooded Tundra' +19, .15, .10, .920, .40, .60, 5, 150., 100., 42.00, 0.025, 3.35, 60., 'Mixed Tundra' +20, .15, .06, .900, .30, .30, 5, 200., 100., 42.00, 0.02, 3.35, 75., 'Barren Tundra' +21, .08, .0001, .980, .00, .00, 9, 100., 30., 51.75, 0.01, 0.01, 70., 'Lakes' +TOPT_DATA +298.0 +CMCMAX_DATA +0.2E-3 +CFACTR_DATA +0.5 +RSMAX_DATA +5000.0 +BARE +16 +NATURAL +14 +Vegetation Parameters +NLCD40 +40,1, 'SHDFAC NROOT RS RGL HS SNUP MAXALB LAIMIN LAIMAX EMISSMIN EMISSMAX ALBEDOMIN ALBEDOMAX Z0MIN Z0MAX ZTOPV ZBOTV' +1, .70, 4, 125., 30., 47.35, 0.08, 52., 5.00, 6.40, .950, .950, .12, .12, .50, .50, 17.00, 8.50, 'Evergreen Needleleaf Forest' +2, .95, 4, 150., 30., 41.69, 0.08, 35., 3.08, 6.48, .950, .950, .12, .12, .50, .50, 35.00, 1.00, 'Evergreen Broadleaf Forest' +3, .70, 4, 150., 30., 47.35, 0.08, 54., 1.00, 5.16, .930, .940, .14, .15, .50, .50, 14.00, 7.00, 'Deciduous Needleleaf Forest' +4, .80, 4, 100., 30., 54.53, 0.08, 58., 1.85, 3.31, .930, .930, .16, .17, .50, .50, 20.00, 11.50, 'Deciduous Broadleaf Forest' +5, .80, 4, 125., 30., 51.93, 0.08, 53., 2.80, 5.50, .930, .970, .17, .25, .20, .50, 18.00, 10.00, 'Mixed Forest' +6, .70, 3, 300., 100., 42.00, 0.03, 60., 0.50, 3.66, .930, .930, .25, .30, .01, .05, 0.50, 0.10, 'Closed Shrubland' +7, .70, 3, 170., 100., 39.18, 0.035, 65., 0.60, 2.60, .930, .950, .22, .30, .01, .06, 0.50, 0.10, 'Open Shrubland' +8, .50, 3, 70., 65., 54.53, 0.04, 50., 0.50, 3.66, .930, .930, .25, .30, .01, .05, 0.00, 0.00, 'Woody Savanna' +9, .50, 3, 70., 65., 54.53, 0.04, 50., 0.50, 3.66, .920, .920, .20, .20, .15, .15, 0.50, 0.10, 'Savanna' +10, .80, 3, 40., 100., 36.35, 0.04, 70., 0.52, 2.90, .920, .960, .19, .23, .10, .12, 0.50, 0.10, 'Grassland' +11, .60, 2, 100., 30., 51.93, 0.02, 50., 1.75, 5.72, .950, .950, .14, .14, .30, .30, 0.50, 0.10, 'Permanent Wetland' +12, .80, 3, 40., 100., 36.25, 0.04, 66., 1.50, 5.68, .920, .985, .15, .23, .05, .15, 0.50, 0.10, 'Cropland' +13, .10, 1, 200., 999., 999.0, 0.04, 46., 1.00, 1.00, .880, .880, .15, .15, .50, .50, 0.00, 0.00, 'Urban and Built-Up' +14, .80, 3, 40., 100., 36.25, 0.04, 66., 2.29, 4.29, .920, .980, .18, .23, .05, .14, 0.50, 0.10, 'Cropland / Natural Veg. Mosaic' +15, .00, 1, 999., 999., 999.0, 0.02, 82., 0.01, 0.01, .950, .950, .55, .70, 0.001, 0.001, 0.00, 0.00, 'Permanent Snow' +16, .01, 1, 999., 999., 999.0, 0.02, 75., 0.10, 0.75, .900, .900, .38, .38, .01, .01, 0.02, 0.01, 'Barren / Sparsely Vegetated' +17, .00, 0, 100., 30., 51.75, 0.01, 70., 0.01, 0.01, .980, .980, .08, .08, 0.0001, 0.0001, 0.00, 0.00, 'IGBP Water' +18, .00, 0, 999., 999., 999.0, 999., 999., 999.0, 999.0, 999., 999.0, 999.0, 999.0, 999.0, 999.0, 0.00, 0.00, 'Unclassified' +19, .00, 0, 999., 999., 999.0, 999., 999., 999.0, 999.0, 999., 999.0, 999.0, 999.0, 999.0, 999.0, 0.00, 0.00, 'Fill Value' +20, .00, 0, 999., 999., 999.0, 999., 999., 999.0, 999.0, 999., 999.0, 999.0, 999.0, 999.0, 999.0, 0.00, 0.00, 'Unclassified' +21, .00, 0, 100., 30., 51.75, 0.01, 70., 0.01, 0.01, .980, .980, .08, .08, 0.0001, 0.0001, 0.00, 0.00, 'Open Water' +22, .00, 1, 999., 999., 999.0, 0.02, 82., 0.01, 0.01, .950, .950, .55, .70, 0.001, 0.001, 0.00, 0.00, 'Perennial Ice/Snow' +23, .30, 1, 200., 999., 999.0, 0.04, 46., 1.00, 1.00, .880, .880, .20, .20, .50, .50, 0.00, 0.00, 'Developed Open Space' +24, .27, 1, 200., 999., 999.0, 0.04, 46., 1.00, 1.00, .880, .880, .20, .20, .70, .70, 0.00, 0.00, 'Developed Low Intensity' +25, .02, 1, 200., 999., 999.0, 0.04, 46., 1.00, 1.00, .880, .880, .20, .20, 1.5, 1.5, 0.00, 0.00, 'Developed Medium Intensity' +26, .11, 1, 200., 999., 999.0, 0.04, 46., 1.00, 1.00, .880, .880, .20, .20, 2.0, 2.0, 0.00, 0.00, 'Developed High Intensity' +27, .01, 1, 999., 999., 999.0, 0.02, 75., 0.10, 0.75, .900, .900, .38, .38, .01, .01, 0.02, 0.01, 'Barren Land' +28, .80, 4, 125., 30., 54.70, 0.08, 56., 1.00, 5.16, .930, .940, .14, .17, .50, .50, 20.00, 11.50, 'Deciduous Forest' +29, .95, 4, 140., 30., 44.00, 0.08, 42., 3.08, 6.48, .950, .950, .12, .12, .50, .50, 17.00, 8.50, 'Evergreen Forest' +30, .80, 4, 125., 30., 51.93, 0.08, 53., 2.80, 5.50, .930, .970, .17, .25, .20, .50, 18.00, 10.00, 'Mixed Forest' +31, .70, 3, 170., 100., 39.18, 0.035, 65., 1.00, 4.00, .930, .950, .16, .30, .01, .04, 0.50, 0.10, 'Dwarf Scrub' +32, .70, 3, 300., 100., 42.00, 0.03, 60., 0.50, 3.66, .930, .930, .22, .30, .01, .05, 0.50, 0.10, 'Shrub/Scrub' +33, .80, 3, 40., 100., 36.35, 0.04, 70., 0.52, 2.90, .920, .960, .19, .23, .10, .12, 0.50, 0.10, 'Grassland/Herbaceous' +34, .60, 2, 40., 100., 60.00, 0.01, 68., 1.50, 5.65, .950, .950, .14, .14, .20, .20, 0.50, 0.10, 'Sedge/Herbaceous' +35, .60, 2, 40., 100., 60.00, 0.01, 68., 1.00, 2.00, .950, .950, .31, .31, .01, .01, 0.00, 0.00, 'Lichens' +36, .60, 2, 40., 100., 60.00, 0.01, 68., 1.00, 2.00, .950, .950, .24, .24, .01, .01, 0.00, 0.00, 'Moss' +37, .80, 3, 40., 100., 36.25, 0.04, 66., 1.56, 5.68, .920, .985, .17, .23, .05, .15, 0.50, 0.10, 'Pasture/Hay' +38, .80, 3, 40., 100., 36.25, 0.04, 66., 1.56, 5.68, .930, .985, .20, .25, .02, .10, 0.50, 0.10, 'Cultivated Crops' +39, .60, 2, 100., 30., 51.93, 0.02, 50., 0.70, 3.50, .950, .950, .14, .14, .40, .40, 20.00, 11.50, 'Woody Wetland' +40, .60, 2, 40., 100., 60.00, 0.01, 68., 0.70, 3.50, .950, .950, .12, .12, .20, .20, 0.50, 0.10, 'Emergent Herbaceous Wetland' +TOPT_DATA +298.0 +CMCMAX_DATA +0.5E-3 +CFACTR_DATA +0.5 +RSMAX_DATA +5000.0 +BARE +16 +NATURAL +14 diff --git a/case_study/base_settings/localParamInfo.txt b/case_study/base_settings/localParamInfo.txt new file mode 100644 index 000000000..4e48f7d5e --- /dev/null +++ b/case_study/base_settings/localParamInfo.txt @@ -0,0 +1,233 @@ +! File provenance +! --------------- +! 2021-08-10: taken from the SUMMA test cases v3 distribution (WRR figures) and included as case study default file +! +! +! ======================================================================================================================= +! ======================================================================================================================= +! ===== DEFINITION OF MODEL PARAMETERS ================================================================================== +! ======================================================================================================================= +! ======================================================================================================================= +! Note: lines starting with "!" are treated as comment lines -- there is no limit on the number of comment lines. +! +! ======================================================================================================================= +! DEFINE SITE MODEL PARAMETERS +! ------------------------------------ +! the format definition defines the format of the file, which can be changed +! the delimiters "| " must be present (format a2), as these are used to check the integrety of the file +! columns are: +! 1: parameter name +! 2: default parameter value +! 3: lower parameter limit +! 4: upper parameter limit +! ======================================================================================================================= +! +! ==================================================================== +! define format string for parameter descriptions +! ==================================================================== +'(a25,1x,3(a1,1x,f12.4,1x))' ! format string (must be in single quotes) +! ==================================================================== +! boundary conditions +! ==================================================================== +upperBoundHead | -0.7500 | -100.0000 | -0.0100 +lowerBoundHead | 0.0000 | -100.0000 | -0.0100 +upperBoundTheta | 0.2004 | 0.1020 | 0.3680 +lowerBoundTheta | 0.1100 | 0.1020 | 0.3680 +upperBoundTemp | 272.1600 | 270.1600 | 280.1600 +lowerBoundTemp | 274.1600 | 270.1600 | 280.1600 +! ==================================================================== +! precipitation partitioning +! ==================================================================== +tempCritRain | 273.1600 | 272.1600 | 274.1600 +tempRangeTimestep | 2.0000 | 0.5000 | 5.0000 +frozenPrecipMultip | 1.0000 | 0.5000 | 1.5000 +! ==================================================================== +! snow properties +! ==================================================================== +snowfrz_scale | 50.0000 | 10.0000 | 1000.0000 +fixedThermalCond_snow | 0.3500 | 0.1000 | 1.0000 +! ==================================================================== +! snow albedo +! ==================================================================== +albedoMax | 0.8400 | 0.7000 | 0.9500 +albedoMinWinter | 0.5500 | 0.6000 | 1.0000 +albedoMinSpring | 0.5500 | 0.3000 | 1.0000 +albedoMaxVisible | 0.9500 | 0.7000 | 0.9500 +albedoMinVisible | 0.7500 | 0.5000 | 0.7500 +albedoMaxNearIR | 0.6500 | 0.5000 | 0.7500 +albedoMinNearIR | 0.3000 | 0.1500 | 0.4500 +albedoDecayRate | 1.0d+6 | 0.1d+6 | 5.0d+6 +albedoSootLoad | 0.3000 | 0.1000 | 0.5000 +albedoRefresh | 1.0000 | 1.0000 | 10.0000 +! ==================================================================== +! radiation transfer within snow +! ==================================================================== +radExt_snow | 20.0000 | 20.0000 | 20.0000 +directScale | 0.0900 | 0.0000 | 0.5000 +Frad_direct | 0.7000 | 0.0000 | 1.0000 +Frad_vis | 0.5000 | 0.0000 | 1.0000 +! ==================================================================== +! new snow density +! ==================================================================== +newSnowDenMin | 100.0000 | 50.0000 | 100.0000 +newSnowDenMult | 100.0000 | 25.0000 | 75.0000 +newSnowDenScal | 5.0000 | 1.0000 | 5.0000 +constSnowDen | 100.0000 | 50.0000 | 250.0000 +newSnowDenAdd | 109.0000 | 80.0000 | 120.0000 +newSnowDenMultTemp | 6.0000 | 1.0000 | 12.0000 +newSnowDenMultWind | 26.0000 | 16.0000 | 36.0000 +newSnowDenMultAnd | 1.0000 | 1.0000 | 3.0000 +newSnowDenBase | 0.0000 | 0.0000 | 0.0000 +! ==================================================================== +! snow compaction +! ==================================================================== +densScalGrowth | 0.0460 | 0.0230 | 0.0920 +tempScalGrowth | 0.0400 | 0.0200 | 0.0600 +grainGrowthRate | 2.7d-6 | 1.0d-6 | 5.0d-6 +densScalOvrbdn | 0.0230 | 0.0115 | 0.0460 +tempScalOvrbdn | 0.0800 | 0.6000 | 1.0000 +baseViscosity | 9.0d+5 | 5.0d+5 | 1.5d+6 +! ==================================================================== +! water flow through snow +! ==================================================================== +Fcapil | 0.0600 | 0.0100 | 0.1000 +k_snow | 0.0150 | 0.0050 | 0.0500 +mw_exp | 3.0000 | 1.0000 | 5.0000 +! ==================================================================== +! turbulent heat fluxes +! ==================================================================== +z0Snow | 0.0010 | 0.0010 | 10.0000 +z0Soil | 0.0100 | 0.0010 | 10.0000 +z0Canopy | 0.1000 | 0.0010 | 10.0000 +zpdFraction | 0.6500 | 0.5000 | 0.8500 +critRichNumber | 0.2000 | 0.1000 | 1.0000 +Louis79_bparam | 9.4000 | 9.2000 | 9.6000 +Louis79_cStar | 5.3000 | 5.1000 | 5.5000 +Mahrt87_eScale | 1.0000 | 0.5000 | 2.0000 +leafExchangeCoeff | 0.0100 | 0.0010 | 0.1000 +windReductionParam | 0.2800 | 0.0000 | 1.0000 +! ==================================================================== +! stomatal conductance +! ==================================================================== +Kc25 | 296.0770 | 296.0770 | 296.0770 +Ko25 | 0.2961 | 0.2961 | 0.2961 +Kc_qFac | 2.1000 | 2.1000 | 2.1000 +Ko_qFac | 1.2000 | 1.2000 | 1.2000 +kc_Ha | 79430.0000 | 79430.0000 | 79430.0000 +ko_Ha | 36380.0000 | 36380.0000 | 36380.0000 +vcmax25_canopyTop | 40.0000 | 20.0000 | 100.0000 +vcmax_qFac | 2.4000 | 2.4000 | 2.4000 +vcmax_Ha | 65330.0000 | 65330.0000 | 65330.0000 +vcmax_Hd | 220000.0000 | 149250.0000 | 149250.0000 +vcmax_Sv | 710.0000 | 485.0000 | 485.0000 +vcmax_Kn | 0.6000 | 0.0000 | 1.2000 +jmax25_scale | 2.0000 | 2.0000 | 2.0000 +jmax_Ha | 43540.0000 | 43540.0000 | 43540.0000 +jmax_Hd | 152040.0000 | 152040.0000 | 152040.0000 +jmax_Sv | 495.0000 | 495.0000 | 495.0000 +fractionJ | 0.1500 | 0.1500 | 0.1500 +quantamYield | 0.0500 | 0.0500 | 0.0500 +vpScaleFactor | 1500.0000 | 1500.0000 | 1500.0000 +cond2photo_slope | 9.0000 | 1.0000 | 10.0000 +minStomatalConductance | 2000.0000 | 2000.0000 | 2000.0000 +! ==================================================================== +! vegetation properties +! ==================================================================== +winterSAI | 1.0000 | 0.0100 | 3.0000 +summerLAI | 3.0000 | 0.0100 | 10.0000 +rootScaleFactor1 | 2.0000 | 1.0000 | 10.0000 +rootScaleFactor2 | 5.0000 | 1.0000 | 10.0000 +rootingDepth | 2.0000 | 0.0100 | 10.0000 +rootDistExp | 1.0000 | 0.0100 | 1.0000 +plantWiltPsi | -150.0000 | -500.0000 | 0.0000 +soilStressParam | 5.8000 | 4.3600 | 6.3700 +critSoilWilting | 0.0750 | 0.0000 | 1.0000 +critSoilTranspire | 0.1750 | 0.0000 | 1.0000 +critAquiferTranspire | 0.2000 | 0.1000 | 10.0000 +minStomatalResistance | 50.0000 | 10.0000 | 200.0000 +leafDimension | 0.0400 | 0.0100 | 0.1000 +heightCanopyTop | 20.0000 | 0.0500 | 100.0000 +heightCanopyBottom | 2.0000 | 0.0000 | 5.0000 +specificHeatVeg | 874.0000 | 500.0000 | 1500.0000 +maxMassVegetation | 25.0000 | 1.0000 | 50.0000 +throughfallScaleSnow | 0.5000 | 0.1000 | 0.9000 +throughfallScaleRain | 0.5000 | 0.1000 | 0.9000 +refInterceptCapSnow | 6.6000 | 1.0000 | 10.0000 +refInterceptCapRain | 1.0000 | 0.0100 | 1.0000 +snowUnloadingCoeff | 0.0000 | 0.0000 | 1.5d-6 +canopyDrainageCoeff | 0.0050 | 0.0010 | 0.0100 +ratioDrip2Unloading | 0.4000 | 0.0000 | 1.0000 +canopyWettingFactor | 0.7000 | 0.0000 | 1.0000 +canopyWettingExp | 1.0000 | 0.0000 | 1.0000 +! ==================================================================== +! soil properties +! ==================================================================== +soil_dens_intr | 2700.0000 | 500.0000 | 4000.0000 +thCond_soil | 5.5000 | 2.9000 | 8.4000 +frac_sand | 0.1600 | 0.0000 | 1.0000 +frac_silt | 0.2800 | 0.0000 | 1.0000 +frac_clay | 0.5600 | 0.0000 | 1.0000 +fieldCapacity | 0.2000 | 0.0000 | 1.0000 +wettingFrontSuction | 0.3000 | 0.1000 | 1.5000 +theta_mp | 0.4010 | 0.3000 | 0.6000 +theta_sat | 0.5500 | 0.3000 | 0.6000 +theta_res | 0.1390 | 0.0010 | 0.1000 +vGn_alpha | -0.8400 | -1.0000 | -0.0100 +vGn_n | 1.3000 | 1.0000 | 3.0000 +mpExp | 5.0000 | 1.0000 | 10.0000 +k_soil | 7.5d-06 | 1.d-07 | 100.d-07 +k_macropore | 1.0d-03 | 1.d-07 | 100.d-07 +kAnisotropic | 1.0000 | 0.0001 | 10.0000 +zScale_TOPMODEL | 2.5000 | 0.1000 | 100.0000 +compactedDepth | 1.0000 | 0.0000 | 1.0000 +aquiferScaleFactor | 0.3500 | 0.1000 | 100.0000 +aquiferBaseflowExp | 2.0000 | 1.0000 | 10.0000 +aquiferBaseflowRate | 2.0000 | 1.0000 | 10.0000 +qSurfScale | 50.0000 | 1.0000 | 100.0000 +specificYield | 0.2000 | 0.1000 | 0.3000 +specificStorage | 1.d-09 | 1.d-05 | 1.d-07 +f_impede | 2.0000 | 1.0000 | 10.0000 +soilIceScale | 0.1300 | 0.0001 | 1.0000 +soilIceCV | 0.4500 | 0.1000 | 5.0000 +! ==================================================================== +! algorithmic control parameters +! ==================================================================== +minwind | 0.1000 | 0.0010 | 1.0000 +minstep | 1.0000 | 1.0000 | 1800.0000 +maxstep | 3600.0000 | 60.0000 | 1800.0000 +be_steps | 1.0000 | 1.0000 | 512.0000 +wimplicit | 0.0000 | 0.0000 | 1.0000 +maxiter | 100.0000 | 1.0000 | 100.0000 +relConvTol_liquid | 1.0d-3 | 1.0d-5 | 1.0d-1 +absConvTol_liquid | 1.0d-6 | 1.0d-8 | 1.0d-3 +relConvTol_matric | 1.0d-6 | 1.0d-5 | 1.0d-1 +absConvTol_matric | 1.0d-6 | 1.0d-8 | 1.0d-3 +relConvTol_energy | 1.0d-2 | 1.0d-5 | 1.0d-1 +absConvTol_energy | 1.0d-0 | 1.0d-2 | 1.0d+1 +relConvTol_aquifr | 1.0d-0 | 1.0d-2 | 1.0d+1 +absConvTol_aquifr | 1.0d-5 | 1.0d-5 | 1.0d-1 +relErrTol_ida | 1.0d-6 | 1.0d-10| 1.0d-2 +absErrTol_ida | 1.0d-6 | 1.0d-10| 1.0d-2 +zmin | 0.0100 | 0.0050 | 0.1000 +zmax | 0.0500 | 0.0100 | 0.5000 +! --- +zminLayer1 | 0.0075 | 0.0075 | 0.0075 +zminLayer2 | 0.0100 | 0.0100 | 0.0100 +zminLayer3 | 0.0500 | 0.0500 | 0.0500 +zminLayer4 | 0.1000 | 0.1000 | 0.1000 +zminLayer5 | 0.2500 | 0.2500 | 0.2500 +! --- +zmaxLayer1_lower | 0.0500 | 0.0500 | 0.0500 +zmaxLayer2_lower | 0.2000 | 0.2000 | 0.2000 +zmaxLayer3_lower | 0.5000 | 0.5000 | 0.5000 +zmaxLayer4_lower | 1.0000 | 1.0000 | 1.0000 +! --- +zmaxLayer1_upper | 0.0300 | 0.0300 | 0.0300 +zmaxLayer2_upper | 0.1500 | 0.1500 | 0.1500 +zmaxLayer3_upper | 0.3000 | 0.3000 | 0.3000 +zmaxLayer4_upper | 0.7500 | 0.7500 | 0.7500 +! ==================================================================== +minTempUnloading | 270.16 | 260.16 | 273.16 +minWindUnloading | 0.0000 | 0.0000 | 10.000 +rateTempUnloading | 1.87d+5 | 1.0d+5 | 3.0d+5 +rateWindUnloading | 1.56d+5 | 1.0d+5 | 3.0d+5 \ No newline at end of file From 7811c726540557ee98863067a2acc3c034d3ebdd Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 5 Aug 2023 20:04:07 +0900 Subject: [PATCH 0823/1472] updating some settings in examples and adding to gitignore --- .gitignore | 2 ++ test_ngen/celia_test/settings/meta/Model_Output.txt | 4 +--- .../syntheticTestCases/celia1990/common/summa_zDecisions.txt | 2 +- .../celia1990/common/summa_zLocalParamInfo.txt | 3 +++ 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index caeff16e1..011cfe889 100755 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ # summa executable and bin directory bin summa.exe +summa_sundials.exe # temporary files *~ *.swp @@ -27,6 +28,7 @@ summa.exe *.layout gmon.out summa.exe.dSYM* +summa_sundials.exe.dSYM* summaversion.inc # makefile make.out diff --git a/test_ngen/celia_test/settings/meta/Model_Output.txt b/test_ngen/celia_test/settings/meta/Model_Output.txt index b2a732d2b..1d8b7ae78 100644 --- a/test_ngen/celia_test/settings/meta/Model_Output.txt +++ b/test_ngen/celia_test/settings/meta/Model_Output.txt @@ -35,6 +35,4 @@ basin__AquiferTranspire | 1 averageInstantRunoff | 1 averageRoutedRunoff | 1 scalarLAI | 1 -scalarSAI | 1 - - +scalarSAI | 1 \ No newline at end of file diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zDecisions.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zDecisions.txt index eb2eae897..574ba049f 100644 --- a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zDecisions.txt +++ b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zDecisions.txt @@ -13,7 +13,7 @@ vegeParTbl USGS ! (04) vegetation category datas soilStress NoahType ! (05) choice of function for the soil moisture control on stomatal resistance stomResist BallBerry ! (06) choice of function for stomatal resistance ! *********************************************************************************************************************** -num_method numrec ! (07) choice of numerical method +num_method numrec ! (07) choice of numerical method fDerivMeth analytic ! (08) method used to calculate flux derivatives LAI_method monTable ! (09) method used to determine LAI and SAI f_Richards mixdform ! (10) form of Richard's equation diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zLocalParamInfo.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zLocalParamInfo.txt index 92bbfe8d8..b39855861 100644 --- a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zLocalParamInfo.txt +++ b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zLocalParamInfo.txt @@ -190,6 +190,7 @@ soilIceCV | 0.4500 | 0.1000 | 5.0000 minwind | 0.1000 | 0.0010 | 1.0000 minstep | 1.0000 | 1.0000 | 1800.0000 maxstep | 3600.0000 | 60.0000 | 1800.0000 +be_steps | 1.0000 | 1.0000 | 512.0000 wimplicit | 0.0000 | 0.0000 | 1.0000 maxiter | 100.0000 | 1.0000 | 100.0000 relConvTol_liquid | 1.0d-3 | 1.0d-5 | 1.0d-1 @@ -200,6 +201,8 @@ relConvTol_energy | 1.0d-2 | 1.0d-5 | 1.0d-1 absConvTol_energy | 1.0d-0 | 1.0d-2 | 1.0d+1 relConvTol_aquifr | 1.0d-0 | 1.0d-2 | 1.0d+1 absConvTol_aquifr | 1.0d-5 | 1.0d-5 | 1.0d-1 +relErrTol_ida | 1.0d-6 | 1.0d-10| 1.0d-2 +absErrTol_ida | 1.0d-6 | 1.0d-10| 1.0d-2 zmin | 0.0100 | 0.0050 | 0.1000 zmax | 0.0500 | 0.0100 | 0.5000 ! --- From 161a5c1a734b143f01f2ba6e1770cbed69c629df Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sun, 6 Aug 2023 12:28:25 +0900 Subject: [PATCH 0824/1472] the case study folder never made it in here, adding it. most is somewhat legacy, would be better to direct people to laugh-test repository. Fixing parameter settings --- case_study/base_settings/localParamInfo.txt | 4 ++-- case_study/readme.md | 18 ++++++++++++++++++ .../celia1990/common/summa_zDecisions.txt | 17 ++++++++++------- .../celia1990/common/summa_zLocalParamInfo.txt | 4 ++-- 4 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 case_study/readme.md diff --git a/case_study/base_settings/localParamInfo.txt b/case_study/base_settings/localParamInfo.txt index 4e48f7d5e..8a216667e 100644 --- a/case_study/base_settings/localParamInfo.txt +++ b/case_study/base_settings/localParamInfo.txt @@ -194,13 +194,13 @@ soilIceCV | 0.4500 | 0.1000 | 5.0000 ! ==================================================================== minwind | 0.1000 | 0.0010 | 1.0000 minstep | 1.0000 | 1.0000 | 1800.0000 -maxstep | 3600.0000 | 60.0000 | 1800.0000 +maxstep | 3600.0000 | 60.0000 | 7200.0000 be_steps | 1.0000 | 1.0000 | 512.0000 wimplicit | 0.0000 | 0.0000 | 1.0000 maxiter | 100.0000 | 1.0000 | 100.0000 relConvTol_liquid | 1.0d-3 | 1.0d-5 | 1.0d-1 absConvTol_liquid | 1.0d-6 | 1.0d-8 | 1.0d-3 -relConvTol_matric | 1.0d-6 | 1.0d-5 | 1.0d-1 +relConvTol_matric | 1.0d-6 | 1.0d-7 | 1.0d-1 absConvTol_matric | 1.0d-6 | 1.0d-8 | 1.0d-3 relConvTol_energy | 1.0d-2 | 1.0d-5 | 1.0d-1 absConvTol_energy | 1.0d-0 | 1.0d-2 | 1.0d+1 diff --git a/case_study/readme.md b/case_study/readme.md new file mode 100644 index 000000000..38996f802 --- /dev/null +++ b/case_study/readme.md @@ -0,0 +1,18 @@ +# SUMMA case studies +This folder contains a case study to show how a typical SUMMA setup looks. The folder serves a double purpose as a way to track default versions of certain input files, such as the Noah-MP tables and spatially constant parameter files. + +For more details about SUMMA inputs, see the [online documentation](https://summa.readthedocs.io/en/latest/input_output/SUMMA_input/). + + +## Base settings +This folder contains the setting files that typically do not change for different model applications. Currently these include: +- `GENPARM.TBL`: lookup table for general parameters (legacy, currently unused) +- `MPTABLE.TBL`: lookup table for vegetation parameters +- `SOILPARM.TBL`: lookup table for soil parameters +- `VEGPARM.TBL`: lookup table for vegetation parameters +- `basinParamInfo.txt`: default values for GRU(basin)-level parameters +- `localParamInfo.txt`: default values for HRU(local)-level parameters + + +## Case studies +Instead of providing an example in the code, the user is directed to clone the Laugh-Tests github repository at https://git.cs.usask.ca/numerical_simulations_lab/laugh_tests.git. \ No newline at end of file diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zDecisions.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zDecisions.txt index 574ba049f..4c36cec0c 100644 --- a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zDecisions.txt +++ b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zDecisions.txt @@ -14,7 +14,7 @@ soilStress NoahType ! (05) choice of function for th stomResist BallBerry ! (06) choice of function for stomatal resistance ! *********************************************************************************************************************** num_method numrec ! (07) choice of numerical method -fDerivMeth analytic ! (08) method used to calculate flux derivatives +fDerivMeth analytic ! (08) method used to calculate Jacobian LAI_method monTable ! (09) method used to determine LAI and SAI f_Richards mixdform ! (10) form of Richard's equation groundwatr noXplict ! (11) choice of groundwater parameterization @@ -36,7 +36,7 @@ thCondSnow jrdn1991 ! (26) choice of thermal conduct thCondSoil mixConstit ! (27) choice of thermal conductivity representation for soil spatial_gw localColumn ! (28) choice of method for the spatial representation of groundwater subRouting timeDlay ! (29) choice of method for sub-grid routing -howHeatCap closedForm ! (30) +howHeatCap closedForm ! (30) choice of method to compute heat capacity in energy equation ! *********************************************************************************************** ! ***** description of the options available -- nothing below this point is read **************** ! *********************************************************************************************** @@ -50,7 +50,7 @@ howHeatCap closedForm ! (30) ! ROSETTA ! merged Rosetta table with STAS-RUC ! ----------------------------------------------------------------------------------------------- ! (04) vegetation category dataset -! USGS ! USGS 24/27 category dataset +! USGS ! USGS 24/27 category dataset ! MODIFIED_IGBP_MODIS_NOAH ! MODIS 20-category dataset ! ----------------------------------------------------------------------------------------------- ! (05) choice of function for the soil moisture control on stomatal resistance @@ -65,9 +65,9 @@ howHeatCap closedForm ! (30) ! (07) choice of numerical method ! numrec ! home-grown backward Euler ! kinsol ! SUNDIALS backward Euler solution using Kinsol -! sundials ! SUNDIALS solution using IDA +! ida ! SUNDIALS solution using IDA ! ----------------------------------------------------------------------------------------------- -! (08) method used to calculate flux derivatives +! (08) method used to calculate Jacobian ! numericl ! numerical derivatives ! analytic ! analytical derivatives ! ----------------------------------------------------------------------------------------------- @@ -120,7 +120,7 @@ howHeatCap closedForm ! (30) ! lightSnow ! maximum interception capacity an inverse function of new snow density ! ----------------------------------------------------------------------------------------------- ! (20) choice of wind profile -! exponential ! exponential wind profile extends to the surface +! exponential ! exponential wind profile extends to the surface ! logBelowCanopy ! logarithmic profile below the vegetation canopy ! ----------------------------------------------------------------------------------------------- ! (21) choice of stability function @@ -165,5 +165,8 @@ howHeatCap closedForm ! (30) ! (29) choice of method for sub-grid routing ! timeDlay ! time-delay histogram ! qInstant ! instantaneous routing +! ----------------------------------------------------------------------------------------------- +! (30) choice of method to compute heat capacity in energy equation +! closedForm ! closed form +! enthalpyFD ! enthalpy finite difference, has better coincidence of energy conservation ! *********************************************************************************************** -! history Mon Jul 20 16:08:18 2020: /pool0/home/andrbenn/data/summa_3/utils/convert_summa_config_v2_v3.py ./syntheticTestCases/celia1990/summa_fileManager_celia1990.txt diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zLocalParamInfo.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zLocalParamInfo.txt index b39855861..088ab0975 100644 --- a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zLocalParamInfo.txt +++ b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zLocalParamInfo.txt @@ -189,13 +189,13 @@ soilIceCV | 0.4500 | 0.1000 | 5.0000 ! ==================================================================== minwind | 0.1000 | 0.0010 | 1.0000 minstep | 1.0000 | 1.0000 | 1800.0000 -maxstep | 3600.0000 | 60.0000 | 1800.0000 +maxstep | 3600.0000 | 60.0000 | 7200.0000 be_steps | 1.0000 | 1.0000 | 512.0000 wimplicit | 0.0000 | 0.0000 | 1.0000 maxiter | 100.0000 | 1.0000 | 100.0000 relConvTol_liquid | 1.0d-3 | 1.0d-5 | 1.0d-1 absConvTol_liquid | 1.0d-5 | 1.0d-8 | 1.0d-3 -relConvTol_matric | 1.0d-6 | 1.0d-5 | 1.0d-1 +relConvTol_matric | 1.0d-6 | 1.0d-7 | 1.0d-1 absConvTol_matric | 1.0d-6 | 1.0d-8 | 1.0d-3 relConvTol_energy | 1.0d-2 | 1.0d-5 | 1.0d-1 absConvTol_energy | 1.0d-0 | 1.0d-2 | 1.0d+1 From 0ad666f13eaa89242cb1d10861fcc6ee54a55f65 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sun, 6 Aug 2023 12:29:20 +0900 Subject: [PATCH 0825/1472] remove print statements --- build/source/engine/volicePack.f90 | 4 ---- 1 file changed, 4 deletions(-) diff --git a/build/source/engine/volicePack.f90 b/build/source/engine/volicePack.f90 index 5006c888c..c9bb0b967 100644 --- a/build/source/engine/volicePack.f90 +++ b/build/source/engine/volicePack.f90 @@ -113,8 +113,6 @@ subroutine volicePack(& if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if endif -! print *, 'in coupled_em divideLayer = ', divideLayer - ! merge snow layers if they are too thin call layerMerge(& ! input/output: model data structures @@ -130,8 +128,6 @@ subroutine volicePack(& err,cmessage) ! intent(out): error control if(err/=0)then; err=65; message=trim(message)//trim(cmessage); return; end if -! print *, 'in coupled_em mergeLayers = ', mergedLayers - ! update the number of layers indx_data%var(iLookINDEX%nSnow)%dat(1) = count(indx_data%var(iLookINDEX%layerType)%dat==iname_snow) indx_data%var(iLookINDEX%nSoil)%dat(1) = count(indx_data%var(iLookINDEX%layerType)%dat==iname_soil) From 0842b70633c144ad7271273bb0998186a41f55fa Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sun, 6 Aug 2023 16:16:49 +0900 Subject: [PATCH 0826/1472] compSink check should have been on *dt since changed compression to averages (will affect BE solution) --- build/source/engine/varSubstep.f90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 4846a1e5a..c4d9665c2 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -991,10 +991,10 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! NOTE: fatal errors, though possible to recover using negative error codes if(count(ixSoilOnlyHyd/=integerMissing)==nSoil)then soilBalance1 = sum( (mLayerVolFracLiqTrial(nSnow+1:nLayers) + mLayerVolFracIceTrial(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) - vertFlux = -(iLayerLiqFluxSoil(nSoil) - iLayerLiqFluxSoil(0))*dt ! m s-1 --> m - tranSink = sum(mLayerTranspire)*dt ! m s-1 --> m - baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m - compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m + vertFlux = -(iLayerLiqFluxSoil(nSoil) - iLayerLiqFluxSoil(0))*dt ! m s-1 --> m + tranSink = sum(mLayerTranspire)*dt ! m s-1 --> m + baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m + compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) )*dt ! m s-1 --> m liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues !write(*,'(a,1x,f20.10)') 'dt = ', dt From 138538f6356beb59b7e137bc584ad19faeecb6ee Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sun, 6 Aug 2023 18:50:24 +0900 Subject: [PATCH 0827/1472] fixing spaces, and error in transpiration when splitting --- build/source/engine/opSplittin.f90 | 4 +- build/source/engine/soilLiqFlx.f90 | 70 ++++++++++----------- build/source/engine/varSubstep.f90 | 98 +++++++++++++++--------------- 3 files changed, 85 insertions(+), 87 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index e1d23ca8b..fbcab0541 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -180,7 +180,7 @@ subroutine opSplittin(& dtMultiplier, & ! intent(out): substep multiplier (-) tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt stepFailure, & ! intent(out): flag to denote step failure - ixCoupling, & ! intent(out): coupling method used in this iteration + ixSolution, & ! intent(out): solution method used in this iteration err,message) ! intent(out): error code and error message ! --------------------------------------------------------------------------------------- ! structure allocations @@ -219,6 +219,7 @@ subroutine opSplittin(& real(rkind),intent(out) :: dtMultiplier ! substep multiplier (-) logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt logical(lgt),intent(out) :: stepFailure ! flag to denote step failure + integer(i4b),intent(out) :: ixSolution ! index of solution method (1,2) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! --------------------------------------------------------------------------------------- @@ -256,7 +257,6 @@ subroutine opSplittin(& integer(i4b) :: nSubsteps ! number of substeps taken for a given split ! named variables defining the coupling and solution method integer(i4b) :: ixCoupling ! index of coupling method (1,2) - integer(i4b) :: ixSolution ! index of solution method (1,2) integer(i4b) :: ixStateThenDomain ! switch between the state and domain (1,2) integer(i4b) :: tryDomainSplit ! (0,1) - flag to try the domain split ! actual number of splits diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index 1954a4574..2351c66cd 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -124,29 +124,29 @@ subroutine soilLiqFlx(& xMaxInfilRate, & ! intent(inout): maximum infiltration rate (m s-1) scalarInfilArea, & ! intent(inout): fraction of unfrozen area where water can infiltrate (-) scalarFrozenArea, & ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) - scalarSurfaceRunoff, & ! intent(out): surface runoff (m s-1) + scalarSurfaceRunoff, & ! intent(inout): surface runoff (m s-1) ! output: diagnostic variables for model layers - mLayerdTheta_dPsi, & ! intent(out): derivative in the soil water characteristic w.r.t. psi (m-1) - mLayerdPsi_dTheta, & ! intent(out): derivative in the soil water characteristic w.r.t. theta (m) - dHydCond_dMatric, & ! intent(out): derivative in hydraulic conductivity w.r.t matric head (s-1) + mLayerdTheta_dPsi, & ! intent(inout): derivative in the soil water characteristic w.r.t. psi (m-1) + mLayerdPsi_dTheta, & ! intent(inout): derivative in the soil water characteristic w.r.t. theta (m) + dHydCond_dMatric, & ! intent(inout): derivative in hydraulic conductivity w.r.t matric head (s-1) ! output: fluxes - scalarSurfaceInfiltration, & ! intent(out): surface infiltration rate (m s-1) - iLayerLiqFluxSoil, & ! intent(out): liquid fluxes at layer interfaces (m s-1) - mLayerTranspire, & ! intent(out): transpiration loss from each soil layer (m s-1) - mLayerHydCond, & ! intent(out): hydraulic conductivity in each soil layer (m s-1) + scalarSurfaceInfiltration, & ! intent(inout): surface infiltration rate (m s-1) + iLayerLiqFluxSoil, & ! intent(inout): liquid fluxes at layer interfaces (m s-1) + mLayerTranspire, & ! intent(inout): transpiration loss from each soil layer (m s-1) + mLayerHydCond, & ! intent(inout): hydraulic conductivity in each soil layer (m s-1) ! output: derivatives in fluxes w.r.t. hydrology state variables -- matric head or volumetric lquid water -- in the layer above and layer below (m s-1 or s-1) - dq_dHydStateAbove, & ! intent(out): derivatives in the flux w.r.t. volumetric liquid water content in the layer above (m s-1) - dq_dHydStateBelow, & ! intent(out): derivatives in the flux w.r.t. volumetric liquid water content in the layer below (m s-1) - dq_dHydStateLayerSurfVec, & ! intent(out): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) + dq_dHydStateAbove, & ! intent(inout): derivatives in the flux w.r.t. volumetric liquid water content in the layer above (m s-1) + dq_dHydStateBelow, & ! intent(inout): derivatives in the flux w.r.t. volumetric liquid water content in the layer below (m s-1) + dq_dHydStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) - dq_dNrgStateAbove, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) - dq_dNrgStateBelow, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) - dq_dNrgStateLayerSurfVec, & ! intent(out): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) + dq_dNrgStateAbove, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) + dq_dNrgStateBelow, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) + dq_dNrgStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) ! output: derivatives in transpiration w.r.t. canopy state variables - mLayerdTrans_dTCanair, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature - mLayerdTrans_dTCanopy, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy temperature - mLayerdTrans_dTGround, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. ground temperature - mLayerdTrans_dCanWat, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy total water + mLayerdTrans_dTCanair, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + mLayerdTrans_dTCanopy, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy temperature + mLayerdTrans_dTGround, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. ground temperature + mLayerdTrans_dCanWat, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy total water ! output: error control err,message) ! intent(out): error control ! utility modules @@ -222,26 +222,26 @@ subroutine soilLiqFlx(& character(*),intent(out) :: message ! error message ! ----------------------------------------------------------------------------------------------------------------------------------------------------- ! local variables: general - character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b) :: ibeg,iend ! start and end indices of the soil layers in concatanated snow-soil vector - integer(i4b) :: iLayer,iSoil ! index of soil layer - integer(i4b) :: ixLayerDesired(1) ! layer desired (scalar solution) - integer(i4b) :: ixTop ! top layer in subroutine call - integer(i4b) :: ixBot ! bottom layer in subroutine call + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: ibeg,iend ! start and end indices of the soil layers in concatanated snow-soil vector + integer(i4b) :: iLayer,iSoil ! index of soil layer + integer(i4b) :: ixLayerDesired(1) ! layer desired (scalar solution) + integer(i4b) :: ixTop ! top layer in subroutine call + integer(i4b) :: ixBot ! bottom layer in subroutine call ! transpiration sink term - real(rkind),dimension(nSoil) :: mLayerTranspireFrac ! fraction of transpiration allocated to each soil layer (-) + real(rkind),dimension(nSoil) :: mLayerTranspireFrac ! fraction of transpiration allocated to each soil layer (-) ! diagnostic variables - real(rkind),dimension(nSoil) :: iceImpedeFac ! ice impedence factor at layer mid-points (-) - real(rkind),dimension(nSoil) :: mLayerDiffuse ! diffusivity at layer mid-point (m2 s-1) - real(rkind),dimension(nSoil) :: dHydCond_dVolLiq ! derivative in hydraulic conductivity w.r.t volumetric liquid water content (m s-1) - real(rkind),dimension(nSoil) :: dDiffuse_dVolLiq ! derivative in hydraulic diffusivity w.r.t volumetric liquid water content (m2 s-1) - real(rkind),dimension(nSoil) :: dHydCond_dTemp ! derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) - real(rkind),dimension(0:nSoil) :: iLayerHydCond ! hydraulic conductivity at layer interface (m s-1) - real(rkind),dimension(0:nSoil) :: iLayerDiffuse ! diffusivity at layer interface (m2 s-1) + real(rkind),dimension(nSoil) :: iceImpedeFac ! ice impedence factor at layer mid-points (-) + real(rkind),dimension(nSoil) :: mLayerDiffuse ! diffusivity at layer mid-point (m2 s-1) + real(rkind),dimension(nSoil) :: dHydCond_dVolLiq ! derivative in hydraulic conductivity w.r.t volumetric liquid water content (m s-1) + real(rkind),dimension(nSoil) :: dDiffuse_dVolLiq ! derivative in hydraulic diffusivity w.r.t volumetric liquid water content (m2 s-1) + real(rkind),dimension(nSoil) :: dHydCond_dTemp ! derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) + real(rkind),dimension(0:nSoil) :: iLayerHydCond ! hydraulic conductivity at layer interface (m s-1) + real(rkind),dimension(0:nSoil) :: iLayerDiffuse ! diffusivity at layer interface (m2 s-1) ! compute surface flux - integer(i4b) :: nRoots ! number of soil layers with roots - integer(i4b) :: ixIce ! index of the lowest soil layer that contains ice - real(rkind),dimension(0:nSoil) :: iLayerHeight ! height of the layer interfaces (m) + integer(i4b) :: nRoots ! number of soil layers with roots + integer(i4b) :: ixIce ! index of the lowest soil layer that contains ice + real(rkind),dimension(0:nSoil) :: iLayerHeight ! height of the layer interfaces (m) ! compute fluxes and derivatives at layer interfaces real(rkind) :: scalardPsi_dTheta ! derivative in soil water characteristix, used for perturbations when computing numerical derivatives ! ------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index c4d9665c2..8b22f9c50 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -501,14 +501,13 @@ subroutine varSubstep(& ixMax=ubound(flux_data%var(iVar)%dat) do ixLayer=ixMin(1),ixMax(1) if(fluxMask%var(iVar)%dat(ixLayer)) then - ! Makes Jacobian more diagonal if do this, but less correct ! special case of the transpiration sink from soil layers: only computed for the top soil layer - !if(iVar==iLookFlux%mLayerTranspire)then - ! if(ixLayer==1) flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght + if(iVar==iLookFlux%mLayerTranspire)then + if(ixLayer==1) flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght ! standard case - !else + else flux_mean%var(iVar)%dat(ixLayer) = flux_mean%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght - !endif + endif fluxCount%var(iVar)%dat(ixLayer) = fluxCount%var(iVar)%dat(ixLayer) + 1 endif end do @@ -663,62 +662,61 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! point to flux variables in the data structure associate(& ! model decisions - ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver ! get indices for mass balance - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in) : [i4b] index of canopy hydrology state variable (mass) - ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the soil domain + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in) : [i4b] index of canopy hydrology state variable (mass) + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the soil domain ! get indices for the un-tapped melt - ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in) : [i4b(:)] list of indices for all energy states - ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in) : [i4b(:)] indices defining the domain of the state (iname_veg, iname_snow, iname_soil) - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in) : [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in) : [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in) : [i4b(:)] list of indices for all energy states + ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in) : [i4b(:)] indices defining the domain of the state (iname_veg, iname_snow, iname_soil) + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in) : [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in) : [i4b(:)] [state subset] list of indices of the full state vector in the state subset ! water fluxes - scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in) : [dp] rainfall rate (kg m-2 s-1) - scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(in) : [dp] rain reaches ground without touching the canopy (kg m-2 s-1) - scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(in) : [dp] canopy evaporation/condensation (kg m-2 s-1) - scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(in) : [dp] canopy transpiration (kg m-2 s-1) - scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(in) : [dp] drainage liquid water from vegetation canopy (kg m-2 s-1) - iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(in) : [dp(0:)] vertical liquid water flux at soil layer interfaces (-) - iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(in) : - mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) - mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(in) : [dp(:)] transpiration loss from each soil layer (m s-1) - mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(in) : [dp(:)] baseflow from each soil layer (m s-1) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in) : [dp(:)] change in storage associated with compression of the soil matrix (-) - scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the vegetation canopy (kg m-2 s-1) - scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the snow surface (kg m-2 s-1) + scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in) : [dp] rainfall rate (kg m-2 s-1) + scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(in) : [dp] rain reaches ground without touching the canopy (kg m-2 s-1) + scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(in) : [dp] canopy evaporation/condensation (kg m-2 s-1) + scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(in) : [dp] drainage liquid water from vegetation canopy (kg m-2 s-1) + iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(in) : [dp(0:)] vertical liquid water flux at soil layer interfaces (-) + iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(in) : + mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out) : [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) + mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(in) : [dp(:)] transpiration loss from each soil layer (m s-1) + mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(in) : [dp(:)] baseflow from each soil layer (m s-1) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in) : [dp(:)] change in storage associated with compression of the soil matrix (-) + scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the vegetation canopy (kg m-2 s-1) + scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the snow surface (kg m-2 s-1) ! energy fluxes - scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ,& ! intent(in) : [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ,& ! intent(in) : [dp] sensible heat flux from the canopy to the canopy air space (W m-2) + scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ,& ! intent(in) : [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ,& ! intent(in) : [dp] sensible heat flux from the canopy to the canopy air space (W m-2) ! domain depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in) : [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in) : [dp(:)] depth of each layer in the snow-soil sub-domain (m) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in) : [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in) : [dp(:)] depth of each layer in the snow-soil sub-domain (m) ! model state variables (vegetation canopy) - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout) : [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout) : [dp] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout) : [dp] mass of total water on the vegetation canopy (kg m-2) + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout): [dp] temperature of the vegetation canopy (K) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout): [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout): [dp] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout): [dp] mass of total water on the vegetation canopy (kg m-2) ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout) : [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout) : [dp(:)] matric head (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(inout) : [dp(:)] matric potential of liquid water (m) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(inout): [dp(:)] matric potential of liquid water (m) ! enthalpy - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(inout): [dp(:)] enthalpy of the snow+soil layers (J m-3) + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(inout): [dp(:)] enthalpy of the snow+soil layers (J m-3) ! derivatives, diagnositic for enthalpy - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) ! model state variables (aquifer) - scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(inout) : [dp(:)] storage of water in the aquifer (m) + scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(inout): [dp(:)] storage of water in the aquifer (m) ! error tolerance - absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1) & ! intent(in) : [dp] absolute convergence tolerance for vol frac liq water (-) + absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1) & ! intent(in) : [dp] absolute convergence tolerance for vol frac liq water (-) ) ! associating flux variables in the data structure ! ------------------------------------------------------------------------------------------------------------------- ! initialize error control From 8d15e4e2d59c016678c5925ed34a137e21ed9eed Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 7 Aug 2023 22:30:10 +0900 Subject: [PATCH 0828/1472] use banded matrix if no veg --- build/source/engine/layerDivide.f90 | 2 +- build/source/engine/systemSolv.f90 | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/source/engine/layerDivide.f90 b/build/source/engine/layerDivide.f90 index 606c514a5..256f79b4e 100644 --- a/build/source/engine/layerDivide.f90 +++ b/build/source/engine/layerDivide.f90 @@ -185,7 +185,7 @@ subroutine layerDivide(& ! check if create the first snow layer select case(ix_snowLayers) case(sameRulesAllLayers); createLayer = (scalarSnowDepth > zmax) - case(rulesDependLayerIndex); createLayer = (scalarSnowDepth > (zminLayer1 + zmaxLayer1_lower)/2._rkind) ! Initialize the first layer if we're halfway between the minimum and maximum depth for this layer. This gives some room for the layer to change depth in either direction and avoids excessive layer creation/deletion + case(rulesDependLayerIndex); createLayer = (scalarSnowDepth > zmaxLayer1_lower) case default; err=20; message=trim(message)//'unable to identify option to combine/sub-divide snow layers'; return end select ! (option to combine/sub-divide snow layers) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index d85954972..a71a0d40d 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -217,7 +217,7 @@ subroutine systemSolv(& ! ------------------------------------------------------------------------------------------------------ ! * model solver ! ------------------------------------------------------------------------------------------------------ - logical(lgt),parameter :: forceFullMatrix=.true. ! flag to force the use of the full Jacobian matrix + logical(lgt),parameter :: forceFullMatrix=.false. ! flag to force the use of the full Jacobian matrix integer(i4b) :: ixMatrix ! form of matrix (band diagonal or full matrix) type(var_dlength) :: flux_init ! model fluxes at the start of the time step real(rkind),allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! NOTE: allocatable, since not always needed @@ -347,7 +347,7 @@ subroutine systemSolv(& ! identify the matrix solution method ! (the type of matrix used to solve the linear system A.X=B) - if(local_ixGroundwater==qbaseTopmodel .or. scalarSolution .or. forceFullMatrix)then + if(local_ixGroundwater==qbaseTopmodel .or. scalarSolution .or. forceFullMatrix .or. computeVegFlux)then nLeadDim=nState ! length of the leading dimension ixMatrix=ixFullMatrix ! named variable to denote the full Jacobian matrix else From ede642440a3045930b3e67425499eb6d6e2c3cbb Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 8 Aug 2023 22:10:36 +0900 Subject: [PATCH 0829/1472] spaces --- build/source/engine/run_oneGRU.f90 | 30 ++++++--------------------- build/source/netcdf/read_icond.f90 | 33 +++++++++++------------------- 2 files changed, 18 insertions(+), 45 deletions(-) diff --git a/build/source/engine/run_oneGRU.f90 b/build/source/engine/run_oneGRU.f90 index aa67cf5e0..25e4a95c2 100644 --- a/build/source/engine/run_oneGRU.f90 +++ b/build/source/engine/run_oneGRU.f90 @@ -59,9 +59,6 @@ module run_oneGRU_module localColumn, & ! separate groundwater representation in each local soil column singleBasin, & ! single groundwater store over the entire basin bigBucket ! a big bucket (lumped aquifer model) - -! ----------------------------------------------------------------------------------------------------------------------------------- -! ----------------------------------------------------------------------------------------------------------------------------------- ! ----------------------------------------------------------------------------------------------------------------------------------- implicit none private @@ -97,14 +94,10 @@ subroutine run_oneGRU(& err,message) ! intent(out): error control ! ----- define downstream subroutines ----------------------------------------------------------------------------------- - USE run_oneHRU_module,only:run_oneHRU ! module to run for one HRU USE qTimeDelay_module,only:qOverland ! module to route water through an "unresolved" river network - ! ----- define dummy variables ------------------------------------------------------------------------------------------ - implicit none - ! model control type(gru2hru_map) , intent(inout) :: gruInfo ! HRU information for given GRU (# HRUs, #snow+soil layers) real(rkind) , intent(inout) :: dt_init(:) ! used to initialize the length of the sub-step for each HRU @@ -126,9 +119,7 @@ subroutine run_oneGRU(& ! error control integer(i4b) , intent(out) :: err ! error code character(*) , intent(out) :: message ! error message - ! ----- define local variables ------------------------------------------------------------------------------------------ - ! general local variables character(len=256) :: cmessage ! error message integer(i4b) :: iHRU ! HRU index @@ -160,10 +151,7 @@ subroutine run_oneGRU(& fluxHRU%hru(iHRU)%var(iLookFLUX%mLayerColumnInflow)%dat(:) = 0._rkind end do - ! *********************************************************************************************************************** ! ********** RUN FOR ONE HRU ******************************************************************************************** - ! *********************************************************************************************************************** - ! loop through HRUs do iHRU=1,gruInfo%hruCount @@ -217,7 +205,6 @@ subroutine run_oneGRU(& ! (Note: for efficiency, this could this be done as a setup task, not every timestep) ! ----- compute fluxes across HRUs -------------------------------------------------------------------------------------------------- - ! identify lateral connectivity ! (Note: for efficiency, this could this be done as a setup task, not every timestep) kHRU = 0 @@ -241,30 +228,25 @@ subroutine run_oneGRU(& end if ! ----- calculate weighted basin (GRU) fluxes -------------------------------------------------------------------------------------- - ! increment basin surface runoff (m s-1) - bvarData%var(iLookBVAR%basin__SurfaceRunoff)%dat(1) = bvarData%var(iLookBVAR%basin__SurfaceRunoff)%dat(1) + fluxHRU%hru(iHRU)%var(iLookFLUX%scalarSurfaceRunoff)%dat(1) * fracHRU + bvarData%var(iLookBVAR%basin__SurfaceRunoff)%dat(1) = bvarData%var(iLookBVAR%basin__SurfaceRunoff)%dat(1) + fluxHRU%hru(iHRU)%var(iLookFLUX%scalarSurfaceRunoff)%dat(1)*fracHRU ! increment basin soil drainage (m s-1) - bvarData%var(iLookBVAR%basin__SoilDrainage)%dat(1) = bvarData%var(iLookBVAR%basin__SoilDrainage)%dat(1) + fluxHRU%hru(iHRU)%var(iLookFLUX%scalarSoilDrainage)%dat(1) * fracHRU + bvarData%var(iLookBVAR%basin__SoilDrainage)%dat(1) = bvarData%var(iLookBVAR%basin__SoilDrainage)%dat(1) + fluxHRU%hru(iHRU)%var(iLookFLUX%scalarSoilDrainage)%dat(1) *fracHRU ! increment aquifer variables -- ONLY if aquifer baseflow is computed individually for each HRU and aquifer is run ! NOTE: groundwater computed later for singleBasin if(model_decisions(iLookDECISIONS%spatial_gw)%iDecision == localColumn .and. model_decisions(iLookDECISIONS%groundwatr)%iDecision == bigBucket) then - - bvarData%var(iLookBVAR%basin__AquiferRecharge)%dat(1) = bvarData%var(iLookBVAR%basin__AquiferRecharge)%dat(1) + fluxHRU%hru(iHRU)%var(iLookFLUX%scalarAquiferRecharge)%dat(1) * fracHRU - bvarData%var(iLookBVAR%basin__AquiferTranspire)%dat(1) = bvarData%var(iLookBVAR%basin__AquiferTranspire)%dat(1) + fluxHRU%hru(iHRU)%var(iLookFLUX%scalarAquiferTranspire)%dat(1) * fracHRU - bvarData%var(iLookBVAR%basin__AquiferBaseflow)%dat(1) = bvarData%var(iLookBVAR%basin__AquiferBaseflow)%dat(1) & - + fluxHRU%hru(iHRU)%var(iLookFLUX%scalarAquiferBaseflow)%dat(1) * fracHRU + bvarData%var(iLookBVAR%basin__AquiferRecharge)%dat(1) = bvarData%var(iLookBVAR%basin__AquiferRecharge)%dat(1) + fluxHRU%hru(iHRU)%var(iLookFLUX%scalarAquiferRecharge)%dat(1) *fracHRU + bvarData%var(iLookBVAR%basin__AquiferTranspire)%dat(1) = bvarData%var(iLookBVAR%basin__AquiferTranspire)%dat(1) + fluxHRU%hru(iHRU)%var(iLookFLUX%scalarAquiferTranspire)%dat(1)*fracHRU + bvarData%var(iLookBVAR%basin__AquiferBaseflow)%dat(1) = bvarData%var(iLookBVAR%basin__AquiferBaseflow)%dat(1) + fluxHRU%hru(iHRU)%var(iLookFLUX%scalarAquiferBaseflow)%dat(1) *fracHRU end if ! averaging more fluxes (and/or states) can be added to this section as desired end do ! (looping through HRUs) - - ! *********************************************************************************************************************** ! ********** END LOOP THROUGH HRUS ************************************************************************************** - ! *********************************************************************************************************************** + ! perform the routing associate(totalArea => bvarData%var(iLookBVAR%basin__totalArea)%dat(1) ) diff --git a/build/source/netcdf/read_icond.f90 b/build/source/netcdf/read_icond.f90 index f8c210078..da1c6fb3b 100644 --- a/build/source/netcdf/read_icond.f90 +++ b/build/source/netcdf/read_icond.f90 @@ -58,18 +58,17 @@ subroutine read_icond_nlayers(iconFile,nGRU,indx_meta,err,message) type(var_info) ,intent(in) :: indx_meta(:) ! metadata integer(i4b) ,intent(out) :: err ! error code character(*) ,intent(out) :: message ! returned error message - ! locals - integer(i4b) :: ncID ! netcdf file id - integer(i4b) :: dimID ! netcdf file dimension id - integer(i4b) :: fileHRU ! number of HRUs in netcdf file - integer(i4b) :: snowID, soilID ! netcdf variable ids - integer(i4b) :: iGRU, iHRU ! loop indexes - integer(i4b) :: iHRU_local ! index of HRU in the data subset - integer(i4b) :: iHRU_global ! index of HRU in the netcdf file - integer(i4b),allocatable :: snowData(:) ! number of snow layers in all HRUs - integer(i4b),allocatable :: soilData(:) ! number of soil layers in all HRUs - character(len=256) :: cmessage ! downstream error message + integer(i4b) :: ncID ! netcdf file id + integer(i4b) :: dimID ! netcdf file dimension id + integer(i4b) :: fileHRU ! number of HRUs in netcdf file + integer(i4b) :: snowID, soilID ! netcdf variable ids + integer(i4b) :: iGRU, iHRU ! loop indexes + integer(i4b) :: iHRU_local ! index of HRU in the data subset + integer(i4b) :: iHRU_global ! index of HRU in the netcdf file + integer(i4b),allocatable :: snowData(:) ! number of snow layers in all HRUs + integer(i4b),allocatable :: soilData(:) ! number of soil layers in all HRUs + character(len=256) :: cmessage ! downstream error message ! -------------------------------------------------------------------------------------------------------- ! initialize error message @@ -112,18 +111,15 @@ subroutine read_icond_nlayers(iconFile,nGRU,indx_meta,err,message) do iGRU = 1,nGRU do iHRU = 1,gru_struc(iGRU)%hruCount iHRU_global = gru_struc(iGRU)%hruInfo(iHRU)%hru_nc - ! single HRU (Note: 'restartFileType' is hardwired above to multiHRU) if(restartFileType==singleHRU) then gru_struc(iGRU)%hruInfo(iHRU)%nSnow = snowData(1) gru_struc(iGRU)%hruInfo(iHRU)%nSoil = soilData(1) - ! multi HRU else gru_struc(iGRU)%hruInfo(iHRU)%nSnow = snowData(iHRU_global) gru_struc(iGRU)%hruInfo(iHRU)%nSoil = soilData(iHRU_global) endif - end do end do @@ -172,7 +168,6 @@ subroutine read_icond(iconFile, & ! intent(in): name of USE updatState_module,only:updateSoil ! update soil states implicit none - ! -------------------------------------------------------------------------------------------------------- ! variable declarations ! dummies @@ -184,7 +179,6 @@ subroutine read_icond(iconFile, & ! intent(in): name of type(gru_hru_intVec) ,intent(inout) :: indxData ! model indices integer(i4b) ,intent(out) :: err ! error code character(*) ,intent(out) :: message ! returned error message - ! locals character(len=256) :: cmessage ! downstream error message integer(i4b) :: fileHRU ! number of HRUs in file @@ -201,13 +195,12 @@ subroutine read_icond(iconFile, & ! intent(in): name of integer(i4b) :: ixFile ! index in file integer(i4b) :: iHRU_local ! index of HRU in the data subset integer(i4b) :: iHRU_global ! index of HRU in the netcdf file - real(rkind),allocatable :: varData(:,:) ! variable data storage + real(rkind),allocatable :: varData(:,:) ! variable data storage integer(i4b) :: nSoil, nSnow, nToto ! # layers integer(i4b) :: nTDH ! number of points in time-delay histogram integer(i4b) :: iLayer,jLayer ! layer indices integer(i4b),parameter :: nBand=2 ! number of spectral bands integer(i4b) :: nProgVars ! number of prognostic variables written to state file - character(len=32),parameter :: scalDimName ='scalarv' ! dimension name for scalar data character(len=32),parameter :: midSoilDimName='midSoil' ! dimension name for soil-only layers character(len=32),parameter :: midTotoDimName='midToto' ! dimension name for layered varaiables @@ -215,7 +208,6 @@ subroutine read_icond(iconFile, & ! intent(in): name of character(len=32),parameter :: tdhDimName ='tdh' ! dimension name for time-delay basin variables ! -------------------------------------------------------------------------------------------------------- - ! Start procedure here err=0; message="read_icond/" @@ -318,7 +310,7 @@ subroutine read_icond(iconFile, & ! intent(in): name of if(err==20)then; message=trim(message)//"data set to the fill value (name='"//trim(prog_meta(iVar)%varName)//"')"; return; endif - ! fix the snow albedo + ! make sure snow albedo is not negative if(progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarSnowAlbedo)%dat(1) < 0._rkind)then progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarSnowAlbedo)%dat(1) = mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%albedoMax)%dat(1) endif @@ -361,7 +353,6 @@ subroutine read_icond(iconFile, & ! intent(in): name of ! -------------------------------------------------------------------------------------------------------- ! (3) update soil layers (diagnostic variables) ! -------------------------------------------------------------------------------------------------------- - ! loop through GRUs and HRUs do iGRU = 1,nGRU do iHRU = 1,gru_struc(iGRU)%hruCount From b08d725d53ead90a39f60668383e0485c213162c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 8 Aug 2023 22:43:01 +0900 Subject: [PATCH 0830/1472] initialization of should be on canopy water, not canopy liquid --- build/source/netcdf/read_icond.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/netcdf/read_icond.f90 b/build/source/netcdf/read_icond.f90 index da1c6fb3b..07e6eff06 100644 --- a/build/source/netcdf/read_icond.f90 +++ b/build/source/netcdf/read_icond.f90 @@ -316,8 +316,8 @@ subroutine read_icond(iconFile, & ! intent(in): name of endif ! make sure canopy water is positive - if( progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyLiq)%dat(1) < 0.0001_rkind)then - progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyLiq)%dat(1) = 0.0001_rkind + if( progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyWat)%dat(1) < 0.0001_rkind)then + progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyWat)%dat(1) = 0.0001_rkind endif ! initialize the spectral albedo From 3f11144593daceaa7e3b2fbab4f34c633f1739f0 Mon Sep 17 00:00:00 2001 From: kck540 Date: Tue, 8 Aug 2023 11:36:07 -0400 Subject: [PATCH 0831/1472] Add changes to actors to call intermediate file for mDecisions to keep consitent function signature --- build/cmake/CMakeLists.txt | 1 + build/source/engine/mDecisions.f90 | 18 +++++------------- build/source/engine/read_pinit.f90 | 4 ++-- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 565902153..f507f5d2d 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -122,6 +122,7 @@ if(CMAKE_BUILD_TYPE MATCHES Sundials) "${PARENT_DIR}../sundials/instdir" "${F_MASTER}../../SummaSundials/sundials/instdir" "${PARENT_DIR}../../SummaSundials/sundials/instdir" + "/home/kklenk/projects/rpp-kshook/CompHydCore/SummaSundials/sundials/sundials/instdir" "../../../../SummaSundials/sundials/instdir") #look in same head directory as summa or up to another installation if(EXISTS ${dir}) set(DIR_SUNDIALS ${dir}) diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index 4826a3932..640d158db 100755 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -156,15 +156,11 @@ module mDecisions_module ! ----------------------------------------------------------------------------------------------------------- contains - + ! ************************************************************************************************ ! public subroutine mDecisions: save model decisions as named integers ! ************************************************************************************************ -#ifdef ACTORS_ACTIVE -subroutine mDecisions(num_steps,err) bind(C, name='mDecisions') -#else subroutine mDecisions(err,message) -#endif ! model time structures USE multiconst,only:secprday ! number of seconds in a day USE var_lookup,only:iLookTIME ! named variables that identify indices in the time structures @@ -193,15 +189,10 @@ subroutine mDecisions(err,message) implicit none ! define output, depends on if using Actors -#ifdef ACTORS_ACTIVE - integer(c_int),intent(out) :: num_steps ! number of time steps in the simulation - integer(c_int),intent(out) :: err ! error code - character(256) :: message ! error message -#else integer(i4b) :: num_steps ! number of time steps in the simulation integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message -#endif + ! define local variables character(len=256) :: cmessage ! error message for downwind routine real(rkind) :: dsec,dsec_tz ! second @@ -604,8 +595,8 @@ subroutine mDecisions(err,message) ! choice of method to combine and sub-divide snow layers select case(trim(model_decisions(iLookDECISIONS%snowLayers)%cDecision)) - case('jrdn1991'); model_decisions(iLookDECISIONS%snowLayers)%iDecision = sameRulesAllLayers ! SNTHERM option: same combination/sub-division rules applied to all layers - case('CLM_2010'); model_decisions(iLookDECISIONS%snowLayers)%iDecision = rulesDependLayerIndex ! CLM option: combination/sub-division rules depend on layer index + case('jrdn1991'); model_decisions(iLookDECISIONS%snowLayers)%iDecision = sameRulesAllLayers ! SNTHERM option: same combination/sub-dividion rules applied to all layers + case('CLM_2010'); model_decisions(iLookDECISIONS%snowLayers)%iDecision = rulesDependLayerIndex ! CLM option: combination/sub-dividion rules depend on layer index case default err=10; message=trim(message)//"unknown option for combination/sub-division of snow layers [option="//trim(model_decisions(iLookDECISIONS%snowLayers)%cDecision)//"]"; return end select @@ -753,3 +744,4 @@ subroutine readoption(err,message) end do end subroutine readoption end module mDecisions_module + \ No newline at end of file diff --git a/build/source/engine/read_pinit.f90 b/build/source/engine/read_pinit.f90 index 34be9581c..e419807b2 100644 --- a/build/source/engine/read_pinit.f90 +++ b/build/source/engine/read_pinit.f90 @@ -136,10 +136,10 @@ subroutine read_pinit(filenm,isLocal,mpar_meta,parFallback,err,message) parFallback(iLookPARAM%be_steps)%default_val = 1._rkind end if if (parFallback(iLookPARAM%relErrTol_ida)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%relErrTol_ida)%default_val = 1.e-6_rkind + parFallback(iLookPARAM%relErrTol_ida)%default_val = 1.e-4_rkind end if if (parFallback(iLookPARAM%absErrTol_ida)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%absErrTol_ida)%default_val = 1.e-6_rkind + parFallback(iLookPARAM%absErrTol_ida)%default_val = 1.e-4_rkind end if end if From 68378b754bc58d56e63c28556ea67facd1842315 Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Wed, 9 Aug 2023 08:29:27 -0600 Subject: [PATCH 0832/1472] modified cmake build for working on personal computer --- build/cmake/CMakeLists.txt | 4 +++- build/cmake/build.mac.bash | 9 ++++----- build/cmake/build_actors.mac.bash | 10 +++++++--- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index f507f5d2d..4ff65258c 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -122,6 +122,7 @@ if(CMAKE_BUILD_TYPE MATCHES Sundials) "${PARENT_DIR}../sundials/instdir" "${F_MASTER}../../SummaSundials/sundials/instdir" "${PARENT_DIR}../../SummaSundials/sundials/instdir" + "/usr/local/sundials-6.3.0" "/home/kklenk/projects/rpp-kshook/CompHydCore/SummaSundials/sundials/sundials/instdir" "../../../../SummaSundials/sundials/instdir") #look in same head directory as summa or up to another installation if(EXISTS ${dir}) @@ -194,6 +195,7 @@ else() "${PARENT_DIR}../actor-framework/install" "${F_MASTER}../../SummaActors/actor-framework/install" "${PARENT_DIR}../../SummaActors/actor-framework/install" + "/usr/local" "../../../../SummaActors/actor-framework/install") #look in same head directory as summa or up to another installation if(EXISTS ${dir}) set(DIR_ACTORS ${dir}) @@ -393,9 +395,9 @@ set(SOLVER_SUNDIALS set(DRIVER ${DRIVER_DIR}/summa_alarms.f90 ${SUB_DRIVER_DIR}/summa_globalData.f90 - ${SUB_DRIVER_DIR}/summa_type.f90 ${SUB_DRIVER_DIR}/summa_util.f90) set(DRIVER_NOT_ACTORS + ${DRIVER_DIR}/summa_type.f90 ${DRIVER_DIR}/summa_defineOutput.f90 ${DRIVER_DIR}/summa_init.f90 ${DRIVER_DIR}/summa_forcing.f90 diff --git a/build/cmake/build.mac.bash b/build/cmake/build.mac.bash index 4bbb60f85..0e85a6a75 100755 --- a/build/cmake/build.mac.bash +++ b/build/cmake/build.mac.bash @@ -5,10 +5,9 @@ # Mac Example using MacPorts: export FC=gfortran # Fortran compiler family -export LINK_DIRS=/opt/local/lib # Link directories for cmake -export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) +export LINK_DIRS='/usr/local/lib;/usr/lib/' # Link directories for cmake +export INCLUDES_DIRS='/usr/local/include;/usr/local/lib;/usr/include' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) -cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials -cmake --build ../cmake_build --target all - +cmake -B ../cmake_build_reg -S . -DCMAKE_BUILD_TYPE=Sundials +cmake --build ../cmake_build_reg --target all \ No newline at end of file diff --git a/build/cmake/build_actors.mac.bash b/build/cmake/build_actors.mac.bash index bc43513be..e91883224 100755 --- a/build/cmake/build_actors.mac.bash +++ b/build/cmake/build_actors.mac.bash @@ -1,7 +1,11 @@ #!/bin/bash # build on Mac, from cmake directory run this as ./build_actors.mac.bash - -cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials_Actors -cmake --build ../cmake_build --target all +export FC=gfortran # Fortran compiler family +export LINK_DIRS='/usr/lib;/usr/lib/x86_64-linux-gnu' # Link directories for cmake +export INCLUDES_DIRS='/usr/include;/usr/local/include' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) +export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) +export DIR_SUNDIALS='/usr/local/sundials-6.3.0' +cmake -B ../cmake_build_regular -S . -DCMAKE_BUILD_TYPE=Sundials_Actors +cmake --build ../cmake_build_regular --target all From 5c194a4d13a7e2a84e8512a2e8f9d01cbace316a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 10 Aug 2023 13:12:14 +0900 Subject: [PATCH 0833/1472] Don't want to restart scalar aquifer at a different value (basin okay to restart at 1) --- build/source/engine/computResid.f90 | 1 - build/source/engine/coupled_em.f90 | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/build/source/engine/computResid.f90 b/build/source/engine/computResid.f90 index 66d2010cd..dde88bee5 100644 --- a/build/source/engine/computResid.f90 +++ b/build/source/engine/computResid.f90 @@ -265,7 +265,6 @@ subroutine computResid(& ! compute the residual vector for the aquifer if(ixAqWat/=integerMissing) rVec(ixAqWat) = sMul(ixAqWat)*scalarAquiferStorageTrial - ( sMul(ixAqWat)*scalarAquiferStorage + fVec(ixAqWat)*dt + rAdd(ixAqWat) ) - if(globalPrintFlag)then write(*,'(a,1x,100(e12.5,1x))') 'rVec = ', rVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) write(*,'(a,1x,100(e12.5,1x))') 'fVec = ', fVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index ef4ff7c62..be895baab 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -318,7 +318,7 @@ subroutine coupled_em(& lastInnerStep = .false. ! count the number of snow and soil layers - ! NOTE: need to re-compute the number of snow and soil layers at the start of each sub-step because the number of layers may change + ! NOTE: need to recompute the number of snow and soil layers at the start of each sub-step because the number of layers may change ! (nSnow and nSoil are shared in the data structure) nSnow = count(indx_data%var(iLookINDEX%layerType)%dat==iname_snow) nSoil = count(indx_data%var(iLookINDEX%layerType)%dat==iname_soil) @@ -1116,7 +1116,7 @@ subroutine coupled_em(& err,cmessage) ! error control if(err/=0)then; err=30; message=trim(message)//trim(cmessage); return; end if - ! re-compute snow depth, SWE, and top layer water + ! recompute snow depth, SWE, and top layer water if(nSnow > 0)then prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow)) prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow)*iden_water + & From 19e986ed493f43703bf2a27952844fc95f29f27c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 10 Aug 2023 17:49:03 +0900 Subject: [PATCH 0834/1472] spaces --- build/source/engine/computFlux.f90 | 20 ++-- build/source/engine/coupled_em.f90 | 4 +- build/source/engine/varSubstep.f90 | 2 - build/source/engine/vegNrgFlux.f90 | 181 ++++++++++++++--------------- 4 files changed, 102 insertions(+), 105 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index a88aa71bf..8c18b1822 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -474,16 +474,16 @@ subroutine computFlux(& ! check fluxes if(globalPrintFlag)then - print*, '**' - write(*,'(a,1x,10(f30.20))') 'canopyDepth = ', canopyDepth - write(*,'(a,1x,10(f30.20))') 'mLayerDepth(1:2) = ', mLayerDepth(1:2) - write(*,'(a,1x,10(f30.20))') 'scalarCanairTempTrial = ', scalarCanairTempTrial ! trial value of the canopy air space temperature (K) - write(*,'(a,1x,10(f30.20))') 'scalarCanopyTempTrial = ', scalarCanopyTempTrial ! trial value of canopy temperature (K) - write(*,'(a,1x,10(f30.20))') 'mLayerTempTrial(1:2) = ', mLayerTempTrial(1:2) ! trial value of ground temperature (K) - write(*,'(a,1x,10(f30.20))') 'scalarCanairNetNrgFlux = ', scalarCanairNetNrgFlux - write(*,'(a,1x,10(f30.20))') 'scalarCanopyNetNrgFlux = ', scalarCanopyNetNrgFlux - write(*,'(a,1x,10(f30.20))') 'scalarGroundNetNrgFlux = ', scalarGroundNetNrgFlux - write(*,'(a,1x,10(f30.20))') 'dGroundNetFlux_dGroundTemp = ', dGroundNetFlux_dGroundTemp + print*, '**' + write(*,'(a,1x,10(f30.20))') 'canopyDepth = ', canopyDepth + write(*,'(a,1x,10(f30.20))') 'mLayerDepth(1:2) = ', mLayerDepth(1:2) + write(*,'(a,1x,10(f30.20))') 'scalarCanairTempTrial = ', scalarCanairTempTrial ! trial value of the canopy air space temperature (K) + write(*,'(a,1x,10(f30.20))') 'scalarCanopyTempTrial = ', scalarCanopyTempTrial ! trial value of canopy temperature (K) + write(*,'(a,1x,10(f30.20))') 'mLayerTempTrial(1:2) = ', mLayerTempTrial(1:2) ! trial value of ground temperature (K) + write(*,'(a,1x,10(f30.20))') 'scalarCanairNetNrgFlux = ', scalarCanairNetNrgFlux + write(*,'(a,1x,10(f30.20))') 'scalarCanopyNetNrgFlux = ', scalarCanopyNetNrgFlux + write(*,'(a,1x,10(f30.20))') 'scalarGroundNetNrgFlux = ', scalarGroundNetNrgFlux + write(*,'(a,1x,10(f30.20))') 'dGroundNetFlux_dGroundTemp = ', dGroundNetFlux_dGroundTemp endif ! if checking fluxes endif ! if calculating the energy fluxes over vegetation diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index be895baab..2b30b040f 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -127,7 +127,7 @@ subroutine coupled_em(& USE allocspace_module,only:allocLocal ! allocate local data structures USE allocspace_module,only:resizeData ! clone a data structure ! simulation of fluxes and residuals given a trial state vector - USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential + USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential ! preliminary subroutines USE vegPhenlgy_module,only:vegPhenlgy ! compute vegetation phenology USE vegNrgFlux_module,only:wettedFrac ! compute wetted fraction of the canopy (used in sw radiation fluxes) @@ -1213,7 +1213,7 @@ subroutine coupled_em(& balanceCanopyWater1 = scalarCanopyWat ! balance checks for the canopy - ! NOTE: need to put the balance checks in the sub-step loop so that we can re-compute if necessary + ! NOTE: need to put the balance checks in the sub-step loop so that we can recompute if necessary scalarCanopyWatBalError = balanceCanopyWater1 - (balanceCanopyWater0 + (scalarSnowfall - averageThroughfallSnow)*data_step + (scalarRainfall - averageThroughfallRain)*data_step & - averageCanopySnowUnloading*data_step - averageCanopyLiqDrainage*data_step + averageCanopySublimation*data_step + averageCanopyEvaporation*data_step) if(abs(scalarCanopyWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 8b22f9c50..1458f7399 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -682,8 +682,6 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(in) : [dp(:)] transpiration loss from each soil layer (m s-1) mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(in) : [dp(:)] baseflow from each soil layer (m s-1) mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in) : [dp(:)] change in storage associated with compression of the soil matrix (-) - scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the vegetation canopy (kg m-2 s-1) - scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the snow surface (kg m-2 s-1) ! energy fluxes scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ,& ! intent(in) : [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ,& ! intent(in) : [dp] sensible heat flux from the canopy to the canopy air space (W m-2) diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 9c08c2ed9..edcf86fe1 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -220,15 +220,15 @@ subroutine vegNrgFlux(& ! input: model derivatives real(rkind),intent(in) :: dCanLiq_dTcanopy ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) ! input/output: data structures - type(var_i),intent(in) :: type_data ! type of vegetation and soil - type(var_d),intent(in) :: forc_data ! model forcing data - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! state vector geometry - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin - type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(var_i),intent(in) :: type_data ! type of vegetation and soil + type(var_d),intent(in) :: forc_data ! model forcing data + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! state vector geometry + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin + type(model_options),intent(in) :: model_decisions(:) ! model decisions ! output: liquid water fluxes associated with evaporation/transpiration (needed for coupling) real(rkind),intent(out) :: returnCanopyTranspiration ! canopy transpiration (kg m-2 s-1) real(rkind),intent(out) :: returnCanopyEvaporation ! canopy evaporation/condensation (kg m-2 s-1) @@ -266,93 +266,93 @@ subroutine vegNrgFlux(& real(rkind),intent(out) :: dCanopyNetFlux_dCanWat ! derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) real(rkind),intent(out) :: dGroundNetFlux_dCanWat ! derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! --------------------------------------------------------------------------------------- ! * local variables ! --------------------------------------------------------------------------------------- ! general) - character(LEN=256) :: cmessage ! error message of downwind routine - real(rkind) :: VAI ! vegetation area index (m2 m-2) - real(rkind) :: exposedVAI ! exposed vegetation area index (m2 m-2) - real(rkind) :: totalCanopyWater ! total water on the vegetation canopy (kg m-2) - real(rkind) :: scalarAquiferStorage ! aquifer storage (m) + character(LEN=256) :: cmessage ! error message of downwind routine + real(rkind) :: VAI ! vegetation area index (m2 m-2) + real(rkind) :: exposedVAI ! exposed vegetation area index (m2 m-2) + real(rkind) :: totalCanopyWater ! total water on the vegetation canopy (kg m-2) + real(rkind) :: scalarAquiferStorage ! aquifer storage (m) ! saturation vapor pressure of veg - real(rkind) :: TV_celcius ! vegetaion temperature (C) - real(rkind) :: TG_celcius ! ground temperature (C) - real(rkind) :: dSVPCanopy_dCanopyTemp ! derivative in canopy saturated vapor pressure w.r.t. vegetation temperature (Pa/K) - real(rkind) :: dSVPGround_dGroundTemp ! derivative in ground saturated vapor pressure w.r.t. ground temperature (Pa/K) + real(rkind) :: TV_celcius ! vegetaion temperature (C) + real(rkind) :: TG_celcius ! ground temperature (C) + real(rkind) :: dSVPCanopy_dCanopyTemp ! derivative in canopy saturated vapor pressure w.r.t. vegetation temperature (Pa/K) + real(rkind) :: dSVPGround_dGroundTemp ! derivative in ground saturated vapor pressure w.r.t. ground temperature (Pa/K) ! wetted canopy area - real(rkind) :: fracLiquidCanopy ! fraction of liquid water in the canopy (-) - real(rkind) :: canopyWetFraction ! trial value of the canopy wetted fraction (-) - real(rkind) :: dCanopyWetFraction_dWat ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) - real(rkind) :: dCanopyWetFraction_dT ! derivative in wetted fraction w.r.t. canopy temperature (K-1) + real(rkind) :: fracLiquidCanopy ! fraction of liquid water in the canopy (-) + real(rkind) :: canopyWetFraction ! trial value of the canopy wetted fraction (-) + real(rkind) :: dCanopyWetFraction_dWat ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) + real(rkind) :: dCanopyWetFraction_dT ! derivative in wetted fraction w.r.t. canopy temperature (K-1) ! longwave radiation - real(rkind) :: expi ! exponential integral - real(rkind) :: scaleLAI ! scaled LAI (computing diffuse transmissivity) - real(rkind) :: diffuseTrans ! diffuse transmissivity (-) - real(rkind) :: groundEmissivity ! emissivity of the ground surface (-) - real(rkind),parameter :: vegEmissivity=0.98_rkind ! emissivity of vegetation (0.9665 in JULES) (-) - real(rkind),parameter :: soilEmissivity=0.98_rkind ! emmisivity of the soil (0.9665 in JULES) (-) - real(rkind),parameter :: snowEmissivity=0.99_rkind ! emissivity of snow (-) - real(rkind) :: dLWNetCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dLWNetGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dLWNetCanopy_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dLWNetGround_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: expi ! exponential integral + real(rkind) :: scaleLAI ! scaled LAI (computing diffuse transmissivity) + real(rkind) :: diffuseTrans ! diffuse transmissivity (-) + real(rkind) :: groundEmissivity ! emissivity of the ground surface (-) + real(rkind),parameter :: vegEmissivity=0.98_rkind ! emissivity of vegetation (0.9665 in JULES) (-) + real(rkind),parameter :: soilEmissivity=0.98_rkind ! emmisivity of the soil (0.9665 in JULES) (-) + real(rkind),parameter :: snowEmissivity=0.99_rkind ! emissivity of snow (-) + real(rkind) :: dLWNetCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dLWNetGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dLWNetCanopy_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dLWNetGround_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) ! aerodynamic resistance - real(rkind) :: scalarCanopyStabilityCorrection_old ! stability correction for the canopy (-) - real(rkind) :: scalarGroundStabilityCorrection_old ! stability correction for the ground surface (-) + real(rkind) :: scalarCanopyStabilityCorrection_old ! stability correction for the canopy (-) + real(rkind) :: scalarGroundStabilityCorrection_old ! stability correction for the ground surface (-) ! turbulent heat transfer - real(rkind) :: z0Ground ! roughness length of the ground (ground below the canopy or non-vegetated surface) (m) - real(rkind) :: soilEvapFactor ! soil water control on evaporation from non-vegetated surfaces - real(rkind) :: soilRelHumidity_noSnow ! relative humidity in the soil pores [0-1] - real(rkind) :: scalarLeafConductance ! leaf conductance (m s-1) - real(rkind) :: scalarCanopyConductance ! canopy conductance (m s-1) - real(rkind) :: scalarGroundConductanceSH ! ground conductance for sensible heat (m s-1) - real(rkind) :: scalarGroundConductanceLH ! ground conductance for latent heat -- includes soil resistance (m s-1) - real(rkind) :: scalarEvapConductance ! conductance for evaporation (m s-1) - real(rkind) :: scalarTransConductance ! conductance for transpiration (m s-1) - real(rkind) :: scalarTotalConductanceSH ! total conductance for sensible heat (m s-1) - real(rkind) :: scalarTotalConductanceLH ! total conductance for latent heat (m s-1) - real(rkind) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - real(rkind) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - real(rkind) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - real(rkind) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) - real(rkind) :: turbFluxCanair ! total turbulent heat fluxes exchanged at the canopy air space (W m-2) - real(rkind) :: turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) - real(rkind) :: turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) + real(rkind) :: z0Ground ! roughness length of the ground (ground below the canopy or non-vegetated surface) (m) + real(rkind) :: soilEvapFactor ! soil water control on evaporation from non-vegetated surfaces + real(rkind) :: soilRelHumidity_noSnow ! relative humidity in the soil pores [0-1] + real(rkind) :: scalarLeafConductance ! leaf conductance (m s-1) + real(rkind) :: scalarCanopyConductance ! canopy conductance (m s-1) + real(rkind) :: scalarGroundConductanceSH ! ground conductance for sensible heat (m s-1) + real(rkind) :: scalarGroundConductanceLH ! ground conductance for latent heat -- includes soil resistance (m s-1) + real(rkind) :: scalarEvapConductance ! conductance for evaporation (m s-1) + real(rkind) :: scalarTransConductance ! conductance for transpiration (m s-1) + real(rkind) :: scalarTotalConductanceSH ! total conductance for sensible heat (m s-1) + real(rkind) :: scalarTotalConductanceLH ! total conductance for latent heat (m s-1) + real(rkind) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + real(rkind) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind) :: dGroundResistance_dTCanair ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + real(rkind) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + real(rkind) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + real(rkind) :: turbFluxCanair ! total turbulent heat fluxes exchanged at the canopy air space (W m-2) + real(rkind) :: turbFluxCanopy ! total turbulent heat fluxes from the canopy to the canopy air space (W m-2) + real(rkind) :: turbFluxGround ! total turbulent heat fluxes from the ground to the canopy air space (W m-2) ! flux derivatives -- canopy air space - real(rkind) :: dTurbFluxCanair_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanair_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanair_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanair_dCanWat ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) + real(rkind) :: dTurbFluxCanair_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanair_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanair_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanair_dCanWat ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) ! flux derivatives -- vegetation canopy - real(rkind) :: dTurbFluxCanopy_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanopy_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanopy_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dTurbFluxCanopy_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + real(rkind) :: dTurbFluxCanopy_dTCanair ! derivative in net canopy turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanopy_dTCanopy ! derivative in net canopy turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanopy_dTGround ! derivative in net canopy turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dTurbFluxCanopy_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) ! flux derivatives -- ground surface - real(rkind) :: dTurbFluxGround_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dTurbFluxGround_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dTurbFluxGround_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dTurbFluxGround_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) + real(rkind) :: dTurbFluxGround_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dTurbFluxGround_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dTurbFluxGround_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dTurbFluxGround_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) ! liquid water flux derivatives -- canopy evap - real(rkind) :: dLatHeatCanopyEvap_dCanWat ! derivative in latent heat of canopy evaporation w.r.t. canopy total water content (W kg-1) - real(rkind) :: dLatHeatCanopyEvap_dTCanair ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dLatHeatCanopyEvap_dTCanopy ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dLatHeatCanopyEvap_dTGround ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dLatHeatCanopyEvap_dCanWat ! derivative in latent heat of canopy evaporation w.r.t. canopy total water content (W kg-1) + real(rkind) :: dLatHeatCanopyEvap_dTCanair ! derivative in latent heat of canopy evaporation w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dLatHeatCanopyEvap_dTCanopy ! derivative in latent heat of canopy evaporation w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dLatHeatCanopyEvap_dTGround ! derivative in latent heat of canopy evaporation w.r.t. ground temperature (W m-2 K-1) ! liquid water flux derivatives -- ground evap - real(rkind) :: dLatHeatGroundEvap_dCanWat ! derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) - real(rkind) :: dLatHeatGroundEvap_dTCanair ! derivative in latent heat of ground evaporation w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dLatHeatGroundEvap_dCanWat ! derivative in latent heat of ground evaporation w.r.t. canopy total water content (J kg-1 s-1) + real(rkind) :: dLatHeatGroundEvap_dTCanair ! derivative in latent heat of ground evaporation w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dLatHeatGroundEvap_dTCanopy ! derivative in latent heat of ground evaporation w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dLatHeatGroundEvap_dTGround ! derivative in latent heat of ground evaporation w.r.t. ground temperature (W m-2 K-1) ! latent heat flux derivatives -- canopy trans - real(rkind) :: dLatHeatCanopyTrans_dCanWat ! derivative in the latent heat of canopy transpiration w.r.t. canopy total water (J kg-1 s-1) - real(rkind) :: dLatHeatCanopyTrans_dTCanair ! derivative in the latent heat of canopy transpiration w.r.t. canopy air temperature - real(rkind) :: dLatHeatCanopyTrans_dTCanopy ! derivative in the latent heat of canopy transpiration w.r.t. canopy temperature - real(rkind) :: dLatHeatCanopyTrans_dTGround ! derivative in the latent heat of canopy transpiration w.r.t. ground temperature + real(rkind) :: dLatHeatCanopyTrans_dCanWat ! derivative in the latent heat of canopy transpiration w.r.t. canopy total water (J kg-1 s-1) + real(rkind) :: dLatHeatCanopyTrans_dTCanair ! derivative in the latent heat of canopy transpiration w.r.t. canopy air temperature + real(rkind) :: dLatHeatCanopyTrans_dTCanopy ! derivative in the latent heat of canopy transpiration w.r.t. canopy temperature + real(rkind) :: dLatHeatCanopyTrans_dTGround ! derivative in the latent heat of canopy transpiration w.r.t. ground temperature ! --------------------------------------------------------------------------------------- ! point to variables in the data structure @@ -812,7 +812,7 @@ subroutine vegNrgFlux(& ! compute canopy longwave radiation balance call longwaveBal(& ! input: model control - computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation + computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation checkLWBalance, & ! intent(in): flag to check longwave balance ! input: canopy and ground temperature canopyTempTrial, & ! intent(in): temperature of the vegetation canopy (K) @@ -1343,8 +1343,8 @@ subroutine longwaveBal(& ! initialize error control err=0; message='longwaveBal/' - ! NOTE: emc should be set to zero when not computing canopy fluxes ! compute longwave fluxes from canopy and the ground + ! NOTE: emc should be set to zero when not computing canopy fluxes if(computeVegFlux)then LWRadCanopy = emc*sb*canopyTemp**4_i4b ! longwave radiation emitted from the canopy (W m-2) else @@ -1353,23 +1353,22 @@ subroutine longwaveBal(& LWRadGround = emg*sb*groundTemp**4_i4b ! longwave radiation emitted at the ground surface (W m-2) ! compute fluxes originating from the atmosphere - LWRadUbound2Canopy = (emc + (1._rkind - emc)*(1._rkind - emg)*emc)*LWRadUbound ! downward atmospheric longwave radiation absorbed by the canopy (W m-2) - LWRadUbound2Ground = (1._rkind - emc)*emg*LWRadUbound ! downward atmospheric longwave radiation absorbed by the ground (W m-2) + LWRadUbound2Canopy = (emc + (1._rkind - emc)*(1._rkind - emg)*emc)*LWRadUbound ! downward atmospheric longwave radiation absorbed by the canopy (W m-2) + LWRadUbound2Ground = (1._rkind - emc)*emg*LWRadUbound ! downward atmospheric longwave radiation absorbed by the ground (W m-2) LWRadUbound2Ubound = (1._rkind - emc)*(1._rkind - emg)*(1._rkind - emc)*LWRadUbound ! atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) ! compute fluxes originating from the canopy LWRadCanopy2Ubound = (1._rkind + (1._rkind - emc)*(1._rkind - emg))*LWRadCanopy ! longwave radiation emitted from canopy lost thru upper boundary (W m-2) - LWRadCanopy2Ground = emg*LWRadCanopy ! longwave radiation emitted from canopy absorbed by the ground (W m-2) - LWRadCanopy2Canopy = emc*(1._rkind - emg)*LWRadCanopy ! canopy longwave reflected from ground and absorbed by the canopy (W m-2) - + LWRadCanopy2Ground = emg*LWRadCanopy ! longwave radiation emitted from canopy absorbed by the ground (W m-2) + LWRadCanopy2Canopy = emc*(1._rkind - emg)*LWRadCanopy ! canopy longwave reflected from ground and absorbed by the canopy (W m-2) ! compute fluxes originating from the ground surface - LWRadGround2Ubound = (1._rkind - emc)*LWRadGround ! longwave radiation emitted from ground lost thru upper boundary (W m-2) - LWRadGround2Canopy = emc*LWRadGround ! longwave radiation emitted from ground and absorbed by the canopy (W m-2) + LWRadGround2Ubound = (1._rkind - emc)*LWRadGround ! longwave radiation emitted from ground lost thru upper boundary (W m-2) + LWRadGround2Canopy = emc*LWRadGround ! longwave radiation emitted from ground and absorbed by the canopy (W m-2) ! compute net longwave radiation (W m-2) LWNetCanopy = LWRadUbound2Canopy + LWRadGround2Canopy + LWRadCanopy2Canopy - 2._rkind*LWRadCanopy ! canopy - LWNetGround = LWRadUbound2Ground + LWRadCanopy2Ground - LWRadGround ! ground surface - LWNetUbound = LWRadUbound - LWRadUbound2Ubound - LWRadCanopy2Ubound - LWRadGround2Ubound ! upper boundary + LWNetGround = LWRadUbound2Ground + LWRadCanopy2Ground - LWRadGround ! ground surface + LWNetUbound = LWRadUbound - LWRadUbound2Ubound - LWRadCanopy2Ubound - LWRadGround2Ubound ! upper boundary ! check the flux balance fluxBalance = LWNetUbound - (LWNetCanopy + LWNetGround) From ecd50bae1e68978b3468eba163cc9bf395808cf8 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 10 Aug 2023 04:36:49 -0600 Subject: [PATCH 0835/1472] Pull in Ashley's Jacobian solver update --- build/cmake/CMakeLists.txt | 24 +++++++++++++++--------- build/cmake/build.pc.bash | 10 +++++----- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 565902153..5c53c571b 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -104,14 +104,14 @@ endif() if(CMAKE_BUILD_TYPE MATCHES Debug) message("\nSetting Debug Options") add_compile_definitions(DEBUG) - set(FLAGS_NOAH -g -O0 -fbounds-check -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) - set(FLAGS_ALL -g -O0 -fbounds-check -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp) - set(FLAGS_CXX -g -O0 -fbounds-check -Wfatal-errors -std=c++17) + set(FLAGS_NOAH -g -O0 -fbacktrace -fbounds-check -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) + set(FLAGS_ALL -g -O0 -fbacktrace -fbounds-check -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp) + set(FLAGS_CXX -g -O0 -fbacktrace -fbounds-check -Wfatal-errors -std=c++17) else() message("\nSetting Release Options") - set(FLAGS_NOAH -O3 -flto -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) - set(FLAGS_ALL -O3 -flto -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp) - set(FLAGS_CXX -O3 -flto -Wfatal-errors -std=c++17) + set(FLAGS_NOAH -O3 -flto=1 -fuse-linker-plugin -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) + set(FLAGS_ALL -O3 -flto=1 -fuse-linker-plugin -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp) + set(FLAGS_CXX -O3 -flto=1 -fuse-linker-plugin -Wfatal-errors -std=c++17) endif() # Find Sundials if exists, add path here if FATAL_ERROR "Did not find Sundials directory" @@ -542,7 +542,9 @@ target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) add_library(SUMMA_COMM OBJECT ${COMM_ALL}) target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES}) -target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP) +#target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP) #OG +target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP ${FLAGS_ALL}) #SJT +#target_link_options(SUMMA_COMM PUBLIC ${FLAGS_ALL}) #SJT # For NexGen, build SUMMA Shared Library and add the outside BMI libraries if(CMAKE_BUILD_TYPE MATCHES NexGen) @@ -607,10 +609,14 @@ else() target_link_libraries(summa PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_NOAHMP SUMMA_COMM) else() target_include_directories(summa PUBLIC ${INCLUDES}) - target_link_libraries(summa PUBLIC ${LIBRARIES} SUMMA_NOAHMP SUMMA_COMM) + #target_link_libraries(summa PUBLIC ${LIBRARIES} SUMMA_NOAHMP SUMMA_COMM) #OG + target_link_libraries(summa PUBLIC ${LIBRARIES} SUMMA_NOAHMP SUMMA_COMM ${FLAGS_ALL}) #SJT + #target_link_options(summa PUBLIC ${FLAGS_ALL}) #SJT endif() add_executable(${EXEC_NAME} ${MAIN_SUMMA}) set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) - target_link_libraries(${EXEC_NAME} summa) + #target_link_libraries(${EXEC_NAME} summa) #OG + target_link_libraries(${EXEC_NAME} summa ${FLAGS_ALL}) #SJT + #target_link_options(${EXEC_NAME} PUBLIC ${FLAGS_ALL}) #SJT endif() diff --git a/build/cmake/build.pc.bash b/build/cmake/build.pc.bash index 8ad3cff03..baad8a265 100755 --- a/build/cmake/build.pc.bash +++ b/build/cmake/build.pc.bash @@ -4,11 +4,11 @@ # Environment variables that must be set for cmake: FC, LINK_DIRS, INCLUDES_DIRS, and LIBRARY_LINKS # PC Example using Ubuntu -export FC=gfortran # Fortran compiler family -export LINK_DIRS=/usr/local/lib # Link directories for cmake -export INCLUDES_DIRS='/usr/include;/usr/local/include' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) -export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) +#export FC=gfortran # Fortran compiler family +#export LINK_DIRS=/usr/local/lib # Link directories for cmake +#export INCLUDES_DIRS='/usr/include;/usr/local/include' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) +#export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) -cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=BE_Debug +cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=BE cmake --build ../cmake_build --target all From 1411759c41bbdbb19d45bd1ef25f8a6149909549 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 10 Aug 2023 05:39:29 -0600 Subject: [PATCH 0836/1472] Updates to link stage in CMakeLists.txt and for Intel oneMKL builds on PC. --- build/cmake/CMakeLists.txt | 25 ++++++++++--------------- build/cmake/build.pc.bash | 21 +++++++++++++++++---- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 5c53c571b..c5435b15a 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -101,17 +101,18 @@ endif() #========================================================================================= # Set compiler flags +set(FLAGS_OPT $ENV{FLAGS_OPT}) # get optional user-specified flags from environment variables if(CMAKE_BUILD_TYPE MATCHES Debug) message("\nSetting Debug Options") add_compile_definitions(DEBUG) - set(FLAGS_NOAH -g -O0 -fbacktrace -fbounds-check -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) - set(FLAGS_ALL -g -O0 -fbacktrace -fbounds-check -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp) - set(FLAGS_CXX -g -O0 -fbacktrace -fbounds-check -Wfatal-errors -std=c++17) + set(FLAGS_NOAH -g -O0 -fbacktrace -fbounds-check -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) + set(FLAGS_ALL -g -O0 -fbacktrace -fbounds-check -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp ${FLAGS_OPT}) + set(FLAGS_CXX -g -O0 -fbacktrace -fbounds-check -Wfatal-errors -std=c++17 ${FLAGS_OPT}) else() message("\nSetting Release Options") - set(FLAGS_NOAH -O3 -flto=1 -fuse-linker-plugin -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) - set(FLAGS_ALL -O3 -flto=1 -fuse-linker-plugin -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp) - set(FLAGS_CXX -O3 -flto=1 -fuse-linker-plugin -Wfatal-errors -std=c++17) + set(FLAGS_NOAH -O3 -flto=1 -fuse-linker-plugin -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) + set(FLAGS_ALL -O3 -flto=1 -fuse-linker-plugin -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp ${FLAGS_OPT}) + set(FLAGS_CXX -O3 -flto=1 -fuse-linker-plugin -Wfatal-errors -std=c++17 ${FLAGS_OPT}) endif() # Find Sundials if exists, add path here if FATAL_ERROR "Did not find Sundials directory" @@ -542,9 +543,7 @@ target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) add_library(SUMMA_COMM OBJECT ${COMM_ALL}) target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES}) -#target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP) #OG -target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP ${FLAGS_ALL}) #SJT -#target_link_options(SUMMA_COMM PUBLIC ${FLAGS_ALL}) #SJT +target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP ${FLAGS_ALL}) # added flags to the link step # For NexGen, build SUMMA Shared Library and add the outside BMI libraries if(CMAKE_BUILD_TYPE MATCHES NexGen) @@ -609,14 +608,10 @@ else() target_link_libraries(summa PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_NOAHMP SUMMA_COMM) else() target_include_directories(summa PUBLIC ${INCLUDES}) - #target_link_libraries(summa PUBLIC ${LIBRARIES} SUMMA_NOAHMP SUMMA_COMM) #OG - target_link_libraries(summa PUBLIC ${LIBRARIES} SUMMA_NOAHMP SUMMA_COMM ${FLAGS_ALL}) #SJT - #target_link_options(summa PUBLIC ${FLAGS_ALL}) #SJT + target_link_libraries(summa PUBLIC ${LIBRARIES} SUMMA_NOAHMP SUMMA_COMM ${FLAGS_ALL}) # added flags to the link step endif() add_executable(${EXEC_NAME} ${MAIN_SUMMA}) set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) - #target_link_libraries(${EXEC_NAME} summa) #OG - target_link_libraries(${EXEC_NAME} summa ${FLAGS_ALL}) #SJT - #target_link_options(${EXEC_NAME} PUBLIC ${FLAGS_ALL}) #SJT + target_link_libraries(${EXEC_NAME} summa ${FLAGS_ALL}) # added flags to the link step endif() diff --git a/build/cmake/build.pc.bash b/build/cmake/build.pc.bash index baad8a265..c4c98f842 100755 --- a/build/cmake/build.pc.bash +++ b/build/cmake/build.pc.bash @@ -1,14 +1,27 @@ #!/bin/bash -# build SUMMA on a PC using Bash, from cmake directory run this as ./build.pc.bash +# Build SUMMA on a PC using Bash, from cmake directory run this as ./build.pc.bash # Environment variables that must be set for cmake: FC, LINK_DIRS, INCLUDES_DIRS, and LIBRARY_LINKS +# Environment variables may be set within this script (see examples below) or in the terminal environment before executing this script +# Actual settings may vary -# PC Example using Ubuntu +# PC Example using Ubuntu: LAPACK Builds #export FC=gfortran # Fortran compiler family #export LINK_DIRS=/usr/local/lib # Link directories for cmake -#export INCLUDES_DIRS='/usr/include;/usr/local/include' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) -#export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) +#export INCLUDES_DIRS="/usr/include;/usr/local/include" # directories for INCLUDES cmake variable (cmake uses semicolons as separators) +#export LIBRARY_LINKS="-llapack;-lnetcdff;-lnetcdf" # list of library links -- LAPACK builds +#export FLAGS_OPT="" # optional compiler flags -- LAPACK builds +# PC Example using Ubuntu: Intel oneMKL builds (see https://www.intel.com/content/www/us/en/developer/tools/oneapi/onemkl-link-line-advisor.html) +#oneAPI_dir=/opt/intel/oneapi # Intel oneAPI main directory +#source $oneAPI_dir/setvars.sh # initialize environment variables for Intel oneAPI +#export FC=gfortran # Fortran compiler family +#export LINK_DIRS=/usr/local/lib # Link directories for cmake +#export INCLUDES_DIRS="/usr/include;/usr/local/include" # directories for INCLUDES cmake variable (cmake uses semicolons as separators) +#export LIBRARY_LINKS="-lnetcdff;-lnetcdf;-Wl,--start-group ${MKLROOT}/lib/intel64/libmkl_gf_lp64.a ${MKLROOT}/lib/intel64/libmkl_gnu_thread.a ${MKLROOT}/lib/intel64/libmkl_core.a -Wl,--end-group;-lgomp;-lpthread;-lm;-ldl" # list of library links -- Intel oneMKL builds +#export FLAGS_OPT="-m64;-I"${MKLROOT}/include"" # optional compiler flags -- Intel oneMKL builds + +# CMake Commands (build type controlled using DCMAKE_BUILD_TYPE) cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=BE cmake --build ../cmake_build --target all From 787e7d95a4e3250530ff170b696a30fb7ce9c261 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 10 Aug 2023 23:28:03 +0900 Subject: [PATCH 0837/1472] Don't want to restart scalar aquifer at a different value (basin okay to restart at 1), this was supposed to push before and did not --- build/source/driver/summa_restart.f90 | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/build/source/driver/summa_restart.f90 b/build/source/driver/summa_restart.f90 index ea1d657ad..2b9e09a75 100644 --- a/build/source/driver/summa_restart.f90 +++ b/build/source/driver/summa_restart.f90 @@ -192,9 +192,6 @@ subroutine summa_readRestart(summa1_struc, err, message) ! There are two options for groundwater: ! (1) where groundwater is included in the local column (i.e., the HRUs); and ! (2) where groundwater is included for the single basin (i.e., the GRUS, where multiple HRUS drain into a GRU). - ! Start with this full, since easier to spin up by draining than filling (filling we need to wait for precip). - ! There will be an initial pulse of water, but the spinup runs should be discarded. - ! For water balance calculations it is important to ensure that the local aquifer storage is zero if groundwater is treated as a basin-average state variable (singleBasin); ! and ensure that basin-average aquifer storage is zero when groundwater is included in the local columns (localColumn). @@ -204,14 +201,11 @@ subroutine summa_readRestart(summa1_struc, err, message) ! the basin-average aquifer storage is not used if the groundwater is included in the local column case(localColumn) bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 0._rkind ! set to zero to be clear that there is no basin-average aquifer storage in this configuration - do iHRU=1,gru_struc(iGRU)%hruCount - progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarAquiferStorage)%dat(1) = 1._rkind - end do ! the local column aquifer storage is not used if the groundwater is basin-average ! (i.e., where multiple HRUs drain to a basin-average aquifer) case(singleBasin) - bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 1._rkind + bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 1._rkind ! Start with this full, since easier to spin up by draining than filling (filling we need to wait for precip). do iHRU=1,gru_struc(iGRU)%hruCount progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarAquiferStorage)%dat(1) = 0._rkind ! set to zero to be clear that there is no local aquifer storage in this configuration end do From 1f409f81d177e5ebe0b18ebdcf84a51f31135bfc Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Thu, 10 Aug 2023 08:52:06 -0600 Subject: [PATCH 0838/1472] Prep for Merge: Changed the CMakeLists.txt file back to how it was from source repo. Removed use of summa_type.f90 from Summa_Actors in CMakeLists.txt Adjusted mDecisions to not have directives for actors. The actors call an interface before calling this file now. Reset pinit to how it came from source repo --- build/cmake/CMakeLists.txt | 15 ++++++--------- build/cmake/build.mac.bash | 8 ++++---- build/cmake/build_actors.mac.bash | 10 +++------- build/source/engine/mDecisions.f90 | 18 +++++------------- build/source/engine/read_pinit.f90 | 4 ++-- 5 files changed, 20 insertions(+), 35 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 4ff65258c..2988257dc 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -104,14 +104,14 @@ endif() if(CMAKE_BUILD_TYPE MATCHES Debug) message("\nSetting Debug Options") add_compile_definitions(DEBUG) - set(FLAGS_NOAH -g -O0 -fbounds-check -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) - set(FLAGS_ALL -g -O0 -fbounds-check -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp) - set(FLAGS_CXX -g -O0 -fbounds-check -Wfatal-errors -std=c++17) + set(FLAGS_NOAH -g -O0 -fbacktrace -fbounds-check -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) + set(FLAGS_ALL -g -O0 -fbacktrace -fbounds-check -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp ${FLAGS_OPT}) + set(FLAGS_CXX -g -O0 -fbacktrace -fbounds-check -Wfatal-errors -std=c++17 ${FLAGS_OPT}) else() message("\nSetting Release Options") - set(FLAGS_NOAH -O3 -flto -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) - set(FLAGS_ALL -O3 -flto -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp) - set(FLAGS_CXX -O3 -flto -Wfatal-errors -std=c++17) + set(FLAGS_NOAH -O3 -flto=1 -fuse-linker-plugin -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) + set(FLAGS_ALL -O3 -flto=1 -fuse-linker-plugin -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp ${FLAGS_OPT}) + set(FLAGS_CXX -O3 -flto=1 -fuse-linker-plugin -Wfatal-errors -std=c++17 ${FLAGS_OPT}) endif() # Find Sundials if exists, add path here if FATAL_ERROR "Did not find Sundials directory" @@ -122,8 +122,6 @@ if(CMAKE_BUILD_TYPE MATCHES Sundials) "${PARENT_DIR}../sundials/instdir" "${F_MASTER}../../SummaSundials/sundials/instdir" "${PARENT_DIR}../../SummaSundials/sundials/instdir" - "/usr/local/sundials-6.3.0" - "/home/kklenk/projects/rpp-kshook/CompHydCore/SummaSundials/sundials/sundials/instdir" "../../../../SummaSundials/sundials/instdir") #look in same head directory as summa or up to another installation if(EXISTS ${dir}) set(DIR_SUNDIALS ${dir}) @@ -195,7 +193,6 @@ else() "${PARENT_DIR}../actor-framework/install" "${F_MASTER}../../SummaActors/actor-framework/install" "${PARENT_DIR}../../SummaActors/actor-framework/install" - "/usr/local" "../../../../SummaActors/actor-framework/install") #look in same head directory as summa or up to another installation if(EXISTS ${dir}) set(DIR_ACTORS ${dir}) diff --git a/build/cmake/build.mac.bash b/build/cmake/build.mac.bash index 0e85a6a75..04e23f3a3 100755 --- a/build/cmake/build.mac.bash +++ b/build/cmake/build.mac.bash @@ -5,9 +5,9 @@ # Mac Example using MacPorts: export FC=gfortran # Fortran compiler family -export LINK_DIRS='/usr/local/lib;/usr/lib/' # Link directories for cmake -export INCLUDES_DIRS='/usr/local/include;/usr/local/lib;/usr/include' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) +export LINK_DIRS=/opt/local/lib # Link directories for cmake +export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) -cmake -B ../cmake_build_reg -S . -DCMAKE_BUILD_TYPE=Sundials -cmake --build ../cmake_build_reg --target all \ No newline at end of file +cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials +cmake --build ../cmake_build --target all \ No newline at end of file diff --git a/build/cmake/build_actors.mac.bash b/build/cmake/build_actors.mac.bash index e91883224..bc43513be 100755 --- a/build/cmake/build_actors.mac.bash +++ b/build/cmake/build_actors.mac.bash @@ -1,11 +1,7 @@ #!/bin/bash # build on Mac, from cmake directory run this as ./build_actors.mac.bash -export FC=gfortran # Fortran compiler family -export LINK_DIRS='/usr/lib;/usr/lib/x86_64-linux-gnu' # Link directories for cmake -export INCLUDES_DIRS='/usr/include;/usr/local/include' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) -export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) -export DIR_SUNDIALS='/usr/local/sundials-6.3.0' -cmake -B ../cmake_build_regular -S . -DCMAKE_BUILD_TYPE=Sundials_Actors -cmake --build ../cmake_build_regular --target all + +cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials_Actors +cmake --build ../cmake_build --target all diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index 640d158db..4dc787a35 100755 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -19,9 +19,6 @@ ! along with this program. If not, see . module mDecisions_module -#ifdef ACTORS_ACTIVE -USE, intrinsic :: iso_c_binding -#endif USE nrtype USE var_lookup, only: maxvarDecisions ! maximum number of decisions implicit none @@ -126,7 +123,7 @@ module mDecisions_module integer(i4b),parameter,public :: andersonEmpirical = 252 ! semi-empirical method of Anderson (1976) ! look-up values for the choice of method to combine and sub-divide snow layers integer(i4b),parameter,public :: sameRulesAllLayers = 261 ! same combination/sub-division rules applied to all layers -integer(i4b),parameter,public :: rulesDependLayerIndex= 262 ! combination/sub-dividion rules depend on layer index +integer(i4b),parameter,public :: rulesDependLayerIndex= 262 ! combination/sub-division rules depend on layer index ! look-up values for the choice of thermal conductivity representation for snow integer(i4b),parameter,public :: Yen1965 = 271 ! Yen (1965) integer(i4b),parameter,public :: Mellor1977 = 272 ! Mellor (1977) @@ -188,8 +185,7 @@ subroutine mDecisions(err,message) USE summaFileManager,only: SIM_START_TM, SIM_END_TM ! time info from control file module implicit none - ! define output, depends on if using Actors - integer(i4b) :: num_steps ! number of time steps in the simulation + ! define output integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -293,11 +289,8 @@ subroutine mDecisions(err,message) oldTime%var(:) = startTime%var(:) ! compute the number of time steps - num_steps = nint( (dJulianFinsh - dJulianStart)*secprday/data_step ) + 1 - numtim = num_steps -#ifndef ACTORS_ACTIVE - write(*,'(a,1x,i10)') 'number of time steps = ', numtim -#endif + numtim = nint( (dJulianFinsh - dJulianStart)*secprday/data_step ) + 1 + ! set Noah-MP options DVEG=3 ! option for dynamic vegetation @@ -743,5 +736,4 @@ subroutine readoption(err,message) model_decisions(iVar)%cDecision = trim(decision) end do end subroutine readoption -end module mDecisions_module - \ No newline at end of file +end module mDecisions_module \ No newline at end of file diff --git a/build/source/engine/read_pinit.f90 b/build/source/engine/read_pinit.f90 index e419807b2..34be9581c 100644 --- a/build/source/engine/read_pinit.f90 +++ b/build/source/engine/read_pinit.f90 @@ -136,10 +136,10 @@ subroutine read_pinit(filenm,isLocal,mpar_meta,parFallback,err,message) parFallback(iLookPARAM%be_steps)%default_val = 1._rkind end if if (parFallback(iLookPARAM%relErrTol_ida)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%relErrTol_ida)%default_val = 1.e-4_rkind + parFallback(iLookPARAM%relErrTol_ida)%default_val = 1.e-6_rkind end if if (parFallback(iLookPARAM%absErrTol_ida)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%absErrTol_ida)%default_val = 1.e-4_rkind + parFallback(iLookPARAM%absErrTol_ida)%default_val = 1.e-6_rkind end if end if From 2f315287d5d224159ffd1f0c02f8e0aa5d7d55ae Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 11 Aug 2023 00:49:13 +0900 Subject: [PATCH 0839/1472] comments typos --- build/source/engine/summaSolve4numrec.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 72e027f23..b8743f287 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -648,7 +648,7 @@ subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fl ! get brackets if they do not exist if( ieee_is_nan(xMin) .or. ieee_is_nan(xMax) )then - call getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,message) + call getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) endif @@ -666,7 +666,7 @@ subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fl ! compute the iteration increment stateVecNew = stateVecTrial + xInc - endif ! if the iteration increment is the same sign as the residual vecto + endif ! if the iteration increment is the same sign as the residual vector ! bi-section bracketsDefined = ( .not.ieee_is_nan(xMin) .and. .not.ieee_is_nan(xMax) ) ! check that the brackets are defined From 0c5af7db3dff60bb7b902d40d0988c93014fe1e9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 11 Aug 2023 12:59:43 +0900 Subject: [PATCH 0840/1472] move nrgFluxModified flag initialization into updateProgress routine (was in main varSubstep routine so not as correct) --- build/source/engine/varSubstep.f90 | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 1458f7399..e80d06a82 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -264,10 +264,7 @@ subroutine varSubstep(& ! change maxstep with hard code here to make only the newton step loop in systemSolv* happen more frequently ! NOTE: this may just be amplifying the splitting error if maxstep is smaller than the full possible step - maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) - - ! initalize flag for checking if energy fluxes had been modified - nrgFluxModified = .false. + maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s). ! allocate space for the temporary model flux structure call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) @@ -720,8 +717,9 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! initialize error control err=0; message='updateProg/' - ! initialize water balancmLayerVolFracWatTrial error + ! initialize flags for water balance error and energy flux modification waterBalanceError=.false. + nrgFluxModified = .false. ! get storage at the start of the step canopyBalance0 = merge(scalarCanopyLiq + scalarCanopyIce, realMissing, computeVegFlux) @@ -920,7 +918,6 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! ----------------------- ! NOTE: should not need to do this, since mass balance is checked in the solver, and cannot do for IDA - ! if do not check could cause problems if should modify nrgFlux if(checkMassBalance)then ! check mass balance for the canopy From f3a5b42885baf459c1e8231c6f48039722a33bb0 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 11 Aug 2023 13:34:09 +0900 Subject: [PATCH 0841/1472] spaces --- build/source/engine/coupled_em.f90 | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 2b30b040f..6e4fdd1bb 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -532,7 +532,6 @@ subroutine coupled_em(& err,cmessage) ! intent(out): error control if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - ! *** compute canopy sw radiation fluxes... ! ----------------------------------------- call vegSWavRad(& @@ -548,7 +547,6 @@ subroutine coupled_em(& err,cmessage) ! intent(out): error control if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - ! *** compute canopy throughfall and unloading... ! ----------------------------------------------- ! NOTE 1: this needs to be done before solving the energy and liquid water equations, to account for the heat advected with precipitation (and throughfall/unloading) @@ -814,10 +812,10 @@ subroutine coupled_em(& ! NOTE: include ice content as part of the solid porosity - major effect of ice is to reduce the pore size; ensure that effSat=1 at saturation ! (from Zhao et al., J. Hydrol., 1997: Numerical analysis of simultaneous heat and mass transfer...) do iSoil=1,nSoil - call liquidHead(mLayerMatricHead(iSoil),mLayerVolFracLiq(nSnow+iSoil),mLayerVolFracIce(nSnow+iSoil), & ! input: state variables - vGn_alpha(iSoil),vGn_n(iSoil),theta_sat(iSoil),theta_res(iSoil),vGn_m(iSoil), & ! input: parameters - matricHeadLiq=mLayerMatricHeadLiq(iSoil), & ! output: liquid water matric potential (m) - err=err,message=cmessage) ! output: error control + call liquidHead(mLayerMatricHead(iSoil),mLayerVolFracLiq(nSnow+iSoil),mLayerVolFracIce(nSnow+iSoil), & ! input: state variables + vGn_alpha(iSoil),vGn_n(iSoil),theta_sat(iSoil),theta_res(iSoil),vGn_m(iSoil), & ! input: parameters + matricHeadLiq=mLayerMatricHeadLiq(iSoil), & ! output: liquid water matric potential (m) + err=err,message=cmessage) ! output: error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif end do ! looping through soil layers (computing liquid water matric potential) From b92e9a6181352e898b45ec88374498793d52d409 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 11 Aug 2023 17:07:23 +0900 Subject: [PATCH 0842/1472] only need to recalculate top layer of snow after sublimation, and fixing error printouts for clarity --- build/source/engine/coupled_em.f90 | 57 ++++++++++++++++-------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 6e4fdd1bb..05d4f4eb4 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -972,6 +972,9 @@ subroutine coupled_em(& end if ! (if computing the vegetation flux) + ! * compute change in ice content of the top snow layer due to sublimation + ! and account for compaction and cavitation in the snowpack... + ! ------------------------------------------------------------------------ call computSnowDepth(& whole_step, & ! intent(in) nSnow, & ! intent(in) @@ -1021,12 +1024,12 @@ subroutine coupled_em(& err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - ! recompute snow depth, SWE, and layer water + ! recompute snow depth, SWE, and top layer water if(nSnow > 0)then prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( mLayerDepth(1:nSnow) ) prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (mLayerVolFracLiq(1:nSnow)*iden_water & + mLayerVolFracIce(1:nSnow)*iden_ice) * mLayerDepth(1:nSnow) ) - mLayerVolFracWat(1:nSnow) = mLayerVolFracLiq(1:nSnow) + mLayerVolFracIce(1:nSnow)*iden_ice/iden_water + mLayerVolFracWat(1) = mLayerVolFracLiq(1) + mLayerVolFracIce(1)*iden_ice/iden_water endif end associate sublime @@ -1215,18 +1218,18 @@ subroutine coupled_em(& scalarCanopyWatBalError = balanceCanopyWater1 - (balanceCanopyWater0 + (scalarSnowfall - averageThroughfallSnow)*data_step + (scalarRainfall - averageThroughfallRain)*data_step & - averageCanopySnowUnloading*data_step - averageCanopyLiqDrainage*data_step + averageCanopySublimation*data_step + averageCanopyEvaporation*data_step) if(abs(scalarCanopyWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then - write(*,'(a,1x,f20.10)') 'data_step = ', data_step - write(*,'(a,1x,f20.10)') 'balanceCanopyWater0 = ', balanceCanopyWater0 - write(*,'(a,1x,f20.10)') 'balanceCanopyWater1 = ', balanceCanopyWater1 - write(*,'(a,1x,f20.10)') 'scalarSnowfall = ', scalarSnowfall - write(*,'(a,1x,f20.10)') 'scalarRainfall = ', scalarRainfall - write(*,'(a,1x,f20.10)') '(scalarSnowfall - averageThroughfallSnow) = ', (scalarSnowfall - averageThroughfallSnow)!*data_step - write(*,'(a,1x,f20.10)') '(scalarRainfall - averageThroughfallRain) = ', (scalarRainfall - averageThroughfallRain)!*data_step - write(*,'(a,1x,f20.10)') 'averageCanopySnowUnloading = ', averageCanopySnowUnloading!*data_step - write(*,'(a,1x,f20.10)') 'averageCanopyLiqDrainage = ', averageCanopyLiqDrainage!*data_step - write(*,'(a,1x,f20.10)') 'averageCanopySublimation = ', averageCanopySublimation!*data_step - write(*,'(a,1x,f20.10)') 'averageCanopyEvaporation = ', averageCanopyEvaporation!*data_step - write(*,'(a,1x,f20.10)') 'scalarCanopyWatBalError = ', scalarCanopyWatBalError + write(*,'(a,1x,f20.10)') 'data_step = ', data_step + write(*,'(a,1x,f20.10)') 'balanceCanopyWater0 = ', balanceCanopyWater0 + write(*,'(a,1x,f20.10)') 'balanceCanopyWater1 = ', balanceCanopyWater1 + write(*,'(a,1x,f20.10)') 'snowfall = ', scalarSnowfall*data_step + write(*,'(a,1x,f20.10)') 'rainfall = ', scalarRainfall*data_step + write(*,'(a,1x,f20.10)') '(snowfall - throughfallSnow) = ', (scalarSnowfall - averageThroughfallSnow)*data_step + write(*,'(a,1x,f20.10)') '(rainfall - throughfallRain) = ', (scalarRainfall - averageThroughfallRain)*data_step + write(*,'(a,1x,f20.10)') 'canopySnowUnloading = ', averageCanopySnowUnloading*data_step + write(*,'(a,1x,f20.10)') 'canopyLiqDrainage = ', averageCanopyLiqDrainage*data_step + write(*,'(a,1x,f20.10)') 'canopySublimation = ', averageCanopySublimation*data_step + write(*,'(a,1x,f20.10)') 'canopyEvaporation = ', averageCanopyEvaporation*data_step + write(*,'(a,1x,f20.10)') 'canopyWatBalError = ', scalarCanopyWatBalError message=trim(message)//'canopy hydrology does not balance' err=20; return end if @@ -1270,7 +1273,7 @@ subroutine coupled_em(& write(*,'(a,1x,f20.10)') 'sublimation = ', averageSnowSublimation*data_step write(*,'(a,1x,f20.10)') 'snwDrainage = ', averageSnowDrainage*iden_water*data_step write(*,'(a,1x,f20.10)') 'sfcMeltPond = ', sfcMeltPond - write(*,'(a,1x,f20.10)') 'massBalance = ', massBalance + write(*,'(a,1x,f20.10)') 'SWE_BalErr = ', massBalance message=trim(message)//'SWE does not balance' err=20; return endif ! if failed mass balance check @@ -1314,18 +1317,18 @@ subroutine coupled_em(& ! check the soil water balance scalarSoilWatBalError = balanceSoilWater1 - (balanceSoilWater0 + (balanceSoilInflux + balanceSoilET - balanceSoilBaseflow - balanceSoilDrainage - balanceSoilCompress) ) if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues - write(*,*) 'solution method = ', ixSolution - write(*,'(a,1x,f20.10)') 'data_step = ', data_step - write(*,'(a,1x,f20.10)') 'balanceSoilCompress = ', balanceSoilCompress - write(*,'(a,1x,f20.10)') 'scalarTotalSoilLiq = ', scalarTotalSoilLiq - write(*,'(a,1x,f20.10)') 'scalarTotalSoilIce = ', scalarTotalSoilIce - write(*,'(a,1x,f20.10)') 'balanceSoilWater0 = ', balanceSoilWater0 - write(*,'(a,1x,f20.10)') 'balanceSoilWater1 = ', balanceSoilWater1 - write(*,'(a,1x,f20.10)') 'balanceSoilInflux = ', balanceSoilInflux - write(*,'(a,1x,f20.10)') 'balanceSoilBaseflow = ', balanceSoilBaseflow - write(*,'(a,1x,f20.10)') 'balanceSoilDrainage = ', balanceSoilDrainage - write(*,'(a,1x,f20.10)') 'balanceSoilET = ', balanceSoilET - write(*,'(a,1x,f20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError + write(*,*) 'solution method = ', ixSolution + write(*,'(a,1x,f20.10)') 'data_step = ', data_step + write(*,'(a,1x,f20.10)') 'balanceSoilCompress = ', balanceSoilCompress + write(*,'(a,1x,f20.10)') 'scalarTotalSoilLiq = ', scalarTotalSoilLiq + write(*,'(a,1x,f20.10)') 'scalarTotalSoilIce = ', scalarTotalSoilIce + write(*,'(a,1x,f20.10)') 'balanceSoilWater0 = ', balanceSoilWater0 + write(*,'(a,1x,f20.10)') 'balanceSoilWater1 = ', balanceSoilWater1 + write(*,'(a,1x,f20.10)') 'balanceSoilInflux = ', balanceSoilInflux + write(*,'(a,1x,f20.10)') 'balanceSoilBaseflow = ', balanceSoilBaseflow + write(*,'(a,1x,f20.10)') 'balanceSoilDrainage = ', balanceSoilDrainage + write(*,'(a,1x,f20.10)') 'balanceSoilET = ', balanceSoilET + write(*,'(a,1x,f20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError message=trim(message)//'soil hydrology does not balance' err=20; return end if From f4d8dcabe6445d9d0938fd7fafe46415cdd4c8f7 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 11 Aug 2023 17:22:00 +0900 Subject: [PATCH 0843/1472] comment on dense Jacobian, more correct but slow ... might need to change --- build/source/engine/systemSolv.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index a71a0d40d..c89e3d98a 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -345,7 +345,7 @@ subroutine systemSolv(& end if if(err/=0)then; err=20; message=trim(message)//'unable to allocate space for the baseflow derivatives'; return; end if - ! identify the matrix solution method + ! identify the matrix solution method, using the full matrix can be slow in many-layered systems ! (the type of matrix used to solve the linear system A.X=B) if(local_ixGroundwater==qbaseTopmodel .or. scalarSolution .or. forceFullMatrix .or. computeVegFlux)then nLeadDim=nState ! length of the leading dimension From 752230e9d5cbf4aeab3400396cb271a12809d7d5 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 10 May 2023 03:19:19 -0600 Subject: [PATCH 0844/1472] General clean up of computFlux.f90 --- build/source/engine/computFlux.f90 | 289 ++++++++++++++--------------- 1 file changed, 140 insertions(+), 149 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 8c18b1822..5dc0e6bc6 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -100,7 +100,6 @@ module computFlux_module public::soilCmpresPrime contains - ! ********************************************************************************************************* ! public subroutine computFlux: compute model fluxes ! ********************************************************************************************************* @@ -116,7 +115,7 @@ subroutine computFlux(& scalarSolution, & ! intent(in): flag to indicate the scalar solution checkLWBalance, & ! intent(in): flag to check longwave balance drainageMeltPond, & ! intent(in): drainage from the surface melt pond (kg m-2 s-1) - ! input: state variables + ! input: state variables scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) @@ -146,15 +145,15 @@ subroutine computFlux(& dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) fluxVec, & ! intent(out): flux vector (mixed units) ! output: error control - err,message) ! intent(out): error code and error message + err,message) ! intent(out): error code and error message ! provide access to flux subroutines - USE vegNrgFlux_module,only:vegNrgFlux ! compute energy fluxes over vegetation - USE ssdNrgFlux_module,only:ssdNrgFlux ! compute energy fluxes throughout the snow and soil subdomains - USE vegLiqFlux_module,only:vegLiqFlux ! compute liquid water fluxes through vegetation - USE snowLiqFlx_module,only:snowLiqflx ! compute liquid water fluxes through snow - USE soilLiqFlx_module,only:soilLiqflx ! compute liquid water fluxes through soil - USE groundwatr_module,only:groundwatr ! compute the baseflow flux - USE bigAquifer_module,only:bigAquifer ! compute fluxes for the big aquifer + USE vegNrgFlux_module,only:vegNrgFlux ! compute energy fluxes over vegetation + USE ssdNrgFlux_module,only:ssdNrgFlux ! compute energy fluxes throughout the snow and soil subdomains + USE vegLiqFlux_module,only:vegLiqFlux ! compute liquid water fluxes through vegetation + USE snowLiqFlx_module,only:snowLiqflx ! compute liquid water fluxes through snow + USE soilLiqFlx_module,only:soilLiqflx ! compute liquid water fluxes through soil + USE groundwatr_module,only:groundwatr ! compute the baseflow flux + USE bigAquifer_module,only:bigAquifer ! compute fluxes for the big aquifer implicit none ! --------------------------------------------------------------------------------------- ! * dummy variables @@ -205,23 +204,22 @@ subroutine computFlux(& ! --------------------------------------------------------------------------------------- ! * local variables ! --------------------------------------------------------------------------------------- - integer(i4b) :: local_ixGroundwater ! local index for groundwater representation - integer(i4b) :: iLayer ! index of model layers - logical(lgt) :: doVegNrgFlux ! flag to compute the energy flux over vegetation - real(rkind),dimension(nSoil) :: dHydCond_dMatric ! derivative in hydraulic conductivity w.r.t matric head (s-1) - character(LEN=256) :: cmessage ! error message of downwind routine - real(rkind) :: above_soilLiqFluxDeriv ! derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water - real(rkind) :: above_soildLiq_dTk ! derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature - real(rkind) :: above_soilFracLiq ! fraction of liquid water layer above soil (canopy or snow) (-) + integer(i4b) :: local_ixGroundwater ! local index for groundwater representation + integer(i4b) :: iLayer ! index of model layers + logical(lgt) :: doVegNrgFlux ! flag to compute the energy flux over vegetation + real(rkind),dimension(nSoil) :: dHydCond_dMatric ! derivative in hydraulic conductivity w.r.t matric head (s-1) + character(LEN=256) :: cmessage ! error message of downwind routine + real(rkind) :: above_soilLiqFluxDeriv ! derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + real(rkind) :: above_soildLiq_dTk ! derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + real(rkind) :: above_soilFracLiq ! fraction of liquid water layer above soil (canopy or snow) (-) ! -------------------------------------------------------------- ! initialize error control err=0; message='computFlux/' ! ***** - ! (0) PRELIMINARIES... + ! * PRELIMINARIES... ! ******************** - ! get the necessary variables for the flux computations associate(& ! model decisions @@ -372,27 +370,21 @@ subroutine computFlux(& dAquiferTrans_dTCanopy => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanopy )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy temperature dAquiferTrans_dTGround => deriv_data%var(iLookDERIV%dAquiferTrans_dTGround )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. ground temperature dAquiferTrans_dCanWat => deriv_data%var(iLookDERIV%dAquiferTrans_dCanWat )%dat(1) & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy total water - ) ! association to data in structures - - ! ***** - ! * PRELIMINARIES... - ! ****************** + ) ! end association to data in structures - ! increment the number of flux calls - numFluxCalls = numFluxCalls+1 + numFluxCalls = numFluxCalls+1 ! increment the number of flux calls ! modify the groundwater representation for this single-column implementation select case(ixSpatialGroundwater) case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale case(localColumn); local_ixGroundwater = ixGroundwater ! go with the specified decision case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return - end select ! (modify the groundwater representation for this single-column implementation) + end select ! end modify the groundwater representation for this single-column implementation ! initialize liquid water fluxes throughout the snow and soil domains ! NOTE: used in the energy routines, which is called before the hydrology routines - if(firstFluxCall)then - if(nSnow>0)& - iLayerLiqFluxSnow(0:nSnow) = 0._rkind + if (firstFluxCall) then + if (nSnow>0) iLayerLiqFluxSnow(0:nSnow) = 0._rkind iLayerLiqFluxSoil(0:nSoil) = 0._rkind end if @@ -403,7 +395,7 @@ subroutine computFlux(& ! identify the need to calculate the energy flux over vegetation doVegNrgFlux = (ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing .or. ixTopNrg/=integerMissing) - if(doVegNrgFlux)then ! check if there is a need to calculate the energy fluxes over vegetation + if (doVegNrgFlux) then ! check if there is a need to calculate the energy fluxes over vegetation dCanLiq_dTcanopy = dTheta_dTkCanopy*iden_water*canopyDepth ! derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) ! calculate the energy fluxes over vegetation @@ -470,10 +462,10 @@ subroutine computFlux(& dGroundNetFlux_dCanWat, & ! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) ! output: error control err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! check fluxes - if(globalPrintFlag)then + if (globalPrintFlag) then print*, '**' write(*,'(a,1x,10(f30.20))') 'canopyDepth = ', canopyDepth write(*,'(a,1x,10(f30.20))') 'mLayerDepth(1:2) = ', mLayerDepth(1:2) @@ -484,16 +476,16 @@ subroutine computFlux(& write(*,'(a,1x,10(f30.20))') 'scalarCanopyNetNrgFlux = ', scalarCanopyNetNrgFlux write(*,'(a,1x,10(f30.20))') 'scalarGroundNetNrgFlux = ', scalarGroundNetNrgFlux write(*,'(a,1x,10(f30.20))') 'dGroundNetFlux_dGroundTemp = ', dGroundNetFlux_dGroundTemp - endif ! if checking fluxes + end if ! if checking fluxes - endif ! if calculating the energy fluxes over vegetation + end if ! if calculating the energy fluxes over vegetation ! ***** ! * CALCULATE ENERGY FLUXES THROUGH THE SNOW-SOIL DOMAIN... ! ********************************************************** ! check the need to compute energy fluxes throughout the snow+soil domain - if(nSnowSoilNrg>0)then + if (nSnowSoilNrg>0) then ! calculate energy fluxes at layer interfaces through the snow and soil domain call ssdNrgFlux(& @@ -526,24 +518,24 @@ subroutine computFlux(& dNrgFlux_dWatBelow, & ! intent(out):  derivatives in the flux w.r.t. water state in the layer below ! output: error control err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! calculate net energy fluxes for each snow and soil layer (J m-3 s-1) do iLayer=1,nLayers mLayerNrgFlux(iLayer) = -(iLayerNrgFlux(iLayer) - iLayerNrgFlux(iLayer-1))/mLayerDepth(iLayer) - if(globalPrintFlag)then + if (globalPrintFlag) then if(iLayer < 10) write(*,'(a,1x,i4,1x,10(f25.15,1x))') 'iLayer, iLayerNrgFlux(iLayer-1:iLayer), mLayerNrgFlux(iLayer) = ', iLayer, iLayerNrgFlux(iLayer-1:iLayer), mLayerNrgFlux(iLayer) - endif + end if end do - endif ! if computing energy fluxes throughout the snow+soil domain + end if ! end if computing energy fluxes throughout the snow+soil domain ! ***** ! * CALCULATE THE LIQUID FLUX THROUGH VEGETATION... ! ************************************************** ! check the need to compute the liquid water fluxes through vegetation - if(ixVegHyd/=integerMissing)then + if (ixVegHyd/=integerMissing) then ! calculate liquid water fluxes through vegetation call vegLiqFlux(& @@ -560,7 +552,7 @@ subroutine computFlux(& scalarThroughfallRainDeriv, & ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) scalarCanopyLiqDrainageDeriv, & ! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! calculate the net liquid water flux for the vegetation canopy scalarCanopyNetLiqFlux = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage @@ -569,7 +561,7 @@ subroutine computFlux(& scalarCanopyLiqDeriv = scalarThroughfallRainDeriv + scalarCanopyLiqDrainageDeriv ! test - if(globalPrintFlag)then + if (globalPrintFlag) then print*, '**' print*, 'scalarRainfall = ', scalarRainfall print*, 'scalarThroughfallRain = ', scalarThroughfallRain @@ -577,28 +569,28 @@ subroutine computFlux(& print*, 'scalarCanopyLiqDrainage = ', scalarCanopyLiqDrainage print*, 'scalarCanopyNetLiqFlux = ', scalarCanopyNetLiqFlux print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial - endif + end if - endif ! computing the liquid water fluxes through vegetation + end if ! end if computing the liquid water fluxes through vegetation ! ***** ! * CALCULATE THE LIQUID FLUX THROUGH SNOW... ! ******************************************** ! check the need to compute liquid water fluxes through snow - if(nSnowOnlyHyd>0)then + if (nSnowOnlyHyd>0) then ! compute liquid fluxes through snow call snowLiqFlx(& ! input: model control - nSnow, & ! intent(in): number of snow layers - firstFluxCall, & ! intent(in): the first flux call (compute variables that are constant over the iterations) - (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution + nSnow, & ! intent(in): number of snow layers + firstFluxCall, & ! intent(in): the first flux call (compute variables that are constant over the iterations) + (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution ! input: forcing for the snow domain - scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) - scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) + scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) + scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) ! input: model state vector - mLayerVolFracLiqTrial(1:nSnow), & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) + mLayerVolFracLiqTrial(1:nSnow), & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) ! input-output: data structures indx_data, & ! intent(in): model indices mpar_data, & ! intent(in): model parameters @@ -609,7 +601,7 @@ subroutine computFlux(& iLayerLiqFluxSnowDeriv(0:nSnow), & ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) ! output: error control err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! define forcing for the soil domain scalarRainPlusMelt = iLayerLiqFluxSnow(nSnow) ! drainage from the base of the snowpack @@ -631,11 +623,11 @@ subroutine computFlux(& ! define forcing for the soil domain for the case of no snow layers ! NOTE: in case where nSnowOnlyHyd==0 AND snow layers exist, then scalarRainPlusMelt is taken from the previous flux evaluation - if(nSnow==0)then !no snow layers + if (nSnow==0) then !no snow layers scalarRainPlusMelt = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water & ! liquid flux from the canopy (m s-1) + drainageMeltPond/iden_water ! melt of the snow without a layer (m s-1) - if(ixVegHyd/=integerMissing)then + if (ixVegHyd/=integerMissing) then ! save canopy derivatives above_soilLiqFluxDeriv = scalarCanopyLiqDeriv/iden_water ! derivative in (throughfall + drainage) w.r.t. canopy liquid water above_soildLiq_dTk = dCanLiq_dTcanopy ! derivative of canopy liquid storage w.r.t. temperature @@ -644,21 +636,21 @@ subroutine computFlux(& above_soilLiqFluxDeriv = 0._rkind above_soildLiq_dTk = 0._rkind above_soilFracLiq = 0._rkind - endif + end if else ! snow layers, take from previous flux calculation above_soilLiqFluxDeriv = iLayerLiqFluxSnowDeriv(nSnow) ! derivative in vertical liquid water flux at bottom snow layer interface above_soildLiq_dTk = mLayerdTheta_dTk(nSnow) ! derivative in volumetric liquid water content in bottom snow layer w.r.t. temperature above_soilFracLiq = mLayerFracLiqSnow(nSnow) ! fraction of liquid water in bottom snow layer (-) - endif ! snow layers or not + end if ! snow layers or not - endif ! if calculating the liquid flux through snow + end if ! if calculating the liquid flux through snow ! ***** ! * CALCULATE THE LIQUID FLUX THROUGH SOIL... ! ******************************************** ! check the need to calculate the liquid flux through soil - if(nSoilOnlyHyd>0)then + if (nSoilOnlyHyd>0) then ! calculate the liquid flux through soil call soilLiqFlx(& @@ -680,9 +672,9 @@ subroutine computFlux(& dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water - above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature - above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) + above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) ! input: fluxes scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) scalarGroundEvaporation, & ! intent(in): ground evaporation (kg m-2 s-1) @@ -722,7 +714,7 @@ subroutine computFlux(& mLayerdTrans_dCanWat, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy total water ! output: error control err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! calculate net liquid water fluxes for each soil layer (s-1) do iLayer=1,nSoil @@ -730,39 +722,39 @@ subroutine computFlux(& end do ! calculate the soil control on infiltration - if(nSnow==0) then + if (nSnow==0) then ! * case of infiltration into soil - if(scalarMaxInfilRate > scalarRainPlusMelt)then ! infiltration is not rate-limited + if (scalarMaxInfilRate > scalarRainPlusMelt) then ! infiltration is not rate-limited scalarSoilControl = (1._rkind - scalarFrozenArea)*scalarInfilArea else scalarSoilControl = 0._rkind ! (scalarRainPlusMelt exceeds maximum infiltration rate - endif + end if else ! * case of infiltration into snow scalarSoilControl = 1._rkind - endif + end if ! compute drainage from the soil zone (needed for mass balance checks and in aquifer recharge) scalarSoilDrainage = iLayerLiqFluxSoil(nSoil) ! expand derivatives to the total water matric potential ! NOTE: arrays are offset because computing derivatives in interface fluxes, at the top and bottom of the layer respectively - if(globalPrintFlag) print*, 'dPsiLiq_dPsi0(1:nSoil) = ', dPsiLiq_dPsi0(1:nSoil) + if (globalPrintFlag) print*, 'dPsiLiq_dPsi0(1:nSoil) = ', dPsiLiq_dPsi0(1:nSoil) dq_dHydStateAbove(1:nSoil) = dq_dHydStateAbove(1:nSoil) *dPsiLiq_dPsi0(1:nSoil) dq_dHydStateBelow(0:nSoil-1) = dq_dHydStateBelow(0:nSoil-1)*dPsiLiq_dPsi0(1:nSoil) dq_dHydStateLayerSurfVec(1:nSoil) = dq_dHydStateLayerSurfVec(1:nSoil)*dPsiLiq_dPsi0(1:nSoil) - endif ! if calculating the liquid flux through soil + end if ! end if calculating the liquid flux through soil ! ***** ! * CALCULATE THE GROUNDWATER FLOW... ! ************************************ ! check if computing soil hydrology - if(nSoilOnlyHyd>0)then + if (nSoilOnlyHyd>0) then ! set baseflow fluxes to zero if the topmodel baseflow routine is not used - if(local_ixGroundwater/=qbaseTopmodel)then + if (local_ixGroundwater/=qbaseTopmodel) then ! (diagnostic variables in the data structures) scalarExfiltration = 0._rkind ! exfiltration from the soil profile (m s-1) mLayerColumnOutflow(:) = 0._rkind ! column outflow from each soil layer (m3 s-1) @@ -773,10 +765,10 @@ subroutine computFlux(& else ! local_ixGroundwater==qbaseTopmodel ! check the derivative matrix is sized appropriately - if(size(dBaseflow_dMatric,1)/=nSoil .or. size(dBaseflow_dMatric,2)/=nSoil)then + if (size(dBaseflow_dMatric,1)/=nSoil .or. size(dBaseflow_dMatric,2)/=nSoil) then message=trim(message)//'expect dBaseflow_dMatric to be nSoil x nSoil' err=20; return - endif + end if ! compute the baseflow flux call groundwatr(& @@ -797,12 +789,12 @@ subroutine computFlux(& diag_data, & ! intent(in): model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU ! output - ixSaturation, & ! intent(inout) index of lowest saturated layer (NOTE: only computed on the first iteration) - mLayerBaseflow, & ! intent(out): baseflow from each soil layer (m s-1) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - endif ! computing baseflow flux + ixSaturation, & ! intent(inout): index of lowest saturated layer (NOTE: only computed on the first iteration) + mLayerBaseflow, & ! intent(out): baseflow from each soil layer (m s-1) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + err,cmessage) ! intent(out): error control + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + end if ! computing baseflow flux ! compute total baseflow from the soil zone (needed for mass balance checks) scalarSoilBaseflow = sum(mLayerBaseflow) @@ -812,7 +804,7 @@ subroutine computFlux(& ! (Note: scalarSoilBaseflow may need to re-envisioned in topmodel formulation if parts of it flow into neighboring soil rather than exfiltrate) scalarTotalRunoff = scalarSurfaceRunoff + scalarSoilDrainage + scalarSoilBaseflow - endif ! if computing soil hydrology + end if ! end if computing soil hydrology ! ***** @@ -820,36 +812,36 @@ subroutine computFlux(& ! ******************************************** ! check if computing aquifer fluxes - if(ixAqWat/=integerMissing)then - if(local_ixGroundwater==bigBucket)then ! identify modeling decision + if (ixAqWat/=integerMissing) then + if (local_ixGroundwater==bigBucket) then ! identify modeling decision ! compute fluxes for the big bucket call bigAquifer(& ! input: state variables and fluxes - scalarAquiferStorageTrial, & ! intent(in): trial value of aquifer storage (m) - scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) - scalarSoilDrainage, & ! intent(in): soil drainage (m s-1) + scalarAquiferStorageTrial, & ! intent(in): trial value of aquifer storage (m) + scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) + scalarSoilDrainage, & ! intent(in): soil drainage (m s-1) ! input: pre-computed derivatives - dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) ! input: diagnostic variables and parameters - mpar_data, & ! intent(in): model parameter structure - diag_data, & ! intent(in): diagnostic variable structure + mpar_data, & ! intent(in): model parameter structure + diag_data, & ! intent(in): diagnostic variable structure ! output: fluxes - scalarAquiferTranspire, & ! intent(out): transpiration loss from the aquifer (m s-1) - scalarAquiferRecharge, & ! intent(out): recharge to the aquifer (m s-1) - scalarAquiferBaseflow, & ! intent(out): total baseflow from the aquifer (m s-1) - dBaseflow_dAquifer, & ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) + scalarAquiferTranspire, & ! intent(out): transpiration loss from the aquifer (m s-1) + scalarAquiferRecharge, & ! intent(out): recharge to the aquifer (m s-1) + scalarAquiferBaseflow, & ! intent(out): total baseflow from the aquifer (m s-1) + dBaseflow_dAquifer, & ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) ! output: derivatives in transpiration w.r.t. canopy state variables dAquiferTrans_dTCanair, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature dAquiferTrans_dTCanopy, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy temperature dAquiferTrans_dTGround, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. ground temperature dAquiferTrans_dCanWat, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy total water ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + err,cmessage) ! intent(out): error control + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! compute total runoff (overwrite previously calculated value before considering aquifer). ! (Note: SoilDrainage goes into aquifer, not runoff) @@ -863,40 +855,40 @@ subroutine computFlux(& dBaseflow_dAquifer = 0._rkind ! change in baseflow flux w.r.t. aquifer storage (s-1) end if ! no aquifer - endif ! if computing aquifer fluxes + end if ! if computing aquifer fluxes ! ***** ! (X) WRAP UP... ! ************* ! define model flux vector for the vegetation sub-domain - if(ixCasNrg/=integerMissing) fluxVec(ixCasNrg) = scalarCanairNetNrgFlux/canopyDepth - if(ixVegNrg/=integerMissing) fluxVec(ixVegNrg) = scalarCanopyNetNrgFlux/canopyDepth - if(ixVegHyd/=integerMissing) fluxVec(ixVegHyd) = scalarCanopyNetLiqFlux ! NOTE: solid fluxes are handled separately + if (ixCasNrg/=integerMissing) fluxVec(ixCasNrg) = scalarCanairNetNrgFlux/canopyDepth + if (ixVegNrg/=integerMissing) fluxVec(ixVegNrg) = scalarCanopyNetNrgFlux/canopyDepth + if (ixVegHyd/=integerMissing) fluxVec(ixVegHyd) = scalarCanopyNetLiqFlux ! NOTE: solid fluxes are handled separately ! populate the flux vector for energy - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + if (nSnowSoilNrg>0) then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! loop through non-missing energy state variables in the snow+soil domain fluxVec( ixSnowSoilNrg(iLayer) ) = mLayerNrgFlux(iLayer) end do ! looping through non-missing energy state variables in the snow+soil domain - endif + end if ! populate the flux vector for hydrology ! NOTE: ixVolFracWat and ixVolFracLiq can also include states in the soil domain, hence enable primary variable switching - if(nSnowSoilHyd>0)then ! check if any hydrology states exist + if (nSnowSoilHyd>0) then ! check if any hydrology states exist do iLayer=1,nLayers - if(ixSnowSoilHyd(iLayer)/=integerMissing)then ! check if a given hydrology state exists - select case( layerType(iLayer) ) - case(iname_snow); fluxVec( ixSnowSoilHyd(iLayer) ) = mLayerLiqFluxSnow(iLayer) - case(iname_soil); fluxVec( ixSnowSoilHyd(iLayer) ) = mLayerLiqFluxSoil(iLayer-nSnow) + if (ixSnowSoilHyd(iLayer)/=integerMissing) then ! check if a given hydrology state exists + select case(layerType(iLayer)) + case(iname_snow); fluxVec(ixSnowSoilHyd(iLayer)) = mLayerLiqFluxSnow(iLayer) + case(iname_soil); fluxVec(ixSnowSoilHyd(iLayer)) = mLayerLiqFluxSoil(iLayer-nSnow) case default; err=20; message=trim(message)//'expect layerType to be either iname_snow or iname_soil'; return end select - endif ! if a given hydrology state exists - end do ! looping through non-missing energy state variables in the snow+soil domain - endif ! if any hydrology states exist + end if ! end if a given hydrology state exists + end do ! end looping through non-missing energy state variables in the snow+soil domain + end if ! end if any hydrology states exist ! compute the flux vector for the aquifer - if(ixAqWat/=integerMissing) fluxVec(ixAqWat) = scalarAquiferTranspire + scalarAquiferRecharge - scalarAquiferBaseflow + if (ixAqWat/=integerMissing) fluxVec(ixAqWat) = scalarAquiferTranspire + scalarAquiferRecharge - scalarAquiferBaseflow ! set the first flux call to false firstFluxCall=.false. @@ -912,15 +904,15 @@ end subroutine computFlux ! ********************************************************************************************************** subroutine soilCmpres(& ! input: - dt, & ! intent(in): length of the time step (seconds) - ixRichards, & ! intent(in): choice of option for Richards' equation - ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers - mLayerMatricHead, & ! intent(in): matric head at the start of the time step (m) - mLayerMatricHeadTrial, & ! intent(in): trial value of matric head (m) - mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) - mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice content in each soil layer (-) - specificStorage, & ! intent(in): specific storage coefficient (m-1) - theta_sat, & ! intent(in): soil porosity (-) + dt, & ! intent(in): length of the time step (seconds) + ixRichards, & ! intent(in): choice of option for Richards' equation + ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers + mLayerMatricHead, & ! intent(in): matric head at the start of the time step (m) + mLayerMatricHeadTrial, & ! intent(in): trial value of matric head (m) + mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) + mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice content in each soil layer (-) + specificStorage, & ! intent(in): specific storage coefficient (m-1) + theta_sat, & ! intent(in): soil porosity (-) ! output: compress, & ! intent(out): compressibility of the soil matrix (-), per second dCompress_dPsi, & ! intent(out): derivative in compressibility w.r.t. matric head (m-1) @@ -947,14 +939,14 @@ subroutine soilCmpres(& ! initialize error control err=0; message='soilCmpres/' ! (only compute for the mixed form of Richards' equation) - if(ixRichards==mixdform)then + if (ixRichards==mixdform) then do iLayer=1,size(mLayerMatricHead) - if(iLayer>=ixBeg .and. iLayer<=ixEnd)then + if (iLayer>=ixBeg .and. iLayer<=ixEnd) then ! compute the derivative for the compressibility term (m-1), no volume expansion for total water dCompress_dPsi(iLayer) = specificStorage*(mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer))/theta_sat(iLayer) ! compute the compressibility term (-) per second compress(iLayer) = (mLayerMatricHeadTrial(iLayer) - mLayerMatricHead(iLayer))*dCompress_dPsi(iLayer)/dt - endif + end if end do else compress(:) = 0._rkind @@ -962,51 +954,50 @@ subroutine soilCmpres(& end if end subroutine soilCmpres - ! ********************************************************************************************************** ! public subroutine soilCmpres: compute soil compressibility (-) and its derivative w.r.t matric head (m-1) ! ********************************************************************************************************** subroutine soilCmpresPrime(& ! input: - ixRichards, & ! intent(in): choice of option for Richards' equation - ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers - mLayerMatricHeadPrime, & ! intent(in): matric head at the start of the time step (m) - mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) - mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice content in each soil layer (-) - specificStorage, & ! intent(in): specific storage coefficient (m-1) - theta_sat, & ! intent(in): soil porosity (-) + ixRichards, & ! intent(in): choice of option for Richards' equation + ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers + mLayerMatricHeadPrime, & ! intent(in): matric head at the start of the time step (m) + mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) + mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice content in each soil layer (-) + specificStorage, & ! intent(in): specific storage coefficient (m-1) + theta_sat, & ! intent(in): soil porosity (-) ! output: compress, & ! intent(out): compressibility of the soil matrix (-) dCompress_dPsi, & ! intent(out): derivative in compressibility w.r.t. matric head (m-1) err,message) ! intent(out): error code and error message implicit none ! input: - integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation - integer(i4b),intent(in) :: ixBeg,ixEnd ! start and end indices defining desired layers - real(rkind),intent(in) :: mLayerMatricHeadPrime(:) ! matric head at the start of the time step (m) - real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) - real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) - real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) - real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) + integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation + integer(i4b),intent(in) :: ixBeg,ixEnd ! start and end indices defining desired layers + real(rkind),intent(in) :: mLayerMatricHeadPrime(:) ! matric head at the start of the time step (m) + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) + real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) + real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) ! output: - real(rkind),intent(inout) :: compress(:) ! soil compressibility (-) - real(rkind),intent(inout) :: dCompress_dPsi(:) ! derivative in soil compressibility w.r.t. matric head (m-1) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + real(rkind),intent(inout) :: compress(:) ! soil compressibility (-) + real(rkind),intent(inout) :: dCompress_dPsi(:) ! derivative in soil compressibility w.r.t. matric head (m-1) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! local variables - integer(i4b) :: iLayer ! index of soil layer + integer(i4b) :: iLayer ! index of soil layer ! -------------------------------------------------------------- ! initialize error control err=0; message='soilCmpresPrime/' ! (only compute for the mixed form of Richards' equation) - if(ixRichards==mixdform)then + if (ixRichards==mixdform) then do iLayer=1,size(mLayerMatricHeadPrime) - if(iLayer>=ixBeg .and. iLayer<=ixEnd)then + if (iLayer>=ixBeg .and. iLayer<=ixEnd) then ! compute the derivative for the compressibility term (m-1), no volume expansion for total water dCompress_dPsi(iLayer) = specificStorage*(mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer))/theta_sat(iLayer) ! compute the compressibility term (-) instantaneously compress(iLayer) = mLayerMatricHeadPrime(iLayer) * dCompress_dPsi(iLayer) - endif + end if end do else compress(:) = 0._rkind From 63c77cc3f18eba9387657bde5afe623d5a4e38c0 Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Fri, 11 Aug 2023 08:42:56 -0600 Subject: [PATCH 0845/1472] Modified for files that now depend on SUMMA instead of SUMMA-Actors having its own version --- build/cmake/CMakeLists.txt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 4ff65258c..213111efa 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -104,9 +104,9 @@ endif() if(CMAKE_BUILD_TYPE MATCHES Debug) message("\nSetting Debug Options") add_compile_definitions(DEBUG) - set(FLAGS_NOAH -g -O0 -fbounds-check -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) - set(FLAGS_ALL -g -O0 -fbounds-check -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp) - set(FLAGS_CXX -g -O0 -fbounds-check -Wfatal-errors -std=c++17) + set(FLAGS_NOAH -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) + set(FLAGS_ALL -g -O0 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp) + set(FLAGS_CXX -g -O0 -Wfatal-errors -std=c++17) else() message("\nSetting Release Options") set(FLAGS_NOAH -O3 -flto -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors) @@ -279,7 +279,7 @@ set(NRPROC # Hook-up modules set(HOOKUP ${HOOKUP_DIR}/ascii_util.f90 - ${SUB_HOOKUP_DIR}/summaFileManager.f90) + ${HOOKUP_DIR}/summaFileManager.f90) # Data modules set(DATAMS @@ -330,8 +330,8 @@ set(PRELIM ${ENGINE_DIR}/sunGeomtry.f90) set(PRELIM_NOT_ACTORS ${ENGINE_DIR}/read_param.f90) -set(PRELIM_ACTORS - ${SUB_ENGINE_DIR}/alloc_fileAccess.f90) +# set(PRELIM_ACTORS +# ${SUB_ENGINE_DIR}/alloc_fileAccess.f90) # Model run support modules set(MODRUN @@ -394,7 +394,7 @@ set(SOLVER_SUNDIALS # Driver support modules set(DRIVER ${DRIVER_DIR}/summa_alarms.f90 - ${SUB_DRIVER_DIR}/summa_globalData.f90 + ${DRIVER_DIR}/summa_globalData.f90 ${SUB_DRIVER_DIR}/summa_util.f90) set(DRIVER_NOT_ACTORS ${DRIVER_DIR}/summa_type.f90 @@ -492,7 +492,7 @@ set(SUMMA_ALL if(CMAKE_BUILD_TYPE MATCHES Actors) set(SUMMA_ALL ${SUMMA_ALL} - ${PRELIM_ACTORS} + # ${PRELIM_ACTORS} ${FILE_ACCESS_INTERFACE} ${JOB_INTERFACE} ${GRU_INTERFACE} From 95465edba908875263f5de3fef20887b5c7157c4 Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Fri, 11 Aug 2023 11:29:23 -0600 Subject: [PATCH 0846/1472] Summa-Actors can use allocspace.f90 from Summa now --- build/cmake/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 150884402..8579165ae 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -315,7 +315,7 @@ set(NETCDF # Preliminary modules set(PRELIM - ${SUB_ENGINE_DIR}/allocspace.f90 + ${ENGINE_DIR}/allocspace.f90 ${SUB_ENGINE_DIR}/check_icond.f90 ${ENGINE_DIR}/checkStruc.f90 ${ENGINE_DIR}/childStruc.f90 From 3d7eb40ac0006f762c08a60c280bad2eb3bfa6a8 Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Fri, 11 Aug 2023 16:44:43 -0600 Subject: [PATCH 0847/1472] updated cmakeLists file for actors files that are no longer needed --- build/cmake/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 8579165ae..6692717c5 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -122,7 +122,7 @@ if(CMAKE_BUILD_TYPE MATCHES Sundials) "${PARENT_DIR}../sundials/instdir" "${F_MASTER}../../SummaSundials/sundials/instdir" "${PARENT_DIR}../../SummaSundials/sundials/instdir" - "/usr/local/sundials-6.3.0" + "/usr/local/sundials/v6.6" "../../../../SummaSundials/sundials/instdir") #look in same head directory as summa or up to another installation if(EXISTS ${dir}) set(DIR_SUNDIALS ${dir}) @@ -393,9 +393,9 @@ set(SOLVER_SUNDIALS # Driver support modules set(DRIVER ${DRIVER_DIR}/summa_alarms.f90 - ${DRIVER_DIR}/summa_globalData.f90 - ${SUB_DRIVER_DIR}/summa_util.f90) -set(DRIVER_NOT_ACTORS + ${DRIVER_DIR}/summa_globalData.f90) + set(DRIVER_NOT_ACTORS + ${DRIVER_DIR}/summa_util.f90 ${DRIVER_DIR}/summa_type.f90 ${DRIVER_DIR}/summa_defineOutput.f90 ${DRIVER_DIR}/summa_init.f90 From 207fd6c57be4c3909a21767440c88cf1852dec2e Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 11 May 2023 04:14:38 -0600 Subject: [PATCH 0848/1472] General clean up and shortening of computFlux.f90, bigAquifer.f90, snowLiqFlx.f90, ssdNrgFlux.f90, and vegLiqFlux.f90. --- build/source/engine/bigAquifer.f90 | 51 ++++++++++------------ build/source/engine/computFlux.f90 | 1 - build/source/engine/snowLiqFlx.f90 | 51 +++++++++++----------- build/source/engine/ssdNrgFlux.f90 | 68 ++++++++++++------------------ build/source/engine/vegLiqFlux.f90 | 19 ++++----- 5 files changed, 83 insertions(+), 107 deletions(-) diff --git a/build/source/engine/bigAquifer.f90 b/build/source/engine/bigAquifer.f90 index 064ae2524..37c3a807c 100644 --- a/build/source/engine/bigAquifer.f90 +++ b/build/source/engine/bigAquifer.f90 @@ -20,7 +20,6 @@ module bigAquifer_module ! ----------------------------------------------------------------------------------------------------------- - ! numerical recipes data types USE nrtype @@ -30,16 +29,13 @@ module bigAquifer_module ! physical constants USE multiconst,only:& - LH_vap, & ! latent heat of vaporization (J kg-1) - iden_water ! intrinsic density of water (kg m-3) - + LH_vap, & ! latent heat of vaporization (J kg-1) + iden_water ! intrinsic density of water (kg m-3) ! ----------------------------------------------------------------------------------------------------------- implicit none private -public::bigAquifer +public :: bigAquifer contains - - ! *************************************************************************************************************** ! public subroutine bigAquifer: compute aquifer water fluxes and their derivatives ! *************************************************************************************************************** @@ -62,17 +58,17 @@ subroutine bigAquifer(& scalarAquiferBaseflow, & ! intent(out): total baseflow from the aquifer (m s-1) dBaseflow_dAquifer, & ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) ! output: derivatives in transpiration w.r.t. canopy state variables - dAquiferTrans_dTCanair, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature - dAquiferTrans_dTCanopy, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy temperature - dAquiferTrans_dTGround, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. ground temperature - dAquiferTrans_dCanWat, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy total water + dAquiferTrans_dTCanair, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + dAquiferTrans_dTCanopy, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy temperature + dAquiferTrans_dTGround, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. ground temperature + dAquiferTrans_dCanWat, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy total water ! output: error control err,message) ! intent(out): error control ! named variables - USE var_lookup,only:iLookDIAG ! named variables for structure elements - USE var_lookup,only:iLookPARAM ! named variables for structure elements + USE var_lookup,only:iLookDIAG ! named variables for structure elements + USE var_lookup,only:iLookPARAM ! named variables for structure elements ! data types - USE data_types,only:var_dlength ! x%var(:)%dat (rkind) + USE data_types,only:var_dlength ! x%var(:)%dat (rkind) ! ------------------------------------------------------------------------------------------------------------------------------------------------- implicit none ! input: state variables, fluxes, and parameters @@ -80,26 +76,26 @@ subroutine bigAquifer(& real(rkind),intent(in) :: scalarCanopyTranspiration ! canopy transpiration (kg m-2 s-1) real(rkind),intent(in) :: scalarSoilDrainage ! soil drainage (m s-1) ! input: pre-computed derivatves - real(rkind),intent(in) :: dCanopyTrans_dCanWat ! derivative in canopy transpiration w.r.t. canopy total water content (s-1) - real(rkind),intent(in) :: dCanopyTrans_dTCanair ! derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - real(rkind),intent(in) :: dCanopyTrans_dTCanopy ! derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - real(rkind),intent(in) :: dCanopyTrans_dTGround ! derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + real(rkind),intent(in) :: dCanopyTrans_dCanWat ! derivative in canopy transpiration w.r.t. canopy total water content (s-1) + real(rkind),intent(in) :: dCanopyTrans_dTCanair ! derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(rkind),intent(in) :: dCanopyTrans_dTCanopy ! derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(rkind),intent(in) :: dCanopyTrans_dTGround ! derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) ! input: diagnostic variables and parameters - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU ! output: fluxes real(rkind),intent(out) :: scalarAquiferTranspire ! transpiration loss from the aquifer (m s-1) real(rkind),intent(out) :: scalarAquiferRecharge ! recharge to the aquifer (m s-1) real(rkind),intent(out) :: scalarAquiferBaseflow ! total baseflow from the aquifer (m s-1) real(rkind),intent(out) :: dBaseflow_dAquifer ! change in baseflow flux w.r.t. aquifer storage (s-1) ! output: derivatives in transpiration w.r.t. canopy state variables - real(rkind),intent(inout) :: dAquiferTrans_dTCanair ! derivatives in the aquifer transpiration flux w.r.t. canopy air temperature - real(rkind),intent(inout) :: dAquiferTrans_dTCanopy ! derivatives in the aquifer transpiration flux w.r.t. canopy temperature - real(rkind),intent(inout) :: dAquiferTrans_dTGround ! derivatives in the aquifer transpiration flux w.r.t. ground temperature - real(rkind),intent(inout) :: dAquiferTrans_dCanWat ! derivatives in the aquifer transpiration flux w.r.t. canopy total water + real(rkind),intent(inout) :: dAquiferTrans_dTCanair ! derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + real(rkind),intent(inout) :: dAquiferTrans_dTCanopy ! derivatives in the aquifer transpiration flux w.r.t. canopy temperature + real(rkind),intent(inout) :: dAquiferTrans_dTGround ! derivatives in the aquifer transpiration flux w.r.t. ground temperature + real(rkind),intent(inout) :: dAquiferTrans_dCanWat ! derivatives in the aquifer transpiration flux w.r.t. canopy total water ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! ----------------------------------------------------------------------------------------------------------------------------------------------------- ! local variables real(rkind) :: aquiferTranspireFrac ! fraction of total transpiration that comes from the aquifer (-) @@ -140,8 +136,7 @@ subroutine bigAquifer(& ! compute the derivative in the net aquifer flux dBaseflow_dAquifer = -(aquiferBaseflowExp*aquiferBaseflowRate*(xTemp**(aquiferBaseflowExp - 1._rkind)))/aquiferScaleFactor - ! end association to data in structures - end associate + end associate ! end association to data in structure end subroutine bigAquifer diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 5dc0e6bc6..4e925e7fe 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -99,7 +99,6 @@ module computFlux_module public::soilCmpres public::soilCmpresPrime contains - ! ********************************************************************************************************* ! public subroutine computFlux: compute model fluxes ! ********************************************************************************************************* diff --git a/build/source/engine/snowLiqFlx.f90 b/build/source/engine/snowLiqFlx.f90 index 7223e84ea..7a9c01648 100644 --- a/build/source/engine/snowLiqFlx.f90 +++ b/build/source/engine/snowLiqFlx.f90 @@ -21,31 +21,29 @@ module snowLiqFlx_module ! access modules -USE nrtype ! numerical recipes data types -USE multiconst,only:iden_ice,iden_water ! intrinsic density of ice and water (kg m-3) +USE nrtype ! numerical recipes data types +USE multiconst,only:iden_ice,iden_water ! intrinsic density of ice and water (kg m-3) ! access missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing real number +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing real number ! named variables -USE var_lookup,only:iLookINDEX ! named variables for structure elements -USE var_lookup,only:iLookPARAM ! named variables for structure elements -USE var_lookup,only:iLookPROG ! named variables for structure elements -USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements +USE var_lookup,only:iLookPARAM ! named variables for structure elements +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookDIAG ! named variables for structure elements ! data types -USE data_types,only:var_d ! x%var(:) (rkind) -USE data_types,only:var_dlength ! x%var(:)%dat (rkind) -USE data_types,only:var_ilength ! x%var(:)%dat (i4b) +USE data_types,only:var_d ! x%var(:) (rkind) +USE data_types,only:var_dlength ! x%var(:)%dat (rkind) +USE data_types,only:var_ilength ! x%var(:)%dat (i4b) ! privacy implicit none private -public::snowLiqFlx +public :: snowLiqFlx contains - - ! ************************************************************************************************ ! public subroutine snowLiqFlx: compute liquid water flux through the snowpack ! ************************************************************************************************ @@ -122,45 +120,45 @@ subroutine snowLiqFlx(& err=0; message='snowLiqFlx/' ! check that the input vectors match nSnow - if(size(mLayerVolFracLiqTrial)/=nSnow .or. size(mLayerVolFracIce)/=nSnow .or. & + if (size(mLayerVolFracLiqTrial)/=nSnow .or. size(mLayerVolFracIce)/=nSnow .or. & size(iLayerLiqFluxSnow)/=nSnow+1 .or. size(iLayerLiqFluxSnowDeriv)/=nSnow+1) then err=20; message=trim(message)//'size mismatch of input/output vectors'; return end if ! check the meltwater exponent is >=1 - if(mw_exp<1._rkind)then; err=20; message=trim(message)//'meltwater exponent < 1'; return; end if + if (mw_exp<1._rkind) then; err=20; message=trim(message)//'meltwater exponent < 1'; return; end if ! get the indices for the snow+soil layers ixTop = integerMissing - if(scalarSolution)then + if (scalarSolution) then do i=1,size(ixSnowOnlyHyd) - if(ixSnowOnlyHyd(i) /= integerMissing)then + if (ixSnowOnlyHyd(i) /= integerMissing) then ixTop=ixLayerState(i) ixBot=ixTop exit ! break out of loop once found - endif + end if end do - if(ixTop == integerMissing)then + if (ixTop == integerMissing) then err=20; message=trim(message)//'Unable to identify snow layer for scalar solution!'; return end if else ixTop = 1 ixBot = nSnow - endif + end if ! define the liquid flux at the upper boundary (m s-1) iLayerLiqFluxSnow(0) = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water iLayerLiqFluxSnowDeriv(0) = 0._rkind !computed inside computJacob ! compute properties fixed over the time step - if(firstFluxCall)then + if (firstFluxCall) then ! loop through snow layers do iLayer=1,nSnow ! loop through snow layers multResid = 1._rkind/(1._rkind + exp((mLayerVolFracIce(iLayer)*iden_ice - residThrs)/residScal)) ! compute the reduction in liquid water holding capacity at high snow density (-) mLayerPoreSpace(iLayer) = 1._rkind - mLayerVolFracIce(iLayer) ! compute the pore space (-) mLayerThetaResid(iLayer) = Fcapil*mLayerPoreSpace(iLayer)*multResid ! compute the residual volumetric liquid water content (-) end do ! end looping through snow layers - endif ! end if the first flux call + end if ! end if the first flux call ! compute fluxes do iLayer=ixTop,ixBot ! (loop through snow layers) @@ -170,18 +168,17 @@ subroutine snowLiqFlx(& relSaturn = (mLayerVolFracLiqTrial(iLayer) - mLayerThetaResid(iLayer)) / availCap ! relative saturation iLayerLiqFluxSnow(iLayer) = k_snow*relSaturn**mw_exp iLayerLiqFluxSnowDeriv(iLayer) = ( (k_snow*mw_exp)/availCap ) * relSaturn**(mw_exp - 1._rkind) - if(mLayerVolFracIce(iLayer) > maxVolIceContent)then ! NOTE: use start-of-step ice content, to avoid convergence problems + if (mLayerVolFracIce(iLayer) > maxVolIceContent) then ! NOTE: use start-of-step ice content, to avoid convergence problems ! ** allow liquid water to pass through under very high ice density iLayerLiqFluxSnow(iLayer) = iLayerLiqFluxSnow(iLayer) + iLayerLiqFluxSnow(iLayer-1) !NOTE: derivative may need to be updated in future. end if else ! flow does not occur iLayerLiqFluxSnow(iLayer) = 0._rkind iLayerLiqFluxSnowDeriv(iLayer) = 0._rkind - endif ! storage above residual content + end if ! storage above residual content end do ! loop through snow layers - ! end association of local variables with information in the data structures - end associate + end associate ! end association of local variables with information in the data structures end subroutine snowLiqFlx diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index 6f4e3bc86..ee6de9efc 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -31,7 +31,6 @@ module ssdNrgFlux_module ! physical constants USE multiconst,only:& iden_water, & ! intrinsic density of water (kg m-3) - ! specific heat Cp_water ! specific heat of liquid water (J kg-1 K-1) ! missing values @@ -39,8 +38,8 @@ module ssdNrgFlux_module USE globalData,only:realMissing ! missing real number ! named variables for snow and soil -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil ! named variables USE var_lookup,only:iLookPROG ! named variables for structure elements @@ -50,27 +49,25 @@ module ssdNrgFlux_module USE var_lookup,only:iLookINDEX ! named variables for structure elements ! model decisions -USE globalData,only:model_decisions ! model decision structure -USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure +USE globalData,only:model_decisions ! model decision structure +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure ! provide access to look-up values for model decisions USE mDecisions_module,only: & ! look-up values for method used to compute derivative - numerical, & ! numerical solution - analytical, & ! analytical solution + numerical, & ! numerical solution + analytical, & ! analytical solution ! look-up values for choice of boundary conditions for thermodynamics - prescribedTemp, & ! prescribed temperature - energyFlux, & ! energy flux - zeroFlux ! zero flux - + prescribedTemp, & ! prescribed temperature + energyFlux, & ! energy flux + zeroFlux ! zero flux ! ------------------------------------------------------------------------------------------------- implicit none private -public::ssdNrgFlux +public :: ssdNrgFlux ! global parameters real(rkind),parameter :: dx=1.e-10_rkind ! finite difference increment (K) contains - ! ********************************************************************************************************** ! public subroutine ssdNrgFlux: compute energy fluxes and derivatives at layer interfaces ! ********************************************************************************************************** @@ -104,7 +101,6 @@ subroutine ssdNrgFlux(& dFlux_dWatBelow, & ! intent(out): derivatives in the flux w.r.t. water state in the layer below (W m-2 K-1) ! output: error control err,message) ! intent(out): error control - ! ------------------------------------------------------------------------------------------------------------------------------------------------- implicit none ! input: model control @@ -176,49 +172,46 @@ subroutine ssdNrgFlux(& iLayerAdvectiveFlux(0) = realMissing !included in the ground heat flux ! get the indices for the snow+soil layers - if(scalarSolution)then + if (scalarSolution) then ixLayerDesired = pack(ixLayerState, ixSnowSoilNrg/=integerMissing) ixTop = ixLayerDesired(1) ixBot = ixLayerDesired(1) else ixTop = 1 ixBot = nLayers - endif + end if ! ------------------------------------------------------------------------------------------------------------------------- ! ***** compute the conductive fluxes at layer interfaces ***** ! ------------------------------------------------------------------------------------------------------------------------- do iLayer=ixTop,ixBot - if(iLayer==nLayers)then ! (lower boundary fluxes -- positive downwards) + if (iLayer==nLayers) then ! lower boundary fluxes -- positive downwards ! flux depends on the type of lower boundary condition - select case(ix_bcLowrTdyn) ! (identify the lower boundary condition for thermodynamics + select case(ix_bcLowrTdyn) ! identify the lower boundary condition for thermodynamics case(prescribedTemp); iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*(lowerBoundTemp - mLayerTempTrial(iLayer))/(mLayerDepth(iLayer)*0.5_rkind) case(zeroFlux); iLayerConductiveFlux(iLayer) = 0._rkind - end select ! (identifying the lower boundary condition for thermodynamics) - else ! (domain boundary fluxes -- positive downwards) + end select ! identifying the lower boundary condition for thermodynamics + else ! domain boundary fluxes -- positive downwards iLayerConductiveFlux(iLayer) = -iLayerThermalC(iLayer)*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) / & (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) - end if ! (the type of layer) - end do ! looping through layers + end if ! the type of layer + end do ! end looping through layers ! ------------------------------------------------------------------------------------------------------------------------- ! ***** compute the advective fluxes at layer interfaces ***** ! ------------------------------------------------------------------------------------------------------------------------- do iLayer=ixTop,ixBot - ! get the liquid flux at layer interfaces - select case(layerType(iLayer)) + select case(layerType(iLayer)) ! get the liquid flux at layer interfaces case(iname_snow); qFlux = iLayerLiqFluxSnow(iLayer) case(iname_soil); qFlux = iLayerLiqFluxSoil(iLayer-nSnow) case default; err=20; message=trim(message)//'unable to identify layer type'; return end select - ! compute fluxes at the lower boundary -- positive downwards - if(iLayer==nLayers)then + if (iLayer==nLayers) then ! compute fluxes at the lower boundary -- positive downwards iLayerAdvectiveFlux(iLayer) = -Cp_water*iden_water*qFlux*(lowerBoundTemp - mLayerTempTrial(iLayer)) - ! compute fluxes within the domain -- positive downwards - else + else ! compute fluxes within the domain -- positive downwards iLayerAdvectiveFlux(iLayer) = -Cp_water*iden_water*qFlux*(mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer)) end if - end do ! looping through layers + end do ! end looping through layers ! ------------------------------------------------------------------------------------------------------------------------- ! ***** compute the total fluxes at layer interfaces ***** @@ -261,17 +254,16 @@ subroutine ssdNrgFlux(& case default; err=20; message=trim(message)//'unable to identify upper boundary condition for thermodynamics'; return - end select ! (identifying the upper boundary condition for thermodynamics) + end select ! end identifying the upper boundary condition for thermodynamics !dGroundNetFlux_dGroundWat = dFlux_dWatBelow(0) ! this is true, but since not used in vegNrgFlux do not define dGroundNetFlux_dGroundTemp = dFlux_dTempBelow(0) ! need this in vegNrgFlux ! loop through INTERFACES... do iLayer=ixTop,ixBot ! ***** the lower boundary - if(iLayer==nLayers)then ! (lower boundary) - + if (iLayer==nLayers) then ! if lower boundary ! identify the lower boundary condition - select case(ix_bcLowrTdyn) !prescribed temperature at the lower boundary + select case(ix_bcLowrTdyn) ! prescribed temperature at the lower boundary case(prescribedTemp) dz = mLayerDepth(iLayer)*0.5_rkind dFlux_dWatAbove(iLayer) = -dThermalC_dWatAbove(iLayer) * ( lowerBoundTemp - mLayerTempTrial(iLayer) )/dz @@ -280,8 +272,7 @@ subroutine ssdNrgFlux(& dFlux_dWatAbove(iLayer) = 0._rkind dFlux_dTempAbove(iLayer) = 0._rkind case default; err=20; message=trim(message)//'unable to identify lower boundary condition for thermodynamics'; return - end select ! (identifying the lower boundary condition for thermodynamics) - + end select ! end identifying the lower boundary condition for thermodynamics ! ***** internal layers else dz = (mLayerHeight(iLayer+1) - mLayerHeight(iLayer)) @@ -290,14 +281,11 @@ subroutine ssdNrgFlux(& dFlux_dTempAbove(iLayer) = -dThermalC_dTempAbove(iLayer) * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz + iLayerThermalC(iLayer)/dz dFlux_dTempBelow(iLayer) = -dThermalC_dTempBelow(iLayer) * ( mLayerTempTrial(iLayer+1) - mLayerTempTrial(iLayer) )/dz - iLayerThermalC(iLayer)/dz end if ! type of layer (upper, internal, or lower) + end do ! end looping through layers - end do ! (looping through layers) - - ! end association of local variables with information in the data structures - end associate + end associate ! end association of local variables with information in the data structures end subroutine ssdNrgFlux - end module ssdNrgFlux_module diff --git a/build/source/engine/vegLiqFlux.f90 b/build/source/engine/vegLiqFlux.f90 index 46de9f949..2c695c645 100644 --- a/build/source/engine/vegLiqFlux.f90 +++ b/build/source/engine/vegLiqFlux.f90 @@ -24,8 +24,8 @@ module vegLiqFlux_module USE nrtype ! data types -USE data_types,only:var_d ! x%var(:) (rkind) -USE data_types,only:var_dlength ! x%var(:)%dat (rkind) +USE data_types,only:var_d ! x%var(:) (rkind) +USE data_types,only:var_dlength ! x%var(:)%dat (rkind) ! named variables USE var_lookup,only:iLookPARAM,iLookDIAG ! named variables for structure elements @@ -36,17 +36,15 @@ module vegLiqFlux_module ! decisions on canopy interception parameterization USE mDecisions_module,only: & - unDefined, & ! original model (no flexibility in canopy interception): 100% of rainfall is intercepted by the vegetation canopy - sparseCanopy, & ! fraction of rainfall that never hits the canopy (throughfall); drainage above threshold - storageFunc ! throughfall a function of canopy storage; 100% throughfall when canopy is at capacity + unDefined, & ! original model (no flexibility in canopy interception): 100% of rainfall is intercepted by the vegetation canopy + sparseCanopy, & ! fraction of rainfall that never hits the canopy (throughfall); drainage above threshold + storageFunc ! throughfall a function of canopy storage; 100% throughfall when canopy is at capacity ! privacy implicit none private -public::vegLiqFlux +public :: vegLiqFlux contains - - ! ************************************************************************************************ ! public subroutine vegLiqFlux: compute water balance for the vegetation canopy ! ************************************************************************************************ @@ -92,7 +90,7 @@ subroutine vegLiqFlux(& err=0; message="vegLiqFlux/" ! set throughfall to inputs if vegetation is completely buried with snow - if(.not.computeVegFlux)then + if (.not.computeVegFlux) then scalarThroughfallRain = scalarRainfall scalarCanopyLiqDrainage = 0._rkind scalarThroughfallRainDeriv = 0._rkind @@ -134,8 +132,7 @@ subroutine vegLiqFlux(& scalarCanopyLiqDrainageDeriv = 0._rkind end if - ! end association of local variables with information in the data structures - end associate + end associate ! end association of local variables with information in the data structures end subroutine vegLiqFlux From 57aa9f2e391a8613db7ab121b2e181c2143e75b3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 14 Aug 2023 12:11:56 +0900 Subject: [PATCH 0849/1472] -fuse-linker-plugin is not supported in this configuration for mac, not sure how to fix so removed --- build/cmake/CMakeLists.txt | 6 +++--- build/cmake/build.cluster.bash | 2 ++ build/cmake/build.pc.bash | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 942177081..d11d9f7db 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -110,9 +110,9 @@ if(CMAKE_BUILD_TYPE MATCHES Debug) set(FLAGS_CXX -g -O0 -fbacktrace -fbounds-check -Wfatal-errors -std=c++17 ${FLAGS_OPT}) else() message("\nSetting Release Options") - set(FLAGS_NOAH -O3 -flto=1 -fuse-linker-plugin -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) - set(FLAGS_ALL -O3 -flto=1 -fuse-linker-plugin -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp ${FLAGS_OPT}) - set(FLAGS_CXX -O3 -flto=1 -fuse-linker-plugin -Wfatal-errors -std=c++17 ${FLAGS_OPT}) + set(FLAGS_NOAH -O3 -flto=1 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) + set(FLAGS_ALL -O3 -flto=1 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp ${FLAGS_OPT}) + set(FLAGS_CXX -O3 -flto=1 -Wfatal-errors -std=c++17 ${FLAGS_OPT}) endif() # Find Sundials if exists, add path here if FATAL_ERROR "Did not find Sundials directory" diff --git a/build/cmake/build.cluster.bash b/build/cmake/build.cluster.bash index 744804b6c..5aa490ac0 100755 --- a/build/cmake/build.cluster.bash +++ b/build/cmake/build.cluster.bash @@ -7,6 +7,8 @@ module load gcc/9.3.0 module load openblas/0.3.17 module load netcdf-fortran/4.5.2 +export FLAGS_OPT="-fuse-linker-plugin" + cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials_Cluster cmake --build ../cmake_build --target all diff --git a/build/cmake/build.pc.bash b/build/cmake/build.pc.bash index c4c98f842..69356d12a 100755 --- a/build/cmake/build.pc.bash +++ b/build/cmake/build.pc.bash @@ -10,7 +10,7 @@ #export LINK_DIRS=/usr/local/lib # Link directories for cmake #export INCLUDES_DIRS="/usr/include;/usr/local/include" # directories for INCLUDES cmake variable (cmake uses semicolons as separators) #export LIBRARY_LINKS="-llapack;-lnetcdff;-lnetcdf" # list of library links -- LAPACK builds -#export FLAGS_OPT="" # optional compiler flags -- LAPACK builds +#export FLAGS_OPT="-fuse-linker-plugin" # optional compiler flags -- LAPACK builds # PC Example using Ubuntu: Intel oneMKL builds (see https://www.intel.com/content/www/us/en/developer/tools/oneapi/onemkl-link-line-advisor.html) #oneAPI_dir=/opt/intel/oneapi # Intel oneAPI main directory @@ -19,7 +19,7 @@ #export LINK_DIRS=/usr/local/lib # Link directories for cmake #export INCLUDES_DIRS="/usr/include;/usr/local/include" # directories for INCLUDES cmake variable (cmake uses semicolons as separators) #export LIBRARY_LINKS="-lnetcdff;-lnetcdf;-Wl,--start-group ${MKLROOT}/lib/intel64/libmkl_gf_lp64.a ${MKLROOT}/lib/intel64/libmkl_gnu_thread.a ${MKLROOT}/lib/intel64/libmkl_core.a -Wl,--end-group;-lgomp;-lpthread;-lm;-ldl" # list of library links -- Intel oneMKL builds -#export FLAGS_OPT="-m64;-I"${MKLROOT}/include"" # optional compiler flags -- Intel oneMKL builds +#export FLAGS_OPT="--m64;-I"${MKLROOT}/include;-fuse-linker-plugin" # optional compiler flags -- Intel oneMKL builds # CMake Commands (build type controlled using DCMAKE_BUILD_TYPE) cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=BE From d1cb2b03d0ea577ffbec19b9ff488bbf5439b26f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 14 Aug 2023 18:11:33 +0900 Subject: [PATCH 0850/1472] if fails step, kinsol needs to reduce step --- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/summaSolve4ida.f90 | 23 +++++++++++------------ build/source/engine/summaSolve4kinsol.f90 | 22 +++++++++++++--------- build/source/engine/summaSolve4numrec.f90 | 2 +- build/source/engine/systemSolv.f90 | 17 ++++++----------- build/source/engine/varSubstep.f90 | 2 +- 6 files changed, 33 insertions(+), 35 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index efa95e0c2..1abb736cd 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -849,7 +849,7 @@ subroutine imposeConstraints(indx_data, prog_data, mpar_data, stateVec, stateVec ! choice of constraints to impose logical(lgt),parameter :: small_delTemp=.false. ! flag to constain temperature change to be less than zMaxTempIncrement logical(lgt),parameter :: detect_events=.true. ! flag to do freezing point event detection and cross-over with epsT - logical(lgt),parameter :: positive_wat=.false. ! flag to force water to not go negative + logical(lgt),parameter :: positive_wat=.true. ! flag to force water to not go negative ! ----------------------------------------------------------------------------------------------------- ! associate variables with indices of model state variables diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 8fc75649d..3df516b92 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -451,10 +451,12 @@ subroutine summaSolve4ida( & ! call IDASolve, advance solver just one internal step retvalr = FIDASolve(ida_mem, dt_cur, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) + ! early return if IDASolve failed if( retvalr < 0 )then idaSucceeds = .false. call getErrMessage(retvalr,cmessage) message=trim(message)//trim(cmessage) + !if(retvalr==-1) err = -20 ! max iterations failure, exit and reduce the data window time in varSubStep exit end if @@ -482,11 +484,11 @@ subroutine summaSolve4ida( & ! output: error control err,cmessage) ! intent(out): error control - ! early return for non-feasible solutions, will fail in current IDA formulation + ! early return for non-feasible solutions if(.not.feasible)then - eqns_data%fluxVec(:) = realMissing - message=trim(message)//trim(cmessage)//'non-feasible' - return + idaSucceeds = .false. + message=trim(message)//trim(cmessage)//'non-feasible' ! err=0 is already set, could make this a warning and reduce the data window time in varSubStep + exit end if ! sum of fluxes smoothed over the time step, average from instantaneous values @@ -538,20 +540,17 @@ subroutine summaSolve4ida( & enddo ! while loop on one_step mode until time dt_cur !****************************** End of Main Solver *************************************** - err = eqns_data%err - message = eqns_data%message - if( .not. feasible)then - idaSucceeds = .false. - message=trim(message)//trim(cmessage)//'non-feasible' - endif - if(idaSucceeds)then ! copy to output data diag_data = eqns_data%diag_data flux_data = eqns_data%flux_data deriv_data = eqns_data%deriv_data ixSaturation = eqns_data%ixSaturation - indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = eqns_data%indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) !only number of Flux Calculations changes + indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = eqns_data%indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) !only number of flux calculations changes in indx_data + err = eqns_data%err + message = eqns_data%message + else + eqns_data%fluxVec(:) = realMissing endif ! free memory diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index 444f1d500..1c8a5858c 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -344,10 +344,12 @@ subroutine summaSolve4kinsol(& !retval = FKINSol(kinsol_mem, sunvec_y, KIN_LINESEARCH, sunvec_xscale, sunvec_fscale) retval = FKINSol(kinsol_mem, sunvec_y, KIN_PICARD, sunvec_xscale, sunvec_fscale) + ! check if KINSol failed if( retvalr < 0 )then kinsolSucceeds = .false. - call getErrMessage(retval,cmessage) + call getErrMessage(retvalr,cmessage) message=trim(message)//trim(cmessage) + if(retvalr==-6) err = -20 ! max iterations failure, exit and reduce the data window time in varSubStep else ! check the feasibility of the solution feasible=.true. @@ -361,15 +363,13 @@ subroutine summaSolve4kinsol(& feasible, & ! intent(inout): flag to denote the feasibility of the solution ! output: error control err,cmessage) ! intent(out): error control - endif - !****************************** End of Main Solver *************************************** - err = eqns_data%err - message = eqns_data%message - if( .not. feasible)then - kinsolSucceeds = .false. - message=trim(message)//trim(cmessage)//'non-feasible' + if(.not. feasible)then + kinsolSucceeds = .false. + message=trim(message)//trim(cmessage)//'non-feasible' ! err=0 is already set, could make this a warning and reduce the data window time in varSubStep + endif endif + !****************************** End of Main Solver *************************************** if(kinsolSucceeds)then ! copy to output data @@ -377,7 +377,11 @@ subroutine summaSolve4kinsol(& flux_data = eqns_data%flux_data deriv_data = eqns_data%deriv_data ixSaturation = eqns_data%ixSaturation - indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = eqns_data%indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) !only number of Flux Calculations changes + indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = eqns_data%indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) !only number of flux calculations changes in indx_data + err = eqns_data%err + message = eqns_data%message + else + eqns_data%fluxVec(:) = realMissing endif ! free memory diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index b8743f287..f326da3b0 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -446,7 +446,7 @@ subroutine lineSearchRefinement(doLineSearch,stateVecTrial,newtStepScaled,aJacSc end if ! check feasibility - if(.not.feasible) return + if(.not.feasible) cycle ! go back and impose constraints again ! check convergence ! NOTE: some efficiency gains possible by scaling the full newton step outside the line search loop diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index c89e3d98a..b31a4123d 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -304,10 +304,6 @@ subroutine systemSolv(& ! (0) PRELIMINARIES... ! ******************** - ! ----- - ! * initialize... - ! --------------- - ! check if(dt_cur < tinyStep)then message=trim(message)//'dt is tiny' @@ -563,11 +559,10 @@ subroutine systemSolv(& stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step err,cmessage) ! intent(out): error control - ! check if IDA is successful + ! check if IDA is successful, only fail outright in the case of a non-recoverable error if( .not.sunSucceeds )then - err = 20 - message=trim(message)//trim(cmessage) - ! reduceCoupledStep = .true. + !if(err.ne.-20 .or. err=0) err = 20 ! 0 if infeasible solution, could happen since not using imposeConstraints + if(err.ne.-20) err = 20 ! -20 is a recoverable error return else if (tooMuchMelt) return !exit to start same step over after merge @@ -634,11 +629,11 @@ subroutine systemSolv(& sunSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step err,cmessage) ! intent(out): error control - ! check if KINSOL is successful + ! check if KINSOL is successful, only fail outright in the case of a non-recoverable error if( .not.sunSucceeds )then - err = 20 message=trim(message)//trim(cmessage) - ! reduceCoupledStep = .true. + !if(err.ne.-20 .or. err=0) err = 20 ! 0 if infeasible solution, should not happen with imposeConstraints + if(err.ne.-20) err = 20 ! -20 if hit maximum iterations return endif niter = 0 ! iterations are counted inside KINSOL solver diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index e80d06a82..6e85168e1 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -359,7 +359,7 @@ subroutine varSubstep(& ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry if(tooMuchMelt .or. reduceCoupledStep) return - ! identify failure, should not happen in SUNDIALS + ! identify failure failedSubstep = (err<0) ! check From e217bf4b079d086e66ec288c70d221af88fa6d40 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 14 Aug 2023 18:18:00 +0900 Subject: [PATCH 0851/1472] typo in retval --- build/source/engine/summaSolve4kinsol.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index 1c8a5858c..d48f977dd 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -341,8 +341,8 @@ subroutine summaSolve4kinsol(& !****************************** Main Solver ********************************************** ! Call KINSol to solve problem with choice of solver, linesearch or Picard - !retval = FKINSol(kinsol_mem, sunvec_y, KIN_LINESEARCH, sunvec_xscale, sunvec_fscale) - retval = FKINSol(kinsol_mem, sunvec_y, KIN_PICARD, sunvec_xscale, sunvec_fscale) + !retvalr = FKINSol(kinsol_mem, sunvec_y, KIN_LINESEARCH, sunvec_xscale, sunvec_fscale) + retvalr = FKINSol(kinsol_mem, sunvec_y, KIN_PICARD, sunvec_xscale, sunvec_fscale) ! check if KINSol failed if( retvalr < 0 )then From c2ce49f3be6a296b52ecc0eec74d2ce607145ebe Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 14 Aug 2023 18:56:34 +0900 Subject: [PATCH 0852/1472] comments in about best constraint set for Kinsol as of now --- build/source/engine/eval8summa.f90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 1abb736cd..8e4142cdd 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -839,7 +839,7 @@ subroutine imposeConstraints(indx_data, prog_data, mpar_data, stateVec, stateVec real(rkind) :: xPsi00 ! matric head after applying the iteration increment (m) real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) real(rkind) :: critDiff ! temperature difference from critical (K) - real(rkind),parameter :: epsT=1.e-3_rkind ! small interval above/below critical (K) + real(rkind),parameter :: epsT=1.e-3_rkind ! small interval above/below critical (K), doesn't work well at 1e-7 real(rkind),parameter :: zMaxTempIncrement=1._rkind ! maximum temperature increment (K) ! indices of model state variables integer(i4b) :: iState ! index of state within a specific variable type @@ -847,9 +847,9 @@ subroutine imposeConstraints(indx_data, prog_data, mpar_data, stateVec, stateVec ! indices of model layers integer(i4b) :: iLayer ! index of model layer ! choice of constraints to impose - logical(lgt),parameter :: small_delTemp=.false. ! flag to constain temperature change to be less than zMaxTempIncrement - logical(lgt),parameter :: detect_events=.true. ! flag to do freezing point event detection and cross-over with epsT - logical(lgt),parameter :: positive_wat=.true. ! flag to force water to not go negative + logical(lgt),parameter :: small_delTemp=.false. ! flag to constain temperature change to be less than zMaxTempIncrement, gets more accurate solution off + logical(lgt),parameter :: detect_events=.true. ! flag to do freezing point event detection and cross-over with epsT, works best on + logical(lgt),parameter :: positive_wat=.true. ! flag to force water to not go negative, works best on ! ----------------------------------------------------------------------------------------------------- ! associate variables with indices of model state variables From 291f7cb51cd249340c442dfc85190795172d86f9 Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Mon, 14 Aug 2023 14:11:19 -0600 Subject: [PATCH 0853/1472] Add evnironement variables for CMAKE to look for Sunidals and Actor Framework Libraries --- build/cmake/CMakeLists.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 1ef9c6c71..1bc58f4d9 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -123,7 +123,7 @@ if(CMAKE_BUILD_TYPE MATCHES Sundials) "${PARENT_DIR}../sundials/instdir" "${F_MASTER}../../SummaSundials/sundials/instdir" "${PARENT_DIR}../../SummaSundials/sundials/instdir" - "/usr/local/sundials/v6.6" + $ENV{SUNDIALS_PATH} "../../../../SummaSundials/sundials/instdir") #look in same head directory as summa or up to another installation if(EXISTS ${dir}) set(DIR_SUNDIALS ${dir}) @@ -144,7 +144,6 @@ if(CMAKE_BUILD_TYPE MATCHES Cluster) set(BLA_VENDOR OpenBLAS) # Packages that are required - find_package(NetCDF REQUIRED) find_package(LAPACK REQUIRED) # Set include directories @@ -195,7 +194,7 @@ else() "${PARENT_DIR}../actor-framework/install" "${F_MASTER}../../SummaActors/actor-framework/install" "${PARENT_DIR}../../SummaActors/actor-framework/install" - "/usr/local/" + $ENV{ACTOR_FRAMEWORK_PATH} "../../../../SummaActors/actor-framework/install") #look in same head directory as summa or up to another installation if(EXISTS ${dir}) set(DIR_ACTORS ${dir}) From 38503a270d1807d87ba3234269a3c69ef539b0f6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 15 Aug 2023 11:33:15 +0900 Subject: [PATCH 0854/1472] error message screwed up in ida --- build/source/engine/systemSolv.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index b31a4123d..d42576fee 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -561,6 +561,7 @@ subroutine systemSolv(& err,cmessage) ! intent(out): error control ! check if IDA is successful, only fail outright in the case of a non-recoverable error if( .not.sunSucceeds )then + message=trim(message)//trim(cmessage) !if(err.ne.-20 .or. err=0) err = 20 ! 0 if infeasible solution, could happen since not using imposeConstraints if(err.ne.-20) err = 20 ! -20 is a recoverable error return From 0f2e9df201eb95bb80f8ac5d087d1e25da936dcc Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 15 Aug 2023 13:31:14 +0900 Subject: [PATCH 0855/1472] Changed so init with canopy liq as small positive (1e-4) if canopy water was small or negative. We were doing this bump on just the water, but then that would get reset as water=liq+ice and water would start at 0, causing the first step to go infeasible in IDA since it was close to infeasible (0 or less). The previous versions of IDA were not properly failing with an infeasible solution, so this was not caught before. Also turned off -flto=1 on mac compilation since was not working well --- build/cmake/CMakeLists.txt | 8 ++++---- build/cmake/build.cluster.bash | 2 +- build/cmake/build.mac.bash | 3 ++- build/cmake/build.pc.bash | 4 ++-- build/cmake/build_actors.cluster.bash | 2 ++ build/cmake/build_actors.mac.bash | 7 +++++++ build/cmake/build_ngen.cluster.bash | 2 ++ build/cmake/build_ngen.mac.bash | 6 ++++++ build/source/netcdf/read_icond.f90 | 4 ++-- 9 files changed, 28 insertions(+), 10 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index d11d9f7db..7bc1c92bc 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -110,9 +110,9 @@ if(CMAKE_BUILD_TYPE MATCHES Debug) set(FLAGS_CXX -g -O0 -fbacktrace -fbounds-check -Wfatal-errors -std=c++17 ${FLAGS_OPT}) else() message("\nSetting Release Options") - set(FLAGS_NOAH -O3 -flto=1 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) - set(FLAGS_ALL -O3 -flto=1 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp ${FLAGS_OPT}) - set(FLAGS_CXX -O3 -flto=1 -Wfatal-errors -std=c++17 ${FLAGS_OPT}) + set(FLAGS_NOAH -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) + set(FLAGS_ALL -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp ${FLAGS_OPT}) + set(FLAGS_CXX -O3 -Wfatal-errors -std=c++17 ${FLAGS_OPT}) endif() # Find Sundials if exists, add path here if FATAL_ERROR "Did not find Sundials directory" @@ -543,7 +543,7 @@ target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) add_library(SUMMA_COMM OBJECT ${COMM_ALL}) target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES}) -target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP ${FLAGS_ALL}) # added flags to the link step +target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP ${FLAGS_ALL}) # added flags to the link step # For NexGen, build SUMMA Shared Library and add the outside BMI libraries if(CMAKE_BUILD_TYPE MATCHES NexGen) diff --git a/build/cmake/build.cluster.bash b/build/cmake/build.cluster.bash index 5aa490ac0..c80ac1d79 100755 --- a/build/cmake/build.cluster.bash +++ b/build/cmake/build.cluster.bash @@ -7,7 +7,7 @@ module load gcc/9.3.0 module load openblas/0.3.17 module load netcdf-fortran/4.5.2 -export FLAGS_OPT="-fuse-linker-plugin" +export FLAGS_OPT="-flto=1;-fuse-linker-plugin" cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials_Cluster cmake --build ../cmake_build --target all diff --git a/build/cmake/build.mac.bash b/build/cmake/build.mac.bash index 04e23f3a3..e2a9014f9 100755 --- a/build/cmake/build.mac.bash +++ b/build/cmake/build.mac.bash @@ -8,6 +8,7 @@ export FC=gfortran # Fortran compiler export LINK_DIRS=/opt/local/lib # Link directories for cmake export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) +#export FLAGS_OPT="-flto=1" # -flto=1 is slow to compile, but might want to use cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials -cmake --build ../cmake_build --target all \ No newline at end of file +cmake --build ../cmake_build --target all diff --git a/build/cmake/build.pc.bash b/build/cmake/build.pc.bash index 69356d12a..b8f1398fd 100755 --- a/build/cmake/build.pc.bash +++ b/build/cmake/build.pc.bash @@ -10,7 +10,7 @@ #export LINK_DIRS=/usr/local/lib # Link directories for cmake #export INCLUDES_DIRS="/usr/include;/usr/local/include" # directories for INCLUDES cmake variable (cmake uses semicolons as separators) #export LIBRARY_LINKS="-llapack;-lnetcdff;-lnetcdf" # list of library links -- LAPACK builds -#export FLAGS_OPT="-fuse-linker-plugin" # optional compiler flags -- LAPACK builds +#export FLAGS_OPT="-flto=1;-fuse-linker-plugin" # optional compiler flags -- LAPACK builds # PC Example using Ubuntu: Intel oneMKL builds (see https://www.intel.com/content/www/us/en/developer/tools/oneapi/onemkl-link-line-advisor.html) #oneAPI_dir=/opt/intel/oneapi # Intel oneAPI main directory @@ -19,7 +19,7 @@ #export LINK_DIRS=/usr/local/lib # Link directories for cmake #export INCLUDES_DIRS="/usr/include;/usr/local/include" # directories for INCLUDES cmake variable (cmake uses semicolons as separators) #export LIBRARY_LINKS="-lnetcdff;-lnetcdf;-Wl,--start-group ${MKLROOT}/lib/intel64/libmkl_gf_lp64.a ${MKLROOT}/lib/intel64/libmkl_gnu_thread.a ${MKLROOT}/lib/intel64/libmkl_core.a -Wl,--end-group;-lgomp;-lpthread;-lm;-ldl" # list of library links -- Intel oneMKL builds -#export FLAGS_OPT="--m64;-I"${MKLROOT}/include;-fuse-linker-plugin" # optional compiler flags -- Intel oneMKL builds +#export FLAGS_OPT="--m64;-I"${MKLROOT}/include;-flto=1;-fuse-linker-plugin" # optional compiler flags -- Intel oneMKL builds # CMake Commands (build type controlled using DCMAKE_BUILD_TYPE) cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=BE diff --git a/build/cmake/build_actors.cluster.bash b/build/cmake/build_actors.cluster.bash index d4046af8e..b8b7ce4d7 100755 --- a/build/cmake/build_actors.cluster.bash +++ b/build/cmake/build_actors.cluster.bash @@ -10,6 +10,8 @@ module load netcdf-fortran/4.5.2 # for Actors module load caf +export FLAGS_OPT="-flto=1;-fuse-linker-plugin" + cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials_Actors_Cluster cmake --build ../cmake_build --target all diff --git a/build/cmake/build_actors.mac.bash b/build/cmake/build_actors.mac.bash index bc43513be..14c8353b7 100755 --- a/build/cmake/build_actors.mac.bash +++ b/build/cmake/build_actors.mac.bash @@ -2,6 +2,13 @@ # build on Mac, from cmake directory run this as ./build_actors.mac.bash +# Mac Example using MacPorts: +export FC=gfortran # Fortran compiler family +export LINK_DIRS=/opt/local/lib # Link directories for cmake +export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) +export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) +#export FLAGS_OPT="-flto=1" # -flto=1 is slow to compile, but might want to use + cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials_Actors cmake --build ../cmake_build --target all diff --git a/build/cmake/build_ngen.cluster.bash b/build/cmake/build_ngen.cluster.bash index 672a29e30..b081f6861 100755 --- a/build/cmake/build_ngen.cluster.bash +++ b/build/cmake/build_ngen.cluster.bash @@ -7,6 +7,8 @@ module load gcc/9.3.0 module load openblas/0.3.17 module load netcdf-fortran/4.5.2 +export FLAGS_OPT="-flto=1;-fuse-linker-plugin" + # for NexGen module load boost module load udunits/2.2.28 diff --git a/build/cmake/build_ngen.mac.bash b/build/cmake/build_ngen.mac.bash index 87cc77414..457c2777e 100755 --- a/build/cmake/build_ngen.mac.bash +++ b/build/cmake/build_ngen.mac.bash @@ -1,6 +1,12 @@ #!/bin/bash # build nextgen on Mac, from ngen directory put this one directory up and run this as ../build_ngen.mac.bash +# Mac Example using MacPorts: +export FC=gfortran # Fortran compiler family +export LINK_DIRS=/opt/local/lib # Link directories for cmake +export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) +export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) +#export FLAGS_OPT="-flto=1" # -flto=1 is slow to compile, but might want to use cmake -B extern/iso_c_fortran_bmi/cmake_build -S extern/iso_c_fortran_bmi cmake --build extern/iso_c_fortran_bmi/cmake_build --target all diff --git a/build/source/netcdf/read_icond.f90 b/build/source/netcdf/read_icond.f90 index 07e6eff06..43a4a686f 100644 --- a/build/source/netcdf/read_icond.f90 +++ b/build/source/netcdf/read_icond.f90 @@ -315,9 +315,9 @@ subroutine read_icond(iconFile, & ! intent(in): name of progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarSnowAlbedo)%dat(1) = mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%albedoMax)%dat(1) endif - ! make sure canopy water is positive + ! make sure canopy water is positive, otherwise add liquid water to canopy and make total water consistent later if( progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyWat)%dat(1) < 0.0001_rkind)then - progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyWat)%dat(1) = 0.0001_rkind + progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyLiq)%dat(1) = 0.0001_rkind endif ! initialize the spectral albedo From d7199bb40321b64f3ca11cee43d61c9e82e4804c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 15 Aug 2023 14:29:30 +0900 Subject: [PATCH 0856/1472] added comments about infeasible --- build/source/engine/summaSolve4ida.f90 | 2 +- build/source/engine/summaSolve4kinsol.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 3df516b92..0ec77b10a 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -484,7 +484,7 @@ subroutine summaSolve4ida( & ! output: error control err,cmessage) ! intent(out): error control - ! early return for non-feasible solutions + ! early return for non-feasible solutions, right now will just fail if goes infeasible if(.not.feasible)then idaSucceeds = .false. message=trim(message)//trim(cmessage)//'non-feasible' ! err=0 is already set, could make this a warning and reduce the data window time in varSubStep diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index d48f977dd..8a5bea193 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -351,7 +351,7 @@ subroutine summaSolve4kinsol(& message=trim(message)//trim(cmessage) if(retvalr==-6) err = -20 ! max iterations failure, exit and reduce the data window time in varSubStep else - ! check the feasibility of the solution + ! check the feasibility of the solution, imposeConstraints should keep it from going infeasible feasible=.true. call checkFeas(& ! input From b120751e9ec1a0882e385d580c16fe53186e4c40 Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Tue, 15 Aug 2023 08:28:54 -0600 Subject: [PATCH 0857/1472] Add model parameters for adjusting state variable abs and rel tolerances --- build/source/dshare/get_ixname.f90 | 14 ++++++++++ build/source/dshare/popMetadat.f90 | 14 ++++++++++ build/source/dshare/var_lookup.f90 | 21 ++++++++++++--- build/source/engine/read_pinit.f90 | 42 ++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 3 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 79af0c0bf..b66245cbf 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -392,6 +392,20 @@ function get_ixparam(varName) case('absConvTol_aquifr' ); get_ixparam = iLookPARAM%absConvTol_aquifr ! absolute convergence tolerance for aquifer storage (m) numrec case('relErrTol_ida' ); get_ixparam = iLookPARAM%relErrTol_ida ! relative error tolerance for ida case('absErrTol_ida' ); get_ixparam = iLookPARAM%absErrTol_ida ! absolute error tolerance for ida + case('relTolTempCas' ); get_ixparam = iLookPARAM%relTolTempCas ! relative error tolerance for canopy temperature state variable + case('absTolTempCas' ); get_ixparam = iLookPARAM%absTolTempCas ! absolute error tolerance for canopy temperature state variable + case('relTolTempVeg' ); get_ixparam = iLookPARAM%relTolTempVeg ! relative error tolerance for vegitation temp state var + case('absTolTempVeg' ); get_ixparam = iLookPARAM%absTolTempVeg ! absolute error tolerance for vegitation temp state var + case('relTolWatVeg' ); get_ixparam = iLookPARAM%relTolWatVeg ! relative error tolerance for vegitation hydrology + case('absTolWatVeg' ); get_ixparam = iLookPARAM%absTolWatVeg ! absolute error tolerance for vegitation hydrology + case('relTolTempSoilSnow' ); get_ixparam = iLookPARAM%relTolTempSoilSnow ! relative error tolerance for snow+soil energy + case('absTolTempSoilSnow' ); get_ixparam = iLookPARAM%absTolTempSoilSnow ! absolute error tolerance for snow+soil energy + case('relTolWatSnow' ); get_ixparam = iLookPARAM%relTolWatSnow ! relative error tolerance for snow hydrology + case('absTolWatSnow' ); get_ixparam = iLookPARAM%absTolWatSnow ! absolute error tolerance for snow hydrology + case('relTolMatric' ); get_ixparam = iLookPARAM%relTolMatric ! relative error tolerance for matric head + case('absTolMatric' ); get_ixparam = iLookPARAM%absTolMatric ! absolute error tolerance for matric head + case('relTolAquifr' ); get_ixparam = iLookPARAM%relTolAquifr ! relative error tolerance for aquifer hydrology + case('absTolAquifr' ); get_ixparam = iLookPARAM%absTolAquifr ! absolute error tolerance for aquifer hydrology case('zmin' ); get_ixparam = iLookPARAM%zmin ! minimum layer depth (m) case('zmax' ); get_ixparam = iLookPARAM%zmax ! maximum layer depth (m) case('zminLayer1' ); get_ixparam = iLookPARAM%zminLayer1 ! minimum layer depth for the 1st (top) layer (m) diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 202a6a242..69698bc89 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -269,6 +269,20 @@ subroutine popMetadat(err,message) mpar_meta(iLookPARAM%absConvTol_aquifr) = var_info('absConvTol_aquifr' , 'absolute convergence tolerance for aquifer storage numrec' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%relErrTol_ida) = var_info('relErrTol_ida' , 'relative error tolerance for ida' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%absErrTol_ida) = var_info('absErrTol_ida' , 'absolute error tolerance for ida' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relTolTempCas) = var_info('relTolTempCas' , 'relative error tolerance for canopy temperature state variable' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absTolTempCas) = var_info('absTolTempCas' , 'absolute error tolerance for canopy temperature state variable' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relTolTempVeg) = var_info('relTolTempVeg' , 'relative error tolerance for vegitation temp state var' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absTolTempVeg) = var_info('absTolTempVeg' , 'absolute error tolerance for vegitation temp state var' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relTolWatVeg) = var_info('relTolWatVeg' , 'absolute error tolerance for vegitation hydrology' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absTolWatVeg) = var_info('absTolWatVeg' , 'relative error tolerance for vegitation hydrology' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relTolTempSoilSnow) = var_info('relTolTempSoilSnow' , 'relative error tolerance for snow+soil energy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absTolTempSoilSnow) = var_info('absTolTempSoilSnow' , 'absolute error tolerance for snow+soil energy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relTolWatSnow) = var_info('relTolWatSnow' , 'relative error tolerance for snow hydrology' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absTolWatSnow) = var_info('absTolWatSnow' , 'absolute error tolerance for snow hydrology' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relTolMatric) = var_info('relTolMatric' , 'relative error tolerance for matric head' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absTolMatric) = var_info('absTolMatric' , 'absolute error tolerance for matric head' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relTolAquifr) = var_info('relTolAquifr' , 'relative error tolerance for aquifer hydrology' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absTolAquifr) = var_info('absTolAquifr' , 'absolute error tolerance for aquifer hydrology' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%zmin) = var_info('zmin' , 'minimum layer depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%zmax) = var_info('zmax' , 'maximum layer depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%zminLayer1) = var_info('zminLayer1' , 'minimum layer depth for the 1st (top) layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index d4d4bf66b..d7a230cb4 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -294,8 +294,22 @@ MODULE var_lookup integer(i4b) :: absConvTol_energy = integerMissing ! absolute convergence tolerance for energy (J m-3) integer(i4b) :: relConvTol_aquifr = integerMissing ! relative convergence tolerance for aquifer storage (-) integer(i4b) :: absConvTol_aquifr = integerMissing ! absolute convergence tolerance for aquifer storage (J m-3) - integer(i4b) :: relErrTol_ida = integerMissing ! relative error tolerance for ida' - integer(i4b) :: absErrTol_ida = integerMissing ! absolute error tolerance for ida' + integer(i4b) :: relErrTol_ida = integerMissing ! relative error tolerance for ida + integer(i4b) :: absErrTol_ida = integerMissing ! absolute error tolerance for ida + integer(i4b) :: relTolTempCas = integerMissing ! relative error tolerance for canopy temperature state variable + integer(i4b) :: absTolTempCas = integerMissing ! absolute error tolerance for canopy temperature state variable + integer(i4b) :: relTolTempVeg = integerMissing ! relative error tolerance for vegitation temp state var + integer(i4b) :: absTolTempVeg = integerMissing ! absolute error tolerance for vegitation temp state var + integer(i4b) :: relTolWatVeg = integerMissing ! relative error tolerance for vegitation hydrology + integer(i4b) :: absTolWatVeg = integerMissing ! absolute error tolerance for vegitation hydrology + integer(i4b) :: relTolTempSoilSnow = integerMissing ! relative error tolerance for snow+soil energy + integer(i4b) :: absTolTempSoilSnow = integerMissing ! absolute error tolerance for snow+soil energy + integer(i4b) :: relTolWatSnow = integerMissing ! relative error tolerance for snow hydrology + integer(i4b) :: absTolWatSnow = integerMissing ! absolute error tolerance for snow hydrology + integer(i4b) :: relTolMatric = integerMissing ! relative error tolerance for matric head + integer(i4b) :: absTolMatric = integerMissing ! absolute error tolerance for matric head + integer(i4b) :: relTolAquifr = integerMissing ! relative error tolerance for aquifer hydrology + integer(i4b) :: absTolAquifr = integerMissing ! absolute error tolerance for aquifer hydrology integer(i4b) :: zmin = integerMissing ! minimum layer depth (m) integer(i4b) :: zmax = integerMissing ! maximum layer depth (m) integer(i4b) :: zminLayer1 = integerMissing ! minimum layer depth for the 1st (top) layer (m) @@ -858,7 +872,8 @@ MODULE var_lookup 131,132,133,134,135,136,137,138,139,140,& 141,142,143,144,145,146,147,148,149,150,& 151,152,153,154,155,156,157,158,159,160,& - 161,162) + 161,162,163,164,165,166,167,168,169,170,& + 171,172,173,174,175,176) ! named variables: model prognostic (state) variables type(iLook_prog), public,parameter :: iLookPROG =iLook_prog ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& diff --git a/build/source/engine/read_pinit.f90 b/build/source/engine/read_pinit.f90 index 34be9581c..dd79308d5 100644 --- a/build/source/engine/read_pinit.f90 +++ b/build/source/engine/read_pinit.f90 @@ -141,6 +141,48 @@ subroutine read_pinit(filenm,isLocal,mpar_meta,parFallback,err,message) if (parFallback(iLookPARAM%absErrTol_ida)%default_val < 0.99_rkind*realMissing) then parFallback(iLookPARAM%absErrTol_ida)%default_val = 1.e-6_rkind end if + if (parFallback(iLookPARAM%relTolTempCas)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%relTolTempCas)%default_val = 1.e-6_rkind + end if + if (parFallback(iLookPARAM%absTolTempCas)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%absTolTempCas)%default_val = 1.e-6_rkind + end if + if (parFallback(iLookPARAM%relTolTempVeg)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%relTolTempVeg)%default_val = 1.e-6_rkind + end if + if (parFallback(iLookPARAM%absTolTempVeg)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%absTolTempVeg)%default_val = 1.e-6_rkind + end if + if (parFallback(iLookPARAM%relTolWatVeg)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%relTolWatVeg)%default_val = 1.e-6_rkind + end if + if (parFallback(iLookPARAM%absTolWatVeg)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%absTolWatVeg)%default_val = 1.e-6_rkind + end if + if (parFallback(iLookPARAM%relTolTempSoilSnow)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%relTolTempSoilSnow)%default_val = 1.e-6_rkind + end if + if (parFallback(iLookPARAM%absTolTempSoilSnow)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%absTolTempSoilSnow)%default_val = 1.e-6_rkind + end if + if (parFallback(iLookPARAM%relTolWatSnow)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%relTolWatSnow)%default_val = 1.e-6_rkind + end if + if (parFallback(iLookPARAM%absTolWatSnow)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%absTolWatSnow)%default_val = 1.e-6_rkind + end if + if (parFallback(iLookPARAM%relTolMatric)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%relTolMatric)%default_val = 1.e-6_rkind + end if + if (parFallback(iLookPARAM%absTolMatric)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%absTolMatric)%default_val = 1.e-6_rkind + end if + if (parFallback(iLookPARAM%relTolAquifr)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%relTolAquifr)%default_val = 1.e-6_rkind + end if + if (parFallback(iLookPARAM%absTolAquifr)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%absTolAquifr)%default_val = 1.e-6_rkind + end if end if ! check we have populated all variables From d13944b5429c50aa73a57786b49b6183cffbf35a Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Tue, 15 Aug 2023 08:41:49 -0600 Subject: [PATCH 0858/1472] Add state variables tolerance parameters setting tolerance Tol4ida now uses the state variable tolerance set by the mpar structure --- build/source/engine/tol4ida.f90 | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/build/source/engine/tol4ida.f90 b/build/source/engine/tol4ida.f90 index fb1752001..03037b9cf 100644 --- a/build/source/engine/tol4ida.f90 +++ b/build/source/engine/tol4ida.f90 @@ -205,20 +205,20 @@ subroutine popTol4ida(& ! initialize error control err=0; message='popTol4ida/' - absTolTempCas = mpar_data%var(iLookPARAM%absErrTol_ida)%dat(1) - relTolTempCas = mpar_data%var(iLookPARAM%relErrTol_ida)%dat(1) - absTolTempVeg = mpar_data%var(iLookPARAM%absErrTol_ida)%dat(1) - relTolTempVeg = mpar_data%var(iLookPARAM%relErrTol_ida)%dat(1) - absTolWatVeg = mpar_data%var(iLookPARAM%absErrTol_ida)%dat(1) - relTolWatVeg = mpar_data%var(iLookPARAM%relErrTol_ida)%dat(1) - absTolTempSoilSnow = mpar_data%var(iLookPARAM%absErrTol_ida)%dat(1) - relTolTempSoilSnow = mpar_data%var(iLookPARAM%relErrTol_ida)%dat(1) - absTolWatSnow = mpar_data%var(iLookPARAM%absErrTol_ida)%dat(1) - relTolWatSnow = mpar_data%var(iLookPARAM%relErrTol_ida)%dat(1) - absTolMatric = mpar_data%var(iLookPARAM%absErrTol_ida)%dat(1) - relTolMatric = mpar_data%var(iLookPARAM%relErrTol_ida)%dat(1) - absTolAquifr = mpar_data%var(iLookPARAM%absErrTol_ida)%dat(1) - relTolAquifr = mpar_data%var(iLookPARAM%relErrTol_ida)%dat(1) + absTolTempCas = mpar_data%var(iLookPARAM%absErrTol_ida)%dat(1) + relTolTempCas = mpar_data%var(iLookPARAM%relTolTempCas)%dat(1) + absTolTempVeg = mpar_data%var(iLookPARAM%absTolTempVeg)%dat(1) + relTolTempVeg = mpar_data%var(iLookPARAM%relTolTempVeg)%dat(1) + absTolWatVeg = mpar_data%var(iLookPARAM%absTolWatVeg)%dat(1) + relTolWatVeg = mpar_data%var(iLookPARAM%relTolWatVeg)%dat(1) + absTolTempSoilSnow = mpar_data%var(iLookPARAM%absTolTempSoilSnow)%dat(1) + relTolTempSoilSnow = mpar_data%var(iLookPARAM%relTolTempSoilSnow)%dat(1) + absTolWatSnow = mpar_data%var(iLookPARAM%absTolWatSnow)%dat(1) + relTolWatSnow = mpar_data%var(iLookPARAM%relTolWatSnow)%dat(1) + absTolMatric = mpar_data%var(iLookPARAM%absTolMatric)%dat(1) + relTolMatric = mpar_data%var(iLookPARAM%relTolMatric)%dat(1) + absTolAquifr = mpar_data%var(iLookPARAM%absTolAquifr)%dat(1) + relTolAquifr = mpar_data%var(iLookPARAM%relTolAquifr)%dat(1) ! ----- ! * initialize state vectors... From f5bb1b5caa70ac44d399ac034e677fc2e6d1188c Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Tue, 15 Aug 2023 08:48:46 -0600 Subject: [PATCH 0859/1472] Remove old actor compilation lines that were commented out --- build/cmake/CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index cdb0276e7..39e5134a9 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -329,8 +329,6 @@ set(PRELIM ${ENGINE_DIR}/sunGeomtry.f90) set(PRELIM_NOT_ACTORS ${ENGINE_DIR}/read_param.f90) -# set(PRELIM_ACTORS -# ${SUB_ENGINE_DIR}/alloc_fileAccess.f90) # Model run support modules set(MODRUN @@ -491,7 +489,6 @@ set(SUMMA_ALL if(CMAKE_BUILD_TYPE MATCHES Actors) set(SUMMA_ALL ${SUMMA_ALL} - # ${PRELIM_ACTORS} ${FILE_ACCESS_INTERFACE} ${JOB_INTERFACE} ${GRU_INTERFACE} From 6e3d48d80c9c432a6a00b8839112fdc48365e73b Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Tue, 15 Aug 2023 08:50:09 -0600 Subject: [PATCH 0860/1472] Add one more tolerance that was forgotten --- build/source/engine/tol4ida.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/tol4ida.f90 b/build/source/engine/tol4ida.f90 index 03037b9cf..c3cc7fc7c 100644 --- a/build/source/engine/tol4ida.f90 +++ b/build/source/engine/tol4ida.f90 @@ -205,7 +205,7 @@ subroutine popTol4ida(& ! initialize error control err=0; message='popTol4ida/' - absTolTempCas = mpar_data%var(iLookPARAM%absErrTol_ida)%dat(1) + absTolTempCas = mpar_data%var(iLookPARAM%absTolTempCas)%dat(1) relTolTempCas = mpar_data%var(iLookPARAM%relTolTempCas)%dat(1) absTolTempVeg = mpar_data%var(iLookPARAM%absTolTempVeg)%dat(1) relTolTempVeg = mpar_data%var(iLookPARAM%relTolTempVeg)%dat(1) From 36cb23f2fb471909887a50d836a1a1ec5d4e7dc9 Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Tue, 15 Aug 2023 09:20:43 -0600 Subject: [PATCH 0861/1472] remove relErrTol and absErrTol in favour of per state variable changes. --- build/source/dshare/get_ixname.f90 | 2 -- build/source/dshare/popMetadat.f90 | 2 -- build/source/dshare/var_lookup.f90 | 2 -- build/source/engine/read_pinit.f90 | 6 ------ 4 files changed, 12 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index b66245cbf..aef630e4a 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -390,8 +390,6 @@ function get_ixparam(varName) case('absConvTol_energy' ); get_ixparam = iLookPARAM%absConvTol_energy ! absolute convergence tolerance for energy (J m-3) numrec case('relConvTol_aquifr' ); get_ixparam = iLookPARAM%relConvTol_aquifr ! relative convergence tolerance for aquifer storage (-) numrec case('absConvTol_aquifr' ); get_ixparam = iLookPARAM%absConvTol_aquifr ! absolute convergence tolerance for aquifer storage (m) numrec - case('relErrTol_ida' ); get_ixparam = iLookPARAM%relErrTol_ida ! relative error tolerance for ida - case('absErrTol_ida' ); get_ixparam = iLookPARAM%absErrTol_ida ! absolute error tolerance for ida case('relTolTempCas' ); get_ixparam = iLookPARAM%relTolTempCas ! relative error tolerance for canopy temperature state variable case('absTolTempCas' ); get_ixparam = iLookPARAM%absTolTempCas ! absolute error tolerance for canopy temperature state variable case('relTolTempVeg' ); get_ixparam = iLookPARAM%relTolTempVeg ! relative error tolerance for vegitation temp state var diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 69698bc89..413efd860 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -267,8 +267,6 @@ subroutine popMetadat(err,message) mpar_meta(iLookPARAM%absConvTol_energy) = var_info('absConvTol_energy' , 'absolute convergence tolerance for energy numrec' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%relConvTol_aquifr) = var_info('relConvTol_aquifr' , 'relative convergence tolerance for aquifer storage numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%absConvTol_aquifr) = var_info('absConvTol_aquifr' , 'absolute convergence tolerance for aquifer storage numrec' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relErrTol_ida) = var_info('relErrTol_ida' , 'relative error tolerance for ida' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absErrTol_ida) = var_info('absErrTol_ida' , 'absolute error tolerance for ida' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%relTolTempCas) = var_info('relTolTempCas' , 'relative error tolerance for canopy temperature state variable' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%absTolTempCas) = var_info('absTolTempCas' , 'absolute error tolerance for canopy temperature state variable' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%relTolTempVeg) = var_info('relTolTempVeg' , 'relative error tolerance for vegitation temp state var' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index d7a230cb4..b719585c5 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -294,8 +294,6 @@ MODULE var_lookup integer(i4b) :: absConvTol_energy = integerMissing ! absolute convergence tolerance for energy (J m-3) integer(i4b) :: relConvTol_aquifr = integerMissing ! relative convergence tolerance for aquifer storage (-) integer(i4b) :: absConvTol_aquifr = integerMissing ! absolute convergence tolerance for aquifer storage (J m-3) - integer(i4b) :: relErrTol_ida = integerMissing ! relative error tolerance for ida - integer(i4b) :: absErrTol_ida = integerMissing ! absolute error tolerance for ida integer(i4b) :: relTolTempCas = integerMissing ! relative error tolerance for canopy temperature state variable integer(i4b) :: absTolTempCas = integerMissing ! absolute error tolerance for canopy temperature state variable integer(i4b) :: relTolTempVeg = integerMissing ! relative error tolerance for vegitation temp state var diff --git a/build/source/engine/read_pinit.f90 b/build/source/engine/read_pinit.f90 index dd79308d5..7231492cb 100644 --- a/build/source/engine/read_pinit.f90 +++ b/build/source/engine/read_pinit.f90 @@ -135,12 +135,6 @@ subroutine read_pinit(filenm,isLocal,mpar_meta,parFallback,err,message) if (parFallback(iLookPARAM%be_steps)%default_val < 0.99_rkind*realMissing) then parFallback(iLookPARAM%be_steps)%default_val = 1._rkind end if - if (parFallback(iLookPARAM%relErrTol_ida)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%relErrTol_ida)%default_val = 1.e-6_rkind - end if - if (parFallback(iLookPARAM%absErrTol_ida)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%absErrTol_ida)%default_val = 1.e-6_rkind - end if if (parFallback(iLookPARAM%relTolTempCas)%default_val < 0.99_rkind*realMissing) then parFallback(iLookPARAM%relTolTempCas)%default_val = 1.e-6_rkind end if From 63689dc51636bea2f9813d625780752f7f7361f8 Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Tue, 15 Aug 2023 09:26:33 -0600 Subject: [PATCH 0862/1472] Fixed issue in var_lookup: Removed absErrTol and relErrTol but did not adust the iLook_param structure. I have now removed the two extra indices. --- build/source/dshare/var_lookup.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index b719585c5..2c5630510 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -871,7 +871,7 @@ MODULE var_lookup 141,142,143,144,145,146,147,148,149,150,& 151,152,153,154,155,156,157,158,159,160,& 161,162,163,164,165,166,167,168,169,170,& - 171,172,173,174,175,176) + 171,172,173,174) ! named variables: model prognostic (state) variables type(iLook_prog), public,parameter :: iLookPROG =iLook_prog ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& From e2e9b55cf85c19793f8e39261be0f20ea6cc290f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 16 Aug 2023 10:31:19 +0900 Subject: [PATCH 0863/1472] adding default parameter update --- case_study/base_settings/localParamInfo.txt | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/case_study/base_settings/localParamInfo.txt b/case_study/base_settings/localParamInfo.txt index 8a216667e..e5a3db595 100644 --- a/case_study/base_settings/localParamInfo.txt +++ b/case_study/base_settings/localParamInfo.txt @@ -206,8 +206,20 @@ relConvTol_energy | 1.0d-2 | 1.0d-5 | 1.0d-1 absConvTol_energy | 1.0d-0 | 1.0d-2 | 1.0d+1 relConvTol_aquifr | 1.0d-0 | 1.0d-2 | 1.0d+1 absConvTol_aquifr | 1.0d-5 | 1.0d-5 | 1.0d-1 -relErrTol_ida | 1.0d-6 | 1.0d-10| 1.0d-2 -absErrTol_ida | 1.0d-6 | 1.0d-10| 1.0d-2 +relTolTempCas | 1.0d-6 | 1.0d-10| 1.0d-1 +absTolTempCas | 1.0d-6 | 1.0d-10| 1.0d-1 +relTolTempVeg | 1.0d-6 | 1.0d-10| 1.0d-1 +absTolTempVeg | 1.0d-6 | 1.0d-10| 1.0d-1 +relTolWatVeg | 1.0d-6 | 1.0d-10| 1.0d-1 +absTolWatVeg | 1.0d-6 | 1.0d-10| 1.0d-1 +relTolTempSoilSnow | 1.0d-6 | 1.0d-10| 1.0d-1 +absTolTempSoilSnow | 1.0d-6 | 1.0d-10| 1.0d-1 +relTolWatSnow | 1.0d-6 | 1.0d-10| 1.0d-1 +absTolWatSnow | 1.0d-6 | 1.0d-10| 1.0d-1 +relTolMatric | 1.0d-6 | 1.0d-10| 1.0d-1 +absTolMatric | 1.0d-6 | 1.0d-10| 1.0d-1 +relTolAquifr | 1.0d-6 | 1.0d-10| 1.0d-1 +absTolAquifr | 1.0d-6 | 1.0d-10| 1.0d-1 zmin | 0.0100 | 0.0050 | 0.1000 zmax | 0.0500 | 0.0100 | 0.5000 ! --- From 96184117d1347de9facedbc34259a791a248ac32 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 16 Aug 2023 10:55:30 +0900 Subject: [PATCH 0864/1472] still fixing read_icond canopy water logic, should be good now --- build/source/engine/check_icond.f90 | 1 - build/source/netcdf/read_icond.f90 | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/build/source/engine/check_icond.f90 b/build/source/engine/check_icond.f90 index d8a506ef7..d99114cb0 100644 --- a/build/source/engine/check_icond.f90 +++ b/build/source/engine/check_icond.f90 @@ -129,7 +129,6 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU ! state variables in the vegetation canopy scalarCanopyTemp => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyTemp)%dat(1) , & ! canopy temperature scalarCanopyIce => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyIce)%dat(1) , & ! mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiq => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyLiq)%dat(1) , & ! mass of liquid water on the vegetation canopy (kg m-2) ! state variables in the snow+soil domain mLayerTemp => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerTemp)%dat , & ! temperature (K) mLayerVolFracLiq => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracLiq)%dat , & ! volumetric fraction of liquid water in each snow layer (-) diff --git a/build/source/netcdf/read_icond.f90 b/build/source/netcdf/read_icond.f90 index 43a4a686f..d7634b8bb 100644 --- a/build/source/netcdf/read_icond.f90 +++ b/build/source/netcdf/read_icond.f90 @@ -315,8 +315,8 @@ subroutine read_icond(iconFile, & ! intent(in): name of progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarSnowAlbedo)%dat(1) = mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%albedoMax)%dat(1) endif - ! make sure canopy water is positive, otherwise add liquid water to canopy and make total water consistent later - if( progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyWat)%dat(1) < 0.0001_rkind)then + ! make sure canopy ice + liq is positive, otherwise add liquid water to canopy and make total water consistent later + if( (progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyLiq)%dat(1) + progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyIce)%dat(1)) < 0.0001_rkind)then progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyLiq)%dat(1) = 0.0001_rkind endif From 3ab18eeabb416516fae945d033cde1c631512023 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 16 Aug 2023 15:42:16 +0900 Subject: [PATCH 0865/1472] comments (and spaces) explaining that the restrictive zMaxTempIncrement is what causes all the weirdness on step 1 in summa BE (numrec, itertive) --- build/source/engine/eval8summa.f90 | 20 +++++++++---------- build/source/engine/summaSolve4numrec.f90 | 24 +++++++++++------------ 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 8e4142cdd..8a3ac81d2 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -839,8 +839,8 @@ subroutine imposeConstraints(indx_data, prog_data, mpar_data, stateVec, stateVec real(rkind) :: xPsi00 ! matric head after applying the iteration increment (m) real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) real(rkind) :: critDiff ! temperature difference from critical (K) - real(rkind),parameter :: epsT=1.e-3_rkind ! small interval above/below critical (K), doesn't work well at 1e-7 - real(rkind),parameter :: zMaxTempIncrement=1._rkind ! maximum temperature increment (K) + real(rkind),parameter :: epsT=1.e-3_rkind ! small interval above/below critical (K), doesn't work well at usual 1e-7 + real(rkind),parameter :: zMaxTempIncrement=1._rkind ! maximum temperature increment (K),NOTE: this can cause problems especially from a cold start when we are far from the solution ! indices of model state variables integer(i4b) :: iState ! index of state within a specific variable type integer(i4b) :: ixNrg,ixLiq ! index of energy and mass state variables in full state vector @@ -860,11 +860,11 @@ subroutine imposeConstraints(indx_data, prog_data, mpar_data, stateVec, stateVec ixMassOnly => indx_data%var(iLookINDEX%ixMassOnly)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for canopy storage states ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] named variables defining the states in the subset ! indices for specific state variables - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow-soil subdomain - ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow-soil subdomain + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow-soil subdomain + ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow-soil subdomain ! vector of energy indices for the snow and soil domains ! NOTE: states not in the subset are equal to integerMissing ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain @@ -992,9 +992,9 @@ subroutine imposeConstraints(indx_data, prog_data, mpar_data, stateVec, stateVec ! check if new value of storage will be negative if(stateVecPrev(ixVegHyd)+xInc(ixVegHyd) < 0._rkind)then ! scale iteration increment - cInc = -0.5_rkind*stateVecPrev(ixVegHyd) ! constrained iteration increment (K) -- simplified bi-section - xIncFactor = cInc/xInc(ixVegHyd) ! scaling factor for the iteration increment (-) - xInc = xIncFactor*xInc ! new iteration increment + cInc = -0.5_rkind*stateVecPrev(ixVegHyd) ! constrained iteration increment (K) -- simplified bi-section + xIncFactor = cInc/xInc(ixVegHyd) ! scaling factor for the iteration increment (-) + xInc = xIncFactor*xInc ! new iteration increment end if endif ! (if the state variable for canopy water is included within the state subset) diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index f326da3b0..4b8666371 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -379,7 +379,7 @@ subroutine lineSearchRefinement(doLineSearch,stateVecTrial,newtStepScaled,aJacSc logical(lgt) :: feasible ! flag to denote the feasibility of the solution integer(i4b) :: iLine ! line search index integer(i4b),parameter :: maxLineSearch=5 ! maximum number of backtracks - real(rkind),parameter :: alpha=1.e-4_rkind ! check on gradient + real(rkind),parameter :: alpha=1.e-4_rkind ! check on gradient real(rkind) :: xLambda ! backtrack magnitude real(rkind) :: xLambdaTemp ! temporary backtrack magnitude real(rkind) :: slopeInit ! initial slope @@ -1124,7 +1124,7 @@ subroutine imposeConstraints(stateVecTrial,xInc,err,message) real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) real(rkind) :: critDiff ! temperature difference from critical (K) real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical (K) - real(rkind),parameter :: zMaxTempIncrement=1._rkind ! maximum temperature increment (K) + real(rkind),parameter :: zMaxTempIncrement=1._rkind ! maximum temperature increment (K), NOTE: this can cause problems especially from a cold start when we are far from the solution ! indices of model state variables integer(i4b) :: iState ! index of state within a specific variable type integer(i4b) :: ixNrg,ixLiq ! index of energy and mass state variables in full state vector @@ -1139,11 +1139,11 @@ subroutine imposeConstraints(stateVecTrial,xInc,err,message) ixMassOnly => indx_data%var(iLookINDEX%ixMassOnly)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for canopy storage states ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] named variables defining the states in the subset ! indices for specific state variables - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow-soil subdomain - ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow-soil subdomain + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow-soil subdomain + ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow-soil subdomain ! vector of energy indices for the snow and soil domains ! NOTE: states not in the subset are equal to integerMissing ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain @@ -1218,9 +1218,9 @@ subroutine imposeConstraints(stateVecTrial,xInc,err,message) ! check if new value of storage will be negative if(stateVecTrial(ixVegHyd)+xInc(ixVegHyd) < 0._rkind)then ! scale iteration increment - cInc = -0.5_rkind*stateVecTrial(ixVegHyd) ! constrained iteration increment (K) -- simplified bi-section - xIncFactor = cInc/xInc(ixVegHyd) ! scaling factor for the iteration increment (-) - xInc = xIncFactor*xInc ! new iteration increment + cInc = -0.5_rkind*stateVecTrial(ixVegHyd) ! constrained iteration increment (K) -- simplified bi-section + xIncFactor = cInc/xInc(ixVegHyd) ! scaling factor for the iteration increment (-) + xInc = xIncFactor*xInc ! new iteration increment end if endif ! if the state variable for canopy water is included within the state subset @@ -1239,8 +1239,8 @@ subroutine imposeConstraints(stateVecTrial,xInc,err,message) iState = ixSnowOnlyNrg(iLayer) if(stateVecTrial(iState) + xInc(iState) > Tfreeze)then ! scale iteration increment - cInc = 0.5_rkind*(Tfreeze - stateVecTrial(iState) ) ! constrained temperature increment (K) -- simplified bi-section - xIncFactor = cInc/xInc(iState) ! scaling factor for the iteration increment (-) + cInc = 0.5_rkind*(Tfreeze - stateVecTrial(iState) ) ! constrained temperature increment (K) -- simplified bi-section + xIncFactor = cInc/xInc(iState) ! scaling factor for the iteration increment (-) xInc = xIncFactor*xInc end if ! if snow temperature > freezing From c9336619f45bcff0e4068cc356ed8e402ddfb9a1 Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 12 May 2023 05:39:10 -0600 Subject: [PATCH 0866/1472] Refactoring performed on bigAquifer.f90, groundwatr.f90, snowLiqFlx.f90, soilLiqFlx.f90, and ssdNrgFlux.f90. --- build/cmake/build.pc.bash | 16 +- build/source/engine/groundwatr.f90 | 93 ++-- build/source/engine/snowLiqFlx.f90 | 4 +- build/source/engine/soilLiqFlx.f90 | 807 ++++++++++++++--------------- build/source/engine/ssdNrgFlux.f90 | 2 +- 5 files changed, 455 insertions(+), 467 deletions(-) diff --git a/build/cmake/build.pc.bash b/build/cmake/build.pc.bash index b8f1398fd..d85abf080 100755 --- a/build/cmake/build.pc.bash +++ b/build/cmake/build.pc.bash @@ -10,16 +10,16 @@ #export LINK_DIRS=/usr/local/lib # Link directories for cmake #export INCLUDES_DIRS="/usr/include;/usr/local/include" # directories for INCLUDES cmake variable (cmake uses semicolons as separators) #export LIBRARY_LINKS="-llapack;-lnetcdff;-lnetcdf" # list of library links -- LAPACK builds -#export FLAGS_OPT="-flto=1;-fuse-linker-plugin" # optional compiler flags -- LAPACK builds +#export FLAGS_OPT="-flto=1;-fuse-linker-plugin" # optional compiler flags -- LAPACK builds # PC Example using Ubuntu: Intel oneMKL builds (see https://www.intel.com/content/www/us/en/developer/tools/oneapi/onemkl-link-line-advisor.html) -#oneAPI_dir=/opt/intel/oneapi # Intel oneAPI main directory -#source $oneAPI_dir/setvars.sh # initialize environment variables for Intel oneAPI -#export FC=gfortran # Fortran compiler family -#export LINK_DIRS=/usr/local/lib # Link directories for cmake -#export INCLUDES_DIRS="/usr/include;/usr/local/include" # directories for INCLUDES cmake variable (cmake uses semicolons as separators) -#export LIBRARY_LINKS="-lnetcdff;-lnetcdf;-Wl,--start-group ${MKLROOT}/lib/intel64/libmkl_gf_lp64.a ${MKLROOT}/lib/intel64/libmkl_gnu_thread.a ${MKLROOT}/lib/intel64/libmkl_core.a -Wl,--end-group;-lgomp;-lpthread;-lm;-ldl" # list of library links -- Intel oneMKL builds -#export FLAGS_OPT="--m64;-I"${MKLROOT}/include;-flto=1;-fuse-linker-plugin" # optional compiler flags -- Intel oneMKL builds +#oneAPI_dir=/opt/intel/oneapi # Intel oneAPI main directory +#source $oneAPI_dir/setvars.sh # initialize environment variables for Intel oneAPI +#export FC=gfortran # Fortran compiler family +#export LINK_DIRS=/usr/local/lib # Link directories for cmake +#export INCLUDES_DIRS="/usr/include;/usr/local/include" # directories for INCLUDES cmake variable (cmake uses semicolons as separators) +#export LIBRARY_LINKS="-lnetcdff;-lnetcdf;-Wl,--start-group ${MKLROOT}/lib/intel64/libmkl_gf_lp64.a ${MKLROOT}/lib/intel64/libmkl_gnu_thread.a ${MKLROOT}/lib/intel64/libmkl_core.a -Wl,--end-group;-lgomp;-lpthread;-lm;-ldl" # list of library links -- Intel oneMKL builds +#export FLAGS_OPT="--m64;-I"${MKLROOT}/include;-flto=1;-fuse-linker-plugin" # optional compiler flags -- Intel oneMKL builds # CMake Commands (build type controlled using DCMAKE_BUILD_TYPE) cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=BE diff --git a/build/source/engine/groundwatr.f90 b/build/source/engine/groundwatr.f90 index bc5ca3972..71fa9fe5b 100644 --- a/build/source/engine/groundwatr.f90 +++ b/build/source/engine/groundwatr.f90 @@ -24,7 +24,7 @@ module groundwatr_module USE nrtype ! model constants -USE multiconst,only:iden_water ! density of water (kg m-3) +USE multiconst,only:iden_water ! density of water (kg m-3) ! derived types to define the data structures USE data_types,only:& @@ -40,9 +40,9 @@ module groundwatr_module ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: & - qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization - bigBucket, & ! a big bucket (lumped aquifer model) - noExplicit ! no explicit groundwater parameterization + qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization + bigBucket, & ! a big bucket (lumped aquifer model) + noExplicit ! no explicit groundwater parameterization ! privacy implicit none @@ -51,10 +51,9 @@ module groundwatr_module real(rkind),parameter :: verySmall=epsilon(1.0_rkind) ! a very small number (used to avoid divide by zero) real(rkind),parameter :: dx=1.e-8_rkind ! finite difference increment private -public::groundwatr +public :: groundwatr contains - ! ************************************************************************************************ ! public subroutine groundwatr: compute the groundwater sink term in Richards' equation ! ************************************************************************************************ @@ -115,10 +114,10 @@ subroutine groundwatr(& integer(i4b),intent(in) :: nLayers ! total number of layers logical(lgt),intent(in) :: getSatDepth ! logical flag to compute index of the lowest saturated layer ! input: state and diagnostic variables - real(rkind),intent(in) :: mLayerdTheta_dPsi(:) ! derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) - real(rkind),intent(in) :: mLayerMatricHeadLiq(:) ! matric head in each layer at the current iteration (m) - real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid water (-) - real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice (-) + real(rkind),intent(in) :: mLayerdTheta_dPsi(:) ! derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) + real(rkind),intent(in) :: mLayerMatricHeadLiq(:) ! matric head in each layer at the current iteration (m) + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid water (-) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice (-) ! input/output: data structures type(var_d),intent(in) :: attr_data ! spatial attributes type(var_dlength),intent(in) :: mpar_data ! model parameters @@ -127,8 +126,8 @@ subroutine groundwatr(& type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU ! output: baseflow integer(i4b),intent(inout) :: ixSaturation ! index of lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: mLayerBaseflow(:) ! baseflow from each soil layer (m s-1) - real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + real(rkind),intent(out) :: mLayerBaseflow(:) ! baseflow from each soil layer (m s-1) + real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -136,8 +135,8 @@ subroutine groundwatr(& ! * local variables ! --------------------------------------------------------------------------------------- ! general local variables - integer(i4b) :: iLayer ! index of soil layer - real(rkind),dimension(nSoil,nSoil) :: dBaseflow_dVolLiq ! derivative in the baseflow flux w.r.t. volumetric liquid water content (m s-1) + integer(i4b) :: iLayer ! index of soil layer + real(rkind),dimension(nSoil,nSoil) :: dBaseflow_dVolLiq ! derivative in the baseflow flux w.r.t. volumetric liquid water content (m s-1) ! *************************************************************************************** ! *************************************************************************************** ! initialize error control @@ -164,16 +163,16 @@ subroutine groundwatr(& ! ************************************************************************************************ ! get index of the lowest saturated layer - if(getSatDepth)then ! NOTE: only compute for the first flux call + if (getSatDepth) then ! NOTE: only compute for the first flux call ixSaturation = nSoil+1 ! unsaturated profile when ixSaturation>nSoil do iLayer=nSoil,1,-1 ! start at the lowest soil layer and work upwards to the top layer - if(mLayerVolFracLiq(iLayer) > fieldCapacity)then; ixSaturation = iLayer ! index of saturated layer -- keeps getting over-written as move upwards - else; exit; end if ! (only consider saturated layer at the bottom of the soil profile) + if (mLayerVolFracLiq(iLayer) > fieldCapacity) then; ixSaturation = iLayer ! index of saturated layer -- keeps getting over-written as move upwards + else; exit; end if ! only consider saturated layer at the bottom of the soil profile end do ! (looping through soil layers) end if ! check for an early return (no layers are "active") - if(ixSaturation > nSoil)then + if (ixSaturation > nSoil) then scalarExfiltration = 0._rkind ! exfiltration from the soil profile (m s-1) mLayerColumnOutflow(:) = 0._rkind ! column outflow from each soil layer (m3 s-1) mLayerBaseflow(:) = 0._rkind ! baseflow from each soil layer (m s-1) @@ -188,26 +187,26 @@ subroutine groundwatr(& ! use private subroutine to compute baseflow (for multiple calls for numerical Jacobian) call computeBaseflow(& ! input: control and state variables - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - .true., & ! intent(in): .true. if analytical derivatives are desired - ixSaturation, & ! intent(in): index of upper-most "saturated" layer - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water in each soil layer (-) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice in each soil layer (-) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + .true., & ! intent(in): .true. if analytical derivatives are desired + ixSaturation, & ! intent(in): index of upper-most "saturated" layer + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water in each soil layer (-) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice in each soil layer (-) ! input/output: data structures attr_data, & ! intent(in): spatial attributes mpar_data, & ! intent(in): model parameters prog_data, & ! intent(in): model prognostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU ! output: fluxes and derivatives - mLayerBaseflow, & ! intent(out): baseflow flux in each soil layer (m s-1) - dBaseflow_dVolLiq) ! intent(out): derivative in baseflow w.r.t. volumetric liquid water content (s-1) + mLayerBaseflow, & ! intent(out): baseflow flux in each soil layer (m s-1) + dBaseflow_dVolLiq) ! intent(out): derivative in baseflow w.r.t. volumetric liquid water content (s-1) ! use the chain rule to compute the baseflow derivative w.r.t. matric head (s-1) do iLayer=1,nSoil dBaseflow_dMatric(1:iLayer,iLayer) = dBaseflow_dVolLiq(1:iLayer,iLayer)*mLayerdTheta_dPsi(iLayer) - if(iLayer flux_data%var(iLookFLUX%mLayerColumnInflow)%dat, & ! intent(in): [dp(:)] inflow into each soil layer (m3/s) ! input: local attributes - HRUarea => attr_data%var(iLookATTR%HRUarea), & ! intent(in): [dp] HRU area (m2) - tan_slope => attr_data%var(iLookATTR%tan_slope), & ! intent(in): [dp] tan water table slope, taken as tan local ground surface slope (-) - contourLength => attr_data%var(iLookATTR%contourLength), & ! intent(in): [dp] length of contour at downslope edge of HRU (m) + HRUarea => attr_data%var(iLookATTR%HRUarea), & ! intent(in): [dp] HRU area (m2) + tan_slope => attr_data%var(iLookATTR%tan_slope), & ! intent(in): [dp] tan water table slope, taken as tan local ground surface slope (-) + contourLength => attr_data%var(iLookATTR%contourLength), & ! intent(in): [dp] length of contour at downslope edge of HRU (m) ! input: baseflow parameters zScale_TOPMODEL => mpar_data%var(iLookPARAM%zScale_TOPMODEL)%dat(1), & ! intent(in): [dp] TOPMODEL exponent (-) @@ -330,7 +329,7 @@ subroutine computeBaseflow(& activePorosity = theta_sat(iLayer) - fieldCapacity ! "active" porosity (-) drainableWater = mLayerDepth(iLayer)*(max(0._rkind,mLayerVolFracLiq(iLayer) - fieldCapacity))/activePorosity ! compute layer transmissivity - if(iLayer==nSoil)then + if (iLayer==nSoil) then zActive(iLayer) = drainableWater ! water table thickness associated with storage in a given layer (m) trTotal(iLayer) = tran0*(zActive(iLayer)/soilDepth)**zScale_TOPMODEL ! total transmissivity for total depth zActive (m2 s-1) trSoil(iLayer) = trTotal(iLayer) ! transmissivity of water in a given layer (m2 s-1) @@ -342,7 +341,7 @@ subroutine computeBaseflow(& end do ! looping through soil layers ! set un-used portions of the vectors to zero - if(ixSaturation>1)then + if (ixSaturation>1) then zActive(1:ixSaturation-1) = 0._rkind trTotal(1:ixSaturation-1) = 0._rkind trSoil(1:ixSaturation-1) = 0._rkind @@ -356,10 +355,10 @@ subroutine computeBaseflow(& totalColumnOutflow = sum(mLayerColumnOutflow(1:nSoil))/HRUarea ! compute the available storage (m) - availStorage = sum(mLayerDepth(1:nSoil)*(theta_sat - (mLayerVolFracLiq(1:nSoil)+mLayerVolFracIce(1:nSoil))) ) + availStorage = sum(mLayerDepth(1:nSoil)*(theta_sat - (mLayerVolFracLiq(1:nSoil)+mLayerVolFracIce(1:nSoil)))) ! compute the smoothing function (-) - if(availStorage < xMinEval)then + if (availStorage < xMinEval) then ! (compute the logistic function) expF = exp((availStorage - xCenter)/xWidth) logF = 1._rkind / (1._rkind + expF) @@ -371,7 +370,7 @@ subroutine computeBaseflow(& end if ! compute the exfiltartion (m s-1) - if(totalColumnInflow > totalColumnOutflow .and. logF > tiny(1._rkind))then + if (totalColumnInflow > totalColumnOutflow .and. logF > tiny(1._rkind)) then scalarExfiltration = logF*(totalColumnInflow - totalColumnOutflow) ! m s-1 else scalarExfiltration = 0._rkind @@ -388,7 +387,7 @@ subroutine computeBaseflow(& mLayerColumnOutflow(1) = mLayerColumnOutflow(1) + scalarExfiltration*HRUarea ! test - if(printFlag)then + if (printFlag) then xDepth = sum(mLayerDepth(ixSaturation:nSoil)*(mLayerVolFracLiq(ixSaturation:nSoil) - fieldCapacity))/sum(theta_sat(ixSaturation:nSoil) - fieldCapacity) ! "effective" water table thickness (m) xTran = tran0*(xDepth/soilDepth)**zScale_TOPMODEL ! transmissivity for the entire aquifer (m2 s-1) xFlow = xTran*tan_slope*contourLength/HRUarea ! total column outflow (m s-1) @@ -431,7 +430,7 @@ subroutine computeBaseflow(& end do ! looping through soil layers ! compute the derivative in the exfiltration flux w.r.t. volumetric liquid water content (m s-1) - if(qbTotal < 0._rkind)then + if (qbTotal < 0._rkind) then do iLayer=1,nSoil dExfiltrate_dVolLiq(iLayer) = dBaseflow_dVolLiq(iLayer,iLayer)*logF + dLogFunc_dLiq(iLayer)*qbTotal end do ! looping through soil layers diff --git a/build/source/engine/snowLiqFlx.f90 b/build/source/engine/snowLiqFlx.f90 index 7a9c01648..38ee31935 100644 --- a/build/source/engine/snowLiqFlx.f90 +++ b/build/source/engine/snowLiqFlx.f90 @@ -161,7 +161,7 @@ subroutine snowLiqFlx(& end if ! end if the first flux call ! compute fluxes - do iLayer=ixTop,ixBot ! (loop through snow layers) + do iLayer=ixTop,ixBot ! loop through snow layers if (mLayerVolFracLiqTrial(iLayer) > mLayerThetaResid(iLayer)) then ! check that flow occurs ! compute the relative saturation (-) availCap = mLayerPoreSpace(iLayer) - mLayerThetaResid(iLayer) ! available capacity @@ -176,7 +176,7 @@ subroutine snowLiqFlx(& iLayerLiqFluxSnow(iLayer) = 0._rkind iLayerLiqFluxSnowDeriv(iLayer) = 0._rkind end if ! storage above residual content - end do ! loop through snow layers + end do ! end loop through snow layers end associate ! end association of local variables with information in the data structures diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index 2351c66cd..ad41c9c35 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -28,19 +28,19 @@ module soilLiqFlx_module USE data_types,only:var_dlength ! x%var(:)%dat (rkind) ! missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing real number +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing real number ! physical constants USE multiconst,only:& - LH_fus, & ! latent heat of fusion (J kg-1) - LH_vap, & ! latent heat of vaporization (J kg-1) - LH_sub, & ! latent heat of sublimation (J kg-1) - gravity, & ! gravitational acceleteration (m s-2) - Tfreeze, & ! freezing point of pure water (K) - iden_air,& ! intrinsic density of air (kg m-3) - iden_ice,& ! intrinsic density of ice (kg m-3) - iden_water ! intrinsic density of water (kg m-3) + LH_fus, & ! latent heat of fusion (J kg-1) + LH_vap, & ! latent heat of vaporization (J kg-1) + LH_sub, & ! latent heat of sublimation (J kg-1) + gravity, & ! gravitational acceleteration (m s-2) + Tfreeze, & ! freezing point of pure water (K) + iden_air,& ! intrinsic density of air (kg m-3) + iden_ice,& ! intrinsic density of ice (kg m-3) + iden_water ! intrinsic density of water (kg m-3) ! named variables USE var_lookup,only:iLookPROG ! named variables for structure elements @@ -54,7 +54,7 @@ module soilLiqFlx_module USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure ! provide access to look-up values for model decisions -USE mDecisions_module,only: & +USE mDecisions_module,only: & ! look-up values for method used to compute derivative numerical, & ! numerical solution analytical, & ! analytical solution @@ -78,42 +78,40 @@ module soilLiqFlx_module ! ----------------------------------------------------------------------------------------------------------- implicit none private -public::soilLiqFlx +public :: soilLiqFlx ! constant parameters real(rkind),parameter :: verySmall=1.e-12_rkind ! a very small number (used to avoid divide by zero) real(rkind),parameter :: dx=1.e-8_rkind ! finite difference increment contains - - ! *************************************************************************************************************** ! public subroutine soilLiqFlx: compute liquid water fluxes and their derivatives ! *************************************************************************************************************** subroutine soilLiqFlx(& ! input: model control - nSoil, & ! intent(in): number of soil layers - firstSplitOper, & ! intent(in): flag to compute infiltration, if firstSplitOper - scalarSolution, & ! intent(in): flag to indicate the scalar solution - deriv_desired, & ! intent(in): flag indicating if derivatives are desired + nSoil, & ! intent(in): number of soil layers + firstSplitOper, & ! intent(in): flag to compute infiltration, if firstSplitOper + scalarSolution, & ! intent(in): flag to indicate the scalar solution + deriv_desired, & ! intent(in): flag indicating if derivatives are desired ! input: trial state variables - mLayerTempTrial, & ! intent(in): temperature (K) - mLayerMatricHeadTrial, & ! intent(in): matric head (m) - mLayerMatricHeadLiqTrial, & ! intent(in): liquid matric head (m) - mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water (-) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice (-) + mLayerTempTrial, & ! intent(in): temperature (K) + mLayerMatricHeadTrial, & ! intent(in): matric head (m) + mLayerMatricHeadLiqTrial, & ! intent(in): liquid matric head (m) + mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water (-) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice (-) ! input: pre-computed derivatives - mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - dPsiLiq_dTemp, & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) - dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water - above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature - above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) + mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + dPsiLiq_dTemp, & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) + dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) ! input: fluxes - scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) - scalarGroundEvaporation, & ! intent(in): ground evaporation (kg m-2 s-1) - scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) + scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) + scalarGroundEvaporation, & ! intent(in): ground evaporation (kg m-2 s-1) + scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) ! input-output: data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model indices @@ -150,13 +148,13 @@ subroutine soilLiqFlx(& ! output: error control err,message) ! intent(out): error control ! utility modules - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water - USE soil_utils_module,only:matricHead ! compute matric head (m) - USE soil_utils_module,only:dTheta_dPsi ! compute derivative of the soil moisture characteristic w.r.t. psi (m-1) - USE soil_utils_module,only:dPsi_dTheta ! compute derivative of the soil moisture characteristic w.r.t. theta (m) - USE soil_utils_module,only:hydCond_psi ! compute hydraulic conductivity as a function of matric head - USE soil_utils_module,only:hydCond_liq ! compute hydraulic conductivity as a function of volumetric liquid water content - USE soil_utils_module,only:hydCondMP_liq ! compute hydraulic conductivity of macropores as a function of volumetric liquid water content + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water + USE soil_utils_module,only:matricHead ! compute matric head (m) + USE soil_utils_module,only:dTheta_dPsi ! compute derivative of the soil moisture characteristic w.r.t. psi (m-1) + USE soil_utils_module,only:dPsi_dTheta ! compute derivative of the soil moisture characteristic w.r.t. theta (m) + USE soil_utils_module,only:hydCond_psi ! compute hydraulic conductivity as a function of matric head + USE soil_utils_module,only:hydCond_liq ! compute hydraulic conductivity as a function of volumetric liquid water content + USE soil_utils_module,only:hydCondMP_liq ! compute hydraulic conductivity of macropores as a function of volumetric liquid water content ! ------------------------------------------------------------------------------------------------------------------------------------------------- implicit none ! input: model control @@ -243,7 +241,7 @@ subroutine soilLiqFlx(& integer(i4b) :: ixIce ! index of the lowest soil layer that contains ice real(rkind),dimension(0:nSoil) :: iLayerHeight ! height of the layer interfaces (m) ! compute fluxes and derivatives at layer interfaces - real(rkind) :: scalardPsi_dTheta ! derivative in soil water characteristix, used for perturbations when computing numerical derivatives + real(rkind) :: scalardPsi_dTheta ! derivative in soil water characteristix, used for perturbations when computing numerical derivatives ! ------------------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='soilLiqFlx/' @@ -299,54 +297,53 @@ subroutine soilLiqFlx(& mLayerRootDensity => diag_data%var(iLookDIAG%mLayerRootDensity)%dat, & ! intent(in): root density in each layer (-) scalarTranspireLim => diag_data%var(iLookDIAG%scalarTranspireLim)%dat(1), & ! intent(in): weighted average of the transpiration limiting factor (-) mLayerTranspireLim => diag_data%var(iLookDIAG%mLayerTranspireLim)%dat & ! intent(in): transpiration limiting factor in each layer (-) - ) ! associating local variables with the information in the data structures + ) ! end associating local variables with the information in the data structures ! ------------------------------------------------------------------------------------------------------------------------------------------------- ! preliminaries ! ------------------------------------------------------------------------------------------------------------------------------------------------- ! get the indices for the soil layers - if(scalarSolution)then + if (scalarSolution) then ixLayerDesired = pack(ixMatricHead, ixSoilOnlyHyd/=integerMissing) ixTop = ixLayerDesired(1) ixBot = ixLayerDesired(1) else ixTop = 1 ixBot = nSoil - endif + end if ! identify the number of layers that contain roots nRoots = count(iLayerHeight(0:nSoil-1) < rootingDepth-verySmall) - if(nRoots==0)then + if (nRoots==0) then message=trim(message)//'no layers with roots' err=20; return - endif + end if ! identify lowest soil layer with ice ! NOTE: cannot use count because there may be an unfrozen wedge ixIce = 0 ! initialize the index of the ice layer (0 means no ice in the soil profile) do iLayer=1,nSoil ! (loop through soil layers) - if(mLayerVolFracIceTrial(iLayer) > verySmall) ixIce = iLayer + if (mLayerVolFracIceTrial(iLayer) > verySmall) ixIce = iLayer end do ! ------------------------------------------------------------------------------------------------------------------------------------------------- ! compute the transpiration sink term ! ------------------------------------------------------------------------------------------------------------------------------------------------- - ! check the need to compute transpiration (NOTE: intent=inout) - if( .not. (scalarSolution .and. ixTop>1) )then + if ( .not. (scalarSolution .and. ixTop>1) ) then ! check the need to compute transpiration (NOTE: intent=inout) ! compute the fraction of transpiration loss from each soil layer - if(scalarTranspireLim > tiny(scalarTranspireLim))then ! (transpiration may be non-zero even if the soil moisture limiting factor is zero) + if (scalarTranspireLim > tiny(scalarTranspireLim)) then ! (transpiration may be non-zero even if the soil moisture limiting factor is zero) mLayerTranspireFrac(:) = mLayerRootDensity(:)*mLayerTranspireLim(:)/scalarTranspireLim else ! (possible for there to be non-zero conductance and therefore transpiration in this case) mLayerTranspireFrac(:) = mLayerRootDensity(:) / sum(mLayerRootDensity) end if ! check fractions sum to one - if(abs(sum(mLayerTranspireFrac) - 1._rkind) > verySmall)then + if (abs(sum(mLayerTranspireFrac) - 1._rkind) > verySmall) then message=trim(message)//'fraction transpiration in soil layers does not sum to one' err=20; return - endif + end if ! compute transpiration loss from each soil layer (kg m-2 s-1 --> m s-1) mLayerTranspire(:) = mLayerTranspireFrac(:)*scalarCanopyTranspiration/iden_water @@ -357,16 +354,16 @@ subroutine soilLiqFlx(& mLayerdTrans_dTGround(:) = mLayerTranspireFrac(:)*dCanopyTrans_dTGround/iden_water ! special case of prescribed head -- no transpiration - if(ixBcUpperSoilHydrology==prescribedHead) then + if (ixBcUpperSoilHydrology==prescribedHead) then mLayerTranspire(:) = 0._rkind ! derivatives in transpiration w.r.t. canopy state variables mLayerdTrans_dCanWat(:) = 0._rkind mLayerdTrans_dTCanair(:)= 0._rkind mLayerdTrans_dTCanopy(:)= 0._rkind mLayerdTrans_dTGround(:)= 0._rkind - endif + end if - endif ! if need to compute transpiration + end if ! if need to compute transpiration ! ------------------------------------------------------------------------------------------------------------------------------------------------- ! compute diagnostic variables at the nodes throughout the soil profile @@ -374,28 +371,28 @@ subroutine soilLiqFlx(& do iSoil=ixTop,min(ixBot+1,nSoil) ! (loop through soil layers) call diagv_node(& ! input: model control - deriv_desired, & ! intent(in): flag indicating if derivatives are desired - ixRichards, & ! intent(in): index defining the option for Richards' equation (moisture or mixdform) + deriv_desired, & ! intent(in): flag indicating if derivatives are desired + ixRichards, & ! intent(in): index defining the option for Richards' equation (moisture or mixdform) ! input: state variables - mLayerTempTrial(iSoil), & ! intent(in): temperature (K) - mLayerMatricHeadLiqTrial(iSoil), & ! intent(in): liquid matric head in each layer (m) - mLayerVolFracLiqTrial(iSoil), & ! intent(in): volumetric liquid water content in each soil layer (-) - mLayerVolFracIceTrial(iSoil), & ! intent(in): volumetric ice content in each soil layer (-) + mLayerTempTrial(iSoil), & ! intent(in): temperature (K) + mLayerMatricHeadLiqTrial(iSoil), & ! intent(in): liquid matric head in each layer (m) + mLayerVolFracLiqTrial(iSoil), & ! intent(in): volumetric liquid water content in each soil layer (-) + mLayerVolFracIceTrial(iSoil), & ! intent(in): volumetric ice content in each soil layer (-) ! input: pre-computed deriavatives - mLayerdTheta_dTk(iSoil), & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - dPsiLiq_dTemp(iSoil), & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) + mLayerdTheta_dTk(iSoil), & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + dPsiLiq_dTemp(iSoil), & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) ! input: soil parameters - vGn_alpha(iSoil), & ! intent(in): van Genutchen "alpha" parameter (m-1) - vGn_n(iSoil), & ! intent(in): van Genutchen "n" parameter (-) - vGn_m(iSoil), & ! intent(in): van Genutchen "m" parameter (-) - mpExp, & ! intent(in): empirical exponent in macropore flow equation (-) - theta_sat(iSoil), & ! intent(in): soil porosity (-) - theta_res(iSoil), & ! intent(in): soil residual volumetric water content (-) - theta_mp, & ! intent(in): volumetric liquid water content when macropore flow begins (-) - f_impede, & ! intent(in): ice impedence factor (-) + vGn_alpha(iSoil), & ! intent(in): van Genutchen "alpha" parameter (m-1) + vGn_n(iSoil), & ! intent(in): van Genutchen "n" parameter (-) + vGn_m(iSoil), & ! intent(in): van Genutchen "m" parameter (-) + mpExp, & ! intent(in): empirical exponent in macropore flow equation (-) + theta_sat(iSoil), & ! intent(in): soil porosity (-) + theta_res(iSoil), & ! intent(in): soil residual volumetric water content (-) + theta_mp, & ! intent(in): volumetric liquid water content when macropore flow begins (-) + f_impede, & ! intent(in): ice impedence factor (-) ! input: saturated hydraulic conductivity - mLayerSatHydCond(iSoil), & ! intent(in): saturated hydraulic conductivity at the mid-point of each layer (m s-1) - mLayerSatHydCondMP(iSoil), & ! intent(in): saturated hydraulic conductivity of macropores at the mid-point of each layer (m s-1) + mLayerSatHydCond(iSoil), & ! intent(in): saturated hydraulic conductivity at the mid-point of each layer (m s-1) + mLayerSatHydCondMP(iSoil), & ! intent(in): saturated hydraulic conductivity of macropores at the mid-point of each layer (m s-1) ! output: derivative in the soil water characteristic mLayerdPsi_dTheta(iSoil), & ! intent(out): derivative in the soil water characteristic mLayerdTheta_dPsi(iSoil), & ! intent(out): derivative in the soil water characteristic @@ -410,8 +407,8 @@ subroutine soilLiqFlx(& dHydCond_dTemp(iSoil), & ! intent(out): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) ! output: error control err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - end do ! (looping through soil layers) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + end do ! end looping through soil layers ! ------------------------------------------------------------------------------------------------------------------------------------------------- ! compute infiltraton at the surface and its derivative w.r.t. mass in the upper soil layer @@ -424,51 +421,51 @@ subroutine soilLiqFlx(& ! compute surface flux and its derivative... call surfaceFlx(& ! input: model control - firstSplitOper, & ! intent(in): flag indicating if desire to compute infiltration - deriv_desired, & ! intent(in): flag indicating if derivatives are desired - ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) - ixBcUpperSoilHydrology, & ! intent(in): index defining the type of boundary conditions (neumann or diriclet) - nRoots, & ! intent(in): number of layers that contain roots - ixIce, & ! intent(in): index of lowest ice layer - nSoil, & ! intent(in): number of soil layers + firstSplitOper, & ! intent(in): flag indicating if desire to compute infiltration + deriv_desired, & ! intent(in): flag indicating if derivatives are desired + ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) + ixBcUpperSoilHydrology, & ! intent(in): index defining the type of boundary conditions (neumann or diriclet) + nRoots, & ! intent(in): number of layers that contain roots + ixIce, & ! intent(in): index of lowest ice layer + nSoil, & ! intent(in): number of soil layers ! input: state variables - mLayerTempTrial, & ! intent(in): temperature (K) - mLayerMatricHeadLiqTrial(1), & ! intent(in): liquid matric head in the upper-most soil layer (m) - mLayerMatricHeadTrial, & ! intent(in): matric head in each soil layer (m) - mLayerVolFracLiqTrial(1), & ! intent(in): volumetric liquid water content the upper-most soil layer (-) - mLayerVolFracLiqTrial, & ! intent(in): volumetric liquid water content in each soil layer (-) - mLayerVolFracIceTrial, & ! intent(in): volumetric ice content in each soil layer (-) + mLayerTempTrial, & ! intent(in): temperature (K) + mLayerMatricHeadLiqTrial(1), & ! intent(in): liquid matric head in the upper-most soil layer (m) + mLayerMatricHeadTrial, & ! intent(in): matric head in each soil layer (m) + mLayerVolFracLiqTrial(1), & ! intent(in): volumetric liquid water content the upper-most soil layer (-) + mLayerVolFracLiqTrial, & ! intent(in): volumetric liquid water content in each soil layer (-) + mLayerVolFracIceTrial, & ! intent(in): volumetric ice content in each soil layer (-) ! input: pre-computed deriavatives - mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) - mLayerdPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) - above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water - above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature - above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) + mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) + mLayerdPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) + above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) ! input: depth of upper-most soil layer (m) - mLayerDepth, & ! intent(in): depth of each soil layer (m) - iLayerHeight, & ! intent(in): height at the interface of each layer (m) + mLayerDepth, & ! intent(in): depth of each soil layer (m) + iLayerHeight, & ! intent(in): height at the interface of each layer (m) ! input: boundary conditions - upperBoundHead, & ! intent(in): upper boundary condition (m) - upperBoundTheta, & ! intent(in): upper boundary condition (-) + upperBoundHead, & ! intent(in): upper boundary condition (m) + upperBoundTheta, & ! intent(in): upper boundary condition (-) ! input: flux at the upper boundary - scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) + scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) ! input: transmittance - iLayerSatHydCond(0), & ! intent(in): saturated hydraulic conductivity at the surface (m s-1) - dHydCond_dTemp(1), & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) - iceImpedeFac(1), & ! intent(in): ice impedence factor in the upper-most soil layer (-) + iLayerSatHydCond(0), & ! intent(in): saturated hydraulic conductivity at the surface (m s-1) + dHydCond_dTemp(1), & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) + iceImpedeFac(1), & ! intent(in): ice impedence factor in the upper-most soil layer (-) ! input: soil parameters - vGn_alpha(1), & ! intent(in): van Genutchen "alpha" parameter (m-1) - vGn_n(1), & ! intent(in): van Genutchen "n" parameter (-) - vGn_m(1), & ! intent(in): van Genutchen "m" parameter (-) - theta_sat(1), & ! intent(in): soil porosity (-) - theta_res(1), & ! intent(in): soil residual volumetric water content (-) - qSurfScale, & ! intent(in): scaling factor in the surface runoff parameterization (-) - zScale_TOPMODEL, & ! intent(in): scaling factor used to describe decrease in hydraulic conductivity with depth (m) - rootingDepth, & ! intent(in): rooting depth (m) - wettingFrontSuction, & ! intent(in): Green-Ampt wetting front suction (m) - soilIceScale, & ! intent(in): soil ice scaling factor in Gamma distribution used to define frozen area (m) - soilIceCV, & ! intent(in): soil ice CV in Gamma distribution used to define frozen area (-) + vGn_alpha(1), & ! intent(in): van Genutchen "alpha" parameter (m-1) + vGn_n(1), & ! intent(in): van Genutchen "n" parameter (-) + vGn_m(1), & ! intent(in): van Genutchen "m" parameter (-) + theta_sat(1), & ! intent(in): soil porosity (-) + theta_res(1), & ! intent(in): soil residual volumetric water content (-) + qSurfScale, & ! intent(in): scaling factor in the surface runoff parameterization (-) + zScale_TOPMODEL, & ! intent(in): scaling factor used to describe decrease in hydraulic conductivity with depth (m) + rootingDepth, & ! intent(in): rooting depth (m) + wettingFrontSuction, & ! intent(in): Green-Ampt wetting front suction (m) + soilIceScale, & ! intent(in): soil ice scaling factor in Gamma distribution used to define frozen area (m) + soilIceCV, & ! intent(in): soil ice CV in Gamma distribution used to define frozen area (-) ! input-output: hydraulic conductivity and diffusivity at the surface iLayerHydCond(0), & ! intent(inout): hydraulic conductivity at the surface (m s-1) iLayerDiffuse(0), & ! intent(inout): hydraulic diffusivity at the surface (m2 s-1) @@ -476,14 +473,14 @@ subroutine soilLiqFlx(& xMaxInfilRate, & ! intent(inout): maximum infiltration rate (m s-1) scalarInfilArea, & ! intent(inout): fraction of unfrozen area where water can infiltrate (-) scalarFrozenArea, & ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) - scalarSurfaceRunoff, & ! intent(out): surface runoff (m s-1) - scalarSurfaceInfiltration, & ! intent(out): surface infiltration (m s-1) + scalarSurfaceRunoff, & ! intent(out): surface runoff (m s-1) + scalarSurfaceInfiltration, & ! intent(out): surface infiltration (m s-1) ! input-output: deriavtives in surface infiltration w.r.t. volumetric liquid water (m s-1) and matric head (s-1) in the upper-most soil layer dq_dHydStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) dq_dNrgStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + err,cmessage) ! intent(out): error control + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! include base soil evaporation as the upper boundary flux iLayerLiqFluxSoil(0) = scalarGroundEvaporation/iden_water + scalarSurfaceInfiltration @@ -499,79 +496,78 @@ subroutine soilLiqFlx(& do iLayer=ixTop,min(ixBot,nSoil-1) call iLayerFlux(& ! input: model control - deriv_desired, & ! intent(in): flag indicating if derivatives are desired - ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) + deriv_desired, & ! intent(in): flag indicating if derivatives are desired + ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) ! input: state variables (adjacent layers) - mLayerMatricHeadLiqTrial(iLayer:iLayer+1), & ! intent(in): liquid matric head at the soil nodes (m) - mLayerVolFracLiqTrial(iLayer:iLayer+1), & ! intent(in): volumetric liquid water content at the soil nodes (-) + mLayerMatricHeadLiqTrial(iLayer:iLayer+1), & ! intent(in): liquid matric head at the soil nodes (m) + mLayerVolFracLiqTrial(iLayer:iLayer+1), & ! intent(in): volumetric liquid water content at the soil nodes (-) ! input: model coordinate variables (adjacent layers) - mLayerHeight(iLayer:iLayer+1), & ! intent(in): height of the soil nodes (m) + mLayerHeight(iLayer:iLayer+1), & ! intent(in): height of the soil nodes (m) ! input: temperature derivatives - dPsiLiq_dTemp(iLayer:iLayer+1), & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) - dHydCond_dTemp(iLayer:iLayer+1), & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) + dPsiLiq_dTemp(iLayer:iLayer+1), & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) + dHydCond_dTemp(iLayer:iLayer+1), & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) ! input: transmittance (adjacent layers) - mLayerHydCond(iLayer:iLayer+1), & ! intent(in): hydraulic conductivity at the soil nodes (m s-1) - mLayerDiffuse(iLayer:iLayer+1), & ! intent(in): hydraulic diffusivity at the soil nodes (m2 s-1) + mLayerHydCond(iLayer:iLayer+1), & ! intent(in): hydraulic conductivity at the soil nodes (m s-1) + mLayerDiffuse(iLayer:iLayer+1), & ! intent(in): hydraulic diffusivity at the soil nodes (m2 s-1) ! input: transmittance derivatives (adjacent layers) - dHydCond_dVolLiq(iLayer:iLayer+1), & ! intent(in): change in hydraulic conductivity w.r.t. change in volumetric liquid water content (m s-1) - dDiffuse_dVolLiq(iLayer:iLayer+1), & ! intent(in): change in hydraulic diffusivity w.r.t. change in volumetric liquid water content (m2 s-1) - dHydCond_dMatric(iLayer:iLayer+1), & ! intent(in): change in hydraulic conductivity w.r.t. change in matric head (s-1) + dHydCond_dVolLiq(iLayer:iLayer+1), & ! intent(in): change in hydraulic conductivity w.r.t. change in volumetric liquid water content (m s-1) + dDiffuse_dVolLiq(iLayer:iLayer+1), & ! intent(in): change in hydraulic diffusivity w.r.t. change in volumetric liquid water content (m2 s-1) + dHydCond_dMatric(iLayer:iLayer+1), & ! intent(in): change in hydraulic conductivity w.r.t. change in matric head (s-1) ! output: tranmsmittance at the layer interface (scalars) - iLayerHydCond(iLayer), & ! intent(out): hydraulic conductivity at the interface between layers (m s-1) - iLayerDiffuse(iLayer), & ! intent(out): hydraulic diffusivity at the interface between layers (m2 s-1) + iLayerHydCond(iLayer), & ! intent(out): hydraulic conductivity at the interface between layers (m s-1) + iLayerDiffuse(iLayer), & ! intent(out): hydraulic diffusivity at the interface between layers (m2 s-1) ! output: vertical flux at the layer interface (scalars) - iLayerLiqFluxSoil(iLayer), & ! intent(out): vertical flux of liquid water at the layer interface (m s-1) + iLayerLiqFluxSoil(iLayer), & ! intent(out): vertical flux of liquid water at the layer interface (m s-1) ! output: derivatives in fluxes w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below (m s-1 or s-1) - dq_dHydStateAbove(iLayer), & ! intent(out): derivatives in the flux w.r.t. matric head or volumetric lquid water in the layer above (m s-1 or s-1) - dq_dHydStateBelow(iLayer), & ! intent(out): derivatives in the flux w.r.t. matric head or volumetric lquid water in the layer below (m s-1 or s-1) + dq_dHydStateAbove(iLayer), & ! intent(out): derivatives in the flux w.r.t. matric head or volumetric lquid water in the layer above (m s-1 or s-1) + dq_dHydStateBelow(iLayer), & ! intent(out): derivatives in the flux w.r.t. matric head or volumetric lquid water in the layer below (m s-1 or s-1) ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) - dq_dNrgStateAbove(iLayer), & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) - dq_dNrgStateBelow(iLayer), & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) + dq_dNrgStateAbove(iLayer), & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) + dq_dNrgStateBelow(iLayer), & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - end do ! (looping through soil layers) + err,cmessage) ! intent(out): error control + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + end do ! end looping through soil layers ! ------------------------------------------------------------------------------------------------------------------------------------------------- ! * compute drainage flux from the bottom of the soil profile, and its derivative ! ------------------------------------------------------------------------------------------------------------------------------------------------- - ! define the need to compute drainage - if( .not. (scalarSolution .and. ixTop epsilon(iceImpedeFac))then + if (deriv_desired) then + if (scalarVolFracIceTrial > epsilon(iceImpedeFac)) then dK_dLiq__noIce = dHydCond_dLiq(scalarVolFracLiqTrial,scalarSatHydCond,theta_res,theta_sat,vGn_m,.true.) ! [.true. = analytical] dHydCond_dVolLiq = hydCond_noIce*dIceImpede_dLiq + dK_dLiq__noIce*iceImpedeFac else @@ -756,9 +752,9 @@ subroutine diagv_node(& scalarHydCond = hydCond_noIce*iceImpedeFac + scalarHydCondMP ! compute derivative in hydraulic conductivity (m s-1) - if(deriv_desired)then - ! (compute derivative for macropores) - if(localVolFracLiq > theta_mp)then + if (deriv_desired) then + ! compute derivative for macropores + if (localVolFracLiq > theta_mp) then relSatMP = (localVolFracLiq - theta_mp)/(theta_sat - theta_mp) dHydCondMacro_dVolLiq = ((scalarSatHydCondMP - scalarSatHydCond)/(theta_sat - theta_mp))*mpExp*(relSatMP**(mpExp - 1._rkind)) dHydCondMacro_dMatric = scalardTheta_dPsi*dHydCondMacro_dVolLiq @@ -766,8 +762,8 @@ subroutine diagv_node(& dHydCondMacro_dVolLiq = 0._rkind dHydCondMacro_dMatric = 0._rkind end if - ! (compute derivatives for micropores) - if(scalarVolFracIceTrial > verySmall)then + ! compute derivatives for micropores + if (scalarVolFracIceTrial > verySmall) then dK_dPsi__noIce = dHydCond_dPsi(scalarMatricHeadLiqTrial,scalarSatHydCond,vGn_alpha,vGn_n,vGn_m,.true.) ! analytical dHydCondMicro_dTemp = dPsiLiq_dTemp*dK_dPsi__noIce ! m s-1 K-1 dHydCondMicro_dMatric = hydCond_noIce*dIceImpede_dLiq*scalardTheta_dPsi + dK_dPsi__noIce*iceImpedeFac @@ -775,25 +771,25 @@ subroutine diagv_node(& dHydCondMicro_dTemp = 0._rkind dHydCondMicro_dMatric = dHydCond_dPsi(scalarMatricHeadLiqTrial,scalarSatHydCond,vGn_alpha,vGn_n,vGn_m,.true.) end if - ! (combine derivatives) + ! combine derivatives dHydCond_dMatric = dHydCondMicro_dMatric + dHydCondMacro_dMatric - ! (compute analytical derivative for change in ice impedance factor w.r.t. temperature) - call dIceImpede_dTemp(scalarVolFracIceTrial, & ! intent(in): trial value of volumetric ice content (-) - dTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - f_impede, & ! intent(in): ice impedance parameter (-) + ! compute analytical derivative for change in ice impedance factor w.r.t. temperature + call dIceImpede_dTemp(scalarVolFracIceTrial, & ! intent(in): trial value of volumetric ice content (-) + dTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + f_impede, & ! intent(in): ice impedance parameter (-) dIceImpede_dT ) ! intent(out): derivative in ice impedance factor w.r.t. temperature (K-1) - ! (compute derivative in hydraulic conductivity w.r.t. temperature) + ! compute derivative in hydraulic conductivity w.r.t. temperature dHydCond_dTemp = hydCond_noIce*dIceImpede_dT + dHydCondMicro_dTemp*iceImpedeFac - ! (set values that are not used to missing) + ! set values that are not used to missing dHydCond_dVolLiq = realMissing ! not used, so cause problems dDiffuse_dVolLiq = realMissing ! not used, so cause problems end if case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return - end select ! select form of Richards' equation + end select ! end select form of Richards' equation ! if derivatives are not desired, then set values to missing - if(.not.deriv_desired)then + if (.not.deriv_desired) then dHydCond_dVolLiq = realMissing ! not used, so cause problems dDiffuse_dVolLiq = realMissing ! not used, so cause problems dHydCond_dMatric = realMissing ! not used, so cause problems @@ -801,57 +797,56 @@ subroutine diagv_node(& end subroutine diagv_node - ! *************************************************************************************************************** ! private subroutine surfaceFlx: compute the surface flux and its derivative ! *************************************************************************************************************** subroutine surfaceFlx(& ! input: model control - firstSplitOper, & ! intent(in): flag indicating if desire to compute infiltration - deriv_desired, & ! intent(in): flag indicating if derivatives are desired - ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) - bc_upper, & ! intent(in): index defining the type of boundary conditions (neumann or diriclet) - nRoots, & ! intent(in): number of layers that contain roots - ixIce, & ! intent(in): index of lowest ice layer - nSoil, & ! intent(in): number of soil layers + firstSplitOper, & ! intent(in): flag indicating if desire to compute infiltration + deriv_desired, & ! intent(in): flag indicating if derivatives are desired + ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) + bc_upper, & ! intent(in): index defining the type of boundary conditions (neumann or diriclet) + nRoots, & ! intent(in): number of layers that contain roots + ixIce, & ! intent(in): index of lowest ice layer + nSoil, & ! intent(in): number of soil layers ! input: state variables - mLayerTemp, & ! intent(in): temperature (K) - scalarMatricHeadLiq, & ! intent(in): liquid matric head in the upper-most soil layer (m) - mLayerMatricHead, & ! intent(in): matric head in each soil layer (m) - scalarVolFracLiq, & ! intent(in): volumetric liquid water content in the upper-most soil layer (-) - mLayerVolFracLiq, & ! intent(in): volumetric liquid water content in each soil layer (-) - mLayerVolFracIce, & ! intent(in): volumetric ice content in each soil layer (-) + mLayerTemp, & ! intent(in): temperature (K) + scalarMatricHeadLiq, & ! intent(in): liquid matric head in the upper-most soil layer (m) + mLayerMatricHead, & ! intent(in): matric head in each soil layer (m) + scalarVolFracLiq, & ! intent(in): volumetric liquid water content in the upper-most soil layer (-) + mLayerVolFracLiq, & ! intent(in): volumetric liquid water content in each soil layer (-) + mLayerVolFracIce, & ! intent(in): volumetric ice content in each soil layer (-) ! input: pre-computed derivatives - dTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - dTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) - mLayerdPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) - above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water - above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature - above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) + dTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + dTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. psi (m-1) + mLayerdPsi_dTheta, & ! intent(in): derivative in the soil water characteristic w.r.t. theta (m) + above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) ! input: depth of upper-most soil layer (m) - mLayerDepth, & ! intent(in): depth of each soil layer (m) - iLayerHeight, & ! intent(in): height at the interface of each layer (m) + mLayerDepth, & ! intent(in): depth of each soil layer (m) + iLayerHeight, & ! intent(in): height at the interface of each layer (m) ! input: boundary conditions - upperBoundHead, & ! intent(in): upper boundary condition (m) - upperBoundTheta, & ! intent(in): upper boundary condition (-) + upperBoundHead, & ! intent(in): upper boundary condition (m) + upperBoundTheta, & ! intent(in): upper boundary condition (-) ! input: flux at the upper boundary - scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) + scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) ! input: transmittance - surfaceSatHydCond, & ! intent(in): saturated hydraulic conductivity at the surface (m s-1) - dHydCond_dTemp, & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) - iceImpedeFac, & ! intent(in): ice impedence factor in the upper-most soil layer (-) + surfaceSatHydCond, & ! intent(in): saturated hydraulic conductivity at the surface (m s-1) + dHydCond_dTemp, & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) + iceImpedeFac, & ! intent(in): ice impedence factor in the upper-most soil layer (-) ! input: soil parameters - vGn_alpha, & ! intent(in): van Genutchen "alpha" parameter (m-1) - vGn_n, & ! intent(in): van Genutchen "n" parameter (-) - vGn_m, & ! intent(in): van Genutchen "m" parameter (-) - theta_sat, & ! intent(in): soil porosity (-) - theta_res, & ! intent(in): soil residual volumetric water content (-) - qSurfScale, & ! intent(in): scaling factor in the surface runoff parameterization (-) - zScale_TOPMODEL, & ! intent(in): scaling factor used to describe decrease in hydraulic conductivity with depth (m) - rootingDepth, & ! intent(in): rooting depth (m) - wettingFrontSuction, & ! intent(in): Green-Ampt wetting front suction (m) - soilIceScale, & ! intent(in): soil ice scaling factor in Gamma distribution used to define frozen area (m) - soilIceCV, & ! intent(in): soil ice CV in Gamma distribution used to define frozen area (-) + vGn_alpha, & ! intent(in): van Genutchen "alpha" parameter (m-1) + vGn_n, & ! intent(in): van Genutchen "n" parameter (-) + vGn_m, & ! intent(in): van Genutchen "m" parameter (-) + theta_sat, & ! intent(in): soil porosity (-) + theta_res, & ! intent(in): soil residual volumetric water content (-) + qSurfScale, & ! intent(in): scaling factor in the surface runoff parameterization (-) + zScale_TOPMODEL, & ! intent(in): scaling factor used to describe decrease in hydraulic conductivity with depth (m) + rootingDepth, & ! intent(in): rooting depth (m) + wettingFrontSuction, & ! intent(in): Green-Ampt wetting front suction (m) + soilIceScale, & ! intent(in): soil ice scaling factor in Gamma distribution used to define frozen area (m) + soilIceCV, & ! intent(in): soil ice CV in Gamma distribution used to define frozen area (-) ! input-output: hydraulic conductivity and diffusivity at the surface surfaceHydCond, & ! intent(inout): hydraulic conductivity at the surface (m s-1) surfaceDiffuse, & ! intent(inout): hydraulic diffusivity at the surface (m2 s-1) @@ -859,19 +854,19 @@ subroutine surfaceFlx(& xMaxInfilRate, & ! intent(inout): maximum infiltration rate (m s-1) scalarInfilArea, & ! intent(inout): fraction of unfrozen area where water can infiltrate (-) scalarFrozenArea, & ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) - scalarSurfaceRunoff, & ! intent(out): surface runoff (m s-1) - scalarSurfaceInfiltration, & ! intent(out): surface infiltration (m s-1) + scalarSurfaceRunoff, & ! intent(out): surface runoff (m s-1) + scalarSurfaceInfiltration, & ! intent(out): surface infiltration (m s-1) ! input-output: deriavtives in surface infiltration w.r.t. volumetric liquid water (m s-1) and matric head (s-1) in the upper-most soil layer dq_dHydStateVec, & ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) dq_dNrgStateVec, & ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) ! output: error control - err,message) ! intent(out): error control - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water as a function of matric head (-) - USE soil_utils_module,only:hydCond_psi ! compute hydraulic conductivity as a function of matric head (m s-1) - USE soil_utils_module,only:hydCond_liq ! compute hydraulic conductivity as a function of volumetric liquid water content (m s-1) - USE soil_utils_module,only:dPsi_dTheta ! compute derivative of the soil moisture characteristic w.r.t. theta (m) - USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists - USE soil_utils_module,only:gammp ! compute the cumulative probabilty based on the Gamma distribution + err,message) ! intent(out): error control + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water as a function of matric head (-) + USE soil_utils_module,only:hydCond_psi ! compute hydraulic conductivity as a function of matric head (m s-1) + USE soil_utils_module,only:hydCond_liq ! compute hydraulic conductivity as a function of volumetric liquid water content (m s-1) + USE soil_utils_module,only:dPsi_dTheta ! compute derivative of the soil moisture characteristic w.r.t. theta (m) + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + USE soil_utils_module,only:gammp ! compute the cumulative probabilty based on the Gamma distribution ! compute infiltraton at the surface and its derivative w.r.t. mass in the upper soil layer implicit none ! ----------------------------------------------------------------------------------------------------------------------------- @@ -941,34 +936,34 @@ subroutine surfaceFlx(& ! ----------------------------------------------------------------------------------------------------------------------------- ! local variables ! general - integer(i4b) :: iLayer ! index of soil layer - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - real(rkind) :: fpart1,fpart2 ! different parts of a function + integer(i4b) :: iLayer ! index of soil layer + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind) :: fpart1,fpart2 ! different parts of a function real(rkind) :: dpart1(1:nSoil),dpart2(1:nSoil) ! derivatives for different parts of a function - real(rkind) :: dfracCap(1:nSoil),dfInfRaw(1:nSoil) ! derivatives for different parts of a function + real(rkind) :: dfracCap(1:nSoil),dfInfRaw(1:nSoil) ! derivatives for different parts of a function ! head boundary condition - real(rkind) :: cFlux ! capillary flux (m s-1) - real(rkind) :: dNum ! numerical derivative + real(rkind) :: cFlux ! capillary flux (m s-1) + real(rkind) :: dNum ! numerical derivative ! simplified Green-Ampt infiltration - real(rkind) :: rootZoneLiq ! depth of liquid water in the root zone (m) - real(rkind) :: rootZoneIce ! depth of ice in the root zone (m) - real(rkind) :: availCapacity ! available storage capacity in the root zone (m) - real(rkind) :: depthWettingFront ! depth to the wetting front (m) - real(rkind) :: hydCondWettingFront ! hydraulic conductivity at the wetting front (m s-1) + real(rkind) :: rootZoneLiq ! depth of liquid water in the root zone (m) + real(rkind) :: rootZoneIce ! depth of ice in the root zone (m) + real(rkind) :: availCapacity ! available storage capacity in the root zone (m) + real(rkind) :: depthWettingFront ! depth to the wetting front (m) + real(rkind) :: hydCondWettingFront ! hydraulic conductivity at the wetting front (m s-1) ! saturated area associated with variable storage capacity - real(rkind) :: fracCap ! fraction of pore space filled with liquid water and ice (-) - real(rkind) :: fInfRaw ! infiltrating area before imposing solution constraints (-) - real(rkind),parameter :: maxFracCap=0.995_rkind ! maximum fraction capacity -- used to avoid numerical problems associated with an enormous derivative - real(rkind),parameter :: scaleFactor=0.000001_rkind ! scale factor for the smoothing function (-) - real(rkind),parameter :: qSurfScaleMax=1000._rkind ! maximum surface runoff scaling factor (-) + real(rkind) :: fracCap ! fraction of pore space filled with liquid water and ice (-) + real(rkind) :: fInfRaw ! infiltrating area before imposing solution constraints (-) + real(rkind),parameter :: maxFracCap=0.995_rkind ! maximum fraction capacity -- used to avoid numerical problems associated with an enormous derivative + real(rkind),parameter :: scaleFactor=0.000001_rkind ! scale factor for the smoothing function (-) + real(rkind),parameter :: qSurfScaleMax=1000._rkind ! maximum surface runoff scaling factor (-) ! fraction of impermeable area associated with frozen ground - real(rkind) :: alpha ! shape parameter in the Gamma distribution - real(rkind) :: xLimg ! upper limit of the integral + real(rkind) :: alpha ! shape parameter in the Gamma distribution + real(rkind) :: xLimg ! upper limit of the integral ! derivatives - real(rkind) :: dVolFracLiq_dWat(1:nSoil) ! derivative in vol fraction of liquid w.r.t. water state variable in root layers - real(rkind) :: dVolFracIce_dWat(1:nSoil) ! derivative in vol fraction of ice w.r.t. water state variable in root layers - real(rkind) :: dVolFracLiq_dTk(1:nSoil) ! derivative in vol fraction of liquid w.r.t. temperature in root layers - real(rkind) :: dVolFracIce_dTk(1:nSoil) ! derivative in vol fraction of ice w.r.t. temperature in root layers + real(rkind) :: dVolFracLiq_dWat(1:nSoil) ! derivative in vol fraction of liquid w.r.t. water state variable in root layers + real(rkind) :: dVolFracIce_dWat(1:nSoil) ! derivative in vol fraction of ice w.r.t. water state variable in root layers + real(rkind) :: dVolFracLiq_dTk(1:nSoil) ! derivative in vol fraction of liquid w.r.t. temperature in root layers + real(rkind) :: dVolFracIce_dTk(1:nSoil) ! derivative in vol fraction of ice w.r.t. temperature in root layers real(rkind) :: dRootZoneLiq_dWat(1:nSoil) ! derivative in vol fraction of scalar root zone liquid w.r.t. water state variable in root layers real(rkind) :: dRootZoneIce_dWat(1:nSoil) ! derivative in vol fraction of scalar root zone ice w.r.t. water state variable in root layers real(rkind) :: dRootZoneLiq_dTk(1:nSoil) ! derivative in vol fraction of scalar root zone liquid w.r.t. temperature in root layers @@ -1001,7 +996,7 @@ subroutine surfaceFlx(& ! surface runoff iz zero for the head condition scalarSurfaceRunoff = 0._rkind ! compute transmission and the capillary flux - select case(ixRichards) ! (form of Richards' equation) + select case(ixRichards) ! select form of Richards' equation case(moisture) ! compute the hydraulic conductivity and diffusivity at the boundary surfaceHydCond = hydCond_liq(upperBoundTheta,surfaceSatHydCond,theta_res,theta_sat,vGn_m) * iceImpedeFac @@ -1015,13 +1010,13 @@ subroutine surfaceFlx(& ! compute the capillary flux cflux = -surfaceHydCond*(scalarMatricHeadLiq - upperBoundHead) / (mLayerDepth(1)*0.5_rkind) case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return - end select ! (form of Richards' eqn) + end select ! end select form of Richards' eqn ! compute the total flux scalarSurfaceInfiltration = cflux + surfaceHydCond ! compute the derivative - if(deriv_desired)then + if (deriv_desired) then ! compute the hydrology derivative at the surface - select case(ixRichards) ! (form of Richards' equation) + select case(ixRichards) ! select form of Richards' equation case(moisture); dq_dHydStateVec(1) = -surfaceDiffuse/(mLayerDepth(1)/2._rkind) case(mixdform); dq_dHydStateVec(1) = -surfaceHydCond/(mLayerDepth(1)/2._rkind) case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return @@ -1036,13 +1031,13 @@ subroutine surfaceFlx(& ! flux condition case(liquidFlux) ! force infiltration to be constant over the iterations - if(firstSplitOper)then + if (firstSplitOper) then ! process root layers only liquid and ice derivatives dVolFracLiq_dWat(:) = 0._rkind dVolFracIce_dWat(:) = 0._rkind dVolFracLiq_dTk(:) = 0._rkind dVolFracIce_dTk(:) = 0._rkind - if(deriv_desired .and. nRoots > 0)then + if (deriv_desired .and. nRoots > 0) then select case(ixRichards) ! form of Richards' equation case(moisture) dVolFracLiq_dWat(:) = 1._rkind @@ -1050,18 +1045,18 @@ subroutine surfaceFlx(& case(mixdform) do iLayer=1,nRoots Tcrit = crit_soilT( mLayerMatricHead(iLayer) ) - if(mLayerTemp(iLayer) < Tcrit)then + if (mLayerTemp(iLayer) < Tcrit) then dVolFracLiq_dWat(iLayer) = 0._rkind dVolFracIce_dWat(iLayer) = dTheta_dPsi(iLayer) else dVolFracLiq_dWat(iLayer) = dTheta_dPsi(iLayer) dVolFracIce_dWat(iLayer) = 0._rkind - endif + end if enddo end select ! (form of Richards' equation) dVolFracLiq_dTk(:) = dTheta_dTk(:) !already zeroed out if not below critical temperature dVolFracIce_dTk(:) = -dVolFracLiq_dTk(:) !often can and will simplify one of these terms out - endif + end if ! define the storage in the root zone (m) and derivatives rootZoneLiq = 0._rkind @@ -1072,7 +1067,7 @@ subroutine surfaceFlx(& dRootZoneIce_dTk(:) = 0._rkind ! process layers where the roots extend to the bottom of the layer - if(nRoots > 1)then + if (nRoots > 1) then do iLayer=1,nRoots-1 rootZoneLiq = rootZoneLiq + mLayerVolFracLiq(iLayer)*mLayerDepth(iLayer) rootZoneIce = rootZoneIce + mLayerVolFracIce(iLayer)*mLayerDepth(iLayer) @@ -1092,7 +1087,7 @@ subroutine surfaceFlx(& ! define available capacity to hold water (m) availCapacity = theta_sat*rootingDepth - rootZoneIce - if(rootZoneLiq > availCapacity+verySmall)then + if (rootZoneLiq > availCapacity+verySmall) then message=trim(message)//'liquid water in the root zone exceeds capacity' err=20; return end if @@ -1107,7 +1102,6 @@ subroutine surfaceFlx(& ! define the maximum infiltration rate (m s-1) and derivatives xMaxInfilRate = hydCondWettingFront*( (wettingFrontSuction + depthWettingFront)/depthWettingFront ) ! maximum infiltration rate (m s-1) - !write(*,'(a,1x,f9.3,1x,10(e20.10,1x))') 'depthWettingFront, surfaceSatHydCond, hydCondWettingFront, xMaxInfilRate = ', depthWettingFront, surfaceSatHydCond, hydCondWettingFront, xMaxInfilRate fPart1 = hydCondWettingFront fPart2 = (wettingFrontSuction + depthWettingFront)/depthWettingFront dPart1(:) = surfaceSatHydCond*(zScale_TOPMODEL - 1._rkind) * ( (1._rkind - depthWettingFront/sum(mLayerDepth))**(zScale_TOPMODEL - 2._rkind) ) * (-dDepthWettingFront_dWat(:))/sum(mLayerDepth) @@ -1118,7 +1112,7 @@ subroutine surfaceFlx(& dxMaxInfilRate_dTk(:) = fPart1*dpart2(:) + fPart2*dPart1(:) ! define the infiltrating area and derivatives for the non-frozen part of the cell/basin - if(qSurfScale < qSurfScaleMax)then + if (qSurfScale < qSurfScaleMax) then fracCap = rootZoneLiq/(maxFracCap*availCapacity) ! fraction of available root zone filled with water fInfRaw = 1._rkind - exp(-qSurfScale*(1._rkind - fracCap)) ! infiltrating area -- allowed to violate solution constraints scalarInfilArea = min(0.5_rkind*(fInfRaw + sqrt(fInfRaw**2_i4b + scaleFactor)), 1._rkind) ! infiltrating area -- constrained @@ -1132,22 +1126,22 @@ subroutine surfaceFlx(& else ! scalarInfilArea = 1._rkind dInfilArea_dWat(1:nSoil) = 0._rkind dInfilArea_dTk(1:nSoil) = 0._rkind - endif + end if else scalarInfilArea = 1._rkind dInfilArea_dWat(1:nSoil) = 0._rkind dInfilArea_dTk(1:nSoil) = 0._rkind - endif + end if dInfilArea_dWat(0) = 0._rkind dInfilArea_dTk(0) = 0._rkind ! check to ensure we are not infiltrating into a fully saturated column - if(ixIce 0.9999_rkind*theta_sat*sum(mLayerDepth(ixIce+1:nRoots))) scalarInfilArea=0._rkind - endif + if (ixIce 0.9999_rkind*theta_sat*sum(mLayerDepth(ixIce+1:nRoots))) scalarInfilArea=0._rkind + end if ! define the impermeable area and derivatives due to frozen ground - if(rootZoneIce > tiny(rootZoneIce))then ! (avoid divide by zero) + if (rootZoneIce > tiny(rootZoneIce)) then ! (avoid divide by zero) alpha = 1._rkind/(soilIceCV**2_i4b) ! shape parameter in the Gamma distribution xLimg = alpha*soilIceScale/rootZoneIce ! upper limit of the integral @@ -1174,7 +1168,7 @@ subroutine surfaceFlx(& dInfilRate_dTk(0) = above_soilLiqFluxDeriv*above_soildLiq_dTk dInfilRate_dWat(1:nSoil) = 0._rkind dInfilRate_dTk(1:nSoil) = 0._rkind - endif + end if ! dq w.r.t. infiltration only, scalarRainPlusMelt accounted for in computJacob module dq_dHydStateVec(:) = (1._rkind - scalarFrozenArea) * ( dInfilArea_dWat(:)*min(scalarRainPlusMelt,xMaxInfilRate) + scalarInfilArea*dInfilRate_dWat(:) ) +& @@ -1186,7 +1180,7 @@ subroutine surfaceFlx(& dq_dHydStateVec(:) = 0._rkind dq_dNrgStateVec(:) = 0._rkind - end if ! (if desire to compute infiltration) + end if ! end if desire to compute infiltration ! compute infiltration (m s-1), if after first flux call in a splitting operation does not change scalarSurfaceInfiltration = (1._rkind - scalarFrozenArea)*scalarInfilArea*min(scalarRainPlusMelt,xMaxInfilRate) @@ -1200,7 +1194,7 @@ subroutine surfaceFlx(& case default; err=20; message=trim(message)//'unknown upper boundary condition for soil hydrology'; return - end select ! (type of upper boundary condition) + end select ! end select type of upper boundary condition end subroutine surfaceFlx @@ -1210,23 +1204,23 @@ end subroutine surfaceFlx ! *************************************************************************************************************** subroutine iLayerFlux(& ! input: model control - deriv_desired, & ! intent(in): flag indicating if derivatives are desired - ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) + deriv_desired, & ! intent(in): flag indicating if derivatives are desired + ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) ! input: state variables (adjacent layers) - nodeMatricHeadLiqTrial, & ! intent(in): liquid matric head at the soil nodes (m) - nodeVolFracLiqTrial, & ! intent(in): volumetric liquid water content at the soil nodes (-) + nodeMatricHeadLiqTrial, & ! intent(in): liquid matric head at the soil nodes (m) + nodeVolFracLiqTrial, & ! intent(in): volumetric liquid water content at the soil nodes (-) ! input: model coordinate variables (adjacent layers) - nodeHeight, & ! intent(in): height of the soil nodes (m) + nodeHeight, & ! intent(in): height of the soil nodes (m) ! input: temperature derivatives - dPsiLiq_dTemp, & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) - dHydCond_dTemp, & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) + dPsiLiq_dTemp, & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) + dHydCond_dTemp, & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) ! input: transmittance (adjacent layers) - nodeHydCondTrial, & ! intent(in): hydraulic conductivity at the soil nodes (m s-1) - nodeDiffuseTrial, & ! intent(in): hydraulic diffusivity at the soil nodes (m2 s-1) + nodeHydCondTrial, & ! intent(in): hydraulic conductivity at the soil nodes (m s-1) + nodeDiffuseTrial, & ! intent(in): hydraulic diffusivity at the soil nodes (m2 s-1) ! input: transmittance derivatives (adjacent layers) - dHydCond_dVolLiq, & ! intent(in): derivative in hydraulic conductivity w.r.t. change in volumetric liquid water content (m s-1) - dDiffuse_dVolLiq, & ! intent(in): derivative in hydraulic diffusivity w.r.t. change in volumetric liquid water content (m2 s-1) - dHydCond_dMatric, & ! intent(in): derivative in hydraulic conductivity w.r.t. change in matric head (s-1) + dHydCond_dVolLiq, & ! intent(in): derivative in hydraulic conductivity w.r.t. change in volumetric liquid water content (m s-1) + dDiffuse_dVolLiq, & ! intent(in): derivative in hydraulic diffusivity w.r.t. change in volumetric liquid water content (m2 s-1) + dHydCond_dMatric, & ! intent(in): derivative in hydraulic conductivity w.r.t. change in matric head (s-1) ! output: tranmsmittance at the layer interface (scalars) iLayerHydCond, & ! intent(out): hydraulic conductivity at the interface between layers (m s-1) iLayerDiffuse, & ! intent(out): hydraulic diffusivity at the interface between layers (m2 s-1) @@ -1240,10 +1234,10 @@ subroutine iLayerFlux(& dq_dNrgStateBelow, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) ! output: error control err,message) ! intent(out): error control - ! ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + ! ----------------------------------------------------------------------------------------------------------------------------------------------------------------- ! input: model control - logical(lgt),intent(in) :: deriv_desired ! flag indicating if derivatives are desired - integer(i4b),intent(in) :: ixRichards ! index defining the option for Richards' equation (moisture or mixdform) + logical(lgt),intent(in) :: deriv_desired ! flag indicating if derivatives are desired + integer(i4b),intent(in) :: ixRichards ! index defining the option for Richards' equation (moisture or mixdform) ! input: state variables real(rkind),intent(in) :: nodeMatricHeadLiqTrial(:) ! liquid matric head at the soil nodes (m) real(rkind),intent(in) :: nodeVolFracLiqTrial(:) ! volumetric fraction of liquid water at the soil nodes (-) @@ -1271,13 +1265,13 @@ subroutine iLayerFlux(& real(rkind),intent(out) :: dq_dNrgStateAbove ! derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) real(rkind),intent(out) :: dq_dNrgStateBelow ! derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ! local variables (named variables to provide index of 2-element vectors) - integer(i4b),parameter :: ixUpper=1 ! index of upper node in the 2-element vectors - integer(i4b),parameter :: ixLower=2 ! index of lower node in the 2-element vectors - logical(lgt),parameter :: useGeometric=.false. ! switch between the arithmetic and geometric mean + integer(i4b),parameter :: ixUpper=1 ! index of upper node in the 2-element vectors + integer(i4b),parameter :: ixLower=2 ! index of lower node in the 2-element vectors + logical(lgt),parameter :: useGeometric=.false. ! switch between the arithmetic and geometric mean ! local variables (Darcy flux) real(rkind) :: dPsi ! spatial difference in matric head (m) real(rkind) :: dLiq ! spatial difference in volumetric liquid water (-) @@ -1290,14 +1284,14 @@ subroutine iLayerFlux(& real(rkind) :: dDiffuseIface_dVolLiqBelow ! derivative in hydraulic diffusivity at layer interface w.r.t. volumetric liquid water content in layer below real(rkind) :: dHydCondIface_dMatricAbove ! derivative in hydraulic conductivity at layer interface w.r.t. matric head in layer above real(rkind) :: dHydCondIface_dMatricBelow ! derivative in hydraulic conductivity at layer interface w.r.t. matric head in layer below - ! ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + ! ----------------------------------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="iLayerFlux/" + err=0; message="iLayerFlux/" ! initialize error control ! ***** ! compute the vertical flux of liquid water ! compute the hydraulic conductivity at the interface - if(useGeometric)then + if (useGeometric) then iLayerHydCond = sqrt(nodeHydCondTrial(ixLower) * nodeHydCondTrial(ixUpper)) else iLayerHydCond = (nodeHydCondTrial(ixLower) + nodeHydCondTrial(ixUpper))*0.5_rkind @@ -1305,7 +1299,7 @@ subroutine iLayerFlux(& dz = nodeHeight(ixLower) - nodeHeight(ixUpper) ! compute the capillary flux - select case(ixRichards) ! (form of Richards' equation) + select case(ixRichards) ! select form of Richards' equation case(moisture) iLayerDiffuse = sqrt(nodeDiffuseTrial(ixLower) * nodeDiffuseTrial(ixUpper)) dLiq = nodeVolFracLiqTrial(ixLower) - nodeVolFracLiqTrial(ixUpper) @@ -1320,11 +1314,11 @@ subroutine iLayerFlux(& iLayerLiqFluxSoil = cflux + iLayerHydCond ! ** compute the derivatives - if(deriv_desired)then - select case(ixRichards) ! (form of Richards' equation) + if (deriv_desired) then + select case(ixRichards) ! select form of Richards' equation case(moisture) ! still need to implement arithmetric mean for the moisture-based form - if(.not.useGeometric)then + if (.not.useGeometric) then message=trim(message)//'only currently implemented for geometric mean -- change local flag' err=20; return end if @@ -1339,7 +1333,7 @@ subroutine iLayerFlux(& dq_dHydStateBelow = -dDiffuseIface_dVolLiqBelow*dLiq/dz - iLayerDiffuse/dz + dHydCondIface_dVolLiqBelow case(mixdform) ! derivatives in hydraulic conductivity - if(useGeometric)then + if (useGeometric) then dHydCondIface_dMatricAbove = dHydCond_dMatric(ixUpper)*nodeHydCondTrial(ixLower) * 0.5_rkind/max(iLayerHydCond,verySmall) dHydCondIface_dMatricBelow = dHydCond_dMatric(ixLower)*nodeHydCondTrial(ixUpper) * 0.5_rkind/max(iLayerHydCond,verySmall) else @@ -1361,43 +1355,42 @@ subroutine iLayerFlux(& end subroutine iLayerFlux - ! *************************************************************************************************************** ! private subroutine qDrainFlux: compute the drainage flux from the bottom of the soil profile and its derivative ! *************************************************************************************************************** subroutine qDrainFlux(& ! input: model control - deriv_desired, & ! intent(in): flag indicating if derivatives are desired - ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) - bc_lower, & ! intent(in): index defining the type of boundary conditions + deriv_desired, & ! intent(in): flag indicating if derivatives are desired + ixRichards, & ! intent(in): index defining the form of Richards' equation (moisture or mixdform) + bc_lower, & ! intent(in): index defining the type of boundary conditions ! input: state variables - nodeMatricHeadLiq, & ! intent(in): liquid matric head in the lowest unsaturated node (m) - nodeVolFracLiq, & ! intent(in): volumetric liquid water content the lowest unsaturated node (-) + nodeMatricHeadLiq, & ! intent(in): liquid matric head in the lowest unsaturated node (m) + nodeVolFracLiq, & ! intent(in): volumetric liquid water content the lowest unsaturated node (-) ! input: model coordinate variables - nodeDepth, & ! intent(in): depth of the lowest unsaturated soil layer (m) - nodeHeight, & ! intent(in): height of the lowest unsaturated soil node (m) + nodeDepth, & ! intent(in): depth of the lowest unsaturated soil layer (m) + nodeHeight, & ! intent(in): height of the lowest unsaturated soil node (m) ! input: boundary conditions - lowerBoundHead, & ! intent(in): lower boundary condition (m) - lowerBoundTheta, & ! intent(in): lower boundary condition (-) + lowerBoundHead, & ! intent(in): lower boundary condition (m) + lowerBoundTheta, & ! intent(in): lower boundary condition (-) ! input: derivative in soil water characteristix - node_dPsi_dTheta, & ! intent(in): derivative of the soil moisture characteristic w.r.t. theta (m) + node_dPsi_dTheta, & ! intent(in): derivative of the soil moisture characteristic w.r.t. theta (m) ! input: transmittance - surfaceSatHydCond, & ! intent(in): saturated hydraulic conductivity at the surface (m s-1) - bottomSatHydCond, & ! intent(in): saturated hydraulic conductivity at the bottom of the unsaturated zone (m s-1) - nodeHydCond, & ! intent(in): hydraulic conductivity at the node itself (m s-1) - iceImpedeFac, & ! intent(in): ice impedence factor in the lower-most soil layer (-) + surfaceSatHydCond, & ! intent(in): saturated hydraulic conductivity at the surface (m s-1) + bottomSatHydCond, & ! intent(in): saturated hydraulic conductivity at the bottom of the unsaturated zone (m s-1) + nodeHydCond, & ! intent(in): hydraulic conductivity at the node itself (m s-1) + iceImpedeFac, & ! intent(in): ice impedence factor in the lower-most soil layer (-) ! input: transmittance derivatives - dHydCond_dVolLiq, & ! intent(in): derivative in hydraulic conductivity w.r.t. volumetric liquid water content (m s-1) - dHydCond_dMatric, & ! intent(in): derivative in hydraulic conductivity w.r.t. matric head (s-1) - dHydCond_dTemp, & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) + dHydCond_dVolLiq, & ! intent(in): derivative in hydraulic conductivity w.r.t. volumetric liquid water content (m s-1) + dHydCond_dMatric, & ! intent(in): derivative in hydraulic conductivity w.r.t. matric head (s-1) + dHydCond_dTemp, & ! intent(in): derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) ! input: soil parameters - vGn_alpha, & ! intent(in): van Genutchen "alpha" parameter (m-1) - vGn_n, & ! intent(in): van Genutchen "n" parameter (-) - vGn_m, & ! intent(in): van Genutchen "m" parameter (-) - theta_sat, & ! intent(in): soil porosity (-) - theta_res, & ! intent(in): soil residual volumetric water content (-) - kAnisotropic, & ! intent(in): anisotropy factor for lateral hydraulic conductivity (-) - zScale_TOPMODEL, & ! intent(in): TOPMODEL scaling factor (m) + vGn_alpha, & ! intent(in): van Genutchen "alpha" parameter (m-1) + vGn_n, & ! intent(in): van Genutchen "n" parameter (-) + vGn_m, & ! intent(in): van Genutchen "m" parameter (-) + theta_sat, & ! intent(in): soil porosity (-) + theta_res, & ! intent(in): soil residual volumetric water content (-) + kAnisotropic, & ! intent(in): anisotropy factor for lateral hydraulic conductivity (-) + zScale_TOPMODEL, & ! intent(in): TOPMODEL scaling factor (m) ! output: hydraulic conductivity and diffusivity at the surface bottomHydCond, & ! intent(out): hydraulic conductivity at the bottom of the unsatuarted zone (m s-1) bottomDiffuse, & ! intent(out): hydraulic diffusivity at the bottom of the unsatuarted zone (m2 s-1) @@ -1408,18 +1401,18 @@ subroutine qDrainFlux(& dq_dNrgStateUnsat, & ! intent(out): change in drainage flux w.r.t. change in energy state variable in lowest unsaturated node (m s-1 K-1) ! output: error control err,message) ! intent(out): error control - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water as a function of matric head (-) - USE soil_utils_module,only:matricHead ! compute matric head as a function of volumetric fraction of liquid water (m) - USE soil_utils_module,only:hydCond_psi ! compute hydraulic conductivity as a function of matric head (m s-1) - USE soil_utils_module,only:hydCond_liq ! compute hydraulic conductivity as a function of volumetric liquid water content (m s-1) - USE soil_utils_module,only:dPsi_dTheta ! compute derivative of the soil moisture characteristic w.r.t. theta (m) + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water as a function of matric head (-) + USE soil_utils_module,only:matricHead ! compute matric head as a function of volumetric fraction of liquid water (m) + USE soil_utils_module,only:hydCond_psi ! compute hydraulic conductivity as a function of matric head (m s-1) + USE soil_utils_module,only:hydCond_liq ! compute hydraulic conductivity as a function of volumetric liquid water content (m s-1) + USE soil_utils_module,only:dPsi_dTheta ! compute derivative of the soil moisture characteristic w.r.t. theta (m) ! compute infiltraton at the surface and its derivative w.r.t. mass in the upper soil layer implicit none ! ----------------------------------------------------------------------------------------------------------------------------- ! input: model control - logical(lgt),intent(in) :: deriv_desired ! flag to indicate if derivatives are desired - integer(i4b),intent(in) :: ixRichards ! index defining the option for Richards' equation (moisture or mixdform) - integer(i4b),intent(in) :: bc_lower ! index defining the type of boundary conditions + logical(lgt),intent(in) :: deriv_desired ! flag to indicate if derivatives are desired + integer(i4b),intent(in) :: ixRichards ! index defining the option for Richards' equation (moisture or mixdform) + integer(i4b),intent(in) :: bc_lower ! index defining the type of boundary conditions ! input: state and diagnostic variables real(rkind),intent(in) :: nodeMatricHeadLiq ! liquid matric head in the lowest unsaturated node (m) real(rkind),intent(in) :: nodeVolFracLiq ! volumetric liquid water content in the lowest unsaturated node (-) @@ -1430,7 +1423,7 @@ subroutine qDrainFlux(& real(rkind),intent(in) :: lowerBoundHead ! lower boundary condition for matric head (m) real(rkind),intent(in) :: lowerBoundTheta ! lower boundary condition for volumetric liquid water content (-) ! input: derivative in soil water characteristix - real(rkind),intent(in) :: node_dPsi_dTheta ! derivative of the soil moisture characteristic w.r.t. theta (m) + real(rkind),intent(in) :: node_dPsi_dTheta ! derivative of the soil moisture characteristic w.r.t. theta (m) ! input: transmittance real(rkind),intent(in) :: surfaceSatHydCond ! saturated hydraulic conductivity at the surface (m s-1) real(rkind),intent(in) :: bottomSatHydCond ! saturated hydraulic conductivity at the bottom of the unsaturated zone (m s-1) @@ -1458,8 +1451,8 @@ subroutine qDrainFlux(& real(rkind),intent(out) :: dq_dHydStateUnsat ! change in drainage flux w.r.t. change in state variable in lowest unsaturated node (m s-1 or s-1) real(rkind),intent(out) :: dq_dNrgStateUnsat ! change in drainage flux w.r.t. change in energy state variable in lowest unsaturated node (m s-1 K-1) ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! ----------------------------------------------------------------------------------------------------------------------------- ! local variables real(rkind) :: zWater ! effective water table depth (m) @@ -1475,83 +1468,79 @@ subroutine qDrainFlux(& case(prescribedHead) ! compute flux select case(ixRichards) - case(moisture) ! (moisture-based form of Richards' equation) + case(moisture) ! moisture-based form of Richards' equation ! compute the hydraulic conductivity and diffusivity at the boundary bottomHydCond = hydCond_liq(lowerBoundTheta,bottomSatHydCond,theta_res,theta_sat,vGn_m) * iceImpedeFac bottomDiffuse = dPsi_dTheta(lowerBoundTheta,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * bottomHydCond ! compute the capillary flux cflux = -bottomDiffuse*(lowerBoundTheta - nodeVolFracLiq) / (nodeDepth*0.5_rkind) - case(mixdform) + case(mixdform) ! mixed form of Richards' equation ! compute the hydraulic conductivity and diffusivity at the boundary bottomHydCond = hydCond_psi(lowerBoundHead,bottomSatHydCond,vGn_alpha,vGn_n,vGn_m) * iceImpedeFac bottomDiffuse = realMissing ! compute the capillary flux cflux = -bottomHydCond*(lowerBoundHead - nodeMatricHeadLiq) / (nodeDepth*0.5_rkind) case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return - end select ! (form of Richards' eqn) + end select ! end select form of Richards' eqn scalarDrainage = cflux + bottomHydCond - ! compute derivatives - if(deriv_desired)then + if (deriv_desired) then ! compute derivatives ! hydrology derivatives - select case(ixRichards) ! (form of Richards' equation) + select case(ixRichards) ! select form of Richards' equation case(moisture); dq_dHydStateUnsat = bottomDiffuse/(nodeDepth/2._rkind) case(mixdform); dq_dHydStateUnsat = bottomHydCond/(nodeDepth/2._rkind) case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return end select ! energy derivatives dq_dNrgStateUnsat = -(dHydCond_dTemp/2._rkind)*(lowerBoundHead - nodeMatricHeadLiq)/(nodeDepth*0.5_rkind) + dHydCond_dTemp/2._rkind - else ! (do not desire derivatives) + else ! do not desire derivatives dq_dHydStateUnsat = realMissing dq_dNrgStateUnsat = realMissing end if - case(funcBottomHead) !function of matric head in the bottom layer + case(funcBottomHead) ! function of matric head in the bottom layer ! compute flux - select case(ixRichards) + select case(ixRichards) ! select form of Richards' equation case(moisture); nodePsi = matricHead(nodeVolFracLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) case(mixdform); nodePsi = nodeMatricHeadLiq end select zWater = nodeHeight - nodePsi scalarDrainage = kAnisotropic*surfaceSatHydCond * exp(-zWater/zScale_TOPMODEL) - ! compute derivatives - if(deriv_desired)then + if (deriv_desired) then ! compute derivatives ! hydrology derivatives - select case(ixRichards) ! (form of Richards' equation) + select case(ixRichards) ! select form of Richards' equation case(moisture); dq_dHydStateUnsat = kAnisotropic*surfaceSatHydCond * node_dPsi_dTheta*exp(-zWater/zScale_TOPMODEL)/zScale_TOPMODEL case(mixdform); dq_dHydStateUnsat = kAnisotropic*surfaceSatHydCond * exp(-zWater/zScale_TOPMODEL)/zScale_TOPMODEL case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return end select ! energy derivatives err=20; message=trim(message)//"not yet implemented energy derivatives"; return - else ! (do not desire derivatives) + else ! do not desire derivatives dq_dHydStateUnsat = realMissing dq_dNrgStateUnsat = realMissing end if case(freeDrainage) - ! compute flux - scalarDrainage = nodeHydCond*kAnisotropic + scalarDrainage = nodeHydCond*kAnisotropic ! compute flux - ! compute derivatives - if(deriv_desired)then + if (deriv_desired) then ! compute derivatives ! hydrology derivatives - select case(ixRichards) ! (form of Richards' equation) + select case(ixRichards) ! select form of Richards' equation case(moisture); dq_dHydStateUnsat = dHydCond_dVolLiq*kAnisotropic case(mixdform); dq_dHydStateUnsat = dHydCond_dMatric*kAnisotropic case default; err=10; message=trim(message)//"unknown form of Richards' equation"; return end select ! energy derivatives dq_dNrgStateUnsat = dHydCond_dTemp*kAnisotropic - else ! (do not desire derivatives) + else ! do not desire derivatives dq_dHydStateUnsat = realMissing dq_dNrgStateUnsat = realMissing end if case(zeroFlux) scalarDrainage = 0._rkind - if(deriv_desired)then + if (deriv_desired) then dq_dHydStateUnsat = 0._rkind dq_dNrgStateUnsat = 0._rkind else @@ -1561,7 +1550,7 @@ subroutine qDrainFlux(& case default; err=20; message=trim(message)//'unknown lower boundary condition for soil hydrology'; return - end select ! (type of boundary condition) + end select ! end select type of boundary condition end subroutine qDrainFlux diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index ee6de9efc..c2ad055ce 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -217,7 +217,7 @@ subroutine ssdNrgFlux(& ! ***** compute the total fluxes at layer interfaces ***** ! ------------------------------------------------------------------------------------------------------------------------- ! NOTE: ignore advective fluxes for now - iLayerNrgFlux(0) = groundNetFlux !from vegNrgFlux module + iLayerNrgFlux(0) = groundNetFlux ! from vegNrgFlux module iLayerNrgFlux(ixTop:ixBot) = iLayerConductiveFlux(ixTop:ixBot) ! ------------------------------------------------------------------------------------------------------------------- From 7734f441504bc1000dd1029050637522201a863c Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 17 Aug 2023 06:22:19 -0600 Subject: [PATCH 0867/1472] Applied refactoring steps to v4 code blocks. --- build/source/engine/bigAquifer.f90 | 12 +- build/source/engine/groundwatr.f90 | 175 ++++++++++++++--------------- build/source/engine/snowLiqFlx.f90 | 6 +- build/source/engine/ssdNrgFlux.f90 | 34 +++--- 4 files changed, 110 insertions(+), 117 deletions(-) diff --git a/build/source/engine/bigAquifer.f90 b/build/source/engine/bigAquifer.f90 index 37c3a807c..66400d358 100644 --- a/build/source/engine/bigAquifer.f90 +++ b/build/source/engine/bigAquifer.f90 @@ -45,10 +45,10 @@ subroutine bigAquifer(& scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) scalarSoilDrainage, & ! intent(in): soil drainage (m s-1) ! input: pre-computed derivatives - dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) ! input: diagnostic variables and parameters mpar_data, & ! intent(in): model parameter structure diag_data, & ! intent(in): diagnostic variable structure @@ -68,7 +68,7 @@ subroutine bigAquifer(& USE var_lookup,only:iLookDIAG ! named variables for structure elements USE var_lookup,only:iLookPARAM ! named variables for structure elements ! data types - USE data_types,only:var_dlength ! x%var(:)%dat (rkind) + USE data_types,only:var_dlength ! x%var(:)%dat [rkind] ! ------------------------------------------------------------------------------------------------------------------------------------------------- implicit none ! input: state variables, fluxes, and parameters @@ -114,7 +114,7 @@ subroutine bigAquifer(& aquiferBaseflowRate => mpar_data%var(iLookPARAM%aquiferBaseflowRate)%dat(1), & ! intent(in): [dp] tbaseflow rate when aquiferStorage = aquiferScaleFactor (m s-1) aquiferScaleFactor => mpar_data%var(iLookPARAM%aquiferScaleFactor)%dat(1), & ! intent(in): [dp] scaling factor for aquifer storage in the big bucket (m) aquiferBaseflowExp => mpar_data%var(iLookPARAM%aquiferBaseflowExp)%dat(1) & ! intent(in): [dp] baseflow exponent (-) - ) ! associating local variables with the information in the data structures + ) ! end associating local variables with the information in the data structures ! compute aquifer transpiration (m s-1) aquiferTranspireFrac = scalarAquiferRootFrac*scalarTranspireLimAqfr/scalarTranspireLim ! fraction of total transpiration that comes from the aquifer (-) diff --git a/build/source/engine/groundwatr.f90 b/build/source/engine/groundwatr.f90 index 71fa9fe5b..9e060fc25 100644 --- a/build/source/engine/groundwatr.f90 +++ b/build/source/engine/groundwatr.f90 @@ -102,41 +102,41 @@ subroutine groundwatr(& err,message) ! intent(out): error control ! --------------------------------------------------------------------------------------- ! utility modules - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water as a function of matric head - USE soil_utils_module,only:hydCond_psi ! compute hydraulic conductivity as a function of matric head + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water as a function of matric head + USE soil_utils_module,only:hydCond_psi ! compute hydraulic conductivity as a function of matric head implicit none ! --------------------------------------------------------------------------------------- ! * dummy variables ! --------------------------------------------------------------------------------------- ! input: model control - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - logical(lgt),intent(in) :: getSatDepth ! logical flag to compute index of the lowest saturated layer + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + logical(lgt),intent(in) :: getSatDepth ! logical flag to compute index of the lowest saturated layer ! input: state and diagnostic variables - real(rkind),intent(in) :: mLayerdTheta_dPsi(:) ! derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) - real(rkind),intent(in) :: mLayerMatricHeadLiq(:) ! matric head in each layer at the current iteration (m) - real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid water (-) - real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice (-) + real(rkind),intent(in) :: mLayerdTheta_dPsi(:) ! derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) + real(rkind),intent(in) :: mLayerMatricHeadLiq(:) ! matric head in each layer at the current iteration (m) + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid water (-) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice (-) ! input/output: data structures - type(var_d),intent(in) :: attr_data ! spatial attributes - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_d),intent(in) :: attr_data ! spatial attributes + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU ! output: baseflow - integer(i4b),intent(inout) :: ixSaturation ! index of lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: mLayerBaseflow(:) ! baseflow from each soil layer (m s-1) - real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + integer(i4b),intent(inout) :: ixSaturation ! index of lowest saturated layer (NOTE: only computed on the first iteration) + real(rkind),intent(out) :: mLayerBaseflow(:) ! baseflow from each soil layer (m s-1) + real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! --------------------------------------------------------------------------------------- ! * local variables ! --------------------------------------------------------------------------------------- ! general local variables - integer(i4b) :: iLayer ! index of soil layer - real(rkind),dimension(nSoil,nSoil) :: dBaseflow_dVolLiq ! derivative in the baseflow flux w.r.t. volumetric liquid water content (m s-1) + integer(i4b) :: iLayer ! index of soil layer + real(rkind),dimension(nSoil,nSoil) :: dBaseflow_dVolLiq ! derivative in the baseflow flux w.r.t. volumetric liquid water content (m s-1) ! *************************************************************************************** ! *************************************************************************************** ! initialize error control @@ -146,16 +146,16 @@ subroutine groundwatr(& ! associate variables in data structures associate(& ! input: baseflow parameters - fieldCapacity => mpar_data%var(iLookPARAM%fieldCapacity)%dat(1), & ! intent(in): [dp] field capacity (-) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): [dp] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat, & ! intent(in): [dp] residual volumetric water content (-) + fieldCapacity => mpar_data%var(iLookPARAM%fieldCapacity)%dat(1), & ! intent(in): [dp] field capacity (-) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): [dp] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat, & ! intent(in): [dp] residual volumetric water content (-) ! input: van Genuchten soil parametrers - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat, & ! intent(in): [dp] van Genutchen "alpha" parameter (m-1) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat, & ! intent(in): [dp] van Genutchen "n" parameter (-) - vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat, & ! intent(in): [dp] van Genutchen "m" parameter (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat, & ! intent(in): [dp] van Genutchen "alpha" parameter (m-1) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat, & ! intent(in): [dp] van Genutchen "n" parameter (-) + vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat, & ! intent(in): [dp] van Genutchen "m" parameter (-) ! output: diagnostic variables - scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1), & ! intent(out):[dp] exfiltration from the soil profile (m s-1) - mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat & ! intent(out):[dp(:)] column outflow from each soil layer (m3 s-1) + scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1), & ! intent(out): [dp] exfiltration from the soil profile (m s-1) + mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat & ! intent(out): [dp(:)] column outflow from each soil layer (m3 s-1) ) ! end association to variables in data structures ! ************************************************************************************************ @@ -168,7 +168,7 @@ subroutine groundwatr(& do iLayer=nSoil,1,-1 ! start at the lowest soil layer and work upwards to the top layer if (mLayerVolFracLiq(iLayer) > fieldCapacity) then; ixSaturation = iLayer ! index of saturated layer -- keeps getting over-written as move upwards else; exit; end if ! only consider saturated layer at the bottom of the soil profile - end do ! (looping through soil layers) + end do ! end looping through soil layers end if ! check for an early return (no layers are "active") @@ -220,21 +220,21 @@ end subroutine groundwatr ! *********************************************************************************************************************** subroutine computeBaseflow(& ! input: control and state variables - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - derivDesired, & ! intent(in): .true. if derivatives are desired - ixSaturation, & ! intent(in): index of upper-most "saturated" layer - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water in each soil layer (-) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice in each soil layer (-) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + derivDesired, & ! intent(in): .true. if derivatives are desired + ixSaturation, & ! intent(in): index of upper-most "saturated" layer + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water in each soil layer (-) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice in each soil layer (-) ! input/output: data structures attr_data, & ! intent(in): spatial attributes mpar_data, & ! intent(in): model parameters prog_data, & ! intent(in): model prognostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU ! output: fluxes and derivatives - mLayerBaseflow, & ! intent(out): baseflow flux in each soil layer (m s-1) - dBaseflow_dVolLiq) ! intent(out): derivative in baseflow w.r.t. volumetric liquid water content (s-1) + mLayerBaseflow, & ! intent(out): baseflow flux in each soil layer (m s-1) + dBaseflow_dVolLiq) ! intent(out): derivative in baseflow w.r.t. volumetric liquid water content (s-1) implicit none ! --------------------------------------------------------------------------------------- ! * dummy variables @@ -259,60 +259,54 @@ subroutine computeBaseflow(& ! * local variables ! --------------------------------------------------------------------------------------- ! general local variables - integer(i4b) :: iLayer,jLayer ! index of model layer + integer(i4b) :: iLayer,jLayer ! index of model layer ! local variables for the exfiltration - real(rkind) :: totalColumnInflow ! total column inflow (m s-1) - real(rkind) :: totalColumnOutflow ! total column outflow (m s-1) - real(rkind) :: availStorage ! available storage (m) - real(rkind),parameter :: xMinEval=0.002_rkind ! minimum value to evaluate the exfiltration function (m) - real(rkind),parameter :: xCenter=0.001_rkind ! center of the exfiltration function (m) - real(rkind),parameter :: xWidth=0.0001_rkind ! width of the exfiltration function (m) - real(rkind) :: expF,logF ! logistic smoothing function (-) + real(rkind) :: totalColumnInflow ! total column inflow (m s-1) + real(rkind) :: totalColumnOutflow ! total column outflow (m s-1) + real(rkind) :: availStorage ! available storage (m) + real(rkind),parameter :: xMinEval=0.002_rkind ! minimum value to evaluate the exfiltration function (m) + real(rkind),parameter :: xCenter=0.001_rkind ! center of the exfiltration function (m) + real(rkind),parameter :: xWidth=0.0001_rkind ! width of the exfiltration function (m) + real(rkind) :: expF,logF ! logistic smoothing function (-) ! local variables for the lateral flux among soil columns - real(rkind) :: activePorosity ! "active" porosity associated with storage above a threshold (-) - real(rkind) :: drainableWater ! drainable water in eaxch layer (m) - real(rkind) :: tran0 ! maximum transmissivity (m2 s-1) - real(rkind),dimension(nSoil) :: zActive ! water table thickness associated with storage below and including the given layer (m) - real(rkind),dimension(nSoil) :: trTotal ! total transmissivity associated with total water table depth zActive (m2 s-1) - real(rkind),dimension(nSoil) :: trSoil ! transmissivity of water in a given layer (m2 s-1) + real(rkind) :: activePorosity ! "active" porosity associated with storage above a threshold (-) + real(rkind) :: drainableWater ! drainable water in eaxch layer (m) + real(rkind) :: tran0 ! maximum transmissivity (m2 s-1) + real(rkind),dimension(nSoil) :: zActive ! water table thickness associated with storage below and including the given layer (m) + real(rkind),dimension(nSoil) :: trTotal ! total transmissivity associated with total water table depth zActive (m2 s-1) + real(rkind),dimension(nSoil) :: trSoil ! transmissivity of water in a given layer (m2 s-1) ! local variables for the derivatives - real(rkind) :: qbTotal ! total baseflow (m s-1) - real(rkind) :: length2area ! ratio of hillslope width to hillslope area (m m-2) - real(rkind),dimension(nSoil) :: depth2capacity ! ratio of layer depth to total subsurface storage capacity (-) - real(rkind),dimension(nSoil) :: dXdS ! change in dimensionless flux w.r.t. change in dimensionless storage (-) - real(rkind),dimension(nSoil) :: dLogFunc_dLiq ! derivative in the logistic function w.r.t. volumetric liquid water content (-) - real(rkind),dimension(nSoil) :: dExfiltrate_dVolLiq ! derivative in exfiltration w.r.t. volumetric liquid water content (-) + real(rkind) :: qbTotal ! total baseflow (m s-1) + real(rkind) :: length2area ! ratio of hillslope width to hillslope area (m m-2) + real(rkind),dimension(nSoil) :: depth2capacity ! ratio of layer depth to total subsurface storage capacity (-) + real(rkind),dimension(nSoil) :: dXdS ! change in dimensionless flux w.r.t. change in dimensionless storage (-) + real(rkind),dimension(nSoil) :: dLogFunc_dLiq ! derivative in the logistic function w.r.t. volumetric liquid water content (-) + real(rkind),dimension(nSoil) :: dExfiltrate_dVolLiq ! derivative in exfiltration w.r.t. volumetric liquid water content (-) ! local variables for testing (debugging) - logical(lgt),parameter :: printFlag=.false. ! flag for printing (debugging) - real(rkind) :: xDepth,xTran,xFlow ! temporary variables (depth, transmissivity, flow) + logical(lgt),parameter :: printFlag=.false. ! flag for printing (debugging) + real(rkind) :: xDepth,xTran,xFlow ! temporary variables (depth, transmissivity, flow) ! --------------------------------------------------------------------------------------- ! * association to data in structures ! --------------------------------------------------------------------------------------- associate(& - ! input: coordinate variables - soilDepth => prog_data%var(iLookPROG%iLayerHeight)%dat(nLayers), & ! intent(in): [dp] total soil depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers),& ! intent(in): [dp(:)] depth of each soil layer (m) - + soilDepth => prog_data%var(iLookPROG%iLayerHeight)%dat(nLayers), & ! intent(in): [dp] total soil depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers),& ! intent(in): [dp(:)] depth of each soil layer (m) ! input: diagnostic variables - surfaceHydCond => flux_data%var(iLookFLUX%mLayerSatHydCondMP)%dat(1), & ! intent(in): [dp] saturated hydraulic conductivity at the surface (m s-1) - mLayerColumnInflow => flux_data%var(iLookFLUX%mLayerColumnInflow)%dat, & ! intent(in): [dp(:)] inflow into each soil layer (m3/s) - + surfaceHydCond => flux_data%var(iLookFLUX%mLayerSatHydCondMP)%dat(1), & ! intent(in): [dp] saturated hydraulic conductivity at the surface (m s-1) + mLayerColumnInflow => flux_data%var(iLookFLUX%mLayerColumnInflow)%dat, & ! intent(in): [dp(:)] inflow into each soil layer (m3/s) ! input: local attributes - HRUarea => attr_data%var(iLookATTR%HRUarea), & ! intent(in): [dp] HRU area (m2) - tan_slope => attr_data%var(iLookATTR%tan_slope), & ! intent(in): [dp] tan water table slope, taken as tan local ground surface slope (-) - contourLength => attr_data%var(iLookATTR%contourLength), & ! intent(in): [dp] length of contour at downslope edge of HRU (m) - + HRUarea => attr_data%var(iLookATTR%HRUarea), & ! intent(in): [dp] HRU area (m2) + tan_slope => attr_data%var(iLookATTR%tan_slope), & ! intent(in): [dp] tan water table slope, taken as tan local ground surface slope (-) + contourLength => attr_data%var(iLookATTR%contourLength), & ! intent(in): [dp] length of contour at downslope edge of HRU (m) ! input: baseflow parameters - zScale_TOPMODEL => mpar_data%var(iLookPARAM%zScale_TOPMODEL)%dat(1), & ! intent(in): [dp] TOPMODEL exponent (-) - kAnisotropic => mpar_data%var(iLookPARAM%kAnisotropic)%dat(1), & ! intent(in): [dp] anisotropy factor for lateral hydraulic conductivity (- - fieldCapacity => mpar_data%var(iLookPARAM%fieldCapacity)%dat(1), & ! intent(in): [dp] field capacity (-) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): [dp(:)] soil porosity (-) - + zScale_TOPMODEL => mpar_data%var(iLookPARAM%zScale_TOPMODEL)%dat(1), & ! intent(in): [dp] TOPMODEL exponent (-) + kAnisotropic => mpar_data%var(iLookPARAM%kAnisotropic)%dat(1), & ! intent(in): [dp] anisotropy factor for lateral hydraulic conductivity (- + fieldCapacity => mpar_data%var(iLookPARAM%fieldCapacity)%dat(1), & ! intent(in): [dp] field capacity (-) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): [dp(:)] soil porosity (-) ! output: diagnostic variables - scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1), & ! intent(out):[dp] exfiltration from the soil profile (m s-1) - mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat & ! intent(out):[dp(:)] column outflow from each soil layer (m3 s-1) - + scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1), & ! intent(out): [dp] exfiltration from the soil profile (m s-1) + mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat & ! intent(out): [dp(:)] column outflow from each soil layer (m3 s-1) ) ! end association to variables in data structures ! *********************************************************************************************************************** @@ -338,7 +332,7 @@ subroutine computeBaseflow(& trTotal(iLayer) = tran0*(zActive(iLayer)/soilDepth)**zScale_TOPMODEL trSoil(iLayer) = trTotal(iLayer) - trTotal(iLayer+1) end if - end do ! looping through soil layers + end do ! end looping through soil layers ! set un-used portions of the vectors to zero if (ixSaturation>1) then @@ -359,10 +353,10 @@ subroutine computeBaseflow(& ! compute the smoothing function (-) if (availStorage < xMinEval) then - ! (compute the logistic function) + ! compute the logistic function expF = exp((availStorage - xCenter)/xWidth) logF = 1._rkind / (1._rkind + expF) - ! (compute the derivative in the logistic function w.r.t. volumetric liquid water content in each soil layer) + ! compute the derivative in the logistic function w.r.t. volumetric liquid water content in each soil layer dLogFunc_dLiq(1:nSoil) = mLayerDepth(1:nSoil)*(expF/xWidth)/(1._rkind + expF)**2_i4b else logF = 0._rkind @@ -408,7 +402,7 @@ subroutine computeBaseflow(& dBaseflow_dVolLiq(:,:) = 0._rkind ! check if derivatives are actually required - if(.not.derivDesired) return + if (.not.derivDesired) return ! compute ratio of hillslope width to hillslope area (m m-2) length2area = tan_slope*contourLength/HRUarea @@ -424,21 +418,20 @@ subroutine computeBaseflow(& ! compute diagonal terms (s-1) dBaseflow_dVolLiq(iLayer,iLayer) = tran0*dXdS(iLayer)*depth2capacity(iLayer)*length2area ! compute off-diagonal terms - do jLayer=iLayer+1,nSoil ! (only dependent on layers below) + do jLayer=iLayer+1,nSoil ! only dependent on layers below dBaseflow_dVolLiq(iLayer,jLayer) = tran0*(dXdS(iLayer) - dXdS(iLayer+1))*depth2capacity(jLayer)*length2area - end do ! looping through soil layers - end do ! looping through soil layers + end do ! end looping through soil layers + end do ! end looping through soil layers ! compute the derivative in the exfiltration flux w.r.t. volumetric liquid water content (m s-1) if (qbTotal < 0._rkind) then do iLayer=1,nSoil dExfiltrate_dVolLiq(iLayer) = dBaseflow_dVolLiq(iLayer,iLayer)*logF + dLogFunc_dLiq(iLayer)*qbTotal - end do ! looping through soil layers + end do ! end looping through soil layers dBaseflow_dVolLiq(1,1:nSoil) = dBaseflow_dVolLiq(1,1:nSoil) - dExfiltrate_dVolLiq(1:nSoil) end if - ! end association to data in structures - end associate + end associate ! end association to data in structures end subroutine computeBaseflow diff --git a/build/source/engine/snowLiqFlx.f90 b/build/source/engine/snowLiqFlx.f90 index 38ee31935..d79744421 100644 --- a/build/source/engine/snowLiqFlx.f90 +++ b/build/source/engine/snowLiqFlx.f90 @@ -35,9 +35,9 @@ module snowLiqFlx_module USE var_lookup,only:iLookDIAG ! named variables for structure elements ! data types -USE data_types,only:var_d ! x%var(:) (rkind) -USE data_types,only:var_dlength ! x%var(:)%dat (rkind) -USE data_types,only:var_ilength ! x%var(:)%dat (i4b) +USE data_types,only:var_d ! x%var(:) [rkind] +USE data_types,only:var_dlength ! x%var(:)%dat [rkind] +USE data_types,only:var_ilength ! x%var(:)%dat [i4b] ! privacy implicit none diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index c2ad055ce..a95e6ee74 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -24,9 +24,9 @@ module ssdNrgFlux_module USE nrtype ! data types -USE data_types,only:var_d ! x%var(:) (rkind) -USE data_types,only:var_dlength ! x%var(:)%dat (rkind) -USE data_types,only:var_ilength ! x%var(:)%dat (i4b) +USE data_types,only:var_d ! x%var(:) [rkind] +USE data_types,only:var_dlength ! x%var(:)%dat [rkind] +USE data_types,only:var_ilength ! x%var(:)%dat [i4b] ! physical constants USE multiconst,only:& @@ -66,7 +66,7 @@ module ssdNrgFlux_module private public :: ssdNrgFlux ! global parameters -real(rkind),parameter :: dx=1.e-10_rkind ! finite difference increment (K) +real(rkind),parameter :: dx=1.e-10_rkind ! finite difference increment (K) contains ! ********************************************************************************************************** ! public subroutine ssdNrgFlux: compute energy fluxes and derivatives at layer interfaces @@ -145,24 +145,24 @@ subroutine ssdNrgFlux(& ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! make association of local variables with information in the data structures associate(& - ix_bcUpprTdyn => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision, & ! intent(in): method used to calculate the upper boundary condition for thermodynamics - ix_bcLowrTdyn => model_decisions(iLookDECISIONS%bcLowrTdyn)%iDecision, & ! intent(in): method used to calculate the lower boundary condition for thermodynamics + ix_bcUpprTdyn => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision, & ! intent(in): method used to calculate the upper boundary condition for thermodynamics + ix_bcLowrTdyn => model_decisions(iLookDECISIONS%bcLowrTdyn)%iDecision, & ! intent(in): method used to calculate the lower boundary condition for thermodynamics ! input: coordinate variables - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers - layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) - ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat, & ! intent(in): index in the state subset for energy state variables in the snow+soil domain - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): depth of each layer (m) - mLayerHeight => prog_data%var(iLookPROG%mLayerHeight)%dat, & ! intent(in): height at the mid-point of each layer (m) + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers + layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) + ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat, & ! intent(in): index in the state subset for energy state variables in the snow+soil domain + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): depth of each layer (m) + mLayerHeight => prog_data%var(iLookPROG%mLayerHeight)%dat, & ! intent(in): height at the mid-point of each layer (m) ! input: thermal properties - upperBoundTemp => mpar_data%var(iLookPARAM%upperBoundTemp)%dat(1), & ! intent(in): temperature of the upper boundary (K) - lowerBoundTemp => mpar_data%var(iLookPARAM%lowerBoundTemp)%dat(1), & ! intent(in): temperature of the lower boundary (K) - iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(in): thermal conductivity at the interface of each layer (W m-1 K-1) + upperBoundTemp => mpar_data%var(iLookPARAM%upperBoundTemp)%dat(1), & ! intent(in): temperature of the upper boundary (K) + lowerBoundTemp => mpar_data%var(iLookPARAM%lowerBoundTemp)%dat(1), & ! intent(in): temperature of the lower boundary (K) + iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(in): thermal conductivity at the interface of each layer (W m-1 K-1) ! output: diagnostic fluxes iLayerConductiveFlux => flux_data%var(iLookFLUX%iLayerConductiveFlux)%dat, & ! intent(out): conductive energy flux at layer interfaces at end of time step (W m-2) iLayerAdvectiveFlux => flux_data%var(iLookFLUX%iLayerAdvectiveFlux)%dat & ! intent(out): advective energy flux at layer interfaces at end of time step (W m-2) - ) ! association of local variables with information in the data structures + ) ! end association of local variables with information in the data structures ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! initialize error control err=0; message='ssdNrgFlux/' From ba21a4cb6e0218d6b70513f634ae9aeb5ec1e7a5 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 13 May 2023 05:45:46 -0600 Subject: [PATCH 0868/1472] Refactoring performed for vegNrgFlux.f90 including arithmetic updates. --- build/source/engine/vegNrgFlux.f90 | 557 ++++++++++++++--------------- 1 file changed, 266 insertions(+), 291 deletions(-) diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index edcf86fe1..5f01be14c 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -43,82 +43,75 @@ module vegNrgFlux_module USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure ! constants -USE multiconst,only:gravity ! acceleration of gravity (m s-2) -USE multiconst,only:vkc ! von Karman's constant (-) -USE multiconst,only:w_ratio ! molecular ratio water to dry air (-) -USE multiconst,only:R_wv ! gas constant for water vapor (Pa K-1 m3 kg-1; J kg-1 K-1) -USE multiconst,only:Cp_air ! specific heat of air (J kg-1 K-1) -USE multiconst,only:Cp_ice ! specific heat of ice (J kg-1 K-1) -USE multiconst,only:Cp_soil ! specific heat of soil (J kg-1 K-1) -USE multiconst,only:Cp_water ! specific heat of liquid water (J kg-1 K-1) -USE multiconst,only:Tfreeze ! temperature at freezing (K) -USE multiconst,only:LH_fus ! latent heat of fusion (J kg-1) -USE multiconst,only:LH_vap ! latent heat of vaporization (J kg-1) -USE multiconst,only:LH_sub ! latent heat of sublimation (J kg-1) -USE multiconst,only:sb ! Stefan Boltzman constant (W m-2 K-4) -USE multiconst,only:iden_air ! intrinsic density of air (kg m-3) -USE multiconst,only:iden_ice ! intrinsic density of ice (kg m-3) -USE multiconst,only:iden_water ! intrinsic density of liquid water (kg m-3) +USE multiconst,only:gravity ! acceleration of gravity (m s-2) +USE multiconst,only:vkc ! von Karman's constant (-) +USE multiconst,only:w_ratio ! molecular ratio water to dry air (-) +USE multiconst,only:R_wv ! gas constant for water vapor (Pa K-1 m3 kg-1; J kg-1 K-1) +USE multiconst,only:Cp_air ! specific heat of air (J kg-1 K-1) +USE multiconst,only:Cp_ice ! specific heat of ice (J kg-1 K-1) +USE multiconst,only:Cp_soil ! specific heat of soil (J kg-1 K-1) +USE multiconst,only:Cp_water ! specific heat of liquid water (J kg-1 K-1) +USE multiconst,only:Tfreeze ! temperature at freezing (K) +USE multiconst,only:LH_fus ! latent heat of fusion (J kg-1) +USE multiconst,only:LH_vap ! latent heat of vaporization (J kg-1) +USE multiconst,only:LH_sub ! latent heat of sublimation (J kg-1) +USE multiconst,only:sb ! Stefan Boltzman constant (W m-2 K-4) +USE multiconst,only:iden_air ! intrinsic density of air (kg m-3) +USE multiconst,only:iden_ice ! intrinsic density of ice (kg m-3) +USE multiconst,only:iden_water ! intrinsic density of liquid water (kg m-3) ! look-up values for method used to compute derivative USE mDecisions_module,only: & - numerical, & ! numerical solution - analytical ! analytical solution - + numerical, & ! numerical solution + analytical ! analytical solution ! look-up values for choice of boundary conditions for thermodynamics USE mDecisions_module,only: & - prescribedTemp, & ! prescribed temperature - energyFlux, & ! energy flux - zeroFlux ! zero flux - + prescribedTemp, & ! prescribed temperature + energyFlux, & ! energy flux + zeroFlux ! zero flux ! look-up values for the choice of parameterization for vegetation roughness length and displacement height USE mDecisions_module,only: & - Raupach_BLM1994, & ! Raupach (BLM 1994) "Simplified expressions..." - CM_QJRMS1988, & ! Choudhury and Monteith (QJRMS 1988) "A four layer model for the heat budget..." - vegTypeTable ! constant parameters dependent on the vegetation type - + Raupach_BLM1994, & ! Raupach (BLM 1994) "Simplified expressions..." + CM_QJRMS1988, & ! Choudhury and Monteith (QJRMS 1988) "A four layer model for the heat budget..." + vegTypeTable ! constant parameters dependent on the vegetation type ! look-up values for the choice of parameterization for canopy emissivity USE mDecisions_module,only: & - simplExp, & ! simple exponential function - difTrans ! parameterized as a function of diffuse transmissivity - + simplExp, & ! simple exponential function + difTrans ! parameterized as a function of diffuse transmissivity ! look-up values for the choice of canopy wind profile USE mDecisions_module,only: & - exponential, & ! exponential wind profile extends to the surface - logBelowCanopy ! logarithmic profile below the vegetation canopy - + exponential, & ! exponential wind profile extends to the surface + logBelowCanopy ! logarithmic profile below the vegetation canopy ! look-up values for choice of stability function USE mDecisions_module,only: & - standard, & ! standard MO similarity, a la Anderson (1976) - louisInversePower, & ! Louis (1979) inverse power function - mahrtExponential ! Mahrt (1987) exponential - + standard, & ! standard MO similarity, a la Anderson (1976) + louisInversePower, & ! Louis (1979) inverse power function + mahrtExponential ! Mahrt (1987) exponential ! look-up values for the choice of groundwater representation (local-column, or single-basin) USE mDecisions_module,only: & - localColumn, & ! separate groundwater representation in each local soil column - singleBasin ! single groundwater store over the entire basin - + localColumn, & ! separate groundwater representation in each local soil column + singleBasin ! single groundwater store over the entire basin ! ------------------------------------------------------------------------------------------------- ! privacy implicit none private -public::vegNrgFlux -public::wettedFrac +public :: vegNrgFlux +public :: wettedFrac ! dimensions -integer(i4b),parameter :: nBands=2 ! number of spectral bands for shortwave radiation +integer(i4b),parameter :: nBands=2 ! number of spectral bands for shortwave radiation ! named variables -integer(i4b),parameter :: ist = 1 ! Surface type: IST=1 => soil; IST=2 => lake -integer(i4b),parameter :: isc = 4 ! Soil color type -integer(i4b),parameter :: ice = 0 ! Surface type: ICE=0 => soil; ICE=1 => sea-ice +integer(i4b),parameter :: ist = 1 ! Surface type: IST=1 => soil; IST=2 => lake +integer(i4b),parameter :: isc = 4 ! Soil color type +integer(i4b),parameter :: ice = 0 ! Surface type: ICE=0 => soil; ICE=1 => sea-ice ! spatial indices -integer(i4b),parameter :: iLoc = 1 ! i-location -integer(i4b),parameter :: jLoc = 1 ! j-location +integer(i4b),parameter :: iLoc = 1 ! i-location +integer(i4b),parameter :: jLoc = 1 ! j-location ! algorithmic parameters -real(rkind),parameter :: missingValue=-9999._rkind ! missing value, used when diagnostic or state variables are undefined -real(rkind),parameter :: verySmall=1.e-6_rkind ! used as an additive constant to check if substantial difference among real numbers -real(rkind),parameter :: tinyVal=epsilon(1._rkind) ! used as an additive constant to check if substantial difference among real numbers -real(rkind),parameter :: mpe=1.e-6_rkind ! prevents overflow error if division by zero -real(rkind),parameter :: dx=1.e-11_rkind ! finite difference increment +real(rkind),parameter :: missingValue=-9999._rkind ! missing value, used when diagnostic or state variables are undefined +real(rkind),parameter :: verySmall=1.e-6_rkind ! used as an additive constant to check if substantial difference among real numbers +real(rkind),parameter :: tinyVal=epsilon(1._rkind) ! used as an additive constant to check if substantial difference among real numbers +real(rkind),parameter :: mpe=1.e-6_rkind ! prevents overflow error if division by zero +real(rkind),parameter :: dx=1.e-11_rkind ! finite difference increment contains @@ -127,19 +120,19 @@ module vegNrgFlux_module ! ******************************************************************************************************* subroutine vegNrgFlux(& ! input: model control - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - checkLWBalance, & ! intent(in): flag to check longwave balance + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + checkLWBalance, & ! intent(in): flag to check longwave balance ! input: model state variables - upperBoundTemp, & ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature - canairTempTrial, & ! intent(in): trial value of the canopy air space temperature (K) - canopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - groundTempTrial, & ! intent(in): trial value of ground temperature (K) - canopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) - canopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) + upperBoundTemp, & ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature + canairTempTrial, & ! intent(in): trial value of the canopy air space temperature (K) + canopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + groundTempTrial, & ! intent(in): trial value of ground temperature (K) + canopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) + canopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) ! input: model derivatives - dCanLiq_dTcanopy, & ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) + dCanLiq_dTcanopy, & ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) ! input/output: data structures type_data, & ! intent(in): type of vegetation and soil forc_data, & ! intent(in): model forcing data @@ -151,53 +144,53 @@ subroutine vegNrgFlux(& bvar_data, & ! intent(in): model variables for the local basin model_decisions, & ! intent(in): model decisions ! output: liquid water fluxes associated with evaporation/transpiration (needed for coupling) - returnCanopyTranspiration, & ! intent(out): canopy transpiration (kg m-2 s-1) - returnCanopyEvaporation, & ! intent(out): canopy evaporation/condensation (kg m-2 s-1) - returnGroundEvaporation, & ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + returnCanopyTranspiration, & ! intent(out): canopy transpiration (kg m-2 s-1) + returnCanopyEvaporation, & ! intent(out): canopy evaporation/condensation (kg m-2 s-1) + returnGroundEvaporation, & ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) ! output: fluxes - canairNetFlux, & ! intent(out): net energy flux for the canopy air space (W m-2) - canopyNetFlux, & ! intent(out): net energy flux for the vegetation canopy (W m-2) - groundNetFlux, & ! intent(out): net energy flux for the ground surface (W m-2) + canairNetFlux, & ! intent(out): net energy flux for the canopy air space (W m-2) + canopyNetFlux, & ! intent(out): net energy flux for the vegetation canopy (W m-2) + groundNetFlux, & ! intent(out): net energy flux for the ground surface (W m-2) ! output: energy flux derivatives - dCanairNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) - dCanairNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) - dCanairNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) - dCanopyNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) - dCanopyNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) - dCanopyNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - dGroundNetFlux_dCanairTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) - dGroundNetFlux_dCanopyTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) - dGroundNetFlux_dGroundTemp, & ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + dCanairNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) + dCanairNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) + dCanairNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) + dCanopyNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) + dCanopyNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) + dCanopyNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) + dGroundNetFlux_dCanairTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) + dGroundNetFlux_dCanopyTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) + dGroundNetFlux_dGroundTemp, & ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) ! output liquid water flux derivarives (canopy evap) - dCanopyEvaporation_dCanWat, & ! intent(out): derivative in canopy evaporation w.r.t. canopy total water content (s-1) - dCanopyEvaporation_dTCanair, & ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTCanopy, & ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTGround, & ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dCanWat, & ! intent(out): derivative in canopy evaporation w.r.t. canopy total water content (s-1) + dCanopyEvaporation_dTCanair, & ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTCanopy, & ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTGround, & ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) ! output: liquid water flux derivarives (ground evap) - dGroundEvaporation_dCanWat, & ! intent(out): derivative in ground evaporation w.r.t. canopy total water content (s-1) - dGroundEvaporation_dTCanair, & ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTCanopy, & ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTGround, & ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dCanWat, & ! intent(out): derivative in ground evaporation w.r.t. canopy total water content (s-1) + dGroundEvaporation_dTCanair, & ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTCanopy, & ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTGround, & ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) ! output: transpiration derivatives - dCanopyTrans_dCanWat, & ! intent(out): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - dCanopyTrans_dTCanair, & ! intent(out): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTCanopy, & ! intent(out): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTGround, & ! intent(out): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + dCanopyTrans_dCanWat, & ! intent(out): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair, & ! intent(out): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy, & ! intent(out): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround, & ! intent(out): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) ! output: cross derivative terms - dCanopyNetFlux_dCanWat, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) - dGroundNetFlux_dCanWat, & ! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) + dCanopyNetFlux_dCanWat, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) + dGroundNetFlux_dCanWat, & ! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) ! output: error control err,message) ! intent(out): error control ! utilities - USE expIntegral_module,only:expInt ! function to calculate the exponential integral + USE expIntegral_module,only:expInt ! function to calculate the exponential integral ! conversion functions - USE conv_funcs_module,only:satVapPress ! function to compute the saturated vapor pressure (Pa) - USE conv_funcs_module,only:getLatentHeatValue ! function to identify latent heat of vaporization/sublimation (J kg-1) + USE conv_funcs_module,only:satVapPress ! function to compute the saturated vapor pressure (Pa) + USE conv_funcs_module,only:getLatentHeatValue ! function to identify latent heat of vaporization/sublimation (J kg-1) ! stomatal resistance - USE stomResist_module,only:stomResist ! subroutine to calculate stomatal resistance + USE stomResist_module,only:stomResist ! subroutine to calculate stomatal resistance ! phase changes - USE snow_utils_module,only:fracliquid ! compute fraction of liquid water at a given temperature + USE snow_utils_module,only:fracliquid ! compute fraction of liquid water at a given temperature ! compute energy and mass fluxes for vegetation implicit none @@ -521,14 +514,12 @@ subroutine vegNrgFlux(& ! identify the type of boundary condition for thermodynamics select case(ix_bcUpprTdyn) - ! ***** ! (1) DIRICHLET OR ZERO FLUX BOUNDARY CONDITION... ! ** prescribed temperature or zero flux at the upper boundary of the snow-soil system ! NOTE: Vegetation fluxes are not computed in this case ! ************************************************ case(prescribedTemp,zeroFlux) - ! derived fluxes scalarTotalET = 0._rkind ! total ET (kg m-2 s-1) scalarNetRadiation = 0._rkind ! net radiation (W m-2) @@ -552,28 +543,28 @@ subroutine vegNrgFlux(& dGroundNetFlux_dCanairTemp = 0._rkind ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) dGroundNetFlux_dCanopyTemp = 0._rkind ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) ! set liquid flux derivatives to zero (canopy evap) - dCanopyEvaporation_dCanWat = 0._rkind ! derivative in canopy evaporation w.r.t. canopy total water content (s-1) - dCanopyEvaporation_dTCanair= 0._rkind ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTCanopy= 0._rkind ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTGround= 0._rkind ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dCanWat = 0._rkind ! derivative in canopy evaporation w.r.t. canopy total water content (s-1) + dCanopyEvaporation_dTCanair= 0._rkind ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTCanopy= 0._rkind ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTGround= 0._rkind ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) ! set liquid flux derivatives to zero (ground evap) - dGroundEvaporation_dCanWat = 0._rkind ! derivative in ground evaporation w.r.t. canopy total water content (s-1) - dGroundEvaporation_dTCanair= 0._rkind ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTCanopy= 0._rkind ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTGround= 0._rkind ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dCanWat = 0._rkind ! derivative in ground evaporation w.r.t. canopy total water content (s-1) + dGroundEvaporation_dTCanair= 0._rkind ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTCanopy= 0._rkind ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTGround= 0._rkind ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) ! set transpiration derivatives to zero - dCanopyTrans_dCanWat = 0._rkind ! derivative in canopy transpiration w.r.t. canopy total water content (s-1) - dCanopyTrans_dTCanair= 0._rkind ! derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTCanopy= 0._rkind ! derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTGround= 0._rkind ! derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + dCanopyTrans_dCanWat = 0._rkind ! derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair= 0._rkind ! derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy= 0._rkind ! derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround= 0._rkind ! derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) ! compute fluxes and derivatives -- separate approach for prescribed temperature and zero flux, - if(ix_bcUpprTdyn == prescribedTemp)then + if (ix_bcUpprTdyn == prescribedTemp) then ! compute ground net flux (W m-2) groundNetFlux = -diag_data%var(iLookDIAG%iLayerThermalC)%dat(0)*(groundTempTrial - upperBoundTemp)/(prog_data%var(iLookPROG%mLayerDepth)%dat(1)*0.5_rkind) ! compute derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) inside soil and snow (ssd) energy flux routine ! dGroundNetFlux_dGroundTemp = missingValue - elseif(ix_bcUpprTdyn == zeroFlux)then + elseif (ix_bcUpprTdyn == zeroFlux) then groundNetFlux = 0._rkind ! dGroundNetFlux_dGroundTemp = missingValue else @@ -585,9 +576,8 @@ subroutine vegNrgFlux(& ! ** flux boundary condition ! NOTE 1: This is the main routine for calculating vegetation fluxes ! NOTE 2: This routine also calculates surface fluxes for the case where vegetation is buried with snow (or bare soil) - ! ************************************************ + ! ************************************************ case(energyFlux) - ! ***** PRELIMINARIES ***************************************************************************************************************************************** ! identify the appropriate groundwater variable select case(ix_spatial_gw) @@ -601,20 +591,20 @@ subroutine vegNrgFlux(& scalarGroundStabilityCorrection_old = scalarGroundStabilityCorrection ! stability correction for the ground surface (-) ! initialize variables to compute stomatal resistance - if(firstFluxCall .and. firstSubStep)then + if (firstFluxCall .and. firstSubStep) then ! vapor pressure in the canopy air space initialized as vapor pressure of air above the vegetation canopy ! NOTE: this is needed for the stomatal resistance calculations - if(scalarVP_CanopyAir < 0._rkind)then + if (scalarVP_CanopyAir < 0._rkind) then scalarVP_CanopyAir = scalarVPair - 1._rkind ! "small" offset used to assist in checking initial derivative calculations end if end if ! set latent heat of sublimation/vaporization for canopy and ground surface (Pa/K) ! NOTE: variables are constant over the substep, to simplify relating energy and mass fluxes - if(firstFluxCall)then + if (firstFluxCall) then scalarLatHeatSubVapCanopy = getLatentHeatValue(canopyTempTrial) ! case when there is snow on the ground (EXCLUDE "snow without a layer" -- in this case, evaporate from the soil) - if(nSnow > 0)then + if (nSnow > 0) then if(groundTempTrial > Tfreeze)then; err=20; message=trim(message)//'do not expect ground temperature > 0 when snow is on the ground'; return; end if scalarLatHeatSubVapGround = LH_sub ! sublimation from snow scalarGroundSnowFraction = 1._rkind @@ -622,8 +612,8 @@ subroutine vegNrgFlux(& else scalarLatHeatSubVapGround = LH_vap ! evaporation of water in the soil pores: this occurs even if frozen because of super-cooled water scalarGroundSnowFraction = 0._rkind - end if ! (if there is snow on the ground) - end if ! (if the first flux call) + end if ! end if there is snow on the ground + end if ! end if the first flux call ! compute the roughness length of the ground (ground below the canopy or non-vegetated surface) z0Ground = z0soil*(1._rkind - scalarGroundSnowFraction) + z0Snow*scalarGroundSnowFraction ! roughness length (m) @@ -633,7 +623,7 @@ subroutine vegNrgFlux(& exposedVAI = scalarExposedLAI + scalarExposedSAI ! exposed vegetation area index ! compute emissivity of the canopy (-) - if(computeVegFlux)then + if (computeVegFlux) then select case(ix_canopyEmis) case(simplExp) ! *** simple exponential function scalarCanopyEmissivity = 1._rkind - exp(-exposedVAI) ! effective emissivity of the canopy (-) @@ -648,15 +638,14 @@ subroutine vegNrgFlux(& end if ! ensure canopy longwave fluxes are zero when not computing canopy fluxes - if(.not.computeVegFlux) scalarCanopyEmissivity=0._rkind + if (.not.computeVegFlux) scalarCanopyEmissivity=0._rkind ! compute emissivity of the ground surface (-) groundEmissivity = scalarGroundSnowFraction*snowEmissivity + (1._rkind - scalarGroundSnowFraction)*soilEmissivity ! emissivity of the ground surface (-) ! compute the fraction of canopy that is wet ! NOTE: we either sublimate or evaporate over the entire substep - if(computeVegFlux)then - + if (computeVegFlux) then ! compute the fraction of liquid water in the canopy (-) totalCanopyWater = canopyLiqTrial + canopyIceTrial if(totalCanopyWater > tiny(1.0_rkind))then @@ -683,15 +672,14 @@ subroutine vegNrgFlux(& dCanopyWetFraction_dWat, & ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) dCanopyWetFraction_dT, & ! derivative in wetted fraction w.r.t. canopy temperature (K-1) err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if else scalarCanopyWetFraction = 0._rkind ! canopy wetted fraction (-) dCanopyWetFraction_dWat = 0._rkind ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) dCanopyWetFraction_dT = 0._rkind ! derivative in wetted fraction w.r.t. canopy temperature (K-1) end if - ! ***** AERODYNAMIC RESISTANCE ***************************************************************************************************************************************** + ! ***** AERODYNAMIC RESISTANCE ************************************************************************************************************************** ! NOTE: compute for all iterations ! Refs: Choudhury and Monteith (4-layer model for heat budget of homogenous surfaces; QJRMS, 1988) ! Niu and Yang (Canopy effects on snow processes; JGR, 2004) @@ -748,17 +736,16 @@ subroutine vegNrgFlux(& dCanopyResistance_dTCanair, & ! intent(out): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) ! output: error control err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - ! ***** STOMATAL RESISTANCE ***************************************************************************************************************************************** + ! ***** STOMATAL RESISTANCE ****************************************************************************************************************************** ! stomatal resistance is constant over the SUBSTEP ! NOTE: This is a simplification, as stomatal resistance does depend on canopy temperature ! This is a "short-cut" made because: ! (1) computations are expensive; ! (2) derivative calculations are rather complex (iterations within the Ball-Berry routine); and ! (3) stomatal resistance does not change rapidly - if(firstFluxCall)then - + if (firstFluxCall) then ! compute the saturation vapor pressure for vegetation temperature TV_celcius = canopyTempTrial - Tfreeze call satVapPress(TV_celcius, scalarSatVP_CanopyTemp, dSVPCanopy_dCanopyTemp) @@ -766,34 +753,34 @@ subroutine vegNrgFlux(& ! compute soil moisture factor controlling stomatal resistance call soilResist(& ! input (model decisions) - ix_soilStress, & ! intent(in): choice of function for the soil moisture control on stomatal resistance - ix_groundwatr, & ! intent(in): groundwater parameterization + ix_soilStress, & ! intent(in): choice of function for the soil moisture control on stomatal resistance + ix_groundwatr, & ! intent(in): groundwater parameterization ! input (state variables) - mLayerMatricHead(1:nSoil), & ! intent(in): matric head in each soil layer (m) - mLayerVolFracLiq(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water in each soil layer (-) - scalarAquiferStorage, & ! intent(in): aquifer storage (m) + mLayerMatricHead(1:nSoil), & ! intent(in): matric head in each soil layer (m) + mLayerVolFracLiq(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water in each soil layer (-) + scalarAquiferStorage, & ! intent(in): aquifer storage (m) ! input (diagnostic variables) - mLayerRootDensity(1:nSoil), & ! intent(in): root density in each layer (-) - scalarAquiferRootFrac, & ! intent(in): fraction of roots below the lowest soil layer (-) + mLayerRootDensity(1:nSoil), & ! intent(in): root density in each layer (-) + scalarAquiferRootFrac, & ! intent(in): fraction of roots below the lowest soil layer (-) ! input (parameters) - plantWiltPsi, & ! intent(in): matric head at wilting point (m) - soilStressParam, & ! intent(in): parameter in the exponential soil stress function (-) - critSoilWilting, & ! intent(in): critical vol. liq. water content when plants are wilting (-) - critSoilTranspire, & ! intent(in): critical vol. liq. water content when transpiration is limited (-) - critAquiferTranspire, & ! intent(in): critical aquifer storage value when transpiration is limited (m) + plantWiltPsi, & ! intent(in): matric head at wilting point (m) + soilStressParam, & ! intent(in): parameter in the exponential soil stress function (-) + critSoilWilting, & ! intent(in): critical vol. liq. water content when plants are wilting (-) + critSoilTranspire, & ! intent(in): critical vol. liq. water content when transpiration is limited (-) + critAquiferTranspire, & ! intent(in): critical aquifer storage value when transpiration is limited (m) ! output scalarTranspireLim, & ! intent(out): weighted average of the transpiration limiting factor (-) mLayerTranspireLim(1:nSoil), & ! intent(out): transpiration limiting factor in each layer (-) scalarTranspireLimAqfr, & ! intent(out): transpiration limiting factor for the aquifer (-) err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! compute stomatal resistance call stomResist(& ! input (state and diagnostic variables) - canopyTempTrial, & ! intent(in): temperature of the vegetation canopy (K) - scalarSatVP_CanopyTemp, & ! intent(in): saturation vapor pressure at the temperature of the veg canopy (Pa) - scalarVP_CanopyAir, & ! intent(in): canopy air vapor pressure (Pa) + canopyTempTrial, & ! intent(in): temperature of the vegetation canopy (K) + scalarSatVP_CanopyTemp, & ! intent(in): saturation vapor pressure at the temperature of the veg canopy (Pa) + scalarVP_CanopyAir, & ! intent(in): canopy air vapor pressure (Pa) ! input: data structures type_data, & ! intent(in): type of vegetation and soil forc_data, & ! intent(in): model forcing data @@ -803,25 +790,24 @@ subroutine vegNrgFlux(& diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU ! output: error control - err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + err,cmessage ) ! intent(out): error control + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + end if ! end if the first flux call in a given sub-step - end if ! (if the first flux call in a given sub-step) - - ! ***** LONGWAVE RADIATION ***************************************************************************************************************************************** + ! ***** LONGWAVE RADIATION **************************************************************************************************************************** ! compute canopy longwave radiation balance call longwaveBal(& ! input: model control - computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation - checkLWBalance, & ! intent(in): flag to check longwave balance + computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation + checkLWBalance, & ! intent(in): flag to check longwave balance ! input: canopy and ground temperature - canopyTempTrial, & ! intent(in): temperature of the vegetation canopy (K) - groundTempTrial, & ! intent(in): temperature of the ground surface (K) + canopyTempTrial, & ! intent(in): temperature of the vegetation canopy (K) + groundTempTrial, & ! intent(in): temperature of the ground surface (K) ! input: canopy and ground emissivity - scalarCanopyEmissivity, & ! intent(in): canopy emissivity (-) - groundEmissivity, & ! intent(in): ground emissivity (-) + scalarCanopyEmissivity, & ! intent(in): canopy emissivity (-) + groundEmissivity, & ! intent(in): ground emissivity (-) ! input: forcing - LWRadAtm, & ! intent(in): downwelling longwave radiation at the upper boundary (W m-2) + LWRadAtm, & ! intent(in): downwelling longwave radiation at the upper boundary (W m-2) ! output: emitted radiation from the canopy and ground scalarLWRadCanopy, & ! intent(out): longwave radiation emitted from the canopy (W m-2) scalarLWRadGround, & ! intent(out): longwave radiation emitted at the ground surface (W m-2) @@ -845,9 +831,9 @@ subroutine vegNrgFlux(& dLWNetGround_dTCanopy, & ! intent(out): derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) ! output: error control err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - ! ***** TURBULENT HEAT FLUXES ************************************************************************************************************************************** + ! ***** TURBULENT HEAT FLUXES ************************************************************************************************************************** ! compute the saturation vapor pressure for vegetation temperature ! NOTE: saturated vapor pressure derivatives don't seem that accurate.... TV_celcius = canopyTempTrial - Tfreeze @@ -860,60 +846,61 @@ subroutine vegNrgFlux(& ! compute the relative humidity in the top soil layer and the resistance at the ground surface ! NOTE: computations are based on start-of-step values, so only compute for the first flux call - if(firstFluxCall)then + if (firstFluxCall) then ! soil water evaporation factor [0-1] soilEvapFactor = mLayerVolFracLiq(nSnow+1)/(theta_sat - theta_res) ! resistance from the soil [s m-1] scalarSoilResistance = scalarGroundSnowFraction*1._rkind + (1._rkind - scalarGroundSnowFraction)*EXP(8.25_rkind - 4.225_rkind*soilEvapFactor) ! Sellers (1992) !scalarSoilResistance = scalarGroundSnowFraction*0._rkind + (1._rkind - scalarGroundSnowFraction)*exp(8.25_rkind - 6.0_rkind*soilEvapFactor) ! Niu adjustment to decrease resitance for wet soil ! relative humidity in the soil pores [0-1] - if(mLayerMatricHead(1) > -1.e+6_rkind)then ! avoid problems with numerical precision when soil is very dry + if (mLayerMatricHead(1) > -1.e+6_rkind) then ! avoid problems with numerical precision when soil is very dry soilRelHumidity_noSnow = exp( (mLayerMatricHead(1)*gravity) / (groundTempTrial*R_wv) ) else soilRelHumidity_noSnow = 0._rkind - end if ! (if matric head is very low) - scalarSoilRelHumidity = scalarGroundSnowFraction*1._rkind + (1._rkind - scalarGroundSnowFraction)*soilRelHumidity_noSnow - end if ! (if the first flux call) + end if ! end if matric head is very low + ! scalarSoilRelHumidity = scalarGroundSnowFraction*1._rkind + (1._rkind - scalarGroundSnowFraction)*soilRelHumidity_noSnow ! original + scalarSoilRelHumidity = scalarGroundSnowFraction + (1._rkind - scalarGroundSnowFraction)*soilRelHumidity_noSnow ! factor of unity removed for speed + end if ! end if the first flux call ! compute turbulent heat fluxes call turbFluxes(& ! input: model control - computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) + computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) ! input: above-canopy forcing data - airtemp, & ! intent(in): air temperature at some height above the surface (K) - airpres, & ! intent(in): air pressure of the air above the vegetation canopy (Pa) - scalarVPair, & ! intent(in): vapor pressure of the air above the vegetation canopy (Pa) + airtemp, & ! intent(in): air temperature at some height above the surface (K) + airpres, & ! intent(in): air pressure of the air above the vegetation canopy (Pa) + scalarVPair, & ! intent(in): vapor pressure of the air above the vegetation canopy (Pa) ! input: latent heat of sublimation/vaporization - scalarLatHeatSubVapCanopy, & ! intent(in): latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) - scalarLatHeatSubVapGround, & ! intent(in): latent heat of sublimation/vaporization for the ground surface (J kg-1) + scalarLatHeatSubVapCanopy, & ! intent(in): latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) + scalarLatHeatSubVapGround, & ! intent(in): latent heat of sublimation/vaporization for the ground surface (J kg-1) ! input: canopy/ground temperature and saturated vapor pressure - canairTempTrial, & ! intent(in): temperature of the canopy air space (K) - canopyTempTrial, & ! intent(in): canopy temperature (K) - groundTempTrial, & ! intent(in): ground temperature (K) - scalarSatVP_CanopyTemp, & ! intent(in): saturation vapor pressure at the temperature of the veg canopy (Pa) - scalarSatVP_GroundTemp, & ! intent(in): saturation vapor pressure at the temperature of the ground (Pa) - dSVPCanopy_dCanopyTemp, & ! intent(in): derivative in canopy saturation vapor pressure w.r.t. canopy temperature (Pa K-1) - dSVPGround_dGroundTemp, & ! intent(in): derivative in ground saturation vapor pressure w.r.t. ground temperature (Pa K-1) + canairTempTrial, & ! intent(in): temperature of the canopy air space (K) + canopyTempTrial, & ! intent(in): canopy temperature (K) + groundTempTrial, & ! intent(in): ground temperature (K) + scalarSatVP_CanopyTemp, & ! intent(in): saturation vapor pressure at the temperature of the veg canopy (Pa) + scalarSatVP_GroundTemp, & ! intent(in): saturation vapor pressure at the temperature of the ground (Pa) + dSVPCanopy_dCanopyTemp, & ! intent(in): derivative in canopy saturation vapor pressure w.r.t. canopy temperature (Pa K-1) + dSVPGround_dGroundTemp, & ! intent(in): derivative in ground saturation vapor pressure w.r.t. ground temperature (Pa K-1) ! input: diagnostic variables - exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) - scalarCanopyWetFraction, & ! intent(in): trial value for the fraction of canopy that is wet [0-1] - dCanopyWetFraction_dWat, & ! intent(in): derivative in the canopy wetted fraction w.r.t. total water content (kg-1 m-2) - dCanopyWetFraction_dT, & ! intent(in): derivative in wetted fraction w.r.t. canopy temperature (K-1) - scalarCanopySunlitLAI, & ! intent(in): sunlit leaf area (-) - scalarCanopyShadedLAI, & ! intent(in): shaded leaf area (-) - scalarSoilRelHumidity, & ! intent(in): relative humidity in the soil pores [0-1] - scalarSoilResistance, & ! intent(in): resistance from the soil (s m-1) - scalarLeafResistance, & ! intent(in): mean leaf boundary layer resistance per unit leaf area (s m-1) - scalarGroundResistance, & ! intent(in): below canopy aerodynamic resistance (s m-1) - scalarCanopyResistance, & ! intent(in): above canopy aerodynamic resistance (s m-1) - scalarStomResistSunlit, & ! intent(in): stomatal resistance for sunlit leaves (s m-1) - scalarStomResistShaded, & ! intent(in): stomatal resistance for shaded leaves (s m-1) + exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) + scalarCanopyWetFraction, & ! intent(in): trial value for the fraction of canopy that is wet [0-1] + dCanopyWetFraction_dWat, & ! intent(in): derivative in the canopy wetted fraction w.r.t. total water content (kg-1 m-2) + dCanopyWetFraction_dT, & ! intent(in): derivative in wetted fraction w.r.t. canopy temperature (K-1) + scalarCanopySunlitLAI, & ! intent(in): sunlit leaf area (-) + scalarCanopyShadedLAI, & ! intent(in): shaded leaf area (-) + scalarSoilRelHumidity, & ! intent(in): relative humidity in the soil pores [0-1] + scalarSoilResistance, & ! intent(in): resistance from the soil (s m-1) + scalarLeafResistance, & ! intent(in): mean leaf boundary layer resistance per unit leaf area (s m-1) + scalarGroundResistance, & ! intent(in): below canopy aerodynamic resistance (s m-1) + scalarCanopyResistance, & ! intent(in): above canopy aerodynamic resistance (s m-1) + scalarStomResistSunlit, & ! intent(in): stomatal resistance for sunlit leaves (s m-1) + scalarStomResistShaded, & ! intent(in): stomatal resistance for shaded leaves (s m-1) ! input: derivatives in scalar resistances - dGroundResistance_dTGround, & ! intent(in): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) - dGroundResistance_dTCanopy, & ! intent(in): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) - dGroundResistance_dTCanair, & ! intent(in): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - dCanopyResistance_dTCanopy, & ! intent(in): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) - dCanopyResistance_dTCanair, & ! intent(in): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) + dGroundResistance_dTGround, & ! intent(in): derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) + dGroundResistance_dTCanopy, & ! intent(in): derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) + dGroundResistance_dTCanair, & ! intent(in): derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) + dCanopyResistance_dTCanopy, & ! intent(in): derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) + dCanopyResistance_dTCanair, & ! intent(in): derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) ! output: conductances (used to check derivative calculations) scalarLeafConductance, & ! intent(out): leaf conductance (m s-1) scalarCanopyConductance, & ! intent(out): canopy conductance (m s-1) @@ -970,7 +957,7 @@ subroutine vegNrgFlux(& dTurbFluxGround_dCanWat, & ! intent(out): derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) ! output: error control err,cmessage ) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! compute the heat advected with precipitation (W m-2) ! NOTE: fluxes are in kg m-2 s-1, so no need to use density of water/ice here @@ -981,7 +968,7 @@ subroutine vegNrgFlux(& ! compute the mass flux associated with transpiration and evaporation/sublimation (J m-2 s-1 --> kg m-2 s-1) ! NOTE: remove water from the snow on the ground in preference to removing water from the water in soil pores - if(scalarLatHeatSubVapCanopy > LH_vap+verySmall)then ! canopy sublimation + if (scalarLatHeatSubVapCanopy > LH_vap+verySmall) then ! canopy sublimation scalarCanopyEvaporation = 0._rkind scalarCanopySublimation = scalarLatHeatCanopyEvap/LH_sub if(scalarLatHeatCanopyTrans > 0._rkind)then ! flux directed towards the veg @@ -1000,19 +987,19 @@ subroutine vegNrgFlux(& scalarCanopyTranspiration = scalarLatHeatCanopyTrans/LH_vap end if end if - if(scalarLatHeatSubVapGround > LH_vap+verySmall)then ! ground sublimation + if (scalarLatHeatSubVapGround > LH_vap+verySmall) then ! ground sublimation ! NOTE: this should only occur when we have formed snow layers, so check - if(nSnow == 0)then; err=20; message=trim(message)//'only expect snow sublimation when we have formed some snow layers'; return; end if + if (nSnow == 0) then; err=20; message=trim(message)//'only expect snow sublimation when we have formed some snow layers'; return; end if scalarGroundEvaporation = 0._rkind ! ground evaporation is zero once the snowpack has formed scalarSnowSublimation = scalarLatHeatGround/LH_sub else ! NOTE: this should only occur when we have no snow layers, so check - if(nSnow > 0)then; err=20; message=trim(message)//'only expect ground evaporation when there are no snow layers'; return; end if + if (nSnow > 0) then; err=20; message=trim(message)//'only expect ground evaporation when there are no snow layers'; return; end if scalarGroundEvaporation = scalarLatHeatGround/LH_vap scalarSnowSublimation = 0._rkind ! no sublimation from snow if no snow layers have formed end if - ! ***** AND STITCH EVERYTHING TOGETHER ***************************************************************************************************************************** + ! ***** AND STITCH EVERYTHING TOGETHER ***************************************************************************************************************** ! compute derived fluxes scalarTotalET = scalarGroundEvaporation + scalarCanopyEvaporation + scalarCanopyTranspiration @@ -1035,7 +1022,7 @@ subroutine vegNrgFlux(& dGroundNetFlux_dGroundTemp = dLWNetGround_dTGround + dTurbFluxGround_dTGround - Cp_water*scalarThroughfallRain - Cp_ice*scalarThroughfallSnow ! check if evaporation or sublimation - if(scalarLatHeatSubVapCanopy < LH_vap+verySmall)then ! evaporation + if (scalarLatHeatSubVapCanopy < LH_vap+verySmall) then ! evaporation ! compute the liquid water derivarives dCanopyEvaporation_dCanWat = dLatHeatCanopyEvap_dCanWat/LH_vap ! (s-1) dCanopyEvaporation_dTCanair = dLatHeatCanopyEvap_dTCanair/LH_vap ! (kg m-2 s-1 K-1) @@ -1049,7 +1036,7 @@ subroutine vegNrgFlux(& end if ! transpiration - if(scalarLatHeatCanopyTrans > 0._rkind)then ! flux directed towards the veg + if (scalarLatHeatCanopyTrans > 0._rkind) then ! flux directed towards the veg dCanopyTrans_dCanWat = 0._rkind dCanopyTrans_dTCanair= 0._rkind dCanopyTrans_dTCanopy= 0._rkind @@ -1073,7 +1060,6 @@ subroutine vegNrgFlux(& ! * check upper boundary condition case default; err=10; message=trim(message)//'unable to identify upper boundary condition for thermodynamics'; return - ! end case statement end select ! upper boundary condition for thermodynamics @@ -1082,8 +1068,7 @@ subroutine vegNrgFlux(& returnCanopyEvaporation = scalarCanopyEvaporation ! canopy evaporation/condensation (kg m-2 s-1) returnGroundEvaporation = scalarGroundEvaporation ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - ! end associations - end associate + end associate ! end associations end subroutine vegNrgFlux @@ -1134,7 +1119,7 @@ subroutine wettedFrac(& err=0; message='wettedFrac/' ! compute case where the canopy is frozen - if(frozen)then + if (frozen) then ! compute fraction of liquid water on the canopy call wetFraction(deriv,smoothing,canopyIce,canopyIceMax,canopyWettingFactor,canopyWettingExp,canopyWetFraction,canopyWetFractionDeriv) @@ -1162,21 +1147,21 @@ end subroutine wettedFrac subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFactor,canopyWettingExp,canopyWetFraction,canopyWetFractionDeriv) implicit none ! dummy variables - logical(lgt),intent(in) :: derDesire ! flag to denote if analytical derivatives are desired - logical(lgt),intent(in) :: smoothing ! flag to denote if smoothing is required - real(rkind),intent(in) :: canopyLiq ! liquid water content (kg m-2) - real(rkind),intent(in) :: canopyMax ! liquid water content (kg m-2) - real(rkind),intent(in) :: canopyWettingFactor ! maximum wetted fraction of the canopy (-) - real(rkind),intent(in) :: canopyWettingExp ! exponent in canopy wetting function (-) - - real(rkind),intent(out) :: canopyWetFraction ! canopy wetted fraction (-) - real(rkind),intent(out) :: canopyWetFractionDeriv ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) + logical(lgt),intent(in) :: derDesire ! flag to denote if analytical derivatives are desired + logical(lgt),intent(in) :: smoothing ! flag to denote if smoothing is required + real(rkind),intent(in) :: canopyLiq ! liquid water content (kg m-2) + real(rkind),intent(in) :: canopyMax ! liquid water content (kg m-2) + real(rkind),intent(in) :: canopyWettingFactor ! maximum wetted fraction of the canopy (-) + real(rkind),intent(in) :: canopyWettingExp ! exponent in canopy wetting function (-) + + real(rkind),intent(out) :: canopyWetFraction ! canopy wetted fraction (-) + real(rkind),intent(out) :: canopyWetFractionDeriv ! derivative in wetted fraction w.r.t. canopy liquid water (kg-1 m2) ! local variables - real(rkind) :: relativeCanopyWater ! water stored on vegetation canopy, expressed as a fraction of maximum storage (-) - real(rkind) :: rawCanopyWetFraction ! initial value of the canopy wet fraction (before smoothing) - real(rkind) :: rawWetFractionDeriv ! derivative in canopy wet fraction w.r.t. storage (kg-1 m2) - real(rkind) :: smoothFunc ! smoothing function used to improve numerical stability at times with limited water storage (-) - real(rkind) :: smoothFuncDeriv ! derivative in the smoothing function w.r.t.canopy storage (kg-1 m2) + real(rkind) :: relativeCanopyWater ! water stored on vegetation canopy, expressed as a fraction of maximum storage (-) + real(rkind) :: rawCanopyWetFraction ! initial value of the canopy wet fraction (before smoothing) + real(rkind) :: rawWetFractionDeriv ! derivative in canopy wet fraction w.r.t. storage (kg-1 m2) + real(rkind) :: smoothFunc ! smoothing function used to improve numerical stability at times with limited water storage (-) + real(rkind) :: smoothFuncDeriv ! derivative in the smoothing function w.r.t.canopy storage (kg-1 m2) real(rkind) :: verySmall=epsilon(1._rkind) ! a very small number ! -------------------------------------------------------------------------------------------------------------- ! compute relative canopy water @@ -1184,9 +1169,9 @@ subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFact ! compute an initial value of the canopy wet fraction ! - canopy below value where canopy is 100% wet - if(relativeCanopyWater < 1._rkind)then + if (relativeCanopyWater < 1._rkind) then rawCanopyWetFraction = canopyWettingFactor*(relativeCanopyWater**canopyWettingExp) - if(derDesire .and. relativeCanopyWater>verySmall)then + if (derDesire .and. relativeCanopyWater>verySmall) then rawWetFractionDeriv = (canopyWettingFactor*canopyWettingExp/canopyMax)*relativeCanopyWater**(canopyWettingExp - 1._rkind) else rawWetFractionDeriv = 0._rkind @@ -1198,7 +1183,7 @@ subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFact end if ! smooth canopy wetted fraction - if(smoothing)then + if (smoothing) then call logisticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) canopyWetFraction = rawCanopyWetFraction*smoothFunc ! logistic smoother else @@ -1206,12 +1191,12 @@ subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFact end if ! compute derivative (product rule) - if(derDesire)then - if(smoothing)then + if (derDesire) then + if (smoothing) then canopyWetFractionDeriv = rawWetFractionDeriv*smoothFunc + rawCanopyWetFraction*smoothFuncDeriv else ! raw derivative is used if not smoothing canopyWetFractionDeriv = rawWetFractionDeriv - endif + end if else canopyWetFractionDeriv = 0._rkind end if @@ -1224,13 +1209,13 @@ end subroutine wetFraction subroutine logisticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) implicit none ! dummy variables - logical(lgt),intent(in) :: derDesire ! flag to denote if analytical derivatives are desired - real(rkind),intent(in) :: canopyLiq ! liquid water content (kg m-2) - real(rkind),intent(out) :: smoothFunc ! smoothing function (-) - real(rkind),intent(out) :: smoothFuncDeriv ! derivative in smoothing function (kg-1 m-2) + logical(lgt),intent(in) :: derDesire ! flag to denote if analytical derivatives are desired + real(rkind),intent(in) :: canopyLiq ! liquid water content (kg m-2) + real(rkind),intent(out) :: smoothFunc ! smoothing function (-) + real(rkind),intent(out) :: smoothFuncDeriv ! derivative in smoothing function (kg-1 m-2) ! local variables - real(rkind) :: xArg ! argument used in the smoothing function (-) - real(rkind) :: expX ! exp(-xArg) -- used multiple times + real(rkind) :: xArg ! argument used in the smoothing function (-) + real(rkind) :: expX ! exp(-xArg) -- used multiple times real(rkind),parameter :: smoothThresh=0.01_rkind ! mid-point of the smoothing function (kg m-2) real(rkind),parameter :: smoothScale=0.001_rkind ! scaling factor for the smoothing function (kg m-2) real(rkind),parameter :: xLimit=50._rkind ! don't compute exponents for > xLimit @@ -1239,10 +1224,10 @@ subroutine logisticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) xArg = (canopyLiq - smoothThresh)/smoothScale ! only compute smoothing function for small exponents - if(xArg > -xLimit .and. xArg < xLimit)then ! avoid huge exponents + if (xArg > -xLimit .and. xArg < xLimit) then ! avoid huge exponents expX = exp(-xarg) ! also used in the derivative smoothFunc = 1._rkind / (1._rkind + expX) ! logistic smoother - if(derDesire)then + if (derDesire) then smoothFuncDeriv = expX / (smoothScale * (1._rkind + expX)**2_i4b) ! derivative in the smoothing function else smoothFuncDeriv = 0._rkind @@ -1250,8 +1235,8 @@ subroutine logisticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) ! outside limits: special case of smooth exponents else - if(xArg < 0._rkind)then; smoothFunc = 0._rkind ! xArg < -xLimit - else; smoothFunc = 1._rkind ! xArg > xLimit + if (xArg < 0._rkind) then; smoothFunc = 0._rkind ! xArg < -xLimit + else; smoothFunc = 1._rkind ! xArg > xLimit end if smoothFuncDeriv = 0._rkind end if ! check for huge exponents @@ -1263,16 +1248,16 @@ end subroutine logisticSmoother ! ******************************************************************************************************* subroutine longwaveBal(& ! input: model control - computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation - checkLWBalance, & ! intent(in): flag to check longwave balance + computeVegFlux, & ! intent(in): flag to compute fluxes over vegetation + checkLWBalance, & ! intent(in): flag to check longwave balance ! input: canopy and ground temperature - canopyTemp, & ! intent(in): canopy temperature (K) - groundTemp, & ! intent(in): ground temperature (K) + canopyTemp, & ! intent(in): canopy temperature (K) + groundTemp, & ! intent(in): ground temperature (K) ! input: canopy and ground emissivity - emc, & ! intent(in): canopy emissivity (-) - emg, & ! intent(in): ground emissivity (-) + emc, & ! intent(in): canopy emissivity (-) + emg, & ! intent(in): ground emissivity (-) ! input: forcing - LWRadUbound, & ! intent(in): downwelling longwave radiation at the upper boundary (W m-2) + LWRadUbound, & ! intent(in): downwelling longwave radiation at the upper boundary (W m-2) ! output: sources LWRadCanopy, & ! intent(out): longwave radiation emitted from the canopy (W m-2) LWRadGround, & ! intent(out): longwave radiation emitted at the ground surface (W m-2) @@ -1335,17 +1320,17 @@ subroutine longwaveBal(& character(*),intent(out) :: message ! error message ! ----------------------------------------------------------------------------------------------------------------------------------------------- ! local variables - real(rkind) :: fluxBalance ! check energy closure (W m-2) + real(rkind) :: fluxBalance ! check energy closure (W m-2) real(rkind),parameter :: fluxTolerance=1.e-10_rkind ! tolerance for energy closure (W m-2) - real(rkind) :: dLWRadCanopy_dTCanopy ! derivative in emitted radiation at the canopy w.r.t. canopy temperature - real(rkind) :: dLWRadGround_dTGround ! derivative in emitted radiation at the ground w.r.t. ground temperature + real(rkind) :: dLWRadCanopy_dTCanopy ! derivative in emitted radiation at the canopy w.r.t. canopy temperature + real(rkind) :: dLWRadGround_dTGround ! derivative in emitted radiation at the ground w.r.t. ground temperature ! ----------------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='longwaveBal/' ! compute longwave fluxes from canopy and the ground ! NOTE: emc should be set to zero when not computing canopy fluxes - if(computeVegFlux)then + if (computeVegFlux) then LWRadCanopy = emc*sb*canopyTemp**4_i4b ! longwave radiation emitted from the canopy (W m-2) else LWRadCanopy = 0._rkind @@ -1356,7 +1341,6 @@ subroutine longwaveBal(& LWRadUbound2Canopy = (emc + (1._rkind - emc)*(1._rkind - emg)*emc)*LWRadUbound ! downward atmospheric longwave radiation absorbed by the canopy (W m-2) LWRadUbound2Ground = (1._rkind - emc)*emg*LWRadUbound ! downward atmospheric longwave radiation absorbed by the ground (W m-2) LWRadUbound2Ubound = (1._rkind - emc)*(1._rkind - emg)*(1._rkind - emc)*LWRadUbound ! atmospheric radiation reflected by the ground and lost thru upper boundary (W m-2) - ! compute fluxes originating from the canopy LWRadCanopy2Ubound = (1._rkind + (1._rkind - emc)*(1._rkind - emg))*LWRadCanopy ! longwave radiation emitted from canopy lost thru upper boundary (W m-2) LWRadCanopy2Ground = emg*LWRadCanopy ! longwave radiation emitted from canopy absorbed by the ground (W m-2) @@ -1364,7 +1348,6 @@ subroutine longwaveBal(& ! compute fluxes originating from the ground surface LWRadGround2Ubound = (1._rkind - emc)*LWRadGround ! longwave radiation emitted from ground lost thru upper boundary (W m-2) LWRadGround2Canopy = emc*LWRadGround ! longwave radiation emitted from ground and absorbed by the canopy (W m-2) - ! compute net longwave radiation (W m-2) LWNetCanopy = LWRadUbound2Canopy + LWRadGround2Canopy + LWRadCanopy2Canopy - 2._rkind*LWRadCanopy ! canopy LWNetGround = LWRadUbound2Ground + LWRadCanopy2Ground - LWRadGround ! ground surface @@ -1372,7 +1355,7 @@ subroutine longwaveBal(& ! check the flux balance fluxBalance = LWNetUbound - (LWNetCanopy + LWNetGround) - if(abs(fluxBalance) > fluxTolerance .and. checkLWBalance)then + if (abs(fluxBalance) > fluxTolerance .and. checkLWBalance) then print*, 'fluxBalance = ', fluxBalance print*, 'emg, emc = ', emg, emc print*, 'canopyTemp, groundTemp = ', canopyTemp, groundTemp @@ -1569,7 +1552,7 @@ subroutine aeroResist(& err=0; message='aeroResist/' ! check that measurement height is above the top of the canopy - if(mHeight < heightCanopyTop)then + if (mHeight < heightCanopyTop) then err=20; message=trim(message)//'measurement height is below the top of the canopy'; return end if @@ -1583,36 +1566,31 @@ subroutine aeroResist(& heightCanopyTopAboveSnow = heightCanopyTop - snowDepth heightCanopyBottomAboveSnow = max(heightCanopyBottom - snowDepth, 0.0_rkind) select case(ixVegTraits) - ! Raupach (BLM 1994) "Simplified expressions..." case(Raupach_BLM1994) - ! (compute zero-plane displacement) + ! compute zero-plane displacement funcLAI = sqrt(c_d1*exposedVAI) fracCanopyHeight = -(1._rkind - exp(-funcLAI))/funcLAI + 1._rkind zeroPlaneDisplacement = fracCanopyHeight*(heightCanopyTopAboveSnow-heightCanopyBottomAboveSnow)+heightCanopyBottomAboveSnow - ! (coupute roughness length of the veg canopy) + ! coupute roughness length of the veg canopy approxDragCoef = min( sqrt(C_s + C_r*exposedVAI/2._rkind), approxDragCoef_max) z0Canopy = (1._rkind - fracCanopyHeight) * exp(-vkc*approxDragCoef - psi_h) * (heightCanopyTopAboveSnow-heightCanopyBottomAboveSnow) - ! Choudhury and Monteith (QJRMS 1988) "A four layer model for the heat budget..." case(CM_QJRMS1988) funcLAI = cd_CM*exposedVAI zeroPlaneDisplacement = 1.1_rkind*heightCanopyTopAboveSnow*log(1._rkind + sqrt(sqrt(funcLAI))) - if(funcLAI < 0.2_rkind)then + if (funcLAI < 0.2_rkind) then z0Canopy = z0Ground + 0.3_rkind*heightCanopyTopAboveSnow*sqrt(funcLAI) else z0Canopy = 0.3_rkind*heightCanopyTopAboveSnow*(1._rkind - zeroPlaneDisplacement/heightCanopyTopAboveSnow) end if - ! constant parameters dependent on the vegetation type case(vegTypeTable) zeroPlaneDisplacement = zpdFraction*heightCanopyTopAboveSnow ! zero-plane displacement (m) z0Canopy = z0CanopyParam ! roughness length of the veg canopy (m) - ! check case default err=10; message=trim(message)//"unknown parameterization for vegetation roughness length and displacement height"; return - end select ! vegetation traits (z0, zpd) ! check zero plane displacement @@ -1754,7 +1732,7 @@ subroutine aeroResist(& leafResistance = 1.e12_rkind ! not used: huge resistance, so conductance is essentially zero ! check that measurement height above the ground surface is above the roughness length - if(mHeight < snowDepth+z0Ground)then; err=20; message=trim(message)//'measurement height < snow depth + roughness length'; return; end if + if (mHeight < snowDepth+z0Ground) then; err=20; message=trim(message)//'measurement height < snow depth + roughness length'; return; end if ! compute the resistance between the surface and canopy air UNDER NEUTRAL CONDITIONS (s m-1) groundExNeut = (vkc**2_i4b) / ( log((mHeight - snowDepth)/z0Ground)**2_i4b) ! turbulent transfer coefficient under conditions of neutral stability (-) @@ -2230,7 +2208,7 @@ subroutine turbFluxes(& ! compute derivatives in individual conductances for sensible heat w.r.t. canopy temperature (m s-1 K-1) ! NOTE: it may be more efficient to compute these derivatives when computing resistances - if(computeVegFlux)then + if (computeVegFlux) then dEvapCond_dCanopyTemp = dCanopyWetFraction_dT*leafConductance ! derivative in evap conductance w.r.t. canopy temperature dTransCond_dCanopyTemp = -dCanopyWetFraction_dT*leafConductanceTr ! derivative in trans conductance w.r.t. canopy temperature dCanopyCond_dCanairTemp = -dCanopyResistance_dTCanair/canopyResistance**2_i4b ! derivative in canopy conductance w.r.t. canopy air emperature @@ -2249,7 +2227,7 @@ subroutine turbFluxes(& endif ! compute derivatives in individual conductances for latent heat w.r.t. canopy temperature (m s-1 K-1) - if(computeVegFlux)then + if (computeVegFlux) then dGroundCondLH_dCanairTemp = -dGroundResistance_dTCanair/(groundResistance+soilResistance)**2_i4b ! derivative in ground conductance w.r.t. canopy air temperature dGroundCondLH_dCanopyTemp = -dGroundResistance_dTCanopy/(groundResistance+soilResistance)**2_i4b ! derivative in ground conductance w.r.t. canopy temperature dGroundCondLH_dGroundTemp = -dGroundResistance_dTGround/(groundResistance+soilResistance)**2_i4b ! derivative in ground conductance w.r.t. ground temperature @@ -2302,23 +2280,23 @@ subroutine turbFluxes(& ! * compute derivatives ! differentiate CANOPY fluxes - if(computeVegFlux)then + if (computeVegFlux) then ! compute derivatives of vapor pressure in the canopy air space w.r.t. all state variables - ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the canopy air space) + ! derivative of vapor pressure in the canopy air space w.r.t. temperature of the canopy air space dPart1 = dCanopyCond_dCanairTemp*VPair + dGroundCondLH_dCanairTemp*satVP_GroundTemp*soilRelHumidity dPart2 = -(dCanopyCond_dCanairTemp + dGroundCondLH_dCanairTemp)/(totalConductanceLH**2_i4b) dVPCanopyAir_dTCanair = dPart1/totalConductanceLH + fPart_VP*dPart2 - ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the canopy) + ! derivative of vapor pressure in the canopy air space w.r.t. temperature of the canopy dPart0 = (evapConductance + transConductance)*dSVPCanopy_dCanopyTemp + (dEvapCond_dCanopyTemp + dTransCond_dCanopyTemp)*satVP_CanopyTemp dPart1 = dCanopyCond_dCanopyTemp*VPair + dPart0 + dGroundCondLH_dCanopyTemp*satVP_GroundTemp*soilRelHumidity dPart2 = -(dCanopyCond_dCanopyTemp + dEvapCond_dCanopyTemp + dTransCond_dCanopyTemp + dGroundCondLH_dCanopyTemp)/(totalConductanceLH**2_i4b) dVPCanopyAir_dTCanopy = dPart1/totalConductanceLH + fPart_VP*dPart2 - ! (derivative of vapor pressure in the canopy air space w.r.t. temperature of the ground) + ! derivative of vapor pressure in the canopy air space w.r.t. temperature of the ground dPart1 = dGroundCondLH_dGroundTemp*satVP_GroundTemp*soilRelHumidity + groundConductanceLH*dSVPGround_dGroundTemp*soilRelHumidity dPart2 = -dGroundCondLH_dGroundTemp/(totalConductanceLH**2_i4b) dVPCanopyAir_dTGround = dPart1/totalConductanceLH + fPart_VP*dPart2 - ! (derivative of vapor pressure in the canopy air space w.r.t. wetted fraction of the canopy) + ! derivative of vapor pressure in the canopy air space w.r.t. wetted fraction of the canopy dPart1 = (leafConductance - leafConductanceTr)*satVP_CanopyTemp dPart2 = -(leafConductance - leafConductanceTr)/(totalConductanceLH**2_i4b) dVPCanopyAir_dWetFrac = dPart1/totalConductanceLH + fPart_VP*dPart2 @@ -2509,11 +2487,12 @@ subroutine aStability(& err,message) ! output: error control ! ***** process unstable cases - if(RiBulk<0._rkind)then + if (RiBulk<0._rkind) then ! compute surface-atmosphere exchange coefficient (-) stabilityCorrection = sqrt(1._rkind - 16._rkind*RiBulk) ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) - dStabilityCorrection_dRich = (-16._rkind) * 0.5_rkind*(1._rkind - 16._rkind*RiBulk)**(-0.5_rkind) + ! dStabilityCorrection_dRich = (-16._rkind) * 0.5_rkind*(1._rkind - 16._rkind*RiBulk)**(-0.5_rkind) ! original + dStabilityCorrection_dRich = (-16._rkind) * 0.5_rkind/sqrt(1._rkind - 16._rkind*RiBulk) ! use sqrt intrinsic for speed dStabilityCorrection_dAirTemp = dRiBulk_dAirTemp * dStabilityCorrection_dRich dStabilityCorrection_dSfcTemp = dRiBulk_dSfcTemp * dStabilityCorrection_dRich return @@ -2521,8 +2500,7 @@ subroutine aStability(& ! ***** process stable cases select case(ixStability) - - ! ("standard" stability correction, a la Anderson 1976) + ! "standard" stability correction, a la Anderson 1976 case(standard) ! compute surface-atmosphere exchange coefficient (-) if(RiBulk < critRichNumber) stabilityCorrection = (1._rkind - 5._rkind*RiBulk)**2_i4b @@ -2530,16 +2508,15 @@ subroutine aStability(& ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) if(RiBulk < critRichNumber) dStabilityCorrection_dRich = -10._rkind*(1._rkind - 5._rkind*RiBulk) if(RiBulk >= critRichNumber) dStabilityCorrection_dRich = verySmall - - ! (Louis 1979) - case(louisInversePower) + ! Louis 1979 + case(louisInversePower) ! scale the "b" parameter for stable conditions bprime = Louis79_bparam/2._rkind ! compute surface-atmosphere exchange coefficient (-) stabilityCorrection = 1._rkind / ( (1._rkind + bprime*RiBulk)**2_i4b ) if(stabilityCorrection < epsilon(stabilityCorrection)) stabilityCorrection = epsilon(stabilityCorrection) ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) - dStabilityCorrection_dRich = bprime * (-2._rkind)*(1._rkind + bprime*RiBulk)**(-3._rkind) + dStabilityCorrection_dRich = bprime * (-2._rkind)*(1._rkind + bprime*RiBulk)**(-3_i4b) ! (Mahrt 1987) case(mahrtExponential) @@ -2549,10 +2526,9 @@ subroutine aStability(& ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) dStabilityCorrection_dRich = (-Mahrt87_eScale) * exp(-Mahrt87_eScale * RiBulk) - ! (return error if the stability correction method is not found) + ! return error if the stability correction method is not found case default err=10; message=trim(message)//"optionNotFound[stability correction]"; return - end select ! get the stability correction with respect to air temperature and surface temperature @@ -2567,10 +2543,10 @@ end subroutine aStability ! ******************************************************************************************************* subroutine bulkRichardson(& ! input - airTemp, & ! input: air temperature (K) - sfcTemp, & ! input: surface temperature (K) - windspd, & ! input: wind speed (m s-1) - mHeight, & ! input: measurement height (m) + airTemp, & ! input: air temperature (K) + sfcTemp, & ! input: surface temperature (K) + windspd, & ! input: wind speed (m s-1) + mHeight, & ! input: measurement height (m) ! output RiBulk, & ! output: bulk Richardson number (-) dRiBulk_dAirTemp, & ! output: derivative in the bulk Richardson number w.r.t. air temperature (K-1) @@ -2589,9 +2565,9 @@ subroutine bulkRichardson(& integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local variables -real(rkind) :: T_grad ! gradient in temperature between the atmosphere and surface (K) -real(rkind) :: T_mean ! mean of the atmosphere and surface temperature (K) -real(rkind) :: RiMult ! dimensionless scaling factor (-) +real(rkind) :: T_grad ! gradient in temperature between the atmosphere and surface (K) +real(rkind) :: T_mean ! mean of the atmosphere and surface temperature (K) +real(rkind) :: RiMult ! dimensionless scaling factor (-) ! initialize error control err=0; message='bulkRichardson/' ! compute local variables @@ -2606,5 +2582,4 @@ subroutine bulkRichardson(& end subroutine bulkRichardson - -end module vegNrgFlux_module \ No newline at end of file +end module vegNrgFlux_module From 5b9777b28150ad1540f85f8c369958cd789f36d0 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 24 May 2023 01:54:58 -0600 Subject: [PATCH 0869/1472] Additional refactoring of vegNrgFlux.f90. --- build/source/engine/vegNrgFlux.f90 | 408 ++++++++++++++--------------- 1 file changed, 199 insertions(+), 209 deletions(-) diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 5f01be14c..892ef2857 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -1385,9 +1385,9 @@ subroutine longwaveBal(& dLWRadGround_dTGround = 4._rkind*emg*sb*groundTemp**3_i4b ! compute analytical derivatives dLWNetCanopy_dTCanopy = (emc*(1._rkind - emg) - 2._rkind)*dLWRadCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) - dLWNetGround_dTGround = -dLWRadGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetCanopy_dTGround = emc*dLWRadGround_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) - dLWNetGround_dTCanopy = emg*dLWRadCanopy_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) + dLWNetGround_dTGround = -dLWRadGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) + dLWNetCanopy_dTGround = emc*dLWRadGround_dTGround ! derivative in net canopy radiation w.r.t. ground temperature (W m-2 K-1) + dLWNetGround_dTCanopy = emg*dLWRadCanopy_dTCanopy ! derivative in net ground radiation w.r.t. canopy temperature (W m-2 K-1) end subroutine longwaveBal @@ -1396,32 +1396,32 @@ end subroutine longwaveBal ! ******************************************************************************************************* subroutine aeroResist(& ! input: model control - computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) - ixVegTraits, & ! intent(in): choice of parameterization for vegetation roughness length and displacement height - ixWindProfile, & ! intent(in): choice of canopy wind profile - ixStability, & ! intent(in): choice of stability function + computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) + ixVegTraits, & ! intent(in): choice of parameterization for vegetation roughness length and displacement height + ixWindProfile, & ! intent(in): choice of canopy wind profile + ixStability, & ! intent(in): choice of stability function ! input: above-canopy forcing data - mHeight, & ! intent(in): measurement height (m) - airtemp, & ! intent(in): air temperature at some height above the surface (K) - windspd, & ! intent(in): wind speed at some height above the surface (m s-1) + mHeight, & ! intent(in): measurement height (m) + airtemp, & ! intent(in): air temperature at some height above the surface (K) + windspd, & ! intent(in): wind speed at some height above the surface (m s-1) ! input: temperature (canopy, ground, canopy air space) - canairTemp, & ! intent(in): temperature of the canopy air space (K) - groundTemp, & ! intent(in): ground temperature (K) + canairTemp, & ! intent(in): temperature of the canopy air space (K) + groundTemp, & ! intent(in): ground temperature (K) ! input: diagnostic variables - exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) - snowDepth, & ! intent(in): snow depth (m) + exposedVAI, & ! intent(in): exposed vegetation area index -- leaf plus stem (m2 m-2) + snowDepth, & ! intent(in): snow depth (m) ! input: parameters - z0Ground, & ! intent(in): roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) - z0CanopyParam, & ! intent(in): roughness length of the canopy (m) - zpdFraction, & ! intent(in): zero plane displacement / canopy height (-) - critRichNumber, & ! intent(in): critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam, & ! intent(in): parameter in Louis (1979) stability function - Mahrt87_eScale, & ! intent(in): exponential scaling factor in the Mahrt (1987) stability function - windReductionParam, & ! intent(in): canopy wind reduction parameter (-) - leafExchangeCoeff, & ! intent(in): turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) - leafDimension, & ! intent(in): characteristic leaf dimension (m) - heightCanopyTop, & ! intent(in): height at the top of the vegetation canopy (m) - heightCanopyBottom, & ! intent(in): height at the bottom of the vegetation canopy (m) + z0Ground, & ! intent(in): roughness length of the ground (below canopy or non-vegetated surface [snow]) (m) + z0CanopyParam, & ! intent(in): roughness length of the canopy (m) + zpdFraction, & ! intent(in): zero plane displacement / canopy height (-) + critRichNumber, & ! intent(in): critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam, & ! intent(in): parameter in Louis (1979) stability function + Mahrt87_eScale, & ! intent(in): exponential scaling factor in the Mahrt (1987) stability function + windReductionParam, & ! intent(in): canopy wind reduction parameter (-) + leafExchangeCoeff, & ! intent(in): turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) + leafDimension, & ! intent(in): characteristic leaf dimension (m) + heightCanopyTop, & ! intent(in): height at the top of the vegetation canopy (m) + heightCanopyBottom, & ! intent(in): height at the bottom of the vegetation canopy (m) ! output: stability corrections RiBulkCanopy, & ! intent(out): bulk Richardson number for the canopy (-) RiBulkGround, & ! intent(out): bulk Richardson number for the ground surface (-) @@ -1453,10 +1453,10 @@ subroutine aeroResist(& ! Mahat et al. (Below-canopy turbulence in a snowmelt model, WRR, 2012) implicit none ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) - integer(i4b),intent(in) :: ixVegTraits ! choice of parameterization for vegetation roughness length and displacement height - integer(i4b),intent(in) :: ixWindProfile ! choice of canopy wind profile - integer(i4b),intent(in) :: ixStability ! choice of stability function + logical(lgt),intent(in) :: computeVegFlux ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) + integer(i4b),intent(in) :: ixVegTraits ! choice of parameterization for vegetation roughness length and displacement height + integer(i4b),intent(in) :: ixWindProfile ! choice of canopy wind profile + integer(i4b),intent(in) :: ixStability ! choice of stability function ! input: above-canopy forcing data real(rkind),intent(in) :: mHeight ! measurement height (m) real(rkind),intent(in) :: airtemp ! air temperature at some height above the surface (K) @@ -1502,41 +1502,41 @@ subroutine aeroResist(& real(rkind),intent(out) :: dCanopyResistance_dTCanopy ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) real(rkind),intent(out) :: dCanopyResistance_dTCanair ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! ----------------------------------------------------------------------------------------------------------------------------------------- ! local variables: general - character(LEN=256) :: cmessage ! error message of downwind routine + character(LEN=256) :: cmessage ! error message of downwind routine ! local variables: vegetation roughness and dispalcement height - real(rkind),parameter :: oneThird=1._rkind/3._rkind ! 1/3 - real(rkind),parameter :: twoThirds=2._rkind/3._rkind ! 2/3 - real(rkind),parameter :: C_r = 0.3_rkind ! roughness element drag coefficient (-) from Raupach (BLM, 1994) - real(rkind),parameter :: C_s = 0.003_rkind ! substrate surface drag coefficient (-) from Raupach (BLM, 1994) - real(rkind),parameter :: approxDragCoef_max = 0.3_rkind ! maximum value of the approximate drag coefficient (-) from Raupach (BLM, 1994) - real(rkind),parameter :: psi_h = 0.193_rkind ! roughness sub-layer influence function (-) from Raupach (BLM, 1994) - real(rkind),parameter :: c_d1 = 7.5_rkind ! scaling parameter used to define displacement height (-) from Raupach (BLM, 1994) - real(rkind),parameter :: cd_CM = 0.2_rkind ! mean drag coefficient for individual leaves (-) from Choudhury and Monteith (QJRMS, 1988) - real(rkind) :: funcLAI ! temporary variable to calculate zero plane displacement for the canopy - real(rkind) :: fracCanopyHeight ! zero plane displacement expressed as a fraction of canopy height - real(rkind) :: approxDragCoef ! approximate drag coefficient used in the computation of canopy roughness length (-) + real(rkind),parameter :: oneThird=1._rkind/3._rkind ! 1/3 + real(rkind),parameter :: twoThirds=2._rkind/3._rkind ! 2/3 + real(rkind),parameter :: C_r = 0.3_rkind ! roughness element drag coefficient (-) from Raupach (BLM, 1994) + real(rkind),parameter :: C_s = 0.003_rkind ! substrate surface drag coefficient (-) from Raupach (BLM, 1994) + real(rkind),parameter :: approxDragCoef_max = 0.3_rkind ! maximum value of the approximate drag coefficient (-) from Raupach (BLM, 1994) + real(rkind),parameter :: psi_h = 0.193_rkind ! roughness sub-layer influence function (-) from Raupach (BLM, 1994) + real(rkind),parameter :: c_d1 = 7.5_rkind ! scaling parameter used to define displacement height (-) from Raupach (BLM, 1994) + real(rkind),parameter :: cd_CM = 0.2_rkind ! mean drag coefficient for individual leaves (-) from Choudhury and Monteith (QJRMS, 1988) + real(rkind) :: funcLAI ! temporary variable to calculate zero plane displacement for the canopy + real(rkind) :: fracCanopyHeight ! zero plane displacement expressed as a fraction of canopy height + real(rkind) :: approxDragCoef ! approximate drag coefficient used in the computation of canopy roughness length (-) ! local variables: resistance - real(rkind) :: canopyExNeut ! surface-atmosphere exchange coefficient under neutral conditions (-) - real(rkind) :: groundExNeut ! surface-atmosphere exchange coefficient under neutral conditions (-) - real(rkind) :: sfc2AtmExchangeCoeff_canopy ! surface-atmosphere exchange coefficient after stability corrections (-) - real(rkind) :: groundResistanceNeutral ! ground resistance under neutral conditions (s m-1) - real(rkind) :: windConvFactor_fv ! factor to convert friction velocity to wind speed at top of canopy (-) - real(rkind) :: windConvFactor ! factor to convert wind speed at top of canopy to wind speed at a given height in the canopy (-) - real(rkind) :: referenceHeight ! z0Canopy+zeroPlaneDisplacement (m) - real(rkind) :: windspdRefHeight ! windspeed at the reference height (m/s) - real(rkind) :: heightAboveGround ! height above the snow surface (m) - real(rkind) :: heightCanopyTopAboveSnow ! height at the top of the vegetation canopy relative to snowpack (m) - real(rkind) :: heightCanopyBottomAboveSnow ! height at the bottom of the vegetation canopy relative to snowpack (m) - real(rkind),parameter :: xTolerance=0.1_rkind ! tolerance to handle the transition from exponential to log-below canopy + real(rkind) :: canopyExNeut ! surface-atmosphere exchange coefficient under neutral conditions (-) + real(rkind) :: groundExNeut ! surface-atmosphere exchange coefficient under neutral conditions (-) + real(rkind) :: sfc2AtmExchangeCoeff_canopy ! surface-atmosphere exchange coefficient after stability corrections (-) + real(rkind) :: groundResistanceNeutral ! ground resistance under neutral conditions (s m-1) + real(rkind) :: windConvFactor_fv ! factor to convert friction velocity to wind speed at top of canopy (-) + real(rkind) :: windConvFactor ! factor to convert wind speed at top of canopy to wind speed at a given height in the canopy (-) + real(rkind) :: referenceHeight ! z0Canopy+zeroPlaneDisplacement (m) + real(rkind) :: windspdRefHeight ! windspeed at the reference height (m/s) + real(rkind) :: heightAboveGround ! height above the snow surface (m) + real(rkind) :: heightCanopyTopAboveSnow ! height at the top of the vegetation canopy relative to snowpack (m) + real(rkind) :: heightCanopyBottomAboveSnow ! height at the bottom of the vegetation canopy relative to snowpack (m) + real(rkind),parameter :: xTolerance=0.1_rkind ! tolerance to handle the transition from exponential to log-below canopy ! local variables: derivatives - real(rkind) :: dFV_dT ! derivative in friction velocity w.r.t. canopy air temperature - real(rkind) :: dED_dT ! derivative in eddy diffusivity at the top of the canopy w.r.t. canopy air temperature - real(rkind) :: dGR_dT ! derivative in neutral ground resistance w.r.t. canopy air temperature - real(rkind) :: tmp1,tmp2 ! temporary variables used in calculation of ground resistance + real(rkind) :: dFV_dT ! derivative in friction velocity w.r.t. canopy air temperature + real(rkind) :: dED_dT ! derivative in eddy diffusivity at the top of the canopy w.r.t. canopy air temperature + real(rkind) :: dGR_dT ! derivative in neutral ground resistance w.r.t. canopy air temperature + real(rkind) :: tmp1,tmp2 ! temporary variables used in calculation of ground resistance real(rkind) :: dCanopyStabilityCorrection_dRich ! derivative in stability correction w.r.t. Richardson number for the canopy (-) real(rkind) :: dGroundStabilityCorrection_dRich ! derivative in stability correction w.r.t. Richardson number for the ground surface (-) real(rkind) :: dCanopyStabilityCorrection_dAirTemp ! (not used) derivative in stability correction w.r.t. air temperature (K-1) @@ -1544,9 +1544,9 @@ subroutine aeroResist(& real(rkind) :: dCanopyStabilityCorrection_dCasTemp ! derivative in canopy stability correction w.r.t. canopy air space temperature (K-1) real(rkind) :: dGroundStabilityCorrection_dCasTemp ! derivative in ground stability correction w.r.t. canopy air space temperature (K-1) real(rkind) :: dGroundStabilityCorrection_dSfcTemp ! derivative in ground stability correction w.r.t. surface temperature (K-1) - real(rkind) :: singleLeafConductance ! leaf boundary layer conductance (m s-1) - real(rkind) :: canopyLeafConductance ! leaf boundary layer conductance -- scaled up to the canopy (m s-1) - real(rkind) :: leaf2CanopyScaleFactor ! factor to scale from the leaf to the canopy [m s-(1/2)] + real(rkind) :: singleLeafConductance ! leaf boundary layer conductance (m s-1) + real(rkind) :: canopyLeafConductance ! leaf boundary layer conductance -- scaled up to the canopy (m s-1) + real(rkind) :: leaf2CanopyScaleFactor ! factor to scale from the leaf to the canopy [m s-(1/2)] ! ----------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='aeroResist/' @@ -1558,7 +1558,7 @@ subroutine aeroResist(& ! ----------------------------------------------------------------------------------------------------------------------------------------- ! * compute vegetation poperties (could be done at the same time as phenology.. does not have to be in the flux routine!) - if(computeVegFlux) then ! (if vegetation is exposed) + if (computeVegFlux) then ! if vegetation is exposed ! ***** identify zero plane displacement, roughness length, and surface temperature for the canopy (m) ! First, calculate new coordinate system above snow - use these to scale wind profiles and resistances @@ -1594,16 +1594,16 @@ subroutine aeroResist(& end select ! vegetation traits (z0, zpd) ! check zero plane displacement - if(zeroPlaneDisplacement < heightCanopyBottomAboveSnow)then + if (zeroPlaneDisplacement < heightCanopyBottomAboveSnow) then write(*,'(a,1x,10(f12.5,1x))') 'heightCanopyTop, snowDepth, heightCanopyTopAboveSnow, heightCanopyBottomAboveSnow, exposedVAI = ', & heightCanopyTop, snowDepth, heightCanopyTopAboveSnow, heightCanopyBottomAboveSnow, exposedVAI message=trim(message)//'zero plane displacement is below the canopy bottom' err=20; return - endif + end if ! check measurement height - if(mHeight < zeroPlaneDisplacement)then; err=20; message=trim(message)//'measurement height is below the displacement height'; return; end if - if(mHeight < z0Canopy)then; err=20; message=trim(message)//'measurement height is below the roughness length'; return; end if + if (mHeight < zeroPlaneDisplacement) then; err=20; message=trim(message)//'measurement height is below the displacement height'; return; end if + if (mHeight < z0Canopy) then; err=20; message=trim(message)//'measurement height is below the roughness length'; return; end if ! ----------------------------------------------------------------------------------------------------------------------------------------- ! ----------------------------------------------------------------------------------------------------------------------------------------- @@ -1611,16 +1611,16 @@ subroutine aeroResist(& ! compute the stability correction for resistance from canopy air space to air above the canopy (-) call aStability(& ! input - ixStability, & ! input: choice of stability function + ixStability, & ! input: choice of stability function ! input: forcing data, diagnostic and state variables - mHeight, & ! input: measurement height (m) - airTemp, & ! input: air temperature above the canopy (K) - canairTemp, & ! input: temperature of the canopy air space (K) - windspd, & ! input: wind speed above the canopy (m s-1) + mHeight, & ! input: measurement height (m) + airTemp, & ! input: air temperature above the canopy (K) + canairTemp, & ! input: temperature of the canopy air space (K) + windspd, & ! input: wind speed above the canopy (m s-1) ! input: stability parameters - critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam, & ! input: parameter in Louis (1979) stability function - Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function + critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam, & ! input: parameter in Louis (1979) stability function + Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function ! output RiBulkCanopy, & ! output: bulk Richardson number (-) canopyStabilityCorrection, & ! output: stability correction for turbulent heat fluxes (-) @@ -1628,7 +1628,7 @@ subroutine aeroResist(& dCanopyStabilityCorrection_dAirTemp, & ! output: (not used) derivative in stability correction w.r.t. air temperature (K-1) dCanopyStabilityCorrection_dCasTemp, & ! output: derivative in stability correction w.r.t. canopy air space temperature (K-1) err, cmessage ) ! output: error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! compute turbulent exchange coefficient (-) canopyExNeut = (vkc**2_i4b) / ( log((mHeight - zeroPlaneDisplacement)/z0Canopy))**2_i4b ! coefficient under conditions of neutral stability @@ -1639,7 +1639,7 @@ subroutine aeroResist(& ! compute the above-canopy resistance (s m-1) canopyResistance = 1._rkind/(sfc2AtmExchangeCoeff_canopy*windspd) - if(canopyResistance < 0._rkind)then; err=20; message=trim(message)//'canopy resistance < 0'; return; end if + if (canopyResistance < 0._rkind) then; err=20; message=trim(message)//'canopy resistance < 0'; return; end if ! compute windspeed at the top of the canopy above snow depth (m s-1) ! NOTE: stability corrections cancel out @@ -1664,7 +1664,7 @@ subroutine aeroResist(& leaf2CanopyScaleFactor = (2._rkind/windReductionFactor) * (1._rkind - exp(-windReductionFactor/2._rkind)) ! factor to scale from the leaf to the canopy canopyLeafConductance = singleLeafConductance*leaf2CanopyScaleFactor leafResistance = 1._rkind/(canopyLeafConductance) - if(leafResistance < 0._rkind)then; err=20; message=trim(message)//'leaf resistance < 0'; return; end if + if (leafResistance < 0._rkind) then; err=20; message=trim(message)//'leaf resistance < 0'; return; end if ! compute eddy diffusivity for heat at the top of the canopy (m2 s-1) ! Note: use of friction velocity here includes stability adjustments @@ -1672,43 +1672,38 @@ subroutine aeroResist(& eddyDiffusCanopyTop = max(vkc*FrictionVelocity*(heightCanopyTopAboveSnow - zeroPlaneDisplacement), mpe) ! compute the resistance between the surface and canopy air UNDER NEUTRAL CONDITIONS (s m-1) - ! case 1: assume exponential profile extends from the snow depth plus surface roughness length to the displacement height plus vegetation roughness - if(ixWindProfile==exponential .or. heightCanopyBottomAboveSnowz0Ground+xTolerance else - ! compute the neutral ground resistance - ! (first, component between heightCanopyBottomAboveSnow and z0Canopy+zeroPlaneDisplacement) + ! first, component between heightCanopyBottomAboveSnow and z0Canopy+zeroPlaneDisplacement tmp1 = exp(-windReductionFactor* heightCanopyBottomAboveSnow/heightCanopyTopAboveSnow) tmp2 = exp(-windReductionFactor*(z0Canopy+zeroPlaneDisplacement)/heightCanopyTopAboveSnow) groundResistanceNeutral = ( heightCanopyTopAboveSnow*exp(windReductionFactor) / (windReductionFactor*eddyDiffusCanopyTop) ) * (tmp1 - tmp2) - ! (add log-below-canopy component) + ! add log-below-canopy component groundResistanceNeutral = groundResistanceNeutral + (1._rkind/(max(0.1_rkind,windspdCanopyBottom)*vkc**2_i4b))*(log(heightCanopyBottomAboveSnow/z0Ground))**2_i4b - endif ! switch between exponential profile and log-below-canopy ! compute the stability correction for resistance from the ground to the canopy air space (-) ! NOTE: here we are interested in the windspeed at height z0Canopy+zeroPlaneDisplacement call aStability(& ! input - ixStability, & ! input: choice of stability function + ixStability, & ! input: choice of stability function ! input: forcing data, diagnostic and state variables - referenceHeight, & ! input: height of the canopy air space temperature/wind (m) - canairTemp, & ! input: temperature of the canopy air space (K) - groundTemp, & ! input: temperature of the ground surface (K) - max(0.1_rkind,windspdRefHeight), & ! input: wind speed at height z0Canopy+zeroPlaneDisplacement (m s-1) + referenceHeight, & ! input: height of the canopy air space temperature/wind (m) + canairTemp, & ! input: temperature of the canopy air space (K) + groundTemp, & ! input: temperature of the ground surface (K) + max(0.1_rkind,windspdRefHeight), & ! input: wind speed at height z0Canopy+zeroPlaneDisplacement (m s-1) ! input: stability parameters - critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam, & ! input: parameter in Louis (1979) stability function - Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function + critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam, & ! input: parameter in Louis (1979) stability function + Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function ! output RiBulkGround, & ! output: bulk Richardson number (-) groundStabilityCorrection, & ! output: stability correction for turbulent heat fluxes (-) @@ -1716,11 +1711,11 @@ subroutine aeroResist(& dGroundStabilityCorrection_dCasTemp, & ! output: derivative in stability correction w.r.t. canopy air space temperature (K-1) dGroundStabilityCorrection_dSfcTemp, & ! output: derivative in stability correction w.r.t. surface temperature (K-1) err, cmessage ) ! output: error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! compute the ground resistance groundResistance = groundResistanceNeutral / groundStabilityCorrection - if(groundResistance < 0._rkind)then; err=20; message=trim(message)//'ground resistance < 0 [vegetation is present]'; return; end if + if (groundResistance < 0._rkind) then; err=20; message=trim(message)//'ground resistance < 0 [vegetation is present]'; return; end if ! ----------------------------------------------------------------------------------------------------------------------------------------- ! ----------------------------------------------------------------------------------------------------------------------------------------- @@ -1742,7 +1737,7 @@ subroutine aeroResist(& heightAboveGround = mHeight - snowDepth ! check that measurement height above the ground surface is above the roughness length - if(heightAboveGround < z0Ground)then + if (heightAboveGround < z0Ground) then print*, 'z0Ground = ', z0Ground print*, 'mHeight = ', mHeight print*, 'snowDepth = ', snowDepth @@ -1754,16 +1749,16 @@ subroutine aeroResist(& ! compute ground stability correction call aStability(& ! input - ixStability, & ! input: choice of stability function + ixStability, & ! input: choice of stability function ! input: forcing data, diagnostic and state variables - heightAboveGround, & ! input: measurement height above the ground surface (m) - airtemp, & ! input: temperature above the ground surface (K) - groundTemp, & ! input: trial value of surface temperature -- "surface" is either canopy or ground (K) - windspd, & ! input: wind speed above the ground surface (m s-1) + heightAboveGround, & ! input: measurement height above the ground surface (m) + airtemp, & ! input: temperature above the ground surface (K) + groundTemp, & ! input: trial value of surface temperature -- "surface" is either canopy or ground (K) + windspd, & ! input: wind speed above the ground surface (m s-1) ! input: stability parameters - critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam, & ! input: parameter in Louis (1979) stability function - Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function + critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam, & ! input: parameter in Louis (1979) stability function + Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function ! output RiBulkGround, & ! output: bulk Richardson number (-) groundStabilityCorrection, & ! output: stability correction for turbulent heat fluxes (-) @@ -1771,11 +1766,11 @@ subroutine aeroResist(& dGroundStabilityCorrection_dAirTemp, & ! output: (not used) derivative in stability correction w.r.t. air temperature (K-1) dGroundStabilityCorrection_dSfcTemp, & ! output: derivative in stability correction w.r.t. surface temperature (K-1) err, cmessage ) ! output: error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! compute the ground resistance (after stability corrections) groundResistance = groundResistanceNeutral/groundStabilityCorrection - if(groundResistance < 0._rkind)then; err=20; message=trim(message)//'ground resistance < 0 [no vegetation]'; return; end if + if (groundResistance < 0._rkind) then; err=20; message=trim(message)//'ground resistance < 0 [no vegetation]'; return; end if ! set all canopy variables to missing (no canopy!) z0Canopy = missingValue ! roughness length of the vegetation canopy (m) @@ -1788,10 +1783,10 @@ subroutine aeroResist(& windspdCanopyTop = missingValue ! windspeed at the top of the canopy (m s-1) windspdCanopyBottom = missingValue ! windspeed at the height of the bottom of the canopy (m s-1) - end if ! (if no canopy) + end if ! end if no canopy ! derivatives for the vegetation canopy - if(computeVegFlux) then ! (if vegetation is exposed) + if (computeVegFlux) then ! if vegetation is exposed ! ***** compute derivatives w.r.t. canopy temperature ! NOTE: derivatives are zero because using canopy air space temperature dCanopyResistance_dTCanopy = 0._rkind ! derivative in canopy resistance w.r.t. canopy temperature (s m-1 K-1) @@ -1802,11 +1797,11 @@ subroutine aeroResist(& ! derivative in canopy resistance w.r.t. canopy air temperature (s m-1 K-1) dCanopyResistance_dTCanair = -dCanopyStabilityCorrection_dCasTemp/(windspd*canopyExNeut*canopyStabilityCorrection**2_i4b) ! derivative in ground resistance w.r.t. canopy air temperature (s m-1 K-1) - ! (compute derivative in NEUTRAL ground resistance w.r.t. canopy air temperature (s m-1 K-1)) - dFV_dT = windspd*canopyExNeut*dCanopyStabilityCorrection_dCasTemp/(sqrt(sfc2AtmExchangeCoeff_canopy)*2._rkind) ! d(frictionVelocity)/d(canopy air temperature) + ! compute derivative in NEUTRAL ground resistance w.r.t. canopy air temperature (s m-1 K-1) + dFV_dT = windspd*canopyExNeut*dCanopyStabilityCorrection_dCasTemp/(sqrt(sfc2AtmExchangeCoeff_canopy)*2._rkind) ! d(frictionVelocity)/d(canopy air temperature) dED_dT = dFV_dT*vkc*(heightCanopyTopAboveSnow - zeroPlaneDisplacement) ! d(eddyDiffusCanopyTop)d(canopy air temperature) dGR_dT = -dED_dT*(tmp1 - tmp2)*heightCanopyTopAboveSnow*exp(windReductionFactor) / (windReductionFactor*eddyDiffusCanopyTop**2_i4b) ! d(groundResistanceNeutral)/d(canopy air temperature) - ! (stitch everything together -- product rule) + ! stitch everything together -- product rule dGroundResistance_dTCanair = dGR_dT/groundStabilityCorrection - groundResistanceNeutral*dGroundStabilityCorrection_dCasTemp/(groundStabilityCorrection**2_i4b) ! ***** compute resistances for non-vegetated surfaces (e.g., snow) else @@ -1815,7 +1810,7 @@ subroutine aeroResist(& dGroundResistance_dTCanopy = 0._rkind ! compute derivatives for ground resistance dGroundResistance_dTGround = -dGroundStabilityCorrection_dSfcTemp/(windspd*groundExNeut*groundStabilityCorrection**2_i4b) - end if ! (switch between vegetated and non-vegetated surfaces) + end if ! end switch between vegetated and non-vegetated surfaces end subroutine aeroResist @@ -1824,29 +1819,29 @@ end subroutine aeroResist ! ******************************************************************************************************* subroutine soilResist(& ! input (model decisions) - ixSoilResist, & ! intent(in): choice of function for the soil moisture control on stomatal resistance - ixGroundwater, & ! intent(in): choice of groundwater representation + ixSoilResist, & ! intent(in): choice of function for the soil moisture control on stomatal resistance + ixGroundwater, & ! intent(in): choice of groundwater representation ! input (state variables) - mLayerMatricHead, & ! intent(in): matric head in each layer (m) - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water in each layer - scalarAquiferStorage, & ! intent(in): aquifer storage (m) + mLayerMatricHead, & ! intent(in): matric head in each layer (m) + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water in each layer + scalarAquiferStorage, & ! intent(in): aquifer storage (m) ! input (diagnostic variables) - mLayerRootDensity, & ! intent(in): root density in each layer (-) - scalarAquiferRootFrac, & ! intent(in): fraction of roots below the lowest unsaturated layer (-) + mLayerRootDensity, & ! intent(in): root density in each layer (-) + scalarAquiferRootFrac, & ! intent(in): fraction of roots below the lowest unsaturated layer (-) ! input (parameters) - plantWiltPsi, & ! intent(in): matric head at wilting point (m) - soilStressParam, & ! intent(in): parameter in the exponential soil stress function (-) - critSoilWilting, & ! intent(in): critical vol. liq. water content when plants are wilting (-) - critSoilTranspire, & ! intent(in): critical vol. liq. water content when transpiration is limited (-) - critAquiferTranspire, & ! intent(in): critical aquifer storage value when transpiration is limited (m) + plantWiltPsi, & ! intent(in): matric head at wilting point (m) + soilStressParam, & ! intent(in): parameter in the exponential soil stress function (-) + critSoilWilting, & ! intent(in): critical vol. liq. water content when plants are wilting (-) + critSoilTranspire, & ! intent(in): critical vol. liq. water content when transpiration is limited (-) + critAquiferTranspire, & ! intent(in): critical aquifer storage value when transpiration is limited (m) ! output wAvgTranspireLimitFac, & ! intent(out): weighted average of the transpiration limiting factor (-) mLayerTranspireLimitFac, & ! intent(out): transpiration limiting factor in each layer (-) aquiferTranspireLimitFac, & ! intent(out): transpiration limiting factor for the aquifer (-) err,message) ! intent(out): error control ! ----------------------------------------------------------------------------------------------------------------------------------------- - USE mDecisions_module, only: NoahType,CLM_Type,SiB_Type ! options for the choice of function for the soil moisture control on stomatal resistance - USE mDecisions_module, only: bigBucket ! named variable that defines the "bigBucket" groundwater parameterization + USE mDecisions_module, only: NoahType,CLM_Type,SiB_Type ! options for the choice of function for the soil moisture control on stomatal resistance + USE mDecisions_module, only: bigBucket ! named variable that defines the "bigBucket" groundwater parameterization implicit none ! input (model decisions) integer(i4b),intent(in) :: ixSoilResist ! choice of function for the soil moisture control on stomatal resistance @@ -1865,35 +1860,35 @@ subroutine soilResist(& real(rkind),intent(in) :: critSoilTranspire ! critical vol. liq. water content when transpiration is limited (-) real(rkind),intent(in) :: critAquiferTranspire ! critical aquifer storage value when transpiration is limited (m) ! output - real(rkind),intent(out) :: wAvgTranspireLimitFac ! intent(out): weighted average of the transpiration limiting factor (-) + real(rkind),intent(out) :: wAvgTranspireLimitFac ! intent(out): weighted average of the transpiration limiting factor (-) real(rkind),intent(out) :: mLayerTranspireLimitFac(:) ! intent(out): transpiration limiting factor in each layer (-) - real(rkind),intent(out) :: aquiferTranspireLimitFac ! intent(out): transpiration limiting factor for the aquifer (-) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + real(rkind),intent(out) :: aquiferTranspireLimitFac ! intent(out): transpiration limiting factor for the aquifer (-) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! local variables - real(rkind) :: gx ! stress function for the soil layers - real(rkind),parameter :: verySmall=epsilon(gx) ! a very small number - integer(i4b) :: iLayer ! index of soil layer + real(rkind) :: gx ! stress function for the soil layers + real(rkind),parameter :: verySmall=epsilon(gx) ! a very small number + integer(i4b) :: iLayer ! index of soil layer ! initialize error control err=0; message='soilResist/' ! ** compute the factor limiting transpiration for each soil layer (-) - wAvgTranspireLimitFac = 0._rkind ! (initialize the weighted average) + wAvgTranspireLimitFac = 0._rkind ! initialize the weighted average do iLayer=1,size(mLayerMatricHead) ! compute the soil stress function select case(ixSoilResist) case(NoahType) ! thresholded linear function of volumetric liquid water content gx = (mLayerVolFracLiq(iLayer) - critSoilWilting) / (critSoilTranspire - critSoilWilting) case(CLM_Type) ! thresholded linear function of matric head - if(mLayerMatricHead(iLayer) > plantWiltPsi)then + if (mLayerMatricHead(iLayer) > plantWiltPsi) then gx = 1._rkind - mLayerMatricHead(iLayer)/plantWiltPsi else gx = 0._rkind end if case(SiB_Type) ! exponential of the log of matric head - if(mLayerMatricHead(iLayer) < 0._rkind)then ! (unsaturated) + if (mLayerMatricHead(iLayer) < 0._rkind) then ! unsaturated gx = 1._rkind - exp( -soilStressParam * ( log(plantWiltPsi/mLayerMatricHead(iLayer)) ) ) - else ! (saturated) + else ! saturated gx = 1._rkind end if case default ! check identified the option @@ -1903,18 +1898,18 @@ subroutine soilResist(& mLayerTranspireLimitFac(iLayer) = min( max(verySmall,gx), 1._rkind) ! compute the weighted average (weighted by root density) wAvgTranspireLimitFac = wAvgTranspireLimitFac + mLayerTranspireLimitFac(iLayer)*mLayerRootDensity(iLayer) - end do ! (looping through soil layers) + end do ! end looping through soil layers ! ** compute the factor limiting evaporation in the aquifer - if(scalarAquiferRootFrac > verySmall)then + if (scalarAquiferRootFrac > verySmall) then ! check that aquifer root fraction is allowed - if(ixGroundwater /= bigBucket)then + if (ixGroundwater /= bigBucket) then message=trim(message)//'aquifer evaporation only allowed for the big groundwater bucket -- increase the soil depth to account for roots' err=20; return end if ! compute the factor limiting evaporation for the aquifer aquiferTranspireLimitFac = min(scalarAquiferStorage/critAquiferTranspire, 1._rkind) - else ! (if there are roots in the aquifer) + else ! if there are roots in the aquifer aquiferTranspireLimitFac = 0._rkind end if @@ -2170,7 +2165,7 @@ subroutine turbFluxes(& ! ****************************************** ! compute conductances for sensible heat (m s-1) - if(computeVegFlux)then + if (computeVegFlux) then leafConductance = exposedVAI/leafResistance leafConductanceTr = canopySunlitLAI/(leafResistance+stomResistSunlit) + canopyShadedLAI/(leafResistance+stomResistShaded) canopyConductance = 1._rkind/canopyResistance @@ -2195,16 +2190,16 @@ subroutine turbFluxes(& totalConductanceLH = evapConductance + transConductance + groundConductanceLH + canopyConductance ! check sensible heat conductance - if(totalConductanceSH < -tinyVal .or. groundConductanceSH < -tinyVal .or. canopyConductance < -tinyVal)then + if (totalConductanceSH < -tinyVal .or. groundConductanceSH < -tinyVal .or. canopyConductance < -tinyVal) then message=trim(message)//'negative conductance for sensible heat' err=20; return - endif + end if ! check latent heat conductance - if(totalConductanceLH < tinyVal .or. groundConductanceLH < -tinyVal)then + if (totalConductanceLH < tinyVal .or. groundConductanceLH < -tinyVal) then message=trim(message)//'negative conductance for latent heat' err=20; return - endif + end if ! compute derivatives in individual conductances for sensible heat w.r.t. canopy temperature (m s-1 K-1) ! NOTE: it may be more efficient to compute these derivatives when computing resistances @@ -2217,12 +2212,12 @@ subroutine turbFluxes(& dGroundCondSH_dCanopyTemp = -dGroundResistance_dTCanopy/groundResistance**2_i4b ! derivative in ground conductance w.r.t. canopy temperature dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2_i4b ! derivative in ground conductance w.r.t. ground temperature else - dEvapCond_dCanopyTemp = 0._rkind ! derivative in evap conductance w.r.t. canopy temperature - dTransCond_dCanopyTemp = 0._rkind ! derivative in trans conductance w.r.t. canopy temperature - dCanopyCond_dCanairTemp = 0._rkind ! derivative in canopy conductance w.r.t. canopy air emperature - dCanopyCond_dCanopyTemp = 0._rkind ! derivative in canopy conductance w.r.t. canopy temperature - dGroundCondSH_dCanairTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy air temperature - dGroundCondSH_dCanopyTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy temperature + dEvapCond_dCanopyTemp = 0._rkind ! derivative in evap conductance w.r.t. canopy temperature + dTransCond_dCanopyTemp = 0._rkind ! derivative in trans conductance w.r.t. canopy temperature + dCanopyCond_dCanairTemp = 0._rkind ! derivative in canopy conductance w.r.t. canopy air emperature + dCanopyCond_dCanopyTemp = 0._rkind ! derivative in canopy conductance w.r.t. canopy temperature + dGroundCondSH_dCanairTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy air temperature + dGroundCondSH_dCanopyTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy temperature dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2_i4b ! derivative in ground conductance w.r.t. ground temperature endif @@ -2242,8 +2237,7 @@ subroutine turbFluxes(& ! ************************************************************* ! * compute sensible and latent heat fluxes from the canopy to the canopy air space (W m-2) - if(computeVegFlux)then - + if (computeVegFlux) then ! compute the vapor pressure in the canopy air space (Pa) fPart_VP = canopyConductance*VPair + (evapConductance + transConductance)*satVP_CanopyTemp + groundConductanceLH*satVP_GroundTemp*soilRelHumidity VP_CanopyAir = fPart_VP/totalConductanceLH @@ -2253,26 +2247,25 @@ subroutine turbFluxes(& senHeatTotal = -volHeatCapacityAir*canopyConductance*(canairTemp - airtemp) ! compute fluxes - senHeatCanopy = -volHeatCapacityAir*leafConductance*(canopyTemp - canairTemp) ! (positive downwards) - latHeatCanopyEvap = -latHeatSubVapCanopy*latentHeatConstant*evapConductance*(satVP_CanopyTemp - VP_CanopyAir) ! (positive downwards) - latHeatCanopyTrans = -LH_vap*latentHeatConstant*transConductance*(satVP_CanopyTemp - VP_CanopyAir) ! (positive downwards) - + senHeatCanopy = -volHeatCapacityAir*leafConductance*(canopyTemp - canairTemp) ! positive downwards + latHeatCanopyEvap = -latHeatSubVapCanopy*latentHeatConstant*evapConductance*(satVP_CanopyTemp - VP_CanopyAir) ! positive downwards + latHeatCanopyTrans = -LH_vap*latentHeatConstant*transConductance*(satVP_CanopyTemp - VP_CanopyAir) ! positive downwards ! * no vegetation, so fluxes are zero else senHeatCanopy = 0._rkind latHeatCanopyEvap = 0._rkind latHeatCanopyTrans = 0._rkind - endif + end if ! compute sensible and latent heat fluxes from the ground to the canopy air space (W m-2) - if(computeVegFlux)then - senHeatGround = -volHeatCapacityAir*groundConductanceSH*(groundTemp - canairTemp) ! (positive downwards) - latHeatGround = -latHeatSubVapGround*latentHeatConstant*groundConductanceLH*(satVP_GroundTemp*soilRelHumidity - VP_CanopyAir) ! (positive downwards) + if (computeVegFlux) then + senHeatGround = -volHeatCapacityAir*groundConductanceSH*(groundTemp - canairTemp) ! positive downwards + latHeatGround = -latHeatSubVapGround*latentHeatConstant*groundConductanceLH*(satVP_GroundTemp*soilRelHumidity - VP_CanopyAir) ! positive downwards else - senHeatGround = -volHeatCapacityAir*groundConductanceSH*(groundTemp - airtemp) ! (positive downwards) - latHeatGround = -latHeatSubVapGround*latentHeatConstant*groundConductanceLH*(satVP_GroundTemp*soilRelHumidity - VPair) ! (positive downwards) + senHeatGround = -volHeatCapacityAir*groundConductanceSH*(groundTemp - airtemp) ! positive downwards + latHeatGround = -latHeatSubVapGround*latentHeatConstant*groundConductanceLH*(satVP_GroundTemp*soilRelHumidity - VPair) ! positive downwards senHeatTotal = senHeatGround - endif + end if ! compute latent heat flux from the canopy air space to the atmosphere ! NOTE: VP_CanopyAir is a diagnostic variable @@ -2281,7 +2274,6 @@ subroutine turbFluxes(& ! * compute derivatives ! differentiate CANOPY fluxes if (computeVegFlux) then - ! compute derivatives of vapor pressure in the canopy air space w.r.t. all state variables ! derivative of vapor pressure in the canopy air space w.r.t. temperature of the canopy air space dPart1 = dCanopyCond_dCanairTemp*VPair + dGroundCondLH_dCanairTemp*satVP_GroundTemp*soilRelHumidity @@ -2318,21 +2310,21 @@ subroutine turbFluxes(& dSenHeatGround_dTGround = -volHeatCapacityAir*dGroundCondSH_dGroundTemp*(groundTemp - canairTemp) - volHeatCapacityAir*groundConductanceSH ! latent heat associated with canopy evaporation - ! (initial calculations) + ! initial calculations fPart1 = -latHeatSubVapCanopy*latentHeatConstant*evapConductance dPart1 = -latHeatSubVapCanopy*latentHeatConstant*dEvapCond_dCanopyTemp fPart2 = satVP_CanopyTemp - VP_CanopyAir dPart2 = dSVPCanopy_dCanopyTemp - dVPCanopyAir_dTCanopy - ! (derivatives) + ! derivatives dLatHeatCanopyEvap_dTCanair = fPart1*(-dVPCanopyAir_dTCanair) dLatHeatCanopyEvap_dTCanopy = fPart1*dpart2 + fPart2*dPart1 dLatHeatCanopyEvap_dTGround = fPart1*(-dVPCanopyAir_dTGround) ! latent heat associated with canopy transpiration - ! (initial calculations) + ! initial calculations fPart1 = -LH_vap*latentHeatConstant*transConductance dPart1 = -LH_vap*latentHeatConstant*dTransCond_dCanopyTemp - ! (derivatives) + ! derivatives dLatHeatCanopyTrans_dTCanair = fPart1*(-dVPCanopyAir_dTCanair) dLatHeatCanopyTrans_dTCanopy = fPart1*dPart2 + fPart2*dPart1 dLatHeatCanopyTrans_dTGround = fPart1*(-dVPCanopyAir_dTGround) @@ -2356,9 +2348,7 @@ subroutine turbFluxes(& ! latent heat associated with canopy transpiration w.r.t. canopy total water dLatHeatCanopyTrans_dCanWat = dLatHeatCanopyTrans_dWetFrac*dCanopyWetFraction_dWat ! (J s-1 kg-1) - else ! canopy is undefined - ! set derivatives for canopy fluxes to zero (no canopy, so fluxes are undefined) dSenHeatTotal_dTCanair = 0._rkind dSenHeatTotal_dTCanopy = 0._rkind @@ -2390,7 +2380,7 @@ subroutine turbFluxes(& (-volHeatCapacityAir*groundConductanceSH) dLatHeatGroundEvap_dTGround = (-latHeatSubVapGround*latentHeatConstant*dGroundCondLH_dGroundTemp)*(satVP_GroundTemp*soilRelHumidity - VPair) + & ! d(ground latent heat flux)/d(ground temp) (-latHeatSubVapGround*latentHeatConstant*groundConductanceLH)*dSVPGround_dGroundTemp*soilRelHumidity - end if ! (if canopy is defined) + end if ! end if canopy is defined ! ***** ! * compute net turbulent fluxes, and derivatives... @@ -2402,7 +2392,7 @@ subroutine turbFluxes(& turbFluxGround = senHeatGround + latHeatGround ! net turbulent flux at the ground surface (W m-2) ! * compute derivatives - ! (energy derivatives) + ! energy derivatives dTurbFluxCanair_dTCanair = dSenHeatTotal_dTCanair - dSenHeatCanopy_dTCanair - dSenHeatGround_dTCanair ! derivative in net canopy air space fluxes w.r.t. canopy air temperature (W m-2 K-1) dTurbFluxCanair_dTCanopy = dSenHeatTotal_dTCanopy - dSenHeatCanopy_dTCanopy - dSenHeatGround_dTCanopy ! derivative in net canopy air space fluxes w.r.t. canopy temperature (W m-2 K-1) dTurbFluxCanair_dTGround = dSenHeatTotal_dTGround - dSenHeatCanopy_dTGround - dSenHeatGround_dTGround ! derivative in net canopy air space fluxes w.r.t. ground temperature (W m-2 K-1) @@ -2412,11 +2402,11 @@ subroutine turbFluxes(& dTurbFluxGround_dTCanair = dSenHeatGround_dTCanair + dLatHeatGroundEvap_dTCanair ! derivative in net ground turbulent fluxes w.r.t. canopy air temperature (W m-2 K-1) dTurbFluxGround_dTCanopy = dSenHeatGround_dTCanopy + dLatHeatGroundEvap_dTCanopy ! derivative in net ground turbulent fluxes w.r.t. canopy temperature (W m-2 K-1) dTurbFluxGround_dTGround = dSenHeatGround_dTGround + dLatHeatGroundEvap_dTGround ! derivative in net ground turbulent fluxes w.r.t. ground temperature (W m-2 K-1) - ! (liquid water derivatives) + ! liquid water derivatives dLatHeatCanopyEvap_dCanWat = dLatHeatCanopyEvap_dWetFrac*dCanopyWetFraction_dWat ! derivative in latent heat of canopy evaporation w.r.t. canopy total water (W kg-1) dLatHeatGroundEvap_dCanWat = latHeatSubVapGround*latentHeatConstant*groundConductanceLH*dVPCanopyAir_dCanWat ! derivative in latent heat of ground evaporation w.r.t. canopy total water (J kg-1 s-1) - ! (cross derivatives) - dTurbFluxCanair_dCanWat = 0._rkind ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) + ! cross derivatives + dTurbFluxCanair_dCanWat = 0._rkind ! derivative in net canopy air space fluxes w.r.t. canopy total water content (J kg-1 s-1) dTurbFluxCanopy_dCanWat = dLatHeatCanopyEvap_dCanWat + dLatHeatCanopyTrans_dCanWat ! derivative in net canopy turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) dTurbFluxGround_dCanWat = dLatHeatGroundEvap_dCanWat ! derivative in net ground turbulent fluxes w.r.t. canopy total water content (J kg-1 s-1) @@ -2427,16 +2417,16 @@ end subroutine turbFluxes ! ******************************************************************************************************* subroutine aStability(& ! input: control - ixStability, & ! input: choice of stability function + ixStability, & ! input: choice of stability function ! input: forcing data, diagnostic and state variables - mHeight, & ! input: measurement height (m) - airTemp, & ! input: air temperature (K) - sfcTemp, & ! input: surface temperature (K) - windspd, & ! input: wind speed (m s-1) + mHeight, & ! input: measurement height (m) + airTemp, & ! input: air temperature (K) + sfcTemp, & ! input: surface temperature (K) + windspd, & ! input: wind speed (m s-1) ! input: stability parameters - critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) - Louis79_bparam, & ! input: parameter in Louis (1979) stability function - Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function + critRichNumber, & ! input: critical value for the bulk Richardson number where turbulence ceases (-) + Louis79_bparam, & ! input: parameter in Louis (1979) stability function + Mahrt87_eScale, & ! input: exponential scaling factor in the Mahrt (1987) stability function ! output RiBulk, & ! output: bulk Richardson number (-) stabilityCorrection, & ! output: stability correction for turbulent heat fluxes (-) @@ -2446,29 +2436,29 @@ subroutine aStability(& err, message ) ! output: error control implicit none ! input: control - integer(i4b),intent(in) :: ixStability ! choice of stability function + integer(i4b),intent(in) :: ixStability ! choice of stability function ! input: forcing data, diagnostic and state variables - real(rkind),intent(in) :: mHeight ! measurement height (m) - real(rkind),intent(in) :: airtemp ! air temperature (K) - real(rkind),intent(in) :: sfcTemp ! surface temperature (K) - real(rkind),intent(in) :: windspd ! wind speed (m s-1) + real(rkind),intent(in) :: mHeight ! measurement height (m) + real(rkind),intent(in) :: airtemp ! air temperature (K) + real(rkind),intent(in) :: sfcTemp ! surface temperature (K) + real(rkind),intent(in) :: windspd ! wind speed (m s-1) ! input: stability parameters - real(rkind),intent(in) :: critRichNumber ! critical value for the bulk Richardson number where turbulence ceases (-) - real(rkind),intent(in) :: Louis79_bparam ! parameter in Louis (1979) stability function - real(rkind),intent(in) :: Mahrt87_eScale ! exponential scaling factor in the Mahrt (1987) stability function + real(rkind),intent(in) :: critRichNumber ! critical value for the bulk Richardson number where turbulence ceases (-) + real(rkind),intent(in) :: Louis79_bparam ! parameter in Louis (1979) stability function + real(rkind),intent(in) :: Mahrt87_eScale ! exponential scaling factor in the Mahrt (1987) stability function ! output - real(rkind),intent(out) :: RiBulk ! bulk Richardson number (-) - real(rkind),intent(out) :: stabilityCorrection ! stability correction for turbulent heat fluxes (-) + real(rkind),intent(out) :: RiBulk ! bulk Richardson number (-) + real(rkind),intent(out) :: stabilityCorrection ! stability correction for turbulent heat fluxes (-) real(rkind),intent(out) :: dStabilityCorrection_dRich ! derivative in stability correction w.r.t. Richardson number (-) real(rkind),intent(out) :: dStabilityCorrection_dAirTemp ! derivative in stability correction w.r.t. air temperature (K-1) real(rkind),intent(out) :: dStabilityCorrection_dSfcTemp ! derivative in stability correction w.r.t. surface temperature (K-1) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! local - real(rkind), parameter :: verySmall=1.e-10_rkind ! a very small number (avoid stability of zero) - real(rkind) :: dRiBulk_dAirTemp ! derivative in the bulk Richardson number w.r.t. air temperature (K-1) - real(rkind) :: dRiBulk_dSfcTemp ! derivative in the bulk Richardson number w.r.t. surface temperature (K-1) - real(rkind) :: bPrime ! scaled "b" parameter for stability calculations in Louis (1979) + real(rkind), parameter :: verySmall=1.e-10_rkind ! a very small number (avoid stability of zero) + real(rkind) :: dRiBulk_dAirTemp ! derivative in the bulk Richardson number w.r.t. air temperature (K-1) + real(rkind) :: dRiBulk_dSfcTemp ! derivative in the bulk Richardson number w.r.t. surface temperature (K-1) + real(rkind) :: bPrime ! scaled "b" parameter for stability calculations in Louis (1979) ! ----------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='aStability/' @@ -2492,7 +2482,7 @@ subroutine aStability(& stabilityCorrection = sqrt(1._rkind - 16._rkind*RiBulk) ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) ! dStabilityCorrection_dRich = (-16._rkind) * 0.5_rkind*(1._rkind - 16._rkind*RiBulk)**(-0.5_rkind) ! original - dStabilityCorrection_dRich = (-16._rkind) * 0.5_rkind/sqrt(1._rkind - 16._rkind*RiBulk) ! use sqrt intrinsic for speed + dStabilityCorrection_dRich = -8._rkind/sqrt(1._rkind - 16._rkind*RiBulk) ! simplify and use sqrt intrinsic for speed dStabilityCorrection_dAirTemp = dRiBulk_dAirTemp * dStabilityCorrection_dRich dStabilityCorrection_dSfcTemp = dRiBulk_dSfcTemp * dStabilityCorrection_dRich return @@ -2503,18 +2493,18 @@ subroutine aStability(& ! "standard" stability correction, a la Anderson 1976 case(standard) ! compute surface-atmosphere exchange coefficient (-) - if(RiBulk < critRichNumber) stabilityCorrection = (1._rkind - 5._rkind*RiBulk)**2_i4b - if(RiBulk >= critRichNumber) stabilityCorrection = verySmall + if (RiBulk < critRichNumber) stabilityCorrection = (1._rkind - 5._rkind*RiBulk)**2_i4b + if (RiBulk >= critRichNumber) stabilityCorrection = verySmall ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) - if(RiBulk < critRichNumber) dStabilityCorrection_dRich = -10._rkind*(1._rkind - 5._rkind*RiBulk) - if(RiBulk >= critRichNumber) dStabilityCorrection_dRich = verySmall + if (RiBulk < critRichNumber) dStabilityCorrection_dRich = -10._rkind*(1._rkind - 5._rkind*RiBulk) + if (RiBulk >= critRichNumber) dStabilityCorrection_dRich = verySmall ! Louis 1979 case(louisInversePower) ! scale the "b" parameter for stable conditions bprime = Louis79_bparam/2._rkind ! compute surface-atmosphere exchange coefficient (-) stabilityCorrection = 1._rkind / ( (1._rkind + bprime*RiBulk)**2_i4b ) - if(stabilityCorrection < epsilon(stabilityCorrection)) stabilityCorrection = epsilon(stabilityCorrection) + if (stabilityCorrection < epsilon(stabilityCorrection)) stabilityCorrection = epsilon(stabilityCorrection) ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) dStabilityCorrection_dRich = bprime * (-2._rkind)*(1._rkind + bprime*RiBulk)**(-3_i4b) @@ -2522,7 +2512,7 @@ subroutine aStability(& case(mahrtExponential) ! compute surface-atmosphere exchange coefficient (-) stabilityCorrection = exp(-Mahrt87_eScale * RiBulk) - if(stabilityCorrection < epsilon(stabilityCorrection)) stabilityCorrection = epsilon(stabilityCorrection) + if (stabilityCorrection < epsilon(stabilityCorrection)) stabilityCorrection = epsilon(stabilityCorrection) ! compute derivative in surface-atmosphere exchange coefficient w.r.t. temperature (K-1) dStabilityCorrection_dRich = (-Mahrt87_eScale) * exp(-Mahrt87_eScale * RiBulk) From 7a15b370e87449c517d3ea7ae6960596d5cc78bd Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 26 May 2023 05:25:08 -0600 Subject: [PATCH 0870/1472] Minor formatting updates to vegNrgFlux.f90 --- build/source/engine/vegNrgFlux.f90 | 3 --- 1 file changed, 3 deletions(-) diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 892ef2857..957ffab20 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -1559,7 +1559,6 @@ subroutine aeroResist(& ! ----------------------------------------------------------------------------------------------------------------------------------------- ! * compute vegetation poperties (could be done at the same time as phenology.. does not have to be in the flux routine!) if (computeVegFlux) then ! if vegetation is exposed - ! ***** identify zero plane displacement, roughness length, and surface temperature for the canopy (m) ! First, calculate new coordinate system above snow - use these to scale wind profiles and resistances ! NOTE: the new coordinate system makes zeroPlaneDisplacement and z0Canopy consistent @@ -1721,7 +1720,6 @@ subroutine aeroResist(& ! ----------------------------------------------------------------------------------------------------------------------------------------- ! * compute resistance for the case without a canopy (bare ground, or canopy completely buried with snow) else - ! no canopy, so set huge resistances (not used) canopyResistance = 1.e12_rkind ! not used: huge resistance, so conductance is essentially zero leafResistance = 1.e12_rkind ! not used: huge resistance, so conductance is essentially zero @@ -1782,7 +1780,6 @@ subroutine aeroResist(& frictionVelocity = missingValue ! friction velocity (m s-1) windspdCanopyTop = missingValue ! windspeed at the top of the canopy (m s-1) windspdCanopyBottom = missingValue ! windspeed at the height of the bottom of the canopy (m s-1) - end if ! end if no canopy ! derivatives for the vegetation canopy From 80b5f49602afffd3cdccb52554c078fed84795e7 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 19 Aug 2023 03:42:48 -0600 Subject: [PATCH 0871/1472] Minor update to build.pc.bash optional flags for oneMKL builds. --- build/cmake/build.pc.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/cmake/build.pc.bash b/build/cmake/build.pc.bash index d85abf080..88e8ff6b8 100755 --- a/build/cmake/build.pc.bash +++ b/build/cmake/build.pc.bash @@ -19,7 +19,7 @@ #export LINK_DIRS=/usr/local/lib # Link directories for cmake #export INCLUDES_DIRS="/usr/include;/usr/local/include" # directories for INCLUDES cmake variable (cmake uses semicolons as separators) #export LIBRARY_LINKS="-lnetcdff;-lnetcdf;-Wl,--start-group ${MKLROOT}/lib/intel64/libmkl_gf_lp64.a ${MKLROOT}/lib/intel64/libmkl_gnu_thread.a ${MKLROOT}/lib/intel64/libmkl_core.a -Wl,--end-group;-lgomp;-lpthread;-lm;-ldl" # list of library links -- Intel oneMKL builds -#export FLAGS_OPT="--m64;-I"${MKLROOT}/include;-flto=1;-fuse-linker-plugin" # optional compiler flags -- Intel oneMKL builds +#export FLAGS_OPT="-m64;-I"${MKLROOT}/include";-flto=1;-fuse-linker-plugin" # optional compiler flags -- Intel oneMKL builds # CMake Commands (build type controlled using DCMAKE_BUILD_TYPE) cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=BE From 41ac029fe52ee69d2c3ee35eda01e8d85fcce453 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 19 Aug 2023 04:12:59 -0600 Subject: [PATCH 0872/1472] Refactoring of vegNrgFlux.f90 that was not covered by v3 commits. --- build/source/engine/vegNrgFlux.f90 | 68 +++++++++++++++--------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 957ffab20..e432b9822 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -40,7 +40,7 @@ module vegNrgFlux_module USE var_lookup,only:iLookPARAM ! named variables for structure elements USE var_lookup,only:iLookINDEX ! named variables for structure elements USE var_lookup,only:iLookBVAR ! named variables for structure elements -USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure ! constants USE multiconst,only:gravity ! acceleration of gravity (m s-2) @@ -98,7 +98,7 @@ module vegNrgFlux_module public :: vegNrgFlux public :: wettedFrac ! dimensions -integer(i4b),parameter :: nBands=2 ! number of spectral bands for shortwave radiation +integer(i4b),parameter :: nBands = 2 ! number of spectral bands for shortwave radiation ! named variables integer(i4b),parameter :: ist = 1 ! Surface type: IST=1 => soil; IST=2 => lake integer(i4b),parameter :: isc = 4 ! Soil color type @@ -180,7 +180,7 @@ subroutine vegNrgFlux(& dCanopyNetFlux_dCanWat, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) dGroundNetFlux_dCanWat, & ! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) ! output: error control - err,message) ! intent(out): error control + err,message) ! intent(out): error control ! utilities USE expIntegral_module,only:expInt ! function to calculate the exponential integral @@ -605,7 +605,7 @@ subroutine vegNrgFlux(& scalarLatHeatSubVapCanopy = getLatentHeatValue(canopyTempTrial) ! case when there is snow on the ground (EXCLUDE "snow without a layer" -- in this case, evaporate from the soil) if (nSnow > 0) then - if(groundTempTrial > Tfreeze)then; err=20; message=trim(message)//'do not expect ground temperature > 0 when snow is on the ground'; return; end if + if (groundTempTrial > Tfreeze) then; err=20; message=trim(message)//'do not expect ground temperature > 0 when snow is on the ground'; return; end if scalarLatHeatSubVapGround = LH_sub ! sublimation from snow scalarGroundSnowFraction = 1._rkind ! case when the ground is snow-free @@ -648,7 +648,7 @@ subroutine vegNrgFlux(& if (computeVegFlux) then ! compute the fraction of liquid water in the canopy (-) totalCanopyWater = canopyLiqTrial + canopyIceTrial - if(totalCanopyWater > tiny(1.0_rkind))then + if (totalCanopyWater > tiny(1.0_rkind)) then fracLiquidCanopy = canopyLiqTrial / (canopyLiqTrial + canopyIceTrial) else fracLiquidCanopy = 0._rkind @@ -971,7 +971,7 @@ subroutine vegNrgFlux(& if (scalarLatHeatSubVapCanopy > LH_vap+verySmall) then ! canopy sublimation scalarCanopyEvaporation = 0._rkind scalarCanopySublimation = scalarLatHeatCanopyEvap/LH_sub - if(scalarLatHeatCanopyTrans > 0._rkind)then ! flux directed towards the veg + if (scalarLatHeatCanopyTrans > 0._rkind) then ! flux directed towards the veg scalarCanopySublimation = scalarCanopySublimation + scalarLatHeatCanopyTrans/LH_sub ! frost scalarCanopyTranspiration = 0._rkind else @@ -980,7 +980,7 @@ subroutine vegNrgFlux(& else ! canopy evaporation scalarCanopyEvaporation = scalarLatHeatCanopyEvap/LH_vap scalarCanopySublimation = 0._rkind - if(scalarLatHeatCanopyTrans > 0._rkind)then ! flux directed towards the veg + if (scalarLatHeatCanopyTrans > 0._rkind) then ! flux directed towards the veg scalarCanopyEvaporation = scalarCanopyEvaporation + scalarLatHeatCanopyTrans/LH_vap scalarCanopyTranspiration = 0._rkind else @@ -2015,36 +2015,36 @@ subroutine turbFluxes(& ! ----------------------------------------------------------------------------------------------------------------------------------------- implicit none ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) + logical(lgt),intent(in) :: computeVegFlux ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) ! input: above-canopy forcing data - real(rkind),intent(in) :: airtemp ! air temperature at some height above the surface (K) - real(rkind),intent(in) :: airpres ! air pressure of the air above the vegetation canopy (Pa) - real(rkind),intent(in) :: VPair ! vapor pressure of the air above the vegetation canopy (Pa) + real(rkind),intent(in) :: airtemp ! air temperature at some height above the surface (K) + real(rkind),intent(in) :: airpres ! air pressure of the air above the vegetation canopy (Pa) + real(rkind),intent(in) :: VPair ! vapor pressure of the air above the vegetation canopy (Pa) ! input: latent heat of sublimation/vaporization - real(rkind),intent(in) :: latHeatSubVapCanopy ! latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) - real(rkind),intent(in) :: latHeatSubVapGround ! latent heat of sublimation/vaporization for the ground surface (J kg-1) + real(rkind),intent(in) :: latHeatSubVapCanopy ! latent heat of sublimation/vaporization for the vegetation canopy (J kg-1) + real(rkind),intent(in) :: latHeatSubVapGround ! latent heat of sublimation/vaporization for the ground surface (J kg-1) ! input: canopy and ground temperature - real(rkind),intent(in) :: canairTemp ! temperature of the canopy air space (K) - real(rkind),intent(in) :: canopyTemp ! canopy temperature (K) - real(rkind),intent(in) :: groundTemp ! ground temperature (K) - real(rkind),intent(in) :: satVP_CanopyTemp ! saturation vapor pressure at the temperature of the veg canopy (Pa) - real(rkind),intent(in) :: satVP_GroundTemp ! saturation vapor pressure at the temperature of the ground (Pa) + real(rkind),intent(in) :: canairTemp ! temperature of the canopy air space (K) + real(rkind),intent(in) :: canopyTemp ! canopy temperature (K) + real(rkind),intent(in) :: groundTemp ! ground temperature (K) + real(rkind),intent(in) :: satVP_CanopyTemp ! saturation vapor pressure at the temperature of the veg canopy (Pa) + real(rkind),intent(in) :: satVP_GroundTemp ! saturation vapor pressure at the temperature of the ground (Pa) real(rkind),intent(in) :: dSVPCanopy_dCanopyTemp ! derivative in canopy saturation vapor pressure w.r.t. canopy temperature (Pa K-1) real(rkind),intent(in) :: dSVPGround_dGroundTemp ! derivative in ground saturation vapor pressure w.r.t. ground temperature (Pa K-1) ! input: diagnostic variables - real(rkind),intent(in) :: exposedVAI ! exposed vegetation area index -- leaf plus stem (m2 m-2) - real(rkind),intent(in) :: canopyWetFraction ! fraction of canopy that is wet [0-1] + real(rkind),intent(in) :: exposedVAI ! exposed vegetation area index -- leaf plus stem (m2 m-2) + real(rkind),intent(in) :: canopyWetFraction ! fraction of canopy that is wet [0-1] real(rkind),intent(in) :: dCanopyWetFraction_dWat ! derivative in the canopy wetted fraction w.r.t. liquid water content (kg-1 m-2) real(rkind),intent(in) :: dCanopyWetFraction_dT ! derivative in the canopy wetted fraction w.r.t. canopy temperature (K-1) - real(rkind),intent(in) :: canopySunlitLAI ! sunlit leaf area (-) - real(rkind),intent(in) :: canopyShadedLAI ! shaded leaf area (-) - real(rkind),intent(in) :: soilRelHumidity ! relative humidity in the soil pores [0-1] - real(rkind),intent(in) :: soilResistance ! resistance from the soil (s m-1) - real(rkind),intent(in) :: leafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) - real(rkind),intent(in) :: groundResistance ! below canopy aerodynamic resistance (s m-1) - real(rkind),intent(in) :: canopyResistance ! above canopy aerodynamic resistance (s m-1) - real(rkind),intent(in) :: stomResistSunlit ! stomatal resistance for sunlit leaves (s m-1) - real(rkind),intent(in) :: stomResistShaded ! stomatal resistance for shaded leaves (s m-1) + real(rkind),intent(in) :: canopySunlitLAI ! sunlit leaf area (-) + real(rkind),intent(in) :: canopyShadedLAI ! shaded leaf area (-) + real(rkind),intent(in) :: soilRelHumidity ! relative humidity in the soil pores [0-1] + real(rkind),intent(in) :: soilResistance ! resistance from the soil (s m-1) + real(rkind),intent(in) :: leafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) + real(rkind),intent(in) :: groundResistance ! below canopy aerodynamic resistance (s m-1) + real(rkind),intent(in) :: canopyResistance ! above canopy aerodynamic resistance (s m-1) + real(rkind),intent(in) :: stomResistSunlit ! stomatal resistance for sunlit leaves (s m-1) + real(rkind),intent(in) :: stomResistShaded ! stomatal resistance for shaded leaves (s m-1) ! input: derivatives in scalar resistances real(rkind),intent(in) :: dGroundResistance_dTGround ! derivative in ground resistance w.r.t. ground temperature (s m-1 K-1) real(rkind),intent(in) :: dGroundResistance_dTCanopy ! derivative in ground resistance w.r.t. canopy temperature (s m-1 K-1) @@ -2176,7 +2176,7 @@ subroutine turbFluxes(& totalConductanceSH = leafConductance + groundConductanceSH + canopyConductance ! compute conductances for latent heat (m s-1) - if(computeVegFlux)then + if (computeVegFlux) then evapConductance = canopyWetFraction*leafConductance transConductance = (1._rkind - canopyWetFraction) * leafConductanceTr else @@ -2367,10 +2367,10 @@ subroutine turbFluxes(& dVPCanopyAir_dCanWat = 0._rkind ! set derivatives for ground fluxes w.r.t canopy temperature to zero (no canopy, so fluxes are undefined) - dSenHeatGround_dTCanair = 0._rkind - dSenHeatGround_dTCanopy = 0._rkind - dLatHeatGroundEvap_dTCanair = 0._rkind - dLatHeatGroundEvap_dTCanopy = 0._rkind + dSenHeatGround_dTCanair = 0._rkind + dSenHeatGround_dTCanopy = 0._rkind + dLatHeatGroundEvap_dTCanair = 0._rkind + dLatHeatGroundEvap_dTCanopy = 0._rkind ! compute derivatives for the ground fluxes w.r.t. ground temperature dSenHeatGround_dTGround = (-volHeatCapacityAir*dGroundCondSH_dGroundTemp)*(groundTemp - airtemp) + & ! d(ground sensible heat flux)/d(ground temp) From c037afcd242a3b95f2337c9d803c295b8b0d11ed Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Mon, 21 Aug 2023 16:29:56 -0600 Subject: [PATCH 0873/1472] remove unneccary global variables that actors no longer use. Modify cmakelists.txt to include the changes made to summa-actors. Summa-Actors now uses the parameter and attributes files from SUMMA and not its own. --- build/cmake/CMakeLists.txt | 12 ++++-------- build/source/dshare/globalData.f90 | 2 -- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 39e5134a9..7bd2d2691 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -311,23 +311,22 @@ set(NETCDF ${SUB_NETCDF_DIR}/def_output.f90 ${SUB_NETCDF_DIR}/modelwrite.f90 ${NETCDF_DIR}/netcdf_util.f90 - ${SUB_NETCDF_DIR}/read_icond.f90) + ${NETCDF_DIR}/read_icond.f90) # Preliminary modules set(PRELIM ${ENGINE_DIR}/allocspace.f90 - ${SUB_ENGINE_DIR}/check_icond.f90 + ${ENGINE_DIR}/check_icond.f90 ${ENGINE_DIR}/checkStruc.f90 ${ENGINE_DIR}/childStruc.f90 ${ENGINE_DIR}/conv_funcs.f90 ${ENGINE_DIR}/convE2Temp.f90 ${SUB_ENGINE_DIR}/ffile_info.f90 ${ENGINE_DIR}/read_pinit.f90 - ${SUB_ENGINE_DIR}/read_attrb.f90 + ${ENGINE_DIR}/read_attrb.f90 ${ENGINE_DIR}/paramCheck.f90 ${ENGINE_DIR}/pOverwrite.f90 - ${ENGINE_DIR}/sunGeomtry.f90) -set(PRELIM_NOT_ACTORS + ${ENGINE_DIR}/sunGeomtry.f90 ${ENGINE_DIR}/read_param.f90) # Model run support modules @@ -413,10 +412,7 @@ set(INTERFACE set(FILE_ACCESS_INTERFACE ${FILE_ACCESS_DIR}/fortran_code/cppwrap_fileAccess.f90 ${FILE_ACCESS_DIR}/fortran_code/output_structure.f90 - ${FILE_ACCESS_DIR}/fortran_code/read_attrb.f90 ${FILE_ACCESS_DIR}/fortran_code/read_force.f90 - ${FILE_ACCESS_DIR}/fortran_code/read_icondFromStructure.f90 - ${FILE_ACCESS_DIR}/fortran_code/read_param.f90 ${FILE_ACCESS_DIR}/fortran_code/write_to_netcdf.f90 ${FILE_ACCESS_DIR}/fortran_code/writeOutputFromOutputStructure.f90) set(JOB_INTERFACE diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 index 194788641..798aac4be 100755 --- a/build/source/dshare/globalData.f90 +++ b/build/source/dshare/globalData.f90 @@ -271,8 +271,6 @@ MODULE globalData logical(lgt),allocatable,save,public :: failedHRUs(:) ! list of true and false values to indicate if an HRU has failed type(ilength),allocatable,save,public :: outputTimeStep(:) ! timestep in output files ! inital conditions for Actors - type(init_cond),allocatable,save,public :: init_cond_prog(:) ! variable data for initial conditions - type(init_cond),allocatable,save,public :: init_cond_bvar(:) ! variable data for initial conditions #else ! define metadata for model forcing datafile non-Actors type(file_info),save,public,allocatable :: forcFileInfo(:) ! file info for model forcing data From 27631278b36ce9fd8abd9770dc42ab42b8fb6ab4 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 22 Aug 2023 22:18:17 +0900 Subject: [PATCH 0874/1472] add plotting utilities --- utils/concat_groups_split_summa.py | 2 +- utils/hist_per_GRU.py | 2 +- utils/input_per_GRU.py | 236 +++++++++++++++++++++++++++++ utils/plot_per_GRU.py | 2 +- utils/scat_per_GRU.py | 10 +- utils/wallClock_per_GRU.py | 99 ++++++++++++ 6 files changed, 343 insertions(+), 8 deletions(-) create mode 100644 utils/input_per_GRU.py create mode 100644 utils/wallClock_per_GRU.py diff --git a/utils/concat_groups_split_summa.py b/utils/concat_groups_split_summa.py index abbc8d0a9..1a182ed9e 100644 --- a/utils/concat_groups_split_summa.py +++ b/utils/concat_groups_split_summa.py @@ -12,7 +12,7 @@ import numpy as np catby_num = 2 #number of files to cat into one, if had to divide runs from regular batches into sub-batches to finish in 7 days -top_fold = '/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/' +top_fold = '/home/avanb/scratch/' missing = False # if appending nan hrus to batch because failed missgru = 72055933 # batch 205 summa-be32 value diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index f50961402..c25ec7a42 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -19,7 +19,7 @@ import matplotlib.pyplot as plt import copy -viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') +viz_dir = Path('/home/avanb/scratch/statistics') testing = False if testing: diff --git a/utils/input_per_GRU.py b/utils/input_per_GRU.py new file mode 100644 index 000000000..b79ef556e --- /dev/null +++ b/utils/input_per_GRU.py @@ -0,0 +1,236 @@ +# written originally by W. Knoben, modified by A. Van Beusekom (2023) + +## Visualize batch number and wallClocktime per GRU, (could be modified to visualize other attributes or forcing per GRU) +## Needs: +# Catchment shapefile with GRU delineation +# ? SUMMA forcing folder and attribute folder +# SUMMA stats file for wall clock times + +## Special note +# To improve visualization of large lakes, HydroLAKES lake delineations are plotted on top of the catchment GRUs and river segments. +# Dealing with HydroLAKES inputs is not considered within scope of the workflow and therefore requires some manual downloading and preprocessing of this data for those who wish to reproduce this step. +# The relevant code is easily disabled by switching the plot_lakes = True flag to False. + +# Run: +# python input_per_GRU.py be32 + + +# modules +import sys +import os +import matplotlib +import numpy as np +import xarray as xr +from pathlib import Path +import matplotlib.pyplot as plt +import copy +import pyproj +import fiona +import geopandas as gpd + +# The first input argument specifies the run where the files are +method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) +stat = sys.argv[2] + +# Simulation statistics file locations +settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] +viz_dir = Path('/home/avanb/scratch/statistics') +viz_fil = method_name + '_hrly_diff_stats_{}.nc' +viz_fil = viz_fil.format(','.join(settings)) + +# Specify variables of interest +plot_vars = ['batchNum','batchNumMultWallClockTime','wallClockTime','batchNumMultWallClockMax','wallClockMax'] +plt_titl = ['(a) Number in batch','(b) Number in batch * Wall clock mean time','(c) Wall clock mean time','(d) Number in batch * Wall clock max time','(e) Wall clock max time'] +leg_titl = ['$#$','$#~s$','$s$','$#~s$','$s$'] +maxes = [518,5,1e-3,260,0.5] + +fig_fil = method_name + '_wallClockTime_batchNum_compressed.png' + +# Get the albers shapes +main = Path('/home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/albers_projection') + +# Plot lakes? +plot_lakes = True +# lakes shapefile WHERE IS THIS +#lake_path = Path('C:/Globus endpoint/HydroLAKES/HydroLAKES_polys_v10_shp') +#lake_name = 'HydroLAKES_polys_v10_subset_NA.shp' + +## Control file handling +# Store the name of the 'active' file in a variable +controlFile = 'plot_control_NorthAmerica.txt' + +# Function to extract a given setting from the control file +def read_from_control( file, setting ): + + # Open controlFile and ... + with open(file) as contents: + for line in contents: + + # ... find the line with the requested setting + if setting in line and not line.startswith('#'): + break + + # Extract the setting's value + substring = line.split('|',1)[1] # Remove the setting's name (split into 2 based on '|', keep only 2nd part) + substring = substring.split('#',1)[0] # Remove comments, does nothing if no '#' is found + substring = substring.strip() # Remove leading and trailing whitespace, tabs, newlines + + # Return this value + return substring + +# Function to specify a default path +def make_default_path(suffix): + + # Get the root path + rootPath = Path( read_from_control(controlFile,'root_path') ) + + # Get the domain folder + domainName = read_from_control(controlFile,'domain_name') + domainFolder = 'domain_' + domainName + + # Specify the forcing path + defaultPath = rootPath / domainFolder / suffix + + return defaultPath + + +## Catchment shapefile location and variable names +# HM catchment shapefile path & name +hm_catchment_path = read_from_control(controlFile,'catchment_shp_path') +hm_catchment_name = read_from_control(controlFile,'catchment_shp_name') +# Specify default path if needed +if hm_catchment_path == 'default': + hm_catchment_path = make_default_path('shapefiles/catchment') # outputs a Path() +else: + hm_catchment_path = Path(hm_catchment_path) # make sure a user-specified path is a Path() + +# Find the GRU and HRU identifiers +hm_hruid = read_from_control(controlFile,'catchment_shp_hruid') + + +## River network shapefile location and variable names +# Plot rivers? +plot_rivers = False +# River network path & name +river_network_path = read_from_control(controlFile,'river_network_shp_path') +river_network_name = read_from_control(controlFile,'river_network_shp_name') +# Specify default path if needed +if river_network_path == 'default': + river_network_path = make_default_path('shapefiles/river_network') # outputs a Path() +else: + river_network_path = Path(river_network_path) # make sure a user-specified path is a Path() + +# Find the segment ID +seg_id = read_from_control(controlFile,'river_network_shp_segid') + + +## Load all shapefiles and project to Albers Conformal Conic and reproject +# Set the target CRS +acc = 'ESRI:102008' + +# catchment shapefile, first 2 lines throw error so cutting them +#bas = gpd.read_file(hm_catchment_path/hm_catchment_name) +#bas_albers = bas.to_crs(acc) +bas_albers = gpd.read_file(main/'basin.shp') + +# river network shapefile, first 2 lines throw error so cutting them +if plot_rivers: + #riv = gpd.read_file(river_network_path/river_network_name) + #riv_albers = riv.to_crs(acc) + riv_albers = gpd.read_file(main/'river.shp') + +# lakes shapefile, first 2 lines throw error so cutting them +if plot_lakes: + #lakes = gpd.read_file(lake_path/lake_name) + #lak_albers = lakes.to_crs(acc) + lak_albers = gpd.read_file(main/'lakes.shp') + +## Pre-processing, map SUMMA sims to catchment shapes +# Get the aggregated statistics of SUMMA simulations +summa = xr.open_dataset(viz_dir/viz_fil) + +# Match the accummulated values to the correct HRU IDs in the shapefile +hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile +s0 = summa['wallClockTime'].sel(stat='mean') +s1 = summa['wallClockTime'].sel(stat='amax') +modulus = s0.indexes['hru'] % 518 +for plot_var in plot_vars: + if plot_var == 'batchNum': + s = modulus + if plot_var == 'batchNumMultWallClockTime': + s = modulus*s0 + if plot_var == 'wallClockTime': + s = s0 + if plot_var == 'batchNumMultWallClockMax': + s = modulus*s1 + if plot_var == 'wallClockMax': + s = s1 + bas_albers[plot_var] = s.sel(hru=hru_ids_shp.values) + +# Select lakes of a certain size for plotting +if plot_lakes: + minSize = 1000 # km2 + in_domain = (lak_albers['Country'] == 'Canada') | \ + (lak_albers['Country'] == 'United States of America') | \ + (lak_albers['Country'] == 'Mexico') + out_domain = (lak_albers['Pour_long'] > -80) & (lak_albers['Pour_lat'] > 65) # Exclude Baffin Island + large_lakes_albers = lak_albers.loc[(lak_albers['Lake_area'] > minSize) & in_domain & (~out_domain) ] +# Set the lake color +if plot_lakes: + lake_col = (8/255,81/255,156/255) + +##Figure + +# Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug +if 'compressed' in fig_fil: + plt.rcParams.update({'font.size': 25}) +else: + plt.rcParams.update({'font.size': 100}) + +# Flip the evaporation values so that they become positive, not if plotting diffs +#bas_albers['plot_ET'] = bas_albers['scalarTotalET'] * -1 +#bas_albers['plot_ET'] = bas_albers['plot_ET'].where(bas_albers['scalarTotalET'] != -9999, np.nan) + +if 'compressed' in fig_fil: + fig,axs = plt.subplots(3,2,figsize=(35,33)) +else: + fig,axs = plt.subplots(3,2,figsize=(140,133)) + +plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines + +# colorbar axes +f_x_mat = [0.443,0.94,0.443,0.94,0.443,0.94] +f_y_mat = [0.71,0.71,0.38,0.38,0.047,0.047] + +plt.tight_layout() + +def run_loop(i,var,the_max,f_x,f_y): + my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap + my_cmap.set_bad(color='white') #nan color white + vmin,vmax = 0, the_max + norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) + r = i//2 + c = i-r*2 + + # Data + bas_albers.plot(ax=axs[r,c], column=var, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) + + # Custom colorbar + cax = fig.add_axes([f_x,f_y,0.02,0.25]) + sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) + sm._A = [] + cbr = fig.colorbar(sm, cax=cax) #, extend='max') #if max extend can't get title right + cbr.ax.set_ylabel('[{}]'.format(leg_titl[i]), labelpad=40, rotation=270) + #cbr.ax.yaxis.set_offset_position('right') + + axs[r,c].set_title(plt_titl[i]) + axs[r,c].axis('off') + + # lakes + if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) + +for i,(var,the_max,f_x,f_y) in enumerate(zip(plot_vars,maxes,f_x_mat,f_y_mat)): + run_loop(i,var,the_max,f_x,f_y) + +# Save +plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=True) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 04ed9fc20..2176d86cd 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -35,7 +35,7 @@ # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] -viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') +viz_dir = Path('/home/avanb/scratch/statistics') viz_fil = method_name + '_hrly_diff_stats_{}.nc' viz_fil = viz_fil.format(','.join(settings)) diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index eb474cb9d..e3e7db8b6 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -19,7 +19,7 @@ import matplotlib.pyplot as plt import copy -viz_dir = Path('/home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/statistics') +viz_dir = Path('/home/avanb/scratch/statistics') testing = False if testing: @@ -72,7 +72,7 @@ fig,axs = plt.subplots(3,2,figsize=(140,133)) -def run_loop(i,var0): +def run_loop(i,var): r = i//2 c = i-r*2 if stat == 'rmse' or stat == 'kgem': stat0 = 'mean' @@ -88,10 +88,10 @@ def run_loop(i,var0): stat_word = 'Hourly RMSE ' stat0_word ='Hourly mean ' if stat == 'maxe': - stat_word = ' Hourly max abs error ' - stat0_word =' Hourly max ' + stat_word = 'Hourly max abs error ' + stat0_word ='Hourly max ' if stat == 'kgem': - stat_word = ' Hourly KGEm' + stat_word = 'Hourly KGEm ' stat0_word ='Hourly mean ' diff --git a/utils/wallClock_per_GRU.py b/utils/wallClock_per_GRU.py new file mode 100644 index 000000000..7bc990701 --- /dev/null +++ b/utils/wallClock_per_GRU.py @@ -0,0 +1,99 @@ +# written by A. Van Beusekom (2023) + +## Visualize batch number and wallClocktime per GRU, (could be modified to visualize other attributes or forcing per GRU) +## Needs: +# SUMMA output statistics + +## Special note +# SUMMA simulations have been preprocessed into single value statistics per model element, using auxiliary scripts in ~/utils +# Run: +# python wallClock_per_GRU.py + +# modules +import os +import matplotlib +import numpy as np +import xarray as xr +from pathlib import Path +import matplotlib.pyplot as plt +import copy + +viz_dir = Path('/home/avanb/scratch/statistics') + +testing = True +if testing: + viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') + method_name=['be64','be1','sundials_1en6'] #maybe make this an argument +else: + import sys + # The first input argument specifies the run where the files are + stat = sys.argv[1] + method_name=['be64','be32','be1','sundials_1en6'] #maybe make this an argument + +# Simulation statistics file locations +settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] +viz_fil = method_name.copy() +for i, m in enumerate(method_name): + viz_fil[i] = m + '_hrly_diff_stats_{}.nc' + viz_fil[i] = viz_fil[i].format(','.join(settings)) + +# Specify variables of interest +plt_titl = ['(a) Wall clock mean time','(b) Wall clock max time'] +leg_titl = ['$s$','$s$'] + +#fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' +#fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) +fig_fil = 'WallClockTime_batchNum_scat_compressed.png' + +# Get the aggregated statistics of SUMMA simulations +summa = {} +for i, m in enumerate(method_name): + summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) + +##Figure + +# Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug +if 'compressed' in fig_fil: + plt.rcParams.update({'font.size': 25}) +else: + plt.rcParams.update({'font.size': 100}) + +# Flip the evaporation values so that they become positive, not if plotting diffs +#bas_albers['plot_ET'] = bas_albers['scalarTotalET'] * -1 +#bas_albers['plot_ET'] = bas_albers['plot_ET'].where(bas_albers['scalarTotalET'] != -9999, np.nan) + +if 'compressed' in fig_fil: + fig,axs = plt.subplots(1,2,figsize=(35,33)) +else: + fig,axs = plt.subplots(1,2,figsize=(140,133)) + + +def run_loop(i,stat): + r = i//2 + c = i-r*2 + + # Data + for m in method_name: + s = summa[m]['wallClockTime'].sel(stat=stat) + modulus = s.indexes['hru'] % 518 + axs[c].scatter(x=s.values,y=modulus.values,s=1,zorder=0,label=m) + + if stat == 'mean': stat_word = 'Wall clock time hourly mean ' + if stat == 'amax': stat_word = 'Wall clock time hourly max ' + stat0_word ='Batch number' + + + lgnd = axs[c].legend() + for j, m in enumerate(method_name): + lgnd.legendHandles[j]._sizes = [80] + axs[c].set_title(plt_titl[i]) + axs[c].set_xlabel(stat_word + '[{}]'.format(leg_titl[i])) + axs[c].set_ylabel(stat0_word) + + +for i,stat in enumerate(['mean','amax']): + run_loop(i,stat) + +# Save +plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) + From c22a4eb5fbadbabfdcb7b4fda17b9b7d68121d57 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 22 Aug 2023 22:27:55 +0900 Subject: [PATCH 0875/1472] utils testing off --- utils/wallClock_per_GRU.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/wallClock_per_GRU.py b/utils/wallClock_per_GRU.py index 7bc990701..31712daf1 100644 --- a/utils/wallClock_per_GRU.py +++ b/utils/wallClock_per_GRU.py @@ -20,7 +20,7 @@ viz_dir = Path('/home/avanb/scratch/statistics') -testing = True +testing = False if testing: viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') method_name=['be64','be1','sundials_1en6'] #maybe make this an argument From 33f62e7849bdb072b936e5950101446ed2018b48 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 22 Aug 2023 23:48:55 +0900 Subject: [PATCH 0876/1472] fixing utils --- utils/hist_per_GRU.py | 4 ++-- utils/input_per_GRU.py | 3 +-- utils/scat_per_GRU.py | 4 ++-- utils/wallClock_per_GRU.py | 8 +++----- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index c25ec7a42..c3af4ad61 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -25,12 +25,12 @@ if testing: stat = 'kgem' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be64','be1','sundials_1en6'] #maybe make this an argument + method_name=['be1','be64','sundials_1en6'] #maybe make this an argument else: import sys # The first input argument specifies the run where the files are stat = sys.argv[1] - method_name=['be64','be32','be1','sundials_1en6'] #maybe make this an argument + method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] diff --git a/utils/input_per_GRU.py b/utils/input_per_GRU.py index b79ef556e..b9e5e9d57 100644 --- a/utils/input_per_GRU.py +++ b/utils/input_per_GRU.py @@ -30,7 +30,6 @@ # The first input argument specifies the run where the files are method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) -stat = sys.argv[2] # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] @@ -156,7 +155,7 @@ def make_default_path(suffix): modulus = s0.indexes['hru'] % 518 for plot_var in plot_vars: if plot_var == 'batchNum': - s = modulus + s = modulus*s0/s0 if plot_var == 'batchNumMultWallClockTime': s = modulus*s0 if plot_var == 'wallClockTime': diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index e3e7db8b6..d93150334 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -25,12 +25,12 @@ if testing: stat = 'kgem' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be64','be1','sundials_1en6'] #maybe make this an argument + method_name=['be1','be64','sundials_1en6'] #maybe make this an argument else: import sys # The first input argument specifies the run where the files are stat = sys.argv[1] - method_name=['be64','be32','be1','sundials_1en6'] #maybe make this an argument + method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] diff --git a/utils/wallClock_per_GRU.py b/utils/wallClock_per_GRU.py index 31712daf1..8fa2c782d 100644 --- a/utils/wallClock_per_GRU.py +++ b/utils/wallClock_per_GRU.py @@ -20,15 +20,13 @@ viz_dir = Path('/home/avanb/scratch/statistics') -testing = False +testing = True if testing: viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be64','be1','sundials_1en6'] #maybe make this an argument + method_name=['be1','be64','sundials_1en6'] #maybe make this an argument else: import sys - # The first input argument specifies the run where the files are - stat = sys.argv[1] - method_name=['be64','be32','be1','sundials_1en6'] #maybe make this an argument + method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] From 8c52328327f234676ddc7a4beab05daff60a0104 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 23 Aug 2023 11:50:34 +0900 Subject: [PATCH 0877/1472] fixing utils --- utils/input_per_GRU.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/input_per_GRU.py b/utils/input_per_GRU.py index b9e5e9d57..a470219c2 100644 --- a/utils/input_per_GRU.py +++ b/utils/input_per_GRU.py @@ -155,13 +155,13 @@ def make_default_path(suffix): modulus = s0.indexes['hru'] % 518 for plot_var in plot_vars: if plot_var == 'batchNum': - s = modulus*s0/s0 + s = s0*modulus/s0 if plot_var == 'batchNumMultWallClockTime': - s = modulus*s0 + s = s0*modulus if plot_var == 'wallClockTime': s = s0 if plot_var == 'batchNumMultWallClockMax': - s = modulus*s1 + s = s1*modulus if plot_var == 'wallClockMax': s = s1 bas_albers[plot_var] = s.sel(hru=hru_ids_shp.values) From c373cbffefa838837fcaff2ef35a1cb8eb5cabf7 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 23 Aug 2023 12:57:50 +0900 Subject: [PATCH 0878/1472] last utils? --- utils/input_per_GRU.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/input_per_GRU.py b/utils/input_per_GRU.py index a470219c2..cd3dca6e5 100644 --- a/utils/input_per_GRU.py +++ b/utils/input_per_GRU.py @@ -40,7 +40,7 @@ # Specify variables of interest plot_vars = ['batchNum','batchNumMultWallClockTime','wallClockTime','batchNumMultWallClockMax','wallClockMax'] plt_titl = ['(a) Number in batch','(b) Number in batch * Wall clock mean time','(c) Wall clock mean time','(d) Number in batch * Wall clock max time','(e) Wall clock max time'] -leg_titl = ['$#$','$#~s$','$s$','$#~s$','$s$'] +leg_titl = ['$num$','$num~s$','$s$','$num~s$','$s$'] maxes = [518,5,1e-3,260,0.5] fig_fil = method_name + '_wallClockTime_batchNum_compressed.png' From fc7bce7e68e531685ae215d2a9963668b731c8fc Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 23 Aug 2023 14:10:34 +0900 Subject: [PATCH 0879/1472] still fixing utils --- utils/input_per_GRU.py | 13 ++++++++----- utils/wallClock_per_GRU.py | 4 ++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/utils/input_per_GRU.py b/utils/input_per_GRU.py index cd3dca6e5..d6e95516b 100644 --- a/utils/input_per_GRU.py +++ b/utils/input_per_GRU.py @@ -38,10 +38,10 @@ viz_fil = viz_fil.format(','.join(settings)) # Specify variables of interest -plot_vars = ['batchNum','batchNumMultWallClockTime','wallClockTime','batchNumMultWallClockMax','wallClockMax'] -plt_titl = ['(a) Number in batch','(b) Number in batch * Wall clock mean time','(c) Wall clock mean time','(d) Number in batch * Wall clock max time','(e) Wall clock max time'] -leg_titl = ['$num$','$num~s$','$s$','$num~s$','$s$'] -maxes = [518,5,1e-3,260,0.5] +plot_vars = ['batch','batchNum','batchNumMultWallClockTime','wallClockTime','batchNumMultWallClockMax','wallClockMax'] +plt_titl = ['(a) Batch','(b) Basin in batch','(c) Basin in batch * Wall clock mean time','(d) Wall clock mean time','(e) Basin in batch * Wall clock max time','(f) Wall clock max time'] +leg_titl = ['$num$','$num$','$num~s$','$s$','$num~s$','$s$'] +maxes = [998,517,4.6,9e-3,100,0.2] fig_fil = method_name + '_wallClockTime_batchNum_compressed.png' @@ -152,8 +152,11 @@ def make_default_path(suffix): hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile s0 = summa['wallClockTime'].sel(stat='mean') s1 = summa['wallClockTime'].sel(stat='amax') -modulus = s0.indexes['hru'] % 518 +modulus = np.arange(len(s0.indexes['hru'])) % 518 +batch = np.floor(np.arange(len(s0.indexes['hru'])) /518) for plot_var in plot_vars: + if plot_var == 'batch': + s = s0*batch/s0 if plot_var == 'batchNum': s = s0*modulus/s0 if plot_var == 'batchNumMultWallClockTime': diff --git a/utils/wallClock_per_GRU.py b/utils/wallClock_per_GRU.py index 8fa2c782d..7a8bcc532 100644 --- a/utils/wallClock_per_GRU.py +++ b/utils/wallClock_per_GRU.py @@ -20,7 +20,7 @@ viz_dir = Path('/home/avanb/scratch/statistics') -testing = True +testing = False if testing: viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') method_name=['be1','be64','sundials_1en6'] #maybe make this an argument @@ -73,7 +73,7 @@ def run_loop(i,stat): # Data for m in method_name: s = summa[m]['wallClockTime'].sel(stat=stat) - modulus = s.indexes['hru'] % 518 + modulus = np.arange(len(s.indexes['hru'])) % 518 axs[c].scatter(x=s.values,y=modulus.values,s=1,zorder=0,label=m) if stat == 'mean': stat_word = 'Wall clock time hourly mean ' From 4ff66e418a264ff08fa989ef3e286a5b2bdde944 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 23 Aug 2023 05:16:08 -0600 Subject: [PATCH 0880/1472] General clean up of data_types.f90 - cosmetic changes only. --- build/source/dshare/data_types.f90 | 148 ++++++++++++++--------------- 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 4d65e180f..866ff2d04 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -42,14 +42,14 @@ MODULE data_types ! *********************************************************************************************************** ! define a derived type for the data in the file type,public :: file_info - character(len=256) :: filenmData='notPopulatedYet' ! name of data file - integer(i4b) :: nVars ! number of variables in the file - integer(i4b) :: nTimeSteps ! number of variables in the file - integer(i4b),allocatable :: var_ix(:) ! index of each forcing data variable in the data structure - integer(i4b),allocatable :: data_id(:) ! netcdf variable id for each forcing data variable - character(len=256),allocatable :: varName(:) ! netcdf variable name for each forcing data variable - real(rkind) :: firstJulDay ! first julian day in forcing file - real(rkind) :: convTime2Days ! factor to convert time to days + character(len=256) :: filenmData='notPopulatedYet' ! name of data file + integer(i4b) :: nVars ! number of variables in the file + integer(i4b) :: nTimeSteps ! number of variables in the file + integer(i4b),allocatable :: var_ix(:) ! index of each forcing data variable in the data structure + integer(i4b),allocatable :: data_id(:) ! netcdf variable id for each forcing data variable + character(len=256),allocatable :: varName(:) ! netcdf variable name for each forcing data variable + real(rkind) :: firstJulDay ! first julian day in forcing file + real(rkind) :: convTime2Days ! factor to convert time to days end type file_info ! *********************************************************************************************************** @@ -57,9 +57,9 @@ MODULE data_types ! *********************************************************************************************************** ! define a data type to store model parameter information type,public :: par_info - real(rkind) :: default_val ! default parameter value - real(rkind) :: lower_limit ! lower bound - real(rkind) :: upper_limit ! upper bound + real(rkind) :: default_val ! default parameter value + real(rkind) :: lower_limit ! lower bound + real(rkind) :: upper_limit ! upper bound endtype par_info ! *********************************************************************************************************** @@ -67,24 +67,24 @@ MODULE data_types ! *********************************************************************************************************** ! define derived type for model variables, including name, description, and units type,public :: var_info - character(len=64) :: varname = 'empty' ! variable name - character(len=128) :: vardesc = 'empty' ! variable description - character(len=64) :: varunit = 'empty' ! variable units - integer(i4b) :: vartype = integerMissing ! variable type - integer(i4b),dimension(maxvarFreq) :: ncVarID = integerMissing ! netcdf variable id (missing if frequency is not desired) - integer(i4b),dimension(maxvarFreq) :: statIndex = integerMissing ! index of desired statistic for temporal aggregation - logical(lgt) :: varDesire = .false. ! flag to denote if the variable is desired for model output + character(len=64) :: varname = 'empty' ! variable name + character(len=128) :: vardesc = 'empty' ! variable description + character(len=64) :: varunit = 'empty' ! variable units + integer(i4b) :: vartype = integerMissing ! variable type + integer(i4b),dimension(maxvarFreq) :: ncVarID = integerMissing ! netcdf variable id (missing if frequency is not desired) + integer(i4b),dimension(maxvarFreq) :: statIndex = integerMissing ! index of desired statistic for temporal aggregation + logical(lgt) :: varDesire = .false. ! flag to denote if the variable is desired for model output endtype var_info ! define extended data type (include indices to map onto parent data type) type,extends(var_info),public :: extended_info - integer(i4b) :: ixParent ! index in the parent data structure + integer(i4b) :: ixParent ! index in the parent data structure endtype extended_info ! define extended data type (includes named variables for the states affected by each flux) type,extends(var_info),public :: flux2state - integer(i4b) :: state1 ! named variable of the 1st state affected by the flux - integer(i4b) :: state2 ! named variable of the 2nd state affected by the flux + integer(i4b) :: state1 ! named variable of the 1st state affected by the flux + integer(i4b) :: state2 ! named variable of the 2nd state affected by the flux endtype flux2state ! *********************************************************************************************************** @@ -92,9 +92,9 @@ MODULE data_types ! *********************************************************************************************************** ! data structure information type,public :: struct_info - character(len=32) :: structName ! name of the data structure - character(len=32) :: lookName ! name of the look-up variables - integer(i4b) :: nVar ! number of variables in each data structure + character(len=32) :: structName ! name of the data structure + character(len=32) :: lookName ! name of the look-up variables + integer(i4b) :: nVar ! number of variables in each data structure end type struct_info ! *********************************************************************************************************** @@ -103,25 +103,25 @@ MODULE data_types ! hru info data structure type, public :: hru_info - integer(i4b) :: hru_nc ! index of the hru in the netcdf file - integer(i4b) :: hru_ix ! index of the hru in the run domain - integer(8) :: hru_id ! id (non-sequential number) of the hru - integer(i4b) :: nSnow ! number of snow layers - integer(i4b) :: nSoil ! number of soil layers + integer(i4b) :: hru_nc ! index of the hru in the netcdf file + integer(i4b) :: hru_ix ! index of the hru in the run domain + integer(8) :: hru_id ! id (non-sequential number) of the hru + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers endtype hru_info ! define mapping from GRUs to the HRUs type, public :: gru2hru_map - integer(8) :: gru_id ! id of the gru - integer(i4b) :: hruCount ! total number of hrus in the gru - type(hru_info), allocatable :: hruInfo(:) ! basic information of HRUs within the gru - integer(i4b) :: gru_nc ! index of gru in the netcdf file + integer(8) :: gru_id ! id of the gru + integer(i4b) :: hruCount ! total number of hrus in the gru + type(hru_info), allocatable :: hruInfo(:) ! basic information of HRUs within the gru + integer(i4b) :: gru_nc ! index of gru in the netcdf file endtype gru2hru_map ! define the mapping from the HRUs to the GRUs type, public :: hru2gru_map - integer(i4b) :: gru_ix ! index of gru which the hru belongs to - integer(i4b) :: localHRU_ix ! index of a hru within a gru (start from 1 per gru) + integer(i4b) :: gru_ix ! index of gru which the hru belongs to + integer(i4b) :: localHRU_ix ! index of a hru within a gru (start from 1 per gru) endtype hru2gru_map ! *********************************************************************************************************** @@ -130,41 +130,41 @@ MODULE data_types ! define derived types to hold look-up tables for each soil layer ! ** double precision type type, public :: dLookup - real(rkind),allocatable :: lookup(:) ! lookup(:) + real(rkind),allocatable :: lookup(:) ! lookup(:) endtype dLookup ! ** double precision type for a variable number of soil layers; variable length type, public :: vLookup - type(dLookup),allocatable :: var(:) ! var(:)%lookup(:) + type(dLookup),allocatable :: var(:) ! var(:)%lookup(:) endtype vLookup ! ** double precision type for a variable number of soil layers type, public :: zLookup - type(vLookup),allocatable :: z(:) ! z(:)%var(:)%lookup(:) + type(vLookup),allocatable :: z(:) ! z(:)%var(:)%lookup(:) endtype zLookup ! ** double precision type for a variable number of soil layers type, public :: hru_z_vLookup - type(zLookup),allocatable :: hru(:) ! hru(:)%z(:)%var(:)%lookup(:) + type(zLookup),allocatable :: hru(:) ! hru(:)%z(:)%var(:)%lookup(:) endtype hru_z_vLookup ! ** double precision type for a variable number of soil layers type, public :: gru_hru_z_vLookup - type(hru_z_vLookup),allocatable :: gru(:) ! gru(:)%hru(:)%z(:)%var(:)%lookup(:) + type(hru_z_vLookup),allocatable :: gru(:) ! gru(:)%hru(:)%z(:)%var(:)%lookup(:) endtype gru_hru_z_vLookup ! define derived types to hold multivariate data for a single variable (different variables have different length) ! NOTE: use derived types here to facilitate adding the "variable" dimension ! ** double precision type type, public :: dlength - real(rkind),allocatable :: dat(:) ! dat(:) + real(rkind),allocatable :: dat(:) ! dat(:) endtype dlength ! ** integer type (4 byte) type, public :: ilength - integer(i4b),allocatable :: dat(:) ! dat(:) + integer(i4b),allocatable :: dat(:) ! dat(:) endtype ilength ! ** integer type (8 byte) type, public :: i8length - integer(8),allocatable :: dat(:) ! dat(:) + integer(8),allocatable :: dat(:) ! dat(:) endtype i8length ! ** logical type type, public :: flagVec - logical(lgt),allocatable :: dat(:) ! dat(:) + logical(lgt),allocatable :: dat(:) ! dat(:) endtype flagVec ! define derived types to hold data for multiple variables @@ -172,131 +172,131 @@ MODULE data_types ! ** double precision type of variable length type, public :: var_dlength - type(dlength),allocatable :: var(:) ! var(:)%dat + type(dlength),allocatable :: var(:) ! var(:)%dat endtype var_dlength ! ** integer type of variable length (4 byte) type, public :: var_ilength - type(ilength),allocatable :: var(:) ! var(:)%dat + type(ilength),allocatable :: var(:) ! var(:)%dat endtype var_ilength ! ** integer type of variable length (8 byte) type, public :: var_i8length - type(i8length),allocatable :: var(:) ! var(:)%dat + type(i8length),allocatable :: var(:) ! var(:)%dat endtype var_i8length ! ** logical type of variable length type, public :: var_flagVec - type(flagVec),allocatable :: var(:) ! var(:)%dat + type(flagVec),allocatable :: var(:) ! var(:)%dat endtype var_flagVec ! ** double precision type of fixed length type, public :: var_d - real(rkind),allocatable :: var(:) ! var(:) + real(rkind),allocatable :: var(:) ! var(:) endtype var_d ! ** integer type of fixed length (4 byte) type, public :: var_i - integer(i4b),allocatable :: var(:) ! var(:) + integer(i4b),allocatable :: var(:) ! var(:) endtype var_i ! ** integer type of fixed length (8 byte) type, public :: var_i8 - integer(8),allocatable :: var(:) ! var(:) + integer(8),allocatable :: var(:) ! var(:) endtype var_i8 ! ** double precision type of fixed length type, public :: hru_d - real(rkind),allocatable :: hru(:) ! hru(:) + real(rkind),allocatable :: hru(:) ! hru(:) endtype hru_d ! ** integer type of fixed length (4 byte) type, public :: hru_i - integer(i4b),allocatable :: hru(:) ! hru(:) + integer(i4b),allocatable :: hru(:) ! hru(:) endtype hru_i ! ** integer type of fixed length (8 byte) type, public :: hru_i8 - integer(8),allocatable :: hru(:) ! hru(:) + integer(8),allocatable :: hru(:) ! hru(:) endtype hru_i8 ! define derived types to hold JUST the HRU dimension ! ** double precision type of variable length type, public :: hru_doubleVec - type(var_dlength),allocatable :: hru(:) ! hru(:)%var(:)%dat + type(var_dlength),allocatable :: hru(:) ! hru(:)%var(:)%dat endtype hru_doubleVec ! ** integer type of variable length (4 byte) type, public :: hru_intVec - type(var_ilength),allocatable :: hru(:) ! hru(:)%var(:)%dat + type(var_ilength),allocatable :: hru(:) ! hru(:)%var(:)%dat endtype hru_intVec ! ** integer type of variable length (8 byte) type, public :: hru_int8Vec - type(var_i8length),allocatable :: hru(:) ! hru(:)%var(:)%dat + type(var_i8length),allocatable :: hru(:) ! hru(:)%var(:)%dat endtype hru_int8Vec ! ** double precision type of fixed length type, public :: hru_double - type(var_d),allocatable :: hru(:) ! hru(:)%var(:) + type(var_d),allocatable :: hru(:) ! hru(:)%var(:) endtype hru_double ! ** integer type of fixed length (4 byte) type, public :: hru_int - type(var_i),allocatable :: hru(:) ! hru(:)%var(:) + type(var_i),allocatable :: hru(:) ! hru(:)%var(:) endtype hru_int ! ** integer type of fixed length (8 byte) type, public :: hru_int8 - type(var_i8),allocatable :: hru(:) ! hru(:)%var(:) + type(var_i8),allocatable :: hru(:) ! hru(:)%var(:) endtype hru_int8 ! define derived types to hold JUST the HRU dimension ! ** double precision type of variable length type, public :: gru_doubleVec - type(var_dlength),allocatable :: gru(:) ! gru(:)%var(:)%dat + type(var_dlength),allocatable :: gru(:) ! gru(:)%var(:)%dat endtype gru_doubleVec ! ** integer type of variable length (4 byte) type, public :: gru_intVec - type(var_ilength),allocatable :: gru(:) ! gru(:)%var(:)%dat + type(var_ilength),allocatable :: gru(:) ! gru(:)%var(:)%dat endtype gru_intVec ! ** integer type of variable length (8 byte) type, public :: gru_int8Vec - type(var_i8length),allocatable :: gru(:) ! gru(:)%var(:)%dat + type(var_i8length),allocatable :: gru(:) ! gru(:)%var(:)%dat endtype gru_int8Vec ! ** double precision type of fixed length type, public :: gru_double - type(var_d),allocatable :: gru(:) ! gru(:)%var(:) + type(var_d),allocatable :: gru(:) ! gru(:)%var(:) endtype gru_double ! ** integer type of variable length (4 byte) type, public :: gru_int - type(var_i),allocatable :: gru(:) ! gru(:)%var(:) + type(var_i),allocatable :: gru(:) ! gru(:)%var(:) endtype gru_int ! ** integer type of variable length (8 byte) type, public :: gru_int8 - type(var_i8),allocatable :: gru(:) ! gru(:)%var(:) + type(var_i8),allocatable :: gru(:) ! gru(:)%var(:) endtype gru_int8 ! define derived types to hold BOTH the GRU and HRU dimension ! ** double precision type of variable length type, public :: gru_hru_doubleVec - type(hru_doubleVec),allocatable :: gru(:) ! gru(:)%hru(:)%var(:)%dat + type(hru_doubleVec),allocatable :: gru(:) ! gru(:)%hru(:)%var(:)%dat endtype gru_hru_doubleVec ! ** integer type of variable length (4 byte) type, public :: gru_hru_intVec - type(hru_intVec),allocatable :: gru(:) ! gru(:)%hru(:)%var(:)%dat + type(hru_intVec),allocatable :: gru(:) ! gru(:)%hru(:)%var(:)%dat endtype gru_hru_intVec ! ** integer type of variable length (8 byte) type, public :: gru_hru_int8Vec - type(hru_int8Vec),allocatable :: gru(:) ! gru(:)%hru(:)%var(:)%dat + type(hru_int8Vec),allocatable :: gru(:) ! gru(:)%hru(:)%var(:)%dat endtype gru_hru_int8Vec ! ** double precision type of fixed length type, public :: gru_hru_double - type(hru_double),allocatable :: gru(:) ! gru(:)%hru(:)%var(:) + type(hru_double),allocatable :: gru(:) ! gru(:)%hru(:)%var(:) endtype gru_hru_double ! ** integer type of variable length (4 byte) type, public :: gru_hru_int - type(hru_int),allocatable :: gru(:) ! gru(:)%hru(:)%var(:) + type(hru_int),allocatable :: gru(:) ! gru(:)%hru(:)%var(:) endtype gru_hru_int ! ** integer type of variable length (8 byte) type, public :: gru_hru_int8 - type(hru_int8),allocatable :: gru(:) ! gru(:)%hru(:)%var(:) + type(hru_int8),allocatable :: gru(:) ! gru(:)%hru(:)%var(:) endtype gru_hru_int8 ! ** double precision type of fixed length type, public :: gru_d - type(hru_d),allocatable :: gru(:) ! gru(:)%hru(:) + type(hru_d),allocatable :: gru(:) ! gru(:)%hru(:) endtype gru_d ! ** integer type of fixed length type, public :: gru_i - type(hru_i),allocatable :: gru(:) ! gru(:)%hru(:) + type(hru_i),allocatable :: gru(:) ! gru(:)%hru(:) endtype gru_i END MODULE data_types From 0fec73f73116f54465219144d3dad6532097c1bf Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 23 Aug 2023 05:59:13 -0600 Subject: [PATCH 0881/1472] Added data_bin and data_array derived types to data_types.f90. --- build/source/dshare/data_types.f90 | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 866ff2d04..106751351 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -299,5 +299,21 @@ MODULE data_types type(hru_i),allocatable :: gru(:) ! gru(:)%hru(:) endtype gru_i + ! define derived types used to simplify passing subroutine arguments + ! ** arrays for holding arguments of different intrinsic types + type, public :: data_array + logical(lgt), allocatable :: lgt(:) ! vector of logical arguments + real(rkind), allocatable :: rkind(:) ! vector of rkind arguments + real(rkind), allocatable :: rmatrix(:,:) ! matrix of rkind arguments + integer(i4b), allocatable :: i4b(:) ! vector of i4b integer arguments + character(:), allocatable :: string ! character string arguments + end type data_array + ! ** derived type used to hold data passed as subroutine arguments + type, public :: data_bin ! x%bin(:)%{lgt(:),i4b(:),rkind(:),rmatrix(:,:),string}, x%err [i4b], x%msg [character] + type(data_array), allocatable :: bin(:) ! allocatable number of data bins + integer(i4b) :: err ! error code + character(:), allocatable :: msg ! error message + end type data_bin + END MODULE data_types From 17b5504f86f5354088af345acc18a32e51c09d19 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 24 Aug 2023 03:56:29 -0600 Subject: [PATCH 0882/1472] Add lookup variable for flux subroutines. --- build/source/dshare/var_lookup.f90 | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 2c5630510..07641f7db 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -834,6 +834,19 @@ MODULE var_lookup integer(i4b) :: deriv2 = integerMissing ! second derivatives of the interpolating function endtype iLook_vLookup + ! *********************************************************************************************************** + ! (17) structure for looking up flux subroutines + ! *********************************************************************************************************** + type, public :: iLook_routine + integer(i4b) :: vegNrgFlux = integerMissing ! vegNrgFlux + integer(i4b) :: ssdNrgFlux = integerMissing ! ssdNrgFlux + integer(i4b) :: vegLiqFlux = integerMissing ! vegLiqFlux + integer(i4b) :: snowLiqFlx = integerMissing ! snowLiqFlx + integer(i4b) :: soilLiqFlx = integerMissing ! soilLiqFlx + integer(i4b) :: groundwatr = integerMissing ! groundwatr + integer(i4b) :: bigAquifer = integerMissing ! bigAquifer + end type iLook_routine + ! *********************************************************************************************************** ! (X) define data structures and maximum number of variables of each type ! *********************************************************************************************************** @@ -926,6 +939,8 @@ MODULE var_lookup type(iLook_freq), public,parameter :: iLookFreq =ilook_freq ( 1, 2, 3, 4) ! named variables in the lookup table structure type(iLook_vLookup), public,parameter :: iLookLOOKUP =ilook_vLookup ( 1, 2, 3) + ! named variables: flux subroutines + type(iLook_routine) ,public,parameter :: iLookROUTINE =iLook_routine ( 1, 2, 3, 4, 5, 6, 7) ! define maximum number of variables of each type integer(i4b),parameter,public :: maxvarDecisions = storage_size(iLookDECISIONS)/iLength integer(i4b),parameter,public :: maxvarTime = storage_size(iLookTIME)/iLength From e0adc39b3064dfd830fd5cdd19bc73612fa25449 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 24 Aug 2023 19:01:29 +0900 Subject: [PATCH 0883/1472] change files to not have executable permissions, and remove lapack directory --- build/source/dshare/globalData.f90 | 0 build/source/engine/derivforce.f90 | 0 build/source/engine/mDecisions.f90 | 0 build/source/engine/vegPhenlgy.f90 | 0 build/source/hookup/ascii_util.f90 | 0 build/source/hookup/summaFileManager.f90 | 0 build/source/lapack/Makefile | 74 ------------------- build/source/lapack/README | 1 - build/source/netcdf/def_output.f90 | 0 build/source/netcdf/modelwrite.f90 | 0 build/source/netcdf/netcdf_util.f90 | 0 build/source/noah-mp/module_model_constants.F | 0 build/source/noah-mp/module_sf_noahlsm.F | 0 build/source/noah-mp/module_sf_noahmplsm.F | 0 build/source/noah-mp/module_sf_noahutl.F | 0 15 files changed, 75 deletions(-) mode change 100755 => 100644 build/source/dshare/globalData.f90 mode change 100755 => 100644 build/source/engine/derivforce.f90 mode change 100755 => 100644 build/source/engine/mDecisions.f90 mode change 100755 => 100644 build/source/engine/vegPhenlgy.f90 mode change 100755 => 100644 build/source/hookup/ascii_util.f90 mode change 100755 => 100644 build/source/hookup/summaFileManager.f90 delete mode 100755 build/source/lapack/Makefile delete mode 100755 build/source/lapack/README mode change 100755 => 100644 build/source/netcdf/def_output.f90 mode change 100755 => 100644 build/source/netcdf/modelwrite.f90 mode change 100755 => 100644 build/source/netcdf/netcdf_util.f90 mode change 100755 => 100644 build/source/noah-mp/module_model_constants.F mode change 100755 => 100644 build/source/noah-mp/module_sf_noahlsm.F mode change 100755 => 100644 build/source/noah-mp/module_sf_noahmplsm.F mode change 100755 => 100644 build/source/noah-mp/module_sf_noahutl.F diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/derivforce.f90 b/build/source/engine/derivforce.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 old mode 100755 new mode 100644 diff --git a/build/source/engine/vegPhenlgy.f90 b/build/source/engine/vegPhenlgy.f90 old mode 100755 new mode 100644 diff --git a/build/source/hookup/ascii_util.f90 b/build/source/hookup/ascii_util.f90 old mode 100755 new mode 100644 diff --git a/build/source/hookup/summaFileManager.f90 b/build/source/hookup/summaFileManager.f90 old mode 100755 new mode 100644 diff --git a/build/source/lapack/Makefile b/build/source/lapack/Makefile deleted file mode 100755 index 2d59c7114..000000000 --- a/build/source/lapack/Makefile +++ /dev/null @@ -1,74 +0,0 @@ -#======================================================================== -# PART 0: Define directory paths -#======================================================================== - -# Define core directory below which everything resides. This is the -# parent directory of the 'build' directory -F_MASTER = /home/mclark/summa - -# Core directory that contains source code -F_KORE_DIR = $(F_MASTER)/build/source - -# Location of the compiled modules -MOD_PATH = $(F_MASTER)/build/source/lapack - -# Define the directory for the executables -EXE_PATH = $(F_MASTER)/build/source/lapack - -#======================================================================== -# PART 1: Assemble all of the sub-routines -#======================================================================== - -# Define directories -NUMREC_DIR = $(F_KORE_DIR)/numrec -LAPACK_DIR = $(F_KORE_DIR)/lapack - -# -# Numerical Recipes utilities -SUMMA_NRUTIL= \ - nrtype.f90 -NRUTIL = $(patsubst %, $(NUMREC_DIR)/%, $(SUMMA_NRUTIL)) - -# numerical recipes routines -SUMMA_NUMREC= \ - luSolv_numrec.f90 -NUMREC = $(patsubst %, $(LAPACK_DIR)/%, $(SUMMA_NUMREC)) - -# ... stitch together all programs -LUTEST = $(NRUTIL) $(NUMREC) - -#======================================================================== -# PART 2: Define the libraries, driver programs, and executables -#======================================================================== - -# Define the Fortran Compiler -FC = ifort - -# Define the lapack library -LAPK_PATH = /usr -LIBLAPACK = -L$(LAPK_PATH)/lib -llapack - -DRIVER = test_lusolve.f90 - -# Define the executable -DRIVER__EX = test_lusolve.exe - - -#======================================================================== -# PART 3: Compile the puppy -#======================================================================== - -# Define flags -FLAGS = -debug -warn all -check all -FR -O0 -auto -WB -traceback -g -fltconsistency -fpe0 - -all: compile link clean - -compile: - $(FC) $(FLAGS) -c $(LUTEST) $(DRIVER) - -link: - $(FC) *.o $(LIBLAPACK) -o $(DRIVER__EX) - -clean: - rm -f *.o - rm -f *.mod diff --git a/build/source/lapack/README b/build/source/lapack/README deleted file mode 100755 index c7fe5ddb9..000000000 --- a/build/source/lapack/README +++ /dev/null @@ -1 +0,0 @@ -Used to test the lapack routines diff --git a/build/source/netcdf/def_output.f90 b/build/source/netcdf/def_output.f90 old mode 100755 new mode 100644 diff --git a/build/source/netcdf/modelwrite.f90 b/build/source/netcdf/modelwrite.f90 old mode 100755 new mode 100644 diff --git a/build/source/netcdf/netcdf_util.f90 b/build/source/netcdf/netcdf_util.f90 old mode 100755 new mode 100644 diff --git a/build/source/noah-mp/module_model_constants.F b/build/source/noah-mp/module_model_constants.F old mode 100755 new mode 100644 diff --git a/build/source/noah-mp/module_sf_noahlsm.F b/build/source/noah-mp/module_sf_noahlsm.F old mode 100755 new mode 100644 diff --git a/build/source/noah-mp/module_sf_noahmplsm.F b/build/source/noah-mp/module_sf_noahmplsm.F old mode 100755 new mode 100644 diff --git a/build/source/noah-mp/module_sf_noahutl.F b/build/source/noah-mp/module_sf_noahutl.F old mode 100755 new mode 100644 From 41bf0aa8272af4beac457cdf2ca49a0d8c6694f2 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 24 Aug 2023 19:43:54 +0900 Subject: [PATCH 0884/1472] updating cmake files --- build/cmake/README_ngen.md | 2 +- build/cmake/README_not_ngen.md | 2 +- build/cmake/build.cluster.bash | 1 - build/cmake/build.pc.bash | 1 - build/cmake/build_actors.cluster.bash | 1 - build/cmake/build_actors.mac.bash | 1 - build/cmake/build_ngen.cluster.bash | 1 - build/cmake/build_ngen.mac.bash | 1 - build/cmake_external/build_cmakeActors.bash | 9 + build/cmake_external/build_cmakeBMI.bash | 8 + .../build_cmakeSundials.bash} | 9 +- build/makefiles/Actors/build_cmakeActors.sh | 7 - build/makefiles/Actors/compile_copernicus.sh | 30 -- build/makefiles/Actors/compile_graham.sh | 30 -- build/makefiles/Actors/makefile_apptainer | 418 ----------------- build/makefiles/Actors/makefile_cluster | 419 ----------------- build/makefiles/BE/makefile_old_summa | 421 ------------------ build/makefiles/Ngen/build_cmakeBMI | 5 - build/makefiles/Ngen/compile_copernicus.sh | 17 - build/makefiles/Ngen/compile_mac.sh | 11 - build/makefiles/Ngen/makefile_cluster | 351 --------------- build/makefiles/Ngen/makefile_mac | 351 --------------- .../makefiles/Sundials/compile_copernicus.sh | 17 - build/makefiles/Sundials/compile_graham.sh | 17 - build/makefiles/Sundials/compile_mac.sh | 11 - .../makefiles/Sundials/compile_richardson.sh | 11 - build/makefiles/Sundials/makefile_cluster | 310 ------------- build/makefiles/Sundials/makefile_mac | 309 ------------- 28 files changed, 24 insertions(+), 2747 deletions(-) create mode 100755 build/cmake_external/build_cmakeActors.bash create mode 100755 build/cmake_external/build_cmakeBMI.bash rename build/{makefiles/Sundials/build_cmakeSundials.sh => cmake_external/build_cmakeSundials.bash} (72%) delete mode 100755 build/makefiles/Actors/build_cmakeActors.sh delete mode 100644 build/makefiles/Actors/compile_copernicus.sh delete mode 100644 build/makefiles/Actors/compile_graham.sh delete mode 100644 build/makefiles/Actors/makefile_apptainer delete mode 100644 build/makefiles/Actors/makefile_cluster delete mode 100644 build/makefiles/BE/makefile_old_summa delete mode 100755 build/makefiles/Ngen/build_cmakeBMI delete mode 100755 build/makefiles/Ngen/compile_copernicus.sh delete mode 100755 build/makefiles/Ngen/compile_mac.sh delete mode 100644 build/makefiles/Ngen/makefile_cluster delete mode 100644 build/makefiles/Ngen/makefile_mac delete mode 100755 build/makefiles/Sundials/compile_copernicus.sh delete mode 100755 build/makefiles/Sundials/compile_graham.sh delete mode 100755 build/makefiles/Sundials/compile_mac.sh delete mode 100755 build/makefiles/Sundials/compile_richardson.sh delete mode 100644 build/makefiles/Sundials/makefile_cluster delete mode 100644 build/makefiles/Sundials/makefile_mac diff --git a/build/cmake/README_ngen.md b/build/cmake/README_ngen.md index e0cb04ef3..85daf9f8e 100644 --- a/build/cmake/README_ngen.md +++ b/build/cmake/README_ngen.md @@ -60,7 +60,7 @@ Extract the corresponding compressed file tar -xzf sundials-6.3.0.tar.gz -Enter the buildir and run +An example build_cmake file is at summa/build/cmake_external/build_cmakeSundials.bash which you can copy to builddir as build_cmake. Then, enter the buildir and run cd sundials/buildir ./build_cmake diff --git a/build/cmake/README_not_ngen.md b/build/cmake/README_not_ngen.md index 3ad0e0f78..8ad5ed77d 100644 --- a/build/cmake/README_not_ngen.md +++ b/build/cmake/README_not_ngen.md @@ -40,7 +40,7 @@ Extract the corresponding compressed file tar -xzf sundials-6.3.0.tar.gz -Enter the buildir and run +An example build_cmake file is at summa/build/cmake_external/build_cmakeSundials.bash which you can copy to builddir as build_cmake. Then, enter the buildir and run cd sundials/buildir ./build_cmake diff --git a/build/cmake/build.cluster.bash b/build/cmake/build.cluster.bash index c80ac1d79..dc985bd38 100755 --- a/build/cmake/build.cluster.bash +++ b/build/cmake/build.cluster.bash @@ -11,4 +11,3 @@ export FLAGS_OPT="-flto=1;-fuse-linker-plugin" cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials_Cluster cmake --build ../cmake_build --target all - diff --git a/build/cmake/build.pc.bash b/build/cmake/build.pc.bash index 88e8ff6b8..ff2c882ae 100755 --- a/build/cmake/build.pc.bash +++ b/build/cmake/build.pc.bash @@ -24,4 +24,3 @@ # CMake Commands (build type controlled using DCMAKE_BUILD_TYPE) cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=BE cmake --build ../cmake_build --target all - diff --git a/build/cmake/build_actors.cluster.bash b/build/cmake/build_actors.cluster.bash index b8b7ce4d7..72cb6a90a 100755 --- a/build/cmake/build_actors.cluster.bash +++ b/build/cmake/build_actors.cluster.bash @@ -14,4 +14,3 @@ export FLAGS_OPT="-flto=1;-fuse-linker-plugin" cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials_Actors_Cluster cmake --build ../cmake_build --target all - diff --git a/build/cmake/build_actors.mac.bash b/build/cmake/build_actors.mac.bash index 14c8353b7..fd2624493 100755 --- a/build/cmake/build_actors.mac.bash +++ b/build/cmake/build_actors.mac.bash @@ -11,4 +11,3 @@ export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials_Actors cmake --build ../cmake_build --target all - diff --git a/build/cmake/build_ngen.cluster.bash b/build/cmake/build_ngen.cluster.bash index b081f6861..1a9acb531 100755 --- a/build/cmake/build_ngen.cluster.bash +++ b/build/cmake/build_ngen.cluster.bash @@ -23,4 +23,3 @@ cmake -B cmake_build -S . -DMPI_ACTIVE:BOOL=OFF -DNGEN_ACTIVATE_PYTHON:BOOL=OFF # can also add -DCMAKE_BUILD_TYPE=Debug to be able to run in gdb # make -j 8 -C cmake_build # build w/ 8 parallel jobs, also turn MPI on make -C cmake_build - diff --git a/build/cmake/build_ngen.mac.bash b/build/cmake/build_ngen.mac.bash index 457c2777e..71cb8a6c9 100755 --- a/build/cmake/build_ngen.mac.bash +++ b/build/cmake/build_ngen.mac.bash @@ -18,4 +18,3 @@ cmake -B cmake_build -S . -DMPI_ACTIVE:BOOL=OFF -DNGEN_ACTIVATE_PYTHON:BOOL=OFF # can also add -DCMAKE_BUILD_TYPE=Debug to be able to run in gdb # make -j 8 -C cmake_build # build w/ 8 parallel jobs, also turn MPI on make -C cmake_build - diff --git a/build/cmake_external/build_cmakeActors.bash b/build/cmake_external/build_cmakeActors.bash new file mode 100755 index 000000000..f34ac441d --- /dev/null +++ b/build/cmake_external/build_cmakeActors.bash @@ -0,0 +1,9 @@ +#!/bin/bash + +# from {$maindir}/actor-framework, run +# cp ../../summa/build/summa/build/cmake_external/build_cmakeActors.bash build_cmake +# run script from the actor-framework directory with ./build_cmake +# run `make`, then `make install` + +export CXX="g++" +./configure --prefix=../install diff --git a/build/cmake_external/build_cmakeBMI.bash b/build/cmake_external/build_cmakeBMI.bash new file mode 100755 index 000000000..d03062fa3 --- /dev/null +++ b/build/cmake_external/build_cmakeBMI.bash @@ -0,0 +1,8 @@ +#!/bin/bash + +# from {$maindir}/bmi/builddir, run +# cp ../../summa/build/cmake_external/build_cmakeBMI.bash build_cmake +# run script from the builddir directory with ./build_cmake +# run `make`, then `make install` + +cmake ../../bmi-fortran/ -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=../../bmi/instdir diff --git a/build/makefiles/Sundials/build_cmakeSundials.sh b/build/cmake_external/build_cmakeSundials.bash similarity index 72% rename from build/makefiles/Sundials/build_cmakeSundials.sh rename to build/cmake_external/build_cmakeSundials.bash index cb359d274..957347370 100755 --- a/build/makefiles/Sundials/build_cmakeSundials.sh +++ b/build/cmake_external/build_cmakeSundials.bash @@ -1,8 +1,9 @@ -# from {$maindir}/sundials/builddir, run -# cp ../../summa/build/makefiles/Sundials/build_cmakeSundials.sh build_cmake +#!/bin/bash + +# from {$maindir}/sundials/builddir, run +# cp ../../summa/build/cmake_external/build_cmakeSundials.bash build_cmake # run script from the builddir directory with ./build_cmake +# run `make`, then `make install` # Note, using -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF, if want to run examples should change cmake ../../sundials-6.3.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=../../sundials/instdir -DEXAMPLES_INSTALL_PATH=../../sundials/instdir/examples - - diff --git a/build/makefiles/Actors/build_cmakeActors.sh b/build/makefiles/Actors/build_cmakeActors.sh deleted file mode 100755 index 75698d166..000000000 --- a/build/makefiles/Actors/build_cmakeActors.sh +++ /dev/null @@ -1,7 +0,0 @@ -# from {$maindir}/actor-framework, run -# cp ../../summa/build/summa/build/makefiles/Actors/build_cmakeActors.sh build_cmake -# run script from the actor-framework directory with ./build_cmake -# run `make`, then `make install` - -setenv CXX "g++" -./configure --prefix=../install \ No newline at end of file diff --git a/build/makefiles/Actors/compile_copernicus.sh b/build/makefiles/Actors/compile_copernicus.sh deleted file mode 100644 index defa4b69d..000000000 --- a/build/makefiles/Actors/compile_copernicus.sh +++ /dev/null @@ -1,30 +0,0 @@ -#! /bin/bash - -#### load modules if using Compute Canada or Copernicus #### -module load gcc/9.3.0 -module load netcdf-fortran -module load openblas -module load caf - - -#### parent directory of the 'build' directory #### -export ROOT_DIR=/globalhome/kck540/HPC/Summa-Projects/Summa-Actors - -export INCLUDES="-I$EBROOTNETCDFMINFORTRAN/include" -export LIBRARIES="-L$EBROOTNETCDFMINFORTRAN/lib64\ - -L$EBROOTOPENBLAS/lib\ - -lnetcdff -lopenblas" - -# INCLUDES FOR Actors Component -export ACTORS_INCLUDES="-I$EBROOTCAF/include\ - -I$EBROOTNETCDFMINFORTRAN/include" - -export ACTORS_LIBRARIES="-L$EBROOTCAF/lib\ - -L$EBROOTCAF/lib64\ - -L$EBROOTNETCDFMINFORTRAN/lib64\ - -L$EBROOTOPENBLAS/lib\ - -L$ROOT_DIR/bin\ - -lcaf_core -lcaf_io -lsumma -lopenblas -lnetcdff" - -make -f ${ROOT_DIR}/build/makefiles/makefile_cluster -export LD_LIBRARY_PATH=${ROOT_DIR}/bin \ No newline at end of file diff --git a/build/makefiles/Actors/compile_graham.sh b/build/makefiles/Actors/compile_graham.sh deleted file mode 100644 index 13c2881fc..000000000 --- a/build/makefiles/Actors/compile_graham.sh +++ /dev/null @@ -1,30 +0,0 @@ -#! /bin/bash - -#### load modules if using Compute Canada or Copernicus #### -module load gcc/9.3.0 -module load netcdf-fortran -module load openblas -module load caf - - -#### parent directory of the 'build' directory #### -# export ROOT_DIR=/home/kklenk/Summa-Projects/Summa-Actors - -# export INCLUDES="-I$EBROOTNETCDFMINFORTRAN/include" -# export LIBRARIES="-L$EBROOTNETCDFMINFORTRAN/lib64\ -# -L$EBROOTOPENBLAS/lib\ -# -lnetcdff -lopenblas" - -# # INCLUDES FOR Actors Component -# export ACTORS_INCLUDES="-I$EBROOTCAF/include\ -# -I$EBROOTNETCDFMINFORTRAN/include" - -# export ACTORS_LIBRARIES="-L$EBROOTCAF/lib\ -# -L$EBROOTCAF/lib64\ -# -L$EBROOTNETCDFMINFORTRAN/lib64\ -# -L$EBROOTOPENBLAS/lib\ -# -L$ROOT_DIR/bin\ -# -lcaf_core -lcaf_io -lsumma -lopenblas -lnetcdff" - -# make -f ${ROOT_DIR}/build/makefiles/makefile_cluster cpp -# export LD_LIBRARY_PATH=${ROOT_DIR}/bin \ No newline at end of file diff --git a/build/makefiles/Actors/makefile_apptainer b/build/makefiles/Actors/makefile_apptainer deleted file mode 100644 index d9dff9069..000000000 --- a/build/makefiles/Actors/makefile_apptainer +++ /dev/null @@ -1,418 +0,0 @@ -#### parent directory of the 'build' directory #### -ROOT_DIR = /Summa-Actors - -#### Compilers #### -FC = gfortran # Fortran -CC = g++ # C++ - -DIR_SUNDIALS=/usr/local/sundials -INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran -LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod - - -#### Includes AND Libraries #### -INCLUDES = -I/usr/include -I/usr/local/include $(INC_SUNDIALS) -LIBRARIES = -L/usr/lib -L/usr/local/lib -lnetcdff -lopenblas $(LIB_SUNDIALS) - -ACTORS_INCLUDES = -I/usr/include -I/usr/local/include $(INC_SUNDIALS) -ACTORS_LIBRARIES = -L/usr/lib -L/usr/local/lib -L/Summa-Actors/bin -lcaf_core -lcaf_io -lsumma -lopenblas -lnetcdff $(LIB_SUNDIALS) - - -# Production runs -FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_COMM = -g -O0 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_SUMMA = -g -O0 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_ACTORS = -g -O0 -Wfatal-errors -std=c++17 - -# Debug runs -# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC -# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC -# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC -# FLAGS_ACTORS = -g -O0 -Wall -std=c++17 - - - -#======================================================================== -# PART 1: Define directory paths -#======================================================================== - -# Core directory that contains source code -F_KORE_DIR = $(ROOT_DIR)/build/source - -# Location of the compiled modules -MOD_PATH = $(ROOT_DIR)/build - -# Define the directory for the executables -EXE_PATH = $(ROOT_DIR)/bin - -#################################################################################################### -###################################### Assemble Fortran Files ###################################### -#################################################################################################### -# Define directories -DRIVER_DIR = $(F_KORE_DIR)/driver -HOOKUP_DIR = $(F_KORE_DIR)/hookup -NETCDF_DIR = $(F_KORE_DIR)/netcdf -DSHARE_DIR = $(F_KORE_DIR)/dshare -NUMREC_DIR = $(F_KORE_DIR)/numrec -NOAHMP_DIR = $(F_KORE_DIR)/noah-mp -ENGINE_DIR = $(F_KORE_DIR)/engine -ACTORS_DIR = $(F_KORE_DIR)/actors -JOB_ACTOR_DIR = $(ACTORS_DIR)/job_actor -FILE_ACCESS_DIR = $(ACTORS_DIR)/file_access_actor/fortran_code -HRU_ACTOR_DIR = $(ACTORS_DIR)/hru_actor/fortran_code -GRU_ACTOR_DIR = $(ACTORS_DIR)/gru_actor - -# utilities -SUMMA_NRUTIL= \ - nrtype.f90 \ - f2008funcs.f90 \ - nr_utility.f90 \ - -NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) - -# Numerical recipes procedures -# NOTE: all numerical recipes procedures are now replaced with free versions -SUMMA_NRPROC= \ - expIntegral.f90 \ - spline_int.f90 -NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) - -# Hook-up modules (set files and directory paths) -SUMMA_HOOKUP= \ - ascii_util.f90 \ - summaActors_FileManager.f90 -HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) - -# Data modules -SUMMA_DATAMS= \ - multiconst.f90 \ - var_lookup.f90 \ - data_types.f90 \ - globalData.f90 \ - flxMapping.f90 \ - get_ixname.f90 -DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) - -SUMMA_DEPEND_ON_FILEMANAGER= \ - popMetadat.f90 \ - outpt_stat.f90 -DEPEND_ON_FILEMANAGER = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DEPEND_ON_FILEMANAGER)) - -# utility modules -SUMMA_UTILMS= \ - time_utils.f90 \ - mDecisions.f90 \ - snow_utils.f90 \ - soil_utils.f90 \ - sundials/soil_utilsAddPrime.f90 \ - updatState.f90 \ - sundials/updatStateWithPrime.f90 \ - matrixOper.f90 -UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) - -# Model guts -SUMMA_MODGUT= \ - MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) - -# Solver -SUMMA_SOLVER= \ - vegPhenlgy.f90 \ - diagn_evar.f90 \ - stomResist.f90 \ - groundwatr.f90 \ - vegSWavRad.f90 \ - vegNrgFlux.f90 \ - ssdNrgFlux.f90 \ - vegLiqFlux.f90 \ - snowLiqFlx.f90 \ - soilLiqFlx.f90 \ - bigAquifer.f90 \ - computFlux.f90 \ - computResid.f90 \ - computJacob.f90 \ - eval8summa.f90 \ - summaSolve4numrec.f90 \ - systemSolv.f90 \ - sundials/type4ida.f90 \ - sundials/tol4ida.f90 \ - sundials/computEnthalpy.f90 \ - sundials/computHeatCap.f90 \ - sundials/computThermConduct.f90 \ - sundials/computResidWithPrime.f90 \ - sundials/eval8summaWithPrime.f90 \ - sundials/computJacobWithPrime.f90 \ - sundials/computSnowDepth.f90 \ - sundials/summaSolve4ida.f90 \ - varSubstep.f90 \ - opSplittin.f90 \ - coupled_em.f90 - -SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) - -# Interface code for Fortran-C++ -SUMMA_INTERFACE= \ - cppwrap_datatypes.f90 \ - cppwrap_auxiliary.f90 \ - cppwrap_metadata.f90 \ - -INTERFACE = $(patsubst %, $(ACTORS_DIR)/global/%, $(SUMMA_INTERFACE)) - - -SUMMA_FILEACCESS_INTERFACE = \ - cppwrap_fileAccess.f90 \ - read_attrb.f90 \ - read_force.f90 \ - read_param.f90 \ - write_to_netcdf.f90 - -FILEACCESS_INTERFACE = $(patsubst %, $(FILE_ACCESS_DIR)/%, $(SUMMA_FILEACCESS_INTERFACE)) - -SUMMA_JOB_INTERFACE = \ - job_actor.f90 - -JOB_INTERFACE = $(patsubst %, $(JOB_ACTOR_DIR)/%, $(SUMMA_JOB_INTERFACE)) - -SUMMA_HRU_INTERFACE = \ - cppwrap_hru.f90 \ - hru_actor.f90 \ - hru_init.f90 \ - - -HRU_INTERFACE = $(patsubst %, $(HRU_ACTOR_DIR)/%, $(SUMMA_HRU_INTERFACE)) - -SUMMA_GRU_INTERFACE = \ - gru_actor.f90 \ - -GRU_INTERFACE = $(patsubst %, $(GRU_ACTOR_DIR)/%, $(SUMMA_GRU_INTERFACE)) - - - -# Define routines for SUMMA preliminaries -SUMMA_PRELIM= \ - conv_funcs.f90 \ - sunGeomtry.f90 \ - convE2Temp.f90 \ - allocspaceActors.f90 \ - checkStruc.f90 \ - childStruc.f90 \ - ffile_info.f90 \ - read_dimension.f90 \ - read_pinit.f90 \ - pOverwrite.f90 \ - paramCheck.f90 \ - check_icondActors.f90 \ - # allocspace.f90 -PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) - -SUMMA_NOAHMP= \ - module_model_constants.F \ - module_sf_noahutl.F \ - module_sf_noahlsm.F \ - module_sf_noahmplsm.F - -NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) - -# Define routines for the SUMMA model runs -# sundials - t2enthalpy.f90 -SUMMA_MODRUN = \ - indexState.f90 \ - getVectorz.f90 \ - sundials/t2enthalpy.f90 \ - updateVars.f90 \ - sundials/updateVarsWithPrime.f90 \ - var_derive.f90 \ - derivforce.f90 \ - snowAlbedo.f90 \ - canopySnow.f90 \ - tempAdjust.f90 \ - snwCompact.f90 \ - layerMerge.f90 \ - layerDivide.f90 \ - volicePack.f90 \ - qTimeDelay.f90 -MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) - -# Define NetCDF routines -# OutputStrucWrite is not a netcdf subroutine and should be -# moved -SUMMA_NETCDF = \ - netcdf_util.f90 \ - def_output.f90 \ - writeOutput.f90 \ - read_icondActors.f90 -NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) - -# ... stitch together common programs -COMM_ALL = $(NRUTIL) $(NRPROC) $(DATAMS) $(INTERFACE) $(HOOKUP) $(DEPEND_ON_FILEMANAGER) $(UTILMS) - -# ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) - -# Define the driver routine -SUMMA_DRIVER= \ - summaActors_type.f90 \ - summaActors_util.f90 \ - summaActors_globalData.f90 \ - SummaActors_setup.f90 \ - summaActors_restart.f90 \ - SummaActors_modelRun.f90 \ - summaActors_alarms.f90 - - -DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) - -#################################################################################################### -###################################### Assemble Fortran Files ###################################### -#################################################################################################### - -#################################################################################################### -######################################## Assemble C++ Files ######################################## -#################################################################################################### - -INCLUDE_DIR = /Summa-Actors/build/includes -SOURCE_DIR = /Summa-Actors/build/source/actors - - -GLOBAL_INCLUDES = -I$(INCLUDE_DIR)/global -GLOBAL = $(SOURCE_DIR)/global/global.cpp -TIMEINFO = $(SOURCE_DIR)/global/timing_info.cpp -SETTINGS_FILES = $(SOURCE_DIR)/global/settings_functions.cpp -AUXILARY = $(SOURCE_DIR)/global/auxiliary.cpp - -SUMMA_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/summa_actor -SUMMA_ACTOR = $(SOURCE_DIR)/summa_actor/summa_actor.cpp -SUMMA_CLIENT = $(SOURCE_DIR)/summa_actor/summa_client.cpp -SUMMA_SERVER = $(SOURCE_DIR)/summa_actor/summa_server.cpp -SUMMA_BACKUP_SERVER = $(SOURCE_DIR)/summa_actor/summa_backup_server.cpp - -BATCH = $(SOURCE_DIR)/summa_actor/batch/batch.cpp -BATCH_CONTAINER = $(SOURCE_DIR)/summa_actor/batch/batch_container.cpp - -CLIENT = $(SOURCE_DIR)/summa_actor/client/client.cpp -CLIENT_CONTAINER = $(SOURCE_DIR)/summa_actor/client/client_container.cpp - -CLIENT_BATCH = $(SOURCE_DIR)/summa_actor/batch_client.cpp -CLIENT_BATCH_CONTAINERS = $(SOURCE_DIR)/summa_actor/batch_client_containers.cpp - -GRU_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/gru_actor -GRU_ACTOR = $(SOURCE_DIR)/gru_actor/gru_actor.cpp - -JOB_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/job_actor -JOB_ACTOR = $(SOURCE_DIR)/job_actor/job_actor.cpp -GRUinfo = $(SOURCE_DIR)/job_actor/GRUinfo.cpp - -FILE_ACCESS_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/file_access_actor -FILE_ACCESS_ACTOR = $(SOURCE_DIR)/file_access_actor/cpp_code/file_access_actor.cpp -FORCING_FILE_INFO = $(SOURCE_DIR)/file_access_actor/cpp_code/forcing_file_info.cpp -OUTPUT_MANAGER = $(SOURCE_DIR)/file_access_actor/cpp_code/output_manager.cpp - -HRU_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/hru_actor -HRU_ACTOR = $(SOURCE_DIR)/hru_actor/cpp_code/hru_actor.cpp - -MAIN = $(F_KORE_DIR)/actors/main.cpp - -ACTOR_TEST = $(F_KORE_DIR)/testing/testing_main.cc -#################################################################################################### -######################################## Assemble C++ Files ######################################## -#################################################################################################### - - -#======================================================================== -# PART 3: compilation -#====================================================================== -all: fortran cpp - -fortran: compile_noah compile_comm compile_summa link clean_fortran - -cpp: compile_globals compile_hru_actor compile_gru_actor compile_file_access_actor compile_job_actor compile_summa_actor \ - compile_summa_client compile_summa_server compile_main link_cpp clean_cpp - -test: actors_test actors_testLink actorsClean - -################################################################################################################### -############################################## COMPILE SUMMA-Fortran ############################################## -################################################################################################################### -compile_noah: - $(FC) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) - -# compile common routines -compile_comm: - $(FC) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) - -# compile SUMMA routines -compile_summa: - $(FC) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(JOB_INTERFACE) $(FILEACCESS_INTERFACE) $(HRU_INTERFACE) $(GRU_INTERFACE) $(INCLUDES) - -# generate library -link: - $(FC) -shared *.o -o libsumma.so - mv libsumma.so $(ROOT_DIR)/bin - -# Remove object files -clean_fortran: - rm -f *.o *.mod soil_veg_gen_parm__genmod.f90 -################################################################################################################### -############################################## COMPILE SUMMA-Fortran ############################################## -################################################################################################################### - - -################################################################################################################### -################################################ COMPILE SUMMA-C++ ################################################ -################################################################################################################### -compile_globals: - $(CC) $(FLAGS_ACTORS) -c $(GLOBAL) $(TIMEINFO) $(AUXILARY) $(SETTINGS_FILES) $(GLOBAL_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_gru_actor: - $(CC) $(FLAGS_ACTORS) -c $(GRU_ACTOR) $(HRU_ACTOR_INCLUDES) $(GRU_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_hru_actor: - $(CC) $(FLAGS_ACTORS) -c $(HRU_ACTOR) $(HRU_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_file_access_actor: - $(CC) $(FLAGS_ACTORS) -c $(FILE_ACCESS_ACTOR) $(FORCING_FILE_INFO) $(OUTPUT_MANAGER) \ - $(FILE_ACCESS_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_job_actor: - $(CC) $(FLAGS_ACTORS) -c $(JOB_ACTOR) $(GRUinfo) $(JOB_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) \ - $(FILE_ACCESS_ACTOR_INCLUDES) $(HRU_ACTOR_INCLUDES) $(GRU_ACTOR_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_summa_actor: - $(CC) $(FLAGS_ACTORS) -c $(SUMMA_ACTOR) $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) \ - $(JOB_ACTOR_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_summa_client: - $(CC) $(FLAGS_ACTORS) -c $(SUMMA_CLIENT) $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) - -compile_summa_server: - $(CC) $(FLAGS_ACTORS) -c $(SUMMA_SERVER) $(SUMMA_BACKUP_SERVER) $(BATCH) $(CLIENT) $(BATCH_CONTAINER) $(CLIENT_CONTAINER) \ - $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) - -compile_main: - $(CC) $(FLAGS_ACTORS) -c $(MAIN) $(GLOBAL_INCLUDES) $(SUMMA_ACTOR_INCLUDES) $(JOB_ACTOR_INCLUDES) - -link_cpp: - $(CC) $(FLAGS_ACTORS) -Wl,-rpath='/Summa-Actors/bin:/usr/local/lib:/code/sundials/instdir/lib' -o summaMain *.o $(ACTORS_LIBRARIES) - mv summaMain $(ROOT_DIR)/bin - -clean_cpp: - rm *.o -################################################################################################################### -################################################ COMPILE SUMMA-C++ ################################################ -################################################################################################################### - - -################################################################################################################### -################################################## COMPILE TESTS ################################################## -################################################################################################################### -actors_test: - $(CC) $(FLAGS_ACTORS) -c $(ACTOR_TEST) -std=c++17 $(ACTORS_INCLUDES) - -actors_testLink: - $(CC) -o summaTest *.o $(ACTORS_LIBRARIES) - -clean_lib: - rm *.so - - - - diff --git a/build/makefiles/Actors/makefile_cluster b/build/makefiles/Actors/makefile_cluster deleted file mode 100644 index 8ce4062cf..000000000 --- a/build/makefiles/Actors/makefile_cluster +++ /dev/null @@ -1,419 +0,0 @@ -#### parent directory of the 'build' directory #### -# ROOT_DIR = - -#### Compilers #### -FC = gfortran # Fortran -CC = g++ # C++ - -#### Includes AND Libraries #### -# INCLUDES = -# LIBRARIES = - -# ACTORS_INCLUDES = -# ACTORS_LIBRARIES = - - -# Production runs -FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_ACTORS = -O3 -Wfatal-errors -std=c++17 - -# Debug runs -# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC -# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC -# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC -# FLAGS_ACTORS = -g -O0 -Wall -std=c++17 - -#======================================================================== -# PART 1: Define directory paths -#======================================================================== - -# Core directory that contains source code -F_KORE_DIR = $(ROOT_DIR)/build/source - -# Location of the compiled modules -MOD_PATH = $(ROOT_DIR)/build - -# Define the directory for the executables -EXE_PATH = $(ROOT_DIR)/bin - -#################################################################################################### -###################################### Assemble Fortran Files ###################################### -#################################################################################################### -# Define directories -DRIVER_DIR = $(F_KORE_DIR)/driver -HOOKUP_DIR = $(F_KORE_DIR)/hookup -NETCDF_DIR = $(F_KORE_DIR)/netcdf -DSHARE_DIR = $(F_KORE_DIR)/dshare -NUMREC_DIR = $(F_KORE_DIR)/numrec -NOAHMP_DIR = $(F_KORE_DIR)/noah-mp -ENGINE_DIR = $(F_KORE_DIR)/engine -ACTORS_DIR = $(F_KORE_DIR)/actors - -SUMMA_DSHARE_DIR = $(MOD_PATH)/summa/build/source/dshare -SUMMA_ENGINE_DIR = $(MOD_PATH)/summa/build/source/engine - - -JOB_ACTOR_DIR = $(ACTORS_DIR)/job_actor -FILE_ACCESS_DIR = $(ACTORS_DIR)/file_access_actor/fortran_code -HRU_ACTOR_DIR = $(ACTORS_DIR)/hru_actor/fortran_code -GRU_ACTOR_DIR = $(ACTORS_DIR)/gru_actor - -# utilities -SUMMA_NRUTIL= \ - nrtype.f90 \ - f2008funcs.f90 \ - nr_utility.f90 \ - -NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) - -# Numerical recipes procedures -# NOTE: all numerical recipes procedures are now replaced with free versions -SUMMA_NRPROC= \ - expIntegral.f90 \ - spline_int.f90 -NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) - -# Hook-up modules (set files and directory paths) -SUMMA_HOOKUP= \ - ascii_util.f90 \ - summaActors_FileManager.f90 -HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) - -# Data modules -SUMMA_DATAMS= \ - var_lookup.f90 \ - multiconst.f90 \ - data_types.f90 \ - globalData.f90 \ - flxMapping.f90 -DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) - -IXNAME_METADAT = \ - get_ixname.f90 \ - popMetadat.f90 -IXNAME_METADAT_DATAMS = $(patsubst %, $(SUMMA_DSHARE_DIR)/%, $(IXNAME_METADAT)) - -SUMMA_DEPEND_ON_FILEMANAGER= \ - outpt_stat.f90 -DEPEND_ON_FILEMANAGER = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DEPEND_ON_FILEMANAGER)) - - -# utility modules -SUMMA_UTILMS= \ - time_utils.f90 \ - mDecisions.f90 \ - snow_utils.f90 \ - soil_utils.f90 \ - updatState.f90 \ - matrixOper.f90 -UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) - -# Model guts -SUMMA_MODGUT= \ - MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) - - - -# Solver - Split up to compiling with old summa files vs new summa files -VEG_PHENLGY= \ - vegPhenlgy.f90 -VEG_PHENLGY_SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(VEG_PHENLGY)) -SNOW_DEPTH= \ - sundials/computSnowDepth.f90 -SNOW_DEPTH_SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SNOW_DEPTH)) -SUMMA_SOLVER= \ - diagn_evar.f90 \ - stomResist.f90 \ - groundwatr.f90 \ - vegSWavRad.f90 \ - vegNrgFlux.f90 \ - ssdNrgFlux.f90 \ - vegLiqFlux.f90 \ - snowLiqFlx.f90 \ - soilLiqFlx.f90 \ - bigAquifer.f90 \ - computFlux.f90 \ - computResid.f90 \ - computJacob.f90 \ - eval8summa.f90 \ - summaSolve4numrec.f90 \ - systemSolv.f90 \ - varSubstep.f90 \ - opSplittin.f90 -SOLVER = $(patsubst %, $(SUMMA_ENGINE_DIR)/%, $(SUMMA_SOLVER)) -SUMMA_ACTORS_SOLVER = \ - coupled_em.f90 -COUPLED_EM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_ACTORS_SOLVER)) - - - -# Interface code for Fortran-C++ -SUMMA_INTERFACE= \ - cppwrap_datatypes.f90 \ - cppwrap_auxiliary.f90 \ - cppwrap_metadata.f90 \ - -INTERFACE = $(patsubst %, $(ACTORS_DIR)/global/%, $(SUMMA_INTERFACE)) - - -SUMMA_FILEACCESS_INTERFACE = \ - output_structure.f90 \ - cppwrap_fileAccess.f90 \ - read_attrb.f90 \ - read_force.f90 \ - read_param.f90 \ - read_icondFromStructure.f90 \ - writeOutputFromOutputStructure.f90 \ - write_to_netcdf.f90 - -FILEACCESS_INTERFACE = $(patsubst %, $(FILE_ACCESS_DIR)/%, $(SUMMA_FILEACCESS_INTERFACE)) - -SUMMA_JOB_INTERFACE = \ - job_actor.f90 - -JOB_INTERFACE = $(patsubst %, $(JOB_ACTOR_DIR)/%, $(SUMMA_JOB_INTERFACE)) - -SUMMA_HRU_INTERFACE = \ - cppwrap_hru.f90 \ - hru_actor.f90 \ - hru_init.f90 \ - hru_modelwrite.f90 \ - hru_writeOutput.f90 - - -HRU_INTERFACE = $(patsubst %, $(HRU_ACTOR_DIR)/%, $(SUMMA_HRU_INTERFACE)) - -SUMMA_GRU_INTERFACE = \ - gru_actor.f90 \ - -GRU_INTERFACE = $(patsubst %, $(GRU_ACTOR_DIR)/%, $(SUMMA_GRU_INTERFACE)) - - - -# Define routines for SUMMA preliminaries -SUMMA_PRELIM= \ - conv_funcs.f90 \ - sunGeomtry.f90 \ - convE2Temp.f90 \ - allocspaceActors.f90 \ - alloc_fileAccess.f90 \ - checkStruc.f90 \ - childStruc.f90 \ - ffile_info.f90 \ - read_dimension.f90 \ - read_pinit.f90 \ - pOverwrite.f90 \ - paramCheck.f90 \ - check_icondActors.f90 -PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) - -SUMMA_NOAHMP= \ - module_model_constants.F \ - module_sf_noahutl.F \ - module_sf_noahlsm.F \ - module_sf_noahmplsm.F - -NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) - -# Define routines for the SUMMA model runs -DERIV_FORCE = \ - derivforce.f90 \ - sundials/t2enthalpy.f90 -DERIV_FORCE_MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(DERIV_FORCE)) - -SUMMA_MODRUN = \ - indexState.f90 \ - getVectorz.f90 \ - updateVars.f90 \ - var_derive.f90 \ - snowAlbedo.f90 \ - canopySnow.f90 \ - tempAdjust.f90 \ - snwCompact.f90 \ - layerMerge.f90 \ - layerDivide.f90 \ - volicePack.f90 \ - qTimeDelay.f90 -MODRUN = $(patsubst %, $(SUMMA_ENGINE_DIR)/%, $(SUMMA_MODRUN)) - -# Define NetCDF routines -# OutputStrucWrite is not a netcdf subroutine and should be -# moved -SUMMA_NETCDF = \ - netcdf_util.f90 \ - def_output.f90 \ - writeOutput.f90 \ - read_icondActors.f90 -NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) - -# ... stitch together common programs -COMM_ALL = $(NRUTIL) $(NRPROC) $(DATAMS) \ - $(INTERFACE) $(HOOKUP) $(IXNAME_METADAT_DATAMS) $(DEPEND_ON_FILEMANAGER) \ - $(TIME_UTILS_UTILMS) $(M_DECISIONS_UTILMS) $(UTILMS) -# ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(DERIV_FORCE_MODRUN) $(MODRUN) \ - $(VEG_PHENLGY_SOLVER) $(SOLVER) $(SNOW_DEPTH_SOLVER) $(COUPLED_EM) - -# Define the driver routine -SUMMA_DRIVER= \ - summaActors_type.f90 \ - summaActors_util.f90 \ - summaActors_globalData.f90 \ - SummaActors_setup.f90 \ - summaActors_restart.f90 \ - SummaActors_modelRun.f90 \ - summaActors_alarms.f90 - - -DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) - -#################################################################################################### -###################################### Assemble Fortran Files ###################################### -#################################################################################################### - -#################################################################################################### -######################################## Assemble C++ Files ######################################## -#################################################################################################### - -INCLUDE_DIR = $(ROOT_DIR)/build/includes -SOURCE_DIR = $(ROOT_DIR)/build/source/actors - - -GLOBAL_INCLUDES = -I$(INCLUDE_DIR)/global -GLOBAL = $(SOURCE_DIR)/global/global.cpp -TIMEINFO = $(SOURCE_DIR)/global/timing_info.cpp -SETTINGS_FILES = $(SOURCE_DIR)/global/settings_functions.cpp -AUXILARY = $(SOURCE_DIR)/global/auxiliary.cpp - -SUMMA_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/summa_actor -SUMMA_ACTOR = $(SOURCE_DIR)/summa_actor/summa_actor.cpp -SUMMA_CLIENT = $(SOURCE_DIR)/summa_actor/summa_client.cpp -SUMMA_SERVER = $(SOURCE_DIR)/summa_actor/summa_server.cpp -SUMMA_BACKUP_SERVER = $(SOURCE_DIR)/summa_actor/summa_backup_server.cpp - -BATCH = $(SOURCE_DIR)/summa_actor/batch/batch.cpp -BATCH_CONTAINER = $(SOURCE_DIR)/summa_actor/batch/batch_container.cpp - -CLIENT = $(SOURCE_DIR)/summa_actor/client/client.cpp -CLIENT_CONTAINER = $(SOURCE_DIR)/summa_actor/client/client_container.cpp - -GRU_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/gru_actor -GRU_ACTOR = $(SOURCE_DIR)/gru_actor/gru_actor.cpp - -JOB_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/job_actor -JOB_ACTOR = $(SOURCE_DIR)/job_actor/job_actor.cpp -GRUinfo = $(SOURCE_DIR)/job_actor/GRUinfo.cpp - -FILE_ACCESS_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/file_access_actor -FILE_ACCESS_ACTOR = $(SOURCE_DIR)/file_access_actor/cpp_code/file_access_actor.cpp -FORCING_FILE_INFO = $(SOURCE_DIR)/file_access_actor/cpp_code/forcing_file_info.cpp -OUTPUT_CONTAINER = $(SOURCE_DIR)/file_access_actor/cpp_code/output_container.cpp - -HRU_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/hru_actor -HRU_ACTOR = $(SOURCE_DIR)/hru_actor/cpp_code/hru_actor.cpp - -MAIN = $(F_KORE_DIR)/actors/main.cpp - -ACTOR_TEST = $(F_KORE_DIR)/testing/testing_main.cc -#################################################################################################### -######################################## Assemble C++ Files ######################################## -#################################################################################################### - - -#======================================================================== -# PART 3: compilation -#====================================================================== -all: fortran cpp - -fortran: compile_noah compile_comm compile_summa link clean_fortran - -cpp: compile_globals compile_hru_actor compile_gru_actor compile_file_access_actor compile_job_actor compile_summa_actor \ - compile_summa_client compile_summa_server compile_main link_cpp clean_cpp - -test: actors_test actors_testLink actorsClean - -################################################################################################################### -############################################## COMPILE SUMMA-Fortran ############################################## -################################################################################################################### -compile_noah: - $(FC) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) - -# compile common routines -compile_comm: - $(FC) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) - -# compile SUMMA routines -compile_summa: - $(FC) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(JOB_INTERFACE) $(FILEACCESS_INTERFACE) $(HRU_INTERFACE) $(GRU_INTERFACE) $(INCLUDES) - -# generate library -link: - $(FC) -shared *.o -o libsumma.so - mv libsumma.so $(ROOT_DIR)/bin - -# Remove object files -clean_fortran: - rm -f *.o *.mod soil_veg_gen_parm__genmod.f90 -################################################################################################################### -############################################## COMPILE SUMMA-Fortran ############################################## -################################################################################################################### - - -################################################################################################################### -################################################ COMPILE SUMMA-C++ ################################################ -################################################################################################################### -compile_globals: - $(CC) $(FLAGS_ACTORS) -c $(GLOBAL) $(TIMEINFO) $(AUXILARY) $(SETTINGS_FILES) $(GLOBAL_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_gru_actor: - $(CC) $(FLAGS_ACTORS) -c $(GRU_ACTOR) $(HRU_ACTOR_INCLUDES) $(GRU_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_hru_actor: - $(CC) $(FLAGS_ACTORS) -c $(HRU_ACTOR) $(HRU_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_file_access_actor: - $(CC) $(FLAGS_ACTORS) -c $(OUTPUT_CONTAINER) $(FILE_ACCESS_ACTOR) $(FORCING_FILE_INFO) $(OUTPUT_MANAGER) \ - $(FILE_ACCESS_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_job_actor: - $(CC) $(FLAGS_ACTORS) -c $(JOB_ACTOR) $(GRUinfo) $(JOB_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) \ - $(FILE_ACCESS_ACTOR_INCLUDES) $(HRU_ACTOR_INCLUDES) $(GRU_ACTOR_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_summa_actor: - $(CC) $(FLAGS_ACTORS) -c $(SUMMA_ACTOR) $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) \ - $(JOB_ACTOR_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_summa_client: - $(CC) $(FLAGS_ACTORS) -c $(SUMMA_CLIENT) $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) - -compile_summa_server: - $(CC) $(FLAGS_ACTORS) -c $(SUMMA_SERVER) $(SUMMA_BACKUP_SERVER) $(BATCH) $(CLIENT) $(BATCH_CONTAINER) $(CLIENT_CONTAINER) \ - $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) - -compile_main: - $(CC) $(FLAGS_ACTORS) -c $(MAIN) $(GLOBAL_INCLUDES) $(SUMMA_ACTOR_INCLUDES) $(JOB_ACTOR_INCLUDES) - -link_cpp: - $(CC) $(FLAGS_ACTORS) -Wl,-rpath='$(ROOT_DIR)/bin:/usr/local/lib' -o summaMain *.o $(ACTORS_LIBRARIES) - mv summaMain $(ROOT_DIR)/bin - -clean_cpp: - rm *.o -################################################################################################################### -################################################ COMPILE SUMMA-C++ ################################################ -################################################################################################################### - - -################################################################################################################### -################################################## COMPILE TESTS ################################################## -################################################################################################################### -actors_test: - $(CC) $(FLAGS_ACTORS) -c $(ACTOR_TEST) -std=c++17 $(ACTORS_INCLUDES) - -actors_testLink: - $(CC) -o summaTest *.o $(ACTORS_LIBRARIES) - -clean_lib: - rm *.so \ No newline at end of file diff --git a/build/makefiles/BE/makefile_old_summa b/build/makefiles/BE/makefile_old_summa deleted file mode 100644 index c426eb001..000000000 --- a/build/makefiles/BE/makefile_old_summa +++ /dev/null @@ -1,421 +0,0 @@ -#### parent directory of the 'build' directory #### -ROOT_DIR = /Summa-Actors - -#### Compilers #### -FC = gfortran # Fortran -CC = g++ # C++ - -#### Includes AND Libraries #### -INCLUDES = -I/usr/include -I/usr/local/include -LIBRARIES = -L/usr/lib -L/usr/local/lib -lnetcdff -lopenblas - -ACTORS_INCLUDES = -I/usr/include -I/usr/local/include -ACTORS_LIBRARIES = -L/usr/lib -L/usr/local/lib -L/Summa-Actors/bin -lcaf_core -lcaf_io -lsumma -lopenblas -lnetcdff - -# Production runs -FLAGS_NOAH = -g -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_COMM = -g -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_SUMMA = -g -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_ACTORS = -g -O3 -Wfatal-errors -std=c++17 - -# Debug runs -# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC -# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC -# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC -# FLAGS_ACTORS = -g -O0 -Wall -std=c++17 - - - -#======================================================================== -# PART 1: Define directory paths -#======================================================================== - -# Core directory that contains source code -F_KORE_DIR = $(ROOT_DIR)/build/source - -# Location of the compiled modules -MOD_PATH = $(ROOT_DIR)/build - -# Define the directory for the executables -EXE_PATH = $(ROOT_DIR)/bin - -#################################################################################################### -###################################### Assemble Fortran Files ###################################### -#################################################################################################### -# Define directories -DRIVER_DIR = $(F_KORE_DIR)/driver -HOOKUP_DIR = $(F_KORE_DIR)/hookup -NETCDF_DIR = $(F_KORE_DIR)/netcdf -DSHARE_DIR = $(F_KORE_DIR)/dshare -NUMREC_DIR = $(F_KORE_DIR)/numrec -NOAHMP_DIR = $(F_KORE_DIR)/noah-mp -ENGINE_DIR = $(F_KORE_DIR)/engine -ACTORS_DIR = $(F_KORE_DIR)/actors - -SUMMA_DSHARE_DIR = $(MOD_PATH)/summa/build/source/dshare -SUMMA_ENGINE_DIR = $(MOD_PATH)/summa/build/source/engine - - -JOB_ACTOR_DIR = $(ACTORS_DIR)/job_actor -FILE_ACCESS_DIR = $(ACTORS_DIR)/file_access_actor/fortran_code -HRU_ACTOR_DIR = $(ACTORS_DIR)/hru_actor/fortran_code -GRU_ACTOR_DIR = $(ACTORS_DIR)/gru_actor - -# utilities -SUMMA_NRUTIL= \ - nrtype.f90 \ - f2008funcs.f90 \ - nr_utility.f90 \ - -NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) - -# Numerical recipes procedures -# NOTE: all numerical recipes procedures are now replaced with free versions -SUMMA_NRPROC= \ - expIntegral.f90 \ - spline_int.f90 -NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) - -# Hook-up modules (set files and directory paths) -SUMMA_HOOKUP= \ - ascii_util.f90 \ - summaActors_FileManager.f90 -HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) - -# Data modules -SUMMA_DATAMS= \ - var_lookup.f90 \ - multiconst.f90 \ - data_types.f90 \ - globalData.f90 \ - flxMapping.f90 -DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) - -IXNAME_METADAT = \ - get_ixname.f90 \ - popMetadat.f90 -IXNAME_METADAT_DATAMS = $(patsubst %, $(SUMMA_DSHARE_DIR)/%, $(IXNAME_METADAT)) - -SUMMA_DEPEND_ON_FILEMANAGER= \ - outpt_stat.f90 -DEPEND_ON_FILEMANAGER = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DEPEND_ON_FILEMANAGER)) - - -# utility modules -SUMMA_UTILMS= \ - time_utils.f90 \ - mDecisions.f90 \ - snow_utils.f90 \ - soil_utils.f90 \ - updatState.f90 \ - matrixOper.f90 -UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) - -# Model guts -SUMMA_MODGUT= \ - MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) - - - -# Solver - Split up to compiling with old summa files vs new summa files -VEG_PHENLGY= \ - vegPhenlgy.f90 -VEG_PHENLGY_SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(VEG_PHENLGY)) -SNOW_DEPTH= \ - sundials/computSnowDepth.f90 -SNOW_DEPTH_SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SNOW_DEPTH)) -SUMMA_SOLVER= \ - diagn_evar.f90 \ - stomResist.f90 \ - groundwatr.f90 \ - vegSWavRad.f90 \ - vegNrgFlux.f90 \ - ssdNrgFlux.f90 \ - vegLiqFlux.f90 \ - snowLiqFlx.f90 \ - soilLiqFlx.f90 \ - bigAquifer.f90 \ - computFlux.f90 \ - computResid.f90 \ - computJacob.f90 \ - eval8summa.f90 \ - summaSolve4numrec.f90 \ - systemSolv.f90 \ - varSubstep.f90 \ - opSplittin.f90 -SOLVER = $(patsubst %, $(SUMMA_ENGINE_DIR)/%, $(SUMMA_SOLVER)) -SUMMA_ACTORS_SOLVER = \ - coupled_em.f90 -COUPLED_EM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_ACTORS_SOLVER)) - - - -# Interface code for Fortran-C++ -SUMMA_INTERFACE= \ - cppwrap_datatypes.f90 \ - cppwrap_auxiliary.f90 \ - cppwrap_metadata.f90 \ - -INTERFACE = $(patsubst %, $(ACTORS_DIR)/global/%, $(SUMMA_INTERFACE)) - - -SUMMA_FILEACCESS_INTERFACE = \ - output_structure.f90 \ - cppwrap_fileAccess.f90 \ - read_attrb.f90 \ - read_force.f90 \ - read_param.f90 \ - read_icondFromStructure.f90 \ - writeOutputFromOutputStructure.f90 \ - write_to_netcdf.f90 - -FILEACCESS_INTERFACE = $(patsubst %, $(FILE_ACCESS_DIR)/%, $(SUMMA_FILEACCESS_INTERFACE)) - -SUMMA_JOB_INTERFACE = \ - job_actor.f90 - -JOB_INTERFACE = $(patsubst %, $(JOB_ACTOR_DIR)/%, $(SUMMA_JOB_INTERFACE)) - -SUMMA_HRU_INTERFACE = \ - cppwrap_hru.f90 \ - hru_actor.f90 \ - hru_init.f90 \ - hru_modelwrite.f90 \ - hru_writeOutput.f90 - - -HRU_INTERFACE = $(patsubst %, $(HRU_ACTOR_DIR)/%, $(SUMMA_HRU_INTERFACE)) - -SUMMA_GRU_INTERFACE = \ - gru_actor.f90 \ - -GRU_INTERFACE = $(patsubst %, $(GRU_ACTOR_DIR)/%, $(SUMMA_GRU_INTERFACE)) - - - -# Define routines for SUMMA preliminaries -SUMMA_PRELIM= \ - conv_funcs.f90 \ - sunGeomtry.f90 \ - convE2Temp.f90 \ - allocspaceActors.f90 \ - alloc_fileAccess.f90 \ - checkStruc.f90 \ - childStruc.f90 \ - ffile_info.f90 \ - read_dimension.f90 \ - read_pinit.f90 \ - pOverwrite.f90 \ - paramCheck.f90 \ - check_icondActors.f90 -PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) - -SUMMA_NOAHMP= \ - module_model_constants.F \ - module_sf_noahutl.F \ - module_sf_noahlsm.F \ - module_sf_noahmplsm.F - -NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) - -# Define routines for the SUMMA model runs -DERIV_FORCE = \ - derivforce.f90 \ - sundials/t2enthalpy.f90 -DERIV_FORCE_MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(DERIV_FORCE)) - -SUMMA_MODRUN = \ - indexState.f90 \ - getVectorz.f90 \ - updateVars.f90 \ - var_derive.f90 \ - snowAlbedo.f90 \ - canopySnow.f90 \ - tempAdjust.f90 \ - snwCompact.f90 \ - layerMerge.f90 \ - layerDivide.f90 \ - volicePack.f90 \ - qTimeDelay.f90 -MODRUN = $(patsubst %, $(SUMMA_ENGINE_DIR)/%, $(SUMMA_MODRUN)) - -# Define NetCDF routines -# OutputStrucWrite is not a netcdf subroutine and should be -# moved -SUMMA_NETCDF = \ - netcdf_util.f90 \ - def_output.f90 \ - writeOutput.f90 \ - read_icondActors.f90 -NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) - -# ... stitch together common programs -COMM_ALL = $(NRUTIL) $(NRPROC) $(DATAMS) \ - $(INTERFACE) $(HOOKUP) $(IXNAME_METADAT_DATAMS) $(DEPEND_ON_FILEMANAGER) \ - $(TIME_UTILS_UTILMS) $(M_DECISIONS_UTILMS) $(UTILMS) -# ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(DERIV_FORCE_MODRUN) $(MODRUN) \ - $(VEG_PHENLGY_SOLVER) $(SOLVER) $(SNOW_DEPTH_SOLVER) $(COUPLED_EM) - -# Define the driver routine -SUMMA_DRIVER= \ - summaActors_type.f90 \ - summaActors_util.f90 \ - summaActors_globalData.f90 \ - SummaActors_setup.f90 \ - summaActors_restart.f90 \ - SummaActors_modelRun.f90 \ - summaActors_alarms.f90 - - -DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) - -#################################################################################################### -###################################### Assemble Fortran Files ###################################### -#################################################################################################### - -#################################################################################################### -######################################## Assemble C++ Files ######################################## -#################################################################################################### - -INCLUDE_DIR = $(ROOT_DIR)/build/includes -SOURCE_DIR = $(ROOT_DIR)/build/source/actors - - -GLOBAL_INCLUDES = -I$(INCLUDE_DIR)/global -GLOBAL = $(SOURCE_DIR)/global/global.cpp -TIMEINFO = $(SOURCE_DIR)/global/timing_info.cpp -SETTINGS_FILES = $(SOURCE_DIR)/global/settings_functions.cpp -AUXILARY = $(SOURCE_DIR)/global/auxiliary.cpp - -SUMMA_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/summa_actor -SUMMA_ACTOR = $(SOURCE_DIR)/summa_actor/summa_actor.cpp -SUMMA_CLIENT = $(SOURCE_DIR)/summa_actor/summa_client.cpp -SUMMA_SERVER = $(SOURCE_DIR)/summa_actor/summa_server.cpp -SUMMA_BACKUP_SERVER = $(SOURCE_DIR)/summa_actor/summa_backup_server.cpp - -BATCH = $(SOURCE_DIR)/summa_actor/batch/batch.cpp -BATCH_CONTAINER = $(SOURCE_DIR)/summa_actor/batch/batch_container.cpp - -CLIENT = $(SOURCE_DIR)/summa_actor/client/client.cpp -CLIENT_CONTAINER = $(SOURCE_DIR)/summa_actor/client/client_container.cpp - -GRU_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/gru_actor -GRU_ACTOR = $(SOURCE_DIR)/gru_actor/gru_actor.cpp - -JOB_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/job_actor -JOB_ACTOR = $(SOURCE_DIR)/job_actor/job_actor.cpp -GRUinfo = $(SOURCE_DIR)/job_actor/GRUinfo.cpp - -FILE_ACCESS_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/file_access_actor -FILE_ACCESS_ACTOR = $(SOURCE_DIR)/file_access_actor/cpp_code/file_access_actor.cpp -FORCING_FILE_INFO = $(SOURCE_DIR)/file_access_actor/cpp_code/forcing_file_info.cpp -OUTPUT_CONTAINER = $(SOURCE_DIR)/file_access_actor/cpp_code/output_container.cpp - -HRU_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/hru_actor -HRU_ACTOR = $(SOURCE_DIR)/hru_actor/cpp_code/hru_actor.cpp - -MAIN = $(F_KORE_DIR)/actors/main.cpp - -ACTOR_TEST = $(F_KORE_DIR)/testing/testing_main.cc -#################################################################################################### -######################################## Assemble C++ Files ######################################## -#################################################################################################### - - -#======================================================================== -# PART 3: compilation -#====================================================================== -all: fortran cpp - -fortran: compile_noah compile_comm compile_summa link clean_fortran - -cpp: compile_globals compile_hru_actor compile_gru_actor compile_file_access_actor compile_job_actor compile_summa_actor \ - compile_summa_client compile_summa_server compile_main link_cpp clean_cpp - -test: actors_test actors_testLink actorsClean - -################################################################################################################### -############################################## COMPILE SUMMA-Fortran ############################################## -################################################################################################################### -compile_noah: - $(FC) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) - -# compile common routines -compile_comm: - $(FC) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) - -# compile SUMMA routines -compile_summa: - $(FC) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(JOB_INTERFACE) $(FILEACCESS_INTERFACE) $(HRU_INTERFACE) $(GRU_INTERFACE) $(INCLUDES) - -# generate library -link: - $(FC) -shared *.o -o libsumma.so - mv libsumma.so $(ROOT_DIR)/bin - -# Remove object files -clean_fortran: - rm -f *.o *.mod soil_veg_gen_parm__genmod.f90 -################################################################################################################### -############################################## COMPILE SUMMA-Fortran ############################################## -################################################################################################################### - - -################################################################################################################### -################################################ COMPILE SUMMA-C++ ################################################ -################################################################################################################### -compile_globals: - $(CC) $(FLAGS_ACTORS) -c $(GLOBAL) $(TIMEINFO) $(AUXILARY) $(SETTINGS_FILES) $(GLOBAL_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_gru_actor: - $(CC) $(FLAGS_ACTORS) -c $(GRU_ACTOR) $(HRU_ACTOR_INCLUDES) $(GRU_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_hru_actor: - $(CC) $(FLAGS_ACTORS) -c $(HRU_ACTOR) $(HRU_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_file_access_actor: - $(CC) $(FLAGS_ACTORS) -c $(OUTPUT_CONTAINER) $(FILE_ACCESS_ACTOR) $(FORCING_FILE_INFO) $(OUTPUT_MANAGER) \ - $(FILE_ACCESS_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_job_actor: - $(CC) $(FLAGS_ACTORS) -c $(JOB_ACTOR) $(GRUinfo) $(JOB_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) \ - $(FILE_ACCESS_ACTOR_INCLUDES) $(HRU_ACTOR_INCLUDES) $(GRU_ACTOR_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_summa_actor: - $(CC) $(FLAGS_ACTORS) -c $(SUMMA_ACTOR) $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) \ - $(JOB_ACTOR_INCLUDES) $(SUMMA_ACTOR_INCLUDES) - -compile_summa_client: - $(CC) $(FLAGS_ACTORS) -c $(SUMMA_CLIENT) $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) - -compile_summa_server: - $(CC) $(FLAGS_ACTORS) -c $(SUMMA_SERVER) $(SUMMA_BACKUP_SERVER) $(BATCH) $(CLIENT) $(BATCH_CONTAINER) $(CLIENT_CONTAINER) \ - $(SUMMA_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) - -compile_main: - $(CC) $(FLAGS_ACTORS) -c $(MAIN) $(GLOBAL_INCLUDES) $(SUMMA_ACTOR_INCLUDES) $(JOB_ACTOR_INCLUDES) - -link_cpp: - $(CC) $(FLAGS_ACTORS) -Wl,-rpath='$(ROOT_DIR)/bin:/usr/local/lib' -o summaMain *.o $(ACTORS_LIBRARIES) - mv summaMain $(ROOT_DIR)/bin - -clean_cpp: - rm *.o -################################################################################################################### -################################################ COMPILE SUMMA-C++ ################################################ -################################################################################################################### - - -################################################################################################################### -################################################## COMPILE TESTS ################################################## -################################################################################################################### -actors_test: - $(CC) $(FLAGS_ACTORS) -c $(ACTOR_TEST) -std=c++17 $(ACTORS_INCLUDES) - -actors_testLink: - $(CC) -o summaTest *.o $(ACTORS_LIBRARIES) - -clean_lib: - rm *.so - diff --git a/build/makefiles/Ngen/build_cmakeBMI b/build/makefiles/Ngen/build_cmakeBMI deleted file mode 100755 index 7c859f223..000000000 --- a/build/makefiles/Ngen/build_cmakeBMI +++ /dev/null @@ -1,5 +0,0 @@ -# from ../../bmi/builddir, run -# cp ../../summa/build/build_cmakeBMI build_cmake -# run script from the builddir directory with ./build_cmake - -cmake ../../bmi-fortran/ -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=../../bmi/instdir \ No newline at end of file diff --git a/build/makefiles/Ngen/compile_copernicus.sh b/build/makefiles/Ngen/compile_copernicus.sh deleted file mode 100755 index f818b6ced..000000000 --- a/build/makefiles/Ngen/compile_copernicus.sh +++ /dev/null @@ -1,17 +0,0 @@ -#! /bin/bash - -#### load modules if using Compute Canada or Copernicus #### -module load StdEnv/2020 -module load gcc/9.3.0 -module load openblas/0.3.17 -module load netcdf-fortran/4.5.2 - -#### parent directory of the 'build' directory #### -export ROOT_DIR=/project/gwf/gwf_cmt/ngen/ngen_code/ngen/extern/summa - -export INCLUDES="-I${EBROOTNETCDFMINFORTRAN}/include" -export LDFLAGS="-L${EBROOTNETCDFMINFORTRAN}/lib64 -L${EBROOTOPENBLAS}/lib" -export LIBRARIES="${LDFLAGS} -lnetcdff -lopenblas" - -make -f ${ROOT_DIR}/summa/build/makefiles/makefile_cluster -export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin diff --git a/build/makefiles/Ngen/compile_mac.sh b/build/makefiles/Ngen/compile_mac.sh deleted file mode 100755 index 4777dd2a3..000000000 --- a/build/makefiles/Ngen/compile_mac.sh +++ /dev/null @@ -1,11 +0,0 @@ -#! /bin/bash - -#### parent directory of the 'build' directory #### -export ROOT_DIR=/Users/amedin/Research/SummaBMI/ngen/extern/summa - -export INCLUDES="-I/opt/local/include -I/opt/local/lib" -export LDFLAGS="-L/opt/local/lib" -export LIBRARIES="${LDFLAGS} -llapack -lgfortran -lnetcdff -lnetcdf" - -make -f ${ROOT_DIR}/summa/build/makefiles/makefile_mac -export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin diff --git a/build/makefiles/Ngen/makefile_cluster b/build/makefiles/Ngen/makefile_cluster deleted file mode 100644 index fd8116566..000000000 --- a/build/makefiles/Ngen/makefile_cluster +++ /dev/null @@ -1,351 +0,0 @@ -# Define core directory below which everything resides. This is the -# parent directory of the 'build' directory -# ROOT_DIR = -F_MASTER = $(ROOT_DIR)/summa - -#### Compilers #### -FC = gfortran -FC_EXE = gfortran - -#### Includes AND Libraries #### -# INCLUDES = -# LDFLAGS = -# LIBRARIES = - -DIR_SUNDIALS= $(ROOT_DIR)/sundials/instdir -INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran -LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod - -DIR_BMI= $(ROOT_DIR)/bmi/instdir -INC_BMI=-I$(DIR_BMI)/include -LIB_BMI=-L$(DIR_BMI)/lib -lbmif - -# Production runs -FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors - -# Debug runs -# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC -# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC -# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC - -#======================================================================== -# PART 1: Define directory paths -#======================================================================== - -# Core directory that contains source code -F_KORE_DIR = $(F_MASTER)/build/source - -# Location of the compiled modules -MOD_PATH = $(F_MASTER)/build - -# Define the directory for the executables -EXE_PATH = $(F_MASTER)/bin - -#======================================================================== -# PART 2: Assemble all of the SUMMA sub-routines -#======================================================================== - -# Define directories -DRIVER_DIR = $(F_KORE_DIR)/driver -HOOKUP_DIR = $(F_KORE_DIR)/hookup -NETCDF_DIR = $(F_KORE_DIR)/netcdf -DSHARE_DIR = $(F_KORE_DIR)/dshare -NUMREC_DIR = $(F_KORE_DIR)/numrec -NOAHMP_DIR = $(F_KORE_DIR)/noah-mp -ENGINE_DIR = $(F_KORE_DIR)/engine - -# utilities -SUMMA_NRUTIL= \ - nrtype.f90 \ - f2008funcs.f90 \ - nr_utility.f90 -NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) - -# -# Numerical recipes procedures -# NOTE: all numerical recipes procedures are now replaced with free versions -SUMMA_NRPROC= \ - expIntegral.f90 \ - spline_int.f90 -NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) - -# Hook-up modules (set files and directory paths) -SUMMA_HOOKUP= \ - ascii_util.f90 \ - summaFileManager.f90 - -HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) - -# Data modules -SUMMA_DATAMS= \ - multiconst.f90 \ - var_lookup.f90 \ - data_types.f90 \ - globalData.f90 \ - flxMapping.f90 \ - get_ixname.f90 \ - popMetadat.f90 \ - outpt_stat.f90 -DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) - -# utility modules -SUMMA_UTILMS= \ - time_utils.f90 \ - mDecisions.f90 \ - snow_utils.f90 \ - soil_utils.f90 \ - soil_utilsAddPrime.f90 \ - updatState.f90 \ - updatStateWithPrime.f90 \ - matrixOper.f90 -UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) - -# Model guts -SUMMA_MODGUT= \ - MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) - -# Solver -SUMMA_SOLVER= \ - vegPhenlgy.f90 \ - diagn_evar.f90 \ - stomResist.f90 \ - groundwatr.f90 \ - vegSWavRad.f90 \ - vegNrgFlux.f90 \ - ssdNrgFlux.f90 \ - vegLiqFlux.f90 \ - snowLiqFlx.f90 \ - soilLiqFlx.f90 \ - bigAquifer.f90 \ - computFlux.f90 \ - type4ida.f90 \ - tol4ida.f90 \ - computEnthalpy.f90 \ - computHeatCap.f90 \ - computThermConduct.f90 \ - computResid.f90 \ - computJacob.f90 \ - eval8summa.f90 \ - summaSolve4numrec.f90 \ - systemSolv.f90 \ - computResidWithPrime.f90 \ - eval8summaWithPrime.f90 \ - computJacobWithPrime.f90 \ - computSnowDepth.f90 \ - summaSolve4ida.f90 \ - varSubstep.f90 \ - opSplittin.f90 \ - coupled_em.f90 \ - run_oneHRU.f90 \ - run_oneGRU.f90 -SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) - -# Define routines for SUMMA preliminaries -SUMMA_PRELIM= \ - conv_funcs.f90 \ - sunGeomtry.f90 \ - convE2Temp.f90 \ - allocspace.f90 \ - checkStruc.f90 \ - childStruc.f90 \ - ffile_info.f90 \ - read_attrb.f90 \ - read_pinit.f90 \ - pOverwrite.f90 \ - read_param.f90 \ - paramCheck.f90 \ - check_icond.f90 -PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) - -SUMMA_NOAHMP= \ - module_model_constants.F \ - module_sf_noahutl.F \ - module_sf_noahlsm.F \ - module_sf_noahmplsm.F -NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) - -# Define routines for the SUMMA model runs -SUMMA_MODRUN = \ - indexState.f90 \ - getVectorz.f90 \ - t2enthalpy.f90 \ - updateVars.f90 \ - updateVarsWithPrime.f90 \ - var_derive.f90 \ - read_force.f90 \ - derivforce.f90 \ - snowAlbedo.f90 \ - canopySnow.f90 \ - tempAdjust.f90 \ - snwCompact.f90 \ - layerMerge.f90 \ - layerDivide.f90 \ - volicePack.f90 \ - qTimeDelay.f90 -MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) - -# Define routines for the solver -SUMMA_MSOLVE = \ - -# Define NetCDF routines -SUMMA_NETCDF = \ - netcdf_util.f90 \ - def_output.f90 \ - modelwrite.f90 \ - read_icond.f90 -NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) - -# ... stitch together common programs -COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) - -# ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) - -# Define the driver routines -SUMMA_DRIVER= \ - summa_type.f90 \ - summa_util.f90 \ - summa_alarms.f90 \ - summa_globalData.f90 \ - summa_defineOutput.f90 \ - summa_init.f90 \ - summa_setup.f90 \ - summa_restart.f90 \ - summa_forcing.f90 \ - summa_modelRun.f90 \ - summa_writeOutput.f90 \ - summa_driver.f90 -DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) - -# Define SUMMA running infrastructure -SUMMA_RUN = \ - summa_run.f90 -SUMMA = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_RUN)) - -# Define BMI testing infrastructure -BMI_TEST = \ - summa_runBMI.f90 -BMI = $(patsubst %, $(DRIVER_DIR)/%, $(BMI_TEST)) - -# Define the executable -DRIVER__EX = summa_sundials.exe -BMI__EX = summa_bmi.exe - -# Define version number -VERSIONFILE = $(DRIVER_DIR)/summaversion.inc -VERSION = $(shell git tag | tail -n 1) -BULTTIM = $(shell date) -GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') -GITHASH = $(shell git rev-parse HEAD) - -#======================================================================== -# PART 3: Checks -#====================================================================== -# make sure that the paths are defined. These are just some high level checks -ifndef F_MASTER - $(error F_MASTER is undefined) -endif -ifndef FC - $(error FC is undefined: Specify your compiler) -endif -ifndef FC_EXE - $(error FC_EXE is undefined: Specify your compiler executable) -endif -ifndef FLAGS_SUMMA - $(error Specify flags for your compiler: $(FC)) -endif -ifndef INCLUDES - $(error INCLUDES is undefined) -endif -ifndef LIBRARIES - $(error LIBRARIES is undefined) -endif - -#======================================================================== -# PART 4: compilation -#====================================================================== - -# Compile -all: compile_noah compile_comm compile_rout compile_test link_test clean_test compile_summa link clean install install_test -part: compile_noah compile_comm compile_rout -summa: compile_noah compile_comm compile_rout compile_summa link clean install -bmi: compile_noah compile_comm compile_rout compile_test link_test clean install_test - - -check: - $(info) - $(info Displaying make variables:) - $(info F_MASTER : $(F_MASTER)) - $(info EXE_PATH : $(EXE_PATH)) - $(info FC : $(FC)) - $(info INCLUDES : $(INCLUDES)) - $(info LIBRARIES : $(LIBRARIES)) - $(info FLAGS_NOAH : $(FLAGS_NOAH)) - $(info FLAGS_COMM : $(FLAGS_COMM)) - $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) - $(info SUNDIALSDIR: $(DIR_SUNDIALS)) - $(info INC_SUNDIALS: $(INC_SUNDIALS)) - $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) - $(info BMIDIR: $(DIR_BMI)) - $(info INC_BMI: $(INC_BMI)) - $(info LIB_BMI: $(LIB_BMI)) - $(info) - -# update version information -update_version: - echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) - echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) - -# compile common routines -compile_comm: - $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) - -# compile Noah-MP routines -compile_noah: - $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) - -# compile SUMMA routines -compile_rout: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) - -# compile run scripts -compile_summa: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA) - -# compile test scripts -compile_test: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(BMI) - -# link run routines -link: - $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(DRIVER__EX) - -# link test routines -link_test: - $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(BMI__EX) - -# remove test script objects -clean_test: - rm -f summa_runBMI.o - -# Remove object files -clean: - rm -f *.o - rm -f *.mod - rm -f soil_veg_gen_parm__genmod.f90 - -# Copy the executable to the bin directory -install: - @mkdir -p $(EXE_PATH) - @mv $(DRIVER__EX) $(EXE_PATH) - $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) - -# Copy the executable to the bin directory -install_test: - @mkdir -p $(EXE_PATH) - @mv $(BMI__EX) $(EXE_PATH) - $(info $(BMI__EX) successfully installed in $(EXE_PATH)) diff --git a/build/makefiles/Ngen/makefile_mac b/build/makefiles/Ngen/makefile_mac deleted file mode 100644 index 7b3d88730..000000000 --- a/build/makefiles/Ngen/makefile_mac +++ /dev/null @@ -1,351 +0,0 @@ -# Define core directory below which everything resides. This is the -# parent directory of the 'build' directory -# ROOT_DIR = -F_MASTER = $(ROOT_DIR)/summa - -#### Compilers #### -FC = gfortran -FC_EXE =/opt/local/bin/gfortran - -#### Includes AND Libraries #### -# INCLUDES = -# LDFLAGS = -# LIBRARIES = - -DIR_SUNDIALS= $(ROOT_DIR)/sundials/instdir -INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran -LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod - -DIR_BMI= $(ROOT_DIR)/bmi/instdir -INC_BMI=-I$(DIR_BMI)/include -LIB_BMI=-L$(DIR_BMI)/lib -lbmif -Xlinker -rpath -Xlinker $(DIR_BMI)/lib - -# Production runs -FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors - -# Debug runs -# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC -# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC -# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC - -#======================================================================== -# PART 1: Define directory paths -#======================================================================== - -# Core directory that contains source code -F_KORE_DIR = $(F_MASTER)/build/source - -# Location of the compiled modules -MOD_PATH = $(F_MASTER)/build - -# Define the directory for the executables -EXE_PATH = $(F_MASTER)/bin - -#======================================================================== -# PART 2: Assemble all of the SUMMA sub-routines -#======================================================================== - -# Define directories -DRIVER_DIR = $(F_KORE_DIR)/driver -HOOKUP_DIR = $(F_KORE_DIR)/hookup -NETCDF_DIR = $(F_KORE_DIR)/netcdf -DSHARE_DIR = $(F_KORE_DIR)/dshare -NUMREC_DIR = $(F_KORE_DIR)/numrec -NOAHMP_DIR = $(F_KORE_DIR)/noah-mp -ENGINE_DIR = $(F_KORE_DIR)/engine - -# utilities -SUMMA_NRUTIL= \ - nrtype.f90 \ - f2008funcs.f90 \ - nr_utility.f90 -NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) - -# -# Numerical recipes procedures -# NOTE: all numerical recipes procedures are now replaced with free versions -SUMMA_NRPROC= \ - expIntegral.f90 \ - spline_int.f90 -NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) - -# Hook-up modules (set files and directory paths) -SUMMA_HOOKUP= \ - ascii_util.f90 \ - summaFileManager.f90 - -HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) - -# Data modules -SUMMA_DATAMS= \ - multiconst.f90 \ - var_lookup.f90 \ - data_types.f90 \ - globalData.f90 \ - flxMapping.f90 \ - get_ixname.f90 \ - popMetadat.f90 \ - outpt_stat.f90 -DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) - -# utility modules -SUMMA_UTILMS= \ - time_utils.f90 \ - mDecisions.f90 \ - snow_utils.f90 \ - soil_utils.f90 \ - soil_utilsAddPrime.f90 \ - updatState.f90 \ - updatStateWithPrime.f90 \ - matrixOper.f90 -UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) - -# Model guts -SUMMA_MODGUT= \ - MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) - -# Solver -SUMMA_SOLVER= \ - vegPhenlgy.f90 \ - diagn_evar.f90 \ - stomResist.f90 \ - groundwatr.f90 \ - vegSWavRad.f90 \ - vegNrgFlux.f90 \ - ssdNrgFlux.f90 \ - vegLiqFlux.f90 \ - snowLiqFlx.f90 \ - soilLiqFlx.f90 \ - bigAquifer.f90 \ - computFlux.f90 \ - type4ida.f90 \ - tol4ida.f90 \ - computEnthalpy.f90 \ - computHeatCap.f90 \ - computThermConduct.f90 \ - computResid.f90 \ - computJacob.f90 \ - eval8summa.f90 \ - summaSolve4numrec.f90 \ - systemSolv.f90 \ - computResidWithPrime.f90 \ - eval8summaWithPrime.f90 \ - computJacobWithPrime.f90 \ - computSnowDepth.f90 \ - summaSolve4ida.f90 \ - varSubstep.f90 \ - opSplittin.f90 \ - coupled_em.f90 \ - run_oneHRU.f90 \ - run_oneGRU.f90 -SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) - -# Define routines for SUMMA preliminaries -SUMMA_PRELIM= \ - conv_funcs.f90 \ - sunGeomtry.f90 \ - convE2Temp.f90 \ - allocspace.f90 \ - checkStruc.f90 \ - childStruc.f90 \ - ffile_info.f90 \ - read_attrb.f90 \ - read_pinit.f90 \ - pOverwrite.f90 \ - read_param.f90 \ - paramCheck.f90 \ - check_icond.f90 -PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) - -SUMMA_NOAHMP= \ - module_model_constants.F \ - module_sf_noahutl.F \ - module_sf_noahlsm.F \ - module_sf_noahmplsm.F -NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) - -# Define routines for the SUMMA model runs -SUMMA_MODRUN = \ - indexState.f90 \ - getVectorz.f90 \ - t2enthalpy.f90 \ - updateVars.f90 \ - updateVarsWithPrime.f90 \ - var_derive.f90 \ - read_force.f90 \ - derivforce.f90 \ - snowAlbedo.f90 \ - canopySnow.f90 \ - tempAdjust.f90 \ - snwCompact.f90 \ - layerMerge.f90 \ - layerDivide.f90 \ - volicePack.f90 \ - qTimeDelay.f90 -MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) - -# Define routines for the solver -SUMMA_MSOLVE = \ - -# Define NetCDF routines -SUMMA_NETCDF = \ - netcdf_util.f90 \ - def_output.f90 \ - modelwrite.f90 \ - read_icond.f90 -NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) - -# ... stitch together common programs -COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) - -# ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) - -# Define the driver routines -SUMMA_DRIVER= \ - summa_type.f90 \ - summa_util.f90 \ - summa_alarms.f90 \ - summa_globalData.f90 \ - summa_defineOutput.f90 \ - summa_init.f90 \ - summa_setup.f90 \ - summa_restart.f90 \ - summa_forcing.f90 \ - summa_modelRun.f90 \ - summa_writeOutput.f90 \ - summa_driver.f90 -DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) - -# Define SUMMA running infrastructure -SUMMA_RUN = \ - summa_run.f90 -SUMMA = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_RUN)) - -# Define BMI testing infrastructure -BMI_TEST = \ - summa_runBMI.f90 -BMI = $(patsubst %, $(DRIVER_DIR)/%, $(BMI_TEST)) - -# Define the executable -DRIVER__EX = summa_sundials.exe -BMI__EX = summa_bmi.exe - -# Define version number -VERSIONFILE = $(DRIVER_DIR)/summaversion.inc -VERSION = $(shell git tag | tail -n 1) -BULTTIM = $(shell date) -GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') -GITHASH = $(shell git rev-parse HEAD) - -#======================================================================== -# PART 3: Checks -#====================================================================== -# make sure that the paths are defined. These are just some high level checks -ifndef F_MASTER - $(error F_MASTER is undefined) -endif -ifndef FC - $(error FC is undefined: Specify your compiler) -endif -ifndef FC_EXE - $(error FC_EXE is undefined: Specify your compiler executable) -endif -ifndef FLAGS_SUMMA - $(error Specify flags for your compiler: $(FC)) -endif -ifndef INCLUDES - $(error INCLUDES is undefined) -endif -ifndef LIBRARIES - $(error LIBRARIES is undefined) -endif - -#======================================================================== -# PART 4: compilation -#====================================================================== - -# Compile -all: compile_noah compile_comm compile_rout compile_test link_test clean_test compile_summa link clean install install_test -part: compile_noah compile_comm compile_rout -summa: compile_noah compile_comm compile_rout compile_summa link clean install -bmi: compile_noah compile_comm compile_rout compile_test link_test clean install_test - - -check: - $(info) - $(info Displaying make variables:) - $(info F_MASTER : $(F_MASTER)) - $(info EXE_PATH : $(EXE_PATH)) - $(info FC : $(FC)) - $(info INCLUDES : $(INCLUDES)) - $(info LIBRARIES : $(LIBRARIES)) - $(info FLAGS_NOAH : $(FLAGS_NOAH)) - $(info FLAGS_COMM : $(FLAGS_COMM)) - $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) - $(info SUNDIALSDIR: $(DIR_SUNDIALS)) - $(info INC_SUNDIALS: $(INC_SUNDIALS)) - $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) - $(info BMIDIR: $(DIR_BMI)) - $(info INC_BMI: $(INC_BMI)) - $(info LIB_BMI: $(LIB_BMI)) - $(info) - -# update version information -update_version: - echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) - echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) - -# compile common routines -compile_comm: - $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) - -# compile Noah-MP routines -compile_noah: - $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) - -# compile SUMMA routines -compile_rout: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) $(INCLUDES) $(INC_SUNDIALS) $(INC_BMI) - -# compile run scripts -compile_summa: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA) - -# compile test scripts -compile_test: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(BMI) - -# link run routines -link: - $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(DRIVER__EX) - -# link test routines -link_test: - $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) $(LIB_BMI) -o $(BMI__EX) - -# remove test script objects -clean_test: - rm -f summa_runBMI.o - -# Remove object files -clean: - rm -f *.o - rm -f *.mod - rm -f soil_veg_gen_parm__genmod.f90 - -# Copy the executable to the bin directory -install: - @mkdir -p $(EXE_PATH) - @mv $(DRIVER__EX) $(EXE_PATH) - $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) - -# Copy the executable to the bin directory -install_test: - @mkdir -p $(EXE_PATH) - @mv $(BMI__EX) $(EXE_PATH) - $(info $(BMI__EX) successfully installed in $(EXE_PATH)) diff --git a/build/makefiles/Sundials/compile_copernicus.sh b/build/makefiles/Sundials/compile_copernicus.sh deleted file mode 100755 index a54c13f40..000000000 --- a/build/makefiles/Sundials/compile_copernicus.sh +++ /dev/null @@ -1,17 +0,0 @@ -#! /bin/bash - -#### load modules if using Compute Canada or Copernicus #### -module load StdEnv/2020 -module load gcc/9.3.0 -module load openblas/0.3.17 -module load netcdf-fortran/4.5.2 - -#### parent directory of the 'build' directory #### -export ROOT_DIR=/globalhome/gwu479/HPC/SummaSundials - -export INCLUDES="-I${EBROOTNETCDFMINFORTRAN}/include" -export LDFLAGS="-L${EBROOTNETCDFMINFORTRAN}/lib64 -L${EBROOTOPENBLAS}/lib" -export LIBRARIES="${LDFLAGS} -lnetcdff -lopenblas" - -make -f ${ROOT_DIR}/summa/build/makefiles/makefile_cluster -export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin diff --git a/build/makefiles/Sundials/compile_graham.sh b/build/makefiles/Sundials/compile_graham.sh deleted file mode 100755 index 545353682..000000000 --- a/build/makefiles/Sundials/compile_graham.sh +++ /dev/null @@ -1,17 +0,0 @@ -#! /bin/bash - -#### load modules if using Compute Canada or Copernicus #### -module load StdEnv/2020 -module load gcc/9.3.0 -module load openblas/0.3.17 -module load netcdf-fortran/4.5.2 - -#### parent directory of the 'build' directory #### -export ROOT_DIR=/home/avanb/SummaSundials - -export INCLUDES="-I${EBROOTNETCDFMINFORTRAN}/include" -export LDFLAGS="-L${EBROOTNETCDFMINFORTRAN}/lib64 -L${EBROOTOPENBLAS}/lib" -export LIBRARIES="${LDFLAGS} -lnetcdff -lopenblas" - -make -f ${ROOT_DIR}/summa/build/makefiles/makefile_cluster -export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin diff --git a/build/makefiles/Sundials/compile_mac.sh b/build/makefiles/Sundials/compile_mac.sh deleted file mode 100755 index f1a58cd8c..000000000 --- a/build/makefiles/Sundials/compile_mac.sh +++ /dev/null @@ -1,11 +0,0 @@ -#! /bin/bash - -#### parent directory of the 'build' directory #### -export ROOT_DIR=/Users/amedin/Research/SummaSundials - -export INCLUDES="-I/opt/local/include -I/opt/local/lib" -export LDFLAGS="-L/opt/local/lib" -export LIBRARIES="${LDFLAGS} -llapack -lgfortran -lnetcdff -lnetcdf" - -make -f ${ROOT_DIR}/summa/build/makefiles/makefile_mac -export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin diff --git a/build/makefiles/Sundials/compile_richardson.sh b/build/makefiles/Sundials/compile_richardson.sh deleted file mode 100755 index 34bf5778f..000000000 --- a/build/makefiles/Sundials/compile_richardson.sh +++ /dev/null @@ -1,11 +0,0 @@ -#! /bin/bash - -#### parent directory of the 'build' directory #### -export ROOT_DIR=/u1/gwu479/SummaSundials - -export INCLUDES="-I/usr/include -I/usr/local/include" -export LDFLAGS="-L/usr/local/lib" -export LIBRARIES="${LDFLAGS} -lnetcdff -lopenblas -lnetcdf" - -make -f ${ROOT_DIR}/summa/build/makefiles/makefile_cluster -export LD_LIBRARY_PATH=${ROOT_DIR}/summa/bin diff --git a/build/makefiles/Sundials/makefile_cluster b/build/makefiles/Sundials/makefile_cluster deleted file mode 100644 index a074f0b6d..000000000 --- a/build/makefiles/Sundials/makefile_cluster +++ /dev/null @@ -1,310 +0,0 @@ -# Define core directory below which everything resides. This is the -# parent directory of the 'build' directory -# ROOT_DIR = -F_MASTER = $(ROOT_DIR)/summa - -#### Compilers #### -FC = gfortran -FC_EXE = gfortran - -#### Includes AND Libraries #### -# INCLUDES = -# LDFLAGS = -# LIBRARIES = - -DIR_SUNDIALS= $(ROOT_DIR)/sundials/instdir -INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran -LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib64 -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod - -# Production runs -FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors - -# Debug runs -# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC -# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC -# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC - -#======================================================================== -# PART 1: Define directory paths -#======================================================================== - -# Core directory that contains source code -F_KORE_DIR = $(F_MASTER)/build/source - -# Location of the compiled modules -MOD_PATH = $(F_MASTER)/build - -# Define the directory for the executables -EXE_PATH = $(F_MASTER)/bin - -#======================================================================== -# PART 2: Assemble all of the SUMMA sub-routines -#======================================================================== - -# Define directories -DRIVER_DIR = $(F_KORE_DIR)/driver -HOOKUP_DIR = $(F_KORE_DIR)/hookup -NETCDF_DIR = $(F_KORE_DIR)/netcdf -DSHARE_DIR = $(F_KORE_DIR)/dshare -NUMREC_DIR = $(F_KORE_DIR)/numrec -NOAHMP_DIR = $(F_KORE_DIR)/noah-mp -ENGINE_DIR = $(F_KORE_DIR)/engine - -# utilities -SUMMA_NRUTIL= \ - nrtype.f90 \ - f2008funcs.f90 \ - nr_utility.f90 -NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) - -# -# Numerical recipes procedures -# NOTE: all numerical recipes procedures are now replaced with free versions -SUMMA_NRPROC= \ - expIntegral.f90 \ - spline_int.f90 -NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) - -# Hook-up modules (set files and directory paths) -SUMMA_HOOKUP= \ - ascii_util.f90 \ - summaFileManager.f90 - -HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) - -# Data modules -SUMMA_DATAMS= \ - multiconst.f90 \ - var_lookup.f90 \ - data_types.f90 \ - globalData.f90 \ - flxMapping.f90 \ - get_ixname.f90 \ - popMetadat.f90 \ - outpt_stat.f90 -DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) - -# utility modules -SUMMA_UTILMS= \ - time_utils.f90 \ - mDecisions.f90 \ - snow_utils.f90 \ - soil_utils.f90 \ - soil_utilsAddPrime.f90 \ - updatState.f90 \ - updatStateWithPrime.f90 \ - matrixOper.f90 -UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) - -# Model guts -SUMMA_MODGUT= \ - MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) - -# Solver -SUMMA_SOLVER= \ - vegPhenlgy.f90 \ - diagn_evar.f90 \ - stomResist.f90 \ - groundwatr.f90 \ - vegSWavRad.f90 \ - vegNrgFlux.f90 \ - ssdNrgFlux.f90 \ - vegLiqFlux.f90 \ - snowLiqFlx.f90 \ - soilLiqFlx.f90 \ - bigAquifer.f90 \ - computFlux.f90 \ - type4ida.f90 \ - tol4ida.f90 \ - computEnthalpy.f90 \ - computHeatCap.f90 \ - computThermConduct.f90 \ - computResid.f90 \ - computJacob.f90 \ - eval8summa.f90 \ - summaSolve4numrec.f90 \ - systemSolv.f90 \ - computResidWithPrime.f90 \ - eval8summaWithPrime.f90 \ - computJacobWithPrime.f90 \ - computSnowDepth.f90 \ - summaSolve4ida.f90 \ - varSubstep.f90 \ - opSplittin.f90 \ - coupled_em.f90 \ - run_oneHRU.f90 \ - run_oneGRU.f90 -SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) - -# Define routines for SUMMA preliminaries -SUMMA_PRELIM= \ - conv_funcs.f90 \ - sunGeomtry.f90 \ - convE2Temp.f90 \ - allocspace.f90 \ - checkStruc.f90 \ - childStruc.f90 \ - ffile_info.f90 \ - read_attrb.f90 \ - read_pinit.f90 \ - pOverwrite.f90 \ - read_param.f90 \ - paramCheck.f90 \ - check_icond.f90 -PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) - -SUMMA_NOAHMP= \ - module_model_constants.F \ - module_sf_noahutl.F \ - module_sf_noahlsm.F \ - module_sf_noahmplsm.F -NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) - -# Define routines for the SUMMA model runs -SUMMA_MODRUN = \ - indexState.f90 \ - getVectorz.f90 \ - t2enthalpy.f90 \ - updateVars.f90 \ - updateVarsWithPrime.f90 \ - var_derive.f90 \ - read_force.f90 \ - derivforce.f90 \ - snowAlbedo.f90 \ - canopySnow.f90 \ - tempAdjust.f90 \ - snwCompact.f90 \ - layerMerge.f90 \ - layerDivide.f90 \ - volicePack.f90 \ - qTimeDelay.f90 -MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) - -# Define routines for the solver -SUMMA_MSOLVE = \ - -# Define NetCDF routines -SUMMA_NETCDF = \ - netcdf_util.f90 \ - def_output.f90 \ - modelwrite.f90 \ - read_icond.f90 -NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) - -# ... stitch together common programs -COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) - -# ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) - -# Define the driver routine -SUMMA_DRIVER= \ - summa_type.f90 \ - summa_util.f90 \ - summa_alarms.f90 \ - summa_globalData.f90 \ - summa_defineOutput.f90 \ - summa_init.f90 \ - summa_setup.f90 \ - summa_restart.f90 \ - summa_forcing.f90 \ - summa_modelRun.f90 \ - summa_writeOutput.f90 \ - summa_driver.f90 -DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) - -# Define the executable -DRIVER__EX = summa_sundials.exe - -# Define version number -VERSIONFILE = $(DRIVER_DIR)/summaversion.inc -VERSION = $(shell git tag | tail -n 1) -BULTTIM = $(shell date) -GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') -GITHASH = $(shell git rev-parse HEAD) - -#======================================================================== -# PART 3: Checks -#====================================================================== -# make sure that the paths are defined. These are just some high level checks -ifndef F_MASTER - $(error F_MASTER is undefined) -endif -ifndef FC - $(error FC is undefined: Specify your compiler) -endif -ifndef FC_EXE - $(error FC_EXE is undefined: Specify your compiler executable) -endif -ifndef FLAGS_SUMMA - $(error Specify flags for your compiler: $(FC)) -endif -ifndef INCLUDES - $(error INCLUDES is undefined) -endif -ifndef LIBRARIES - $(error LIBRARIES is undefined) -endif - -#======================================================================== -# PART 4: compilation -#====================================================================== - -# Compile -all: compile_noah compile_comm compile_summa link clean install -part: compile_noah compile_comm compile_summa - -check: - $(info) - $(info Displaying make variables:) - $(info F_MASTER : $(F_MASTER)) - $(info EXE_PATH : $(EXE_PATH)) - $(info FC : $(FC)) - $(info INCLUDES : $(INCLUDES)) - $(info LIBRARIES : $(LIBRARIES)) - $(info FLAGS_NOAH : $(FLAGS_NOAH)) - $(info FLAGS_COMM : $(FLAGS_COMM)) - $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) - $(info SUNDIALSDIR: $(DIR_SUNDIALS)) - $(info INC_SUNDIALS: $(INC_SUNDIALS)) - $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) - $(info) - -# update version information -update_version: - echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) - echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) - -# compile Noah-MP routines -compile_noah: - $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) - -# compile common routines - -compile_comm: - $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) - -# compile SUMMA routines -compile_summa: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ - $(INCLUDES) $(INC_SUNDIALS) - -# link routines -link: - $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) - -# Remove object files -clean: - rm -f *.o - rm -f *.mod - rm -f soil_veg_gen_parm__genmod.f90 - -# Copy the executable to the bin directory -install: - @mkdir -p $(EXE_PATH) - @mv $(DRIVER__EX) $(EXE_PATH) - $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) diff --git a/build/makefiles/Sundials/makefile_mac b/build/makefiles/Sundials/makefile_mac deleted file mode 100644 index f72cfc68c..000000000 --- a/build/makefiles/Sundials/makefile_mac +++ /dev/null @@ -1,309 +0,0 @@ -# Define core directory below which everything resides. This is the -# parent directory of the 'build' directory -# ROOT_DIR = -F_MASTER = $(ROOT_DIR)/summa - -#### Compilers #### -FC = gfortran -FC_EXE =/opt/local/bin/gfortran - -#### Includes AND Libraries #### -# INCLUDES = -# LDFLAGS = -# LIBRARIES = - -DIR_SUNDIALS= $(ROOT_DIR)/sundials/instdir -INC_SUNDIALS=-I$(DIR_SUNDIALS)/include -I$(DIR_SUNDIALS)/fortran -LIB_SUNDIALS=-L$(DIR_SUNDIALS)/lib -lsundials_fnvecmanyvector_mod -lsundials_fida_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod - -# Production runs -FLAGS_NOAH = -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_COMM = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors - -# Debug runs -# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC -# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC -# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC - -#======================================================================== -# PART 1: Define directory paths -#======================================================================== - -# Core directory that contains source code -F_KORE_DIR = $(F_MASTER)/build/source - -# Location of the compiled modules -MOD_PATH = $(F_MASTER)/build - -# Define the directory for the executables -EXE_PATH = $(F_MASTER)/bin - -#======================================================================== -# PART 2: Assemble all of the SUMMA sub-routines -#======================================================================== - -# Define directories -DRIVER_DIR = $(F_KORE_DIR)/driver -HOOKUP_DIR = $(F_KORE_DIR)/hookup -NETCDF_DIR = $(F_KORE_DIR)/netcdf -DSHARE_DIR = $(F_KORE_DIR)/dshare -NUMREC_DIR = $(F_KORE_DIR)/numrec -NOAHMP_DIR = $(F_KORE_DIR)/noah-mp -ENGINE_DIR = $(F_KORE_DIR)/engine - -# utilities -SUMMA_NRUTIL= \ - nrtype.f90 \ - f2008funcs.f90 \ - nr_utility.f90 -NRUTIL = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRUTIL)) - -# -# Numerical recipes procedures -# NOTE: all numerical recipes procedures are now replaced with free versions -SUMMA_NRPROC= \ - expIntegral.f90 \ - spline_int.f90 -NRPROC = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_NRPROC)) - -# Hook-up modules (set files and directory paths) -SUMMA_HOOKUP= \ - ascii_util.f90 \ - summaFileManager.f90 - -HOOKUP = $(patsubst %, $(HOOKUP_DIR)/%, $(SUMMA_HOOKUP)) - -# Data modules -SUMMA_DATAMS= \ - multiconst.f90 \ - var_lookup.f90 \ - data_types.f90 \ - globalData.f90 \ - flxMapping.f90 \ - get_ixname.f90 \ - popMetadat.f90 \ - outpt_stat.f90 -DATAMS = $(patsubst %, $(DSHARE_DIR)/%, $(SUMMA_DATAMS)) - -# utility modules -SUMMA_UTILMS= \ - time_utils.f90 \ - mDecisions.f90 \ - snow_utils.f90 \ - soil_utils.f90 \ - soil_utilsAddPrime.f90 \ - updatState.f90 \ - updatStateWithPrime.f90 \ - matrixOper.f90 -UTILMS = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_UTILMS)) - -# Model guts -SUMMA_MODGUT= \ - MODGUT = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODGUT)) - -# Solver -SUMMA_SOLVER= \ - vegPhenlgy.f90 \ - diagn_evar.f90 \ - stomResist.f90 \ - groundwatr.f90 \ - vegSWavRad.f90 \ - vegNrgFlux.f90 \ - ssdNrgFlux.f90 \ - vegLiqFlux.f90 \ - snowLiqFlx.f90 \ - soilLiqFlx.f90 \ - bigAquifer.f90 \ - computFlux.f90 \ - type4ida.f90 \ - tol4ida.f90 \ - computEnthalpy.f90 \ - computHeatCap.f90 \ - computThermConduct.f90 \ - computResid.f90 \ - computJacob.f90 \ - eval8summa.f90 \ - summaSolve4numrec.f90 \ - systemSolv.f90 \ - computResidWithPrime.f90 \ - eval8summaWithPrime.f90 \ - computJacobWithPrime.f90 \ - computSnowDepth.f90 \ - summaSolve4ida.f90 \ - varSubstep.f90 \ - opSplittin.f90 \ - coupled_em.f90 \ - run_oneHRU.f90 \ - run_oneGRU.f90 -SOLVER = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_SOLVER)) - -# Define routines for SUMMA preliminaries -SUMMA_PRELIM= \ - conv_funcs.f90 \ - sunGeomtry.f90 \ - convE2Temp.f90 \ - allocspace.f90 \ - checkStruc.f90 \ - childStruc.f90 \ - ffile_info.f90 \ - read_attrb.f90 \ - read_pinit.f90 \ - pOverwrite.f90 \ - read_param.f90 \ - paramCheck.f90 \ - check_icond.f90 -PRELIM = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_PRELIM)) - -SUMMA_NOAHMP= \ - module_model_constants.F \ - module_sf_noahutl.F \ - module_sf_noahlsm.F \ - module_sf_noahmplsm.F -NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) - -# Define routines for the SUMMA model runs -SUMMA_MODRUN = \ - indexState.f90 \ - getVectorz.f90 \ - t2enthalpy.f90 \ - updateVars.f90 \ - updateVarsWithPrime.f90 \ - var_derive.f90 \ - read_force.f90 \ - derivforce.f90 \ - snowAlbedo.f90 \ - canopySnow.f90 \ - tempAdjust.f90 \ - snwCompact.f90 \ - layerMerge.f90 \ - layerDivide.f90 \ - volicePack.f90 \ - qTimeDelay.f90 -MODRUN = $(patsubst %, $(ENGINE_DIR)/%, $(SUMMA_MODRUN)) - -# Define routines for the solver -SUMMA_MSOLVE = \ - -# Define NetCDF routines -SUMMA_NETCDF = \ - netcdf_util.f90 \ - def_output.f90 \ - modelwrite.f90 \ - read_icond.f90 -NETCDF = $(patsubst %, $(NETCDF_DIR)/%, $(SUMMA_NETCDF)) - -# ... stitch together common programs -COMM_ALL = $(NRUTIL) $(NRPROC) $(HOOKUP) $(DATAMS) $(UTILMS) - -# ... stitch together SUMMA programs -SUMMA_ALL = $(NETCDF) $(PRELIM) $(MODRUN) $(SOLVER) - -# Define the driver routine -SUMMA_DRIVER= \ - summa_type.f90 \ - summa_util.f90 \ - summa_alarms.f90 \ - summa_globalData.f90 \ - summa_defineOutput.f90 \ - summa_init.f90 \ - summa_setup.f90 \ - summa_restart.f90 \ - summa_forcing.f90 \ - summa_modelRun.f90 \ - summa_writeOutput.f90 \ - summa_driver.f90 -DRIVER = $(patsubst %, $(DRIVER_DIR)/%, $(SUMMA_DRIVER)) - -# Define the executable -DRIVER__EX = summa_sundials.exe - -# Define version number -VERSIONFILE = $(DRIVER_DIR)/summaversion.inc -VERSION = $(shell git tag | tail -n 1) -BULTTIM = $(shell date) -GITBRCH = $(shell git describe --long --all --always | sed -e's/heads\///') -GITHASH = $(shell git rev-parse HEAD) - -#======================================================================== -# PART 3: Checks -#====================================================================== -# make sure that the paths are defined. These are just some high level checks -ifndef F_MASTER - $(error F_MASTER is undefined) -endif -ifndef FC - $(error FC is undefined: Specify your compiler) -endif -ifndef FC_EXE - $(error FC_EXE is undefined: Specify your compiler executable) -endif -ifndef FLAGS_SUMMA - $(error Specify flags for your compiler: $(FC)) -endif -ifndef INCLUDES - $(error INCLUDES is undefined) -endif -ifndef LIBRARIES - $(error LIBRARIES is undefined) -endif - -#======================================================================== -# PART 4: compilation -#====================================================================== - -# Compile -all: compile_noah compile_comm compile_summa link clean install -part: compile_noah compile_comm compile_summa - -check: - $(info) - $(info Displaying make variables:) - $(info F_MASTER : $(F_MASTER)) - $(info EXE_PATH : $(EXE_PATH)) - $(info FC : $(FC)) - $(info INCLUDES : $(INCLUDES)) - $(info LIBRARIES : $(LIBRARIES)) - $(info FLAGS_NOAH : $(FLAGS_NOAH)) - $(info FLAGS_COMM : $(FLAGS_COMM)) - $(info FLAGS_SUMMA: $(FLAGS_SUMMA)) - $(info SUNDIALSDIR: $(DIR_SUNDIALS)) - $(info INC_SUNDIALS: $(INC_SUNDIALS)) - $(info LIB_SUNDIALS: $(LIB_SUNDIALS)) - $(info) - -# update version information -update_version: - echo "character(len=64), parameter :: summaVersion = '${VERSION}'" > $(VERSIONFILE) - echo "character(len=64), parameter :: buildTime = '${BULTTIM}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitBranch = '${GITBRCH}'" >> $(VERSIONFILE) - echo "character(len=64), parameter :: gitHash = '${GITHASH}'" >> $(VERSIONFILE) - -# compile Noah-MP routines -compile_noah: - $(FC_EXE) $(FLAGS_NOAH) -c $(NRUTIL) $(NOAHMP) - -# compile common routines -compile_comm: - $(FC_EXE) $(FLAGS_COMM) -c $(COMM_ALL) $(INCLUDES) - -# compile SUMMA routines -compile_summa: update_version - $(FC_EXE) $(FLAGS_SUMMA) -c $(SUMMA_ALL) $(DRIVER) \ - $(INCLUDES) $(INC_SUNDIALS) - -# link routines -link: - $(FC_EXE) -g *.o $(LIBRARIES) $(LIB_SUNDIALS) -o $(DRIVER__EX) - -# Remove object files -clean: - rm -f *.o - rm -f *.mod - rm -f soil_veg_gen_parm__genmod.f90 - -# Copy the executable to the bin directory -install: - @mkdir -p $(EXE_PATH) - @mv $(DRIVER__EX) $(EXE_PATH) - $(info $(DRIVER__EX) successfully installed in $(EXE_PATH)) From c23acc61f34eae9422f3d0da40395664abde1140 Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 25 Aug 2023 04:48:53 -0600 Subject: [PATCH 0885/1472] Added iLook_op derived type and iLookOP variable in var_lookup.f90. --- build/source/dshare/var_lookup.f90 | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 07641f7db..e319db30e 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -847,6 +847,14 @@ MODULE var_lookup integer(i4b) :: bigAquifer = integerMissing ! bigAquifer end type iLook_routine + ! *********************************************************************************************************** + ! (18) structure for looking up operation argument for subTools subroutine + ! *********************************************************************************************************** + type, public :: iLook_op + integer(i4b) :: pre = integerMissing ! pre-processing + integer(i4b) :: post = integerMissing ! post-processing + end type iLook_op + ! *********************************************************************************************************** ! (X) define data structures and maximum number of variables of each type ! *********************************************************************************************************** @@ -940,7 +948,9 @@ MODULE var_lookup ! named variables in the lookup table structure type(iLook_vLookup), public,parameter :: iLookLOOKUP =ilook_vLookup ( 1, 2, 3) ! named variables: flux subroutines - type(iLook_routine) ,public,parameter :: iLookROUTINE =iLook_routine ( 1, 2, 3, 4, 5, 6, 7) + type(iLook_routine), public,parameter :: iLookROUTINE =iLook_routine ( 1, 2, 3, 4, 5, 6, 7) + ! named variables: operation arguments for subTools subroutine + type(iLook_op), public,parameter :: iLookOP =iLook_op ( 1, 2) ! define maximum number of variables of each type integer(i4b),parameter,public :: maxvarDecisions = storage_size(iLookDECISIONS)/iLength integer(i4b),parameter,public :: maxvarTime = storage_size(iLookTIME)/iLength From c1769e358801b1cc9542071f7ca0ef4b4f30e4a5 Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 25 Aug 2023 04:57:24 -0600 Subject: [PATCH 0886/1472] Added subTools subroutine to computFlux.f90. --- build/source/engine/computFlux.f90 | 201 ++++++++++++++++++++++++++++- 1 file changed, 196 insertions(+), 5 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 4e925e7fe..583d02dee 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -29,7 +29,8 @@ module computFlux_module var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (rkind) - model_options ! defines the model decisions + model_options,& ! defines the model decisions + data_bin ! x%bin(:)%{lgt(:),i4b(:),rkind(:),rmatrix(:,:),string}, x%err [i4b], x%msg [character] ! indices that define elements of the data structures USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure @@ -40,6 +41,8 @@ module computFlux_module USE var_lookup,only:iLookDIAG ! named variables for structure elements USE var_lookup,only:iLookFLUX ! named variables for structure elements USE var_lookup,only:iLookDERIV ! named variables for structure elements +USE var_lookup,only:iLookROUTINE ! named variables for structure elements +USE var_lookup,only:iLookOP ! named variables for structure elements ! missing values USE globalData,only:integerMissing ! missing integer @@ -889,11 +892,199 @@ subroutine computFlux(& ! compute the flux vector for the aquifer if (ixAqWat/=integerMissing) fluxVec(ixAqWat) = scalarAquiferTranspire + scalarAquiferRecharge - scalarAquiferBaseflow - ! set the first flux call to false - firstFluxCall=.false. + firstFluxCall=.false. ! set the first flux call to false - ! end association to variables in the data structures - end associate + end associate ! end association to variables in the data structures + +contains + + subroutine subTools(op,sub) + implicit none + integer(i4b),intent(in) :: op ! index of requested operation: iLookOP%pre for pre-processing or iLookOP%post for post-processing + integer(i4b),intent(in) :: sub ! index of subroutine in computFlux (e.g., iLookROUTINE%vegNrgFlux for vegNrgFlux routine) + + associate(& + ! model decisions + ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization + ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) + ! domain boundary conditions + upperBoundTemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) + scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in): [dp] rainfall rate (kg m-2 s-1) + ! canopy and layer depth + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! indices of model state variables for the vegetation subdomain + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain + ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer + ! indices of model state variables for the snow+soil domain + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] type of layer (iname_soil or iname_snow) + ! number of state variables of a specific type + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain + nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ! derivatives + dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) + dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(in): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + ! number of flux calls + numFluxCalls => diag_data%var(iLookDIAG%numFluxCalls)%dat(1) ,& ! intent(out): [dp] number of flux calls (-) + ! net fluxes over the vegetation domain + scalarCanairNetNrgFlux => flux_data%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the canopy air space (W m-2) + scalarCanopyNetNrgFlux => flux_data%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the vegetation canopy (W m-2) + scalarGroundNetNrgFlux => flux_data%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the ground surface (W m-2) + scalarCanopyNetLiqFlux => flux_data%var(iLookFLUX%scalarCanopyNetLiqFlux)%dat(1) ,& ! intent(out): [dp] net liquid water flux for the vegetation canopy (kg m-2 s-1) + ! net fluxes over the snow+soil domain + mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) + mLayerLiqFluxSnow => flux_data%var(iLookFLUX%mLayerLiqFluxSnow)%dat ,& ! intent(out): [dp] net liquid water flux for each snow layer (s-1) + mLayerLiqFluxSoil => flux_data%var(iLookFLUX%mLayerLiqFluxSoil)%dat ,& ! intent(out): [dp] net liquid water flux for each soil layer (s-1) + ! evaporative fluxes + scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(out): [dp] canopy transpiration (kg m-2 s-1) + scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(out): [dp] canopy evaporation/condensation (kg m-2 s-1) + scalarGroundEvaporation => flux_data%var(iLookFLUX%scalarGroundEvaporation)%dat(1) ,& ! intent(out): [dp] ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(out): [dp(:)] transpiration loss from each soil layer (m s-1) + ! fluxes for the snow+soil domain + iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(out): [dp(0:)] vertical energy flux at the interface of snow and soil layers + iLayerLiqFluxSnow => flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at snow layer interfaces (-) + iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at soil layer interfaces (-) + mLayerHydCond => flux_data%var(iLookFLUX%mLayerHydCond)%dat ,& ! intent(out): [dp(:)] hydraulic conductivity in each soil layer (m s-1) + mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(out): [dp(:)] baseflow from each soil layer (m s-1) + scalarSnowDrainage => flux_data%var(iLookFLUX%scalarSnowDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the snow profile (m s-1) + scalarSoilDrainage => flux_data%var(iLookFLUX%scalarSoilDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the soil profile (m s-1) + scalarSoilBaseflow => flux_data%var(iLookFLUX%scalarSoilBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the soil profile (m s-1) + ! infiltration + scalarInfilArea => diag_data%var(iLookDIAG%scalarInfilArea )%dat(1) ,& ! intent(out): [dp] fraction of unfrozen area where water can infiltrate (-) + scalarFrozenArea => diag_data%var(iLookDIAG%scalarFrozenArea )%dat(1) ,& ! intent(out): [dp] fraction of area that is considered impermeable due to soil ice (-) + scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1) ,& ! intent(out): [dp] soil control on infiltration, zero or one + scalarMaxInfilRate => flux_data%var(iLookFLUX%scalarMaxInfilRate)%dat(1) ,& ! intent(out): [dp] maximum infiltration rate (m s-1) + scalarInfiltration => flux_data%var(iLookFLUX%scalarInfiltration)%dat(1) ,& ! intent(out): [dp] infiltration of water into the soil profile (m s-1) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(inout): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(inout): [dp(:)] fraction of liquid water in each snow layer (-) + ! boundary fluxes in the soil domain + scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(out): [dp] rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(out): [dp] drainage of liquid water from the vegetation canopy (kg m-2 s-1) + scalarRainPlusMelt => flux_data%var(iLookFLUX%scalarRainPlusMelt)%dat(1) ,& ! intent(out): [dp] rain plus melt (m s-1) + scalarSurfaceRunoff => flux_data%var(iLookFLUX%scalarSurfaceRunoff)%dat(1) ,& ! intent(out): [dp] surface runoff (m s-1) + scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1) ,& ! intent(out): [dp] exfiltration from the soil profile (m s-1) + mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat ,& ! intent(out): [dp(:)] column outflow from each soil layer (m3 s-1) + ! fluxes for the aquifer + scalarAquiferTranspire => flux_data%var(iLookFLUX%scalarAquiferTranspire)%dat(1) ,& ! intent(out): [dp] transpiration loss from the aquifer (m s-1 + scalarAquiferRecharge => flux_data%var(iLookFLUX%scalarAquiferRecharge)%dat(1) ,& ! intent(out): [dp] recharge to the aquifer (m s-1) + scalarAquiferBaseflow => flux_data%var(iLookFLUX%scalarAquiferBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the aquifer (m s-1) + ! total runoff + scalarTotalRunoff => flux_data%var(iLookFLUX%scalarTotalRunoff)%dat(1) ,& ! intent(out): [dp] total runoff (m s-1) + ! derivatives in net vegetation energy fluxes w.r.t. relevant state variables + dCanairNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy air temperature + dCanairNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy temperature + dCanairNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. ground temperature + dCanopyNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy air temperature + dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy temperature + dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. ground temperature + dCanopyNetFlux_dCanWat => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in net canopy fluxes w.r.t. canopy total water content + dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy air temperature + dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy temperature + dGroundNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. ground temperature + dGroundNetFlux_dCanWat => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in net ground fluxes w.r.t. canopy total water content + ! derivatives in evaporative fluxes w.r.t. relevant state variables + dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy air temperature + dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy temperature + dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. ground temperature + dCanopyEvaporation_dCanWat => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy total water content + dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy air temperature + dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy temperature + dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. ground temperature + dGroundEvaporation_dCanWat => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy total water content + ! derivatives in transpiration + dCanopyTrans_dTCanair => deriv_data%var(iLookDERIV%dCanopyTrans_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy => deriv_data%var(iLookDERIV%dCanopyTrans_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround => deriv_data%var(iLookDERIV%dCanopyTrans_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + dCanopyTrans_dCanWat => deriv_data%var(iLookDERIV%dCanopyTrans_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy total water content (s-1) + ! derivatives in canopy water w.r.t canopy temperature + dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(out): [dp] derivative of canopy liquid storage w.r.t. temperature + ! derivatives in canopy liquid fluxes w.r.t. canopy water + scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ,& ! intent(out): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water + scalarThroughfallRainDeriv => deriv_data%var(iLookDERIV%scalarThroughfallRainDeriv )%dat(1) ,& ! intent(out): [dp] derivative in throughfall w.r.t. canopy liquid water + scalarCanopyLiqDrainageDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDrainageDeriv)%dat(1) ,& ! intent(out): [dp] derivative in canopy drainage w.r.t. canopy liquid water + ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below + dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above + dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below + dThermalC_dWatAbove => deriv_data%var(iLookDERIV%dThermalC_dWatAbove )%dat ,& ! intent(in): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow )%dat ,& ! intent(in): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above + ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below + dNrgFlux_dWatAbove => deriv_data%var(iLookDERIV%dNrgFlux_dWatAbove )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. water state in the layer above + dNrgFlux_dWatBelow => deriv_data%var(iLookDERIV%dNrgFlux_dWatBelow )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. water state in the layer below + dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove )%dat ,& ! intent(in): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow )%dat ,& ! intent(in): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above + iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(out): [dp(:)] derivative in vertical liquid water flux at layer interfaces + ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential + dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above + dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + dq_dHydStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dHydStateLayerSurfVec )%dat ,& ! intent(out): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers + mLayerdTheta_dPsi => deriv_data%var(iLookDERIV%mLayerdTheta_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. psi + mLayerdPsi_dTheta => deriv_data%var(iLookDERIV%mLayerdPsi_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. theta + dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in compressibility w.r.t matric head + ! derivative in baseflow flux w.r.t. aquifer storage + dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] derivative in baseflow flux w.r.t. aquifer storage (s-1) + ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables + dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above + dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + dq_dNrgStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dNrgStateLayerSurfVec )%dat ,& ! intent(out): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers + ! derivatives in soil transpiration w.r.t. canopy state variables + mLayerdTrans_dTCanair => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanair )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + mLayerdTrans_dTCanopy => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanopy )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy temperature + mLayerdTrans_dTGround => deriv_data%var(iLookDERIV%mLayerdTrans_dTGround )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. ground temperature + mLayerdTrans_dCanWat => deriv_data%var(iLookDERIV%mLayerdTrans_dCanWat )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy total water + ! derivatives in aquifer transpiration w.r.t. canopy state variables + dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + dAquiferTrans_dTCanopy => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanopy )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy temperature + dAquiferTrans_dTGround => deriv_data%var(iLookDERIV%dAquiferTrans_dTGround )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. ground temperature + dAquiferTrans_dCanWat => deriv_data%var(iLookDERIV%dAquiferTrans_dCanWat )%dat(1) & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy total water + ) ! end associating to data in structures + + ! Validate operation argument + if ((op/=iLookOP%pre).and.(op/=iLookOP%post)) then + err=20 + cmessage="Error in subTools: invalid op argument requested." + message=trim(message)//trim(cmessage); return + end if + + select case(sub) + case(iLookROUTINE%vegNrgFlux) ! vegNrgFlux + + case(iLookROUTINE%ssdNrgFlux) ! ssdNrgFlux + + case(iLookROUTINE%vegLiqFlux) ! vegLiqFlux + + case(iLookROUTINE%snowLiqFlx) ! snowLiqFlx + + case(iLookROUTINE%soilLiqFlx) ! soilLiqFlx + + case(iLookROUTINE%groundwatr) ! groundwatr + + case(iLookROUTINE%bigAquifer) ! bigAquifer + + case default ! Error control for sub argument (must be s subroutine index that is included in the above case blocks) + err=20 + cmessage="Error in subTools: invalid sub argument requested." + message=trim(message)//trim(cmessage); return + end select + + end associate ! end associate block + + end subroutine subTools end subroutine computFlux From 01459145ee710652aa3d3ea949987394aa4be9a4 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 26 Aug 2023 05:15:43 -0600 Subject: [PATCH 0887/1472] Added derived types for intent(in), intent(inout), and intent(out) ssdNrgFlux arguments. --- build/source/dshare/data_types.f90 | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 106751351..3ac990ef5 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -315,5 +315,34 @@ MODULE data_types character(:), allocatable :: msg ! error message end type data_bin + ! testing SJT + type, public :: in_type_ssdNrgFlux ! derived type for intent(in) arguments in ssdNrgFlux call + logical(lgt) :: flag + real(rkind) :: scalarGroundNetNrgFlux + real(rkind) :: dGroundNetFlux_dGroundTemp + real(rkind), allocatable :: iLayerLiqFluxSnow(:) + real(rkind), allocatable :: iLayerLiqFluxSoil(:) + real(rkind), allocatable :: mLayerTempTrial(:) + real(rkind), allocatable :: dThermalC_dWatAbove(:) + real(rkind), allocatable :: dThermalC_dWatBelow(:) + real(rkind), allocatable :: dThermalC_dTempAbove(:) + real(rkind), allocatable :: dThermalC_dTempBelow(:) + end type in_type_ssdNrgFlux + + type, public :: io_type_ssdNrgFlux ! derived type for intent(inout) arguments in ssdNrgFlux call + real(rkind) :: dGroundNetFlux_dGroundTemp + end type io_type_ssdNrgFlux + + type, public :: out_type_ssdNrgFlux ! derived type for intent(inout) arguments in ssdNrgFlux call + real(rkind), allocatable :: iLayerNrgFlux(:) + real(rkind), allocatable :: dNrgFlux_dTempAbove(:) + real(rkind), allocatable :: dNrgFlux_dTempBelow(:) + real(rkind), allocatable :: dNrgFlux_dWatAbove(:) + real(rkind), allocatable :: dNrgFlux_dWatBelow(:) + integer(i4b) :: err + character(:),allocatable :: cmessage + end type out_type_ssdNrgFlux + ! end testing SJT + END MODULE data_types From 9ada3121537a36d381c4ac0f690be5fa0ee11300 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 26 Aug 2023 05:16:50 -0600 Subject: [PATCH 0888/1472] Shortened call syntax for ssdNrgFlux using new derived types. --- build/source/engine/computFlux.f90 | 99 +++++++++++++------------ build/source/engine/ssdNrgFlux.f90 | 115 +++++++++++++++-------------- 2 files changed, 108 insertions(+), 106 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 583d02dee..fb14c236c 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -25,12 +25,14 @@ module computFlux_module ! provide access to the derived types to define the data structures USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - model_options,& ! defines the model decisions - data_bin ! x%bin(:)%{lgt(:),i4b(:),rkind(:),rmatrix(:,:),string}, x%err [i4b], x%msg [character] + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (rkind) + model_options, & ! defines the model decisions + in_type_ssdNrgFlux,& ! intent(in) arguments for ssdNrgFlux call + io_type_ssdNrgFlux,& ! intent(inout) arguments for ssdNrgFlux call + out_type_ssdNrgFlux ! intent(inout) arguments for ssdNrgFlux call ! indices that define elements of the data structures USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure @@ -214,7 +216,9 @@ subroutine computFlux(& real(rkind) :: above_soilLiqFluxDeriv ! derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water real(rkind) :: above_soildLiq_dTk ! derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature real(rkind) :: above_soilFracLiq ! fraction of liquid water layer above soil (canopy or snow) (-) - + type(in_type_ssdNrgFlux) :: in_ssdNrgFlux ! data structure for ssdNrgFlux arguments + type(io_type_ssdNrgFlux) :: io_ssdNrgFlux ! data structure for ssdNrgFlux arguments + type(out_type_ssdNrgFlux) :: out_ssdNrgFlux ! data structure for ssdNrgFlux arguments ! -------------------------------------------------------------- ! initialize error control err=0; message='computFlux/' @@ -488,48 +492,10 @@ subroutine computFlux(& ! check the need to compute energy fluxes throughout the snow+soil domain if (nSnowSoilNrg>0) then - ! calculate energy fluxes at layer interfaces through the snow and soil domain - call ssdNrgFlux(& - ! input: model control - (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution - ! input: fluxes and derivatives at the upper boundary - scalarGroundNetNrgFlux, & ! intent(in): total flux at the ground surface (W m-2) - dGroundNetFlux_dGroundTemp, & ! intent(in): derivative in total ground surface flux w.r.t. ground temperature (W m-2 K-1) - ! input: liquid water fluxes throughout the snow and soil domains - iLayerLiqFluxSnow, & ! intent(in): liquid flux at the interface of each snow layer (m s-1) - iLayerLiqFluxSoil, & ! intent(in): liquid flux at the interface of each soil layer (m s-1) - ! input: trial value of model state variables - mLayerTempTrial, & ! intent(in): trial temperature at the current iteration (K) - ! input: derivatives - dThermalC_dWatAbove, & ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dWatBelow, & ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dTempAbove, & ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dTempBelow, & ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above - ! input-output: data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model indices - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output: fluxes and derivatives at all layer interfaces - iLayerNrgFlux, & ! intent(out):  energy flux at the layer interfaces (W m-2) - dNrgFlux_dTempAbove, & ! intent(out):  derivatives in the flux w.r.t. temperature in the layer above (W m-2 K-1) - dNrgFlux_dTempBelow, & ! intent(out):  derivatives in the flux w.r.t. temperature in the layer below (W m-2 K-1) - dNrgFlux_dWatAbove, & ! intent(out):  derivatives in the flux w.r.t. water state in the layer above - dNrgFlux_dWatBelow, & ! intent(out):  derivatives in the flux w.r.t. water state in the layer below - ! output: error control - err,cmessage) ! intent(out): error control - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! calculate net energy fluxes for each snow and soil layer (J m-3 s-1) - do iLayer=1,nLayers - mLayerNrgFlux(iLayer) = -(iLayerNrgFlux(iLayer) - iLayerNrgFlux(iLayer-1))/mLayerDepth(iLayer) - if (globalPrintFlag) then - if(iLayer < 10) write(*,'(a,1x,i4,1x,10(f25.15,1x))') 'iLayer, iLayerNrgFlux(iLayer-1:iLayer), mLayerNrgFlux(iLayer) = ', iLayer, iLayerNrgFlux(iLayer-1:iLayer), mLayerNrgFlux(iLayer) - end if - end do - + call subTools(iLookOP%pre,iLookROUTINE%ssdNrgFlux) ! pre-processing for call to ssdNrgFlux + call ssdNrgFlux(in_ssdNrgFlux,mpar_data,indx_data,prog_data,diag_data,flux_data,io_ssdNrgFlux,out_ssdNrgFlux) + call subTools(iLookOP%post,iLookROUTINE%ssdNrgFlux) ! post-processing for call to ssdNrgFlux end if ! end if computing energy fluxes throughout the snow+soil domain ! ***** @@ -1065,7 +1031,42 @@ subroutine subTools(op,sub) case(iLookROUTINE%vegNrgFlux) ! vegNrgFlux case(iLookROUTINE%ssdNrgFlux) ! ssdNrgFlux - + if (op==iLookOP%pre) then ! pre-processing + ! intent(in) arguments + in_ssdNrgFlux % flag=scalarSolution .and. .not.firstFluxCall + in_ssdNrgFlux % scalarGroundNetNrgFlux=scalarGroundNetNrgFlux + in_ssdNrgFlux % dGroundNetFlux_dGroundTemp=dGroundNetFlux_dGroundTemp + in_ssdNrgFlux % iLayerLiqFluxSnow=iLayerLiqFluxSnow + in_ssdNrgFlux % iLayerLiqFluxSoil=iLayerLiqFluxSoil + in_ssdNrgFlux % mLayerTempTrial=mLayerTempTrial + in_ssdNrgFlux % dThermalC_dWatAbove=dThermalC_dWatAbove + in_ssdNrgFlux % dThermalC_dWatBelow=dThermalC_dWatBelow + in_ssdNrgFlux % dThermalC_dTempAbove=dThermalC_dTempAbove + in_ssdNrgFlux % dThermalC_dTempBelow=dThermalC_dTempBelow + ! intent(inout) arguments + io_ssdNrgFlux % dGroundNetFlux_dGroundTemp=dGroundNetFlux_dGroundTemp + else ! post-processing + ! intent(inout) arguments + dGroundNetFlux_dGroundTemp=io_ssdNrgFlux % dGroundNetFlux_dGroundTemp + ! intent(out) arguments + iLayerNrgFlux =out_ssdNrgFlux % iLayerNrgFlux + dNrgFlux_dTempAbove=out_ssdNrgFlux % dNrgFlux_dTempAbove + dNrgFlux_dTempBelow=out_ssdNrgFlux % dNrgFlux_dTempBelow + dNrgFlux_dWatAbove =out_ssdNrgFlux % dNrgFlux_dWatAbove + dNrgFlux_dWatBelow =out_ssdNrgFlux % dNrgFlux_dWatBelow + err =out_ssdNrgFlux % err + cmessage =out_ssdNrgFlux % cmessage + !deallocate(in_ssdNrgFlux,io_ssdNrgFlux,out_ssdNrgFlux) -- update + ! error control + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + ! calculate net energy fluxes for each snow and soil layer (J m-3 s-1) + do iLayer=1,nLayers + mLayerNrgFlux(iLayer) = -(iLayerNrgFlux(iLayer) - iLayerNrgFlux(iLayer-1))/mLayerDepth(iLayer) + if (globalPrintFlag) then + if (iLayer < 10) write(*,'(a,1x,i4,1x,10(f25.15,1x))') 'iLayer, iLayerNrgFlux(iLayer-1:iLayer), mLayerNrgFlux(iLayer) = ', iLayer, iLayerNrgFlux(iLayer-1:iLayer), mLayerNrgFlux(iLayer) + end if + end do + end if case(iLookROUTINE%vegLiqFlux) ! vegLiqFlux case(iLookROUTINE%snowLiqFlx) ! snowLiqFlx diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index a95e6ee74..31a2ac175 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -24,9 +24,12 @@ module ssdNrgFlux_module USE nrtype ! data types -USE data_types,only:var_d ! x%var(:) [rkind] -USE data_types,only:var_dlength ! x%var(:)%dat [rkind] -USE data_types,only:var_ilength ! x%var(:)%dat [i4b] +USE data_types,only:var_d ! x%var(:) [rkind] +USE data_types,only:var_dlength ! x%var(:)%dat [rkind] +USE data_types,only:var_ilength ! x%var(:)%dat [i4b] +USE data_types,only:in_type_ssdNrgFlux ! intent(in) arguments for ssdNrgFlux +USE data_types,only:io_type_ssdNrgFlux ! intent(inout) arguments for ssdNrgFlux +USE data_types,only:out_type_ssdNrgFlux ! intent(out) arguments for ssdNrgFlux ! physical constants USE multiconst,only:& @@ -72,70 +75,35 @@ module ssdNrgFlux_module ! public subroutine ssdNrgFlux: compute energy fluxes and derivatives at layer interfaces ! ********************************************************************************************************** subroutine ssdNrgFlux(& - ! input: model control - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: fluxes and derivatives at the upper boundary - groundNetFlux, & ! intent(in): total flux at the ground surface (W m-2) - dGroundNetFlux_dGroundTemp, & ! intent(in): derivative in total ground surface flux w.r.t. ground temperature (W m-2 K-1) - ! input: liquid water fluxes - iLayerLiqFluxSnow, & ! intent(in): liquid flux at the interface of each snow layer (m s-1) - iLayerLiqFluxSoil, & ! intent(in): liquid flux at the interface of each soil layer (m s-1) - ! input: trial value of model state variables - mLayerTempTrial, & ! intent(in): trial temperature at the current iteration (K) - ! input: derivatives - dThermalC_dWatAbove, & ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dWatBelow, & ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dTempAbove, & ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dTempBelow, & ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above - ! input-output: data structures + ! input: model control, fluxes, trial variables, and derivatives + in_ssdNrgFlux, & ! intent(in): model control, fluxes, trial variables, and derivatives + ! input-output: data structures and derivatives mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model indices prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU - ! output: fluxes and derivatives at all layer interfaces - iLayerNrgFlux, & ! intent(out): energy flux at the layer interfaces (W m-2) - dFlux_dTempAbove, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (W m-2 K-1) - dFlux_dTempBelow, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (W m-2 K-1) - dFlux_dWatAbove, & ! intent(out): derivatives in the flux w.r.t. water state in the layer above (W m-2 K-1) - dFlux_dWatBelow, & ! intent(out): derivatives in the flux w.r.t. water state in the layer below (W m-2 K-1) - ! output: error control - err,message) ! intent(out): error control + io_ssdNrgFlux, & ! intent(inout): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + ! output: fluxes and derivatives at all layer interfaces and error control + out_ssdNrgFlux) ! intent(out): derivatives and error control ! ------------------------------------------------------------------------------------------------------------------------------------------------- implicit none - ! input: model control - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - ! input: fluxes and derivatives at the upper boundary - real(rkind),intent(in) :: groundNetFlux ! net energy flux for the ground surface (W m-2) - real(rkind),intent(inout) :: dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - ! input: liquid water fluxes - real(rkind),intent(in) :: iLayerLiqFluxSnow(0:) ! liquid flux at the interface of each snow layer (m s-1) - real(rkind),intent(in) :: iLayerLiqFluxSoil(0:) ! liquid flux at the interface of each soil layer (m s-1) - ! input: trial model state variables - real(rkind),intent(in) :: mLayerTempTrial(:) ! temperature in each layer at the current iteration (m) - ! input: derivatives - real(rkind),intent(in) :: dThermalC_dWatAbove(0:) ! derivative in the thermal conductivity w.r.t. water state in the layer above - real(rkind),intent(in) :: dThermalC_dWatBelow(0:) ! derivative in the thermal conductivity w.r.t. water state in the layer above - real(rkind),intent(in) :: dThermalC_dTempAbove(0:) ! derivative in the thermal conductivity w.r.t. energy state in the layer above - real(rkind),intent(in) :: dThermalC_dTempBelow(0:) ! derivative in the thermal conductivity w.r.t. energy state in the layer above + ! input: model control, fluxes, trial variables, and derivatives + type(in_type_ssdNrgFlux),intent(in) :: in_ssdNrgFlux ! input ssdNrgFlux arguments ! input-output: data structures - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! state vector geometry - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! state vector geometry + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + ! input-output: derivatives + type(io_type_ssdNrgFlux),intent(inout) :: io_ssdNrgFlux ! input-output ssdNrgFlux arguments ! output: fluxes and derivatives at all layer interfaces - real(rkind),intent(out) :: iLayerNrgFlux(0:) ! energy flux at the layer interfaces (W m-2) - real(rkind),intent(out) :: dFlux_dTempAbove(0:) ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) - real(rkind),intent(out) :: dFlux_dTempBelow(0:) ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) - real(rkind),intent(out) :: dFlux_dWatAbove(0:) ! derivatives in the flux w.r.t. water state in the layer above (J m-2 s-1 K-1) - real(rkind),intent(out) :: dFlux_dWatBelow(0:) ! derivatives in the flux w.r.t. water state in the layer below (J m-2 s-1 K-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + type(out_type_ssdNrgFlux),intent(inout) :: out_ssdNrgFlux ! output ssdNrgFlux arguments ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! local variables character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: nLayers ! number of model layers integer(i4b) :: iLayer ! index of model layers integer(i4b) :: ixLayerDesired(1) ! layer desired (scalar solution) integer(i4b) :: ixTop ! top layer in subroutine call @@ -143,13 +111,37 @@ subroutine ssdNrgFlux(& real(rkind) :: qFlux ! liquid flux at layer interfaces (m s-1) real(rkind) :: dz ! height difference (m) ! ------------------------------------------------------------------------------------------------------------------------------------------------------ + ! allocate intent(out) data structure components + nLayers=indx_data%var(iLookINDEX%nLayers)%dat(1) + allocate(& + out_ssdNrgFlux % iLayerNrgFlux(0:nLayers), & ! energy flux at the layer interfaces (W m-2) + out_ssdNrgFlux % dNrgFlux_dTempAbove(0:nLayers), & ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) + out_ssdNrgFlux % dNrgFlux_dTempBelow(0:nLayers), & ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) + out_ssdNrgFlux % dNrgFlux_dWatAbove(0:nLayers), & ! derivatives in the flux w.r.t. water state in the layer above (J m-2 s-1 K-1) + out_ssdNrgFlux % dNrgFlux_dWatBelow(0:nLayers)) ! derivatives in the flux w.r.t. water state in the layer below (J m-2 s-1 K-1) ! make association of local variables with information in the data structures associate(& + ! input: model control + scalarSolution => in_ssdNrgFlux % flag, & ! flag to denote if implementing the scalar solution + ! input: fluxes and derivatives at the upper boundary + groundNetFlux => in_ssdNrgFlux % scalarGroundNetNrgFlux, & ! net energy flux for the ground surface (W m-2) + dGroundNetFlux_dGroundTemp => io_ssdNrgFlux % dGroundNetFlux_dGroundTemp, & ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + ! input: liquid water fluxes + iLayerLiqFluxSnow => in_ssdNrgFlux % iLayerLiqFluxSnow, & ! liquid flux at the interface of each snow layer (m s-1) + iLayerLiqFluxSoil => in_ssdNrgFlux % iLayerLiqFluxSoil, & ! liquid flux at the interface of each soil layer (m s-1) + ! input: trial model state variables + mLayerTempTrial => in_ssdNrgFlux % mLayerTempTrial, & ! temperature in each layer at the current iteration (m) + ! input: derivatives + dThermalC_dWatAbove => in_ssdNrgFlux % dThermalC_dWatAbove, & ! derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow => in_ssdNrgFlux % dThermalC_dWatBelow, & ! derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dTempAbove => in_ssdNrgFlux % dThermalC_dTempAbove, & ! derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow => in_ssdNrgFlux % dThermalC_dTempBelow, & ! derivative in the thermal conductivity w.r.t. energy state in the layer above + ! input: boundary conditions ix_bcUpprTdyn => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision, & ! intent(in): method used to calculate the upper boundary condition for thermodynamics ix_bcLowrTdyn => model_decisions(iLookDECISIONS%bcLowrTdyn)%iDecision, & ! intent(in): method used to calculate the lower boundary condition for thermodynamics ! input: coordinate variables nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers + !nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat, & ! intent(in): index in the state subset for energy state variables in the snow+soil domain @@ -161,7 +153,16 @@ subroutine ssdNrgFlux(& iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(in): thermal conductivity at the interface of each layer (W m-1 K-1) ! output: diagnostic fluxes iLayerConductiveFlux => flux_data%var(iLookFLUX%iLayerConductiveFlux)%dat, & ! intent(out): conductive energy flux at layer interfaces at end of time step (W m-2) - iLayerAdvectiveFlux => flux_data%var(iLookFLUX%iLayerAdvectiveFlux)%dat & ! intent(out): advective energy flux at layer interfaces at end of time step (W m-2) + iLayerAdvectiveFlux => flux_data%var(iLookFLUX%iLayerAdvectiveFlux)%dat, & ! intent(out): advective energy flux at layer interfaces at end of time step (W m-2) + ! output: fluxes and derivatives at all layer interfaces + iLayerNrgFlux => out_ssdNrgFlux % iLayerNrgFlux, & ! energy flux at the layer interfaces (W m-2) + dFlux_dTempAbove => out_ssdNrgFlux % dNrgFlux_dTempAbove, & ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) + dFlux_dTempBelow => out_ssdNrgFlux % dNrgFlux_dTempBelow, & ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) + dFlux_dWatAbove => out_ssdNrgFlux % dNrgFlux_dWatAbove, & ! derivatives in the flux w.r.t. water state in the layer above (J m-2 s-1 K-1) + dFlux_dWatBelow => out_ssdNrgFlux % dNrgFlux_dWatBelow, & ! derivatives in the flux w.r.t. water state in the layer below (J m-2 s-1 K-1) + ! output: error control + err => out_ssdNrgFlux % err, & ! error code + message => out_ssdNrgFlux % cmessage & ! error message ) ! end association of local variables with information in the data structures ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! initialize error control From a356618ca825a2a32136b139d3e5e9d404d3d3d6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sun, 27 Aug 2023 15:45:31 +0900 Subject: [PATCH 0889/1472] plotting files --- utils/hist_per_GRU.py | 10 +++++-- utils/input_per_GRU.py | 7 ++--- utils/plot_per_GRU.py | 10 +++++-- utils/process_concat.sh | 0 utils/process_diff.sh | 0 utils/process_hist.sh | 0 utils/process_plot.sh | 0 utils/process_scat.sh | 0 utils/process_stat.sh | 0 utils/scat_per_GRU.py | 1 + utils/subsetGRU.sh | 0 utils/wallClock_per_GRU.py | 38 ++++++++++++++++++++------- utils/write_seff.bash | 54 ++++++++++++++++++++++++++++++++++++++ 13 files changed, 103 insertions(+), 17 deletions(-) mode change 100644 => 100755 utils/process_concat.sh mode change 100644 => 100755 utils/process_diff.sh mode change 100644 => 100755 utils/process_hist.sh mode change 100644 => 100755 utils/process_plot.sh mode change 100644 => 100755 utils/process_scat.sh mode change 100644 => 100755 utils/process_stat.sh mode change 100644 => 100755 utils/subsetGRU.sh create mode 100755 utils/write_seff.bash diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index c3af4ad61..d4e1995d1 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -20,6 +20,7 @@ import copy viz_dir = Path('/home/avanb/scratch/statistics') +nbatch_hrus = 518 # number of HRUs per batch testing = False if testing: @@ -49,9 +50,9 @@ fig_fil = 'Hrly_diff_hist_{}_{}_zoom_compressed.png' fig_fil = fig_fil.format(','.join(settings),stat) # possibly want to use these to shrink the axes a bit -if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,13e-3] +if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,10e-3] if stat=='maxe' : maxes = [20,30,3e-4,2,4e-7,0.7] -if stat=='kgem' : maxes = [0.9,0.7,0.9,0.95,0.95,13e-3] +if stat=='kgem' : maxes = [0.9,0.7,0.9,0.95,0.95,10e-3] # Get the aggregated statistics of SUMMA simulations summa = {} @@ -100,6 +101,11 @@ def run_loop(i,var,mx): # Data for m in method_name: s = summa[m][var].sel(stat=stat0) + if var == 'wallClockTime': + batch = np.floor(np.arange(len(s.indexes['hru'])) /nbatch_hrus) + batch = s*batch/s # batch number for CPU efficiency + # efficiency of batch*wallClockTime + if stat == 'maxe': s = np.fabs(s) # make absolute value norm range = (0,mx) if stat=='kgem' and var!='wallClockTime' : range = (mn,1) diff --git a/utils/input_per_GRU.py b/utils/input_per_GRU.py index d6e95516b..310a494b4 100644 --- a/utils/input_per_GRU.py +++ b/utils/input_per_GRU.py @@ -36,12 +36,13 @@ viz_dir = Path('/home/avanb/scratch/statistics') viz_fil = method_name + '_hrly_diff_stats_{}.nc' viz_fil = viz_fil.format(','.join(settings)) +nbatch_hrus = 518 # number of HRUs per batch # Specify variables of interest plot_vars = ['batch','batchNum','batchNumMultWallClockTime','wallClockTime','batchNumMultWallClockMax','wallClockMax'] plt_titl = ['(a) Batch','(b) Basin in batch','(c) Basin in batch * Wall clock mean time','(d) Wall clock mean time','(e) Basin in batch * Wall clock max time','(f) Wall clock max time'] leg_titl = ['$num$','$num$','$num~s$','$s$','$num~s$','$s$'] -maxes = [998,517,4.6,9e-3,100,0.2] +maxes = [998,517,4.6,10e-3,100,0.2] fig_fil = method_name + '_wallClockTime_batchNum_compressed.png' @@ -152,8 +153,8 @@ def make_default_path(suffix): hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile s0 = summa['wallClockTime'].sel(stat='mean') s1 = summa['wallClockTime'].sel(stat='amax') -modulus = np.arange(len(s0.indexes['hru'])) % 518 -batch = np.floor(np.arange(len(s0.indexes['hru'])) /518) +modulus = np.arange(len(s0.indexes['hru'])) % nbatch_hrus +batch = np.floor(np.arange(len(s0.indexes['hru'])) /nbatch_hrus) for plot_var in plot_vars: if plot_var == 'batch': s = s0*batch/s0 diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 2176d86cd..db340aef0 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -38,14 +38,15 @@ viz_dir = Path('/home/avanb/scratch/statistics') viz_fil = method_name + '_hrly_diff_stats_{}.nc' viz_fil = viz_fil.format(','.join(settings)) +nbatch_hrus = 518 # number of HRUs per batch # Specify variables of interest plot_vars = settings plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] -if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,13e-3] +if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,10e-3] if stat=='maxe': maxes = [20,30,3e-4,2,4e-7,0.7] -if stat=='kgem' : maxes = [0.9,0.7,0.9,0.95,0.95,13e-3] +if stat=='kgem' : maxes = [0.9,0.7,0.9,0.95,0.95,10e-3] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed.png' fig_fil = fig_fil.format(','.join(settings),stat) @@ -161,6 +162,11 @@ def make_default_path(suffix): if stat == 'rmse' or stat == 'kgem': stat0 = 'mean' if stat == 'maxe': stat0 = 'amax' s = summa[plot_var].sel(stat=stat0) + if var == 'wallClockTime': + batch = np.floor(np.arange(len(s.indexes['hru'])) /nbatch_hrus) + batch = s*batch/s # batch number for CPU efficiency + # efficiency of batch*wallClockTime + if stat == 'maxe': s = np.fabs(s) # make absolute value norm, max is not not all positive bas_albers[plot_var] = s.sel(hru=hru_ids_shp.values) diff --git a/utils/process_concat.sh b/utils/process_concat.sh old mode 100644 new mode 100755 diff --git a/utils/process_diff.sh b/utils/process_diff.sh old mode 100644 new mode 100755 diff --git a/utils/process_hist.sh b/utils/process_hist.sh old mode 100644 new mode 100755 diff --git a/utils/process_plot.sh b/utils/process_plot.sh old mode 100644 new mode 100755 diff --git a/utils/process_scat.sh b/utils/process_scat.sh old mode 100644 new mode 100755 diff --git a/utils/process_stat.sh b/utils/process_stat.sh old mode 100644 new mode 100755 diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index d93150334..af560dd7d 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -20,6 +20,7 @@ import copy viz_dir = Path('/home/avanb/scratch/statistics') +nbatch_hrus = 518 # number of HRUs per batch testing = False if testing: diff --git a/utils/subsetGRU.sh b/utils/subsetGRU.sh old mode 100644 new mode 100755 diff --git a/utils/wallClock_per_GRU.py b/utils/wallClock_per_GRU.py index 7a8bcc532..37187f11b 100644 --- a/utils/wallClock_per_GRU.py +++ b/utils/wallClock_per_GRU.py @@ -19,6 +19,7 @@ import copy viz_dir = Path('/home/avanb/scratch/statistics') +nbatch_hrus = 518 # number of HRUs per batch testing = False if testing: @@ -36,8 +37,8 @@ viz_fil[i] = viz_fil[i].format(','.join(settings)) # Specify variables of interest -plt_titl = ['(a) Wall clock mean time','(b) Wall clock max time'] -leg_titl = ['$s$','$s$'] +plt_titl = ['(a) Mean time vs basin in batch','(b) Max time vs basin in batch','(c) Mean time vs batch','(d) Max time vs batch','(e) Mean time vs efficiency','(f) Max time vs efficiency'] +leg_titl = ['$s$','$s$','$s$','$s$','$s$','$s$'] #fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' #fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) @@ -61,9 +62,9 @@ #bas_albers['plot_ET'] = bas_albers['plot_ET'].where(bas_albers['scalarTotalET'] != -9999, np.nan) if 'compressed' in fig_fil: - fig,axs = plt.subplots(1,2,figsize=(35,33)) + fig,axs = plt.subplots(3,2,figsize=(35,33)) else: - fig,axs = plt.subplots(1,2,figsize=(140,133)) + fig,axs = plt.subplots(3,2,figsize=(140,133)) def run_loop(i,stat): @@ -73,20 +74,37 @@ def run_loop(i,stat): # Data for m in method_name: s = summa[m]['wallClockTime'].sel(stat=stat) - modulus = np.arange(len(s.indexes['hru'])) % 518 - axs[c].scatter(x=s.values,y=modulus.values,s=1,zorder=0,label=m) + for r in range(3): + if r == 0: + modulus = np.arange(len(s.indexes['hru'])) % nbatch_hrus + axs[r,c].scatter(x=s.values,y=modulus.values,s=1,zorder=0,label=m) + stat0_word ='Basin number in batch' + if r == 0: + node= np.arange(len(s.indexes['hru'])) % nbatch_hrus + vs wallclock*eff? + axs[r,c].scatter(x=s.values,y=node.values,s=1,zorder=0,label=m) + stat0_word ='Node number' + if r == 1: + batch = np.floor(np.arange(len(s.indexes['hru'])) /nbatch_hrus) + axs[r,c].scatter(x=s.values,y=batch.values,s=1,zorder=0,label=m) + stat0_word ='Batch number' + if r == 2: + eff = np.floor(np.arange(len(s.indexes['hru'])) /nbatch_hrus) + axs[r,c].scatter(x=s.values,y=eff.values,s=1,zorder=0,label=m) + stat0_word ='Fraction efficiency' + + if stat == 'mean': stat_word = 'Wall clock time hourly mean ' if stat == 'amax': stat_word = 'Wall clock time hourly max ' - stat0_word ='Batch number' lgnd = axs[c].legend() for j, m in enumerate(method_name): lgnd.legendHandles[j]._sizes = [80] - axs[c].set_title(plt_titl[i]) - axs[c].set_xlabel(stat_word + '[{}]'.format(leg_titl[i])) - axs[c].set_ylabel(stat0_word) + axs[1,c].set_title(plt_titl[i]) + axs[1,c].set_xlabel(stat_word + '[{}]'.format(leg_titl[i])) + axs[1,c].set_ylabel(stat0_word) for i,stat in enumerate(['mean','amax']): diff --git a/utils/write_seff.bash b/utils/write_seff.bash new file mode 100755 index 000000000..91a981417 --- /dev/null +++ b/utils/write_seff.bash @@ -0,0 +1,54 @@ +#!/bin/bash +# run as bash write_seff.bash /path/to/log.txt /path/to/output.txt + +# Set the path to the log file +LOG_FILE="$1" + +# Set the path to the output file +OUTPUT_FILE="$2" + +# Create the output file if it doesn't exist, and overwrite it with the new output +touch "$OUTPUT_FILE" +> "$OUTPUT_FILE" + +# Loop through each line in the log file with "log_slurmId_" + grep "log_slurmId_" "$LOG_FILE" | while read -r line ; do + + # Extract the job ID from the line + job_id=$(echo "$line" | grep -oP 'log_slurmId_\K\d+(?=_arrayId_)') + + # Call the seff command on the job ID and store the output in a variable + seff_output=$(seff "$job_id") + + # Extract the CPU efficiency percentage from the seff output and convert it to a decimal format + cpu_efficiency=$(echo "$seff_output" | grep -oP 'CPU Efficiency: \K\d+\.\d+') + cpu_efficiency=$(echo "scale=4; $cpu_efficiency/100" | bc) + + # Extract the Array Job ID from the seff output + array_id=$(echo "$seff_output" | grep -oP 'Array Job ID: \K\d+_\d+' | awk -F'_' '{print $2}') + + # Extract the Job Wall-clock time from the seff output, and convert to hours + wall_clock=$(echo "$seff_output" | grep -oP 'Job Wall-clock time: \K.*') + if [[ "$wall_clock" == *-* ]]; then + days=$(echo "$wall_clock" | awk -F'-' '{print $1}') + hours=$(echo "$wall_clock" | awk -F'-' '{print $2}' | awk -F':' '{print $1}') + minutes=$(echo "$wall_clock" | awk -F'-' '{print $2}' | awk -F':' '{print $2}') + else + days=0 + hours=$(echo "$wall_clock" | awk -F':' '{print $1}') + minutes=$(echo "$wall_clock" | awk -F':' '{print $2}') + fi + total_hours=$(echo "scale=2; ($days*24)+$hours+($minutes/60)" | bc) + + # Call the sacct command on the job ID and store the output in a variable + # Note, the user has to be changed to the user who ran the job + sacct_output=$(sacct -j "$job_id" -u avanb -o NodeList) + + # Extract the node number from the NodeList field + # Note, this assumes it is running on graXXXX + node_list=$(echo "$sacct_output" | tail -n 1 | sed 's/^[[:space:]]*//') + node_number=$(echo "$node_list" | sed 's/gra//') + + # Write the CPU efficiency, Array Job ID, Job Wall-clock time, and the node number to the output file + echo "CPU Efficiency: $cpu_efficiency, Array ID: $array_id, Job Wall-clock time: $total_hours, Node Number: $node_number" >> "$OUTPUT_FILE" + done From 8a64ccc0940042ddea69d5d9d36debd4b574b0e7 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 28 Aug 2023 15:07:43 +0900 Subject: [PATCH 0890/1472] plotting utilities to use efficiency --- utils/hist_per_GRU.py | 45 +++++++++++++++---- utils/input_per_GRU.py | 51 ++++++++++++++++------ utils/plot_per_GRU.py | 35 ++++++++++++--- utils/scat_per_GRU.py | 63 ++++++++++++++++++++------- utils/wallClock_per_GRU.py | 88 ++++++++++++++++++++++---------------- 5 files changed, 200 insertions(+), 82 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index d4e1995d1..b445bb09e 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -18,13 +18,15 @@ from pathlib import Path import matplotlib.pyplot as plt import copy +import pandas as pd viz_dir = Path('/home/avanb/scratch/statistics') nbatch_hrus = 518 # number of HRUs per batch +num_bins = 1000 testing = False if testing: - stat = 'kgem' + stat = 'rmse' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') method_name=['be1','be64','sundials_1en6'] #maybe make this an argument else: @@ -36,9 +38,11 @@ # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] viz_fil = method_name.copy() +eff_fil = method_name.copy() for i, m in enumerate(method_name): viz_fil[i] = m + '_hrly_diff_stats_{}.nc' viz_fil[i] = viz_fil[i].format(','.join(settings)) + eff_fil[i] = 'eff_' + m + '.txt' # Specify variables of interest plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] @@ -51,13 +55,21 @@ fig_fil = fig_fil.format(','.join(settings),stat) # possibly want to use these to shrink the axes a bit if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,10e-3] -if stat=='maxe' : maxes = [20,30,3e-4,2,4e-7,0.7] +if stat=='maxe' : maxes = [20,30,3e-4,2,4e-7,0.2] if stat=='kgem' : maxes = [0.9,0.7,0.9,0.95,0.95,10e-3] -# Get the aggregated statistics of SUMMA simulations summa = {} +eff = {} for i, m in enumerate(method_name): + # Get the aggregated statistics of SUMMA simulations summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) + # Read the data from the eff.txt file into a DataFrame + eff[m] = pd.read_csv(viz_dir/eff_fil[i], sep=',', header=None, names=['CPU Efficiency', 'Array ID', 'Job Wall-clock time', 'Node Number']) + # Extract only the values after the ':' character in the 'CPU Efficiency', 'Job Wall-clock time', and 'Node Number' columns + eff[m]['CPU Efficiency'] = eff[m]['CPU Efficiency'].str.split(':').str[1].astype(float) + eff[m]['Array ID'] = eff[m]['Array ID'].str.split(':').str[1].astype(int) + eff[m]['Job Wall-clock time'] = eff[m]['Job Wall-clock time'].str.split(':').str[1].astype(float) + eff[m]['Node Number'] = eff[m]['Node Number'].str.split(':').str[1].astype(int) ##Figure @@ -80,7 +92,6 @@ def run_loop(i,var,mx): r = i//2 c = i-r*2 - num_bins = 200 stat0 = stat if var == 'wallClockTime': if stat == 'rmse' or stat == 'kgem': stat0 = 'mean' @@ -103,14 +114,31 @@ def run_loop(i,var,mx): s = summa[m][var].sel(stat=stat0) if var == 'wallClockTime': batch = np.floor(np.arange(len(s.indexes['hru'])) /nbatch_hrus) - batch = s*batch/s # batch number for CPU efficiency - # efficiency of batch*wallClockTime + #basin_num = np.arange(len(s.indexes['hru'])) % nbatch_hrus #not currently using + # Create a dictionary to store the values for each batch + efficiency = {} + node = {} + # Iterate over the rows in the data DataFrame + for index, row in eff[m].iterrows(): + # Extract the values from the row + batch0 = int(row['Array ID']) + eff0 = row['CPU Efficiency'] + node0 = row['Node Number'] + # Store the value for the current batch in the dictionary + efficiency[batch0] = eff0 + node[batch0] = node0 + # Select the values for the current batch using boolean indexing + eff_batch = np.array([efficiency[b] for b in batch]) + #node_batch = np.array([node[b] for b in batch]) #not currently using + # Multiply the s values by efficiency + s = s*eff_batch if stat == 'maxe': s = np.fabs(s) # make absolute value norm range = (0,mx) if stat=='kgem' and var!='wallClockTime' : range = (mn,1) s.plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=m,linewidth=2.0,range=range) - + + if stat == 'rmse': stat_word = ' Hourly RMSE' if stat == 'maxe': stat_word = ' Hourly max abs error' if stat == 'kgem': stat_word = ' Hourly KGEm' @@ -119,10 +147,11 @@ def run_loop(i,var,mx): if var == 'wallClockTime': if stat == 'rmse' or stat == 'kgem': stat_word = ' Hourly mean' if stat == 'maxe': stat_word = ' Hourly max' + #axs[r,c].set_yscale('log') #log y axis for wall clock time to exaggerate peaks axs[r,c].legend() axs[r,c].set_title(plt_titl[i] + stat_word) - axs[r,c].set_xlabel('[{}]'.format(leg_titl[i])) + axs[r,c].set_xlabel(stat_word + '[{}]'.format(leg_titl[i])) axs[r,c].set_ylabel('GRU count') diff --git a/utils/input_per_GRU.py b/utils/input_per_GRU.py index 310a494b4..58ed2c9c6 100644 --- a/utils/input_per_GRU.py +++ b/utils/input_per_GRU.py @@ -27,6 +27,7 @@ import pyproj import fiona import geopandas as gpd +import pandas as pd # The first input argument specifies the run where the files are method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) @@ -36,13 +37,15 @@ viz_dir = Path('/home/avanb/scratch/statistics') viz_fil = method_name + '_hrly_diff_stats_{}.nc' viz_fil = viz_fil.format(','.join(settings)) +eff_fil = 'eff_' + method_name + '.txt' nbatch_hrus = 518 # number of HRUs per batch # Specify variables of interest -plot_vars = ['batch','batchNum','batchNumMultWallClockTime','wallClockTime','batchNumMultWallClockMax','wallClockMax'] -plt_titl = ['(a) Batch','(b) Basin in batch','(c) Basin in batch * Wall clock mean time','(d) Wall clock mean time','(e) Basin in batch * Wall clock max time','(f) Wall clock max time'] -leg_titl = ['$num$','$num$','$num~s$','$s$','$num~s$','$s$'] -maxes = [998,517,4.6,10e-3,100,0.2] +# Note, max for nodes is based off Graham which has 1185 nodes +plot_vars = ['batch','node','effMultWallClockTime','wallClockTime','effMultWallClockMax','wallClockMax'] +plt_titl = ['(a) Batch number','(b) Node number','(c) Efficiency * Wall clock mean time','(d) Wall clock mean time','(e) Efficiency * Wall clock max time','(f) Wall clock max time'] +leg_titl = ['$num$','$num$','$s$','$s$','$s$','$s$'] +maxes = [998,1185,10e-3,10e-3,0.2,0.2] fig_fil = method_name + '_wallClockTime_batchNum_compressed.png' @@ -123,7 +126,6 @@ def make_default_path(suffix): # Find the segment ID seg_id = read_from_control(controlFile,'river_network_shp_segid') - ## Load all shapefiles and project to Albers Conformal Conic and reproject # Set the target CRS acc = 'ESRI:102008' @@ -148,24 +150,47 @@ def make_default_path(suffix): ## Pre-processing, map SUMMA sims to catchment shapes # Get the aggregated statistics of SUMMA simulations summa = xr.open_dataset(viz_dir/viz_fil) +# Read the data from the eff.txt file into a DataFrame +eff = pd.read_csv(viz_dir/eff_fil, sep=',', header=None, names=['CPU Efficiency', 'Array ID', 'Job Wall-clock time', 'Node Number']) +# Extract only the values after the ':' character in the 'CPU Efficiency', 'Job Wall-clock time', and 'Node Number' columns +eff['CPU Efficiency'] = eff['CPU Efficiency'].str.split(':').str[1].astype(float) +eff['Array ID'] = eff['Array ID'].str.split(':').str[1].astype(int) +eff['Job Wall-clock time'] = eff['Job Wall-clock time'].str.split(':').str[1].astype(float) +eff['Node Number'] = eff['Node Number'].str.split(':').str[1].astype(int) # Match the accummulated values to the correct HRU IDs in the shapefile hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile s0 = summa['wallClockTime'].sel(stat='mean') s1 = summa['wallClockTime'].sel(stat='amax') -modulus = np.arange(len(s0.indexes['hru'])) % nbatch_hrus batch = np.floor(np.arange(len(s0.indexes['hru'])) /nbatch_hrus) +#basin_num = np.arange(len(s0.indexes['hru'])) % nbatch_hrus #not currently using +# Create a dictionary to store the values for each batch +efficiency = {} +node = {} +# Iterate over the rows in the data DataFrame +for index, row in eff.iterrows(): + # Extract the values from the row + batch0 = int(row['Array ID']) + eff0 = row['CPU Efficiency'] + node0 = row['Node Number'] + # Store the value for the current batch in the dictionary + efficiency[batch0] = eff0 + node[batch0] = node0 +# Select the values for the current batch using boolean indexing +eff_batch = np.array([efficiency[b] for b in batch]) +node_batch = np.array([node[b] for b in batch]) + for plot_var in plot_vars: if plot_var == 'batch': s = s0*batch/s0 - if plot_var == 'batchNum': - s = s0*modulus/s0 - if plot_var == 'batchNumMultWallClockTime': - s = s0*modulus + if plot_var == 'node': + s = s0*node_batch/s0 + if plot_var == 'effMultWallClockTime': + s = s0*eff_batch if plot_var == 'wallClockTime': s = s0 - if plot_var == 'batchNumMultWallClockMax': - s = s1*modulus + if plot_var == 'effWallClockMax': + s = s1*eff_batch if plot_var == 'wallClockMax': s = s1 bas_albers[plot_var] = s.sel(hru=hru_ids_shp.values) @@ -178,8 +203,6 @@ def make_default_path(suffix): (lak_albers['Country'] == 'Mexico') out_domain = (lak_albers['Pour_long'] > -80) & (lak_albers['Pour_lat'] > 65) # Exclude Baffin Island large_lakes_albers = lak_albers.loc[(lak_albers['Lake_area'] > minSize) & in_domain & (~out_domain) ] -# Set the lake color -if plot_lakes: lake_col = (8/255,81/255,156/255) ##Figure diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index db340aef0..66e1ae5a4 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -28,6 +28,7 @@ import pyproj import fiona import geopandas as gpd +import pandas as pd # The first input argument specifies the run where the files are method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) @@ -38,6 +39,7 @@ viz_dir = Path('/home/avanb/scratch/statistics') viz_fil = method_name + '_hrly_diff_stats_{}.nc' viz_fil = viz_fil.format(','.join(settings)) +eff_fil = 'eff_' + method_name + '.txt' nbatch_hrus = 518 # number of HRUs per batch # Specify variables of interest @@ -45,7 +47,7 @@ plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,10e-3] -if stat=='maxe': maxes = [20,30,3e-4,2,4e-7,0.7] +if stat=='maxe': maxes = [20,30,3e-4,2,4e-7,0.2] if stat=='kgem' : maxes = [0.9,0.7,0.9,0.95,0.95,10e-3] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed.png' @@ -153,6 +155,13 @@ def make_default_path(suffix): ## Pre-processing, map SUMMA sims to catchment shapes # Get the aggregated statistics of SUMMA simulations summa = xr.open_dataset(viz_dir/viz_fil) +# Read the data from the eff.txt file into a DataFrame +eff = pd.read_csv(viz_dir/eff_fil, sep=',', header=None, names=['CPU Efficiency', 'Array ID', 'Job Wall-clock time', 'Node Number']) +# Extract only the values after the ':' character in the 'CPU Efficiency', 'Job Wall-clock time', and 'Node Number' columns +eff['CPU Efficiency'] = eff['CPU Efficiency'].str.split(':').str[1].astype(float) +eff['Array ID'] = eff['Array ID'].str.split(':').str[1].astype(int) +eff['Job Wall-clock time'] = eff['Job Wall-clock time'].str.split(':').str[1].astype(float) +eff['Node Number'] = eff['Node Number'].str.split(':').str[1].astype(int) # Match the accummulated values to the correct HRU IDs in the shapefile hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile @@ -162,10 +171,26 @@ def make_default_path(suffix): if stat == 'rmse' or stat == 'kgem': stat0 = 'mean' if stat == 'maxe': stat0 = 'amax' s = summa[plot_var].sel(stat=stat0) - if var == 'wallClockTime': + if plot_var == 'wallClockTime': batch = np.floor(np.arange(len(s.indexes['hru'])) /nbatch_hrus) - batch = s*batch/s # batch number for CPU efficiency - # efficiency of batch*wallClockTime + #basin_num = np.arange(len(s.indexes['hru'])) % nbatch_hrus #not currently using + # Create a dictionary to store the values for each batch + efficiency = {} + node = {} + # Iterate over the rows in the data DataFrame + for index, row in eff.iterrows(): + # Extract the values from the row + batch0 = int(row['Array ID']) + eff0 = row['CPU Efficiency'] + node0 = row['Node Number'] + # Store the value for the current batch in the dictionary + efficiency[batch0] = eff0 + node[batch0] = node0 + # Select the values for the current batch using boolean indexing + eff_batch = np.array([efficiency[b] for b in batch]) + #node_batch = np.array([node[b] for b in batch]) #not currently using + # Multiply the s values by efficiency + s = s*eff_batch if stat == 'maxe': s = np.fabs(s) # make absolute value norm, max is not not all positive bas_albers[plot_var] = s.sel(hru=hru_ids_shp.values) @@ -178,8 +203,6 @@ def make_default_path(suffix): (lak_albers['Country'] == 'Mexico') out_domain = (lak_albers['Pour_long'] > -80) & (lak_albers['Pour_lat'] > 65) # Exclude Baffin Island large_lakes_albers = lak_albers.loc[(lak_albers['Lake_area'] > minSize) & in_domain & (~out_domain) ] -# Set the lake color -if plot_lakes: lake_col = (8/255,81/255,156/255) ##Figure diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index af560dd7d..5948b1ba1 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -18,13 +18,14 @@ from pathlib import Path import matplotlib.pyplot as plt import copy +import pandas as pd viz_dir = Path('/home/avanb/scratch/statistics') nbatch_hrus = 518 # number of HRUs per batch testing = False if testing: - stat = 'kgem' + stat = 'rmse' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') method_name=['be1','be64','sundials_1en6'] #maybe make this an argument else: @@ -36,24 +37,36 @@ # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] viz_fil = method_name.copy() +eff_fil = method_name.copy() for i, m in enumerate(method_name): viz_fil[i] = m + '_hrly_diff_stats_{}.nc' viz_fil[i] = viz_fil[i].format(','.join(settings)) + eff_fil[i] = 'eff_' + m + '.txt' # Specify variables of interest plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] -leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] +leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$num$'] +leg_titl0 = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] + #fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' #fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) fig_fil = 'Hrly_diff_scat_{}_{}_compressed.png' fig_fil = fig_fil.format(','.join(settings),stat) -# Get the aggregated statistics of SUMMA simulations summa = {} +eff = {} for i, m in enumerate(method_name): + # Get the aggregated statistics of SUMMA simulations summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) + # Read the data from the eff.txt file into a DataFrame + eff[m] = pd.read_csv(viz_dir/eff_fil[i], sep=',', header=None, names=['CPU Efficiency', 'Array ID', 'Job Wall-clock time', 'Node Number']) + # Extract only the values after the ':' character in the 'CPU Efficiency', 'Job Wall-clock time', and 'Node Number' columns + eff[m]['CPU Efficiency'] = eff[m]['CPU Efficiency'].str.split(':').str[1].astype(float) + eff[m]['Array ID'] = eff[m]['Array ID'].str.split(':').str[1].astype(int) + eff[m]['Job Wall-clock time'] = eff[m]['Job Wall-clock time'].str.split(':').str[1].astype(float) + eff[m]['Node Number'] = eff[m]['Node Number'].str.split(':').str[1].astype(int) ##Figure @@ -83,25 +96,44 @@ def run_loop(i,var): for m in method_name: s = summa[m][var].sel(stat=[stat,stat0]) if stat == 'maxe': s.loc[dict(stat='maxe')] = np.fabs(s.loc[dict(stat='maxe')]) # make absolute value norm - axs[r,c].scatter(x=s.sel(stat=stat).values,y=s.sel(stat=stat0).values,s=1,zorder=0,label=m) - - if stat == 'rmse': - stat_word = 'Hourly RMSE ' - stat0_word ='Hourly mean ' - if stat == 'maxe': - stat_word = 'Hourly max abs error ' - stat0_word ='Hourly max ' - if stat == 'kgem': - stat_word = 'Hourly KGEm ' - stat0_word ='Hourly mean ' + if var == 'wallClockTime': + batch = np.floor(np.arange(len(s.indexes['hru'])) /nbatch_hrus) + #basin_num = np.arange(len(s.indexes['hru'])) % nbatch_hrus #not currently using + # Create a dictionary to store the values for each batch + efficiency = {} + node = {} + # Iterate over the rows in the data DataFrame + for index, row in eff[m].iterrows(): + # Extract the values from the row + batch0 = int(row['Array ID']) + eff0 = row['CPU Efficiency'] + node0 = row['Node Number'] + # Store the value for the current batch in the dictionary + efficiency[batch0] = eff0 + node[batch0] = node0 + # Select the values for the current batch using boolean indexing + eff_batch = np.array([efficiency[b] for b in batch]) + node_batch = np.array([node[b] for b in batch]) #not currently using + # Multiply the s values by efficiency + s = s*eff_batch + axs[r,c].scatter(x=node_batch,y=s.sel(stat=stat0),s=1,zorder=0,label=m) + stat_word = 'Node number' + else: + axs[r,c].scatter(x=s.sel(stat=stat).values,y=s.sel(stat=stat0).values,s=1,zorder=0,label=m) + if stat == 'rmse': stat_word = 'Hourly RMSE ' + if stat == 'maxe': stat_word = 'Hourly max abs error ' + if stat == 'kgem': stat_word = 'Hourly KGEm ' + + if stat0 == 'mean': stat0_word = 'Hourly mean ' + if stat0 == 'amax': stat0_word = 'Hourly max ' lgnd = axs[r,c].legend() for j, m in enumerate(method_name): lgnd.legendHandles[j]._sizes = [80] axs[r,c].set_title(plt_titl[i]) axs[r,c].set_xlabel(stat_word + '[{}]'.format(leg_titl[i])) - axs[r,c].set_ylabel(stat0_word + '[{}]'.format(leg_titl[i])) + axs[r,c].set_ylabel(stat0_word + '[{}]'.format(leg_titl0[i])) for i,var in enumerate(plot_vars): @@ -109,4 +141,3 @@ def run_loop(i,var): # Save plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) - diff --git a/utils/wallClock_per_GRU.py b/utils/wallClock_per_GRU.py index 37187f11b..cc46ca9b5 100644 --- a/utils/wallClock_per_GRU.py +++ b/utils/wallClock_per_GRU.py @@ -17,6 +17,7 @@ from pathlib import Path import matplotlib.pyplot as plt import copy +import pandas as pd viz_dir = Path('/home/avanb/scratch/statistics') nbatch_hrus = 518 # number of HRUs per batch @@ -32,23 +33,33 @@ # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] viz_fil = method_name.copy() +eff_fil = method_name.copy() for i, m in enumerate(method_name): viz_fil[i] = m + '_hrly_diff_stats_{}.nc' viz_fil[i] = viz_fil[i].format(','.join(settings)) + eff_fil[i] = 'eff_' + m + '.txt' # Specify variables of interest -plt_titl = ['(a) Mean time vs basin in batch','(b) Max time vs basin in batch','(c) Mean time vs batch','(d) Max time vs batch','(e) Mean time vs efficiency','(f) Max time vs efficiency'] -leg_titl = ['$s$','$s$','$s$','$s$','$s$','$s$'] +plt_titl = ['(a) Mean time vs basin in batch','(b) Max time vs basin in batch','(c) Mean time vs node','(d) Max time vs node'] +leg_titl = ['$s$','$s$','$s$','$s$'] #fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' #fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) fig_fil = 'WallClockTime_batchNum_scat_compressed.png' -# Get the aggregated statistics of SUMMA simulations summa = {} +eff = {} for i, m in enumerate(method_name): + # Get the aggregated statistics of SUMMA simulations summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) - + # Read the data from the eff.txt file into a DataFrame + eff[m] = pd.read_csv(viz_dir/eff_fil[i], sep=',', header=None, names=['CPU Efficiency', 'Array ID', 'Job Wall-clock time', 'Node Number']) + # Extract only the values after the ':' character in the 'CPU Efficiency', 'Job Wall-clock time', and 'Node Number' columns + eff[m]['CPU Efficiency'] = eff[m]['CPU Efficiency'].str.split(':').str[1].astype(float) + eff[m]['Array ID'] = eff[m]['Array ID'].str.split(':').str[1].astype(int) + eff[m]['Job Wall-clock time'] = eff[m]['Job Wall-clock time'].str.split(':').str[1].astype(float) + eff[m]['Node Number'] = eff[m]['Node Number'].str.split(':').str[1].astype(int) + ##Figure # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug @@ -62,49 +73,51 @@ #bas_albers['plot_ET'] = bas_albers['plot_ET'].where(bas_albers['scalarTotalET'] != -9999, np.nan) if 'compressed' in fig_fil: - fig,axs = plt.subplots(3,2,figsize=(35,33)) + fig,axs = plt.subplots(2,2,figsize=(35,33)) else: - fig,axs = plt.subplots(3,2,figsize=(140,133)) - + fig,axs = plt.subplots(2,2,figsize=(140,133)) -def run_loop(i,stat): - r = i//2 - c = i-r*2 +def run_loop(c,stat): # Data for m in method_name: s = summa[m]['wallClockTime'].sel(stat=stat) - for r in range(3): + batch = np.floor(np.arange(len(s.indexes['hru'])) /nbatch_hrus) + basin_num = np.arange(len(s.indexes['hru'])) % nbatch_hrus + # Create a dictionary to store the values for each batch + efficiency = {} + node = {} + # Iterate over the rows in the data DataFrame + for index, row in eff[m].iterrows(): + # Extract the values from the row + batch0 = int(row['Array ID']) + eff0 = row['CPU Efficiency'] + node0 = row['Node Number'] + # Store the value for the current batch in the dictionary + efficiency[batch0] = eff0 + node[batch0] = node0 + # Select the values for the current batch using boolean indexing + eff_batch = np.array([efficiency[b] for b in batch]) + node_batch = np.array([node[b] for b in batch]) + # Multiply the efficiency values by the s values + x = eff_batch * s.values + + if stat == 'mean': stat_word = 'Wall clock time hourly mean * efficiency ' + if stat == 'amax': stat_word = 'Wall clock time hourly max * efficiency ' + for r in range(2): if r == 0: - modulus = np.arange(len(s.indexes['hru'])) % nbatch_hrus - axs[r,c].scatter(x=s.values,y=modulus.values,s=1,zorder=0,label=m) + axs[r,c].scatter(x=x,y=basin_num,s=1,zorder=0,label=m) stat0_word ='Basin number in batch' - if r == 0: - node= np.arange(len(s.indexes['hru'])) % nbatch_hrus - vs wallclock*eff? - axs[r,c].scatter(x=s.values,y=node.values,s=1,zorder=0,label=m) - stat0_word ='Node number' if r == 1: - batch = np.floor(np.arange(len(s.indexes['hru'])) /nbatch_hrus) - axs[r,c].scatter(x=s.values,y=batch.values,s=1,zorder=0,label=m) - stat0_word ='Batch number' - if r == 2: - eff = np.floor(np.arange(len(s.indexes['hru'])) /nbatch_hrus) - axs[r,c].scatter(x=s.values,y=eff.values,s=1,zorder=0,label=m) - stat0_word ='Fraction efficiency' - - - - if stat == 'mean': stat_word = 'Wall clock time hourly mean ' - if stat == 'amax': stat_word = 'Wall clock time hourly max ' - - - lgnd = axs[c].legend() + axs[r,c].scatter(x=x,y=node_batch,s=1,zorder=0,label=m) + stat0_word ='Node number' + axs[r,c].set_title(plt_titl[r*2+c]) + axs[r,c].set_xlabel(stat_word + '[{}]'.format(leg_titl[r*2+c])) + axs[r,c].set_ylabel(stat0_word) + lgnd = axs[r,c].legend() + for j, m in enumerate(method_name): - lgnd.legendHandles[j]._sizes = [80] - axs[1,c].set_title(plt_titl[i]) - axs[1,c].set_xlabel(stat_word + '[{}]'.format(leg_titl[i])) - axs[1,c].set_ylabel(stat0_word) + lgnd.legendHandles[j]._sizes = [80] for i,stat in enumerate(['mean','amax']): @@ -112,4 +125,3 @@ def run_loop(i,stat): # Save plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) - From cab4dbbdb2a3ce6acde0aac10bb7f1a99ef7fe02 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 28 Aug 2023 15:09:52 +0900 Subject: [PATCH 0891/1472] missed a space --- utils/hist_per_GRU.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index b445bb09e..2c2352e6e 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -151,7 +151,7 @@ def run_loop(i,var,mx): axs[r,c].legend() axs[r,c].set_title(plt_titl[i] + stat_word) - axs[r,c].set_xlabel(stat_word + '[{}]'.format(leg_titl[i])) + axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl[i])) axs[r,c].set_ylabel('GRU count') From 631f95dc8a8b5d0d9cc0802971a8327412bb42cc Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 28 Aug 2023 18:11:06 +0900 Subject: [PATCH 0892/1472] plotting, make efficiency use a flag-- doesn't seem to help wall clocks. Turn off efficiency multiplication on wall clocks. Cut off 0 on SWE hist to make more readable. --- utils/hist_per_GRU.py | 23 +++++++++++++---------- utils/input_per_GRU.py | 22 ++++++++++++---------- utils/plot_per_GRU.py | 22 ++++++++++------------ utils/scat_per_GRU.py | 4 ++-- 4 files changed, 37 insertions(+), 34 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 2c2352e6e..c2e2214cf 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -23,6 +23,7 @@ viz_dir = Path('/home/avanb/scratch/statistics') nbatch_hrus = 518 # number of HRUs per batch num_bins = 1000 +use_eff = False # use efficiency in wall clock time testing = False if testing: @@ -46,7 +47,7 @@ # Specify variables of interest plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] -plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] +plt_titl = ['(a) Snow Water Equivalent >0','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] #fig_fil = '{}_hrly_diff_hist_{}_{}_zoom_compressed.png' @@ -63,13 +64,14 @@ for i, m in enumerate(method_name): # Get the aggregated statistics of SUMMA simulations summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) - # Read the data from the eff.txt file into a DataFrame - eff[m] = pd.read_csv(viz_dir/eff_fil[i], sep=',', header=None, names=['CPU Efficiency', 'Array ID', 'Job Wall-clock time', 'Node Number']) - # Extract only the values after the ':' character in the 'CPU Efficiency', 'Job Wall-clock time', and 'Node Number' columns - eff[m]['CPU Efficiency'] = eff[m]['CPU Efficiency'].str.split(':').str[1].astype(float) - eff[m]['Array ID'] = eff[m]['Array ID'].str.split(':').str[1].astype(int) - eff[m]['Job Wall-clock time'] = eff[m]['Job Wall-clock time'].str.split(':').str[1].astype(float) - eff[m]['Node Number'] = eff[m]['Node Number'].str.split(':').str[1].astype(int) + if use_eff: + # Read the data from the eff.txt file into a DataFrame + eff[m] = pd.read_csv(viz_dir/eff_fil[i], sep=',', header=None, names=['CPU Efficiency', 'Array ID', 'Job Wall-clock time', 'Node Number']) + # Extract only the values after the ':' character in the 'CPU Efficiency', 'Job Wall-clock time', and 'Node Number' columns + eff[m]['CPU Efficiency'] = eff[m]['CPU Efficiency'].str.split(':').str[1].astype(float) + eff[m]['Array ID'] = eff[m]['Array ID'].str.split(':').str[1].astype(int) + eff[m]['Job Wall-clock time'] = eff[m]['Job Wall-clock time'].str.split(':').str[1].astype(float) + eff[m]['Node Number'] = eff[m]['Node Number'].str.split(':').str[1].astype(int) ##Figure @@ -112,7 +114,7 @@ def run_loop(i,var,mx): # Data for m in method_name: s = summa[m][var].sel(stat=stat0) - if var == 'wallClockTime': + if var == 'wallClockTime' and use_eff: batch = np.floor(np.arange(len(s.indexes['hru'])) /nbatch_hrus) #basin_num = np.arange(len(s.indexes['hru'])) % nbatch_hrus #not currently using # Create a dictionary to store the values for each batch @@ -135,6 +137,7 @@ def run_loop(i,var,mx): if stat == 'maxe': s = np.fabs(s) # make absolute value norm range = (0,mx) + if var == 'scalarSWE': range = (1.e-10,mx) # SWE has a lot of zeros if stat=='kgem' and var!='wallClockTime' : range = (mn,1) s.plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=m,linewidth=2.0,range=range) @@ -147,7 +150,7 @@ def run_loop(i,var,mx): if var == 'wallClockTime': if stat == 'rmse' or stat == 'kgem': stat_word = ' Hourly mean' if stat == 'maxe': stat_word = ' Hourly max' - #axs[r,c].set_yscale('log') #log y axis for wall clock time to exaggerate peaks + axs[r,c].set_yscale('log') #log y axis for wall clock time to exaggerate peaks axs[r,c].legend() axs[r,c].set_title(plt_titl[i] + stat_word) diff --git a/utils/input_per_GRU.py b/utils/input_per_GRU.py index 58ed2c9c6..543dfd12b 100644 --- a/utils/input_per_GRU.py +++ b/utils/input_per_GRU.py @@ -162,7 +162,8 @@ def make_default_path(suffix): hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile s0 = summa['wallClockTime'].sel(stat='mean') s1 = summa['wallClockTime'].sel(stat='amax') -batch = np.floor(np.arange(len(s0.indexes['hru'])) /nbatch_hrus) +batch0 = np.floor(np.arange(len(s0.indexes['hru'])) /nbatch_hrus) +batch1 = np.floor(np.arange(len(s1.indexes['hru'])) /nbatch_hrus) #basin_num = np.arange(len(s0.indexes['hru'])) % nbatch_hrus #not currently using # Create a dictionary to store the values for each batch efficiency = {} @@ -170,15 +171,16 @@ def make_default_path(suffix): # Iterate over the rows in the data DataFrame for index, row in eff.iterrows(): # Extract the values from the row - batch0 = int(row['Array ID']) - eff0 = row['CPU Efficiency'] - node0 = row['Node Number'] + batch = int(row['Array ID']) + eff = row['CPU Efficiency'] + nod = row['Node Number'] # Store the value for the current batch in the dictionary - efficiency[batch0] = eff0 - node[batch0] = node0 + efficiency[batch] = eff + node[batch] = nod # Select the values for the current batch using boolean indexing -eff_batch = np.array([efficiency[b] for b in batch]) -node_batch = np.array([node[b] for b in batch]) +eff_batch0 = np.array([efficiency[b] for b in batch0]) +eff_batch1 = np.array([efficiency[b] for b in batch1]) +node_batch = np.array([node[b] for b in batch0]) for plot_var in plot_vars: if plot_var == 'batch': @@ -186,11 +188,11 @@ def make_default_path(suffix): if plot_var == 'node': s = s0*node_batch/s0 if plot_var == 'effMultWallClockTime': - s = s0*eff_batch + s = s0*eff_batch0 if plot_var == 'wallClockTime': s = s0 if plot_var == 'effWallClockMax': - s = s1*eff_batch + s = s1*eff_batch1 if plot_var == 'wallClockMax': s = s1 bas_albers[plot_var] = s.sel(hru=hru_ids_shp.values) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 66e1ae5a4..f17a31d1c 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -41,6 +41,7 @@ viz_fil = viz_fil.format(','.join(settings)) eff_fil = 'eff_' + method_name + '.txt' nbatch_hrus = 518 # number of HRUs per batch +use_eff = False # use efficiency in wall clock time # Specify variables of interest plot_vars = settings @@ -155,13 +156,14 @@ def make_default_path(suffix): ## Pre-processing, map SUMMA sims to catchment shapes # Get the aggregated statistics of SUMMA simulations summa = xr.open_dataset(viz_dir/viz_fil) -# Read the data from the eff.txt file into a DataFrame -eff = pd.read_csv(viz_dir/eff_fil, sep=',', header=None, names=['CPU Efficiency', 'Array ID', 'Job Wall-clock time', 'Node Number']) -# Extract only the values after the ':' character in the 'CPU Efficiency', 'Job Wall-clock time', and 'Node Number' columns -eff['CPU Efficiency'] = eff['CPU Efficiency'].str.split(':').str[1].astype(float) -eff['Array ID'] = eff['Array ID'].str.split(':').str[1].astype(int) -eff['Job Wall-clock time'] = eff['Job Wall-clock time'].str.split(':').str[1].astype(float) -eff['Node Number'] = eff['Node Number'].str.split(':').str[1].astype(int) +if use_eff: + # Read the data from the eff.txt file into a DataFrame + eff = pd.read_csv(viz_dir/eff_fil, sep=',', header=None, names=['CPU Efficiency', 'Array ID', 'Job Wall-clock time', 'Node Number']) + # Extract only the values after the ':' character in the 'CPU Efficiency', 'Job Wall-clock time', and 'Node Number' columns + eff['CPU Efficiency'] = eff['CPU Efficiency'].str.split(':').str[1].astype(float) + eff['Array ID'] = eff['Array ID'].str.split(':').str[1].astype(int) + eff['Job Wall-clock time'] = eff['Job Wall-clock time'].str.split(':').str[1].astype(float) + eff['Node Number'] = eff['Node Number'].str.split(':').str[1].astype(int) # Match the accummulated values to the correct HRU IDs in the shapefile hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile @@ -171,21 +173,18 @@ def make_default_path(suffix): if stat == 'rmse' or stat == 'kgem': stat0 = 'mean' if stat == 'maxe': stat0 = 'amax' s = summa[plot_var].sel(stat=stat0) - if plot_var == 'wallClockTime': + if plot_var == 'wallClockTime' and use_eff: batch = np.floor(np.arange(len(s.indexes['hru'])) /nbatch_hrus) #basin_num = np.arange(len(s.indexes['hru'])) % nbatch_hrus #not currently using # Create a dictionary to store the values for each batch efficiency = {} - node = {} # Iterate over the rows in the data DataFrame for index, row in eff.iterrows(): # Extract the values from the row batch0 = int(row['Array ID']) eff0 = row['CPU Efficiency'] - node0 = row['Node Number'] # Store the value for the current batch in the dictionary efficiency[batch0] = eff0 - node[batch0] = node0 # Select the values for the current batch using boolean indexing eff_batch = np.array([efficiency[b] for b in batch]) #node_batch = np.array([node[b] for b in batch]) #not currently using @@ -238,7 +237,6 @@ def run_loop(i,var,the_max,f_x,f_y): if stat=='kgem' and var!='wallClockTime': my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno')) # copy the default cmap my_cmap.set_bad(color='white') #nan color white - vmin,vmax = the_max, 1.0 norm=matplotlib.colors.Normalize(vmin=vmin, vmax=vmax) r = i//2 diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index 5948b1ba1..43c2ed798 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -22,6 +22,7 @@ viz_dir = Path('/home/avanb/scratch/statistics') nbatch_hrus = 518 # number of HRUs per batch +use_eff = False # use efficiency in wall clock time, still need files for the node number testing = False if testing: @@ -49,7 +50,6 @@ leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$num$'] leg_titl0 = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] - #fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' #fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) fig_fil = 'Hrly_diff_scat_{}_{}_compressed.png' @@ -116,7 +116,7 @@ def run_loop(i,var): eff_batch = np.array([efficiency[b] for b in batch]) node_batch = np.array([node[b] for b in batch]) #not currently using # Multiply the s values by efficiency - s = s*eff_batch + if use_eff: s = s*eff_batch axs[r,c].scatter(x=node_batch,y=s.sel(stat=stat0),s=1,zorder=0,label=m) stat_word = 'Node number' else: From 155f511ee187fd7297a246c1791b959513b1fc37 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 28 Aug 2023 21:47:22 +0900 Subject: [PATCH 0893/1472] fix batch plotting --- utils/input_per_GRU.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/utils/input_per_GRU.py b/utils/input_per_GRU.py index 543dfd12b..acdcea45a 100644 --- a/utils/input_per_GRU.py +++ b/utils/input_per_GRU.py @@ -162,8 +162,7 @@ def make_default_path(suffix): hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile s0 = summa['wallClockTime'].sel(stat='mean') s1 = summa['wallClockTime'].sel(stat='amax') -batch0 = np.floor(np.arange(len(s0.indexes['hru'])) /nbatch_hrus) -batch1 = np.floor(np.arange(len(s1.indexes['hru'])) /nbatch_hrus) +batch = np.floor(np.arange(len(s0.indexes['hru'])) /nbatch_hrus) #basin_num = np.arange(len(s0.indexes['hru'])) % nbatch_hrus #not currently using # Create a dictionary to store the values for each batch efficiency = {} @@ -171,16 +170,15 @@ def make_default_path(suffix): # Iterate over the rows in the data DataFrame for index, row in eff.iterrows(): # Extract the values from the row - batch = int(row['Array ID']) + batch0 = int(row['Array ID']) eff = row['CPU Efficiency'] nod = row['Node Number'] # Store the value for the current batch in the dictionary - efficiency[batch] = eff - node[batch] = nod + efficiency[batch0] = eff + node[batch0] = nod # Select the values for the current batch using boolean indexing -eff_batch0 = np.array([efficiency[b] for b in batch0]) -eff_batch1 = np.array([efficiency[b] for b in batch1]) -node_batch = np.array([node[b] for b in batch0]) +eff_batch = np.array([efficiency[b] for b in batch]) +node_batch = np.array([node[b] for b in batch]) for plot_var in plot_vars: if plot_var == 'batch': @@ -188,11 +186,11 @@ def make_default_path(suffix): if plot_var == 'node': s = s0*node_batch/s0 if plot_var == 'effMultWallClockTime': - s = s0*eff_batch0 + s = s0*eff_batch if plot_var == 'wallClockTime': s = s0 - if plot_var == 'effWallClockMax': - s = s1*eff_batch1 + if plot_var == 'effMultWallClockMax': + s = s1*eff_batch if plot_var == 'wallClockMax': s = s1 bas_albers[plot_var] = s.sel(hru=hru_ids_shp.values) From 7c862280a7940fbc9a0c45464de8e69b99d1125a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 29 Aug 2023 14:33:53 +0900 Subject: [PATCH 0894/1472] some comments and deallocates that should be in there --- build/source/engine/varSubstep.f90 | 6 +++++- build/source/engine/vegPhenlgy.f90 | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 6e85168e1..bee964506 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -357,7 +357,10 @@ subroutine varSubstep(& ! if too much melt or need to reduce length of the coupled step then return ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry - if(tooMuchMelt .or. reduceCoupledStep) return + if(tooMuchMelt .or. reduceCoupledStep)then + deallocate(sumLayerCompress) + return + endif ! identify failure failedSubstep = (err<0) @@ -431,6 +434,7 @@ subroutine varSubstep(& if(waterBalanceError)then message=trim(message)//'water balance error' reduceCoupledStep=.true. + deallocate(sumLayerCompress) err=-20; return endif diff --git a/build/source/engine/vegPhenlgy.f90 b/build/source/engine/vegPhenlgy.f90 index 6e6d5c1c5..27ac7651d 100644 --- a/build/source/engine/vegPhenlgy.f90 +++ b/build/source/engine/vegPhenlgy.f90 @@ -119,7 +119,7 @@ subroutine vegPhenlgy(& ! model state variables scalarSnowDepth => prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(in): [dp] snow depth on the ground surface (m) scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1), & ! intent(in): [dp] temperature of the vegetation canopy at the start of the sub-step (K) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1), & ! intent(in): [dp] liquid water in the vegetation canopy at the start of the sub-step + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1), & ! intent(inout): [dp] liquid water in the vegetation canopy at the start of the sub-step ! diagnostic variables and parameters (input) heightCanopyTop => mpar_data%var(iLookPARAM%heightCanopyTop)%dat(1), & ! intent(in): [dp] height of the top of the canopy layer (m) heightCanopyBottom => mpar_data%var(iLookPARAM%heightCanopyBottom)%dat(1), & ! intent(in): [dp] height of the bottom of the canopy layer (m) From 514ac60d1ccf99f7ad91b8778728de956a5dedd6 Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 29 Aug 2023 03:17:03 -0600 Subject: [PATCH 0895/1472] Removed defunct data_bin derived type from data_types.f90 --- build/source/dshare/data_types.f90 | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 3ac990ef5..a58292847 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -300,22 +300,7 @@ MODULE data_types endtype gru_i ! define derived types used to simplify passing subroutine arguments - ! ** arrays for holding arguments of different intrinsic types - type, public :: data_array - logical(lgt), allocatable :: lgt(:) ! vector of logical arguments - real(rkind), allocatable :: rkind(:) ! vector of rkind arguments - real(rkind), allocatable :: rmatrix(:,:) ! matrix of rkind arguments - integer(i4b), allocatable :: i4b(:) ! vector of i4b integer arguments - character(:), allocatable :: string ! character string arguments - end type data_array - ! ** derived type used to hold data passed as subroutine arguments - type, public :: data_bin ! x%bin(:)%{lgt(:),i4b(:),rkind(:),rmatrix(:,:),string}, x%err [i4b], x%msg [character] - type(data_array), allocatable :: bin(:) ! allocatable number of data bins - integer(i4b) :: err ! error code - character(:), allocatable :: msg ! error message - end type data_bin - - ! testing SJT + ! ** ssdNrgFlux type, public :: in_type_ssdNrgFlux ! derived type for intent(in) arguments in ssdNrgFlux call logical(lgt) :: flag real(rkind) :: scalarGroundNetNrgFlux @@ -342,7 +327,7 @@ MODULE data_types integer(i4b) :: err character(:),allocatable :: cmessage end type out_type_ssdNrgFlux - ! end testing SJT + ! ** end ssdNrgFlux END MODULE data_types From b0a5c1205e4679d2128f8c5a06f8bc84d6bd1a48 Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 29 Aug 2023 04:47:01 -0600 Subject: [PATCH 0896/1472] Improved comments for ssdNrgFlux derived types and added vegNrgFlux intent(in) derived type. --- build/source/dshare/data_types.f90 | 51 +++++++++++++++++++----------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index a58292847..8ce684126 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -300,32 +300,47 @@ MODULE data_types endtype gru_i ! define derived types used to simplify passing subroutine arguments + ! ** vegNrgFlux + type, public :: in_type_vegNrgFlux ! derived type for intent(in) arguments in vegNrgFlux call + logical(lgt) :: firstSubStep ! intent(in): flag to indicate if we are processing the first sub-step + logical(lgt) :: firstFluxCall ! intent(in): flag to indicate if we are processing the first flux call + logical(lgt) :: computeVegFlux ! intent(in): flag to indicate if we need to compute fluxes over vegetation + logical(lgt) :: checkLWBalance ! intent(in): flag to check longwave balance + real(rkind) :: upperBoundTemp ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature + real(rkind) :: scalarCanairTempTrial ! intent(in): trial value of the canopy air space temperature (K) + real(rkind) :: scalarCanopyTempTrial ! intent(in): trial value of canopy temperature (K) + real(rkind) :: mLayerTempTrial_1 ! intent(in): trial value of ground temperature (K) + real(rkind) :: scalarCanopyIceTrial ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyLiqTrial ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: dCanLiq_dTcanopy ! intent(in): derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) + end type in_type_vegNrgFlux + ! ** end vegNrgFlux + ! ** ssdNrgFlux type, public :: in_type_ssdNrgFlux ! derived type for intent(in) arguments in ssdNrgFlux call - logical(lgt) :: flag - real(rkind) :: scalarGroundNetNrgFlux - real(rkind) :: dGroundNetFlux_dGroundTemp - real(rkind), allocatable :: iLayerLiqFluxSnow(:) - real(rkind), allocatable :: iLayerLiqFluxSoil(:) - real(rkind), allocatable :: mLayerTempTrial(:) - real(rkind), allocatable :: dThermalC_dWatAbove(:) - real(rkind), allocatable :: dThermalC_dWatBelow(:) - real(rkind), allocatable :: dThermalC_dTempAbove(:) - real(rkind), allocatable :: dThermalC_dTempBelow(:) + logical(lgt) :: scalarSolution ! intent(in): flag to denote if implementing the scalar solution + real(rkind) :: scalarGroundNetNrgFlux ! intent(in): net energy flux for the ground surface (W m-2) + real(rkind), allocatable :: iLayerLiqFluxSnow(:) ! intent(in): liquid flux at the interface of each snow layer (m s-1) + real(rkind), allocatable :: iLayerLiqFluxSoil(:) ! intent(in): liquid flux at the interface of each soil layer (m s-1) + real(rkind), allocatable :: mLayerTempTrial(:) ! intent(in): temperature in each layer at the current iteration (m) + real(rkind), allocatable :: dThermalC_dWatAbove(:) ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above + real(rkind), allocatable :: dThermalC_dWatBelow(:) ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above + real(rkind), allocatable :: dThermalC_dTempAbove(:) ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above + real(rkind), allocatable :: dThermalC_dTempBelow(:) ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above end type in_type_ssdNrgFlux type, public :: io_type_ssdNrgFlux ! derived type for intent(inout) arguments in ssdNrgFlux call - real(rkind) :: dGroundNetFlux_dGroundTemp + real(rkind) :: dGroundNetFlux_dGroundTemp ! intent(inout): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) end type io_type_ssdNrgFlux type, public :: out_type_ssdNrgFlux ! derived type for intent(inout) arguments in ssdNrgFlux call - real(rkind), allocatable :: iLayerNrgFlux(:) - real(rkind), allocatable :: dNrgFlux_dTempAbove(:) - real(rkind), allocatable :: dNrgFlux_dTempBelow(:) - real(rkind), allocatable :: dNrgFlux_dWatAbove(:) - real(rkind), allocatable :: dNrgFlux_dWatBelow(:) - integer(i4b) :: err - character(:),allocatable :: cmessage + real(rkind), allocatable :: iLayerNrgFlux(:) ! intent(out): energy flux at the layer interfaces (W m-2) + real(rkind), allocatable :: dNrgFlux_dTempAbove(:) ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) + real(rkind), allocatable :: dNrgFlux_dTempBelow(:) ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) + real(rkind), allocatable :: dNrgFlux_dWatAbove(:) ! intent(out): derivatives in the flux w.r.t. water state in the layer above (J m-2 s-1 K-1) + real(rkind), allocatable :: dNrgFlux_dWatBelow(:) ! intent(out): derivatives in the flux w.r.t. water state in the layer below (J m-2 s-1 K-1) + integer(i4b) :: err ! intent(out): error code + character(:),allocatable :: cmessage ! intent(out): error message end type out_type_ssdNrgFlux ! ** end ssdNrgFlux From 22da22dc42d44179f27ae9a850a5ecd696553755 Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 29 Aug 2023 04:48:32 -0600 Subject: [PATCH 0897/1472] Improved comments related to ssdNrgFlux argument derived types. --- build/source/engine/ssdNrgFlux.f90 | 35 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index 31a2ac175..92e2a2341 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -122,26 +122,25 @@ subroutine ssdNrgFlux(& ! make association of local variables with information in the data structures associate(& ! input: model control - scalarSolution => in_ssdNrgFlux % flag, & ! flag to denote if implementing the scalar solution + scalarSolution => in_ssdNrgFlux % scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution ! input: fluxes and derivatives at the upper boundary - groundNetFlux => in_ssdNrgFlux % scalarGroundNetNrgFlux, & ! net energy flux for the ground surface (W m-2) - dGroundNetFlux_dGroundTemp => io_ssdNrgFlux % dGroundNetFlux_dGroundTemp, & ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + groundNetFlux => in_ssdNrgFlux % scalarGroundNetNrgFlux, & ! intent(in): net energy flux for the ground surface (W m-2) + dGroundNetFlux_dGroundTemp => io_ssdNrgFlux % dGroundNetFlux_dGroundTemp, & ! intent(inout): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) ! input: liquid water fluxes - iLayerLiqFluxSnow => in_ssdNrgFlux % iLayerLiqFluxSnow, & ! liquid flux at the interface of each snow layer (m s-1) - iLayerLiqFluxSoil => in_ssdNrgFlux % iLayerLiqFluxSoil, & ! liquid flux at the interface of each soil layer (m s-1) + iLayerLiqFluxSnow => in_ssdNrgFlux % iLayerLiqFluxSnow, & ! intent(in): liquid flux at the interface of each snow layer (m s-1) + iLayerLiqFluxSoil => in_ssdNrgFlux % iLayerLiqFluxSoil, & ! intent(in): liquid flux at the interface of each soil layer (m s-1) ! input: trial model state variables - mLayerTempTrial => in_ssdNrgFlux % mLayerTempTrial, & ! temperature in each layer at the current iteration (m) + mLayerTempTrial => in_ssdNrgFlux % mLayerTempTrial, & ! intent(in): temperature in each layer at the current iteration (m) ! input: derivatives - dThermalC_dWatAbove => in_ssdNrgFlux % dThermalC_dWatAbove, & ! derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dWatBelow => in_ssdNrgFlux % dThermalC_dWatBelow, & ! derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dTempAbove => in_ssdNrgFlux % dThermalC_dTempAbove, & ! derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dTempBelow => in_ssdNrgFlux % dThermalC_dTempBelow, & ! derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dWatAbove => in_ssdNrgFlux % dThermalC_dWatAbove, & ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow => in_ssdNrgFlux % dThermalC_dWatBelow, & ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dTempAbove => in_ssdNrgFlux % dThermalC_dTempAbove, & ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow => in_ssdNrgFlux % dThermalC_dTempBelow, & ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above ! input: boundary conditions ix_bcUpprTdyn => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision, & ! intent(in): method used to calculate the upper boundary condition for thermodynamics ix_bcLowrTdyn => model_decisions(iLookDECISIONS%bcLowrTdyn)%iDecision, & ! intent(in): method used to calculate the lower boundary condition for thermodynamics ! input: coordinate variables nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - !nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat, & ! intent(in): index in the state subset for energy state variables in the snow+soil domain @@ -155,14 +154,14 @@ subroutine ssdNrgFlux(& iLayerConductiveFlux => flux_data%var(iLookFLUX%iLayerConductiveFlux)%dat, & ! intent(out): conductive energy flux at layer interfaces at end of time step (W m-2) iLayerAdvectiveFlux => flux_data%var(iLookFLUX%iLayerAdvectiveFlux)%dat, & ! intent(out): advective energy flux at layer interfaces at end of time step (W m-2) ! output: fluxes and derivatives at all layer interfaces - iLayerNrgFlux => out_ssdNrgFlux % iLayerNrgFlux, & ! energy flux at the layer interfaces (W m-2) - dFlux_dTempAbove => out_ssdNrgFlux % dNrgFlux_dTempAbove, & ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) - dFlux_dTempBelow => out_ssdNrgFlux % dNrgFlux_dTempBelow, & ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) - dFlux_dWatAbove => out_ssdNrgFlux % dNrgFlux_dWatAbove, & ! derivatives in the flux w.r.t. water state in the layer above (J m-2 s-1 K-1) - dFlux_dWatBelow => out_ssdNrgFlux % dNrgFlux_dWatBelow, & ! derivatives in the flux w.r.t. water state in the layer below (J m-2 s-1 K-1) + iLayerNrgFlux => out_ssdNrgFlux % iLayerNrgFlux, & ! intent(out): energy flux at the layer interfaces (W m-2) + dFlux_dTempAbove => out_ssdNrgFlux % dNrgFlux_dTempAbove, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) + dFlux_dTempBelow => out_ssdNrgFlux % dNrgFlux_dTempBelow, & ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) + dFlux_dWatAbove => out_ssdNrgFlux % dNrgFlux_dWatAbove, & ! intent(out): derivatives in the flux w.r.t. water state in the layer above (J m-2 s-1 K-1) + dFlux_dWatBelow => out_ssdNrgFlux % dNrgFlux_dWatBelow, & ! intent(out): derivatives in the flux w.r.t. water state in the layer below (J m-2 s-1 K-1) ! output: error control - err => out_ssdNrgFlux % err, & ! error code - message => out_ssdNrgFlux % cmessage & ! error message + err => out_ssdNrgFlux % err, & ! intent(out): error code + message => out_ssdNrgFlux % cmessage & ! intent(out): error message ) ! end association of local variables with information in the data structures ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! initialize error control From e15b94cabb88f18e3d0beb47f07dec4587f4c510 Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 29 Aug 2023 05:15:14 -0600 Subject: [PATCH 0898/1472] Added intent(in) assignment statements for vegNrgFlux in subTools. --- build/source/engine/computFlux.f90 | 70 ++++++++++++++++++------------ 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index fb14c236c..876de817b 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -25,14 +25,15 @@ module computFlux_module ! provide access to the derived types to define the data structures USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - model_options, & ! defines the model decisions - in_type_ssdNrgFlux,& ! intent(in) arguments for ssdNrgFlux call - io_type_ssdNrgFlux,& ! intent(inout) arguments for ssdNrgFlux call - out_type_ssdNrgFlux ! intent(inout) arguments for ssdNrgFlux call + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (rkind) + model_options, & ! defines the model decisions + in_type_vegNrgFlux, & ! intent(in) arguments for vegNrgFlux call + in_type_ssdNrgFlux, & ! intent(in) arguments for ssdNrgFlux call + io_type_ssdNrgFlux, & ! intent(inout) arguments for ssdNrgFlux call + out_type_ssdNrgFlux ! intent(out) arguments for ssdNrgFlux call ! indices that define elements of the data structures USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure @@ -219,6 +220,7 @@ subroutine computFlux(& type(in_type_ssdNrgFlux) :: in_ssdNrgFlux ! data structure for ssdNrgFlux arguments type(io_type_ssdNrgFlux) :: io_ssdNrgFlux ! data structure for ssdNrgFlux arguments type(out_type_ssdNrgFlux) :: out_ssdNrgFlux ! data structure for ssdNrgFlux arguments + type(in_type_vegNrgFlux) :: in_vegNrgFlux ! data structure for ssdNrgFlux arguments ! -------------------------------------------------------------- ! initialize error control err=0; message='computFlux/' @@ -1029,33 +1031,47 @@ subroutine subTools(op,sub) select case(sub) case(iLookROUTINE%vegNrgFlux) ! vegNrgFlux + if (op==iLookOP%pre) then ! pre-processing + ! intent(in) arguments + in_vegNrgFlux % firstSubStep=firstSubStep ! intent(in): flag to indicate if we are processing the first sub-step + in_vegNrgFlux % firstFluxCall=firstFluxCall ! intent(in): flag to indicate if we are processing the first flux call + in_vegNrgFlux % computeVegFlux=computeVegFlux ! intent(in): flag to indicate if we need to compute fluxes over vegetation + in_vegNrgFlux % checkLWBalance=checkLWBalance ! intent(in): flag to check longwave balance + in_vegNrgFlux % upperBoundTemp=upperBoundTemp ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature + in_vegNrgFlux % scalarCanairTempTrial=scalarCanairTempTrial ! intent(in): trial value of the canopy air space temperature (K) + in_vegNrgFlux % scalarCanopyTempTrial=scalarCanopyTempTrial ! intent(in): trial value of canopy temperature (K) + in_vegNrgFlux % mLayerTempTrial_1=mLayerTempTrial(1) ! intent(in): trial value of ground temperature (K) + in_vegNrgFlux % scalarCanopyIceTrial=scalarCanopyIceTrial ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) + in_vegNrgFlux % scalarCanopyLiqTrial=scalarCanopyLiqTrial ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) + in_vegNrgFlux % dCanLiq_dTcanopy=dCanLiq_dTcanopy ! intent(in): derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) + else ! post-processing + end if case(iLookROUTINE%ssdNrgFlux) ! ssdNrgFlux if (op==iLookOP%pre) then ! pre-processing ! intent(in) arguments - in_ssdNrgFlux % flag=scalarSolution .and. .not.firstFluxCall - in_ssdNrgFlux % scalarGroundNetNrgFlux=scalarGroundNetNrgFlux - in_ssdNrgFlux % dGroundNetFlux_dGroundTemp=dGroundNetFlux_dGroundTemp - in_ssdNrgFlux % iLayerLiqFluxSnow=iLayerLiqFluxSnow - in_ssdNrgFlux % iLayerLiqFluxSoil=iLayerLiqFluxSoil - in_ssdNrgFlux % mLayerTempTrial=mLayerTempTrial - in_ssdNrgFlux % dThermalC_dWatAbove=dThermalC_dWatAbove - in_ssdNrgFlux % dThermalC_dWatBelow=dThermalC_dWatBelow - in_ssdNrgFlux % dThermalC_dTempAbove=dThermalC_dTempAbove - in_ssdNrgFlux % dThermalC_dTempBelow=dThermalC_dTempBelow + in_ssdNrgFlux % scalarSolution=scalarSolution .and. .not.firstFluxCall ! intent(in): flag to denote if implementing the scalar solution + in_ssdNrgFlux % scalarGroundNetNrgFlux=scalarGroundNetNrgFlux ! intent(in): net energy flux for the ground surface (W m-2) + in_ssdNrgFlux % iLayerLiqFluxSnow=iLayerLiqFluxSnow ! intent(in): liquid flux at the interface of each snow layer (m s-1) + in_ssdNrgFlux % iLayerLiqFluxSoil=iLayerLiqFluxSoil ! intent(in): liquid flux at the interface of each soil layer (m s-1) + in_ssdNrgFlux % mLayerTempTrial=mLayerTempTrial ! intent(in): temperature in each layer at the current iteration (m) + in_ssdNrgFlux % dThermalC_dWatAbove=dThermalC_dWatAbove ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above + in_ssdNrgFlux % dThermalC_dWatBelow=dThermalC_dWatBelow ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above + in_ssdNrgFlux % dThermalC_dTempAbove=dThermalC_dTempAbove ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above + in_ssdNrgFlux % dThermalC_dTempBelow=dThermalC_dTempBelow ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above ! intent(inout) arguments - io_ssdNrgFlux % dGroundNetFlux_dGroundTemp=dGroundNetFlux_dGroundTemp + io_ssdNrgFlux % dGroundNetFlux_dGroundTemp=dGroundNetFlux_dGroundTemp ! intent(inout): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) else ! post-processing ! intent(inout) arguments - dGroundNetFlux_dGroundTemp=io_ssdNrgFlux % dGroundNetFlux_dGroundTemp + dGroundNetFlux_dGroundTemp=io_ssdNrgFlux % dGroundNetFlux_dGroundTemp ! intent(inout): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) ! intent(out) arguments - iLayerNrgFlux =out_ssdNrgFlux % iLayerNrgFlux - dNrgFlux_dTempAbove=out_ssdNrgFlux % dNrgFlux_dTempAbove - dNrgFlux_dTempBelow=out_ssdNrgFlux % dNrgFlux_dTempBelow - dNrgFlux_dWatAbove =out_ssdNrgFlux % dNrgFlux_dWatAbove - dNrgFlux_dWatBelow =out_ssdNrgFlux % dNrgFlux_dWatBelow - err =out_ssdNrgFlux % err - cmessage =out_ssdNrgFlux % cmessage + iLayerNrgFlux =out_ssdNrgFlux % iLayerNrgFlux ! intent(out): energy flux at the layer interfaces (W m-2) + dNrgFlux_dTempAbove=out_ssdNrgFlux % dNrgFlux_dTempAbove ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) + dNrgFlux_dTempBelow=out_ssdNrgFlux % dNrgFlux_dTempBelow ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) + dNrgFlux_dWatAbove =out_ssdNrgFlux % dNrgFlux_dWatAbove ! intent(out): derivatives in the flux w.r.t. water state in the layer above (J m-2 s-1 K-1) + dNrgFlux_dWatBelow =out_ssdNrgFlux % dNrgFlux_dWatBelow ! intent(out): derivatives in the flux w.r.t. water state in the layer below (J m-2 s-1 K-1) + err =out_ssdNrgFlux % err ! intent(out): error code + cmessage =out_ssdNrgFlux % cmessage ! intent(out): error message !deallocate(in_ssdNrgFlux,io_ssdNrgFlux,out_ssdNrgFlux) -- update ! error control if (err/=0) then; message=trim(message)//trim(cmessage); return; end if From 5f5a6f1ab61f4f75119be881c25fb5673f0b3917 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 30 Aug 2023 10:36:07 +0900 Subject: [PATCH 0899/1472] make order of terms in residual more like if were prime, makes a difference --- build/source/engine/computResid.f90 | 22 ++++++++++---------- build/source/engine/computResidWithPrime.f90 | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/build/source/engine/computResid.f90 b/build/source/engine/computResid.f90 index dde88bee5..920524576 100644 --- a/build/source/engine/computResid.f90 +++ b/build/source/engine/computResid.f90 @@ -202,15 +202,15 @@ subroutine computResid(& rAdd(:) = 0._rkind ! compute energy associated with melt freeze for the vegetation canopy - if(ixVegNrg/=integerMissing) rAdd(ixVegNrg) = rAdd(ixVegNrg) + LH_fus*(scalarCanopyIceTrial - scalarCanopyIce)/canopyDepth ! energy associated with melt/freeze (J m-3) + if(ixVegNrg/=integerMissing) rAdd(ixVegNrg) = rAdd(ixVegNrg) + LH_fus*( scalarCanopyIceTrial - scalarCanopyIce )/canopyDepth ! energy associated with melt/freeze (J m-3) ! compute energy associated with melt/freeze for snow ! NOTE: allow expansion of ice during melt-freeze for snow; deny expansion of ice during melt-freeze for soil if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) select case( layerType(iLayer) ) - case(iname_snow); rAdd( ixSnowSoilNrg(iLayer) ) = rAdd( ixSnowSoilNrg(iLayer) ) + LH_fus*iden_ice *(mLayerVolFracIceTrial(iLayer) - mLayerVolFracIce(iLayer)) - case(iname_soil); rAdd( ixSnowSoilNrg(iLayer) ) = rAdd( ixSnowSoilNrg(iLayer) ) + LH_fus*iden_water*(mLayerVolFracIceTrial(iLayer) - mLayerVolFracIce(iLayer)) + case(iname_snow); rAdd( ixSnowSoilNrg(iLayer) ) = rAdd( ixSnowSoilNrg(iLayer) ) + LH_fus*iden_ice *( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIce(iLayer) ) + case(iname_soil); rAdd( ixSnowSoilNrg(iLayer) ) = rAdd( ixSnowSoilNrg(iLayer) ) + LH_fus*iden_water*( mLayerVolFracIceTrial(iLayer) - mLayerVolFracIce(iLayer) ) end select end do ! looping through non-missing energy state variables in the snow+soil domain endif @@ -222,7 +222,7 @@ subroutine computResid(& ! NOTE 4: same sink terms for matric head and liquid matric potential if(nSoilOnlyHyd>0)then do concurrent (iLayer=1:nSoil,ixSoilOnlyHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) - rAdd( ixSoilOnlyHyd(iLayer) ) = rAdd( ixSoilOnlyHyd(iLayer) ) + dt*(mLayerTranspire(iLayer) - mLayerBaseflow(iLayer) )/mLayerDepth(iLayer+nSnow) - dt*mLayerCompress(iLayer) + rAdd( ixSoilOnlyHyd(iLayer) ) = rAdd( ixSoilOnlyHyd(iLayer) ) + ( ( mLayerTranspire(iLayer) - mLayerBaseflow(iLayer) )/mLayerDepth(iLayer+nSnow) - mLayerCompress(iLayer) )*dt end do ! looping through non-missing energy state variables in the snow+soil domain endif @@ -233,9 +233,9 @@ subroutine computResid(& ! compute the residual vector for the vegetation canopy ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision ! --> energy balance - if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*scalarCanairTempTrial - ( sMul(ixCasNrg)*scalarCanairTemp + fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) - if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg)*scalarCanopyTempTrial + scalarCanopyCmTrial * scalarCanopyWatTrial/canopyDepth & - - ( sMul(ixVegNrg)*scalarCanopyTemp + scalarCanopyCmTrial * scalarCanopyWat/canopyDepth + fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) + if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*( scalarCanairTempTrial - scalarCanairTemp ) - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg)*( scalarCanopyTempTrial - scalarCanopyTemp ) + scalarCanopyCmTrial*( scalarCanopyWatTrial - scalarCanopyWat )/canopyDepth & + - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) ! --> mass balance if(ixVegHyd/=integerMissing)then scalarCanopyHydTrial = merge(scalarCanopyWatTrial, scalarCanopyLiqTrial, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) @@ -246,8 +246,8 @@ subroutine computResid(& ! compute the residual vector for the snow and soil sub-domains for energy if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) )*mLayerTempTrial(iLayer) + mLayerCmTrial(iLayer) * mLayerVolFracWatTrial(iLayer) & - - ( sMul( ixSnowSoilNrg(iLayer) )*mLayerTemp(iLayer) + mLayerCmTrial(iLayer) * mLayerVolFracWat(iLayer) + fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) + rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) )*( mLayerTempTrial(iLayer) - mLayerTemp(iLayer) ) + mLayerCmTrial(iLayer)*( mLayerVolFracWatTrial(iLayer) - mLayerVolFracWat(iLayer) ) & + - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) end do ! looping through non-missing energy state variables in the snow+soil domain endif @@ -259,12 +259,12 @@ subroutine computResid(& mLayerVolFracHydTrial(iLayer) = merge(mLayerVolFracWatTrial(iLayer), mLayerVolFracLiqTrial(iLayer) , (ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_matLayer) ) mLayerVolFracHyd(iLayer) = merge(mLayerVolFracWat(iLayer), mLayerVolFracLiq(iLayer), (ixHydType(iLayer)==iname_watLayer .or. ixHydType(iLayer)==iname_matLayer) ) ! (compute the residual) - rVec( ixSnowSoilHyd(iLayer) ) = mLayerVolFracHydTrial(iLayer) - ( mLayerVolFracHyd(iLayer) + fVec( ixSnowSoilHyd(iLayer) )*dt + rAdd( ixSnowSoilHyd(iLayer) ) ) + rVec( ixSnowSoilHyd(iLayer) ) = ( mLayerVolFracHydTrial(iLayer) - mLayerVolFracHyd(iLayer) ) - ( fVec( ixSnowSoilHyd(iLayer) )*dt + rAdd( ixSnowSoilHyd(iLayer) ) ) end do ! looping through non-missing energy state variables in the snow+soil domain endif ! compute the residual vector for the aquifer - if(ixAqWat/=integerMissing) rVec(ixAqWat) = sMul(ixAqWat)*scalarAquiferStorageTrial - ( sMul(ixAqWat)*scalarAquiferStorage + fVec(ixAqWat)*dt + rAdd(ixAqWat) ) + if(ixAqWat/=integerMissing) rVec(ixAqWat) = sMul(ixAqWat)*( scalarAquiferStorageTrial - scalarAquiferStorage ) - ( fVec(ixAqWat)*dt + rAdd(ixAqWat) ) if(globalPrintFlag)then write(*,'(a,1x,100(e12.5,1x))') 'rVec = ', rVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) write(*,'(a,1x,100(e12.5,1x))') 'fVec = ', fVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) diff --git a/build/source/engine/computResidWithPrime.f90 b/build/source/engine/computResidWithPrime.f90 index ec2f4b500..d5975895a 100644 --- a/build/source/engine/computResidWithPrime.f90 +++ b/build/source/engine/computResidWithPrime.f90 @@ -192,7 +192,7 @@ subroutine computResidWithPrime(& ! NOTE 4: same sink terms for matric head and liquid matric potential if(nSoilOnlyHyd>0)then do concurrent (iLayer=1:nSoil,ixSoilOnlyHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) - rAdd( ixSoilOnlyHyd(iLayer) ) = rAdd( ixSoilOnlyHyd(iLayer) ) + dt*(mLayerTranspire(iLayer) - mLayerBaseflow(iLayer) )/mLayerDepth(iLayer+nSnow) - dt*mLayerCompress(iLayer) + rAdd( ixSoilOnlyHyd(iLayer) ) = rAdd( ixSoilOnlyHyd(iLayer) ) + ( ( mLayerTranspire(iLayer) - mLayerBaseflow(iLayer) )/mLayerDepth(iLayer+nSnow) - mLayerCompress(iLayer) )*dt end do ! looping through non-missing energy state variables in the snow+soil domain endif From 0585f90f2f512fce07c97ecc8cb1c00d3b5f1795 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 30 Aug 2023 12:48:14 +0900 Subject: [PATCH 0900/1472] utilities --- utils/wallClock_per_GRU.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/utils/wallClock_per_GRU.py b/utils/wallClock_per_GRU.py index cc46ca9b5..012aedd71 100644 --- a/utils/wallClock_per_GRU.py +++ b/utils/wallClock_per_GRU.py @@ -22,13 +22,14 @@ viz_dir = Path('/home/avanb/scratch/statistics') nbatch_hrus = 518 # number of HRUs per batch -testing = False +testing = True if testing: viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') method_name=['be1','be64','sundials_1en6'] #maybe make this an argument else: import sys method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument + method_name=['be1','be16','be32'] #sundials will not show node differences as much # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] @@ -108,16 +109,18 @@ def run_loop(c,stat): if r == 0: axs[r,c].scatter(x=x,y=basin_num,s=1,zorder=0,label=m) stat0_word ='Basin number in batch' + lgnd0 = axs[r,c].legend() if r == 1: axs[r,c].scatter(x=x,y=node_batch,s=1,zorder=0,label=m) stat0_word ='Node number' + lgnd1 = axs[r,c].legend() axs[r,c].set_title(plt_titl[r*2+c]) axs[r,c].set_xlabel(stat_word + '[{}]'.format(leg_titl[r*2+c])) axs[r,c].set_ylabel(stat0_word) - lgnd = axs[r,c].legend() for j, m in enumerate(method_name): - lgnd.legendHandles[j]._sizes = [80] + lgnd0.legendHandles[j]._sizes = [80] + lgnd1.legendHandles[j]._sizes = [80] for i,stat in enumerate(['mean','amax']): From ebb3b367a65181a4373fd93b2ac182097f5ab42d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 30 Aug 2023 12:51:08 +0900 Subject: [PATCH 0901/1472] comments about BE solver --- build/source/engine/summaSolve4numrec.f90 | 4 ++-- build/source/engine/systemSolv.f90 | 1 + utils/wallClock_per_GRU.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 4b8666371..8bd2bf765 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -421,8 +421,8 @@ subroutine lineSearchRefinement(doLineSearch,stateVecTrial,newtStepScaled,aJacSc !if(nrgFormulation==ix_enthalpy) xInc(ixNrgOnly) = xInc(ixNrgOnly)/dMat(ixNrgOnly) ! impose solution constraints - ! NOTE: we may not need to do this (or at least, do ALL of this), as we can probably rely on the line search here - ! (especially the feasibility check) + ! NOTE: We may not need to do this (or at least, do ALL of this), as we can probably rely on the line search here + ! But, imposeConstraints does not impose maxes on temps or water which will make it infeasible call imposeConstraints(stateVecTrial,xInc,err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index d42576fee..cd215d22f 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -733,6 +733,7 @@ subroutine systemSolv(& ! ----- ! * update states... ! Post processing step to “perfectly” conserve mass by pushing the errors into the state variables + ! NOTE: if the residual is large this will cause the state variables to be pushed outside of their bounds ! ------------------ if (post_massCons)then layerVars: associate(& diff --git a/utils/wallClock_per_GRU.py b/utils/wallClock_per_GRU.py index 012aedd71..1e3a9b1c2 100644 --- a/utils/wallClock_per_GRU.py +++ b/utils/wallClock_per_GRU.py @@ -22,7 +22,7 @@ viz_dir = Path('/home/avanb/scratch/statistics') nbatch_hrus = 518 # number of HRUs per batch -testing = True +testing = False if testing: viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') method_name=['be1','be64','sundials_1en6'] #maybe make this an argument From 991fa0dd97cf03b16e10a10a2f41a8648e58d051 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 30 Aug 2023 06:25:08 -0600 Subject: [PATCH 0902/1472] Added derived type for vegNrgFlux intent(out) arguments. --- build/source/dshare/data_types.f90 | 34 ++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 8ce684126..2cd810664 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -314,6 +314,40 @@ MODULE data_types real(rkind) :: scalarCanopyLiqTrial ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) real(rkind) :: dCanLiq_dTcanopy ! intent(in): derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) end type in_type_vegNrgFlux + + type, public :: out_type_vegNrgFlux ! derived type for intent(out) arguments in vegNrgFlux call + real(rkind) :: scalarCanopyTranspiration ! intent(out): canopy transpiration (kg m-2 s-1) + real(rkind) :: scalarCanopyEvaporation ! intent(out): canopy evaporation/condensation (kg m-2 s-1) + real(rkind) :: scalarGroundEvaporation ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + real(rkind) :: scalarCanairNetNrgFlux ! intent(out): net energy flux for the canopy air space (W m-2) + real(rkind) :: scalarCanopyNetNrgFlux ! intent(out): net energy flux for the vegetation canopy (W m-2) + real(rkind) :: scalarGroundNetNrgFlux ! intent(out): net energy flux for the ground surface (W m-2) + real(rkind) :: dCanairNetFlux_dCanairTemp ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dCanairNetFlux_dCanopyTemp ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dCanairNetFlux_dGroundTemp ! intent(out): derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dCanopyNetFlux_dCanairTemp ! intent(out): derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dCanopyNetFlux_dCanopyTemp ! intent(out): derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dCanopyNetFlux_dGroundTemp ! intent(out): derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dGroundNetFlux_dCanairTemp ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dGroundNetFlux_dCanopyTemp ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dGroundNetFlux_dGroundTemp ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dCanopyEvaporation_dCanWat ! intent(out): derivative in canopy evaporation w.r.t. canopy total water content (s-1) + real(rkind) :: dCanopyEvaporation_dTCanair ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(rkind) :: dCanopyEvaporation_dTCanopy ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(rkind) :: dCanopyEvaporation_dTGround ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + real(rkind) :: dGroundEvaporation_dCanWat ! intent(out): derivative in ground evaporation w.r.t. canopy total water content (s-1) + real(rkind) :: dGroundEvaporation_dTCanair ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(rkind) :: dGroundEvaporation_dTCanopy ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(rkind) :: dGroundEvaporation_dTGround ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + real(rkind) :: dCanopyTrans_dCanWat ! intent(out): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + real(rkind) :: dCanopyTrans_dTCanair ! intent(out): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(rkind) :: dCanopyTrans_dTCanopy ! intent(out): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(rkind) :: dCanopyTrans_dTGround ! intent(out): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + real(rkind) :: dCanopyNetFlux_dCanWat ! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) + real(rkind) :: dGroundNetFlux_dCanWat ! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) + integer(i4b) :: err ! intent(out): error code + character(:),allocatable :: cmessage ! intent(out): error message + end type out_type_vegNrgFlux ! ** end vegNrgFlux ! ** ssdNrgFlux From 402ce2a887e756284381cfb882b13f3702c8efd6 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 30 Aug 2023 06:26:24 -0600 Subject: [PATCH 0903/1472] Intent(in) vegNrgFlux arguments now handled by subTools routine. --- build/source/engine/computFlux.f90 | 53 ++++++++++++++++++--------- build/source/engine/vegNrgFlux.f90 | 57 +++++++++++++----------------- 2 files changed, 61 insertions(+), 49 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 876de817b..9101a2f7f 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -31,6 +31,7 @@ module computFlux_module var_dlength, & ! data vector with variable length dimension (rkind) model_options, & ! defines the model decisions in_type_vegNrgFlux, & ! intent(in) arguments for vegNrgFlux call + out_type_vegNrgFlux,& ! intent(out) arguments for vegNrgFlux call in_type_ssdNrgFlux, & ! intent(in) arguments for ssdNrgFlux call io_type_ssdNrgFlux, & ! intent(inout) arguments for ssdNrgFlux call out_type_ssdNrgFlux ! intent(out) arguments for ssdNrgFlux call @@ -217,10 +218,11 @@ subroutine computFlux(& real(rkind) :: above_soilLiqFluxDeriv ! derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water real(rkind) :: above_soildLiq_dTk ! derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature real(rkind) :: above_soilFracLiq ! fraction of liquid water layer above soil (canopy or snow) (-) + type(in_type_vegNrgFlux) :: in_vegNrgFlux ! data structure for ssdNrgFlux arguments + type(out_type_vegNrgFlux) :: out_vegNrgFlux ! data structure for ssdNrgFlux arguments type(in_type_ssdNrgFlux) :: in_ssdNrgFlux ! data structure for ssdNrgFlux arguments type(io_type_ssdNrgFlux) :: io_ssdNrgFlux ! data structure for ssdNrgFlux arguments type(out_type_ssdNrgFlux) :: out_ssdNrgFlux ! data structure for ssdNrgFlux arguments - type(in_type_vegNrgFlux) :: in_vegNrgFlux ! data structure for ssdNrgFlux arguments ! -------------------------------------------------------------- ! initialize error control err=0; message='computFlux/' @@ -407,21 +409,9 @@ subroutine computFlux(& dCanLiq_dTcanopy = dTheta_dTkCanopy*iden_water*canopyDepth ! derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) ! calculate the energy fluxes over vegetation + call subTools(iLookOP%pre,iLookROUTINE%vegNrgFlux) ! pre-processing for call to vegNrgFlux call vegNrgFlux(& - ! input: model control - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - checkLWBalance, & ! intent(in): flag to check longwave balance - ! input: model state variables - upperBoundTemp, & ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature - scalarCanairTempTrial, & ! intent(in): trial value of the canopy air space temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - mLayerTempTrial(1), & ! intent(in): trial value of ground temperature (K) - scalarCanopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) - ! input: model derivatives - dCanLiq_dTcanopy, & ! intent(in): derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) + in_vegNrgFlux, & ! input/output: data structures type_data, & ! intent(in): type of vegetation and soil forc_data, & ! intent(in): model forcing data @@ -1045,7 +1035,38 @@ subroutine subTools(op,sub) in_vegNrgFlux % scalarCanopyLiqTrial=scalarCanopyLiqTrial ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) in_vegNrgFlux % dCanLiq_dTcanopy=dCanLiq_dTcanopy ! intent(in): derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) else ! post-processing - + ! intent(out) arguments + scalarCanopyTranspiration =out_vegNrgFlux % scalarCanopyTranspiration ! intent(out): canopy transpiration (kg m-2 s-1) + scalarCanopyEvaporation =out_vegNrgFlux % scalarCanopyEvaporation ! intent(out): canopy evaporation/condensation (kg m-2 s-1) + scalarGroundEvaporation =out_vegNrgFlux % scalarGroundEvaporation ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + scalarCanairNetNrgFlux =out_vegNrgFlux % scalarCanairNetNrgFlux ! intent(out): net energy flux for the canopy air space (W m-2) + scalarCanopyNetNrgFlux =out_vegNrgFlux % scalarCanopyNetNrgFlux ! intent(out): net energy flux for the vegetation canopy (W m-2) + scalarGroundNetNrgFlux =out_vegNrgFlux % scalarGroundNetNrgFlux ! intent(out): net energy flux for the ground surface (W m-2) + dCanairNetFlux_dCanairTemp =out_vegNrgFlux % dCanairNetFlux_dCanairTemp ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) + dCanairNetFlux_dCanopyTemp =out_vegNrgFlux % dCanairNetFlux_dCanopyTemp ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) + dCanairNetFlux_dGroundTemp =out_vegNrgFlux % dCanairNetFlux_dGroundTemp ! intent(out): derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) + dCanopyNetFlux_dCanairTemp =out_vegNrgFlux % dCanopyNetFlux_dCanairTemp ! intent(out): derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) + dCanopyNetFlux_dCanopyTemp =out_vegNrgFlux % dCanopyNetFlux_dCanopyTemp ! intent(out): derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) + dCanopyNetFlux_dGroundTemp =out_vegNrgFlux % dCanopyNetFlux_dGroundTemp ! intent(out): derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) + dGroundNetFlux_dCanairTemp =out_vegNrgFlux % dGroundNetFlux_dCanairTemp ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) + dGroundNetFlux_dCanopyTemp =out_vegNrgFlux % dGroundNetFlux_dCanopyTemp ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) + dGroundNetFlux_dGroundTemp =out_vegNrgFlux % dGroundNetFlux_dGroundTemp ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + dCanopyEvaporation_dCanWat =out_vegNrgFlux % dCanopyEvaporation_dCanWat ! intent(out): derivative in canopy evaporation w.r.t. canopy total water content (s-1) + dCanopyEvaporation_dTCanair=out_vegNrgFlux % dCanopyEvaporation_dTCanair ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTCanopy=out_vegNrgFlux % dCanopyEvaporation_dTCanopy ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTGround=out_vegNrgFlux % dCanopyEvaporation_dTGround ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dCanWat =out_vegNrgFlux % dGroundEvaporation_dCanWat ! intent(out): derivative in ground evaporation w.r.t. canopy total water content (s-1) + dGroundEvaporation_dTCanair=out_vegNrgFlux % dGroundEvaporation_dTCanair ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTCanopy=out_vegNrgFlux % dGroundEvaporation_dTCanopy ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTGround=out_vegNrgFlux % dGroundEvaporation_dTGround ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + dCanopyTrans_dCanWat =out_vegNrgFlux % dCanopyTrans_dCanWat ! intent(out): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair =out_vegNrgFlux % dCanopyTrans_dTCanair ! intent(out): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy =out_vegNrgFlux % dCanopyTrans_dTCanopy ! intent(out): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround =out_vegNrgFlux % dCanopyTrans_dTGround ! intent(out): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + dCanopyNetFlux_dCanWat =out_vegNrgFlux % dCanopyNetFlux_dCanWat! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) + dGroundNetFlux_dCanWat =out_vegNrgFlux % dGroundNetFlux_dCanWat! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) + err =out_vegNrgFlux % err ! intent(out): error code + cmessage =out_vegNrgFlux % cmessage ! intent(out): error message end if case(iLookROUTINE%ssdNrgFlux) ! ssdNrgFlux if (op==iLookOP%pre) then ! pre-processing diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index e432b9822..e4c886001 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -25,11 +25,12 @@ module vegNrgFlux_module ! derived types to define the data structures USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - model_options ! defines the model decisions + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (rkind) + model_options, & ! defines the model decisions + in_type_vegNrgFlux ! intent(in) arguments for vegNrgFlux call ! indices that define elements of the data structures USE var_lookup,only:iLookTYPE ! named variables for structure elements @@ -119,20 +120,8 @@ module vegNrgFlux_module ! public subroutine vegNrgFlux: muster program to compute energy fluxes at vegetation and ground surfaces ! ******************************************************************************************************* subroutine vegNrgFlux(& - ! input: model control - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(in): flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - checkLWBalance, & ! intent(in): flag to check longwave balance - ! input: model state variables - upperBoundTemp, & ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature - canairTempTrial, & ! intent(in): trial value of the canopy air space temperature (K) - canopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - groundTempTrial, & ! intent(in): trial value of ground temperature (K) - canopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) - canopyLiqTrial, & ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) - ! input: model derivatives - dCanLiq_dTcanopy, & ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) + ! input: model control, model state variables, and derivatives + in_vegNrgFlux, & ! intent(in): model control, model state variables, and derivatives ! input/output: data structures type_data, & ! intent(in): type of vegetation and soil forc_data, & ! intent(in): model forcing data @@ -198,20 +187,8 @@ subroutine vegNrgFlux(& ! --------------------------------------------------------------------------------------- ! * dummy variables ! --------------------------------------------------------------------------------------- - ! input: model control - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(in) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: checkLWBalance ! flag to check longwave balance - ! input: model state variables - real(rkind),intent(in) :: upperBoundTemp ! temperature of the upper boundary (K) --> NOTE: use air temperature - real(rkind),intent(in) :: canairTempTrial ! trial value of canopy air space temperature (K) - real(rkind),intent(in) :: canopyTempTrial ! trial value of canopy temperature (K) - real(rkind),intent(in) :: groundTempTrial ! trial value of ground temperature (K) - real(rkind),intent(in) :: canopyIceTrial ! trial value of mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: canopyLiqTrial ! trial value of mass of liquid water on the vegetation canopy (kg m-2) - ! input: model derivatives - real(rkind),intent(in) :: dCanLiq_dTcanopy ! intent(in): derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) + ! input: model control, model state variables, and derivatives + type(in_type_vegNrgFlux),intent(in) :: in_vegNrgFlux ! model control, model state variables, and derivatives ! input/output: data structures type(var_i),intent(in) :: type_data ! type of vegetation and soil type(var_d),intent(in) :: forc_data ! model forcing data @@ -351,6 +328,20 @@ subroutine vegNrgFlux(& ! point to variables in the data structure ! --------------------------------------------------------------------------------------- associate(& + ! input: model control + firstSubStep => in_vegNrgFlux % firstSubStep, & ! intent(in): [dp] flag to indicate if we are processing the first sub-step + firstFluxCall => in_vegNrgFlux % firstFluxCall, & ! intent(in): [dp] flag to indicate if we are processing the first flux call + computeVegFlux => in_vegNrgFlux % computeVegFlux, & ! intent(in): [dp] flag to indicate if computing fluxes over vegetation + checkLWBalance => in_vegNrgFlux % checkLWBalance, & ! intent(in): [dp] flag to check longwave balance + ! input: model state variables + upperBoundTemp => in_vegNrgFlux % upperBoundTemp, & ! intent(in): [dp] temperature of the upper boundary (K) --> NOTE: use air temperature + canairTempTrial => in_vegNrgFlux % scalarCanairTempTrial, & ! intent(in): [dp] trial value of canopy air space temperature (K) + canopyTempTrial => in_vegNrgFlux % scalarCanopyTempTrial, & ! intent(in): [dp] trial value of canopy temperature (K) + groundTempTrial => in_vegNrgFlux % mLayerTempTrial_1, & ! intent(in): [dp] trial value of ground temperature (K) + canopyIceTrial => in_vegNrgFlux % scalarCanopyIceTrial, & ! intent(in): [dp] trial value of mass of ice on the vegetation canopy (kg m-2) + canopyLiqTrial => in_vegNrgFlux % scalarCanopyLiqTrial, & ! intent(in): [dp] trial value of mass of liquid water on the vegetation canopy (kg m-2) + ! input: model derivatives + dCanLiq_dTcanopy => in_vegNrgFlux % dCanLiq_dTcanopy, & ! intent(in): [dp] derivative in canopy liquid w.r.t. canopy temperature (kg m-2 K-1) ! input: model decisions ix_bcUpprTdyn => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision, & ! intent(in): [i4b] choice of upper boundary condition for thermodynamics ix_veg_traits => model_decisions(iLookDECISIONS%veg_traits)%iDecision, & ! intent(in): [i4b] choice of parameterization for vegetation roughness length and displacement height From 71c1d8db765b1c2628d1cf06210dc42bd1b0dd90 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 31 Aug 2023 02:30:07 -0600 Subject: [PATCH 0904/1472] Implemented and interfaced data structure for intent(out) vegNrgFlux arguments. --- build/source/engine/computFlux.f90 | 98 +++++-------------- build/source/engine/vegNrgFlux.f90 | 146 +++++++++++------------------ 2 files changed, 80 insertions(+), 164 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 9101a2f7f..c8cc28c4f 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -218,11 +218,11 @@ subroutine computFlux(& real(rkind) :: above_soilLiqFluxDeriv ! derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water real(rkind) :: above_soildLiq_dTk ! derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature real(rkind) :: above_soilFracLiq ! fraction of liquid water layer above soil (canopy or snow) (-) - type(in_type_vegNrgFlux) :: in_vegNrgFlux ! data structure for ssdNrgFlux arguments - type(out_type_vegNrgFlux) :: out_vegNrgFlux ! data structure for ssdNrgFlux arguments - type(in_type_ssdNrgFlux) :: in_ssdNrgFlux ! data structure for ssdNrgFlux arguments - type(io_type_ssdNrgFlux) :: io_ssdNrgFlux ! data structure for ssdNrgFlux arguments - type(out_type_ssdNrgFlux) :: out_ssdNrgFlux ! data structure for ssdNrgFlux arguments + type(in_type_vegNrgFlux) :: in_vegNrgFlux ! data structure for intent(in) vegNrgFlux arguments + type(out_type_vegNrgFlux) :: out_vegNrgFlux ! data structure for intent(out) vegNrgFlux arguments + type(in_type_ssdNrgFlux) :: in_ssdNrgFlux ! data structure for intent(in) ssdNrgFlux arguments + type(io_type_ssdNrgFlux) :: io_ssdNrgFlux ! data structure for intent(inout) ssdNrgFlux arguments + type(out_type_ssdNrgFlux) :: out_ssdNrgFlux ! data structure for intent(out) ssdNrgFlux arguments ! -------------------------------------------------------------- ! initialize error control err=0; message='computFlux/' @@ -406,77 +406,11 @@ subroutine computFlux(& doVegNrgFlux = (ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing .or. ixTopNrg/=integerMissing) if (doVegNrgFlux) then ! check if there is a need to calculate the energy fluxes over vegetation - dCanLiq_dTcanopy = dTheta_dTkCanopy*iden_water*canopyDepth ! derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) - ! calculate the energy fluxes over vegetation call subTools(iLookOP%pre,iLookROUTINE%vegNrgFlux) ! pre-processing for call to vegNrgFlux - call vegNrgFlux(& - in_vegNrgFlux, & - ! input/output: data structures - type_data, & ! intent(in): type of vegetation and soil - forc_data, & ! intent(in): model forcing data - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): index data - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - bvar_data, & ! intent(in): model variables for the local basin - model_decisions, & ! intent(in): model decisions - ! output: liquid water fluxes associated with evaporation/transpiration - scalarCanopyTranspiration, & ! intent(out): canopy transpiration (kg m-2 s-1) - scalarCanopyEvaporation, & ! intent(out): canopy evaporation/condensation (kg m-2 s-1) - scalarGroundEvaporation, & ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - ! output: fluxes - scalarCanairNetNrgFlux, & ! intent(out): net energy flux for the canopy air space (W m-2) - scalarCanopyNetNrgFlux, & ! intent(out): net energy flux for the vegetation canopy (W m-2) - scalarGroundNetNrgFlux, & ! intent(out): net energy flux for the ground surface (W m-2) - ! output: flux derivatives - dCanairNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) - dCanairNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) - dCanairNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) - dCanopyNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) - dCanopyNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) - dCanopyNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - dGroundNetFlux_dCanairTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) - dGroundNetFlux_dCanopyTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) - dGroundNetFlux_dGroundTemp, & ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - ! output: liquid water flux derivatives (canopy evap) - dCanopyEvaporation_dCanWat, & ! intent(out): derivative in canopy evaporation w.r.t. canopy total water content (s-1) - dCanopyEvaporation_dTCanair, & ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTCanopy, & ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTGround, & ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - ! output: liquid water flux derivatives (ground evap) - dGroundEvaporation_dCanWat, & ! intent(out): derivative in ground evaporation w.r.t. canopy total water content (s-1) - dGroundEvaporation_dTCanair, & ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTCanopy, & ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTGround, & ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - ! output: transpiration derivatives - dCanopyTrans_dCanWat, & ! intent(out): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - dCanopyTrans_dTCanair, & ! intent(out): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTCanopy, & ! intent(out): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTGround, & ! intent(out): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - ! output: cross derivative terms - dCanopyNetFlux_dCanWat, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) - dGroundNetFlux_dCanWat, & ! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) - ! output: error control - err,cmessage) ! intent(out): error control - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! check fluxes - if (globalPrintFlag) then - print*, '**' - write(*,'(a,1x,10(f30.20))') 'canopyDepth = ', canopyDepth - write(*,'(a,1x,10(f30.20))') 'mLayerDepth(1:2) = ', mLayerDepth(1:2) - write(*,'(a,1x,10(f30.20))') 'scalarCanairTempTrial = ', scalarCanairTempTrial ! trial value of the canopy air space temperature (K) - write(*,'(a,1x,10(f30.20))') 'scalarCanopyTempTrial = ', scalarCanopyTempTrial ! trial value of canopy temperature (K) - write(*,'(a,1x,10(f30.20))') 'mLayerTempTrial(1:2) = ', mLayerTempTrial(1:2) ! trial value of ground temperature (K) - write(*,'(a,1x,10(f30.20))') 'scalarCanairNetNrgFlux = ', scalarCanairNetNrgFlux - write(*,'(a,1x,10(f30.20))') 'scalarCanopyNetNrgFlux = ', scalarCanopyNetNrgFlux - write(*,'(a,1x,10(f30.20))') 'scalarGroundNetNrgFlux = ', scalarGroundNetNrgFlux - write(*,'(a,1x,10(f30.20))') 'dGroundNetFlux_dGroundTemp = ', dGroundNetFlux_dGroundTemp - end if ! if checking fluxes - - end if ! if calculating the energy fluxes over vegetation + call vegNrgFlux(in_vegNrgFlux,type_data,forc_data,mpar_data,indx_data,prog_data,diag_data,flux_data,bvar_data,model_decisions,out_vegNrgFlux) + call subTools(iLookOP%post,iLookROUTINE%vegNrgFlux) ! post-processing for call to vegNrgFlux + end if ! end if calculating the energy fluxes over vegetation ! ***** ! * CALCULATE ENERGY FLUXES THROUGH THE SNOW-SOIL DOMAIN... @@ -1022,6 +956,7 @@ subroutine subTools(op,sub) select case(sub) case(iLookROUTINE%vegNrgFlux) ! vegNrgFlux if (op==iLookOP%pre) then ! pre-processing + dCanLiq_dTcanopy = dTheta_dTkCanopy*iden_water*canopyDepth ! derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) ! intent(in) arguments in_vegNrgFlux % firstSubStep=firstSubStep ! intent(in): flag to indicate if we are processing the first sub-step in_vegNrgFlux % firstFluxCall=firstFluxCall ! intent(in): flag to indicate if we are processing the first flux call @@ -1067,6 +1002,21 @@ subroutine subTools(op,sub) dGroundNetFlux_dCanWat =out_vegNrgFlux % dGroundNetFlux_dCanWat! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) err =out_vegNrgFlux % err ! intent(out): error code cmessage =out_vegNrgFlux % cmessage ! intent(out): error message + ! additional post-processing + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors + ! check fluxes + if (globalPrintFlag) then + print*, '**' + write(*,'(a,1x,10(f30.20))') 'canopyDepth = ', canopyDepth + write(*,'(a,1x,10(f30.20))') 'mLayerDepth(1:2) = ', mLayerDepth(1:2) + write(*,'(a,1x,10(f30.20))') 'scalarCanairTempTrial = ', scalarCanairTempTrial ! trial value of the canopy air space temperature (K) + write(*,'(a,1x,10(f30.20))') 'scalarCanopyTempTrial = ', scalarCanopyTempTrial ! trial value of canopy temperature (K) + write(*,'(a,1x,10(f30.20))') 'mLayerTempTrial(1:2) = ', mLayerTempTrial(1:2) ! trial value of ground temperature (K) + write(*,'(a,1x,10(f30.20))') 'scalarCanairNetNrgFlux = ', scalarCanairNetNrgFlux + write(*,'(a,1x,10(f30.20))') 'scalarCanopyNetNrgFlux = ', scalarCanopyNetNrgFlux + write(*,'(a,1x,10(f30.20))') 'scalarGroundNetNrgFlux = ', scalarGroundNetNrgFlux + write(*,'(a,1x,10(f30.20))') 'dGroundNetFlux_dGroundTemp = ', dGroundNetFlux_dGroundTemp + end if ! end if checking fluxes end if case(iLookROUTINE%ssdNrgFlux) ! ssdNrgFlux if (op==iLookOP%pre) then ! pre-processing diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index e4c886001..50499a043 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -30,7 +30,8 @@ module vegNrgFlux_module var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (rkind) model_options, & ! defines the model decisions - in_type_vegNrgFlux ! intent(in) arguments for vegNrgFlux call + in_type_vegNrgFlux, & ! intent(in) arguments for vegNrgFlux call + out_type_vegNrgFlux ! intent(out) arguments for vegNrgFlux call ! indices that define elements of the data structures USE var_lookup,only:iLookTYPE ! named variables for structure elements @@ -132,44 +133,8 @@ subroutine vegNrgFlux(& flux_data, & ! intent(inout): model fluxes for a local HRU bvar_data, & ! intent(in): model variables for the local basin model_decisions, & ! intent(in): model decisions - ! output: liquid water fluxes associated with evaporation/transpiration (needed for coupling) - returnCanopyTranspiration, & ! intent(out): canopy transpiration (kg m-2 s-1) - returnCanopyEvaporation, & ! intent(out): canopy evaporation/condensation (kg m-2 s-1) - returnGroundEvaporation, & ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - ! output: fluxes - canairNetFlux, & ! intent(out): net energy flux for the canopy air space (W m-2) - canopyNetFlux, & ! intent(out): net energy flux for the vegetation canopy (W m-2) - groundNetFlux, & ! intent(out): net energy flux for the ground surface (W m-2) - ! output: energy flux derivatives - dCanairNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) - dCanairNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) - dCanairNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) - dCanopyNetFlux_dCanairTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) - dCanopyNetFlux_dCanopyTemp, & ! intent(out): derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) - dCanopyNetFlux_dGroundTemp, & ! intent(out): derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - dGroundNetFlux_dCanairTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) - dGroundNetFlux_dCanopyTemp, & ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) - dGroundNetFlux_dGroundTemp, & ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - ! output liquid water flux derivarives (canopy evap) - dCanopyEvaporation_dCanWat, & ! intent(out): derivative in canopy evaporation w.r.t. canopy total water content (s-1) - dCanopyEvaporation_dTCanair, & ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTCanopy, & ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTGround, & ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - ! output: liquid water flux derivarives (ground evap) - dGroundEvaporation_dCanWat, & ! intent(out): derivative in ground evaporation w.r.t. canopy total water content (s-1) - dGroundEvaporation_dTCanair, & ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTCanopy, & ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTGround, & ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - ! output: transpiration derivatives - dCanopyTrans_dCanWat, & ! intent(out): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - dCanopyTrans_dTCanair, & ! intent(out): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTCanopy, & ! intent(out): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTGround, & ! intent(out): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - ! output: cross derivative terms - dCanopyNetFlux_dCanWat, & ! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) - dGroundNetFlux_dCanWat, & ! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) - ! output: error control - err,message) ! intent(out): error control + ! output: fluxes, derivatives, and error control + out_vegNrgFlux) ! intent(out): fluxes, derivatives, and error control ! utilities USE expIntegral_module,only:expInt ! function to calculate the exponential integral @@ -188,57 +153,19 @@ subroutine vegNrgFlux(& ! * dummy variables ! --------------------------------------------------------------------------------------- ! input: model control, model state variables, and derivatives - type(in_type_vegNrgFlux),intent(in) :: in_vegNrgFlux ! model control, model state variables, and derivatives + type(in_type_vegNrgFlux),intent(in) :: in_vegNrgFlux ! model control, model state variables, and derivatives ! input/output: data structures - type(var_i),intent(in) :: type_data ! type of vegetation and soil - type(var_d),intent(in) :: forc_data ! model forcing data - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! state vector geometry - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin - type(model_options),intent(in) :: model_decisions(:) ! model decisions - ! output: liquid water fluxes associated with evaporation/transpiration (needed for coupling) - real(rkind),intent(out) :: returnCanopyTranspiration ! canopy transpiration (kg m-2 s-1) - real(rkind),intent(out) :: returnCanopyEvaporation ! canopy evaporation/condensation (kg m-2 s-1) - real(rkind),intent(out) :: returnGroundEvaporation ! ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - ! output: fluxes - real(rkind),intent(out) :: canairNetFlux ! net energy flux for the canopy air space (W m-2) - real(rkind),intent(out) :: canopyNetFlux ! net energy flux for the vegetation canopy (W m-2) - real(rkind),intent(out) :: groundNetFlux ! net energy flux for the ground surface (W m-2) - ! output: energy flux derivatives - real(rkind),intent(out) :: dCanairNetFlux_dCanairTemp ! derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) - real(rkind),intent(out) :: dCanairNetFlux_dCanopyTemp ! derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dCanairNetFlux_dGroundTemp ! derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) - real(rkind),intent(out) :: dCanopyNetFlux_dCanairTemp ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) - real(rkind),intent(out) :: dCanopyNetFlux_dCanopyTemp ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dCanopyNetFlux_dGroundTemp ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - real(rkind),intent(out) :: dGroundNetFlux_dCanairTemp ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) - real(rkind),intent(out) :: dGroundNetFlux_dCanopyTemp ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) - real(rkind),intent(out) :: dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - ! output: liquid flux derivatives (canopy evap) - real(rkind),intent(out) :: dCanopyEvaporation_dCanWat ! derivative in canopy evaporation w.r.t. canopy total water content (s-1) - real(rkind),intent(out) :: dCanopyEvaporation_dTCanair ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - real(rkind),intent(out) :: dCanopyEvaporation_dTCanopy ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - real(rkind),intent(out) :: dCanopyEvaporation_dTGround ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - ! output: liquid flux derivatives (ground evap) - real(rkind),intent(out) :: dGroundEvaporation_dCanWat ! derivative in ground evaporation w.r.t. canopy total water content (s-1) - real(rkind),intent(out) :: dGroundEvaporation_dTCanair ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - real(rkind),intent(out) :: dGroundEvaporation_dTCanopy ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - real(rkind),intent(out) :: dGroundEvaporation_dTGround ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - ! output: transpiration derivatives - real(rkind),intent(out) :: dCanopyTrans_dCanWat ! intent(out): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - real(rkind),intent(out) :: dCanopyTrans_dTCanair ! intent(out): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - real(rkind),intent(out) :: dCanopyTrans_dTCanopy ! intent(out): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - real(rkind),intent(out) :: dCanopyTrans_dTGround ! intent(out): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - ! output: cross derivative terms - real(rkind),intent(out) :: dCanopyNetFlux_dCanWat ! derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) - real(rkind),intent(out) :: dGroundNetFlux_dCanWat ! derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - + type(var_i),intent(in) :: type_data ! type of vegetation and soil + type(var_d),intent(in) :: forc_data ! model forcing data + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! state vector geometry + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin + type(model_options),intent(in) :: model_decisions(:) ! model decisions + ! output: fluxes, derivatives, and error control + type(out_type_vegNrgFlux),intent(out) :: out_vegNrgFlux ! data structure for vegNrgFlux arguments ! --------------------------------------------------------------------------------------- ! * local variables ! --------------------------------------------------------------------------------------- @@ -497,7 +424,46 @@ subroutine vegNrgFlux(& scalarGroundEvaporation => flux_data%var(iLookFLUX%scalarGroundEvaporation)%dat(1), & ! intent(out): [dp] ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) ! output: derived fluxes scalarTotalET => flux_data%var(iLookFLUX%scalarTotalET)%dat(1), & ! intent(out): [dp] total ET (kg m-2 s-1) - scalarNetRadiation => flux_data%var(iLookFLUX%scalarNetRadiation)%dat(1) & ! intent(out): [dp] net radiation (W m-2) + scalarNetRadiation => flux_data%var(iLookFLUX%scalarNetRadiation)%dat(1), & ! intent(out): [dp] net radiation (W m-2) + ! output: liquid water fluxes associated with evaporation/transpiration (needed for coupling) + returnCanopyTranspiration => out_vegNrgFlux % scalarCanopyTranspiration, & ! intent(out): [dp] canopy transpiration (kg m-2 s-1) + returnCanopyEvaporation => out_vegNrgFlux % scalarCanopyEvaporation, & ! intent(out): [dp] canopy evaporation/condensation (kg m-2 s-1) + returnGroundEvaporation => out_vegNrgFlux % scalarGroundEvaporation, & ! intent(out): [dp] ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + ! output: fluxes + canairNetFlux => out_vegNrgFlux % scalarCanairNetNrgFlux, & ! intent(out): [dp] net energy flux for the canopy air space (W m-2) + canopyNetFlux => out_vegNrgFlux % scalarCanopyNetNrgFlux, & ! intent(out): [dp] net energy flux for the vegetation canopy (W m-2) + groundNetFlux => out_vegNrgFlux % scalarGroundNetNrgFlux, & ! intent(out): [dp] net energy flux for the ground surface (W m-2) + ! output: energy flux derivatives + dCanairNetFlux_dCanairTemp => out_vegNrgFlux % dCanairNetFlux_dCanairTemp,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) + dCanairNetFlux_dCanopyTemp => out_vegNrgFlux % dCanairNetFlux_dCanopyTemp,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) + dCanairNetFlux_dGroundTemp => out_vegNrgFlux % dCanairNetFlux_dGroundTemp,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) + dCanopyNetFlux_dCanairTemp => out_vegNrgFlux % dCanopyNetFlux_dCanairTemp,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) + dCanopyNetFlux_dCanopyTemp => out_vegNrgFlux % dCanopyNetFlux_dCanopyTemp,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) + dCanopyNetFlux_dGroundTemp => out_vegNrgFlux % dCanopyNetFlux_dGroundTemp,& ! intent(out): [dp] derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) + dGroundNetFlux_dCanairTemp => out_vegNrgFlux % dGroundNetFlux_dCanairTemp,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) + dGroundNetFlux_dCanopyTemp => out_vegNrgFlux % dGroundNetFlux_dCanopyTemp,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) + dGroundNetFlux_dGroundTemp => out_vegNrgFlux % dGroundNetFlux_dGroundTemp,& ! intent(out): [dp] derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + ! output: liquid flux derivatives (canopy evap) + dCanopyEvaporation_dCanWat => out_vegNrgFlux % dCanopyEvaporation_dCanWat, & ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy total water content (s-1) + dCanopyEvaporation_dTCanair => out_vegNrgFlux % dCanopyEvaporation_dTCanair, & ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTCanopy => out_vegNrgFlux % dCanopyEvaporation_dTCanopy, & ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTGround => out_vegNrgFlux % dCanopyEvaporation_dTGround, & ! intent(out): [dp] derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + ! output: liquid flux derivatives (ground evap) + dGroundEvaporation_dCanWat => out_vegNrgFlux % dGroundEvaporation_dCanWat, & ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy total water content (s-1) + dGroundEvaporation_dTCanair => out_vegNrgFlux % dGroundEvaporation_dTCanair, & ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTCanopy => out_vegNrgFlux % dGroundEvaporation_dTCanopy, & ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTGround => out_vegNrgFlux % dGroundEvaporation_dTGround, & ! intent(out): [dp] derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + ! output: transpiration derivatives + dCanopyTrans_dCanWat => out_vegNrgFlux % dCanopyTrans_dCanWat, & ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair => out_vegNrgFlux % dCanopyTrans_dTCanair, & ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy => out_vegNrgFlux % dCanopyTrans_dTCanopy, & ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround => out_vegNrgFlux % dCanopyTrans_dTGround, & ! intent(out): [dp] derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + ! output: cross derivative terms + dCanopyNetFlux_dCanWat => out_vegNrgFlux % dCanopyNetFlux_dCanWat, & ! intent(out): [dp] derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) + dGroundNetFlux_dCanWat =>out_vegNrgFlux % dGroundNetFlux_dCanWat, & ! intent(out): [dp] derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) + ! output: error control + err => out_vegNrgFlux % err, & ! intent(out): [i4b] error code + message => out_vegNrgFlux % cmessage & ! intent(out): [character] error message ) ! --------------------------------------------------------------------------------------- ! initialize error control From f50e659eab8c60e68dfcc5cead31ac5779751e89 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 31 Aug 2023 02:44:28 -0600 Subject: [PATCH 0905/1472] Added derived types for vegLiqFlux arguments. --- build/source/dshare/data_types.f90 | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 2cd810664..8e39b32e3 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -378,5 +378,20 @@ MODULE data_types end type out_type_ssdNrgFlux ! ** end ssdNrgFlux -END MODULE data_types + ! ** vegLiqFlux + type, public :: in_type_vegLiqFlux ! derived type for intent(in) arguments in vegLiqFlux call + logical(lgt) :: computeVegFlux ! intent(in): flag to denote if computing energy flux over vegetation + real(rkind) :: scalarCanopyLiqTrial ! intent(in): trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) + real(rkind) :: scalarRainfall ! intent(in): rainfall rate (kg m-2 s-1) + end type in_type_vegLiqFlux + type, public :: out_type_vegLiqFlux ! derived type for intent(out) arguments in vegLiqFlux call + real(rkind) :: scalarThroughfallRain ! intent(out): rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + real(rkind) :: scalarCanopyLiqDrainage ! intent(out): drainage of liquid water from the vegetation canopy (kg m-2 s-1) + real(rkind) :: scalarThroughfallRainDeriv ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) + real(rkind) :: scalarCanopyLiqDrainageDeriv ! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) + integer(i4b) :: err ! intent(out): error code + character(:),allocatable :: cmessage ! intent(out): error control + end type out_type_vegLiqFlux + ! ** end vegLiqFlux +END MODULE data_types From a58ec0d121436274e774e0958568fa28f308cd88 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 31 Aug 2023 23:58:33 +0900 Subject: [PATCH 0906/1472] fix feasibility issue by checking for upper limits. move imposeConstraints into eval8summa and put separate conditions for kinsol and numrec --- build/source/engine/eval8summa.f90 | 143 +++++++--- build/source/engine/getVectorz.f90 | 17 +- build/source/engine/snowAlbedo.f90 | 48 ++-- build/source/engine/stomResist.f90 | 132 ++++----- build/source/engine/summaSolve4ida.f90 | 2 +- build/source/engine/summaSolve4kinsol.f90 | 2 +- build/source/engine/summaSolve4numrec.f90 | 317 +++------------------- utils/plot_per_GRU.py | 11 +- 8 files changed, 240 insertions(+), 432 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 8a3ac81d2..2c2602ed5 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -35,7 +35,6 @@ module eval8summa_module ! constants USE multiconst,only:& Tfreeze, & ! temperature at freezing (K) - iden_ice, & ! intrinsic density of ice (kg m-3) iden_water ! intrinsic density of liquid water (kg m-3) ! provide access to the derived types to define the data structures @@ -68,11 +67,12 @@ module eval8summa_module ida ! SUNDIALS solution using IDA implicit none -private::imposeConstraints +private public::eval8summa #ifdef SUNDIALS_ACTIVE public::eval8summa4kinsol #endif +public::imposeConstraints contains @@ -731,7 +731,7 @@ integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & logical(lgt) :: feasible ! feasibility of state vector real(rkind) :: fNew ! function values, not needed here integer(i4b) :: err ! error in imposeConstraints - character(len=256) :: message ! error message + character(len=256) :: message ! error message of downwind routine !======= Internals ============ ! get equations data from user-defined data @@ -745,12 +745,12 @@ integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & if (eqns_data%firstStateiteration) then eqns_data%firstStateIteration = .false. else - call imposeConstraints(eqns_data%indx_data,eqns_data%prog_data,eqns_data%mpar_data,stateVec(:), & + call imposeConstraints(eqns_data%model_decisions,eqns_data%indx_data,eqns_data%prog_data,eqns_data%mpar_data,stateVec(:), & eqns_data%stateVecPrev, eqns_data%nState, eqns_data%nSoil, eqns_data%nSnow, message, err) - if(err/=0)then; ierr=1; message="Impose Constraints Failed"; print*, message; return; end if ! (check for errors) - eqns_data%stateVecPrev = stateVec(:) + if(err/=0)then; ierr=1; message="eval8summa4kinsol/"//trim(message); print*, message; return; end if ! (check for errors) endif - + eqns_data%stateVecPrev = stateVec(:) ! save the state vector for the next iteration + ! compute the flux and the residual vector for a given state vector call eval8summa(& ! input: model control @@ -805,17 +805,18 @@ end function eval8summa4kinsol #endif ! *************************************************************************************************************************************** -! private subroutine imposeConstraints: impose solution constraints +! public subroutine imposeConstraints: impose solution constraints ! This is simple error control to reduce possible temperature increments, cross over freezing point events, and keep the state feasible ! Imposed after the internal call of KINSOL incrementing the linesearch ! *************************************************************************************************************************************** -subroutine imposeConstraints(indx_data, prog_data, mpar_data, stateVec, stateVecPrev,& +subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, stateVec, stateVecPrev,& nState, nSoil, nSnow, message, err) ! external functions USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water at a given temperature (snow) USE soil_utils_module,only:crit_soilT ! compute the critical temperature below which ice exists implicit none + type(model_options),intent(in) :: model_decisions(:) ! model decisions type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(in) :: mpar_data ! model parameters @@ -833,31 +834,38 @@ subroutine imposeConstraints(indx_data, prog_data, mpar_data, stateVec, stateVec real(rkind) :: xIncFactor ! scaling factor for the iteration increment (-) integer(i4b) :: iMax(1) ! index of maximum temperature real(rkind) :: scalarTemp ! temperature of an individual snow layer (K) - real(rkind) :: volFracLiq ! volumetric liquid water content of an individual snow layer (-) + real(rkind) :: scalarIce ! volumetric ice content of an individual layer (-) + real(rkind) :: volFracLiq ! volumetric liquid water content of an individual layer (-) logical(lgt),dimension(nSoil) :: crosFlag ! flag to denote temperature crossing from unfrozen to frozen (or vice-versa) logical(lgt) :: crosTempVeg ! flag to denoote where temperature crosses the freezing point real(rkind) :: xPsi00 ! matric head after applying the iteration increment (m) real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) real(rkind) :: critDiff ! temperature difference from critical (K) - real(rkind),parameter :: epsT=1.e-3_rkind ! small interval above/below critical (K), doesn't work well at usual 1e-7 - real(rkind),parameter :: zMaxTempIncrement=1._rkind ! maximum temperature increment (K),NOTE: this can cause problems especially from a cold start when we are far from the solution + real(rkind) :: epsT ! small interval above/below critical (K) + real(rkind) :: zMaxTempIncrement ! maximum temperature increment (K) + real(rkind) :: zMaxMatricIncrement ! maximum matric head increment (m) ! indices of model state variables integer(i4b) :: iState ! index of state within a specific variable type integer(i4b) :: ixNrg,ixLiq ! index of energy and mass state variables in full state vector ! indices of model layers integer(i4b) :: iLayer ! index of model layer ! choice of constraints to impose - logical(lgt),parameter :: small_delTemp=.false. ! flag to constain temperature change to be less than zMaxTempIncrement, gets more accurate solution off - logical(lgt),parameter :: detect_events=.true. ! flag to do freezing point event detection and cross-over with epsT, works best on - logical(lgt),parameter :: positive_wat=.true. ! flag to force water to not go negative, works best on + logical(lgt) :: small_delTemp ! flag to constain temperature change to be less than zMaxTempIncrement + logical(lgt) :: small_delMatric ! flag to constain matric head change to be less than zMaxMatricIncrement + logical(lgt) :: detect_events ! flag to do freezing point event detection and cross-over with epsT + logical(lgt) :: water_bounds ! flag to force water to not go above or below physical bounds ! ----------------------------------------------------------------------------------------------------- - ! associate variables with indices of model state variables + ! association to variables in the data structures associate(& + ! model decisions + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver + ! indices of model state variables ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for energy states ixHydOnly => indx_data%var(iLookINDEX%ixHydOnly)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for hydrology states ixMatOnly => indx_data%var(iLookINDEX%ixMatOnly)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for matric head states ixMassOnly => indx_data%var(iLookINDEX%ixMassOnly)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for canopy storage states + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] named variables defining the states in the subset ! indices for specific state variables ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable @@ -882,18 +890,45 @@ subroutine imposeConstraints(indx_data, prog_data, mpar_data, stateVec, stateVec nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + ! soil parameters + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) ! state variables at the start of the time step - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat & ! intent(in): [dp(:)] matric head (m) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] matric head (m) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat & ! intent(in): [dp(:)] volumetric fraction of ice (-) ) ! associating variables with indices of model state variables ! ----------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='imposeConstraints/' - + ! calculate proposed increment in state vector xInc(1:nState) = stateVec(1:nState)*1._qp - stateVecPrev(1:nState)*1._qp - + + ! identify which constraints to impose + select case(ixNumericalMethod) + case(ida); err=20; message=trim(message)//'should not be imposing constraints for IDA solver'; return + case(kinsol) + small_delTemp = .false. ! flag to constain temperature change to be less than zMaxTempIncrement, gets more accurate solution if off + zMaxTempIncrement = 1._rkind ! maximum temperature increment (K) + small_delMatric = .false. ! flag to constain matric head change to be less than zMaxMatricIncrement, gets more accurate solution if off + zMaxMatricIncrement = 1._rkind ! maximum matric head increment (m) + detect_events = .true. ! flag to do freezing point event detection and cross-over with epsT, works best if on + epsT = 1.e-3_rkind ! small interval above/below critical (K), works better if larger + water_bounds = .true. ! flag to force water bounds, works best if on + case(numrec) + small_delTemp = .true. ! flag to constain temperature change to be less than zMaxTempIncrement + zMaxTempIncrement = 1._rkind ! maximum temperature increment (K) + small_delMatric = .true. ! flag to constain matric head change to be less than zMaxMatricIncrement + zMaxMatricIncrement = 1._rkind ! maximum matric head increment (m) + detect_events = .true. ! flag to do freezing point event detection and cross-over with epsT + epsT = 1.e-7_rkind ! small interval above/below critical (K) + water_bounds = .true. ! flag to force water bounds + case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or numrec (or itertive, which is numrec)'; return + end select + ! ** limit temperature increment to zMaxTempIncrement + ! NOTE: this can cause problems especially from a cold start when far from the solution if(small_delTemp)then if(any(abs(xInc(ixNrgOnly)) > zMaxTempIncrement))then iMax = maxloc( abs(xInc(ixNrgOnly)) ) ! index of maximum temperature increment @@ -902,6 +937,21 @@ subroutine imposeConstraints(indx_data, prog_data, mpar_data, stateVec, stateVec endif endif ! (small temperature change) + ! ** limit soil water (matric head) increment to zMaxMatricIncrement if starting positive + if(small_delMatric)then + if(size(ixMatOnly)>0)then + ! loop through soil layers + do iState=1,size(ixMatOnly) + ! define index of the hydrology state variable within the state subset + ixLiq = ixMatOnly(iState) + ! place constraint for matric head + if(xInc(ixLiq) > zMaxMatricIncrement .and. stateVecPrev(ixLiq) > 0._rkind)then + xInc(ixLiq) = zMaxMatricIncrement + endif + end do ! (loop through soil layers) + endif + endif ! (small matric head change) + ! ** stop just above or just below the freezing point if crossing if(detect_events)then @@ -984,8 +1034,8 @@ subroutine imposeConstraints(indx_data, prog_data, mpar_data, stateVec, stateVec endif ! (detect events) - ! ** ensure water is non-negative - if(positive_wat)then + ! ** ensure water is within bounds + if(water_bounds)then ! impose positivity for canopy liquid water if(ixVegHyd/=integerMissing)then @@ -998,7 +1048,7 @@ subroutine imposeConstraints(indx_data, prog_data, mpar_data, stateVec, stateVec end if endif ! (if the state variable for canopy water is included within the state subset) - ! impose positivity for snow water + ! impose bounds for snow water, change in total water is only due to liquid flux if(nSnowOnlyHyd>0)then ! loop through snow layers do iLayer=1,nSnow @@ -1007,40 +1057,47 @@ subroutine imposeConstraints(indx_data, prog_data, mpar_data, stateVec, stateVec if(ixSnowOnlyNrg(iLayer)/=integerMissing)then ! get the layer temperature (from stateVecPrev if ixSnowOnlyNrg(iLayer) is within the state vector scalarTemp = stateVecPrev( ixSnowOnlyNrg(iLayer) ) - else - ! get the layer temperature from the last update + else ! get the layer temperature from the last update scalarTemp = prog_data%var(iLookPROG%mLayerTemp)%dat(iLayer) endif - ! get the volumetric fraction of liquid water + ! get the volumetric fraction of liquid water and ice select case( ixStateType_subset( ixSnowOnlyHyd(iLayer) ) ) case(iname_watLayer); volFracLiq = fracliquid(scalarTemp,mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1)) * stateVecPrev(ixSnowOnlyHyd(iLayer)) case(iname_liqLayer); volFracLiq = stateVecPrev(ixSnowOnlyHyd(iLayer)) case default; err=20; message=trim(message)//'expect ixStateType_subset to be iname_watLayer or iname_liqLayer for snow hydrology'; return end select - ! checking if drain more than what is available (increment does not exceed volumetric liquid water content) - ! NOTE: change in total water is only due to liquid flux - if(-xInc(ixSnowOnlyHyd(iLayer)) > volFracLiq)then + scalarIce = mLayerVolFracIce(iLayer) + ! checking if drain more than what is available or add more than possible + if(-xInc(ixSnowOnlyHyd(iLayer)) > volFracLiq)then xInc(ixSnowOnlyHyd(iLayer)) = -0.5_rkind*volFracLiq + elseif(xInc(ixSnowOnlyHyd(iLayer)) > 1._rkind - scalarIce - volFracLiq)then + xInc(ixSnowOnlyHyd(iLayer)) = 0.5_rkind*(1._rkind - scalarIce - volFracLiq) endif end do ! (looping through snow layers) endif ! (if there are state variables for liquid water in the snow domain) - - ! impose positivity for soil water (matric head) - if(size(ixMatOnly)>0)then + + ! impose bounds for soil water, change in total water is only due to liquid flux + if(nSoilOnlyHyd>0)then ! loop through soil layers - do iState=1,size(ixMatOnly) - ! define index of the hydrology state variable within the state subset - ixLiq = ixMatOnly(iState) - ! place constraint for matric head - if(xInc(ixLiq) > 1._rkind .and. stateVecPrev(ixLiq) > 0._rkind)then - xInc(ixLiq) = 1._rkind - endif ! if constraining matric head - end do ! (loop through soil layers) - endif ! (if there are both energy and liquid water state variables) - - endif ! (water positivity) + do iLayer=1,nSoil + ! check if the layer is included + if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle + if(ixHydType(ixSoilOnlyHyd(iLayer))==iname_watLayer .or. ixHydType(ixSoilOnlyHyd(iLayer))==iname_liqLayer)then + ! get the volumetric fraction of liquid water + volFracLiq = stateVecPrev(ixSoilOnlyHyd(iLayer)) + scalarIce = merge(0._rkind, mLayerVolFracIce(iLayer+nSnow), ixHydType(ixSoilOnlyHyd(iLayer))==iname_watLayer) + ! checking if drain more than what is available or add more than possible + if(-xInc(ixSoilOnlyHyd(iLayer)) > volFracLiq - theta_res(iLayer))then + xInc(ixSoilOnlyHyd(iLayer)) = -0.5_rkind*(volFracLiq - theta_res(iLayer)) + elseif(xInc(ixSoilOnlyHyd(iLayer)) > theta_sat(iLayer) - scalarIce - volFracLiq)then + xInc(ixSoilOnlyHyd(iLayer)) = -0.5_rkind*(theta_sat(iLayer) - scalarIce - volFracLiq) + endif + endif ! (if the state variable is not matric head) + end do ! (looping through soil layers) + endif ! (if there are state variables for liquid water in the soil domain) + + endif ! (water bounds) - ! Update the state vector with the modified iteration increment stateVec(:) = stateVecPrev(:) + xInc(:) diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index bd6b78086..8936c163f 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -430,8 +430,8 @@ subroutine checkFeas(& ! make association with variables in the data structures associate(& ! soil parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) ! model diagnostic variables from the previous solution mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) ! number of model layers, and layer type @@ -461,26 +461,26 @@ subroutine checkFeas(& if(ixCasNrg/=integerMissing)then if(stateVec(ixCasNrg) > canopyTempMax) feasible=.false. if(stateVec(ixCasNrg) > canopyTempMax) message=trim(message)//'canopy air space temp high,' - !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixCasNrg )', feasible, canopyTempMax, stateVec(ixCasNrg) + !if(stateVec(ixCasNrg) > canopyTempMax) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixCasNrg )', feasible, canopyTempMax, stateVec(ixCasNrg) endif ! check that the canopy air space temperature is reasonable if(ixVegNrg/=integerMissing)then if(stateVec(ixVegNrg) > canopyTempMax) feasible=.false. - !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixVegNrg )', feasible, canopyTempMax, stateVec(ixVegNrg) + !if(stateVec(ixVegNrg) > canopyTempMax) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixVegNrg )', feasible, canopyTempMax, stateVec(ixVegNrg) endif ! check canopy liquid water is not negative if(ixVegHyd/=integerMissing)then if(stateVec(ixVegHyd) < 0._rkind) feasible=.false. - !if(.not.feasible) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVec( ixVegHyd )', feasible, 0._rkind, stateVec(ixVegHyd) + !if(stateVec(ixVegHyd) < 0._rkind) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVec( ixVegHyd )', feasible, 0._rkind, stateVec(ixVegHyd) end if ! check snow temperature is below freezing if(count(ixSnowOnlyNrg/=integerMissing)>0)then if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. !do iLayer=1,nSnow - ! if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVec( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVec( ixSnowOnlyNrg(iLayer) ) + ! if(stateVec(ixSnowOnlyNrg(iLayer)) > Tfreeze) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVec( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVec( ixSnowOnlyNrg(iLayer) ) !enddo endif @@ -505,8 +505,9 @@ subroutine checkFeas(& ! --> check if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. - !if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax - + !if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) & + !write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax + endif ! if water states end do ! loop through non-missing hydrology state variables in the snow+soil domain diff --git a/build/source/engine/snowAlbedo.f90 b/build/source/engine/snowAlbedo.f90 index 922fcfd01..3eb914950 100644 --- a/build/source/engine/snowAlbedo.f90 +++ b/build/source/engine/snowAlbedo.f90 @@ -66,8 +66,8 @@ module snowAlbedo_module ! ******************************************************************************************************* subroutine snowAlbedo(& ! input: model control - dt, & ! intent(in): model time step (s) - snowPresence, & ! intent(in): logical flag to denote if snow is present + dt, & ! intent(in): model time step (s) + snowPresence, & ! intent(in): logical flag to denote if snow is present ! input/output: data structures model_decisions, & ! intent(in): model decisions mpar_data, & ! intent(in): model parameters @@ -75,36 +75,36 @@ subroutine snowAlbedo(& diag_data, & ! intent(inout): model diagnostic variables for a local HRU prog_data, & ! intent(inout): model prognostic variables for a local HRU ! output: error control - err,message) ! intent(out): error control + err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------------- ! provide access to desired modules USE snow_utils_module,only:fracliquid ! compute fraction of liquid water at a given temperature ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: model control - real(rkind),intent(in) :: dt ! model time step - logical(lgt),intent(in) :: snowPresence ! logical flag to denote if snow is present + real(rkind),intent(in) :: dt ! model time step + logical(lgt),intent(in) :: snowPresence ! logical flag to denote if snow is present ! input/output: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(in) :: flux_data ! model flux variables - type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: prog_data ! model prognostic variables for a local HRU + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(in) :: flux_data ! model flux variables + type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: prog_data ! model prognostic variables for a local HRU ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! local variables - integer(i4b),parameter :: ixVisible=1 ! named variable to define index in array of visible part of the spectrum - integer(i4b),parameter :: ixNearIR=2 ! named variable to define index in array of near IR part of the spectrum - real(rkind),parameter :: valueMissing=-9999._rkind ! missing value -- will cause problems if snow albedo is ever used for the non-snow case - real(rkind),parameter :: slushExp=10._rkind ! "slush" exponent, to increase decay when snow is near Tfreeze - real(rkind),parameter :: fractionLiqThresh=0.001_rkind ! threshold for the fraction of liquid water to switch to spring albedo minimum - real(rkind) :: fractionLiq ! fraction of liquid water (-) - real(rkind) :: age1,age2,age3 ! aging factors (-) - real(rkind) :: decayFactor ! albedo decay factor (-) - real(rkind) :: refreshFactor ! albedo refreshment factor, representing albedo increase due to snowfall (-) - real(rkind) :: albedoMin ! minimum albedo -- depends if in winter or spring conditions (-) - real(rkind) :: fZen ! factor to modify albedo at low zenith angles (-) - real(rkind),parameter :: bPar=2._rkind ! empirical parameter in fZen + integer(i4b),parameter :: ixVisible=1 ! named variable to define index in array of visible part of the spectrum + integer(i4b),parameter :: ixNearIR=2 ! named variable to define index in array of near IR part of the spectrum + real(rkind),parameter :: valueMissing=-9999._rkind ! missing value -- will cause problems if snow albedo is ever used for the non-snow case + real(rkind),parameter :: slushExp=10._rkind ! "slush" exponent, to increase decay when snow is near Tfreeze + real(rkind),parameter :: fractionLiqThresh=0.001_rkind ! threshold for the fraction of liquid water to switch to spring albedo minimum + real(rkind) :: fractionLiq ! fraction of liquid water (-) + real(rkind) :: age1,age2,age3 ! aging factors (-) + real(rkind) :: decayFactor ! albedo decay factor (-) + real(rkind) :: refreshFactor ! albedo refreshment factor, representing albedo increase due to snowfall (-) + real(rkind) :: albedoMin ! minimum albedo -- depends if in winter or spring conditions (-) + real(rkind) :: fZen ! factor to modify albedo at low zenith angles (-) + real(rkind),parameter :: bPar=2._rkind ! empirical parameter in fZen ! initialize error control err=0; message='snowAlbedo/' ! -------------------------------------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/stomResist.f90 b/build/source/engine/stomResist.f90 index 356e67e66..0a3819420 100644 --- a/build/source/engine/stomResist.f90 +++ b/build/source/engine/stomResist.f90 @@ -131,22 +131,22 @@ subroutine stomResist(& real(rkind),intent(in) :: scalarSatVP_VegTemp ! saturation vapor pressure at vegetation temperature (Pa) real(rkind),intent(in) :: scalarVP_CanopyAir ! canopy air vapor pressure (Pa) ! input: data structures - type(var_i),intent(in) :: type_data ! type of vegetation and soil - type(var_d),intent(in) :: forc_data ! model forcing data - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(var_i),intent(in) :: type_data ! type of vegetation and soil + type(var_d),intent(in) :: forc_data ! model forcing data + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(model_options),intent(in) :: model_decisions(:) ! model decisions ! input-output: data structures - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ------------------------------------------------------------------------------------------------------------------------------------------------------ + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ----------------------------------------------------------------------------------------------------------------------------------------------------- ! local variables - character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b),parameter :: ixSunlit=1 ! named variable for sunlit leaves - integer(i4b),parameter :: ixShaded=2 ! named variable for shaded leaves - integer(i4b) :: iSunShade ! index defining sunlit or shaded leaves + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b),parameter :: ixSunlit=1 ! named variable for sunlit leaves + integer(i4b),parameter :: ixShaded=2 ! named variable for shaded leaves + integer(i4b) :: iSunShade ! index defining sunlit or shaded leaves real(rkind) :: absorbedPAR ! absorbed PAR (W m-2) real(rkind) :: scalarStomResist ! stomatal resistance (s m-1) real(rkind) :: scalarPhotosynthesis ! photosynthesis (umol CO2 m-2 s-1) @@ -356,35 +356,35 @@ subroutine stomResist_flex(& ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! input: state and diagnostic variables - real(rkind),intent(in) :: scalarVegetationTemp ! vegetation temperature (K) - real(rkind),intent(in) :: scalarSatVP_VegTemp ! saturation vapor pressure at vegetation temperature (Pa) - real(rkind),intent(in) :: scalarVP_CanopyAir ! canopy air vapor pressure (Pa) - real(rkind),intent(in) :: absorbedPAR ! absorbed PAR (W m-2) + real(rkind),intent(in) :: scalarVegetationTemp ! vegetation temperature (K) + real(rkind),intent(in) :: scalarSatVP_VegTemp ! saturation vapor pressure at vegetation temperature (Pa) + real(rkind),intent(in) :: scalarVP_CanopyAir ! canopy air vapor pressure (Pa) + real(rkind),intent(in) :: absorbedPAR ! absorbed PAR (W m-2) ! input: data structures - type(var_d),intent(in) :: forc_data ! model forcing data - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU - type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(var_d),intent(in) :: forc_data ! model forcing data + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU + type(model_options),intent(in) :: model_decisions(:) ! model decisions ! output: stomatal resistance and photosynthesis - real(rkind),intent(inout) :: ci ! intercellular co2 partial pressure (Pa) - real(rkind),intent(out) :: scalarStomResist ! stomatal resistance (s m-1) - real(rkind),intent(out) :: scalarPhotosynthesis ! photosynthesis (umol CO2 m-2 s-1) + real(rkind),intent(inout) :: ci ! intercellular co2 partial pressure (Pa) + real(rkind),intent(out) :: scalarStomResist ! stomatal resistance (s m-1) + real(rkind),intent(out) :: scalarPhotosynthesis ! photosynthesis (umol CO2 m-2 s-1) ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! general local variables - logical(lgt),parameter :: testDerivs=.false. ! flag to test the derivatives - real(rkind) :: unitConv ! unit conversion factor (mol m-3, convert m s-1 --> mol H20 m-2 s-1) - real(rkind) :: rlb ! leaf boundary layer rersistance (umol-1 m2 s) - real(rkind) :: x0,x1,x2 ! temporary variables - real(rkind) :: co2compPt ! co2 compensation point (Pa) - real(rkind) :: fHum ! humidity function, fraction [0,1] + logical(lgt),parameter :: testDerivs=.false. ! flag to test the derivatives + real(rkind) :: unitConv ! unit conversion factor (mol m-3, convert m s-1 --> mol H20 m-2 s-1) + real(rkind) :: rlb ! leaf boundary layer rersistance (umol-1 m2 s) + real(rkind) :: x0,x1,x2 ! temporary variables + real(rkind) :: co2compPt ! co2 compensation point (Pa) + real(rkind) :: fHum ! humidity function, fraction [0,1] ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! fixed parameters - integer(i4b),parameter :: maxiter=20 ! maximum number of iterations - integer(i4b),parameter :: maxiter_noahMP=3 ! maximum number of iterations for Noah-MP + integer(i4b),parameter :: maxiter=20 ! maximum number of iterations + integer(i4b),parameter :: maxiter_noahMP=3 ! maximum number of iterations for Noah-MP real(rkind),parameter :: convToler=0.0001_rkind ! convergence tolerance (Pa) real(rkind),parameter :: umol_per_mol=1.e+6_rkind ! factor to relate umol to mol real(rkind),parameter :: o2scaleFactor=0.105_rkind ! scaling factor used to compute co2 compesation point (0.21/2) @@ -396,41 +396,41 @@ subroutine stomResist_flex(& real(rkind),parameter :: fnf=0.6666666667_rkind ! foliage nitrogen factor (-) ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! photosynthesis - real(rkind) :: Kc,Ko ! Michaelis-Menten constants for co2 and o2 (Pa) - real(rkind) :: vcmax25 ! maximum Rubisco carboxylation rate at 25 deg C (umol m-2 s-1) - real(rkind) :: jmax25 ! maximum electron transport rate at 25 deg C (umol m-2 s-1) - real(rkind) :: vcmax ! maximum Rubisco carboxylation rate (umol m-2 s-1) - real(rkind) :: jmax ! maximum electron transport rate (umol m-2 s-1) - real(rkind) :: aQuad ! the quadratic coefficient in the quadratic equation - real(rkind) :: bQuad ! the linear coefficient in the quadratic equation - real(rkind) :: cQuad ! the constant in the quadratic equation - real(rkind) :: bSign ! sign of the linear coeffcient - real(rkind) :: xTemp ! temporary variable in the quadratic equation - real(rkind) :: qQuad ! the "q" term in the quadratic equation - real(rkind) :: root1,root2 ! roots of the quadratic function - real(rkind) :: Js ! scaled electron transport rate (umol co2 m-2 s-1) - real(rkind) :: I_ps2 ! PAR absorbed by PS2 (umol photon m-2 s-1) - real(rkind) :: awb ! Michaelis-Menten control (Pa) - real(rkind) :: cp2 ! additional controls in light-limited assimilation (Pa) - real(rkind) :: psn ! leaf gross photosynthesis rate (umol co2 m-2 s-1) - real(rkind) :: dA_dc ! derivative in photosynthesis w.r.t. intercellular co2 concentration + real(rkind) :: Kc,Ko ! Michaelis-Menten constants for co2 and o2 (Pa) + real(rkind) :: vcmax25 ! maximum Rubisco carboxylation rate at 25 deg C (umol m-2 s-1) + real(rkind) :: jmax25 ! maximum electron transport rate at 25 deg C (umol m-2 s-1) + real(rkind) :: vcmax ! maximum Rubisco carboxylation rate (umol m-2 s-1) + real(rkind) :: jmax ! maximum electron transport rate (umol m-2 s-1) + real(rkind) :: aQuad ! the quadratic coefficient in the quadratic equation + real(rkind) :: bQuad ! the linear coefficient in the quadratic equation + real(rkind) :: cQuad ! the constant in the quadratic equation + real(rkind) :: bSign ! sign of the linear coeffcient + real(rkind) :: xTemp ! temporary variable in the quadratic equation + real(rkind) :: qQuad ! the "q" term in the quadratic equation + real(rkind) :: root1,root2 ! roots of the quadratic function + real(rkind) :: Js ! scaled electron transport rate (umol co2 m-2 s-1) + real(rkind) :: I_ps2 ! PAR absorbed by PS2 (umol photon m-2 s-1) + real(rkind) :: awb ! Michaelis-Menten control (Pa) + real(rkind) :: cp2 ! additional controls in light-limited assimilation (Pa) + real(rkind) :: psn ! leaf gross photosynthesis rate (umol co2 m-2 s-1) + real(rkind) :: dA_dc ! derivative in photosynthesis w.r.t. intercellular co2 concentration ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! stomatal resistance - real(rkind) :: gMin ! scaled minimum conductance (umol m-2 s-1) - real(rkind) :: cs ! co2 partial pressure at leaf surface (Pa) - real(rkind) :: csx ! control of co2 partial pressure at leaf surface on stomatal conductance (Pa) - real(rkind) :: g0 ! stomatal conductance in the absence of humidity controls (umol m-2 s-1) - real(rkind) :: ci_old ! intercellular co2 partial pressure (Pa) - real(rkind) :: rs ! stomatal resistance (umol-1 m2 s) - real(rkind) :: dg0_dc ! derivative in g0 w.r.t intercellular co2 concentration (umol m-2 s-1 Pa-1) - real(rkind) :: drs_dc ! derivative in stomatal resistance w.r.t. intercellular co2 concentration - real(rkind) :: dci_dc ! final derivative (-) + real(rkind) :: gMin ! scaled minimum conductance (umol m-2 s-1) + real(rkind) :: cs ! co2 partial pressure at leaf surface (Pa) + real(rkind) :: csx ! control of co2 partial pressure at leaf surface on stomatal conductance (Pa) + real(rkind) :: g0 ! stomatal conductance in the absence of humidity controls (umol m-2 s-1) + real(rkind) :: ci_old ! intercellular co2 partial pressure (Pa) + real(rkind) :: rs ! stomatal resistance (umol-1 m2 s) + real(rkind) :: dg0_dc ! derivative in g0 w.r.t intercellular co2 concentration (umol m-2 s-1 Pa-1) + real(rkind) :: drs_dc ! derivative in stomatal resistance w.r.t. intercellular co2 concentration + real(rkind) :: dci_dc ! final derivative (-) ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! iterative solution - real(rkind) :: func1,func2 ! functions for numerical derivative calculation - real(rkind) :: cMin,cMax ! solution brackets - real(rkind) :: xInc ! iteration increment (Pa) - integer(i4b) :: iter ! iteration index + real(rkind) :: func1,func2 ! functions for numerical derivative calculation + real(rkind) :: cMin,cMax ! solution brackets + real(rkind) :: xInc ! iteration increment (Pa) + integer(i4b) :: iter ! iteration index ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! associate variables in the data structure associate(& diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 0ec77b10a..32e4f4327 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -175,7 +175,7 @@ subroutine summaSolve4ida( & real(qp),intent(in) :: sMul(:) ! state vector multiplier (used in the residual calculations) real(rkind), intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(model_options),intent(in) :: model_decisions(:) ! model decisions type(zLookup), intent(in) :: lookup_data ! lookup tables type(var_i), intent(in) :: type_data ! type of vegetation and soil type(var_d), intent(in) :: attr_data ! spatial attributes diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index 8a5bea193..027271b3f 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -166,7 +166,7 @@ subroutine summaSolve4kinsol(& real(qp),intent(in) :: sMul(:) ! state vector multiplier (used in the residual calculations) real(rkind), intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(model_options),intent(in) :: model_decisions(:) ! model decisions type(zLookup), intent(in) :: lookup_data ! lookup tables type(var_i), intent(in) :: type_data ! type of vegetation and soil type(var_d), intent(in) :: attr_data ! spatial attributes diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 8bd2bf765..36b7bf29b 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -23,13 +23,13 @@ module summaSolve4numrec_module ! data types USE nrtype -! constants -USE multiconst,only:Tfreeze ! freezing point of pure water (K) -USE multiconst,only:iden_water ! intrinsic density of liquid water (kg m-3) - ! access the global print flag USE globalData,only:globalPrintFlag +! domain types +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil + ! access missing values USE globalData,only:integerMissing ! missing integer USE globalData,only:realMissing ! missing double precision number @@ -61,6 +61,9 @@ module summaSolve4numrec_module USE var_lookup,only:iLookINDEX ! named variables for structure elements USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure +USE multiconst,only:& + iden_water ! intrinsic density of liquid water (kg m-3) + ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) @@ -139,6 +142,7 @@ subroutine summaSolve4numrec(& converged, & ! intent(out): convergence flag err,message) ! intent(out): error control USE computJacob_module, only: computJacob + USE eval8summa_module, only: imposeConstraints USE matrixOper_module, only: lapackSolv USE matrixOper_module, only: scaleMatrices implicit none @@ -420,18 +424,17 @@ subroutine lineSearchRefinement(doLineSearch,stateVecTrial,newtStepScaled,aJacSc ! if enthalpy, then need to convert the iteration increment to temperature !if(nrgFormulation==ix_enthalpy) xInc(ixNrgOnly) = xInc(ixNrgOnly)/dMat(ixNrgOnly) - ! impose solution constraints + ! state vector with proposed iteration increment + stateVecNew = stateVecTrial + xInc + + ! impose solution constraints adjusting state vector and iteration increment ! NOTE: We may not need to do this (or at least, do ALL of this), as we can probably rely on the line search here - ! But, imposeConstraints does not impose maxes on temps or water which will make it infeasible - call imposeConstraints(stateVecTrial,xInc,err,cmessage) + call imposeConstraints(model_decisions,indx_data,prog_data,mpar_data,stateVecNew,stateVecTrial,nState,nSoil,nSnow,cmessage,err) if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! compute the iteration increment - stateVecNew = stateVecTrial + xInc + xInc = stateVecNew - stateVecTrial ! compute the residual vector and function - ! NOTE: This calls eval8summa in an internal subroutine - ! The internal sub routine has access to all data + ! NOTE: This calls eval8summa in an internal subroutine which has access to all data ! Hence, we only need to include the variables of interest in lineSearch call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) @@ -610,10 +613,8 @@ subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fl logical(lgt) :: feasible ! feasibility of the solution logical(lgt) :: doBisection ! flag to do the bi-section logical(lgt) :: bracketsDefined ! flag to define if the brackets are defined - !integer(i4b) :: iCheck ! check the model state variables (not used) integer(i4b),parameter :: nCheck=100 ! number of times to check the model state variables real(rkind),parameter :: delX=1._rkind ! trial increment - !real(rkind) :: xIncrement(nState) ! trial increment (not used) ! -------------------------------------------------------------------------------------------------------- err=0; message='safeRootfinder/' @@ -659,12 +660,13 @@ subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fl ! * case 2: the iteration increment is the correct sign else - ! impose solution constraints - call imposeConstraints(stateVecTrial,xInc,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! compute the iteration increment + ! state vector with proposed iteration increment stateVecNew = stateVecTrial + xInc + + ! impose solution constraints adjusting state vector and iteration increment + call imposeConstraints(model_decisions,indx_data,prog_data,mpar_data,stateVecNew,stateVecTrial,nState,nSoil,nSnow,cmessage,err) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + xInc = stateVecNew - stateVecTrial endif ! if the iteration increment is the same sign as the residual vector @@ -680,7 +682,7 @@ subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fl call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! check feasibility (should be feasible because of the call to imposeConstraints + ! check feasibility (should be feasible because of the call to imposeConstraints, except if canopyTemp>canopyTempMax (500._rkind)) if(.not.feasible)then; err=20; message=trim(message)//'state vector not feasible'; return; endif ! check convergence @@ -701,6 +703,7 @@ subroutine getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,message) integer(i4b),intent(inout) :: err ! error code character(*),intent(out) :: message ! error message ! locals + real(rkind) :: stateVecPrev(nState) ! iteration state vector integer(i4b) :: iCheck ! check the model state variables integer(i4b),parameter :: nCheck=100 ! number of times to check the model state variables logical(lgt) :: feasible ! feasibility of the solution @@ -711,6 +714,7 @@ subroutine getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,message) ! initialize state vector stateVecNew = stateVecTrial + stateVecPrev = stateVecNew ! get xIncrement xIncrement = -sign((/delX/),rVec) @@ -718,18 +722,19 @@ subroutine getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,message) ! try the increment a few times do iCheck=1,nCheck - ! impose solution constraints - call imposeConstraints(stateVecNew,xIncrement,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + ! state vector with proposed iteration increment + stateVecNew = stateVecPrev + xIncrement - ! increment state vector - stateVecNew = stateVecNew + xIncrement + ! impose solution constraints adjusting state vector and iteration increment + call imposeConstraints(model_decisions,indx_data,prog_data,mpar_data,stateVecNew,stateVecPrev,nState,nSoil,nSnow,cmessage,err) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + xIncrement = stateVecNew - stateVecPrev ! evaluate summa call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! check that the trial value is feasible (should not happen because of the call to impose constraints) + ! check feasibility (should be feasible because of the call to imposeConstraints, except if canopyTemp>canopyTempMax (500._rkind)) if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif ! update brackets @@ -748,6 +753,9 @@ subroutine getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,message) err=20; return endif + ! Save the state vector + stateVecPrev = stateVecNew + end do ! multiple checks end subroutine getBrackets @@ -1096,263 +1104,6 @@ function checkConv(rVec,xInc,xVec) end function checkConv - - ! ********************************************************************************************************* - ! internal subroutine imposeConstraints: impose solution constraints - ! ********************************************************************************************************* - subroutine imposeConstraints(stateVecTrial,xInc,err,message) - ! external functions - USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water at a given temperature (snow) - USE soil_utils_module,only:crit_soilT ! compute the critical temperature below which ice exists - implicit none - ! dummies - real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector - real(rkind),intent(inout) :: xInc(:) ! iteration increment - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ----------------------------------------------------------------------------------------------------- - ! temporary variables for model constraints - real(rkind) :: cInc ! constrained temperature increment (K) -- simplified bi-section - real(rkind) :: xIncFactor ! scaling factor for the iteration increment (-) - integer(i4b) :: iMax(1) ! index of maximum temperature - real(rkind) :: scalarTemp ! temperature of an individual snow layer (K) - real(rkind) :: volFracLiq ! volumetric liquid water content of an individual snow layer (-) - logical(lgt),dimension(nSnow) :: drainFlag ! flag to denote when drainage exceeds available capacity - logical(lgt),dimension(nSoil) :: crosFlag ! flag to denote temperature crossing from unfrozen to frozen (or vice-versa) - logical(lgt) :: crosTempVeg ! flag to denoote where temperature crosses the freezing point - real(rkind) :: xPsi00 ! matric head after applying the iteration increment (m) - real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) - real(rkind) :: critDiff ! temperature difference from critical (K) - real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical (K) - real(rkind),parameter :: zMaxTempIncrement=1._rkind ! maximum temperature increment (K), NOTE: this can cause problems especially from a cold start when we are far from the solution - ! indices of model state variables - integer(i4b) :: iState ! index of state within a specific variable type - integer(i4b) :: ixNrg,ixLiq ! index of energy and mass state variables in full state vector - ! indices of model layers - integer(i4b) :: iLayer ! index of model layer - ! ----------------------------------------------------------------------------------------------------- - ! associate variables with indices of model state variables - associate(& - ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for energy states - ixHydOnly => indx_data%var(iLookINDEX%ixHydOnly)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for hydrology states - ixMatOnly => indx_data%var(iLookINDEX%ixMatOnly)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for matric head states - ixMassOnly => indx_data%var(iLookINDEX%ixMassOnly)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for canopy storage states - ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] named variables defining the states in the subset - ! indices for specific state variables - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow-soil subdomain - ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow-soil subdomain - ! vector of energy indices for the snow and soil domains - ! NOTE: states not in the subset are equal to integerMissing - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain - ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow domain - ixSoilOnlyNrg => indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the soil domain - ! vector of hydrology indices for the snow and soil domains - ! NOTE: states not in the subset are equal to integerMissing - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow domain - ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the soil domain - ! number of state variables of a specific type - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain - nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain - nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain - ! state variables at the start of the time step - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat & ! intent(in): [dp(:)] matric head (m) - ) ! associating variables with indices of model state variables - ! ----------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='imposeConstraints/' - - ! ** limit temperature increment to zMaxTempIncrement - if(any(abs(xInc(ixNrgOnly)) > zMaxTempIncrement))then - iMax = maxloc( abs(xInc(ixNrgOnly)) ) ! index of maximum temperature increment - xIncFactor = abs( zMaxTempIncrement/xInc(ixNrgOnly(iMax(1))) ) ! scaling factor for the iteration increment (-) - xInc = xIncFactor*xInc - end if - - ! ** impose solution constraints for vegetation - ! (stop just above or just below the freezing point if crossing) - ! -------------------------------------------------------------------------------------------------------------------- - ! canopy temperatures - - if(ixVegNrg/=integerMissing)then - - ! initialize - critDiff = Tfreeze - stateVecTrial(ixVegNrg) - crosTempVeg = .false. - - ! initially frozen (T < Tfreeze) - if(critDiff > 0._rkind)then - if(xInc(ixVegNrg) > critDiff)then - crosTempVeg = .true. - cInc = critDiff + epsT ! constrained temperature increment (K) - end if - - ! initially unfrozen (T > Tfreeze) - else - if(xInc(ixVegNrg) < critDiff)then - crosTempVeg = .true. - cInc = critDiff - epsT ! constrained temperature increment (K) - end if - - end if ! switch between frozen and unfrozen - - ! scale iterations - if(crosTempVeg)then - xIncFactor = cInc/xInc(ixVegNrg) ! scaling factor for the iteration increment (-) - xInc = xIncFactor*xInc ! scale iteration increments - endif - - endif ! if the state variable for canopy temperature is included within the state subset - - ! -------------------------------------------------------------------------------------------------------------------- - ! canopy liquid water - - if(ixVegHyd/=integerMissing)then - - ! check if new value of storage will be negative - if(stateVecTrial(ixVegHyd)+xInc(ixVegHyd) < 0._rkind)then - ! scale iteration increment - cInc = -0.5_rkind*stateVecTrial(ixVegHyd) ! constrained iteration increment (K) -- simplified bi-section - xIncFactor = cInc/xInc(ixVegHyd) ! scaling factor for the iteration increment (-) - xInc = xIncFactor*xInc ! new iteration increment - end if - - endif ! if the state variable for canopy water is included within the state subset - - ! -------------------------------------------------------------------------------------------------------------------- - ! ** impose solution constraints for snow - if(nSnowOnlyNrg > 0)then - - ! loop through snow layers - checksnow: do iLayer=1,nSnow ! necessary to ensure that NO layers rise above Tfreeze - - ! check of the data is mising - if(ixSnowOnlyNrg(iLayer)==integerMissing) cycle - - ! check temperatures, and, if necessary, scale iteration increment - iState = ixSnowOnlyNrg(iLayer) - if(stateVecTrial(iState) + xInc(iState) > Tfreeze)then - ! scale iteration increment - cInc = 0.5_rkind*(Tfreeze - stateVecTrial(iState) ) ! constrained temperature increment (K) -- simplified bi-section - xIncFactor = cInc/xInc(iState) ! scaling factor for the iteration increment (-) - xInc = xIncFactor*xInc - end if ! if snow temperature > freezing - - end do checkSnow - - endif ! if there are state variables for energy in the snow domain - - ! -------------------------------------------------------------------------------------------------------------------- - ! - check if drain more than what is available - ! NOTE: change in total water is only due to liquid flux - if(nSnowOnlyHyd>0)then - - ! loop through snow layers - do iLayer=1,nSnow - - ! * check if the layer is included - if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle - - ! * get the layer temperature (from stateVecTrial if ixSnowOnlyNrg(iLayer) is within the state vector - if(ixSnowOnlyNrg(iLayer)/=integerMissing)then - scalarTemp = stateVecTrial( ixSnowOnlyNrg(iLayer) ) - - ! * get the layer temperature from the last update - else - scalarTemp = prog_data%var(iLookPROG%mLayerTemp)%dat(iLayer) - endif - - ! * get the volumetric fraction of liquid water - select case( ixStateType_subset( ixSnowOnlyHyd(iLayer) ) ) - case(iname_watLayer); volFracLiq = fracliquid(scalarTemp,mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1)) * stateVecTrial(ixSnowOnlyHyd(iLayer)) - case(iname_liqLayer); volFracLiq = stateVecTrial(ixSnowOnlyHyd(iLayer)) - case default; err=20; message=trim(message)//'expect ixStateType_subset to be iname_watLayer or iname_liqLayer for snow hydrology'; return - end select - - ! * check that the iteration increment does not exceed volumetric liquid water content - if(-xInc(ixSnowOnlyHyd(iLayer)) > volFracLiq)then - drainFlag(iLayer) = .true. - xInc(ixSnowOnlyHyd(iLayer)) = -0.5_rkind*volFracLiq - endif - - end do ! looping through snow layers - - endif ! if there are state variables for liquid water in the snow domain - - ! -------------------------------------------------------------------------------------------------------------------- - ! ** impose solution constraints for soil temperature - if(nSoilOnlyNrg>0)then - do iLayer=1,nSoil - - ! - check if energy state is included - if(ixSoilOnlyNrg(iLayer)==integerMissing) cycle - - ! - define index of the state variables within the state subset - ixNrg = ixSoilOnlyNrg(iLayer) - ixLiq = ixSoilOnlyHyd(iLayer) - - ! get the matric potential of total water - if(ixLiq/=integerMissing)then - xPsi00 = stateVecTrial(ixLiq) + xInc(ixLiq) - else - xPsi00 = mLayerMatricHead(iLayer) - endif - - ! identify the critical point when soil begins to freeze (TcSoil) - TcSoil = crit_soilT(xPsi00) - - ! get the difference from the current state and the crossing point (K) - critDiff = TcSoil - stateVecTrial(ixNrg) - - ! * initially frozen (T < TcSoil) - if(critDiff > 0._rkind)then - - ! (check crossing above zero) - if(xInc(ixNrg) > critDiff)then - crosFlag(iLayer) = .true. - xInc(ixNrg) = critDiff + epsT ! set iteration increment to slightly above critical temperature - endif - - ! * initially unfrozen (T > TcSoil) - else - - ! (check crossing below zero) - if(xInc(ixNrg) < critDiff)then - crosFlag(iLayer) = .true. - xInc(ixNrg) = critDiff - epsT ! set iteration increment to slightly below critical temperature - endif - - endif ! (switch between initially frozen and initially unfrozen) - - end do ! (loop through soil layers) - endif ! (if there are both energy and liquid water state variables) - - ! ** impose solution constraints matric head - if(size(ixMatOnly)>0)then - do iState=1,size(ixMatOnly) - - ! - define index of the hydrology state variable within the state subset - ixLiq = ixMatOnly(iState) - - ! - place constraint for matric head - if(xInc(ixLiq) > 1._rkind .and. stateVecTrial(ixLiq) > 0._rkind)then - xInc(ixLiq) = 1._rkind - endif ! if constraining matric head - - end do ! (loop through soil layers) - endif ! (if there are both energy and liquid water state variables) - - ! end association with variables with indices of model state variables - end associate - - end subroutine imposeConstraints - end subroutine summaSolve4numrec diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index f17a31d1c..19e16ac5c 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -131,7 +131,6 @@ def make_default_path(suffix): # Find the segment ID seg_id = read_from_control(controlFile,'river_network_shp_segid') - ## Load all shapefiles and project to Albers Conformal Conic and reproject # Set the target CRS acc = 'ESRI:102008' @@ -161,7 +160,7 @@ def make_default_path(suffix): eff = pd.read_csv(viz_dir/eff_fil, sep=',', header=None, names=['CPU Efficiency', 'Array ID', 'Job Wall-clock time', 'Node Number']) # Extract only the values after the ':' character in the 'CPU Efficiency', 'Job Wall-clock time', and 'Node Number' columns eff['CPU Efficiency'] = eff['CPU Efficiency'].str.split(':').str[1].astype(float) - eff['Array ID'] = eff['Array ID'].str.split(':').str[1].astype(int) + eff['Array ID'] = eff['Array ID'].str.split(':').str[1].astype(int) eff['Job Wall-clock time'] = eff['Job Wall-clock time'].str.split(':').str[1].astype(float) eff['Node Number'] = eff['Node Number'].str.split(':').str[1].astype(int) @@ -184,7 +183,7 @@ def make_default_path(suffix): batch0 = int(row['Array ID']) eff0 = row['CPU Efficiency'] # Store the value for the current batch in the dictionary - efficiency[batch0] = eff0 + efficiency[batch0] = eff0 # Select the values for the current batch using boolean indexing eff_batch = np.array([efficiency[b] for b in batch]) #node_batch = np.array([node[b] for b in batch]) #not currently using @@ -231,12 +230,12 @@ def make_default_path(suffix): def run_loop(i,var,the_max,f_x,f_y): my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap - my_cmap.set_bad(color='white') #nan color white + my_cmap.set_bad(color='white') #nan color white vmin,vmax = 0, the_max norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) - if stat=='kgem' and var!='wallClockTime': + if stat=='kgem' and var!='wallClockTime': my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno')) # copy the default cmap - my_cmap.set_bad(color='white') #nan color white + my_cmap.set_bad(color='white') #nan color white vmin,vmax = the_max, 1.0 norm=matplotlib.colors.Normalize(vmin=vmin, vmax=vmax) r = i//2 From 6756d29937310e1ecabe31448a6c2186aa3d4b2a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 1 Sep 2023 11:28:43 +0900 Subject: [PATCH 0907/1472] ixHydType only on water layers --- build/source/engine/eval8summa.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 2c2602ed5..a9aa65a14 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -1082,7 +1082,7 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st do iLayer=1,nSoil ! check if the layer is included if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle - if(ixHydType(ixSoilOnlyHyd(iLayer))==iname_watLayer .or. ixHydType(ixSoilOnlyHyd(iLayer))==iname_liqLayer)then + if(ixHydType(iLayer+nSnow)==iname_watLayer .or. ixHydType(iLayer+nSnow)==iname_liqLayer)then ! get the volumetric fraction of liquid water volFracLiq = stateVecPrev(ixSoilOnlyHyd(iLayer)) scalarIce = merge(0._rkind, mLayerVolFracIce(iLayer+nSnow), ixHydType(ixSoilOnlyHyd(iLayer))==iname_watLayer) From 5e44cf06f1de14fb2b06ea649e40b09a81981052 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 1 Sep 2023 11:49:25 +0900 Subject: [PATCH 0908/1472] more of last commit --- build/source/engine/eval8summa.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index a9aa65a14..b2c6aa842 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -1085,7 +1085,7 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st if(ixHydType(iLayer+nSnow)==iname_watLayer .or. ixHydType(iLayer+nSnow)==iname_liqLayer)then ! get the volumetric fraction of liquid water volFracLiq = stateVecPrev(ixSoilOnlyHyd(iLayer)) - scalarIce = merge(0._rkind, mLayerVolFracIce(iLayer+nSnow), ixHydType(ixSoilOnlyHyd(iLayer))==iname_watLayer) + scalarIce = merge(0._rkind, mLayerVolFracIce(iLayer+nSnow), ixHydType(iLayer+nSnow)==iname_watLayer) ! checking if drain more than what is available or add more than possible if(-xInc(ixSoilOnlyHyd(iLayer)) > volFracLiq - theta_res(iLayer))then xInc(ixSoilOnlyHyd(iLayer)) = -0.5_rkind*(volFracLiq - theta_res(iLayer)) From fd2e721b9436221a2bc3142cc27be8a07e306cc6 Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 1 Sep 2023 06:21:57 -0600 Subject: [PATCH 0909/1472] Implemented data structures to shorten the call syntax of vegLiqFlux. --- build/source/engine/computFlux.f90 | 91 ++++++++++++++++++------------ build/source/engine/vegLiqFlux.f90 | 40 +++++++------ 2 files changed, 74 insertions(+), 57 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index c8cc28c4f..fd1174b38 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -34,7 +34,10 @@ module computFlux_module out_type_vegNrgFlux,& ! intent(out) arguments for vegNrgFlux call in_type_ssdNrgFlux, & ! intent(in) arguments for ssdNrgFlux call io_type_ssdNrgFlux, & ! intent(inout) arguments for ssdNrgFlux call - out_type_ssdNrgFlux ! intent(out) arguments for ssdNrgFlux call + out_type_ssdNrgFlux,& ! intent(out) arguments for ssdNrgFlux call + in_type_vegLiqFlux, & ! intent(in) arguments for vegLiqFlux call + out_type_vegLiqFlux ! intent(out) arguments for vegLiqFlux call + ! indices that define elements of the data structures USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure @@ -223,6 +226,8 @@ subroutine computFlux(& type(in_type_ssdNrgFlux) :: in_ssdNrgFlux ! data structure for intent(in) ssdNrgFlux arguments type(io_type_ssdNrgFlux) :: io_ssdNrgFlux ! data structure for intent(inout) ssdNrgFlux arguments type(out_type_ssdNrgFlux) :: out_ssdNrgFlux ! data structure for intent(out) ssdNrgFlux arguments + type(in_type_vegLiqFlux) :: in_vegLiqFlux ! data structure for intent(in) vegLiqFlux arguments + type(out_type_vegLiqFlux) :: out_vegLiqFlux ! data structure for intent(out) vegLiqFlux arguments ! -------------------------------------------------------------- ! initialize error control err=0; message='computFlux/' @@ -430,41 +435,10 @@ subroutine computFlux(& ! check the need to compute the liquid water fluxes through vegetation if (ixVegHyd/=integerMissing) then - ! calculate liquid water fluxes through vegetation - call vegLiqFlux(& - ! input - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation - scalarCanopyLiqTrial, & ! intent(in): trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) - scalarRainfall, & ! intent(in): rainfall rate (kg m-2 s-1) - ! input-output: data structures - mpar_data, & ! intent(in): model parameters - diag_data, & ! intent(in): local HRU diagnostic model variables - ! output - scalarThroughfallRain, & ! intent(out): rain that reaches the ground without ever touching the canopy (kg m-2 s-1) - scalarCanopyLiqDrainage, & ! intent(out): drainage of liquid water from the vegetation canopy (kg m-2 s-1) - scalarThroughfallRainDeriv, & ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) - scalarCanopyLiqDrainageDeriv, & ! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) - err,cmessage) ! intent(out): error control - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! calculate the net liquid water flux for the vegetation canopy - scalarCanopyNetLiqFlux = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage - - ! calculate the total derivative in the downward liquid flux - scalarCanopyLiqDeriv = scalarThroughfallRainDeriv + scalarCanopyLiqDrainageDeriv - - ! test - if (globalPrintFlag) then - print*, '**' - print*, 'scalarRainfall = ', scalarRainfall - print*, 'scalarThroughfallRain = ', scalarThroughfallRain - print*, 'scalarCanopyEvaporation = ', scalarCanopyEvaporation - print*, 'scalarCanopyLiqDrainage = ', scalarCanopyLiqDrainage - print*, 'scalarCanopyNetLiqFlux = ', scalarCanopyNetLiqFlux - print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial - end if - + call subTools(iLookOP%pre,iLookROUTINE%vegLiqFlux) ! pre-processing for call to vegLiqFlux + call vegLiqFlux(in_vegLiqFlux,mpar_data,diag_data,out_vegLiqFlux) + call subTools(iLookOP%post,iLookROUTINE%vegLiqFlux) ! post-processing for call to vegLiqFlux end if ! end if computing the liquid water fluxes through vegetation ! ***** @@ -1055,15 +1029,60 @@ subroutine subTools(op,sub) end do end if case(iLookROUTINE%vegLiqFlux) ! vegLiqFlux - + if (op==iLookOP%pre) then ! pre-processing + ! intent(in) arguments + in_vegLiqFlux % computeVegFlux =computeVegFlux ! intent(in): flag to denote if computing energy flux over vegetation + in_vegLiqFlux % scalarCanopyLiqTrial=scalarCanopyLiqTrial ! intent(in): trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) + in_vegLiqFlux % scalarRainfall =scalarRainfall ! intent(in): rainfall rate (kg m-2 s-1) + else ! post-processing + ! intent(out) arguments + scalarThroughfallRain =out_vegLiqFlux % scalarThroughfallRain ! intent(out): rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + scalarCanopyLiqDrainage =out_vegLiqFlux % scalarCanopyLiqDrainage ! intent(out): drainage of liquid water from the vegetation canopy (kg m-2 s-1) + scalarThroughfallRainDeriv =out_vegLiqFlux % scalarThroughfallRainDeriv ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) + scalarCanopyLiqDrainageDeriv=out_vegLiqFlux % scalarCanopyLiqDrainageDeriv! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) + err =out_vegLiqFlux % err ! intent(out): error code + cmessage =out_vegLiqFlux % cmessage ! intent(out): error control + ! error control + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + ! calculate the net liquid water flux for the vegetation canopy + scalarCanopyNetLiqFlux = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + ! calculate the total derivative in the downward liquid flux + scalarCanopyLiqDeriv = scalarThroughfallRainDeriv + scalarCanopyLiqDrainageDeriv + ! test + if (globalPrintFlag) then + print*, '**' + print*, 'scalarRainfall = ', scalarRainfall + print*, 'scalarThroughfallRain = ', scalarThroughfallRain + print*, 'scalarCanopyEvaporation = ', scalarCanopyEvaporation + print*, 'scalarCanopyLiqDrainage = ', scalarCanopyLiqDrainage + print*, 'scalarCanopyNetLiqFlux = ', scalarCanopyNetLiqFlux + print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial + end if + end if case(iLookROUTINE%snowLiqFlx) ! snowLiqFlx + if (op==iLookOP%pre) then ! pre-processing + + else ! post-processing + end if case(iLookROUTINE%soilLiqFlx) ! soilLiqFlx + if (op==iLookOP%pre) then ! pre-processing + + else ! post-processing + end if case(iLookROUTINE%groundwatr) ! groundwatr + if (op==iLookOP%pre) then ! pre-processing + + else ! post-processing + end if case(iLookROUTINE%bigAquifer) ! bigAquifer + if (op==iLookOP%pre) then ! pre-processing + + else ! post-processing + end if case default ! Error control for sub argument (must be s subroutine index that is included in the above case blocks) err=20 cmessage="Error in subTools: invalid sub argument requested." diff --git a/build/source/engine/vegLiqFlux.f90 b/build/source/engine/vegLiqFlux.f90 index 2c695c645..7e7de362c 100644 --- a/build/source/engine/vegLiqFlux.f90 +++ b/build/source/engine/vegLiqFlux.f90 @@ -26,6 +26,8 @@ module vegLiqFlux_module ! data types USE data_types,only:var_d ! x%var(:) (rkind) USE data_types,only:var_dlength ! x%var(:)%dat (rkind) +USE data_types,only:in_type_vegLiqFlux ! derived type for intent(in) arguments +USE data_types,only:out_type_vegLiqFlux ! derived type for intent(out) arguments ! named variables USE var_lookup,only:iLookPARAM,iLookDIAG ! named variables for structure elements @@ -49,41 +51,37 @@ module vegLiqFlux_module ! public subroutine vegLiqFlux: compute water balance for the vegetation canopy ! ************************************************************************************************ subroutine vegLiqFlux(& - ! input - computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation - scalarCanopyLiqTrial, & ! intent(in): trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) - scalarRainfall, & ! intent(in): rainfall rate (kg m-2 s-1) + ! input: + in_vegLiqFlux, & ! intent(in): model control, trial value, and rainfall rate ! input-output: data structures mpar_data, & ! intent(in): model parameters diag_data, & ! intent(in): local HRU model diagnostic variables ! output - scalarThroughfallRain, & ! intent(out): rain that reaches the ground without ever touching the canopy (kg m-2 s-1) - scalarCanopyLiqDrainage, & ! intent(out): drainage of liquid water from the vegetation canopy (kg m-2 s-1) - scalarThroughfallRainDeriv, & ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) - scalarCanopyLiqDrainageDeriv, & ! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) - err,message) ! intent(out): error control + out_vegLiqFlux) ! intent(out): output rates, derivatives, and error control implicit none ! input - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) - real(rkind),intent(in) :: scalarCanopyLiqTrial ! trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) - real(rkind),intent(in) :: scalarRainfall ! rainfall (kg m-2 s-1) + type(in_type_vegLiqFlux) :: in_vegLiqFlux ! model control, trial value, and rainfall rate ! input-output: data structures - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for the local basin + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for the local basin ! output - real(rkind),intent(out) :: scalarThroughfallRain ! rain that reaches the ground without ever touching the canopy (kg m-2 s-1) - real(rkind),intent(out) :: scalarCanopyLiqDrainage ! drainage of liquid water from the vegetation canopy (kg m-2 s-1) - real(rkind),intent(out) :: scalarThroughfallRainDeriv ! derivative in throughfall w.r.t. canopy liquid water (s-1) - real(rkind),intent(out) :: scalarCanopyLiqDrainageDeriv ! derivative in canopy drainage w.r.t. canopy liquid water (s-1) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + type(out_type_vegLiqFlux) :: out_vegLiqFlux ! output rates, derivatives, and error control ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! make association of local variables with information in the data structures associate(& + computeVegFlux => in_vegLiqFlux % computeVegFlux, & ! intent(in): flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) + scalarCanopyLiqTrial => in_vegLiqFlux % scalarCanopyLiqTrial, & ! intent(in): trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) + scalarRainfall => in_vegLiqFlux % scalarRainfall, & ! intent(in): rainfall (kg m-2 s-1) ixCanopyInterception => model_decisions(iLookDECISIONS%cIntercept)%iDecision, & ! intent(in): index defining choice of parameterization for canopy interception scalarCanopyLiqMax => diag_data%var(iLookDIAG%scalarCanopyLiqMax)%dat(1), & ! intent(in): maximum storage before canopy drainage begins (kg m-2 s-1) scalarThroughfallScaleRain => mpar_data%var(iLookPARAM%throughfallScaleRain)%dat(1),& ! intent(in): fraction of rain that hits the ground without touching the canopy (-) - scalarCanopyDrainageCoeff => mpar_data%var(iLookPARAM%canopyDrainageCoeff)%dat(1) & ! intent(in): canopy drainage coefficient (s-1) + scalarCanopyDrainageCoeff => mpar_data%var(iLookPARAM%canopyDrainageCoeff)%dat(1), & ! intent(in): canopy drainage coefficient (s-1) + scalarThroughfallRain => out_vegLiqFlux % scalarThroughfallRain, & ! intent(out): rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + scalarCanopyLiqDrainage => out_vegLiqFlux % scalarCanopyLiqDrainage, & ! intent(out): drainage of liquid water from the vegetation canopy (kg m-2 s-1) + scalarThroughfallRainDeriv => out_vegLiqFlux % scalarThroughfallRainDeriv, & ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) + scalarCanopyLiqDrainageDeriv => out_vegLiqFlux % scalarCanopyLiqDrainageDeriv, & ! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) + err => out_vegLiqFlux % err, & ! intent(out): error code + message => out_vegLiqFlux % cmessage & ! intent(out): error message ) ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! initialize error control From 13873715b9276cdafbf97e6f7738a33c260d61a6 Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 1 Sep 2023 15:29:04 -0600 Subject: [PATCH 0910/1472] Added derived types for snowLiqFlx arguments. --- build/source/dshare/data_types.f90 | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 8e39b32e3..48d1aad32 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -391,7 +391,28 @@ MODULE data_types real(rkind) :: scalarThroughfallRainDeriv ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) real(rkind) :: scalarCanopyLiqDrainageDeriv ! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error control + character(:),allocatable :: cmessage ! intent(out): error message end type out_type_vegLiqFlux ! ** end vegLiqFlux + + ! ** snowLiqFlx + type, public :: in_type_snowLiqFlx ! derived type for intent(in) arguments in snowLiqFlx call + integer(i4b) :: nSnow ! intent(in): number of snow layers + logical(lgt) :: firstFluxCall ! intent(in): the first flux call (compute variables that are constant over the iterations) + logical(lgt) :: scalarSolution ! intent(in): flag to indicate the scalar solution + real(rkind) :: scalarThroughfallRain ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) + real(rkind) :: scalarCanopyLiqDrainage ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) + real(rkind), allocatable :: mLayerVolFracLiqTrial(:) ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) + end type in_type_snowLiqFlx + + type, public :: io_type_snowLiqFlx ! derived type for intent(inout) arguments in snowLiqFlx call + real(rkind), allocatable :: iLayerLiqFluxSnow(:) ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) + real(rkind), allocatable :: iLayerLiqFluxSnowDeriv(:) ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) + end type io_type_snowLiqFlx + + type, public :: out_type_snowLiqFlx ! derived type for intent(out) arguments in snowLiqFlx call + integer(i4b) :: err ! intent(out): error code + character(:),allocatable :: cmessage ! intent(out): error message + end type out_type_snowLiqFlx + ! ** end snowLiqFlx END MODULE data_types From d263dea478229dec1e013e11918b4b40ca4f969f Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 2 Sep 2023 04:02:19 -0600 Subject: [PATCH 0911/1472] Implement and interface snowLiqFlx argument data structures in computFlux.f90 and snowLiqFlx.f90. --- build/source/engine/computFlux.f90 | 84 +++++++++++++++--------------- build/source/engine/snowLiqFlx.f90 | 62 +++++++++++----------- 2 files changed, 73 insertions(+), 73 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index fd1174b38..5526c7ed4 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -36,8 +36,10 @@ module computFlux_module io_type_ssdNrgFlux, & ! intent(inout) arguments for ssdNrgFlux call out_type_ssdNrgFlux,& ! intent(out) arguments for ssdNrgFlux call in_type_vegLiqFlux, & ! intent(in) arguments for vegLiqFlux call - out_type_vegLiqFlux ! intent(out) arguments for vegLiqFlux call - + out_type_vegLiqFlux,& ! intent(out) arguments for vegLiqFlux call + in_type_snowLiqFlx, & ! intent(in) arguments for snowLiqFlx call + io_type_snowLiqFlx, & ! intent(inout) arguments for snowLiqFlx call + out_type_snowLiqFlx ! intent(out) arguments for snowLiqFlx call ! indices that define elements of the data structures USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure @@ -228,6 +230,9 @@ subroutine computFlux(& type(out_type_ssdNrgFlux) :: out_ssdNrgFlux ! data structure for intent(out) ssdNrgFlux arguments type(in_type_vegLiqFlux) :: in_vegLiqFlux ! data structure for intent(in) vegLiqFlux arguments type(out_type_vegLiqFlux) :: out_vegLiqFlux ! data structure for intent(out) vegLiqFlux arguments + type(in_type_snowLiqFlx) :: in_snowLiqFlx ! data structure for intent(in) snowLiqFlx arguments + type(io_type_snowLiqFlx) :: io_snowLiqFlx ! data structure for intent(inout) snowLiqFlx arguments + type(out_type_snowLiqFlx) :: out_snowLiqFlx ! data structure for intent(out) snowLiqFlx arguments ! -------------------------------------------------------------- ! initialize error control err=0; message='computFlux/' @@ -447,46 +452,10 @@ subroutine computFlux(& ! check the need to compute liquid water fluxes through snow if (nSnowOnlyHyd>0) then - ! compute liquid fluxes through snow - call snowLiqFlx(& - ! input: model control - nSnow, & ! intent(in): number of snow layers - firstFluxCall, & ! intent(in): the first flux call (compute variables that are constant over the iterations) - (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution - ! input: forcing for the snow domain - scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) - scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) - ! input: model state vector - mLayerVolFracLiqTrial(1:nSnow), & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) - ! input-output: data structures - indx_data, & ! intent(in): model indices - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output: fluxes and derivatives - iLayerLiqFluxSnow(0:nSnow), & ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) - iLayerLiqFluxSnowDeriv(0:nSnow), & ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) - ! output: error control - err,cmessage) ! intent(out): error control - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! define forcing for the soil domain - scalarRainPlusMelt = iLayerLiqFluxSnow(nSnow) ! drainage from the base of the snowpack - - ! calculate net liquid water fluxes for each snow layer (s-1) - do iLayer=1,nSnow - mLayerLiqFluxSnow(iLayer) = -(iLayerLiqFluxSnow(iLayer) - iLayerLiqFluxSnow(iLayer-1))/mLayerDepth(iLayer) - end do - - ! compute drainage from the soil zone (needed for mass balance checks) - scalarSnowDrainage = iLayerLiqFluxSnow(nSnow) - - ! save bottom layer of snow derivatives - above_soilLiqFluxDeriv = iLayerLiqFluxSnowDeriv(nSnow) ! derivative in vertical liquid water flux at bottom snow layer interface - above_soildLiq_dTk = mLayerdTheta_dTk(nSnow) ! derivative in volumetric liquid water content in bottom snow layer w.r.t. temperature - above_soilFracLiq = mLayerFracLiqSnow(nSnow) ! fraction of liquid water in bottom snow layer (-) - + call subTools(iLookOP%pre,iLookROUTINE%snowLiqFlx) ! pre-processing for call to snowLiqFlx + call snowLiqFlx(in_snowLiqFlx,indx_data,mpar_data,prog_data,diag_data,io_snowLiqFlx,out_snowLiqFlx) + call subTools(iLookOP%post,iLookROUTINE%snowLiqFlx) ! post-processing for call to snowLiqFlx else ! define forcing for the soil domain for the case of no snow layers @@ -919,6 +888,8 @@ subroutine subTools(op,sub) dAquiferTrans_dTGround => deriv_data%var(iLookDERIV%dAquiferTrans_dTGround )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. ground temperature dAquiferTrans_dCanWat => deriv_data%var(iLookDERIV%dAquiferTrans_dCanWat )%dat(1) & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy total water ) ! end associating to data in structures + ! initialize error control + err=0; message='subTools/' ! Validate operation argument if ((op/=iLookOP%pre).and.(op/=iLookOP%post)) then @@ -1061,9 +1032,36 @@ subroutine subTools(op,sub) end if case(iLookROUTINE%snowLiqFlx) ! snowLiqFlx if (op==iLookOP%pre) then ! pre-processing - + ! intent(in) arguments + in_snowLiqFlx % nSnow =nSnow ! intent(in): number of snow layers + in_snowLiqFlx % firstFluxCall =firstFluxCall ! intent(in): the first flux call (compute variables that are constant over the iterations) + in_snowLiqFlx % scalarSolution =(scalarSolution .and. .not.firstFluxCall) ! intent(in): flag to indicate the scalar solution + in_snowLiqFlx % scalarThroughfallRain =scalarThroughfallRain ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) + in_snowLiqFlx % scalarCanopyLiqDrainage=scalarCanopyLiqDrainage ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) + in_snowLiqFlx % mLayerVolFracLiqTrial =mLayerVolFracLiqTrial(1:nSnow) ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) + io_snowLiqFlx % iLayerLiqFluxSnow =iLayerLiqFluxSnow ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) + io_snowLiqFlx % iLayerLiqFluxSnowDeriv =iLayerLiqFluxSnowDeriv ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) else ! post-processing - + ! intent(inout) arguments + iLayerLiqFluxSnow =io_snowLiqFlx % iLayerLiqFluxSnow ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) + iLayerLiqFluxSnowDeriv=io_snowLiqFlx % iLayerLiqFluxSnowDeriv ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) + ! intent(out) arguments + err =out_snowLiqFlx % err ! intent(out): error code + cmessage=out_snowLiqFlx % cmessage ! intent(out): error message + ! error control + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + ! define forcing for the soil domain + scalarRainPlusMelt = iLayerLiqFluxSnow(nSnow) ! drainage from the base of the snowpack + ! calculate net liquid water fluxes for each snow layer (s-1) + do iLayer=1,nSnow + mLayerLiqFluxSnow(iLayer) = -(iLayerLiqFluxSnow(iLayer) - iLayerLiqFluxSnow(iLayer-1))/mLayerDepth(iLayer) + end do + ! compute drainage from the soil zone (needed for mass balance checks) + scalarSnowDrainage = iLayerLiqFluxSnow(nSnow) + ! save bottom layer of snow derivatives + above_soilLiqFluxDeriv = iLayerLiqFluxSnowDeriv(nSnow) ! derivative in vertical liquid water flux at bottom snow layer interface + above_soildLiq_dTk = mLayerdTheta_dTk(nSnow) ! derivative in volumetric liquid water content in bottom snow layer w.r.t. temperature + above_soilFracLiq = mLayerFracLiqSnow(nSnow) ! fraction of liquid water in bottom snow layer (-) end if case(iLookROUTINE%soilLiqFlx) ! soilLiqFlx if (op==iLookOP%pre) then ! pre-processing diff --git a/build/source/engine/snowLiqFlx.f90 b/build/source/engine/snowLiqFlx.f90 index d79744421..3bb524fbd 100644 --- a/build/source/engine/snowLiqFlx.f90 +++ b/build/source/engine/snowLiqFlx.f90 @@ -38,6 +38,9 @@ module snowLiqFlx_module USE data_types,only:var_d ! x%var(:) [rkind] USE data_types,only:var_dlength ! x%var(:)%dat [rkind] USE data_types,only:var_ilength ! x%var(:)%dat [i4b] +USE data_types,only:in_type_snowLiqFlx ! data type for intent(in) arguments +USE data_types,only:io_type_snowLiqFlx ! data type for intent(inout) arguments +USE data_types,only:out_type_snowLiqFlx ! data type for intent(out) arguments ! privacy implicit none @@ -48,48 +51,32 @@ module snowLiqFlx_module ! public subroutine snowLiqFlx: compute liquid water flux through the snowpack ! ************************************************************************************************ subroutine snowLiqFlx(& - ! input: model control - nSnow, & ! intent(in): number of snow layers - firstFluxCall, & ! intent(in): the first flux call - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: forcing for the snow domain - scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) - scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) - ! input: model state vector - mLayerVolFracLiqTrial, & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) + ! input: model control, forcing, and model state vector + in_snowLiqFlx, & ! intent(in): model control, forcing, and model state vector ! input-output: data structures indx_data, & ! intent(in): model indices mpar_data, & ! intent(in): model parameters prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output: fluxes and derivatives - iLayerLiqFluxSnow, & ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) - iLayerLiqFluxSnowDeriv, & ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) + ! input-output: fluxes and derivatives + io_snowLiqFlx, & ! intent(inout): fluxes and derivatives ! output: error control - err,message) ! intent(out): error control + out_snowLiqFlx) ! intent(out): error control implicit none - ! input: model control - integer(i4b),intent(in) :: nSnow ! number of snow layers - logical(lgt),intent(in) :: firstFluxCall ! the first flux call - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - ! input: forcing for the snow domain - real(rkind),intent(in) :: scalarThroughfallRain ! computed throughfall rate (kg m-2 s-1) - real(rkind),intent(in) :: scalarCanopyLiqDrainage ! computed drainage of liquid water (kg m-2 s-1) - ! input: model state vector - real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value of volumetric fraction of liquid water at the current iteration (-) + ! input: model control, forcing, and model state vector + type(in_type_snowLiqFlx) :: in_snowLiqFlx ! model control, forcing, and model state vector ! input-output: data structures type(var_ilength),intent(in) :: indx_data ! model indices type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - ! output: fluxes and derivatives - real(rkind),intent(inout) :: iLayerLiqFluxSnow(0:) ! vertical liquid water flux at layer interfaces (m s-1) - real(rkind),intent(inout) :: iLayerLiqFluxSnowDeriv(0:) ! derivative in vertical liquid water flux at layer interfaces (m s-1) + ! input-output: fluxes and derivatives + type(io_type_snowLiqFlx) :: io_snowLiqFlx ! fluxes and derivatives ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + type(out_type_snowLiqFlx) :: out_snowLiqFlx ! error control ! ------------------------------ ------------------------------------------------------------------------------------------------------------ ! local variables + integer(i4b) :: nSnow ! number of snow layers integer(i4b) :: i ! search index for scalar solution integer(i4b) :: iLayer ! layer index integer(i4b) :: ixTop ! top layer in subroutine call @@ -102,7 +89,16 @@ subroutine snowLiqFlx(& real(rkind) :: relSaturn ! relative saturation [0,1] (-) ! ------------------------------------------------------------------------------------------------------------------------------------------ ! make association of local variables with information in the data structures + nSnow=in_snowLiqFlx % nSnow ! get number of snow layers associate(& + ! input: model control + firstFluxCall => in_snowLiqFlx % firstFluxCall, & ! intent(in): the first flux call + scalarSolution => in_snowLiqFlx % scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution + ! input: forcing for the snow domain + scalarThroughfallRain => in_snowLiqFlx % scalarThroughfallRain, & ! intent(in): computed throughfall rate (kg m-2 s-1) + scalarCanopyLiqDrainage => in_snowLiqFlx % scalarCanopyLiqDrainage, & ! intent(in): computed drainage of liquid water (kg m-2 s-1) + ! input: model state vector + mLayerVolFracLiqTrial => in_snowLiqFlx % mLayerVolFracLiqTrial, & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) ! input: layer indices ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat, & ! intent(in): index in the state subset for hydrology state variables in the snow domain @@ -111,10 +107,16 @@ subroutine snowLiqFlx(& Fcapil => mpar_data%var(iLookPARAM%Fcapil)%dat(1), & ! intent(in): capillary retention as a fraction of the total pore volume (-) k_snow => mpar_data%var(iLookPARAM%k_snow)%dat(1), & ! intent(in): hydraulic conductivity of snow (m s-1), 0.0055 = approx. 20 m/hr, from UEB mw_exp => mpar_data%var(iLookPARAM%mw_exp)%dat(1), & ! intent(in): exponent for meltwater flow (-) - ! input/output: diagnostic variables -- only computed for the first iteration + ! input-output: diagnostic variables -- only computed for the first iteration mLayerPoreSpace => diag_data%var(iLookDIAG%mLayerPoreSpace)%dat, & ! intent(inout): pore space in each snow layer (-) - mLayerThetaResid => diag_data%var(iLookDIAG%mLayerThetaResid)%dat & ! intent(inout): esidual volumetric liquid water content in each snow layer (-) - ) ! association of local variables with information in the data structures + mLayerThetaResid => diag_data%var(iLookDIAG%mLayerThetaResid)%dat, & ! intent(inout): esidual volumetric liquid water content in each snow layer (-) + ! input-output: fluxes and derivatives + iLayerLiqFluxSnow => io_snowLiqFlx % iLayerLiqFluxSnow, & ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) + iLayerLiqFluxSnowDeriv => io_snowLiqFlx % iLayerLiqFluxSnowDeriv, & ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) + ! output: error control + err => out_snowLiqFlx % err, & ! intent(out): error code + message => out_snowLiqFlx % cmessage & ! intent(out): error message + ) ! end association of local variables with information in the data structures ! ------------------------------------------------------------------------------------------------------------------------------------------ ! initialize error control err=0; message='snowLiqFlx/' From 9d3087c56ad01b7fe090b1da6cc05acf21397eb3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 5 Sep 2023 16:41:51 +0900 Subject: [PATCH 0912/1472] updating utilities --- utils/hist_per_GRU.py | 6 +++--- utils/input_per_GRU.py | 1 + utils/plot_per_GRU.py | 3 +-- utils/scat_per_GRU.py | 3 ++- utils/wallClock_per_GRU.py | 6 +++--- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index c2e2214cf..6603964ea 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -34,7 +34,7 @@ import sys # The first input argument specifies the run where the files are stat = sys.argv[1] - method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument + method_name=['be1','be4','be8','be16','be32','sundials_1en4','sundials_1en6'] #maybe make this an argument # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] @@ -89,7 +89,6 @@ fig,axs = plt.subplots(3,2,figsize=(35,33)) else: fig,axs = plt.subplots(3,2,figsize=(140,133)) - def run_loop(i,var,mx): r = i//2 @@ -145,12 +144,13 @@ def run_loop(i,var,mx): if stat == 'rmse': stat_word = ' Hourly RMSE' if stat == 'maxe': stat_word = ' Hourly max abs error' if stat == 'kgem': stat_word = ' Hourly KGEm' + fig.suptitle('Histograms of Hourly Statistics for each GRU', fontsize=40) # wall Clock doesn't do difference if var == 'wallClockTime': if stat == 'rmse' or stat == 'kgem': stat_word = ' Hourly mean' if stat == 'maxe': stat_word = ' Hourly max' - axs[r,c].set_yscale('log') #log y axis for wall clock time to exaggerate peaks + #axs[r,c].set_yscale('log') #log y axis for wall clock time to exaggerate peaks axs[r,c].legend() axs[r,c].set_title(plt_titl[i] + stat_word) diff --git a/utils/input_per_GRU.py b/utils/input_per_GRU.py index acdcea45a..574b1fc8e 100644 --- a/utils/input_per_GRU.py +++ b/utils/input_per_GRU.py @@ -221,6 +221,7 @@ def make_default_path(suffix): fig,axs = plt.subplots(3,2,figsize=(35,33)) else: fig,axs = plt.subplots(3,2,figsize=(140,133)) +fig.suptitle('{} Wallclock, Batch, and Node'.format(method_name), fontsize=40) plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 19e16ac5c..bd154b99c 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -101,7 +101,6 @@ def make_default_path(suffix): return defaultPath - ## Catchment shapefile location and variable names # HM catchment shapefile path & name hm_catchment_path = read_from_control(controlFile,'catchment_shp_path') @@ -115,7 +114,6 @@ def make_default_path(suffix): # Find the GRU and HRU identifiers hm_hruid = read_from_control(controlFile,'catchment_shp_hruid') - ## River network shapefile location and variable names # Plot rivers? plot_rivers = False @@ -219,6 +217,7 @@ def make_default_path(suffix): fig,axs = plt.subplots(3,2,figsize=(35,33)) else: fig,axs = plt.subplots(3,2,figsize=(140,133)) +fig.suptitle('{} Hourly Statistics'.format(method_name), fontsize=40) plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index 43c2ed798..55be1f53a 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -33,7 +33,7 @@ import sys # The first input argument specifies the run where the files are stat = sys.argv[1] - method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument + method_name=['be1','be4','be8','be16','be32','sundials_1en4','sundials_1en6'] #maybe make this an argument # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] @@ -84,6 +84,7 @@ fig,axs = plt.subplots(3,2,figsize=(35,33)) else: fig,axs = plt.subplots(3,2,figsize=(140,133)) +fig.suptitle('Hourly Errors and Values for each GRU', fontsize=40) def run_loop(i,var): diff --git a/utils/wallClock_per_GRU.py b/utils/wallClock_per_GRU.py index 1e3a9b1c2..f0d240273 100644 --- a/utils/wallClock_per_GRU.py +++ b/utils/wallClock_per_GRU.py @@ -25,11 +25,10 @@ testing = False if testing: viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be1','be64','sundials_1en6'] #maybe make this an argument + method_name=['be1','be64',] #maybe make this an argument else: import sys - method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument - method_name=['be1','be16','be32'] #sundials will not show node differences as much + method_name=['be1','be4','be8','be16','be32'] #sundials will not show node differences as much # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] @@ -77,6 +76,7 @@ fig,axs = plt.subplots(2,2,figsize=(35,33)) else: fig,axs = plt.subplots(2,2,figsize=(140,133)) +fig.suptitle('BE Hourly Wallclock for each GRU', fontsize=40) def run_loop(c,stat): From acd994cdaa67291908174e8d1916e6d22820ca3f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 5 Sep 2023 20:19:11 +0900 Subject: [PATCH 0913/1472] cut off 0 in histogram utilities --- utils/hist_per_GRU.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 6603964ea..0c18ec0c9 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -47,7 +47,7 @@ # Specify variables of interest plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] -plt_titl = ['(a) Snow Water Equivalent >0','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] +plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] #fig_fil = '{}_hrly_diff_hist_{}_{}_zoom_compressed.png' @@ -136,7 +136,6 @@ def run_loop(i,var,mx): if stat == 'maxe': s = np.fabs(s) # make absolute value norm range = (0,mx) - if var == 'scalarSWE': range = (1.e-10,mx) # SWE has a lot of zeros if stat=='kgem' and var!='wallClockTime' : range = (mn,1) s.plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=m,linewidth=2.0,range=range) @@ -156,7 +155,7 @@ def run_loop(i,var,mx): axs[r,c].set_title(plt_titl[i] + stat_word) axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl[i])) axs[r,c].set_ylabel('GRU count') - + axs[r,c].set_ylim([0, 50000]) for i,(var,mx) in enumerate(zip(plot_vars,maxes)): run_loop(i,var,mx) From 1461d9d327a82de700af7f40e700a932bcee1413 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 5 Sep 2023 21:55:16 +0900 Subject: [PATCH 0914/1472] utilites --- utils/hist_per_GRU.py | 4 ++-- utils/scat_per_GRU.py | 2 +- utils/write_seff.bash | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 0c18ec0c9..5002c5f32 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -34,7 +34,7 @@ import sys # The first input argument specifies the run where the files are stat = sys.argv[1] - method_name=['be1','be4','be8','be16','be32','sundials_1en4','sundials_1en6'] #maybe make this an argument + method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] @@ -155,7 +155,7 @@ def run_loop(i,var,mx): axs[r,c].set_title(plt_titl[i] + stat_word) axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl[i])) axs[r,c].set_ylabel('GRU count') - axs[r,c].set_ylim([0, 50000]) + axs[r,c].set_ylim([0, 45000]) for i,(var,mx) in enumerate(zip(plot_vars,maxes)): run_loop(i,var,mx) diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index 55be1f53a..8a6b53097 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -33,7 +33,7 @@ import sys # The first input argument specifies the run where the files are stat = sys.argv[1] - method_name=['be1','be4','be8','be16','be32','sundials_1en4','sundials_1en6'] #maybe make this an argument + method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] diff --git a/utils/write_seff.bash b/utils/write_seff.bash index 91a981417..0b9295843 100755 --- a/utils/write_seff.bash +++ b/utils/write_seff.bash @@ -1,5 +1,5 @@ #!/bin/bash -# run as bash write_seff.bash /path/to/log.txt /path/to/output.txt +# run as write_seff.bash /path/to/log.txt /path/to/output.txt # Set the path to the log file LOG_FILE="$1" From 6632772c8e7d2641f9c0cef68db8e890b9eaca18 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 6 Sep 2023 11:38:52 +0900 Subject: [PATCH 0915/1472] utilites --- utils/largest_error_attrib.py | 73 +++++++++++++++++------------------ 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/utils/largest_error_attrib.py b/utils/largest_error_attrib.py index 43ab91cee..7e1e9b257 100644 --- a/utils/largest_error_attrib.py +++ b/utils/largest_error_attrib.py @@ -33,40 +33,39 @@ # Open the netCDF file with RMSE data summa = xr.open_dataset(src_file) -# Get the RMSE variable from the netCDF file -rmse_var = summa['scalarTotalSoilWat'].sel(stat='rmse') -#rmse_var = summa['scalarTotalET'].sel(stat='rmse') -#rmse_var = summa['averageRoutedRunoff'].sel(stat='rmse') - -# Mask the finite values of the RMSE variable -mask = np.isfinite(rmse_var) -rmse_var = rmse_var[mask] - -# Get the indices of the largest nBig RMSE values -max_rmse_indices = rmse_var.argsort()[-nBig:] - -# Get the largest RMSE values -rmse_of_max_rmse = rmse_var[max_rmse_indices.values] - -# Get the hru coordinate of the largest RMSE values -hru_of_max_rmse = rmse_var[max_rmse_indices.values].hru.values - -# Print all the values of the the biggest rmse hru -rmse_values = summa.sel(hru=hru_of_max_rmse[nBig-1],stat='rmse') -print(settings[0:5]) -print(rmse_values[settings[0]].values,rmse_values[settings[1]].values,rmse_values[settings[2]].values,rmse_values[settings[3]].values,rmse_values[settings[4]].values) - -# Open the netCDF file with local attributes -attr = xr.open_dataset(attr_fil) - -# Mask the HRU variable from the netCDF file -mask = attr['hruId'].isin(hru_of_max_rmse) - -# Get the vegTypeIndex variable from the netCDF file -veg_type_of_max_rmse = attr['vegTypeIndex'][mask] - -# Print the HRU, vegTypeIndex, and RMSE values of the largest RMSE values -print("HRU values of the largest RMSE values:", hru_of_max_rmse) -print("GRU values of the largest RMSE values:", gru_of_max_rmse) -print("vegTypeIndex values of the largest RMSE values:", veg_type_of_max_rmse.values) -print("RMSE values of the largest RMSE values:", rmse_of_max_rmse.values) \ No newline at end of file +for setting in settings: + # Get the RMSE variable from the netCDF file + rmse_var = summa[setting].sel(stat='rmse') + + # Mask the finite values of the RMSE variable + mask = np.isfinite(rmse_var) + rmse_var = rmse_var[mask] + + # Get the indices of the largest nBig RMSE values + max_rmse_indices = rmse_var.argsort()[-nBig:] + + # Get the largest RMSE values + rmse_of_max_rmse = rmse_var[max_rmse_indices.values] + + # Get the hru coordinate of the largest RMSE values + hru_of_max_rmse = rmse_var[max_rmse_indices.values].hru.values + + # Print all the values of the the biggest rmse hru + rmse_values = summa.sel(hru=hru_of_max_rmse[nBig-1],stat='rmse') + print(setting) + print(settings[0:5]) + print(rmse_values[settings[0]].values,rmse_values[settings[1]].values,rmse_values[settings[2]].values,rmse_values[settings[3]].values,rmse_values[settings[4]].values) + + # Open the netCDF file with local attributes + attr = xr.open_dataset(attr_fil) + + # Mask the HRU variable from the netCDF file + mask = attr['hruId'].isin(hru_of_max_rmse) + + # Get the vegTypeIndex variable from the netCDF file + veg_type_of_max_rmse = attr['vegTypeIndex'][mask] + + # Print the HRU, vegTypeIndex, and RMSE values of the largest RMSE values + print("HRU values of the largest RMSE values:", hru_of_max_rmse) + print("vegTypeIndex values of the largest RMSE values:", veg_type_of_max_rmse.values) + print("RMSE values of the largest RMSE values:", rmse_of_max_rmse.values) \ No newline at end of file From 0c67b0f56d42b1843ba11806037d65d18c281a00 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 7 Sep 2023 17:53:32 -0600 Subject: [PATCH 0916/1472] Implemented and applied derived types for intent(in) and intent(out) soilLiqFlx arguments -- intent(inout) arguments in progress. --- build/source/dshare/data_types.f90 | 57 +++++++++++++ build/source/engine/computFlux.f90 | 123 +++++++++++++++-------------- build/source/engine/soilLiqFlx.f90 | 121 +++++++++++++--------------- 3 files changed, 175 insertions(+), 126 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 48d1aad32..66d058b14 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -415,4 +415,61 @@ MODULE data_types character(:),allocatable :: cmessage ! intent(out): error message end type out_type_snowLiqFlx ! ** end snowLiqFlx + + ! ** soilLiqFlx + type, public :: in_type_soilLiqFlx ! derived type for intent(in) arguments in soilLiqFlx call + integer(i4b) :: nSoil ! intent(in): number of soil layers + logical(lgt) :: firstSplitOper ! intent(in): flag indicating first flux call in a splitting operation + logical(lgt) :: scalarSolution ! intent(in): flag to indicate the scalar solution + logical(lgt) :: deriv_desired ! intent(in): flag indicating if derivatives are desired + !! input: trial state variables + real(rkind), allocatable :: mLayerTempTrial(:) ! intent(in): trial temperature at the current iteration (K) + real(rkind), allocatable :: mLayerMatricHeadTrial(:) ! intent(in): matric potential (m) + real(rkind), allocatable :: mLayerMatricHeadLiqTrial(:) ! intent(in): liquid water matric potential (m) + real(rkind), allocatable :: mLayerVolFracLiqTrial(:) ! intent(in): volumetric fraction of liquid water (-) + real(rkind), allocatable :: mLayerVolFracIceTrial(:) ! intent(in): volumetric fraction of ice (-) + !! input: pre-computed deriavatives + real(rkind), allocatable :: mLayerdTheta_dTk(:) ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + real(rkind), allocatable :: dPsiLiq_dTemp(:) ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) + real(rkind) :: dCanopyTrans_dCanWat ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + real(rkind) :: dCanopyTrans_dTCanair ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(rkind) :: dCanopyTrans_dTCanopy ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(rkind) :: dCanopyTrans_dTGround ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + real(rkind) :: above_soilLiqFluxDeriv ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + real(rkind) :: above_soildLiq_dTk ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + real(rkind) :: above_soilFracLiq ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) + !! input: fluxes + real(rkind) :: scalarCanopyTranspiration ! intent(in): canopy transpiration (kg m-2 s-1) + real(rkind) :: scalarGroundEvaporation ! intent(in): ground evaporation (kg m-2 s-1) + real(rkind) :: scalarRainPlusMelt ! intent(in): rain plus melt (m s-1) + end type in_type_soilLiqFlx + + type, public :: io_type_soilLiqFlx ! derived type for intent(inout) arguments in soilLiqFlx call + real(rkind) :: scalarMaxInfilRate ! intent(inout): maximum infiltration rate (m s-1) + real(rkind) :: scalarInfilArea ! intent(inout): fraction of unfrozen area where water can infiltrate (-) + real(rkind) :: scalarFrozenArea ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) + real(rkind) :: scalarSurfaceRunoff ! intent(inout): surface runoff (m s-1) + real(rkind), allocatable :: mLayerdTheta_dPsi(:) ! intent(inout): derivative in the soil water characteristic w.r.t. psi (m-1) + real(rkind), allocatable :: mLayerdPsi_dTheta(:) ! intent(inout): derivative in the soil water characteristic w.r.t. theta (m) + real(rkind), allocatable :: dHydCond_dMatric(:) ! intent(inout): derivative in hydraulic conductivity w.r.t matric head (s-1) + real(rkind) :: scalarInfiltration ! intent(inout): surface infiltration rate (m s-1) -- controls on infiltration only computed for iter==1 + real(rkind), allocatable :: iLayerLiqFluxSoil(:) ! intent(inout): liquid fluxes at layer interfaces (m s-1) + real(rkind), allocatable :: mLayerTranspire(:) ! intent(inout): transpiration loss from each soil layer (m s-1) + real(rkind), allocatable :: mLayerHydCond(:) ! intent(inout): hydraulic conductivity in each layer (m s-1) + real(rkind), allocatable :: dq_dHydStateAbove(:) ! intent(inout): derivatives in the flux w.r.t. matric head in the layer above (s-1) + real(rkind), allocatable :: dq_dHydStateBelow(:) ! intent(inout): derivatives in the flux w.r.t. matric head in the layer below (s-1) + real(rkind), allocatable :: dq_dHydStateLayerSurfVec(:) ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) + real(rkind), allocatable :: dq_dNrgStateAbove(:) ! intent(inout): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) + real(rkind), allocatable :: dq_dNrgStateBelow(:) ! intent(inout): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) + real(rkind), allocatable :: dq_dNrgStateLayerSurfVec(:) ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) + real(rkind), allocatable :: mLayerdTrans_dTCanair(:) ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + real(rkind), allocatable :: mLayerdTrans_dTCanopy(:) ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy temperature + real(rkind), allocatable :: mLayerdTrans_dTGround(:) ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. ground temperature + real(rkind), allocatable :: mLayerdTrans_dCanWat(:) ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy total water + end type io_type_soilLiqFlx + + type, public :: out_type_soilLiqFlx ! derived type for intent(out) arguments in soilLiqFlx call + integer(i4b) :: err ! intent(out): error code + character(:),allocatable :: cmessage ! intent(out): error message + end type out_type_soilLiqFlx END MODULE data_types diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 5526c7ed4..ae17f900a 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -39,7 +39,10 @@ module computFlux_module out_type_vegLiqFlux,& ! intent(out) arguments for vegLiqFlux call in_type_snowLiqFlx, & ! intent(in) arguments for snowLiqFlx call io_type_snowLiqFlx, & ! intent(inout) arguments for snowLiqFlx call - out_type_snowLiqFlx ! intent(out) arguments for snowLiqFlx call + out_type_snowLiqFlx,& ! intent(out) arguments for snowLiqFlx call + in_type_soilLiqFlx, & ! intent(in) arguments for soilLiqFlx call + io_type_soilLiqFlx, & ! intent(inout) arguments for soilLiqFlx call + out_type_soilLiqFlx ! intent(in) arguments for soilLiqFlx call ! indices that define elements of the data structures USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure @@ -233,6 +236,9 @@ subroutine computFlux(& type(in_type_snowLiqFlx) :: in_snowLiqFlx ! data structure for intent(in) snowLiqFlx arguments type(io_type_snowLiqFlx) :: io_snowLiqFlx ! data structure for intent(inout) snowLiqFlx arguments type(out_type_snowLiqFlx) :: out_snowLiqFlx ! data structure for intent(out) snowLiqFlx arguments + type(in_type_soilLiqFlx) :: in_soilLiqFlx ! data structure for intent(in) soilLiqFlx arguments + type(io_type_soilLiqFlx) :: io_soilLiqFlx ! data structure for intent(inout) soilLiqFlx arguments + type(out_type_soilLiqFlx) :: out_soilLiqFlx ! data structure for intent(out) soilLiqFlx arguments ! -------------------------------------------------------------- ! initialize error control err=0; message='computFlux/' @@ -490,32 +496,9 @@ subroutine computFlux(& if (nSoilOnlyHyd>0) then ! calculate the liquid flux through soil + call subTools(iLookOP%pre,iLookROUTINE%soilLiqFlx) ! pre-processing for call to soilLiqFlx call soilLiqFlx(& - ! input: model control - nSoil, & ! intent(in): number of soil layers - firstSplitOper, & ! intent(in): flag indicating first flux call in a splitting operation - (scalarSolution .and. .not.firstFluxCall), & ! intent(in): flag to indicate the scalar solution - .true., & ! intent(in): flag indicating if derivatives are desired - ! input: trial state variables - mLayerTempTrial(nSnow+1:nLayers), & ! intent(in): trial temperature at the current iteration (K) - mLayerMatricHeadTrial(1:nSoil), & ! intent(in): matric potential (m) - mLayerMatricHeadLiqTrial(1:nSoil), & ! intent(in): liquid water matric potential (m) - mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of ice (-) - ! input: pre-computed deriavatives - mLayerdTheta_dTk(nSnow+1:nLayers), & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - dPsiLiq_dTemp(1:nSoil), & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) - dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water - above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature - above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) - ! input: fluxes - scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) - scalarGroundEvaporation, & ! intent(in): ground evaporation (kg m-2 s-1) - scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) + in_soilLiqFlx, & ! input-output: data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model indices @@ -550,37 +533,8 @@ subroutine computFlux(& mLayerdTrans_dTGround, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. ground temperature mLayerdTrans_dCanWat, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy total water ! output: error control - err,cmessage) ! intent(out): error control - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! calculate net liquid water fluxes for each soil layer (s-1) - do iLayer=1,nSoil - mLayerLiqFluxSoil(iLayer) = -(iLayerLiqFluxSoil(iLayer) - iLayerLiqFluxSoil(iLayer-1))/mLayerDepth(iLayer+nSnow) - end do - - ! calculate the soil control on infiltration - if (nSnow==0) then - ! * case of infiltration into soil - if (scalarMaxInfilRate > scalarRainPlusMelt) then ! infiltration is not rate-limited - scalarSoilControl = (1._rkind - scalarFrozenArea)*scalarInfilArea - else - scalarSoilControl = 0._rkind ! (scalarRainPlusMelt exceeds maximum infiltration rate - end if - else - ! * case of infiltration into snow - scalarSoilControl = 1._rkind - end if - - ! compute drainage from the soil zone (needed for mass balance checks and in aquifer recharge) - scalarSoilDrainage = iLayerLiqFluxSoil(nSoil) - - ! expand derivatives to the total water matric potential - ! NOTE: arrays are offset because computing derivatives in interface fluxes, at the top and bottom of the layer respectively - if (globalPrintFlag) print*, 'dPsiLiq_dPsi0(1:nSoil) = ', dPsiLiq_dPsi0(1:nSoil) - dq_dHydStateAbove(1:nSoil) = dq_dHydStateAbove(1:nSoil) *dPsiLiq_dPsi0(1:nSoil) - dq_dHydStateBelow(0:nSoil-1) = dq_dHydStateBelow(0:nSoil-1)*dPsiLiq_dPsi0(1:nSoil) - dq_dHydStateLayerSurfVec(1:nSoil) = dq_dHydStateLayerSurfVec(1:nSoil)*dPsiLiq_dPsi0(1:nSoil) - + out_soilLiqFlx) + call subTools(iLookOP%post,iLookROUTINE%soilLiqFlx) ! post-processing for call to soilLiqFlx end if ! end if calculating the liquid flux through soil ! ***** @@ -1065,9 +1019,62 @@ subroutine subTools(op,sub) end if case(iLookROUTINE%soilLiqFlx) ! soilLiqFlx if (op==iLookOP%pre) then ! pre-processing - + ! intent(in) arguments + in_soilLiqFlx % nSoil =nSoil ! intent(in): number of soil layers + in_soilLiqFlx % firstSplitOper=firstSplitOper ! intent(in): flag indicating first flux call in a splitting operation + in_soilLiqFlx % scalarSolution=(scalarSolution .and. .not.firstFluxCall) ! intent(in): flag to indicate the scalar solution + in_soilLiqFlx % deriv_desired =.true. ! intent(in): flag indicating if derivatives are desired + in_soilLiqFlx % mLayerTempTrial=mLayerTempTrial(nSnow+1:nLayers) ! intent(in): trial temperature at the current iteration (K) + in_soilLiqFlx % mLayerMatricHeadTrial =mLayerMatricHeadTrial(1:nSoil) ! intent(in): matric potential (m) + in_soilLiqFlx % mLayerMatricHeadLiqTrial=mLayerMatricHeadLiqTrial(1:nSoil) ! intent(in): liquid water matric potential (m) + in_soilLiqFlx % mLayerVolFracLiqTrial=mLayerVolFracLiqTrial(nSnow+1:nLayers) ! intent(in): volumetric fraction of liquid water (-) + in_soilLiqFlx % mLayerVolFracIceTrial=mLayerVolFracIceTrial(nSnow+1:nLayers) ! intent(in): volumetric fraction of ice (-) + in_soilLiqFlx % mLayerdTheta_dTk=mLayerdTheta_dTk(nSnow+1:nLayers) ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + in_soilLiqFlx % dPsiLiq_dTemp=dPsiLiq_dTemp(1:nSoil) ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) + in_soilLiqFlx % dCanopyTrans_dCanWat =dCanopyTrans_dCanWat ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + in_soilLiqFlx % dCanopyTrans_dTCanair =dCanopyTrans_dTCanair ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + in_soilLiqFlx % dCanopyTrans_dTCanopy =dCanopyTrans_dTCanopy ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + in_soilLiqFlx % dCanopyTrans_dTGround =dCanopyTrans_dTGround ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + in_soilLiqFlx % above_soilLiqFluxDeriv=above_soilLiqFluxDeriv ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + in_soilLiqFlx % above_soildLiq_dTk =above_soildLiq_dTk ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + in_soilLiqFlx % above_soilFracLiq =above_soilFracLiq ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) + in_soilLiqFlx % scalarCanopyTranspiration=scalarCanopyTranspiration ! intent(in): canopy transpiration (kg m-2 s-1) + in_soilLiqFlx % scalarGroundEvaporation =scalarGroundEvaporation ! intent(in): ground evaporation (kg m-2 s-1) + in_soilLiqFlx % scalarRainPlusMelt =scalarRainPlusMelt ! intent(in): rain plus melt (m s-1) else ! post-processing + ! intent(out) arguments + err =out_soilLiqFlx % err ! intent(out): error code + cmessage=out_soilLiqFlx % cmessage ! intent(out): error message + ! error control + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! calculate net liquid water fluxes for each soil layer (s-1) + do iLayer=1,nSoil + mLayerLiqFluxSoil(iLayer) = -(iLayerLiqFluxSoil(iLayer) - iLayerLiqFluxSoil(iLayer-1))/mLayerDepth(iLayer+nSnow) + end do + + ! calculate the soil control on infiltration + if (nSnow==0) then + ! * case of infiltration into soil + if (scalarMaxInfilRate > scalarRainPlusMelt) then ! infiltration is not rate-limited + scalarSoilControl = (1._rkind - scalarFrozenArea)*scalarInfilArea + else + scalarSoilControl = 0._rkind ! (scalarRainPlusMelt exceeds maximum infiltration rate + end if + else + ! * case of infiltration into snow + scalarSoilControl = 1._rkind + end if + + ! compute drainage from the soil zone (needed for mass balance checks and in aquifer recharge) + scalarSoilDrainage = iLayerLiqFluxSoil(nSoil) + ! expand derivatives to the total water matric potential + ! NOTE: arrays are offset because computing derivatives in interface fluxes, at the top and bottom of the layer respectively + if (globalPrintFlag) print*, 'dPsiLiq_dPsi0(1:nSoil) = ', dPsiLiq_dPsi0(1:nSoil) + dq_dHydStateAbove(1:nSoil) = dq_dHydStateAbove(1:nSoil) *dPsiLiq_dPsi0(1:nSoil) + dq_dHydStateBelow(0:nSoil-1) = dq_dHydStateBelow(0:nSoil-1)*dPsiLiq_dPsi0(1:nSoil) + dq_dHydStateLayerSurfVec(1:nSoil) = dq_dHydStateLayerSurfVec(1:nSoil)*dPsiLiq_dPsi0(1:nSoil) end if case(iLookROUTINE%groundwatr) ! groundwatr if (op==iLookOP%pre) then ! pre-processing diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index ad41c9c35..52d8cad5c 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -26,6 +26,9 @@ module soilLiqFlx_module USE data_types,only:var_d ! x%var(:) (rkind) USE data_types,only:var_ilength ! x%var(:)%dat (i4b) USE data_types,only:var_dlength ! x%var(:)%dat (rkind) +USE data_types,only:in_type_soilLiqFlx ! derived type for intent(in) arguments +USE data_types,only:io_type_soilLiqFlx ! derived type for intent(inout) arguments +USE data_types,only:out_type_soilLiqFlx ! derived type for intent(out) arguments ! missing values USE globalData,only:integerMissing ! missing integer @@ -87,31 +90,8 @@ module soilLiqFlx_module ! public subroutine soilLiqFlx: compute liquid water fluxes and their derivatives ! *************************************************************************************************************** subroutine soilLiqFlx(& - ! input: model control - nSoil, & ! intent(in): number of soil layers - firstSplitOper, & ! intent(in): flag to compute infiltration, if firstSplitOper - scalarSolution, & ! intent(in): flag to indicate the scalar solution - deriv_desired, & ! intent(in): flag indicating if derivatives are desired - ! input: trial state variables - mLayerTempTrial, & ! intent(in): temperature (K) - mLayerMatricHeadTrial, & ! intent(in): matric head (m) - mLayerMatricHeadLiqTrial, & ! intent(in): liquid matric head (m) - mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water (-) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice (-) - ! input: pre-computed derivatives - mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - dPsiLiq_dTemp, & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) - dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water - above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature - above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) - ! input: fluxes - scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) - scalarGroundEvaporation, & ! intent(in): ground evaporation (kg m-2 s-1) - scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) + ! input: model control, trial state variables, derivatives, and fluxes + in_soilLiqFlx, & ! intent(in): model control, trial state variables, derivatives, and fluxes ! input-output: data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model indices @@ -146,7 +126,7 @@ subroutine soilLiqFlx(& mLayerdTrans_dTGround, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. ground temperature mLayerdTrans_dCanWat, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy total water ! output: error control - err,message) ! intent(out): error control + out_soilLiqFlx) ! intent(out): error control ! utility modules USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water USE soil_utils_module,only:matricHead ! compute matric head (m) @@ -157,31 +137,8 @@ subroutine soilLiqFlx(& USE soil_utils_module,only:hydCondMP_liq ! compute hydraulic conductivity of macropores as a function of volumetric liquid water content ! ------------------------------------------------------------------------------------------------------------------------------------------------- implicit none - ! input: model control - integer(i4b),intent(in) :: nSoil ! number of soil layers - logical(lgt),intent(in) :: firstSplitOper ! flag to compute infiltration - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - logical(lgt),intent(in) :: deriv_desired ! flag indicating if derivatives are desired - ! input: trial model state variables - real(rkind),intent(in) :: mLayerTempTrial(:) ! temperature in each layer at the current iteration (m) - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! matric head in each layer at the current iteration (m) - real(rkind),intent(in) :: mLayerMatricHeadLiqTrial(:) ! liquid matric head in each layer at the current iteration (m) - real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! volumetric fraction of liquid water at the current iteration (-) - real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! volumetric fraction of ice at the current iteration (-) - ! input: pre-computed derivatves - real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative in volumetric liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: dPsiLiq_dTemp(:) ! derivative in liquid water matric potential w.r.t. temperature (m K-1) - real(rkind),intent(in) :: dCanopyTrans_dCanWat ! derivative in canopy transpiration w.r.t. canopy total water content (s-1) - real(rkind),intent(in) :: dCanopyTrans_dTCanair ! derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - real(rkind),intent(in) :: dCanopyTrans_dTCanopy ! derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - real(rkind),intent(in) :: dCanopyTrans_dTGround ! derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - real(rkind),intent(in) :: above_soilLiqFluxDeriv ! derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water - real(rkind),intent(in) :: above_soildLiq_dTk ! derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature - real(rkind),intent(in) :: above_soilFracLiq ! fraction of liquid water layer above soil (canopy or snow) (-) - ! input: model fluxes - real(rkind),intent(in) :: scalarCanopyTranspiration ! canopy transpiration (kg m-2 s-1) - real(rkind),intent(in) :: scalarGroundEvaporation ! ground evaporation (kg m-2 s-1) - real(rkind),intent(in) :: scalarRainPlusMelt ! rain plus melt (m s-1) + ! input: model control, trial state variables, derivatives, and fluxes + type(in_type_soilLiqFlx),intent(in) :: in_soilLiqFlx ! model control, trial state variables, derivatives, and fluxes ! input-output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! state vector geometry @@ -216,35 +173,34 @@ subroutine soilLiqFlx(& real(rkind),intent(inout) :: mLayerdTrans_dTGround(:) ! derivatives in the soil layer transpiration flux w.r.t. ground temperature real(rkind),intent(inout) :: mLayerdTrans_dCanWat(:) ! derivatives in the soil layer transpiration flux w.r.t. canopy total water ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + type(out_type_soilLiqFlx),intent(out) :: out_soilLiqFlx ! error code and error message ! ----------------------------------------------------------------------------------------------------------------------------------------------------- ! local variables: general character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: nSoil ! number of soil layers integer(i4b) :: ibeg,iend ! start and end indices of the soil layers in concatanated snow-soil vector integer(i4b) :: iLayer,iSoil ! index of soil layer integer(i4b) :: ixLayerDesired(1) ! layer desired (scalar solution) integer(i4b) :: ixTop ! top layer in subroutine call integer(i4b) :: ixBot ! bottom layer in subroutine call ! transpiration sink term - real(rkind),dimension(nSoil) :: mLayerTranspireFrac ! fraction of transpiration allocated to each soil layer (-) + real(rkind),dimension(in_soilLiqFlx % nSoil) :: mLayerTranspireFrac ! fraction of transpiration allocated to each soil layer (-) ! diagnostic variables - real(rkind),dimension(nSoil) :: iceImpedeFac ! ice impedence factor at layer mid-points (-) - real(rkind),dimension(nSoil) :: mLayerDiffuse ! diffusivity at layer mid-point (m2 s-1) - real(rkind),dimension(nSoil) :: dHydCond_dVolLiq ! derivative in hydraulic conductivity w.r.t volumetric liquid water content (m s-1) - real(rkind),dimension(nSoil) :: dDiffuse_dVolLiq ! derivative in hydraulic diffusivity w.r.t volumetric liquid water content (m2 s-1) - real(rkind),dimension(nSoil) :: dHydCond_dTemp ! derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) - real(rkind),dimension(0:nSoil) :: iLayerHydCond ! hydraulic conductivity at layer interface (m s-1) - real(rkind),dimension(0:nSoil) :: iLayerDiffuse ! diffusivity at layer interface (m2 s-1) + real(rkind),dimension(in_soilLiqFlx % nSoil) :: iceImpedeFac ! ice impedence factor at layer mid-points (-) + real(rkind),dimension(in_soilLiqFlx % nSoil) :: mLayerDiffuse ! diffusivity at layer mid-point (m2 s-1) + real(rkind),dimension(in_soilLiqFlx % nSoil) :: dHydCond_dVolLiq ! derivative in hydraulic conductivity w.r.t volumetric liquid water content (m s-1) + real(rkind),dimension(in_soilLiqFlx % nSoil) :: dDiffuse_dVolLiq ! derivative in hydraulic diffusivity w.r.t volumetric liquid water content (m2 s-1) + real(rkind),dimension(in_soilLiqFlx % nSoil) :: dHydCond_dTemp ! derivative in hydraulic conductivity w.r.t temperature (m s-1 K-1) + real(rkind),dimension(0:in_soilLiqFlx % nSoil) :: iLayerHydCond ! hydraulic conductivity at layer interface (m s-1) + real(rkind),dimension(0:in_soilLiqFlx % nSoil) :: iLayerDiffuse ! diffusivity at layer interface (m2 s-1) ! compute surface flux - integer(i4b) :: nRoots ! number of soil layers with roots - integer(i4b) :: ixIce ! index of the lowest soil layer that contains ice - real(rkind),dimension(0:nSoil) :: iLayerHeight ! height of the layer interfaces (m) + integer(i4b) :: nRoots ! number of soil layers with roots + integer(i4b) :: ixIce ! index of the lowest soil layer that contains ice + real(rkind),dimension(0:in_soilLiqFlx % nSoil) :: iLayerHeight ! height of the layer interfaces (m) ! compute fluxes and derivatives at layer interfaces - real(rkind) :: scalardPsi_dTheta ! derivative in soil water characteristix, used for perturbations when computing numerical derivatives + real(rkind) :: scalardPsi_dTheta ! derivative in soil water characteristix, used for perturbations when computing numerical derivatives ! ------------------------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='soilLiqFlx/' + nSoil = in_soilLiqFlx % nSoil ! get number of soil layers from input arguments ! get indices for the data structures ibeg = indx_data%var(iLookINDEX%nSnow)%dat(1) + 1 @@ -256,6 +212,31 @@ subroutine soilLiqFlx(& ! make association between local variables and the information in the data structures associate(& + ! input: model control + !nSoil => in_soilLiqFlx % nSoil, & ! intent(in): number of soil layers + firstSplitOper => in_soilLiqFlx % firstSplitOper, & ! intent(in): flag to compute infiltration + scalarSolution => in_soilLiqFlx % scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution + deriv_desired => in_soilLiqFlx % deriv_desired, & ! intent(in): flag indicating if derivatives are desired + ! input: trial model state variables + mLayerTempTrial => in_soilLiqFlx % mLayerTempTrial, & ! intent(in): temperature in each layer at the current iteration (m) + mLayerMatricHeadTrial => in_soilLiqFlx % mLayerMatricHeadTrial, & ! intent(in): matric head in each layer at the current iteration (m) + mLayerMatricHeadLiqTrial => in_soilLiqFlx % mLayerMatricHeadLiqTrial, & ! intent(in): liquid matric head in each layer at the current iteration (m) + mLayerVolFracLiqTrial => in_soilLiqFlx % mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the current iteration (-) + mLayerVolFracIceTrial => in_soilLiqFlx % mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the current iteration (-) + ! input: pre-computed derivatves + mLayerdTheta_dTk => in_soilLiqFlx % mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + dPsiLiq_dTemp => in_soilLiqFlx % dPsiLiq_dTemp, & ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) + dCanopyTrans_dCanWat => in_soilLiqFlx % dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair => in_soilLiqFlx % dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy => in_soilLiqFlx % dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround => in_soilLiqFlx % dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + above_soilLiqFluxDeriv => in_soilLiqFlx % above_soilLiqFluxDeriv, & ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + above_soildLiq_dTk => in_soilLiqFlx % above_soildLiq_dTk, & ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + above_soilFracLiq => in_soilLiqFlx % above_soilFracLiq, & ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) + ! input: model fluxes + scalarCanopyTranspiration => in_soilLiqFlx % scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) + scalarGroundEvaporation => in_soilLiqFlx % scalarGroundEvaporation, & ! intent(in): ground evaporation (kg m-2 s-1) + scalarRainPlusMelt => in_soilLiqFlx % scalarRainPlusMelt, & ! intent(in): rain plus melt (m s-1) ! input: model control ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision, & ! intent(in): index of the form of Richards' equation ixBcUpperSoilHydrology => model_decisions(iLookDECISIONS%bcUpprSoiH)%iDecision, & ! intent(in): index of the upper boundary conditions for soil hydrology @@ -296,8 +277,12 @@ subroutine soilLiqFlx(& ! input: factors limiting transpiration (from vegFlux routine) mLayerRootDensity => diag_data%var(iLookDIAG%mLayerRootDensity)%dat, & ! intent(in): root density in each layer (-) scalarTranspireLim => diag_data%var(iLookDIAG%scalarTranspireLim)%dat(1), & ! intent(in): weighted average of the transpiration limiting factor (-) - mLayerTranspireLim => diag_data%var(iLookDIAG%mLayerTranspireLim)%dat & ! intent(in): transpiration limiting factor in each layer (-) + mLayerTranspireLim => diag_data%var(iLookDIAG%mLayerTranspireLim)%dat, & ! intent(in): transpiration limiting factor in each layer (-) + ! output: error control + err => out_soilLiqFlx % err, & ! intent(out): error code + message => out_soilLiqFlx % cmessage & ! intent(out): error message ) ! end associating local variables with the information in the data structures + err=0; message='soilLiqFlx/' ! initialize error control ! ------------------------------------------------------------------------------------------------------------------------------------------------- ! preliminaries From 3ac8574f8fcb4a47b0528e2d7aa35eea7c658271 Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 8 Sep 2023 06:10:14 -0600 Subject: [PATCH 0917/1472] Applied derived type for intent(inout) soilLiqFlx arguments in computFlux.f90 and soilLiqFlx.f90. --- build/source/engine/computFlux.f90 | 89 ++++++++++++------------ build/source/engine/soilLiqFlx.f90 | 104 +++++++++++------------------ 2 files changed, 88 insertions(+), 105 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index ae17f900a..b9d1be875 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -494,47 +494,10 @@ subroutine computFlux(& ! check the need to calculate the liquid flux through soil if (nSoilOnlyHyd>0) then - ! calculate the liquid flux through soil call subTools(iLookOP%pre,iLookROUTINE%soilLiqFlx) ! pre-processing for call to soilLiqFlx - call soilLiqFlx(& - in_soilLiqFlx, & - ! input-output: data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model indices - prog_data, & ! intent(inout): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output: diagnostic variables for surface runoff - scalarMaxInfilRate, & ! intent(inout): maximum infiltration rate (m s-1) - scalarInfilArea, & ! intent(inout): fraction of unfrozen area where water can infiltrate (-) - scalarFrozenArea, & ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) - scalarSurfaceRunoff, & ! intent(inout): surface runoff (m s-1) - ! output: diagnostic variables for model layers - mLayerdTheta_dPsi, & ! intent(inout): derivative in the soil water characteristic w.r.t. psi (m-1) - mLayerdPsi_dTheta, & ! intent(inout): derivative in the soil water characteristic w.r.t. theta (m) - dHydCond_dMatric, & ! intent(inout): derivative in hydraulic conductivity w.r.t matric head (s-1) - ! output: fluxes - scalarInfiltration, & ! intent(inout): surface infiltration rate (m s-1) -- controls on infiltration only computed for iter==1 - iLayerLiqFluxSoil, & ! intent(inout): liquid fluxes at layer interfaces (m s-1) - mLayerTranspire, & ! intent(inout): transpiration loss from each soil layer (m s-1) - mLayerHydCond, & ! intent(inout): hydraulic conductivity in each layer (m s-1) - ! output: derivatives in fluxes w.r.t. state variables -- matric head or volumetric lquid water -- in the layer above and layer below (m s-1 or s-1) - dq_dHydStateAbove, & ! intent(inout): derivatives in the flux w.r.t. matric head in the layer above (s-1) - dq_dHydStateBelow, & ! intent(inout): derivatives in the flux w.r.t. matric head in the layer below (s-1) - dq_dHydStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) - ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) - dq_dNrgStateAbove, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) - dq_dNrgStateBelow, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) - dq_dNrgStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) - ! output: derivatives in transpiration w.r.t. canopy state variables - mLayerdTrans_dTCanair, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature - mLayerdTrans_dTCanopy, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy temperature - mLayerdTrans_dTGround, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. ground temperature - mLayerdTrans_dCanWat, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy total water - ! output: error control - out_soilLiqFlx) - call subTools(iLookOP%post,iLookROUTINE%soilLiqFlx) ! post-processing for call to soilLiqFlx + call soilLiqFlx(in_soilLiqFlx,mpar_data,indx_data,prog_data,diag_data,flux_data,io_soilLiqFlx,out_soilLiqFlx) + call subTools(iLookOP%post,iLookROUTINE%soilLiqFlx) ! post-processing for call to soilLiqFlx end if ! end if calculating the liquid flux through soil ! ***** @@ -1041,10 +1004,54 @@ subroutine subTools(op,sub) in_soilLiqFlx % scalarCanopyTranspiration=scalarCanopyTranspiration ! intent(in): canopy transpiration (kg m-2 s-1) in_soilLiqFlx % scalarGroundEvaporation =scalarGroundEvaporation ! intent(in): ground evaporation (kg m-2 s-1) in_soilLiqFlx % scalarRainPlusMelt =scalarRainPlusMelt ! intent(in): rain plus melt (m s-1) + ! intent(inout) arguments + io_soilLiqFlx % scalarMaxInfilRate =scalarMaxInfilRate ! intent(inout): maximum infiltration rate (m s-1) + io_soilLiqFlx % scalarInfilArea =scalarInfilArea ! intent(inout): fraction of unfrozen area where water can infiltrate (-) + io_soilLiqFlx % scalarFrozenArea =scalarFrozenArea ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) + io_soilLiqFlx % scalarSurfaceRunoff =scalarSurfaceRunoff ! intent(inout): surface runoff (m s-1) + io_soilLiqFlx % mLayerdTheta_dPsi =mLayerdTheta_dPsi ! intent(inout): derivative in the soil water characteristic w.r.t. psi (m-1) + io_soilLiqFlx % mLayerdPsi_dTheta =mLayerdPsi_dTheta ! intent(inout): derivative in the soil water characteristic w.r.t. theta (m) + io_soilLiqFlx % dHydCond_dMatric =dHydCond_dMatric ! intent(inout): derivative in hydraulic conductivity w.r.t matric head (s-1) + io_soilLiqFlx % scalarInfiltration =scalarInfiltration ! intent(inout): surface infiltration rate (m s-1) -- controls on infiltration only computed for iter==1 + io_soilLiqFlx % iLayerLiqFluxSoil =iLayerLiqFluxSoil ! intent(inout): liquid fluxes at layer interfaces (m s-1) + io_soilLiqFlx % mLayerTranspire =mLayerTranspire ! intent(inout): transpiration loss from each soil layer (m s-1) + io_soilLiqFlx % mLayerHydCond =mLayerHydCond ! intent(inout): hydraulic conductivity in each layer (m s-1) + io_soilLiqFlx % dq_dHydStateAbove =dq_dHydStateAbove ! intent(inout): derivatives in the flux w.r.t. matric head in the layer above (s-1) + io_soilLiqFlx % dq_dHydStateBelow =dq_dHydStateBelow ! intent(inout): derivatives in the flux w.r.t. matric head in the layer below (s-1) + io_soilLiqFlx % dq_dHydStateLayerSurfVec=dq_dHydStateLayerSurfVec ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) + io_soilLiqFlx % dq_dNrgStateAbove =dq_dNrgStateAbove ! intent(inout): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) + io_soilLiqFlx % dq_dNrgStateBelow =dq_dNrgStateBelow ! intent(inout): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) + io_soilLiqFlx % dq_dNrgStateLayerSurfVec=dq_dNrgStateLayerSurfVec ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) + io_soilLiqFlx % mLayerdTrans_dTCanair =mLayerdTrans_dTCanair ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + io_soilLiqFlx % mLayerdTrans_dTCanopy =mLayerdTrans_dTCanopy ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy temperature + io_soilLiqFlx % mLayerdTrans_dTGround =mLayerdTrans_dTGround ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. ground temperature + io_soilLiqFlx % mLayerdTrans_dCanWat =mLayerdTrans_dCanWat ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy total water else ! post-processing + ! intent(inout) arguments + scalarMaxInfilRate =io_soilLiqFlx % scalarMaxInfilRate ! intent(inout): maximum infiltration rate (m s-1) + scalarInfilArea =io_soilLiqFlx % scalarInfilArea ! intent(inout): fraction of unfrozen area where water can infiltrate (-) + scalarFrozenArea =io_soilLiqFlx % scalarFrozenArea ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) + scalarSurfaceRunoff =io_soilLiqFlx % scalarSurfaceRunoff ! intent(inout): surface runoff (m s-1) + mLayerdTheta_dPsi =io_soilLiqFlx % mLayerdTheta_dPsi ! intent(inout): derivative in the soil water characteristic w.r.t. psi (m-1) + mLayerdPsi_dTheta =io_soilLiqFlx % mLayerdPsi_dTheta ! intent(inout): derivative in the soil water characteristic w.r.t. theta (m) + dHydCond_dMatric =io_soilLiqFlx % dHydCond_dMatric ! intent(inout): derivative in hydraulic conductivity w.r.t matric head (s-1) + scalarInfiltration =io_soilLiqFlx % scalarInfiltration ! intent(inout): surface infiltration rate (m s-1) -- controls on infiltration only computed for iter==1 + iLayerLiqFluxSoil =io_soilLiqFlx % iLayerLiqFluxSoil ! intent(inout): liquid fluxes at layer interfaces (m s-1) + mLayerTranspire =io_soilLiqFlx % mLayerTranspire ! intent(inout): transpiration loss from each soil layer (m s-1) + mLayerHydCond =io_soilLiqFlx % mLayerHydCond ! intent(inout): hydraulic conductivity in each layer (m s-1) + dq_dHydStateAbove =io_soilLiqFlx % dq_dHydStateAbove ! intent(inout): derivatives in the flux w.r.t. matric head in the layer above (s-1) + dq_dHydStateBelow =io_soilLiqFlx % dq_dHydStateBelow ! intent(inout): derivatives in the flux w.r.t. matric head in the layer below (s-1) + dq_dHydStateLayerSurfVec=io_soilLiqFlx % dq_dHydStateLayerSurfVec ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) + dq_dNrgStateAbove =io_soilLiqFlx % dq_dNrgStateAbove ! intent(inout): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) + dq_dNrgStateBelow =io_soilLiqFlx % dq_dNrgStateBelow ! intent(inout): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) + dq_dNrgStateLayerSurfVec=io_soilLiqFlx % dq_dNrgStateLayerSurfVec ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) + mLayerdTrans_dTCanair =io_soilLiqFlx % mLayerdTrans_dTCanair ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + mLayerdTrans_dTCanopy =io_soilLiqFlx % mLayerdTrans_dTCanopy ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy temperature + mLayerdTrans_dTGround =io_soilLiqFlx % mLayerdTrans_dTGround ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. ground temperature + mLayerdTrans_dCanWat =io_soilLiqFlx % mLayerdTrans_dCanWat ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy total water ! intent(out) arguments - err =out_soilLiqFlx % err ! intent(out): error code - cmessage=out_soilLiqFlx % cmessage ! intent(out): error message + err =out_soilLiqFlx % err ! intent(out): error code + cmessage =out_soilLiqFlx % cmessage ! intent(out): error message ! error control if (err/=0) then; message=trim(message)//trim(cmessage); return; end if diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index 52d8cad5c..bf0f97687 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -98,33 +98,8 @@ subroutine soilLiqFlx(& prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU - ! output: diagnostic variables for surface runoff - xMaxInfilRate, & ! intent(inout): maximum infiltration rate (m s-1) - scalarInfilArea, & ! intent(inout): fraction of unfrozen area where water can infiltrate (-) - scalarFrozenArea, & ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) - scalarSurfaceRunoff, & ! intent(inout): surface runoff (m s-1) - ! output: diagnostic variables for model layers - mLayerdTheta_dPsi, & ! intent(inout): derivative in the soil water characteristic w.r.t. psi (m-1) - mLayerdPsi_dTheta, & ! intent(inout): derivative in the soil water characteristic w.r.t. theta (m) - dHydCond_dMatric, & ! intent(inout): derivative in hydraulic conductivity w.r.t matric head (s-1) - ! output: fluxes - scalarSurfaceInfiltration, & ! intent(inout): surface infiltration rate (m s-1) - iLayerLiqFluxSoil, & ! intent(inout): liquid fluxes at layer interfaces (m s-1) - mLayerTranspire, & ! intent(inout): transpiration loss from each soil layer (m s-1) - mLayerHydCond, & ! intent(inout): hydraulic conductivity in each soil layer (m s-1) - ! output: derivatives in fluxes w.r.t. hydrology state variables -- matric head or volumetric lquid water -- in the layer above and layer below (m s-1 or s-1) - dq_dHydStateAbove, & ! intent(inout): derivatives in the flux w.r.t. volumetric liquid water content in the layer above (m s-1) - dq_dHydStateBelow, & ! intent(inout): derivatives in the flux w.r.t. volumetric liquid water content in the layer below (m s-1) - dq_dHydStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) - ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) - dq_dNrgStateAbove, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) - dq_dNrgStateBelow, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) - dq_dNrgStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) - ! output: derivatives in transpiration w.r.t. canopy state variables - mLayerdTrans_dTCanair, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature - mLayerdTrans_dTCanopy, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy temperature - mLayerdTrans_dTGround, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. ground temperature - mLayerdTrans_dCanWat, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy total water + ! input-output: diagnostic variables, fluxes, and derivatives + io_soilLiqFlx, & ! intent(inout): diagnostic variables, fluxes, and derivatives ! output: error control out_soilLiqFlx) ! intent(out): error control ! utility modules @@ -138,42 +113,17 @@ subroutine soilLiqFlx(& ! ------------------------------------------------------------------------------------------------------------------------------------------------- implicit none ! input: model control, trial state variables, derivatives, and fluxes - type(in_type_soilLiqFlx),intent(in) :: in_soilLiqFlx ! model control, trial state variables, derivatives, and fluxes + type(in_type_soilLiqFlx),intent(in) :: in_soilLiqFlx ! model control, trial state variables, derivatives, and fluxes ! input-output: data structures - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! state vector geometry - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - ! output: diagnostic variables for surface runoff - real(rkind),intent(inout) :: xMaxInfilRate ! maximum infiltration rate (m s-1) - real(rkind),intent(inout) :: scalarInfilArea ! fraction of unfrozen area where water can infiltrate (-) - real(rkind),intent(inout) :: scalarFrozenArea ! fraction of area that is considered impermeable due to soil ice (-) - real(rkind),intent(inout) :: scalarSurfaceRunoff ! surface runoff (m s-1) - ! output: diagnostic variables for each layer - real(rkind),intent(inout) :: mLayerdTheta_dPsi(:) ! derivative in the soil water characteristic w.r.t. psi (m-1) - real(rkind),intent(inout) :: mLayerdPsi_dTheta(:) ! derivative in the soil water characteristic w.r.t. theta (m) - real(rkind),intent(inout) :: dHydCond_dMatric(:) ! derivative in hydraulic conductivity w.r.t matric head (s-1) - ! output: liquid fluxes - real(rkind),intent(inout) :: scalarSurfaceInfiltration ! surface infiltration rate (m s-1) - real(rkind),intent(inout) :: iLayerLiqFluxSoil(0:) ! liquid flux at soil layer interfaces (m s-1) - real(rkind),intent(inout) :: mLayerTranspire(:) ! transpiration loss from each soil layer (m s-1) - real(rkind),intent(inout) :: mLayerHydCond(:) ! hydraulic conductivity in each soil layer (m s-1) - ! output: derivatives in fluxes w.r.t. state variables in the layer above and layer below (m s-1) - real(rkind),intent(inout) :: dq_dHydStateAbove(0:) ! derivative in the flux in layer interfaces w.r.t. state variables in the layer above - real(rkind),intent(inout) :: dq_dHydStateBelow(0:) ! derivative in the flux in layer interfaces w.r.t. state variables in the layer below - real(rkind),intent(inout) :: dq_dHydStateLayerSurfVec(0:) ! derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) - ! output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) - real(rkind),intent(inout) :: dq_dNrgStateAbove(0:) ! derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) - real(rkind),intent(inout) :: dq_dNrgStateBelow(0:) ! derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) - real(rkind),intent(inout) :: dq_dNrgStateLayerSurfVec(0:) ! derivative in surface infiltration w.r.t. temperature in above soil snow or canopy and every soil layer (m s-1 or s-1) - ! output: derivatives in transpiration w.r.t. canopy state variables - real(rkind),intent(inout) :: mLayerdTrans_dTCanair(:) ! derivatives in the soil layer transpiration flux w.r.t. canopy air temperature - real(rkind),intent(inout) :: mLayerdTrans_dTCanopy(:) ! derivatives in the soil layer transpiration flux w.r.t. canopy temperature - real(rkind),intent(inout) :: mLayerdTrans_dTGround(:) ! derivatives in the soil layer transpiration flux w.r.t. ground temperature - real(rkind),intent(inout) :: mLayerdTrans_dCanWat(:) ! derivatives in the soil layer transpiration flux w.r.t. canopy total water + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! state vector geometry + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + ! input-output: diagnostic variables, fluxes, and derivatives + type(io_type_soilLiqFlx),intent(inout) :: io_soilLiqFlx ! diagnostic variables, fluxes, and derivatives ! output: error control - type(out_type_soilLiqFlx),intent(out) :: out_soilLiqFlx ! error code and error message + type(out_type_soilLiqFlx),intent(out) :: out_soilLiqFlx ! error code and error message ! ----------------------------------------------------------------------------------------------------------------------------------------------------- ! local variables: general character(LEN=256) :: cmessage ! error message of downwind routine @@ -213,7 +163,6 @@ subroutine soilLiqFlx(& ! make association between local variables and the information in the data structures associate(& ! input: model control - !nSoil => in_soilLiqFlx % nSoil, & ! intent(in): number of soil layers firstSplitOper => in_soilLiqFlx % firstSplitOper, & ! intent(in): flag to compute infiltration scalarSolution => in_soilLiqFlx % scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution deriv_desired => in_soilLiqFlx % deriv_desired, & ! intent(in): flag indicating if derivatives are desired @@ -278,9 +227,36 @@ subroutine soilLiqFlx(& mLayerRootDensity => diag_data%var(iLookDIAG%mLayerRootDensity)%dat, & ! intent(in): root density in each layer (-) scalarTranspireLim => diag_data%var(iLookDIAG%scalarTranspireLim)%dat(1), & ! intent(in): weighted average of the transpiration limiting factor (-) mLayerTranspireLim => diag_data%var(iLookDIAG%mLayerTranspireLim)%dat, & ! intent(in): transpiration limiting factor in each layer (-) + ! input-output: diagnostic variables for surface runoff + xMaxInfilRate => io_soilLiqFlx % scalarMaxInfilRate, & ! intent(inout): maximum infiltration rate (m s-1) + scalarInfilArea => io_soilLiqFlx % scalarInfilArea, & ! intent(inout): fraction of unfrozen area where water can infiltrate (-) + scalarFrozenArea => io_soilLiqFlx % scalarFrozenArea, & ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) + scalarSurfaceRunoff => io_soilLiqFlx % scalarSurfaceRunoff, & ! intent(inout): surface runoff (m s-1) + ! input-output: diagnostic variables for each layer + mLayerdTheta_dPsi => io_soilLiqFlx % mLayerdTheta_dPsi, & ! intent(inout): derivative in the soil water characteristic w.r.t. psi (m-1) + mLayerdPsi_dTheta => io_soilLiqFlx % mLayerdPsi_dTheta, & ! intent(inout): derivative in the soil water characteristic w.r.t. theta (m) + dHydCond_dMatric => io_soilLiqFlx % dHydCond_dMatric, & ! intent(inout): derivative in hydraulic conductivity w.r.t matric head (s-1) + ! input-output: liquid fluxes + scalarSurfaceInfiltration => io_soilLiqFlx % scalarInfiltration, & ! intent(inout): surface infiltration rate (m s-1) + iLayerLiqFluxSoil => io_soilLiqFlx % iLayerLiqFluxSoil, & ! intent(inout): liquid flux at soil layer interfaces (m s-1) + mLayerTranspire => io_soilLiqFlx % mLayerTranspire, & ! intent(inout): transpiration loss from each soil layer (m s-1) + mLayerHydCond => io_soilLiqFlx % mLayerHydCond, & ! intent(inout): hydraulic conductivity in each soil layer (m s-1) + ! input-output: derivatives in fluxes w.r.t. state variables in the layer above and layer below (m s-1) + dq_dHydStateAbove => io_soilLiqFlx % dq_dHydStateAbove, & ! intent(inout): derivative in the flux in layer interfaces w.r.t. state variables in the layer above + dq_dHydStateBelow => io_soilLiqFlx % dq_dHydStateBelow, & ! intent(inout): derivative in the flux in layer interfaces w.r.t. state variables in the layer below + dq_dHydStateLayerSurfVec => io_soilLiqFlx % dq_dHydStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) + ! input-output: derivatives in fluxes w.r.t. energy state variables -- now just temperature -- in the layer above and layer below (m s-1 K-1) + dq_dNrgStateAbove => io_soilLiqFlx % dq_dNrgStateAbove, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) + dq_dNrgStateBelow => io_soilLiqFlx % dq_dNrgStateBelow, & ! intent(inout): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) + dq_dNrgStateLayerSurfVec => io_soilLiqFlx % dq_dNrgStateLayerSurfVec, & ! intent(inout): derivative in surface infiltration w.r.t. temperature in above soil snow or canopy and every soil layer (m s-1 or s-1) + ! input-output: derivatives in transpiration w.r.t. canopy state variables + mLayerdTrans_dTCanair => io_soilLiqFlx % mLayerdTrans_dTCanair, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + mLayerdTrans_dTCanopy => io_soilLiqFlx % mLayerdTrans_dTCanopy, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy temperature + mLayerdTrans_dTGround => io_soilLiqFlx % mLayerdTrans_dTGround, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. ground temperature + mLayerdTrans_dCanWat => io_soilLiqFlx % mLayerdTrans_dCanWat, & ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy total water ! output: error control - err => out_soilLiqFlx % err, & ! intent(out): error code - message => out_soilLiqFlx % cmessage & ! intent(out): error message + err => out_soilLiqFlx % err, & ! intent(out): error code + message => out_soilLiqFlx % cmessage & ! intent(out): error message ) ! end associating local variables with the information in the data structures err=0; message='soilLiqFlx/' ! initialize error control From ee8922496e09396e3712d8cd2af7ebdfd8869892 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 9 Sep 2023 05:12:41 -0600 Subject: [PATCH 0918/1472] Created and applied derived types for groundwatr subroutine arguments. --- build/source/dshare/data_types.f90 | 28 ++++++++- build/source/engine/computFlux.f90 | 75 +++++++++++------------ build/source/engine/groundwatr.f90 | 95 +++++++++++++++--------------- 3 files changed, 110 insertions(+), 88 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 66d058b14..a2672c317 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -422,13 +422,11 @@ MODULE data_types logical(lgt) :: firstSplitOper ! intent(in): flag indicating first flux call in a splitting operation logical(lgt) :: scalarSolution ! intent(in): flag to indicate the scalar solution logical(lgt) :: deriv_desired ! intent(in): flag indicating if derivatives are desired - !! input: trial state variables real(rkind), allocatable :: mLayerTempTrial(:) ! intent(in): trial temperature at the current iteration (K) real(rkind), allocatable :: mLayerMatricHeadTrial(:) ! intent(in): matric potential (m) real(rkind), allocatable :: mLayerMatricHeadLiqTrial(:) ! intent(in): liquid water matric potential (m) real(rkind), allocatable :: mLayerVolFracLiqTrial(:) ! intent(in): volumetric fraction of liquid water (-) real(rkind), allocatable :: mLayerVolFracIceTrial(:) ! intent(in): volumetric fraction of ice (-) - !! input: pre-computed deriavatives real(rkind), allocatable :: mLayerdTheta_dTk(:) ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) real(rkind), allocatable :: dPsiLiq_dTemp(:) ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) real(rkind) :: dCanopyTrans_dCanWat ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) @@ -438,7 +436,6 @@ MODULE data_types real(rkind) :: above_soilLiqFluxDeriv ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water real(rkind) :: above_soildLiq_dTk ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature real(rkind) :: above_soilFracLiq ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) - !! input: fluxes real(rkind) :: scalarCanopyTranspiration ! intent(in): canopy transpiration (kg m-2 s-1) real(rkind) :: scalarGroundEvaporation ! intent(in): ground evaporation (kg m-2 s-1) real(rkind) :: scalarRainPlusMelt ! intent(in): rain plus melt (m s-1) @@ -472,4 +469,29 @@ MODULE data_types integer(i4b) :: err ! intent(out): error code character(:),allocatable :: cmessage ! intent(out): error message end type out_type_soilLiqFlx + ! ** end soilLiqFlx + + ! ** groundwatr + type, public :: in_type_groundwatr ! derived type for intent(in) arguments in groundwatr call + integer(i4b) :: nSnow ! intent(in): number of snow layers + integer(i4b) :: nSoil ! intent(in): number of soil layers + integer(i4b) :: nLayers ! intent(in): total number of layers + logical(lgt) :: firstFluxCall ! intent(in): logical flag to compute index of the lowest saturated layer + real(rkind), allocatable :: mLayerdTheta_dPsi(:) ! intent(in): derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) + real(rkind), allocatable :: mLayerMatricHeadLiqTrial(:) ! intent(in): liquid water matric potential (m) + real(rkind), allocatable :: mLayerVolFracLiqTrial(:) ! intent(in): volumetric fraction of liquid water (-) + real(rkind), allocatable :: mLayerVolFracIceTrial(:) ! intent(in): volumetric fraction of ice (-) + end type in_type_groundwatr + + type, public :: io_type_groundwatr ! derived type for intent(io) arguments in groundwatr call + integer(i4b) :: ixSaturation ! intent(inout): index of lowest saturated layer (NOTE: only computed on the first iteration) + end type io_type_groundwatr + + type, public :: out_type_groundwatr ! derived type for intent(out) arguments in groundwatr call + real(rkind), allocatable :: mLayerBaseflow(:) ! intent(out): baseflow from each soil layer (m s-1) + real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + integer(i4b) :: err ! intent(out): error code + character(:),allocatable :: cmessage ! intent(out): error message + end type out_type_groundwatr + ! ** end groundwatr END MODULE data_types diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index b9d1be875..c959ab7a5 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -42,7 +42,11 @@ module computFlux_module out_type_snowLiqFlx,& ! intent(out) arguments for snowLiqFlx call in_type_soilLiqFlx, & ! intent(in) arguments for soilLiqFlx call io_type_soilLiqFlx, & ! intent(inout) arguments for soilLiqFlx call - out_type_soilLiqFlx ! intent(in) arguments for soilLiqFlx call + out_type_soilLiqFlx,& ! intent(out) arguments for soilLiqFlx call + in_type_groundwatr, & ! intent(in) arguments for groundwatr call + io_type_groundwatr, & ! intent(inout) arguments for groundwatr call + out_type_groundwatr ! intent(out) arguments for groundwatr call + ! indices that define elements of the data structures USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure @@ -239,6 +243,9 @@ subroutine computFlux(& type(in_type_soilLiqFlx) :: in_soilLiqFlx ! data structure for intent(in) soilLiqFlx arguments type(io_type_soilLiqFlx) :: io_soilLiqFlx ! data structure for intent(inout) soilLiqFlx arguments type(out_type_soilLiqFlx) :: out_soilLiqFlx ! data structure for intent(out) soilLiqFlx arguments + type(in_type_groundwatr) :: in_groundwatr ! data structure for intent(in) groundwatr arguments + type(io_type_groundwatr) :: io_groundwatr ! data structure for intent(inout) groundwatr arguments + type(out_type_groundwatr) :: out_groundwatr ! data structure for intent(out) groundwatr arguments ! -------------------------------------------------------------- ! initialize error control err=0; message='computFlux/' @@ -506,48 +513,20 @@ subroutine computFlux(& ! check if computing soil hydrology if (nSoilOnlyHyd>0) then - ! set baseflow fluxes to zero if the topmodel baseflow routine is not used if (local_ixGroundwater/=qbaseTopmodel) then - ! (diagnostic variables in the data structures) + ! diagnostic variables in the data structures scalarExfiltration = 0._rkind ! exfiltration from the soil profile (m s-1) mLayerColumnOutflow(:) = 0._rkind ! column outflow from each soil layer (m3 s-1) - ! (variables needed for the numerical solution) + ! variables needed for the numerical solution mLayerBaseflow(:) = 0._rkind ! baseflow from each soil layer (m s-1) ! topmodel-ish shallow groundwater else ! local_ixGroundwater==qbaseTopmodel - - ! check the derivative matrix is sized appropriately - if (size(dBaseflow_dMatric,1)/=nSoil .or. size(dBaseflow_dMatric,2)/=nSoil) then - message=trim(message)//'expect dBaseflow_dMatric to be nSoil x nSoil' - err=20; return - end if - ! compute the baseflow flux - call groundwatr(& - ! input: model control - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - firstFluxCall, & ! intent(in): logical flag to compute index of the lowest saturated layer - ! input: state and diagnostic variables - mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) - mLayerMatricHeadLiqTrial, & ! intent(in): liquid water matric potential (m) - mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): volumetric fraction of ice (-) - ! input: data structures - attr_data, & ! intent(in): model attributes - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output - ixSaturation, & ! intent(inout): index of lowest saturated layer (NOTE: only computed on the first iteration) - mLayerBaseflow, & ! intent(out): baseflow from each soil layer (m s-1) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - err,cmessage) ! intent(out): error control - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + call subTools(iLookOP%pre,iLookROUTINE%groundwatr) ! pre-processing for call to groundwatr + call groundwatr(in_groundwatr,attr_data,mpar_data,prog_data,diag_data,flux_data,io_groundwatr,out_groundwatr) + call subTools(iLookOP%post,iLookROUTINE%groundwatr) ! post-processing for call to groundwatr end if ! computing baseflow flux ! compute total baseflow from the soil zone (needed for mass balance checks) @@ -557,7 +536,6 @@ subroutine computFlux(& ! (Note: scalarSoilBaseflow is zero if topmodel is not used) ! (Note: scalarSoilBaseflow may need to re-envisioned in topmodel formulation if parts of it flow into neighboring soil rather than exfiltrate) scalarTotalRunoff = scalarSurfaceRunoff + scalarSoilDrainage + scalarSoilBaseflow - end if ! end if computing soil hydrology @@ -1085,9 +1063,32 @@ subroutine subTools(op,sub) end if case(iLookROUTINE%groundwatr) ! groundwatr if (op==iLookOP%pre) then ! pre-processing - + ! check the derivative matrix is sized appropriately + if (size(dBaseflow_dMatric,1)/=nSoil .or. size(dBaseflow_dMatric,2)/=nSoil) then + message=trim(message)//'expect dBaseflow_dMatric to be nSoil x nSoil' + err=20; return + end if + ! intent(in) arguments + in_groundwatr % nSnow = nSnow ! intent(in): number of snow layers + in_groundwatr % nSoil = nSoil ! intent(in): number of soil layers + in_groundwatr % nLayers = nLayers ! intent(in): total number of layers + in_groundwatr % firstFluxCall = firstFluxCall ! intent(in): logical flag to compute index of the lowest saturated layer + in_groundwatr % mLayerdTheta_dPsi = mLayerdTheta_dPsi ! intent(in): derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) + in_groundwatr % mLayerMatricHeadLiqTrial = mLayerMatricHeadLiqTrial ! intent(in): liquid water matric potential (m) + in_groundwatr % mLayerVolFracLiqTrial = mLayerVolFracLiqTrial(nSnow+1:nLayers) ! intent(in): volumetric fraction of liquid water (-) + in_groundwatr % mLayerVolFracIceTrial = mLayerVolFracIceTrial(nSnow+1:nLayers) ! intent(in): volumetric fraction of ice (-) + ! intent(inout) arguments + io_groundwatr % ixSaturation = ixSaturation ! intent(inout): index of lowest saturated layer (NOTE: only computed on the first iteration) else ! post-processing - + ! intent(inout) arguments + ixSaturation = io_groundwatr % ixSaturation ! intent(inout): index of lowest saturated layer (NOTE: only computed on the first iteration) + ! intent(out) arguments + mLayerBaseflow = out_groundwatr % mLayerBaseflow ! intent(out): baseflow from each soil layer (m s-1) + dBaseflow_dMatric = out_groundwatr % dBaseflow_dMatric ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + err = out_groundwatr % err ! intent(out): error code + cmessage = out_groundwatr % cmessage ! intent(out): error message + ! error control + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if end if case(iLookROUTINE%bigAquifer) ! bigAquifer if (op==iLookOP%pre) then ! pre-processing diff --git a/build/source/engine/groundwatr.f90 b/build/source/engine/groundwatr.f90 index 9e060fc25..a8bb3d6ce 100644 --- a/build/source/engine/groundwatr.f90 +++ b/build/source/engine/groundwatr.f90 @@ -28,8 +28,11 @@ module groundwatr_module ! derived types to define the data structures USE data_types,only:& - var_d, & ! data vector (rkind) - var_dlength ! data vector with variable length dimension (rkind) + var_d, & ! data vector (rkind) + var_dlength, & ! data vector with variable length dimension (rkind) + in_type_groundwatr, & ! intent(in) arguments for groundwatr call + io_type_groundwatr, & ! intent(inout) arguments for groundwatr call + out_type_groundwatr ! intent(out) arguments for groundwatr call ! named variables defining elements in the data structures USE var_lookup,only:iLookATTR ! named variables for structure elements @@ -78,28 +81,18 @@ module groundwatr_module ! ! ************************************************************************************************ subroutine groundwatr(& - ! input: model control - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - getSatDepth, & ! intent(in): logical flag to compute index of the lowest saturated layer - ! input: state and diagnostic variables - mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) - mLayerMatricHeadLiq, & ! intent(in): liquid water matric potential (m) - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water (-) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice (-) + ! input: model control, state variables, and diagnostic variables + in_groundwatr, & ! intent(in): model control, state variables, and diagnostic variables ! input/output: data structures attr_data, & ! intent(in): spatial attributes mpar_data, & ! intent(in): model parameters prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU - ! output: baseflow - ixSaturation, & ! intent(inout) index of lowest saturated layer (NOTE: only computed on the first iteration) - mLayerBaseflow, & ! intent(out): baseflow from each soil layer (m s-1) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - ! output: error control - err,message) ! intent(out): error control + ! input-output: baseflow + io_groundwatr, & ! intent(inout): index of lowest saturated layer (NOTE: only computed on the first iteration) + ! output: baseflow and error control + out_groundwatr) ! intent(out): baseflow and error control ! --------------------------------------------------------------------------------------- ! utility modules USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water as a function of matric head @@ -108,43 +101,39 @@ subroutine groundwatr(& ! --------------------------------------------------------------------------------------- ! * dummy variables ! --------------------------------------------------------------------------------------- - ! input: model control - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - logical(lgt),intent(in) :: getSatDepth ! logical flag to compute index of the lowest saturated layer - ! input: state and diagnostic variables - real(rkind),intent(in) :: mLayerdTheta_dPsi(:) ! derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) - real(rkind),intent(in) :: mLayerMatricHeadLiq(:) ! matric head in each layer at the current iteration (m) - real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid water (-) - real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice (-) - ! input/output: data structures - type(var_d),intent(in) :: attr_data ! spatial attributes - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - ! output: baseflow - integer(i4b),intent(inout) :: ixSaturation ! index of lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: mLayerBaseflow(:) ! baseflow from each soil layer (m s-1) - real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + ! input: model control, state variables, and diagnostic variables + type(in_type_groundwatr),intent(in) :: in_groundwatr ! model control, state variables, and diagnostic variables + ! input-output: data structures + type(var_d),intent(in) :: attr_data ! spatial attributes + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + ! input-output: baseflow + type(io_type_groundwatr),intent(inout) :: io_groundwatr ! index of lowest saturated layer (NOTE: only computed on the first iteration) + ! output: baseflow and error control + type(out_type_groundwatr),intent(out) :: out_groundwatr ! baseflow and error control ! --------------------------------------------------------------------------------------- ! * local variables ! --------------------------------------------------------------------------------------- ! general local variables - integer(i4b) :: iLayer ! index of soil layer - real(rkind),dimension(nSoil,nSoil) :: dBaseflow_dVolLiq ! derivative in the baseflow flux w.r.t. volumetric liquid water content (m s-1) + integer(i4b) :: iLayer ! index of soil layer + real(rkind),dimension(in_groundwatr%nSoil,in_groundwatr%nSoil) :: dBaseflow_dVolLiq ! derivative in the baseflow flux w.r.t. volumetric liquid water content (m s-1) ! *************************************************************************************** ! *************************************************************************************** - ! initialize error control - err=0; message='groundwatr/' - ! --------------------------------------------------------------------------------------- - ! --------------------------------------------------------------------------------------- ! associate variables in data structures + allocate(out_groundwatr % mLayerBaseflow(in_groundwatr%nSoil),out_groundwatr % dBaseflow_dMatric(in_groundwatr%nSoil,in_groundwatr%nSoil)) ! allocate intent(out) data structure components associate(& + ! input: model control + nSnow => in_groundwatr % nSnow, & ! intent(in): [i4b] number of snow layers + nSoil => in_groundwatr % nSoil, & ! intent(in): [i4b] number of soil layers + nLayers => in_groundwatr % nLayers, & ! intent(in): [i4b] total number of layers + getSatDepth => in_groundwatr % firstFluxCall, & ! intent(in): [lgt] logical flag to compute index of the lowest saturated layer + ! input: state and diagnostic variables + mLayerdTheta_dPsi => in_groundwatr % mLayerdTheta_dPsi, & ! intent(in): [dp] derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) + mLayerMatricHeadLiq => in_groundwatr % mLayerMatricHeadLiqTrial, & ! intent(in): [dp] matric head in each layer at the current iteration (m) + mLayerVolFracLiq => in_groundwatr % mLayerVolFracLiqTrial, & ! intent(in): [dp] volumetric fraction of liquid water (-) + mLayerVolFracIce => in_groundwatr % mLayerVolFracIceTrial, & ! intent(in): [dp] volumetric fraction of ice (-) ! input: baseflow parameters fieldCapacity => mpar_data%var(iLookPARAM%fieldCapacity)%dat(1), & ! intent(in): [dp] field capacity (-) theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): [dp] soil porosity (-) @@ -153,10 +142,20 @@ subroutine groundwatr(& vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat, & ! intent(in): [dp] van Genutchen "alpha" parameter (m-1) vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat, & ! intent(in): [dp] van Genutchen "n" parameter (-) vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat, & ! intent(in): [dp] van Genutchen "m" parameter (-) + ! input-output: baseflow + ixSaturation => io_groundwatr % ixSaturation, & ! intent(inout): [i4b] index of lowest saturated layer (NOTE: only computed on the first iteration) ! output: diagnostic variables scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1), & ! intent(out): [dp] exfiltration from the soil profile (m s-1) - mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat & ! intent(out): [dp(:)] column outflow from each soil layer (m3 s-1) + mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat, & ! intent(out): [dp(:)] column outflow from each soil layer (m3 s-1) + ! output: baseflow + mLayerBaseflow => out_groundwatr % mLayerBaseflow, & ! intent(out): [dp(:)] baseflow from each soil layer (m s-1) + dBaseflow_dMatric => out_groundwatr % dBaseflow_dMatric, & ! intent(out): [dp(:,:)] derivative in baseflow w.r.t. matric head (s-1) + ! output: error control + err => out_groundwatr % err, & ! intent(out): [i4b] error code + message => out_groundwatr % cmessage & ! intent(out): [character] error message ) ! end association to variables in data structures + ! initialize error control + err=0; message='groundwatr/' ! ************************************************************************************************ ! (1) compute the "active" portion of the soil profile From a94d83228d409958348c86eb7b7f6989bc714fd6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 11 Sep 2023 11:14:45 +0900 Subject: [PATCH 0919/1472] cut y axis on histogram --- utils/hist_per_GRU.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 5002c5f32..295808c9d 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -155,7 +155,8 @@ def run_loop(i,var,mx): axs[r,c].set_title(plt_titl[i] + stat_word) axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl[i])) axs[r,c].set_ylabel('GRU count') - axs[r,c].set_ylim([0, 45000]) + if var != 'wallClockTime': axs[r,c].set_ylim([0, 25000]) + for i,(var,mx) in enumerate(zip(plot_vars,maxes)): run_loop(i,var,mx) From cbaa747ba860614dfe3c8af2bd4b63d6d7451dc3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 11 Sep 2023 19:54:08 +0900 Subject: [PATCH 0920/1472] try zMax of 10 and maxLineSearch of 100 --- build/source/engine/eval8summa.f90 | 4 ++-- build/source/engine/summaSolve4numrec.f90 | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index b2c6aa842..4d5b41b61 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -918,9 +918,9 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st water_bounds = .true. ! flag to force water bounds, works best if on case(numrec) small_delTemp = .true. ! flag to constain temperature change to be less than zMaxTempIncrement - zMaxTempIncrement = 1._rkind ! maximum temperature increment (K) + zMaxTempIncrement = 10._rkind ! maximum temperature increment (K) small_delMatric = .true. ! flag to constain matric head change to be less than zMaxMatricIncrement - zMaxMatricIncrement = 1._rkind ! maximum matric head increment (m) + zMaxMatricIncrement = 10._rkind ! maximum matric head increment (m) detect_events = .true. ! flag to do freezing point event detection and cross-over with epsT epsT = 1.e-7_rkind ! small interval above/below critical (K) water_bounds = .true. ! flag to force water bounds diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 36b7bf29b..6479319a3 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -382,7 +382,7 @@ subroutine lineSearchRefinement(doLineSearch,stateVecTrial,newtStepScaled,aJacSc real(rkind) :: xInc(nState) ! iteration increment (re-scaled to original units of the state vector) logical(lgt) :: feasible ! flag to denote the feasibility of the solution integer(i4b) :: iLine ! line search index - integer(i4b),parameter :: maxLineSearch=5 ! maximum number of backtracks + integer(i4b),parameter :: maxLineSearch=100 ! maximum number of backtracks real(rkind),parameter :: alpha=1.e-4_rkind ! check on gradient real(rkind) :: xLambda ! backtrack magnitude real(rkind) :: xLambdaTemp ! temporary backtrack magnitude From cf659c5ea7a089325a16e596c9e6c56d96f869ee Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 12 Sep 2023 11:17:12 +0900 Subject: [PATCH 0921/1472] Adding buffers so IDA can deal with small negatives. Not sure about conductance fix-- it could be wrong for numrec?? --- build/source/engine/varSubstep.f90 | 22 +++++++++++++++++----- build/source/engine/vegNrgFlux.f90 | 20 +++++++++++++------- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index bee964506..df0b42c0b 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -610,7 +610,9 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe integer(i4b) :: ixFullVector ! index within full state vector integer(i4b) :: ixControlIndex ! index within a given domain real(rkind) :: volMelt ! volumetric melt (kg m-3) - real(rkind),parameter :: verySmall=epsilon(1._rkind)*2._rkind ! a very small number (deal with precision issues) + real(rkind),parameter :: verySmall=epsilon(1._rkind) ! a very small number (deal with precision issues) + real(rkind) :: verySmall_veg ! precision needs to vary based on set canopy water tolerance for IDA + real(rkind) :: verySmall_snow ! precision needs to vary based on set snow water tolerance for IDA ! mass balance real(rkind) :: canopyBalance0,canopyBalance1 ! canopy storage at start/end of time step real(rkind) :: soilBalance0,soilBalance1 ! soil storage at start/end of time step @@ -786,9 +788,18 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe mLayerMatricHeadLiqPrime = realMissing scalarAquiferStoragePrime = realMissing + ! set the default precision for the very small number + verySmall_veg = verySmall*2._rkind + verySmall_snow = verySmall*2._rkind + select case(ixNumericalMethod) case(ida) #ifdef SUNDIALS_ACTIVE + ! IDA precision needs to vary based on set tolerances + verySmall_veg = mpar_data%var(iLookPARAM%absTolWatVeg)%dat(1)*2._rkind + verySmall_snow = mpar_data%var(iLookPARAM%absTolWatSnow)%dat(1)*2._rkind + + ! extract the derivatives from the state vector call varExtract(& ! input stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) @@ -848,6 +859,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe err,cmessage) ! intent(out): error control #endif case(kinsol, numrec) + ! update diagnostic variables call updateVars(& ! input @@ -1057,7 +1069,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! canopy within numerical precision if(scalarCanopyIceTrial < 0._rkind)then - if(scalarCanopyIceTrial > -verySmall)then + if(scalarCanopyIceTrial > -verySmall_veg)then scalarCanopyLiqTrial = scalarCanopyLiqTrial - scalarCanopyIceTrial scalarCanopyIceTrial = 0._rkind @@ -1079,7 +1091,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! snow layer within numerical precision if(mLayerVolFracIceTrial(iState) < 0._rkind)then - if(mLayerVolFracIceTrial(iState) > -verySmall)then + if(mLayerVolFracIceTrial(iState) > -verySmall_snow)then mLayerVolFracLiqTrial(iState) = mLayerVolFracLiqTrial(iState) - mLayerVolFracIceTrial(iState) mLayerVolFracIceTrial(iState) = 0._rkind @@ -1110,7 +1122,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! canopy within numerical precision if(scalarCanopyLiqTrial < 0._rkind)then - if(scalarCanopyLiqTrial > -verySmall)then + if(scalarCanopyLiqTrial > -verySmall_veg)then scalarCanopyIceTrial = scalarCanopyIceTrial - scalarCanopyLiqTrial scalarCanopyLiqTrial = 0._rkind @@ -1132,7 +1144,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! snow layer within numerical precision if(mLayerVolFracLiqTrial(iState) < 0._rkind)then - if(mLayerVolFracLiqTrial(iState) > -verySmall)then + if(mLayerVolFracLiqTrial(iState) > -verySmall_snow)then mLayerVolFracIceTrial(iState) = mLayerVolFracIceTrial(iState) - mLayerVolFracLiqTrial(iState) mLayerVolFracLiqTrial(iState) = 0._rkind diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 50499a043..b497d7841 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -2144,16 +2144,22 @@ subroutine turbFluxes(& totalConductanceLH = evapConductance + transConductance + groundConductanceLH + canopyConductance ! check sensible heat conductance - if (totalConductanceSH < -tinyVal .or. groundConductanceSH < -tinyVal .or. canopyConductance < -tinyVal) then + if(totalConductanceSH < tinyVal .or. groundConductanceSH < -tinyVal .or. canopyConductance < -tinyVal)then + if(groundConductanceSH < -tinyVal) groundConductanceSH = 0._rkind + if(canopyConductance < -tinyVal) canopyConductance = 0._rkind + totalConductanceSH = leafConductance + groundConductanceSH + canopyConductance + if(totalConductanceSH < tinyVal) totalConductanceSH = 0._rkind message=trim(message)//'negative conductance for sensible heat' - err=20; return - end if - + !err=20; return + endif ! check latent heat conductance - if (totalConductanceLH < tinyVal .or. groundConductanceLH < -tinyVal) then + if(totalConductanceLH < tinyVal .or. groundConductanceLH < -tinyVal)then + if(groundConductanceLH < -tinyVal) groundConductanceLH = 0._rkind + totalConductanceLH = evapConductance + transConductance + groundConductanceLH + canopyConductance + if(totalConductanceLH < tinyVal) totalConductanceLH = 0._rkind message=trim(message)//'negative conductance for latent heat' - err=20; return - end if + !err=20; return + endif ! compute derivatives in individual conductances for sensible heat w.r.t. canopy temperature (m s-1 K-1) ! NOTE: it may be more efficient to compute these derivatives when computing resistances From 4f6d01a424e54a675f321eda5ae00654bd2c027d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 13 Sep 2023 15:34:40 +0900 Subject: [PATCH 0922/1472] update utilities time run --- utils/process_hist.sh | 2 +- utils/process_plot.sh | 2 +- utils/process_scat.sh | 2 +- utils/process_stat.sh | 2 +- utils/timeseries_to_statistics.py | 3 ++- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/utils/process_hist.sh b/utils/process_hist.sh index 860373d30..50797616a 100755 --- a/utils/process_hist.sh +++ b/utils/process_hist.sh @@ -2,7 +2,7 @@ #SBATCH --ntasks-per-node=1 #SBATCH --cpus-per-task=1 #SBATCH --mem-per-cpu=2GB -#SBATCH --time=1-00:00 +#SBATCH --time=0-01:00 #SBATCH --job-name=HIST #SBATCH --mail-user=gwu479@usask.ca #SBATCH --mail-type=ALL diff --git a/utils/process_plot.sh b/utils/process_plot.sh index ff35b398e..a6259221b 100755 --- a/utils/process_plot.sh +++ b/utils/process_plot.sh @@ -2,7 +2,7 @@ #SBATCH --ntasks-per-node=1 #SBATCH --cpus-per-task=1 #SBATCH --mem-per-cpu=50GB -#SBATCH --time=1-00:00 +#SBATCH --time=0-04:00 #SBATCH --job-name=GDB #SBATCH --mail-user=gwu479@usask.ca #SBATCH --mail-type=ALL diff --git a/utils/process_scat.sh b/utils/process_scat.sh index ad0c4ba4c..545704344 100755 --- a/utils/process_scat.sh +++ b/utils/process_scat.sh @@ -2,7 +2,7 @@ #SBATCH --ntasks-per-node=1 #SBATCH --cpus-per-task=1 #SBATCH --mem-per-cpu=2GB -#SBATCH --time=1-00:00 +#SBATCH --time=0-01:00 #SBATCH --job-name=SCAT #SBATCH --mail-user=gwu479@usask.ca #SBATCH --mail-type=ALL diff --git a/utils/process_stat.sh b/utils/process_stat.sh index 83033d926..ce1b2053b 100755 --- a/utils/process_stat.sh +++ b/utils/process_stat.sh @@ -1,7 +1,7 @@ #!/bin/bash #SBATCH --ntasks-per-node=1 #SBATCH --cpus-per-task=32 -#SBATCH --mem-per-cpu=4GB +#SBATCH --mem-per-cpu=3GB #SBATCH --time=1-00:00 #SBATCH --job-name=STAT #SBATCH --mail-user=gwu479@usask.ca diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 4d3c7d2e6..eee77dcf0 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -186,8 +186,9 @@ def merge_subsets_into_one(src,pattern,des,name): # -- start parallel processing ncpus = int(os.environ.get('SLURM_CPUS_PER_TASK',default=1)) if __name__ == "__main__": + import multiprocessing as mp pool = mp.Pool(processes=ncpus) - results = [pool.apply_async(run_loop, args=(file, bench, lock)) for (file, bench) in zip(src_files, ben_files)] + results = [pool.apply_async(run_loop, args=(file, bench)) for (file, bench) in zip(src_files, ben_files)] dojob = [p.get() for p in results] pool.close() # -- end parallel processing From 40664c51e0e01867a2eaea81e5c73dbb2ce6b076 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 13 Sep 2023 01:09:28 -0600 Subject: [PATCH 0923/1472] Minor format update to groundwatr.f90. --- build/source/engine/groundwatr.f90 | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/build/source/engine/groundwatr.f90 b/build/source/engine/groundwatr.f90 index a8bb3d6ce..c1c87b92c 100644 --- a/build/source/engine/groundwatr.f90 +++ b/build/source/engine/groundwatr.f90 @@ -81,18 +81,18 @@ module groundwatr_module ! ! ************************************************************************************************ subroutine groundwatr(& - ! input: model control, state variables, and diagnostic variables - in_groundwatr, & ! intent(in): model control, state variables, and diagnostic variables - ! input/output: data structures - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! input-output: baseflow - io_groundwatr, & ! intent(inout): index of lowest saturated layer (NOTE: only computed on the first iteration) - ! output: baseflow and error control - out_groundwatr) ! intent(out): baseflow and error control + ! input: model control, state variables, and diagnostic variables + in_groundwatr, & ! intent(in): model control, state variables, and diagnostic variables + ! input/output: data structures + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! input-output: baseflow + io_groundwatr, & ! intent(inout): index of lowest saturated layer (NOTE: only computed on the first iteration) + ! output: baseflow and error control + out_groundwatr) ! intent(out): baseflow and error control ! --------------------------------------------------------------------------------------- ! utility modules USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water as a function of matric head From 240fb38e7c4f1d70ffc58118ec17fdbb0c2f293f Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 13 Sep 2023 04:10:05 -0600 Subject: [PATCH 0924/1472] Implement derived types to shorten the call syntax for bigAquifer. --- build/source/dshare/data_types.f90 | 28 ++++++++ build/source/engine/bigAquifer.f90 | 104 +++++++++++++---------------- build/source/engine/computFlux.f90 | 81 +++++++++++----------- 3 files changed, 118 insertions(+), 95 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index a2672c317..f8d0fddc0 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -494,4 +494,32 @@ MODULE data_types character(:),allocatable :: cmessage ! intent(out): error message end type out_type_groundwatr ! ** end groundwatr + + ! ** bigAquifer + type, public :: in_type_bigAquifer ! derived type for intent(in) arguments in bigAquifer call + real(rkind) :: scalarAquiferStorageTrial ! intent(in): trial value of aquifer storage (m) + real(rkind) :: scalarCanopyTranspiration ! intent(in): canopy transpiration (kg m-2 s-1) + real(rkind) :: scalarSoilDrainage ! intent(in): soil drainage (m s-1) + real(rkind) :: dCanopyTrans_dCanWat ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + real(rkind) :: dCanopyTrans_dTCanair ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(rkind) :: dCanopyTrans_dTCanopy ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(rkind) :: dCanopyTrans_dTGround ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + end type in_type_bigAquifer + + type, public :: io_type_bigAquifer ! derived type for intent(inout) arguments in bigAquifer call + real(rkind) :: dAquiferTrans_dTCanair ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + real(rkind) :: dAquiferTrans_dTCanopy ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy temperature + real(rkind) :: dAquiferTrans_dTGround ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. ground temperature + real(rkind) :: dAquiferTrans_dCanWat ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy total water + end type io_type_bigAquifer + + type, public :: out_type_bigAquifer ! derived type for intent(out) arguments in bigAquifer call + real(rkind) :: scalarAquiferTranspire ! intent(out): transpiration loss from the aquifer (m s-1) + real(rkind) :: scalarAquiferRecharge ! intent(out): recharge to the aquifer (m s-1) + real(rkind) :: scalarAquiferBaseflow ! intent(out): total baseflow from the aquifer (m s-1) + real(rkind) :: dBaseflow_dAquifer ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) + integer(i4b) :: err ! intent(out): error code + character(:),allocatable :: cmessage ! intent(out): error message + end type out_type_bigAquifer + ! ** end bigAquifer END MODULE data_types diff --git a/build/source/engine/bigAquifer.f90 b/build/source/engine/bigAquifer.f90 index 66400d358..07e955902 100644 --- a/build/source/engine/bigAquifer.f90 +++ b/build/source/engine/bigAquifer.f90 @@ -40,81 +40,73 @@ module bigAquifer_module ! public subroutine bigAquifer: compute aquifer water fluxes and their derivatives ! *************************************************************************************************************** subroutine bigAquifer(& - ! input: state variables and fluxes - scalarAquiferStorageTrial, & ! intent(in): trial value of aquifer storage (m) - scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) - scalarSoilDrainage, & ! intent(in): soil drainage (m s-1) - ! input: pre-computed derivatives - dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - ! input: diagnostic variables and parameters - mpar_data, & ! intent(in): model parameter structure - diag_data, & ! intent(in): diagnostic variable structure - ! output: fluxes - scalarAquiferTranspire, & ! intent(out): transpiration loss from the aquifer (m s-1) - scalarAquiferRecharge, & ! intent(out): recharge to the aquifer (m s-1) - scalarAquiferBaseflow, & ! intent(out): total baseflow from the aquifer (m s-1) - dBaseflow_dAquifer, & ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) - ! output: derivatives in transpiration w.r.t. canopy state variables - dAquiferTrans_dTCanair, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature - dAquiferTrans_dTCanopy, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy temperature - dAquiferTrans_dTGround, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. ground temperature - dAquiferTrans_dCanWat, & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy total water - ! output: error control - err,message) ! intent(out): error control + ! input: state variables, fluxes, and pre-computed derivatives + in_bigAquifer, & ! intent(in): state variables, fluxes, and pre-computed derivatives + ! input: diagnostic variables and parameters + mpar_data, & ! intent(in): model parameter structure + diag_data, & ! intent(in): diagnostic variable structure + ! input-output: derivatives in transpiration w.r.t. canopy state variables + io_bigAquifer, & ! intent(inout): derivatives in transpiration w.r.t. canopy state variables + ! output: fluxes and error control + out_bigAquifer) ! intent(out): fluxes and error control ! named variables USE var_lookup,only:iLookDIAG ! named variables for structure elements USE var_lookup,only:iLookPARAM ! named variables for structure elements ! data types USE data_types,only:var_dlength ! x%var(:)%dat [rkind] + USE data_types,only:in_type_bigAquifer ! derived typ for intent(in) arguments + USE data_types,only:io_type_bigAquifer ! derived typ for intent(inout) arguments + USE data_types,only:out_type_bigAquifer ! derived typ for intent(out) arguments ! ------------------------------------------------------------------------------------------------------------------------------------------------- implicit none - ! input: state variables, fluxes, and parameters - real(rkind),intent(in) :: scalarAquiferStorageTrial ! trial value of aquifer storage (m) - real(rkind),intent(in) :: scalarCanopyTranspiration ! canopy transpiration (kg m-2 s-1) - real(rkind),intent(in) :: scalarSoilDrainage ! soil drainage (m s-1) - ! input: pre-computed derivatves - real(rkind),intent(in) :: dCanopyTrans_dCanWat ! derivative in canopy transpiration w.r.t. canopy total water content (s-1) - real(rkind),intent(in) :: dCanopyTrans_dTCanair ! derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - real(rkind),intent(in) :: dCanopyTrans_dTCanopy ! derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - real(rkind),intent(in) :: dCanopyTrans_dTGround ! derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + ! input: state variables, fluxes, and pre-computed derivatives + type(in_type_bigAquifer),intent(in) :: in_bigAquifer ! state variables, fluxes, and pre-computed derivatives ! input: diagnostic variables and parameters - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - ! output: fluxes - real(rkind),intent(out) :: scalarAquiferTranspire ! transpiration loss from the aquifer (m s-1) - real(rkind),intent(out) :: scalarAquiferRecharge ! recharge to the aquifer (m s-1) - real(rkind),intent(out) :: scalarAquiferBaseflow ! total baseflow from the aquifer (m s-1) - real(rkind),intent(out) :: dBaseflow_dAquifer ! change in baseflow flux w.r.t. aquifer storage (s-1) - ! output: derivatives in transpiration w.r.t. canopy state variables - real(rkind),intent(inout) :: dAquiferTrans_dTCanair ! derivatives in the aquifer transpiration flux w.r.t. canopy air temperature - real(rkind),intent(inout) :: dAquiferTrans_dTCanopy ! derivatives in the aquifer transpiration flux w.r.t. canopy temperature - real(rkind),intent(inout) :: dAquiferTrans_dTGround ! derivatives in the aquifer transpiration flux w.r.t. ground temperature - real(rkind),intent(inout) :: dAquiferTrans_dCanWat ! derivatives in the aquifer transpiration flux w.r.t. canopy total water - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + ! input-output: derivatives in transpiration w.r.t. canopy state variables + type(io_type_bigAquifer),intent(inout) :: io_bigAquifer ! derivatives in transpiration w.r.t. canopy state variables + ! output: fluxes and error control + type(out_type_bigAquifer),intent(out) :: out_bigAquifer ! fluxes and error control ! ----------------------------------------------------------------------------------------------------------------------------------------------------- ! local variables - real(rkind) :: aquiferTranspireFrac ! fraction of total transpiration that comes from the aquifer (-) - real(rkind) :: xTemp ! temporary variable (-) + real(rkind) :: aquiferTranspireFrac ! fraction of total transpiration that comes from the aquifer (-) + real(rkind) :: xTemp ! temporary variable (-) ! ------------------------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='bigAquifer/' - ! make association between local variables and the information in the data structures associate(& - ! model diagnostic variables: contribution of the aquifer to transpiration + ! input: state variables, fluxes, and parameters + scalarAquiferStorageTrial => in_bigAquifer % scalarAquiferStorageTrial, & ! intent(in): [dp] trial value of aquifer storage (m) + scalarCanopyTranspiration => in_bigAquifer % scalarCanopyTranspiration, & ! intent(in): [dp] canopy transpiration (kg m-2 s-1) + scalarSoilDrainage => in_bigAquifer % scalarSoilDrainage, & ! intent(in): [dp] soil drainage (m s-1) + ! input: pre-computed derivatves + dCanopyTrans_dCanWat => in_bigAquifer % dCanopyTrans_dCanWat, & ! intent(in): [dp] derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair => in_bigAquifer % dCanopyTrans_dTCanair, & ! intent(in): [dp] derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy => in_bigAquifer % dCanopyTrans_dTCanopy, & ! intent(in): [dp] derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround => in_bigAquifer % dCanopyTrans_dTGround, & ! intent(in): [dp] derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + ! input: model diagnostic variables: contribution of the aquifer to transpiration scalarTranspireLim => diag_data%var(iLookDIAG%scalarTranspireLim)%dat(1), & ! intent(in): [dp] weighted average of the transpiration limiting factor (-) scalarAquiferRootFrac => diag_data%var(iLookDIAG%scalarAquiferRootFrac)%dat(1), & ! intent(in): [dp] fraction of roots below the lowest soil layer (-) scalarTranspireLimAqfr => diag_data%var(iLookDIAG%scalarTranspireLimAqfr)%dat(1), & ! intent(in): [dp] transpiration limiting factor for the aquifer (-) - ! model parameters: baseflow flux + ! input: model parameters: baseflow flux aquiferBaseflowRate => mpar_data%var(iLookPARAM%aquiferBaseflowRate)%dat(1), & ! intent(in): [dp] tbaseflow rate when aquiferStorage = aquiferScaleFactor (m s-1) aquiferScaleFactor => mpar_data%var(iLookPARAM%aquiferScaleFactor)%dat(1), & ! intent(in): [dp] scaling factor for aquifer storage in the big bucket (m) - aquiferBaseflowExp => mpar_data%var(iLookPARAM%aquiferBaseflowExp)%dat(1) & ! intent(in): [dp] baseflow exponent (-) + aquiferBaseflowExp => mpar_data%var(iLookPARAM%aquiferBaseflowExp)%dat(1), & ! intent(in): [dp] baseflow exponent (-) + ! input-output: derivatives in transpiration w.r.t. canopy state variables + dAquiferTrans_dTCanair => io_bigAquifer % dAquiferTrans_dTCanair, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + dAquiferTrans_dTCanopy => io_bigAquifer % dAquiferTrans_dTCanopy, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy temperature + dAquiferTrans_dTGround => io_bigAquifer % dAquiferTrans_dTGround, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. ground temperature + dAquiferTrans_dCanWat => io_bigAquifer % dAquiferTrans_dCanWat, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy total water + ! output: fluxes + scalarAquiferTranspire => out_bigAquifer % scalarAquiferTranspire,& ! intent(out): transpiration loss from the aquifer (m s-1) + scalarAquiferRecharge => out_bigAquifer % scalarAquiferRecharge, & ! intent(out): recharge to the aquifer (m s-1) + scalarAquiferBaseflow => out_bigAquifer % scalarAquiferBaseflow, & ! intent(out): total baseflow from the aquifer (m s-1) + dBaseflow_dAquifer => out_bigAquifer % dBaseflow_dAquifer, & ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) + ! output: error control + err => out_bigAquifer % err, & ! intent(out): error code + message => out_bigAquifer % cmessage & ! intent(out): error message ) ! end associating local variables with the information in the data structures + err=0; message='bigAquifer/' ! initialize error control ! compute aquifer transpiration (m s-1) aquiferTranspireFrac = scalarAquiferRootFrac*scalarTranspireLimAqfr/scalarTranspireLim ! fraction of total transpiration that comes from the aquifer (-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index c959ab7a5..26216b6d7 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -45,8 +45,10 @@ module computFlux_module out_type_soilLiqFlx,& ! intent(out) arguments for soilLiqFlx call in_type_groundwatr, & ! intent(in) arguments for groundwatr call io_type_groundwatr, & ! intent(inout) arguments for groundwatr call - out_type_groundwatr ! intent(out) arguments for groundwatr call - + out_type_groundwatr,& ! intent(out) arguments for groundwatr call + in_type_bigAquifer, & ! intent(in) arguments for bigAquifer call + io_type_bigAquifer, & ! intent(inout) arguments for bigAquifer call + out_type_bigAquifer ! intent(out) arguments for bigAquifer call ! indices that define elements of the data structures USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure @@ -246,6 +248,9 @@ subroutine computFlux(& type(in_type_groundwatr) :: in_groundwatr ! data structure for intent(in) groundwatr arguments type(io_type_groundwatr) :: io_groundwatr ! data structure for intent(inout) groundwatr arguments type(out_type_groundwatr) :: out_groundwatr ! data structure for intent(out) groundwatr arguments + type(in_type_bigAquifer) :: in_bigAquifer ! data structure for intent(in) bigAquifer arguments + type(io_type_bigAquifer) :: io_bigAquifer ! data structure for intent(inout) bigAquifer arguments + type(out_type_bigAquifer) :: out_bigAquifer ! data structure for intent(out) bigAquifer arguments ! -------------------------------------------------------------- ! initialize error control err=0; message='computFlux/' @@ -546,47 +551,17 @@ subroutine computFlux(& ! check if computing aquifer fluxes if (ixAqWat/=integerMissing) then if (local_ixGroundwater==bigBucket) then ! identify modeling decision - ! compute fluxes for the big bucket - call bigAquifer(& - ! input: state variables and fluxes - scalarAquiferStorageTrial, & ! intent(in): trial value of aquifer storage (m) - scalarCanopyTranspiration, & ! intent(in): canopy transpiration (kg m-2 s-1) - scalarSoilDrainage, & ! intent(in): soil drainage (m s-1) - ! input: pre-computed derivatives - dCanopyTrans_dCanWat, & ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - dCanopyTrans_dTCanair, & ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTCanopy, & ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTGround, & ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - ! input: diagnostic variables and parameters - mpar_data, & ! intent(in): model parameter structure - diag_data, & ! intent(in): diagnostic variable structure - ! output: fluxes - scalarAquiferTranspire, & ! intent(out): transpiration loss from the aquifer (m s-1) - scalarAquiferRecharge, & ! intent(out): recharge to the aquifer (m s-1) - scalarAquiferBaseflow, & ! intent(out): total baseflow from the aquifer (m s-1) - dBaseflow_dAquifer, & ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) - ! output: derivatives in transpiration w.r.t. canopy state variables - dAquiferTrans_dTCanair, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature - dAquiferTrans_dTCanopy, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy temperature - dAquiferTrans_dTGround, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. ground temperature - dAquiferTrans_dCanWat, & ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy total water - ! output: error control - err,cmessage) ! intent(out): error control - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! compute total runoff (overwrite previously calculated value before considering aquifer). - ! (Note: SoilDrainage goes into aquifer, not runoff) - scalarTotalRunoff = scalarSurfaceRunoff + scalarAquiferBaseflow - + call subTools(iLookOP%pre,iLookROUTINE%bigAquifer) ! pre-processing for call to bigAquifer + call bigAquifer(in_bigAquifer,mpar_data,diag_data,io_bigAquifer,out_bigAquifer) + call subTools(iLookOP%post,iLookROUTINE%bigAquifer) ! post-processing for call to bigAquifer ! if no aquifer, then fluxes are zero - else + else ! if no aquifer, then fluxes are zero scalarAquiferTranspire = 0._rkind ! transpiration loss from the aquifer (m s-1) scalarAquiferRecharge = 0._rkind ! recharge to the aquifer (m s-1) scalarAquiferBaseflow = 0._rkind ! total baseflow from the aquifer (m s-1) dBaseflow_dAquifer = 0._rkind ! change in baseflow flux w.r.t. aquifer storage (s-1) - end if ! no aquifer - + end if ! end if aquifer exists end if ! if computing aquifer fluxes ! ***** @@ -1092,9 +1067,37 @@ subroutine subTools(op,sub) end if case(iLookROUTINE%bigAquifer) ! bigAquifer if (op==iLookOP%pre) then ! pre-processing - + ! intent(in) arguments + in_bigAquifer % scalarAquiferStorageTrial = scalarAquiferStorageTrial ! intent(in): trial value of aquifer storage (m) + in_bigAquifer % scalarCanopyTranspiration = scalarCanopyTranspiration ! intent(in): canopy transpiration (kg m-2 s-1) + in_bigAquifer % scalarSoilDrainage = scalarSoilDrainage ! intent(in): soil drainage (m s-1) + in_bigAquifer % dCanopyTrans_dCanWat = dCanopyTrans_dCanWat ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + in_bigAquifer % dCanopyTrans_dTCanair = dCanopyTrans_dTCanair ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + in_bigAquifer % dCanopyTrans_dTCanopy = dCanopyTrans_dTCanopy ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + in_bigAquifer % dCanopyTrans_dTGround = dCanopyTrans_dTGround ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + ! intent(inout) arguments + io_bigAquifer % dAquiferTrans_dTCanair = dAquiferTrans_dTCanair ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + io_bigAquifer % dAquiferTrans_dTCanopy = dAquiferTrans_dTCanopy ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy temperature + io_bigAquifer % dAquiferTrans_dTGround = dAquiferTrans_dTGround ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. ground temperature + io_bigAquifer % dAquiferTrans_dCanWat = dAquiferTrans_dCanWat ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy total water else ! post-processing - + ! intent(inout) arguments + dAquiferTrans_dTCanair = io_bigAquifer % dAquiferTrans_dTCanair ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + dAquiferTrans_dTCanopy = io_bigAquifer % dAquiferTrans_dTCanopy ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy temperature + dAquiferTrans_dTGround = io_bigAquifer % dAquiferTrans_dTGround ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. ground temperature + dAquiferTrans_dCanWat = io_bigAquifer % dAquiferTrans_dCanWat ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy total water + ! intent(out) arguments + scalarAquiferTranspire = out_bigAquifer % scalarAquiferTranspire ! intent(out): transpiration loss from the aquifer (m s-1) + scalarAquiferRecharge = out_bigAquifer % scalarAquiferRecharge ! intent(out): recharge to the aquifer (m s-1) + scalarAquiferBaseflow = out_bigAquifer % scalarAquiferBaseflow ! intent(out): total baseflow from the aquifer (m s-1) + dBaseflow_dAquifer = out_bigAquifer % dBaseflow_dAquifer ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) + err = out_bigAquifer % err ! intent(out): error code + cmessage = out_bigAquifer % cmessage ! intent(out): error message + ! error control + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + ! compute total runoff (overwrite previously calculated value before considering aquifer). + ! (Note: SoilDrainage goes into aquifer, not runoff) + scalarTotalRunoff = scalarSurfaceRunoff + scalarAquiferBaseflow end if case default ! Error control for sub argument (must be s subroutine index that is included in the above case blocks) err=20 From 3db3445769ec921e4edf143cb5c40eddfb61051c Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 14 Sep 2023 07:47:11 -0600 Subject: [PATCH 0925/1472] Reduced the length of the associate statements in computFlux and subTools. --- build/source/engine/computFlux.f90 | 120 +---------------------------- 1 file changed, 1 insertion(+), 119 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 26216b6d7..7fd174e7e 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -263,18 +263,13 @@ subroutine computFlux(& ! model decisions ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) - ! domain boundary conditions - upperBoundTemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) - scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in): [dp] rainfall rate (kg m-2 s-1) ! canopy and layer depth canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) ! indices of model state variables for the vegetation subdomain ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain - ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer ! indices of model state variables for the snow+soil domain ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain @@ -282,49 +277,28 @@ subroutine computFlux(& layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] type of layer (iname_soil or iname_snow) ! number of state variables of a specific type nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain - nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) ! derivatives - dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) - dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(in): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature ! number of flux calls numFluxCalls => diag_data%var(iLookDIAG%numFluxCalls)%dat(1) ,& ! intent(out): [dp] number of flux calls (-) ! net fluxes over the vegetation domain scalarCanairNetNrgFlux => flux_data%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the canopy air space (W m-2) scalarCanopyNetNrgFlux => flux_data%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the vegetation canopy (W m-2) - scalarGroundNetNrgFlux => flux_data%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the ground surface (W m-2) scalarCanopyNetLiqFlux => flux_data%var(iLookFLUX%scalarCanopyNetLiqFlux)%dat(1) ,& ! intent(out): [dp] net liquid water flux for the vegetation canopy (kg m-2 s-1) ! net fluxes over the snow+soil domain mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) mLayerLiqFluxSnow => flux_data%var(iLookFLUX%mLayerLiqFluxSnow)%dat ,& ! intent(out): [dp] net liquid water flux for each snow layer (s-1) mLayerLiqFluxSoil => flux_data%var(iLookFLUX%mLayerLiqFluxSoil)%dat ,& ! intent(out): [dp] net liquid water flux for each soil layer (s-1) - ! evaporative fluxes - scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(out): [dp] canopy transpiration (kg m-2 s-1) - scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(out): [dp] canopy evaporation/condensation (kg m-2 s-1) - scalarGroundEvaporation => flux_data%var(iLookFLUX%scalarGroundEvaporation)%dat(1) ,& ! intent(out): [dp] ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(out): [dp(:)] transpiration loss from each soil layer (m s-1) ! fluxes for the snow+soil domain - iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(out): [dp(0:)] vertical energy flux at the interface of snow and soil layers iLayerLiqFluxSnow => flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at snow layer interfaces (-) iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at soil layer interfaces (-) - mLayerHydCond => flux_data%var(iLookFLUX%mLayerHydCond)%dat ,& ! intent(out): [dp(:)] hydraulic conductivity in each soil layer (m s-1) mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(out): [dp(:)] baseflow from each soil layer (m s-1) - scalarSnowDrainage => flux_data%var(iLookFLUX%scalarSnowDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the snow profile (m s-1) scalarSoilDrainage => flux_data%var(iLookFLUX%scalarSoilDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the soil profile (m s-1) scalarSoilBaseflow => flux_data%var(iLookFLUX%scalarSoilBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the soil profile (m s-1) ! infiltration - scalarInfilArea => diag_data%var(iLookDIAG%scalarInfilArea )%dat(1) ,& ! intent(out): [dp] fraction of unfrozen area where water can infiltrate (-) - scalarFrozenArea => diag_data%var(iLookDIAG%scalarFrozenArea )%dat(1) ,& ! intent(out): [dp] fraction of area that is considered impermeable due to soil ice (-) - scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1) ,& ! intent(out): [dp] soil control on infiltration, zero or one - scalarMaxInfilRate => flux_data%var(iLookFLUX%scalarMaxInfilRate)%dat(1) ,& ! intent(out): [dp] maximum infiltration rate (m s-1) - scalarInfiltration => flux_data%var(iLookFLUX%scalarInfiltration)%dat(1) ,& ! intent(out): [dp] infiltration of water into the soil profile (m s-1) scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(inout): [dp] fraction of liquid water on vegetation (-) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(inout): [dp(:)] fraction of liquid water in each snow layer (-) ! boundary fluxes in the soil domain @@ -340,74 +314,14 @@ subroutine computFlux(& scalarAquiferBaseflow => flux_data%var(iLookFLUX%scalarAquiferBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the aquifer (m s-1) ! total runoff scalarTotalRunoff => flux_data%var(iLookFLUX%scalarTotalRunoff)%dat(1) ,& ! intent(out): [dp] total runoff (m s-1) - ! derivatives in net vegetation energy fluxes w.r.t. relevant state variables - dCanairNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy air temperature - dCanairNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy temperature - dCanairNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. ground temperature - dCanopyNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy air temperature - dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy temperature - dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. ground temperature - dCanopyNetFlux_dCanWat => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in net canopy fluxes w.r.t. canopy total water content - dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy air temperature - dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy temperature - dGroundNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. ground temperature - dGroundNetFlux_dCanWat => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in net ground fluxes w.r.t. canopy total water content - ! derivatives in evaporative fluxes w.r.t. relevant state variables - dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy air temperature - dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy temperature - dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. ground temperature - dCanopyEvaporation_dCanWat => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy total water content - dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy air temperature - dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy temperature - dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. ground temperature - dGroundEvaporation_dCanWat => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy total water content - ! derivatives in transpiration - dCanopyTrans_dTCanair => deriv_data%var(iLookDERIV%dCanopyTrans_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTCanopy => deriv_data%var(iLookDERIV%dCanopyTrans_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTGround => deriv_data%var(iLookDERIV%dCanopyTrans_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - dCanopyTrans_dCanWat => deriv_data%var(iLookDERIV%dCanopyTrans_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy total water content (s-1) ! derivatives in canopy water w.r.t canopy temperature dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(out): [dp] derivative of canopy liquid storage w.r.t. temperature ! derivatives in canopy liquid fluxes w.r.t. canopy water scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ,& ! intent(out): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water - scalarThroughfallRainDeriv => deriv_data%var(iLookDERIV%scalarThroughfallRainDeriv )%dat(1) ,& ! intent(out): [dp] derivative in throughfall w.r.t. canopy liquid water - scalarCanopyLiqDrainageDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDrainageDeriv)%dat(1) ,& ! intent(out): [dp] derivative in canopy drainage w.r.t. canopy liquid water - ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below - dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above - dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below - dThermalC_dWatAbove => deriv_data%var(iLookDERIV%dThermalC_dWatAbove )%dat ,& ! intent(in): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow )%dat ,& ! intent(in): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above - ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below - dNrgFlux_dWatAbove => deriv_data%var(iLookDERIV%dNrgFlux_dWatAbove )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. water state in the layer above - dNrgFlux_dWatBelow => deriv_data%var(iLookDERIV%dNrgFlux_dWatBelow )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. water state in the layer below - dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove )%dat ,& ! intent(in): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow )%dat ,& ! intent(in): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(out): [dp(:)] derivative in vertical liquid water flux at layer interfaces - ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential - dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above - dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below - dq_dHydStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dHydStateLayerSurfVec )%dat ,& ! intent(out): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers - mLayerdTheta_dPsi => deriv_data%var(iLookDERIV%mLayerdTheta_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. psi - mLayerdPsi_dTheta => deriv_data%var(iLookDERIV%mLayerdPsi_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. theta - dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in compressibility w.r.t matric head ! derivative in baseflow flux w.r.t. aquifer storage - dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] derivative in baseflow flux w.r.t. aquifer storage (s-1) - ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables - dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above - dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below - dq_dNrgStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dNrgStateLayerSurfVec )%dat ,& ! intent(out): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers - ! derivatives in soil transpiration w.r.t. canopy state variables - mLayerdTrans_dTCanair => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanair )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature - mLayerdTrans_dTCanopy => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanopy )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy temperature - mLayerdTrans_dTGround => deriv_data%var(iLookDERIV%mLayerdTrans_dTGround )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. ground temperature - mLayerdTrans_dCanWat => deriv_data%var(iLookDERIV%mLayerdTrans_dCanWat )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy total water - ! derivatives in aquifer transpiration w.r.t. canopy state variables - dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature - dAquiferTrans_dTCanopy => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanopy )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy temperature - dAquiferTrans_dTGround => deriv_data%var(iLookDERIV%dAquiferTrans_dTGround )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. ground temperature - dAquiferTrans_dCanWat => deriv_data%var(iLookDERIV%dAquiferTrans_dCanWat )%dat(1) & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy total water + dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) & ! intent(out): [dp(:)] derivative in baseflow flux w.r.t. aquifer storage (s-1) ) ! end association to data in structures numFluxCalls = numFluxCalls+1 ! increment the number of flux calls @@ -609,42 +523,17 @@ subroutine subTools(op,sub) integer(i4b),intent(in) :: sub ! index of subroutine in computFlux (e.g., iLookROUTINE%vegNrgFlux for vegNrgFlux routine) associate(& - ! model decisions - ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization - ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) ! domain boundary conditions upperBoundTemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in): [dp] rainfall rate (kg m-2 s-1) ! canopy and layer depth canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! indices of model state variables for the vegetation subdomain - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain - ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer - ! indices of model state variables for the snow+soil domain - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] type of layer (iname_soil or iname_snow) - ! number of state variables of a specific type - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain - nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain - nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) ! derivatives dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(in): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature - ! number of flux calls - numFluxCalls => diag_data%var(iLookDIAG%numFluxCalls)%dat(1) ,& ! intent(out): [dp] number of flux calls (-) ! net fluxes over the vegetation domain scalarCanairNetNrgFlux => flux_data%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the canopy air space (W m-2) scalarCanopyNetNrgFlux => flux_data%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the vegetation canopy (W m-2) @@ -667,22 +556,18 @@ subroutine subTools(op,sub) mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(out): [dp(:)] baseflow from each soil layer (m s-1) scalarSnowDrainage => flux_data%var(iLookFLUX%scalarSnowDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the snow profile (m s-1) scalarSoilDrainage => flux_data%var(iLookFLUX%scalarSoilDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the soil profile (m s-1) - scalarSoilBaseflow => flux_data%var(iLookFLUX%scalarSoilBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the soil profile (m s-1) ! infiltration scalarInfilArea => diag_data%var(iLookDIAG%scalarInfilArea )%dat(1) ,& ! intent(out): [dp] fraction of unfrozen area where water can infiltrate (-) scalarFrozenArea => diag_data%var(iLookDIAG%scalarFrozenArea )%dat(1) ,& ! intent(out): [dp] fraction of area that is considered impermeable due to soil ice (-) scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1) ,& ! intent(out): [dp] soil control on infiltration, zero or one scalarMaxInfilRate => flux_data%var(iLookFLUX%scalarMaxInfilRate)%dat(1) ,& ! intent(out): [dp] maximum infiltration rate (m s-1) scalarInfiltration => flux_data%var(iLookFLUX%scalarInfiltration)%dat(1) ,& ! intent(out): [dp] infiltration of water into the soil profile (m s-1) - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(inout): [dp] fraction of liquid water on vegetation (-) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(inout): [dp(:)] fraction of liquid water in each snow layer (-) ! boundary fluxes in the soil domain scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(out): [dp] rain that reaches the ground without ever touching the canopy (kg m-2 s-1) scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(out): [dp] drainage of liquid water from the vegetation canopy (kg m-2 s-1) scalarRainPlusMelt => flux_data%var(iLookFLUX%scalarRainPlusMelt)%dat(1) ,& ! intent(out): [dp] rain plus melt (m s-1) scalarSurfaceRunoff => flux_data%var(iLookFLUX%scalarSurfaceRunoff)%dat(1) ,& ! intent(out): [dp] surface runoff (m s-1) - scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1) ,& ! intent(out): [dp] exfiltration from the soil profile (m s-1) - mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat ,& ! intent(out): [dp(:)] column outflow from each soil layer (m3 s-1) ! fluxes for the aquifer scalarAquiferTranspire => flux_data%var(iLookFLUX%scalarAquiferTranspire)%dat(1) ,& ! intent(out): [dp] transpiration loss from the aquifer (m s-1 scalarAquiferRecharge => flux_data%var(iLookFLUX%scalarAquiferRecharge)%dat(1) ,& ! intent(out): [dp] recharge to the aquifer (m s-1) @@ -734,13 +619,11 @@ subroutine subTools(op,sub) ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(out): [dp(:)] derivative in vertical liquid water flux at layer interfaces ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below dq_dHydStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dHydStateLayerSurfVec )%dat ,& ! intent(out): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers mLayerdTheta_dPsi => deriv_data%var(iLookDERIV%mLayerdTheta_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. psi mLayerdPsi_dTheta => deriv_data%var(iLookDERIV%mLayerdPsi_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. theta - dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in compressibility w.r.t matric head ! derivative in baseflow flux w.r.t. aquifer storage dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] derivative in baseflow flux w.r.t. aquifer storage (s-1) ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables @@ -858,7 +741,6 @@ subroutine subTools(op,sub) dNrgFlux_dWatBelow =out_ssdNrgFlux % dNrgFlux_dWatBelow ! intent(out): derivatives in the flux w.r.t. water state in the layer below (J m-2 s-1 K-1) err =out_ssdNrgFlux % err ! intent(out): error code cmessage =out_ssdNrgFlux % cmessage ! intent(out): error message - !deallocate(in_ssdNrgFlux,io_ssdNrgFlux,out_ssdNrgFlux) -- update ! error control if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! calculate net energy fluxes for each snow and soil layer (J m-3 s-1) From 6026da86cd01fdc1a3993703e84522a8d82bf4c9 Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 15 Sep 2023 06:31:17 -0600 Subject: [PATCH 0926/1472] Additional shortening of computFlux.f90. --- build/source/engine/computFlux.f90 | 160 ++++++++--------------------- 1 file changed, 43 insertions(+), 117 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 7fd174e7e..48b27efab 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -30,25 +30,13 @@ module computFlux_module var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (rkind) model_options, & ! defines the model decisions - in_type_vegNrgFlux, & ! intent(in) arguments for vegNrgFlux call - out_type_vegNrgFlux,& ! intent(out) arguments for vegNrgFlux call - in_type_ssdNrgFlux, & ! intent(in) arguments for ssdNrgFlux call - io_type_ssdNrgFlux, & ! intent(inout) arguments for ssdNrgFlux call - out_type_ssdNrgFlux,& ! intent(out) arguments for ssdNrgFlux call - in_type_vegLiqFlux, & ! intent(in) arguments for vegLiqFlux call - out_type_vegLiqFlux,& ! intent(out) arguments for vegLiqFlux call - in_type_snowLiqFlx, & ! intent(in) arguments for snowLiqFlx call - io_type_snowLiqFlx, & ! intent(inout) arguments for snowLiqFlx call - out_type_snowLiqFlx,& ! intent(out) arguments for snowLiqFlx call - in_type_soilLiqFlx, & ! intent(in) arguments for soilLiqFlx call - io_type_soilLiqFlx, & ! intent(inout) arguments for soilLiqFlx call - out_type_soilLiqFlx,& ! intent(out) arguments for soilLiqFlx call - in_type_groundwatr, & ! intent(in) arguments for groundwatr call - io_type_groundwatr, & ! intent(inout) arguments for groundwatr call - out_type_groundwatr,& ! intent(out) arguments for groundwatr call - in_type_bigAquifer, & ! intent(in) arguments for bigAquifer call - io_type_bigAquifer, & ! intent(inout) arguments for bigAquifer call - out_type_bigAquifer ! intent(out) arguments for bigAquifer call + in_type_vegNrgFlux,out_type_vegNrgFlux, & ! arguments for vegNrgFlux call + in_type_ssdNrgFlux,io_type_ssdNrgFlux,out_type_ssdNrgFlux,& ! arguments for ssdNrgFlux call + in_type_vegLiqFlux,out_type_vegLiqFlux, & ! arguments for vegLiqFlux call + in_type_snowLiqFlx,io_type_snowLiqFlx,out_type_snowLiqFlx,& ! arguments for snowLiqFlx call + in_type_soilLiqFlx,io_type_soilLiqFlx,out_type_soilLiqFlx,& ! arguments for soilLiqFlx call + in_type_groundwatr,io_type_groundwatr,out_type_groundwatr,& ! arguments for groundwatr call + in_type_bigAquifer,io_type_bigAquifer,out_type_bigAquifer ! arguments for bigAquifer call ! indices that define elements of the data structures USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure @@ -232,25 +220,15 @@ subroutine computFlux(& real(rkind) :: above_soilLiqFluxDeriv ! derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water real(rkind) :: above_soildLiq_dTk ! derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature real(rkind) :: above_soilFracLiq ! fraction of liquid water layer above soil (canopy or snow) (-) - type(in_type_vegNrgFlux) :: in_vegNrgFlux ! data structure for intent(in) vegNrgFlux arguments - type(out_type_vegNrgFlux) :: out_vegNrgFlux ! data structure for intent(out) vegNrgFlux arguments - type(in_type_ssdNrgFlux) :: in_ssdNrgFlux ! data structure for intent(in) ssdNrgFlux arguments - type(io_type_ssdNrgFlux) :: io_ssdNrgFlux ! data structure for intent(inout) ssdNrgFlux arguments - type(out_type_ssdNrgFlux) :: out_ssdNrgFlux ! data structure for intent(out) ssdNrgFlux arguments - type(in_type_vegLiqFlux) :: in_vegLiqFlux ! data structure for intent(in) vegLiqFlux arguments - type(out_type_vegLiqFlux) :: out_vegLiqFlux ! data structure for intent(out) vegLiqFlux arguments - type(in_type_snowLiqFlx) :: in_snowLiqFlx ! data structure for intent(in) snowLiqFlx arguments - type(io_type_snowLiqFlx) :: io_snowLiqFlx ! data structure for intent(inout) snowLiqFlx arguments - type(out_type_snowLiqFlx) :: out_snowLiqFlx ! data structure for intent(out) snowLiqFlx arguments - type(in_type_soilLiqFlx) :: in_soilLiqFlx ! data structure for intent(in) soilLiqFlx arguments - type(io_type_soilLiqFlx) :: io_soilLiqFlx ! data structure for intent(inout) soilLiqFlx arguments - type(out_type_soilLiqFlx) :: out_soilLiqFlx ! data structure for intent(out) soilLiqFlx arguments - type(in_type_groundwatr) :: in_groundwatr ! data structure for intent(in) groundwatr arguments - type(io_type_groundwatr) :: io_groundwatr ! data structure for intent(inout) groundwatr arguments - type(out_type_groundwatr) :: out_groundwatr ! data structure for intent(out) groundwatr arguments - type(in_type_bigAquifer) :: in_bigAquifer ! data structure for intent(in) bigAquifer arguments - type(io_type_bigAquifer) :: io_bigAquifer ! data structure for intent(inout) bigAquifer arguments - type(out_type_bigAquifer) :: out_bigAquifer ! data structure for intent(out) bigAquifer arguments + ! data structures for flux subroutine arguments (derived types defined in data_types module) + ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** + type(in_type_vegNrgFlux) :: in_vegNrgFlux; type(out_type_vegNrgFlux) :: out_vegNrgFlux ! vegNrgFlux arguments + type(in_type_ssdNrgFlux) :: in_ssdNrgFlux; type(io_type_ssdNrgFlux) :: io_ssdNrgFlux; type(out_type_ssdNrgFlux) :: out_ssdNrgFlux ! ssdNrgFlux arguments + type(in_type_vegLiqFlux) :: in_vegLiqFlux; type(out_type_vegLiqFlux) :: out_vegLiqFlux ! vegLiqFlux arguments + type(in_type_snowLiqFlx) :: in_snowLiqFlx; type(io_type_snowLiqFlx) :: io_snowLiqFlx; type(out_type_snowLiqFlx) :: out_snowLiqFlx ! snowLiqFlx arguments + type(in_type_soilLiqFlx) :: in_soilLiqFlx; type(io_type_soilLiqFlx) :: io_soilLiqFlx; type(out_type_soilLiqFlx) :: out_soilLiqFlx ! soilLiqFlx arguments + type(in_type_groundwatr) :: in_groundwatr; type(io_type_groundwatr) :: io_groundwatr; type(out_type_groundwatr) :: out_groundwatr ! groundwatr arguments + type(in_type_bigAquifer) :: in_bigAquifer; type(io_type_bigAquifer) :: io_bigAquifer; type(out_type_bigAquifer) :: out_bigAquifer ! bigAquifer arguments ! -------------------------------------------------------------- ! initialize error control err=0; message='computFlux/' @@ -340,62 +318,40 @@ subroutine computFlux(& iLayerLiqFluxSoil(0:nSoil) = 0._rkind end if - ! ***** - ! * CALCULATE ENERGY FLUXES OVER VEGETATION... - ! ********************************************* - + ! *** CALCULATE ENERGY FLUXES OVER VEGETATION *** ! identify the need to calculate the energy flux over vegetation doVegNrgFlux = (ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing .or. ixTopNrg/=integerMissing) - - if (doVegNrgFlux) then ! check if there is a need to calculate the energy fluxes over vegetation - ! calculate the energy fluxes over vegetation + if (doVegNrgFlux) then ! if necessary, calculate the energy fluxes over vegetation call subTools(iLookOP%pre,iLookROUTINE%vegNrgFlux) ! pre-processing for call to vegNrgFlux call vegNrgFlux(in_vegNrgFlux,type_data,forc_data,mpar_data,indx_data,prog_data,diag_data,flux_data,bvar_data,model_decisions,out_vegNrgFlux) - call subTools(iLookOP%post,iLookROUTINE%vegNrgFlux) ! post-processing for call to vegNrgFlux + call subTools(iLookOP%post,iLookROUTINE%vegNrgFlux) ! post-processing for call to vegNrgFlux end if ! end if calculating the energy fluxes over vegetation - ! ***** - ! * CALCULATE ENERGY FLUXES THROUGH THE SNOW-SOIL DOMAIN... - ! ********************************************************** - - ! check the need to compute energy fluxes throughout the snow+soil domain - if (nSnowSoilNrg>0) then - ! calculate energy fluxes at layer interfaces through the snow and soil domain + ! *** CALCULATE ENERGY FLUXES THROUGH THE SNOW-SOIL DOMAIN *** + if (nSnowSoilNrg>0) then ! if necessary, calculate energy fluxes at layer interfaces through the snow and soil domain call subTools(iLookOP%pre,iLookROUTINE%ssdNrgFlux) ! pre-processing for call to ssdNrgFlux call ssdNrgFlux(in_ssdNrgFlux,mpar_data,indx_data,prog_data,diag_data,flux_data,io_ssdNrgFlux,out_ssdNrgFlux) call subTools(iLookOP%post,iLookROUTINE%ssdNrgFlux) ! post-processing for call to ssdNrgFlux end if ! end if computing energy fluxes throughout the snow+soil domain - ! ***** - ! * CALCULATE THE LIQUID FLUX THROUGH VEGETATION... - ! ************************************************** - - ! check the need to compute the liquid water fluxes through vegetation - if (ixVegHyd/=integerMissing) then - ! calculate liquid water fluxes through vegetation + ! *** CALCULATE THE LIQUID FLUX THROUGH VEGETATION *** + if (ixVegHyd/=integerMissing) then ! if necessary, calculate liquid water fluxes through vegetation call subTools(iLookOP%pre,iLookROUTINE%vegLiqFlux) ! pre-processing for call to vegLiqFlux call vegLiqFlux(in_vegLiqFlux,mpar_data,diag_data,out_vegLiqFlux) - call subTools(iLookOP%post,iLookROUTINE%vegLiqFlux) ! post-processing for call to vegLiqFlux + call subTools(iLookOP%post,iLookROUTINE%vegLiqFlux) ! post-processing for call to vegLiqFlux end if ! end if computing the liquid water fluxes through vegetation - ! ***** - ! * CALCULATE THE LIQUID FLUX THROUGH SNOW... - ! ******************************************** - - ! check the need to compute liquid water fluxes through snow - if (nSnowOnlyHyd>0) then - ! compute liquid fluxes through snow + ! *** CALCULATE THE LIQUID FLUX THROUGH SNOW *** + if (nSnowOnlyHyd>0) then ! if necessary, compute liquid fluxes through snow call subTools(iLookOP%pre,iLookROUTINE%snowLiqFlx) ! pre-processing for call to snowLiqFlx call snowLiqFlx(in_snowLiqFlx,indx_data,mpar_data,prog_data,diag_data,io_snowLiqFlx,out_snowLiqFlx) - call subTools(iLookOP%post,iLookROUTINE%snowLiqFlx) ! post-processing for call to snowLiqFlx + call subTools(iLookOP%post,iLookROUTINE%snowLiqFlx) ! post-processing for call to snowLiqFlx else - ! define forcing for the soil domain for the case of no snow layers ! NOTE: in case where nSnowOnlyHyd==0 AND snow layers exist, then scalarRainPlusMelt is taken from the previous flux evaluation if (nSnow==0) then !no snow layers scalarRainPlusMelt = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water & ! liquid flux from the canopy (m s-1) + drainageMeltPond/iden_water ! melt of the snow without a layer (m s-1) - if (ixVegHyd/=integerMissing) then ! save canopy derivatives above_soilLiqFluxDeriv = scalarCanopyLiqDeriv/iden_water ! derivative in (throughfall + drainage) w.r.t. canopy liquid water @@ -411,46 +367,29 @@ subroutine computFlux(& above_soildLiq_dTk = mLayerdTheta_dTk(nSnow) ! derivative in volumetric liquid water content in bottom snow layer w.r.t. temperature above_soilFracLiq = mLayerFracLiqSnow(nSnow) ! fraction of liquid water in bottom snow layer (-) end if ! snow layers or not - end if ! if calculating the liquid flux through snow - ! ***** - ! * CALCULATE THE LIQUID FLUX THROUGH SOIL... - ! ******************************************** - - ! check the need to calculate the liquid flux through soil - if (nSoilOnlyHyd>0) then - ! calculate the liquid flux through soil + ! *** CALCULATE THE LIQUID FLUX THROUGH SOIL *** + if (nSoilOnlyHyd>0) then ! if necessary, calculate the liquid flux through soil call subTools(iLookOP%pre,iLookROUTINE%soilLiqFlx) ! pre-processing for call to soilLiqFlx call soilLiqFlx(in_soilLiqFlx,mpar_data,indx_data,prog_data,diag_data,flux_data,io_soilLiqFlx,out_soilLiqFlx) call subTools(iLookOP%post,iLookROUTINE%soilLiqFlx) ! post-processing for call to soilLiqFlx end if ! end if calculating the liquid flux through soil - ! ***** - ! * CALCULATE THE GROUNDWATER FLOW... - ! ************************************ - - ! check if computing soil hydrology - if (nSoilOnlyHyd>0) then - ! set baseflow fluxes to zero if the topmodel baseflow routine is not used - if (local_ixGroundwater/=qbaseTopmodel) then + ! *** CALCULATE THE GROUNDWATER FLOW *** + if (nSoilOnlyHyd>0) then ! check if computing soil hydrology + if (local_ixGroundwater/=qbaseTopmodel) then ! set baseflow fluxes to zero if the topmodel baseflow routine is not used ! diagnostic variables in the data structures scalarExfiltration = 0._rkind ! exfiltration from the soil profile (m s-1) mLayerColumnOutflow(:) = 0._rkind ! column outflow from each soil layer (m3 s-1) ! variables needed for the numerical solution mLayerBaseflow(:) = 0._rkind ! baseflow from each soil layer (m s-1) - - ! topmodel-ish shallow groundwater - else ! local_ixGroundwater==qbaseTopmodel - ! compute the baseflow flux + else ! compute the baseflow flux for topmodel-ish shallow groundwater call subTools(iLookOP%pre,iLookROUTINE%groundwatr) ! pre-processing for call to groundwatr call groundwatr(in_groundwatr,attr_data,mpar_data,prog_data,diag_data,flux_data,io_groundwatr,out_groundwatr) call subTools(iLookOP%post,iLookROUTINE%groundwatr) ! post-processing for call to groundwatr end if ! computing baseflow flux - - ! compute total baseflow from the soil zone (needed for mass balance checks) - scalarSoilBaseflow = sum(mLayerBaseflow) - + scalarSoilBaseflow = sum(mLayerBaseflow) ! compute total baseflow from the soil zone (needed for mass balance checks) ! compute total runoff ! (Note: scalarSoilBaseflow is zero if topmodel is not used) ! (Note: scalarSoilBaseflow may need to re-envisioned in topmodel formulation if parts of it flow into neighboring soil rather than exfiltrate) @@ -458,46 +397,34 @@ subroutine computFlux(& end if ! end if computing soil hydrology - ! ***** - ! (7) CALCULATE FLUXES FOR THE DEEP AQUIFER... - ! ******************************************** - - ! check if computing aquifer fluxes - if (ixAqWat/=integerMissing) then - if (local_ixGroundwater==bigBucket) then ! identify modeling decision - ! compute fluxes for the big bucket + ! *** CALCULATE FLUXES FOR THE DEEP AQUIFER *** + if (ixAqWat/=integerMissing) then ! check if computing aquifer fluxes + if (local_ixGroundwater==bigBucket) then ! compute fluxes for the big bucket call subTools(iLookOP%pre,iLookROUTINE%bigAquifer) ! pre-processing for call to bigAquifer call bigAquifer(in_bigAquifer,mpar_data,diag_data,io_bigAquifer,out_bigAquifer) call subTools(iLookOP%post,iLookROUTINE%bigAquifer) ! post-processing for call to bigAquifer - ! if no aquifer, then fluxes are zero else ! if no aquifer, then fluxes are zero scalarAquiferTranspire = 0._rkind ! transpiration loss from the aquifer (m s-1) scalarAquiferRecharge = 0._rkind ! recharge to the aquifer (m s-1) scalarAquiferBaseflow = 0._rkind ! total baseflow from the aquifer (m s-1) dBaseflow_dAquifer = 0._rkind ! change in baseflow flux w.r.t. aquifer storage (s-1) - end if ! end if aquifer exists + end if ! end check aquifer model decision end if ! if computing aquifer fluxes - ! ***** - ! (X) WRAP UP... - ! ************* - + ! *** WRAP UP *** ! define model flux vector for the vegetation sub-domain if (ixCasNrg/=integerMissing) fluxVec(ixCasNrg) = scalarCanairNetNrgFlux/canopyDepth if (ixVegNrg/=integerMissing) fluxVec(ixVegNrg) = scalarCanopyNetNrgFlux/canopyDepth if (ixVegHyd/=integerMissing) fluxVec(ixVegHyd) = scalarCanopyNetLiqFlux ! NOTE: solid fluxes are handled separately - - ! populate the flux vector for energy - if (nSnowSoilNrg>0) then + if (nSnowSoilNrg>0) then ! if necessary, populate the flux vector for energy do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! loop through non-missing energy state variables in the snow+soil domain fluxVec( ixSnowSoilNrg(iLayer) ) = mLayerNrgFlux(iLayer) - end do ! looping through non-missing energy state variables in the snow+soil domain + end do end if - ! populate the flux vector for hydrology ! NOTE: ixVolFracWat and ixVolFracLiq can also include states in the soil domain, hence enable primary variable switching if (nSnowSoilHyd>0) then ! check if any hydrology states exist - do iLayer=1,nLayers + do iLayer=1,nLayers ! loop through non-missing energy state variables in the snow+soil domain if (ixSnowSoilHyd(iLayer)/=integerMissing) then ! check if a given hydrology state exists select case(layerType(iLayer)) case(iname_snow); fluxVec(ixSnowSoilHyd(iLayer)) = mLayerLiqFluxSnow(iLayer) @@ -505,9 +432,8 @@ subroutine computFlux(& case default; err=20; message=trim(message)//'expect layerType to be either iname_snow or iname_soil'; return end select end if ! end if a given hydrology state exists - end do ! end looping through non-missing energy state variables in the snow+soil domain + end do end if ! end if any hydrology states exist - ! compute the flux vector for the aquifer if (ixAqWat/=integerMissing) fluxVec(ixAqWat) = scalarAquiferTranspire + scalarAquiferRecharge - scalarAquiferBaseflow From 2d3aa4970edef4ac99894462974a17c20ea70ee8 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 18 Sep 2023 12:35:00 +0900 Subject: [PATCH 0927/1472] fix statistics utilities so runs parallel --- utils/timeseries_to_statistics.py | 51 ++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index eee77dcf0..7f08848bf 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -35,7 +35,8 @@ # The first input argument specifies the run where the files are method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) -des_dir = top_fold + 'statistics' +des_dir = top_fold + 'statistics_temp' +fnl_dir = top_fold + 'statistics' src_dir = top_fold + 'summa-' + method_name ben_dir = top_fold + 'summa-' + bench_name src_pat = 'run1_G*_timestep.nc' @@ -47,18 +48,34 @@ # Make sure we're dealing with the right kind of inputs src_dir = Path(src_dir) +fnl_dir = Path(fnl_dir) ben_dir = Path(ben_dir) des_dir = Path(des_dir) # Ensure the output path exists des_dir.mkdir(parents=True, exist_ok=True) +# Construct the path to the processed_files.txt file +processed_files_path = os.path.join(des_dir, 'processed_files.txt') + # Get the names of all inputs, assuming folders have same splits of domains and same file names src_files = glob.glob(str( src_dir / src_pat )) src_files.sort() ben_files = glob.glob(str( ben_dir / src_pat )) ben_files.sort() +# Load the list of files that have already been processed +if os.path.exists(processed_files_path): + with open(processed_files_path, 'r') as f: + processed_files = f.read().splitlines() +else: + processed_files = [] + + +# Filter out the files that have already been processed +src_files = [f for f in src_files if f not in processed_files] +ben_files = [f for f in ben_files if f not in processed_files] + # definitions for KGE computation def covariance(x,y,dims=None): return xr.dot(x-x.mean(dims), y-y.mean(dims), dims=dims) / x.count(dims) @@ -81,7 +98,7 @@ def correlation(x,y,dims=None): # print('Error opening file:', file, bench) # -- functions -def run_loop(file,bench): +def run_loop(file,bench,processed_files_path): # extract the subset IDs subset = file.split('/')[-1].split('_')[1] @@ -155,7 +172,19 @@ def run_loop(file,bench): new = xr.merge([mean,amax,rmse,maxe,kgem]) new.to_netcdf(des_dir / des_fil.format(var,subset)) - print("wrote output: %s" % (top_fold + 'statistics/' +subset)) + # write the name of the processed file to the file list, acquire the lock before opening the file + if testing: + with open(processed_files_path, 'a') as filew: + filew.write(file + '\n') + filew.write(bench + '\n') + else: + import multiprocessing as mp + lock = mp.Lock() + with lock: + with open(processed_files_path, 'a') as filew: + filew.write(file + '\n') + filew.write(bench + '\n') + filew.close() # close the file after writing to it return #nothing @@ -181,21 +210,29 @@ def merge_subsets_into_one(src,pattern,des,name): if testing: # -- no parallel processing for (file, bench) in zip(src_files,ben_files): - run_loop(file, bench) + run_loop(file,bench,processed_files_path) + # -- end no parallel processing + else: # -- start parallel processing ncpus = int(os.environ.get('SLURM_CPUS_PER_TASK',default=1)) if __name__ == "__main__": import multiprocessing as mp pool = mp.Pool(processes=ncpus) - results = [pool.apply_async(run_loop, args=(file, bench)) for (file, bench) in zip(src_files, ben_files)] - dojob = [p.get() for p in results] + with open(processed_files_path, 'a') as f: + results = [pool.apply_async(run_loop, args=(file,bench,processed_files_path)) for (file, bench) in zip(src_files, ben_files)] + for r in results: + try: + r.get() + except Exception as e: + print(f"Error processing file: {e}") + raise e pool.close() # -- end parallel processing # merge the individual files into one for further vizualization -merge_subsets_into_one(des_dir,des_fil.replace('{}','*'),des_dir,viz_fil) +merge_subsets_into_one(des_dir,des_fil.replace('{}','*'),fnl_dir,viz_fil) # remove the individual files for cleanliness for file in glob.glob(str(des_dir / des_fil.replace('{}','*'))): From 3c2cf715a7df7fab540e1b06facb5e618b1c2a99 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 18 Sep 2023 13:01:28 +0900 Subject: [PATCH 0928/1472] typo --- utils/timeseries_to_statistics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 7f08848bf..36ef75c81 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -225,7 +225,7 @@ def merge_subsets_into_one(src,pattern,des,name): try: r.get() except Exception as e: - print(f"Error processing file: {e}") + print("Error processing file: {e}") raise e pool.close() # -- end parallel processing From a614278a6ada207302fa77302fc56b417e4e4837 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 20 Sep 2023 14:52:38 +0900 Subject: [PATCH 0929/1472] change maxLineSearch back to what was --- build/source/engine/summaSolve4numrec.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 6479319a3..36b7bf29b 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -382,7 +382,7 @@ subroutine lineSearchRefinement(doLineSearch,stateVecTrial,newtStepScaled,aJacSc real(rkind) :: xInc(nState) ! iteration increment (re-scaled to original units of the state vector) logical(lgt) :: feasible ! flag to denote the feasibility of the solution integer(i4b) :: iLine ! line search index - integer(i4b),parameter :: maxLineSearch=100 ! maximum number of backtracks + integer(i4b),parameter :: maxLineSearch=5 ! maximum number of backtracks real(rkind),parameter :: alpha=1.e-4_rkind ! check on gradient real(rkind) :: xLambda ! backtrack magnitude real(rkind) :: xLambdaTemp ! temporary backtrack magnitude From f1a90b5b409949077626ef30a7b2d3a6fd423ef5 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 21 Sep 2023 07:34:08 -0600 Subject: [PATCH 0930/1472] Apply object-oriented approach to vegLiqFlux call using class objects. --- build/source/dshare/data_types.f90 | 87 ++++++++++++++++++++++++++---- build/source/engine/computFlux.f90 | 6 +-- build/source/engine/vegLiqFlux.f90 | 4 +- 3 files changed, 81 insertions(+), 16 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index f8d0fddc0..8dfeb49ee 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -23,6 +23,8 @@ MODULE data_types USE nrtype, integerMissing=>nr_integerMissing USE var_lookup,only:maxvarFreq USE var_lookup,only:maxvarStat + USE var_lookup,only:iLookFLUX ! lookup indices for flux data + USE var_lookup,only:iLookDERIV ! lookup indices for derivative data implicit none ! constants necessary for variable defs private @@ -379,19 +381,23 @@ MODULE data_types ! ** end ssdNrgFlux ! ** vegLiqFlux - type, public :: in_type_vegLiqFlux ! derived type for intent(in) arguments in vegLiqFlux call - logical(lgt) :: computeVegFlux ! intent(in): flag to denote if computing energy flux over vegetation - real(rkind) :: scalarCanopyLiqTrial ! intent(in): trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) - real(rkind) :: scalarRainfall ! intent(in): rainfall rate (kg m-2 s-1) + type, public :: in_type_vegLiqFlux ! class for intent(in) arguments in vegLiqFlux call + logical(lgt) :: computeVegFlux ! intent(in): flag to denote if computing energy flux over vegetation + real(rkind) :: scalarCanopyLiqTrial ! intent(in): trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) + real(rkind) :: scalarRainfall ! intent(in): rainfall rate (kg m-2 s-1) + contains + procedure :: initialize => initialize_in_vegLiqFlux end type in_type_vegLiqFlux - type, public :: out_type_vegLiqFlux ! derived type for intent(out) arguments in vegLiqFlux call - real(rkind) :: scalarThroughfallRain ! intent(out): rain that reaches the ground without ever touching the canopy (kg m-2 s-1) - real(rkind) :: scalarCanopyLiqDrainage ! intent(out): drainage of liquid water from the vegetation canopy (kg m-2 s-1) - real(rkind) :: scalarThroughfallRainDeriv ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) - real(rkind) :: scalarCanopyLiqDrainageDeriv ! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) - integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + type, public :: out_type_vegLiqFlux ! class for intent(out) arguments in vegLiqFlux call + real(rkind) :: scalarThroughfallRain ! intent(out): rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + real(rkind) :: scalarCanopyLiqDrainage ! intent(out): drainage of liquid water from the vegetation canopy (kg m-2 s-1) + real(rkind) :: scalarThroughfallRainDeriv ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) + real(rkind) :: scalarCanopyLiqDrainageDeriv ! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) + integer(i4b) :: err ! intent(out): error code + character(:),allocatable :: cmessage ! intent(out): error message + contains + procedure :: finalize => finalize_out_vegLiqFlux end type out_type_vegLiqFlux ! ** end vegLiqFlux @@ -522,4 +528,63 @@ MODULE data_types character(:),allocatable :: cmessage ! intent(out): error message end type out_type_bigAquifer ! ** end bigAquifer + +contains + + subroutine initialize_in_vegLiqFlux(in_vegLiqFlux,computeVegFlux,scalarCanopyLiqTrial,flux_data) ! SJT + class(in_type_vegLiqFlux),intent(out) :: in_vegLiqFlux ! class object for intent(in) vegLiqFlux arguments + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + real(rkind),intent(in) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU + associate(scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1)) ! intent(in): [dp] rainfall rate (kg m-2 s-1) + ! intent(in) arguments + in_vegLiqFlux % computeVegFlux =computeVegFlux ! intent(in): flag to denote if computing energy flux over vegetation + in_vegLiqFlux % scalarCanopyLiqTrial=scalarCanopyLiqTrial ! intent(in): trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) + in_vegLiqFlux % scalarRainfall =scalarRainfall ! intent(in): rainfall rate (kg m-2 s-1) + end associate + end subroutine initialize_in_vegLiqFlux + + subroutine finalize_out_vegLiqFlux(out_vegLiqFlux,globalPrintFlag,scalarCanopyLiqTrial,flux_data,deriv_data,message,err,cmessage) + class(out_type_vegLiqFlux),intent(in) :: out_vegLiqFlux ! class object for intent(out) vegLiqFlux arguments + logical(lgt),intent(in) :: globalPrintFlag ! global print flag for debug output + real(rkind),intent(in) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + character(*),intent(inout) :: message ! computFlux error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: cmessage ! error message from vegLiqFlux + associate( & + scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1), & ! intent(out): [dp] rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1), & ! intent(out): [dp] drainage of liquid water from the vegetation canopy (kg m-2 s-1) + scalarThroughfallRainDeriv => deriv_data%var(iLookDERIV%scalarThroughfallRainDeriv )%dat(1),& ! intent(out): [dp] derivative in throughfall w.r.t. canopy liquid water + scalarCanopyLiqDrainageDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDrainageDeriv)%dat(1),& ! intent(out): [dp] derivative in canopy drainage w.r.t. canopy liquid water + scalarCanopyNetLiqFlux => flux_data%var(iLookFLUX%scalarCanopyNetLiqFlux)%dat(1), & ! intent(out): [dp] net liquid water flux for the vegetation canopy (kg m-2 s-1) + scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1), & ! intent(in): [dp] rainfall rate (kg m-2 s-1) + scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1), & ! intent(out): [dp] canopy evaporation/condensation (kg m-2 s-1) + scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ) ! intent(out): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water + ! intent(out) arguments + scalarThroughfallRain =out_vegLiqFlux % scalarThroughfallRain ! intent(out): rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + scalarCanopyLiqDrainage =out_vegLiqFlux % scalarCanopyLiqDrainage ! intent(out): drainage of liquid water from the vegetation canopy (kg m-2 s-1) + scalarThroughfallRainDeriv =out_vegLiqFlux % scalarThroughfallRainDeriv ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) + scalarCanopyLiqDrainageDeriv=out_vegLiqFlux % scalarCanopyLiqDrainageDeriv! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) + err =out_vegLiqFlux % err ! intent(out): error code + cmessage =out_vegLiqFlux % cmessage ! intent(out): error control + ! error control + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + ! calculate the net liquid water flux for the vegetation canopy + scalarCanopyNetLiqFlux = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + ! calculate the total derivative in the downward liquid flux + scalarCanopyLiqDeriv = scalarThroughfallRainDeriv + scalarCanopyLiqDrainageDeriv + ! test + if (globalPrintFlag) then + print*, '**' + print*, 'scalarRainfall = ', scalarRainfall + print*, 'scalarThroughfallRain = ', scalarThroughfallRain + print*, 'scalarCanopyEvaporation = ', scalarCanopyEvaporation + print*, 'scalarCanopyLiqDrainage = ', scalarCanopyLiqDrainage + print*, 'scalarCanopyNetLiqFlux = ', scalarCanopyNetLiqFlux + print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial + end if + end associate + end subroutine finalize_out_vegLiqFlux END MODULE data_types diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 48b27efab..eb29e71e9 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -107,6 +107,7 @@ module computFlux_module public::computFlux public::soilCmpres public::soilCmpresPrime + contains ! ********************************************************************************************************* ! public subroutine computFlux: compute model fluxes @@ -336,9 +337,9 @@ subroutine computFlux(& ! *** CALCULATE THE LIQUID FLUX THROUGH VEGETATION *** if (ixVegHyd/=integerMissing) then ! if necessary, calculate liquid water fluxes through vegetation - call subTools(iLookOP%pre,iLookROUTINE%vegLiqFlux) ! pre-processing for call to vegLiqFlux + call in_vegLiqFlux%initialize(computeVegFlux,scalarCanopyLiqTrial,flux_data) call vegLiqFlux(in_vegLiqFlux,mpar_data,diag_data,out_vegLiqFlux) - call subTools(iLookOP%post,iLookROUTINE%vegLiqFlux) ! post-processing for call to vegLiqFlux + call out_vegLiqFlux%finalize(globalPrintFlag,scalarCanopyLiqTrial,flux_data,deriv_data,message,err,cmessage) end if ! end if computing the liquid water fluxes through vegetation ! *** CALCULATE THE LIQUID FLUX THROUGH SNOW *** @@ -919,7 +920,6 @@ end subroutine subTools end subroutine computFlux - ! ********************************************************************************************************** ! public subroutine soilCmpres: compute soil compressibility (-) and its derivative w.r.t matric head (m-1) ! ********************************************************************************************************** diff --git a/build/source/engine/vegLiqFlux.f90 b/build/source/engine/vegLiqFlux.f90 index 7e7de362c..a8d58dbf5 100644 --- a/build/source/engine/vegLiqFlux.f90 +++ b/build/source/engine/vegLiqFlux.f90 @@ -26,8 +26,8 @@ module vegLiqFlux_module ! data types USE data_types,only:var_d ! x%var(:) (rkind) USE data_types,only:var_dlength ! x%var(:)%dat (rkind) -USE data_types,only:in_type_vegLiqFlux ! derived type for intent(in) arguments -USE data_types,only:out_type_vegLiqFlux ! derived type for intent(out) arguments +USE data_types,only:in_type_vegLiqFlux ! class type for intent(in) arguments +USE data_types,only:out_type_vegLiqFlux ! class type for intent(out) arguments ! named variables USE var_lookup,only:iLookPARAM,iLookDIAG ! named variables for structure elements From ed3ee4dd67f85923eb5d651708c2fcfec1cb892d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 22 Sep 2023 12:53:51 +0900 Subject: [PATCH 0931/1472] utilities error --- utils/timeseries_to_statistics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 36ef75c81..7f08848bf 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -225,7 +225,7 @@ def merge_subsets_into_one(src,pattern,des,name): try: r.get() except Exception as e: - print("Error processing file: {e}") + print(f"Error processing file: {e}") raise e pool.close() # -- end parallel processing From aa93a3084d3ce29c2ac5c7ef2a144c1c22092345 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 23 Sep 2023 08:01:12 -0600 Subject: [PATCH 0932/1472] Applied object-oriented methods to simplify snowLiqFlx call. --- build/source/dshare/data_types.f90 | 91 +++++++++++++++++++++++++----- build/source/engine/computFlux.f90 | 38 ++++++++++++- 2 files changed, 114 insertions(+), 15 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 8dfeb49ee..85ec0e401 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -402,23 +402,30 @@ MODULE data_types ! ** end vegLiqFlux ! ** snowLiqFlx - type, public :: in_type_snowLiqFlx ! derived type for intent(in) arguments in snowLiqFlx call - integer(i4b) :: nSnow ! intent(in): number of snow layers - logical(lgt) :: firstFluxCall ! intent(in): the first flux call (compute variables that are constant over the iterations) - logical(lgt) :: scalarSolution ! intent(in): flag to indicate the scalar solution - real(rkind) :: scalarThroughfallRain ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) - real(rkind) :: scalarCanopyLiqDrainage ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) - real(rkind), allocatable :: mLayerVolFracLiqTrial(:) ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) + type, public :: in_type_snowLiqFlx ! class for intent(in) arguments in snowLiqFlx call + integer(i4b) :: nSnow ! intent(in): number of snow layers + logical(lgt) :: firstFluxCall ! intent(in): the first flux call (compute variables that are constant over the iterations) + logical(lgt) :: scalarSolution ! intent(in): flag to indicate the scalar solution + real(rkind) :: scalarThroughfallRain ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) + real(rkind) :: scalarCanopyLiqDrainage ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) + real(rkind), allocatable :: mLayerVolFracLiqTrial(:) ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) + contains + procedure :: initialize => initialize_in_snowLiqFlx end type in_type_snowLiqFlx - type, public :: io_type_snowLiqFlx ! derived type for intent(inout) arguments in snowLiqFlx call - real(rkind), allocatable :: iLayerLiqFluxSnow(:) ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) - real(rkind), allocatable :: iLayerLiqFluxSnowDeriv(:) ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) + type, public :: io_type_snowLiqFlx ! class for intent(inout) arguments in snowLiqFlx call + real(rkind), allocatable :: iLayerLiqFluxSnow(:) ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) + real(rkind), allocatable :: iLayerLiqFluxSnowDeriv(:) ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) + contains + procedure :: initialize => initialize_io_snowLiqFlx + procedure :: finalize => finalize_io_snowLiqFlx end type io_type_snowLiqFlx type, public :: out_type_snowLiqFlx ! derived type for intent(out) arguments in snowLiqFlx call - integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + integer(i4b) :: err ! intent(out): error code + character(:),allocatable :: cmessage ! intent(out): error message + contains + procedure :: finalize => finalize_out_snowLiqFlx end type out_type_snowLiqFlx ! ** end snowLiqFlx @@ -530,7 +537,8 @@ MODULE data_types ! ** end bigAquifer contains - + + ! ** vegLiqFlux subroutine initialize_in_vegLiqFlux(in_vegLiqFlux,computeVegFlux,scalarCanopyLiqTrial,flux_data) ! SJT class(in_type_vegLiqFlux),intent(out) :: in_vegLiqFlux ! class object for intent(in) vegLiqFlux arguments logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation @@ -587,4 +595,61 @@ subroutine finalize_out_vegLiqFlux(out_vegLiqFlux,globalPrintFlag,scalarCanopyLi end if end associate end subroutine finalize_out_vegLiqFlux + ! ** end vegLiqFlux + + ! ** snowLiqFlx + subroutine initialize_in_snowLiqFlx(in_snowLiqFlx,nSnow,firstFluxCall,scalarSolution,mLayerVolFracLiqTrial,flux_data) + class(in_type_snowLiqFlx),intent(out) :: in_snowLiqFlx ! class object of intent(in) arguments + integer(i4b),intent(in) :: nSnow ! number of snow layers + logical(lgt),intent(in) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) + type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU + associate(& + scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1), & ! intent(out): [dp] rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1)) ! intent(out): [dp] drainage of liquid water from the vegetation canopy (kg m-2 s-1) + ! intent(in) arguments + in_snowLiqFlx % nSnow =nSnow ! intent(in): number of snow layers + in_snowLiqFlx % firstFluxCall =firstFluxCall ! intent(in): the first flux call (compute variables that are constant over the iterations) + in_snowLiqFlx % scalarSolution =(scalarSolution .and. .not.firstFluxCall) ! intent(in): flag to indicate the scalar solution + in_snowLiqFlx % scalarThroughfallRain =scalarThroughfallRain ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) + in_snowLiqFlx % scalarCanopyLiqDrainage=scalarCanopyLiqDrainage ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) + in_snowLiqFlx % mLayerVolFracLiqTrial =mLayerVolFracLiqTrial(1:nSnow) ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) + end associate + end subroutine initialize_in_snowLiqFlx + + subroutine initialize_io_snowLiqFlx(io_snowLiqFlx,flux_data,deriv_data) + class(io_type_snowLiqFlx),intent(out) :: io_snowLiqFlx ! class object for intent(inout) arguments + type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + associate(& + iLayerLiqFluxSnow => flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at snow layer interfaces (-) + iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat) ! intent(out): [dp(:)] derivative in vertical liquid water flux at layer interfaces + io_snowLiqFlx % iLayerLiqFluxSnow =iLayerLiqFluxSnow ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) + io_snowLiqFlx % iLayerLiqFluxSnowDeriv =iLayerLiqFluxSnowDeriv ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) + end associate + end subroutine initialize_io_snowLiqFlx + + subroutine finalize_io_snowLiqFlx(io_snowLiqFlx,flux_data,deriv_data) + class(io_type_snowLiqFlx),intent(in) :: io_snowLiqFlx ! class object for intent(inout) arguments + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + associate(& + iLayerLiqFluxSnow => flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at snow layer interfaces (-) + iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat) ! intent(out): [dp(:)] derivative in vertical liquid water flux at layer interfaces + ! intent(inout) arguments + iLayerLiqFluxSnow =io_snowLiqFlx % iLayerLiqFluxSnow ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) + iLayerLiqFluxSnowDeriv=io_snowLiqFlx % iLayerLiqFluxSnowDeriv ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) + end associate + end subroutine finalize_io_snowLiqFlx + + subroutine finalize_out_snowLiqFlx(out_snowLiqFlx,err,cmessage) + class(out_type_snowLiqFlx),intent(in) :: out_snowLiqFlx ! class object for intent(out) arguments + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: cmessage ! error message from vegLiqFlux + ! intent(out) arguments + err =out_snowLiqFlx % err ! intent(out): error code + cmessage=out_snowLiqFlx % cmessage ! intent(out): error message + end subroutine finalize_out_snowLiqFlx + ! ** end snowLiqFlx END MODULE data_types diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index eb29e71e9..9a8c9a52f 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -344,9 +344,9 @@ subroutine computFlux(& ! *** CALCULATE THE LIQUID FLUX THROUGH SNOW *** if (nSnowOnlyHyd>0) then ! if necessary, compute liquid fluxes through snow - call subTools(iLookOP%pre,iLookROUTINE%snowLiqFlx) ! pre-processing for call to snowLiqFlx + call initialize_snowLiqFlx call snowLiqFlx(in_snowLiqFlx,indx_data,mpar_data,prog_data,diag_data,io_snowLiqFlx,out_snowLiqFlx) - call subTools(iLookOP%post,iLookROUTINE%snowLiqFlx) ! post-processing for call to snowLiqFlx + call finalize_snowLiqFlx else ! define forcing for the soil domain for the case of no snow layers ! NOTE: in case where nSnowOnlyHyd==0 AND snow layers exist, then scalarRainPlusMelt is taken from the previous flux evaluation @@ -718,6 +718,7 @@ subroutine subTools(op,sub) in_snowLiqFlx % scalarThroughfallRain =scalarThroughfallRain ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) in_snowLiqFlx % scalarCanopyLiqDrainage=scalarCanopyLiqDrainage ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) in_snowLiqFlx % mLayerVolFracLiqTrial =mLayerVolFracLiqTrial(1:nSnow) ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) + ! intent(inout) arguments io_snowLiqFlx % iLayerLiqFluxSnow =iLayerLiqFluxSnow ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) io_snowLiqFlx % iLayerLiqFluxSnowDeriv =iLayerLiqFluxSnowDeriv ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) else ! post-processing @@ -918,6 +919,39 @@ subroutine subTools(op,sub) end subroutine subTools + subroutine initialize_snowLiqFlx + call in_snowLiqFlx%initialize(nSnow,firstFluxCall,scalarSolution,mLayerVolFracLiqTrial,flux_data) + call io_snowLiqFlx%initialize(flux_data,deriv_data) + end subroutine initialize_snowLiqFlx + + subroutine finalize_snowLiqFlx + call io_snowLiqFlx%finalize(flux_data,deriv_data) + call out_snowLiqFlx%finalize(err,cmessage) + ! error control + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + associate(& + scalarRainPlusMelt => flux_data%var(iLookFLUX%scalarRainPlusMelt)%dat(1), & ! intent(out): [dp] rain plus melt (m s-1) + mLayerLiqFluxSnow => flux_data%var(iLookFLUX%mLayerLiqFluxSnow)%dat, & ! intent(out): [dp] net liquid water flux for each snow layer (s-1) + iLayerLiqFluxSnow => flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat, & ! intent(out): [dp(0:)] vertical liquid water flux at snow layer interfaces (-) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + scalarSnowDrainage => flux_data%var(iLookFLUX%scalarSnowDrainage)%dat(1), & ! intent(out): [dp] drainage from the snow profile (m s-1) + iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv)%dat,& ! intent(out): [dp(:)] derivative in vertical liquid water flux at layer interfaces + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat, & ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat) ! intent(inout): [dp(:)] fraction of liquid water in each snow layer (-) + ! define forcing for the soil domain + scalarRainPlusMelt = iLayerLiqFluxSnow(nSnow) ! drainage from the base of the snowpack + ! calculate net liquid water fluxes for each snow layer (s-1) + do iLayer=1,nSnow + mLayerLiqFluxSnow(iLayer) = -(iLayerLiqFluxSnow(iLayer) - iLayerLiqFluxSnow(iLayer-1))/mLayerDepth(iLayer) + end do + ! compute drainage from the soil zone (needed for mass balance checks) + scalarSnowDrainage = iLayerLiqFluxSnow(nSnow) + ! save bottom layer of snow derivatives + above_soilLiqFluxDeriv = iLayerLiqFluxSnowDeriv(nSnow) ! derivative in vertical liquid water flux at bottom snow layer interface + above_soildLiq_dTk = mLayerdTheta_dTk(nSnow) ! derivative in volumetric liquid water content in bottom snow layer w.r.t. temperature + above_soilFracLiq = mLayerFracLiqSnow(nSnow) ! fraction of liquid water in bottom snow layer (-) + end associate + end subroutine finalize_snowLiqFlx end subroutine computFlux ! ********************************************************************************************************** From 45a24d2ba39c052a66165baf393e28acb3db650d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 25 Sep 2023 10:36:35 +0900 Subject: [PATCH 0933/1472] update instructions to say using sundials 6.6 --- build/cmake_external/build_cmakeSundials.bash | 2 +- docs/sundials_bmi/installation.txt | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/cmake_external/build_cmakeSundials.bash b/build/cmake_external/build_cmakeSundials.bash index 957347370..5cb765d40 100755 --- a/build/cmake_external/build_cmakeSundials.bash +++ b/build/cmake_external/build_cmakeSundials.bash @@ -6,4 +6,4 @@ # run `make`, then `make install` # Note, using -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF, if want to run examples should change -cmake ../../sundials-6.3.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=../../sundials/instdir -DEXAMPLES_INSTALL_PATH=../../sundials/instdir/examples +cmake ../../sundials-6.6.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=../../sundials/instdir -DEXAMPLES_INSTALL_PATH=../../sundials/instdir/examples diff --git a/docs/sundials_bmi/installation.txt b/docs/sundials_bmi/installation.txt index a6a1cb4d4..ed3226392 100644 --- a/docs/sundials_bmi/installation.txt +++ b/docs/sundials_bmi/installation.txt @@ -27,10 +27,10 @@ cp ../../summa/build/makefiles/build_cmakeBMI build_cmake % make install 6. If you are using Sundials, now install that. Download the latest release of IDA solver from SUNDIALS package in https://computing.llnl.gov/projects/sundials/sundials-software -% wget "https://github.com/LLNL/sundials/releases/download/v6.3.0/sundials-6.3.0.tar.gz" +% wget "https://github.com/LLNL/sundials/releases/download/v6.6.0/sundials-6.6.0.tar.gz" 7. Extract the corresponding compressed file -% tar -xzf sundials-6.3.0.tar.gz +% tar -xzf sundials-6.6.0.tar.gz 8. Read INSTALL_GUIDE.pdf and follow the installation instructions. Roughly, do the following: Make a directory outside the sundials-6.3.0 folder and enter it, and make the install and build dirs, enter the build dir. @@ -42,7 +42,7 @@ Make a directory outside the sundials-6.3.0 folder and enter it, and make the in NOTE if you need to recompile after a system upgrade, delete the contents of these directories before running cmake in the next step! 9. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, the following setting needs to be enabled, as well as the setting the install directory, while running the cmake inside the builddir, using your home directory as $(YOUR_HOME) -% cmake ../sundials-5.8.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON DCMAKE_INSTALL_PREFIX=$(YOUR_HOME)/sundials/instdir -DEXAMPLES_INSTALL_PATH=$(YOUR_HOME)/sundials/instdir/examples +% cmake ../sundials-6.6.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON DCMAKE_INSTALL_PREFIX=$(YOUR_HOME)/sundials/instdir -DEXAMPLES_INSTALL_PATH=$(YOUR_HOME)/sundials/instdir/examples 10. The default compiler is gfortan. To change it (it should be the same as the netcdf build and the later summa build), you could also add the following option to cmake with your $(YOUR_GFORTRAN) -DCMAKE_Fortran_COMPILER=$(YOUR_GFORTRAN) @@ -59,7 +59,7 @@ cp ../../summa/build/makefiles/build_cmakeSundials build_cmake git clone https://git.cs.usask.ca/numerical_simulations_lab/summa.git 13. In SUMMA Makefile, define the FC_EXE to be the same compiler as you used for BMI and netcdf - + 14. In SUMMA Makefile, define the SUNDIALS installation directory (and all the folders as required in the SUMMA instructions. DIR_SUNDIALS=$(YOUR_HOME)/sundials/instdir From c19da3b047055c36f056590149b2b61780cbd507 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 25 Sep 2023 20:39:19 +0900 Subject: [PATCH 0934/1472] plotting utilites --- utils/plot_per_GRU.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index bd154b99c..eda4d7086 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -50,6 +50,7 @@ if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,10e-3] if stat=='maxe': maxes = [20,30,3e-4,2,4e-7,0.2] if stat=='kgem' : maxes = [0.9,0.7,0.9,0.95,0.95,10e-3] +if stat=='mean': maxes = [100,1700,5e-5,8,1e-7,10e-3] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed.png' fig_fil = fig_fil.format(','.join(settings),stat) @@ -188,7 +189,7 @@ def make_default_path(suffix): # Multiply the s values by efficiency s = s*eff_batch - if stat == 'maxe': s = np.fabs(s) # make absolute value norm, max is not not all positive + if stat == 'maxe' or 'mean': s = np.fabs(s) # make absolute value norm, max is not not all positive bas_albers[plot_var] = s.sel(hru=hru_ids_shp.values) # Select lakes of a certain size for plotting @@ -209,10 +210,6 @@ def make_default_path(suffix): else: plt.rcParams.update({'font.size': 100}) -# Flip the evaporation values so that they become positive, not if plotting diffs -#bas_albers['plot_ET'] = bas_albers['scalarTotalET'] * -1 -#bas_albers['plot_ET'] = bas_albers['plot_ET'].where(bas_albers['scalarTotalET'] != -9999, np.nan) - if 'compressed' in fig_fil: fig,axs = plt.subplots(3,2,figsize=(35,33)) else: @@ -231,6 +228,7 @@ def run_loop(i,var,the_max,f_x,f_y): my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap my_cmap.set_bad(color='white') #nan color white vmin,vmax = 0, the_max + if stat=='mean' and var=='scalarTotalSoilWater': vmin,vmax = 600, the_max norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) if stat=='kgem' and var!='wallClockTime': my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno')) # copy the default cmap @@ -254,6 +252,7 @@ def run_loop(i,var,the_max,f_x,f_y): if stat == 'rmse': stat_word = ' Hourly RMSE' if stat == 'maxe': stat_word = ' Hourly max abs error' if stat == 'kgem': stat_word = ' Hourly KGEm' + if stat == 'mean': stat_word = ' Hourly abs mean' # wall Clock doesn't do difference if var == 'wallClockTime': From 052c880a93d373f1b7d6beaf1607f8411702a9d4 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 25 Sep 2023 20:55:46 +0900 Subject: [PATCH 0935/1472] update Ngen test files --- .gitignore | 1 + .../{non_actors => }/celia1990/.gitkeep | 0 .../output/celia1990/celia1990_timestep.nc | Bin 0 -> 497224 bytes .../celia1990/fileManager_celia1990.txt | 20 ++++++++++++++++++ .../summa_fileManager_celia1990.txt | 20 ------------------ .../{common => }/summa_zBasinParamInfo.txt | 0 .../{common => }/summa_zDecisions.txt | 0 .../{common => }/summa_zForcingFileList.txt | 0 .../{common => }/summa_zInitialCond.nc | Bin .../{common => }/summa_zLocalAttributes.nc | Bin .../{common => }/summa_zLocalParamInfo.txt | 4 ---- .../{common => }/summa_zParamTrial.nc | Bin test_ngen/summa-init-celia.namelist.input | 2 +- 13 files changed, 22 insertions(+), 25 deletions(-) rename test_ngen/celia_test/output/{non_actors => }/celia1990/.gitkeep (100%) create mode 100644 test_ngen/celia_test/output/celia1990/celia1990_timestep.nc create mode 100644 test_ngen/celia_test/settings/syntheticTestCases/celia1990/fileManager_celia1990.txt delete mode 100644 test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt rename test_ngen/celia_test/settings/syntheticTestCases/celia1990/{common => }/summa_zBasinParamInfo.txt (100%) rename test_ngen/celia_test/settings/syntheticTestCases/celia1990/{common => }/summa_zDecisions.txt (100%) rename test_ngen/celia_test/settings/syntheticTestCases/celia1990/{common => }/summa_zForcingFileList.txt (100%) rename test_ngen/celia_test/settings/syntheticTestCases/celia1990/{common => }/summa_zInitialCond.nc (100%) rename test_ngen/celia_test/settings/syntheticTestCases/celia1990/{common => }/summa_zLocalAttributes.nc (100%) rename test_ngen/celia_test/settings/syntheticTestCases/celia1990/{common => }/summa_zLocalParamInfo.txt (97%) rename test_ngen/celia_test/settings/syntheticTestCases/celia1990/{common => }/summa_zParamTrial.nc (100%) diff --git a/.gitignore b/.gitignore index 011cfe889..ad29648f5 100755 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,4 @@ site/ Laugh-Tests/ # cmake_build containing cmake build objects build/cmake_build +runinfo.txt diff --git a/test_ngen/celia_test/output/non_actors/celia1990/.gitkeep b/test_ngen/celia_test/output/celia1990/.gitkeep similarity index 100% rename from test_ngen/celia_test/output/non_actors/celia1990/.gitkeep rename to test_ngen/celia_test/output/celia1990/.gitkeep diff --git a/test_ngen/celia_test/output/celia1990/celia1990_timestep.nc b/test_ngen/celia_test/output/celia1990/celia1990_timestep.nc new file mode 100644 index 0000000000000000000000000000000000000000..ccc1832534e9bcd7cfc178c2445fc49d810a4c09 GIT binary patch literal 497224 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' +initConditionFile 'syntheticTestCases/celia1990/summa_zInitialCond.nc' +outFilePrefix 'celia1990' +vegTableFile 'meta/VEGPARM.TBL' +soilTableFile 'meta/SOILPARM.TBL' +generalTableFile 'meta/GENPARM.TBL' +noahmpTableFile 'meta/MPTABLE.TBL' \ No newline at end of file diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt deleted file mode 100644 index 42d36d557..000000000 --- a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt +++ /dev/null @@ -1,20 +0,0 @@ -controlVersion 'SUMMA_FILE_MANAGER_V3.0.0' -simStartTime '2000-01-01 01:00' -simEndTime '2000-01-03 12:00' -tmZoneInfo 'localTime' -settingsPath './extern/summa/summa/test_ngen/celia_test/settings/' -forcingPath './extern/summa/summa/test_ngen/celia_test/input_data/celia1990/' -outputPath './extern/summa/summa/test_ngen/celia_test/output/non_actors/celia1990/' -decisionsFile 'syntheticTestCases/celia1990/common/summa_zDecisions.txt' -outputControlFile 'meta/Model_Output.txt' -globalHruParamFile 'syntheticTestCases/celia1990/common/summa_zLocalParamInfo.txt' -globalGruParamFile 'syntheticTestCases/celia1990/common/summa_zBasinParamInfo.txt' -attributeFile 'syntheticTestCases/celia1990/common/summa_zLocalAttributes.nc' -trialParamFile 'syntheticTestCases/celia1990/common/summa_zParamTrial.nc' -forcingListFile 'syntheticTestCases/celia1990/common/summa_zForcingFileList.txt' -initConditionFile 'syntheticTestCases/celia1990/common/summa_zInitialCond.nc' -outFilePrefix 'celia1990' -vegTableFile 'meta/VEGPARM.TBL' -soilTableFile 'meta/SOILPARM.TBL' -generalTableFile 'meta/GENPARM.TBL' -noahmpTableFile 'meta/MPTABLE.TBL' \ No newline at end of file diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zBasinParamInfo.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zBasinParamInfo.txt similarity index 100% rename from test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zBasinParamInfo.txt rename to test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zBasinParamInfo.txt diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zDecisions.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zDecisions.txt similarity index 100% rename from test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zDecisions.txt rename to test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zDecisions.txt diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zForcingFileList.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zForcingFileList.txt similarity index 100% rename from test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zForcingFileList.txt rename to test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zForcingFileList.txt diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zInitialCond.nc b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zInitialCond.nc similarity index 100% rename from test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zInitialCond.nc rename to test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zInitialCond.nc diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zLocalAttributes.nc b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zLocalAttributes.nc similarity index 100% rename from test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zLocalAttributes.nc rename to test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zLocalAttributes.nc diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zLocalParamInfo.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zLocalParamInfo.txt similarity index 97% rename from test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zLocalParamInfo.txt rename to test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zLocalParamInfo.txt index 088ab0975..bafe0c35a 100644 --- a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zLocalParamInfo.txt +++ b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zLocalParamInfo.txt @@ -190,7 +190,6 @@ soilIceCV | 0.4500 | 0.1000 | 5.0000 minwind | 0.1000 | 0.0010 | 1.0000 minstep | 1.0000 | 1.0000 | 1800.0000 maxstep | 3600.0000 | 60.0000 | 7200.0000 -be_steps | 1.0000 | 1.0000 | 512.0000 wimplicit | 0.0000 | 0.0000 | 1.0000 maxiter | 100.0000 | 1.0000 | 100.0000 relConvTol_liquid | 1.0d-3 | 1.0d-5 | 1.0d-1 @@ -201,8 +200,6 @@ relConvTol_energy | 1.0d-2 | 1.0d-5 | 1.0d-1 absConvTol_energy | 1.0d-0 | 1.0d-2 | 1.0d+1 relConvTol_aquifr | 1.0d-0 | 1.0d-2 | 1.0d+1 absConvTol_aquifr | 1.0d-5 | 1.0d-5 | 1.0d-1 -relErrTol_ida | 1.0d-6 | 1.0d-10| 1.0d-2 -absErrTol_ida | 1.0d-6 | 1.0d-10| 1.0d-2 zmin | 0.0100 | 0.0050 | 0.1000 zmax | 0.0500 | 0.0100 | 0.5000 ! --- @@ -226,4 +223,3 @@ minTempUnloading | 270.16 | 260.16 | 273.16 minWindUnloading | 0.0000 | 0.0000 | 10.000 rateTempUnloading | 1.87d+5 | 1.0d+5 | 3.0d+5 rateWindUnloading | 1.56d+5 | 1.0d+5 | 3.0d+5 -! history Mon Jul 20 16:08:18 2020: /pool0/home/andrbenn/data/summa_3/utils/convert_summa_config_v2_v3.py ./syntheticTestCases/celia1990/summa_fileManager_celia1990.txt \ No newline at end of file diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zParamTrial.nc b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zParamTrial.nc similarity index 100% rename from test_ngen/celia_test/settings/syntheticTestCases/celia1990/common/summa_zParamTrial.nc rename to test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zParamTrial.nc diff --git a/test_ngen/summa-init-celia.namelist.input b/test_ngen/summa-init-celia.namelist.input index 7b33f256e..120501fae 100644 --- a/test_ngen/summa-init-celia.namelist.input +++ b/test_ngen/summa-init-celia.namelist.input @@ -1,3 +1,3 @@ ¶meters ! all inclusive stub for now - file_manager = "./extern/summa/summa/test_ngen/celia_test/settings/syntheticTestCases/celia1990/non_actors/summa_fileManager_celia1990.txt" + file_manager = "./extern/summa/summa/test_ngen/celia_test/settings/syntheticTestCases/celia1990/fileManager_celia1990.txt" / From 8b7a91822752de1d5c62524b8510daee67599cbb Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 25 Sep 2023 21:14:26 +0900 Subject: [PATCH 0936/1472] updating NGen make files for changes in NGen --- build/cmake/build_ngen.cluster.bash | 2 +- build/cmake/build_ngen.mac.bash | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/cmake/build_ngen.cluster.bash b/build/cmake/build_ngen.cluster.bash index 1a9acb531..745464b8d 100755 --- a/build/cmake/build_ngen.cluster.bash +++ b/build/cmake/build_ngen.cluster.bash @@ -19,7 +19,7 @@ cmake --build extern/iso_c_fortran_bmi/cmake_build --target all cmake -B extern/summa/cmake_build -S extern/summa -DCMAKE_BUILD_TYPE=BE_NexGen_Cluster cmake --build extern/summa/cmake_build --target all -cmake -B cmake_build -S . -DMPI_ACTIVE:BOOL=OFF -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON +cmake -B cmake_build -S . -DNGEN_WITH_MPI:BOOL=OFF -DNGEN_WITH_PYTHON:BOOL=OFF -DNGEN_WITH_BMI_C:BOOL=ON -DNGEN_WITH_BMI_FORTRAN:BOOL=ON -DNGEN_WITH_NETCDF:BOOL=OFF # can also add -DCMAKE_BUILD_TYPE=Debug to be able to run in gdb # make -j 8 -C cmake_build # build w/ 8 parallel jobs, also turn MPI on make -C cmake_build diff --git a/build/cmake/build_ngen.mac.bash b/build/cmake/build_ngen.mac.bash index 71cb8a6c9..5a8b55022 100755 --- a/build/cmake/build_ngen.mac.bash +++ b/build/cmake/build_ngen.mac.bash @@ -14,7 +14,7 @@ cmake --build extern/iso_c_fortran_bmi/cmake_build --target all cmake -B extern/summa/cmake_build -S extern/summa -DCMAKE_BUILD_TYPE=BE_NexGen cmake --build extern/summa/cmake_build --target all -cmake -B cmake_build -S . -DMPI_ACTIVE:BOOL=OFF -DNGEN_ACTIVATE_PYTHON:BOOL=OFF -DBMI_C_LIB_ACTIVE:BOOL=ON -DBMI_FORTRAN_ACTIVE:BOOL=ON +cmake -B cmake_build -S . -DNGEN_WITH_MPI:BOOL=OFF -DNGEN_WITH_PYTHON:BOOL=OFF -DNGEN_WITH_BMI_C:BOOL=ON -DNGEN_WITH_BMI_FORTRAN:BOOL=ON -DNGEN_WITH_NETCDF:BOOL=OFF # can also add -DCMAKE_BUILD_TYPE=Debug to be able to run in gdb # make -j 8 -C cmake_build # build w/ 8 parallel jobs, also turn MPI on make -C cmake_build From f0d16021bbb2e527bb9a2bacf323c4ce59bc18ad Mon Sep 17 00:00:00 2001 From: seantrim Date: Mon, 25 Sep 2023 15:53:02 -0600 Subject: [PATCH 0937/1472] Streamlined initialize and finalize steps for the vegLiqFlux call. --- build/source/dshare/data_types.f90 | 48 ++++++++++-------------------- build/source/engine/computFlux.f90 | 38 +++++++++++++++++++++-- 2 files changed, 52 insertions(+), 34 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 85ec0e401..dcd5b60b2 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -552,47 +552,24 @@ subroutine initialize_in_vegLiqFlux(in_vegLiqFlux,computeVegFlux,scalarCanopyLiq end associate end subroutine initialize_in_vegLiqFlux - subroutine finalize_out_vegLiqFlux(out_vegLiqFlux,globalPrintFlag,scalarCanopyLiqTrial,flux_data,deriv_data,message,err,cmessage) + subroutine finalize_out_vegLiqFlux(out_vegLiqFlux,flux_data,deriv_data,err,cmessage) class(out_type_vegLiqFlux),intent(in) :: out_vegLiqFlux ! class object for intent(out) vegLiqFlux arguments - logical(lgt),intent(in) :: globalPrintFlag ! global print flag for debug output - real(rkind),intent(in) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - character(*),intent(inout) :: message ! computFlux error message integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: cmessage ! error message from vegLiqFlux associate( & scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1), & ! intent(out): [dp] rain that reaches the ground without ever touching the canopy (kg m-2 s-1) scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1), & ! intent(out): [dp] drainage of liquid water from the vegetation canopy (kg m-2 s-1) scalarThroughfallRainDeriv => deriv_data%var(iLookDERIV%scalarThroughfallRainDeriv )%dat(1),& ! intent(out): [dp] derivative in throughfall w.r.t. canopy liquid water - scalarCanopyLiqDrainageDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDrainageDeriv)%dat(1),& ! intent(out): [dp] derivative in canopy drainage w.r.t. canopy liquid water - scalarCanopyNetLiqFlux => flux_data%var(iLookFLUX%scalarCanopyNetLiqFlux)%dat(1), & ! intent(out): [dp] net liquid water flux for the vegetation canopy (kg m-2 s-1) - scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1), & ! intent(in): [dp] rainfall rate (kg m-2 s-1) - scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1), & ! intent(out): [dp] canopy evaporation/condensation (kg m-2 s-1) - scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ) ! intent(out): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water - ! intent(out) arguments - scalarThroughfallRain =out_vegLiqFlux % scalarThroughfallRain ! intent(out): rain that reaches the ground without ever touching the canopy (kg m-2 s-1) - scalarCanopyLiqDrainage =out_vegLiqFlux % scalarCanopyLiqDrainage ! intent(out): drainage of liquid water from the vegetation canopy (kg m-2 s-1) - scalarThroughfallRainDeriv =out_vegLiqFlux % scalarThroughfallRainDeriv ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) - scalarCanopyLiqDrainageDeriv=out_vegLiqFlux % scalarCanopyLiqDrainageDeriv! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) - err =out_vegLiqFlux % err ! intent(out): error code - cmessage =out_vegLiqFlux % cmessage ! intent(out): error control - ! error control - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - ! calculate the net liquid water flux for the vegetation canopy - scalarCanopyNetLiqFlux = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage - ! calculate the total derivative in the downward liquid flux - scalarCanopyLiqDeriv = scalarThroughfallRainDeriv + scalarCanopyLiqDrainageDeriv - ! test - if (globalPrintFlag) then - print*, '**' - print*, 'scalarRainfall = ', scalarRainfall - print*, 'scalarThroughfallRain = ', scalarThroughfallRain - print*, 'scalarCanopyEvaporation = ', scalarCanopyEvaporation - print*, 'scalarCanopyLiqDrainage = ', scalarCanopyLiqDrainage - print*, 'scalarCanopyNetLiqFlux = ', scalarCanopyNetLiqFlux - print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial - end if + scalarCanopyLiqDrainageDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDrainageDeriv)%dat(1) ) ! intent(out): [dp] derivative in canopy drainage w.r.t. canopy liquid water + ! intent(out) arguments + scalarThroughfallRain =out_vegLiqFlux % scalarThroughfallRain ! intent(out): rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + scalarCanopyLiqDrainage =out_vegLiqFlux % scalarCanopyLiqDrainage ! intent(out): drainage of liquid water from the vegetation canopy (kg m-2 s-1) + scalarThroughfallRainDeriv =out_vegLiqFlux % scalarThroughfallRainDeriv ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) + scalarCanopyLiqDrainageDeriv=out_vegLiqFlux % scalarCanopyLiqDrainageDeriv! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) + err =out_vegLiqFlux % err ! intent(out): error code + cmessage =out_vegLiqFlux % cmessage ! intent(out): error control end associate end subroutine finalize_out_vegLiqFlux ! ** end vegLiqFlux @@ -652,4 +629,11 @@ subroutine finalize_out_snowLiqFlx(out_snowLiqFlx,err,cmessage) cmessage=out_snowLiqFlx % cmessage ! intent(out): error message end subroutine finalize_out_snowLiqFlx ! ** end snowLiqFlx + + ! ** groundwatr + subroutine initialize_in_groundwatr + end subroutine initialize_in_groundwatr + + + ! ** end groundwatr END MODULE data_types diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 9a8c9a52f..c0948bf6c 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -337,9 +337,9 @@ subroutine computFlux(& ! *** CALCULATE THE LIQUID FLUX THROUGH VEGETATION *** if (ixVegHyd/=integerMissing) then ! if necessary, calculate liquid water fluxes through vegetation - call in_vegLiqFlux%initialize(computeVegFlux,scalarCanopyLiqTrial,flux_data) + call initialize_vegLiqFlux call vegLiqFlux(in_vegLiqFlux,mpar_data,diag_data,out_vegLiqFlux) - call out_vegLiqFlux%finalize(globalPrintFlag,scalarCanopyLiqTrial,flux_data,deriv_data,message,err,cmessage) + call finalize_vegLiqFlux end if ! end if computing the liquid water fluxes through vegetation ! *** CALCULATE THE LIQUID FLUX THROUGH SNOW *** @@ -919,6 +919,40 @@ subroutine subTools(op,sub) end subroutine subTools + subroutine initialize_vegLiqFlux + call in_vegLiqFlux%initialize(computeVegFlux,scalarCanopyLiqTrial,flux_data) + end subroutine initialize_vegLiqFlux + + subroutine finalize_vegLiqFlux + call out_vegLiqFlux%finalize(flux_data,deriv_data,err,cmessage) + associate( & + scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1), & ! intent(out): [dp] rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1), & ! intent(out): [dp] drainage of liquid water from the vegetation canopy (kg m-2 s-1) + scalarThroughfallRainDeriv => deriv_data%var(iLookDERIV%scalarThroughfallRainDeriv )%dat(1),& ! intent(out): [dp] derivative in throughfall w.r.t. canopy liquid water + scalarCanopyLiqDrainageDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDrainageDeriv)%dat(1),& ! intent(out): [dp] derivative in canopy drainage w.r.t. canopy liquid water + scalarCanopyNetLiqFlux => flux_data%var(iLookFLUX%scalarCanopyNetLiqFlux)%dat(1), & ! intent(out): [dp] net liquid water flux for the vegetation canopy (kg m-2 s-1) + scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1), & ! intent(in): [dp] rainfall rate (kg m-2 s-1) + scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1), & ! intent(out): [dp] canopy evaporation/condensation (kg m-2 s-1) + scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ) ! intent(out): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water + ! error control + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + ! calculate the net liquid water flux for the vegetation canopy + scalarCanopyNetLiqFlux = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + ! calculate the total derivative in the downward liquid flux + scalarCanopyLiqDeriv = scalarThroughfallRainDeriv + scalarCanopyLiqDrainageDeriv + ! test + if (globalPrintFlag) then + print*, '**' + print*, 'scalarRainfall = ', scalarRainfall + print*, 'scalarThroughfallRain = ', scalarThroughfallRain + print*, 'scalarCanopyEvaporation = ', scalarCanopyEvaporation + print*, 'scalarCanopyLiqDrainage = ', scalarCanopyLiqDrainage + print*, 'scalarCanopyNetLiqFlux = ', scalarCanopyNetLiqFlux + print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial + end if + end associate + end subroutine finalize_vegLiqFlux + subroutine initialize_snowLiqFlx call in_snowLiqFlx%initialize(nSnow,firstFluxCall,scalarSolution,mLayerVolFracLiqTrial,flux_data) call io_snowLiqFlx%initialize(flux_data,deriv_data) From e946a418ac92dfed2b6ba9a9f78aec62b19ec755 Mon Sep 17 00:00:00 2001 From: seantrim Date: Mon, 25 Sep 2023 18:13:54 -0600 Subject: [PATCH 0938/1472] Applied object-oriented techniques to simplify call to groundwatr routine. --- build/source/dshare/data_types.f90 | 85 +++++++++++++++++++++++++----- build/source/engine/computFlux.f90 | 22 +++++++- 2 files changed, 91 insertions(+), 16 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index dcd5b60b2..6ad83195c 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -486,25 +486,32 @@ MODULE data_types ! ** groundwatr type, public :: in_type_groundwatr ! derived type for intent(in) arguments in groundwatr call - integer(i4b) :: nSnow ! intent(in): number of snow layers - integer(i4b) :: nSoil ! intent(in): number of soil layers - integer(i4b) :: nLayers ! intent(in): total number of layers - logical(lgt) :: firstFluxCall ! intent(in): logical flag to compute index of the lowest saturated layer - real(rkind), allocatable :: mLayerdTheta_dPsi(:) ! intent(in): derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) - real(rkind), allocatable :: mLayerMatricHeadLiqTrial(:) ! intent(in): liquid water matric potential (m) - real(rkind), allocatable :: mLayerVolFracLiqTrial(:) ! intent(in): volumetric fraction of liquid water (-) - real(rkind), allocatable :: mLayerVolFracIceTrial(:) ! intent(in): volumetric fraction of ice (-) + integer(i4b) :: nSnow ! intent(in): number of snow layers + integer(i4b) :: nSoil ! intent(in): number of soil layers + integer(i4b) :: nLayers ! intent(in): total number of layers + logical(lgt) :: firstFluxCall ! intent(in): logical flag to compute index of the lowest saturated layer + real(rkind), allocatable :: mLayerdTheta_dPsi(:) ! intent(in): derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) + real(rkind), allocatable :: mLayerMatricHeadLiqTrial(:) ! intent(in): liquid water matric potential (m) + real(rkind), allocatable :: mLayerVolFracLiqTrial(:) ! intent(in): volumetric fraction of liquid water (-) + real(rkind), allocatable :: mLayerVolFracIceTrial(:) ! intent(in): volumetric fraction of ice (-) + contains + procedure :: initialize => initialize_in_groundwatr end type in_type_groundwatr type, public :: io_type_groundwatr ! derived type for intent(io) arguments in groundwatr call - integer(i4b) :: ixSaturation ! intent(inout): index of lowest saturated layer (NOTE: only computed on the first iteration) + integer(i4b) :: ixSaturation ! intent(inout): index of lowest saturated layer (NOTE: only computed on the first iteration) + contains + procedure :: initialize => initialize_io_groundwatr + procedure :: finalize => finalize_io_groundwatr end type io_type_groundwatr type, public :: out_type_groundwatr ! derived type for intent(out) arguments in groundwatr call - real(rkind), allocatable :: mLayerBaseflow(:) ! intent(out): baseflow from each soil layer (m s-1) - real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + real(rkind), allocatable :: mLayerBaseflow(:) ! intent(out): baseflow from each soil layer (m s-1) + real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + integer(i4b) :: err ! intent(out): error code + character(:),allocatable :: cmessage ! intent(out): error message + contains + procedure :: finalize => finalize_out_groundwatr end type out_type_groundwatr ! ** end groundwatr @@ -631,9 +638,59 @@ end subroutine finalize_out_snowLiqFlx ! ** end snowLiqFlx ! ** groundwatr - subroutine initialize_in_groundwatr + subroutine initialize_in_groundwatr(in_groundwatr,nSnow,nSoil,nLayers,firstFluxCall,mLayerMatricHeadLiqTrial,mLayerVolFracLiqTrial,mLayerVolFracIceTrial,deriv_data) + class(in_type_groundwatr),intent(out) :: in_groundwatr ! class object for intent(in) arguments + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + logical(lgt),intent(in) :: firstFluxCall ! logical flag to compute index of the lowest saturated layer + real(rkind),intent(in) :: mLayerMatricHeadLiqTrial(:) ! trial value for the liquid water matric potential (m) + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) + type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + + associate(& + mLayerdTheta_dPsi => deriv_data%var(iLookDERIV%mLayerdTheta_dPsi)%dat )! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. psi + ! intent(in) arguments + in_groundwatr % nSnow = nSnow ! intent(in): number of snow layers + in_groundwatr % nSoil = nSoil ! intent(in): number of soil layers + in_groundwatr % nLayers = nLayers ! intent(in): total number of layers + in_groundwatr % firstFluxCall = firstFluxCall ! intent(in): logical flag to compute index of the lowest saturated layer + in_groundwatr % mLayerdTheta_dPsi = mLayerdTheta_dPsi ! intent(in): derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) + in_groundwatr % mLayerMatricHeadLiqTrial = mLayerMatricHeadLiqTrial ! intent(in): liquid water matric potential (m) + in_groundwatr % mLayerVolFracLiqTrial = mLayerVolFracLiqTrial(nSnow+1:nLayers) ! intent(in): volumetric fraction of liquid water (-) + in_groundwatr % mLayerVolFracIceTrial = mLayerVolFracIceTrial(nSnow+1:nLayers) ! intent(in): volumetric fraction of ice (-) + end associate end subroutine initialize_in_groundwatr + subroutine initialize_io_groundwatr(io_groundwatr,ixSaturation) + class(io_type_groundwatr),intent(out) :: io_groundwatr ! class object for intent(inout) arguments + integer(i4b),intent(in) :: ixSaturation ! index of lowest saturated layer (NOTE: only computed on the first iteration) + ! intent(inout) arguments + io_groundwatr % ixSaturation = ixSaturation ! intent(inout): index of lowest saturated layer (NOTE: only computed on the first iteration) + end subroutine initialize_io_groundwatr + subroutine finalize_io_groundwatr(io_groundwatr,ixSaturation) + class(io_type_groundwatr),intent(in) :: io_groundwatr ! class object for intent(inout) arguments + integer(i4b),intent(out) :: ixSaturation ! index of lowest saturated layer (NOTE: only computed on the first iteration) + ! intent(inout) arguments + ixSaturation = io_groundwatr % ixSaturation ! intent(inout): index of lowest saturated layer (NOTE: only computed on the first iteration) + end subroutine finalize_io_groundwatr + + subroutine finalize_out_groundwatr(out_groundwatr,dBaseflow_dMatric,flux_data,err,cmessage) + class(out_type_groundwatr),intent(in) :: out_groundwatr ! class object for intent(out) arguments + real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: cmessage ! error message from vegLiqFlux + associate(& + mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ) ! intent(out): [dp(:)] baseflow from each soil layer (m s-1) + ! intent(out) arguments + mLayerBaseflow = out_groundwatr % mLayerBaseflow ! intent(out): baseflow from each soil layer (m s-1) + dBaseflow_dMatric = out_groundwatr % dBaseflow_dMatric ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + err = out_groundwatr % err ! intent(out): error code + cmessage = out_groundwatr % cmessage ! intent(out): error message + end associate + end subroutine finalize_out_groundwatr ! ** end groundwatr END MODULE data_types diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index c0948bf6c..14a9cd850 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -386,9 +386,9 @@ subroutine computFlux(& ! variables needed for the numerical solution mLayerBaseflow(:) = 0._rkind ! baseflow from each soil layer (m s-1) else ! compute the baseflow flux for topmodel-ish shallow groundwater - call subTools(iLookOP%pre,iLookROUTINE%groundwatr) ! pre-processing for call to groundwatr + call initialize_groundwatr call groundwatr(in_groundwatr,attr_data,mpar_data,prog_data,diag_data,flux_data,io_groundwatr,out_groundwatr) - call subTools(iLookOP%post,iLookROUTINE%groundwatr) ! post-processing for call to groundwatr + call finalize_groundwatr end if ! computing baseflow flux scalarSoilBaseflow = sum(mLayerBaseflow) ! compute total baseflow from the soil zone (needed for mass balance checks) ! compute total runoff @@ -986,6 +986,24 @@ subroutine finalize_snowLiqFlx above_soilFracLiq = mLayerFracLiqSnow(nSnow) ! fraction of liquid water in bottom snow layer (-) end associate end subroutine finalize_snowLiqFlx + + subroutine initialize_groundwatr + ! check the derivative matrix is sized appropriately + if (size(dBaseflow_dMatric,1)/=nSoil .or. size(dBaseflow_dMatric,2)/=nSoil) then + message=trim(message)//'expect dBaseflow_dMatric to be nSoil x nSoil' + err=20; return + end if + call in_groundwatr%initialize(nSnow,nSoil,nLayers,firstFluxCall,mLayerMatricHeadLiqTrial,mLayerVolFracLiqTrial,mLayerVolFracIceTrial,deriv_data) + call io_groundwatr%initialize(ixSaturation) + end subroutine initialize_groundwatr + + subroutine finalize_groundwatr + call io_groundwatr%finalize(ixSaturation) + call out_groundwatr%finalize(dBaseflow_dMatric,flux_data,err,cmessage) + ! error control + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + end subroutine finalize_groundwatr + end subroutine computFlux ! ********************************************************************************************************** From e7fef2b6e3a4371fd7c97f60b2c0079e3af90b92 Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 26 Sep 2023 19:26:51 -0600 Subject: [PATCH 0939/1472] Applied object-oriented methods to simplify call to biqAquifer in computFlux. --- build/source/dshare/data_types.f90 | 119 ++++++++++++++++++++++++----- build/source/engine/computFlux.f90 | 26 ++++++- 2 files changed, 125 insertions(+), 20 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 6ad83195c..2b6a3b9db 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -517,29 +517,36 @@ MODULE data_types ! ** bigAquifer type, public :: in_type_bigAquifer ! derived type for intent(in) arguments in bigAquifer call - real(rkind) :: scalarAquiferStorageTrial ! intent(in): trial value of aquifer storage (m) - real(rkind) :: scalarCanopyTranspiration ! intent(in): canopy transpiration (kg m-2 s-1) - real(rkind) :: scalarSoilDrainage ! intent(in): soil drainage (m s-1) - real(rkind) :: dCanopyTrans_dCanWat ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - real(rkind) :: dCanopyTrans_dTCanair ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - real(rkind) :: dCanopyTrans_dTCanopy ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - real(rkind) :: dCanopyTrans_dTGround ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + real(rkind) :: scalarAquiferStorageTrial ! intent(in): trial value of aquifer storage (m) + real(rkind) :: scalarCanopyTranspiration ! intent(in): canopy transpiration (kg m-2 s-1) + real(rkind) :: scalarSoilDrainage ! intent(in): soil drainage (m s-1) + real(rkind) :: dCanopyTrans_dCanWat ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + real(rkind) :: dCanopyTrans_dTCanair ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(rkind) :: dCanopyTrans_dTCanopy ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(rkind) :: dCanopyTrans_dTGround ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + contains + procedure :: initialize => initialize_in_bigAquifer end type in_type_bigAquifer type, public :: io_type_bigAquifer ! derived type for intent(inout) arguments in bigAquifer call - real(rkind) :: dAquiferTrans_dTCanair ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature - real(rkind) :: dAquiferTrans_dTCanopy ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy temperature - real(rkind) :: dAquiferTrans_dTGround ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. ground temperature - real(rkind) :: dAquiferTrans_dCanWat ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy total water + real(rkind) :: dAquiferTrans_dTCanair ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + real(rkind) :: dAquiferTrans_dTCanopy ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy temperature + real(rkind) :: dAquiferTrans_dTGround ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. ground temperature + real(rkind) :: dAquiferTrans_dCanWat ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy total water + contains + procedure :: initialize => initialize_io_bigAquifer + procedure :: finalize => finalize_io_bigAquifer end type io_type_bigAquifer type, public :: out_type_bigAquifer ! derived type for intent(out) arguments in bigAquifer call - real(rkind) :: scalarAquiferTranspire ! intent(out): transpiration loss from the aquifer (m s-1) - real(rkind) :: scalarAquiferRecharge ! intent(out): recharge to the aquifer (m s-1) - real(rkind) :: scalarAquiferBaseflow ! intent(out): total baseflow from the aquifer (m s-1) - real(rkind) :: dBaseflow_dAquifer ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) - integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + real(rkind) :: scalarAquiferTranspire ! intent(out): transpiration loss from the aquifer (m s-1) + real(rkind) :: scalarAquiferRecharge ! intent(out): recharge to the aquifer (m s-1) + real(rkind) :: scalarAquiferBaseflow ! intent(out): total baseflow from the aquifer (m s-1) + real(rkind) :: dBaseflow_dAquifer ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) + integer(i4b) :: err ! intent(out): error code + character(:),allocatable :: cmessage ! intent(out): error message + contains + procedure :: finalize => finalize_out_bigAquifer end type out_type_bigAquifer ! ** end bigAquifer @@ -693,4 +700,82 @@ subroutine finalize_out_groundwatr(out_groundwatr,dBaseflow_dMatric,flux_data,er end associate end subroutine finalize_out_groundwatr ! ** end groundwatr + + ! ** bigAquifer + subroutine initialize_in_bigAquifer(in_bigAquifer,scalarAquiferStorageTrial,flux_data,deriv_data) + class(in_type_bigAquifer),intent(out) :: in_bigAquifer ! class object for intent(in) arguments + real(rkind),intent(in) :: scalarAquiferStorageTrial ! trial value of aquifer storage (m) + type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + associate(& + scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1), & ! intent(out): [dp] canopy transpiration (kg m-2 s-1) + scalarSoilDrainage => flux_data%var(iLookFLUX%scalarSoilDrainage)%dat(1), & ! intent(out): [dp] drainage from the soil profile (m s-1) + dCanopyTrans_dCanWat => deriv_data%var(iLookDERIV%dCanopyTrans_dCanWat)%dat(1), & ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair => deriv_data%var(iLookDERIV%dCanopyTrans_dTCanair)%dat(1), & ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy => deriv_data%var(iLookDERIV%dCanopyTrans_dTCanopy)%dat(1), & ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround => deriv_data%var(iLookDERIV%dCanopyTrans_dTGround)%dat(1) ) ! intent(out): [dp] derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + ! intent(in) arguments + in_bigAquifer % scalarAquiferStorageTrial = scalarAquiferStorageTrial ! intent(in): trial value of aquifer storage (m) + in_bigAquifer % scalarCanopyTranspiration = scalarCanopyTranspiration ! intent(in): canopy transpiration (kg m-2 s-1) + in_bigAquifer % scalarSoilDrainage = scalarSoilDrainage ! intent(in): soil drainage (m s-1) + in_bigAquifer % dCanopyTrans_dCanWat = dCanopyTrans_dCanWat ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + in_bigAquifer % dCanopyTrans_dTCanair = dCanopyTrans_dTCanair ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + in_bigAquifer % dCanopyTrans_dTCanopy = dCanopyTrans_dTCanopy ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + in_bigAquifer % dCanopyTrans_dTGround = dCanopyTrans_dTGround ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + end associate + end subroutine initialize_in_bigAquifer + + subroutine initialize_io_bigAquifer(io_bigAquifer,deriv_data) + class(io_type_bigAquifer),intent(out) :: io_bigAquifer ! class object for intent(inout) arguments + type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + associate(& + dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair)%dat(1), & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + dAquiferTrans_dTCanopy => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanopy)%dat(1), & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy temperature + dAquiferTrans_dTGround => deriv_data%var(iLookDERIV%dAquiferTrans_dTGround)%dat(1), & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. ground temperature + dAquiferTrans_dCanWat => deriv_data%var(iLookDERIV%dAquiferTrans_dCanWat)%dat(1) ) ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy total water + ! intent(inout) arguments + io_bigAquifer % dAquiferTrans_dTCanair = dAquiferTrans_dTCanair ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + io_bigAquifer % dAquiferTrans_dTCanopy = dAquiferTrans_dTCanopy ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy temperature + io_bigAquifer % dAquiferTrans_dTGround = dAquiferTrans_dTGround ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. ground temperature + io_bigAquifer % dAquiferTrans_dCanWat = dAquiferTrans_dCanWat ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy total water + end associate + end subroutine initialize_io_bigAquifer + + subroutine finalize_io_bigAquifer(io_bigAquifer,deriv_data) + class(io_type_bigAquifer),intent(in) :: io_bigAquifer ! class object for intent(inout) arguments + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + associate(& + dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair)%dat(1), & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + dAquiferTrans_dTCanopy => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanopy)%dat(1), & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy temperature + dAquiferTrans_dTGround => deriv_data%var(iLookDERIV%dAquiferTrans_dTGround)%dat(1), & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. ground temperature + dAquiferTrans_dCanWat => deriv_data%var(iLookDERIV%dAquiferTrans_dCanWat)%dat(1) ) ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy total water + ! intent(inout) arguments + dAquiferTrans_dTCanair = io_bigAquifer % dAquiferTrans_dTCanair ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + dAquiferTrans_dTCanopy = io_bigAquifer % dAquiferTrans_dTCanopy ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy temperature + dAquiferTrans_dTGround = io_bigAquifer % dAquiferTrans_dTGround ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. ground temperature + dAquiferTrans_dCanWat = io_bigAquifer % dAquiferTrans_dCanWat ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy total water + end associate + end subroutine finalize_io_bigAquifer + + subroutine finalize_out_bigAquifer(out_bigAquifer,flux_data,deriv_data,err,cmessage) + class(out_type_bigAquifer),intent(in) :: out_bigAquifer ! class object for intent(out) arguments + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: cmessage ! error message from vegLiqFlux + associate(& + scalarAquiferTranspire => flux_data%var(iLookFLUX%scalarAquiferTranspire)%dat(1), & ! intent(out): [dp] transpiration loss from the aquifer (m s-1) + scalarAquiferRecharge => flux_data%var(iLookFLUX%scalarAquiferRecharge)%dat(1), & ! intent(out): [dp] recharge to the aquifer (m s-1) + scalarAquiferBaseflow => flux_data%var(iLookFLUX%scalarAquiferBaseflow)%dat(1), & ! intent(out): [dp] total baseflow from the aquifer (m s-1) + dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer)%dat(1) ) ! intent(out): [dp(:)] derivative in baseflow flux w.r.t. aquifer storage (s-1) + ! intent(out) arguments + scalarAquiferTranspire = out_bigAquifer % scalarAquiferTranspire ! intent(out): transpiration loss from the aquifer (m s-1) + scalarAquiferRecharge = out_bigAquifer % scalarAquiferRecharge ! intent(out): recharge to the aquifer (m s-1) + scalarAquiferBaseflow = out_bigAquifer % scalarAquiferBaseflow ! intent(out): total baseflow from the aquifer (m s-1) + dBaseflow_dAquifer = out_bigAquifer % dBaseflow_dAquifer ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) + err = out_bigAquifer % err ! intent(out): error code + cmessage = out_bigAquifer % cmessage ! intent(out): error message + end associate + end subroutine finalize_out_bigAquifer + ! ** end bigAquifer END MODULE data_types diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 14a9cd850..811dda313 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -401,9 +401,9 @@ subroutine computFlux(& ! *** CALCULATE FLUXES FOR THE DEEP AQUIFER *** if (ixAqWat/=integerMissing) then ! check if computing aquifer fluxes if (local_ixGroundwater==bigBucket) then ! compute fluxes for the big bucket - call subTools(iLookOP%pre,iLookROUTINE%bigAquifer) ! pre-processing for call to bigAquifer + call initialize_bigAquifer call bigAquifer(in_bigAquifer,mpar_data,diag_data,io_bigAquifer,out_bigAquifer) - call subTools(iLookOP%post,iLookROUTINE%bigAquifer) ! post-processing for call to bigAquifer + call finalize_bigAquifer else ! if no aquifer, then fluxes are zero scalarAquiferTranspire = 0._rkind ! transpiration loss from the aquifer (m s-1) scalarAquiferRecharge = 0._rkind ! recharge to the aquifer (m s-1) @@ -1003,7 +1003,27 @@ subroutine finalize_groundwatr ! error control if (err/=0) then; message=trim(message)//trim(cmessage); return; end if end subroutine finalize_groundwatr - + + subroutine initialize_bigAquifer + call in_bigAquifer%initialize(scalarAquiferStorageTrial,flux_data,deriv_data) + call io_bigAquifer%initialize(deriv_data) + end subroutine initialize_bigAquifer + + subroutine finalize_bigAquifer + call io_bigAquifer%finalize(deriv_data) + call out_bigAquifer%finalize(flux_data,deriv_data,err,cmessage) + associate(& + scalarTotalRunoff => flux_data%var(iLookFLUX%scalarTotalRunoff)%dat(1) ,& ! intent(out): [dp] total runoff (m s-1) + scalarSurfaceRunoff => flux_data%var(iLookFLUX%scalarSurfaceRunoff)%dat(1) ,& ! intent(out): [dp] surface runoff (m s-1) + scalarAquiferBaseflow => flux_data%var(iLookFLUX%scalarAquiferBaseflow)%dat(1) ) ! intent(out): [dp] total baseflow from the aquifer (m s-1) + ! error control + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + ! compute total runoff (overwrite previously calculated value before considering aquifer). + ! (Note: SoilDrainage goes into aquifer, not runoff) + scalarTotalRunoff = scalarSurfaceRunoff + scalarAquiferBaseflow + end associate + end subroutine finalize_bigAquifer + end subroutine computFlux ! ********************************************************************************************************** From 922daabe6ef69c3ea332b62736ad686b568d81bb Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 27 Sep 2023 17:03:48 -0600 Subject: [PATCH 0940/1472] Applied object-oriented methods to ssdNrgFlux call in computFlux. --- build/source/dshare/data_types.f90 | 114 ++++++++++++++++++++++++----- build/source/engine/computFlux.f90 | 28 ++++++- 2 files changed, 123 insertions(+), 19 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 2b6a3b9db..712f77be6 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -354,29 +354,36 @@ MODULE data_types ! ** ssdNrgFlux type, public :: in_type_ssdNrgFlux ! derived type for intent(in) arguments in ssdNrgFlux call - logical(lgt) :: scalarSolution ! intent(in): flag to denote if implementing the scalar solution - real(rkind) :: scalarGroundNetNrgFlux ! intent(in): net energy flux for the ground surface (W m-2) - real(rkind), allocatable :: iLayerLiqFluxSnow(:) ! intent(in): liquid flux at the interface of each snow layer (m s-1) - real(rkind), allocatable :: iLayerLiqFluxSoil(:) ! intent(in): liquid flux at the interface of each soil layer (m s-1) - real(rkind), allocatable :: mLayerTempTrial(:) ! intent(in): temperature in each layer at the current iteration (m) - real(rkind), allocatable :: dThermalC_dWatAbove(:) ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above - real(rkind), allocatable :: dThermalC_dWatBelow(:) ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above - real(rkind), allocatable :: dThermalC_dTempAbove(:) ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above - real(rkind), allocatable :: dThermalC_dTempBelow(:) ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above + logical(lgt) :: scalarSolution ! intent(in): flag to denote if implementing the scalar solution + real(rkind) :: scalarGroundNetNrgFlux ! intent(in): net energy flux for the ground surface (W m-2) + real(rkind), allocatable :: iLayerLiqFluxSnow(:) ! intent(in): liquid flux at the interface of each snow layer (m s-1) + real(rkind), allocatable :: iLayerLiqFluxSoil(:) ! intent(in): liquid flux at the interface of each soil layer (m s-1) + real(rkind), allocatable :: mLayerTempTrial(:) ! intent(in): temperature in each layer at the current iteration (m) + real(rkind), allocatable :: dThermalC_dWatAbove(:) ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above + real(rkind), allocatable :: dThermalC_dWatBelow(:) ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above + real(rkind), allocatable :: dThermalC_dTempAbove(:) ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above + real(rkind), allocatable :: dThermalC_dTempBelow(:) ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above + contains + procedure :: initialize => initialize_in_ssdNrgFlux end type in_type_ssdNrgFlux type, public :: io_type_ssdNrgFlux ! derived type for intent(inout) arguments in ssdNrgFlux call - real(rkind) :: dGroundNetFlux_dGroundTemp ! intent(inout): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dGroundNetFlux_dGroundTemp ! intent(inout): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + contains + procedure :: initialize => initialize_io_ssdNrgFlux + procedure :: finalize => finalize_io_ssdNrgFlux end type io_type_ssdNrgFlux type, public :: out_type_ssdNrgFlux ! derived type for intent(inout) arguments in ssdNrgFlux call - real(rkind), allocatable :: iLayerNrgFlux(:) ! intent(out): energy flux at the layer interfaces (W m-2) - real(rkind), allocatable :: dNrgFlux_dTempAbove(:) ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) - real(rkind), allocatable :: dNrgFlux_dTempBelow(:) ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) - real(rkind), allocatable :: dNrgFlux_dWatAbove(:) ! intent(out): derivatives in the flux w.r.t. water state in the layer above (J m-2 s-1 K-1) - real(rkind), allocatable :: dNrgFlux_dWatBelow(:) ! intent(out): derivatives in the flux w.r.t. water state in the layer below (J m-2 s-1 K-1) - integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + real(rkind), allocatable :: iLayerNrgFlux(:) ! intent(out): energy flux at the layer interfaces (W m-2) + real(rkind), allocatable :: dNrgFlux_dTempAbove(:) ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) + real(rkind), allocatable :: dNrgFlux_dTempBelow(:) ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) + real(rkind), allocatable :: dNrgFlux_dWatAbove(:) ! intent(out): derivatives in the flux w.r.t. water state in the layer above (J m-2 s-1 K-1) + real(rkind), allocatable :: dNrgFlux_dWatBelow(:) ! intent(out): derivatives in the flux w.r.t. water state in the layer below (J m-2 s-1 K-1) + integer(i4b) :: err ! intent(out): error code + character(:),allocatable :: cmessage ! intent(out): error message + contains + procedure :: finalize => finalize_out_ssdNrgFlux end type out_type_ssdNrgFlux ! ** end ssdNrgFlux @@ -551,6 +558,79 @@ MODULE data_types ! ** end bigAquifer contains + + ! ** ssdNrgFlux + subroutine initialize_in_ssdNrgFlux(in_ssdNrgFlux,scalarSolution,firstFluxCall,mLayerTempTrial,flux_data,deriv_data) + class(in_type_ssdNrgFlux),intent(out) :: in_ssdNrgFlux ! class object for intent(in) ssdNrgFlux arguments + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + logical(lgt),intent(in) :: firstFluxCall ! flag to indicate if we are processing the first flux call + real(rkind),intent(in) :: mLayerTempTrial(:) ! trial value for temperature of each snow/soil layer (K) + type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + associate(& + scalarGroundNetNrgFlux => flux_data%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1), & ! intent(out): [dp] net energy flux for the ground surface (W m-2) + iLayerLiqFluxSnow => flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat, & ! intent(out): [dp(0:)] vertical liquid water flux at snow layer interfaces (-) + iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat, & ! intent(out): [dp(0:)] vertical liquid water flux at soil layer interfaces (-) + dThermalC_dWatAbove => deriv_data%var(iLookDERIV%dThermalC_dWatAbove)%dat, & ! intent(in): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow)%dat, & ! intent(in): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove)%dat, & ! intent(in): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow)%dat ) ! intent(in): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + ! intent(in) arguments + in_ssdNrgFlux % scalarSolution=scalarSolution .and. .not.firstFluxCall ! intent(in): flag to denote if implementing the scalar solution + in_ssdNrgFlux % scalarGroundNetNrgFlux=scalarGroundNetNrgFlux ! intent(in): net energy flux for the ground surface (W m-2) + in_ssdNrgFlux % iLayerLiqFluxSnow=iLayerLiqFluxSnow ! intent(in): liquid flux at the interface of each snow layer (m s-1) + in_ssdNrgFlux % iLayerLiqFluxSoil=iLayerLiqFluxSoil ! intent(in): liquid flux at the interface of each soil layer (m s-1) + in_ssdNrgFlux % mLayerTempTrial=mLayerTempTrial ! intent(in): temperature in each layer at the current iteration (m) + in_ssdNrgFlux % dThermalC_dWatAbove=dThermalC_dWatAbove ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above + in_ssdNrgFlux % dThermalC_dWatBelow=dThermalC_dWatBelow ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above + in_ssdNrgFlux % dThermalC_dTempAbove=dThermalC_dTempAbove ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above + in_ssdNrgFlux % dThermalC_dTempBelow=dThermalC_dTempBelow ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above + end associate + end subroutine initialize_in_ssdNrgFlux + + subroutine initialize_io_ssdNrgFlux(io_ssdNrgFlux,deriv_data) + class(io_type_ssdNrgFlux),intent(out) :: io_ssdNrgFlux ! class object for intent(inout) ssdNrgFlux arguments + type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + associate(& + dGroundNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dGroundTemp)%dat(1) ) ! intent(out): [dp] derivative in net ground flux w.r.t. ground temperature + ! intent(inout) arguments + io_ssdNrgFlux % dGroundNetFlux_dGroundTemp=dGroundNetFlux_dGroundTemp ! intent(inout): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + end associate + end subroutine initialize_io_ssdNrgFlux + + subroutine finalize_io_ssdNrgFlux(io_ssdNrgFlux,deriv_data) + class(io_type_ssdNrgFlux),intent(in) :: io_ssdNrgFlux ! class object for intent(inout) ssdNrgFlux arguments + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + associate(& + dGroundNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dGroundTemp)%dat(1) ) ! intent(out): [dp] derivative in net ground flux w.r.t. ground temperature + ! intent(inout) arguments + dGroundNetFlux_dGroundTemp=io_ssdNrgFlux % dGroundNetFlux_dGroundTemp ! intent(inout): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + end associate + end subroutine finalize_io_ssdNrgFlux + + subroutine finalize_out_ssdNrgFlux(out_ssdNrgFlux,flux_data,deriv_data,err,cmessage) + class(out_type_ssdNrgFlux),intent(in) :: out_ssdNrgFlux ! class object for intent(out) ssdNrgFlux arguments + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: cmessage ! error message from vegLiqFlux + associate(& + iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat, & ! intent(out): [dp(0:)] vertical energy flux at the interface of snow and soil layers + dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove)%dat, & ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above + dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow)%dat, & ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below + dNrgFlux_dWatAbove => deriv_data%var(iLookDERIV%dNrgFlux_dWatAbove)%dat, & ! intent(out): [dp(:)] derivatives in the flux w.r.t. water state in the layer above + dNrgFlux_dWatBelow => deriv_data%var(iLookDERIV%dNrgFlux_dWatBelow)%dat ) ! intent(out): [dp(:)] derivatives in the flux w.r.t. water state in the layer below + ! intent(out) arguments + iLayerNrgFlux =out_ssdNrgFlux % iLayerNrgFlux ! intent(out): energy flux at the layer interfaces (W m-2) + dNrgFlux_dTempAbove=out_ssdNrgFlux % dNrgFlux_dTempAbove ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) + dNrgFlux_dTempBelow=out_ssdNrgFlux % dNrgFlux_dTempBelow ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) + dNrgFlux_dWatAbove =out_ssdNrgFlux % dNrgFlux_dWatAbove ! intent(out): derivatives in the flux w.r.t. water state in the layer above (J m-2 s-1 K-1) + dNrgFlux_dWatBelow =out_ssdNrgFlux % dNrgFlux_dWatBelow ! intent(out): derivatives in the flux w.r.t. water state in the layer below (J m-2 s-1 K-1) + err =out_ssdNrgFlux % err ! intent(out): error code + cmessage =out_ssdNrgFlux % cmessage ! intent(out): error message + end associate + end subroutine finalize_out_ssdNrgFlux + ! ** end ssdNrgFlux ! ** vegLiqFlux subroutine initialize_in_vegLiqFlux(in_vegLiqFlux,computeVegFlux,scalarCanopyLiqTrial,flux_data) ! SJT diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 811dda313..f3533dd9d 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -330,9 +330,9 @@ subroutine computFlux(& ! *** CALCULATE ENERGY FLUXES THROUGH THE SNOW-SOIL DOMAIN *** if (nSnowSoilNrg>0) then ! if necessary, calculate energy fluxes at layer interfaces through the snow and soil domain - call subTools(iLookOP%pre,iLookROUTINE%ssdNrgFlux) ! pre-processing for call to ssdNrgFlux + call initialize_ssdNrgFlux call ssdNrgFlux(in_ssdNrgFlux,mpar_data,indx_data,prog_data,diag_data,flux_data,io_ssdNrgFlux,out_ssdNrgFlux) - call subTools(iLookOP%post,iLookROUTINE%ssdNrgFlux) ! post-processing for call to ssdNrgFlux + call finalize_ssdNrgFlux end if ! end if computing energy fluxes throughout the snow+soil domain ! *** CALCULATE THE LIQUID FLUX THROUGH VEGETATION *** @@ -919,6 +919,30 @@ subroutine subTools(op,sub) end subroutine subTools + subroutine initialize_ssdNrgFlux + call in_ssdNrgFlux%initialize(scalarSolution,firstFluxCall,mLayerTempTrial,flux_data,deriv_data) + call io_ssdNrgFlux%initialize(deriv_data) + end subroutine initialize_ssdNrgFlux + + subroutine finalize_ssdNrgFlux + call io_ssdNrgFlux%finalize(deriv_data) + call out_ssdNrgFlux%finalize(flux_data,deriv_data,err,cmessage) + associate(& + mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat, & ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) + iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat, & ! intent(out): [dp(0:)] vertical energy flux at the interface of snow and soil layers + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ) ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! error control + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + ! calculate net energy fluxes for each snow and soil layer (J m-3 s-1) + do iLayer=1,nLayers + mLayerNrgFlux(iLayer) = -(iLayerNrgFlux(iLayer) - iLayerNrgFlux(iLayer-1))/mLayerDepth(iLayer) + if (globalPrintFlag) then + if (iLayer < 10) write(*,'(a,1x,i4,1x,10(f25.15,1x))') 'iLayer, iLayerNrgFlux(iLayer-1:iLayer), mLayerNrgFlux(iLayer) = ', iLayer, iLayerNrgFlux(iLayer-1:iLayer), mLayerNrgFlux(iLayer) + end if + end do + end associate + end subroutine finalize_ssdNrgFlux + subroutine initialize_vegLiqFlux call in_vegLiqFlux%initialize(computeVegFlux,scalarCanopyLiqTrial,flux_data) end subroutine initialize_vegLiqFlux From 8dcf18163422e72d29e3129e159105146295824d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 28 Sep 2023 10:11:40 +0900 Subject: [PATCH 0941/1472] utilites --- utils/plot_per_GRU.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index eda4d7086..c84829181 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -47,10 +47,12 @@ plot_vars = settings plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] -if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,10e-3] -if stat=='maxe': maxes = [20,30,3e-4,2,4e-7,0.2] -if stat=='kgem' : maxes = [0.9,0.7,0.9,0.95,0.95,10e-3] -if stat=='mean': maxes = [100,1700,5e-5,8,1e-7,10e-3] +if stat == 'rmse': maxes = [2,15,8e-6,0.08,7e-9,10e-3] +# if stat=='rmse': maxes = [0.25,2,1e-6,0.01,1e-9,2e-3] +if stat == 'maxe': maxes = [20,30,3e-4,2,4e-7,0.2] +if stat == 'kgem': maxes = [0.9,0.7,0.9,0.95,0.95,10e-3] +if stat == 'mean': maxes = [80,1500,5e-5,8,1e-7,10e-3] +if stat == 'amax': maxes = [240,1800,1e-3,25,1.554e-5,0.2] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed.png' fig_fil = fig_fil.format(','.join(settings),stat) @@ -189,7 +191,7 @@ def make_default_path(suffix): # Multiply the s values by efficiency s = s*eff_batch - if stat == 'maxe' or 'mean': s = np.fabs(s) # make absolute value norm, max is not not all positive + if stat == 'maxe' or stat =='mean' or stat =='amax': s = np.fabs(s) # make absolute value norm, max is not not all positive bas_albers[plot_var] = s.sel(hru=hru_ids_shp.values) # Select lakes of a certain size for plotting @@ -228,9 +230,11 @@ def run_loop(i,var,the_max,f_x,f_y): my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap my_cmap.set_bad(color='white') #nan color white vmin,vmax = 0, the_max - if stat=='mean' and var=='scalarTotalSoilWater': vmin,vmax = 600, the_max + if stat =='mean' and var=='scalarTotalSoilWat': vmin,vmax = 700, the_max + if stat =='amax' and var=='scalarTotalSoilWat': vmin,vmax = 1000, the_max + if stat =='amax' and var=='averageRoutedRunoff': vmin,vmax = 1.538e-5, the_max norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) - if stat=='kgem' and var!='wallClockTime': + if stat =='kgem' and var!='wallClockTime': my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno')) # copy the default cmap my_cmap.set_bad(color='white') #nan color white vmin,vmax = the_max, 1.0 @@ -253,6 +257,7 @@ def run_loop(i,var,the_max,f_x,f_y): if stat == 'maxe': stat_word = ' Hourly max abs error' if stat == 'kgem': stat_word = ' Hourly KGEm' if stat == 'mean': stat_word = ' Hourly abs mean' + if stat == 'amax': stat_word = ' Hourly abs max' # wall Clock doesn't do difference if var == 'wallClockTime': From 92d728e9f4bae578e13b2ad56b64d7cd876e29ed Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 29 Sep 2023 21:39:27 +0900 Subject: [PATCH 0942/1472] playing with kinsol, nothing should affect main code base --- build/source/engine/eval8summa.f90 | 8 ++++---- build/source/engine/summaSolve4ida.f90 | 10 +++++----- build/source/engine/summaSolve4kinsol.f90 | 2 +- build/source/engine/varSubstep.f90 | 10 +++++----- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 4d5b41b61..7fa952b07 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -909,10 +909,10 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st select case(ixNumericalMethod) case(ida); err=20; message=trim(message)//'should not be imposing constraints for IDA solver'; return case(kinsol) - small_delTemp = .false. ! flag to constain temperature change to be less than zMaxTempIncrement, gets more accurate solution if off - zMaxTempIncrement = 1._rkind ! maximum temperature increment (K) - small_delMatric = .false. ! flag to constain matric head change to be less than zMaxMatricIncrement, gets more accurate solution if off - zMaxMatricIncrement = 1._rkind ! maximum matric head increment (m) + small_delTemp = .true. ! flag to constain temperature change to be less than zMaxTempIncrement + zMaxTempIncrement = 10._rkind ! maximum temperature increment (K) + small_delMatric = .true. ! flag to constain matric head change to be less than zMaxMatricIncrement + zMaxMatricIncrement = 10._rkind ! maximum matric head increment (m) detect_events = .true. ! flag to do freezing point event detection and cross-over with epsT, works best if on epsT = 1.e-3_rkind ! small interval above/below critical (K), works better if larger water_bounds = .true. ! flag to force water bounds, works best if on diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 32e4f4327..3d0418709 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -644,8 +644,8 @@ subroutine setSolverParams(dt_cur,nonlin_iter,ida_mem,retval) integer(i4b),intent(out) :: retval ! return value !======= Internals ============ - integer,parameter :: max_order = 5 ! maximum BDF order, default = 5 - real(qp),parameter :: coef_nonlin = 0.33 ! Coeff. in the nonlinear convergence test, default = 0.33 + integer,parameter :: max_order = 5 ! maximum BDF order, default and max = 5 + real(qp),parameter :: coef_nonlin = 0.33 ! coefficient in the nonlinear convergence test, default = 0.33 integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 integer(c_long),parameter :: max_step = 999999 ! maximum number of steps, default = 500 @@ -656,12 +656,12 @@ subroutine setSolverParams(dt_cur,nonlin_iter,ida_mem,retval) retval = FIDASetMaxOrd(ida_mem, max_order) if (retval /= 0) return - ! Set Coeff. in the nonlinear convergence test + ! Set coefficient in the nonlinear convergence test retval = FIDASetNonlinConvCoef(ida_mem, coef_nonlin) if (retval /= 0) return - ! Set maximun number of nonliear iterations - retval = FIDASetMaxNonlinIters(ida_mem, nonlin_iter) + ! Set maximun number of nonliear iterations, maybe should just make 4 (instead of SUMMA parameter) + retval = FIDASetMaxNonlinIters(ida_mem, nonlin_iter) if (retval /= 0) return ! Set maximum number of convergence test failures diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index 027271b3f..17937059e 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -470,7 +470,7 @@ subroutine setSolverParams(nonlin_iter,kinsol_mem,retval) if (retval /= 0) return ! Set maximum number of iterations - nonlin_itr = nonlin_iter ! maximum number of nonlinear iterations in SUNDIALS type + nonlin_itr = nonlin_iter ! maximum number of nonlinear iterations in SUNDIALS type, maybe should just make 200 (instead of SUMMA parameter) retval = FKINSetNumMaxIters(kinsol_mem, nonlin_itr) if (retval /= 0) return diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index df0b42c0b..b7acf350e 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -414,7 +414,7 @@ subroutine varSubstep(& ! identify the need to check the mass balance select case(ixNumericalMethod) - case(ida); checkMassBalance = .false. ! IDA balance agreement levels are controlled by set tolerances + case(ida); checkMassBalance = .false. ! IDA balance agreement levels are controlled by set tolerances (maybe kinsol should be false too) case(kinsol, numrec); checkMassBalance = .true. ! (.not.scalarSolution) end select @@ -446,7 +446,7 @@ subroutine varSubstep(& ! modify step err=0 ! error recovery - dtSubstep = dtSubstep/2._rkind + dtSubstep = dtSubstep/2._rkind ! check minimum: fail minimum step if there is an error in the update if(dtSubstep m From dd209aaf765e952c66618379864f5a481e006cca Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 3 Oct 2023 02:04:38 -0600 Subject: [PATCH 0943/1472] Applied object-oriented methods to vegNrgFlux call in computFlux. --- build/source/dshare/data_types.f90 | 218 +++++++++++++++++++++++------ build/source/engine/computFlux.f90 | 45 +++++- 2 files changed, 214 insertions(+), 49 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 712f77be6..b5ba49d3d 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -25,6 +25,7 @@ MODULE data_types USE var_lookup,only:maxvarStat USE var_lookup,only:iLookFLUX ! lookup indices for flux data USE var_lookup,only:iLookDERIV ! lookup indices for derivative data + USE var_lookup,only:iLookFORCE ! lookup indices for forcing data implicit none ! constants necessary for variable defs private @@ -304,51 +305,55 @@ MODULE data_types ! define derived types used to simplify passing subroutine arguments ! ** vegNrgFlux type, public :: in_type_vegNrgFlux ! derived type for intent(in) arguments in vegNrgFlux call - logical(lgt) :: firstSubStep ! intent(in): flag to indicate if we are processing the first sub-step - logical(lgt) :: firstFluxCall ! intent(in): flag to indicate if we are processing the first flux call - logical(lgt) :: computeVegFlux ! intent(in): flag to indicate if we need to compute fluxes over vegetation - logical(lgt) :: checkLWBalance ! intent(in): flag to check longwave balance - real(rkind) :: upperBoundTemp ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature - real(rkind) :: scalarCanairTempTrial ! intent(in): trial value of the canopy air space temperature (K) - real(rkind) :: scalarCanopyTempTrial ! intent(in): trial value of canopy temperature (K) - real(rkind) :: mLayerTempTrial_1 ! intent(in): trial value of ground temperature (K) - real(rkind) :: scalarCanopyIceTrial ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyLiqTrial ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: dCanLiq_dTcanopy ! intent(in): derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) + logical(lgt) :: firstSubStep ! intent(in): flag to indicate if we are processing the first sub-step + logical(lgt) :: firstFluxCall ! intent(in): flag to indicate if we are processing the first flux call + logical(lgt) :: computeVegFlux ! intent(in): flag to indicate if we need to compute fluxes over vegetation + logical(lgt) :: checkLWBalance ! intent(in): flag to check longwave balance + real(rkind) :: upperBoundTemp ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature + real(rkind) :: scalarCanairTempTrial ! intent(in): trial value of the canopy air space temperature (K) + real(rkind) :: scalarCanopyTempTrial ! intent(in): trial value of canopy temperature (K) + real(rkind) :: mLayerTempTrial_1 ! intent(in): trial value of ground temperature (K) + real(rkind) :: scalarCanopyIceTrial ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyLiqTrial ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: dCanLiq_dTcanopy ! intent(in): derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) + contains + procedure :: initialize => initialize_in_vegNrgFlux end type in_type_vegNrgFlux type, public :: out_type_vegNrgFlux ! derived type for intent(out) arguments in vegNrgFlux call - real(rkind) :: scalarCanopyTranspiration ! intent(out): canopy transpiration (kg m-2 s-1) - real(rkind) :: scalarCanopyEvaporation ! intent(out): canopy evaporation/condensation (kg m-2 s-1) - real(rkind) :: scalarGroundEvaporation ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - real(rkind) :: scalarCanairNetNrgFlux ! intent(out): net energy flux for the canopy air space (W m-2) - real(rkind) :: scalarCanopyNetNrgFlux ! intent(out): net energy flux for the vegetation canopy (W m-2) - real(rkind) :: scalarGroundNetNrgFlux ! intent(out): net energy flux for the ground surface (W m-2) - real(rkind) :: dCanairNetFlux_dCanairTemp ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dCanairNetFlux_dCanopyTemp ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dCanairNetFlux_dGroundTemp ! intent(out): derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dCanopyNetFlux_dCanairTemp ! intent(out): derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dCanopyNetFlux_dCanopyTemp ! intent(out): derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dCanopyNetFlux_dGroundTemp ! intent(out): derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dGroundNetFlux_dCanairTemp ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) - real(rkind) :: dGroundNetFlux_dCanopyTemp ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) - real(rkind) :: dGroundNetFlux_dGroundTemp ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - real(rkind) :: dCanopyEvaporation_dCanWat ! intent(out): derivative in canopy evaporation w.r.t. canopy total water content (s-1) - real(rkind) :: dCanopyEvaporation_dTCanair ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - real(rkind) :: dCanopyEvaporation_dTCanopy ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - real(rkind) :: dCanopyEvaporation_dTGround ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - real(rkind) :: dGroundEvaporation_dCanWat ! intent(out): derivative in ground evaporation w.r.t. canopy total water content (s-1) - real(rkind) :: dGroundEvaporation_dTCanair ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - real(rkind) :: dGroundEvaporation_dTCanopy ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - real(rkind) :: dGroundEvaporation_dTGround ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - real(rkind) :: dCanopyTrans_dCanWat ! intent(out): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - real(rkind) :: dCanopyTrans_dTCanair ! intent(out): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - real(rkind) :: dCanopyTrans_dTCanopy ! intent(out): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - real(rkind) :: dCanopyTrans_dTGround ! intent(out): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - real(rkind) :: dCanopyNetFlux_dCanWat ! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) - real(rkind) :: dGroundNetFlux_dCanWat ! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) - integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + real(rkind) :: scalarCanopyTranspiration ! intent(out): canopy transpiration (kg m-2 s-1) + real(rkind) :: scalarCanopyEvaporation ! intent(out): canopy evaporation/condensation (kg m-2 s-1) + real(rkind) :: scalarGroundEvaporation ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + real(rkind) :: scalarCanairNetNrgFlux ! intent(out): net energy flux for the canopy air space (W m-2) + real(rkind) :: scalarCanopyNetNrgFlux ! intent(out): net energy flux for the vegetation canopy (W m-2) + real(rkind) :: scalarGroundNetNrgFlux ! intent(out): net energy flux for the ground surface (W m-2) + real(rkind) :: dCanairNetFlux_dCanairTemp ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dCanairNetFlux_dCanopyTemp ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dCanairNetFlux_dGroundTemp ! intent(out): derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dCanopyNetFlux_dCanairTemp ! intent(out): derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dCanopyNetFlux_dCanopyTemp ! intent(out): derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dCanopyNetFlux_dGroundTemp ! intent(out): derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dGroundNetFlux_dCanairTemp ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) + real(rkind) :: dGroundNetFlux_dCanopyTemp ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) + real(rkind) :: dGroundNetFlux_dGroundTemp ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + real(rkind) :: dCanopyEvaporation_dCanWat ! intent(out): derivative in canopy evaporation w.r.t. canopy total water content (s-1) + real(rkind) :: dCanopyEvaporation_dTCanair ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(rkind) :: dCanopyEvaporation_dTCanopy ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(rkind) :: dCanopyEvaporation_dTGround ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + real(rkind) :: dGroundEvaporation_dCanWat ! intent(out): derivative in ground evaporation w.r.t. canopy total water content (s-1) + real(rkind) :: dGroundEvaporation_dTCanair ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(rkind) :: dGroundEvaporation_dTCanopy ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(rkind) :: dGroundEvaporation_dTGround ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + real(rkind) :: dCanopyTrans_dCanWat ! intent(out): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + real(rkind) :: dCanopyTrans_dTCanair ! intent(out): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(rkind) :: dCanopyTrans_dTCanopy ! intent(out): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(rkind) :: dCanopyTrans_dTGround ! intent(out): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + real(rkind) :: dCanopyNetFlux_dCanWat ! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) + real(rkind) :: dGroundNetFlux_dCanWat ! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) + integer(i4b) :: err ! intent(out): error code + character(:),allocatable :: cmessage ! intent(out): error message + contains + procedure :: finalize => finalize_out_vegNrgFlux end type out_type_vegNrgFlux ! ** end vegNrgFlux @@ -558,6 +563,127 @@ MODULE data_types ! ** end bigAquifer contains + + ! ** vegNrgFlux + subroutine initialize_in_vegNrgFlux(in_vegNrgFlux,firstSubStep,firstFluxCall,computeVegFlux,checkLWBalance,& + scalarCanairTempTrial,scalarCanopyTempTrial,mLayerTempTrial,scalarCanopyIceTrial,& + scalarCanopyLiqTrial,forc_data,deriv_data) + class(in_type_vegNrgFlux),intent(out) :: in_vegNrgFlux ! class object for intent(in) vegNrgFlux arguments + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(in) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: checkLWBalance ! flag to check longwave balance + real(rkind),intent(in) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind),intent(in) :: mLayerTempTrial(:) ! trial value for temperature of each snow/soil layer (K) + real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + type(var_d),intent(in) :: forc_data ! model forcing data + type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + associate(& + upperBoundTemp => forc_data%var(iLookFORCE%airtemp), & ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) + dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy)%dat(1) ) ! intent(out): [dp] derivative of canopy liquid storage w.r.t. temperature + ! intent(in) arguments + in_vegNrgFlux % firstSubStep=firstSubStep ! intent(in): flag to indicate if we are processing the first sub-step + in_vegNrgFlux % firstFluxCall=firstFluxCall ! intent(in): flag to indicate if we are processing the first flux call + in_vegNrgFlux % computeVegFlux=computeVegFlux ! intent(in): flag to indicate if we need to compute fluxes over vegetation + in_vegNrgFlux % checkLWBalance=checkLWBalance ! intent(in): flag to check longwave balance + in_vegNrgFlux % upperBoundTemp=upperBoundTemp ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature + in_vegNrgFlux % scalarCanairTempTrial=scalarCanairTempTrial ! intent(in): trial value of the canopy air space temperature (K) + in_vegNrgFlux % scalarCanopyTempTrial=scalarCanopyTempTrial ! intent(in): trial value of canopy temperature (K) + in_vegNrgFlux % mLayerTempTrial_1=mLayerTempTrial(1) ! intent(in): trial value of ground temperature (K) + in_vegNrgFlux % scalarCanopyIceTrial=scalarCanopyIceTrial ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) + in_vegNrgFlux % scalarCanopyLiqTrial=scalarCanopyLiqTrial ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) + in_vegNrgFlux % dCanLiq_dTcanopy=dCanLiq_dTcanopy ! intent(in): derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) + end associate + end subroutine initialize_in_vegNrgFlux + + subroutine finalize_out_vegNrgFlux(out_vegNrgFlux,flux_data,deriv_data,err,cmessage) + class(out_type_vegNrgFlux),intent(in) :: out_vegNrgFlux ! class object for intent(out) vegNrgFlux arguments + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: cmessage ! error message from ssdNrgFlux + + ! intent(out) arguments: evapotranspiration values and net energy fluxes + associate(& + scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1), & ! intent(out): [dp] canopy transpiration (kg m-2 s-1) + scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1), & ! intent(out): [dp] canopy evaporation/condensation (kg m-2 s-1) + scalarGroundEvaporation => flux_data%var(iLookFLUX%scalarGroundEvaporation)%dat(1), & ! intent(out): [dp] ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + scalarCanairNetNrgFlux => flux_data%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1), & ! intent(out): [dp] net energy flux for the canopy air space (W m-2) + scalarCanopyNetNrgFlux => flux_data%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1), & ! intent(out): [dp] net energy flux for the vegetation canopy (W m-2) + scalarGroundNetNrgFlux => flux_data%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1) ) ! intent(out): [dp] net energy flux for the ground surface (W m-2) + scalarCanopyTranspiration =out_vegNrgFlux % scalarCanopyTranspiration ! intent(out): canopy transpiration (kg m-2 s-1) + scalarCanopyEvaporation =out_vegNrgFlux % scalarCanopyEvaporation ! intent(out): canopy evaporation/condensation (kg m-2 s-1) + scalarGroundEvaporation =out_vegNrgFlux % scalarGroundEvaporation ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + scalarCanairNetNrgFlux =out_vegNrgFlux % scalarCanairNetNrgFlux ! intent(out): net energy flux for the canopy air space (W m-2) + scalarCanopyNetNrgFlux =out_vegNrgFlux % scalarCanopyNetNrgFlux ! intent(out): net energy flux for the vegetation canopy (W m-2) + scalarGroundNetNrgFlux =out_vegNrgFlux % scalarGroundNetNrgFlux ! intent(out): net energy flux for the ground surface (W m-2) + end associate + + ! intent(out) arguments: net canopy flux derivatives + associate(& + dCanairNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanairTemp)%dat(1), & ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy air temperature + dCanairNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanopyTemp)%dat(1), & ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy temperature + dCanairNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dGroundTemp)%dat(1), & ! intent(out): [dp] derivative in net canopy air space flux w.r.t. ground temperature + dCanopyNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp)%dat(1), & ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy air temperature + dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp)%dat(1), & ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy temperature + dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp)%dat(1), & ! intent(out): [dp] derivative in net canopy flux w.r.t. ground temperature + dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp)%dat(1), & ! intent(out): [dp] derivative in net ground flux w.r.t. canopy air temperature + dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp)%dat(1), & ! intent(out): [dp] derivative in net ground flux w.r.t. canopy temperature + dGroundNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dGroundTemp)%dat(1) ) ! intent(out): [dp] derivative in net ground flux w.r.t. ground temperature + dCanairNetFlux_dCanairTemp =out_vegNrgFlux % dCanairNetFlux_dCanairTemp ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) + dCanairNetFlux_dCanopyTemp =out_vegNrgFlux % dCanairNetFlux_dCanopyTemp ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) + dCanairNetFlux_dGroundTemp =out_vegNrgFlux % dCanairNetFlux_dGroundTemp ! intent(out): derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) + dCanopyNetFlux_dCanairTemp =out_vegNrgFlux % dCanopyNetFlux_dCanairTemp ! intent(out): derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) + dCanopyNetFlux_dCanopyTemp =out_vegNrgFlux % dCanopyNetFlux_dCanopyTemp ! intent(out): derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) + dCanopyNetFlux_dGroundTemp =out_vegNrgFlux % dCanopyNetFlux_dGroundTemp ! intent(out): derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) + dGroundNetFlux_dCanairTemp =out_vegNrgFlux % dGroundNetFlux_dCanairTemp ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) + dGroundNetFlux_dCanopyTemp =out_vegNrgFlux % dGroundNetFlux_dCanopyTemp ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) + dGroundNetFlux_dGroundTemp =out_vegNrgFlux % dGroundNetFlux_dGroundTemp ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + end associate + + ! intent(out) arguments: canopy evaporation derivatives + associate(& + dCanopyEvaporation_dCanWat => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanWat)%dat(1), & ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy total water content + dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair)%dat(1), & ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy air temperature + dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy)%dat(1), & ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy temperature + dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround)%dat(1), & ! intent(out): [dp] derivative in canopy evaporation w.r.t. ground temperature + dGroundEvaporation_dCanWat => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanWat)%dat(1), & ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy total water content + dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair)%dat(1), & ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy air temperature + dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy)%dat(1), & ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy temperature + dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround)%dat(1) ) ! intent(out): [dp] derivative in ground evaporation w.r.t. ground temperature + dCanopyEvaporation_dCanWat =out_vegNrgFlux % dCanopyEvaporation_dCanWat ! intent(out): derivative in canopy evaporation w.r.t. canopy total water content (s-1) + dCanopyEvaporation_dTCanair=out_vegNrgFlux % dCanopyEvaporation_dTCanair ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTCanopy=out_vegNrgFlux % dCanopyEvaporation_dTCanopy ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyEvaporation_dTGround=out_vegNrgFlux % dCanopyEvaporation_dTGround ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dCanWat =out_vegNrgFlux % dGroundEvaporation_dCanWat ! intent(out): derivative in ground evaporation w.r.t. canopy total water content (s-1) + dGroundEvaporation_dTCanair=out_vegNrgFlux % dGroundEvaporation_dTCanair ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTCanopy=out_vegNrgFlux % dGroundEvaporation_dTCanopy ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + dGroundEvaporation_dTGround=out_vegNrgFlux % dGroundEvaporation_dTGround ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + end associate + + ! intent(out) arguments: canopy transpiration and net flux derivatives + associate(& + dCanopyTrans_dCanWat => deriv_data%var(iLookDERIV%dCanopyTrans_dCanWat)%dat(1), & ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair => deriv_data%var(iLookDERIV%dCanopyTrans_dTCanair)%dat(1), & ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy => deriv_data%var(iLookDERIV%dCanopyTrans_dTCanopy)%dat(1), & ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround => deriv_data%var(iLookDERIV%dCanopyTrans_dTGround)%dat(1), & ! intent(out): [dp] derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + dCanopyNetFlux_dCanWat => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanWat)%dat(1), & ! intent(out): [dp] derivative in net canopy fluxes w.r.t. canopy total water content + dGroundNetFlux_dCanWat => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanWat)%dat(1) ) ! intent(out): [dp] derivative in net ground fluxes w.r.t. canopy total water content + dCanopyTrans_dCanWat =out_vegNrgFlux % dCanopyTrans_dCanWat ! intent(out): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair =out_vegNrgFlux % dCanopyTrans_dTCanair ! intent(out): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy =out_vegNrgFlux % dCanopyTrans_dTCanopy ! intent(out): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround =out_vegNrgFlux % dCanopyTrans_dTGround ! intent(out): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + dCanopyNetFlux_dCanWat =out_vegNrgFlux % dCanopyNetFlux_dCanWat! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) + dGroundNetFlux_dCanWat =out_vegNrgFlux % dGroundNetFlux_dCanWat! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) + end associate + + ! intent(out) arguments: error control + err =out_vegNrgFlux % err ! intent(out): error code + cmessage =out_vegNrgFlux % cmessage ! intent(out): error message + end subroutine finalize_out_vegNrgFlux + ! ** end vegNrgFlux ! ** ssdNrgFlux subroutine initialize_in_ssdNrgFlux(in_ssdNrgFlux,scalarSolution,firstFluxCall,mLayerTempTrial,flux_data,deriv_data) @@ -613,7 +739,7 @@ subroutine finalize_out_ssdNrgFlux(out_ssdNrgFlux,flux_data,deriv_data,err,cmess type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: cmessage ! error message from vegLiqFlux + character(*),intent(out) :: cmessage ! error message from ssdNrgFlux associate(& iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat, & ! intent(out): [dp(0:)] vertical energy flux at the interface of snow and soil layers dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove)%dat, & ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above @@ -717,7 +843,7 @@ end subroutine finalize_io_snowLiqFlx subroutine finalize_out_snowLiqFlx(out_snowLiqFlx,err,cmessage) class(out_type_snowLiqFlx),intent(in) :: out_snowLiqFlx ! class object for intent(out) arguments integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: cmessage ! error message from vegLiqFlux + character(*),intent(out) :: cmessage ! error message from snowLiqFlx ! intent(out) arguments err =out_snowLiqFlx % err ! intent(out): error code cmessage=out_snowLiqFlx % cmessage ! intent(out): error message @@ -769,7 +895,7 @@ subroutine finalize_out_groundwatr(out_groundwatr,dBaseflow_dMatric,flux_data,er real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: cmessage ! error message from vegLiqFlux + character(*),intent(out) :: cmessage ! error message from groundwatr associate(& mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ) ! intent(out): [dp(:)] baseflow from each soil layer (m s-1) ! intent(out) arguments @@ -842,7 +968,7 @@ subroutine finalize_out_bigAquifer(out_bigAquifer,flux_data,deriv_data,err,cmess type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: cmessage ! error message from vegLiqFlux + character(*),intent(out) :: cmessage ! error message from bigAquifer associate(& scalarAquiferTranspire => flux_data%var(iLookFLUX%scalarAquiferTranspire)%dat(1), & ! intent(out): [dp] transpiration loss from the aquifer (m s-1) scalarAquiferRecharge => flux_data%var(iLookFLUX%scalarAquiferRecharge)%dat(1), & ! intent(out): [dp] recharge to the aquifer (m s-1) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index f3533dd9d..2d1f06852 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -323,9 +323,9 @@ subroutine computFlux(& ! identify the need to calculate the energy flux over vegetation doVegNrgFlux = (ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing .or. ixTopNrg/=integerMissing) if (doVegNrgFlux) then ! if necessary, calculate the energy fluxes over vegetation - call subTools(iLookOP%pre,iLookROUTINE%vegNrgFlux) ! pre-processing for call to vegNrgFlux + call initialize_vegNrgFlux call vegNrgFlux(in_vegNrgFlux,type_data,forc_data,mpar_data,indx_data,prog_data,diag_data,flux_data,bvar_data,model_decisions,out_vegNrgFlux) - call subTools(iLookOP%post,iLookROUTINE%vegNrgFlux) ! post-processing for call to vegNrgFlux + call finalize_vegNrgFlux end if ! end if calculating the energy fluxes over vegetation ! *** CALCULATE ENERGY FLUXES THROUGH THE SNOW-SOIL DOMAIN *** @@ -397,7 +397,6 @@ subroutine computFlux(& scalarTotalRunoff = scalarSurfaceRunoff + scalarSoilDrainage + scalarSoilBaseflow end if ! end if computing soil hydrology - ! *** CALCULATE FLUXES FOR THE DEEP AQUIFER *** if (ixAqWat/=integerMissing) then ! check if computing aquifer fluxes if (local_ixGroundwater==bigBucket) then ! compute fluxes for the big bucket @@ -919,6 +918,46 @@ subroutine subTools(op,sub) end subroutine subTools + subroutine initialize_vegNrgFlux + associate(& + dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy)%dat(1), & ! intent(out): [dp] derivative of canopy liquid storage w.r.t. temperature + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1), & ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ) ! intent(in): [dp] canopy depth (m) + + dCanLiq_dTcanopy = dTheta_dTkCanopy*iden_water*canopyDepth ! derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) + end associate + call in_vegNrgFlux % initialize(firstSubStep,firstFluxCall,computeVegFlux,checkLWBalance,& + scalarCanairTempTrial,scalarCanopyTempTrial,mLayerTempTrial,scalarCanopyIceTrial,& + scalarCanopyLiqTrial,forc_data,deriv_data) + end subroutine initialize_vegNrgFlux + + subroutine finalize_vegNrgFlux + call out_vegNrgFlux%finalize(flux_data,deriv_data,err,cmessage) + associate(& + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + scalarCanairNetNrgFlux => flux_data%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1), & ! intent(out): [dp] net energy flux for the canopy air space (W m-2) + scalarCanopyNetNrgFlux => flux_data%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1), & ! intent(out): [dp] net energy flux for the vegetation canopy (W m-2) + scalarGroundNetNrgFlux => flux_data%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1), & ! intent(out): [dp] net energy flux for the ground surface (W m-2) + dGroundNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dGroundTemp)%dat(1) ) ! intent(out): [dp] derivative in net ground flux w.r.t. ground temperature + ! error control + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors + ! check fluxes + if (globalPrintFlag) then + print*, '**' + write(*,'(a,1x,10(f30.20))') 'canopyDepth = ', canopyDepth + write(*,'(a,1x,10(f30.20))') 'mLayerDepth(1:2) = ', mLayerDepth(1:2) + write(*,'(a,1x,10(f30.20))') 'scalarCanairTempTrial = ', scalarCanairTempTrial ! trial value of the canopy air space temperature (K) + write(*,'(a,1x,10(f30.20))') 'scalarCanopyTempTrial = ', scalarCanopyTempTrial ! trial value of canopy temperature (K) + write(*,'(a,1x,10(f30.20))') 'mLayerTempTrial(1:2) = ', mLayerTempTrial(1:2) ! trial value of ground temperature (K) + write(*,'(a,1x,10(f30.20))') 'scalarCanairNetNrgFlux = ', scalarCanairNetNrgFlux + write(*,'(a,1x,10(f30.20))') 'scalarCanopyNetNrgFlux = ', scalarCanopyNetNrgFlux + write(*,'(a,1x,10(f30.20))') 'scalarGroundNetNrgFlux = ', scalarGroundNetNrgFlux + write(*,'(a,1x,10(f30.20))') 'dGroundNetFlux_dGroundTemp = ', dGroundNetFlux_dGroundTemp + end if ! end if checking fluxes + end associate + end subroutine finalize_vegNrgFlux + subroutine initialize_ssdNrgFlux call in_ssdNrgFlux%initialize(scalarSolution,firstFluxCall,mLayerTempTrial,flux_data,deriv_data) call io_ssdNrgFlux%initialize(deriv_data) From 220b0f83ff52d037ad48407cc7410dc1fdb1a1ac Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 4 Oct 2023 21:04:09 -0600 Subject: [PATCH 0944/1472] Applied object-oriented methods to simplify the call to soilLiqFlx in computFlux. --- build/source/dshare/data_types.f90 | 309 +++++++++++++++++++++++++---- build/source/engine/computFlux.f90 | 64 +++++- 2 files changed, 327 insertions(+), 46 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index b5ba49d3d..6a125d49c 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -26,6 +26,7 @@ MODULE data_types USE var_lookup,only:iLookFLUX ! lookup indices for flux data USE var_lookup,only:iLookDERIV ! lookup indices for derivative data USE var_lookup,only:iLookFORCE ! lookup indices for forcing data + USE var_lookup,only:iLookDIAG ! lookup indices for diagnostic variable data implicit none ! constants necessary for variable defs private @@ -443,56 +444,63 @@ MODULE data_types ! ** soilLiqFlx type, public :: in_type_soilLiqFlx ! derived type for intent(in) arguments in soilLiqFlx call - integer(i4b) :: nSoil ! intent(in): number of soil layers - logical(lgt) :: firstSplitOper ! intent(in): flag indicating first flux call in a splitting operation - logical(lgt) :: scalarSolution ! intent(in): flag to indicate the scalar solution - logical(lgt) :: deriv_desired ! intent(in): flag indicating if derivatives are desired - real(rkind), allocatable :: mLayerTempTrial(:) ! intent(in): trial temperature at the current iteration (K) - real(rkind), allocatable :: mLayerMatricHeadTrial(:) ! intent(in): matric potential (m) - real(rkind), allocatable :: mLayerMatricHeadLiqTrial(:) ! intent(in): liquid water matric potential (m) - real(rkind), allocatable :: mLayerVolFracLiqTrial(:) ! intent(in): volumetric fraction of liquid water (-) - real(rkind), allocatable :: mLayerVolFracIceTrial(:) ! intent(in): volumetric fraction of ice (-) - real(rkind), allocatable :: mLayerdTheta_dTk(:) ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - real(rkind), allocatable :: dPsiLiq_dTemp(:) ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) - real(rkind) :: dCanopyTrans_dCanWat ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - real(rkind) :: dCanopyTrans_dTCanair ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - real(rkind) :: dCanopyTrans_dTCanopy ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - real(rkind) :: dCanopyTrans_dTGround ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - real(rkind) :: above_soilLiqFluxDeriv ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water - real(rkind) :: above_soildLiq_dTk ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature - real(rkind) :: above_soilFracLiq ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) - real(rkind) :: scalarCanopyTranspiration ! intent(in): canopy transpiration (kg m-2 s-1) - real(rkind) :: scalarGroundEvaporation ! intent(in): ground evaporation (kg m-2 s-1) - real(rkind) :: scalarRainPlusMelt ! intent(in): rain plus melt (m s-1) + integer(i4b) :: nSoil ! intent(in): number of soil layers + logical(lgt) :: firstSplitOper ! intent(in): flag indicating first flux call in a splitting operation + logical(lgt) :: scalarSolution ! intent(in): flag to indicate the scalar solution + logical(lgt) :: deriv_desired ! intent(in): flag indicating if derivatives are desired + real(rkind), allocatable :: mLayerTempTrial(:) ! intent(in): trial temperature at the current iteration (K) + real(rkind), allocatable :: mLayerMatricHeadTrial(:) ! intent(in): matric potential (m) + real(rkind), allocatable :: mLayerMatricHeadLiqTrial(:) ! intent(in): liquid water matric potential (m) + real(rkind), allocatable :: mLayerVolFracLiqTrial(:) ! intent(in): volumetric fraction of liquid water (-) + real(rkind), allocatable :: mLayerVolFracIceTrial(:) ! intent(in): volumetric fraction of ice (-) + real(rkind), allocatable :: mLayerdTheta_dTk(:) ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + real(rkind), allocatable :: dPsiLiq_dTemp(:) ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) + real(rkind) :: dCanopyTrans_dCanWat ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + real(rkind) :: dCanopyTrans_dTCanair ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + real(rkind) :: dCanopyTrans_dTCanopy ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + real(rkind) :: dCanopyTrans_dTGround ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + real(rkind) :: above_soilLiqFluxDeriv ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + real(rkind) :: above_soildLiq_dTk ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + real(rkind) :: above_soilFracLiq ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) + real(rkind) :: scalarCanopyTranspiration ! intent(in): canopy transpiration (kg m-2 s-1) + real(rkind) :: scalarGroundEvaporation ! intent(in): ground evaporation (kg m-2 s-1) + real(rkind) :: scalarRainPlusMelt ! intent(in): rain plus melt (m s-1) + contains + procedure :: initialize => initialize_in_soilLiqFlx end type in_type_soilLiqFlx type, public :: io_type_soilLiqFlx ! derived type for intent(inout) arguments in soilLiqFlx call - real(rkind) :: scalarMaxInfilRate ! intent(inout): maximum infiltration rate (m s-1) - real(rkind) :: scalarInfilArea ! intent(inout): fraction of unfrozen area where water can infiltrate (-) - real(rkind) :: scalarFrozenArea ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) - real(rkind) :: scalarSurfaceRunoff ! intent(inout): surface runoff (m s-1) - real(rkind), allocatable :: mLayerdTheta_dPsi(:) ! intent(inout): derivative in the soil water characteristic w.r.t. psi (m-1) - real(rkind), allocatable :: mLayerdPsi_dTheta(:) ! intent(inout): derivative in the soil water characteristic w.r.t. theta (m) - real(rkind), allocatable :: dHydCond_dMatric(:) ! intent(inout): derivative in hydraulic conductivity w.r.t matric head (s-1) - real(rkind) :: scalarInfiltration ! intent(inout): surface infiltration rate (m s-1) -- controls on infiltration only computed for iter==1 - real(rkind), allocatable :: iLayerLiqFluxSoil(:) ! intent(inout): liquid fluxes at layer interfaces (m s-1) - real(rkind), allocatable :: mLayerTranspire(:) ! intent(inout): transpiration loss from each soil layer (m s-1) - real(rkind), allocatable :: mLayerHydCond(:) ! intent(inout): hydraulic conductivity in each layer (m s-1) - real(rkind), allocatable :: dq_dHydStateAbove(:) ! intent(inout): derivatives in the flux w.r.t. matric head in the layer above (s-1) - real(rkind), allocatable :: dq_dHydStateBelow(:) ! intent(inout): derivatives in the flux w.r.t. matric head in the layer below (s-1) - real(rkind), allocatable :: dq_dHydStateLayerSurfVec(:) ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) - real(rkind), allocatable :: dq_dNrgStateAbove(:) ! intent(inout): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) - real(rkind), allocatable :: dq_dNrgStateBelow(:) ! intent(inout): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) - real(rkind), allocatable :: dq_dNrgStateLayerSurfVec(:) ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) - real(rkind), allocatable :: mLayerdTrans_dTCanair(:) ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature - real(rkind), allocatable :: mLayerdTrans_dTCanopy(:) ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy temperature - real(rkind), allocatable :: mLayerdTrans_dTGround(:) ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. ground temperature - real(rkind), allocatable :: mLayerdTrans_dCanWat(:) ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy total water + real(rkind) :: scalarMaxInfilRate ! intent(inout): maximum infiltration rate (m s-1) + real(rkind) :: scalarInfilArea ! intent(inout): fraction of unfrozen area where water can infiltrate (-) + real(rkind) :: scalarFrozenArea ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) + real(rkind) :: scalarSurfaceRunoff ! intent(inout): surface runoff (m s-1) + real(rkind), allocatable :: mLayerdTheta_dPsi(:) ! intent(inout): derivative in the soil water characteristic w.r.t. psi (m-1) + real(rkind), allocatable :: mLayerdPsi_dTheta(:) ! intent(inout): derivative in the soil water characteristic w.r.t. theta (m) + real(rkind), allocatable :: dHydCond_dMatric(:) ! intent(inout): derivative in hydraulic conductivity w.r.t matric head (s-1) + real(rkind) :: scalarInfiltration ! intent(inout): surface infiltration rate (m s-1) -- controls on infiltration only computed for iter==1 + real(rkind), allocatable :: iLayerLiqFluxSoil(:) ! intent(inout): liquid fluxes at layer interfaces (m s-1) + real(rkind), allocatable :: mLayerTranspire(:) ! intent(inout): transpiration loss from each soil layer (m s-1) + real(rkind), allocatable :: mLayerHydCond(:) ! intent(inout): hydraulic conductivity in each layer (m s-1) + real(rkind), allocatable :: dq_dHydStateAbove(:) ! intent(inout): derivatives in the flux w.r.t. matric head in the layer above (s-1) + real(rkind), allocatable :: dq_dHydStateBelow(:) ! intent(inout): derivatives in the flux w.r.t. matric head in the layer below (s-1) + real(rkind), allocatable :: dq_dHydStateLayerSurfVec(:) ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) + real(rkind), allocatable :: dq_dNrgStateAbove(:) ! intent(inout): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) + real(rkind), allocatable :: dq_dNrgStateBelow(:) ! intent(inout): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) + real(rkind), allocatable :: dq_dNrgStateLayerSurfVec(:) ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) + real(rkind), allocatable :: mLayerdTrans_dTCanair(:) ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + real(rkind), allocatable :: mLayerdTrans_dTCanopy(:) ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy temperature + real(rkind), allocatable :: mLayerdTrans_dTGround(:) ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. ground temperature + real(rkind), allocatable :: mLayerdTrans_dCanWat(:) ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy total water + contains + procedure :: initialize => initialize_io_soilLiqFlx + procedure :: finalize => finalize_io_soilLiqFlx end type io_type_soilLiqFlx type, public :: out_type_soilLiqFlx ! derived type for intent(out) arguments in soilLiqFlx call - integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + integer(i4b) :: err ! intent(out): error code + character(:),allocatable :: cmessage ! intent(out): error message + contains + procedure :: finalize => finalize_out_soilLiqFlx end type out_type_soilLiqFlx ! ** end soilLiqFlx @@ -850,6 +858,219 @@ subroutine finalize_out_snowLiqFlx(out_snowLiqFlx,err,cmessage) end subroutine finalize_out_snowLiqFlx ! ** end snowLiqFlx + ! ** soilLiqFlx + subroutine initialize_in_soilLiqFlx(in_soilLiqFlx,nsnow,nSoil,nlayers,firstSplitOper,scalarSolution,firstFluxCall,& + mLayerTempTrial,mLayerMatricHeadTrial,mLayerMatricHeadLiqTrial,mLayerVolFracLiqTrial,mLayerVolFracIceTrial,& + above_soilLiqFluxDeriv,above_soildLiq_dTk,above_soilFracLiq,flux_data,deriv_data) + class(in_type_soilLiqFlx),intent(out) :: in_soilLiqFlx ! class object for intent(in) arguments + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + logical(lgt),intent(in) :: firstFluxCall ! flag to indicate if we are processing the first flux call + real(rkind),intent(in) :: mLayerTempTrial(:) ! trial value for temperature of each snow/soil layer (K) + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial value for the total water matric potential (m) + real(rkind),intent(in) :: mLayerMatricHeadLiqTrial(:) ! trial value for the liquid water matric potential (m) + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) + real(rkind),intent(in) :: above_soilLiqFluxDeriv ! derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + real(rkind),intent(in) :: above_soildLiq_dTk ! derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + real(rkind),intent(in) :: above_soilFracLiq ! fraction of liquid water layer above soil (canopy or snow) (-) + type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + + ! intent(in) arguments: model control + in_soilLiqFlx % nSoil =nSoil ! intent(in): number of soil layers + in_soilLiqFlx % firstSplitOper=firstSplitOper ! intent(in): flag indicating first flux call in a splitting operation + in_soilLiqFlx % scalarSolution=(scalarSolution .and. .not.firstFluxCall) ! intent(in): flag to indicate the scalar solution + in_soilLiqFlx % deriv_desired =.true. ! intent(in): flag indicating if derivatives are desired + + ! intent(in) arguments: trial temperature, matric potential, and volumetric fractions + in_soilLiqFlx % mLayerTempTrial=mLayerTempTrial(nSnow+1:nLayers) ! intent(in): trial temperature at the current iteration (K) + in_soilLiqFlx % mLayerMatricHeadTrial =mLayerMatricHeadTrial(1:nSoil) ! intent(in): matric potential (m) + in_soilLiqFlx % mLayerMatricHeadLiqTrial=mLayerMatricHeadLiqTrial(1:nSoil) ! intent(in): liquid water matric potential (m) + in_soilLiqFlx % mLayerVolFracLiqTrial=mLayerVolFracLiqTrial(nSnow+1:nLayers) ! intent(in): volumetric fraction of liquid water (-) + in_soilLiqFlx % mLayerVolFracIceTrial=mLayerVolFracIceTrial(nSnow+1:nLayers) ! intent(in): volumetric fraction of ice (-) + + ! intent(in) arguments: derivatives for liquid water + associate(& + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat, & ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp)%dat ) ! intent(in): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature + in_soilLiqFlx % mLayerdTheta_dTk=mLayerdTheta_dTk(nSnow+1:nLayers) ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + in_soilLiqFlx % dPsiLiq_dTemp=dPsiLiq_dTemp(1:nSoil) ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) + end associate + + ! intent(in) arguments: canopy transpiration derivatives + associate(& + dCanopyTrans_dCanWat => deriv_data%var(iLookDERIV%dCanopyTrans_dCanWat)%dat(1), & ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy total water content (s-1) + dCanopyTrans_dTCanair => deriv_data%var(iLookDERIV%dCanopyTrans_dTCanair)%dat(1), & ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTCanopy => deriv_data%var(iLookDERIV%dCanopyTrans_dTCanopy)%dat(1), & ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + dCanopyTrans_dTGround => deriv_data%var(iLookDERIV%dCanopyTrans_dTGround)%dat(1) ) ! intent(out): [dp] derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + in_soilLiqFlx % dCanopyTrans_dCanWat =dCanopyTrans_dCanWat ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) + in_soilLiqFlx % dCanopyTrans_dTCanair =dCanopyTrans_dTCanair ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + in_soilLiqFlx % dCanopyTrans_dTCanopy =dCanopyTrans_dTCanopy ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + in_soilLiqFlx % dCanopyTrans_dTGround =dCanopyTrans_dTGround ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + end associate + + ! intent(in) arguments: above soil liquid flux derivatives and liquid water fraction + in_soilLiqFlx % above_soilLiqFluxDeriv=above_soilLiqFluxDeriv ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water + in_soilLiqFlx % above_soildLiq_dTk =above_soildLiq_dTk ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature + in_soilLiqFlx % above_soilFracLiq =above_soilFracLiq ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) + + ! intent(in) arguments: evaporative fluxes and rain plus melt + associate(& + scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1), & ! intent(out): [dp] canopy transpiration (kg m-2 s-1) + scalarGroundEvaporation => flux_data%var(iLookFLUX%scalarGroundEvaporation)%dat(1), & ! intent(out): [dp] ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) + scalarRainPlusMelt => flux_data%var(iLookFLUX%scalarRainPlusMelt)%dat(1) ) ! intent(out): [dp] rain plus melt (m s-1) + in_soilLiqFlx % scalarCanopyTranspiration=scalarCanopyTranspiration ! intent(in): canopy transpiration (kg m-2 s-1) + in_soilLiqFlx % scalarGroundEvaporation =scalarGroundEvaporation ! intent(in): ground evaporation (kg m-2 s-1) + in_soilLiqFlx % scalarRainPlusMelt =scalarRainPlusMelt ! intent(in): rain plus melt (m s-1) + end associate + end subroutine initialize_in_soilLiqFlx + + subroutine initialize_io_soilLiqFlx(io_soilLiqFlx,nsoil,dHydCond_dMatric,flux_data,diag_data,deriv_data) + class(io_type_soilLiqFlx),intent(out) :: io_soilLiqFlx ! class object for intent(inout) arguments + integer(i4b),intent(in) :: nSoil ! number of soil layers + real(rkind),intent(in) :: dHydCond_dMatric(nSoil) ! derivative in hydraulic conductivity w.r.t matric head (s-1) + type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + + ! intent(inout) arguments: max infiltration rate, frozen area, and surface runoff + associate(& + scalarMaxInfilRate => flux_data%var(iLookFLUX%scalarMaxInfilRate)%dat(1), & ! intent(out): [dp] maximum infiltration rate (m s-1) + scalarInfilArea => diag_data%var(iLookDIAG%scalarInfilArea )%dat(1), & ! intent(out): [dp] fraction of unfrozen area where water can infiltrate (-) + scalarFrozenArea => diag_data%var(iLookDIAG%scalarFrozenArea )%dat(1), & ! intent(out): [dp] fraction of area that is considered impermeable due to soil ice (-) + scalarSurfaceRunoff => flux_data%var(iLookFLUX%scalarSurfaceRunoff)%dat(1) ) ! intent(out): [dp] surface runoff (m s-1) + io_soilLiqFlx % scalarMaxInfilRate =scalarMaxInfilRate ! intent(inout): maximum infiltration rate (m s-1) + io_soilLiqFlx % scalarInfilArea =scalarInfilArea ! intent(inout): fraction of unfrozen area where water can infiltrate (-) + io_soilLiqFlx % scalarFrozenArea =scalarFrozenArea ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) + io_soilLiqFlx % scalarSurfaceRunoff =scalarSurfaceRunoff ! intent(inout): surface runoff (m s-1) + end associate + + ! intent(inout) arguments: derivatives, fluxes, and layer properties + associate(& + mLayerdTheta_dPsi => deriv_data%var(iLookDERIV%mLayerdTheta_dPsi)%dat, & ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. psi + mLayerdPsi_dTheta => deriv_data%var(iLookDERIV%mLayerdPsi_dTheta)%dat, & ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. theta + scalarInfiltration => flux_data%var(iLookFLUX%scalarInfiltration)%dat(1), & ! intent(out): [dp] infiltration of water into the soil profile (m s-1) + iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat, & ! intent(out): [dp(0:)] vertical liquid water flux at soil layer interfaces (-) + mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat, & ! intent(out): [dp(:)] transpiration loss from each soil layer (m s-1) + mLayerHydCond => flux_data%var(iLookFLUX%mLayerHydCond)%dat ) ! intent(out): [dp(:)] hydraulic conductivity in each soil layer (m s-1) + io_soilLiqFlx % mLayerdTheta_dPsi =mLayerdTheta_dPsi ! intent(inout): derivative in the soil water characteristic w.r.t. psi (m-1) + io_soilLiqFlx % mLayerdPsi_dTheta =mLayerdPsi_dTheta ! intent(inout): derivative in the soil water characteristic w.r.t. theta (m) + io_soilLiqFlx % dHydCond_dMatric =dHydCond_dMatric ! intent(inout): derivative in hydraulic conductivity w.r.t matric head (s-1) + io_soilLiqFlx % scalarInfiltration =scalarInfiltration ! intent(inout): surface infiltration rate (m s-1) -- controls on infiltration only computed for iter==1 + io_soilLiqFlx % iLayerLiqFluxSoil =iLayerLiqFluxSoil ! intent(inout): liquid fluxes at layer interfaces (m s-1) + io_soilLiqFlx % mLayerTranspire =mLayerTranspire ! intent(inout): transpiration loss from each soil layer (m s-1) + io_soilLiqFlx % mLayerHydCond =mLayerHydCond ! intent(inout): hydraulic conductivity in each layer (m s-1) + end associate + + ! intent(inout) arguments: flux and surface infiltration derivatives + associate(& + dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove)%dat, & ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above + dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow)%dat, & ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + dq_dHydStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dHydStateLayerSurfVec)%dat, & ! intent(out): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers + dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove)%dat, & ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above + dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow)%dat, & ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + dq_dNrgStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dNrgStateLayerSurfVec)%dat ) ! intent(out): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers + io_soilLiqFlx % dq_dHydStateAbove =dq_dHydStateAbove ! intent(inout): derivatives in the flux w.r.t. matric head in the layer above (s-1) + io_soilLiqFlx % dq_dHydStateBelow =dq_dHydStateBelow ! intent(inout): derivatives in the flux w.r.t. matric head in the layer below (s-1) + io_soilLiqFlx % dq_dHydStateLayerSurfVec=dq_dHydStateLayerSurfVec ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) + io_soilLiqFlx % dq_dNrgStateAbove =dq_dNrgStateAbove ! intent(inout): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) + io_soilLiqFlx % dq_dNrgStateBelow =dq_dNrgStateBelow ! intent(inout): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) + io_soilLiqFlx % dq_dNrgStateLayerSurfVec=dq_dNrgStateLayerSurfVec ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) + end associate + + ! intent(inout) arguments: transpiration flux derivatives + associate(& + mLayerdTrans_dTCanair => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanair)%dat, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + mLayerdTrans_dTCanopy => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanopy)%dat, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy temperature + mLayerdTrans_dTGround => deriv_data%var(iLookDERIV%mLayerdTrans_dTGround)%dat, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. ground temperature + mLayerdTrans_dCanWat => deriv_data%var(iLookDERIV%mLayerdTrans_dCanWat)%dat ) ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy total water + io_soilLiqFlx % mLayerdTrans_dTCanair =mLayerdTrans_dTCanair ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + io_soilLiqFlx % mLayerdTrans_dTCanopy =mLayerdTrans_dTCanopy ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy temperature + io_soilLiqFlx % mLayerdTrans_dTGround =mLayerdTrans_dTGround ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. ground temperature + io_soilLiqFlx % mLayerdTrans_dCanWat =mLayerdTrans_dCanWat ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy total water + end associate + end subroutine initialize_io_soilLiqFlx + + subroutine finalize_io_soilLiqFlx(io_soilLiqFlx,nsoil,dHydCond_dMatric,flux_data,diag_data,deriv_data) + class(io_type_soilLiqFlx),intent(in) :: io_soilLiqFlx ! class object for intent(inout) arguments + integer(i4b),intent(in) :: nSoil ! number of soil layers + real(rkind),intent(out) :: dHydCond_dMatric(nSoil) ! derivative in hydraulic conductivity w.r.t matric head (s-1) + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + + ! intent(inout) arguments: max infiltration rate, frozen area, and surface runoff + associate(& + scalarMaxInfilRate => flux_data%var(iLookFLUX%scalarMaxInfilRate)%dat(1), & ! intent(out): [dp] maximum infiltration rate (m s-1) + scalarInfilArea => diag_data%var(iLookDIAG%scalarInfilArea )%dat(1), & ! intent(out): [dp] fraction of unfrozen area where water can infiltrate (-) + scalarFrozenArea => diag_data%var(iLookDIAG%scalarFrozenArea )%dat(1), & ! intent(out): [dp] fraction of area that is considered impermeable due to soil ice (-) + scalarSurfaceRunoff => flux_data%var(iLookFLUX%scalarSurfaceRunoff)%dat(1) ) ! intent(out): [dp] surface runoff (m s-1) + scalarMaxInfilRate =io_soilLiqFlx % scalarMaxInfilRate ! intent(inout): maximum infiltration rate (m s-1) + scalarInfilArea =io_soilLiqFlx % scalarInfilArea ! intent(inout): fraction of unfrozen area where water can infiltrate (-) + scalarFrozenArea =io_soilLiqFlx % scalarFrozenArea ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) + scalarSurfaceRunoff =io_soilLiqFlx % scalarSurfaceRunoff ! intent(inout): surface runoff (m s-1) + end associate + + ! intent(inout) arguments: derivatives, fluxes, and layer properties + associate(& + mLayerdTheta_dPsi => deriv_data%var(iLookDERIV%mLayerdTheta_dPsi)%dat, & ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. psi + mLayerdPsi_dTheta => deriv_data%var(iLookDERIV%mLayerdPsi_dTheta)%dat, & ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. theta + scalarInfiltration => flux_data%var(iLookFLUX%scalarInfiltration)%dat(1), & ! intent(out): [dp] infiltration of water into the soil profile (m s-1) + iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat, & ! intent(out): [dp(0:)] vertical liquid water flux at soil layer interfaces (-) + mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat, & ! intent(out): [dp(:)] transpiration loss from each soil layer (m s-1) + mLayerHydCond => flux_data%var(iLookFLUX%mLayerHydCond)%dat ) ! intent(out): [dp(:)] hydraulic conductivity in each soil layer (m s-1) + mLayerdTheta_dPsi =io_soilLiqFlx % mLayerdTheta_dPsi ! intent(inout): derivative in the soil water characteristic w.r.t. psi (m-1) + mLayerdPsi_dTheta =io_soilLiqFlx % mLayerdPsi_dTheta ! intent(inout): derivative in the soil water characteristic w.r.t. theta (m) + dHydCond_dMatric =io_soilLiqFlx % dHydCond_dMatric ! intent(inout): derivative in hydraulic conductivity w.r.t matric head (s-1) + scalarInfiltration =io_soilLiqFlx % scalarInfiltration ! intent(inout): surface infiltration rate (m s-1) -- controls on infiltration only computed for iter==1 + iLayerLiqFluxSoil =io_soilLiqFlx % iLayerLiqFluxSoil ! intent(inout): liquid fluxes at layer interfaces (m s-1) + mLayerTranspire =io_soilLiqFlx % mLayerTranspire ! intent(inout): transpiration loss from each soil layer (m s-1) + mLayerHydCond =io_soilLiqFlx % mLayerHydCond ! intent(inout): hydraulic conductivity in each layer (m s-1) + end associate + + ! intent(inout) arguments: flux and surface infiltration derivatives + associate(& + dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove)%dat, & ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above + dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow)%dat, & ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + dq_dHydStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dHydStateLayerSurfVec)%dat, & ! intent(out): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers + dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove)%dat, & ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above + dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow)%dat, & ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + dq_dNrgStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dNrgStateLayerSurfVec)%dat ) ! intent(out): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers + dq_dHydStateAbove =io_soilLiqFlx % dq_dHydStateAbove ! intent(inout): derivatives in the flux w.r.t. matric head in the layer above (s-1) + dq_dHydStateBelow =io_soilLiqFlx % dq_dHydStateBelow ! intent(inout): derivatives in the flux w.r.t. matric head in the layer below (s-1) + dq_dHydStateLayerSurfVec=io_soilLiqFlx % dq_dHydStateLayerSurfVec ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) + dq_dNrgStateAbove =io_soilLiqFlx % dq_dNrgStateAbove ! intent(inout): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) + dq_dNrgStateBelow =io_soilLiqFlx % dq_dNrgStateBelow ! intent(inout): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) + dq_dNrgStateLayerSurfVec=io_soilLiqFlx % dq_dNrgStateLayerSurfVec ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) + end associate + + ! intent(inout) arguments: transpiration flux derivatives + associate(& + mLayerdTrans_dTCanair => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanair)%dat, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + mLayerdTrans_dTCanopy => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanopy)%dat, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy temperature + mLayerdTrans_dTGround => deriv_data%var(iLookDERIV%mLayerdTrans_dTGround)%dat, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. ground temperature + mLayerdTrans_dCanWat => deriv_data%var(iLookDERIV%mLayerdTrans_dCanWat)%dat ) ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy total water + mLayerdTrans_dTCanair =io_soilLiqFlx % mLayerdTrans_dTCanair ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + mLayerdTrans_dTCanopy =io_soilLiqFlx % mLayerdTrans_dTCanopy ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy temperature + mLayerdTrans_dTGround =io_soilLiqFlx % mLayerdTrans_dTGround ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. ground temperature + mLayerdTrans_dCanWat =io_soilLiqFlx % mLayerdTrans_dCanWat ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy total water + end associate + end subroutine finalize_io_soilLiqFlx + + subroutine finalize_out_soilLiqFlx(out_soilLiqFlx,err,cmessage) + class(out_type_soilLiqFlx),intent(in) :: out_soilLiqFlx ! class object for intent(out) arguments + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: cmessage ! error message from groundwatr + ! intent(out) arguments + err =out_soilLiqFlx % err ! intent(out): error code + cmessage =out_soilLiqFlx % cmessage ! intent(out): error message + end subroutine finalize_out_soilLiqFlx + ! ** end soilLiqFlx + ! ** groundwatr subroutine initialize_in_groundwatr(in_groundwatr,nSnow,nSoil,nLayers,firstFluxCall,mLayerMatricHeadLiqTrial,mLayerVolFracLiqTrial,mLayerVolFracIceTrial,deriv_data) class(in_type_groundwatr),intent(out) :: in_groundwatr ! class object for intent(in) arguments diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 2d1f06852..d1f3ed48c 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -372,9 +372,9 @@ subroutine computFlux(& ! *** CALCULATE THE LIQUID FLUX THROUGH SOIL *** if (nSoilOnlyHyd>0) then ! if necessary, calculate the liquid flux through soil - call subTools(iLookOP%pre,iLookROUTINE%soilLiqFlx) ! pre-processing for call to soilLiqFlx + call initialize_soilLiqFlx call soilLiqFlx(in_soilLiqFlx,mpar_data,indx_data,prog_data,diag_data,flux_data,io_soilLiqFlx,out_soilLiqFlx) - call subTools(iLookOP%post,iLookROUTINE%soilLiqFlx) ! post-processing for call to soilLiqFlx + call finalize_soilLiqFlx end if ! end if calculating the liquid flux through soil ! *** CALCULATE THE GROUNDWATER FLOW *** @@ -1050,6 +1050,66 @@ subroutine finalize_snowLiqFlx end associate end subroutine finalize_snowLiqFlx + subroutine initialize_soilLiqFlx + call in_soilLiqFlx%initialize(nsnow,nSoil,nlayers,firstSplitOper,scalarSolution,firstFluxCall,& + mLayerTempTrial,mLayerMatricHeadTrial,mLayerMatricHeadLiqTrial,mLayerVolFracLiqTrial,mLayerVolFracIceTrial,& + above_soilLiqFluxDeriv,above_soildLiq_dTk,above_soilFracLiq,flux_data,deriv_data) + call io_soilLiqFlx%initialize(nsoil,dHydCond_dMatric,flux_data,diag_data,deriv_data) + end subroutine initialize_soilLiqFlx + + subroutine finalize_soilLiqFlx + call io_soilLiqFlx%finalize(nsoil,dHydCond_dMatric,flux_data,diag_data,deriv_data) + call out_soilLiqFlx%finalize(err,cmessage) + ! error control + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + + associate(& + mLayerLiqFluxSoil => flux_data%var(iLookFLUX%mLayerLiqFluxSoil)%dat, & ! intent(out): [dp] net liquid water flux for each soil layer (s-1) + iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat, & ! intent(out): [dp(0:)] vertical liquid water flux at soil layer interfaces (-) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + scalarMaxInfilRate => flux_data%var(iLookFLUX%scalarMaxInfilRate)%dat(1), & ! intent(out): [dp] maximum infiltration rate (m s-1) + scalarRainPlusMelt => flux_data%var(iLookFLUX%scalarRainPlusMelt)%dat(1), & ! intent(out): [dp] rain plus melt (m s-1) + scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1), & ! intent(out): [dp] soil control on infiltration, zero or one + scalarInfilArea => diag_data%var(iLookDIAG%scalarInfilArea )%dat(1), & ! intent(out): [dp] fraction of unfrozen area where water can infiltrate (-) + scalarFrozenArea => diag_data%var(iLookDIAG%scalarFrozenArea )%dat(1), & ! intent(out): [dp] fraction of area that is considered impermeable due to soil ice (-) + scalarSoilDrainage => flux_data%var(iLookFLUX%scalarSoilDrainage)%dat(1) ) ! intent(out): [dp] drainage from the soil profile (m s-1) + + ! calculate net liquid water fluxes for each soil layer (s-1) + do iLayer=1,nSoil + mLayerLiqFluxSoil(iLayer) = -(iLayerLiqFluxSoil(iLayer) - iLayerLiqFluxSoil(iLayer-1))/mLayerDepth(iLayer+nSnow) + end do + + ! calculate the soil control on infiltration + if (nSnow==0) then + ! * case of infiltration into soil + if (scalarMaxInfilRate > scalarRainPlusMelt) then ! infiltration is not rate-limited + scalarSoilControl = (1._rkind - scalarFrozenArea)*scalarInfilArea + else + scalarSoilControl = 0._rkind ! (scalarRainPlusMelt exceeds maximum infiltration rate + end if + else + ! * case of infiltration into snow + scalarSoilControl = 1._rkind + end if + + ! compute drainage from the soil zone (needed for mass balance checks and in aquifer recharge) + scalarSoilDrainage = iLayerLiqFluxSoil(nSoil) + end associate + + associate(& + dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove)%dat, & ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above + dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow)%dat, & ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below + dq_dHydStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dHydStateLayerSurfVec)%dat, & ! intent(out): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers + dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ) ! intent(in): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) + ! expand derivatives to the total water matric potential + ! NOTE: arrays are offset because computing derivatives in interface fluxes, at the top and bottom of the layer respectively + if (globalPrintFlag) print*, 'dPsiLiq_dPsi0(1:nSoil) = ', dPsiLiq_dPsi0(1:nSoil) + dq_dHydStateAbove(1:nSoil) = dq_dHydStateAbove(1:nSoil) *dPsiLiq_dPsi0(1:nSoil) + dq_dHydStateBelow(0:nSoil-1) = dq_dHydStateBelow(0:nSoil-1)*dPsiLiq_dPsi0(1:nSoil) + dq_dHydStateLayerSurfVec(1:nSoil) = dq_dHydStateLayerSurfVec(1:nSoil)*dPsiLiq_dPsi0(1:nSoil) + end associate + end subroutine finalize_soilLiqFlx + subroutine initialize_groundwatr ! check the derivative matrix is sized appropriately if (size(dBaseflow_dMatric,1)/=nSoil .or. size(dBaseflow_dMatric,2)/=nSoil) then From a92b2df233527828c2c7995c86be982440bb1196 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 6 Oct 2023 00:24:37 +0900 Subject: [PATCH 0945/1472] fix stat utils --- utils/process_stat.sh | 6 ++-- utils/timeseries_to_statistics.py | 46 +++++++++++++++++++++++-------- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/utils/process_stat.sh b/utils/process_stat.sh index ce1b2053b..249c1de80 100755 --- a/utils/process_stat.sh +++ b/utils/process_stat.sh @@ -1,7 +1,7 @@ #!/bin/bash #SBATCH --ntasks-per-node=1 -#SBATCH --cpus-per-task=32 -#SBATCH --mem-per-cpu=3GB +#SBATCH --cpus-per-task=20 +#SBATCH --mem-per-cpu=6GB #SBATCH --time=1-00:00 #SBATCH --job-name=STAT #SBATCH --mail-user=gwu479@usask.ca @@ -22,4 +22,4 @@ pip install --no-index netCDF4 python timeseries_to_statistics.py sundials_1en6 python timeseries_to_statistics.py be1 python timeseries_to_statistics.py be32 -python timeseries_to_statistics.py be64 +python timeseries_to_statistics.py be16 diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 7f08848bf..96bbf56d7 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -21,19 +21,25 @@ import xarray as xr from pathlib import Path import numpy as np -import sys import warnings warnings.simplefilter("ignore") #deal with correlation warnings from variance 0 in kgem, both have no snow # Settings bench_name = 'sundials_1en8' -top_fold = '/home/avanb/scratch/' +not_parallel = False # should usually be false, runs faster testing = False -# The first input argument specifies the run where the files are -method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) +if testing: + top_fold = '/Users/amedin/Research/USask/test_py/' + method_name='be1' + not_parallel = True +else: + import sys + # The first input argument specifies the run where the files are + method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) + top_fold = '/home/avanb/scratch/' des_dir = top_fold + 'statistics_temp' fnl_dir = top_fold + 'statistics' @@ -104,7 +110,7 @@ def run_loop(file,bench,processed_files_path): subset = file.split('/')[-1].split('_')[1] # acquire the lock before opening the file - if testing: + if not_parallel: dat, ben = xr.open_dataset(file), xr.open_dataset(bench) else: import multiprocessing as mp @@ -117,9 +123,7 @@ def run_loop(file,bench,processed_files_path): ben = ben.where(ben!=-9999) # some weird negative values in runoff if not routed dat['averageRoutedRunoff'] = dat['averageRoutedRunoff'].where(dat['averageRoutedRunoff']>=0) - ben['averageRoutedRunoff'] = ben['averageRoutedRunoff'].where(ben['averageRoutedRunoff']>=0) - #dat['averageRoutedRunoff'] = dat['averageRoutedRunoff'].fillna(0) - #ben['averageRoutedRunoff'] = ben['averageRoutedRunoff'].fillna(0) + ben['averageRoutedRunoff'] = ben['averageRoutedRunoff'].where(ben['averageRoutedRunoff']>=0) # get rid of gru dimension, assuming hru and gru are one to one (everything now as hruId) dat = dat.drop_vars(['hruId','gruId']) @@ -142,15 +146,35 @@ def run_loop(file,bench,processed_files_path): for var in settings: mean = dat[var].mean(dim='time') mean = mean.expand_dims("stat").assign_coords(stat=("stat",["mean"])) + + datnz = dat[var].where(np.logical_and(ben[var] != 0,dat[var] != 0)) # don't include both 0 + mnnz = datnz.mean(dim='time') + mnnz = mnnz.expand_dims("stat").assign_coords(stat=("stat",["mnnz"])) + + mean_ben = ben[var].mean(dim='time') + mean_ben = mean_ben.expand_dims("stat").assign_coords(stat=("stat",["mean_ben"])) + + datnz = ben[var].where(np.logical_and(ben[var] != 0,dat[var] != 0)) # don't include both 0 + mnnz_ben = datnz.mean(dim='time') + mnnz_ben = mnnz_ben.expand_dims("stat").assign_coords(stat=("stat",["mnnz_ben"])) na_mx = np.fabs(dat[var]).max()+1 amx = np.fabs(dat[var].fillna(na_mx)).argmax(dim=['time']) amax = dat[var].isel(amx).drop_vars('time') amax = amax.expand_dims("stat").assign_coords(stat=("stat",["amax"])) + + na_mx = np.fabs(ben[var]).max()+1 + amx = np.fabs(ben[var].fillna(na_mx)).argmax(dim=['time']) + amax_ben = ben[var].isel(amx).drop_vars('time') + amax_ben = amax_ben.expand_dims("stat").assign_coords(stat=("stat",["amax_ben"])) rmse = (np.square(diff[var]).mean(dim='time'))**(1/2) #RMSE SHOULD THIS BE NORMALIZED? colorbar will normalize rmse = rmse.expand_dims("stat").assign_coords(stat=("stat",["rmse"])) + diffnz = diff[var].where(np.logical_and(ben[var] != 0,dat[var] != 0)) # don't include both 0 + rmnz = (np.square(diffnz).mean(dim='time'))**(1/2) + rmnz = rmnz.expand_dims("stat").assign_coords(stat=("stat",["rmnz"])) + na_mx = np.fabs(diff[var]).max()+1 amx = np.fabs(diff[var].fillna(na_mx)).argmax(dim=['time']) maxe = diff[var].isel(amx).drop_vars('time') @@ -169,11 +193,11 @@ def run_loop(file,bench,processed_files_path): kgem = kgem/(2.0-kgem) kgem = kgem.expand_dims("stat").assign_coords(stat=("stat",["kgem"])) - new = xr.merge([mean,amax,rmse,maxe,kgem]) + new = xr.merge([mean,mnnz,amax, mean_ben,mnnz_ben,amax_ben, rmse,rmnz, maxe, kgem]) new.to_netcdf(des_dir / des_fil.format(var,subset)) # write the name of the processed file to the file list, acquire the lock before opening the file - if testing: + if not_parallel: with open(processed_files_path, 'a') as filew: filew.write(file + '\n') filew.write(bench + '\n') @@ -207,7 +231,7 @@ def merge_subsets_into_one(src,pattern,des,name): # -- end functions -if testing: +if not_parallel: # -- no parallel processing for (file, bench) in zip(src_files,ben_files): run_loop(file,bench,processed_files_path) From 483580a3d2b0c09dfb563a6afc3a0c23415ef33a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 6 Oct 2023 00:26:30 +0900 Subject: [PATCH 0946/1472] fixing other utils, not done yet --- utils/hist_per_GRU.py | 79 ++++++++++++++++++++++++++++++++----------- utils/plot_per_GRU.py | 45 ++++++++++++++---------- utils/scat_per_GRU.py | 55 ++++++++++++++++++++++-------- 3 files changed, 128 insertions(+), 51 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 295808c9d..ad5efa0a7 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -24,17 +24,19 @@ nbatch_hrus = 518 # number of HRUs per batch num_bins = 1000 use_eff = False # use efficiency in wall clock time +do_rel = True # plot relative to the benchmark simulation testing = False if testing: - stat = 'rmse' + stat = 'rmnz' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be1','be64','sundials_1en6'] #maybe make this an argument + method_name=['be1','sundials_1en6'] #maybe make this an argument else: import sys # The first input argument specifies the run where the files are stat = sys.argv[1] method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument +if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] @@ -48,16 +50,26 @@ # Specify variables of interest plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] -leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] +leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] +leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$','$s$'] #fig_fil = '{}_hrly_diff_hist_{}_{}_zoom_compressed.png' #fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) fig_fil = 'Hrly_diff_hist_{}_{}_zoom_compressed.png' fig_fil = fig_fil.format(','.join(settings),stat) # possibly want to use these to shrink the axes a bit -if stat=='rmse': maxes = [2,15,8e-6,0.08,7e-9,10e-3] -if stat=='maxe' : maxes = [20,30,3e-4,2,4e-7,0.2] -if stat=='kgem' : maxes = [0.9,0.7,0.9,0.95,0.95,10e-3] +if stat == 'rmse': + maxes = [2,15,250,0.08,200,10e-3] #[2,15,8e-6,0.08,6e-9,10e-3] + #maxes = [0.25,2,30,0.01,30,2e-3] #[0.25,2,1e-6,0.01,1e-9,2e-3] + if do_rel: maxes = [1,1,1,1,1,10e-3] +if stat == 'rmnz': + maxes = [2,15,250,0.08,200,10e-3] + if do_rel: maxes = [1,1,1,1,1,10e-3] +if stat == 'maxe': + maxes = [15,25,0.8,2,0.3,0.2] #[15,25,25e-5,2,1e-7,0.2] + if do_rel: maxes = [1,1,1,1,1,10e-3] +if stat == 'kgem': + maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] summa = {} eff = {} @@ -94,9 +106,15 @@ def run_loop(i,var,mx): r = i//2 c = i-r*2 stat0 = stat - if var == 'wallClockTime': - if stat == 'rmse' or stat == 'kgem': stat0 = 'mean' - if stat == 'maxe': stat0 = 'amax' + if stat == 'rmse' or stat == 'kgem': + if var == 'wallClockTime': stat0 = 'mean' + statr = 'mean_ben' + if stat == 'rmnz': + if var == 'wallClockTime': stat0 = 'mnnz' + statr = 'mnnz_ben' + if stat == 'maxe': + if var == 'wallClockTime': stat0 = 'amax' + statr = 'amax_ben' if 'zoom' in fig_fil: mx = mx @@ -104,15 +122,22 @@ def run_loop(i,var,mx): else: mx = 0.0 mn = 1.0 + if do_rel: s_rel = summa[method_name[0]][var].sel(stat=statr) for m in method_name: s = summa[m][var].sel(stat=stat0) + if do_rel and var != 'wallClockTime': s = s/s_rel if stat == 'maxe': s = np.fabs(s) # make absolute value norm mx = max(s.max(),mx) if stat=='kgem' : mn = min(s.min(),mn) # Data + if do_rel: s_rel = summa[method_name[0]][var].sel(stat=statr) for m in method_name: s = summa[m][var].sel(stat=stat0) + if do_rel and var != 'wallClockTime': + s = s/s_rel + word_add = 'rel to bench ' + if var == 'wallClockTime' and use_eff: batch = np.floor(np.arange(len(s.indexes['hru'])) /nbatch_hrus) #basin_num = np.arange(len(s.indexes['hru'])) % nbatch_hrus #not currently using @@ -134,28 +159,42 @@ def run_loop(i,var,mx): # Multiply the s values by efficiency s = s*eff_batch + if var == 'scalarTotalET' and not do_rel: + if stat =='rmse' or stat =='rmnz' : s = s*31557600 # make annual total + if stat =='maxe': s = s*3600 # make hourly max + if var == 'averageRoutedRunoff' and not do_rel: + if stat =='rmse' or stat =='rmnz' : s = s*31557600*1000 # make annual total + if stat =='maxe': s = s*3600*1000 # make hourly max if stat == 'maxe': s = np.fabs(s) # make absolute value norm range = (0,mx) if stat=='kgem' and var!='wallClockTime' : range = (mn,1) - s.plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=m,linewidth=2.0,range=range) + np.fabs(s).plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=m,linewidth=2.0,range=range) - if stat == 'rmse': stat_word = ' Hourly RMSE' - if stat == 'maxe': stat_word = ' Hourly max abs error' - if stat == 'kgem': stat_word = ' Hourly KGEm' + if stat0 == 'rmse': stat_word = 'RMSE' + if stat0 == 'rmnz': stat_word = 'RMSE no 0s' + if stat0 == 'maxe': stat_word = 'max abs error' + if stat0 == 'kgem': stat_word = 'KGE"' + if stat0 == 'mean': stat_word = 'mean ' + if stat0 == 'mnnz': stat_word = 'mean no 0s ' + if stat0 == 'amax': stat_word = 'max ' fig.suptitle('Histograms of Hourly Statistics for each GRU', fontsize=40) + + if statr == 'mean_ben': stat0_word = 'mean ' + if statr == 'mnnz_ben': stat0_word = 'mean excluding 0s ' + if statr == 'amax_ben': stat0_word = 'max ' - # wall Clock doesn't do difference - if var == 'wallClockTime': - if stat == 'rmse' or stat == 'kgem': stat_word = ' Hourly mean' - if stat == 'maxe': stat_word = ' Hourly max' - #axs[r,c].set_yscale('log') #log y axis for wall clock time to exaggerate peaks + #if var == 'wallClockTime': axs[r,c].set_yscale('log') #log y axis for wall clock time to exaggerate peaks axs[r,c].legend() axs[r,c].set_title(plt_titl[i] + stat_word) - axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl[i])) + if stat == 'rmse' or stat == 'rmnz': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl[i])) + if stat == 'maxe': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titlm[i])) + if stat == 'kgem': axs[r,c].set_xlabel(stat_word) + if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + word_add + stat0_word) + axs[r,c].set_ylabel('GRU count') - if var != 'wallClockTime': axs[r,c].set_ylim([0, 25000]) + if var != 'wallClockTime' and not testing: axs[r,c].set_ylim([0, 25000]) for i,(var,mx) in enumerate(zip(plot_vars,maxes)): diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index c84829181..f00151bac 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -46,13 +46,16 @@ # Specify variables of interest plot_vars = settings plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] -leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] -if stat == 'rmse': maxes = [2,15,8e-6,0.08,7e-9,10e-3] -# if stat=='rmse': maxes = [0.25,2,1e-6,0.01,1e-9,2e-3] -if stat == 'maxe': maxes = [20,30,3e-4,2,4e-7,0.2] -if stat == 'kgem': maxes = [0.9,0.7,0.9,0.95,0.95,10e-3] -if stat == 'mean': maxes = [80,1500,5e-5,8,1e-7,10e-3] -if stat == 'amax': maxes = [240,1800,1e-3,25,1.554e-5,0.2] +leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] +leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$','$s$'] + +if stat == 'rmse': maxes = [2,15,250,0.08,200,10e-3] #[2,15,8e-6,0.08,6e-9,10e-3] +# if stat=='rmse': maxes = [0.25,2,30,0.01,30,2e-3] #[0.25,2,1e-6,0.01,1e-9,2e-3] +if stat == 'rmnz': maxes = [2,15,250,0.08,200,10e-3] +if stat == 'maxe': maxes = [15,25,0.8,2,0.3,0.2] #[15,25,25e-5,2,1e-7,0.2] +if stat == 'kgem': maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] +if stat == 'mean': maxes = [80,1500,1500,3000,10e-3] #[80,1500,5e-5,8,1e-7,10e-3] +if stat == 'amax': maxes = [240,1800,3.5,25,7.5,0.2] #[240,1800,1e-3,25,2e-6,0.2] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed.png' fig_fil = fig_fil.format(','.join(settings),stat) @@ -192,6 +195,12 @@ def make_default_path(suffix): s = s*eff_batch if stat == 'maxe' or stat =='mean' or stat =='amax': s = np.fabs(s) # make absolute value norm, max is not not all positive + if plot_var == 'scalarTotalET': + if stat =='rmse' or stat =='rmnz' : s = s*31557600 # make annual total + if stat =='maxe': s = s*3600 # make hourly max + if plot_var == 'averageRoutedRunoff': + if stat =='rmse' or stat =='rmnz' : s = s*31557600*1000 # make annual total + if stat =='maxe': s = s*3600*1000 # make hourly max bas_albers[plot_var] = s.sel(hru=hru_ids_shp.values) # Select lakes of a certain size for plotting @@ -232,13 +241,12 @@ def run_loop(i,var,the_max,f_x,f_y): vmin,vmax = 0, the_max if stat =='mean' and var=='scalarTotalSoilWat': vmin,vmax = 700, the_max if stat =='amax' and var=='scalarTotalSoilWat': vmin,vmax = 1000, the_max - if stat =='amax' and var=='averageRoutedRunoff': vmin,vmax = 1.538e-5, the_max norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) if stat =='kgem' and var!='wallClockTime': my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno')) # copy the default cmap my_cmap.set_bad(color='white') #nan color white vmin,vmax = the_max, 1.0 - norm=matplotlib.colors.Normalize(vmin=vmin, vmax=vmax) + norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=1.5) r = i//2 c = i-r*2 @@ -250,19 +258,22 @@ def run_loop(i,var,the_max,f_x,f_y): sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) sm._A = [] cbr = fig.colorbar(sm, cax=cax) #, extend='max') #if max extend can't get title right - cbr.ax.set_ylabel('[{}]'.format(leg_titl[i]), labelpad=40, rotation=270) + if stat == 'rmse' or stat == 'rmnz' or stat == 'mean': cbr.ax.set_ylabel('[{}]'.format(leg_titl[i]), labelpad=40, rotation=270) + if stat == 'maxe' or stat == 'amax': cbr.ax.set_ylabel('[{}]'.format(leg_titl[i]), labelpad=40, rotation=270) + #cbr.ax.yaxis.set_offset_position('right') - if stat == 'rmse': stat_word = ' Hourly RMSE' - if stat == 'maxe': stat_word = ' Hourly max abs error' - if stat == 'kgem': stat_word = ' Hourly KGEm' - if stat == 'mean': stat_word = ' Hourly abs mean' - if stat == 'amax': stat_word = ' Hourly abs max' + if stat == 'rmse': stat_word = ' RMSE' + if stat == 'rmnz': stat_word = ' RMSE no 0s' + if stat == 'maxe': stat_word = ' max abs error' + if stat == 'kgem': stat_word = ' KGE"' + if stat == 'mean': stat_word = ' abs mean' + if stat == 'amax': stat_word = ' abs max' # wall Clock doesn't do difference if var == 'wallClockTime': - if stat == 'rmse' or stat == 'kgem': stat_word = ' Hourly mean' - if stat == 'maxe': stat_word = ' Hourly max' + if stat == 'rmse' or stat == 'kgem': stat_word = ' mean' + if stat == 'maxe': stat_word = ' max' axs[r,c].set_title(plt_titl[i] + stat_word) axs[r,c].axis('off') diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index 8a6b53097..4591624e8 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -23,17 +23,19 @@ viz_dir = Path('/home/avanb/scratch/statistics') nbatch_hrus = 518 # number of HRUs per batch use_eff = False # use efficiency in wall clock time, still need files for the node number +do_rel = True # plot relative to the benchmark simulation testing = False if testing: - stat = 'rmse' + stat = 'rmnz' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be1','be64','sundials_1en6'] #maybe make this an argument + method_name=['be1','sundials_1en6'] #maybe make this an argument else: import sys # The first input argument specifies the run where the files are stat = sys.argv[1] method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument +if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] @@ -47,8 +49,9 @@ # Specify variables of interest plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] -leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$num$'] -leg_titl0 = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}$','$m~s^{-1}$','$s$'] +leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$num$'] +leg_titl0 = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] +leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$','$s$'] #fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' #fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) @@ -85,17 +88,34 @@ else: fig,axs = plt.subplots(3,2,figsize=(140,133)) fig.suptitle('Hourly Errors and Values for each GRU', fontsize=40) - def run_loop(i,var): r = i//2 c = i-r*2 - if stat == 'rmse' or stat == 'kgem': stat0 = 'mean' - if stat == 'maxe': stat0 = 'amax' + if stat == 'rmse' or stat == 'kgem': + stat0 = 'mean' + statr = 'mean_ben' + if stat == 'rmnz': + stat0 = 'mnnz' + statr = 'mnnz_ben' + if stat == 'maxe': + stat0 = 'amax' + statr = 'amax_ben' # Data + if do_rel: s_rel = summa[method_name[0]][var].sel(stat=statr) for m in method_name: s = summa[m][var].sel(stat=[stat,stat0]) + if do_rel and var != 'wallClockTime': + s = s/s_rel + word_add = 'rel to bench ' + + if var == 'scalarTotalET' and not do_rel: + if stat =='rmse' or stat =='rmnz' : s = s*31557600 # make annual total + if stat =='maxe': s = s*3600 # make hourly max + if var == 'averageRoutedRunoff'and not do_rel: + if stat =='rmse' or stat =='rmnz' : s = s*31557600*1000 # make annual total + if stat =='maxe': s = s*3600*1000 # make hourly max if stat == 'maxe': s.loc[dict(stat='maxe')] = np.fabs(s.loc[dict(stat='maxe')]) # make absolute value norm if var == 'wallClockTime': @@ -121,20 +141,27 @@ def run_loop(i,var): axs[r,c].scatter(x=node_batch,y=s.sel(stat=stat0),s=1,zorder=0,label=m) stat_word = 'Node number' else: - axs[r,c].scatter(x=s.sel(stat=stat).values,y=s.sel(stat=stat0).values,s=1,zorder=0,label=m) - if stat == 'rmse': stat_word = 'Hourly RMSE ' - if stat == 'maxe': stat_word = 'Hourly max abs error ' - if stat == 'kgem': stat_word = 'Hourly KGEm ' + axs[r,c].scatter(x=np.fabs(s.sel(stat=stat).values),y=s.sel(stat=stat0).values,s=1,zorder=0,label=m) + if stat == 'rmse': stat_word = 'RMSE ' + if stat == 'rmnz': stat_word = 'RMSE no 0s ' + if stat == 'maxe': stat_word = 'max abs error ' + if stat == 'kgem': stat_word = 'KGE" ' - if stat0 == 'mean': stat0_word = 'Hourly mean ' - if stat0 == 'amax': stat0_word = 'Hourly max ' + if stat0 == 'mean': stat0_word = 'mean ' + if stat0 == 'mnnz': stat0_word = 'mean no 0s ' + if stat0 == 'amax': stat0_word = 'max ' lgnd = axs[r,c].legend() for j, m in enumerate(method_name): lgnd.legendHandles[j]._sizes = [80] axs[r,c].set_title(plt_titl[i]) - axs[r,c].set_xlabel(stat_word + '[{}]'.format(leg_titl[i])) + if stat == 'rmse' or stat == 'rmnz': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl[i])) + if stat == 'maxe': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titlm[i])) + if stat == 'kgem': axs[r,c].set_xlabel(stat_word) + if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + word_add + stat0_word) + axs[r,c].set_ylabel(stat0_word + '[{}]'.format(leg_titl0[i])) + if do_rel and var!='wallClockTime': axs[r,c].set_ylabel(stat0_word + word_add + stat0_word) for i,var in enumerate(plot_vars): From 86d41618db8fd707607f7bf51d68a8914b8ea286 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 5 Oct 2023 18:07:03 -0600 Subject: [PATCH 0947/1472] General cleanup, updates to comments, and removal of defunct features (subTools and related data structures). --- build/source/dshare/data_types.f90 | 103 +++--- build/source/dshare/var_lookup.f90 | 25 -- build/source/engine/computFlux.f90 | 499 +---------------------------- 3 files changed, 70 insertions(+), 557 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 6a125d49c..79a1664e5 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -303,7 +303,10 @@ MODULE data_types type(hru_i),allocatable :: gru(:) ! gru(:)%hru(:) endtype gru_i - ! define derived types used to simplify passing subroutine arguments + ! *********************************************************************************************************** + ! Define classes used to simplify calls to the subrotuines in computFlux + ! *********************************************************************************************************** + ! Note: class procedures are located in the contains block of this (data_types) module ! ** vegNrgFlux type, public :: in_type_vegNrgFlux ! derived type for intent(in) arguments in vegNrgFlux call logical(lgt) :: firstSubStep ! intent(in): flag to indicate if we are processing the first sub-step @@ -572,7 +575,7 @@ MODULE data_types contains - ! ** vegNrgFlux + ! **** vegNrgFlux **** subroutine initialize_in_vegNrgFlux(in_vegNrgFlux,firstSubStep,firstFluxCall,computeVegFlux,checkLWBalance,& scalarCanairTempTrial,scalarCanopyTempTrial,mLayerTempTrial,scalarCanopyIceTrial,& scalarCanopyLiqTrial,forc_data,deriv_data) @@ -691,9 +694,9 @@ subroutine finalize_out_vegNrgFlux(out_vegNrgFlux,flux_data,deriv_data,err,cmess err =out_vegNrgFlux % err ! intent(out): error code cmessage =out_vegNrgFlux % cmessage ! intent(out): error message end subroutine finalize_out_vegNrgFlux - ! ** end vegNrgFlux + ! **** end vegNrgFlux **** - ! ** ssdNrgFlux + ! **** ssdNrgFlux **** subroutine initialize_in_ssdNrgFlux(in_ssdNrgFlux,scalarSolution,firstFluxCall,mLayerTempTrial,flux_data,deriv_data) class(in_type_ssdNrgFlux),intent(out) :: in_ssdNrgFlux ! class object for intent(in) ssdNrgFlux arguments logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution @@ -764,10 +767,10 @@ subroutine finalize_out_ssdNrgFlux(out_ssdNrgFlux,flux_data,deriv_data,err,cmess cmessage =out_ssdNrgFlux % cmessage ! intent(out): error message end associate end subroutine finalize_out_ssdNrgFlux - ! ** end ssdNrgFlux + ! **** end ssdNrgFlux **** - ! ** vegLiqFlux - subroutine initialize_in_vegLiqFlux(in_vegLiqFlux,computeVegFlux,scalarCanopyLiqTrial,flux_data) ! SJT + ! **** vegLiqFlux **** + subroutine initialize_in_vegLiqFlux(in_vegLiqFlux,computeVegFlux,scalarCanopyLiqTrial,flux_data) class(in_type_vegLiqFlux),intent(out) :: in_vegLiqFlux ! class object for intent(in) vegLiqFlux arguments logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation real(rkind),intent(in) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) @@ -800,11 +803,11 @@ subroutine finalize_out_vegLiqFlux(out_vegLiqFlux,flux_data,deriv_data,err,cmess cmessage =out_vegLiqFlux % cmessage ! intent(out): error control end associate end subroutine finalize_out_vegLiqFlux - ! ** end vegLiqFlux + ! **** end vegLiqFlux **** - ! ** snowLiqFlx + ! **** snowLiqFlx **** subroutine initialize_in_snowLiqFlx(in_snowLiqFlx,nSnow,firstFluxCall,scalarSolution,mLayerVolFracLiqTrial,flux_data) - class(in_type_snowLiqFlx),intent(out) :: in_snowLiqFlx ! class object of intent(in) arguments + class(in_type_snowLiqFlx),intent(out) :: in_snowLiqFlx ! class object for intent(in) snowLiqFlx arguments integer(i4b),intent(in) :: nSnow ! number of snow layers logical(lgt),intent(in) :: firstFluxCall ! flag to indicate if we are processing the first flux call logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution @@ -824,45 +827,45 @@ subroutine initialize_in_snowLiqFlx(in_snowLiqFlx,nSnow,firstFluxCall,scalarSolu end subroutine initialize_in_snowLiqFlx subroutine initialize_io_snowLiqFlx(io_snowLiqFlx,flux_data,deriv_data) - class(io_type_snowLiqFlx),intent(out) :: io_snowLiqFlx ! class object for intent(inout) arguments + class(io_type_snowLiqFlx),intent(out) :: io_snowLiqFlx ! class object for intent(inout) snowLiqFlx arguments type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables associate(& - iLayerLiqFluxSnow => flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at snow layer interfaces (-) - iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat) ! intent(out): [dp(:)] derivative in vertical liquid water flux at layer interfaces + iLayerLiqFluxSnow => flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat, & ! intent(out): [dp(0:)] vertical liquid water flux at snow layer interfaces (-) + iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv)%dat ) ! intent(out): [dp(:)] derivative in vertical liquid water flux at layer interfaces io_snowLiqFlx % iLayerLiqFluxSnow =iLayerLiqFluxSnow ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) io_snowLiqFlx % iLayerLiqFluxSnowDeriv =iLayerLiqFluxSnowDeriv ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) end associate end subroutine initialize_io_snowLiqFlx subroutine finalize_io_snowLiqFlx(io_snowLiqFlx,flux_data,deriv_data) - class(io_type_snowLiqFlx),intent(in) :: io_snowLiqFlx ! class object for intent(inout) arguments + class(io_type_snowLiqFlx),intent(in) :: io_snowLiqFlx ! class object for intent(inout) snowLiqFlx arguments type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables associate(& - iLayerLiqFluxSnow => flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at snow layer interfaces (-) - iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat) ! intent(out): [dp(:)] derivative in vertical liquid water flux at layer interfaces + iLayerLiqFluxSnow => flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat, & ! intent(out): [dp(0:)] vertical liquid water flux at snow layer interfaces (-) + iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv)%dat ) ! intent(out): [dp(:)] derivative in vertical liquid water flux at layer interfaces ! intent(inout) arguments - iLayerLiqFluxSnow =io_snowLiqFlx % iLayerLiqFluxSnow ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) - iLayerLiqFluxSnowDeriv=io_snowLiqFlx % iLayerLiqFluxSnowDeriv ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) + iLayerLiqFluxSnow =io_snowLiqFlx % iLayerLiqFluxSnow ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) + iLayerLiqFluxSnowDeriv=io_snowLiqFlx % iLayerLiqFluxSnowDeriv ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) end associate end subroutine finalize_io_snowLiqFlx subroutine finalize_out_snowLiqFlx(out_snowLiqFlx,err,cmessage) - class(out_type_snowLiqFlx),intent(in) :: out_snowLiqFlx ! class object for intent(out) arguments + class(out_type_snowLiqFlx),intent(in) :: out_snowLiqFlx ! class object for intent(out) snowLiqFlx arguments integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: cmessage ! error message from snowLiqFlx ! intent(out) arguments - err =out_snowLiqFlx % err ! intent(out): error code - cmessage=out_snowLiqFlx % cmessage ! intent(out): error message + err =out_snowLiqFlx % err ! intent(out): error code + cmessage=out_snowLiqFlx % cmessage ! intent(out): error message end subroutine finalize_out_snowLiqFlx - ! ** end snowLiqFlx + ! **** end snowLiqFlx **** - ! ** soilLiqFlx + ! **** soilLiqFlx **** subroutine initialize_in_soilLiqFlx(in_soilLiqFlx,nsnow,nSoil,nlayers,firstSplitOper,scalarSolution,firstFluxCall,& mLayerTempTrial,mLayerMatricHeadTrial,mLayerMatricHeadLiqTrial,mLayerVolFracLiqTrial,mLayerVolFracIceTrial,& above_soilLiqFluxDeriv,above_soildLiq_dTk,above_soilFracLiq,flux_data,deriv_data) - class(in_type_soilLiqFlx),intent(out) :: in_soilLiqFlx ! class object for intent(in) arguments + class(in_type_soilLiqFlx),intent(out) :: in_soilLiqFlx ! class object for intent(in) soilLiqFlx arguments integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers @@ -930,7 +933,7 @@ subroutine initialize_in_soilLiqFlx(in_soilLiqFlx,nsnow,nSoil,nlayers,firstSplit end subroutine initialize_in_soilLiqFlx subroutine initialize_io_soilLiqFlx(io_soilLiqFlx,nsoil,dHydCond_dMatric,flux_data,diag_data,deriv_data) - class(io_type_soilLiqFlx),intent(out) :: io_soilLiqFlx ! class object for intent(inout) arguments + class(io_type_soilLiqFlx),intent(out) :: io_soilLiqFlx ! class object for intent(inout) soilLiqFlx arguments integer(i4b),intent(in) :: nSoil ! number of soil layers real(rkind),intent(in) :: dHydCond_dMatric(nSoil) ! derivative in hydraulic conductivity w.r.t matric head (s-1) type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU @@ -996,7 +999,7 @@ subroutine initialize_io_soilLiqFlx(io_soilLiqFlx,nsoil,dHydCond_dMatric,flux_da end subroutine initialize_io_soilLiqFlx subroutine finalize_io_soilLiqFlx(io_soilLiqFlx,nsoil,dHydCond_dMatric,flux_data,diag_data,deriv_data) - class(io_type_soilLiqFlx),intent(in) :: io_soilLiqFlx ! class object for intent(inout) arguments + class(io_type_soilLiqFlx),intent(in) :: io_soilLiqFlx ! class object for intent(inout) soilLiqFlx arguments integer(i4b),intent(in) :: nSoil ! number of soil layers real(rkind),intent(out) :: dHydCond_dMatric(nSoil) ! derivative in hydraulic conductivity w.r.t matric head (s-1) type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU @@ -1054,26 +1057,26 @@ subroutine finalize_io_soilLiqFlx(io_soilLiqFlx,nsoil,dHydCond_dMatric,flux_data mLayerdTrans_dTCanopy => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanopy)%dat, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy temperature mLayerdTrans_dTGround => deriv_data%var(iLookDERIV%mLayerdTrans_dTGround)%dat, & ! intent(out): derivatives in the soil layer transpiration flux w.r.t. ground temperature mLayerdTrans_dCanWat => deriv_data%var(iLookDERIV%mLayerdTrans_dCanWat)%dat ) ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy total water - mLayerdTrans_dTCanair =io_soilLiqFlx % mLayerdTrans_dTCanair ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature - mLayerdTrans_dTCanopy =io_soilLiqFlx % mLayerdTrans_dTCanopy ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy temperature - mLayerdTrans_dTGround =io_soilLiqFlx % mLayerdTrans_dTGround ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. ground temperature - mLayerdTrans_dCanWat =io_soilLiqFlx % mLayerdTrans_dCanWat ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy total water + mLayerdTrans_dTCanair =io_soilLiqFlx % mLayerdTrans_dTCanair ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + mLayerdTrans_dTCanopy =io_soilLiqFlx % mLayerdTrans_dTCanopy ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy temperature + mLayerdTrans_dTGround =io_soilLiqFlx % mLayerdTrans_dTGround ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. ground temperature + mLayerdTrans_dCanWat =io_soilLiqFlx % mLayerdTrans_dCanWat ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy total water end associate end subroutine finalize_io_soilLiqFlx subroutine finalize_out_soilLiqFlx(out_soilLiqFlx,err,cmessage) - class(out_type_soilLiqFlx),intent(in) :: out_soilLiqFlx ! class object for intent(out) arguments - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: cmessage ! error message from groundwatr + class(out_type_soilLiqFlx),intent(in) :: out_soilLiqFlx ! class object for intent(out) soilLiqFlx arguments + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: cmessage ! error message from groundwatr ! intent(out) arguments - err =out_soilLiqFlx % err ! intent(out): error code - cmessage =out_soilLiqFlx % cmessage ! intent(out): error message + err =out_soilLiqFlx % err ! intent(out): error code + cmessage =out_soilLiqFlx % cmessage ! intent(out): error message end subroutine finalize_out_soilLiqFlx - ! ** end soilLiqFlx + ! **** end soilLiqFlx **** - ! ** groundwatr + ! **** groundwatr **** subroutine initialize_in_groundwatr(in_groundwatr,nSnow,nSoil,nLayers,firstFluxCall,mLayerMatricHeadLiqTrial,mLayerVolFracLiqTrial,mLayerVolFracIceTrial,deriv_data) - class(in_type_groundwatr),intent(out) :: in_groundwatr ! class object for intent(in) arguments + class(in_type_groundwatr),intent(out) :: in_groundwatr ! class object for intent(in) groundwatr arguments integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers @@ -1098,21 +1101,21 @@ subroutine initialize_in_groundwatr(in_groundwatr,nSnow,nSoil,nLayers,firstFluxC end subroutine initialize_in_groundwatr subroutine initialize_io_groundwatr(io_groundwatr,ixSaturation) - class(io_type_groundwatr),intent(out) :: io_groundwatr ! class object for intent(inout) arguments + class(io_type_groundwatr),intent(out) :: io_groundwatr ! class object for intent(inout) groundwatr arguments integer(i4b),intent(in) :: ixSaturation ! index of lowest saturated layer (NOTE: only computed on the first iteration) ! intent(inout) arguments io_groundwatr % ixSaturation = ixSaturation ! intent(inout): index of lowest saturated layer (NOTE: only computed on the first iteration) end subroutine initialize_io_groundwatr subroutine finalize_io_groundwatr(io_groundwatr,ixSaturation) - class(io_type_groundwatr),intent(in) :: io_groundwatr ! class object for intent(inout) arguments + class(io_type_groundwatr),intent(in) :: io_groundwatr ! class object for intent(inout) groundwatr arguments integer(i4b),intent(out) :: ixSaturation ! index of lowest saturated layer (NOTE: only computed on the first iteration) ! intent(inout) arguments ixSaturation = io_groundwatr % ixSaturation ! intent(inout): index of lowest saturated layer (NOTE: only computed on the first iteration) end subroutine finalize_io_groundwatr subroutine finalize_out_groundwatr(out_groundwatr,dBaseflow_dMatric,flux_data,err,cmessage) - class(out_type_groundwatr),intent(in) :: out_groundwatr ! class object for intent(out) arguments + class(out_type_groundwatr),intent(in) :: out_groundwatr ! class object for intent(out) groundwatr arguments real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU integer(i4b),intent(out) :: err ! error code @@ -1126,11 +1129,11 @@ subroutine finalize_out_groundwatr(out_groundwatr,dBaseflow_dMatric,flux_data,er cmessage = out_groundwatr % cmessage ! intent(out): error message end associate end subroutine finalize_out_groundwatr - ! ** end groundwatr + ! **** end groundwatr **** - ! ** bigAquifer + ! **** bigAquifer **** subroutine initialize_in_bigAquifer(in_bigAquifer,scalarAquiferStorageTrial,flux_data,deriv_data) - class(in_type_bigAquifer),intent(out) :: in_bigAquifer ! class object for intent(in) arguments + class(in_type_bigAquifer),intent(out) :: in_bigAquifer ! class object for intent(in) bigAquifer arguments real(rkind),intent(in) :: scalarAquiferStorageTrial ! trial value of aquifer storage (m) type(var_dlength),intent(in) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables @@ -1153,7 +1156,7 @@ subroutine initialize_in_bigAquifer(in_bigAquifer,scalarAquiferStorageTrial,flux end subroutine initialize_in_bigAquifer subroutine initialize_io_bigAquifer(io_bigAquifer,deriv_data) - class(io_type_bigAquifer),intent(out) :: io_bigAquifer ! class object for intent(inout) arguments + class(io_type_bigAquifer),intent(out) :: io_bigAquifer ! class object for intent(inout) bigAquifer arguments type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables associate(& dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair)%dat(1), & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature @@ -1169,7 +1172,7 @@ subroutine initialize_io_bigAquifer(io_bigAquifer,deriv_data) end subroutine initialize_io_bigAquifer subroutine finalize_io_bigAquifer(io_bigAquifer,deriv_data) - class(io_type_bigAquifer),intent(in) :: io_bigAquifer ! class object for intent(inout) arguments + class(io_type_bigAquifer),intent(in) :: io_bigAquifer ! class object for intent(inout) bigAquifer arguments type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables associate(& dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair)%dat(1), & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature @@ -1185,11 +1188,11 @@ subroutine finalize_io_bigAquifer(io_bigAquifer,deriv_data) end subroutine finalize_io_bigAquifer subroutine finalize_out_bigAquifer(out_bigAquifer,flux_data,deriv_data,err,cmessage) - class(out_type_bigAquifer),intent(in) :: out_bigAquifer ! class object for intent(out) arguments + class(out_type_bigAquifer),intent(in) :: out_bigAquifer ! class object for intent(out) bigAquifer arguments type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: cmessage ! error message from bigAquifer + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: cmessage ! error message from bigAquifer associate(& scalarAquiferTranspire => flux_data%var(iLookFLUX%scalarAquiferTranspire)%dat(1), & ! intent(out): [dp] transpiration loss from the aquifer (m s-1) scalarAquiferRecharge => flux_data%var(iLookFLUX%scalarAquiferRecharge)%dat(1), & ! intent(out): [dp] recharge to the aquifer (m s-1) @@ -1204,5 +1207,5 @@ subroutine finalize_out_bigAquifer(out_bigAquifer,flux_data,deriv_data,err,cmess cmessage = out_bigAquifer % cmessage ! intent(out): error message end associate end subroutine finalize_out_bigAquifer - ! ** end bigAquifer + ! **** end bigAquifer **** END MODULE data_types diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index e319db30e..2c5630510 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -834,27 +834,6 @@ MODULE var_lookup integer(i4b) :: deriv2 = integerMissing ! second derivatives of the interpolating function endtype iLook_vLookup - ! *********************************************************************************************************** - ! (17) structure for looking up flux subroutines - ! *********************************************************************************************************** - type, public :: iLook_routine - integer(i4b) :: vegNrgFlux = integerMissing ! vegNrgFlux - integer(i4b) :: ssdNrgFlux = integerMissing ! ssdNrgFlux - integer(i4b) :: vegLiqFlux = integerMissing ! vegLiqFlux - integer(i4b) :: snowLiqFlx = integerMissing ! snowLiqFlx - integer(i4b) :: soilLiqFlx = integerMissing ! soilLiqFlx - integer(i4b) :: groundwatr = integerMissing ! groundwatr - integer(i4b) :: bigAquifer = integerMissing ! bigAquifer - end type iLook_routine - - ! *********************************************************************************************************** - ! (18) structure for looking up operation argument for subTools subroutine - ! *********************************************************************************************************** - type, public :: iLook_op - integer(i4b) :: pre = integerMissing ! pre-processing - integer(i4b) :: post = integerMissing ! post-processing - end type iLook_op - ! *********************************************************************************************************** ! (X) define data structures and maximum number of variables of each type ! *********************************************************************************************************** @@ -947,10 +926,6 @@ MODULE var_lookup type(iLook_freq), public,parameter :: iLookFreq =ilook_freq ( 1, 2, 3, 4) ! named variables in the lookup table structure type(iLook_vLookup), public,parameter :: iLookLOOKUP =ilook_vLookup ( 1, 2, 3) - ! named variables: flux subroutines - type(iLook_routine), public,parameter :: iLookROUTINE =iLook_routine ( 1, 2, 3, 4, 5, 6, 7) - ! named variables: operation arguments for subTools subroutine - type(iLook_op), public,parameter :: iLookOP =iLook_op ( 1, 2) ! define maximum number of variables of each type integer(i4b),parameter,public :: maxvarDecisions = storage_size(iLookDECISIONS)/iLength integer(i4b),parameter,public :: maxvarTime = storage_size(iLookTIME)/iLength diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index d1f3ed48c..fcc057ec2 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -23,7 +23,7 @@ module computFlux_module ! data types USE nrtype -! provide access to the derived types to define the data structures +! provide access to the derived types and classes used to define data structures and class objects USE data_types,only:& var_i, & ! data vector (i4b) var_d, & ! data vector (rkind) @@ -47,8 +47,6 @@ module computFlux_module USE var_lookup,only:iLookDIAG ! named variables for structure elements USE var_lookup,only:iLookFLUX ! named variables for structure elements USE var_lookup,only:iLookDERIV ! named variables for structure elements -USE var_lookup,only:iLookROUTINE ! named variables for structure elements -USE var_lookup,only:iLookOP ! named variables for structure elements ! missing values USE globalData,only:integerMissing ! missing integer @@ -221,7 +219,7 @@ subroutine computFlux(& real(rkind) :: above_soilLiqFluxDeriv ! derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water real(rkind) :: above_soildLiq_dTk ! derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature real(rkind) :: above_soilFracLiq ! fraction of liquid water layer above soil (canopy or snow) (-) - ! data structures for flux subroutine arguments (derived types defined in data_types module) + ! ---------------------- classes for flux subroutine arguments (classes defined in data_types module) ---------------------- ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** type(in_type_vegNrgFlux) :: in_vegNrgFlux; type(out_type_vegNrgFlux) :: out_vegNrgFlux ! vegNrgFlux arguments type(in_type_ssdNrgFlux) :: in_ssdNrgFlux; type(io_type_ssdNrgFlux) :: io_ssdNrgFlux; type(out_type_ssdNrgFlux) :: out_ssdNrgFlux ! ssdNrgFlux arguments @@ -234,9 +232,7 @@ subroutine computFlux(& ! initialize error control err=0; message='computFlux/' - ! ***** - ! * PRELIMINARIES... - ! ******************** + ! *** PRELIMINARIES *** ! get the necessary variables for the flux computations associate(& ! model decisions @@ -443,481 +439,7 @@ subroutine computFlux(& contains - subroutine subTools(op,sub) - implicit none - integer(i4b),intent(in) :: op ! index of requested operation: iLookOP%pre for pre-processing or iLookOP%post for post-processing - integer(i4b),intent(in) :: sub ! index of subroutine in computFlux (e.g., iLookROUTINE%vegNrgFlux for vegNrgFlux routine) - - associate(& - ! domain boundary conditions - upperBoundTemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) - scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in): [dp] rainfall rate (kg m-2 s-1) - ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! derivatives - dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) - dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(in): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature - ! net fluxes over the vegetation domain - scalarCanairNetNrgFlux => flux_data%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the canopy air space (W m-2) - scalarCanopyNetNrgFlux => flux_data%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the vegetation canopy (W m-2) - scalarGroundNetNrgFlux => flux_data%var(iLookFLUX%scalarGroundNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the ground surface (W m-2) - scalarCanopyNetLiqFlux => flux_data%var(iLookFLUX%scalarCanopyNetLiqFlux)%dat(1) ,& ! intent(out): [dp] net liquid water flux for the vegetation canopy (kg m-2 s-1) - ! net fluxes over the snow+soil domain - mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) - mLayerLiqFluxSnow => flux_data%var(iLookFLUX%mLayerLiqFluxSnow)%dat ,& ! intent(out): [dp] net liquid water flux for each snow layer (s-1) - mLayerLiqFluxSoil => flux_data%var(iLookFLUX%mLayerLiqFluxSoil)%dat ,& ! intent(out): [dp] net liquid water flux for each soil layer (s-1) - ! evaporative fluxes - scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(out): [dp] canopy transpiration (kg m-2 s-1) - scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(out): [dp] canopy evaporation/condensation (kg m-2 s-1) - scalarGroundEvaporation => flux_data%var(iLookFLUX%scalarGroundEvaporation)%dat(1) ,& ! intent(out): [dp] ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(out): [dp(:)] transpiration loss from each soil layer (m s-1) - ! fluxes for the snow+soil domain - iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(out): [dp(0:)] vertical energy flux at the interface of snow and soil layers - iLayerLiqFluxSnow => flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at snow layer interfaces (-) - iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at soil layer interfaces (-) - mLayerHydCond => flux_data%var(iLookFLUX%mLayerHydCond)%dat ,& ! intent(out): [dp(:)] hydraulic conductivity in each soil layer (m s-1) - mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(out): [dp(:)] baseflow from each soil layer (m s-1) - scalarSnowDrainage => flux_data%var(iLookFLUX%scalarSnowDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the snow profile (m s-1) - scalarSoilDrainage => flux_data%var(iLookFLUX%scalarSoilDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the soil profile (m s-1) - ! infiltration - scalarInfilArea => diag_data%var(iLookDIAG%scalarInfilArea )%dat(1) ,& ! intent(out): [dp] fraction of unfrozen area where water can infiltrate (-) - scalarFrozenArea => diag_data%var(iLookDIAG%scalarFrozenArea )%dat(1) ,& ! intent(out): [dp] fraction of area that is considered impermeable due to soil ice (-) - scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1) ,& ! intent(out): [dp] soil control on infiltration, zero or one - scalarMaxInfilRate => flux_data%var(iLookFLUX%scalarMaxInfilRate)%dat(1) ,& ! intent(out): [dp] maximum infiltration rate (m s-1) - scalarInfiltration => flux_data%var(iLookFLUX%scalarInfiltration)%dat(1) ,& ! intent(out): [dp] infiltration of water into the soil profile (m s-1) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(inout): [dp(:)] fraction of liquid water in each snow layer (-) - ! boundary fluxes in the soil domain - scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(out): [dp] rain that reaches the ground without ever touching the canopy (kg m-2 s-1) - scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(out): [dp] drainage of liquid water from the vegetation canopy (kg m-2 s-1) - scalarRainPlusMelt => flux_data%var(iLookFLUX%scalarRainPlusMelt)%dat(1) ,& ! intent(out): [dp] rain plus melt (m s-1) - scalarSurfaceRunoff => flux_data%var(iLookFLUX%scalarSurfaceRunoff)%dat(1) ,& ! intent(out): [dp] surface runoff (m s-1) - ! fluxes for the aquifer - scalarAquiferTranspire => flux_data%var(iLookFLUX%scalarAquiferTranspire)%dat(1) ,& ! intent(out): [dp] transpiration loss from the aquifer (m s-1 - scalarAquiferRecharge => flux_data%var(iLookFLUX%scalarAquiferRecharge)%dat(1) ,& ! intent(out): [dp] recharge to the aquifer (m s-1) - scalarAquiferBaseflow => flux_data%var(iLookFLUX%scalarAquiferBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the aquifer (m s-1) - ! total runoff - scalarTotalRunoff => flux_data%var(iLookFLUX%scalarTotalRunoff)%dat(1) ,& ! intent(out): [dp] total runoff (m s-1) - ! derivatives in net vegetation energy fluxes w.r.t. relevant state variables - dCanairNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy air temperature - dCanairNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. canopy temperature - dCanairNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy air space flux w.r.t. ground temperature - dCanopyNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy air temperature - dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. canopy temperature - dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net canopy flux w.r.t. ground temperature - dCanopyNetFlux_dCanWat => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in net canopy fluxes w.r.t. canopy total water content - dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy air temperature - dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. canopy temperature - dGroundNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dGroundTemp )%dat(1) ,& ! intent(out): [dp] derivative in net ground flux w.r.t. ground temperature - dGroundNetFlux_dCanWat => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in net ground fluxes w.r.t. canopy total water content - ! derivatives in evaporative fluxes w.r.t. relevant state variables - dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy air temperature - dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy temperature - dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. ground temperature - dCanopyEvaporation_dCanWat => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in canopy evaporation w.r.t. canopy total water content - dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy air temperature - dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy temperature - dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. ground temperature - dGroundEvaporation_dCanWat => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in ground evaporation w.r.t. canopy total water content - ! derivatives in transpiration - dCanopyTrans_dTCanair => deriv_data%var(iLookDERIV%dCanopyTrans_dTCanair )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTCanopy => deriv_data%var(iLookDERIV%dCanopyTrans_dTCanopy )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTGround => deriv_data%var(iLookDERIV%dCanopyTrans_dTGround )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - dCanopyTrans_dCanWat => deriv_data%var(iLookDERIV%dCanopyTrans_dCanWat )%dat(1) ,& ! intent(out): [dp] derivative in canopy transpiration w.r.t. canopy total water content (s-1) - ! derivatives in canopy water w.r.t canopy temperature - dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(out): [dp] derivative of canopy liquid storage w.r.t. temperature - ! derivatives in canopy liquid fluxes w.r.t. canopy water - scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ,& ! intent(out): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water - scalarThroughfallRainDeriv => deriv_data%var(iLookDERIV%scalarThroughfallRainDeriv )%dat(1) ,& ! intent(out): [dp] derivative in throughfall w.r.t. canopy liquid water - scalarCanopyLiqDrainageDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDrainageDeriv)%dat(1) ,& ! intent(out): [dp] derivative in canopy drainage w.r.t. canopy liquid water - ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below - dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above - dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below - dThermalC_dWatAbove => deriv_data%var(iLookDERIV%dThermalC_dWatAbove )%dat ,& ! intent(in): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow )%dat ,& ! intent(in): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above - ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below - dNrgFlux_dWatAbove => deriv_data%var(iLookDERIV%dNrgFlux_dWatAbove )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. water state in the layer above - dNrgFlux_dWatBelow => deriv_data%var(iLookDERIV%dNrgFlux_dWatBelow )%dat ,& ! intent(out): [dp(:)] derivatives in the flux w.r.t. water state in the layer below - dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove )%dat ,& ! intent(in): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow )%dat ,& ! intent(in): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above - ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above - iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(out): [dp(:)] derivative in vertical liquid water flux at layer interfaces - ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables - dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above - dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below - dq_dHydStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dHydStateLayerSurfVec )%dat ,& ! intent(out): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers - mLayerdTheta_dPsi => deriv_data%var(iLookDERIV%mLayerdTheta_dPsi )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. psi - mLayerdPsi_dTheta => deriv_data%var(iLookDERIV%mLayerdPsi_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in the soil water characteristic w.r.t. theta - ! derivative in baseflow flux w.r.t. aquifer storage - dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] derivative in baseflow flux w.r.t. aquifer storage (s-1) - ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables - dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above - dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(out): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below - dq_dNrgStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dNrgStateLayerSurfVec )%dat ,& ! intent(out): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers - ! derivatives in soil transpiration w.r.t. canopy state variables - mLayerdTrans_dTCanair => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanair )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature - mLayerdTrans_dTCanopy => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanopy )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy temperature - mLayerdTrans_dTGround => deriv_data%var(iLookDERIV%mLayerdTrans_dTGround )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. ground temperature - mLayerdTrans_dCanWat => deriv_data%var(iLookDERIV%mLayerdTrans_dCanWat )%dat ,& ! intent(out): derivatives in the soil layer transpiration flux w.r.t. canopy total water - ! derivatives in aquifer transpiration w.r.t. canopy state variables - dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature - dAquiferTrans_dTCanopy => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanopy )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy temperature - dAquiferTrans_dTGround => deriv_data%var(iLookDERIV%dAquiferTrans_dTGround )%dat(1) ,& ! intent(out): derivatives in the aquifer transpiration flux w.r.t. ground temperature - dAquiferTrans_dCanWat => deriv_data%var(iLookDERIV%dAquiferTrans_dCanWat )%dat(1) & ! intent(out): derivatives in the aquifer transpiration flux w.r.t. canopy total water - ) ! end associating to data in structures - ! initialize error control - err=0; message='subTools/' - - ! Validate operation argument - if ((op/=iLookOP%pre).and.(op/=iLookOP%post)) then - err=20 - cmessage="Error in subTools: invalid op argument requested." - message=trim(message)//trim(cmessage); return - end if - - select case(sub) - case(iLookROUTINE%vegNrgFlux) ! vegNrgFlux - if (op==iLookOP%pre) then ! pre-processing - dCanLiq_dTcanopy = dTheta_dTkCanopy*iden_water*canopyDepth ! derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) - ! intent(in) arguments - in_vegNrgFlux % firstSubStep=firstSubStep ! intent(in): flag to indicate if we are processing the first sub-step - in_vegNrgFlux % firstFluxCall=firstFluxCall ! intent(in): flag to indicate if we are processing the first flux call - in_vegNrgFlux % computeVegFlux=computeVegFlux ! intent(in): flag to indicate if we need to compute fluxes over vegetation - in_vegNrgFlux % checkLWBalance=checkLWBalance ! intent(in): flag to check longwave balance - in_vegNrgFlux % upperBoundTemp=upperBoundTemp ! intent(in): temperature of the upper boundary (K) --> NOTE: use air temperature - in_vegNrgFlux % scalarCanairTempTrial=scalarCanairTempTrial ! intent(in): trial value of the canopy air space temperature (K) - in_vegNrgFlux % scalarCanopyTempTrial=scalarCanopyTempTrial ! intent(in): trial value of canopy temperature (K) - in_vegNrgFlux % mLayerTempTrial_1=mLayerTempTrial(1) ! intent(in): trial value of ground temperature (K) - in_vegNrgFlux % scalarCanopyIceTrial=scalarCanopyIceTrial ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) - in_vegNrgFlux % scalarCanopyLiqTrial=scalarCanopyLiqTrial ! intent(in): trial value of mass of liquid water on the vegetation canopy (kg m-2) - in_vegNrgFlux % dCanLiq_dTcanopy=dCanLiq_dTcanopy ! intent(in): derivative in canopy liquid storage w.r.t. canopy temperature (kg m-2 K-1) - else ! post-processing - ! intent(out) arguments - scalarCanopyTranspiration =out_vegNrgFlux % scalarCanopyTranspiration ! intent(out): canopy transpiration (kg m-2 s-1) - scalarCanopyEvaporation =out_vegNrgFlux % scalarCanopyEvaporation ! intent(out): canopy evaporation/condensation (kg m-2 s-1) - scalarGroundEvaporation =out_vegNrgFlux % scalarGroundEvaporation ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) - scalarCanairNetNrgFlux =out_vegNrgFlux % scalarCanairNetNrgFlux ! intent(out): net energy flux for the canopy air space (W m-2) - scalarCanopyNetNrgFlux =out_vegNrgFlux % scalarCanopyNetNrgFlux ! intent(out): net energy flux for the vegetation canopy (W m-2) - scalarGroundNetNrgFlux =out_vegNrgFlux % scalarGroundNetNrgFlux ! intent(out): net energy flux for the ground surface (W m-2) - dCanairNetFlux_dCanairTemp =out_vegNrgFlux % dCanairNetFlux_dCanairTemp ! intent(out): derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) - dCanairNetFlux_dCanopyTemp =out_vegNrgFlux % dCanairNetFlux_dCanopyTemp ! intent(out): derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) - dCanairNetFlux_dGroundTemp =out_vegNrgFlux % dCanairNetFlux_dGroundTemp ! intent(out): derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) - dCanopyNetFlux_dCanairTemp =out_vegNrgFlux % dCanopyNetFlux_dCanairTemp ! intent(out): derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) - dCanopyNetFlux_dCanopyTemp =out_vegNrgFlux % dCanopyNetFlux_dCanopyTemp ! intent(out): derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) - dCanopyNetFlux_dGroundTemp =out_vegNrgFlux % dCanopyNetFlux_dGroundTemp ! intent(out): derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - dGroundNetFlux_dCanairTemp =out_vegNrgFlux % dGroundNetFlux_dCanairTemp ! intent(out): derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) - dGroundNetFlux_dCanopyTemp =out_vegNrgFlux % dGroundNetFlux_dCanopyTemp ! intent(out): derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) - dGroundNetFlux_dGroundTemp =out_vegNrgFlux % dGroundNetFlux_dGroundTemp ! intent(out): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - dCanopyEvaporation_dCanWat =out_vegNrgFlux % dCanopyEvaporation_dCanWat ! intent(out): derivative in canopy evaporation w.r.t. canopy total water content (s-1) - dCanopyEvaporation_dTCanair=out_vegNrgFlux % dCanopyEvaporation_dTCanair ! intent(out): derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTCanopy=out_vegNrgFlux % dCanopyEvaporation_dTCanopy ! intent(out): derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyEvaporation_dTGround=out_vegNrgFlux % dCanopyEvaporation_dTGround ! intent(out): derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dCanWat =out_vegNrgFlux % dGroundEvaporation_dCanWat ! intent(out): derivative in ground evaporation w.r.t. canopy total water content (s-1) - dGroundEvaporation_dTCanair=out_vegNrgFlux % dGroundEvaporation_dTCanair ! intent(out): derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTCanopy=out_vegNrgFlux % dGroundEvaporation_dTCanopy ! intent(out): derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - dGroundEvaporation_dTGround=out_vegNrgFlux % dGroundEvaporation_dTGround ! intent(out): derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - dCanopyTrans_dCanWat =out_vegNrgFlux % dCanopyTrans_dCanWat ! intent(out): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - dCanopyTrans_dTCanair =out_vegNrgFlux % dCanopyTrans_dTCanair ! intent(out): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTCanopy =out_vegNrgFlux % dCanopyTrans_dTCanopy ! intent(out): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - dCanopyTrans_dTGround =out_vegNrgFlux % dCanopyTrans_dTGround ! intent(out): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - dCanopyNetFlux_dCanWat =out_vegNrgFlux % dCanopyNetFlux_dCanWat! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) - dGroundNetFlux_dCanWat =out_vegNrgFlux % dGroundNetFlux_dCanWat! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) - err =out_vegNrgFlux % err ! intent(out): error code - cmessage =out_vegNrgFlux % cmessage ! intent(out): error message - ! additional post-processing - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors - ! check fluxes - if (globalPrintFlag) then - print*, '**' - write(*,'(a,1x,10(f30.20))') 'canopyDepth = ', canopyDepth - write(*,'(a,1x,10(f30.20))') 'mLayerDepth(1:2) = ', mLayerDepth(1:2) - write(*,'(a,1x,10(f30.20))') 'scalarCanairTempTrial = ', scalarCanairTempTrial ! trial value of the canopy air space temperature (K) - write(*,'(a,1x,10(f30.20))') 'scalarCanopyTempTrial = ', scalarCanopyTempTrial ! trial value of canopy temperature (K) - write(*,'(a,1x,10(f30.20))') 'mLayerTempTrial(1:2) = ', mLayerTempTrial(1:2) ! trial value of ground temperature (K) - write(*,'(a,1x,10(f30.20))') 'scalarCanairNetNrgFlux = ', scalarCanairNetNrgFlux - write(*,'(a,1x,10(f30.20))') 'scalarCanopyNetNrgFlux = ', scalarCanopyNetNrgFlux - write(*,'(a,1x,10(f30.20))') 'scalarGroundNetNrgFlux = ', scalarGroundNetNrgFlux - write(*,'(a,1x,10(f30.20))') 'dGroundNetFlux_dGroundTemp = ', dGroundNetFlux_dGroundTemp - end if ! end if checking fluxes - end if - case(iLookROUTINE%ssdNrgFlux) ! ssdNrgFlux - if (op==iLookOP%pre) then ! pre-processing - ! intent(in) arguments - in_ssdNrgFlux % scalarSolution=scalarSolution .and. .not.firstFluxCall ! intent(in): flag to denote if implementing the scalar solution - in_ssdNrgFlux % scalarGroundNetNrgFlux=scalarGroundNetNrgFlux ! intent(in): net energy flux for the ground surface (W m-2) - in_ssdNrgFlux % iLayerLiqFluxSnow=iLayerLiqFluxSnow ! intent(in): liquid flux at the interface of each snow layer (m s-1) - in_ssdNrgFlux % iLayerLiqFluxSoil=iLayerLiqFluxSoil ! intent(in): liquid flux at the interface of each soil layer (m s-1) - in_ssdNrgFlux % mLayerTempTrial=mLayerTempTrial ! intent(in): temperature in each layer at the current iteration (m) - in_ssdNrgFlux % dThermalC_dWatAbove=dThermalC_dWatAbove ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above - in_ssdNrgFlux % dThermalC_dWatBelow=dThermalC_dWatBelow ! intent(in): derivative in the thermal conductivity w.r.t. water state in the layer above - in_ssdNrgFlux % dThermalC_dTempAbove=dThermalC_dTempAbove ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above - in_ssdNrgFlux % dThermalC_dTempBelow=dThermalC_dTempBelow ! intent(in): derivative in the thermal conductivity w.r.t. energy state in the layer above - ! intent(inout) arguments - io_ssdNrgFlux % dGroundNetFlux_dGroundTemp=dGroundNetFlux_dGroundTemp ! intent(inout): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - else ! post-processing - ! intent(inout) arguments - dGroundNetFlux_dGroundTemp=io_ssdNrgFlux % dGroundNetFlux_dGroundTemp ! intent(inout): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - ! intent(out) arguments - iLayerNrgFlux =out_ssdNrgFlux % iLayerNrgFlux ! intent(out): energy flux at the layer interfaces (W m-2) - dNrgFlux_dTempAbove=out_ssdNrgFlux % dNrgFlux_dTempAbove ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) - dNrgFlux_dTempBelow=out_ssdNrgFlux % dNrgFlux_dTempBelow ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) - dNrgFlux_dWatAbove =out_ssdNrgFlux % dNrgFlux_dWatAbove ! intent(out): derivatives in the flux w.r.t. water state in the layer above (J m-2 s-1 K-1) - dNrgFlux_dWatBelow =out_ssdNrgFlux % dNrgFlux_dWatBelow ! intent(out): derivatives in the flux w.r.t. water state in the layer below (J m-2 s-1 K-1) - err =out_ssdNrgFlux % err ! intent(out): error code - cmessage =out_ssdNrgFlux % cmessage ! intent(out): error message - ! error control - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - ! calculate net energy fluxes for each snow and soil layer (J m-3 s-1) - do iLayer=1,nLayers - mLayerNrgFlux(iLayer) = -(iLayerNrgFlux(iLayer) - iLayerNrgFlux(iLayer-1))/mLayerDepth(iLayer) - if (globalPrintFlag) then - if (iLayer < 10) write(*,'(a,1x,i4,1x,10(f25.15,1x))') 'iLayer, iLayerNrgFlux(iLayer-1:iLayer), mLayerNrgFlux(iLayer) = ', iLayer, iLayerNrgFlux(iLayer-1:iLayer), mLayerNrgFlux(iLayer) - end if - end do - end if - case(iLookROUTINE%vegLiqFlux) ! vegLiqFlux - if (op==iLookOP%pre) then ! pre-processing - ! intent(in) arguments - in_vegLiqFlux % computeVegFlux =computeVegFlux ! intent(in): flag to denote if computing energy flux over vegetation - in_vegLiqFlux % scalarCanopyLiqTrial=scalarCanopyLiqTrial ! intent(in): trial mass of liquid water on the vegetation canopy at the current iteration (kg m-2) - in_vegLiqFlux % scalarRainfall =scalarRainfall ! intent(in): rainfall rate (kg m-2 s-1) - else ! post-processing - ! intent(out) arguments - scalarThroughfallRain =out_vegLiqFlux % scalarThroughfallRain ! intent(out): rain that reaches the ground without ever touching the canopy (kg m-2 s-1) - scalarCanopyLiqDrainage =out_vegLiqFlux % scalarCanopyLiqDrainage ! intent(out): drainage of liquid water from the vegetation canopy (kg m-2 s-1) - scalarThroughfallRainDeriv =out_vegLiqFlux % scalarThroughfallRainDeriv ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) - scalarCanopyLiqDrainageDeriv=out_vegLiqFlux % scalarCanopyLiqDrainageDeriv! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) - err =out_vegLiqFlux % err ! intent(out): error code - cmessage =out_vegLiqFlux % cmessage ! intent(out): error control - ! error control - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - ! calculate the net liquid water flux for the vegetation canopy - scalarCanopyNetLiqFlux = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage - ! calculate the total derivative in the downward liquid flux - scalarCanopyLiqDeriv = scalarThroughfallRainDeriv + scalarCanopyLiqDrainageDeriv - ! test - if (globalPrintFlag) then - print*, '**' - print*, 'scalarRainfall = ', scalarRainfall - print*, 'scalarThroughfallRain = ', scalarThroughfallRain - print*, 'scalarCanopyEvaporation = ', scalarCanopyEvaporation - print*, 'scalarCanopyLiqDrainage = ', scalarCanopyLiqDrainage - print*, 'scalarCanopyNetLiqFlux = ', scalarCanopyNetLiqFlux - print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial - end if - end if - case(iLookROUTINE%snowLiqFlx) ! snowLiqFlx - if (op==iLookOP%pre) then ! pre-processing - ! intent(in) arguments - in_snowLiqFlx % nSnow =nSnow ! intent(in): number of snow layers - in_snowLiqFlx % firstFluxCall =firstFluxCall ! intent(in): the first flux call (compute variables that are constant over the iterations) - in_snowLiqFlx % scalarSolution =(scalarSolution .and. .not.firstFluxCall) ! intent(in): flag to indicate the scalar solution - in_snowLiqFlx % scalarThroughfallRain =scalarThroughfallRain ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) - in_snowLiqFlx % scalarCanopyLiqDrainage=scalarCanopyLiqDrainage ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) - in_snowLiqFlx % mLayerVolFracLiqTrial =mLayerVolFracLiqTrial(1:nSnow) ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) - ! intent(inout) arguments - io_snowLiqFlx % iLayerLiqFluxSnow =iLayerLiqFluxSnow ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) - io_snowLiqFlx % iLayerLiqFluxSnowDeriv =iLayerLiqFluxSnowDeriv ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) - else ! post-processing - ! intent(inout) arguments - iLayerLiqFluxSnow =io_snowLiqFlx % iLayerLiqFluxSnow ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) - iLayerLiqFluxSnowDeriv=io_snowLiqFlx % iLayerLiqFluxSnowDeriv ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) - ! intent(out) arguments - err =out_snowLiqFlx % err ! intent(out): error code - cmessage=out_snowLiqFlx % cmessage ! intent(out): error message - ! error control - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - ! define forcing for the soil domain - scalarRainPlusMelt = iLayerLiqFluxSnow(nSnow) ! drainage from the base of the snowpack - ! calculate net liquid water fluxes for each snow layer (s-1) - do iLayer=1,nSnow - mLayerLiqFluxSnow(iLayer) = -(iLayerLiqFluxSnow(iLayer) - iLayerLiqFluxSnow(iLayer-1))/mLayerDepth(iLayer) - end do - ! compute drainage from the soil zone (needed for mass balance checks) - scalarSnowDrainage = iLayerLiqFluxSnow(nSnow) - ! save bottom layer of snow derivatives - above_soilLiqFluxDeriv = iLayerLiqFluxSnowDeriv(nSnow) ! derivative in vertical liquid water flux at bottom snow layer interface - above_soildLiq_dTk = mLayerdTheta_dTk(nSnow) ! derivative in volumetric liquid water content in bottom snow layer w.r.t. temperature - above_soilFracLiq = mLayerFracLiqSnow(nSnow) ! fraction of liquid water in bottom snow layer (-) - end if - case(iLookROUTINE%soilLiqFlx) ! soilLiqFlx - if (op==iLookOP%pre) then ! pre-processing - ! intent(in) arguments - in_soilLiqFlx % nSoil =nSoil ! intent(in): number of soil layers - in_soilLiqFlx % firstSplitOper=firstSplitOper ! intent(in): flag indicating first flux call in a splitting operation - in_soilLiqFlx % scalarSolution=(scalarSolution .and. .not.firstFluxCall) ! intent(in): flag to indicate the scalar solution - in_soilLiqFlx % deriv_desired =.true. ! intent(in): flag indicating if derivatives are desired - in_soilLiqFlx % mLayerTempTrial=mLayerTempTrial(nSnow+1:nLayers) ! intent(in): trial temperature at the current iteration (K) - in_soilLiqFlx % mLayerMatricHeadTrial =mLayerMatricHeadTrial(1:nSoil) ! intent(in): matric potential (m) - in_soilLiqFlx % mLayerMatricHeadLiqTrial=mLayerMatricHeadLiqTrial(1:nSoil) ! intent(in): liquid water matric potential (m) - in_soilLiqFlx % mLayerVolFracLiqTrial=mLayerVolFracLiqTrial(nSnow+1:nLayers) ! intent(in): volumetric fraction of liquid water (-) - in_soilLiqFlx % mLayerVolFracIceTrial=mLayerVolFracIceTrial(nSnow+1:nLayers) ! intent(in): volumetric fraction of ice (-) - in_soilLiqFlx % mLayerdTheta_dTk=mLayerdTheta_dTk(nSnow+1:nLayers) ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - in_soilLiqFlx % dPsiLiq_dTemp=dPsiLiq_dTemp(1:nSoil) ! intent(in): derivative in liquid water matric potential w.r.t. temperature (m K-1) - in_soilLiqFlx % dCanopyTrans_dCanWat =dCanopyTrans_dCanWat ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - in_soilLiqFlx % dCanopyTrans_dTCanair =dCanopyTrans_dTCanair ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - in_soilLiqFlx % dCanopyTrans_dTCanopy =dCanopyTrans_dTCanopy ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - in_soilLiqFlx % dCanopyTrans_dTGround =dCanopyTrans_dTGround ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - in_soilLiqFlx % above_soilLiqFluxDeriv=above_soilLiqFluxDeriv ! intent(in): derivative in layer above soil (canopy or snow) liquid flux w.r.t. liquid water - in_soilLiqFlx % above_soildLiq_dTk =above_soildLiq_dTk ! intent(in): derivative of layer above soil (canopy or snow) liquid flux w.r.t. temperature - in_soilLiqFlx % above_soilFracLiq =above_soilFracLiq ! intent(in): fraction of liquid water layer above soil (canopy or snow) (-) - in_soilLiqFlx % scalarCanopyTranspiration=scalarCanopyTranspiration ! intent(in): canopy transpiration (kg m-2 s-1) - in_soilLiqFlx % scalarGroundEvaporation =scalarGroundEvaporation ! intent(in): ground evaporation (kg m-2 s-1) - in_soilLiqFlx % scalarRainPlusMelt =scalarRainPlusMelt ! intent(in): rain plus melt (m s-1) - ! intent(inout) arguments - io_soilLiqFlx % scalarMaxInfilRate =scalarMaxInfilRate ! intent(inout): maximum infiltration rate (m s-1) - io_soilLiqFlx % scalarInfilArea =scalarInfilArea ! intent(inout): fraction of unfrozen area where water can infiltrate (-) - io_soilLiqFlx % scalarFrozenArea =scalarFrozenArea ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) - io_soilLiqFlx % scalarSurfaceRunoff =scalarSurfaceRunoff ! intent(inout): surface runoff (m s-1) - io_soilLiqFlx % mLayerdTheta_dPsi =mLayerdTheta_dPsi ! intent(inout): derivative in the soil water characteristic w.r.t. psi (m-1) - io_soilLiqFlx % mLayerdPsi_dTheta =mLayerdPsi_dTheta ! intent(inout): derivative in the soil water characteristic w.r.t. theta (m) - io_soilLiqFlx % dHydCond_dMatric =dHydCond_dMatric ! intent(inout): derivative in hydraulic conductivity w.r.t matric head (s-1) - io_soilLiqFlx % scalarInfiltration =scalarInfiltration ! intent(inout): surface infiltration rate (m s-1) -- controls on infiltration only computed for iter==1 - io_soilLiqFlx % iLayerLiqFluxSoil =iLayerLiqFluxSoil ! intent(inout): liquid fluxes at layer interfaces (m s-1) - io_soilLiqFlx % mLayerTranspire =mLayerTranspire ! intent(inout): transpiration loss from each soil layer (m s-1) - io_soilLiqFlx % mLayerHydCond =mLayerHydCond ! intent(inout): hydraulic conductivity in each layer (m s-1) - io_soilLiqFlx % dq_dHydStateAbove =dq_dHydStateAbove ! intent(inout): derivatives in the flux w.r.t. matric head in the layer above (s-1) - io_soilLiqFlx % dq_dHydStateBelow =dq_dHydStateBelow ! intent(inout): derivatives in the flux w.r.t. matric head in the layer below (s-1) - io_soilLiqFlx % dq_dHydStateLayerSurfVec=dq_dHydStateLayerSurfVec ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) - io_soilLiqFlx % dq_dNrgStateAbove =dq_dNrgStateAbove ! intent(inout): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) - io_soilLiqFlx % dq_dNrgStateBelow =dq_dNrgStateBelow ! intent(inout): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) - io_soilLiqFlx % dq_dNrgStateLayerSurfVec=dq_dNrgStateLayerSurfVec ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) - io_soilLiqFlx % mLayerdTrans_dTCanair =mLayerdTrans_dTCanair ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature - io_soilLiqFlx % mLayerdTrans_dTCanopy =mLayerdTrans_dTCanopy ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy temperature - io_soilLiqFlx % mLayerdTrans_dTGround =mLayerdTrans_dTGround ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. ground temperature - io_soilLiqFlx % mLayerdTrans_dCanWat =mLayerdTrans_dCanWat ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy total water - else ! post-processing - ! intent(inout) arguments - scalarMaxInfilRate =io_soilLiqFlx % scalarMaxInfilRate ! intent(inout): maximum infiltration rate (m s-1) - scalarInfilArea =io_soilLiqFlx % scalarInfilArea ! intent(inout): fraction of unfrozen area where water can infiltrate (-) - scalarFrozenArea =io_soilLiqFlx % scalarFrozenArea ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) - scalarSurfaceRunoff =io_soilLiqFlx % scalarSurfaceRunoff ! intent(inout): surface runoff (m s-1) - mLayerdTheta_dPsi =io_soilLiqFlx % mLayerdTheta_dPsi ! intent(inout): derivative in the soil water characteristic w.r.t. psi (m-1) - mLayerdPsi_dTheta =io_soilLiqFlx % mLayerdPsi_dTheta ! intent(inout): derivative in the soil water characteristic w.r.t. theta (m) - dHydCond_dMatric =io_soilLiqFlx % dHydCond_dMatric ! intent(inout): derivative in hydraulic conductivity w.r.t matric head (s-1) - scalarInfiltration =io_soilLiqFlx % scalarInfiltration ! intent(inout): surface infiltration rate (m s-1) -- controls on infiltration only computed for iter==1 - iLayerLiqFluxSoil =io_soilLiqFlx % iLayerLiqFluxSoil ! intent(inout): liquid fluxes at layer interfaces (m s-1) - mLayerTranspire =io_soilLiqFlx % mLayerTranspire ! intent(inout): transpiration loss from each soil layer (m s-1) - mLayerHydCond =io_soilLiqFlx % mLayerHydCond ! intent(inout): hydraulic conductivity in each layer (m s-1) - dq_dHydStateAbove =io_soilLiqFlx % dq_dHydStateAbove ! intent(inout): derivatives in the flux w.r.t. matric head in the layer above (s-1) - dq_dHydStateBelow =io_soilLiqFlx % dq_dHydStateBelow ! intent(inout): derivatives in the flux w.r.t. matric head in the layer below (s-1) - dq_dHydStateLayerSurfVec=io_soilLiqFlx % dq_dHydStateLayerSurfVec ! intent(inout): derivative in surface infiltration w.r.t. hydrology state in above soil snow or canopy and every soil layer (m s-1 or s-1) - dq_dNrgStateAbove =io_soilLiqFlx % dq_dNrgStateAbove ! intent(inout): derivatives in the flux w.r.t. temperature in the layer above (m s-1 K-1) - dq_dNrgStateBelow =io_soilLiqFlx % dq_dNrgStateBelow ! intent(inout): derivatives in the flux w.r.t. temperature in the layer below (m s-1 K-1) - dq_dNrgStateLayerSurfVec=io_soilLiqFlx % dq_dNrgStateLayerSurfVec ! intent(inout): derivative in surface infiltration w.r.t. energy state in above soil snow or canopy and every soil layer (m s-1 K-1) - mLayerdTrans_dTCanair =io_soilLiqFlx % mLayerdTrans_dTCanair ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature - mLayerdTrans_dTCanopy =io_soilLiqFlx % mLayerdTrans_dTCanopy ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy temperature - mLayerdTrans_dTGround =io_soilLiqFlx % mLayerdTrans_dTGround ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. ground temperature - mLayerdTrans_dCanWat =io_soilLiqFlx % mLayerdTrans_dCanWat ! intent(inout): derivatives in the soil layer transpiration flux w.r.t. canopy total water - ! intent(out) arguments - err =out_soilLiqFlx % err ! intent(out): error code - cmessage =out_soilLiqFlx % cmessage ! intent(out): error message - ! error control - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! calculate net liquid water fluxes for each soil layer (s-1) - do iLayer=1,nSoil - mLayerLiqFluxSoil(iLayer) = -(iLayerLiqFluxSoil(iLayer) - iLayerLiqFluxSoil(iLayer-1))/mLayerDepth(iLayer+nSnow) - end do - - ! calculate the soil control on infiltration - if (nSnow==0) then - ! * case of infiltration into soil - if (scalarMaxInfilRate > scalarRainPlusMelt) then ! infiltration is not rate-limited - scalarSoilControl = (1._rkind - scalarFrozenArea)*scalarInfilArea - else - scalarSoilControl = 0._rkind ! (scalarRainPlusMelt exceeds maximum infiltration rate - end if - else - ! * case of infiltration into snow - scalarSoilControl = 1._rkind - end if - - ! compute drainage from the soil zone (needed for mass balance checks and in aquifer recharge) - scalarSoilDrainage = iLayerLiqFluxSoil(nSoil) - - ! expand derivatives to the total water matric potential - ! NOTE: arrays are offset because computing derivatives in interface fluxes, at the top and bottom of the layer respectively - if (globalPrintFlag) print*, 'dPsiLiq_dPsi0(1:nSoil) = ', dPsiLiq_dPsi0(1:nSoil) - dq_dHydStateAbove(1:nSoil) = dq_dHydStateAbove(1:nSoil) *dPsiLiq_dPsi0(1:nSoil) - dq_dHydStateBelow(0:nSoil-1) = dq_dHydStateBelow(0:nSoil-1)*dPsiLiq_dPsi0(1:nSoil) - dq_dHydStateLayerSurfVec(1:nSoil) = dq_dHydStateLayerSurfVec(1:nSoil)*dPsiLiq_dPsi0(1:nSoil) - end if - case(iLookROUTINE%groundwatr) ! groundwatr - if (op==iLookOP%pre) then ! pre-processing - ! check the derivative matrix is sized appropriately - if (size(dBaseflow_dMatric,1)/=nSoil .or. size(dBaseflow_dMatric,2)/=nSoil) then - message=trim(message)//'expect dBaseflow_dMatric to be nSoil x nSoil' - err=20; return - end if - ! intent(in) arguments - in_groundwatr % nSnow = nSnow ! intent(in): number of snow layers - in_groundwatr % nSoil = nSoil ! intent(in): number of soil layers - in_groundwatr % nLayers = nLayers ! intent(in): total number of layers - in_groundwatr % firstFluxCall = firstFluxCall ! intent(in): logical flag to compute index of the lowest saturated layer - in_groundwatr % mLayerdTheta_dPsi = mLayerdTheta_dPsi ! intent(in): derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) - in_groundwatr % mLayerMatricHeadLiqTrial = mLayerMatricHeadLiqTrial ! intent(in): liquid water matric potential (m) - in_groundwatr % mLayerVolFracLiqTrial = mLayerVolFracLiqTrial(nSnow+1:nLayers) ! intent(in): volumetric fraction of liquid water (-) - in_groundwatr % mLayerVolFracIceTrial = mLayerVolFracIceTrial(nSnow+1:nLayers) ! intent(in): volumetric fraction of ice (-) - ! intent(inout) arguments - io_groundwatr % ixSaturation = ixSaturation ! intent(inout): index of lowest saturated layer (NOTE: only computed on the first iteration) - else ! post-processing - ! intent(inout) arguments - ixSaturation = io_groundwatr % ixSaturation ! intent(inout): index of lowest saturated layer (NOTE: only computed on the first iteration) - ! intent(out) arguments - mLayerBaseflow = out_groundwatr % mLayerBaseflow ! intent(out): baseflow from each soil layer (m s-1) - dBaseflow_dMatric = out_groundwatr % dBaseflow_dMatric ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - err = out_groundwatr % err ! intent(out): error code - cmessage = out_groundwatr % cmessage ! intent(out): error message - ! error control - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - end if - case(iLookROUTINE%bigAquifer) ! bigAquifer - if (op==iLookOP%pre) then ! pre-processing - ! intent(in) arguments - in_bigAquifer % scalarAquiferStorageTrial = scalarAquiferStorageTrial ! intent(in): trial value of aquifer storage (m) - in_bigAquifer % scalarCanopyTranspiration = scalarCanopyTranspiration ! intent(in): canopy transpiration (kg m-2 s-1) - in_bigAquifer % scalarSoilDrainage = scalarSoilDrainage ! intent(in): soil drainage (m s-1) - in_bigAquifer % dCanopyTrans_dCanWat = dCanopyTrans_dCanWat ! intent(in): derivative in canopy transpiration w.r.t. canopy total water content (s-1) - in_bigAquifer % dCanopyTrans_dTCanair = dCanopyTrans_dTCanair ! intent(in): derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - in_bigAquifer % dCanopyTrans_dTCanopy = dCanopyTrans_dTCanopy ! intent(in): derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - in_bigAquifer % dCanopyTrans_dTGround = dCanopyTrans_dTGround ! intent(in): derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - ! intent(inout) arguments - io_bigAquifer % dAquiferTrans_dTCanair = dAquiferTrans_dTCanair ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature - io_bigAquifer % dAquiferTrans_dTCanopy = dAquiferTrans_dTCanopy ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy temperature - io_bigAquifer % dAquiferTrans_dTGround = dAquiferTrans_dTGround ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. ground temperature - io_bigAquifer % dAquiferTrans_dCanWat = dAquiferTrans_dCanWat ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy total water - else ! post-processing - ! intent(inout) arguments - dAquiferTrans_dTCanair = io_bigAquifer % dAquiferTrans_dTCanair ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature - dAquiferTrans_dTCanopy = io_bigAquifer % dAquiferTrans_dTCanopy ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy temperature - dAquiferTrans_dTGround = io_bigAquifer % dAquiferTrans_dTGround ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. ground temperature - dAquiferTrans_dCanWat = io_bigAquifer % dAquiferTrans_dCanWat ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy total water - ! intent(out) arguments - scalarAquiferTranspire = out_bigAquifer % scalarAquiferTranspire ! intent(out): transpiration loss from the aquifer (m s-1) - scalarAquiferRecharge = out_bigAquifer % scalarAquiferRecharge ! intent(out): recharge to the aquifer (m s-1) - scalarAquiferBaseflow = out_bigAquifer % scalarAquiferBaseflow ! intent(out): total baseflow from the aquifer (m s-1) - dBaseflow_dAquifer = out_bigAquifer % dBaseflow_dAquifer ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) - err = out_bigAquifer % err ! intent(out): error code - cmessage = out_bigAquifer % cmessage ! intent(out): error message - ! error control - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - ! compute total runoff (overwrite previously calculated value before considering aquifer). - ! (Note: SoilDrainage goes into aquifer, not runoff) - scalarTotalRunoff = scalarSurfaceRunoff + scalarAquiferBaseflow - end if - case default ! Error control for sub argument (must be s subroutine index that is included in the above case blocks) - err=20 - cmessage="Error in subTools: invalid sub argument requested." - message=trim(message)//trim(cmessage); return - end select - - end associate ! end associate block - - end subroutine subTools - + ! **** vegNrgFlux **** subroutine initialize_vegNrgFlux associate(& dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy)%dat(1), & ! intent(out): [dp] derivative of canopy liquid storage w.r.t. temperature @@ -957,7 +479,9 @@ subroutine finalize_vegNrgFlux end if ! end if checking fluxes end associate end subroutine finalize_vegNrgFlux + ! **** end vegNrgFlux **** + ! **** ssdNrgFlux **** subroutine initialize_ssdNrgFlux call in_ssdNrgFlux%initialize(scalarSolution,firstFluxCall,mLayerTempTrial,flux_data,deriv_data) call io_ssdNrgFlux%initialize(deriv_data) @@ -981,7 +505,9 @@ subroutine finalize_ssdNrgFlux end do end associate end subroutine finalize_ssdNrgFlux + ! **** end ssdNrgFlux **** + ! **** vegLiqFlux **** subroutine initialize_vegLiqFlux call in_vegLiqFlux%initialize(computeVegFlux,scalarCanopyLiqTrial,flux_data) end subroutine initialize_vegLiqFlux @@ -1015,7 +541,9 @@ subroutine finalize_vegLiqFlux end if end associate end subroutine finalize_vegLiqFlux + ! **** end vegLiqFlux **** + ! **** snowLiqFlx **** subroutine initialize_snowLiqFlx call in_snowLiqFlx%initialize(nSnow,firstFluxCall,scalarSolution,mLayerVolFracLiqTrial,flux_data) call io_snowLiqFlx%initialize(flux_data,deriv_data) @@ -1049,7 +577,9 @@ subroutine finalize_snowLiqFlx above_soilFracLiq = mLayerFracLiqSnow(nSnow) ! fraction of liquid water in bottom snow layer (-) end associate end subroutine finalize_snowLiqFlx + ! **** end snowLiqFlx **** + ! **** soilLiqFlx **** subroutine initialize_soilLiqFlx call in_soilLiqFlx%initialize(nsnow,nSoil,nlayers,firstSplitOper,scalarSolution,firstFluxCall,& mLayerTempTrial,mLayerMatricHeadTrial,mLayerMatricHeadLiqTrial,mLayerVolFracLiqTrial,mLayerVolFracIceTrial,& @@ -1109,7 +639,9 @@ subroutine finalize_soilLiqFlx dq_dHydStateLayerSurfVec(1:nSoil) = dq_dHydStateLayerSurfVec(1:nSoil)*dPsiLiq_dPsi0(1:nSoil) end associate end subroutine finalize_soilLiqFlx + ! **** end soilLiqFlx **** + ! **** groundwatr **** subroutine initialize_groundwatr ! check the derivative matrix is sized appropriately if (size(dBaseflow_dMatric,1)/=nSoil .or. size(dBaseflow_dMatric,2)/=nSoil) then @@ -1126,7 +658,9 @@ subroutine finalize_groundwatr ! error control if (err/=0) then; message=trim(message)//trim(cmessage); return; end if end subroutine finalize_groundwatr + ! **** end groundwatr **** + ! **** bigAquifer **** subroutine initialize_bigAquifer call in_bigAquifer%initialize(scalarAquiferStorageTrial,flux_data,deriv_data) call io_bigAquifer%initialize(deriv_data) @@ -1146,6 +680,7 @@ subroutine finalize_bigAquifer scalarTotalRunoff = scalarSurfaceRunoff + scalarAquiferBaseflow end associate end subroutine finalize_bigAquifer + ! **** end bigAquifer **** end subroutine computFlux From e066692595596f6029a3f7bd87973efa189cf98b Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 7 Oct 2023 04:13:52 -0600 Subject: [PATCH 0948/1472] Modularized initialize and finalize operations for computFlux -- reduced the main associate statement. --- build/source/engine/computFlux.f90 | 143 +++++++++++++++++------------ 1 file changed, 82 insertions(+), 61 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index fcc057ec2..f480987e7 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -237,39 +237,20 @@ subroutine computFlux(& associate(& ! model decisions ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization - ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) - ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) ! indices of model state variables for the vegetation subdomain ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer - ! indices of model state variables for the snow+soil domain - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] type of layer (iname_soil or iname_snow) ! number of state variables of a specific type nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain ! derivatives mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - ! number of flux calls - numFluxCalls => diag_data%var(iLookDIAG%numFluxCalls)%dat(1) ,& ! intent(out): [dp] number of flux calls (-) - ! net fluxes over the vegetation domain - scalarCanairNetNrgFlux => flux_data%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the canopy air space (W m-2) - scalarCanopyNetNrgFlux => flux_data%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1) ,& ! intent(out): [dp] net energy flux for the vegetation canopy (W m-2) - scalarCanopyNetLiqFlux => flux_data%var(iLookFLUX%scalarCanopyNetLiqFlux)%dat(1) ,& ! intent(out): [dp] net liquid water flux for the vegetation canopy (kg m-2 s-1) - ! net fluxes over the snow+soil domain - mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) - mLayerLiqFluxSnow => flux_data%var(iLookFLUX%mLayerLiqFluxSnow)%dat ,& ! intent(out): [dp] net liquid water flux for each snow layer (s-1) - mLayerLiqFluxSoil => flux_data%var(iLookFLUX%mLayerLiqFluxSoil)%dat ,& ! intent(out): [dp] net liquid water flux for each soil layer (s-1) ! fluxes for the snow+soil domain iLayerLiqFluxSnow => flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at snow layer interfaces (-) - iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at soil layer interfaces (-) mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(out): [dp(:)] baseflow from each soil layer (m s-1) scalarSoilDrainage => flux_data%var(iLookFLUX%scalarSoilDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the soil profile (m s-1) scalarSoilBaseflow => flux_data%var(iLookFLUX%scalarSoilBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the soil profile (m s-1) @@ -299,21 +280,7 @@ subroutine computFlux(& dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) & ! intent(out): [dp(:)] derivative in baseflow flux w.r.t. aquifer storage (s-1) ) ! end association to data in structures - numFluxCalls = numFluxCalls+1 ! increment the number of flux calls - - ! modify the groundwater representation for this single-column implementation - select case(ixSpatialGroundwater) - case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale - case(localColumn); local_ixGroundwater = ixGroundwater ! go with the specified decision - case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return - end select ! end modify the groundwater representation for this single-column implementation - - ! initialize liquid water fluxes throughout the snow and soil domains - ! NOTE: used in the energy routines, which is called before the hydrology routines - if (firstFluxCall) then - if (nSnow>0) iLayerLiqFluxSnow(0:nSnow) = 0._rkind - iLayerLiqFluxSoil(0:nSoil) = 0._rkind - end if + call initialize_computFlux ! *** CALCULATE ENERGY FLUXES OVER VEGETATION *** ! identify the need to calculate the energy flux over vegetation @@ -407,38 +374,92 @@ subroutine computFlux(& end if ! end check aquifer model decision end if ! if computing aquifer fluxes - ! *** WRAP UP *** - ! define model flux vector for the vegetation sub-domain - if (ixCasNrg/=integerMissing) fluxVec(ixCasNrg) = scalarCanairNetNrgFlux/canopyDepth - if (ixVegNrg/=integerMissing) fluxVec(ixVegNrg) = scalarCanopyNetNrgFlux/canopyDepth - if (ixVegHyd/=integerMissing) fluxVec(ixVegHyd) = scalarCanopyNetLiqFlux ! NOTE: solid fluxes are handled separately - if (nSnowSoilNrg>0) then ! if necessary, populate the flux vector for energy - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! loop through non-missing energy state variables in the snow+soil domain - fluxVec( ixSnowSoilNrg(iLayer) ) = mLayerNrgFlux(iLayer) - end do - end if - ! populate the flux vector for hydrology - ! NOTE: ixVolFracWat and ixVolFracLiq can also include states in the soil domain, hence enable primary variable switching - if (nSnowSoilHyd>0) then ! check if any hydrology states exist - do iLayer=1,nLayers ! loop through non-missing energy state variables in the snow+soil domain - if (ixSnowSoilHyd(iLayer)/=integerMissing) then ! check if a given hydrology state exists - select case(layerType(iLayer)) - case(iname_snow); fluxVec(ixSnowSoilHyd(iLayer)) = mLayerLiqFluxSnow(iLayer) - case(iname_soil); fluxVec(ixSnowSoilHyd(iLayer)) = mLayerLiqFluxSoil(iLayer-nSnow) - case default; err=20; message=trim(message)//'expect layerType to be either iname_snow or iname_soil'; return - end select - end if ! end if a given hydrology state exists - end do - end if ! end if any hydrology states exist - ! compute the flux vector for the aquifer - if (ixAqWat/=integerMissing) fluxVec(ixAqWat) = scalarAquiferTranspire + scalarAquiferRecharge - scalarAquiferBaseflow - - firstFluxCall=.false. ! set the first flux call to false + call finalize_computFlux end associate ! end association to variables in the data structures contains + subroutine initialize_computFlux + associate(& + numFluxCalls => diag_data%var(iLookDIAG%numFluxCalls)%dat(1), & ! intent(out): [dp] number of flux calls (-) + ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision, & ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) + ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision, & ! intent(in): [i4b] groundwater parameterization + iLayerLiqFluxSnow => flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat, & ! intent(out): [dp(0:)] vertical liquid water flux at snow layer interfaces (-) + iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ) ! intent(out): [dp(0:)] vertical liquid water flux at soil layer interfaces (-) + + numFluxCalls = numFluxCalls+1 ! increment the number of flux calls + + ! modify the groundwater representation for this single-column implementation + select case(ixSpatialGroundwater) + case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale + case(localColumn); local_ixGroundwater = ixGroundwater ! go with the specified decision + case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return + end select ! end modify the groundwater representation for this single-column implementation + + ! initialize liquid water fluxes throughout the snow and soil domains + ! NOTE: used in the energy routines, which is called before the hydrology routines + if (firstFluxCall) then + if (nSnow>0) iLayerLiqFluxSnow(0:nSnow) = 0._rkind + iLayerLiqFluxSoil(0:nSoil) = 0._rkind + end if + end associate + end subroutine initialize_computFlux + + subroutine finalize_computFlux + associate(& + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1), & ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1), & ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1), & ! intent(in): [i4b] index of canopy hydrology state variable (mass) + scalarCanairNetNrgFlux => flux_data%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1), & ! intent(out): [dp] net energy flux for the canopy air space (W m-2) + scalarCanopyNetNrgFlux => flux_data%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1), & ! intent(out): [dp] net energy flux for the vegetation canopy (W m-2) + scalarCanopyNetLiqFlux => flux_data%var(iLookFLUX%scalarCanopyNetLiqFlux)%dat(1), & ! intent(out): [dp] net liquid water flux for the vegetation canopy (kg m-2 s-1) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): [dp] canopy depth (m) + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg)%dat(1), & ! intent(in): [i4b] number of energy state variables in the snow+soil domain + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat, & ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain + mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ) ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) + ! *** WRAP UP *** + ! define model flux vector for the vegetation sub-domain + if (ixCasNrg/=integerMissing) fluxVec(ixCasNrg) = scalarCanairNetNrgFlux/canopyDepth + if (ixVegNrg/=integerMissing) fluxVec(ixVegNrg) = scalarCanopyNetNrgFlux/canopyDepth + if (ixVegHyd/=integerMissing) fluxVec(ixVegHyd) = scalarCanopyNetLiqFlux ! NOTE: solid fluxes are handled separately + if (nSnowSoilNrg>0) then ! if necessary, populate the flux vector for energy + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! loop through non-missing energy state variables in the snow+soil domain + fluxVec( ixSnowSoilNrg(iLayer) ) = mLayerNrgFlux(iLayer) + end do + end if + end associate + + associate(& + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1), & ! intent(in): [i4b] index of water storage in the aquifer + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat, & ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd)%dat(1), & ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): [i4b(:)] type of layer (iname_soil or iname_snow) + mLayerLiqFluxSnow => flux_data%var(iLookFLUX%mLayerLiqFluxSnow)%dat, & ! intent(out): [dp] net liquid water flux for each snow layer (s-1) + mLayerLiqFluxSoil => flux_data%var(iLookFLUX%mLayerLiqFluxSoil)%dat, & ! intent(out): [dp] net liquid water flux for each soil layer (s-1) + scalarAquiferTranspire => flux_data%var(iLookFLUX%scalarAquiferTranspire)%dat(1), & ! intent(out): [dp] transpiration loss from the aquifer (m s-1 + scalarAquiferRecharge => flux_data%var(iLookFLUX%scalarAquiferRecharge)%dat(1), & ! intent(out): [dp] recharge to the aquifer (m s-1) + scalarAquiferBaseflow => flux_data%var(iLookFLUX%scalarAquiferBaseflow)%dat(1) ) ! intent(out): [dp] total baseflow from the aquifer (m s-1) + ! populate the flux vector for hydrology + ! NOTE: ixVolFracWat and ixVolFracLiq can also include states in the soil domain, hence enable primary variable switching + if (nSnowSoilHyd>0) then ! check if any hydrology states exist + do iLayer=1,nLayers ! loop through non-missing energy state variables in the snow+soil domain + if (ixSnowSoilHyd(iLayer)/=integerMissing) then ! check if a given hydrology state exists + select case(layerType(iLayer)) + case(iname_snow); fluxVec(ixSnowSoilHyd(iLayer)) = mLayerLiqFluxSnow(iLayer) + case(iname_soil); fluxVec(ixSnowSoilHyd(iLayer)) = mLayerLiqFluxSoil(iLayer-nSnow) + case default; err=20; message=trim(message)//'expect layerType to be either iname_snow or iname_soil'; return + end select + end if ! end if a given hydrology state exists + end do + end if ! end if any hydrology states exist + ! compute the flux vector for the aquifer + if (ixAqWat/=integerMissing) fluxVec(ixAqWat) = scalarAquiferTranspire + scalarAquiferRecharge - scalarAquiferBaseflow + end associate + + firstFluxCall=.false. ! set the first flux call to false + end subroutine finalize_computFlux + ! **** vegNrgFlux **** subroutine initialize_vegNrgFlux associate(& From 29330795b56df1889f1452d8897bad0cfeb4353c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 10 Oct 2023 11:59:36 +0900 Subject: [PATCH 0949/1472] utilities relative values --- utils/hist_per_GRU.py | 26 +++++------- utils/plot_per_GRU.py | 92 ++++++++++++++++++++++++++++--------------- utils/scat_per_GRU.py | 19 +++------ 3 files changed, 75 insertions(+), 62 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index ad5efa0a7..cb2981751 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -53,11 +53,10 @@ leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$','$s$'] -#fig_fil = '{}_hrly_diff_hist_{}_{}_zoom_compressed.png' -#fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) fig_fil = 'Hrly_diff_hist_{}_{}_zoom_compressed.png' +if do_rel: fig_fil = 'Hrly_diff_hist_{}_{}_zoom_rel_compressed.png' fig_fil = fig_fil.format(','.join(settings),stat) -# possibly want to use these to shrink the axes a bit + if stat == 'rmse': maxes = [2,15,250,0.08,200,10e-3] #[2,15,8e-6,0.08,6e-9,10e-3] #maxes = [0.25,2,30,0.01,30,2e-3] #[0.25,2,1e-6,0.01,1e-9,2e-3] @@ -93,10 +92,6 @@ else: plt.rcParams.update({'font.size': 100}) -# Flip the evaporation values so that they become positive, not if plotting diffs -#bas_albers['plot_ET'] = bas_albers['scalarTotalET'] * -1 -#bas_albers['plot_ET'] = bas_albers['plot_ET'].where(bas_albers['scalarTotalET'] != -9999, np.nan) - if 'compressed' in fig_fil: fig,axs = plt.subplots(3,2,figsize=(35,33)) else: @@ -128,15 +123,13 @@ def run_loop(i,var,mx): if do_rel and var != 'wallClockTime': s = s/s_rel if stat == 'maxe': s = np.fabs(s) # make absolute value norm mx = max(s.max(),mx) - if stat=='kgem' : mn = min(s.min(),mn) + if stat == 'kgem': mn = min(s.min(),mn) # Data if do_rel: s_rel = summa[method_name[0]][var].sel(stat=statr) for m in method_name: s = summa[m][var].sel(stat=stat0) - if do_rel and var != 'wallClockTime': - s = s/s_rel - word_add = 'rel to bench ' + if do_rel and var != 'wallClockTime': s = s/s_rel if var == 'wallClockTime' and use_eff: batch = np.floor(np.arange(len(s.indexes['hru'])) /nbatch_hrus) @@ -170,7 +163,6 @@ def run_loop(i,var,mx): if stat=='kgem' and var!='wallClockTime' : range = (mn,1) np.fabs(s).plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=m,linewidth=2.0,range=range) - if stat0 == 'rmse': stat_word = 'RMSE' if stat0 == 'rmnz': stat_word = 'RMSE no 0s' if stat0 == 'maxe': stat_word = 'max abs error' @@ -180,18 +172,18 @@ def run_loop(i,var,mx): if stat0 == 'amax': stat_word = 'max ' fig.suptitle('Histograms of Hourly Statistics for each GRU', fontsize=40) - if statr == 'mean_ben': stat0_word = 'mean ' - if statr == 'mnnz_ben': stat0_word = 'mean excluding 0s ' - if statr == 'amax_ben': stat0_word = 'max ' + if statr == 'mean_ben': statr_word = 'mean ' + if statr == 'mnnz_ben': statr_word = 'mean excluding 0s ' + if statr == 'amax_ben': statr_word = 'max ' #if var == 'wallClockTime': axs[r,c].set_yscale('log') #log y axis for wall clock time to exaggerate peaks axs[r,c].legend() - axs[r,c].set_title(plt_titl[i] + stat_word) + axs[r,c].set_title(plt_titl[i]) if stat == 'rmse' or stat == 'rmnz': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl[i])) if stat == 'maxe': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titlm[i])) if stat == 'kgem': axs[r,c].set_xlabel(stat_word) - if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + word_add + stat0_word) + if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + 'rel to bench ' + statr_word) axs[r,c].set_ylabel('GRU count') if var != 'wallClockTime' and not testing: axs[r,c].set_ylim([0, 25000]) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index f00151bac..9f618daca 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -42,6 +42,8 @@ eff_fil = 'eff_' + method_name + '.txt' nbatch_hrus = 518 # number of HRUs per batch use_eff = False # use efficiency in wall clock time +do_rel = True # plot relative to the benchmark simulation +if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE # Specify variables of interest plot_vars = settings @@ -49,15 +51,27 @@ leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$','$s$'] -if stat == 'rmse': maxes = [2,15,250,0.08,200,10e-3] #[2,15,8e-6,0.08,6e-9,10e-3] -# if stat=='rmse': maxes = [0.25,2,30,0.01,30,2e-3] #[0.25,2,1e-6,0.01,1e-9,2e-3] -if stat == 'rmnz': maxes = [2,15,250,0.08,200,10e-3] -if stat == 'maxe': maxes = [15,25,0.8,2,0.3,0.2] #[15,25,25e-5,2,1e-7,0.2] -if stat == 'kgem': maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] -if stat == 'mean': maxes = [80,1500,1500,3000,10e-3] #[80,1500,5e-5,8,1e-7,10e-3] -if stat == 'amax': maxes = [240,1800,3.5,25,7.5,0.2] #[240,1800,1e-3,25,2e-6,0.2] +if stat == 'rmse': + maxes = [2,15,250,0.08,200,10e-3] #[2,15,8e-6,0.08,6e-9,10e-3] + #maxes = [0.25,2,30,0.01,30,2e-3] #[0.25,2,1e-6,0.01,1e-9,2e-3] + if do_rel: maxes = [1,1,1,1,1,10e-3] +if stat == 'rmnz': + maxes = [2,15,250,0.08,200,10e-3] + if do_rel: maxes = [1,1,1,1,1,10e-3] +if stat == 'maxe': + maxes = [15,25,0.8,2,0.3,0.2] #[15,25,25e-5,2,1e-7,0.2] + if do_rel: maxes = [1,1,1,1,1,10e-3] +if stat == 'kgem': + maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] +if stat == 'mean': + maxes = [80,1500,1500,3000,10e-3] #[80,1500,5e-5,8,1e-7,10e-3] + if do_rel: maxes = [1.05,1.05,1.05,1.05,1.05,10e-3] +if stat == 'amax': + maxes = [240,1800,3.5,25,7.5,0.2] #[240,1800,1e-3,25,2e-6,0.2] + if do_rel: maxes = [1.05,1.05,1.05,1.05,1.05,10e-3] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed.png' +if do_rel: fig_fil = method_name + '_hrly_diff_stats_{}_{}_rel_compressed.png' fig_fil = fig_fil.format(','.join(settings),stat) # Get the albers shapes @@ -172,10 +186,20 @@ def make_default_path(suffix): hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile for plot_var in plot_vars: stat0 = stat - if plot_var == 'wallClockTime': - if stat == 'rmse' or stat == 'kgem': stat0 = 'mean' - if stat == 'maxe': stat0 = 'amax' + if stat == 'rmse' or stat == 'kgem' or stat == 'mean': + if plot_var == 'wallClockTime': stat0 = 'mean' + statr = 'mean_ben' + if stat == 'rmnz' or stat == 'mnnz': + if plot_var == 'wallClockTime': stat0 = 'mnnz' + statr = 'mnnz_ben' + if stat == 'maxe' or stat == 'amax': + if plot_var == 'wallClockTime': stat0 = 'amax' + statr = 'amax_ben' + + if do_rel: s_rel = summa[plot_var].sel(stat=statr) s = summa[plot_var].sel(stat=stat0) + if do_rel and plot_var != 'wallClockTime': s = s/s_rel + if plot_var == 'wallClockTime' and use_eff: batch = np.floor(np.arange(len(s.indexes['hru'])) /nbatch_hrus) #basin_num = np.arange(len(s.indexes['hru'])) % nbatch_hrus #not currently using @@ -194,11 +218,11 @@ def make_default_path(suffix): # Multiply the s values by efficiency s = s*eff_batch - if stat == 'maxe' or stat =='mean' or stat =='amax': s = np.fabs(s) # make absolute value norm, max is not not all positive - if plot_var == 'scalarTotalET': + s = np.fabs(s) # make absolute value norm, not all positive + if plot_var == 'scalarTotalET' and not do_rel: if stat =='rmse' or stat =='rmnz' : s = s*31557600 # make annual total if stat =='maxe': s = s*3600 # make hourly max - if plot_var == 'averageRoutedRunoff': + if plot_var == 'averageRoutedRunoff' and not do_rel: if stat =='rmse' or stat =='rmnz' : s = s*31557600*1000 # make annual total if stat =='maxe': s = s*3600*1000 # make hourly max bas_albers[plot_var] = s.sel(hru=hru_ids_shp.values) @@ -239,8 +263,10 @@ def run_loop(i,var,the_max,f_x,f_y): my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap my_cmap.set_bad(color='white') #nan color white vmin,vmax = 0, the_max - if stat =='mean' and var=='scalarTotalSoilWat': vmin,vmax = 700, the_max - if stat =='amax' and var=='scalarTotalSoilWat': vmin,vmax = 1000, the_max + if stat =='mean' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 700, the_max + if stat =='amax' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 1000, the_max + if (stat == 'mean' or stat == 'mnnz' or stat == 'amax') and var!='wallClockTime' and do_rel: vmin,vmax = 0.995, the_max + norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) if stat =='kgem' and var!='wallClockTime': my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno')) # copy the default cmap @@ -253,31 +279,33 @@ def run_loop(i,var,the_max,f_x,f_y): # Data bas_albers.plot(ax=axs[r,c], column=var, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) + if stat0 == 'rmse': stat_word = 'RMSE' + if stat0 == 'rmnz': stat_word = 'RMSE no 0s' + if stat0 == 'maxe': stat_word = 'max abs error' + if stat0 == 'kgem': stat_word = 'KGE"' + if stat0 == 'mean': stat_word = 'abs mean' + if stat0 == 'mnnz': stat_word = 'abs mean no 0s ' + if stat0 == 'amax': stat_word = 'abs max' + + if statr == 'mean_ben': statr_word = 'mean ' + if statr == 'mnnz_ben': statr_word = 'mean excluding 0s ' + if statr == 'amax_ben': statr_word = 'max ' + + axs[r,c].set_title(plt_titl[i]) + axs[r,c].axis('off') + # Custom colorbar cax = fig.add_axes([f_x,f_y,0.02,0.25]) sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) sm._A = [] cbr = fig.colorbar(sm, cax=cax) #, extend='max') #if max extend can't get title right - if stat == 'rmse' or stat == 'rmnz' or stat == 'mean': cbr.ax.set_ylabel('[{}]'.format(leg_titl[i]), labelpad=40, rotation=270) - if stat == 'maxe' or stat == 'amax': cbr.ax.set_ylabel('[{}]'.format(leg_titl[i]), labelpad=40, rotation=270) + if stat == 'rmse' or stat == 'rmnz' or stat == 'mean': cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titl[i]), labelpad=40, rotation=270) + if stat == 'maxe' or stat == 'amax': cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titlm[i]), labelpad=40, rotation=270) + if stat == 'kgem': cbr.ax.set_ylabel(stat_word, labelpad=40, rotation=270) + if do_rel and var!='wallClockTime': cbr.ax.set_ylabel(stat_word + 'rel to bench ' + statr_word, labelpad=40, rotation=270) #cbr.ax.yaxis.set_offset_position('right') - if stat == 'rmse': stat_word = ' RMSE' - if stat == 'rmnz': stat_word = ' RMSE no 0s' - if stat == 'maxe': stat_word = ' max abs error' - if stat == 'kgem': stat_word = ' KGE"' - if stat == 'mean': stat_word = ' abs mean' - if stat == 'amax': stat_word = ' abs max' - - # wall Clock doesn't do difference - if var == 'wallClockTime': - if stat == 'rmse' or stat == 'kgem': stat_word = ' mean' - if stat == 'maxe': stat_word = ' max' - - axs[r,c].set_title(plt_titl[i] + stat_word) - axs[r,c].axis('off') - # lakes if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index 4591624e8..758009c73 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -27,7 +27,7 @@ testing = False if testing: - stat = 'rmnz' + stat = 'maxe' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') method_name=['be1','sundials_1en6'] #maybe make this an argument else: @@ -51,11 +51,10 @@ plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$num$'] leg_titl0 = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] -leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$','$s$'] +leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$','$num$'] -#fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' -#fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) fig_fil = 'Hrly_diff_scat_{}_{}_compressed.png' +if do_rel: fig_fil = 'Hrly_diff_scat_{}_{}_rel_compressed.png' fig_fil = fig_fil.format(','.join(settings),stat) summa = {} @@ -79,10 +78,6 @@ else: plt.rcParams.update({'font.size': 100}) -# Flip the evaporation values so that they become positive, not if plotting diffs -#bas_albers['plot_ET'] = bas_albers['scalarTotalET'] * -1 -#bas_albers['plot_ET'] = bas_albers['plot_ET'].where(bas_albers['scalarTotalET'] != -9999, np.nan) - if 'compressed' in fig_fil: fig,axs = plt.subplots(3,2,figsize=(35,33)) else: @@ -106,9 +101,7 @@ def run_loop(i,var): if do_rel: s_rel = summa[method_name[0]][var].sel(stat=statr) for m in method_name: s = summa[m][var].sel(stat=[stat,stat0]) - if do_rel and var != 'wallClockTime': - s = s/s_rel - word_add = 'rel to bench ' + if do_rel and var != 'wallClockTime': s = s/s_rel if var == 'scalarTotalET' and not do_rel: if stat =='rmse' or stat =='rmnz' : s = s*31557600 # make annual total @@ -158,10 +151,10 @@ def run_loop(i,var): if stat == 'rmse' or stat == 'rmnz': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl[i])) if stat == 'maxe': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titlm[i])) if stat == 'kgem': axs[r,c].set_xlabel(stat_word) - if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + word_add + stat0_word) + if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + 'rel to bench ' + stat0_word) axs[r,c].set_ylabel(stat0_word + '[{}]'.format(leg_titl0[i])) - if do_rel and var!='wallClockTime': axs[r,c].set_ylabel(stat0_word + word_add + stat0_word) + if do_rel and var!='wallClockTime': axs[r,c].set_ylabel(stat0_word + 'rel to bench ' + stat0_word) for i,var in enumerate(plot_vars): From 5aaf59309c96610216a3353531484eef34f92f98 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 11 Oct 2023 12:43:26 +0900 Subject: [PATCH 0950/1472] fixing utils --- utils/hist_per_GRU.py | 22 +++++----- utils/largest_error_attrib.py | 80 +++++++++++++++++++++++------------ utils/plot_per_GRU.py | 24 +++++------ utils/scat_per_GRU.py | 20 ++++----- 4 files changed, 85 insertions(+), 61 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index cb2981751..4dd2e3a71 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -48,7 +48,7 @@ eff_fil[i] = 'eff_' + m + '.txt' # Specify variables of interest -plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] +plot_vars = settings.copy() plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$','$s$'] @@ -60,13 +60,13 @@ if stat == 'rmse': maxes = [2,15,250,0.08,200,10e-3] #[2,15,8e-6,0.08,6e-9,10e-3] #maxes = [0.25,2,30,0.01,30,2e-3] #[0.25,2,1e-6,0.01,1e-9,2e-3] - if do_rel: maxes = [1,1,1,1,1,10e-3] + if do_rel: maxes = [0.6,0.1,0.6,0.6,0.6,10e-3] if stat == 'rmnz': maxes = [2,15,250,0.08,200,10e-3] - if do_rel: maxes = [1,1,1,1,1,10e-3] + if do_rel: maxes = [0.6,0.1,0.6,0.6,0.6,10e-3] if stat == 'maxe': maxes = [15,25,0.8,2,0.3,0.2] #[15,25,25e-5,2,1e-7,0.2] - if do_rel: maxes = [1,1,1,1,1,10e-3] + if do_rel: maxes = [0.6,0.1,0.6,0.6,0.6,0.2] if stat == 'kgem': maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] @@ -167,14 +167,14 @@ def run_loop(i,var,mx): if stat0 == 'rmnz': stat_word = 'RMSE no 0s' if stat0 == 'maxe': stat_word = 'max abs error' if stat0 == 'kgem': stat_word = 'KGE"' - if stat0 == 'mean': stat_word = 'mean ' - if stat0 == 'mnnz': stat_word = 'mean no 0s ' - if stat0 == 'amax': stat_word = 'max ' + if stat0 == 'mean': stat_word = 'mean' + if stat0 == 'mnnz': stat_word = 'mean no 0s' + if stat0 == 'amax': stat_word = 'max' fig.suptitle('Histograms of Hourly Statistics for each GRU', fontsize=40) - if statr == 'mean_ben': statr_word = 'mean ' - if statr == 'mnnz_ben': statr_word = 'mean excluding 0s ' - if statr == 'amax_ben': statr_word = 'max ' + if statr == 'mean_ben': statr_word = 'mean' + if statr == 'mnnz_ben': statr_word = 'mean excluding 0s' + if statr == 'amax_ben': statr_word = 'max' #if var == 'wallClockTime': axs[r,c].set_yscale('log') #log y axis for wall clock time to exaggerate peaks @@ -183,7 +183,7 @@ def run_loop(i,var,mx): if stat == 'rmse' or stat == 'rmnz': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl[i])) if stat == 'maxe': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titlm[i])) if stat == 'kgem': axs[r,c].set_xlabel(stat_word) - if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + 'rel to bench ' + statr_word) + if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + ' rel to bench ' + statr_word) axs[r,c].set_ylabel('GRU count') if var != 'wallClockTime' and not testing: axs[r,c].set_ylim([0, 25000]) diff --git a/utils/largest_error_attrib.py b/utils/largest_error_attrib.py index 7e1e9b257..6ad188d8a 100644 --- a/utils/largest_error_attrib.py +++ b/utils/largest_error_attrib.py @@ -1,8 +1,7 @@ #!/usr/bin/env python -# python largest_error_attrib.py [method_name] +# python largest_error_attrib.py [method_name] [stat] -import sys import numpy as np import xarray as xr from pathlib import Path @@ -10,15 +9,18 @@ top_fold = '/home/avanb/scratch/' attr_fold = '/home/avanb/TestScripts/settings/' nBig = 10 +do_rel = True # plot relative to the benchmark simulation testing = False if testing: - method_name= 'be1' #sys.argv[1] top_fold = '/Users/amedin/Research/USask/test_py/' attr_fold = '/Users/amedin/Research/USask/test_py/settings/' - nBig = 10 + method_name= 'be1' + stat = 'rmse' else: + import sys method_name = sys.argv[1] + stat = sys.argv[2] des_dir = top_fold + 'statistics' des_dir = Path(des_dir) @@ -27,45 +29,67 @@ viz_fil = method_name + '_hrly_diff_stats_{}.nc' viz_fil = viz_fil.format(','.join(settings)) src_file = des_dir / viz_fil - +plot_vars = settings.copy() attr_fil = Path(attr_fold) / 'attributes.nc' # Open the netCDF file with RMSE data summa = xr.open_dataset(src_file) -for setting in settings: - # Get the RMSE variable from the netCDF file - rmse_var = summa[setting].sel(stat='rmse') +for var in plot_vars: + stat0 = stat + if stat == 'rmse' or stat == 'kgem': + if var == 'wallClockTime': stat0 = 'mean' + statr = 'mean_ben' + if stat == 'rmnz': + if var == 'wallClockTime': stat0 = 'mnnz' + statr = 'mnnz_ben' + if stat == 'maxe': + if var == 'wallClockTime': stat0 = 'amax' + statr = 'amax_ben' + + # Get the variable from the netCDF file + s = summa[var].sel(stat=stat0) + if do_rel: + s_rel = summa[var].sel(stat=statr) + if var != 'wallClockTime': s = s/s_rel - # Mask the finite values of the RMSE variable - mask = np.isfinite(rmse_var) - rmse_var = rmse_var[mask] + # Mask the finite values of the variable + mask = np.isfinite(s) + s = s[mask] - # Get the indices of the largest nBig RMSE values - max_rmse_indices = rmse_var.argsort()[-nBig:] + # Get the indices of the largest nBig values + big_indices = s.argsort()[-nBig:] - # Get the largest RMSE values - rmse_of_max_rmse = rmse_var[max_rmse_indices.values] + # Get the largest nBig values + val_big = s[big_indices.values] - # Get the hru coordinate of the largest RMSE values - hru_of_max_rmse = rmse_var[max_rmse_indices.values].hru.values + # Get the hru coordinate of the largest nBig values + hru_big = s[big_indices.values].hru.values - # Print all the values of the the biggest rmse hru - rmse_values = summa.sel(hru=hru_of_max_rmse[nBig-1],stat='rmse') - print(setting) - print(settings[0:5]) - print(rmse_values[settings[0]].values,rmse_values[settings[1]].values,rmse_values[settings[2]].values,rmse_values[settings[3]].values,rmse_values[settings[4]].values) + # Print all the values of the biggest rmse hru + if do_rel: + print(f"\n{var} raw values of largest relative {stat} values:") + else: + print(f"\n{var} raw values of largest {stat} values:") + raw_vals = summa.sel(stat=stat0, hru = hru_big) + # Print all the raw values of the largest nBig values + for var in plot_vars: + print(f"{var}: {[f'{val:.1e}' for val in raw_vals[var].values]}") # Open the netCDF file with local attributes attr = xr.open_dataset(attr_fil) # Mask the HRU variable from the netCDF file - mask = attr['hruId'].isin(hru_of_max_rmse) + mask = attr['hruId'].isin(hru_big) # Get the vegTypeIndex variable from the netCDF file - veg_type_of_max_rmse = attr['vegTypeIndex'][mask] + vegType_big = attr['vegTypeIndex'][mask] + lat_big = attr['latitude'][mask] + lon_big = attr['longitude'][mask] - # Print the HRU, vegTypeIndex, and RMSE values of the largest RMSE values - print("HRU values of the largest RMSE values:", hru_of_max_rmse) - print("vegTypeIndex values of the largest RMSE values:", veg_type_of_max_rmse.values) - print("RMSE values of the largest RMSE values:", rmse_of_max_rmse.values) \ No newline at end of file + # Print the HRU, vegTypeIndex, and values of the largest nBig values + print("HRU values of the largest values:", hru_big) + print("vegTypeIndex:", vegType_big.values) + print("latitude:", np.round(lat_big.values,2)) + print("longitude:", np.round(lon_big.values,2)) + print("Largest error values:", [f"{val:.2e}" for val in val_big.values]) \ No newline at end of file diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 9f618daca..f0cc2d572 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -46,7 +46,7 @@ if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE # Specify variables of interest -plot_vars = settings +plot_vars = settings.copy() plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$','$s$'] @@ -54,21 +54,21 @@ if stat == 'rmse': maxes = [2,15,250,0.08,200,10e-3] #[2,15,8e-6,0.08,6e-9,10e-3] #maxes = [0.25,2,30,0.01,30,2e-3] #[0.25,2,1e-6,0.01,1e-9,2e-3] - if do_rel: maxes = [1,1,1,1,1,10e-3] + if do_rel: maxes = [0.6,0.1,0.6,0.6,0.6,10e-3] if stat == 'rmnz': maxes = [2,15,250,0.08,200,10e-3] - if do_rel: maxes = [1,1,1,1,1,10e-3] + if do_rel: maxes = [0.6,0.1,0.6,0.6,0.6,10e-3] if stat == 'maxe': maxes = [15,25,0.8,2,0.3,0.2] #[15,25,25e-5,2,1e-7,0.2] - if do_rel: maxes = [1,1,1,1,1,10e-3] + if do_rel: maxes = [0.6,0.1,0.6,0.6,0.6,0.2] if stat == 'kgem': maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] if stat == 'mean': maxes = [80,1500,1500,3000,10e-3] #[80,1500,5e-5,8,1e-7,10e-3] - if do_rel: maxes = [1.05,1.05,1.05,1.05,1.05,10e-3] + if do_rel: maxes = [1.1,1.1,1.1,1.1,1.1,10e-3] if stat == 'amax': maxes = [240,1800,3.5,25,7.5,0.2] #[240,1800,1e-3,25,2e-6,0.2] - if do_rel: maxes = [1.05,1.05,1.05,1.05,1.05,10e-3] + if do_rel: maxes = [1.1,1.1,1.1,1.1,1.1,0.2] fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed.png' if do_rel: fig_fil = method_name + '_hrly_diff_stats_{}_{}_rel_compressed.png' @@ -265,7 +265,7 @@ def run_loop(i,var,the_max,f_x,f_y): vmin,vmax = 0, the_max if stat =='mean' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 700, the_max if stat =='amax' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 1000, the_max - if (stat == 'mean' or stat == 'mnnz' or stat == 'amax') and var!='wallClockTime' and do_rel: vmin,vmax = 0.995, the_max + if (stat == 'mean' or stat == 'mnnz' or stat == 'amax') and var!='wallClockTime' and do_rel: vmin,vmax = 0.9, the_max norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) if stat =='kgem' and var!='wallClockTime': @@ -284,12 +284,12 @@ def run_loop(i,var,the_max,f_x,f_y): if stat0 == 'maxe': stat_word = 'max abs error' if stat0 == 'kgem': stat_word = 'KGE"' if stat0 == 'mean': stat_word = 'abs mean' - if stat0 == 'mnnz': stat_word = 'abs mean no 0s ' + if stat0 == 'mnnz': stat_word = 'abs mean no 0s' if stat0 == 'amax': stat_word = 'abs max' - if statr == 'mean_ben': statr_word = 'mean ' - if statr == 'mnnz_ben': statr_word = 'mean excluding 0s ' - if statr == 'amax_ben': statr_word = 'max ' + if statr == 'mean_ben': statr_word = 'mean' + if statr == 'mnnz_ben': statr_word = 'mean excluding 0s' + if statr == 'amax_ben': statr_word = 'max' axs[r,c].set_title(plt_titl[i]) axs[r,c].axis('off') @@ -302,7 +302,7 @@ def run_loop(i,var,the_max,f_x,f_y): if stat == 'rmse' or stat == 'rmnz' or stat == 'mean': cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titl[i]), labelpad=40, rotation=270) if stat == 'maxe' or stat == 'amax': cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titlm[i]), labelpad=40, rotation=270) if stat == 'kgem': cbr.ax.set_ylabel(stat_word, labelpad=40, rotation=270) - if do_rel and var!='wallClockTime': cbr.ax.set_ylabel(stat_word + 'rel to bench ' + statr_word, labelpad=40, rotation=270) + if do_rel and var!='wallClockTime': cbr.ax.set_ylabel(stat_word + ' rel to bench ' + statr_word, labelpad=40, rotation=270) #cbr.ax.yaxis.set_offset_position('right') diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index 758009c73..9f2ec40f5 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -47,7 +47,7 @@ eff_fil[i] = 'eff_' + m + '.txt' # Specify variables of interest -plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] +plot_vars = settings.copy() plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$num$'] leg_titl0 = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] @@ -135,14 +135,14 @@ def run_loop(i,var): stat_word = 'Node number' else: axs[r,c].scatter(x=np.fabs(s.sel(stat=stat).values),y=s.sel(stat=stat0).values,s=1,zorder=0,label=m) - if stat == 'rmse': stat_word = 'RMSE ' - if stat == 'rmnz': stat_word = 'RMSE no 0s ' - if stat == 'maxe': stat_word = 'max abs error ' - if stat == 'kgem': stat_word = 'KGE" ' + if stat == 'rmse': stat_word = 'RMSE' + if stat == 'rmnz': stat_word = 'RMSE no 0s' + if stat == 'maxe': stat_word = 'max abs error' + if stat == 'kgem': stat_word = 'KGE"' - if stat0 == 'mean': stat0_word = 'mean ' - if stat0 == 'mnnz': stat0_word = 'mean no 0s ' - if stat0 == 'amax': stat0_word = 'max ' + if stat0 == 'mean': stat0_word = 'mean' + if stat0 == 'mnnz': stat0_word = 'mean no 0s' + if stat0 == 'amax': stat0_word = 'max' lgnd = axs[r,c].legend() for j, m in enumerate(method_name): @@ -151,10 +151,10 @@ def run_loop(i,var): if stat == 'rmse' or stat == 'rmnz': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl[i])) if stat == 'maxe': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titlm[i])) if stat == 'kgem': axs[r,c].set_xlabel(stat_word) - if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + 'rel to bench ' + stat0_word) + if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + ' rel to bench ' + stat0_word) axs[r,c].set_ylabel(stat0_word + '[{}]'.format(leg_titl0[i])) - if do_rel and var!='wallClockTime': axs[r,c].set_ylabel(stat0_word + 'rel to bench ' + stat0_word) + if do_rel and var!='wallClockTime': axs[r,c].set_ylabel(stat0_word + ' rel to bench ' + stat0_word) for i,var in enumerate(plot_vars): From f5eb912051fdafed3861cd24a7892bb877263869 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 11 Oct 2023 04:00:30 -0600 Subject: [PATCH 0951/1472] Added new subroutines to the contains block of computFlux to improve modularity and reduce the main body associate statement. --- build/source/engine/computFlux.f90 | 180 ++++++++++++++++------------- 1 file changed, 97 insertions(+), 83 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index f480987e7..07104b211 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -30,13 +30,13 @@ module computFlux_module var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (rkind) model_options, & ! defines the model decisions - in_type_vegNrgFlux,out_type_vegNrgFlux, & ! arguments for vegNrgFlux call - in_type_ssdNrgFlux,io_type_ssdNrgFlux,out_type_ssdNrgFlux,& ! arguments for ssdNrgFlux call - in_type_vegLiqFlux,out_type_vegLiqFlux, & ! arguments for vegLiqFlux call - in_type_snowLiqFlx,io_type_snowLiqFlx,out_type_snowLiqFlx,& ! arguments for snowLiqFlx call - in_type_soilLiqFlx,io_type_soilLiqFlx,out_type_soilLiqFlx,& ! arguments for soilLiqFlx call - in_type_groundwatr,io_type_groundwatr,out_type_groundwatr,& ! arguments for groundwatr call - in_type_bigAquifer,io_type_bigAquifer,out_type_bigAquifer ! arguments for bigAquifer call + in_type_vegNrgFlux,out_type_vegNrgFlux, & ! classes for vegNrgFlux call + in_type_ssdNrgFlux,io_type_ssdNrgFlux,out_type_ssdNrgFlux,& ! classes for ssdNrgFlux call + in_type_vegLiqFlux,out_type_vegLiqFlux, & ! classes for vegLiqFlux call + in_type_snowLiqFlx,io_type_snowLiqFlx,out_type_snowLiqFlx,& ! classes for snowLiqFlx call + in_type_soilLiqFlx,io_type_soilLiqFlx,out_type_soilLiqFlx,& ! classes for soilLiqFlx call + in_type_groundwatr,io_type_groundwatr,out_type_groundwatr,& ! classes for groundwatr call + in_type_bigAquifer,io_type_bigAquifer,out_type_bigAquifer ! classes for bigAquifer call ! indices that define elements of the data structures USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure @@ -235,50 +235,16 @@ subroutine computFlux(& ! *** PRELIMINARIES *** ! get the necessary variables for the flux computations associate(& - ! model decisions - ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization ! indices of model state variables for the vegetation subdomain - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1), & ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1), & ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1), & ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1), & ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1), & ! intent(in): [i4b] index of water storage in the aquifer ! number of state variables of a specific type - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain - ! derivatives - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - ! fluxes for the snow+soil domain - iLayerLiqFluxSnow => flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat ,& ! intent(out): [dp(0:)] vertical liquid water flux at snow layer interfaces (-) - mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(out): [dp(:)] baseflow from each soil layer (m s-1) - scalarSoilDrainage => flux_data%var(iLookFLUX%scalarSoilDrainage)%dat(1) ,& ! intent(out): [dp] drainage from the soil profile (m s-1) - scalarSoilBaseflow => flux_data%var(iLookFLUX%scalarSoilBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the soil profile (m s-1) - ! infiltration - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(inout): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(inout): [dp(:)] fraction of liquid water in each snow layer (-) - ! boundary fluxes in the soil domain - scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(out): [dp] rain that reaches the ground without ever touching the canopy (kg m-2 s-1) - scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(out): [dp] drainage of liquid water from the vegetation canopy (kg m-2 s-1) - scalarRainPlusMelt => flux_data%var(iLookFLUX%scalarRainPlusMelt)%dat(1) ,& ! intent(out): [dp] rain plus melt (m s-1) - scalarSurfaceRunoff => flux_data%var(iLookFLUX%scalarSurfaceRunoff)%dat(1) ,& ! intent(out): [dp] surface runoff (m s-1) - scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1) ,& ! intent(out): [dp] exfiltration from the soil profile (m s-1) - mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat ,& ! intent(out): [dp(:)] column outflow from each soil layer (m3 s-1) - ! fluxes for the aquifer - scalarAquiferTranspire => flux_data%var(iLookFLUX%scalarAquiferTranspire)%dat(1) ,& ! intent(out): [dp] transpiration loss from the aquifer (m s-1 - scalarAquiferRecharge => flux_data%var(iLookFLUX%scalarAquiferRecharge)%dat(1) ,& ! intent(out): [dp] recharge to the aquifer (m s-1) - scalarAquiferBaseflow => flux_data%var(iLookFLUX%scalarAquiferBaseflow)%dat(1) ,& ! intent(out): [dp] total baseflow from the aquifer (m s-1) - ! total runoff - scalarTotalRunoff => flux_data%var(iLookFLUX%scalarTotalRunoff)%dat(1) ,& ! intent(out): [dp] total runoff (m s-1) - ! derivatives in canopy water w.r.t canopy temperature - dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(out): [dp] derivative of canopy liquid storage w.r.t. temperature - ! derivatives in canopy liquid fluxes w.r.t. canopy water - scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ,& ! intent(out): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water - ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above - iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(out): [dp(:)] derivative in vertical liquid water flux at layer interfaces - ! derivative in baseflow flux w.r.t. aquifer storage - dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) & ! intent(out): [dp(:)] derivative in baseflow flux w.r.t. aquifer storage (s-1) - ) ! end association to data in structures + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg)%dat(1), & ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd)%dat(1), & ! intent(in): [i4b] number of hydrology variables in the snow domain + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd)%dat(1) ) ! intent(in): [i4b] number of hydrology variables in the soil domain call initialize_computFlux @@ -311,26 +277,7 @@ subroutine computFlux(& call snowLiqFlx(in_snowLiqFlx,indx_data,mpar_data,prog_data,diag_data,io_snowLiqFlx,out_snowLiqFlx) call finalize_snowLiqFlx else - ! define forcing for the soil domain for the case of no snow layers - ! NOTE: in case where nSnowOnlyHyd==0 AND snow layers exist, then scalarRainPlusMelt is taken from the previous flux evaluation - if (nSnow==0) then !no snow layers - scalarRainPlusMelt = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water & ! liquid flux from the canopy (m s-1) - + drainageMeltPond/iden_water ! melt of the snow without a layer (m s-1) - if (ixVegHyd/=integerMissing) then - ! save canopy derivatives - above_soilLiqFluxDeriv = scalarCanopyLiqDeriv/iden_water ! derivative in (throughfall + drainage) w.r.t. canopy liquid water - above_soildLiq_dTk = dCanLiq_dTcanopy ! derivative of canopy liquid storage w.r.t. temperature - above_soilFracLiq = scalarFracLiqVeg ! fraction of liquid water in canopy (-) - else - above_soilLiqFluxDeriv = 0._rkind - above_soildLiq_dTk = 0._rkind - above_soilFracLiq = 0._rkind - end if - else ! snow layers, take from previous flux calculation - above_soilLiqFluxDeriv = iLayerLiqFluxSnowDeriv(nSnow) ! derivative in vertical liquid water flux at bottom snow layer interface - above_soildLiq_dTk = mLayerdTheta_dTk(nSnow) ! derivative in volumetric liquid water content in bottom snow layer w.r.t. temperature - above_soilFracLiq = mLayerFracLiqSnow(nSnow) ! fraction of liquid water in bottom snow layer (-) - end if ! snow layers or not + call soilForcingNoSnow ! define forcing for the soil domain for the case of no snow layers end if ! if calculating the liquid flux through snow ! *** CALCULATE THE LIQUID FLUX THROUGH SOIL *** @@ -343,21 +290,13 @@ subroutine computFlux(& ! *** CALCULATE THE GROUNDWATER FLOW *** if (nSoilOnlyHyd>0) then ! check if computing soil hydrology if (local_ixGroundwater/=qbaseTopmodel) then ! set baseflow fluxes to zero if the topmodel baseflow routine is not used - ! diagnostic variables in the data structures - scalarExfiltration = 0._rkind ! exfiltration from the soil profile (m s-1) - mLayerColumnOutflow(:) = 0._rkind ! column outflow from each soil layer (m3 s-1) - ! variables needed for the numerical solution - mLayerBaseflow(:) = 0._rkind ! baseflow from each soil layer (m s-1) + call zeroBaseflowFluxes else ! compute the baseflow flux for topmodel-ish shallow groundwater call initialize_groundwatr call groundwatr(in_groundwatr,attr_data,mpar_data,prog_data,diag_data,flux_data,io_groundwatr,out_groundwatr) call finalize_groundwatr end if ! computing baseflow flux - scalarSoilBaseflow = sum(mLayerBaseflow) ! compute total baseflow from the soil zone (needed for mass balance checks) - ! compute total runoff - ! (Note: scalarSoilBaseflow is zero if topmodel is not used) - ! (Note: scalarSoilBaseflow may need to re-envisioned in topmodel formulation if parts of it flow into neighboring soil rather than exfiltrate) - scalarTotalRunoff = scalarSurfaceRunoff + scalarSoilDrainage + scalarSoilBaseflow + call computeBaseflowRunoff ! compute total baseflow from soil and runoff end if ! end if computing soil hydrology ! *** CALCULATE FLUXES FOR THE DEEP AQUIFER *** @@ -367,10 +306,7 @@ subroutine computFlux(& call bigAquifer(in_bigAquifer,mpar_data,diag_data,io_bigAquifer,out_bigAquifer) call finalize_bigAquifer else ! if no aquifer, then fluxes are zero - scalarAquiferTranspire = 0._rkind ! transpiration loss from the aquifer (m s-1) - scalarAquiferRecharge = 0._rkind ! recharge to the aquifer (m s-1) - scalarAquiferBaseflow = 0._rkind ! total baseflow from the aquifer (m s-1) - dBaseflow_dAquifer = 0._rkind ! change in baseflow flux w.r.t. aquifer storage (s-1) + call zeroAquiferFluxes end if ! end check aquifer model decision end if ! if computing aquifer fluxes @@ -380,6 +316,84 @@ subroutine computFlux(& contains + subroutine soilForcingNoSnow + ! define forcing for the soil domain for the case of no snow layers + ! NOTE: in case where nSnowOnlyHyd==0 AND snow layers exist, then scalarRainPlusMelt is taken from the previous flux evaluation + associate(& + scalarRainPlusMelt => flux_data%var(iLookFLUX%scalarRainPlusMelt)%dat(1), & ! intent(out): [dp] rain plus melt (m s-1) + scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1), & ! intent(out): [dp] rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1), & ! intent(out): [dp] drainage of liquid water from the vegetation canopy (kg m-2 s-1) + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1), & ! intent(in): [i4b] index of canopy hydrology state variable (mass) + scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv)%dat(1), & ! intent(out): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water + dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy)%dat(1), & ! intent(out): [dp] derivative of canopy liquid storage w.r.t. temperature + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1), & ! intent(inout): [dp] fraction of liquid water on vegetation (-) + iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv)%dat, & ! intent(out): [dp(:)] derivative in vertical liquid water flux at layer interfaces + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat, & ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ) ! intent(inout): [dp(:)] fraction of liquid water in each snow layer (-) + if (nSnow==0) then !no snow layers + scalarRainPlusMelt = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water & ! liquid flux from the canopy (m s-1) + + drainageMeltPond/iden_water ! melt of the snow without a layer (m s-1) + if (ixVegHyd/=integerMissing) then + ! save canopy derivatives + above_soilLiqFluxDeriv = scalarCanopyLiqDeriv/iden_water ! derivative in (throughfall + drainage) w.r.t. canopy liquid water + above_soildLiq_dTk = dCanLiq_dTcanopy ! derivative of canopy liquid storage w.r.t. temperature + above_soilFracLiq = scalarFracLiqVeg ! fraction of liquid water in canopy (-) + else + above_soilLiqFluxDeriv = 0._rkind + above_soildLiq_dTk = 0._rkind + above_soilFracLiq = 0._rkind + end if + else ! snow layers, take from previous flux calculation + above_soilLiqFluxDeriv = iLayerLiqFluxSnowDeriv(nSnow) ! derivative in vertical liquid water flux at bottom snow layer interface + above_soildLiq_dTk = mLayerdTheta_dTk(nSnow) ! derivative in volumetric liquid water content in bottom snow layer w.r.t. temperature + above_soilFracLiq = mLayerFracLiqSnow(nSnow) ! fraction of liquid water in bottom snow layer (-) + end if ! snow layers or not + end associate + end subroutine soilForcingNoSnow + + subroutine zeroBaseflowFluxes + ! set baseflow fluxes to zero if the topmodel baseflow routine is not used + associate(& + scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1), & ! intent(out): [dp] exfiltration from the soil profile (m s-1) + mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat, & ! intent(out): [dp(:)] column outflow from each soil layer (m3 s-1) + mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ) ! intent(out): [dp(:)] baseflow from each soil layer (m s-1) + ! diagnostic variables in the data structures + scalarExfiltration = 0._rkind ! exfiltration from the soil profile (m s-1) + mLayerColumnOutflow(:) = 0._rkind ! column outflow from each soil layer (m3 s-1) + ! variables needed for the numerical solution + mLayerBaseflow(:) = 0._rkind ! baseflow from each soil layer (m s-1) + end associate + end subroutine zeroBaseflowFluxes + + subroutine computeBaseflowRunoff + ! compute total baseflow from the soil zone (needed for mass balance checks) and total runoff + ! (Note: scalarSoilBaseflow is zero if topmodel is not used) + ! (Note: scalarSoilBaseflow may need to re-envisioned in topmodel formulation if parts of it flow into neighboring soil rather than exfiltrate) + associate(& + scalarSoilBaseflow => flux_data%var(iLookFLUX%scalarSoilBaseflow)%dat(1), & ! intent(out): [dp] total baseflow from the soil profile (m s-1) + mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat, & ! intent(out): [dp(:)] baseflow from each soil layer (m s-1) + scalarTotalRunoff => flux_data%var(iLookFLUX%scalarTotalRunoff)%dat(1), & ! intent(out): [dp] total runoff (m s-1) + scalarSurfaceRunoff => flux_data%var(iLookFLUX%scalarSurfaceRunoff)%dat(1), & ! intent(out): [dp] surface runoff (m s-1) + scalarSoilDrainage => flux_data%var(iLookFLUX%scalarSoilDrainage)%dat(1) ) ! intent(out): [dp] drainage from the soil profile (m s-1) + scalarSoilBaseflow = sum(mLayerBaseflow) ! baseflow from the soil zone + scalarTotalRunoff = scalarSurfaceRunoff + scalarSoilDrainage + scalarSoilBaseflow ! total runoff + end associate + end subroutine computeBaseflowRunoff + + subroutine zeroAquiferFluxes + ! set aquifer fluxes to zero (if no aquifer exists) + associate(& + scalarAquiferTranspire => flux_data%var(iLookFLUX%scalarAquiferTranspire)%dat(1), & ! intent(out): [dp] transpiration loss from the aquifer (m s-1 + scalarAquiferRecharge => flux_data%var(iLookFLUX%scalarAquiferRecharge)%dat(1), & ! intent(out): [dp] recharge to the aquifer (m s-1) + scalarAquiferBaseflow => flux_data%var(iLookFLUX%scalarAquiferBaseflow)%dat(1), & ! intent(out): [dp] total baseflow from the aquifer (m s-1) + dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer)%dat(1) ) ! intent(out): [dp(:)] derivative in baseflow flux w.r.t. aquifer storage (s-1) + scalarAquiferTranspire = 0._rkind ! transpiration loss from the aquifer (m s-1) + scalarAquiferRecharge = 0._rkind ! recharge to the aquifer (m s-1) + scalarAquiferBaseflow = 0._rkind ! total baseflow from the aquifer (m s-1) + dBaseflow_dAquifer = 0._rkind ! change in baseflow flux w.r.t. aquifer storage (s-1) + end associate + end subroutine zeroAquiferFluxes + subroutine initialize_computFlux associate(& numFluxCalls => diag_data%var(iLookDIAG%numFluxCalls)%dat(1), & ! intent(out): [dp] number of flux calls (-) From ab0f327e269b2e05e073d62cae529835cd18f2c9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 12 Oct 2023 10:59:03 +0900 Subject: [PATCH 0952/1472] fixing utils, now dropping first day for stats --- utils/largest_error_attrib.py | 67 ++++++++++++++++++++----------- utils/timeseries_to_statistics.py | 6 ++- 2 files changed, 47 insertions(+), 26 deletions(-) diff --git a/utils/largest_error_attrib.py b/utils/largest_error_attrib.py index 6ad188d8a..37ad450ad 100644 --- a/utils/largest_error_attrib.py +++ b/utils/largest_error_attrib.py @@ -16,7 +16,7 @@ top_fold = '/Users/amedin/Research/USask/test_py/' attr_fold = '/Users/amedin/Research/USask/test_py/settings/' method_name= 'be1' - stat = 'rmse' + stat = 'rmnz' else: import sys method_name = sys.argv[1] @@ -30,24 +30,28 @@ viz_fil = viz_fil.format(','.join(settings)) src_file = des_dir / viz_fil plot_vars = settings.copy() +short_name= ['SWE ', + 'soilWat ', + 'ET ', + 'canWat ', + 'runoff '] + attr_fil = Path(attr_fold) / 'attributes.nc' # Open the netCDF file with RMSE data summa = xr.open_dataset(src_file) +if stat == 'rmse' or stat == 'kgem': statr = 'mean_ben' +if stat == 'rmnz': statr = 'mnnz_ben' +if stat == 'maxe': statr = 'amax_ben' for var in plot_vars: - stat0 = stat - if stat == 'rmse' or stat == 'kgem': - if var == 'wallClockTime': stat0 = 'mean' - statr = 'mean_ben' - if stat == 'rmnz': - if var == 'wallClockTime': stat0 = 'mnnz' - statr = 'mnnz_ben' - if stat == 'maxe': - if var == 'wallClockTime': stat0 = 'amax' - statr = 'amax_ben' # Get the variable from the netCDF file + stat0 = stat + if var == 'wallClockTime': + if stat == 'rmse' or stat == 'kgem': stat0 = 'mean' + if stat == 'rmnz': stat0 = 'mnnz' + if stat == 'maxe': stat0 = 'amax' s = summa[var].sel(stat=stat0) if do_rel: s_rel = summa[var].sel(stat=statr) @@ -58,23 +62,32 @@ s = s[mask] # Get the indices of the largest nBig values - big_indices = s.argsort()[-nBig:] + big_indices = abs(s).argsort()[-nBig:] # Get the largest nBig values val_big = s[big_indices.values] # Get the hru coordinate of the largest nBig values hru_big = s[big_indices.values].hru.values + + # Get the largest nBig bench values + if do_rel: ben_big = s_rel.sel(hru=hru_big) # Print all the values of the biggest rmse hru if do_rel: - print(f"\n{var} raw values of largest relative {stat} values:") + print(f"\n{var} raw error values of largest relative {stat} values:") else: - print(f"\n{var} raw values of largest {stat} values:") - raw_vals = summa.sel(stat=stat0, hru = hru_big) + print(f"\n{var} raw error values of largest {stat} values:") # Print all the raw values of the largest nBig values - for var in plot_vars: - print(f"{var}: {[f'{val:.1e}' for val in raw_vals[var].values]}") + raw_vals = summa.sel(stat=stat0, hru=hru_big) + for i,var0 in enumerate(plot_vars[:-1]): + print(f"{short_name[i]}: [{' '.join(f'{val:8.1e}' for val in raw_vals[var0].values)}]") + var0 = 'wallClockTime' + if stat == 'rmse' or stat == 'kgem': stat00 = 'mean' + if stat == 'rmnz': stat00 = 'mnnz' + if stat == 'maxe': stat00 = 'amax' + raw_vals = summa.sel(stat=stat00, hru=hru_big) + print("wall"f"{stat00}: [{' '.join(f'{val:8.1e}' for val in raw_vals[var0].values)}]") # Open the netCDF file with local attributes attr = xr.open_dataset(attr_fil) @@ -82,14 +95,20 @@ # Mask the HRU variable from the netCDF file mask = attr['hruId'].isin(hru_big) - # Get the vegTypeIndex variable from the netCDF file + # Get the vegTypeIndex, lat, lon variables from the netCDF file vegType_big = attr['vegTypeIndex'][mask] lat_big = attr['latitude'][mask] lon_big = attr['longitude'][mask] - # Print the HRU, vegTypeIndex, and values of the largest nBig values - print("HRU values of the largest values:", hru_big) - print("vegTypeIndex:", vegType_big.values) - print("latitude:", np.round(lat_big.values,2)) - print("longitude:", np.round(lon_big.values,2)) - print("Largest error values:", [f"{val:.2e}" for val in val_big.values]) \ No newline at end of file + # Print the attributes of the largest nBig values + print("HRU vals: [", " ".join([f"{val:8d}" for val in hru_big]), "]", sep="") + print("vegType : [", " ".join([f"{val:8d}" for val in vegType_big.values]), "]", sep="") + print("latitude: [", " ".join([f"{val:8.2f}" for val in lat_big.values]), "]", sep="") + print("longitud: [", " ".join([f"{val:8.2f}" for val in lon_big.values]), "]", sep="") + + # Print the values of the largest nBig values, bench will be the mean, mnnz, or amax and err will be the rmse, rmnz, or maxe + if do_rel: + #print(summa[var].sel(stat=stat0, hru=hru_big)) + print("Ben vals: [", " ".join([f"{val:8.1e}" for val in ben_big.values]), "]", sep="") + print("Err vals: [", " ".join([f"{val:8.1e}" for val in val_big.values]), "]", sep="") + \ No newline at end of file diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 96bbf56d7..349228ebe 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -131,14 +131,16 @@ def run_loop(file,bench,processed_files_path): m = m.rename({'gru': 'hru'}) dat = dat.drop_dims('gru') dat = xr.merge([dat,m]) - dat = dat.where(dat.time!=dat.time[0],drop=True) #first timestep weird + #dat = dat.where(dat.time!=dat.time[0],drop=True) #drop first timestep, weird + dat = dat.isel(time=slice(24, None)) #drop first day, weird ben = ben.drop_vars(['hruId','gruId']) m = ben.drop_dims('hru') m = m.rename({'gru': 'hru'}) ben = ben.drop_dims('gru') ben = xr.merge([ben,m]) - ben = ben.where(ben.time!=ben.time[0],drop=True) #first timestep weird + #ben = ben.where(ben.time!=ben.time[0],drop=True) drop first timestep, weird + ben = ben.isel(time=slice(24, None)) #drop first day, weird diff = dat - ben the_hru = np.array(ben['hru']) From 3b59cfebe6d0f9d5dd3563bd9a32667a0e13d3eb Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 12 Oct 2023 05:45:23 -0600 Subject: [PATCH 0953/1472] Updated computFlux to use short associate blocks and improved organization of computFlux's contains block. --- build/source/engine/computFlux.f90 | 80 ++++++++++++++++-------------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 07104b211..d0e7471d2 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -162,9 +162,9 @@ subroutine computFlux(& USE groundwatr_module,only:groundwatr ! compute the baseflow flux USE bigAquifer_module,only:bigAquifer ! compute fluxes for the big aquifer implicit none - ! --------------------------------------------------------------------------------------- + ! ------------------------------------------------------------------------------------------------------------------------- ! * dummy variables - ! --------------------------------------------------------------------------------------- + ! ------------------------------------------------------------------------------------------------------------------------- ! input-output: control integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers @@ -208,9 +208,9 @@ subroutine computFlux(& ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message - ! --------------------------------------------------------------------------------------- + ! ------------------------------------------------------------------------------------------------------------------------- ! * local variables - ! --------------------------------------------------------------------------------------- + ! ------------------------------------------------------------------------------------------------------------------------- integer(i4b) :: local_ixGroundwater ! local index for groundwater representation integer(i4b) :: iLayer ! index of model layers logical(lgt) :: doVegNrgFlux ! flag to compute the energy flux over vegetation @@ -228,66 +228,66 @@ subroutine computFlux(& type(in_type_soilLiqFlx) :: in_soilLiqFlx; type(io_type_soilLiqFlx) :: io_soilLiqFlx; type(out_type_soilLiqFlx) :: out_soilLiqFlx ! soilLiqFlx arguments type(in_type_groundwatr) :: in_groundwatr; type(io_type_groundwatr) :: io_groundwatr; type(out_type_groundwatr) :: out_groundwatr ! groundwatr arguments type(in_type_bigAquifer) :: in_bigAquifer; type(io_type_bigAquifer) :: io_bigAquifer; type(out_type_bigAquifer) :: out_bigAquifer ! bigAquifer arguments - ! -------------------------------------------------------------- + ! ------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='computFlux/' - ! *** PRELIMINARIES *** - ! get the necessary variables for the flux computations + call initialize_computFlux ! Preliminary operations to start routine + + ! *** CALCULATE ENERGY FLUXES OVER VEGETATION *** associate(& - ! indices of model state variables for the vegetation subdomain - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1), & ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1), & ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1), & ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1), & ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1), & ! intent(in): [i4b] index of water storage in the aquifer - ! number of state variables of a specific type - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg)%dat(1), & ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd)%dat(1), & ! intent(in): [i4b] number of hydrology variables in the snow domain - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd)%dat(1) ) ! intent(in): [i4b] number of hydrology variables in the soil domain - - call initialize_computFlux - - ! *** CALCULATE ENERGY FLUXES OVER VEGETATION *** + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1), & ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1), & ! intent(in): [i4b] index of canopy energy state variable + ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ) ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain ! identify the need to calculate the energy flux over vegetation doVegNrgFlux = (ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing .or. ixTopNrg/=integerMissing) if (doVegNrgFlux) then ! if necessary, calculate the energy fluxes over vegetation call initialize_vegNrgFlux call vegNrgFlux(in_vegNrgFlux,type_data,forc_data,mpar_data,indx_data,prog_data,diag_data,flux_data,bvar_data,model_decisions,out_vegNrgFlux) call finalize_vegNrgFlux - end if ! end if calculating the energy fluxes over vegetation + end if + end associate - ! *** CALCULATE ENERGY FLUXES THROUGH THE SNOW-SOIL DOMAIN *** + ! *** CALCULATE ENERGY FLUXES THROUGH THE SNOW-SOIL DOMAIN *** + associate(nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg)%dat(1)) ! intent(in): [i4b] number of energy state variables in the snow+soil domain if (nSnowSoilNrg>0) then ! if necessary, calculate energy fluxes at layer interfaces through the snow and soil domain call initialize_ssdNrgFlux call ssdNrgFlux(in_ssdNrgFlux,mpar_data,indx_data,prog_data,diag_data,flux_data,io_ssdNrgFlux,out_ssdNrgFlux) call finalize_ssdNrgFlux - end if ! end if computing energy fluxes throughout the snow+soil domain + end if + end associate - ! *** CALCULATE THE LIQUID FLUX THROUGH VEGETATION *** + ! *** CALCULATE THE LIQUID FLUX THROUGH VEGETATION *** + associate(ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1)) ! intent(in): [i4b] index of canopy hydrology state variable (mass) if (ixVegHyd/=integerMissing) then ! if necessary, calculate liquid water fluxes through vegetation call initialize_vegLiqFlux call vegLiqFlux(in_vegLiqFlux,mpar_data,diag_data,out_vegLiqFlux) call finalize_vegLiqFlux - end if ! end if computing the liquid water fluxes through vegetation + end if + end associate - ! *** CALCULATE THE LIQUID FLUX THROUGH SNOW *** + ! *** CALCULATE THE LIQUID FLUX THROUGH SNOW *** + associate(nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd)%dat(1)) ! intent(in): [i4b] number of hydrology variables in the snow domain if (nSnowOnlyHyd>0) then ! if necessary, compute liquid fluxes through snow call initialize_snowLiqFlx call snowLiqFlx(in_snowLiqFlx,indx_data,mpar_data,prog_data,diag_data,io_snowLiqFlx,out_snowLiqFlx) call finalize_snowLiqFlx else call soilForcingNoSnow ! define forcing for the soil domain for the case of no snow layers - end if ! if calculating the liquid flux through snow + end if + end associate - ! *** CALCULATE THE LIQUID FLUX THROUGH SOIL *** + ! *** CALCULATE THE LIQUID FLUX THROUGH SOIL *** + associate(nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd)%dat(1)) ! intent(in): [i4b] number of hydrology variables in the soil domain if (nSoilOnlyHyd>0) then ! if necessary, calculate the liquid flux through soil call initialize_soilLiqFlx call soilLiqFlx(in_soilLiqFlx,mpar_data,indx_data,prog_data,diag_data,flux_data,io_soilLiqFlx,out_soilLiqFlx) call finalize_soilLiqFlx - end if ! end if calculating the liquid flux through soil + end if + end associate - ! *** CALCULATE THE GROUNDWATER FLOW *** + ! *** CALCULATE THE GROUNDWATER FLOW *** + associate(nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd)%dat(1)) ! intent(in): [i4b] number of hydrology variables in the soil domain if (nSoilOnlyHyd>0) then ! check if computing soil hydrology if (local_ixGroundwater/=qbaseTopmodel) then ! set baseflow fluxes to zero if the topmodel baseflow routine is not used call zeroBaseflowFluxes @@ -295,11 +295,13 @@ subroutine computFlux(& call initialize_groundwatr call groundwatr(in_groundwatr,attr_data,mpar_data,prog_data,diag_data,flux_data,io_groundwatr,out_groundwatr) call finalize_groundwatr - end if ! computing baseflow flux + end if call computeBaseflowRunoff ! compute total baseflow from soil and runoff - end if ! end if computing soil hydrology + end if + end associate - ! *** CALCULATE FLUXES FOR THE DEEP AQUIFER *** + ! *** CALCULATE FLUXES FOR THE DEEP AQUIFER *** + associate(ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1)) ! intent(in): [i4b] index of water storage in the aquifer if (ixAqWat/=integerMissing) then ! check if computing aquifer fluxes if (local_ixGroundwater==bigBucket) then ! compute fluxes for the big bucket call initialize_bigAquifer @@ -309,13 +311,13 @@ subroutine computFlux(& call zeroAquiferFluxes end if ! end check aquifer model decision end if ! if computing aquifer fluxes + end associate - call finalize_computFlux - - end associate ! end association to variables in the data structures + call finalize_computFlux ! final operations to prep for end of routine contains + ! **** Subroutines that handle the absence of model features **** subroutine soilForcingNoSnow ! define forcing for the soil domain for the case of no snow layers ! NOTE: in case where nSnowOnlyHyd==0 AND snow layers exist, then scalarRainPlusMelt is taken from the previous flux evaluation @@ -394,7 +396,9 @@ subroutine zeroAquiferFluxes end associate end subroutine zeroAquiferFluxes + ! **** Subroutines for starting/ending operations of computFlux **** subroutine initialize_computFlux + ! operations to prep for the start of computFlux associate(& numFluxCalls => diag_data%var(iLookDIAG%numFluxCalls)%dat(1), & ! intent(out): [dp] number of flux calls (-) ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision, & ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) @@ -421,6 +425,7 @@ subroutine initialize_computFlux end subroutine initialize_computFlux subroutine finalize_computFlux + ! operations to prep for the end of computFlux associate(& ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1), & ! intent(in): [i4b] index of canopy air space energy state variable ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1), & ! intent(in): [i4b] index of canopy energy state variable @@ -474,6 +479,7 @@ subroutine finalize_computFlux firstFluxCall=.false. ! set the first flux call to false end subroutine finalize_computFlux + ! ----------------------- Initialize and Finalize procedures for the flux routines ----------------------- ! **** vegNrgFlux **** subroutine initialize_vegNrgFlux associate(& From f65ef81cefe8f8734bbe711fd218bf6173aaffc4 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 12 Oct 2023 05:46:30 -0600 Subject: [PATCH 0954/1472] Updated whats-new.md to reflect flux refactoring features. --- docs/whats-new.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/whats-new.md b/docs/whats-new.md index d598308be..3221d4445 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -3,7 +3,11 @@ This page provides simple, high-level documentation about what has changed in ea ## Pre-release ### Major changes -- +- General cleanup and shortening of computFlux.f90, vegNrgFlux.f90, ssdNrgFlux.f90, vegLiqFlux.f90, snowLiqFlx.f90, soilLiqFlx.f90, groundwatr.f90, and bigAquifer.f90 +- Added object-oriented methods to simplify flux routine calls in computFlux and improve modularity + - classes for each flux routine were added to data_types.f90 + - large associate statemements are no longer needed in computFlux (associate blocks are now much shorter) + - the length of computFlux has been decreased substantially ### Minor changes - From 269a8e49bb91f12f82dbc43fcd8093bd96f9245d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 13 Oct 2023 22:18:34 +0900 Subject: [PATCH 0955/1472] utilities, only drop and hour in stats --- utils/timeseries_to_statistics.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 349228ebe..96bbf56d7 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -131,16 +131,14 @@ def run_loop(file,bench,processed_files_path): m = m.rename({'gru': 'hru'}) dat = dat.drop_dims('gru') dat = xr.merge([dat,m]) - #dat = dat.where(dat.time!=dat.time[0],drop=True) #drop first timestep, weird - dat = dat.isel(time=slice(24, None)) #drop first day, weird + dat = dat.where(dat.time!=dat.time[0],drop=True) #first timestep weird ben = ben.drop_vars(['hruId','gruId']) m = ben.drop_dims('hru') m = m.rename({'gru': 'hru'}) ben = ben.drop_dims('gru') ben = xr.merge([ben,m]) - #ben = ben.where(ben.time!=ben.time[0],drop=True) drop first timestep, weird - ben = ben.isel(time=slice(24, None)) #drop first day, weird + ben = ben.where(ben.time!=ben.time[0],drop=True) #first timestep weird diff = dat - ben the_hru = np.array(ben['hru']) From 3637f3a068fbd384933af3c104fb613773ab4e5b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 13 Oct 2023 22:19:37 +0900 Subject: [PATCH 0956/1472] First flux call fluxes need be added to the mask in the first flux call --- build/source/engine/opSplittin.f90 | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index fbcab0541..2b54f0a55 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -239,7 +239,7 @@ subroutine opSplittin(& type(var_dlength) :: diag_temp ! temporary model diagnostic variables type(var_dlength) :: flux_temp ! temporary model fluxes type(var_dlength) :: flux_mean ! mean model fluxes - type(var_dlength) :: flux_mntemp ! temporary mean model fluxes + type(var_dlength) :: flux_mntemp ! temporary mean model fluxes type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! ------------------------------------------------------------------------------------------------------ ! * operator splitting @@ -535,6 +535,16 @@ subroutine opSplittin(& ! * identify flux mask for the fully coupled solution if(ixCoupling==fullyCoupled)then desiredFlux = any(ixStateType_subset==flux2state_orig(iVar)%state1) .or. any(ixStateType_subset==flux2state_orig(iVar)%state2) + + ! make sure firstFluxCall fluxes are included in the mask + if (firstFluxCall) then + if (iVar==iLookFlux%scalarSoilResistance) desiredFlux = .true. + if (iVar==iLookFlux%scalarStomResistSunlit) desiredFlux = .true. + if (iVar==iLookFlux%scalarStomResistShaded) desiredFlux = .true. + if (iVar==iLookFlux%scalarPhotosynthesisSunlit) desiredFlux = .true. + if (iVar==iLookFlux%scalarPhotosynthesisShaded) desiredFlux = .true. + endif + fluxMask%var(iVar)%dat = desiredFlux ! * identify flux mask for the split solution @@ -547,13 +557,21 @@ subroutine opSplittin(& case default; err=20; message=trim(message)//'unable to identify split based on state type'; return end select + ! make sure firstFluxCall fluxes are included in the mask + if (firstFluxCall) then + if (iVar==iLookFlux%scalarSoilResistance) desiredFlux = .true. + if (iVar==iLookFlux%scalarStomResistSunlit) desiredFlux = .true. + if (iVar==iLookFlux%scalarStomResistShaded) desiredFlux = .true. + if (iVar==iLookFlux%scalarPhotosynthesisSunlit) desiredFlux = .true. + if (iVar==iLookFlux%scalarPhotosynthesisShaded) desiredFlux = .true. + endif + ! no domain splitting if(nDomains==1)then fluxMask%var(iVar)%dat = desiredFlux ! domain splitting else - !print*,"split domain" ! initialize to .false. fluxMask%var(iVar)%dat = .false. From 02baeba04d3fa39c1e39176c97d38e9df90fc80c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 17 Oct 2023 14:45:17 +0900 Subject: [PATCH 0957/1472] new variable for output, meanStepSize (seconds over data window) --- build/source/dshare/get_ixname.f90 | 1 + build/source/dshare/popMetadat.f90 | 1 + build/source/dshare/var_lookup.f90 | 3 ++- build/source/engine/coupled_em.f90 | 15 +++++++++++ build/source/engine/opSplittin.f90 | 35 ++++++++++++++++++++------ build/source/engine/summaSolve4ida.f90 | 4 +++ build/source/engine/systemSolv.f90 | 12 +++++---- build/source/engine/varSubstep.f90 | 10 +++----- 8 files changed, 61 insertions(+), 20 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index aef630e4a..b71c40f0a 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -583,6 +583,7 @@ function get_ixdiag(varName) ! timing information case('numFluxCalls' ); get_ixdiag = iLookDIAG%numFluxCalls ! number of flux calls (-) case('wallClockTime' ); get_ixdiag = iLookDIAG%wallClockTime ! wall clock time (s) + case('meanStepSize' ); get_ixdiag = iLookDIAG%meanStepSize ! mean time step size (s) over data window') ! get to here if cannot find the variable case default get_ixdiag = integerMissing diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 413efd860..d1f86a04d 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -438,6 +438,7 @@ subroutine popMetadat(err,message) ! timing information diag_meta(iLookDIAG%numFluxCalls) = var_info('numFluxCalls' , 'number of flux calls' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%wallClockTime) = var_info('wallClockTime' , 'wall clock time' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%meanStepSize) = var_info('meanStepSize' , 'mean time step size over data window' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! ----- ! * local model fluxes... ! ----------------------- diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 2c5630510..909bbefb6 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -463,6 +463,7 @@ MODULE var_lookup ! number of function evaluations integer(i4b) :: numFluxCalls = integerMissing ! number of flux calls (-) integer(i4b) :: wallClockTime = integerMissing ! wall clock time (s) + integer(i4b) :: meanStepSize = integerMissing ! mean time step size over data window (s) endtype iLook_diag ! *********************************************************************************************************** @@ -885,7 +886,7 @@ MODULE var_lookup 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,& 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,& 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,& - 81, 82, 83, 84, 85, 86, 87, 88, 89) + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90) ! named variables: model fluxes type(iLook_flux), public,parameter :: iLookFLUX =iLook_flux ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 05d4f4eb4..424baf6aa 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -266,6 +266,9 @@ subroutine coupled_em(& ! timing information real(rkind) :: startTime ! start time (used to compute wall clock time) real(rkind) :: endTime ! end time (used to compute wall clock time) + real(rkind) :: mean_step_dt_sub ! mean solution step for the sub-step + real(rkind) :: sumStepSize ! sum solution step for the data step + integer(i4b) :: nSteps ! number of solution steps for the data step ! outer loop control logical(lgt) :: firstInnerStep ! flag to denote if the first time step in maxstep subStep logical(lgt) :: lastInnerStep ! flag to denote if the last time step in maxstep subStep @@ -357,6 +360,8 @@ subroutine coupled_em(& meanSenHeatCanopy = 0._rkind ! mean sensible heat flux from the canopy effRainfall = 0._rkind ! mean total effective rainfall over snow + diag_data%var(iLookDIAG%meanStepSize)%dat(1) = 0._rkind ! mean step size over data_step + ! Need mean soil compression for balance checks but it is not in flux structure so handle differently ! This will be a problem if nSoil changes (currently not possible)-- then might need to not keep the average allocate(meanSoilCompress(nSoil)) @@ -837,6 +842,8 @@ subroutine coupled_em(& end do innerEffRainfall = 0._rkind ! mean total effective rainfall over snow innerSoilCompress = 0._rkind ! mean total soil compression + nsteps = 0 ! initialize the number of steps + sumStepSize= 0._rkind ! initialize the sum of the step sizes endif ! (do_outer loop) @@ -874,6 +881,7 @@ subroutine coupled_em(& tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt stepFailure, & ! intent(out): flag to denote that the coupled step failed ixSolution, & ! intent(out): solution method used in this iteration + mean_step_dt_sub, & ! intent(out): mean solution step for the sub-step err,cmessage) ! intent(out): error code and error message ! check for all errors (error recovery within opSplittin) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if @@ -910,6 +918,10 @@ subroutine coupled_em(& sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dt_sub*flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) sumSenHeatCanopy = sumSenHeatCanopy + dt_sub*flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) + ! update the step size sum + sumStepSize = sumStepSize + mean_step_dt_sub + nsteps = nsteps + 1 + ! update first step and first and last inner steps firstSubStep = .false. firstInnerStep = .false. @@ -1073,6 +1085,9 @@ subroutine coupled_em(& flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySublimation))%dat(1) = meanCanopySublimation flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarLatHeatCanopyEvap))%dat(1) = meanLatHeatCanopyEvap flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSenHeatCanopy))%dat(1) = meanSenHeatCanopy + + ! add mean step size for the data_step to the total mean step size + diag_data%var(iLookDIAG%meanStepSize)%dat(1) = diag_data%var(iLookDIAG%meanStepSize)%dat(1) + sumStepSize/nsteps endif ! save the time step to initialize the subsequent step diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 2b54f0a55..fe2cb4a74 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -181,6 +181,7 @@ subroutine opSplittin(& tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt stepFailure, & ! intent(out): flag to denote step failure ixSolution, & ! intent(out): solution method used in this iteration + mean_step_dt, & ! intent(out): mean solution step for the time step err,message) ! intent(out): error code and error message ! --------------------------------------------------------------------------------------- ! structure allocations @@ -220,6 +221,7 @@ subroutine opSplittin(& logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt logical(lgt),intent(out) :: stepFailure ! flag to denote step failure integer(i4b),intent(out) :: ixSolution ! index of solution method (1,2) + real(rkind),intent(out) :: mean_step_dt ! mean solution step for the time step integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! --------------------------------------------------------------------------------------- @@ -283,6 +285,9 @@ subroutine opSplittin(& integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) integer(i4b) :: nCoupling logical(lgt) :: firstInnerStep ! flag to denote if the first time step in maxstep subStep + ! mean steps + real(rkind) :: mean_step_state ! mean step over the state (with or without domain splits) + real(rkind) :: mean_step_solution ! mean step for a solution (scalar or vector) ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- @@ -412,18 +417,20 @@ subroutine opSplittin(& ! define the number of operator splits for the state type select case(ixCoupling) - case(fullyCoupled); nStateTypeSplit=1 - case(stateTypeSplit); nStateTypeSplit=nStateTypes - case default; err=20; message=trim(message)//'coupling case not found'; return + case(fullyCoupled); nStateTypeSplit=1 + case(stateTypeSplit); nStateTypeSplit=nStateTypes + case default; err=20; message=trim(message)//'coupling case not found'; return end select ! operator splitting option ! define if we wish to try the domain split select case(ixCoupling) - case(fullyCoupled); tryDomainSplit=0 - case(stateTypeSplit); tryDomainSplit=1 - case default; err=20; message=trim(message)//'coupling case not found'; return + case(fullyCoupled); tryDomainSplit=0 + case(stateTypeSplit); tryDomainSplit=1 + case default; err=20; message=trim(message)//'coupling case not found'; return end select ! operator splitting option + mean_step_dt = 0._rkind ! initialize mean step for the time step + ! state splitting loop stateTypeSplit: do iStateTypeSplit=1,nStateTypeSplit @@ -463,11 +470,14 @@ subroutine opSplittin(& err=20; return endif + mean_step_state = 0._rkind ! initialize mean step for state + ! domain splitting loop domainSplit: do iDomainSplit=1,nDomainSplit ! trial with the vector then scalar solution solution: do ixSolution=1,nSolutions + mean_step_solution = 0._rkind ! initialize mean step for a solution ! initialize error control err=0; message="opSplittin/" @@ -489,7 +499,6 @@ subroutine opSplittin(& case default; err=20; message=trim(message)//'unknown solution method'; return end select - ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) stateSplit: do iStateSplit=1,nStateSplit @@ -792,6 +801,8 @@ subroutine opSplittin(& ! success = exit solution if(.not.failure)then + ! sum the mean steps for the successful solution type + mean_step_solution = mean_step_solution + (dt/nSubsteps)/nStateSplit select case(ixStateThenDomain) case(fullDomain); if(iStateSplit==nStateSplit) exit stateThenDomain case(subDomain); if(iStateSplit==nStateSplit) exit solution @@ -816,13 +827,21 @@ subroutine opSplittin(& end do solution ! trial with the full layer solution then the split layer solution + ! sum the mean steps for the state over each domain split + mean_step_state = mean_step_state + mean_step_solution/nDomainSplit + ! ***** trial with a given solution method... ! ******************************************************************************************************************************* - end do domainSplit ! domain type splitting loop end do stateThenDomain ! switch between the state and the domain + ! sum the mean steps for the time step over each state type split + select case(ixStateThenDomain) + case(fullDomain); mean_step_dt = mean_step_dt + mean_step_solution/nStateTypeSplit + case(subDomain); mean_step_dt = mean_step_dt + mean_step_state/nStateTypeSplit + end select + ! ----- ! * reset state variables for the mass split... ! --------------------------------------------- diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 3d0418709..d2abc7cbd 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -126,6 +126,7 @@ subroutine summaSolve4ida( & ixSaturation, & ! intent(inout) index of the lowest saturated layer (NOTE: only computed on the first iteration) idaSucceeds, & ! intent(out): flag to indicate if IDA successfully solved the problem in current data step tooMuchMelt, & ! intent(inout): lag to denote that there was too much melt + nSteps, & ! intent(out): number of time steps taken in solver stateVec, & ! intent(out): model state vector stateVecPrime, & ! intent(out): derivative of model state vector err,message) ! intent(out): error control @@ -192,6 +193,7 @@ subroutine summaSolve4ida( & real(rkind),intent(inout) :: mLayerCmpress_sum(:) ! sum of soil compress ! output: state vectors integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer + integer(i4b),intent(out) :: nSteps ! number of time steps taken in solver real(rkind),intent(inout) :: stateVec(:) ! model state vector (y) real(rkind),intent(inout) :: stateVecPrime(:) ! model state vector (y') logical(lgt),intent(out) :: idaSucceeds ! flag to indicate if IDA is successful @@ -437,6 +439,7 @@ subroutine summaSolve4ida( & tinystep = .false. tret(1) = t0 ! initial time tretPrev = tret(1) + nSteps = 0 ! initialize number of time steps taken in solver do while(tret(1) < dt_cur) ! call this at beginning of step to reduce root bouncing (only looking in one direction) @@ -470,6 +473,7 @@ subroutine summaSolve4ida( & ! get the last stepsize and difference from previous end time, not necessarily the same retval = FIDAGetLastStep(ida_mem, dt_last) dt_diff = tret(1) - tretPrev + nSteps = nSteps + 1 ! number of time steps taken in solver ! check the feasibility of the solution feasible=.true. diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index cd215d22f..c98ce0fa1 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -145,6 +145,7 @@ subroutine systemSolv(& stateVecPrime, & ! intent(out): updated state vector if need the prime space (ida) untappedMelt, & ! intent(out): un-tapped melt energy (J m-3 s-1) niter, & ! intent(out): number of iterations taken (numrec) + nSteps, & ! intent(out): number of time steps taken in solver reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out): flag to denote that there was too much melt err,message) ! intent(out): error code and error message @@ -198,6 +199,7 @@ subroutine systemSolv(& real(rkind),intent(out) :: stateVecPrime(:) ! trial state vector (mixed units) logical(lgt),intent(out) :: reduceCoupledStep ! flag to reduce the length of the coupled step logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that there was too much melt + integer(i4b),intent(out) :: nSteps ! number of time steps taken in solver integer(i4b),intent(out) :: niter ! number of iterations taken integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -299,6 +301,7 @@ subroutine systemSolv(& ! --------------------------------------------------------------------------------------- ! initialize error control err=0; message="systemSolv/" + nSteps = 0 ! initialize number of time steps taken in solver ! ***** ! (0) PRELIMINARIES... @@ -556,6 +559,7 @@ subroutine systemSolv(& ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) sunSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt + nSteps, & ! intent(out): number of time steps taken in solver stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step err,cmessage) ! intent(out): error control @@ -638,6 +642,7 @@ subroutine systemSolv(& return endif niter = 0 ! iterations are counted inside KINSOL solver + nSteps = 1 ! number of time steps taken in solver ! save the computed solution stateVecTrial = stateVecNew @@ -713,8 +718,8 @@ subroutine systemSolv(& fOld = fNew rVec = resVecNew stateVecTrial = stateVecNew - ! NOTE 1: The derivatives computed in summaSolve4numrec are used to calculate the Jacobian matrix at the next iteration - ! NOTE 2: The Jacobian matrix together with the residual vector is used to calculate the new iteration increment + stateVecPrime = stateVecTrial !prime values not used here, dummy + nSteps = 1 ! number of time steps taken in solver ! exit iteration loop if converged if(converged) exit @@ -727,9 +732,6 @@ subroutine systemSolv(& end do ! iterating - ! prime values not used here, dummy - stateVecPrime = stateVecTrial - ! ----- ! * update states... ! Post processing step to “perfectly” conserve mass by pushing the errors into the state variables diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index b7acf350e..6041ef6ee 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -192,12 +192,9 @@ subroutine varSubstep(& real(rkind) :: dt_wght ! weight given to a given flux calculation real(rkind) :: dtSubstep ! length of a substep (s) real(rkind) :: maxstep ! maximum time step length (seconds) - ! adaptive sub-stepping for the explicit solution + integer(i4b) :: nSteps ! number of time steps taken in solver + ! adaptive sub-stepping for the i solution logical(lgt) :: failedSubstep ! flag to denote success of substepping for a given split - real(rkind),parameter :: safety=0.85_rkind ! safety factor in adaptive sub-stepping - real(rkind),parameter :: reduceMin=0.1_rkind ! mimimum factor that time step is reduced - real(rkind),parameter :: increaseMax=4.0_rkind ! maximum factor that time step is increased - ! adaptive sub-stepping for the implicit solution integer(i4b) :: niter ! number of iterations taken integer(i4b),parameter :: n_inc=5 ! minimum number of iterations to increase time step integer(i4b),parameter :: n_dec=15 ! maximum number of iterations to decrease time step @@ -347,6 +344,7 @@ subroutine varSubstep(& stateVecPrime, & ! intent(out): updated state vector if need the prime space (ida) untappedMelt, & ! intent(out): un-tapped melt energy (J m-3 s-1) niter, & ! intent(out): number of iterations taken (numrec) + nSteps, & ! intent(out): number of time steps taken in solver reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt err,cmessage) ! intent(out): error code and error message @@ -518,7 +516,7 @@ subroutine varSubstep(& end do ! (loop through fluxes) ! increment the number of substeps - nSubsteps = nSubsteps+1 + nSubsteps = nSubsteps + nSteps ! increment the sub-step legth dtSum = dtSum + dtSubstep From 2368fb675df84cb8b35c89b12d8747e2866a5dd5 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 17 Oct 2023 14:48:19 +0900 Subject: [PATCH 0958/1472] typo in comment --- build/source/dshare/get_ixname.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index b71c40f0a..60a6f9ccd 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -583,7 +583,7 @@ function get_ixdiag(varName) ! timing information case('numFluxCalls' ); get_ixdiag = iLookDIAG%numFluxCalls ! number of flux calls (-) case('wallClockTime' ); get_ixdiag = iLookDIAG%wallClockTime ! wall clock time (s) - case('meanStepSize' ); get_ixdiag = iLookDIAG%meanStepSize ! mean time step size (s) over data window') + case('meanStepSize' ); get_ixdiag = iLookDIAG%meanStepSize ! mean time step size (s) over data window ! get to here if cannot find the variable case default get_ixdiag = integerMissing From 2c018c10444314fc5a1b71002b5c07e5647e2249 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 18 Oct 2023 17:53:15 +0900 Subject: [PATCH 0959/1472] fixing problem where first fluxes were now masked twice --- build/source/engine/coupled_em.f90 | 12 ++++++---- build/source/engine/opSplittin.f90 | 38 ++++++++++++++++++------------ 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 424baf6aa..966c5a842 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -190,6 +190,7 @@ subroutine coupled_em(& real(rkind) :: maxstep_op ! maximum time step length (seconds) to run opSplittin over real(rkind) :: whole_step ! step the surface pond drainage and sublimation calculated over integer(i4b) :: nsub ! number of substeps + integer(i4b) :: nsub_success ! number of successful substeps logical(lgt) :: computeVegFluxOld ! flag to indicate if we are computing fluxes over vegetation on the previous sub step logical(lgt) :: includeAquifer ! flag to denote that an aquifer is included logical(lgt) :: modifiedLayers ! flag to denote that snow layers were modified @@ -268,7 +269,6 @@ subroutine coupled_em(& real(rkind) :: endTime ! end time (used to compute wall clock time) real(rkind) :: mean_step_dt_sub ! mean solution step for the sub-step real(rkind) :: sumStepSize ! sum solution step for the data step - integer(i4b) :: nSteps ! number of solution steps for the data step ! outer loop control logical(lgt) :: firstInnerStep ! flag to denote if the first time step in maxstep subStep logical(lgt) :: lastInnerStep ! flag to denote if the last time step in maxstep subStep @@ -610,7 +610,8 @@ subroutine coupled_em(& dtSave = whole_step ! length of whole substep ! initialize the number of sub-steps - nsub=0 + nsub = 0 + nsub_success = 0 ! loop through sub-steps substeps: do ! continuous do statement with exit clause (alternative to "while") @@ -920,7 +921,6 @@ subroutine coupled_em(& ! update the step size sum sumStepSize = sumStepSize + mean_step_dt_sub - nsteps = nsteps + 1 ! update first step and first and last inner steps firstSubStep = .false. @@ -1086,8 +1086,8 @@ subroutine coupled_em(& flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarLatHeatCanopyEvap))%dat(1) = meanLatHeatCanopyEvap flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSenHeatCanopy))%dat(1) = meanSenHeatCanopy - ! add mean step size for the data_step to the total mean step size - diag_data%var(iLookDIAG%meanStepSize)%dat(1) = diag_data%var(iLookDIAG%meanStepSize)%dat(1) + sumStepSize/nsteps + ! add mean step size for the data_step to the total step size sum + diag_data%var(iLookDIAG%meanStepSize)%dat(1) = diag_data%var(iLookDIAG%meanStepSize)%dat(1) + sumStepSize endif ! save the time step to initialize the subsequent step @@ -1097,6 +1097,7 @@ subroutine coupled_em(& if(globalPrintFlag)& write(*,'(a,1x,3(f18.5,1x))') 'dt_sub, dt_solv, data_step: ', dt_sub, dt_solv, data_step + nsub_success = nsub_success + 1 ! check that we have completed the sub-step if(dt_solv >= data_step-verySmall) then exit substeps @@ -1106,6 +1107,7 @@ subroutine coupled_em(& dt_sub = min(data_step - dt_solv, dt_sub) end do substeps ! (sub-step loop) + diag_data%var(iLookDIAG%meanStepSize)%dat(1) = diag_data%var(iLookDIAG%meanStepSize)%dat(1)/nsub_success ! *** add snowfall to the snowpack... ! ----------------------------------- diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index fe2cb4a74..f2a15f433 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -129,7 +129,7 @@ module opSplittin_module ! named variables for the switch between states and domains integer(i4b),parameter :: fullDomain=1 ! full domain (veg+snow+soil) -integer(i4b),parameter :: subDomain=2 ! sub domain (veg, snow, and soil separately) +integer(i4b),parameter :: subDomain=2 ! sub domain (veg, snow, soil, and aquifer separately) ! maximum number of possible splits integer(i4b),parameter :: nStateTypes=2 ! number of state types (energy, water) @@ -288,6 +288,7 @@ subroutine opSplittin(& ! mean steps real(rkind) :: mean_step_state ! mean step over the state (with or without domain splits) real(rkind) :: mean_step_solution ! mean step for a solution (scalar or vector) + logical(lgt) :: addFirstFlux ! flag to add the first flux to the mask ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- @@ -471,6 +472,7 @@ subroutine opSplittin(& endif mean_step_state = 0._rkind ! initialize mean step for state + addFirstFlux = .true. ! flag to add the first flux ! domain splitting loop domainSplit: do iDomainSplit=1,nDomainSplit @@ -501,8 +503,8 @@ subroutine opSplittin(& ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) stateSplit: do iStateSplit=1,nStateSplit - - ! ----- + + ! ----- ! * define state subsets for a given split... ! ------------------------------------------- @@ -527,6 +529,15 @@ subroutine opSplittin(& err,cmessage) ! intent(out) : error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + ! check that we do not attempt the scalar solution for the fully coupled case + if(ixCoupling==fullyCoupled .and. ixSolution==scalar)then + message=trim(message)//'only apply the scalar solution to the fully split coupling strategy' + err=20; return + endif + + ! reset the flag for the first flux call + if(.not.firstSuccess) firstFluxCall=.true. + ! ----- ! * define the mask of the fluxes used... ! --------------------------------------- @@ -543,15 +554,16 @@ subroutine opSplittin(& ! * identify flux mask for the fully coupled solution if(ixCoupling==fullyCoupled)then - desiredFlux = any(ixStateType_subset==flux2state_orig(iVar)%state1) .or. any(ixStateType_subset==flux2state_orig(iVar)%state2) + desiredFlux = any(ixStateType_subset==flux2state_orig(iVar)%state1) .or. any(ixStateType_subset==flux2state_orig(iVar)%state2) ! make sure firstFluxCall fluxes are included in the mask - if (firstFluxCall) then + if (firstFluxCall .and. addFirstFlux) then if (iVar==iLookFlux%scalarSoilResistance) desiredFlux = .true. if (iVar==iLookFlux%scalarStomResistSunlit) desiredFlux = .true. if (iVar==iLookFlux%scalarStomResistShaded) desiredFlux = .true. if (iVar==iLookFlux%scalarPhotosynthesisSunlit) desiredFlux = .true. if (iVar==iLookFlux%scalarPhotosynthesisShaded) desiredFlux = .true. + addFirstFlux = .false. endif fluxMask%var(iVar)%dat = desiredFlux @@ -567,12 +579,13 @@ subroutine opSplittin(& end select ! make sure firstFluxCall fluxes are included in the mask - if (firstFluxCall) then + if (firstFluxCall .and. addFirstFlux) then if (iVar==iLookFlux%scalarSoilResistance) desiredFlux = .true. if (iVar==iLookFlux%scalarStomResistSunlit) desiredFlux = .true. if (iVar==iLookFlux%scalarStomResistShaded) desiredFlux = .true. if (iVar==iLookFlux%scalarPhotosynthesisSunlit) desiredFlux = .true. if (iVar==iLookFlux%scalarPhotosynthesisShaded) desiredFlux = .true. + addFirstFlux = .false. endif ! no domain splitting @@ -643,6 +656,10 @@ subroutine opSplittin(& endif ! if the layer is active end do ! looping through layers + ! fluxes through aquifer + case(aquiferSplit) + fluxMask%var(iVar)%dat(:) = desiredFlux ! only would be firstFluxCall variables, no aquifer fluxes + ! check case default; err=20; message=trim(message)//'unable to identify split based on domain type'; return end select ! domain split @@ -667,15 +684,6 @@ subroutine opSplittin(& ! ******************************************************************************************************************************* ! ***** trial with a given solution method... - ! check that we do not attempt the scalar solution for the fully coupled case - if(ixCoupling==fullyCoupled .and. ixSolution==scalar)then - message=trim(message)//'only apply the scalar solution to the fully split coupling strategy' - err=20; return - endif - - ! reset the flag for the first flux call - if(.not.firstSuccess) firstFluxCall=.true. - ! save/recover copies of prognostic variables do iVar=1,size(prog_data%var) select case(failure) From db3a9cbaa6f0b196cdd84e1906faee88ec53158c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 18 Oct 2023 18:07:20 +0900 Subject: [PATCH 0960/1472] no nSteps --- build/source/engine/coupled_em.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 966c5a842..f10eaefc8 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -843,7 +843,6 @@ subroutine coupled_em(& end do innerEffRainfall = 0._rkind ! mean total effective rainfall over snow innerSoilCompress = 0._rkind ! mean total soil compression - nsteps = 0 ! initialize the number of steps sumStepSize= 0._rkind ! initialize the sum of the step sizes endif ! (do_outer loop) From b56562815655743a51f7bec4fd6c04eb275f7a0a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 19 Oct 2023 12:07:17 +0900 Subject: [PATCH 0961/1472] still fixing addFirstFlux for pathological splitting and failure cases --- build/source/engine/opSplittin.f90 | 81 +++++++++++++++--------------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index f2a15f433..efbbbd5a5 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -431,6 +431,7 @@ subroutine opSplittin(& end select ! operator splitting option mean_step_dt = 0._rkind ! initialize mean step for the time step + addFirstFlux = .true. ! flag to add the first flux to the mask ! state splitting loop stateTypeSplit: do iStateTypeSplit=1,nStateTypeSplit @@ -472,7 +473,6 @@ subroutine opSplittin(& endif mean_step_state = 0._rkind ! initialize mean step for state - addFirstFlux = .true. ! flag to add the first flux ! domain splitting loop domainSplit: do iDomainSplit=1,nDomainSplit @@ -504,7 +504,7 @@ subroutine opSplittin(& ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) stateSplit: do iStateSplit=1,nStateSplit - ! ----- + ! ----- ! * define state subsets for a given split... ! ------------------------------------------- @@ -519,6 +519,45 @@ subroutine opSplittin(& ! avoid redundant case where vector solution is of length 1 if(ixSolution==vector .and. count(stateMask)==1) cycle solution + ! check that we do not attempt the scalar solution for the fully coupled case + if(ixCoupling==fullyCoupled .and. ixSolution==scalar)then + message=trim(message)//'only apply the scalar solution to the fully split coupling strategy' + err=20; return + endif + + ! reset the flag for the first flux call + if(.not.firstSuccess) firstFluxCall=.true. + + ! save/recover copies of prognostic variables + do iVar=1,size(prog_data%var) + select case(failure) + case(.false.); prog_temp%var(iVar)%dat(:) = prog_data%var(iVar)%dat(:) + case(.true.); prog_data%var(iVar)%dat(:) = prog_temp%var(iVar)%dat(:) + end select + end do ! looping through variables + + ! save/recover copies of diagnostic variables + do iVar=1,size(diag_data%var) + select case(failure) + case(.false.); diag_temp%var(iVar)%dat(:) = diag_data%var(iVar)%dat(:) + case(.true.); diag_data%var(iVar)%dat(:) = diag_temp%var(iVar)%dat(:) + end select + end do ! looping through variables + + ! save/recover copies of model fluxes and mean fluxes + do iVar=1,size(flux_data%var) + select case(failure) + case(.false.) + flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_mntemp%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + addFirstFlux = .false. + case(.true.) + flux_data%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) + flux_mean%var(iVar)%dat(:) = flux_mntemp%var(iVar)%dat(:) + if(addFirstFlux) addFirstFlux = .true. + end select + end do ! looping through variables + ! ----- ! * assemble vectors for a given split... ! --------------------------------------- @@ -529,15 +568,6 @@ subroutine opSplittin(& err,cmessage) ! intent(out) : error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! check that we do not attempt the scalar solution for the fully coupled case - if(ixCoupling==fullyCoupled .and. ixSolution==scalar)then - message=trim(message)//'only apply the scalar solution to the fully split coupling strategy' - err=20; return - endif - - ! reset the flag for the first flux call - if(.not.firstSuccess) firstFluxCall=.true. - ! ----- ! * define the mask of the fluxes used... ! --------------------------------------- @@ -563,7 +593,6 @@ subroutine opSplittin(& if (iVar==iLookFlux%scalarStomResistShaded) desiredFlux = .true. if (iVar==iLookFlux%scalarPhotosynthesisSunlit) desiredFlux = .true. if (iVar==iLookFlux%scalarPhotosynthesisShaded) desiredFlux = .true. - addFirstFlux = .false. endif fluxMask%var(iVar)%dat = desiredFlux @@ -585,7 +614,6 @@ subroutine opSplittin(& if (iVar==iLookFlux%scalarStomResistShaded) desiredFlux = .true. if (iVar==iLookFlux%scalarPhotosynthesisSunlit) desiredFlux = .true. if (iVar==iLookFlux%scalarPhotosynthesisShaded) desiredFlux = .true. - addFirstFlux = .false. endif ! no domain splitting @@ -684,33 +712,6 @@ subroutine opSplittin(& ! ******************************************************************************************************************************* ! ***** trial with a given solution method... - ! save/recover copies of prognostic variables - do iVar=1,size(prog_data%var) - select case(failure) - case(.false.); prog_temp%var(iVar)%dat(:) = prog_data%var(iVar)%dat(:) - case(.true.); prog_data%var(iVar)%dat(:) = prog_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - ! save/recover copies of diagnostic variables - do iVar=1,size(diag_data%var) - select case(failure) - case(.false.); diag_temp%var(iVar)%dat(:) = diag_data%var(iVar)%dat(:) - case(.true.); diag_data%var(iVar)%dat(:) = diag_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - ! save/recover copies of model fluxes and mean fluxes - do iVar=1,size(flux_data%var) - select case(failure) - case(.false.) - flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) - flux_mntemp%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) - case(.true.) - flux_data%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) - flux_mean%var(iVar)%dat(:) = flux_mntemp%var(iVar)%dat(:) - end select - end do ! looping through variables ! ----- ! * solve variable subset for one time step... From 6d96dd684c85bfbc3ade2b0ee81dc147dcb4fd32 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 19 Oct 2023 13:01:43 +0900 Subject: [PATCH 0962/1472] modify utils to process split numbers --- utils/timeseries_to_statistics.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 96bbf56d7..c86668edb 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -46,8 +46,10 @@ src_dir = top_fold + 'summa-' + method_name ben_dir = top_fold + 'summa-' + bench_name src_pat = 'run1_G*_timestep.nc' -des_fil = method_name + '_hrly_diff_stats_{}_{}.nc' +des_fil = method_name + '_hrly_diff_stats_{}_{}.nc' +des_fl2 = method_name + '_hrly_diff_steps_{}_{}.nc' settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] +stepsets= ['numberStateSplit','numberDomainSplitNrg','numberDomainSplitMass','numberScalarSolutions','meanStepSize'] viz_fil = method_name + '_hrly_diff_stats_{}.nc' viz_fil = viz_fil.format(','.join(settings)) @@ -196,6 +198,19 @@ def run_loop(file,bench,processed_files_path): new = xr.merge([mean,mnnz,amax, mean_ben,mnnz_ben,amax_ben, rmse,rmnz, maxe, kgem]) new.to_netcdf(des_dir / des_fil.format(var,subset)) + for var in stepsets: + mean = dat[var].mean(dim='time') + mean = mean.expand_dims("stat").assign_coords(stat=("stat",["mean"])) + + na_mx = np.fabs(dat[var]).max()+1 + amx = np.fabs(dat[var].fillna(na_mx)).argmax(dim=['time']) + amax = dat[var].isel(amx).drop_vars('time') + amax = amax.expand_dims("stat").assign_coords(stat=("stat",["amax"])) + + new = xr.merge([mean,amax]) + new.to_netcdf(des_dir / des_fl2.format(var,subset)) + + # write the name of the processed file to the file list, acquire the lock before opening the file if not_parallel: with open(processed_files_path, 'a') as filew: @@ -257,8 +272,10 @@ def merge_subsets_into_one(src,pattern,des,name): # merge the individual files into one for further vizualization merge_subsets_into_one(des_dir,des_fil.replace('{}','*'),fnl_dir,viz_fil) +merge_subsets_into_one(des_dir,des_fl2.replace('{}','*'),fnl_dir,viz_fil) # remove the individual files for cleanliness for file in glob.glob(str(des_dir / des_fil.replace('{}','*'))): os.remove(file) - +for file in glob.glob(str(des_dir / des_fl2.replace('{}','*'))): + os.remove(file) From dce9a2f792539820fa319a99a26dca155bb60415 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 19 Oct 2023 13:51:22 +0900 Subject: [PATCH 0963/1472] just utilities --- utils/scat_per_GRU.py | 2 +- utils/steps_per_GRU.py | 120 ++++++++++++++++++++++++++++++ utils/timeseries_to_statistics.py | 4 +- utils/wallClock_per_GRU.py | 2 +- 4 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 utils/steps_per_GRU.py diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index 9f2ec40f5..8d8e87a38 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -153,7 +153,7 @@ def run_loop(i,var): if stat == 'kgem': axs[r,c].set_xlabel(stat_word) if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + ' rel to bench ' + stat0_word) - axs[r,c].set_ylabel(stat0_word + '[{}]'.format(leg_titl0[i])) + axs[r,c].set_ylabel(stat0_word + ' [{}]'.format(leg_titl0[i])) if do_rel and var!='wallClockTime': axs[r,c].set_ylabel(stat0_word + ' rel to bench ' + stat0_word) diff --git a/utils/steps_per_GRU.py b/utils/steps_per_GRU.py new file mode 100644 index 000000000..c26f7f4ef --- /dev/null +++ b/utils/steps_per_GRU.py @@ -0,0 +1,120 @@ +# written by A. Van Beusekom (2023) + +## Visualize statistics per GRU +## Needs: +# SUMMA output statistics + +## Special note +# SUMMA simulations have been preprocessed into single value statistics per model element, using auxiliary scripts in ~/utils +# Run: +# python steps_per_GRU.py [stat] +# where stat is mean or amax + +# modules +import os +import matplotlib +import numpy as np +import xarray as xr +from pathlib import Path +import matplotlib.pyplot as plt +import copy +import pandas as pd + +viz_dir = Path('/home/avanb/scratch/statistics') +nbatch_hrus = 518 # number of HRUs per batch + +testing = False +if testing: + stat = 'mean' + viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') + method_name=['be1'] #maybe make this an argument +else: + import sys + # The first input argument specifies the run where the files are + stat = sys.argv[1] + method_name=['be1','be4','be8','be16','be32'] #maybe make this an argument + #method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument + +# Simulation statistics file locations +settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] +stepsets= ['numberStateSplit','numberDomainSplitNrg','numberDomainSplitMass','numberScalarSolutions','meanStepSize'] + +viz_fil = method_name.copy() +viz_fl2 = method_name.copy() +for i, m in enumerate(method_name): + viz_fil[i] = m + '_hrly_diff_stats_{}.nc' + viz_fil[i] = viz_fil[i].format(','.join(settings)) + viz_fl2[i] = m + '_hrly_diff_steps_{}.nc' + viz_fl2[i] = viz_fl2[i].format(','.join(stepsets)) + +# Specify variables of interest +plot_vars = stepsets.copy() +plt_titl = ['(a) Energy Domain Splits','(b) Mass Domain Splits','(c) Scalar Solutions', '(d) Step Size'] +leg_titl = ['$num$', '$num$','$num$','$s$'] +leg_titl0 = ['$num$', '$num$','$num$','$s$'] + +#fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' +#fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) +fig_fil = 'Splits_steps_scat_compressed.png' + +summa = {} +wall = {} +for i, m in enumerate(method_name): + # Get the aggregated statistics of SUMMA simulations + summa[m] = xr.open_dataset(viz_dir/viz_fl2[i]) + wall[m] = xr.open_dataset(viz_dir/viz_fl2[i]) + +##Figure + +# Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug +if 'compressed' in fig_fil: + plt.rcParams.update({'font.size': 25}) +else: + plt.rcParams.update({'font.size': 100}) + +if 'compressed' in fig_fil: + fig,axs = plt.subplots(2,2,figsize=(35,33)) +else: + fig,axs = plt.subplots(2,2,figsize=(140,133)) +fig.suptitle('Hourly Splits and Time Steps for each GRU', fontsize=40) + +def run_loop(i,var): + r = i//2 + c = i-r*2 + + # Data + for m in method_name: + s = summa[m][var].sel(stat=stat) + + if var == 'numberDomainSplitNrg' or var == 'numberDomainSplitMass': + s0 = summa[m]['numberStateSplit',].sel(stat=stat) + stat0_word = 'State Splits' + if var == 'numberDomainSplitNrg': stat_word = 'Energy Domain Splits' + if var == 'numberDomainSplitMass': stat_word = 'Mass Domain Splits' + if var == 'numberScalarSolutions': + s0 = summa[m]['numberDomainSplitMass'].sel(stat=stat) + summa[m]['numberDomainSplitNrg'].sel(stat=stat) + stat0_word = 'Domain Splits' + stat_word = 'Scalar Solutions' + if var == 'meanStepSize': + s0 = wall[m]['wallClockTime'].sel(stat=stat) + stat0_word = 'Wallclock Time' + stat_word = 'Mean Step Size' + + axs[r,c].scatter(x=s.values,y=s0.values,s=1,zorder=0,label=m) + + if stat == 'mean': word = ' mean' + if stat == 'amax': word = ' max' + + lgnd = axs[r,c].legend() + for j, m in enumerate(method_name): + lgnd.legendHandles[j]._sizes = [80] + axs[r,c].set_title(plt_titl[i]) + axs[r,c].set_xlabel(stat_word + word + ' [{}]'.format(leg_titl[i])) + axs[r,c].set_ylabel(stat0_word + word + ' [{}]'.format(leg_titl0[i])) + + +for i,var in enumerate(plot_vars): + run_loop(i,var) + +# Save +plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index c86668edb..98dd7543d 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -53,6 +53,8 @@ viz_fil = method_name + '_hrly_diff_stats_{}.nc' viz_fil = viz_fil.format(','.join(settings)) +viz_fl2 = method_name + '_hrly_diff_steps_{}.nc' +viz_fl2 = viz_fl2.format(','.join(stepsets)) # Make sure we're dealing with the right kind of inputs src_dir = Path(src_dir) @@ -272,7 +274,7 @@ def merge_subsets_into_one(src,pattern,des,name): # merge the individual files into one for further vizualization merge_subsets_into_one(des_dir,des_fil.replace('{}','*'),fnl_dir,viz_fil) -merge_subsets_into_one(des_dir,des_fl2.replace('{}','*'),fnl_dir,viz_fil) +merge_subsets_into_one(des_dir,des_fl2.replace('{}','*'),fnl_dir,viz_fl2) # remove the individual files for cleanliness for file in glob.glob(str(des_dir / des_fil.replace('{}','*'))): diff --git a/utils/wallClock_per_GRU.py b/utils/wallClock_per_GRU.py index f0d240273..6636b8916 100644 --- a/utils/wallClock_per_GRU.py +++ b/utils/wallClock_per_GRU.py @@ -25,7 +25,7 @@ testing = False if testing: viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be1','be64',] #maybe make this an argument + method_name=['be1'] #maybe make this an argument else: import sys method_name=['be1','be4','be8','be16','be32'] #sundials will not show node differences as much From 2fd46f1a6ef3831ba23df79342a1457f5cb1db27 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 19 Oct 2023 21:00:38 +0900 Subject: [PATCH 0964/1472] utilities --- utils/stepsMap_per_GRU.py | 242 ++++++++++++++++++ utils/steps_per_GRU.py | 3 +- ...put_per_GRU.py => wallClockMap_per_GRU.py} | 2 +- 3 files changed, 244 insertions(+), 3 deletions(-) create mode 100644 utils/stepsMap_per_GRU.py rename utils/{input_per_GRU.py => wallClockMap_per_GRU.py} (99%) diff --git a/utils/stepsMap_per_GRU.py b/utils/stepsMap_per_GRU.py new file mode 100644 index 000000000..be2a78108 --- /dev/null +++ b/utils/stepsMap_per_GRU.py @@ -0,0 +1,242 @@ +# written originally by W. Knoben, modified by A. Van Beusekom (2023) + +## Visualize batch number and wallClocktime per GRU, (could be modified to visualize other attributes or forcing per GRU) +## Needs: +# Catchment shapefile with GRU delineation +# ? SUMMA forcing folder and attribute folder +# SUMMA stats file for wall clock times + +## Special note +# To improve visualization of large lakes, HydroLAKES lake delineations are plotted on top of the catchment GRUs and river segments. +# Dealing with HydroLAKES inputs is not considered within scope of the workflow and therefore requires some manual downloading and preprocessing of this data for those who wish to reproduce this step. +# The relevant code is easily disabled by switching the plot_lakes = True flag to False. + +# Run: +# python stepsMap_per_GRU.py be32 + + +# modules +import sys +import os +import matplotlib +import numpy as np +import xarray as xr +from pathlib import Path +import matplotlib.pyplot as plt +import copy +import pyproj +import fiona +import geopandas as gpd +import pandas as pd + +# The first input argument specifies the run where the files are +method_name = sys.argv[1] # sys.argv values are strings by default so this is fine +stat = sys.argv[2] + +# Simulation statistics file locations +stepsets= ['numberStateSplit','numberDomainSplitNrg','numberDomainSplitMass','numberScalarSolutions','meanStepSize'] +settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] + +viz_dir = Path('/home/avanb/scratch/statistics') +viz_fil = method_name + '_hrly_diff_steps_{}.nc' +viz_fil = viz_fil.format(','.join(settings)) +viz_fl2 = method_name + '_hrly_diff_steps_{}.nc' +viz_fl2 = viz_fl2.format(','.join(stepsets)) + +# Specify variables of interest +# Note, max for nodes is based off Graham which has 1185 nodes +plot_vars = ['numberStateSplit','numberDomainSplitNrg','numberDomainSplitMass','numberScalarSolutions','meanStepSize','wallClockTime'] +plt_titl = ['(a) State Splits','(b) Energy Domain Splits','(c) Mass Domain Splits','(d) Scalar Solutions', '(e) Mean Step Size','(f) Wallclock Time'] +leg_titl = ['$num$','$num$','$num$','$num$','$s$','$s$'] +if stat == 'mean': maxes = [1,1,1,1,3600,10e-3] +if stat == 'amax': maxes = [1,1,1,1,3600,0.2] + +fig_fil = method_name + '_splits_steps_compressed.png' + +# Get the albers shapes +main = Path('/home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/albers_projection') + +# Plot lakes? +plot_lakes = True +# lakes shapefile WHERE IS THIS +#lake_path = Path('C:/Globus endpoint/HydroLAKES/HydroLAKES_polys_v10_shp') +#lake_name = 'HydroLAKES_polys_v10_subset_NA.shp' + +## Control file handling +# Store the name of the 'active' file in a variable +controlFile = 'plot_control_NorthAmerica.txt' + +# Function to extract a given setting from the control file +def read_from_control( file, setting ): + + # Open controlFile and ... + with open(file) as contents: + for line in contents: + + # ... find the line with the requested setting + if setting in line and not line.startswith('#'): + break + + # Extract the setting's value + substring = line.split('|',1)[1] # Remove the setting's name (split into 2 based on '|', keep only 2nd part) + substring = substring.split('#',1)[0] # Remove comments, does nothing if no '#' is found + substring = substring.strip() # Remove leading and trailing whitespace, tabs, newlines + + # Return this value + return substring + +# Function to specify a default path +def make_default_path(suffix): + + # Get the root path + rootPath = Path( read_from_control(controlFile,'root_path') ) + + # Get the domain folder + domainName = read_from_control(controlFile,'domain_name') + domainFolder = 'domain_' + domainName + + # Specify the forcing path + defaultPath = rootPath / domainFolder / suffix + + return defaultPath + + +## Catchment shapefile location and variable names +# HM catchment shapefile path & name +hm_catchment_path = read_from_control(controlFile,'catchment_shp_path') +hm_catchment_name = read_from_control(controlFile,'catchment_shp_name') +# Specify default path if needed +if hm_catchment_path == 'default': + hm_catchment_path = make_default_path('shapefiles/catchment') # outputs a Path() +else: + hm_catchment_path = Path(hm_catchment_path) # make sure a user-specified path is a Path() + +# Find the GRU and HRU identifiers +hm_hruid = read_from_control(controlFile,'catchment_shp_hruid') + + +## River network shapefile location and variable names +# Plot rivers? +plot_rivers = False +# River network path & name +river_network_path = read_from_control(controlFile,'river_network_shp_path') +river_network_name = read_from_control(controlFile,'river_network_shp_name') +# Specify default path if needed +if river_network_path == 'default': + river_network_path = make_default_path('shapefiles/river_network') # outputs a Path() +else: + river_network_path = Path(river_network_path) # make sure a user-specified path is a Path() + +# Find the segment ID +seg_id = read_from_control(controlFile,'river_network_shp_segid') + +## Load all shapefiles and project to Albers Conformal Conic and reproject +# Set the target CRS +acc = 'ESRI:102008' + +# catchment shapefile, first 2 lines throw error so cutting them +#bas = gpd.read_file(hm_catchment_path/hm_catchment_name) +#bas_albers = bas.to_crs(acc) +bas_albers = gpd.read_file(main/'basin.shp') + +# river network shapefile, first 2 lines throw error so cutting them +if plot_rivers: + #riv = gpd.read_file(river_network_path/river_network_name) + #riv_albers = riv.to_crs(acc) + riv_albers = gpd.read_file(main/'river.shp') + +# lakes shapefile, first 2 lines throw error so cutting them +if plot_lakes: + #lakes = gpd.read_file(lake_path/lake_name) + #lak_albers = lakes.to_crs(acc) + lak_albers = gpd.read_file(main/'lakes.shp') + +## Pre-processing, map SUMMA sims to catchment shapes +# Get the aggregated statistics of SUMMA simulations +summa = xr.open_dataset(viz_dir/viz_fl2) +wall = xr.open_dataset(viz_dir/viz_fil) + +# Match the accummulated values to the correct HRU IDs in the shapefile +hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile +for plot_var in plot_vars: + stat0 = stat + + if plot_var != 'wallClockTime': + s = summa[plot_var].sel(stat=stat0) + else: + s = wall[plot_var].sel(stat=stat0) + + s = np.fabs(s) # make absolute value norm, not all positive + bas_albers[plot_var] = s.sel(hru=hru_ids_shp.values) + +# Select lakes of a certain size for plotting +if plot_lakes: + minSize = 1000 # km2 + in_domain = (lak_albers['Country'] == 'Canada') | \ + (lak_albers['Country'] == 'United States of America') | \ + (lak_albers['Country'] == 'Mexico') + out_domain = (lak_albers['Pour_long'] > -80) & (lak_albers['Pour_lat'] > 65) # Exclude Baffin Island + large_lakes_albers = lak_albers.loc[(lak_albers['Lake_area'] > minSize) & in_domain & (~out_domain) ] + lake_col = (8/255,81/255,156/255) + +##Figure + +# Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug +if 'compressed' in fig_fil: + plt.rcParams.update({'font.size': 25}) +else: + plt.rcParams.update({'font.size': 100}) + +if 'compressed' in fig_fil: + fig,axs = plt.subplots(3,2,figsize=(35,33)) +else: + fig,axs = plt.subplots(3,2,figsize=(140,133)) +fig.suptitle('{} Hourly Statistics'.format(method_name), fontsize=40) + +plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines + +# colorbar axes +f_x_mat = [0.443,0.94,0.443,0.94,0.443,0.94] +f_y_mat = [0.71,0.71,0.38,0.38,0.047,0.047] + +plt.tight_layout() + +def run_loop(i,var,the_max,f_x,f_y): + my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap + my_cmap.set_bad(color='white') #nan color white + vmin,vmax = 0, the_max + norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) + if var=='meanStepSize': + my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno')) # copy the default cmap + my_cmap.set_bad(color='white') #nan color white + vmin,vmax = 0, the_max + norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) + r = i//2 + c = i-r*2 + + # Data + bas_albers.plot(ax=axs[r,c], column=var, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) + + if stat == 'mean': word = 'mean' + if stat == 'amax': word = 'max' + + axs[r,c].set_title(plt_titl[i]) + axs[r,c].axis('off') + + # Custom colorbar + cax = fig.add_axes([f_x,f_y,0.02,0.25]) + sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) + sm._A = [] + cbr = fig.colorbar(sm, cax=cax) #, extend='max') #if max extend can't get title right + cbr.ax.set_ylabel(word + ' [{}]'.format(leg_titl[i]), labelpad=40, rotation=270) + + #cbr.ax.yaxis.set_offset_position('right') + + # lakes + if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) + +for i,(var,the_max,f_x,f_y) in enumerate(zip(plot_vars,maxes,f_x_mat,f_y_mat)): + run_loop(i,var,the_max,f_x,f_y) + +# Save +plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=True) \ No newline at end of file diff --git a/utils/steps_per_GRU.py b/utils/steps_per_GRU.py index c26f7f4ef..1d534be25 100644 --- a/utils/steps_per_GRU.py +++ b/utils/steps_per_GRU.py @@ -21,7 +21,6 @@ import pandas as pd viz_dir = Path('/home/avanb/scratch/statistics') -nbatch_hrus = 518 # number of HRUs per batch testing = False if testing: @@ -49,7 +48,7 @@ # Specify variables of interest plot_vars = stepsets.copy() -plt_titl = ['(a) Energy Domain Splits','(b) Mass Domain Splits','(c) Scalar Solutions', '(d) Step Size'] +plt_titl = ['(a) Energy Domain Splits','(b) Mass Domain Splits','(c) Scalar Solutions', '(d) Mean Step Size'] leg_titl = ['$num$', '$num$','$num$','$s$'] leg_titl0 = ['$num$', '$num$','$num$','$s$'] diff --git a/utils/input_per_GRU.py b/utils/wallClockMap_per_GRU.py similarity index 99% rename from utils/input_per_GRU.py rename to utils/wallClockMap_per_GRU.py index 574b1fc8e..d1744da40 100644 --- a/utils/input_per_GRU.py +++ b/utils/wallClockMap_per_GRU.py @@ -12,7 +12,7 @@ # The relevant code is easily disabled by switching the plot_lakes = True flag to False. # Run: -# python input_per_GRU.py be32 +# python wallClockMap_per_GRU.py be32 # modules From 5e2e29572d4993c5a0153aa8cdad5c49338324e6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 24 Oct 2023 11:05:18 +0900 Subject: [PATCH 0965/1472] utils change only --- utils/hist_per_GRU.py | 6 +++--- utils/plot_per_GRU.py | 18 +++++++++++++----- utils/stepsMap_per_GRU.py | 12 ++++++++---- utils/steps_per_GRU.py | 16 +++++++++------- utils/wallClockMap_per_GRU.py | 3 +++ 5 files changed, 36 insertions(+), 19 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 4dd2e3a71..70b954f57 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -60,13 +60,13 @@ if stat == 'rmse': maxes = [2,15,250,0.08,200,10e-3] #[2,15,8e-6,0.08,6e-9,10e-3] #maxes = [0.25,2,30,0.01,30,2e-3] #[0.25,2,1e-6,0.01,1e-9,2e-3] - if do_rel: maxes = [0.6,0.1,0.6,0.6,0.6,10e-3] + if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] if stat == 'rmnz': maxes = [2,15,250,0.08,200,10e-3] - if do_rel: maxes = [0.6,0.1,0.6,0.6,0.6,10e-3] + if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] if stat == 'maxe': maxes = [15,25,0.8,2,0.3,0.2] #[15,25,25e-5,2,1e-7,0.2] - if do_rel: maxes = [0.6,0.1,0.6,0.6,0.6,0.2] + if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,0.2] if stat == 'kgem': maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index f0cc2d572..8386f5955 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -54,13 +54,13 @@ if stat == 'rmse': maxes = [2,15,250,0.08,200,10e-3] #[2,15,8e-6,0.08,6e-9,10e-3] #maxes = [0.25,2,30,0.01,30,2e-3] #[0.25,2,1e-6,0.01,1e-9,2e-3] - if do_rel: maxes = [0.6,0.1,0.6,0.6,0.6,10e-3] + if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] if stat == 'rmnz': maxes = [2,15,250,0.08,200,10e-3] - if do_rel: maxes = [0.6,0.1,0.6,0.6,0.6,10e-3] + if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] if stat == 'maxe': maxes = [15,25,0.8,2,0.3,0.2] #[15,25,25e-5,2,1e-7,0.2] - if do_rel: maxes = [0.6,0.1,0.6,0.6,0.6,0.2] + if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,0.2] if stat == 'kgem': maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] if stat == 'mean': @@ -157,6 +157,7 @@ def make_default_path(suffix): #bas = gpd.read_file(hm_catchment_path/hm_catchment_name) #bas_albers = bas.to_crs(acc) bas_albers = gpd.read_file(main/'basin.shp') +xmin, ymin, xmax, ymax = bas_albers.total_bounds # river network shapefile, first 2 lines throw error so cutting them if plot_rivers: @@ -218,7 +219,12 @@ def make_default_path(suffix): # Multiply the s values by efficiency s = s*eff_batch - s = np.fabs(s) # make absolute value norm, not all positive + # Make absolute value norm, not all positive + s = np.fabs(s) + + # Replace inf values with NaN in the s DataArray + s = s.where(~np.isinf(s), np.nan) + if plot_var == 'scalarTotalET' and not do_rel: if stat =='rmse' or stat =='rmnz' : s = s*31557600 # make annual total if stat =='maxe': s = s*3600 # make hourly max @@ -276,7 +282,7 @@ def run_loop(i,var,the_max,f_x,f_y): r = i//2 c = i-r*2 - # Data + # Plot the data with the full extent of the bas_albers shape bas_albers.plot(ax=axs[r,c], column=var, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) if stat0 == 'rmse': stat_word = 'RMSE' @@ -293,6 +299,8 @@ def run_loop(i,var,the_max,f_x,f_y): axs[r,c].set_title(plt_titl[i]) axs[r,c].axis('off') + axs[r,c].set_xlim(xmin, xmax) + axs[r,c].set_ylim(ymin, ymax) # Custom colorbar cax = fig.add_axes([f_x,f_y,0.02,0.25]) diff --git a/utils/stepsMap_per_GRU.py b/utils/stepsMap_per_GRU.py index be2a78108..d1304693c 100644 --- a/utils/stepsMap_per_GRU.py +++ b/utils/stepsMap_per_GRU.py @@ -38,7 +38,7 @@ settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] viz_dir = Path('/home/avanb/scratch/statistics') -viz_fil = method_name + '_hrly_diff_steps_{}.nc' +viz_fil = method_name + '_hrly_diff_stats_{}.nc' viz_fil = viz_fil.format(','.join(settings)) viz_fl2 = method_name + '_hrly_diff_steps_{}.nc' viz_fl2 = viz_fl2.format(','.join(stepsets)) @@ -48,10 +48,11 @@ plot_vars = ['numberStateSplit','numberDomainSplitNrg','numberDomainSplitMass','numberScalarSolutions','meanStepSize','wallClockTime'] plt_titl = ['(a) State Splits','(b) Energy Domain Splits','(c) Mass Domain Splits','(d) Scalar Solutions', '(e) Mean Step Size','(f) Wallclock Time'] leg_titl = ['$num$','$num$','$num$','$num$','$s$','$s$'] -if stat == 'mean': maxes = [1,1,1,1,3600,10e-3] -if stat == 'amax': maxes = [1,1,1,1,3600,0.2] +if stat == 'mean': maxes = [0.06,0.0004,0.0004,0.0015,3600,10e-3] +if stat == 'amax': maxes = [2,1,1,10,3600,0.2] -fig_fil = method_name + '_splits_steps_compressed.png' +fig_fil = method_name + '_splits_steps_{}_compressed.png' +fig_fil = fig_fil.format(stat) # Get the albers shapes main = Path('/home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/albers_projection') @@ -138,6 +139,7 @@ def make_default_path(suffix): #bas = gpd.read_file(hm_catchment_path/hm_catchment_name) #bas_albers = bas.to_crs(acc) bas_albers = gpd.read_file(main/'basin.shp') +xmin, ymin, xmax, ymax = bas_albers.total_bounds # river network shapefile, first 2 lines throw error so cutting them if plot_rivers: @@ -222,6 +224,8 @@ def run_loop(i,var,the_max,f_x,f_y): axs[r,c].set_title(plt_titl[i]) axs[r,c].axis('off') + axs[r,c].set_xlim(xmin, xmax) + axs[r,c].set_ylim(ymin, ymax) # Custom colorbar cax = fig.add_axes([f_x,f_y,0.02,0.25]) diff --git a/utils/steps_per_GRU.py b/utils/steps_per_GRU.py index 1d534be25..f7dc3326e 100644 --- a/utils/steps_per_GRU.py +++ b/utils/steps_per_GRU.py @@ -22,9 +22,9 @@ viz_dir = Path('/home/avanb/scratch/statistics') -testing = False +testing = True if testing: - stat = 'mean' + stat = 'amax' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') method_name=['be1'] #maybe make this an argument else: @@ -32,6 +32,7 @@ # The first input argument specifies the run where the files are stat = sys.argv[1] method_name=['be1','be4','be8','be16','be32'] #maybe make this an argument + method_name=['be1'] #maybe make this an argument #method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument # Simulation statistics file locations @@ -47,21 +48,22 @@ viz_fl2[i] = viz_fl2[i].format(','.join(stepsets)) # Specify variables of interest -plot_vars = stepsets.copy() +plot_vars = ['numberDomainSplitNrg','numberDomainSplitMass','numberScalarSolutions','meanStepSize'] plt_titl = ['(a) Energy Domain Splits','(b) Mass Domain Splits','(c) Scalar Solutions', '(d) Mean Step Size'] leg_titl = ['$num$', '$num$','$num$','$s$'] leg_titl0 = ['$num$', '$num$','$num$','$s$'] #fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' #fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) -fig_fil = 'Splits_steps_scat_compressed.png' +fig_fil = 'Splits_steps_scat_{}_compressed.png' +fig_fil = fig_fil.format(stat) summa = {} wall = {} for i, m in enumerate(method_name): # Get the aggregated statistics of SUMMA simulations summa[m] = xr.open_dataset(viz_dir/viz_fl2[i]) - wall[m] = xr.open_dataset(viz_dir/viz_fl2[i]) + wall[m] = xr.open_dataset(viz_dir/viz_fil[i]) ##Figure @@ -86,7 +88,7 @@ def run_loop(i,var): s = summa[m][var].sel(stat=stat) if var == 'numberDomainSplitNrg' or var == 'numberDomainSplitMass': - s0 = summa[m]['numberStateSplit',].sel(stat=stat) + s0 = summa[m]['numberStateSplit'].sel(stat=stat) stat0_word = 'State Splits' if var == 'numberDomainSplitNrg': stat_word = 'Energy Domain Splits' if var == 'numberDomainSplitMass': stat_word = 'Mass Domain Splits' @@ -99,7 +101,7 @@ def run_loop(i,var): stat0_word = 'Wallclock Time' stat_word = 'Mean Step Size' - axs[r,c].scatter(x=s.values,y=s0.values,s=1,zorder=0,label=m) + axs[r,c].scatter(x=s.values,y=s0.values,s=10,zorder=0,label=m) if stat == 'mean': word = ' mean' if stat == 'amax': word = ' max' diff --git a/utils/wallClockMap_per_GRU.py b/utils/wallClockMap_per_GRU.py index d1744da40..6881bd9b1 100644 --- a/utils/wallClockMap_per_GRU.py +++ b/utils/wallClockMap_per_GRU.py @@ -134,6 +134,7 @@ def make_default_path(suffix): #bas = gpd.read_file(hm_catchment_path/hm_catchment_name) #bas_albers = bas.to_crs(acc) bas_albers = gpd.read_file(main/'basin.shp') +xmin, ymin, xmax, ymax = bas_albers.total_bounds # river network shapefile, first 2 lines throw error so cutting them if plot_rivers: @@ -252,6 +253,8 @@ def run_loop(i,var,the_max,f_x,f_y): axs[r,c].set_title(plt_titl[i]) axs[r,c].axis('off') + axs[r,c].set_xlim(xmin, xmax) + axs[r,c].set_ylim(ymin, ymax) # lakes if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) From babeba31d593dd5e982194dff1929b67dacfd655 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 24 Oct 2023 14:05:28 +0900 Subject: [PATCH 0966/1472] utils, testing should be false --- utils/steps_per_GRU.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/steps_per_GRU.py b/utils/steps_per_GRU.py index f7dc3326e..a3cb23ad4 100644 --- a/utils/steps_per_GRU.py +++ b/utils/steps_per_GRU.py @@ -22,7 +22,7 @@ viz_dir = Path('/home/avanb/scratch/statistics') -testing = True +testing = False if testing: stat = 'amax' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') From b25345220df10465ce440605b5f3cda742b886e3 Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 24 Oct 2023 01:20:15 -0600 Subject: [PATCH 0967/1472] Added a contains block for opSplittin with initialize and finalize subroutines. --- build/source/engine/opSplittin.f90 | 202 ++++++++++++++++------------- 1 file changed, 109 insertions(+), 93 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index efbbbd5a5..61c95f83c 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -328,82 +328,8 @@ subroutine opSplittin(& case(kinsol, numrec); nCoupling = 2 end select - ! ----- - ! * initialize... - ! --------------- - - ! set the global print flag - globalPrintFlag=.false. - - if(globalPrintFlag)& - print*, trim(message), dt - - ! initialize the first success call - firstSuccess=.false. - if (.not.firstInnerStep) firstSuccess=.true. - - ! initialize the flags - tooMuchMelt=.false. ! too much melt (merge snow layers) - stepFailure=.false. ! step failure - - ! initialize flag for the success of the substepping - failure=.false. - - ! initialize the flux check - neededFlux(:) = .false. - - ! initialize the state check - stateCheck(:) = 0 - - ! allocate space for the flux mask (used to define when fluxes are updated) - call allocLocal(flux_meta(:),fluxMask,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! allocate space for the flux count (used to check that fluxes are only updated once) - call allocLocal(flux_meta(:),fluxCount,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! allocate space for the temporary prognostic variable structure - call allocLocal(prog_meta(:),prog_temp,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! allocate space for the temporary diagnostic variable structure - call allocLocal(diag_meta(:),diag_temp,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! allocate space for the temporary flux variable structure - call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! allocate space for the mean flux variable structure - call allocLocal(flux_meta(:),flux_mean,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! allocate space for the temporary mean flux variable structure - call allocLocal(flux_meta(:),flux_mntemp,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! allocate space for the derivative structure - call allocLocal(deriv_meta(:),deriv_data,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - - ! intialize the flux counter - do iVar=1,size(flux_meta) ! loop through fluxes - fluxCount%var(iVar)%dat(:) = 0 - end do - - ! initialize the model fluxes - do iVar=1,size(flux_meta) ! loop through fluxes - if(flux2state_orig(iVar)%state1==integerMissing .and. flux2state_orig(iVar)%state2==integerMissing) cycle ! flux does not depend on state (e.g., input) - if(flux2state_orig(iVar)%state1==iname_watCanopy .and. .not.computeVegFlux) cycle ! use input fluxes in cases where there is no canopy - if (firstInnerStep) flux_data%var(iVar)%dat(:) = 0._rkind - flux_mean%var(iVar)%dat(:) = 0._rkind - end do - - ! initialize derivatives - do iVar=1,size(deriv_meta) - deriv_data%var(iVar)%dat(:) = 0._rkind - end do + ! *** initialize *** + call initialize_opSplittin ! ========================================================================================================================================== ! loop through different coupling strategies @@ -870,26 +796,116 @@ subroutine opSplittin(& end do coupling ! coupling method - ! check that all state variables were updated - if(any(stateCheck==0))then - message=trim(message)//'some state variables were not updated!' - err=20; return - endif + ! *** Finalize *** + call finalize_opSplittin - ! check that the desired fluxes were computed - do iVar=1,size(flux_meta) - if(neededFlux(iVar) .and. any(fluxCount%var(iVar)%dat==0))then - print*, 'fluxCount%var(iVar)%dat = ', fluxCount%var(iVar)%dat - message=trim(message)//'flux '//trim(flux_meta(iVar)%varname)//' was not computed' - err=20; return - endif - end do + ! end associate statements + end associate globalVars - ! use step halving if unable to complete the fully coupled solution in one substep - if(ixCoupling/=fullyCoupled .or. nSubsteps>1) dtMultiplier=0.5_rkind + contains + + subroutine initialize_opSplittin + ! *** initial steps for opSplittin subroutine *** + + ! set the global print flag + globalPrintFlag=.false. + + if (globalPrintFlag) print *, trim(message), dt + + ! initialize the first success call + firstSuccess=.false. + if (.not.firstInnerStep) firstSuccess=.true. + + ! initialize the flags + tooMuchMelt=.false. ! too much melt (merge snow layers) + stepFailure=.false. ! step failure + + ! initialize flag for the success of the substepping + failure=.false. + + ! initialize the flux check + neededFlux(:) = .false. + + ! initialize the state check + stateCheck(:) = 0 + + ! allocate local structures based on the number of snow and soil layers + call allocate_memory + + ! intialize the flux counter + do iVar=1,size(flux_meta) ! loop through fluxes + fluxCount%var(iVar)%dat(:) = 0 + end do + + ! initialize the model fluxes + do iVar=1,size(flux_meta) ! loop through fluxes + if (flux2state_orig(iVar)%state1==integerMissing .and. flux2state_orig(iVar)%state2==integerMissing) cycle ! flux does not depend on state (e.g., input) + if (flux2state_orig(iVar)%state1==iname_watCanopy .and. .not.computeVegFlux) cycle ! use input fluxes in cases where there is no canopy + if (firstInnerStep) flux_data%var(iVar)%dat(:) = 0._rkind + flux_mean%var(iVar)%dat(:) = 0._rkind + end do + + ! initialize derivatives + do iVar=1,size(deriv_meta) + deriv_data%var(iVar)%dat(:) = 0._rkind + end do + end subroutine initialize_opSplittin + + subroutine allocate_memory + ! *** allocate memory for local structures *** + ! allocate space for the flux mask (used to define when fluxes are updated) + call allocLocal(flux_meta(:),fluxMask,nSnow,nSoil,err,cmessage) + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! allocate space for the flux count (used to check that fluxes are only updated once) + call allocLocal(flux_meta(:),fluxCount,nSnow,nSoil,err,cmessage) + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! allocate space for the temporary prognostic variable structure + call allocLocal(prog_meta(:),prog_temp,nSnow,nSoil,err,cmessage) + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! allocate space for the temporary diagnostic variable structure + call allocLocal(diag_meta(:),diag_temp,nSnow,nSoil,err,cmessage) + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! allocate space for the temporary flux variable structure + call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! allocate space for the mean flux variable structure + call allocLocal(flux_meta(:),flux_mean,nSnow,nSoil,err,cmessage) + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! allocate space for the temporary mean flux variable structure + call allocLocal(flux_meta(:),flux_mntemp,nSnow,nSoil,err,cmessage) + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; endif + + ! allocate space for the derivative structure + call allocLocal(deriv_meta(:),deriv_data,nSnow,nSoil,err,cmessage) + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if + end subroutine allocate_memory + + subroutine finalize_opSplittin + ! *** final operations for opSplittin subroutine *** + ! check that all state variables were updated + if (any(stateCheck==0)) then + message=trim(message)//'some state variables were not updated!' + err=20; return + endif + + ! check that the desired fluxes were computed + do iVar=1,size(flux_meta) + if (neededFlux(iVar) .and. any(fluxCount%var(iVar)%dat==0)) then + print*, 'fluxCount%var(iVar)%dat = ', fluxCount%var(iVar)%dat + message=trim(message)//'flux '//trim(flux_meta(iVar)%varname)//' was not computed' + err=20; return + endif + end do - ! end associate statements - end associate globalVars + ! use step halving if unable to complete the fully coupled solution in one substep + if (ixCoupling/=fullyCoupled .or. nSubsteps>1) dtMultiplier=0.5_rkind + end subroutine finalize_opSplittin end subroutine opSplittin From 2e89a20b298f875855ef3b5976df52191b06b33c Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 25 Oct 2023 02:28:50 -0600 Subject: [PATCH 0968/1472] Applied object-oriented methods to simplify intent(in) arguments for varSubstep. --- build/source/dshare/data_types.f90 | 70 ++++++++++++++++++++++++++++++ build/source/engine/opSplittin.f90 | 38 ++++++++-------- build/source/engine/varSubstep.f90 | 65 +++++++++++++-------------- 3 files changed, 118 insertions(+), 55 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 79a1664e5..f1174ce0c 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -573,6 +573,44 @@ MODULE data_types end type out_type_bigAquifer ! ** end bigAquifer + ! ** varSubstep + type, public :: in_type_varSubstep ! derived type for intent(in) arguments in varSubstep call + real(rkind) :: dt ! intent(in): time step (s) + real(rkind) :: dtInit ! intent(in): initial time step (seconds) + real(rkind) :: dt_min ! intent(in): minimum time step (seconds) + real(rkind) :: whole_step ! intent(in): length of whole step for surface drainage and average flux + integer(i4b) :: nSubset ! intent(in): total number of variables in the state subset + logical(lgt) :: doAdjustTemp ! intent(in): flag to indicate if we adjust the temperature + logical(lgt) :: firstSubStep ! intent(in): flag to denote first sub-step + logical(lgt) :: computeVegFlux ! intent(in): flag to denote if computing energy flux over vegetation + logical(lgt) :: scalarSolution ! intent(in): flag to denote computing the scalar solution + integer(i4b) :: iStateSplit ! intent(in): index of the layer in the splitting operation + type(var_flagVec) :: fluxMask ! intent(in): mask for the fluxes used in this given state subset + contains + procedure :: initialize => initialize_in_varSubstep + end type in_type_varSubstep + + type, public :: io_type_varSubstep ! derived type for intent(inout) arguments in varSubstep call + logical(lgt) :: firstFluxCall ! intent(inout) : flag to indicate if we are processing the first flux call + integer(i4b) :: fluxCount ! intent(inout) : number of times fluxes are updated (should equal nsubstep) + integer(i4b) :: ixSaturation ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) + contains + + end type io_type_varSubstep + + type, public :: out_type_varSubstep ! derived type for intent(out) arguments in varSubstep call + real(rkind) :: dtMultiplier ! intent(out): substep multiplier (-) + integer(i4b) :: nSubsteps ! intent(out): number of substeps taken for a given split + logical(lgt) :: failedMinimumStep ! intent(out): flag for failed substeps + logical(lgt) :: reduceCoupledStep ! intent(out): flag to reduce the length of the coupled step + logical(lgt) :: tooMuchMelt ! intent(out): flag to denote that ice is insufficient to support melt + integer(i4b) :: err ! intent(out): error code + character(:),allocatable :: cmessage ! intent(out): error message + contains + + end type out_type_varSubstep + ! ** end varSubstep + contains ! **** vegNrgFlux **** @@ -1208,4 +1246,36 @@ subroutine finalize_out_bigAquifer(out_bigAquifer,flux_data,deriv_data,err,cmess end associate end subroutine finalize_out_bigAquifer ! **** end bigAquifer **** + + ! **** varSubstep **** + subroutine initialize_in_varSubstep(in_varSubstep,dt,dtInit,dt_min,whole_step,nSubset,& + doAdjustTemp,firstSubStep,computeVegFlux,ixSolution,scalar,iStateSplit,fluxMask) + class(in_type_varSubstep),intent(out) :: in_varSubstep ! class object for intent(in) varSubstep arguments + real(rkind),intent(in) :: dt ! time step (s) + real(rkind),intent(in) :: dtInit ! initial time step (s) + real(rkind),intent(in) :: dt_min ! minimum time step (s) + real(rkind),intent(in) :: whole_step ! length of whole step for surface drainage and average flux + integer(i4b),intent(in) :: nSubset ! total number of variables in the state subset + logical(lgt),intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature + logical(lgt),intent(in) :: firstSubStep ! flag to denote first sub-step + logical(lgt),intent(in) :: computeVegFlux ! flag to denote if computing energy flux over vegetation + integer(i4b),intent(in) :: ixSolution ! index of solution method + integer(i4b),intent(in) :: scalar ! scalar solution method + integer(i4b),intent(in) :: iStateSplit ! index of the layer in the splitting operation + type(var_flagVec),intent(in) :: fluxMask ! mask for the fluxes used in this given state subset + + ! intent(in) arguments + in_varSubstep % dt = dt ! intent(in): time step (s) + in_varSubstep % dtInit = dtInit ! intent(in): initial time step (s) + in_varSubstep % dt_min = dt_min ! intent(in): minimum time step (s) + in_varSubstep % whole_step = whole_step ! intent(in): length of whole step for surface drainage and average flux + in_varSubstep % nSubset = nSubset ! intent(in): total number of variables in the state subset + in_varSubstep % doAdjustTemp = doAdjustTemp ! intent(in): flag to indicate if we adjust the temperature + in_varSubstep % firstSubStep = firstSubStep ! intent(in): flag to denote first sub-step + in_varSubstep % computeVegFlux = computeVegFlux ! intent(in): flag to denote if computing energy flux over vegetation + in_varSubstep % scalarSolution = (ixSolution==scalar) ! intent(in): flag to denote computing the scalar solution + in_varSubstep % iStateSplit = iStateSplit ! intent(in): index of the layer in the splitting operation + in_varSubstep % fluxMask = fluxMask ! intent(in): mask for the fluxes used in this given state subset + end subroutine initialize_in_varSubstep + ! **** end varSubstep **** END MODULE data_types diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 61c95f83c..3a589e223 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -88,14 +88,14 @@ module opSplittin_module ! provide access to the derived types to define the data structures USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_flagVec, & ! data vector with variable length dimension (i4b) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! lookup tables - model_options ! defines the model decisions - + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_flagVec, & ! data vector with variable length dimension (i4b) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! lookup tables + model_options, & ! defines the model decisions + in_type_varSubstep ! class for varSubstep objects ! look-up values for the numerical method USE mDecisions_module,only: & @@ -289,6 +289,10 @@ subroutine opSplittin(& real(rkind) :: mean_step_state ! mean step over the state (with or without domain splits) real(rkind) :: mean_step_solution ! mean step for a solution (scalar or vector) logical(lgt) :: addFirstFlux ! flag to add the first flux to the mask + ! ---------------------- classes for flux subroutine arguments (classes defined in data_types module) ---------------------- + ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** + type(in_type_varSubstep) :: in_varSubstep; ! varSubstep arguments + ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- @@ -647,20 +651,11 @@ subroutine opSplittin(& if(ixSolution==scalar) numberScalarSolutions = numberScalarSolutions + 1 ! solve variable subset for one full time steps + call initialize_varSubstep call varSubstep(& ! input: model control - dt, & ! intent(in) : time step (s) - dtInit, & ! intent(in) : initial time step (seconds) - dt_min, & ! intent(in) : minimum time step (seconds) - whole_step, & ! intent(in) : length of whole step for surface drainage and average flux - nSubset, & ! intent(in) : total number of variables in the state subset - doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature - firstSubStep, & ! intent(in) : flag to denote first sub-step + in_varSubstep, & ! intent(in) : model control firstFluxCall, & ! intent(inout) : flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in) : flag to denote if computing energy flux over vegetation - (ixSolution==scalar), & ! intent(in) : flag to denote computing the scalar solution - iStateSplit, & ! intent(in) : index of the layer in the splitting operation - fluxMask, & ! intent(in) : mask for the fluxes used in this given state subset fluxCount, & ! intent(inout) : number of times fluxes are updated (should equal nsubstep) ! input/output: data structures model_decisions, & ! intent(in) : model decisions @@ -907,6 +902,11 @@ subroutine finalize_opSplittin if (ixCoupling/=fullyCoupled .or. nSubsteps>1) dtMultiplier=0.5_rkind end subroutine finalize_opSplittin + ! **** varSubstep **** + subroutine initialize_varSubstep + call in_varSubstep%initialize(dt,dtInit,dt_min,whole_step,nSubset,doAdjustTemp,firstSubStep,computeVegFlux,ixSolution,scalar,iStateSplit,fluxMask) + end subroutine initialize_varSubstep + end subroutine opSplittin diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 6041ef6ee..144c53534 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -42,13 +42,14 @@ module varSubstep_module ! derived types to define the data structures USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_flagVec, & ! data vector with variable length dimension (i4b) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! lookup tables - model_options ! defines the model decisions + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_flagVec, & ! data vector with variable length dimension (i4b) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! lookup tables + model_options, & ! defines the model decisions + in_type_varSubstep ! class for intent(in) arguments ! provide access to indices that define elements of the data structures USE var_lookup,only:iLookFLUX ! named variables for structure elements @@ -92,20 +93,10 @@ module varSubstep_module ! ********************************************************************************************************** subroutine varSubstep(& ! input: model control - dt, & ! intent(in) : time step (s) - dtInit, & ! intent(in) : initial time step (seconds) - dt_min, & ! intent(in) : minimum time step (seconds) - whole_step, & ! intent(in) : length of whole step for surface drainage and average flux - nState, & ! intent(in) : total number of state variables - doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature - firstSubStep, & ! intent(in) : flag to denote first sub-step + in_varSubstep, & ! intent(in) : model control firstFluxCall, & ! intent(inout) : flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in) : flag to denote if computing energy flux over vegetation - scalarSolution, & ! intent(in) : flag to denote implementing the scalar solution - iStateSplit, & ! intent(in) : index of the state in the splitting operation - fluxMask, & ! intent(in) : mask for the fluxes used in this given state subset fluxCount, & ! intent(inout) : number of times that fluxes are updated (should equal nSubsteps) - ! input/output: data structures + ! input/output: data structures model_decisions, & ! intent(in) : model decisions lookup_data, & ! intent(in) : lookup tables type_data, & ! intent(in) : type of vegetation and soil @@ -140,19 +131,9 @@ subroutine varSubstep(& ! --------------------------------------------------------------------------------------- ! * dummy variables ! --------------------------------------------------------------------------------------- + type(in_type_varSubstep),intent(in) :: in_varSubstep ! model control ! input: model control - real(rkind),intent(in) :: dt ! time step (seconds) - real(rkind),intent(in) :: dtInit ! initial time step (seconds) - real(rkind),intent(in) :: dt_min ! minimum time step (seconds) - real(rkind),intent(in) :: whole_step ! length of whole step for surface drainage and average flux - integer(i4b),intent(in) :: nState ! total number of state variables - logical(lgt),intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) - logical(lgt),intent(in) :: scalarSolution ! flag to denote implementing the scalar solution - integer(i4b),intent(in) :: iStateSplit ! index of the state in the splitting operation - type(var_flagVec),intent(in) :: fluxMask ! flags to denote if the flux is calculated in the given state subset type(var_ilength),intent(inout) :: fluxCount ! number of times that the flux is updated (should equal nSubsteps) ! input/output: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions @@ -200,12 +181,12 @@ subroutine varSubstep(& integer(i4b),parameter :: n_dec=15 ! maximum number of iterations to decrease time step real(rkind),parameter :: F_inc = 1.25_rkind ! factor used to increase time step real(rkind),parameter :: F_dec = 0.90_rkind ! factor used to decrease time step - ! state and flux vectors - real(rkind) :: untappedMelt(nState) ! un-tapped melt energy (J m-3 s-1) - real(rkind) :: stateVecInit(nState) ! initial state vector (mixed units) - real(rkind) :: stateVecTrial(nState) ! trial state vector (mixed units) - real(rkind) :: stateVecPrime(nState) ! trial state vector (mixed units) - type(var_dlength) :: flux_temp ! temporary model fluxes + ! state and flux vectors (Note: nstate = in_varSubstep % nSubset) + real(rkind) :: untappedMelt(in_varSubstep % nSubset) ! un-tapped melt energy (J m-3 s-1) + real(rkind) :: stateVecInit(in_varSubstep % nSubset) ! initial state vector (mixed units) + real(rkind) :: stateVecTrial(in_varSubstep % nSubset) ! trial state vector (mixed units) + real(rkind) :: stateVecPrime(in_varSubstep % nSubset) ! trial state vector (mixed units) + type(var_dlength) :: flux_temp ! temporary model fluxes ! flags logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt) :: checkMassBalance ! flag to check the mass balance @@ -222,6 +203,18 @@ subroutine varSubstep(& ! point to variables in the data structures ! --------------------------------------------------------------------------------------- globalVars: associate(& + ! input: model control + dt => in_varSubstep % dt, & ! intent(in): time step (seconds) + dtInit => in_varSubstep % dtInit, & ! intent(in): initial time step (seconds) + dt_min => in_varSubstep % dt_min, & ! intent(in): minimum time step (seconds) + whole_step => in_varSubstep % whole_step, & ! intent(in): length of whole step for surface drainage and average flux + nState => in_varSubstep % nSubset, & ! intent(in): total number of state variables + doAdjustTemp => in_varSubstep % doAdjustTemp, & ! intent(in): flag to indicate if we adjust the temperature + firstSubStep => in_varSubstep % firstSubStep, & ! intent(in): flag to indicate if processing the first sub-step + computeVegFlux => in_varSubstep % computeVegFlux, & ! intent(in): flag to indicate if computing fluxes over vegetation (.false. means veg is buried with snow) + scalarSolution => in_varSubstep % scalarSolution, & ! intent(in): flag to denote implementing the scalar solution + iStateSplit => in_varSubstep % iStateSplit, & ! intent(in): index of the state in the splitting operation + fluxMask => in_varSubstep % fluxMask, & ! intent(in): flags to denote if the flux is calculated in the given state subset ! model decisions ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver ! number of layers From 739a3802c3c3f4c528ecbd792fb90d60f486e572 Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Wed, 25 Oct 2023 15:48:49 -0600 Subject: [PATCH 0969/1472] Update files to correlate with Summa-Actors changes: Summa-Actors data types are now contained in its files. The data_types.f90 can now be used directly in compiling Summa-Actors. Summa-Actors uses a V4_ACTIVE compiler flag to remain backward compatible to V3. Adjusted unneeded uses of the ACTORS_ACTIVE compiler flag --- build/cmake/CMakeLists.txt | 89 ++++++++++++++---------------- build/source/dshare/globalData.f90 | 15 ----- build/source/engine/coupled_em.f90 | 6 +- 3 files changed, 42 insertions(+), 68 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 7bd2d2691..3c5e4fda7 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -72,7 +72,7 @@ else() if(CMAKE_BUILD_TYPE MATCHES Actors) message("\nUsing Actors Framework, should have been installed previously") add_compile_definitions(ACTORS_ACTIVE) - + add_compile_definitions(V4_ACTIVE) project(summaactors DESCRIPTION "Summa-Sundials-BE Actors") include(FortranCInterface) @@ -158,7 +158,7 @@ if(CMAKE_BUILD_TYPE MATCHES Cluster) if(CMAKE_BUILD_TYPE MATCHES Actors) find_package(CAF REQUIRED) - set(INC_ACTORS ${CAF_INCLUDES} ${PARENT_DIR}/build/includes/global ${PARENT_DIR}/build/includes/summa_actor ${PARENT_DIR}/build/includes/gru_actor ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) + set(INC_ACTORS ${CAF_INCLUDES} ${PARENT_DIR}/build/includes/global ${PARENT_DIR}/build/includes/summa_actor ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) set(LIB_ACTORS ${CAF_LIBRARIES} -lcaf_core -lcaf_io) endif() @@ -206,7 +206,12 @@ else() message(FATAL_ERROR "Did not find Actors directory, edit CMakeLists.txt to add path") endif() link_directories(${DIR_ACTORS}/lib) - set(INC_ACTORS ${DIR_ACTORS}/include ${PARENT_DIR}/build/includes/global ${PARENT_DIR}/build/includes/summa_actor ${PARENT_DIR}/build/includes/gru_actor ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) + set(INC_ACTORS ${DIR_ACTORS}/include + ${PARENT_DIR}/build/includes/global + ${PARENT_DIR}/build/includes/summa_actor + ${PARENT_DIR}/build/includes/job_actor + ${PARENT_DIR}/build/includes/file_access_actor + ${PARENT_DIR}/build/includes/hru_actor) set(LIB_ACTORS ${DIR_ACTORS}/lib -lcaf_core -lcaf_io) endif() @@ -238,21 +243,20 @@ set(HOOKUP_DIR ${F_MASTER}/build/source/hookup) set(NETCDF_DIR ${F_MASTER}/build/source/netcdf) set(NOAHMP_DIR ${F_MASTER}/build/source/noah-mp) -# Define directories for source files that might be replaced by actors (identical if not using actors) -set(SUB_DRIVER_DIR ${PARENT_DIR}/build/source/driver) -set(SUB_DSHARE_DIR ${PARENT_DIR}/build/source/dshare) -set(SUB_ENGINE_DIR ${PARENT_DIR}/build/source/engine) -set(SUB_HOOKUP_DIR ${PARENT_DIR}/build/source/hookup) -set(SUB_NETCDF_DIR ${PARENT_DIR}/build/source/netcdf) - # Define Actors specific directories set(ACTORS_DIR ${PARENT_DIR}/build/source/actors) set(FILE_ACCESS_DIR ${ACTORS_DIR}/file_access_actor) set(JOB_ACTOR_DIR ${ACTORS_DIR}/job_actor) set(HRU_ACTOR_DIR ${ACTORS_DIR}/hru_actor) -set(GRU_ACTOR_DIR ${ACTORS_DIR}/gru_actor) - +if(CMAKE_BUILD_TYPE MATCHES Actors) + # Define directories for source files that might be replaced by actors (identical if not using actors) + set(SUB_ENGINE_DIR ${FILE_ACCESS_DIR}) + set(SUB_NETCDF_DIR ${FILE_ACCESS_DIR}) +else() + set(SUB_ENGINE_DIR ${PARENT_DIR}/build/source/engine) + set(SUB_NETCDF_DIR ${PARENT_DIR}/build/source/netcdf) +endif() #========================================================================================= # COMPILE PART 2: Assemble all of the SUMMA sub-routines #========================================================================================= @@ -282,7 +286,7 @@ set(HOOKUP # Data modules set(DATAMS - ${SUB_DSHARE_DIR}/data_types.f90 + ${DSHARE_DIR}/data_types.f90 ${DSHARE_DIR}/flxMapping.f90 ${DSHARE_DIR}/get_ixname.f90 ${DSHARE_DIR}/globalData.f90 @@ -293,6 +297,8 @@ set(DATAMS set(DATAMS_SUNDIALS ${DSHARE_DIR}/type4ida.f90 ${DSHARE_DIR}/type4kinsol.f90) +set(DATAMS_ACTORS + ${ACTORS_DIR}/global/actor_data_types.f90) # Utility modules set(UTILMS @@ -309,7 +315,7 @@ set(UTILMS_SUNDIALS # NetCDF routines set(NETCDF ${SUB_NETCDF_DIR}/def_output.f90 - ${SUB_NETCDF_DIR}/modelwrite.f90 + ${NETCDF_DIR}/modelwrite.f90 ${NETCDF_DIR}/netcdf_util.f90 ${NETCDF_DIR}/read_icond.f90) @@ -410,23 +416,17 @@ set(INTERFACE ${ACTORS_DIR}/global/cppwrap_datatypes.f90 ${ACTORS_DIR}/global/cppwrap_metadata.f90) set(FILE_ACCESS_INTERFACE - ${FILE_ACCESS_DIR}/fortran_code/cppwrap_fileAccess.f90 - ${FILE_ACCESS_DIR}/fortran_code/output_structure.f90 - ${FILE_ACCESS_DIR}/fortran_code/read_force.f90 - ${FILE_ACCESS_DIR}/fortran_code/write_to_netcdf.f90 - ${FILE_ACCESS_DIR}/fortran_code/writeOutputFromOutputStructure.f90) + ${FILE_ACCESS_DIR}/cppwrap_fileAccess.f90 + ${FILE_ACCESS_DIR}/output_structure.f90 + ${FILE_ACCESS_DIR}/read_force.f90 + ${FILE_ACCESS_DIR}/fileAccess_writeOutput.f90) set(JOB_INTERFACE ${JOB_ACTOR_DIR}/job_actor.f90) -set(GRU_INTERFACE - ${GRU_ACTOR_DIR}/gru_actor.f90) set(HRU_INTERFACE - ${HRU_ACTOR_DIR}/fortran_code/hru_actor.f90 - ${HRU_ACTOR_DIR}/fortran_code/hru_init.f90 - ${HRU_ACTOR_DIR}/fortran_code/hru_modelRun.f90 - ${HRU_ACTOR_DIR}/fortran_code/hru_modelwrite.f90 - ${HRU_ACTOR_DIR}/fortran_code/hru_restart.f90 - ${HRU_ACTOR_DIR}/fortran_code/hru_setup.f90 - ${HRU_ACTOR_DIR}/fortran_code/hru_writeOutput.f90) + ${HRU_ACTOR_DIR}/hru_init.f90 + ${HRU_ACTOR_DIR}/hru_read.f90 + ${HRU_ACTOR_DIR}/hru_modelRun.f90 + ${HRU_ACTOR_DIR}/hru_writeOutput.f90) # Actors actual actor modules set(ACTORS_GLOBAL @@ -436,25 +436,23 @@ set(ACTORS_GLOBAL ${ACTORS_DIR}/global/settings_functions.cpp ${ACTORS_DIR}/global/timing_info.cpp) set(SUMMA_ACTOR - ${ACTORS_DIR}/summa_actor/batch/batch.cpp - ${ACTORS_DIR}/summa_actor/batch/batch_container.cpp - ${ACTORS_DIR}/summa_actor/client/client.cpp - ${ACTORS_DIR}/summa_actor/client/client_container.cpp + ${ACTORS_DIR}/summa_actor/batch.cpp + ${ACTORS_DIR}/summa_actor/batch_container.cpp + ${ACTORS_DIR}/summa_actor/client.cpp + ${ACTORS_DIR}/summa_actor/client_container.cpp ${ACTORS_DIR}/summa_actor/summa_actor.cpp ${ACTORS_DIR}/summa_actor/summa_backup_server.cpp ${ACTORS_DIR}/summa_actor/summa_client.cpp ${ACTORS_DIR}/summa_actor/summa_server.cpp) set(FILE_ACCESS_ACTOR - ${ACTORS_DIR}/file_access_actor/cpp_code/file_access_actor.cpp - ${ACTORS_DIR}/file_access_actor/cpp_code/forcing_file_info.cpp - ${ACTORS_DIR}/file_access_actor/cpp_code/output_container.cpp) + ${ACTORS_DIR}/file_access_actor/file_access_actor.cpp + ${ACTORS_DIR}/file_access_actor/forcing_file_info.cpp + ${ACTORS_DIR}/file_access_actor/output_container.cpp) set(JOB_ACTOR ${ACTORS_DIR}/job_actor/GRU.cpp ${ACTORS_DIR}/job_actor/job_actor.cpp) -set(GRU_ACTOR - ${ACTORS_DIR}/gru_actor/gru_actor.cpp) set(HRU_ACTOR - ${ACTORS_DIR}/hru_actor/cpp_code/hru_actor.cpp) + ${ACTORS_DIR}/hru_actor/hru_actor.cpp) #========================================================================================= @@ -467,13 +465,10 @@ set(COMM_ALL ${DATAMS} ${UTILMS}) if(CMAKE_BUILD_TYPE MATCHES Actors) - set(COMM_ALL ${COMM_ALL} - ${INTERFACE}) + set(COMM_ALL ${COMM_ALL} ${DATAMS_ACTORS} ${INTERFACE}) endif() if(CMAKE_BUILD_TYPE MATCHES Sundials) - set(COMM_ALL ${COMM_ALL} - ${DATAMS_SUNDIALS} - ${UTILMS_SUNDIALS}) + set(COMM_ALL ${COMM_ALL} ${DATAMS_SUNDIALS} ${UTILMS_SUNDIALS}) endif() set(SUMMA_ALL @@ -485,10 +480,9 @@ set(SUMMA_ALL if(CMAKE_BUILD_TYPE MATCHES Actors) set(SUMMA_ALL ${SUMMA_ALL} - ${FILE_ACCESS_INTERFACE} - ${JOB_INTERFACE} - ${GRU_INTERFACE} - ${HRU_INTERFACE}) + ${FILE_ACCESS_INTERFACE} + ${JOB_INTERFACE} + ${HRU_INTERFACE}) else() set(SUMMA_ALL ${SUMMA_ALL} ${PRELIM_NOT_ACTORS} @@ -582,7 +576,6 @@ elseif(CMAKE_BUILD_TYPE MATCHES Actors) ${ACTORS_GLOBAL} ${FILE_ACCESS_ACTOR} ${JOB_ACTOR} - ${GRU_ACTOR} ${HRU_ACTOR} ${SUMMA_ACTOR} ${SUMMA_CLIENT} diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 index 798aac4be..6206d2bc7 100644 --- a/build/source/dshare/globalData.f90 +++ b/build/source/dshare/globalData.f90 @@ -37,12 +37,6 @@ MODULE globalData USE data_types,only:extended_info ! extended metadata for variables in each model structure USE data_types,only:struct_info ! summary information on all data structures USE data_types,only:var_i ! vector of integers -#ifdef ACTORS_ACTIVE - USE data_types,only:var_forc ! for Actors - USE data_types,only:dlength ! for Actors - USE data_types,only:ilength ! for Actors - USE data_types,only:init_cond ! for Actors -#endif ! number of variables in each data structure USE var_lookup,only:maxvarTime ! time: maximum number variables USE var_lookup,only:maxvarForc ! forcing data: maximum number variables @@ -264,14 +258,6 @@ MODULE globalData integer(i4b),parameter,public :: ncTime=1 ! time zone information from NetCDF file (timeOffset = longitude/15. - ncTimeOffset) integer(i4b),parameter,public :: utcTime=2 ! all times in UTC (timeOffset = longitude/15. hours) integer(i4b),parameter,public :: localTime=3 ! all times local (timeOffset = 0) -#ifdef ACTORS_ACTIVE - ! global data structures are managed by FileAccessActor - type(var_forc),allocatable,save,public :: forcingDataStruct(:) ! forcingDataStruct(:)%var(:)%dataFromFile(:,:) - type(dlength),allocatable,save,public :: vecTime(:) - logical(lgt),allocatable,save,public :: failedHRUs(:) ! list of true and false values to indicate if an HRU has failed - type(ilength),allocatable,save,public :: outputTimeStep(:) ! timestep in output files - ! inital conditions for Actors -#else ! define metadata for model forcing datafile non-Actors type(file_info),save,public,allocatable :: forcFileInfo(:) ! file info for model forcing data ! define indices in the forcing data files non-Actors @@ -286,7 +272,6 @@ MODULE globalData real(rkind),save,public :: fracJulDay ! fractional julian days since the start of year real(rkind),save,public :: tmZoneOffsetFracDay ! time zone offset in fractional days integer(i4b),save,public :: yearLength ! number of days in the current year -#endif ! define fixed dimensions integer(i4b),parameter,public :: nBand=2 ! number of spectral bands integer(i4b),parameter,public :: nTimeDelay=2000 ! number of time steps in the time delay histogram (default: ~1 season = 24*365/4) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index f10eaefc8..d3f6b5622 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -147,12 +147,8 @@ subroutine coupled_em(& USE computSnowDepth_module,only:computSnowDepth implicit none - ! model control -#ifdef ACTORS_ACTIVE - integer(4),intent(in) :: hruId ! hruId -#else + integer(8),intent(in) :: hruId ! hruId -#endif real(rkind),intent(inout) :: dt_init ! used to initialize the size of the sub-step integer(i4b),intent(in) :: dt_init_factor ! Used to adjust the length of the timestep in the event of a failure logical(lgt),intent(inout) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) From 86766c8ec30cfbbd84ea41eb9f1b33872896a976 Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Wed, 25 Oct 2023 16:38:58 -0600 Subject: [PATCH 0970/1472] Summa-Actors now uses the same def_output.f90 file as SUMMA --- build/cmake/CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 3c5e4fda7..04f2fbb38 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -252,10 +252,8 @@ set(HRU_ACTOR_DIR ${ACTORS_DIR}/hru_actor) if(CMAKE_BUILD_TYPE MATCHES Actors) # Define directories for source files that might be replaced by actors (identical if not using actors) set(SUB_ENGINE_DIR ${FILE_ACCESS_DIR}) - set(SUB_NETCDF_DIR ${FILE_ACCESS_DIR}) else() set(SUB_ENGINE_DIR ${PARENT_DIR}/build/source/engine) - set(SUB_NETCDF_DIR ${PARENT_DIR}/build/source/netcdf) endif() #========================================================================================= # COMPILE PART 2: Assemble all of the SUMMA sub-routines @@ -314,7 +312,7 @@ set(UTILMS_SUNDIALS # NetCDF routines set(NETCDF - ${SUB_NETCDF_DIR}/def_output.f90 + ${NETCDF_DIR}/def_output.f90 ${NETCDF_DIR}/modelwrite.f90 ${NETCDF_DIR}/netcdf_util.f90 ${NETCDF_DIR}/read_icond.f90) From edb392f3e0038c6a764af368c0e99677dfe65216 Mon Sep 17 00:00:00 2001 From: Kyle Klenk Date: Wed, 25 Oct 2023 16:40:51 -0600 Subject: [PATCH 0971/1472] Revert "Changes for Summa-Actors updates" --- build/cmake/CMakeLists.txt | 89 ++++++++++++++++-------------- build/source/dshare/globalData.f90 | 15 +++++ build/source/engine/coupled_em.f90 | 6 +- docs/whats-new.md | 2 +- 4 files changed, 69 insertions(+), 43 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 3c5e4fda7..7bd2d2691 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -72,7 +72,7 @@ else() if(CMAKE_BUILD_TYPE MATCHES Actors) message("\nUsing Actors Framework, should have been installed previously") add_compile_definitions(ACTORS_ACTIVE) - add_compile_definitions(V4_ACTIVE) + project(summaactors DESCRIPTION "Summa-Sundials-BE Actors") include(FortranCInterface) @@ -158,7 +158,7 @@ if(CMAKE_BUILD_TYPE MATCHES Cluster) if(CMAKE_BUILD_TYPE MATCHES Actors) find_package(CAF REQUIRED) - set(INC_ACTORS ${CAF_INCLUDES} ${PARENT_DIR}/build/includes/global ${PARENT_DIR}/build/includes/summa_actor ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) + set(INC_ACTORS ${CAF_INCLUDES} ${PARENT_DIR}/build/includes/global ${PARENT_DIR}/build/includes/summa_actor ${PARENT_DIR}/build/includes/gru_actor ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) set(LIB_ACTORS ${CAF_LIBRARIES} -lcaf_core -lcaf_io) endif() @@ -206,12 +206,7 @@ else() message(FATAL_ERROR "Did not find Actors directory, edit CMakeLists.txt to add path") endif() link_directories(${DIR_ACTORS}/lib) - set(INC_ACTORS ${DIR_ACTORS}/include - ${PARENT_DIR}/build/includes/global - ${PARENT_DIR}/build/includes/summa_actor - ${PARENT_DIR}/build/includes/job_actor - ${PARENT_DIR}/build/includes/file_access_actor - ${PARENT_DIR}/build/includes/hru_actor) + set(INC_ACTORS ${DIR_ACTORS}/include ${PARENT_DIR}/build/includes/global ${PARENT_DIR}/build/includes/summa_actor ${PARENT_DIR}/build/includes/gru_actor ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) set(LIB_ACTORS ${DIR_ACTORS}/lib -lcaf_core -lcaf_io) endif() @@ -243,20 +238,21 @@ set(HOOKUP_DIR ${F_MASTER}/build/source/hookup) set(NETCDF_DIR ${F_MASTER}/build/source/netcdf) set(NOAHMP_DIR ${F_MASTER}/build/source/noah-mp) +# Define directories for source files that might be replaced by actors (identical if not using actors) +set(SUB_DRIVER_DIR ${PARENT_DIR}/build/source/driver) +set(SUB_DSHARE_DIR ${PARENT_DIR}/build/source/dshare) +set(SUB_ENGINE_DIR ${PARENT_DIR}/build/source/engine) +set(SUB_HOOKUP_DIR ${PARENT_DIR}/build/source/hookup) +set(SUB_NETCDF_DIR ${PARENT_DIR}/build/source/netcdf) + # Define Actors specific directories set(ACTORS_DIR ${PARENT_DIR}/build/source/actors) set(FILE_ACCESS_DIR ${ACTORS_DIR}/file_access_actor) set(JOB_ACTOR_DIR ${ACTORS_DIR}/job_actor) set(HRU_ACTOR_DIR ${ACTORS_DIR}/hru_actor) +set(GRU_ACTOR_DIR ${ACTORS_DIR}/gru_actor) + -if(CMAKE_BUILD_TYPE MATCHES Actors) - # Define directories for source files that might be replaced by actors (identical if not using actors) - set(SUB_ENGINE_DIR ${FILE_ACCESS_DIR}) - set(SUB_NETCDF_DIR ${FILE_ACCESS_DIR}) -else() - set(SUB_ENGINE_DIR ${PARENT_DIR}/build/source/engine) - set(SUB_NETCDF_DIR ${PARENT_DIR}/build/source/netcdf) -endif() #========================================================================================= # COMPILE PART 2: Assemble all of the SUMMA sub-routines #========================================================================================= @@ -286,7 +282,7 @@ set(HOOKUP # Data modules set(DATAMS - ${DSHARE_DIR}/data_types.f90 + ${SUB_DSHARE_DIR}/data_types.f90 ${DSHARE_DIR}/flxMapping.f90 ${DSHARE_DIR}/get_ixname.f90 ${DSHARE_DIR}/globalData.f90 @@ -297,8 +293,6 @@ set(DATAMS set(DATAMS_SUNDIALS ${DSHARE_DIR}/type4ida.f90 ${DSHARE_DIR}/type4kinsol.f90) -set(DATAMS_ACTORS - ${ACTORS_DIR}/global/actor_data_types.f90) # Utility modules set(UTILMS @@ -315,7 +309,7 @@ set(UTILMS_SUNDIALS # NetCDF routines set(NETCDF ${SUB_NETCDF_DIR}/def_output.f90 - ${NETCDF_DIR}/modelwrite.f90 + ${SUB_NETCDF_DIR}/modelwrite.f90 ${NETCDF_DIR}/netcdf_util.f90 ${NETCDF_DIR}/read_icond.f90) @@ -416,17 +410,23 @@ set(INTERFACE ${ACTORS_DIR}/global/cppwrap_datatypes.f90 ${ACTORS_DIR}/global/cppwrap_metadata.f90) set(FILE_ACCESS_INTERFACE - ${FILE_ACCESS_DIR}/cppwrap_fileAccess.f90 - ${FILE_ACCESS_DIR}/output_structure.f90 - ${FILE_ACCESS_DIR}/read_force.f90 - ${FILE_ACCESS_DIR}/fileAccess_writeOutput.f90) + ${FILE_ACCESS_DIR}/fortran_code/cppwrap_fileAccess.f90 + ${FILE_ACCESS_DIR}/fortran_code/output_structure.f90 + ${FILE_ACCESS_DIR}/fortran_code/read_force.f90 + ${FILE_ACCESS_DIR}/fortran_code/write_to_netcdf.f90 + ${FILE_ACCESS_DIR}/fortran_code/writeOutputFromOutputStructure.f90) set(JOB_INTERFACE ${JOB_ACTOR_DIR}/job_actor.f90) +set(GRU_INTERFACE + ${GRU_ACTOR_DIR}/gru_actor.f90) set(HRU_INTERFACE - ${HRU_ACTOR_DIR}/hru_init.f90 - ${HRU_ACTOR_DIR}/hru_read.f90 - ${HRU_ACTOR_DIR}/hru_modelRun.f90 - ${HRU_ACTOR_DIR}/hru_writeOutput.f90) + ${HRU_ACTOR_DIR}/fortran_code/hru_actor.f90 + ${HRU_ACTOR_DIR}/fortran_code/hru_init.f90 + ${HRU_ACTOR_DIR}/fortran_code/hru_modelRun.f90 + ${HRU_ACTOR_DIR}/fortran_code/hru_modelwrite.f90 + ${HRU_ACTOR_DIR}/fortran_code/hru_restart.f90 + ${HRU_ACTOR_DIR}/fortran_code/hru_setup.f90 + ${HRU_ACTOR_DIR}/fortran_code/hru_writeOutput.f90) # Actors actual actor modules set(ACTORS_GLOBAL @@ -436,23 +436,25 @@ set(ACTORS_GLOBAL ${ACTORS_DIR}/global/settings_functions.cpp ${ACTORS_DIR}/global/timing_info.cpp) set(SUMMA_ACTOR - ${ACTORS_DIR}/summa_actor/batch.cpp - ${ACTORS_DIR}/summa_actor/batch_container.cpp - ${ACTORS_DIR}/summa_actor/client.cpp - ${ACTORS_DIR}/summa_actor/client_container.cpp + ${ACTORS_DIR}/summa_actor/batch/batch.cpp + ${ACTORS_DIR}/summa_actor/batch/batch_container.cpp + ${ACTORS_DIR}/summa_actor/client/client.cpp + ${ACTORS_DIR}/summa_actor/client/client_container.cpp ${ACTORS_DIR}/summa_actor/summa_actor.cpp ${ACTORS_DIR}/summa_actor/summa_backup_server.cpp ${ACTORS_DIR}/summa_actor/summa_client.cpp ${ACTORS_DIR}/summa_actor/summa_server.cpp) set(FILE_ACCESS_ACTOR - ${ACTORS_DIR}/file_access_actor/file_access_actor.cpp - ${ACTORS_DIR}/file_access_actor/forcing_file_info.cpp - ${ACTORS_DIR}/file_access_actor/output_container.cpp) + ${ACTORS_DIR}/file_access_actor/cpp_code/file_access_actor.cpp + ${ACTORS_DIR}/file_access_actor/cpp_code/forcing_file_info.cpp + ${ACTORS_DIR}/file_access_actor/cpp_code/output_container.cpp) set(JOB_ACTOR ${ACTORS_DIR}/job_actor/GRU.cpp ${ACTORS_DIR}/job_actor/job_actor.cpp) +set(GRU_ACTOR + ${ACTORS_DIR}/gru_actor/gru_actor.cpp) set(HRU_ACTOR - ${ACTORS_DIR}/hru_actor/hru_actor.cpp) + ${ACTORS_DIR}/hru_actor/cpp_code/hru_actor.cpp) #========================================================================================= @@ -465,10 +467,13 @@ set(COMM_ALL ${DATAMS} ${UTILMS}) if(CMAKE_BUILD_TYPE MATCHES Actors) - set(COMM_ALL ${COMM_ALL} ${DATAMS_ACTORS} ${INTERFACE}) + set(COMM_ALL ${COMM_ALL} + ${INTERFACE}) endif() if(CMAKE_BUILD_TYPE MATCHES Sundials) - set(COMM_ALL ${COMM_ALL} ${DATAMS_SUNDIALS} ${UTILMS_SUNDIALS}) + set(COMM_ALL ${COMM_ALL} + ${DATAMS_SUNDIALS} + ${UTILMS_SUNDIALS}) endif() set(SUMMA_ALL @@ -480,9 +485,10 @@ set(SUMMA_ALL if(CMAKE_BUILD_TYPE MATCHES Actors) set(SUMMA_ALL ${SUMMA_ALL} - ${FILE_ACCESS_INTERFACE} - ${JOB_INTERFACE} - ${HRU_INTERFACE}) + ${FILE_ACCESS_INTERFACE} + ${JOB_INTERFACE} + ${GRU_INTERFACE} + ${HRU_INTERFACE}) else() set(SUMMA_ALL ${SUMMA_ALL} ${PRELIM_NOT_ACTORS} @@ -576,6 +582,7 @@ elseif(CMAKE_BUILD_TYPE MATCHES Actors) ${ACTORS_GLOBAL} ${FILE_ACCESS_ACTOR} ${JOB_ACTOR} + ${GRU_ACTOR} ${HRU_ACTOR} ${SUMMA_ACTOR} ${SUMMA_CLIENT} diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 index 6206d2bc7..798aac4be 100644 --- a/build/source/dshare/globalData.f90 +++ b/build/source/dshare/globalData.f90 @@ -37,6 +37,12 @@ MODULE globalData USE data_types,only:extended_info ! extended metadata for variables in each model structure USE data_types,only:struct_info ! summary information on all data structures USE data_types,only:var_i ! vector of integers +#ifdef ACTORS_ACTIVE + USE data_types,only:var_forc ! for Actors + USE data_types,only:dlength ! for Actors + USE data_types,only:ilength ! for Actors + USE data_types,only:init_cond ! for Actors +#endif ! number of variables in each data structure USE var_lookup,only:maxvarTime ! time: maximum number variables USE var_lookup,only:maxvarForc ! forcing data: maximum number variables @@ -258,6 +264,14 @@ MODULE globalData integer(i4b),parameter,public :: ncTime=1 ! time zone information from NetCDF file (timeOffset = longitude/15. - ncTimeOffset) integer(i4b),parameter,public :: utcTime=2 ! all times in UTC (timeOffset = longitude/15. hours) integer(i4b),parameter,public :: localTime=3 ! all times local (timeOffset = 0) +#ifdef ACTORS_ACTIVE + ! global data structures are managed by FileAccessActor + type(var_forc),allocatable,save,public :: forcingDataStruct(:) ! forcingDataStruct(:)%var(:)%dataFromFile(:,:) + type(dlength),allocatable,save,public :: vecTime(:) + logical(lgt),allocatable,save,public :: failedHRUs(:) ! list of true and false values to indicate if an HRU has failed + type(ilength),allocatable,save,public :: outputTimeStep(:) ! timestep in output files + ! inital conditions for Actors +#else ! define metadata for model forcing datafile non-Actors type(file_info),save,public,allocatable :: forcFileInfo(:) ! file info for model forcing data ! define indices in the forcing data files non-Actors @@ -272,6 +286,7 @@ MODULE globalData real(rkind),save,public :: fracJulDay ! fractional julian days since the start of year real(rkind),save,public :: tmZoneOffsetFracDay ! time zone offset in fractional days integer(i4b),save,public :: yearLength ! number of days in the current year +#endif ! define fixed dimensions integer(i4b),parameter,public :: nBand=2 ! number of spectral bands integer(i4b),parameter,public :: nTimeDelay=2000 ! number of time steps in the time delay histogram (default: ~1 season = 24*365/4) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index d3f6b5622..f10eaefc8 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -147,8 +147,12 @@ subroutine coupled_em(& USE computSnowDepth_module,only:computSnowDepth implicit none - + ! model control +#ifdef ACTORS_ACTIVE + integer(4),intent(in) :: hruId ! hruId +#else integer(8),intent(in) :: hruId ! hruId +#endif real(rkind),intent(inout) :: dt_init ! used to initialize the size of the sub-step integer(i4b),intent(in) :: dt_init_factor ! Used to adjust the length of the timestep in the event of a failure logical(lgt),intent(inout) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) diff --git a/docs/whats-new.md b/docs/whats-new.md index 9ac160bcc..3221d4445 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -10,7 +10,7 @@ This page provides simple, high-level documentation about what has changed in ea - the length of computFlux has been decreased substantially ### Minor changes -- Updated SWE balance check in coupled_em for cases where all snow melts in one of the substeps +- ## Version 3.2.0 ### Major changes From e527b2985f410f22f48651f15ff4342b9d26e37a Mon Sep 17 00:00:00 2001 From: Kyle Klenk Date: Wed, 25 Oct 2023 16:52:37 -0600 Subject: [PATCH 0972/1472] Revert "Revert "Changes for Summa-Actors updates"" --- build/cmake/CMakeLists.txt | 89 ++++++++++++++---------------- build/source/dshare/globalData.f90 | 15 ----- build/source/engine/coupled_em.f90 | 6 +- docs/whats-new.md | 2 +- 4 files changed, 43 insertions(+), 69 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 7bd2d2691..3c5e4fda7 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -72,7 +72,7 @@ else() if(CMAKE_BUILD_TYPE MATCHES Actors) message("\nUsing Actors Framework, should have been installed previously") add_compile_definitions(ACTORS_ACTIVE) - + add_compile_definitions(V4_ACTIVE) project(summaactors DESCRIPTION "Summa-Sundials-BE Actors") include(FortranCInterface) @@ -158,7 +158,7 @@ if(CMAKE_BUILD_TYPE MATCHES Cluster) if(CMAKE_BUILD_TYPE MATCHES Actors) find_package(CAF REQUIRED) - set(INC_ACTORS ${CAF_INCLUDES} ${PARENT_DIR}/build/includes/global ${PARENT_DIR}/build/includes/summa_actor ${PARENT_DIR}/build/includes/gru_actor ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) + set(INC_ACTORS ${CAF_INCLUDES} ${PARENT_DIR}/build/includes/global ${PARENT_DIR}/build/includes/summa_actor ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) set(LIB_ACTORS ${CAF_LIBRARIES} -lcaf_core -lcaf_io) endif() @@ -206,7 +206,12 @@ else() message(FATAL_ERROR "Did not find Actors directory, edit CMakeLists.txt to add path") endif() link_directories(${DIR_ACTORS}/lib) - set(INC_ACTORS ${DIR_ACTORS}/include ${PARENT_DIR}/build/includes/global ${PARENT_DIR}/build/includes/summa_actor ${PARENT_DIR}/build/includes/gru_actor ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) + set(INC_ACTORS ${DIR_ACTORS}/include + ${PARENT_DIR}/build/includes/global + ${PARENT_DIR}/build/includes/summa_actor + ${PARENT_DIR}/build/includes/job_actor + ${PARENT_DIR}/build/includes/file_access_actor + ${PARENT_DIR}/build/includes/hru_actor) set(LIB_ACTORS ${DIR_ACTORS}/lib -lcaf_core -lcaf_io) endif() @@ -238,21 +243,20 @@ set(HOOKUP_DIR ${F_MASTER}/build/source/hookup) set(NETCDF_DIR ${F_MASTER}/build/source/netcdf) set(NOAHMP_DIR ${F_MASTER}/build/source/noah-mp) -# Define directories for source files that might be replaced by actors (identical if not using actors) -set(SUB_DRIVER_DIR ${PARENT_DIR}/build/source/driver) -set(SUB_DSHARE_DIR ${PARENT_DIR}/build/source/dshare) -set(SUB_ENGINE_DIR ${PARENT_DIR}/build/source/engine) -set(SUB_HOOKUP_DIR ${PARENT_DIR}/build/source/hookup) -set(SUB_NETCDF_DIR ${PARENT_DIR}/build/source/netcdf) - # Define Actors specific directories set(ACTORS_DIR ${PARENT_DIR}/build/source/actors) set(FILE_ACCESS_DIR ${ACTORS_DIR}/file_access_actor) set(JOB_ACTOR_DIR ${ACTORS_DIR}/job_actor) set(HRU_ACTOR_DIR ${ACTORS_DIR}/hru_actor) -set(GRU_ACTOR_DIR ${ACTORS_DIR}/gru_actor) - +if(CMAKE_BUILD_TYPE MATCHES Actors) + # Define directories for source files that might be replaced by actors (identical if not using actors) + set(SUB_ENGINE_DIR ${FILE_ACCESS_DIR}) + set(SUB_NETCDF_DIR ${FILE_ACCESS_DIR}) +else() + set(SUB_ENGINE_DIR ${PARENT_DIR}/build/source/engine) + set(SUB_NETCDF_DIR ${PARENT_DIR}/build/source/netcdf) +endif() #========================================================================================= # COMPILE PART 2: Assemble all of the SUMMA sub-routines #========================================================================================= @@ -282,7 +286,7 @@ set(HOOKUP # Data modules set(DATAMS - ${SUB_DSHARE_DIR}/data_types.f90 + ${DSHARE_DIR}/data_types.f90 ${DSHARE_DIR}/flxMapping.f90 ${DSHARE_DIR}/get_ixname.f90 ${DSHARE_DIR}/globalData.f90 @@ -293,6 +297,8 @@ set(DATAMS set(DATAMS_SUNDIALS ${DSHARE_DIR}/type4ida.f90 ${DSHARE_DIR}/type4kinsol.f90) +set(DATAMS_ACTORS + ${ACTORS_DIR}/global/actor_data_types.f90) # Utility modules set(UTILMS @@ -309,7 +315,7 @@ set(UTILMS_SUNDIALS # NetCDF routines set(NETCDF ${SUB_NETCDF_DIR}/def_output.f90 - ${SUB_NETCDF_DIR}/modelwrite.f90 + ${NETCDF_DIR}/modelwrite.f90 ${NETCDF_DIR}/netcdf_util.f90 ${NETCDF_DIR}/read_icond.f90) @@ -410,23 +416,17 @@ set(INTERFACE ${ACTORS_DIR}/global/cppwrap_datatypes.f90 ${ACTORS_DIR}/global/cppwrap_metadata.f90) set(FILE_ACCESS_INTERFACE - ${FILE_ACCESS_DIR}/fortran_code/cppwrap_fileAccess.f90 - ${FILE_ACCESS_DIR}/fortran_code/output_structure.f90 - ${FILE_ACCESS_DIR}/fortran_code/read_force.f90 - ${FILE_ACCESS_DIR}/fortran_code/write_to_netcdf.f90 - ${FILE_ACCESS_DIR}/fortran_code/writeOutputFromOutputStructure.f90) + ${FILE_ACCESS_DIR}/cppwrap_fileAccess.f90 + ${FILE_ACCESS_DIR}/output_structure.f90 + ${FILE_ACCESS_DIR}/read_force.f90 + ${FILE_ACCESS_DIR}/fileAccess_writeOutput.f90) set(JOB_INTERFACE ${JOB_ACTOR_DIR}/job_actor.f90) -set(GRU_INTERFACE - ${GRU_ACTOR_DIR}/gru_actor.f90) set(HRU_INTERFACE - ${HRU_ACTOR_DIR}/fortran_code/hru_actor.f90 - ${HRU_ACTOR_DIR}/fortran_code/hru_init.f90 - ${HRU_ACTOR_DIR}/fortran_code/hru_modelRun.f90 - ${HRU_ACTOR_DIR}/fortran_code/hru_modelwrite.f90 - ${HRU_ACTOR_DIR}/fortran_code/hru_restart.f90 - ${HRU_ACTOR_DIR}/fortran_code/hru_setup.f90 - ${HRU_ACTOR_DIR}/fortran_code/hru_writeOutput.f90) + ${HRU_ACTOR_DIR}/hru_init.f90 + ${HRU_ACTOR_DIR}/hru_read.f90 + ${HRU_ACTOR_DIR}/hru_modelRun.f90 + ${HRU_ACTOR_DIR}/hru_writeOutput.f90) # Actors actual actor modules set(ACTORS_GLOBAL @@ -436,25 +436,23 @@ set(ACTORS_GLOBAL ${ACTORS_DIR}/global/settings_functions.cpp ${ACTORS_DIR}/global/timing_info.cpp) set(SUMMA_ACTOR - ${ACTORS_DIR}/summa_actor/batch/batch.cpp - ${ACTORS_DIR}/summa_actor/batch/batch_container.cpp - ${ACTORS_DIR}/summa_actor/client/client.cpp - ${ACTORS_DIR}/summa_actor/client/client_container.cpp + ${ACTORS_DIR}/summa_actor/batch.cpp + ${ACTORS_DIR}/summa_actor/batch_container.cpp + ${ACTORS_DIR}/summa_actor/client.cpp + ${ACTORS_DIR}/summa_actor/client_container.cpp ${ACTORS_DIR}/summa_actor/summa_actor.cpp ${ACTORS_DIR}/summa_actor/summa_backup_server.cpp ${ACTORS_DIR}/summa_actor/summa_client.cpp ${ACTORS_DIR}/summa_actor/summa_server.cpp) set(FILE_ACCESS_ACTOR - ${ACTORS_DIR}/file_access_actor/cpp_code/file_access_actor.cpp - ${ACTORS_DIR}/file_access_actor/cpp_code/forcing_file_info.cpp - ${ACTORS_DIR}/file_access_actor/cpp_code/output_container.cpp) + ${ACTORS_DIR}/file_access_actor/file_access_actor.cpp + ${ACTORS_DIR}/file_access_actor/forcing_file_info.cpp + ${ACTORS_DIR}/file_access_actor/output_container.cpp) set(JOB_ACTOR ${ACTORS_DIR}/job_actor/GRU.cpp ${ACTORS_DIR}/job_actor/job_actor.cpp) -set(GRU_ACTOR - ${ACTORS_DIR}/gru_actor/gru_actor.cpp) set(HRU_ACTOR - ${ACTORS_DIR}/hru_actor/cpp_code/hru_actor.cpp) + ${ACTORS_DIR}/hru_actor/hru_actor.cpp) #========================================================================================= @@ -467,13 +465,10 @@ set(COMM_ALL ${DATAMS} ${UTILMS}) if(CMAKE_BUILD_TYPE MATCHES Actors) - set(COMM_ALL ${COMM_ALL} - ${INTERFACE}) + set(COMM_ALL ${COMM_ALL} ${DATAMS_ACTORS} ${INTERFACE}) endif() if(CMAKE_BUILD_TYPE MATCHES Sundials) - set(COMM_ALL ${COMM_ALL} - ${DATAMS_SUNDIALS} - ${UTILMS_SUNDIALS}) + set(COMM_ALL ${COMM_ALL} ${DATAMS_SUNDIALS} ${UTILMS_SUNDIALS}) endif() set(SUMMA_ALL @@ -485,10 +480,9 @@ set(SUMMA_ALL if(CMAKE_BUILD_TYPE MATCHES Actors) set(SUMMA_ALL ${SUMMA_ALL} - ${FILE_ACCESS_INTERFACE} - ${JOB_INTERFACE} - ${GRU_INTERFACE} - ${HRU_INTERFACE}) + ${FILE_ACCESS_INTERFACE} + ${JOB_INTERFACE} + ${HRU_INTERFACE}) else() set(SUMMA_ALL ${SUMMA_ALL} ${PRELIM_NOT_ACTORS} @@ -582,7 +576,6 @@ elseif(CMAKE_BUILD_TYPE MATCHES Actors) ${ACTORS_GLOBAL} ${FILE_ACCESS_ACTOR} ${JOB_ACTOR} - ${GRU_ACTOR} ${HRU_ACTOR} ${SUMMA_ACTOR} ${SUMMA_CLIENT} diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 index 798aac4be..6206d2bc7 100644 --- a/build/source/dshare/globalData.f90 +++ b/build/source/dshare/globalData.f90 @@ -37,12 +37,6 @@ MODULE globalData USE data_types,only:extended_info ! extended metadata for variables in each model structure USE data_types,only:struct_info ! summary information on all data structures USE data_types,only:var_i ! vector of integers -#ifdef ACTORS_ACTIVE - USE data_types,only:var_forc ! for Actors - USE data_types,only:dlength ! for Actors - USE data_types,only:ilength ! for Actors - USE data_types,only:init_cond ! for Actors -#endif ! number of variables in each data structure USE var_lookup,only:maxvarTime ! time: maximum number variables USE var_lookup,only:maxvarForc ! forcing data: maximum number variables @@ -264,14 +258,6 @@ MODULE globalData integer(i4b),parameter,public :: ncTime=1 ! time zone information from NetCDF file (timeOffset = longitude/15. - ncTimeOffset) integer(i4b),parameter,public :: utcTime=2 ! all times in UTC (timeOffset = longitude/15. hours) integer(i4b),parameter,public :: localTime=3 ! all times local (timeOffset = 0) -#ifdef ACTORS_ACTIVE - ! global data structures are managed by FileAccessActor - type(var_forc),allocatable,save,public :: forcingDataStruct(:) ! forcingDataStruct(:)%var(:)%dataFromFile(:,:) - type(dlength),allocatable,save,public :: vecTime(:) - logical(lgt),allocatable,save,public :: failedHRUs(:) ! list of true and false values to indicate if an HRU has failed - type(ilength),allocatable,save,public :: outputTimeStep(:) ! timestep in output files - ! inital conditions for Actors -#else ! define metadata for model forcing datafile non-Actors type(file_info),save,public,allocatable :: forcFileInfo(:) ! file info for model forcing data ! define indices in the forcing data files non-Actors @@ -286,7 +272,6 @@ MODULE globalData real(rkind),save,public :: fracJulDay ! fractional julian days since the start of year real(rkind),save,public :: tmZoneOffsetFracDay ! time zone offset in fractional days integer(i4b),save,public :: yearLength ! number of days in the current year -#endif ! define fixed dimensions integer(i4b),parameter,public :: nBand=2 ! number of spectral bands integer(i4b),parameter,public :: nTimeDelay=2000 ! number of time steps in the time delay histogram (default: ~1 season = 24*365/4) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index f10eaefc8..d3f6b5622 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -147,12 +147,8 @@ subroutine coupled_em(& USE computSnowDepth_module,only:computSnowDepth implicit none - ! model control -#ifdef ACTORS_ACTIVE - integer(4),intent(in) :: hruId ! hruId -#else + integer(8),intent(in) :: hruId ! hruId -#endif real(rkind),intent(inout) :: dt_init ! used to initialize the size of the sub-step integer(i4b),intent(in) :: dt_init_factor ! Used to adjust the length of the timestep in the event of a failure logical(lgt),intent(inout) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) diff --git a/docs/whats-new.md b/docs/whats-new.md index 3221d4445..9ac160bcc 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -10,7 +10,7 @@ This page provides simple, high-level documentation about what has changed in ea - the length of computFlux has been decreased substantially ### Minor changes -- +- Updated SWE balance check in coupled_em for cases where all snow melts in one of the substeps ## Version 3.2.0 ### Major changes From 6893b51ddc1a2b4e29c89f96832a7df5557c5c8f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 26 Oct 2023 12:49:45 +0900 Subject: [PATCH 0973/1472] small error on actors build instrucitons --- build/cmake_external/build_cmakeActors.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/cmake_external/build_cmakeActors.bash b/build/cmake_external/build_cmakeActors.bash index f34ac441d..4c27c3561 100755 --- a/build/cmake_external/build_cmakeActors.bash +++ b/build/cmake_external/build_cmakeActors.bash @@ -2,8 +2,8 @@ # from {$maindir}/actor-framework, run # cp ../../summa/build/summa/build/cmake_external/build_cmakeActors.bash build_cmake -# run script from the actor-framework directory with ./build_cmake -# run `make`, then `make install` +# run script from the actor-framework directory with ./build_cmake +# run `cd build`, `make`, then `make install` export CXX="g++" ./configure --prefix=../install From aab05639bcab3e49742f5cb667167d5a12b408b2 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 26 Oct 2023 04:27:43 -0600 Subject: [PATCH 0974/1472] Applied object-oriented methods to intent(inout) and intent(out) varSubstep arguments. --- build/source/dshare/data_types.f90 | 92 +++++++++++++++++++++-------- build/source/engine/opSplittin.f90 | 64 +++++++------------- build/source/engine/varSubstep.f90 | 93 +++++++++++++++--------------- 3 files changed, 136 insertions(+), 113 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index f1174ce0c..d26a691d1 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -308,7 +308,7 @@ MODULE data_types ! *********************************************************************************************************** ! Note: class procedures are located in the contains block of this (data_types) module ! ** vegNrgFlux - type, public :: in_type_vegNrgFlux ! derived type for intent(in) arguments in vegNrgFlux call + type, public :: in_type_vegNrgFlux ! class for intent(in) arguments in vegNrgFlux call logical(lgt) :: firstSubStep ! intent(in): flag to indicate if we are processing the first sub-step logical(lgt) :: firstFluxCall ! intent(in): flag to indicate if we are processing the first flux call logical(lgt) :: computeVegFlux ! intent(in): flag to indicate if we need to compute fluxes over vegetation @@ -324,7 +324,7 @@ MODULE data_types procedure :: initialize => initialize_in_vegNrgFlux end type in_type_vegNrgFlux - type, public :: out_type_vegNrgFlux ! derived type for intent(out) arguments in vegNrgFlux call + type, public :: out_type_vegNrgFlux ! class for intent(out) arguments in vegNrgFlux call real(rkind) :: scalarCanopyTranspiration ! intent(out): canopy transpiration (kg m-2 s-1) real(rkind) :: scalarCanopyEvaporation ! intent(out): canopy evaporation/condensation (kg m-2 s-1) real(rkind) :: scalarGroundEvaporation ! intent(out): ground evaporation/condensation -- below canopy or non-vegetated (kg m-2 s-1) @@ -362,7 +362,7 @@ MODULE data_types ! ** end vegNrgFlux ! ** ssdNrgFlux - type, public :: in_type_ssdNrgFlux ! derived type for intent(in) arguments in ssdNrgFlux call + type, public :: in_type_ssdNrgFlux ! class for intent(in) arguments in ssdNrgFlux call logical(lgt) :: scalarSolution ! intent(in): flag to denote if implementing the scalar solution real(rkind) :: scalarGroundNetNrgFlux ! intent(in): net energy flux for the ground surface (W m-2) real(rkind), allocatable :: iLayerLiqFluxSnow(:) ! intent(in): liquid flux at the interface of each snow layer (m s-1) @@ -376,14 +376,14 @@ MODULE data_types procedure :: initialize => initialize_in_ssdNrgFlux end type in_type_ssdNrgFlux - type, public :: io_type_ssdNrgFlux ! derived type for intent(inout) arguments in ssdNrgFlux call + type, public :: io_type_ssdNrgFlux ! class for intent(inout) arguments in ssdNrgFlux call real(rkind) :: dGroundNetFlux_dGroundTemp ! intent(inout): derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) contains procedure :: initialize => initialize_io_ssdNrgFlux procedure :: finalize => finalize_io_ssdNrgFlux end type io_type_ssdNrgFlux - type, public :: out_type_ssdNrgFlux ! derived type for intent(inout) arguments in ssdNrgFlux call + type, public :: out_type_ssdNrgFlux ! class for intent(inout) arguments in ssdNrgFlux call real(rkind), allocatable :: iLayerNrgFlux(:) ! intent(out): energy flux at the layer interfaces (W m-2) real(rkind), allocatable :: dNrgFlux_dTempAbove(:) ! intent(out): derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) real(rkind), allocatable :: dNrgFlux_dTempBelow(:) ! intent(out): derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) @@ -437,7 +437,7 @@ MODULE data_types procedure :: finalize => finalize_io_snowLiqFlx end type io_type_snowLiqFlx - type, public :: out_type_snowLiqFlx ! derived type for intent(out) arguments in snowLiqFlx call + type, public :: out_type_snowLiqFlx ! class for intent(out) arguments in snowLiqFlx call integer(i4b) :: err ! intent(out): error code character(:),allocatable :: cmessage ! intent(out): error message contains @@ -446,7 +446,7 @@ MODULE data_types ! ** end snowLiqFlx ! ** soilLiqFlx - type, public :: in_type_soilLiqFlx ! derived type for intent(in) arguments in soilLiqFlx call + type, public :: in_type_soilLiqFlx ! class for intent(in) arguments in soilLiqFlx call integer(i4b) :: nSoil ! intent(in): number of soil layers logical(lgt) :: firstSplitOper ! intent(in): flag indicating first flux call in a splitting operation logical(lgt) :: scalarSolution ! intent(in): flag to indicate the scalar solution @@ -472,7 +472,7 @@ MODULE data_types procedure :: initialize => initialize_in_soilLiqFlx end type in_type_soilLiqFlx - type, public :: io_type_soilLiqFlx ! derived type for intent(inout) arguments in soilLiqFlx call + type, public :: io_type_soilLiqFlx ! class for intent(inout) arguments in soilLiqFlx call real(rkind) :: scalarMaxInfilRate ! intent(inout): maximum infiltration rate (m s-1) real(rkind) :: scalarInfilArea ! intent(inout): fraction of unfrozen area where water can infiltrate (-) real(rkind) :: scalarFrozenArea ! intent(inout): fraction of area that is considered impermeable due to soil ice (-) @@ -499,7 +499,7 @@ MODULE data_types procedure :: finalize => finalize_io_soilLiqFlx end type io_type_soilLiqFlx - type, public :: out_type_soilLiqFlx ! derived type for intent(out) arguments in soilLiqFlx call + type, public :: out_type_soilLiqFlx ! class for intent(out) arguments in soilLiqFlx call integer(i4b) :: err ! intent(out): error code character(:),allocatable :: cmessage ! intent(out): error message contains @@ -508,7 +508,7 @@ MODULE data_types ! ** end soilLiqFlx ! ** groundwatr - type, public :: in_type_groundwatr ! derived type for intent(in) arguments in groundwatr call + type, public :: in_type_groundwatr ! class for intent(in) arguments in groundwatr call integer(i4b) :: nSnow ! intent(in): number of snow layers integer(i4b) :: nSoil ! intent(in): number of soil layers integer(i4b) :: nLayers ! intent(in): total number of layers @@ -521,14 +521,14 @@ MODULE data_types procedure :: initialize => initialize_in_groundwatr end type in_type_groundwatr - type, public :: io_type_groundwatr ! derived type for intent(io) arguments in groundwatr call + type, public :: io_type_groundwatr ! class for intent(io) arguments in groundwatr call integer(i4b) :: ixSaturation ! intent(inout): index of lowest saturated layer (NOTE: only computed on the first iteration) contains procedure :: initialize => initialize_io_groundwatr procedure :: finalize => finalize_io_groundwatr end type io_type_groundwatr - type, public :: out_type_groundwatr ! derived type for intent(out) arguments in groundwatr call + type, public :: out_type_groundwatr ! class for intent(out) arguments in groundwatr call real(rkind), allocatable :: mLayerBaseflow(:) ! intent(out): baseflow from each soil layer (m s-1) real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! intent(out): derivative in baseflow w.r.t. matric head (s-1) integer(i4b) :: err ! intent(out): error code @@ -539,7 +539,7 @@ MODULE data_types ! ** end groundwatr ! ** bigAquifer - type, public :: in_type_bigAquifer ! derived type for intent(in) arguments in bigAquifer call + type, public :: in_type_bigAquifer ! class for intent(in) arguments in bigAquifer call real(rkind) :: scalarAquiferStorageTrial ! intent(in): trial value of aquifer storage (m) real(rkind) :: scalarCanopyTranspiration ! intent(in): canopy transpiration (kg m-2 s-1) real(rkind) :: scalarSoilDrainage ! intent(in): soil drainage (m s-1) @@ -551,7 +551,7 @@ MODULE data_types procedure :: initialize => initialize_in_bigAquifer end type in_type_bigAquifer - type, public :: io_type_bigAquifer ! derived type for intent(inout) arguments in bigAquifer call + type, public :: io_type_bigAquifer ! class for intent(inout) arguments in bigAquifer call real(rkind) :: dAquiferTrans_dTCanair ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature real(rkind) :: dAquiferTrans_dTCanopy ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. canopy temperature real(rkind) :: dAquiferTrans_dTGround ! intent(inout): derivatives in the aquifer transpiration flux w.r.t. ground temperature @@ -561,7 +561,7 @@ MODULE data_types procedure :: finalize => finalize_io_bigAquifer end type io_type_bigAquifer - type, public :: out_type_bigAquifer ! derived type for intent(out) arguments in bigAquifer call + type, public :: out_type_bigAquifer ! class for intent(out) arguments in bigAquifer call real(rkind) :: scalarAquiferTranspire ! intent(out): transpiration loss from the aquifer (m s-1) real(rkind) :: scalarAquiferRecharge ! intent(out): recharge to the aquifer (m s-1) real(rkind) :: scalarAquiferBaseflow ! intent(out): total baseflow from the aquifer (m s-1) @@ -574,7 +574,7 @@ MODULE data_types ! ** end bigAquifer ! ** varSubstep - type, public :: in_type_varSubstep ! derived type for intent(in) arguments in varSubstep call + type, public :: in_type_varSubstep ! class for intent(in) arguments in varSubstep call real(rkind) :: dt ! intent(in): time step (s) real(rkind) :: dtInit ! intent(in): initial time step (seconds) real(rkind) :: dt_min ! intent(in): minimum time step (seconds) @@ -590,15 +590,16 @@ MODULE data_types procedure :: initialize => initialize_in_varSubstep end type in_type_varSubstep - type, public :: io_type_varSubstep ! derived type for intent(inout) arguments in varSubstep call - logical(lgt) :: firstFluxCall ! intent(inout) : flag to indicate if we are processing the first flux call - integer(i4b) :: fluxCount ! intent(inout) : number of times fluxes are updated (should equal nsubstep) - integer(i4b) :: ixSaturation ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) + type, public :: io_type_varSubstep ! class for intent(inout) arguments in varSubstep call + logical(lgt) :: firstFluxCall ! intent(inout): flag to indicate if we are processing the first flux call + type(var_ilength) :: fluxCount ! intent(inout): number of times fluxes are updated (should equal nsubstep) + integer(i4b) :: ixSaturation ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) contains - + procedure :: initialize => initialize_io_varSubstep + procedure :: finalize => finalize_io_varSubstep end type io_type_varSubstep - type, public :: out_type_varSubstep ! derived type for intent(out) arguments in varSubstep call + type, public :: out_type_varSubstep ! class for intent(out) arguments in varSubstep call real(rkind) :: dtMultiplier ! intent(out): substep multiplier (-) integer(i4b) :: nSubsteps ! intent(out): number of substeps taken for a given split logical(lgt) :: failedMinimumStep ! intent(out): flag for failed substeps @@ -607,7 +608,7 @@ MODULE data_types integer(i4b) :: err ! intent(out): error code character(:),allocatable :: cmessage ! intent(out): error message contains - + procedure :: finalize => finalize_out_varSubstep end type out_type_varSubstep ! ** end varSubstep @@ -1277,5 +1278,50 @@ subroutine initialize_in_varSubstep(in_varSubstep,dt,dtInit,dt_min,whole_step,nS in_varSubstep % iStateSplit = iStateSplit ! intent(in): index of the layer in the splitting operation in_varSubstep % fluxMask = fluxMask ! intent(in): mask for the fluxes used in this given state subset end subroutine initialize_in_varSubstep + + subroutine initialize_io_varSubstep(io_varSubstep,firstFluxCall,fluxCount,ixSaturation) + class(io_type_varSubstep),intent(out) :: io_varSubstep ! class object for intent(in) varSubstep arguments + logical(lgt),intent(in) :: firstFluxCall ! flag to indicate if we are processing the first flux call + type(var_ilength),intent(in) :: fluxCount ! number of times fluxes are updated (should equal nsubstep) + integer(i4b),intent(in) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + + ! intent(inout) arguments + io_varSubstep % firstFluxCall = firstFluxCall ! intent(inout): flag to indicate if we are processing the first flux call + io_varSubstep % fluxCount = fluxCount ! intent(inout): number of times fluxes are updated (should equal nsubstep) + io_varSubstep % ixSaturation = ixSaturation ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + end subroutine initialize_io_varSubstep + + subroutine finalize_io_varSubstep(io_varSubstep,firstFluxCall,fluxCount,ixSaturation) + class(io_type_varSubstep),intent(in) :: io_varSubstep ! class object for intent(in) varSubstep arguments + logical(lgt),intent(out) :: firstFluxCall ! flag to indicate if we are processing the first flux call + type(var_ilength),intent(out) :: fluxCount ! number of times fluxes are updated (should equal nsubstep) + integer(i4b),intent(out) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + + ! intent(inout) arguments + firstFluxCall = io_varSubstep % firstFluxCall ! intent(inout): flag to indicate if we are processing the first flux call + fluxCount = io_varSubstep % fluxCount ! intent(inout): number of times fluxes are updated (should equal nsubstep) + ixSaturation = io_varSubstep % ixSaturation ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + end subroutine finalize_io_varSubstep + + subroutine finalize_out_varSubstep(out_varSubstep,dtMultiplier,nSubsteps,failedMinimumStep,reduceCoupledStep,tooMuchMelt,err,cmessage) + class(out_type_varSubstep),intent(in) :: out_varSubstep ! class object for intent(out) varSubstep arguments + real(rkind),intent(out) :: dtMultiplier ! substep multiplier (-) + integer(i4b),intent(out) :: nSubsteps ! number of substeps taken for a given split + logical(lgt),intent(out) :: failedMinimumStep ! flag for failed substeps + logical(lgt),intent(out) :: reduceCoupledStep ! flag to reduce the length of the coupled step + logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: cmessage ! error message + + ! intent(out) arguments + dtMultiplier = out_varSubstep % dtMultiplier ! intent(out): substep multiplier (-) + nSubsteps = out_varSubstep % nSubsteps ! intent(out): number of substeps taken for a given split + failedMinimumStep = out_varSubstep % failedMinimumStep ! intent(out): flag for failed substeps + reduceCoupledStep = out_varSubstep % reduceCoupledStep ! intent(out): flag to reduce the length of the coupled step + tooMuchMelt = out_varSubstep % tooMuchMelt ! intent(out): flag to denote that ice is insufficient to support melt + err = out_varSubstep % err ! intent(out): error code + cmessage = out_varSubstep % cmessage ! intent(out): error message + end subroutine finalize_out_varSubstep ! **** end varSubstep **** + END MODULE data_types diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 3a589e223..0f474c92e 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -88,14 +88,14 @@ module opSplittin_module ! provide access to the derived types to define the data structures USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_flagVec, & ! data vector with variable length dimension (i4b) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! lookup tables - model_options, & ! defines the model decisions - in_type_varSubstep ! class for varSubstep objects + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_flagVec, & ! data vector with variable length dimension (i4b) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! lookup tables + model_options, & ! defines the model decisions + in_type_varSubstep,io_type_varSubstep,out_type_varSubstep ! classes for varSubstep objects ! look-up values for the numerical method USE mDecisions_module,only: & @@ -291,7 +291,7 @@ subroutine opSplittin(& logical(lgt) :: addFirstFlux ! flag to add the first flux to the mask ! ---------------------- classes for flux subroutine arguments (classes defined in data_types module) ---------------------- ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** - type(in_type_varSubstep) :: in_varSubstep; ! varSubstep arguments + type(in_type_varSubstep) :: in_varSubstep; type(io_type_varSubstep) :: io_varSubstep; type(out_type_varSubstep) :: out_varSubstep;! varSubstep arguments ! --------------------------------------------------------------------------------------- ! point to variables in the data structures @@ -650,39 +650,13 @@ subroutine opSplittin(& ! keep track of the number of scalar solutions if(ixSolution==scalar) numberScalarSolutions = numberScalarSolutions + 1 - ! solve variable subset for one full time steps + ! solve variable subset for one full time step call initialize_varSubstep - call varSubstep(& - ! input: model control - in_varSubstep, & ! intent(in) : model control - firstFluxCall, & ! intent(inout) : flag to indicate if we are processing the first flux call - fluxCount, & ! intent(inout) : number of times fluxes are updated (should equal nsubstep) - ! input/output: data structures - model_decisions, & ! intent(in) : model decisions - lookup_data, & ! intent(in) : lookup tables - type_data, & ! intent(in) : type of vegetation and soil - attr_data, & ! intent(in) : spatial attributes - forc_data, & ! intent(in) : model forcing data - mpar_data, & ! intent(in) : model parameters - indx_data, & ! intent(inout) : index data - prog_data, & ! intent(inout) : model prognostic variables for a local HRU - diag_data, & ! intent(inout) : model diagnostic variables for a local HRU - flux_data, & ! intent(inout) : model fluxes for a local HRU - flux_mean, & ! intent(inout) : mean model fluxes for a local HRU - deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables - bvar_data, & ! intent(in) : model variables for the local basin - ! output: control - ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) - dtMultiplier, & ! intent(out) : substep multiplier (-) - nSubsteps, & ! intent(out) : number of substeps taken for a given split - failedMinimumStep, & ! intent(out) : flag for failed substeps - reduceCoupledStep, & ! intent(out) : flag to reduce the length of the coupled step - tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt - err,cmessage) ! intent(out) : error code and error message - if(err/=0)then - message=trim(message)//trim(cmessage) - if(err>0) return - endif ! (check for errors) + call varSubstep(in_varSubstep,io_varSubstep,& ! intent(inout): class objects for model control + model_decisions,lookup_data,type_data,attr_data,forc_data,mpar_data,& ! intent(inout): data structures for model properties + indx_data,prog_data,diag_data,flux_data,flux_mean,deriv_data,bvar_data,& + out_varSubstep) ! intent(out): class object for model control + call finalize_varSubstep ! reduce coupled step if failed the minimum step for the scalar solution if(failedMinimumStep .and. ixSolution==scalar) reduceCoupledStep=.true. @@ -904,9 +878,15 @@ end subroutine finalize_opSplittin ! **** varSubstep **** subroutine initialize_varSubstep - call in_varSubstep%initialize(dt,dtInit,dt_min,whole_step,nSubset,doAdjustTemp,firstSubStep,computeVegFlux,ixSolution,scalar,iStateSplit,fluxMask) + call in_varSubstep % initialize(dt,dtInit,dt_min,whole_step,nSubset,doAdjustTemp,firstSubStep,computeVegFlux,ixSolution,scalar,iStateSplit,fluxMask) + call io_varSubstep % initialize(firstFluxCall,fluxCount,ixSaturation) end subroutine initialize_varSubstep + subroutine finalize_varSubstep + call io_varSubstep % finalize(firstFluxCall,fluxCount,ixSaturation) + call out_varSubstep % finalize(dtMultiplier,nSubsteps,failedMinimumStep,reduceCoupledStep,tooMuchMelt,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); if (err>0) return; end if ! error control + end subroutine finalize_varSubstep end subroutine opSplittin diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 144c53534..6928b98cc 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -42,14 +42,16 @@ module varSubstep_module ! derived types to define the data structures USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_flagVec, & ! data vector with variable length dimension (i4b) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! lookup tables - model_options, & ! defines the model decisions - in_type_varSubstep ! class for intent(in) arguments + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_flagVec, & ! data vector with variable length dimension (i4b) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! lookup tables + model_options, & ! defines the model decisions + in_type_varSubstep, & ! class for intent(in) arguments + io_type_varSubstep, & ! class for intent(inout) arguments + out_type_varSubstep ! class for intent(out) arguments ! provide access to indices that define elements of the data structures USE var_lookup,only:iLookFLUX ! named variables for structure elements @@ -94,8 +96,7 @@ module varSubstep_module subroutine varSubstep(& ! input: model control in_varSubstep, & ! intent(in) : model control - firstFluxCall, & ! intent(inout) : flag to indicate if we are processing the first flux call - fluxCount, & ! intent(inout) : number of times that fluxes are updated (should equal nSubsteps) + io_varSubstep, & ! intent(inout) : model control ! input/output: data structures model_decisions, & ! intent(in) : model decisions lookup_data, & ! intent(in) : lookup tables @@ -111,13 +112,7 @@ subroutine varSubstep(& deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables bvar_data, & ! intent(in) : model variables for the local basin ! output: model control - ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) - dtMultiplier, & ! intent(out) : substep multiplier (-) - nSubsteps, & ! intent(out) : number of substeps taken for a given split - failedMinimumStep, & ! intent(out) : flag to denote success of substepping for a given split - reduceCoupledStep, & ! intent(out) : flag to denote need to reduce the length of the coupled step - tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt - err,message) ! intent(out) : error code and error message + out_varSubstep) ! intent(out) : model control ! --------------------------------------------------------------------------------------- ! structure allocations USE allocspace_module,only:allocLocal ! allocate local data structures @@ -131,33 +126,25 @@ subroutine varSubstep(& ! --------------------------------------------------------------------------------------- ! * dummy variables ! --------------------------------------------------------------------------------------- - type(in_type_varSubstep),intent(in) :: in_varSubstep ! model control ! input: model control - logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call - type(var_ilength),intent(inout) :: fluxCount ! number of times that the flux is updated (should equal nSubsteps) + type(in_type_varSubstep),intent(in) :: in_varSubstep ! model control + type(io_type_varSubstep),intent(inout) :: io_varSubstep ! model control ! input/output: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(zLookup),intent(in) :: lookup_data ! lookup tables - type(var_i),intent(in) :: type_data ! type of vegetation and soil - type(var_d),intent(in) :: attr_data ! spatial attributes - type(var_d),intent(in) :: forc_data ! model forcing data - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU - type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: flux_mean ! mean model fluxes for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_i),intent(in) :: type_data ! type of vegetation and soil + type(var_d),intent(in) :: attr_data ! spatial attributes + type(var_d),intent(in) :: forc_data ! model forcing data + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU + type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: flux_mean ! mean model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin ! output: model control - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: dtMultiplier ! substep multiplier (-) - integer(i4b),intent(out) :: nSubsteps ! number of substeps taken for a given split - logical(lgt),intent(out) :: failedMinimumStep ! flag to denote success of substepping for a given split - logical(lgt),intent(out) :: reduceCoupledStep ! flag to denote need to reduce the length of the coupled step - logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + type(out_type_varSubstep),intent(out) :: out_varSubstep ! model control ! --------------------------------------------------------------------------------------- ! * general local variables ! --------------------------------------------------------------------------------------- @@ -215,6 +202,9 @@ subroutine varSubstep(& scalarSolution => in_varSubstep % scalarSolution, & ! intent(in): flag to denote implementing the scalar solution iStateSplit => in_varSubstep % iStateSplit, & ! intent(in): index of the state in the splitting operation fluxMask => in_varSubstep % fluxMask, & ! intent(in): flags to denote if the flux is calculated in the given state subset + firstFluxCall => io_varSubstep % firstFluxCall, & ! intent(inout): flag to define the first flux call + fluxCount => io_varSubstep % fluxCount, & ! intent(inout): number of times that the flux is updated (should equal nSubsteps) + ixSaturation => io_varSubstep % ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) ! model decisions ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver ! number of layers @@ -239,7 +229,15 @@ subroutine varSubstep(& mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of liquid water (-) mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of total water (-) mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat & ! intent(inout): [dp(:)] matric potential of liquid water (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(inout): [dp(:)] matric potential of liquid water (m) + ! model control + dtMultiplier => out_varSubstep % dtMultiplier ,& ! intent(out): substep multiplier (-) + nSubsteps => out_varSubstep % nSubsteps ,& ! intent(out): number of substeps taken for a given split + failedMinimumStep => out_varSubstep % failedMinimumStep ,& ! intent(out): flag to denote success of substepping for a given split + reduceCoupledStep => out_varSubstep % reduceCoupledStep ,& ! intent(out): flag to denote need to reduce the length of the coupled step + tooMuchMelt => out_varSubstep % tooMuchMelt ,& ! intent(out): flag to denote that ice is insufficient to support melt + err => out_varSubstep % err ,& ! intent(out): error code + message => out_varSubstep % cmessage & ! intent(out): error message ) ! end association with variables in the data structures ! ********************************************************************************************************************************************************* @@ -544,14 +542,13 @@ subroutine varSubstep(& end do deallocate(sumLayerCompress) + ! update error codes + if (failedMinimumStep) then + err=-20 ! negative = recoverable error + message=trim(message)//'failed minimum step' + end if ! end associate statements end associate globalVars - - ! update error codes - if(failedMinimumStep)then - err=-20 ! negative = recoverable error - message=trim(message)//'failed minimum step' - endif end subroutine varSubstep From ab1cfefa9bb948bd7c20667be305828b1fe01a4f Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 28 Oct 2023 04:13:14 -0600 Subject: [PATCH 0975/1472] Applied object-oriented methods to simplify call to stateFilter in opSplittin. --- build/source/dshare/data_types.f90 | 49 ++++++++++++++++++ build/source/engine/opSplittin.f90 | 80 ++++++++++++++++++------------ 2 files changed, 97 insertions(+), 32 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index d26a691d1..c48a28007 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -573,6 +573,27 @@ MODULE data_types end type out_type_bigAquifer ! ** end bigAquifer + ! ** stateFilter + type, public :: in_type_stateFilter ! class for intent(in) arguments in stateFilter call + integer(i4b) :: ixCoupling ! intent(in): index of coupling method (1,2) + integer(i4b) :: ixSolution ! intent(in): index of solution method (1,2) + integer(i4b) :: ixStateThenDomain ! intent(in): switch between full domain and sub domains + integer(i4b) :: iStateTypeSplit ! intent(in): index of the state type split + integer(i4b) :: iDomainSplit ! intent(in): index of the domain split + integer(i4b) :: iStateSplit ! intent(in): index of the layer split + contains + procedure :: initialize => initialize_in_stateFilter + end type in_type_stateFilter + + type, public :: out_type_stateFilter ! class for intent(out) arguments in stateFilter call + integer(i4b) :: nSubset ! intent(out): number of selected state variables for a given split + integer(i4b) :: err ! intent(out): error code + character(:),allocatable :: cmessage ! intent(out): error message + contains + procedure :: finalize => finalize_out_stateFilter + end type out_type_stateFilter + ! ** end stateFilter + ! ** varSubstep type, public :: in_type_varSubstep ! class for intent(in) arguments in varSubstep call real(rkind) :: dt ! intent(in): time step (s) @@ -1248,6 +1269,34 @@ subroutine finalize_out_bigAquifer(out_bigAquifer,flux_data,deriv_data,err,cmess end subroutine finalize_out_bigAquifer ! **** end bigAquifer **** + ! **** stateFilter **** + subroutine initialize_in_stateFilter(in_stateFilter,ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) + class(in_type_stateFilter),intent(out) :: in_stateFilter ! class object for intent(in) stateFilter arguments + integer(i4b),intent(in) :: ixCoupling ! intent(in): index of coupling method (1,2) + integer(i4b),intent(in) :: ixSolution ! intent(in): index of solution method (1,2) + integer(i4b),intent(in) :: ixStateThenDomain ! intent(in): switch between full domain and sub domains + integer(i4b),intent(in) :: iStateTypeSplit ! intent(in): index of the state type split + integer(i4b),intent(in) :: iDomainSplit ! intent(in): index of the domain split + integer(i4b),intent(in) :: iStateSplit ! intent(in): index of the layer split + in_stateFilter % ixCoupling = ixCoupling ! intent(in): index of coupling method (1,2) + in_stateFilter % ixSolution = ixSolution ! intent(in): index of solution method (1,2) + in_stateFilter % ixStateThenDomain = ixStateThenDomain ! intent(in): switch between full domain and sub domains + in_stateFilter % iStateTypeSplit = iStateTypeSplit ! intent(in): index of the state type split + in_stateFilter % iDomainSplit = iDomainSplit ! intent(in): index of the domain split + in_stateFilter % iStateSplit = iStateSplit ! intent(in): index of the layer split + end subroutine initialize_in_stateFilter + + subroutine finalize_out_stateFilter(out_stateFilter,nSubset,err,cmessage) + class(out_type_stateFilter),intent(in) :: out_stateFilter ! class object for intent(in) stateFilter arguments + integer(i4b),intent(out) :: nSubset ! intent(out): number of selected state variables for a given split + integer(i4b),intent(out) :: err ! intent(out): error code + character(*),intent(out) :: cmessage ! intent(out): error message + nSubset = out_stateFilter % nSubset ! intent(out): number of selected state variables for a given split + err = out_stateFilter % err ! intent(out): error code + cmessage = out_stateFilter % cmessage ! intent(out): error message + end subroutine finalize_out_stateFilter + ! **** end stateFilter **** + ! **** varSubstep **** subroutine initialize_in_varSubstep(in_varSubstep,dt,dtInit,dt_min,whole_step,nSubset,& doAdjustTemp,firstSubStep,computeVegFlux,ixSolution,scalar,iStateSplit,fluxMask) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 0f474c92e..4c849b097 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -95,6 +95,7 @@ module opSplittin_module var_dlength, & ! data vector with variable length dimension (rkind) zLookup, & ! lookup tables model_options, & ! defines the model decisions + in_type_statefilter,out_type_statefilter, & ! classes for stateFilter objects in_type_varSubstep,io_type_varSubstep,out_type_varSubstep ! classes for varSubstep objects ! look-up values for the numerical method @@ -289,9 +290,10 @@ subroutine opSplittin(& real(rkind) :: mean_step_state ! mean step over the state (with or without domain splits) real(rkind) :: mean_step_solution ! mean step for a solution (scalar or vector) logical(lgt) :: addFirstFlux ! flag to add the first flux to the mask - ! ---------------------- classes for flux subroutine arguments (classes defined in data_types module) ---------------------- - ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** - type(in_type_varSubstep) :: in_varSubstep; type(io_type_varSubstep) :: io_varSubstep; type(out_type_varSubstep) :: out_varSubstep;! varSubstep arguments + ! ------------------------ classes for subroutine arguments (classes defined in data_types module) ------------------------ + ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** + type(in_type_stateFilter) :: in_stateFilter; type(out_type_stateFilter) :: out_stateFilter; ! stateFilter arguments + type(in_type_varSubstep) :: in_varSubstep; type(io_type_varSubstep) :: io_varSubstep; type(out_type_varSubstep) :: out_varSubstep; ! varSubstep arguments ! --------------------------------------------------------------------------------------- ! point to variables in the data structures @@ -334,6 +336,7 @@ subroutine opSplittin(& ! *** initialize *** call initialize_opSplittin + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! ========================================================================================================================================== ! loop through different coupling strategies @@ -439,9 +442,10 @@ subroutine opSplittin(& ! ------------------------------------------- ! get the mask for the state subset - call stateFilter(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit,& - indx_data,stateMask,nSubset,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + call initialize_stateFilter + call stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) + call finalize_stateFilter + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! error control ! check that state variables exist if(nSubset==0) cycle domainSplit @@ -657,6 +661,7 @@ subroutine opSplittin(& indx_data,prog_data,diag_data,flux_data,flux_mean,deriv_data,bvar_data,& out_varSubstep) ! intent(out): class object for model control call finalize_varSubstep + if (err/=0) then; message=trim(message)//trim(cmessage); if (err>0) return; end if ! error control ! reduce coupled step if failed the minimum step for the scalar solution if(failedMinimumStep .and. ixSolution==scalar) reduceCoupledStep=.true. @@ -824,31 +829,31 @@ subroutine allocate_memory ! *** allocate memory for local structures *** ! allocate space for the flux mask (used to define when fluxes are updated) call allocLocal(flux_meta(:),fluxMask,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; endif + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if ! allocate space for the flux count (used to check that fluxes are only updated once) call allocLocal(flux_meta(:),fluxCount,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; endif + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if ! allocate space for the temporary prognostic variable structure call allocLocal(prog_meta(:),prog_temp,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; endif + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if ! allocate space for the temporary diagnostic variable structure call allocLocal(diag_meta(:),diag_temp,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; endif + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if ! allocate space for the temporary flux variable structure call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; endif + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if ! allocate space for the mean flux variable structure call allocLocal(flux_meta(:),flux_mean,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; endif + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if ! allocate space for the temporary mean flux variable structure call allocLocal(flux_meta(:),flux_mntemp,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; endif + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if ! allocate space for the derivative structure call allocLocal(deriv_meta(:),deriv_data,nSnow,nSoil,err,cmessage) @@ -869,13 +874,22 @@ subroutine finalize_opSplittin print*, 'fluxCount%var(iVar)%dat = ', fluxCount%var(iVar)%dat message=trim(message)//'flux '//trim(flux_meta(iVar)%varname)//' was not computed' err=20; return - endif + end if end do ! use step halving if unable to complete the fully coupled solution in one substep if (ixCoupling/=fullyCoupled .or. nSubsteps>1) dtMultiplier=0.5_rkind end subroutine finalize_opSplittin + ! **** stateFilter **** + subroutine initialize_stateFilter + call in_stateFilter % initialize(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) + end subroutine initialize_stateFilter + + subroutine finalize_stateFilter + call out_stateFilter % finalize(nSubset,err,cmessage) + end subroutine finalize_stateFilter + ! **** varSubstep **** subroutine initialize_varSubstep call in_varSubstep % initialize(dt,dtInit,dt_min,whole_step,nSubset,doAdjustTemp,firstSubStep,computeVegFlux,ixSolution,scalar,iStateSplit,fluxMask) @@ -885,7 +899,6 @@ end subroutine initialize_varSubstep subroutine finalize_varSubstep call io_varSubstep % finalize(firstFluxCall,fluxCount,ixSaturation) call out_varSubstep % finalize(dtMultiplier,nSubsteps,failedMinimumStep,reduceCoupledStep,tooMuchMelt,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); if (err>0) return; end if ! error control end subroutine finalize_varSubstep end subroutine opSplittin @@ -893,30 +906,29 @@ end subroutine opSplittin ! ********************************************************************************************************** ! private subroutine stateFilter: get a mask for the desired state variables ! ********************************************************************************************************** -subroutine stateFilter(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit,& - indx_data,stateMask,nSubset,err,message) +subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) - USE indexState_module,only:indxSubset ! get state indices + USE indexState_module,only:indxSubset ! get state indices implicit none ! input - integer(i4b),intent(in) :: ixCoupling ! index of coupling method (1,2) - integer(i4b),intent(in) :: ixSolution ! index of solution method (1,2) - integer(i4b),intent(in) :: ixStateThenDomain ! switch between full domain and sub domains - integer(i4b),intent(in) :: iStateTypeSplit ! index of the state type split - integer(i4b),intent(in) :: iDomainSplit ! index of the domain split - integer(i4b),intent(in) :: iStateSplit ! index of the layer split - type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU + type(in_type_stateFilter),intent(in) :: in_stateFilter ! indices + type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU ! output - logical(lgt),intent(out) :: stateMask(:) ! mask defining desired state variables - integer(i4b),intent(out) :: nSubset ! number of selected state variables for a given split - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + logical(lgt),intent(out) :: stateMask(:) ! mask defining desired state variables + type(out_type_stateFilter),intent(out) :: out_stateFilter ! number of selected state variables for a given split and error control ! local - integer(i4b),allocatable :: ixSubset(:) ! list of indices in the state subset - character(len=256) :: cmessage ! error message + integer(i4b),allocatable :: ixSubset(:) ! list of indices in the state subset + character(len=256) :: cmessage ! error message ! -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ! data structures associate(& + ! indices for splitting methods + ixCoupling => in_stateFilter % ixCoupling ,& ! intent(in): [i4b] index of coupling method (1,2) + ixSolution => in_stateFilter % ixSolution ,& ! intent(in): [i4b] index of solution method (1,2) + ixStateThenDomain => in_stateFilter % ixStateThenDomain ,& ! intent(in): [i4b] switch between full domain and sub domains + iStateTypeSplit => in_stateFilter % iStateTypeSplit ,& ! intent(in): [i4b] index of the state type split + iDomainSplit => in_stateFilter % iDomainSplit ,& ! intent(in): [i4b] index of the domain split + iStateSplit => in_stateFilter % iStateSplit ,& ! intent(in): [i4b] index of the layer split ! indices of model state variables ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain @@ -929,7 +941,11 @@ subroutine stateFilter(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,i ! number of layers nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in): [i4b] total number of layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers + ! output + nSubset => out_stateFilter % nSubset ,& ! intent(out): number of selected state variables for a given split + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage & ! intent(out): error message ) ! data structures ! -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control From ee4dfa608e326e7f1f18c0d3545f72ba9ac7d7ef Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 30 Oct 2023 20:00:33 +0900 Subject: [PATCH 0976/1472] the enthalpy derivatives should be nLayers dim --- build/source/engine/systemSolv.f90 | 9 +++++---- build/source/engine/varSubstep.f90 | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index c98ce0fa1..39f6a268d 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -120,6 +120,7 @@ subroutine systemSolv(& dt_cur, & ! intent(in): current stepsize dt, & ! intent(in): entire time step (s) nState, & ! intent(in): total number of state variables + nLayers, & ! intent(in): total number of layers firstSubStep, & ! intent(in): flag to denote first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation @@ -173,6 +174,7 @@ subroutine systemSolv(& real(rkind),intent(in) :: dt_cur ! current stepsize real(rkind),intent(in) :: dt ! entire time step for drainage pond rate integer(i4b),intent(in) :: nState ! total number of state variables + integer(i4b),intent(in) :: nLayers ! total number of layers logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call logical(lgt),intent(inout) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation @@ -253,8 +255,8 @@ subroutine systemSolv(& ! enthalpy derivatives real(rkind) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state - real(rkind) :: dEnthalpy_dTk(nState) ! derivatives in layer enthalpy w.r.t. temperature - real(rkind) :: dEnthalpy_dWat(nState) ! derivatives in layer enthalpy w.r.t. water state + real(rkind) :: dEnthalpy_dTk(nLayers) ! derivatives in layer enthalpy w.r.t. temperature + real(rkind) :: dEnthalpy_dWat(nLayers) ! derivatives in layer enthalpy w.r.t. water state ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- @@ -295,8 +297,7 @@ subroutine systemSolv(& ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] domain for desired model state variables ! layer geometry nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in): [i4b] total number of layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) & ! intent(in): [i4b] number of soil layers ) ! --------------------------------------------------------------------------------------- ! initialize error control diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 6928b98cc..80be14455 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -177,7 +177,7 @@ subroutine varSubstep(& ! flags logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt) :: checkMassBalance ! flag to check the mass balance - logical(lgt) :: checkNrgBalance + logical(lgt) :: checkNrgBalance ! flag to check the energy balance logical(lgt) :: waterBalanceError ! flag to denote that there is a water balance error logical(lgt) :: nrgFluxModified ! flag to denote that the energy fluxes were modified ! energy fluxes @@ -310,6 +310,7 @@ subroutine varSubstep(& dtSubstep, & ! intent(in): time step (s) whole_step, & ! intent(in): entire time step (s) nState, & ! intent(in): total number of state variables + nLayers, & ! intent(in): total number of layers firstSubStep, & ! intent(in): flag to denote first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation @@ -904,9 +905,9 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy - scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature From dbd7acba614dd1a582a7cba0d44ec58c3c492234 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 30 Oct 2023 20:01:18 +0900 Subject: [PATCH 0977/1472] turn on updateCp to test enthalpy formulation. Sundials works BE does not --- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/eval8summaWithPrime.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 7fa952b07..01eba3c54 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -215,7 +215,7 @@ subroutine eval8summa(& character(LEN=256) :: cmessage ! error message of downwind routine real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil - logical(lgt),parameter :: updateCp=.false. ! flag to indicate if we update Cp at each step + logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m ! -------------------------------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index cb4deff81..181bd6126 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -236,7 +236,7 @@ subroutine eval8summaWithPrime(& character(LEN=256) :: cmessage ! error message of downwind routine real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil - logical(lgt),parameter :: updateCp=.false. ! flag to indicate if we update Cp at each step + logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m ! -------------------------------------------------------------------------------------------------------------------------------- From c70ece42bf669a6f27a960b5ab87b457e13cbec9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 30 Oct 2023 20:00:33 +0900 Subject: [PATCH 0978/1472] the enthalpy derivatives should be nLayers dim --- build/source/engine/systemSolv.f90 | 9 +++++---- build/source/engine/varSubstep.f90 | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index c98ce0fa1..39f6a268d 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -120,6 +120,7 @@ subroutine systemSolv(& dt_cur, & ! intent(in): current stepsize dt, & ! intent(in): entire time step (s) nState, & ! intent(in): total number of state variables + nLayers, & ! intent(in): total number of layers firstSubStep, & ! intent(in): flag to denote first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation @@ -173,6 +174,7 @@ subroutine systemSolv(& real(rkind),intent(in) :: dt_cur ! current stepsize real(rkind),intent(in) :: dt ! entire time step for drainage pond rate integer(i4b),intent(in) :: nState ! total number of state variables + integer(i4b),intent(in) :: nLayers ! total number of layers logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call logical(lgt),intent(inout) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation @@ -253,8 +255,8 @@ subroutine systemSolv(& ! enthalpy derivatives real(rkind) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state - real(rkind) :: dEnthalpy_dTk(nState) ! derivatives in layer enthalpy w.r.t. temperature - real(rkind) :: dEnthalpy_dWat(nState) ! derivatives in layer enthalpy w.r.t. water state + real(rkind) :: dEnthalpy_dTk(nLayers) ! derivatives in layer enthalpy w.r.t. temperature + real(rkind) :: dEnthalpy_dWat(nLayers) ! derivatives in layer enthalpy w.r.t. water state ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- @@ -295,8 +297,7 @@ subroutine systemSolv(& ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] domain for desired model state variables ! layer geometry nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in): [i4b] total number of layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) & ! intent(in): [i4b] number of soil layers ) ! --------------------------------------------------------------------------------------- ! initialize error control diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 6928b98cc..80be14455 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -177,7 +177,7 @@ subroutine varSubstep(& ! flags logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt) :: checkMassBalance ! flag to check the mass balance - logical(lgt) :: checkNrgBalance + logical(lgt) :: checkNrgBalance ! flag to check the energy balance logical(lgt) :: waterBalanceError ! flag to denote that there is a water balance error logical(lgt) :: nrgFluxModified ! flag to denote that the energy fluxes were modified ! energy fluxes @@ -310,6 +310,7 @@ subroutine varSubstep(& dtSubstep, & ! intent(in): time step (s) whole_step, & ! intent(in): entire time step (s) nState, & ! intent(in): total number of state variables + nLayers, & ! intent(in): total number of layers firstSubStep, & ! intent(in): flag to denote first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation @@ -904,9 +905,9 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy - scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature From 38ba5fce2d521faf17d27739fd49ca4130a21849 Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 31 Oct 2023 04:10:26 -0600 Subject: [PATCH 0979/1472] Applied object-oriented methods to simplify call to indexSplit in opSplittin. --- build/source/dshare/data_types.f90 | 47 ++++++++++++++++++++++++-- build/source/engine/indexState.f90 | 53 +++++++++++++++++------------- build/source/engine/opSplittin.f90 | 19 ++++++++--- 3 files changed, 89 insertions(+), 30 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index c48a28007..97607e7b3 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -594,6 +594,25 @@ MODULE data_types end type out_type_stateFilter ! ** end stateFilter + ! ** indexSplit + type, public :: in_type_indexSplit ! class for intent(in) arguments in indexSplit call + integer(i4b) :: nSnow ! intent(in): number of snow layers + integer(i4b) :: nSoil ! intent(in): number of soil layers + integer(i4b) :: nLayers ! intent(in): total number of layers + integer(i4b) :: nSubset ! intent(in): number of states in the subset + contains + procedure :: initialize => initialize_in_indexSplit + end type in_type_indexSplit + + type, public :: out_type_indexSplit ! class for intent(out) arguments in indexSplit call + integer(i4b) :: err ! intent(out): error code + character(:),allocatable :: cmessage ! intent(out): error message + contains + procedure :: finalize => finalize_out_indexSplit + end type out_type_indexSplit + ! ** end indexSplit + + ! ** varSubstep type, public :: in_type_varSubstep ! class for intent(in) arguments in varSubstep call real(rkind) :: dt ! intent(in): time step (s) @@ -1287,7 +1306,7 @@ subroutine initialize_in_stateFilter(in_stateFilter,ixCoupling,ixSolution,ixStat end subroutine initialize_in_stateFilter subroutine finalize_out_stateFilter(out_stateFilter,nSubset,err,cmessage) - class(out_type_stateFilter),intent(in) :: out_stateFilter ! class object for intent(in) stateFilter arguments + class(out_type_stateFilter),intent(in) :: out_stateFilter ! class object for intent(out) stateFilter arguments integer(i4b),intent(out) :: nSubset ! intent(out): number of selected state variables for a given split integer(i4b),intent(out) :: err ! intent(out): error code character(*),intent(out) :: cmessage ! intent(out): error message @@ -1297,6 +1316,28 @@ subroutine finalize_out_stateFilter(out_stateFilter,nSubset,err,cmessage) end subroutine finalize_out_stateFilter ! **** end stateFilter **** + ! **** indexSplit **** + subroutine initialize_in_indexSplit(in_indexSplit,nSnow,nSoil,nLayers,nSubset) + class(in_type_indexSplit),intent(out) :: in_indexSplit ! class object for intent(in) indexSplit arguments + integer(i4b),intent(in) :: nSnow ! intent(in): number of snow layers + integer(i4b),intent(in) :: nSoil ! intent(in): number of soil layers + integer(i4b),intent(in) :: nLayers ! intent(in): total number of layers + integer(i4b),intent(in) :: nSubset ! intent(in): number of states in the subset + in_indexSplit % nSnow = nSnow ! intent(in): number of snow layers + in_indexSplit % nSoil = nSoil ! intent(in): number of soil layers + in_indexSplit % nLayers = nLayers ! intent(in): total number of layers + in_indexSplit % nSubset = nSubset ! intent(in): number of states in the subset + end subroutine initialize_in_indexSplit + + subroutine finalize_out_indexSplit(out_indexSplit,err,cmessage) + class(out_type_indexSplit),intent(in) :: out_indexSplit ! class object for intent(out) indexSplit arguments + integer(i4b),intent(out) :: err ! intent(out): error code + character(*),intent(out) :: cmessage ! intent(out): error message + err = out_indexSplit % err ! intent(out): error code + cmessage = out_indexSplit % cmessage ! intent(out): error message + end subroutine finalize_out_indexSplit + ! **** end indexSplit **** + ! **** varSubstep **** subroutine initialize_in_varSubstep(in_varSubstep,dt,dtInit,dt_min,whole_step,nSubset,& doAdjustTemp,firstSubStep,computeVegFlux,ixSolution,scalar,iStateSplit,fluxMask) @@ -1329,7 +1370,7 @@ subroutine initialize_in_varSubstep(in_varSubstep,dt,dtInit,dt_min,whole_step,nS end subroutine initialize_in_varSubstep subroutine initialize_io_varSubstep(io_varSubstep,firstFluxCall,fluxCount,ixSaturation) - class(io_type_varSubstep),intent(out) :: io_varSubstep ! class object for intent(in) varSubstep arguments + class(io_type_varSubstep),intent(out) :: io_varSubstep ! class object for intent(inout) varSubstep arguments logical(lgt),intent(in) :: firstFluxCall ! flag to indicate if we are processing the first flux call type(var_ilength),intent(in) :: fluxCount ! number of times fluxes are updated (should equal nsubstep) integer(i4b),intent(in) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) @@ -1341,7 +1382,7 @@ subroutine initialize_io_varSubstep(io_varSubstep,firstFluxCall,fluxCount,ixSatu end subroutine initialize_io_varSubstep subroutine finalize_io_varSubstep(io_varSubstep,firstFluxCall,fluxCount,ixSaturation) - class(io_type_varSubstep),intent(in) :: io_varSubstep ! class object for intent(in) varSubstep arguments + class(io_type_varSubstep),intent(in) :: io_varSubstep ! class object for intent(inout) varSubstep arguments logical(lgt),intent(out) :: firstFluxCall ! flag to indicate if we are processing the first flux call type(var_ilength),intent(out) :: fluxCount ! number of times fluxes are updated (should equal nsubstep) integer(i4b),intent(out) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) diff --git a/build/source/engine/indexState.f90 b/build/source/engine/indexState.f90 index 9c87d9833..be42a07ce 100644 --- a/build/source/engine/indexState.f90 +++ b/build/source/engine/indexState.f90 @@ -24,7 +24,8 @@ module indexState_module USE nrtype ! derived types to define the data structures -USE data_types,only:var_ilength ! data vector with variable length dimension (i4b) +USE data_types,only:var_ilength ! data vector with variable length dimension (i4b) +USE data_types,only:in_type_indexSplit,out_type_indexSplit ! classes for indexSplit subroutine arguments ! missing data USE globalData,only:integerMissing ! missing integer @@ -276,41 +277,44 @@ end subroutine indexState ! ********************************************************************************************************** ! public subroutine indexSplit: define list of indices for each state variable ! ********************************************************************************************************** - subroutine indexSplit(stateSubsetMask, & ! intent(in) : logical vector (.true. if state is in the subset) - nSnow,nSoil,nLayers,nSubset, & ! intent(in) : number of snow and soil layers, and total number of layers + subroutine indexSplit(in_indexSplit, & ! intent(in) : number of model layers and states in a subset + stateSubsetMask, & ! intent(in) : logical vector (.true. if state is in the subset) indx_data, & ! intent(inout) : index data structure - err,message) ! intent(out) : error control + out_indexSplit) ! intent(out) : error control ! external modules USE f2008funcs_module,only:findIndex ! finds the index of the first value within a vector USE nr_utility_module,only:arth ! creates a sequence of numbers (start, incr, n) implicit none ! -------------------------------------------------------------------------------------------------------------------------------- ! input - logical(lgt),intent(in) :: stateSubsetMask(:) ! logical vector (.true. if state is in the subset) - integer(i4b),intent(in) :: nSnow,nSoil,nLayers,nSubset ! number of snow and soil layers, total number of layers, and number of states in the subset - type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers + type(in_type_indexSplit),intent(in) :: in_indexSplit ! number of model layers and states in a subset + logical(lgt),intent(in) :: stateSubsetMask(:) ! logical vector (.true. if state is in the subset) + type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers ! output - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + type(out_type_indexSplit),intent(out) :: out_indexSplit ! error control ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables - integer(i4b) :: iVar ! variable index - integer(i4b) :: ixVegWat ! index of total water in the vegetation canopy - integer(i4b) :: ixVegLiq ! index of liquid water in the vegetation canopy - integer(i4b) :: ixTopWat ! index of upper-most total water state in the snow-soil subdomain - integer(i4b) :: ixTopLiq ! index of upper-most liquid water state in the snow-soil subdomain - integer(i4b) :: ixTopMat ! index of upper-most total water matric potential state in the soil subdomain - integer(i4b) :: ixTopLMP ! index of upper-most liquid water matric potential state in the soil subdomain - integer(i4b),dimension(nSubset) :: ixSequence ! sequential index in model state vector - logical(lgt),dimension(nSubset) :: stateTypeMask ! mask of state vector for specific state subsets - logical(lgt),dimension(nLayers) :: volFracWat_mask ! mask of layers within the snow+soil domain - logical(lgt),dimension(nSoil) :: matricHead_mask ! mask of layers within the soil domain - character(len=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iVar ! variable index + integer(i4b) :: ixVegWat ! index of total water in the vegetation canopy + integer(i4b) :: ixVegLiq ! index of liquid water in the vegetation canopy + integer(i4b) :: ixTopWat ! index of upper-most total water state in the snow-soil subdomain + integer(i4b) :: ixTopLiq ! index of upper-most liquid water state in the snow-soil subdomain + integer(i4b) :: ixTopMat ! index of upper-most total water matric potential state in the soil subdomain + integer(i4b) :: ixTopLMP ! index of upper-most liquid water matric potential state in the soil subdomain + integer(i4b),dimension(in_indexSplit % nSubset) :: ixSequence ! sequential index in model state vector + logical(lgt),dimension(in_indexSplit % nSubset) :: stateTypeMask ! mask of state vector for specific state subsets + logical(lgt),dimension(in_indexSplit % nLayers) :: volFracWat_mask ! mask of layers within the snow+soil domain + logical(lgt),dimension(in_indexSplit % nSoil) :: matricHead_mask ! mask of layers within the soil domain + character(len=256) :: cmessage ! error message of downwind routine ! ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ! ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ! make association to variables in the data structures fullState: associate(& - + ! number of snow and soil layers, total number of layers, and number of states in the subset + nSnow => in_indexSplit % nSnow ,& ! intent(in): [i4b] number of snow layers + nSoil => in_indexSplit % nSoil ,& ! intent(in): [i4b] number of soil layers + nLayers => in_indexSplit % nLayers ,& ! intent(in): [i4b] total number of layers + nSubset => in_indexSplit % nSubset ,& ! intent(in): [i4b] number of states in the subset ! indices of model state variables for the vegetation domain ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable @@ -357,8 +361,11 @@ subroutine indexSplit(stateSubsetMask, & ! intent(in) : logical v nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) & ! intent(in): [i4b] number of hydrology variables in the soil domain + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + ! error control + err => out_indexSplit % err ,& ! intent(out): [i4b] error code + message => out_indexSplit % cmessage & ! intent(out): [character] error message ) ! association to variables in the data structures ! ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 4c849b097..69c38de47 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -96,6 +96,7 @@ module opSplittin_module zLookup, & ! lookup tables model_options, & ! defines the model decisions in_type_statefilter,out_type_statefilter, & ! classes for stateFilter objects + in_type_indexSplit,out_type_indexSplit, & ! classes for indexSplit objects in_type_varSubstep,io_type_varSubstep,out_type_varSubstep ! classes for varSubstep objects ! look-up values for the numerical method @@ -293,6 +294,7 @@ subroutine opSplittin(& ! ------------------------ classes for subroutine arguments (classes defined in data_types module) ------------------------ ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** type(in_type_stateFilter) :: in_stateFilter; type(out_type_stateFilter) :: out_stateFilter; ! stateFilter arguments + type(in_type_indexSplit) :: in_indexSplit; type(out_type_indexSplit) :: out_indexSplit; ! indexSplit arguments type(in_type_varSubstep) :: in_varSubstep; type(io_type_varSubstep) :: io_varSubstep; type(out_type_varSubstep) :: out_varSubstep; ! varSubstep arguments ! --------------------------------------------------------------------------------------- @@ -496,10 +498,9 @@ subroutine opSplittin(& ! * assemble vectors for a given split... ! --------------------------------------- ! get indices for a given split - call indexSplit(stateMask, & ! intent(in) : logical vector (.true. if state is in the subset) - nSnow,nSoil,nLayers,nSubset, & ! intent(in) : number of snow and soil layers, and total number of layers - indx_data, & ! intent(inout) : index data structure - err,cmessage) ! intent(out) : error control + call initialize_indexSplit + call indexSplit(in_indexSplit,stateMask,indx_data,out_indexSplit) + call finalize_indexSplit if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! ----- @@ -890,6 +891,16 @@ subroutine finalize_stateFilter call out_stateFilter % finalize(nSubset,err,cmessage) end subroutine finalize_stateFilter + ! **** indexSplit **** + subroutine initialize_indexSplit + call in_indexSplit % initialize(nSnow,nSoil,nLayers,nSubset) + end subroutine initialize_indexSplit + + subroutine finalize_indexSplit + call out_indexSplit % finalize(err,cmessage) + end subroutine finalize_indexSplit + ! **** end indexSplit **** + ! **** varSubstep **** subroutine initialize_varSubstep call in_varSubstep % initialize(dt,dtInit,dt_min,whole_step,nSubset,doAdjustTemp,firstSubStep,computeVegFlux,ixSolution,scalar,iStateSplit,fluxMask) From a23ad9bac02a84aa8a12892a728a1bf4e5c7fe2a Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 1 Nov 2023 05:01:38 -0600 Subject: [PATCH 0980/1472] Added update_fluxMask subroutine to contains block of opSplittin. --- build/source/engine/opSplittin.f90 | 252 +++++++++++++---------------- 1 file changed, 113 insertions(+), 139 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 69c38de47..c1f17a44f 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -501,149 +501,14 @@ subroutine opSplittin(& call initialize_indexSplit call indexSplit(in_indexSplit,stateMask,indx_data,out_indexSplit) call finalize_indexSplit - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! ----- ! * define the mask of the fluxes used... ! --------------------------------------- - - ! identify the type of state for the states in the subset - stateSubset: associate(ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] indices of state types - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) - ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat ,& ! intent(in): [i4b(:)] list of indices for all active layers (inactive=integerM - ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ) ! intent(in): [i4b(:)] indices defining the type of the domain (iname_veg, iname_snow, iname_soil) - - ! loop through flux variables - do iVar=1,size(flux_meta) - - ! * identify flux mask for the fully coupled solution - if(ixCoupling==fullyCoupled)then - desiredFlux = any(ixStateType_subset==flux2state_orig(iVar)%state1) .or. any(ixStateType_subset==flux2state_orig(iVar)%state2) - - ! make sure firstFluxCall fluxes are included in the mask - if (firstFluxCall .and. addFirstFlux) then - if (iVar==iLookFlux%scalarSoilResistance) desiredFlux = .true. - if (iVar==iLookFlux%scalarStomResistSunlit) desiredFlux = .true. - if (iVar==iLookFlux%scalarStomResistShaded) desiredFlux = .true. - if (iVar==iLookFlux%scalarPhotosynthesisSunlit) desiredFlux = .true. - if (iVar==iLookFlux%scalarPhotosynthesisShaded) desiredFlux = .true. - endif - - fluxMask%var(iVar)%dat = desiredFlux - - ! * identify flux mask for the split solution - else - - ! identify the flux mask for a given state split - select case(iStateTypeSplit) - case(nrgSplit); desiredFlux = any(ixStateType_subset==flux2state_orig(iVar)%state1) .or. any(ixStateType_subset==flux2state_orig(iVar)%state2) - case(massSplit); desiredFlux = any(ixStateType_subset==flux2state_liq(iVar)%state1) .or. any(ixStateType_subset==flux2state_liq(iVar)%state2) - case default; err=20; message=trim(message)//'unable to identify split based on state type'; return - end select - - ! make sure firstFluxCall fluxes are included in the mask - if (firstFluxCall .and. addFirstFlux) then - if (iVar==iLookFlux%scalarSoilResistance) desiredFlux = .true. - if (iVar==iLookFlux%scalarStomResistSunlit) desiredFlux = .true. - if (iVar==iLookFlux%scalarStomResistShaded) desiredFlux = .true. - if (iVar==iLookFlux%scalarPhotosynthesisSunlit) desiredFlux = .true. - if (iVar==iLookFlux%scalarPhotosynthesisShaded) desiredFlux = .true. - endif - - ! no domain splitting - if(nDomains==1)then - fluxMask%var(iVar)%dat = desiredFlux - - ! domain splitting - else - - ! initialize to .false. - fluxMask%var(iVar)%dat = .false. - - ! only need to proceed if the flux is desired - if(desiredFlux)then - - ! different domain splitting operations - select case(iDomainSplit) - - ! canopy fluxes -- (:1) gets the upper boundary(0) if it exists - case(vegSplit) - - ! vector solution (should only be present for energy) - if(ixSolution==vector)then - fluxMask%var(iVar)%dat(:1) = desiredFlux - if(ixStateThenDomain>1 .and. iStateTypeSplit/=nrgSplit)then - message=trim(message)//'only expect a vector solution for the vegetation domain for energy' - err=20; return - endif - - ! scalar solution - else - fluxMask%var(iVar)%dat(:1) = desiredFlux - endif - - ! fluxes through snow and soil - case(snowSplit,soilSplit) - - ! loop through layers - do iLayer=1,nLayers - if(ixlayerActive(iLayer)/=integerMissing)then - - ! get the offset (ixLayerActive=1,2,3,...nLayers, and soil vectors nSnow+1, nSnow+2, ..., nLayers) - iOffset = merge(nSnow, 0, flux_meta(iVar)%vartype==iLookVarType%midSoil .or. flux_meta(iVar)%vartype==iLookVarType%ifcSoil) - jLayer = iLayer-iOffset - - ! identify the minimum layer - select case(flux_meta(iVar)%vartype) - case(iLookVarType%ifcToto, iLookVarType%ifcSnow, iLookVarType%ifcSoil); minLayer=merge(jLayer-1, jLayer, jLayer==1) - case(iLookVarType%midToto, iLookVarType%midSnow, iLookVarType%midSoil); minLayer=jLayer - case default; minLayer=integerMissing - end select - - ! set desired layers - select case(flux_meta(iVar)%vartype) - case(iLookVarType%midToto,iLookVarType%ifcToto); fluxMask%var(iVar)%dat(minLayer:jLayer) = desiredFlux - case(iLookVarType%midSnow,iLookVarType%ifcSnow); if(iLayer<=nSnow) fluxMask%var(iVar)%dat(minLayer:jLayer) = desiredFlux - case(iLookVarType%midSoil,iLookVarType%ifcSoil); if(iLayer> nSnow) fluxMask%var(iVar)%dat(minLayer:jLayer) = desiredFlux - end select - - ! add hydrology states for scalar variables - if(iStateTypeSplit==massSplit .and. flux_meta(iVar)%vartype==iLookVarType%scalarv)then - select case(iDomainSplit) - case(snowSplit); if(iLayer==nSnow) fluxMask%var(iVar)%dat = desiredFlux - case(soilSplit); if(iLayer==nSnow+1) fluxMask%var(iVar)%dat = desiredFlux - end select - endif ! if hydrology split and scalar - - endif ! if the layer is active - end do ! looping through layers - - ! fluxes through aquifer - case(aquiferSplit) - fluxMask%var(iVar)%dat(:) = desiredFlux ! only would be firstFluxCall variables, no aquifer fluxes - - ! check - case default; err=20; message=trim(message)//'unable to identify split based on domain type'; return - end select ! domain split - - endif ! if flux is desired - - endif ! domain splitting - endif ! not fully coupled - - ! define if the flux is desired - if(desiredFlux) neededFlux(iVar)=.true. - !if(desiredFlux) print*, flux_meta(iVar)%varname, fluxMask%var(iVar)%dat - - ! * check - if( globalPrintFlag .and. count(fluxMask%var(iVar)%dat)>0 )& - print*, trim(flux_meta(iVar)%varname) - - end do ! (loop through fluxes) - - end associate stateSubset - + call update_fluxMask + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + ! ******************************************************************************************************************************* ! ***** trial with a given solution method... @@ -911,6 +776,115 @@ subroutine finalize_varSubstep call io_varSubstep % finalize(firstFluxCall,fluxCount,ixSaturation) call out_varSubstep % finalize(dtMultiplier,nSubsteps,failedMinimumStep,reduceCoupledStep,tooMuchMelt,err,cmessage) end subroutine finalize_varSubstep + + subroutine update_fluxMask + ! *** update the fluxMask data structure *** + do iVar=1,size(flux_meta) ! loop through flux variables + + if (ixCoupling==fullyCoupled) then ! * identify flux mask for the fully coupled solution + associate(ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat) ! intent(in): [i4b(:)] indices of state types + desiredFlux = any(ixStateType_subset==flux2state_orig(iVar)%state1) .or. any(ixStateType_subset==flux2state_orig(iVar)%state2) + end associate + + ! make sure firstFluxCall fluxes are included in the mask + if (firstFluxCall .and. addFirstFlux) then + if (iVar==iLookFlux%scalarSoilResistance) desiredFlux = .true. + if (iVar==iLookFlux%scalarStomResistSunlit) desiredFlux = .true. + if (iVar==iLookFlux%scalarStomResistShaded) desiredFlux = .true. + if (iVar==iLookFlux%scalarPhotosynthesisSunlit) desiredFlux = .true. + if (iVar==iLookFlux%scalarPhotosynthesisShaded) desiredFlux = .true. + end if + + fluxMask%var(iVar)%dat = desiredFlux + + else ! * identify flux mask for the split solution + + associate(ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat) ! intent(in): [i4b(:)] indices of state types + select case(iStateTypeSplit) ! identify the flux mask for a given state split + case(nrgSplit); desiredFlux = any(ixStateType_subset==flux2state_orig(iVar)%state1) .or. any(ixStateType_subset==flux2state_orig(iVar)%state2) + case(massSplit); desiredFlux = any(ixStateType_subset==flux2state_liq(iVar)%state1) .or. any(ixStateType_subset==flux2state_liq(iVar)%state2) + case default; err=20; message=trim(message)//'unable to identify split based on state type'; return + end select + end associate + + ! make sure firstFluxCall fluxes are included in the mask + if (firstFluxCall .and. addFirstFlux) then + if (iVar==iLookFlux%scalarSoilResistance) desiredFlux = .true. + if (iVar==iLookFlux%scalarStomResistSunlit) desiredFlux = .true. + if (iVar==iLookFlux%scalarStomResistShaded) desiredFlux = .true. + if (iVar==iLookFlux%scalarPhotosynthesisSunlit) desiredFlux = .true. + if (iVar==iLookFlux%scalarPhotosynthesisShaded) desiredFlux = .true. + end if + + if (nDomains==1) then ! no domain splitting + fluxMask%var(iVar)%dat = desiredFlux + else ! domain splitting + fluxMask%var(iVar)%dat = .false. ! initialize to .false. + if (desiredFlux) then ! only need to proceed if the flux is desired + select case(iDomainSplit) ! different domain splitting operations + case(vegSplit) ! canopy fluxes -- (:1) gets the upper boundary(0) if it exists + if (ixSolution==vector) then ! vector solution (should only be present for energy) + fluxMask%var(iVar)%dat(:1) = desiredFlux + if (ixStateThenDomain>1 .and. iStateTypeSplit/=nrgSplit) then + message=trim(message)//'only expect a vector solution for the vegetation domain for energy' + err=20; return + end if + else ! scalar solution + fluxMask%var(iVar)%dat(:1) = desiredFlux + end if + case(snowSplit,soilSplit) ! fluxes through snow and soil + + do iLayer=1,nLayers! loop through layers + associate(ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat) ! intent(in): [i4b(:)] indices for all active layers (inactive=integerMissing) + if (ixLayerActive(iLayer)/=integerMissing) then + + ! get the offset (ixLayerActive=1,2,3,...nLayers, and soil vectors nSnow+1, nSnow+2, ..., nLayers) + iOffset = merge(nSnow, 0, flux_meta(iVar)%vartype==iLookVarType%midSoil .or. flux_meta(iVar)%vartype==iLookVarType%ifcSoil) + jLayer = iLayer-iOffset + + ! identify the minimum layer + select case(flux_meta(iVar)%vartype) + case(iLookVarType%ifcToto, iLookVarType%ifcSnow, iLookVarType%ifcSoil); minLayer=merge(jLayer-1, jLayer, jLayer==1) + case(iLookVarType%midToto, iLookVarType%midSnow, iLookVarType%midSoil); minLayer=jLayer + case default; minLayer=integerMissing + end select + + ! set desired layers + select case(flux_meta(iVar)%vartype) + case(iLookVarType%midToto,iLookVarType%ifcToto); fluxMask%var(iVar)%dat(minLayer:jLayer) = desiredFlux + case(iLookVarType%midSnow,iLookVarType%ifcSnow); if (iLayer<=nSnow) fluxMask%var(iVar)%dat(minLayer:jLayer) = desiredFlux + case(iLookVarType%midSoil,iLookVarType%ifcSoil); if (iLayer> nSnow) fluxMask%var(iVar)%dat(minLayer:jLayer) = desiredFlux + end select + + ! add hydrology states for scalar variables + if (iStateTypeSplit==massSplit .and. flux_meta(iVar)%vartype==iLookVarType%scalarv) then + select case(iDomainSplit) + case(snowSplit); if(iLayer==nSnow) fluxMask%var(iVar)%dat = desiredFlux + case(soilSplit); if(iLayer==nSnow+1) fluxMask%var(iVar)%dat = desiredFlux + end select + end if ! if hydrology split and scalar + + end if ! if the layer is active + end associate + end do ! looping through layers + + case(aquiferSplit) ! fluxes through aquifer + fluxMask%var(iVar)%dat(:) = desiredFlux ! only would be firstFluxCall variables, no aquifer fluxes + case default; err=20; message=trim(message)//'unable to identify split based on domain type'; return ! check + end select ! domain split + end if ! end if flux is desired + end if ! end if domain splitting + end if ! end if not fully coupled + + ! define if the flux is desired + if (desiredFlux) neededFlux(iVar)=.true. + !if(desiredFlux) print*, flux_meta(iVar)%varname, fluxMask%var(iVar)%dat + + if ( globalPrintFlag .and. count(fluxMask%var(iVar)%dat)>0 ) print*, trim(flux_meta(iVar)%varname) ! * check + + end do ! end looping through fluxes + + end subroutine update_fluxMask end subroutine opSplittin From 2beba86db14f1551995a22564c5f2ed694f3c6d7 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 2 Nov 2023 18:24:33 +0900 Subject: [PATCH 0981/1472] Cm deriviatives --- build/source/dshare/get_ixname.f90 | 13 ++- build/source/dshare/popMetadat.f90 | 11 ++- build/source/dshare/var_lookup.f90 | 18 ++-- build/source/engine/computEnthalpy.f90 | 78 ++++++++--------- build/source/engine/computHeatCap.f90 | 91 ++++++++++++-------- build/source/engine/computJacob.f90 | 39 ++++++--- build/source/engine/computJacobWithPrime.f90 | 29 ++++--- build/source/engine/computResid.f90 | 4 +- build/source/engine/computResidWithPrime.f90 | 4 +- build/source/engine/eval8summa.f90 | 21 +++-- build/source/engine/eval8summaWithPrime.f90 | 20 +++-- build/source/engine/t2enthalpy.f90 | 23 +++-- build/source/engine/updateVars.f90 | 56 +++++++----- 13 files changed, 246 insertions(+), 161 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 60a6f9ccd..dde76f749 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -486,6 +486,7 @@ function get_ixdiag(varName) case('scalarCanopyDepth' ); get_ixdiag = iLookDIAG%scalarCanopyDepth ! canopy depth (m) case('scalarGreenVegFraction' ); get_ixdiag = iLookDIAG%scalarGreenVegFraction ! green vegetation fraction used to compute LAI (-) case('scalarBulkVolHeatCapVeg' ); get_ixdiag = iLookDIAG%scalarBulkVolHeatCapVeg ! bulk volumetric heat capacity of vegetation (J m-3 K-1) + case('scalarCanopyCm' ); get_ixdiag = iLookDIAG%scalarCanopyCm ! Cm of canopy (J kg-1 K-1) case('scalarCanopyEmissivity' ); get_ixdiag = iLookDIAG%scalarCanopyEmissivity ! effective canopy emissivity (-) case('scalarRootZoneTemp' ); get_ixdiag = iLookDIAG%scalarRootZoneTemp ! average temperature of the root zone (K) case('scalarLAI' ); get_ixdiag = iLookDIAG%scalarLAI ! one-sided leaf area index (m2 m-2) @@ -501,6 +502,7 @@ function get_ixdiag(varName) case('scalarVolHtCap_soil' ); get_ixdiag = iLookDIAG%scalarVolHtCap_soil ! volumetric heat capacity dry soil (J m-3 K-1) case('scalarVolHtCap_water' ); get_ixdiag = iLookDIAG%scalarVolHtCap_water ! volumetric heat capacity liquid wat (J m-3 K-1) case('mLayerVolHtCapBulk' ); get_ixdiag = iLookDIAG%mLayerVolHtCapBulk ! volumetric heat capacity in each layer (J m-3 K-1) + case('mLayerCm' ); get_ixdiag = iLookDIAG%mLayerCm ! Cm of each layer (J kg-1 K-1) case('scalarLambda_drysoil' ); get_ixdiag = iLookDIAG%scalarLambda_drysoil ! thermal conductivity of dry soil (W m-1) case('scalarLambda_wetsoil' ); get_ixdiag = iLookDIAG%scalarLambda_wetsoil ! thermal conductivity of wet soil (W m-1) case('mLayerThermalC' ); get_ixdiag = iLookDIAG%mLayerThermalC ! thermal conductivity at the mid-point of each layer (W m-1 K-1) @@ -755,18 +757,21 @@ function get_ixderiv(varName) case('scalarCanopyLiqDeriv' ); get_ixderiv = iLookDERIV%scalarCanopyLiqDeriv ! derivative in (throughfall + canopy drainage) w.r.t. canopy liquid water (s-1) case('scalarThroughfallRainDeriv' ); get_ixderiv = iLookDERIV%scalarThroughfallRainDeriv ! derivative in throughfall w.r.t. canopy liquid water (s-1) case('scalarCanopyLiqDrainageDeriv' ); get_ixderiv = iLookDERIV%scalarCanopyLiqDrainageDeriv ! derivative in canopy drainage w.r.t. canopy liquid water (s-1) - ! energy derivatives that might be treated as constant if heat capacity and thermal conductivity not updated + ! energy derivatives that might be treated as constant if heat capacity and thermal conductivity not updated case('dVolHtCapBulk_dPsi0' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dPsi0 ! derivative in bulk heat capacity w.r.t. matric potential case('dVolHtCapBulk_dTheta' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTheta ! derivative in bulk heat capacity w.r.t. volumetric water content - case('dVolHtCapBulk_dCanWat' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. volumetric water content + case('dVolHtCapBulk_dCanWat' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. canopy volumetric water content case('dVolHtCapBulk_dTk' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTk ! derivative in bulk heat capacity w.r.t. temperature - case('dVolHtCapBulk_dTkCanopy' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature + case('dVolHtCapBulk_dTkCanopy' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. canopy temperature case('dThermalC_dTempAbove' ); get_ixderiv = iLookDERIV%dThermalC_dTempAbove ! derivative in the thermal conductivity w.r.t. energy state in the layer above case('dThermalC_dTempBelow' ); get_ixderiv = iLookDERIV%dThermalC_dTempBelow ! derivative in the thermal conductivity w.r.t. energy state in the layer above case('dThermalC_dWatAbove' ); get_ixderiv = iLookDERIV%dThermalC_dWatAbove ! derivative in the thermal conductivity w.r.t. water state in the layer above case('dThermalC_dWatBelow' ); get_ixderiv = iLookDERIV%dThermalC_dWatBelow ! derivative in the thermal conductivity w.r.t. water state in the layer above case('dNrgFlux_dTempAbove' ); get_ixderiv = iLookDERIV%dNrgFlux_dTempAbove ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) case('dNrgFlux_dTempBelow' ); get_ixderiv = iLookDERIV%dNrgFlux_dTempBelow ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) + ! energy derivatives that might be treated as constant if Cm not updated + case('dCm_dTk' ); get_ixderiv = iLookDERIV%dCm_dTk ! derivative in Cm w.r.t. temperature (J kg K-2) + case('dCm_dTkCanopy' ); get_ixderiv = iLookDERIV%dCm_dTkCanopy ! derivative in Cm w.r.t. canopy temperature (J kg K-2) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below case('dNrgFlux_dWatAbove' ); get_ixderiv = iLookDERIV%dNrgFlux_dWatAbove ! derivatives in the flux w.r.t. water state temperature in the layer above case('dNrgFlux_dWatBelow' ); get_ixderiv = iLookDERIV%dNrgFlux_dWatBelow ! derivatives in the flux w.r.t. water state in the layer below @@ -806,6 +811,8 @@ function get_ixderiv(varName) ! derivatives in time case( 'mLayerdTemp_dt' ); get_ixderiv = iLookDERIV%mLayerdTemp_dt ! timestep change in layer temperature case( 'scalarCanopydTemp_dt' ); get_ixderiv = iLookDERIV%scalarCanopydTemp_dt ! timestep change in canopy temperature + case( 'mLayerdWat_dt' ); get_ixderiv = iLookDERIV%mLayerdWat_dt ! timestep change in layer volumetric fraction of total water + case( 'scalarCanopydWat_dt' ); get_ixderiv = iLookDERIV%scalarCanopydWat_dt ! timestep change in canopy water content case default get_ixderiv = integerMissing diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index d1f86a04d..e527cc7b4 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -341,6 +341,7 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%scalarCanopyDepth) = var_info('scalarCanopyDepth' , 'canopy depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarGreenVegFraction) = var_info('scalarGreenVegFraction' , 'green vegetation fraction (used to compute LAI)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarBulkVolHeatCapVeg) = var_info('scalarBulkVolHeatCapVeg' , 'bulk volumetric heat capacity of vegetation' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarCanopyCm) = var_info('scalarCanopyCm' , 'Cm of canopy' , 'J kg-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarCanopyEmissivity) = var_info('scalarCanopyEmissivity' , 'effective canopy emissivity' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarRootZoneTemp) = var_info('scalarRootZoneTemp' , 'average temperature of the root zone' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarLAI) = var_info('scalarLAI' , 'one-sided leaf area index' , 'm2 m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) @@ -356,6 +357,7 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%scalarVolHtCap_soil) = var_info('scalarVolHtCap_soil' , 'volumetric heat capacity dry soil' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarVolHtCap_water) = var_info('scalarVolHtCap_water' , 'volumetric heat capacity liquid wat' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%mLayerVolHtCapBulk) = var_info('mLayerVolHtCapBulk' , 'volumetric heat capacity in each layer' , 'J m-3 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%mLayerCm) = var_info('mLayerCm' , 'Cm of each layer' , 'J kg-1 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarLambda_drysoil) = var_info('scalarLambda_drysoil' , 'thermal conductivity of dry soil' , 'W m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarLambda_wetsoil) = var_info('scalarLambda_wetsoil' , 'thermal conductivity of wet soil' , 'W m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%mLayerThermalC) = var_info('mLayerThermalC' , 'thermal conductivity at the mid-point of each layer' , 'W m-1 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) @@ -583,13 +585,16 @@ subroutine popMetadat(err,message) ! energy derivatives that might be treated as constant if heat capacity and thermal conductivity not updated deriv_meta(iLookDERIV%dVolHtCapBulk_dPsi0) = var_info('dVolHtCapBulk_dPsi0' , 'derivative in bulk heat capacity w.r.t. matric potential' , 'J m-4 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dVolHtCapBulk_dTheta) = var_info('dVolHtCapBulk_dTheta' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dCanWat) = var_info('dVolHtCapBulk_dCanWat' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dCanWat) = var_info('dVolHtCapBulk_dCanWat' , 'derivative in bulk heat capacity w.r.t. canopy volumetric water content', 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dVolHtCapBulk_dTk) = var_info('dVolHtCapBulk_dTk' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dTkCanopy) = var_info('dVolHtCapBulk_dTkCanopy' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dTkCanopy) = var_info('dVolHtCapBulk_dTkCanopy' , 'derivative in bulk heat capacity w.r.t. canopy temperature' , 'J m-3 K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dThermalC_dTempAbove) = var_info('dThermalC_dTempAbove' , 'derivative in the thermal conductivity w.r.t. energy in the layer above','J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dThermalC_dTempBelow) = var_info('dThermalC_dTempBelow' , 'derivative in the thermal conductivity w.r.t. energy in the layer above','J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dThermalC_dWatAbove) = var_info('dThermalC_dWatAbove' , 'derivative in the thermal conductivity w.r.t. water in the layer above', 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dThermalC_dWatBelow) = var_info('dThermalC_dWatBelow' , 'derivative in the thermal conductivity w.r.t. water in the layer above', 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + ! energy derivatives that might be treated as constant if Cm not updated + deriv_meta(iLookDERIV%dCm_dTk) = var_info('dCm_dTk' , 'derivative in Cm w.r.t. temperature' , 'J kg K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCm_dTkCanopy) = var_info('dCm_dTkCanopy' , 'derivative in Cm w.r.t. canopy temperature' , 'J kg K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below deriv_meta(iLookDERIV%dNrgFlux_dTempAbove) = var_info('dNrgFlux_dTempAbove' , 'derivatives in the flux w.r.t. temperature in the layer above' , 'J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dNrgFlux_dTempBelow) = var_info('dNrgFlux_dTempBelow' , 'derivatives in the flux w.r.t. temperature in the layer below' , 'J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) @@ -632,6 +637,8 @@ subroutine popMetadat(err,message) ! derivatives in time deriv_meta(iLookDERIV%mLayerdTemp_dt) = var_info('mLayerdTemp_dt' , 'timestep change in layer temperature' , 'K' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%scalarCanopydTemp_dt) = var_info('scalarCanopydTemp_dt' , 'timestep change in canopy temperature' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%mLayerdWat_dt) = var_info('mLayerdWat_dt' , 'timestep change in layer volumetric fraction of total water' , '-' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%scalarCanopydWat_dt) = var_info('scalarCanopydWat_dt' , 'timestep change in canopy water content' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! ----- ! * basin-wide runoff and aquifer fluxes... ! ----------------------------------------- diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 909bbefb6..ac8b6ffd9 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -366,6 +366,7 @@ MODULE var_lookup integer(i4b) :: scalarCanopyDepth = integerMissing ! canopy depth (m) integer(i4b) :: scalarGreenVegFraction = integerMissing ! green vegetation fraction used to compute LAI (-) integer(i4b) :: scalarBulkVolHeatCapVeg = integerMissing ! bulk volumetric heat capacity of vegetation (J m-3 K-1) + integer(i4b) :: scalarCanopyCm = integerMissing ! Cm of canopy (J kg-1 K-1) integer(i4b) :: scalarCanopyEmissivity = integerMissing ! effective canopy emissivity (-) integer(i4b) :: scalarRootZoneTemp = integerMissing ! average temperature of the root zone (K) integer(i4b) :: scalarLAI = integerMissing ! one-sided leaf area index (m2 m-2) @@ -381,6 +382,7 @@ MODULE var_lookup integer(i4b) :: scalarVolHtCap_soil = integerMissing ! volumetric heat capacity dry soil (J m-3 K-1) integer(i4b) :: scalarVolHtCap_water = integerMissing ! volumetric heat capacity liquid wat (J m-3 K-1) integer(i4b) :: mLayerVolHtCapBulk = integerMissing ! volumetric heat capacity in each layer (J m-3 K-1) + integer(i4b) :: mLayerCm = integerMissing ! Cm of each layer (J kg-1 K-1) integer(i4b) :: scalarLambda_drysoil = integerMissing ! thermal conductivity of dry soil (W m-1 K-1) integer(i4b) :: scalarLambda_wetsoil = integerMissing ! thermal conductivity of wet soil (W m-1 K-1) integer(i4b) :: mLayerThermalC = integerMissing ! thermal conductivity at the mid-point of each layer (W m-1 K-1) @@ -611,16 +613,19 @@ MODULE var_lookup integer(i4b) :: scalarCanopyLiqDeriv = integerMissing ! derivative in (throughfall + canopy drainage) w.r.t. canopy liquid water (s-1) integer(i4b) :: scalarThroughfallRainDeriv = integerMissing ! derivative in throughfall w.r.t. canopy liquid water (s-1) integer(i4b) :: scalarCanopyLiqDrainageDeriv = integerMissing ! derivative in canopy drainage w.r.t. canopy liquid water (s-1) - ! energy derivatives that might be treated as constant if heat capacity and thermal conductivity not updated + ! energy derivatives that might be treated as constant if heat capacity and thermal conductivity not updated integer(i4b) :: dVolHtCapBulk_dPsi0 = integerMissing ! derivative in bulk heat capacity w.r.t. matric potential integer(i4b) :: dVolHtCapBulk_dTheta = integerMissing ! derivative in bulk heat capacity w.r.t. volumetric water content - integer(i4b) :: dVolHtCapBulk_dCanWat = integerMissing ! derivative in bulk heat capacity w.r.t. volumetric water content + integer(i4b) :: dVolHtCapBulk_dCanWat = integerMissing ! derivative in bulk heat capacity w.r.t. canopy volumetric water content integer(i4b) :: dVolHtCapBulk_dTk = integerMissing ! derivative in bulk heat capacity w.r.t. temperature - integer(i4b) :: dVolHtCapBulk_dTkCanopy = integerMissing ! derivative in bulk heat capacity w.r.t. temperature + integer(i4b) :: dVolHtCapBulk_dTkCanopy = integerMissing ! derivative in bulk heat capacity w.r.t. canopy temperature integer(i4b) :: dThermalC_dTempAbove = integerMissing ! derivative in the thermal conductivity w.r.t. energy state in the layer above integer(i4b) :: dThermalC_dTempBelow = integerMissing ! derivative in the thermal conductivity w.r.t. energy state in the layer above integer(i4b) :: dThermalC_dWatAbove = integerMissing ! derivative in the thermal conductivity w.r.t. water state in the layer above integer(i4b) :: dThermalC_dWatBelow = integerMissing ! derivative in the thermal conductivity w.r.t. water state in the layer above + ! energy derivatives that might be treated as constant if Cm not updated + integer(i4b) :: dCm_dTk = integerMissing ! derivative in heat capacity w.r.t. temperature (J kg-1 K-2) + integer(i4b) :: dCm_dTkCanopy = integerMissing ! derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below integer(i4b) :: dNrgFlux_dTempAbove = integerMissing ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) integer(i4b) :: dNrgFlux_dTempBelow = integerMissing ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) @@ -663,6 +668,8 @@ MODULE var_lookup ! derivatives in time integer(i4b) :: mLayerdTemp_dt = integerMissing ! timestep change in layer temperature integer(i4b) :: scalarCanopydTemp_dt = integerMissing ! timestep change in canopy temperature + integer(i4b) :: mLayerdWat_dt = integerMissing ! timestep change in layer volumetric fraction of total water + integer(i4b) :: scalarCanopydWat_dt = integerMissing ! timestep change in canopy water content endtype iLook_deriv @@ -886,7 +893,8 @@ MODULE var_lookup 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,& 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,& 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,& - 81, 82, 83, 84, 85, 86, 87, 88, 89, 90) + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,& + 91, 92) ! named variables: model fluxes type(iLook_flux), public,parameter :: iLookFLUX =iLook_flux ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& @@ -905,7 +913,7 @@ MODULE var_lookup 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,& 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,& 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,& - 71) + 71, 72, 73, 74, 75) ! named variables: model indices type(iLook_index), public,parameter :: iLookINDEX =ilook_index ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& diff --git a/build/source/engine/computEnthalpy.f90 b/build/source/engine/computEnthalpy.f90 index 3d736d6cc..7b649d538 100644 --- a/build/source/engine/computEnthalpy.f90 +++ b/build/source/engine/computEnthalpy.f90 @@ -49,32 +49,31 @@ module computEnthalpy_module ! ********************************************************************************************************** subroutine computEnthalpy(& ! input - indx_data, & - nLayers, & - mLayerTemp, & - mLayerVolFracIce, & - mLayerHeatCap, & + indx_data, & ! intent(in): indices defining model states and layers + nLayers, & ! intent(in): number of snow layers + mLayerTemp, & ! intent(in): temperature of each snow/soil layer (K) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice (-) + mLayerHeatCap, & ! intent(in): heat capacity of each snow/soil layer (J m-3 K-1) ! output - mLayerEnthalpy & + mLayerEnthalpy & ! intent(out): enthalpy of each snow/soil layer (J m-3) ) ! -------------------------------------------------------------------------------------------------------------------------------- implicit none ! input: model control type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - integer(i4b),intent(in) :: nLayers ! number of snow layers + integer(i4b),intent(in) :: nLayers ! number of snow layers real(rkind),intent(in) :: mLayerTemp(:) ! temperature of each snow/soil layer (K) real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice (-) - real(rkind),intent(in) :: mLayerHeatCap(:) - real(rkind),intent(out) :: mLayerEnthalpy(:) - + real(rkind),intent(in) :: mLayerHeatCap(:) ! heat capacity of each snow/soil layer (J m-3 K-1) + real(rkind),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow/soil layer (J m-3) ! local variables - integer(i4b) :: iLayer + integer(i4b) :: iLayer ! loop index ! -------------------------------------------------------------------------------------------------------------------------------- associate(& - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of layer - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat & ! intent(in): [i4b(:)] indices for energy states + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of layer + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat & ! intent(in): [i4b(:)] indices for energy states ) ! (loop through non-missing energy state variables in the snow+soil domain) do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) @@ -95,44 +94,43 @@ end subroutine computEnthalpy ! ********************************************************************************************************** subroutine computEnthalpyPrime(& ! input - computeVegFlux, & - indx_data, & - nLayers, & - canopyDepth, & ! intent(in): canopy depth (m) - scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K) - scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2) - mLayerTempPrime, & - mLayerVolFracIcePrime, & - heatCapVeg, & - mLayerHeatCap, & - ! output - scalarCanopyEnthalpyPrime, & - mLayerEnthalpyPrime & + computeVegFlux, & ! intent(in): logical flag to denote if computing the vegetation flux + indx_data, & ! intent(in): indices defining model states and layers + nLayers, & ! intent(in): number of snow layers + canopyDepth, & ! intent(in): canopy depth (m) + scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K) + scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2) + mLayerTempPrime, & ! intent(in): temperature of each snow/soil layer (K) + mLayerVolFracIcePrime, & ! intent(in): Prime value for volumetric fraction of ice (-) + heatCapVeg, & ! intent(in): specific heat of vegetation (J kg-1 K-1) + mLayerHeatCap, & ! intent(in): heat capacity of each snow/soil layer (J m-3 K-1) + ! output + scalarCanopyEnthalpyPrime, & ! intent(out): Prime value for the enthalpy of the vegetation canopy (J m-2) + mLayerEnthalpyPrime & ! intent(out): enthalpy of each snow/soil layer (J m-3) ) ! -------------------------------------------------------------------------------------------------------------------------------- implicit none ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers integer(i4b),intent(in) :: nLayers ! number of snow layers - real(rkind),intent(in) :: canopyDepth ! canopy depth (m) + real(rkind),intent(in) :: canopyDepth ! canopy depth (m) real(rkind),intent(in) :: scalarCanopyTempPrime ! Prime value for the temperature of the vegetation canopy (K) - real(rkind),intent(in) :: scalarCanopyIcePrime ! Prime value for the ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: heatCapVeg - real(rkind),intent(in) :: mLayerTempPrime(:) ! temperature of each snow/soil layer (K) - real(rkind),intent(in) :: mLayerVolFracIcePrime(:) ! volumetric fraction of ice (-) - real(rkind),intent(in) :: mLayerHeatCap(:) - real(rkind),intent(out) :: scalarCanopyEnthalpyPrime - real(rkind),intent(out) :: mLayerEnthalpyPrime(:) - + real(rkind),intent(in) :: scalarCanopyIcePrime ! Prime value for the ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: heatCapVeg ! specific heat of vegetation (J kg-1 K-1) + real(rkind),intent(in) :: mLayerTempPrime(:) ! Prime value for temperature of each snow/soil layer (K) + real(rkind),intent(in) :: mLayerVolFracIcePrime(:) ! Prime value for volumetric fraction of ice (-) + real(rkind),intent(in) :: mLayerHeatCap(:) ! heat capacity of each snow/soil layer (J m-3 K-1) + real(rkind),intent(out) :: scalarCanopyEnthalpyPrime ! Prime value for the enthalpy of the vegetation canopy (J m-2) + real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! enthalpy of each snow/soil layer (J m-3) ! local variables - integer(i4b) :: iLayer + integer(i4b) :: iLayer ! loop index ! -------------------------------------------------------------------------------------------------------------------------------- associate(& - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of layer - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat & ! intent(in): [i4b(:)] indices for energy states + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of layer + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat & ! intent(in): [i4b(:)] indices for energy states ) if(computeVegFlux)then diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index b0c48ad67..d16bae97f 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -283,14 +283,14 @@ end subroutine computHeatCap ! public subroutine computStatMult: get scale factors ! ********************************************************************************************************** subroutine computStatMult(& - heatCapVeg, & - mLayerHeatCap, & + heatCapVeg, & ! intent(in): heat capacity for canopy + mLayerHeatCap, & ! intent(in): heat capacity for snow and soil ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers ! output - sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) - err,message) ! intent(out): error control + sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) + err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- USE nr_utility_module,only:arth ! get a sequence of numbers arth(start, incr, count) USE f2008funcs_module,only:findIndex ! finds the index of the first value within a vector @@ -546,17 +546,19 @@ end subroutine computHeatCapAnalytic ! ********************************************************************************************************** subroutine computCm(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux ! input: state variables - scalarCanopyTemp, & ! intent(in): value of canopy temperature (kg m-2) - mLayerTemp, & ! intent(in): vector of temperature (-) - mLayerMatricHead, & ! intent(in): vector of total water matric potential (m) + scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) + mLayerTemp, & ! intent(in): vector of temperature (K) + mLayerMatricHead, & ! intent(in): vector of total water matric potential (-) ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices ! output - scalarCanopyCm, & ! intent(out): Cm for vegetation - mLayerCm, & ! intent(out): Cm for soil and snow + scalarCanopyCm, & ! intent(out): Cm for vegetation (J kg K-1) + mLayerCm, & ! intent(out): Cm for soil and snow (J kg K-1) + dCm_dTk, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) + dCm_dTkCanopy, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) ! output: error control err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------------- @@ -565,15 +567,18 @@ subroutine computCm(& ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: model control logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - real(rkind),intent(in) :: scalarCanopyTemp ! value of canopy temperature (kg m-2) - real(rkind),intent(in) :: mLayerTemp(:) ! vector of temperature (-) - real(rkind),intent(in) :: mLayerMatricHead(:) ! vector of total water matric potential (m) + real(rkind),intent(in) :: scalarCanopyTemp ! value of canopy temperature (K) + real(rkind),intent(in) :: mLayerTemp(:) ! vector of temperature (K) + real(rkind),intent(in) :: mLayerMatricHead(:) ! vector of total water matric potential (-) ! input/output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model layer indices + ! output: Cm and derivatives + real(qp),intent(out) :: scalarCanopyCm ! Cm for vegetation (J kg K-1) + real(qp),intent(out) :: mLayerCm(:) ! Cm for soil and snow (J kg K-1) + real(rkind),intent(out) :: dCm_dTk(:) ! derivative in Cm w.r.t. temperature (J kg K-2) + real(rkind),intent(out) :: dCm_dTkCanopy ! derivative in Cm w.r.t. temperature (J kg K-2) ! output: error control - real(qp),intent(out) :: scalarCanopyCm ! Cm for vegetation - real(qp),intent(out) :: mLayerCm(:) ! Cm for soil and snow integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- @@ -581,10 +586,10 @@ subroutine computCm(& character(LEN=256) :: cmessage ! error message of downwind routine integer(i4b) :: iLayer ! index of model layer integer(i4b) :: iSoil ! index of soil layer - real(rkind) :: g1 - real(rkind) :: g2 + real(rkind) :: diffT ! temperature difference from Tfreeze + real(rkind) :: integral ! integral of snow freezing curve real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - + real(rkind) :: d_integral_dTk ! derivative of integral with temperature ! -------------------------------------------------------------------------------------------------------------------------------- ! associate variables in data structure associate(& @@ -604,12 +609,16 @@ subroutine computCm(& ! compute Cm of vegetation ! Note that scalarCanopyCm/iden_water is computed if(computeVegFlux)then - g2 = scalarCanopyTemp - Tfreeze - g1 = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * g2) - if(scalarCanopyTemp < Tfreeze)then - scalarCanopyCm = Cp_water * g1 + Cp_ice * (g2 - g1) + if(diffT>=0._rkind)then + scalarCanopyCm = Cp_water * diffT + ! derivatives + dCm_dTkCanopy = Cp_water else - scalarCanopyCm = Cp_water * g2 + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + scalarCanopyCm = Cp_water * integral + Cp_ice * (diffT - integral) + ! derivatives + d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) + dCm_dTkCanopy = Cp_water * d_integral_dTk + Cp_ice * (1._rkind - d_integral_dTk) end if end if @@ -625,19 +634,27 @@ subroutine computCm(& select case(layerType(iLayer)) ! * soil case(iname_soil) - g2 = mLayerTemp(iLayer) - Tfreeze + diffT = mLayerTemp(iLayer) - Tfreeze Tcrit = crit_soilT( mLayerMatricHead(iSoil) ) - if( mLayerTemp(iLayer) < Tcrit)then - mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air) * g2 - else - mLayerCm(iLayer) = (iden_water * Cp_water - iden_air * Cp_air) * g2 - end if + if( mLayerTemp(iLayer)>=Tcrit)then + mLayerCm(iLayer) = (iden_water * Cp_water - iden_air * Cp_air) * diffT + ! derivatives + dCm_dTk(iLayer) = (iden_water * Cp_water - iden_air * Cp_air) + else + mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air) * diffT + ! derivatives + dCm_dTk(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air) + endif case(iname_snow) - g2 = mLayerTemp(iLayer) - Tfreeze - g1 = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * g2) - mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( g2 - g1 ) & - + (iden_water * Cp_water - iden_air * Cp_air) * g1 + diffT = mLayerTemp(iLayer) - Tfreeze + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( diffT - integral ) & + + (iden_water * Cp_water - iden_air * Cp_air) * integral + ! derivatives + d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) + dCm_dTk(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( 1._rkind - d_integral_dTk ) & + + (iden_water * Cp_water - iden_air * Cp_air) * d_integral_dTk case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute Cm'; return end select diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 8c1dca2ef..a6c4aab87 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -237,14 +237,21 @@ subroutine computJacob(& dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. temperature + ! derivative in Cm w.r.t. relevant state variables + dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk )%dat ,& ! intent(in): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) + dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) ! derivatives in time - mLayerdTemp_dt => deriv_data%var(iLookDERIV%mLayerdTemp_dt )%dat ,& ! intent(in): [dp(:)] timestep change in layer temperature - scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt )%dat(1) ,& ! intent(in): [dp ] timestep change in canopy temperature + mLayerdTemp_dt => deriv_data%var(iLookDERIV%mLayerdTemp_dt )%dat ,& ! intent(in): [dp(:)] timestep change in layer temperature + scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt )%dat(1) ,& ! intent(in): [dp ] timestep change in canopy temperature + mLayerdWat_dt => deriv_data%var(iLookDERIV%mLayerdWat_dt )%dat ,& ! intent(in): [dp(:)] timestep change in layer volumetric fraction of total water + scalarCanopydWat_dt => deriv_data%var(iLookDERIV%scalarCanopydWat_dt )%dat(1) ,& ! intent(in): [dp ] timestep change in canopy total water ! diagnostic variables - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg )%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg )%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg )%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) + scalarCanopyCm => diag_data%var(iLookDIAG%scalarCanopyCm )%dat(1) ,& ! intent(in): [dp] Cm of canopy (J kg-1 K-1) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow )%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk )%dat ,& ! intent(in): [dp(:)] bulk volumetric heat capacity in each snow and soil layer (J m-3 K-1) + mLayerCm => diag_data%var(iLookDIAG%mLayerCm )%dat ,& ! intent(in): [dp(:)] Cm in each snow and soil layer (J kg-1 K-1) scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1) ,& ! intent(in): [dp] soil control on infiltration, zero or one ! canopy and layer depth canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth )%dat(1) ,& ! intent(in): [dp ] canopy depth (m) @@ -269,7 +276,8 @@ subroutine computJacob(& ! NOTE: energy for vegetation is computed *within* the iteration loop as it includes phase change if(ixVegNrg/=integerMissing)then dMat(ixVegNrg) = scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy & - + dVolHtCapBulk_dTkCanopy * scalarCanopydTemp_dt + + dVolHtCapBulk_dTkCanopy * scalarCanopydTemp_dt & + + dCm_dTkCanopy * scalarCanopydWat_dt/canopyDepth endif ! compute additional terms for the Jacobian for the snow-soil domain (excluding fluxes) @@ -277,14 +285,15 @@ subroutine computJacob(& do iLayer=1,nLayers if(ixSnowSoilNrg(iLayer)/=integerMissing)then dMat(ixSnowSoilNrg(iLayer)) = mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) & - + dVolHtCapBulk_dTk(iLayer) * mLayerdTemp_dt(iLayer) + + dVolHtCapBulk_dTk(iLayer) * mLayerdTemp_dt(iLayer) & + + dCm_dTk(iLayer) * mLayerdWat_dt(iLayer) endif end do ! compute additional terms for the Jacobian for the soil domain (excluding fluxes) do iLayer=1,nSoil if(ixSoilOnlyHyd(iLayer)/=integerMissing)then - dMat(ixSoilOnlyHyd(iLayer)) = dVolTot_dPsi0(iLayer) + dCompress_dPsi(iLayer) + dMat(ixSoilOnlyHyd(iLayer)) = dVolTot_dPsi0(iLayer) + dCompress_dPsi(iLayer) endif end do @@ -323,7 +332,8 @@ subroutine computJacob(& ! * cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth & - + dVolHtCapBulk_dCanWat * scalarCanopydTemp_dt - (dt/canopyDepth) * dCanopyNetFlux_dCanWat ! dF/dLiq + + dVolHtCapBulk_dCanWat * scalarCanopydTemp_dt + scalarCanopyCm/canopyDepth & + - (dt/canopyDepth) * dCanopyNetFlux_dCanWat if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) endif @@ -435,8 +445,8 @@ subroutine computJacob(& ! - include derivatives of energy fluxes w.r.t water fluxes for current layer aJac(ixOffDiag(nrgState,watState),watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water & - + dVolHtCapBulk_dTheta(iLayer) * mLayerdTemp_dt(iLayer) & - + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) ! (dF/dLiq) + + dVolHtCapBulk_dTheta(iLayer) * mLayerdTemp_dt(iLayer) + mLayerCm(iLayer) & + + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) ! - include derivatives of water fluxes w.r.t energy fluxes for current layer aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) @@ -604,7 +614,7 @@ subroutine computJacob(& endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerdTemp_dt(jLayer) & + aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerdTemp_dt(jLayer) + mLayerCm(iLayer) * dVolTot_dPsi0(iLayer) & + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(ixOffDiag(nrgState,watState),watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water + aJac(ixOffDiag(nrgState,watState),watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content @@ -673,7 +683,8 @@ subroutine computJacob(& ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth & - + dVolHtCapBulk_dCanWat * scalarCanopydTemp_dt - (dt/canopyDepth) * dCanopyNetFlux_dCanWat ! dF/dLiq + + dVolHtCapBulk_dCanWat * scalarCanopydTemp_dt + scalarCanopyCm/canopyDepth & + - (dt/canopyDepth) * dCanopyNetFlux_dCanWat if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) endif @@ -785,8 +796,8 @@ subroutine computJacob(& ! - include derivatives of energy fluxes w.r.t water fluxes for current layer aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water & - + dVolHtCapBulk_dTheta(iLayer) * mLayerdTemp_dt(iLayer) & - + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) ! (dF/dLiq) + + dVolHtCapBulk_dTheta(iLayer) * mLayerdTemp_dt(iLayer) + mLayerCm(iLayer) & + + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) ! - include derivatives of water fluxes w.r.t energy fluxes for current layer aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) @@ -933,7 +944,7 @@ subroutine computJacob(& endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerdTemp_dt(jLayer) & + aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerdTemp_dt(jLayer) + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) & + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(nrgState,watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index 2e22fbdee..c189af2c7 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -273,11 +273,16 @@ subroutine computJacobWithPrime(& dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. temperature + ! derivative in Cm w.r.t. relevant state variables + dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk )%dat ,& ! intent(in): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) + dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) ! diagnostic variables scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg )%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg )%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) + scalarCanopyCm => diag_data%var(iLookDIAG%scalarCanopyCm )%dat(1) ,& ! intent(in): [dp] Cm of canopy (J kg-1 K-1) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow )%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk )%dat ,& ! intent(in): [dp(:)] bulk volumetric heat capacity in each snow and soil layer (J m-3 K-1) + mLayerCm => diag_data%var(iLookDIAG%mLayerCm )%dat ,& ! intent(in): [dp(:)] Cm in each snow and soil layer (J kg-1 K-1) scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1) ,& ! intent(in): [dp] soil control on infiltration, zero or one ! canopy and layer depth canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth )%dat(1) ,& ! intent(in): [dp ] canopy depth (m) @@ -304,6 +309,7 @@ subroutine computJacobWithPrime(& if(ixVegNrg/=integerMissing)then dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy ) * cj & + dVolHtCapBulk_dTkCanopy * scalarCanopyTempPrime & + + dCm_dTkCanopy * scalarCanopyWatPrime / canopyDepth & + LH_fus*iden_water * scalarCanopyTempPrime * d2Theta_dTkCanopy2 & + LH_fus * dFracLiqVeg_dTkCanopy * scalarCanopyWatPrime / canopyDepth endif @@ -314,6 +320,7 @@ subroutine computJacobWithPrime(& if(ixSnowSoilNrg(iLayer)/=integerMissing)then dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj & + dVolHtCapBulk_dTk(iLayer) * mLayerTempPrime(iLayer) & + + dCm_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) & + LH_fus*iden_water * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & + LH_fus*iden_water * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) endif @@ -365,8 +372,9 @@ subroutine computJacobWithPrime(& ! * cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth * cj & - + dVolHtCapBulk_dCanWat * scalarCanopyTempPrime - (dt/canopyDepth) * dCanopyNetFlux_dCanWat & - + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth ! dF/dLiq + + dVolHtCapBulk_dCanWat * scalarCanopyTempPrime + scalarCanopyCm/canopyDepth * cj & + - (dt/canopyDepth) * dCanopyNetFlux_dCanWat & + + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) endif @@ -478,7 +486,7 @@ subroutine computJacobWithPrime(& ! - include derivatives of energy fluxes w.r.t water fluxes for current layer aJac(ixOffDiag(nrgState,watState),watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & - + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) & + + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) + mLayerCm(iLayer) * cj & + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) & + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) @@ -648,8 +656,8 @@ subroutine computJacobWithPrime(& endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) & - + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) * cj & + + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + mLayerCm(jLayer) * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(ixOffDiag(nrgState,watState),watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) * cj & - LH_fus*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_d2Psi0(iLayer) + aJac(ixOffDiag(nrgState,watState),watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content @@ -718,8 +726,9 @@ subroutine computJacobWithPrime(& ! * cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth * cj & - + dVolHtCapBulk_dCanWat * scalarCanopyTempPrime - (dt/canopyDepth) * dCanopyNetFlux_dCanWat & - + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth ! dF/dLiq + + dVolHtCapBulk_dCanWat * scalarCanopyTempPrime + scalarCanopyCm/canopyDepth * cj& + - (dt/canopyDepth) * dCanopyNetFlux_dCanWat & + + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) endif @@ -831,7 +840,7 @@ subroutine computJacobWithPrime(& ! - include derivatives of energy fluxes w.r.t water fluxes for current layer aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & - + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) & + + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) + mLayerCm(iLayer) * cj & + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) & + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) @@ -980,8 +989,8 @@ subroutine computJacobWithPrime(& endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) & - + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) * cj & + + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + mLayerCm(jLayer) * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(nrgState,watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) * cj & - LH_fus*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_d2Psi0(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content diff --git a/build/source/engine/computResid.f90 b/build/source/engine/computResid.f90 index 920524576..7e1134589 100644 --- a/build/source/engine/computResid.f90 +++ b/build/source/engine/computResid.f90 @@ -95,8 +95,8 @@ subroutine computResid(& mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) mLayerVolFracWatTrial, & ! intent(in): trial value for the volumetric water in each snow and soil layer (-) mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liq in each snow and soil layer (-) - scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) - mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) + scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (J kg K-1) + mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (J kg K-1) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU diff --git a/build/source/engine/computResidWithPrime.f90 b/build/source/engine/computResidWithPrime.f90 index d5975895a..8f2c34874 100644 --- a/build/source/engine/computResidWithPrime.f90 +++ b/build/source/engine/computResidWithPrime.f90 @@ -79,8 +79,8 @@ subroutine computResidWithPrime(& mLayerVolFracIcePrime, & ! intent(in): Prime value for the volumetric ice in each snow and soil layer (s-1) mLayerVolFracWatPrime, & ! intent(in): Prime value for the volumetric water in each snow and soil layer (s-1) mLayerVolFracLiqPrime, & ! intent(in): Prime value for the volumetric liq in each snow and soil layer (s-1) - scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) - mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) + scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (J kg K-1) + mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (J kg K-1) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 01eba3c54..568e51ce1 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -272,12 +272,17 @@ subroutine eval8summa(& dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk)%dat ,& ! intent(out): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) + dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) ! mapping ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)]mapping of full state vector to the state subset ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)]index of control volume for different domains (veg, snow, soil) ! heat capacity - heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),&! intent(out): [dp] volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & ! intent(out): [dp(:)] heat capacity for snow and soil + heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(out): [dp] volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(out): [dp(:)] heat capacity for snow and soil + ! Cm + canopyCmTrial => diag_data%var(iLookDIAG%scalarCanopyCm)%dat(1) ,& ! intent(out): [dp] Cm of the canopy + mLayerCmTrial => diag_data%var(iLookDIAG%mLayerCm)%dat & ! intent(out): [dp(:)] Cm of snow and soil ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -565,17 +570,21 @@ subroutine eval8summa(& ! input: state variables scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (m) + mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (-) ! input data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices ! output - scalarCanopyCmTrial, & ! intent(out): Cm for vegetation - mLayerCmTrial, & ! intent(out): Cm for soil and snow + canopyCmTrial, & ! intent(out): Cm for vegetation (J kg K-1) + mLayerCmTrial, & ! intent(out): Cm for soil and snow (J kg K-1) + dCm_dTk, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) + dCm_dTkCanopy, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) err,cmessage) ! intent(out): error control else - scalarCanopyCmTrial = 0._qp + canopyCmTrial = 0._qp mLayerCmTrial = 0._qp + dCm_dTk = 0._rkind + dCm_dTkCanopy = 0._rkind endif ! needCm ! save the number of flux calls per time step diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 181bd6126..6ea676e59 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -275,12 +275,17 @@ subroutine eval8summaWithPrime(& dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk)%dat ,& ! intent(out): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) + dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) ! mapping ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)]mapping of full state vector to the state subset ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)]index of control volume for different domains (veg, snow, soil) ! heat capacity heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(out): [dp] volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & ! intent(out): [dp(:)] heat capacity for snow and soil + mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(out): [dp(:)] heat capacity for snow and soil + ! Cm + canopyCmTrial => diag_data%var(iLookDIAG%scalarCanopyCm)%dat(1) ,& ! intent(out): [dp] Cm of the canopy + mLayerCmTrial => diag_data%var(iLookDIAG%mLayerCm)%dat & ! intent(out): [dp(:)] Cm of snow and soil ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -612,17 +617,22 @@ subroutine eval8summaWithPrime(& ! input: state variables scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (m) + mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (-) ! input data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices ! output - scalarCanopyCmTrial, & ! intent(out): Cm for vegetation - mLayerCmTrial, & ! intent(out): Cm for soil and snow + ! output + canopyCmTrial, & ! intent(out): Cm for vegetation (J kg K-1) + mLayerCmTrial, & ! intent(out): Cm for soil and snow (J kg K-1) + dCm_dTk, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) + dCm_dTkCanopy, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) err,cmessage) ! intent(out): error control else - scalarCanopyCmTrial = 0._qp + canopyCmTrial = 0._qp mLayerCmTrial = 0._qp + dCm_dTk = 0._rkind + dCm_dTkCanopy = 0._rkind endif ! needCm ! save the number of flux calls per time step diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index 344bb335b..dd1e85842 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -241,7 +241,7 @@ end subroutine T2E_lookup ! public subroutine t2enthalpy: compute enthalpy from temperature and total water content ! ************************************************************************************************************************ subroutine t2enthalpy(& - doPhase, & ! intent(in): logical flag to include phase change in enthalpy or not + doPhase, & ! intent(in): logical flag to include phase change in enthalpy or not ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure @@ -258,11 +258,11 @@ subroutine t2enthalpy(& mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) mLayerVolFracIceTrial, & ! intent(in) ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) @@ -329,7 +329,7 @@ subroutine t2enthalpy(& real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) real(rkind) :: psiLiq ! matric head of liquid water (m) real(rkind) :: volFracWat ! volumetric fraction of total water, liquid+ice (-) - real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) + real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) real(rkind) :: volFracIce ! volumetric fraction of ice (-) real(rkind) :: diffT ! temperature difference from Tfreeze real(rkind) :: integral ! integral of snow freezing curve @@ -338,7 +338,6 @@ subroutine t2enthalpy(& real(rkind) :: d_integral_dTk ! derivative of integral with temperature real(rkind) :: dE ! derivative of enthalpy with temperature at layer temperature real(rkind) :: dEcrit ! derivative of enthalpy with temperature at critical temperature - ! enthalpy real(rkind) :: enthVeg ! enthalpy of the vegetation (J m-3) real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) @@ -472,7 +471,7 @@ subroutine t2enthalpy(& scalarCanopyEnthalpy = enthVeg + enthLiq + enthIce dCanEnthalpy_dTk = dEnthVeg_dTk + dEnthLiq_dTk + dEnthIce_dTk dCanEnthalpy_dWat = dEnthVeg_dWat + dEnthLiq_dWat + dEnthIce_dWat - if (doPhase)then + if (doPhase)then ! only need for calculating energy balance error so shouldn't need derivatives scalarCanopyEnthalpy = scalarCanopyEnthalpy - enthPhase dCanEnthalpy_dTk = dCanEnthalpy_dTk - dEnthPhase_dTk dCanEnthalpy_dWat = dCanEnthalpy_dWat - dEnthPhase_dWat @@ -520,7 +519,7 @@ subroutine t2enthalpy(& mLayerEnthalpy(iLayer) = enthLiq + enthIce + enthAir dEnthalpy_dTk(iLayer) = dEnthLiq_dTk + dEnthIce_dTk + dEnthAir_dTk dEnthalpy_dWat(iLayer) = dEnthLiq_dWat + dEnthIce_dWat + dEnthAir_dWat - if (doPhase)then + if (doPhase)then ! only need for calculating energy balance error so shouldn't need derivatives mLayerEnthalpy(iLayer) = mLayerEnthalpy(iLayer) - enthPhase dEnthalpy_dTk(iLayer) = dEnthalpy_dTk(iLayer) - dEnthPhase_dTk dEnthalpy_dWat(iLayer) = dEnthalpy_dWat(iLayer) - dEnthPhase_dWat @@ -554,7 +553,7 @@ subroutine t2enthalpy(& diffT = mLayerTempTrial(iLayer) - Tfreeze ! *** compute enthalpy of water for unfrozen conditions - if(mlayerTempTrial(iLayer) > Tcrit)then + if(mlayerTempTrial(iLayer)>=Tcrit)then enthWater = iden_water*Cp_water*volFracWat*diffT ! valid for temperatures below freezing also enthPhase = 0._rkind ! enthalpy derivatives @@ -607,7 +606,7 @@ subroutine t2enthalpy(& mLayerEnthalpy(iLayer) = enthSoil + enthWater + enthAir dEnthalpy_dTk(iLayer) = dEnthSoil_dTk + dEnthWater_dTk + dEnthAir_dTk dEnthalpy_dWat(iLayer) = dEnthSoil_dWat + dEnthWater_dWat + dEnthAir_dWat - if (doPhase)then + if (doPhase)then ! only need for calculating energy balance error so shouldn't need derivatives mLayerEnthalpy(iLayer) = mLayerEnthalpy(iLayer) - enthPhase dEnthalpy_dTk(iLayer) = dEnthalpy_dTk(iLayer) - dEnthPhase_dTk dEnthalpy_dWat(iLayer) = dEnthalpy_dWat(iLayer) - dEnthPhase_dWat diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 index 03cd43729..e2295aaf1 100644 --- a/build/source/engine/updateVars.f90 +++ b/build/source/engine/updateVars.f90 @@ -206,35 +206,39 @@ subroutine updateVars(& ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ! depth-varying model parameters - vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ! depth-varying model parameters + vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) ! model diagnostic variables (heat capacity) - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(in): [dp ] volumetric heat capacity of the vegetation (J m-3 K-1) - mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] volumetric heat capacity in each layer (J m-3 K-1) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(in): [dp ] volumetric heat capacity of the vegetation (J m-3 K-1) + mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] volumetric heat capacity in each layer (J m-3 K-1) ! model diagnostic variables (fraction of liquid water) - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) ! model states from a previous solution - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) ! model diagnostic variables from a previous solution - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) ! derivatives - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential - dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) - dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(out): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential + dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) + dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(out): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature ! derivatives inside solver for Jacobian only - mLayerdTemp_dt => deriv_data%var(iLookDERIV%mLayerdTemp_dt )%dat ,& ! intent(out): [dp(:)] timestep change in layer temperature - scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt )%dat(1) & ! intent(out): [dp ] timestep change in canopy temperature + mLayerdTemp_dt => deriv_data%var(iLookDERIV%mLayerdTemp_dt )%dat ,& ! intent(out): [dp(:)] timestep change in layer temperature + scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt )%dat(1),& ! intent(out): [dp ] timestep change in canopy temperature + mLayerdWat_dt => deriv_data%var(iLookDERIV%mLayerdWat_dt)%dat ,& ! intent(out): [dp(:)] timestep change in layer volumetric fraction of total water + scalarCanopydWat_dt => deriv_data%var(iLookDERIV%scalarCanopydWat_dt)%dat(1) & ! intent(out): [dp ] timestep change in canopy total water ) ! association with variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- @@ -500,6 +504,12 @@ subroutine updateVars(& endif ! if energy state or solution is coupled + ! compute water time derivatives + select case(ixDomainType) + case(iname_veg); scalarCanopydWat_dt = scalarCanopyWatTrial - scalarCanopyWat + case(iname_snow, iname_soil); mLayerdWat_dt(iLayer) = mLayerVolFracWatTrial(iLayer) - mLayerVolFracWat(iLayer) + end select + ! ----- ! - update temperatures... ! ------------------------ From 01dcb99d88c1d6d1ecd542016f6cb52c9251033e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 2 Nov 2023 18:24:33 +0900 Subject: [PATCH 0982/1472] Cm deriviatives --- build/source/dshare/get_ixname.f90 | 13 ++- build/source/dshare/popMetadat.f90 | 11 ++- build/source/dshare/var_lookup.f90 | 18 ++-- build/source/engine/computEnthalpy.f90 | 78 ++++++++--------- build/source/engine/computHeatCap.f90 | 91 ++++++++++++-------- build/source/engine/computJacob.f90 | 39 ++++++--- build/source/engine/computJacobWithPrime.f90 | 29 ++++--- build/source/engine/computResid.f90 | 4 +- build/source/engine/computResidWithPrime.f90 | 4 +- build/source/engine/eval8summa.f90 | 21 +++-- build/source/engine/eval8summaWithPrime.f90 | 20 +++-- build/source/engine/t2enthalpy.f90 | 23 +++-- build/source/engine/updateVars.f90 | 56 +++++++----- 13 files changed, 246 insertions(+), 161 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 60a6f9ccd..dde76f749 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -486,6 +486,7 @@ function get_ixdiag(varName) case('scalarCanopyDepth' ); get_ixdiag = iLookDIAG%scalarCanopyDepth ! canopy depth (m) case('scalarGreenVegFraction' ); get_ixdiag = iLookDIAG%scalarGreenVegFraction ! green vegetation fraction used to compute LAI (-) case('scalarBulkVolHeatCapVeg' ); get_ixdiag = iLookDIAG%scalarBulkVolHeatCapVeg ! bulk volumetric heat capacity of vegetation (J m-3 K-1) + case('scalarCanopyCm' ); get_ixdiag = iLookDIAG%scalarCanopyCm ! Cm of canopy (J kg-1 K-1) case('scalarCanopyEmissivity' ); get_ixdiag = iLookDIAG%scalarCanopyEmissivity ! effective canopy emissivity (-) case('scalarRootZoneTemp' ); get_ixdiag = iLookDIAG%scalarRootZoneTemp ! average temperature of the root zone (K) case('scalarLAI' ); get_ixdiag = iLookDIAG%scalarLAI ! one-sided leaf area index (m2 m-2) @@ -501,6 +502,7 @@ function get_ixdiag(varName) case('scalarVolHtCap_soil' ); get_ixdiag = iLookDIAG%scalarVolHtCap_soil ! volumetric heat capacity dry soil (J m-3 K-1) case('scalarVolHtCap_water' ); get_ixdiag = iLookDIAG%scalarVolHtCap_water ! volumetric heat capacity liquid wat (J m-3 K-1) case('mLayerVolHtCapBulk' ); get_ixdiag = iLookDIAG%mLayerVolHtCapBulk ! volumetric heat capacity in each layer (J m-3 K-1) + case('mLayerCm' ); get_ixdiag = iLookDIAG%mLayerCm ! Cm of each layer (J kg-1 K-1) case('scalarLambda_drysoil' ); get_ixdiag = iLookDIAG%scalarLambda_drysoil ! thermal conductivity of dry soil (W m-1) case('scalarLambda_wetsoil' ); get_ixdiag = iLookDIAG%scalarLambda_wetsoil ! thermal conductivity of wet soil (W m-1) case('mLayerThermalC' ); get_ixdiag = iLookDIAG%mLayerThermalC ! thermal conductivity at the mid-point of each layer (W m-1 K-1) @@ -755,18 +757,21 @@ function get_ixderiv(varName) case('scalarCanopyLiqDeriv' ); get_ixderiv = iLookDERIV%scalarCanopyLiqDeriv ! derivative in (throughfall + canopy drainage) w.r.t. canopy liquid water (s-1) case('scalarThroughfallRainDeriv' ); get_ixderiv = iLookDERIV%scalarThroughfallRainDeriv ! derivative in throughfall w.r.t. canopy liquid water (s-1) case('scalarCanopyLiqDrainageDeriv' ); get_ixderiv = iLookDERIV%scalarCanopyLiqDrainageDeriv ! derivative in canopy drainage w.r.t. canopy liquid water (s-1) - ! energy derivatives that might be treated as constant if heat capacity and thermal conductivity not updated + ! energy derivatives that might be treated as constant if heat capacity and thermal conductivity not updated case('dVolHtCapBulk_dPsi0' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dPsi0 ! derivative in bulk heat capacity w.r.t. matric potential case('dVolHtCapBulk_dTheta' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTheta ! derivative in bulk heat capacity w.r.t. volumetric water content - case('dVolHtCapBulk_dCanWat' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. volumetric water content + case('dVolHtCapBulk_dCanWat' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. canopy volumetric water content case('dVolHtCapBulk_dTk' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTk ! derivative in bulk heat capacity w.r.t. temperature - case('dVolHtCapBulk_dTkCanopy' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature + case('dVolHtCapBulk_dTkCanopy' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. canopy temperature case('dThermalC_dTempAbove' ); get_ixderiv = iLookDERIV%dThermalC_dTempAbove ! derivative in the thermal conductivity w.r.t. energy state in the layer above case('dThermalC_dTempBelow' ); get_ixderiv = iLookDERIV%dThermalC_dTempBelow ! derivative in the thermal conductivity w.r.t. energy state in the layer above case('dThermalC_dWatAbove' ); get_ixderiv = iLookDERIV%dThermalC_dWatAbove ! derivative in the thermal conductivity w.r.t. water state in the layer above case('dThermalC_dWatBelow' ); get_ixderiv = iLookDERIV%dThermalC_dWatBelow ! derivative in the thermal conductivity w.r.t. water state in the layer above case('dNrgFlux_dTempAbove' ); get_ixderiv = iLookDERIV%dNrgFlux_dTempAbove ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) case('dNrgFlux_dTempBelow' ); get_ixderiv = iLookDERIV%dNrgFlux_dTempBelow ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) + ! energy derivatives that might be treated as constant if Cm not updated + case('dCm_dTk' ); get_ixderiv = iLookDERIV%dCm_dTk ! derivative in Cm w.r.t. temperature (J kg K-2) + case('dCm_dTkCanopy' ); get_ixderiv = iLookDERIV%dCm_dTkCanopy ! derivative in Cm w.r.t. canopy temperature (J kg K-2) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below case('dNrgFlux_dWatAbove' ); get_ixderiv = iLookDERIV%dNrgFlux_dWatAbove ! derivatives in the flux w.r.t. water state temperature in the layer above case('dNrgFlux_dWatBelow' ); get_ixderiv = iLookDERIV%dNrgFlux_dWatBelow ! derivatives in the flux w.r.t. water state in the layer below @@ -806,6 +811,8 @@ function get_ixderiv(varName) ! derivatives in time case( 'mLayerdTemp_dt' ); get_ixderiv = iLookDERIV%mLayerdTemp_dt ! timestep change in layer temperature case( 'scalarCanopydTemp_dt' ); get_ixderiv = iLookDERIV%scalarCanopydTemp_dt ! timestep change in canopy temperature + case( 'mLayerdWat_dt' ); get_ixderiv = iLookDERIV%mLayerdWat_dt ! timestep change in layer volumetric fraction of total water + case( 'scalarCanopydWat_dt' ); get_ixderiv = iLookDERIV%scalarCanopydWat_dt ! timestep change in canopy water content case default get_ixderiv = integerMissing diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index d1f86a04d..e527cc7b4 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -341,6 +341,7 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%scalarCanopyDepth) = var_info('scalarCanopyDepth' , 'canopy depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarGreenVegFraction) = var_info('scalarGreenVegFraction' , 'green vegetation fraction (used to compute LAI)' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarBulkVolHeatCapVeg) = var_info('scalarBulkVolHeatCapVeg' , 'bulk volumetric heat capacity of vegetation' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarCanopyCm) = var_info('scalarCanopyCm' , 'Cm of canopy' , 'J kg-1 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarCanopyEmissivity) = var_info('scalarCanopyEmissivity' , 'effective canopy emissivity' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarRootZoneTemp) = var_info('scalarRootZoneTemp' , 'average temperature of the root zone' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarLAI) = var_info('scalarLAI' , 'one-sided leaf area index' , 'm2 m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) @@ -356,6 +357,7 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%scalarVolHtCap_soil) = var_info('scalarVolHtCap_soil' , 'volumetric heat capacity dry soil' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarVolHtCap_water) = var_info('scalarVolHtCap_water' , 'volumetric heat capacity liquid wat' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%mLayerVolHtCapBulk) = var_info('mLayerVolHtCapBulk' , 'volumetric heat capacity in each layer' , 'J m-3 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%mLayerCm) = var_info('mLayerCm' , 'Cm of each layer' , 'J kg-1 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarLambda_drysoil) = var_info('scalarLambda_drysoil' , 'thermal conductivity of dry soil' , 'W m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarLambda_wetsoil) = var_info('scalarLambda_wetsoil' , 'thermal conductivity of wet soil' , 'W m-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%mLayerThermalC) = var_info('mLayerThermalC' , 'thermal conductivity at the mid-point of each layer' , 'W m-1 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) @@ -583,13 +585,16 @@ subroutine popMetadat(err,message) ! energy derivatives that might be treated as constant if heat capacity and thermal conductivity not updated deriv_meta(iLookDERIV%dVolHtCapBulk_dPsi0) = var_info('dVolHtCapBulk_dPsi0' , 'derivative in bulk heat capacity w.r.t. matric potential' , 'J m-4 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dVolHtCapBulk_dTheta) = var_info('dVolHtCapBulk_dTheta' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dCanWat) = var_info('dVolHtCapBulk_dCanWat' , 'derivative in bulk heat capacity w.r.t. volumetric water content' , 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dCanWat) = var_info('dVolHtCapBulk_dCanWat' , 'derivative in bulk heat capacity w.r.t. canopy volumetric water content', 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dVolHtCapBulk_dTk) = var_info('dVolHtCapBulk_dTk' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dTkCanopy) = var_info('dVolHtCapBulk_dTkCanopy' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dTkCanopy) = var_info('dVolHtCapBulk_dTkCanopy' , 'derivative in bulk heat capacity w.r.t. canopy temperature' , 'J m-3 K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dThermalC_dTempAbove) = var_info('dThermalC_dTempAbove' , 'derivative in the thermal conductivity w.r.t. energy in the layer above','J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dThermalC_dTempBelow) = var_info('dThermalC_dTempBelow' , 'derivative in the thermal conductivity w.r.t. energy in the layer above','J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dThermalC_dWatAbove) = var_info('dThermalC_dWatAbove' , 'derivative in the thermal conductivity w.r.t. water in the layer above', 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dThermalC_dWatBelow) = var_info('dThermalC_dWatBelow' , 'derivative in the thermal conductivity w.r.t. water in the layer above', 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) + ! energy derivatives that might be treated as constant if Cm not updated + deriv_meta(iLookDERIV%dCm_dTk) = var_info('dCm_dTk' , 'derivative in Cm w.r.t. temperature' , 'J kg K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCm_dTkCanopy) = var_info('dCm_dTkCanopy' , 'derivative in Cm w.r.t. canopy temperature' , 'J kg K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below deriv_meta(iLookDERIV%dNrgFlux_dTempAbove) = var_info('dNrgFlux_dTempAbove' , 'derivatives in the flux w.r.t. temperature in the layer above' , 'J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dNrgFlux_dTempBelow) = var_info('dNrgFlux_dTempBelow' , 'derivatives in the flux w.r.t. temperature in the layer below' , 'J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) @@ -632,6 +637,8 @@ subroutine popMetadat(err,message) ! derivatives in time deriv_meta(iLookDERIV%mLayerdTemp_dt) = var_info('mLayerdTemp_dt' , 'timestep change in layer temperature' , 'K' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%scalarCanopydTemp_dt) = var_info('scalarCanopydTemp_dt' , 'timestep change in canopy temperature' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%mLayerdWat_dt) = var_info('mLayerdWat_dt' , 'timestep change in layer volumetric fraction of total water' , '-' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%scalarCanopydWat_dt) = var_info('scalarCanopydWat_dt' , 'timestep change in canopy water content' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! ----- ! * basin-wide runoff and aquifer fluxes... ! ----------------------------------------- diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 909bbefb6..ac8b6ffd9 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -366,6 +366,7 @@ MODULE var_lookup integer(i4b) :: scalarCanopyDepth = integerMissing ! canopy depth (m) integer(i4b) :: scalarGreenVegFraction = integerMissing ! green vegetation fraction used to compute LAI (-) integer(i4b) :: scalarBulkVolHeatCapVeg = integerMissing ! bulk volumetric heat capacity of vegetation (J m-3 K-1) + integer(i4b) :: scalarCanopyCm = integerMissing ! Cm of canopy (J kg-1 K-1) integer(i4b) :: scalarCanopyEmissivity = integerMissing ! effective canopy emissivity (-) integer(i4b) :: scalarRootZoneTemp = integerMissing ! average temperature of the root zone (K) integer(i4b) :: scalarLAI = integerMissing ! one-sided leaf area index (m2 m-2) @@ -381,6 +382,7 @@ MODULE var_lookup integer(i4b) :: scalarVolHtCap_soil = integerMissing ! volumetric heat capacity dry soil (J m-3 K-1) integer(i4b) :: scalarVolHtCap_water = integerMissing ! volumetric heat capacity liquid wat (J m-3 K-1) integer(i4b) :: mLayerVolHtCapBulk = integerMissing ! volumetric heat capacity in each layer (J m-3 K-1) + integer(i4b) :: mLayerCm = integerMissing ! Cm of each layer (J kg-1 K-1) integer(i4b) :: scalarLambda_drysoil = integerMissing ! thermal conductivity of dry soil (W m-1 K-1) integer(i4b) :: scalarLambda_wetsoil = integerMissing ! thermal conductivity of wet soil (W m-1 K-1) integer(i4b) :: mLayerThermalC = integerMissing ! thermal conductivity at the mid-point of each layer (W m-1 K-1) @@ -611,16 +613,19 @@ MODULE var_lookup integer(i4b) :: scalarCanopyLiqDeriv = integerMissing ! derivative in (throughfall + canopy drainage) w.r.t. canopy liquid water (s-1) integer(i4b) :: scalarThroughfallRainDeriv = integerMissing ! derivative in throughfall w.r.t. canopy liquid water (s-1) integer(i4b) :: scalarCanopyLiqDrainageDeriv = integerMissing ! derivative in canopy drainage w.r.t. canopy liquid water (s-1) - ! energy derivatives that might be treated as constant if heat capacity and thermal conductivity not updated + ! energy derivatives that might be treated as constant if heat capacity and thermal conductivity not updated integer(i4b) :: dVolHtCapBulk_dPsi0 = integerMissing ! derivative in bulk heat capacity w.r.t. matric potential integer(i4b) :: dVolHtCapBulk_dTheta = integerMissing ! derivative in bulk heat capacity w.r.t. volumetric water content - integer(i4b) :: dVolHtCapBulk_dCanWat = integerMissing ! derivative in bulk heat capacity w.r.t. volumetric water content + integer(i4b) :: dVolHtCapBulk_dCanWat = integerMissing ! derivative in bulk heat capacity w.r.t. canopy volumetric water content integer(i4b) :: dVolHtCapBulk_dTk = integerMissing ! derivative in bulk heat capacity w.r.t. temperature - integer(i4b) :: dVolHtCapBulk_dTkCanopy = integerMissing ! derivative in bulk heat capacity w.r.t. temperature + integer(i4b) :: dVolHtCapBulk_dTkCanopy = integerMissing ! derivative in bulk heat capacity w.r.t. canopy temperature integer(i4b) :: dThermalC_dTempAbove = integerMissing ! derivative in the thermal conductivity w.r.t. energy state in the layer above integer(i4b) :: dThermalC_dTempBelow = integerMissing ! derivative in the thermal conductivity w.r.t. energy state in the layer above integer(i4b) :: dThermalC_dWatAbove = integerMissing ! derivative in the thermal conductivity w.r.t. water state in the layer above integer(i4b) :: dThermalC_dWatBelow = integerMissing ! derivative in the thermal conductivity w.r.t. water state in the layer above + ! energy derivatives that might be treated as constant if Cm not updated + integer(i4b) :: dCm_dTk = integerMissing ! derivative in heat capacity w.r.t. temperature (J kg-1 K-2) + integer(i4b) :: dCm_dTkCanopy = integerMissing ! derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below integer(i4b) :: dNrgFlux_dTempAbove = integerMissing ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) integer(i4b) :: dNrgFlux_dTempBelow = integerMissing ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) @@ -663,6 +668,8 @@ MODULE var_lookup ! derivatives in time integer(i4b) :: mLayerdTemp_dt = integerMissing ! timestep change in layer temperature integer(i4b) :: scalarCanopydTemp_dt = integerMissing ! timestep change in canopy temperature + integer(i4b) :: mLayerdWat_dt = integerMissing ! timestep change in layer volumetric fraction of total water + integer(i4b) :: scalarCanopydWat_dt = integerMissing ! timestep change in canopy water content endtype iLook_deriv @@ -886,7 +893,8 @@ MODULE var_lookup 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,& 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,& 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,& - 81, 82, 83, 84, 85, 86, 87, 88, 89, 90) + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,& + 91, 92) ! named variables: model fluxes type(iLook_flux), public,parameter :: iLookFLUX =iLook_flux ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& @@ -905,7 +913,7 @@ MODULE var_lookup 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,& 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,& 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,& - 71) + 71, 72, 73, 74, 75) ! named variables: model indices type(iLook_index), public,parameter :: iLookINDEX =ilook_index ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& diff --git a/build/source/engine/computEnthalpy.f90 b/build/source/engine/computEnthalpy.f90 index 3d736d6cc..7b649d538 100644 --- a/build/source/engine/computEnthalpy.f90 +++ b/build/source/engine/computEnthalpy.f90 @@ -49,32 +49,31 @@ module computEnthalpy_module ! ********************************************************************************************************** subroutine computEnthalpy(& ! input - indx_data, & - nLayers, & - mLayerTemp, & - mLayerVolFracIce, & - mLayerHeatCap, & + indx_data, & ! intent(in): indices defining model states and layers + nLayers, & ! intent(in): number of snow layers + mLayerTemp, & ! intent(in): temperature of each snow/soil layer (K) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice (-) + mLayerHeatCap, & ! intent(in): heat capacity of each snow/soil layer (J m-3 K-1) ! output - mLayerEnthalpy & + mLayerEnthalpy & ! intent(out): enthalpy of each snow/soil layer (J m-3) ) ! -------------------------------------------------------------------------------------------------------------------------------- implicit none ! input: model control type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - integer(i4b),intent(in) :: nLayers ! number of snow layers + integer(i4b),intent(in) :: nLayers ! number of snow layers real(rkind),intent(in) :: mLayerTemp(:) ! temperature of each snow/soil layer (K) real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice (-) - real(rkind),intent(in) :: mLayerHeatCap(:) - real(rkind),intent(out) :: mLayerEnthalpy(:) - + real(rkind),intent(in) :: mLayerHeatCap(:) ! heat capacity of each snow/soil layer (J m-3 K-1) + real(rkind),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow/soil layer (J m-3) ! local variables - integer(i4b) :: iLayer + integer(i4b) :: iLayer ! loop index ! -------------------------------------------------------------------------------------------------------------------------------- associate(& - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of layer - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat & ! intent(in): [i4b(:)] indices for energy states + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of layer + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat & ! intent(in): [i4b(:)] indices for energy states ) ! (loop through non-missing energy state variables in the snow+soil domain) do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) @@ -95,44 +94,43 @@ end subroutine computEnthalpy ! ********************************************************************************************************** subroutine computEnthalpyPrime(& ! input - computeVegFlux, & - indx_data, & - nLayers, & - canopyDepth, & ! intent(in): canopy depth (m) - scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K) - scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2) - mLayerTempPrime, & - mLayerVolFracIcePrime, & - heatCapVeg, & - mLayerHeatCap, & - ! output - scalarCanopyEnthalpyPrime, & - mLayerEnthalpyPrime & + computeVegFlux, & ! intent(in): logical flag to denote if computing the vegetation flux + indx_data, & ! intent(in): indices defining model states and layers + nLayers, & ! intent(in): number of snow layers + canopyDepth, & ! intent(in): canopy depth (m) + scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K) + scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2) + mLayerTempPrime, & ! intent(in): temperature of each snow/soil layer (K) + mLayerVolFracIcePrime, & ! intent(in): Prime value for volumetric fraction of ice (-) + heatCapVeg, & ! intent(in): specific heat of vegetation (J kg-1 K-1) + mLayerHeatCap, & ! intent(in): heat capacity of each snow/soil layer (J m-3 K-1) + ! output + scalarCanopyEnthalpyPrime, & ! intent(out): Prime value for the enthalpy of the vegetation canopy (J m-2) + mLayerEnthalpyPrime & ! intent(out): enthalpy of each snow/soil layer (J m-3) ) ! -------------------------------------------------------------------------------------------------------------------------------- implicit none ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers integer(i4b),intent(in) :: nLayers ! number of snow layers - real(rkind),intent(in) :: canopyDepth ! canopy depth (m) + real(rkind),intent(in) :: canopyDepth ! canopy depth (m) real(rkind),intent(in) :: scalarCanopyTempPrime ! Prime value for the temperature of the vegetation canopy (K) - real(rkind),intent(in) :: scalarCanopyIcePrime ! Prime value for the ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: heatCapVeg - real(rkind),intent(in) :: mLayerTempPrime(:) ! temperature of each snow/soil layer (K) - real(rkind),intent(in) :: mLayerVolFracIcePrime(:) ! volumetric fraction of ice (-) - real(rkind),intent(in) :: mLayerHeatCap(:) - real(rkind),intent(out) :: scalarCanopyEnthalpyPrime - real(rkind),intent(out) :: mLayerEnthalpyPrime(:) - + real(rkind),intent(in) :: scalarCanopyIcePrime ! Prime value for the ice on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: heatCapVeg ! specific heat of vegetation (J kg-1 K-1) + real(rkind),intent(in) :: mLayerTempPrime(:) ! Prime value for temperature of each snow/soil layer (K) + real(rkind),intent(in) :: mLayerVolFracIcePrime(:) ! Prime value for volumetric fraction of ice (-) + real(rkind),intent(in) :: mLayerHeatCap(:) ! heat capacity of each snow/soil layer (J m-3 K-1) + real(rkind),intent(out) :: scalarCanopyEnthalpyPrime ! Prime value for the enthalpy of the vegetation canopy (J m-2) + real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! enthalpy of each snow/soil layer (J m-3) ! local variables - integer(i4b) :: iLayer + integer(i4b) :: iLayer ! loop index ! -------------------------------------------------------------------------------------------------------------------------------- associate(& - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of layer - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat & ! intent(in): [i4b(:)] indices for energy states + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of layer + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat & ! intent(in): [i4b(:)] indices for energy states ) if(computeVegFlux)then diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index b0c48ad67..d16bae97f 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -283,14 +283,14 @@ end subroutine computHeatCap ! public subroutine computStatMult: get scale factors ! ********************************************************************************************************** subroutine computStatMult(& - heatCapVeg, & - mLayerHeatCap, & + heatCapVeg, & ! intent(in): heat capacity for canopy + mLayerHeatCap, & ! intent(in): heat capacity for snow and soil ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers ! output - sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) - err,message) ! intent(out): error control + sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) + err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- USE nr_utility_module,only:arth ! get a sequence of numbers arth(start, incr, count) USE f2008funcs_module,only:findIndex ! finds the index of the first value within a vector @@ -546,17 +546,19 @@ end subroutine computHeatCapAnalytic ! ********************************************************************************************************** subroutine computCm(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux ! input: state variables - scalarCanopyTemp, & ! intent(in): value of canopy temperature (kg m-2) - mLayerTemp, & ! intent(in): vector of temperature (-) - mLayerMatricHead, & ! intent(in): vector of total water matric potential (m) + scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) + mLayerTemp, & ! intent(in): vector of temperature (K) + mLayerMatricHead, & ! intent(in): vector of total water matric potential (-) ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices ! output - scalarCanopyCm, & ! intent(out): Cm for vegetation - mLayerCm, & ! intent(out): Cm for soil and snow + scalarCanopyCm, & ! intent(out): Cm for vegetation (J kg K-1) + mLayerCm, & ! intent(out): Cm for soil and snow (J kg K-1) + dCm_dTk, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) + dCm_dTkCanopy, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) ! output: error control err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------------- @@ -565,15 +567,18 @@ subroutine computCm(& ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: model control logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - real(rkind),intent(in) :: scalarCanopyTemp ! value of canopy temperature (kg m-2) - real(rkind),intent(in) :: mLayerTemp(:) ! vector of temperature (-) - real(rkind),intent(in) :: mLayerMatricHead(:) ! vector of total water matric potential (m) + real(rkind),intent(in) :: scalarCanopyTemp ! value of canopy temperature (K) + real(rkind),intent(in) :: mLayerTemp(:) ! vector of temperature (K) + real(rkind),intent(in) :: mLayerMatricHead(:) ! vector of total water matric potential (-) ! input/output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model layer indices + ! output: Cm and derivatives + real(qp),intent(out) :: scalarCanopyCm ! Cm for vegetation (J kg K-1) + real(qp),intent(out) :: mLayerCm(:) ! Cm for soil and snow (J kg K-1) + real(rkind),intent(out) :: dCm_dTk(:) ! derivative in Cm w.r.t. temperature (J kg K-2) + real(rkind),intent(out) :: dCm_dTkCanopy ! derivative in Cm w.r.t. temperature (J kg K-2) ! output: error control - real(qp),intent(out) :: scalarCanopyCm ! Cm for vegetation - real(qp),intent(out) :: mLayerCm(:) ! Cm for soil and snow integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- @@ -581,10 +586,10 @@ subroutine computCm(& character(LEN=256) :: cmessage ! error message of downwind routine integer(i4b) :: iLayer ! index of model layer integer(i4b) :: iSoil ! index of soil layer - real(rkind) :: g1 - real(rkind) :: g2 + real(rkind) :: diffT ! temperature difference from Tfreeze + real(rkind) :: integral ! integral of snow freezing curve real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - + real(rkind) :: d_integral_dTk ! derivative of integral with temperature ! -------------------------------------------------------------------------------------------------------------------------------- ! associate variables in data structure associate(& @@ -604,12 +609,16 @@ subroutine computCm(& ! compute Cm of vegetation ! Note that scalarCanopyCm/iden_water is computed if(computeVegFlux)then - g2 = scalarCanopyTemp - Tfreeze - g1 = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * g2) - if(scalarCanopyTemp < Tfreeze)then - scalarCanopyCm = Cp_water * g1 + Cp_ice * (g2 - g1) + if(diffT>=0._rkind)then + scalarCanopyCm = Cp_water * diffT + ! derivatives + dCm_dTkCanopy = Cp_water else - scalarCanopyCm = Cp_water * g2 + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + scalarCanopyCm = Cp_water * integral + Cp_ice * (diffT - integral) + ! derivatives + d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) + dCm_dTkCanopy = Cp_water * d_integral_dTk + Cp_ice * (1._rkind - d_integral_dTk) end if end if @@ -625,19 +634,27 @@ subroutine computCm(& select case(layerType(iLayer)) ! * soil case(iname_soil) - g2 = mLayerTemp(iLayer) - Tfreeze + diffT = mLayerTemp(iLayer) - Tfreeze Tcrit = crit_soilT( mLayerMatricHead(iSoil) ) - if( mLayerTemp(iLayer) < Tcrit)then - mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air) * g2 - else - mLayerCm(iLayer) = (iden_water * Cp_water - iden_air * Cp_air) * g2 - end if + if( mLayerTemp(iLayer)>=Tcrit)then + mLayerCm(iLayer) = (iden_water * Cp_water - iden_air * Cp_air) * diffT + ! derivatives + dCm_dTk(iLayer) = (iden_water * Cp_water - iden_air * Cp_air) + else + mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air) * diffT + ! derivatives + dCm_dTk(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air) + endif case(iname_snow) - g2 = mLayerTemp(iLayer) - Tfreeze - g1 = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * g2) - mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( g2 - g1 ) & - + (iden_water * Cp_water - iden_air * Cp_air) * g1 + diffT = mLayerTemp(iLayer) - Tfreeze + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( diffT - integral ) & + + (iden_water * Cp_water - iden_air * Cp_air) * integral + ! derivatives + d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) + dCm_dTk(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( 1._rkind - d_integral_dTk ) & + + (iden_water * Cp_water - iden_air * Cp_air) * d_integral_dTk case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute Cm'; return end select diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 8c1dca2ef..a6c4aab87 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -237,14 +237,21 @@ subroutine computJacob(& dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. temperature + ! derivative in Cm w.r.t. relevant state variables + dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk )%dat ,& ! intent(in): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) + dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) ! derivatives in time - mLayerdTemp_dt => deriv_data%var(iLookDERIV%mLayerdTemp_dt )%dat ,& ! intent(in): [dp(:)] timestep change in layer temperature - scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt )%dat(1) ,& ! intent(in): [dp ] timestep change in canopy temperature + mLayerdTemp_dt => deriv_data%var(iLookDERIV%mLayerdTemp_dt )%dat ,& ! intent(in): [dp(:)] timestep change in layer temperature + scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt )%dat(1) ,& ! intent(in): [dp ] timestep change in canopy temperature + mLayerdWat_dt => deriv_data%var(iLookDERIV%mLayerdWat_dt )%dat ,& ! intent(in): [dp(:)] timestep change in layer volumetric fraction of total water + scalarCanopydWat_dt => deriv_data%var(iLookDERIV%scalarCanopydWat_dt )%dat(1) ,& ! intent(in): [dp ] timestep change in canopy total water ! diagnostic variables - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg )%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg )%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg )%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) + scalarCanopyCm => diag_data%var(iLookDIAG%scalarCanopyCm )%dat(1) ,& ! intent(in): [dp] Cm of canopy (J kg-1 K-1) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow )%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk )%dat ,& ! intent(in): [dp(:)] bulk volumetric heat capacity in each snow and soil layer (J m-3 K-1) + mLayerCm => diag_data%var(iLookDIAG%mLayerCm )%dat ,& ! intent(in): [dp(:)] Cm in each snow and soil layer (J kg-1 K-1) scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1) ,& ! intent(in): [dp] soil control on infiltration, zero or one ! canopy and layer depth canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth )%dat(1) ,& ! intent(in): [dp ] canopy depth (m) @@ -269,7 +276,8 @@ subroutine computJacob(& ! NOTE: energy for vegetation is computed *within* the iteration loop as it includes phase change if(ixVegNrg/=integerMissing)then dMat(ixVegNrg) = scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy & - + dVolHtCapBulk_dTkCanopy * scalarCanopydTemp_dt + + dVolHtCapBulk_dTkCanopy * scalarCanopydTemp_dt & + + dCm_dTkCanopy * scalarCanopydWat_dt/canopyDepth endif ! compute additional terms for the Jacobian for the snow-soil domain (excluding fluxes) @@ -277,14 +285,15 @@ subroutine computJacob(& do iLayer=1,nLayers if(ixSnowSoilNrg(iLayer)/=integerMissing)then dMat(ixSnowSoilNrg(iLayer)) = mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) & - + dVolHtCapBulk_dTk(iLayer) * mLayerdTemp_dt(iLayer) + + dVolHtCapBulk_dTk(iLayer) * mLayerdTemp_dt(iLayer) & + + dCm_dTk(iLayer) * mLayerdWat_dt(iLayer) endif end do ! compute additional terms for the Jacobian for the soil domain (excluding fluxes) do iLayer=1,nSoil if(ixSoilOnlyHyd(iLayer)/=integerMissing)then - dMat(ixSoilOnlyHyd(iLayer)) = dVolTot_dPsi0(iLayer) + dCompress_dPsi(iLayer) + dMat(ixSoilOnlyHyd(iLayer)) = dVolTot_dPsi0(iLayer) + dCompress_dPsi(iLayer) endif end do @@ -323,7 +332,8 @@ subroutine computJacob(& ! * cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth & - + dVolHtCapBulk_dCanWat * scalarCanopydTemp_dt - (dt/canopyDepth) * dCanopyNetFlux_dCanWat ! dF/dLiq + + dVolHtCapBulk_dCanWat * scalarCanopydTemp_dt + scalarCanopyCm/canopyDepth & + - (dt/canopyDepth) * dCanopyNetFlux_dCanWat if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) endif @@ -435,8 +445,8 @@ subroutine computJacob(& ! - include derivatives of energy fluxes w.r.t water fluxes for current layer aJac(ixOffDiag(nrgState,watState),watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water & - + dVolHtCapBulk_dTheta(iLayer) * mLayerdTemp_dt(iLayer) & - + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) ! (dF/dLiq) + + dVolHtCapBulk_dTheta(iLayer) * mLayerdTemp_dt(iLayer) + mLayerCm(iLayer) & + + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) ! - include derivatives of water fluxes w.r.t energy fluxes for current layer aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) @@ -604,7 +614,7 @@ subroutine computJacob(& endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerdTemp_dt(jLayer) & + aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerdTemp_dt(jLayer) + mLayerCm(iLayer) * dVolTot_dPsi0(iLayer) & + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(ixOffDiag(nrgState,watState),watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water + aJac(ixOffDiag(nrgState,watState),watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content @@ -673,7 +683,8 @@ subroutine computJacob(& ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth & - + dVolHtCapBulk_dCanWat * scalarCanopydTemp_dt - (dt/canopyDepth) * dCanopyNetFlux_dCanWat ! dF/dLiq + + dVolHtCapBulk_dCanWat * scalarCanopydTemp_dt + scalarCanopyCm/canopyDepth & + - (dt/canopyDepth) * dCanopyNetFlux_dCanWat if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) endif @@ -785,8 +796,8 @@ subroutine computJacob(& ! - include derivatives of energy fluxes w.r.t water fluxes for current layer aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water & - + dVolHtCapBulk_dTheta(iLayer) * mLayerdTemp_dt(iLayer) & - + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) ! (dF/dLiq) + + dVolHtCapBulk_dTheta(iLayer) * mLayerdTemp_dt(iLayer) + mLayerCm(iLayer) & + + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) ! - include derivatives of water fluxes w.r.t energy fluxes for current layer aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) @@ -933,7 +944,7 @@ subroutine computJacob(& endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerdTemp_dt(jLayer) & + aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerdTemp_dt(jLayer) + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) & + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(nrgState,watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index 2e22fbdee..c189af2c7 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -273,11 +273,16 @@ subroutine computJacobWithPrime(& dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. temperature + ! derivative in Cm w.r.t. relevant state variables + dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk )%dat ,& ! intent(in): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) + dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) ! diagnostic variables scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg )%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg )%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) + scalarCanopyCm => diag_data%var(iLookDIAG%scalarCanopyCm )%dat(1) ,& ! intent(in): [dp] Cm of canopy (J kg-1 K-1) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow )%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk )%dat ,& ! intent(in): [dp(:)] bulk volumetric heat capacity in each snow and soil layer (J m-3 K-1) + mLayerCm => diag_data%var(iLookDIAG%mLayerCm )%dat ,& ! intent(in): [dp(:)] Cm in each snow and soil layer (J kg-1 K-1) scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1) ,& ! intent(in): [dp] soil control on infiltration, zero or one ! canopy and layer depth canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth )%dat(1) ,& ! intent(in): [dp ] canopy depth (m) @@ -304,6 +309,7 @@ subroutine computJacobWithPrime(& if(ixVegNrg/=integerMissing)then dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy ) * cj & + dVolHtCapBulk_dTkCanopy * scalarCanopyTempPrime & + + dCm_dTkCanopy * scalarCanopyWatPrime / canopyDepth & + LH_fus*iden_water * scalarCanopyTempPrime * d2Theta_dTkCanopy2 & + LH_fus * dFracLiqVeg_dTkCanopy * scalarCanopyWatPrime / canopyDepth endif @@ -314,6 +320,7 @@ subroutine computJacobWithPrime(& if(ixSnowSoilNrg(iLayer)/=integerMissing)then dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj & + dVolHtCapBulk_dTk(iLayer) * mLayerTempPrime(iLayer) & + + dCm_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) & + LH_fus*iden_water * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & + LH_fus*iden_water * dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) endif @@ -365,8 +372,9 @@ subroutine computJacobWithPrime(& ! * cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth * cj & - + dVolHtCapBulk_dCanWat * scalarCanopyTempPrime - (dt/canopyDepth) * dCanopyNetFlux_dCanWat & - + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth ! dF/dLiq + + dVolHtCapBulk_dCanWat * scalarCanopyTempPrime + scalarCanopyCm/canopyDepth * cj & + - (dt/canopyDepth) * dCanopyNetFlux_dCanWat & + + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) endif @@ -478,7 +486,7 @@ subroutine computJacobWithPrime(& ! - include derivatives of energy fluxes w.r.t water fluxes for current layer aJac(ixOffDiag(nrgState,watState),watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & - + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) & + + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) + mLayerCm(iLayer) * cj & + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) & + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) @@ -648,8 +656,8 @@ subroutine computJacobWithPrime(& endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) & - + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) * cj & + + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + mLayerCm(jLayer) * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(ixOffDiag(nrgState,watState),watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) * cj & - LH_fus*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_d2Psi0(iLayer) + aJac(ixOffDiag(nrgState,watState),watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content @@ -718,8 +726,9 @@ subroutine computJacobWithPrime(& ! * cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth * cj & - + dVolHtCapBulk_dCanWat * scalarCanopyTempPrime - (dt/canopyDepth) * dCanopyNetFlux_dCanWat & - + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth ! dF/dLiq + + dVolHtCapBulk_dCanWat * scalarCanopyTempPrime + scalarCanopyCm/canopyDepth * cj& + - (dt/canopyDepth) * dCanopyNetFlux_dCanWat & + + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) endif @@ -831,7 +840,7 @@ subroutine computJacobWithPrime(& ! - include derivatives of energy fluxes w.r.t water fluxes for current layer aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & - + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) & + + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) + mLayerCm(iLayer) * cj & + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) & + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) @@ -980,8 +989,8 @@ subroutine computJacobWithPrime(& endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) & - + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) * cj & + + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + mLayerCm(jLayer) * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(nrgState,watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) * cj & - LH_fus*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_d2Psi0(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content diff --git a/build/source/engine/computResid.f90 b/build/source/engine/computResid.f90 index 920524576..7e1134589 100644 --- a/build/source/engine/computResid.f90 +++ b/build/source/engine/computResid.f90 @@ -95,8 +95,8 @@ subroutine computResid(& mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) mLayerVolFracWatTrial, & ! intent(in): trial value for the volumetric water in each snow and soil layer (-) mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liq in each snow and soil layer (-) - scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) - mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) + scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (J kg K-1) + mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (J kg K-1) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU diff --git a/build/source/engine/computResidWithPrime.f90 b/build/source/engine/computResidWithPrime.f90 index d5975895a..8f2c34874 100644 --- a/build/source/engine/computResidWithPrime.f90 +++ b/build/source/engine/computResidWithPrime.f90 @@ -79,8 +79,8 @@ subroutine computResidWithPrime(& mLayerVolFracIcePrime, & ! intent(in): Prime value for the volumetric ice in each snow and soil layer (s-1) mLayerVolFracWatPrime, & ! intent(in): Prime value for the volumetric water in each snow and soil layer (s-1) mLayerVolFracLiqPrime, & ! intent(in): Prime value for the volumetric liq in each snow and soil layer (s-1) - scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) - mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) + scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (J kg K-1) + mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (J kg K-1) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 7fa952b07..46987a4f5 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -272,12 +272,17 @@ subroutine eval8summa(& dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk)%dat ,& ! intent(out): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) + dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) ! mapping ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)]mapping of full state vector to the state subset ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)]index of control volume for different domains (veg, snow, soil) ! heat capacity - heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),&! intent(out): [dp] volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & ! intent(out): [dp(:)] heat capacity for snow and soil + heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(out): [dp] volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(out): [dp(:)] heat capacity for snow and soil + ! Cm + canopyCmTrial => diag_data%var(iLookDIAG%scalarCanopyCm)%dat(1) ,& ! intent(out): [dp] Cm of the canopy + mLayerCmTrial => diag_data%var(iLookDIAG%mLayerCm)%dat & ! intent(out): [dp(:)] Cm of snow and soil ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -565,17 +570,21 @@ subroutine eval8summa(& ! input: state variables scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (m) + mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (-) ! input data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices ! output - scalarCanopyCmTrial, & ! intent(out): Cm for vegetation - mLayerCmTrial, & ! intent(out): Cm for soil and snow + canopyCmTrial, & ! intent(out): Cm for vegetation (J kg K-1) + mLayerCmTrial, & ! intent(out): Cm for soil and snow (J kg K-1) + dCm_dTk, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) + dCm_dTkCanopy, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) err,cmessage) ! intent(out): error control else - scalarCanopyCmTrial = 0._qp + canopyCmTrial = 0._qp mLayerCmTrial = 0._qp + dCm_dTk = 0._rkind + dCm_dTkCanopy = 0._rkind endif ! needCm ! save the number of flux calls per time step diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index cb4deff81..57263af37 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -275,12 +275,17 @@ subroutine eval8summaWithPrime(& dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk)%dat ,& ! intent(out): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) + dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) ! mapping ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)]mapping of full state vector to the state subset ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)]index of control volume for different domains (veg, snow, soil) ! heat capacity heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(out): [dp] volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat & ! intent(out): [dp(:)] heat capacity for snow and soil + mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(out): [dp(:)] heat capacity for snow and soil + ! Cm + canopyCmTrial => diag_data%var(iLookDIAG%scalarCanopyCm)%dat(1) ,& ! intent(out): [dp] Cm of the canopy + mLayerCmTrial => diag_data%var(iLookDIAG%mLayerCm)%dat & ! intent(out): [dp(:)] Cm of snow and soil ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -612,17 +617,22 @@ subroutine eval8summaWithPrime(& ! input: state variables scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (m) + mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (-) ! input data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices ! output - scalarCanopyCmTrial, & ! intent(out): Cm for vegetation - mLayerCmTrial, & ! intent(out): Cm for soil and snow + ! output + canopyCmTrial, & ! intent(out): Cm for vegetation (J kg K-1) + mLayerCmTrial, & ! intent(out): Cm for soil and snow (J kg K-1) + dCm_dTk, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) + dCm_dTkCanopy, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) err,cmessage) ! intent(out): error control else - scalarCanopyCmTrial = 0._qp + canopyCmTrial = 0._qp mLayerCmTrial = 0._qp + dCm_dTk = 0._rkind + dCm_dTkCanopy = 0._rkind endif ! needCm ! save the number of flux calls per time step diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index 344bb335b..dd1e85842 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -241,7 +241,7 @@ end subroutine T2E_lookup ! public subroutine t2enthalpy: compute enthalpy from temperature and total water content ! ************************************************************************************************************************ subroutine t2enthalpy(& - doPhase, & ! intent(in): logical flag to include phase change in enthalpy or not + doPhase, & ! intent(in): logical flag to include phase change in enthalpy or not ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure @@ -258,11 +258,11 @@ subroutine t2enthalpy(& mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) mLayerVolFracIceTrial, & ! intent(in) ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) @@ -329,7 +329,7 @@ subroutine t2enthalpy(& real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) real(rkind) :: psiLiq ! matric head of liquid water (m) real(rkind) :: volFracWat ! volumetric fraction of total water, liquid+ice (-) - real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) + real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) real(rkind) :: volFracIce ! volumetric fraction of ice (-) real(rkind) :: diffT ! temperature difference from Tfreeze real(rkind) :: integral ! integral of snow freezing curve @@ -338,7 +338,6 @@ subroutine t2enthalpy(& real(rkind) :: d_integral_dTk ! derivative of integral with temperature real(rkind) :: dE ! derivative of enthalpy with temperature at layer temperature real(rkind) :: dEcrit ! derivative of enthalpy with temperature at critical temperature - ! enthalpy real(rkind) :: enthVeg ! enthalpy of the vegetation (J m-3) real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) @@ -472,7 +471,7 @@ subroutine t2enthalpy(& scalarCanopyEnthalpy = enthVeg + enthLiq + enthIce dCanEnthalpy_dTk = dEnthVeg_dTk + dEnthLiq_dTk + dEnthIce_dTk dCanEnthalpy_dWat = dEnthVeg_dWat + dEnthLiq_dWat + dEnthIce_dWat - if (doPhase)then + if (doPhase)then ! only need for calculating energy balance error so shouldn't need derivatives scalarCanopyEnthalpy = scalarCanopyEnthalpy - enthPhase dCanEnthalpy_dTk = dCanEnthalpy_dTk - dEnthPhase_dTk dCanEnthalpy_dWat = dCanEnthalpy_dWat - dEnthPhase_dWat @@ -520,7 +519,7 @@ subroutine t2enthalpy(& mLayerEnthalpy(iLayer) = enthLiq + enthIce + enthAir dEnthalpy_dTk(iLayer) = dEnthLiq_dTk + dEnthIce_dTk + dEnthAir_dTk dEnthalpy_dWat(iLayer) = dEnthLiq_dWat + dEnthIce_dWat + dEnthAir_dWat - if (doPhase)then + if (doPhase)then ! only need for calculating energy balance error so shouldn't need derivatives mLayerEnthalpy(iLayer) = mLayerEnthalpy(iLayer) - enthPhase dEnthalpy_dTk(iLayer) = dEnthalpy_dTk(iLayer) - dEnthPhase_dTk dEnthalpy_dWat(iLayer) = dEnthalpy_dWat(iLayer) - dEnthPhase_dWat @@ -554,7 +553,7 @@ subroutine t2enthalpy(& diffT = mLayerTempTrial(iLayer) - Tfreeze ! *** compute enthalpy of water for unfrozen conditions - if(mlayerTempTrial(iLayer) > Tcrit)then + if(mlayerTempTrial(iLayer)>=Tcrit)then enthWater = iden_water*Cp_water*volFracWat*diffT ! valid for temperatures below freezing also enthPhase = 0._rkind ! enthalpy derivatives @@ -607,7 +606,7 @@ subroutine t2enthalpy(& mLayerEnthalpy(iLayer) = enthSoil + enthWater + enthAir dEnthalpy_dTk(iLayer) = dEnthSoil_dTk + dEnthWater_dTk + dEnthAir_dTk dEnthalpy_dWat(iLayer) = dEnthSoil_dWat + dEnthWater_dWat + dEnthAir_dWat - if (doPhase)then + if (doPhase)then ! only need for calculating energy balance error so shouldn't need derivatives mLayerEnthalpy(iLayer) = mLayerEnthalpy(iLayer) - enthPhase dEnthalpy_dTk(iLayer) = dEnthalpy_dTk(iLayer) - dEnthPhase_dTk dEnthalpy_dWat(iLayer) = dEnthalpy_dWat(iLayer) - dEnthPhase_dWat diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 index 03cd43729..e2295aaf1 100644 --- a/build/source/engine/updateVars.f90 +++ b/build/source/engine/updateVars.f90 @@ -206,35 +206,39 @@ subroutine updateVars(& ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ! depth-varying model parameters - vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ! depth-varying model parameters + vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) ! model diagnostic variables (heat capacity) - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(in): [dp ] volumetric heat capacity of the vegetation (J m-3 K-1) - mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] volumetric heat capacity in each layer (J m-3 K-1) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(in): [dp ] volumetric heat capacity of the vegetation (J m-3 K-1) + mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] volumetric heat capacity in each layer (J m-3 K-1) ! model diagnostic variables (fraction of liquid water) - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) ! model states from a previous solution - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) ! model diagnostic variables from a previous solution - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) ! derivatives - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential - dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) - dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(out): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential + dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) + dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(out): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature ! derivatives inside solver for Jacobian only - mLayerdTemp_dt => deriv_data%var(iLookDERIV%mLayerdTemp_dt )%dat ,& ! intent(out): [dp(:)] timestep change in layer temperature - scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt )%dat(1) & ! intent(out): [dp ] timestep change in canopy temperature + mLayerdTemp_dt => deriv_data%var(iLookDERIV%mLayerdTemp_dt )%dat ,& ! intent(out): [dp(:)] timestep change in layer temperature + scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt )%dat(1),& ! intent(out): [dp ] timestep change in canopy temperature + mLayerdWat_dt => deriv_data%var(iLookDERIV%mLayerdWat_dt)%dat ,& ! intent(out): [dp(:)] timestep change in layer volumetric fraction of total water + scalarCanopydWat_dt => deriv_data%var(iLookDERIV%scalarCanopydWat_dt)%dat(1) & ! intent(out): [dp ] timestep change in canopy total water ) ! association with variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- @@ -500,6 +504,12 @@ subroutine updateVars(& endif ! if energy state or solution is coupled + ! compute water time derivatives + select case(ixDomainType) + case(iname_veg); scalarCanopydWat_dt = scalarCanopyWatTrial - scalarCanopyWat + case(iname_snow, iname_soil); mLayerdWat_dt(iLayer) = mLayerVolFracWatTrial(iLayer) - mLayerVolFracWat(iLayer) + end select + ! ----- ! - update temperatures... ! ------------------------ From 0150e5c890ceb28a8355343acd1ec7f0c737e523 Mon Sep 17 00:00:00 2001 From: ashleymedin <34691332+ashleymedin@users.noreply.github.com> Date: Sat, 28 Oct 2023 22:10:52 +0900 Subject: [PATCH 0983/1472] Merge pull request #27 from seantrim/develop_object_oriented Applied object-oriented methods to simplify call to stateFilter in opSplittin --- build/source/dshare/data_types.f90 | 49 ++++++++++++++++++ build/source/engine/opSplittin.f90 | 80 ++++++++++++++++++------------ 2 files changed, 97 insertions(+), 32 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index d26a691d1..c48a28007 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -573,6 +573,27 @@ MODULE data_types end type out_type_bigAquifer ! ** end bigAquifer + ! ** stateFilter + type, public :: in_type_stateFilter ! class for intent(in) arguments in stateFilter call + integer(i4b) :: ixCoupling ! intent(in): index of coupling method (1,2) + integer(i4b) :: ixSolution ! intent(in): index of solution method (1,2) + integer(i4b) :: ixStateThenDomain ! intent(in): switch between full domain and sub domains + integer(i4b) :: iStateTypeSplit ! intent(in): index of the state type split + integer(i4b) :: iDomainSplit ! intent(in): index of the domain split + integer(i4b) :: iStateSplit ! intent(in): index of the layer split + contains + procedure :: initialize => initialize_in_stateFilter + end type in_type_stateFilter + + type, public :: out_type_stateFilter ! class for intent(out) arguments in stateFilter call + integer(i4b) :: nSubset ! intent(out): number of selected state variables for a given split + integer(i4b) :: err ! intent(out): error code + character(:),allocatable :: cmessage ! intent(out): error message + contains + procedure :: finalize => finalize_out_stateFilter + end type out_type_stateFilter + ! ** end stateFilter + ! ** varSubstep type, public :: in_type_varSubstep ! class for intent(in) arguments in varSubstep call real(rkind) :: dt ! intent(in): time step (s) @@ -1248,6 +1269,34 @@ subroutine finalize_out_bigAquifer(out_bigAquifer,flux_data,deriv_data,err,cmess end subroutine finalize_out_bigAquifer ! **** end bigAquifer **** + ! **** stateFilter **** + subroutine initialize_in_stateFilter(in_stateFilter,ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) + class(in_type_stateFilter),intent(out) :: in_stateFilter ! class object for intent(in) stateFilter arguments + integer(i4b),intent(in) :: ixCoupling ! intent(in): index of coupling method (1,2) + integer(i4b),intent(in) :: ixSolution ! intent(in): index of solution method (1,2) + integer(i4b),intent(in) :: ixStateThenDomain ! intent(in): switch between full domain and sub domains + integer(i4b),intent(in) :: iStateTypeSplit ! intent(in): index of the state type split + integer(i4b),intent(in) :: iDomainSplit ! intent(in): index of the domain split + integer(i4b),intent(in) :: iStateSplit ! intent(in): index of the layer split + in_stateFilter % ixCoupling = ixCoupling ! intent(in): index of coupling method (1,2) + in_stateFilter % ixSolution = ixSolution ! intent(in): index of solution method (1,2) + in_stateFilter % ixStateThenDomain = ixStateThenDomain ! intent(in): switch between full domain and sub domains + in_stateFilter % iStateTypeSplit = iStateTypeSplit ! intent(in): index of the state type split + in_stateFilter % iDomainSplit = iDomainSplit ! intent(in): index of the domain split + in_stateFilter % iStateSplit = iStateSplit ! intent(in): index of the layer split + end subroutine initialize_in_stateFilter + + subroutine finalize_out_stateFilter(out_stateFilter,nSubset,err,cmessage) + class(out_type_stateFilter),intent(in) :: out_stateFilter ! class object for intent(in) stateFilter arguments + integer(i4b),intent(out) :: nSubset ! intent(out): number of selected state variables for a given split + integer(i4b),intent(out) :: err ! intent(out): error code + character(*),intent(out) :: cmessage ! intent(out): error message + nSubset = out_stateFilter % nSubset ! intent(out): number of selected state variables for a given split + err = out_stateFilter % err ! intent(out): error code + cmessage = out_stateFilter % cmessage ! intent(out): error message + end subroutine finalize_out_stateFilter + ! **** end stateFilter **** + ! **** varSubstep **** subroutine initialize_in_varSubstep(in_varSubstep,dt,dtInit,dt_min,whole_step,nSubset,& doAdjustTemp,firstSubStep,computeVegFlux,ixSolution,scalar,iStateSplit,fluxMask) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 0f474c92e..4c849b097 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -95,6 +95,7 @@ module opSplittin_module var_dlength, & ! data vector with variable length dimension (rkind) zLookup, & ! lookup tables model_options, & ! defines the model decisions + in_type_statefilter,out_type_statefilter, & ! classes for stateFilter objects in_type_varSubstep,io_type_varSubstep,out_type_varSubstep ! classes for varSubstep objects ! look-up values for the numerical method @@ -289,9 +290,10 @@ subroutine opSplittin(& real(rkind) :: mean_step_state ! mean step over the state (with or without domain splits) real(rkind) :: mean_step_solution ! mean step for a solution (scalar or vector) logical(lgt) :: addFirstFlux ! flag to add the first flux to the mask - ! ---------------------- classes for flux subroutine arguments (classes defined in data_types module) ---------------------- - ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** - type(in_type_varSubstep) :: in_varSubstep; type(io_type_varSubstep) :: io_varSubstep; type(out_type_varSubstep) :: out_varSubstep;! varSubstep arguments + ! ------------------------ classes for subroutine arguments (classes defined in data_types module) ------------------------ + ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** + type(in_type_stateFilter) :: in_stateFilter; type(out_type_stateFilter) :: out_stateFilter; ! stateFilter arguments + type(in_type_varSubstep) :: in_varSubstep; type(io_type_varSubstep) :: io_varSubstep; type(out_type_varSubstep) :: out_varSubstep; ! varSubstep arguments ! --------------------------------------------------------------------------------------- ! point to variables in the data structures @@ -334,6 +336,7 @@ subroutine opSplittin(& ! *** initialize *** call initialize_opSplittin + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! ========================================================================================================================================== ! loop through different coupling strategies @@ -439,9 +442,10 @@ subroutine opSplittin(& ! ------------------------------------------- ! get the mask for the state subset - call stateFilter(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit,& - indx_data,stateMask,nSubset,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + call initialize_stateFilter + call stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) + call finalize_stateFilter + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! error control ! check that state variables exist if(nSubset==0) cycle domainSplit @@ -657,6 +661,7 @@ subroutine opSplittin(& indx_data,prog_data,diag_data,flux_data,flux_mean,deriv_data,bvar_data,& out_varSubstep) ! intent(out): class object for model control call finalize_varSubstep + if (err/=0) then; message=trim(message)//trim(cmessage); if (err>0) return; end if ! error control ! reduce coupled step if failed the minimum step for the scalar solution if(failedMinimumStep .and. ixSolution==scalar) reduceCoupledStep=.true. @@ -824,31 +829,31 @@ subroutine allocate_memory ! *** allocate memory for local structures *** ! allocate space for the flux mask (used to define when fluxes are updated) call allocLocal(flux_meta(:),fluxMask,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; endif + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if ! allocate space for the flux count (used to check that fluxes are only updated once) call allocLocal(flux_meta(:),fluxCount,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; endif + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if ! allocate space for the temporary prognostic variable structure call allocLocal(prog_meta(:),prog_temp,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; endif + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if ! allocate space for the temporary diagnostic variable structure call allocLocal(diag_meta(:),diag_temp,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; endif + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if ! allocate space for the temporary flux variable structure call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; endif + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if ! allocate space for the mean flux variable structure call allocLocal(flux_meta(:),flux_mean,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; endif + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if ! allocate space for the temporary mean flux variable structure call allocLocal(flux_meta(:),flux_mntemp,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; endif + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if ! allocate space for the derivative structure call allocLocal(deriv_meta(:),deriv_data,nSnow,nSoil,err,cmessage) @@ -869,13 +874,22 @@ subroutine finalize_opSplittin print*, 'fluxCount%var(iVar)%dat = ', fluxCount%var(iVar)%dat message=trim(message)//'flux '//trim(flux_meta(iVar)%varname)//' was not computed' err=20; return - endif + end if end do ! use step halving if unable to complete the fully coupled solution in one substep if (ixCoupling/=fullyCoupled .or. nSubsteps>1) dtMultiplier=0.5_rkind end subroutine finalize_opSplittin + ! **** stateFilter **** + subroutine initialize_stateFilter + call in_stateFilter % initialize(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) + end subroutine initialize_stateFilter + + subroutine finalize_stateFilter + call out_stateFilter % finalize(nSubset,err,cmessage) + end subroutine finalize_stateFilter + ! **** varSubstep **** subroutine initialize_varSubstep call in_varSubstep % initialize(dt,dtInit,dt_min,whole_step,nSubset,doAdjustTemp,firstSubStep,computeVegFlux,ixSolution,scalar,iStateSplit,fluxMask) @@ -885,7 +899,6 @@ end subroutine initialize_varSubstep subroutine finalize_varSubstep call io_varSubstep % finalize(firstFluxCall,fluxCount,ixSaturation) call out_varSubstep % finalize(dtMultiplier,nSubsteps,failedMinimumStep,reduceCoupledStep,tooMuchMelt,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); if (err>0) return; end if ! error control end subroutine finalize_varSubstep end subroutine opSplittin @@ -893,30 +906,29 @@ end subroutine opSplittin ! ********************************************************************************************************** ! private subroutine stateFilter: get a mask for the desired state variables ! ********************************************************************************************************** -subroutine stateFilter(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit,& - indx_data,stateMask,nSubset,err,message) +subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) - USE indexState_module,only:indxSubset ! get state indices + USE indexState_module,only:indxSubset ! get state indices implicit none ! input - integer(i4b),intent(in) :: ixCoupling ! index of coupling method (1,2) - integer(i4b),intent(in) :: ixSolution ! index of solution method (1,2) - integer(i4b),intent(in) :: ixStateThenDomain ! switch between full domain and sub domains - integer(i4b),intent(in) :: iStateTypeSplit ! index of the state type split - integer(i4b),intent(in) :: iDomainSplit ! index of the domain split - integer(i4b),intent(in) :: iStateSplit ! index of the layer split - type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU + type(in_type_stateFilter),intent(in) :: in_stateFilter ! indices + type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU ! output - logical(lgt),intent(out) :: stateMask(:) ! mask defining desired state variables - integer(i4b),intent(out) :: nSubset ! number of selected state variables for a given split - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + logical(lgt),intent(out) :: stateMask(:) ! mask defining desired state variables + type(out_type_stateFilter),intent(out) :: out_stateFilter ! number of selected state variables for a given split and error control ! local - integer(i4b),allocatable :: ixSubset(:) ! list of indices in the state subset - character(len=256) :: cmessage ! error message + integer(i4b),allocatable :: ixSubset(:) ! list of indices in the state subset + character(len=256) :: cmessage ! error message ! -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ! data structures associate(& + ! indices for splitting methods + ixCoupling => in_stateFilter % ixCoupling ,& ! intent(in): [i4b] index of coupling method (1,2) + ixSolution => in_stateFilter % ixSolution ,& ! intent(in): [i4b] index of solution method (1,2) + ixStateThenDomain => in_stateFilter % ixStateThenDomain ,& ! intent(in): [i4b] switch between full domain and sub domains + iStateTypeSplit => in_stateFilter % iStateTypeSplit ,& ! intent(in): [i4b] index of the state type split + iDomainSplit => in_stateFilter % iDomainSplit ,& ! intent(in): [i4b] index of the domain split + iStateSplit => in_stateFilter % iStateSplit ,& ! intent(in): [i4b] index of the layer split ! indices of model state variables ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain @@ -929,7 +941,11 @@ subroutine stateFilter(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,i ! number of layers nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in): [i4b] total number of layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers + ! output + nSubset => out_stateFilter % nSubset ,& ! intent(out): number of selected state variables for a given split + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage & ! intent(out): error message ) ! data structures ! -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control From e8133e906b71093f4570cf52db9967098a1f4c7d Mon Sep 17 00:00:00 2001 From: ashleymedin <34691332+ashleymedin@users.noreply.github.com> Date: Wed, 1 Nov 2023 21:49:01 +0900 Subject: [PATCH 0984/1472] Merge pull request #28 from seantrim/develop_object_oriented Simplfiy call to indexSplit in opSplittin using object-oriented methods --- build/source/dshare/data_types.f90 | 47 ++++- build/source/engine/indexState.f90 | 53 +++--- build/source/engine/opSplittin.f90 | 271 ++++++++++++++--------------- 3 files changed, 202 insertions(+), 169 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index c48a28007..97607e7b3 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -594,6 +594,25 @@ MODULE data_types end type out_type_stateFilter ! ** end stateFilter + ! ** indexSplit + type, public :: in_type_indexSplit ! class for intent(in) arguments in indexSplit call + integer(i4b) :: nSnow ! intent(in): number of snow layers + integer(i4b) :: nSoil ! intent(in): number of soil layers + integer(i4b) :: nLayers ! intent(in): total number of layers + integer(i4b) :: nSubset ! intent(in): number of states in the subset + contains + procedure :: initialize => initialize_in_indexSplit + end type in_type_indexSplit + + type, public :: out_type_indexSplit ! class for intent(out) arguments in indexSplit call + integer(i4b) :: err ! intent(out): error code + character(:),allocatable :: cmessage ! intent(out): error message + contains + procedure :: finalize => finalize_out_indexSplit + end type out_type_indexSplit + ! ** end indexSplit + + ! ** varSubstep type, public :: in_type_varSubstep ! class for intent(in) arguments in varSubstep call real(rkind) :: dt ! intent(in): time step (s) @@ -1287,7 +1306,7 @@ subroutine initialize_in_stateFilter(in_stateFilter,ixCoupling,ixSolution,ixStat end subroutine initialize_in_stateFilter subroutine finalize_out_stateFilter(out_stateFilter,nSubset,err,cmessage) - class(out_type_stateFilter),intent(in) :: out_stateFilter ! class object for intent(in) stateFilter arguments + class(out_type_stateFilter),intent(in) :: out_stateFilter ! class object for intent(out) stateFilter arguments integer(i4b),intent(out) :: nSubset ! intent(out): number of selected state variables for a given split integer(i4b),intent(out) :: err ! intent(out): error code character(*),intent(out) :: cmessage ! intent(out): error message @@ -1297,6 +1316,28 @@ subroutine finalize_out_stateFilter(out_stateFilter,nSubset,err,cmessage) end subroutine finalize_out_stateFilter ! **** end stateFilter **** + ! **** indexSplit **** + subroutine initialize_in_indexSplit(in_indexSplit,nSnow,nSoil,nLayers,nSubset) + class(in_type_indexSplit),intent(out) :: in_indexSplit ! class object for intent(in) indexSplit arguments + integer(i4b),intent(in) :: nSnow ! intent(in): number of snow layers + integer(i4b),intent(in) :: nSoil ! intent(in): number of soil layers + integer(i4b),intent(in) :: nLayers ! intent(in): total number of layers + integer(i4b),intent(in) :: nSubset ! intent(in): number of states in the subset + in_indexSplit % nSnow = nSnow ! intent(in): number of snow layers + in_indexSplit % nSoil = nSoil ! intent(in): number of soil layers + in_indexSplit % nLayers = nLayers ! intent(in): total number of layers + in_indexSplit % nSubset = nSubset ! intent(in): number of states in the subset + end subroutine initialize_in_indexSplit + + subroutine finalize_out_indexSplit(out_indexSplit,err,cmessage) + class(out_type_indexSplit),intent(in) :: out_indexSplit ! class object for intent(out) indexSplit arguments + integer(i4b),intent(out) :: err ! intent(out): error code + character(*),intent(out) :: cmessage ! intent(out): error message + err = out_indexSplit % err ! intent(out): error code + cmessage = out_indexSplit % cmessage ! intent(out): error message + end subroutine finalize_out_indexSplit + ! **** end indexSplit **** + ! **** varSubstep **** subroutine initialize_in_varSubstep(in_varSubstep,dt,dtInit,dt_min,whole_step,nSubset,& doAdjustTemp,firstSubStep,computeVegFlux,ixSolution,scalar,iStateSplit,fluxMask) @@ -1329,7 +1370,7 @@ subroutine initialize_in_varSubstep(in_varSubstep,dt,dtInit,dt_min,whole_step,nS end subroutine initialize_in_varSubstep subroutine initialize_io_varSubstep(io_varSubstep,firstFluxCall,fluxCount,ixSaturation) - class(io_type_varSubstep),intent(out) :: io_varSubstep ! class object for intent(in) varSubstep arguments + class(io_type_varSubstep),intent(out) :: io_varSubstep ! class object for intent(inout) varSubstep arguments logical(lgt),intent(in) :: firstFluxCall ! flag to indicate if we are processing the first flux call type(var_ilength),intent(in) :: fluxCount ! number of times fluxes are updated (should equal nsubstep) integer(i4b),intent(in) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) @@ -1341,7 +1382,7 @@ subroutine initialize_io_varSubstep(io_varSubstep,firstFluxCall,fluxCount,ixSatu end subroutine initialize_io_varSubstep subroutine finalize_io_varSubstep(io_varSubstep,firstFluxCall,fluxCount,ixSaturation) - class(io_type_varSubstep),intent(in) :: io_varSubstep ! class object for intent(in) varSubstep arguments + class(io_type_varSubstep),intent(in) :: io_varSubstep ! class object for intent(inout) varSubstep arguments logical(lgt),intent(out) :: firstFluxCall ! flag to indicate if we are processing the first flux call type(var_ilength),intent(out) :: fluxCount ! number of times fluxes are updated (should equal nsubstep) integer(i4b),intent(out) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) diff --git a/build/source/engine/indexState.f90 b/build/source/engine/indexState.f90 index 9c87d9833..be42a07ce 100644 --- a/build/source/engine/indexState.f90 +++ b/build/source/engine/indexState.f90 @@ -24,7 +24,8 @@ module indexState_module USE nrtype ! derived types to define the data structures -USE data_types,only:var_ilength ! data vector with variable length dimension (i4b) +USE data_types,only:var_ilength ! data vector with variable length dimension (i4b) +USE data_types,only:in_type_indexSplit,out_type_indexSplit ! classes for indexSplit subroutine arguments ! missing data USE globalData,only:integerMissing ! missing integer @@ -276,41 +277,44 @@ end subroutine indexState ! ********************************************************************************************************** ! public subroutine indexSplit: define list of indices for each state variable ! ********************************************************************************************************** - subroutine indexSplit(stateSubsetMask, & ! intent(in) : logical vector (.true. if state is in the subset) - nSnow,nSoil,nLayers,nSubset, & ! intent(in) : number of snow and soil layers, and total number of layers + subroutine indexSplit(in_indexSplit, & ! intent(in) : number of model layers and states in a subset + stateSubsetMask, & ! intent(in) : logical vector (.true. if state is in the subset) indx_data, & ! intent(inout) : index data structure - err,message) ! intent(out) : error control + out_indexSplit) ! intent(out) : error control ! external modules USE f2008funcs_module,only:findIndex ! finds the index of the first value within a vector USE nr_utility_module,only:arth ! creates a sequence of numbers (start, incr, n) implicit none ! -------------------------------------------------------------------------------------------------------------------------------- ! input - logical(lgt),intent(in) :: stateSubsetMask(:) ! logical vector (.true. if state is in the subset) - integer(i4b),intent(in) :: nSnow,nSoil,nLayers,nSubset ! number of snow and soil layers, total number of layers, and number of states in the subset - type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers + type(in_type_indexSplit),intent(in) :: in_indexSplit ! number of model layers and states in a subset + logical(lgt),intent(in) :: stateSubsetMask(:) ! logical vector (.true. if state is in the subset) + type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers ! output - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + type(out_type_indexSplit),intent(out) :: out_indexSplit ! error control ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables - integer(i4b) :: iVar ! variable index - integer(i4b) :: ixVegWat ! index of total water in the vegetation canopy - integer(i4b) :: ixVegLiq ! index of liquid water in the vegetation canopy - integer(i4b) :: ixTopWat ! index of upper-most total water state in the snow-soil subdomain - integer(i4b) :: ixTopLiq ! index of upper-most liquid water state in the snow-soil subdomain - integer(i4b) :: ixTopMat ! index of upper-most total water matric potential state in the soil subdomain - integer(i4b) :: ixTopLMP ! index of upper-most liquid water matric potential state in the soil subdomain - integer(i4b),dimension(nSubset) :: ixSequence ! sequential index in model state vector - logical(lgt),dimension(nSubset) :: stateTypeMask ! mask of state vector for specific state subsets - logical(lgt),dimension(nLayers) :: volFracWat_mask ! mask of layers within the snow+soil domain - logical(lgt),dimension(nSoil) :: matricHead_mask ! mask of layers within the soil domain - character(len=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iVar ! variable index + integer(i4b) :: ixVegWat ! index of total water in the vegetation canopy + integer(i4b) :: ixVegLiq ! index of liquid water in the vegetation canopy + integer(i4b) :: ixTopWat ! index of upper-most total water state in the snow-soil subdomain + integer(i4b) :: ixTopLiq ! index of upper-most liquid water state in the snow-soil subdomain + integer(i4b) :: ixTopMat ! index of upper-most total water matric potential state in the soil subdomain + integer(i4b) :: ixTopLMP ! index of upper-most liquid water matric potential state in the soil subdomain + integer(i4b),dimension(in_indexSplit % nSubset) :: ixSequence ! sequential index in model state vector + logical(lgt),dimension(in_indexSplit % nSubset) :: stateTypeMask ! mask of state vector for specific state subsets + logical(lgt),dimension(in_indexSplit % nLayers) :: volFracWat_mask ! mask of layers within the snow+soil domain + logical(lgt),dimension(in_indexSplit % nSoil) :: matricHead_mask ! mask of layers within the soil domain + character(len=256) :: cmessage ! error message of downwind routine ! ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ! ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ! make association to variables in the data structures fullState: associate(& - + ! number of snow and soil layers, total number of layers, and number of states in the subset + nSnow => in_indexSplit % nSnow ,& ! intent(in): [i4b] number of snow layers + nSoil => in_indexSplit % nSoil ,& ! intent(in): [i4b] number of soil layers + nLayers => in_indexSplit % nLayers ,& ! intent(in): [i4b] total number of layers + nSubset => in_indexSplit % nSubset ,& ! intent(in): [i4b] number of states in the subset ! indices of model state variables for the vegetation domain ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable @@ -357,8 +361,11 @@ subroutine indexSplit(stateSubsetMask, & ! intent(in) : logical v nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) & ! intent(in): [i4b] number of hydrology variables in the soil domain + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + ! error control + err => out_indexSplit % err ,& ! intent(out): [i4b] error code + message => out_indexSplit % cmessage & ! intent(out): [character] error message ) ! association to variables in the data structures ! ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 4c849b097..c1f17a44f 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -96,6 +96,7 @@ module opSplittin_module zLookup, & ! lookup tables model_options, & ! defines the model decisions in_type_statefilter,out_type_statefilter, & ! classes for stateFilter objects + in_type_indexSplit,out_type_indexSplit, & ! classes for indexSplit objects in_type_varSubstep,io_type_varSubstep,out_type_varSubstep ! classes for varSubstep objects ! look-up values for the numerical method @@ -293,6 +294,7 @@ subroutine opSplittin(& ! ------------------------ classes for subroutine arguments (classes defined in data_types module) ------------------------ ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** type(in_type_stateFilter) :: in_stateFilter; type(out_type_stateFilter) :: out_stateFilter; ! stateFilter arguments + type(in_type_indexSplit) :: in_indexSplit; type(out_type_indexSplit) :: out_indexSplit; ! indexSplit arguments type(in_type_varSubstep) :: in_varSubstep; type(io_type_varSubstep) :: io_varSubstep; type(out_type_varSubstep) :: out_varSubstep; ! varSubstep arguments ! --------------------------------------------------------------------------------------- @@ -496,153 +498,17 @@ subroutine opSplittin(& ! * assemble vectors for a given split... ! --------------------------------------- ! get indices for a given split - call indexSplit(stateMask, & ! intent(in) : logical vector (.true. if state is in the subset) - nSnow,nSoil,nLayers,nSubset, & ! intent(in) : number of snow and soil layers, and total number of layers - indx_data, & ! intent(inout) : index data structure - err,cmessage) ! intent(out) : error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + call initialize_indexSplit + call indexSplit(in_indexSplit,stateMask,indx_data,out_indexSplit) + call finalize_indexSplit + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! ----- ! * define the mask of the fluxes used... ! --------------------------------------- - - ! identify the type of state for the states in the subset - stateSubset: associate(ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] indices of state types - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) - ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat ,& ! intent(in): [i4b(:)] list of indices for all active layers (inactive=integerM - ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ) ! intent(in): [i4b(:)] indices defining the type of the domain (iname_veg, iname_snow, iname_soil) - - ! loop through flux variables - do iVar=1,size(flux_meta) - - ! * identify flux mask for the fully coupled solution - if(ixCoupling==fullyCoupled)then - desiredFlux = any(ixStateType_subset==flux2state_orig(iVar)%state1) .or. any(ixStateType_subset==flux2state_orig(iVar)%state2) - - ! make sure firstFluxCall fluxes are included in the mask - if (firstFluxCall .and. addFirstFlux) then - if (iVar==iLookFlux%scalarSoilResistance) desiredFlux = .true. - if (iVar==iLookFlux%scalarStomResistSunlit) desiredFlux = .true. - if (iVar==iLookFlux%scalarStomResistShaded) desiredFlux = .true. - if (iVar==iLookFlux%scalarPhotosynthesisSunlit) desiredFlux = .true. - if (iVar==iLookFlux%scalarPhotosynthesisShaded) desiredFlux = .true. - endif - - fluxMask%var(iVar)%dat = desiredFlux - - ! * identify flux mask for the split solution - else - - ! identify the flux mask for a given state split - select case(iStateTypeSplit) - case(nrgSplit); desiredFlux = any(ixStateType_subset==flux2state_orig(iVar)%state1) .or. any(ixStateType_subset==flux2state_orig(iVar)%state2) - case(massSplit); desiredFlux = any(ixStateType_subset==flux2state_liq(iVar)%state1) .or. any(ixStateType_subset==flux2state_liq(iVar)%state2) - case default; err=20; message=trim(message)//'unable to identify split based on state type'; return - end select - - ! make sure firstFluxCall fluxes are included in the mask - if (firstFluxCall .and. addFirstFlux) then - if (iVar==iLookFlux%scalarSoilResistance) desiredFlux = .true. - if (iVar==iLookFlux%scalarStomResistSunlit) desiredFlux = .true. - if (iVar==iLookFlux%scalarStomResistShaded) desiredFlux = .true. - if (iVar==iLookFlux%scalarPhotosynthesisSunlit) desiredFlux = .true. - if (iVar==iLookFlux%scalarPhotosynthesisShaded) desiredFlux = .true. - endif - - ! no domain splitting - if(nDomains==1)then - fluxMask%var(iVar)%dat = desiredFlux - - ! domain splitting - else - - ! initialize to .false. - fluxMask%var(iVar)%dat = .false. - - ! only need to proceed if the flux is desired - if(desiredFlux)then - - ! different domain splitting operations - select case(iDomainSplit) - - ! canopy fluxes -- (:1) gets the upper boundary(0) if it exists - case(vegSplit) - - ! vector solution (should only be present for energy) - if(ixSolution==vector)then - fluxMask%var(iVar)%dat(:1) = desiredFlux - if(ixStateThenDomain>1 .and. iStateTypeSplit/=nrgSplit)then - message=trim(message)//'only expect a vector solution for the vegetation domain for energy' - err=20; return - endif - - ! scalar solution - else - fluxMask%var(iVar)%dat(:1) = desiredFlux - endif - - ! fluxes through snow and soil - case(snowSplit,soilSplit) - - ! loop through layers - do iLayer=1,nLayers - if(ixlayerActive(iLayer)/=integerMissing)then - - ! get the offset (ixLayerActive=1,2,3,...nLayers, and soil vectors nSnow+1, nSnow+2, ..., nLayers) - iOffset = merge(nSnow, 0, flux_meta(iVar)%vartype==iLookVarType%midSoil .or. flux_meta(iVar)%vartype==iLookVarType%ifcSoil) - jLayer = iLayer-iOffset - - ! identify the minimum layer - select case(flux_meta(iVar)%vartype) - case(iLookVarType%ifcToto, iLookVarType%ifcSnow, iLookVarType%ifcSoil); minLayer=merge(jLayer-1, jLayer, jLayer==1) - case(iLookVarType%midToto, iLookVarType%midSnow, iLookVarType%midSoil); minLayer=jLayer - case default; minLayer=integerMissing - end select - - ! set desired layers - select case(flux_meta(iVar)%vartype) - case(iLookVarType%midToto,iLookVarType%ifcToto); fluxMask%var(iVar)%dat(minLayer:jLayer) = desiredFlux - case(iLookVarType%midSnow,iLookVarType%ifcSnow); if(iLayer<=nSnow) fluxMask%var(iVar)%dat(minLayer:jLayer) = desiredFlux - case(iLookVarType%midSoil,iLookVarType%ifcSoil); if(iLayer> nSnow) fluxMask%var(iVar)%dat(minLayer:jLayer) = desiredFlux - end select - - ! add hydrology states for scalar variables - if(iStateTypeSplit==massSplit .and. flux_meta(iVar)%vartype==iLookVarType%scalarv)then - select case(iDomainSplit) - case(snowSplit); if(iLayer==nSnow) fluxMask%var(iVar)%dat = desiredFlux - case(soilSplit); if(iLayer==nSnow+1) fluxMask%var(iVar)%dat = desiredFlux - end select - endif ! if hydrology split and scalar - - endif ! if the layer is active - end do ! looping through layers - - ! fluxes through aquifer - case(aquiferSplit) - fluxMask%var(iVar)%dat(:) = desiredFlux ! only would be firstFluxCall variables, no aquifer fluxes - - ! check - case default; err=20; message=trim(message)//'unable to identify split based on domain type'; return - end select ! domain split - - endif ! if flux is desired - - endif ! domain splitting - endif ! not fully coupled - - ! define if the flux is desired - if(desiredFlux) neededFlux(iVar)=.true. - !if(desiredFlux) print*, flux_meta(iVar)%varname, fluxMask%var(iVar)%dat - - ! * check - if( globalPrintFlag .and. count(fluxMask%var(iVar)%dat)>0 )& - print*, trim(flux_meta(iVar)%varname) - - end do ! (loop through fluxes) - - end associate stateSubset - + call update_fluxMask + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + ! ******************************************************************************************************************************* ! ***** trial with a given solution method... @@ -890,6 +756,16 @@ subroutine finalize_stateFilter call out_stateFilter % finalize(nSubset,err,cmessage) end subroutine finalize_stateFilter + ! **** indexSplit **** + subroutine initialize_indexSplit + call in_indexSplit % initialize(nSnow,nSoil,nLayers,nSubset) + end subroutine initialize_indexSplit + + subroutine finalize_indexSplit + call out_indexSplit % finalize(err,cmessage) + end subroutine finalize_indexSplit + ! **** end indexSplit **** + ! **** varSubstep **** subroutine initialize_varSubstep call in_varSubstep % initialize(dt,dtInit,dt_min,whole_step,nSubset,doAdjustTemp,firstSubStep,computeVegFlux,ixSolution,scalar,iStateSplit,fluxMask) @@ -900,6 +776,115 @@ subroutine finalize_varSubstep call io_varSubstep % finalize(firstFluxCall,fluxCount,ixSaturation) call out_varSubstep % finalize(dtMultiplier,nSubsteps,failedMinimumStep,reduceCoupledStep,tooMuchMelt,err,cmessage) end subroutine finalize_varSubstep + + subroutine update_fluxMask + ! *** update the fluxMask data structure *** + do iVar=1,size(flux_meta) ! loop through flux variables + + if (ixCoupling==fullyCoupled) then ! * identify flux mask for the fully coupled solution + associate(ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat) ! intent(in): [i4b(:)] indices of state types + desiredFlux = any(ixStateType_subset==flux2state_orig(iVar)%state1) .or. any(ixStateType_subset==flux2state_orig(iVar)%state2) + end associate + + ! make sure firstFluxCall fluxes are included in the mask + if (firstFluxCall .and. addFirstFlux) then + if (iVar==iLookFlux%scalarSoilResistance) desiredFlux = .true. + if (iVar==iLookFlux%scalarStomResistSunlit) desiredFlux = .true. + if (iVar==iLookFlux%scalarStomResistShaded) desiredFlux = .true. + if (iVar==iLookFlux%scalarPhotosynthesisSunlit) desiredFlux = .true. + if (iVar==iLookFlux%scalarPhotosynthesisShaded) desiredFlux = .true. + end if + + fluxMask%var(iVar)%dat = desiredFlux + + else ! * identify flux mask for the split solution + + associate(ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat) ! intent(in): [i4b(:)] indices of state types + select case(iStateTypeSplit) ! identify the flux mask for a given state split + case(nrgSplit); desiredFlux = any(ixStateType_subset==flux2state_orig(iVar)%state1) .or. any(ixStateType_subset==flux2state_orig(iVar)%state2) + case(massSplit); desiredFlux = any(ixStateType_subset==flux2state_liq(iVar)%state1) .or. any(ixStateType_subset==flux2state_liq(iVar)%state2) + case default; err=20; message=trim(message)//'unable to identify split based on state type'; return + end select + end associate + + ! make sure firstFluxCall fluxes are included in the mask + if (firstFluxCall .and. addFirstFlux) then + if (iVar==iLookFlux%scalarSoilResistance) desiredFlux = .true. + if (iVar==iLookFlux%scalarStomResistSunlit) desiredFlux = .true. + if (iVar==iLookFlux%scalarStomResistShaded) desiredFlux = .true. + if (iVar==iLookFlux%scalarPhotosynthesisSunlit) desiredFlux = .true. + if (iVar==iLookFlux%scalarPhotosynthesisShaded) desiredFlux = .true. + end if + + if (nDomains==1) then ! no domain splitting + fluxMask%var(iVar)%dat = desiredFlux + else ! domain splitting + fluxMask%var(iVar)%dat = .false. ! initialize to .false. + if (desiredFlux) then ! only need to proceed if the flux is desired + select case(iDomainSplit) ! different domain splitting operations + case(vegSplit) ! canopy fluxes -- (:1) gets the upper boundary(0) if it exists + if (ixSolution==vector) then ! vector solution (should only be present for energy) + fluxMask%var(iVar)%dat(:1) = desiredFlux + if (ixStateThenDomain>1 .and. iStateTypeSplit/=nrgSplit) then + message=trim(message)//'only expect a vector solution for the vegetation domain for energy' + err=20; return + end if + else ! scalar solution + fluxMask%var(iVar)%dat(:1) = desiredFlux + end if + case(snowSplit,soilSplit) ! fluxes through snow and soil + + do iLayer=1,nLayers! loop through layers + associate(ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat) ! intent(in): [i4b(:)] indices for all active layers (inactive=integerMissing) + if (ixLayerActive(iLayer)/=integerMissing) then + + ! get the offset (ixLayerActive=1,2,3,...nLayers, and soil vectors nSnow+1, nSnow+2, ..., nLayers) + iOffset = merge(nSnow, 0, flux_meta(iVar)%vartype==iLookVarType%midSoil .or. flux_meta(iVar)%vartype==iLookVarType%ifcSoil) + jLayer = iLayer-iOffset + + ! identify the minimum layer + select case(flux_meta(iVar)%vartype) + case(iLookVarType%ifcToto, iLookVarType%ifcSnow, iLookVarType%ifcSoil); minLayer=merge(jLayer-1, jLayer, jLayer==1) + case(iLookVarType%midToto, iLookVarType%midSnow, iLookVarType%midSoil); minLayer=jLayer + case default; minLayer=integerMissing + end select + + ! set desired layers + select case(flux_meta(iVar)%vartype) + case(iLookVarType%midToto,iLookVarType%ifcToto); fluxMask%var(iVar)%dat(minLayer:jLayer) = desiredFlux + case(iLookVarType%midSnow,iLookVarType%ifcSnow); if (iLayer<=nSnow) fluxMask%var(iVar)%dat(minLayer:jLayer) = desiredFlux + case(iLookVarType%midSoil,iLookVarType%ifcSoil); if (iLayer> nSnow) fluxMask%var(iVar)%dat(minLayer:jLayer) = desiredFlux + end select + + ! add hydrology states for scalar variables + if (iStateTypeSplit==massSplit .and. flux_meta(iVar)%vartype==iLookVarType%scalarv) then + select case(iDomainSplit) + case(snowSplit); if(iLayer==nSnow) fluxMask%var(iVar)%dat = desiredFlux + case(soilSplit); if(iLayer==nSnow+1) fluxMask%var(iVar)%dat = desiredFlux + end select + end if ! if hydrology split and scalar + + end if ! if the layer is active + end associate + end do ! looping through layers + + case(aquiferSplit) ! fluxes through aquifer + fluxMask%var(iVar)%dat(:) = desiredFlux ! only would be firstFluxCall variables, no aquifer fluxes + case default; err=20; message=trim(message)//'unable to identify split based on domain type'; return ! check + end select ! domain split + end if ! end if flux is desired + end if ! end if domain splitting + end if ! end if not fully coupled + + ! define if the flux is desired + if (desiredFlux) neededFlux(iVar)=.true. + !if(desiredFlux) print*, flux_meta(iVar)%varname, fluxMask%var(iVar)%dat + + if ( globalPrintFlag .and. count(fluxMask%var(iVar)%dat)>0 ) print*, trim(flux_meta(iVar)%varname) ! * check + + end do ! end looping through fluxes + + end subroutine update_fluxMask end subroutine opSplittin From 3ed3908129bf6881e59625caa32edf29f8d0008e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 2 Nov 2023 18:37:13 +0900 Subject: [PATCH 0985/1472] turn on needCm --- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/eval8summaWithPrime.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 568e51ce1..b385dd5c8 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -216,7 +216,7 @@ subroutine eval8summa(& real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step - logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m + logical(lgt),parameter :: needCm=.true. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m ! -------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 6ea676e59..4d7300891 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -237,7 +237,7 @@ subroutine eval8summaWithPrime(& real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step - logical(lgt),parameter :: needCm=.false. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m + logical(lgt),parameter :: needCm=.true. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m ! -------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures From 5a7b9721fe33955c7aa6568da56f25ac37425e09 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 2 Nov 2023 07:01:31 -0600 Subject: [PATCH 0986/1472] Added save_recover and check_failure routines to the contains block of opSplittin. --- build/source/engine/opSplittin.f90 | 127 +++++++++++++++-------------- 1 file changed, 66 insertions(+), 61 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index c1f17a44f..f1c17c418 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -450,49 +450,22 @@ subroutine opSplittin(& if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! error control ! check that state variables exist - if(nSubset==0) cycle domainSplit + if (nSubset==0) cycle domainSplit ! avoid redundant case where vector solution is of length 1 - if(ixSolution==vector .and. count(stateMask)==1) cycle solution + if (ixSolution==vector .and. count(stateMask)==1) cycle solution ! check that we do not attempt the scalar solution for the fully coupled case - if(ixCoupling==fullyCoupled .and. ixSolution==scalar)then + if (ixCoupling==fullyCoupled .and. ixSolution==scalar) then message=trim(message)//'only apply the scalar solution to the fully split coupling strategy' err=20; return - endif + end if ! reset the flag for the first flux call - if(.not.firstSuccess) firstFluxCall=.true. - - ! save/recover copies of prognostic variables - do iVar=1,size(prog_data%var) - select case(failure) - case(.false.); prog_temp%var(iVar)%dat(:) = prog_data%var(iVar)%dat(:) - case(.true.); prog_data%var(iVar)%dat(:) = prog_temp%var(iVar)%dat(:) - end select - end do ! looping through variables + if (.not.firstSuccess) firstFluxCall=.true. - ! save/recover copies of diagnostic variables - do iVar=1,size(diag_data%var) - select case(failure) - case(.false.); diag_temp%var(iVar)%dat(:) = diag_data%var(iVar)%dat(:) - case(.true.); diag_data%var(iVar)%dat(:) = diag_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - ! save/recover copies of model fluxes and mean fluxes - do iVar=1,size(flux_data%var) - select case(failure) - case(.false.) - flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) - flux_mntemp%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) - addFirstFlux = .false. - case(.true.) - flux_data%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) - flux_mean%var(iVar)%dat(:) = flux_mntemp%var(iVar)%dat(:) - if(addFirstFlux) addFirstFlux = .true. - end select - end do ! looping through variables + ! save/recover copies of variables and fluxes + call save_recover ! ----- ! * assemble vectors for a given split... @@ -518,7 +491,7 @@ subroutine opSplittin(& ! -------------------------------------------- ! keep track of the number of scalar solutions - if(ixSolution==scalar) numberScalarSolutions = numberScalarSolutions + 1 + if (ixSolution==scalar) numberScalarSolutions = numberScalarSolutions + 1 ! solve variable subset for one full time step call initialize_varSubstep @@ -530,11 +503,11 @@ subroutine opSplittin(& if (err/=0) then; message=trim(message)//trim(cmessage); if (err>0) return; end if ! error control ! reduce coupled step if failed the minimum step for the scalar solution - if(failedMinimumStep .and. ixSolution==scalar) reduceCoupledStep=.true. + if (failedMinimumStep .and. ixSolution==scalar) reduceCoupledStep=.true. ! if too much melt (or some other need to reduce the coupled step) then return ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry - if(tooMuchMelt .or. reduceCoupledStep)then + if (tooMuchMelt .or. reduceCoupledStep) then stepFailure=.true. err=0 ! recovering return @@ -542,24 +515,24 @@ subroutine opSplittin(& ! define failure failure = (failedMinimumStep .or. err<0) - if(.not.failure) firstSuccess=.true. + if (.not.failure) firstSuccess=.true. ! if failed, need to reset the flux counter - if(failure)then + if (failure) then do iVar=1,size(flux_meta) iMin=lbound(flux_data%var(iVar)%dat) iMax=ubound(flux_data%var(iVar)%dat) do iLayer=iMin(1),iMax(1) - if(fluxMask%var(iVar)%dat(iLayer)) fluxCount%var(iVar)%dat(iLayer) = fluxCount%var(iVar)%dat(iLayer) - nSubsteps + if (fluxMask%var(iVar)%dat(iLayer)) fluxCount%var(iVar)%dat(iLayer) = fluxCount%var(iVar)%dat(iLayer) - nSubsteps end do end do endif ! try the fully split solution if failed to converge with a minimum time step in the coupled solution - if(ixCoupling==fullyCoupled .and. failure) cycle coupling + if (ixCoupling==fullyCoupled .and. failure) cycle coupling ! try the scalar solution if failed to converge with a minimum time step in the split solution - if(ixCoupling/=fullyCoupled)then + if (ixCoupling/=fullyCoupled) then select case(ixStateThenDomain) case(fullDomain); if(failure) cycle stateThenDomain case(subDomain); if(failure) cycle solution @@ -569,34 +542,23 @@ subroutine opSplittin(& ! check that state variables updated where(stateMask) stateCheck = stateCheck+1 - if(any(stateCheck>1))then + if (any(stateCheck>1)) then message=trim(message)//'state variable updated more than once!' err=20; return - endif + end if ! success = exit solution - if(.not.failure)then + if (.not.failure) then ! sum the mean steps for the successful solution type mean_step_solution = mean_step_solution + (dt/nSubsteps)/nStateSplit select case(ixStateThenDomain) - case(fullDomain); if(iStateSplit==nStateSplit) exit stateThenDomain - case(subDomain); if(iStateSplit==nStateSplit) exit solution + case(fullDomain); if (iStateSplit==nStateSplit) exit stateThenDomain + case(subDomain); if (iStateSplit==nStateSplit) exit solution case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' end select - else - - ! check that we did not fail for the scalar solution (last resort) - if(ixSolution==scalar)then - message=trim(message)//'failed the minimum step for the scalar solution' - err=20; return - - ! check for an unexpected failure - else - message=trim(message)//'unexpected failure' - err=20; return - endif - - endif ! success check + else ! failure + call check_failure; return ! check reason for failure and return + end if ! success check end do stateSplit ! solution with split layers @@ -777,6 +739,49 @@ subroutine finalize_varSubstep call out_varSubstep % finalize(dtMultiplier,nSubsteps,failedMinimumStep,reduceCoupledStep,tooMuchMelt,err,cmessage) end subroutine finalize_varSubstep + subroutine save_recover + ! save/recover copies of prognostic variables + do iVar=1,size(prog_data%var) + select case(failure) + case(.false.); prog_temp%var(iVar)%dat(:) = prog_data%var(iVar)%dat(:) + case(.true.); prog_data%var(iVar)%dat(:) = prog_temp%var(iVar)%dat(:) + end select + end do + + ! save/recover copies of diagnostic variables + do iVar=1,size(diag_data%var) + select case(failure) + case(.false.); diag_temp%var(iVar)%dat(:) = diag_data%var(iVar)%dat(:) + case(.true.); diag_data%var(iVar)%dat(:) = diag_temp%var(iVar)%dat(:) + end select + end do + + ! save/recover copies of model fluxes and mean fluxes + do iVar=1,size(flux_data%var) + select case(failure) + case(.false.) + flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_mntemp%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + addFirstFlux = .false. + case(.true.) + flux_data%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) + flux_mean%var(iVar)%dat(:) = flux_mntemp%var(iVar)%dat(:) + if (addFirstFlux) addFirstFlux = .true. + end select + end do + end subroutine save_recover + + subroutine check_failure + ! *** Analyze reason for failure *** + if (ixSolution==scalar) then ! check that we did not fail for the scalar solution (last resort) + message=trim(message)//'failed the minimum step for the scalar solution' + err=20; return + else ! check for an unexpected failure + message=trim(message)//'unexpected failure' + err=20; return + end if + end subroutine check_failure + subroutine update_fluxMask ! *** update the fluxMask data structure *** do iVar=1,size(flux_meta) ! loop through flux variables From 2cea0a0cb22778410ed7b2d613cfeed72bf2b890 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 3 Nov 2023 07:30:24 +0900 Subject: [PATCH 0987/1472] divide t2enthalpy into regular and add phase --- build/source/engine/eval8summa.f90 | 7 - build/source/engine/eval8summaWithPrime.f90 | 9 +- build/source/engine/summaSolve4ida.f90 | 47 ++++ build/source/engine/systemSolv.f90 | 9 +- build/source/engine/t2enthalpy.f90 | 256 +++++++++++++++----- build/source/engine/varSubstep.f90 | 33 ++- 6 files changed, 265 insertions(+), 96 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index b385dd5c8..2d0004461 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -390,7 +390,6 @@ subroutine eval8summa(& if(ixHowHeatCap == enthalpyFD)then ! compute H_T without phase change call t2enthalpy(& - .false., & ! intent(in): ogical flag to not include phase change in enthalpy ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure @@ -400,17 +399,11 @@ subroutine eval8summa(& scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) ! input: variables for the snow-soil domain mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 4d7300891..067c662fa 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -440,7 +440,6 @@ subroutine eval8summaWithPrime(& if(ixHowHeatCap == enthalpyFD)then ! compute H_T without phase change call t2enthalpy(& - .false., & ! intent(in): logical flag to not include phase change in enthalpy ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure @@ -450,17 +449,11 @@ subroutine eval8summaWithPrime(& scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value for canopy ice content (kg m-2) - ! input: variables for the snow-soil domain + ! input: variables for the snow-soil domain mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index d2abc7cbd..dff30975e 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -151,6 +151,7 @@ subroutine summaSolve4ida( & USE computJacobWithPrime_module,only:computJacob4ida ! system Jacobian USE tol4ida_module,only:computWeight4ida ! weight required for tolerances USE var_lookup,only:maxvarDecisions ! maximum number of decisions + USE t2enthalpy_module,only:t2enthalpy_addphase ! add phase to enthalpy !======= Declarations ========= implicit none @@ -236,6 +237,7 @@ subroutine summaSolve4ida( & logical(lgt) :: use_fdJac ! flag to use finite difference Jacobian, controlled by decision fDerivMeth logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true logical(lgt),parameter :: detect_events = .true. ! flag to do event detection and restarting, default true + logical(lgt),parameter :: checkNrgBalance = .false. ! flag to check energy balance, default false ! ----------------------------------------------------------------------------------------------------- @@ -503,6 +505,51 @@ subroutine summaSolve4ida( & mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + ( eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) * eqns_data%mLayerMatricHeadPrime(:) & + dCompress_dPsiPrev(:) * mLayerMatricHeadPrimePrev(:) ) * dt_diff/2._rkind + ! ---- + ! * check energy balance, have to call again because need to include phase change + !------------------------ + if(checkNrgBalance)then + ! compute enthalpy at t_{n+1} + call t2enthalpy_addphase(& + ! input: data structures + eqns_data%diag_data, & ! intent(in): model diagnostic variables for a local HRU + eqns_data%mpar_data, & ! intent(in): parameter data structure + eqns_data%indx_data, & ! intent(in): model indices + eqns_data%lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + eqns_data%scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + eqns_data%mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + eqns_data%mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + eqns_data%mLayerVolFracIceTrial, & ! intent(in): trial vector of ice volume fraction (-) + ! input/output: enthalpy + eqns_data%scalarCanopyEnthalpyTrial, & ! intent(inout): enthalpy of the vegetation canopy (J m-3) + eqns_data%mLayerEnthalpyTrial, & ! intent(inout): enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,message) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! compute energy balance + !sum_bal = eqns_data%scalarCanopyEnthalpyTrial - eqns_data%scalarCanopyEnthalpyPrev - rVec*dt_diff + + endif + + + + + + + + + + + + + + + + ! save required quantities for next step eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 39f6a268d..2f4b0e290 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -385,9 +385,8 @@ subroutine systemSolv(& stateVecTrial = stateVecInit if(ixHowHeatCap == enthalpyFD)then - ! compute H_T at the beginning of the data step without phase change + ! compute H_T at the beginning of the data step call t2enthalpy(& - .false., & ! intent(in): logical flag to not include phase change in enthalpy ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure @@ -397,17 +396,11 @@ subroutine systemSolv(& scalarCanairTemp, & ! intent(in): value of canopy air temperature (K) scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) scalarCanopyWat, & ! intent(in): value of canopy total water (kg m-2) - scalarCanopyIce, & ! intent(in): value for canopy ice content (kg m-2) ! input: variables for the snow-soil domain mLayerTemp, & ! intent(in): vector of layer temperature (K) mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) mLayerMatricHead, & ! intent(in): vector of total water matric potential (m) - mLayerVolFracIce, & ! intent(in): vector of volumetric fraction of ice (-) ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy scalarCanairEnthalpy, & ! intent(out): temperature component of enthalpy of the canopy air space (J m-3) diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index dd1e85842..10ef07dd3 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -63,6 +63,7 @@ module t2enthalpy_module private public::T2E_lookup public::t2enthalpy +public::t2enthalpy_addphase ! define the look-up table used to compute temperature based on enthalpy contains @@ -241,7 +242,6 @@ end subroutine T2E_lookup ! public subroutine t2enthalpy: compute enthalpy from temperature and total water content ! ************************************************************************************************************************ subroutine t2enthalpy(& - doPhase, & ! intent(in): logical flag to include phase change in enthalpy or not ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure @@ -251,17 +251,11 @@ subroutine t2enthalpy(& scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) ! input: variables for the snow-soil domain mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in) ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) @@ -277,13 +271,10 @@ subroutine t2enthalpy(& ! downwind routines USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water - USE soil_utils_module,only:dTheta_dPsi ! derivative in the soil water characteristic (soil) USE spline_int_module,only:splint ! use for cubic spline interpolation implicit none ! delare dummy variables ! ------------------------------------------------------------------------------------------------------------------------- - ! input: decisions - logical(lgt),intent(in) :: doPhase ! logical flag to include phase change in enthalpy or not ! input: data structures type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(in) :: mpar_data ! model parameters @@ -293,17 +284,11 @@ subroutine t2enthalpy(& real(rkind),intent(in) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) ! input: variables for the snow-soil domain real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric fraction of Ice (-) ! input: pre-computed derivatives - real(rkind),intent(in) :: dTheta_dTkCanopy ! derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: scalarFracLiqVeg ! fraction of canopy liquid water (-) - real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative of volumetric liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy real(rkind),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) @@ -327,14 +312,10 @@ subroutine t2enthalpy(& integer(i4b) :: ixControlIndex ! index within a given model domain real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - real(rkind) :: psiLiq ! matric head of liquid water (m) real(rkind) :: volFracWat ! volumetric fraction of total water, liquid+ice (-) - real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) - real(rkind) :: volFracIce ! volumetric fraction of ice (-) real(rkind) :: diffT ! temperature difference from Tfreeze real(rkind) :: integral ! integral of snow freezing curve real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head - real(rkind) :: dVolFracLiq_dTk ! derivative of volumetric fraction of liquid water with temperature real(rkind) :: d_integral_dTk ! derivative of integral with temperature real(rkind) :: dE ! derivative of enthalpy with temperature at layer temperature real(rkind) :: dEcrit ! derivative of enthalpy with temperature at critical temperature @@ -355,14 +336,12 @@ subroutine t2enthalpy(& real(rkind) :: dEnthLiq_dTk ! derivative of enthalpy of the liquid with temperature real(rkind) :: dEnthIce_dTk ! derivative of enthalpy of the ice with temperature real(rkind) :: dEnthAir_dTk ! derivative of enthalpy of the air with temperature - real(rkind) :: dEnthPhase_dTk ! derivative of enthalpy of the phase change with temperature real(rkind) :: dEnthWater_dTk ! derivative of enthalpy of the total water with temperature real(rkind) :: dEnthVeg_dWat ! derivative of enthalpy of the vegetation with water state real(rkind) :: dEnthSoil_dWat ! derivative of enthalpy of the soil with water state real(rkind) :: dEnthLiq_dWat ! derivative of enthalpy of the liquid with water state real(rkind) :: dEnthIce_dWat ! derivative of enthalpy of the ice with water state real(rkind) :: dEnthAir_dWat ! derivative of enthalpy of the air with water state - real(rkind) :: dEnthPhase_dWat ! derivative of enthalpy of the phase change with water state real(rkind) :: dEnthWater_dWat ! derivative of enthalpy of the total water with water state ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures @@ -377,9 +356,7 @@ subroutine t2enthalpy(& ! type of domain, type of state variable, and index of control volume within domain ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat & ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) ) ! end associate statement ! -------------------------------------------------------------------------------------------------------------------------------- @@ -417,14 +394,12 @@ subroutine t2enthalpy(& dEnthLiq_dTk = 0._rkind dEnthIce_dTk = 0._rkind dEnthAir_dTk = 0._rkind - dEnthPhase_dTk = 0._rkind dEnthWater_dTk = 0._rkind dEnthVeg_dWat = 0._rkind dEnthSoil_dWat = 0._rkind dEnthLiq_dWat = 0._rkind dEnthIce_dWat = 0._rkind dEnthAir_dWat = 0._rkind - dEnthPhase_dWat = 0._rkind dEnthWater_dWat = 0._rkind ! identify domain @@ -448,7 +423,6 @@ subroutine t2enthalpy(& if(diffT>=0._rkind)then enthLiq = Cp_water * scalarCanopyWatTrial * diffT / canopyDepth enthIce = 0._rkind - enthPhase = 0._rkind ! derivatives dEnthLiq_dTk = Cp_water * scalarCanopyWatTrial / canopyDepth dEnthLiq_dWat = Cp_water * diffT / canopyDepth @@ -456,26 +430,18 @@ subroutine t2enthalpy(& integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth - enthPhase = LH_fus * scalarCanopyIceTrial / canopyDepth ! derivatives d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) ! enthalpy derivatives dEnthLiq_dTk = Cp_water * scalarCanopyWatTrial * d_integral_dTk / canopyDepth dEnthIce_dTk = Cp_ice * scalarCanopyWatTrial * ( 1._rkind - d_integral_dTk ) / canopyDepth - dEnthPhase_dTk = -LH_fus * dTheta_dTkCanopy / canopyDepth ! dCanopyIce_dTk = -dTheta_dTkCanopy dEnthLiq_dWat = Cp_water * integral / canopyDepth dEnthIce_dWat = Cp_ice * ( diffT - integral ) / canopyDepth - dEnthPhase_dWat = LH_fus * ( 1._rkind - scalarFracLiqVeg ) / canopyDepth ! dCanopyIce_dWat = ( 1._rkind - scalarFracLiqVeg ) endif scalarCanopyEnthalpy = enthVeg + enthLiq + enthIce dCanEnthalpy_dTk = dEnthVeg_dTk + dEnthLiq_dTk + dEnthIce_dTk dCanEnthalpy_dWat = dEnthVeg_dWat + dEnthLiq_dWat + dEnthIce_dWat - if (doPhase)then ! only need for calculating energy balance error so shouldn't need derivatives - scalarCanopyEnthalpy = scalarCanopyEnthalpy - enthPhase - dCanEnthalpy_dTk = dCanEnthalpy_dTk - dEnthPhase_dTk - dCanEnthalpy_dWat = dCanEnthalpy_dWat - dEnthPhase_dWat - endif end associate vegVars @@ -491,7 +457,6 @@ subroutine t2enthalpy(& enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * diffT enthIce = 0._rkind enthAir = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) ) * diffT - enthPhase = 0._rkind ! enthalpy derivatives dEnthLiq_dTk = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) dEnthAir_dTk = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) ) @@ -502,28 +467,20 @@ subroutine t2enthalpy(& enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) - enthPhase = iden_ice * LH_fus * mLayerVolFracIceTrial(iLayer) ! derivatives d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) ! enthalpy derivatives dEnthLiq_dTk = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * d_integral_dTk dEnthIce_dTk = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( 1._rkind - d_integral_dTk ) dEnthAir_dTk = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(1._rkind-d_integral_dTk) + d_integral_dTk ) ) - dEnthPhase_dTk = -iden_water * LH_fus * mLayerdTheta_dTk(iLayer) ! dVolFracIce_dTk = -mLayerdTheta_dTk(iLayer)*(iden_water/iden_ice) dEnthLiq_dWat = iden_water * Cp_water * integral dEnthIce_dWat = iden_water * Cp_ice * ( diffT - integral ) dEnthAir_dWat = -iden_air * Cp_air * ( (iden_water/iden_ice)*(diffT-integral) + integral ) - dEnthPhase_dWat = iden_water * LH_fus * ( 1._rkind - mLayerFracLiqSnow(iLayer) )! dVolFracIce_dWat = ( 1._rkind - mLayerFracLiqSnow(iLayer) )*(iden_water/iden_ice) - endif + endif mLayerEnthalpy(iLayer) = enthLiq + enthIce + enthAir dEnthalpy_dTk(iLayer) = dEnthLiq_dTk + dEnthIce_dTk + dEnthAir_dTk dEnthalpy_dWat(iLayer) = dEnthLiq_dWat + dEnthIce_dWat + dEnthAir_dWat - if (doPhase)then ! only need for calculating energy balance error so shouldn't need derivatives - mLayerEnthalpy(iLayer) = mLayerEnthalpy(iLayer) - enthPhase - dEnthalpy_dTk(iLayer) = dEnthalpy_dTk(iLayer) - dEnthPhase_dTk - dEnthalpy_dWat(iLayer) = dEnthalpy_dWat(iLayer) - dEnthPhase_dWat - endif end associate snowVars @@ -555,7 +512,6 @@ subroutine t2enthalpy(& ! *** compute enthalpy of water for unfrozen conditions if(mlayerTempTrial(iLayer)>=Tcrit)then enthWater = iden_water*Cp_water*volFracWat*diffT ! valid for temperatures below freezing also - enthPhase = 0._rkind ! enthalpy derivatives dEnthWater_dTk = iden_water*Cp_water*volFracWat dEnthWater_dWat = iden_water*Cp_water*dVolTot_dPsi0(ixControlIndex) !dVolFracWat_dWat = dVolTot_dPsi0(ixControlIndex) @@ -575,20 +531,12 @@ subroutine t2enthalpy(& enthLiq = iden_water*Cp_water*volFracWat*(Tcrit - Tfreeze) enthWater = enthMix + enthLiq - ! *** compute the enthalpy associated with phase change - psiLiq = diffT*LH_fus/(gravity*Tfreeze) - vFracLiq = volFracLiq(psiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - volFracIce = volFracWat - vFracLiq - enthPhase = iden_water*LH_fus*volFracIce ! derivatives - dVolFracLiq_dTk = dTheta_dPsi(psiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m)*LH_fus/(gravity*Tfreeze) dTcrit_dPsi0 = 0._rkind if (mLayerMatricHeadTrial(ixControlIndex)<0._rkind) dTcrit_dPsi0 = gravity*Tfreeze/LH_fus ! enthalpy derivatives dEnthWater_dTk = dE - dEnthPhase_dTk = -iden_water*LH_fus*dVolFracLiq_dTk dEnthWater_dWat = -dEcrit*dTcrit_dPsi0 + iden_water*Cp_water*dVolTot_dPsi0(ixControlIndex)*(Tcrit - Tfreeze) + iden_water*Cp_water*volFracWat*dTcrit_dPsi0 - dEnthPhase_dWat = iden_water*LH_fus*dVolTot_dPsi0(ixControlIndex) endif ! (if frozen conditions) ! *** compute the enthalpy of soil @@ -606,11 +554,6 @@ subroutine t2enthalpy(& mLayerEnthalpy(iLayer) = enthSoil + enthWater + enthAir dEnthalpy_dTk(iLayer) = dEnthSoil_dTk + dEnthWater_dTk + dEnthAir_dTk dEnthalpy_dWat(iLayer) = dEnthSoil_dWat + dEnthWater_dWat + dEnthAir_dWat - if (doPhase)then ! only need for calculating energy balance error so shouldn't need derivatives - mLayerEnthalpy(iLayer) = mLayerEnthalpy(iLayer) - enthPhase - dEnthalpy_dTk(iLayer) = dEnthalpy_dTk(iLayer) - dEnthPhase_dTk - dEnthalpy_dWat(iLayer) = dEnthalpy_dWat(iLayer) - dEnthPhase_dWat - endif end associate soilVars @@ -628,4 +571,197 @@ subroutine t2enthalpy(& end subroutine t2enthalpy + +! ************************************************************************************************************************ +! public subroutine t2enthalpy: compute enthalpy with phase change from temperature and total water content +! ************************************************************************************************************************ +subroutine t2enthalpy_addphase(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in) + ! input/output: enthalpy + scalarCanopyEnthalpy, & ! intent(inout): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy, & ! intent(inout): enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,message) ! intent(out): error control + ! ------------------------------------------------------------------------------------------------------------------------- + ! downwind routines + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water + implicit none + ! delare dummy variables + ! ------------------------------------------------------------------------------------------------------------------------- + ! input: data structures + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model indices + type(zLookup),intent(in) :: lookup_data ! lookup tables + ! input: state variables for the vegetation canopy + real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric fraction of Ice (-) + ! input output: enthalpy + real(rkind),intent(inout) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(inout) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------------------------------- + ! declare local variables + character(len=128) :: cmessage ! error message in downwind routine + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixDomainType ! name of a given model domain + integer(i4b) :: ixControlIndex ! index within a given model domain + real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind) :: psiLiq ! matric head of liquid water (m) + real(rkind) :: volFracWat ! volumetric fraction of total water, liquid+ice (-) + real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) + real(rkind) :: volFracIce ! volumetric fraction of ice (-) + real(rkind) :: diffT ! temperature difference from Tfreeze + real(rkind) :: integral ! integral of snow freezing curve + real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head + ! enthalpy + real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + generalVars: associate(& + ! number of model layers, and layer type + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + ! mapping between the full state vector and the state subset + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ! type of domain, type of state variable, and index of control volume within domain + ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat & ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ) ! end associate statement + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message="t2enthalpy_addphase/" + + ! loop through model state variables + do iState=1,size(ixMapSubset2Full) + + ! ----- + ! - compute indices... + ! -------------------- + + ! get domain type, and index of the control volume within the domain + ixFullVector = ixMapSubset2Full(iState) ! index within full state vector + ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) + ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain + + ! check an energy state + if(ixStateType(ixFullVector)==iname_nrgCanair .or. ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer)then + + ! get the layer index + select case(ixDomainType) + case(iname_cas); iLayer = integerMissing + case(iname_veg); iLayer = integerMissing + case(iname_snow); iLayer = ixControlIndex + case(iname_soil); iLayer = ixControlIndex + nSnow + case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + + ! identify domain + select case(ixDomainType) + case(iname_cas); cycle ! aquifer: do nothing + + case(iname_veg) + ! association to necessary variables for vegetation + vegVars: associate(& + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) & ! intent(in): [dp] canopy depth (m) + ) + + if(diffT>=0._rkind)then + enthPhase = 0._rkind + else + enthPhase = LH_fus * scalarCanopyIceTrial / canopyDepth + endif + scalarCanopyEnthalpy = scalarCanopyEnthalpy - enthPhase + + end associate vegVars + + case(iname_snow) + diffT = mLayerTempTrial(iLayer) - Tfreeze + if(diffT>=0._rkind)then + enthPhase = 0._rkind + else + enthPhase = iden_ice * LH_fus * mLayerVolFracIceTrial(iLayer) + endif + mLayerEnthalpy(iLayer) = mLayerEnthalpy(iLayer) - enthPhase + + case(iname_soil) + ! make association to variables in the data structures... + soilVars: associate(& + + ! associate model parameters + soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex) , & ! intrinsic soil density (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex) , & ! soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex) , & ! volumetric residual water content (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) + + ! associate values in the lookup table + Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) + Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) + E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + + ) ! end associate statement + + ! diagnostic variables + vGn_m = 1._rkind - 1._rkind/vGn_n + Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) + volFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + diffT = mLayerTempTrial(iLayer) - Tfreeze + + ! *** compute enthalpy of water for unfrozen conditions + if(mlayerTempTrial(iLayer)>=Tcrit)then + enthPhase = 0._rkind + + ! *** compute enthalpy of water for frozen conditions + else + ! *** compute the enthalpy associated with phase change + psiLiq = diffT*LH_fus/(gravity*Tfreeze) + vFracLiq = volFracLiq(psiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + volFracIce = volFracWat - vFracLiq + enthPhase = iden_water*LH_fus*volFracIce + endif ! (if frozen conditions) + mLayerEnthalpy(iLayer) = mLayerEnthalpy(iLayer) - enthPhase + + end associate soilVars + + ! ----- + ! - checks... + ! ----------- + case(iname_aquifer); cycle ! aquifer: do nothing + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + + end if ! if an energy layer + end do ! looping through state variables + + end associate generalVars + +end subroutine t2enthalpy_addphase + end module t2enthalpy_module diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 80be14455..064b701d9 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -698,11 +698,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-3) mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(inout): [dp(:)] enthalpy of the snow+soil layers (J m-3) ! derivatives, diagnositic for enthalpy - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) ! model state variables (aquifer) scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(inout): [dp(:)] storage of water in the aquifer (m) ! error tolerance @@ -882,7 +878,6 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe if(checkNrgBalance)then ! compute enthalpy at t_{n+1} call t2enthalpy(& - .true., & ! intent(in): logical flag to include phase change in enthalpy ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure @@ -892,17 +887,11 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) - ! input: variables for the snow-soil domain + ! input: variables for the snow-soil domain mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice (-) ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) @@ -915,7 +904,25 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - + call t2enthalpy_addphase(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric ice water content (-) + ! input/output: enthalpy + scalarCanopyEnthalpyTrial, & ! intent(inout): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(inout): enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,message) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif endif ! ----- From 003041c977fe6bdc9c0a16d448bd886587f1cc1f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 3 Nov 2023 07:39:59 +0900 Subject: [PATCH 0988/1472] diffT missed --- build/source/engine/computHeatCap.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index d16bae97f..6e157a9af 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -609,6 +609,7 @@ subroutine computCm(& ! compute Cm of vegetation ! Note that scalarCanopyCm/iden_water is computed if(computeVegFlux)then + diffT = scalarCanopyTemp - Tfreeze if(diffT>=0._rkind)then scalarCanopyCm = Cp_water * diffT ! derivatives From 603c006ee070b2d38202096cd33ba68e918eefb8 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 3 Nov 2023 07:39:59 +0900 Subject: [PATCH 0989/1472] diffT missed --- build/source/engine/computHeatCap.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index d16bae97f..6e157a9af 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -609,6 +609,7 @@ subroutine computCm(& ! compute Cm of vegetation ! Note that scalarCanopyCm/iden_water is computed if(computeVegFlux)then + diffT = scalarCanopyTemp - Tfreeze if(diffT>=0._rkind)then scalarCanopyCm = Cp_water * diffT ! derivatives From f3dbc2d113ba0d708c6b356a01ead84a1c197db1 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 3 Nov 2023 12:32:09 +0900 Subject: [PATCH 0990/1472] variable was misnamed in eval8summa --- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/eval8summaWithPrime.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 46987a4f5..bb24dfc32 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -685,7 +685,7 @@ subroutine eval8summa(& mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) mLayerVolFracWatTrial, & ! intent(in): trial value for the volumetric water in each snow and soil layer (-) mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liq in each snow and soil layer (-) - scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) + canopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 57263af37..f8f486061 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -733,7 +733,7 @@ subroutine eval8summaWithPrime(& mLayerVolFracIcePrime, & ! intent(in): Prime value for the volumetric ice in each snow and soil layer (s-1) mLayerVolFracWatPrime, & ! intent(in): Prime value for the volumetric water in each snow and soil layer (s-1) mLayerVolFracLiqPrime, & ! intent(in): Prime value for the volumetric liq in each snow and soil layer (s-1) - scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) + canopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU From 9041731173684f4fd7632796717054b94ecbdf47 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 3 Nov 2023 12:32:09 +0900 Subject: [PATCH 0991/1472] variable was misnamed in eval8summa --- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/eval8summaWithPrime.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 2d0004461..c74ce8bce 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -678,7 +678,7 @@ subroutine eval8summa(& mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) mLayerVolFracWatTrial, & ! intent(in): trial value for the volumetric water in each snow and soil layer (-) mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liq in each snow and soil layer (-) - scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) + canopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 067c662fa..5f1e4935f 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -726,7 +726,7 @@ subroutine eval8summaWithPrime(& mLayerVolFracIcePrime, & ! intent(in): Prime value for the volumetric ice in each snow and soil layer (s-1) mLayerVolFracWatPrime, & ! intent(in): Prime value for the volumetric water in each snow and soil layer (s-1) mLayerVolFracLiqPrime, & ! intent(in): Prime value for the volumetric liq in each snow and soil layer (s-1) - scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) + canopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU From 1ee1975687edba00b8b85f8e9fec30a2cf10e446 Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 3 Nov 2023 06:16:56 -0600 Subject: [PATCH 0992/1472] Added initialize_stateSplit_loop and judge_solution subroutines to the contains block of opSplittin. --- build/source/engine/opSplittin.f90 | 116 +++++++++++++++++------------ 1 file changed, 68 insertions(+), 48 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index f1c17c418..2215082cd 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -239,6 +239,7 @@ subroutine opSplittin(& logical(lgt) :: firstSuccess ! flag to define the first success logical(lgt) :: firstFluxCall ! flag to define the first flux call logical(lgt) :: reduceCoupledStep ! flag to define the need to reduce the length of the coupled step + logical(lgt) :: return_flag ! flag to indicate the execution of a return statement type(var_dlength) :: prog_temp ! temporary model prognostic variables type(var_dlength) :: diag_temp ! temporary model diagnostic variables type(var_dlength) :: flux_temp ! temporary model fluxes @@ -414,27 +415,9 @@ subroutine opSplittin(& ! trial with the vector then scalar solution solution: do ixSolution=1,nSolutions - mean_step_solution = 0._rkind ! initialize mean step for a solution - ! initialize error control - err=0; message="opSplittin/" - - ! refine the time step - if(ixSolution==scalar)then - dtInit = min(dtmin_split, dt) ! initial time step - dt_min = min(dtmin_scalar, dt) ! minimum time step - endif - - ! initialize the first flux call - firstFluxCall=.true. - if (.not.firstInnerStep) firstFluxCall=.false. - - ! get the number of split layers - select case(ixSolution) - case(vector); nStateSplit=1 - case(scalar); nStateSplit=count(stateMask) - case default; err=20; message=trim(message)//'unknown solution method'; return - end select + call initialize_stateSplit_loop + if (err/=0) then; return; end if ! error control ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) stateSplit: do iStateSplit=1,nStateSplit @@ -502,31 +485,9 @@ subroutine opSplittin(& call finalize_varSubstep if (err/=0) then; message=trim(message)//trim(cmessage); if (err>0) return; end if ! error control - ! reduce coupled step if failed the minimum step for the scalar solution - if (failedMinimumStep .and. ixSolution==scalar) reduceCoupledStep=.true. - - ! if too much melt (or some other need to reduce the coupled step) then return - ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry - if (tooMuchMelt .or. reduceCoupledStep) then - stepFailure=.true. - err=0 ! recovering - return - endif - - ! define failure - failure = (failedMinimumStep .or. err<0) - if (.not.failure) firstSuccess=.true. - - ! if failed, need to reset the flux counter - if (failure) then - do iVar=1,size(flux_meta) - iMin=lbound(flux_data%var(iVar)%dat) - iMax=ubound(flux_data%var(iVar)%dat) - do iLayer=iMin(1),iMax(1) - if (fluxMask%var(iVar)%dat(iLayer)) fluxCount%var(iVar)%dat(iLayer) = fluxCount%var(iVar)%dat(iLayer) - nSubsteps - end do - end do - endif + ! determine whether solution is a success or a failure + call judge_solution + if (return_flag.eqv..true.) return ! return for a recovering solution ! try the fully split solution if failed to converge with a minimum time step in the coupled solution if (ixCoupling==fullyCoupled .and. failure) cycle coupling @@ -534,11 +495,11 @@ subroutine opSplittin(& ! try the scalar solution if failed to converge with a minimum time step in the split solution if (ixCoupling/=fullyCoupled) then select case(ixStateThenDomain) - case(fullDomain); if(failure) cycle stateThenDomain - case(subDomain); if(failure) cycle solution + case(fullDomain); if (failure) cycle stateThenDomain + case(subDomain); if (failure) cycle solution case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' end select - endif + end if ! check that state variables updated where(stateMask) stateCheck = stateCheck+1 @@ -709,6 +670,32 @@ subroutine finalize_opSplittin if (ixCoupling/=fullyCoupled .or. nSubsteps>1) dtMultiplier=0.5_rkind end subroutine finalize_opSplittin + + subroutine initialize_stateSplit_loop + ! *** initial operations to set up stateSplit loop *** + mean_step_solution = 0._rkind ! initialize mean step for a solution + + ! initialize error control + err=0; message="opSplittin/" + + ! refine the time step + if (ixSolution==scalar) then + dtInit = min(dtmin_split, dt) ! initial time step + dt_min = min(dtmin_scalar, dt) ! minimum time step + end if + + ! initialize the first flux call + firstFluxCall=.true. + if (.not.firstInnerStep) firstFluxCall=.false. + + ! get the number of split layers + select case(ixSolution) + case(vector); nStateSplit=1 + case(scalar); nStateSplit=count(stateMask) + case default; err=20; message=trim(message)//'unknown solution method'; return + end select + end subroutine initialize_stateSplit_loop + ! **** stateFilter **** subroutine initialize_stateFilter call in_stateFilter % initialize(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) @@ -739,6 +726,39 @@ subroutine finalize_varSubstep call out_varSubstep % finalize(dtMultiplier,nSubsteps,failedMinimumStep,reduceCoupledStep,tooMuchMelt,err,cmessage) end subroutine finalize_varSubstep + + subroutine judge_solution + ! *** determine whether solution is a success or a failure *** + return_flag=.false. ! initialize flag + + ! reduce coupled step if failed the minimum step for the scalar solution + if (failedMinimumStep .and. ixSolution==scalar) reduceCoupledStep=.true. + + ! if too much melt (or some other need to reduce the coupled step) then return + ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry + if (tooMuchMelt .or. reduceCoupledStep) then + stepFailure=.true. + err=0 ! recovering + return_flag=.true. ! return statement required in opSplittin + return + end if + + ! define failure + failure = (failedMinimumStep .or. err<0) + if (.not.failure) firstSuccess=.true. + + ! if failed, need to reset the flux counter + if (failure) then + do iVar=1,size(flux_meta) + iMin=lbound(flux_data%var(iVar)%dat) + iMax=ubound(flux_data%var(iVar)%dat) + do iLayer=iMin(1),iMax(1) + if (fluxMask%var(iVar)%dat(iLayer)) fluxCount%var(iVar)%dat(iLayer) = fluxCount%var(iVar)%dat(iLayer) - nSubsteps + end do + end do + end if + end subroutine judge_solution + subroutine save_recover ! save/recover copies of prognostic variables do iVar=1,size(prog_data%var) From 02dd56ba7e8e3e83b2be2cbaf3799d702020d521 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 4 Nov 2023 06:59:14 -0600 Subject: [PATCH 0993/1472] Added initialize_domainSplit_loop subroutine in contains block of opSplittin. --- build/source/engine/opSplittin.f90 | 57 ++++++++++++++++++------------ 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 2215082cd..aeb81b771 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -323,8 +323,6 @@ subroutine opSplittin(& ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) ! numerix tracking numberStateSplit => indx_data%var(iLookINDEX%numberStateSplit )%dat(1) ,& ! intent(inout): [i4b] number of state splitting solutions (-) - numberDomainSplitNrg => indx_data%var(iLookINDEX%numberDomainSplitNrg )%dat(1) ,& ! intent(inout): [i4b] number of domain splitting solutions for energy (-) - numberDomainSplitMass => indx_data%var(iLookINDEX%numberDomainSplitMass)%dat(1) ,& ! intent(inout): [i4b] number of domain splitting solutions for mass (-) numberScalarSolutions => indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1) & ! intent(inout): [i4b] number of scalar solutions (-) ) ! --------------------------------------------------------------------------------------- @@ -391,24 +389,8 @@ subroutine opSplittin(& ! first try the state type split, then try the domain split within a given state type stateThenDomain: do ixStateThenDomain=1,1+tryDomainSplit ! 1=state type split; 2=domain split within a given state type - ! keep track of the number of domain splits - if(iStateTypeSplit==nrgSplit .and. ixStateThenDomain==subDomain) numberDomainSplitNrg = numberDomainSplitNrg + 1 - if(iStateTypeSplit==massSplit .and. ixStateThenDomain==subDomain) numberDomainSplitMass = numberDomainSplitMass + 1 - - ! define the number of domain splits for the state type - select case(ixStateThenDomain) - case(fullDomain); nDomainSplit=1 - case(subDomain); nDomainSplit=nDomains - case default; err=20; message=trim(message)//'coupling case not found'; return - end select - - ! check that we haven't split the domain when we are fully coupled - if(ixCoupling==fullyCoupled .and. nDomainSplit==nDomains)then - message=trim(message)//'cannot split domains when fully coupled' - err=20; return - endif - - mean_step_state = 0._rkind ! initialize mean step for state + call initialize_domainSplit_loop + if (return_flag.eqv..true.) return ! return if error occurs during initialization ! domain splitting loop domainSplit: do iDomainSplit=1,nDomainSplit @@ -417,7 +399,7 @@ subroutine opSplittin(& solution: do ixSolution=1,nSolutions call initialize_stateSplit_loop - if (err/=0) then; return; end if ! error control + if (return_flag.eqv..true.) return ! return if error occurs during initialization ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) stateSplit: do iStateSplit=1,nStateSplit @@ -670,9 +652,38 @@ subroutine finalize_opSplittin if (ixCoupling/=fullyCoupled .or. nSubsteps>1) dtMultiplier=0.5_rkind end subroutine finalize_opSplittin + subroutine initialize_domainSplit_loop + ! *** initial operations to set up domainSplit loop *** + return_flag=.false. ! initialize flag + associate(numberDomainSplitNrg => indx_data%var(iLookINDEX%numberDomainSplitNrg )%dat(1),& ! intent(inout): [i4b] number of domain splitting solutions for energy (-) + numberDomainSplitMass => indx_data%var(iLookINDEX%numberDomainSplitMass)%dat(1) )! intent(inout): [i4b] number of domain splitting solutions for mass (-) + ! keep track of the number of domain splits + if (iStateTypeSplit==nrgSplit .and. ixStateThenDomain==subDomain) numberDomainSplitNrg = numberDomainSplitNrg + 1 + if (iStateTypeSplit==massSplit .and. ixStateThenDomain==subDomain) numberDomainSplitMass = numberDomainSplitMass + 1 + end associate + + ! define the number of domain splits for the state type + select case(ixStateThenDomain) + case(fullDomain); nDomainSplit=1 + case(subDomain); nDomainSplit=nDomains + case default; err=20; message=trim(message)//'coupling case not found'; + return_flag=.true. ! return statement required in opSplittin + return + end select + + ! check that we haven't split the domain when we are fully coupled + if (ixCoupling==fullyCoupled .and. nDomainSplit==nDomains) then + message=trim(message)//'cannot split domains when fully coupled' + return_flag=.true. ! return statement required in opSplittin + err=20; return + end if + + mean_step_state = 0._rkind ! initialize mean step for state + end subroutine initialize_domainSplit_loop subroutine initialize_stateSplit_loop ! *** initial operations to set up stateSplit loop *** + return_flag=.false. ! initialize flag mean_step_solution = 0._rkind ! initialize mean step for a solution ! initialize error control @@ -692,7 +703,9 @@ subroutine initialize_stateSplit_loop select case(ixSolution) case(vector); nStateSplit=1 case(scalar); nStateSplit=count(stateMask) - case default; err=20; message=trim(message)//'unknown solution method'; return + case default; err=20; message=trim(message)//'unknown solution method'; + return_flag=.true. ! return statement required in opSplittin + return end select end subroutine initialize_stateSplit_loop From cecb5d25eef4a674d945aeae8e70e6871de3cbdd Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 15 Nov 2023 14:37:27 +0900 Subject: [PATCH 0994/1472] add in balance output, make compute regardless of numerical solution method of heat capacity choice. compiles, but the solutions don't look correct yet. --- build/cmake/CMakeLists.txt | 1 - build/source/driver/summa_setup.f90 | 6 +- build/source/dshare/get_ixname.f90 | 9 + build/source/dshare/popMetadat.f90 | 9 + build/source/dshare/type4ida.f90 | 6 +- build/source/dshare/type4kinsol.f90 | 1 + build/source/dshare/var_lookup.f90 | 11 +- build/source/engine/computEnthalpy.f90 | 153 ---- build/source/engine/computResidWithPrime.f90 | 2 +- build/source/engine/coupled_em.f90 | 24 + build/source/engine/eval8summa.f90 | 5 +- build/source/engine/eval8summaWithPrime.f90 | 29 +- build/source/engine/opSplittin.f90 | 7 +- build/source/engine/summaSolve4ida.f90 | 892 +++++++++++-------- build/source/engine/summaSolve4kinsol.f90 | 15 +- build/source/engine/systemSolv.f90 | 85 +- build/source/engine/t2enthalpy.f90 | 2 +- build/source/engine/varSubstep.f90 | 222 +++-- 18 files changed, 802 insertions(+), 677 deletions(-) delete mode 100644 build/source/engine/computEnthalpy.f90 diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 04f2fbb38..8518978ef 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -358,7 +358,6 @@ set(MODRUN_SUNDIALS # Solver main modules set(SOLVER ${ENGINE_DIR}/bigAquifer.f90 - ${ENGINE_DIR}/computEnthalpy.f90 ${ENGINE_DIR}/computFlux.f90 ${ENGINE_DIR}/computHeatCap.f90 ${ENGINE_DIR}/computJacob.f90 diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index 65e56a7d6..940f91b2f 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -306,14 +306,14 @@ subroutine summa_paramSetup(summa1_struc, err, message) call E2T_lookup(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! calculate a lookup table to compute enthalpy from temperature, only for enthalpyFD - if(model_decisions(iLookDECISIONS%howHeatCap)%iDecision == enthalpyFD)then + ! calculate a lookup table to compute enthalpy from temperature, need for enthalpyFD or if checkNrgBalance is true + !if(model_decisions(iLookDECISIONS%howHeatCap)%iDecision == enthalpyFD)then call T2E_lookup(gru_struc(iGRU)%hruInfo(iHRU)%nSoil, & ! intent(in): number of soil layers mparStruct%gru(iGRU)%hru(iHRU), & ! intent(in): parameter data structure lookupStruct%gru(iGRU)%hru(iHRU), & ! intent(inout): lookup table data structure err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - endif + !endif ! overwrite the vegetation height HVT(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex)) = mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%heightCanopyTop)%dat(1) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index dde76f749..e2fa24370 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -586,6 +586,15 @@ function get_ixdiag(varName) case('numFluxCalls' ); get_ixdiag = iLookDIAG%numFluxCalls ! number of flux calls (-) case('wallClockTime' ); get_ixdiag = iLookDIAG%wallClockTime ! wall clock time (s) case('meanStepSize' ); get_ixdiag = iLookDIAG%meanStepSize ! mean time step size (s) over data window + ! balances + case('balanceCasNrg' ); get_ixdiag = iLookDIAG%balanceCasNrg ! balance of energy in the canopy + case('balanceVegNrg' ); get_ixdiag = iLookDIAG%balanceVegNrg ! balance of energy in the vegetation + case('balanceSnowNrg' ); get_ixdiag = iLookDIAG%balanceSnowNrg ! balance of energy in the snow + case('balanceSoilNrg' ); get_ixdiag = iLookDIAG%balanceSoilNrg ! balance of energy in the soil + case('balanceVegMass' ); get_ixdiag = iLookDIAG%balanceVegMass ! balance of water in the vegetation + case('balanceSnowMass' ); get_ixdiag = iLookDIAG%balanceSnowMass ! balance of water in the snow + case('balanceSoilMass' ); get_ixdiag = iLookDIAG%balanceSoilMass ! balance of water in the soil + case('balanceAqMass' ); get_ixdiag = iLookDIAG%balanceAqMass ! balance of water in the aquifer ! get to here if cannot find the variable case default get_ixdiag = integerMissing diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index e527cc7b4..e28bcedd2 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -441,6 +441,15 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%numFluxCalls) = var_info('numFluxCalls' , 'number of flux calls' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%wallClockTime) = var_info('wallClockTime' , 'wall clock time' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%meanStepSize) = var_info('meanStepSize' , 'mean time step size over data window' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! balances + diag_meta(iLookDIAG%balanceCasNrg) = var_info('balanceCasNrg' , 'balance of energy in the canopy' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceVegNrg) = var_info('balanceVegNrg' , 'balance of energy in the vegetation' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceSnowNrg) = var_info('balanceSnowNrg' , 'balance of energy in the snow' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceSoilNrg) = var_info('balanceSoilNrg' , 'balance of energy in the soil' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceVegMass) = var_info('balanceVegMass' , 'balance of water in the vegetation' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceSnowMass) = var_info('balanceSnowMass' , 'balance of water in the snow' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceSoilMass) = var_info('balanceSoilMass' , 'balance of water in the soil' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceAqMass) = var_info('balanceAqMass' , 'balance of water in the aquifer' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! ----- ! * local model fluxes... ! ----------------------- diff --git a/build/source/dshare/type4ida.f90 b/build/source/dshare/type4ida.f90 index 9f7f8e403..68d55aca6 100644 --- a/build/source/dshare/type4ida.f90 +++ b/build/source/dshare/type4ida.f90 @@ -40,7 +40,10 @@ module type4ida type(var_dlength) :: diag_data ! diagnostic variables for a local HRU type(var_dlength) :: flux_data ! model fluxes for a local HRU type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(rkind) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) + real(rkind) :: scalarCanairEnthalpyTrial ! trial enthalpy of the canopy air space (J m-3) + real(rkind) :: scalarCanairEnthalpyPrev ! previous enthalpy of the canopy air space (J m-3) + real(rkind) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) real(rkind) :: scalarCanopyTempPrev ! previous value of canopy temperature (K) real(rkind) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) real(rkind) :: scalarCanopyIcePrev ! value of canopy ice content (kg m-2) at previous step @@ -51,6 +54,7 @@ module type4ida real(qp), allocatable :: sMul(:) ! state vector multiplier (used in the residual calculations) real(rkind), allocatable :: dMat(:) ! diagonal of the Jacobian matrix real(rkind), allocatable :: fluxVec(:) ! flux vector + real(qp), allocatable :: resVec(:) ! residual vector real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation real(rkind), allocatable :: atol(:) ! vector of absolute tolerances real(rkind), allocatable :: rtol(:) ! vector of relative tolerances diff --git a/build/source/dshare/type4kinsol.f90 b/build/source/dshare/type4kinsol.f90 index 1b8614817..d7df38ecd 100644 --- a/build/source/dshare/type4kinsol.f90 +++ b/build/source/dshare/type4kinsol.f90 @@ -42,6 +42,7 @@ module type4kinsol real(qp), allocatable :: sMul(:) ! state vector multiplier (used in the residual calculations) real(rkind), allocatable :: dMat(:) ! diagonal of the Jacobian matrix real(rkind), allocatable :: fluxVec(:) ! flux vector + real(qp), allocatable :: resVec(:) ! residual vector real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation real(rkind),allocatable :: fScale(:) ! characteristic scale of the function evaluations real(rkind),allocatable :: xScale(:) ! characteristic scale of the state vector diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index ac8b6ffd9..f21134bff 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -466,6 +466,15 @@ MODULE var_lookup integer(i4b) :: numFluxCalls = integerMissing ! number of flux calls (-) integer(i4b) :: wallClockTime = integerMissing ! wall clock time (s) integer(i4b) :: meanStepSize = integerMissing ! mean time step size over data window (s) + ! balances + integer(i4b) :: balanceCasNrg = integerMissing ! balance of energy in the canopy air space (W m-2) + integer(i4b) :: balanceVegNrg = integerMissing ! balance of energy in the vegetation (W m-2) + integer(i4b) :: balanceSnowNrg = integerMissing ! balance of energy in the snow (W m-2) + integer(i4b) :: balanceSoilNrg = integerMissing ! balance of energy in the soil (W m-2) + integer(i4b) :: balanceVegMass = integerMissing ! balance of water in the vegetation (kg m-2) + integer(i4b) :: balanceSnowMass = integerMissing ! balance of water in the snow (kg m-2) + integer(i4b) :: balanceSoilMass = integerMissing ! balance of water in the soil (kg m-2) + integer(i4b) :: balanceAqMass = integerMissing ! balance of water in the aquifer (kg m-2) endtype iLook_diag ! *********************************************************************************************************** @@ -894,7 +903,7 @@ MODULE var_lookup 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,& 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,& 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,& - 91, 92) + 91, 92, 93, 94, 95, 96, 97, 98, 99,100) ! named variables: model fluxes type(iLook_flux), public,parameter :: iLookFLUX =iLook_flux ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& diff --git a/build/source/engine/computEnthalpy.f90 b/build/source/engine/computEnthalpy.f90 deleted file mode 100644 index 7b649d538..000000000 --- a/build/source/engine/computEnthalpy.f90 +++ /dev/null @@ -1,153 +0,0 @@ -module computEnthalpy_module - -! data types -USE nrtype - -! derived types to define the data structures -USE data_types,only:& - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (rkind) - -! named variables -USE var_lookup,only:iLookPROG ! named variables for structure elements -USE var_lookup,only:iLookDIAG ! named variables for structure elements -USE var_lookup,only:iLookFLUX ! named variables for structure elements -USE var_lookup,only:iLookINDEX ! named variables for structure elements -USE var_lookup,only:iLookPARAM ! named variables for structure elements - -! access the global print flag -USE globalData,only:globalPrintFlag - -! access missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing real number -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil - -! constants -USE multiconst,only:& - Tfreeze, & ! freezing temperature (K) - LH_fus, & ! latent heat of fusion (J kg-1) - LH_vap, & ! latent heat of vaporization (J kg-1) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water, & ! intrinsic density of liquid water (kg m-3) - iden_air, & - ! specific heat - Cp_air, & ! specific heat of air (J kg-1 K-1) - Cp_ice, & ! specific heat of ice (J kg-1 K-1) - Cp_soil, & ! specific heat of soil (J kg-1 K-1) - Cp_water ! specific heat of liquid water (J kg-1 K-1) -! privacy -implicit none -private -public::computEnthalpy -public::computEnthalpyPrime -contains - -! ********************************************************************************************************** -! public subroutine computEnthalpy -! ********************************************************************************************************** -subroutine computEnthalpy(& - ! input - indx_data, & ! intent(in): indices defining model states and layers - nLayers, & ! intent(in): number of snow layers - mLayerTemp, & ! intent(in): temperature of each snow/soil layer (K) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice (-) - mLayerHeatCap, & ! intent(in): heat capacity of each snow/soil layer (J m-3 K-1) - ! output - mLayerEnthalpy & ! intent(out): enthalpy of each snow/soil layer (J m-3) - ) - ! -------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input: model control - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - integer(i4b),intent(in) :: nLayers ! number of snow layers - real(rkind),intent(in) :: mLayerTemp(:) ! temperature of each snow/soil layer (K) - real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice (-) - real(rkind),intent(in) :: mLayerHeatCap(:) ! heat capacity of each snow/soil layer (J m-3 K-1) - real(rkind),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow/soil layer (J m-3) - ! local variables - integer(i4b) :: iLayer ! loop index - - ! -------------------------------------------------------------------------------------------------------------------------------- - - associate(& - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of layer - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat & ! intent(in): [i4b(:)] indices for energy states - ) - ! (loop through non-missing energy state variables in the snow+soil domain) - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) - select case( layerType(iLayer) ) - case(iname_snow) - mLayerEnthalpy(iLayer) = mLayerHeatCap(iLayer)*mLayerTemp(iLayer) - LH_fus*iden_ice * mLayerVolFracIce(iLayer) - case(iname_soil) - mLayerEnthalpy(iLayer) = mLayerHeatCap(iLayer)*mLayerTemp(iLayer) - LH_fus*iden_water * mLayerVolFracIce(iLayer) - end select - end do ! looping through non-missing energy state variables in the snow+soil domain - - end associate - -end subroutine computEnthalpy - -! ********************************************************************************************************** -! public subroutine computEnthalpyPrime -! ********************************************************************************************************** -subroutine computEnthalpyPrime(& - ! input - computeVegFlux, & ! intent(in): logical flag to denote if computing the vegetation flux - indx_data, & ! intent(in): indices defining model states and layers - nLayers, & ! intent(in): number of snow layers - canopyDepth, & ! intent(in): canopy depth (m) - scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K) - scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2) - mLayerTempPrime, & ! intent(in): temperature of each snow/soil layer (K) - mLayerVolFracIcePrime, & ! intent(in): Prime value for volumetric fraction of ice (-) - heatCapVeg, & ! intent(in): specific heat of vegetation (J kg-1 K-1) - mLayerHeatCap, & ! intent(in): heat capacity of each snow/soil layer (J m-3 K-1) - ! output - scalarCanopyEnthalpyPrime, & ! intent(out): Prime value for the enthalpy of the vegetation canopy (J m-2) - mLayerEnthalpyPrime & ! intent(out): enthalpy of each snow/soil layer (J m-3) - ) - ! -------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - integer(i4b),intent(in) :: nLayers ! number of snow layers - real(rkind),intent(in) :: canopyDepth ! canopy depth (m) - real(rkind),intent(in) :: scalarCanopyTempPrime ! Prime value for the temperature of the vegetation canopy (K) - real(rkind),intent(in) :: scalarCanopyIcePrime ! Prime value for the ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: heatCapVeg ! specific heat of vegetation (J kg-1 K-1) - real(rkind),intent(in) :: mLayerTempPrime(:) ! Prime value for temperature of each snow/soil layer (K) - real(rkind),intent(in) :: mLayerVolFracIcePrime(:) ! Prime value for volumetric fraction of ice (-) - real(rkind),intent(in) :: mLayerHeatCap(:) ! heat capacity of each snow/soil layer (J m-3 K-1) - real(rkind),intent(out) :: scalarCanopyEnthalpyPrime ! Prime value for the enthalpy of the vegetation canopy (J m-2) - real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! enthalpy of each snow/soil layer (J m-3) - ! local variables - integer(i4b) :: iLayer ! loop index - - ! -------------------------------------------------------------------------------------------------------------------------------- - - associate(& - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of layer - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat & ! intent(in): [i4b(:)] indices for energy states - ) - - if(computeVegFlux)then - scalarCanopyEnthalpyPrime = heatCapVeg * scalarCanopyTempPrime - LH_fus*scalarCanopyIcePrime/canopyDepth - end if - ! (loop through non-missing energy state variables in the snow+soil domain) - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) - select case( layerType(iLayer) ) - case(iname_snow) - mLayerEnthalpyPrime(iLayer) = mLayerHeatCap(iLayer)*mLayerTempPrime(iLayer) - LH_fus*iden_ice * mLayerVolFracIcePrime(iLayer) - case(iname_soil) - mLayerEnthalpyPrime(iLayer) = mLayerHeatCap(iLayer)*mLayerTempPrime(iLayer) - LH_fus*iden_water * mLayerVolFracIcePrime(iLayer) - end select - end do ! looping through non-missing energy state variables in the snow+soil domain - - end associate - -end subroutine computEnthalpyPrime - -end module computEnthalpy_module diff --git a/build/source/engine/computResidWithPrime.f90 b/build/source/engine/computResidWithPrime.f90 index 8f2c34874..167728cca 100644 --- a/build/source/engine/computResidWithPrime.f90 +++ b/build/source/engine/computResidWithPrime.f90 @@ -203,7 +203,7 @@ subroutine computResidWithPrime(& ! compute the residual vector for the vegetation canopy ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision ! --> energy balance - if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*scalarCanairTempPrime - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) + if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg) * scalarCanairTempPrime - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime + scalarCanopyCmTrial * scalarCanopyWatPrime/canopyDepth - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) ! --> mass balance if(ixVegHyd/=integerMissing)then diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index d3f6b5622..160b0cbd2 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -256,6 +256,8 @@ subroutine coupled_em(& real(rkind) :: balanceSoilET ! output from the soil zone real(rkind) :: balanceAquifer0 ! total aquifer storage at the start of the step (kg m-2) real(rkind) :: balanceAquifer1 ! total aquifer storage at the end of the step (kg m-2) + real(rkind) :: balanceNrg(4) ! substep balance of energy per domain + real(rkind) :: balanceMass(4) ! substep balance of mass per domain ! test balance checks logical(lgt), parameter :: printBalance=.false. ! flag to print the balance checks real(rkind), allocatable :: liqSnowInit(:) ! volumetric liquid water conetnt of snow at the start of the time step @@ -605,6 +607,16 @@ subroutine coupled_em(& dt_sub = dt_init dtSave = whole_step ! length of whole substep + ! initialize balances from inside solver + diag_data%var(iLookDIAG%balanceCasNrg)%dat(1) = 0._rkind ! balance of energy in the canopy + diag_data%var(iLookDIAG%balanceVegNrg)%dat(1) = 0._rkind ! balance of energy in the vegetation + diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = 0._rkind ! balance of energy in the snow + diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = 0._rkind ! balance of energy in the soil + diag_data%var(iLookDIAG%balanceVegMass)%dat(1) = 0._rkind ! balance of water in the vegetation + diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = 0._rkind ! balance of water in the snow + diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = 0._rkind ! balance of water in the soil + diag_data%var(iLookDIAG%balanceAqMass)%dat(1) = 0._rkind ! balance of water in the aquifer + ! initialize the number of sub-steps nsub = 0 nsub_success = 0 @@ -878,6 +890,8 @@ subroutine coupled_em(& stepFailure, & ! intent(out): flag to denote that the coupled step failed ixSolution, & ! intent(out): solution method used in this iteration mean_step_dt_sub, & ! intent(out): mean solution step for the sub-step + balanceNrg, & ! intent(out): balance of energy per domain + balanceMass, & ! intent(out): balance of mass per domain err,cmessage) ! intent(out): error code and error message ! check for all errors (error recovery within opSplittin) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if @@ -1058,6 +1072,16 @@ subroutine coupled_em(& innerSoilCompress(:) = innerSoilCompress(:) + diag_data%var(iLookDIAG%mLayerCompress)%dat(:)*dt_wght if (nSnow>0) innerEffRainfall = innerEffRainfall + ( flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) + flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) )*dt_wght + ! sum the balance of energy and water per domain + diag_data%var(iLookDIAG%balanceCasNrg)%dat(1) = diag_data%var(iLookDIAG%balanceCasNrg)%dat(1) + balanceNrg(1) ! balance of energy in the canopy + diag_data%var(iLookDIAG%balanceVegNrg)%dat(1) = diag_data%var(iLookDIAG%balanceVegNrg)%dat(1) + balanceNrg(2) ! balance of energy in the vegetation + diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) + balanceNrg(3) ! balance of energy in the snow + diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) + balanceNrg(4) ! balance of energy in the soil + diag_data%var(iLookDIAG%balanceVegMass)%dat(1) = diag_data%var(iLookDIAG%balanceVegMass)%dat(1) + balanceMass(1) ! balance of water in the vegetation + diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) + balanceMass(2) ! balance of water in the snow + diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) + balanceMass(3) ! balance of water in the soil + diag_data%var(iLookDIAG%balanceAqMass)%dat(1) = diag_data%var(iLookDIAG%balanceAqMass)%dat(1) + balanceMass(4) ! balance of water in the aquifer + ! increment sub-step accepted step dt_solvInner = dt_solvInner + dt_sub dt_solv = dt_solv + dt_sub diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index c74ce8bce..b4095c793 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -417,7 +417,6 @@ subroutine eval8summa(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! *** compute volumetric heat capacity C_p = dH_T/dT call computHeatCap(& ! input: control variables @@ -729,7 +728,6 @@ integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & ! pointers to data in SUNDIALS vectors type(data4kinsol), pointer :: eqns_data ! equations data real(rkind), pointer :: stateVec(:) ! solution vector - real(rkind), pointer :: rVec(:) ! residual vector logical(lgt) :: feasible ! feasibility of state vector real(rkind) :: fNew ! function values, not needed here integer(i4b) :: err ! error in imposeConstraints @@ -741,7 +739,6 @@ integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & ! get data arrays from SUNDIALS vectors stateVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_y) - rVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_r) ! increment the proposed iteration for simple error control if needed if (eqns_data%firstStateiteration) then @@ -793,7 +790,7 @@ integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & feasible, & ! intent(out): flag to denote the feasibility of the solution always true inside SUNDIALS eqns_data%fluxVec, & ! intent(out): flux vector eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector + eqns_data%resVec, & ! intent(out): residual vector fNew, & ! intent(out): new function evaluation eqns_data%err,eqns_data%message) ! intent(out): error control if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 5f1e4935f..15999b3b6 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -81,6 +81,8 @@ subroutine eval8summaWithPrime(& flux_data, & ! intent(inout): model fluxes for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! input-output: here we need to pass some extra variables that do not get updated in in the IDA loops + scalarCanairTempTrial, & ! intent(out): trial value for temperature of the canopy air space (K) + scalarCanairEnthalpyTrial,& ! intent(out): enthalpy of the canopy air space (J m-3) scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) scalarCanopyTempPrev, & ! intent(in): value of canopy temperature (K) scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) @@ -167,6 +169,8 @@ subroutine eval8summaWithPrime(& type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! input-output: here we need to pass some extra variables that do not get updated in in the IDA loops + real(rkind),intent(out) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind),intent(out) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3) real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) real(rkind),intent(in) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) real(rkind),intent(out) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) @@ -212,7 +216,6 @@ subroutine eval8summaWithPrime(& ! -------------------------------------------------------------------------------------------------------------------------------- real(rkind) :: dt1 ! residual step size ! state variables - real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) ! derivative of state variables real(rkind) :: scalarCanairTempPrime ! derivative value for temperature of the canopy air space (K) @@ -223,8 +226,6 @@ subroutine eval8summaWithPrime(& real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! derivative value for volumetric fraction of liquid water (-) real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! derivative value for volumetric fraction of ice (-) ! enthalpy - real(rkind) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthalpyPrime ! enthalpy of each snow+soil layer (J m-3) real(rkind) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state real(rkind),dimension(nLayers) :: dEnthalpy_dTk ! derivatives in layer enthalpy w.r.t. temperature @@ -456,7 +457,7 @@ subroutine eval8summaWithPrime(& ! input: pre-computed derivatives dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature @@ -780,7 +781,6 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user type(data4ida), pointer :: eqns_data ! equations data real(rkind), pointer :: stateVec(:) ! solution vector real(rkind), pointer :: stateVecPrime(:) ! derivative vector - real(rkind), pointer :: rVec(:) ! residual vector logical(lgt) :: feasible ! feasibility of state vector !======= Internals ============ @@ -790,7 +790,6 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user ! get data arrays from SUNDIALS vectors stateVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_y) stateVecPrime(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_yp) - rVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_r) ! compute the flux and the residual vector for a given state vector call eval8summaWithPrime(& @@ -824,7 +823,9 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: here we need to pass some extra variables that do not get updated in in the IDA loops + ! input-output: here we need to pass some extra variables that do not get updated in in the IDA loops + eqns_data%scalarCanairTempTrial, & ! intent(out): trial value for the temperature of the canopy air space (K) + eqns_data%scalarCanairEnthalpyTrial,& ! intent(out): enthalpy of the canopy air space (J m-3) eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) @@ -848,12 +849,12 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) - eqns_data%mLayerTempPrime, & ! intent(out): derivative value for temperature of each snow and soil layer (K) - eqns_data%mLayerMatricHeadPrime, & ! intent(out): derivative value for matric head of each snow and soil layer (m) - eqns_data%mLayerMatricHeadLiqPrime,& ! intent(out): derivative value for liquid water matric head of each snow and soil layer (m) - eqns_data%mLayerVolFracWatPrime, & ! intent(out): derivative value for volumetric total water content of each snow and soil layer (-) - eqns_data%scalarCanopyTempPrime, & ! intent(out): derivative value for temperature of the vegetation canopy (K) - eqns_data%scalarCanopyWatPrime, & ! intent(out): derivative value for total water content of the vegetation canopy (kg m-2) + eqns_data%mLayerTempPrime, & ! intent(out): derivative value for temperature of each snow and soil layer (K) + eqns_data%mLayerMatricHeadPrime, & ! intent(out): derivative value for matric head of each snow and soil layer (m) + eqns_data%mLayerMatricHeadLiqPrime,& ! intent(out): derivative value for liquid water matric head of each snow and soil layer (m) + eqns_data%mLayerVolFracWatPrime, & ! intent(out): derivative value for volumetric total water content of each snow and soil layer (-) + eqns_data%scalarCanopyTempPrime, & ! intent(out): derivative value for temperature of the vegetation canopy (K) + eqns_data%scalarCanopyWatPrime, & ! intent(out): derivative value for total water content of the vegetation canopy (kg m-2) ! input-output: baseflow eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) @@ -861,7 +862,7 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user feasible, & ! intent(out): flag to denote the feasibility of the solution always true inside SUNDIALS eqns_data%fluxVec, & ! intent(out): flux vector eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector + eqns_data%resVec, & ! intent(out): residual vector eqns_data%err,eqns_data%message) ! intent(out): error control if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index c1f17a44f..d47881ae3 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -184,6 +184,8 @@ subroutine opSplittin(& stepFailure, & ! intent(out): flag to denote step failure ixSolution, & ! intent(out): solution method used in this iteration mean_step_dt, & ! intent(out): mean solution step for the time step + balanceNrg, & ! intent(out): balance of energy per domain + balanceMass, & ! intent(out): balance of mass per domain err,message) ! intent(out): error code and error message ! --------------------------------------------------------------------------------------- ! structure allocations @@ -224,6 +226,8 @@ subroutine opSplittin(& logical(lgt),intent(out) :: stepFailure ! flag to denote step failure integer(i4b),intent(out) :: ixSolution ! index of solution method (1,2) real(rkind),intent(out) :: mean_step_dt ! mean solution step for the time step + real(rkind),intent(out) :: balanceNrg(4) ! balance of energy per domain + real(rkind),intent(out) :: balanceMass(4) ! balance of mass per domain integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! --------------------------------------------------------------------------------------- @@ -485,7 +489,7 @@ subroutine opSplittin(& select case(failure) case(.false.) flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) - flux_mntemp%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_mntemp%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) addFirstFlux = .false. case(.true.) flux_data%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) @@ -525,6 +529,7 @@ subroutine opSplittin(& call varSubstep(in_varSubstep,io_varSubstep,& ! intent(inout): class objects for model control model_decisions,lookup_data,type_data,attr_data,forc_data,mpar_data,& ! intent(inout): data structures for model properties indx_data,prog_data,diag_data,flux_data,flux_mean,deriv_data,bvar_data,& + balanceNrg,balanceMass, & ! intent(inout): balances out_varSubstep) ! intent(out): class object for model control call finalize_varSubstep if (err/=0) then; message=trim(message)//trim(cmessage); if (err>0) return; end if ! error control diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index dff30975e..ad3d5dba5 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -29,6 +29,12 @@ module summaSolve4ida_module ! access the global print flag USE globalData,only:globalPrintFlag +! domain types +USE globalData,only:iname_cas ! named variables for the canopy air space +USE globalData,only:iname_veg ! named variables for vegetation +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil + ! access missing values USE globalData,only:integerMissing ! missing integer USE globalData,only:realMissing ! missing double precision number @@ -69,6 +75,11 @@ module summaSolve4ida_module bigBucket, & ! a big bucket (lumped aquifer model) noExplicit ! no explicit groundwater parameterization +! look-up values for the choice of heat capacity computation +USE mDecisions_module,only: & + closedForm, & ! heat capacity using closed form, not using enthalpy + enthalpyFD ! heat capacity using enthalpy + ! look-up values for method used to compute derivative USE mDecisions_module,only: & numerical, & ! numerical solution @@ -102,6 +113,8 @@ subroutine summaSolve4ida( & firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution + checkMassBalance, & ! intent(in): flag to check mass balance + checkNrgBalance, & ! intent(in): flag to check energy balance ! input: state vectors stateVecInit, & ! intent(in): initial state vector sMul, & ! intent(inout): state vector multiplier (USEd in the residual calculations) @@ -129,6 +142,8 @@ subroutine summaSolve4ida( & nSteps, & ! intent(out): number of time steps taken in solver stateVec, & ! intent(out): model state vector stateVecPrime, & ! intent(out): derivative of model state vector + balanceNrg, & ! intent(out): balance of energy per domain + balanceMass, & ! intent(out): balance of mass per domain err,message) ! intent(out): error control !======= Inclusions =========== @@ -151,6 +166,7 @@ subroutine summaSolve4ida( & USE computJacobWithPrime_module,only:computJacob4ida ! system Jacobian USE tol4ida_module,only:computWeight4ida ! weight required for tolerances USE var_lookup,only:maxvarDecisions ! maximum number of decisions + USE t2enthalpy_module,only:t2enthalpy ! compute enthalpy USE t2enthalpy_module,only:t2enthalpy_addphase ! add phase to enthalpy !======= Declarations ========= @@ -172,6 +188,8 @@ subroutine summaSolve4ida( & logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + logical(lgt),intent(in) :: checkMassBalance ! flag to check mass balance + logical(lgt),intent(in) :: checkNrgBalance ! flag to check energy balance ! input: state vectors real(rkind),intent(in) :: stateVecInit(:) ! model state vector real(qp),intent(in) :: sMul(:) ! state vector multiplier (used in the residual calculations) @@ -199,6 +217,9 @@ subroutine summaSolve4ida( & real(rkind),intent(inout) :: stateVecPrime(:) ! model state vector (y') logical(lgt),intent(out) :: idaSucceeds ! flag to indicate if IDA is successful logical(lgt),intent(inout) :: tooMuchMelt ! flag to denote that there was too much melt + ! output: residual terms and balances + real(rkind),intent(out) :: balanceNrg(4) ! balance of energy per domain + real(rkind),intent(out) :: balanceMass(4) ! balance of mass per domain ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -234,413 +255,492 @@ subroutine summaSolve4ida( & character(LEN=256) :: cmessage ! error message of downwind routine real(rkind),allocatable :: mLayerMatricHeadPrimePrev(:) ! previous derivative value for total water matric potential (m s-1) real(rkind),allocatable :: dCompress_dPsiPrev(:) ! previous derivative value soil compression + ! enthalpy derivatives + real(rkind) :: dCanEnthalpy_dTk_unused ! will not be used, derivatives in canopy enthalpy w.r.t. temperature + real(rkind) :: dCanEnthalpy_dWat_unused ! will not be used, derivatives in canopy enthalpy w.r.t. water state + real(rkind),allocatable :: dEnthalpy_dTk_unused(:) ! will not be used, derivatives in layer enthalpy w.r.t. temperature + real(rkind),allocatable :: dEnthalpy_dWat_unused(:) ! will not be used, derivatives in layer enthalpy w.r.t. water state + ! flags logical(lgt) :: use_fdJac ! flag to use finite difference Jacobian, controlled by decision fDerivMeth logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true logical(lgt),parameter :: detect_events = .true. ! flag to do event detection and restarting, default true - logical(lgt),parameter :: checkNrgBalance = .false. ! flag to check energy balance, default false - ! ----------------------------------------------------------------------------------------------------- - - ! initialize error control - err=0; message="summaSolve4ida/" - - ! choose Jacobian type - select case(model_decisions(iLookDECISIONS%fDerivMeth)%iDecision) - case(numerical); use_fdJac =.true. - case(analytical); use_fdJac =.false. - case default; err=20; message=trim(message)//'expect choice numericl or analytic to calculate derivatives for Jacobian'; return - end select - - nState = nStat ! total number of state variables in SUNDIALS type - idaSucceeds = .true. - - ! fill eqns_data which will be required later to call eval8summaWithPrime - eqns_data%dt = dt - eqns_data%nSnow = nSnow - eqns_data%nSoil = nSoil - eqns_data%nLayers = nLayers - eqns_data%nState = nState - eqns_data%ixMatrix = ixMatrix - eqns_data%firstSubStep = firstSubStep - eqns_data%computeVegFlux = computeVegFlux - eqns_data%scalarSolution = scalarSolution - eqns_data%lookup_data = lookup_data - eqns_data%type_data = type_data - eqns_data%attr_data = attr_data - eqns_data%mpar_data = mpar_data - eqns_data%forc_data = forc_data - eqns_data%bvar_data = bvar_data - eqns_data%prog_data = prog_data - eqns_data%indx_data = indx_data - eqns_data%diag_data = diag_data - eqns_data%flux_data = flux_data - eqns_data%deriv_data = deriv_data - eqns_data%ixSaturation = ixSaturation - - ! allocate space and fill - allocate( eqns_data%model_decisions(maxvarDecisions) ); eqns_data%model_decisions = model_decisions - allocate( eqns_data%atol(nState) ); eqns_data%atol = atol - allocate( eqns_data%rtol(nState) ); eqns_data%rtol = rtol - allocate( eqns_data%sMul(nState) ); eqns_data%sMul = sMul - allocate( eqns_data%dMat(nState) ); eqns_data%dMat = dMat - - ! allocate space for the to save previous fluxes - call allocLocal(flux_meta(:),flux_prev,nSnow,nSoil,err,message) - if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif - flux_prev = eqns_data%flux_data - - ! allocate space for other variables - if(model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel)then - allocate(eqns_data%dBaseflow_dMatric(nSoil,nSoil),stat=err) - else - allocate(eqns_data%dBaseflow_dMatric(0,0),stat=err) - end if - allocate( eqns_data%mLayerMatricHeadLiqTrial(nSoil) ) - allocate( eqns_data%mLayerMatricHeadTrial(nSoil) ) - allocate( eqns_data%mLayerMatricHeadPrev(nSoil) ) - allocate( eqns_data%mLayerVolFracWatTrial(nLayers) ) - allocate( eqns_data%mLayerVolFracWatPrev(nLayers) ) - allocate( eqns_data%mLayerTempTrial(nLayers) ) - allocate( eqns_data%mLayerTempPrev(nLayers) ) - allocate( eqns_data%mLayerVolFracIceTrial(nLayers) ) - allocate( eqns_data%mLayerVolFracIcePrev(nLayers) ) - allocate( eqns_data%mLayerVolFracLiqTrial(nLayers) ) - allocate( eqns_data%mLayerVolFracLiqPrev(nLayers) ) - allocate( eqns_data%mLayerEnthalpyTrial(nLayers) ) - allocate( eqns_data%mLayerEnthalpyPrev(nLayers) ) - allocate( eqns_data%mLayerTempPrime(nLayers) ) - allocate( eqns_data%mLayerMatricHeadPrime(nSoil) ) - allocate( eqns_data%mLayerMatricHeadLiqPrime(nSoil) ) - allocate( eqns_data%mLayerVolFracWatPrime(nLayers) ) - allocate( mLayerMatricHeadPrimePrev(nSoil) ) - allocate( dCompress_dPsiPrev(nSoil) ) - allocate( eqns_data%fluxVec(nState) ) - allocate( eqns_data%resSink(nState) ) - - ! need the following values for the first substep - eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) - eqns_data%scalarCanopyIcePrev = prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) - eqns_data%scalarCanopyLiqPrev = prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) - eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) - eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) - eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) - eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) - eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) - eqns_data%mLayerVolFracLiqPrev(:) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(:) - eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) - eqns_data%scalarAquiferStoragePrev = prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) - mLayerMatricHeadPrimePrev(:) = 0._rkind - dCompress_dPsiPrev(:) = 0._rkind - - retval = FSUNContext_Create(c_null_ptr, sunctx) - - ! create serial vectors - sunvec_y => FN_VMake_Serial(nState, stateVec, sunctx) - if (.not. associated(sunvec_y)) then; err=20; message='summaSolve4ida: sunvec = NULL'; return; endif - sunvec_yp => FN_VMake_Serial(nState, stateVecPrime, sunctx) - if (.not. associated(sunvec_yp)) then; err=20; message='summaSolve4ida: sunvec = NULL'; return; endif - - ! initialize solution vectors - call setInitialCondition(nState, stateVecInit, sunvec_y, sunvec_yp) - - ! create memory - ida_mem = FIDACreate(sunctx) - if (.not. c_associated(ida_mem)) then; err=20; message='summaSolve4ida: ida_mem = NULL'; return; endif - - ! Attach user data to memory - retval = FIDASetUserData(ida_mem, c_loc(eqns_data)) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetUserData'; return; endif - - ! Set the function IDA will use to advance the state - t0 = 0._rkind - retval = FIDAInit(ida_mem, c_funloc(eval8summa4ida), t0, sunvec_y, sunvec_yp) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDAInit'; return; endif - - ! set tolerances - retval = FIDAWFtolerances(ida_mem, c_funloc(computWeight4ida)) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDAWFtolerances'; return; endif - - ! initialize rootfinding problem and allocate space, counting roots - if(detect_events)then - nRoot = 0 - if(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing) nRoot = nRoot+1 - if(nSnow>0)then - do i = 1,nSnow - if(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing) nRoot = nRoot+1 - enddo + ! link to the necessary variables + associate(& + ! number of state variables of a specific type + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + ! model indices + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain + ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain + ixSoilOnlyNrg => indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the soil subdomain + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the soil subdomain + layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] named variables defining the type of layer in snow+soil domain + ) ! association to necessary variables for the residual computations + + ! initialize error control + err=0; message="summaSolve4ida/" + + ! choose Jacobian type + select case(model_decisions(iLookDECISIONS%fDerivMeth)%iDecision) + case(numerical); use_fdJac =.true. + case(analytical); use_fdJac =.false. + case default; err=20; message=trim(message)//'expect choice numericl or analytic to calculate derivatives for Jacobian'; return + end select + + nState = nStat ! total number of state variables in SUNDIALS type + idaSucceeds = .true. + + ! fill eqns_data which will be required later to call eval8summaWithPrime + eqns_data%dt = dt + eqns_data%nSnow = nSnow + eqns_data%nSoil = nSoil + eqns_data%nLayers = nLayers + eqns_data%nState = nState + eqns_data%ixMatrix = ixMatrix + eqns_data%firstSubStep = firstSubStep + eqns_data%computeVegFlux = computeVegFlux + eqns_data%scalarSolution = scalarSolution + eqns_data%lookup_data = lookup_data + eqns_data%type_data = type_data + eqns_data%attr_data = attr_data + eqns_data%mpar_data = mpar_data + eqns_data%forc_data = forc_data + eqns_data%bvar_data = bvar_data + eqns_data%prog_data = prog_data + eqns_data%indx_data = indx_data + eqns_data%diag_data = diag_data + eqns_data%flux_data = flux_data + eqns_data%deriv_data = deriv_data + eqns_data%ixSaturation = ixSaturation + + ! allocate space and fill + allocate( eqns_data%model_decisions(maxvarDecisions) ); eqns_data%model_decisions = model_decisions + allocate( eqns_data%atol(nState) ); eqns_data%atol = atol + allocate( eqns_data%rtol(nState) ); eqns_data%rtol = rtol + allocate( eqns_data%sMul(nState) ); eqns_data%sMul = sMul + allocate( eqns_data%dMat(nState) ); eqns_data%dMat = dMat + + ! allocate space for the to save previous fluxes + call allocLocal(flux_meta(:),flux_prev,nSnow,nSoil,err,message) + if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif + flux_prev = eqns_data%flux_data + + ! allocate space for other variables + if(model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel)then + allocate(eqns_data%dBaseflow_dMatric(nSoil,nSoil),stat=err) + else + allocate(eqns_data%dBaseflow_dMatric(0,0),stat=err) + end if + allocate( eqns_data%mLayerMatricHeadLiqTrial(nSoil) ) + allocate( eqns_data%mLayerMatricHeadTrial(nSoil) ) + allocate( eqns_data%mLayerMatricHeadPrev(nSoil) ) + allocate( eqns_data%mLayerVolFracWatTrial(nLayers) ) + allocate( eqns_data%mLayerVolFracWatPrev(nLayers) ) + allocate( eqns_data%mLayerTempTrial(nLayers) ) + allocate( eqns_data%mLayerTempPrev(nLayers) ) + allocate( eqns_data%mLayerVolFracIceTrial(nLayers) ) + allocate( eqns_data%mLayerVolFracIcePrev(nLayers) ) + allocate( eqns_data%mLayerVolFracLiqTrial(nLayers) ) + allocate( eqns_data%mLayerVolFracLiqPrev(nLayers) ) + allocate( eqns_data%mLayerEnthalpyTrial(nLayers) ) + allocate( eqns_data%mLayerEnthalpyPrev(nLayers) ) + allocate( eqns_data%mLayerTempPrime(nLayers) ) + allocate( eqns_data%mLayerMatricHeadPrime(nSoil) ) + allocate( eqns_data%mLayerMatricHeadLiqPrime(nSoil) ) + allocate( eqns_data%mLayerVolFracWatPrime(nLayers) ) + allocate( mLayerMatricHeadPrimePrev(nSoil) ) + allocate( dCompress_dPsiPrev(nSoil) ) + allocate( eqns_data%fluxVec(nState) ) + allocate( eqns_data%resVec(nState) ) + allocate( eqns_data%resSink(nState) ) + allocate( dEnthalpy_dTk_unused(nLayers) ) + allocate( dEnthalpy_dWat_unused(nLayers) ) + + ! need the following values for the first substep + eqns_data%scalarCanairEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) + eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) + eqns_data%scalarCanopyIcePrev = prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) + eqns_data%scalarCanopyLiqPrev = prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) + eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) + eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) + eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) + eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) + eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) + eqns_data%mLayerVolFracLiqPrev(:) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(:) + eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) + eqns_data%scalarAquiferStoragePrev = prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) + mLayerMatricHeadPrimePrev(:) = 0._rkind + dCompress_dPsiPrev(:) = 0._rkind + + retval = FSUNContext_Create(c_null_ptr, sunctx) + + ! create serial vectors + sunvec_y => FN_VMake_Serial(nState, stateVec, sunctx) + if (.not. associated(sunvec_y)) then; err=20; message='summaSolve4ida: sunvec = NULL'; return; endif + sunvec_yp => FN_VMake_Serial(nState, stateVecPrime, sunctx) + if (.not. associated(sunvec_yp)) then; err=20; message='summaSolve4ida: sunvec = NULL'; return; endif + + ! initialize solution vectors + call setInitialCondition(nState, stateVecInit, sunvec_y, sunvec_yp) + + ! create memory + ida_mem = FIDACreate(sunctx) + if (.not. c_associated(ida_mem)) then; err=20; message='summaSolve4ida: ida_mem = NULL'; return; endif + + ! Attach user data to memory + retval = FIDASetUserData(ida_mem, c_loc(eqns_data)) + if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetUserData'; return; endif + + ! Set the function IDA will use to advance the state + t0 = 0._rkind + retval = FIDAInit(ida_mem, c_funloc(eval8summa4ida), t0, sunvec_y, sunvec_yp) + if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDAInit'; return; endif + + ! set tolerances + retval = FIDAWFtolerances(ida_mem, c_funloc(computWeight4ida)) + if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDAWFtolerances'; return; endif + + ! initialize rootfinding problem and allocate space, counting roots + if(detect_events)then + nRoot = 0 + if(ixVegNrg/=integerMissing) nRoot = nRoot+1 + if(nSnow>0)then + do i = 1,nSnow + if(ixSnowOnlyNrg(i)/=integerMissing) nRoot = nRoot+1 + enddo + endif + if(nSoil>0)then + do i = 1,nSoil + if(ixSoilOnlyHyd(i)/=integerMissing) nRoot = nRoot+1 + if(ixSoilOnlyNrg(i)/=integerMissing) nRoot = nRoot+1 + enddo + endif + allocate( rootsfound(nRoot) ) + allocate( rootdir(nRoot) ) + rootdir = 0 + retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4ida)) + if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDARootInit'; return; endif + else ! will not use, allocate at something + nRoot = 1 + allocate( rootsfound(nRoot) ) + allocate( rootdir(nRoot) ) endif - if(nSoil>0)then - do i = 1,nSoil - if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(i)/=integerMissing) nRoot = nRoot+1 - if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing) nRoot = nRoot+1 - enddo + + ! define the form of the matrix + select case(ixMatrix) + case(ixBandMatrix) + mu = ku; lu = kl; + ! Create banded SUNMatrix for use in linear solves + sunmat_A => FSUNBandMatrix(nState, mu, lu, sunctx) + if (.not. associated(sunmat_A)) then; err=20; message='summaSolve4ida: sunmat = NULL'; return; endif + + ! Create banded SUNLinearSolver object + sunlinsol_LS => FSUNLinSol_Band(sunvec_y, sunmat_A, sunctx) + if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolve4ida: sunlinsol = NULL'; return; endif + + case(ixFullMatrix) + ! Create dense SUNMatrix for use in linear solves + sunmat_A => FSUNDenseMatrix(nState, nState, sunctx) + if (.not. associated(sunmat_A)) then; err=20; message='summaSolve4ida: sunmat = NULL'; return; endif + + ! Create dense SUNLinearSolver object + sunlinsol_LS => FSUNLinSol_Dense(sunvec_y, sunmat_A, sunctx) + if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolve4ida: sunlinsol = NULL'; return; endif + + ! check + case default; err=20; message='summaSolve4ida: error in type of matrix'; return + + end select ! form of matrix + + ! Attach the matrix and linear solver + ! For the nonlinear solver, IDA uses a Newton SUNNonlinearSolver-- it is not necessary to create and attach it + retval = FIDASetLinearSolver(ida_mem, sunlinsol_LS, sunmat_A); + if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetLinearSolver'; return; endif + + ! Set the user-supplied Jacobian routine + if(.not.use_fdJac)then + retval = FIDASetJacFn(ida_mem, c_funloc(computJacob4ida)) + if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetJacFn'; return; endif endif - allocate( rootsfound(nRoot) ) - allocate( rootdir(nRoot) ) - rootdir = 0 - retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4ida)) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDARootInit'; return; endif - else ! will not use, allocate at something - nRoot = 1 - allocate( rootsfound(nRoot) ) - allocate( rootdir(nRoot) ) - endif - - ! define the form of the matrix - select case(ixMatrix) - case(ixBandMatrix) - mu = ku; lu = kl; - ! Create banded SUNMatrix for use in linear solves - sunmat_A => FSUNBandMatrix(nState, mu, lu, sunctx) - if (.not. associated(sunmat_A)) then; err=20; message='summaSolve4ida: sunmat = NULL'; return; endif - - ! Create banded SUNLinearSolver object - sunlinsol_LS => FSUNLinSol_Band(sunvec_y, sunmat_A, sunctx) - if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolve4ida: sunlinsol = NULL'; return; endif - - case(ixFullMatrix) - ! Create dense SUNMatrix for use in linear solves - sunmat_A => FSUNDenseMatrix(nState, nState, sunctx) - if (.not. associated(sunmat_A)) then; err=20; message='summaSolve4ida: sunmat = NULL'; return; endif - - ! Create dense SUNLinearSolver object - sunlinsol_LS => FSUNLinSol_Dense(sunvec_y, sunmat_A, sunctx) - if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolve4ida: sunlinsol = NULL'; return; endif - - ! check - case default; err=20; message='summaSolve4ida: error in type of matrix'; return - - end select ! form of matrix - - ! Attach the matrix and linear solver - ! For the nonlinear solver, IDA uses a Newton SUNNonlinearSolver-- it is not necessary to create and attach it - retval = FIDASetLinearSolver(ida_mem, sunlinsol_LS, sunmat_A); - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetLinearSolver'; return; endif - - ! Set the user-supplied Jacobian routine - if(.not.use_fdJac)then - retval = FIDASetJacFn(ida_mem, c_funloc(computJacob4ida)) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetJacFn'; return; endif - endif - - ! Enforce the solver to stop at end of the time step - retval = FIDASetStopTime(ida_mem, dt_cur) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetStopTime'; return; endif - - ! Set solver parameters at end of setup - call setSolverParams(dt_cur, nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)), ida_mem, retval) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in setSolverParams'; return; endif - - ! Disable error messages and warnings - if(offErrWarnMessage) then - retval = FIDASetErrFile(ida_mem, c_null_ptr) - retval = FIDASetNoInactiveRootWarn(ida_mem) - endif - - !*********************** Main Solver * loop on one_step mode ***************************** - tinystep = .false. - tret(1) = t0 ! initial time - tretPrev = tret(1) - nSteps = 0 ! initialize number of time steps taken in solver - do while(tret(1) < dt_cur) - - ! call this at beginning of step to reduce root bouncing (only looking in one direction) - if(detect_events .and. .not.tinystep)then - call find_rootdir(eqns_data, rootdir) - retval = FIDASetRootDirection(ida_mem, rootdir) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetRootDirection'; return; endif + + ! Enforce the solver to stop at end of the time step + retval = FIDASetStopTime(ida_mem, dt_cur) + if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetStopTime'; return; endif + + ! Set solver parameters at end of setup + call setSolverParams(dt_cur, nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)), ida_mem, retval) + if (retval /= 0) then; err=20; message='summaSolve4ida: error in setSolverParams'; return; endif + + ! Disable error messages and warnings + if(offErrWarnMessage) then + retval = FIDASetErrFile(ida_mem, c_null_ptr) + retval = FIDASetNoInactiveRootWarn(ida_mem) endif + + !*********************** Main Solver * loop on one_step mode ***************************** + tinystep = .false. + tret(1) = t0 ! initial time + tretPrev = tret(1) + nSteps = 0 ! initialize number of time steps taken in solver + + ! initialize balances + balanceNrg = 0._rkind + balanceMass = 0._rkind + + do while(tret(1) < dt_cur) + + ! call this at beginning of step to reduce root bouncing (only looking in one direction) + if(detect_events .and. .not.tinystep)then + call find_rootdir(eqns_data, rootdir) + retval = FIDASetRootDirection(ida_mem, rootdir) + if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetRootDirection'; return; endif + endif + + eqns_data%firstFluxCall = .false. ! already called for initial + eqns_data%firstSplitOper = .true. ! always true at start of dt_cur since no splitting + + ! call IDASolve, advance solver just one internal step + retvalr = FIDASolve(ida_mem, dt_cur, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) + ! early return if IDASolve failed + if( retvalr < 0 )then + idaSucceeds = .false. + call getErrMessage(retvalr,cmessage) + message=trim(message)//trim(cmessage) + !if(retvalr==-1) err = -20 ! max iterations failure, exit and reduce the data window time in varSubStep + exit + end if + + tooMuchMelt = .false. + ! loop through non-missing energy state variables in the snow domain to see if need to merge + do concurrent (i=1:nSnow,ixSnowOnlyNrg(i)/=integerMissing) + if (stateVec(ixSnowOnlyNrg(i)) > Tfreeze) tooMuchMelt = .true. !need to merge + enddo + if(tooMuchMelt)exit + + ! get the last stepsize and difference from previous end time, not necessarily the same + retval = FIDAGetLastStep(ida_mem, dt_last) + dt_diff = tret(1) - tretPrev + nSteps = nSteps + 1 ! number of time steps taken in solver + + ! check the feasibility of the solution + feasible=.true. + call checkFeas(& + ! input + stateVec, & ! intent(in): model state vector (mixed units) + eqns_data%mpar_data, & ! intent(in): model parameters + eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU + eqns_data%indx_data, & ! intent(in): indices defining model states and layers + ! output: feasibility + feasible, & ! intent(inout): flag to denote the feasibility of the solution + ! output: error control + err,cmessage) ! intent(out): error control + + ! early return for non-feasible solutions, right now will just fail if goes infeasible + if(.not.feasible)then + idaSucceeds = .false. + message=trim(message)//trim(cmessage)//'non-feasible' ! err=0 is already set, could make this a warning and reduce the data window time in varSubStep + exit + end if + + ! sum of fluxes smoothed over the time step, average from instantaneous values + do iVar=1,size(flux_meta) + flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + ( eqns_data%flux_data%var(iVar)%dat(:) & + + flux_prev%var(iVar)%dat(:) ) *dt_diff/2._rkind + end do + mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + ( eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) * eqns_data%mLayerMatricHeadPrime(:) & + + dCompress_dPsiPrev(:) * mLayerMatricHeadPrimePrev(:) ) * dt_diff/2._rkind + + ! ---- + ! * check energy balance, need to include phase change + !------------------------ + if(checkNrgBalance)then + + if( model_decisions(iLookDECISIONS%howHeatCap)%iDecision == closedForm)then ! did not compute enthalpy without phase already + call t2enthalpy(& + ! input: data structures + eqns_data%diag_data, & ! intent(in): model diagnostic variables for a local HRU + eqns_data%mpar_data, & ! intent(in): parameter data structure + eqns_data%indx_data, & ! intent(in): model indices + eqns_data%lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + eqns_data%scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) + eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + eqns_data%scalarCanopyLiqTrial + eqns_data%scalarCanopyIceTrial, & ! intent(in): trial value of canopy total water (kg m-2) + ! input: variables for the snow-soil domain + eqns_data%mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + eqns_data%mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + eqns_data%mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + ! input: pre-computed derivatives + deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + ! output: enthalpy + eqns_data%scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) + eqns_data%scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + eqns_data%mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + dCanEnthalpy_dTk_unused, & ! intent(out): will not be used, derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat_unused, & ! intent(out): will not be used, derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk_unused, & ! intent(out): will not be used, derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat_unused, & ! intent(out): will not be used, derivatives in layer enthalpy w.r.t. water state + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + endif - eqns_data%firstFluxCall = .false. ! already called for initial - eqns_data%firstSplitOper = .true. ! always true at start of dt_cur since no splitting - - ! call IDASolve, advance solver just one internal step - retvalr = FIDASolve(ida_mem, dt_cur, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) - ! early return if IDASolve failed - if( retvalr < 0 )then - idaSucceeds = .false. - call getErrMessage(retvalr,cmessage) - message=trim(message)//trim(cmessage) - !if(retvalr==-1) err = -20 ! max iterations failure, exit and reduce the data window time in varSubStep - exit - end if - - tooMuchMelt = .false. - ! loop through non-missing energy state variables in the snow domain to see if need to merge - do concurrent (i=1:nSnow,indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing) - if (stateVec(indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)) > Tfreeze) tooMuchMelt = .true. !need to merge - enddo - if(tooMuchMelt)exit - - ! get the last stepsize and difference from previous end time, not necessarily the same - retval = FIDAGetLastStep(ida_mem, dt_last) - dt_diff = tret(1) - tretPrev - nSteps = nSteps + 1 ! number of time steps taken in solver - - ! check the feasibility of the solution - feasible=.true. - call checkFeas(& - ! input - stateVec, & ! intent(in): model state vector (mixed units) - eqns_data%mpar_data, & ! intent(in): model parameters - eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU - eqns_data%indx_data, & ! intent(in): indices defining model states and layers - ! output: feasibility - feasible, & ! intent(inout): flag to denote the feasibility of the solution - ! output: error control - err,cmessage) ! intent(out): error control - - ! early return for non-feasible solutions, right now will just fail if goes infeasible - if(.not.feasible)then - idaSucceeds = .false. - message=trim(message)//trim(cmessage)//'non-feasible' ! err=0 is already set, could make this a warning and reduce the data window time in varSubStep - exit - end if - - ! sum of fluxes smoothed over the time step, average from instantaneous values - do iVar=1,size(flux_meta) - flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + ( eqns_data%flux_data%var(iVar)%dat(:) & - + flux_prev%var(iVar)%dat(:) ) *dt_diff/2._rkind - end do - mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + ( eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) * eqns_data%mLayerMatricHeadPrime(:) & - + dCompress_dPsiPrev(:) * mLayerMatricHeadPrimePrev(:) ) * dt_diff/2._rkind - - ! ---- - ! * check energy balance, have to call again because need to include phase change - !------------------------ - if(checkNrgBalance)then - ! compute enthalpy at t_{n+1} - call t2enthalpy_addphase(& - ! input: data structures - eqns_data%diag_data, & ! intent(in): model diagnostic variables for a local HRU - eqns_data%mpar_data, & ! intent(in): parameter data structure - eqns_data%indx_data, & ! intent(in): model indices - eqns_data%lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - eqns_data%scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - eqns_data%mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - eqns_data%mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - eqns_data%mLayerVolFracIceTrial, & ! intent(in): trial vector of ice volume fraction (-) - ! input/output: enthalpy - eqns_data%scalarCanopyEnthalpyTrial, & ! intent(inout): enthalpy of the vegetation canopy (J m-3) - eqns_data%mLayerEnthalpyTrial, & ! intent(inout): enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,message) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! compute energy balance - !sum_bal = eqns_data%scalarCanopyEnthalpyTrial - eqns_data%scalarCanopyEnthalpyPrev - rVec*dt_diff - - endif - - - - - - - - - - - - - - - - - ! save required quantities for next step - eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial - eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial - eqns_data%scalarCanopyLiqPrev = eqns_data%scalarCanopyLiqTrial - eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial - eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) - eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) - eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) - eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) - eqns_data%mLayerVolFracLiqPrev(:) = eqns_data%mLayerVolFracLiqTrial(:) - eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) - eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial - mLayerMatricHeadPrimePrev(:) = eqns_data%mLayerMatricHeadPrime(:) - dCompress_dPsiPrev(:) = eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) - tretPrev = tret(1) - flux_prev = eqns_data%flux_data - - ! Restart for where vegetation and layers cross freezing point - if(detect_events)then - if (retvalr .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) - ! rootsfound[i]= +1 indicates that gi is increasing, -1 g[i] decreasing, 0 no root - !retval = FIDAGetRootInfo(ida_mem, rootsfound) - !if (retval < 0) then; err=20; message='summaSolve4ida: error in FIDAGetRootInfo'; return; endif - !print '(a,f15.7,2x,17(i2,2x))', "time, rootsfound[] = ", tret(1), rootsfound - ! Reininitialize solver for running after discontinuity and restart - retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDAReInit'; return; endif - if(dt_last(1) < 0.1_rkind)then ! don't keep calling if step is small (more accurate with this tiny but getting hung up) - retval = FIDARootInit(ida_mem, 0, c_funloc(layerDisCont4ida)) - tinystep = .true. - else - retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4ida)) - tinystep = .false. + ! compute enthalpy at t_{n+1} + call t2enthalpy_addphase(& + ! input: data structures + eqns_data%diag_data, & ! intent(in): model diagnostic variables for a local HRU + eqns_data%mpar_data, & ! intent(in): parameter data structure + eqns_data%indx_data, & ! intent(in): model indices + eqns_data%lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + eqns_data%scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + eqns_data%mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + eqns_data%mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + eqns_data%mLayerVolFracIceTrial, & ! intent(in): trial vector of ice volume fraction (-) + ! input/output: enthalpy + eqns_data%scalarCanopyEnthalpyTrial, & ! intent(inout): enthalpy of the vegetation canopy (J m-3) + eqns_data%mLayerEnthalpyTrial, & ! intent(inout): enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,message) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! compute energy balance + if(ixCasNrg/=integerMissing) balanceNrg(1) = balanceNrg(1) + eqns_data%scalarCanairEnthalpyTrial - eqns_data%scalarCanairEnthalpyPrev - eqns_data%fluxVec(ixVegNrg)*dt_diff + if(ixVegNrg/=integerMissing) balanceNrg(2) = balanceNrg(2) + eqns_data%scalarCanopyEnthalpyTrial - eqns_data%scalarCanopyEnthalpyPrev - eqns_data%fluxVec(ixVegNrg)*dt_diff + if(nSnowSoilNrg>0)then + do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + select case(layerType(i)) + case(iname_snow); balanceNrg(3) = balanceNrg(3) + (eqns_data%mLayerEnthalpyTrial(i) - eqns_data%mLayerEnthalpyPrev(i) - eqns_data%fluxVec(ixSnowSoilNrg(i))*dt_diff)/nSnow + case(iname_soil); balanceNrg(4) = balanceNrg(4) + (eqns_data%mLayerEnthalpyTrial(i) - eqns_data%mLayerEnthalpyPrev(i) - eqns_data%fluxVec(ixSnowSoilNrg(i))*dt_diff)/(nLayers-nSnow) + end select + enddo endif - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDARootInit'; return; endif endif + + ! ---- + ! * check mass balance, from residuals + !------------------------ + if(checkMassBalance)then + + ! compute mass balance + ! resVec is the instanteous residual vector from the solver + if(ixVegHyd/=integerMissing) balanceMass(1) = balanceMass(1) + eqns_data%resVec(ixVegHyd)*dt_diff + if(nSnowSoilHyd>0)then + do concurrent (i=1:nLayers,ixSnowSoilHyd(i)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + select case(layerType(i)) + case(iname_snow); balanceMass(2) = balanceMass(2) + eqns_data%resVec(ixSnowSoilHyd(i))*dt_diff/nSnow + case(iname_soil); balanceMass(3) = balanceMass(3) + eqns_data%resVec(ixSnowSoilHyd(i))*dt_diff/(nLayers-nSnow) + end select + enddo + endif + if(ixAqWat/=integerMissing) balanceMass(4) = balanceMass(4) + eqns_data%resVec(ixAqWat)*dt_diff + endif + + ! save required quantities for next step + eqns_data%scalarCanairEnthalpyPrev = eqns_data%scalarCanairEnthalpyTrial + eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial + eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial + eqns_data%scalarCanopyLiqPrev = eqns_data%scalarCanopyLiqTrial + eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial + eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) + eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) + eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) + eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) + eqns_data%mLayerVolFracLiqPrev(:) = eqns_data%mLayerVolFracLiqTrial(:) + eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) + eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial + mLayerMatricHeadPrimePrev(:) = eqns_data%mLayerMatricHeadPrime(:) + dCompress_dPsiPrev(:) = eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) + tretPrev = tret(1) + flux_prev = eqns_data%flux_data + + ! Restart for where vegetation and layers cross freezing point + if(detect_events)then + if (retvalr .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) + ! rootsfound[i]= +1 indicates that gi is increasing, -1 g[i] decreasing, 0 no root + !retval = FIDAGetRootInfo(ida_mem, rootsfound) + !if (retval < 0) then; err=20; message='summaSolve4ida: error in FIDAGetRootInfo'; return; endif + !print '(a,f15.7,2x,17(i2,2x))', "time, rootsfound[] = ", tret(1), rootsfound + ! Reininitialize solver for running after discontinuity and restart + retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) + if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDAReInit'; return; endif + if(dt_last(1) < 0.1_rkind)then ! don't keep calling if step is small (more accurate with this tiny but getting hung up) + retval = FIDARootInit(ida_mem, 0, c_funloc(layerDisCont4ida)) + tinystep = .true. + else + retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4ida)) + tinystep = .false. + endif + if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDARootInit'; return; endif + endif + endif + + enddo ! while loop on one_step mode until time dt_cur + !****************************** End of Main Solver *************************************** + + if(idaSucceeds)then + ! copy to output data + diag_data = eqns_data%diag_data + flux_data = eqns_data%flux_data + deriv_data = eqns_data%deriv_data + ixSaturation = eqns_data%ixSaturation + indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = eqns_data%indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) !only number of flux calculations changes in indx_data + err = eqns_data%err + message = eqns_data%message + else + eqns_data%fluxVec(:) = realMissing endif - - enddo ! while loop on one_step mode until time dt_cur - !****************************** End of Main Solver *************************************** - - if(idaSucceeds)then - ! copy to output data - diag_data = eqns_data%diag_data - flux_data = eqns_data%flux_data - deriv_data = eqns_data%deriv_data - ixSaturation = eqns_data%ixSaturation - indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = eqns_data%indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) !only number of flux calculations changes in indx_data - err = eqns_data%err - message = eqns_data%message - else - eqns_data%fluxVec(:) = realMissing - endif - - ! free memory - deallocate( eqns_data%model_decisions) - deallocate( eqns_data%sMul ) - deallocate( eqns_data%dMat ) - deallocate( eqns_data%dBaseflow_dMatric ) - deallocate( eqns_data%mLayerMatricHeadLiqTrial ) - deallocate( eqns_data%mLayerMatricHeadTrial ) - deallocate( eqns_data%mLayerMatricHeadPrev ) - deallocate( eqns_data%mLayerVolFracWatTrial ) - deallocate( eqns_data%mLayerVolFracWatPrev ) - deallocate( eqns_data%mLayerVolFracIceTrial ) - deallocate( eqns_data%mLayerTempPrev ) - deallocate( eqns_data%mLayerTempTrial ) - deallocate( eqns_data%mLayerVolFracIcePrev ) - deallocate( eqns_data%mLayerVolFracLiqPrev ) - deallocate( eqns_data%mLayerEnthalpyTrial ) - deallocate( eqns_data%mLayerEnthalpyPrev ) - deallocate( eqns_data%mLayerTempPrime ) - deallocate( eqns_data%mLayerMatricHeadPrime ) - deallocate( eqns_data%mLayerMatricHeadLiqPrime ) - deallocate( eqns_data%mLayerVolFracWatPrime ) - - deallocate( mLayerMatricHeadPrimePrev ) - deallocate( dCompress_dPsiPrev ) - deallocate( eqns_data%fluxVec ) - deallocate( eqns_data%resSink ) - deallocate( rootsfound ) - deallocate( rootdir ) - - call FIDAFree(ida_mem) - retval = FSUNLinSolFree(sunlinsol_LS) - if(retval /= 0)then; err=20; message='summaSolve4ida: unable to free the linear solver'; return; endif - call FSUNMatDestroy(sunmat_A) - call FN_VDestroy(sunvec_y) - call FN_VDestroy(sunvec_yp) - retval = FSUNContext_Free(sunctx) - if(retval /= 0)then; err=20; message='summaSolve4ida: unable to free the SUNDIALS context'; return; endif + + ! free memory + deallocate( eqns_data%model_decisions) + deallocate( eqns_data%sMul ) + deallocate( eqns_data%dMat ) + deallocate( eqns_data%dBaseflow_dMatric ) + deallocate( eqns_data%mLayerMatricHeadLiqTrial ) + deallocate( eqns_data%mLayerMatricHeadTrial ) + deallocate( eqns_data%mLayerMatricHeadPrev ) + deallocate( eqns_data%mLayerVolFracWatTrial ) + deallocate( eqns_data%mLayerVolFracWatPrev ) + deallocate( eqns_data%mLayerVolFracIceTrial ) + deallocate( eqns_data%mLayerTempPrev ) + deallocate( eqns_data%mLayerTempTrial ) + deallocate( eqns_data%mLayerVolFracIcePrev ) + deallocate( eqns_data%mLayerVolFracLiqPrev ) + deallocate( eqns_data%mLayerEnthalpyTrial ) + deallocate( eqns_data%mLayerEnthalpyPrev ) + deallocate( eqns_data%mLayerTempPrime ) + deallocate( eqns_data%mLayerMatricHeadPrime ) + deallocate( eqns_data%mLayerMatricHeadLiqPrime ) + deallocate( eqns_data%mLayerVolFracWatPrime ) + deallocate( dEnthalpy_dTk_unused ) + deallocate( dEnthalpy_dWat_unused ) + + deallocate( mLayerMatricHeadPrimePrev ) + deallocate( dCompress_dPsiPrev ) + deallocate( eqns_data%fluxVec ) + deallocate( eqns_data%resVec ) + deallocate( eqns_data%resSink ) + deallocate( rootsfound ) + deallocate( rootdir ) + + call FIDAFree(ida_mem) + retval = FSUNLinSolFree(sunlinsol_LS) + if(retval /= 0)then; err=20; message='summaSolve4ida: unable to free the linear solver'; return; endif + call FSUNMatDestroy(sunmat_A) + call FN_VDestroy(sunvec_y) + call FN_VDestroy(sunvec_yp) + retval = FSUNContext_Free(sunctx) + if(retval /= 0)then; err=20; message='summaSolve4ida: unable to free the SUNDIALS context'; return; endif + + end associate end subroutine summaSolve4ida diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index 17937059e..a4d1fde08 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -120,7 +120,10 @@ subroutine summaSolve4kinsol(& ! output ixSaturation, & ! intent(inout) index of the lowest saturated layer (NOTE: only computed on the first iteration) kinsolSucceeds, & ! intent(out): flag to indicate if KINSOL successfully solved the problem in current data step - stateVec, & ! intent(out): model state vector + stateVec, & ! intent(inout): model state vector + fluxVec, & ! intent(out): model flux vector + resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + resVec, & ! intent(out): residual vector err,message) ! intent(out): error control !======= Inclusions =========== @@ -136,7 +139,7 @@ subroutine summaSolve4kinsol(& USE fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver USE allocspace_module,only:allocLocal ! allocate local data structures - USE getVectorz_module, only:checkFeas ! check feasibility of state vector + USE getVectorz_module,only:checkFeas ! check feasibility of state vector USE eval8summa_module,only:eval8summa4kinsol ! DAE/ODE functions USE eval8summa_module,only:eval8summa ! residual of DAE USE computJacob_module,only:computJacob4kinsol ! system Jacobian @@ -182,6 +185,9 @@ subroutine summaSolve4kinsol(& ! output: state vectors integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer real(rkind),intent(inout) :: stateVec(:) ! model state vector (y) + real(rkind),intent(out) :: fluxVec(:) ! model flux vector (f) + real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation + real(qp),intent(out) :: resVec(:) ! residual vector logical(lgt),intent(out) :: kinsolSucceeds ! flag to indicate if KINSOL is successful ! output: error control integer(i4b),intent(out) :: err ! error code @@ -265,6 +271,7 @@ subroutine summaSolve4kinsol(& allocate(eqns_data%dBaseflow_dMatric(0,0),stat=err) end if allocate( eqns_data%fluxVec(nState) ) + allocate( eqns_data%resVec(nState) ) allocate( eqns_data%resSink(nState) ) retval = FSUNContext_Create(c_null_ptr, sunctx) @@ -375,6 +382,9 @@ subroutine summaSolve4kinsol(& ! copy to output data diag_data = eqns_data%diag_data flux_data = eqns_data%flux_data + fluxVec = eqns_data%fluxVec + resVec = eqns_data%resVec + resSink = eqns_data%resSink deriv_data = eqns_data%deriv_data ixSaturation = eqns_data%ixSaturation indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = eqns_data%indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) !only number of flux calculations changes in indx_data @@ -393,6 +403,7 @@ subroutine summaSolve4kinsol(& deallocate( eqns_data%stateVecPrev ) deallocate( eqns_data%dBaseflow_dMatric ) deallocate( eqns_data%fluxVec ) + deallocate( eqns_data%resVec ) deallocate( eqns_data%resSink ) call FKINFree(kinsol_mem) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 2f4b0e290..01b4f3b21 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -126,6 +126,8 @@ subroutine systemSolv(& firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution + checkMassBalance, & ! intent(in): flag to check mass balance + checkNrgBalance, & ! intent(in): flag to check energy balance ! input/output: data structures lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil @@ -144,7 +146,14 @@ subroutine systemSolv(& ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) stateVecTrial, & ! intent(out): updated state vector stateVecPrime, & ! intent(out): updated state vector if need the prime space (ida) + fluxVec, & ! intent(out): new flux vector + resSink, & ! intent(out): additional (sink) terms on the RHS of the state equa + resVec, & ! intent(out): new residual vector untappedMelt, & ! intent(out): un-tapped melt energy (J m-3 s-1) + ! output: balances (only computed at this level for ida) + balanceNrg, & ! intent(out): balance of energy per domain + balanceMass, & ! intent(out): balance of mass per domain + ! output: model control niter, & ! intent(out): number of iterations taken (numrec) nSteps, & ! intent(out): number of time steps taken in solver reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step @@ -152,19 +161,19 @@ subroutine systemSolv(& err,message) ! intent(out): error code and error message ! --------------------------------------------------------------------------------------- ! structure allocations - USE allocspace_module,only:allocLocal ! allocate local data structures + USE allocspace_module,only:allocLocal ! allocate local data structures ! state vector and solver - USE getVectorz_module,only:getScaling ! get the scaling vectors - USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy - USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy + USE getVectorz_module,only:getScaling ! get the scaling vectors + USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy + USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy #ifdef SUNDIALS_ACTIVE USE tol4ida_module,only:popTol4ida ! populate tolerances USE eval8summaWithPrime_module,only:eval8summaWithPrime ! get the fluxes and residuals USE summaSolve4ida_module,only:summaSolve4ida ! solve DAE by IDA USE summaSolve4kinsol_module,only:summaSolve4kinsol ! solve DAE by KINSOL #endif - USE eval8summa_module,only:eval8summa ! get the fluxes and residuals - USE summaSolve4numrec_module,only:summaSolve4numrec ! solve DAE by numerical recipes + USE eval8summa_module,only:eval8summa ! get the fluxes and residuals + USE summaSolve4numrec_module,only:summaSolve4numrec ! solve DAE by numerical recipes implicit none ! --------------------------------------------------------------------------------------- @@ -180,6 +189,8 @@ subroutine systemSolv(& logical(lgt),intent(inout) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + logical(lgt),intent(in) :: checkMassBalance ! flag to check mass balance + logical(lgt),intent(in) :: checkNrgBalance ! flag to check energy balance ! input/output: data structures type(zLookup),intent(in) :: lookup_data ! lookup tables type(var_i),intent(in) :: type_data ! type of vegetation and soil @@ -193,16 +204,23 @@ subroutine systemSolv(& type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin type(model_options),intent(in) :: model_decisions(:) ! model decisions real(rkind),intent(in) :: stateVecInit(:) ! initial state vector (mixed units) - ! output: model control + ! output type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) real(rkind),intent(out) :: stateVecTrial(:) ! trial state vector (mixed units) real(rkind),intent(out) :: stateVecPrime(:) ! trial state vector (mixed units) + real(rkind),intent(out) :: fluxVec(nState) ! flux vector (mixed units) + real(rkind),intent(out) :: resSink(nState) ! additional terms in the residual vector numrec + real(qp),intent(out) :: resVec(nState) ! NOTE: qp ! residual vector + real(rkind),intent(out) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) + ! output: balances (only computed at this level for ida) + real(rkind),intent(out) :: balanceNrg(4) ! balance of energy per domain + real(rkind),intent(out) :: balanceMass(4) ! balance of mass per domain + ! output: model control + integer(i4b),intent(out) :: niter ! number of iterations taken + integer(i4b),intent(out) :: nSteps ! number of time steps taken in solver logical(lgt),intent(out) :: reduceCoupledStep ! flag to reduce the length of the coupled step logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that there was too much melt - integer(i4b),intent(out) :: nSteps ! number of time steps taken in solver - integer(i4b),intent(out) :: niter ! number of iterations taken integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! --------------------------------------------------------------------------------------- @@ -229,7 +247,6 @@ subroutine systemSolv(& real(rkind) :: fluxVec0(nState) ! flux vector (mixed units) real(rkind) :: dMat(nState) ! diagonal matrix (excludes flux derivatives) real(qp) :: sMul(nState) ! NOTE: qp ! multiplier for state vector for the residual calculations - real(qp) :: rVec(nState) ! NOTE: qp ! residual vector real(rkind) :: rAdd(nState) ! additional terms in the residual vector logical(lgt) :: feasible ! feasibility flag logical(lgt) :: sunSucceeds ! flag to indicate if SUNDIALS successfully solved the problem in current data step @@ -241,22 +258,20 @@ subroutine systemSolv(& ! kinsol and numrec variables real(rkind) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) real(rkind) :: xScale(nState) ! characteristic scale of the state vector (mixed units) - ! numrec variables + real(qp) :: resVecNew(nState) ! NOTE: qp ! new residual vector numrec + ! numrec variables real(rkind) :: fOld,fNew ! function values (-); NOTE: dimensionless because scaled numrec real(rkind) :: xMin,xMax ! state minimum and maximum (mixed units) numrec integer(i4b) :: maxiter ! maximum number of iterations numrec integer(i4b) :: localMaxIter ! maximum number of iterations (depends on solution type) numrec integer(i4b), parameter :: scalarMaxIter=100 ! maximum number of iterations for the scalar solution numrec logical(lgt) :: converged ! convergence flag numrec - real(rkind) :: resSinkNew(nState) ! additional terms in the residual vector numrec - real(rkind) :: fluxVecNew(nState) ! new flux vector numrec - real(qp) :: resVecNew(nState) ! NOTE: qp ! new residual vector numrec logical(lgt), parameter :: post_massCons=.false. ! “perfectly” conserve mass by pushing the errors into the states, turn off for now to agree with SUNDIALS ! enthalpy derivatives real(rkind) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state - real(rkind) :: dEnthalpy_dTk(nLayers) ! derivatives in layer enthalpy w.r.t. temperature - real(rkind) :: dEnthalpy_dWat(nLayers) ! derivatives in layer enthalpy w.r.t. water state + real(rkind) :: dEnthalpy_dTk(nLayers) ! derivatives in layer enthalpy w.r.t. temperature + real(rkind) :: dEnthalpy_dWat(nLayers) ! derivatives in layer enthalpy w.r.t. water state ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- @@ -304,6 +319,13 @@ subroutine systemSolv(& err=0; message="systemSolv/" nSteps = 0 ! initialize number of time steps taken in solver + ! initialize the residual parts and balances + fluxVec = 0._rkind + resSink = 0._rkind + resVec = 0._rkind + balanceNrg = 0._rkind + balanceMass = 0._rkind + ! ***** ! (0) PRELIMINARIES... ! ******************** @@ -456,7 +478,7 @@ subroutine systemSolv(& feasible, & ! intent(out): flag to denote the feasibility of the solution fluxVec0, & ! intent(out): flux vector rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector + resVec, & ! intent(out): residual vector fOld, & ! intent(out): function evaluation err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) @@ -529,6 +551,8 @@ subroutine systemSolv(& firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution + checkMassBalance, & ! intent(in): flag to check mass balance + checkNrgBalance, & ! intent(in): flag to check energy balance ! input: state vector stateVecTrial, & ! intent(in): model state vector at the beginning of the data time step sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) @@ -556,6 +580,8 @@ subroutine systemSolv(& nSteps, & ! intent(out): number of time steps taken in solver stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step + balanceNrg, & ! intent(out): balance of energy per domain + balanceMass, & ! intent(out): balance of mass per domain err,cmessage) ! intent(out): error control ! check if IDA is successful, only fail outright in the case of a non-recoverable error if( .not.sunSucceeds )then @@ -627,6 +653,9 @@ subroutine systemSolv(& ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) sunSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step + fluxVec, & ! intent(out): new flux vector + resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + resVec, & ! intent(out): new residual vector err,cmessage) ! intent(out): error control ! check if KINSOL is successful, only fail outright in the case of a non-recoverable error if( .not.sunSucceeds )then @@ -677,7 +706,7 @@ subroutine systemSolv(& xMin,xMax, & ! intent(inout): state maximum and minimum fScale, & ! intent(in): characteristic scale of the function evaluations xScale, & ! intent(in): characteristic scale of the state vector - rVec, & ! intent(in): residual vector + resVec, & ! intent(in): residual vector sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout): diagonal matrix (excludes flux derivatives) fOld, & ! intent(in): old function evaluation @@ -700,8 +729,8 @@ subroutine systemSolv(& dBaseflow_dMatric, & ! intent(inout): derivative in baseflow w.r.t. matric head (s-1) ! output stateVecNew, & ! intent(out): new state vector - fluxVecNew, & ! intent(out): new flux vector - resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equa + fluxVec, & ! intent(out): new flux vector + resSink, & ! intent(out): additional (sink) terms on the RHS of the state equa resVecNew, & ! intent(out): new residual vector fNew, & ! intent(out): new function evaluation converged, & ! intent(out): convergence flag @@ -710,7 +739,7 @@ subroutine systemSolv(& ! save the computed functions, residuals, and solution fOld = fNew - rVec = resVecNew + resVec = resVecNew stateVecTrial = stateVecNew stateVecPrime = stateVecTrial !prime values not used here, dummy nSteps = 1 ! number of time steps taken in solver @@ -725,7 +754,7 @@ subroutine systemSolv(& endif end do ! iterating - + ! ----- ! * update states... ! Post processing step to “perfectly” conserve mass by pushing the errors into the state variables @@ -743,8 +772,9 @@ subroutine systemSolv(& ! update temperatures (ensure new temperature is consistent with the fluxes) if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - iState = ixSnowSoilNrg(iLayer) - stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState))/real(sMul(iState), rkind) + iState = ixSnowSoilNrg(iLayer) + stateVecTrial(iState) = stateVecInit(iState) + (fluxVec(iState)*dt_cur + resSink(iState))/real(sMul(iState), rkind) + resVec(iState) = 0._qp end do ! looping through non-missing energy state variables in the snow+soil domain endif @@ -752,8 +782,9 @@ subroutine systemSolv(& ! NOTE: for soil water balance is constrained within the iteration loop if(nSnowSoilHyd>0)then do concurrent (iLayer=1:nSnow,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing water state variables in the snow domain) - iState = ixSnowSoilHyd(iLayer) - stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState)) + iState = ixSnowSoilHyd(iLayer) + stateVecTrial(iState) = stateVecInit(iState) + (fluxVec(iState)*dt_cur + resSink(iState)) + resVec(iState) = 0._qp end do ! looping through non-missing water state variables in the soil domain endif end associate layerVars diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index 10ef07dd3..49107b203 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -606,7 +606,7 @@ subroutine t2enthalpy_addphase(& type(var_ilength),intent(in) :: indx_data ! model indices type(zLookup),intent(in) :: lookup_data ! lookup tables ! input: state variables for the vegetation canopy - real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) ! input: variables for the snow-soil domain real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 064b701d9..95e96dc46 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -111,6 +111,9 @@ subroutine varSubstep(& flux_mean, & ! intent(inout) : mean model fluxes for a local HRU deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables bvar_data, & ! intent(in) : model variables for the local basin + ! input/output: balances + sumBalanceNrg, & ! intent(inout) : step balance of energy per domain + sumBalanceMass, & ! intent(inout) : step balance of mass per domain ! output: model control out_varSubstep) ! intent(out) : model control ! --------------------------------------------------------------------------------------- @@ -143,7 +146,9 @@ subroutine varSubstep(& type(var_dlength),intent(inout) :: flux_mean ! mean model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin - ! output: model control + real(rkind),intent(inout) :: sumBalanceNrg(:) ! step balance of energy per domain + real(rkind),intent(inout) :: sumBalanceMass(:) ! step balance of mass per domain + ! output: model control type(out_type_varSubstep),intent(out) :: out_varSubstep ! model control ! --------------------------------------------------------------------------------------- ! * general local variables @@ -154,7 +159,7 @@ subroutine varSubstep(& integer(i4b) :: iVar ! index of variables in data structures integer(i4b) :: iSoil ! index of soil layers integer(i4b) :: ixLayer ! index in a given domain - integer(i4b), dimension(1) :: ixMin,ixMax ! bounds of a given flux vector + integer(i4b),dimension(1) :: ixMin,ixMax ! bounds of a given flux vector ! time stepping real(rkind) :: dtSum ! sum of time from successful steps (seconds) real(rkind) :: dt_wght ! weight given to a given flux calculation @@ -176,8 +181,6 @@ subroutine varSubstep(& type(var_dlength) :: flux_temp ! temporary model fluxes ! flags logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt) :: checkMassBalance ! flag to check the mass balance - logical(lgt) :: checkNrgBalance ! flag to check the energy balance logical(lgt) :: waterBalanceError ! flag to denote that there is a water balance error logical(lgt) :: nrgFluxModified ! flag to denote that the energy fluxes were modified ! energy fluxes @@ -186,6 +189,15 @@ subroutine varSubstep(& real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) real(rkind) :: sumSoilCompress ! sum of total soil compression real(rkind),allocatable :: sumLayerCompress(:) ! sum of soil compression by layer + ! balances and residual vectors + real(rkind) :: fluxVec(in_varSubstep % nSubset) ! flux vector (mixed units) + real(rkind) :: resSink(in_varSubstep % nSubset) ! sink terms on the RHS of the state equation + real(qp) :: resVec(in_varSubstep % nSubset) ! residual vector + real(rkind),dimension(4) :: balanceNrg ! substep balance of energy per domain + real(rkind),dimension(4) :: balanceMass ! substep balance of mass per domain + logical(lgt),parameter :: checkMassBalance = .true. ! flag to check the mass balance + logical(lgt),parameter :: checkNrgBalance = .true. ! flag to check the energy balance + ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- @@ -240,7 +252,11 @@ subroutine varSubstep(& message => out_varSubstep % cmessage & ! intent(out): error message ) ! end association with variables in the data structures ! ********************************************************************************************************************************************************* - + + ! initialize balances + balanceNrg = 0._rkind + balanceMass = 0._rkind + ! initialize error control err=0; message='varSubstep/' @@ -316,6 +332,8 @@ subroutine varSubstep(& firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution + checkMassBalance, & ! intent(in): flag to check mass balance + checkNrgBalance, & ! intent(in): flag to check energy balance ! input/output: data structures lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil @@ -334,7 +352,14 @@ subroutine varSubstep(& ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) stateVecTrial, & ! intent(out): updated state vector stateVecPrime, & ! intent(out): updated state vector if need the prime space (ida) + fluxVec, & ! intent(out): model flux vector + resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + resVec, & ! intent(out): residual vector untappedMelt, & ! intent(out): un-tapped melt energy (J m-3 s-1) + ! output: balances (only computed at this level for ida) + balanceNrg, & ! intent(out): balance of energy per domain + balanceMass, & ! intent(out): balance of mass per domain + ! output model control niter, & ! intent(out): number of iterations taken (numrec) nSteps, & ! intent(out): number of time steps taken in solver reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step @@ -402,19 +427,10 @@ subroutine varSubstep(& return endif - ! identify the need to check the mass balance - select case(ixNumericalMethod) - case(ida); checkMassBalance = .false. ! IDA balance agreement levels are controlled by set tolerances (maybe kinsol should be false too) - case(kinsol, numrec); checkMassBalance = .true. ! (.not.scalarSolution) - end select - - ! identify the need to check the energy balance, DOES NOT WORK YET and only check if ixHowHeatCap == enthalpyFD - checkNrgBalance = .false. - - ! update prognostic variables + ! update prognostic variables, update balances, and check them for possible step reduction if numrec or kinsol call updateProg(dtSubstep,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control model_decisions,lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures - waterBalanceError,nrgFluxModified,err,cmessage) ! output: flags and error control + fluxVec,resVec,balanceNrg,balanceMass,waterBalanceError,nrgFluxModified,err,message) ! output: balances, flags, and error control if(err/=0)then message=trim(message)//trim(cmessage) if(err>0) return @@ -506,6 +522,9 @@ subroutine varSubstep(& endif ! (if the flux is desired) end do ! (loop through fluxes) + ! If did not compute the layer residual vector on this step, added balance + sumBalanceNrg = sumBalanceNrg + balanceNrg + sumBalanceMass = sumBalanceMass + balanceMass ! increment the number of substeps nSubsteps = nSubsteps + nSteps @@ -558,13 +577,14 @@ end subroutine varSubstep ! ********************************************************************************************************** subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control model_decisions,lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures - waterBalanceError,nrgFluxModified,err,message) ! output: flags and error control + fluxVec,resVec,balanceNrg,balanceMass,waterBalanceError,nrgFluxModified,err,message) ! output: balances, flags, and error control USE getVectorz_module,only:varExtract ! extract variables from the state vector #ifdef SUNDIALS_ACTIVE - USE updateVarsWithPrime_module,only:updateVarsWithPrime ! update prognostic variables + USE updateVarsWithPrime_module,only:updateVarsWithPrime ! update prognostic variables #endif USE updateVars_module,only:updateVars ! update prognostic variables - USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy + USE t2enthalpy_module,only:t2enthalpy ! compute enthalpy + USE t2enthalpy_module,only:t2enthalpy_addphase ! add phase to enthalpy implicit none ! model control real(rkind) ,intent(in) :: dt ! time step (s) @@ -587,13 +607,18 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! flags and error control + ! balances, flags, and error control + real(rkind) ,intent(in) :: fluxVec(:) ! flux vector (mixed units) + real(qp) ,intent(in) :: resVec(:) ! NOTE: qp ! residual vector + real(rkind) ,intent(inout) :: balanceNrg(4) ! balance of energy per domain + real(rkind) ,intent(inout) :: balanceMass(4) ! balance of mass per domain logical(lgt) ,intent(out) :: waterBalanceError ! flag to denote that there is a water balance error logical(lgt) ,intent(out) :: nrgFluxModified ! flag to denote that the energy fluxes were modified integer(i4b) ,intent(out) :: err ! error code character(*) ,intent(out) :: message ! error message ! ================================================================================================================== ! general + integer(i4b) :: i ! indices integer(i4b) :: iState ! index of model state variable integer(i4b) :: ixSubset ! index within the state subset integer(i4b) :: ixFullVector ! index within full state vector @@ -644,10 +669,10 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe real(rkind) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! enthalpy of snow + soil (J m-3) ! enthalpy derivatives - real(rkind) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature - real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state - real(rkind),dimension(nLayers) :: dEnthalpy_dTk ! derivatives in layer enthalpy w.r.t. temperature - real(rkind),dimension(nLayers) :: dEnthalpy_dWat ! derivatives in layer enthalpy w.r.t. water state + real(rkind) :: dCanEnthalpy_dTk_unused ! will not be used, derivatives in canopy enthalpy w.r.t. temperature + real(rkind) :: dCanEnthalpy_dWat_unused ! will not be used, derivatives in canopy enthalpy w.r.t. water state + real(rkind),dimension(nLayers) :: dEnthalpy_dTk_unused ! will not be used, derivatives in layer enthalpy w.r.t. temperature + real(rkind),dimension(nLayers) :: dEnthalpy_dWat_unused ! will not be used, derivatives in layer enthalpy w.r.t. water state ! ------------------------------------------------------------------------------------------------------------------- ! ------------------------------------------------------------------------------------------------------------------- @@ -655,9 +680,19 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe associate(& ! model decisions ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver - ! get indices for mass balance + ! get indices for balances + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in) : [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in) : [i4b] index of canopy energy state variable ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in) : [i4b] index of canopy hydrology state variable (mass) + ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in) : [i4b] index of upper-most energy state in the snow+soil subdomain + ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in) : [i4b] index of upper-most hydrology state in the snow+soil subdomain + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in) : [i4b] index of water storage in the aquifer ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the soil domain + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in) : [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in) : [i4b] number of hydrology state variables in the snow+soil domain + layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in) : [i4b(:)] named variables defining the type of layer in snow+soil domain ! get indices for the un-tapped melt ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in) : [i4b(:)] list of indices for all energy states ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in) : [i4b(:)] indices defining the domain of the state (iname_veg, iname_snow, iname_soil) @@ -722,16 +757,19 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! initialize to state variable from the last update scalarCanairTempTrial = scalarCanairTemp + scalarCanairEnthalpyTrial = scalarCanairEnthalpy scalarCanopyTempTrial = scalarCanopyTemp scalarCanopyWatTrial = scalarCanopyWat scalarCanopyLiqTrial = scalarCanopyLiq scalarCanopyIceTrial = scalarCanopyIce + scalarCanopyEnthalpyTrial = scalarCanopyEnthalpy mLayerTempTrial = mLayerTemp mLayerVolFracWatTrial = mLayerVolFracWat mLayerVolFracLiqTrial = mLayerVolFracLiq mLayerVolFracIceTrial = mLayerVolFracIce mLayerMatricHeadTrial = mLayerMatricHead mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq + mLayerEnthalpyTrial = mLayerEnthalpy scalarAquiferStorageTrial = scalarAquiferStorage ! extract states from the state vector @@ -875,7 +913,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! ---- ! * check energy balance !------------------------ - if(checkNrgBalance)then + if(checkNrgBalance)then ! update diagnostic enthalpy variables ! compute enthalpy at t_{n+1} call t2enthalpy(& ! input: data structures @@ -897,10 +935,10 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) - dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature - dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state - dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature - dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state + dCanEnthalpy_dTk_unused, & ! intent(out): will not be used, derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat_unused, & ! intent(out): will not be used, derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk_unused, & ! intent(out): will not be used, derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat_unused, & ! intent(out): will not be used, derivatives in layer enthalpy w.r.t. water state ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -923,7 +961,25 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! output: error control err,message) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - endif + + ! compute energy balance if didn't do inside solver substeps + select case(ixNumericalMethod) + case(ida); ! do nothing + case(kinsol, numrec) + ! compute energy balance, maybe should use to check for step reduction + if(ixCasNrg/=integerMissing) balanceNrg(1) = scalarCanairEnthalpyTrial - scalarCanairEnthalpy - fluxVec(ixVegNrg)*dt + if(ixVegNrg/=integerMissing) balanceNrg(2) = scalarCanopyEnthalpyTrial - scalarCanopyEnthalpy - fluxVec(ixVegNrg)*dt + if(nSnowSoilNrg>0)then + do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + select case(layerType(i)) + case(iname_snow); balanceNrg(3) = (mLayerEnthalpyTrial(i) - mLayerEnthalpy(i) - fluxVec(ixSnowSoilNrg(i))*dt)/nSnow + case(iname_soil); balanceNrg(4) = (mLayerEnthalpyTrial(i) - mLayerEnthalpy(i) - fluxVec(ixSnowSoilNrg(i))*dt)/(nLayers-nSnow) + end select + enddo + endif + end select + + endif ! if checking energy balance ! ----- ! * check mass balance... @@ -933,8 +989,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! Negative error code will mean step will be failed and retried with smaller step size if(checkMassBalance)then - ! check mass balance for the canopy - if(ixVegHyd/=integerMissing)then + if(ixVegHyd/=integerMissing)then ! check for complete drainage ! handle cases where fluxes empty the canopy fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage @@ -974,46 +1029,69 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe canopyBalance1 = canopyBalance0 + fluxNet*dt nrgFluxModified = .false. endif ! cases where fluxes empty the canopy + + endif ! check for complete drainage - ! check the mass balance - fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage - liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial - if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues - !write(*,'(a,1x,f20.10)') 'dt = ', dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial - !write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 - !write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 - !write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt - !write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt - !write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt - !write(*,'(a,1x,f20.10)') 'liqError = ', liqError - waterBalanceError = .true. - return - endif ! if there is a water balance error - endif ! if veg canopy - - ! check mass balance for soil, again already satisfied for numrec solver and not checked for ida and solver - if(count(ixSoilOnlyHyd/=integerMissing)==nSoil)then - soilBalance1 = sum( (mLayerVolFracLiqTrial(nSnow+1:nLayers) + mLayerVolFracIceTrial(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) - vertFlux = -(iLayerLiqFluxSoil(nSoil) - iLayerLiqFluxSoil(0))*dt ! m s-1 --> m - tranSink = sum(mLayerTranspire)*dt ! m s-1 --> m - baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m - compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) )*dt ! m s-1 --> m - liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) - if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues - !write(*,'(a,1x,f20.10)') 'dt = ', dt - !write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 - !write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 - !write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux - !write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink - !write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink - !write(*,'(a,1x,f20.10)') 'compSink = ', compSink - !write(*,'(a,1x,f20.10)') 'liqError = ', liqError - waterBalanceError = .true. - return - endif ! if there is a water balance error - endif ! if hydrology states exist in the soil domain + ! compute mass balance if didn't do inside solver substeps + select case(ixNumericalMethod) + case(ida); ! do nothing + case(kinsol, numrec) + ! old mass balance checks + if(ixVegHyd/=integerMissing)then + ! check the mass balance for the canopy for step reduction (ida and kinsol should have done this already unless modified canopy water above) + fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial + if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues + !write(*,'(a,1x,f20.10)') 'dt = ', dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + !write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 + !write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 + !write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt + !write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt + !write(*,'(a,1x,f20.10)') 'liqError = ', liqError + waterBalanceError = .true. + return + endif ! if there is a water balance error + endif ! if veg canopy + + ! check mass balance for soil domain for step reduction (ida and kinsol should have done this already + if(count(ixSoilOnlyHyd/=integerMissing)==nSoil)then + soilBalance1 = sum( (mLayerVolFracLiqTrial(nSnow+1:nLayers) + mLayerVolFracIceTrial(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) + vertFlux = -(iLayerLiqFluxSoil(nSoil) - iLayerLiqFluxSoil(0))*dt ! m s-1 --> m + tranSink = sum(mLayerTranspire)*dt ! m s-1 --> m + baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m + compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) )*dt ! m s-1 --> m + liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) + if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues + !write(*,'(a,1x,f20.10)') 'dt = ', dt + !write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 + !write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 + !write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux + !write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink + !write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink + !write(*,'(a,1x,f20.10)') 'compSink = ', compSink + !write(*,'(a,1x,f20.10)') 'liqError = ', liqError + waterBalanceError = .true. + return + endif ! if there is a water balance error + endif ! if hydrology states exist in the soil domain + + ! compute mass balance, maybe should use to check for step reduction + ! resVec is the residual vector from the solver over dt + if(ixVegHyd/=integerMissing) balanceMass(1) = balanceMass(1) + resVec(ixVegHyd) + if(nSnowSoilHyd>0)then + do concurrent (i=1:nLayers,ixSnowSoilHyd(i)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + select case(layerType(i)) + case(iname_snow); balanceMass(2) = balanceMass(2) + resVec(ixSnowSoilHyd(i))/nSnow + case(iname_soil); balanceMass(3) = balanceMass(3) + resVec(ixSnowSoilHyd(i))/(nLayers-nSnow) + end select + enddo + endif + if(ixAqWat/=integerMissing) balanceMass(4) = balanceMass(4) + resVec(ixAqWat)*dt + + end select endif ! if checking the mass balance ! ----- From 961792e880fdaeb6c61f0081901f2ce0ee5f8809 Mon Sep 17 00:00:00 2001 From: ashleymedin <34691332+ashleymedin@users.noreply.github.com> Date: Sun, 5 Nov 2023 21:15:19 +0900 Subject: [PATCH 0995/1472] Merge pull request #29 from seantrim/develop_object_oriented --- build/source/engine/opSplittin.f90 | 286 ++++++++++++++++------------- 1 file changed, 162 insertions(+), 124 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index d47881ae3..9e1990c86 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -243,6 +243,7 @@ subroutine opSplittin(& logical(lgt) :: firstSuccess ! flag to define the first success logical(lgt) :: firstFluxCall ! flag to define the first flux call logical(lgt) :: reduceCoupledStep ! flag to define the need to reduce the length of the coupled step + logical(lgt) :: return_flag ! flag to indicate the execution of a return statement type(var_dlength) :: prog_temp ! temporary model prognostic variables type(var_dlength) :: diag_temp ! temporary model diagnostic variables type(var_dlength) :: flux_temp ! temporary model fluxes @@ -326,8 +327,6 @@ subroutine opSplittin(& ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) ! numerix tracking numberStateSplit => indx_data%var(iLookINDEX%numberStateSplit )%dat(1) ,& ! intent(inout): [i4b] number of state splitting solutions (-) - numberDomainSplitNrg => indx_data%var(iLookINDEX%numberDomainSplitNrg )%dat(1) ,& ! intent(inout): [i4b] number of domain splitting solutions for energy (-) - numberDomainSplitMass => indx_data%var(iLookINDEX%numberDomainSplitMass)%dat(1) ,& ! intent(inout): [i4b] number of domain splitting solutions for mass (-) numberScalarSolutions => indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1) & ! intent(inout): [i4b] number of scalar solutions (-) ) ! --------------------------------------------------------------------------------------- @@ -394,51 +393,17 @@ subroutine opSplittin(& ! first try the state type split, then try the domain split within a given state type stateThenDomain: do ixStateThenDomain=1,1+tryDomainSplit ! 1=state type split; 2=domain split within a given state type - ! keep track of the number of domain splits - if(iStateTypeSplit==nrgSplit .and. ixStateThenDomain==subDomain) numberDomainSplitNrg = numberDomainSplitNrg + 1 - if(iStateTypeSplit==massSplit .and. ixStateThenDomain==subDomain) numberDomainSplitMass = numberDomainSplitMass + 1 - - ! define the number of domain splits for the state type - select case(ixStateThenDomain) - case(fullDomain); nDomainSplit=1 - case(subDomain); nDomainSplit=nDomains - case default; err=20; message=trim(message)//'coupling case not found'; return - end select - - ! check that we haven't split the domain when we are fully coupled - if(ixCoupling==fullyCoupled .and. nDomainSplit==nDomains)then - message=trim(message)//'cannot split domains when fully coupled' - err=20; return - endif - - mean_step_state = 0._rkind ! initialize mean step for state + call initialize_domainSplit_loop + if (return_flag.eqv..true.) return ! return if error occurs during initialization ! domain splitting loop domainSplit: do iDomainSplit=1,nDomainSplit ! trial with the vector then scalar solution solution: do ixSolution=1,nSolutions - mean_step_solution = 0._rkind ! initialize mean step for a solution - - ! initialize error control - err=0; message="opSplittin/" - ! refine the time step - if(ixSolution==scalar)then - dtInit = min(dtmin_split, dt) ! initial time step - dt_min = min(dtmin_scalar, dt) ! minimum time step - endif - - ! initialize the first flux call - firstFluxCall=.true. - if (.not.firstInnerStep) firstFluxCall=.false. - - ! get the number of split layers - select case(ixSolution) - case(vector); nStateSplit=1 - case(scalar); nStateSplit=count(stateMask) - case default; err=20; message=trim(message)//'unknown solution method'; return - end select + call initialize_stateSplit_loop + if (return_flag.eqv..true.) return ! return if error occurs during initialization ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) stateSplit: do iStateSplit=1,nStateSplit @@ -454,49 +419,22 @@ subroutine opSplittin(& if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! error control ! check that state variables exist - if(nSubset==0) cycle domainSplit + if (nSubset==0) cycle domainSplit ! avoid redundant case where vector solution is of length 1 - if(ixSolution==vector .and. count(stateMask)==1) cycle solution + if (ixSolution==vector .and. count(stateMask)==1) cycle solution ! check that we do not attempt the scalar solution for the fully coupled case - if(ixCoupling==fullyCoupled .and. ixSolution==scalar)then + if (ixCoupling==fullyCoupled .and. ixSolution==scalar) then message=trim(message)//'only apply the scalar solution to the fully split coupling strategy' err=20; return - endif + end if ! reset the flag for the first flux call - if(.not.firstSuccess) firstFluxCall=.true. - - ! save/recover copies of prognostic variables - do iVar=1,size(prog_data%var) - select case(failure) - case(.false.); prog_temp%var(iVar)%dat(:) = prog_data%var(iVar)%dat(:) - case(.true.); prog_data%var(iVar)%dat(:) = prog_temp%var(iVar)%dat(:) - end select - end do ! looping through variables + if (.not.firstSuccess) firstFluxCall=.true. - ! save/recover copies of diagnostic variables - do iVar=1,size(diag_data%var) - select case(failure) - case(.false.); diag_temp%var(iVar)%dat(:) = diag_data%var(iVar)%dat(:) - case(.true.); diag_data%var(iVar)%dat(:) = diag_temp%var(iVar)%dat(:) - end select - end do ! looping through variables - - ! save/recover copies of model fluxes and mean fluxes - do iVar=1,size(flux_data%var) - select case(failure) - case(.false.) - flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) - flux_mntemp%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) - addFirstFlux = .false. - case(.true.) - flux_data%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) - flux_mean%var(iVar)%dat(:) = flux_mntemp%var(iVar)%dat(:) - if(addFirstFlux) addFirstFlux = .true. - end select - end do ! looping through variables + ! save/recover copies of variables and fluxes + call save_recover ! ----- ! * assemble vectors for a given split... @@ -522,7 +460,7 @@ subroutine opSplittin(& ! -------------------------------------------- ! keep track of the number of scalar solutions - if(ixSolution==scalar) numberScalarSolutions = numberScalarSolutions + 1 + if (ixSolution==scalar) numberScalarSolutions = numberScalarSolutions + 1 ! solve variable subset for one full time step call initialize_varSubstep @@ -534,74 +472,41 @@ subroutine opSplittin(& call finalize_varSubstep if (err/=0) then; message=trim(message)//trim(cmessage); if (err>0) return; end if ! error control - ! reduce coupled step if failed the minimum step for the scalar solution - if(failedMinimumStep .and. ixSolution==scalar) reduceCoupledStep=.true. - - ! if too much melt (or some other need to reduce the coupled step) then return - ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry - if(tooMuchMelt .or. reduceCoupledStep)then - stepFailure=.true. - err=0 ! recovering - return - endif - - ! define failure - failure = (failedMinimumStep .or. err<0) - if(.not.failure) firstSuccess=.true. - - ! if failed, need to reset the flux counter - if(failure)then - do iVar=1,size(flux_meta) - iMin=lbound(flux_data%var(iVar)%dat) - iMax=ubound(flux_data%var(iVar)%dat) - do iLayer=iMin(1),iMax(1) - if(fluxMask%var(iVar)%dat(iLayer)) fluxCount%var(iVar)%dat(iLayer) = fluxCount%var(iVar)%dat(iLayer) - nSubsteps - end do - end do - endif + ! determine whether solution is a success or a failure + call judge_solution + if (return_flag.eqv..true.) return ! return for a recovering solution ! try the fully split solution if failed to converge with a minimum time step in the coupled solution - if(ixCoupling==fullyCoupled .and. failure) cycle coupling + if (ixCoupling==fullyCoupled .and. failure) cycle coupling ! try the scalar solution if failed to converge with a minimum time step in the split solution - if(ixCoupling/=fullyCoupled)then + if (ixCoupling/=fullyCoupled) then select case(ixStateThenDomain) - case(fullDomain); if(failure) cycle stateThenDomain - case(subDomain); if(failure) cycle solution + case(fullDomain); if (failure) cycle stateThenDomain + case(subDomain); if (failure) cycle solution case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' end select - endif + end if ! check that state variables updated where(stateMask) stateCheck = stateCheck+1 - if(any(stateCheck>1))then + if (any(stateCheck>1)) then message=trim(message)//'state variable updated more than once!' err=20; return - endif + end if ! success = exit solution - if(.not.failure)then + if (.not.failure) then ! sum the mean steps for the successful solution type mean_step_solution = mean_step_solution + (dt/nSubsteps)/nStateSplit select case(ixStateThenDomain) - case(fullDomain); if(iStateSplit==nStateSplit) exit stateThenDomain - case(subDomain); if(iStateSplit==nStateSplit) exit solution + case(fullDomain); if (iStateSplit==nStateSplit) exit stateThenDomain + case(subDomain); if (iStateSplit==nStateSplit) exit solution case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' end select - else - - ! check that we did not fail for the scalar solution (last resort) - if(ixSolution==scalar)then - message=trim(message)//'failed the minimum step for the scalar solution' - err=20; return - - ! check for an unexpected failure - else - message=trim(message)//'unexpected failure' - err=20; return - endif - - endif ! success check + else ! failure + call check_failure; return ! check reason for failure and return + end if ! success check end do stateSplit ! solution with split layers @@ -752,6 +657,63 @@ subroutine finalize_opSplittin if (ixCoupling/=fullyCoupled .or. nSubsteps>1) dtMultiplier=0.5_rkind end subroutine finalize_opSplittin + subroutine initialize_domainSplit_loop + ! *** initial operations to set up domainSplit loop *** + return_flag=.false. ! initialize flag + associate(numberDomainSplitNrg => indx_data%var(iLookINDEX%numberDomainSplitNrg )%dat(1),& ! intent(inout): [i4b] number of domain splitting solutions for energy (-) + numberDomainSplitMass => indx_data%var(iLookINDEX%numberDomainSplitMass)%dat(1) )! intent(inout): [i4b] number of domain splitting solutions for mass (-) + ! keep track of the number of domain splits + if (iStateTypeSplit==nrgSplit .and. ixStateThenDomain==subDomain) numberDomainSplitNrg = numberDomainSplitNrg + 1 + if (iStateTypeSplit==massSplit .and. ixStateThenDomain==subDomain) numberDomainSplitMass = numberDomainSplitMass + 1 + end associate + + ! define the number of domain splits for the state type + select case(ixStateThenDomain) + case(fullDomain); nDomainSplit=1 + case(subDomain); nDomainSplit=nDomains + case default; err=20; message=trim(message)//'coupling case not found'; + return_flag=.true. ! return statement required in opSplittin + return + end select + + ! check that we haven't split the domain when we are fully coupled + if (ixCoupling==fullyCoupled .and. nDomainSplit==nDomains) then + message=trim(message)//'cannot split domains when fully coupled' + return_flag=.true. ! return statement required in opSplittin + err=20; return + end if + + mean_step_state = 0._rkind ! initialize mean step for state + end subroutine initialize_domainSplit_loop + + subroutine initialize_stateSplit_loop + ! *** initial operations to set up stateSplit loop *** + return_flag=.false. ! initialize flag + mean_step_solution = 0._rkind ! initialize mean step for a solution + + ! initialize error control + err=0; message="opSplittin/" + + ! refine the time step + if (ixSolution==scalar) then + dtInit = min(dtmin_split, dt) ! initial time step + dt_min = min(dtmin_scalar, dt) ! minimum time step + end if + + ! initialize the first flux call + firstFluxCall=.true. + if (.not.firstInnerStep) firstFluxCall=.false. + + ! get the number of split layers + select case(ixSolution) + case(vector); nStateSplit=1 + case(scalar); nStateSplit=count(stateMask) + case default; err=20; message=trim(message)//'unknown solution method'; + return_flag=.true. ! return statement required in opSplittin + return + end select + end subroutine initialize_stateSplit_loop + ! **** stateFilter **** subroutine initialize_stateFilter call in_stateFilter % initialize(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) @@ -782,6 +744,82 @@ subroutine finalize_varSubstep call out_varSubstep % finalize(dtMultiplier,nSubsteps,failedMinimumStep,reduceCoupledStep,tooMuchMelt,err,cmessage) end subroutine finalize_varSubstep + + subroutine judge_solution + ! *** determine whether solution is a success or a failure *** + return_flag=.false. ! initialize flag + + ! reduce coupled step if failed the minimum step for the scalar solution + if (failedMinimumStep .and. ixSolution==scalar) reduceCoupledStep=.true. + + ! if too much melt (or some other need to reduce the coupled step) then return + ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry + if (tooMuchMelt .or. reduceCoupledStep) then + stepFailure=.true. + err=0 ! recovering + return_flag=.true. ! return statement required in opSplittin + return + end if + + ! define failure + failure = (failedMinimumStep .or. err<0) + if (.not.failure) firstSuccess=.true. + + ! if failed, need to reset the flux counter + if (failure) then + do iVar=1,size(flux_meta) + iMin=lbound(flux_data%var(iVar)%dat) + iMax=ubound(flux_data%var(iVar)%dat) + do iLayer=iMin(1),iMax(1) + if (fluxMask%var(iVar)%dat(iLayer)) fluxCount%var(iVar)%dat(iLayer) = fluxCount%var(iVar)%dat(iLayer) - nSubsteps + end do + end do + end if + end subroutine judge_solution + + subroutine save_recover + ! save/recover copies of prognostic variables + do iVar=1,size(prog_data%var) + select case(failure) + case(.false.); prog_temp%var(iVar)%dat(:) = prog_data%var(iVar)%dat(:) + case(.true.); prog_data%var(iVar)%dat(:) = prog_temp%var(iVar)%dat(:) + end select + end do + + ! save/recover copies of diagnostic variables + do iVar=1,size(diag_data%var) + select case(failure) + case(.false.); diag_temp%var(iVar)%dat(:) = diag_data%var(iVar)%dat(:) + case(.true.); diag_data%var(iVar)%dat(:) = diag_temp%var(iVar)%dat(:) + end select + end do + + ! save/recover copies of model fluxes and mean fluxes + do iVar=1,size(flux_data%var) + select case(failure) + case(.false.) + flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_mntemp%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + addFirstFlux = .false. + case(.true.) + flux_data%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) + flux_mean%var(iVar)%dat(:) = flux_mntemp%var(iVar)%dat(:) + if (addFirstFlux) addFirstFlux = .true. + end select + end do + end subroutine save_recover + + subroutine check_failure + ! *** Analyze reason for failure *** + if (ixSolution==scalar) then ! check that we did not fail for the scalar solution (last resort) + message=trim(message)//'failed the minimum step for the scalar solution' + err=20; return + else ! check for an unexpected failure + message=trim(message)//'unexpected failure' + err=20; return + end if + end subroutine check_failure + subroutine update_fluxMask ! *** update the fluxMask data structure *** do iVar=1,size(flux_meta) ! loop through flux variables From d2e730b96f671b09fba7e1a0be14e11989fb12b1 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 16 Nov 2023 00:26:38 +0900 Subject: [PATCH 0996/1472] rVec has to be part of sundials structure, not user data --- build/source/engine/eval8summa.f90 | 6 +++--- build/source/engine/eval8summaWithPrime.f90 | 4 +++- build/source/engine/summaSolve4kinsol.f90 | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index b4095c793..41b0ad78d 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -728,10 +728,9 @@ integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & ! pointers to data in SUNDIALS vectors type(data4kinsol), pointer :: eqns_data ! equations data real(rkind), pointer :: stateVec(:) ! solution vector + real(rkind), pointer :: rVec(:) ! residual vector logical(lgt) :: feasible ! feasibility of state vector real(rkind) :: fNew ! function values, not needed here - integer(i4b) :: err ! error in imposeConstraints - character(len=256) :: message ! error message of downwind routine !======= Internals ============ ! get equations data from user-defined data @@ -739,6 +738,7 @@ integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & ! get data arrays from SUNDIALS vectors stateVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_y) + rVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_r) ! increment the proposed iteration for simple error control if needed if (eqns_data%firstStateiteration) then @@ -790,7 +790,7 @@ integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & feasible, & ! intent(out): flag to denote the feasibility of the solution always true inside SUNDIALS eqns_data%fluxVec, & ! intent(out): flux vector eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - eqns_data%resVec, & ! intent(out): residual vector + rVec, & ! intent(out): residual vector fNew, & ! intent(out): new function evaluation eqns_data%err,eqns_data%message) ! intent(out): error control if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 15999b3b6..be5dd4389 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -781,6 +781,7 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user type(data4ida), pointer :: eqns_data ! equations data real(rkind), pointer :: stateVec(:) ! solution vector real(rkind), pointer :: stateVecPrime(:) ! derivative vector + real(rkind), pointer :: rVec(:) ! residual vector logical(lgt) :: feasible ! feasibility of state vector !======= Internals ============ @@ -790,6 +791,7 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user ! get data arrays from SUNDIALS vectors stateVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_y) stateVecPrime(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_yp) + rVec(1:eqns_data%nState) => FN_VGetArrayPointer(sunvec_r) ! compute the flux and the residual vector for a given state vector call eval8summaWithPrime(& @@ -862,7 +864,7 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user feasible, & ! intent(out): flag to denote the feasibility of the solution always true inside SUNDIALS eqns_data%fluxVec, & ! intent(out): flux vector eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - eqns_data%resVec, & ! intent(out): residual vector + rVec, & ! intent(out): residual vector eqns_data%err,eqns_data%message) ! intent(out): error control if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index a4d1fde08..7fd6d8832 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -186,7 +186,7 @@ subroutine summaSolve4kinsol(& integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer real(rkind),intent(inout) :: stateVec(:) ! model state vector (y) real(rkind),intent(out) :: fluxVec(:) ! model flux vector (f) - real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation + real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation real(qp),intent(out) :: resVec(:) ! residual vector logical(lgt),intent(out) :: kinsolSucceeds ! flag to indicate if KINSOL is successful ! output: error control From 6ec3844945718a7cdc93082f0a3f4c7e2b9c9950 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 16 Nov 2023 01:13:01 +0900 Subject: [PATCH 0997/1472] ida closed form works now --- build/source/engine/eval8summa.f90 | 5 ++++- build/source/engine/eval8summaWithPrime.f90 | 5 +++-- build/source/engine/summaSolve4ida.f90 | 1 - build/source/engine/summaSolve4kinsol.f90 | 1 - 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 41b0ad78d..1d43847ae 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -731,6 +731,8 @@ integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & real(rkind), pointer :: rVec(:) ! residual vector logical(lgt) :: feasible ! feasibility of state vector real(rkind) :: fNew ! function values, not needed here + integer(i4b) :: err ! error in imposeConstraints + character(len=256) :: message ! error message of downwind routine !======= Internals ============ ! get equations data from user-defined data @@ -796,7 +798,8 @@ integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif - ! return success + ! save residual and return success + eqns_data%resVec = rVec ierr = 0 return diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index be5dd4389..e0ec5b1c7 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -868,8 +868,9 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%err,eqns_data%message) ! intent(out): error control if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif - - ! return success + + ! save residual and return success + eqns_data%resVec = rVec ierr = 0 return diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index ad3d5dba5..9d4b05e20 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -243,7 +243,6 @@ subroutine summaSolve4ida( & real(qp) :: dt_diff ! difference from previous timestep integer(c_long) :: mu, lu ! in banded matrix mode in SUNDIALS type integer(c_long) :: nState ! total number of state variables in SUNDIALS type - real(rkind) :: rVec(nStat) ! residual vector integer(i4b) :: iVar, i ! indices integer(i4b) :: nRoot ! total number of roots (events) to find real(qp) :: tret(1) ! time in data window diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index 7fd6d8832..a2ef944a1 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -208,7 +208,6 @@ subroutine summaSolve4kinsol(& logical(lgt) :: feasible ! feasibility flag integer(c_long) :: mu, lu ! in banded matrix mode in SUNDIALS type integer(c_long) :: nState ! total number of state variables in SUNDIALS type - real(rkind) :: rVec(nStat) ! residual vector integer(i4b) :: iVar, i ! indices character(LEN=256) :: cmessage ! error message of downwind routine logical(lgt) :: use_fdJac ! flag to use finite difference Jacobian, controlled by decision fDerivMeth From fcee13bacf0ef6162d95924d9beaf2689cc704d7 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 20 Nov 2023 22:51:34 +0900 Subject: [PATCH 0998/1472] raise the cutoff for not using delE/delT to 1e-2 from 1e-14. It's very unstable. --- build/source/dshare/get_ixname.f90 | 2 +- build/source/dshare/popMetadat.f90 | 2 +- build/source/engine/computHeatCap.f90 | 78 ++++++++++----------- build/source/engine/coupled_em.f90 | 18 ++--- build/source/engine/eval8summa.f90 | 15 ++-- build/source/engine/eval8summaWithPrime.f90 | 13 ++-- build/source/engine/summaSolve4ida.f90 | 16 ++--- build/source/engine/t2enthalpy.f90 | 53 ++++++-------- build/source/engine/varSubstep.f90 | 63 +++++++++++------ 9 files changed, 134 insertions(+), 126 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index e2fa24370..03da03975 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -587,7 +587,7 @@ function get_ixdiag(varName) case('wallClockTime' ); get_ixdiag = iLookDIAG%wallClockTime ! wall clock time (s) case('meanStepSize' ); get_ixdiag = iLookDIAG%meanStepSize ! mean time step size (s) over data window ! balances - case('balanceCasNrg' ); get_ixdiag = iLookDIAG%balanceCasNrg ! balance of energy in the canopy + case('balanceCasNrg' ); get_ixdiag = iLookDIAG%balanceCasNrg ! balance of energy in the canopy air space case('balanceVegNrg' ); get_ixdiag = iLookDIAG%balanceVegNrg ! balance of energy in the vegetation case('balanceSnowNrg' ); get_ixdiag = iLookDIAG%balanceSnowNrg ! balance of energy in the snow case('balanceSoilNrg' ); get_ixdiag = iLookDIAG%balanceSoilNrg ! balance of energy in the soil diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index e28bcedd2..db5c43c23 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -442,7 +442,7 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%wallClockTime) = var_info('wallClockTime' , 'wall clock time' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%meanStepSize) = var_info('meanStepSize' , 'mean time step size over data window' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! balances - diag_meta(iLookDIAG%balanceCasNrg) = var_info('balanceCasNrg' , 'balance of energy in the canopy' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceCasNrg) = var_info('balanceCasNrg' , 'balance of energy in the canopy air space' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%balanceVegNrg) = var_info('balanceVegNrg' , 'balance of energy in the vegetation' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%balanceSnowNrg) = var_info('balanceSnowNrg' , 'balance of energy in the snow' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%balanceSoilNrg) = var_info('balanceSoilNrg' , 'balance of energy in the soil' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index 6e157a9af..507093c2b 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -80,45 +80,45 @@ module computHeatCap_module ! ********************************************************************************************************** subroutine computHeatCap(& ! input: control variables - nLayers, & ! intent(in): number of layers (soil+snow) - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) + nLayers, & ! intent(in): number of layers (soil+snow) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) ! input output data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! input: state variables - scalarCanopyIce, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiquid, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) - scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial temperature - mLayerTempPrev, & ! intent(in): previous temperature - mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil - mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil - mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) + scalarCanopyIce, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiquid, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial temperature + mLayerTempPrev, & ! intent(in): previous temperature + mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil + mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil + mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - dCanEnthalpy_dTk, & ! intent(in): derivatives in canopy enthalpy w.r.t. temperature - dCanEnthalpy_dWat, & ! intent(in): derivatives in canopy enthalpy w.r.t. water state - dEnthalpy_dTk, & ! intent(in): derivatives in layer enthalpy w.r.t. temperature - dEnthalpy_dWat, & ! intent(in): derivatives in layer enthalpy w.r.t. water state + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + dCanEnthalpy_dTk, & ! intent(in): derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat, & ! intent(in): derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk, & ! intent(in): derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat, & ! intent(in): derivatives in layer enthalpy w.r.t. water state ! output - heatCapVeg, & ! intent(out): heat capacity for canopy - mLayerHeatCap, & ! intent(out): heat capacity for snow and soil - dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + heatCapVeg, & ! intent(out): heat capacity for canopy + mLayerHeatCap, & ! intent(out): heat capacity for snow and soil + dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature ! output: error control err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------------- @@ -198,7 +198,7 @@ subroutine computHeatCap(& ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) if(computeVegFlux)then delT = scalarCanopyTempTrial - scalarCanopyTempPrev - if(abs(delT) <= 1e-14_rkind)then + if(abs(delT) <= 1e-2_rkind)then heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component Cp_ice*scalarCanopyIce/canopyDepth ! ice component @@ -224,7 +224,7 @@ subroutine computHeatCap(& ! loop through layers do iLayer=1,nLayers delT = mLayerTempTrial(iLayer) - mLayerTempPrev(iLayer) - if(abs(delT) <= 1e-14_rkind)then + if(abs(delT) <= 1e-2_rkind)then ! get the soil layer if(iLayer>nSnow) iSoil = iLayer-nSnow select case(layerType(iLayer)) @@ -296,8 +296,8 @@ subroutine computStatMult(& USE f2008funcs_module,only:findIndex ! finds the index of the first value within a vector ! -------------------------------------------------------------------------------------------------------------------------------- ! input: data structures - real(qp),intent(out) :: heatCapVeg - real(qp),intent(out) :: mLayerHeatCap(:) + real(qp),intent(in) :: heatCapVeg ! volumetric heat capacity of vegetation (J m-3 K-1) + real(qp),intent(in) :: mLayerHeatCap(:) ! volumetric heat capacity of snow and soil (J m-3 K-1) type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers ! output: state vectors @@ -347,8 +347,8 @@ subroutine computStatMult(& where(ixStateType_subset==iname_nrgCanair) sMul = Cp_air*iden_air ! volumetric heat capacity of air (J m-3 K-1) where(ixStateType_subset==iname_nrgCanopy) sMul = heatCapVeg ! volumetric heat capacity of the vegetation (J m-3 K-1) - where(ixStateType_subset==iname_watCanopy) sMul = 1._rkind ! nothing else on the left hand side - where(ixStateType_subset==iname_liqCanopy) sMul = 1._rkind ! nothing else on the left hand side + where(ixStateType_subset==iname_watCanopy) sMul = 1._rkind ! nothing else on the left hand side + where(ixStateType_subset==iname_liqCanopy) sMul = 1._rkind ! nothing else on the left hand side ! define the energy multiplier for the state vector for residual calculations (snow-soil domain) if(nSnowSoilNrg>0)then @@ -408,7 +408,7 @@ subroutine computHeatCapAnalytic(& dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature ! output: error control err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 160b0cbd2..1c911f6db 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -608,7 +608,7 @@ subroutine coupled_em(& dtSave = whole_step ! length of whole substep ! initialize balances from inside solver - diag_data%var(iLookDIAG%balanceCasNrg)%dat(1) = 0._rkind ! balance of energy in the canopy + diag_data%var(iLookDIAG%balanceCasNrg)%dat(1) = 0._rkind ! balance of energy in the canopy air space diag_data%var(iLookDIAG%balanceVegNrg)%dat(1) = 0._rkind ! balance of energy in the vegetation diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = 0._rkind ! balance of energy in the snow diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = 0._rkind ! balance of energy in the soil @@ -1073,14 +1073,14 @@ subroutine coupled_em(& if (nSnow>0) innerEffRainfall = innerEffRainfall + ( flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) + flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) )*dt_wght ! sum the balance of energy and water per domain - diag_data%var(iLookDIAG%balanceCasNrg)%dat(1) = diag_data%var(iLookDIAG%balanceCasNrg)%dat(1) + balanceNrg(1) ! balance of energy in the canopy - diag_data%var(iLookDIAG%balanceVegNrg)%dat(1) = diag_data%var(iLookDIAG%balanceVegNrg)%dat(1) + balanceNrg(2) ! balance of energy in the vegetation - diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) + balanceNrg(3) ! balance of energy in the snow - diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) + balanceNrg(4) ! balance of energy in the soil - diag_data%var(iLookDIAG%balanceVegMass)%dat(1) = diag_data%var(iLookDIAG%balanceVegMass)%dat(1) + balanceMass(1) ! balance of water in the vegetation - diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) + balanceMass(2) ! balance of water in the snow - diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) + balanceMass(3) ! balance of water in the soil - diag_data%var(iLookDIAG%balanceAqMass)%dat(1) = diag_data%var(iLookDIAG%balanceAqMass)%dat(1) + balanceMass(4) ! balance of water in the aquifer + diag_data%var(iLookDIAG%balanceCasNrg)%dat(1) = diag_data%var(iLookDIAG%balanceCasNrg)%dat(1) + balanceNrg(1)*dt_wght ! balance of energy in the canopy air space + diag_data%var(iLookDIAG%balanceVegNrg)%dat(1) = diag_data%var(iLookDIAG%balanceVegNrg)%dat(1) + balanceNrg(2)*dt_wght ! balance of energy in the vegetation + diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) + balanceNrg(3)*dt_wght ! balance of energy in the snow + diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) + balanceNrg(4)*dt_wght ! balance of energy in the soil + diag_data%var(iLookDIAG%balanceVegMass)%dat(1) = diag_data%var(iLookDIAG%balanceVegMass)%dat(1) + balanceMass(1)*dt_wght ! balance of water in the vegetation + diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) + balanceMass(2)*dt_wght ! balance of water in the snow + diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) + balanceMass(3)*dt_wght ! balance of water in the soil + diag_data%var(iLookDIAG%balanceAqMass)%dat(1) = diag_data%var(iLookDIAG%balanceAqMass)%dat(1) + balanceMass(4)*dt_wght ! balance of water in the aquifer ! increment sub-step accepted step dt_solvInner = dt_solvInner + dt_sub diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 1d43847ae..0e20c21da 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -462,9 +462,6 @@ subroutine eval8summa(& ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! update values - mLayerEnthalpy = mLayerEnthalpyTrial - scalarCanopyEnthalpy = scalarCanopyEnthalpyTrial else if(ixHowHeatCap == closedForm)then call computHeatCapAnalytic(& ! input: control variables @@ -503,13 +500,13 @@ subroutine eval8summa(& ! compute multiplier of state vector call computStatMult(& ! input - heatCapVegTrial, & ! intent(in): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(in): volumetric heat capacity of soil and snow - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers + heatCapVegTrial, & ! intent(in): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(in): volumetric heat capacity of soil and snow + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers ! output - sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) - err,cmessage) ! intent(out): error control + sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) ! update thermal conductivity diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index e0ec5b1c7..bd5caecec 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -551,13 +551,13 @@ subroutine eval8summaWithPrime(& ! compute multiplier of state vector call computStatMult(& ! input - heatCapVegTrial, & ! intent(in): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(in): volumetric heat capacity of soil and snow - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers + heatCapVegTrial, & ! intent(in): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(in): volumetric heat capacity of soil and snow + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers ! output - sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) - err,cmessage) ! intent(out): error control + sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) ! update thermal conductivity @@ -616,7 +616,6 @@ subroutine eval8summaWithPrime(& mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices ! output - ! output canopyCmTrial, & ! intent(out): Cm for vegetation (J kg K-1) mLayerCmTrial, & ! intent(out): Cm for soil and snow (J kg K-1) dCm_dTk, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 9d4b05e20..86545ce9e 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -612,13 +612,13 @@ subroutine summaSolve4ida( & if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! compute energy balance - if(ixCasNrg/=integerMissing) balanceNrg(1) = balanceNrg(1) + eqns_data%scalarCanairEnthalpyTrial - eqns_data%scalarCanairEnthalpyPrev - eqns_data%fluxVec(ixVegNrg)*dt_diff - if(ixVegNrg/=integerMissing) balanceNrg(2) = balanceNrg(2) + eqns_data%scalarCanopyEnthalpyTrial - eqns_data%scalarCanopyEnthalpyPrev - eqns_data%fluxVec(ixVegNrg)*dt_diff + if(ixCasNrg/=integerMissing) balanceNrg(1) = balanceNrg(1) + eqns_data%scalarCanairEnthalpyTrial - eqns_data%scalarCanairEnthalpyPrev - eqns_data%fluxVec(ixVegNrg)*dt_diff/dt + if(ixVegNrg/=integerMissing) balanceNrg(2) = balanceNrg(2) + eqns_data%scalarCanopyEnthalpyTrial - eqns_data%scalarCanopyEnthalpyPrev - eqns_data%fluxVec(ixVegNrg)*dt_diff/dt if(nSnowSoilNrg>0)then do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) select case(layerType(i)) - case(iname_snow); balanceNrg(3) = balanceNrg(3) + (eqns_data%mLayerEnthalpyTrial(i) - eqns_data%mLayerEnthalpyPrev(i) - eqns_data%fluxVec(ixSnowSoilNrg(i))*dt_diff)/nSnow - case(iname_soil); balanceNrg(4) = balanceNrg(4) + (eqns_data%mLayerEnthalpyTrial(i) - eqns_data%mLayerEnthalpyPrev(i) - eqns_data%fluxVec(ixSnowSoilNrg(i))*dt_diff)/(nLayers-nSnow) + case(iname_snow); balanceNrg(3) = balanceNrg(3) + (eqns_data%mLayerEnthalpyTrial(i) - eqns_data%mLayerEnthalpyPrev(i) - eqns_data%fluxVec(ixSnowSoilNrg(i))*dt_diff/dt)/nSnow + case(iname_soil); balanceNrg(4) = balanceNrg(4) + (eqns_data%mLayerEnthalpyTrial(i) - eqns_data%mLayerEnthalpyPrev(i) - eqns_data%fluxVec(ixSnowSoilNrg(i))*dt_diff/dt)/(nLayers-nSnow) end select enddo endif @@ -631,16 +631,16 @@ subroutine summaSolve4ida( & ! compute mass balance ! resVec is the instanteous residual vector from the solver - if(ixVegHyd/=integerMissing) balanceMass(1) = balanceMass(1) + eqns_data%resVec(ixVegHyd)*dt_diff + if(ixVegHyd/=integerMissing) balanceMass(1) = balanceMass(1) + eqns_data%resVec(ixVegHyd) if(nSnowSoilHyd>0)then do concurrent (i=1:nLayers,ixSnowSoilHyd(i)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) select case(layerType(i)) - case(iname_snow); balanceMass(2) = balanceMass(2) + eqns_data%resVec(ixSnowSoilHyd(i))*dt_diff/nSnow - case(iname_soil); balanceMass(3) = balanceMass(3) + eqns_data%resVec(ixSnowSoilHyd(i))*dt_diff/(nLayers-nSnow) + case(iname_snow); balanceMass(2) = balanceMass(2) + eqns_data%resVec(ixSnowSoilHyd(i))*dt_diff/dt/nSnow + case(iname_soil); balanceMass(3) = balanceMass(3) + eqns_data%resVec(ixSnowSoilHyd(i))*dt_diff/dt/(nLayers-nSnow) end select enddo endif - if(ixAqWat/=integerMissing) balanceMass(4) = balanceMass(4) + eqns_data%resVec(ixAqWat)*dt_diff + if(ixAqWat/=integerMissing) balanceMass(4) = balanceMass(4) + eqns_data%resVec(ixAqWat) endif ! save required quantities for next step diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index 49107b203..7fccee75e 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -201,7 +201,7 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number of ! compute enthalpy ! NOTE: assume intrrinsic density of ice is the intrinsic density of water ! NOTE: kg m-3 J kg-1 K-1 K - Ey(iLook) = Ey(iLook) + iden_water*Cp_water*vFracLiq*T_incr + iden_water*Cp_ice*volFracIce*T_incr + Ey(iLook) = Ey(iLook) + iden_water * Cp_water*vFracLiq*T_incr + iden_water * Cp_ice*volFracIce*T_incr end do ! numerical integration @@ -452,31 +452,20 @@ subroutine t2enthalpy(& snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) ) - diffT = mLayerTempTrial(iLayer) - Tfreeze - if(diffT>=0._rkind)then - enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * diffT - enthIce = 0._rkind - enthAir = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) ) * diffT - ! enthalpy derivatives - dEnthLiq_dTk = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) - dEnthAir_dTk = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) ) - dEnthLiq_dWat = iden_water * Cp_water * diffT - dEnthAir_dWat = -iden_air * Cp_air * diffT - else - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral - enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) - enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) - ! derivatives - d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) - ! enthalpy derivatives - dEnthLiq_dTk = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * d_integral_dTk - dEnthIce_dTk = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( 1._rkind - d_integral_dTk ) - dEnthAir_dTk = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(1._rkind-d_integral_dTk) + d_integral_dTk ) ) - dEnthLiq_dWat = iden_water * Cp_water * integral - dEnthIce_dWat = iden_water * Cp_ice * ( diffT - integral ) - dEnthAir_dWat = -iden_air * Cp_air * ( (iden_water/iden_ice)*(diffT-integral) + integral ) - endif + diffT = mLayerTempTrial(iLayer) - Tfreeze ! diffT<0._rkind because snow is frozen + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral + enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) + enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) + ! derivatives + d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) + ! enthalpy derivatives + dEnthLiq_dTk = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * d_integral_dTk + dEnthIce_dTk = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( 1._rkind - d_integral_dTk ) + dEnthAir_dTk = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(1._rkind-d_integral_dTk) + d_integral_dTk ) ) + dEnthLiq_dWat = iden_water * Cp_water * integral + dEnthIce_dWat = iden_water * Cp_ice * ( diffT - integral ) + dEnthAir_dWat = -iden_air * Cp_air * ( (iden_water/iden_ice)*(diffT-integral) + integral ) mLayerEnthalpy(iLayer) = enthLiq + enthIce + enthAir dEnthalpy_dTk(iLayer) = dEnthLiq_dTk + dEnthIce_dTk + dEnthAir_dTk @@ -511,10 +500,10 @@ subroutine t2enthalpy(& ! *** compute enthalpy of water for unfrozen conditions if(mlayerTempTrial(iLayer)>=Tcrit)then - enthWater = iden_water*Cp_water*volFracWat*diffT ! valid for temperatures below freezing also + enthWater = iden_water * Cp_water * volFracWat * diffT ! valid for temperatures below freezing also ! enthalpy derivatives - dEnthWater_dTk = iden_water*Cp_water*volFracWat - dEnthWater_dWat = iden_water*Cp_water*dVolTot_dPsi0(ixControlIndex) !dVolFracWat_dWat = dVolTot_dPsi0(ixControlIndex) + dEnthWater_dTk = iden_water * Cp_water * volFracWat + dEnthWater_dWat = iden_water * Cp_water * dVolTot_dPsi0(ixControlIndex) !dVolFracWat_dWat = dVolTot_dPsi0(ixControlIndex) ! *** compute enthalpy of water for frozen conditions else @@ -528,15 +517,15 @@ subroutine t2enthalpy(& ! calculate the enthalpy of water enthMix = enthTemp - enthTcrit ! enthalpy of the liquid+ice mix - enthLiq = iden_water*Cp_water*volFracWat*(Tcrit - Tfreeze) + enthLiq = iden_water * Cp_water * volFracWat * (Tcrit - Tfreeze) enthWater = enthMix + enthLiq ! derivatives dTcrit_dPsi0 = 0._rkind - if (mLayerMatricHeadTrial(ixControlIndex)<0._rkind) dTcrit_dPsi0 = gravity*Tfreeze/LH_fus + if (mLayerMatricHeadTrial(ixControlIndex)<0._rkind) dTcrit_dPsi0 = gravity * Tfreeze/LH_fus ! enthalpy derivatives dEnthWater_dTk = dE - dEnthWater_dWat = -dEcrit*dTcrit_dPsi0 + iden_water*Cp_water*dVolTot_dPsi0(ixControlIndex)*(Tcrit - Tfreeze) + iden_water*Cp_water*volFracWat*dTcrit_dPsi0 + dEnthWater_dWat = -dEcrit*dTcrit_dPsi0 + iden_water * Cp_water * dVolTot_dPsi0(ixControlIndex) * (Tcrit - Tfreeze) + iden_water * Cp_water * volFracWat*dTcrit_dPsi0 endif ! (if frozen conditions) ! *** compute the enthalpy of soil diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 95e96dc46..62ee50174 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -79,6 +79,11 @@ module varSubstep_module kinsol ,& ! SUNDIALS backward Euler solution using Kinsol ida ! SUNDIALS solution using IDA +! look-up values for the choice of heat capacity computation +USE mDecisions_module,only: & + closedForm, & ! heat capacity using closed form, not using enthalpy + enthalpyFD ! heat capacity using enthalpy + ! safety: set private unless specified otherwise implicit none private @@ -112,8 +117,8 @@ subroutine varSubstep(& deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables bvar_data, & ! intent(in) : model variables for the local basin ! input/output: balances - sumBalanceNrg, & ! intent(inout) : step balance of energy per domain - sumBalanceMass, & ! intent(inout) : step balance of mass per domain + sumBalanceNrg, & ! intent(out) : step balance of energy per domain + sumBalanceMass, & ! intent(out) : step balance of mass per domain ! output: model control out_varSubstep) ! intent(out) : model control ! --------------------------------------------------------------------------------------- @@ -146,8 +151,8 @@ subroutine varSubstep(& type(var_dlength),intent(inout) :: flux_mean ! mean model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin - real(rkind),intent(inout) :: sumBalanceNrg(:) ! step balance of energy per domain - real(rkind),intent(inout) :: sumBalanceMass(:) ! step balance of mass per domain + real(rkind),intent(out) :: sumBalanceNrg(:) ! step balance of energy per domain + real(rkind),intent(out) :: sumBalanceMass(:) ! step balance of mass per domain ! output: model control type(out_type_varSubstep),intent(out) :: out_varSubstep ! model control ! --------------------------------------------------------------------------------------- @@ -219,6 +224,7 @@ subroutine varSubstep(& ixSaturation => io_varSubstep % ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) ! model decisions ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver + ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy ! number of layers nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers @@ -254,8 +260,8 @@ subroutine varSubstep(& ! ********************************************************************************************************************************************************* ! initialize balances - balanceNrg = 0._rkind - balanceMass = 0._rkind + sumBalanceNrg = 0._rkind + sumBalanceMass = 0._rkind ! initialize error control err=0; message='varSubstep/' @@ -428,9 +434,10 @@ subroutine varSubstep(& endif ! update prognostic variables, update balances, and check them for possible step reduction if numrec or kinsol - call updateProg(dtSubstep,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control - model_decisions,lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures - fluxVec,resVec,balanceNrg,balanceMass,waterBalanceError,nrgFluxModified,err,message) ! output: balances, flags, and error control + call updateProg(dtSubstep,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states + doAdjustTemp,computeVegFlux,checkMassBalance, checkNrgBalance,ixHowHeatCap == enthalpyFD, & ! input: model control + model_decisions,lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures + fluxVec,resVec,balanceNrg,balanceMass,waterBalanceError,nrgFluxModified,err,message) ! output: balances, flags, and error control if(err/=0)then message=trim(message)//trim(cmessage) if(err>0) return @@ -523,8 +530,8 @@ subroutine varSubstep(& endif ! (if the flux is desired) end do ! (loop through fluxes) ! If did not compute the layer residual vector on this step, added balance - sumBalanceNrg = sumBalanceNrg + balanceNrg - sumBalanceMass = sumBalanceMass + balanceMass + sumBalanceNrg = sumBalanceNrg + balanceNrg*dt_wght + sumBalanceMass = sumBalanceMass + balanceMass*dt_wght ! increment the number of substeps nSubsteps = nSubsteps + nSteps @@ -575,10 +582,11 @@ end subroutine varSubstep ! ********************************************************************************************************** ! private subroutine updateProg: update prognostic variables ! ********************************************************************************************************** -subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control - model_decisions,lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures - fluxVec,resVec,balanceNrg,balanceMass,waterBalanceError,nrgFluxModified,err,message) ! output: balances, flags, and error control - USE getVectorz_module,only:varExtract ! extract variables from the state vector +subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states + doAdjustTemp,computeVegFlux,checkMassBalance, checkNrgBalance,use_enthalpyFD, & ! input: model control + model_decisions,lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures + fluxVec,resVec,balanceNrg,balanceMass,waterBalanceError,nrgFluxModified,err,message) ! output: balances, flags, and error control +USE getVectorz_module,only:varExtract ! extract variables from the state vector #ifdef SUNDIALS_ACTIVE USE updateVarsWithPrime_module,only:updateVarsWithPrime ! update prognostic variables #endif @@ -598,6 +606,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe real(rkind) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance logical(lgt) ,intent(in) :: checkNrgBalance ! flag to check the energy balance + logical(lgt) ,intent(in) :: use_enthalpyFD ! flag that using enthalpy finite difference and need to update ! data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions type(zLookup),intent(in) :: lookup_data ! lookup tables @@ -646,6 +655,11 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector for total water matric potential (m) real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector for liquid water matric potential (m) real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) + real(rkind) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3) + real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) + real(rkind) :: scalarCanopyEnthalpy_nophase ! trial value for enthalpy of the vegetation canopy (J m-3), no phase change + real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector for enthalpy of snow + soil (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpy_nophase ! trial vector for enthalpy of snow + soil (J m-3), no phase change ! diagnostic variables real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) @@ -665,9 +679,6 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe real(rkind) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector for volumetric fraction of liquid water (-) real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector for volumetric fraction of ice (-) - real(rkind) :: scalarCanairEnthalpyTrial ! enthalpy of the canopy air space (J m-3) - real(rkind) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! enthalpy of snow + soil (J m-3) ! enthalpy derivatives real(rkind) :: dCanEnthalpy_dTk_unused ! will not be used, derivatives in canopy enthalpy w.r.t. temperature real(rkind) :: dCanEnthalpy_dWat_unused ! will not be used, derivatives in canopy enthalpy w.r.t. water state @@ -913,7 +924,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! ---- ! * check energy balance !------------------------ - if(checkNrgBalance)then ! update diagnostic enthalpy variables + if(checkNrgBalance .or. use_enthalpyFD)then ! update diagnostic enthalpy variables ! compute enthalpy at t_{n+1} call t2enthalpy(& ! input: data structures @@ -942,6 +953,12 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + ! update no phase values for enthalpy + mLayerEnthalpy_nophase = mLayerEnthalpyTrial + scalarCanopyEnthalpy_nophase = scalarCanopyEnthalpyTrial + endif + + if(checkNrgBalance)then call t2enthalpy_addphase(& ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -978,9 +995,15 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappe enddo endif end select - endif ! if checking energy balance + ! save the trial values + if(checkNrgBalance .or. use_enthalpyFD)then + scalarCanairEnthalpy = scalarCanairEnthalpyTrial + mLayerEnthalpy_nophase = mLayerEnthalpy_nophase + scalarCanopyEnthalpy_nophase = scalarCanopyEnthalpy_nophase + endif + ! ----- ! * check mass balance... ! ----------------------- From e134ea2c125525f59f3e5c5af42627f6e8f4478b Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 21 Nov 2023 03:01:17 -0600 Subject: [PATCH 0999/1472] Added initialize/finalize steps for stateThenDomain loop and simplifiedassociate statements. --- build/source/engine/opSplittin.f90 | 446 ++++++++++++++--------------- 1 file changed, 218 insertions(+), 228 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index aeb81b771..3c7bfc78e 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -299,253 +299,200 @@ subroutine opSplittin(& type(in_type_varSubstep) :: in_varSubstep; type(io_type_varSubstep) :: io_varSubstep; type(out_type_varSubstep) :: out_varSubstep; ! varSubstep arguments ! --------------------------------------------------------------------------------------- - ! point to variables in the data structures - ! --------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="opSplittin/" + + associate(ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision) ! intent(in): [i4b] choice of numerical solver + ! we just solve the fully coupled problem if IDA for now, splitting can happen otherwise + select case(ixNumericalMethod) + case(ida); nCoupling = 1 + case(kinsol, numrec); nCoupling = 2 + end select + end associate - globalVars: associate(& - ! model decisions - ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver - ! vector of energy and hydrology indices for the snow and soil domains - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology state variables in the snow+soil domain - ! indices of model state variables - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset (missing for values not in the subset) - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) - ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain - ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain - ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain - ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ! numerix tracking - numberStateSplit => indx_data%var(iLookINDEX%numberStateSplit )%dat(1) ,& ! intent(inout): [i4b] number of state splitting solutions (-) - numberScalarSolutions => indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1) & ! intent(inout): [i4b] number of scalar solutions (-) - ) - ! --------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="opSplittin/" + ! *** initialize *** + call initialize_opSplittin + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - ! we just solve the fully coupled problem if IDA for now, splitting can happen otherwise - select case(ixNumericalMethod) - case(ida); nCoupling = 1 - case(kinsol, numrec); nCoupling = 2 - end select + ! ========================================================================================================================================== + ! loop through different coupling strategies + coupling: do ixCoupling=1,nCoupling - ! *** initialize *** - call initialize_opSplittin - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + ! initialize the time step + dtInit = min( merge(dt, dtmin_coupled, ixCoupling==fullyCoupled), dt) ! initial time step + dt_min = min( merge(dtmin_coupled, dtmin_split, ixCoupling==fullyCoupled), dt) ! minimum time step - ! ========================================================================================================================================== - ! loop through different coupling strategies - coupling: do ixCoupling=1,nCoupling + ! keep track of the number of state splits + associate(numberStateSplit => indx_data%var(iLookINDEX%numberStateSplit)%dat(1)) ! intent(inout): [i4b] number of state splitting solutions + if (ixCoupling/=fullyCoupled) numberStateSplit = numberStateSplit + 1 + end associate - ! initialize the time step - dtInit = min( merge(dt, dtmin_coupled, ixCoupling==fullyCoupled), dt) ! initial time step - dt_min = min( merge(dtmin_coupled, dtmin_split, ixCoupling==fullyCoupled), dt) ! minimum time step + ! define the number of operator splits for the state type + select case(ixCoupling) + case(fullyCoupled); nStateTypeSplit=1 + case(stateTypeSplit); nStateTypeSplit=nStateTypes + case default; err=20; message=trim(message)//'coupling case not found'; return + end select ! operator splitting option - ! keep track of the number of state splits - if(ixCoupling/=fullyCoupled) numberStateSplit = numberStateSplit + 1 + ! define if we wish to try the domain split + select case(ixCoupling) + case(fullyCoupled); tryDomainSplit=0 + case(stateTypeSplit); tryDomainSplit=1 + case default; err=20; message=trim(message)//'coupling case not found'; return + end select ! operator splitting option - ! define the number of operator splits for the state type - select case(ixCoupling) - case(fullyCoupled); nStateTypeSplit=1 - case(stateTypeSplit); nStateTypeSplit=nStateTypes - case default; err=20; message=trim(message)//'coupling case not found'; return - end select ! operator splitting option + mean_step_dt = 0._rkind ! initialize mean step for the time step + addFirstFlux = .true. ! flag to add the first flux to the mask - ! define if we wish to try the domain split - select case(ixCoupling) - case(fullyCoupled); tryDomainSplit=0 - case(stateTypeSplit); tryDomainSplit=1 - case default; err=20; message=trim(message)//'coupling case not found'; return - end select ! operator splitting option + ! state splitting loop + stateTypeSplit: do iStateTypeSplit=1,nStateTypeSplit - mean_step_dt = 0._rkind ! initialize mean step for the time step - addFirstFlux = .true. ! flag to add the first flux to the mask + ! identify state-specific variables for a given state split + call initialize_stateThenDomain_loop - ! state splitting loop - stateTypeSplit: do iStateTypeSplit=1,nStateTypeSplit + ! first try the state type split, then try the domain split within a given state type + stateThenDomain: do ixStateThenDomain=1,1+tryDomainSplit ! 1=state type split; 2=domain split within a given state type - ! ----- - ! * identify state-specific variables for a given state split... - ! -------------------------------------------------------------- + call initialize_domainSplit_loop + if (return_flag.eqv..true.) return ! return if error occurs during initialization - ! flag to adjust the temperature - doAdjustTemp = (ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit) + ! domain splitting loop + domainSplit: do iDomainSplit=1,nDomainSplit - ! modify the state type names associated with the state vector - if(ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit)then - if(computeVegFlux)then - where(ixStateType(ixHydCanopy)==iname_watCanopy) ixStateType(ixHydCanopy)=iname_liqCanopy - endif - where(ixStateType(ixHydLayer) ==iname_watLayer) ixStateType(ixHydLayer) =iname_liqLayer - where(ixStateType(ixHydLayer) ==iname_matLayer) ixStateType(ixHydLayer) =iname_lmpLayer - endif ! if modifying state variables for the mass split + ! trial with the vector then scalar solution + solution: do ixSolution=1,nSolutions - ! first try the state type split, then try the domain split within a given state type - stateThenDomain: do ixStateThenDomain=1,1+tryDomainSplit ! 1=state type split; 2=domain split within a given state type + call initialize_stateSplit_loop + if (return_flag.eqv..true.) return ! return if error occurs during initialization - call initialize_domainSplit_loop - if (return_flag.eqv..true.) return ! return if error occurs during initialization + ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) + stateSplit: do iStateSplit=1,nStateSplit + + ! ----- + ! * define state subsets for a given split... + ! ------------------------------------------- + + ! get the mask for the state subset + call initialize_stateFilter + call stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) + call finalize_stateFilter + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! error control + + ! check that state variables exist + if (nSubset==0) cycle domainSplit + + ! avoid redundant case where vector solution is of length 1 + if (ixSolution==vector .and. count(stateMask)==1) cycle solution + + ! check that we do not attempt the scalar solution for the fully coupled case + if (ixCoupling==fullyCoupled .and. ixSolution==scalar) then + message=trim(message)//'only apply the scalar solution to the fully split coupling strategy' + err=20; return + end if + + ! reset the flag for the first flux call + if (.not.firstSuccess) firstFluxCall=.true. + + ! save/recover copies of variables and fluxes + call save_recover + + ! ----- + ! * assemble vectors for a given split... + ! --------------------------------------- + ! get indices for a given split + call initialize_indexSplit + call indexSplit(in_indexSplit,stateMask,indx_data,out_indexSplit) + call finalize_indexSplit + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! ----- + ! * define the mask of the fluxes used... + ! --------------------------------------- + call update_fluxMask + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! ******************************************************************************************************************************* + ! ***** trial with a given solution method... + + + ! ----- + ! * solve variable subset for one time step... + ! -------------------------------------------- + + ! keep track of the number of scalar solutions + associate(numberScalarSolutions => indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1)) ! intent(inout): [i4b] number of scalar solutions + if (ixSolution==scalar) numberScalarSolutions = numberScalarSolutions + 1 + end associate + + ! solve variable subset for one full time step + call initialize_varSubstep + call varSubstep(in_varSubstep,io_varSubstep,& ! intent(inout): class objects for model control + model_decisions,lookup_data,type_data,attr_data,forc_data,mpar_data,& ! intent(inout): data structures for model properties + indx_data,prog_data,diag_data,flux_data,flux_mean,deriv_data,bvar_data,& + out_varSubstep) ! intent(out): class object for model control + call finalize_varSubstep + if (err/=0) then; message=trim(message)//trim(cmessage); if (err>0) return; end if ! error control + + ! determine whether solution is a success or a failure + call judge_solution + if (return_flag.eqv..true.) return ! return for a recovering solution + + ! try the fully split solution if failed to converge with a minimum time step in the coupled solution + if (ixCoupling==fullyCoupled .and. failure) cycle coupling + + ! try the scalar solution if failed to converge with a minimum time step in the split solution + if (ixCoupling/=fullyCoupled) then + select case(ixStateThenDomain) + case(fullDomain); if (failure) cycle stateThenDomain + case(subDomain); if (failure) cycle solution + case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' + end select + end if + + ! check that state variables updated + where(stateMask) stateCheck = stateCheck+1 + if (any(stateCheck>1)) then + message=trim(message)//'state variable updated more than once!' + err=20; return + end if + + ! success = exit solution + if (.not.failure) then + ! sum the mean steps for the successful solution type + mean_step_solution = mean_step_solution + (dt/nSubsteps)/nStateSplit + select case(ixStateThenDomain) + case(fullDomain); if (iStateSplit==nStateSplit) exit stateThenDomain + case(subDomain); if (iStateSplit==nStateSplit) exit solution + case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' + end select + else ! failure + call check_failure; return ! check reason for failure and return + end if ! success check - ! domain splitting loop - domainSplit: do iDomainSplit=1,nDomainSplit + end do stateSplit ! solution with split layers - ! trial with the vector then scalar solution - solution: do ixSolution=1,nSolutions + end do solution ! trial with the full layer solution then the split layer solution - call initialize_stateSplit_loop - if (return_flag.eqv..true.) return ! return if error occurs during initialization + ! sum the mean steps for the state over each domain split + mean_step_state = mean_step_state + mean_step_solution/nDomainSplit - ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) - stateSplit: do iStateSplit=1,nStateSplit - - ! ----- - ! * define state subsets for a given split... - ! ------------------------------------------- - - ! get the mask for the state subset - call initialize_stateFilter - call stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) - call finalize_stateFilter - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! error control - - ! check that state variables exist - if (nSubset==0) cycle domainSplit - - ! avoid redundant case where vector solution is of length 1 - if (ixSolution==vector .and. count(stateMask)==1) cycle solution - - ! check that we do not attempt the scalar solution for the fully coupled case - if (ixCoupling==fullyCoupled .and. ixSolution==scalar) then - message=trim(message)//'only apply the scalar solution to the fully split coupling strategy' - err=20; return - end if - - ! reset the flag for the first flux call - if (.not.firstSuccess) firstFluxCall=.true. - - ! save/recover copies of variables and fluxes - call save_recover - - ! ----- - ! * assemble vectors for a given split... - ! --------------------------------------- - ! get indices for a given split - call initialize_indexSplit - call indexSplit(in_indexSplit,stateMask,indx_data,out_indexSplit) - call finalize_indexSplit - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! ----- - ! * define the mask of the fluxes used... - ! --------------------------------------- - call update_fluxMask - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! ******************************************************************************************************************************* - ! ***** trial with a given solution method... - - - ! ----- - ! * solve variable subset for one time step... - ! -------------------------------------------- - - ! keep track of the number of scalar solutions - if (ixSolution==scalar) numberScalarSolutions = numberScalarSolutions + 1 - - ! solve variable subset for one full time step - call initialize_varSubstep - call varSubstep(in_varSubstep,io_varSubstep,& ! intent(inout): class objects for model control - model_decisions,lookup_data,type_data,attr_data,forc_data,mpar_data,& ! intent(inout): data structures for model properties - indx_data,prog_data,diag_data,flux_data,flux_mean,deriv_data,bvar_data,& - out_varSubstep) ! intent(out): class object for model control - call finalize_varSubstep - if (err/=0) then; message=trim(message)//trim(cmessage); if (err>0) return; end if ! error control - - ! determine whether solution is a success or a failure - call judge_solution - if (return_flag.eqv..true.) return ! return for a recovering solution - - ! try the fully split solution if failed to converge with a minimum time step in the coupled solution - if (ixCoupling==fullyCoupled .and. failure) cycle coupling - - ! try the scalar solution if failed to converge with a minimum time step in the split solution - if (ixCoupling/=fullyCoupled) then - select case(ixStateThenDomain) - case(fullDomain); if (failure) cycle stateThenDomain - case(subDomain); if (failure) cycle solution - case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' - end select - end if - - ! check that state variables updated - where(stateMask) stateCheck = stateCheck+1 - if (any(stateCheck>1)) then - message=trim(message)//'state variable updated more than once!' - err=20; return - end if - - ! success = exit solution - if (.not.failure) then - ! sum the mean steps for the successful solution type - mean_step_solution = mean_step_solution + (dt/nSubsteps)/nStateSplit - select case(ixStateThenDomain) - case(fullDomain); if (iStateSplit==nStateSplit) exit stateThenDomain - case(subDomain); if (iStateSplit==nStateSplit) exit solution - case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' - end select - else ! failure - call check_failure; return ! check reason for failure and return - end if ! success check - - end do stateSplit ! solution with split layers - - end do solution ! trial with the full layer solution then the split layer solution - - ! sum the mean steps for the state over each domain split - mean_step_state = mean_step_state + mean_step_solution/nDomainSplit - - ! ***** trial with a given solution method... - ! ******************************************************************************************************************************* - end do domainSplit ! domain type splitting loop - - end do stateThenDomain ! switch between the state and the domain - - ! sum the mean steps for the time step over each state type split - select case(ixStateThenDomain) - case(fullDomain); mean_step_dt = mean_step_dt + mean_step_solution/nStateTypeSplit - case(subDomain); mean_step_dt = mean_step_dt + mean_step_state/nStateTypeSplit - end select - - ! ----- - ! * reset state variables for the mass split... - ! --------------------------------------------- - ! modify the state type names associated with the state vector - if(ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit)then - if(computeVegFlux)then - where(ixStateType(ixHydCanopy)==iname_liqCanopy) ixStateType(ixHydCanopy)=iname_watCanopy - endif - where(ixStateType(ixHydLayer) ==iname_liqLayer) ixStateType(ixHydLayer) =iname_watLayer - where(ixStateType(ixHydLayer) ==iname_lmpLayer) ixStateType(ixHydLayer) =iname_matLayer - endif ! if modifying state variables for the mass split - - end do stateTypeSplit ! state type splitting loop - - ! success = exit the coupling loop - if(ixCoupling==fullyCoupled .and. .not.failure) exit coupling - - end do coupling ! coupling method - - ! *** Finalize *** - call finalize_opSplittin - - ! end associate statements - end associate globalVars + ! ***** trial with a given solution method... + ! ******************************************************************************************************************************* + end do domainSplit ! domain type splitting loop + + end do stateThenDomain ! switch between the state and the domain + + call finalize_stateThenDomain_loop ! final steps following the stateThenDomain loop + + end do stateTypeSplit ! state type splitting loop + + ! success = exit the coupling loop + if(ixCoupling==fullyCoupled .and. .not.failure) exit coupling + + end do coupling ! coupling method + + ! *** Finalize *** + call finalize_opSplittin contains @@ -652,6 +599,49 @@ subroutine finalize_opSplittin if (ixCoupling/=fullyCoupled .or. nSubsteps>1) dtMultiplier=0.5_rkind end subroutine finalize_opSplittin + subroutine initialize_stateThenDomain_loop + ! *** Identify state-specific variables for a given state split *** + doAdjustTemp = (ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit) ! flag to adjust the temperature + associate(& + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat, & ! intent(in): [i4b(:)] indices defining the type of the state + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat, & ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + + ! modify the state type names associated with the state vector + if (ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit) then ! if modifying state variables for the mass split + if (computeVegFlux) then + where(ixStateType(ixHydCanopy)==iname_watCanopy) ixStateType(ixHydCanopy)=iname_liqCanopy + end if + where(ixStateType(ixHydLayer)==iname_watLayer) ixStateType(ixHydLayer)=iname_liqLayer + where(ixStateType(ixHydLayer)==iname_matLayer) ixStateType(ixHydLayer)=iname_lmpLayer + end if + end associate + end subroutine initialize_stateThenDomain_loop + + subroutine finalize_stateThenDomain_loop + ! *** Final steps following the stateThenDomain loop *** + ! sum the mean steps for the time step over each state type split + select case(ixStateThenDomain) + case(fullDomain); mean_step_dt = mean_step_dt + mean_step_solution/nStateTypeSplit + case(subDomain); mean_step_dt = mean_step_dt + mean_step_state/nStateTypeSplit + end select + associate(& + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat, & ! intent(in): [i4b(:)] indices defining the type of the state + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat, & ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + + ! * reset state variables for the mass split... + ! modify the state type names associated with the state vector + if (ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit) then ! if modifying state variables for the mass split + if (computeVegFlux) then + where(ixStateType(ixHydCanopy)==iname_liqCanopy) ixStateType(ixHydCanopy)=iname_watCanopy + end if + where(ixStateType(ixHydLayer)==iname_liqLayer) ixStateType(ixHydLayer)=iname_watLayer + where(ixStateType(ixHydLayer)==iname_lmpLayer) ixStateType(ixHydLayer)=iname_matLayer + end if + end associate + end subroutine finalize_stateThenDomain_loop + subroutine initialize_domainSplit_loop ! *** initial operations to set up domainSplit loop *** return_flag=.false. ! initialize flag From 1b7d439a21543bb3a993379a700dda4c2349b99f Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 22 Nov 2023 04:10:18 -0600 Subject: [PATCH 1000/1472] Changed a loop name in opSplittin to stateTypeSplitting resolve compiler errors -- also added a corresponding initialize routine. --- build/source/engine/opSplittin.f90 | 80 ++++++++++++++++-------------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 3c7bfc78e..0b3950cdc 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -297,8 +297,8 @@ subroutine opSplittin(& type(in_type_stateFilter) :: in_stateFilter; type(out_type_stateFilter) :: out_stateFilter; ! stateFilter arguments type(in_type_indexSplit) :: in_indexSplit; type(out_type_indexSplit) :: out_indexSplit; ! indexSplit arguments type(in_type_varSubstep) :: in_varSubstep; type(io_type_varSubstep) :: io_varSubstep; type(out_type_varSubstep) :: out_varSubstep; ! varSubstep arguments - ! --------------------------------------------------------------------------------------- + ! initialize error control err=0; message="opSplittin/" @@ -318,34 +318,11 @@ subroutine opSplittin(& ! loop through different coupling strategies coupling: do ixCoupling=1,nCoupling - ! initialize the time step - dtInit = min( merge(dt, dtmin_coupled, ixCoupling==fullyCoupled), dt) ! initial time step - dt_min = min( merge(dtmin_coupled, dtmin_split, ixCoupling==fullyCoupled), dt) ! minimum time step - - ! keep track of the number of state splits - associate(numberStateSplit => indx_data%var(iLookINDEX%numberStateSplit)%dat(1)) ! intent(inout): [i4b] number of state splitting solutions - if (ixCoupling/=fullyCoupled) numberStateSplit = numberStateSplit + 1 - end associate - - ! define the number of operator splits for the state type - select case(ixCoupling) - case(fullyCoupled); nStateTypeSplit=1 - case(stateTypeSplit); nStateTypeSplit=nStateTypes - case default; err=20; message=trim(message)//'coupling case not found'; return - end select ! operator splitting option - - ! define if we wish to try the domain split - select case(ixCoupling) - case(fullyCoupled); tryDomainSplit=0 - case(stateTypeSplit); tryDomainSplit=1 - case default; err=20; message=trim(message)//'coupling case not found'; return - end select ! operator splitting option - - mean_step_dt = 0._rkind ! initialize mean step for the time step - addFirstFlux = .true. ! flag to add the first flux to the mask + call initialize_stateTypeSplitting_loop + if (return_flag.eqv..true.) return ! return if error occurs during initialization ! state splitting loop - stateTypeSplit: do iStateTypeSplit=1,nStateTypeSplit + stateTypeSplitting: do iStateTypeSplit=1,nStateTypeSplit ! identify state-specific variables for a given state split call initialize_stateThenDomain_loop @@ -484,10 +461,10 @@ subroutine opSplittin(& call finalize_stateThenDomain_loop ! final steps following the stateThenDomain loop - end do stateTypeSplit ! state type splitting loop + end do stateTypeSplitting ! state type splitting loop ! success = exit the coupling loop - if(ixCoupling==fullyCoupled .and. .not.failure) exit coupling + if (ixCoupling==fullyCoupled .and. .not.failure) exit coupling end do coupling ! coupling method @@ -599,6 +576,36 @@ subroutine finalize_opSplittin if (ixCoupling/=fullyCoupled .or. nSubsteps>1) dtMultiplier=0.5_rkind end subroutine finalize_opSplittin + subroutine initialize_stateTypeSplitting_loop + ! *** Initial steps to prepare for iterations of the stateTypeSplit loop *** + return_flag=.false. ! initialize flag + ! initialize the time step + dtInit = min(merge(dt, dtmin_coupled, ixCoupling==fullyCoupled), dt) ! initial time step + dt_min = min(merge(dtmin_coupled, dtmin_split, ixCoupling==fullyCoupled), dt) ! minimum time step + + ! keep track of the number of state splits + associate(numberStateSplit => indx_data%var(iLookINDEX%numberStateSplit)%dat(1)) ! intent(inout): [i4b] number of state splitting solutions + if (ixCoupling/=fullyCoupled) numberStateSplit = numberStateSplit + 1 + end associate + + ! define the number of operator splits for the state type + select case(ixCoupling) + case(fullyCoupled); nStateTypeSplit=1 + case(stateTypeSplit); nStateTypeSplit=nStateTypes + case default; err=20; message=trim(message)//'coupling case not found'; return_flag=.true.; return + end select ! operator splitting option + + ! define if we wish to try the domain split + select case(ixCoupling) + case(fullyCoupled); tryDomainSplit=0 + case(stateTypeSplit); tryDomainSplit=1 + case default; err=20; message=trim(message)//'coupling case not found'; return_flag=.true.; return + end select ! operator splitting option + + mean_step_dt = 0._rkind ! initialize mean step for the time step + addFirstFlux = .true. ! flag to add the first flux to the mask + end subroutine initialize_stateTypeSplitting_loop + subroutine initialize_stateThenDomain_loop ! *** Identify state-specific variables for a given state split *** doAdjustTemp = (ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit) ! flag to adjust the temperature @@ -681,8 +688,8 @@ subroutine initialize_stateSplit_loop ! refine the time step if (ixSolution==scalar) then - dtInit = min(dtmin_split, dt) ! initial time step - dt_min = min(dtmin_scalar, dt) ! minimum time step + dtInit = min(dtmin_split, dt) ! initial time step + dt_min = min(dtmin_scalar, dt) ! minimum time step end if ! initialize the first flux call @@ -691,11 +698,11 @@ subroutine initialize_stateSplit_loop ! get the number of split layers select case(ixSolution) - case(vector); nStateSplit=1 - case(scalar); nStateSplit=count(stateMask) - case default; err=20; message=trim(message)//'unknown solution method'; - return_flag=.true. ! return statement required in opSplittin - return + case(vector); nStateSplit=1 + case(scalar); nStateSplit=count(stateMask) + case default; err=20; message=trim(message)//'unknown solution method'; + return_flag=.true. ! return statement required in opSplittin + return end select end subroutine initialize_stateSplit_loop @@ -913,6 +920,7 @@ subroutine update_fluxMask end do ! end looping through fluxes end subroutine update_fluxMask + end subroutine opSplittin From 0f6dcf5a82d572bd06b8b29bab7999f3687572b6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 23 Nov 2023 14:37:30 +0900 Subject: [PATCH 1001/1472] splitting balances should be correct now --- build/source/dshare/get_ixname.f90 | 2 + build/source/dshare/popMetadat.f90 | 18 ++-- build/source/dshare/var_lookup.f90 | 5 +- build/source/engine/coupled_em.f90 | 85 ++++++++++++----- build/source/engine/opSplittin.f90 | 7 +- build/source/engine/summaSolve4ida.f90 | 38 ++------ build/source/engine/systemSolv.f90 | 21 ++--- build/source/engine/t2enthalpy.f90 | 2 +- build/source/engine/varSubstep.f90 | 122 +++++++++++++++---------- 9 files changed, 169 insertions(+), 131 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 03da03975..4775753c2 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -589,9 +589,11 @@ function get_ixdiag(varName) ! balances case('balanceCasNrg' ); get_ixdiag = iLookDIAG%balanceCasNrg ! balance of energy in the canopy air space case('balanceVegNrg' ); get_ixdiag = iLookDIAG%balanceVegNrg ! balance of energy in the vegetation + case('balanceLayerNrg' ); get_ixdiag = iLookDIAG%balanceLayerNrg ! balance of energy in each snow+soil layer case('balanceSnowNrg' ); get_ixdiag = iLookDIAG%balanceSnowNrg ! balance of energy in the snow case('balanceSoilNrg' ); get_ixdiag = iLookDIAG%balanceSoilNrg ! balance of energy in the soil case('balanceVegMass' ); get_ixdiag = iLookDIAG%balanceVegMass ! balance of water in the vegetation + case('balanceLayerMass' ); get_ixdiag = iLookDIAG%balanceLayerMass ! balance of water in each snow+soil layer case('balanceSnowMass' ); get_ixdiag = iLookDIAG%balanceSnowMass ! balance of water in the snow case('balanceSoilMass' ); get_ixdiag = iLookDIAG%balanceSoilMass ! balance of water in the soil case('balanceAqMass' ); get_ixdiag = iLookDIAG%balanceAqMass ! balance of water in the aquifer diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index db5c43c23..0f933c073 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -442,14 +442,16 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%wallClockTime) = var_info('wallClockTime' , 'wall clock time' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%meanStepSize) = var_info('meanStepSize' , 'mean time step size over data window' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! balances - diag_meta(iLookDIAG%balanceCasNrg) = var_info('balanceCasNrg' , 'balance of energy in the canopy air space' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%balanceVegNrg) = var_info('balanceVegNrg' , 'balance of energy in the vegetation' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%balanceSnowNrg) = var_info('balanceSnowNrg' , 'balance of energy in the snow' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%balanceSoilNrg) = var_info('balanceSoilNrg' , 'balance of energy in the soil' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%balanceVegMass) = var_info('balanceVegMass' , 'balance of water in the vegetation' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%balanceSnowMass) = var_info('balanceSnowMass' , 'balance of water in the snow' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%balanceSoilMass) = var_info('balanceSoilMass' , 'balance of water in the soil' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%balanceAqMass) = var_info('balanceAqMass' , 'balance of water in the aquifer' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceCasNrg) = var_info('balanceCasNrg' , 'balance of energy in the canopy air space on data window' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceVegNrg) = var_info('balanceVegNrg' , 'balance of energy in the vegetation on data window' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceLayerNrg) = var_info('balanceLayerNrg' , 'balance of energy in each snow+soil layer on substep' , 'W m-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceSnowNrg) = var_info('balanceSnowNrg' , 'balance of energy in the snow on data window' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceSoilNrg) = var_info('balanceSoilNrg' , 'balance of energy in the soil on data window' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceVegMass) = var_info('balanceVegMass' , 'balance of water in the vegetation on data window' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceLayerMass) = var_info('balanceLayerMass' , 'balance of water in each snow+soil layer on substep' , 'kg m-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceSnowMass) = var_info('balanceSnowMass' , 'balance of water in the snow on data window' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceSoilMass) = var_info('balanceSoilMass' , 'balance of water in the soil on data window' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceAqMass) = var_info('balanceAqMass' , 'balance of water in the aquifer on data window' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! ----- ! * local model fluxes... ! ----------------------- diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index f21134bff..e36e08bbe 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -469,9 +469,11 @@ MODULE var_lookup ! balances integer(i4b) :: balanceCasNrg = integerMissing ! balance of energy in the canopy air space (W m-2) integer(i4b) :: balanceVegNrg = integerMissing ! balance of energy in the vegetation (W m-2) + integer(i4b) :: balanceLayerNrg = integerMissing ! balance of energy in each snow+soil layer (W m-2) integer(i4b) :: balanceSnowNrg = integerMissing ! balance of energy in the snow (W m-2) integer(i4b) :: balanceSoilNrg = integerMissing ! balance of energy in the soil (W m-2) integer(i4b) :: balanceVegMass = integerMissing ! balance of water in the vegetation (kg m-2) + integer(i4b) :: balanceLayerMass = integerMissing ! balance of water in each snow+soil layer (kg m-2) integer(i4b) :: balanceSnowMass = integerMissing ! balance of water in the snow (kg m-2) integer(i4b) :: balanceSoilMass = integerMissing ! balance of water in the soil (kg m-2) integer(i4b) :: balanceAqMass = integerMissing ! balance of water in the aquifer (kg m-2) @@ -903,7 +905,8 @@ MODULE var_lookup 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,& 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,& 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,& - 91, 92, 93, 94, 95, 96, 97, 98, 99,100) + 91, 92, 93, 94, 95, 96, 97, 98, 99,100,& + 101,102) ! named variables: model fluxes type(iLook_flux), public,parameter :: iLookFLUX =iLook_flux ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 1c911f6db..14861b7f0 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -256,8 +256,10 @@ subroutine coupled_em(& real(rkind) :: balanceSoilET ! output from the soil zone real(rkind) :: balanceAquifer0 ! total aquifer storage at the start of the step (kg m-2) real(rkind) :: balanceAquifer1 ! total aquifer storage at the end of the step (kg m-2) - real(rkind) :: balanceNrg(4) ! substep balance of energy per domain - real(rkind) :: balanceMass(4) ! substep balance of mass per domain + real(rkind) :: innerBalance(4) ! inner step balances for domain with one layer + real(rkind) :: meanBalance(8) ! timestep-average balances for domain with one layer + real(rkind),allocatable :: innerBalanceLayerMass(:) ! inner step balances for domain with multiple layers + real(rkind),allocatable :: innerBalanceLayerNrg(:) ! inner step balances for domain with multiple layers ! test balance checks logical(lgt), parameter :: printBalance=.false. ! flag to print the balance checks real(rkind), allocatable :: liqSnowInit(:) ! volumetric liquid water conetnt of snow at the start of the time step @@ -366,6 +368,9 @@ subroutine coupled_em(& allocate(innerSoilCompress(nSoil)) meanSoilCompress = 0._rkind ! mean total soil compression + ! initialize the balance checks + meanBalance = 0._rkind + ! associate local variables with information in the data structures associate(& ! state variables in the vegetation canopy @@ -607,16 +612,6 @@ subroutine coupled_em(& dt_sub = dt_init dtSave = whole_step ! length of whole substep - ! initialize balances from inside solver - diag_data%var(iLookDIAG%balanceCasNrg)%dat(1) = 0._rkind ! balance of energy in the canopy air space - diag_data%var(iLookDIAG%balanceVegNrg)%dat(1) = 0._rkind ! balance of energy in the vegetation - diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = 0._rkind ! balance of energy in the snow - diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = 0._rkind ! balance of energy in the soil - diag_data%var(iLookDIAG%balanceVegMass)%dat(1) = 0._rkind ! balance of water in the vegetation - diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = 0._rkind ! balance of water in the snow - diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = 0._rkind ! balance of water in the soil - diag_data%var(iLookDIAG%balanceAqMass)%dat(1) = 0._rkind ! balance of water in the aquifer - ! initialize the number of sub-steps nsub = 0 nsub_success = 0 @@ -851,6 +846,9 @@ subroutine coupled_em(& end do innerEffRainfall = 0._rkind ! mean total effective rainfall over snow innerSoilCompress = 0._rkind ! mean total soil compression + innerBalance = 0._rkind ! mean total balance + allocate(innerBalanceLayerNrg(nLayers)); innerBalanceLayerNrg = 0._rkind ! mean total balance of energy in layers + allocate(innerBalanceLayerMass(nLayers)); innerBalanceLayerMass = 0._rkind ! mean total balance of mass in layers sumStepSize= 0._rkind ! initialize the sum of the step sizes endif ! (do_outer loop) @@ -890,8 +888,6 @@ subroutine coupled_em(& stepFailure, & ! intent(out): flag to denote that the coupled step failed ixSolution, & ! intent(out): solution method used in this iteration mean_step_dt_sub, & ! intent(out): mean solution step for the sub-step - balanceNrg, & ! intent(out): balance of energy per domain - balanceMass, & ! intent(out): balance of mass per domain err,cmessage) ! intent(out): error code and error message ! check for all errors (error recovery within opSplittin) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if @@ -919,6 +915,8 @@ subroutine coupled_em(& endif ! try again, restart step deallocate(mLayerVolFracIceInit) + deallocate(innerBalanceLayerNrg) + deallocate(innerBalanceLayerMass) cycle substeps endif @@ -1072,16 +1070,36 @@ subroutine coupled_em(& innerSoilCompress(:) = innerSoilCompress(:) + diag_data%var(iLookDIAG%mLayerCompress)%dat(:)*dt_wght if (nSnow>0) innerEffRainfall = innerEffRainfall + ( flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) + flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) )*dt_wght - ! sum the balance of energy and water per domain - diag_data%var(iLookDIAG%balanceCasNrg)%dat(1) = diag_data%var(iLookDIAG%balanceCasNrg)%dat(1) + balanceNrg(1)*dt_wght ! balance of energy in the canopy air space - diag_data%var(iLookDIAG%balanceVegNrg)%dat(1) = diag_data%var(iLookDIAG%balanceVegNrg)%dat(1) + balanceNrg(2)*dt_wght ! balance of energy in the vegetation - diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) + balanceNrg(3)*dt_wght ! balance of energy in the snow - diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) + balanceNrg(4)*dt_wght ! balance of energy in the soil - diag_data%var(iLookDIAG%balanceVegMass)%dat(1) = diag_data%var(iLookDIAG%balanceVegMass)%dat(1) + balanceMass(1)*dt_wght ! balance of water in the vegetation - diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) + balanceMass(2)*dt_wght ! balance of water in the snow - diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) + balanceMass(3)*dt_wght ! balance of water in the soil - diag_data%var(iLookDIAG%balanceAqMass)%dat(1) = diag_data%var(iLookDIAG%balanceAqMass)%dat(1) + balanceMass(4)*dt_wght ! balance of water in the aquifer - + ! sum the balance of energy and water per state + innerBalance(1) = innerBalance(1) + diag_data%var(iLookDIAG%balanceCasNrg)%dat(1)*dt_wght + innerBalance(2) = innerBalance(2) + diag_data%var(iLookDIAG%balanceVegNrg)%dat(1)*dt_wght + innerBalance(3) = innerBalance(3) + diag_data%var(iLookDIAG%balanceVegMass)%dat(1)*dt_wght + innerBalance(4) = innerBalance(4) + diag_data%var(iLookDIAG%balanceAqMass)%dat(1)*dt_wght + innerBalanceLayerNrg(:) = innerBalanceLayerNrg(:) + diag_data%var(iLookDIAG%balanceLayerNrg)%dat(:)*dt_wght + innerBalanceLayerMass(:) = innerBalanceLayerMass(:) + diag_data%var(iLookDIAG%balanceLayerMass)%dat(:)*dt_wght + + ! save balance of energy and water per snow+soil layer after inner step, since can change nLayers with outer steps + diag_data%var(iLookDIAG%balanceLayerNrg)%dat(:) = innerBalanceLayerNrg(:) + diag_data%var(iLookDIAG%balanceLayerMass)%dat(:) = innerBalanceLayerMass(:) + + ! compute the mean balance of energy and water per entire snow and soil domain + diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = 0._rkind + diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = 0._rkind + diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = 0._rkind + diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = 0._rkind + do iLayer=1,nLayers + select case (indx_data%var(iLookINDEX%layerType)%dat(iLayer)) + case (iname_snow) + diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) + innerBalanceLayerNrg(iLayer)/nSnow + diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) + innerBalanceLayerMass(iLayer)/nSnow + case (iname_soil) + diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) + innerBalanceLayerNrg(iLayer)/nSoil + diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) + innerBalanceLayerMass(iLayer)/nSoil + end select + end do + deallocate(innerBalanceLayerNrg) + deallocate(innerBalanceLayerMass) + ! increment sub-step accepted step dt_solvInner = dt_solvInner + dt_sub dt_solv = dt_solv + dt_sub @@ -1100,6 +1118,15 @@ subroutine coupled_em(& meanLatHeatCanopyEvap = meanLatHeatCanopyEvap + sumLatHeatCanopyEvap/data_step meanSenHeatCanopy = meanSenHeatCanopy + sumSenHeatCanopy/data_step meanSoilCompress(:) = meanSoilCompress(:) + innerSoilCompress(:)*dt_wght + meanBalance(1) = meanBalance(1) + innerBalance(1)*dt_wght + meanBalance(2) = meanBalance(2) + innerBalance(2)*dt_wght + meanBalance(3) = meanBalance(3) + innerBalance(3)*dt_wght + meanBalance(4) = meanBalance(4) + innerBalance(4)*dt_wght + meanBalance(5) = meanBalance(5) + diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1)*dt_wght + meanBalance(6) = meanBalance(6) + diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1)*dt_wght + meanBalance(7) = meanBalance(7) + diag_data%var(iLookDIAG%balanceSnowMass)%dat(1)*dt_wght + meanBalance(8) = meanBalance(8) + diag_data%var(iLookDIAG%balanceSoilMass)%dat(1)*dt_wght + effRainfall = effRainfall + innerEffRainfall*dt_wght flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopySublimation))%dat(1) = meanCanopySublimation flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarLatHeatCanopyEvap))%dat(1) = meanLatHeatCanopyEvap @@ -1188,6 +1215,16 @@ subroutine coupled_em(& deallocate(innerSoilCompress) deallocate(meanSoilCompress) + ! save balance of energy and water per single layer domain + diag_data%var(iLookDIAG%balanceCasNrg)%dat(1) = meanBalance(1) + diag_data%var(iLookDIAG%balanceVegNrg)%dat(1) = meanBalance(2) + diag_data%var(iLookDIAG%balanceVegMass)%dat(1) = meanBalance(3) + diag_data%var(iLookDIAG%balanceAqMass)%dat(1) = meanBalance(4) + diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = meanBalance(5) + diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = meanBalance(6) + diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = meanBalance(7) + diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = meanBalance(8) + ! *********************************************************************************************************************************** ! --- ! *** balance checks... diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 9e1990c86..b5e5eeb9f 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -184,8 +184,6 @@ subroutine opSplittin(& stepFailure, & ! intent(out): flag to denote step failure ixSolution, & ! intent(out): solution method used in this iteration mean_step_dt, & ! intent(out): mean solution step for the time step - balanceNrg, & ! intent(out): balance of energy per domain - balanceMass, & ! intent(out): balance of mass per domain err,message) ! intent(out): error code and error message ! --------------------------------------------------------------------------------------- ! structure allocations @@ -226,8 +224,6 @@ subroutine opSplittin(& logical(lgt),intent(out) :: stepFailure ! flag to denote step failure integer(i4b),intent(out) :: ixSolution ! index of solution method (1,2) real(rkind),intent(out) :: mean_step_dt ! mean solution step for the time step - real(rkind),intent(out) :: balanceNrg(4) ! balance of energy per domain - real(rkind),intent(out) :: balanceMass(4) ! balance of mass per domain integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! --------------------------------------------------------------------------------------- @@ -466,8 +462,7 @@ subroutine opSplittin(& call initialize_varSubstep call varSubstep(in_varSubstep,io_varSubstep,& ! intent(inout): class objects for model control model_decisions,lookup_data,type_data,attr_data,forc_data,mpar_data,& ! intent(inout): data structures for model properties - indx_data,prog_data,diag_data,flux_data,flux_mean,deriv_data,bvar_data,& - balanceNrg,balanceMass, & ! intent(inout): balances + indx_data,prog_data,diag_data,flux_data,flux_mean,deriv_data,bvar_data,& ! intent(inout): data structures for model variables out_varSubstep) ! intent(out): class object for model control call finalize_varSubstep if (err/=0) then; message=trim(message)//trim(cmessage); if (err>0) return; end if ! error control diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 86545ce9e..e684e24f1 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -29,12 +29,6 @@ module summaSolve4ida_module ! access the global print flag USE globalData,only:globalPrintFlag -! domain types -USE globalData,only:iname_cas ! named variables for the canopy air space -USE globalData,only:iname_veg ! named variables for vegetation -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil - ! access missing values USE globalData,only:integerMissing ! missing integer USE globalData,only:realMissing ! missing double precision number @@ -142,8 +136,7 @@ subroutine summaSolve4ida( & nSteps, & ! intent(out): number of time steps taken in solver stateVec, & ! intent(out): model state vector stateVecPrime, & ! intent(out): derivative of model state vector - balanceNrg, & ! intent(out): balance of energy per domain - balanceMass, & ! intent(out): balance of mass per domain + balance, & ! intent(inout): balance per state err,message) ! intent(out): error control !======= Inclusions =========== @@ -218,8 +211,7 @@ subroutine summaSolve4ida( & logical(lgt),intent(out) :: idaSucceeds ! flag to indicate if IDA is successful logical(lgt),intent(inout) :: tooMuchMelt ! flag to denote that there was too much melt ! output: residual terms and balances - real(rkind),intent(out) :: balanceNrg(4) ! balance of energy per domain - real(rkind),intent(out) :: balanceMass(4) ! balance of mass per domain + real(rkind),intent(inout) :: balance(:) ! balance per state ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -488,10 +480,6 @@ subroutine summaSolve4ida( & tretPrev = tret(1) nSteps = 0 ! initialize number of time steps taken in solver - ! initialize balances - balanceNrg = 0._rkind - balanceMass = 0._rkind - do while(tret(1) < dt_cur) ! call this at beginning of step to reduce root bouncing (only looking in one direction) @@ -612,14 +600,11 @@ subroutine summaSolve4ida( & if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! compute energy balance - if(ixCasNrg/=integerMissing) balanceNrg(1) = balanceNrg(1) + eqns_data%scalarCanairEnthalpyTrial - eqns_data%scalarCanairEnthalpyPrev - eqns_data%fluxVec(ixVegNrg)*dt_diff/dt - if(ixVegNrg/=integerMissing) balanceNrg(2) = balanceNrg(2) + eqns_data%scalarCanopyEnthalpyTrial - eqns_data%scalarCanopyEnthalpyPrev - eqns_data%fluxVec(ixVegNrg)*dt_diff/dt + if(ixCasNrg/=integerMissing) balance(ixCasNrg) = balance(ixCasNrg) + (eqns_data%scalarCanairEnthalpyTrial - eqns_data%scalarCanairEnthalpyPrev - eqns_data%fluxVec(ixCasNrg)*dt_diff)*dt_diff/dt + if(ixVegNrg/=integerMissing) balance(ixVegNrg) = balance(ixVegNrg) + (eqns_data%scalarCanopyEnthalpyTrial - eqns_data%scalarCanopyEnthalpyPrev - eqns_data%fluxVec(ixVegNrg)*dt_diff)*dt_diff/dt if(nSnowSoilNrg>0)then - do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - select case(layerType(i)) - case(iname_snow); balanceNrg(3) = balanceNrg(3) + (eqns_data%mLayerEnthalpyTrial(i) - eqns_data%mLayerEnthalpyPrev(i) - eqns_data%fluxVec(ixSnowSoilNrg(i))*dt_diff/dt)/nSnow - case(iname_soil); balanceNrg(4) = balanceNrg(4) + (eqns_data%mLayerEnthalpyTrial(i) - eqns_data%mLayerEnthalpyPrev(i) - eqns_data%fluxVec(ixSnowSoilNrg(i))*dt_diff/dt)/(nLayers-nSnow) - end select + do i=1,nSnowSoilNrg + if(ixSnowSoilNrg(i)/=integerMissing) balance(ixSnowSoilNrg(i)) = balance(ixSnowSoilNrg(i)) + (eqns_data%mLayerEnthalpyTrial(i) - eqns_data%mLayerEnthalpyPrev(i) - eqns_data%fluxVec(ixSnowSoilNrg(i))*dt_diff)*dt_diff/dt enddo endif endif @@ -631,16 +616,13 @@ subroutine summaSolve4ida( & ! compute mass balance ! resVec is the instanteous residual vector from the solver - if(ixVegHyd/=integerMissing) balanceMass(1) = balanceMass(1) + eqns_data%resVec(ixVegHyd) + if(ixVegHyd/=integerMissing) balance(ixVegHyd) = balance(ixVegHyd) + eqns_data%resVec(ixVegHyd)*dt_diff/dt if(nSnowSoilHyd>0)then - do concurrent (i=1:nLayers,ixSnowSoilHyd(i)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - select case(layerType(i)) - case(iname_snow); balanceMass(2) = balanceMass(2) + eqns_data%resVec(ixSnowSoilHyd(i))*dt_diff/dt/nSnow - case(iname_soil); balanceMass(3) = balanceMass(3) + eqns_data%resVec(ixSnowSoilHyd(i))*dt_diff/dt/(nLayers-nSnow) - end select + do i=1,nSnowSoilHyd + if(ixSnowSoilHyd(i)/=integerMissing) balance(ixSnowSoilHyd(i)) = balance(ixSnowSoilHyd(i)) + eqns_data%resVec(ixSnowSoilHyd(i))*dt_diff/dt enddo endif - if(ixAqWat/=integerMissing) balanceMass(4) = balanceMass(4) + eqns_data%resVec(ixAqWat) + if(ixAqWat/=integerMissing) balance(ixAqWat) = balance(ixAqWat) + eqns_data%resVec(ixAqWat)*dt_diff/dt endif ! save required quantities for next step diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 01b4f3b21..b34ea1caa 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -38,11 +38,6 @@ module systemSolv_module USE globalData,only: iJac1 ! first layer of the Jacobian to print USE globalData,only: iJac2 ! last layer of the Jacobian to print -! domain types -USE globalData,only:iname_veg ! named variables for vegetation -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil - ! state variable type USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy @@ -151,8 +146,7 @@ subroutine systemSolv(& resVec, & ! intent(out): new residual vector untappedMelt, & ! intent(out): un-tapped melt energy (J m-3 s-1) ! output: balances (only computed at this level for ida) - balanceNrg, & ! intent(out): balance of energy per domain - balanceMass, & ! intent(out): balance of mass per domain + balance, & ! intent(out): balance of energy per state ! output: model control niter, & ! intent(out): number of iterations taken (numrec) nSteps, & ! intent(out): number of time steps taken in solver @@ -214,8 +208,7 @@ subroutine systemSolv(& real(qp),intent(out) :: resVec(nState) ! NOTE: qp ! residual vector real(rkind),intent(out) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) ! output: balances (only computed at this level for ida) - real(rkind),intent(out) :: balanceNrg(4) ! balance of energy per domain - real(rkind),intent(out) :: balanceMass(4) ! balance of mass per domain + real(rkind),intent(out) :: balance(nState) ! balance per state ! output: model control integer(i4b),intent(out) :: niter ! number of iterations taken integer(i4b),intent(out) :: nSteps ! number of time steps taken in solver @@ -323,8 +316,7 @@ subroutine systemSolv(& fluxVec = 0._rkind resSink = 0._rkind resVec = 0._rkind - balanceNrg = 0._rkind - balanceMass = 0._rkind + balance = 0._rkind ! ***** ! (0) PRELIMINARIES... @@ -578,10 +570,9 @@ subroutine systemSolv(& sunSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt nSteps, & ! intent(out): number of time steps taken in solver - stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step - stateVecPrime, & ! intent(out): derivative of model state vector (y') at the end of the data time step - balanceNrg, & ! intent(out): balance of energy per domain - balanceMass, & ! intent(out): balance of mass per domain + stateVecNew, & ! intent(inout): model state vector (y) at the end of the data time step + stateVecPrime, & ! intent(inout): derivative of model state vector (y') at the end of the data time step + balance, & ! intent(inout): balance per state err,cmessage) ! intent(out): error control ! check if IDA is successful, only fail outright in the case of a non-recoverable error if( .not.sunSucceeds )then diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index 7fccee75e..f715d7cf3 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -673,7 +673,7 @@ subroutine t2enthalpy_addphase(& ! identify domain select case(ixDomainType) - case(iname_cas); cycle ! aquifer: do nothing + case(iname_cas); cycle ! canopy air space: do nothing case(iname_veg) ! association to necessary variables for vegetation diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 62ee50174..1a986ec7d 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -116,9 +116,6 @@ subroutine varSubstep(& flux_mean, & ! intent(inout) : mean model fluxes for a local HRU deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables bvar_data, & ! intent(in) : model variables for the local basin - ! input/output: balances - sumBalanceNrg, & ! intent(out) : step balance of energy per domain - sumBalanceMass, & ! intent(out) : step balance of mass per domain ! output: model control out_varSubstep) ! intent(out) : model control ! --------------------------------------------------------------------------------------- @@ -151,9 +148,7 @@ subroutine varSubstep(& type(var_dlength),intent(inout) :: flux_mean ! mean model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin - real(rkind),intent(out) :: sumBalanceNrg(:) ! step balance of energy per domain - real(rkind),intent(out) :: sumBalanceMass(:) ! step balance of mass per domain - ! output: model control + ! output: model control type(out_type_varSubstep),intent(out) :: out_varSubstep ! model control ! --------------------------------------------------------------------------------------- ! * general local variables @@ -171,7 +166,7 @@ subroutine varSubstep(& real(rkind) :: dtSubstep ! length of a substep (s) real(rkind) :: maxstep ! maximum time step length (seconds) integer(i4b) :: nSteps ! number of time steps taken in solver - ! adaptive sub-stepping for the i solution + ! adaptive sub-stepping for the solution logical(lgt) :: failedSubstep ! flag to denote success of substepping for a given split integer(i4b) :: niter ! number of iterations taken integer(i4b),parameter :: n_inc=5 ! minimum number of iterations to increase time step @@ -195,11 +190,11 @@ subroutine varSubstep(& real(rkind) :: sumSoilCompress ! sum of total soil compression real(rkind),allocatable :: sumLayerCompress(:) ! sum of soil compression by layer ! balances and residual vectors - real(rkind) :: fluxVec(in_varSubstep % nSubset) ! flux vector (mixed units) - real(rkind) :: resSink(in_varSubstep % nSubset) ! sink terms on the RHS of the state equation - real(qp) :: resVec(in_varSubstep % nSubset) ! residual vector - real(rkind),dimension(4) :: balanceNrg ! substep balance of energy per domain - real(rkind),dimension(4) :: balanceMass ! substep balance of mass per domain + real(rkind) :: fluxVec(in_varSubstep % nSubset) ! substep flux vector (mixed units) + real(rkind) :: resSink(in_varSubstep % nSubset) ! substep sink terms on the RHS of the state equation + real(qp) :: resVec(in_varSubstep % nSubset) ! substep residual vector + real(rkind) :: balance(in_varSubstep % nSubset) ! substep balance + real(rkind) :: sumBalance(in_varSubstep % nSubset) ! sum of substeps balance logical(lgt),parameter :: checkMassBalance = .true. ! flag to check the mass balance logical(lgt),parameter :: checkNrgBalance = .true. ! flag to check the energy balance @@ -231,6 +226,18 @@ subroutine varSubstep(& nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! get indices for balances + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain + ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the soil domain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg)%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd)%dat(1) ,& ! intent(in): [i4b] number of hydrology state variables in the snow+soil domain ! mapping between state vectors and control volumes ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat ,& ! intent(in): [i4b(:)] list of indices for all active layers (inactive=integerMissing) ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset @@ -259,10 +266,6 @@ subroutine varSubstep(& ) ! end association with variables in the data structures ! ********************************************************************************************************************************************************* - ! initialize balances - sumBalanceNrg = 0._rkind - sumBalanceMass = 0._rkind - ! initialize error control err=0; message='varSubstep/' @@ -292,6 +295,9 @@ subroutine varSubstep(& sumSoilCompress = 0._rkind ! total soil compression allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._rkind ! soil compression by layer + ! initialize balances + sumBalance = 0._rkind + ! define the first flux call in a splitting operation firstSplitOper = (.not.scalarSolution .or. iStateSplit==1) @@ -363,8 +369,7 @@ subroutine varSubstep(& resVec, & ! intent(out): residual vector untappedMelt, & ! intent(out): un-tapped melt energy (J m-3 s-1) ! output: balances (only computed at this level for ida) - balanceNrg, & ! intent(out): balance of energy per domain - balanceMass, & ! intent(out): balance of mass per domain + balance, & ! intent(out): balance per state variable ! output model control niter, & ! intent(out): number of iterations taken (numrec) nSteps, & ! intent(out): number of time steps taken in solver @@ -437,7 +442,7 @@ subroutine varSubstep(& call updateProg(dtSubstep,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states doAdjustTemp,computeVegFlux,checkMassBalance, checkNrgBalance,ixHowHeatCap == enthalpyFD, & ! input: model control model_decisions,lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures - fluxVec,resVec,balanceNrg,balanceMass,waterBalanceError,nrgFluxModified,err,message) ! output: balances, flags, and error control + fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! output: balances, flags, and error control if(err/=0)then message=trim(message)//trim(cmessage) if(err>0) return @@ -471,9 +476,25 @@ subroutine varSubstep(& endif endif ! if errors in prognostic update + + ! add balances to the total balances + if(ixCasNrg/=integerMissing) sumBalance(ixCasNrg) = sumBalance(ixCasNrg) + dtSubstep*balance(ixCasNrg) + if(ixVegNrg/=integerMissing) sumBalance(ixVegNrg) = sumBalance(ixVegNrg) + dtSubstep*balance(ixVegNrg) + if(nSnowSoilNrg>0) then + do ixLayer=1,nSnowSoilNrg + if(ixSnowSoilNrg(ixLayer)/=integerMissing) sumBalance(ixSnowSoilNrg(ixLayer)) = sumBalance(ixSnowSoilNrg(ixLayer)) + dtSubstep*balance(ixSnowSoilNrg(ixLayer)) + end do + endif + if(ixVegHyd/=integerMissing) sumBalance(ixVegHyd) = sumBalance(ixVegHyd) + dtSubstep*balance(ixVegHyd) + if(nSnowSoilHyd>0) then + do ixLayer=1,nSnowSoilHyd + if(ixSnowSoilHyd(ixLayer)/=integerMissing) sumBalance(ixSnowSoilHyd(ixLayer)) = sumBalance(ixSnowSoilHyd(ixLayer)) + dtSubstep*balance(ixSnowSoilHyd(ixLayer)) + end do + endif + if(ixAqWat/=integerMissing) sumBalance(ixAqWat) = sumBalance(ixAqWat) + dtSubstep*balance(ixAqWat) ! get the total energy fluxes (modified in updateProg), have to do differently - if(nrgFluxModified .or. indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then + if(nrgFluxModified .or. ixVegNrg/=integerMissing)then sumCanopyEvaporation = sumCanopyEvaporation + dtSubstep*flux_temp%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dtSubstep*flux_temp%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) sumSenHeatCanopy = sumSenHeatCanopy + dtSubstep*flux_temp%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) @@ -484,13 +505,13 @@ subroutine varSubstep(& endif ! if energy fluxes were modified ! get the total soil compression - if (count(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat/=integerMissing)>0) then + if (count(ixSoilOnlyHyd/=integerMissing)>0) then ! scalar compression if(.not.scalarSolution .or. iStateSplit==nSoil)& sumSoilCompress = sumSoilCompress + dtSubstep*diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression ! vector compression do iSoil=1,nSoil - if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& + if(ixSoilOnlyHyd(iSoil)/=integerMissing)& sumLayerCompress(iSoil) = sumLayerCompress(iSoil) + dtSubstep*diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) ! soil compression in layers end do endif @@ -529,9 +550,6 @@ subroutine varSubstep(& endif ! (if the flux is desired) end do ! (loop through fluxes) - ! If did not compute the layer residual vector on this step, added balance - sumBalanceNrg = sumBalanceNrg + balanceNrg*dt_wght - sumBalanceMass = sumBalanceMass + balanceMass*dt_wght ! increment the number of substeps nSubsteps = nSubsteps + nSteps @@ -564,11 +582,27 @@ subroutine varSubstep(& ! save the soil compression diagnostics as averages diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress/dt do iSoil=1,nSoil - if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& + if(ixSoilOnlyHyd(iSoil)/=integerMissing)& diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) = sumLayerCompress(iSoil)/dt end do deallocate(sumLayerCompress) + ! save the balance diagnostics as averages + if(ixCasNrg/=integerMissing) diag_data%var(iLookDIAG%balanceCasNrg)%dat(1) = sumBalance(ixCasNrg)/dt + if(ixVegNrg/=integerMissing) diag_data%var(iLookDIAG%balanceVegNrg)%dat(1) = sumBalance(ixVegNrg)/dt + if(nSnowSoilNrg>0) then + do ixLayer=1,nSnowSoilNrg + if(ixSnowSoilNrg(ixLayer)/=integerMissing) diag_data%var(iLookDIAG%balanceLayerNrg)%dat(ixLayer) = sumBalance(ixSnowSoilNrg(ixLayer))/dt + end do + endif + if(ixVegHyd/=integerMissing) diag_data%var(iLookDIAG%balanceVegMass)%dat(1) = sumBalance(ixVegHyd)/dt + if(nSnowSoilHyd>0) then + do ixLayer=1,nSnowSoilHyd + if(ixSnowSoilHyd(ixLayer)/=integerMissing) diag_data%var(iLookDIAG%balanceLayerMass)%dat(ixLayer) = sumBalance(ixSnowSoilHyd(ixLayer))/dt + end do + endif + if(ixAqWat/=integerMissing) diag_data%var(iLookDIAG%balanceAqMass)%dat(1) = sumBalance(ixAqWat)/dt + ! update error codes if (failedMinimumStep) then err=-20 ! negative = recoverable error @@ -585,7 +619,7 @@ end subroutine varSubstep subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states doAdjustTemp,computeVegFlux,checkMassBalance, checkNrgBalance,use_enthalpyFD, & ! input: model control model_decisions,lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures - fluxVec,resVec,balanceNrg,balanceMass,waterBalanceError,nrgFluxModified,err,message) ! output: balances, flags, and error control + fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! input-output: balances, flags, and error control USE getVectorz_module,only:varExtract ! extract variables from the state vector #ifdef SUNDIALS_ACTIVE USE updateVarsWithPrime_module,only:updateVarsWithPrime ! update prognostic variables @@ -619,8 +653,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! balances, flags, and error control real(rkind) ,intent(in) :: fluxVec(:) ! flux vector (mixed units) real(qp) ,intent(in) :: resVec(:) ! NOTE: qp ! residual vector - real(rkind) ,intent(inout) :: balanceNrg(4) ! balance of energy per domain - real(rkind) ,intent(inout) :: balanceMass(4) ! balance of mass per domain + real(rkind) ,intent(inout) :: balance(:) ! balance of energy per domain logical(lgt) ,intent(out) :: waterBalanceError ! flag to denote that there is a water balance error logical(lgt) ,intent(out) :: nrgFluxModified ! flag to denote that the energy fluxes were modified integer(i4b) ,intent(out) :: err ! error code @@ -701,9 +734,8 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the soil domain ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for energy state variables in the snow+soil domain ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in) : [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in) : [i4b] number of hydrology state variables in the snow+soil domain - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in) : [i4b(:)] named variables defining the type of layer in snow+soil domain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg)%dat(1) ,& ! intent(in) : [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd)%dat(1) ,& ! intent(in) : [i4b] number of hydrology state variables in the snow+soil domain ! get indices for the un-tapped melt ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in) : [i4b(:)] list of indices for all energy states ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in) : [i4b(:)] indices defining the domain of the state (iname_veg, iname_snow, iname_soil) @@ -981,17 +1013,14 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! compute energy balance if didn't do inside solver substeps select case(ixNumericalMethod) - case(ida); ! do nothing + case(ida); ! do nothing, already computed case(kinsol, numrec) ! compute energy balance, maybe should use to check for step reduction - if(ixCasNrg/=integerMissing) balanceNrg(1) = scalarCanairEnthalpyTrial - scalarCanairEnthalpy - fluxVec(ixVegNrg)*dt - if(ixVegNrg/=integerMissing) balanceNrg(2) = scalarCanopyEnthalpyTrial - scalarCanopyEnthalpy - fluxVec(ixVegNrg)*dt + if(ixCasNrg/=integerMissing) balance(ixCasNrg) = scalarCanairEnthalpyTrial - scalarCanairEnthalpy - fluxVec(ixCasNrg)*dt + if(ixVegNrg/=integerMissing) balance(ixVegNrg) = scalarCanopyEnthalpyTrial - scalarCanopyEnthalpy - fluxVec(ixVegNrg)*dt if(nSnowSoilNrg>0)then - do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - select case(layerType(i)) - case(iname_snow); balanceNrg(3) = (mLayerEnthalpyTrial(i) - mLayerEnthalpy(i) - fluxVec(ixSnowSoilNrg(i))*dt)/nSnow - case(iname_soil); balanceNrg(4) = (mLayerEnthalpyTrial(i) - mLayerEnthalpy(i) - fluxVec(ixSnowSoilNrg(i))*dt)/(nLayers-nSnow) - end select + do i=1,nSnowSoilNrg + balance(ixSnowSoilNrg(i)) = mLayerEnthalpyTrial(i) - mLayerEnthalpy(i) - fluxVec(ixSnowSoilNrg(i))*dt enddo endif end select @@ -1103,16 +1132,13 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! compute mass balance, maybe should use to check for step reduction ! resVec is the residual vector from the solver over dt - if(ixVegHyd/=integerMissing) balanceMass(1) = balanceMass(1) + resVec(ixVegHyd) + if(ixVegHyd/=integerMissing) balance(ixVegHyd) = resVec(ixVegHyd) if(nSnowSoilHyd>0)then - do concurrent (i=1:nLayers,ixSnowSoilHyd(i)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - select case(layerType(i)) - case(iname_snow); balanceMass(2) = balanceMass(2) + resVec(ixSnowSoilHyd(i))/nSnow - case(iname_soil); balanceMass(3) = balanceMass(3) + resVec(ixSnowSoilHyd(i))/(nLayers-nSnow) - end select - enddo + do i=1,nSnowSoilHyd + if(ixSnowSoilHyd(i)/=integerMissing) balance(ixSnowSoilHyd(i)) = resVec(ixSnowSoilHyd(i)) + end do endif - if(ixAqWat/=integerMissing) balanceMass(4) = balanceMass(4) + resVec(ixAqWat)*dt + if(ixAqWat/=integerMissing) balance(ixAqWat) = resVec(ixAqWat) end select endif ! if checking the mass balance From 5bc8ce88df97f408dd47ec5bf1f0a1f3ffa28f2f Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 23 Nov 2023 05:22:28 -0600 Subject: [PATCH 1002/1472] Simplified stateSplit loop in opSplittin -- started implementing more flexible loop control. --- build/source/engine/opSplittin.f90 | 116 ++++++++++++++--------------- 1 file changed, 57 insertions(+), 59 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 0b3950cdc..648f4e9d3 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -292,6 +292,8 @@ subroutine opSplittin(& real(rkind) :: mean_step_state ! mean step over the state (with or without domain splits) real(rkind) :: mean_step_solution ! mean step for a solution (scalar or vector) logical(lgt) :: addFirstFlux ! flag to add the first flux to the mask + ! loop control + logical(lgt) :: exit_coupling,exit_stateTypeSplitting,exit_stateThenDomain,exit_domainSplit,exit_solution,exit_stateSplit ! ------------------------ classes for subroutine arguments (classes defined in data_types module) ------------------------ ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** type(in_type_stateFilter) :: in_stateFilter; type(out_type_stateFilter) :: out_stateFilter; ! stateFilter arguments @@ -299,51 +301,32 @@ subroutine opSplittin(& type(in_type_varSubstep) :: in_varSubstep; type(io_type_varSubstep) :: io_varSubstep; type(out_type_varSubstep) :: out_varSubstep; ! varSubstep arguments ! --------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="opSplittin/" + call initialize_opSplittin ! initial setup steps -- select coupling options and allocate memory + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! return if error in initialize step - associate(ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision) ! intent(in): [i4b] choice of numerical solver - ! we just solve the fully coupled problem if IDA for now, splitting can happen otherwise - select case(ixNumericalMethod) - case(ida); nCoupling = 1 - case(kinsol, numrec); nCoupling = 2 - end select - end associate - - ! *** initialize *** - call initialize_opSplittin - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! ========================================================================================================================================== - ! loop through different coupling strategies - coupling: do ixCoupling=1,nCoupling + coupling: do ixCoupling=1,nCoupling ! loop through different coupling strategies - call initialize_stateTypeSplitting_loop - if (return_flag.eqv..true.) return ! return if error occurs during initialization + call initialize_stateTypeSplitting_loop ! setup steps for stateTypeSplitting loop + if (return_flag.eqv..true.) return ! return if error occurs during initialization - ! state splitting loop - stateTypeSplitting: do iStateTypeSplit=1,nStateTypeSplit + stateTypeSplitting: do iStateTypeSplit=1,nStateTypeSplit ! state splitting loop - ! identify state-specific variables for a given state split - call initialize_stateThenDomain_loop + call initialize_stateThenDomain_loop ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split ! first try the state type split, then try the domain split within a given state type stateThenDomain: do ixStateThenDomain=1,1+tryDomainSplit ! 1=state type split; 2=domain split within a given state type - call initialize_domainSplit_loop + call initialize_domainSplit_loop ! setup steps for domainSplit loop if (return_flag.eqv..true.) return ! return if error occurs during initialization - ! domain splitting loop - domainSplit: do iDomainSplit=1,nDomainSplit + domainSplit: do iDomainSplit=1,nDomainSplit ! domain splitting loop - ! trial with the vector then scalar solution - solution: do ixSolution=1,nSolutions + solution: do ixSolution=1,nSolutions ! trial with the vector then scalar solution - call initialize_stateSplit_loop + call initialize_stateSplit_loop ! setup steps for stateSplit loop if (return_flag.eqv..true.) return ! return if error occurs during initialization - ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) - stateSplit: do iStateSplit=1,nStateSplit + stateSplit: do iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) ! ----- ! * define state subsets for a given split... @@ -387,10 +370,6 @@ subroutine opSplittin(& ! --------------------------------------- call update_fluxMask if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! ******************************************************************************************************************************* - ! ***** trial with a given solution method... - ! ----- ! * solve variable subset for one time step... @@ -403,15 +382,14 @@ subroutine opSplittin(& ! solve variable subset for one full time step call initialize_varSubstep - call varSubstep(in_varSubstep,io_varSubstep,& ! intent(inout): class objects for model control + call varSubstep(in_varSubstep,io_varSubstep,& ! intent(inout): class objects for model control model_decisions,lookup_data,type_data,attr_data,forc_data,mpar_data,& ! intent(inout): data structures for model properties indx_data,prog_data,diag_data,flux_data,flux_mean,deriv_data,bvar_data,& out_varSubstep) ! intent(out): class object for model control call finalize_varSubstep if (err/=0) then; message=trim(message)//trim(cmessage); if (err>0) return; end if ! error control - ! determine whether solution is a success or a failure - call judge_solution + call judge_solution ! determine whether solution is a success or a failure if (return_flag.eqv..true.) return ! return for a recovering solution ! try the fully split solution if failed to converge with a minimum time step in the coupled solution @@ -433,48 +411,44 @@ subroutine opSplittin(& err=20; return end if - ! success = exit solution - if (.not.failure) then - ! sum the mean steps for the successful solution type - mean_step_solution = mean_step_solution + (dt/nSubsteps)/nStateSplit - select case(ixStateThenDomain) - case(fullDomain); if (iStateSplit==nStateSplit) exit stateThenDomain - case(subDomain); if (iStateSplit==nStateSplit) exit solution - case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' - end select - else ! failure - call check_failure; return ! check reason for failure and return - end if ! success check + call success_check ! check for success + if (exit_stateThenDomain) exit stateThenDomain ! exit loops if necessary + if (exit_solution) exit solution end do stateSplit ! solution with split layers end do solution ! trial with the full layer solution then the split layer solution - ! sum the mean steps for the state over each domain split - mean_step_state = mean_step_state + mean_step_solution/nDomainSplit + call finalize_solution_loop ! final steps following solution loop - ! ***** trial with a given solution method... - ! ******************************************************************************************************************************* end do domainSplit ! domain type splitting loop - end do stateThenDomain ! switch between the state and the domain + end do stateThenDomain ! switch between the state type and domain type splitting call finalize_stateThenDomain_loop ! final steps following the stateThenDomain loop end do stateTypeSplitting ! state type splitting loop - ! success = exit the coupling loop - if (ixCoupling==fullyCoupled .and. .not.failure) exit coupling + if (ixCoupling==fullyCoupled .and. .not.failure) exit coupling ! success = exit the coupling loop end do coupling ! coupling method - ! *** Finalize *** - call finalize_opSplittin + call finalize_opSplittin ! final steps -- check variables and fluxes, and apply step halving if needed contains subroutine initialize_opSplittin ! *** initial steps for opSplittin subroutine *** + ! initialize error control + err=0; message="opSplittin/" + + associate(ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision) ! intent(in): [i4b] choice of numerical solver + ! we just solve the fully coupled problem if IDA for now, splitting can happen otherwise + select case(ixNumericalMethod) + case(ida); nCoupling = 1 + case(kinsol, numrec); nCoupling = 2 + end select + end associate ! set the global print flag globalPrintFlag=.false. @@ -678,6 +652,12 @@ subroutine initialize_domainSplit_loop mean_step_state = 0._rkind ! initialize mean step for state end subroutine initialize_domainSplit_loop + subroutine finalize_solution_loop + ! *** final operations following solution loop *** + ! sum the mean steps for the state over each domain split + mean_step_state = mean_step_state + mean_step_solution/nDomainSplit + end subroutine finalize_solution_loop + subroutine initialize_stateSplit_loop ! *** initial operations to set up stateSplit loop *** return_flag=.false. ! initialize flag @@ -801,6 +781,24 @@ subroutine save_recover end do end subroutine save_recover + subroutine success_check + ! initialize flags + exit_stateThenDomain=.false. + exit_solution=.false. + ! success = exit solution + if (.not.failure) then + ! sum the mean steps for the successful solution type + mean_step_solution = mean_step_solution + (dt/nSubsteps)/nStateSplit + select case(ixStateThenDomain) + case(fullDomain); if (iStateSplit==nStateSplit) exit_stateThenDomain=.true. !exit stateThenDomain + case(subDomain); if (iStateSplit==nStateSplit) exit_solution=.true. !exit solution + case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' + end select + else ! failure + call check_failure; return ! check reason for failure and return + end if ! success check + end subroutine success_check + subroutine check_failure ! *** Analyze reason for failure *** if (ixSolution==scalar) then ! check that we did not fail for the scalar solution (last resort) From 94354cd4f76f2272971ecae7ca9eaf725bbd7c0a Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 24 Nov 2023 14:27:38 -0600 Subject: [PATCH 1003/1472] Implemented loop cycle flags in opSplittin - stateSplit loop modularization enhanced. --- build/source/engine/opSplittin.f90 | 64 ++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 648f4e9d3..418cfaa5d 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -294,6 +294,7 @@ subroutine opSplittin(& logical(lgt) :: addFirstFlux ! flag to add the first flux to the mask ! loop control logical(lgt) :: exit_coupling,exit_stateTypeSplitting,exit_stateThenDomain,exit_domainSplit,exit_solution,exit_stateSplit + logical(lgt) :: cycle_coupling,cycle_stateTypeSplitting,cycle_stateThenDomain,cycle_domainSplit,cycle_solution,cycle_stateSplit ! ------------------------ classes for subroutine arguments (classes defined in data_types module) ------------------------ ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** type(in_type_stateFilter) :: in_stateFilter; type(out_type_stateFilter) :: out_stateFilter; ! stateFilter arguments @@ -392,28 +393,18 @@ subroutine opSplittin(& call judge_solution ! determine whether solution is a success or a failure if (return_flag.eqv..true.) return ! return for a recovering solution - ! try the fully split solution if failed to converge with a minimum time step in the coupled solution - if (ixCoupling==fullyCoupled .and. failure) cycle coupling + call try_other_solution_methods ! if solution failed to converge, try other splitting methods + if (cycle_coupling) cycle coupling ! exit loops if necessary + if (cycle_stateThenDomain) cycle stateThenDomain + if (cycle_solution) cycle solution - ! try the scalar solution if failed to converge with a minimum time step in the split solution - if (ixCoupling/=fullyCoupled) then - select case(ixStateThenDomain) - case(fullDomain); if (failure) cycle stateThenDomain - case(subDomain); if (failure) cycle solution - case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' - end select - end if - - ! check that state variables updated - where(stateMask) stateCheck = stateCheck+1 - if (any(stateCheck>1)) then - message=trim(message)//'state variable updated more than once!' - err=20; return - end if + call confirm_variable_updates ! check that state variables updated + if (return_flag.eqv..true.) return ! return if error call success_check ! check for success if (exit_stateThenDomain) exit stateThenDomain ! exit loops if necessary if (exit_solution) exit solution + if (return_flag.eqv..true.) return ! return if error end do stateSplit ! solution with split layers @@ -749,6 +740,26 @@ subroutine judge_solution end if end subroutine judge_solution + subroutine try_other_solution_methods + ! *** if solution failed to converge, try other splitting methods *** + ! initialize flags + cycle_coupling=.false. + cycle_stateThenDomain=.false. + cycle_solution=.false. + + ! try the fully split solution if failed to converge with a minimum time step in the coupled solution + if (ixCoupling==fullyCoupled .and. failure) then; cycle_coupling=.true.; return; end if! return required to execute cycle statement in opSplittin + + ! try the scalar solution if failed to converge with a minimum time step in the split solution + if (ixCoupling/=fullyCoupled) then + select case(ixStateThenDomain) + case(fullDomain); if (failure) cycle_stateThenDomain=.true.; return ! return required to execute cycle statement in opSplittin + case(subDomain); if (failure) cycle_solution=.true.; return + case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' + end select + end if + end subroutine try_other_solution_methods + subroutine save_recover ! save/recover copies of prognostic variables do iVar=1,size(prog_data%var) @@ -781,8 +792,21 @@ subroutine save_recover end do end subroutine save_recover + + subroutine confirm_variable_updates + ! *** check that state variables updated *** + return_flag=.false. ! set flag + ! check that state variables updated + where(stateMask) stateCheck = stateCheck+1 + if (any(stateCheck>1)) then + message=trim(message)//'state variable updated more than once!' + err=20; return_flag=.true.; return + end if + end subroutine confirm_variable_updates + subroutine success_check ! initialize flags + return_flag=.false. exit_stateThenDomain=.false. exit_solution=.false. ! success = exit solution @@ -790,12 +814,12 @@ subroutine success_check ! sum the mean steps for the successful solution type mean_step_solution = mean_step_solution + (dt/nSubsteps)/nStateSplit select case(ixStateThenDomain) - case(fullDomain); if (iStateSplit==nStateSplit) exit_stateThenDomain=.true. !exit stateThenDomain - case(subDomain); if (iStateSplit==nStateSplit) exit_solution=.true. !exit solution + case(fullDomain); if (iStateSplit==nStateSplit) exit_stateThenDomain=.true. ! exit stateThenDomain + case(subDomain); if (iStateSplit==nStateSplit) exit_solution=.true. ! exit solution case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' end select else ! failure - call check_failure; return ! check reason for failure and return + call check_failure; return_flag=.true.; return ! check reason for failure and return end if ! success check end subroutine success_check From ea05c28ca27a92dbb559d03dcc118fbde92624b2 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 25 Nov 2023 04:55:10 -0600 Subject: [PATCH 1004/1472] Improved modularization of the latter half of the stateSplit loop in opSplittin. --- build/source/engine/opSplittin.f90 | 63 +++++++++++++++++------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 418cfaa5d..94f901c41 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -360,35 +360,14 @@ subroutine opSplittin(& ! ----- ! * assemble vectors for a given split... ! --------------------------------------- - ! get indices for a given split - call initialize_indexSplit - call indexSplit(in_indexSplit,stateMask,indx_data,out_indexSplit) - call finalize_indexSplit + call get_split_indices ! get indices for a given split + if (return_flag.eqv..true.) return ! return for a non-zero error code + + call update_fluxMask ! define the mask of the fluxes used if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - ! ----- - ! * define the mask of the fluxes used... - ! --------------------------------------- - call update_fluxMask - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! ----- - ! * solve variable subset for one time step... - ! -------------------------------------------- - - ! keep track of the number of scalar solutions - associate(numberScalarSolutions => indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1)) ! intent(inout): [i4b] number of scalar solutions - if (ixSolution==scalar) numberScalarSolutions = numberScalarSolutions + 1 - end associate - - ! solve variable subset for one full time step - call initialize_varSubstep - call varSubstep(in_varSubstep,io_varSubstep,& ! intent(inout): class objects for model control - model_decisions,lookup_data,type_data,attr_data,forc_data,mpar_data,& ! intent(inout): data structures for model properties - indx_data,prog_data,diag_data,flux_data,flux_mean,deriv_data,bvar_data,& - out_varSubstep) ! intent(out): class object for model control - call finalize_varSubstep - if (err/=0) then; message=trim(message)//trim(cmessage); if (err>0) return; end if ! error control + call solve_subset ! solve variable subset for one time step + if (return_flag.eqv..true.) return ! return for a positive error code call judge_solution ! determine whether solution is a success or a failure if (return_flag.eqv..true.) return ! return for a recovering solution @@ -707,6 +686,28 @@ subroutine finalize_varSubstep call out_varSubstep % finalize(dtMultiplier,nSubsteps,failedMinimumStep,reduceCoupledStep,tooMuchMelt,err,cmessage) end subroutine finalize_varSubstep + subroutine solve_subset + ! *** Solve variable subset for one time step *** + return_flag=.false. ! initialize flag + ! keep track of the number of scalar solutions + associate(numberScalarSolutions => indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1)) ! intent(inout): [i4b] number of scalar solutions + if (ixSolution==scalar) numberScalarSolutions = numberScalarSolutions + 1 + end associate + + ! solve variable subset for one full time step + call initialize_varSubstep + call varSubstep(in_varSubstep,io_varSubstep,& ! intent(inout): class objects for model control + model_decisions,lookup_data,type_data,attr_data,forc_data,mpar_data,& ! intent(inout): data structures for model properties + indx_data,prog_data,diag_data,flux_data,flux_mean,deriv_data,bvar_data,& + out_varSubstep) ! intent(out): class object for model control + call finalize_varSubstep + if (err/=0) then + message=trim(message)//trim(cmessage) + if (err>0) then ! return for positive error codes + return_flag=.true.; return + end if + end if ! error control + end subroutine solve_subset subroutine judge_solution ! *** determine whether solution is a success or a failure *** @@ -792,6 +793,14 @@ subroutine save_recover end do end subroutine save_recover + subroutine get_split_indices + ! *** Get indices for a given split *** + return_flag=.false. ! initialize flag + call initialize_indexSplit + call indexSplit(in_indexSplit,stateMask,indx_data,out_indexSplit) + call finalize_indexSplit + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if + end subroutine get_split_indices subroutine confirm_variable_updates ! *** check that state variables updated *** From 188144b80f60505675a9f23b736c16062ba8db8d Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 28 Nov 2023 02:54:18 -0600 Subject: [PATCH 1005/1472] Added update_stateFilter subroutine to the contains block of opSplittin. --- build/source/engine/opSplittin.f90 | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 94f901c41..b803162d6 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -328,16 +328,13 @@ subroutine opSplittin(& if (return_flag.eqv..true.) return ! return if error occurs during initialization stateSplit: do iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) - + ! ----- ! * define state subsets for a given split... ! ------------------------------------------- - ! get the mask for the state subset - call initialize_stateFilter - call stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) - call finalize_stateFilter - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! error control + call update_stateFilter ! get the mask for the state subset + if (return_flag.eqv..true.) return ! return for a non-zero error code ! check that state variables exist if (nSubset==0) cycle domainSplit @@ -761,6 +758,15 @@ subroutine try_other_solution_methods end if end subroutine try_other_solution_methods + subroutine update_stateFilter + ! *** Get the mask for the state subset *** + return_flag=.false. ! initialize flag + call initialize_stateFilter + call stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) + call finalize_stateFilter + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! error control + end subroutine update_stateFilter + subroutine save_recover ! save/recover copies of prognostic variables do iVar=1,size(prog_data%var) From c478d4a5296bf59cc1f50882cbb45fc8b4dac10b Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 28 Nov 2023 04:52:57 -0600 Subject: [PATCH 1006/1472] Added validate_split subroutine to the contains block of opSplittin. --- build/source/engine/opSplittin.f90 | 40 +++++++++++++++++++----------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index b803162d6..5a3370f24 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -336,20 +336,10 @@ subroutine opSplittin(& call update_stateFilter ! get the mask for the state subset if (return_flag.eqv..true.) return ! return for a non-zero error code - ! check that state variables exist - if (nSubset==0) cycle domainSplit - - ! avoid redundant case where vector solution is of length 1 - if (ixSolution==vector .and. count(stateMask)==1) cycle solution - - ! check that we do not attempt the scalar solution for the fully coupled case - if (ixCoupling==fullyCoupled .and. ixSolution==scalar) then - message=trim(message)//'only apply the scalar solution to the fully split coupling strategy' - err=20; return - end if - - ! reset the flag for the first flux call - if (.not.firstSuccess) firstFluxCall=.true. + call validate_split ! verify that the split is valid + if (cycle_domainSplit) cycle domainSplit + if (cycle_solution) cycle solution + if (return_flag.eqv..true.) return ! return for a non-zero error code ! save/recover copies of variables and fluxes call save_recover @@ -767,6 +757,28 @@ subroutine update_stateFilter if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! error control end subroutine update_stateFilter + subroutine validate_split + ! *** Verify that the split is valid *** + ! initialize flags + cycle_domainSplit=.false. + cycle_solution=.false. + return_flag=.false. + ! check that state variables exist + if (nSubset==0) then; cycle_domainSplit=.true.; return; end if + + ! avoid redundant case where vector solution is of length 1 + if (ixSolution==vector .and. count(stateMask)==1) then; cycle_solution=.true.; return; end if + + ! check that we do not attempt the scalar solution for the fully coupled case + if (ixCoupling==fullyCoupled .and. ixSolution==scalar) then + message=trim(message)//'only apply the scalar solution to the fully split coupling strategy' + err=20; return_flag=.true.; return + end if + + ! reset the flag for the first flux call + if (.not.firstSuccess) firstFluxCall=.true. + end subroutine validate_split + subroutine save_recover ! save/recover copies of prognostic variables do iVar=1,size(prog_data%var) From 3bf7c88215797f379eaf1556565d91dac3b3d37a Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 29 Nov 2023 06:42:08 -0600 Subject: [PATCH 1007/1472] Updates to error control statements in opSplittin. --- build/source/engine/opSplittin.f90 | 39 +++++++++++++++++------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 5a3370f24..b9e41953b 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -302,8 +302,8 @@ subroutine opSplittin(& type(in_type_varSubstep) :: in_varSubstep; type(io_type_varSubstep) :: io_varSubstep; type(out_type_varSubstep) :: out_varSubstep; ! varSubstep arguments ! --------------------------------------------------------------------------------------- - call initialize_opSplittin ! initial setup steps -- select coupling options and allocate memory - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! return if error in initialize step + call initialize_opSplittin ! select coupling options and allocate memory + if (return_flag.eqv..true.) return ! return if error occurs during initialization coupling: do ixCoupling=1,nCoupling ! loop through different coupling strategies @@ -350,8 +350,8 @@ subroutine opSplittin(& call get_split_indices ! get indices for a given split if (return_flag.eqv..true.) return ! return for a non-zero error code - call update_fluxMask ! define the mask of the fluxes used - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + call update_fluxMask ! define the mask of the fluxes used + if (return_flag.eqv..true.) return ! return for a non-zero error code call solve_subset ! solve variable subset for one time step if (return_flag.eqv..true.) return ! return for a positive error code @@ -430,7 +430,8 @@ subroutine initialize_opSplittin stateCheck(:) = 0 ! allocate local structures based on the number of snow and soil layers - call allocate_memory + call allocate_memory + if (return_flag.eqv..true.) return ! return if an error occurs during memory allocation ! intialize the flux counter do iVar=1,size(flux_meta) ! loop through fluxes @@ -453,37 +454,39 @@ end subroutine initialize_opSplittin subroutine allocate_memory ! *** allocate memory for local structures *** + return_flag=.false. ! initialize flag + ! allocate space for the flux mask (used to define when fluxes are updated) call allocLocal(flux_meta(:),fluxMask,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! allocate space for the flux count (used to check that fluxes are only updated once) call allocLocal(flux_meta(:),fluxCount,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! allocate space for the temporary prognostic variable structure call allocLocal(prog_meta(:),prog_temp,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! allocate space for the temporary diagnostic variable structure call allocLocal(diag_meta(:),diag_temp,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! allocate space for the temporary flux variable structure call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! allocate space for the mean flux variable structure call allocLocal(flux_meta(:),flux_mean,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! allocate space for the temporary mean flux variable structure call allocLocal(flux_meta(:),flux_mntemp,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! allocate space for the derivative structure call allocLocal(deriv_meta(:),deriv_data,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if end subroutine allocate_memory subroutine finalize_opSplittin @@ -862,7 +865,9 @@ subroutine check_failure end subroutine check_failure subroutine update_fluxMask - ! *** update the fluxMask data structure *** + ! *** update the fluxMask data structure *** + return_flag=.false. ! initialize flag + do iVar=1,size(flux_meta) ! loop through flux variables if (ixCoupling==fullyCoupled) then ! * identify flux mask for the fully coupled solution @@ -887,7 +892,7 @@ subroutine update_fluxMask select case(iStateTypeSplit) ! identify the flux mask for a given state split case(nrgSplit); desiredFlux = any(ixStateType_subset==flux2state_orig(iVar)%state1) .or. any(ixStateType_subset==flux2state_orig(iVar)%state2) case(massSplit); desiredFlux = any(ixStateType_subset==flux2state_liq(iVar)%state1) .or. any(ixStateType_subset==flux2state_liq(iVar)%state2) - case default; err=20; message=trim(message)//'unable to identify split based on state type'; return + case default; err=20; message=trim(message)//'unable to identify split based on state type'; return_flag=.true.; return end select end associate @@ -911,7 +916,7 @@ subroutine update_fluxMask fluxMask%var(iVar)%dat(:1) = desiredFlux if (ixStateThenDomain>1 .and. iStateTypeSplit/=nrgSplit) then message=trim(message)//'only expect a vector solution for the vegetation domain for energy' - err=20; return + err=20; return_flag=.true.; return end if else ! scalar solution fluxMask%var(iVar)%dat(:1) = desiredFlux @@ -954,7 +959,7 @@ subroutine update_fluxMask case(aquiferSplit) ! fluxes through aquifer fluxMask%var(iVar)%dat(:) = desiredFlux ! only would be firstFluxCall variables, no aquifer fluxes - case default; err=20; message=trim(message)//'unable to identify split based on domain type'; return ! check + case default; err=20; message=trim(message)//'unable to identify split based on domain type'; return_flag=.true.; return ! check end select ! domain split end if ! end if flux is desired end if ! end if domain splitting From 36fea923a3c1910399b437f18c739b496bd0dff9 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 30 Nov 2023 03:11:00 -0600 Subject: [PATCH 1008/1472] Various opSplittin updates: added finalize_stateTypeSplitting subroutine, improved subroutine names, and general cleanup. --- build/source/engine/opSplittin.f90 | 126 ++++++++++++----------------- 1 file changed, 52 insertions(+), 74 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index b9e41953b..8a69e7b9c 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -302,70 +302,46 @@ subroutine opSplittin(& type(in_type_varSubstep) :: in_varSubstep; type(io_type_varSubstep) :: io_varSubstep; type(out_type_varSubstep) :: out_varSubstep; ! varSubstep arguments ! --------------------------------------------------------------------------------------- - call initialize_opSplittin ! select coupling options and allocate memory - if (return_flag.eqv..true.) return ! return if error occurs during initialization + call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs + coupling: do ixCoupling=1,nCoupling ! loop through different coupling strategies - coupling: do ixCoupling=1,nCoupling ! loop through different coupling strategies - - call initialize_stateTypeSplitting_loop ! setup steps for stateTypeSplitting loop - if (return_flag.eqv..true.) return ! return if error occurs during initialization - - stateTypeSplitting: do iStateTypeSplit=1,nStateTypeSplit ! state splitting loop - - call initialize_stateThenDomain_loop ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split + call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs + stateTypeSplitting: do iStateTypeSplit=1,nStateTypeSplit ! state splitting loop ! first try the state type split, then try the domain split within a given state type + call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split stateThenDomain: do ixStateThenDomain=1,1+tryDomainSplit ! 1=state type split; 2=domain split within a given state type - call initialize_domainSplit_loop ! setup steps for domainSplit loop - if (return_flag.eqv..true.) return ! return if error occurs during initialization - - domainSplit: do iDomainSplit=1,nDomainSplit ! domain splitting loop + call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs + domainSplit: do iDomainSplit=1,nDomainSplit ! domain splitting loop solution: do ixSolution=1,nSolutions ! trial with the vector then scalar solution - call initialize_stateSplit_loop ! setup steps for stateSplit loop - if (return_flag.eqv..true.) return ! return if error occurs during initialization - + call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs stateSplit: do iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) - ! ----- - ! * define state subsets for a given split... - ! ------------------------------------------- - - call update_stateFilter ! get the mask for the state subset - if (return_flag.eqv..true.) return ! return for a non-zero error code - + ! define state subsets for a given split... + call update_stateFilter; if (return_flag.eqv..true.) return ! get the mask for the state subset - return for a non-zero error code call validate_split ! verify that the split is valid if (cycle_domainSplit) cycle domainSplit if (cycle_solution) cycle solution if (return_flag.eqv..true.) return ! return for a non-zero error code + call save_recover ! save/recover copies of variables and fluxes - ! save/recover copies of variables and fluxes - call save_recover + ! assemble vectors for a given split... + call get_split_indices; if (return_flag.eqv..true.) return ! get indices for a given split - return for a non-zero error code + call update_fluxMask; if (return_flag.eqv..true.) return ! define the mask of the fluxes used - return for a non-zero error code - ! ----- - ! * assemble vectors for a given split... - ! --------------------------------------- - call get_split_indices ! get indices for a given split - if (return_flag.eqv..true.) return ! return for a non-zero error code - - call update_fluxMask ! define the mask of the fluxes used - if (return_flag.eqv..true.) return ! return for a non-zero error code + call solve_subset; if (return_flag.eqv..true.) return ! solve variable subset for one time step - return for a positive error code - call solve_subset ! solve variable subset for one time step - if (return_flag.eqv..true.) return ! return for a positive error code - - call judge_solution ! determine whether solution is a success or a failure - if (return_flag.eqv..true.) return ! return for a recovering solution + call assess_solution; if (return_flag.eqv..true.) return ! is solution a success or failure? - return for a recovering solution call try_other_solution_methods ! if solution failed to converge, try other splitting methods if (cycle_coupling) cycle coupling ! exit loops if necessary if (cycle_stateThenDomain) cycle stateThenDomain if (cycle_solution) cycle solution - call confirm_variable_updates ! check that state variables updated - if (return_flag.eqv..true.) return ! return if error + call confirm_variable_updates; if (return_flag.eqv..true.) return ! check that state variables updated - return if error call success_check ! check for success if (exit_stateThenDomain) exit stateThenDomain ! exit loops if necessary @@ -374,28 +350,24 @@ subroutine opSplittin(& end do stateSplit ! solution with split layers - end do solution ! trial with the full layer solution then the split layer solution - - call finalize_solution_loop ! final steps following solution loop + end do solution ! trial with the full layer solution then the split layer solution + call finalize_solution ! final steps following solution loop end do domainSplit ! domain type splitting loop - end do stateThenDomain ! switch between the state type and domain type splitting + end do stateThenDomain ! switch between the state type and domain type splitting + call finalize_stateThenDomain ! final steps following the stateThenDomain loop - call finalize_stateThenDomain_loop ! final steps following the stateThenDomain loop + end do stateTypeSplitting ! state type splitting loop + call finalize_stateTypeSplitting; if (exit_coupling) exit coupling ! success = exit the coupling loop - end do stateTypeSplitting ! state type splitting loop - - if (ixCoupling==fullyCoupled .and. .not.failure) exit coupling ! success = exit the coupling loop - - end do coupling ! coupling method - - call finalize_opSplittin ! final steps -- check variables and fluxes, and apply step halving if needed + end do coupling ! loop over coupling methods + call finalize_coupling ! check variables and fluxes, and apply step halving if needed contains - subroutine initialize_opSplittin - ! *** initial steps for opSplittin subroutine *** + subroutine initialize_coupling + ! *** initial steps for coupling loop *** ! initialize error control err=0; message="opSplittin/" @@ -450,7 +422,7 @@ subroutine initialize_opSplittin do iVar=1,size(deriv_meta) deriv_data%var(iVar)%dat(:) = 0._rkind end do - end subroutine initialize_opSplittin + end subroutine initialize_coupling subroutine allocate_memory ! *** allocate memory for local structures *** @@ -489,8 +461,8 @@ subroutine allocate_memory if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if end subroutine allocate_memory - subroutine finalize_opSplittin - ! *** final operations for opSplittin subroutine *** + subroutine finalize_coupling + ! *** final operations for coupling loop *** ! check that all state variables were updated if (any(stateCheck==0)) then message=trim(message)//'some state variables were not updated!' @@ -508,9 +480,9 @@ subroutine finalize_opSplittin ! use step halving if unable to complete the fully coupled solution in one substep if (ixCoupling/=fullyCoupled .or. nSubsteps>1) dtMultiplier=0.5_rkind - end subroutine finalize_opSplittin + end subroutine finalize_coupling - subroutine initialize_stateTypeSplitting_loop + subroutine initialize_stateTypeSplitting ! *** Initial steps to prepare for iterations of the stateTypeSplit loop *** return_flag=.false. ! initialize flag ! initialize the time step @@ -538,9 +510,15 @@ subroutine initialize_stateTypeSplitting_loop mean_step_dt = 0._rkind ! initialize mean step for the time step addFirstFlux = .true. ! flag to add the first flux to the mask - end subroutine initialize_stateTypeSplitting_loop + end subroutine initialize_stateTypeSplitting + + subroutine finalize_stateTypeSplitting + ! *** Final operations subsequent to the stateTypeSplitting loop *** + exit_coupling=.false. ! initialize flag for loop control + if (ixCoupling==fullyCoupled .and. .not.failure) then; exit_coupling=.true.; return; end if ! success = exit the coupling loop in opSplittin + end subroutine finalize_stateTypeSplitting - subroutine initialize_stateThenDomain_loop + subroutine initialize_stateThenDomain ! *** Identify state-specific variables for a given state split *** doAdjustTemp = (ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit) ! flag to adjust the temperature associate(& @@ -557,9 +535,9 @@ subroutine initialize_stateThenDomain_loop where(ixStateType(ixHydLayer)==iname_matLayer) ixStateType(ixHydLayer)=iname_lmpLayer end if end associate - end subroutine initialize_stateThenDomain_loop + end subroutine initialize_stateThenDomain - subroutine finalize_stateThenDomain_loop + subroutine finalize_stateThenDomain ! *** Final steps following the stateThenDomain loop *** ! sum the mean steps for the time step over each state type split select case(ixStateThenDomain) @@ -581,9 +559,9 @@ subroutine finalize_stateThenDomain_loop where(ixStateType(ixHydLayer)==iname_lmpLayer) ixStateType(ixHydLayer)=iname_matLayer end if end associate - end subroutine finalize_stateThenDomain_loop + end subroutine finalize_stateThenDomain - subroutine initialize_domainSplit_loop + subroutine initialize_domainSplit ! *** initial operations to set up domainSplit loop *** return_flag=.false. ! initialize flag associate(numberDomainSplitNrg => indx_data%var(iLookINDEX%numberDomainSplitNrg )%dat(1),& ! intent(inout): [i4b] number of domain splitting solutions for energy (-) @@ -610,15 +588,15 @@ subroutine initialize_domainSplit_loop end if mean_step_state = 0._rkind ! initialize mean step for state - end subroutine initialize_domainSplit_loop + end subroutine initialize_domainSplit - subroutine finalize_solution_loop + subroutine finalize_solution ! *** final operations following solution loop *** ! sum the mean steps for the state over each domain split mean_step_state = mean_step_state + mean_step_solution/nDomainSplit - end subroutine finalize_solution_loop + end subroutine finalize_solution - subroutine initialize_stateSplit_loop + subroutine initialize_stateSplit ! *** initial operations to set up stateSplit loop *** return_flag=.false. ! initialize flag mean_step_solution = 0._rkind ! initialize mean step for a solution @@ -644,7 +622,7 @@ subroutine initialize_stateSplit_loop return_flag=.true. ! return statement required in opSplittin return end select - end subroutine initialize_stateSplit_loop + end subroutine initialize_stateSplit ! **** stateFilter **** subroutine initialize_stateFilter @@ -699,7 +677,7 @@ subroutine solve_subset end if ! error control end subroutine solve_subset - subroutine judge_solution + subroutine assess_solution ! *** determine whether solution is a success or a failure *** return_flag=.false. ! initialize flag @@ -729,7 +707,7 @@ subroutine judge_solution end do end do end if - end subroutine judge_solution + end subroutine assess_solution subroutine try_other_solution_methods ! *** if solution failed to converge, try other splitting methods *** @@ -955,7 +933,7 @@ subroutine update_fluxMask end if ! if the layer is active end associate - end do ! looping through layers + end do ! end looping through layers case(aquiferSplit) ! fluxes through aquifer fluxMask%var(iVar)%dat(:) = desiredFlux ! only would be firstFluxCall variables, no aquifer fluxes From 6e1a470bd8daaa1700f75f0aa5ca4b7149babd79 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 1 Dec 2023 19:36:07 +0900 Subject: [PATCH 1009/1472] Using t2enthalpyPrime and heatCap from that prime as well as balances off prime now for Sundials. Removed some of the unneeded previous values, Soil values still off Lookup table, not hypergeometric gaussian function. --- build/cmake/CMakeLists.txt | 10 +- build/source/dshare/get_ixname.f90 | 7 +- build/source/dshare/popMetadat.f90 | 7 +- build/source/dshare/type4ida.f90 | 124 ++-- build/source/dshare/type4kinsol.f90 | 2 +- build/source/dshare/var_lookup.f90 | 9 +- .../source/engine/computHeatCapWithPrime.f90 | 304 ++++++++ build/source/engine/computJacobWithPrime.f90 | 51 +- build/source/engine/eval8summa.f90 | 10 +- build/source/engine/eval8summaWithPrime.f90 | 700 +++++++++--------- build/source/engine/summaSolve4ida.f90 | 276 +++---- build/source/engine/t2enthalpy.f90 | 159 +--- build/source/engine/t2enthalpyAddPrime.f90 | 451 +++++++++++ build/source/engine/updateVarsWithPrime.f90 | 6 +- build/source/engine/varSubstep.f90 | 22 +- 15 files changed, 1417 insertions(+), 721 deletions(-) create mode 100644 build/source/engine/computHeatCapWithPrime.f90 create mode 100644 build/source/engine/t2enthalpyAddPrime.f90 diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 8518978ef..1de6f43e0 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -206,10 +206,10 @@ else() message(FATAL_ERROR "Did not find Actors directory, edit CMakeLists.txt to add path") endif() link_directories(${DIR_ACTORS}/lib) - set(INC_ACTORS ${DIR_ACTORS}/include - ${PARENT_DIR}/build/includes/global - ${PARENT_DIR}/build/includes/summa_actor - ${PARENT_DIR}/build/includes/job_actor + set(INC_ACTORS ${DIR_ACTORS}/include + ${PARENT_DIR}/build/includes/global + ${PARENT_DIR}/build/includes/summa_actor + ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) set(LIB_ACTORS ${DIR_ACTORS}/lib -lcaf_core -lcaf_io) @@ -353,6 +353,7 @@ set(MODRUN_NOT_ACTORS ${ENGINE_DIR}/read_force.f90) set(MODRUN_SUNDIALS ${ENGINE_DIR}/tol4ida.f90 + ${ENGINE_DIR}/t2enthalpyAddPrime.f90 ${ENGINE_DIR}/updateVarsWithPrime.f90) # Solver main modules @@ -384,6 +385,7 @@ set(SOLVER_NOT_ACTORS ${ENGINE_DIR}/run_oneGRU.f90 ${ENGINE_DIR}/run_oneHRU.f90) set(SOLVER_SUNDIALS + ${ENGINE_DIR}/computHeatCapWithPrime.f90 ${ENGINE_DIR}/computJacobWithPrime.f90 ${ENGINE_DIR}/computResidWithPrime.f90 ${ENGINE_DIR}/eval8summaWithPrime.f90 diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 4775753c2..a72d881f5 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -774,6 +774,11 @@ function get_ixderiv(varName) case('dVolHtCapBulk_dCanWat' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. canopy volumetric water content case('dVolHtCapBulk_dTk' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTk ! derivative in bulk heat capacity w.r.t. temperature case('dVolHtCapBulk_dTkCanopy' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. canopy temperature + case('dVolHtCapBulk_dPsi0Prime' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dPsi0Prime ! derivative in bulk heat capacity w.r.t. prime matric potential + case('dVolHtCapBulk_dThetaPrime' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dThetaPrime ! derivative in bulk heat capacity w.r.t. prime volumetric water content + case('dVolHtCapBulk_dCanWatPrime' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dCanWatPrime ! derivative in bulk heat capacity w.r.t. prime volumetric water content + case('dVolHtCapBulk_dTkPrime' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTkPrime ! derivative in bulk heat capacity w.r.t. prime temperature + case('dVolHtCapBulk_dTkCanPrime' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTkCanPrime ! derivative in bulk heat capacity w.r.t. prime temperature case('dThermalC_dTempAbove' ); get_ixderiv = iLookDERIV%dThermalC_dTempAbove ! derivative in the thermal conductivity w.r.t. energy state in the layer above case('dThermalC_dTempBelow' ); get_ixderiv = iLookDERIV%dThermalC_dTempBelow ! derivative in the thermal conductivity w.r.t. energy state in the layer above case('dThermalC_dWatAbove' ); get_ixderiv = iLookDERIV%dThermalC_dWatAbove ! derivative in the thermal conductivity w.r.t. water state in the layer above @@ -790,7 +795,7 @@ function get_ixderiv(varName) case('iLayerLiqFluxSnowDeriv' ); get_ixderiv = iLookDERIV%iLayerLiqFluxSnowDeriv ! derivative in vertical liquid water flux at layer interfaces (m s-1) ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables case('dVolTot_dPsi0' ); get_ixderiv = iLookDERIV%dVolTot_dPsi0 ! derivative in total water content w.r.t. total water matric potential (m-1) - case('d2VolTot_d2Psi0' ); get_ixderiv = iLookDERIV%d2VolTot_d2Psi0 ! second derivative in total water content w.r.t. total water matric potential + case('d2VolTot_dPsi02' ); get_ixderiv = iLookDERIV%d2VolTot_dPsi02 ! second derivative in total water content w.r.t. total water matric potential case('dq_dHydStateAbove' ); get_ixderiv = iLookDERIV%dq_dHydStateAbove ! change in the flux in layer interfaces w.r.t. state variables in the layer above case('dq_dHydStateBelow' ); get_ixderiv = iLookDERIV%dq_dHydStateBelow ! change in the flux in layer interfaces w.r.t. state variables in the layer below case('dq_dHydStateLayerSurfVec' ); get_ixderiv = iLookDERIV%dq_dHydStateLayerSurfVec ! change in the flux in soil surface interface w.r.t. state variables in layer above and below diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 0f933c073..b99d67a66 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -599,6 +599,11 @@ subroutine popMetadat(err,message) deriv_meta(iLookDERIV%dVolHtCapBulk_dCanWat) = var_info('dVolHtCapBulk_dCanWat' , 'derivative in bulk heat capacity w.r.t. canopy volumetric water content', 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dVolHtCapBulk_dTk) = var_info('dVolHtCapBulk_dTk' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dVolHtCapBulk_dTkCanopy) = var_info('dVolHtCapBulk_dTkCanopy' , 'derivative in bulk heat capacity w.r.t. canopy temperature' , 'J m-3 K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dPsi0Prime) = var_info('dVolHtCapBulk_dPsi0Prime' , 'derivative in bulk heat capacity w.r.t. prime matric potential' , 'J s m-4 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dThetaPrime) = var_info('dVolHtCapBulk_dThetaPrime' , 'derivative in bulk heat capacity w.r.t. prime volumetric water content', 'J s m-3 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dCanWatPrime) = var_info('dVolHtCapBulk_dCanWatPrime' , 'derivative in bulk heat capacity w.r.t. prime canopy volumetric water content','J s m-3 K-1',get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dTkPrime) = var_info('dVolHtCapBulk_dTkPrime' , 'derivative in bulk heat capacity w.r.t. prime temperature' , 'J s m-3 K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dVolHtCapBulk_dTkCanPrime) = var_info('dVolHtCapBulk_dTkCanPrime' , 'derivative in bulk heat capacity w.r.t. prime canopy temperature' , 'J s m-3 K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dThermalC_dTempAbove) = var_info('dThermalC_dTempAbove' , 'derivative in the thermal conductivity w.r.t. energy in the layer above','J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dThermalC_dTempBelow) = var_info('dThermalC_dTempBelow' , 'derivative in the thermal conductivity w.r.t. energy in the layer above','J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dThermalC_dWatAbove) = var_info('dThermalC_dWatAbove' , 'derivative in the thermal conductivity w.r.t. water in the layer above', 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) @@ -616,7 +621,7 @@ subroutine popMetadat(err,message) deriv_meta(iLookDERIV%iLayerLiqFluxSnowDeriv) = var_info('iLayerLiqFluxSnowDeriv' , 'derivative in vertical liquid water flux at layer interfaces' , 'm s-1' , get_ixVarType('ifcSnow'), iMissVec, iMissVec, .false.) ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables deriv_meta(iLookDERIV%dVolTot_dPsi0) = var_info('dVolTot_dPsi0' , 'derivative in total water content w.r.t. total water matric potential', 'm-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%d2VolTot_d2Psi0) = var_info('d2VolTot_d2Psi0' , 'second derivative in total water content w.r.t. total water matric potential', 'm-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%d2VolTot_dPsi02) = var_info('d2VolTot_dPsi02' , 'second derivative in total water content w.r.t. total water matric potential', 'm-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dCompress_dPsi) = var_info('dCompress_dPsi' , 'derivative in compressibility w.r.t matric head' , 'm-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%mLayerdTheta_dPsi) = var_info('mLayerdTheta_dPsi' , 'derivative in the soil water characteristic w.r.t. psi' , 'm-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%mLayerdPsi_dTheta) = var_info('mLayerdPsi_dTheta' , 'derivative in the soil water characteristic w.r.t. theta' , 'm' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/type4ida.f90 b/build/source/dshare/type4ida.f90 index 68d55aca6..fa6ee0df9 100644 --- a/build/source/dshare/type4ida.f90 +++ b/build/source/dshare/type4ida.f90 @@ -16,74 +16,62 @@ module type4ida implicit none type data4ida - type(c_ptr) :: ida_mem ! IDA memory - real(rkind) :: dt ! data step - integer(i4b) :: nSnow ! number of snow layers - integer(i4b) :: nSoil ! number of soil layers - integer(i4b) :: nLayers ! total number of layers - integer(i4b) :: nState ! total number of state variables - integer(i4b) :: ixMatrix ! form of matrix (dense or banded) - logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution - type(model_options),allocatable :: model_decisions(:) ! model decisions - type(zLookup) :: lookup_data ! lookup tables - type(var_i) :: type_data ! type of vegetation and soil - type(var_d) :: attr_data ! spatial attributes - type(var_dlength) :: mpar_data ! model parameters - type(var_d) :: forc_data ! model forcing data - type(var_dlength) :: bvar_data ! model variables for the local basin - type(var_dlength) :: prog_data ! prognostic variables for a local HRU - type(var_ilength) :: indx_data ! indices defining model states and layers - type(var_dlength) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength) :: flux_data ! model fluxes for a local HRU - type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(rkind) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) - real(rkind) :: scalarCanairEnthalpyTrial ! trial enthalpy of the canopy air space (J m-3) - real(rkind) :: scalarCanairEnthalpyPrev ! previous enthalpy of the canopy air space (J m-3) - real(rkind) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind) :: scalarCanopyTempPrev ! previous value of canopy temperature (K) - real(rkind) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - real(rkind) :: scalarCanopyIcePrev ! value of canopy ice content (kg m-2) at previous step - real(rkind) :: scalarCanopyLiqTrial ! trial value of canopy ice content (kg m-2) - real(rkind) :: scalarCanopyLiqPrev ! value of canopy ice content (kg m-2) at previous step - real(rkind) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) - real(rkind) :: scalarCanopyEnthalpyPrev ! previous enthalpy of the vegetation canopy (J m-3) - real(qp), allocatable :: sMul(:) ! state vector multiplier (used in the residual calculations) - real(rkind), allocatable :: dMat(:) ! diagonal of the Jacobian matrix - real(rkind), allocatable :: fluxVec(:) ! flux vector - real(qp), allocatable :: resVec(:) ! residual vector - real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation - real(rkind), allocatable :: atol(:) ! vector of absolute tolerances - real(rkind), allocatable :: rtol(:) ! vector of relative tolerances - real(rkind), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind), allocatable :: mLayerTempPrev(:) ! vector of layer temperature (K) at previous step - real(rkind), allocatable :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) - real(rkind), allocatable :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind), allocatable :: mLayerMatricHeadPrev(:) ! vector of total water matric potential (m) at previous step - real(rkind), allocatable :: mLayerVolFracWatTrial(:) ! trial value for volumetric fraction of total water (-) - real(rkind), allocatable :: mLayerVolFracWatPrev(:) ! value for volumetric fraction of total water (-) at previous step - real(rkind), allocatable :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) - real(rkind), allocatable :: mLayerVolFracIcePrev(:) ! value for volumetric fraction of ice (-) at previous step - real(rkind), allocatable :: mLayerVolFracLiqTrial(:) ! trial value for volumetric fraction of liquid water (-) - real(rkind), allocatable :: mLayerVolFracLiqPrev(:) ! value for volumetric fraction of liquid water (-) at previous step - real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) - real(rkind) :: scalarAquiferStoragePrev ! value of storage of water in the aquifer (m) at previous step - real(rkind), allocatable :: mLayerEnthalpyTrial(:) ! trial enthalpy of snow and soil (J m-3) - real(rkind), allocatable :: mLayerEnthalpyPrev(:) ! enthalpy of snow and soil (J m-3) at previous step - real(rkind), allocatable :: mLayerTempPrime(:) ! derivative value for temperature of each snow and soil layer (K) - real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! derivative value for matric head of each snow and soil layer (m) - real(rkind), allocatable :: mLayerMatricHeadLiqPrime(:) ! derivative value for liquid water matric head of each snow and soil layer (m) - real(rkind), allocatable :: mLayerVolFracWatPrime(:) ! derivative value for volumetric total water content of each snow and soil layer (-) - real(rkind) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatPrime ! derivative value for total water content of the vegetation canopy (kg m-2) - real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - integer(i4b) :: ixSaturation ! index of the lowest saturated layer - integer(i4b) :: err ! error code - character(len=50) :: message ! error message -end type data4ida + type(c_ptr) :: ida_mem ! IDA memory + real(rkind) :: dt ! data step + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers + integer(i4b) :: nLayers ! total number of layers + integer(i4b) :: nState ! total number of state variables + integer(i4b) :: ixMatrix ! form of matrix (dense or banded) + logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution + type(model_options),allocatable :: model_decisions(:) ! model decisions + type(zLookup) :: lookup_data ! lookup tables + type(var_i) :: type_data ! type of vegetation and soil + type(var_d) :: attr_data ! spatial attributes + type(var_dlength) :: mpar_data ! model parameters + type(var_d) :: forc_data ! model forcing data + type(var_dlength) :: bvar_data ! model variables for the local basin + type(var_dlength) :: prog_data ! prognostic variables for a local HRU + type(var_ilength) :: indx_data ! indices defining model states and layers + type(var_dlength) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength) :: flux_data ! model fluxes for a local HRU + type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + real(qp), allocatable :: sMul(:) ! state vector multiplier (used in the residual calculations) + real(rkind), allocatable :: dMat(:) ! diagonal of the Jacobian matrix + real(rkind), allocatable :: fluxVec(:) ! flux vector + real(qp), allocatable :: resVec(:) ! residual vector + real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation + real(rkind), allocatable :: atol(:) ! vector of absolute tolerances + real(rkind), allocatable :: rtol(:) ! vector of relative tolerances + integer(i4b) :: ixSaturation ! index of the lowest saturated layer + real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + integer(i4b) :: err ! error code + character(len=50) :: message ! error message + real(rkind) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) + real(rkind), allocatable :: mLayerTempPrev(:) ! previous vector of layer temperature (K) + real(rkind), allocatable :: mLayerMatricHeadPrev(:) ! previous value for total water matric potential (m) + real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatTrial ! trial value for mass of total water on the vegetation canopy (kg m-2) + real(rkind), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind), allocatable :: mLayerMatricHeadTrial(:) ! trial value for total water matric potential (m) + real(rkind), allocatable :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(rkind) :: scalarCanairTempPrime ! prime value for temperature of the canopy air space (K s-1) + real(rkind) :: scalarCanopyTempPrime ! prime value for temperature of the vegetation canopy (K s-1) + real(rkind) :: scalarCanopyWatPrime ! prime value for mass of total water on the vegetation canopy (kg m-2 s-1) + real(rkind) :: scalarCanopyIcePrime ! prime value for mass of ice on the vegetation canopy (kg m-2 s-1) + real(rkind), allocatable :: mLayerTempPrime(:) ! prime value for temperature of each snow and soil layer (K s-1) + real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! prime value for matric head of each snow and soil layer (m s-1) + real(rkind), allocatable :: mLayerMatricHeadLiqPrime(:) ! prime value for liquid matric head of each snow and soil layer (m s-1) + real(rkind), allocatable :: mLayerVolFracWatPrime(:) ! prime value for volumetric total water content of each snow and soil layer (s-1) + real(rkind), allocatable :: mLayerVolFracIcePrime(:) ! prime value for volumetric fraction of ice (s-1) + real(rkind) :: scalarCanairEnthalpyPrime ! prime value for enthalpy of the canopy air space (J m-3 s-1) + real(rkind) :: scalarCanopyEnthalpyPrime ! prime value of enthalpy of the vegetation canopy (J m-3 s-1) + real(rkind), allocatable :: mLayerEnthalpyPrime(:) ! prime vector of enthalpy for snow+soil layers (J m-3 s-1) + end type data4ida end module type4ida diff --git a/build/source/dshare/type4kinsol.f90 b/build/source/dshare/type4kinsol.f90 index d7df38ecd..514a093ab 100644 --- a/build/source/dshare/type4kinsol.f90 +++ b/build/source/dshare/type4kinsol.f90 @@ -48,10 +48,10 @@ module type4kinsol real(rkind),allocatable :: xScale(:) ! characteristic scale of the state vector real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) integer(i4b) :: ixSaturation ! index of the lowest saturated layer - real(rkind), allocatable :: stateVecPrev(:) ! state vector from the previous iteration to help with infeasibility logical(lgt) :: firstStateIteration ! flag to denote if we computed an iteration so we know to save the state integer(i4b) :: err ! error code character(len=50) :: message ! error message + real(rkind), allocatable :: stateVecPrev(:) ! state vector from the previous iteration to help with infeasibility end type data4kinsol diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index e36e08bbe..43f9a7f67 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -630,6 +630,11 @@ MODULE var_lookup integer(i4b) :: dVolHtCapBulk_dCanWat = integerMissing ! derivative in bulk heat capacity w.r.t. canopy volumetric water content integer(i4b) :: dVolHtCapBulk_dTk = integerMissing ! derivative in bulk heat capacity w.r.t. temperature integer(i4b) :: dVolHtCapBulk_dTkCanopy = integerMissing ! derivative in bulk heat capacity w.r.t. canopy temperature + integer(i4b) :: dVolHtCapBulk_dPsi0Prime = integerMissing ! derivative in bulk heat capacity w.r.t. prime matric potential + integer(i4b) :: dVolHtCapBulk_dThetaPrime = integerMissing ! derivative in bulk heat capacity w.r.t. prime volumetric water content + integer(i4b) :: dVolHtCapBulk_dCanWatPrime = integerMissing ! derivative in bulk heat capacity w.r.t. prime canopy volumetric water content + integer(i4b) :: dVolHtCapBulk_dTkPrime = integerMissing ! derivative in bulk heat capacity w.r.t. prime temperature + integer(i4b) :: dVolHtCapBulk_dTkCanPrime = integerMissing ! derivative in bulk heat capacity w.r.t. prime canopy temperature integer(i4b) :: dThermalC_dTempAbove = integerMissing ! derivative in the thermal conductivity w.r.t. energy state in the layer above integer(i4b) :: dThermalC_dTempBelow = integerMissing ! derivative in the thermal conductivity w.r.t. energy state in the layer above integer(i4b) :: dThermalC_dWatAbove = integerMissing ! derivative in the thermal conductivity w.r.t. water state in the layer above @@ -647,7 +652,7 @@ MODULE var_lookup integer(i4b) :: iLayerLiqFluxSnowDeriv = integerMissing ! derivative in vertical liquid water flux at layer interfaces (m s-1) ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables integer(i4b) :: dVolTot_dPsi0 = integerMissing ! derivative in total water content w.r.t. total water matric potential (m-1) - integer(i4b) :: d2VolTot_d2Psi0 = integerMissing ! second derivative in total water content w.r.t. total water matric potential + integer(i4b) :: d2VolTot_dPsi02 = integerMissing ! second derivative in total water content w.r.t. total water matric potential integer(i4b) :: dq_dHydStateAbove = integerMissing ! change in the flux in layer interfaces w.r.t. state variables in the layer above integer(i4b) :: dq_dHydStateBelow = integerMissing ! change in the flux in layer interfaces w.r.t. state variables in the layer below integer(i4b) :: dq_dHydStateLayerSurfVec = integerMissing ! change in the flux in soil surface interface w.r.t. state variables in layer above and below @@ -925,7 +930,7 @@ MODULE var_lookup 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,& 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,& 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,& - 71, 72, 73, 74, 75) + 71, 72, 73, 74, 75, 76, 77, 78, 79, 80) ! named variables: model indices type(iLook_index), public,parameter :: iLookINDEX =ilook_index ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& diff --git a/build/source/engine/computHeatCapWithPrime.f90 b/build/source/engine/computHeatCapWithPrime.f90 new file mode 100644 index 000000000..22a900f9e --- /dev/null +++ b/build/source/engine/computHeatCapWithPrime.f90 @@ -0,0 +1,304 @@ +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2015 NCAR/RAL +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . + +module computHeatCapWithPrime_module + +! data types +USE nrtype + +! derived types to define the data structures +USE data_types,only:& + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength ! data vector with variable length dimension (rkind) + +! named variables defining elements in the data structures +USE var_lookup,only:iLookPARAM,iLookDIAG,iLookINDEX ! named variables for structure elements + +! physical constants +USE multiconst,only:& + Tfreeze, & ! freezing point of water (K) + iden_air, & ! intrinsic density of air (kg m-3) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water, & ! intrinsic density of water (kg m-3) + ! specific heat + Cp_air, & ! specific heat of air (J kg-1 K-1) + Cp_ice, & ! specific heat of ice (J kg-1 K-1) + Cp_soil, & ! specific heat of soil (J kg-1 K-1) + Cp_water ! specific heat of liquid water (J kg-1 K-1) +! named variables to describe the state variable type +USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space +USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy +USE globalData,only:iname_watCanopy ! named variable defining the mass of total water on the vegetation canopy +USE globalData,only:iname_liqCanopy ! named variable defining the mass of liquid water on the vegetation canopy +USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers +USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers +USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers +USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers +USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers +USE globalData,only:iname_watAquifer ! named variable defining the water storage in the aquifer + +! missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing real + +! named variables that define the layer type +USE globalData,only:iname_snow ! snow +USE globalData,only:iname_soil ! soil + + +! privacy +implicit none +private +public::computHeatCapWithPrime + +contains + + +! ********************************************************************************************************** +! public subroutine computHeatCapWithPrime: compute diagnostic energy variables (heat capacity) +! ********************************************************************************************************** +subroutine computHeatCapWithPrime(& + ! input: control variables + nLayers, & ! intent(in): number of layers (soil+snow) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) + ! input output data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + ! input: state variables + scalarCanopyIce, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiquid, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature + scalarCanopyTempPrime, & ! intent(in): prime value of canopy temperature (K) + scalarCanopyEnthalpyPrime, & ! intent(in): prime enthalpy of the vegetation canopy (J m-3) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial temperature + mLayerTempPrime, & ! intent(in): prime temperature + mLayerEnthalpyPrime, & ! intent(in): prime enthalpy for snow and soil + mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) + ! input: pre-computed derivatives + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + dCanEnthalpyPrime_dTk, & ! intent(in): derivatives in prime canopy enthalpy w.r.t. temperature + dCanEnthalpyPrime_dWat, & ! intent(in): derivatives in prime canopy enthalpy w.r.t. water state + dEnthalpyPrime_dTk, & ! intent(in): derivatives in prime layer enthalpy w.r.t. temperature + dEnthalpyPrime_dWat, & ! intent(in): derivatives in prime layer enthalpy w.r.t. water state + dCanEnthalpyPrime_dTkPrime, & ! intent(in): derivatives in prime canopy enthalpy w.r.t. prime temperature + dCanEnthalpyPrime_dWatPrime, & ! intent(in): derivatives in prime canopy enthalpy w.r.t. prime water state + dEnthalpyPrime_dTkPrime, & ! intent(in): derivatives in prime layer enthalpy w.r.t. prime temperature + dEnthalpyPrime_dWatPrime, & ! intent(in): derivatives in prime layer enthalpy w.r.t. prime water state + ! output + heatCapVeg, & ! intent(out): heat capacity for canopy + mLayerHeatCap, & ! intent(out): heat capacity for snow and soil + dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dPsi0Prime, & ! intent(out): derivative in bulk heat capacity w.r.t. prime matric potential + dVolHtCapBulk_dThetaPrime, & ! intent(out): derivative in bulk heat capacity w.r.t. prime volumetric water content + dVolHtCapBulk_dCanWatPrime, & ! intent(out): derivative in bulk heat capacity w.r.t. prime volumetric water content + dVolHtCapBulk_dTkPrime, & ! intent(out): derivative in bulk heat capacity w.r.t. prime temperature + dVolHtCapBulk_dTkCanPrime, & ! intent(out): derivative in bulk heat capacity w.r.t. prime temperature + ! output: error control + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! provide access to external subroutines + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + ! -------------------------------------------------------------------------------------------------------------------------------- + ! input: control variables + integer(i4b),intent(in) :: nLayers ! number of layers (soil+snow) + logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) + ! input/output: data structures + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model layer indices + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + ! input: state variables + real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) + real(rkind),intent(in) :: scalarCanopyLiquid ! trial value for the liquid water on the vegetation canopy (kg m-2) + real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature + real(rkind),intent(in) :: scalarCanopyTempPrime ! prime value of canopy temperature + real(rkind),intent(in) :: scalarCanopyEnthalpyPrime ! prime enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) + real(rkind),intent(in) :: mLayerTempTrial(:) ! trial temperature + real(rkind),intent(in) :: mLayerTempPrime(:) ! prime temperature + real(rkind),intent(in) :: mLayerEnthalpyPrime(:) ! prime enthalpy for snow and soil + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! vector of total water matric potential (m) + ! input: pre-computed derivatives + real(rkind),intent(in) :: dTheta_dTkCanopy ! derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: scalarFracLiqVeg ! fraction of canopy liquid water (-) + real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative of volumetric liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) + real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) + real(rkind),intent(in) :: dCanEnthalpyPrime_dTk ! derivatives in canopy enthalpy w.r.t. temperature + real(rkind),intent(in) :: dCanEnthalpyPrime_dWat ! derivatives in canopy enthalpy w.r.t. water state + real(rkind),intent(in) :: dEnthalpyPrime_dTk(:) ! derivatives in layer enthalpy w.r.t. temperature + real(rkind),intent(in) :: dEnthalpyPrime_dWat(:) ! derivatives in layer enthalpy w.r.t. water state + real(rkind),intent(in) :: dCanEnthalpyPrime_dTkPrime ! derivatives in canopy enthalpy w.r.t. prime temperature + real(rkind),intent(in) :: dCanEnthalpyPrime_dWatPrime ! derivatives in canopy enthalpy w.r.t. prime water state + real(rkind),intent(in) :: dEnthalpyPrime_dTkPrime(:) ! derivatives in layer enthalpy w.r.t. prime temperature + real(rkind),intent(in) :: dEnthalpyPrime_dWatPrime(:) ! derivatives in layer enthalpy w.r.t. prime water state + ! output: + real(qp),intent(out) :: heatCapVeg ! heat capacity for canopy + real(qp),intent(out) :: mLayerHeatCap(:) ! heat capacity for snow and soil + real(rkind),intent(out) :: dVolHtCapBulk_dPsi0(:) ! derivative in bulk heat capacity w.r.t. matric potential + real(rkind),intent(out) :: dVolHtCapBulk_dTheta(:) ! derivative in bulk heat capacity w.r.t. volumetric water content + real(rkind),intent(out) :: dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. volumetric water content + real(rkind),intent(out) :: dVolHtCapBulk_dTk(:) ! derivative in bulk heat capacity w.r.t. temperature + real(rkind),intent(out) :: dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature + real(rkind),intent(out) :: dVolHtCapBulk_dPsi0Prime(:) ! derivative in bulk heat capacity w.r.t. prime matric potential + real(rkind),intent(out) :: dVolHtCapBulk_dThetaPrime(:) ! derivative in bulk heat capacity w.r.t. prime volumetric water content + real(rkind),intent(out) :: dVolHtCapBulk_dCanWatPrime ! derivative in bulk heat capacity w.r.t. prime volumetric water content + real(rkind),intent(out) :: dVolHtCapBulk_dTkPrime(:) ! derivative in bulk heat capacity w.r.t. prime temperature + real(rkind),intent(out) :: dVolHtCapBulk_dTkCanPrime ! derivative in bulk heat capacity w.r.t. prime temperature + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: iSoil ! index of soil layer + real(rkind) :: delT ! temperature change + real(rkind) :: delEnt ! enthalpy change + real(rkind) :: fLiq ! fraction of liquid water + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + ! -------------------------------------------------------------------------------------------------------------------------------- + ! associate variables in data structure + associate(& + ! input: coordinate variables + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers + layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) + ! input: heat capacity and thermal conductivity + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) + ! input: depth varying soil parameters + iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) + ) ! end associate statemen + ! initialize error control + err=0; message="computHeatCapWithPrime/" + + ! initialize the soil layer + iSoil=integerMissing + + ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) + if(computeVegFlux)then + delT = scalarCanopyTempPrime + if(abs(delT) <= 1e-2_rkind)then + heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component + Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component + Cp_ice*scalarCanopyIce/canopyDepth ! ice component + + ! derivatives + fLiq = scalarFracLiqVeg + dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) + if(scalarCanopyTempTrial < Tfreeze)then + dVolHtCapBulk_dTkCanopy = iden_water * (-Cp_ice + Cp_water) * dTheta_dTkCanopy ! no derivative in air + else + dVolHtCapBulk_dTkCanopy = 0._rkind + endif + dVolHtCapBulk_dCanWatPrime = 0._rkind + dVolHtCapBulk_dTkCanPrime = 0._rkind + else + delEnt = scalarCanopyEnthalpyPrime + heatCapVeg = delEnt / delT + ! derivatives + dVolHtCapBulk_dCanWat = dCanEnthalpyPrime_dWat / delT + dVolHtCapBulk_dCanWatPrime = dCanEnthalpyPrime_dWatPrime / delT + dVolHtCapBulk_dTkCanopy = dCanEnthalpyPrime_dTk / delT + dVolHtCapBulk_dTkCanPrime = ( dCanEnthalpyPrime_dTkPrime - delEnt/delT ) / delT + endif + endif + + ! loop through layers + do iLayer=1,nLayers + delT = mLayerTempPrime(iLayer) + if(abs(delT) <= 1e-2_rkind)then + ! get the soil layer + if(iLayer>nSnow) iSoil = iLayer-nSnow + select case(layerType(iLayer)) + ! * soil + case(iname_soil) + mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component + iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component + ! derivatives + dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use + Tcrit = crit_soilT( mLayerMatricHeadTrial(iSoil) ) + if( mLayerTempTrial(iLayer) < Tcrit)then + dVolHtCapBulk_dPsi0(iSoil) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(iSoil) + dVolHtCapBulk_dTk(iLayer) = (-iden_ice * Cp_ice + iden_water * Cp_water) * mLayerdTheta_dTk(iLayer) + else + dVolHtCapBulk_dPsi0(iSoil) = (iden_water*Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(iSoil) + dVolHtCapBulk_dTk(iLayer) = 0._rkind + endif + dVolHtCapBulk_dPsi0Prime(iLayer) = 0._rkind + + case(iname_snow) + mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component + ! derivatives + fLiq = mLayerFracLiqSnow(iLayer) + dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air + if( mLayerTempTrial(iLayer) < Tfreeze)then + dVolHtCapBulk_dTk(iLayer) = ( iden_water * (-Cp_ice + Cp_water) + iden_air * (iden_water/iden_ice - 1._rkind) * Cp_air ) * mLayerdTheta_dTk(iLayer) + else + dVolHtCapBulk_dTk(iLayer) = 0._rkind + endif + dVolHtCapBulk_dThetaPrime(iLayer) = 0._rkind + + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return + end select + dVolHtCapBulk_dTkPrime(iLayer) = 0._rkind + else + delEnt = mLayerEnthalpyPrime(iLayer) + mLayerHeatCap(iLayer) = delEnt / delT + ! derivatives + if(iLayer>nSnow)then + iSoil = iLayer-nSnow + dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use + dVolHtCapBulk_dThetaPrime(iLayer) = realMissing ! do not use + dVolHtCapBulk_dPsi0(iSoil) = dEnthalpyPrime_dWat(iLayer) / delT + dVolHtCapBulk_dPsi0Prime(iSoil) = dEnthalpyPrime_dWatPrime(iLayer) / delT + else + dVolHtCapBulk_dTheta(iLayer) = dEnthalpyPrime_dWat(iLayer) / delT + dVolHtCapBulk_dThetaPrime(iLayer) = dEnthalpyPrime_dWatPrime(iLayer) / delT + endif + dVolHtCapBulk_dTk(iLayer) = dEnthalpyPrime_dTk(iLayer) / delT + dVolHtCapBulk_dTkPrime(iLayer) = ( dEnthalpyPrime_dTkPrime(iLayer) - delEnt/delT ) / delT + endif + end do ! looping through layers + + end associate + +end subroutine computHeatCapWithPrime + + +end module computHeatCapWithPrime_module \ No newline at end of file diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index c189af2c7..3e9f1fccf 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -175,12 +175,12 @@ subroutine computJacobWithPrime(& ! associate variables from data structures associate(& ! indices of model state variables - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain - ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain + ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer ! vectors of indices for specfic state types within specific sub-domains IN THE FULL STATE VECTOR ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain ! vector of energy indices for the snow and soil domains @@ -239,30 +239,30 @@ subroutine computJacobWithPrime(& dNrgFlux_dWatAbove => deriv_data%var(iLookDERIV%dNrgFlux_dWatAbove )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. water state in the layer above dNrgFlux_dWatBelow => deriv_data%var(iLookDERIV%dNrgFlux_dWatBelow )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. water state in the layer below ! derivatives in soil transpiration w.r.t. canopy state variables - mLayerdTrans_dTCanair => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanair )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. canopy air temperature - mLayerdTrans_dTCanopy => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanopy )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. canopy temperature - mLayerdTrans_dTGround => deriv_data%var(iLookDERIV%mLayerdTrans_dTGround )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. ground temperature - mLayerdTrans_dCanWat => deriv_data%var(iLookDERIV%mLayerdTrans_dCanWat )%dat ,& ! intent(in): derivatives in the soil layer transpiration flux w.r.t. canopy total water + mLayerdTrans_dTCanair => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanair )%dat ,& ! intent(in): [dp(:)] derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + mLayerdTrans_dTCanopy => deriv_data%var(iLookDERIV%mLayerdTrans_dTCanopy )%dat ,& ! intent(in): [dp(:)] derivatives in the soil layer transpiration flux w.r.t. canopy temperature + mLayerdTrans_dTGround => deriv_data%var(iLookDERIV%mLayerdTrans_dTGround )%dat ,& ! intent(in): [dp(:)] derivatives in the soil layer transpiration flux w.r.t. ground temperature + mLayerdTrans_dCanWat => deriv_data%var(iLookDERIV%mLayerdTrans_dCanWat )%dat ,& ! intent(in): [dp(:)] derivatives in the soil layer transpiration flux w.r.t. canopy total water ! derivatives in aquifer transpiration w.r.t. canopy state variables - dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. canopy air temperature - dAquiferTrans_dTCanopy => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanopy )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. canopy temperature - dAquiferTrans_dTGround => deriv_data%var(iLookDERIV%dAquiferTrans_dTGround )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. ground temperature - dAquiferTrans_dCanWat => deriv_data%var(iLookDERIV%dAquiferTrans_dCanWat )%dat(1) ,& ! intent(in): derivatives in the aquifer transpiration flux w.r.t. canopy total water + dAquiferTrans_dTCanair => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanair )%dat(1) ,& ! intent(in): [dp] derivatives in the aquifer transpiration flux w.r.t. canopy air temperature + dAquiferTrans_dTCanopy => deriv_data%var(iLookDERIV%dAquiferTrans_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivatives in the aquifer transpiration flux w.r.t. canopy temperature + dAquiferTrans_dTGround => deriv_data%var(iLookDERIV%dAquiferTrans_dTGround )%dat(1) ,& ! intent(in): [dp] derivatives in the aquifer transpiration flux w.r.t. ground temperature + dAquiferTrans_dCanWat => deriv_data%var(iLookDERIV%dAquiferTrans_dCanWat )%dat(1) ,& ! intent(in): [dp] derivatives in the aquifer transpiration flux w.r.t. canopy total water ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(in): [dp(:)] derivative in vertical liquid water flux at layer interfaces ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential - d2VolTot_d2Psi0 => deriv_data%var(iLookDERIV%d2VolTot_d2Psi0 )%dat ,& ! intent(in): [dp(:)] second derivative in total water content w.r.t. total water matric potential + d2VolTot_dPsi02 => deriv_data%var(iLookDERIV%d2VolTot_dPsi02 )%dat ,& ! intent(in): [dp(:)] second derivative in total water content w.r.t. total water matric potential dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t matric head dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below - dq_dHydStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dHydStateLayerSurfVec )%dat ,& ! intent(in): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers + dq_dHydStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dHydStateLayerSurfVec )%dat ,& ! intent(in): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers ! derivative in baseflow flux w.r.t. aquifer storage - dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(in): [dp(:)] derivative in baseflow flux w.r.t. aquifer storage (s-1) + dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(in): [dp(:)] derivative in baseflow flux w.r.t. aquifer storage (s-1) ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below - dq_dNrgStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dNrgStateLayerSurfVec )%dat ,& ! intent(in): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers + dq_dNrgStateLayerSurfVec => deriv_data%var(iLookDERIV%dq_dNrgStateLayerSurfVec )%dat ,& ! intent(in): [dp(:)] change in the flux in soil surface interface w.r.t. state variables in layers ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature dFracLiqSnow_dTk => deriv_data%var(iLookDERIV%dFracLiqSnow_dTk )%dat ,& ! intent(in): [dp(:)] derivative in fraction of liquid snow w.r.t. temperature mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk )%dat ,& ! intent(in): [dp(:)] derivative in volumetric liquid water content w.r.t. temperature @@ -273,6 +273,11 @@ subroutine computJacobWithPrime(& dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dPsi0Prime => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0Prime )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. prime matric potential + dVolHtCapBulk_dThetaPrime => deriv_data%var(iLookDERIV%dVolHtCapBulk_dThetaPrime )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. prime volumetric water content + dVolHtCapBulk_dCanWatPrime => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWatPrime )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. prime volumetric water content + dVolHtCapBulk_dTkPrime => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkPrime )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. prime temperature + dVolHtCapBulk_dTkCanPrime => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanPrime )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. prime temperature ! derivative in Cm w.r.t. relevant state variables dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk )%dat ,& ! intent(in): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) @@ -329,7 +334,7 @@ subroutine computJacobWithPrime(& ! compute additional terms for the Jacobian for the soil domain (excluding fluxes) do iLayer=1,nSoil if(ixSoilOnlyHyd(iLayer)/=integerMissing)then - dMat(ixSoilOnlyHyd(iLayer)) = ( dVolTot_dPsi0(iLayer) + dCompress_dPsi(iLayer) ) * cj + d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) + dMat(ixSoilOnlyHyd(iLayer)) = ( dVolTot_dPsi0(iLayer) + dCompress_dPsi(iLayer) ) * cj + d2VolTot_dPsi02(iLayer) * mLayerMatricHeadPrime(iLayer) if(ixRichards==mixdform)then dMat(ixSoilOnlyHyd(iLayer)) = dMat(ixSoilOnlyHyd(iLayer)) + specificStorage * dVolTot_dPsi0(iLayer) * mLayerMatricHeadPrime(iLayer) / theta_sat(iLayer) @@ -657,10 +662,10 @@ subroutine computJacobWithPrime(& ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) * cj & - + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + mLayerCm(jLayer) * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) + + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + mLayerCm(jLayer) * d2VolTot_dPsi02(iLayer) * mLayerMatricHeadPrime(iLayer) if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(ixOffDiag(nrgState,watState),watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) * cj & - - LH_fus*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_d2Psi0(iLayer) + aJac(ixOffDiag(nrgState,watState),watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + - LH_fus*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_dPsi02(iLayer) + aJac(ixOffDiag(nrgState,watState),watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content endif ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above @@ -990,10 +995,10 @@ subroutine computJacobWithPrime(& ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) * cj & - + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + mLayerCm(jLayer) * d2VolTot_d2Psi0(iLayer) * mLayerMatricHeadPrime(iLayer) + + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + mLayerCm(jLayer) * d2VolTot_dPsi02(iLayer) * mLayerMatricHeadPrime(iLayer) if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(nrgState,watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) * cj & - - LH_fus*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_d2Psi0(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + - LH_fus*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_dPsi02(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content endif ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 0e20c21da..233180de3 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -130,9 +130,9 @@ subroutine eval8summa(& USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy USE computFlux_module, only:soilCmpres ! compute soil compression USE computFlux_module, only:computFlux ! compute fluxes given a state vector - USE computHeatCap_module,only:computHeatCap ! recompute heat capacity and derivatives - USE computHeatCap_module,only:computHeatCapAnalytic ! recompute heat capacity and derivatives - USE computHeatCap_module,only:computCm + USE computHeatCap_module,only:computHeatCap ! recompute heat capacity (Cp) and derivatives + USE computHeatCap_module,only:computHeatCapAnalytic ! recompute closed form heat capacity (Cp) and derivatives + USE computHeatCap_module,only:computCm ! compute Cm and derivatives USE computHeatCap_module, only:computStatMult ! recompute state multiplier USE computResid_module,only:computResid ! compute residuals given a state vector USE computThermConduct_module,only:computThermConduct ! recompute thermal conductivity and derivatives @@ -322,7 +322,7 @@ subroutine eval8summa(& ixEnd = nSoil endif - ! initialize to state variable from the last update + ! initialize to state variable from the last update incase splitting is used scalarCanairTempTrial = scalarCanairTemp scalarCanopyTempTrial = scalarCanopyTemp scalarCanopyWatTrial = scalarCanopyWat @@ -390,7 +390,7 @@ subroutine eval8summa(& if(ixHowHeatCap == enthalpyFD)then ! compute H_T without phase change call t2enthalpy(& - ! input: data structures + ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure indx_data, & ! intent(in): model indices diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index bd5caecec..fed0cea50 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -51,87 +51,79 @@ module eval8summaWithPrime_module ! ********************************************************************************************************** subroutine eval8summaWithPrime(& ! input: model control - dt, & ! intent(in): entire time step for drainage pond rate - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - nState, & ! intent(in): total number of state variables - insideSUN, & ! intent(in): flag to indicate if we are inside Sundials solver - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVec, & ! intent(in): model state vector - stateVecPrime, & ! intent(in): derivative of model state vector - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + dt, & ! intent(in): entire time step for drainage pond rate + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + nState, & ! intent(in): total number of state variables + insideSUN, & ! intent(in): flag to indicate if we are inside Sundials solver + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVec, & ! intent(in): model state vector + stateVecPrime, & ! intent(in): derivative of model state vector + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup data - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout) model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: here we need to pass some extra variables that do not get updated in in the IDA loops - scalarCanairTempTrial, & ! intent(out): trial value for temperature of the canopy air space (K) - scalarCanairEnthalpyTrial,& ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) - scalarCanopyTempPrev, & ! intent(in): value of canopy temperature (K) - scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) - scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) - scalarCanopyEnthalpyPrev,& ! intent(in): value for enthalpy of the vegetation canopy (J m-3) - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - mLayerTempPrev, & ! intent(in): vector of layer temperature (K) - mLayerMatricHeadLiqTrial,& ! intent(out): trial value for liquid water matric potential (m) - mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) - mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) - mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - mLayerVolFracWatPrev, & ! intent(in): vector of volumetric total water content (-) - mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) - mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) - mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) - scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) - scalarAquiferStoragePrev,& ! intent(in): value of storage of water in the aquifer (m) - mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) - mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) - mLayerTempPrime, & ! intent(out): derivative value for temperature of each snow and soil layer (K) - mLayerMatricHeadPrime, & ! intent(out): derivative value for matric head of each snow and soil layer (m) - mLayerMatricHeadLiqPrime,& ! intent(out): derivative value for liquid water matric head of each snow and soil layer (m) - mLayerVolFracWatPrime, & ! intent(out): derivative value for volumetric total water content of each snow and soil layer (-) - scalarCanopyTempPrime, & ! intent(out): derivative value for temperature of the vegetation canopy (K) - scalarCanopyWatPrime, & ! intent(out): derivative value for total water content of the vegetation canopy (kg m-2) - ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup data + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data stuctures + indx_data, & ! intent(inout): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input: previous values of variables needed in data window outside of internal IDA + scalarCanopyTempPrev, & ! intent(in): previous value for temperature of the vegetation canopy (K) + mLayerTempPrev, & ! intent(in): previous vector of layer temperature (K) + mLayerMatricHeadPrev, & ! intent(in): previous value for total water matric potential (m) + ! output: new values of variables needed in data window outside of internal IDA + scalarCanopyTempTrial, & ! intent(out): trial value for temperature of the vegetation canopy (K) + scalarCanopyWatTrial, & ! intent(out): trial value for mass of total water on the vegetation canopy (kg m-2) + mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) + mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) + ! output: new prime values of variables needed in data window outside of internal IDA + scalarCanairTempPrime, & ! intent(out): prime value for temperature of the canopy air space (K s-1) + scalarCanopyTempPrime, & ! intent(out): prime value for temperature of the vegetation canopy (K s-1) + scalarCanopyWatPrime, & ! intent(out): prime value for total water content of the vegetation canopy (kg m-2 s-1) + scalarCanopyIcePrime, & ! intent(out): prime value for mass of ice on the vegetation canopy (kg m-2 s-1) + mLayerTempPrime, & ! intent(out): prime value for temperature of each snow and soil layer (K s-1) + mLayerMatricHeadPrime, & ! intent(out): prime value for matric head of each snow and soil layer (m s-1) + mLayerMatricHeadLiqPrime, & ! intent(out): prime value for liquid water matric potential (m s-1) + mLayerVolFracWatPrime, & ! intent(out): prime value for volumetric total water content of each snow and soil layer (s-1) + mLayerVolFracIcePrime, & ! intent(out): prime value for volumetric fraction of ice (s-1) + ! output: enthalpy prime values + scalarCanairEnthalpyPrime, & ! intent(out): prime value for enthalpy of the canopy air space (J m-3 s-1) + scalarCanopyEnthalpyPrime, & ! intent(out): prime value of enthalpy of the vegetation canopy (J m-3 s-1) + mLayerEnthalpyPrime, & ! intent(out): prime vector of enthalpy for snow+soil layers (J m-3 s-1) + ! input-output: baseflow + ixSaturation, & ! intent(inout): index of the lowest saturated layer + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors - feasible, & ! intent(out): flag to denote the feasibility of the solution - fluxVec, & ! intent(out): flux vector - resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - resVec, & ! intent(out): residual vector - err,message) ! intent(out): error control + feasible, & ! intent(out): flag to denote the feasibility of the solution + fluxVec, & ! intent(out): flux vector + resSink, & ! intent(out): sink terms on the RHS of the flux equation + resVec, & ! intent(out): residual vector + err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- ! provide access to subroutines USE getVectorz_module, only:varExtract ! extract variables from the state vector USE getVectorz_module, only:checkFeas ! check feasibility of state vector USE updateVarsWithPrime_module, only:updateVarsWithPrime ! update variables - USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy + USE t2enthalpyAddPrime_module, only:t2enthalpyPrime ! compute enthalpy prime and derivatives USE computFlux_module, only:soilCmpresPrime ! compute soil compression USE computFlux_module, only:computFlux ! compute fluxes given a state vector - USE computHeatCap_module,only:computHeatCap ! recompute heat capacity and derivatives - USE computHeatCap_module,only:computHeatCapAnalytic ! recompute heat capacity and derivatives - USE computHeatCap_module,only:computCm + USE computHeatCapWithPrime_module,only:computHeatCapWithPrime ! recompute heat capacity (Cp) and derivatives + USE computHeatCap_module,only:computHeatCapAnalytic ! recompute closed form heat capacity (Cp) and derivatives + USE computHeatCap_module,only:computCm ! compute Cm and derivatives USE computHeatCap_module, only:computStatMult ! recompute state multiplier USE computResidWithPrime_module,only:computResidWithPrime ! compute residuals given a state vector USE computThermConduct_module,only:computThermConduct ! recompute thermal conductivity and derivatives @@ -139,154 +131,157 @@ subroutine eval8summaWithPrime(& ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control - real(rkind),intent(in) :: dt ! entire time step for drainage pond rate - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - integer,intent(in) :: nState ! total number of state variables - logical(lgt),intent(in) :: insideSUN ! flag to indicate if we are inside Sundials solver - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt),intent(inout) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - ! input: state vectors - real(rkind),intent(in) :: stateVec(:) ! model state vector - real(rkind),intent(in) :: stateVecPrime(:) ! model state vector - real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + real(rkind),intent(in) :: dt ! entire time step for drainage pond rate + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + integer,intent(in) :: nState ! total number of state variables + logical(lgt),intent(in) :: insideSUN ! flag to indicate if we are inside Sundials solver + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt),intent(inout) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + ! input: state vectors + real(rkind),intent(in) :: stateVec(:) ! model state vector + real(rkind),intent(in) :: stateVecPrime(:) ! model state vector + real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) ! input: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(zLookup), intent(in) :: lookup_data ! lookup tables - type(var_i), intent(in) :: type_data ! type of vegetation and soil - type(var_d), intent(in) :: attr_data ! spatial attributes - type(var_dlength), intent(in) :: mpar_data ! model parameters - type(var_d), intent(in) :: forc_data ! model forcing data - type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin - type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup), intent(in) :: lookup_data ! lookup tables + type(var_i), intent(in) :: type_data ! type of vegetation and soil + type(var_d), intent(in) :: attr_data ! spatial attributes + type(var_dlength), intent(in) :: mpar_data ! model parameters + type(var_d), intent(in) :: forc_data ! model forcing data + type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin + type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU ! output: data structures - type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! input-output: here we need to pass some extra variables that do not get updated in in the IDA loops - real(rkind),intent(out) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind),intent(out) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3) - real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind),intent(in) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) - real(rkind),intent(out) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: scalarCanopyIcePrev ! previous value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(out) :: scalarCanopyLiqTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: scalarCanopyLiqPrev ! previous value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),intent(out) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(in) :: scalarCanopyEnthalpyPrev ! previous value of enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(in) :: mLayerTempPrev(:) ! previous vector of layer temperature (K) - real(rkind),intent(out) :: mLayerMatricHeadLiqTrial(:) ! trial value for liquid water matric potential (m) - real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial value for total water matric potential (m) - real(rkind),intent(in) :: mLayerMatricHeadPrev(:) ! previous value for total water matric potential (m) - real(rkind),intent(out) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(rkind),intent(in) :: mLayerVolFracWatPrev(:) ! previous vector of volumetric total water content (-) - real(rkind),intent(out) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric ice water content (-) - real(rkind),intent(in) :: mLayerVolFracIcePrev(:) ! previous vector of volumetric ice water content (-) - real(rkind),intent(out) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(in) :: mLayerVolFracLiqPrev(:) ! previous vector of volumetric liquid water content (-) - real(rkind),intent(out) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) - real(rkind),intent(in) :: scalarAquiferStoragePrev ! previous value of storage of water in the aquifer (m) - real(rkind),intent(in) :: mLayerEnthalpyPrev(:) ! previous vector of enthalpy for snow+soil layers (J m-3) - real(rkind),intent(out) :: mLayerEnthalpyTrial(:) ! trial vector of enthalpy for snow+soil layers (J m-3) - real(rkind),intent(out) :: mLayerTempPrime(:) ! derivative value for temperature of each snow and soil layer (K) - real(rkind),intent(out) :: mLayerMatricHeadPrime(:) ! derivative value for matric head of each snow and soil layer (m) - real(rkind),intent(out) :: mLayerMatricHeadLiqPrime(:) ! derivative value for liquid water matric head of each snow and soil layer (m) - real(rkind),intent(out) :: mLayerVolFracWatPrime(:) ! derivative value for volumetric total water content of each snow and soil layer (-) - real(rkind),intent(out) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) - real(rkind),intent(out) :: scalarCanopyWatPrime ! derivative value for total water content of the vegetation canopy (kg m-2) - ! input-output: baseflow - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer - real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + ! input: previous values of variables needed in data window outside of internal IDA + real(rkind),intent(in) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) + real(rkind),intent(in) :: mLayerTempPrev(:) ! previous vector of layer temperature (K) + real(rkind),intent(in) :: mLayerMatricHeadPrev(:) ! previous value for total water matric potential (m) + ! output: new values of variables needed in data window outside of internal IDA + real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind),intent(out) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial vector for total water matric potential (m) + real(rkind),intent(out) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + ! output: new prime values of variables needed in data window outside of internal IDA + real(rkind),intent(out) :: scalarCanairTempPrime ! prime value for temperature of the canopy air space (K s-1) + real(rkind),intent(out) :: scalarCanopyTempPrime ! prime value for temperature of the vegetation canopy (K s-1) + real(rkind),intent(out) :: scalarCanopyWatPrime ! prime value for total water content of the vegetation canopy (kg m-2 s-1) + real(rkind),intent(out) :: scalarCanopyIcePrime ! prime value for mass of ice on the vegetation canopy (kg m-2 s-1) + real(rkind),intent(out) :: mLayerTempPrime(:) ! prime vector for temperature of each snow and soil layer (K s-1) + real(rkind),intent(out) :: mLayerMatricHeadPrime(:) ! prime vector for matric head of each snow and soil layer (m s-1) + real(rkind),intent(out) :: mLayerMatricHeadLiqPrime(:) ! prime vector for liquid water matric potential (m s-1) + real(rkind),intent(out) :: mLayerVolFracWatPrime(:) ! prime vector for volumetric total water content of each snow and soil layer (s-1) + real(rkind),intent(out) :: mLayerVolFracIcePrime(:) ! prime vector for volumetric fraction of ice (s-1) + ! output: enthalpy prime values + real(rkind),intent(out) :: scalarCanairEnthalpyPrime ! prime value for enthalpy of the canopy air space (J m-3 s-1) + real(rkind),intent(out) :: scalarCanopyEnthalpyPrime ! prime value of enthalpy of the vegetation canopy (J m-3 s-1) + real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! prime vector of enthalpy for snow+soil layers (J m-3 s-1) + ! input-output: baseflow + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer + real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors - logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution - real(rkind),intent(out) :: fluxVec(:) ! flux vector - real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation - real(qp),intent(out) :: resVec(:) ! NOTE: qp ! residual vector + logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution + real(rkind),intent(out) :: fluxVec(:) ! flux vector + real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation + real(qp),intent(out) :: resVec(:) ! NOTE: qp ! residual vector ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- - real(rkind) :: dt1 ! residual step size + real(rkind) :: dt1 ! residual step size ! state variables - real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - ! derivative of state variables - real(rkind) :: scalarCanairTempPrime ! derivative value for temperature of the canopy air space (K) - real(rkind) :: scalarAquiferStoragePrime ! derivative value of storage of water in the aquifer (m) - ! derivative of diagnostic variables - real(rkind) :: scalarCanopyLiqPrime ! derivative value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIcePrime ! derivative value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! derivative value for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! derivative value for volumetric fraction of ice (-) - ! enthalpy - real(rkind) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature - real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state - real(rkind),dimension(nLayers) :: dEnthalpy_dTk ! derivatives in layer enthalpy w.r.t. temperature - real(rkind),dimension(nLayers) :: dEnthalpy_dWat ! derivatives in layer enthalpy w.r.t. water state + real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyLiqTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind) :: scalarCanopyIceTrial ! trial value for ice storage in the canopy (kg m-2) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric liquid water content (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric ice content (-) + real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + real(rkind) :: scalarCanopyLiqPrime ! prime value for liquid water storage in the canopy (kg m-2 s-1) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! prime vector for volumetric liquid water content (s-1) + real(rkind) :: scalarAquiferStoragePrime ! prime value of storage of water in the aquifer (m s-1) + ! enthalpy derivatives + real(rkind) :: dCanEnthalpyPrime_dTk ! derivatives in prime canopy enthalpy w.r.t. temperature + real(rkind) :: dCanEnthalpyPrime_dWat ! derivatives in prime canopy enthalpy w.r.t. water state + real(rkind),dimension(nLayers) :: dEnthalpyPrime_dTk ! derivatives in prime layer enthalpy w.r.t. temperature + real(rkind),dimension(nLayers) :: dEnthalpyPrime_dWat ! derivatives in prime layer enthalpy w.r.t. water state + real(rkind) :: dCanEnthalpyPrime_dTkPrime ! derivatives in prime canopy enthalpy w.r.t. prime temperature + real(rkind) :: dCanEnthalpyPrime_dWatPrime ! derivatives in prime canopy enthalpy w.r.t. prime water state + real(rkind),dimension(nLayers) :: dEnthalpyPrime_dTkPrime ! derivatives in prime layer enthalpy w.r.t. prime temperature + real(rkind),dimension(nLayers) :: dEnthalpyPrime_dWatPrime ! derivatives in prime layer enthalpy w.r.t. prime water state ! other local variables - integer(i4b) :: iLayer ! index of model layer in the snow+soil domain - integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain - integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine - character(LEN=256) :: cmessage ! error message of downwind routine - real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy - real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil - logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step - logical(lgt),parameter :: needCm=.true. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m + integer(i4b) :: iLayer ! index of model layer in the snow+soil domain + integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain + integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine + character(LEN=256) :: cmessage ! error message of downwind routine + real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy + real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil + logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step + logical(lgt),parameter :: needCm=.true. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m ! -------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- associate(& ! model decisions - ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy - ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation + ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy + ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) ! soil parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) ! model diagnostic variables from a previous solution - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) - scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) ! soil compression - scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2 s-1) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in volumetric water content due to compression of soil (s-1) + scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2 s-1) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in volumetric water content due to compression of soil (s-1) ! derivatives - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential - dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1),& ! intent(out): [dp] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1),&!intent(out): [dp] derivative in bulk heat capacity w.r.t. temperature - dThermalC_dWatAbove => deriv_data%var(iLookDERIV%dThermalC_dWatAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above - dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk)%dat ,& ! intent(out): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) - dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + d2VolTot_dPsi02 => deriv_data%var(iLookDERIV%d2VolTot_dPsi02)%dat ,& ! intent(in): [dp(:)] second derivative in total water content w.r.t. total water matric potential + dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1) ,& ! intent(out): [dp] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dPsi0Prime => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0Prime)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. prime matric potential + dVolHtCapBulk_dThetaPrime => deriv_data%var(iLookDERIV%dVolHtCapBulk_dThetaPrime)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. prime volumetric water content + dVolHtCapBulk_dCanWatPrime=> deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWatPrime)%dat(1) ,& ! intent(out): [dp] derivative in bulk heat capacity w.r.t. prime volumetric water content + dVolHtCapBulk_dTkPrime => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkPrime)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. prime temperature + dVolHtCapBulk_dTkCanPrime => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanPrime)%dat(1) ,& ! intent(out): [dp] derivative in bulk heat capacity w.r.t. prime temperature + dThermalC_dWatAbove => deriv_data%var(iLookDERIV%dThermalC_dWatAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk)%dat ,& ! intent(out): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) + dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) ! mapping - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)]mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)]index of control volume for different domains (veg, snow, soil) + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) ! heat capacity - heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(out): [dp] volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(out): [dp(:)] heat capacity for snow and soil + heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(out): [dp] volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(out): [dp(:)] heat capacity for snow and soil ! Cm - canopyCmTrial => diag_data%var(iLookDIAG%scalarCanopyCm)%dat(1) ,& ! intent(out): [dp] Cm of the canopy - mLayerCmTrial => diag_data%var(iLookDIAG%mLayerCm)%dat & ! intent(out): [dp(:)] Cm of snow and soil + canopyCmTrial => diag_data%var(iLookDIAG%scalarCanopyCm)%dat(1) ,& ! intent(out): [dp] Cm of the canopy + mLayerCmTrial => diag_data%var(iLookDIAG%mLayerCm)%dat & ! intent(out): [dp(:)] Cm of snow and soil ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -325,18 +320,19 @@ subroutine eval8summaWithPrime(& ixEnd = nSoil endif - ! initialize to state variable from the last update - scalarCanairTempTrial = realMissing ! should be set to previous values if splits, but for now operator splitting is not hooked up + ! Placeholder: if we decide to use splitting, we need to pass all the previous values of the state variables + scalarCanairTempTrial = realMissing scalarCanopyTempTrial = scalarCanopyTempPrev - scalarCanopyWatTrial = scalarCanopyLiqPrev + scalarCanopyIcePrev - scalarCanopyLiqTrial = scalarCanopyLiqPrev - scalarCanopyIceTrial = scalarCanopyIcePrev + scalarCanopyWatTrial = realMissing + scalarCanopyLiqTrial = realMissing + scalarCanopyIceTrial = realMissing mLayerTempTrial = mLayerTempPrev - mLayerVolFracWatTrial = mLayerVolFracWatPrev - mLayerVolFracLiqTrial = mLayerVolFracLiqPrev - mLayerVolFracIceTrial = mLayerVolFracIcePrev + mLayerVolFracWatTrial = realMissing + mLayerVolFracLiqTrial = realMissing + mLayerVolFracIceTrial = realMissing mLayerMatricHeadTrial = mLayerMatricHeadPrev - scalarAquiferStorageTrial = scalarAquiferStoragePrev + mLayerMatricHeadLiqTrial = realMissing + scalarAquiferStorageTrial = realMissing ! extract states from the state vector call varExtract(& @@ -362,8 +358,7 @@ subroutine eval8summaWithPrime(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! initialize to state variable from the last update - ! should all be set to previous values if splits, but for now operator splitting is not hooked up + ! Placeholder: if we decide to use splitting, we need to pass all the previous values of the state variables scalarCanairTempPrime = realMissing scalarCanopyTempPrime = realMissing scalarCanopyWatPrime = realMissing @@ -386,7 +381,7 @@ subroutine eval8summaWithPrime(& ! output: variables for the vegetation canopy scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K) scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(niout): derivative of canopy total water (kg m-2) + scalarCanopyWatPrime, & ! intent(inout): derivative of canopy total water (kg m-2) scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K) @@ -426,7 +421,7 @@ subroutine eval8summaWithPrime(& mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! + mLayerTempPrime, & ! intent(inout): trial vector of time derivative layer temperature (K) mLayerVolFracWatPrime, & ! intent(inout): trial vector of time derivative volumetric total water content (-) mLayerVolFracLiqPrime, & ! intent(inout): trial vector of time derivative volumetric liquid water content (-) mLayerVolFracIcePrime, & ! intent(inout): trial vector of time derivative volumetric ice water content (-) @@ -439,77 +434,94 @@ subroutine eval8summaWithPrime(& if(updateCp)then ! *** compute volumetric heat capacity C_p if(ixHowHeatCap == enthalpyFD)then - ! compute H_T without phase change - call t2enthalpy(& + ! compute H_T prime without phase change + call t2enthalpyPrime(& ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTempPrime, & ! intent(in): prime value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + scalarCanopyTempPrime, & ! intent(in): prime value of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(in): prime value of canopy total water (kg m-2) ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerTempPrime, & ! intent(in): prime vector of layer temperature (K) + mLayerVolFracWatPrime, & ! intent(in): prime vector of volumetric total water content (-) + mLayerMatricHeadPrime, & ! intent(in): prime vector of total water matric potential (m) ! input: pre-computed derivatives - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - ! output: enthalpy - scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) - dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature - dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state - dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature - dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + d2VolTot_dPsi02, & ! intent(in): second derivative in total water content w.r.t. total water matric potential (m-2) + ! output: enthalpy prime and derivatives + scalarCanairEnthalpyPrime, & ! intent(out): prime enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyPrime, & ! intent(out): prime enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyPrime, & ! intent(out): prime enthalpy of each snow+soil layer (J m-3) + dCanEnthalpyPrime_dTk, & ! intent(out): derivatives in prime canopy enthalpy w.r.t. temperature + dCanEnthalpyPrime_dWat, & ! intent(out): derivatives in prime canopy enthalpy w.r.t. water state + dEnthalpyPrime_dTk, & ! intent(out): derivatives in prime layer enthalpy w.r.t. temperature + dEnthalpyPrime_dWat, & ! intent(out): derivatives in prime layer enthalpy w.r.t. water state + dCanEnthalpyPrime_dTkPrime, & ! intent(out): derivatives in prime canopy enthalpy w.r.t. prime temperature + dCanEnthalpyPrime_dWatPrime, & ! intent(out): derivatives in prime canopy enthalpy w.r.t. prime water state + dEnthalpyPrime_dTkPrime, & ! intent(out): derivatives in prime layer enthalpy w.r.t. prime temperature + dEnthalpyPrime_dWatPrime, & ! intent(out): derivatives in prime layer enthalpy w.r.t. prime water state ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! *** compute volumetric heat capacity C_p = dH_T/dT - call computHeatCap(& + call computHeatCapWithPrime(& ! input: control variables - nLayers, & ! intent(in): number of layers (soil+snow) - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) + nLayers, & ! intent(in): number of layers (soil+snow) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) ! input output data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - diag_data, & ! intent(inout): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) - scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial temperature - mLayerTempPrev, & ! intent(in): previous temperature - mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil - mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil - mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) + scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyTempPrime, & ! intent(in): prime value of canopy temperature (K) + scalarCanopyEnthalpyPrime, & ! intent(in): prime enthalpy of the vegetation canopy (J m-3) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial temperature + mLayerTempPrime, & ! intent(in): prime temperature + mLayerEnthalpyPrime, & ! intent(in): prime enthalpy for snow and soil + mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - dCanEnthalpy_dTk, & ! intent(in): derivatives in canopy enthalpy w.r.t. temperature - dCanEnthalpy_dWat, & ! intent(in): derivatives in canopy enthalpy w.r.t. water state - dEnthalpy_dTk, & ! intent(in): derivatives in layer enthalpy w.r.t. temperature - dEnthalpy_dWat, & ! intent(in): derivatives in layer enthalpy w.r.t. water state + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + dCanEnthalpyPrime_dTk, & ! intent(in): derivatives in prime canopy enthalpy w.r.t. temperature + dCanEnthalpyPrime_dWat, & ! intent(in): derivatives in prime canopy enthalpy w.r.t. water state + dEnthalpyPrime_dTk, & ! intent(in): derivatives in prime layer enthalpy w.r.t. temperature + dEnthalpyPrime_dWat, & ! intent(in): derivatives in prime layer enthalpy w.r.t. water state + dCanEnthalpyPrime_dTkPrime, & ! intent(in): derivatives in prime canopy enthalpy w.r.t. prime temperature + dCanEnthalpyPrime_dWatPrime,& ! intent(in): derivatives in prime canopy enthalpy w.r.t. prime water state + dEnthalpyPrime_dTkPrime, & ! intent(in): derivatives in prime layer enthalpy w.r.t. prime temperature + dEnthalpyPrime_dWatPrime, & ! intent(in): derivatives in prime layer enthalpy w.r.t. prime water state ! output - heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil - dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil + dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dPsi0Prime, & ! intent(out): derivative in bulk heat capacity w.r.t. prime matric potential + dVolHtCapBulk_dThetaPrime, & ! intent(out): derivative in bulk heat capacity w.r.t. prime volumetric water content + dVolHtCapBulk_dCanWatPrime, & ! intent(out): derivative in bulk heat capacity w.r.t. prime volumetric water content + dVolHtCapBulk_dTkPrime, & ! intent(out): derivative in bulk heat capacity w.r.t. prime temperature + dVolHtCapBulk_dTkCanPrime, & ! intent(out): derivative in bulk heat capacity w.r.t. prime temperature ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -546,6 +558,13 @@ subroutine eval8summaWithPrime(& dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature ! output: error control err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + ! set derivatives to 0 for closed form + dVolHtCapBulk_dPsi0Prime = 0._rkind + dVolHtCapBulk_dThetaPrime = 0._rkind + dVolHtCapBulk_dCanWatPrime = 0._rkind + dVolHtCapBulk_dTkPrime = 0._rkind + dVolHtCapBulk_dTkCanPrime = 0._rkind endif !(choice of how compute heat capacity) ! compute multiplier of state vector @@ -592,15 +611,20 @@ subroutine eval8summaWithPrime(& if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if else ! set heat capacity derivatives to 0 for constant through step - dVolHtCapBulk_dPsi0 = 0._rkind - dVolHtCapBulk_dTheta = 0._rkind - dVolHtCapBulk_dCanWat = 0._rkind - dVolHtCapBulk_dTk = 0._rkind - dVolHtCapBulk_dTkCanopy = 0._rkind - dThermalC_dWatAbove = 0._rkind - dThermalC_dWatBelow = 0._rkind - dThermalC_dTempAbove = 0._rkind - dThermalC_dTempBelow = 0._rkind + dVolHtCapBulk_dPsi0 = 0._rkind + dVolHtCapBulk_dTheta = 0._rkind + dVolHtCapBulk_dCanWat = 0._rkind + dVolHtCapBulk_dTk = 0._rkind + dVolHtCapBulk_dTkCanopy = 0._rkind + dVolHtCapBulk_dPsi0Prime = 0._rkind + dVolHtCapBulk_dThetaPrime = 0._rkind + dVolHtCapBulk_dCanWatPrime = 0._rkind + dVolHtCapBulk_dTkPrime = 0._rkind + dVolHtCapBulk_dTkCanPrime = 0._rkind + dThermalC_dWatAbove = 0._rkind + dThermalC_dWatBelow = 0._rkind + dThermalC_dTempAbove = 0._rkind + dThermalC_dTempBelow = 0._rkind endif ! updateCp if(needCm)then @@ -795,76 +819,68 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user ! compute the flux and the residual vector for a given state vector call eval8summaWithPrime(& ! input: model control - eqns_data%dt, & ! intent(in): data step - eqns_data%nSnow, & ! intent(in): number of snow layers - eqns_data%nSoil, & ! intent(in): number of soil layers - eqns_data%nLayers, & ! intent(in): number of layers - eqns_data%nState, & ! intent(in): number of state variables in the current subset - .true., & ! intent(in): inside SUNDIALS solver - eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation - eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution + eqns_data%dt, & ! intent(in): data step + eqns_data%nSnow, & ! intent(in): number of snow layers + eqns_data%nSoil, & ! intent(in): number of soil layers + eqns_data%nLayers, & ! intent(in): number of layers + eqns_data%nState, & ! intent(in): number of state variables in the current subset + .true., & ! intent(in): inside SUNDIALS solver + eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + eqns_data%firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation + eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + eqns_data%scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors - stateVec, & ! intent(in): model state vector - stateVecPrime, & ! intent(in): model state vector - eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + stateVec, & ! intent(in): model state vector + stateVecPrime, & ! intent(in): model state vector + eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures - eqns_data%model_decisions, & ! intent(in): model decisions - eqns_data%lookup_data, & ! intent(in): lookup data - eqns_data%type_data, & ! intent(in): type of vegetation and soil - eqns_data%attr_data, & ! intent(in): spatial attributes - eqns_data%mpar_data, & ! intent(in): model parameters - eqns_data%forc_data, & ! intent(in): model forcing data - eqns_data%bvar_data, & ! intent(in): average model variables for the entire basin - eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU + eqns_data%model_decisions, & ! intent(in): model decisions + eqns_data%lookup_data, & ! intent(in): lookup data + eqns_data%type_data, & ! intent(in): type of vegetation and soil + eqns_data%attr_data, & ! intent(in): spatial attributes + eqns_data%mpar_data, & ! intent(in): model parameters + eqns_data%forc_data, & ! intent(in): model forcing data + eqns_data%bvar_data, & ! intent(in): average model variables for the entire basin + eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU ! input-output: data structures - eqns_data%indx_data, & ! intent(inout): index data - eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU - eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) - eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: here we need to pass some extra variables that do not get updated in in the IDA loops - eqns_data%scalarCanairTempTrial, & ! intent(out): trial value for the temperature of the canopy air space (K) - eqns_data%scalarCanairEnthalpyTrial,& ! intent(out): enthalpy of the canopy air space (J m-3) - eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - eqns_data%scalarCanopyIceTrial, & ! intent(out): trial value for mass of ice on the vegetation canopy (kg m-2) - eqns_data%scalarCanopyIcePrev, & ! intent(in): value for mass of ice on the vegetation canopy (kg m-2) - eqns_data%scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - eqns_data%scalarCanopyLiqPrev, & ! intent(in): value of canopy liquid water (kg m-2) - eqns_data%scalarCanopyEnthalpyTrial,& ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) - eqns_data%scalarCanopyEnthalpyPrev, & ! intent(in): value for enthalpy of the vegetation canopy (J m-3) - eqns_data%mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - eqns_data%mLayerTempPrev, & ! intent(in): vector of layer temperature (K) - eqns_data%mLayerMatricHeadLiqTrial,& ! intent(out): trial value for liquid water matric potential (m) - eqns_data%mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) - eqns_data%mLayerMatricHeadPrev, & ! intent(in): value for total water matric potential (m) - eqns_data%mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - eqns_data%mLayerVolFracWatPrev, & ! intent(in): vector of volumetric total water content (-) - eqns_data%mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) - eqns_data%mLayerVolFracIcePrev, & ! intent(in): vector of volumetric ice water content (-) - eqns_data%mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - eqns_data%mLayerVolFracLiqPrev, & ! intent(in): vector of volumetric liquid water content (-) - eqns_data%scalarAquiferStorageTrial,& ! intent(out): trial value of storage of water in the aquifer (m) - eqns_data%scalarAquiferStoragePrev, & ! intent(in): value of storage of water in the aquifer (m) - eqns_data%mLayerEnthalpyPrev, & ! intent(in): vector of enthalpy for snow+soil layers (J m-3) - eqns_data%mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy for snow+soil layers (J m-3) - eqns_data%mLayerTempPrime, & ! intent(out): derivative value for temperature of each snow and soil layer (K) - eqns_data%mLayerMatricHeadPrime, & ! intent(out): derivative value for matric head of each snow and soil layer (m) - eqns_data%mLayerMatricHeadLiqPrime,& ! intent(out): derivative value for liquid water matric head of each snow and soil layer (m) - eqns_data%mLayerVolFracWatPrime, & ! intent(out): derivative value for volumetric total water content of each snow and soil layer (-) - eqns_data%scalarCanopyTempPrime, & ! intent(out): derivative value for temperature of the vegetation canopy (K) - eqns_data%scalarCanopyWatPrime, & ! intent(out): derivative value for total water content of the vegetation canopy (kg m-2) + eqns_data%indx_data, & ! intent(inout): index data + eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU + eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) + eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input: previous values of variables needed in data window outside of internal IDA + eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value for temperature of the vegetation canopy (K) + eqns_data%mLayerTempPrev, & ! intent(in): previous vector of layer temperature (K) + eqns_data%mLayerMatricHeadPrev, & ! intent(in): previous value for total water matric potential (m) + ! output: new values of variables needed in data window outside of internal IDA + eqns_data%scalarCanopyTempTrial, & ! intent(out): trial value for temperature of the vegetation canopy (K) + eqns_data%scalarCanopyWatTrial, & ! intent(out): trial value for mass of total water on the vegetation canopy (kg m-2) + eqns_data%mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + eqns_data%mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) + eqns_data%mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) + ! output: new prime values of variables needed in data window outside of internal IDA + eqns_data%scalarCanairTempPrime, & ! intent(out): prime value for temperature of the canopy air space (K s-1) + eqns_data%scalarCanopyTempPrime, & ! intent(out): prime value for temperature of the vegetation canopy (K s-1) + eqns_data%scalarCanopyWatPrime, & ! intent(out): prime value for total water content of the vegetation canopy (kg m-2 s-1) + eqns_data%scalarCanopyIcePrime, & ! intent(out): prime value for mass of ice on the vegetation canopy (kg m-2 s-1) + eqns_data%mLayerTempPrime, & ! intent(out): prime value for temperature of each snow and soil layer (K s-1) + eqns_data%mLayerMatricHeadPrime, & ! intent(out): prime value for matric head of each snow and soil layer (m s-1) + eqns_data%mLayerMatricHeadLiqPrime, & ! intent(out): prime value for liquid water matric potential (m s-1) + eqns_data%mLayerVolFracWatPrime, & ! intent(out): prime value for volumetric total water content of each snow and soil layer (s-1) + eqns_data%mLayerVolFracIcePrime, & ! intent(out): prime value for volumetric fraction of ice (s-1) + ! output: enthalpy prime values + eqns_data%scalarCanairEnthalpyPrime, & ! intent(out): prime value for enthalpy of the canopy air space (J m-3 s-1) + eqns_data%scalarCanopyEnthalpyPrime, & ! intent(out): prime value of enthalpy of the vegetation canopy (J m-3 s-1) + eqns_data%mLayerEnthalpyPrime, & ! intent(out): prime vector of enthalpy for snow+soil layers (J m-3 s-1) ! input-output: baseflow - eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer - eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer + eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors - feasible, & ! intent(out): flag to denote the feasibility of the solution always true inside SUNDIALS - eqns_data%fluxVec, & ! intent(out): flux vector - eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector - eqns_data%err,eqns_data%message) ! intent(out): error control + feasible, & ! intent(out): flag to denote the feasibility of the solution always true inside SUNDIALS + eqns_data%fluxVec, & ! intent(out): flux vector + eqns_data%resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec, & ! intent(out): residual vector + eqns_data%err,eqns_data%message) ! intent(out): error control if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index e684e24f1..751e7b787 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -140,27 +140,27 @@ subroutine summaSolve4ida( & err,message) ! intent(out): error control !======= Inclusions =========== - USE fida_mod ! Fortran interface to IDA - USE fsundials_context_mod ! Fortran interface to SUNContext - USE fnvector_serial_mod ! Fortran interface to serial N_Vector - USE fsundials_nvector_mod ! Fortran interface to generic N_Vector - USE fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix - USE fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix - USE fsundials_matrix_mod ! Fortran interface to generic SUNMatrix - USE fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver - USE fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver - USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver - USE fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver - USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver - USE allocspace_module,only:allocLocal ! allocate local data structures - USE getVectorz_module, only:checkFeas ! check feasibility of state vector - USE eval8summaWithPrime_module,only:eval8summa4ida ! DAE/ODE functions - USE eval8summaWithPrime_module,only:eval8summaWithPrime ! residual of DAE - USE computJacobWithPrime_module,only:computJacob4ida ! system Jacobian - USE tol4ida_module,only:computWeight4ida ! weight required for tolerances - USE var_lookup,only:maxvarDecisions ! maximum number of decisions - USE t2enthalpy_module,only:t2enthalpy ! compute enthalpy - USE t2enthalpy_module,only:t2enthalpy_addphase ! add phase to enthalpy + USE fida_mod ! Fortran interface to IDA + USE fsundials_context_mod ! Fortran interface to SUNContext + USE fnvector_serial_mod ! Fortran interface to serial N_Vector + USE fsundials_nvector_mod ! Fortran interface to generic N_Vector + USE fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix + USE fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix + USE fsundials_matrix_mod ! Fortran interface to generic SUNMatrix + USE fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver + USE fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver + USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver + USE fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver + USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver + USE allocspace_module,only:allocLocal ! allocate local data structures + USE getVectorz_module, only:checkFeas ! check feasibility of state vector + USE eval8summaWithPrime_module,only:eval8summa4ida ! DAE/ODE functions + USE eval8summaWithPrime_module,only:eval8summaWithPrime ! residual of DAE + USE computJacobWithPrime_module,only:computJacob4ida ! system Jacobian + USE tol4ida_module,only:computWeight4ida ! weight required for tolerances + USE var_lookup,only:maxvarDecisions ! maximum number of decisions + USE t2enthalpyAddPrime_module,only:t2enthalpyPrime ! compute enthalpy + USE t2enthalpy_module,only:t2enthalpy_addphase ! add phase to enthalpy !======= Declarations ========= implicit none @@ -219,42 +219,51 @@ subroutine summaSolve4ida( & ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- - type(N_Vector), pointer :: sunvec_y ! sundials solution vector - type(N_Vector), pointer :: sunvec_yp ! sundials derivative vector - type(N_Vector), pointer :: sunvec_av ! sundials tolerance vector - type(SUNMatrix), pointer :: sunmat_A ! sundials matrix - type(SUNLinearSolver), pointer :: sunlinsol_LS ! sundials linear solver - type(SUNNonLinearSolver), pointer :: sunnonlin_NLS ! sundials nonlinear solver - type(c_ptr) :: ida_mem ! IDA memory - type(c_ptr) :: sunctx ! SUNDIALS simulation context - type(data4ida), target :: eqns_data ! IDA type - integer(i4b) :: retval, retvalr ! return value - logical(lgt) :: feasible ! feasibility flag - real(qp) :: t0 ! starting time - real(qp) :: dt_last(1) ! last time step - real(qp) :: dt_diff ! difference from previous timestep - integer(c_long) :: mu, lu ! in banded matrix mode in SUNDIALS type - integer(c_long) :: nState ! total number of state variables in SUNDIALS type - integer(i4b) :: iVar, i ! indices - integer(i4b) :: nRoot ! total number of roots (events) to find - real(qp) :: tret(1) ! time in data window - real(qp) :: tretPrev ! previous time in data window - integer(i4b),allocatable :: rootsfound(:) ! crossing direction of discontinuities - integer(i4b),allocatable :: rootdir(:) ! forced crossing direction of discontinuities - logical(lgt) :: tinystep ! if step goes below small size - type(var_dlength) :: flux_prev ! previous model fluxes for a local HRU - character(LEN=256) :: cmessage ! error message of downwind routine - real(rkind),allocatable :: mLayerMatricHeadPrimePrev(:) ! previous derivative value for total water matric potential (m s-1) - real(rkind),allocatable :: dCompress_dPsiPrev(:) ! previous derivative value soil compression - ! enthalpy derivatives - real(rkind) :: dCanEnthalpy_dTk_unused ! will not be used, derivatives in canopy enthalpy w.r.t. temperature - real(rkind) :: dCanEnthalpy_dWat_unused ! will not be used, derivatives in canopy enthalpy w.r.t. water state - real(rkind),allocatable :: dEnthalpy_dTk_unused(:) ! will not be used, derivatives in layer enthalpy w.r.t. temperature - real(rkind),allocatable :: dEnthalpy_dWat_unused(:) ! will not be used, derivatives in layer enthalpy w.r.t. water state + type(N_Vector), pointer :: sunvec_y ! sundials solution vector + type(N_Vector), pointer :: sunvec_yp ! sundials derivative vector + type(N_Vector), pointer :: sunvec_av ! sundials tolerance vector + type(SUNMatrix), pointer :: sunmat_A ! sundials matrix + type(SUNLinearSolver), pointer :: sunlinsol_LS ! sundials linear solver + type(SUNNonLinearSolver), pointer :: sunnonlin_NLS ! sundials nonlinear solver + type(c_ptr) :: ida_mem ! IDA memory + type(c_ptr) :: sunctx ! SUNDIALS simulation context + type(data4ida), target :: eqns_data ! IDA type + integer(i4b) :: retval, retvalr ! return value + logical(lgt) :: feasible ! feasibility flag + real(qp) :: t0 ! starting time + real(qp) :: dt_last(1) ! last time step + real(qp) :: dt_diff ! difference from previous timestep + integer(c_long) :: mu, lu ! in banded matrix mode in SUNDIALS type + integer(c_long) :: nState ! total number of state variables in SUNDIALS type + integer(i4b) :: iVar, i ! indices + integer(i4b) :: nRoot ! total number of roots (events) to find + real(qp) :: tret(1) ! time in data window + real(qp) :: tretPrev ! previous time in data window + integer(i4b),allocatable :: rootsfound(:) ! crossing direction of discontinuities + integer(i4b),allocatable :: rootdir(:) ! forced crossing direction of discontinuities + logical(lgt) :: tinystep ! if step goes below small size + type(var_dlength) :: flux_prev ! previous model fluxes for a local HRU + character(LEN=256) :: cmessage ! error message of downwind routine + real(rkind),allocatable :: mLayerMatricHeadPrimePrev(:) ! previous derivative value for total water matric potential (m s-1) + real(rkind) :: scalarCanairEnthalpyPrimePrev ! previous derivative value for canopy air enthalpy (J m-3) + real(rkind) :: scalarCanopyEnthalpyPrimePrev ! previous derivative value for canopy enthalpy (J m-3) + real(rkind),allocatable :: mLayerEnthalpyPrimePrev(:) ! previous derivative value for total water enthalpy (J m-3) + real(rkind),allocatable :: fluxVecPrev(:) ! previous value for fluxes + real(rkind),allocatable :: resVecPrev(:) ! previous value for residuals + real(rkind),allocatable :: dCompress_dPsiPrev(:) ! previous derivative value soil compression + ! enthalpy derivatives, not used here since only required for Jacobian updates + real(rkind) :: scalarDeriv_unused1 + real(rkind) :: scalarDeriv_unused2 + real(rkind) :: scalarDeriv_unused3 + real(rkind) :: scalarDeriv_unused4 + real(rkind),allocatable :: mLayerDeriv_unused1(:) + real(rkind),allocatable :: mLayerDeriv_unused2(:) + real(rkind),allocatable :: mLayerDeriv_unused3(:) + real(rkind),allocatable :: mLayerDeriv_unused4(:) ! flags - logical(lgt) :: use_fdJac ! flag to use finite difference Jacobian, controlled by decision fDerivMeth - logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true - logical(lgt),parameter :: detect_events = .true. ! flag to do event detection and restarting, default true + logical(lgt) :: use_fdJac ! flag to use finite difference Jacobian, controlled by decision fDerivMeth + logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true + logical(lgt),parameter :: detect_events = .true. ! flag to do event detection and restarting, default true ! ----------------------------------------------------------------------------------------------------- ! link to the necessary variables associate(& @@ -328,46 +337,41 @@ subroutine summaSolve4ida( & else allocate(eqns_data%dBaseflow_dMatric(0,0),stat=err) end if - allocate( eqns_data%mLayerMatricHeadLiqTrial(nSoil) ) - allocate( eqns_data%mLayerMatricHeadTrial(nSoil) ) + allocate( eqns_data%mLayerTempPrev(nLayers) ) allocate( eqns_data%mLayerMatricHeadPrev(nSoil) ) - allocate( eqns_data%mLayerVolFracWatTrial(nLayers) ) - allocate( eqns_data%mLayerVolFracWatPrev(nLayers) ) allocate( eqns_data%mLayerTempTrial(nLayers) ) - allocate( eqns_data%mLayerTempPrev(nLayers) ) - allocate( eqns_data%mLayerVolFracIceTrial(nLayers) ) - allocate( eqns_data%mLayerVolFracIcePrev(nLayers) ) - allocate( eqns_data%mLayerVolFracLiqTrial(nLayers) ) - allocate( eqns_data%mLayerVolFracLiqPrev(nLayers) ) - allocate( eqns_data%mLayerEnthalpyTrial(nLayers) ) - allocate( eqns_data%mLayerEnthalpyPrev(nLayers) ) - allocate( eqns_data%mLayerTempPrime(nLayers) ) - allocate( eqns_data%mLayerMatricHeadPrime(nSoil) ) + allocate( eqns_data%mLayerMatricHeadTrial(nSoil) ) allocate( eqns_data%mLayerMatricHeadLiqPrime(nSoil) ) - allocate( eqns_data%mLayerVolFracWatPrime(nLayers) ) + allocate( eqns_data%mLayerVolFracWatTrial(nLayers) ) + allocate( eqns_data%mLayerTempPrime(nLayers) ) + allocate( eqns_data%mLayerMatricHeadPrime(nSoil) ) + allocate( eqns_data%mLayerVolFracWatPrime(nLayers) ) + allocate( eqns_data%mLayerVolFracIcePrime(nLayers) ) + allocate( eqns_data%mLayerEnthalpyPrime(nLayers) ) allocate( mLayerMatricHeadPrimePrev(nSoil) ) + allocate( mLayerEnthalpyPrimePrev(nLayers) ) allocate( dCompress_dPsiPrev(nSoil) ) allocate( eqns_data%fluxVec(nState) ) allocate( eqns_data%resVec(nState) ) allocate( eqns_data%resSink(nState) ) - allocate( dEnthalpy_dTk_unused(nLayers) ) - allocate( dEnthalpy_dWat_unused(nLayers) ) + allocate( fluxVecPrev(nState) ) + allocate( resVecPrev(nState) ) + allocate( mLayerDeriv_unused1(nLayers) ) + allocate( mLayerDeriv_unused2(nLayers) ) + allocate( mLayerDeriv_unused3(nLayers) ) + allocate( mLayerDeriv_unused4(nLayers) ) ! need the following values for the first substep - eqns_data%scalarCanairEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) - eqns_data%scalarCanopyIcePrev = prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) - eqns_data%scalarCanopyLiqPrev = prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) - eqns_data%scalarCanopyEnthalpyPrev = diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) - eqns_data%mLayerVolFracWatPrev(:) = prog_data%var(iLookPROG%mLayerVolFracWat)%dat(:) - eqns_data%mLayerVolFracIcePrev(:) = prog_data%var(iLookPROG%mLayerVolFracIce)%dat(:) - eqns_data%mLayerVolFracLiqPrev(:) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(:) - eqns_data%mLayerEnthalpyPrev(:) = diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(:) - eqns_data%scalarAquiferStoragePrev = prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) mLayerMatricHeadPrimePrev(:) = 0._rkind + scalarCanairEnthalpyPrimePrev = 0._rkind + scalarCanopyEnthalpyPrimePrev = 0._rkind + mLayerEnthalpyPrimePrev(:) = 0._rkind dCompress_dPsiPrev(:) = 0._rkind + fluxVecPrev(:) = 0._rkind + resVecPrev(:) = 0._rkind retval = FSUNContext_Create(c_null_ptr, sunctx) @@ -549,62 +553,71 @@ subroutine summaSolve4ida( & if(checkNrgBalance)then if( model_decisions(iLookDECISIONS%howHeatCap)%iDecision == closedForm)then ! did not compute enthalpy without phase already - call t2enthalpy(& + call t2enthalpyPrime(& ! input: data structures eqns_data%diag_data, & ! intent(in): model diagnostic variables for a local HRU eqns_data%mpar_data, & ! intent(in): parameter data structure eqns_data%indx_data, & ! intent(in): model indices eqns_data%lookup_data, & ! intent(in): lookup table data structure ! input: state variables for the vegetation canopy - eqns_data%scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) + eqns_data%scalarCanairTempPrime, & ! intent(in): prime value of canopy air temperature (K) eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - eqns_data%scalarCanopyLiqTrial + eqns_data%scalarCanopyIceTrial, & ! intent(in): trial value of canopy total water (kg m-2) + eqns_data%scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + eqns_data%scalarCanopyTempPrime, & ! intent(in): prime temperature of the vegetation canopy (K) + eqns_data%scalarCanopyWatPrime, & ! intent(in): prime total water of the vegetation canopy (kg m-2) ! input: variables for the snow-soil domain eqns_data%mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) eqns_data%mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) eqns_data%mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + eqns_data%mLayerTempPrime, & ! intent(in): prime temperature of each snow+soil layer (K) + eqns_data%mLayerVolFracWatPrime, & ! intent(in): prime volumetric total water content of each snow+soil layer (-) + eqns_data%mLayerMatricHeadPrime, & ! intent(in): prime total water matric potential of each snow+soil layer (m) ! input: pre-computed derivatives - deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + deriv_data%var(iLookDERIV%d2VolTot_dPsi02)%dat,& ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy - eqns_data%scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) - eqns_data%scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - eqns_data%mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) - dCanEnthalpy_dTk_unused, & ! intent(out): will not be used, derivatives in canopy enthalpy w.r.t. temperature - dCanEnthalpy_dWat_unused, & ! intent(out): will not be used, derivatives in canopy enthalpy w.r.t. water state - dEnthalpy_dTk_unused, & ! intent(out): will not be used, derivatives in layer enthalpy w.r.t. temperature - dEnthalpy_dWat_unused, & ! intent(out): will not be used, derivatives in layer enthalpy w.r.t. water state + eqns_data%scalarCanairEnthalpyPrime, & ! intent(out): prime enthalpy of the canopy air space (J m-3) + eqns_data%scalarCanopyEnthalpyPrime, & ! intent(out): prime enthalpy of the vegetation canopy (J m-3) + eqns_data%mLayerEnthalpyPrime, & ! intent(out): prime enthalpy of each snow+soil layer (J m-3) + scalarDeriv_unused1, & ! intent(out): will not be used, derivatives + scalarDeriv_unused2, & ! intent(out): will not be used, derivatives + mLayerDeriv_unused1, & ! intent(out): will not be used, derivatives + mLayerDeriv_unused2, & ! intent(out): will not be used, derivatives + scalarDeriv_unused3, & ! intent(out): will not be used, derivatives + scalarDeriv_unused4, & ! intent(out): will not be used, derivatives + mLayerDeriv_unused3, & ! intent(out): will not be used, derivatives + mLayerDeriv_unused4, & ! intent(out): will not be used, derivatives ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif endif - ! compute enthalpy at t_{n+1} + ! compute enthalpy prime with phase change call t2enthalpy_addphase(& ! input: data structures eqns_data%diag_data, & ! intent(in): model diagnostic variables for a local HRU - eqns_data%mpar_data, & ! intent(in): parameter data structure eqns_data%indx_data, & ! intent(in): model indices - eqns_data%lookup_data, & ! intent(in): lookup table data structure ! input: state variables for the vegetation canopy - eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - eqns_data%scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) + eqns_data%scalarCanopyIcePrime, & ! intent(in): prime value of canopy ice content (kg m-2 s-1) ! input: variables for the snow-soil domain - eqns_data%mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - eqns_data%mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - eqns_data%mLayerVolFracIceTrial, & ! intent(in): trial vector of ice volume fraction (-) + eqns_data%mLayerVolFracIcePrime, & ! intent(in): prime vector of ice volume fraction (s-1) ! input/output: enthalpy - eqns_data%scalarCanopyEnthalpyTrial, & ! intent(inout): enthalpy of the vegetation canopy (J m-3) - eqns_data%mLayerEnthalpyTrial, & ! intent(inout): enthalpy of each snow+soil layer (J m-3) + eqns_data%scalarCanopyEnthalpyPrime, & ! intent(inout): prime enthalpy of the vegetation canopy (J m-3 s-1) + eqns_data%mLayerEnthalpyPrime, & ! intent(inout): prime enthalpy of each snow+soil layer (J m-3 s-1) ! output: error control err,message) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! compute energy balance - if(ixCasNrg/=integerMissing) balance(ixCasNrg) = balance(ixCasNrg) + (eqns_data%scalarCanairEnthalpyTrial - eqns_data%scalarCanairEnthalpyPrev - eqns_data%fluxVec(ixCasNrg)*dt_diff)*dt_diff/dt - if(ixVegNrg/=integerMissing) balance(ixVegNrg) = balance(ixVegNrg) + (eqns_data%scalarCanopyEnthalpyTrial - eqns_data%scalarCanopyEnthalpyPrev - eqns_data%fluxVec(ixVegNrg)*dt_diff)*dt_diff/dt + ! compute energy balance mean + if(ixCasNrg/=integerMissing) balance(ixCasNrg) = balance(ixCasNrg) + ( eqns_data%scalarCanairEnthalpyPrime - eqns_data%fluxVec(ixCasNrg) & + + scalarCanairEnthalpyPrimePrev - fluxVecPrev(ixCasNrg) )*dt_diff/2._rkind/dt + !if(ixCasNrg/=integerMissing) balance(ixCasNrg) = balance(ixCasNrg) + ( eqns_data%resVec(ixCasNrg) + resVecPrev(ixCasNrg) )*dt_diff/2._rkind/dt ! should be equivalent to above, use for debugging + if(ixVegNrg/=integerMissing) balance(ixVegNrg) = balance(ixVegNrg) + ( eqns_data%scalarCanopyEnthalpyPrime - eqns_data%fluxVec(ixVegNrg) & + + scalarCanopyEnthalpyPrimePrev - fluxVecPrev(ixVegNrg) )*dt_diff/2._rkind/dt if(nSnowSoilNrg>0)then do i=1,nSnowSoilNrg - if(ixSnowSoilNrg(i)/=integerMissing) balance(ixSnowSoilNrg(i)) = balance(ixSnowSoilNrg(i)) + (eqns_data%mLayerEnthalpyTrial(i) - eqns_data%mLayerEnthalpyPrev(i) - eqns_data%fluxVec(ixSnowSoilNrg(i))*dt_diff)*dt_diff/dt + if(ixSnowSoilNrg(i)/=integerMissing) balance(ixSnowSoilNrg(i)) = balance(ixSnowSoilNrg(i)) + ( eqns_data%mLayerEnthalpyPrime(i) - eqns_data%fluxVec(ixSnowSoilNrg(i)) & + + mLayerEnthalpyPrimePrev(i) - fluxVecPrev(ixSnowSoilNrg(i)) )*dt_diff/2._rkind/dt enddo endif endif @@ -614,34 +627,30 @@ subroutine summaSolve4ida( & !------------------------ if(checkMassBalance)then - ! compute mass balance + ! compute mass balance mean ! resVec is the instanteous residual vector from the solver - if(ixVegHyd/=integerMissing) balance(ixVegHyd) = balance(ixVegHyd) + eqns_data%resVec(ixVegHyd)*dt_diff/dt + if(ixVegHyd/=integerMissing) balance(ixVegHyd) = balance(ixVegHyd) + ( eqns_data%resVec(ixVegHyd) + resVecPrev(ixVegHyd) )*dt_diff/2._rkind/dt if(nSnowSoilHyd>0)then do i=1,nSnowSoilHyd - if(ixSnowSoilHyd(i)/=integerMissing) balance(ixSnowSoilHyd(i)) = balance(ixSnowSoilHyd(i)) + eqns_data%resVec(ixSnowSoilHyd(i))*dt_diff/dt + if(ixSnowSoilHyd(i)/=integerMissing) balance(ixSnowSoilHyd(i)) = balance(ixSnowSoilHyd(i)) + ( eqns_data%resVec(ixSnowSoilHyd(i)) + resVecPrev(ixSnowSoilHyd(i)) )*dt_diff/2._rkind/dt enddo endif - if(ixAqWat/=integerMissing) balance(ixAqWat) = balance(ixAqWat) + eqns_data%resVec(ixAqWat)*dt_diff/dt + if(ixAqWat/=integerMissing) balance(ixAqWat) = balance(ixAqWat) + ( eqns_data%resVec(ixAqWat) + resVecPrev(ixAqWat) )*dt_diff/2._rkind/dt endif ! save required quantities for next step - eqns_data%scalarCanairEnthalpyPrev = eqns_data%scalarCanairEnthalpyTrial eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial - eqns_data%scalarCanopyIcePrev = eqns_data%scalarCanopyIceTrial - eqns_data%scalarCanopyLiqPrev = eqns_data%scalarCanopyLiqTrial - eqns_data%scalarCanopyEnthalpyPrev = eqns_data%scalarCanopyEnthalpyTrial eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) - eqns_data%mLayerVolFracWatPrev(:) = eqns_data%mLayerVolFracWatTrial(:) - eqns_data%mLayerVolFracIcePrev(:) = eqns_data%mLayerVolFracIceTrial(:) - eqns_data%mLayerVolFracLiqPrev(:) = eqns_data%mLayerVolFracLiqTrial(:) - eqns_data%mLayerEnthalpyPrev(:) = eqns_data%mLayerEnthalpyTrial(:) - eqns_data%scalarAquiferStoragePrev = eqns_data%scalarAquiferStorageTrial mLayerMatricHeadPrimePrev(:) = eqns_data%mLayerMatricHeadPrime(:) + scalarCanairEnthalpyPrimePrev = eqns_data%scalarCanairEnthalpyPrime + scalarCanopyEnthalpyPrimePrev = eqns_data%scalarCanopyEnthalpyPrime + mLayerEnthalpyPrimePrev(:) = eqns_data%mLayerEnthalpyPrime(:) dCompress_dPsiPrev(:) = eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) tretPrev = tret(1) flux_prev = eqns_data%flux_data + fluxVecPrev(:) = eqns_data%fluxVec(:) + resVecPrev(:) = eqns_data%resVec(:) ! Restart for where vegetation and layers cross freezing point if(detect_events)then @@ -685,27 +694,24 @@ subroutine summaSolve4ida( & deallocate( eqns_data%sMul ) deallocate( eqns_data%dMat ) deallocate( eqns_data%dBaseflow_dMatric ) - deallocate( eqns_data%mLayerMatricHeadLiqTrial ) - deallocate( eqns_data%mLayerMatricHeadTrial ) - deallocate( eqns_data%mLayerMatricHeadPrev ) - deallocate( eqns_data%mLayerVolFracWatTrial ) - deallocate( eqns_data%mLayerVolFracWatPrev ) - deallocate( eqns_data%mLayerVolFracIceTrial ) deallocate( eqns_data%mLayerTempPrev ) + deallocate( eqns_data%mLayerMatricHeadPrev ) deallocate( eqns_data%mLayerTempTrial ) - deallocate( eqns_data%mLayerVolFracIcePrev ) - deallocate( eqns_data%mLayerVolFracLiqPrev ) - deallocate( eqns_data%mLayerEnthalpyTrial ) - deallocate( eqns_data%mLayerEnthalpyPrev ) - deallocate( eqns_data%mLayerTempPrime ) + deallocate( eqns_data%mLayerMatricHeadTrial ) + deallocate( eqns_data%mLayerVolFracWatTrial ) + deallocate( eqns_data%mLayerTempPrime ) deallocate( eqns_data%mLayerMatricHeadPrime ) - deallocate( eqns_data%mLayerMatricHeadLiqPrime ) - deallocate( eqns_data%mLayerVolFracWatPrime ) - deallocate( dEnthalpy_dTk_unused ) - deallocate( dEnthalpy_dWat_unused ) - + deallocate( eqns_data%mLayerMatricHeadLiqPrime) + deallocate( eqns_data%mLayerVolFracWatPrime ) + deallocate( eqns_data%mLayerVolFracIcePrime ) + deallocate( eqns_data%mLayerEnthalpyPrime ) deallocate( mLayerMatricHeadPrimePrev ) + deallocate( mLayerEnthalpyPrimePrev ) deallocate( dCompress_dPsiPrev ) + deallocate( mLayerDeriv_unused1 ) + deallocate( mLayerDeriv_unused2 ) + deallocate( mLayerDeriv_unused3 ) + deallocate( mLayerDeriv_unused4 ) deallocate( eqns_data%fluxVec ) deallocate( eqns_data%resVec ) deallocate( eqns_data%resSink ) diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index f715d7cf3..823b2e07b 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -388,20 +388,6 @@ subroutine t2enthalpy(& case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return end select - ! Initialize - dEnthVeg_dTk = 0._rkind - dEnthSoil_dTk = 0._rkind - dEnthLiq_dTk = 0._rkind - dEnthIce_dTk = 0._rkind - dEnthAir_dTk = 0._rkind - dEnthWater_dTk = 0._rkind - dEnthVeg_dWat = 0._rkind - dEnthSoil_dWat = 0._rkind - dEnthLiq_dWat = 0._rkind - dEnthIce_dWat = 0._rkind - dEnthAir_dWat = 0._rkind - dEnthWater_dWat = 0._rkind - ! identify domain select case(ixDomainType) case(iname_cas) @@ -419,13 +405,17 @@ subroutine t2enthalpy(& diffT = scalarCanopyTempTrial - Tfreeze enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth ! enthalpy derivatives - dEnthVeg_dTk = specificHeatVeg * maxMassVegetation / canopyDepth + dEnthVeg_dTk = specificHeatVeg * maxMassVegetation / canopyDepth + dEnthVeg_dWat = 0._rkind + if(diffT>=0._rkind)then enthLiq = Cp_water * scalarCanopyWatTrial * diffT / canopyDepth enthIce = 0._rkind - ! derivatives - dEnthLiq_dTk = Cp_water * scalarCanopyWatTrial / canopyDepth + ! enthalpy derivatives + dEnthLiq_dTk = Cp_water * scalarCanopyWatTrial / canopyDepth ! Note, equals Cp_water*VolFracLiq dEnthLiq_dWat = Cp_water * diffT / canopyDepth + dEnthIce_dTk = 0._rkind + dEnthIce_dWat = 0._rkind else integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth @@ -433,9 +423,9 @@ subroutine t2enthalpy(& ! derivatives d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) ! enthalpy derivatives - dEnthLiq_dTk = Cp_water * scalarCanopyWatTrial * d_integral_dTk / canopyDepth - dEnthIce_dTk = Cp_ice * scalarCanopyWatTrial * ( 1._rkind - d_integral_dTk ) / canopyDepth + dEnthLiq_dTk = Cp_water * scalarCanopyWatTrial * d_integral_dTk / canopyDepth ! Note, equals Cp_water*VolFracLiq dEnthLiq_dWat = Cp_water * integral / canopyDepth + dEnthIce_dTk = Cp_ice * scalarCanopyWatTrial * ( 1._rkind - d_integral_dTk ) / canopyDepth ! Note, equals Cp_ice*VolFracIce dEnthIce_dWat = Cp_ice * ( diffT - integral ) / canopyDepth endif @@ -460,12 +450,12 @@ subroutine t2enthalpy(& ! derivatives d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) ! enthalpy derivatives - dEnthLiq_dTk = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * d_integral_dTk - dEnthIce_dTk = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( 1._rkind - d_integral_dTk ) - dEnthAir_dTk = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(1._rkind-d_integral_dTk) + d_integral_dTk ) ) + dEnthLiq_dTk = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * d_integral_dTk ! Note, equals Cp_water*VolFracLiq dEnthLiq_dWat = iden_water * Cp_water * integral + dEnthIce_dTk = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( 1._rkind - d_integral_dTk ) ! Note, equals Cp_ice*VolFracIce dEnthIce_dWat = iden_water * Cp_ice * ( diffT - integral ) - dEnthAir_dWat = -iden_air * Cp_air * ( (iden_water/iden_ice)*(diffT-integral) + integral ) + dEnthAir_dTk = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(1._rkind-d_integral_dTk) + d_integral_dTk ) ) + dEnthAir_dWat = -iden_air * Cp_air * ( (iden_water/iden_ice)*(diffT-integral) + integral ) mLayerEnthalpy(iLayer) = enthLiq + enthIce + enthAir dEnthalpy_dTk(iLayer) = dEnthLiq_dTk + dEnthIce_dTk + dEnthAir_dTk @@ -502,8 +492,8 @@ subroutine t2enthalpy(& if(mlayerTempTrial(iLayer)>=Tcrit)then enthWater = iden_water * Cp_water * volFracWat * diffT ! valid for temperatures below freezing also ! enthalpy derivatives - dEnthWater_dTk = iden_water * Cp_water * volFracWat - dEnthWater_dWat = iden_water * Cp_water * dVolTot_dPsi0(ixControlIndex) !dVolFracWat_dWat = dVolTot_dPsi0(ixControlIndex) + dEnthWater_dTk = iden_water * Cp_water * volFracWat + dEnthWater_dWat = iden_water * Cp_water * dVolTot_dPsi0(ixControlIndex) ! dVolFracWat_dWat = dVolTot_dPsi0(ixControlIndex) ! *** compute enthalpy of water for frozen conditions else @@ -519,30 +509,30 @@ subroutine t2enthalpy(& enthMix = enthTemp - enthTcrit ! enthalpy of the liquid+ice mix enthLiq = iden_water * Cp_water * volFracWat * (Tcrit - Tfreeze) enthWater = enthMix + enthLiq - ! derivatives dTcrit_dPsi0 = 0._rkind if (mLayerMatricHeadTrial(ixControlIndex)<0._rkind) dTcrit_dPsi0 = gravity * Tfreeze/LH_fus ! enthalpy derivatives - dEnthWater_dTk = dE + dEnthWater_dTk = dE - dEcrit dEnthWater_dWat = -dEcrit*dTcrit_dPsi0 + iden_water * Cp_water * dVolTot_dPsi0(ixControlIndex) * (Tcrit - Tfreeze) + iden_water * Cp_water * volFracWat*dTcrit_dPsi0 endif ! (if frozen conditions) ! *** compute the enthalpy of soil - enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*diffT + enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*diffT ! enthalpy derivatives - dEnthSoil_dTk = soil_dens_intr*Cp_soil*(1._rkind - theta_sat) + dEnthSoil_dTk = soil_dens_intr*Cp_soil*(1._rkind - theta_sat) + dEnthSoil_dWat = 0._rkind ! *** compute the enthalpy of air - enthAir = iden_air*Cp_air*(1._rkind - theta_sat - volFracWat)*diffT + enthAir = iden_air*Cp_air*(1._rkind - theta_sat - volFracWat)*diffT ! enthalpy derivatives - dEnthAir_dTk = iden_air*Cp_air*(1._rkind - theta_sat - volFracWat) + dEnthAir_dTk = iden_air*Cp_air*(1._rkind - theta_sat - volFracWat) dEnthAir_dWat = -iden_air*Cp_air*dVolTot_dPsi0(ixControlIndex)*diffT ! *** compute the total enthalpy (J m-3) - mLayerEnthalpy(iLayer) = enthSoil + enthWater + enthAir - dEnthalpy_dTk(iLayer) = dEnthSoil_dTk + dEnthWater_dTk + dEnthAir_dTk - dEnthalpy_dWat(iLayer) = dEnthSoil_dWat + dEnthWater_dWat + dEnthAir_dWat + mLayerEnthalpy(iLayer) = enthWater + enthSoil + enthAir + dEnthalpy_dTk(iLayer) = dEnthWater_dTk + dEnthSoil_dTk + dEnthAir_dTk + dEnthalpy_dWat(iLayer) = dEnthWater_dWat + dEnthSoil_dWat + dEnthAir_dWat end associate soilVars @@ -562,24 +552,19 @@ end subroutine t2enthalpy ! ************************************************************************************************************************ -! public subroutine t2enthalpy: compute enthalpy with phase change from temperature and total water content +! public subroutine t2enthalpy_addphase: compute enthalpy or enthalpy prime with phase change from ice content ! ************************************************************************************************************************ subroutine t2enthalpy_addphase(& ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): model indices ! input: state variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) + scalarCanopyIce, & ! intent(in): value of canopy ice content (kg m-2) or prime ice content (kg m-2 s-1) ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in) + mLayerVolFracIce, & ! intent(in) : vector of volumetric fraction of ice (-) or prime volumetric fraction of ice (s-1) ! input/output: enthalpy - scalarCanopyEnthalpy, & ! intent(inout): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy, & ! intent(inout): enthalpy of each snow+soil layer (J m-3) + scalarCanopyEnthalpy, & ! intent(inout): enthalpy of the vegetation canopy (J m-3) or enthalpy prime (J m-3 s-1) + mLayerEnthalpy, & ! intent(inout): enthalpy of each snow+soil layer (J m-3) or enthalpy prime (J m-3 s-1) ! output: error control err,message) ! intent(out): error control ! ------------------------------------------------------------------------------------------------------------------------- @@ -591,19 +576,14 @@ subroutine t2enthalpy_addphase(& ! ------------------------------------------------------------------------------------------------------------------------- ! input: data structures type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model indices - type(zLookup),intent(in) :: lookup_data ! lookup tables ! input: state variables for the vegetation canopy - real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + real(rkind),intent(in) :: scalarCanopyIce ! value of canopy ice content (kg m-2) or prime ice content (kg m-2 s-1) ! input: variables for the snow-soil domain - real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric fraction of Ice (-) + real(rkind),intent(in) :: mLayerVolFracIce (:) ! vector of volumetric fraction of ice (-) or prime volumetric fraction of ice (s-1) ! input output: enthalpy - real(rkind),intent(inout) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(inout) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) + real(rkind),intent(inout) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) or enthalpy prime (J m-3 s-1) + real(rkind),intent(inout) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) or enthalpy prime (J m-3 s-1) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -615,17 +595,6 @@ subroutine t2enthalpy_addphase(& integer(i4b) :: ixFullVector ! index within full state vector integer(i4b) :: ixDomainType ! name of a given model domain integer(i4b) :: ixControlIndex ! index within a given model domain - real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - real(rkind) :: psiLiq ! matric head of liquid water (m) - real(rkind) :: volFracWat ! volumetric fraction of total water, liquid+ice (-) - real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) - real(rkind) :: volFracIce ! volumetric fraction of ice (-) - real(rkind) :: diffT ! temperature difference from Tfreeze - real(rkind) :: integral ! integral of snow freezing curve - real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head - ! enthalpy - real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures generalVars: associate(& @@ -680,68 +649,16 @@ subroutine t2enthalpy_addphase(& vegVars: associate(& canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) & ! intent(in): [dp] canopy depth (m) ) - - if(diffT>=0._rkind)then - enthPhase = 0._rkind - else - enthPhase = LH_fus * scalarCanopyIceTrial / canopyDepth - endif - scalarCanopyEnthalpy = scalarCanopyEnthalpy - enthPhase + scalarCanopyEnthalpy = scalarCanopyEnthalpy - LH_fus * scalarCanopyIce / canopyDepth end associate vegVars case(iname_snow) - diffT = mLayerTempTrial(iLayer) - Tfreeze - if(diffT>=0._rkind)then - enthPhase = 0._rkind - else - enthPhase = iden_ice * LH_fus * mLayerVolFracIceTrial(iLayer) - endif - mLayerEnthalpy(iLayer) = mLayerEnthalpy(iLayer) - enthPhase + mLayerEnthalpy(iLayer) = mLayerEnthalpy(iLayer) - iden_ice * LH_fus * mLayerVolFracIce(iLayer) case(iname_soil) - ! make association to variables in the data structures... - soilVars: associate(& + mLayerEnthalpy(iLayer) = mLayerEnthalpy(iLayer) - iden_water * LH_fus * mLayerVolFracIce(iLayer) - ! associate model parameters - soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex) , & ! intrinsic soil density (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex) , & ! soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex) , & ! volumetric residual water content (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) - - ! associate values in the lookup table - Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) - E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function - - ) ! end associate statement - - ! diagnostic variables - vGn_m = 1._rkind - 1._rkind/vGn_n - Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) - volFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - diffT = mLayerTempTrial(iLayer) - Tfreeze - - ! *** compute enthalpy of water for unfrozen conditions - if(mlayerTempTrial(iLayer)>=Tcrit)then - enthPhase = 0._rkind - - ! *** compute enthalpy of water for frozen conditions - else - ! *** compute the enthalpy associated with phase change - psiLiq = diffT*LH_fus/(gravity*Tfreeze) - vFracLiq = volFracLiq(psiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - volFracIce = volFracWat - vFracLiq - enthPhase = iden_water*LH_fus*volFracIce - endif ! (if frozen conditions) - mLayerEnthalpy(iLayer) = mLayerEnthalpy(iLayer) - enthPhase - - end associate soilVars - - ! ----- - ! - checks... - ! ----------- case(iname_aquifer); cycle ! aquifer: do nothing case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return end select diff --git a/build/source/engine/t2enthalpyAddPrime.f90 b/build/source/engine/t2enthalpyAddPrime.f90 new file mode 100644 index 000000000..a56737db7 --- /dev/null +++ b/build/source/engine/t2enthalpyAddPrime.f90 @@ -0,0 +1,451 @@ +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . + +module t2enthalpyAddPrime_module + +! constants +USE multiconst, only: gravity, & ! gravitational acceleration (m s-1) + Tfreeze, & ! freezing point of water (K) + Cp_soil,Cp_water,Cp_ice,Cp_air,& ! specific heat of soil, water and ice (J kg-1 K-1) + iden_water,iden_ice,iden_air,& ! intrinsic density of water and ice (kg m-3) + LH_fus ! latent heat of fusion (J kg-1) + +! data types +USE nrtype +USE data_types,only:var_iLength ! var(:)%dat(:) +USE data_types,only:var_dLength ! var(:)%dat(:) +USE data_types,only:zLookup ! z(:)%var(:)%lookup(:) + +! indices within parameter structure +USE var_lookup,only:iLookPARAM ! named variables to define structure element +USE var_lookup,only:iLookINDEX ! named variables to define structure element +USE var_lookup,only:iLookLOOKUP ! named variables to define structure element +USE var_lookup,only:iLookDIAG ! named variables for structure elements + +! data dimensions +USE var_lookup,only:maxvarLookup ! maximum number of variables in the lookup tables + +! domain types +USE globalData,only:iname_cas ! named variables for canopy air space +USE globalData,only:iname_veg ! named variables for vegetation canopy +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil +USE globalData,only:iname_aquifer ! named variables for the aquifer + +! named variables to describe the state variable type +USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space +USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy +USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers + +! missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing real number + +! privacy +implicit none +private +public::t2enthalpyPrime + +! define the look-up table used to compute temperature based on enthalpy +contains + + +! ************************************************************************************************************************ +! public subroutine t2enthalpyPrime: compute enthalpy prime from temperature and total water content +! ************************************************************************************************************************ +subroutine t2enthalpyPrime(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTempPrime, & ! intent(in): prime value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + scalarCanopyTempPrime, & ! intent(in): prime value of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(in): prime value of canopy total water (kg m-2) + ! input: variables for the snow-soil domain + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerTempPrime, & ! intent(in): prime vector of layer temperature (K) + mLayerVolFracWatPrime, & ! intent(in): prime vector of volumetric total water content (-) + mLayerMatricHeadPrime, & ! intent(in): prime vector of total water matric potential (m) + ! input: pre-computed derivatives + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + d2VolTot_dPsi02, & ! intent(in): second derivative in total water content w.r.t. total water matric potential (m-2) + ! output: enthalpy prime and derivatives + scalarCanairEnthalpyPrime, & ! intent(out): prime enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyPrime, & ! intent(out): prime enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyPrime, & ! intent(out): prime enthalpy of each snow+soil layer (J m-3) + dCanEnthalpyPrime_dTk, & ! intent(out): derivatives in prime canopy enthalpy w.r.t. temperature + dCanEnthalpyPrime_dWat, & ! intent(out): derivatives in prime canopy enthalpy w.r.t. water state + dEnthalpyPrime_dTk, & ! intent(out): derivatives in prime layer enthalpy w.r.t. temperature + dEnthalpyPrime_dWat, & ! intent(out): derivatives in prime layer enthalpy w.r.t. water state + dCanEnthalpyPrime_dTkPrime, & ! intent(out): derivatives in prime canopy enthalpy w.r.t. prime temperature + dCanEnthalpyPrime_dWatPrime, & ! intent(out): derivatives in prime canopy enthalpy w.r.t. prime water state + dEnthalpyPrime_dTkPrime, & ! intent(out): derivatives in prime layer enthalpy w.r.t. prime temperature + dEnthalpyPrime_dWatPrime, & ! intent(out): derivatives in prime layer enthalpy w.r.t. prime water state + ! output: error control + err,message) ! intent(out): error control + ! ------------------------------------------------------------------------------------------------------------------------- + ! downwind routines + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water + USE spline_int_module,only:splint ! use for cubic spline interpolation + implicit none + ! delare dummy variables + ! ------------------------------------------------------------------------------------------------------------------------- + ! input: data structures + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model indices + type(zLookup),intent(in) :: lookup_data ! lookup tables + ! input: state variables for the vegetation canopy + real(rkind),intent(in) :: scalarCanairTempPrime ! prime value of canopy air temperature (K) + real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + real(rkind),intent(in) :: scalarCanopyTempPrime ! prime value of canopy temperature (K) + real(rkind),intent(in) :: scalarCanopyWatPrime ! prime value of canopy total water (kg m-2) + ! input: variables for the snow-soil domain + real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind),intent(in) :: mLayerTempPrime(:) ! prime vector of layer temperature (K) + real(rkind),intent(in) :: mLayerVolFracWatPrime(:) ! prime vector of volumetric total water content (-) + real(rkind),intent(in) :: mLayerMatricHeadPrime(:) ! prime vector of total water matric potential (m) + ! input: pre-computed derivatives + real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) + real(rkind),intent(in) :: d2VolTot_dPsi02(:) ! second derivative in total water content w.r.t. total water matric potential (m-2) + ! output: enthalpy prime + real(rkind),intent(out) :: scalarCanairEnthalpyPrime ! prime enthalpy of the canopy air space (J m-3) + real(rkind),intent(out) :: scalarCanopyEnthalpyPrime ! prime enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! prime enthalpy of each snow+soil layer (J m-3) + ! output: derivatives + real(rkind),intent(out) :: dCanEnthalpyPrime_dTk ! derivatives in prime canopy enthalpy w.r.t. temperature + real(rkind),intent(out) :: dCanEnthalpyPrime_dWat ! derivatives in prime canopy enthalpy w.r.t. water state + real(rkind),intent(out) :: dEnthalpyPrime_dTk(:) ! derivatives in prime layer enthalpy w.r.t. temperature + real(rkind),intent(out) :: dEnthalpyPrime_dWat(:) ! derivatives in prime layer enthalpy w.r.t. water state + real(rkind),intent(out) :: dCanEnthalpyPrime_dTkPrime ! derivatives in prime canopy enthalpy w.r.t. priem temperature + real(rkind),intent(out) :: dCanEnthalpyPrime_dWatPrime ! derivatives in prime canopy enthalpy w.r.t. prime water state + real(rkind),intent(out) :: dEnthalpyPrime_dTkPrime(:) ! derivatives in prime layer enthalpy w.r.t. prime temperature + real(rkind),intent(out) :: dEnthalpyPrime_dWatPrime(:) ! derivatives in prime layer enthalpy w.r.t. prime water state + + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------------------------------- + ! declare local variables + character(len=128) :: cmessage ! error message in downwind routine + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixDomainType ! name of a given model domain + integer(i4b) :: ixControlIndex ! index within a given model domain + real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind) :: volFracWat ! volumetric fraction of total water, liquid+ice (-) + real(rkind) :: diffT ! temperature difference from Tfreeze + real(rkind) :: integral ! integral of snow freezing curve + real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head + real(rkind) :: d_integral_dTk ! derivative of integral with temperature + real(rkind) :: d2_integral_dTk2 ! second derivative of integral with temperature + real(rkind) :: dE ! derivative of enthalpy with temperature at layer temperature + real(rkind) :: dEcrit ! derivative of enthalpy with temperature at critical temperature + ! enthalpy + real(rkind) :: enthVegP ! prime enthalpy of the vegetation (J m-3) + real(rkind) :: enthSoilP ! prime enthalpy of soil particles (J m-3) + real(rkind) :: enthMixP ! prime enthalpy of the mixed region, liquid+ice (J m-3) + real(rkind) :: enthLiqP ! prime enthalpy of the liquid region (J m-3) + real(rkind) :: enthIceP ! prime enthalpy of the ice region (J m-3) + real(rkind) :: enthAirP ! prime enthalpy of air (J m-3) + real(rkind) :: enthTemp ! enthalpy at the temperature of the control volume (J m-3) + real(rkind) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) + real(rkind) :: enthPhaseP ! prime enthalpy associated with phase change (J m-3) + real(rkind) :: enthWaterP ! prime enthalpy of total water (J m-3) + ! enthalpy derivatives + real(rkind) :: dEnthVegP_dTk ! derivative of prime enthalpy of the vegetation with temperature + real(rkind) :: dEnthSoilP_dTk ! derivative of prime enthalpy of the soil with temperature + real(rkind) :: dEnthLiqP_dTk ! derivative of prime enthalpy of the liquid with temperature + real(rkind) :: dEnthIceP_dTk ! derivative of prime enthalpy of the ice with temperature + real(rkind) :: dEnthAirP_dTk ! derivative of prime enthalpy of the air with temperature + real(rkind) :: dEnthWaterP_dTk ! derivative of prime enthalpy of the total water with temperature + real(rkind) :: dEnthVegP_dWat ! derivative of prime enthalpy of the vegetation with water state + real(rkind) :: dEnthSoilP_dWat ! derivative of prime enthalpy of the soil with water state + real(rkind) :: dEnthLiqP_dWat ! derivative of prime enthalpy of the liquid with water state + real(rkind) :: dEnthIceP_dWat ! derivative of prime enthalpy of the ice with water state + real(rkind) :: dEnthAirP_dWat ! derivative of prime enthalpy of the air with water state + real(rkind) :: dEnthWaterP_dWat ! derivative of prime enthalpy of the total water with water state + real(rkind) :: dEnthVegP_dTkP ! derivative of prime enthalpy of the vegetation with prime temperature + real(rkind) :: dEnthSoilP_dTkP ! derivative of prime enthalpy of the soil with prime temperature + real(rkind) :: dEnthLiqP_dTkP ! derivative of prime enthalpy of the liquid with prime temperature + real(rkind) :: dEnthIceP_dTkP ! derivative of prime enthalpy of the ice with prime temperature + real(rkind) :: dEnthAirP_dTkP ! derivative of prime enthalpy of the air with prime temperature + real(rkind) :: dEnthWaterP_dTkP ! derivative of prime enthalpy of the total water with prime temperature + real(rkind) :: dEnthVegP_dWatP ! derivative of prime enthalpy of the vegetation with prime water state + real(rkind) :: dEnthSoilP_dWatP ! derivative of prime enthalpy of the soil with prime water state + real(rkind) :: dEnthLiqP_dWatP ! derivative of prime enthalpy of the liquid with prime water state + real(rkind) :: dEnthIceP_dWatP ! derivative of prime enthalpy of the ice with prime water state + real(rkind) :: dEnthAirP_dWatP ! derivative of prime enthalpy of the air with prime water state + real(rkind) :: dEnthWaterP_dWatP ! derivative of prime enthalpy of the total water with prime water state + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + generalVars: associate(& + ! number of model layers, and layer type + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + ! mapping between the full state vector and the state subset + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ! type of domain, type of state variable, and index of control volume within domain + ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat & ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ) ! end associate statement + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message="t2enthalpyPrime/" + + ! loop through model state variables + do iState=1,size(ixMapSubset2Full) + + ! ----- + ! - compute indices... + ! -------------------- + + ! get domain type, and index of the control volume within the domain + ixFullVector = ixMapSubset2Full(iState) ! index within full state vector + ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) + ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain + + ! check an energy state + if(ixStateType(ixFullVector)==iname_nrgCanair .or. ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer)then + + ! get the layer index + select case(ixDomainType) + case(iname_cas); iLayer = integerMissing + case(iname_veg); iLayer = integerMissing + case(iname_snow); iLayer = ixControlIndex + case(iname_soil); iLayer = ixControlIndex + nSnow + case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + + ! identify domain + select case(ixDomainType) + case(iname_cas) + scalarCanairEnthalpyPrime = Cp_air * iden_air * scalarCanairTempPrime + + case(iname_veg) + ! association to necessary variables for vegetation + vegVars: associate(& + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): [dp] canopy depth (m) + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) + + diffT = scalarCanopyTempTrial - Tfreeze + enthVegP = specificHeatVeg * maxMassVegetation * scalarCanopyTempPrime / canopyDepth + ! enthalpy prime derivatives + dEnthVegP_dTk = specificHeatVeg * maxMassVegetation / canopyDepth + dEnthVegP_dWat = 0._rkind + dEnthVegP_dTkP = 0._rkind + dEnthVegP_dWatP = 0._rkind + + if(diffT>=0._rkind)then + enthLiqP = Cp_water * ( scalarCanopyWatPrime * diffT + scalarCanopyWatTrial * scalarCanopyTempPrime )/ canopyDepth + enthIceP = 0._rkind + ! enthalpy prime derivatives + dEnthLiqP_dTk = Cp_water * scalarCanopyWatPrime / canopyDepth + dEnthLiqP_dWat = Cp_water * scalarCanopyTempPrime / canopyDepth + dEnthLiqP_dTkP = Cp_water * scalarCanopyWatTrial / canopyDepth + dEnthLiqP_dWatP = Cp_water * diffT / canopyDepth + dEnthIceP_dTk = 0._rkind + dEnthIceP_dWat = 0._rkind + dEnthIceP_dTkP = 0._rkind + dEnthIceP_dWatP = 0._rkind + else + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) ! Note: VolFracLiq = d_integral_dTk*VolFracWat + enthLiqP = Cp_water * ( scalarCanopyWatPrime*integral + scalarCanopyWatTrial*scalarCanopyTempPrime*d_integral_dTk )/ canopyDepth + enthIceP = Cp_ice * ( scalarCanopyWatPrime*( diffT - integral ) + scalarCanopyWatTrial*scalarCanopyTempPrime*( 1._rkind - d_integral_dTk ) ) / canopyDepth + ! derivatives + d2_integral_dTk2 = -2._rkind*snowfrz_scale*diffT / (1._rkind + (snowfrz_scale * diffT)**2_i4b)**2_i4b + ! enthalpy prime derivatives + dEnthLiqP_dTk = Cp_water * ( scalarCanopyWatPrime*d_integral_dTk + scalarCanopyWatTrial*scalarCanopyTempPrime*d2_integral_dTk2 ) / canopyDepth + dEnthLiqP_dWat = Cp_water * scalarCanopyTempPrime * d_integral_dTk / canopyDepth + dEnthLiqP_dTkP = Cp_water * scalarCanopyWatTrial * d_integral_dTk / canopyDepth + dEnthLiqP_dWatP = Cp_water * integral / canopyDepth + dEnthIceP_dTk = Cp_ice * ( scalarCanopyWatPrime*( 1._rkind - d_integral_dTk ) - scalarCanopyWatTrial*scalarCanopyTempPrime*d2_integral_dTk2 ) / canopyDepth + dEnthIceP_dWat = Cp_ice * ( scalarCanopyTempPrime*( 1._rkind - d_integral_dTk ) ) / canopyDepth + dEnthIceP_dTkP = Cp_ice * scalarCanopyWatTrial*( 1._rkind - d_integral_dTk ) / canopyDepth + dEnthIceP_dWatP = Cp_ice * ( diffT - integral ) / canopyDepth + endif + + scalarCanopyEnthalpyPrime = enthVegP + enthLiqP + enthIceP + dCanEnthalpyPrime_dTk = dEnthVegP_dTk + dEnthLiqP_dTk + dEnthIceP_dTk + dCanEnthalpyPrime_dWat = dEnthVegP_dWat + dEnthLiqP_dWat + dEnthIceP_dWat + dCanEnthalpyPrime_dTkPrime = dEnthVegP_dTkP + dEnthLiqP_dTkP + dEnthIceP_dTkP + dCanEnthalpyPrime_dWatPrime = dEnthVegP_dWatP + dEnthLiqP_dWatP + dEnthIceP_dWatP + + end associate vegVars + + case(iname_snow) + + ! association to necessary variables for snow + snowVars: associate(& + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) + + diffT = mLayerTempTrial(iLayer) - Tfreeze ! diffT<0._rkind because snow is frozen + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) ! Note: VolFracLiq = d_integral_dTk*VolFracWat + enthLiqP = iden_water * Cp_water * ( mLayerVolFracWatPrime(iLayer)*integral + mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer)*d_integral_dTk ) + enthIceP = iden_water * Cp_ice * ( mLayerVolFracWatPrime(iLayer)*( diffT - integral ) + mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer)*( 1._rkind - d_integral_dTk ) ) + enthAirP = iden_air * Cp_air * ( mLayerTempPrime(iLayer) - mLayerVolFracWatPrime(iLayer) * ( (iden_water/iden_ice)*( diffT - integral ) + integral ) & + - mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer)* ( (iden_water/iden_ice)*( 1._rkind - d_integral_dTk ) + d_integral_dTk ) ) + ! derivatives + d2_integral_dTk2 = -2._rkind*snowfrz_scale*diffT / (1._rkind + (snowfrz_scale * diffT)**2_i4b)**2_i4b + ! enthalpy prime derivatives + dEnthLiqP_dTk = iden_water * Cp_water * ( mLayerVolFracWatPrime(iLayer)*d_integral_dTk + mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer)*d2_integral_dTk2 ) + dEnthLiqP_dWat = iden_water * Cp_water * mLayerTempPrime(iLayer) * d_integral_dTk + dEnthLiqP_dTkP = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * d_integral_dTk + dEnthLiqP_dWatP = iden_water * Cp_water * integral + dEnthIceP_dTk = iden_water * Cp_ice * ( mLayerVolFracWatPrime(iLayer)*( 1._rkind - d_integral_dTk ) - mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer)*d2_integral_dTk2 ) + dEnthIceP_dWat = iden_water * Cp_ice * ( mLayerTempPrime(iLayer)*( 1._rkind - d_integral_dTk ) ) + dEnthIceP_dTkP = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer)*( 1._rkind - d_integral_dTk ) + dEnthIceP_dWatP = iden_water * Cp_ice * ( diffT - integral ) + dEnthAirP_dTk = iden_air * Cp_air * ( -mLayerVolFracWatPrime(iLayer) * ( (iden_water/iden_ice)*( 1._rkind - d_integral_dTk ) + d_integral_dTk ) & + - mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer) * (-(iden_water/iden_ice)*d2_integral_dTk2 + d2_integral_dTk2 ) ) + dEnthAirP_dWat = -iden_air * Cp_air * ( mLayerTempPrime(iLayer) * (iden_water/iden_ice)*( 1._rkind - d_integral_dTk ) + d_integral_dTk ) + dEnthAirP_dTkP = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer)* ( (iden_water/iden_ice)*( 1._rkind - d_integral_dTk ) + d_integral_dTk ) ) + dEnthAirP_dWatP =- iden_air * Cp_air * ( (iden_water/iden_ice)*( diffT - integral ) + integral ) + + mLayerEnthalpyPrime(iLayer) = enthLiqP + enthIceP + enthAirP + dEnthalpyPrime_dTk(iLayer) = dEnthLiqP_dTk + dEnthIceP_dTk + dEnthAirP_dTk + dEnthalpyPrime_dWat(iLayer) = dEnthLiqP_dWat + dEnthIceP_dWat + dEnthAirP_dWat + dEnthalpyPrime_dTkPrime(iLayer) = dEnthLiqP_dTkP + dEnthIceP_dTkP + dEnthAirP_dTkP + dEnthalpyPrime_dWatPrime(iLayer) = dEnthLiqP_dWatP + dEnthIceP_dWatP + dEnthAirP_dWatP + + end associate snowVars + + case(iname_soil) + + ! make association to variables in the data structures... + soilVars: associate(& + + ! associate model parameters + soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex) , & ! intrinsic soil density (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex) , & ! soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex) , & ! volumetric residual water content (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) + + ! associate values in the lookup table + Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) + Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) + E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + + ) ! end associate statement + + ! diagnostic variables + vGn_m = 1._rkind - 1._rkind/vGn_n + Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) + volFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + diffT = mLayerTempTrial(iLayer) - Tfreeze + dTcrit_dPsi0 = 0._rkind + if (mLayerMatricHeadTrial(ixControlIndex)<0._rkind) dTcrit_dPsi0 = gravity * Tfreeze/LH_fus + + ! *** compute enthalpy prime of water for unfrozen conditions + if(mlayerTempTrial(iLayer)>=Tcrit)then + enthWaterP = iden_water * Cp_water * ( mLayerMatricHeadPrime(ixControlIndex)*dVolTot_dPsi0(ixControlIndex)*diffT + volFracWat*mLayerTempPrime(iLayer) ) ! valid for temperatures below freezing also + ! enthalpy derivatives + dEnthWaterP_dTk = iden_water * Cp_water * mLayerMatricHeadPrime(ixControlIndex) * dVolTot_dPsi0(ixControlIndex) ! dVolFracWat_dWat = dVolTot_dPsi0(ixControlIndex) + dEnthWaterP_dWat = iden_water * Cp_water * ( mLayerMatricHeadPrime(ixControlIndex)*d2VolTot_dPsi02(ixControlIndex)*diffT & + + dVolTot_dPsi0(ixControlIndex)*mLayerTempPrime(iLayer) ) + dEnthWaterP_dTkP = iden_water * Cp_water * volFracWat + dEnthWaterP_dWatP = iden_water * Cp_water * dVolTot_dPsi0(ixControlIndex)*diffT + + ! *** compute enthalpy prime of water for frozen conditions + else + ! calculate enthalpy prime at the temperature (cubic spline interpolation) + call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),enthTemp,dE,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! calculate enthalpy prime at the critical temperature (cubic spline interpolation) + call splint(Tk,Ey,E2,Tcrit,enthTcrit,dEcrit,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! calculate the enthalpy prime of water + enthMixP = mlayerTempPrime(iLayer)*dE - mLayerMatricHeadPrime(ixControlIndex)*dEcrit ! enthalpy prime of the liquid+ice mix + enthLiqP = iden_water * Cp_water * mLayerMatricHeadPrime(ixControlIndex) * ( dVolTot_dPsi0(ixControlIndex) * (Tcrit - Tfreeze) + volFracWat * dTcrit_dPsi0 ) + enthWaterP = enthMixP + enthLiqP + ! enthalpy derivatives + dEnthWaterP_dTk = 0._rkind ! mlayerTempPrime(iLayer)*dE_dTk - mLayerMatricHeadPrime(ixControlIndex)*dEcrit_dTk, but derivatives of dE and dEcrit are zero + dEnthWaterP_dWat = 0._rkind ! -mLayerMatricHeadPrime(ixControlIndex)*dEcrit_dTk*dTcrit_dPsi0, but derivatives of dEcrit are zero + dEnthWaterP_dTkP = dE + dEnthWaterP_dWatP = -dEcrit + endif ! (if frozen conditions) + + ! *** compute the enthalpy of soil + enthSoilP = soil_dens_intr * Cp_soil * (1._rkind - theta_sat)*mlayerTempPrime(iLayer) + ! enthalpy derivatives + dEnthSoilP_dTk = 0._rkind + dEnthSoilP_dWat = 0._rkind + dEnthSoilP_dTkP = soil_dens_intr * Cp_soil * (1._rkind - theta_sat) + dEnthSoilP_dWatP = 0._rkind + + ! *** compute the enthalpy of air + enthAirP = iden_air * Cp_air * ( mLayerMatricHeadPrime(ixControlIndex)*dVolTot_dPsi0(ixControlIndex)*diffT + (1._rkind - theta_sat - volFracWat)*mlayerTempPrime(iLayer) ) + ! enthalpy derivatives + dEnthAirP_dTk = iden_air * Cp_air * mLayerMatricHeadPrime(ixControlIndex) * dVolTot_dPsi0(ixControlIndex) + dEnthAirP_dWat = iden_air * Cp_air * ( mLayerMatricHeadPrime(ixControlIndex)*d2VolTot_dPsi02(ixControlIndex)*diffT - dVolTot_dPsi0(ixControlIndex)*mlayerTempPrime(iLayer) ) + dEnthAirP_dTkP = iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) + dEnthAirP_dWatP = iden_air * Cp_air * dVolTot_dPsi0(ixControlIndex) * diffT + + ! *** compute the total enthalpy (J m-3) + mLayerEnthalpyPrime(iLayer) = enthWaterP + enthSoilP + enthAirP + dEnthalpyPrime_dTk(iLayer) = dEnthWaterP_dTk + dEnthSoilP_dTk + dEnthAirP_dTk + dEnthalpyPrime_dWat(iLayer) = dEnthWaterP_dWat + dEnthSoilP_dWat + dEnthAirP_dWat + dEnthalpyPrime_dTkPrime(iLayer) = dEnthWaterP_dTkP + dEnthSoilP_dTkP + dEnthAirP_dTkP + dEnthalpyPrime_dWatPrime(iLayer) = dEnthWaterP_dWatP + dEnthSoilP_dWatP + dEnthAirP_dWatP + + end associate soilVars + + ! ----- + ! - checks... + ! ----------- + case(iname_aquifer); cycle ! aquifer: do nothing + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + + end if ! if an energy layer + end do ! looping through state variables + + end associate generalVars + +end subroutine t2enthalpyPrime + +end module t2enthalpyAddPrime_module diff --git a/build/source/engine/updateVarsWithPrime.f90 b/build/source/engine/updateVarsWithPrime.f90 index b10443c8a..9ad3aa20c 100644 --- a/build/source/engine/updateVarsWithPrime.f90 +++ b/build/source/engine/updateVarsWithPrime.f90 @@ -261,7 +261,7 @@ subroutine updateVarsWithPrime(& dFracLiqSnow_dTk => deriv_data%var(iLookDERIV%dFracLiqSnow_dTk )%dat ,& ! intent(out): [dp(:)] derivative in fraction of liquid snow w.r.t. temperature dFracLiqVeg_dTkCanopy => deriv_data%var(iLookDERIV%dFracLiqVeg_dTkCanopy )%dat(1) ,& ! intent(out): [dp ] derivative in fraction of (throughfall + drainage) w.r.t. temperature ! derivatives inside solver for Jacobian only - d2VolTot_d2Psi0 => deriv_data%var(iLookDERIV%d2VolTot_d2Psi0 )%dat ,& ! intent(out): [dp(:)] second derivative in total water content w.r.t. total water matric potential + d2VolTot_dPsi02 => deriv_data%var(iLookDERIV%d2VolTot_dPsi02 )%dat ,& ! intent(out): [dp(:)] second derivative in total water content w.r.t. total water matric potential mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2 )%dat ,& ! intent(out): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature d2Theta_dTkCanopy2 => deriv_data%var(iLookDERIV%d2Theta_dTkCanopy2 )%dat(1) & ! intent(out): [dp ] second derivative of volumetric liquid water content w.r.t. temperature ) ! association with variables in the data structures @@ -420,10 +420,10 @@ subroutine updateVarsWithPrime(& select case( ixStateType(ixFullVector) ) case(iname_lmpLayer) dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore - if(computJac) d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + if(computJac) d2VolTot_dPsi02(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore case default dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - if(computJac) d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),& + if(computJac) d2VolTot_dPsi02(ixControlIndex) = d2Theta_dPsi2(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),& vGn_n(ixControlIndex),vGn_m(ixControlIndex)) end select endif diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 1a986ec7d..490c2263a 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -690,9 +690,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) real(rkind) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3) real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) - real(rkind) :: scalarCanopyEnthalpy_nophase ! trial value for enthalpy of the vegetation canopy (J m-3), no phase change real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector for enthalpy of snow + soil (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthalpy_nophase ! trial vector for enthalpy of snow + soil (J m-3), no phase change ! diagnostic variables real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) @@ -956,7 +954,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! ---- ! * check energy balance !------------------------ - if(checkNrgBalance .or. use_enthalpyFD)then ! update diagnostic enthalpy variables + if(checkNrgBalance .or. use_enthalpyFD)then ! update diagnostic enthalpy variables, don't do if not checking energy balance and closed form enthalpy ! compute enthalpy at t_{n+1} call t2enthalpy(& ! input: data structures @@ -985,24 +983,15 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! update no phase values for enthalpy - mLayerEnthalpy_nophase = mLayerEnthalpyTrial - scalarCanopyEnthalpy_nophase = scalarCanopyEnthalpyTrial - endif - if(checkNrgBalance)then + ! compute enthalpy with phase change call t2enthalpy_addphase(& ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure ! input: state variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric ice water content (-) ! input/output: enthalpy scalarCanopyEnthalpyTrial, & ! intent(inout): enthalpy of the vegetation canopy (J m-3) @@ -1010,13 +999,16 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! output: error control err,message) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + endif + if(checkNrgBalance)then ! compute energy balance if didn't do inside solver substeps select case(ixNumericalMethod) case(ida); ! do nothing, already computed case(kinsol, numrec) ! compute energy balance, maybe should use to check for step reduction if(ixCasNrg/=integerMissing) balance(ixCasNrg) = scalarCanairEnthalpyTrial - scalarCanairEnthalpy - fluxVec(ixCasNrg)*dt + !if(ixCasNrg/=integerMissing) balance(ixCasNrg) = resVec(ixCasNrg) ! should be equivalent to above, use for debugging if(ixVegNrg/=integerMissing) balance(ixVegNrg) = scalarCanopyEnthalpyTrial - scalarCanopyEnthalpy - fluxVec(ixVegNrg)*dt if(nSnowSoilNrg>0)then do i=1,nSnowSoilNrg @@ -1029,8 +1021,8 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! save the trial values if(checkNrgBalance .or. use_enthalpyFD)then scalarCanairEnthalpy = scalarCanairEnthalpyTrial - mLayerEnthalpy_nophase = mLayerEnthalpy_nophase - scalarCanopyEnthalpy_nophase = scalarCanopyEnthalpy_nophase + mLayerEnthalpy = mLayerEnthalpyTrial + scalarCanopyEnthalpy = scalarCanopyEnthalpyTrial endif ! ----- From 40a0c439069b2d7b7eb3cdfa3940e6f13d089296 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 1 Dec 2023 22:37:34 +0900 Subject: [PATCH 1010/1472] Add Jacobian terms in for enthalpy prime, solution is giving NaN right now. --- build/source/engine/computJacobWithPrime.f90 | 26 +++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index 3e9f1fccf..38f7f4d4c 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -312,7 +312,7 @@ subroutine computJacobWithPrime(& ! compute terms in the Jacobian for vegetation (excluding fluxes) ! NOTE: energy for vegetation is computed *within* the iteration loop as it includes phase change if(ixVegNrg/=integerMissing)then - dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy ) * cj & + dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + dVolHtCapBulk_dTkCanPrime*scalarCanopyTempPrime + LH_fus*iden_water*dTheta_dTkCanopy ) * cj & + dVolHtCapBulk_dTkCanopy * scalarCanopyTempPrime & + dCm_dTkCanopy * scalarCanopyWatPrime / canopyDepth & + LH_fus*iden_water * scalarCanopyTempPrime * d2Theta_dTkCanopy2 & @@ -323,7 +323,7 @@ subroutine computJacobWithPrime(& ! NOTE: energy for snow+soil is computed *within* the iteration loop as it includes phase change do iLayer=1,nLayers if(ixSnowSoilNrg(iLayer)/=integerMissing)then - dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj & + dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + dVolHtCapBulk_dTkPrime(iLayer)*mLayerTempPrime(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj & + dVolHtCapBulk_dTk(iLayer) * mLayerTempPrime(iLayer) & + dCm_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) & + LH_fus*iden_water * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & @@ -376,7 +376,7 @@ subroutine computJacobWithPrime(& ! * cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth * cj & + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = ( (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth + dVolHtCapBulk_dCanWatPrime*scalarCanopyTempPrime ) * cj & + dVolHtCapBulk_dCanWat * scalarCanopyTempPrime + scalarCanopyCm/canopyDepth * cj & - (dt/canopyDepth) * dCanopyNetFlux_dCanWat & + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth @@ -490,7 +490,7 @@ subroutine computJacobWithPrime(& if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) ! - include derivatives of energy fluxes w.r.t water fluxes for current layer - aJac(ixOffDiag(nrgState,watState),watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & + aJac(ixOffDiag(nrgState,watState),watState) = ( (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water + dVolHtCapBulk_dThetaPrime(iLayer)*mLayerTempPrime(iLayer) ) * cj & + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) + mLayerCm(iLayer) * cj & + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) & + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) @@ -661,7 +661,8 @@ subroutine computJacobWithPrime(& endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) * cj & + aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) & + + ( dVolHtCapBulk_dPsi0Prime(iLayer) * mLayerTempPrime(jLayer) + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) ) * cj & + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + mLayerCm(jLayer) * d2VolTot_dPsi02(iLayer) * mLayerMatricHeadPrime(iLayer) if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(ixOffDiag(nrgState,watState),watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) * cj & @@ -730,7 +731,7 @@ subroutine computJacobWithPrime(& ! * cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth * cj & + if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = ( (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth + dVolHtCapBulk_dCanWatPrime * scalarCanopyTempPrime ) * cj & + dVolHtCapBulk_dCanWat * scalarCanopyTempPrime + scalarCanopyCm/canopyDepth * cj& - (dt/canopyDepth) * dCanopyNetFlux_dCanWat & + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth @@ -844,11 +845,10 @@ subroutine computJacobWithPrime(& if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) ! - include derivatives of energy fluxes w.r.t water fluxes for current layer - aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & - + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) + mLayerCm(iLayer) * cj & - + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) & - + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) - + aJac(nrgState,watState) = ( (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water + dVolHtCapBulk_dThetaPrime(iLayer)*mLayerTempPrime(iLayer) ) * cj & + + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) + mLayerCm(iLayer) * cj & + + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) & + + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) ! - include derivatives of water fluxes w.r.t energy fluxes for current layer aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) @@ -994,8 +994,10 @@ subroutine computJacobWithPrime(& endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) * cj & + aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) & + + ( dVolHtCapBulk_dPsi0Prime(iLayer) * mLayerTempPrime(jLayer) + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) ) * cj & + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + mLayerCm(jLayer) * d2VolTot_dPsi02(iLayer) * mLayerMatricHeadPrime(iLayer) + if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(nrgState,watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) * cj & - LH_fus*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_dPsi02(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content From 80850743a46bbc542b909692fef13793249ddb7e Mon Sep 17 00:00:00 2001 From: ashleymedin <34691332+ashleymedin@users.noreply.github.com> Date: Fri, 1 Dec 2023 22:42:21 +0900 Subject: [PATCH 1011/1472] Merge pull request #30 from seantrim/develop_object_oriented --- build/source/engine/opSplittin.f90 | 554 +++++++++++++++-------------- 1 file changed, 292 insertions(+), 262 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index b5e5eeb9f..8a69e7b9c 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -292,265 +292,92 @@ subroutine opSplittin(& real(rkind) :: mean_step_state ! mean step over the state (with or without domain splits) real(rkind) :: mean_step_solution ! mean step for a solution (scalar or vector) logical(lgt) :: addFirstFlux ! flag to add the first flux to the mask + ! loop control + logical(lgt) :: exit_coupling,exit_stateTypeSplitting,exit_stateThenDomain,exit_domainSplit,exit_solution,exit_stateSplit + logical(lgt) :: cycle_coupling,cycle_stateTypeSplitting,cycle_stateThenDomain,cycle_domainSplit,cycle_solution,cycle_stateSplit ! ------------------------ classes for subroutine arguments (classes defined in data_types module) ------------------------ ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** type(in_type_stateFilter) :: in_stateFilter; type(out_type_stateFilter) :: out_stateFilter; ! stateFilter arguments type(in_type_indexSplit) :: in_indexSplit; type(out_type_indexSplit) :: out_indexSplit; ! indexSplit arguments type(in_type_varSubstep) :: in_varSubstep; type(io_type_varSubstep) :: io_varSubstep; type(out_type_varSubstep) :: out_varSubstep; ! varSubstep arguments - - ! --------------------------------------------------------------------------------------- - ! point to variables in the data structures ! --------------------------------------------------------------------------------------- - globalVars: associate(& - ! model decisions - ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver - ! vector of energy and hydrology indices for the snow and soil domains - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology state variables in the snow+soil domain - ! indices of model state variables - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset (missing for values not in the subset) - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) - ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain - ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain - ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain - ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ! numerix tracking - numberStateSplit => indx_data%var(iLookINDEX%numberStateSplit )%dat(1) ,& ! intent(inout): [i4b] number of state splitting solutions (-) - numberScalarSolutions => indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1) & ! intent(inout): [i4b] number of scalar solutions (-) - ) - ! --------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="opSplittin/" + call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs + coupling: do ixCoupling=1,nCoupling ! loop through different coupling strategies - ! we just solve the fully coupled problem if IDA for now, splitting can happen otherwise - select case(ixNumericalMethod) - case(ida); nCoupling = 1 - case(kinsol, numrec); nCoupling = 2 - end select + call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs + stateTypeSplitting: do iStateTypeSplit=1,nStateTypeSplit ! state splitting loop - ! *** initialize *** - call initialize_opSplittin - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if + ! first try the state type split, then try the domain split within a given state type + call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split + stateThenDomain: do ixStateThenDomain=1,1+tryDomainSplit ! 1=state type split; 2=domain split within a given state type - ! ========================================================================================================================================== - ! loop through different coupling strategies - coupling: do ixCoupling=1,nCoupling + call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs + domainSplit: do iDomainSplit=1,nDomainSplit ! domain splitting loop - ! initialize the time step - dtInit = min( merge(dt, dtmin_coupled, ixCoupling==fullyCoupled), dt) ! initial time step - dt_min = min( merge(dtmin_coupled, dtmin_split, ixCoupling==fullyCoupled), dt) ! minimum time step + solution: do ixSolution=1,nSolutions ! trial with the vector then scalar solution - ! keep track of the number of state splits - if(ixCoupling/=fullyCoupled) numberStateSplit = numberStateSplit + 1 + call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs + stateSplit: do iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) - ! define the number of operator splits for the state type - select case(ixCoupling) - case(fullyCoupled); nStateTypeSplit=1 - case(stateTypeSplit); nStateTypeSplit=nStateTypes - case default; err=20; message=trim(message)//'coupling case not found'; return - end select ! operator splitting option + ! define state subsets for a given split... + call update_stateFilter; if (return_flag.eqv..true.) return ! get the mask for the state subset - return for a non-zero error code + call validate_split ! verify that the split is valid + if (cycle_domainSplit) cycle domainSplit + if (cycle_solution) cycle solution + if (return_flag.eqv..true.) return ! return for a non-zero error code + call save_recover ! save/recover copies of variables and fluxes - ! define if we wish to try the domain split - select case(ixCoupling) - case(fullyCoupled); tryDomainSplit=0 - case(stateTypeSplit); tryDomainSplit=1 - case default; err=20; message=trim(message)//'coupling case not found'; return - end select ! operator splitting option + ! assemble vectors for a given split... + call get_split_indices; if (return_flag.eqv..true.) return ! get indices for a given split - return for a non-zero error code + call update_fluxMask; if (return_flag.eqv..true.) return ! define the mask of the fluxes used - return for a non-zero error code - mean_step_dt = 0._rkind ! initialize mean step for the time step - addFirstFlux = .true. ! flag to add the first flux to the mask + call solve_subset; if (return_flag.eqv..true.) return ! solve variable subset for one time step - return for a positive error code - ! state splitting loop - stateTypeSplit: do iStateTypeSplit=1,nStateTypeSplit + call assess_solution; if (return_flag.eqv..true.) return ! is solution a success or failure? - return for a recovering solution - ! ----- - ! * identify state-specific variables for a given state split... - ! -------------------------------------------------------------- + call try_other_solution_methods ! if solution failed to converge, try other splitting methods + if (cycle_coupling) cycle coupling ! exit loops if necessary + if (cycle_stateThenDomain) cycle stateThenDomain + if (cycle_solution) cycle solution - ! flag to adjust the temperature - doAdjustTemp = (ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit) + call confirm_variable_updates; if (return_flag.eqv..true.) return ! check that state variables updated - return if error - ! modify the state type names associated with the state vector - if(ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit)then - if(computeVegFlux)then - where(ixStateType(ixHydCanopy)==iname_watCanopy) ixStateType(ixHydCanopy)=iname_liqCanopy - endif - where(ixStateType(ixHydLayer) ==iname_watLayer) ixStateType(ixHydLayer) =iname_liqLayer - where(ixStateType(ixHydLayer) ==iname_matLayer) ixStateType(ixHydLayer) =iname_lmpLayer - endif ! if modifying state variables for the mass split + call success_check ! check for success + if (exit_stateThenDomain) exit stateThenDomain ! exit loops if necessary + if (exit_solution) exit solution + if (return_flag.eqv..true.) return ! return if error - ! first try the state type split, then try the domain split within a given state type - stateThenDomain: do ixStateThenDomain=1,1+tryDomainSplit ! 1=state type split; 2=domain split within a given state type + end do stateSplit ! solution with split layers - call initialize_domainSplit_loop - if (return_flag.eqv..true.) return ! return if error occurs during initialization + end do solution ! trial with the full layer solution then the split layer solution + call finalize_solution ! final steps following solution loop - ! domain splitting loop - domainSplit: do iDomainSplit=1,nDomainSplit + end do domainSplit ! domain type splitting loop - ! trial with the vector then scalar solution - solution: do ixSolution=1,nSolutions + end do stateThenDomain ! switch between the state type and domain type splitting + call finalize_stateThenDomain ! final steps following the stateThenDomain loop - call initialize_stateSplit_loop - if (return_flag.eqv..true.) return ! return if error occurs during initialization + end do stateTypeSplitting ! state type splitting loop + call finalize_stateTypeSplitting; if (exit_coupling) exit coupling ! success = exit the coupling loop - ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) - stateSplit: do iStateSplit=1,nStateSplit - - ! ----- - ! * define state subsets for a given split... - ! ------------------------------------------- - - ! get the mask for the state subset - call initialize_stateFilter - call stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) - call finalize_stateFilter - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! error control - - ! check that state variables exist - if (nSubset==0) cycle domainSplit - - ! avoid redundant case where vector solution is of length 1 - if (ixSolution==vector .and. count(stateMask)==1) cycle solution - - ! check that we do not attempt the scalar solution for the fully coupled case - if (ixCoupling==fullyCoupled .and. ixSolution==scalar) then - message=trim(message)//'only apply the scalar solution to the fully split coupling strategy' - err=20; return - end if - - ! reset the flag for the first flux call - if (.not.firstSuccess) firstFluxCall=.true. - - ! save/recover copies of variables and fluxes - call save_recover - - ! ----- - ! * assemble vectors for a given split... - ! --------------------------------------- - ! get indices for a given split - call initialize_indexSplit - call indexSplit(in_indexSplit,stateMask,indx_data,out_indexSplit) - call finalize_indexSplit - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! ----- - ! * define the mask of the fluxes used... - ! --------------------------------------- - call update_fluxMask - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! ******************************************************************************************************************************* - ! ***** trial with a given solution method... - - - ! ----- - ! * solve variable subset for one time step... - ! -------------------------------------------- - - ! keep track of the number of scalar solutions - if (ixSolution==scalar) numberScalarSolutions = numberScalarSolutions + 1 - - ! solve variable subset for one full time step - call initialize_varSubstep - call varSubstep(in_varSubstep,io_varSubstep,& ! intent(inout): class objects for model control - model_decisions,lookup_data,type_data,attr_data,forc_data,mpar_data,& ! intent(inout): data structures for model properties - indx_data,prog_data,diag_data,flux_data,flux_mean,deriv_data,bvar_data,& ! intent(inout): data structures for model variables - out_varSubstep) ! intent(out): class object for model control - call finalize_varSubstep - if (err/=0) then; message=trim(message)//trim(cmessage); if (err>0) return; end if ! error control - - ! determine whether solution is a success or a failure - call judge_solution - if (return_flag.eqv..true.) return ! return for a recovering solution - - ! try the fully split solution if failed to converge with a minimum time step in the coupled solution - if (ixCoupling==fullyCoupled .and. failure) cycle coupling - - ! try the scalar solution if failed to converge with a minimum time step in the split solution - if (ixCoupling/=fullyCoupled) then - select case(ixStateThenDomain) - case(fullDomain); if (failure) cycle stateThenDomain - case(subDomain); if (failure) cycle solution - case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' - end select - end if - - ! check that state variables updated - where(stateMask) stateCheck = stateCheck+1 - if (any(stateCheck>1)) then - message=trim(message)//'state variable updated more than once!' - err=20; return - end if - - ! success = exit solution - if (.not.failure) then - ! sum the mean steps for the successful solution type - mean_step_solution = mean_step_solution + (dt/nSubsteps)/nStateSplit - select case(ixStateThenDomain) - case(fullDomain); if (iStateSplit==nStateSplit) exit stateThenDomain - case(subDomain); if (iStateSplit==nStateSplit) exit solution - case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' - end select - else ! failure - call check_failure; return ! check reason for failure and return - end if ! success check - - end do stateSplit ! solution with split layers - - end do solution ! trial with the full layer solution then the split layer solution - - ! sum the mean steps for the state over each domain split - mean_step_state = mean_step_state + mean_step_solution/nDomainSplit - - ! ***** trial with a given solution method... - ! ******************************************************************************************************************************* - end do domainSplit ! domain type splitting loop - - end do stateThenDomain ! switch between the state and the domain - - ! sum the mean steps for the time step over each state type split - select case(ixStateThenDomain) - case(fullDomain); mean_step_dt = mean_step_dt + mean_step_solution/nStateTypeSplit - case(subDomain); mean_step_dt = mean_step_dt + mean_step_state/nStateTypeSplit - end select - - ! ----- - ! * reset state variables for the mass split... - ! --------------------------------------------- - ! modify the state type names associated with the state vector - if(ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit)then - if(computeVegFlux)then - where(ixStateType(ixHydCanopy)==iname_liqCanopy) ixStateType(ixHydCanopy)=iname_watCanopy - endif - where(ixStateType(ixHydLayer) ==iname_liqLayer) ixStateType(ixHydLayer) =iname_watLayer - where(ixStateType(ixHydLayer) ==iname_lmpLayer) ixStateType(ixHydLayer) =iname_matLayer - endif ! if modifying state variables for the mass split - - end do stateTypeSplit ! state type splitting loop - - ! success = exit the coupling loop - if(ixCoupling==fullyCoupled .and. .not.failure) exit coupling - - end do coupling ! coupling method - - ! *** Finalize *** - call finalize_opSplittin - - ! end associate statements - end associate globalVars + end do coupling ! loop over coupling methods + call finalize_coupling ! check variables and fluxes, and apply step halving if needed contains - subroutine initialize_opSplittin - ! *** initial steps for opSplittin subroutine *** + subroutine initialize_coupling + ! *** initial steps for coupling loop *** + ! initialize error control + err=0; message="opSplittin/" + + associate(ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision) ! intent(in): [i4b] choice of numerical solver + ! we just solve the fully coupled problem if IDA for now, splitting can happen otherwise + select case(ixNumericalMethod) + case(ida); nCoupling = 1 + case(kinsol, numrec); nCoupling = 2 + end select + end associate ! set the global print flag globalPrintFlag=.false. @@ -575,7 +402,8 @@ subroutine initialize_opSplittin stateCheck(:) = 0 ! allocate local structures based on the number of snow and soil layers - call allocate_memory + call allocate_memory + if (return_flag.eqv..true.) return ! return if an error occurs during memory allocation ! intialize the flux counter do iVar=1,size(flux_meta) ! loop through fluxes @@ -594,45 +422,47 @@ subroutine initialize_opSplittin do iVar=1,size(deriv_meta) deriv_data%var(iVar)%dat(:) = 0._rkind end do - end subroutine initialize_opSplittin + end subroutine initialize_coupling subroutine allocate_memory ! *** allocate memory for local structures *** + return_flag=.false. ! initialize flag + ! allocate space for the flux mask (used to define when fluxes are updated) call allocLocal(flux_meta(:),fluxMask,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! allocate space for the flux count (used to check that fluxes are only updated once) call allocLocal(flux_meta(:),fluxCount,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! allocate space for the temporary prognostic variable structure call allocLocal(prog_meta(:),prog_temp,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! allocate space for the temporary diagnostic variable structure call allocLocal(diag_meta(:),diag_temp,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! allocate space for the temporary flux variable structure call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! allocate space for the mean flux variable structure call allocLocal(flux_meta(:),flux_mean,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! allocate space for the temporary mean flux variable structure call allocLocal(flux_meta(:),flux_mntemp,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! allocate space for the derivative structure call allocLocal(deriv_meta(:),deriv_data,nSnow,nSoil,err,cmessage) - if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if end subroutine allocate_memory - subroutine finalize_opSplittin - ! *** final operations for opSplittin subroutine *** + subroutine finalize_coupling + ! *** final operations for coupling loop *** ! check that all state variables were updated if (any(stateCheck==0)) then message=trim(message)//'some state variables were not updated!' @@ -650,9 +480,88 @@ subroutine finalize_opSplittin ! use step halving if unable to complete the fully coupled solution in one substep if (ixCoupling/=fullyCoupled .or. nSubsteps>1) dtMultiplier=0.5_rkind - end subroutine finalize_opSplittin + end subroutine finalize_coupling + + subroutine initialize_stateTypeSplitting + ! *** Initial steps to prepare for iterations of the stateTypeSplit loop *** + return_flag=.false. ! initialize flag + ! initialize the time step + dtInit = min(merge(dt, dtmin_coupled, ixCoupling==fullyCoupled), dt) ! initial time step + dt_min = min(merge(dtmin_coupled, dtmin_split, ixCoupling==fullyCoupled), dt) ! minimum time step - subroutine initialize_domainSplit_loop + ! keep track of the number of state splits + associate(numberStateSplit => indx_data%var(iLookINDEX%numberStateSplit)%dat(1)) ! intent(inout): [i4b] number of state splitting solutions + if (ixCoupling/=fullyCoupled) numberStateSplit = numberStateSplit + 1 + end associate + + ! define the number of operator splits for the state type + select case(ixCoupling) + case(fullyCoupled); nStateTypeSplit=1 + case(stateTypeSplit); nStateTypeSplit=nStateTypes + case default; err=20; message=trim(message)//'coupling case not found'; return_flag=.true.; return + end select ! operator splitting option + + ! define if we wish to try the domain split + select case(ixCoupling) + case(fullyCoupled); tryDomainSplit=0 + case(stateTypeSplit); tryDomainSplit=1 + case default; err=20; message=trim(message)//'coupling case not found'; return_flag=.true.; return + end select ! operator splitting option + + mean_step_dt = 0._rkind ! initialize mean step for the time step + addFirstFlux = .true. ! flag to add the first flux to the mask + end subroutine initialize_stateTypeSplitting + + subroutine finalize_stateTypeSplitting + ! *** Final operations subsequent to the stateTypeSplitting loop *** + exit_coupling=.false. ! initialize flag for loop control + if (ixCoupling==fullyCoupled .and. .not.failure) then; exit_coupling=.true.; return; end if ! success = exit the coupling loop in opSplittin + end subroutine finalize_stateTypeSplitting + + subroutine initialize_stateThenDomain + ! *** Identify state-specific variables for a given state split *** + doAdjustTemp = (ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit) ! flag to adjust the temperature + associate(& + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat, & ! intent(in): [i4b(:)] indices defining the type of the state + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat, & ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + + ! modify the state type names associated with the state vector + if (ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit) then ! if modifying state variables for the mass split + if (computeVegFlux) then + where(ixStateType(ixHydCanopy)==iname_watCanopy) ixStateType(ixHydCanopy)=iname_liqCanopy + end if + where(ixStateType(ixHydLayer)==iname_watLayer) ixStateType(ixHydLayer)=iname_liqLayer + where(ixStateType(ixHydLayer)==iname_matLayer) ixStateType(ixHydLayer)=iname_lmpLayer + end if + end associate + end subroutine initialize_stateThenDomain + + subroutine finalize_stateThenDomain + ! *** Final steps following the stateThenDomain loop *** + ! sum the mean steps for the time step over each state type split + select case(ixStateThenDomain) + case(fullDomain); mean_step_dt = mean_step_dt + mean_step_solution/nStateTypeSplit + case(subDomain); mean_step_dt = mean_step_dt + mean_step_state/nStateTypeSplit + end select + associate(& + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat, & ! intent(in): [i4b(:)] indices defining the type of the state + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat, & ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + + ! * reset state variables for the mass split... + ! modify the state type names associated with the state vector + if (ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit) then ! if modifying state variables for the mass split + if (computeVegFlux) then + where(ixStateType(ixHydCanopy)==iname_liqCanopy) ixStateType(ixHydCanopy)=iname_watCanopy + end if + where(ixStateType(ixHydLayer)==iname_liqLayer) ixStateType(ixHydLayer)=iname_watLayer + where(ixStateType(ixHydLayer)==iname_lmpLayer) ixStateType(ixHydLayer)=iname_matLayer + end if + end associate + end subroutine finalize_stateThenDomain + + subroutine initialize_domainSplit ! *** initial operations to set up domainSplit loop *** return_flag=.false. ! initialize flag associate(numberDomainSplitNrg => indx_data%var(iLookINDEX%numberDomainSplitNrg )%dat(1),& ! intent(inout): [i4b] number of domain splitting solutions for energy (-) @@ -679,9 +588,15 @@ subroutine initialize_domainSplit_loop end if mean_step_state = 0._rkind ! initialize mean step for state - end subroutine initialize_domainSplit_loop + end subroutine initialize_domainSplit + + subroutine finalize_solution + ! *** final operations following solution loop *** + ! sum the mean steps for the state over each domain split + mean_step_state = mean_step_state + mean_step_solution/nDomainSplit + end subroutine finalize_solution - subroutine initialize_stateSplit_loop + subroutine initialize_stateSplit ! *** initial operations to set up stateSplit loop *** return_flag=.false. ! initialize flag mean_step_solution = 0._rkind ! initialize mean step for a solution @@ -691,8 +606,8 @@ subroutine initialize_stateSplit_loop ! refine the time step if (ixSolution==scalar) then - dtInit = min(dtmin_split, dt) ! initial time step - dt_min = min(dtmin_scalar, dt) ! minimum time step + dtInit = min(dtmin_split, dt) ! initial time step + dt_min = min(dtmin_scalar, dt) ! minimum time step end if ! initialize the first flux call @@ -701,13 +616,13 @@ subroutine initialize_stateSplit_loop ! get the number of split layers select case(ixSolution) - case(vector); nStateSplit=1 - case(scalar); nStateSplit=count(stateMask) - case default; err=20; message=trim(message)//'unknown solution method'; - return_flag=.true. ! return statement required in opSplittin - return + case(vector); nStateSplit=1 + case(scalar); nStateSplit=count(stateMask) + case default; err=20; message=trim(message)//'unknown solution method'; + return_flag=.true. ! return statement required in opSplittin + return end select - end subroutine initialize_stateSplit_loop + end subroutine initialize_stateSplit ! **** stateFilter **** subroutine initialize_stateFilter @@ -739,8 +654,30 @@ subroutine finalize_varSubstep call out_varSubstep % finalize(dtMultiplier,nSubsteps,failedMinimumStep,reduceCoupledStep,tooMuchMelt,err,cmessage) end subroutine finalize_varSubstep + subroutine solve_subset + ! *** Solve variable subset for one time step *** + return_flag=.false. ! initialize flag + ! keep track of the number of scalar solutions + associate(numberScalarSolutions => indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1)) ! intent(inout): [i4b] number of scalar solutions + if (ixSolution==scalar) numberScalarSolutions = numberScalarSolutions + 1 + end associate - subroutine judge_solution + ! solve variable subset for one full time step + call initialize_varSubstep + call varSubstep(in_varSubstep,io_varSubstep,& ! intent(inout): class objects for model control + model_decisions,lookup_data,type_data,attr_data,forc_data,mpar_data,& ! intent(inout): data structures for model properties + indx_data,prog_data,diag_data,flux_data,flux_mean,deriv_data,bvar_data,& + out_varSubstep) ! intent(out): class object for model control + call finalize_varSubstep + if (err/=0) then + message=trim(message)//trim(cmessage) + if (err>0) then ! return for positive error codes + return_flag=.true.; return + end if + end if ! error control + end subroutine solve_subset + + subroutine assess_solution ! *** determine whether solution is a success or a failure *** return_flag=.false. ! initialize flag @@ -770,7 +707,58 @@ subroutine judge_solution end do end do end if - end subroutine judge_solution + end subroutine assess_solution + + subroutine try_other_solution_methods + ! *** if solution failed to converge, try other splitting methods *** + ! initialize flags + cycle_coupling=.false. + cycle_stateThenDomain=.false. + cycle_solution=.false. + + ! try the fully split solution if failed to converge with a minimum time step in the coupled solution + if (ixCoupling==fullyCoupled .and. failure) then; cycle_coupling=.true.; return; end if! return required to execute cycle statement in opSplittin + + ! try the scalar solution if failed to converge with a minimum time step in the split solution + if (ixCoupling/=fullyCoupled) then + select case(ixStateThenDomain) + case(fullDomain); if (failure) cycle_stateThenDomain=.true.; return ! return required to execute cycle statement in opSplittin + case(subDomain); if (failure) cycle_solution=.true.; return + case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' + end select + end if + end subroutine try_other_solution_methods + + subroutine update_stateFilter + ! *** Get the mask for the state subset *** + return_flag=.false. ! initialize flag + call initialize_stateFilter + call stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) + call finalize_stateFilter + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! error control + end subroutine update_stateFilter + + subroutine validate_split + ! *** Verify that the split is valid *** + ! initialize flags + cycle_domainSplit=.false. + cycle_solution=.false. + return_flag=.false. + ! check that state variables exist + if (nSubset==0) then; cycle_domainSplit=.true.; return; end if + + ! avoid redundant case where vector solution is of length 1 + if (ixSolution==vector .and. count(stateMask)==1) then; cycle_solution=.true.; return; end if + + ! check that we do not attempt the scalar solution for the fully coupled case + if (ixCoupling==fullyCoupled .and. ixSolution==scalar) then + message=trim(message)//'only apply the scalar solution to the fully split coupling strategy' + err=20; return_flag=.true.; return + end if + + ! reset the flag for the first flux call + if (.not.firstSuccess) firstFluxCall=.true. + end subroutine validate_split subroutine save_recover ! save/recover copies of prognostic variables @@ -804,6 +792,45 @@ subroutine save_recover end do end subroutine save_recover + subroutine get_split_indices + ! *** Get indices for a given split *** + return_flag=.false. ! initialize flag + call initialize_indexSplit + call indexSplit(in_indexSplit,stateMask,indx_data,out_indexSplit) + call finalize_indexSplit + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if + end subroutine get_split_indices + + subroutine confirm_variable_updates + ! *** check that state variables updated *** + return_flag=.false. ! set flag + ! check that state variables updated + where(stateMask) stateCheck = stateCheck+1 + if (any(stateCheck>1)) then + message=trim(message)//'state variable updated more than once!' + err=20; return_flag=.true.; return + end if + end subroutine confirm_variable_updates + + subroutine success_check + ! initialize flags + return_flag=.false. + exit_stateThenDomain=.false. + exit_solution=.false. + ! success = exit solution + if (.not.failure) then + ! sum the mean steps for the successful solution type + mean_step_solution = mean_step_solution + (dt/nSubsteps)/nStateSplit + select case(ixStateThenDomain) + case(fullDomain); if (iStateSplit==nStateSplit) exit_stateThenDomain=.true. ! exit stateThenDomain + case(subDomain); if (iStateSplit==nStateSplit) exit_solution=.true. ! exit solution + case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' + end select + else ! failure + call check_failure; return_flag=.true.; return ! check reason for failure and return + end if ! success check + end subroutine success_check + subroutine check_failure ! *** Analyze reason for failure *** if (ixSolution==scalar) then ! check that we did not fail for the scalar solution (last resort) @@ -816,7 +843,9 @@ subroutine check_failure end subroutine check_failure subroutine update_fluxMask - ! *** update the fluxMask data structure *** + ! *** update the fluxMask data structure *** + return_flag=.false. ! initialize flag + do iVar=1,size(flux_meta) ! loop through flux variables if (ixCoupling==fullyCoupled) then ! * identify flux mask for the fully coupled solution @@ -841,7 +870,7 @@ subroutine update_fluxMask select case(iStateTypeSplit) ! identify the flux mask for a given state split case(nrgSplit); desiredFlux = any(ixStateType_subset==flux2state_orig(iVar)%state1) .or. any(ixStateType_subset==flux2state_orig(iVar)%state2) case(massSplit); desiredFlux = any(ixStateType_subset==flux2state_liq(iVar)%state1) .or. any(ixStateType_subset==flux2state_liq(iVar)%state2) - case default; err=20; message=trim(message)//'unable to identify split based on state type'; return + case default; err=20; message=trim(message)//'unable to identify split based on state type'; return_flag=.true.; return end select end associate @@ -865,7 +894,7 @@ subroutine update_fluxMask fluxMask%var(iVar)%dat(:1) = desiredFlux if (ixStateThenDomain>1 .and. iStateTypeSplit/=nrgSplit) then message=trim(message)//'only expect a vector solution for the vegetation domain for energy' - err=20; return + err=20; return_flag=.true.; return end if else ! scalar solution fluxMask%var(iVar)%dat(:1) = desiredFlux @@ -904,11 +933,11 @@ subroutine update_fluxMask end if ! if the layer is active end associate - end do ! looping through layers + end do ! end looping through layers case(aquiferSplit) ! fluxes through aquifer fluxMask%var(iVar)%dat(:) = desiredFlux ! only would be firstFluxCall variables, no aquifer fluxes - case default; err=20; message=trim(message)//'unable to identify split based on domain type'; return ! check + case default; err=20; message=trim(message)//'unable to identify split based on domain type'; return_flag=.true.; return ! check end select ! domain split end if ! end if flux is desired end if ! end if domain splitting @@ -923,6 +952,7 @@ subroutine update_fluxMask end do ! end looping through fluxes end subroutine update_fluxMask + end subroutine opSplittin From 97aace8ba66b24ea4fcdd13044ab581a65544f9d Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 2 Dec 2023 03:57:06 -0600 Subject: [PATCH 1012/1472] Created initialize_split_select subroutine in contains block of opSplittin -- not yet used and currently under development. --- build/source/engine/opSplittin.f90 | 71 ++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 8a69e7b9c..99dbdf826 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -366,6 +366,77 @@ subroutine opSplittin(& contains + subroutine initialize_split_select + ! *** Initialize split_select class object - currently under development *** + call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs + coupling: do ixCoupling=1,nCoupling ! loop through different coupling strategies + + call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs + stateTypeSplitting: do iStateTypeSplit=1,nStateTypeSplit ! state splitting loop + + ! first try the state type split, then try the domain split within a given state type + call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split + stateThenDomain: do ixStateThenDomain=1,1+tryDomainSplit ! 1=state type split; 2=domain split within a given state type + + call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs + domainSplit: do iDomainSplit=1,nDomainSplit ! domain splitting loop + + solution: do ixSolution=1,nSolutions ! trial with the vector then scalar solution + + call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs + stateSplit: do iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) + +!!! Draft -- assign data components to class object +! possible class object for handling split selection +!split_select(ixCoupling,iStateTypeSplit,ixStateThenDomain,iDomainSplit,ixSolution,iStateSplit) +!!! End Draft + +! ! define state subsets for a given split... +! call update_stateFilter; if (return_flag.eqv..true.) return ! get the mask for the state subset - return for a non-zero error code +! call validate_split ! verify that the split is valid +! if (cycle_domainSplit) cycle domainSplit +! if (cycle_solution) cycle solution +! if (return_flag.eqv..true.) return ! return for a non-zero error code +! call save_recover ! save/recover copies of variables and fluxes + +! ! assemble vectors for a given split... +! call get_split_indices; if (return_flag.eqv..true.) return ! get indices for a given split - return for a non-zero error code +! call update_fluxMask; if (return_flag.eqv..true.) return ! define the mask of the fluxes used - return for a non-zero error code + +! call solve_subset; if (return_flag.eqv..true.) return ! solve variable subset for one time step - return for a positive error code + +! call assess_solution; if (return_flag.eqv..true.) return ! is solution a success or failure? - return for a recovering solution + +! call try_other_solution_methods ! if solution failed to converge, try other splitting methods +! if (cycle_coupling) cycle coupling ! exit loops if necessary +! if (cycle_stateThenDomain) cycle stateThenDomain +! if (cycle_solution) cycle solution + +! call confirm_variable_updates; if (return_flag.eqv..true.) return ! check that state variables updated - return if error + +! call success_check ! check for success +! if (exit_stateThenDomain) exit stateThenDomain ! exit loops if necessary +! if (exit_solution) exit solution +! if (return_flag.eqv..true.) return ! return if error + + end do stateSplit ! solution with split layers + + end do solution ! trial with the full layer solution then the split layer solution + call finalize_solution ! final steps following solution loop + + end do domainSplit ! domain type splitting loop + + end do stateThenDomain ! switch between the state type and domain type splitting + call finalize_stateThenDomain ! final steps following the stateThenDomain loop + + end do stateTypeSplitting ! state type splitting loop + call finalize_stateTypeSplitting; if (exit_coupling) exit coupling ! success = exit the coupling loop + + end do coupling ! loop over coupling methods + call finalize_coupling ! check variables and fluxes, and apply step halving if needed + + end subroutine initialize_split_select + subroutine initialize_coupling ! *** initial steps for coupling loop *** ! initialize error control From 3c1394a7bec8eeb442ac2cd2a386866c6d481259 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 5 Dec 2023 17:57:13 +0900 Subject: [PATCH 1013/1472] take out lookup tables in put in gaussian hypergeometric. take out enthalpy derivatives, start trying enthalpy prime but going to delete that. --- build/cmake/CMakeLists.txt | 4 +- build/source/driver/summa_init.f90 | 2 - build/source/driver/summa_modelRun.f90 | 4 - build/source/driver/summa_setup.f90 | 20 -- build/source/driver/summa_type.f90 | 9 +- build/source/dshare/data_types.f90 | 21 -- build/source/dshare/get_ixname.f90 | 1 - build/source/dshare/globalData.f90 | 5 +- build/source/dshare/popMetadat.f90 | 15 +- build/source/dshare/type4ida.f90 | 1 - build/source/dshare/type4kinsol.f90 | 1 - build/source/dshare/var_lookup.f90 | 18 +- build/source/engine/allocspace.f90 | 4 +- build/source/engine/checkStruc.f90 | 6 +- build/source/engine/computHeatCap.f90 | 81 ++--- .../source/engine/computHeatCapWithPrime.f90 | 112 ++---- build/source/engine/computJacobWithPrime.f90 | 25 +- build/source/engine/convE2Temp.f90 | 231 ------------- build/source/engine/coupled_em.f90 | 6 +- build/source/engine/eval8summa.f90 | 18 - build/source/engine/eval8summaWithPrime.f90 | 59 +--- build/source/engine/opSplittin.f90 | 5 +- build/source/engine/run_oneGRU.f90 | 7 +- build/source/engine/run_oneHRU.f90 | 6 +- build/source/engine/spline_int.f90 | 161 --------- build/source/engine/summaSolve4ida.f90 | 31 -- build/source/engine/summaSolve4kinsol.f90 | 4 - build/source/engine/summaSolve4numrec.f90 | 4 - build/source/engine/systemSolv.f90 | 20 -- build/source/engine/t2enthalpy.f90 | 319 +++--------------- build/source/engine/t2enthalpyAddPrime.f90 | 199 +++-------- build/source/engine/varSubstep.f90 | 32 +- build/source/netcdf/def_output.f90 | 3 +- 33 files changed, 198 insertions(+), 1236 deletions(-) delete mode 100644 build/source/engine/convE2Temp.f90 delete mode 100644 build/source/engine/spline_int.f90 diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 1de6f43e0..f669210cd 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -274,8 +274,7 @@ set(NRUTIL # Free versions of numerical recipes procedures for SUMMA modules set(NRPROC - ${ENGINE_DIR}/expIntegral.f90 - ${ENGINE_DIR}/spline_int.f90) + ${ENGINE_DIR}/expIntegral.f90) # Hook-up modules set(HOOKUP @@ -324,7 +323,6 @@ set(PRELIM ${ENGINE_DIR}/checkStruc.f90 ${ENGINE_DIR}/childStruc.f90 ${ENGINE_DIR}/conv_funcs.f90 - ${ENGINE_DIR}/convE2Temp.f90 ${SUB_ENGINE_DIR}/ffile_info.f90 ${ENGINE_DIR}/read_pinit.f90 ${ENGINE_DIR}/read_attrb.f90 diff --git a/build/source/driver/summa_init.f90 b/build/source/driver/summa_init.f90 index 51252525f..3f961be4b 100644 --- a/build/source/driver/summa_init.f90 +++ b/build/source/driver/summa_init.f90 @@ -34,7 +34,6 @@ module summa_init USE globalData,only:mpar_meta,indx_meta ! metadata structures USE globalData,only:bpar_meta,bvar_meta ! metadata structures USE globalData,only:averageFlux_meta ! metadata for time-step average fluxes -USE globalData,only:lookup_meta ! statistics metadata structures USE globalData,only:statForc_meta ! child metadata for stats @@ -248,7 +247,6 @@ subroutine summa_initialize(summa1_struc, err, message) case('flux' ); call allocGlobal(flux_meta, fluxStruct, err, cmessage) ! model fluxes case('bpar' ); call allocGlobal(bpar_meta, bparStruct, err, cmessage) ! basin-average parameters case('bvar' ); call allocGlobal(bvar_meta, bvarStruct, err, cmessage) ! basin-average variables - case('lookup'); call allocGlobal(lookup_meta, lookupStruct, err, cmessage) ! basin-average variables case('deriv' ); cycle case default; err=20; message='unable to find structure name: '//trim(structInfo(iStruct)%structName) end select diff --git a/build/source/driver/summa_modelRun.f90 b/build/source/driver/summa_modelRun.f90 index dae3e4b1b..27f3ca5bd 100644 --- a/build/source/driver/summa_modelRun.f90 +++ b/build/source/driver/summa_modelRun.f90 @@ -226,9 +226,6 @@ subroutine summa_runPhysics(modelTimeStep, summa1_struc, err, message) bparStruct => summa1_struc%bparStruct , & ! x%gru(:)%var(:) -- basin-average parameters bvarStruct => summa1_struc%bvarStruct , & ! x%gru(:)%var(:)%dat -- basin-average variables - ! lookup table structure - lookupStruct => summa1_struc%lookupStruct , & ! x%gru(:)%hru(:)%z(:)%var(:)%lookup -- lookup-tables - ! run time variables greenVegFrac_monthly => summa1_struc%greenVegFrac_monthly, & ! fraction of green vegetation in each month (0-1) computeVegFlux => summa1_struc%computeVegFlux , & ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) @@ -260,7 +257,6 @@ subroutine summa_runPhysics(modelTimeStep, summa1_struc, err, message) typeStruct%gru(iGRU), & ! intent(in): local classification of soil veg etc. for each HRU idStruct%gru(iGRU), & ! intent(in): local classification of soil veg etc. for each HRU attrStruct%gru(iGRU), & ! intent(in): local attributes for each HRU - lookupStruct%gru(iGRU), & ! intent(in): local lookup tables for each HRU ! data structures (input-output) mparStruct%gru(iGRU), & ! intent(inout): local model parameters indxStruct%gru(iGRU), & ! intent(inout): model indices diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index 940f91b2f..ea5e25de7 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -33,7 +33,6 @@ module summa_setup USE var_lookup,only:iLookTYPE ! look-up values for classification of veg, soils etc. USE var_lookup,only:iLookPARAM ! look-up values for local column model parameters USE var_lookup,only:iLookINDEX ! look-up values for local column model indices -USE var_lookup,only:iLookLOOKUP ! look-up values for local column lookup tables USE var_lookup,only:iLookID ! look-up values for local column model ids USE var_lookup,only:iLookBVAR ! look-up values for basin-average model variables USE var_lookup,only:iLookDECISIONS ! look-up values for model decisions @@ -78,8 +77,6 @@ subroutine summa_paramSetup(summa1_struc, err, message) USE paramCheck_module,only:paramCheck ! module to check consistency of model parameters USE pOverwrite_module,only:pOverwrite ! module to overwrite default parameter values with info from the Noah tables USE read_param_module,only:read_param ! module to read model parameter sets - USE convE2Temp_module,only:E2T_lookup ! module to calculate a look-up table for the temperature-enthalpy conversion - USE t2enthalpy_module,only:T2E_lookup ! module to calculate a look-up table for the temperature-enthalpy conversion USE var_derive_module,only:fracFuture ! module to calculate the fraction of runoff in future time steps (time delay histogram) USE module_sf_noahmplsm,only:read_mp_veg_parameters ! module to read NOAH vegetation tables ! global data structures @@ -137,9 +134,6 @@ subroutine summa_paramSetup(summa1_struc, err, message) bparStruct => summa1_struc%bparStruct , & ! x%gru(:)%var(:) -- basin-average parameters bvarStruct => summa1_struc%bvarStruct , & ! x%gru(:)%var(:)%dat -- basin-average variables - ! lookup table structure - lookupStruct => summa1_struc%lookupStruct , & ! x%gru(:)%hru(:)%z(:)%var(:)%lookup -- lookup-tables - ! miscellaneous variables upArea => summa1_struc%upArea , & ! area upslope of each HRU nGRU => summa1_struc%nGRU , & ! number of grouped response units @@ -300,20 +294,6 @@ subroutine summa_paramSetup(summa1_struc, err, message) call paramCheck(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! calculate a look-up table for the temperature-enthalpy conversion: snow - ! NOTE1: this should eventually be replaced by the more general routine below - ! NOTE2: this does not actually need to be called for each HRU and GRU - call E2T_lookup(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! calculate a lookup table to compute enthalpy from temperature, need for enthalpyFD or if checkNrgBalance is true - !if(model_decisions(iLookDECISIONS%howHeatCap)%iDecision == enthalpyFD)then - call T2E_lookup(gru_struc(iGRU)%hruInfo(iHRU)%nSoil, & ! intent(in): number of soil layers - mparStruct%gru(iGRU)%hru(iHRU), & ! intent(in): parameter data structure - lookupStruct%gru(iGRU)%hru(iHRU), & ! intent(inout): lookup table data structure - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - !endif ! overwrite the vegetation height HVT(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex)) = mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%heightCanopyTop)%dat(1) diff --git a/build/source/driver/summa_type.f90 b/build/source/driver/summa_type.f90 index 8ea489820..a1053f6a2 100644 --- a/build/source/driver/summa_type.f90 +++ b/build/source/driver/summa_type.f90 @@ -45,19 +45,14 @@ MODULE summa_type gru_hru_int8, & ! x%gru(:)%hru(:)%var(:) (i8b) gru_hru_double, & ! x%gru(:)%hru(:)%var(:) (dp) gru_hru_intVec, & ! x%gru(:)%hru(:)%var(:)%dat (i4b) - gru_hru_doubleVec, & ! x%gru(:)%hru(:)%var(:)%dat (dp) - ! gru+hru+z dimension - gru_hru_z_vLookup ! x%gru(:)%hru(:)%z(:)%var(:)%lookup(:) (dp) + gru_hru_doubleVec ! x%gru(:)%hru(:)%var(:)%dat (dp) implicit none private ! ************************************************************************ ! * master summa data type ! ***************************************************************************** -type, public :: summa1_type_dec - ! define the lookup tables - type(gru_hru_z_vLookup) :: lookupStruct ! x%gru(:)%hru(:)%z(:)%var(:)%lookup(:) -- lookup tables - +type, public :: summa1_type_dec ! define the statistics structures type(gru_hru_doubleVec) :: forcStat ! x%gru(:)%hru(:)%var(:)%dat -- model forcing data type(gru_hru_doubleVec) :: progStat ! x%gru(:)%hru(:)%var(:)%dat -- model prognostic (state) variables diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 97607e7b3..d7558c73f 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -131,27 +131,6 @@ MODULE data_types ! *********************************************************************************************************** ! Define hierarchal derived data types ! *********************************************************************************************************** - ! define derived types to hold look-up tables for each soil layer - ! ** double precision type - type, public :: dLookup - real(rkind),allocatable :: lookup(:) ! lookup(:) - endtype dLookup - ! ** double precision type for a variable number of soil layers; variable length - type, public :: vLookup - type(dLookup),allocatable :: var(:) ! var(:)%lookup(:) - endtype vLookup - ! ** double precision type for a variable number of soil layers - type, public :: zLookup - type(vLookup),allocatable :: z(:) ! z(:)%var(:)%lookup(:) - endtype zLookup - ! ** double precision type for a variable number of soil layers - type, public :: hru_z_vLookup - type(zLookup),allocatable :: hru(:) ! hru(:)%z(:)%var(:)%lookup(:) - endtype hru_z_vLookup - ! ** double precision type for a variable number of soil layers - type, public :: gru_hru_z_vLookup - type(hru_z_vLookup),allocatable :: gru(:) ! gru(:)%hru(:)%z(:)%var(:)%lookup(:) - endtype gru_hru_z_vLookup ! define derived types to hold multivariate data for a single variable (different variables have different length) ! NOTE: use derived types here to facilitate adding the "variable" dimension ! ** double precision type diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index a72d881f5..34f457ca7 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -1081,7 +1081,6 @@ subroutine get_ixUnknown(varName,typeName,vDex,err,message) case ('bpar' ); vDex = get_ixBpar(trim(varName)) case ('bvar' ); vDex = get_ixBvar(trim(varName)) case ('deriv'); vDex = get_ixDeriv(trim(varName)) - case ('lookup'); vDex = get_ixLookup(trim(varName)) end select if (vDex>0) then; typeName=trim(structInfo(iStruc)%structName); return; end if end do diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 index 6206d2bc7..74b9b51f3 100644 --- a/build/source/dshare/globalData.f90 +++ b/build/source/dshare/globalData.f90 @@ -53,7 +53,6 @@ MODULE globalData USE var_lookup,only:maxvarBpar ! basin-average parameters: maximum number variables USE var_lookup,only:maxvarDecisions ! maximum number of decisions USE var_lookup,only:maxvarFreq ! maximum number of output files - USE var_lookup,only:maxvarLookup ! maximum number of variables in the lookup implicit none private @@ -138,8 +137,7 @@ MODULE globalData struct_info('prog', 'PROG', maxvarProg ), & ! the prognostic (state) variable data structure struct_info('diag', 'DIAG' , maxvarDiag ), & ! the diagnostic variable data structure struct_info('flux', 'FLUX' , maxvarFlux ), & ! the flux data structure - struct_info('deriv', 'DERIV', maxvarDeriv), & ! the model derivative data structure - struct_info('lookup','LOOKUP',maxvarLookup) /) ! the lookup table data structure + struct_info('deriv', 'DERIV', maxvarDeriv) /) ! the model derivative data structure ! fixed model decisions logical(lgt) , parameter, public :: overwriteRSMIN=.false. ! flag to overwrite RSMIN integer(i4b) , parameter, public :: maxSoilLayers=10000 ! Maximum Number of Soil Layers @@ -165,7 +163,6 @@ MODULE globalData type(var_info),save,public :: diag_meta(maxvarDiag) ! local diagnostic variables for each HRU type(var_info),save,public :: flux_meta(maxvarFlux) ! local model fluxes for each HRU type(var_info),save,public :: deriv_meta(maxvarDeriv) ! local model derivatives for each HRU - type(var_info),save,public :: lookup_meta(maxvarLookup) ! local lookup tables for each HRU type(var_info),save,public :: bpar_meta(maxvarBpar) ! basin parameters for aggregated processes type(var_info),save,public :: bvar_meta(maxvarBvar) ! basin variables for aggregated processes ! ancillary metadata structures diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index b99d67a66..6873fc712 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -31,8 +31,7 @@ subroutine popMetadat(err,message) USE globalData, only: diag_meta ! data structure for local diagnostic variables USE globalData, only: flux_meta ! data structure for local flux variables USE globalData, only: deriv_meta ! data structure for local flux derivatives - USE globalData, only: lookup_meta ! data structure for lookup tables - ! structures of named variables + ! structures of named variables USE var_lookup, only: iLookTIME ! named variables for time data structure USE var_lookup, only: iLookFORCE ! named variables for forcing data structure USE var_lookup, only: iLookTYPE ! named variables for categorical attribute data structure @@ -46,7 +45,6 @@ subroutine popMetadat(err,message) USE var_lookup, only: iLookDIAG ! named variables for local diagnostic variables USE var_lookup, only: iLookFLUX ! named variables for local flux variables USE var_lookup, only: iLookDERIV ! named variables for local flux derivatives - USE var_lookup, only: iLookLOOKUP ! named variables for lookup tables USE var_lookup, only: maxvarFreq ! number of output frequencies USE var_lookup, only: maxvarStat ! number of statistics USE get_ixName_module,only:get_ixVarType ! to turn vartype strings to integers @@ -599,11 +597,6 @@ subroutine popMetadat(err,message) deriv_meta(iLookDERIV%dVolHtCapBulk_dCanWat) = var_info('dVolHtCapBulk_dCanWat' , 'derivative in bulk heat capacity w.r.t. canopy volumetric water content', 'J m-3 K-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dVolHtCapBulk_dTk) = var_info('dVolHtCapBulk_dTk' , 'derivative in bulk heat capacity w.r.t. temperature' , 'J m-3 K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dVolHtCapBulk_dTkCanopy) = var_info('dVolHtCapBulk_dTkCanopy' , 'derivative in bulk heat capacity w.r.t. canopy temperature' , 'J m-3 K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dPsi0Prime) = var_info('dVolHtCapBulk_dPsi0Prime' , 'derivative in bulk heat capacity w.r.t. prime matric potential' , 'J s m-4 K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dThetaPrime) = var_info('dVolHtCapBulk_dThetaPrime' , 'derivative in bulk heat capacity w.r.t. prime volumetric water content', 'J s m-3 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dCanWatPrime) = var_info('dVolHtCapBulk_dCanWatPrime' , 'derivative in bulk heat capacity w.r.t. prime canopy volumetric water content','J s m-3 K-1',get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dTkPrime) = var_info('dVolHtCapBulk_dTkPrime' , 'derivative in bulk heat capacity w.r.t. prime temperature' , 'J s m-3 K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%dVolHtCapBulk_dTkCanPrime) = var_info('dVolHtCapBulk_dTkCanPrime' , 'derivative in bulk heat capacity w.r.t. prime canopy temperature' , 'J s m-3 K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dThermalC_dTempAbove) = var_info('dThermalC_dTempAbove' , 'derivative in the thermal conductivity w.r.t. energy in the layer above','J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dThermalC_dTempBelow) = var_info('dThermalC_dTempBelow' , 'derivative in the thermal conductivity w.r.t. energy in the layer above','J m-2 s-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dThermalC_dWatAbove) = var_info('dThermalC_dWatAbove' , 'derivative in the thermal conductivity w.r.t. water in the layer above', 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) @@ -672,12 +665,6 @@ subroutine popMetadat(err,message) bvar_meta(iLookBVAR%averageInstantRunoff) = var_info('averageInstantRunoff' , 'instantaneous runoff' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) bvar_meta(iLookBVAR%averageRoutedRunoff) = var_info('averageRoutedRunoff' , 'routed runoff' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! ----- - ! * temperature and enthalpy lookup tables... - ! ------------------------------------------- - lookup_meta(iLookLOOKUP%temperature) = var_info('temperature' , 'value of temperature in the lookup table' , 'K' , get_ixVarType('unknown'), iMissVec, iMissVec, .false.) - lookup_meta(iLookLOOKUP%enthalpy) = var_info('enthalpy' , 'value of enthalpy in the lookup table' , 'J m-3' , get_ixVarType('unknown'), iMissVec, iMissVec, .false.) - lookup_meta(iLookLOOKUP%deriv2) = var_info('deriv2' , 'second derivatives of the interpolating function' , 'mixed' , get_ixVarType('unknown'), iMissVec, iMissVec, .false.) - ! ----- ! * model indices... ! ------------------ ! number of model layers, and layer indices diff --git a/build/source/dshare/type4ida.f90 b/build/source/dshare/type4ida.f90 index fa6ee0df9..59058421f 100644 --- a/build/source/dshare/type4ida.f90 +++ b/build/source/dshare/type4ida.f90 @@ -29,7 +29,6 @@ module type4ida logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution type(model_options),allocatable :: model_decisions(:) ! model decisions - type(zLookup) :: lookup_data ! lookup tables type(var_i) :: type_data ! type of vegetation and soil type(var_d) :: attr_data ! spatial attributes type(var_dlength) :: mpar_data ! model parameters diff --git a/build/source/dshare/type4kinsol.f90 b/build/source/dshare/type4kinsol.f90 index 514a093ab..dca7a8d76 100644 --- a/build/source/dshare/type4kinsol.f90 +++ b/build/source/dshare/type4kinsol.f90 @@ -28,7 +28,6 @@ module type4kinsol logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution type(model_options),allocatable :: model_decisions(:) ! model decisions - type(zLookup) :: lookup_data ! lookup tables type(var_i) :: type_data ! type of vegetation and soil type(var_d) :: attr_data ! spatial attributes type(var_dlength) :: mpar_data ! model parameters diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 43f9a7f67..02c3fb949 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -630,11 +630,6 @@ MODULE var_lookup integer(i4b) :: dVolHtCapBulk_dCanWat = integerMissing ! derivative in bulk heat capacity w.r.t. canopy volumetric water content integer(i4b) :: dVolHtCapBulk_dTk = integerMissing ! derivative in bulk heat capacity w.r.t. temperature integer(i4b) :: dVolHtCapBulk_dTkCanopy = integerMissing ! derivative in bulk heat capacity w.r.t. canopy temperature - integer(i4b) :: dVolHtCapBulk_dPsi0Prime = integerMissing ! derivative in bulk heat capacity w.r.t. prime matric potential - integer(i4b) :: dVolHtCapBulk_dThetaPrime = integerMissing ! derivative in bulk heat capacity w.r.t. prime volumetric water content - integer(i4b) :: dVolHtCapBulk_dCanWatPrime = integerMissing ! derivative in bulk heat capacity w.r.t. prime canopy volumetric water content - integer(i4b) :: dVolHtCapBulk_dTkPrime = integerMissing ! derivative in bulk heat capacity w.r.t. prime temperature - integer(i4b) :: dVolHtCapBulk_dTkCanPrime = integerMissing ! derivative in bulk heat capacity w.r.t. prime canopy temperature integer(i4b) :: dThermalC_dTempAbove = integerMissing ! derivative in the thermal conductivity w.r.t. energy state in the layer above integer(i4b) :: dThermalC_dTempBelow = integerMissing ! derivative in the thermal conductivity w.r.t. energy state in the layer above integer(i4b) :: dThermalC_dWatAbove = integerMissing ! derivative in the thermal conductivity w.r.t. water state in the layer above @@ -849,15 +844,6 @@ MODULE var_lookup integer(i4b) :: timestep = integerMissing ! timestep-level output (no temporal aggregation) endtype iLook_freq - ! *********************************************************************************************************** - ! (16) structure for looking up lookup tables - ! *********************************************************************************************************** - type, public :: iLook_vLookup - integer(i4b) :: temperature = integerMissing ! temperature (K) - integer(i4b) :: enthalpy = integerMissing ! enthalpy (J m-3) - integer(i4b) :: deriv2 = integerMissing ! second derivatives of the interpolating function - endtype iLook_vLookup - ! *********************************************************************************************************** ! (X) define data structures and maximum number of variables of each type ! *********************************************************************************************************** @@ -930,7 +916,7 @@ MODULE var_lookup 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,& 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,& 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,& - 71, 72, 73, 74, 75, 76, 77, 78, 79, 80) + 71, 72, 73, 74, 75) ! named variables: model indices type(iLook_index), public,parameter :: iLookINDEX =ilook_index ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& @@ -950,8 +936,6 @@ MODULE var_lookup type(iLook_stat), public,parameter :: iLookStat =ilook_stat ( 1, 2, 3, 4, 5, 6, 7) ! number of possible output frequencies type(iLook_freq), public,parameter :: iLookFreq =ilook_freq ( 1, 2, 3, 4) - ! named variables in the lookup table structure - type(iLook_vLookup), public,parameter :: iLookLOOKUP =ilook_vLookup ( 1, 2, 3) ! define maximum number of variables of each type integer(i4b),parameter,public :: maxvarDecisions = storage_size(iLookDECISIONS)/iLength integer(i4b),parameter,public :: maxvarTime = storage_size(iLookTIME)/iLength diff --git a/build/source/engine/allocspace.f90 b/build/source/engine/allocspace.f90 index 3c35318fa..e263daad9 100644 --- a/build/source/engine/allocspace.f90 +++ b/build/source/engine/allocspace.f90 @@ -46,9 +46,7 @@ module allocspace_module gru_hru_int8, & ! x%gru(:)%hru(:)%var(:) integer(8) gru_hru_double, & ! x%gru(:)%hru(:)%var(:) (rkind) gru_hru_intVec, & ! x%gru(:)%hru(:)%var(:)%dat (i4b) - gru_hru_doubleVec, & ! x%gru(:)%hru(:)%var(:)%dat (rkind) - ! gru+hru+z dimension - gru_hru_z_vLookup ! x%gru(:)%hru(:)%z(:)%var(:)%lookup (rkind) + gru_hru_doubleVec ! x%gru(:)%hru(:)%var(:)%dat (rkind) ! metadata structure USE data_types,only:var_info ! data type for metadata diff --git a/build/source/engine/checkStruc.f90 b/build/source/engine/checkStruc.f90 index 20074d66c..75254b237 100644 --- a/build/source/engine/checkStruc.f90 +++ b/build/source/engine/checkStruc.f90 @@ -40,13 +40,11 @@ subroutine checkStruc(err,message) USE globalData,only:prog_meta,diag_meta,flux_meta,deriv_meta ! metadata structures USE globalData,only:mpar_meta,indx_meta ! metadata structures USE globalData,only:bpar_meta,bvar_meta ! metadata structures - USE globalData,only:lookup_meta ! metadata structures - ! named variables defining strructure elements + ! named variables defining strructure elements USE var_lookup,only:iLookTIME,iLookFORCE,iLookATTR,iLookTYPE,iLookID ! named variables showing the elements of each data structure USE var_lookup,only:iLookPROG,iLookDIAG,iLookFLUX,iLookDERIV ! named variables showing the elements of each data structure USE var_lookup,only:iLookPARAM,iLookINDEX ! named variables showing the elements of each data structure USE var_lookup,only:iLookBPAR,iLookBVAR ! named variables showing the elements of each data structure - USE var_lookup,only:iLookLOOKUP ! named variables showing the elements of each data structure implicit none ! dummy variables integer(i4b),intent(out) :: err ! error code @@ -85,7 +83,6 @@ subroutine checkStruc(err,message) case('diag'); write(longString,*) iLookDIAG case('flux'); write(longString,*) iLookFLUX case('deriv'); write(longString,*) iLookDERIV - case('lookup'); write(longString,*) iLookLOOKUP case default; err=20; message=trim(message)//'unable to identify lookup structure'; return end select ! check that the length of the lookup structure matches the number of variables in the data structure @@ -123,7 +120,6 @@ subroutine checkStruc(err,message) case('diag'); call checkPopulated(iStruct,diag_meta,err,cmessage) case('flux'); call checkPopulated(iStruct,flux_meta,err,cmessage) case('deriv'); call checkPopulated(iStruct,deriv_meta,err,cmessage) - case('lookup'); call checkPopulated(iStruct,lookup_meta,err,cmessage) case default; err=20; message=trim(message)//'unable to identify lookup structure'; return end select if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index 507093c2b..a90b6ca58 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -107,10 +107,6 @@ subroutine computHeatCap(& mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - dCanEnthalpy_dTk, & ! intent(in): derivatives in canopy enthalpy w.r.t. temperature - dCanEnthalpy_dWat, & ! intent(in): derivatives in canopy enthalpy w.r.t. water state - dEnthalpy_dTk, & ! intent(in): derivatives in layer enthalpy w.r.t. temperature - dEnthalpy_dWat, & ! intent(in): derivatives in layer enthalpy w.r.t. water state ! output heatCapVeg, & ! intent(out): heat capacity for canopy mLayerHeatCap, & ! intent(out): heat capacity for snow and soil @@ -153,10 +149,6 @@ subroutine computHeatCap(& real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative of volumetric liquid water content w.r.t. temperature (K-1) real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) - real(rkind),intent(in) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature - real(rkind),intent(in) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state - real(rkind),intent(in) :: dEnthalpy_dTk(:) ! derivatives in layer enthalpy w.r.t. temperature - real(rkind),intent(in) :: dEnthalpy_dWat(:) ! derivatives in layer enthalpy w.r.t. water state ! output: real(qp),intent(out) :: heatCapVeg ! heat capacity for canopy real(qp),intent(out) :: mLayerHeatCap(:) ! heat capacity for snow and soil @@ -202,16 +194,6 @@ subroutine computHeatCap(& heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component Cp_ice*scalarCanopyIce/canopyDepth ! ice component - - ! derivatives - fLiq = scalarFracLiqVeg - dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) - if(scalarCanopyTempTrial < Tfreeze)then - dVolHtCapBulk_dTkCanopy = iden_water * (-Cp_ice + Cp_water) * dTheta_dTkCanopy ! no derivative in air - else - dVolHtCapBulk_dTkCanopy = 0._rkind - endif - else delEnt = scalarCanopyEnthalpyTrial - scalarCanopyEnthalpyPrev heatCapVeg = delEnt / delT @@ -219,6 +201,15 @@ subroutine computHeatCap(& dVolHtCapBulk_dCanWat = dCanEnthalpy_dWat / delT dVolHtCapBulk_dTkCanopy = ( dCanEnthalpy_dTk - delEnt/delT ) / delT endif + + ! derivatives for Jacobian are from analytical solution + fLiq = scalarFracLiqVeg + dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) + if(scalarCanopyTempTrial < Tfreeze)then + dVolHtCapBulk_dTkCanopy = iden_water * (-Cp_ice + Cp_water) * dTheta_dTkCanopy ! no derivative in air + else + dVolHtCapBulk_dTkCanopy = 0._rkind + endif endif ! loop through layers @@ -234,45 +225,41 @@ subroutine computHeatCap(& iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component - ! derivatives - dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use - Tcrit = crit_soilT( mLayerMatricHeadTrial(iSoil) ) - if( mLayerTempTrial(iLayer) < Tcrit)then - dVolHtCapBulk_dPsi0(iSoil) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(iSoil) - dVolHtCapBulk_dTk(iLayer) = (-iden_ice * Cp_ice + iden_water * Cp_water) * mLayerdTheta_dTk(iLayer) - else - dVolHtCapBulk_dPsi0(iSoil) = (iden_water*Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(iSoil) - dVolHtCapBulk_dTk(iLayer) = 0._rkind - endif - case(iname_snow) mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component - ! derivatives - fLiq = mLayerFracLiqSnow(iLayer) - dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air - if( mLayerTempTrial(iLayer) < Tfreeze)then - dVolHtCapBulk_dTk(iLayer) = ( iden_water * (-Cp_ice + Cp_water) + iden_air * (iden_water/iden_ice - 1._rkind) * Cp_air ) * mLayerdTheta_dTk(iLayer) - else - dVolHtCapBulk_dTk(iLayer) = 0._rkind - endif - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return end select else delEnt = mLayerEnthalpyTrial(iLayer) - mLayerEnthalpyPrev(iLayer) mLayerHeatCap(iLayer) = delEnt / delT - ! derivatives - if(iLayer>nSnow)then - iSoil = iLayer-nSnow - dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use - dVolHtCapBulk_dPsi0(iSoil) = dEnthalpy_dWat(iLayer) / delT - else - dVolHtCapBulk_dTheta(iLayer) = dEnthalpy_dWat(iLayer) / delT - endif - dVolHtCapBulk_dTk(iLayer) = ( dEnthalpy_dTk(iLayer) - delEnt/delT ) / delT endif + + ! derivatives for Jacobian are from analytical solution + select case(layerType(iLayer)) + ! * soil + case(iname_soil) + dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use + Tcrit = crit_soilT( mLayerMatricHeadTrial(iSoil) ) + if( mLayerTempTrial(iLayer) < Tcrit)then + dVolHtCapBulk_dPsi0(iSoil) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(iSoil) + dVolHtCapBulk_dTk(iLayer) = (-iden_ice * Cp_ice + iden_water * Cp_water) * mLayerdTheta_dTk(iLayer) + else + dVolHtCapBulk_dPsi0(iSoil) = (iden_water*Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(iSoil) + dVolHtCapBulk_dTk(iLayer) = 0._rkind + endif + ! * snow + case(iname_snow) + fLiq = mLayerFracLiqSnow(iLayer) + dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air + if( mLayerTempTrial(iLayer) < Tfreeze)then + dVolHtCapBulk_dTk(iLayer) = ( iden_water * (-Cp_ice + Cp_water) + iden_air * (iden_water/iden_ice - 1._rkind) * Cp_air ) * mLayerdTheta_dTk(iLayer) + else + dVolHtCapBulk_dTk(iLayer) = 0._rkind + endif + end select + end do ! looping through layers end associate diff --git a/build/source/engine/computHeatCapWithPrime.f90 b/build/source/engine/computHeatCapWithPrime.f90 index 22a900f9e..260ff8b91 100644 --- a/build/source/engine/computHeatCapWithPrime.f90 +++ b/build/source/engine/computHeatCapWithPrime.f90 @@ -102,14 +102,6 @@ subroutine computHeatCapWithPrime(& mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - dCanEnthalpyPrime_dTk, & ! intent(in): derivatives in prime canopy enthalpy w.r.t. temperature - dCanEnthalpyPrime_dWat, & ! intent(in): derivatives in prime canopy enthalpy w.r.t. water state - dEnthalpyPrime_dTk, & ! intent(in): derivatives in prime layer enthalpy w.r.t. temperature - dEnthalpyPrime_dWat, & ! intent(in): derivatives in prime layer enthalpy w.r.t. water state - dCanEnthalpyPrime_dTkPrime, & ! intent(in): derivatives in prime canopy enthalpy w.r.t. prime temperature - dCanEnthalpyPrime_dWatPrime, & ! intent(in): derivatives in prime canopy enthalpy w.r.t. prime water state - dEnthalpyPrime_dTkPrime, & ! intent(in): derivatives in prime layer enthalpy w.r.t. prime temperature - dEnthalpyPrime_dWatPrime, & ! intent(in): derivatives in prime layer enthalpy w.r.t. prime water state ! output heatCapVeg, & ! intent(out): heat capacity for canopy mLayerHeatCap, & ! intent(out): heat capacity for snow and soil @@ -118,11 +110,6 @@ subroutine computHeatCapWithPrime(& dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dPsi0Prime, & ! intent(out): derivative in bulk heat capacity w.r.t. prime matric potential - dVolHtCapBulk_dThetaPrime, & ! intent(out): derivative in bulk heat capacity w.r.t. prime volumetric water content - dVolHtCapBulk_dCanWatPrime, & ! intent(out): derivative in bulk heat capacity w.r.t. prime volumetric water content - dVolHtCapBulk_dTkPrime, & ! intent(out): derivative in bulk heat capacity w.r.t. prime temperature - dVolHtCapBulk_dTkCanPrime, & ! intent(out): derivative in bulk heat capacity w.r.t. prime temperature ! output: error control err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------------- @@ -155,14 +142,6 @@ subroutine computHeatCapWithPrime(& real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative of volumetric liquid water content w.r.t. temperature (K-1) real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) - real(rkind),intent(in) :: dCanEnthalpyPrime_dTk ! derivatives in canopy enthalpy w.r.t. temperature - real(rkind),intent(in) :: dCanEnthalpyPrime_dWat ! derivatives in canopy enthalpy w.r.t. water state - real(rkind),intent(in) :: dEnthalpyPrime_dTk(:) ! derivatives in layer enthalpy w.r.t. temperature - real(rkind),intent(in) :: dEnthalpyPrime_dWat(:) ! derivatives in layer enthalpy w.r.t. water state - real(rkind),intent(in) :: dCanEnthalpyPrime_dTkPrime ! derivatives in canopy enthalpy w.r.t. prime temperature - real(rkind),intent(in) :: dCanEnthalpyPrime_dWatPrime ! derivatives in canopy enthalpy w.r.t. prime water state - real(rkind),intent(in) :: dEnthalpyPrime_dTkPrime(:) ! derivatives in layer enthalpy w.r.t. prime temperature - real(rkind),intent(in) :: dEnthalpyPrime_dWatPrime(:) ! derivatives in layer enthalpy w.r.t. prime water state ! output: real(qp),intent(out) :: heatCapVeg ! heat capacity for canopy real(qp),intent(out) :: mLayerHeatCap(:) ! heat capacity for snow and soil @@ -171,11 +150,6 @@ subroutine computHeatCapWithPrime(& real(rkind),intent(out) :: dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. volumetric water content real(rkind),intent(out) :: dVolHtCapBulk_dTk(:) ! derivative in bulk heat capacity w.r.t. temperature real(rkind),intent(out) :: dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature - real(rkind),intent(out) :: dVolHtCapBulk_dPsi0Prime(:) ! derivative in bulk heat capacity w.r.t. prime matric potential - real(rkind),intent(out) :: dVolHtCapBulk_dThetaPrime(:) ! derivative in bulk heat capacity w.r.t. prime volumetric water content - real(rkind),intent(out) :: dVolHtCapBulk_dCanWatPrime ! derivative in bulk heat capacity w.r.t. prime volumetric water content - real(rkind),intent(out) :: dVolHtCapBulk_dTkPrime(:) ! derivative in bulk heat capacity w.r.t. prime temperature - real(rkind),intent(out) :: dVolHtCapBulk_dTkCanPrime ! derivative in bulk heat capacity w.r.t. prime temperature ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -213,25 +187,18 @@ subroutine computHeatCapWithPrime(& heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component Cp_ice*scalarCanopyIce/canopyDepth ! ice component - - ! derivatives - fLiq = scalarFracLiqVeg - dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) - if(scalarCanopyTempTrial < Tfreeze)then - dVolHtCapBulk_dTkCanopy = iden_water * (-Cp_ice + Cp_water) * dTheta_dTkCanopy ! no derivative in air - else - dVolHtCapBulk_dTkCanopy = 0._rkind - endif - dVolHtCapBulk_dCanWatPrime = 0._rkind - dVolHtCapBulk_dTkCanPrime = 0._rkind else delEnt = scalarCanopyEnthalpyPrime heatCapVeg = delEnt / delT - ! derivatives - dVolHtCapBulk_dCanWat = dCanEnthalpyPrime_dWat / delT - dVolHtCapBulk_dCanWatPrime = dCanEnthalpyPrime_dWatPrime / delT - dVolHtCapBulk_dTkCanopy = dCanEnthalpyPrime_dTk / delT - dVolHtCapBulk_dTkCanPrime = ( dCanEnthalpyPrime_dTkPrime - delEnt/delT ) / delT + endif + + ! derivatives for Jacobian are from analytical solution + fLiq = scalarFracLiqVeg + dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) + if(scalarCanopyTempTrial < Tfreeze)then + dVolHtCapBulk_dTkCanopy = iden_water * (-Cp_ice + Cp_water) * dTheta_dTkCanopy ! no derivative in air + else + dVolHtCapBulk_dTkCanopy = 0._rkind endif endif @@ -248,52 +215,43 @@ subroutine computHeatCapWithPrime(& iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component - ! derivatives - dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use - Tcrit = crit_soilT( mLayerMatricHeadTrial(iSoil) ) - if( mLayerTempTrial(iLayer) < Tcrit)then - dVolHtCapBulk_dPsi0(iSoil) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(iSoil) - dVolHtCapBulk_dTk(iLayer) = (-iden_ice * Cp_ice + iden_water * Cp_water) * mLayerdTheta_dTk(iLayer) - else - dVolHtCapBulk_dPsi0(iSoil) = (iden_water*Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(iSoil) - dVolHtCapBulk_dTk(iLayer) = 0._rkind - endif - dVolHtCapBulk_dPsi0Prime(iLayer) = 0._rkind - + ! * snow case(iname_snow) mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component - ! derivatives - fLiq = mLayerFracLiqSnow(iLayer) - dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air - if( mLayerTempTrial(iLayer) < Tfreeze)then - dVolHtCapBulk_dTk(iLayer) = ( iden_water * (-Cp_ice + Cp_water) + iden_air * (iden_water/iden_ice - 1._rkind) * Cp_air ) * mLayerdTheta_dTk(iLayer) - else - dVolHtCapBulk_dTk(iLayer) = 0._rkind - endif - dVolHtCapBulk_dThetaPrime(iLayer) = 0._rkind case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return end select - dVolHtCapBulk_dTkPrime(iLayer) = 0._rkind else delEnt = mLayerEnthalpyPrime(iLayer) mLayerHeatCap(iLayer) = delEnt / delT - ! derivatives - if(iLayer>nSnow)then - iSoil = iLayer-nSnow - dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use - dVolHtCapBulk_dThetaPrime(iLayer) = realMissing ! do not use - dVolHtCapBulk_dPsi0(iSoil) = dEnthalpyPrime_dWat(iLayer) / delT - dVolHtCapBulk_dPsi0Prime(iSoil) = dEnthalpyPrime_dWatPrime(iLayer) / delT - else - dVolHtCapBulk_dTheta(iLayer) = dEnthalpyPrime_dWat(iLayer) / delT - dVolHtCapBulk_dThetaPrime(iLayer) = dEnthalpyPrime_dWatPrime(iLayer) / delT - endif - dVolHtCapBulk_dTk(iLayer) = dEnthalpyPrime_dTk(iLayer) / delT - dVolHtCapBulk_dTkPrime(iLayer) = ( dEnthalpyPrime_dTkPrime(iLayer) - delEnt/delT ) / delT endif + + ! derivatives for Jacobian are from analytical solution + select case(layerType(iLayer)) + ! * soil + case(iname_soil) + dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use + Tcrit = crit_soilT( mLayerMatricHeadTrial(iSoil) ) + if( mLayerTempTrial(iLayer) < Tcrit)then + dVolHtCapBulk_dPsi0(iSoil) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(iSoil) + dVolHtCapBulk_dTk(iLayer) = (-iden_ice * Cp_ice + iden_water * Cp_water) * mLayerdTheta_dTk(iLayer) + else + dVolHtCapBulk_dPsi0(iSoil) = (iden_water*Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(iSoil) + dVolHtCapBulk_dTk(iLayer) = 0._rkind + endif + ! * snow + case(iname_snow) + fLiq = mLayerFracLiqSnow(iLayer) + dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air + if( mLayerTempTrial(iLayer) < Tfreeze)then + dVolHtCapBulk_dTk(iLayer) = ( iden_water * (-Cp_ice + Cp_water) + iden_air * (iden_water/iden_ice - 1._rkind) * Cp_air ) * mLayerdTheta_dTk(iLayer) + else + dVolHtCapBulk_dTk(iLayer) = 0._rkind + endif + end select + end do ! looping through layers end associate diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index 38f7f4d4c..e25b46dee 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -273,11 +273,6 @@ subroutine computJacobWithPrime(& dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dPsi0Prime => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0Prime )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. prime matric potential - dVolHtCapBulk_dThetaPrime => deriv_data%var(iLookDERIV%dVolHtCapBulk_dThetaPrime )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. prime volumetric water content - dVolHtCapBulk_dCanWatPrime => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWatPrime )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. prime volumetric water content - dVolHtCapBulk_dTkPrime => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkPrime )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. prime temperature - dVolHtCapBulk_dTkCanPrime => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanPrime )%dat(1) ,& ! intent(in): [dp] derivative in bulk heat capacity w.r.t. prime temperature ! derivative in Cm w.r.t. relevant state variables dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk )%dat ,& ! intent(in): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) @@ -312,7 +307,7 @@ subroutine computJacobWithPrime(& ! compute terms in the Jacobian for vegetation (excluding fluxes) ! NOTE: energy for vegetation is computed *within* the iteration loop as it includes phase change if(ixVegNrg/=integerMissing)then - dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + dVolHtCapBulk_dTkCanPrime*scalarCanopyTempPrime + LH_fus*iden_water*dTheta_dTkCanopy ) * cj & + dMat(ixVegNrg) = ( scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy ) * cj & + dVolHtCapBulk_dTkCanopy * scalarCanopyTempPrime & + dCm_dTkCanopy * scalarCanopyWatPrime / canopyDepth & + LH_fus*iden_water * scalarCanopyTempPrime * d2Theta_dTkCanopy2 & @@ -323,7 +318,7 @@ subroutine computJacobWithPrime(& ! NOTE: energy for snow+soil is computed *within* the iteration loop as it includes phase change do iLayer=1,nLayers if(ixSnowSoilNrg(iLayer)/=integerMissing)then - dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + dVolHtCapBulk_dTkPrime(iLayer)*mLayerTempPrime(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj & + dMat(ixSnowSoilNrg(iLayer)) = ( mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) ) * cj & + dVolHtCapBulk_dTk(iLayer) * mLayerTempPrime(iLayer) & + dCm_dTk(iLayer) * mLayerVolFracWatPrime(iLayer) & + LH_fus*iden_water * mLayerTempPrime(iLayer) * mLayerd2Theta_dTk2(iLayer) & @@ -376,7 +371,7 @@ subroutine computJacobWithPrime(& ! * cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = ( (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth + dVolHtCapBulk_dCanWatPrime*scalarCanopyTempPrime ) * cj & + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth * cj & + dVolHtCapBulk_dCanWat * scalarCanopyTempPrime + scalarCanopyCm/canopyDepth * cj & - (dt/canopyDepth) * dCanopyNetFlux_dCanWat & + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth @@ -490,7 +485,7 @@ subroutine computJacobWithPrime(& if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) ! - include derivatives of energy fluxes w.r.t water fluxes for current layer - aJac(ixOffDiag(nrgState,watState),watState) = ( (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water + dVolHtCapBulk_dThetaPrime(iLayer)*mLayerTempPrime(iLayer) ) * cj & + aJac(ixOffDiag(nrgState,watState),watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) + mLayerCm(iLayer) * cj & + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) & + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) @@ -661,8 +656,7 @@ subroutine computJacobWithPrime(& endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) & - + ( dVolHtCapBulk_dPsi0Prime(iLayer) * mLayerTempPrime(jLayer) + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) ) * cj & + aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) * cj & + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + mLayerCm(jLayer) * d2VolTot_dPsi02(iLayer) * mLayerMatricHeadPrime(iLayer) if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(ixOffDiag(nrgState,watState),watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) * cj & @@ -731,7 +725,7 @@ subroutine computJacobWithPrime(& ! * cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = ( (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth + dVolHtCapBulk_dCanWatPrime * scalarCanopyTempPrime ) * cj & + if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth * cj & + dVolHtCapBulk_dCanWat * scalarCanopyTempPrime + scalarCanopyCm/canopyDepth * cj& - (dt/canopyDepth) * dCanopyNetFlux_dCanWat & + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth @@ -845,10 +839,11 @@ subroutine computJacobWithPrime(& if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) ! - include derivatives of energy fluxes w.r.t water fluxes for current layer - aJac(nrgState,watState) = ( (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water + dVolHtCapBulk_dThetaPrime(iLayer)*mLayerTempPrime(iLayer) ) * cj & + aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) + mLayerCm(iLayer) * cj & + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) & + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) + ! - include derivatives of water fluxes w.r.t energy fluxes for current layer aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) @@ -994,10 +989,8 @@ subroutine computJacobWithPrime(& endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) & - + ( dVolHtCapBulk_dPsi0Prime(iLayer) * mLayerTempPrime(jLayer) + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) ) * cj & + aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) * cj & + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + mLayerCm(jLayer) * d2VolTot_dPsi02(iLayer) * mLayerMatricHeadPrime(iLayer) - if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(nrgState,watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) * cj & - LH_fus*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_dPsi02(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content diff --git a/build/source/engine/convE2Temp.f90 b/build/source/engine/convE2Temp.f90 deleted file mode 100644 index fe80dd9c6..000000000 --- a/build/source/engine/convE2Temp.f90 +++ /dev/null @@ -1,231 +0,0 @@ -! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington -! -! This file is part of SUMMA -! -! For more information see: http://www.ral.ucar.edu/projects/summa -! -! This program is free software: you can redistribute it and/or modify -! it under the terms of the GNU General Public License as published by -! the Free Software Foundation, either version 3 of the License, or -! (at your option) any later version. -! -! This program is distributed in the hope that it will be useful, -! but WITHOUT ANY WARRANTY; without even the implied warranty of -! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -! GNU General Public License for more details. -! -! You should have received a copy of the GNU General Public License -! along with this program. If not, see . - -module convE2Temp_module - -! data types -USE nrtype -USE data_types,only:var_dlength ! data vector with variable length dimension (rkind): x%var(:)%dat(:) - -! constants -USE multiconst, only: Tfreeze, & ! freezing point of water (K) - Cp_soil,Cp_water,Cp_ice,& ! specific heat of soil, water and ice (J kg-1 K-1) - LH_fus ! latent heat of fusion (J kg-1) - -! indices within parameter structure -USE var_lookup,only:iLookPARAM ! named variables to define structure element - -! privacy -implicit none -private -public::E2T_lookup -public::E2T_nosoil -public::temp2ethpy - -! define the look-up table used to compute temperature based on enthalpy -integer(i4b),parameter :: nlook=10001 ! number of elements in the lookup table -real(rkind),dimension(nlook),public :: E_lookup ! enthalpy values (J kg-1) -real(rkind),dimension(nlook),public :: T_lookup ! temperature values (K) -contains - - - ! ************************************************************************************************************************ - ! public subroutine E2T_lookup: define a look-up table to compute specific enthalpy based on temperature, assuming no soil - ! ************************************************************************************************************************ - subroutine E2T_lookup(mpar_data,err,message) - USE nr_utility_module,only:arth ! use to build vectors with regular increments - USE spline_int_module,only:spline,splint ! use for cubic spline interpolation - implicit none - ! declare dummy variables - type(var_dlength),intent(in) :: mpar_data ! model parameters - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! declare local variables - character(len=128) :: cmessage ! error message in downwind routine - real(rkind),parameter :: T_start=260.0_rkind ! start temperature value where all liquid water is assumed frozen (K) - real(rkind) :: T_incr,E_incr ! temperature/enthalpy increments - real(rkind),dimension(nlook) :: Tk ! initial temperature vector - real(rkind),dimension(nlook) :: Ey ! initial enthalpy vector - real(rkind),parameter :: waterWght=1._rkind ! weight applied to total water (kg m-3) --- cancels out - real(rkind),dimension(nlook) :: T2deriv ! 2nd derivatives of the interpolating function at tabulated points - real(rkind) :: dT ! derivative of temperature with enthalpy at E_lookup - integer(i4b) :: ilook ! loop through lookup table - ! initialize error control - err=0; message="E2T_lookup/" - ! associate - associate( snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ) - ! define initial temperature vector - T_incr = (Tfreeze - T_start) / real(nlook-1, kind(rkind)) ! temperature increment - Tk = arth(T_start,T_incr,nlook) - ! ***** compute specific enthalpy (NOTE: J m-3 --> J kg-1) ***** - do ilook=1,nlook - Ey(ilook) = temp2ethpy(Tk(ilook),waterWght,snowfrz_scale)/waterWght ! (J m-3 --> J kg-1) - end do - ! define the final enthalpy vector - E_incr = (-Ey(1)) / real(nlook-1, kind(rkind)) ! enthalpy increment - E_lookup = arth(Ey(1),E_incr,nlook) - ! use cubic spline interpolation to obtain temperature values at the desired values of enthalpy - call spline(Ey,Tk,1.e30_rkind,1.e30_rkind,T2deriv,err,cmessage) ! get the second derivatives - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - do ilook=1,nlook - call splint(Ey,Tk,T2deriv,E_lookup(ilook),T_lookup(ilook),dT,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - !write(*,'(i6,1x,2(f20.4,1x))') ilook, E_lookup(ilook), T_lookup(ilook) - end do - end associate - end subroutine E2T_lookup - - - ! ************************************************************************************************************************ - ! public subroutine E2T_nosoil: compute temperature based on specific enthalpy -- appropriate when no dry mass, as in snow - ! ************************************************************************************************************************ - subroutine E2T_nosoil(Ey,BulkDenWater,fc_param,Tk,err,message) - ! compute temperature based on enthalpy -- appropriate when no dry mass, as in snow - implicit none - ! declare dummy variables - real(rkind),intent(in) :: Ey ! total enthalpy (J m-3) - real(rkind),intent(in) :: BulkDenWater ! bulk density of water (kg m-3) - real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) - real(rkind),intent(out) :: Tk ! initial temperature guess / final temperature value (K) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! declare local variables - real(rkind),parameter :: dx=1.d-8 ! finite difference increment (J kg-1) - real(rkind),parameter :: atol=1.d-12 ! convergence criteria (J kg-1) - real(rkind) :: E_spec ! specific enthalpy (J kg-1) - real(rkind) :: E_incr ! enthalpy increment - integer(i4b) :: niter=15 ! maximum number of iterations - integer(i4b) :: iter ! iteration index - integer(i4b) :: i0 ! position in lookup table - real(rkind) :: Tg0,Tg1 ! trial temperatures (K) - real(rkind) :: Ht0,Ht1 ! specific enthalpy, based on the trial temperatures (J kg-1) - real(rkind) :: f0,f1 ! function evaluations (difference between enthalpy guesses) - real(rkind) :: dh ! enthalpy derivative - real(rkind) :: dT ! temperature increment - ! initialize error control - err=0; message="E2T_nosoil/" - ! convert input of total enthalpy (J m-3) to total specific enthalpy (J kg-1) - E_spec = Ey/BulkDenWater ! (NOTE: no soil) - !write(*,'(a,1x,10(e20.10,1x))') 'E_spec, E_lookup(1)', E_spec, E_lookup(1) - - ! ***** get initial guess and derivative assuming all water is frozen - if(E_spec E_lookup(i0+1) .or. & - i0 < 1 .or. i0+1 > nlook)then - err=10; message=trim(message)//'problem finding appropriate value in lookup table'; return - end if - ! get temperature guess - Tg0 = T_lookup(i0) - Tg1 = T_lookup(i0+1) - ! compute function evaluations - f0 = E_lookup(i0) - E_spec - f1 = E_lookup(i0+1) - E_spec - end if - - ! compute initial derivative - dh = (f1 - f0) / (Tg1 - Tg0) - ! compute initial change in T - dT = -f0/dh - !write(*,'(a,1x,f12.5,1x,10(e20.10,1x))') 'Tg1, f0, f1, dh, dT = ', Tg1, f0, f1, dh, dT - ! exit if already close enough - if(abs(dT) mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) , & ! scaling parameter for freezing (K-1) - soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(iSoil) , & ! intrinsic soil density (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(iSoil) , & ! soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(iSoil) , & ! volumetric residual water content (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(iSoil) , & ! van Genuchten "alpha" parameter (m-1) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(iSoil) , & ! van Genuchten "n" parameter (-) - - ! associate values in the lookup table - Tk => lookup_data%z(iSoil)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ey => lookup_data%z(iSoil)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) - E2 => lookup_data%z(iSoil)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function - - ) ! end associate statement - - ! compute vGn_m - vGn_m = 1._rkind - 1._rkind/vGn_n - - ! ----- - ! * populate the lookup table... - ! ------------------------------ - - ! initialize temperature and enthalpy - Tk(nLook) = Tfreeze - Ey(nLook) = 0._rkind - - ! loop through lookup table - do iLook=(nLook-1),1,-1 - - ! update temperature and enthalpy - Tk(iLook) = Tk(iLook+1) - Ey(iLook) = Ey(iLook+1) - - ! get the temperature increment for the numerical integration - T_incr = (xTemp(iLook)-xTemp(iLook+1))/real(nIntegr8, kind(rkind)) - - ! numerical integration between different values of the lookup table - do jIntegr8=1,nIntegr8 - - ! update temperature - Tk(iLook) = Tk(iLook) + T_incr - - ! compute the volumetric liquid water and ice content - ! NOTE: assume saturation - matricHead = (LH_fus/gravity)*(Tk(iLook) - Tfreeze)/Tfreeze - vFracLiq = volFracLiq(matricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - volFracIce = theta_sat - vFracLiq - - ! compute enthalpy - ! NOTE: assume intrrinsic density of ice is the intrinsic density of water - ! NOTE: kg m-3 J kg-1 K-1 K - Ey(iLook) = Ey(iLook) + iden_water * Cp_water*vFracLiq*T_incr + iden_water * Cp_ice*volFracIce*T_incr - - end do ! numerical integration - - end do ! loop through lookup table - - ! use cubic spline interpolation to obtain enthalpy values at the desired values of temperature - call spline(Tk,Ey,1.e30_rkind,1.e30_rkind,E2,err,cmessage) ! get the second derivatives - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! test - if(doTest)then - - ! calculate enthalpy - call splint(Tk,Ey,E2,T_test,E_test,dE,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! write values - print*, 'doTest = ', doTest - print*, 'T_test = ', T_test ! temperature (K) - print*, 'E_test = ', E_test ! enthalpy (J m-3) - print*, 'theta_sat = ', theta_sat ! soil porosity (-) - print*, 'theta_res = ', theta_res ! volumetric residual water content (-) - print*, 'vGn_alpha = ', vGn_alpha ! van Genuchten "alpha" parameter (m-1) - print*, 'vGn_n = ', vGn_n ! van Genuchten "n" parameter (-) - print*, trim(message)//'PAUSE: Set doTest=.false. to complete simulations' - read(*,*) - - endif ! if testing - - ! end asssociation to variables in the data structures - end associate - - end do ! (looping through soil layers) -end subroutine T2E_lookup - - ! ************************************************************************************************************************ ! public subroutine t2enthalpy: compute enthalpy from temperature and total water content ! ************************************************************************************************************************ @@ -246,7 +72,6 @@ subroutine t2enthalpy(& diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure ! input: state variables for the vegetation canopy scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) @@ -255,23 +80,16 @@ subroutine t2enthalpy(& mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - ! input: pre-computed derivatives - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) mLayerEnthalpy, & ! intent(out): enthalpy of each snow+soil layer (J m-3) - dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature - dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state - dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature - dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state ! output: error control err,message) ! intent(out): error control ! ------------------------------------------------------------------------------------------------------------------------- ! downwind routines USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water - USE spline_int_module,only:splint ! use for cubic spline interpolation implicit none ! delare dummy variables ! ------------------------------------------------------------------------------------------------------------------------- @@ -279,7 +97,6 @@ subroutine t2enthalpy(& type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model indices - type(zLookup),intent(in) :: lookup_data ! lookup tables ! input: state variables for the vegetation canopy real(rkind),intent(in) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) @@ -288,8 +105,7 @@ subroutine t2enthalpy(& real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - ! input: pre-computed derivatives - real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) + real(rkind),intent(in) :: mLayerMatricHeadLiqTrial(:)! trial vector of liquid water matric potential (m) ! output: enthalpy real(rkind),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) real(rkind),intent(out) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) @@ -316,9 +132,8 @@ subroutine t2enthalpy(& real(rkind) :: diffT ! temperature difference from Tfreeze real(rkind) :: integral ! integral of snow freezing curve real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head - real(rkind) :: d_integral_dTk ! derivative of integral with temperature - real(rkind) :: dE ! derivative of enthalpy with temperature at layer temperature - real(rkind) :: dEcrit ! derivative of enthalpy with temperature at critical temperature + real(rkind) :: arg ! argument of hypergeometric function + real(rkind) :: gauss_hg_T ! hypergeometric function result ! enthalpy real(rkind) :: enthVeg ! enthalpy of the vegetation (J m-3) real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) @@ -330,19 +145,6 @@ subroutine t2enthalpy(& real(rkind) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) real(rkind) :: enthWater ! enthalpy of total water (J m-3) - ! enthalpy derivatives - real(rkind) :: dEnthVeg_dTk ! derivative of enthalpy of the vegetation with temperature - real(rkind) :: dEnthSoil_dTk ! derivative of enthalpy of the soil with temperature - real(rkind) :: dEnthLiq_dTk ! derivative of enthalpy of the liquid with temperature - real(rkind) :: dEnthIce_dTk ! derivative of enthalpy of the ice with temperature - real(rkind) :: dEnthAir_dTk ! derivative of enthalpy of the air with temperature - real(rkind) :: dEnthWater_dTk ! derivative of enthalpy of the total water with temperature - real(rkind) :: dEnthVeg_dWat ! derivative of enthalpy of the vegetation with water state - real(rkind) :: dEnthSoil_dWat ! derivative of enthalpy of the soil with water state - real(rkind) :: dEnthLiq_dWat ! derivative of enthalpy of the liquid with water state - real(rkind) :: dEnthIce_dWat ! derivative of enthalpy of the ice with water state - real(rkind) :: dEnthAir_dWat ! derivative of enthalpy of the air with water state - real(rkind) :: dEnthWater_dWat ! derivative of enthalpy of the total water with water state ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures generalVars: associate(& @@ -404,34 +206,17 @@ subroutine t2enthalpy(& diffT = scalarCanopyTempTrial - Tfreeze enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth - ! enthalpy derivatives - dEnthVeg_dTk = specificHeatVeg * maxMassVegetation / canopyDepth - dEnthVeg_dWat = 0._rkind if(diffT>=0._rkind)then enthLiq = Cp_water * scalarCanopyWatTrial * diffT / canopyDepth enthIce = 0._rkind - ! enthalpy derivatives - dEnthLiq_dTk = Cp_water * scalarCanopyWatTrial / canopyDepth ! Note, equals Cp_water*VolFracLiq - dEnthLiq_dWat = Cp_water * diffT / canopyDepth - dEnthIce_dTk = 0._rkind - dEnthIce_dWat = 0._rkind else integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth - ! derivatives - d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) - ! enthalpy derivatives - dEnthLiq_dTk = Cp_water * scalarCanopyWatTrial * d_integral_dTk / canopyDepth ! Note, equals Cp_water*VolFracLiq - dEnthLiq_dWat = Cp_water * integral / canopyDepth - dEnthIce_dTk = Cp_ice * scalarCanopyWatTrial * ( 1._rkind - d_integral_dTk ) / canopyDepth ! Note, equals Cp_ice*VolFracIce - dEnthIce_dWat = Cp_ice * ( diffT - integral ) / canopyDepth endif scalarCanopyEnthalpy = enthVeg + enthLiq + enthIce - dCanEnthalpy_dTk = dEnthVeg_dTk + dEnthLiq_dTk + dEnthIce_dTk - dCanEnthalpy_dWat = dEnthVeg_dWat + dEnthLiq_dWat + dEnthIce_dWat end associate vegVars @@ -447,19 +232,8 @@ subroutine t2enthalpy(& enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) - ! derivatives - d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) - ! enthalpy derivatives - dEnthLiq_dTk = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * d_integral_dTk ! Note, equals Cp_water*VolFracLiq - dEnthLiq_dWat = iden_water * Cp_water * integral - dEnthIce_dTk = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( 1._rkind - d_integral_dTk ) ! Note, equals Cp_ice*VolFracIce - dEnthIce_dWat = iden_water * Cp_ice * ( diffT - integral ) - dEnthAir_dTk = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(1._rkind-d_integral_dTk) + d_integral_dTk ) ) - dEnthAir_dWat = -iden_air * Cp_air * ( (iden_water/iden_ice)*(diffT-integral) + integral ) - + mLayerEnthalpy(iLayer) = enthLiq + enthIce + enthAir - dEnthalpy_dTk(iLayer) = dEnthLiq_dTk + dEnthIce_dTk + dEnthAir_dTk - dEnthalpy_dWat(iLayer) = dEnthLiq_dWat + dEnthIce_dWat + dEnthAir_dWat end associate snowVars @@ -475,11 +249,6 @@ subroutine t2enthalpy(& vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) - ! associate values in the lookup table - Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) - E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function - ) ! end associate statement ! diagnostic variables @@ -491,48 +260,31 @@ subroutine t2enthalpy(& ! *** compute enthalpy of water for unfrozen conditions if(mlayerTempTrial(iLayer)>=Tcrit)then enthWater = iden_water * Cp_water * volFracWat * diffT ! valid for temperatures below freezing also - ! enthalpy derivatives - dEnthWater_dTk = iden_water * Cp_water * volFracWat - dEnthWater_dWat = iden_water * Cp_water * dVolTot_dPsi0(ixControlIndex) ! dVolFracWat_dWat = dVolTot_dPsi0(ixControlIndex) ! *** compute enthalpy of water for frozen conditions else - ! calculate enthalpy at the temperature (cubic spline interpolation) - call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),enthTemp,dE,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! calculate enthalpy at the critical temperature (cubic spline interpolation) - call splint(Tk,Ey,E2,Tcrit,enthTcrit,dEcrit,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + ! compute enthalpy of unfrozen and frozen water + enthLiq = iden_water * Cp_water * volFracWat * (Tcrit - Tfreeze) + enthIce = iden_ice * Cp_ice * volFracWat * (mLayerTempTrial(iLayer) - Tcrit) + + ! compute enthalpy of the mixed region, liquid+ice + arg = (-vGn_alpha * mLayerMatricHeadLiqTrial(ixControlIndex))**vGn_n + gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + enthTemp = (iden_water * Cp_water - iden_ice * Cp_ice) * diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + arg = (-vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n + gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + enthTcrit = (iden_water * Cp_water - iden_ice * Cp_ice) * diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) ! calculate the enthalpy of water enthMix = enthTemp - enthTcrit ! enthalpy of the liquid+ice mix - enthLiq = iden_water * Cp_water * volFracWat * (Tcrit - Tfreeze) - enthWater = enthMix + enthLiq - ! derivatives - dTcrit_dPsi0 = 0._rkind - if (mLayerMatricHeadTrial(ixControlIndex)<0._rkind) dTcrit_dPsi0 = gravity * Tfreeze/LH_fus - ! enthalpy derivatives - dEnthWater_dTk = dE - dEcrit - dEnthWater_dWat = -dEcrit*dTcrit_dPsi0 + iden_water * Cp_water * dVolTot_dPsi0(ixControlIndex) * (Tcrit - Tfreeze) + iden_water * Cp_water * volFracWat*dTcrit_dPsi0 + enthWater = enthMix + enthLiq + enthIce + endif ! (if frozen conditions) - ! *** compute the enthalpy of soil enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*diffT - ! enthalpy derivatives - dEnthSoil_dTk = soil_dens_intr*Cp_soil*(1._rkind - theta_sat) - dEnthSoil_dWat = 0._rkind - - ! *** compute the enthalpy of air enthAir = iden_air*Cp_air*(1._rkind - theta_sat - volFracWat)*diffT - ! enthalpy derivatives - dEnthAir_dTk = iden_air*Cp_air*(1._rkind - theta_sat - volFracWat) - dEnthAir_dWat = -iden_air*Cp_air*dVolTot_dPsi0(ixControlIndex)*diffT - ! *** compute the total enthalpy (J m-3) mLayerEnthalpy(iLayer) = enthWater + enthSoil + enthAir - dEnthalpy_dTk(iLayer) = dEnthWater_dTk + dEnthSoil_dTk + dEnthAir_dTk - dEnthalpy_dWat(iLayer) = dEnthWater_dWat + dEnthSoil_dWat + dEnthAir_dWat end associate soilVars @@ -670,4 +422,37 @@ subroutine t2enthalpy_addphase(& end subroutine t2enthalpy_addphase +! ************************************************************************************************************************ +! private function hypergeometric: compute Gaussian hypergeometric function +! ************************************************************************************************************************ +function hypergeometric(a, b, c, z) + implicit none + real(rkind),intent(in) :: a, b, c, z ! input parameters + real(rkind) :: term, factorial ! local variables + real(rkind) :: hypergeometric ! output result + integer(i4b) :: n, max_iter ! iteration count variables + + max_iter = 1000 ! maximum number of iterations + + ! initialize + hypergeometric = 1._rkind + term = 1._rkind + factorial = 1._rkind + + do n = 1, max_iter + factorial = factorial * n + term = term * (a + n - 1) * (b + n - 1) / ((c + n - 1) * factorial) * z + hypergeometric = hypergeometric + term + + if (abs(term) < 1.e-6_rkind) exit ! convergence condition + + if (n == max_iter) then + ! handle non-convergence + write(*, *) "Warning: Hypergeometric function did not converge within the maximum number of iterations." + exit + end if + end do + +end function hypergeometric + end module t2enthalpy_module diff --git a/build/source/engine/t2enthalpyAddPrime.f90 b/build/source/engine/t2enthalpyAddPrime.f90 index a56737db7..53598ec0a 100644 --- a/build/source/engine/t2enthalpyAddPrime.f90 +++ b/build/source/engine/t2enthalpyAddPrime.f90 @@ -31,7 +31,6 @@ module t2enthalpyAddPrime_module USE nrtype USE data_types,only:var_iLength ! var(:)%dat(:) USE data_types,only:var_dLength ! var(:)%dat(:) -USE data_types,only:zLookup ! z(:)%var(:)%lookup(:) ! indices within parameter structure USE var_lookup,only:iLookPARAM ! named variables to define structure element @@ -39,9 +38,6 @@ module t2enthalpyAddPrime_module USE var_lookup,only:iLookLOOKUP ! named variables to define structure element USE var_lookup,only:iLookDIAG ! named variables for structure elements -! data dimensions -USE var_lookup,only:maxvarLookup ! maximum number of variables in the lookup tables - ! domain types USE globalData,only:iname_cas ! named variables for canopy air space USE globalData,only:iname_veg ! named variables for vegetation canopy @@ -63,7 +59,6 @@ module t2enthalpyAddPrime_module private public::t2enthalpyPrime -! define the look-up table used to compute temperature based on enthalpy contains @@ -75,7 +70,6 @@ subroutine t2enthalpyPrime(& diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure ! input: state variables for the vegetation canopy scalarCanairTempPrime, & ! intent(in): prime value of canopy air temperature (K) scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) @@ -91,26 +85,16 @@ subroutine t2enthalpyPrime(& mLayerMatricHeadPrime, & ! intent(in): prime vector of total water matric potential (m) ! input: pre-computed derivatives dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - d2VolTot_dPsi02, & ! intent(in): second derivative in total water content w.r.t. total water matric potential (m-2) - ! output: enthalpy prime and derivatives + ! output: enthalpy prime scalarCanairEnthalpyPrime, & ! intent(out): prime enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpyPrime, & ! intent(out): prime enthalpy of the vegetation canopy (J m-3) mLayerEnthalpyPrime, & ! intent(out): prime enthalpy of each snow+soil layer (J m-3) - dCanEnthalpyPrime_dTk, & ! intent(out): derivatives in prime canopy enthalpy w.r.t. temperature - dCanEnthalpyPrime_dWat, & ! intent(out): derivatives in prime canopy enthalpy w.r.t. water state - dEnthalpyPrime_dTk, & ! intent(out): derivatives in prime layer enthalpy w.r.t. temperature - dEnthalpyPrime_dWat, & ! intent(out): derivatives in prime layer enthalpy w.r.t. water state - dCanEnthalpyPrime_dTkPrime, & ! intent(out): derivatives in prime canopy enthalpy w.r.t. prime temperature - dCanEnthalpyPrime_dWatPrime, & ! intent(out): derivatives in prime canopy enthalpy w.r.t. prime water state - dEnthalpyPrime_dTkPrime, & ! intent(out): derivatives in prime layer enthalpy w.r.t. prime temperature - dEnthalpyPrime_dWatPrime, & ! intent(out): derivatives in prime layer enthalpy w.r.t. prime water state ! output: error control err,message) ! intent(out): error control ! ------------------------------------------------------------------------------------------------------------------------- ! downwind routines USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water - USE spline_int_module,only:splint ! use for cubic spline interpolation implicit none ! delare dummy variables ! ------------------------------------------------------------------------------------------------------------------------- @@ -118,7 +102,6 @@ subroutine t2enthalpyPrime(& type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model indices - type(zLookup),intent(in) :: lookup_data ! lookup tables ! input: state variables for the vegetation canopy real(rkind),intent(in) :: scalarCanairTempPrime ! prime value of canopy air temperature (K) real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) @@ -134,21 +117,10 @@ subroutine t2enthalpyPrime(& real(rkind),intent(in) :: mLayerMatricHeadPrime(:) ! prime vector of total water matric potential (m) ! input: pre-computed derivatives real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) - real(rkind),intent(in) :: d2VolTot_dPsi02(:) ! second derivative in total water content w.r.t. total water matric potential (m-2) - ! output: enthalpy prime + ! output: enthalpy prime real(rkind),intent(out) :: scalarCanairEnthalpyPrime ! prime enthalpy of the canopy air space (J m-3) real(rkind),intent(out) :: scalarCanopyEnthalpyPrime ! prime enthalpy of the vegetation canopy (J m-3) real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! prime enthalpy of each snow+soil layer (J m-3) - ! output: derivatives - real(rkind),intent(out) :: dCanEnthalpyPrime_dTk ! derivatives in prime canopy enthalpy w.r.t. temperature - real(rkind),intent(out) :: dCanEnthalpyPrime_dWat ! derivatives in prime canopy enthalpy w.r.t. water state - real(rkind),intent(out) :: dEnthalpyPrime_dTk(:) ! derivatives in prime layer enthalpy w.r.t. temperature - real(rkind),intent(out) :: dEnthalpyPrime_dWat(:) ! derivatives in prime layer enthalpy w.r.t. water state - real(rkind),intent(out) :: dCanEnthalpyPrime_dTkPrime ! derivatives in prime canopy enthalpy w.r.t. priem temperature - real(rkind),intent(out) :: dCanEnthalpyPrime_dWatPrime ! derivatives in prime canopy enthalpy w.r.t. prime water state - real(rkind),intent(out) :: dEnthalpyPrime_dTkPrime(:) ! derivatives in prime layer enthalpy w.r.t. prime temperature - real(rkind),intent(out) :: dEnthalpyPrime_dWatPrime(:) ! derivatives in prime layer enthalpy w.r.t. prime water state - ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -167,9 +139,6 @@ subroutine t2enthalpyPrime(& real(rkind) :: integral ! integral of snow freezing curve real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head real(rkind) :: d_integral_dTk ! derivative of integral with temperature - real(rkind) :: d2_integral_dTk2 ! second derivative of integral with temperature - real(rkind) :: dE ! derivative of enthalpy with temperature at layer temperature - real(rkind) :: dEcrit ! derivative of enthalpy with temperature at critical temperature ! enthalpy real(rkind) :: enthVegP ! prime enthalpy of the vegetation (J m-3) real(rkind) :: enthSoilP ! prime enthalpy of soil particles (J m-3) @@ -181,32 +150,6 @@ subroutine t2enthalpyPrime(& real(rkind) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) real(rkind) :: enthPhaseP ! prime enthalpy associated with phase change (J m-3) real(rkind) :: enthWaterP ! prime enthalpy of total water (J m-3) - ! enthalpy derivatives - real(rkind) :: dEnthVegP_dTk ! derivative of prime enthalpy of the vegetation with temperature - real(rkind) :: dEnthSoilP_dTk ! derivative of prime enthalpy of the soil with temperature - real(rkind) :: dEnthLiqP_dTk ! derivative of prime enthalpy of the liquid with temperature - real(rkind) :: dEnthIceP_dTk ! derivative of prime enthalpy of the ice with temperature - real(rkind) :: dEnthAirP_dTk ! derivative of prime enthalpy of the air with temperature - real(rkind) :: dEnthWaterP_dTk ! derivative of prime enthalpy of the total water with temperature - real(rkind) :: dEnthVegP_dWat ! derivative of prime enthalpy of the vegetation with water state - real(rkind) :: dEnthSoilP_dWat ! derivative of prime enthalpy of the soil with water state - real(rkind) :: dEnthLiqP_dWat ! derivative of prime enthalpy of the liquid with water state - real(rkind) :: dEnthIceP_dWat ! derivative of prime enthalpy of the ice with water state - real(rkind) :: dEnthAirP_dWat ! derivative of prime enthalpy of the air with water state - real(rkind) :: dEnthWaterP_dWat ! derivative of prime enthalpy of the total water with water state - real(rkind) :: dEnthVegP_dTkP ! derivative of prime enthalpy of the vegetation with prime temperature - real(rkind) :: dEnthSoilP_dTkP ! derivative of prime enthalpy of the soil with prime temperature - real(rkind) :: dEnthLiqP_dTkP ! derivative of prime enthalpy of the liquid with prime temperature - real(rkind) :: dEnthIceP_dTkP ! derivative of prime enthalpy of the ice with prime temperature - real(rkind) :: dEnthAirP_dTkP ! derivative of prime enthalpy of the air with prime temperature - real(rkind) :: dEnthWaterP_dTkP ! derivative of prime enthalpy of the total water with prime temperature - real(rkind) :: dEnthVegP_dWatP ! derivative of prime enthalpy of the vegetation with prime water state - real(rkind) :: dEnthSoilP_dWatP ! derivative of prime enthalpy of the soil with prime water state - real(rkind) :: dEnthLiqP_dWatP ! derivative of prime enthalpy of the liquid with prime water state - real(rkind) :: dEnthIceP_dWatP ! derivative of prime enthalpy of the ice with prime water state - real(rkind) :: dEnthAirP_dWatP ! derivative of prime enthalpy of the air with prime water state - real(rkind) :: dEnthWaterP_dWatP ! derivative of prime enthalpy of the total water with prime water state - ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures generalVars: associate(& @@ -268,47 +211,18 @@ subroutine t2enthalpyPrime(& diffT = scalarCanopyTempTrial - Tfreeze enthVegP = specificHeatVeg * maxMassVegetation * scalarCanopyTempPrime / canopyDepth - ! enthalpy prime derivatives - dEnthVegP_dTk = specificHeatVeg * maxMassVegetation / canopyDepth - dEnthVegP_dWat = 0._rkind - dEnthVegP_dTkP = 0._rkind - dEnthVegP_dWatP = 0._rkind if(diffT>=0._rkind)then enthLiqP = Cp_water * ( scalarCanopyWatPrime * diffT + scalarCanopyWatTrial * scalarCanopyTempPrime )/ canopyDepth enthIceP = 0._rkind - ! enthalpy prime derivatives - dEnthLiqP_dTk = Cp_water * scalarCanopyWatPrime / canopyDepth - dEnthLiqP_dWat = Cp_water * scalarCanopyTempPrime / canopyDepth - dEnthLiqP_dTkP = Cp_water * scalarCanopyWatTrial / canopyDepth - dEnthLiqP_dWatP = Cp_water * diffT / canopyDepth - dEnthIceP_dTk = 0._rkind - dEnthIceP_dWat = 0._rkind - dEnthIceP_dTkP = 0._rkind - dEnthIceP_dWatP = 0._rkind else integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) ! Note: VolFracLiq = d_integral_dTk*VolFracWat enthLiqP = Cp_water * ( scalarCanopyWatPrime*integral + scalarCanopyWatTrial*scalarCanopyTempPrime*d_integral_dTk )/ canopyDepth enthIceP = Cp_ice * ( scalarCanopyWatPrime*( diffT - integral ) + scalarCanopyWatTrial*scalarCanopyTempPrime*( 1._rkind - d_integral_dTk ) ) / canopyDepth - ! derivatives - d2_integral_dTk2 = -2._rkind*snowfrz_scale*diffT / (1._rkind + (snowfrz_scale * diffT)**2_i4b)**2_i4b - ! enthalpy prime derivatives - dEnthLiqP_dTk = Cp_water * ( scalarCanopyWatPrime*d_integral_dTk + scalarCanopyWatTrial*scalarCanopyTempPrime*d2_integral_dTk2 ) / canopyDepth - dEnthLiqP_dWat = Cp_water * scalarCanopyTempPrime * d_integral_dTk / canopyDepth - dEnthLiqP_dTkP = Cp_water * scalarCanopyWatTrial * d_integral_dTk / canopyDepth - dEnthLiqP_dWatP = Cp_water * integral / canopyDepth - dEnthIceP_dTk = Cp_ice * ( scalarCanopyWatPrime*( 1._rkind - d_integral_dTk ) - scalarCanopyWatTrial*scalarCanopyTempPrime*d2_integral_dTk2 ) / canopyDepth - dEnthIceP_dWat = Cp_ice * ( scalarCanopyTempPrime*( 1._rkind - d_integral_dTk ) ) / canopyDepth - dEnthIceP_dTkP = Cp_ice * scalarCanopyWatTrial*( 1._rkind - d_integral_dTk ) / canopyDepth - dEnthIceP_dWatP = Cp_ice * ( diffT - integral ) / canopyDepth endif scalarCanopyEnthalpyPrime = enthVegP + enthLiqP + enthIceP - dCanEnthalpyPrime_dTk = dEnthVegP_dTk + dEnthLiqP_dTk + dEnthIceP_dTk - dCanEnthalpyPrime_dWat = dEnthVegP_dWat + dEnthLiqP_dWat + dEnthIceP_dWat - dCanEnthalpyPrime_dTkPrime = dEnthVegP_dTkP + dEnthLiqP_dTkP + dEnthIceP_dTkP - dCanEnthalpyPrime_dWatPrime = dEnthVegP_dWatP + dEnthLiqP_dWatP + dEnthIceP_dWatP end associate vegVars @@ -326,28 +240,8 @@ subroutine t2enthalpyPrime(& enthIceP = iden_water * Cp_ice * ( mLayerVolFracWatPrime(iLayer)*( diffT - integral ) + mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer)*( 1._rkind - d_integral_dTk ) ) enthAirP = iden_air * Cp_air * ( mLayerTempPrime(iLayer) - mLayerVolFracWatPrime(iLayer) * ( (iden_water/iden_ice)*( diffT - integral ) + integral ) & - mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer)* ( (iden_water/iden_ice)*( 1._rkind - d_integral_dTk ) + d_integral_dTk ) ) - ! derivatives - d2_integral_dTk2 = -2._rkind*snowfrz_scale*diffT / (1._rkind + (snowfrz_scale * diffT)**2_i4b)**2_i4b - ! enthalpy prime derivatives - dEnthLiqP_dTk = iden_water * Cp_water * ( mLayerVolFracWatPrime(iLayer)*d_integral_dTk + mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer)*d2_integral_dTk2 ) - dEnthLiqP_dWat = iden_water * Cp_water * mLayerTempPrime(iLayer) * d_integral_dTk - dEnthLiqP_dTkP = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * d_integral_dTk - dEnthLiqP_dWatP = iden_water * Cp_water * integral - dEnthIceP_dTk = iden_water * Cp_ice * ( mLayerVolFracWatPrime(iLayer)*( 1._rkind - d_integral_dTk ) - mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer)*d2_integral_dTk2 ) - dEnthIceP_dWat = iden_water * Cp_ice * ( mLayerTempPrime(iLayer)*( 1._rkind - d_integral_dTk ) ) - dEnthIceP_dTkP = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer)*( 1._rkind - d_integral_dTk ) - dEnthIceP_dWatP = iden_water * Cp_ice * ( diffT - integral ) - dEnthAirP_dTk = iden_air * Cp_air * ( -mLayerVolFracWatPrime(iLayer) * ( (iden_water/iden_ice)*( 1._rkind - d_integral_dTk ) + d_integral_dTk ) & - - mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer) * (-(iden_water/iden_ice)*d2_integral_dTk2 + d2_integral_dTk2 ) ) - dEnthAirP_dWat = -iden_air * Cp_air * ( mLayerTempPrime(iLayer) * (iden_water/iden_ice)*( 1._rkind - d_integral_dTk ) + d_integral_dTk ) - dEnthAirP_dTkP = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer)* ( (iden_water/iden_ice)*( 1._rkind - d_integral_dTk ) + d_integral_dTk ) ) - dEnthAirP_dWatP =- iden_air * Cp_air * ( (iden_water/iden_ice)*( diffT - integral ) + integral ) mLayerEnthalpyPrime(iLayer) = enthLiqP + enthIceP + enthAirP - dEnthalpyPrime_dTk(iLayer) = dEnthLiqP_dTk + dEnthIceP_dTk + dEnthAirP_dTk - dEnthalpyPrime_dWat(iLayer) = dEnthLiqP_dWat + dEnthIceP_dWat + dEnthAirP_dWat - dEnthalpyPrime_dTkPrime(iLayer) = dEnthLiqP_dTkP + dEnthIceP_dTkP + dEnthAirP_dTkP - dEnthalpyPrime_dWatPrime(iLayer) = dEnthLiqP_dWatP + dEnthIceP_dWatP + dEnthAirP_dWatP end associate snowVars @@ -363,11 +257,6 @@ subroutine t2enthalpyPrime(& vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) - ! associate values in the lookup table - Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) - E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function - ) ! end associate statement ! diagnostic variables @@ -381,56 +270,60 @@ subroutine t2enthalpyPrime(& ! *** compute enthalpy prime of water for unfrozen conditions if(mlayerTempTrial(iLayer)>=Tcrit)then enthWaterP = iden_water * Cp_water * ( mLayerMatricHeadPrime(ixControlIndex)*dVolTot_dPsi0(ixControlIndex)*diffT + volFracWat*mLayerTempPrime(iLayer) ) ! valid for temperatures below freezing also - ! enthalpy derivatives - dEnthWaterP_dTk = iden_water * Cp_water * mLayerMatricHeadPrime(ixControlIndex) * dVolTot_dPsi0(ixControlIndex) ! dVolFracWat_dWat = dVolTot_dPsi0(ixControlIndex) - dEnthWaterP_dWat = iden_water * Cp_water * ( mLayerMatricHeadPrime(ixControlIndex)*d2VolTot_dPsi02(ixControlIndex)*diffT & - + dVolTot_dPsi0(ixControlIndex)*mLayerTempPrime(iLayer) ) - dEnthWaterP_dTkP = iden_water * Cp_water * volFracWat - dEnthWaterP_dWatP = iden_water * Cp_water * dVolTot_dPsi0(ixControlIndex)*diffT ! *** compute enthalpy prime of water for frozen conditions else - ! calculate enthalpy prime at the temperature (cubic spline interpolation) - call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),enthTemp,dE,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! calculate enthalpy prime at the critical temperature (cubic spline interpolation) - call splint(Tk,Ey,E2,Tcrit,enthTcrit,dEcrit,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! calculate the enthalpy prime of water - enthMixP = mlayerTempPrime(iLayer)*dE - mLayerMatricHeadPrime(ixControlIndex)*dEcrit ! enthalpy prime of the liquid+ice mix - enthLiqP = iden_water * Cp_water * mLayerMatricHeadPrime(ixControlIndex) * ( dVolTot_dPsi0(ixControlIndex) * (Tcrit - Tfreeze) + volFracWat * dTcrit_dPsi0 ) - enthWaterP = enthMixP + enthLiqP - ! enthalpy derivatives - dEnthWaterP_dTk = 0._rkind ! mlayerTempPrime(iLayer)*dE_dTk - mLayerMatricHeadPrime(ixControlIndex)*dEcrit_dTk, but derivatives of dE and dEcrit are zero - dEnthWaterP_dWat = 0._rkind ! -mLayerMatricHeadPrime(ixControlIndex)*dEcrit_dTk*dTcrit_dPsi0, but derivatives of dEcrit are zero - dEnthWaterP_dTkP = dE - dEnthWaterP_dWatP = -dEcrit + ! compute enthalpy of unfrozen and frozen water + enthLiqP = iden_water * Cp_water * ( mLayerMatricHeadPrime(ixControlIndex)*dVolTot_dPsi0(ixControlIndex)*(Tcrit - Tfreeze) + volFracWat*dTcrit_dPsi0 ) + enthIceP = iden_ice * Cp_ice * ( mLayerMatricHeadPrime(ixControlIndex)*dVolTot_dPsi0(ixControlIndex)*(mLayerTempTrial(iLayer) - Tcrit) + volFracWat*(mLayerTempPrime(iLayer) - dTcrit_dPsi0) ) + + ! compute enthalpy of the mixed region, liquid+ice + arg = (-vGn_alpha * mLayerMatricHeadLiqTrial(ixControlIndex))**vGn_n + gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + enthTemp = (iden_water * Cp_water - iden_ice * Cp_ice) * ( diffT * ( (theta_sat - theta_res)*gauss_hg_T ) + mLayerTempPrime(iLayer)* ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) ) + arg = (-vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n + gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + enthTcrit = (iden_water * Cp_water - iden_ice * Cp_ice) * diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + + endif ! (if frozen conditions) - ! *** compute the enthalpy of soil enthSoilP = soil_dens_intr * Cp_soil * (1._rkind - theta_sat)*mlayerTempPrime(iLayer) - ! enthalpy derivatives - dEnthSoilP_dTk = 0._rkind - dEnthSoilP_dWat = 0._rkind - dEnthSoilP_dTkP = soil_dens_intr * Cp_soil * (1._rkind - theta_sat) - dEnthSoilP_dWatP = 0._rkind - - ! *** compute the enthalpy of air enthAirP = iden_air * Cp_air * ( mLayerMatricHeadPrime(ixControlIndex)*dVolTot_dPsi0(ixControlIndex)*diffT + (1._rkind - theta_sat - volFracWat)*mlayerTempPrime(iLayer) ) - ! enthalpy derivatives - dEnthAirP_dTk = iden_air * Cp_air * mLayerMatricHeadPrime(ixControlIndex) * dVolTot_dPsi0(ixControlIndex) - dEnthAirP_dWat = iden_air * Cp_air * ( mLayerMatricHeadPrime(ixControlIndex)*d2VolTot_dPsi02(ixControlIndex)*diffT - dVolTot_dPsi0(ixControlIndex)*mlayerTempPrime(iLayer) ) - dEnthAirP_dTkP = iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) - dEnthAirP_dWatP = iden_air * Cp_air * dVolTot_dPsi0(ixControlIndex) * diffT ! *** compute the total enthalpy (J m-3) mLayerEnthalpyPrime(iLayer) = enthWaterP + enthSoilP + enthAirP - dEnthalpyPrime_dTk(iLayer) = dEnthWaterP_dTk + dEnthSoilP_dTk + dEnthAirP_dTk - dEnthalpyPrime_dWat(iLayer) = dEnthWaterP_dWat + dEnthSoilP_dWat + dEnthAirP_dWat - dEnthalpyPrime_dTkPrime(iLayer) = dEnthWaterP_dTkP + dEnthSoilP_dTkP + dEnthAirP_dTkP - dEnthalpyPrime_dWatPrime(iLayer) = dEnthWaterP_dWatP + dEnthSoilP_dWatP + dEnthAirP_dWatP + + + + + + + + ! compute enthalpy of the mixed region, liquid+ice + arg = (-vGn_alpha * mLayerMatricHeadLiqTrial(ixControlIndex))**vGn_n + gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + enthTemp = (iden_water * Cp_water - iden_ice * Cp_ice) * diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) & + arg = (-vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n + gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + enthTcrit = (iden_water * Cp_water - iden_ice * Cp_ice) * diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) & + + ! calculate the enthalpy of water + enthMix = enthTemp - enthTcrit ! enthalpy of the liquid+ice mix + enthWater = enthMix + enthLiq + enthIce + + endif ! (if frozen conditions) + + enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*diffT + enthAir = iden_air*Cp_air*(1._rkind - theta_sat - volFracWat)*diffT + + mLayerEnthalpy(iLayer) = enthWater + enthSoil + enthAir + + + + + + end associate soilVars diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 490c2263a..07b0b0913 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -47,7 +47,6 @@ module varSubstep_module var_flagVec, & ! data vector with variable length dimension (i4b) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! lookup tables model_options, & ! defines the model decisions in_type_varSubstep, & ! class for intent(in) arguments io_type_varSubstep, & ! class for intent(inout) arguments @@ -104,7 +103,6 @@ subroutine varSubstep(& io_varSubstep, & ! intent(inout) : model control ! input/output: data structures model_decisions, & ! intent(in) : model decisions - lookup_data, & ! intent(in) : lookup tables type_data, & ! intent(in) : type of vegetation and soil attr_data, & ! intent(in) : spatial attributes forc_data, & ! intent(in) : model forcing data @@ -136,7 +134,6 @@ subroutine varSubstep(& type(io_type_varSubstep),intent(inout) :: io_varSubstep ! model control ! input/output: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(zLookup),intent(in) :: lookup_data ! lookup tables type(var_i),intent(in) :: type_data ! type of vegetation and soil type(var_d),intent(in) :: attr_data ! spatial attributes type(var_d),intent(in) :: forc_data ! model forcing data @@ -347,7 +344,6 @@ subroutine varSubstep(& checkMassBalance, & ! intent(in): flag to check mass balance checkNrgBalance, & ! intent(in): flag to check energy balance ! input/output: data structures - lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes forc_data, & ! intent(in): model forcing data @@ -441,7 +437,7 @@ subroutine varSubstep(& ! update prognostic variables, update balances, and check them for possible step reduction if numrec or kinsol call updateProg(dtSubstep,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states doAdjustTemp,computeVegFlux,checkMassBalance, checkNrgBalance,ixHowHeatCap == enthalpyFD, & ! input: model control - model_decisions,lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures + model_decisions,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! output: balances, flags, and error control if(err/=0)then message=trim(message)//trim(cmessage) @@ -616,10 +612,10 @@ end subroutine varSubstep ! ********************************************************************************************************** ! private subroutine updateProg: update prognostic variables ! ********************************************************************************************************** -subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states - doAdjustTemp,computeVegFlux,checkMassBalance, checkNrgBalance,use_enthalpyFD, & ! input: model control - model_decisions,lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures - fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! input-output: balances, flags, and error control +subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states + doAdjustTemp,computeVegFlux,checkMassBalance, checkNrgBalance,use_enthalpyFD, & ! input: model control + model_decisions,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures + fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! input-output: balances, flags, and error control USE getVectorz_module,only:varExtract ! extract variables from the state vector #ifdef SUNDIALS_ACTIVE USE updateVarsWithPrime_module,only:updateVarsWithPrime ! update prognostic variables @@ -643,7 +639,6 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec logical(lgt) ,intent(in) :: use_enthalpyFD ! flag that using enthalpy finite difference and need to update ! data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(zLookup),intent(in) :: lookup_data ! lookup tables type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! indices for a local HRU type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU @@ -710,13 +705,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec real(rkind) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector for volumetric fraction of liquid water (-) real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector for volumetric fraction of ice (-) - ! enthalpy derivatives - real(rkind) :: dCanEnthalpy_dTk_unused ! will not be used, derivatives in canopy enthalpy w.r.t. temperature - real(rkind) :: dCanEnthalpy_dWat_unused ! will not be used, derivatives in canopy enthalpy w.r.t. water state - real(rkind),dimension(nLayers) :: dEnthalpy_dTk_unused ! will not be used, derivatives in layer enthalpy w.r.t. temperature - real(rkind),dimension(nLayers) :: dEnthalpy_dWat_unused ! will not be used, derivatives in layer enthalpy w.r.t. water state ! ------------------------------------------------------------------------------------------------------------------- - ! ------------------------------------------------------------------------------------------------------------------- ! point to flux variables in the data structure associate(& @@ -773,8 +762,6 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-3) mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(inout): [dp(:)] enthalpy of the snow+soil layers (J m-3) - ! derivatives, diagnositic for enthalpy - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential ! model state variables (aquifer) scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(inout): [dp(:)] storage of water in the aquifer (m) ! error tolerance @@ -961,25 +948,18 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure ! input: state variables for the vegetation canopy scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - ! input: variables for the snow-soil domain + ! input: variables for the snow-soil domain mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - ! input: pre-computed derivatives - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) - dCanEnthalpy_dTk_unused, & ! intent(out): will not be used, derivatives in canopy enthalpy w.r.t. temperature - dCanEnthalpy_dWat_unused, & ! intent(out): will not be used, derivatives in canopy enthalpy w.r.t. water state - dEnthalpy_dTk_unused, & ! intent(out): will not be used, derivatives in layer enthalpy w.r.t. temperature - dEnthalpy_dWat_unused, & ! intent(out): will not be used, derivatives in layer enthalpy w.r.t. water state ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif diff --git a/build/source/netcdf/def_output.f90 b/build/source/netcdf/def_output.f90 index 7305e0c46..e648d482b 100644 --- a/build/source/netcdf/def_output.f90 +++ b/build/source/netcdf/def_output.f90 @@ -165,8 +165,7 @@ subroutine def_output(summaVersion,buildTime,gitBranch,gitHash,nGRU,nHRU,nSoil,i case('diag' ); call def_variab(ncid(iFreq),iFreq,needHRU,needTime,diag_meta, outputPrecision, err,cmessage) ! model diagnostic variables case('flux' ); call def_variab(ncid(iFreq),iFreq,needHRU,needTime,flux_meta, outputPrecision, err,cmessage) ! model fluxes case('bvar' ); call def_variab(ncid(iFreq),iFreq,needGRU,needTime,bvar_meta, outputPrecision, err,cmessage) ! basin-average variables - case('id' ); cycle ! ids -- see write_hru_info() - case('lookup'); cycle ! ids -- see write_hru_info() + case('id' ); cycle ! ids -- see write_hru_info() ! ids -- see write_hru_info() case default; err=20; message=trim(message)//'unable to identify lookup structure'; end select ! error handling From 75df7a60ba9bca19edc179e63b2f6ed21e6f1de5 Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 5 Dec 2023 05:30:17 -0600 Subject: [PATCH 1014/1472] Created split_select_type class in data_types module for operator splitting selection. --- build/source/dshare/data_types.f90 | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 97607e7b3..7d1279531 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -573,6 +573,9 @@ MODULE data_types end type out_type_bigAquifer ! ** end bigAquifer + ! *********************************************************************************************************** + ! Define classes used to simplify calls to the subrotuines in opSplittin + ! *********************************************************************************************************** ! ** stateFilter type, public :: in_type_stateFilter ! class for intent(in) arguments in stateFilter call integer(i4b) :: ixCoupling ! intent(in): index of coupling method (1,2) @@ -652,6 +655,17 @@ MODULE data_types end type out_type_varSubstep ! ** end varSubstep + ! *********************************************************************************************************** + ! Define classes used define operator splitting methods in opSplittin + ! *********************************************************************************************************** + + type, public :: split_select_type ! class for selecting operator splitting methods + type(var_flagVec) :: fluxMask ! integer mask defining model fluxes + logical(lgt),allocatable :: stateMask(:) ! mask defining desired state variables + contains + !procedure :: initialize => initialize_split_select ! currently under development in opSplittin + end type split_select_type + contains ! **** vegNrgFlux **** From f813fe42a867a3aae432858dbb47441f16b022ff Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 5 Dec 2023 05:34:16 -0600 Subject: [PATCH 1015/1472] Added split_select_type class to opSplittin. --- build/source/engine/opSplittin.f90 | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 99dbdf826..06cbc5102 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -90,14 +90,15 @@ module opSplittin_module USE data_types,only:& var_i, & ! data vector (i4b) var_d, & ! data vector (rkind) - var_flagVec, & ! data vector with variable length dimension (i4b) + var_flagVec, & ! data vector with variable length dimension (lgt) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (rkind) zLookup, & ! lookup tables model_options, & ! defines the model decisions in_type_statefilter,out_type_statefilter, & ! classes for stateFilter objects in_type_indexSplit,out_type_indexSplit, & ! classes for indexSplit objects - in_type_varSubstep,io_type_varSubstep,out_type_varSubstep ! classes for varSubstep objects + in_type_varSubstep,io_type_varSubstep,out_type_varSubstep, & ! classes for varSubstep objects + split_select_type ! classs for selecting operator splitting methods ! look-up values for the numerical method USE mDecisions_module,only: & @@ -300,7 +301,8 @@ subroutine opSplittin(& type(in_type_stateFilter) :: in_stateFilter; type(out_type_stateFilter) :: out_stateFilter; ! stateFilter arguments type(in_type_indexSplit) :: in_indexSplit; type(out_type_indexSplit) :: out_indexSplit; ! indexSplit arguments type(in_type_varSubstep) :: in_varSubstep; type(io_type_varSubstep) :: io_varSubstep; type(out_type_varSubstep) :: out_varSubstep; ! varSubstep arguments - ! --------------------------------------------------------------------------------------- + ! ------------------------------------------------------------------------------------------------------------------------- + type(split_select_type),allocatable :: split_select(:) ! class object for selecting operator splitting methods call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs coupling: do ixCoupling=1,nCoupling ! loop through different coupling strategies From e6a73d95c37ddb5fb159785bd3228de88868fba7 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 6 Dec 2023 07:16:05 -0600 Subject: [PATCH 1016/1472] Added subroutines in the contains block of opSplittin (e.g., get_nStateSplit) for integer quantities related to operator splitting. --- build/source/engine/opSplittin.f90 | 219 +++++++++++++++++------------ 1 file changed, 131 insertions(+), 88 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 06cbc5102..407cdd7a0 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -370,72 +370,82 @@ subroutine opSplittin(& subroutine initialize_split_select ! *** Initialize split_select class object - currently under development *** - call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs - coupling: do ixCoupling=1,nCoupling ! loop through different coupling strategies + integer(i4b) :: nSplit - call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs - stateTypeSplitting: do iStateTypeSplit=1,nStateTypeSplit ! state splitting loop - - ! first try the state type split, then try the domain split within a given state type - call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split - stateThenDomain: do ixStateThenDomain=1,1+tryDomainSplit ! 1=state type split; 2=domain split within a given state type - - call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs - domainSplit: do iDomainSplit=1,nDomainSplit ! domain splitting loop - - solution: do ixSolution=1,nSolutions ! trial with the vector then scalar solution - - call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs - stateSplit: do iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) - -!!! Draft -- assign data components to class object -! possible class object for handling split selection -!split_select(ixCoupling,iStateTypeSplit,ixStateThenDomain,iDomainSplit,ixSolution,iStateSplit) -!!! End Draft - -! ! define state subsets for a given split... -! call update_stateFilter; if (return_flag.eqv..true.) return ! get the mask for the state subset - return for a non-zero error code -! call validate_split ! verify that the split is valid -! if (cycle_domainSplit) cycle domainSplit -! if (cycle_solution) cycle solution -! if (return_flag.eqv..true.) return ! return for a non-zero error code -! call save_recover ! save/recover copies of variables and fluxes - -! ! assemble vectors for a given split... -! call get_split_indices; if (return_flag.eqv..true.) return ! get indices for a given split - return for a non-zero error code -! call update_fluxMask; if (return_flag.eqv..true.) return ! define the mask of the fluxes used - return for a non-zero error code - -! call solve_subset; if (return_flag.eqv..true.) return ! solve variable subset for one time step - return for a positive error code - -! call assess_solution; if (return_flag.eqv..true.) return ! is solution a success or failure? - return for a recovering solution - -! call try_other_solution_methods ! if solution failed to converge, try other splitting methods -! if (cycle_coupling) cycle coupling ! exit loops if necessary -! if (cycle_stateThenDomain) cycle stateThenDomain -! if (cycle_solution) cycle solution - -! call confirm_variable_updates; if (return_flag.eqv..true.) return ! check that state variables updated - return if error - -! call success_check ! check for success -! if (exit_stateThenDomain) exit stateThenDomain ! exit loops if necessary -! if (exit_solution) exit solution -! if (return_flag.eqv..true.) return ! return if error - - end do stateSplit ! solution with split layers - - end do solution ! trial with the full layer solution then the split layer solution - call finalize_solution ! final steps following solution loop - - end do domainSplit ! domain type splitting loop - - end do stateThenDomain ! switch between the state type and domain type splitting - call finalize_stateThenDomain ! final steps following the stateThenDomain loop - - end do stateTypeSplitting ! state type splitting loop - call finalize_stateTypeSplitting; if (exit_coupling) exit coupling ! success = exit the coupling loop - - end do coupling ! loop over coupling methods - call finalize_coupling ! check variables and fluxes, and apply step halving if needed + ! determine total number of possible splits + return_flag=.false. ! initialize flag + call get_nCoupling; if (return_flag.eqv..true.) return ! get nCoupling value + call get_nStateTypeSplit_tryDomainSplit(nCoupling); if (return_flag.eqv..true.) return ! get nStateTypeSplit and tryDomainSplit values + call get_nDomainSplit(1+tryDomainSplit); if (return_flag.eqv..true.) return ! get nDomainSplit value + nSplit=nCoupling*nStateTypeSplit*(1+tryDomainSplit)*nDomainSplit*nSolutions*nStateSplit + +! ! opSplittin loop structure +! call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs +! coupling: do ixCoupling=1,nCoupling ! loop through different coupling strategies +! +! call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs +! stateTypeSplitting: do iStateTypeSplit=1,nStateTypeSplit ! state splitting loop +! +! ! first try the state type split, then try the domain split within a given state type +! call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split +! stateThenDomain: do ixStateThenDomain=1,1+tryDomainSplit ! 1=state type split; 2=domain split within a given state type +! +! call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs +! domainSplit: do iDomainSplit=1,nDomainSplit ! domain splitting loop +! +! solution: do ixSolution=1,nSolutions ! trial with the vector then scalar solution +! +! call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs +! stateSplit: do iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) +! +!!!! Draft -- assign data components to class object +!! possible class object for handling split selection +!!split_select(ixCoupling,iStateTypeSplit,ixStateThenDomain,iDomainSplit,ixSolution,iStateSplit) +!!!! End Draft +! +!! ! define state subsets for a given split... +!! call update_stateFilter; if (return_flag.eqv..true.) return ! get the mask for the state subset - return for a non-zero error code +!! call validate_split ! verify that the split is valid +!! if (cycle_domainSplit) cycle domainSplit +!! if (cycle_solution) cycle solution +!! if (return_flag.eqv..true.) return ! return for a non-zero error code +!! call save_recover ! save/recover copies of variables and fluxes +! +!! ! assemble vectors for a given split... +!! call get_split_indices; if (return_flag.eqv..true.) return ! get indices for a given split - return for a non-zero error code +!! call update_fluxMask; if (return_flag.eqv..true.) return ! define the mask of the fluxes used - return for a non-zero error code +! +!! call solve_subset; if (return_flag.eqv..true.) return ! solve variable subset for one time step - return for a positive error code +! +!! call assess_solution; if (return_flag.eqv..true.) return ! is solution a success or failure? - return for a recovering solution +! +!! call try_other_solution_methods ! if solution failed to converge, try other splitting methods +!! if (cycle_coupling) cycle coupling ! exit loops if necessary +!! if (cycle_stateThenDomain) cycle stateThenDomain +!! if (cycle_solution) cycle solution +! +!! call confirm_variable_updates; if (return_flag.eqv..true.) return ! check that state variables updated - return if error +! +!! call success_check ! check for success +!! if (exit_stateThenDomain) exit stateThenDomain ! exit loops if necessary +!! if (exit_solution) exit solution +!! if (return_flag.eqv..true.) return ! return if error +! +! end do stateSplit ! solution with split layers +! +! end do solution ! trial with the full layer solution then the split layer solution +! call finalize_solution ! final steps following solution loop +! +! end do domainSplit ! domain type splitting loop +! +! end do stateThenDomain ! switch between the state type and domain type splitting +! call finalize_stateThenDomain ! final steps following the stateThenDomain loop +! +! end do stateTypeSplitting ! state type splitting loop +! call finalize_stateTypeSplitting; if (exit_coupling) exit coupling ! success = exit the coupling loop +! +! end do coupling ! loop over coupling methods +! call finalize_coupling ! check variables and fluxes, and apply step halving if needed end subroutine initialize_split_select @@ -444,13 +454,7 @@ subroutine initialize_coupling ! initialize error control err=0; message="opSplittin/" - associate(ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision) ! intent(in): [i4b] choice of numerical solver - ! we just solve the fully coupled problem if IDA for now, splitting can happen otherwise - select case(ixNumericalMethod) - case(ida); nCoupling = 1 - case(kinsol, numrec); nCoupling = 2 - end select - end associate + call get_nCoupling; if (return_flag.eqv..true.) return ! get nCoupling value -- return if error ! set the global print flag globalPrintFlag=.false. @@ -497,6 +501,18 @@ subroutine initialize_coupling end do end subroutine initialize_coupling + subroutine get_nCoupling + ! *** Get nCoupling value *** + associate(ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision) ! intent(in): [i4b] choice of numerical solver + ! we just solve the fully coupled problem if IDA for now, splitting can happen otherwise + select case(ixNumericalMethod) + case(ida); nCoupling = 1 + case(kinsol, numrec); nCoupling = 2 + case default; err=20; message=trim(message)//'solver choice not found'; return_flag=.true.; return + end select + end associate + end subroutine get_nCoupling + subroutine allocate_memory ! *** allocate memory for local structures *** return_flag=.false. ! initialize flag @@ -562,28 +578,35 @@ subroutine initialize_stateTypeSplitting dtInit = min(merge(dt, dtmin_coupled, ixCoupling==fullyCoupled), dt) ! initial time step dt_min = min(merge(dtmin_coupled, dtmin_split, ixCoupling==fullyCoupled), dt) ! minimum time step + ! get nStateTypeSplit and tryDomainSplit values + call get_nStateTypeSplit_tryDomainSplit(ixCoupling); if (return_flag.eqv..true.) return + + mean_step_dt = 0._rkind ! initialize mean step for the time step + addFirstFlux = .true. ! flag to add the first flux to the mask + end subroutine initialize_stateTypeSplitting + + subroutine get_nStateTypeSplit_tryDomainSplit(ixCoupling_value) + ! *** Get nStateTypeSplit and tryDomainSplit values *** + integer(i4b),intent(in) :: ixCoupling_value ! keep track of the number of state splits associate(numberStateSplit => indx_data%var(iLookINDEX%numberStateSplit)%dat(1)) ! intent(inout): [i4b] number of state splitting solutions if (ixCoupling/=fullyCoupled) numberStateSplit = numberStateSplit + 1 end associate ! define the number of operator splits for the state type - select case(ixCoupling) + select case(ixCoupling_value) case(fullyCoupled); nStateTypeSplit=1 case(stateTypeSplit); nStateTypeSplit=nStateTypes case default; err=20; message=trim(message)//'coupling case not found'; return_flag=.true.; return end select ! operator splitting option ! define if we wish to try the domain split - select case(ixCoupling) + select case(ixCoupling_value) case(fullyCoupled); tryDomainSplit=0 case(stateTypeSplit); tryDomainSplit=1 case default; err=20; message=trim(message)//'coupling case not found'; return_flag=.true.; return end select ! operator splitting option - - mean_step_dt = 0._rkind ! initialize mean step for the time step - addFirstFlux = .true. ! flag to add the first flux to the mask - end subroutine initialize_stateTypeSplitting + end subroutine get_nStateTypeSplit_tryDomainSplit subroutine finalize_stateTypeSplitting ! *** Final operations subsequent to the stateTypeSplitting loop *** @@ -644,14 +667,7 @@ subroutine initialize_domainSplit if (iStateTypeSplit==massSplit .and. ixStateThenDomain==subDomain) numberDomainSplitMass = numberDomainSplitMass + 1 end associate - ! define the number of domain splits for the state type - select case(ixStateThenDomain) - case(fullDomain); nDomainSplit=1 - case(subDomain); nDomainSplit=nDomains - case default; err=20; message=trim(message)//'coupling case not found'; - return_flag=.true. ! return statement required in opSplittin - return - end select + call get_nDomainSplit(ixStateThenDomain); if (return_flag.eqv..true.) return ! get nDomainSplit value -- return if error occurs ! check that we haven't split the domain when we are fully coupled if (ixCoupling==fullyCoupled .and. nDomainSplit==nDomains) then @@ -663,6 +679,19 @@ subroutine initialize_domainSplit mean_step_state = 0._rkind ! initialize mean step for state end subroutine initialize_domainSplit + subroutine get_nDomainSplit(ixStateThenDomain_value) + ! *** Get nDomainSplit value *** + integer(i4b),intent(in) :: ixStateThenDomain_value + ! define the number of domain splits for the state type + select case(ixStateThenDomain_value) + case(fullDomain); nDomainSplit=1 + case(subDomain); nDomainSplit=nDomains + case default; err=20; message=trim(message)//'coupling case not found'; + return_flag=.true. ! return statement required in opSplittin + return + end select + end subroutine get_nDomainSplit + subroutine finalize_solution ! *** final operations following solution loop *** ! sum the mean steps for the state over each domain split @@ -687,15 +716,29 @@ subroutine initialize_stateSplit firstFluxCall=.true. if (.not.firstInnerStep) firstFluxCall=.false. + call get_nStateSplit(ixSolution); if (return_flag.eqv..true.) return ! get nStateSplit value -- return if error occurs +! ! get the number of split layers +! select case(ixSolution) +! case(vector); nStateSplit=1 +! case(scalar); nStateSplit=count(stateMask) +! case default; err=20; message=trim(message)//'unknown solution method'; +! return_flag=.true. ! return statement required in opSplittin +! return +! end select + end subroutine initialize_stateSplit + + subroutine get_nStateSplit(ixSolution_value) + ! *** Get nStateSplit value *** + integer(i4b),intent(in) :: ixSolution_value ! get the number of split layers - select case(ixSolution) + select case(ixSolution_value) case(vector); nStateSplit=1 case(scalar); nStateSplit=count(stateMask) case default; err=20; message=trim(message)//'unknown solution method'; return_flag=.true. ! return statement required in opSplittin return end select - end subroutine initialize_stateSplit + end subroutine get_nStateSplit ! **** stateFilter **** subroutine initialize_stateFilter From 7e074349ff21f8dfa01423cbdd7763da85fd17d5 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 7 Dec 2023 12:25:02 +0900 Subject: [PATCH 1017/1472] lookup tables with a flag right now, hypergeometric also. stub flag for different hypergeo methods. HyperGeo not currently converging, lookup slow. No enthalpy derivatives on RHS since terms should cancel. --- build/cmake/CMakeLists.txt | 5 +- build/cmake/build.mac.bash | 2 +- build/source/driver/summa_init.f90 | 2 + build/source/driver/summa_modelRun.f90 | 4 + build/source/driver/summa_setup.f90 | 20 ++ build/source/driver/summa_type.f90 | 7 +- build/source/dshare/data_types.f90 | 21 ++ build/source/dshare/get_ixname.f90 | 6 +- build/source/dshare/globalData.f90 | 5 +- build/source/dshare/popMetadat.f90 | 8 + build/source/dshare/type4ida.f90 | 1 + build/source/dshare/type4kinsol.f90 | 1 + build/source/dshare/var_lookup.f90 | 11 + build/source/engine/allocspace.f90 | 4 +- build/source/engine/checkStruc.f90 | 4 + build/source/engine/computHeatCap.f90 | 42 +-- .../source/engine/computHeatCapWithPrime.f90 | 262 ---------------- build/source/engine/convE2Temp.f90 | 231 ++++++++++++++ build/source/engine/coupled_em.f90 | 6 +- build/source/engine/eval8summa.f90 | 16 +- build/source/engine/eval8summaWithPrime.f90 | 11 +- build/source/engine/opSplittin.f90 | 5 +- build/source/engine/run_oneGRU.f90 | 7 +- build/source/engine/run_oneHRU.f90 | 6 +- build/source/engine/spline_int.f90 | 161 ++++++++++ build/source/engine/summaSolve4ida.f90 | 13 +- build/source/engine/summaSolve4kinsol.f90 | 4 + build/source/engine/summaSolve4numrec.f90 | 4 + build/source/engine/systemSolv.f90 | 8 + build/source/engine/t2enthalpy.f90 | 282 +++++++++++++++--- build/source/engine/t2enthalpyAddPrime.f90 | 226 ++++++++------ build/source/engine/varSubstep.f90 | 28 +- build/source/netcdf/def_output.f90 | 3 +- 33 files changed, 961 insertions(+), 455 deletions(-) delete mode 100644 build/source/engine/computHeatCapWithPrime.f90 create mode 100644 build/source/engine/convE2Temp.f90 create mode 100644 build/source/engine/spline_int.f90 diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index f669210cd..f0beddd11 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -274,7 +274,8 @@ set(NRUTIL # Free versions of numerical recipes procedures for SUMMA modules set(NRPROC - ${ENGINE_DIR}/expIntegral.f90) + ${ENGINE_DIR}/expIntegral.f90 + ${ENGINE_DIR}/spline_int.f90) # Hook-up modules set(HOOKUP @@ -323,6 +324,7 @@ set(PRELIM ${ENGINE_DIR}/checkStruc.f90 ${ENGINE_DIR}/childStruc.f90 ${ENGINE_DIR}/conv_funcs.f90 + ${ENGINE_DIR}/convE2Temp.f90 ${SUB_ENGINE_DIR}/ffile_info.f90 ${ENGINE_DIR}/read_pinit.f90 ${ENGINE_DIR}/read_attrb.f90 @@ -383,7 +385,6 @@ set(SOLVER_NOT_ACTORS ${ENGINE_DIR}/run_oneGRU.f90 ${ENGINE_DIR}/run_oneHRU.f90) set(SOLVER_SUNDIALS - ${ENGINE_DIR}/computHeatCapWithPrime.f90 ${ENGINE_DIR}/computJacobWithPrime.f90 ${ENGINE_DIR}/computResidWithPrime.f90 ${ENGINE_DIR}/eval8summaWithPrime.f90 diff --git a/build/cmake/build.mac.bash b/build/cmake/build.mac.bash index e2a9014f9..d620f6dd1 100755 --- a/build/cmake/build.mac.bash +++ b/build/cmake/build.mac.bash @@ -10,5 +10,5 @@ export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) #export FLAGS_OPT="-flto=1" # -flto=1 is slow to compile, but might want to use -cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials +cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials_Debug cmake --build ../cmake_build --target all diff --git a/build/source/driver/summa_init.f90 b/build/source/driver/summa_init.f90 index 3f961be4b..51252525f 100644 --- a/build/source/driver/summa_init.f90 +++ b/build/source/driver/summa_init.f90 @@ -34,6 +34,7 @@ module summa_init USE globalData,only:mpar_meta,indx_meta ! metadata structures USE globalData,only:bpar_meta,bvar_meta ! metadata structures USE globalData,only:averageFlux_meta ! metadata for time-step average fluxes +USE globalData,only:lookup_meta ! statistics metadata structures USE globalData,only:statForc_meta ! child metadata for stats @@ -247,6 +248,7 @@ subroutine summa_initialize(summa1_struc, err, message) case('flux' ); call allocGlobal(flux_meta, fluxStruct, err, cmessage) ! model fluxes case('bpar' ); call allocGlobal(bpar_meta, bparStruct, err, cmessage) ! basin-average parameters case('bvar' ); call allocGlobal(bvar_meta, bvarStruct, err, cmessage) ! basin-average variables + case('lookup'); call allocGlobal(lookup_meta, lookupStruct, err, cmessage) ! basin-average variables case('deriv' ); cycle case default; err=20; message='unable to find structure name: '//trim(structInfo(iStruct)%structName) end select diff --git a/build/source/driver/summa_modelRun.f90 b/build/source/driver/summa_modelRun.f90 index 27f3ca5bd..dae3e4b1b 100644 --- a/build/source/driver/summa_modelRun.f90 +++ b/build/source/driver/summa_modelRun.f90 @@ -226,6 +226,9 @@ subroutine summa_runPhysics(modelTimeStep, summa1_struc, err, message) bparStruct => summa1_struc%bparStruct , & ! x%gru(:)%var(:) -- basin-average parameters bvarStruct => summa1_struc%bvarStruct , & ! x%gru(:)%var(:)%dat -- basin-average variables + ! lookup table structure + lookupStruct => summa1_struc%lookupStruct , & ! x%gru(:)%hru(:)%z(:)%var(:)%lookup -- lookup-tables + ! run time variables greenVegFrac_monthly => summa1_struc%greenVegFrac_monthly, & ! fraction of green vegetation in each month (0-1) computeVegFlux => summa1_struc%computeVegFlux , & ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) @@ -257,6 +260,7 @@ subroutine summa_runPhysics(modelTimeStep, summa1_struc, err, message) typeStruct%gru(iGRU), & ! intent(in): local classification of soil veg etc. for each HRU idStruct%gru(iGRU), & ! intent(in): local classification of soil veg etc. for each HRU attrStruct%gru(iGRU), & ! intent(in): local attributes for each HRU + lookupStruct%gru(iGRU), & ! intent(in): local lookup tables for each HRU ! data structures (input-output) mparStruct%gru(iGRU), & ! intent(inout): local model parameters indxStruct%gru(iGRU), & ! intent(inout): model indices diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index ea5e25de7..940f91b2f 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -33,6 +33,7 @@ module summa_setup USE var_lookup,only:iLookTYPE ! look-up values for classification of veg, soils etc. USE var_lookup,only:iLookPARAM ! look-up values for local column model parameters USE var_lookup,only:iLookINDEX ! look-up values for local column model indices +USE var_lookup,only:iLookLOOKUP ! look-up values for local column lookup tables USE var_lookup,only:iLookID ! look-up values for local column model ids USE var_lookup,only:iLookBVAR ! look-up values for basin-average model variables USE var_lookup,only:iLookDECISIONS ! look-up values for model decisions @@ -77,6 +78,8 @@ subroutine summa_paramSetup(summa1_struc, err, message) USE paramCheck_module,only:paramCheck ! module to check consistency of model parameters USE pOverwrite_module,only:pOverwrite ! module to overwrite default parameter values with info from the Noah tables USE read_param_module,only:read_param ! module to read model parameter sets + USE convE2Temp_module,only:E2T_lookup ! module to calculate a look-up table for the temperature-enthalpy conversion + USE t2enthalpy_module,only:T2E_lookup ! module to calculate a look-up table for the temperature-enthalpy conversion USE var_derive_module,only:fracFuture ! module to calculate the fraction of runoff in future time steps (time delay histogram) USE module_sf_noahmplsm,only:read_mp_veg_parameters ! module to read NOAH vegetation tables ! global data structures @@ -134,6 +137,9 @@ subroutine summa_paramSetup(summa1_struc, err, message) bparStruct => summa1_struc%bparStruct , & ! x%gru(:)%var(:) -- basin-average parameters bvarStruct => summa1_struc%bvarStruct , & ! x%gru(:)%var(:)%dat -- basin-average variables + ! lookup table structure + lookupStruct => summa1_struc%lookupStruct , & ! x%gru(:)%hru(:)%z(:)%var(:)%lookup -- lookup-tables + ! miscellaneous variables upArea => summa1_struc%upArea , & ! area upslope of each HRU nGRU => summa1_struc%nGRU , & ! number of grouped response units @@ -294,6 +300,20 @@ subroutine summa_paramSetup(summa1_struc, err, message) call paramCheck(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + ! calculate a look-up table for the temperature-enthalpy conversion: snow + ! NOTE1: this should eventually be replaced by the more general routine below + ! NOTE2: this does not actually need to be called for each HRU and GRU + call E2T_lookup(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! calculate a lookup table to compute enthalpy from temperature, need for enthalpyFD or if checkNrgBalance is true + !if(model_decisions(iLookDECISIONS%howHeatCap)%iDecision == enthalpyFD)then + call T2E_lookup(gru_struc(iGRU)%hruInfo(iHRU)%nSoil, & ! intent(in): number of soil layers + mparStruct%gru(iGRU)%hru(iHRU), & ! intent(in): parameter data structure + lookupStruct%gru(iGRU)%hru(iHRU), & ! intent(inout): lookup table data structure + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + !endif ! overwrite the vegetation height HVT(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex)) = mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%heightCanopyTop)%dat(1) diff --git a/build/source/driver/summa_type.f90 b/build/source/driver/summa_type.f90 index a1053f6a2..643ac30bc 100644 --- a/build/source/driver/summa_type.f90 +++ b/build/source/driver/summa_type.f90 @@ -45,7 +45,9 @@ MODULE summa_type gru_hru_int8, & ! x%gru(:)%hru(:)%var(:) (i8b) gru_hru_double, & ! x%gru(:)%hru(:)%var(:) (dp) gru_hru_intVec, & ! x%gru(:)%hru(:)%var(:)%dat (i4b) - gru_hru_doubleVec ! x%gru(:)%hru(:)%var(:)%dat (dp) + gru_hru_doubleVec, & ! x%gru(:)%hru(:)%var(:)%dat (dp) + ! gru+hru+z dimension + gru_hru_z_vLookup ! x%gru(:)%hru(:)%z(:)%var(:)%lookup(:) (dp) implicit none private @@ -53,6 +55,9 @@ MODULE summa_type ! * master summa data type ! ***************************************************************************** type, public :: summa1_type_dec + ! define the lookup tables + type(gru_hru_z_vLookup) :: lookupStruct ! x%gru(:)%hru(:)%z(:)%var(:)%lookup(:) -- lookup tables + ! define the statistics structures type(gru_hru_doubleVec) :: forcStat ! x%gru(:)%hru(:)%var(:)%dat -- model forcing data type(gru_hru_doubleVec) :: progStat ! x%gru(:)%hru(:)%var(:)%dat -- model prognostic (state) variables diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index d7558c73f..97607e7b3 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -131,6 +131,27 @@ MODULE data_types ! *********************************************************************************************************** ! Define hierarchal derived data types ! *********************************************************************************************************** + ! define derived types to hold look-up tables for each soil layer + ! ** double precision type + type, public :: dLookup + real(rkind),allocatable :: lookup(:) ! lookup(:) + endtype dLookup + ! ** double precision type for a variable number of soil layers; variable length + type, public :: vLookup + type(dLookup),allocatable :: var(:) ! var(:)%lookup(:) + endtype vLookup + ! ** double precision type for a variable number of soil layers + type, public :: zLookup + type(vLookup),allocatable :: z(:) ! z(:)%var(:)%lookup(:) + endtype zLookup + ! ** double precision type for a variable number of soil layers + type, public :: hru_z_vLookup + type(zLookup),allocatable :: hru(:) ! hru(:)%z(:)%var(:)%lookup(:) + endtype hru_z_vLookup + ! ** double precision type for a variable number of soil layers + type, public :: gru_hru_z_vLookup + type(hru_z_vLookup),allocatable :: gru(:) ! gru(:)%hru(:)%z(:)%var(:)%lookup(:) + endtype gru_hru_z_vLookup ! define derived types to hold multivariate data for a single variable (different variables have different length) ! NOTE: use derived types here to facilitate adding the "variable" dimension ! ** double precision type diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 34f457ca7..cef4c3a00 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -774,11 +774,6 @@ function get_ixderiv(varName) case('dVolHtCapBulk_dCanWat' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. canopy volumetric water content case('dVolHtCapBulk_dTk' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTk ! derivative in bulk heat capacity w.r.t. temperature case('dVolHtCapBulk_dTkCanopy' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. canopy temperature - case('dVolHtCapBulk_dPsi0Prime' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dPsi0Prime ! derivative in bulk heat capacity w.r.t. prime matric potential - case('dVolHtCapBulk_dThetaPrime' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dThetaPrime ! derivative in bulk heat capacity w.r.t. prime volumetric water content - case('dVolHtCapBulk_dCanWatPrime' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dCanWatPrime ! derivative in bulk heat capacity w.r.t. prime volumetric water content - case('dVolHtCapBulk_dTkPrime' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTkPrime ! derivative in bulk heat capacity w.r.t. prime temperature - case('dVolHtCapBulk_dTkCanPrime' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTkCanPrime ! derivative in bulk heat capacity w.r.t. prime temperature case('dThermalC_dTempAbove' ); get_ixderiv = iLookDERIV%dThermalC_dTempAbove ! derivative in the thermal conductivity w.r.t. energy state in the layer above case('dThermalC_dTempBelow' ); get_ixderiv = iLookDERIV%dThermalC_dTempBelow ! derivative in the thermal conductivity w.r.t. energy state in the layer above case('dThermalC_dWatAbove' ); get_ixderiv = iLookDERIV%dThermalC_dWatAbove ! derivative in the thermal conductivity w.r.t. water state in the layer above @@ -1081,6 +1076,7 @@ subroutine get_ixUnknown(varName,typeName,vDex,err,message) case ('bpar' ); vDex = get_ixBpar(trim(varName)) case ('bvar' ); vDex = get_ixBvar(trim(varName)) case ('deriv'); vDex = get_ixDeriv(trim(varName)) + case ('lookup'); vDex = get_ixLookup(trim(varName)) end select if (vDex>0) then; typeName=trim(structInfo(iStruc)%structName); return; end if end do diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 index 74b9b51f3..6206d2bc7 100644 --- a/build/source/dshare/globalData.f90 +++ b/build/source/dshare/globalData.f90 @@ -53,6 +53,7 @@ MODULE globalData USE var_lookup,only:maxvarBpar ! basin-average parameters: maximum number variables USE var_lookup,only:maxvarDecisions ! maximum number of decisions USE var_lookup,only:maxvarFreq ! maximum number of output files + USE var_lookup,only:maxvarLookup ! maximum number of variables in the lookup implicit none private @@ -137,7 +138,8 @@ MODULE globalData struct_info('prog', 'PROG', maxvarProg ), & ! the prognostic (state) variable data structure struct_info('diag', 'DIAG' , maxvarDiag ), & ! the diagnostic variable data structure struct_info('flux', 'FLUX' , maxvarFlux ), & ! the flux data structure - struct_info('deriv', 'DERIV', maxvarDeriv) /) ! the model derivative data structure + struct_info('deriv', 'DERIV', maxvarDeriv), & ! the model derivative data structure + struct_info('lookup','LOOKUP',maxvarLookup) /) ! the lookup table data structure ! fixed model decisions logical(lgt) , parameter, public :: overwriteRSMIN=.false. ! flag to overwrite RSMIN integer(i4b) , parameter, public :: maxSoilLayers=10000 ! Maximum Number of Soil Layers @@ -163,6 +165,7 @@ MODULE globalData type(var_info),save,public :: diag_meta(maxvarDiag) ! local diagnostic variables for each HRU type(var_info),save,public :: flux_meta(maxvarFlux) ! local model fluxes for each HRU type(var_info),save,public :: deriv_meta(maxvarDeriv) ! local model derivatives for each HRU + type(var_info),save,public :: lookup_meta(maxvarLookup) ! local lookup tables for each HRU type(var_info),save,public :: bpar_meta(maxvarBpar) ! basin parameters for aggregated processes type(var_info),save,public :: bvar_meta(maxvarBvar) ! basin variables for aggregated processes ! ancillary metadata structures diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 6873fc712..fdb7e88f9 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -31,6 +31,7 @@ subroutine popMetadat(err,message) USE globalData, only: diag_meta ! data structure for local diagnostic variables USE globalData, only: flux_meta ! data structure for local flux variables USE globalData, only: deriv_meta ! data structure for local flux derivatives + USE globalData, only: lookup_meta ! data structure for lookup tables ! structures of named variables USE var_lookup, only: iLookTIME ! named variables for time data structure USE var_lookup, only: iLookFORCE ! named variables for forcing data structure @@ -45,6 +46,7 @@ subroutine popMetadat(err,message) USE var_lookup, only: iLookDIAG ! named variables for local diagnostic variables USE var_lookup, only: iLookFLUX ! named variables for local flux variables USE var_lookup, only: iLookDERIV ! named variables for local flux derivatives + USE var_lookup, only: iLookLOOKUP ! named variables for lookup tables USE var_lookup, only: maxvarFreq ! number of output frequencies USE var_lookup, only: maxvarStat ! number of statistics USE get_ixName_module,only:get_ixVarType ! to turn vartype strings to integers @@ -665,6 +667,12 @@ subroutine popMetadat(err,message) bvar_meta(iLookBVAR%averageInstantRunoff) = var_info('averageInstantRunoff' , 'instantaneous runoff' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) bvar_meta(iLookBVAR%averageRoutedRunoff) = var_info('averageRoutedRunoff' , 'routed runoff' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! ----- + ! * temperature and enthalpy lookup tables... + ! ------------------------------------------- + lookup_meta(iLookLOOKUP%temperature) = var_info('temperature' , 'value of temperature in the lookup table' , 'K' , get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + lookup_meta(iLookLOOKUP%enthalpy) = var_info('enthalpy' , 'value of enthalpy in the lookup table' , 'J m-3' , get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + lookup_meta(iLookLOOKUP%deriv2) = var_info('deriv2' , 'second derivatives of the interpolating function' , 'mixed' , get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + ! ----- ! * model indices... ! ------------------ ! number of model layers, and layer indices diff --git a/build/source/dshare/type4ida.f90 b/build/source/dshare/type4ida.f90 index 59058421f..fa6ee0df9 100644 --- a/build/source/dshare/type4ida.f90 +++ b/build/source/dshare/type4ida.f90 @@ -29,6 +29,7 @@ module type4ida logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution type(model_options),allocatable :: model_decisions(:) ! model decisions + type(zLookup) :: lookup_data ! lookup tables type(var_i) :: type_data ! type of vegetation and soil type(var_d) :: attr_data ! spatial attributes type(var_dlength) :: mpar_data ! model parameters diff --git a/build/source/dshare/type4kinsol.f90 b/build/source/dshare/type4kinsol.f90 index dca7a8d76..514a093ab 100644 --- a/build/source/dshare/type4kinsol.f90 +++ b/build/source/dshare/type4kinsol.f90 @@ -28,6 +28,7 @@ module type4kinsol logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution type(model_options),allocatable :: model_decisions(:) ! model decisions + type(zLookup) :: lookup_data ! lookup tables type(var_i) :: type_data ! type of vegetation and soil type(var_d) :: attr_data ! spatial attributes type(var_dlength) :: mpar_data ! model parameters diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 02c3fb949..cddf038b1 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -844,6 +844,15 @@ MODULE var_lookup integer(i4b) :: timestep = integerMissing ! timestep-level output (no temporal aggregation) endtype iLook_freq + ! *********************************************************************************************************** + ! (16) structure for looking up lookup tables + ! *********************************************************************************************************** + type, public :: iLook_vLookup + integer(i4b) :: temperature = integerMissing ! temperature (K) + integer(i4b) :: enthalpy = integerMissing ! enthalpy (J m-3) + integer(i4b) :: deriv2 = integerMissing ! second derivatives of the interpolating function + endtype iLook_vLookup + ! *********************************************************************************************************** ! (X) define data structures and maximum number of variables of each type ! *********************************************************************************************************** @@ -936,6 +945,8 @@ MODULE var_lookup type(iLook_stat), public,parameter :: iLookStat =ilook_stat ( 1, 2, 3, 4, 5, 6, 7) ! number of possible output frequencies type(iLook_freq), public,parameter :: iLookFreq =ilook_freq ( 1, 2, 3, 4) + ! named variables in the lookup table structure + type(iLook_vLookup), public,parameter :: iLookLOOKUP =ilook_vLookup ( 1, 2, 3) ! define maximum number of variables of each type integer(i4b),parameter,public :: maxvarDecisions = storage_size(iLookDECISIONS)/iLength integer(i4b),parameter,public :: maxvarTime = storage_size(iLookTIME)/iLength diff --git a/build/source/engine/allocspace.f90 b/build/source/engine/allocspace.f90 index e263daad9..3c35318fa 100644 --- a/build/source/engine/allocspace.f90 +++ b/build/source/engine/allocspace.f90 @@ -46,7 +46,9 @@ module allocspace_module gru_hru_int8, & ! x%gru(:)%hru(:)%var(:) integer(8) gru_hru_double, & ! x%gru(:)%hru(:)%var(:) (rkind) gru_hru_intVec, & ! x%gru(:)%hru(:)%var(:)%dat (i4b) - gru_hru_doubleVec ! x%gru(:)%hru(:)%var(:)%dat (rkind) + gru_hru_doubleVec, & ! x%gru(:)%hru(:)%var(:)%dat (rkind) + ! gru+hru+z dimension + gru_hru_z_vLookup ! x%gru(:)%hru(:)%z(:)%var(:)%lookup (rkind) ! metadata structure USE data_types,only:var_info ! data type for metadata diff --git a/build/source/engine/checkStruc.f90 b/build/source/engine/checkStruc.f90 index 75254b237..d40bbcb21 100644 --- a/build/source/engine/checkStruc.f90 +++ b/build/source/engine/checkStruc.f90 @@ -40,11 +40,13 @@ subroutine checkStruc(err,message) USE globalData,only:prog_meta,diag_meta,flux_meta,deriv_meta ! metadata structures USE globalData,only:mpar_meta,indx_meta ! metadata structures USE globalData,only:bpar_meta,bvar_meta ! metadata structures + USE globalData,only:lookup_meta ! metadata structures ! named variables defining strructure elements USE var_lookup,only:iLookTIME,iLookFORCE,iLookATTR,iLookTYPE,iLookID ! named variables showing the elements of each data structure USE var_lookup,only:iLookPROG,iLookDIAG,iLookFLUX,iLookDERIV ! named variables showing the elements of each data structure USE var_lookup,only:iLookPARAM,iLookINDEX ! named variables showing the elements of each data structure USE var_lookup,only:iLookBPAR,iLookBVAR ! named variables showing the elements of each data structure + USE var_lookup,only:iLookLOOKUP ! named variables showing the elements of each data structure implicit none ! dummy variables integer(i4b),intent(out) :: err ! error code @@ -83,6 +85,7 @@ subroutine checkStruc(err,message) case('diag'); write(longString,*) iLookDIAG case('flux'); write(longString,*) iLookFLUX case('deriv'); write(longString,*) iLookDERIV + case('lookup'); write(longString,*) iLookLOOKUP case default; err=20; message=trim(message)//'unable to identify lookup structure'; return end select ! check that the length of the lookup structure matches the number of variables in the data structure @@ -120,6 +123,7 @@ subroutine checkStruc(err,message) case('diag'); call checkPopulated(iStruct,diag_meta,err,cmessage) case('flux'); call checkPopulated(iStruct,flux_meta,err,cmessage) case('deriv'); call checkPopulated(iStruct,deriv_meta,err,cmessage) + case('lookup'); call checkPopulated(iStruct,lookup_meta,err,cmessage) case default; err=20; message=trim(message)//'unable to identify lookup structure'; return end select if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index a90b6ca58..8f6f75eaa 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -91,15 +91,13 @@ subroutine computHeatCap(& scalarCanopyIce, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) scalarCanopyLiquid, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) - scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) + scalarCanopyTempDelta, & ! intent(in): delta value of canopy temperature (prime or trial-prev) + scalarCanopyEnthalpyDelta, & ! intent(in): delta enthalpy of the vegetation canopy (prime or trial-prev) mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) mLayerTempTrial, & ! intent(in): trial temperature - mLayerTempPrev, & ! intent(in): previous temperature - mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil - mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil + mLayerTempDelta, & ! intent(in): delta temperature (prime or trial-prev) + mLayerEnthalpyDelta, & ! intent(in): delta enthalpy for snow and soil (prime or trial-prev) mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) ! input: pre-computed derivatives dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) @@ -133,15 +131,13 @@ subroutine computHeatCap(& real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) real(rkind),intent(in) :: scalarCanopyLiquid ! trial value for the liquid water on the vegetation canopy (kg m-2) real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature - real(rkind),intent(in) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(in) :: scalarCanopyEnthalpyPrev ! intent(in): previous enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(in) :: scalarCanopyTempPrev ! Previous value of canopy temperature + real(rkind),intent(in) :: scalarCanopyEnthalpyDelta ! delta enthalpy of the vegetation canopy (prime or trial-prev) + real(rkind),intent(in) :: scalarCanopyTempDelta ! delta of canopy temperature (prime or trial-prev) real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) - real(rkind),intent(in) :: mLayerTempTrial(:) ! trial temperature - real(rkind),intent(in) :: mLayerTempPrev(:) ! previous temperature - real(rkind),intent(in) :: mLayerEnthalpyTrial(:) ! trial enthalpy for snow and soil - real(rkind),intent(in) :: mLayerEnthalpyPrev(:) ! previous enthalpy for snow and soil + real(rkind),intent(in) :: mLayerTempTrial(:) ! trial temperature (prime or trial-prev) + real(rkind),intent(in) :: mLayerTempDelta(:) ! delta temperature + real(rkind),intent(in) :: mLayerEnthalpyDelta(:) ! delta enthalpy for snow and soil (prime or trial-prev) real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! vector of total water matric potential (m) ! input: pre-computed derivatives real(rkind),intent(in) :: dTheta_dTkCanopy ! derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) @@ -189,17 +185,12 @@ subroutine computHeatCap(& ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) if(computeVegFlux)then - delT = scalarCanopyTempTrial - scalarCanopyTempPrev - if(abs(delT) <= 1e-2_rkind)then + if(abs(scalarCanopyTempDelta) <= 1e-2_rkind)then heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component Cp_ice*scalarCanopyIce/canopyDepth ! ice component else - delEnt = scalarCanopyEnthalpyTrial - scalarCanopyEnthalpyPrev - heatCapVeg = delEnt / delT - ! derivatives - dVolHtCapBulk_dCanWat = dCanEnthalpy_dWat / delT - dVolHtCapBulk_dTkCanopy = ( dCanEnthalpy_dTk - delEnt/delT ) / delT + heatCapVeg = scalarCanopyEnthalpyDelta / scalarCanopyTempDelta endif ! derivatives for Jacobian are from analytical solution @@ -214,10 +205,10 @@ subroutine computHeatCap(& ! loop through layers do iLayer=1,nLayers - delT = mLayerTempTrial(iLayer) - mLayerTempPrev(iLayer) - if(abs(delT) <= 1e-2_rkind)then - ! get the soil layer - if(iLayer>nSnow) iSoil = iLayer-nSnow + ! get the soil layer + if(iLayer>nSnow) iSoil = iLayer-nSnow + + if(abs(mLayerTempDelta(iLayer)) <= 1e-2_rkind)then select case(layerType(iLayer)) ! * soil case(iname_soil) @@ -232,8 +223,7 @@ subroutine computHeatCap(& case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return end select else - delEnt = mLayerEnthalpyTrial(iLayer) - mLayerEnthalpyPrev(iLayer) - mLayerHeatCap(iLayer) = delEnt / delT + mLayerHeatCap(iLayer) = mLayerEnthalpyDelta(iLayer) / mLayerTempDelta(iLayer) endif ! derivatives for Jacobian are from analytical solution diff --git a/build/source/engine/computHeatCapWithPrime.f90 b/build/source/engine/computHeatCapWithPrime.f90 deleted file mode 100644 index 260ff8b91..000000000 --- a/build/source/engine/computHeatCapWithPrime.f90 +++ /dev/null @@ -1,262 +0,0 @@ -! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2015 NCAR/RAL -! -! This file is part of SUMMA -! -! For more information see: http://www.ral.ucar.edu/projects/summa -! -! This program is free software: you can redistribute it and/or modify -! it under the terms of the GNU General Public License as published by -! the Free Software Foundation, either version 3 of the License, or -! (at your option) any later version. -! -! This program is distributed in the hope that it will be useful, -! but WITHOUT ANY WARRANTY; without even the implied warranty of -! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -! GNU General Public License for more details. -! -! You should have received a copy of the GNU General Public License -! along with this program. If not, see . - -module computHeatCapWithPrime_module - -! data types -USE nrtype - -! derived types to define the data structures -USE data_types,only:& - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (rkind) - -! named variables defining elements in the data structures -USE var_lookup,only:iLookPARAM,iLookDIAG,iLookINDEX ! named variables for structure elements - -! physical constants -USE multiconst,only:& - Tfreeze, & ! freezing point of water (K) - iden_air, & ! intrinsic density of air (kg m-3) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water, & ! intrinsic density of water (kg m-3) - ! specific heat - Cp_air, & ! specific heat of air (J kg-1 K-1) - Cp_ice, & ! specific heat of ice (J kg-1 K-1) - Cp_soil, & ! specific heat of soil (J kg-1 K-1) - Cp_water ! specific heat of liquid water (J kg-1 K-1) -! named variables to describe the state variable type -USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space -USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy -USE globalData,only:iname_watCanopy ! named variable defining the mass of total water on the vegetation canopy -USE globalData,only:iname_liqCanopy ! named variable defining the mass of liquid water on the vegetation canopy -USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers -USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers -USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers -USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers -USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers -USE globalData,only:iname_watAquifer ! named variable defining the water storage in the aquifer - -! missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing real - -! named variables that define the layer type -USE globalData,only:iname_snow ! snow -USE globalData,only:iname_soil ! soil - - -! privacy -implicit none -private -public::computHeatCapWithPrime - -contains - - -! ********************************************************************************************************** -! public subroutine computHeatCapWithPrime: compute diagnostic energy variables (heat capacity) -! ********************************************************************************************************** -subroutine computHeatCapWithPrime(& - ! input: control variables - nLayers, & ! intent(in): number of layers (soil+snow) - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) - ! input output data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! input: state variables - scalarCanopyIce, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiquid, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature - scalarCanopyTempPrime, & ! intent(in): prime value of canopy temperature (K) - scalarCanopyEnthalpyPrime, & ! intent(in): prime enthalpy of the vegetation canopy (J m-3) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial temperature - mLayerTempPrime, & ! intent(in): prime temperature - mLayerEnthalpyPrime, & ! intent(in): prime enthalpy for snow and soil - mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) - ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - ! output - heatCapVeg, & ! intent(out): heat capacity for canopy - mLayerHeatCap, & ! intent(out): heat capacity for snow and soil - dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - ! output: error control - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------------- - ! provide access to external subroutines - USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists - ! -------------------------------------------------------------------------------------------------------------------------------- - ! input: control variables - integer(i4b),intent(in) :: nLayers ! number of layers (soil+snow) - logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) - ! input/output: data structures - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model layer indices - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - ! input: state variables - real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) - real(rkind),intent(in) :: scalarCanopyLiquid ! trial value for the liquid water on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature - real(rkind),intent(in) :: scalarCanopyTempPrime ! prime value of canopy temperature - real(rkind),intent(in) :: scalarCanopyEnthalpyPrime ! prime enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) - real(rkind),intent(in) :: mLayerTempTrial(:) ! trial temperature - real(rkind),intent(in) :: mLayerTempPrime(:) ! prime temperature - real(rkind),intent(in) :: mLayerEnthalpyPrime(:) ! prime enthalpy for snow and soil - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! vector of total water matric potential (m) - ! input: pre-computed derivatives - real(rkind),intent(in) :: dTheta_dTkCanopy ! derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: scalarFracLiqVeg ! fraction of canopy liquid water (-) - real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative of volumetric liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) - real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) - ! output: - real(qp),intent(out) :: heatCapVeg ! heat capacity for canopy - real(qp),intent(out) :: mLayerHeatCap(:) ! heat capacity for snow and soil - real(rkind),intent(out) :: dVolHtCapBulk_dPsi0(:) ! derivative in bulk heat capacity w.r.t. matric potential - real(rkind),intent(out) :: dVolHtCapBulk_dTheta(:) ! derivative in bulk heat capacity w.r.t. volumetric water content - real(rkind),intent(out) :: dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. volumetric water content - real(rkind),intent(out) :: dVolHtCapBulk_dTk(:) ! derivative in bulk heat capacity w.r.t. temperature - real(rkind),intent(out) :: dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: iSoil ! index of soil layer - real(rkind) :: delT ! temperature change - real(rkind) :: delEnt ! enthalpy change - real(rkind) :: fLiq ! fraction of liquid water - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - ! -------------------------------------------------------------------------------------------------------------------------------- - ! associate variables in data structure - associate(& - ! input: coordinate variables - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) - ! input: heat capacity and thermal conductivity - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) - ! input: depth varying soil parameters - iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) - ) ! end associate statemen - ! initialize error control - err=0; message="computHeatCapWithPrime/" - - ! initialize the soil layer - iSoil=integerMissing - - ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) - if(computeVegFlux)then - delT = scalarCanopyTempPrime - if(abs(delT) <= 1e-2_rkind)then - heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component - Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component - Cp_ice*scalarCanopyIce/canopyDepth ! ice component - else - delEnt = scalarCanopyEnthalpyPrime - heatCapVeg = delEnt / delT - endif - - ! derivatives for Jacobian are from analytical solution - fLiq = scalarFracLiqVeg - dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) - if(scalarCanopyTempTrial < Tfreeze)then - dVolHtCapBulk_dTkCanopy = iden_water * (-Cp_ice + Cp_water) * dTheta_dTkCanopy ! no derivative in air - else - dVolHtCapBulk_dTkCanopy = 0._rkind - endif - endif - - ! loop through layers - do iLayer=1,nLayers - delT = mLayerTempPrime(iLayer) - if(abs(delT) <= 1e-2_rkind)then - ! get the soil layer - if(iLayer>nSnow) iSoil = iLayer-nSnow - select case(layerType(iLayer)) - ! * soil - case(iname_soil) - mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component - iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component - ! * snow - case(iname_snow) - mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component - - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return - end select - else - delEnt = mLayerEnthalpyPrime(iLayer) - mLayerHeatCap(iLayer) = delEnt / delT - endif - - ! derivatives for Jacobian are from analytical solution - select case(layerType(iLayer)) - ! * soil - case(iname_soil) - dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use - Tcrit = crit_soilT( mLayerMatricHeadTrial(iSoil) ) - if( mLayerTempTrial(iLayer) < Tcrit)then - dVolHtCapBulk_dPsi0(iSoil) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(iSoil) - dVolHtCapBulk_dTk(iLayer) = (-iden_ice * Cp_ice + iden_water * Cp_water) * mLayerdTheta_dTk(iLayer) - else - dVolHtCapBulk_dPsi0(iSoil) = (iden_water*Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(iSoil) - dVolHtCapBulk_dTk(iLayer) = 0._rkind - endif - ! * snow - case(iname_snow) - fLiq = mLayerFracLiqSnow(iLayer) - dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air - if( mLayerTempTrial(iLayer) < Tfreeze)then - dVolHtCapBulk_dTk(iLayer) = ( iden_water * (-Cp_ice + Cp_water) + iden_air * (iden_water/iden_ice - 1._rkind) * Cp_air ) * mLayerdTheta_dTk(iLayer) - else - dVolHtCapBulk_dTk(iLayer) = 0._rkind - endif - end select - - end do ! looping through layers - - end associate - -end subroutine computHeatCapWithPrime - - -end module computHeatCapWithPrime_module \ No newline at end of file diff --git a/build/source/engine/convE2Temp.f90 b/build/source/engine/convE2Temp.f90 new file mode 100644 index 000000000..59800c29e --- /dev/null +++ b/build/source/engine/convE2Temp.f90 @@ -0,0 +1,231 @@ +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . + +module convE2Temp_module + +! data types +USE nrtype +USE data_types,only:var_dlength ! data vector with variable length dimension (rkind): x%var(:)%dat(:) + +! constants +USE multiconst, only: Tfreeze, & ! freezing point of water (K) + Cp_soil,Cp_water,Cp_ice,& ! specific heat of soil, water and ice (J kg-1 K-1) + LH_fus ! latent heat of fusion (J kg-1) + +! indices within parameter structure +USE var_lookup,only:iLookPARAM ! named variables to define structure element + +! privacy +implicit none +private +public::E2T_lookup +public::E2T_nosoil +public::temp2ethpy + +! define the look-up table used to compute temperature based on enthalpy +integer(i4b),parameter :: nlook=10001 ! number of elements in the lookup table +real(rkind),dimension(nlook),public :: E_lookup ! enthalpy values (J kg-1) +real(rkind),dimension(nlook),public :: T_lookup ! temperature values (K) +contains + + + ! ************************************************************************************************************************ + ! public subroutine E2T_lookup: define a look-up table to compute specific enthalpy based on temperature, assuming no soil + ! ************************************************************************************************************************ + subroutine E2T_lookup(mpar_data,err,message) + USE nr_utility_module,only:arth ! use to build vectors with regular increments + USE spline_int_module,only:spline,splint ! use for cubic spline interpolation + implicit none + ! declare dummy variables + type(var_dlength),intent(in) :: mpar_data ! model parameters + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! declare local variables + character(len=128) :: cmessage ! error message in downwind routine + real(rkind),parameter :: T_start=260.0_rkind ! start temperature value where all liquid water is assumed frozen (K) + real(rkind) :: T_incr,E_incr ! temperature/enthalpy increments + real(rkind),dimension(nlook) :: Tk ! initial temperature vector + real(rkind),dimension(nlook) :: Ey ! initial enthalpy vector + real(rkind),parameter :: waterWght=1._rkind ! weight applied to total water (kg m-3) --- cancels out + real(rkind),dimension(nlook) :: T2deriv ! 2nd derivatives of the interpolating function at tabulated points + real(rkind) :: dT ! derivative of temperature with enthalpy at E_lookup + integer(i4b) :: ilook ! loop through lookup table + ! initialize error control + err=0; message="E2T_lookup/" + ! associate + associate( snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ) + ! define initial temperature vector + T_incr = (Tfreeze - T_start) / real(nlook-1, kind(rkind)) ! temperature increment + Tk = arth(T_start,T_incr,nlook) + ! ***** compute specific enthalpy (NOTE: J m-3 --> J kg-1) ***** + do ilook=1,nlook + Ey(ilook) = temp2ethpy(Tk(ilook),waterWght,snowfrz_scale)/waterWght ! (J m-3 --> J kg-1) + end do + ! define the final enthalpy vector + E_incr = (-Ey(1)) / real(nlook-1, kind(rkind)) ! enthalpy increment + E_lookup = arth(Ey(1),E_incr,nlook) + ! use cubic spline interpolation to obtain temperature values at the desired values of enthalpy + call spline(Ey,Tk,1.e30_rkind,1.e30_rkind,T2deriv,err,cmessage) ! get the second derivatives + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + do ilook=1,nlook + call splint(Ey,Tk,T2deriv,E_lookup(ilook),T_lookup(ilook),dT,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + !write(*,'(i6,1x,2(f20.4,1x))') ilook, E_lookup(ilook), T_lookup(ilook) + end do + end associate + end subroutine E2T_lookup + + + ! ************************************************************************************************************************ + ! public subroutine E2T_nosoil: compute temperature based on specific enthalpy -- appropriate when no dry mass, as in snow + ! ************************************************************************************************************************ + subroutine E2T_nosoil(Ey,BulkDenWater,fc_param,Tk,err,message) + ! compute temperature based on enthalpy -- appropriate when no dry mass, as in snow + implicit none + ! declare dummy variables + real(rkind),intent(in) :: Ey ! total enthalpy (J m-3) + real(rkind),intent(in) :: BulkDenWater ! bulk density of water (kg m-3) + real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) + real(rkind),intent(out) :: Tk ! initial temperature guess / final temperature value (K) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! declare local variables + real(rkind),parameter :: dx=1.d-8 ! finite difference increment (J kg-1) + real(rkind),parameter :: atol=1.d-12 ! convergence criteria (J kg-1) + real(rkind) :: E_spec ! specific enthalpy (J kg-1) + real(rkind) :: E_incr ! enthalpy increment + integer(i4b) :: niter=15 ! maximum number of iterations + integer(i4b) :: iter ! iteration index + integer(i4b) :: i0 ! position in lookup table + real(rkind) :: Tg0,Tg1 ! trial temperatures (K) + real(rkind) :: Ht0,Ht1 ! specific enthalpy, based on the trial temperatures (J kg-1) + real(rkind) :: f0,f1 ! function evaluations (difference between enthalpy guesses) + real(rkind) :: dh ! enthalpy derivative + real(rkind) :: dT ! temperature increment + ! initialize error control + err=0; message="E2T_nosoil/" + ! convert input of total enthalpy (J m-3) to total specific enthalpy (J kg-1) + E_spec = Ey/BulkDenWater ! (NOTE: no soil) + !write(*,'(a,1x,10(e20.10,1x))') 'E_spec, E_lookup(1)', E_spec, E_lookup(1) + + ! ***** get initial guess and derivative assuming all water is frozen + if(E_spec E_lookup(i0+1) .or. & + i0 < 1 .or. i0+1 > nlook)then + err=10; message=trim(message)//'problem finding appropriate value in lookup table'; return + end if + ! get temperature guess + Tg0 = T_lookup(i0) + Tg1 = T_lookup(i0+1) + ! compute function evaluations + f0 = E_lookup(i0) - E_spec + f1 = E_lookup(i0+1) - E_spec + end if + + ! compute initial derivative + dh = (f1 - f0) / (Tg1 - Tg0) + ! compute initial change in T + dT = -f0/dh + !write(*,'(a,1x,f12.5,1x,10(e20.10,1x))') 'Tg1, f0, f1, dh, dT = ', Tg1, f0, f1, dh, dT + ! exit if already close enough + if(abs(dT) mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) , & ! scaling parameter for freezing (K-1) + soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(iSoil) , & ! intrinsic soil density (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(iSoil) , & ! soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(iSoil) , & ! volumetric residual water content (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(iSoil) , & ! van Genuchten "alpha" parameter (m-1) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(iSoil) , & ! van Genuchten "n" parameter (-) + + ! associate values in the lookup table + Tk => lookup_data%z(iSoil)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) + Ey => lookup_data%z(iSoil)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) + E2 => lookup_data%z(iSoil)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + + ) ! end associate statement + + ! compute vGn_m + vGn_m = 1._rkind - 1._rkind/vGn_n + + ! ----- + ! * populate the lookup table... + ! ------------------------------ + + ! initialize temperature and enthalpy + Tk(nLook) = Tfreeze + Ey(nLook) = 0._rkind + + ! loop through lookup table + do iLook=(nLook-1),1,-1 + + ! update temperature and enthalpy + Tk(iLook) = Tk(iLook+1) + Ey(iLook) = Ey(iLook+1) + + ! get the temperature increment for the numerical integration + T_incr = (xTemp(iLook)-xTemp(iLook+1))/real(nIntegr8, kind(rkind)) + + ! numerical integration between different values of the lookup table + do jIntegr8=1,nIntegr8 + + ! update temperature + Tk(iLook) = Tk(iLook) + T_incr + + ! compute the volumetric liquid water and ice content + ! NOTE: assume saturation + matricHead = (LH_fus/gravity)*(Tk(iLook) - Tfreeze)/Tfreeze + vFracLiq = volFracLiq(matricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + volFracIce = theta_sat - vFracLiq + + ! compute enthalpy + ! NOTE: assume intrrinsic density of ice is the intrinsic density of water + ! NOTE: kg m-3 J kg-1 K-1 K + Ey(iLook) = Ey(iLook) + iden_water * Cp_water*vFracLiq*T_incr + iden_water * Cp_ice*volFracIce*T_incr + + end do ! numerical integration + + end do ! loop through lookup table + + ! use cubic spline interpolation to obtain enthalpy values at the desired values of temperature + call spline(Tk,Ey,1.e30_rkind,1.e30_rkind,E2,err,cmessage) ! get the second derivatives + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! test + if(doTest)then + + ! calculate enthalpy + call splint(Tk,Ey,E2,T_test,E_test,dE,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! write values + print*, 'doTest = ', doTest + print*, 'T_test = ', T_test ! temperature (K) + print*, 'E_test = ', E_test ! enthalpy (J m-3) + print*, 'theta_sat = ', theta_sat ! soil porosity (-) + print*, 'theta_res = ', theta_res ! volumetric residual water content (-) + print*, 'vGn_alpha = ', vGn_alpha ! van Genuchten "alpha" parameter (m-1) + print*, 'vGn_n = ', vGn_n ! van Genuchten "n" parameter (-) + print*, trim(message)//'PAUSE: Set doTest=.false. to complete simulations' + read(*,*) + + endif ! if testing + + ! end asssociation to variables in the data structures + end associate + + end do ! (looping through soil layers) +end subroutine T2E_lookup + + ! ************************************************************************************************************************ ! public subroutine t2enthalpy: compute enthalpy from temperature and total water content ! ************************************************************************************************************************ @@ -72,6 +246,7 @@ subroutine t2enthalpy(& diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure ! input: state variables for the vegetation canopy scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) @@ -90,6 +265,7 @@ subroutine t2enthalpy(& ! downwind routines USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water + USE spline_int_module,only:splint ! use for cubic spline interpolation implicit none ! delare dummy variables ! ------------------------------------------------------------------------------------------------------------------------- @@ -97,6 +273,7 @@ subroutine t2enthalpy(& type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model indices + type(zLookup),intent(in) :: lookup_data ! lookup tables ! input: state variables for the vegetation canopy real(rkind),intent(in) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) @@ -105,16 +282,10 @@ subroutine t2enthalpy(& real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind),intent(in) :: mLayerMatricHeadLiqTrial(:)! trial vector of liquid water matric potential (m) ! output: enthalpy real(rkind),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) real(rkind),intent(out) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) real(rkind),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) - ! output: derivatives - real(rkind),intent(out) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature - real(rkind),intent(out) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state - real(rkind),intent(out) :: dEnthalpy_dTk(:) ! derivatives in layer enthalpy w.r.t. temperature - real(rkind),intent(out) :: dEnthalpy_dWat(:) ! derivatives in layer enthalpy w.r.t. water state ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -132,8 +303,12 @@ subroutine t2enthalpy(& real(rkind) :: diffT ! temperature difference from Tfreeze real(rkind) :: integral ! integral of snow freezing curve real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head + real(rkind) :: dE ! derivative of enthalpy with temperature at layer temperature + real(rkind) :: dEcrit ! derivative of enthalpy with temperature at critical temperature real(rkind) :: arg ! argument of hypergeometric function real(rkind) :: gauss_hg_T ! hypergeometric function result + real(rkind) :: xConst ! constant in the freezing curve function (m K-1) + real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) ! enthalpy real(rkind) :: enthVeg ! enthalpy of the vegetation (J m-3) real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) @@ -145,6 +320,8 @@ subroutine t2enthalpy(& real(rkind) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) real(rkind) :: enthWater ! enthalpy of total water (J m-3) + logical(lgt),parameter :: use_lookup=.true. ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function + logical(lgt),parameter :: quick_hyper=.false. ! flag to use a quick hypergeometric function, currently no difference between quick and slow hypergeometric functions ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures generalVars: associate(& @@ -160,7 +337,7 @@ subroutine t2enthalpy(& ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat & ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) ) ! end associate statement - ! -------------------------------------------------------------------------------------------------------------------------------- + ! ------------------------------------------------------------------------------------------------------------------------------ ! initialize error control err=0; message="t2enthalpy/" @@ -198,10 +375,10 @@ subroutine t2enthalpy(& case(iname_veg) ! association to necessary variables for vegetation vegVars: associate(& - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): [dp] canopy depth (m) - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! canopy depth (m) + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! maximum mass of vegetation (kg m-2) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! scaling parameter for the snow freezing curve (K-1) ) diffT = scalarCanopyTempTrial - Tfreeze @@ -224,7 +401,7 @@ subroutine t2enthalpy(& ! association to necessary variables for snow snowVars: associate(& - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) ) diffT = mLayerTempTrial(iLayer) - Tfreeze ! diffT<0._rkind because snow is frozen @@ -238,17 +415,17 @@ subroutine t2enthalpy(& end associate snowVars case(iname_soil) - - ! make association to variables in the data structures... + ! make association to variables for soil soilVars: associate(& - - ! associate model parameters soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex) , & ! intrinsic soil density (kg m-3) theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex) , & ! soil porosity (-) theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex) , & ! volumetric residual water content (-) vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) - + ! associate values in the lookup table + Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) + Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) + E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function ) ! end associate statement ! diagnostic variables @@ -268,12 +445,35 @@ subroutine t2enthalpy(& enthIce = iden_ice * Cp_ice * volFracWat * (mLayerTempTrial(iLayer) - Tcrit) ! compute enthalpy of the mixed region, liquid+ice - arg = (-vGn_alpha * mLayerMatricHeadLiqTrial(ixControlIndex))**vGn_n - gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - enthTemp = (iden_water * Cp_water - iden_ice * Cp_ice) * diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) - arg = (-vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n - gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - enthTcrit = (iden_water * Cp_water - iden_ice * Cp_ice) * diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + if(use_lookup)then !cubic spline interpolation + ! calculate enthalpy at the temperature + call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),enthTemp,dE,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + ! calculate enthalpy at the critical temperature + call splint(Tk,Ey,E2,Tcrit,enthTcrit,dEcrit,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + else ! hypergeometric function + ! calculate enthalpy at the temperature + ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice + ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations + xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) + mLayerPsiLiq = xConst*(mLayerTempTrial(iLayer) - Tfreeze) ! liquid water matric potential from the Clapeyron eqution + arg = (-vGn_alpha * mLayerPsiLiq)**vGn_n + if(quick_hyper)then + gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + else + gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + endif + enthTemp = (iden_water * Cp_water - iden_ice * Cp_ice) * diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + ! calculate enthalpy at the critical temperature + arg = (-vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n + if(quick_hyper)then + gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + else + gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + endif + enthTcrit = (iden_water * Cp_water - iden_ice * Cp_ice) * diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + endif ! calculate the enthalpy of water enthMix = enthTemp - enthTcrit ! enthalpy of the liquid+ice mix @@ -327,27 +527,27 @@ subroutine t2enthalpy_addphase(& ! delare dummy variables ! ------------------------------------------------------------------------------------------------------------------------- ! input: data structures - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_ilength),intent(in) :: indx_data ! model indices + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_ilength),intent(in) :: indx_data ! model indices ! input: state variables for the vegetation canopy - real(rkind),intent(in) :: scalarCanopyIce ! value of canopy ice content (kg m-2) or prime ice content (kg m-2 s-1) + real(rkind),intent(in) :: scalarCanopyIce ! value of canopy ice content (kg m-2) or prime ice content (kg m-2 s-1) ! input: variables for the snow-soil domain - real(rkind),intent(in) :: mLayerVolFracIce (:) ! vector of volumetric fraction of ice (-) or prime volumetric fraction of ice (s-1) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! vector of volumetric fraction of ice (-) or prime volumetric fraction of ice (s-1) ! input output: enthalpy - real(rkind),intent(inout) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) or enthalpy prime (J m-3 s-1) - real(rkind),intent(inout) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) or enthalpy prime (J m-3 s-1) + real(rkind),intent(inout) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) or enthalpy prime (J m-3 s-1) + real(rkind),intent(inout) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) or enthalpy prime (J m-3 s-1) ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------------------------------- ! declare local variables - character(len=128) :: cmessage ! error message in downwind routine - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixDomainType ! name of a given model domain - integer(i4b) :: ixControlIndex ! index within a given model domain - ! -------------------------------------------------------------------------------------------------------------------------------- + character(len=128) :: cmessage ! error message in downwind routine + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixDomainType ! name of a given model domain + integer(i4b) :: ixControlIndex ! index within a given model domain + ! ------------------------------------------------------------------------------------------------------------------------ ! make association with variables in the data structures generalVars: associate(& ! number of model layers, and layer type @@ -362,7 +562,7 @@ subroutine t2enthalpy_addphase(& ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat & ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) ) ! end associate statement - ! -------------------------------------------------------------------------------------------------------------------------------- + ! ----------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="t2enthalpy_addphase/" @@ -399,7 +599,7 @@ subroutine t2enthalpy_addphase(& case(iname_veg) ! association to necessary variables for vegetation vegVars: associate(& - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) & ! intent(in): [dp] canopy depth (m) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) & ! canopy depth (m) ) scalarCanopyEnthalpy = scalarCanopyEnthalpy - LH_fus * scalarCanopyIce / canopyDepth diff --git a/build/source/engine/t2enthalpyAddPrime.f90 b/build/source/engine/t2enthalpyAddPrime.f90 index 53598ec0a..276f1e9b2 100644 --- a/build/source/engine/t2enthalpyAddPrime.f90 +++ b/build/source/engine/t2enthalpyAddPrime.f90 @@ -31,6 +31,7 @@ module t2enthalpyAddPrime_module USE nrtype USE data_types,only:var_iLength ! var(:)%dat(:) USE data_types,only:var_dLength ! var(:)%dat(:) +USE data_types,only:zLookup ! z(:)%var(:)%lookup(:) ! indices within parameter structure USE var_lookup,only:iLookPARAM ! named variables to define structure element @@ -38,6 +39,9 @@ module t2enthalpyAddPrime_module USE var_lookup,only:iLookLOOKUP ! named variables to define structure element USE var_lookup,only:iLookDIAG ! named variables for structure elements +! data dimensions +USE var_lookup,only:maxvarLookup ! maximum number of variables in the lookup tables + ! domain types USE globalData,only:iname_cas ! named variables for canopy air space USE globalData,only:iname_veg ! named variables for vegetation canopy @@ -70,6 +74,7 @@ subroutine t2enthalpyPrime(& diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure ! input: state variables for the vegetation canopy scalarCanairTempPrime, & ! intent(in): prime value of canopy air temperature (K) scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) @@ -95,35 +100,37 @@ subroutine t2enthalpyPrime(& ! downwind routines USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water + USE spline_int_module,only:splint ! use for cubic spline interpolation implicit none ! delare dummy variables ! ------------------------------------------------------------------------------------------------------------------------- ! input: data structures - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model indices + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model indices + type(zLookup),intent(in) :: lookup_data ! lookup tables ! input: state variables for the vegetation canopy - real(rkind),intent(in) :: scalarCanairTempPrime ! prime value of canopy air temperature (K) - real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - real(rkind),intent(in) :: scalarCanopyTempPrime ! prime value of canopy temperature (K) - real(rkind),intent(in) :: scalarCanopyWatPrime ! prime value of canopy total water (kg m-2) + real(rkind),intent(in) :: scalarCanairTempPrime ! prime value of canopy air temperature (K) + real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + real(rkind),intent(in) :: scalarCanopyTempPrime ! prime value of canopy temperature (K) + real(rkind),intent(in) :: scalarCanopyWatPrime ! prime value of canopy total water (kg m-2) ! input: variables for the snow-soil domain - real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind),intent(in) :: mLayerTempPrime(:) ! prime vector of layer temperature (K) - real(rkind),intent(in) :: mLayerVolFracWatPrime(:) ! prime vector of volumetric total water content (-) - real(rkind),intent(in) :: mLayerMatricHeadPrime(:) ! prime vector of total water matric potential (m) + real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind),intent(in) :: mLayerTempPrime(:) ! prime vector of layer temperature (K) + real(rkind),intent(in) :: mLayerVolFracWatPrime(:) ! prime vector of volumetric total water content (-) + real(rkind),intent(in) :: mLayerMatricHeadPrime(:) ! prime vector of total water matric potential (m) ! input: pre-computed derivatives - real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) + real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy prime - real(rkind),intent(out) :: scalarCanairEnthalpyPrime ! prime enthalpy of the canopy air space (J m-3) - real(rkind),intent(out) :: scalarCanopyEnthalpyPrime ! prime enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! prime enthalpy of each snow+soil layer (J m-3) + real(rkind),intent(out) :: scalarCanairEnthalpyPrime ! prime enthalpy of the canopy air space (J m-3) + real(rkind),intent(out) :: scalarCanopyEnthalpyPrime ! prime enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! prime enthalpy of each snow+soil layer (J m-3) ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------------------------------- ! declare local variables character(len=128) :: cmessage ! error message in downwind routine @@ -139,17 +146,28 @@ subroutine t2enthalpyPrime(& real(rkind) :: integral ! integral of snow freezing curve real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head real(rkind) :: d_integral_dTk ! derivative of integral with temperature + real(rkind) :: dE ! derivative of enthalpy with temperature at layer temperature + real(rkind) :: dEcrit ! derivative of enthalpy with temperature at critical temperature + real(rkind) :: arg ! argument of hypergeometric function + real(rkind) :: gauss_hg_T ! hypergeometric function result + real(rkind) :: xConst ! constant in the freezing curve function (m K-1) + real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) + real(rkind) :: volFracPsiWat ! volumetric fraction of liquid water (-) ! enthalpy - real(rkind) :: enthVegP ! prime enthalpy of the vegetation (J m-3) - real(rkind) :: enthSoilP ! prime enthalpy of soil particles (J m-3) - real(rkind) :: enthMixP ! prime enthalpy of the mixed region, liquid+ice (J m-3) - real(rkind) :: enthLiqP ! prime enthalpy of the liquid region (J m-3) - real(rkind) :: enthIceP ! prime enthalpy of the ice region (J m-3) - real(rkind) :: enthAirP ! prime enthalpy of air (J m-3) - real(rkind) :: enthTemp ! enthalpy at the temperature of the control volume (J m-3) - real(rkind) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) - real(rkind) :: enthPhaseP ! prime enthalpy associated with phase change (J m-3) - real(rkind) :: enthWaterP ! prime enthalpy of total water (J m-3) + real(rkind) :: enthVegP ! prime enthalpy of the vegetation (J m-3) + real(rkind) :: enthSoilP ! prime enthalpy of soil particles (J m-3) + real(rkind) :: enthMixP ! prime enthalpy of the mixed region, liquid+ice (J m-3) + real(rkind) :: enthLiqP ! prime enthalpy of the liquid region (J m-3) + real(rkind) :: enthIceP ! prime enthalpy of the ice region (J m-3) + real(rkind) :: enthAirP ! prime enthalpy of air (J m-3) + real(rkind) :: enthTemp ! enthalpy at the temperature of the control volume (J m-3) + real(rkind) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) + real(rkind) :: enthTempP ! prime enthalpy at the temperature of the control volume (J m-3) + real(rkind) :: enthTcritP ! prime enthalpy at the critical temperature where all water is unfrozen (J m-3) + real(rkind) :: enthPhaseP ! prime enthalpy associated with phase change (J m-3) + real(rkind) :: enthWaterP ! prime enthalpy of total water (J m-3) + logical(lgt),parameter :: use_lookup=.true. ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function + logical(lgt),parameter :: quick_hyper=.false. ! flag to use a quick hypergeometric function, currently no difference between quick and slow hypergeometric functions ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures generalVars: associate(& @@ -203,10 +221,10 @@ subroutine t2enthalpyPrime(& case(iname_veg) ! association to necessary variables for vegetation vegVars: associate(& - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): [dp] canopy depth (m) - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! canopy depth (m) + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! maximum mass of vegetation (kg m-2) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! scaling parameter for the snow freezing curve (K-1) ) diffT = scalarCanopyTempTrial - Tfreeze @@ -230,14 +248,16 @@ subroutine t2enthalpyPrime(& ! association to necessary variables for snow snowVars: associate(& - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! scaling parameter for the snow freezing curve (K-1) ) diffT = mLayerTempTrial(iLayer) - Tfreeze ! diffT<0._rkind because snow is frozen integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) ! Note: VolFracLiq = d_integral_dTk*VolFracWat - enthLiqP = iden_water * Cp_water * ( mLayerVolFracWatPrime(iLayer)*integral + mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer)*d_integral_dTk ) - enthIceP = iden_water * Cp_ice * ( mLayerVolFracWatPrime(iLayer)*( diffT - integral ) + mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer)*( 1._rkind - d_integral_dTk ) ) + enthLiqP = iden_water * Cp_water * ( mLayerVolFracWatPrime(iLayer)*integral & + + mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer)*d_integral_dTk ) + enthIceP = iden_water * Cp_ice * ( mLayerVolFracWatPrime(iLayer)*( diffT - integral ) & + + mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer)*( 1._rkind - d_integral_dTk ) ) enthAirP = iden_air * Cp_air * ( mLayerTempPrime(iLayer) - mLayerVolFracWatPrime(iLayer) * ( (iden_water/iden_ice)*( diffT - integral ) + integral ) & - mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer)* ( (iden_water/iden_ice)*( 1._rkind - d_integral_dTk ) + d_integral_dTk ) ) @@ -246,17 +266,17 @@ subroutine t2enthalpyPrime(& end associate snowVars case(iname_soil) - - ! make association to variables in the data structures... + ! make association to variables for soil soilVars: associate(& - - ! associate model parameters soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex) , & ! intrinsic soil density (kg m-3) theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex) , & ! soil porosity (-) theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex) , & ! volumetric residual water content (-) vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) - + ! associate values in the lookup table + Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) + Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) + E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function ) ! end associate statement ! diagnostic variables @@ -269,61 +289,64 @@ subroutine t2enthalpyPrime(& ! *** compute enthalpy prime of water for unfrozen conditions if(mlayerTempTrial(iLayer)>=Tcrit)then - enthWaterP = iden_water * Cp_water * ( mLayerMatricHeadPrime(ixControlIndex)*dVolTot_dPsi0(ixControlIndex)*diffT + volFracWat*mLayerTempPrime(iLayer) ) ! valid for temperatures below freezing also + enthWaterP = iden_water * Cp_water * ( mLayerMatricHeadPrime(ixControlIndex)*dVolTot_dPsi0(ixControlIndex)*diffT & + + volFracWat*mLayerTempPrime(iLayer) ) ! valid for temperatures below freezing also ! *** compute enthalpy prime of water for frozen conditions else ! compute enthalpy of unfrozen and frozen water - enthLiqP = iden_water * Cp_water * ( mLayerMatricHeadPrime(ixControlIndex)*dVolTot_dPsi0(ixControlIndex)*(Tcrit - Tfreeze) + volFracWat*dTcrit_dPsi0 ) - enthIceP = iden_ice * Cp_ice * ( mLayerMatricHeadPrime(ixControlIndex)*dVolTot_dPsi0(ixControlIndex)*(mLayerTempTrial(iLayer) - Tcrit) + volFracWat*(mLayerTempPrime(iLayer) - dTcrit_dPsi0) ) - - ! compute enthalpy of the mixed region, liquid+ice - arg = (-vGn_alpha * mLayerMatricHeadLiqTrial(ixControlIndex))**vGn_n - gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - enthTemp = (iden_water * Cp_water - iden_ice * Cp_ice) * ( diffT * ( (theta_sat - theta_res)*gauss_hg_T ) + mLayerTempPrime(iLayer)* ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) ) - arg = (-vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n - gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - enthTcrit = (iden_water * Cp_water - iden_ice * Cp_ice) * diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) - - - endif ! (if frozen conditions) - - enthSoilP = soil_dens_intr * Cp_soil * (1._rkind - theta_sat)*mlayerTempPrime(iLayer) - enthAirP = iden_air * Cp_air * ( mLayerMatricHeadPrime(ixControlIndex)*dVolTot_dPsi0(ixControlIndex)*diffT + (1._rkind - theta_sat - volFracWat)*mlayerTempPrime(iLayer) ) - - ! *** compute the total enthalpy (J m-3) - mLayerEnthalpyPrime(iLayer) = enthWaterP + enthSoilP + enthAirP - - - - - - + enthLiqP = iden_water * Cp_water * mLayerMatricHeadPrime(ixControlIndex) * ( dVolTot_dPsi0(ixControlIndex)*(Tcrit - Tfreeze) + volFracWat*dTcrit_dPsi0 ) + enthIceP = iden_ice * Cp_ice * mLayerMatricHeadPrime(ixControlIndex) * ( dVolTot_dPsi0(ixControlIndex)*(mLayerTempTrial(iLayer) - Tcrit) + volFracWat*(mLayerTempPrime(iLayer) - dTcrit_dPsi0) ) ! compute enthalpy of the mixed region, liquid+ice - arg = (-vGn_alpha * mLayerMatricHeadLiqTrial(ixControlIndex))**vGn_n - gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - enthTemp = (iden_water * Cp_water - iden_ice * Cp_ice) * diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) & - arg = (-vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n - gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - enthTcrit = (iden_water * Cp_water - iden_ice * Cp_ice) * diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) & + if(use_lookup)then !cubic spline interpolation + ! calculate enthalpy at the temperature + call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),enthTemp,dE,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + enthTempP = mlayerTempPrime(iLayer) * dE + ! calculate enthalpy at the critical temperature + call splint(Tk,Ey,E2,Tcrit,enthTcrit,dEcrit,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + enthTcritP = mLayerMatricHeadPrime(ixControlIndex)* dTcrit_dPsi0 * dEcrit + + else ! hypergeometric function + ! calculate enthalpy at the temperature + ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice + ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations + xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) + mLayerPsiLiq = xConst*(mLayerTempTrial(iLayer) - Tfreeze) ! liquid water matric potential from the Clapeyron eqution + volFracPsiWat = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + arg = (-vGn_alpha * mLayerPsiLiq)**vGn_n + if(quick_hyper)then + gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + else + gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + endif + volFracPsiWat = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + enthTempP = (iden_water * Cp_water - iden_ice * Cp_ice) * ( diffT * (theta_sat - theta_res)* volFracPsiWat * xConst*mLayerTempPrime(iLayer)& + + mLayerTempPrime(iLayer) * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) ) + ! calculate enthalpy at the critical temperature + arg = (-vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n + if(quick_hyper)then + gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + else + gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + endif + enthTcritP = (iden_water * Cp_water - iden_ice * Cp_ice) * ( diffT * (theta_sat - theta_res)* volFracWat * mLayerMatricHeadPrime(ixControlIndex) & + + mLayerTempPrime(iLayer) * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) ) + endif ! calculate the enthalpy of water - enthMix = enthTemp - enthTcrit ! enthalpy of the liquid+ice mix - enthWater = enthMix + enthLiq + enthIce + enthMixP = enthTempP - enthTcritP ! enthalpy of the liquid+ice mix + enthWaterP = enthMixP + enthLiqP + enthIceP endif ! (if frozen conditions) - enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*diffT - enthAir = iden_air*Cp_air*(1._rkind - theta_sat - volFracWat)*diffT - - mLayerEnthalpy(iLayer) = enthWater + enthSoil + enthAir - - - - - + enthSoilP = soil_dens_intr * Cp_soil * (1._rkind - theta_sat)*mlayerTempPrime(iLayer) + enthAirP = iden_air * Cp_air * ( mLayerMatricHeadPrime(ixControlIndex)*dVolTot_dPsi0(ixControlIndex)*diffT & + + (1._rkind - theta_sat - volFracWat)*mlayerTempPrime(iLayer) ) + mLayerEnthalpyPrime(iLayer) = enthWaterP + enthSoilP + enthAirP end associate soilVars @@ -341,4 +364,37 @@ subroutine t2enthalpyPrime(& end subroutine t2enthalpyPrime +! ************************************************************************************************************************ +! private function hypergeometric: compute Gaussian hypergeometric function with iterative xpansion method. +! ************************************************************************************************************************ +function hypergeometric(a, b, c, z) + implicit none + real(rkind),intent(in) :: a, b, c, z ! input parameters + real(rkind) :: term, factorial ! local variables + real(rkind) :: hypergeometric ! output result + integer(i4b) :: n, max_iter ! iteration count variables + + max_iter = 1000 ! maximum number of iterations + + ! initialize + hypergeometric = 1._rkind + term = 1._rkind + factorial = 1._rkind + + do n = 1, max_iter + factorial = factorial * n + term = term * (a + n - 1) * (b + n - 1) / ((c + n - 1) * factorial) * z + hypergeometric = hypergeometric + term + + if (abs(term) < 1.e-6_rkind) exit ! convergence condition + + if (n == max_iter) then + ! handle non-convergence + write(*, *) "Warning: Hypergeometric function did not converge within the maximum number of iterations." + exit + end if + end do + +end function hypergeometric + end module t2enthalpyAddPrime_module diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 07b0b0913..02e941f15 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -47,6 +47,7 @@ module varSubstep_module var_flagVec, & ! data vector with variable length dimension (i4b) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! lookup tables model_options, & ! defines the model decisions in_type_varSubstep, & ! class for intent(in) arguments io_type_varSubstep, & ! class for intent(inout) arguments @@ -103,6 +104,7 @@ subroutine varSubstep(& io_varSubstep, & ! intent(inout) : model control ! input/output: data structures model_decisions, & ! intent(in) : model decisions + lookup_data, & ! intent(in) : lookup tables type_data, & ! intent(in) : type of vegetation and soil attr_data, & ! intent(in) : spatial attributes forc_data, & ! intent(in) : model forcing data @@ -134,6 +136,7 @@ subroutine varSubstep(& type(io_type_varSubstep),intent(inout) :: io_varSubstep ! model control ! input/output: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup),intent(in) :: lookup_data ! lookup tables type(var_i),intent(in) :: type_data ! type of vegetation and soil type(var_d),intent(in) :: attr_data ! spatial attributes type(var_d),intent(in) :: forc_data ! model forcing data @@ -344,6 +347,7 @@ subroutine varSubstep(& checkMassBalance, & ! intent(in): flag to check mass balance checkNrgBalance, & ! intent(in): flag to check energy balance ! input/output: data structures + lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes forc_data, & ! intent(in): model forcing data @@ -437,7 +441,7 @@ subroutine varSubstep(& ! update prognostic variables, update balances, and check them for possible step reduction if numrec or kinsol call updateProg(dtSubstep,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states doAdjustTemp,computeVegFlux,checkMassBalance, checkNrgBalance,ixHowHeatCap == enthalpyFD, & ! input: model control - model_decisions,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures + model_decisions,lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! output: balances, flags, and error control if(err/=0)then message=trim(message)//trim(cmessage) @@ -477,13 +481,13 @@ subroutine varSubstep(& if(ixCasNrg/=integerMissing) sumBalance(ixCasNrg) = sumBalance(ixCasNrg) + dtSubstep*balance(ixCasNrg) if(ixVegNrg/=integerMissing) sumBalance(ixVegNrg) = sumBalance(ixVegNrg) + dtSubstep*balance(ixVegNrg) if(nSnowSoilNrg>0) then - do ixLayer=1,nSnowSoilNrg + do concurrent (ixLayer=1:nLayers,ixSnowSoilNrg(ixLayer)/=integerMissing) if(ixSnowSoilNrg(ixLayer)/=integerMissing) sumBalance(ixSnowSoilNrg(ixLayer)) = sumBalance(ixSnowSoilNrg(ixLayer)) + dtSubstep*balance(ixSnowSoilNrg(ixLayer)) end do endif if(ixVegHyd/=integerMissing) sumBalance(ixVegHyd) = sumBalance(ixVegHyd) + dtSubstep*balance(ixVegHyd) if(nSnowSoilHyd>0) then - do ixLayer=1,nSnowSoilHyd + do concurrent (ixLayer=1:nLayers,ixSnowSoilHyd(ixLayer)/=integerMissing) if(ixSnowSoilHyd(ixLayer)/=integerMissing) sumBalance(ixSnowSoilHyd(ixLayer)) = sumBalance(ixSnowSoilHyd(ixLayer)) + dtSubstep*balance(ixSnowSoilHyd(ixLayer)) end do endif @@ -587,14 +591,14 @@ subroutine varSubstep(& if(ixCasNrg/=integerMissing) diag_data%var(iLookDIAG%balanceCasNrg)%dat(1) = sumBalance(ixCasNrg)/dt if(ixVegNrg/=integerMissing) diag_data%var(iLookDIAG%balanceVegNrg)%dat(1) = sumBalance(ixVegNrg)/dt if(nSnowSoilNrg>0) then - do ixLayer=1,nSnowSoilNrg - if(ixSnowSoilNrg(ixLayer)/=integerMissing) diag_data%var(iLookDIAG%balanceLayerNrg)%dat(ixLayer) = sumBalance(ixSnowSoilNrg(ixLayer))/dt + do concurrent (ixLayer=1:nLayers,ixSnowSoilNrg(ixLayer)/=integerMissing) + diag_data%var(iLookDIAG%balanceLayerNrg)%dat(ixLayer) = sumBalance(ixSnowSoilNrg(ixLayer))/dt end do endif if(ixVegHyd/=integerMissing) diag_data%var(iLookDIAG%balanceVegMass)%dat(1) = sumBalance(ixVegHyd)/dt if(nSnowSoilHyd>0) then - do ixLayer=1,nSnowSoilHyd - if(ixSnowSoilHyd(ixLayer)/=integerMissing) diag_data%var(iLookDIAG%balanceLayerMass)%dat(ixLayer) = sumBalance(ixSnowSoilHyd(ixLayer))/dt + do concurrent (ixLayer=1:nLayers,ixSnowSoilHyd(ixLayer)/=integerMissing) + diag_data%var(iLookDIAG%balanceLayerMass)%dat(ixLayer) = sumBalance(ixSnowSoilHyd(ixLayer))/dt end do endif if(ixAqWat/=integerMissing) diag_data%var(iLookDIAG%balanceAqMass)%dat(1) = sumBalance(ixAqWat)/dt @@ -614,7 +618,7 @@ end subroutine varSubstep ! ********************************************************************************************************** subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states doAdjustTemp,computeVegFlux,checkMassBalance, checkNrgBalance,use_enthalpyFD, & ! input: model control - model_decisions,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures + model_decisions,lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! input-output: balances, flags, and error control USE getVectorz_module,only:varExtract ! extract variables from the state vector #ifdef SUNDIALS_ACTIVE @@ -639,6 +643,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec logical(lgt) ,intent(in) :: use_enthalpyFD ! flag that using enthalpy finite difference and need to update ! data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup),intent(in) :: lookup_data ! lookup tables type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! indices for a local HRU type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU @@ -948,6 +953,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure ! input: state variables for the vegetation canopy scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) @@ -991,7 +997,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec !if(ixCasNrg/=integerMissing) balance(ixCasNrg) = resVec(ixCasNrg) ! should be equivalent to above, use for debugging if(ixVegNrg/=integerMissing) balance(ixVegNrg) = scalarCanopyEnthalpyTrial - scalarCanopyEnthalpy - fluxVec(ixVegNrg)*dt if(nSnowSoilNrg>0)then - do i=1,nSnowSoilNrg + do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) balance(ixSnowSoilNrg(i)) = mLayerEnthalpyTrial(i) - mLayerEnthalpy(i) - fluxVec(ixSnowSoilNrg(i))*dt enddo endif @@ -1106,8 +1112,8 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! resVec is the residual vector from the solver over dt if(ixVegHyd/=integerMissing) balance(ixVegHyd) = resVec(ixVegHyd) if(nSnowSoilHyd>0)then - do i=1,nSnowSoilHyd - if(ixSnowSoilHyd(i)/=integerMissing) balance(ixSnowSoilHyd(i)) = resVec(ixSnowSoilHyd(i)) + do concurrent (i=1:nLayers,ixSnowSoilHyd(i)/=integerMissing) + balance(ixSnowSoilHyd(i)) = resVec(ixSnowSoilHyd(i)) end do endif if(ixAqWat/=integerMissing) balance(ixAqWat) = resVec(ixAqWat) diff --git a/build/source/netcdf/def_output.f90 b/build/source/netcdf/def_output.f90 index e648d482b..7305e0c46 100644 --- a/build/source/netcdf/def_output.f90 +++ b/build/source/netcdf/def_output.f90 @@ -165,7 +165,8 @@ subroutine def_output(summaVersion,buildTime,gitBranch,gitHash,nGRU,nHRU,nSoil,i case('diag' ); call def_variab(ncid(iFreq),iFreq,needHRU,needTime,diag_meta, outputPrecision, err,cmessage) ! model diagnostic variables case('flux' ); call def_variab(ncid(iFreq),iFreq,needHRU,needTime,flux_meta, outputPrecision, err,cmessage) ! model fluxes case('bvar' ); call def_variab(ncid(iFreq),iFreq,needGRU,needTime,bvar_meta, outputPrecision, err,cmessage) ! basin-average variables - case('id' ); cycle ! ids -- see write_hru_info() ! ids -- see write_hru_info() + case('id' ); cycle ! ids -- see write_hru_info() + case('lookup'); cycle ! ids -- see write_hru_info() case default; err=20; message=trim(message)//'unable to identify lookup structure'; end select ! error handling From 54c1e5c7f9b5fe2f8a0e2aacaab6fb7b3c91c934 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 8 Dec 2023 12:56:49 +0900 Subject: [PATCH 1018/1472] Put flags in for if want to compute lookup tables or not. Should make some sort of parameter eventually. --- build/source/driver/summa_setup.f90 | 21 ++++-- build/source/engine/summaSolve4ida.f90 | 9 ++- build/source/engine/systemSolv.f90 | 6 +- build/source/engine/varSubstep.f90 | 93 ++++++++++++++------------ 4 files changed, 72 insertions(+), 57 deletions(-) diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index 940f91b2f..5df05eaa5 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -43,12 +43,13 @@ module summa_setup USE globalData,only:mpar_meta,bpar_meta ! parameter metadata structures ! look-up values for the choice of heat capacity computation -USE mDecisions_module,only: & - enthalpyFD ! heat capacity using enthalpy +USE mDecisions_module,only:& + closedForm,& ! heat capacity using closed form, not using enthalpy + enthalpyFD ! heat capacity using enthalpy ! named variables to define the decisions for snow layers USE mDecisions_module,only:& - sameRulesAllLayers, & ! SNTHERM option: same combination/sub-dividion rules applied to all layers + sameRulesAllLayers,& ! SNTHERM option: same combination/sub-dividion rules applied to all layers rulesDependLayerIndex ! CLM option: combination/sub-dividion rules depend on layer index ! named variables to define LAI decisions @@ -120,6 +121,7 @@ subroutine summa_paramSetup(summa1_struc, err, message) integer(i4b) :: jHRU,kHRU ! HRU indices integer(i4b) :: iGRU,iHRU ! looping variables integer(i4b) :: iVar ! looping variables + logical :: needLookup ! logical to decide if computing enthalpy lookup tables ! --------------------------------------------------------------------------------------- ! associate to elements in the data structure summaVars: associate(& @@ -150,6 +152,13 @@ subroutine summa_paramSetup(summa1_struc, err, message) ! initialize error control err=0; message='summa_paramSetup/' + ! decide if computing enthalpy lookup tables, if need enthalpy and not using hypergeometric function + ! NOTE: this should be replaced by a parameter of if want lookup table enthalpy, but for now it is hard-coded + ! need enthalpy if checkNrgBalance in varSubstep is turned on or using howHeatCap=enthalpyFD + ! then, need lookups if use_lookup=.true. in t2enthalpy (numrec or kin) or t2enthalpyPrime (ida) + needLookup = .true. + !if (model_decisions(iLookDECISIONS%howHeatCap)%iDecision == enthalpyFD) needLookup = .true. + ! initialize the start of the initialization call date_and_time(values=startSetup) @@ -306,14 +315,14 @@ subroutine summa_paramSetup(summa1_struc, err, message) call E2T_lookup(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! calculate a lookup table to compute enthalpy from temperature, need for enthalpyFD or if checkNrgBalance is true - !if(model_decisions(iLookDECISIONS%howHeatCap)%iDecision == enthalpyFD)then + ! calculate a lookup table to compute enthalpy from temperature + if(needLookup)then call T2E_lookup(gru_struc(iGRU)%hruInfo(iHRU)%nSoil, & ! intent(in): number of soil layers mparStruct%gru(iGRU)%hru(iHRU), & ! intent(in): parameter data structure lookupStruct%gru(iGRU)%hru(iHRU), & ! intent(inout): lookup table data structure err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - !endif + endif ! overwrite the vegetation height HVT(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex)) = mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%heightCanopyTop)%dat(1) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index d3cd9819e..031b154b5 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -63,16 +63,15 @@ module summaSolve4ida_module zLookup, & ! lookup tables model_options ! defines the model decisions +USE mDecisions_module,only:& + closedForm, & ! heat capacity using closed form, not using enthalpy + enthalpyFD ! heat capacity using enthalpy + ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: & qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization bigBucket, & ! a big bucket (lumped aquifer model) noExplicit ! no explicit groundwater parameterization - -! look-up values for the choice of heat capacity computation -USE mDecisions_module,only: & - closedForm, & ! heat capacity using closed form, not using enthalpy - enthalpyFD ! heat capacity using enthalpy ! look-up values for method used to compute derivative USE mDecisions_module,only: & diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 293b8b6b0..2cd49e801 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -79,8 +79,10 @@ module systemSolv_module model_options ! defines the model decisions ! look-up values for the choice of heat capacity computation -USE mDecisions_module,only:& - enthalpyFD ! heat capacity using enthalpy + USE mDecisions_module,only: & + closedForm, & ! heat capacity using closed form, not using enthalpy + enthalpyFD ! heat capacity using enthalpy + ! look-up values for the choice of groundwater representation (local-column, or single-basin) USE mDecisions_module,only:& diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 02e941f15..c7639d1ff 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -154,25 +154,25 @@ subroutine varSubstep(& ! * general local variables ! --------------------------------------------------------------------------------------- ! error control - character(LEN=256) :: cmessage ! error message of downwind routine + character(LEN=256) :: cmessage ! error message of downwind routine ! general local variables - integer(i4b) :: iVar ! index of variables in data structures - integer(i4b) :: iSoil ! index of soil layers - integer(i4b) :: ixLayer ! index in a given domain - integer(i4b),dimension(1) :: ixMin,ixMax ! bounds of a given flux vector + integer(i4b) :: iVar ! index of variables in data structures + integer(i4b) :: iSoil ! index of soil layers + integer(i4b) :: ixLayer ! index in a given domain + integer(i4b),dimension(1) :: ixMin,ixMax ! bounds of a given flux vector ! time stepping - real(rkind) :: dtSum ! sum of time from successful steps (seconds) - real(rkind) :: dt_wght ! weight given to a given flux calculation - real(rkind) :: dtSubstep ! length of a substep (s) - real(rkind) :: maxstep ! maximum time step length (seconds) - integer(i4b) :: nSteps ! number of time steps taken in solver + real(rkind) :: dtSum ! sum of time from successful steps (seconds) + real(rkind) :: dt_wght ! weight given to a given flux calculation + real(rkind) :: dtSubstep ! length of a substep (s) + real(rkind) :: maxstep ! maximum time step length (seconds) + integer(i4b) :: nSteps ! number of time steps taken in solver ! adaptive sub-stepping for the solution - logical(lgt) :: failedSubstep ! flag to denote success of substepping for a given split - integer(i4b) :: niter ! number of iterations taken - integer(i4b),parameter :: n_inc=5 ! minimum number of iterations to increase time step - integer(i4b),parameter :: n_dec=15 ! maximum number of iterations to decrease time step - real(rkind),parameter :: F_inc = 1.25_rkind ! factor used to increase time step - real(rkind),parameter :: F_dec = 0.90_rkind ! factor used to decrease time step + logical(lgt) :: failedSubstep ! flag to denote success of substepping for a given split + integer(i4b) :: niter ! number of iterations taken + integer(i4b),parameter :: n_inc=5 ! minimum number of iterations to increase time step + integer(i4b),parameter :: n_dec=15 ! maximum number of iterations to decrease time step + real(rkind),parameter :: F_inc = 1.25_rkind ! factor used to increase time step + real(rkind),parameter :: F_dec = 0.90_rkind ! factor used to decrease time step ! state and flux vectors (Note: nstate = in_varSubstep % nSubset) real(rkind) :: untappedMelt(in_varSubstep % nSubset) ! un-tapped melt energy (J m-3 s-1) real(rkind) :: stateVecInit(in_varSubstep % nSubset) ! initial state vector (mixed units) @@ -180,23 +180,24 @@ subroutine varSubstep(& real(rkind) :: stateVecPrime(in_varSubstep % nSubset) ! trial state vector (mixed units) type(var_dlength) :: flux_temp ! temporary model fluxes ! flags - logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt) :: waterBalanceError ! flag to denote that there is a water balance error - logical(lgt) :: nrgFluxModified ! flag to denote that the energy fluxes were modified + logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt) :: waterBalanceError ! flag to denote that there is a water balance error + logical(lgt) :: nrgFluxModified ! flag to denote that the energy fluxes were modified ! energy fluxes - real(rkind) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) - real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) - real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) - real(rkind) :: sumSoilCompress ! sum of total soil compression - real(rkind),allocatable :: sumLayerCompress(:) ! sum of soil compression by layer + real(rkind) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) + real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) + real(rkind) :: sumSoilCompress ! sum of total soil compression + real(rkind),allocatable :: sumLayerCompress(:) ! sum of soil compression by layer ! balances and residual vectors - real(rkind) :: fluxVec(in_varSubstep % nSubset) ! substep flux vector (mixed units) - real(rkind) :: resSink(in_varSubstep % nSubset) ! substep sink terms on the RHS of the state equation - real(qp) :: resVec(in_varSubstep % nSubset) ! substep residual vector - real(rkind) :: balance(in_varSubstep % nSubset) ! substep balance - real(rkind) :: sumBalance(in_varSubstep % nSubset) ! sum of substeps balance - logical(lgt),parameter :: checkMassBalance = .true. ! flag to check the mass balance - logical(lgt),parameter :: checkNrgBalance = .true. ! flag to check the energy balance + real(rkind) :: fluxVec(in_varSubstep % nSubset) ! substep flux vector (mixed units) + real(rkind) :: resSink(in_varSubstep % nSubset) ! substep sink terms on the RHS of the state equation + real(qp) :: resVec(in_varSubstep % nSubset) ! substep residual vector + real(rkind) :: balance(in_varSubstep % nSubset) ! substep balance + real(rkind) :: sumBalance(in_varSubstep % nSubset) ! sum of substeps balance + logical(lgt),parameter :: checkMassBalance = .true. ! flag to check the mass balance + logical(lgt),parameter :: checkNrgBalance = .true. ! flag to check the energy balance + logical(lgt) :: computeEnthalpy ! flag to compute enthalpy regardless of the model decision ! --------------------------------------------------------------------------------------- ! point to variables in the data structures @@ -272,6 +273,10 @@ subroutine varSubstep(& ! initialize flag for the success of the substepping failedMinimumStep=.false. + ! set the flag to compute enthalpy + computeEnthalpy = .false. + if(checkNrgBalance .or. ixHowHeatCap==enthalpyFD) computeEnthalpy = .true. ! need to get enthalpy regardles of other decisions + ! initialize the length of the substep dtSubstep = dtInit @@ -439,10 +444,10 @@ subroutine varSubstep(& endif ! update prognostic variables, update balances, and check them for possible step reduction if numrec or kinsol - call updateProg(dtSubstep,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states - doAdjustTemp,computeVegFlux,checkMassBalance, checkNrgBalance,ixHowHeatCap == enthalpyFD, & ! input: model control - model_decisions,lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures - fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! output: balances, flags, and error control + call updateProg(dtSubstep,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states + doAdjustTemp,computeVegFlux,checkMassBalance, checkNrgBalance,computeEnthalpy, & ! input: model control + model_decisions,lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures + fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! output: balances, flags, and error control if(err/=0)then message=trim(message)//trim(cmessage) if(err>0) return @@ -616,10 +621,10 @@ end subroutine varSubstep ! ********************************************************************************************************** ! private subroutine updateProg: update prognostic variables ! ********************************************************************************************************** -subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states - doAdjustTemp,computeVegFlux,checkMassBalance, checkNrgBalance,use_enthalpyFD, & ! input: model control - model_decisions,lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures - fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! input-output: balances, flags, and error control +subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states + doAdjustTemp,computeVegFlux,checkMassBalance, checkNrgBalance,computeEnthalpy, & ! input: model control + model_decisions,lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures + fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! input-output: balances, flags, and error control USE getVectorz_module,only:varExtract ! extract variables from the state vector #ifdef SUNDIALS_ACTIVE USE updateVarsWithPrime_module,only:updateVarsWithPrime ! update prognostic variables @@ -640,7 +645,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec real(rkind) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance logical(lgt) ,intent(in) :: checkNrgBalance ! flag to check the energy balance - logical(lgt) ,intent(in) :: use_enthalpyFD ! flag that using enthalpy finite difference and need to update + logical(lgt) ,intent(in) :: computeEnthalpy ! flag to compute enthalpy ! data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions type(zLookup),intent(in) :: lookup_data ! lookup tables @@ -944,9 +949,9 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! ---- - ! * check energy balance + ! * update enthalpy !------------------------ - if(checkNrgBalance .or. use_enthalpyFD)then ! update diagnostic enthalpy variables, don't do if not checking energy balance and closed form enthalpy + if(computeEnthalpy)then ! update diagnostic enthalpy variables, don't do if not checking energy balance and closed form enthalpy ! compute enthalpy at t_{n+1} call t2enthalpy(& ! input: data structures @@ -1005,7 +1010,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec endif ! if checking energy balance ! save the trial values - if(checkNrgBalance .or. use_enthalpyFD)then + if(computeEnthalpy)then scalarCanairEnthalpy = scalarCanairEnthalpyTrial mLayerEnthalpy = mLayerEnthalpyTrial scalarCanopyEnthalpy = scalarCanopyEnthalpyTrial @@ -1268,7 +1273,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec endif ! (if energy state variables exist) ! ----- - ! * update enthalpy as a diagnostic variable... + ! * update enthalpy as a diagnostic variable... if computeEnthalpy is false this will not change ! -------------------------------- scalarCanairEnthalpy = scalarCanairEnthalpyTrial scalarCanopyEnthalpy = scalarCanopyEnthalpyTrial From 2aa0cec5b87c2bb692a462f9f2fa8819eb26329a Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 8 Dec 2023 05:51:44 -0600 Subject: [PATCH 1019/1472] Added debug output for operator splitting selector object -- under development. --- build/source/engine/opSplittin.f90 | 31 ++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 407cdd7a0..97362c361 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -296,6 +296,9 @@ subroutine opSplittin(& ! loop control logical(lgt) :: exit_coupling,exit_stateTypeSplitting,exit_stateThenDomain,exit_domainSplit,exit_solution,exit_stateSplit logical(lgt) :: cycle_coupling,cycle_stateTypeSplitting,cycle_stateThenDomain,cycle_domainSplit,cycle_solution,cycle_stateSplit + ! debug testing + logical(lgt), parameter :: test_flag=.false. ! to create test output + logical(lgt) :: stop_flag ! to control stopping ! ------------------------ classes for subroutine arguments (classes defined in data_types module) ------------------------ ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** type(in_type_stateFilter) :: in_stateFilter; type(out_type_stateFilter) :: out_stateFilter; ! stateFilter arguments @@ -304,6 +307,12 @@ subroutine opSplittin(& ! ------------------------------------------------------------------------------------------------------------------------- type(split_select_type),allocatable :: split_select(:) ! class object for selecting operator splitting methods + ! *** Initialize Split Selector Object *** + stop_flag=.false. + if (test_flag.eqv..true.) then + call initialize_split_select + end if + call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs coupling: do ixCoupling=1,nCoupling ! loop through different coupling strategies @@ -321,7 +330,14 @@ subroutine opSplittin(& call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs stateSplit: do iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) - +!!SJT +! if (ixSolution==nSolutions) then +! write(*,*) "nStateSplit=",nStateSplit +! write(*,*) ixCoupling,iStateTypeSplit,ixStateThenDomain,iDomainSplit,ixSolution,iStateSplit,nState +! stop_flag=.true. +! if (stop_flag.eqv..true.) stop +! end if +!!End SJT ! define state subsets for a given split... call update_stateFilter; if (return_flag.eqv..true.) return ! get the mask for the state subset - return for a non-zero error code call validate_split ! verify that the split is valid @@ -377,7 +393,10 @@ subroutine initialize_split_select call get_nCoupling; if (return_flag.eqv..true.) return ! get nCoupling value call get_nStateTypeSplit_tryDomainSplit(nCoupling); if (return_flag.eqv..true.) return ! get nStateTypeSplit and tryDomainSplit values call get_nDomainSplit(1+tryDomainSplit); if (return_flag.eqv..true.) return ! get nDomainSplit value - nSplit=nCoupling*nStateTypeSplit*(1+tryDomainSplit)*nDomainSplit*nSolutions*nStateSplit +! add update to stateMask here +! call get_nStateSplit(nSolutions); if (return_flag.eqv..true.) return ! get nStateSplit value +! nSplit=nCoupling*nStateTypeSplit*(1+tryDomainSplit)*nDomainSplit*nSolutions*nStateSplit + if (test_flag.eqv..true.) print *, "Test Ouput: ",nCoupling,nStateTypeSplit,tryDomainSplit,nDomainSplit ! ! opSplittin loop structure ! call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs @@ -717,14 +736,6 @@ subroutine initialize_stateSplit if (.not.firstInnerStep) firstFluxCall=.false. call get_nStateSplit(ixSolution); if (return_flag.eqv..true.) return ! get nStateSplit value -- return if error occurs -! ! get the number of split layers -! select case(ixSolution) -! case(vector); nStateSplit=1 -! case(scalar); nStateSplit=count(stateMask) -! case default; err=20; message=trim(message)//'unknown solution method'; -! return_flag=.true. ! return statement required in opSplittin -! return -! end select end subroutine initialize_stateSplit subroutine get_nStateSplit(ixSolution_value) From 21b7bb8ae017788f55aa014a6843b60b4e2fb422 Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 8 Dec 2023 05:52:50 -0600 Subject: [PATCH 1020/1472] Refactoring updates for the stateFilter routine in opSplittin.f90. --- build/source/engine/opSplittin.f90 | 234 ++++++++++++++++------------- 1 file changed, 132 insertions(+), 102 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 97362c361..f5cea8fc2 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -1088,81 +1088,65 @@ end subroutine opSplittin ! ********************************************************************************************************** subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) - USE indexState_module,only:indxSubset ! get state indices - implicit none - ! input - type(in_type_stateFilter),intent(in) :: in_stateFilter ! indices - type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU - ! output - logical(lgt),intent(out) :: stateMask(:) ! mask defining desired state variables - type(out_type_stateFilter),intent(out) :: out_stateFilter ! number of selected state variables for a given split and error control - ! local - integer(i4b),allocatable :: ixSubset(:) ! list of indices in the state subset - character(len=256) :: cmessage ! error message - ! -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ! data structures - associate(& - ! indices for splitting methods - ixCoupling => in_stateFilter % ixCoupling ,& ! intent(in): [i4b] index of coupling method (1,2) - ixSolution => in_stateFilter % ixSolution ,& ! intent(in): [i4b] index of solution method (1,2) - ixStateThenDomain => in_stateFilter % ixStateThenDomain ,& ! intent(in): [i4b] switch between full domain and sub domains - iStateTypeSplit => in_stateFilter % iStateTypeSplit ,& ! intent(in): [i4b] index of the state type split - iDomainSplit => in_stateFilter % iDomainSplit ,& ! intent(in): [i4b] index of the domain split - iStateSplit => in_stateFilter % iStateSplit ,& ! intent(in): [i4b] index of the layer split - ! indices of model state variables - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) - ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain - ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain - ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain - ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain - ixWatAquifer => indx_data%var(iLookINDEX%ixWatAquifer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for water storage in the aquifer - ixAllState => indx_data%var(iLookINDEX%ixAllState)%dat ,& ! intent(in): [i4b(:)] list of indices for all model state variables (1,2,3,...nState) - ! number of layers - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers - ! output - nSubset => out_stateFilter % nSubset ,& ! intent(out): number of selected state variables for a given split - err => out_stateFilter % err ,& ! intent(out): error code - message => out_stateFilter % cmessage & ! intent(out): error message - ) ! data structures - ! -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='stateFilter/' - - ! identify splitting option - select case(ixCoupling) - - ! ----- - ! - fully coupled... - ! ------------------ - - ! use all state variables - case(fullyCoupled); stateMask(:) = .true. - - ! ----- - ! - splitting by state type... - ! ---------------------------- - - ! initial split by state type - case(stateTypeSplit) - - ! switch between full domain and sub domains - select case(ixStateThenDomain) - - ! split into energy and mass - case(fullDomain) - select case(iStateTypeSplit) - case(nrgSplit); stateMask = (ixStateType==iname_nrgCanair .or. ixStateType==iname_nrgCanopy .or. ixStateType==iname_nrgLayer) - case(massSplit); stateMask = (ixStateType==iname_liqCanopy .or. ixStateType==iname_liqLayer .or. ixStateType==iname_lmpLayer .or. ixStateType==iname_watAquifer) - case default; err=20; message=trim(message)//'unable to identify split based on state type'; return - end select - - ! split into vegetation, snow, and soil - case(subDomain) + USE indexState_module,only:indxSubset ! get state indices + implicit none + ! input + type(in_type_stateFilter),intent(in) :: in_stateFilter ! indices + type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU + ! output + logical(lgt),intent(out) :: stateMask(:) ! mask defining desired state variables + type(out_type_stateFilter),intent(out) :: out_stateFilter ! number of selected state variables for a given split and error control + ! local + integer(i4b),allocatable :: ixSubset(:) ! list of indices in the state subset + character(len=256) :: cmessage ! error message + logical(lgt) :: return_flag ! flag to indicate a return + ! ---------------------------------------------------------------------------------------------------------------------------------------------------- + ! data structures + associate(& + ! indices for splitting methods + ixCoupling => in_stateFilter % ixCoupling ,& ! intent(in): [i4b] index of coupling method (1,2) + ixStateThenDomain => in_stateFilter % ixStateThenDomain ) ! intent(in): [i4b] switch between full domain and sub domains + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + + associate(err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message + err=0; message='stateFilter/' ! initialize error control + end associate - ! define state mask + ! identify splitting option + select case(ixCoupling) + + ! *** fully coupled *** + case(fullyCoupled) + call get_fullyCoupled_stateMask + + ! *** splitting by state type *** + case(stateTypeSplit) ! initial split by state type + + ! switch between full domain and sub domains + select case(ixStateThenDomain) + + ! split into energy and mass + case(fullDomain) + call get_fullDomain_stateMask; if (return_flag.eqv..true.) return + + ! split into vegetation, snow, and soil + case(subDomain) + + ! define state mask + associate(& + iStateTypeSplit => in_stateFilter % iStateTypeSplit ,& ! intent(in): [i4b] index of the state type split + iDomainSplit => in_stateFilter % iDomainSplit ,& ! intent(in): [i4b] index of the domain split + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers + ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain + ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + ixWatAquifer => indx_data%var(iLookINDEX%ixWatAquifer)%dat,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for water storage in the aquifer + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message stateMask=.false. ! (initialize state mask) select case(iStateTypeSplit) @@ -1170,10 +1154,10 @@ subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) case(nrgSplit) select case(iDomainSplit) case(vegSplit) - if(ixNrgCanair(1)/=integerMissing) stateMask(ixNrgCanair) = .true. ! energy of the canopy air space - if(ixNrgCanopy(1)/=integerMissing) stateMask(ixNrgCanopy) = .true. ! energy of the vegetation canopy + if (ixNrgCanair(1)/=integerMissing) stateMask(ixNrgCanair) = .true. ! energy of the canopy air space + if (ixNrgCanopy(1)/=integerMissing) stateMask(ixNrgCanopy) = .true. ! energy of the vegetation canopy stateMask(ixNrgLayer(1)) = .true. ! energy of the upper-most layer in the snow+soil domain - case(snowSplit); if(nSnow>1) stateMask(ixNrgLayer(2:nSnow)) = .true. ! NOTE: (2:) because the top layer in the snow+soil domain included in vegSplit + case(snowSplit); if (nSnow>1) stateMask(ixNrgLayer(2:nSnow)) = .true. ! NOTE: (2:) because the top layer in the snow+soil domain included in vegSplit case(soilSplit); stateMask(ixNrgLayer(max(2,nSnow+1):nLayers)) = .true. ! NOTE: max(2,nSnow+1) gives second layer unless more than 2 snow layers case(aquiferSplit) ! do nothing: no energy state variable for the aquifer domain case default; err=20; message=trim(message)//'unable to identify model sub-domain'; return @@ -1182,50 +1166,96 @@ subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) ! define mask for water case(massSplit) select case(iDomainSplit) - case(vegSplit); if(ixHydCanopy(1)/=integerMissing) stateMask(ixHydCanopy) = .true. ! hydrology of the vegetation canopy + case(vegSplit); if (ixHydCanopy(1)/=integerMissing) stateMask(ixHydCanopy) = .true. ! hydrology of the vegetation canopy case(snowSplit); stateMask(ixHydLayer(1:nSnow)) = .true. ! snow hydrology case(soilSplit); stateMask(ixHydLayer(nSnow+1:nLayers)) = .true. ! soil hydrology - case(aquiferSplit); if(ixWatAquifer(1)/=integerMissing) stateMask(ixWatAquifer) = .true. ! aquifer storage + case(aquiferSplit); if (ixWatAquifer(1)/=integerMissing) stateMask(ixWatAquifer) = .true. ! aquifer storage case default; err=20; message=trim(message)//'unable to identify model sub-domain'; return end select ! check case default; err=20; message=trim(message)//'unable to identify the state type'; return end select ! (split based on state type) + end associate + + ! check + case default + associate(err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message + + err=20; message=trim(message)//'unable to identify the switch between full domains and sub domains'; return + end associate + end select ! (switch between full domains and sub domains) + + ! check + case default + associate(err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message + err=20; message=trim(message)//'unable to identify coupling method'; return + end associate + end select ! (selecting solution method) - ! check - case default; err=20; message=trim(message)//'unable to identify the switch between full domains and sub domains'; return - end select ! (switch between full domains and sub domains) + call identify_scalar_solutions; if (return_flag.eqv..true.) return ! identify scalar solutions -- return if error occurs - ! check - case default; err=20; message=trim(message)//'unable to identify coupling method'; return - end select ! (selecting solution method) + ! get the number of selected state variables + associate(nSubset => out_stateFilter % nSubset) ! intent(out): number of selected state variables for a given split + nSubset = count(stateMask) + end associate - ! identify scalar solutions - if(ixSolution==scalar)then + ! end associations + end associate - ! get the subset of indices - call indxSubset(ixSubset, ixAllState, stateMask, err, cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif +contains - ! get the mask - stateMask(:) = .false. - stateMask( ixSubset(iStateSplit) ) = .true. + subroutine get_fullyCoupled_stateMask + ! *** Get fully coupled stateMask *** + stateMask(:) = .true. ! use all state variables + end subroutine get_fullyCoupled_stateMask + + subroutine get_fullDomain_stateMask + ! *** Get full domain stateMask *** + return_flag=.false. ! initialize flag + associate(ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) + iStateTypeSplit => in_stateFilter % iStateTypeSplit ,& ! intent(in): [i4b] index of the state type split + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message + select case(iStateTypeSplit) + case(nrgSplit); stateMask = (ixStateType==iname_nrgCanair .or. ixStateType==iname_nrgCanopy .or. ixStateType==iname_nrgLayer) + case(massSplit); stateMask = (ixStateType==iname_liqCanopy .or. ixStateType==iname_liqLayer .or. ixStateType==iname_lmpLayer .or. ixStateType==iname_watAquifer) + case default; err=20; message=trim(message)//'unable to identify split based on state type'; return_flag=.true.; return + end select + end associate + end subroutine get_fullDomain_stateMask + + subroutine identify_scalar_solutions + ! *** Identify scalar solutions *** + return_flag=.false. ! initialize flag + associate(ixAllState => indx_data%var(iLookINDEX%ixAllState)%dat ,& ! intent(in): [i4b(:)] list of indices for all model state variables (1,2,3,...nState) + ixSolution => in_stateFilter % ixSolution ,& ! intent(in): [i4b] index of solution method (1,2) + iStateSplit => in_stateFilter % iStateSplit ,& ! intent(in): [i4b] index of the layer split + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message + if (ixSolution==scalar) then - ! check - if(count(stateMask)/=1)then - message=trim(message)//'expect size=1 (scalar)' - err=20; return - endif + ! get the subset of indices + call indxSubset(ixSubset, ixAllState, stateMask, err, cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if - endif + ! get the mask + stateMask(:) = .false. + stateMask( ixSubset(iStateSplit) ) = .true. - ! get the number of selected state variables - nSubset = count(stateMask) + ! check + if (count(stateMask)/=1) then + message=trim(message)//'expect size=1 (scalar)' + err=20; return_flag=.true.; return + end if - ! end associations + end if end associate + end subroutine identify_scalar_solutions + end subroutine stateFilter end module opSplittin_module From 64238a2eb51fa93f5386b8ab11ad8b5cb0b2d71a Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 9 Dec 2023 05:18:13 -0600 Subject: [PATCH 1021/1472] Modularization of the stateFilter subroutine up to the mass and energy operator splitting methods. --- build/source/engine/opSplittin.f90 | 224 +++++++++++++++-------------- 1 file changed, 115 insertions(+), 109 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index f5cea8fc2..694d7fcf5 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -1102,117 +1102,59 @@ subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) logical(lgt) :: return_flag ! flag to indicate a return ! ---------------------------------------------------------------------------------------------------------------------------------------------------- ! data structures - associate(& - ! indices for splitting methods - ixCoupling => in_stateFilter % ixCoupling ,& ! intent(in): [i4b] index of coupling method (1,2) - ixStateThenDomain => in_stateFilter % ixStateThenDomain ) ! intent(in): [i4b] switch between full domain and sub domains - ! ------------------------------------------------------------------------------------------------------------------------------------------------- + associate(ixCoupling => in_stateFilter % ixCoupling,& ! intent(in): [i4b] index of coupling method (1,2) + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message - associate(err => out_stateFilter % err ,& ! intent(out): error code - message => out_stateFilter % cmessage ) ! intent(out): error message - err=0; message='stateFilter/' ! initialize error control - end associate - - ! identify splitting option - select case(ixCoupling) - - ! *** fully coupled *** - case(fullyCoupled) - call get_fullyCoupled_stateMask - - ! *** splitting by state type *** - case(stateTypeSplit) ! initial split by state type - - ! switch between full domain and sub domains - select case(ixStateThenDomain) - - ! split into energy and mass - case(fullDomain) - call get_fullDomain_stateMask; if (return_flag.eqv..true.) return - - ! split into vegetation, snow, and soil - case(subDomain) - - ! define state mask - associate(& - iStateTypeSplit => in_stateFilter % iStateTypeSplit ,& ! intent(in): [i4b] index of the state type split - iDomainSplit => in_stateFilter % iDomainSplit ,& ! intent(in): [i4b] index of the domain split - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers - ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain - ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain - ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain - ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain - ixWatAquifer => indx_data%var(iLookINDEX%ixWatAquifer)%dat,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for water storage in the aquifer - err => out_stateFilter % err ,& ! intent(out): error code - message => out_stateFilter % cmessage ) ! intent(out): error message - stateMask=.false. ! (initialize state mask) - select case(iStateTypeSplit) - - ! define mask for energy - case(nrgSplit) - select case(iDomainSplit) - case(vegSplit) - if (ixNrgCanair(1)/=integerMissing) stateMask(ixNrgCanair) = .true. ! energy of the canopy air space - if (ixNrgCanopy(1)/=integerMissing) stateMask(ixNrgCanopy) = .true. ! energy of the vegetation canopy - stateMask(ixNrgLayer(1)) = .true. ! energy of the upper-most layer in the snow+soil domain - case(snowSplit); if (nSnow>1) stateMask(ixNrgLayer(2:nSnow)) = .true. ! NOTE: (2:) because the top layer in the snow+soil domain included in vegSplit - case(soilSplit); stateMask(ixNrgLayer(max(2,nSnow+1):nLayers)) = .true. ! NOTE: max(2,nSnow+1) gives second layer unless more than 2 snow layers - case(aquiferSplit) ! do nothing: no energy state variable for the aquifer domain - case default; err=20; message=trim(message)//'unable to identify model sub-domain'; return - end select - - ! define mask for water - case(massSplit) - select case(iDomainSplit) - case(vegSplit); if (ixHydCanopy(1)/=integerMissing) stateMask(ixHydCanopy) = .true. ! hydrology of the vegetation canopy - case(snowSplit); stateMask(ixHydLayer(1:nSnow)) = .true. ! snow hydrology - case(soilSplit); stateMask(ixHydLayer(nSnow+1:nLayers)) = .true. ! soil hydrology - case(aquiferSplit); if (ixWatAquifer(1)/=integerMissing) stateMask(ixWatAquifer) = .true. ! aquifer storage - case default; err=20; message=trim(message)//'unable to identify model sub-domain'; return - end select - - ! check - case default; err=20; message=trim(message)//'unable to identify the state type'; return - end select ! (split based on state type) - end associate - - ! check - case default - associate(err => out_stateFilter % err ,& ! intent(out): error code - message => out_stateFilter % cmessage ) ! intent(out): error message - - err=20; message=trim(message)//'unable to identify the switch between full domains and sub domains'; return - end associate - end select ! (switch between full domains and sub domains) - - ! check - case default - associate(err => out_stateFilter % err ,& ! intent(out): error code - message => out_stateFilter % cmessage ) ! intent(out): error message - err=20; message=trim(message)//'unable to identify coupling method'; return - end associate - end select ! (selecting solution method) - - call identify_scalar_solutions; if (return_flag.eqv..true.) return ! identify scalar solutions -- return if error occurs + err=0; message='stateFilter/'; return_flag=.false. ! initialize error control + + ! identify splitting option + select case(ixCoupling) + ! *** fully coupled *** + case(fullyCoupled); call fullyCoupled_stateMask ! get stateMask for fully coupled method + ! *** splitting by state type *** + case(stateTypeSplit) ! initial split by state type + call stateTypeSplit_stateMask; if (return_flag.eqv..true.) return ! get stateMask for state split method -- return if error + ! check + case default; err=20; message=trim(message)//'unable to identify coupling method'; return + end select ! (selecting solution method) + end associate - ! get the number of selected state variables - associate(nSubset => out_stateFilter % nSubset) ! intent(out): number of selected state variables for a given split - nSubset = count(stateMask) - end associate + call identify_scalar_solutions; if (return_flag.eqv..true.) return ! identify scalar solutions -- return if error occurs - ! end associations + ! get the number of selected state variables + associate(nSubset => out_stateFilter % nSubset) ! intent(out): number of selected state variables for a given split + nSubset = count(stateMask) end associate contains - subroutine get_fullyCoupled_stateMask + subroutine fullyCoupled_stateMask ! *** Get fully coupled stateMask *** stateMask(:) = .true. ! use all state variables - end subroutine get_fullyCoupled_stateMask + end subroutine fullyCoupled_stateMask - subroutine get_fullDomain_stateMask + subroutine stateTypeSplit_stateMask + ! *** Get state type split stateMask *** + return_flag=.false. ! initialize flag + ! switch between full domain and sub domains + associate(& + ixStateThenDomain => in_stateFilter % ixStateThenDomain ,& ! intent(in): [i4b] switch between full domain and sub domains + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message + select case(ixStateThenDomain) + ! split into energy and mass + case(fullDomain); call stateTypeSplit_fullDomain_stateMask; if (return_flag.eqv..true.) return + ! split into vegetation, snow, and soil + case(subDomain); call stateTypeSplit_subDomain_stateMask; if (return_flag.eqv..true.) return + ! check + case default + err=20; message=trim(message)//'unable to identify the switch between full domains and sub domains'; return_flag=.true.; return + end select + end associate + end subroutine stateTypeSplit_stateMask + + subroutine stateTypeSplit_fullDomain_stateMask ! *** Get full domain stateMask *** return_flag=.false. ! initialize flag associate(ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) @@ -1220,12 +1162,80 @@ subroutine get_fullDomain_stateMask err => out_stateFilter % err ,& ! intent(out): error code message => out_stateFilter % cmessage ) ! intent(out): error message select case(iStateTypeSplit) - case(nrgSplit); stateMask = (ixStateType==iname_nrgCanair .or. ixStateType==iname_nrgCanopy .or. ixStateType==iname_nrgLayer) - case(massSplit); stateMask = (ixStateType==iname_liqCanopy .or. ixStateType==iname_liqLayer .or. ixStateType==iname_lmpLayer .or. ixStateType==iname_watAquifer) - case default; err=20; message=trim(message)//'unable to identify split based on state type'; return_flag=.true.; return + case(nrgSplit); stateMask = (ixStateType==iname_nrgCanair .or. ixStateType==iname_nrgCanopy .or. ixStateType==iname_nrgLayer) + case(massSplit); stateMask = (ixStateType==iname_liqCanopy .or. ixStateType==iname_liqLayer .or. ixStateType==iname_lmpLayer .or. ixStateType==iname_watAquifer) + case default; err=20; message=trim(message)//'unable to identify split based on state type'; return_flag=.true.; return + end select + end associate + end subroutine stateTypeSplit_fullDomain_stateMask + + subroutine stateTypeSplit_subDomain_stateMask + ! *** Get subdomain stateMask *** + return_flag=.false. ! initialize flag + ! define state mask + associate(& + iStateTypeSplit => in_stateFilter % iStateTypeSplit ,& ! intent(in): [i4b] index of the state type split + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message + stateMask=.false. ! initialize state mask + select case(iStateTypeSplit) + ! define mask for energy + case(nrgSplit); call stateTypeSplit_subDomain_nrgSplit_stateMask; if (return_flag.eqv..true.) return + ! define mask for water + case(massSplit); call stateTypeSplit_subDomain_massSplit_stateMask; if (return_flag.eqv..true.) return + + ! check + case default; err=20; message=trim(message)//'unable to identify the state type'; return_flag=.true.; return + end select ! (split based on state type) + end associate + end subroutine stateTypeSplit_subDomain_stateMask + + subroutine stateTypeSplit_subDomain_nrgSplit_stateMask + ! *** Get subdomain energy split stateMask *** + return_flag=.false. ! initialize flag + associate(& + iDomainSplit => in_stateFilter % iDomainSplit ,& ! intent(in): [i4b] index of the domain split + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers + ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain + ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message + select case(iDomainSplit) + case(vegSplit) + if (ixNrgCanair(1)/=integerMissing) stateMask(ixNrgCanair) = .true. ! energy of the canopy air space + if (ixNrgCanopy(1)/=integerMissing) stateMask(ixNrgCanopy) = .true. ! energy of the vegetation canopy + stateMask(ixNrgLayer(1)) = .true. ! energy of the upper-most layer in the snow+soil domain + case(snowSplit); if (nSnow>1) stateMask(ixNrgLayer(2:nSnow)) = .true. ! NOTE: (2:) because the top layer in the snow+soil domain included in vegSplit + case(soilSplit); stateMask(ixNrgLayer(max(2,nSnow+1):nLayers)) = .true. ! NOTE: max(2,nSnow+1) gives second layer unless more than 2 snow layers + case(aquiferSplit) ! do nothing: no energy state variable for the aquifer domain + case default; err=20; message=trim(message)//'unable to identify model sub-domain'; return_flag=.true.; return end select end associate - end subroutine get_fullDomain_stateMask + end subroutine stateTypeSplit_subDomain_nrgSplit_stateMask + + subroutine stateTypeSplit_subDomain_massSplit_stateMask + ! *** Get subdomain mass split stateMask *** + return_flag=.false. ! initialize flag + associate(& + iDomainSplit => in_stateFilter % iDomainSplit ,& ! intent(in): [i4b] index of the domain split + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + ixWatAquifer => indx_data%var(iLookINDEX%ixWatAquifer)%dat,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for water storage in the aquifer + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message + select case(iDomainSplit) + case(vegSplit); if (ixHydCanopy(1)/=integerMissing) stateMask(ixHydCanopy) = .true. ! hydrology of the vegetation canopy + case(snowSplit); stateMask(ixHydLayer(1:nSnow)) = .true. ! snow hydrology + case(soilSplit); stateMask(ixHydLayer(nSnow+1:nLayers)) = .true. ! soil hydrology + case(aquiferSplit); if (ixWatAquifer(1)/=integerMissing) stateMask(ixWatAquifer) = .true. ! aquifer storage + case default; err=20; message=trim(message)//'unable to identify model sub-domain'; return_flag=.true.; return + end select + end associate + end subroutine stateTypeSplit_subDomain_massSplit_stateMask subroutine identify_scalar_solutions ! *** Identify scalar solutions *** @@ -1236,21 +1246,17 @@ subroutine identify_scalar_solutions err => out_stateFilter % err ,& ! intent(out): error code message => out_stateFilter % cmessage ) ! intent(out): error message if (ixSolution==scalar) then - ! get the subset of indices call indxSubset(ixSubset, ixAllState, stateMask, err, cmessage) if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if - ! get the mask stateMask(:) = .false. stateMask( ixSubset(iStateSplit) ) = .true. - ! check if (count(stateMask)/=1) then message=trim(message)//'expect size=1 (scalar)' err=20; return_flag=.true.; return end if - end if end associate From c407f07c8204d80d650e2b9bd465a68b7386461e Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 12 Dec 2023 04:57:41 -0600 Subject: [PATCH 1022/1472] Further modularized stateFilter by creating new subroutines in its contains block -- all available splits are now accounted for. --- build/source/engine/opSplittin.f90 | 114 ++++++++++++++++++++++------- 1 file changed, 89 insertions(+), 25 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 694d7fcf5..4d0928783 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -1157,18 +1157,31 @@ end subroutine stateTypeSplit_stateMask subroutine stateTypeSplit_fullDomain_stateMask ! *** Get full domain stateMask *** return_flag=.false. ! initialize flag - associate(ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) - iStateTypeSplit => in_stateFilter % iStateTypeSplit ,& ! intent(in): [i4b] index of the state type split + associate(iStateTypeSplit => in_stateFilter % iStateTypeSplit ,& ! intent(in): [i4b] index of the state type split err => out_stateFilter % err ,& ! intent(out): error code message => out_stateFilter % cmessage ) ! intent(out): error message select case(iStateTypeSplit) - case(nrgSplit); stateMask = (ixStateType==iname_nrgCanair .or. ixStateType==iname_nrgCanopy .or. ixStateType==iname_nrgLayer) - case(massSplit); stateMask = (ixStateType==iname_liqCanopy .or. ixStateType==iname_liqLayer .or. ixStateType==iname_lmpLayer .or. ixStateType==iname_watAquifer) + case(nrgSplit); call stateTypeSplit_fullDomain_nrgSplit_stateMask + case(massSplit); call stateTypeSplit_fullDomain_massSplit_stateMask case default; err=20; message=trim(message)//'unable to identify split based on state type'; return_flag=.true.; return end select end associate end subroutine stateTypeSplit_fullDomain_stateMask + subroutine stateTypeSplit_fullDomain_nrgSplit_stateMask + ! *** Get state type full domain energy split stateMask *** + associate(ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat) ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) + stateMask = (ixStateType==iname_nrgCanair .or. ixStateType==iname_nrgCanopy .or. ixStateType==iname_nrgLayer) + end associate + end subroutine stateTypeSplit_fullDomain_nrgSplit_stateMask + + subroutine stateTypeSplit_fullDomain_massSplit_stateMask + ! *** Get state type full domain mass split stateMask *** + associate(ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat) ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) + stateMask = (ixStateType==iname_liqCanopy .or. ixStateType==iname_liqLayer .or. ixStateType==iname_lmpLayer .or. ixStateType==iname_watAquifer) + end associate + end subroutine stateTypeSplit_fullDomain_massSplit_stateMask + subroutine stateTypeSplit_subDomain_stateMask ! *** Get subdomain stateMask *** return_flag=.false. ! initialize flag @@ -1195,48 +1208,99 @@ subroutine stateTypeSplit_subDomain_nrgSplit_stateMask return_flag=.false. ! initialize flag associate(& iDomainSplit => in_stateFilter % iDomainSplit ,& ! intent(in): [i4b] index of the domain split - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers - ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain - ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain - ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain err => out_stateFilter % err ,& ! intent(out): error code message => out_stateFilter % cmessage ) ! intent(out): error message select case(iDomainSplit) - case(vegSplit) - if (ixNrgCanair(1)/=integerMissing) stateMask(ixNrgCanair) = .true. ! energy of the canopy air space - if (ixNrgCanopy(1)/=integerMissing) stateMask(ixNrgCanopy) = .true. ! energy of the vegetation canopy - stateMask(ixNrgLayer(1)) = .true. ! energy of the upper-most layer in the snow+soil domain - case(snowSplit); if (nSnow>1) stateMask(ixNrgLayer(2:nSnow)) = .true. ! NOTE: (2:) because the top layer in the snow+soil domain included in vegSplit - case(soilSplit); stateMask(ixNrgLayer(max(2,nSnow+1):nLayers)) = .true. ! NOTE: max(2,nSnow+1) gives second layer unless more than 2 snow layers - case(aquiferSplit) ! do nothing: no energy state variable for the aquifer domain + case(vegSplit); call stateTypeSplit_subDomain_nrgSplit_vegSplit_stateMask ! vegetation subdomain + case(snowSplit); call stateTypeSplit_subDomain_nrgSplit_snowSplit_stateMask ! snow subdomain + case(soilSplit); call stateTypeSplit_subDomain_nrgSplit_soilSplit_stateMask ! soil subdomain + case(aquiferSplit) ! do nothing: no energy state variable for the aquifer domain ! aquifer subdomain case default; err=20; message=trim(message)//'unable to identify model sub-domain'; return_flag=.true.; return end select end associate end subroutine stateTypeSplit_subDomain_nrgSplit_stateMask + subroutine stateTypeSplit_subDomain_nrgSplit_vegSplit_stateMask + ! *** Get state type subdomain energy vegetation split *** + associate(& + ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain + ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + if (ixNrgCanair(1)/=integerMissing) stateMask(ixNrgCanair) = .true. ! energy of the canopy air space + if (ixNrgCanopy(1)/=integerMissing) stateMask(ixNrgCanopy) = .true. ! energy of the vegetation canopy + stateMask(ixNrgLayer(1)) = .true. ! energy of the upper-most layer in the snow+soil domain + end associate + end subroutine stateTypeSplit_subDomain_nrgSplit_vegSplit_stateMask + + subroutine stateTypeSplit_subDomain_nrgSplit_snowSplit_stateMask + associate(& + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + ! *** Get state type subdomain energy snow split *** + if (nSnow>1) stateMask(ixNrgLayer(2:nSnow)) = .true. ! NOTE: (2:) because the top layer in the snow+soil domain included in vegSplit + end associate + end subroutine stateTypeSplit_subDomain_nrgSplit_snowSplit_stateMask + + subroutine stateTypeSplit_subDomain_nrgSplit_soilSplit_stateMask + ! *** Get state type subdomain energy soil split *** + associate(& + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + stateMask(ixNrgLayer(max(2,nSnow+1):nLayers)) = .true. ! NOTE: max(2,nSnow+1) gives second layer unless more than 2 snow layers + end associate + end subroutine stateTypeSplit_subDomain_nrgSplit_soilSplit_stateMask + subroutine stateTypeSplit_subDomain_massSplit_stateMask ! *** Get subdomain mass split stateMask *** return_flag=.false. ! initialize flag associate(& iDomainSplit => in_stateFilter % iDomainSplit ,& ! intent(in): [i4b] index of the domain split - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain - ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain - ixWatAquifer => indx_data%var(iLookINDEX%ixWatAquifer)%dat,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for water storage in the aquifer err => out_stateFilter % err ,& ! intent(out): error code message => out_stateFilter % cmessage ) ! intent(out): error message select case(iDomainSplit) - case(vegSplit); if (ixHydCanopy(1)/=integerMissing) stateMask(ixHydCanopy) = .true. ! hydrology of the vegetation canopy - case(snowSplit); stateMask(ixHydLayer(1:nSnow)) = .true. ! snow hydrology - case(soilSplit); stateMask(ixHydLayer(nSnow+1:nLayers)) = .true. ! soil hydrology - case(aquiferSplit); if (ixWatAquifer(1)/=integerMissing) stateMask(ixWatAquifer) = .true. ! aquifer storage + case(vegSplit); call stateTypeSplit_subDomain_massSplit_vegSplit_stateMask ! vegetation subdomain + case(snowSplit); call stateTypeSplit_subDomain_massSplit_snowSplit_stateMask ! snow subdomain + case(soilSplit); call stateTypeSplit_subDomain_massSplit_soilSplit_stateMask ! soil subdomain + case(aquiferSplit); call stateTypeSplit_subDomain_massSplit_aquiferSplit_stateMask ! aquifer subdomain case default; err=20; message=trim(message)//'unable to identify model sub-domain'; return_flag=.true.; return end select end associate end subroutine stateTypeSplit_subDomain_massSplit_stateMask + subroutine stateTypeSplit_subDomain_massSplit_vegSplit_stateMask + ! *** Get mass state vegetation subdomain split stateMask *** + associate(ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain + if (ixHydCanopy(1)/=integerMissing) stateMask(ixHydCanopy) = .true. ! hydrology of the vegetation canopy + end associate + end subroutine stateTypeSplit_subDomain_massSplit_vegSplit_stateMask + + subroutine stateTypeSplit_subDomain_massSplit_snowSplit_stateMask + ! *** Get mass state snow subdomain split stateMask *** + associate(& + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + stateMask(ixHydLayer(1:nSnow)) = .true. ! snow hydrology + end associate + end subroutine stateTypeSplit_subDomain_massSplit_snowSplit_stateMask + + subroutine stateTypeSplit_subDomain_massSplit_soilSplit_stateMask + ! *** Get mass state soil subdomain split stateMask *** + associate(& + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + stateMask(ixHydLayer(nSnow+1:nLayers)) = .true. ! soil hydrology + end associate + end subroutine stateTypeSplit_subDomain_massSplit_soilSplit_stateMask + + subroutine stateTypeSplit_subDomain_massSplit_aquiferSplit_stateMask + ! *** Get mass state aquifer subdomain split stateMask *** + associate(ixWatAquifer => indx_data%var(iLookINDEX%ixWatAquifer)%dat) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for water storage in the aquifer + if (ixWatAquifer(1)/=integerMissing) stateMask(ixWatAquifer) = .true. ! aquifer storage + end associate + end subroutine stateTypeSplit_subDomain_massSplit_aquiferSplit_stateMask + subroutine identify_scalar_solutions ! *** Identify scalar solutions *** return_flag=.false. ! initialize flag From c4bbcb66b2cc9d1be246be838e9cdd98260bc700 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 12 Dec 2023 23:04:46 +0900 Subject: [PATCH 1023/1472] utilites --- utils/hist_per_GRU.py | 40 +++++++++++++++++++++++++++++----------- utils/plot_per_GRU.py | 11 ++++++----- utils/scat_per_GRU.py | 26 +++++++++++++++++--------- 3 files changed, 52 insertions(+), 25 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 70b954f57..e9bd9d026 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -27,15 +27,20 @@ do_rel = True # plot relative to the benchmark simulation testing = False +do_hist = False # plot histogram instead of CDF if testing: stat = 'rmnz' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') method_name=['be1','sundials_1en6'] #maybe make this an argument + plt_name=['BE1','IDAe-6'] #maybe make this an argument else: import sys # The first input argument specifies the run where the files are stat = sys.argv[1] - method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument + #method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument + #plt_name=['BE1','IDAe-4','BE4','BE8','BE16','BE32','IDAe-6'] #maybe make this an argument + method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument + plt_name=['BE1','BE16','BE32','IDAe-6'] #maybe make this an argument if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE # Simulation statistics file locations @@ -93,9 +98,11 @@ plt.rcParams.update({'font.size': 100}) if 'compressed' in fig_fil: - fig,axs = plt.subplots(3,2,figsize=(35,33)) + fig,axs = plt.subplots(3,2,figsize=(31,33)) else: fig,axs = plt.subplots(3,2,figsize=(140,133)) +#fig.suptitle('Histograms of Hourly Statistics for each GRU', fontsize=40) +fig.subplots_adjust(hspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space def run_loop(i,var,mx): r = i//2 @@ -161,31 +168,42 @@ def run_loop(i,var,mx): if stat == 'maxe': s = np.fabs(s) # make absolute value norm range = (0,mx) if stat=='kgem' and var!='wallClockTime' : range = (mn,1) - np.fabs(s).plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=m,linewidth=2.0,range=range) + if (do_hist): + np.fabs(s).plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=m,linewidth=2.0,range=range) + else: #cdf + sorted_data = np.sort(np.fabs(s)) + yvals = np.arange(len(sorted_data)) / float(len(sorted_data) - 1) + axs[r,c].plot(sorted_data, yvals, zorder=0, label=m, linewidth=2.0) + axs[r,c].set_xlim(range) # Replace xmin and xmax with the desired limits + if stat0 == 'rmse': stat_word = 'RMSE' - if stat0 == 'rmnz': stat_word = 'RMSE no 0s' + if stat0 == 'rmnz': stat_word = 'RMSE' # no 0s' if stat0 == 'maxe': stat_word = 'max abs error' if stat0 == 'kgem': stat_word = 'KGE"' if stat0 == 'mean': stat_word = 'mean' - if stat0 == 'mnnz': stat_word = 'mean no 0s' + if stat0 == 'mnnz': stat_word = 'mean' # no 0s' if stat0 == 'amax': stat_word = 'max' - fig.suptitle('Histograms of Hourly Statistics for each GRU', fontsize=40) - + if statr == 'mean_ben': statr_word = 'mean' - if statr == 'mnnz_ben': statr_word = 'mean excluding 0s' + if statr == 'mnnz_ben': statr_word = 'mean' # no 0s' if statr == 'amax_ben': statr_word = 'max' #if var == 'wallClockTime': axs[r,c].set_yscale('log') #log y axis for wall clock time to exaggerate peaks - axs[r,c].legend() + axs[r,c].legend(plt_name) axs[r,c].set_title(plt_titl[i]) if stat == 'rmse' or stat == 'rmnz': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl[i])) if stat == 'maxe': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titlm[i])) if stat == 'kgem': axs[r,c].set_xlabel(stat_word) - if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + ' rel to bench ' + statr_word) + #if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + ' rel to bench ' + statr_word) + if do_rel and var!='wallClockTime': axs[r,c].set_xlabel('relative '+ stat_word) - axs[r,c].set_ylabel('GRU count') + + if (do_hist): + axs[r,c].set_ylabel('GRU count') + else: + axs[r,c].set_ylabel('cumulative distribution') if var != 'wallClockTime' and not testing: axs[r,c].set_ylim([0, 25000]) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 8386f5955..36a409b12 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -255,7 +255,7 @@ def make_default_path(suffix): fig,axs = plt.subplots(3,2,figsize=(35,33)) else: fig,axs = plt.subplots(3,2,figsize=(140,133)) -fig.suptitle('{} Hourly Statistics'.format(method_name), fontsize=40) +fig.suptitle('{} Hourly Statistics'.format(method_name), fontsize=40,y=0.95) plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines @@ -286,15 +286,15 @@ def run_loop(i,var,the_max,f_x,f_y): bas_albers.plot(ax=axs[r,c], column=var, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) if stat0 == 'rmse': stat_word = 'RMSE' - if stat0 == 'rmnz': stat_word = 'RMSE no 0s' + if stat0 == 'rmnz': stat_word = 'RMSE' # no 0s' if stat0 == 'maxe': stat_word = 'max abs error' if stat0 == 'kgem': stat_word = 'KGE"' if stat0 == 'mean': stat_word = 'abs mean' - if stat0 == 'mnnz': stat_word = 'abs mean no 0s' + if stat0 == 'mnnz': stat_word = 'abs mean' # no 0s' if stat0 == 'amax': stat_word = 'abs max' if statr == 'mean_ben': statr_word = 'mean' - if statr == 'mnnz_ben': statr_word = 'mean excluding 0s' + if statr == 'mnnz_ben': statr_word = 'mean' # no 0s' if statr == 'amax_ben': statr_word = 'max' axs[r,c].set_title(plt_titl[i]) @@ -310,7 +310,8 @@ def run_loop(i,var,the_max,f_x,f_y): if stat == 'rmse' or stat == 'rmnz' or stat == 'mean': cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titl[i]), labelpad=40, rotation=270) if stat == 'maxe' or stat == 'amax': cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titlm[i]), labelpad=40, rotation=270) if stat == 'kgem': cbr.ax.set_ylabel(stat_word, labelpad=40, rotation=270) - if do_rel and var!='wallClockTime': cbr.ax.set_ylabel(stat_word + ' rel to bench ' + statr_word, labelpad=40, rotation=270) + #if do_rel and var!='wallClockTime': cbr.ax.set_ylabel(stat_word + ' rel to bench ' + statr_word, labelpad=40, rotation=270) + if do_rel and var!='wallClockTime': axs[r,c].set_xlabel('relative '+ stat_word) #cbr.ax.yaxis.set_offset_position('right') diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index 8d8e87a38..32b5852fc 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -30,11 +30,16 @@ stat = 'maxe' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') method_name=['be1','sundials_1en6'] #maybe make this an argument + plt_name=['BE1','IDAe-6'] #maybe make this an argument else: import sys # The first input argument specifies the run where the files are stat = sys.argv[1] - method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument + #method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument + #plt_name=['BE1','IDAe-4','BE4','BE8','BE16','BE32','IDAe-6'] #maybe make this an argument + method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument + plt_name=['BE1','BE16','BE32','IDAe-6'] #maybe make this an argument + if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE # Simulation statistics file locations @@ -79,10 +84,12 @@ plt.rcParams.update({'font.size': 100}) if 'compressed' in fig_fil: - fig,axs = plt.subplots(3,2,figsize=(35,33)) + fig,axs = plt.subplots(3,2,figsize=(31,33)) else: fig,axs = plt.subplots(3,2,figsize=(140,133)) -fig.suptitle('Hourly Errors and Values for each GRU', fontsize=40) +#fig.suptitle('Hourly Errors and Values for each GRU', fontsize=40) +fig.subplots_adjust(hspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space + def run_loop(i,var): r = i//2 @@ -136,26 +143,27 @@ def run_loop(i,var): else: axs[r,c].scatter(x=np.fabs(s.sel(stat=stat).values),y=s.sel(stat=stat0).values,s=1,zorder=0,label=m) if stat == 'rmse': stat_word = 'RMSE' - if stat == 'rmnz': stat_word = 'RMSE no 0s' + if stat == 'rmnz': stat_word = 'RMSE' # no 0s' if stat == 'maxe': stat_word = 'max abs error' if stat == 'kgem': stat_word = 'KGE"' if stat0 == 'mean': stat0_word = 'mean' - if stat0 == 'mnnz': stat0_word = 'mean no 0s' + if stat0 == 'mnnz': stat0_word = 'mean' # no 0s' if stat0 == 'amax': stat0_word = 'max' - lgnd = axs[r,c].legend() + lgnd = axs[r,c].legend(plt_name) for j, m in enumerate(method_name): lgnd.legendHandles[j]._sizes = [80] axs[r,c].set_title(plt_titl[i]) if stat == 'rmse' or stat == 'rmnz': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl[i])) if stat == 'maxe': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titlm[i])) if stat == 'kgem': axs[r,c].set_xlabel(stat_word) - if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + ' rel to bench ' + stat0_word) + #if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + ' rel to bench ' + stat0_word) + if do_rel and var!='wallClockTime': axs[r,c].set_xlabel('relative '+ stat_word) axs[r,c].set_ylabel(stat0_word + ' [{}]'.format(leg_titl0[i])) - if do_rel and var!='wallClockTime': axs[r,c].set_ylabel(stat0_word + ' rel to bench ' + stat0_word) - + #if do_rel and var!='wallClockTime': axs[r,c].set_ylabel(stat0_word + ' rel to bench ' + stat0_word) + if do_rel and var!='wallClockTime': axs[r,c].set_ylabel('relative '+ stat0_word) for i,var in enumerate(plot_vars): run_loop(i,var) From b15ca51c1ccddffd00f7199e6d1310d0314a1c9d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 12 Dec 2023 23:04:46 +0900 Subject: [PATCH 1024/1472] utilites --- utils/hist_per_GRU.py | 40 +++++++++++++++++++++++++++++----------- utils/plot_per_GRU.py | 11 ++++++----- utils/scat_per_GRU.py | 26 +++++++++++++++++--------- 3 files changed, 52 insertions(+), 25 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 70b954f57..e9bd9d026 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -27,15 +27,20 @@ do_rel = True # plot relative to the benchmark simulation testing = False +do_hist = False # plot histogram instead of CDF if testing: stat = 'rmnz' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') method_name=['be1','sundials_1en6'] #maybe make this an argument + plt_name=['BE1','IDAe-6'] #maybe make this an argument else: import sys # The first input argument specifies the run where the files are stat = sys.argv[1] - method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument + #method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument + #plt_name=['BE1','IDAe-4','BE4','BE8','BE16','BE32','IDAe-6'] #maybe make this an argument + method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument + plt_name=['BE1','BE16','BE32','IDAe-6'] #maybe make this an argument if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE # Simulation statistics file locations @@ -93,9 +98,11 @@ plt.rcParams.update({'font.size': 100}) if 'compressed' in fig_fil: - fig,axs = plt.subplots(3,2,figsize=(35,33)) + fig,axs = plt.subplots(3,2,figsize=(31,33)) else: fig,axs = plt.subplots(3,2,figsize=(140,133)) +#fig.suptitle('Histograms of Hourly Statistics for each GRU', fontsize=40) +fig.subplots_adjust(hspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space def run_loop(i,var,mx): r = i//2 @@ -161,31 +168,42 @@ def run_loop(i,var,mx): if stat == 'maxe': s = np.fabs(s) # make absolute value norm range = (0,mx) if stat=='kgem' and var!='wallClockTime' : range = (mn,1) - np.fabs(s).plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=m,linewidth=2.0,range=range) + if (do_hist): + np.fabs(s).plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=m,linewidth=2.0,range=range) + else: #cdf + sorted_data = np.sort(np.fabs(s)) + yvals = np.arange(len(sorted_data)) / float(len(sorted_data) - 1) + axs[r,c].plot(sorted_data, yvals, zorder=0, label=m, linewidth=2.0) + axs[r,c].set_xlim(range) # Replace xmin and xmax with the desired limits + if stat0 == 'rmse': stat_word = 'RMSE' - if stat0 == 'rmnz': stat_word = 'RMSE no 0s' + if stat0 == 'rmnz': stat_word = 'RMSE' # no 0s' if stat0 == 'maxe': stat_word = 'max abs error' if stat0 == 'kgem': stat_word = 'KGE"' if stat0 == 'mean': stat_word = 'mean' - if stat0 == 'mnnz': stat_word = 'mean no 0s' + if stat0 == 'mnnz': stat_word = 'mean' # no 0s' if stat0 == 'amax': stat_word = 'max' - fig.suptitle('Histograms of Hourly Statistics for each GRU', fontsize=40) - + if statr == 'mean_ben': statr_word = 'mean' - if statr == 'mnnz_ben': statr_word = 'mean excluding 0s' + if statr == 'mnnz_ben': statr_word = 'mean' # no 0s' if statr == 'amax_ben': statr_word = 'max' #if var == 'wallClockTime': axs[r,c].set_yscale('log') #log y axis for wall clock time to exaggerate peaks - axs[r,c].legend() + axs[r,c].legend(plt_name) axs[r,c].set_title(plt_titl[i]) if stat == 'rmse' or stat == 'rmnz': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl[i])) if stat == 'maxe': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titlm[i])) if stat == 'kgem': axs[r,c].set_xlabel(stat_word) - if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + ' rel to bench ' + statr_word) + #if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + ' rel to bench ' + statr_word) + if do_rel and var!='wallClockTime': axs[r,c].set_xlabel('relative '+ stat_word) - axs[r,c].set_ylabel('GRU count') + + if (do_hist): + axs[r,c].set_ylabel('GRU count') + else: + axs[r,c].set_ylabel('cumulative distribution') if var != 'wallClockTime' and not testing: axs[r,c].set_ylim([0, 25000]) diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 8386f5955..36a409b12 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -255,7 +255,7 @@ def make_default_path(suffix): fig,axs = plt.subplots(3,2,figsize=(35,33)) else: fig,axs = plt.subplots(3,2,figsize=(140,133)) -fig.suptitle('{} Hourly Statistics'.format(method_name), fontsize=40) +fig.suptitle('{} Hourly Statistics'.format(method_name), fontsize=40,y=0.95) plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines @@ -286,15 +286,15 @@ def run_loop(i,var,the_max,f_x,f_y): bas_albers.plot(ax=axs[r,c], column=var, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) if stat0 == 'rmse': stat_word = 'RMSE' - if stat0 == 'rmnz': stat_word = 'RMSE no 0s' + if stat0 == 'rmnz': stat_word = 'RMSE' # no 0s' if stat0 == 'maxe': stat_word = 'max abs error' if stat0 == 'kgem': stat_word = 'KGE"' if stat0 == 'mean': stat_word = 'abs mean' - if stat0 == 'mnnz': stat_word = 'abs mean no 0s' + if stat0 == 'mnnz': stat_word = 'abs mean' # no 0s' if stat0 == 'amax': stat_word = 'abs max' if statr == 'mean_ben': statr_word = 'mean' - if statr == 'mnnz_ben': statr_word = 'mean excluding 0s' + if statr == 'mnnz_ben': statr_word = 'mean' # no 0s' if statr == 'amax_ben': statr_word = 'max' axs[r,c].set_title(plt_titl[i]) @@ -310,7 +310,8 @@ def run_loop(i,var,the_max,f_x,f_y): if stat == 'rmse' or stat == 'rmnz' or stat == 'mean': cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titl[i]), labelpad=40, rotation=270) if stat == 'maxe' or stat == 'amax': cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titlm[i]), labelpad=40, rotation=270) if stat == 'kgem': cbr.ax.set_ylabel(stat_word, labelpad=40, rotation=270) - if do_rel and var!='wallClockTime': cbr.ax.set_ylabel(stat_word + ' rel to bench ' + statr_word, labelpad=40, rotation=270) + #if do_rel and var!='wallClockTime': cbr.ax.set_ylabel(stat_word + ' rel to bench ' + statr_word, labelpad=40, rotation=270) + if do_rel and var!='wallClockTime': axs[r,c].set_xlabel('relative '+ stat_word) #cbr.ax.yaxis.set_offset_position('right') diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index 8d8e87a38..32b5852fc 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -30,11 +30,16 @@ stat = 'maxe' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') method_name=['be1','sundials_1en6'] #maybe make this an argument + plt_name=['BE1','IDAe-6'] #maybe make this an argument else: import sys # The first input argument specifies the run where the files are stat = sys.argv[1] - method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument + #method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument + #plt_name=['BE1','IDAe-4','BE4','BE8','BE16','BE32','IDAe-6'] #maybe make this an argument + method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument + plt_name=['BE1','BE16','BE32','IDAe-6'] #maybe make this an argument + if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE # Simulation statistics file locations @@ -79,10 +84,12 @@ plt.rcParams.update({'font.size': 100}) if 'compressed' in fig_fil: - fig,axs = plt.subplots(3,2,figsize=(35,33)) + fig,axs = plt.subplots(3,2,figsize=(31,33)) else: fig,axs = plt.subplots(3,2,figsize=(140,133)) -fig.suptitle('Hourly Errors and Values for each GRU', fontsize=40) +#fig.suptitle('Hourly Errors and Values for each GRU', fontsize=40) +fig.subplots_adjust(hspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space + def run_loop(i,var): r = i//2 @@ -136,26 +143,27 @@ def run_loop(i,var): else: axs[r,c].scatter(x=np.fabs(s.sel(stat=stat).values),y=s.sel(stat=stat0).values,s=1,zorder=0,label=m) if stat == 'rmse': stat_word = 'RMSE' - if stat == 'rmnz': stat_word = 'RMSE no 0s' + if stat == 'rmnz': stat_word = 'RMSE' # no 0s' if stat == 'maxe': stat_word = 'max abs error' if stat == 'kgem': stat_word = 'KGE"' if stat0 == 'mean': stat0_word = 'mean' - if stat0 == 'mnnz': stat0_word = 'mean no 0s' + if stat0 == 'mnnz': stat0_word = 'mean' # no 0s' if stat0 == 'amax': stat0_word = 'max' - lgnd = axs[r,c].legend() + lgnd = axs[r,c].legend(plt_name) for j, m in enumerate(method_name): lgnd.legendHandles[j]._sizes = [80] axs[r,c].set_title(plt_titl[i]) if stat == 'rmse' or stat == 'rmnz': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl[i])) if stat == 'maxe': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titlm[i])) if stat == 'kgem': axs[r,c].set_xlabel(stat_word) - if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + ' rel to bench ' + stat0_word) + #if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + ' rel to bench ' + stat0_word) + if do_rel and var!='wallClockTime': axs[r,c].set_xlabel('relative '+ stat_word) axs[r,c].set_ylabel(stat0_word + ' [{}]'.format(leg_titl0[i])) - if do_rel and var!='wallClockTime': axs[r,c].set_ylabel(stat0_word + ' rel to bench ' + stat0_word) - + #if do_rel and var!='wallClockTime': axs[r,c].set_ylabel(stat0_word + ' rel to bench ' + stat0_word) + if do_rel and var!='wallClockTime': axs[r,c].set_ylabel('relative '+ stat0_word) for i,var in enumerate(plot_vars): run_loop(i,var) From ec50055ea3cc9328b704b14a84ff5ca012bf5b7b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 12 Dec 2023 23:20:43 +0900 Subject: [PATCH 1025/1472] utils --- utils/hist_per_GRU.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index e9bd9d026..8058e270b 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -202,10 +202,9 @@ def run_loop(i,var,mx): if (do_hist): axs[r,c].set_ylabel('GRU count') + if var != 'wallClockTime' and not testing: axs[r,c].set_ylim([0, 25000]) else: axs[r,c].set_ylabel('cumulative distribution') - if var != 'wallClockTime' and not testing: axs[r,c].set_ylim([0, 25000]) - for i,(var,mx) in enumerate(zip(plot_vars,maxes)): run_loop(i,var,mx) From 925d0cf5aa4d17869f0739639c4ae016795f8bf7 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 12 Dec 2023 23:20:43 +0900 Subject: [PATCH 1026/1472] utils --- utils/hist_per_GRU.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index e9bd9d026..8058e270b 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -202,10 +202,9 @@ def run_loop(i,var,mx): if (do_hist): axs[r,c].set_ylabel('GRU count') + if var != 'wallClockTime' and not testing: axs[r,c].set_ylim([0, 25000]) else: axs[r,c].set_ylabel('cumulative distribution') - if var != 'wallClockTime' and not testing: axs[r,c].set_ylim([0, 25000]) - for i,(var,mx) in enumerate(zip(plot_vars,maxes)): run_loop(i,var,mx) From 5eb89d7283b43f6115eecb242d71d032d711be00 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 12 Dec 2023 23:51:35 +0900 Subject: [PATCH 1027/1472] utils --- utils/hist_per_GRU.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 8058e270b..68cc1821d 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -58,8 +58,12 @@ leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$','$s$'] -fig_fil = 'Hrly_diff_hist_{}_{}_zoom_compressed.png' -if do_rel: fig_fil = 'Hrly_diff_hist_{}_{}_zoom_rel_compressed.png' +if do_hist: + fig_fil = 'Hrly_diff_hist_{}_{}_zoom_compressed.png' + if do_rel: fig_fil = 'Hrly_diff_hist_{}_{}_zoom_rel_compressed.png' +else: + fig_fil = 'Hrly_diff_cdf_{}_{}_zoom_compressed.png' + if do_rel: fig_fil = 'Hrly_diff_cdf_{}_{}_zoom_rel_compressed.png' fig_fil = fig_fil.format(','.join(settings),stat) if stat == 'rmse': @@ -168,7 +172,7 @@ def run_loop(i,var,mx): if stat == 'maxe': s = np.fabs(s) # make absolute value norm range = (0,mx) if stat=='kgem' and var!='wallClockTime' : range = (mn,1) - if (do_hist): + if do_hist: np.fabs(s).plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=m,linewidth=2.0,range=range) else: #cdf sorted_data = np.sort(np.fabs(s)) @@ -199,8 +203,7 @@ def run_loop(i,var,mx): #if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + ' rel to bench ' + statr_word) if do_rel and var!='wallClockTime': axs[r,c].set_xlabel('relative '+ stat_word) - - if (do_hist): + if do_hist: axs[r,c].set_ylabel('GRU count') if var != 'wallClockTime' and not testing: axs[r,c].set_ylim([0, 25000]) else: From 82e864c1e9b54cc8817d6d593943f6a0a973d499 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 12 Dec 2023 23:51:35 +0900 Subject: [PATCH 1028/1472] utils --- utils/hist_per_GRU.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 8058e270b..68cc1821d 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -58,8 +58,12 @@ leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$','$s$'] -fig_fil = 'Hrly_diff_hist_{}_{}_zoom_compressed.png' -if do_rel: fig_fil = 'Hrly_diff_hist_{}_{}_zoom_rel_compressed.png' +if do_hist: + fig_fil = 'Hrly_diff_hist_{}_{}_zoom_compressed.png' + if do_rel: fig_fil = 'Hrly_diff_hist_{}_{}_zoom_rel_compressed.png' +else: + fig_fil = 'Hrly_diff_cdf_{}_{}_zoom_compressed.png' + if do_rel: fig_fil = 'Hrly_diff_cdf_{}_{}_zoom_rel_compressed.png' fig_fil = fig_fil.format(','.join(settings),stat) if stat == 'rmse': @@ -168,7 +172,7 @@ def run_loop(i,var,mx): if stat == 'maxe': s = np.fabs(s) # make absolute value norm range = (0,mx) if stat=='kgem' and var!='wallClockTime' : range = (mn,1) - if (do_hist): + if do_hist: np.fabs(s).plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=m,linewidth=2.0,range=range) else: #cdf sorted_data = np.sort(np.fabs(s)) @@ -199,8 +203,7 @@ def run_loop(i,var,mx): #if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + ' rel to bench ' + statr_word) if do_rel and var!='wallClockTime': axs[r,c].set_xlabel('relative '+ stat_word) - - if (do_hist): + if do_hist: axs[r,c].set_ylabel('GRU count') if var != 'wallClockTime' and not testing: axs[r,c].set_ylim([0, 25000]) else: From a4ad37397dd88a2590f121a07d440197a70a2c49 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 13 Dec 2023 11:25:01 +0900 Subject: [PATCH 1029/1472] utils --- utils/hist_per_GRU.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 68cc1821d..eb706b6f6 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -43,6 +43,10 @@ plt_name=['BE1','BE16','BE32','IDAe-6'] #maybe make this an argument if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE +# Define the power transformation function +def power_transform(x): + return x ** 0.5 # Adjust the exponent as needed + # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] viz_fil = method_name.copy() @@ -106,7 +110,8 @@ else: fig,axs = plt.subplots(3,2,figsize=(140,133)) #fig.suptitle('Histograms of Hourly Statistics for each GRU', fontsize=40) -fig.subplots_adjust(hspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space +fig.subplots_adjust(hspace=0.28) # Adjust the bottom margin, vertical space, and horizontal space +#fig.subplots_adjust(hspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space def run_loop(i,var,mx): r = i//2 @@ -192,8 +197,6 @@ def run_loop(i,var,mx): if statr == 'mean_ben': statr_word = 'mean' if statr == 'mnnz_ben': statr_word = 'mean' # no 0s' if statr == 'amax_ben': statr_word = 'max' - - #if var == 'wallClockTime': axs[r,c].set_yscale('log') #log y axis for wall clock time to exaggerate peaks axs[r,c].legend(plt_name) axs[r,c].set_title(plt_titl[i]) @@ -206,8 +209,14 @@ def run_loop(i,var,mx): if do_hist: axs[r,c].set_ylabel('GRU count') if var != 'wallClockTime' and not testing: axs[r,c].set_ylim([0, 25000]) + #if var == 'wallClockTime': axs[r,c].set_yscale('log') #log y axis for wall clock time to exaggerate peaks + else: axs[r,c].set_ylabel('cumulative distribution') + axs[r,c].set_ylim([0.0, 1.0]) + axs[r,c].set_xscale('function', functions=(power_transform, np.power)) #log x axis + if r == 0 and c == 1: # Rotate x-axis labels for axs[2, 1] subplot + axs[r, c].tick_params(axis='x', rotation=45) for i,(var,mx) in enumerate(zip(plot_vars,maxes)): run_loop(i,var,mx) From 1e996e1719b64fa4d56a0512f6c5d03d88549be9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 13 Dec 2023 11:36:24 +0900 Subject: [PATCH 1030/1472] utils --- utils/hist_per_GRU.py | 2 +- utils/plot_per_GRU.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index eb706b6f6..ac5015b59 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -109,7 +109,7 @@ def power_transform(x): fig,axs = plt.subplots(3,2,figsize=(31,33)) else: fig,axs = plt.subplots(3,2,figsize=(140,133)) -#fig.suptitle('Histograms of Hourly Statistics for each GRU', fontsize=40) +#fig.suptitle('Histograms of Hourly Statistics for each GRU', fontsize=40,y=1.0) fig.subplots_adjust(hspace=0.28) # Adjust the bottom margin, vertical space, and horizontal space #fig.subplots_adjust(hspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 36a409b12..73fc5fd74 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -255,7 +255,7 @@ def make_default_path(suffix): fig,axs = plt.subplots(3,2,figsize=(35,33)) else: fig,axs = plt.subplots(3,2,figsize=(140,133)) -fig.suptitle('{} Hourly Statistics'.format(method_name), fontsize=40,y=0.95) +fig.suptitle('{} Hourly Statistics'.format(method_name), fontsize=40,y=1.05) plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines @@ -311,7 +311,7 @@ def run_loop(i,var,the_max,f_x,f_y): if stat == 'maxe' or stat == 'amax': cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titlm[i]), labelpad=40, rotation=270) if stat == 'kgem': cbr.ax.set_ylabel(stat_word, labelpad=40, rotation=270) #if do_rel and var!='wallClockTime': cbr.ax.set_ylabel(stat_word + ' rel to bench ' + statr_word, labelpad=40, rotation=270) - if do_rel and var!='wallClockTime': axs[r,c].set_xlabel('relative '+ stat_word) + if do_rel and var!='wallClockTime': cbr.ax.set_ylabel('relative '+ stat_word) #cbr.ax.yaxis.set_offset_position('right') From 1e0dca4ce1821c8f29c209a054942d08c7d27342 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 13 Dec 2023 05:43:25 -0600 Subject: [PATCH 1031/1472] Migrated split_select_type class from data_types module to opSplittin_module -- created compute_stateMask class procedure. --- build/source/dshare/data_types.f90 | 11 ------- build/source/engine/opSplittin.f90 | 46 ++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 7d1279531..55b45f724 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -655,17 +655,6 @@ MODULE data_types end type out_type_varSubstep ! ** end varSubstep - ! *********************************************************************************************************** - ! Define classes used define operator splitting methods in opSplittin - ! *********************************************************************************************************** - - type, public :: split_select_type ! class for selecting operator splitting methods - type(var_flagVec) :: fluxMask ! integer mask defining model fluxes - logical(lgt),allocatable :: stateMask(:) ! mask defining desired state variables - contains - !procedure :: initialize => initialize_split_select ! currently under development in opSplittin - end type split_select_type - contains ! **** vegNrgFlux **** diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 4d0928783..30b357ca2 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -97,8 +97,7 @@ module opSplittin_module model_options, & ! defines the model decisions in_type_statefilter,out_type_statefilter, & ! classes for stateFilter objects in_type_indexSplit,out_type_indexSplit, & ! classes for indexSplit objects - in_type_varSubstep,io_type_varSubstep,out_type_varSubstep, & ! classes for varSubstep objects - split_select_type ! classs for selecting operator splitting methods + in_type_varSubstep,io_type_varSubstep,out_type_varSubstep ! classes for varSubstep objects ! look-up values for the numerical method USE mDecisions_module,only: & @@ -144,6 +143,22 @@ module opSplittin_module real(rkind),parameter :: veryBig=1.e+20_rkind ! a very big number real(rkind),parameter :: dx = 1.e-8_rkind ! finite difference increment +! class definitions + +type, public :: split_select_type ! class for selecting operator splitting methods + integer(i4b) :: ixCoupling + integer(i4b) :: ixSolution + integer(i4b) :: ixStateThenDomain + integer(i4b) :: iStateTypeSplit + integer(i4b) :: iDomainSplit + integer(i4b) :: iStateSplit + type(var_flagVec) :: fluxMask ! integer mask defining model fluxes + logical(lgt),allocatable :: stateMask(:) ! mask defining desired state variables + contains + procedure :: get_stateMask => compute_stateMask ! currently under development in opSplittin +end type split_select_type + + contains @@ -1082,6 +1097,33 @@ end subroutine update_fluxMask end subroutine opSplittin +subroutine compute_stateMask(split_select,in_stateFilter,indx_data,out_stateFilter,nSubset,err,cmessage,message,return_flag) + ! *** Get the mask for the state subset *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + type(in_type_stateFilter),intent(out) :: in_stateFilter ! indices + type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU + type(out_type_stateFilter),intent(out) :: out_stateFilter ! number of selected state variables for a given split and error control + integer(i4b),intent(out) :: nSubset ! intent(out): number of selected state variables for a given split + integer(i4b),intent(out) :: err ! intent(out): error code + character(*),intent(out) :: cmessage ! intent(out): error message + character(*),intent(out) :: message ! error message + logical(lgt),intent(out) :: return_flag + return_flag=.false. ! initialize flag + associate(& + ixCoupling => split_select % ixCoupling ,& + ixSolution => split_select % ixSolution ,& + ixStateThenDomain => split_select % ixStateThenDomain ,& + iStateTypeSplit => split_select % iStateTypeSplit ,& + iDomainSplit => split_select % iDomainSplit ,& + iStateSplit => split_select % iStateSplit ) + call in_stateFilter % initialize(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) + end associate + associate(stateMask => split_select % stateMask) + call stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) + end associate + call out_stateFilter % finalize(nSubset,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! error control +end subroutine compute_stateMask ! ********************************************************************************************************** ! private subroutine stateFilter: get a mask for the desired state variables From b90371f48ae651f8377947be4de6036797d2e13c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 14 Dec 2023 11:40:00 +0900 Subject: [PATCH 1032/1472] utils --- utils/hist_per_GRU.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index ac5015b59..5353fd5ac 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -181,8 +181,9 @@ def run_loop(i,var,mx): np.fabs(s).plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=m,linewidth=2.0,range=range) else: #cdf sorted_data = np.sort(np.fabs(s)) - yvals = np.arange(len(sorted_data)) / float(len(sorted_data) - 1) - axs[r,c].plot(sorted_data, yvals, zorder=0, label=m, linewidth=2.0) + valid_data = sorted_data[~np.isnan(sorted_data)] + yvals = np.arange(len(valid_data)) / float(len(valid_data) - 1) + axs[r,c].plot(valid_data, yvals, zorder=0, label=m, linewidth=2.0) axs[r,c].set_xlim(range) # Replace xmin and xmax with the desired limits From ac8b9a47b5780c926a0ff1192a01de9670d720a8 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 13 Dec 2023 11:25:01 +0900 Subject: [PATCH 1033/1472] utils --- utils/hist_per_GRU.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 68cc1821d..eb706b6f6 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -43,6 +43,10 @@ plt_name=['BE1','BE16','BE32','IDAe-6'] #maybe make this an argument if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE +# Define the power transformation function +def power_transform(x): + return x ** 0.5 # Adjust the exponent as needed + # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] viz_fil = method_name.copy() @@ -106,7 +110,8 @@ else: fig,axs = plt.subplots(3,2,figsize=(140,133)) #fig.suptitle('Histograms of Hourly Statistics for each GRU', fontsize=40) -fig.subplots_adjust(hspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space +fig.subplots_adjust(hspace=0.28) # Adjust the bottom margin, vertical space, and horizontal space +#fig.subplots_adjust(hspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space def run_loop(i,var,mx): r = i//2 @@ -192,8 +197,6 @@ def run_loop(i,var,mx): if statr == 'mean_ben': statr_word = 'mean' if statr == 'mnnz_ben': statr_word = 'mean' # no 0s' if statr == 'amax_ben': statr_word = 'max' - - #if var == 'wallClockTime': axs[r,c].set_yscale('log') #log y axis for wall clock time to exaggerate peaks axs[r,c].legend(plt_name) axs[r,c].set_title(plt_titl[i]) @@ -206,8 +209,14 @@ def run_loop(i,var,mx): if do_hist: axs[r,c].set_ylabel('GRU count') if var != 'wallClockTime' and not testing: axs[r,c].set_ylim([0, 25000]) + #if var == 'wallClockTime': axs[r,c].set_yscale('log') #log y axis for wall clock time to exaggerate peaks + else: axs[r,c].set_ylabel('cumulative distribution') + axs[r,c].set_ylim([0.0, 1.0]) + axs[r,c].set_xscale('function', functions=(power_transform, np.power)) #log x axis + if r == 0 and c == 1: # Rotate x-axis labels for axs[2, 1] subplot + axs[r, c].tick_params(axis='x', rotation=45) for i,(var,mx) in enumerate(zip(plot_vars,maxes)): run_loop(i,var,mx) From 860727db06e644609eecc16916efdbab08dc7746 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 13 Dec 2023 11:36:24 +0900 Subject: [PATCH 1034/1472] utils --- utils/hist_per_GRU.py | 2 +- utils/plot_per_GRU.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index eb706b6f6..ac5015b59 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -109,7 +109,7 @@ def power_transform(x): fig,axs = plt.subplots(3,2,figsize=(31,33)) else: fig,axs = plt.subplots(3,2,figsize=(140,133)) -#fig.suptitle('Histograms of Hourly Statistics for each GRU', fontsize=40) +#fig.suptitle('Histograms of Hourly Statistics for each GRU', fontsize=40,y=1.0) fig.subplots_adjust(hspace=0.28) # Adjust the bottom margin, vertical space, and horizontal space #fig.subplots_adjust(hspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 36a409b12..73fc5fd74 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -255,7 +255,7 @@ def make_default_path(suffix): fig,axs = plt.subplots(3,2,figsize=(35,33)) else: fig,axs = plt.subplots(3,2,figsize=(140,133)) -fig.suptitle('{} Hourly Statistics'.format(method_name), fontsize=40,y=0.95) +fig.suptitle('{} Hourly Statistics'.format(method_name), fontsize=40,y=1.05) plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines @@ -311,7 +311,7 @@ def run_loop(i,var,the_max,f_x,f_y): if stat == 'maxe' or stat == 'amax': cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titlm[i]), labelpad=40, rotation=270) if stat == 'kgem': cbr.ax.set_ylabel(stat_word, labelpad=40, rotation=270) #if do_rel and var!='wallClockTime': cbr.ax.set_ylabel(stat_word + ' rel to bench ' + statr_word, labelpad=40, rotation=270) - if do_rel and var!='wallClockTime': axs[r,c].set_xlabel('relative '+ stat_word) + if do_rel and var!='wallClockTime': cbr.ax.set_ylabel('relative '+ stat_word) #cbr.ax.yaxis.set_offset_position('right') From 79b1e94d1c1ca36a16f8ba978d71ac268c34679a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 14 Dec 2023 11:40:00 +0900 Subject: [PATCH 1035/1472] utils --- utils/hist_per_GRU.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index ac5015b59..5353fd5ac 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -181,8 +181,9 @@ def run_loop(i,var,mx): np.fabs(s).plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=m,linewidth=2.0,range=range) else: #cdf sorted_data = np.sort(np.fabs(s)) - yvals = np.arange(len(sorted_data)) / float(len(sorted_data) - 1) - axs[r,c].plot(sorted_data, yvals, zorder=0, label=m, linewidth=2.0) + valid_data = sorted_data[~np.isnan(sorted_data)] + yvals = np.arange(len(valid_data)) / float(len(valid_data) - 1) + axs[r,c].plot(valid_data, yvals, zorder=0, label=m, linewidth=2.0) axs[r,c].set_xlim(range) # Replace xmin and xmax with the desired limits From 7916171b1a0a67a5cbd15f4744ee752e5255ce87 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 15 Dec 2023 12:16:48 +0900 Subject: [PATCH 1036/1472] fix a deallocate problem --- build/source/engine/coupled_em.f90 | 8 +++++--- build/source/engine/varSubstep.f90 | 7 ------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 14861b7f0..e6279644f 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1078,7 +1078,7 @@ subroutine coupled_em(& innerBalanceLayerNrg(:) = innerBalanceLayerNrg(:) + diag_data%var(iLookDIAG%balanceLayerNrg)%dat(:)*dt_wght innerBalanceLayerMass(:) = innerBalanceLayerMass(:) + diag_data%var(iLookDIAG%balanceLayerMass)%dat(:)*dt_wght - ! save balance of energy and water per snow+soil layer after inner step, since can change nLayers with outer steps + ! save balance of energy and water per snow+soil layer after inner step, since can change nLayers with outer steps diag_data%var(iLookDIAG%balanceLayerNrg)%dat(:) = innerBalanceLayerNrg(:) diag_data%var(iLookDIAG%balanceLayerMass)%dat(:) = innerBalanceLayerMass(:) @@ -1097,8 +1097,10 @@ subroutine coupled_em(& diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) + innerBalanceLayerMass(iLayer)/nSoil end select end do - deallocate(innerBalanceLayerNrg) - deallocate(innerBalanceLayerMass) + if(do_outer)then + deallocate(innerBalanceLayerNrg) + deallocate(innerBalanceLayerMass) + endif ! increment sub-step accepted step dt_solvInner = dt_solvInner + dt_sub diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index c7639d1ff..6cbac426c 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -1009,13 +1009,6 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec end select endif ! if checking energy balance - ! save the trial values - if(computeEnthalpy)then - scalarCanairEnthalpy = scalarCanairEnthalpyTrial - mLayerEnthalpy = mLayerEnthalpyTrial - scalarCanopyEnthalpy = scalarCanopyEnthalpyTrial - endif - ! ----- ! * check mass balance... ! ----------------------- From 203e6050740c9801e833e1a947e56e289d548f03 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 15 Dec 2023 22:44:39 +0900 Subject: [PATCH 1037/1472] Making enthalpy diagnostic the temperature component, and not saving the enthalpy with phase variable because need only the temperature component for the bulk heat capacity. The BE bulk heat capacity seems to still be computing wrongly, like not passing correct previous value --- build/source/dshare/get_ixname.f90 | 6 +- build/source/dshare/popMetadat.f90 | 6 +- build/source/dshare/type4ida.f90 | 6 +- build/source/dshare/var_lookup.f90 | 4 +- build/source/engine/eval8summa.f90 | 48 ++--- build/source/engine/eval8summaWithPrime.f90 | 46 ++-- build/source/engine/summaSolve4ida.f90 | 160 +++++++------- build/source/engine/systemSolv.f90 | 6 +- build/source/engine/t2enthalpy.f90 | 18 +- build/source/engine/varSubstep.f90 | 227 +++++++++++--------- 10 files changed, 284 insertions(+), 243 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index cef4c3a00..efe620658 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -508,9 +508,9 @@ function get_ixdiag(varName) case('mLayerThermalC' ); get_ixdiag = iLookDIAG%mLayerThermalC ! thermal conductivity at the mid-point of each layer (W m-1 K-1) case('iLayerThermalC' ); get_ixdiag = iLookDIAG%iLayerThermalC ! thermal conductivity at the interface of each layer (W m-1 K-1) ! enthalpy - case('scalarCanairEnthalpy' ); get_ixdiag = iLookDIAG%scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - case('scalarCanopyEnthalpy' ); get_ixdiag = iLookDIAG%scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) - case('mLayerEnthalpy' ); get_ixdiag = iLookDIAG%mLayerEnthalpy ! enthalpy of the snow+soil layers (J m-3) + case('scalarCanairEnthalpy' ); get_ixdiag = iLookDIAG%scalarCanairEnthalpy ! temperature component of enthalpy of the canopy air space (J m-3) + case('scalarCanopyEnthalpy' ); get_ixdiag = iLookDIAG%scalarCanopyEnthalpy ! temperature component of enthalpy of the vegetation canopy (J m-3) + case('mLayerEnthalpy' ); get_ixdiag = iLookDIAG%mLayerEnthalpy ! temperature component of enthalpy of the snow+soil layers (J m-3) ! forcing case('scalarVPair' ); get_ixdiag = iLookDIAG%scalarVPair ! vapor pressure of the air above the vegetation canopy (Pa) case('scalarVP_CanopyAir' ); get_ixdiag = iLookDIAG%scalarVP_CanopyAir ! vapor pressure of the canopy air space (Pa) diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index fdb7e88f9..8f540a24b 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -363,9 +363,9 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%mLayerThermalC) = var_info('mLayerThermalC' , 'thermal conductivity at the mid-point of each layer' , 'W m-1 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%iLayerThermalC) = var_info('iLayerThermalC' , 'thermal conductivity at the interface of each layer' , 'W m-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) ! enthalpy - diag_meta(iLookDIAG%scalarCanairEnthalpy) = var_info('scalarCanairEnthalpy' , 'enthalpy of the canopy air space' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarCanopyEnthalpy) = var_info('scalarCanopyEnthalpy' , 'enthalpy of the vegetation canopy' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%mLayerEnthalpy) = var_info('mLayerEnthalpy' , 'enthalpy of the snow+soil layers' , 'J m-3' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarCanairEnthalpy) = var_info('scalarCanairEnthalpy' , 'temperature component of enthalpy of the canopy air space' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarCanopyEnthalpy) = var_info('scalarCanopyEnthalpy' , 'temperature component of enthalpy of the vegetation canopy' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%mLayerEnthalpy) = var_info('mLayerEnthalpy' , 'temperature component of enthalpy of the snow+soil layers' , 'J m-3' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) ! forcing diag_meta(iLookDIAG%scalarVPair) = var_info('scalarVPair' , 'vapor pressure of the air above the vegetation canopy' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarVP_CanopyAir) = var_info('scalarVP_CanopyAir' , 'vapor pressure of the canopy air space' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/type4ida.f90 b/build/source/dshare/type4ida.f90 index fa6ee0df9..fda9a219f 100644 --- a/build/source/dshare/type4ida.f90 +++ b/build/source/dshare/type4ida.f90 @@ -68,9 +68,9 @@ module type4ida real(rkind), allocatable :: mLayerMatricHeadLiqPrime(:) ! prime value for liquid matric head of each snow and soil layer (m s-1) real(rkind), allocatable :: mLayerVolFracWatPrime(:) ! prime value for volumetric total water content of each snow and soil layer (s-1) real(rkind), allocatable :: mLayerVolFracIcePrime(:) ! prime value for volumetric fraction of ice (s-1) - real(rkind) :: scalarCanairEnthalpyPrime ! prime value for enthalpy of the canopy air space (J m-3 s-1) - real(rkind) :: scalarCanopyEnthalpyPrime ! prime value of enthalpy of the vegetation canopy (J m-3 s-1) - real(rkind), allocatable :: mLayerEnthalpyPrime(:) ! prime vector of enthalpy for snow+soil layers (J m-3 s-1) + real(rkind) :: scalarCanairEnthalpyPrime ! prime value for temperature component of enthalpy of the canopy air space (J m-3 s-1) + real(rkind) :: scalarCanopyEnthalpyPrime ! prime value for temperature component of enthalpy of the vegetation canopy (J m-3 s-1) + real(rkind), allocatable :: mLayerEnthalpyPrime(:) ! prime vector of temperature component of enthalpy for snow+soil layers (J m-3 s-1) end type data4ida diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index cddf038b1..4e3741085 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -389,8 +389,8 @@ MODULE var_lookup integer(i4b) :: iLayerThermalC = integerMissing ! thermal conductivity at the interface of each layer (W m-1 K-1) ! enthalpy integer(i4b) :: scalarCanairEnthalpy = integerMissing ! enthalpy of the canopy air space (J m-3) - integer(i4b) :: scalarCanopyEnthalpy = integerMissing ! enthalpy of the vegetation canopy (J m-3) - integer(i4b) :: mLayerEnthalpy = integerMissing ! enthalpy of the snow+soil layers (J m-3) + integer(i4b) :: scalarCanopyEnthalpy = integerMissing ! temperature component of enthalpy of the vegetation canopy (J m-3) + integer(i4b) :: mLayerEnthalpy = integerMissing ! temperature component of enthalpy of the snow+soil layers (J m-3) ! forcing integer(i4b) :: scalarVPair = integerMissing ! vapor pressure of the air above the vegetation canopy (Pa) integer(i4b) :: scalarVP_CanopyAir = integerMissing ! vapor pressure of the canopy air space (Pa) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 0d04b6bff..ee829561e 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -248,9 +248,9 @@ subroutine eval8summa(& mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) ! enthalpy from the previous solution - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(in): [dp] enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(in): [dp] enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(in): [dp(:)] enthalpy of the snow+soil layers (J m-3) + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(in): [dp] temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(in): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(in): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) ! soil compression scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2 s-1) mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in volumetric water content due to compression of soil (s-1) @@ -366,10 +366,10 @@ subroutine eval8summa(& diag_data, & ! intent(inout): model diagnostic variables for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + scalarCanopyTempTrial, & ! intent(inout): trial value for canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value for canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value for canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value for canopy ice content (kg m-2) ! output: variables for the snow-soil domain mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) @@ -392,17 +392,17 @@ subroutine eval8summa(& indx_data, & ! intent(in): model indices lookup_data, & ! intent(in): lookup table data structure ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + scalarCanairTempTrial, & ! intent(in): trial value for canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) ! input: variables for the snow-soil domain mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpy, & ! intent(out): trial value for enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy of each snow+soil layer (J m-3) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -420,15 +420,15 @@ subroutine eval8summa(& ! input: state variables scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyTempTrial - scalarCanopyTemp, & ! intent(in): delta value of canopy temperature (K) - scalarCanopyEnthalpyTrial - scalarCanopyEnthalpy, & ! intent(in): delta enthalpy of the vegetation canopy (J m-3) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial temperature - mLayerTempTrial - mLayerTemp, & ! intent(in): delta temperature - mLayerEnthalpyTrial - mLayerEnthalpy, & ! intent(in): delta enthalpy for snow and soil - mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) + scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) + scalarCanopyTempTrial - scalarCanopyTemp, & ! intent(in): delta value for canopy temperature (K) + scalarCanopyEnthalpyTrial - scalarCanopyEnthalpy, & ! intent(in): delta value for temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): trial vector of volumetric fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial vector of temperature + mLayerTempTrial - mLayerTemp, & ! intent(in): delta vector of temperature + mLayerEnthalpyTrial - mLayerEnthalpy, & ! intent(in): delta vector of temperature component of enthalpy for snow and soil + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) ! input: pre-computed derivatives dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) @@ -436,8 +436,8 @@ subroutine eval8summa(& mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output - heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil + heatCapVegTrial, & ! intent(out): trial value for volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(out): trial vector of heat capacity for snow and soil dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 24813dbb4..2bfc58745 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -101,9 +101,9 @@ subroutine eval8summaWithPrime(& mLayerVolFracWatPrime, & ! intent(out): prime value for volumetric total water content of each snow and soil layer (s-1) mLayerVolFracIcePrime, & ! intent(out): prime value for volumetric fraction of ice (s-1) ! output: enthalpy prime values - scalarCanairEnthalpyPrime, & ! intent(out): prime value for enthalpy of the canopy air space (J m-3 s-1) - scalarCanopyEnthalpyPrime, & ! intent(out): prime value of enthalpy of the vegetation canopy (J m-3 s-1) - mLayerEnthalpyPrime, & ! intent(out): prime vector of enthalpy for snow+soil layers (J m-3 s-1) + scalarCanairEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the canopy air space (J m-3 s-1) + scalarCanopyEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the vegetation canopy (J m-3 s-1) + mLayerEnthalpyPrime, & ! intent(out): prime vector for temperature component of enthalpy for snow+soil layers (J m-3 s-1) ! input-output: baseflow ixSaturation, & ! intent(inout): index of the lowest saturated layer dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) @@ -181,9 +181,9 @@ subroutine eval8summaWithPrime(& real(rkind),intent(out) :: mLayerVolFracWatPrime(:) ! prime vector for volumetric total water content of each snow and soil layer (s-1) real(rkind),intent(out) :: mLayerVolFracIcePrime(:) ! prime vector for volumetric fraction of ice (s-1) ! output: enthalpy prime values - real(rkind),intent(out) :: scalarCanairEnthalpyPrime ! prime value for enthalpy of the canopy air space (J m-3 s-1) - real(rkind),intent(out) :: scalarCanopyEnthalpyPrime ! prime value of enthalpy of the vegetation canopy (J m-3 s-1) - real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! prime vector of enthalpy for snow+soil layers (J m-3 s-1) + real(rkind),intent(out) :: scalarCanairEnthalpyPrime ! prime value for temperature component of enthalpy of the canopy air space (J m-3 s-1) + real(rkind),intent(out) :: scalarCanopyEnthalpyPrime ! prime value for temperature component of enthalpy of the vegetation canopy (J m-3 s-1) + real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! prime vector of temperature component of enthalpy for snow+soil layers (J m-3 s-1) ! input-output: baseflow integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) @@ -427,11 +427,11 @@ subroutine eval8summaWithPrime(& indx_data, & ! intent(in): model indices lookup_data, & ! intent(in): lookup table data structure ! input: state variables for the vegetation canopy - scalarCanairTempPrime, & ! intent(in): prime value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - scalarCanopyTempPrime, & ! intent(in): prime value of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(in): prime value of canopy total water (kg m-2) + scalarCanairTempPrime, & ! intent(in): prime value for canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) + scalarCanopyTempPrime, & ! intent(in): prime value for canopy temperature (K) + scalarCanopyWatPrime, & ! intent(in): prime value for canopy total water (kg m-2) ! input: variables for the snow-soil domain mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) @@ -442,9 +442,9 @@ subroutine eval8summaWithPrime(& ! input: pre-computed derivatives dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy prime - scalarCanairEnthalpyPrime, & ! intent(out): prime enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyPrime, & ! intent(out): prime enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyPrime, & ! intent(out): prime enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyPrime, & ! intent(out): prime vector of temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyPrime, & ! intent(out): prime vector of temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -460,16 +460,16 @@ subroutine eval8summaWithPrime(& indx_data, & ! intent(in): model layer indices diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value of the liquid water on the vegetation canopy (kg m-2) scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) scalarCanopyTempPrime, & ! intent(in): prime value of canopy temperature (K) - scalarCanopyEnthalpyPrime, & ! intent(in): prime enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpyPrime, & ! intent(in): prime value of temperature component of enthalpy of the vegetation canopy (J m-3) mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial temperature - mLayerTempPrime, & ! intent(in): prime temperature - mLayerEnthalpyPrime, & ! intent(in): prime enthalpy for snow and soil + mLayerTempTrial, & ! intent(in): trial vector of temperature + mLayerTempPrime, & ! intent(in): prime vector of temperature + mLayerEnthalpyPrime, & ! intent(in): prime vector of temperature component of enthalpy for snow and soil mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) ! input: pre-computed derivatives dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) @@ -821,9 +821,9 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%mLayerVolFracWatPrime, & ! intent(out): prime value for volumetric total water content of each snow and soil layer (s-1) eqns_data%mLayerVolFracIcePrime, & ! intent(out): prime value for volumetric fraction of ice (s-1) ! output: enthalpy prime values - eqns_data%scalarCanairEnthalpyPrime, & ! intent(out): prime value for enthalpy of the canopy air space (J m-3 s-1) - eqns_data%scalarCanopyEnthalpyPrime, & ! intent(out): prime value of enthalpy of the vegetation canopy (J m-3 s-1) - eqns_data%mLayerEnthalpyPrime, & ! intent(out): prime vector of enthalpy for snow+soil layers (J m-3 s-1) + eqns_data%scalarCanairEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the canopy air space (J m-3 s-1) + eqns_data%scalarCanopyEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the vegetation canopy (J m-3 s-1) + eqns_data%mLayerEnthalpyPrime, & ! intent(out): prime vector of temperature component of enthalpy for snow+soil layers (J m-3 s-1) ! input-output: baseflow eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 031b154b5..1405d7b74 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -218,42 +218,44 @@ subroutine summaSolve4ida( & ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- - type(N_Vector), pointer :: sunvec_y ! sundials solution vector - type(N_Vector), pointer :: sunvec_yp ! sundials derivative vector - type(N_Vector), pointer :: sunvec_av ! sundials tolerance vector - type(SUNMatrix), pointer :: sunmat_A ! sundials matrix - type(SUNLinearSolver), pointer :: sunlinsol_LS ! sundials linear solver - type(SUNNonLinearSolver), pointer :: sunnonlin_NLS ! sundials nonlinear solver - type(c_ptr) :: ida_mem ! IDA memory - type(c_ptr) :: sunctx ! SUNDIALS simulation context - type(data4ida), target :: eqns_data ! IDA type - integer(i4b) :: retval, retvalr ! return value - logical(lgt) :: feasible ! feasibility flag - real(qp) :: t0 ! starting time - real(qp) :: dt_last(1) ! last time step - real(qp) :: dt_diff ! difference from previous timestep - integer(c_long) :: mu, lu ! in banded matrix mode in SUNDIALS type - integer(c_long) :: nState ! total number of state variables in SUNDIALS type - integer(i4b) :: iVar, i ! indices - integer(i4b) :: nRoot ! total number of roots (events) to find - real(qp) :: tret(1) ! time in data window - real(qp) :: tretPrev ! previous time in data window - integer(i4b),allocatable :: rootsfound(:) ! crossing direction of discontinuities - integer(i4b),allocatable :: rootdir(:) ! forced crossing direction of discontinuities - logical(lgt) :: tinystep ! if step goes below small size - type(var_dlength) :: flux_prev ! previous model fluxes for a local HRU - character(LEN=256) :: cmessage ! error message of downwind routine - real(rkind),allocatable :: mLayerMatricHeadPrimePrev(:) ! previous derivative value for total water matric potential (m s-1) - real(rkind) :: scalarCanairEnthalpyPrimePrev ! previous derivative value for canopy air enthalpy (J m-3) - real(rkind) :: scalarCanopyEnthalpyPrimePrev ! previous derivative value for canopy enthalpy (J m-3) - real(rkind),allocatable :: mLayerEnthalpyPrimePrev(:) ! previous derivative value for total water enthalpy (J m-3) - real(rkind),allocatable :: fluxVecPrev(:) ! previous value for fluxes - real(rkind),allocatable :: resVecPrev(:) ! previous value for residuals - real(rkind),allocatable :: dCompress_dPsiPrev(:) ! previous derivative value soil compression + type(N_Vector), pointer :: sunvec_y ! sundials solution vector + type(N_Vector), pointer :: sunvec_yp ! sundials derivative vector + type(N_Vector), pointer :: sunvec_av ! sundials tolerance vector + type(SUNMatrix), pointer :: sunmat_A ! sundials matrix + type(SUNLinearSolver), pointer :: sunlinsol_LS ! sundials linear solver + type(SUNNonLinearSolver), pointer :: sunnonlin_NLS ! sundials nonlinear solver + type(c_ptr) :: ida_mem ! IDA memory + type(c_ptr) :: sunctx ! SUNDIALS simulation context + type(data4ida), target :: eqns_data ! IDA type + integer(i4b) :: retval, retvalr ! return value + logical(lgt) :: feasible ! feasibility flag + real(qp) :: t0 ! starting time + real(qp) :: dt_last(1) ! last time step + real(qp) :: dt_diff ! difference from previous timestep + integer(c_long) :: mu, lu ! in banded matrix mode in SUNDIALS type + integer(c_long) :: nState ! total number of state variables in SUNDIALS type + integer(i4b) :: iVar, i ! indices + integer(i4b) :: nRoot ! total number of roots (events) to find + real(qp) :: tret(1) ! time in data window + real(qp) :: tretPrev ! previous time in data window + integer(i4b),allocatable :: rootsfound(:) ! crossing direction of discontinuities + integer(i4b),allocatable :: rootdir(:) ! forced crossing direction of discontinuities + logical(lgt) :: tinystep ! if step goes below small size + type(var_dlength) :: flux_prev ! previous model fluxes for a local HRU + character(LEN=256) :: cmessage ! error message of downwind routine + real(rkind),allocatable :: mLayerMatricHeadPrimePrev(:) ! previous derivative value for total water matric potential (m s-1) + real(rkind) :: scalarCanairEnthalpyPrimePrev ! previous derivative value for canopy air enthalpy (J m-3) + real(rkind) :: scalarCanopyEnthalpyPrime_addphasePrev ! previous derivative value for canopy enthalpy (J m-3) + real(rkind),allocatable :: mLayerEnthalpyPrime_addphase(:) ! derivative value for total water enthalpy (J m-3) + real(rkind) :: scalarCanopyEnthalpyPrime_addphase ! derivative value for canopy enthalpy (J m-3) + real(rkind),allocatable :: mLayerEnthalpyPrime_addphasePrev(:) ! previous derivative value for total water enthalpy (J m-3) + real(rkind),allocatable :: fluxVecPrev(:) ! previous value for fluxes + real(rkind),allocatable :: resVecPrev(:) ! previous value for residuals + real(rkind),allocatable :: dCompress_dPsiPrev(:) ! previous derivative value soil compression ! flags - logical(lgt) :: use_fdJac ! flag to use finite difference Jacobian, controlled by decision fDerivMeth - logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true - logical(lgt),parameter :: detect_events = .true. ! flag to do event detection and restarting, default true + logical(lgt) :: use_fdJac ! flag to use finite difference Jacobian, controlled by decision fDerivMeth + logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true + logical(lgt),parameter :: detect_events = .true. ! flag to do event detection and restarting, default true ! ----------------------------------------------------------------------------------------------------- ! link to the necessary variables associate(& @@ -339,7 +341,8 @@ subroutine summaSolve4ida( & allocate( eqns_data%mLayerVolFracIcePrime(nLayers) ) allocate( eqns_data%mLayerEnthalpyPrime(nLayers) ) allocate( mLayerMatricHeadPrimePrev(nSoil) ) - allocate( mLayerEnthalpyPrimePrev(nLayers) ) + allocate( mLayerEnthalpyPrime_addphase(nLayers) ) + allocate( mLayerEnthalpyPrime_addphasePrev(nLayers) ) allocate( dCompress_dPsiPrev(nSoil) ) allocate( eqns_data%fluxVec(nState) ) allocate( eqns_data%resVec(nState) ) @@ -347,17 +350,21 @@ subroutine summaSolve4ida( & allocate( fluxVecPrev(nState) ) allocate( resVecPrev(nState) ) + !initialize + scalarCanopyEnthalpyPrime_addphase = 0._rkind + mLayerEnthalpyPrime_addphase(:) = 0._rkind + ! need the following values for the first substep - eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) - eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) - eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) - mLayerMatricHeadPrimePrev(:) = 0._rkind - scalarCanairEnthalpyPrimePrev = 0._rkind - scalarCanopyEnthalpyPrimePrev = 0._rkind - mLayerEnthalpyPrimePrev(:) = 0._rkind - dCompress_dPsiPrev(:) = 0._rkind - fluxVecPrev(:) = 0._rkind - resVecPrev(:) = 0._rkind + eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) + eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) + eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) + mLayerMatricHeadPrimePrev(:) = 0._rkind + scalarCanairEnthalpyPrimePrev = 0._rkind + scalarCanopyEnthalpyPrime_addphasePrev = 0._rkind + mLayerEnthalpyPrime_addphasePrev(:) = 0._rkind + dCompress_dPsiPrev(:) = 0._rkind + fluxVecPrev(:) = 0._rkind + resVecPrev(:) = 0._rkind retval = FSUNContext_Create(c_null_ptr, sunctx) @@ -549,38 +556,42 @@ subroutine summaSolve4ida( & eqns_data%scalarCanairTempPrime, & ! intent(in): prime value of canopy air temperature (K) eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) eqns_data%scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - eqns_data%scalarCanopyTempPrime, & ! intent(in): prime temperature of the vegetation canopy (K) - eqns_data%scalarCanopyWatPrime, & ! intent(in): prime total water of the vegetation canopy (kg m-2) + eqns_data%scalarCanopyTempPrime, & ! intent(in): prime value of temperature of the vegetation canopy (K) + eqns_data%scalarCanopyWatPrime, & ! intent(in): prime value of total water of the vegetation canopy (kg m-2) ! input: variables for the snow-soil domain eqns_data%mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) eqns_data%mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) eqns_data%mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - eqns_data%mLayerTempPrime, & ! intent(in): prime temperature of each snow+soil layer (K) - eqns_data%mLayerVolFracWatPrime, & ! intent(in): prime volumetric total water content of each snow+soil layer (-) - eqns_data%mLayerMatricHeadPrime, & ! intent(in): prime total water matric potential of each snow+soil layer (m) + eqns_data%mLayerTempPrime, & ! intent(in): prime vector of temperature of each snow+soil layer (K) + eqns_data%mLayerVolFracWatPrime, & ! intent(in): prime vector of volumetric total water content of each snow+soil layer (-) + eqns_data%mLayerMatricHeadPrime, & ! intent(in): prime vector of total water matric potential of each snow+soil layer (m) ! input: pre-computed derivatives deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy - eqns_data%scalarCanairEnthalpyPrime, & ! intent(out): prime enthalpy of the canopy air space (J m-3) - eqns_data%scalarCanopyEnthalpyPrime, & ! intent(out): prime enthalpy of the vegetation canopy (J m-3) - eqns_data%mLayerEnthalpyPrime, & ! intent(out): prime enthalpy of each snow+soil layer (J m-3) + eqns_data%scalarCanairEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the canopy air space (J m-3) + eqns_data%scalarCanopyEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the vegetation canopy (J m-3) + eqns_data%mLayerEnthalpyPrime, & ! intent(out): prime vector oftemperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif endif + ! initialize enthalpy prime with phase change, no phase change in canopy air space + scalarCanopyEnthalpyPrime_addphase = eqns_data%scalarCanopyEnthalpyPrime + mLayerEnthalpyPrime_addPhase = eqns_data%mLayerEnthalpyPrime + ! compute enthalpy prime with phase change call t2enthalpy_addphase(& ! input: data structures eqns_data%diag_data, & ! intent(in): model diagnostic variables for a local HRU eqns_data%indx_data, & ! intent(in): model indices ! input: state variables for the vegetation canopy - eqns_data%scalarCanopyIcePrime, & ! intent(in): prime value of canopy ice content (kg m-2 s-1) + eqns_data%scalarCanopyIcePrime, & ! intent(in): prime value for canopy ice content (kg m-2 s-1) ! input: variables for the snow-soil domain eqns_data%mLayerVolFracIcePrime, & ! intent(in): prime vector of ice volume fraction (s-1) ! input/output: enthalpy - eqns_data%scalarCanopyEnthalpyPrime, & ! intent(inout): prime enthalpy of the vegetation canopy (J m-3 s-1) - eqns_data%mLayerEnthalpyPrime, & ! intent(inout): prime enthalpy of each snow+soil layer (J m-3 s-1) + scalarCanopyEnthalpyPrime_addphase, & ! intent(inout): prime value for enthalpy of the vegetation canopy (J m-3 s-1) + mLayerEnthalpyPrime_addPhase, & ! intent(inout): prime vector of enthalpy of each snow+soil layer (J m-3 s-1) ! output: error control err,message) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -589,12 +600,12 @@ subroutine summaSolve4ida( & if(ixCasNrg/=integerMissing) balance(ixCasNrg) = balance(ixCasNrg) + ( eqns_data%scalarCanairEnthalpyPrime - eqns_data%fluxVec(ixCasNrg) & + scalarCanairEnthalpyPrimePrev - fluxVecPrev(ixCasNrg) )*dt_diff/2._rkind/dt !if(ixCasNrg/=integerMissing) balance(ixCasNrg) = balance(ixCasNrg) + ( eqns_data%resVec(ixCasNrg) + resVecPrev(ixCasNrg) )*dt_diff/2._rkind/dt ! should be equivalent to above, use for debugging - if(ixVegNrg/=integerMissing) balance(ixVegNrg) = balance(ixVegNrg) + ( eqns_data%scalarCanopyEnthalpyPrime - eqns_data%fluxVec(ixVegNrg) & - + scalarCanopyEnthalpyPrimePrev - fluxVecPrev(ixVegNrg) )*dt_diff/2._rkind/dt + if(ixVegNrg/=integerMissing) balance(ixVegNrg) = balance(ixVegNrg) + ( scalarCanopyEnthalpyPrime_addphase - eqns_data%fluxVec(ixVegNrg) & + + scalarCanopyEnthalpyPrime_addphasePrev - fluxVecPrev(ixVegNrg) )*dt_diff/2._rkind/dt if(nSnowSoilNrg>0)then do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) - balance(ixSnowSoilNrg(i)) = balance(ixSnowSoilNrg(i)) + ( eqns_data%mLayerEnthalpyPrime(i) - eqns_data%fluxVec(ixSnowSoilNrg(i)) & - + mLayerEnthalpyPrimePrev(i) - fluxVecPrev(ixSnowSoilNrg(i)) )*dt_diff/2._rkind/dt + balance(ixSnowSoilNrg(i)) = balance(ixSnowSoilNrg(i)) + ( mLayerEnthalpyPrime_addPhase(i) - eqns_data%fluxVec(ixSnowSoilNrg(i)) & + + mLayerEnthalpyPrime_addPhasePrev(i) - fluxVecPrev(ixSnowSoilNrg(i)) )*dt_diff/2._rkind/dt enddo endif endif @@ -616,18 +627,18 @@ subroutine summaSolve4ida( & endif ! save required quantities for next step - eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial - eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) - eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) - mLayerMatricHeadPrimePrev(:) = eqns_data%mLayerMatricHeadPrime(:) - scalarCanairEnthalpyPrimePrev = eqns_data%scalarCanairEnthalpyPrime - scalarCanopyEnthalpyPrimePrev = eqns_data%scalarCanopyEnthalpyPrime - mLayerEnthalpyPrimePrev(:) = eqns_data%mLayerEnthalpyPrime(:) - dCompress_dPsiPrev(:) = eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) - tretPrev = tret(1) - flux_prev = eqns_data%flux_data - fluxVecPrev(:) = eqns_data%fluxVec(:) - resVecPrev(:) = eqns_data%resVec(:) + eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial + eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) + eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) + mLayerMatricHeadPrimePrev(:) = eqns_data%mLayerMatricHeadPrime(:) + scalarCanairEnthalpyPrimePrev = eqns_data%scalarCanairEnthalpyPrime + scalarCanopyEnthalpyPrime_addphasePrev = scalarCanopyEnthalpyPrime_addphase + mLayerEnthalpyPrime_addphasePrev(:) = mLayerEnthalpyPrime_addphase(:) + dCompress_dPsiPrev(:) = eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) + tretPrev = tret(1) + flux_prev = eqns_data%flux_data + fluxVecPrev(:) = eqns_data%fluxVec(:) + resVecPrev(:) = eqns_data%resVec(:) ! Restart for where vegetation and layers cross freezing point if(detect_events)then @@ -683,7 +694,8 @@ subroutine summaSolve4ida( & deallocate( eqns_data%mLayerVolFracIcePrime ) deallocate( eqns_data%mLayerEnthalpyPrime ) deallocate( mLayerMatricHeadPrimePrev ) - deallocate( mLayerEnthalpyPrimePrev ) + deallocate( mLayerEnthalpyPrime_addphase ) + deallocate( mLayerEnthalpyPrime_addphasePrev ) deallocate( dCompress_dPsiPrev ) deallocate( eqns_data%fluxVec ) deallocate( eqns_data%resVec ) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 2cd49e801..702232d47 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -272,9 +272,9 @@ subroutine systemSolv(& ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) ! enthalpy - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) ! derivatives, diagnostic for enthalpy dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index a57c0ff16..352061a40 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -256,9 +256,9 @@ subroutine t2enthalpy(& mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpy, & ! intent(out): temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control err,message) ! intent(out): error control ! ------------------------------------------------------------------------------------------------------------------------- @@ -283,9 +283,9 @@ subroutine t2enthalpy(& real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) ! output: enthalpy - real(rkind),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - real(rkind),intent(out) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) + real(rkind),intent(out) :: scalarCanairEnthalpy ! temperature component of enthalpy of the canopy air space (J m-3) + real(rkind),intent(out) :: scalarCanopyEnthalpy ! temperature component of enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(out) :: mLayerEnthalpy(:) ! temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -530,12 +530,12 @@ subroutine t2enthalpy_addphase(& type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_ilength),intent(in) :: indx_data ! model indices ! input: state variables for the vegetation canopy - real(rkind),intent(in) :: scalarCanopyIce ! value of canopy ice content (kg m-2) or prime ice content (kg m-2 s-1) + real(rkind),intent(in) :: scalarCanopyIce ! value for canopy ice content (kg m-2) or prime ice content (kg m-2 s-1) ! input: variables for the snow-soil domain real(rkind),intent(in) :: mLayerVolFracIce(:) ! vector of volumetric fraction of ice (-) or prime volumetric fraction of ice (s-1) ! input output: enthalpy - real(rkind),intent(inout) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) or enthalpy prime (J m-3 s-1) - real(rkind),intent(inout) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) or enthalpy prime (J m-3 s-1) + real(rkind),intent(inout) :: scalarCanopyEnthalpy ! value for enthalpy of the vegetation canopy (J m-3) or enthalpy prime (J m-3 s-1) + real(rkind),intent(inout) :: mLayerEnthalpy(:) ! vector of enthalpy of each snow+soil layer (J m-3) or enthalpy prime (J m-3 s-1) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 6cbac426c..9339aca35 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -625,96 +625,101 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec doAdjustTemp,computeVegFlux,checkMassBalance, checkNrgBalance,computeEnthalpy, & ! input: model control model_decisions,lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! input-output: balances, flags, and error control -USE getVectorz_module,only:varExtract ! extract variables from the state vector +USE getVectorz_module,only:varExtract ! extract variables from the state vector #ifdef SUNDIALS_ACTIVE - USE updateVarsWithPrime_module,only:updateVarsWithPrime ! update prognostic variables + USE updateVarsWithPrime_module,only:updateVarsWithPrime ! update prognostic variables #endif - USE updateVars_module,only:updateVars ! update prognostic variables - USE t2enthalpy_module,only:t2enthalpy ! compute enthalpy - USE t2enthalpy_module,only:t2enthalpy_addphase ! add phase to enthalpy + USE updateVars_module,only:updateVars ! update prognostic variables + USE t2enthalpy_module,only:t2enthalpy ! compute enthalpy + USE t2enthalpy_module,only:t2enthalpy_addphase ! add phase to enthalpy implicit none ! model control - real(rkind) ,intent(in) :: dt ! time step (s) - integer(i4b) ,intent(in) :: nSnow ! number of snow layers - integer(i4b) ,intent(in) :: nSoil ! number of soil layers - integer(i4b) ,intent(in) :: nLayers ! total number of layers - logical(lgt) ,intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature - logical(lgt) ,intent(in) :: computeVegFlux ! flag to compute the vegetation flux - real(rkind) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) - real(rkind) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) - real(rkind) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) - logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance - logical(lgt) ,intent(in) :: checkNrgBalance ! flag to check the energy balance - logical(lgt) ,intent(in) :: computeEnthalpy ! flag to compute enthalpy + real(rkind) ,intent(in) :: dt ! time step (s) + integer(i4b) ,intent(in) :: nSnow ! number of snow layers + integer(i4b) ,intent(in) :: nSoil ! number of soil layers + integer(i4b) ,intent(in) :: nLayers ! total number of layers + logical(lgt) ,intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature + logical(lgt) ,intent(in) :: computeVegFlux ! flag to compute the vegetation flux + real(rkind) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) + real(rkind) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) + real(rkind) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) + logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance + logical(lgt) ,intent(in) :: checkNrgBalance ! flag to check the energy balance + logical(lgt) ,intent(in) :: computeEnthalpy ! flag to compute enthalpy ! data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(zLookup),intent(in) :: lookup_data ! lookup tables - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! indices for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! indices for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! balances, flags, and error control - real(rkind) ,intent(in) :: fluxVec(:) ! flux vector (mixed units) - real(qp) ,intent(in) :: resVec(:) ! NOTE: qp ! residual vector - real(rkind) ,intent(inout) :: balance(:) ! balance of energy per domain - logical(lgt) ,intent(out) :: waterBalanceError ! flag to denote that there is a water balance error - logical(lgt) ,intent(out) :: nrgFluxModified ! flag to denote that the energy fluxes were modified - integer(i4b) ,intent(out) :: err ! error code - character(*) ,intent(out) :: message ! error message + real(rkind) ,intent(in) :: fluxVec(:) ! flux vector (mixed units) + real(qp) ,intent(in) :: resVec(:) ! NOTE: qp ! residual vector + real(rkind) ,intent(inout) :: balance(:) ! balance of energy per domain + logical(lgt) ,intent(out) :: waterBalanceError ! flag to denote that there is a water balance error + logical(lgt) ,intent(out) :: nrgFluxModified ! flag to denote that the energy fluxes were modified + integer(i4b) ,intent(out) :: err ! error code + character(*) ,intent(out) :: message ! error message ! ================================================================================================================== ! general - integer(i4b) :: i ! indices - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: ixSubset ! index within the state subset - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixControlIndex ! index within a given domain - real(rkind) :: volMelt ! volumetric melt (kg m-3) - real(rkind),parameter :: verySmall=epsilon(1._rkind) ! a very small number (deal with precision issues) - real(rkind) :: verySmall_veg ! precision needs to vary based on set canopy water tolerance for IDA - real(rkind) :: verySmall_snow ! precision needs to vary based on set snow water tolerance for IDA + integer(i4b) :: i ! indices + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: ixSubset ! index within the state subset + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixControlIndex ! index within a given domain + real(rkind) :: volMelt ! volumetric melt (kg m-3) + real(rkind),parameter :: verySmall=epsilon(1._rkind) ! a very small number (deal with precision issues) + real(rkind) :: verySmall_veg ! precision needs to vary based on set canopy water tolerance for IDA + real(rkind) :: verySmall_snow ! precision needs to vary based on set snow water tolerance for IDA ! mass balance - real(rkind) :: canopyBalance0,canopyBalance1 ! canopy storage at start/end of time step - real(rkind) :: soilBalance0,soilBalance1 ! soil storage at start/end of time step - real(rkind) :: vertFlux ! change in storage due to vertical fluxes - real(rkind) :: tranSink,baseSink,compSink ! change in storage due to sink terms - real(rkind) :: liqError ! water balance error - real(rkind) :: fluxNet ! net water fluxes (kg m-2 s-1) - real(rkind) :: superflousWat ! superflous water used for evaporation (kg m-2 s-1) - real(rkind) :: superflousNrg ! superflous energy that cannot be used for evaporation (W m-2 [J m-2 s-1]) - character(LEN=256) :: cmessage ! error message of downwind routine + real(rkind) :: canopyBalance0,canopyBalance1 ! canopy storage at start/end of time step + real(rkind) :: soilBalance0,soilBalance1 ! soil storage at start/end of time step + real(rkind) :: vertFlux ! change in storage due to vertical fluxes + real(rkind) :: tranSink,baseSink,compSink ! change in storage due to sink terms + real(rkind) :: liqError ! water balance error + real(rkind) :: fluxNet ! net water fluxes (kg m-2 s-1) + real(rkind) :: superflousWat ! superflous water used for evaporation (kg m-2 s-1) + real(rkind) :: superflousNrg ! superflous energy that cannot be used for evaporation (W m-2 [J m-2 s-1]) + character(LEN=256) :: cmessage ! error message of downwind routine ! trial state variables - real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial vector for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector for liquid water matric potential (m) - real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) - real(rkind) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3) - real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector for enthalpy of snow + soil (J m-3) + real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial vector for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector for liquid water matric potential (m) + real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) + real(rkind) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3) + real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector for enthalpy of snow + soil (J m-3) ! diagnostic variables - real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric fraction of ice (-) - ! derivative of state variables - real(rkind) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatPrime ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempPrime ! trial vector for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! trial vector for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! trial vector for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! trial vector for liquid water matric potential (m) - real(rkind) :: scalarAquiferStoragePrime ! trial value for storage of water in the aquifer (m) + real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric fraction of ice (-) + ! derivative of state variables + real(rkind) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatPrime ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempPrime ! trial vector for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! trial vector for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! trial vector for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! trial vector for liquid water matric potential (m) + real(rkind) :: scalarAquiferStoragePrime ! trial value for storage of water in the aquifer (m) ! diagnostic variables - real(rkind) :: scalarCanopyLiqPrime ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector for volumetric fraction of ice (-) + real(rkind) :: scalarCanopyLiqPrime ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector for volumetric fraction of ice (-) + real(rkind) :: scalarCanopyEnthalpy_addphase ! trial value for senthalpy of the vegetation canopy with change (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpy_addphase ! trial vector of enthalpy of snow + soil with phase change (J m-3) + real(rkind) :: scalarCanopyEnthalpy_addphasePrev ! previous value for senthalpy of the vegetation canopy with change (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpy_addphasePrev ! previous vector of enthalpy of snow + soil with phase change (J m-3) + ! ------------------------------------------------------------------------------------------------------------------- ! ------------------------------------------------------------------------------------------------------------------- ! point to flux variables in the data structure @@ -769,9 +774,9 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(inout): [dp(:)] matric potential of liquid water (m) ! enthalpy - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(inout): [dp(:)] enthalpy of the snow+soil layers (J m-3) + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(inout): [dp] temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(inout): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(inout): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) ! model state variables (aquifer) scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(inout): [dp(:)] storage of water in the aquifer (m) ! error tolerance @@ -974,22 +979,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! compute enthalpy with phase change - call t2enthalpy_addphase(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): model indices - ! input: state variables for the vegetation canopy - scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric ice water content (-) - ! input/output: enthalpy - scalarCanopyEnthalpyTrial, & ! intent(inout): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(inout): enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,message) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + endif if(checkNrgBalance)then @@ -997,13 +987,52 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec select case(ixNumericalMethod) case(ida); ! do nothing, already computed case(kinsol, numrec) + + ! initialize enthalpy with phase change, no phase change in canopy air space + scalarCanopyEnthalpy_addphasePrev = scalarCanopyEnthalpy + mLayerEnthalpy_addphasePrev = mLayerEnthalpy + scalarCanopyEnthalpy_addphase = scalarCanopyEnthalpyTrial + mLayerEnthalpy_addphase = mLayerEnthalpyTrial + + ! compute enthalpy with phase change for previous values + call t2enthalpy_addphase(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): model indices + ! input: state variables for the vegetation canopy + scalarCanopyIce, & ! intent(in): previous trial value for canopy ice content (kg m-2) + ! input: variables for the snow-soil domain previous + mLayerVolFracIce, & ! intent(in): previous trial vector of volumetric ice water content (-) + ! input/output: enthalpyprevious + scalarCanopyEnthalpy_addphasePrev, & ! intent(inout): previous enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy_addphasePrev, & ! intent(inout): trial vector of previous enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,message) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! compute enthalpy with phase change for current values + call t2enthalpy_addphase(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): model indices + ! input: state variables for the vegetation canopy + scalarCanopyIceTrial, & ! intent(in): trial value for canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric ice water content (-) + ! input/output: enthalpy + scalarCanopyEnthalpy_addphase, & ! intent(inout): trial value for enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy_addphase, & ! intent(inout): trial vector of enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,message) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + ! compute energy balance, maybe should use to check for step reduction if(ixCasNrg/=integerMissing) balance(ixCasNrg) = scalarCanairEnthalpyTrial - scalarCanairEnthalpy - fluxVec(ixCasNrg)*dt !if(ixCasNrg/=integerMissing) balance(ixCasNrg) = resVec(ixCasNrg) ! should be equivalent to above, use for debugging - if(ixVegNrg/=integerMissing) balance(ixVegNrg) = scalarCanopyEnthalpyTrial - scalarCanopyEnthalpy - fluxVec(ixVegNrg)*dt + if(ixVegNrg/=integerMissing) balance(ixVegNrg) = scalarCanopyEnthalpy_addphase - scalarCanopyEnthalpy_addphasePrev - fluxVec(ixVegNrg)*dt if(nSnowSoilNrg>0)then do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) - balance(ixSnowSoilNrg(i)) = mLayerEnthalpyTrial(i) - mLayerEnthalpy(i) - fluxVec(ixSnowSoilNrg(i))*dt + balance(ixSnowSoilNrg(i)) = mLayerEnthalpy_addphase(i) - mLayerEnthalpy_addphasePrev(i) - fluxVec(ixSnowSoilNrg(i))*dt enddo endif end select From b464963b26158c40d441d1a4c6601a5934a677f9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 16 Dec 2023 06:56:20 +0900 Subject: [PATCH 1038/1472] call t2enthalpy_addphase on delta value --- build/source/engine/varSubstep.f90 | 208 +++++++++++++---------------- 1 file changed, 94 insertions(+), 114 deletions(-) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 9339aca35..c6ea3a5cf 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -625,100 +625,98 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec doAdjustTemp,computeVegFlux,checkMassBalance, checkNrgBalance,computeEnthalpy, & ! input: model control model_decisions,lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! input-output: balances, flags, and error control -USE getVectorz_module,only:varExtract ! extract variables from the state vector +USE getVectorz_module,only:varExtract ! extract variables from the state vector #ifdef SUNDIALS_ACTIVE - USE updateVarsWithPrime_module,only:updateVarsWithPrime ! update prognostic variables + USE updateVarsWithPrime_module,only:updateVarsWithPrime ! update prognostic variables #endif - USE updateVars_module,only:updateVars ! update prognostic variables - USE t2enthalpy_module,only:t2enthalpy ! compute enthalpy - USE t2enthalpy_module,only:t2enthalpy_addphase ! add phase to enthalpy + USE updateVars_module,only:updateVars ! update prognostic variables + USE t2enthalpy_module,only:t2enthalpy ! compute enthalpy + USE t2enthalpy_module,only:t2enthalpy_addphase ! add phase to enthalpy implicit none ! model control - real(rkind) ,intent(in) :: dt ! time step (s) - integer(i4b) ,intent(in) :: nSnow ! number of snow layers - integer(i4b) ,intent(in) :: nSoil ! number of soil layers - integer(i4b) ,intent(in) :: nLayers ! total number of layers - logical(lgt) ,intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature - logical(lgt) ,intent(in) :: computeVegFlux ! flag to compute the vegetation flux - real(rkind) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) - real(rkind) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) - real(rkind) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) - logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance - logical(lgt) ,intent(in) :: checkNrgBalance ! flag to check the energy balance - logical(lgt) ,intent(in) :: computeEnthalpy ! flag to compute enthalpy + real(rkind) ,intent(in) :: dt ! time step (s) + integer(i4b) ,intent(in) :: nSnow ! number of snow layers + integer(i4b) ,intent(in) :: nSoil ! number of soil layers + integer(i4b) ,intent(in) :: nLayers ! total number of layers + logical(lgt) ,intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature + logical(lgt) ,intent(in) :: computeVegFlux ! flag to compute the vegetation flux + real(rkind) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) + real(rkind) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) + real(rkind) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) + logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance + logical(lgt) ,intent(in) :: checkNrgBalance ! flag to check the energy balance + logical(lgt) ,intent(in) :: computeEnthalpy ! flag to compute enthalpy ! data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(zLookup),intent(in) :: lookup_data ! lookup tables - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! indices for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! indices for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! balances, flags, and error control - real(rkind) ,intent(in) :: fluxVec(:) ! flux vector (mixed units) - real(qp) ,intent(in) :: resVec(:) ! NOTE: qp ! residual vector - real(rkind) ,intent(inout) :: balance(:) ! balance of energy per domain - logical(lgt) ,intent(out) :: waterBalanceError ! flag to denote that there is a water balance error - logical(lgt) ,intent(out) :: nrgFluxModified ! flag to denote that the energy fluxes were modified - integer(i4b) ,intent(out) :: err ! error code - character(*) ,intent(out) :: message ! error message + real(rkind) ,intent(in) :: fluxVec(:) ! flux vector (mixed units) + real(qp) ,intent(in) :: resVec(:) ! NOTE: qp ! residual vector + real(rkind) ,intent(inout) :: balance(:) ! balance of energy per domain + logical(lgt) ,intent(out) :: waterBalanceError ! flag to denote that there is a water balance error + logical(lgt) ,intent(out) :: nrgFluxModified ! flag to denote that the energy fluxes were modified + integer(i4b) ,intent(out) :: err ! error code + character(*) ,intent(out) :: message ! error message ! ================================================================================================================== ! general - integer(i4b) :: i ! indices - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: ixSubset ! index within the state subset - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixControlIndex ! index within a given domain - real(rkind) :: volMelt ! volumetric melt (kg m-3) - real(rkind),parameter :: verySmall=epsilon(1._rkind) ! a very small number (deal with precision issues) - real(rkind) :: verySmall_veg ! precision needs to vary based on set canopy water tolerance for IDA - real(rkind) :: verySmall_snow ! precision needs to vary based on set snow water tolerance for IDA + integer(i4b) :: i ! indices + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: ixSubset ! index within the state subset + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixControlIndex ! index within a given domain + real(rkind) :: volMelt ! volumetric melt (kg m-3) + real(rkind),parameter :: verySmall=epsilon(1._rkind) ! a very small number (deal with precision issues) + real(rkind) :: verySmall_veg ! precision needs to vary based on set canopy water tolerance for IDA + real(rkind) :: verySmall_snow ! precision needs to vary based on set snow water tolerance for IDA ! mass balance - real(rkind) :: canopyBalance0,canopyBalance1 ! canopy storage at start/end of time step - real(rkind) :: soilBalance0,soilBalance1 ! soil storage at start/end of time step - real(rkind) :: vertFlux ! change in storage due to vertical fluxes - real(rkind) :: tranSink,baseSink,compSink ! change in storage due to sink terms - real(rkind) :: liqError ! water balance error - real(rkind) :: fluxNet ! net water fluxes (kg m-2 s-1) - real(rkind) :: superflousWat ! superflous water used for evaporation (kg m-2 s-1) - real(rkind) :: superflousNrg ! superflous energy that cannot be used for evaporation (W m-2 [J m-2 s-1]) - character(LEN=256) :: cmessage ! error message of downwind routine + real(rkind) :: canopyBalance0,canopyBalance1 ! canopy storage at start/end of time step + real(rkind) :: soilBalance0,soilBalance1 ! soil storage at start/end of time step + real(rkind) :: vertFlux ! change in storage due to vertical fluxes + real(rkind) :: tranSink,baseSink,compSink ! change in storage due to sink terms + real(rkind) :: liqError ! water balance error + real(rkind) :: fluxNet ! net water fluxes (kg m-2 s-1) + real(rkind) :: superflousWat ! superflous water used for evaporation (kg m-2 s-1) + real(rkind) :: superflousNrg ! superflous energy that cannot be used for evaporation (W m-2 [J m-2 s-1]) + character(LEN=256) :: cmessage ! error message of downwind routine ! trial state variables - real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial vector for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector for liquid water matric potential (m) - real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) - real(rkind) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3) - real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector for enthalpy of snow + soil (J m-3) + real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial vector for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector for liquid water matric potential (m) + real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) + real(rkind) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3) + real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector for enthalpy of snow + soil (J m-3) ! diagnostic variables - real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric fraction of ice (-) + real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric fraction of ice (-) ! derivative of state variables - real(rkind) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatPrime ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempPrime ! trial vector for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! trial vector for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! trial vector for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! trial vector for liquid water matric potential (m) - real(rkind) :: scalarAquiferStoragePrime ! trial value for storage of water in the aquifer (m) + real(rkind) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatPrime ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempPrime ! trial vector for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! trial vector for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! trial vector for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! trial vector for liquid water matric potential (m) + real(rkind) :: scalarAquiferStoragePrime ! trial value for storage of water in the aquifer (m) ! diagnostic variables - real(rkind) :: scalarCanopyLiqPrime ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector for volumetric fraction of ice (-) - real(rkind) :: scalarCanopyEnthalpy_addphase ! trial value for senthalpy of the vegetation canopy with change (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthalpy_addphase ! trial vector of enthalpy of snow + soil with phase change (J m-3) - real(rkind) :: scalarCanopyEnthalpy_addphasePrev ! previous value for senthalpy of the vegetation canopy with change (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthalpy_addphasePrev ! previous vector of enthalpy of snow + soil with phase change (J m-3) + real(rkind) :: scalarCanopyLiqPrime ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector for volumetric fraction of ice (-) + real(rkind) :: scalarCanopyEnthalpyDelta_addphase ! delta value for enthalpy of the vegetation canopy with change (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpyDelta_addphase ! delta vector of enthalpy of snow + soil with phase change (J m-3) ! ------------------------------------------------------------------------------------------------------------------- ! ------------------------------------------------------------------------------------------------------------------- @@ -988,51 +986,33 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec case(ida); ! do nothing, already computed case(kinsol, numrec) - ! initialize enthalpy with phase change, no phase change in canopy air space - scalarCanopyEnthalpy_addphasePrev = scalarCanopyEnthalpy - mLayerEnthalpy_addphasePrev = mLayerEnthalpy - scalarCanopyEnthalpy_addphase = scalarCanopyEnthalpyTrial - mLayerEnthalpy_addphase = mLayerEnthalpyTrial - - ! compute enthalpy with phase change for previous values + ! initialize delta enthalpy with phase change, no phase change in canopy air space + scalarCanopyEnthalpyDelta_addphase = scalarCanopyEnthalpyTrial - scalarCanopyEnthalpy + mLayerEnthalpyDelta_addphase = mLayerEnthalpyTrial - mLayerEnthalpy + + ! compute enthalpy with phase change for current values, do on delta value so only have to do once call t2enthalpy_addphase(& ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): model indices - ! input: state variables for the vegetation canopy - scalarCanopyIce, & ! intent(in): previous trial value for canopy ice content (kg m-2) - ! input: variables for the snow-soil domain previous - mLayerVolFracIce, & ! intent(in): previous trial vector of volumetric ice water content (-) - ! input/output: enthalpyprevious - scalarCanopyEnthalpy_addphasePrev, & ! intent(inout): previous enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy_addphasePrev, & ! intent(inout): trial vector of previous enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,message) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! compute enthalpy with phase change for current values - call t2enthalpy_addphase(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): model indices - ! input: state variables for the vegetation canopy - scalarCanopyIceTrial, & ! intent(in): trial value for canopy ice content (kg m-2) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): model indices + ! input: state variables for the ve getation canopy + scalarCanopyIceTrial - scalarCanopyIce, & ! intent(in): delta value for canopy ice content (kg m-2) ! input: variables for the snow-soil domain - mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric ice water content (-) + mLayerVolFracIceTrial - mLayerVolFracIce, & ! intent(in): delta vector of volumetric ice water content (-) ! input/output: enthalpy - scalarCanopyEnthalpy_addphase, & ! intent(inout): trial value for enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy_addphase, & ! intent(inout): trial vector of enthalpy of each snow+soil layer (J m-3) + scalarCanopyEnthalpyDelta_addphase, & ! intent(inout): delta value for enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyDelta_addphase, & ! intent(inout): delta vector of enthalpy of each snow+soil layer (J m-3) ! output: error control err,message) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - + ! compute energy balance, maybe should use to check for step reduction if(ixCasNrg/=integerMissing) balance(ixCasNrg) = scalarCanairEnthalpyTrial - scalarCanairEnthalpy - fluxVec(ixCasNrg)*dt !if(ixCasNrg/=integerMissing) balance(ixCasNrg) = resVec(ixCasNrg) ! should be equivalent to above, use for debugging - if(ixVegNrg/=integerMissing) balance(ixVegNrg) = scalarCanopyEnthalpy_addphase - scalarCanopyEnthalpy_addphasePrev - fluxVec(ixVegNrg)*dt + if(ixVegNrg/=integerMissing) balance(ixVegNrg) = scalarCanopyEnthalpyDelta_addphase - fluxVec(ixVegNrg)*dt if(nSnowSoilNrg>0)then do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) - balance(ixSnowSoilNrg(i)) = mLayerEnthalpy_addphase(i) - mLayerEnthalpy_addphasePrev(i) - fluxVec(ixSnowSoilNrg(i))*dt + balance(ixSnowSoilNrg(i)) = mLayerEnthalpyDelta_addphase(i) - fluxVec(ixSnowSoilNrg(i))*dt enddo endif end select From 8277c0ed8b4c0ecf1b2f6925c9027aca60211064 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 16 Dec 2023 04:32:40 -0600 Subject: [PATCH 1039/1472] Calculation of stateMask is now handled via the split_select object in opSplittin. --- build/source/engine/opSplittin.f90 | 148 +++++++++++------------------ 1 file changed, 57 insertions(+), 91 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 30b357ca2..b028d3896 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -152,10 +152,12 @@ module opSplittin_module integer(i4b) :: iStateTypeSplit integer(i4b) :: iDomainSplit integer(i4b) :: iStateSplit + integer(i4b) :: nSubset ! number of selected state variables for a given split type(var_flagVec) :: fluxMask ! integer mask defining model fluxes - logical(lgt),allocatable :: stateMask(:) ! mask defining desired state variables + logical(lgt),allocatable :: stateMask(:) ! mask defining desired state variables contains - procedure :: get_stateMask => compute_stateMask ! currently under development in opSplittin + procedure :: get_stateMask => compute_stateMask ! compute stateMask and nSubset and load into class object + procedure :: get_indices => initialize_indices ! load operator splitting indices into class object end type split_select_type @@ -320,13 +322,13 @@ subroutine opSplittin(& type(in_type_indexSplit) :: in_indexSplit; type(out_type_indexSplit) :: out_indexSplit; ! indexSplit arguments type(in_type_varSubstep) :: in_varSubstep; type(io_type_varSubstep) :: io_varSubstep; type(out_type_varSubstep) :: out_varSubstep; ! varSubstep arguments ! ------------------------------------------------------------------------------------------------------------------------- - type(split_select_type),allocatable :: split_select(:) ! class object for selecting operator splitting methods + type(split_select_type) :: split_select ! class object for selecting operator splitting methods + + ! initialize debug flags + stop_flag=.false. ! *** Initialize Split Selector Object *** - stop_flag=.false. - if (test_flag.eqv..true.) then - call initialize_split_select - end if + call initialize_split_select call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs coupling: do ixCoupling=1,nCoupling ! loop through different coupling strategies @@ -345,16 +347,22 @@ subroutine opSplittin(& call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs stateSplit: do iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) -!!SJT -! if (ixSolution==nSolutions) then -! write(*,*) "nStateSplit=",nStateSplit -! write(*,*) ixCoupling,iStateTypeSplit,ixStateThenDomain,iDomainSplit,ixSolution,iStateSplit,nState -! stop_flag=.true. -! if (stop_flag.eqv..true.) stop -! end if -!!End SJT + + if (test_flag.eqv..true.) then + if (ixSolution==nSolutions) then + write(*,*) "nStateSplit=",nStateSplit + write(*,*) ixCoupling,iStateTypeSplit,ixStateThenDomain,iDomainSplit,ixSolution,iStateSplit,nState + stop_flag=.true. + if (stop_flag.eqv..true.) stop + end if + end if + ! define state subsets for a given split... - call update_stateFilter; if (return_flag.eqv..true.) return ! get the mask for the state subset - return for a non-zero error code + call split_select % get_indices(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) + call split_select % get_stateMask(indx_data,err,cmessage,message,return_flag) + nSubset = split_select % nSubset; stateMask = split_select % stateMask + if (return_flag.eqv..true.) return + !call update_stateFilter; if (return_flag.eqv..true.) return ! get the mask for the state subset - return for a non-zero error code call validate_split ! verify that the split is valid if (cycle_domainSplit) cycle domainSplit if (cycle_solution) cycle solution @@ -403,6 +411,9 @@ subroutine initialize_split_select ! *** Initialize split_select class object - currently under development *** integer(i4b) :: nSplit + ! allocate data components + allocate(split_select % stateMask(1:nState)) ! allocate split_select components + ! determine total number of possible splits return_flag=.false. ! initialize flag call get_nCoupling; if (return_flag.eqv..true.) return ! get nCoupling value @@ -413,73 +424,6 @@ subroutine initialize_split_select ! nSplit=nCoupling*nStateTypeSplit*(1+tryDomainSplit)*nDomainSplit*nSolutions*nStateSplit if (test_flag.eqv..true.) print *, "Test Ouput: ",nCoupling,nStateTypeSplit,tryDomainSplit,nDomainSplit -! ! opSplittin loop structure -! call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs -! coupling: do ixCoupling=1,nCoupling ! loop through different coupling strategies -! -! call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs -! stateTypeSplitting: do iStateTypeSplit=1,nStateTypeSplit ! state splitting loop -! -! ! first try the state type split, then try the domain split within a given state type -! call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split -! stateThenDomain: do ixStateThenDomain=1,1+tryDomainSplit ! 1=state type split; 2=domain split within a given state type -! -! call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs -! domainSplit: do iDomainSplit=1,nDomainSplit ! domain splitting loop -! -! solution: do ixSolution=1,nSolutions ! trial with the vector then scalar solution -! -! call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs -! stateSplit: do iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) -! -!!!! Draft -- assign data components to class object -!! possible class object for handling split selection -!!split_select(ixCoupling,iStateTypeSplit,ixStateThenDomain,iDomainSplit,ixSolution,iStateSplit) -!!!! End Draft -! -!! ! define state subsets for a given split... -!! call update_stateFilter; if (return_flag.eqv..true.) return ! get the mask for the state subset - return for a non-zero error code -!! call validate_split ! verify that the split is valid -!! if (cycle_domainSplit) cycle domainSplit -!! if (cycle_solution) cycle solution -!! if (return_flag.eqv..true.) return ! return for a non-zero error code -!! call save_recover ! save/recover copies of variables and fluxes -! -!! ! assemble vectors for a given split... -!! call get_split_indices; if (return_flag.eqv..true.) return ! get indices for a given split - return for a non-zero error code -!! call update_fluxMask; if (return_flag.eqv..true.) return ! define the mask of the fluxes used - return for a non-zero error code -! -!! call solve_subset; if (return_flag.eqv..true.) return ! solve variable subset for one time step - return for a positive error code -! -!! call assess_solution; if (return_flag.eqv..true.) return ! is solution a success or failure? - return for a recovering solution -! -!! call try_other_solution_methods ! if solution failed to converge, try other splitting methods -!! if (cycle_coupling) cycle coupling ! exit loops if necessary -!! if (cycle_stateThenDomain) cycle stateThenDomain -!! if (cycle_solution) cycle solution -! -!! call confirm_variable_updates; if (return_flag.eqv..true.) return ! check that state variables updated - return if error -! -!! call success_check ! check for success -!! if (exit_stateThenDomain) exit stateThenDomain ! exit loops if necessary -!! if (exit_solution) exit solution -!! if (return_flag.eqv..true.) return ! return if error -! -! end do stateSplit ! solution with split layers -! -! end do solution ! trial with the full layer solution then the split layer solution -! call finalize_solution ! final steps following solution loop -! -! end do domainSplit ! domain type splitting loop -! -! end do stateThenDomain ! switch between the state type and domain type splitting -! call finalize_stateThenDomain ! final steps following the stateThenDomain loop -! -! end do stateTypeSplitting ! state type splitting loop -! call finalize_stateTypeSplitting; if (exit_coupling) exit coupling ! success = exit the coupling loop -! -! end do coupling ! loop over coupling methods -! call finalize_coupling ! check variables and fluxes, and apply step halving if needed end subroutine initialize_split_select @@ -1097,17 +1041,37 @@ end subroutine update_fluxMask end subroutine opSplittin -subroutine compute_stateMask(split_select,in_stateFilter,indx_data,out_stateFilter,nSubset,err,cmessage,message,return_flag) +! ****** Class procedures for split_select_type class ****** + +subroutine initialize_indices(split_select,ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) + ! *** initialize operator aplitting indices for split_select_type class *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + integer(i4b),intent(in) :: ixCoupling + integer(i4b),intent(in) :: ixSolution + integer(i4b),intent(in) :: ixStateThenDomain + integer(i4b),intent(in) :: iStateTypeSplit + integer(i4b),intent(in) :: iDomainSplit + integer(i4b),intent(in) :: iStateSplit + split_select % ixCoupling = ixCoupling + split_select % ixSolution = ixSolution + split_select % ixStateThenDomain = ixStateThenDomain + split_select % iStateTypeSplit = iStateTypeSplit + split_select % iDomainSplit = iDomainSplit + split_select % iStateSplit = iStateSplit +end subroutine initialize_indices + +subroutine compute_stateMask(split_select,indx_data,err,cmessage,message,return_flag) ! *** Get the mask for the state subset *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector - type(in_type_stateFilter),intent(out) :: in_stateFilter ! indices - type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU - type(out_type_stateFilter),intent(out) :: out_stateFilter ! number of selected state variables for a given split and error control - integer(i4b),intent(out) :: nSubset ! intent(out): number of selected state variables for a given split + type(var_ilength),intent(in) :: indx_data ! indices for a local HRU integer(i4b),intent(out) :: err ! intent(out): error code character(*),intent(out) :: cmessage ! intent(out): error message character(*),intent(out) :: message ! error message logical(lgt),intent(out) :: return_flag + ! local variables + type(in_type_stateFilter) :: in_stateFilter ! indices + type(out_type_stateFilter) :: out_stateFilter ! number of selected state variables for a given split and error control + return_flag=.false. ! initialize flag associate(& ixCoupling => split_select % ixCoupling ,& @@ -1121,7 +1085,9 @@ subroutine compute_stateMask(split_select,in_stateFilter,indx_data,out_stateFilt associate(stateMask => split_select % stateMask) call stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) end associate - call out_stateFilter % finalize(nSubset,err,cmessage) + associate(nSubset => split_select % nSubset) + call out_stateFilter % finalize(nSubset,err,cmessage) + end associate if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! error control end subroutine compute_stateMask @@ -1134,7 +1100,7 @@ subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) implicit none ! input type(in_type_stateFilter),intent(in) :: in_stateFilter ! indices - type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices for a local HRU ! output logical(lgt),intent(out) :: stateMask(:) ! mask defining desired state variables type(out_type_stateFilter),intent(out) :: out_stateFilter ! number of selected state variables for a given split and error control @@ -1159,7 +1125,7 @@ subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) call stateTypeSplit_stateMask; if (return_flag.eqv..true.) return ! get stateMask for state split method -- return if error ! check case default; err=20; message=trim(message)//'unable to identify coupling method'; return - end select ! (selecting solution method) + end select ! selecting solution method end associate call identify_scalar_solutions; if (return_flag.eqv..true.) return ! identify scalar solutions -- return if error occurs From 975690d2bec674d73947f86899e23ec53e122cf0 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 16 Dec 2023 05:38:14 -0600 Subject: [PATCH 1040/1472] Introduced split_select loop in opSplittin -- currently under development. --- build/source/engine/opSplittin.f90 | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index b028d3896..3d130eac5 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -160,6 +160,10 @@ module opSplittin_module procedure :: get_indices => initialize_indices ! load operator splitting indices into class object end type split_select_type +type, public :: split_index_type ! class for operator splitting indices + integer(i4b) :: ixCoupling + contains +end type split_index_type contains @@ -313,6 +317,7 @@ subroutine opSplittin(& ! loop control logical(lgt) :: exit_coupling,exit_stateTypeSplitting,exit_stateThenDomain,exit_domainSplit,exit_solution,exit_stateSplit logical(lgt) :: cycle_coupling,cycle_stateTypeSplitting,cycle_stateThenDomain,cycle_domainSplit,cycle_solution,cycle_stateSplit + integer(i4b) :: iSplit,nSplit ! debug testing logical(lgt), parameter :: test_flag=.false. ! to create test output logical(lgt) :: stop_flag ! to control stopping @@ -323,12 +328,14 @@ subroutine opSplittin(& type(in_type_varSubstep) :: in_varSubstep; type(io_type_varSubstep) :: io_varSubstep; type(out_type_varSubstep) :: out_varSubstep; ! varSubstep arguments ! ------------------------------------------------------------------------------------------------------------------------- type(split_select_type) :: split_select ! class object for selecting operator splitting methods - + type(split_index_type),allocatable :: split_index(:) ! class object for operator splitting indices + ! initialize debug flags stop_flag=.false. ! *** Initialize Split Selector Object *** call initialize_split_select + !split_select: do iSplit=1,nSplit call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs coupling: do ixCoupling=1,nCoupling ! loop through different coupling strategies @@ -405,11 +412,23 @@ subroutine opSplittin(& end do coupling ! loop over coupling methods call finalize_coupling ! check variables and fluxes, and apply step halving if needed + !end do split_select contains + subroutine initialize_split + ! *** Initialize split *** + + call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs + + ! allocate split_index object + allocate(split_index(1:nSplit)) + + ! convert iSplit to ixCoupling + split_index(iSplit) % ixCoupling = iSplit + end subroutine initialize_split + subroutine initialize_split_select ! *** Initialize split_select class object - currently under development *** - integer(i4b) :: nSplit ! allocate data components allocate(split_select % stateMask(1:nState)) ! allocate split_select components @@ -422,6 +441,7 @@ subroutine initialize_split_select ! add update to stateMask here ! call get_nStateSplit(nSolutions); if (return_flag.eqv..true.) return ! get nStateSplit value ! nSplit=nCoupling*nStateTypeSplit*(1+tryDomainSplit)*nDomainSplit*nSolutions*nStateSplit + nSplit=nCoupling ! temporary definition -- adding one opSplittin loop at a time if (test_flag.eqv..true.) print *, "Test Ouput: ",nCoupling,nStateTypeSplit,tryDomainSplit,nDomainSplit From 949104b28fd914872c32dc4b8b4c97db28c5a8a6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 2 Jan 2024 11:59:33 +0900 Subject: [PATCH 1041/1472] printing stuff --- build/source/engine/varSubstep.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index c6ea3a5cf..56a5a5185 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -1005,7 +1005,8 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! output: error control err,message) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - + print*, mLayerEnthalpyTrial(1),mLayerEnthalpy(1),mLayerEnthalpyTrial(1) - mLayerEnthalpy(1),mLayerEnthalpyDelta_addphase(1) + ! compute energy balance, maybe should use to check for step reduction if(ixCasNrg/=integerMissing) balance(ixCasNrg) = scalarCanairEnthalpyTrial - scalarCanairEnthalpy - fluxVec(ixCasNrg)*dt !if(ixCasNrg/=integerMissing) balance(ixCasNrg) = resVec(ixCasNrg) ! should be equivalent to above, use for debugging From c61cdd9a4b8159478c4c8580473cafbfb22d3670 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 6 Jan 2024 10:16:03 +0900 Subject: [PATCH 1042/1472] utils --- utils/hist_per_GRU.py | 2 +- utils/plot_per_GRU.py | 19 ++- utils/plot_per_GRUMult.py | 348 ++++++++++++++++++++++++++++++++++++++ utils/process_plot.sh | 2 +- 4 files changed, 365 insertions(+), 6 deletions(-) create mode 100644 utils/plot_per_GRUMult.py diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 5353fd5ac..7571f8e8c 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -40,7 +40,7 @@ #method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument #plt_name=['BE1','IDAe-4','BE4','BE8','BE16','BE32','IDAe-6'] #maybe make this an argument method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument - plt_name=['BE1','BE16','BE32','IDAe-6'] #maybe make this an argument + plt_name=['BE1','BE16','BE32','SUNDIALS'] #maybe make this an argument if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE # Define the power transformation function diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 73fc5fd74..fe7c370bd 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -266,6 +266,17 @@ def make_default_path(suffix): plt.tight_layout() def run_loop(i,var,the_max,f_x,f_y): + stat0 = stat + if stat == 'rmse' or stat == 'kgem' or stat == 'mean': + if plot_var == 'wallClockTime': stat0 = 'mean' + statr = 'mean_ben' + if stat == 'rmnz' or stat == 'mnnz': + if plot_var == 'wallClockTime': stat0 = 'mnnz' + statr = 'mnnz_ben' + if stat == 'maxe' or stat == 'amax': + if plot_var == 'wallClockTime': stat0 = 'amax' + statr = 'amax_ben' + my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap my_cmap.set_bad(color='white') #nan color white vmin,vmax = 0, the_max @@ -289,9 +300,9 @@ def run_loop(i,var,the_max,f_x,f_y): if stat0 == 'rmnz': stat_word = 'RMSE' # no 0s' if stat0 == 'maxe': stat_word = 'max abs error' if stat0 == 'kgem': stat_word = 'KGE"' - if stat0 == 'mean': stat_word = 'abs mean' - if stat0 == 'mnnz': stat_word = 'abs mean' # no 0s' - if stat0 == 'amax': stat_word = 'abs max' + if stat0 == 'mean': stat_word = 'mean' + if stat0 == 'mnnz': stat_word = 'mean' # no 0s' + if stat0 == 'amax': stat_word = 'max' if statr == 'mean_ben': statr_word = 'mean' if statr == 'mnnz_ben': statr_word = 'mean' # no 0s' @@ -311,7 +322,7 @@ def run_loop(i,var,the_max,f_x,f_y): if stat == 'maxe' or stat == 'amax': cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titlm[i]), labelpad=40, rotation=270) if stat == 'kgem': cbr.ax.set_ylabel(stat_word, labelpad=40, rotation=270) #if do_rel and var!='wallClockTime': cbr.ax.set_ylabel(stat_word + ' rel to bench ' + statr_word, labelpad=40, rotation=270) - if do_rel and var!='wallClockTime': cbr.ax.set_ylabel('relative '+ stat_word) + if do_rel and var!='wallClockTime': cbr.ax.set_ylabel('relative '+ stat_word, labelpad=40, rotation=270) #cbr.ax.yaxis.set_offset_position('right') diff --git a/utils/plot_per_GRUMult.py b/utils/plot_per_GRUMult.py new file mode 100644 index 000000000..8a0c84b3d --- /dev/null +++ b/utils/plot_per_GRUMult.py @@ -0,0 +1,348 @@ +# written originally by W. Knoben, modified by A. Van Beusekom (2023) + +## Visualize statistics per GRU +## Needs: +# Catchment shapefile with GRU delineation +# SUMMA output statistics + +## Special note +# SUMMA simulations have been preprocessed into single value statistics per model element, using auxiliary scripts in ~/utils +# To improve visualization of large lakes, HydroLAKES lake delineations are plotted on top of the catchment GRUs and river segments. +# Dealing with HydroLAKES inputs is not considered within scope of the workflow and therefore requires some manual downloading and preprocessing of this data for those who wish to reproduce this step. +# The relevant code is easily disabled by switching the plot_lakes = True flag to False. + +# Run: +# python plot_per_GRUMult.py [stat] +# where stat is rmse or maxe or kgem + + +# modules +import sys +import os +import matplotlib +import numpy as np +import xarray as xr +from pathlib import Path +import matplotlib.pyplot as plt +import copy +import pyproj +import fiona +import geopandas as gpd +import pandas as pd + +# plot all runs, pick statistic +stat = sys.argv[1] +method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument +plt_name=['(a) SUMMA-BE1','(b) SUMMA-BE16','(c) SUMMA-BE32','(d) SUMMA-SUNDIALS'] #maybe make this an argument + +# Simulation statistics file locations +settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] +viz_dir = Path('/home/avanb/scratch/statistics') +viz_fil = method_name.copy() +eff_fil = method_name.copy() +for i, m in enumerate(method_name): + viz_fil[i] = m + '_hrly_diff_stats_{}.nc' + viz_fil[i] = viz_fil[i].format(','.join(settings)) + eff_fil[i] = 'eff_' + m + '.txt' +nbatch_hrus = 518 # number of HRUs per batch +use_eff = False # use efficiency in wall clock time +do_rel = True # plot relative to the benchmark simulation +if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE + +# Specify variables of interest +plot_vars = settings.copy() +plt_titl = ['Snow Water Equivalent','Total soil water content','Total evapotranspiration', 'Total water on the vegetation canopy','Average routed runoff','Wall clock time'] +leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] +leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$','$s$'] + +fig_fil= '_hrly_diff_stats_{}_compressed.png' +if do_rel: fig_fil = '_hrly_diff_stats_{}_rel_compressed.png' + +if stat == 'rmse': + maxes = [2,15,250,0.08,200,10e-3] #[2,15,8e-6,0.08,6e-9,10e-3] + #maxes = [0.25,2,30,0.01,30,2e-3] #[0.25,2,1e-6,0.01,1e-9,2e-3] + if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] +if stat == 'rmnz': + maxes = [2,15,250,0.08,200,10e-3] + if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] +if stat == 'maxe': + maxes = [15,25,0.8,2,0.3,0.2] #[15,25,25e-5,2,1e-7,0.2] + if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,0.2] +if stat == 'kgem': + maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] +if stat == 'mean': + maxes = [80,1500,1500,3000,10e-3] #[80,1500,5e-5,8,1e-7,10e-3] + if do_rel: maxes = [1.1,1.1,1.1,1.1,1.1,10e-3] +if stat == 'amax': + maxes = [240,1800,3.5,25,7.5,0.2] #[240,1800,1e-3,25,2e-6,0.2] + if do_rel: maxes = [1.1,1.1,1.1,1.1,1.1,0.2] + +# Get the albers shapes +main = Path('/home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/albers_projection') + +# Plot lakes? +plot_lakes = True +# lakes shapefile WHERE IS THIS +#lake_path = Path('C:/Globus endpoint/HydroLAKES/HydroLAKES_polys_v10_shp') +#lake_name = 'HydroLAKES_polys_v10_subset_NA.shp' + +## Control file handling +# Store the name of the 'active' file in a variable +controlFile = 'plot_control_NorthAmerica.txt' + +# Function to extract a given setting from the control file +def read_from_control( file, setting ): + + # Open controlFile and ... + with open(file) as contents: + for line in contents: + + # ... find the line with the requested setting + if setting in line and not line.startswith('#'): + break + + # Extract the setting's value + substring = line.split('|',1)[1] # Remove the setting's name (split into 2 based on '|', keep only 2nd part) + substring = substring.split('#',1)[0] # Remove comments, does nothing if no '#' is found + substring = substring.strip() # Remove leading and trailing whitespace, tabs, newlines + + # Return this value + return substring + +# Function to specify a default path +def make_default_path(suffix): + + # Get the root path + rootPath = Path( read_from_control(controlFile,'root_path') ) + + # Get the domain folder + domainName = read_from_control(controlFile,'domain_name') + domainFolder = 'domain_' + domainName + + # Specify the forcing path + defaultPath = rootPath / domainFolder / suffix + + return defaultPath + +## Catchment shapefile location and variable names +# HM catchment shapefile path & name +hm_catchment_path = read_from_control(controlFile,'catchment_shp_path') +hm_catchment_name = read_from_control(controlFile,'catchment_shp_name') +# Specify default path if needed +if hm_catchment_path == 'default': + hm_catchment_path = make_default_path('shapefiles/catchment') # outputs a Path() +else: + hm_catchment_path = Path(hm_catchment_path) # make sure a user-specified path is a Path() + +# Find the GRU and HRU identifiers +hm_hruid = read_from_control(controlFile,'catchment_shp_hruid') + +## River network shapefile location and variable names +# Plot rivers? +plot_rivers = False +# River network path & name +river_network_path = read_from_control(controlFile,'river_network_shp_path') +river_network_name = read_from_control(controlFile,'river_network_shp_name') +# Specify default path if needed +if river_network_path == 'default': + river_network_path = make_default_path('shapefiles/river_network') # outputs a Path() +else: + river_network_path = Path(river_network_path) # make sure a user-specified path is a Path() + +# Find the segment ID +seg_id = read_from_control(controlFile,'river_network_shp_segid') + +## Load all shapefiles and project to Albers Conformal Conic and reproject +# Set the target CRS +acc = 'ESRI:102008' + +# catchment shapefile, first 2 lines throw error so cutting them +#bas = gpd.read_file(hm_catchment_path/hm_catchment_name) +#bas_albers = bas.to_crs(acc) +bas_albers = gpd.read_file(main/'basin.shp') +xmin, ymin, xmax, ymax = bas_albers.total_bounds + +# river network shapefile, first 2 lines throw error so cutting them +if plot_rivers: + #riv = gpd.read_file(river_network_path/river_network_name) + #riv_albers = riv.to_crs(acc) + riv_albers = gpd.read_file(main/'river.shp') + +# lakes shapefile, first 2 lines throw error so cutting them +if plot_lakes: + #lakes = gpd.read_file(lake_path/lake_name) + #lak_albers = lakes.to_crs(acc) + lak_albers = gpd.read_file(main/'lakes.shp') + +## Pre-processing, map SUMMA sims to catchment shapes +# Get the aggregated statistics of SUMMA simulations +summa = {} +eff = {} +for i, m in enumerate(method_name): + # Get the aggregated statistics of SUMMA simulations + summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) + if use_eff: + # Read the data from the eff.txt file into a DataFrame + eff[m] = pd.read_csv(viz_dir/eff_fil[i], sep=',', header=None, names=['CPU Efficiency', 'Array ID', 'Job Wall-clock time', 'Node Number']) + # Extract only the values after the ':' character in the 'CPU Efficiency', 'Job Wall-clock time', and 'Node Number' columns + eff[m]['CPU Efficiency'] = eff[m]['CPU Efficiency'].str.split(':').str[1].astype(float) + eff[m]['Array ID'] = eff[m]['Array ID'].str.split(':').str[1].astype(int) + eff[m]['Job Wall-clock time'] = eff[m]['Job Wall-clock time'].str.split(':').str[1].astype(float) + eff[m]['Node Number'] = eff[m]['Node Number'].str.split(':').str[1].astype(int) + +# Match the accummulated values to the correct HRU IDs in the shapefile +hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile +for plot_var in plot_vars: + stat0 = stat + if stat == 'rmse' or stat == 'kgem' or stat == 'mean': + if plot_var == 'wallClockTime': stat0 = 'mean' + statr = 'mean_ben' + if stat == 'rmnz' or stat == 'mnnz': + if plot_var == 'wallClockTime': stat0 = 'mnnz' + statr = 'mnnz_ben' + if stat == 'maxe' or stat == 'amax': + if plot_var == 'wallClockTime': stat0 = 'amax' + statr = 'amax_ben' + + if do_rel: s_rel = summa[method_name[0]][plot_var].sel(stat=statr) + for m in method_name: + s = summa[m][plot_var].sel(stat=stat0) + if do_rel and plot_var != 'wallClockTime': s = s/s_rel + + if plot_var == 'wallClockTime' and use_eff: + batch = np.floor(np.arange(len(s.indexes['hru'])) /nbatch_hrus) + #basin_num = np.arange(len(s.indexes['hru'])) % nbatch_hrus #not currently using + # Create a dictionary to store the values for each batch + efficiency = {} + # Iterate over the rows in the data DataFrame + for index, row in eff.iterrows(): + # Extract the values from the row + batch0 = int(row['Array ID']) + eff0 = row['CPU Efficiency'] + # Store the value for the current batch in the dictionary + efficiency[batch0] = eff0 + # Select the values for the current batch using boolean indexing + eff_batch = np.array([efficiency[b] for b in batch]) + #node_batch = np.array([node[b] for b in batch]) #not currently using + # Multiply the s values by efficiency + s = s*eff_batch + + # Make absolute value norm, not all positive + s = np.fabs(s) + + # Replace inf values with NaN in the s DataArray + s = s.where(~np.isinf(s), np.nan) + + if plot_var == 'scalarTotalET' and not do_rel: + if stat =='rmse' or stat =='rmnz' : s = s*31557600 # make annual total + if stat =='maxe': s = s*3600 # make hourly max + if plot_var == 'averageRoutedRunoff' and not do_rel: + if stat =='rmse' or stat =='rmnz' : s = s*31557600*1000 # make annual total + if stat =='maxe': s = s*3600*1000 # make hourly max + bas_albers[plot_var+m] = s.sel(hru=hru_ids_shp.values) + +# Select lakes of a certain size for plotting +if plot_lakes: + minSize = 1000 # km2 + in_domain = (lak_albers['Country'] == 'Canada') | \ + (lak_albers['Country'] == 'United States of America') | \ + (lak_albers['Country'] == 'Mexico') + out_domain = (lak_albers['Pour_long'] > -80) & (lak_albers['Pour_lat'] > 65) # Exclude Baffin Island + large_lakes_albers = lak_albers.loc[(lak_albers['Lake_area'] > minSize) & in_domain & (~out_domain) ] + lake_col = (8/255,81/255,156/255) + +##Figure + +def run_loop(j,var,the_max): + stat0 = stat + if stat == 'rmse' or stat == 'kgem' or stat == 'mean': + if var == 'wallClockTime': stat0 = 'mean' + statr = 'mean_ben' + if stat == 'rmnz' or stat == 'mnnz': + if var == 'wallClockTime': stat0 = 'mnnz' + statr = 'mnnz_ben' + if stat == 'maxe' or stat == 'amax': + if var == 'wallClockTime': stat0 = 'amax' + statr = 'amax_ben' + + my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap + my_cmap.set_bad(color='white') #nan color white + vmin,vmax = 0, the_max + if stat =='mean' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 700, the_max + if stat =='amax' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 1000, the_max + if (stat == 'mean' or stat == 'mnnz' or stat == 'amax') and var!='wallClockTime' and do_rel: vmin,vmax = 0.9, the_max + + norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) + if stat =='kgem' and var!='wallClockTime': + my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno')) # copy the default cmap + my_cmap.set_bad(color='white') #nan color white + vmin,vmax = the_max, 1.0 + norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=1.5) + + if stat0 == 'rmse': stat_word = 'RMSE' + if stat0 == 'rmnz': stat_word = 'RMSE' # no 0s' + if stat0 == 'maxe': stat_word = 'max abs error' + if stat0 == 'kgem': stat_word = 'KGE"' + if stat0 == 'mean': stat_word = 'mean' + if stat0 == 'mnnz': stat_word = 'mean' # no 0s' + if stat0 == 'amax': stat_word = 'max' + + if statr == 'mean_ben': statr_word = 'mean' + if statr == 'mnnz_ben': statr_word = 'mean' # no 0s' + if statr == 'amax_ben': statr_word = 'max' + + # colorbar axes + f_x_mat = [0.46,0.96,0.46,0.96] + f_y_mat = [0.55,0.55,0.07,0.07] + + for i,(m,f_x,f_y) in enumerate(zip(method_name,f_x_mat,f_y_mat)): + r = i//2 + c = i-r*2 + + # Plot the data with the full extent of the bas_albers shape + bas_albers.plot(ax=axs[r,c], column=var+m, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) + + axs[r,c].set_title(plt_name[i]) + axs[r,c].axis('off') + axs[r,c].set_xlim(xmin, xmax) + axs[r,c].set_ylim(ymin, ymax) + + # Custom colorbar + cax = fig.add_axes([f_x,f_y,0.02,0.375]) + sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) + sm._A = [] + cbr = fig.colorbar(sm, cax=cax) #, extend='max') #if max extend can't get title right + if stat == 'rmse' or stat == 'rmnz' or stat == 'mean': cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titl[j]), labelpad=40, rotation=270) + if stat == 'maxe' or stat == 'amax': cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titlm[j]), labelpad=40, rotation=270) + if stat == 'kgem': cbr.ax.set_ylabel(stat_word, labelpad=40, rotation=270) + #if do_rel and var!='wallClockTime': cbr.ax.set_ylabel(stat_word + ' rel to bench ' + statr_word, labelpad=40, rotation=270) + if do_rel and var!='wallClockTime': cbr.ax.set_ylabel('relative '+ stat_word, labelpad=40, rotation=270) + + #cbr.ax.yaxis.set_offset_position('right') + + # lakes + if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) + +for i,(var,the_max) in enumerate(zip(plot_vars,maxes)): + + # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug + if 'compressed' in fig_fil: + plt.rcParams.update({'font.size': 25}) + else: + plt.rcParams.update({'font.size': 100}) + + if 'compressed' in fig_fil: + fig,axs = plt.subplots(2,2,figsize=(35,28)) + else: + fig,axs = plt.subplots(2,2,figsize=(140,133)) + fig.suptitle('{} Hourly Statistics'.format(plt_titl[i]), fontsize=40,y=1.05) + + plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines + + plt.tight_layout() + + run_loop(i,var,the_max) + + fig_fil1 = (var+fig_fil).format(stat) + # Save + plt.savefig(viz_dir/fig_fil1, bbox_inches='tight', transparent=True) diff --git a/utils/process_plot.sh b/utils/process_plot.sh index a6259221b..de600efef 100755 --- a/utils/process_plot.sh +++ b/utils/process_plot.sh @@ -23,4 +23,4 @@ pip install --no-index netCDF4 python plot_per_GRU.py sundials_1en6 rmse python plot_per_GRU.py sundials_1en6 maxe -python plot_per_GRU.py sundials_1en6 kgem \ No newline at end of file +python plot_per_GRU.py sundials_1en6 kgem From 85b473d8356877c012cf11abb8164ea0b02642f2 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 6 Jan 2024 10:16:03 +0900 Subject: [PATCH 1043/1472] utils --- utils/hist_per_GRU.py | 2 +- utils/plot_per_GRU.py | 19 ++- utils/plot_per_GRUMult.py | 348 ++++++++++++++++++++++++++++++++++++++ utils/process_plot.sh | 2 +- 4 files changed, 365 insertions(+), 6 deletions(-) create mode 100644 utils/plot_per_GRUMult.py diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 5353fd5ac..7571f8e8c 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -40,7 +40,7 @@ #method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument #plt_name=['BE1','IDAe-4','BE4','BE8','BE16','BE32','IDAe-6'] #maybe make this an argument method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument - plt_name=['BE1','BE16','BE32','IDAe-6'] #maybe make this an argument + plt_name=['BE1','BE16','BE32','SUNDIALS'] #maybe make this an argument if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE # Define the power transformation function diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py index 73fc5fd74..fe7c370bd 100644 --- a/utils/plot_per_GRU.py +++ b/utils/plot_per_GRU.py @@ -266,6 +266,17 @@ def make_default_path(suffix): plt.tight_layout() def run_loop(i,var,the_max,f_x,f_y): + stat0 = stat + if stat == 'rmse' or stat == 'kgem' or stat == 'mean': + if plot_var == 'wallClockTime': stat0 = 'mean' + statr = 'mean_ben' + if stat == 'rmnz' or stat == 'mnnz': + if plot_var == 'wallClockTime': stat0 = 'mnnz' + statr = 'mnnz_ben' + if stat == 'maxe' or stat == 'amax': + if plot_var == 'wallClockTime': stat0 = 'amax' + statr = 'amax_ben' + my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap my_cmap.set_bad(color='white') #nan color white vmin,vmax = 0, the_max @@ -289,9 +300,9 @@ def run_loop(i,var,the_max,f_x,f_y): if stat0 == 'rmnz': stat_word = 'RMSE' # no 0s' if stat0 == 'maxe': stat_word = 'max abs error' if stat0 == 'kgem': stat_word = 'KGE"' - if stat0 == 'mean': stat_word = 'abs mean' - if stat0 == 'mnnz': stat_word = 'abs mean' # no 0s' - if stat0 == 'amax': stat_word = 'abs max' + if stat0 == 'mean': stat_word = 'mean' + if stat0 == 'mnnz': stat_word = 'mean' # no 0s' + if stat0 == 'amax': stat_word = 'max' if statr == 'mean_ben': statr_word = 'mean' if statr == 'mnnz_ben': statr_word = 'mean' # no 0s' @@ -311,7 +322,7 @@ def run_loop(i,var,the_max,f_x,f_y): if stat == 'maxe' or stat == 'amax': cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titlm[i]), labelpad=40, rotation=270) if stat == 'kgem': cbr.ax.set_ylabel(stat_word, labelpad=40, rotation=270) #if do_rel and var!='wallClockTime': cbr.ax.set_ylabel(stat_word + ' rel to bench ' + statr_word, labelpad=40, rotation=270) - if do_rel and var!='wallClockTime': cbr.ax.set_ylabel('relative '+ stat_word) + if do_rel and var!='wallClockTime': cbr.ax.set_ylabel('relative '+ stat_word, labelpad=40, rotation=270) #cbr.ax.yaxis.set_offset_position('right') diff --git a/utils/plot_per_GRUMult.py b/utils/plot_per_GRUMult.py new file mode 100644 index 000000000..8a0c84b3d --- /dev/null +++ b/utils/plot_per_GRUMult.py @@ -0,0 +1,348 @@ +# written originally by W. Knoben, modified by A. Van Beusekom (2023) + +## Visualize statistics per GRU +## Needs: +# Catchment shapefile with GRU delineation +# SUMMA output statistics + +## Special note +# SUMMA simulations have been preprocessed into single value statistics per model element, using auxiliary scripts in ~/utils +# To improve visualization of large lakes, HydroLAKES lake delineations are plotted on top of the catchment GRUs and river segments. +# Dealing with HydroLAKES inputs is not considered within scope of the workflow and therefore requires some manual downloading and preprocessing of this data for those who wish to reproduce this step. +# The relevant code is easily disabled by switching the plot_lakes = True flag to False. + +# Run: +# python plot_per_GRUMult.py [stat] +# where stat is rmse or maxe or kgem + + +# modules +import sys +import os +import matplotlib +import numpy as np +import xarray as xr +from pathlib import Path +import matplotlib.pyplot as plt +import copy +import pyproj +import fiona +import geopandas as gpd +import pandas as pd + +# plot all runs, pick statistic +stat = sys.argv[1] +method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument +plt_name=['(a) SUMMA-BE1','(b) SUMMA-BE16','(c) SUMMA-BE32','(d) SUMMA-SUNDIALS'] #maybe make this an argument + +# Simulation statistics file locations +settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] +viz_dir = Path('/home/avanb/scratch/statistics') +viz_fil = method_name.copy() +eff_fil = method_name.copy() +for i, m in enumerate(method_name): + viz_fil[i] = m + '_hrly_diff_stats_{}.nc' + viz_fil[i] = viz_fil[i].format(','.join(settings)) + eff_fil[i] = 'eff_' + m + '.txt' +nbatch_hrus = 518 # number of HRUs per batch +use_eff = False # use efficiency in wall clock time +do_rel = True # plot relative to the benchmark simulation +if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE + +# Specify variables of interest +plot_vars = settings.copy() +plt_titl = ['Snow Water Equivalent','Total soil water content','Total evapotranspiration', 'Total water on the vegetation canopy','Average routed runoff','Wall clock time'] +leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] +leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$','$s$'] + +fig_fil= '_hrly_diff_stats_{}_compressed.png' +if do_rel: fig_fil = '_hrly_diff_stats_{}_rel_compressed.png' + +if stat == 'rmse': + maxes = [2,15,250,0.08,200,10e-3] #[2,15,8e-6,0.08,6e-9,10e-3] + #maxes = [0.25,2,30,0.01,30,2e-3] #[0.25,2,1e-6,0.01,1e-9,2e-3] + if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] +if stat == 'rmnz': + maxes = [2,15,250,0.08,200,10e-3] + if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] +if stat == 'maxe': + maxes = [15,25,0.8,2,0.3,0.2] #[15,25,25e-5,2,1e-7,0.2] + if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,0.2] +if stat == 'kgem': + maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] +if stat == 'mean': + maxes = [80,1500,1500,3000,10e-3] #[80,1500,5e-5,8,1e-7,10e-3] + if do_rel: maxes = [1.1,1.1,1.1,1.1,1.1,10e-3] +if stat == 'amax': + maxes = [240,1800,3.5,25,7.5,0.2] #[240,1800,1e-3,25,2e-6,0.2] + if do_rel: maxes = [1.1,1.1,1.1,1.1,1.1,0.2] + +# Get the albers shapes +main = Path('/home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/albers_projection') + +# Plot lakes? +plot_lakes = True +# lakes shapefile WHERE IS THIS +#lake_path = Path('C:/Globus endpoint/HydroLAKES/HydroLAKES_polys_v10_shp') +#lake_name = 'HydroLAKES_polys_v10_subset_NA.shp' + +## Control file handling +# Store the name of the 'active' file in a variable +controlFile = 'plot_control_NorthAmerica.txt' + +# Function to extract a given setting from the control file +def read_from_control( file, setting ): + + # Open controlFile and ... + with open(file) as contents: + for line in contents: + + # ... find the line with the requested setting + if setting in line and not line.startswith('#'): + break + + # Extract the setting's value + substring = line.split('|',1)[1] # Remove the setting's name (split into 2 based on '|', keep only 2nd part) + substring = substring.split('#',1)[0] # Remove comments, does nothing if no '#' is found + substring = substring.strip() # Remove leading and trailing whitespace, tabs, newlines + + # Return this value + return substring + +# Function to specify a default path +def make_default_path(suffix): + + # Get the root path + rootPath = Path( read_from_control(controlFile,'root_path') ) + + # Get the domain folder + domainName = read_from_control(controlFile,'domain_name') + domainFolder = 'domain_' + domainName + + # Specify the forcing path + defaultPath = rootPath / domainFolder / suffix + + return defaultPath + +## Catchment shapefile location and variable names +# HM catchment shapefile path & name +hm_catchment_path = read_from_control(controlFile,'catchment_shp_path') +hm_catchment_name = read_from_control(controlFile,'catchment_shp_name') +# Specify default path if needed +if hm_catchment_path == 'default': + hm_catchment_path = make_default_path('shapefiles/catchment') # outputs a Path() +else: + hm_catchment_path = Path(hm_catchment_path) # make sure a user-specified path is a Path() + +# Find the GRU and HRU identifiers +hm_hruid = read_from_control(controlFile,'catchment_shp_hruid') + +## River network shapefile location and variable names +# Plot rivers? +plot_rivers = False +# River network path & name +river_network_path = read_from_control(controlFile,'river_network_shp_path') +river_network_name = read_from_control(controlFile,'river_network_shp_name') +# Specify default path if needed +if river_network_path == 'default': + river_network_path = make_default_path('shapefiles/river_network') # outputs a Path() +else: + river_network_path = Path(river_network_path) # make sure a user-specified path is a Path() + +# Find the segment ID +seg_id = read_from_control(controlFile,'river_network_shp_segid') + +## Load all shapefiles and project to Albers Conformal Conic and reproject +# Set the target CRS +acc = 'ESRI:102008' + +# catchment shapefile, first 2 lines throw error so cutting them +#bas = gpd.read_file(hm_catchment_path/hm_catchment_name) +#bas_albers = bas.to_crs(acc) +bas_albers = gpd.read_file(main/'basin.shp') +xmin, ymin, xmax, ymax = bas_albers.total_bounds + +# river network shapefile, first 2 lines throw error so cutting them +if plot_rivers: + #riv = gpd.read_file(river_network_path/river_network_name) + #riv_albers = riv.to_crs(acc) + riv_albers = gpd.read_file(main/'river.shp') + +# lakes shapefile, first 2 lines throw error so cutting them +if plot_lakes: + #lakes = gpd.read_file(lake_path/lake_name) + #lak_albers = lakes.to_crs(acc) + lak_albers = gpd.read_file(main/'lakes.shp') + +## Pre-processing, map SUMMA sims to catchment shapes +# Get the aggregated statistics of SUMMA simulations +summa = {} +eff = {} +for i, m in enumerate(method_name): + # Get the aggregated statistics of SUMMA simulations + summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) + if use_eff: + # Read the data from the eff.txt file into a DataFrame + eff[m] = pd.read_csv(viz_dir/eff_fil[i], sep=',', header=None, names=['CPU Efficiency', 'Array ID', 'Job Wall-clock time', 'Node Number']) + # Extract only the values after the ':' character in the 'CPU Efficiency', 'Job Wall-clock time', and 'Node Number' columns + eff[m]['CPU Efficiency'] = eff[m]['CPU Efficiency'].str.split(':').str[1].astype(float) + eff[m]['Array ID'] = eff[m]['Array ID'].str.split(':').str[1].astype(int) + eff[m]['Job Wall-clock time'] = eff[m]['Job Wall-clock time'].str.split(':').str[1].astype(float) + eff[m]['Node Number'] = eff[m]['Node Number'].str.split(':').str[1].astype(int) + +# Match the accummulated values to the correct HRU IDs in the shapefile +hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile +for plot_var in plot_vars: + stat0 = stat + if stat == 'rmse' or stat == 'kgem' or stat == 'mean': + if plot_var == 'wallClockTime': stat0 = 'mean' + statr = 'mean_ben' + if stat == 'rmnz' or stat == 'mnnz': + if plot_var == 'wallClockTime': stat0 = 'mnnz' + statr = 'mnnz_ben' + if stat == 'maxe' or stat == 'amax': + if plot_var == 'wallClockTime': stat0 = 'amax' + statr = 'amax_ben' + + if do_rel: s_rel = summa[method_name[0]][plot_var].sel(stat=statr) + for m in method_name: + s = summa[m][plot_var].sel(stat=stat0) + if do_rel and plot_var != 'wallClockTime': s = s/s_rel + + if plot_var == 'wallClockTime' and use_eff: + batch = np.floor(np.arange(len(s.indexes['hru'])) /nbatch_hrus) + #basin_num = np.arange(len(s.indexes['hru'])) % nbatch_hrus #not currently using + # Create a dictionary to store the values for each batch + efficiency = {} + # Iterate over the rows in the data DataFrame + for index, row in eff.iterrows(): + # Extract the values from the row + batch0 = int(row['Array ID']) + eff0 = row['CPU Efficiency'] + # Store the value for the current batch in the dictionary + efficiency[batch0] = eff0 + # Select the values for the current batch using boolean indexing + eff_batch = np.array([efficiency[b] for b in batch]) + #node_batch = np.array([node[b] for b in batch]) #not currently using + # Multiply the s values by efficiency + s = s*eff_batch + + # Make absolute value norm, not all positive + s = np.fabs(s) + + # Replace inf values with NaN in the s DataArray + s = s.where(~np.isinf(s), np.nan) + + if plot_var == 'scalarTotalET' and not do_rel: + if stat =='rmse' or stat =='rmnz' : s = s*31557600 # make annual total + if stat =='maxe': s = s*3600 # make hourly max + if plot_var == 'averageRoutedRunoff' and not do_rel: + if stat =='rmse' or stat =='rmnz' : s = s*31557600*1000 # make annual total + if stat =='maxe': s = s*3600*1000 # make hourly max + bas_albers[plot_var+m] = s.sel(hru=hru_ids_shp.values) + +# Select lakes of a certain size for plotting +if plot_lakes: + minSize = 1000 # km2 + in_domain = (lak_albers['Country'] == 'Canada') | \ + (lak_albers['Country'] == 'United States of America') | \ + (lak_albers['Country'] == 'Mexico') + out_domain = (lak_albers['Pour_long'] > -80) & (lak_albers['Pour_lat'] > 65) # Exclude Baffin Island + large_lakes_albers = lak_albers.loc[(lak_albers['Lake_area'] > minSize) & in_domain & (~out_domain) ] + lake_col = (8/255,81/255,156/255) + +##Figure + +def run_loop(j,var,the_max): + stat0 = stat + if stat == 'rmse' or stat == 'kgem' or stat == 'mean': + if var == 'wallClockTime': stat0 = 'mean' + statr = 'mean_ben' + if stat == 'rmnz' or stat == 'mnnz': + if var == 'wallClockTime': stat0 = 'mnnz' + statr = 'mnnz_ben' + if stat == 'maxe' or stat == 'amax': + if var == 'wallClockTime': stat0 = 'amax' + statr = 'amax_ben' + + my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap + my_cmap.set_bad(color='white') #nan color white + vmin,vmax = 0, the_max + if stat =='mean' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 700, the_max + if stat =='amax' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 1000, the_max + if (stat == 'mean' or stat == 'mnnz' or stat == 'amax') and var!='wallClockTime' and do_rel: vmin,vmax = 0.9, the_max + + norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) + if stat =='kgem' and var!='wallClockTime': + my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno')) # copy the default cmap + my_cmap.set_bad(color='white') #nan color white + vmin,vmax = the_max, 1.0 + norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=1.5) + + if stat0 == 'rmse': stat_word = 'RMSE' + if stat0 == 'rmnz': stat_word = 'RMSE' # no 0s' + if stat0 == 'maxe': stat_word = 'max abs error' + if stat0 == 'kgem': stat_word = 'KGE"' + if stat0 == 'mean': stat_word = 'mean' + if stat0 == 'mnnz': stat_word = 'mean' # no 0s' + if stat0 == 'amax': stat_word = 'max' + + if statr == 'mean_ben': statr_word = 'mean' + if statr == 'mnnz_ben': statr_word = 'mean' # no 0s' + if statr == 'amax_ben': statr_word = 'max' + + # colorbar axes + f_x_mat = [0.46,0.96,0.46,0.96] + f_y_mat = [0.55,0.55,0.07,0.07] + + for i,(m,f_x,f_y) in enumerate(zip(method_name,f_x_mat,f_y_mat)): + r = i//2 + c = i-r*2 + + # Plot the data with the full extent of the bas_albers shape + bas_albers.plot(ax=axs[r,c], column=var+m, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) + + axs[r,c].set_title(plt_name[i]) + axs[r,c].axis('off') + axs[r,c].set_xlim(xmin, xmax) + axs[r,c].set_ylim(ymin, ymax) + + # Custom colorbar + cax = fig.add_axes([f_x,f_y,0.02,0.375]) + sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) + sm._A = [] + cbr = fig.colorbar(sm, cax=cax) #, extend='max') #if max extend can't get title right + if stat == 'rmse' or stat == 'rmnz' or stat == 'mean': cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titl[j]), labelpad=40, rotation=270) + if stat == 'maxe' or stat == 'amax': cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titlm[j]), labelpad=40, rotation=270) + if stat == 'kgem': cbr.ax.set_ylabel(stat_word, labelpad=40, rotation=270) + #if do_rel and var!='wallClockTime': cbr.ax.set_ylabel(stat_word + ' rel to bench ' + statr_word, labelpad=40, rotation=270) + if do_rel and var!='wallClockTime': cbr.ax.set_ylabel('relative '+ stat_word, labelpad=40, rotation=270) + + #cbr.ax.yaxis.set_offset_position('right') + + # lakes + if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) + +for i,(var,the_max) in enumerate(zip(plot_vars,maxes)): + + # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug + if 'compressed' in fig_fil: + plt.rcParams.update({'font.size': 25}) + else: + plt.rcParams.update({'font.size': 100}) + + if 'compressed' in fig_fil: + fig,axs = plt.subplots(2,2,figsize=(35,28)) + else: + fig,axs = plt.subplots(2,2,figsize=(140,133)) + fig.suptitle('{} Hourly Statistics'.format(plt_titl[i]), fontsize=40,y=1.05) + + plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines + + plt.tight_layout() + + run_loop(i,var,the_max) + + fig_fil1 = (var+fig_fil).format(stat) + # Save + plt.savefig(viz_dir/fig_fil1, bbox_inches='tight', transparent=True) diff --git a/utils/process_plot.sh b/utils/process_plot.sh index a6259221b..de600efef 100755 --- a/utils/process_plot.sh +++ b/utils/process_plot.sh @@ -23,4 +23,4 @@ pip install --no-index netCDF4 python plot_per_GRU.py sundials_1en6 rmse python plot_per_GRU.py sundials_1en6 maxe -python plot_per_GRU.py sundials_1en6 kgem \ No newline at end of file +python plot_per_GRU.py sundials_1en6 kgem From 7d9c98f974d660d803f424f45653a88f5ba9f0f1 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 8 Jan 2024 22:50:54 +0900 Subject: [PATCH 1044/1472] alpha is negative so equation should be not negative --- build/source/engine/t2enthalpy.f90 | 4 ++-- build/source/engine/varSubstep.f90 | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index 352061a40..3cba6dae3 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -458,7 +458,7 @@ subroutine t2enthalpy(& ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*(mLayerTempTrial(iLayer) - Tfreeze) ! liquid water matric potential from the Clapeyron eqution - arg = (-vGn_alpha * mLayerPsiLiq)**vGn_n + arg = (vGn_alpha * mLayerPsiLiq)**vGn_n if(quick_hyper)then gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) else @@ -466,7 +466,7 @@ subroutine t2enthalpy(& endif enthTemp = (iden_water * Cp_water - iden_ice * Cp_ice) * diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) ! calculate enthalpy at the critical temperature - arg = (-vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n + arg = (vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n if(quick_hyper)then gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) else diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 56a5a5185..1628b736a 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -1005,7 +1005,6 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! output: error control err,message) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - print*, mLayerEnthalpyTrial(1),mLayerEnthalpy(1),mLayerEnthalpyTrial(1) - mLayerEnthalpy(1),mLayerEnthalpyDelta_addphase(1) ! compute energy balance, maybe should use to check for step reduction if(ixCasNrg/=integerMissing) balance(ixCasNrg) = scalarCanairEnthalpyTrial - scalarCanairEnthalpy - fluxVec(ixCasNrg)*dt From a540a7dc6f77b338cd017ce80fbabdc09a02327e Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 9 Jan 2024 05:26:47 -0600 Subject: [PATCH 1045/1472] Absorbed coupling loop into the split_select loop in opSplittin - several class procedures were created. --- build/source/engine/opSplittin.f90 | 84 +++++++++++++++++++++++++----- 1 file changed, 70 insertions(+), 14 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 3d130eac5..0400e1958 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -156,8 +156,12 @@ module opSplittin_module type(var_flagVec) :: fluxMask ! integer mask defining model fluxes logical(lgt),allocatable :: stateMask(:) ! mask defining desired state variables contains - procedure :: get_stateMask => compute_stateMask ! compute stateMask and nSubset and load into class object - procedure :: get_indices => initialize_indices ! load operator splitting indices into class object + procedure :: initialize_indices => split_select_initialize_indices ! initialize operator splitting indices + procedure :: get_stateMask => split_select_compute_stateMask ! compute stateMask and nSubset and load into class object + procedure :: get_indices => split_select_load_indices ! load operator splitting indices into class object + procedure :: indices_from_iSplit => split_select_indices_from_iSplit + procedure :: loop_cycle => split_select_cycle + procedure :: loop_exit => split_select_exit end type split_select_type type, public :: split_index_type ! class for operator splitting indices @@ -317,6 +321,7 @@ subroutine opSplittin(& ! loop control logical(lgt) :: exit_coupling,exit_stateTypeSplitting,exit_stateThenDomain,exit_domainSplit,exit_solution,exit_stateSplit logical(lgt) :: cycle_coupling,cycle_stateTypeSplitting,cycle_stateThenDomain,cycle_domainSplit,cycle_solution,cycle_stateSplit + logical(lgt) :: cycle_flag,exit_flag integer(i4b) :: iSplit,nSplit ! debug testing logical(lgt), parameter :: test_flag=.false. ! to create test output @@ -335,10 +340,13 @@ subroutine opSplittin(& ! *** Initialize Split Selector Object *** call initialize_split_select - !split_select: do iSplit=1,nSplit - + !call split_select % initialize_indices call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs - coupling: do ixCoupling=1,nCoupling ! loop through different coupling strategies + split_select_loop: do iSplit=1,nSplit + call split_select % indices_from_iSplit(iSplit); ixCoupling=split_select % ixCoupling + + !call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs + !coupling: do ixCoupling=1,nCoupling ! loop through different coupling strategies call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs stateTypeSplitting: do iStateTypeSplit=1,nStateTypeSplit ! state splitting loop @@ -385,7 +393,11 @@ subroutine opSplittin(& call assess_solution; if (return_flag.eqv..true.) return ! is solution a success or failure? - return for a recovering solution call try_other_solution_methods ! if solution failed to converge, try other splitting methods - if (cycle_coupling) cycle coupling ! exit loops if necessary + !if (cycle_coupling) cycle coupling ! exit loops if necessary + if (cycle_coupling) then + call split_select % loop_cycle('coupling',cycle_flag) + if (cycle_flag.eqv..true.) cycle split_select_loop ! exit loops if necessary + end if if (cycle_stateThenDomain) cycle stateThenDomain if (cycle_solution) cycle solution @@ -407,12 +419,17 @@ subroutine opSplittin(& call finalize_stateThenDomain ! final steps following the stateThenDomain loop end do stateTypeSplitting ! state type splitting loop - call finalize_stateTypeSplitting; if (exit_coupling) exit coupling ! success = exit the coupling loop + call finalize_stateTypeSplitting + !if (exit_coupling) exit coupling ! success = exit the coupling loop + if (exit_coupling) then + call split_select % loop_exit('coupling',exit_flag); + if (exit_flag.eqv..true.) exit split_select_loop ! success = exit the coupling loop + end if - end do coupling ! loop over coupling methods + !end do coupling ! loop over coupling methods call finalize_coupling ! check variables and fluxes, and apply step halving if needed - !end do split_select + end do split_select_loop contains subroutine initialize_split @@ -447,6 +464,7 @@ subroutine initialize_split_select end subroutine initialize_split_select + subroutine initialize_coupling ! *** initial steps for coupling loop *** ! initialize error control @@ -1063,8 +1081,8 @@ end subroutine opSplittin ! ****** Class procedures for split_select_type class ****** -subroutine initialize_indices(split_select,ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) - ! *** initialize operator aplitting indices for split_select_type class *** +subroutine split_select_load_indices(split_select,ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) + ! *** load operator splitting indices for split_select_type class *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector integer(i4b),intent(in) :: ixCoupling integer(i4b),intent(in) :: ixSolution @@ -1078,9 +1096,24 @@ subroutine initialize_indices(split_select,ixCoupling,ixSolution,ixStateThenDoma split_select % iStateTypeSplit = iStateTypeSplit split_select % iDomainSplit = iDomainSplit split_select % iStateSplit = iStateSplit -end subroutine initialize_indices +end subroutine split_select_load_indices + +subroutine split_select_initialize_indices(split_select) + ! *** initialize operator splitting indices for split_select_type class *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % ixCoupling = 1 +end subroutine split_select_initialize_indices + +subroutine split_select_indices_from_iSplit(split_select,iSplit) + ! *** Convert iSplit to split_select_type index data components *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + integer(i4b),intent(in) :: iSplit ! index of split_select_loop + + ! convert iSplit to ixCoupling + split_select % ixCoupling = iSplit +end subroutine split_select_indices_from_iSplit -subroutine compute_stateMask(split_select,indx_data,err,cmessage,message,return_flag) +subroutine split_select_compute_stateMask(split_select,indx_data,err,cmessage,message,return_flag) ! *** Get the mask for the state subset *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector type(var_ilength),intent(in) :: indx_data ! indices for a local HRU @@ -1109,7 +1142,30 @@ subroutine compute_stateMask(split_select,indx_data,err,cmessage,message,return_ call out_stateFilter % finalize(nSubset,err,cmessage) end associate if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! error control -end subroutine compute_stateMask +end subroutine split_select_compute_stateMask + +subroutine split_select_cycle(split_select,loop,cycle_flag) +! *** handle cycle statements of the original opSplittin loops *** + class(split_select_type),intent(inout) :: split_select + character(*),intent(in) :: loop + logical(lgt),intent(out) :: cycle_flag + cycle_flag=.false. ! initialize flag + if (loop.eq.'coupling') then + cycle_flag=.true. ! exit split select loop + split_select % ixCoupling = split_select % ixCoupling+1 + end if +end subroutine split_select_cycle + +subroutine split_select_exit(split_select,loop,exit_flag) +! *** handle exit statements of the original opSplittin loops *** + class(split_select_type),intent(inout) :: split_select + character(*),intent(in) :: loop + logical(lgt),intent(out) :: exit_flag + exit_flag=.false. ! initialize flag + if (loop.eq.'coupling') then + exit_flag=.true. ! exit split select loop + end if +end subroutine split_select_exit ! ********************************************************************************************************** ! private subroutine stateFilter: get a mask for the desired state variables From cd32b65647963dbc9977f803d1361c43e76fa698 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 10 Jan 2024 00:18:09 +0900 Subject: [PATCH 1046/1472] C_m snow should not have iden_ice in it --- build/source/engine/computHeatCap.f90 | 8 ++++---- build/source/engine/t2enthalpy.f90 | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index 8f6f75eaa..c71b903be 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -185,7 +185,7 @@ subroutine computHeatCap(& ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) if(computeVegFlux)then - if(abs(scalarCanopyTempDelta) <= 1e-2_rkind)then + if(abs(scalarCanopyTempDelta) <= 1e-14_rkind)then heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component Cp_ice*scalarCanopyIce/canopyDepth ! ice component @@ -208,7 +208,7 @@ subroutine computHeatCap(& ! get the soil layer if(iLayer>nSnow) iSoil = iLayer-nSnow - if(abs(mLayerTempDelta(iLayer)) <= 1e-2_rkind)then + if(abs(mLayerTempDelta(iLayer)) <= 1e-14_rkind)then select case(layerType(iLayer)) ! * soil case(iname_soil) @@ -627,11 +627,11 @@ subroutine computCm(& case(iname_snow) diffT = mLayerTemp(iLayer) - Tfreeze integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( diffT - integral ) & + mLayerCm(iLayer) = (iden_water * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( diffT - integral ) & + (iden_water * Cp_water - iden_air * Cp_air) * integral ! derivatives d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) - dCm_dTk(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( 1._rkind - d_integral_dTk ) & + dCm_dTk(iLayer) = (iden_water * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( 1._rkind - d_integral_dTk ) & + (iden_water * Cp_water - iden_air * Cp_air) * d_integral_dTk case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute Cm'; return diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index 3cba6dae3..496ba2013 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -199,7 +199,7 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number of volFracIce = theta_sat - vFracLiq ! compute enthalpy - ! NOTE: assume intrrinsic density of ice is the intrinsic density of water + ! NOTE: assume intrinsic density of ice is the intrinsic density of water ! NOTE: kg m-3 J kg-1 K-1 K Ey(iLook) = Ey(iLook) + iden_water * Cp_water*vFracLiq*T_incr + iden_water * Cp_ice*volFracIce*T_incr From 390891b4f4dd8701573812de6d595eef05227c18 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 10 Jan 2024 03:46:11 -0600 Subject: [PATCH 1047/1472] Refinements were made to split_select_loop - there is now an explicit exit criterion. --- build/source/engine/opSplittin.f90 | 41 +++++++++++++----------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 0400e1958..471ebe54a 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -162,6 +162,7 @@ module opSplittin_module procedure :: indices_from_iSplit => split_select_indices_from_iSplit procedure :: loop_cycle => split_select_cycle procedure :: loop_exit => split_select_exit + procedure :: advance_coupling => split_select_advance_coupling ! advance coupling loop iterator end type split_select_type type, public :: split_index_type ! class for operator splitting indices @@ -321,7 +322,6 @@ subroutine opSplittin(& ! loop control logical(lgt) :: exit_coupling,exit_stateTypeSplitting,exit_stateThenDomain,exit_domainSplit,exit_solution,exit_stateSplit logical(lgt) :: cycle_coupling,cycle_stateTypeSplitting,cycle_stateThenDomain,cycle_domainSplit,cycle_solution,cycle_stateSplit - logical(lgt) :: cycle_flag,exit_flag integer(i4b) :: iSplit,nSplit ! debug testing logical(lgt), parameter :: test_flag=.false. ! to create test output @@ -340,13 +340,10 @@ subroutine opSplittin(& ! *** Initialize Split Selector Object *** call initialize_split_select - !call split_select % initialize_indices + call split_select % initialize_indices call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs - split_select_loop: do iSplit=1,nSplit - call split_select % indices_from_iSplit(iSplit); ixCoupling=split_select % ixCoupling - - !call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs - !coupling: do ixCoupling=1,nCoupling ! loop through different coupling strategies + split_select_loop: do + ixCoupling=split_select % ixCoupling; if (ixCoupling.gt.nCoupling) exit split_select_loop ! exit if all splits are exhausted call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs stateTypeSplitting: do iStateTypeSplit=1,nStateTypeSplit ! state splitting loop @@ -393,10 +390,8 @@ subroutine opSplittin(& call assess_solution; if (return_flag.eqv..true.) return ! is solution a success or failure? - return for a recovering solution call try_other_solution_methods ! if solution failed to converge, try other splitting methods - !if (cycle_coupling) cycle coupling ! exit loops if necessary if (cycle_coupling) then - call split_select % loop_cycle('coupling',cycle_flag) - if (cycle_flag.eqv..true.) cycle split_select_loop ! exit loops if necessary + call split_select % loop_cycle('coupling'); cycle split_select_loop ! cycle loops if necessary end if if (cycle_stateThenDomain) cycle stateThenDomain if (cycle_solution) cycle solution @@ -420,16 +415,13 @@ subroutine opSplittin(& end do stateTypeSplitting ! state type splitting loop call finalize_stateTypeSplitting - !if (exit_coupling) exit coupling ! success = exit the coupling loop if (exit_coupling) then - call split_select % loop_exit('coupling',exit_flag); - if (exit_flag.eqv..true.) exit split_select_loop ! success = exit the coupling loop + call split_select % loop_exit('coupling'); exit split_select_loop ! success = exit the coupling loop end if - !end do coupling ! loop over coupling methods - call finalize_coupling ! check variables and fluxes, and apply step halving if needed - + call split_select % advance_coupling end do split_select_loop + call finalize_coupling ! check variables and fluxes, and apply step halving if needed contains subroutine initialize_split @@ -1081,6 +1073,12 @@ end subroutine opSplittin ! ****** Class procedures for split_select_type class ****** +subroutine split_select_advance_coupling(split_select) + ! *** Advance index for coupling loop *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % ixCoupling = split_select % ixCoupling + 1 +end subroutine split_select_advance_coupling + subroutine split_select_load_indices(split_select,ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) ! *** load operator splitting indices for split_select_type class *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector @@ -1144,26 +1142,21 @@ subroutine split_select_compute_stateMask(split_select,indx_data,err,cmessage,me if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! error control end subroutine split_select_compute_stateMask -subroutine split_select_cycle(split_select,loop,cycle_flag) +subroutine split_select_cycle(split_select,loop) ! *** handle cycle statements of the original opSplittin loops *** class(split_select_type),intent(inout) :: split_select character(*),intent(in) :: loop - logical(lgt),intent(out) :: cycle_flag - cycle_flag=.false. ! initialize flag if (loop.eq.'coupling') then - cycle_flag=.true. ! exit split select loop split_select % ixCoupling = split_select % ixCoupling+1 end if end subroutine split_select_cycle -subroutine split_select_exit(split_select,loop,exit_flag) +subroutine split_select_exit(split_select,loop) ! *** handle exit statements of the original opSplittin loops *** class(split_select_type),intent(inout) :: split_select character(*),intent(in) :: loop - logical(lgt),intent(out) :: exit_flag - exit_flag=.false. ! initialize flag if (loop.eq.'coupling') then - exit_flag=.true. ! exit split select loop + split_select % ixCoupling = 1 ! reinitialize index end if end subroutine split_select_exit From c8b2c314a291c3ba5ac7e950efba02f4e5c0234a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 11 Jan 2024 13:20:18 +0900 Subject: [PATCH 1048/1472] adding better hypergeometric, does not compile! --- build/cmake/CMakeLists.txt | 3 +- build/source/engine/hyp_2F1.f90 | 1879 ++++++++++++++++++++++++++++ build/source/engine/t2enthalpy.f90 | 62 +- 3 files changed, 1906 insertions(+), 38 deletions(-) create mode 100644 build/source/engine/hyp_2F1.f90 diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index f0beddd11..391b1dc67 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -275,7 +275,8 @@ set(NRUTIL # Free versions of numerical recipes procedures for SUMMA modules set(NRPROC ${ENGINE_DIR}/expIntegral.f90 - ${ENGINE_DIR}/spline_int.f90) + ${ENGINE_DIR}/spline_int.f90 + ${ENGINE_DIR}/hyp_2F1.f90) # Hook-up modules set(HOOKUP diff --git a/build/source/engine/hyp_2F1.f90 b/build/source/engine/hyp_2F1.f90 new file mode 100644 index 000000000..617da8d86 --- /dev/null +++ b/build/source/engine/hyp_2F1.f90 @@ -0,0 +1,1879 @@ +module hyp_2F1_module + +! data types +USE nrtype + +! privacy +implicit none + +public::INF_NORM +public::TANZ +public::LOG1P +public::EXPM1 +public::GAMMA_INV +public::GAMMA_RATIO_DIFF_SMALL_EPS +public::GAMMA_INV_DIFF_EPS +public::A_SUM_INIT +public::LOG_A_SUM_INIT +public::B_SUM_INIT_PS_ONE +public::B_SUM_INIT_PS_INFINITY +public::CV_POLY_DER_CALC +public::MIN_N_CALC +public::HYP_PS_ZERO +public::HYP_PS_ONE +public::HYP_PS_INFINITY +public::HYP_PS_COMPLEX_PLANE_REST +public::HYP_2F1 +private::TEST_2F1 + +! constant parameters +real(rkind),parameter :: EPS15=1.e-15_rkind +real(rkind),parameter :: ZERO=0._rkind,ONE=1._rkind,TWO=2._rkind,HALF=0.5_rkind +real(rkind),parameter :: M_PI=3.14159265358979323846_rkind +real(rkind),parameter :: M_PI_2=1.57079632679489661923_rkind +real(rkind),parameter :: M_1_PI=0.31830988618379067154_rkind + +contains + + !============== START HYP_2F1 FILE ==================================== + ! + ! Gamma_inv denotes the entire inverse of the Gamma function. + ! F(z) means 2F1(a,b,c,z) with the a, b, c and z given as inputs + ! in the routine. + ! + ! Elementary functions and standard constants + ! are defined in the module. + ! See N.J.~Higham, ``Accuracy and Stability of Numerical Algorithms'', + ! SIAM, Philadelphia, 1996 for expm1 implementation. + ! log1p follows instantly. + ! + ! 19/04/2012 Modifications by Daniel Sabanes Bove: + ! - renamed LOG_GAMMA to LOG_GAMMA_FUN to avoid name + ! clash with intrinsic function + ! 11/01/2024 Modifications by Ashley Van Beusekom: + ! - made one module + ! - made precision rkind dependent + ! - lowercase for readability + !---------------------------------------------------------------------- + ! + function INF_NORM(Z) + complex(rkind),intent(in) :: Z + real(rkind) :: INF_NORM + INF_NORM=MAX(ABS(REAL(Z,rkind)),ABS(AIMAG(Z))) + return + end function INF_NORM + ! + function TANZ(Z) + complex(rkind),intent(in) :: Z + complex(rkind) :: TANZ + TANZ=SIN(Z)/COS(Z) + return + end function TANZ + ! + function LOG1P(Z) + complex(rkind),intent(in) :: Z + real(rkind) :: X,XP1,LOG1P_X + real(rkind) :: Y,YX,YX2,YX2P1,LOG1P_YX2 + real(rkind) :: RE_LOG1P,IM_LOG1P + complex(rkind) :: LOG1P + if(INF_NORM(Z).lt.ONE) then + X = REAL(Z,rkind); XP1 = X+ONE + if(XP1.eq.ONE) then + LOG1P_X = X + else + LOG1P_X = LOG(XP1)*X/(XP1-ONE) + endif + Y = AIMAG(Z) + YX = Y/XP1; YX2 = YX*YX; YX2P1 = YX2+ONE + if(YX2P1.eq.ONE) then + LOG1P_YX2 = YX2 + else + LOG1P_YX2 = LOG(YX2P1)*YX2/(YX2P1-ONE) + endif + RE_LOG1P = LOG1P_X + HALF*LOG1P_YX2 + IM_LOG1P = ATAN2(Y,XP1) + LOG1P = CMPLX(RE_LOG1P,IM_LOG1P,rkind) + return + else + LOG1P=LOG(ONE+Z) + return + endif + end function LOG1P + ! + function EXPM1(Z) + complex(rkind),intent(in) :: Z + real(rkind) :: X,EXPM1_X,EXP_X,Y,SIN_HALF_Y + real(rkind) :: RE_EXPM1,IM_EXPM1 + complex(rkind) :: EXPM1 + if(INF_NORM(Z).lt.ONE) then + X = real(Z,rkind); EXP_X = EXP(X) + Y = AIMAG(Z); SIN_HALF_Y=SIN(HALF*Y) + if(EXP_X.eq.ONE) then + EXPM1_X = X + else + EXPM1_X = (EXP_X-ONE)*X/LOG(EXP_X) + endif + RE_EXPM1 = EXPM1_X-TWO*EXP_X*SIN_HALF_Y*SIN_HALF_Y + IM_EXPM1 = EXP_X*SIN(Y) + EXPM1 = CMPLX(RE_EXPM1,IM_EXPM1,rkind) + return + else + EXPM1=EXP(Z)-ONE + return + endif + end function EXPM1 + ! + !---------------------------------------------------------------------- + recursive function LOG_GAMMA_FUN(Z) result(RES) + !---------------------------------------------------------------------- + ! Logarithm of Gamma[z] and Gamma inverse function + ! ------------------------------------------------ + ! + ! For log[Gamma[z]],if z is not finite + ! or is a negative integer, the program + ! returns an error message and stops. + ! The Lanczos method is used. Precision : ~ 1E-15 + ! The method works for Re[z]>0.5 . + ! If Re[z]<=0.5, one uses the formula Gamma[z].Gamma[1-z]=Pi/sin(Pi.z) + ! log[sin(Pi.z)] is calculated with the Kolbig method + ! (K.S. Kolbig, Comp. Phys. Comm., Vol. 4, p.221(1972)): + ! If z=x+iy and y>=0, log[sin(Pi.z)]=log[sin(Pi.eps)]-i.Pi.n, + ! with z=n+eps so 0<=Re[eps]< 1 and n integer. + ! If y>110, log[sin(Pi.z)]=-i.Pi.z+log[0.5]+i.Pi/2 + ! numerically so that no overflow can occur. + ! If z=x+iy and y< 0, log[Gamma(z)]=[log[Gamma(z*)]]*, + ! so that one can use the previous formula with z*. + ! + ! For Gamma inverse, Lanczos method is also used + ! with Euler reflection formula. + ! sin (Pi.z) is calculated as sin (Pi.(z-n)) + ! to avoid inaccuracy with z = n + eps + ! with n integer and |eps| as small as possible. + ! + ! + ! Variables: + ! ---------- + ! x,y: Re[z], Im[z] + ! log_sqrt_2Pi,log_Pi : log[sqrt(2.Pi)], log(Pi). + ! sum : Rational function in the Lanczos method + ! log_Gamma_z : log[Gamma(z)] value. + ! c : table containing the fifteen coefficients in the expansion + ! used in the Lanczos method. + ! eps,n : z=n+eps so 0<=Re[eps]< 1 and n integer for Log[Gamma]. + ! z=n+eps and n integer + ! so |eps| is as small as possible for Gamma_inv. + ! log_const : log[0.5]+i.Pi/2 + ! g : coefficient used in the Lanczos formula. It is here 607/128. + ! z,z_m_0p5,z_p_g_m0p5,zm1 : argument of the Gamma function, + ! z-0.5, z-0.5+g, z-1 + ! res: returned value + !---------------------------------------------------------------------- + implicit none + complex(rkind),intent(in) :: Z + integer(i4b) :: N,I + real(rkind) :: X,Y,LOG_SQRT_2PI,G,LOG_PI,M_LN2,C(0:14) + complex(rkind) :: GAMMA_SUM,Z_M_0P5,Z_P_G_M0P5,ZM1 + complex(rkind) :: LOG_CONST,I_PI,EPS,LOG_SIN_PI_Z,RES + ! + M_LN2=0.69314718055994530942_rkind; X=REAL(Z,rkind); Y=AIMAG(Z) + if((Z.eq.NINT(X)).and.(X.le.ZERO)) & + print*,'Z IS NEGATIVE integer IN LOG_GAMMA_FUN' + if(X.ge.HALF) then + LOG_SQRT_2PI=0.91893853320467274177_rkind; G=4.7421875_rkind + Z_M_0P5=Z-HALF; Z_P_G_M0P5=Z_M_0P5+G; ZM1=Z-ONE + C=(/ 0.99999999999999709182_rkind , 57.156235665862923517_rkind, & + -59.597960355475491248_rkind , 14.136097974741747174_rkind, & + -0.49191381609762019978_rkind , 0.33994649984811888699e-4_rkind, & + 0.46523628927048575665e-4_rkind, -0.98374475304879564677e-4_rkind, & + 0.15808870322491248884e-3_rkind, -0.21026444172410488319e-3_rkind, & + 0.21743961811521264320e-3_rkind, -0.16431810653676389022e-3_rkind, & + 0.84418223983852743293e-4_rkind, -0.26190838401581408670e-4_rkind, & + 0.36899182659531622704e-5_rkind /) + + GAMMA_SUM=C(0) + do I=1,14 + GAMMA_SUM=GAMMA_SUM+C(I)/(ZM1+I) + enddo + RES=LOG_SQRT_2PI+LOG(GAMMA_SUM)+Z_M_0P5*LOG(Z_P_G_M0P5) & + -Z_P_G_M0P5 + return + else if(Y.ge.ZERO) then + if(X.lt.NINT(X)) then + N=NINT(X)-1 + else + N=NINT(X) + endif + LOG_PI=1.1447298858494002_rkind + LOG_CONST=CMPLX(-M_LN2,M_PI_2,rkind); I_PI=CMPLX(ZERO,M_PI,rkind) + EPS=Z-N + if(Y.gt.110._rkind) then + LOG_SIN_PI_Z=-I_PI*Z+LOG_CONST + else + LOG_SIN_PI_Z=LOG(SIN(M_PI*EPS))-I_PI*N + endif + RES=LOG_PI-LOG_SIN_PI_Z-LOG_GAMMA_FUN(ONE-Z); + return + else + RES=CONJG(LOG_GAMMA_FUN(CONJG(Z))) + return + endif + end function LOG_GAMMA_FUN + ! + !---------------------------------------------------------------------- + ! Inverse of the Gamma function [1/Gamma](z) + ! ------------------------------------------ + ! It is calculated with the Lanczos method for Re[z] >= 0.5 + ! and is precise up to 10^{-15}. + ! If Re[z] <= 0.5, one uses the formula + ! Gamma[z].Gamma[1-z] = Pi/sin (Pi.z). + ! sin (Pi.z) is calculated as sin (Pi.(z-n)) to avoid inaccuracy, + ! with z = n + eps with n integer and |eps| as small as possible. + ! + ! Variables + ! --------- + ! z : argument of the function + ! x: Re[z] + ! eps,n : z = n + eps with n integer and |eps| as small as possible. + ! res: returned value + !---------------------------------------------------------------------- + recursive function GAMMA_INV(Z) result(RES) + !-------------------------------------------------------------------- + implicit none + complex(rkind),intent(in) :: Z + integer(i4b) :: N,I + real(rkind) :: X,LOG_SQRT_2PI,G,C(0:14) + complex(rkind) :: RES,GAMMA_SUM,Z_M_0P5,Z_P_G_M0P5,ZM1,EPS + ! + X=REAL(Z,rkind) + if(X.ge.HALF) then + LOG_SQRT_2PI=0.91893853320467274177_rkind; G=4.7421875_rkind + Z_M_0P5=Z-HALF; Z_P_G_M0P5=Z_M_0P5+G; ZM1=Z-ONE + C=(/ 0.99999999999999709182_rkind , 57.156235665862923517_rkind, & + -59.597960355475491248_rkind , 14.136097974741747174_rkind, & + -0.49191381609762019978_rkind , 0.33994649984811888699e-4_rkind, & + 0.46523628927048575665e-4_rkind, -0.98374475304879564677e-4_rkind, & + 0.15808870322491248884e-3_rkind, -0.21026444172410488319e-3_rkind, & + 0.21743961811521264320e-3_rkind, -0.16431810653676389022e-3_rkind, & + 0.84418223983852743293e-4_rkind, -0.26190838401581408670e-4_rkind, & + 0.36899182659531622704e-5_rkind /) + + GAMMA_SUM=C(0) + do I=1,14 + GAMMA_SUM=GAMMA_SUM+C(I)/(ZM1+I); + enddo + RES=EXP(Z_P_G_M0P5-Z_M_0P5*LOG(Z_P_G_M0P5)-LOG_SQRT_2PI) & + /GAMMA_SUM + return + else + X=REAL(Z,rkind); N=NINT(X) + EPS=Z-N + if(MOD(N,2).eq.0) then + RES=SIN(M_PI*EPS)*M_1_PI/GAMMA_INV (ONE-Z) + return + else + RES=-SIN(M_PI*EPS)*M_1_PI/GAMMA_INV (ONE-Z) + return + endif + endif + end function GAMMA_INV + !---------------------------------------------------------------------- + ! + ! Calculation of H(z,eps) = [Gamma(z+eps)/Gamma(z) - 1]/eps, with e and + ! --------------------------------------------------------------------- + ! z complex so z,z+eps are not negative integers and 0 <= |eps|oo < 0.1 + ! --------------------------------------------------------------------- + ! The function H(z,eps) = [Gamma(z+eps)/Gamma(z) - 1]/e is calculated + ! here with the Lanczos method. + ! For the Lanczos method, the gamma parameter, denoted as g, + ! is 4.7421875 and one uses a sum of 15 numbers with the table c[15], + ! so that it is precise up to machine accuracy. + ! The H(z,eps) function is used in formulas occuring in1-z and 1/z + ! transformations (see Comp. Phys. Comm. paper). + ! + ! One must have z and z+eps not negative integers as otherwise + ! it is clearly not defined. + ! As this function is meant to be precise for small |eps|oo, + ! one has to have 0 <= |eps|oo < 0.1 . + ! Indeed, a direct implementation of H(z,eps) with Gamma_inv or + ! log_Gamma for |eps|oo >= 0.1 is numerically stable. + ! The returned function has full numerical accuracy + ! even if |eps|oo is very small. + ! + ! eps not equal to zero + ! --------------------- + ! If Re(z) >= 0.5 or Re(z+eps) >= 0.5, one clearly has Re(z) > 0.4 + ! and Re(z+eps) > 0.4, + ! so that the Lanczos summation can be used for both Gamma(z) + ! and Gamma(z+eps). + ! One then has: + ! log[Gamma(z+eps)/Gamma(z)] = + ! (z-0.5) log1p[eps/(z+g-0.5)] + eps log(z+g-0.5+eps) - eps + ! + log1p[-eps \sum_{i=1}^{14} c[i]/((z-1+i)(z-1+i+eps)) + ! / (c[0] + \sum_{i=1}^{14} c[i]/(z-1+i))] + ! H(z,eps) = expm1[log[Gamma(z+eps)/Gamma(z)]]/eps . + ! + ! If Re(z) < 0.5 and Re(z+eps) < 0.5, + ! Euler reflection formula is used for both Gamma(z) and Gamma(z+eps). + ! One then has: + ! H(z+eps,-eps) = [cos(pi.eps) + sin(pi.eps)/tan(pi(z-n))].H(1-z,-eps) + ! + (2/eps).sin^2(eps.pi/2) - sin(pi.eps)/(eps.tan(pi.(z-n))) + ! H(1-z,-eps) is calculated with the Lanczos summation + ! as Re(1-z) >= 0.5 and Re(1-z-eps) >= 0.5 . + ! z-n is used in tan(pi.z) instead of z to avoid inaccuracies + ! due the finite number of digits of pi. + ! H(z,eps) = H(z+eps,-eps)/(1 - eps.H(z+eps,-eps)) + ! provides the final result. + ! + ! eps equal to zero + ! ----------------- + ! It is obtained with the previous case and eps -> 0 : + ! If Re(z) >= 0.5, one has: + ! H(z,eps) = (z-0.5)/(z+g-0.5) + log(z+g-0.5) - 1 - + ! \sum_{i=1}^{14} c[i]/((z-1+i)^2)/(c[0]+\sum_{i=1}^{14} c[i]/(z-1+i)) + ! + ! If Re(z) < 0.5, one has: + ! H(z,0) = H(1-z,0) - pi/tan(pi.(z-n)) + ! + ! Variables + ! --------- + ! z,eps: input variables of the function H(z,eps) + ! g,c[15]: double and table of 15 doubles defining the Lanczos sum + ! so that it provides the Gamma function + ! precise up to machine accuracy. + ! eps_pz,z_m_0p5,z_pg_m0p5,eps_pz_pg_m0p5,zm1,zm1_p_eps: + ! z+eps,z-0.5,z+g-0.5,z+eps+g-0.5,z-1,z-1+eps + ! x,eps_px: real parts of z and z+eps. + ! n,m: closest integer ot the real part of z, same for z+eps. + ! sum_num,sum_den: \sum_{i=1}^{14} c[i]/((z-1+i)(z-1+i+eps)) + ! and (c[0] + \sum_{i=1}^{14} c[i]/(z-1+i)). + ! They appear respectively as numerator and denominator in formulas. + ! Pi_eps,term,T1_eps_z: pi.eps, sin (pi.eps)/tan(pi.(z-n)), + ! [cos(pi.eps) + sin(pi.eps)/tan(pi(z-n))].H(1-z,-eps) + ! sin_Pi_2_eps,T2_eps_z,T_eps_z: sin^2(eps.pi/2), + ! (2/eps).sin^2(eps.pi/2) - sin(pi.eps)/(eps.tan(pi.(z-n))), + ! H(z+eps,-eps) + ! res: returned value + !---------------------------------------------------------------------- + recursive function GAMMA_RATIO_DIFF_SMALL_EPS(Z,EPS) result(RES) + !-------------------------------------------------------------------- + implicit none + complex(rkind),intent(in) :: Z,EPS + integer(i4b) :: N,M,I + real(rkind) :: G,X,EPS_PX,C(0:14) + complex(rkind) :: RES,SUM_NUM,SUM_DEN + complex(rkind) :: EPS_PZ,Z_M_0P5,Z_PG_M0P5,EPS_PZ_PG_M0P5,ZM1 + complex(rkind) :: CI_ZM1_PI_INV,PI_EPS,TT,T1_EPS_Z,SIN_PI_2_EPS + complex(rkind) :: ZM1_P_EPS,T2_EPS_Z,T_EPS_Z + ! + G=4.74218750_rkind + if(INF_NORM(EPS).gt.0.1_rkind) & + print*,'ONE MUST HAVE |EPS|< 0.1 IN GAMMA_RATIO_DIFF_SMALL_EPS' + EPS_PZ=Z+EPS; Z_M_0P5=Z-HALF; Z_PG_M0P5=Z_M_0P5+G + EPS_PZ_PG_M0P5=Z_PG_M0P5+EPS; ZM1=Z-ONE; ZM1_P_EPS=ZM1+EPS + X=REAL(Z,rkind); EPS_PX=REAL(EPS_PZ,rkind); N=NINT(X); M=NINT(EPS_PX) + if((Z.eq.N).and.(N.le.0)) then + print*,'Z IS NEGATIVE integer IN GAMMA_RATIO_DIFF_SMALL_EPS' + endif + if((EPS_PZ.eq.M).and.(M.le.0)) then + print*,'Z+EPS IS NEGATIVE integer IN GAMMA_RATIO_DIFF_SMALL_EPS' + endif + C=(/ 0.99999999999999709182_rkind , 57.156235665862923517_rkind, & + -59.597960355475491248_rkind , 14.136097974741747174_rkind, & + -0.49191381609762019978_rkind , 0.33994649984811888699e-4_rkind, & + 0.46523628927048575665e-4_rkind, -0.98374475304879564677e-4_rkind, & + 0.15808870322491248884e-3_rkind, -0.21026444172410488319e-3_rkind, & + 0.21743961811521264320e-3_rkind, -0.16431810653676389022e-3_rkind, & + 0.84418223983852743293e-4_rkind, -0.26190838401581408670e-4_rkind, & + 0.36899182659531622704e-5_rkind /) + if((X.ge.HALF).or.(EPS_PX.ge.HALF)) then + SUM_NUM=ZERO;SUM_DEN=C(0) + do I=1,14 + CI_ZM1_PI_INV=C(I)/(ZM1+I) + SUM_NUM=SUM_NUM+CI_ZM1_PI_INV/(ZM1_P_EPS+I) + SUM_DEN=SUM_DEN+CI_ZM1_PI_INV + enddo + if(EPS.ne.ZERO) then + RES=EXPM1(Z_M_0P5*LOG1P(EPS/Z_PG_M0P5) & + +EPS*LOG(EPS_PZ_PG_M0P5)-EPS+LOG1P(-EPS*SUM_NUM/SUM_DEN))& + /EPS + return + else + RES=Z_M_0P5/Z_PG_M0P5 & + +LOG(EPS_PZ_PG_M0P5)-ONE-SUM_NUM/SUM_DEN + return + endif + else + if(EPS.ne.ZERO) then + PI_EPS=M_PI*EPS + TT=SIN(PI_EPS)/TANZ(M_PI*(Z-N)) + T1_EPS_Z=(COS(PI_EPS)+TT)*& + GAMMA_RATIO_DIFF_SMALL_EPS(ONE-Z,-EPS) + SIN_PI_2_EPS=SIN(M_PI_2*EPS) + T2_EPS_Z=(TWO*SIN_PI_2_EPS*SIN_PI_2_EPS-TT)/EPS + T_EPS_Z=T1_EPS_Z+T2_EPS_Z + RES=(T_EPS_Z/(ONE-EPS*T_EPS_Z)) + return + else + RES=GAMMA_RATIO_DIFF_SMALL_EPS(ONE-Z,-EPS) & + -M_PI/TANZ(M_PI*(Z-N)) + return + endif + endif + end function GAMMA_RATIO_DIFF_SMALL_EPS + ! + !---------------------------------------------------------------------- + ! Calculation of G(z,eps) = [Gamma_inv(z) - Gamma_inv(z+eps)]/eps + ! --------------------------------------------------------------- + ! with e and z complex + !--------------------- + ! The G(z,eps) function is used in formulas occuring in 1-z + ! and 1/z transformations (see Comp. Phys. Comm. paper). + ! Several case have to be considered for its evaluation. + ! eps is considered equal to zero + ! if z+eps and z are equal numerically. + ! + ! |eps|oo > 0.1 + ! ------------- + ! A direct evaluation with the values Gamma_inv(z) + ! and Gamma_inv(z+eps) is stable and returned. + ! + ! |eps|oo <= 0.1 with z+eps and z numerically different + ! ----------------------------------------------------- + ! If z is a negative integer, z+eps is not, + ! so that G(z,eps) = -Gamma_inv(z+eps)/eps, + ! for which a direct evaluation is precise and returned. + ! If z+eps is a negative integer, z is not, + ! so that G(z,eps) = Gamma_inv(z)/eps, + ! for which a direct evaluation is precise and returned. + ! If both of them are not negative integers, + ! one looks for the one of z and z+eps + ! which is the closest to a negative integer. + ! If it is z, one returns H(z,eps).Gamma_inv(z+eps). + ! If it is z+eps, one returns H(z+eps,-eps).Gamma_inv(z). + ! Both values are equal, so that one chooses the one + ! which makes the Gamma ratio Gamma(z+eps)/Gamma(z) + ! in H(z,eps) the smallest in modulus. + ! + ! z+eps and z numerically equal + ! ----------------------------- + ! If z is negative integer, G(z,0) = (-1)^(n+1) n!, + ! where z = -n, n integer, which is returned. + ! If z is not negative integer, one returns H(z,eps).Gamma_inv(z+eps) + ! + ! Variables + ! --------- + ! z,eps: input variables of the function G(z,eps) + ! eps_pz,x,eps_px: z+eps,real parts of z and z+eps. + ! n,m: closest integer ot the real part of z, same for z+eps. + ! fact,k: (-1)^(n+1) n!, returned when z = -n, n integer + ! and z and z+eps identical numerically (eps ~ 0). + ! It is calculated with integer index k. + ! is_z_negative_integer,is_eps_pz_negative_integer: + ! true if z is a negative integer, false if not, same for z+eps. + ! z_neg_int_distance, eps_pz_neg_int_distance: + ! |z + |n||oo, |z + eps + |m||oo. + ! If |z + |n||oo < |z + eps + |m||oo, + ! z is closer to the set of negative integers than z+eps. + ! Gamma_inv(z+eps) is then of moderate modulus + ! if Gamma_inv(z) is very small. + ! If z ~ n, H(z,eps) ~ -1/eps, + ! that so returning + ! G(z,eps) = H(z,eps).Gamma_inv(z+eps) here is preferred. + ! Same for |z + |n||oo > |z + eps + |m||oo with z <-> z+eps. + ! + !---------------------------------------------------------------------- + function GAMMA_INV_DIFF_EPS(Z,EPS) + !-------------------------------------------------------------------- + implicit none + complex(rkind),intent(in) :: Z,EPS + integer(i4b) :: M,N,K + real(rkind) :: X,EPS_PX,FACT + real(rkind) :: Z_NEG_INT_DISTANCE + real(rkind) :: EPS_PZ_NEG_INT_DISTANCE + complex(rkind) :: GAMMA_INV_DIFF_EPS,EPS_PZ,GAMMA_INV + complex(rkind) :: GAMMA_RATIO_DIFF_SMALL_EPS + logical(lgt) :: IS_Z_NEG_INT,IS_EPS_PZ_NEG_INT + + EPS_PZ=Z+EPS; X=REAL(Z,rkind); EPS_PX=REAL(EPS_PZ,rkind) + N=NINT(X); M=NINT(EPS_PX) + IS_Z_NEG_INT=(Z.eq.N).and.(N.le.0) + IS_EPS_PZ_NEG_INT=(EPS_PZ.eq.M).and.(M.le.0) + if(INF_NORM(EPS).gt.0.10_rkind) then + GAMMA_INV_DIFF_EPS = (GAMMA_INV (Z) - GAMMA_INV (EPS_PZ))/EPS + return + else if(EPS_PZ.ne.Z) then + if(IS_Z_NEG_INT) then + GAMMA_INV_DIFF_EPS = (-GAMMA_INV (EPS_PZ)/EPS) + return + else if(IS_EPS_PZ_NEG_INT) then + GAMMA_INV_DIFF_EPS = (GAMMA_INV (Z)/EPS) + return + else + Z_NEG_INT_DISTANCE = INF_NORM (Z + ABS (N)) + EPS_PZ_NEG_INT_DISTANCE = INF_NORM (EPS_PZ + ABS (M)) + if(Z_NEG_INT_DISTANCE.lt.EPS_PZ_NEG_INT_DISTANCE) then + GAMMA_INV_DIFF_EPS= & + GAMMA_RATIO_DIFF_SMALL_EPS (Z,EPS)*GAMMA_INV (EPS_PZ) + return + else + GAMMA_INV_DIFF_EPS= & + GAMMA_RATIO_DIFF_SMALL_EPS (EPS_PZ,-EPS)*GAMMA_INV (Z) + return + endif + endif + else if(IS_Z_NEG_INT.and.IS_EPS_PZ_NEG_INT) then + FACT = -ONE;K=-1 + do while (K.ge.N) + FACT=FACT*K + K=K-1 + enddo + GAMMA_INV_DIFF_EPS = FACT + return + else + GAMMA_INV_DIFF_EPS = & + GAMMA_RATIO_DIFF_SMALL_EPS (Z,EPS)*GAMMA_INV (EPS_PZ) + return + endif + end function GAMMA_INV_DIFF_EPS + !---------------------------------------------------------------------- + ! + ! Calculation of Gamma_inv(1-m-eps)/eps of the A(z) polynomial in 1-z + ! ------------------------------------------------------------------- + ! and 1/z transformations + ! ----------------------- + ! This value occurs in A(z) in 1-z and 1/z transformations + ! (see Comp. Phys. Comm. paper) for m > 0. + ! Both cases of 1-m-eps numerically negative integer + ! or not have to be considered + ! + ! 1-eps-m and 1-m numerically different + ! ------------------------------------- + ! One returns Gamma_inv(1-m-eps)/eps directly + ! as its value is accurate. + ! To calculate Gamma_inv(1-m-eps), + ! one uses the value Gamma_inv(1-eps), + ! needed in considered transformations, + ! and one uses the equality + ! Gamma_inv(1-m-eps) = Gamma_inv(1-eps) \prod_{i=1}^{m} (1-eps-i) + ! for m > 0. + ! It is trivially demonstrated + ! from the equality Gamma(x+1) = x.Gamma(x). + ! One Gamma function evaluation is removed this way + ! from the calculation. + ! + ! 1-eps-m and 1-m numerically equal + ! --------------------------------- + ! This implies that 1-m-eps is negative integer numerically. + ! Here, eps~0, so that one returns the limit of Gamma_inv(1-m-eps)/eps + ! for eps -> 0, which is (-1)^m (m-1)! + ! + ! Variables + ! --------- + ! m,eps: variable inputs of the function + ! (m,eps) -> Gamma_inv(1-m-eps)/eps + ! Gamma_inv_one_meps: Gamma_inv(1-eps), + ! previously calculated and here recycled + ! to quickly calculate Gamma_inv(1-m-eps). + ! one_meps: 1-eps + !---------------------------------------------------------------------- + function A_SUM_INIT(M,EPS,GAMMA_INV_ONE_MEPS) + !-------------------------------------------------------------------- + implicit none + integer(i4b),intent(in) :: M + complex(rkind),intent(in) :: EPS,GAMMA_INV_ONE_MEPS + integer(i4b) :: N,I + real(rkind) :: FACT + complex(rkind) :: A_SUM_INIT,ONE_MEPS + complex(rkind) :: GAMMA_INV_ONE_MEPS_MM + ! + ONE_MEPS = ONE - EPS + if(ONE_MEPS-M.ne.1-M) then + GAMMA_INV_ONE_MEPS_MM = GAMMA_INV_ONE_MEPS + do I=1,M + GAMMA_INV_ONE_MEPS_MM = GAMMA_INV_ONE_MEPS_MM*(ONE_MEPS-I) + enddo + A_SUM_INIT=GAMMA_INV_ONE_MEPS_MM/EPS + return + else + FACT=ONE + do N=2,M-1 + FACT=FACT*N + enddo + if(MOD(M,2).eq.0) then + A_SUM_INIT=FACT + else + A_SUM_INIT=-FACT + endif + return + endif + end function A_SUM_INIT + ! + !---------------------------------------------------------------------- + ! Calculation of the log of Gamma_inv(1-m-eps)/eps + ! ------------------------------------------------ + ! See previous function. + ! It is used in case Gamma_inv(1-m-eps)/eps might overflow. + ! + ! Variables + ! --------- + ! m,eps: variable inputs of the function + ! (m,eps) -> log[Gamma_inv(1-m-eps)/eps] + ! one_meps_mm: 1-eps-m + ! i_Pi: i.Pi + ! log_fact: logarithm of (-1)^m (m-1)!, + ! here defined as log((m-1)!) + i.Pi if m is odd. + !---------------------------------------------------------------------- + function LOG_A_SUM_INIT(M,EPS) + !-------------------------------------------------------------------- + implicit none + integer(i4b),intent(in) :: M + complex(rkind),intent(in) :: EPS + integer(i4b) :: N + real(rkind) :: LOG_FACT + complex(rkind) :: ONE_MEPS_MM,LOG_A_SUM_INIT,LOG_GAMMA_FUN + ! + ONE_MEPS_MM=ONE-EPS-M + if(ONE_MEPS_MM.ne.1-M) then + LOG_A_SUM_INIT=(-LOG_GAMMA_FUN(ONE_MEPS_MM) - LOG(EPS)) + return + else + LOG_FACT=ZERO + do N=2,M-1 + LOG_FACT=LOG_FACT + LOG(DBLE(N)) + enddo + if(MOD(M,2).eq.0) then + LOG_A_SUM_INIT=LOG_FACT + else + LOG_A_SUM_INIT=CMPLX(LOG_FACT,M_PI,rkind) + endif + return + endif + end function LOG_A_SUM_INIT + !---------------------------------------------------------------------- + ! Calculation of the first term of the B(z) power series + ! ------------------------------------------------------ + ! in the 1-z transformation, divided by (1-z)^m + ! ---------------------------------------------- + ! In the 1-z transformation, + ! the power series B(z) = \sum_{n=0}^{+oo} \beta_n (1-z)^n occurs + ! (see Comp. Phys. Comm. paper). + ! The first term \beta_0, divided by (1-z)^m, is calculated here. + ! m is the closest integer to Re(c-a-b) >= 0 and eps = c-a-b-m. + ! + ! One has to consider |eps|oo > 0.1 and |eps|oo <= 0.1, + ! where 1-m-eps and 1-m can be different or equal numerically, + ! leading to some changes in this last case. + ! + ! |eps|oo > 0.1 + ! ------------- + ! One has \beta_0/(1-z)^m = [(a)_m (b)_m Gamma_inv(1-eps) + ! Gamma_inv(a+m+eps) Gamma_inv(b+m+eps) Gamma_inv(m+1) + ! - (1-z)^eps Gamma_inv(a) Gamma_inv(b) Gamma_inv(1+m+eps)] + ! [Gamma(c)/eps], stable in this regime for a direct evaluation. + ! + ! The values of Gamma(c), Gamma_inv(a+m+eps) + ! and Gamma_inv(b+m+eps) were already calculated and recycled here. + ! Gamma_inv(m+1) is calculated as 1/(m!). + ! + ! Gamma_inv(1+m+eps) is calculated from Gamma_inv(1-eps), + ! using the equalities: + ! Gamma_inv(1-m-eps) = Gamma_inv(1-eps) \prod_{i=1}^{m} (1-eps-i), + ! where the product is 1 by definition if m = 0, + ! Gamma_inv(1+m+eps) = (-1)^m sin (pi.eps) + ! /[pi.(eps+m).Gamma_inv(1-m-eps)] + ! from Euler reflection formula, Gamma(x+1) = x.Gamma(x) equality, + ! and m+eps no zero. + ! This scheme is much faster than + ! to recalculate Gamma_inv(1+m+eps) directly. + ! + ! |eps|oo <= 0.1 + ! -------------- + ! The \beta_0/(1-z)^m expression is rewritten + ! so that it contains no instabilities: + ! \beta_0/(1-z)^m = Gamma_inv(a+m+eps) Gamma_inv(b+m+eps) + ! [(G(1,-eps) Gamma_inv(m+1) + G(m+1,eps)) + ! - Gamma_inv(1+m+eps) (G(a+m,eps) Gamma_inv(b+m+eps) + ! + G(b+m,eps) Gamma_inv(a+m)) + ! - E(log(1-z),eps) Gamma_inv(a+m) Gamma_inv(b+m) Gamma_inv(1+m+eps)] + ! (a)_m (b)_m Gamma(c) + ! + ! E(log(1-z),eps) is [(1-z)^eps - 1]/eps + ! if 1-m-eps and 1-m are different numerically, + ! and log(1-z) otherwise (eps ~ 0). + ! If 1-m-eps and 1-m are equal numerically, + ! Gamma_inv(1+m+eps) is numerically equal to Gamma_inv(1+m), + ! already calculated as 1/(m!). + ! See |eps|oo > 0.1 case for data recycling of other values + ! or for 1-m-eps and 1-m different numerically. + ! + !---------------------------------------------------------------------- + ! Variables + ! --------- + ! a,b,c,one_minus_z: a,b,c and 1-z parameters and arguments + ! of the 2F1(a,b,c,z) function. + ! m,eps: closest integer to c-a-b, with Re(c-a-b) >= 0 + ! and eps = c-a-b-m + ! Gamma_c,Gamma_inv_one_meps,Gamma_inv_eps_pa_pm, Gamma_inv_eps_pb_pm: + ! recycled values of Gamma(c), Gamma_inv(1-eps), + ! Gamma_inv(a+m+eps) and Gamma_inv(b+m+eps). + ! inf_norm_eps,phase,a_pm,b_pm,one_meps,Pi_eps,Pi_eps_pm: + ! |eps|oo,(-1)^m,a+m,b+m,1-eps,pi.eps,pi.(eps+m) + ! Gamma_inv_one_meps_mm,Gamma_inv_eps_pm_p1: + ! Gamma_inv(1-m-eps) and Gamma_inv(1+m+eps) + ! calculated with the recycling scheme. + ! prod1: (a)_m (b)_m Gamma_inv(1-eps) Gamma_inv(a+m+eps) + ! x Gamma_inv(b+m+eps) Gamma_inv(m+1) in |eps|oo > 0.1 case. + ! prod2: (1-z)^eps Gamma_inv(a) Gamma_inv(b) Gamma_inv(1+m+eps) + ! in |eps|oo > 0.1 case. + ! Gamma_inv_mp1,prod_ab: Gamma_inv(m+1) calculated as 1/(m!) + ! and (a)_m (b)_m in |eps|oo <= 0.1 case. + ! is_eps_non_zero: true if 1-m-eps and 1-m are different numerically, + ! false if not. + ! Gamma_inv_a_pm,Gamma_inv_b_pm,z_term: Gamma_inv(a+m),Gamma_inv(b+m), + ! E(eps,log(1-z)) + ! prod1: Gamma_inv(a+m+eps) Gamma_inv(b+m+eps) + ! x [(G(1,-eps) Gamma_inv(m+1) + G(m+1,eps)) in |eps|oo <= 0.1 case. + ! prod2: Gamma_inv(1+m+eps) (G(a+m,eps) Gamma_inv(b+m+eps) + ! + G(b+m,eps) Gamma_inv(a+m)) + ! prod3: E(eps,log(1-z)) Gamma_inv(a+m) Gamma_inv(b+m) + ! Gamma_inv(1+m+eps) + ! res: returned \beta_0/(1-z)^m value in all cases. + !---------------------------------------------------------------------- + function B_SUM_INIT_PS_ONE(A,B,GAMMA_C,GAMMA_INV_ONE_MEPS, & + GAMMA_INV_EPS_PA_PM,GAMMA_INV_EPS_PB_PM,MZP1,M,EPS) + !-------------------------------------------------------------------- + implicit none + integer(i4b),intent(in) :: M + complex(rkind),intent(in) :: A,B,GAMMA_C,GAMMA_INV_ONE_MEPS + complex(rkind),intent(in) :: GAMMA_INV_EPS_PA_PM,GAMMA_INV_EPS_PB_PM,MZP1,EPS + integer(i4b) :: M_M1,N,I,PHASE + real(rkind) :: INF_NORM_EPS,GAMMA_INV_MP1 + complex(rkind) :: A_PM,B_SUM_INIT_PS_ONE,PI_EPS,GAMMA_INV_ONE_MEPS_MM + complex(rkind) :: B_PM,TMP1,TMP2 + complex(rkind) :: Z_TERM,PROD1,PROD2,PROD3,ONE_MEPS,PI_EPS_PM + complex(rkind) :: GAMMA_INV_A_PM,PROD_AB,GAMMA_INV,GAMMA_INV_B_PM + complex(rkind) :: GAMMA_INV_DIFF_EPS,GAMMA_INV_EPS_PM_P1 + ! + INF_NORM_EPS=INF_NORM(EPS); M_M1=M-1; A_PM=A+M; B_PM=B+M + ONE_MEPS=ONE-EPS; PI_EPS=M_PI*EPS; PI_EPS_PM = M_PI*(EPS+M) + if(MOD(M,2).eq.0) then + PHASE = 1 + else + PHASE = -1 + endif + GAMMA_INV_ONE_MEPS_MM = GAMMA_INV_ONE_MEPS + do I=1,M + GAMMA_INV_ONE_MEPS_MM = GAMMA_INV_ONE_MEPS_MM*(ONE_MEPS - I) + enddo + if(INF_NORM_EPS.gt.0.10_rkind) then + GAMMA_INV_EPS_PM_P1 = PHASE*SIN(PI_EPS) & + /(PI_EPS_PM*GAMMA_INV_ONE_MEPS_MM) + PROD1=GAMMA_INV_ONE_MEPS*GAMMA_INV_EPS_PA_PM*GAMMA_INV_EPS_PB_PM + do N=0,M_M1 + PROD1=PROD1*(A+N)*(B+N)/(N+ONE) + enddo + PROD2=GAMMA_INV(A)*GAMMA_INV(B)*GAMMA_INV_EPS_PM_P1*(MZP1**EPS) + B_SUM_INIT_PS_ONE=GAMMA_C*(PROD1-PROD2)/EPS + return + else + GAMMA_INV_MP1=ONE;PROD_AB=ONE + do N=0,M_M1 + GAMMA_INV_MP1 = GAMMA_INV_MP1/(N+ONE) + PROD_AB = PROD_AB*(A+N)*(B+N) + enddo + if(ONE_MEPS-M.ne.1-M) then + Z_TERM=EXPM1(EPS*LOG(MZP1))/EPS + GAMMA_INV_EPS_PM_P1 = PHASE*SIN(PI_EPS) & + /(PI_EPS_PM*GAMMA_INV_ONE_MEPS_MM) + else + Z_TERM=LOG(MZP1) + GAMMA_INV_EPS_PM_P1 = GAMMA_INV_MP1 + endif + GAMMA_INV_A_PM=GAMMA_INV(A_PM);GAMMA_INV_B_PM=GAMMA_INV(B_PM) + TMP1=ONE; TMP2=M+1; + PROD1 = GAMMA_INV_EPS_PA_PM*GAMMA_INV_EPS_PB_PM & + *(GAMMA_INV_MP1*GAMMA_INV_DIFF_EPS(TMP1,-EPS) & + +GAMMA_INV_DIFF_EPS(TMP2,EPS)) + PROD2 = GAMMA_INV_EPS_PM_P1 & + *(GAMMA_INV_EPS_PB_PM*GAMMA_INV_DIFF_EPS(A_PM,EPS) & + +GAMMA_INV_A_PM*GAMMA_INV_DIFF_EPS (B_PM,EPS)) + PROD3 = GAMMA_INV_A_PM*GAMMA_INV_B_PM*GAMMA_INV_EPS_PM_P1*Z_TERM + B_SUM_INIT_PS_ONE=GAMMA_C*PROD_AB*(PROD1-PROD2-PROD3) + return + endif + end function B_SUM_INIT_PS_ONE + ! + !---------------------------------------------------------------------- + ! Calculation of the first term of the B(z) power series + ! ------------------------------------------------------ + ! in the 1/z transformation, divided by z^{-m} + !--------------------------------------------- + ! In the 1/z transformation, the power series + ! B(z) = \sum_{n=0}^{+oo} \beta_n z^{-n} occurs + ! (see Comp. Phys. Comm. paper). + ! The first term \beta_0, divided by z^{-m}, is calculated here. + ! m is the closest integer to Re(b-a) >= 0 and eps = b-a-m. + ! + ! One has to consider |eps|oo > 0.1 and |eps|oo <= 0.1, + ! where 1-m-eps and 1-m can be different or equal numerically, + ! leading to some changes in this last case. + ! + ! |eps|oo > 0.1 + ! ------------- + ! One has \beta_0/z^{-m} = [(a)_m (1-c+a)_m Gamma_inv(1-eps) + ! Gamma_inv(a+m+eps) Gamma_inv(c-a) Gamma_inv(m+1) + ! - (-z)^{-eps} (1-c+a+eps)_m Gamma_inv(a) Gamma_inv(c-a-eps) + ! Gamma_inv(1+m+eps)].[Gamma(c)/eps], + ! stable in this regime for a direct evaluation. + ! + ! The values of Gamma(c), Gamma_inv(c-a) and Gamma_inv(a+m+eps) + ! were already calculated and recycled here. + ! Gamma_inv(m+1) is calculated as 1/(m!). + ! Gamma_inv(1+m+eps) is calculated from Gamma_inv(1-eps) + ! as in the 1-z transformation routine. + ! + ! |eps|oo <= 0.1 + ! -------------- + ! The \beta_0/z^{-m} expression is rewritten + ! so that it contains no instabilities: + ! \beta_0/z^{-m} = [((1-c+a+eps)_m G(1,-eps) - P(m,eps,1-c+a) + ! Gamma_inv(1-eps)) Gamma_inv(c-a) Gamma_inv(a+m+eps) Gamma_inv(m+1) + ! + (1-c+a+eps)_m [G(m+1,eps) Gamma_inv(c-a) Gamma_inv(a+m+eps) + ! - G(a+m,eps) Gamma_inv(c-a) Gamma_inv(m+1+eps)] + ! - (G(c-a,-eps) - E(log(-z),-eps)) Gamma_inv(m+1+eps) + ! Gamma_inv(a+m)]] (a)_m Gamma(c) + ! + ! Definitions and method are the same + ! as in the 1-z transformation routine, except for P(m,eps,1-c+a). + ! P(m,eps,s) = [(s+eps)_m - (s)_m]/eps + ! for eps non zero and has a limit for eps -> 0. + ! Let n0 be the closest integer to -Re(s) for s complex. + ! A stable formula available for eps -> 0 for P(m,eps,s) is: + ! P(m,eps,s) = (s)_m E(\sum_{n=0}^{m-1} L(1/(s+n),eps),eps) + ! if n0 is not in [0:m-1], + ! P(m,eps,s) = \prod_{n=0, n not equal to n0}^{m-1} (s+eps+n) + ! + (s)_m E(\sum_{n=0, n not equal to n0}^{m-1} L(1/(s+n),eps),eps) + ! if n0 is in [0:m-1]. + ! L(s,eps) is log1p(s eps)/eps if eps is not zero, + ! and L(s,0) = s. + ! This expression is used in the code. + ! + ! Variables + ! --------- + ! a,b,c,z: a,b,c and z parameters + ! and arguments of the 2F1(a,b,c,z) function. + ! m,eps: closest integer to b-a, with Re(b-a) >= 0 and eps = b-a-m. + ! Gamma_c,Gamma_inv_cma,Gamma_inv_one_meps,Gamma_inv_eps_pa_pm: + ! recycled values of Gamma(c), Gamma_inv(c-a), Gamma_inv(1-eps) + ! and Gamma_inv(a+m+eps). + ! inf_norm_eps,phase,cma,a_mc_p1,a_mc_p1_pm,cma_eps,eps_pa_mc_p1,a_pm: + ! |eps|oo,(-1)^m,c-a,1-c+a+m,c-a-eps,1-c+a+eps,a+m + ! Gamma_inv_cma_meps,one_meps,Pi_eps,Pi_eps_pm: + ! Gamma_inv(c-a-eps),1-eps,pi.eps,pi.(eps+m) + ! Gamma_inv_one_meps_mm,Gamma_inv_eps_pm_p1: Gamma_inv(1-m-eps) + ! and Gamma_inv(1+m+eps) calculated with the recycling scheme. + ! prod1: (a)_m (1-c+a)_m Gamma_inv(1-eps) Gamma_inv(a+m+eps) + ! x Gamma_inv(c-a) Gamma_inv(m+1) in |eps|oo > 0.1 case. + ! prod2: (-z)^{-eps} (1-c+a+eps)_m Gamma_inv(a) + ! x Gamma_inv(c-a-eps) Gamma_inv(1+m+eps) in |eps|oo > 0.1 case. + ! n0: closest integer to -Re(1-c+a) + ! is_n0_here: true is n0 belongs to [0:m-1], false if not. + ! is_eps_non_zero: true if 1-m-eps and 1-m are different numerically, + ! false if not. + ! Gamma_inv_mp1,prod_a,prod_a_mc_p1: + ! Gamma_inv(m+1) calculated as 1/(m!), + ! (a)_m and (1-c+a)_m in |eps|oo <= 0.1 case. + ! prod_eps_pa_mc_p1_n0: + ! \prod_{n=0, n not equal to n0}^{m-1} (1-c+a+eps+n) + ! if n0 belongs to [0:m-1], 0.0 if not, in |eps|oo <= 0.1 case. + ! prod_eps_pa_mc_p1: (1-c+a+eps)_m in |eps|oo <= 0.1 case. + ! sum: \sum_{n=0, n not equal to n0}^{m-1} L(1/(s+n),eps) if 1-m-eps + ! and 1-m are different numerically, + ! \sum_{n=0, n not equal to n0}^{m-1} 1/(s+n) if not. + ! a_pn,a_mc_p1_pn,eps_pa_mc_p1_pn: a+n,1-c+a+n,1-c+a+eps+n values + ! used in (a)_m, (1-c+a)_m and (1-c+a+eps)_m evaluations. + ! sum_term,prod_diff_eps,z_term: + ! E(\sum_{n=0, n not equal to n0}^{m-1} L(1/(s+n),eps),eps), + ! P(m,eps,1-c+a), -E(-eps,log(-z)) + ! Gamma_inv_a_pm,Gamma_prod1: Gamma_inv(a+m), + ! Gamma_inv(c-a).Gamma_inv(a+m+eps) + ! prod1: ((1-c+a+eps)_m G(1,-eps) + ! - P(m,eps,1-c+a) Gamma_inv(1-eps)) Gamma_inv(c-a) + ! x Gamma_inv(a+m+eps) Gamma_inv(m+1) + ! prod_2a: Gamma_inv(c-a).Gamma_inv(a+m+eps).G(m+1,eps) + ! prod_2b: G(a+m,eps) Gamma_inv(c-a) Gamma_inv(m+1+eps) + ! prod_2c: (G(c-a,-eps) + ! - E(log(-z),-eps)) Gamma_inv(m+1+eps) Gamma_inv(a+m) + ! prod2: (1-c+a+eps)_m [G(m+1,eps) Gamma_inv(c-a) Gamma_inv(a+m+eps) + ! - G(a+m,eps) Gamma_inv(c-a) Gamma_inv(m+1+eps)] + ! - (G(c-a,-eps) - E(log(-z),-eps)) + ! x Gamma_inv(m+1+eps) Gamma_inv(a+m)]] + ! res: returned \beta_0/z^{-m} value in all cases. + !---------------------------------------------------------------------- + function B_SUM_INIT_PS_INFINITY(A,C,GAMMA_C,GAMMA_INV_CMA, & + GAMMA_INV_ONE_MEPS,GAMMA_INV_EPS_PA_PM,Z,M,EPS) + !-------------------------------------------------------------------- + implicit none + integer(i4b),intent(in) :: M + complex(rkind),intent(in) :: A,C,GAMMA_C,GAMMA_INV_CMA,Z,EPS + complex(rkind),intent(in) :: GAMMA_INV_ONE_MEPS,GAMMA_INV_EPS_PA_PM + integer(i4b) :: M_M1,I,N,N0,PHASE + logical(lgt) :: IS_N0_HERE,IS_EPS_NON_ZERO + real(rkind) :: INF_NORM_EPS,NP1,GAMMA_INV_MP1 + complex(rkind) :: B_SUM_INIT_PS_INFINITY,GAMMA_INV,TMP1 + complex(rkind) :: CMA,A_MC_P1,A_MC_P1_PM,CMA_MEPS,EPS_PA_MC_P1,A_PM + complex(rkind) :: GAMMA_INV_EPS_PM_P1,GAMMA_INV_CMA_MEPS,PI_EPS + complex(rkind) :: PROD1,PROD2,A_PN,A_MC_P1_PN,ONE_MEPS + complex(rkind) :: PROD_A,PROD_A_MC_P1,PROD_EPS_PA_MC_P1_N0,PI_EPS_PM + complex(rkind) :: PROD_EPS_PA_MC_P1,SUM_N0,Z_TERM,SUM_TERM + complex(rkind) :: PROD_DIFF_EPS,GAMMA_INV_A_PM,GAMMA_PROD1 + complex(rkind) :: PROD_2A,PROD_2B,PROD_2C,GAMMA_INV_DIFF_EPS + complex(rkind) :: EPS_PA_MC_P1_PN,GAMMA_INV_ONE_MEPS_MM + ! + INF_NORM_EPS=INF_NORM(EPS); CMA=C-A; A_MC_P1=A-C+ONE + A_MC_P1_PM=A_MC_P1+M; CMA_MEPS=CMA-EPS; EPS_PA_MC_P1=EPS+A_MC_P1 + A_PM=A+M; M_M1=M-1; ONE_MEPS=ONE-EPS; PI_EPS=M_PI*EPS + PI_EPS_PM=M_PI*(EPS+M); GAMMA_INV_CMA_MEPS=GAMMA_INV(CMA_MEPS) + if(MOD(M,2).eq.0) then + PHASE = 1 + else + PHASE = -1 + endif + GAMMA_INV_ONE_MEPS_MM = GAMMA_INV_ONE_MEPS + do I=1,M + GAMMA_INV_ONE_MEPS_MM = GAMMA_INV_ONE_MEPS_MM*(ONE_MEPS - I) + enddo + if(INF_NORM_EPS.gt.0.1_rkind) then + GAMMA_INV_EPS_PM_P1 = PHASE*SIN(PI_EPS) & + /(PI_EPS_PM*GAMMA_INV_ONE_MEPS_MM) + PROD1 = GAMMA_INV_CMA*GAMMA_INV_EPS_PA_PM*GAMMA_INV_ONE_MEPS + PROD2 = GAMMA_INV(A)*GAMMA_INV_CMA_MEPS*GAMMA_INV_EPS_PM_P1 & + *((-Z)**(-EPS)) + do N=0,M_M1 + A_PN=A+N; A_MC_P1_PN=A_MC_P1+N + EPS_PA_MC_P1_PN=EPS+A_MC_P1_PN;NP1=N+ONE + PROD1 = PROD1*A_PN*A_MC_P1_PN/NP1 + PROD2 = PROD2*EPS_PA_MC_P1_PN + enddo + B_SUM_INIT_PS_INFINITY = GAMMA_C*(PROD1-PROD2)/EPS + return + else + N0=-NINT(REAL(A_MC_P1,rkind)) + IS_EPS_NON_ZERO=ONE_MEPS-M.ne.1-M + IS_N0_HERE=(N0.ge.0).and.(N0.lt.M) + GAMMA_INV_MP1=ONE; PROD_A=ONE; PROD_A_MC_P1=ONE + PROD_EPS_PA_MC_P1=ONE; SUM_N0=ZERO + if(IS_N0_HERE) then + PROD_EPS_PA_MC_P1_N0 = ONE + else + PROD_EPS_PA_MC_P1_N0 = ZERO + endif + do N=0,M_M1 + A_PN=A+N; A_MC_P1_PN=A_MC_P1+N + EPS_PA_MC_P1_PN=EPS+A_MC_P1_PN; NP1=N+ONE + PROD_A = PROD_A*A_PN + PROD_A_MC_P1 = PROD_A_MC_P1*A_MC_P1_PN + PROD_EPS_PA_MC_P1 = PROD_EPS_PA_MC_P1*EPS_PA_MC_P1_PN + GAMMA_INV_MP1 = GAMMA_INV_MP1/NP1 + if(N.ne.N0) then + if(IS_N0_HERE) then + PROD_EPS_PA_MC_P1_N0=PROD_EPS_PA_MC_P1_N0 & + *EPS_PA_MC_P1_PN + endif + if(IS_EPS_NON_ZERO) then + SUM_N0 = SUM_N0 + LOG1P(EPS/A_MC_P1_PN) + else + SUM_N0 = SUM_N0 + ONE/A_MC_P1_PN + endif + endif + enddo + if(IS_EPS_NON_ZERO) then + GAMMA_INV_EPS_PM_P1 = PHASE*SIN(PI_EPS) & + /(PI_EPS_PM*GAMMA_INV_ONE_MEPS_MM) + SUM_TERM = EXPM1(SUM_N0)/EPS + Z_TERM = EXPM1(-EPS*LOG(-Z))/EPS + else + GAMMA_INV_EPS_PM_P1 = GAMMA_INV_MP1 + SUM_TERM = SUM_N0 + Z_TERM = -LOG(-Z) + endif + PROD_DIFF_EPS = PROD_EPS_PA_MC_P1_N0 + PROD_A_MC_P1*SUM_TERM + GAMMA_INV_A_PM = GAMMA_INV(A_PM) + GAMMA_PROD1=GAMMA_INV_CMA*GAMMA_INV_EPS_PA_PM + TMP1=ONE + PROD1 = GAMMA_PROD1*GAMMA_INV_MP1*(GAMMA_INV_DIFF_EPS(TMP1,-EPS) & + *PROD_EPS_PA_MC_P1 - GAMMA_INV_ONE_MEPS*PROD_DIFF_EPS) + TMP1=M+1 + PROD_2A = GAMMA_PROD1*GAMMA_INV_DIFF_EPS(TMP1,EPS) + PROD_2B = GAMMA_INV_CMA*GAMMA_INV_EPS_PM_P1 & + *GAMMA_INV_DIFF_EPS(A_PM,EPS) + PROD_2C = GAMMA_INV_EPS_PM_P1*GAMMA_INV_A_PM & + *(GAMMA_INV_DIFF_EPS(CMA,-EPS) + GAMMA_INV_CMA_MEPS*Z_TERM) + PROD2 = PROD_EPS_PA_MC_P1*(PROD_2A - PROD_2B - PROD_2C) + B_SUM_INIT_PS_INFINITY = GAMMA_C*PROD_A*(PROD1+PROD2) + return + endif + end function B_SUM_INIT_PS_INFINITY + ! + !---------------------------------------------------------------------- + ! Calculation of the derivative of the polynomial P(X) + ! ---------------------------------------------------- + ! testing power series convergence + ! -------------------------------- + ! P(X) = |z(a+X)(b+X)|^2 - |(c+X)(X+1)|^2 + ! = \sum_{i=0}^{4} c[i] X^{i}, for |z| < 1. + ! It is positive when the power series term modulus increases + ! and negative when it decreases, + ! so that its derivative provides information on its convergence + ! (see Comp. Phys. Comm. paper). + ! Its derivative components cv_poly_der_tab[i] = (i+1) c[i+1] + ! for i in [0:3] + ! so that P'(X) = \sum_{i=0}^{3} cv_poly_der_tab[i] X^{i} + ! are calculated. + ! + ! Variables: + ! ---------- + ! a,b,c,z: a,b,c and z parameters and arguments + ! of the 2F1(a,b,c,z) function. + ! cv_poly_der_tab[3]: table of four doubles + ! containing the P'(X) components. + ! mod_a2,mod_b2,mod_c2,mod_z2,R_a,Re_b,Re_c: |a|^2, |b|^2, |c|^2, + ! |z|^2, Re(a), Re(b), Re(c), with which P(X) can be expressed. + !---------------------------------------------------------------------- + subroutine CV_POLY_DER_TAB_CALC(A,B,C,Z,CV_POLY_DER_TAB) + !-------------------------------------------------------------------- + implicit none + complex(rkind),intent(in) :: A,B,C,Z + real(rkind),intent(out) :: CV_POLY_DER_TAB(0:3) + real(rkind) :: MOD_A2,MOD_B2,MOD_C2,MOD_Z2 + real(rkind) :: RE_A,RE_B,RE_C,IM_A,IM_B,IM_C,RE_Z,IM_Z + ! + RE_A=REAL(A,rkind); IM_A=AIMAG(A); MOD_A2=RE_A*RE_A+IM_A*IM_A + RE_B=REAL(B,rkind); IM_B=AIMAG(B); MOD_B2=RE_B*RE_B+IM_B*IM_B + RE_C=REAL(C,rkind); IM_C=AIMAG(C); MOD_C2=RE_C*RE_C+IM_C*IM_C + RE_Z=REAL(Z,rkind); IM_Z=AIMAG(Z); MOD_Z2=RE_Z*RE_Z+IM_Z*IM_Z + CV_POLY_DER_TAB(0)=TWO*((RE_A*MOD_B2+RE_B*MOD_A2)*MOD_Z2-RE_C-MOD_C2) + CV_POLY_DER_TAB(1)=TWO*((MOD_A2+MOD_B2+4._rkind*RE_A*RE_B)*MOD_Z2 & + -ONE-4._rkind*RE_C-MOD_C2) + CV_POLY_DER_TAB(2)=6._rkind*((RE_A+RE_B)*MOD_Z2-RE_C-ONE) + CV_POLY_DER_TAB(3)=4._rkind*(MOD_Z2-ONE) + end subroutine CV_POLY_DER_TAB_CALC + ! + !---------------------------------------------------------------------- + ! Calculation of the derivative of the polynomial P(X) + ! ---------------------------------------------------- + ! testing power series convergence at one x value + ! ----------------------------------------------- + ! P'(x) is calculated for a real x. + ! See P'(X) components calculation routine for definitions. + !---------------------------------------------------------------------- + function CV_POLY_DER_CALC(CV_POLY_DER_TAB,X) + !-------------------------------------------------------------------- + implicit none + real(rkind),intent(in) :: X + real(rkind),intent(in) :: CV_POLY_DER_TAB(0:3) + real(rkind) :: CV_POLY_DER_CALC + ! + CV_POLY_DER_CALC=CV_POLY_DER_TAB(0)+X*(CV_POLY_DER_TAB(1) & + +X*(CV_POLY_DER_TAB(2)+X*CV_POLY_DER_TAB(3))) + return + end function CV_POLY_DER_CALC + ! + !---------------------------------------------------------------------- + ! Calculation of an integer after which false convergence cannot occur + ! -------------------------------------------------------------------- + ! See cv_poly_der_tab_calc routine for definitions. + ! If P'(x) < 0 and P''(x) < 0 for x > xc, it will be so for all x > xc + ! as P(x) -> -oo for x -> +oo + ! and P(x) can have at most one maximum for x > xc. + ! It means that the 2F1 power series term modulus will increase + ! or decrease to 0 for n > nc, + ! with nc the smallest positive integer larger than xc. + ! + ! If P'(X) = C0 + C1.X + C2.X^2 + C3.X^3, + ! the discriminant of P''(X) is Delta = C2^2 - 3 C1 C3. + ! + ! If Delta > 0, P''(X) has two different real roots + ! and its largest root is -(C2 + sqrt(Delta))/(3 C3), + ! because C3 = 4(|z|^2 - 1) < 0. + ! One can take xc = -(C2 + sqrt(Delta))/(3 C3) + ! and one returns its associated nc integer. + ! + ! If Delta <= 0, P''(X) has at most one real root, + ! so that P'(X) has only one root and then P(X) only one maximum. + ! In this case, one can choose xc = nc = 0, which is returned. + ! + ! Variables + ! --------- + ! cv_poly_der_tab: table of four doubles + ! containing the P'(X) coefficients + ! C1,C2,three_C3: cv_poly_der_tab[1], cv_poly_der_tab[2] + ! and 3.0*cv_poly_der_tab[3], so that P''(X) = C1 + 2.C2.x + three_C3.x^2 + ! Delta: discriminant of P''(X), equal to C2^2 - 3 C1 C3. + ! largest_root: if Delta > 0, + ! P''(X) largest real root equal to -(C2 + sqrt(Delta))/(3 C3). + !---------------------------------------------------------------------- + function MIN_N_CALC(CV_POLY_DER_TAB) + !-------------------------------------------------------------------- + implicit none + real(rkind),intent(in) :: CV_POLY_DER_TAB(0:3) + integer(i4b) :: MIN_N_CALC + real(rkind) :: C1,C2,THREE_C3,DELTA,LARGEST_ROOT + ! + C1=CV_POLY_DER_TAB(1); C2=CV_POLY_DER_TAB(2) + THREE_C3=3._rkind*CV_POLY_DER_TAB(3); DELTA = C2*C2 - THREE_C3*C1 + if(DELTA.le.ZERO) then + MIN_N_CALC = 0 + return + else + LARGEST_ROOT = -(C2 + SQRT (DELTA))/THREE_C3 + MIN_N_CALC = MAX(CEILING(LARGEST_ROOT),0) + return + endif + end function MIN_N_CALC + ! + !---------------------------------------------------------------------- + ! Calculation of the 2F1 power series converging for |z| < 1 + ! ---------------------------------------------------------- + ! One has 2F1(a,b,c,z) + ! = \sum_{n = 0}^{+oo} (a)_n (b)_n / ((c)_n n!) z^n, + ! so that 2F1(a,b,c,z) = \sum_{n = 0}^{+oo} t[n] z^n, + ! with t[0] = 1 and t[n+1] = (a+n)(b+n)/((c+n)(n+1)) t[n] for n >= 0. + ! If a or b are negative integers, + ! F(z) is a polynomial of degree -a or -b, evaluated directly. + ! If not, one uses the test of convergence |t[n] z^n|oo < 1E-15 + ! to truncate the series after it was checked + ! that false convergence cannot occur. + ! Variables: + ! ---------- + ! a,b,c,z: a,b,c and z parameters and arguments + ! of the 2F1(a,b,c,z) function. One must have here |z| < 1. + ! term,sum: term of the 2F1 power series equal to t[n] z^n, + ! truncated sum at given n of the 2F1 power series. + ! na,nb: absolute values of the closest integers to Re(a) and Re(b). + ! a = -na or b = -nb means one is in the polynomial case. + ! cv_poly_der_tab: coefficients of the derivative + ! of the polynomial P(X) = |z(a+X)(b+X)|^2 - |(c+X)(X+1)|^2 + ! min_n: smallest integer after which false convergence cannot occur. + ! It is calculated in min_n_calc. + ! possible_false_cv: always true if n < min_n. + ! If n >= min_n, it is true if P'(n) > 0. + ! If n >= min_n and P'(n) < 0, + ! it becomes false and remains as such for the rest of the calculation. + ! One can then check if |t[n] z^n|oo < 1E-15 to truncate the series. + !---------------------------------------------------------------------- + function HYP_PS_ZERO(A,B,C,Z) + !-------------------------------------------------------------------- + implicit none + complex(rkind),intent(in) :: A,B,C,Z + integer(i4b) :: N,NA,NB,MIN_N,MIN_N_CALC + complex(rkind) :: HYP_PS_ZERO,TERM + logical(lgt) :: POSSIBLE_FALSE_CV + real(rkind) :: CV_POLY_DER_TAB(0:3) + real(rkind) :: CV_POLY_DER_CALC + ! + NA = ABS(NINT(REAL(A,rkind))) + NB = ABS(NINT(REAL(B,rkind))) + TERM=ONE; HYP_PS_ZERO=ONE + if(A.eq.(-NA)) then + do N=0,NA-1 + TERM = TERM*Z*(A+N)*(B+N)/((N+ONE)*(C+N)) + HYP_PS_ZERO = HYP_PS_ZERO + TERM + enddo + return + else if(B.eq.(-NB)) then + do N=0,NB-1 + TERM = TERM*Z*(A+N)*(B+N)/((N+ONE)*(C+N)) + HYP_PS_ZERO = HYP_PS_ZERO + TERM + enddo + return + else + call CV_POLY_DER_TAB_CALC(A,B,C,Z,CV_POLY_DER_TAB) + POSSIBLE_FALSE_CV=.TRUE. + MIN_N=MIN_N_CALC(CV_POLY_DER_TAB);N=0 + do while(POSSIBLE_FALSE_CV.or.(INF_NORM(TERM).gt.EPS15)) + TERM = TERM*Z*(A+N)*(B+N)/((N+ONE)*(C+N)) + HYP_PS_ZERO = HYP_PS_ZERO + TERM + if(POSSIBLE_FALSE_CV.and.(N.gt.MIN_N)) then + POSSIBLE_FALSE_CV = & + (CV_POLY_DER_CALC (CV_POLY_DER_TAB,DBLE(N)).gt.ZERO) + endif + N=N+1 + enddo + return + endif + end function HYP_PS_ZERO + ! + !---------------------------------------------------------------------- + ! Calculation of the 2F1 power series + ! ----------------------------------- + ! converging with the 1-z transformation + ! -------------------------------------- + ! The formula for F(z) in the 1-z transformation holds: + ! F(z) = (-1)^m (pi.eps)/sin (pi.eps) [A(z) + B(z)] + ! for eps not equal to zero, F(z) = (-1)^m [A(z) + B(z)] for eps = 0 + ! where m = |Re(c-a-b)], eps = c-a-b-m, + ! A(z) = \sum_{n=0}^{m-1} alpha[n] (1-z)^n, + ! B(z) = \sum_{n=0}^{+oo} beta[n] (1-z)^n, and: + ! + ! alpha[0] = [Gamma_inv(1-m-eps)/eps] Gamma_inv(a+m+eps) + ! x Gamma_inv(b+m+eps) Gamma(c) + ! [Gamma_inv(1-m-eps)/eps] is calculated in A_sum_init. + ! alpha[0] is calculated with log[Gamma] + ! if the previous expression might overflow, + ! and its imaginary part removed if a, b and c are real. + ! alpha[n+1] = (a+n)(b+n)/[(n+1)(1-m-eps+n)] alpha[n], n in [0:m-2]. + ! + ! beta[0] is defined in B_sum_init_PS_one function comments. + ! gamma[0] = Gamma(c) (a)_m (b)_m (1-z)^m Gamma_inv(a+m+eps) + ! x Gamma_inv(b+m+eps) Gamma_inv(m+1) Gamma_inv(1-eps) + ! + ! beta[n+1] = (a+m+n+eps)(b+m+n+eps)/[(m+n+1+eps)(n+1)] beta[n] + ! + [(a+m+n)(b+m+n)/(m+n+1) - (a+m+n) - (b+m+n) - eps + ! + (a+m+n+eps)(b+m+n+eps)/(n+1)] + ! x gamma[n]/[(n+m+1+eps)(n+1+eps)], n >= 0. + ! gamma[n+1] = (a+m+n)(b+m+n)/[(m+n+1)(n+1-eps)] gamma[n], n >= 0. + ! + ! B(z) converges <=> |1-z| < 1 + ! The test of convergence is |beta[n] (1-z)^n|oo < 1E-15 |beta[0]|oo + ! for n large enough so that false convergence cannot occur. + ! + ! Variables + ! --------- + ! a,b,c,one_minus_z: a,b,c parameters + ! and 1-z from z argument of 2F1(a,b,c,z) + ! m,phase,m_p1,eps,eps_pm,eps_pm_p1, + ! a_pm,b_pm,one_meps,one_meps_pm: + ! |Re(c-a-b)], (-1)^m, m+1, c-a-b-m, + ! eps+m, eps+m+1, a+m, b+m, 1-eps, 1-eps-m + ! eps_pa,eps_pb,eps_pa_pm,eps_pb_pm,Pi_eps,Gamma_c: + ! eps+a, eps+b, eps+a+m, eps+b+m, pi.eps, Gamma(c) + ! Gamma_inv_eps_pa_pm,Gamma_inv_eps_pb_pm,Gamma_prod: + ! Gamma_inv(eps+a+m), Gamma_inv(eps+b+m), + ! Gamma(c).Gamma_inv(eps+a+m).Gamma_inv(eps+b+m) + ! Gamma_inv_one_meps,A_first_term,A_sum,A_term: + ! Gamma_inv(1-eps), alpha[0], A(z), alpha[n] (1-z)^n + ! pow_mzp1_m,B_first_term,prod_B,ratio: (1-z)^m, beta[0], + ! (a)_m (b)_m (1-z)^m, (a+n)(b+n)/(n+1) for n in [0:m-2]. + ! B_extra_term,B_term,B_sum,B_prec: + ! gamma[n], beta[n] (1-z)^n, B(z), 1E-15 |beta[0|oo + ! cv_poly1_der_tab,cv_poly2_der_tab: P1'(X) and P2'(X) coefficients + ! of the potentials derivatives of P1(X) and P2(X) + ! defined in cv_poly_der_tab_calc with parameters + ! a1 = a, b1 = b, c1 = 1-m-eps, z1 = 1-z + ! and a2 = eps+b+m, b2 = eps+a+m,c2 = eps+m+1, z2 = 1-z. + ! min_n: smallest integer after which false convergence cannot occur. + ! It is calculated in min_n_calc with both P1'(X) and P2'(X), + ! so one takes the largest integer coming from both calculations. + ! possible_false_cv: always true if n < min_n. + ! If n >= min_n, it is true if P1'(n) > 0 or P2'(n) > 0. + ! If n >= min_n and P1'(n) < 0 and P2'(n) < 0, + ! it becomes false and remains as such for the rest of the calculation. + ! One can then check if |beta[n] z^n|oo < 1E-15 to truncate the series. + ! n,n_pm_p1,n_p1,a_pm_pn,b_pm_pn,eps_pm_p1_pn,n_p1_meps,eps_pa_pm_pn, + ! eps_pb_pm_pn,eps_pm_pn: index of power series, n+m+1, n+1, + ! a+m+n, b+m+n, eps+m+n+1, n+1-eps, eps+a+m+n, eps+b+m+n, eps+m+n, + ! prod1,prod2,prod3: (eps+a+m+n)(eps+b+m+n), + ! (eps+m+1+n)(n+1), (a+m+n)(b+m+n) + !---------------------------------------------------------------------- + function HYP_PS_ONE(A,B,C,MZP1) + !-------------------------------------------------------------------- + implicit none + complex(rkind),intent(in) :: A,B,C,MZP1 + integer(i4b) :: N,M,PHASE,M_M2,MIN_N,MIN_N_CALC,M_P1 + real(rkind) :: B_PREC,N_P1,N_PM_P1,CV_POLY_DER_CALC + complex(rkind) :: HYP_PS_ONE,EPS,EPS_PM,EPS_PM_P1,A_PM + complex(rkind) :: B_PM,ONE_MEPS_MM,EPS_PA,EPS_PB,PI_EPS,GAMMA_PROD + complex(rkind) :: EPS_PA_PM,EPS_PB_PM,GAMMA_INV,B_SUM_INIT_PS_ONE + complex(rkind) :: A_SUM_INIT,LOG_A_SUM_INIT,A_SUM,A_TERM,ONE_MEPS + complex(rkind) :: B_EXTRA_TERM,B_TERM,B_SUM,GAMMA_C,LOG_GAMMA_FUN,RATIO + complex(rkind) :: A_PM_PN,B_PM_PN,EPS_PM_P1_PN,N_P1_MEPS + complex(rkind) :: PROD1,PROD2,PROD3 + complex(rkind) :: EPS_PA_PM_PN,EPS_PB_PM_PN,EPS_PM_PN,PROD_B,POW_MZP1_M + complex(rkind) :: GAMMA_INV_EPS_PA_PM,GAMMA_INV_EPS_PB_PM + complex(rkind) :: GAMMA_INV_ONE_MEPS + logical(lgt) :: POSSIBLE_FALSE_CV + real(rkind) :: CV_POLY1_DER_TAB(0:3),CV_POLY2_DER_TAB(0:3) + ! + M=NINT(REAL(C-A-B,rkind)); M_M2=M-2; M_P1=M+1 + if(MOD(M,2).eq.0) then + PHASE=1 + else + PHASE=-1 + endif + EPS=C-A-B-M; EPS_PM=EPS+M; EPS_PM_P1=EPS_PM+ONE; A_PM=A+M;B_PM=B+M + ONE_MEPS=ONE-EPS; ONE_MEPS_MM=ONE_MEPS-M; EPS_PA=EPS+A; EPS_PB=EPS+B + PI_EPS=M_PI*EPS; EPS_PA_PM=EPS_PA+M; EPS_PB_PM=EPS_PB+M + GAMMA_C=ONE/GAMMA_INV(C) + GAMMA_INV_EPS_PA_PM=GAMMA_INV(EPS_PA_PM) + GAMMA_INV_EPS_PB_PM=GAMMA_INV(EPS_PB_PM) + GAMMA_PROD=GAMMA_C*GAMMA_INV_EPS_PA_PM*GAMMA_INV_EPS_PB_PM + GAMMA_INV_ONE_MEPS=GAMMA_INV(ONE_MEPS) + if(M.eq.0) then + A_TERM=ZERO + else if(INF_NORM(ONE_MEPS_MM & + *(LOG(ONE + ABS(ONE_MEPS_MM))-ONE)).lt.300.0d0) then + A_TERM=GAMMA_PROD*A_SUM_INIT(M,EPS,GAMMA_INV_ONE_MEPS) + else + A_TERM=EXP(LOG_GAMMA_FUN(C)-LOG_GAMMA_FUN(EPS_PA_PM)& + -LOG_GAMMA_FUN(EPS_PB_PM)+LOG_A_SUM_INIT(M,EPS)) + if((AIMAG(A).eq.ZERO).and.(AIMAG(B).eq.ZERO)& + .and.(AIMAG(C).eq.ZERO)) then + A_TERM=REAL(A_TERM,rkind) + endif + endif + A_SUM=A_TERM + POW_MZP1_M = MZP1**M + B_TERM=B_SUM_INIT_PS_ONE(A,B,GAMMA_C,GAMMA_INV_ONE_MEPS, & + GAMMA_INV_EPS_PA_PM,GAMMA_INV_EPS_PB_PM,MZP1,M,EPS)*POW_MZP1_M + PROD_B=POW_MZP1_M + do N=0,M_M2 + RATIO=(A+N)*(B+N)/(N+ONE) + A_TERM=A_TERM*MZP1*RATIO/(N+ONE_MEPS_MM) + A_SUM=A_SUM+A_TERM + PROD_B = PROD_B*RATIO + enddo + if(M.gt.0) then + PROD_B = PROD_B*(A+M-ONE)*(B+M-ONE)/DBLE(M) + endif + B_EXTRA_TERM = PROD_B*GAMMA_PROD*GAMMA_INV_ONE_MEPS; B_SUM=B_TERM + B_PREC=EPS15*INF_NORM(B_TERM) + call CV_POLY_DER_TAB_CALC(A,B,ONE_MEPS_MM,MZP1,CV_POLY1_DER_TAB) + call CV_POLY_DER_TAB_CALC(EPS_PB_PM,EPS_PA_PM,EPS_PM_P1,MZP1, & + CV_POLY2_DER_TAB) + MIN_N=MAX(MIN_N_CALC(CV_POLY1_DER_TAB),MIN_N_CALC(CV_POLY2_DER_TAB)) + POSSIBLE_FALSE_CV=.TRUE.; N=0 + do while(POSSIBLE_FALSE_CV.or.(INF_NORM(B_TERM).gt.B_PREC)) + N_PM_P1=N+M_P1; N_P1=N+ONE; A_PM_PN=A_PM+N; B_PM_PN=B_PM+N + EPS_PM_P1_PN=EPS_PM_P1+N; N_P1_MEPS=ONE_MEPS+N + EPS_PM_PN=EPS_PM+N; EPS_PA_PM_PN=EPS_PA_PM+N + EPS_PB_PM_PN=EPS_PB_PM+N + PROD1=EPS_PA_PM_PN*EPS_PB_PM_PN + PROD2=EPS_PM_P1_PN*N_P1 + PROD3=A_PM_PN*B_PM_PN + B_TERM = MZP1*(B_TERM*PROD1/PROD2+B_EXTRA_TERM*(PROD3/N_PM_P1 & + -A_PM_PN-B_PM_PN-EPS+PROD1/N_P1)/(EPS_PM_P1_PN*N_P1_MEPS)) + B_SUM=B_SUM+B_TERM + B_EXTRA_TERM=B_EXTRA_TERM*MZP1*PROD3/(N_PM_P1*N_P1_MEPS) + if(POSSIBLE_FALSE_CV.and.(N.gt.MIN_N)) then + POSSIBLE_FALSE_CV = & + (CV_POLY_DER_CALC(CV_POLY1_DER_TAB,DBLE(N)).gt.ZERO).or. & + (CV_POLY_DER_CALC(CV_POLY2_DER_TAB,DBLE(N)).gt.ZERO) + endif + N=N+1 + enddo + if(EPS.eq.ZERO) then + HYP_PS_ONE=PHASE*(A_SUM+B_SUM) + return + else + HYP_PS_ONE=PHASE*(A_SUM+B_SUM)*PI_EPS/SIN(PI_EPS) + return + endif + end function HYP_PS_ONE + ! + !---------------------------------------------------------------------- + ! Calculation of the 2F1 power series + ! ----------------------------------- + ! converging with the 1/z transformation + ! -------------------------------------- + ! The formula for F(z) in the 1/z transformation holds: + ! F(z) = (-1)^m (pi.eps)/sin (pi.eps) [A(z) + B(z)] + ! for eps not equal to zero, + ! F(z) = (-1)^m [A(z) + B(z)] for eps = 0 + ! where m = |Re(b-a)], eps = b-a-m, + ! A(z) = \sum_{n=0}^{m-1} alpha[n] z^{-n}, + ! B(z) = \sum_{n=0}^{+oo} beta[n] z^{-n}, and: + ! + ! alpha[0] = [Gamma_inv(1-m-eps)/eps] Gamma_inv(c-a) + ! x Gamma_inv(a+m+eps) Gamma(c) + ! [Gamma_inv(1-m-eps)/eps] is calculated in A_sum_init. + ! alpha[0] is calculated with log[Gamma] + ! if the previous expression might overflow, + ! and its imaginary part removed if a, b and c are real. + ! alpha[n+1] = (a+n)(1-c+a+n)/[(n+1)(1-m-eps+n)] alpha[n], + ! n in [0:m-2]. + ! + ! beta[0] is defined in B_sum_init_PS_infinity function comments. + ! gamma[0] = Gamma(c) (a)_m (1-c+a)_m z^{-m} Gamma_inv(a+m+eps) + ! x Gamma_inv(c-a) Gamma_inv(m+1) Gamma_inv(1-eps) + ! + ! beta[n+1] = (a+m+n+eps)(1-c+a+m+n+eps)/[(m+n+1+eps)(n+1)] beta[n] + ! + [(a+m+n)(1-c+a+m+n)/(m+n+1) - (a+m+n) - (1-c+a+m+n) + ! - eps + (a+m+n+eps)(1-c+a+m+n+eps)/(n+1)] + ! x gamma[n]/[(n+m+1+eps)(n+1+eps)], n >= 0. + ! gamma[n+1] = (a+m+n)(b+m+n)/[(m+n+1)(n+1-eps)] gamma[n], n >= 0. + ! + ! B(z) converges <=> |z| > 1 + ! The test of convergence is |beta[n] z^{-n}|oo < 1E-15 |beta[0]|oo + ! for n large enough so that false convergence cannot occur. + ! + ! Variables + ! --------- + ! a,b,c,z: a,b,c parameters and z argument of 2F1(a,b,c,z) + ! m,phase,m_p1,eps,a_mc_p1,one_meps, + ! one_meps_pm,a_pm,a_mc_p1_pm,cma: |Re(b-a)], (-1)^m, m+1, b-a-m, + ! 1-c+a, 1-eps, 1-eps-m, a+m, 1-c+a+m, c-a + ! eps_pa,eps_pm_p1,eps_pa_mc_p1_pm,Pi_eps,eps_pa_pm,eps_pm,Gamma_c: + ! eps+a, eps+m+1, eps+1-c+a+m, pi.eps, eps+a+m, eps+m, Gamma(c) + ! Gamma_inv_eps_pa_pm,Gamma_inv_cma,z_inv,pow_mz_ma, + ! Gamma_inv_one_meps,Gamma_prod: Gamma_inv(eps+a+m), Gamma_inv(c-a), + ! 1/z, (-z)^(-a), Gamma_inv(1-eps), + ! Gamma(c) Gamma_inv(c-a) Gamma_inv(eps+a+m) + ! A_first_term,A_sum,A_term: alpha[0], A(z), alpha[n] z^{-n} + ! pow_z_inv_m,B_first_term,prod_B,ratio: z^{-m}, beta[0], + ! (a)_m (1-c+a)_m z^{-m}, (a+n)(1-c+a+n)/(n+1) for n in [0:m-2]. + ! B_extra_term,B_term,B_sum,B_prec: + ! gamma[n], beta[n] z^{-n}, B(z), 1E-15 |beta[0|oo + ! cv_poly1_der_tab,cv_poly2_der_tab: P1'(X) and P2'(X) coefficients + ! of the potentials derivatives of P1(X) and P2(X) + ! defined in cv_poly_der_tab_calc + ! with parameters a1 = a, b1 = 1-c+a, c1 = 1-m-eps, z1 = 1/z + ! and a2 = b, b2 = eps+1-c+a+m,c2 = eps+m+1, z2 = 1/z. + ! min_n: smallest integer after which false convergence cannot occur. + ! It is calculated in min_n_calc with both P1'(X) and P2'(X), + ! so one takes the largest integer coming from both calculations. + ! possible_false_cv: always true if n < min_n. If n >= min_n, + ! it is true if P1'(n) > 0 or P2'(n) > 0. + ! If n >= min_n and P1'(n) < 0 and P2'(n) < 0, + ! it becomes false and remains as such for the rest of the calculation. + ! One can then check if |beta[n] z^n|oo < 1E-15 to truncate the series. + ! n,n_pm_p1,n_p1,a_pm_pn,a_mc_p1_pm_pn,eps_pm_p1_pn,n_p1_meps, + ! eps_pa_pm_pn,eps_pa_mc_p1_pm_pn,eps_pm_pn: + ! index of power series, n+m+1, n+1, a+m+n, 1-c+a+m+n, eps+m+n+1, + ! n+1-eps, eps+a+m+n, eps+1-c+a+m+n, eps+m+n, + ! prod1,prod2,prod3: (eps+a+m+n)(eps+1-c+a+m+n), + ! (eps+m+1+n)(n+1), (a+m+n)(1-c+a+m+n) + !---------------------------------------------------------------------- + function HYP_PS_INFINITY(A,B,C,Z) + !-------------------------------------------------------------------- + implicit none + complex(rkind),intent(in) :: A,B,C,Z + integer(i4b) :: N,M,PHASE,M_M2,MIN_N,MIN_N_CALC,M_P1 + real(rkind) :: B_PREC,N_P1,N_PM_P1,CV_POLY_DER_CALC + complex(rkind) :: B_SUM_INIT_PS_INFINITY,LOG_GAMMA_FUN,POW_Z_INV_M + complex(rkind) :: HYP_PS_INFINITY,Z_INV,GAMMA_INV,RATIO + complex(rkind) :: EPS,A_MC_P1,ONE_MEPS,ONE_MEPS_MM,A_PM,A_MC_P1_PM + complex(rkind) :: CMA,EPS_PA,EPS_PM_P1,EPS_PA_MC_P1_PM,PI_EPS + complex(rkind) :: EPS_PA_PM,EPS_PM,GAMMA_C,GAMMA_INV_CMA,POW_MZ_MA + complex(rkind) :: A_SUM_INIT,LOG_A_SUM_INIT,A_SUM,A_TERM + complex(rkind) :: GAMMA_INV_EPS_PA_PM,GAMMA_INV_ONE_MEPS + complex(rkind) :: PROD_B,B_EXTRA_TERM,B_TERM,B_SUM,PROD1 + complex(rkind) :: A_PM_PN,A_MC_P1_PM_PN,EPS_PM_P1_PN,N_P1_MEPS + complex(rkind) :: PROD2,PROD3,GAMMA_PROD + complex(rkind) :: EPS_PA_PM_PN,EPS_PA_MC_P1_PM_PN,EPS_PM_PN + logical(lgt) :: POSSIBLE_FALSE_CV + real(rkind) :: CV_POLY1_DER_TAB(0:3),CV_POLY2_DER_TAB(0:3) + ! + M=NINT(REAL(B-A,rkind)); M_M2=M-2;M_P1=M+1 + if(MOD(M,2).eq.0) then + PHASE=1 + else + PHASE=-1 + endif + EPS=B-A-M; A_MC_P1=ONE-C+A; ONE_MEPS=ONE-EPS; ONE_MEPS_MM=ONE_MEPS-M + A_PM=A+M; A_MC_P1_PM=A_MC_P1+M; CMA=C-A; EPS_PA=EPS+A + EPS_PM=EPS+M; EPS_PM_P1=EPS_PM+ONE; EPS_PA_MC_P1_PM=EPS+A_MC_P1_PM + PI_EPS=M_PI*EPS; EPS_PA_PM=EPS_PA+M + GAMMA_C=ONE/GAMMA_INV(C); GAMMA_INV_EPS_PA_PM = GAMMA_INV(EPS_PA_PM) + GAMMA_INV_ONE_MEPS = GAMMA_INV(ONE_MEPS) + GAMMA_INV_CMA=GAMMA_INV(CMA); Z_INV=ONE/Z;POW_MZ_MA=(-Z)**(-A) + GAMMA_PROD=GAMMA_C*GAMMA_INV_CMA*GAMMA_INV_EPS_PA_PM + if(M.eq.0) then + A_TERM=ZERO + else if(INF_NORM(ONE_MEPS_MM & + *(LOG(ONE + ABS(ONE_MEPS_MM))-ONE)).lt.300._rkind) then + A_TERM=GAMMA_PROD*A_SUM_INIT(M,EPS,GAMMA_INV_ONE_MEPS) + else + A_TERM=EXP(LOG_GAMMA_FUN(C)-LOG_GAMMA_FUN(CMA)-LOG_GAMMA_FUN(B) & + + LOG_A_SUM_INIT(M,EPS)) + if((AIMAG(A).eq.ZERO).and.(AIMAG(B).eq.ZERO).and. & + (AIMAG(C).eq.ZERO)) then + A_TERM=REAL(A_TERM,rkind) + endif + endif + A_SUM=A_TERM + POW_Z_INV_M=Z_INV**M + B_TERM=B_SUM_INIT_PS_INFINITY(A,C,GAMMA_C,GAMMA_INV_CMA, & + GAMMA_INV_ONE_MEPS,GAMMA_INV_EPS_PA_PM,Z,M,EPS)*POW_Z_INV_M + PROD_B=POW_Z_INV_M + do N=0,M_M2 + RATIO=(A+N)*(A_MC_P1+N)/(N+ONE) + A_TERM = A_TERM*Z_INV*RATIO/(N+ONE_MEPS_MM) + A_SUM = A_SUM+A_TERM + PROD_B = PROD_B*RATIO + enddo + if (M.gt.0) then + PROD_B=PROD_B*(A+M-ONE)*(A_MC_P1+M-ONE)/DBLE(M) + endif + B_EXTRA_TERM = PROD_B*GAMMA_PROD*GAMMA_INV_ONE_MEPS + B_SUM=B_TERM + B_PREC=EPS15*INF_NORM(B_TERM) + call CV_POLY_DER_TAB_CALC(A,A_MC_P1,ONE_MEPS_MM,Z_INV, & + CV_POLY1_DER_TAB) + call CV_POLY_DER_TAB_CALC(B,EPS_PA_MC_P1_PM,EPS_PM_P1, & + Z_INV,CV_POLY2_DER_TAB) + MIN_N=MAX(MIN_N_CALC(CV_POLY1_DER_TAB),MIN_N_CALC(CV_POLY2_DER_TAB)) + POSSIBLE_FALSE_CV=.TRUE.; N=0 + do while(POSSIBLE_FALSE_CV.or.(INF_NORM(B_TERM).gt.B_PREC)) + N_PM_P1=N+M_P1; N_P1=N+ONE; A_PM_PN=A_PM+N + A_MC_P1_PM_PN=A_MC_P1_PM+N; EPS_PM_P1_PN=EPS_PM_P1+N + N_P1_MEPS=N_P1-EPS; EPS_PA_PM_PN=EPS_PA_PM+N + EPS_PA_MC_P1_PM_PN=EPS_PA_MC_P1_PM+N; EPS_PM_PN=EPS_PM+N + PROD1=EPS_PA_PM_PN*EPS_PA_MC_P1_PM_PN; PROD2=EPS_PM_P1_PN*N_P1 + PROD3=A_PM_PN*A_MC_P1_PM_PN + B_TERM = Z_INV*(B_TERM*PROD1/PROD2+B_EXTRA_TERM*(PROD3/N_PM_P1 & + -A_PM_PN-A_MC_P1_PM_PN-EPS+PROD1/N_P1) & + /(EPS_PM_P1_PN*N_P1_MEPS)) + B_SUM=B_SUM+B_TERM + B_EXTRA_TERM=B_EXTRA_TERM*Z_INV*PROD3/(N_PM_P1*N_P1_MEPS) + if(POSSIBLE_FALSE_CV.and.(N.gt.MIN_N)) then + POSSIBLE_FALSE_CV = (CV_POLY_DER_CALC( & + CV_POLY1_DER_TAB,DBLE(N)).gt.ZERO).or.(& + CV_POLY_DER_CALC(CV_POLY2_DER_TAB,DBLE(N)).gt.ZERO) + endif + N=N+1 + enddo + if(EPS.eq.ZERO) then + HYP_PS_INFINITY=PHASE*POW_MZ_MA*(A_SUM+B_SUM) + return + else + HYP_PS_INFINITY=PHASE*POW_MZ_MA*(A_SUM+B_SUM)*PI_EPS & + /SIN(PI_EPS) + return + endif + end function HYP_PS_INFINITY + ! + !---------------------------------------------------------------------- + ! Calculation of F(z) in transformation theory missing zones + ! ---------------------------------------------------------- + ! of the complex plane with a Taylor series + ! ----------------------------------------- + ! If z is close to exp(+/- i.pi/3), no transformation in 1-z, z, + ! z/(z-1) or combination of them can transform z in a complex number + ! of modulus smaller than a given Rmax < 1 . + ! Rmax is a radius for which one considers power series summation + ! for |z| > Rmax is too slow to be processed. One takes Rmax = 0.9 . + ! Nevertheless, for Rmax = 0.9, + ! these zones are small enough to be handled + ! with a Taylor series expansion around a point z0 close to z + ! where transformation theory can be used to calculate F(z). + ! One then chooses z0 to be 0.9 z/|z| if |z| < 1, and 1.1 z/|z| + ! if |z| > 1, + ! so that hyp_PS_zero or hyp_PS_infinity can be used + ! (see comments of these functions above). + ! For this z0, F(z) = \sum_{n=0}^{+oo} q[n] (z-z0)^n, with: + ! q[0] = F(z0), q[1] = F'(z0) = (a b/c) 2F1(a+1,b+1,c+1,z0) + ! q[n+2] = [q[n+1] (n (2 z0 - 1) - c + (a+b+c+1) z0) + ! + q[n] (a+n)(b+n)/(n+1)]/(z0(1-z0)(n+2)) + ! As |z-z0| < 0.1, it converges with around 15 terms, + ! so that no instability can occur for moderate a, b and c. + ! Convergence is tested + ! with |q[n] (z-z0)^n|oo + |q[n+1] (z-z0)^{n+1}|oo. + ! Series is truncated when this test is smaller + ! than 1E-15 (|q[0]|oo + |q[1] (z-z0)|oo). + ! No false convergence can happen here + ! as q[n] behaves smoothly for n -> +oo. + ! + ! Variables + ! --------- + ! a,b,c,z: a,b,c parameters and z argument of 2F1(a,b,c,z) + ! abs_z,is_abs_z_small: |z|, true if |z| < 1 and false if not. + ! z0,zc_z0_ratio,z0_term1,z0_term2: 0.9 z/|z| if |z| < 1, + ! and 1.1 z/|z| if |z| > 1, (z-z0)/(z0 (1-z0)), + ! 2 z0 - 1, c - (a+b+c+1) z0 + ! hyp_PS_z0,dhyp_PS_z0,prec: F(z0), F'(z0) calculated with 2F1 + ! as F'(z0) = (a b/c) 2F1(a+1,b+1,c+1,z0), + ! precision demanded for series truncation + ! equal to 1E-15 (|q[0]|oo + |q[1] (z-z0)|oo). + ! n,an,anp1,anp2,sum: index of the series, q[n] (z-z0)^n, + ! q[n+1] (z-z0)^{n+1}, q[n+2] (z-z0)^{n+2}, + ! truncated sum of the power series. + !---------------------------------------------------------------------- + function HYP_PS_COMPLEX_PLANE_REST(A,B,C,Z) + !-------------------------------------------------------------------- + implicit none + complex(rkind),intent(in) :: A,B,C,Z + integer(i4b) :: N + real(rkind) :: ABS_Z,PREC + complex(rkind) :: HYP_PS_COMPLEX_PLANE_REST + complex(rkind) :: Z0,ZC,ZC_Z0_RATIO,Z0_TERM1,Z0_TERM2 + complex(rkind) :: HYP_PS_Z0,DHYP_PS_Z0,AN,ANP1,ANP2 + complex(rkind) :: HYP_PS_ZERO,HYP_PS_INFINITY + ! + ABS_Z=ABS(Z) + if(ABS_Z.lt.ONE) then + Z0=0.9_rkind*Z/ABS_Z; ZC=Z-Z0; ZC_Z0_RATIO=ZC/(Z0*(ONE-Z0)) + Z0_TERM1=TWO*Z0 - ONE; Z0_TERM2=C-(A+B+ONE)*Z0 + HYP_PS_Z0=HYP_PS_ZERO(A,B,C,Z0) + DHYP_PS_Z0=HYP_PS_ZERO(A+ONE,B+ONE,C+ONE,Z0)*A*B/C + else + Z0=1.1_rkind*Z/ABS_Z; ZC=Z-Z0; ZC_Z0_RATIO=ZC/(Z0*(ONE-Z0)) + Z0_TERM1=TWO*Z0 - ONE; Z0_TERM2=C-(A+B+ONE)*Z0 + HYP_PS_Z0=HYP_PS_INFINITY(A,B,C,Z0) + DHYP_PS_Z0=HYP_PS_INFINITY(A+ONE,B+ONE,C+ONE,Z0)*A*B/C + endif + AN=HYP_PS_Z0;ANP1=ZC*DHYP_PS_Z0;HYP_PS_COMPLEX_PLANE_REST=AN+ANP1 + PREC=EPS15*(INF_NORM(AN)+INF_NORM(ANP1)); N=0 + do while(INF_NORM(AN).gt.PREC) + ANP2=ZC_Z0_RATIO*(ANP1*(N*Z0_TERM1-Z0_TERM2)+AN*ZC*(A+N)*(B+N) & + /(N+ONE))/(N+TWO) + HYP_PS_COMPLEX_PLANE_REST = HYP_PS_COMPLEX_PLANE_REST + ANP2 + N=N+1 + AN=ANP1 + ANP1=ANP2 + enddo + return + end function HYP_PS_COMPLEX_PLANE_REST + ! + !---------------------------------------------------------------------- + ! Calculation of F(z) for arbitrary z using previous routines + ! ----------------------------------------------------------- + ! Firstly, it is checked if a,b and c are negative integers. + ! If neither a nor b is negative integer but c is, + ! F(z) is undefined so that the program stops with an error message. + ! If a and c are negative integers with c < a, + ! or b and c are negative integers with b < a, + ! or c is not negative integer integer but a or b is, + ! one is in the polynomial case. + ! In this case, if |z| < |z/(z-1)| or z = 1, + ! hyp_PS_zero is used directly, as then |z| <= 2 + ! and no instability arises with hyp_PS_zero + ! as long the degree of the polynomial is small (<= 10 typically). + ! If not, one uses the transformation + ! F(z) = (1-z)^{-a} 2F1(a,c-b,c,z/(z-1)) if a is negative integer + ! or F(z) = (1-z)^{-b} 2F1(b,c-a,c,z/(z-1)) if b is negative integer + ! along with hyp_PS_zero. + ! Indeed, 2F1(a,c-b,c,X) is a polynomial if a is negative integer, + ! and so is 2F1(b,c-a,c,X) if b is negative integer, + ! so that one has here |z/(z-1)| <= 2 + ! and the stability of the method is the same + ! as for the |z| < |z/(z-1)| case. + ! If one is in the non-polynomial case, one checks if z >= 1. + ! If it is, one is the cut of F(z) + ! so that z is replaced by z - 10^{-307}i. + ! Then, using F(z) = 2F1(b,a,c,z) + ! and F(z) = (1-z)^{c-a-b} 2F1(c-a,c-b,c,z), + ! one replaces a,b,c parameters by combinations of them + ! so that Re(b-a) >= 0 and Re(c-a-b) >= 0. + ! Exchanging a and b does not change convergence properties, + ! while having Re(c-a-b) >= 0 accelerates it + ! (In hyp_PS_zero, t[n] z^n ~ z^n/(n^{c-a-b}) for n -> +oo). + ! If |1-z| < 1E-5, one uses hyp_PS_one + ! as the vicinity of the singular point z = 1 is treated properly. + ! After that, one compares |z| and |z/(z-1)| + ! to R in {0.5,0.6,0.7,0.8,0.9}. + ! If one of them is smaller than R, + ! one uses hyp_PS_zero without transformation + ! or with the transformation F(z) = (1-z)^{-a} 2F1(a,c-b,c,z/(z-1)). + ! Then, if both of them are larger than 0.9, + ! one compares |1/z|, |(z-1)/z|, |1-z| and |1/(1-z)| + ! to R in {0.5,0.6,0.7,0.8,0.9}. + ! If one of them is found smaller than R, + ! with the condition that |c-b|oo < 5 for (z-1)/z transformation, + ! |a,b,c|oo < 5 for |1-z| transformation + ! and |a,c-b,c|oo < 5 for |1/(1-z)| transformation, + ! the corresponding transformation is used. + ! If none of them was smaller than 0.9, + ! one is in the missing zones of transformation theory + ! so that the Taylor series of hyp_PS_complex_plane_rest is used. + ! + ! Variables + ! --------- + ! a,b,c,z: a,b,c parameters and z argument of 2F1(a,b,c,z) + ! Re_a,Re_b,Re_c,na,nb,nc,is_a_neg_int,is_b_neg_int,is_c_neg_int: + ! real parts of a,b,c, closest integers to a,b,c, + ! true if a,b,c is negative integers and false if not. + ! zm1,z_over_zm1,z_shift: z-1, z/(z-1), z - 10^{-307}i in case z >= 1. + ! ab_condition, cab_condition: true if Re(b-a) >= 0 and false if not, + ! true if Re(c-a-b) >= 0 and false if not. + ! abs_zm1,abz_z,abs_z_inv,abs_z_over_zm1,abs_zm1_inv,abs_zm1_over_z: + ! |z-1|, |z|, |1/z|, |z/(z-1)|, |1/(z-1)|, |(z-1)/z| + ! are_ac_small: true if |a|oo < 5 and |c|oo < 5, false if not. + ! is_cmb_small: true if |c-b|oo < 5, false if not. + ! are_abc_small: true if |a|oo < 5, |b|oo < 5 and |c|oo < 5, + ! false if not. + ! are_a_cmb_c_small: true if |a|oo < 5, |c-b|oo < 5 and |c|oo < 5, + ! false if not. + ! R_tab,R: table of radii {0.5,0.6,0.7,0.8,0.9}, one of these radii. + ! res: returned result + !---------------------------------------------------------------------- + recursive function HYP_2F1(A,B,C,Z) result(RES) + !-------------------------------------------------------------------- + implicit none + complex(rkind),intent(in) :: A,B,C,Z + integer(i4b) :: NA,NB,NC,I + real(rkind) :: RE_A,RE_B,RE_C,ABS_Z,ABS_ZM1,ABS_Z_OVER_ZM1 + real(rkind) :: ABS_ZM1_OVER_Z,ABS_ZM1_INV,R_TABLE(1:5),R,ABS_Z_INV + complex(rkind) :: RES,HYP_PS_INFINITY,HYP_PS_ZERO,Z_SHIFT + complex(rkind) :: HYP_PS_COMPLEX_PLANE_REST,HYP_PS_ONE,Z_OVER_ZM1,ZM1 + logical(lgt) :: IS_A_NEG_INT,IS_B_NEG_INT,IS_C_NEG_INT + logical(lgt) :: AB_CONDITION,CAB_CONDITION,ARE_A_CMB_C_SMALL + logical(lgt) :: IS_CMB_SMALL,ARE_AC_SMALL,ARE_ABC_SMALL + ! + RE_A=REAL(A,rkind); RE_B=REAL(B,rkind); RE_C=REAL(C,rkind); + NA=NINT(RE_A); NB=NINT(RE_B); NC=NINT(RE_C); + IS_A_NEG_INT=A.eq.NA.and.NA.le.0 + IS_B_NEG_INT=B.eq.NB.and.NB.le.0 + IS_C_NEG_INT=C.eq.NC.and.NC.le.0 + ZM1=Z-ONE + if(IS_C_NEG_INT) then + ABS_Z=ABS(Z); Z_OVER_ZM1 = Z/ZM1 + ABS_Z_OVER_ZM1=ABS(Z_OVER_ZM1) + if(IS_A_NEG_INT.and.(NC.lt.NA)) then + if((Z.eq.ONE).or.(ABS_Z.lt.ABS_Z_OVER_ZM1)) then + RES=HYP_PS_ZERO(A,B,C,Z) + return + else + RES=((-ZM1)**(-A))*HYP_PS_ZERO(A,C-B,C,Z_OVER_ZM1) + return + endif + else if(IS_B_NEG_INT.and.(NC.lt.NB)) then + if((Z.eq.ONE).or.(ABS_Z.lt.ABS_Z_OVER_ZM1)) then + RES=HYP_PS_ZERO(A,B,C,Z) + return + else + RES=((-ZM1)**(-B))*HYP_PS_ZERO(B,C-A,C,Z_OVER_ZM1) + return + endif + else + print*,'2F1 UNDEFINED' + endif + endif + if(IS_A_NEG_INT) then + ABS_Z=ABS(Z); Z_OVER_ZM1 = Z/ZM1 + ABS_Z_OVER_ZM1=ABS(Z_OVER_ZM1) + if((Z.eq.ONE).or.(ABS_Z.lt.ABS_Z_OVER_ZM1)) then + RES=HYP_PS_ZERO(A,B,C,Z) + return + else + RES=((-ZM1)**(-A))*HYP_PS_ZERO(A,C-B,C,Z_OVER_ZM1) + return + endif + else if(IS_B_NEG_INT) then + ABS_Z=ABS(Z); Z_OVER_ZM1 = Z/ZM1 + ABS_Z_OVER_ZM1=ABS(Z_OVER_ZM1) + if((Z.eq.ONE).or.(ABS_Z.lt.ABS_Z_OVER_ZM1)) then + RES=HYP_PS_ZERO(A,B,C,Z) + return + else + RES=((-ZM1)**(-B))*HYP_PS_ZERO(B,C-A,C,Z_OVER_ZM1) + return + endif + endif + if((REAL(Z,rkind).ge.ONE).and.(AIMAG(Z).eq.ZERO)) then + Z_SHIFT=CMPLX(REAL(Z,rkind),-1.e-307_rkind,rkind) + RES=HYP_2F1(A,B,C,Z_SHIFT) + return + endif + AB_CONDITION = (RE_B.ge.RE_A) + CAB_CONDITION = (RE_C.ge.RE_A + RE_B) + if ((.NOT.AB_CONDITION).or.(.NOT.CAB_CONDITION)) then + if ((.NOT.AB_CONDITION).and.(CAB_CONDITION)) then + RES=HYP_2F1(B,A,C,Z) + return + else if((.NOT.CAB_CONDITION).and.(AB_CONDITION)) then + RES=((-ZM1)**(C-A-B))*HYP_2F1(C-B,C-A,C,Z) + return + else + RES=((-ZM1)**(C-A-B))*HYP_2F1(C-A,C-B,C,Z) + return + endif + endif + ABS_ZM1=ABS(ZM1) + if(ABS_ZM1.lt.1.e-5_rkind) then + RES=HYP_PS_ONE (A,B,C,-ZM1) + return + endif + ABS_Z=ABS(Z); ABS_Z_OVER_ZM1=ABS_Z/ABS_ZM1; ABS_Z_INV=ONE/ABS_Z + ABS_ZM1_OVER_Z=ONE/ABS_Z_OVER_ZM1; ABS_ZM1_INV=ONE/ABS_ZM1 + IS_CMB_SMALL = INF_NORM(C-B).lt.5._rkind; + ARE_AC_SMALL = (INF_NORM(A).lt.5._rkind).and.(INF_NORM(C).lt.5._rkind) + ARE_ABC_SMALL = ARE_AC_SMALL.and.(INF_NORM(B).lt.5._rkind) + ARE_A_CMB_C_SMALL = ARE_AC_SMALL.and.IS_CMB_SMALL + R_TABLE=(/0.5_rkind,0.6_rkind,0.7_rkind,0.8_rkind,0.9_rkind/) + do I=1,5 + R=R_TABLE(I) + if(ABS_Z.le.R) then + RES=HYP_PS_ZERO (A,B,C,Z) + return + endif + if(IS_CMB_SMALL.and.(ABS_Z_OVER_ZM1.le.R)) then + RES=((-ZM1)**(-A))*HYP_PS_ZERO (A,C-B,C,Z/ZM1) + return + endif + enddo + do I=1,5 + R=R_TABLE(I) + if(ABS_Z_INV.le.R) then + RES=HYP_PS_INFINITY (A,B,C,Z) + return + endif + if(IS_CMB_SMALL.and.(ABS_ZM1_OVER_Z.le.R)) then + RES=((-ZM1)**(-A))*HYP_PS_INFINITY (A,C-B,C,Z/ZM1) + return + endif + if(ARE_ABC_SMALL.and.(ABS_ZM1.le.R)) then + RES=HYP_PS_ONE (A,B,C,-ZM1) + return + endif + if(ARE_A_CMB_C_SMALL.and.(ABS_ZM1_INV.le.R)) then + RES=((-ZM1)**(-A))*HYP_PS_ONE (A,C-B,C,-ONE/ZM1) + return + endif + enddo + RES=HYP_PS_COMPLEX_PLANE_REST (A,B,C,Z) + return + end function HYP_2F1 + ! + !---------------------------------------------------------------------- + ! Test of 2F1 numerical accuracy + ! ------------------------------ + ! using hypergeometric differential equation + ! ------------------------------------------ + ! If z = 0, F(z) = 1 so that this value is trivially tested. + ! To test otherwise if the value of F(z) is accurate, + ! one uses the fact that + ! z(z-1) F''(z) + (c - (a+b+1) z) F'(z) - a b F(z) = 0. + ! If z is not equal to one, a relative precision test is provided + ! by |F''(z) + [(c - (a+b+1) z) F'(z) - a b F(z)]/[z(z-1)]|oo + ! /(|F(z)|oo + F'(z)|oo + |F''(z)|oo). + ! If z is equal to one, one uses |(c - (a+b+1)) F'(z) - a b F(z)|oo + ! /(|F(z)|oo + F'(z)|oo + 1E-307). + ! F'(z) and F''(z) are calculated using equalities + ! F'(z) = (a b/c) 2F1(a+1,b+1,c+1,z) + ! and F'(z) = ((a+1)(b+1)/(c+1)) (a b/c) 2F1(a+2,b+2,c+2,z). + ! + ! Variables + ! --------- + ! a,b,c,z: a,b,c parameters and z argument of 2F1(a,b,c,z) + ! F,dF,d2F: F(z), F'(z) and F''(z) calculated with hyp_2F1 + ! using F'(z) = (a b/c) 2F1(a+1,b+1,c+1,z) + ! and F'(z) = ((a+1)(b+1)/(c+1)) (a b/c) 2F1(a+2,b+2,c+2,z). + !---------------------------------------------------------------------- + function TEST_2F1(A,B,C,Z,F) + !-------------------------------------------------------------------- + implicit none + complex(rkind),intent(in) :: A,B,C,Z + real(rkind) :: TEST_2F1 + complex(rkind) :: F,DF,D2F,HYP_2F1 + ! + if(Z.eq.ZERO) then + TEST_2F1=INF_NORM(F-ONE) + return + else if(Z.eq.ONE) then + DF = HYP_2F1(A+ONE,B+ONE,C+ONE,Z)*A*B/C + TEST_2F1=INF_NORM((C-(A+B+ONE))*DF-A*B*F) & + /(INF_NORM (F)+INF_NORM(DF)+1.e-307_rkind) + return + else + DF = HYP_2F1(A+ONE,B+ONE,C+ONE,Z)*A*B/C + D2F = HYP_2F1(A+TWO,B+TWO,C+TWO,Z)*A*(A+ONE)*B*(B+ONE) & + /(C*(C+ONE)) + TEST_2F1=INF_NORM(D2F+((C-(A+B+ONE)*Z)*DF-A*B*F)/(Z*(ONE-Z))) & + /(INF_NORM(F)+INF_NORM(DF)+INF_NORM(D2F)) + return + endif + end function TEST_2F1 + !============== END HYP_2F1 FILE ====================================== + +end module hyp_2F1_module diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index 496ba2013..8e905a142 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -64,6 +64,7 @@ module t2enthalpy_module public::T2E_lookup public::t2enthalpy public::t2enthalpy_addphase +private::hyp_2F1_real ! define the look-up table used to compute temperature based on enthalpy contains @@ -460,17 +461,17 @@ subroutine t2enthalpy(& mLayerPsiLiq = xConst*(mLayerTempTrial(iLayer) - Tfreeze) ! liquid water matric potential from the Clapeyron eqution arg = (vGn_alpha * mLayerPsiLiq)**vGn_n if(quick_hyper)then - gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) else - gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) endif enthTemp = (iden_water * Cp_water - iden_ice * Cp_ice) * diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) ! calculate enthalpy at the critical temperature arg = (vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n if(quick_hyper)then - gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) else - gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) endif enthTcrit = (iden_water * Cp_water - iden_ice * Cp_ice) * diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) endif @@ -622,37 +623,24 @@ subroutine t2enthalpy_addphase(& end subroutine t2enthalpy_addphase -! ************************************************************************************************************************ -! private function hypergeometric: compute Gaussian hypergeometric function -! ************************************************************************************************************************ -function hypergeometric(a, b, c, z) +!---------------------------------------------------------------------- +! private function: compute hypergeometric function with real arguments into real result +!---------------------------------------------------------------------- +function hyp_2F1_real(a_real, b_real, c_real, z_real) + !-------------------------------------------------------------------- + USE hyp_2F1_module ! use for hypergeometric function implicit none - real(rkind),intent(in) :: a, b, c, z ! input parameters - real(rkind) :: term, factorial ! local variables - real(rkind) :: hypergeometric ! output result - integer(i4b) :: n, max_iter ! iteration count variables - - max_iter = 1000 ! maximum number of iterations - - ! initialize - hypergeometric = 1._rkind - term = 1._rkind - factorial = 1._rkind - - do n = 1, max_iter - factorial = factorial * n - term = term * (a + n - 1) * (b + n - 1) / ((c + n - 1) * factorial) * z - hypergeometric = hypergeometric + term - - if (abs(term) < 1.e-6_rkind) exit ! convergence condition - - if (n == max_iter) then - ! handle non-convergence - write(*, *) "Warning: Hypergeometric function did not converge within the maximum number of iterations." - exit - end if - end do - -end function hypergeometric - -end module t2enthalpy_module + real(rkind),intent(in) :: a_real, b_real, c_real, z_real + complex(rkind) :: a_complex, b_complex, c_complex, z_complex, result + real(rkind) :: hyp_2F1_real + + a_complex = CMPLX(a_real, 0._rkind, rkind) + b_complex = CMPLX(b_real, 0._rkind, rkind) + c_complex = CMPLX(c_real, 0._rkind, rkind) + z_complex = CMPLX(z_real, 0._rkind, rkind) + result = HYP_2F1(a_complex, b_complex, c_complex, z_complex) + hyp_2F1_real = REAL(result, rkind) + +end function hyp_2F1_real + +end module t2enthalpy_module \ No newline at end of file From fc8224e9a23780af99e4a1856b6a4d57076ac4bc Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 11 Jan 2024 14:16:08 +0900 Subject: [PATCH 1049/1472] new hypergeometric function compiles --- build/source/engine/hyp_2F1.f90 | 114 ++++++++++++++--------------- build/source/engine/t2enthalpy.f90 | 2 +- 2 files changed, 55 insertions(+), 61 deletions(-) diff --git a/build/source/engine/hyp_2F1.f90 b/build/source/engine/hyp_2F1.f90 index 617da8d86..33382a634 100644 --- a/build/source/engine/hyp_2F1.f90 +++ b/build/source/engine/hyp_2F1.f90 @@ -6,25 +6,25 @@ module hyp_2F1_module ! privacy implicit none -public::INF_NORM -public::TANZ -public::LOG1P -public::EXPM1 -public::GAMMA_INV -public::GAMMA_RATIO_DIFF_SMALL_EPS -public::GAMMA_INV_DIFF_EPS -public::A_SUM_INIT -public::LOG_A_SUM_INIT -public::B_SUM_INIT_PS_ONE -public::B_SUM_INIT_PS_INFINITY -public::CV_POLY_DER_CALC -public::MIN_N_CALC -public::HYP_PS_ZERO -public::HYP_PS_ONE -public::HYP_PS_INFINITY -public::HYP_PS_COMPLEX_PLANE_REST -public::HYP_2F1 +private::INF_NORM +private::TANZ +private::LOG1P +private::EXPM1 +private::GAMMA_INV +private::GAMMA_RATIO_DIFF_SMALL_EPS +private::GAMMA_INV_DIFF_EPS +private::A_SUM_INIT +private::LOG_A_SUM_INIT +private::B_SUM_INIT_PS_ONE +private::B_SUM_INIT_PS_INFINITY +private::CV_POLY_DER_CALC +private::MIN_N_CALC +private::HYP_PS_ZERO +private::HYP_PS_ONE +private::HYP_PS_INFINITY +private::HYP_PS_COMPLEX_PLANE_REST private::TEST_2F1 +public::HYP_2F1 ! constant parameters real(rkind),parameter :: EPS15=1.e-15_rkind @@ -51,7 +51,7 @@ module hyp_2F1_module ! - renamed LOG_GAMMA to LOG_GAMMA_FUN to avoid name ! clash with intrinsic function ! 11/01/2024 Modifications by Ashley Van Beusekom: - ! - made one module + ! - made one module and removed functions as variable declarations ! - made precision rkind dependent ! - lowercase for readability !---------------------------------------------------------------------- @@ -268,10 +268,10 @@ recursive function GAMMA_INV(Z) result(RES) X=REAL(Z,rkind); N=NINT(X) EPS=Z-N if(MOD(N,2).eq.0) then - RES=SIN(M_PI*EPS)*M_1_PI/GAMMA_INV (ONE-Z) + RES=SIN(M_PI*EPS)*M_1_PI/GAMMA_INV(ONE-Z) return else - RES=-SIN(M_PI*EPS)*M_1_PI/GAMMA_INV (ONE-Z) + RES=-SIN(M_PI*EPS)*M_1_PI/GAMMA_INV(ONE-Z) return endif endif @@ -490,8 +490,7 @@ function GAMMA_INV_DIFF_EPS(Z,EPS) real(rkind) :: X,EPS_PX,FACT real(rkind) :: Z_NEG_INT_DISTANCE real(rkind) :: EPS_PZ_NEG_INT_DISTANCE - complex(rkind) :: GAMMA_INV_DIFF_EPS,EPS_PZ,GAMMA_INV - complex(rkind) :: GAMMA_RATIO_DIFF_SMALL_EPS + complex(rkind) :: GAMMA_INV_DIFF_EPS,EPS_PZ logical(lgt) :: IS_Z_NEG_INT,IS_EPS_PZ_NEG_INT EPS_PZ=Z+EPS; X=REAL(Z,rkind); EPS_PX=REAL(EPS_PZ,rkind) @@ -499,25 +498,25 @@ function GAMMA_INV_DIFF_EPS(Z,EPS) IS_Z_NEG_INT=(Z.eq.N).and.(N.le.0) IS_EPS_PZ_NEG_INT=(EPS_PZ.eq.M).and.(M.le.0) if(INF_NORM(EPS).gt.0.10_rkind) then - GAMMA_INV_DIFF_EPS = (GAMMA_INV (Z) - GAMMA_INV (EPS_PZ))/EPS + GAMMA_INV_DIFF_EPS = (GAMMA_INV(Z) - GAMMA_INV(EPS_PZ))/EPS return else if(EPS_PZ.ne.Z) then if(IS_Z_NEG_INT) then - GAMMA_INV_DIFF_EPS = (-GAMMA_INV (EPS_PZ)/EPS) + GAMMA_INV_DIFF_EPS = (-GAMMA_INV(EPS_PZ)/EPS) return else if(IS_EPS_PZ_NEG_INT) then - GAMMA_INV_DIFF_EPS = (GAMMA_INV (Z)/EPS) + GAMMA_INV_DIFF_EPS = (GAMMA_INV(Z)/EPS) return else Z_NEG_INT_DISTANCE = INF_NORM (Z + ABS (N)) EPS_PZ_NEG_INT_DISTANCE = INF_NORM (EPS_PZ + ABS (M)) if(Z_NEG_INT_DISTANCE.lt.EPS_PZ_NEG_INT_DISTANCE) then GAMMA_INV_DIFF_EPS= & - GAMMA_RATIO_DIFF_SMALL_EPS (Z,EPS)*GAMMA_INV (EPS_PZ) + GAMMA_RATIO_DIFF_SMALL_EPS(Z,EPS)*GAMMA_INV(EPS_PZ) return else GAMMA_INV_DIFF_EPS= & - GAMMA_RATIO_DIFF_SMALL_EPS (EPS_PZ,-EPS)*GAMMA_INV (Z) + GAMMA_RATIO_DIFF_SMALL_EPS(EPS_PZ,-EPS)*GAMMA_INV(Z) return endif endif @@ -531,7 +530,7 @@ function GAMMA_INV_DIFF_EPS(Z,EPS) return else GAMMA_INV_DIFF_EPS = & - GAMMA_RATIO_DIFF_SMALL_EPS (Z,EPS)*GAMMA_INV (EPS_PZ) + GAMMA_RATIO_DIFF_SMALL_EPS(Z,EPS)*GAMMA_INV(EPS_PZ) return endif end function GAMMA_INV_DIFF_EPS @@ -630,7 +629,7 @@ function LOG_A_SUM_INIT(M,EPS) complex(rkind),intent(in) :: EPS integer(i4b) :: N real(rkind) :: LOG_FACT - complex(rkind) :: ONE_MEPS_MM,LOG_A_SUM_INIT,LOG_GAMMA_FUN + complex(rkind) :: ONE_MEPS_MM,LOG_A_SUM_INIT ! ONE_MEPS_MM=ONE-EPS-M if(ONE_MEPS_MM.ne.1-M) then @@ -751,8 +750,8 @@ function B_SUM_INIT_PS_ONE(A,B,GAMMA_C,GAMMA_INV_ONE_MEPS, & complex(rkind) :: A_PM,B_SUM_INIT_PS_ONE,PI_EPS,GAMMA_INV_ONE_MEPS_MM complex(rkind) :: B_PM,TMP1,TMP2 complex(rkind) :: Z_TERM,PROD1,PROD2,PROD3,ONE_MEPS,PI_EPS_PM - complex(rkind) :: GAMMA_INV_A_PM,PROD_AB,GAMMA_INV,GAMMA_INV_B_PM - complex(rkind) :: GAMMA_INV_DIFF_EPS,GAMMA_INV_EPS_PM_P1 + complex(rkind) :: GAMMA_INV_A_PM,PROD_AB,GAMMA_INV_B_PM + complex(rkind) :: GAMMA_INV_EPS_PM_P1 ! INF_NORM_EPS=INF_NORM(EPS); M_M1=M-1; A_PM=A+M; B_PM=B+M ONE_MEPS=ONE-EPS; PI_EPS=M_PI*EPS; PI_EPS_PM = M_PI*(EPS+M) @@ -796,7 +795,7 @@ function B_SUM_INIT_PS_ONE(A,B,GAMMA_C,GAMMA_INV_ONE_MEPS, & +GAMMA_INV_DIFF_EPS(TMP2,EPS)) PROD2 = GAMMA_INV_EPS_PM_P1 & *(GAMMA_INV_EPS_PB_PM*GAMMA_INV_DIFF_EPS(A_PM,EPS) & - +GAMMA_INV_A_PM*GAMMA_INV_DIFF_EPS (B_PM,EPS)) + +GAMMA_INV_A_PM*GAMMA_INV_DIFF_EPS(B_PM,EPS)) PROD3 = GAMMA_INV_A_PM*GAMMA_INV_B_PM*GAMMA_INV_EPS_PM_P1*Z_TERM B_SUM_INIT_PS_ONE=GAMMA_C*PROD_AB*(PROD1-PROD2-PROD3) return @@ -920,14 +919,14 @@ function B_SUM_INIT_PS_INFINITY(A,C,GAMMA_C,GAMMA_INV_CMA, & integer(i4b) :: M_M1,I,N,N0,PHASE logical(lgt) :: IS_N0_HERE,IS_EPS_NON_ZERO real(rkind) :: INF_NORM_EPS,NP1,GAMMA_INV_MP1 - complex(rkind) :: B_SUM_INIT_PS_INFINITY,GAMMA_INV,TMP1 + complex(rkind) :: B_SUM_INIT_PS_INFINITY,TMP1 complex(rkind) :: CMA,A_MC_P1,A_MC_P1_PM,CMA_MEPS,EPS_PA_MC_P1,A_PM complex(rkind) :: GAMMA_INV_EPS_PM_P1,GAMMA_INV_CMA_MEPS,PI_EPS complex(rkind) :: PROD1,PROD2,A_PN,A_MC_P1_PN,ONE_MEPS complex(rkind) :: PROD_A,PROD_A_MC_P1,PROD_EPS_PA_MC_P1_N0,PI_EPS_PM complex(rkind) :: PROD_EPS_PA_MC_P1,SUM_N0,Z_TERM,SUM_TERM complex(rkind) :: PROD_DIFF_EPS,GAMMA_INV_A_PM,GAMMA_PROD1 - complex(rkind) :: PROD_2A,PROD_2B,PROD_2C,GAMMA_INV_DIFF_EPS + complex(rkind) :: PROD_2A,PROD_2B,PROD_2C complex(rkind) :: EPS_PA_MC_P1_PN,GAMMA_INV_ONE_MEPS_MM ! INF_NORM_EPS=INF_NORM(EPS); CMA=C-A; A_MC_P1=A-C+ONE @@ -1166,11 +1165,10 @@ function HYP_PS_ZERO(A,B,C,Z) !-------------------------------------------------------------------- implicit none complex(rkind),intent(in) :: A,B,C,Z - integer(i4b) :: N,NA,NB,MIN_N,MIN_N_CALC + integer(i4b) :: N,NA,NB,MIN_N complex(rkind) :: HYP_PS_ZERO,TERM logical(lgt) :: POSSIBLE_FALSE_CV real(rkind) :: CV_POLY_DER_TAB(0:3) - real(rkind) :: CV_POLY_DER_CALC ! NA = ABS(NINT(REAL(A,rkind))) NB = ABS(NINT(REAL(B,rkind))) @@ -1196,7 +1194,7 @@ function HYP_PS_ZERO(A,B,C,Z) HYP_PS_ZERO = HYP_PS_ZERO + TERM if(POSSIBLE_FALSE_CV.and.(N.gt.MIN_N)) then POSSIBLE_FALSE_CV = & - (CV_POLY_DER_CALC (CV_POLY_DER_TAB,DBLE(N)).gt.ZERO) + (CV_POLY_DER_CALC(CV_POLY_DER_TAB,DBLE(N)).gt.ZERO) endif N=N+1 enddo @@ -1280,13 +1278,12 @@ function HYP_PS_ONE(A,B,C,MZP1) !-------------------------------------------------------------------- implicit none complex(rkind),intent(in) :: A,B,C,MZP1 - integer(i4b) :: N,M,PHASE,M_M2,MIN_N,MIN_N_CALC,M_P1 - real(rkind) :: B_PREC,N_P1,N_PM_P1,CV_POLY_DER_CALC + integer(i4b) :: N,M,PHASE,M_M2,MIN_N,M_P1 + real(rkind) :: B_PREC,N_P1,N_PM_P1 complex(rkind) :: HYP_PS_ONE,EPS,EPS_PM,EPS_PM_P1,A_PM complex(rkind) :: B_PM,ONE_MEPS_MM,EPS_PA,EPS_PB,PI_EPS,GAMMA_PROD - complex(rkind) :: EPS_PA_PM,EPS_PB_PM,GAMMA_INV,B_SUM_INIT_PS_ONE - complex(rkind) :: A_SUM_INIT,LOG_A_SUM_INIT,A_SUM,A_TERM,ONE_MEPS - complex(rkind) :: B_EXTRA_TERM,B_TERM,B_SUM,GAMMA_C,LOG_GAMMA_FUN,RATIO + complex(rkind) :: EPS_PA_PM,EPS_PB_PM, A_SUM,A_TERM,ONE_MEPS + complex(rkind) :: B_EXTRA_TERM,B_TERM,B_SUM,GAMMA_C,RATIO complex(rkind) :: A_PM_PN,B_PM_PN,EPS_PM_P1_PN,N_P1_MEPS complex(rkind) :: PROD1,PROD2,PROD3 complex(rkind) :: EPS_PA_PM_PN,EPS_PB_PM_PN,EPS_PM_PN,PROD_B,POW_MZP1_M @@ -1448,14 +1445,13 @@ function HYP_PS_INFINITY(A,B,C,Z) !-------------------------------------------------------------------- implicit none complex(rkind),intent(in) :: A,B,C,Z - integer(i4b) :: N,M,PHASE,M_M2,MIN_N,MIN_N_CALC,M_P1 - real(rkind) :: B_PREC,N_P1,N_PM_P1,CV_POLY_DER_CALC - complex(rkind) :: B_SUM_INIT_PS_INFINITY,LOG_GAMMA_FUN,POW_Z_INV_M - complex(rkind) :: HYP_PS_INFINITY,Z_INV,GAMMA_INV,RATIO + integer(i4b) :: N,M,PHASE,M_M2,MIN_N,M_P1 + real(rkind) :: B_PREC,N_P1,N_PM_P1 + complex(rkind) :: POW_Z_INV_M,HYP_PS_INFINITY,Z_INV,RATIO complex(rkind) :: EPS,A_MC_P1,ONE_MEPS,ONE_MEPS_MM,A_PM,A_MC_P1_PM complex(rkind) :: CMA,EPS_PA,EPS_PM_P1,EPS_PA_MC_P1_PM,PI_EPS complex(rkind) :: EPS_PA_PM,EPS_PM,GAMMA_C,GAMMA_INV_CMA,POW_MZ_MA - complex(rkind) :: A_SUM_INIT,LOG_A_SUM_INIT,A_SUM,A_TERM + complex(rkind) :: A_SUM,A_TERM complex(rkind) :: GAMMA_INV_EPS_PA_PM,GAMMA_INV_ONE_MEPS complex(rkind) :: PROD_B,B_EXTRA_TERM,B_TERM,B_SUM,PROD1 complex(rkind) :: A_PM_PN,A_MC_P1_PM_PN,EPS_PM_P1_PN,N_P1_MEPS @@ -1598,7 +1594,6 @@ function HYP_PS_COMPLEX_PLANE_REST(A,B,C,Z) complex(rkind) :: HYP_PS_COMPLEX_PLANE_REST complex(rkind) :: Z0,ZC,ZC_Z0_RATIO,Z0_TERM1,Z0_TERM2 complex(rkind) :: HYP_PS_Z0,DHYP_PS_Z0,AN,ANP1,ANP2 - complex(rkind) :: HYP_PS_ZERO,HYP_PS_INFINITY ! ABS_Z=ABS(Z) if(ABS_Z.lt.ONE) then @@ -1704,8 +1699,7 @@ recursive function HYP_2F1(A,B,C,Z) result(RES) integer(i4b) :: NA,NB,NC,I real(rkind) :: RE_A,RE_B,RE_C,ABS_Z,ABS_ZM1,ABS_Z_OVER_ZM1 real(rkind) :: ABS_ZM1_OVER_Z,ABS_ZM1_INV,R_TABLE(1:5),R,ABS_Z_INV - complex(rkind) :: RES,HYP_PS_INFINITY,HYP_PS_ZERO,Z_SHIFT - complex(rkind) :: HYP_PS_COMPLEX_PLANE_REST,HYP_PS_ONE,Z_OVER_ZM1,ZM1 + complex(rkind) :: RES,Z_SHIFT,Z_OVER_ZM1,ZM1 logical(lgt) :: IS_A_NEG_INT,IS_B_NEG_INT,IS_C_NEG_INT logical(lgt) :: AB_CONDITION,CAB_CONDITION,ARE_A_CMB_C_SMALL logical(lgt) :: IS_CMB_SMALL,ARE_AC_SMALL,ARE_ABC_SMALL @@ -1781,7 +1775,7 @@ recursive function HYP_2F1(A,B,C,Z) result(RES) endif ABS_ZM1=ABS(ZM1) if(ABS_ZM1.lt.1.e-5_rkind) then - RES=HYP_PS_ONE (A,B,C,-ZM1) + RES=HYP_PS_ONE(A,B,C,-ZM1) return endif ABS_Z=ABS(Z); ABS_Z_OVER_ZM1=ABS_Z/ABS_ZM1; ABS_Z_INV=ONE/ABS_Z @@ -1794,34 +1788,34 @@ recursive function HYP_2F1(A,B,C,Z) result(RES) do I=1,5 R=R_TABLE(I) if(ABS_Z.le.R) then - RES=HYP_PS_ZERO (A,B,C,Z) + RES=HYP_PS_ZERO(A,B,C,Z) return endif if(IS_CMB_SMALL.and.(ABS_Z_OVER_ZM1.le.R)) then - RES=((-ZM1)**(-A))*HYP_PS_ZERO (A,C-B,C,Z/ZM1) + RES=((-ZM1)**(-A))*HYP_PS_ZERO(A,C-B,C,Z/ZM1) return endif enddo do I=1,5 R=R_TABLE(I) if(ABS_Z_INV.le.R) then - RES=HYP_PS_INFINITY (A,B,C,Z) + RES=HYP_PS_INFINITY(A,B,C,Z) return endif if(IS_CMB_SMALL.and.(ABS_ZM1_OVER_Z.le.R)) then - RES=((-ZM1)**(-A))*HYP_PS_INFINITY (A,C-B,C,Z/ZM1) + RES=((-ZM1)**(-A))*HYP_PS_INFINITY(A,C-B,C,Z/ZM1) return endif if(ARE_ABC_SMALL.and.(ABS_ZM1.le.R)) then - RES=HYP_PS_ONE (A,B,C,-ZM1) + RES=HYP_PS_ONE(A,B,C,-ZM1) return endif if(ARE_A_CMB_C_SMALL.and.(ABS_ZM1_INV.le.R)) then - RES=((-ZM1)**(-A))*HYP_PS_ONE (A,C-B,C,-ONE/ZM1) + RES=((-ZM1)**(-A))*HYP_PS_ONE(A,C-B,C,-ONE/ZM1) return endif enddo - RES=HYP_PS_COMPLEX_PLANE_REST (A,B,C,Z) + RES=HYP_PS_COMPLEX_PLANE_REST(A,B,C,Z) return end function HYP_2F1 ! @@ -1855,7 +1849,7 @@ function TEST_2F1(A,B,C,Z,F) implicit none complex(rkind),intent(in) :: A,B,C,Z real(rkind) :: TEST_2F1 - complex(rkind) :: F,DF,D2F,HYP_2F1 + complex(rkind) :: F,DF,D2F ! if(Z.eq.ZERO) then TEST_2F1=INF_NORM(F-ONE) diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index 8e905a142..9fd14d83f 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -628,7 +628,7 @@ end subroutine t2enthalpy_addphase !---------------------------------------------------------------------- function hyp_2F1_real(a_real, b_real, c_real, z_real) !-------------------------------------------------------------------- - USE hyp_2F1_module ! use for hypergeometric function + USE hyp_2F1_module,only:HYP_2F1 ! use for hypergeometric function implicit none real(rkind),intent(in) :: a_real, b_real, c_real, z_real complex(rkind) :: a_complex, b_complex, c_complex, z_complex, result From 77d7b4cce5a5cedfbe35fef899dae75ec2fad6ed Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 11 Jan 2024 14:26:41 +0900 Subject: [PATCH 1050/1472] remove quick_hyper, just working on getting something to be bomb-proof --- build/source/engine/t2enthalpy.f90 | 9 --------- 1 file changed, 9 deletions(-) diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index 9fd14d83f..db65471fa 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -322,7 +322,6 @@ subroutine t2enthalpy(& real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) real(rkind) :: enthWater ! enthalpy of total water (J m-3) logical(lgt),parameter :: use_lookup=.true. ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function - logical(lgt),parameter :: quick_hyper=.false. ! flag to use a quick hypergeometric function, currently no difference between quick and slow hypergeometric functions ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures generalVars: associate(& @@ -460,19 +459,11 @@ subroutine t2enthalpy(& xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*(mLayerTempTrial(iLayer) - Tfreeze) ! liquid water matric potential from the Clapeyron eqution arg = (vGn_alpha * mLayerPsiLiq)**vGn_n - if(quick_hyper)then gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - else - gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - endif enthTemp = (iden_water * Cp_water - iden_ice * Cp_ice) * diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) ! calculate enthalpy at the critical temperature arg = (vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n - if(quick_hyper)then - gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - else gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - endif enthTcrit = (iden_water * Cp_water - iden_ice * Cp_ice) * diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) endif From e221c1f2c240f7049932f566885b4565c7cf0897 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 11 Jan 2024 06:21:17 -0600 Subject: [PATCH 1051/1472] Absorbed stateTypeSplitting loop into split_select_loop in opSplittin -- additional refinement in progress. --- build/source/engine/opSplittin.f90 | 117 ++++++++++++++--------------- 1 file changed, 57 insertions(+), 60 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 471ebe54a..cc62673ca 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -156,20 +156,17 @@ module opSplittin_module type(var_flagVec) :: fluxMask ! integer mask defining model fluxes logical(lgt),allocatable :: stateMask(:) ! mask defining desired state variables contains - procedure :: initialize_indices => split_select_initialize_indices ! initialize operator splitting indices + procedure :: initialize_ixCoupling => split_select_initialize_ixCoupling ! initialize operator splitting indices + procedure :: initialize_iStateTypeSplit => split_select_initialize_iStateTypeSplit ! initialize operator splitting indices procedure :: get_stateMask => split_select_compute_stateMask ! compute stateMask and nSubset and load into class object procedure :: get_indices => split_select_load_indices ! load operator splitting indices into class object - procedure :: indices_from_iSplit => split_select_indices_from_iSplit - procedure :: loop_cycle => split_select_cycle - procedure :: loop_exit => split_select_exit - procedure :: advance_coupling => split_select_advance_coupling ! advance coupling loop iterator + procedure :: cycle_coupling => split_select_cycle_coupling + procedure :: exit_coupling => split_select_exit_coupling + procedure :: exit_stateTypeSplitting => split_select_exit_stateTypeSplitting + procedure :: advance_ixCoupling => split_select_advance_ixCoupling ! advance coupling loop iterator + procedure :: advance_iStateTypeSplit => split_select_advance_iStateTypeSplit ! advance coupling loop iterator end type split_select_type -type, public :: split_index_type ! class for operator splitting indices - integer(i4b) :: ixCoupling - contains -end type split_index_type - contains @@ -322,6 +319,7 @@ subroutine opSplittin(& ! loop control logical(lgt) :: exit_coupling,exit_stateTypeSplitting,exit_stateThenDomain,exit_domainSplit,exit_solution,exit_stateSplit logical(lgt) :: cycle_coupling,cycle_stateTypeSplitting,cycle_stateThenDomain,cycle_domainSplit,cycle_solution,cycle_stateSplit + logical(lgt) :: stateTypeSplitting_flag integer(i4b) :: iSplit,nSplit ! debug testing logical(lgt), parameter :: test_flag=.false. ! to create test output @@ -333,21 +331,25 @@ subroutine opSplittin(& type(in_type_varSubstep) :: in_varSubstep; type(io_type_varSubstep) :: io_varSubstep; type(out_type_varSubstep) :: out_varSubstep; ! varSubstep arguments ! ------------------------------------------------------------------------------------------------------------------------- type(split_select_type) :: split_select ! class object for selecting operator splitting methods - type(split_index_type),allocatable :: split_index(:) ! class object for operator splitting indices ! initialize debug flags stop_flag=.false. ! *** Initialize Split Selector Object *** call initialize_split_select - call split_select % initialize_indices + call split_select % initialize_ixCoupling call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs - split_select_loop: do - ixCoupling=split_select % ixCoupling; if (ixCoupling.gt.nCoupling) exit split_select_loop ! exit if all splits are exhausted - - call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs - stateTypeSplitting: do iStateTypeSplit=1,nStateTypeSplit ! state splitting loop - + stateTypeSplitting_flag=.false. ! initialize flag + split_select_loop: do iSplit=1,1000 + if (stateTypeSplitting_flag.eqv..false.) then !! ----------------------- + ixCoupling=split_select % ixCoupling; if (ixCoupling.gt.nCoupling) exit split_select_loop ! exit if all splits are exhausted + call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs + call split_select % initialize_iStateTypeSplit; stateTypeSplitting_flag=.true. + end if !! ------------------------ + if (stateTypeSplitting_flag.eqv..true.) then !! ----------------- + iStateTypeSplit=split_select % iStateTypeSplit; if (iStateTypeSplit.gt.nStateTypeSplit) stateTypeSplitting_flag=.false. + end if + if (stateTypeSplitting_flag.eqv..true.) then !! ----------------- ! first try the state type split, then try the domain split within a given state type call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split stateThenDomain: do ixStateThenDomain=1,1+tryDomainSplit ! 1=state type split; 2=domain split within a given state type @@ -391,7 +393,7 @@ subroutine opSplittin(& call try_other_solution_methods ! if solution failed to converge, try other splitting methods if (cycle_coupling) then - call split_select % loop_cycle('coupling'); cycle split_select_loop ! cycle loops if necessary + call split_select % cycle_coupling; stateTypeSplitting_flag=.false.; cycle split_select_loop ! cycle loops if necessary end if if (cycle_stateThenDomain) cycle stateThenDomain if (cycle_solution) cycle solution @@ -413,28 +415,20 @@ subroutine opSplittin(& end do stateThenDomain ! switch between the state type and domain type splitting call finalize_stateThenDomain ! final steps following the stateThenDomain loop - end do stateTypeSplitting ! state type splitting loop - call finalize_stateTypeSplitting - if (exit_coupling) then - call split_select % loop_exit('coupling'); exit split_select_loop ! success = exit the coupling loop - end if + call split_select % advance_iStateTypeSplit + end if !! end stateTypeSplitting ----------------- + if (stateTypeSplitting_flag.eqv..false.) then + call finalize_stateTypeSplitting + if (exit_coupling) then + call split_select % exit_coupling; exit split_select_loop ! success = exit the coupling loop + end if - call split_select % advance_coupling + call split_select % advance_ixCoupling + end if end do split_select_loop call finalize_coupling ! check variables and fluxes, and apply step halving if needed - contains - - subroutine initialize_split - ! *** Initialize split *** - - call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs - - ! allocate split_index object - allocate(split_index(1:nSplit)) - ! convert iSplit to ixCoupling - split_index(iSplit) % ixCoupling = iSplit - end subroutine initialize_split + contains subroutine initialize_split_select ! *** Initialize split_select class object - currently under development *** @@ -1073,11 +1067,17 @@ end subroutine opSplittin ! ****** Class procedures for split_select_type class ****** -subroutine split_select_advance_coupling(split_select) +subroutine split_select_advance_ixCoupling(split_select) ! *** Advance index for coupling loop *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector split_select % ixCoupling = split_select % ixCoupling + 1 -end subroutine split_select_advance_coupling +end subroutine split_select_advance_ixCoupling + +subroutine split_select_advance_iStateTypeSplit(split_select) + ! *** Advance index for coupling loop *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % iStateTypeSplit = split_select % iStateTypeSplit + 1 +end subroutine split_select_advance_iStateTypeSplit subroutine split_select_load_indices(split_select,ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) ! *** load operator splitting indices for split_select_type class *** @@ -1096,20 +1096,17 @@ subroutine split_select_load_indices(split_select,ixCoupling,ixSolution,ixStateT split_select % iStateSplit = iStateSplit end subroutine split_select_load_indices -subroutine split_select_initialize_indices(split_select) +subroutine split_select_initialize_ixCoupling(split_select) ! *** initialize operator splitting indices for split_select_type class *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector split_select % ixCoupling = 1 -end subroutine split_select_initialize_indices +end subroutine split_select_initialize_ixCoupling -subroutine split_select_indices_from_iSplit(split_select,iSplit) - ! *** Convert iSplit to split_select_type index data components *** +subroutine split_select_initialize_iStateTypeSplit(split_select) + ! *** initialize operator splitting indices for split_select_type class *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector - integer(i4b),intent(in) :: iSplit ! index of split_select_loop - - ! convert iSplit to ixCoupling - split_select % ixCoupling = iSplit -end subroutine split_select_indices_from_iSplit + split_select % iStateTypeSplit = 1 +end subroutine split_select_initialize_iStateTypeSplit subroutine split_select_compute_stateMask(split_select,indx_data,err,cmessage,message,return_flag) ! *** Get the mask for the state subset *** @@ -1142,23 +1139,23 @@ subroutine split_select_compute_stateMask(split_select,indx_data,err,cmessage,me if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! error control end subroutine split_select_compute_stateMask -subroutine split_select_cycle(split_select,loop) +subroutine split_select_cycle_coupling(split_select) ! *** handle cycle statements of the original opSplittin loops *** class(split_select_type),intent(inout) :: split_select - character(*),intent(in) :: loop - if (loop.eq.'coupling') then - split_select % ixCoupling = split_select % ixCoupling+1 - end if -end subroutine split_select_cycle + split_select % ixCoupling = split_select % ixCoupling+1 +end subroutine split_select_cycle_coupling + +subroutine split_select_exit_coupling(split_select) +! *** handle exit statements of the original opSplittin loops *** + class(split_select_type),intent(inout) :: split_select + split_select % ixCoupling = 1 ! reinitialize index +end subroutine split_select_exit_coupling -subroutine split_select_exit(split_select,loop) +subroutine split_select_exit_stateTypeSplitting(split_select) ! *** handle exit statements of the original opSplittin loops *** class(split_select_type),intent(inout) :: split_select - character(*),intent(in) :: loop - if (loop.eq.'coupling') then - split_select % ixCoupling = 1 ! reinitialize index - end if -end subroutine split_select_exit + split_select % iStateTypeSplit = 1 ! reinitialize index +end subroutine split_select_exit_stateTypeSplitting ! ********************************************************************************************************** ! private subroutine stateFilter: get a mask for the desired state variables From 78114b195443a36edd1e3e83a3d45d68999c6534 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 13 Jan 2024 01:19:31 +0900 Subject: [PATCH 1052/1472] hypergeometric function works now, lookup tables still wrong --- build/source/engine/t2enthalpy.f90 | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index db65471fa..f6cc6d27a 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -433,43 +433,41 @@ subroutine t2enthalpy(& Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) volFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) diffT = mLayerTempTrial(iLayer) - Tfreeze - ! *** compute enthalpy of water for unfrozen conditions if(mlayerTempTrial(iLayer)>=Tcrit)then enthWater = iden_water * Cp_water * volFracWat * diffT ! valid for temperatures below freezing also ! *** compute enthalpy of water for frozen conditions else + if(use_lookup)then + !cubic spline interpolation ! compute enthalpy of unfrozen and frozen water enthLiq = iden_water * Cp_water * volFracWat * (Tcrit - Tfreeze) enthIce = iden_ice * Cp_ice * volFracWat * (mLayerTempTrial(iLayer) - Tcrit) ! compute enthalpy of the mixed region, liquid+ice - if(use_lookup)then !cubic spline interpolation ! calculate enthalpy at the temperature call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),enthTemp,dE,err,cmessage) if(err/=0) then; message=trim(message)//trim(cmessage); return; end if ! calculate enthalpy at the critical temperature call splint(Tk,Ey,E2,Tcrit,enthTcrit,dEcrit,err,cmessage) if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! calculate the enthalpy of water + enthMix = enthTemp - enthTcrit ! enthalpy of the liquid+ice mix + enthWater = enthMix + enthLiq + enthIce else ! hypergeometric function - ! calculate enthalpy at the temperature ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) - mLayerPsiLiq = xConst*(mLayerTempTrial(iLayer) - Tfreeze) ! liquid water matric potential from the Clapeyron eqution + mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution arg = (vGn_alpha * mLayerPsiLiq)**vGn_n gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - enthTemp = (iden_water * Cp_water - iden_ice * Cp_ice) * diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) - ! calculate enthalpy at the critical temperature - arg = (vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n - gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - enthTcrit = (iden_water * Cp_water - iden_ice * Cp_ice) * diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + enthLiq = iden_water * Cp_water* diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + enthIce = iden_ice * Cp_ice * diffT * ( volFracWat - (theta_sat - theta_res)*gauss_hg_T - theta_res ) + enthWater = enthLiq + enthIce endif - ! calculate the enthalpy of water - enthMix = enthTemp - enthTcrit ! enthalpy of the liquid+ice mix - enthWater = enthMix + enthLiq + enthIce endif ! (if frozen conditions) From fbf017ca91a9d77655a0bd4c5aab66db6028658b Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 13 Jan 2024 04:48:32 -0600 Subject: [PATCH 1053/1472] Bug fix for the calculation of mean_step_dt in opSplittin - stateThenDomain loop partially transitioned to object-oriented approach. --- build/source/engine/opSplittin.f90 | 52 +++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index cc62673ca..e159a04dd 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -156,15 +156,17 @@ module opSplittin_module type(var_flagVec) :: fluxMask ! integer mask defining model fluxes logical(lgt),allocatable :: stateMask(:) ! mask defining desired state variables contains - procedure :: initialize_ixCoupling => split_select_initialize_ixCoupling ! initialize operator splitting indices - procedure :: initialize_iStateTypeSplit => split_select_initialize_iStateTypeSplit ! initialize operator splitting indices - procedure :: get_stateMask => split_select_compute_stateMask ! compute stateMask and nSubset and load into class object - procedure :: get_indices => split_select_load_indices ! load operator splitting indices into class object - procedure :: cycle_coupling => split_select_cycle_coupling - procedure :: exit_coupling => split_select_exit_coupling - procedure :: exit_stateTypeSplitting => split_select_exit_stateTypeSplitting - procedure :: advance_ixCoupling => split_select_advance_ixCoupling ! advance coupling loop iterator - procedure :: advance_iStateTypeSplit => split_select_advance_iStateTypeSplit ! advance coupling loop iterator + procedure :: initialize_ixCoupling => split_select_initialize_ixCoupling ! initialize operator splitting indices + procedure :: initialize_iStateTypeSplit => split_select_initialize_iStateTypeSplit ! initialize operator splitting indices + procedure :: initialize_ixStateThenDomain => split_select_initialize_ixStateThenDomain + procedure :: get_stateMask => split_select_compute_stateMask ! compute stateMask and nSubset and load into class object + procedure :: get_indices => split_select_load_indices ! load operator splitting indices into class object + procedure :: cycle_coupling => split_select_cycle_coupling + procedure :: exit_coupling => split_select_exit_coupling + procedure :: exit_stateTypeSplitting => split_select_exit_stateTypeSplitting + procedure :: advance_ixCoupling => split_select_advance_ixCoupling ! advance coupling loop iterator + procedure :: advance_iStateTypeSplit => split_select_advance_iStateTypeSplit ! advance coupling loop iterator + procedure :: advance_ixStateThenDomain => split_select_advance_ixStateThenDomain ! advance coupling loop iterator end type split_select_type contains @@ -319,8 +321,8 @@ subroutine opSplittin(& ! loop control logical(lgt) :: exit_coupling,exit_stateTypeSplitting,exit_stateThenDomain,exit_domainSplit,exit_solution,exit_stateSplit logical(lgt) :: cycle_coupling,cycle_stateTypeSplitting,cycle_stateThenDomain,cycle_domainSplit,cycle_solution,cycle_stateSplit - logical(lgt) :: stateTypeSplitting_flag - integer(i4b) :: iSplit,nSplit + logical(lgt) :: stateTypeSplitting_flag,stateThenDomain_flag + integer(i4b) :: iSplit,nSplit,itest ! debug testing logical(lgt), parameter :: test_flag=.false. ! to create test output logical(lgt) :: stop_flag ! to control stopping @@ -352,7 +354,13 @@ subroutine opSplittin(& if (stateTypeSplitting_flag.eqv..true.) then !! ----------------- ! first try the state type split, then try the domain split within a given state type call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split - stateThenDomain: do ixStateThenDomain=1,1+tryDomainSplit ! 1=state type split; 2=domain split within a given state type + call split_select % initialize_ixStateThenDomain + stateThenDomain: do !ixStateThenDomain=1,1+tryDomainSplit ! 1=state type split; 2=domain split within a given state type + ixStateThenDomain=split_select % ixStateThenDomain + if (ixStateThenDomain.gt.(1+tryDomainSplit)) then + ixStateThenDomain=ixStateThenDomain-1; split_select % ixStateThenDomain = ixStateThenDomain ! correct index needed after loop exit + exit stateThenDomain + end if call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs domainSplit: do iDomainSplit=1,nDomainSplit ! domain splitting loop @@ -395,7 +403,7 @@ subroutine opSplittin(& if (cycle_coupling) then call split_select % cycle_coupling; stateTypeSplitting_flag=.false.; cycle split_select_loop ! cycle loops if necessary end if - if (cycle_stateThenDomain) cycle stateThenDomain + if (cycle_stateThenDomain) then; call split_select % advance_ixStateThenDomain; cycle stateThenDomain; end if if (cycle_solution) cycle solution call confirm_variable_updates; if (return_flag.eqv..true.) return ! check that state variables updated - return if error @@ -411,9 +419,9 @@ subroutine opSplittin(& call finalize_solution ! final steps following solution loop end do domainSplit ! domain type splitting loop - + call split_select % advance_ixStateThenDomain end do stateThenDomain ! switch between the state type and domain type splitting - call finalize_stateThenDomain ! final steps following the stateThenDomain loop + call finalize_stateThenDomain; if (return_flag.eqv..true.) return ! final steps following the stateThenDomain loop call split_select % advance_iStateTypeSplit end if !! end stateTypeSplitting ----------------- @@ -638,9 +646,11 @@ end subroutine initialize_stateThenDomain subroutine finalize_stateThenDomain ! *** Final steps following the stateThenDomain loop *** ! sum the mean steps for the time step over each state type split + !if (ixStateThenDomain == 2+tryDomainSplit) ixStateThenDomain=1+tryDomainSplit ! correct index value if stateThenDomain loop is completed fully select case(ixStateThenDomain) case(fullDomain); mean_step_dt = mean_step_dt + mean_step_solution/nStateTypeSplit case(subDomain); mean_step_dt = mean_step_dt + mean_step_state/nStateTypeSplit + case default; err=20; message=trim(message)//'ixStateThenDomain case not found'; return_flag=.true.; return end select associate(& ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat, & ! intent(in): [i4b(:)] indices defining the type of the state @@ -1079,6 +1089,12 @@ subroutine split_select_advance_iStateTypeSplit(split_select) split_select % iStateTypeSplit = split_select % iStateTypeSplit + 1 end subroutine split_select_advance_iStateTypeSplit +subroutine split_select_advance_ixStateThenDomain(split_select) + ! *** Advance index for coupling loop *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % ixStateThenDomain = split_select % ixStateThenDomain + 1 +end subroutine split_select_advance_ixStateThenDomain + subroutine split_select_load_indices(split_select,ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) ! *** load operator splitting indices for split_select_type class *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector @@ -1108,6 +1124,12 @@ subroutine split_select_initialize_iStateTypeSplit(split_select) split_select % iStateTypeSplit = 1 end subroutine split_select_initialize_iStateTypeSplit +subroutine split_select_initialize_ixStateThenDomain(split_select) + ! *** initialize operator splitting indices for split_select_type class *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % ixStateThenDomain = 1 +end subroutine split_select_initialize_ixStateThenDomain + subroutine split_select_compute_stateMask(split_select,indx_data,err,cmessage,message,return_flag) ! *** Get the mask for the state subset *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector From 56add1adb4a49645fc3962c6ecaf5fe17a2bf69a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 16 Jan 2024 11:15:50 +0900 Subject: [PATCH 1054/1472] utils --- utils/hist_per_GRU.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 7571f8e8c..8489038ba 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -48,19 +48,25 @@ def power_transform(x): return x ** 0.5 # Adjust the exponent as needed # Simulation statistics file locations -settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] +use_vars = [1,2,4,5] +settings0= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] +settings = [settings0[i] for i in use_vars] + viz_fil = method_name.copy() eff_fil = method_name.copy() for i, m in enumerate(method_name): viz_fil[i] = m + '_hrly_diff_stats_{}.nc' - viz_fil[i] = viz_fil[i].format(','.join(settings)) + viz_fil[i] = viz_fil[i].format(','.join(settings0)) eff_fil[i] = 'eff_' + m + '.txt' # Specify variables of interest plot_vars = settings.copy() -plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] +plt_titl = ['Snow Water Equivalent','Total soil water content','Total evapotranspiration', 'Total water on the vegetation canopy','Average routed runoff','Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$','$s$'] +plt_titl = [f"({chr(97+n)}) {plt_titl[i]}" for n,i in enumerate(use_vars)] +leg_titl = [leg_titl[i] for i in use_vars] +leg_titlm= [leg_titlm[i] for i in use_vars] if do_hist: fig_fil = 'Hrly_diff_hist_{}_{}_zoom_compressed.png' @@ -71,17 +77,17 @@ def power_transform(x): fig_fil = fig_fil.format(','.join(settings),stat) if stat == 'rmse': - maxes = [2,15,250,0.08,200,10e-3] #[2,15,8e-6,0.08,6e-9,10e-3] - #maxes = [0.25,2,30,0.01,30,2e-3] #[0.25,2,1e-6,0.01,1e-9,2e-3] + maxes = [2,15,250,0.08,200,10e-3] if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] if stat == 'rmnz': maxes = [2,15,250,0.08,200,10e-3] if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] if stat == 'maxe': - maxes = [15,25,0.8,2,0.3,0.2] #[15,25,25e-5,2,1e-7,0.2] + maxes = [15,25,0.8,2,0.3,0.2] if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,0.2] if stat == 'kgem': maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] +maxes = [maxes[i] for i in use_vars] summa = {} eff = {} @@ -99,19 +105,17 @@ def power_transform(x): ##Figure -# Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug if 'compressed' in fig_fil: plt.rcParams.update({'font.size': 25}) else: plt.rcParams.update({'font.size': 100}) if 'compressed' in fig_fil: - fig,axs = plt.subplots(3,2,figsize=(31,33)) + fig,axs = plt.subplots(3,2,figsize=(35,38)) else: - fig,axs = plt.subplots(3,2,figsize=(140,133)) + fig,axs = plt.subplots(3,2,figsize=(140,160)) +fig.subplots_adjust(hspace=0.24, wspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space #fig.suptitle('Histograms of Hourly Statistics for each GRU', fontsize=40,y=1.0) -fig.subplots_adjust(hspace=0.28) # Adjust the bottom margin, vertical space, and horizontal space -#fig.subplots_adjust(hspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space def run_loop(i,var,mx): r = i//2 @@ -216,7 +220,7 @@ def run_loop(i,var,mx): axs[r,c].set_ylabel('cumulative distribution') axs[r,c].set_ylim([0.0, 1.0]) axs[r,c].set_xscale('function', functions=(power_transform, np.power)) #log x axis - if r == 0 and c == 1: # Rotate x-axis labels for axs[2, 1] subplot + if var=='scalarTotalSoilWat': # Rotate x-axis labels for axs[2, 1] subplot axs[r, c].tick_params(axis='x', rotation=45) for i,(var,mx) in enumerate(zip(plot_vars,maxes)): From f8a47fe58e3fd2f2484fde0396ea772bf48d9a41 Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 16 Jan 2024 07:33:09 -0600 Subject: [PATCH 1055/1472] The stateThenDomain loop was absorbed into the split_select loop in opSplittin - further simplifications in progress. --- build/source/engine/opSplittin.f90 | 203 ++++++++++++++--------------- 1 file changed, 100 insertions(+), 103 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index e159a04dd..97708a126 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -161,9 +161,6 @@ module opSplittin_module procedure :: initialize_ixStateThenDomain => split_select_initialize_ixStateThenDomain procedure :: get_stateMask => split_select_compute_stateMask ! compute stateMask and nSubset and load into class object procedure :: get_indices => split_select_load_indices ! load operator splitting indices into class object - procedure :: cycle_coupling => split_select_cycle_coupling - procedure :: exit_coupling => split_select_exit_coupling - procedure :: exit_stateTypeSplitting => split_select_exit_stateTypeSplitting procedure :: advance_ixCoupling => split_select_advance_ixCoupling ! advance coupling loop iterator procedure :: advance_iStateTypeSplit => split_select_advance_iStateTypeSplit ! advance coupling loop iterator procedure :: advance_ixStateThenDomain => split_select_advance_ixStateThenDomain ! advance coupling loop iterator @@ -341,103 +338,120 @@ subroutine opSplittin(& call initialize_split_select call split_select % initialize_ixCoupling call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs - stateTypeSplitting_flag=.false. ! initialize flag - split_select_loop: do iSplit=1,1000 - if (stateTypeSplitting_flag.eqv..false.) then !! ----------------------- - ixCoupling=split_select % ixCoupling; if (ixCoupling.gt.nCoupling) exit split_select_loop ! exit if all splits are exhausted - call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs - call split_select % initialize_iStateTypeSplit; stateTypeSplitting_flag=.true. - end if !! ------------------------ - if (stateTypeSplitting_flag.eqv..true.) then !! ----------------- - iStateTypeSplit=split_select % iStateTypeSplit; if (iStateTypeSplit.gt.nStateTypeSplit) stateTypeSplitting_flag=.false. - end if - if (stateTypeSplitting_flag.eqv..true.) then !! ----------------- - ! first try the state type split, then try the domain split within a given state type - call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split - call split_select % initialize_ixStateThenDomain - stateThenDomain: do !ixStateThenDomain=1,1+tryDomainSplit ! 1=state type split; 2=domain split within a given state type + call initialize_flags ! initialize loop control flags + split_select_loop: do iSplit=1,500 + if (stateThenDomain_flag.eqv..false.) then + if (stateTypeSplitting_flag.eqv..false.) then + ixCoupling=split_select % ixCoupling; if (ixCoupling.gt.nCoupling) exit split_select_loop ! exit if all splits are exhausted + call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs + call split_select % initialize_iStateTypeSplit; stateTypeSplitting_flag=.true. + end if + if (stateTypeSplitting_flag.eqv..true.) then + iStateTypeSplit=split_select % iStateTypeSplit; if (iStateTypeSplit.gt.nStateTypeSplit) stateTypeSplitting_flag=.false. + end if + end if + if (stateTypeSplitting_flag.eqv..true.) then + if (stateThenDomain_flag.eqv..false.) then + ! first try the state type split, then try the domain split within a given state type + call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split + call split_select % initialize_ixStateThenDomain; stateThenDomain_flag=.true. + end if + !stateThenDomain: do !ixStateThenDomain=1,1+tryDomainSplit ! 1=state type split; 2=domain split within a given state type + if (stateThenDomain_flag.eqv..true.) then ixStateThenDomain=split_select % ixStateThenDomain if (ixStateThenDomain.gt.(1+tryDomainSplit)) then ixStateThenDomain=ixStateThenDomain-1; split_select % ixStateThenDomain = ixStateThenDomain ! correct index needed after loop exit - exit stateThenDomain + stateThenDomain_flag=.false. +!FIX -- probably take out exit stateThenDomain end if + end if + if (stateThenDomain_flag.eqv..true.) then + call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs + domainSplit: do iDomainSplit=1,nDomainSplit ! domain splitting loop - call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs - domainSplit: do iDomainSplit=1,nDomainSplit ! domain splitting loop - - solution: do ixSolution=1,nSolutions ! trial with the vector then scalar solution - - call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs - stateSplit: do iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) - - if (test_flag.eqv..true.) then - if (ixSolution==nSolutions) then - write(*,*) "nStateSplit=",nStateSplit - write(*,*) ixCoupling,iStateTypeSplit,ixStateThenDomain,iDomainSplit,ixSolution,iStateSplit,nState - stop_flag=.true. - if (stop_flag.eqv..true.) stop - end if - end if - - ! define state subsets for a given split... - call split_select % get_indices(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) - call split_select % get_stateMask(indx_data,err,cmessage,message,return_flag) - nSubset = split_select % nSubset; stateMask = split_select % stateMask - if (return_flag.eqv..true.) return - !call update_stateFilter; if (return_flag.eqv..true.) return ! get the mask for the state subset - return for a non-zero error code - call validate_split ! verify that the split is valid - if (cycle_domainSplit) cycle domainSplit - if (cycle_solution) cycle solution - if (return_flag.eqv..true.) return ! return for a non-zero error code - call save_recover ! save/recover copies of variables and fluxes - - ! assemble vectors for a given split... - call get_split_indices; if (return_flag.eqv..true.) return ! get indices for a given split - return for a non-zero error code - call update_fluxMask; if (return_flag.eqv..true.) return ! define the mask of the fluxes used - return for a non-zero error code - - call solve_subset; if (return_flag.eqv..true.) return ! solve variable subset for one time step - return for a positive error code - - call assess_solution; if (return_flag.eqv..true.) return ! is solution a success or failure? - return for a recovering solution - - call try_other_solution_methods ! if solution failed to converge, try other splitting methods - if (cycle_coupling) then - call split_select % cycle_coupling; stateTypeSplitting_flag=.false.; cycle split_select_loop ! cycle loops if necessary - end if - if (cycle_stateThenDomain) then; call split_select % advance_ixStateThenDomain; cycle stateThenDomain; end if - if (cycle_solution) cycle solution - - call confirm_variable_updates; if (return_flag.eqv..true.) return ! check that state variables updated - return if error - - call success_check ! check for success - if (exit_stateThenDomain) exit stateThenDomain ! exit loops if necessary - if (exit_solution) exit solution - if (return_flag.eqv..true.) return ! return if error - - end do stateSplit ! solution with split layers + solution: do ixSolution=1,nSolutions ! trial with the vector then scalar solution - end do solution ! trial with the full layer solution then the split layer solution - call finalize_solution ! final steps following solution loop + call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs + stateSplit: do iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) - end do domainSplit ! domain type splitting loop - call split_select % advance_ixStateThenDomain - end do stateThenDomain ! switch between the state type and domain type splitting - call finalize_stateThenDomain; if (return_flag.eqv..true.) return ! final steps following the stateThenDomain loop + if (test_flag.eqv..true.) then + if (ixSolution==nSolutions) then + write(*,*) "nStateSplit=",nStateSplit + write(*,*) ixCoupling,iStateTypeSplit,ixStateThenDomain,iDomainSplit,ixSolution,iStateSplit,nState + stop_flag=.true. + if (stop_flag.eqv..true.) stop + end if + end if + + ! define state subsets for a given split... + call split_select % get_indices(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) + call split_select % get_stateMask(indx_data,err,cmessage,message,return_flag) + nSubset = split_select % nSubset; stateMask = split_select % stateMask + if (return_flag.eqv..true.) return + !call update_stateFilter; if (return_flag.eqv..true.) return ! get the mask for the state subset - return for a non-zero error code + call validate_split ! verify that the split is valid + if (cycle_domainSplit) cycle domainSplit + if (cycle_solution) cycle solution + if (return_flag.eqv..true.) return ! return for a non-zero error code + call save_recover ! save/recover copies of variables and fluxes + + ! assemble vectors for a given split... + call get_split_indices; if (return_flag.eqv..true.) return ! get indices for a given split - return for a non-zero error code + call update_fluxMask; if (return_flag.eqv..true.) return ! define the mask of the fluxes used - return for a non-zero error code + + call solve_subset; if (return_flag.eqv..true.) return ! solve variable subset for one time step - return for a positive error code + + call assess_solution; if (return_flag.eqv..true.) return ! is solution a success or failure? - return for a recovering solution + + call try_other_solution_methods ! if solution failed to converge, try other splitting methods + if (cycle_coupling) then + call split_select % advance_ixCoupling; call initialize_flags; cycle split_select_loop ! cycle loops if necessary + end if + if (cycle_stateThenDomain) then; call split_select % advance_ixStateThenDomain; cycle split_select_loop; end if + if (cycle_solution) cycle solution + + call confirm_variable_updates; if (return_flag.eqv..true.) return ! check that state variables updated - return if error + + call success_check ! check for success + if (exit_stateThenDomain) then; call split_select % initialize_ixStateThenDomain; stateThenDomain_flag=.false.; exit domainSplit; end if ! exit loops if necessary -- exit last available loop to avoid unnecessary operations + if (exit_solution) exit solution + if (return_flag.eqv..true.) return ! return if error + + end do stateSplit ! solution with split layers + + end do solution ! trial with the full layer solution then the split layer solution + call finalize_solution ! final steps following solution loop + + end do domainSplit ! domain type splitting loop + if (stateThenDomain_flag.eqv..true.) call split_select % advance_ixStateThenDomain + end if ! stateThenDomain + !end do stateThenDomain ! switch between the state type and domain type splitting + if (stateThenDomain_flag.eqv..false.) then + call finalize_stateThenDomain; if (return_flag.eqv..true.) return ! final steps following the stateThenDomain loop + call split_select % advance_iStateTypeSplit + end if + end if !! end stateTypeSplitting + if (stateThenDomain_flag.eqv..false.) then + if (stateTypeSplitting_flag.eqv..false.) then + call finalize_stateTypeSplitting + if (exit_coupling) then + call split_select % initialize_ixCoupling; exit split_select_loop ! success = exit the coupling loop + end if - call split_select % advance_iStateTypeSplit - end if !! end stateTypeSplitting ----------------- - if (stateTypeSplitting_flag.eqv..false.) then - call finalize_stateTypeSplitting - if (exit_coupling) then - call split_select % exit_coupling; exit split_select_loop ! success = exit the coupling loop + call split_select % advance_ixCoupling end if - - call split_select % advance_ixCoupling - end if + end if end do split_select_loop call finalize_coupling ! check variables and fluxes, and apply step halving if needed contains + subroutine initialize_flags + ! *** Initialize flags for opSplittin loops *** + stateTypeSplitting_flag=.false. + stateThenDomain_flag=.false. + end subroutine initialize_flags + subroutine initialize_split_select ! *** Initialize split_select class object - currently under development *** @@ -1161,23 +1175,6 @@ subroutine split_select_compute_stateMask(split_select,indx_data,err,cmessage,me if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! error control end subroutine split_select_compute_stateMask -subroutine split_select_cycle_coupling(split_select) -! *** handle cycle statements of the original opSplittin loops *** - class(split_select_type),intent(inout) :: split_select - split_select % ixCoupling = split_select % ixCoupling+1 -end subroutine split_select_cycle_coupling - -subroutine split_select_exit_coupling(split_select) -! *** handle exit statements of the original opSplittin loops *** - class(split_select_type),intent(inout) :: split_select - split_select % ixCoupling = 1 ! reinitialize index -end subroutine split_select_exit_coupling - -subroutine split_select_exit_stateTypeSplitting(split_select) -! *** handle exit statements of the original opSplittin loops *** - class(split_select_type),intent(inout) :: split_select - split_select % iStateTypeSplit = 1 ! reinitialize index -end subroutine split_select_exit_stateTypeSplitting ! ********************************************************************************************************** ! private subroutine stateFilter: get a mask for the desired state variables From f518ed32200719da416c9c570c7af66fe2123a05 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 17 Jan 2024 12:13:42 +0900 Subject: [PATCH 1056/1472] lookup tables work now --- build/source/engine/t2enthalpy.f90 | 47 ++++++++++-------------------- 1 file changed, 15 insertions(+), 32 deletions(-) diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index f6cc6d27a..0d1b27db8 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -193,17 +193,15 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number of ! update temperature Tk(iLook) = Tk(iLook) + T_incr - ! compute the volumetric liquid water and ice content - ! NOTE: assume saturation - matricHead = (LH_fus/gravity)*(Tk(iLook) - Tfreeze)/Tfreeze + ! compute the volumetric liquid water and ice content at the mid point of the temperature increment + matricHead = (LH_fus/gravity)*(Tk(iLook) - Tfreeze - T_incr/2._rkind)/Tfreeze vFracLiq = volFracLiq(matricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - volFracIce = theta_sat - vFracLiq ! compute enthalpy ! NOTE: assume intrinsic density of ice is the intrinsic density of water ! NOTE: kg m-3 J kg-1 K-1 K - Ey(iLook) = Ey(iLook) + iden_water * Cp_water*vFracLiq*T_incr + iden_water * Cp_ice*volFracIce*T_incr - + Ey(iLook) = Ey(iLook) + vFracLiq*T_incr + end do ! numerical integration end do ! loop through lookup table @@ -305,20 +303,17 @@ subroutine t2enthalpy(& real(rkind) :: integral ! integral of snow freezing curve real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head real(rkind) :: dE ! derivative of enthalpy with temperature at layer temperature - real(rkind) :: dEcrit ! derivative of enthalpy with temperature at critical temperature real(rkind) :: arg ! argument of hypergeometric function real(rkind) :: gauss_hg_T ! hypergeometric function result + real(rkind) :: theta_int ! integral of soil mLayerPsiLiq from Tfreeze to layer temperature real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) ! enthalpy real(rkind) :: enthVeg ! enthalpy of the vegetation (J m-3) real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) - real(rkind) :: enthMix ! enthalpy of the mixed region, liquid+ice (J m-3) real(rkind) :: enthLiq ! enthalpy of the liquid region (J m-3) real(rkind) :: enthIce ! enthalpy of the ice region (J m-3) real(rkind) :: enthAir ! enthalpy of air (J m-3) - real(rkind) :: enthTemp ! enthalpy at the temperature of the control volume (J m-3) - real(rkind) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) real(rkind) :: enthWater ! enthalpy of total water (J m-3) logical(lgt),parameter :: use_lookup=.true. ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function @@ -437,37 +432,25 @@ subroutine t2enthalpy(& if(mlayerTempTrial(iLayer)>=Tcrit)then enthWater = iden_water * Cp_water * volFracWat * diffT ! valid for temperatures below freezing also - ! *** compute enthalpy of water for frozen conditions + ! *** compute enthalpy of water for frozen and unfrozen conditions else if(use_lookup)then - !cubic spline interpolation - ! compute enthalpy of unfrozen and frozen water - enthLiq = iden_water * Cp_water * volFracWat * (Tcrit - Tfreeze) - enthIce = iden_ice * Cp_ice * volFracWat * (mLayerTempTrial(iLayer) - Tcrit) - - ! compute enthalpy of the mixed region, liquid+ice - ! calculate enthalpy at the temperature - call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),enthTemp,dE,err,cmessage) + ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature + call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),theta_int,dE,err,cmessage) if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - ! calculate enthalpy at the critical temperature - call splint(Tk,Ey,E2,Tcrit,enthTcrit,dEcrit,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! calculate the enthalpy of water - enthMix = enthTemp - enthTcrit ! enthalpy of the liquid+ice mix - enthWater = enthMix + enthLiq + enthIce - else ! hypergeometric function - ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice + else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature + ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution arg = (vGn_alpha * mLayerPsiLiq)**vGn_n - gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - enthLiq = iden_water * Cp_water* diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) - enthIce = iden_ice * Cp_ice * diffT * ( volFracWat - (theta_sat - theta_res)*gauss_hg_T - theta_res ) - enthWater = enthLiq + enthIce + gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + theta_int = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) endif + enthLiq = iden_water * Cp_water * theta_int + enthIce = iden_ice * Cp_ice * diffT * volFracWat - iden_ice * Cp_ice * theta_int + enthWater = enthIce + enthLiq endif ! (if frozen conditions) From 95c8d232fcb62eaa32f59209a1f00721972eb459 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 17 Jan 2024 18:00:27 +0900 Subject: [PATCH 1057/1472] start of redoing names for lookup table from enthalpy to integral of mLayerPsiLiq, correcting t2enthalpyPrime but I think that function can be removed completely. --- build/source/dshare/get_ixname.f90 | 2 +- build/source/dshare/popMetadat.f90 | 2 +- build/source/dshare/var_lookup.f90 | 2 +- build/source/engine/convE2Temp.f90 | 12 ++-- build/source/engine/t2enthalpy.f90 | 50 +++++++-------- build/source/engine/t2enthalpyAddPrime.f90 | 72 ++++++---------------- 6 files changed, 51 insertions(+), 89 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index efe620658..453965f03 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -1098,7 +1098,7 @@ function get_ixLookup(varName) ! get the index of the named variables select case(trim(varName)) case('temperature'); get_ixLookup = iLookLOOKUP%temperature ! temperature (K) - case('enthalpy' ); get_ixLookup = iLookLOOKUP%enthalpy ! enthalpy (J m-3) + case('psiLiq_int' ); get_ixLookup = iLookLOOKUP%psiLiq_int ! integral of mLayerPsiLiq from Tfreeze to Tk (K) case('deriv2' ); get_ixLookup = iLookLOOKUP%deriv2 ! secind derivative of the interpolating function ! get to here if cannot find the variable case default diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 8f540a24b..8be590e73 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -670,7 +670,7 @@ subroutine popMetadat(err,message) ! * temperature and enthalpy lookup tables... ! ------------------------------------------- lookup_meta(iLookLOOKUP%temperature) = var_info('temperature' , 'value of temperature in the lookup table' , 'K' , get_ixVarType('unknown'), iMissVec, iMissVec, .false.) - lookup_meta(iLookLOOKUP%enthalpy) = var_info('enthalpy' , 'value of enthalpy in the lookup table' , 'J m-3' , get_ixVarType('unknown'), iMissVec, iMissVec, .false.) + lookup_meta(iLookLOOKUP%psiLiq_int) = var_info('psiLiq_int' , 'value of integral of mLayerPsiLiq in the lookup table' , 'K' , get_ixVarType('unknown'), iMissVec, iMissVec, .false.) lookup_meta(iLookLOOKUP%deriv2) = var_info('deriv2' , 'second derivatives of the interpolating function' , 'mixed' , get_ixVarType('unknown'), iMissVec, iMissVec, .false.) ! ----- ! * model indices... diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 4e3741085..dd27b305d 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -849,7 +849,7 @@ MODULE var_lookup ! *********************************************************************************************************** type, public :: iLook_vLookup integer(i4b) :: temperature = integerMissing ! temperature (K) - integer(i4b) :: enthalpy = integerMissing ! enthalpy (J m-3) + integer(i4b) :: psiLiq_int = integerMissing ! integral of mLayerPsiLiq from Tfreeze to Tk (K) integer(i4b) :: deriv2 = integerMissing ! second derivatives of the interpolating function endtype iLook_vLookup diff --git a/build/source/engine/convE2Temp.f90 b/build/source/engine/convE2Temp.f90 index 59800c29e..40a5b8cf8 100644 --- a/build/source/engine/convE2Temp.f90 +++ b/build/source/engine/convE2Temp.f90 @@ -104,16 +104,16 @@ subroutine E2T_nosoil(Ey,BulkDenWater,fc_param,Tk,err,message) real(rkind),intent(in) :: BulkDenWater ! bulk density of water (kg m-3) real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) real(rkind),intent(out) :: Tk ! initial temperature guess / final temperature value (K) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! declare local variables real(rkind),parameter :: dx=1.d-8 ! finite difference increment (J kg-1) real(rkind),parameter :: atol=1.d-12 ! convergence criteria (J kg-1) real(rkind) :: E_spec ! specific enthalpy (J kg-1) real(rkind) :: E_incr ! enthalpy increment - integer(i4b) :: niter=15 ! maximum number of iterations - integer(i4b) :: iter ! iteration index - integer(i4b) :: i0 ! position in lookup table + integer(i4b) :: niter=15 ! maximum number of iterations + integer(i4b) :: iter ! iteration index + integer(i4b) :: i0 ! position in lookup table real(rkind) :: Tg0,Tg1 ! trial temperatures (K) real(rkind) :: Ht0,Ht1 ! specific enthalpy, based on the trial temperatures (J kg-1) real(rkind) :: f0,f1 ! function evaluations (difference between enthalpy guesses) @@ -126,7 +126,7 @@ subroutine E2T_nosoil(Ey,BulkDenWater,fc_param,Tk,err,message) !write(*,'(a,1x,10(e20.10,1x))') 'E_spec, E_lookup(1)', E_spec, E_lookup(1) ! ***** get initial guess and derivative assuming all water is frozen - if(E_spec lookup_data%z(iSoil)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ey => lookup_data%z(iSoil)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) + Ey => lookup_data%z(iSoil)%var(iLookLOOKUP%psiLiq_int)%lookup , & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) E2 => lookup_data%z(iSoil)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function ) ! end associate statement @@ -173,14 +172,14 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number of ! * populate the lookup table... ! ------------------------------ - ! initialize temperature and enthalpy + ! initialize temperature and integral Tk(nLook) = Tfreeze Ey(nLook) = 0._rkind ! loop through lookup table do iLook=(nLook-1),1,-1 - ! update temperature and enthalpy + ! update temperature and integral Tk(iLook) = Tk(iLook+1) Ey(iLook) = Ey(iLook+1) @@ -197,16 +196,14 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number of matricHead = (LH_fus/gravity)*(Tk(iLook) - Tfreeze - T_incr/2._rkind)/Tfreeze vFracLiq = volFracLiq(matricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - ! compute enthalpy - ! NOTE: assume intrinsic density of ice is the intrinsic density of water - ! NOTE: kg m-3 J kg-1 K-1 K + ! compute integral Ey(iLook) = Ey(iLook) + vFracLiq*T_incr end do ! numerical integration end do ! loop through lookup table - ! use cubic spline interpolation to obtain enthalpy values at the desired values of temperature + ! use cubic spline interpolation to obtain integral values at the desired values of temperature call spline(Tk,Ey,1.e30_rkind,1.e30_rkind,E2,err,cmessage) ! get the second derivatives if(err/=0) then; message=trim(message)//trim(cmessage); return; end if @@ -220,7 +217,7 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number of ! write values print*, 'doTest = ', doTest print*, 'T_test = ', T_test ! temperature (K) - print*, 'E_test = ', E_test ! enthalpy (J m-3) + print*, 'E_test = ', E_test ! integral (K) print*, 'theta_sat = ', theta_sat ! soil porosity (-) print*, 'theta_res = ', theta_res ! volumetric residual water content (-) print*, 'vGn_alpha = ', vGn_alpha ! van Genuchten "alpha" parameter (m-1) @@ -305,7 +302,7 @@ subroutine t2enthalpy(& real(rkind) :: dE ! derivative of enthalpy with temperature at layer temperature real(rkind) :: arg ! argument of hypergeometric function real(rkind) :: gauss_hg_T ! hypergeometric function result - real(rkind) :: theta_int ! integral of soil mLayerPsiLiq from Tfreeze to layer temperature + real(rkind) :: integral_psiLiq ! integral of soil mLayerPsiLiq from Tfreeze to layer temperature real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) ! enthalpy @@ -419,7 +416,7 @@ subroutine t2enthalpy(& vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) ! associate values in the lookup table Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) + Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup , & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function ) ! end associate statement @@ -432,11 +429,10 @@ subroutine t2enthalpy(& if(mlayerTempTrial(iLayer)>=Tcrit)then enthWater = iden_water * Cp_water * volFracWat * diffT ! valid for temperatures below freezing also - ! *** compute enthalpy of water for frozen and unfrozen conditions + ! *** compute enthalpy of water for frozen conditions else - if(use_lookup)then - ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature - call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),theta_int,dE,err,cmessage) + if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature + call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),integral_psiLiq,dE,err,cmessage) if(err/=0) then; message=trim(message)//trim(cmessage); return; end if else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice @@ -445,11 +441,11 @@ subroutine t2enthalpy(& mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution arg = (vGn_alpha * mLayerPsiLiq)**vGn_n gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - theta_int = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + integral_psiLiq = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) endif - enthLiq = iden_water * Cp_water * theta_int - enthIce = iden_ice * Cp_ice * diffT * volFracWat - iden_ice * Cp_ice * theta_int + enthLiq = iden_water * Cp_water * integral_psiLiq + enthIce = iden_ice * Cp_ice * diffT * volFracWat - iden_ice * Cp_ice * integral_psiLiq enthWater = enthIce + enthLiq endif ! (if frozen conditions) @@ -596,7 +592,7 @@ subroutine t2enthalpy_addphase(& end subroutine t2enthalpy_addphase !---------------------------------------------------------------------- -! private function: compute hypergeometric function with real arguments into real result +! public function: compute hypergeometric function with real arguments into real result !---------------------------------------------------------------------- function hyp_2F1_real(a_real, b_real, c_real, z_real) !-------------------------------------------------------------------- diff --git a/build/source/engine/t2enthalpyAddPrime.f90 b/build/source/engine/t2enthalpyAddPrime.f90 index 276f1e9b2..f172af084 100644 --- a/build/source/engine/t2enthalpyAddPrime.f90 +++ b/build/source/engine/t2enthalpyAddPrime.f90 @@ -147,27 +147,19 @@ subroutine t2enthalpyPrime(& real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head real(rkind) :: d_integral_dTk ! derivative of integral with temperature real(rkind) :: dE ! derivative of enthalpy with temperature at layer temperature - real(rkind) :: dEcrit ! derivative of enthalpy with temperature at critical temperature - real(rkind) :: arg ! argument of hypergeometric function - real(rkind) :: gauss_hg_T ! hypergeometric function result + real(rkind) :: integral_psiLiq ! integral of soil mLayerPsiLiq from Tfreeze to layer temperature + real(rkind) :: d_integral_psiLiq_dTk ! derivative with temperature of integral of soil mLayerPsiLiq from Tfreeze to layer temperature real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) - real(rkind) :: volFracPsiWat ! volumetric fraction of liquid water (-) ! enthalpy real(rkind) :: enthVegP ! prime enthalpy of the vegetation (J m-3) real(rkind) :: enthSoilP ! prime enthalpy of soil particles (J m-3) - real(rkind) :: enthMixP ! prime enthalpy of the mixed region, liquid+ice (J m-3) real(rkind) :: enthLiqP ! prime enthalpy of the liquid region (J m-3) real(rkind) :: enthIceP ! prime enthalpy of the ice region (J m-3) real(rkind) :: enthAirP ! prime enthalpy of air (J m-3) - real(rkind) :: enthTemp ! enthalpy at the temperature of the control volume (J m-3) - real(rkind) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) - real(rkind) :: enthTempP ! prime enthalpy at the temperature of the control volume (J m-3) - real(rkind) :: enthTcritP ! prime enthalpy at the critical temperature where all water is unfrozen (J m-3) real(rkind) :: enthPhaseP ! prime enthalpy associated with phase change (J m-3) real(rkind) :: enthWaterP ! prime enthalpy of total water (J m-3) logical(lgt),parameter :: use_lookup=.true. ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function - logical(lgt),parameter :: quick_hyper=.false. ! flag to use a quick hypergeometric function, currently no difference between quick and slow hypergeometric functions ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures generalVars: associate(& @@ -253,13 +245,13 @@ subroutine t2enthalpyPrime(& diffT = mLayerTempTrial(iLayer) - Tfreeze ! diffT<0._rkind because snow is frozen integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) ! Note: VolFracLiq = d_integral_dTk*VolFracWat + d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) enthLiqP = iden_water * Cp_water * ( mLayerVolFracWatPrime(iLayer)*integral & + mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer)*d_integral_dTk ) enthIceP = iden_water * Cp_ice * ( mLayerVolFracWatPrime(iLayer)*( diffT - integral ) & + mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer)*( 1._rkind - d_integral_dTk ) ) enthAirP = iden_air * Cp_air * ( mLayerTempPrime(iLayer) - mLayerVolFracWatPrime(iLayer) * ( (iden_water/iden_ice)*( diffT - integral ) + integral ) & - - mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer)* ( (iden_water/iden_ice)*( 1._rkind - d_integral_dTk ) + d_integral_dTk ) ) + - mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer)*( (iden_water/iden_ice)*( 1._rkind - d_integral_dTk ) + d_integral_dTk ) ) mLayerEnthalpyPrime(iLayer) = enthLiqP + enthIceP + enthAirP @@ -275,7 +267,7 @@ subroutine t2enthalpyPrime(& vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) ! associate values in the lookup table Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) + Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup , & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function ) ! end associate statement @@ -286,7 +278,6 @@ subroutine t2enthalpyPrime(& diffT = mLayerTempTrial(iLayer) - Tfreeze dTcrit_dPsi0 = 0._rkind if (mLayerMatricHeadTrial(ixControlIndex)<0._rkind) dTcrit_dPsi0 = gravity * Tfreeze/LH_fus - ! *** compute enthalpy prime of water for unfrozen conditions if(mlayerTempTrial(iLayer)>=Tcrit)then enthWaterP = iden_water * Cp_water * ( mLayerMatricHeadPrime(ixControlIndex)*dVolTot_dPsi0(ixControlIndex)*diffT & @@ -294,51 +285,26 @@ subroutine t2enthalpyPrime(& ! *** compute enthalpy prime of water for frozen conditions else - ! compute enthalpy of unfrozen and frozen water - enthLiqP = iden_water * Cp_water * mLayerMatricHeadPrime(ixControlIndex) * ( dVolTot_dPsi0(ixControlIndex)*(Tcrit - Tfreeze) + volFracWat*dTcrit_dPsi0 ) - enthIceP = iden_ice * Cp_ice * mLayerMatricHeadPrime(ixControlIndex) * ( dVolTot_dPsi0(ixControlIndex)*(mLayerTempTrial(iLayer) - Tcrit) + volFracWat*(mLayerTempPrime(iLayer) - dTcrit_dPsi0) ) - - ! compute enthalpy of the mixed region, liquid+ice - if(use_lookup)then !cubic spline interpolation - ! calculate enthalpy at the temperature - call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),enthTemp,dE,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - enthTempP = mlayerTempPrime(iLayer) * dE - ! calculate enthalpy at the critical temperature - call splint(Tk,Ey,E2,Tcrit,enthTcrit,dEcrit,err,cmessage) + if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature + call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),integral_psiLiq,dE,err,cmessage) if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - enthTcritP = mLayerMatricHeadPrime(ixControlIndex)* dTcrit_dPsi0 * dEcrit - - else ! hypergeometric function - ! calculate enthalpy at the temperature + d_integral_psiLiq_dTk = dE + else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) - mLayerPsiLiq = xConst*(mLayerTempTrial(iLayer) - Tfreeze) ! liquid water matric potential from the Clapeyron eqution - volFracPsiWat = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - arg = (-vGn_alpha * mLayerPsiLiq)**vGn_n - if(quick_hyper)then - gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - else - gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - endif - volFracPsiWat = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - enthTempP = (iden_water * Cp_water - iden_ice * Cp_ice) * ( diffT * (theta_sat - theta_res)* volFracPsiWat * xConst*mLayerTempPrime(iLayer)& - + mLayerTempPrime(iLayer) * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) ) - ! calculate enthalpy at the critical temperature - arg = (-vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n - if(quick_hyper)then - gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - else - gauss_hg_T = hypergeometric(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - endif - enthTcritP = (iden_water * Cp_water - iden_ice * Cp_ice) * ( diffT * (theta_sat - theta_res)* volFracWat * mLayerMatricHeadPrime(ixControlIndex) & - + mLayerTempPrime(iLayer) * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) ) + mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution + ! NOTE: the following is the integral of mLayerPsiLiq from Tfreeze to layer temperature, it cancels out + !arg = (vGn_alpha * mLayerPsiLiq)**vGn_n + !gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + !integral_psiLiq = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + d_integral_psiLiq_dTk = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) endif - ! calculate the enthalpy of water - enthMixP = enthTempP - enthTcritP ! enthalpy of the liquid+ice mix - enthWaterP = enthMixP + enthLiqP + enthIceP + enthLiqP = iden_water * Cp_water * mLayerTempPrime(iLayer)*d_integral_psiLiq_dTk + enthIceP = iden_ice * Cp_ice * ( mLayerMatricHeadPrime(ixControlIndex)*dVolTot_dPsi0(ixControlIndex)*diffT & + + volFracWat*mLayerTempPrime(iLayer) ) - iden_ice * Cp_ice * mLayerTempPrime(iLayer)* d_integral_psiLiq_dTk + enthWaterP = enthIceP + enthLiqP endif ! (if frozen conditions) From d59337d3a771c249ba5d0b11f91c99325f6c0357 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 17 Jan 2024 18:01:36 +0900 Subject: [PATCH 1058/1472] keep hypergeometric function private --- build/source/engine/t2enthalpy.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index 6d7f46c9b..8bda061c4 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -64,7 +64,7 @@ module t2enthalpy_module public::T2E_lookup public::t2enthalpy public::t2enthalpy_addphase -public::hyp_2F1_real +private::hyp_2F1_real ! define the look-up table used to compute temperature based on enthalpy contains @@ -592,7 +592,7 @@ subroutine t2enthalpy_addphase(& end subroutine t2enthalpy_addphase !---------------------------------------------------------------------- -! public function: compute hypergeometric function with real arguments into real result +! private function: compute hypergeometric function with real arguments into real result !---------------------------------------------------------------------- function hyp_2F1_real(a_real, b_real, c_real, z_real) !-------------------------------------------------------------------- From 55a74807f9d681ee0624970729e51273bd25a95a Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 17 Jan 2024 07:05:27 -0600 Subject: [PATCH 1059/1472] Updates to the split_select_type class in opSplittin - loop operation flags now included in split_select object. --- build/source/engine/opSplittin.f90 | 104 ++++++++++++++++------------- 1 file changed, 56 insertions(+), 48 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 97708a126..aa020582d 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -146,23 +146,29 @@ module opSplittin_module ! class definitions type, public :: split_select_type ! class for selecting operator splitting methods + ! opSplittin loop indices (in order) integer(i4b) :: ixCoupling - integer(i4b) :: ixSolution - integer(i4b) :: ixStateThenDomain integer(i4b) :: iStateTypeSplit + integer(i4b) :: ixStateThenDomain ! 1=state type split; 2=domain split within a given state type integer(i4b) :: iDomainSplit + integer(i4b) :: ixSolution integer(i4b) :: iStateSplit + ! variables for specifying the split integer(i4b) :: nSubset ! number of selected state variables for a given split type(var_flagVec) :: fluxMask ! integer mask defining model fluxes logical(lgt),allocatable :: stateMask(:) ! mask defining desired state variables + ! flags for loop operations + logical(lgt) :: stateTypeSplitting,stateThenDomain,domainSplit contains - procedure :: initialize_ixCoupling => split_select_initialize_ixCoupling ! initialize operator splitting indices - procedure :: initialize_iStateTypeSplit => split_select_initialize_iStateTypeSplit ! initialize operator splitting indices - procedure :: initialize_ixStateThenDomain => split_select_initialize_ixStateThenDomain - procedure :: get_stateMask => split_select_compute_stateMask ! compute stateMask and nSubset and load into class object - procedure :: get_indices => split_select_load_indices ! load operator splitting indices into class object - procedure :: advance_ixCoupling => split_select_advance_ixCoupling ! advance coupling loop iterator - procedure :: advance_iStateTypeSplit => split_select_advance_iStateTypeSplit ! advance coupling loop iterator + procedure :: initialize_flags => split_select_initialize_flags ! initialize flags that control loop operations + procedure :: initialize_ixCoupling => split_select_initialize_ixCoupling ! initialize operator splitting indices + procedure :: initialize_iStateTypeSplit => split_select_initialize_iStateTypeSplit ! initialize operator splitting indices + procedure :: initialize_ixStateThenDomain => split_select_initialize_ixStateThenDomain ! initialize operator splitting indices + procedure :: initialize_iDomainSplit => split_select_initialize_iDomainSplit ! initialize operator splitting indices + procedure :: get_stateMask => split_select_compute_stateMask ! compute stateMask and nSubset and load into class object + procedure :: get_indices => split_select_load_indices ! load operator splitting indices into class object + procedure :: advance_ixCoupling => split_select_advance_ixCoupling ! advance coupling loop iterator + procedure :: advance_iStateTypeSplit => split_select_advance_iStateTypeSplit ! advance coupling loop iterator procedure :: advance_ixStateThenDomain => split_select_advance_ixStateThenDomain ! advance coupling loop iterator end type split_select_type @@ -318,7 +324,6 @@ subroutine opSplittin(& ! loop control logical(lgt) :: exit_coupling,exit_stateTypeSplitting,exit_stateThenDomain,exit_domainSplit,exit_solution,exit_stateSplit logical(lgt) :: cycle_coupling,cycle_stateTypeSplitting,cycle_stateThenDomain,cycle_domainSplit,cycle_solution,cycle_stateSplit - logical(lgt) :: stateTypeSplitting_flag,stateThenDomain_flag integer(i4b) :: iSplit,nSplit,itest ! debug testing logical(lgt), parameter :: test_flag=.false. ! to create test output @@ -338,34 +343,32 @@ subroutine opSplittin(& call initialize_split_select call split_select % initialize_ixCoupling call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs - call initialize_flags ! initialize loop control flags + call split_select % initialize_flags ! initialize loop control flags split_select_loop: do iSplit=1,500 - if (stateThenDomain_flag.eqv..false.) then - if (stateTypeSplitting_flag.eqv..false.) then + if (split_select % stateThenDomain.eqv..false.) then + if (split_select % stateTypeSplitting.eqv..false.) then ixCoupling=split_select % ixCoupling; if (ixCoupling.gt.nCoupling) exit split_select_loop ! exit if all splits are exhausted call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs - call split_select % initialize_iStateTypeSplit; stateTypeSplitting_flag=.true. + call split_select % initialize_iStateTypeSplit; split_select % stateTypeSplitting=.true. end if - if (stateTypeSplitting_flag.eqv..true.) then - iStateTypeSplit=split_select % iStateTypeSplit; if (iStateTypeSplit.gt.nStateTypeSplit) stateTypeSplitting_flag=.false. + if (split_select % stateTypeSplitting.eqv..true.) then + iStateTypeSplit=split_select % iStateTypeSplit; if (iStateTypeSplit.gt.nStateTypeSplit) split_select % stateTypeSplitting=.false. end if end if - if (stateTypeSplitting_flag.eqv..true.) then - if (stateThenDomain_flag.eqv..false.) then + if (split_select % stateTypeSplitting.eqv..true.) then + if (split_select % stateThenDomain.eqv..false.) then ! first try the state type split, then try the domain split within a given state type call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split - call split_select % initialize_ixStateThenDomain; stateThenDomain_flag=.true. + call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.true. end if - !stateThenDomain: do !ixStateThenDomain=1,1+tryDomainSplit ! 1=state type split; 2=domain split within a given state type - if (stateThenDomain_flag.eqv..true.) then + if (split_select % stateThenDomain.eqv..true.) then ixStateThenDomain=split_select % ixStateThenDomain if (ixStateThenDomain.gt.(1+tryDomainSplit)) then ixStateThenDomain=ixStateThenDomain-1; split_select % ixStateThenDomain = ixStateThenDomain ! correct index needed after loop exit - stateThenDomain_flag=.false. -!FIX -- probably take out exit stateThenDomain + split_select % stateThenDomain=.false. ! eqivalent to exiting the stateThenDomain loop end if end if - if (stateThenDomain_flag.eqv..true.) then + if (split_select % stateThenDomain.eqv..true.) then call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs domainSplit: do iDomainSplit=1,nDomainSplit ! domain splitting loop @@ -374,15 +377,6 @@ subroutine opSplittin(& call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs stateSplit: do iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) - if (test_flag.eqv..true.) then - if (ixSolution==nSolutions) then - write(*,*) "nStateSplit=",nStateSplit - write(*,*) ixCoupling,iStateTypeSplit,ixStateThenDomain,iDomainSplit,ixSolution,iStateSplit,nState - stop_flag=.true. - if (stop_flag.eqv..true.) stop - end if - end if - ! define state subsets for a given split... call split_select % get_indices(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) call split_select % get_stateMask(indx_data,err,cmessage,message,return_flag) @@ -405,7 +399,7 @@ subroutine opSplittin(& call try_other_solution_methods ! if solution failed to converge, try other splitting methods if (cycle_coupling) then - call split_select % advance_ixCoupling; call initialize_flags; cycle split_select_loop ! cycle loops if necessary + call split_select % advance_ixCoupling; call split_select % initialize_flags; cycle split_select_loop ! cycle loops if necessary end if if (cycle_stateThenDomain) then; call split_select % advance_ixStateThenDomain; cycle split_select_loop; end if if (cycle_solution) cycle solution @@ -413,7 +407,7 @@ subroutine opSplittin(& call confirm_variable_updates; if (return_flag.eqv..true.) return ! check that state variables updated - return if error call success_check ! check for success - if (exit_stateThenDomain) then; call split_select % initialize_ixStateThenDomain; stateThenDomain_flag=.false.; exit domainSplit; end if ! exit loops if necessary -- exit last available loop to avoid unnecessary operations + if (exit_stateThenDomain) then; call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.false.; exit domainSplit; end if ! exit loops if necessary -- exit last available loop to avoid unnecessary operations if (exit_solution) exit solution if (return_flag.eqv..true.) return ! return if error @@ -423,16 +417,15 @@ subroutine opSplittin(& call finalize_solution ! final steps following solution loop end do domainSplit ! domain type splitting loop - if (stateThenDomain_flag.eqv..true.) call split_select % advance_ixStateThenDomain + if (split_select % stateThenDomain.eqv..true.) call split_select % advance_ixStateThenDomain end if ! stateThenDomain - !end do stateThenDomain ! switch between the state type and domain type splitting - if (stateThenDomain_flag.eqv..false.) then + if (split_select % stateThenDomain.eqv..false.) then call finalize_stateThenDomain; if (return_flag.eqv..true.) return ! final steps following the stateThenDomain loop call split_select % advance_iStateTypeSplit end if - end if !! end stateTypeSplitting - if (stateThenDomain_flag.eqv..false.) then - if (stateTypeSplitting_flag.eqv..false.) then + end if ! stateTypeSplitting + if (split_select % stateThenDomain.eqv..false.) then + if (split_select % stateTypeSplitting.eqv..false.) then call finalize_stateTypeSplitting if (exit_coupling) then call split_select % initialize_ixCoupling; exit split_select_loop ! success = exit the coupling loop @@ -446,11 +439,6 @@ subroutine opSplittin(& contains - subroutine initialize_flags - ! *** Initialize flags for opSplittin loops *** - stateTypeSplitting_flag=.false. - stateThenDomain_flag=.false. - end subroutine initialize_flags subroutine initialize_split_select ! *** Initialize split_select class object - currently under development *** @@ -1091,6 +1079,14 @@ end subroutine opSplittin ! ****** Class procedures for split_select_type class ****** +subroutine split_select_initialize_flags(split_select) + ! *** Initialize flags for opSplittin loops *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % stateTypeSplitting=.false. + split_select % stateThenDomain=.false. + split_select % domainSplit=.false. +end subroutine split_select_initialize_flags + subroutine split_select_advance_ixCoupling(split_select) ! *** Advance index for coupling loop *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector @@ -1098,17 +1094,23 @@ subroutine split_select_advance_ixCoupling(split_select) end subroutine split_select_advance_ixCoupling subroutine split_select_advance_iStateTypeSplit(split_select) - ! *** Advance index for coupling loop *** + ! *** Advance index for stateTypeSplit loop *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector split_select % iStateTypeSplit = split_select % iStateTypeSplit + 1 end subroutine split_select_advance_iStateTypeSplit subroutine split_select_advance_ixStateThenDomain(split_select) - ! *** Advance index for coupling loop *** + ! *** Advance index for stateThenDomain loop *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector split_select % ixStateThenDomain = split_select % ixStateThenDomain + 1 end subroutine split_select_advance_ixStateThenDomain +subroutine split_select_advance_iDomainSplit(split_select) + ! *** Advance index for domainSplit loop *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % iDomainSplit = split_select % iDomainSplit + 1 +end subroutine split_select_advance_iDomainSplit + subroutine split_select_load_indices(split_select,ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) ! *** load operator splitting indices for split_select_type class *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector @@ -1144,6 +1146,12 @@ subroutine split_select_initialize_ixStateThenDomain(split_select) split_select % ixStateThenDomain = 1 end subroutine split_select_initialize_ixStateThenDomain +subroutine split_select_initialize_iDomainSplit(split_select) + ! *** initialize operator splitting indices for split_select_type class *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % iDomainSplit = 1 +end subroutine split_select_initialize_iDomainSplit + subroutine split_select_compute_stateMask(split_select,indx_data,err,cmessage,message,return_flag) ! *** Get the mask for the state subset *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector From 4f95a04454640cdf9674814cee110ae12f2a2998 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 18 Jan 2024 11:35:46 +0900 Subject: [PATCH 1060/1472] fix up t2enthlapy prime, I think I still need this --- build/source/engine/t2enthalpyAddPrime.f90 | 63 ++++++++++------------ 1 file changed, 27 insertions(+), 36 deletions(-) diff --git a/build/source/engine/t2enthalpyAddPrime.f90 b/build/source/engine/t2enthalpyAddPrime.f90 index f172af084..4d9fbfe9e 100644 --- a/build/source/engine/t2enthalpyAddPrime.f90 +++ b/build/source/engine/t2enthalpyAddPrime.f90 @@ -62,6 +62,7 @@ module t2enthalpyAddPrime_module implicit none private public::t2enthalpyPrime +private::hyp_2F1_real contains @@ -147,6 +148,8 @@ subroutine t2enthalpyPrime(& real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head real(rkind) :: d_integral_dTk ! derivative of integral with temperature real(rkind) :: dE ! derivative of enthalpy with temperature at layer temperature + real(rkind) :: arg ! argument of hypergeometric function + real(rkind) :: gauss_hg_T ! hypergeometric function result real(rkind) :: integral_psiLiq ! integral of soil mLayerPsiLiq from Tfreeze to layer temperature real(rkind) :: d_integral_psiLiq_dTk ! derivative with temperature of integral of soil mLayerPsiLiq from Tfreeze to layer temperature real(rkind) :: xConst ! constant in the freezing curve function (m K-1) @@ -294,11 +297,12 @@ subroutine t2enthalpyPrime(& ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution - ! NOTE: the following is the integral of mLayerPsiLiq from Tfreeze to layer temperature, it cancels out - !arg = (vGn_alpha * mLayerPsiLiq)**vGn_n - !gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - !integral_psiLiq = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) - d_integral_psiLiq_dTk = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + ! NOTE: the following is the integral of mLayerPsiLiq from Tfreeze to layer temperature + arg = (vGn_alpha * mLayerPsiLiq)**vGn_n + gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + !integral_psiLiq = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) # NOTE: this is the integral of mLayerPsiLiq from Tfreeze to layer temperature + d_integral_psiLiq_dTk = diffT*( volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - theta_res) & + + ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) endif enthLiqP = iden_water * Cp_water * mLayerTempPrime(iLayer)*d_integral_psiLiq_dTk @@ -330,37 +334,24 @@ subroutine t2enthalpyPrime(& end subroutine t2enthalpyPrime -! ************************************************************************************************************************ -! private function hypergeometric: compute Gaussian hypergeometric function with iterative xpansion method. -! ************************************************************************************************************************ -function hypergeometric(a, b, c, z) +!---------------------------------------------------------------------- +! private function: compute hypergeometric function with real arguments into real result +!---------------------------------------------------------------------- +function hyp_2F1_real(a_real, b_real, c_real, z_real) + !-------------------------------------------------------------------- + USE hyp_2F1_module,only:HYP_2F1 ! use for hypergeometric function implicit none - real(rkind),intent(in) :: a, b, c, z ! input parameters - real(rkind) :: term, factorial ! local variables - real(rkind) :: hypergeometric ! output result - integer(i4b) :: n, max_iter ! iteration count variables - - max_iter = 1000 ! maximum number of iterations - - ! initialize - hypergeometric = 1._rkind - term = 1._rkind - factorial = 1._rkind - - do n = 1, max_iter - factorial = factorial * n - term = term * (a + n - 1) * (b + n - 1) / ((c + n - 1) * factorial) * z - hypergeometric = hypergeometric + term - - if (abs(term) < 1.e-6_rkind) exit ! convergence condition - - if (n == max_iter) then - ! handle non-convergence - write(*, *) "Warning: Hypergeometric function did not converge within the maximum number of iterations." - exit - end if - end do - -end function hypergeometric + real(rkind),intent(in) :: a_real, b_real, c_real, z_real + complex(rkind) :: a_complex, b_complex, c_complex, z_complex, result + real(rkind) :: hyp_2F1_real + + a_complex = CMPLX(a_real, 0._rkind, rkind) + b_complex = CMPLX(b_real, 0._rkind, rkind) + c_complex = CMPLX(c_real, 0._rkind, rkind) + z_complex = CMPLX(z_real, 0._rkind, rkind) + result = HYP_2F1(a_complex, b_complex, c_complex, z_complex) + hyp_2F1_real = REAL(result, rkind) + +end function hyp_2F1_real end module t2enthalpyAddPrime_module From ef9deac7167b49c04dc66355a8c54d284da39942 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 16 Jan 2024 11:15:50 +0900 Subject: [PATCH 1061/1472] utils --- utils/hist_per_GRU.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 7571f8e8c..8489038ba 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -48,19 +48,25 @@ def power_transform(x): return x ** 0.5 # Adjust the exponent as needed # Simulation statistics file locations -settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] +use_vars = [1,2,4,5] +settings0= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] +settings = [settings0[i] for i in use_vars] + viz_fil = method_name.copy() eff_fil = method_name.copy() for i, m in enumerate(method_name): viz_fil[i] = m + '_hrly_diff_stats_{}.nc' - viz_fil[i] = viz_fil[i].format(','.join(settings)) + viz_fil[i] = viz_fil[i].format(','.join(settings0)) eff_fil[i] = 'eff_' + m + '.txt' # Specify variables of interest plot_vars = settings.copy() -plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] +plt_titl = ['Snow Water Equivalent','Total soil water content','Total evapotranspiration', 'Total water on the vegetation canopy','Average routed runoff','Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$','$s$'] +plt_titl = [f"({chr(97+n)}) {plt_titl[i]}" for n,i in enumerate(use_vars)] +leg_titl = [leg_titl[i] for i in use_vars] +leg_titlm= [leg_titlm[i] for i in use_vars] if do_hist: fig_fil = 'Hrly_diff_hist_{}_{}_zoom_compressed.png' @@ -71,17 +77,17 @@ def power_transform(x): fig_fil = fig_fil.format(','.join(settings),stat) if stat == 'rmse': - maxes = [2,15,250,0.08,200,10e-3] #[2,15,8e-6,0.08,6e-9,10e-3] - #maxes = [0.25,2,30,0.01,30,2e-3] #[0.25,2,1e-6,0.01,1e-9,2e-3] + maxes = [2,15,250,0.08,200,10e-3] if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] if stat == 'rmnz': maxes = [2,15,250,0.08,200,10e-3] if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] if stat == 'maxe': - maxes = [15,25,0.8,2,0.3,0.2] #[15,25,25e-5,2,1e-7,0.2] + maxes = [15,25,0.8,2,0.3,0.2] if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,0.2] if stat == 'kgem': maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] +maxes = [maxes[i] for i in use_vars] summa = {} eff = {} @@ -99,19 +105,17 @@ def power_transform(x): ##Figure -# Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug if 'compressed' in fig_fil: plt.rcParams.update({'font.size': 25}) else: plt.rcParams.update({'font.size': 100}) if 'compressed' in fig_fil: - fig,axs = plt.subplots(3,2,figsize=(31,33)) + fig,axs = plt.subplots(3,2,figsize=(35,38)) else: - fig,axs = plt.subplots(3,2,figsize=(140,133)) + fig,axs = plt.subplots(3,2,figsize=(140,160)) +fig.subplots_adjust(hspace=0.24, wspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space #fig.suptitle('Histograms of Hourly Statistics for each GRU', fontsize=40,y=1.0) -fig.subplots_adjust(hspace=0.28) # Adjust the bottom margin, vertical space, and horizontal space -#fig.subplots_adjust(hspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space def run_loop(i,var,mx): r = i//2 @@ -216,7 +220,7 @@ def run_loop(i,var,mx): axs[r,c].set_ylabel('cumulative distribution') axs[r,c].set_ylim([0.0, 1.0]) axs[r,c].set_xscale('function', functions=(power_transform, np.power)) #log x axis - if r == 0 and c == 1: # Rotate x-axis labels for axs[2, 1] subplot + if var=='scalarTotalSoilWat': # Rotate x-axis labels for axs[2, 1] subplot axs[r, c].tick_params(axis='x', rotation=45) for i,(var,mx) in enumerate(zip(plot_vars,maxes)): From b6b2cf927320cad263be8199685cf7e3e85eaea1 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 18 Jan 2024 12:05:00 +0900 Subject: [PATCH 1062/1472] Changing T2E_lookup to T2L_lookup and since only want to compute soil mLayerPsiLiq integral with temp, not the whole enthalpy (otherwise would be multiplying terms by Cp_ice*iden_ice /(Cp_water*iden_water)) --- build/source/driver/summa_setup.f90 | 6 ++-- build/source/engine/t2enthalpy.f90 | 35 +++++++++++----------- build/source/engine/t2enthalpyAddPrime.f90 | 6 ++-- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index 5df05eaa5..9f9390c63 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -80,7 +80,7 @@ subroutine summa_paramSetup(summa1_struc, err, message) USE pOverwrite_module,only:pOverwrite ! module to overwrite default parameter values with info from the Noah tables USE read_param_module,only:read_param ! module to read model parameter sets USE convE2Temp_module,only:E2T_lookup ! module to calculate a look-up table for the temperature-enthalpy conversion - USE t2enthalpy_module,only:T2E_lookup ! module to calculate a look-up table for the temperature-enthalpy conversion + USE t2enthalpy_module,only:T2L_lookup ! module to calculate a look-up table for the temperature-enthalpy conversion USE var_derive_module,only:fracFuture ! module to calculate the fraction of runoff in future time steps (time delay histogram) USE module_sf_noahmplsm,only:read_mp_veg_parameters ! module to read NOAH vegetation tables ! global data structures @@ -315,9 +315,9 @@ subroutine summa_paramSetup(summa1_struc, err, message) call E2T_lookup(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! calculate a lookup table to compute enthalpy from temperature + ! calculate a lookup table to compute integral of soil Clapeyron equation liquid water matric potential from temperature if(needLookup)then - call T2E_lookup(gru_struc(iGRU)%hruInfo(iHRU)%nSoil, & ! intent(in): number of soil layers + call T2L_lookup(gru_struc(iGRU)%hruInfo(iHRU)%nSoil, & ! intent(in): number of soil layers mparStruct%gru(iGRU)%hru(iHRU), & ! intent(in): parameter data structure lookupStruct%gru(iGRU)%hru(iHRU), & ! intent(inout): lookup table data structure err,cmessage) ! intent(out): error control diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 index 8bda061c4..f7227e12d 100644 --- a/build/source/engine/t2enthalpy.f90 +++ b/build/source/engine/t2enthalpy.f90 @@ -61,7 +61,7 @@ module t2enthalpy_module ! privacy implicit none private -public::T2E_lookup +public::T2L_lookup public::t2enthalpy public::t2enthalpy_addphase private::hyp_2F1_real @@ -71,9 +71,10 @@ module t2enthalpy_module ! ************************************************************************************************************************ -! public subroutine T2E_lookup: define a look-up table to compute integral of mLayerPsiLiq based on temperature +! public subroutine T2L_lookup: define a look-up table to compute integral of soil Clapeyron equation liquid water +! matric potential from temperature ! ************************************************************************************************************************ -subroutine T2E_lookup(nSoil, & ! intent(in): number of soil layers +subroutine T2L_lookup(nSoil, & ! intent(in): number of soil layers mpar_data, & ! intent(in): parameter data structure lookup_data, & ! intent(inout): lookup table data structure err,message) @@ -97,7 +98,7 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number real(rkind) :: xIncr ! temporary increment real(rkind) :: T_incr ! temperature increment real(rkind),parameter :: T_test=272.9742_rkind! test value for temperature (K) - real(rkind) :: E_test ! test value for integral of mLayerPsiLiq from Tfreeze to T_test (K) + real(rkind) :: L_test ! test value for integral of mLayerPsiLiq from Tfreeze to T_test (K) real(rkind) :: dE ! derivative of integral with temperature at T_test integer(i4b) :: iVar ! loop through variables integer(i4b) :: iSoil ! loop through soil layers @@ -108,7 +109,7 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) real(rkind) :: matricHead ! matric head (m) ! initialize error control - err=0; message="T2E_lookup/" + err=0; message="T2L_lookup/" ! get the values of temperature for the lookup table xIncr = 1._rkind/real(nLook-1, kind(rkind)) @@ -160,8 +161,8 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number ! associate values in the lookup table Tk => lookup_data%z(iSoil)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ey => lookup_data%z(iSoil)%var(iLookLOOKUP%psiLiq_int)%lookup , & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) - E2 => lookup_data%z(iSoil)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + Ly => lookup_data%z(iSoil)%var(iLookLOOKUP%psiLiq_int)%lookup , & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) + L2 => lookup_data%z(iSoil)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function ) ! end associate statement @@ -174,14 +175,14 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number ! initialize temperature and integral Tk(nLook) = Tfreeze - Ey(nLook) = 0._rkind + Ly(nLook) = 0._rkind ! loop through lookup table do iLook=(nLook-1),1,-1 ! update temperature and integral Tk(iLook) = Tk(iLook+1) - Ey(iLook) = Ey(iLook+1) + Ly(iLook) = Ly(iLook+1) ! get the temperature increment for the numerical integration T_incr = (xTemp(iLook)-xTemp(iLook+1))/real(nIntegr8, kind(rkind)) @@ -197,27 +198,27 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number vFracLiq = volFracLiq(matricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) ! compute integral - Ey(iLook) = Ey(iLook) + vFracLiq*T_incr + Ly(iLook) = Ly(iLook) + vFracLiq*T_incr end do ! numerical integration end do ! loop through lookup table ! use cubic spline interpolation to obtain integral values at the desired values of temperature - call spline(Tk,Ey,1.e30_rkind,1.e30_rkind,E2,err,cmessage) ! get the second derivatives + call spline(Tk,Ly,1.e30_rkind,1.e30_rkind,L2,err,cmessage) ! get the second derivatives if(err/=0) then; message=trim(message)//trim(cmessage); return; end if ! test if(doTest)then ! calculate enthalpy - call splint(Tk,Ey,E2,T_test,E_test,dE,err,cmessage) + call splint(Tk,Ly,L2,T_test,L_test,dE,err,cmessage) if(err/=0) then; message=trim(message)//trim(cmessage); return; end if ! write values print*, 'doTest = ', doTest print*, 'T_test = ', T_test ! temperature (K) - print*, 'E_test = ', E_test ! integral (K) + print*, 'L_test = ', L_test ! integral (K) print*, 'theta_sat = ', theta_sat ! soil porosity (-) print*, 'theta_res = ', theta_res ! volumetric residual water content (-) print*, 'vGn_alpha = ', vGn_alpha ! van Genuchten "alpha" parameter (m-1) @@ -231,7 +232,7 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number end associate end do ! (looping through soil layers) -end subroutine T2E_lookup +end subroutine T2L_lookup ! ************************************************************************************************************************ @@ -416,8 +417,8 @@ subroutine t2enthalpy(& vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) ! associate values in the lookup table Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup , & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) - E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup , & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) + L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function ) ! end associate statement ! diagnostic variables @@ -432,7 +433,7 @@ subroutine t2enthalpy(& ! *** compute enthalpy of water for frozen conditions else if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature - call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),integral_psiLiq,dE,err,cmessage) + call splint(Tk,Ly,L2,mlayerTempTrial(iLayer),integral_psiLiq,dE,err,cmessage) if(err/=0) then; message=trim(message)//trim(cmessage); return; end if else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice diff --git a/build/source/engine/t2enthalpyAddPrime.f90 b/build/source/engine/t2enthalpyAddPrime.f90 index 4d9fbfe9e..bb01a9899 100644 --- a/build/source/engine/t2enthalpyAddPrime.f90 +++ b/build/source/engine/t2enthalpyAddPrime.f90 @@ -270,8 +270,8 @@ subroutine t2enthalpyPrime(& vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) ! associate values in the lookup table Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup , & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) - E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup , & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) + L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function ) ! end associate statement ! diagnostic variables @@ -289,7 +289,7 @@ subroutine t2enthalpyPrime(& ! *** compute enthalpy prime of water for frozen conditions else if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature - call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),integral_psiLiq,dE,err,cmessage) + call splint(Tk,Ly,L2,mlayerTempTrial(iLayer),integral_psiLiq,dE,err,cmessage) if(err/=0) then; message=trim(message)//trim(cmessage); return; end if d_integral_psiLiq_dTk = dE else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature From 90cd9102efa43ba368b2733ab579c86d59fa4a86 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 18 Jan 2024 12:05:51 +0900 Subject: [PATCH 1063/1472] renaming old E2T lookup of snow only to T2E_lookup since that is accurate --- build/source/driver/summa_setup.f90 | 8 ++++---- build/source/engine/convE2Temp.f90 | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index 9f9390c63..8574510f3 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -79,8 +79,8 @@ subroutine summa_paramSetup(summa1_struc, err, message) USE paramCheck_module,only:paramCheck ! module to check consistency of model parameters USE pOverwrite_module,only:pOverwrite ! module to overwrite default parameter values with info from the Noah tables USE read_param_module,only:read_param ! module to read model parameter sets - USE convE2Temp_module,only:E2T_lookup ! module to calculate a look-up table for the temperature-enthalpy conversion - USE t2enthalpy_module,only:T2L_lookup ! module to calculate a look-up table for the temperature-enthalpy conversion + USE convE2Temp_module,only:T2E_lookup ! module to calculate a look-up table for the snow temperature-enthalpy conversion + USE t2enthalpy_module,only:T2L_lookup ! module to calculate a look-up table for the soil temperature-enthalpy conversion USE var_derive_module,only:fracFuture ! module to calculate the fraction of runoff in future time steps (time delay histogram) USE module_sf_noahmplsm,only:read_mp_veg_parameters ! module to read NOAH vegetation tables ! global data structures @@ -310,9 +310,9 @@ subroutine summa_paramSetup(summa1_struc, err, message) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! calculate a look-up table for the temperature-enthalpy conversion: snow - ! NOTE1: this should eventually be replaced by the more general routine below + ! NOTE1: might be able to make this more efficient by only doing this for the HRUs that have snow ! NOTE2: this does not actually need to be called for each HRU and GRU - call E2T_lookup(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) + call T2E_lookup(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! calculate a lookup table to compute integral of soil Clapeyron equation liquid water matric potential from temperature diff --git a/build/source/engine/convE2Temp.f90 b/build/source/engine/convE2Temp.f90 index 40a5b8cf8..804b99c73 100644 --- a/build/source/engine/convE2Temp.f90 +++ b/build/source/engine/convE2Temp.f90 @@ -35,7 +35,7 @@ module convE2Temp_module ! privacy implicit none private -public::E2T_lookup +public::T2E_lookup public::E2T_nosoil public::temp2ethpy @@ -47,9 +47,9 @@ module convE2Temp_module ! ************************************************************************************************************************ - ! public subroutine E2T_lookup: define a look-up table to compute specific enthalpy based on temperature, assuming no soil + ! public subroutine T2E_lookup: define a look-up table to compute specific enthalpy based on temperature, assuming no soil ! ************************************************************************************************************************ - subroutine E2T_lookup(mpar_data,err,message) + subroutine T2E_lookup(mpar_data,err,message) USE nr_utility_module,only:arth ! use to build vectors with regular increments USE spline_int_module,only:spline,splint ! use for cubic spline interpolation implicit none @@ -68,7 +68,7 @@ subroutine E2T_lookup(mpar_data,err,message) real(rkind) :: dT ! derivative of temperature with enthalpy at E_lookup integer(i4b) :: ilook ! loop through lookup table ! initialize error control - err=0; message="E2T_lookup/" + err=0; message="T2E_lookup/" ! associate associate( snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ) ! define initial temperature vector @@ -90,7 +90,7 @@ subroutine E2T_lookup(mpar_data,err,message) !write(*,'(i6,1x,2(f20.4,1x))') ilook, E_lookup(ilook), T_lookup(ilook) end do end associate - end subroutine E2T_lookup + end subroutine T2E_lookup ! ************************************************************************************************************************ From 4d42669dde05f77b3fa9c7ae0d4b742c1ea89702 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 18 Jan 2024 13:30:59 +0900 Subject: [PATCH 1064/1472] putting all temp/enthalpy routines in the same module in hopes of someday condensing or simplifying them. Includes all the routines for merging snow layers, and the ones for getting enthalpy conservation with definition of bulkHeatCap by enthalpy change --- build/cmake/CMakeLists.txt | 5 +- build/source/driver/summa_setup.f90 | 9 +- build/source/engine/convE2Temp.f90 | 231 ------------------ .../{t2enthalpy.f90 => enthalpyTemp.f90} | 221 ++++++++++++++++- ...yAddPrime.f90 => enthalpyTempAddPrime.f90} | 11 +- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/eval8summaWithPrime.f90 | 2 +- build/source/engine/layerMerge.f90 | 14 +- build/source/engine/summaSolve4ida.f90 | 4 +- build/source/engine/systemSolv.f90 | 6 +- build/source/engine/varSubstep.f90 | 4 +- 11 files changed, 240 insertions(+), 269 deletions(-) delete mode 100644 build/source/engine/convE2Temp.f90 rename build/source/engine/{t2enthalpy.f90 => enthalpyTemp.f90} (76%) rename build/source/engine/{t2enthalpyAddPrime.f90 => enthalpyTempAddPrime.f90} (99%) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 391b1dc67..f461c72d3 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -325,7 +325,6 @@ set(PRELIM ${ENGINE_DIR}/checkStruc.f90 ${ENGINE_DIR}/childStruc.f90 ${ENGINE_DIR}/conv_funcs.f90 - ${ENGINE_DIR}/convE2Temp.f90 ${SUB_ENGINE_DIR}/ffile_info.f90 ${ENGINE_DIR}/read_pinit.f90 ${ENGINE_DIR}/read_attrb.f90 @@ -338,6 +337,7 @@ set(PRELIM set(MODRUN ${ENGINE_DIR}/canopySnow.f90 ${ENGINE_DIR}/derivforce.f90 + ${ENGINE_DIR}/enthalpyTemp.f90 ${ENGINE_DIR}/getVectorz.f90 ${ENGINE_DIR}/indexState.f90 ${ENGINE_DIR}/layerMerge.f90 @@ -345,7 +345,6 @@ set(MODRUN ${ENGINE_DIR}/qTimeDelay.f90 ${ENGINE_DIR}/snowAlbedo.f90 ${ENGINE_DIR}/snwCompact.f90 - ${ENGINE_DIR}/t2enthalpy.f90 ${ENGINE_DIR}/tempAdjust.f90 ${ENGINE_DIR}/updateVars.f90 ${ENGINE_DIR}/var_derive.f90 @@ -353,8 +352,8 @@ set(MODRUN set(MODRUN_NOT_ACTORS ${ENGINE_DIR}/read_force.f90) set(MODRUN_SUNDIALS + ${ENGINE_DIR}/enthalpyTempAddPrime.f90 ${ENGINE_DIR}/tol4ida.f90 - ${ENGINE_DIR}/t2enthalpyAddPrime.f90 ${ENGINE_DIR}/updateVarsWithPrime.f90) # Solver main modules diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index 8574510f3..cacc21744 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -79,8 +79,8 @@ subroutine summa_paramSetup(summa1_struc, err, message) USE paramCheck_module,only:paramCheck ! module to check consistency of model parameters USE pOverwrite_module,only:pOverwrite ! module to overwrite default parameter values with info from the Noah tables USE read_param_module,only:read_param ! module to read model parameter sets - USE convE2Temp_module,only:T2E_lookup ! module to calculate a look-up table for the snow temperature-enthalpy conversion - USE t2enthalpy_module,only:T2L_lookup ! module to calculate a look-up table for the soil temperature-enthalpy conversion + USE enthalpyTemp_module,only:T2E_lookup ! module to calculate a look-up table for the snow temperature-enthalpy conversion + USE enthalpyTemp_module,only:T2L_lookup ! module to calculate a look-up table for the soil temperature-enthalpy conversion USE var_derive_module,only:fracFuture ! module to calculate the fraction of runoff in future time steps (time delay histogram) USE module_sf_noahmplsm,only:read_mp_veg_parameters ! module to read NOAH vegetation tables ! global data structures @@ -309,13 +309,14 @@ subroutine summa_paramSetup(summa1_struc, err, message) call paramCheck(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! calculate a look-up table for the temperature-enthalpy conversion: snow + ! calculate a look-up table for the temperature-enthalpy conversion of snow ! NOTE1: might be able to make this more efficient by only doing this for the HRUs that have snow ! NOTE2: this does not actually need to be called for each HRU and GRU call T2E_lookup(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! calculate a lookup table to compute integral of soil Clapeyron equation liquid water matric potential from temperature + ! calculate a lookup table for the temperature-enthalpy conversion of soil + ! NOTE3: L is the integral of soil Clapeyron equation liquid water matric potential from temperature, multiply by Cp_liq*iden_water to get enthalpy if(needLookup)then call T2L_lookup(gru_struc(iGRU)%hruInfo(iHRU)%nSoil, & ! intent(in): number of soil layers mparStruct%gru(iGRU)%hru(iHRU), & ! intent(in): parameter data structure diff --git a/build/source/engine/convE2Temp.f90 b/build/source/engine/convE2Temp.f90 deleted file mode 100644 index 804b99c73..000000000 --- a/build/source/engine/convE2Temp.f90 +++ /dev/null @@ -1,231 +0,0 @@ -! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington -! -! This file is part of SUMMA -! -! For more information see: http://www.ral.ucar.edu/projects/summa -! -! This program is free software: you can redistribute it and/or modify -! it under the terms of the GNU General Public License as published by -! the Free Software Foundation, either version 3 of the License, or -! (at your option) any later version. -! -! This program is distributed in the hope that it will be useful, -! but WITHOUT ANY WARRANTY; without even the implied warranty of -! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -! GNU General Public License for more details. -! -! You should have received a copy of the GNU General Public License -! along with this program. If not, see . - -module convE2Temp_module - -! data types -USE nrtype -USE data_types,only:var_dlength ! data vector with variable length dimension (rkind): x%var(:)%dat(:) - -! constants -USE multiconst, only: Tfreeze, & ! freezing point of water (K) - Cp_soil,Cp_water,Cp_ice,& ! specific heat of soil, water and ice (J kg-1 K-1) - LH_fus ! latent heat of fusion (J kg-1) - -! indices within parameter structure -USE var_lookup,only:iLookPARAM ! named variables to define structure element - -! privacy -implicit none -private -public::T2E_lookup -public::E2T_nosoil -public::temp2ethpy - -! define the look-up table used to compute temperature based on enthalpy -integer(i4b),parameter :: nlook=10001 ! number of elements in the lookup table -real(rkind),dimension(nlook),public :: E_lookup ! enthalpy values (J kg-1) -real(rkind),dimension(nlook),public :: T_lookup ! temperature values (K) -contains - - - ! ************************************************************************************************************************ - ! public subroutine T2E_lookup: define a look-up table to compute specific enthalpy based on temperature, assuming no soil - ! ************************************************************************************************************************ - subroutine T2E_lookup(mpar_data,err,message) - USE nr_utility_module,only:arth ! use to build vectors with regular increments - USE spline_int_module,only:spline,splint ! use for cubic spline interpolation - implicit none - ! declare dummy variables - type(var_dlength),intent(in) :: mpar_data ! model parameters - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! declare local variables - character(len=128) :: cmessage ! error message in downwind routine - real(rkind),parameter :: T_start=260.0_rkind ! start temperature value where all liquid water is assumed frozen (K) - real(rkind) :: T_incr,E_incr ! temperature/enthalpy increments - real(rkind),dimension(nlook) :: Tk ! initial temperature vector - real(rkind),dimension(nlook) :: Ey ! initial enthalpy vector - real(rkind),parameter :: waterWght=1._rkind ! weight applied to total water (kg m-3) --- cancels out - real(rkind),dimension(nlook) :: T2deriv ! 2nd derivatives of the interpolating function at tabulated points - real(rkind) :: dT ! derivative of temperature with enthalpy at E_lookup - integer(i4b) :: ilook ! loop through lookup table - ! initialize error control - err=0; message="T2E_lookup/" - ! associate - associate( snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ) - ! define initial temperature vector - T_incr = (Tfreeze - T_start) / real(nlook-1, kind(rkind)) ! temperature increment - Tk = arth(T_start,T_incr,nlook) - ! ***** compute specific enthalpy (NOTE: J m-3 --> J kg-1) ***** - do ilook=1,nlook - Ey(ilook) = temp2ethpy(Tk(ilook),waterWght,snowfrz_scale)/waterWght ! (J m-3 --> J kg-1) - end do - ! define the final enthalpy vector - E_incr = (-Ey(1)) / real(nlook-1, kind(rkind)) ! enthalpy increment - E_lookup = arth(Ey(1),E_incr,nlook) - ! use cubic spline interpolation to obtain temperature values at the desired values of enthalpy - call spline(Ey,Tk,1.e30_rkind,1.e30_rkind,T2deriv,err,cmessage) ! get the second derivatives - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - do ilook=1,nlook - call splint(Ey,Tk,T2deriv,E_lookup(ilook),T_lookup(ilook),dT,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - !write(*,'(i6,1x,2(f20.4,1x))') ilook, E_lookup(ilook), T_lookup(ilook) - end do - end associate - end subroutine T2E_lookup - - - ! ************************************************************************************************************************ - ! public subroutine E2T_nosoil: compute temperature based on specific enthalpy -- appropriate when no dry mass, as in snow - ! ************************************************************************************************************************ - subroutine E2T_nosoil(Ey,BulkDenWater,fc_param,Tk,err,message) - ! compute temperature based on enthalpy -- appropriate when no dry mass, as in snow - implicit none - ! declare dummy variables - real(rkind),intent(in) :: Ey ! total enthalpy (J m-3) - real(rkind),intent(in) :: BulkDenWater ! bulk density of water (kg m-3) - real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) - real(rkind),intent(out) :: Tk ! initial temperature guess / final temperature value (K) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! declare local variables - real(rkind),parameter :: dx=1.d-8 ! finite difference increment (J kg-1) - real(rkind),parameter :: atol=1.d-12 ! convergence criteria (J kg-1) - real(rkind) :: E_spec ! specific enthalpy (J kg-1) - real(rkind) :: E_incr ! enthalpy increment - integer(i4b) :: niter=15 ! maximum number of iterations - integer(i4b) :: iter ! iteration index - integer(i4b) :: i0 ! position in lookup table - real(rkind) :: Tg0,Tg1 ! trial temperatures (K) - real(rkind) :: Ht0,Ht1 ! specific enthalpy, based on the trial temperatures (J kg-1) - real(rkind) :: f0,f1 ! function evaluations (difference between enthalpy guesses) - real(rkind) :: dh ! enthalpy derivative - real(rkind) :: dT ! temperature increment - ! initialize error control - err=0; message="E2T_nosoil/" - ! convert input of total enthalpy (J m-3) to total specific enthalpy (J kg-1) - E_spec = Ey/BulkDenWater ! (NOTE: no soil) - !write(*,'(a,1x,10(e20.10,1x))') 'E_spec, E_lookup(1)', E_spec, E_lookup(1) - - ! ***** get initial guess and derivative assuming all water is frozen - if(E_spec E_lookup(i0+1) .or. & - i0 < 1 .or. i0+1 > nlook)then - err=10; message=trim(message)//'problem finding appropriate value in lookup table'; return - end if - ! get temperature guess - Tg0 = T_lookup(i0) - Tg1 = T_lookup(i0+1) - ! compute function evaluations - f0 = E_lookup(i0) - E_spec - f1 = E_lookup(i0+1) - E_spec - end if - - ! compute initial derivative - dh = (f1 - f0) / (Tg1 - Tg0) - ! compute initial change in T - dT = -f0/dh - !write(*,'(a,1x,f12.5,1x,10(e20.10,1x))') 'Tg1, f0, f1, dh, dT = ', Tg1, f0, f1, dh, dT - ! exit if already close enough - if(abs(dT)=Tfreeze) enthTempWater = Cp_water*(Tk - Tfreeze) + + ! compute the mass component of enthalpy -- energy required to melt ice (J kg-1) + ! NOTE: negative enthalpy means require energy to bring to Tfreeze + enthMass = -LH_fus*(1._rkind - frac_liq) + + ! finally, compute the total enthalpy (J m-3) + t2enthalpy_snow = BulkDenWater*(enthTempWater + enthMass) !+ BulkDenSoil*enthTempSoil +end function t2enthalpy_snow + + ! ************************************************************************************************************************ ! public subroutine t2enthalpy: compute enthalpy from temperature and total water content ! ************************************************************************************************************************ @@ -300,7 +503,7 @@ subroutine t2enthalpy(& real(rkind) :: diffT ! temperature difference from Tfreeze real(rkind) :: integral ! integral of snow freezing curve real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head - real(rkind) :: dE ! derivative of enthalpy with temperature at layer temperature + real(rkind) :: dL ! derivative of enthalpy with temperature at layer temperature real(rkind) :: arg ! argument of hypergeometric function real(rkind) :: gauss_hg_T ! hypergeometric function result real(rkind) :: integral_psiLiq ! integral of soil mLayerPsiLiq from Tfreeze to layer temperature @@ -433,7 +636,7 @@ subroutine t2enthalpy(& ! *** compute enthalpy of water for frozen conditions else if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature - call splint(Tk,Ly,L2,mlayerTempTrial(iLayer),integral_psiLiq,dE,err,cmessage) + call splint(Tk,Ly,L2,mlayerTempTrial(iLayer),integral_psiLiq,dL,err,cmessage) if(err/=0) then; message=trim(message)//trim(cmessage); return; end if else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice @@ -595,7 +798,7 @@ end subroutine t2enthalpy_addphase !---------------------------------------------------------------------- ! private function: compute hypergeometric function with real arguments into real result !---------------------------------------------------------------------- -function hyp_2F1_real(a_real, b_real, c_real, z_real) + function hyp_2F1_real(a_real, b_real, c_real, z_real) !-------------------------------------------------------------------- USE hyp_2F1_module,only:HYP_2F1 ! use for hypergeometric function implicit none @@ -612,4 +815,4 @@ function hyp_2F1_real(a_real, b_real, c_real, z_real) end function hyp_2F1_real -end module t2enthalpy_module \ No newline at end of file +end module enthalpyTemp_module diff --git a/build/source/engine/t2enthalpyAddPrime.f90 b/build/source/engine/enthalpyTempAddPrime.f90 similarity index 99% rename from build/source/engine/t2enthalpyAddPrime.f90 rename to build/source/engine/enthalpyTempAddPrime.f90 index bb01a9899..e5172dd3f 100644 --- a/build/source/engine/t2enthalpyAddPrime.f90 +++ b/build/source/engine/enthalpyTempAddPrime.f90 @@ -18,7 +18,7 @@ ! You should have received a copy of the GNU General Public License ! along with this program. If not, see . -module t2enthalpyAddPrime_module +module enthalpyTempAddPrime_module ! constants USE multiconst, only: gravity, & ! gravitational acceleration (m s-1) @@ -60,7 +60,6 @@ module t2enthalpyAddPrime_module ! privacy implicit none -private public::t2enthalpyPrime private::hyp_2F1_real @@ -147,7 +146,7 @@ subroutine t2enthalpyPrime(& real(rkind) :: integral ! integral of snow freezing curve real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head real(rkind) :: d_integral_dTk ! derivative of integral with temperature - real(rkind) :: dE ! derivative of enthalpy with temperature at layer temperature + real(rkind) :: dL ! derivative of enthalpy with temperature at layer temperature real(rkind) :: arg ! argument of hypergeometric function real(rkind) :: gauss_hg_T ! hypergeometric function result real(rkind) :: integral_psiLiq ! integral of soil mLayerPsiLiq from Tfreeze to layer temperature @@ -289,9 +288,9 @@ subroutine t2enthalpyPrime(& ! *** compute enthalpy prime of water for frozen conditions else if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature - call splint(Tk,Ly,L2,mlayerTempTrial(iLayer),integral_psiLiq,dE,err,cmessage) + call splint(Tk,Ly,L2,mlayerTempTrial(iLayer),integral_psiLiq,dL,err,cmessage) if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - d_integral_psiLiq_dTk = dE + d_integral_psiLiq_dTk = dL else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations @@ -354,4 +353,4 @@ function hyp_2F1_real(a_real, b_real, c_real, z_real) end function hyp_2F1_real -end module t2enthalpyAddPrime_module +end module enthalpyTempAddPrime_module diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index ee829561e..70c7c1289 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -127,7 +127,7 @@ subroutine eval8summa(& USE getVectorz_module, only:varExtract ! extract variables from the state vector USE getVectorz_module, only:checkFeas ! check feasibility of state vector USE updateVars_module, only:updateVars ! update prognostic variables - USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy + USE enthalpyTemp_module, only:t2enthalpy ! compute enthalpy USE computFlux_module, only:soilCmpres ! compute soil compression USE computFlux_module, only:computFlux ! compute fluxes given a state vector USE computHeatCap_module,only:computHeatCap ! recompute enthalpy finite difference heat capacity (Cp) and derivatives diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 2bfc58745..bba04e828 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -118,7 +118,7 @@ subroutine eval8summaWithPrime(& USE getVectorz_module, only:varExtract ! extract variables from the state vector USE getVectorz_module, only:checkFeas ! check feasibility of state vector USE updateVarsWithPrime_module, only:updateVarsWithPrime ! update variables - USE t2enthalpyAddPrime_module, only:t2enthalpyPrime ! compute enthalpy prime + USE enthalpyTempAddPrime_module, only:t2enthalpyPrime ! compute enthalpy prime USE computFlux_module, only:soilCmpresPrime ! compute soil compression USE computFlux_module, only:computFlux ! compute fluxes given a state vector USE computHeatCap_module,only:computHeatCap ! recompute enthalpy finite difference heat capacity (Cp) and derivatives diff --git a/build/source/engine/layerMerge.f90 b/build/source/engine/layerMerge.f90 index c0fbf88b7..0c828aac1 100644 --- a/build/source/engine/layerMerge.f90 +++ b/build/source/engine/layerMerge.f90 @@ -297,7 +297,7 @@ subroutine layer_combine(mpar_data,prog_data,diag_data,flux_data,indx_data,iSnow USE data_types,only:var_d ! data structures with fixed dimension ! provide access to external modules USE snow_utils_module,only:fracliquid ! compute fraction of liquid water - USE convE2Temp_module,only:E2T_nosoil,temp2ethpy ! convert temperature to enthalpy + USE enthalpyTemp_module,only:enthalpy2t_snow,t2enthalpy_snow ! convert temperature to enthalpy for a snow layer implicit none ! ------------------------------------------------------------------------------------------------------------ ! input/output: data structures @@ -361,19 +361,19 @@ subroutine layer_combine(mpar_data,prog_data,diag_data,flux_data,indx_data,iSnow cBulkDenWat = (mLayerDepth(isnow)*bulkDenWat(1) + mLayerDepth(isnow+1)*bulkDenWat(2))/cDepth ! compute enthalpy for each layer (J m-3) - l1Enthalpy = temp2ethpy(mLayerTemp(iSnow), BulkDenWat(1),snowfrz_scale) - l2Enthalpy = temp2ethpy(mLayerTemp(iSnow+1),BulkDenWat(2),snowfrz_scale) + l1Enthalpy = t2enthalpy_snow(mLayerTemp(iSnow), BulkDenWat(1),snowfrz_scale) + l2Enthalpy = t2enthalpy_snow(mLayerTemp(iSnow+1),BulkDenWat(2),snowfrz_scale) ! compute combined enthalpy (J m-3) - cEnthalpy = (mLayerDepth(isnow)*l1Enthalpy + mLayerDepth(isnow+1)*l2Enthalpy)/cDepth + cEnthalpy = (mLayerDepth(isnow)*l1Enthalpy + mLayerDepth(isnow+1)*l2Enthalpy)/cDepth ! convert enthalpy (J m-3) to temperature (K) - call E2T_nosoil(cEnthalpy,cBulkDenWat,snowfrz_scale,cTemp,err,cmessage) + call enthalpy2t_snow(cEnthalpy,cBulkDenWat,snowfrz_scale,cTemp,err,cmessage) if(err/=0)then; err=10; message=trim(message)//trim(cmessage); return; end if ! test enthalpy conversion - if(abs(temp2ethpy(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat - cEnthalpy/cBulkDenWat) > eTol)then - write(*,'(a,1x,f12.5,1x,2(e20.10,1x))') 'enthalpy test', cBulkDenWat, temp2ethpy(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat, cEnthalpy/cBulkDenWat + if(abs(t2enthalpy_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat - cEnthalpy/cBulkDenWat) > eTol)then + write(*,'(a,1x,f12.5,1x,2(e20.10,1x))') 'enthalpy test', cBulkDenWat, t2enthalpy_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat, cEnthalpy/cBulkDenWat message=trim(message)//'problem with enthalpy-->temperature conversion' err=20; return end if diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 1405d7b74..5029c7c89 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -158,8 +158,8 @@ subroutine summaSolve4ida( & USE computJacobWithPrime_module,only:computJacob4ida ! system Jacobian USE tol4ida_module,only:computWeight4ida ! weight required for tolerances USE var_lookup,only:maxvarDecisions ! maximum number of decisions - USE t2enthalpyAddPrime_module,only:t2enthalpyPrime ! compute enthalpy - USE t2enthalpy_module,only:t2enthalpy_addphase ! add phase to enthalpy + USE enthalpyTempAddPrime_module,only:t2enthalpyPrime ! compute enthalpy + USE enthalpyTemp_module,only:t2enthalpy_addphase ! add phase to enthalpy !======= Declarations ========= implicit none diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 702232d47..af0015e51 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -160,8 +160,8 @@ subroutine systemSolv(& USE allocspace_module,only:allocLocal ! allocate local data structures ! state vector and solver USE getVectorz_module,only:getScaling ! get the scaling vectors - USE convE2Temp_module,only:temp2ethpy ! convert temperature to enthalpy - USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy + USE enthalpyTemp_module,only:t2enthalpy_snow ! convert temperature to enthalpy for a snow layer + USE enthalpyTemp_module,only:t2enthalpy ! compute enthalpy #ifdef SUNDIALS_ACTIVE USE tol4ida_module,only:popTol4ida ! populate tolerances USE eval8summaWithPrime_module,only:eval8summaWithPrime ! get the fluxes and residuals @@ -476,7 +476,7 @@ subroutine systemSolv(& if(nSnow>0)then ! compute the energy required to melt the top snow layer (J m-2) bulkDensity = mLayerVolFracIce(1)*iden_ice + mLayerVolFracLiq(1)*iden_water - volEnthalpy = temp2ethpy(mLayerTemp(1),bulkDensity,snowfrz_scale) + volEnthalpy = t2enthalpy_snow(mLayerTemp(1),bulkDensity,snowfrz_scale) ! set flag and error codes for too much melt if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_cur)then tooMuchMelt = .true. diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 1628b736a..641cd7d64 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -630,8 +630,8 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec USE updateVarsWithPrime_module,only:updateVarsWithPrime ! update prognostic variables #endif USE updateVars_module,only:updateVars ! update prognostic variables - USE t2enthalpy_module,only:t2enthalpy ! compute enthalpy - USE t2enthalpy_module,only:t2enthalpy_addphase ! add phase to enthalpy + USE enthalpyTemp_module,only:t2enthalpy ! compute enthalpy + USE enthalpyTemp_module,only:t2enthalpy_addphase ! add phase to enthalpy implicit none ! model control real(rkind) ,intent(in) :: dt ! time step (s) From 936b40225a625ea33299b106fc17e9ff354795f7 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 18 Jan 2024 02:57:38 -0600 Subject: [PATCH 1065/1472] Removed iDomainSplit index from domainSplit do statement in opSplittin - split_select object now used. --- build/source/engine/opSplittin.f90 | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index aa020582d..42da24864 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -170,6 +170,7 @@ module opSplittin_module procedure :: advance_ixCoupling => split_select_advance_ixCoupling ! advance coupling loop iterator procedure :: advance_iStateTypeSplit => split_select_advance_iStateTypeSplit ! advance coupling loop iterator procedure :: advance_ixStateThenDomain => split_select_advance_ixStateThenDomain ! advance coupling loop iterator + procedure :: advance_iDomainSplit => split_select_advance_iDomainSplit ! advance coupling loop iterator end type split_select_type contains @@ -370,21 +371,25 @@ subroutine opSplittin(& end if if (split_select % stateThenDomain.eqv..true.) then call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs - domainSplit: do iDomainSplit=1,nDomainSplit ! domain splitting loop - + call split_select % initialize_iDomainSplit + domainSplit: do !iDomainSplit=1,nDomainSplit ! domain splitting loop + iDomainSplit=split_select % iDomainSplit + if (split_select % iDomainSplit > nDomainSplit) then + exit domainSplit + end if solution: do ixSolution=1,nSolutions ! trial with the vector then scalar solution call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs stateSplit: do iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) ! define state subsets for a given split... - call split_select % get_indices(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) + call split_select % get_indices(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) ! may be able to remove this once all indices are fully handled by split_select call split_select % get_stateMask(indx_data,err,cmessage,message,return_flag) nSubset = split_select % nSubset; stateMask = split_select % stateMask if (return_flag.eqv..true.) return !call update_stateFilter; if (return_flag.eqv..true.) return ! get the mask for the state subset - return for a non-zero error code call validate_split ! verify that the split is valid - if (cycle_domainSplit) cycle domainSplit + if (cycle_domainSplit) then; call split_select % advance_iDomainSplit; cycle domainSplit; end if if (cycle_solution) cycle solution if (return_flag.eqv..true.) return ! return for a non-zero error code call save_recover ! save/recover copies of variables and fluxes @@ -415,7 +420,7 @@ subroutine opSplittin(& end do solution ! trial with the full layer solution then the split layer solution call finalize_solution ! final steps following solution loop - + call split_select % advance_iDomainSplit end do domainSplit ! domain type splitting loop if (split_select % stateThenDomain.eqv..true.) call split_select % advance_ixStateThenDomain end if ! stateThenDomain From b8dcc6357990e2f64f690852c3551e93abb18831 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 19 Jan 2024 16:57:37 +0900 Subject: [PATCH 1066/1472] enthalpy prime soil is correct now, but shouldn't have to compute two hypergeometric functions it seems, something should cancel, work on that. --- build/source/engine/enthalpyTempAddPrime.f90 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/build/source/engine/enthalpyTempAddPrime.f90 b/build/source/engine/enthalpyTempAddPrime.f90 index e5172dd3f..a12b6e70f 100644 --- a/build/source/engine/enthalpyTempAddPrime.f90 +++ b/build/source/engine/enthalpyTempAddPrime.f90 @@ -149,6 +149,7 @@ subroutine t2enthalpyPrime(& real(rkind) :: dL ! derivative of enthalpy with temperature at layer temperature real(rkind) :: arg ! argument of hypergeometric function real(rkind) :: gauss_hg_T ! hypergeometric function result + real(rkind) :: d_gauss_hg_T_dTk ! derivative of hypergeometric function with temperature real(rkind) :: integral_psiLiq ! integral of soil mLayerPsiLiq from Tfreeze to layer temperature real(rkind) :: d_integral_psiLiq_dTk ! derivative with temperature of integral of soil mLayerPsiLiq from Tfreeze to layer temperature real(rkind) :: xConst ! constant in the freezing curve function (m K-1) @@ -300,8 +301,10 @@ subroutine t2enthalpyPrime(& arg = (vGn_alpha * mLayerPsiLiq)**vGn_n gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) !integral_psiLiq = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) # NOTE: this is the integral of mLayerPsiLiq from Tfreeze to layer temperature - d_integral_psiLiq_dTk = diffT*( volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - theta_res) & - + ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + d_gauss_hg_T_dTk = - vGn_n * vGn_alpha * (vGn_alpha * mLayerPsiLiq)**(vGn_n-1._rkind) * (vGn_m/(1._rkind + vGn_n)) & + * hyp_2F1_real(1._rkind + vGn_m,1._rkind + 1._rkind/vGn_n,2._rkind + 1._rkind/vGn_n,-arg) + d_integral_psiLiq_dTk = diffT * (theta_sat - theta_res)* d_gauss_hg_T_dTk + (theta_sat - theta_res)*gauss_hg_T + theta_res + endif enthLiqP = iden_water * Cp_water * mLayerTempPrime(iLayer)*d_integral_psiLiq_dTk From ea0da42ac990ae9ef93cb8d13da635e2c1fb66dc Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 19 Jan 2024 05:13:01 -0600 Subject: [PATCH 1067/1472] The domainSplit loop in opSplittin was successfully absorbed into the new split_select loop. --- build/source/engine/opSplittin.f90 | 106 ++++++++++++++++++----------- 1 file changed, 65 insertions(+), 41 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 42da24864..b839af45e 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -346,37 +346,52 @@ subroutine opSplittin(& call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs call split_select % initialize_flags ! initialize loop control flags split_select_loop: do iSplit=1,500 - if (split_select % stateThenDomain.eqv..false.) then - if (split_select % stateTypeSplitting.eqv..false.) then - ixCoupling=split_select % ixCoupling; if (ixCoupling.gt.nCoupling) exit split_select_loop ! exit if all splits are exhausted - call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs - call split_select % initialize_iStateTypeSplit; split_select % stateTypeSplitting=.true. - end if - if (split_select % stateTypeSplitting.eqv..true.) then - iStateTypeSplit=split_select % iStateTypeSplit; if (iStateTypeSplit.gt.nStateTypeSplit) split_select % stateTypeSplitting=.false. +!write(*,*) "A:",ixCoupling,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit + if (split_select % domainSplit.eqv..false.) then + if (split_select % stateThenDomain.eqv..false.) then + if (split_select % stateTypeSplitting.eqv..false.) then + ixCoupling=split_select % ixCoupling; if (ixCoupling.gt.nCoupling) exit split_select_loop ! exit if all splits are exhausted + call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs + call split_select % initialize_iStateTypeSplit; split_select % stateTypeSplitting=.true. + end if + if (split_select % stateTypeSplitting.eqv..true.) then + iStateTypeSplit=split_select % iStateTypeSplit; if (iStateTypeSplit.gt.nStateTypeSplit) split_select % stateTypeSplitting=.false. +!write(*,*) "B:",iStateTypeSplit,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit + end if end if - end if + end if if (split_select % stateTypeSplitting.eqv..true.) then - if (split_select % stateThenDomain.eqv..false.) then - ! first try the state type split, then try the domain split within a given state type - call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split - call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.true. + if (split_select % domainSplit.eqv..false.) then + if (split_select % stateThenDomain.eqv..false.) then + ! first try the state type split, then try the domain split within a given state type + call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split + call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.true. + end if + if (split_select % stateThenDomain.eqv..true.) then + ixStateThenDomain=split_select % ixStateThenDomain +!write(*,*) "C:",ixStateThenDomain,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit + if (ixStateThenDomain.gt.(1+tryDomainSplit)) then + ixStateThenDomain=ixStateThenDomain-1; split_select % ixStateThenDomain = ixStateThenDomain ! correct index needed after loop exit + split_select % stateThenDomain=.false. ! eqivalent to exiting the stateThenDomain loop + end if + end if end if if (split_select % stateThenDomain.eqv..true.) then - ixStateThenDomain=split_select % ixStateThenDomain - if (ixStateThenDomain.gt.(1+tryDomainSplit)) then - ixStateThenDomain=ixStateThenDomain-1; split_select % ixStateThenDomain = ixStateThenDomain ! correct index needed after loop exit - split_select % stateThenDomain=.false. ! eqivalent to exiting the stateThenDomain loop - end if - end if - if (split_select % stateThenDomain.eqv..true.) then - call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs - call split_select % initialize_iDomainSplit - domainSplit: do !iDomainSplit=1,nDomainSplit ! domain splitting loop + if (split_select % domainSplit.eqv..false.) then + call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs + call split_select % initialize_iDomainSplit; split_select % domainSplit=.true. + end if +!! domainSplit: do !iDomainSplit=1,nDomainSplit ! domain splitting loop + if (split_select % domainSplit.eqv..true.) then iDomainSplit=split_select % iDomainSplit +!write(*,*) "D:",iDomainSplit,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit if (split_select % iDomainSplit > nDomainSplit) then - exit domainSplit + split_select % domainSplit=.false. + !exit domainSplit end if + end if + if (split_select % domainSplit.eqv..true.) then + solution: do ixSolution=1,nSolutions ! trial with the vector then scalar solution call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs @@ -389,7 +404,7 @@ subroutine opSplittin(& if (return_flag.eqv..true.) return !call update_stateFilter; if (return_flag.eqv..true.) return ! get the mask for the state subset - return for a non-zero error code call validate_split ! verify that the split is valid - if (cycle_domainSplit) then; call split_select % advance_iDomainSplit; cycle domainSplit; end if + if (cycle_domainSplit) then; call split_select % advance_iDomainSplit; cycle split_select_loop; end if if (cycle_solution) cycle solution if (return_flag.eqv..true.) return ! return for a non-zero error code call save_recover ! save/recover copies of variables and fluxes @@ -406,37 +421,46 @@ subroutine opSplittin(& if (cycle_coupling) then call split_select % advance_ixCoupling; call split_select % initialize_flags; cycle split_select_loop ! cycle loops if necessary end if - if (cycle_stateThenDomain) then; call split_select % advance_ixStateThenDomain; cycle split_select_loop; end if + if (cycle_stateThenDomain) then; call split_select % advance_ixStateThenDomain; split_select % domainSplit=.false.; cycle split_select_loop; end if ! deactivate flags for inner loops if (cycle_solution) cycle solution call confirm_variable_updates; if (return_flag.eqv..true.) return ! check that state variables updated - return if error call success_check ! check for success - if (exit_stateThenDomain) then; call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.false.; exit domainSplit; end if ! exit loops if necessary -- exit last available loop to avoid unnecessary operations + if (exit_stateThenDomain) then; call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.false.; split_select % domainSplit=.false.; exit solution; end if ! exit loops if necessary -- exit last available loop to avoid unnecessary operations -- deactivate inner loops if (exit_solution) exit solution if (return_flag.eqv..true.) return ! return if error end do stateSplit ! solution with split layers end do solution ! trial with the full layer solution then the split layer solution - call finalize_solution ! final steps following solution loop - call split_select % advance_iDomainSplit - end do domainSplit ! domain type splitting loop - if (split_select % stateThenDomain.eqv..true.) call split_select % advance_ixStateThenDomain + if (split_select % stateThenDomain.eqv..true.) then + call finalize_solution ! final steps following solution loop + call split_select % advance_iDomainSplit + end if + end if ! domainSplit +!! end do domainSplit ! domain type splitting loop + if (split_select % domainSplit.eqv..false.) then + if (split_select % stateThenDomain.eqv..true.) call split_select % advance_ixStateThenDomain + end if end if ! stateThenDomain - if (split_select % stateThenDomain.eqv..false.) then - call finalize_stateThenDomain; if (return_flag.eqv..true.) return ! final steps following the stateThenDomain loop - call split_select % advance_iStateTypeSplit + if (split_select % domainSplit.eqv..false.) then + if (split_select % stateThenDomain.eqv..false.) then + call finalize_stateThenDomain; if (return_flag.eqv..true.) return ! final steps following the stateThenDomain loop + call split_select % advance_iStateTypeSplit + end if end if end if ! stateTypeSplitting - if (split_select % stateThenDomain.eqv..false.) then - if (split_select % stateTypeSplitting.eqv..false.) then - call finalize_stateTypeSplitting - if (exit_coupling) then - call split_select % initialize_ixCoupling; exit split_select_loop ! success = exit the coupling loop + if (split_select % domainSplit.eqv..false.) then + if (split_select % stateThenDomain.eqv..false.) then + if (split_select % stateTypeSplitting.eqv..false.) then + call finalize_stateTypeSplitting + if (exit_coupling) then + call split_select % initialize_ixCoupling; exit split_select_loop ! success = exit the coupling loop + end if + + call split_select % advance_ixCoupling end if - - call split_select % advance_ixCoupling end if end if end do split_select_loop From 37affbfc762ed024a40ff308c44d7c6d395d1e67 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 20 Jan 2024 02:14:24 +0900 Subject: [PATCH 1068/1472] cancelled out a hypergeo function --- build/source/engine/enthalpyTempAddPrime.f90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/source/engine/enthalpyTempAddPrime.f90 b/build/source/engine/enthalpyTempAddPrime.f90 index a12b6e70f..6d186bb2e 100644 --- a/build/source/engine/enthalpyTempAddPrime.f90 +++ b/build/source/engine/enthalpyTempAddPrime.f90 @@ -301,10 +301,10 @@ subroutine t2enthalpyPrime(& arg = (vGn_alpha * mLayerPsiLiq)**vGn_n gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) !integral_psiLiq = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) # NOTE: this is the integral of mLayerPsiLiq from Tfreeze to layer temperature - d_gauss_hg_T_dTk = - vGn_n * vGn_alpha * (vGn_alpha * mLayerPsiLiq)**(vGn_n-1._rkind) * (vGn_m/(1._rkind + vGn_n)) & - * hyp_2F1_real(1._rkind + vGn_m,1._rkind + 1._rkind/vGn_n,2._rkind + 1._rkind/vGn_n,-arg) - d_integral_psiLiq_dTk = diffT * (theta_sat - theta_res)* d_gauss_hg_T_dTk + (theta_sat - theta_res)*gauss_hg_T + theta_res - + !d_gauss_hg_T_dTk = - vGn_n * arg/diffT * (vGn_m/(1._rkind + vGn_n)) * hyp_2F1_real(1._rkind + vGn_m,1._rkind + 1._rkind/vGn_n,2._rkind + 1._rkind/vGn_n,-arg) + !d_integral_psiLiq_dTk = diffT * (theta_sat - theta_res)* d_gauss_hg_T_dTk + (theta_sat - theta_res)*gauss_hg_T + theta_res + !volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - theta_res = diffT * (theta_sat - theta_res)* d_gauss_hg_T_dTk + d_integral_psiLiq_dTk = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + (theta_sat - theta_res)*gauss_hg_T endif enthLiqP = iden_water * Cp_water * mLayerTempPrime(iLayer)*d_integral_psiLiq_dTk From 1688fdc07ae718e4b236228b5c49ae29fc982ad2 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 20 Jan 2024 02:27:41 +0900 Subject: [PATCH 1069/1472] Fix commented part --- build/source/engine/enthalpyTempAddPrime.f90 | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/build/source/engine/enthalpyTempAddPrime.f90 b/build/source/engine/enthalpyTempAddPrime.f90 index 6d186bb2e..d0c9c9d15 100644 --- a/build/source/engine/enthalpyTempAddPrime.f90 +++ b/build/source/engine/enthalpyTempAddPrime.f90 @@ -149,7 +149,6 @@ subroutine t2enthalpyPrime(& real(rkind) :: dL ! derivative of enthalpy with temperature at layer temperature real(rkind) :: arg ! argument of hypergeometric function real(rkind) :: gauss_hg_T ! hypergeometric function result - real(rkind) :: d_gauss_hg_T_dTk ! derivative of hypergeometric function with temperature real(rkind) :: integral_psiLiq ! integral of soil mLayerPsiLiq from Tfreeze to layer temperature real(rkind) :: d_integral_psiLiq_dTk ! derivative with temperature of integral of soil mLayerPsiLiq from Tfreeze to layer temperature real(rkind) :: xConst ! constant in the freezing curve function (m K-1) @@ -297,13 +296,11 @@ subroutine t2enthalpyPrime(& ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution - ! NOTE: the following is the integral of mLayerPsiLiq from Tfreeze to layer temperature arg = (vGn_alpha * mLayerPsiLiq)**vGn_n gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - !integral_psiLiq = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) # NOTE: this is the integral of mLayerPsiLiq from Tfreeze to layer temperature - !d_gauss_hg_T_dTk = - vGn_n * arg/diffT * (vGn_m/(1._rkind + vGn_n)) * hyp_2F1_real(1._rkind + vGn_m,1._rkind + 1._rkind/vGn_n,2._rkind + 1._rkind/vGn_n,-arg) - !d_integral_psiLiq_dTk = diffT * (theta_sat - theta_res)* d_gauss_hg_T_dTk + (theta_sat - theta_res)*gauss_hg_T + theta_res - !volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - theta_res = diffT * (theta_sat - theta_res)* d_gauss_hg_T_dTk + ! NOTE: integral_psiLiq = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + ! and thus: d_integral_psiLiq_dTk = diffT * (theta_sat - theta_res)* d_gauss_hg_T_dTk + (theta_sat - theta_res)*gauss_hg_T + theta_res + ! but: volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - theta_res = diffT * (theta_sat - theta_res)* d_gauss_hg_T_dTk d_integral_psiLiq_dTk = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + (theta_sat - theta_res)*gauss_hg_T endif From 69b125a0715f39679fba1fcbf41f08cbdc91e1a4 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 20 Jan 2024 03:10:14 +0900 Subject: [PATCH 1070/1472] fixing prime, don't need hypergeometric function at all, should probably remove lookup option --- build/source/engine/enthalpyTempAddPrime.f90 | 30 +------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/build/source/engine/enthalpyTempAddPrime.f90 b/build/source/engine/enthalpyTempAddPrime.f90 index d0c9c9d15..5c12ee904 100644 --- a/build/source/engine/enthalpyTempAddPrime.f90 +++ b/build/source/engine/enthalpyTempAddPrime.f90 @@ -61,7 +61,6 @@ module enthalpyTempAddPrime_module ! privacy implicit none public::t2enthalpyPrime -private::hyp_2F1_real contains @@ -147,8 +146,6 @@ subroutine t2enthalpyPrime(& real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head real(rkind) :: d_integral_dTk ! derivative of integral with temperature real(rkind) :: dL ! derivative of enthalpy with temperature at layer temperature - real(rkind) :: arg ! argument of hypergeometric function - real(rkind) :: gauss_hg_T ! hypergeometric function result real(rkind) :: integral_psiLiq ! integral of soil mLayerPsiLiq from Tfreeze to layer temperature real(rkind) :: d_integral_psiLiq_dTk ! derivative with temperature of integral of soil mLayerPsiLiq from Tfreeze to layer temperature real(rkind) :: xConst ! constant in the freezing curve function (m K-1) @@ -296,12 +293,7 @@ subroutine t2enthalpyPrime(& ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution - arg = (vGn_alpha * mLayerPsiLiq)**vGn_n - gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - ! NOTE: integral_psiLiq = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) - ! and thus: d_integral_psiLiq_dTk = diffT * (theta_sat - theta_res)* d_gauss_hg_T_dTk + (theta_sat - theta_res)*gauss_hg_T + theta_res - ! but: volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - theta_res = diffT * (theta_sat - theta_res)* d_gauss_hg_T_dTk - d_integral_psiLiq_dTk = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + (theta_sat - theta_res)*gauss_hg_T + d_integral_psiLiq_dTk = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) endif enthLiqP = iden_water * Cp_water * mLayerTempPrime(iLayer)*d_integral_psiLiq_dTk @@ -333,24 +325,4 @@ subroutine t2enthalpyPrime(& end subroutine t2enthalpyPrime -!---------------------------------------------------------------------- -! private function: compute hypergeometric function with real arguments into real result -!---------------------------------------------------------------------- -function hyp_2F1_real(a_real, b_real, c_real, z_real) - !-------------------------------------------------------------------- - USE hyp_2F1_module,only:HYP_2F1 ! use for hypergeometric function - implicit none - real(rkind),intent(in) :: a_real, b_real, c_real, z_real - complex(rkind) :: a_complex, b_complex, c_complex, z_complex, result - real(rkind) :: hyp_2F1_real - - a_complex = CMPLX(a_real, 0._rkind, rkind) - b_complex = CMPLX(b_real, 0._rkind, rkind) - c_complex = CMPLX(c_real, 0._rkind, rkind) - z_complex = CMPLX(z_real, 0._rkind, rkind) - result = HYP_2F1(a_complex, b_complex, c_complex, z_complex) - hyp_2F1_real = REAL(result, rkind) - -end function hyp_2F1_real - end module enthalpyTempAddPrime_module From 0c135d312e1b15ea977762f98ece337add78a087 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 20 Jan 2024 03:32:12 +0900 Subject: [PATCH 1071/1472] remove lookup tables from primeVersion, IDA --- build/source/driver/summa_setup.f90 | 2 +- build/source/dshare/type4ida.f90 | 1 - build/source/engine/enthalpyTempAddPrime.f90 | 40 ++++++-------------- build/source/engine/eval8summaWithPrime.f90 | 5 --- build/source/engine/summaSolve4ida.f90 | 5 --- build/source/engine/systemSolv.f90 | 1 - 6 files changed, 12 insertions(+), 42 deletions(-) diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index cacc21744..d75bc96cd 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -155,7 +155,7 @@ subroutine summa_paramSetup(summa1_struc, err, message) ! decide if computing enthalpy lookup tables, if need enthalpy and not using hypergeometric function ! NOTE: this should be replaced by a parameter of if want lookup table enthalpy, but for now it is hard-coded ! need enthalpy if checkNrgBalance in varSubstep is turned on or using howHeatCap=enthalpyFD - ! then, need lookups if use_lookup=.true. in t2enthalpy (numrec or kin) or t2enthalpyPrime (ida) + ! then, need lookups if use_lookup=.true. in t2enthalpy (numrec or kin) needLookup = .true. !if (model_decisions(iLookDECISIONS%howHeatCap)%iDecision == enthalpyFD) needLookup = .true. diff --git a/build/source/dshare/type4ida.f90 b/build/source/dshare/type4ida.f90 index fda9a219f..a02e253d1 100644 --- a/build/source/dshare/type4ida.f90 +++ b/build/source/dshare/type4ida.f90 @@ -29,7 +29,6 @@ module type4ida logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution type(model_options),allocatable :: model_decisions(:) ! model decisions - type(zLookup) :: lookup_data ! lookup tables type(var_i) :: type_data ! type of vegetation and soil type(var_d) :: attr_data ! spatial attributes type(var_dlength) :: mpar_data ! model parameters diff --git a/build/source/engine/enthalpyTempAddPrime.f90 b/build/source/engine/enthalpyTempAddPrime.f90 index 5c12ee904..4cc8fb397 100644 --- a/build/source/engine/enthalpyTempAddPrime.f90 +++ b/build/source/engine/enthalpyTempAddPrime.f90 @@ -31,16 +31,12 @@ module enthalpyTempAddPrime_module USE nrtype USE data_types,only:var_iLength ! var(:)%dat(:) USE data_types,only:var_dLength ! var(:)%dat(:) -USE data_types,only:zLookup ! z(:)%var(:)%lookup(:) ! indices within parameter structure USE var_lookup,only:iLookPARAM ! named variables to define structure element USE var_lookup,only:iLookINDEX ! named variables to define structure element USE var_lookup,only:iLookLOOKUP ! named variables to define structure element -USE var_lookup,only:iLookDIAG ! named variables for structure elements - -! data dimensions -USE var_lookup,only:maxvarLookup ! maximum number of variables in the lookup tables +USE var_lookup,only:iLookDIAG ! named variables for structure elements ! domain types USE globalData,only:iname_cas ! named variables for canopy air space @@ -73,7 +69,6 @@ subroutine t2enthalpyPrime(& diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure ! input: state variables for the vegetation canopy scalarCanairTempPrime, & ! intent(in): prime value of canopy air temperature (K) scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) @@ -107,7 +102,6 @@ subroutine t2enthalpyPrime(& type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model indices - type(zLookup),intent(in) :: lookup_data ! lookup tables ! input: state variables for the vegetation canopy real(rkind),intent(in) :: scalarCanairTempPrime ! prime value of canopy air temperature (K) real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) @@ -145,8 +139,6 @@ subroutine t2enthalpyPrime(& real(rkind) :: integral ! integral of snow freezing curve real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head real(rkind) :: d_integral_dTk ! derivative of integral with temperature - real(rkind) :: dL ! derivative of enthalpy with temperature at layer temperature - real(rkind) :: integral_psiLiq ! integral of soil mLayerPsiLiq from Tfreeze to layer temperature real(rkind) :: d_integral_psiLiq_dTk ! derivative with temperature of integral of soil mLayerPsiLiq from Tfreeze to layer temperature real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) @@ -158,8 +150,7 @@ subroutine t2enthalpyPrime(& real(rkind) :: enthAirP ! prime enthalpy of air (J m-3) real(rkind) :: enthPhaseP ! prime enthalpy associated with phase change (J m-3) real(rkind) :: enthWaterP ! prime enthalpy of total water (J m-3) - logical(lgt),parameter :: use_lookup=.true. ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function - ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures generalVars: associate(& ! number of model layers, and layer type @@ -226,7 +217,7 @@ subroutine t2enthalpyPrime(& enthIceP = 0._rkind else integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) ! Note: VolFracLiq = d_integral_dTk*VolFracWat + d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) ! Note: volFracLiq = d_integral_dTk*volFracWat enthLiqP = Cp_water * ( scalarCanopyWatPrime*integral + scalarCanopyWatTrial*scalarCanopyTempPrime*d_integral_dTk )/ canopyDepth enthIceP = Cp_ice * ( scalarCanopyWatPrime*( diffT - integral ) + scalarCanopyWatTrial*scalarCanopyTempPrime*( 1._rkind - d_integral_dTk ) ) / canopyDepth endif @@ -244,7 +235,7 @@ subroutine t2enthalpyPrime(& diffT = mLayerTempTrial(iLayer) - Tfreeze ! diffT<0._rkind because snow is frozen integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) + d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) ! Note: volFracLiq = d_integral_dTk*volFracWat enthLiqP = iden_water * Cp_water * ( mLayerVolFracWatPrime(iLayer)*integral & + mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer)*d_integral_dTk ) enthIceP = iden_water * Cp_ice * ( mLayerVolFracWatPrime(iLayer)*( diffT - integral ) & @@ -263,11 +254,7 @@ subroutine t2enthalpyPrime(& theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex) , & ! soil porosity (-) theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex) , & ! volumetric residual water content (-) vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) - ! associate values in the lookup table - Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup , & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) - L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) & ! van Genuchten "n" parameter (-) ) ! end associate statement ! diagnostic variables @@ -284,17 +271,12 @@ subroutine t2enthalpyPrime(& ! *** compute enthalpy prime of water for frozen conditions else - if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature - call splint(Tk,Ly,L2,mlayerTempTrial(iLayer),integral_psiLiq,dL,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - d_integral_psiLiq_dTk = dL - else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature - ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice - ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations - xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) - mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution - d_integral_psiLiq_dTk = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - endif + ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice + ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations + xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) + mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution + ! NOTE: integral_psiLiq = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + d_integral_psiLiq_dTk = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) enthLiqP = iden_water * Cp_water * mLayerTempPrime(iLayer)*d_integral_psiLiq_dTk enthIceP = iden_ice * Cp_ice * ( mLayerMatricHeadPrime(ixControlIndex)*dVolTot_dPsi0(ixControlIndex)*diffT & diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index bba04e828..619af4b24 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -21,7 +21,6 @@ module eval8summaWithPrime_module var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (rkind) - zlookup, & ! lookup tables model_options ! defines the model decisions ! indices that define elements of the data structures @@ -68,7 +67,6 @@ subroutine eval8summaWithPrime(& sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup data type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes mpar_data, & ! intent(in): model parameters @@ -148,7 +146,6 @@ subroutine eval8summaWithPrime(& real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) ! input: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(zLookup), intent(in) :: lookup_data ! lookup tables type(var_i), intent(in) :: type_data ! type of vegetation and soil type(var_d), intent(in) :: attr_data ! spatial attributes type(var_dlength), intent(in) :: mpar_data ! model parameters @@ -425,7 +422,6 @@ subroutine eval8summaWithPrime(& diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure ! input: state variables for the vegetation canopy scalarCanairTempPrime, & ! intent(in): prime value for canopy air temperature (K) scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) @@ -788,7 +784,6 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures eqns_data%model_decisions, & ! intent(in): model decisions - eqns_data%lookup_data, & ! intent(in): lookup data eqns_data%type_data, & ! intent(in): type of vegetation and soil eqns_data%attr_data, & ! intent(in): spatial attributes eqns_data%mpar_data, & ! intent(in): model parameters diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 5029c7c89..cbc431e91 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -60,7 +60,6 @@ module summaSolve4ida_module var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! lookup tables model_options ! defines the model decisions USE mDecisions_module,only:& @@ -114,7 +113,6 @@ subroutine summaSolve4ida( & dMat, & ! intent(inout): diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes mpar_data, & ! intent(in): model parameters @@ -188,7 +186,6 @@ subroutine summaSolve4ida( & real(rkind), intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(zLookup), intent(in) :: lookup_data ! lookup tables type(var_i), intent(in) :: type_data ! type of vegetation and soil type(var_d), intent(in) :: attr_data ! spatial attributes type(var_dlength), intent(in) :: mpar_data ! model parameters @@ -298,7 +295,6 @@ subroutine summaSolve4ida( & eqns_data%firstSubStep = firstSubStep eqns_data%computeVegFlux = computeVegFlux eqns_data%scalarSolution = scalarSolution - eqns_data%lookup_data = lookup_data eqns_data%type_data = type_data eqns_data%attr_data = attr_data eqns_data%mpar_data = mpar_data @@ -551,7 +547,6 @@ subroutine summaSolve4ida( & eqns_data%diag_data, & ! intent(in): model diagnostic variables for a local HRU eqns_data%mpar_data, & ! intent(in): parameter data structure eqns_data%indx_data, & ! intent(in): model indices - eqns_data%lookup_data, & ! intent(in): lookup table data structure ! input: state variables for the vegetation canopy eqns_data%scalarCanairTempPrime, & ! intent(in): prime value of canopy air temperature (K) eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index af0015e51..5270b2a17 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -541,7 +541,6 @@ subroutine systemSolv(& dMat, & ! intent(inout) diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes mpar_data, & ! intent(in): model parameters From 869fd2a46f235e59d9bfc82a6b28899b638a6b78 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 20 Jan 2024 12:10:29 +0900 Subject: [PATCH 1072/1472] sending precomputed values into t2enthalpyPrime --- build/source/engine/enthalpyTempAddPrime.f90 | 35 +++++++++--------- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/eval8summaWithPrime.f90 | 6 ++-- build/source/engine/summaSolve4ida.f90 | 38 ++++++++++---------- 4 files changed, 41 insertions(+), 40 deletions(-) diff --git a/build/source/engine/enthalpyTempAddPrime.f90 b/build/source/engine/enthalpyTempAddPrime.f90 index 4cc8fb397..05afb1f17 100644 --- a/build/source/engine/enthalpyTempAddPrime.f90 +++ b/build/source/engine/enthalpyTempAddPrime.f90 @@ -83,6 +83,8 @@ subroutine t2enthalpyPrime(& mLayerVolFracWatPrime, & ! intent(in): prime vector of volumetric total water content (-) mLayerMatricHeadPrime, & ! intent(in): prime vector of total water matric potential (m) ! input: pre-computed derivatives + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy prime scalarCanairEnthalpyPrime, & ! intent(out): prime enthalpy of the canopy air space (J m-3) @@ -116,6 +118,8 @@ subroutine t2enthalpyPrime(& real(rkind),intent(in) :: mLayerVolFracWatPrime(:) ! prime vector of volumetric total water content (-) real(rkind),intent(in) :: mLayerMatricHeadPrime(:) ! prime vector of total water matric potential (m) ! input: pre-computed derivatives + real(rkind),intent(in) :: scalarFracLiqVeg ! fraction of canopy liquid water (-) + real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of snow liquid water (-) real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy prime real(rkind),intent(out) :: scalarCanairEnthalpyPrime ! prime enthalpy of the canopy air space (J m-3) @@ -137,9 +141,7 @@ subroutine t2enthalpyPrime(& real(rkind) :: volFracWat ! volumetric fraction of total water, liquid+ice (-) real(rkind) :: diffT ! temperature difference from Tfreeze real(rkind) :: integral ! integral of snow freezing curve - real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head - real(rkind) :: d_integral_dTk ! derivative of integral with temperature - real(rkind) :: d_integral_psiLiq_dTk ! derivative with temperature of integral of soil mLayerPsiLiq from Tfreeze to layer temperature + real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) in frozen soil real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) ! enthalpy @@ -216,10 +218,9 @@ subroutine t2enthalpyPrime(& enthLiqP = Cp_water * ( scalarCanopyWatPrime * diffT + scalarCanopyWatTrial * scalarCanopyTempPrime )/ canopyDepth enthIceP = 0._rkind else - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) ! Note: volFracLiq = d_integral_dTk*volFracWat - enthLiqP = Cp_water * ( scalarCanopyWatPrime*integral + scalarCanopyWatTrial*scalarCanopyTempPrime*d_integral_dTk )/ canopyDepth - enthIceP = Cp_ice * ( scalarCanopyWatPrime*( diffT - integral ) + scalarCanopyWatTrial*scalarCanopyTempPrime*( 1._rkind - d_integral_dTk ) ) / canopyDepth + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) ! Note: scalarFracLiqVeg = d_integral_dTk*scalarCanopyWatTrial + enthLiqP = Cp_water * ( scalarCanopyWatPrime*integral + scalarCanopyTempPrime * scalarFracLiqVeg)/ canopyDepth + enthIceP = Cp_ice * ( scalarCanopyWatPrime*( diffT - integral ) + scalarCanopyTempPrime*( scalarCanopyWatTrial - scalarFracLiqVeg ) ) / canopyDepth endif scalarCanopyEnthalpyPrime = enthVegP + enthLiqP + enthIceP @@ -235,13 +236,12 @@ subroutine t2enthalpyPrime(& diffT = mLayerTempTrial(iLayer) - Tfreeze ! diffT<0._rkind because snow is frozen integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) ! Note: volFracLiq = d_integral_dTk*volFracWat - enthLiqP = iden_water * Cp_water * ( mLayerVolFracWatPrime(iLayer)*integral & - + mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer)*d_integral_dTk ) + enthLiqP = iden_water * Cp_water * ( mLayerVolFracWatPrime(iLayer)*integral + mLayerTempPrime(iLayer)*mLayerFracLiqSnow(iLayer) ) enthIceP = iden_water * Cp_ice * ( mLayerVolFracWatPrime(iLayer)*( diffT - integral ) & - + mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer)*( 1._rkind - d_integral_dTk ) ) + + mLayerTempPrime(iLayer)*( mLayerVolFracWatTrial(iLayer) - mLayerFracLiqSnow(iLayer) ) ) enthAirP = iden_air * Cp_air * ( mLayerTempPrime(iLayer) - mLayerVolFracWatPrime(iLayer) * ( (iden_water/iden_ice)*( diffT - integral ) + integral ) & - - mLayerVolFracWatTrial(iLayer)*mLayerTempPrime(iLayer)*( (iden_water/iden_ice)*( 1._rkind - d_integral_dTk ) + d_integral_dTk ) ) + - mLayerTempPrime(iLayer)*( (iden_water/iden_ice)*( mLayerVolFracWatTrial(iLayer) - mLayerFracLiqSnow(iLayer) ) & + + mLayerFracLiqSnow(iLayer) ) ) mLayerEnthalpyPrime(iLayer) = enthLiqP + enthIceP + enthAirP @@ -262,8 +262,6 @@ subroutine t2enthalpyPrime(& Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) volFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) diffT = mLayerTempTrial(iLayer) - Tfreeze - dTcrit_dPsi0 = 0._rkind - if (mLayerMatricHeadTrial(ixControlIndex)<0._rkind) dTcrit_dPsi0 = gravity * Tfreeze/LH_fus ! *** compute enthalpy prime of water for unfrozen conditions if(mlayerTempTrial(iLayer)>=Tcrit)then enthWaterP = iden_water * Cp_water * ( mLayerMatricHeadPrime(ixControlIndex)*dVolTot_dPsi0(ixControlIndex)*diffT & @@ -275,12 +273,11 @@ subroutine t2enthalpyPrime(& ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution - ! NOTE: integral_psiLiq = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) - d_integral_psiLiq_dTk = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - - enthLiqP = iden_water * Cp_water * mLayerTempPrime(iLayer)*d_integral_psiLiq_dTk + ! NOTE: integral_psiLiq = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) and d_integral_psiLiq_dTk = vFracLiq + vFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + enthLiqP = iden_water * Cp_water * mLayerTempPrime(iLayer)*vFracLiq enthIceP = iden_ice * Cp_ice * ( mLayerMatricHeadPrime(ixControlIndex)*dVolTot_dPsi0(ixControlIndex)*diffT & - + volFracWat*mLayerTempPrime(iLayer) ) - iden_ice * Cp_ice * mLayerTempPrime(iLayer)* d_integral_psiLiq_dTk + + volFracWat*mLayerTempPrime(iLayer) ) - iden_ice * Cp_ice * mLayerTempPrime(iLayer) * vFracLiq enthWaterP = enthIceP + enthLiqP endif ! (if frozen conditions) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 70c7c1289..5cca1fbab 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -239,7 +239,7 @@ subroutine eval8summa(& mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] total water matric potential (m) mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in): [dp] storage of water in the aquifer (m) - ! model diagnostic variables from the previous solution + ! model diagnostic variables, will be updated before used scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 619af4b24..44460f477 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -229,10 +229,10 @@ subroutine eval8summaWithPrime(& ! soil parameters theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) - ! canopy and layer depth + ! canopy and layer depth canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! model diagnostic variables from a previous solution + ! model diagnostic variables, will be updated before used scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) @@ -436,6 +436,8 @@ subroutine eval8summaWithPrime(& mLayerVolFracWatPrime, & ! intent(in): prime vector of volumetric total water content (-) mLayerMatricHeadPrime, & ! intent(in): prime vector of total water matric potential (m) ! input: pre-computed derivatives + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy prime scalarCanairEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the canopy air space (J m-3) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index cbc431e91..820038a4c 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -544,28 +544,30 @@ subroutine summaSolve4ida( & if( model_decisions(iLookDECISIONS%howHeatCap)%iDecision == closedForm)then ! did not compute enthalpy without phase already call t2enthalpyPrime(& ! input: data structures - eqns_data%diag_data, & ! intent(in): model diagnostic variables for a local HRU - eqns_data%mpar_data, & ! intent(in): parameter data structure - eqns_data%indx_data, & ! intent(in): model indices + eqns_data%diag_data, & ! intent(in): model diagnostic variables for a local HRU + eqns_data%mpar_data, & ! intent(in): parameter data structure + eqns_data%indx_data, & ! intent(in): model indices ! input: state variables for the vegetation canopy - eqns_data%scalarCanairTempPrime, & ! intent(in): prime value of canopy air temperature (K) - eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - eqns_data%scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - eqns_data%scalarCanopyTempPrime, & ! intent(in): prime value of temperature of the vegetation canopy (K) - eqns_data%scalarCanopyWatPrime, & ! intent(in): prime value of total water of the vegetation canopy (kg m-2) + eqns_data%scalarCanairTempPrime, & ! intent(in): prime value of canopy air temperature (K) + eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + eqns_data%scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + eqns_data%scalarCanopyTempPrime, & ! intent(in): prime value of temperature of the vegetation canopy (K) + eqns_data%scalarCanopyWatPrime, & ! intent(in): prime value of total water of the vegetation canopy (kg m-2) ! input: variables for the snow-soil domain - eqns_data%mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - eqns_data%mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - eqns_data%mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - eqns_data%mLayerTempPrime, & ! intent(in): prime vector of temperature of each snow+soil layer (K) - eqns_data%mLayerVolFracWatPrime, & ! intent(in): prime vector of volumetric total water content of each snow+soil layer (-) - eqns_data%mLayerMatricHeadPrime, & ! intent(in): prime vector of total water matric potential of each snow+soil layer (m) + eqns_data%mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + eqns_data%mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + eqns_data%mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + eqns_data%mLayerTempPrime, & ! intent(in): prime vector of temperature of each snow+soil layer (K) + eqns_data%mLayerVolFracWatPrime, & ! intent(in): prime vector of volumetric total water content of each snow+soil layer (-) + eqns_data%mLayerMatricHeadPrime, & ! intent(in): prime vector of total water matric potential of each snow+soil layer (m) ! input: pre-computed derivatives - deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + eqns_data%diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1), & ! intent(in): fraction of canopy liquid water (-) + eqns_data%diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat, & ! intent(in): fraction of liquid water (-) + eqns_data%deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy - eqns_data%scalarCanairEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the canopy air space (J m-3) - eqns_data%scalarCanopyEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the vegetation canopy (J m-3) - eqns_data%mLayerEnthalpyPrime, & ! intent(out): prime vector oftemperature component of enthalpy of each snow+soil layer (J m-3) + eqns_data%scalarCanairEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the canopy air space (J m-3) + eqns_data%scalarCanopyEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the vegetation canopy (J m-3) + eqns_data%mLayerEnthalpyPrime, & ! intent(out): prime vector oftemperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif From 6ce7ef879067f8b91faf6c432dc41932911fbf8c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 20 Jan 2024 12:12:00 +0900 Subject: [PATCH 1073/1472] added comment --- build/source/engine/enthalpyTempAddPrime.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/enthalpyTempAddPrime.f90 b/build/source/engine/enthalpyTempAddPrime.f90 index 05afb1f17..c5d2206f3 100644 --- a/build/source/engine/enthalpyTempAddPrime.f90 +++ b/build/source/engine/enthalpyTempAddPrime.f90 @@ -235,7 +235,7 @@ subroutine t2enthalpyPrime(& ) diffT = mLayerTempTrial(iLayer) - Tfreeze ! diffT<0._rkind because snow is frozen - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) ! Note: mLayerFracLiqSnow = d_integral_dTk*mLayerVolFracWatTrial enthLiqP = iden_water * Cp_water * ( mLayerVolFracWatPrime(iLayer)*integral + mLayerTempPrime(iLayer)*mLayerFracLiqSnow(iLayer) ) enthIceP = iden_water * Cp_ice * ( mLayerVolFracWatPrime(iLayer)*( diffT - integral ) & + mLayerTempPrime(iLayer)*( mLayerVolFracWatTrial(iLayer) - mLayerFracLiqSnow(iLayer) ) ) From 7f034c190d924bb34c9f524978d202d73c437579 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 20 Jan 2024 05:30:58 -0600 Subject: [PATCH 1074/1472] The solution loop index in opSplittin is now handled using the split_select object. --- build/source/engine/opSplittin.f90 | 37 +++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index b839af45e..8ac6b0996 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -158,19 +158,21 @@ module opSplittin_module type(var_flagVec) :: fluxMask ! integer mask defining model fluxes logical(lgt),allocatable :: stateMask(:) ! mask defining desired state variables ! flags for loop operations - logical(lgt) :: stateTypeSplitting,stateThenDomain,domainSplit + logical(lgt) :: stateTypeSplitting,stateThenDomain,domainSplit,solution contains procedure :: initialize_flags => split_select_initialize_flags ! initialize flags that control loop operations procedure :: initialize_ixCoupling => split_select_initialize_ixCoupling ! initialize operator splitting indices procedure :: initialize_iStateTypeSplit => split_select_initialize_iStateTypeSplit ! initialize operator splitting indices procedure :: initialize_ixStateThenDomain => split_select_initialize_ixStateThenDomain ! initialize operator splitting indices procedure :: initialize_iDomainSplit => split_select_initialize_iDomainSplit ! initialize operator splitting indices + procedure :: initialize_ixSolution => split_select_initialize_ixSolution ! initialize operator splitting indices procedure :: get_stateMask => split_select_compute_stateMask ! compute stateMask and nSubset and load into class object procedure :: get_indices => split_select_load_indices ! load operator splitting indices into class object procedure :: advance_ixCoupling => split_select_advance_ixCoupling ! advance coupling loop iterator procedure :: advance_iStateTypeSplit => split_select_advance_iStateTypeSplit ! advance coupling loop iterator procedure :: advance_ixStateThenDomain => split_select_advance_ixStateThenDomain ! advance coupling loop iterator procedure :: advance_iDomainSplit => split_select_advance_iDomainSplit ! advance coupling loop iterator + procedure :: advance_ixSolution => split_select_advance_ixSolution ! advance coupling loop iterator end type split_select_type contains @@ -370,7 +372,7 @@ subroutine opSplittin(& if (split_select % stateThenDomain.eqv..true.) then ixStateThenDomain=split_select % ixStateThenDomain !write(*,*) "C:",ixStateThenDomain,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit - if (ixStateThenDomain.gt.(1+tryDomainSplit)) then + if (ixStateThenDomain > (1+tryDomainSplit)) then ixStateThenDomain=ixStateThenDomain-1; split_select % ixStateThenDomain = ixStateThenDomain ! correct index needed after loop exit split_select % stateThenDomain=.false. ! eqivalent to exiting the stateThenDomain loop end if @@ -381,19 +383,21 @@ subroutine opSplittin(& call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs call split_select % initialize_iDomainSplit; split_select % domainSplit=.true. end if -!! domainSplit: do !iDomainSplit=1,nDomainSplit ! domain splitting loop if (split_select % domainSplit.eqv..true.) then iDomainSplit=split_select % iDomainSplit !write(*,*) "D:",iDomainSplit,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit if (split_select % iDomainSplit > nDomainSplit) then split_select % domainSplit=.false. - !exit domainSplit end if end if if (split_select % domainSplit.eqv..true.) then - - solution: do ixSolution=1,nSolutions ! trial with the vector then scalar solution - + call split_select % initialize_ixSolution; split_select % solution=.true. + solution: do !ixSolution=1,nSolutions ! trial with the vector then scalar solution + ixSolution=split_select % ixSolution + if (split_select % ixSolution > nsolutions) then + split_select % solution=.false. + exit solution + end if call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs stateSplit: do iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) @@ -405,7 +409,7 @@ subroutine opSplittin(& !call update_stateFilter; if (return_flag.eqv..true.) return ! get the mask for the state subset - return for a non-zero error code call validate_split ! verify that the split is valid if (cycle_domainSplit) then; call split_select % advance_iDomainSplit; cycle split_select_loop; end if - if (cycle_solution) cycle solution + if (cycle_solution) then; call split_select % advance_ixSolution; cycle solution; end if if (return_flag.eqv..true.) return ! return for a non-zero error code call save_recover ! save/recover copies of variables and fluxes @@ -422,7 +426,7 @@ subroutine opSplittin(& call split_select % advance_ixCoupling; call split_select % initialize_flags; cycle split_select_loop ! cycle loops if necessary end if if (cycle_stateThenDomain) then; call split_select % advance_ixStateThenDomain; split_select % domainSplit=.false.; cycle split_select_loop; end if ! deactivate flags for inner loops - if (cycle_solution) cycle solution + if (cycle_solution) then; call split_select % advance_ixSolution; cycle solution; end if call confirm_variable_updates; if (return_flag.eqv..true.) return ! check that state variables updated - return if error @@ -432,14 +436,13 @@ subroutine opSplittin(& if (return_flag.eqv..true.) return ! return if error end do stateSplit ! solution with split layers - + call split_select % advance_ixSolution end do solution ! trial with the full layer solution then the split layer solution if (split_select % stateThenDomain.eqv..true.) then call finalize_solution ! final steps following solution loop call split_select % advance_iDomainSplit end if end if ! domainSplit -!! end do domainSplit ! domain type splitting loop if (split_select % domainSplit.eqv..false.) then if (split_select % stateThenDomain.eqv..true.) call split_select % advance_ixStateThenDomain end if @@ -1140,6 +1143,12 @@ subroutine split_select_advance_iDomainSplit(split_select) split_select % iDomainSplit = split_select % iDomainSplit + 1 end subroutine split_select_advance_iDomainSplit +subroutine split_select_advance_ixSolution(split_select) + ! *** Advance index for solution loop *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % ixSolution = split_select % ixSolution + 1 +end subroutine split_select_advance_ixSolution + subroutine split_select_load_indices(split_select,ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) ! *** load operator splitting indices for split_select_type class *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector @@ -1181,6 +1190,12 @@ subroutine split_select_initialize_iDomainSplit(split_select) split_select % iDomainSplit = 1 end subroutine split_select_initialize_iDomainSplit +subroutine split_select_initialize_ixSolution(split_select) + ! *** initialize operator splitting indices for split_select_type class *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % ixSolution = 1 +end subroutine split_select_initialize_ixSolution + subroutine split_select_compute_stateMask(split_select,indx_data,err,cmessage,message,return_flag) ! *** Get the mask for the state subset *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector From 70183c156583de7c866239a39c05af2f25c3bbc4 Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 23 Jan 2024 02:40:58 -0600 Subject: [PATCH 1075/1472] Additional operations from opSplittin's solution loop transferred to split_select loop. --- build/source/engine/opSplittin.f90 | 128 ++++++++++++++++------------- 1 file changed, 72 insertions(+), 56 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 8ac6b0996..11777d2cd 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -349,54 +349,60 @@ subroutine opSplittin(& call split_select % initialize_flags ! initialize loop control flags split_select_loop: do iSplit=1,500 !write(*,*) "A:",ixCoupling,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit - if (split_select % domainSplit.eqv..false.) then - if (split_select % stateThenDomain.eqv..false.) then - if (split_select % stateTypeSplitting.eqv..false.) then - ixCoupling=split_select % ixCoupling; if (ixCoupling.gt.nCoupling) exit split_select_loop ! exit if all splits are exhausted - call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs - call split_select % initialize_iStateTypeSplit; split_select % stateTypeSplitting=.true. - end if - if (split_select % stateTypeSplitting.eqv..true.) then - iStateTypeSplit=split_select % iStateTypeSplit; if (iStateTypeSplit.gt.nStateTypeSplit) split_select % stateTypeSplitting=.false. -!write(*,*) "B:",iStateTypeSplit,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit + if (split_select % solution.eqv..false.) then + if (split_select % domainSplit.eqv..false.) then + if (split_select % stateThenDomain.eqv..false.) then + if (split_select % stateTypeSplitting.eqv..false.) then + ixCoupling=split_select % ixCoupling; if (ixCoupling.gt.nCoupling) exit split_select_loop ! exit if all splits are exhausted + call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs + call split_select % initialize_iStateTypeSplit; split_select % stateTypeSplitting=.true. + end if + if (split_select % stateTypeSplitting.eqv..true.) then + iStateTypeSplit=split_select % iStateTypeSplit; if (iStateTypeSplit.gt.nStateTypeSplit) split_select % stateTypeSplitting=.false. +!wri te(*,*) "B:",iStateTypeSplit,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit + end if end if end if end if if (split_select % stateTypeSplitting.eqv..true.) then - if (split_select % domainSplit.eqv..false.) then - if (split_select % stateThenDomain.eqv..false.) then - ! first try the state type split, then try the domain split within a given state type - call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split - call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.true. - end if - if (split_select % stateThenDomain.eqv..true.) then - ixStateThenDomain=split_select % ixStateThenDomain -!write(*,*) "C:",ixStateThenDomain,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit - if (ixStateThenDomain > (1+tryDomainSplit)) then - ixStateThenDomain=ixStateThenDomain-1; split_select % ixStateThenDomain = ixStateThenDomain ! correct index needed after loop exit - split_select % stateThenDomain=.false. ! eqivalent to exiting the stateThenDomain loop - end if - end if - end if - if (split_select % stateThenDomain.eqv..true.) then + if (split_select % solution.eqv..false.) then if (split_select % domainSplit.eqv..false.) then - call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs - call split_select % initialize_iDomainSplit; split_select % domainSplit=.true. + if (split_select % stateThenDomain.eqv..false.) then + ! first try the state type split, then try the domain split within a given state type + call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split + call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.true. + end if + if (split_select % stateThenDomain.eqv..true.) then ! stateThenDomain loop + ixStateThenDomain=split_select % ixStateThenDomain +!write (*,*) "C:",ixStateThenDomain,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit + if (ixStateThenDomain > (1+tryDomainSplit)) then + ixStateThenDomain=ixStateThenDomain-1; split_select % ixStateThenDomain = ixStateThenDomain ! correct index needed after loop exit + split_select % stateThenDomain=.false. ! eqivalent to exiting the stateThenDomain loop + end if + end if end if - if (split_select % domainSplit.eqv..true.) then - iDomainSplit=split_select % iDomainSplit -!write(*,*) "D:",iDomainSplit,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit - if (split_select % iDomainSplit > nDomainSplit) then - split_select % domainSplit=.false. - end if + end if + if (split_select % stateThenDomain.eqv..true.) then + if (split_select % solution.eqv..false.) then + if (split_select % domainSplit.eqv..false.) then + call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs + call split_select % initialize_iDomainSplit; split_select % domainSplit=.true. + end if + if (split_select % domainSplit.eqv..true.) then + iDomainSplit=split_select % iDomainSplit +!write( *,*) "D:",iDomainSplit,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit + if (split_select % iDomainSplit > nDomainSplit) then + split_select % domainSplit=.false. + end if + end if end if if (split_select % domainSplit.eqv..true.) then - call split_select % initialize_ixSolution; split_select % solution=.true. + if (split_select % solution.eqv..false.) then; call split_select % initialize_ixSolution; split_select % solution=.true.; end if + if (split_select % solution.eqv..true.) then solution: do !ixSolution=1,nSolutions ! trial with the vector then scalar solution ixSolution=split_select % ixSolution if (split_select % ixSolution > nsolutions) then - split_select % solution=.false. - exit solution + split_select % solution=.false.; exit solution end if call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs stateSplit: do iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) @@ -408,7 +414,7 @@ subroutine opSplittin(& if (return_flag.eqv..true.) return !call update_stateFilter; if (return_flag.eqv..true.) return ! get the mask for the state subset - return for a non-zero error code call validate_split ! verify that the split is valid - if (cycle_domainSplit) then; call split_select % advance_iDomainSplit; cycle split_select_loop; end if + if (cycle_domainSplit) then; call split_select % advance_iDomainSplit; split_select % solution=.false.; cycle split_select_loop; end if if (cycle_solution) then; call split_select % advance_ixSolution; cycle solution; end if if (return_flag.eqv..true.) return ! return for a non-zero error code call save_recover ! save/recover copies of variables and fluxes @@ -425,44 +431,53 @@ subroutine opSplittin(& if (cycle_coupling) then call split_select % advance_ixCoupling; call split_select % initialize_flags; cycle split_select_loop ! cycle loops if necessary end if - if (cycle_stateThenDomain) then; call split_select % advance_ixStateThenDomain; split_select % domainSplit=.false.; cycle split_select_loop; end if ! deactivate flags for inner loops + if (cycle_stateThenDomain) then; call split_select % advance_ixStateThenDomain; split_select % domainSplit=.false.; split_select % solution=.false.; cycle split_select_loop; end if ! deactivate flags for inner loops if (cycle_solution) then; call split_select % advance_ixSolution; cycle solution; end if call confirm_variable_updates; if (return_flag.eqv..true.) return ! check that state variables updated - return if error call success_check ! check for success - if (exit_stateThenDomain) then; call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.false.; split_select % domainSplit=.false.; exit solution; end if ! exit loops if necessary -- exit last available loop to avoid unnecessary operations -- deactivate inner loops - if (exit_solution) exit solution + if (exit_stateThenDomain) then; call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.false.; split_select % domainSplit=.false.; split_select % solution=.false.; exit solution; end if ! exit loops if necessary -- exit last available loop to avoid unnecessary operations -- deactivate inner loops + if (exit_solution) then; split_select % solution=.false.; exit solution; end if if (return_flag.eqv..true.) return ! return if error end do stateSplit ! solution with split layers call split_select % advance_ixSolution end do solution ! trial with the full layer solution then the split layer solution + end if ! end solution loop + if (split_select % solution.eqv..false.) then if (split_select % stateThenDomain.eqv..true.) then call finalize_solution ! final steps following solution loop call split_select % advance_iDomainSplit end if + end if end if ! domainSplit - if (split_select % domainSplit.eqv..false.) then - if (split_select % stateThenDomain.eqv..true.) call split_select % advance_ixStateThenDomain - end if + if (split_select % solution.eqv..false.) then + if (split_select % domainSplit.eqv..false.) then + if (split_select % stateThenDomain.eqv..true.) call split_select % advance_ixStateThenDomain + end if + end if end if ! stateThenDomain - if (split_select % domainSplit.eqv..false.) then - if (split_select % stateThenDomain.eqv..false.) then - call finalize_stateThenDomain; if (return_flag.eqv..true.) return ! final steps following the stateThenDomain loop - call split_select % advance_iStateTypeSplit + if (split_select % solution.eqv..false.) then + if (split_select % domainSplit.eqv..false.) then + if (split_select % stateThenDomain.eqv..false.) then + call finalize_stateThenDomain; if (return_flag.eqv..true.) return ! final steps following the stateThenDomain loop + call split_select % advance_iStateTypeSplit + end if end if end if end if ! stateTypeSplitting - if (split_select % domainSplit.eqv..false.) then - if (split_select % stateThenDomain.eqv..false.) then - if (split_select % stateTypeSplitting.eqv..false.) then - call finalize_stateTypeSplitting - if (exit_coupling) then - call split_select % initialize_ixCoupling; exit split_select_loop ! success = exit the coupling loop + if (split_select % solution.eqv..false.) then + if (split_select % domainSplit.eqv..false.) then + if (split_select % stateThenDomain.eqv..false.) then + if (split_select % stateTypeSplitting.eqv..false.) then + call finalize_stateTypeSplitting + if (exit_coupling) then + call split_select % initialize_ixCoupling; exit split_select_loop ! success = exit the coupling loop + end if + + call split_select % advance_ixCoupling end if - - call split_select % advance_ixCoupling end if end if end if @@ -1117,6 +1132,7 @@ subroutine split_select_initialize_flags(split_select) split_select % stateTypeSplitting=.false. split_select % stateThenDomain=.false. split_select % domainSplit=.false. + split_select % solution=.false. end subroutine split_select_initialize_flags subroutine split_select_advance_ixCoupling(split_select) From 048b1aaf212953b07eaf966a9c0c764ac6cef114 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 23 Jan 2024 21:13:04 +0900 Subject: [PATCH 1076/1472] put default values back on IDA nonlinear iteration values (not enthalpy related) -- this means maxiter param does not do anything --- build/source/engine/summaSolve4ida.f90 | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 820038a4c..a0ecbed8f 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -748,7 +748,7 @@ end subroutine setInitialCondition ! ---------------------------------------------------------------- ! setSolverParams: private routine to set parameters in IDA solver ! ---------------------------------------------------------------- -subroutine setSolverParams(dt_cur,nonlin_iter,ida_mem,retval) +subroutine setSolverParams(dt_cur,fail_iter_param,ida_mem,retval) !======= Inclusions =========== USE, intrinsic :: iso_c_binding @@ -759,16 +759,16 @@ subroutine setSolverParams(dt_cur,nonlin_iter,ida_mem,retval) ! calling variables real(rkind),intent(in) :: dt_cur ! current whole time step - integer,intent(in) :: nonlin_iter ! maximum number of nonlinear iterations, default = 4, set in parameters + integer(i4b),intent(in) :: fail_iter_param ! maximum number of error test and convergence test failures, default 10, set in params type(c_ptr),intent(inout) :: ida_mem ! IDA memory integer(i4b),intent(out) :: retval ! return value !======= Internals ============ + integer,parameter :: nonlin_iter = 4 ! maximum number of nonlinear iterations before reducing step size, default = 4 integer,parameter :: max_order = 5 ! maximum BDF order, default and max = 5 real(qp),parameter :: coef_nonlin = 0.33 ! coefficient in the nonlinear convergence test, default = 0.33 - integer,parameter :: acurtest_fail = 50 ! maximum number of error test failures, default = 10 - integer,parameter :: convtest_fail = 50 ! maximum number of convergence test failures, default = 10 integer(c_long),parameter :: max_step = 999999 ! maximum number of steps, default = 500 + integer :: fail_iter ! maximum number of error test and convergence test failures, default 10 real(qp) :: h_max ! maximum stepsize, default = infinity real(qp),parameter :: h_init = 0 ! initial stepsize @@ -784,12 +784,14 @@ subroutine setSolverParams(dt_cur,nonlin_iter,ida_mem,retval) retval = FIDASetMaxNonlinIters(ida_mem, nonlin_iter) if (retval /= 0) return + ! fail_iter = fail_iter_param ! use SUMMA parameter + fail_iter = 10 ! default, maybe leave here ! Set maximum number of convergence test failures - retval = FIDASetMaxConvFails(ida_mem, convtest_fail) + retval = FIDASetMaxConvFails(ida_mem, fail_iter) if (retval /= 0) return ! Set maximum number of error test failures - retval = FIDASetMaxErrTestFails(ida_mem, acurtest_fail) + retval = FIDASetMaxErrTestFails(ida_mem, fail_iter) if (retval /= 0) return ! Set maximum number of steps From 1d4bf6a60cba74ed759b3ac4bacdea07b1ce72d6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 23 Jan 2024 21:20:12 +0900 Subject: [PATCH 1077/1472] maxiter now for only numrec and kinsol --- build/source/dshare/get_ixname.f90 | 2 +- build/source/dshare/popMetadat.f90 | 2 +- build/source/dshare/var_lookup.f90 | 2 +- build/source/engine/summaSolve4ida.f90 | 9 +++------ 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 453965f03..921e6d6ec 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -381,7 +381,7 @@ function get_ixparam(varName) case('maxstep' ); get_ixparam = iLookPARAM%maxstep ! maximum length of the time step numrec case('be_steps' ); get_ixparam = iLookPARAM%be_steps ! minimum number of substeps to take in a maxstep numrec case('wimplicit' ); get_ixparam = iLookPARAM%wimplicit ! weight assigned to start-of-step fluxes numrec, not currently used - case('maxiter' ); get_ixparam = iLookPARAM%maxiter ! maximum number of iterations numrec, kinsol, or nonlinear iterations ida + case('maxiter' ); get_ixparam = iLookPARAM%maxiter ! maximum number of iterations numrec and kinsol case('relConvTol_liquid' ); get_ixparam = iLookPARAM%relConvTol_liquid ! relative convergence tolerance for vol frac liq water (-) numrec case('absConvTol_liquid' ); get_ixparam = iLookPARAM%absConvTol_liquid ! absolute convergence tolerance for vol frac liq water (-) numrec case('relConvTol_matric' ); get_ixparam = iLookPARAM%relConvTol_matric ! relative convergence tolerance for matric head (-) numrec diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 8be590e73..adc80e7c0 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -258,7 +258,7 @@ subroutine popMetadat(err,message) mpar_meta(iLookPARAM%maxstep) = var_info('maxstep' , 'maximum length of the time step numrec' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%be_steps) = var_info('be_steps' , 'minimum number of substeps to take in a maxstep numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%wimplicit) = var_info('wimplicit' , 'weight assigned to the start-of-step fluxes ,numrec, not currently used', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%maxiter) = var_info('maxiter' , 'maximum number of iterations numrec, kinsol, or nonlinear iterations ida', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%maxiter) = var_info('maxiter' , 'maximum number of iterations numrec and kinsol' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%relConvTol_liquid) = var_info('relConvTol_liquid' , 'relative convergence tolerance for vol frac liq water numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%absConvTol_liquid) = var_info('absConvTol_liquid' , 'absolute convergence tolerance for vol frac liq water numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%relConvTol_matric) = var_info('relConvTol_matric' , 'relative convergence tolerance for matric head numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index dd27b305d..8b13ccc7e 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -285,7 +285,7 @@ MODULE var_lookup integer(i4b) :: maxstep = integerMissing ! maximum length of the time step integer(i4b) :: be_steps = integerMissing ! minimum number of substeps to take in a maxstep numrec integer(i4b) :: wimplicit = integerMissing ! weight assigned to the start-of-step fluxes - integer(i4b) :: maxiter = integerMissing ! maximum number of iteration + integer(i4b) :: maxiter = integerMissing ! maximum number of iterations numrec and kinsol integer(i4b) :: relConvTol_liquid = integerMissing ! relative convergence tolerance for vol frac liq water (-) integer(i4b) :: absConvTol_liquid = integerMissing ! absolute convergence tolerance for vol frac liq water (-) integer(i4b) :: relConvTol_matric = integerMissing ! relative convergence tolerance for matric head (-) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index a0ecbed8f..f6b4f29bf 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -458,7 +458,7 @@ subroutine summaSolve4ida( & if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetStopTime'; return; endif ! Set solver parameters at end of setup - call setSolverParams(dt_cur, nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)), ida_mem, retval) + call setSolverParams(dt_cur, ida_mem, retval) if (retval /= 0) then; err=20; message='summaSolve4ida: error in setSolverParams'; return; endif ! Disable error messages and warnings @@ -748,7 +748,7 @@ end subroutine setInitialCondition ! ---------------------------------------------------------------- ! setSolverParams: private routine to set parameters in IDA solver ! ---------------------------------------------------------------- -subroutine setSolverParams(dt_cur,fail_iter_param,ida_mem,retval) +subroutine setSolverParams(dt_cur,ida_mem,retval) !======= Inclusions =========== USE, intrinsic :: iso_c_binding @@ -759,7 +759,6 @@ subroutine setSolverParams(dt_cur,fail_iter_param,ida_mem,retval) ! calling variables real(rkind),intent(in) :: dt_cur ! current whole time step - integer(i4b),intent(in) :: fail_iter_param ! maximum number of error test and convergence test failures, default 10, set in params type(c_ptr),intent(inout) :: ida_mem ! IDA memory integer(i4b),intent(out) :: retval ! return value @@ -768,7 +767,7 @@ subroutine setSolverParams(dt_cur,fail_iter_param,ida_mem,retval) integer,parameter :: max_order = 5 ! maximum BDF order, default and max = 5 real(qp),parameter :: coef_nonlin = 0.33 ! coefficient in the nonlinear convergence test, default = 0.33 integer(c_long),parameter :: max_step = 999999 ! maximum number of steps, default = 500 - integer :: fail_iter ! maximum number of error test and convergence test failures, default 10 + integer,parameter :: fail_iter = 10 ! maximum number of error test and convergence test failures, default 10 real(qp) :: h_max ! maximum stepsize, default = infinity real(qp),parameter :: h_init = 0 ! initial stepsize @@ -784,8 +783,6 @@ subroutine setSolverParams(dt_cur,fail_iter_param,ida_mem,retval) retval = FIDASetMaxNonlinIters(ida_mem, nonlin_iter) if (retval /= 0) return - ! fail_iter = fail_iter_param ! use SUMMA parameter - fail_iter = 10 ! default, maybe leave here ! Set maximum number of convergence test failures retval = FIDASetMaxConvFails(ida_mem, fail_iter) if (retval /= 0) return From c6a40a9515242b5215bd1c83a0eb290dd9e80107 Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 23 Jan 2024 06:23:36 -0600 Subject: [PATCH 1078/1472] All opeartions from opSplittin's solution loop now handled by split_select loop. --- build/source/engine/opSplittin.f90 | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 11777d2cd..d752acff5 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -399,11 +399,13 @@ subroutine opSplittin(& if (split_select % domainSplit.eqv..true.) then if (split_select % solution.eqv..false.) then; call split_select % initialize_ixSolution; split_select % solution=.true.; end if if (split_select % solution.eqv..true.) then - solution: do !ixSolution=1,nSolutions ! trial with the vector then scalar solution +! solution: do !ixSolution=1,nSolutions ! trial with the vector then scalar solution ixSolution=split_select % ixSolution if (split_select % ixSolution > nsolutions) then - split_select % solution=.false.; exit solution + split_select % solution=.false. end if + end if + if (split_select % solution.eqv..true.) then call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs stateSplit: do iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) @@ -415,7 +417,7 @@ subroutine opSplittin(& !call update_stateFilter; if (return_flag.eqv..true.) return ! get the mask for the state subset - return for a non-zero error code call validate_split ! verify that the split is valid if (cycle_domainSplit) then; call split_select % advance_iDomainSplit; split_select % solution=.false.; cycle split_select_loop; end if - if (cycle_solution) then; call split_select % advance_ixSolution; cycle solution; end if + if (cycle_solution) then; call split_select % advance_ixSolution; cycle split_select_loop; end if if (return_flag.eqv..true.) return ! return for a non-zero error code call save_recover ! save/recover copies of variables and fluxes @@ -432,18 +434,22 @@ subroutine opSplittin(& call split_select % advance_ixCoupling; call split_select % initialize_flags; cycle split_select_loop ! cycle loops if necessary end if if (cycle_stateThenDomain) then; call split_select % advance_ixStateThenDomain; split_select % domainSplit=.false.; split_select % solution=.false.; cycle split_select_loop; end if ! deactivate flags for inner loops - if (cycle_solution) then; call split_select % advance_ixSolution; cycle solution; end if + if (cycle_solution) then; call split_select % advance_ixSolution; cycle split_select_loop; end if call confirm_variable_updates; if (return_flag.eqv..true.) return ! check that state variables updated - return if error call success_check ! check for success - if (exit_stateThenDomain) then; call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.false.; split_select % domainSplit=.false.; split_select % solution=.false.; exit solution; end if ! exit loops if necessary -- exit last available loop to avoid unnecessary operations -- deactivate inner loops - if (exit_solution) then; split_select % solution=.false.; exit solution; end if + if (exit_stateThenDomain) then; call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.false.; split_select % domainSplit=.false.; split_select % solution=.false.; exit stateSplit; end if ! exit loops if necessary -- exit last available loop to avoid unnecessary operations -- deactivate inner loops + if (exit_solution) then; split_select % solution=.false.; exit stateSplit; end if if (return_flag.eqv..true.) return ! return if error end do stateSplit ! solution with split layers - call split_select % advance_ixSolution - end do solution ! trial with the full layer solution then the split layer solution + if (split_select % solution.eqv..true.) then + if (split_select % stateThenDomain.eqv..true.) then + call split_select % advance_ixSolution + end if + end if +! end do solution ! trial with the full layer solution then the split layer solution end if ! end solution loop if (split_select % solution.eqv..false.) then if (split_select % stateThenDomain.eqv..true.) then From 683589a7ee2f53a8b08528f976504fb817675eef Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 23 Jan 2024 23:20:54 +0900 Subject: [PATCH 1079/1472] missing Trial name, bug fix --- build/source/engine/eval8summa.f90 | 207 ++++++------- build/source/engine/eval8summaWithPrime.f90 | 304 ++++++++++---------- build/source/engine/varSubstep.f90 | 9 +- 3 files changed, 261 insertions(+), 259 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 5cca1fbab..cbe9e4095 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -200,6 +200,7 @@ subroutine eval8summa(& real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) ! enthalpy + real(rkind) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3 real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector of enthalpy for snow+soil layers (J m-3) ! other local variables @@ -387,96 +388,96 @@ subroutine eval8summa(& ! compute H_T without phase change call t2enthalpy(& ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value for canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) + scalarCanairTempTrial, & ! intent(in): trial value for canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): trial value for enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpyTrial, & ! intent(out): trial value for enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy of each snow+soil layer (J m-3) ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! *** compute volumetric heat capacity C_p = dH_T/dT call computHeatCap(& ! input: control variables - nLayers, & ! intent(in): number of layers (soil+snow) - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) + nLayers, & ! intent(in): number of layers (soil+snow) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) ! input output data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - diag_data, & ! intent(inout): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) - scalarCanopyTempTrial - scalarCanopyTemp, & ! intent(in): delta value for canopy temperature (K) - scalarCanopyEnthalpyTrial - scalarCanopyEnthalpy, & ! intent(in): delta value for temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): trial vector of volumetric fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial vector of temperature - mLayerTempTrial - mLayerTemp, & ! intent(in): delta vector of temperature - mLayerEnthalpyTrial - mLayerEnthalpy, & ! intent(in): delta vector of temperature component of enthalpy for snow and soil - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) + scalarCanopyTempTrial - scalarCanopyTemp, & ! intent(in): delta value for canopy temperature (K) + scalarCanopyEnthalpyTrial - scalarCanopyEnthalpy, & ! intent(in): delta value for temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): trial vector of volumetric fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial vector of temperature + mLayerTempTrial - mLayerTemp, & ! intent(in): delta vector of temperature + mLayerEnthalpyTrial - mLayerEnthalpy, & ! intent(in): delta vector of temperature component of enthalpy for snow and soil + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output - heatCapVegTrial, & ! intent(out): trial value for volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(out): trial vector of heat capacity for snow and soil - dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + heatCapVegTrial, & ! intent(out): trial value for volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(out): trial vector of heat capacity for snow and soil + dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif else if(ixHowHeatCap == closedForm)then call computHeatCapAnalytic(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) + scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! input output data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - diag_data, & ! intent(inout): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! output - heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow - dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow + dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature ! output: error control err,cmessage) ! intent(out): error control endif !(choice of how compute heat capacity) @@ -484,41 +485,41 @@ subroutine eval8summa(& ! compute multiplier of state vector call computStatMult(& ! input - heatCapVegTrial, & ! intent(in): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(in): volumetric heat capacity of soil and snow - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers + heatCapVegTrial, & ! intent(in): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(in): volumetric heat capacity of soil and snow + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers ! output - sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) - err,cmessage) ! intent(out): error control + sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) ! update thermal conductivity call computThermConduct(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - nLayers, & ! intent(in): total number of layers - canopyDepth, & ! intent(in): canopy depth (m) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + nLayers, & ! intent(in): total number of layers + canopyDepth, & ! intent(in): canopy depth (m) ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value of canopy liquid water (kg m-2) - mLayerTempTrial, & ! intent(in): trial temperature of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value of canopy liquid water (kg m-2) + mLayerTempTrial, & ! intent(in): trial temperature of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) ! input: pre-computed derivatives - mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) ! input/output: data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! output: derivatives - dThermalC_dWatAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dWatBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dTempAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dTempBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dWatAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dTempAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if @@ -539,20 +540,20 @@ subroutine eval8summa(& ! compute C_m call computCm(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux ! input: state variables - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (-) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (-) ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices ! output - canopyCmTrial, & ! intent(out): Cm for vegetation (J kg K-1) - mLayerCmTrial, & ! intent(out): Cm for soil and snow (J kg K-1) - dCm_dTk, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) - dCm_dTkCanopy, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) - err,cmessage) ! intent(out): error control + canopyCmTrial, & ! intent(out): Cm for vegetation (J kg K-1) + mLayerCmTrial, & ! intent(out): Cm for soil and snow (J kg K-1) + dCm_dTk, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) + dCm_dTkCanopy, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) + err,cmessage) ! intent(out): error control else canopyCmTrial = 0._qp mLayerCmTrial = 0._qp diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 44460f477..38e486f65 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -356,23 +356,23 @@ subroutine eval8summaWithPrime(& call varExtract(& ! input - stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers + stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy - scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K) - scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): derivative of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2) + scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K) + scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): derivative of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K) - mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (-) - mLayerMatricHeadPrime, & ! intent(inout): derivative of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): derivative of liquid water matric potential (m) + mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K) + mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (-) + mLayerMatricHeadPrime, & ! intent(inout): derivative of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): derivative of liquid water matric potential (m) ! output: variables for the aquifer - scalarAquiferStoragePrime,& ! intent(inout): derivative of storage of water in the aquifer (m) + scalarAquiferStoragePrime, & ! intent(inout): derivative of storage of water in the aquifer (m) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) @@ -380,35 +380,35 @@ subroutine eval8summaWithPrime(& ! update diagnostic variables and derivatives call updateVarsWithPrime(& ! input - .false., & ! intent(in): logical flag if computing for Jacobian update - .false., & ! intent(in): logical flag to adjust temperature to account for the energy - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + .false., & ! intent(in): logical flag if computing for Jacobian update + .false., & ! intent(in): logical flag to adjust temperature to account for the energy + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - scalarCanopyTempPrime, & ! intent(inout): trial value of time derivative canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): trial value of time derivative canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): trial value of time derivative canopy liquid water (kg m-2) - scalarCanopyIcePrime, & ! intent(inout): trial value of time derivative canopy ice content (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! intent(inout): trial vector of time derivative layer temperature (K) - mLayerVolFracWatPrime, & ! intent(inout): trial vector of time derivative volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): trial vector of time derivative volumetric liquid water content (-) - mLayerVolFracIcePrime, & ! intent(inout): trial vector of time derivative volumetric ice water content (-) - mLayerMatricHeadPrime, & ! intent(inout): trial vector of time derivative total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): trial vector of time derivative liquid water matric potential (m) + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + scalarCanopyTempPrime, & ! intent(inout): trial value of time derivative canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): trial value of time derivative canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): trial value of time derivative canopy liquid water (kg m-2) + scalarCanopyIcePrime, & ! intent(inout): trial value of time derivative canopy ice content (kg m-2) + ! output: variables for th snow-soil domain + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempPrime, & ! intent(inout): trial vector of time derivative layer temperature (K) + mLayerVolFracWatPrime, & ! intent(inout): trial vector of time derivative volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): trial vector of time derivative volumetric liquid water content (-) + mLayerVolFracIcePrime, & ! intent(inout): trial vector of time derivative volumetric ice water content (-) + mLayerMatricHeadPrime, & ! intent(inout): trial vector of time derivative total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): trial vector of time derivative liquid water matric potential (m) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) @@ -419,30 +419,30 @@ subroutine eval8summaWithPrime(& ! compute H_T prime without phase change call t2enthalpyPrime(& ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices ! input: state variables for the vegetation canopy - scalarCanairTempPrime, & ! intent(in): prime value for canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) - scalarCanopyTempPrime, & ! intent(in): prime value for canopy temperature (K) - scalarCanopyWatPrime, & ! intent(in): prime value for canopy total water (kg m-2) + scalarCanairTempPrime, & ! intent(in): prime value for canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) + scalarCanopyTempPrime, & ! intent(in): prime value for canopy temperature (K) + scalarCanopyWatPrime, & ! intent(in): prime value for canopy total water (kg m-2) ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerTempPrime, & ! intent(in): prime vector of layer temperature (K) - mLayerVolFracWatPrime, & ! intent(in): prime vector of volumetric total water content (-) - mLayerMatricHeadPrime, & ! intent(in): prime vector of total water matric potential (m) + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerTempPrime, & ! intent(in): prime vector of layer temperature (K) + mLayerVolFracWatPrime, & ! intent(in): prime vector of volumetric total water content (-) + mLayerMatricHeadPrime, & ! intent(in): prime vector of total water matric potential (m) ! input: pre-computed derivatives - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy prime - scalarCanairEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyPrime, & ! intent(out): prime vector of temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyPrime, & ! intent(out): prime vector of temperature component of enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyPrime, & ! intent(out): prime vector of temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyPrime, & ! intent(out): prime vector of temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -450,73 +450,73 @@ subroutine eval8summaWithPrime(& ! *** compute volumetric heat capacity C_p = dH_T/dT call computHeatCap(& ! input: control variables - nLayers, & ! intent(in): number of layers (soil+snow) - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) + nLayers, & ! intent(in): number of layers (soil+snow) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) ! input output data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - diag_data, & ! intent(inout): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value of the liquid water on the vegetation canopy (kg m-2) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyTempPrime, & ! intent(in): prime value of canopy temperature (K) - scalarCanopyEnthalpyPrime, & ! intent(in): prime value of temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial vector of temperature - mLayerTempPrime, & ! intent(in): prime vector of temperature - mLayerEnthalpyPrime, & ! intent(in): prime vector of temperature component of enthalpy for snow and soil - mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) + scalarCanopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value of the liquid water on the vegetation canopy (kg m-2) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyTempPrime, & ! intent(in): prime value of canopy temperature (K) + scalarCanopyEnthalpyPrime, & ! intent(in): prime value of temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial vector of temperature + mLayerTempPrime, & ! intent(in): prime vector of temperature + mLayerEnthalpyPrime, & ! intent(in): prime vector of temperature component of enthalpy for snow and soil + mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output - heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil - dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil + dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif else if(ixHowHeatCap == closedForm)then call computHeatCapAnalytic(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) + scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! input output data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - diag_data, & ! intent(inout): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! output - heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow - dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow + dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -525,41 +525,41 @@ subroutine eval8summaWithPrime(& ! compute multiplier of state vector call computStatMult(& ! input - heatCapVegTrial, & ! intent(in): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(in): volumetric heat capacity of soil and snow - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers + heatCapVegTrial, & ! intent(in): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(in): volumetric heat capacity of soil and snow + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers ! output - sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) - err,cmessage) ! intent(out): error control + sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) ! update thermal conductivity call computThermConduct(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - nLayers, & ! intent(in): total number of layers - canopyDepth, & ! intent(in): canopy depth (m) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + nLayers, & ! intent(in): total number of layers + canopyDepth, & ! intent(in): canopy depth (m) ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value of canopy liquid water (kg m-2) - mLayerTempTrial, & ! intent(in): trial temperature of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value of canopy liquid water (kg m-2) + mLayerTempTrial, & ! intent(in): trial temperature of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) ! input: pre-computed derivatives - mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative in volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) ! input/output: data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output: derivatives - dThermalC_dWatAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dWatBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dTempAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dTempBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + ! output: derivative + dThermalC_dWatAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dTempAbove, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow, & ! intent(out): derivative in the thermal conductivity w.r.t. energy state in the layer above ! output: error control ! output: error control err,cmessage) ! intent(out): error control @@ -581,20 +581,20 @@ subroutine eval8summaWithPrime(& ! compute C_m call computCm(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux ! input: state variables - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (-) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (-) ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices ! output - canopyCmTrial, & ! intent(out): Cm for vegetation (J kg K-1) - mLayerCmTrial, & ! intent(out): Cm for soil and snow (J kg K-1) - dCm_dTk, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) - dCm_dTkCanopy, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) - err,cmessage) ! intent(out): error control + canopyCmTrial, & ! intent(out): Cm for vegetation (J kg K-1) + mLayerCmTrial, & ! intent(out): Cm for soil and snow (J kg K-1) + dCm_dTk, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) + dCm_dTkCanopy, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) + err,cmessage) ! intent(out): error control else canopyCmTrial = 0._qp mLayerCmTrial = 0._qp diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 641cd7d64..134f13657 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -996,19 +996,20 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): model indices ! input: state variables for the ve getation canopy - scalarCanopyIceTrial - scalarCanopyIce, & ! intent(in): delta value for canopy ice content (kg m-2) + scalarCanopyIceTrial - scalarCanopyIce, & ! intent(in): delta value for canopy ice content (kg m-2) ! input: variables for the snow-soil domain - mLayerVolFracIceTrial - mLayerVolFracIce, & ! intent(in): delta vector of volumetric ice water content (-) + mLayerVolFracIceTrial - mLayerVolFracIce, & ! intent(in): delta vector of volumetric ice water content (-) ! input/output: enthalpy scalarCanopyEnthalpyDelta_addphase, & ! intent(inout): delta value for enthalpy of the vegetation canopy (J m-3) mLayerEnthalpyDelta_addphase, & ! intent(inout): delta vector of enthalpy of each snow+soil layer (J m-3) - ! output: error control + ! output: error control err,message) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! compute energy balance, maybe should use to check for step reduction if(ixCasNrg/=integerMissing) balance(ixCasNrg) = scalarCanairEnthalpyTrial - scalarCanairEnthalpy - fluxVec(ixCasNrg)*dt !if(ixCasNrg/=integerMissing) balance(ixCasNrg) = resVec(ixCasNrg) ! should be equivalent to above, use for debugging + print*,balance(ixCasNrg),resVec(ixCasNrg),scalarCanairEnthalpyTrial, scalarCanairEnthalpy, fluxVec(ixCasNrg)*dt if(ixVegNrg/=integerMissing) balance(ixVegNrg) = scalarCanopyEnthalpyDelta_addphase - fluxVec(ixVegNrg)*dt if(nSnowSoilNrg>0)then do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) @@ -1279,7 +1280,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! -------------------------------- scalarCanairEnthalpy = scalarCanairEnthalpyTrial scalarCanopyEnthalpy = scalarCanopyEnthalpyTrial - mLayerEnthalpy = mLayerEnthalpyTrial + mLayerEnthalpy = mLayerEnthalpyTrial ! ----- ! * update prognostic variables... From 7c2b0850d2a517ca09d851c94845c90726b94451 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 23 Jan 2024 23:21:28 +0900 Subject: [PATCH 1080/1472] remove print --- build/source/engine/varSubstep.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 134f13657..1c8fdb0be 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -1009,7 +1009,6 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! compute energy balance, maybe should use to check for step reduction if(ixCasNrg/=integerMissing) balance(ixCasNrg) = scalarCanairEnthalpyTrial - scalarCanairEnthalpy - fluxVec(ixCasNrg)*dt !if(ixCasNrg/=integerMissing) balance(ixCasNrg) = resVec(ixCasNrg) ! should be equivalent to above, use for debugging - print*,balance(ixCasNrg),resVec(ixCasNrg),scalarCanairEnthalpyTrial, scalarCanairEnthalpy, fluxVec(ixCasNrg)*dt if(ixVegNrg/=integerMissing) balance(ixVegNrg) = scalarCanopyEnthalpyDelta_addphase - fluxVec(ixVegNrg)*dt if(nSnowSoilNrg>0)then do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) From 1ff8f2ba2b4714e64068623578582bd416d5845d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 24 Jan 2024 00:22:49 +0900 Subject: [PATCH 1081/1472] keep enthalpy as analytical solution instead of lookup, seems to work better (more accurate, faster) --- build/source/engine/enthalpyTemp.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index ab761fa60..48339f8ba 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -517,7 +517,7 @@ subroutine t2enthalpy(& real(rkind) :: enthAir ! enthalpy of air (J m-3) real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) real(rkind) :: enthWater ! enthalpy of total water (J m-3) - logical(lgt),parameter :: use_lookup=.true. ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function + logical(lgt),parameter :: use_lookup=.false. ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures generalVars: associate(& From a618ac076c9762a76946f45a86736f6c15c8c1de Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 24 Jan 2024 18:03:31 +0900 Subject: [PATCH 1082/1472] just a module name change for clarification --- build/source/engine/enthalpyTemp.f90 | 42 +++++++++++++------------- build/source/engine/summaSolve4ida.f90 | 4 +-- build/source/engine/varSubstep.f90 | 4 +-- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 48339f8ba..2016217a1 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -66,7 +66,7 @@ module enthalpyTemp_module public::enthalpy2t_snow public::t2enthalpy_snow public::t2enthalpy -public::t2enthalpy_addphase +public::t2enthalpyChange_addphase private::hyp_2F1_real ! define the snow look-up table used to compute temperature based on enthalpy @@ -677,21 +677,21 @@ end subroutine t2enthalpy ! ************************************************************************************************************************ -! public subroutine t2enthalpy_addphase: compute enthalpy or enthalpy prime with phase change from ice content +! public subroutine t2enthalpyChange_addphase: compute change in enthalpy or enthalpy prime with phase change from ice content change ! ************************************************************************************************************************ -subroutine t2enthalpy_addphase(& +subroutine t2enthalpyChange_addphase(& ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): model indices ! input: state variables for the vegetation canopy - scalarCanopyIce, & ! intent(in): value of canopy ice content (kg m-2) or prime ice content (kg m-2 s-1) + scalarCanopyIceDelta, & ! intent(in): value of canopy ice content (kg m-2) or prime ice content (kg m-2 s-1) ! input: variables for the snow-soil domain - mLayerVolFracIce, & ! intent(in) : vector of volumetric fraction of ice (-) or prime volumetric fraction of ice (s-1) + mLayerVolFracIceDelta, & ! intent(in) : vector of volumetric fraction of ice (-) or prime volumetric fraction of ice (s-1) ! input/output: enthalpy - scalarCanopyEnthalpy, & ! intent(inout): enthalpy of the vegetation canopy (J m-3) or enthalpy prime (J m-3 s-1) - mLayerEnthalpy, & ! intent(inout): enthalpy of each snow+soil layer (J m-3) or enthalpy prime (J m-3 s-1) + scalarCanopyEnthalpyDelta, & ! intent(inout): enthalpy of the vegetation canopy (J m-3) or enthalpy prime (J m-3 s-1) + mLayerEnthalpyDelta, & ! intent(inout): enthalpy of each snow+soil layer (J m-3) or enthalpy prime (J m-3 s-1) ! output: error control - err,message) ! intent(out): error control + err,message) ! intent(out): error control ! ------------------------------------------------------------------------------------------------------------------------- ! downwind routines USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists @@ -700,18 +700,18 @@ subroutine t2enthalpy_addphase(& ! delare dummy variables ! ------------------------------------------------------------------------------------------------------------------------- ! input: data structures - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_ilength),intent(in) :: indx_data ! model indices + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_ilength),intent(in) :: indx_data ! model indices ! input: state variables for the vegetation canopy - real(rkind),intent(in) :: scalarCanopyIce ! value for canopy ice content (kg m-2) or prime ice content (kg m-2 s-1) + real(rkind),intent(in) :: scalarCanopyIceDelta ! value for canopy ice content (kg m-2) or prime ice content (kg m-2 s-1) ! input: variables for the snow-soil domain - real(rkind),intent(in) :: mLayerVolFracIce(:) ! vector of volumetric fraction of ice (-) or prime volumetric fraction of ice (s-1) + real(rkind),intent(in) :: mLayerVolFracIceDelta(:) ! vector of volumetric fraction of ice (-) or prime volumetric fraction of ice (s-1) ! input output: enthalpy - real(rkind),intent(inout) :: scalarCanopyEnthalpy ! value for enthalpy of the vegetation canopy (J m-3) or enthalpy prime (J m-3 s-1) - real(rkind),intent(inout) :: mLayerEnthalpy(:) ! vector of enthalpy of each snow+soil layer (J m-3) or enthalpy prime (J m-3 s-1) + real(rkind),intent(inout) :: scalarCanopyEnthalpyDelta ! value for enthalpy of the vegetation canopy (J m-3) or enthalpy prime (J m-3 s-1) + real(rkind),intent(inout) :: mLayerEnthalpyDelta(:) ! vector of enthalpy of each snow+soil layer (J m-3) or enthalpy prime (J m-3 s-1) ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------------------------------- ! declare local variables character(len=128) :: cmessage ! error message in downwind routine @@ -738,7 +738,7 @@ subroutine t2enthalpy_addphase(& ! ----------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="t2enthalpy_addphase/" + err=0; message="t2enthalpyChange_addphase/" ! loop through model state variables do iState=1,size(ixMapSubset2Full) @@ -774,15 +774,15 @@ subroutine t2enthalpy_addphase(& vegVars: associate(& canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) & ! canopy depth (m) ) - scalarCanopyEnthalpy = scalarCanopyEnthalpy - LH_fus * scalarCanopyIce / canopyDepth + scalarCanopyEnthalpyDelta = scalarCanopyEnthalpyDelta - LH_fus * scalarCanopyIceDelta / canopyDepth end associate vegVars case(iname_snow) - mLayerEnthalpy(iLayer) = mLayerEnthalpy(iLayer) - iden_ice * LH_fus * mLayerVolFracIce(iLayer) + mLayerEnthalpyDelta(iLayer) = mLayerEnthalpyDelta(iLayer) - iden_ice * LH_fus * mLayerVolFracIceDelta(iLayer) case(iname_soil) - mLayerEnthalpy(iLayer) = mLayerEnthalpy(iLayer) - iden_water * LH_fus * mLayerVolFracIce(iLayer) + mLayerEnthalpyDelta(iLayer) = mLayerEnthalpyDelta(iLayer) - iden_water * LH_fus * mLayerVolFracIceDelta(iLayer) case(iname_aquifer); cycle ! aquifer: do nothing case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return @@ -793,7 +793,7 @@ subroutine t2enthalpy_addphase(& end associate generalVars -end subroutine t2enthalpy_addphase +end subroutine t2enthalpyChange_addphase !---------------------------------------------------------------------- ! private function: compute hypergeometric function with real arguments into real result diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index f6b4f29bf..ef58567d4 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -157,7 +157,7 @@ subroutine summaSolve4ida( & USE tol4ida_module,only:computWeight4ida ! weight required for tolerances USE var_lookup,only:maxvarDecisions ! maximum number of decisions USE enthalpyTempAddPrime_module,only:t2enthalpyPrime ! compute enthalpy - USE enthalpyTemp_module,only:t2enthalpy_addphase ! add phase to enthalpy + USE enthalpyTemp_module,only:t2enthalpyChange_addphase ! add phase to enthalpy !======= Declarations ========= implicit none @@ -578,7 +578,7 @@ subroutine summaSolve4ida( & mLayerEnthalpyPrime_addPhase = eqns_data%mLayerEnthalpyPrime ! compute enthalpy prime with phase change - call t2enthalpy_addphase(& + call t2enthalpyChange_addphase(& ! input: data structures eqns_data%diag_data, & ! intent(in): model diagnostic variables for a local HRU eqns_data%indx_data, & ! intent(in): model indices diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 1c8fdb0be..c4514c9cd 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -631,7 +631,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec #endif USE updateVars_module,only:updateVars ! update prognostic variables USE enthalpyTemp_module,only:t2enthalpy ! compute enthalpy - USE enthalpyTemp_module,only:t2enthalpy_addphase ! add phase to enthalpy + USE enthalpyTemp_module,only:t2enthalpyChange_addphase ! add phase to enthalpy implicit none ! model control real(rkind) ,intent(in) :: dt ! time step (s) @@ -991,7 +991,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec mLayerEnthalpyDelta_addphase = mLayerEnthalpyTrial - mLayerEnthalpy ! compute enthalpy with phase change for current values, do on delta value so only have to do once - call t2enthalpy_addphase(& + call t2enthalpyChange_addphase(& ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): model indices From 14f7c1992ba2ad93b56c83b99a3ca167ae789785 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 24 Jan 2024 05:00:16 -0600 Subject: [PATCH 1083/1472] Transferred control of iStateSplit loop index to split_select object in opSplittin --- build/source/engine/opSplittin.f90 | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index d752acff5..94e284222 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -158,7 +158,7 @@ module opSplittin_module type(var_flagVec) :: fluxMask ! integer mask defining model fluxes logical(lgt),allocatable :: stateMask(:) ! mask defining desired state variables ! flags for loop operations - logical(lgt) :: stateTypeSplitting,stateThenDomain,domainSplit,solution + logical(lgt) :: stateTypeSplitting,stateThenDomain,domainSplit,solution,stateSplit contains procedure :: initialize_flags => split_select_initialize_flags ! initialize flags that control loop operations procedure :: initialize_ixCoupling => split_select_initialize_ixCoupling ! initialize operator splitting indices @@ -166,6 +166,7 @@ module opSplittin_module procedure :: initialize_ixStateThenDomain => split_select_initialize_ixStateThenDomain ! initialize operator splitting indices procedure :: initialize_iDomainSplit => split_select_initialize_iDomainSplit ! initialize operator splitting indices procedure :: initialize_ixSolution => split_select_initialize_ixSolution ! initialize operator splitting indices + procedure :: initialize_iStateSplit => split_select_initialize_iStateSplit ! initialize operator splitting indices procedure :: get_stateMask => split_select_compute_stateMask ! compute stateMask and nSubset and load into class object procedure :: get_indices => split_select_load_indices ! load operator splitting indices into class object procedure :: advance_ixCoupling => split_select_advance_ixCoupling ! advance coupling loop iterator @@ -173,6 +174,7 @@ module opSplittin_module procedure :: advance_ixStateThenDomain => split_select_advance_ixStateThenDomain ! advance coupling loop iterator procedure :: advance_iDomainSplit => split_select_advance_iDomainSplit ! advance coupling loop iterator procedure :: advance_ixSolution => split_select_advance_ixSolution ! advance coupling loop iterator + procedure :: advance_iStateSplit => split_select_advance_iStateSplit ! advance coupling loop iterator end type split_select_type contains @@ -399,7 +401,6 @@ subroutine opSplittin(& if (split_select % domainSplit.eqv..true.) then if (split_select % solution.eqv..false.) then; call split_select % initialize_ixSolution; split_select % solution=.true.; end if if (split_select % solution.eqv..true.) then -! solution: do !ixSolution=1,nSolutions ! trial with the vector then scalar solution ixSolution=split_select % ixSolution if (split_select % ixSolution > nsolutions) then split_select % solution=.false. @@ -407,7 +408,12 @@ subroutine opSplittin(& end if if (split_select % solution.eqv..true.) then call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs - stateSplit: do iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) + call split_select % initialize_iStateSplit; split_select % stateSplit=.true.; ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) + stateSplit: do !iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) + iStateSplit=split_select % iStateSplit + if (split_select % iStateSplit > nStateSplit) then + split_select % stateSplit=.false.; exit stateSplit + end if ! define state subsets for a given split... call split_select % get_indices(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) ! may be able to remove this once all indices are fully handled by split_select @@ -443,13 +449,13 @@ subroutine opSplittin(& if (exit_solution) then; split_select % solution=.false.; exit stateSplit; end if if (return_flag.eqv..true.) return ! return if error + call split_select % advance_iStateSplit end do stateSplit ! solution with split layers if (split_select % solution.eqv..true.) then if (split_select % stateThenDomain.eqv..true.) then call split_select % advance_ixSolution end if end if -! end do solution ! trial with the full layer solution then the split layer solution end if ! end solution loop if (split_select % solution.eqv..false.) then if (split_select % stateThenDomain.eqv..true.) then @@ -1139,6 +1145,7 @@ subroutine split_select_initialize_flags(split_select) split_select % stateThenDomain=.false. split_select % domainSplit=.false. split_select % solution=.false. + split_select % stateSplit=.false. end subroutine split_select_initialize_flags subroutine split_select_advance_ixCoupling(split_select) @@ -1171,6 +1178,12 @@ subroutine split_select_advance_ixSolution(split_select) split_select % ixSolution = split_select % ixSolution + 1 end subroutine split_select_advance_ixSolution +subroutine split_select_advance_iStateSplit(split_select) + ! *** Advance index for stateSplit loop *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % iStateSplit = split_select % iStateSplit + 1 +end subroutine split_select_advance_iStateSplit + subroutine split_select_load_indices(split_select,ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) ! *** load operator splitting indices for split_select_type class *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector @@ -1218,6 +1231,12 @@ subroutine split_select_initialize_ixSolution(split_select) split_select % ixSolution = 1 end subroutine split_select_initialize_ixSolution +subroutine split_select_initialize_iStateSplit(split_select) + ! *** initialize operator splitting indices for split_select_type class *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % iStateSplit = 1 +end subroutine split_select_initialize_iStateSplit + subroutine split_select_compute_stateMask(split_select,indx_data,err,cmessage,message,return_flag) ! *** Get the mask for the state subset *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector From a85f14dae2d423ead3526d1e0fae6440e16e0528 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 25 Jan 2024 04:41:10 -0600 Subject: [PATCH 1084/1472] Hotfix for error message issue in varSubstep. --- build/source/engine/varSubstep.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 80be14455..51eec9e4f 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -187,6 +187,9 @@ subroutine varSubstep(& real(rkind) :: sumSoilCompress ! sum of total soil compression real(rkind),allocatable :: sumLayerCompress(:) ! sum of soil compression by layer ! --------------------------------------------------------------------------------------- + ! initialize error control + out_varSubstep % err=0; out_varSubstep % cmessage='varSubstep/' + ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- globalVars: associate(& @@ -240,9 +243,6 @@ subroutine varSubstep(& message => out_varSubstep % cmessage & ! intent(out): error message ) ! end association with variables in the data structures ! ********************************************************************************************************************************************************* - - ! initialize error control - err=0; message='varSubstep/' ! initialize flag for the success of the substepping failedMinimumStep=.false. From 61251f2c63a94990172f1bb1a27eaa9bb5942e5a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 25 Jan 2024 20:33:51 +0900 Subject: [PATCH 1085/1472] cmessage screwed up --- build/source/engine/summaSolve4ida.f90 | 92 +++++++++++++------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index ef58567d4..db96cca66 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -157,7 +157,7 @@ subroutine summaSolve4ida( & USE tol4ida_module,only:computWeight4ida ! weight required for tolerances USE var_lookup,only:maxvarDecisions ! maximum number of decisions USE enthalpyTempAddPrime_module,only:t2enthalpyPrime ! compute enthalpy - USE enthalpyTemp_module,only:t2enthalpyChange_addphase ! add phase to enthalpy + USE enthalpyTemp_module,only:t2enthalpyChange_addphase ! add phase to enthalpy !======= Declarations ========= implicit none @@ -257,19 +257,19 @@ subroutine summaSolve4ida( & ! link to the necessary variables associate(& ! number of state variables of a specific type - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain ! model indices - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain - ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain - ixSoilOnlyNrg => indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the soil subdomain - ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the soil subdomain - layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] named variables defining the type of layer in snow+soil domain + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain + ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain + ixSoilOnlyNrg => indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the soil subdomain + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the soil subdomain + layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] named variables defining the type of layer in snow+soil domain ) ! association to necessary variables for the residual computations ! initialize error control @@ -286,26 +286,26 @@ subroutine summaSolve4ida( & idaSucceeds = .true. ! fill eqns_data which will be required later to call eval8summaWithPrime - eqns_data%dt = dt - eqns_data%nSnow = nSnow - eqns_data%nSoil = nSoil - eqns_data%nLayers = nLayers - eqns_data%nState = nState - eqns_data%ixMatrix = ixMatrix - eqns_data%firstSubStep = firstSubStep - eqns_data%computeVegFlux = computeVegFlux - eqns_data%scalarSolution = scalarSolution - eqns_data%type_data = type_data - eqns_data%attr_data = attr_data - eqns_data%mpar_data = mpar_data - eqns_data%forc_data = forc_data - eqns_data%bvar_data = bvar_data - eqns_data%prog_data = prog_data - eqns_data%indx_data = indx_data - eqns_data%diag_data = diag_data - eqns_data%flux_data = flux_data - eqns_data%deriv_data = deriv_data - eqns_data%ixSaturation = ixSaturation + eqns_data%dt = dt + eqns_data%nSnow = nSnow + eqns_data%nSoil = nSoil + eqns_data%nLayers = nLayers + eqns_data%nState = nState + eqns_data%ixMatrix = ixMatrix + eqns_data%firstSubStep = firstSubStep + eqns_data%computeVegFlux = computeVegFlux + eqns_data%scalarSolution = scalarSolution + eqns_data%type_data = type_data + eqns_data%attr_data = attr_data + eqns_data%mpar_data = mpar_data + eqns_data%forc_data = forc_data + eqns_data%bvar_data = bvar_data + eqns_data%prog_data = prog_data + eqns_data%indx_data = indx_data + eqns_data%diag_data = diag_data + eqns_data%flux_data = flux_data + eqns_data%deriv_data = deriv_data + eqns_data%ixSaturation = ixSaturation ! allocate space and fill allocate( eqns_data%model_decisions(maxvarDecisions) ); eqns_data%model_decisions = model_decisions @@ -512,14 +512,14 @@ subroutine summaSolve4ida( & feasible=.true. call checkFeas(& ! input - stateVec, & ! intent(in): model state vector (mixed units) - eqns_data%mpar_data, & ! intent(in): model parameters - eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU - eqns_data%indx_data, & ! intent(in): indices defining model states and layers + stateVec, & ! intent(in): model state vector (mixed units) + eqns_data%mpar_data, & ! intent(in): model parameters + eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU + eqns_data%indx_data, & ! intent(in): indices defining model states and layers ! output: feasibility - feasible, & ! intent(inout): flag to denote the feasibility of the solution + feasible, & ! intent(inout): flag to denote the feasibility of the solution ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control ! early return for non-feasible solutions, right now will just fail if goes infeasible if(.not.feasible)then @@ -580,17 +580,17 @@ subroutine summaSolve4ida( & ! compute enthalpy prime with phase change call t2enthalpyChange_addphase(& ! input: data structures - eqns_data%diag_data, & ! intent(in): model diagnostic variables for a local HRU - eqns_data%indx_data, & ! intent(in): model indices + eqns_data%diag_data, & ! intent(in): model diagnostic variables for a local HRU + eqns_data%indx_data, & ! intent(in): model indices ! input: state variables for the vegetation canopy - eqns_data%scalarCanopyIcePrime, & ! intent(in): prime value for canopy ice content (kg m-2 s-1) + eqns_data%scalarCanopyIcePrime, & ! intent(in): prime value for canopy ice content (kg m-2 s-1) ! input: variables for the snow-soil domain - eqns_data%mLayerVolFracIcePrime, & ! intent(in): prime vector of ice volume fraction (s-1) + eqns_data%mLayerVolFracIcePrime, & ! intent(in): prime vector of ice volume fraction (s-1) ! input/output: enthalpy - scalarCanopyEnthalpyPrime_addphase, & ! intent(inout): prime value for enthalpy of the vegetation canopy (J m-3 s-1) - mLayerEnthalpyPrime_addPhase, & ! intent(inout): prime vector of enthalpy of each snow+soil layer (J m-3 s-1) + scalarCanopyEnthalpyPrime_addphase, & ! intent(inout): prime value for enthalpy of the vegetation canopy (J m-3 s-1) + mLayerEnthalpyPrime_addPhase, & ! intent(inout): prime vector of enthalpy of each snow+soil layer (J m-3 s-1) ! output: error control - err,message) ! intent(out): error control + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! compute energy balance mean From 48cdd6d3972a8982f9cc6ff0e2fe83304bf081c5 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 25 Jan 2024 06:14:56 -0600 Subject: [PATCH 1086/1472] Transferred additional operations from the stateSplit loop to the split_select loop in opSplittin. --- build/source/engine/opSplittin.f90 | 162 ++++++++++++++++------------- 1 file changed, 92 insertions(+), 70 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 94e284222..c13dbb5f9 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -351,64 +351,75 @@ subroutine opSplittin(& call split_select % initialize_flags ! initialize loop control flags split_select_loop: do iSplit=1,500 !write(*,*) "A:",ixCoupling,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit - if (split_select % solution.eqv..false.) then - if (split_select % domainSplit.eqv..false.) then - if (split_select % stateThenDomain.eqv..false.) then - if (split_select % stateTypeSplitting.eqv..false.) then - ixCoupling=split_select % ixCoupling; if (ixCoupling.gt.nCoupling) exit split_select_loop ! exit if all splits are exhausted - call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs - call split_select % initialize_iStateTypeSplit; split_select % stateTypeSplitting=.true. - end if - if (split_select % stateTypeSplitting.eqv..true.) then - iStateTypeSplit=split_select % iStateTypeSplit; if (iStateTypeSplit.gt.nStateTypeSplit) split_select % stateTypeSplitting=.false. -!wri te(*,*) "B:",iStateTypeSplit,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit + if (split_select % stateSplit.eqv..false.) then + if (split_select % solution.eqv..false.) then + if (split_select % domainSplit.eqv..false.) then + if (split_select % stateThenDomain.eqv..false.) then + if (split_select % stateTypeSplitting.eqv..false.) then + ixCoupling=split_select % ixCoupling; if (ixCoupling.gt.nCoupling) exit split_select_loop ! exit if all splits are exhausted + call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs + call split_select % initialize_iStateTypeSplit; split_select % stateTypeSplitting=.true. + end if + if (split_select % stateTypeSplitting.eqv..true.) then + iStateTypeSplit=split_select % iStateTypeSplit; if (iStateTypeSplit.gt.nStateTypeSplit) split_select % stateTypeSplitting=.false. +!wri te(*,*) "B:",iStateTypeSplit,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit + end if end if end if end if end if if (split_select % stateTypeSplitting.eqv..true.) then - if (split_select % solution.eqv..false.) then - if (split_select % domainSplit.eqv..false.) then - if (split_select % stateThenDomain.eqv..false.) then - ! first try the state type split, then try the domain split within a given state type - call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split - call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.true. - end if - if (split_select % stateThenDomain.eqv..true.) then ! stateThenDomain loop - ixStateThenDomain=split_select % ixStateThenDomain -!write (*,*) "C:",ixStateThenDomain,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit - if (ixStateThenDomain > (1+tryDomainSplit)) then - ixStateThenDomain=ixStateThenDomain-1; split_select % ixStateThenDomain = ixStateThenDomain ! correct index needed after loop exit - split_select % stateThenDomain=.false. ! eqivalent to exiting the stateThenDomain loop - end if + if (split_select % stateSplit.eqv..false.) then + if (split_select % solution.eqv..false.) then + if (split_select % domainSplit.eqv..false.) then + if (split_select % stateThenDomain.eqv..false.) then + ! first try the state type split, then try the domain split within a given state type + call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split + call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.true. + end if + if (split_select % stateThenDomain.eqv..true.) then ! stateThenDomain loop + ixStateThenDomain=split_select % ixStateThenDomain +!write (*,*) "C:",ixStateThenDomain,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit + if (ixStateThenDomain > (1+tryDomainSplit)) then + ixStateThenDomain=ixStateThenDomain-1; split_select % ixStateThenDomain = ixStateThenDomain ! correct index needed after loop exit + split_select % stateThenDomain=.false. ! eqivalent to exiting the stateThenDomain loop + end if + end if end if end if end if if (split_select % stateThenDomain.eqv..true.) then - if (split_select % solution.eqv..false.) then - if (split_select % domainSplit.eqv..false.) then - call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs - call split_select % initialize_iDomainSplit; split_select % domainSplit=.true. - end if - if (split_select % domainSplit.eqv..true.) then - iDomainSplit=split_select % iDomainSplit -!write( *,*) "D:",iDomainSplit,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit - if (split_select % iDomainSplit > nDomainSplit) then - split_select % domainSplit=.false. - end if + if (split_select % stateSplit.eqv..false.) then + if (split_select % solution.eqv..false.) then + if (split_select % domainSplit.eqv..false.) then + call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs + call split_select % initialize_iDomainSplit; split_select % domainSplit=.true. + end if + if (split_select % domainSplit.eqv..true.) then + iDomainSplit=split_select % iDomainSplit +!write( *,*) "D:",iDomainSplit,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit + if (split_select % iDomainSplit > nDomainSplit) then + split_select % domainSplit=.false. + end if + end if end if end if if (split_select % domainSplit.eqv..true.) then - if (split_select % solution.eqv..false.) then; call split_select % initialize_ixSolution; split_select % solution=.true.; end if - if (split_select % solution.eqv..true.) then - ixSolution=split_select % ixSolution - if (split_select % ixSolution > nsolutions) then - split_select % solution=.false. - end if + if (split_select % stateSplit.eqv..false.) then + if (split_select % solution.eqv..false.) then; call split_select % initialize_ixSolution; split_select % solution=.true.; end if + if (split_select % solution.eqv..true.) then + ixSolution=split_select % ixSolution + if (split_select % ixSolution > nsolutions) then + split_select % solution=.false. + end if + end if end if if (split_select % solution.eqv..true.) then + if (split_select % stateSplit.eqv..false.) then call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs call split_select % initialize_iStateSplit; split_select % stateSplit=.true.; ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) + end if + if (split_select % stateSplit.eqv..true.) then stateSplit: do !iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) iStateSplit=split_select % iStateSplit if (split_select % iStateSplit > nStateSplit) then @@ -422,8 +433,8 @@ subroutine opSplittin(& if (return_flag.eqv..true.) return !call update_stateFilter; if (return_flag.eqv..true.) return ! get the mask for the state subset - return for a non-zero error code call validate_split ! verify that the split is valid - if (cycle_domainSplit) then; call split_select % advance_iDomainSplit; split_select % solution=.false.; cycle split_select_loop; end if - if (cycle_solution) then; call split_select % advance_ixSolution; cycle split_select_loop; end if + if (cycle_domainSplit) then; call split_select % advance_iDomainSplit; split_select % solution=.false.; split_select % stateSplit=.false.; cycle split_select_loop; end if + if (cycle_solution) then; call split_select % advance_ixSolution; split_select % stateSplit=.false.; cycle split_select_loop; end if if (return_flag.eqv..true.) return ! return for a non-zero error code call save_recover ! save/recover copies of variables and fluxes @@ -439,60 +450,71 @@ subroutine opSplittin(& if (cycle_coupling) then call split_select % advance_ixCoupling; call split_select % initialize_flags; cycle split_select_loop ! cycle loops if necessary end if - if (cycle_stateThenDomain) then; call split_select % advance_ixStateThenDomain; split_select % domainSplit=.false.; split_select % solution=.false.; cycle split_select_loop; end if ! deactivate flags for inner loops - if (cycle_solution) then; call split_select % advance_ixSolution; cycle split_select_loop; end if + if (cycle_stateThenDomain) then; call split_select % advance_ixStateThenDomain; split_select % domainSplit=.false.; split_select % solution=.false.; split_select % stateSplit=.false.; cycle split_select_loop; end if ! deactivate flags for inner loops + if (cycle_solution) then; call split_select % advance_ixSolution; split_select % stateSplit=.false.; cycle split_select_loop; end if call confirm_variable_updates; if (return_flag.eqv..true.) return ! check that state variables updated - return if error call success_check ! check for success - if (exit_stateThenDomain) then; call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.false.; split_select % domainSplit=.false.; split_select % solution=.false.; exit stateSplit; end if ! exit loops if necessary -- exit last available loop to avoid unnecessary operations -- deactivate inner loops - if (exit_solution) then; split_select % solution=.false.; exit stateSplit; end if + if (exit_stateThenDomain) then; call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.false.; split_select % domainSplit=.false.; split_select % solution=.false.; split_select % stateSplit=.false.; exit stateSplit; end if ! exit loops if necessary -- exit last available loop to avoid unnecessary operations -- deactivate inner loops + if (exit_solution) then; split_select % solution=.false.; split_select % stateSplit=.false.; exit stateSplit; end if if (return_flag.eqv..true.) return ! return if error call split_select % advance_iStateSplit end do stateSplit ! solution with split layers + end if ! stateSplit + if (split_select % stateSplit.eqv..false.) then if (split_select % solution.eqv..true.) then if (split_select % stateThenDomain.eqv..true.) then call split_select % advance_ixSolution end if end if + end if end if ! end solution loop - if (split_select % solution.eqv..false.) then - if (split_select % stateThenDomain.eqv..true.) then - call finalize_solution ! final steps following solution loop - call split_select % advance_iDomainSplit + if (split_select % stateSplit.eqv..false.) then + if (split_select % solution.eqv..false.) then + if (split_select % stateThenDomain.eqv..true.) then + call finalize_solution ! final steps following solution loop + call split_select % advance_iDomainSplit + end if end if end if end if ! domainSplit - if (split_select % solution.eqv..false.) then - if (split_select % domainSplit.eqv..false.) then - if (split_select % stateThenDomain.eqv..true.) call split_select % advance_ixStateThenDomain + if (split_select % stateSplit.eqv..false.) then + if (split_select % solution.eqv..false.) then + if (split_select % domainSplit.eqv..false.) then + if (split_select % stateThenDomain.eqv..true.) call split_select % advance_ixStateThenDomain + end if end if end if end if ! stateThenDomain - if (split_select % solution.eqv..false.) then - if (split_select % domainSplit.eqv..false.) then - if (split_select % stateThenDomain.eqv..false.) then - call finalize_stateThenDomain; if (return_flag.eqv..true.) return ! final steps following the stateThenDomain loop - call split_select % advance_iStateTypeSplit + if (split_select % stateSplit.eqv..false.) then + if (split_select % solution.eqv..false.) then + if (split_select % domainSplit.eqv..false.) then + if (split_select % stateThenDomain.eqv..false.) then + call finalize_stateThenDomain; if (return_flag.eqv..true.) return ! final steps following the stateThenDomain loop + call split_select % advance_iStateTypeSplit + end if end if end if end if end if ! stateTypeSplitting - if (split_select % solution.eqv..false.) then - if (split_select % domainSplit.eqv..false.) then - if (split_select % stateThenDomain.eqv..false.) then - if (split_select % stateTypeSplitting.eqv..false.) then - call finalize_stateTypeSplitting - if (exit_coupling) then - call split_select % initialize_ixCoupling; exit split_select_loop ! success = exit the coupling loop - end if + if (split_select % stateSplit.eqv..false.) then + if (split_select % solution.eqv..false.) then + if (split_select % domainSplit.eqv..false.) then + if (split_select % stateThenDomain.eqv..false.) then + if (split_select % stateTypeSplitting.eqv..false.) then + call finalize_stateTypeSplitting + if (exit_coupling) then + call split_select % initialize_ixCoupling; exit split_select_loop ! success = exit the coupling loop + end if - call split_select % advance_ixCoupling + call split_select % advance_ixCoupling + end if end if end if end if - end if + end if end do split_select_loop call finalize_coupling ! check variables and fluxes, and apply step halving if needed From c97c21ca02706964187506def1e551a9ccbe1252 Mon Sep 17 00:00:00 2001 From: ashleymedin <34691332+ashleymedin@users.noreply.github.com> Date: Thu, 25 Jan 2024 22:29:07 +0900 Subject: [PATCH 1087/1472] Merge pull request #31 from seantrim/develop_AVB_error_msg --- build/source/engine/varSubstep.f90 | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index c4514c9cd..24562af8c 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -199,6 +199,9 @@ subroutine varSubstep(& logical(lgt),parameter :: checkNrgBalance = .true. ! flag to check the energy balance logical(lgt) :: computeEnthalpy ! flag to compute enthalpy regardless of the model decision + ! --------------------------------------------------------------------------------------- + ! initialize error control + out_varSubstep % err=0; out_varSubstep % cmessage='varSubstep/' ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- @@ -266,9 +269,12 @@ subroutine varSubstep(& message => out_varSubstep % cmessage & ! intent(out): error message ) ! end association with variables in the data structures ! ********************************************************************************************************************************************************* +<<<<<<< HEAD ! initialize error control err=0; message='varSubstep/' +======= +>>>>>>> 61cc5073 (Merge pull request #31 from seantrim/develop_AVB_error_msg) ! initialize flag for the success of the substepping failedMinimumStep=.false. From d911325ecaf26db31eba268a482b9fc44bc0842f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 25 Jan 2024 23:03:27 +0900 Subject: [PATCH 1088/1472] part of last commit --- build/source/engine/varSubstep.f90 | 6 ------ 1 file changed, 6 deletions(-) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 24562af8c..bda7b0454 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -269,12 +269,6 @@ subroutine varSubstep(& message => out_varSubstep % cmessage & ! intent(out): error message ) ! end association with variables in the data structures ! ********************************************************************************************************************************************************* -<<<<<<< HEAD - - ! initialize error control - err=0; message='varSubstep/' -======= ->>>>>>> 61cc5073 (Merge pull request #31 from seantrim/develop_AVB_error_msg) ! initialize flag for the success of the substepping failedMinimumStep=.false. From 32ec0e0a0e0a337a856594c9399f789ed2b837bf Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 25 Jan 2024 23:04:17 +0900 Subject: [PATCH 1089/1472] fix cmessage message --- build/source/engine/summaSolve4ida.f90 | 48 +++++++++++------------ build/source/engine/summaSolve4kinsol.f90 | 32 +++++++-------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index db96cca66..46b033644 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -315,8 +315,8 @@ subroutine summaSolve4ida( & allocate( eqns_data%dMat(nState) ); eqns_data%dMat = dMat ! allocate space for the to save previous fluxes - call allocLocal(flux_meta(:),flux_prev,nSnow,nSoil,err,message) - if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif + call allocLocal(flux_meta(:),flux_prev,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif flux_prev = eqns_data%flux_data ! allocate space for other variables @@ -366,29 +366,29 @@ subroutine summaSolve4ida( & ! create serial vectors sunvec_y => FN_VMake_Serial(nState, stateVec, sunctx) - if (.not. associated(sunvec_y)) then; err=20; message='summaSolve4ida: sunvec = NULL'; return; endif + if (.not. associated(sunvec_y)) then; err=20; message=trim(message)//'sunvec = NULL'; return; endif sunvec_yp => FN_VMake_Serial(nState, stateVecPrime, sunctx) - if (.not. associated(sunvec_yp)) then; err=20; message='summaSolve4ida: sunvec = NULL'; return; endif + if (.not. associated(sunvec_yp)) then; err=20; message=trim(message)//'sunvec = NULL'; return; endif ! initialize solution vectors call setInitialCondition(nState, stateVecInit, sunvec_y, sunvec_yp) ! create memory ida_mem = FIDACreate(sunctx) - if (.not. c_associated(ida_mem)) then; err=20; message='summaSolve4ida: ida_mem = NULL'; return; endif + if (.not. c_associated(ida_mem)) then; err=20; message=trim(message)//'ida_mem = NULL'; return; endif ! Attach user data to memory retval = FIDASetUserData(ida_mem, c_loc(eqns_data)) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetUserData'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FIDASetUserData'; return; endif ! Set the function IDA will use to advance the state t0 = 0._rkind retval = FIDAInit(ida_mem, c_funloc(eval8summa4ida), t0, sunvec_y, sunvec_yp) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDAInit'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FIDAInit'; return; endif ! set tolerances retval = FIDAWFtolerances(ida_mem, c_funloc(computWeight4ida)) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDAWFtolerances'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FIDAWFtolerances'; return; endif ! initialize rootfinding problem and allocate space, counting roots if(detect_events)then @@ -409,7 +409,7 @@ subroutine summaSolve4ida( & allocate( rootdir(nRoot) ) rootdir = 0 retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4ida)) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDARootInit'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FIDARootInit'; return; endif else ! will not use, allocate at something nRoot = 1 allocate( rootsfound(nRoot) ) @@ -422,44 +422,44 @@ subroutine summaSolve4ida( & mu = ku; lu = kl; ! Create banded SUNMatrix for use in linear solves sunmat_A => FSUNBandMatrix(nState, mu, lu, sunctx) - if (.not. associated(sunmat_A)) then; err=20; message='summaSolve4ida: sunmat = NULL'; return; endif + if (.not. associated(sunmat_A)) then; err=20; message=trim(message)//'sunmat = NULL'; return; endif ! Create banded SUNLinearSolver object sunlinsol_LS => FSUNLinSol_Band(sunvec_y, sunmat_A, sunctx) - if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolve4ida: sunlinsol = NULL'; return; endif + if (.not. associated(sunlinsol_LS)) then; err=20; message=trim(message)//'sunlinsol = NULL'; return; endif case(ixFullMatrix) ! Create dense SUNMatrix for use in linear solves sunmat_A => FSUNDenseMatrix(nState, nState, sunctx) - if (.not. associated(sunmat_A)) then; err=20; message='summaSolve4ida: sunmat = NULL'; return; endif + if (.not. associated(sunmat_A)) then; err=20; message=trim(message)//'sunmat = NULL'; return; endif ! Create dense SUNLinearSolver object sunlinsol_LS => FSUNLinSol_Dense(sunvec_y, sunmat_A, sunctx) - if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolve4ida: sunlinsol = NULL'; return; endif + if (.not. associated(sunlinsol_LS)) then; err=20; message=trim(message)//'sunlinsol = NULL'; return; endif ! check - case default; err=20; message='summaSolve4ida: error in type of matrix'; return + case default; err=20; message=trim(message)//'error in type of matrix'; return end select ! form of matrix ! Attach the matrix and linear solver ! For the nonlinear solver, IDA uses a Newton SUNNonlinearSolver-- it is not necessary to create and attach it retval = FIDASetLinearSolver(ida_mem, sunlinsol_LS, sunmat_A); - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetLinearSolver'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FIDASetLinearSolver'; return; endif ! Set the user-supplied Jacobian routine if(.not.use_fdJac)then retval = FIDASetJacFn(ida_mem, c_funloc(computJacob4ida)) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetJacFn'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FIDASetJacFn'; return; endif endif ! Enforce the solver to stop at end of the time step retval = FIDASetStopTime(ida_mem, dt_cur) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetStopTime'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FIDASetStopTime'; return; endif ! Set solver parameters at end of setup call setSolverParams(dt_cur, ida_mem, retval) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in setSolverParams'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in setSolverParams'; return; endif ! Disable error messages and warnings if(offErrWarnMessage) then @@ -479,7 +479,7 @@ subroutine summaSolve4ida( & if(detect_events .and. .not.tinystep)then call find_rootdir(eqns_data, rootdir) retval = FIDASetRootDirection(ida_mem, rootdir) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetRootDirection'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FIDASetRootDirection'; return; endif endif eqns_data%firstFluxCall = .false. ! already called for initial @@ -642,11 +642,11 @@ subroutine summaSolve4ida( & if (retvalr .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) ! rootsfound[i]= +1 indicates that gi is increasing, -1 g[i] decreasing, 0 no root !retval = FIDAGetRootInfo(ida_mem, rootsfound) - !if (retval < 0) then; err=20; message='summaSolve4ida: error in FIDAGetRootInfo'; return; endif + !if (retval < 0) then; err=20; message=trim(message)//'error in FIDAGetRootInfo'; return; endif !print '(a,f15.7,2x,17(i2,2x))', "time, rootsfound[] = ", tret(1), rootsfound ! Reininitialize solver for running after discontinuity and restart retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDAReInit'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FIDAReInit'; return; endif if(dt_last(1) < 0.1_rkind)then ! don't keep calling if step is small (more accurate with this tiny but getting hung up) retval = FIDARootInit(ida_mem, 0, c_funloc(layerDisCont4ida)) tinystep = .true. @@ -654,7 +654,7 @@ subroutine summaSolve4ida( & retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4ida)) tinystep = .false. endif - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDARootInit'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FIDARootInit'; return; endif endif endif @@ -702,12 +702,12 @@ subroutine summaSolve4ida( & call FIDAFree(ida_mem) retval = FSUNLinSolFree(sunlinsol_LS) - if(retval /= 0)then; err=20; message='summaSolve4ida: unable to free the linear solver'; return; endif + if(retval /= 0)then; err=20; message=trim(message)//'unable to free the linear solver'; return; endif call FSUNMatDestroy(sunmat_A) call FN_VDestroy(sunvec_y) call FN_VDestroy(sunvec_yp) retval = FSUNContext_Free(sunctx) - if(retval /= 0)then; err=20; message='summaSolve4ida: unable to free the SUNDIALS context'; return; endif + if(retval /= 0)then; err=20; message=trim(message)//'unable to free the SUNDIALS context'; return; endif end associate diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index a2ef944a1..49cb8b058 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -277,32 +277,32 @@ subroutine summaSolve4kinsol(& ! create serial vectors sunvec_y => FN_VMake_Serial(nState, stateVec, sunctx) - if (.not. associated(sunvec_y)) then; err=20; message='summaSolve4kinsol: sunvec = NULL'; return; endif + if (.not. associated(sunvec_y)) then; err=20; message=trim(message)//'sunvec = NULL'; return; endif ! create the scaling vectors sunvec_fscale => FN_VMake_Serial(nState, fscale, sunctx) - if (.not. associated(sunvec_fscale)) then; err=20; message='summaSolve4kinsol: sunvec = NULL'; return; endif + if (.not. associated(sunvec_fscale)) then; err=20; message=trim(message)//'sunvec = NULL'; return; endif sunvec_xscale => FN_VMake_Serial(nState, xscale, sunctx) - if (.not. associated(sunvec_xscale)) then; err=20; message='summaSolve4kinsol: sunvec = NULL'; return; endif + if (.not. associated(sunvec_xscale)) then; err=20; message=trim(message)//'sunvec = NULL'; return; endif ! initialize solution vectors call setInitialCondition(nState, stateVecInit, sunvec_y) ! create memory kinsol_mem = FKINCreate(sunctx) - if (.not. c_associated(kinsol_mem)) then; err=20; message='summaSolve4kinsol: kinsol_mem = NULL'; return; endif + if (.not. c_associated(kinsol_mem)) then; err=20; message=trim(message)//'kinsol_mem = NULL'; return; endif ! Attach user data to memory retval = FKINSetUserData(kinsol_mem, c_loc(eqns_data)) - if (retval /= 0) then; err=20; message='summaSolve4kinsol: error in FKINSetUserData'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FKINSetUserData'; return; endif ! Set solver parameters before calling FKINInit call setSolverParams(nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)), kinsol_mem, retval) - if (retval /= 0) then; err=20; message='summaSolve4kinsol: error in setSolverParams'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in setSolverParams'; return; endif ! Set the function Kinsol will use to advance the state retval = FKINInit(kinsol_mem, c_funloc(eval8summa4kinsol), sunvec_y) - if (retval /= 0) then; err=20; message='summaSolve4kinsol: error in FKINInit'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FKINInit'; return; endif ! define the form of the matrix select case(ixMatrix) @@ -310,34 +310,34 @@ subroutine summaSolve4kinsol(& mu = ku; lu = kl; ! Create banded SUNMatrix for use in linear solves sunmat_A => FSUNBandMatrix(nState, mu, lu, sunctx) - if (.not. associated(sunmat_A)) then; err=20; message='summaSolve4kinsol: sunmat = NULL'; return; endif + if (.not. associated(sunmat_A)) then; err=20; message=trim(message)//'sunmat = NULL'; return; endif ! Create banded SUNLinearSolver object sunlinsol_LS => FSUNLinSol_Band(sunvec_y, sunmat_A, sunctx) - if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolve4kinsol: sunlinsol = NULL'; return; endif + if (.not. associated(sunlinsol_LS)) then; err=20; message=trim(message)//'sunlinsol = NULL'; return; endif case(ixFullMatrix) ! Create dense SUNMatrix for use in linear solves sunmat_A => FSUNDenseMatrix(nState, nState, sunctx) - if (.not. associated(sunmat_A)) then; err=20; message='summaSolve4kinsol: sunmat = NULL'; return; endif + if (.not. associated(sunmat_A)) then; err=20; message=trim(message)//'sunmat = NULL'; return; endif ! Create dense SUNLinearSolver object sunlinsol_LS => FSUNLinSol_Dense(sunvec_y, sunmat_A, sunctx) - if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolve4kinsol: sunlinsol = NULL'; return; endif + if (.not. associated(sunlinsol_LS)) then; err=20; message=trim(message)//'sunlinsol = NULL'; return; endif ! check - case default; err=20; message='summaSolve4kinsol: error in type of matrix'; return + case default; err=20; message=trim(message)//'error in type of matrix'; return end select ! form of matrix ! Attach the matrix and linear solver retval = FKINSetLinearSolver(kinsol_mem, sunlinsol_LS, sunmat_A); - if (retval /= 0) then; err=20; message='summaSolve4kinsol: error in FKINSetLinearSolver'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FKINSetLinearSolver'; return; endif ! Set the user-supplied Jacobian routine if(.not.use_fdJac)then retval = FKINSetJacFn(kinsol_mem, c_funloc(computJacob4kinsol)) - if (retval /= 0) then; err=20; message='summaSolve4kinsol: error in FKINSetJacFn'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FKINSetJacFn'; return; endif endif ! Disable error messages and warnings @@ -407,13 +407,13 @@ subroutine summaSolve4kinsol(& call FKINFree(kinsol_mem) retval = FSUNLinSolFree(sunlinsol_LS) - if(retval /= 0)then; err=20; message='summaSolve4kinsol: unable to free the linear solver'; return; endif + if(retval /= 0)then; err=20; message=trim(message)//'unable to free the linear solver'; return; endif call FSUNMatDestroy(sunmat_A) call FN_VDestroy(sunvec_y) call FN_VDestroy(sunvec_xscale) call FN_VDestroy(sunvec_fscale) retval = FSUNContext_Free(sunctx) - if(retval /= 0)then; err=20; message='summaSolve4kinsol: unable to free the SUNDIALS context'; return; endif + if(retval /= 0)then; err=20; message=trim(message)//'unable to free the SUNDIALS context'; return; endif end subroutine summaSolve4kinsol From 0e0bbe692daf34b7d0b2ea558d4a727dd77ac7c1 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 25 Jan 2024 23:17:26 +0900 Subject: [PATCH 1090/1472] remove extra initialize error in varSubStep --- build/source/engine/varSubstep.f90 | 3 --- 1 file changed, 3 deletions(-) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index bda7b0454..f1be0868a 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -315,9 +315,6 @@ subroutine varSubstep(& substeps: do dtSubstep = min(dtSubstep,maxstep) - ! initialize error control - err=0; message='varSubstep/' - ! ----- ! * populate state vectors... ! --------------------------- From 170d4b692d6b2bb6af3ad601add836c029f0071d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 25 Jan 2024 20:33:51 +0900 Subject: [PATCH 1091/1472] fix cmessage message --- build/source/engine/summaSolve4ida.f90 | 48 +++++++++++------------ build/source/engine/summaSolve4kinsol.f90 | 32 +++++++-------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index d2abc7cbd..90a5717e7 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -283,8 +283,8 @@ subroutine summaSolve4ida( & allocate( eqns_data%dMat(nState) ); eqns_data%dMat = dMat ! allocate space for the to save previous fluxes - call allocLocal(flux_meta(:),flux_prev,nSnow,nSoil,err,message) - if(err/=0)then; err=20; message=trim(message)//trim(message); return; endif + call allocLocal(flux_meta(:),flux_prev,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif flux_prev = eqns_data%flux_data ! allocate space for other variables @@ -334,29 +334,29 @@ subroutine summaSolve4ida( & ! create serial vectors sunvec_y => FN_VMake_Serial(nState, stateVec, sunctx) - if (.not. associated(sunvec_y)) then; err=20; message='summaSolve4ida: sunvec = NULL'; return; endif + if (.not. associated(sunvec_y)) then; err=20; message=trim(message)//'sunvec = NULL'; return; endif sunvec_yp => FN_VMake_Serial(nState, stateVecPrime, sunctx) - if (.not. associated(sunvec_yp)) then; err=20; message='summaSolve4ida: sunvec = NULL'; return; endif + if (.not. associated(sunvec_yp)) then; err=20; message=trim(message)//'sunvec = NULL'; return; endif ! initialize solution vectors call setInitialCondition(nState, stateVecInit, sunvec_y, sunvec_yp) ! create memory ida_mem = FIDACreate(sunctx) - if (.not. c_associated(ida_mem)) then; err=20; message='summaSolve4ida: ida_mem = NULL'; return; endif + if (.not. c_associated(ida_mem)) then; err=20; message=trim(message)//'ida_mem = NULL'; return; endif ! Attach user data to memory retval = FIDASetUserData(ida_mem, c_loc(eqns_data)) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetUserData'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FIDASetUserData'; return; endif ! Set the function IDA will use to advance the state t0 = 0._rkind retval = FIDAInit(ida_mem, c_funloc(eval8summa4ida), t0, sunvec_y, sunvec_yp) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDAInit'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FIDAInit'; return; endif ! set tolerances retval = FIDAWFtolerances(ida_mem, c_funloc(computWeight4ida)) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDAWFtolerances'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FIDAWFtolerances'; return; endif ! initialize rootfinding problem and allocate space, counting roots if(detect_events)then @@ -377,7 +377,7 @@ subroutine summaSolve4ida( & allocate( rootdir(nRoot) ) rootdir = 0 retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4ida)) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDARootInit'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FIDARootInit'; return; endif else ! will not use, allocate at something nRoot = 1 allocate( rootsfound(nRoot) ) @@ -390,44 +390,44 @@ subroutine summaSolve4ida( & mu = ku; lu = kl; ! Create banded SUNMatrix for use in linear solves sunmat_A => FSUNBandMatrix(nState, mu, lu, sunctx) - if (.not. associated(sunmat_A)) then; err=20; message='summaSolve4ida: sunmat = NULL'; return; endif + if (.not. associated(sunmat_A)) then; err=20; message=trim(message)//'sunmat = NULL'; return; endif ! Create banded SUNLinearSolver object sunlinsol_LS => FSUNLinSol_Band(sunvec_y, sunmat_A, sunctx) - if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolve4ida: sunlinsol = NULL'; return; endif + if (.not. associated(sunlinsol_LS)) then; err=20; message=trim(message)//'sunlinsol = NULL'; return; endif case(ixFullMatrix) ! Create dense SUNMatrix for use in linear solves sunmat_A => FSUNDenseMatrix(nState, nState, sunctx) - if (.not. associated(sunmat_A)) then; err=20; message='summaSolve4ida: sunmat = NULL'; return; endif + if (.not. associated(sunmat_A)) then; err=20; message=trim(message)//'sunmat = NULL'; return; endif ! Create dense SUNLinearSolver object sunlinsol_LS => FSUNLinSol_Dense(sunvec_y, sunmat_A, sunctx) - if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolve4ida: sunlinsol = NULL'; return; endif + if (.not. associated(sunlinsol_LS)) then; err=20; message=trim(message)//'sunlinsol = NULL'; return; endif ! check - case default; err=20; message='summaSolve4ida: error in type of matrix'; return + case default; err=20; message=trim(message)//'error in type of matrix'; return end select ! form of matrix ! Attach the matrix and linear solver ! For the nonlinear solver, IDA uses a Newton SUNNonlinearSolver-- it is not necessary to create and attach it retval = FIDASetLinearSolver(ida_mem, sunlinsol_LS, sunmat_A); - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetLinearSolver'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FIDASetLinearSolver'; return; endif ! Set the user-supplied Jacobian routine if(.not.use_fdJac)then retval = FIDASetJacFn(ida_mem, c_funloc(computJacob4ida)) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetJacFn'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FIDASetJacFn'; return; endif endif ! Enforce the solver to stop at end of the time step retval = FIDASetStopTime(ida_mem, dt_cur) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetStopTime'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FIDASetStopTime'; return; endif ! Set solver parameters at end of setup call setSolverParams(dt_cur, nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)), ida_mem, retval) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in setSolverParams'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in setSolverParams'; return; endif ! Disable error messages and warnings if(offErrWarnMessage) then @@ -446,7 +446,7 @@ subroutine summaSolve4ida( & if(detect_events .and. .not.tinystep)then call find_rootdir(eqns_data, rootdir) retval = FIDASetRootDirection(ida_mem, rootdir) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDASetRootDirection'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FIDASetRootDirection'; return; endif endif eqns_data%firstFluxCall = .false. ! already called for initial @@ -525,11 +525,11 @@ subroutine summaSolve4ida( & if (retvalr .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) ! rootsfound[i]= +1 indicates that gi is increasing, -1 g[i] decreasing, 0 no root !retval = FIDAGetRootInfo(ida_mem, rootsfound) - !if (retval < 0) then; err=20; message='summaSolve4ida: error in FIDAGetRootInfo'; return; endif + !if (retval < 0) then; err=20; message=trim(message)//'error in FIDAGetRootInfo'; return; endif !print '(a,f15.7,2x,17(i2,2x))', "time, rootsfound[] = ", tret(1), rootsfound ! Reininitialize solver for running after discontinuity and restart retval = FIDAReInit(ida_mem, tret(1), sunvec_y, sunvec_yp) - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDAReInit'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FIDAReInit'; return; endif if(dt_last(1) < 0.1_rkind)then ! don't keep calling if step is small (more accurate with this tiny but getting hung up) retval = FIDARootInit(ida_mem, 0, c_funloc(layerDisCont4ida)) tinystep = .true. @@ -537,7 +537,7 @@ subroutine summaSolve4ida( & retval = FIDARootInit(ida_mem, nRoot, c_funloc(layerDisCont4ida)) tinystep = .false. endif - if (retval /= 0) then; err=20; message='summaSolve4ida: error in FIDARootInit'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FIDARootInit'; return; endif endif endif @@ -588,12 +588,12 @@ subroutine summaSolve4ida( & call FIDAFree(ida_mem) retval = FSUNLinSolFree(sunlinsol_LS) - if(retval /= 0)then; err=20; message='summaSolve4ida: unable to free the linear solver'; return; endif + if(retval /= 0)then; err=20; message=trim(message)//'unable to free the linear solver'; return; endif call FSUNMatDestroy(sunmat_A) call FN_VDestroy(sunvec_y) call FN_VDestroy(sunvec_yp) retval = FSUNContext_Free(sunctx) - if(retval /= 0)then; err=20; message='summaSolve4ida: unable to free the SUNDIALS context'; return; endif + if(retval /= 0)then; err=20; message=trim(message)//'unable to free the SUNDIALS context'; return; endif end subroutine summaSolve4ida diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index 17937059e..43c08f6d4 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -271,32 +271,32 @@ subroutine summaSolve4kinsol(& ! create serial vectors sunvec_y => FN_VMake_Serial(nState, stateVec, sunctx) - if (.not. associated(sunvec_y)) then; err=20; message='summaSolve4kinsol: sunvec = NULL'; return; endif + if (.not. associated(sunvec_y)) then; err=20; message=trim(message)//'sunvec = NULL'; return; endif ! create the scaling vectors sunvec_fscale => FN_VMake_Serial(nState, fscale, sunctx) - if (.not. associated(sunvec_fscale)) then; err=20; message='summaSolve4kinsol: sunvec = NULL'; return; endif + if (.not. associated(sunvec_fscale)) then; err=20; message=trim(message)//'sunvec = NULL'; return; endif sunvec_xscale => FN_VMake_Serial(nState, xscale, sunctx) - if (.not. associated(sunvec_xscale)) then; err=20; message='summaSolve4kinsol: sunvec = NULL'; return; endif + if (.not. associated(sunvec_xscale)) then; err=20; message=trim(message)//'sunvec = NULL'; return; endif ! initialize solution vectors call setInitialCondition(nState, stateVecInit, sunvec_y) ! create memory kinsol_mem = FKINCreate(sunctx) - if (.not. c_associated(kinsol_mem)) then; err=20; message='summaSolve4kinsol: kinsol_mem = NULL'; return; endif + if (.not. c_associated(kinsol_mem)) then; err=20; message=trim(message)//'kinsol_mem = NULL'; return; endif ! Attach user data to memory retval = FKINSetUserData(kinsol_mem, c_loc(eqns_data)) - if (retval /= 0) then; err=20; message='summaSolve4kinsol: error in FKINSetUserData'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FKINSetUserData'; return; endif ! Set solver parameters before calling FKINInit call setSolverParams(nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)), kinsol_mem, retval) - if (retval /= 0) then; err=20; message='summaSolve4kinsol: error in setSolverParams'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in setSolverParams'; return; endif ! Set the function Kinsol will use to advance the state retval = FKINInit(kinsol_mem, c_funloc(eval8summa4kinsol), sunvec_y) - if (retval /= 0) then; err=20; message='summaSolve4kinsol: error in FKINInit'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FKINInit'; return; endif ! define the form of the matrix select case(ixMatrix) @@ -304,34 +304,34 @@ subroutine summaSolve4kinsol(& mu = ku; lu = kl; ! Create banded SUNMatrix for use in linear solves sunmat_A => FSUNBandMatrix(nState, mu, lu, sunctx) - if (.not. associated(sunmat_A)) then; err=20; message='summaSolve4kinsol: sunmat = NULL'; return; endif + if (.not. associated(sunmat_A)) then; err=20; message=trim(message)//'sunmat = NULL'; return; endif ! Create banded SUNLinearSolver object sunlinsol_LS => FSUNLinSol_Band(sunvec_y, sunmat_A, sunctx) - if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolve4kinsol: sunlinsol = NULL'; return; endif + if (.not. associated(sunlinsol_LS)) then; err=20; message=trim(message)//'sunlinsol = NULL'; return; endif case(ixFullMatrix) ! Create dense SUNMatrix for use in linear solves sunmat_A => FSUNDenseMatrix(nState, nState, sunctx) - if (.not. associated(sunmat_A)) then; err=20; message='summaSolve4kinsol: sunmat = NULL'; return; endif + if (.not. associated(sunmat_A)) then; err=20; message=trim(message)//'sunmat = NULL'; return; endif ! Create dense SUNLinearSolver object sunlinsol_LS => FSUNLinSol_Dense(sunvec_y, sunmat_A, sunctx) - if (.not. associated(sunlinsol_LS)) then; err=20; message='summaSolve4kinsol: sunlinsol = NULL'; return; endif + if (.not. associated(sunlinsol_LS)) then; err=20; message=trim(message)//'sunlinsol = NULL'; return; endif ! check - case default; err=20; message='summaSolve4kinsol: error in type of matrix'; return + case default; err=20; message=trim(message)//'error in type of matrix'; return end select ! form of matrix ! Attach the matrix and linear solver retval = FKINSetLinearSolver(kinsol_mem, sunlinsol_LS, sunmat_A); - if (retval /= 0) then; err=20; message='summaSolve4kinsol: error in FKINSetLinearSolver'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FKINSetLinearSolver'; return; endif ! Set the user-supplied Jacobian routine if(.not.use_fdJac)then retval = FKINSetJacFn(kinsol_mem, c_funloc(computJacob4kinsol)) - if (retval /= 0) then; err=20; message='summaSolve4kinsol: error in FKINSetJacFn'; return; endif + if (retval /= 0) then; err=20; message=trim(message)//'error in FKINSetJacFn'; return; endif endif ! Disable error messages and warnings @@ -397,13 +397,13 @@ subroutine summaSolve4kinsol(& call FKINFree(kinsol_mem) retval = FSUNLinSolFree(sunlinsol_LS) - if(retval /= 0)then; err=20; message='summaSolve4kinsol: unable to free the linear solver'; return; endif + if(retval /= 0)then; err=20; message=trim(message)//'unable to free the linear solver'; return; endif call FSUNMatDestroy(sunmat_A) call FN_VDestroy(sunvec_y) call FN_VDestroy(sunvec_xscale) call FN_VDestroy(sunvec_fscale) retval = FSUNContext_Free(sunctx) - if(retval /= 0)then; err=20; message='summaSolve4kinsol: unable to free the SUNDIALS context'; return; endif + if(retval /= 0)then; err=20; message=trim(message)//'unable to free the SUNDIALS context'; return; endif end subroutine summaSolve4kinsol From cf1bf825f5df4e710b2522e8274a6791fdb3a5d2 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 25 Jan 2024 23:17:26 +0900 Subject: [PATCH 1092/1472] remove extra initialize error in varSubStep --- build/source/engine/varSubstep.f90 | 3 --- 1 file changed, 3 deletions(-) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 51eec9e4f..f42be7aab 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -282,9 +282,6 @@ subroutine varSubstep(& substeps: do dtSubstep = min(dtSubstep,maxstep) - ! initialize error control - err=0; message='varSubstep/' - ! ----- ! * populate state vectors... ! --------------------------- From cb23076b4879fe66d9757939c3b1bc6497e82102 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 26 Jan 2024 07:55:34 +0900 Subject: [PATCH 1093/1472] makefile --- build/cmake/CMakeLists.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 04f2fbb38..cbefd6867 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -206,10 +206,10 @@ else() message(FATAL_ERROR "Did not find Actors directory, edit CMakeLists.txt to add path") endif() link_directories(${DIR_ACTORS}/lib) - set(INC_ACTORS ${DIR_ACTORS}/include - ${PARENT_DIR}/build/includes/global - ${PARENT_DIR}/build/includes/summa_actor - ${PARENT_DIR}/build/includes/job_actor + set(INC_ACTORS ${DIR_ACTORS}/include + ${PARENT_DIR}/build/includes/global + ${PARENT_DIR}/build/includes/summa_actor + ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) set(LIB_ACTORS ${DIR_ACTORS}/lib -lcaf_core -lcaf_io) @@ -395,7 +395,7 @@ set(SOLVER_SUNDIALS set(DRIVER ${DRIVER_DIR}/summa_alarms.f90 ${DRIVER_DIR}/summa_globalData.f90) - set(DRIVER_NOT_ACTORS +set(DRIVER_NOT_ACTORS ${DRIVER_DIR}/summa_util.f90 ${DRIVER_DIR}/summa_type.f90 ${DRIVER_DIR}/summa_defineOutput.f90 From 903585f3ae042cf82dc327e75f4fb2b214172fa8 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 25 Jan 2024 18:01:17 -0600 Subject: [PATCH 1094/1472] Hotfix for issue causing unintentional error message truncation. --- build/source/dshare/data_types.f90 | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 97607e7b3..7a6c64cb6 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -303,6 +303,8 @@ MODULE data_types type(hru_i),allocatable :: gru(:) ! gru(:)%hru(:) endtype gru_i + integer(i4b),parameter :: len_msg=256 ! length of character string used in class definitions + ! *********************************************************************************************************** ! Define classes used to simplify calls to the subrotuines in computFlux ! *********************************************************************************************************** @@ -355,7 +357,7 @@ MODULE data_types real(rkind) :: dCanopyNetFlux_dCanWat ! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) real(rkind) :: dGroundNetFlux_dCanWat ! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + character(len=len_msg) :: cmessage ! intent(out): error message contains procedure :: finalize => finalize_out_vegNrgFlux end type out_type_vegNrgFlux @@ -390,7 +392,7 @@ MODULE data_types real(rkind), allocatable :: dNrgFlux_dWatAbove(:) ! intent(out): derivatives in the flux w.r.t. water state in the layer above (J m-2 s-1 K-1) real(rkind), allocatable :: dNrgFlux_dWatBelow(:) ! intent(out): derivatives in the flux w.r.t. water state in the layer below (J m-2 s-1 K-1) integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + character(len=len_msg) :: cmessage ! intent(out): error message contains procedure :: finalize => finalize_out_ssdNrgFlux end type out_type_ssdNrgFlux @@ -411,7 +413,7 @@ MODULE data_types real(rkind) :: scalarThroughfallRainDeriv ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) real(rkind) :: scalarCanopyLiqDrainageDeriv ! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + character(len=len_msg) :: cmessage ! intent(out): error message contains procedure :: finalize => finalize_out_vegLiqFlux end type out_type_vegLiqFlux @@ -439,7 +441,7 @@ MODULE data_types type, public :: out_type_snowLiqFlx ! class for intent(out) arguments in snowLiqFlx call integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + character(len=len_msg) :: cmessage ! intent(out): error message contains procedure :: finalize => finalize_out_snowLiqFlx end type out_type_snowLiqFlx @@ -501,7 +503,7 @@ MODULE data_types type, public :: out_type_soilLiqFlx ! class for intent(out) arguments in soilLiqFlx call integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + character(len=len_msg) :: cmessage ! intent(out): error message contains procedure :: finalize => finalize_out_soilLiqFlx end type out_type_soilLiqFlx @@ -532,7 +534,7 @@ MODULE data_types real(rkind), allocatable :: mLayerBaseflow(:) ! intent(out): baseflow from each soil layer (m s-1) real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! intent(out): derivative in baseflow w.r.t. matric head (s-1) integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + character(len=len_msg) :: cmessage ! intent(out): error message contains procedure :: finalize => finalize_out_groundwatr end type out_type_groundwatr @@ -567,7 +569,7 @@ MODULE data_types real(rkind) :: scalarAquiferBaseflow ! intent(out): total baseflow from the aquifer (m s-1) real(rkind) :: dBaseflow_dAquifer ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + character(len=len_msg) :: cmessage ! intent(out): error message contains procedure :: finalize => finalize_out_bigAquifer end type out_type_bigAquifer @@ -588,7 +590,7 @@ MODULE data_types type, public :: out_type_stateFilter ! class for intent(out) arguments in stateFilter call integer(i4b) :: nSubset ! intent(out): number of selected state variables for a given split integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + character(len=len_msg) :: cmessage ! intent(out): error message contains procedure :: finalize => finalize_out_stateFilter end type out_type_stateFilter @@ -606,7 +608,7 @@ MODULE data_types type, public :: out_type_indexSplit ! class for intent(out) arguments in indexSplit call integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + character(len=len_msg) :: cmessage ! intent(out): error message contains procedure :: finalize => finalize_out_indexSplit end type out_type_indexSplit @@ -646,7 +648,7 @@ MODULE data_types logical(lgt) :: reduceCoupledStep ! intent(out): flag to reduce the length of the coupled step logical(lgt) :: tooMuchMelt ! intent(out): flag to denote that ice is insufficient to support melt integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + character(len=len_msg) :: cmessage ! intent(out): error message contains procedure :: finalize => finalize_out_varSubstep end type out_type_varSubstep From f6b0cac9d7e6a0a398bd7a5ff25282203ce2a540 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 26 Jan 2024 13:36:45 +0900 Subject: [PATCH 1095/1472] clarifying temperature component of enthalpy vs full enthalpy in comments, shouldn't change anything --- build/cmake/CMakeLists.txt | 2 +- build/source/dshare/var_lookup.f90 | 2 +- build/source/engine/enthalpyTemp.f90 | 125 ++++++++----------- build/source/engine/enthalpyTempAddPrime.f90 | 14 +-- build/source/engine/eval8summa.f90 | 12 +- build/source/engine/eval8summaWithPrime.f90 | 16 ++- build/source/engine/summaSolve4ida.f90 | 98 ++++++++------- build/source/engine/varSubstep.f90 | 89 +++++++------ 8 files changed, 175 insertions(+), 183 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index f461c72d3..dc1a6556c 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -395,7 +395,7 @@ set(SOLVER_SUNDIALS set(DRIVER ${DRIVER_DIR}/summa_alarms.f90 ${DRIVER_DIR}/summa_globalData.f90) - set(DRIVER_NOT_ACTORS +set(DRIVER_NOT_ACTORS ${DRIVER_DIR}/summa_util.f90 ${DRIVER_DIR}/summa_type.f90 ${DRIVER_DIR}/summa_defineOutput.f90 diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 8b13ccc7e..5b15e2fe3 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -388,7 +388,7 @@ MODULE var_lookup integer(i4b) :: mLayerThermalC = integerMissing ! thermal conductivity at the mid-point of each layer (W m-1 K-1) integer(i4b) :: iLayerThermalC = integerMissing ! thermal conductivity at the interface of each layer (W m-1 K-1) ! enthalpy - integer(i4b) :: scalarCanairEnthalpy = integerMissing ! enthalpy of the canopy air space (J m-3) + integer(i4b) :: scalarCanairEnthalpy = integerMissing ! temperature component of enthalpy of the canopy air space (J m-3) integer(i4b) :: scalarCanopyEnthalpy = integerMissing ! temperature component of enthalpy of the vegetation canopy (J m-3) integer(i4b) :: mLayerEnthalpy = integerMissing ! temperature component of enthalpy of the snow+soil layers (J m-3) ! forcing diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 2016217a1..dd0c6d3e3 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -66,7 +66,7 @@ module enthalpyTemp_module public::enthalpy2t_snow public::t2enthalpy_snow public::t2enthalpy -public::t2enthalpyChange_addphase +public::enthalpy2DeltaH private::hyp_2F1_real ! define the snow look-up table used to compute temperature based on enthalpy @@ -77,7 +77,7 @@ module enthalpyTemp_module ! ************************************************************************************************************************ -! public subroutine T2E_lookup: define a look-up table to compute specific enthalpy based on temperature +! public subroutine T2E_lookup: define a look-up table to compute temperature component of enthalpy based on temperature ! appropriate when no dry mass, as in snow ! ************************************************************************************************************************ subroutine T2E_lookup(mpar_data, & ! intent(in): parameter data structure @@ -305,14 +305,15 @@ end subroutine T2L_lookup ! ************************************************************************************************************************ -! public subroutine enthalpy2t_snow: compute temperature based on specific enthalpy -- appropriate when no dry mass, as in snow +! public subroutine enthalpy2t_snow: compute temperature based on specific temperature component of enthalpy +! appropriate when no dry mass, as in snow ! ************************************************************************************************************************ subroutine enthalpy2t_snow(Ey,BulkDenWater,fc_param,Tk,err,message) ! ------------------------------------------------------------------------------------------------------------------------- implicit none ! ------------------------------------------------------------------------------------------------------------------------- ! declare dummy variables - real(rkind),intent(in) :: Ey ! total enthalpy (J m-3) + real(rkind),intent(in) :: Ey ! total temperature component of enthalpy (J m-3) real(rkind),intent(in) :: BulkDenWater ! bulk density of water (kg m-3) real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) real(rkind),intent(out) :: Tk ! initial temperature guess / final temperature value (K) @@ -404,8 +405,8 @@ end subroutine enthalpy2t_snow ! ************************************************************************************************************************ -! public function t2enthalpy_snow: compute total enthalpy based on temperature and mass (J m-3) for a layer only -! where the layer has no dry mass, as in snow +! public function t2enthalpy_snow: compute temperature component of enthalpy based on temperature and mass (J m-3) for a +! layer only where the layer has no dry mass, as in snow ! NOTE: enthalpy is a relative value, defined as zero at Tfreeze where all water is liquid ! ************************************************************************************************************************ function t2enthalpy_snow(Tk,BulkDenWater,fc_param) @@ -439,7 +440,7 @@ end function t2enthalpy_snow ! ************************************************************************************************************************ -! public subroutine t2enthalpy: compute enthalpy from temperature and total water content +! public subroutine t2enthalpy: compute temperature component of enthalpy from temperature and total water content ! ************************************************************************************************************************ subroutine t2enthalpy(& ! input: data structures @@ -677,68 +678,66 @@ end subroutine t2enthalpy ! ************************************************************************************************************************ -! public subroutine t2enthalpyChange_addphase: compute change in enthalpy or enthalpy prime with phase change from ice content change +! public subroutine enthalpy2DeltaH: compute change in mixture enthalpy or mixture enthalpy prime by adding terms of +! phase change from ice content change (= delta H) ! ************************************************************************************************************************ -subroutine t2enthalpyChange_addphase(& +subroutine enthalpy2DeltaH(& ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): model indices - ! input: state variables for the vegetation canopy - scalarCanopyIceDelta, & ! intent(in): value of canopy ice content (kg m-2) or prime ice content (kg m-2 s-1) - ! input: variables for the snow-soil domain - mLayerVolFracIceDelta, & ! intent(in) : vector of volumetric fraction of ice (-) or prime volumetric fraction of ice (s-1) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): model indices + ! input: ice content change + scalarCanopyIceDelta, & ! intent(in): value of canopy ice content (kg m-2) or prime ice content (kg m-2 s-1) + mLayerVolFracIceDelta, & ! intent(in): vector of volumetric fraction of ice (-) or prime volumetric fraction of ice (s-1) ! input/output: enthalpy - scalarCanopyEnthalpyDelta, & ! intent(inout): enthalpy of the vegetation canopy (J m-3) or enthalpy prime (J m-3 s-1) - mLayerEnthalpyDelta, & ! intent(inout): enthalpy of each snow+soil layer (J m-3) or enthalpy prime (J m-3 s-1) + scalarCanopyHmixDelta, & ! intent(inout): mixture enthalpy of the vegetation canopy (J m-3) or enthalpy prime (J m-3 s-1) + mLayerHmixDelta, & ! intent(inout): mixture enthalpy of each snow+soil layer (J m-3) or enthalpy prime (J m-3 s-1) ! output: error control - err,message) ! intent(out): error control + err,message) ! intent(out): error control ! ------------------------------------------------------------------------------------------------------------------------- - ! downwind routines - USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water implicit none ! delare dummy variables ! ------------------------------------------------------------------------------------------------------------------------- ! input: data structures - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_ilength),intent(in) :: indx_data ! model indices - ! input: state variables for the vegetation canopy - real(rkind),intent(in) :: scalarCanopyIceDelta ! value for canopy ice content (kg m-2) or prime ice content (kg m-2 s-1) - ! input: variables for the snow-soil domain - real(rkind),intent(in) :: mLayerVolFracIceDelta(:) ! vector of volumetric fraction of ice (-) or prime volumetric fraction of ice (s-1) + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_ilength),intent(in) :: indx_data ! model indices + ! input: ice content change + real(rkind),intent(in) :: scalarCanopyIceDelta ! delta value for canopy ice content (kg m-2) or prime ice content (kg m-2 s-1) + real(rkind),intent(in) :: mLayerVolFracIceDelta(:) ! delta vector of volumetric fraction of ice (-) or prime volumetric fraction of ice (s-1) ! input output: enthalpy - real(rkind),intent(inout) :: scalarCanopyEnthalpyDelta ! value for enthalpy of the vegetation canopy (J m-3) or enthalpy prime (J m-3 s-1) - real(rkind),intent(inout) :: mLayerEnthalpyDelta(:) ! vector of enthalpy of each snow+soil layer (J m-3) or enthalpy prime (J m-3 s-1) + real(rkind),intent(inout) :: scalarCanopyHmixDelta ! delta value for mixture enthalpy of the vegetation canopy (J m-3 s-1) + real(rkind),intent(inout) :: mLayerHmixDelta(:) ! delta vector of mixture enthalpy of each snow+soil layer (J m-3 s-1) ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------------------------------- ! declare local variables - character(len=128) :: cmessage ! error message in downwind routine - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixDomainType ! name of a given model domain - integer(i4b) :: ixControlIndex ! index within a given model domain + character(len=128) :: cmessage ! error message in downwind routine + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixDomainType ! name of a given model domain + integer(i4b) :: ixControlIndex ! index within a given model domain ! ------------------------------------------------------------------------------------------------------------------------ ! make association with variables in the data structures generalVars: associate(& ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers ! mapping between the full state vector and the state subset - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset ! type of domain, type of state variable, and index of control volume within domain - ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat & ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ! canopy depth + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) & ! intent(in): [dp] canopy depth (m) ) ! end associate statement ! ----------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="t2enthalpyChange_addphase/" + err=0; message="enthalpy2DeltaH/" ! loop through model state variables do iState=1,size(ixMapSubset2Full) @@ -757,34 +756,16 @@ subroutine t2enthalpyChange_addphase(& ! get the layer index select case(ixDomainType) - case(iname_cas); iLayer = integerMissing - case(iname_veg); iLayer = integerMissing - case(iname_snow); iLayer = ixControlIndex - case(iname_soil); iLayer = ixControlIndex + nSnow - case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) - case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return - end select - - ! identify domain - select case(ixDomainType) - case(iname_cas); cycle ! canopy air space: do nothing - + case(iname_cas); cycle ! canopy air space: do nothing (no water stored in canopy air space) case(iname_veg) - ! association to necessary variables for vegetation - vegVars: associate(& - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) & ! canopy depth (m) - ) - scalarCanopyEnthalpyDelta = scalarCanopyEnthalpyDelta - LH_fus * scalarCanopyIceDelta / canopyDepth - - end associate vegVars - + scalarCanopyHmixDelta = scalarCanopyHmixDelta - LH_fus * scalarCanopyIceDelta / canopyDepth case(iname_snow) - mLayerEnthalpyDelta(iLayer) = mLayerEnthalpyDelta(iLayer) - iden_ice * LH_fus * mLayerVolFracIceDelta(iLayer) - + iLayer = ixControlIndex + mLayerHmixDelta(iLayer) = mLayerHmixDelta(iLayer) - iden_ice * LH_fus * mLayerVolFracIceDelta(iLayer) case(iname_soil) - mLayerEnthalpyDelta(iLayer) = mLayerEnthalpyDelta(iLayer) - iden_water * LH_fus * mLayerVolFracIceDelta(iLayer) - - case(iname_aquifer); cycle ! aquifer: do nothing + iLayer = ixControlIndex + nSnow + mLayerHmixDelta(iLayer) = mLayerHmixDelta(iLayer) - iden_water * LH_fus * mLayerVolFracIceDelta(iLayer) + case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return end select @@ -793,7 +774,7 @@ subroutine t2enthalpyChange_addphase(& end associate generalVars -end subroutine t2enthalpyChange_addphase +end subroutine enthalpy2DeltaH !---------------------------------------------------------------------- ! private function: compute hypergeometric function with real arguments into real result diff --git a/build/source/engine/enthalpyTempAddPrime.f90 b/build/source/engine/enthalpyTempAddPrime.f90 index c5d2206f3..c4d57e0e7 100644 --- a/build/source/engine/enthalpyTempAddPrime.f90 +++ b/build/source/engine/enthalpyTempAddPrime.f90 @@ -62,7 +62,7 @@ module enthalpyTempAddPrime_module ! ************************************************************************************************************************ -! public subroutine t2enthalpyPrime: compute enthalpy prime from temperature and total water content +! public subroutine t2enthalpyPrime: compute temperature component of enthalpy prime from temperature and total water content ! ************************************************************************************************************************ subroutine t2enthalpyPrime(& ! input: data structures @@ -87,9 +87,9 @@ subroutine t2enthalpyPrime(& mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy prime - scalarCanairEnthalpyPrime, & ! intent(out): prime enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyPrime, & ! intent(out): prime enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyPrime, & ! intent(out): prime enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpyPrime, & ! intent(out): prime temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyPrime, & ! intent(out): prime temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyPrime, & ! intent(out): prime temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control err,message) ! intent(out): error control ! ------------------------------------------------------------------------------------------------------------------------- @@ -122,9 +122,9 @@ subroutine t2enthalpyPrime(& real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of snow liquid water (-) real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) ! output: enthalpy prime - real(rkind),intent(out) :: scalarCanairEnthalpyPrime ! prime enthalpy of the canopy air space (J m-3) - real(rkind),intent(out) :: scalarCanopyEnthalpyPrime ! prime enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! prime enthalpy of each snow+soil layer (J m-3) + real(rkind),intent(out) :: scalarCanairEnthalpyPrime ! prime temperature component of enthalpy of the canopy air space (J m-3) + real(rkind),intent(out) :: scalarCanopyEnthalpyPrime ! prime temperature component of enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! prime temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index cbe9e4095..1b74fb90a 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -200,9 +200,9 @@ subroutine eval8summa(& real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) ! enthalpy - real(rkind) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3 - real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector of enthalpy for snow+soil layers (J m-3) + real(rkind) :: scalarCanairEnthalpyTrial ! trial value for temperature component of enthalpy of the canopy air space (J m-3 + real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector of temperature component of enthalpy for snow+soil layers (J m-3) ! other local variables logical(lgt) :: checkLWBalance ! flag to check longwave balance integer(i4b) :: iLayer ! index of model layer in the snow+soil domain @@ -401,9 +401,9 @@ subroutine eval8summa(& mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) ! output: enthalpy - scalarCanairEnthalpyTrial, & ! intent(out): trial value for enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(out): trial value for enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(out): trial vector of enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpyTrial, & ! intent(out): trial value for temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(out): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(out): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 38e486f65..31077a1c7 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -101,7 +101,7 @@ subroutine eval8summaWithPrime(& ! output: enthalpy prime values scalarCanairEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the canopy air space (J m-3 s-1) scalarCanopyEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the vegetation canopy (J m-3 s-1) - mLayerEnthalpyPrime, & ! intent(out): prime vector for temperature component of enthalpy for snow+soil layers (J m-3 s-1) + mLayerEnthalpyPrime, & ! intent(out): prime vector of temperature component of enthalpy for snow+soil layers (J m-3 s-1) ! input-output: baseflow ixSaturation, & ! intent(inout): index of the lowest saturated layer dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) @@ -172,11 +172,11 @@ subroutine eval8summaWithPrime(& real(rkind),intent(out) :: scalarCanopyTempPrime ! prime value for temperature of the vegetation canopy (K s-1) real(rkind),intent(out) :: scalarCanopyWatPrime ! prime value for total water content of the vegetation canopy (kg m-2 s-1) real(rkind),intent(out) :: scalarCanopyIcePrime ! prime value for mass of ice on the vegetation canopy (kg m-2 s-1) - real(rkind),intent(out) :: mLayerTempPrime(:) ! prime vector for temperature of each snow and soil layer (K s-1) - real(rkind),intent(out) :: mLayerMatricHeadPrime(:) ! prime vector for matric head of each snow and soil layer (m s-1) - real(rkind),intent(out) :: mLayerMatricHeadLiqPrime(:) ! prime vector for liquid water matric potential (m s-1) - real(rkind),intent(out) :: mLayerVolFracWatPrime(:) ! prime vector for volumetric total water content of each snow and soil layer (s-1) - real(rkind),intent(out) :: mLayerVolFracIcePrime(:) ! prime vector for volumetric fraction of ice (s-1) + real(rkind),intent(out) :: mLayerTempPrime(:) ! prime vector of temperature of each snow and soil layer (K s-1) + real(rkind),intent(out) :: mLayerMatricHeadPrime(:) ! prime vector of matric head of each snow and soil layer (m s-1) + real(rkind),intent(out) :: mLayerMatricHeadLiqPrime(:) ! prime vector of liquid water matric potential (m s-1) + real(rkind),intent(out) :: mLayerVolFracWatPrime(:) ! prime vector of volumetric total water content of each snow and soil layer (s-1) + real(rkind),intent(out) :: mLayerVolFracIcePrime(:) ! prime vector of volumetric fraction of ice (s-1) ! output: enthalpy prime values real(rkind),intent(out) :: scalarCanairEnthalpyPrime ! prime value for temperature component of enthalpy of the canopy air space (J m-3 s-1) real(rkind),intent(out) :: scalarCanopyEnthalpyPrime ! prime value for temperature component of enthalpy of the vegetation canopy (J m-3 s-1) @@ -205,15 +205,13 @@ subroutine eval8summaWithPrime(& real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric ice content (-) real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) real(rkind) :: scalarCanopyLiqPrime ! prime value for liquid water storage in the canopy (kg m-2 s-1) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! prime vector for volumetric liquid water content (s-1) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! prime vector of volumetric liquid water content (s-1) real(rkind) :: scalarAquiferStoragePrime ! prime value of storage of water in the aquifer (m s-1) ! other local variables integer(i4b) :: iLayer ! index of model layer in the snow+soil domain integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine character(LEN=256) :: cmessage ! error message of downwind routine - real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy - real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step logical(lgt),parameter :: needCm=.true. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 46b033644..099d40517 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -157,7 +157,7 @@ subroutine summaSolve4ida( & USE tol4ida_module,only:computWeight4ida ! weight required for tolerances USE var_lookup,only:maxvarDecisions ! maximum number of decisions USE enthalpyTempAddPrime_module,only:t2enthalpyPrime ! compute enthalpy - USE enthalpyTemp_module,only:t2enthalpyChange_addphase ! add phase to enthalpy + USE enthalpyTemp_module,only:enthalpy2DeltaH ! add phase change terms to delta temperature component of enthalpy !======= Declarations ========= implicit none @@ -241,11 +241,11 @@ subroutine summaSolve4ida( & type(var_dlength) :: flux_prev ! previous model fluxes for a local HRU character(LEN=256) :: cmessage ! error message of downwind routine real(rkind),allocatable :: mLayerMatricHeadPrimePrev(:) ! previous derivative value for total water matric potential (m s-1) - real(rkind) :: scalarCanairEnthalpyPrimePrev ! previous derivative value for canopy air enthalpy (J m-3) - real(rkind) :: scalarCanopyEnthalpyPrime_addphasePrev ! previous derivative value for canopy enthalpy (J m-3) - real(rkind),allocatable :: mLayerEnthalpyPrime_addphase(:) ! derivative value for total water enthalpy (J m-3) - real(rkind) :: scalarCanopyEnthalpyPrime_addphase ! derivative value for canopy enthalpy (J m-3) - real(rkind),allocatable :: mLayerEnthalpyPrime_addphasePrev(:) ! previous derivative value for total water enthalpy (J m-3) + real(rkind) :: scalarCanairEnthalpyPrimePrev ! previous derivative value for canopy air temperature component of enthalpy (J m-3) + real(rkind) :: scalarCanopyHmixPrimePrev ! previous derivative value for canopy mixture enthalpy (J m-3) + real(rkind),allocatable :: mLayerHmixPrime(:) ! derivative value for total water mixture enthalpy (J m-3) + real(rkind) :: scalarCanopyHmixPrime ! derivative value for canopy mixture enthalpy (J m-3) + real(rkind),allocatable :: mLayerHmixPrimePrev(:) ! previous derivative value for total water enthalpy (J m-3) real(rkind),allocatable :: fluxVecPrev(:) ! previous value for fluxes real(rkind),allocatable :: resVecPrev(:) ! previous value for residuals real(rkind),allocatable :: dCompress_dPsiPrev(:) ! previous derivative value soil compression @@ -337,8 +337,8 @@ subroutine summaSolve4ida( & allocate( eqns_data%mLayerVolFracIcePrime(nLayers) ) allocate( eqns_data%mLayerEnthalpyPrime(nLayers) ) allocate( mLayerMatricHeadPrimePrev(nSoil) ) - allocate( mLayerEnthalpyPrime_addphase(nLayers) ) - allocate( mLayerEnthalpyPrime_addphasePrev(nLayers) ) + allocate( mLayerHmixPrime(nLayers) ) + allocate( mLayerHmixPrimePrev(nLayers) ) allocate( dCompress_dPsiPrev(nSoil) ) allocate( eqns_data%fluxVec(nState) ) allocate( eqns_data%resVec(nState) ) @@ -347,20 +347,20 @@ subroutine summaSolve4ida( & allocate( resVecPrev(nState) ) !initialize - scalarCanopyEnthalpyPrime_addphase = 0._rkind - mLayerEnthalpyPrime_addphase(:) = 0._rkind + scalarCanopyHmixPrime = 0._rkind + mLayerHmixPrime(:) = 0._rkind ! need the following values for the first substep - eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) - eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) - eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) - mLayerMatricHeadPrimePrev(:) = 0._rkind - scalarCanairEnthalpyPrimePrev = 0._rkind - scalarCanopyEnthalpyPrime_addphasePrev = 0._rkind - mLayerEnthalpyPrime_addphasePrev(:) = 0._rkind - dCompress_dPsiPrev(:) = 0._rkind - fluxVecPrev(:) = 0._rkind - resVecPrev(:) = 0._rkind + eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) + eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) + eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) + mLayerMatricHeadPrimePrev(:) = 0._rkind + scalarCanairEnthalpyPrimePrev = 0._rkind + scalarCanopyHmixPrimePrev = 0._rkind + mLayerHmixPrimePrev(:) = 0._rkind + dCompress_dPsiPrev(:) = 0._rkind + fluxVecPrev(:) = 0._rkind + resVecPrev(:) = 0._rkind retval = FSUNContext_Create(c_null_ptr, sunctx) @@ -567,44 +567,50 @@ subroutine summaSolve4ida( & ! output: enthalpy eqns_data%scalarCanairEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the canopy air space (J m-3) eqns_data%scalarCanopyEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the vegetation canopy (J m-3) - eqns_data%mLayerEnthalpyPrime, & ! intent(out): prime vector oftemperature component of enthalpy of each snow+soil layer (J m-3) + eqns_data%mLayerEnthalpyPrime, & ! intent(out): prime vector of temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif endif - ! initialize enthalpy prime with phase change, no phase change in canopy air space - scalarCanopyEnthalpyPrime_addphase = eqns_data%scalarCanopyEnthalpyPrime - mLayerEnthalpyPrime_addPhase = eqns_data%mLayerEnthalpyPrime + ! initialize mixture enthalpy prime to temperature component of enthalpy, no difference in canopy air space + scalarCanopyHmixPrime = eqns_data%scalarCanopyEnthalpyPrime + mLayerHmixPrime = eqns_data%mLayerEnthalpyPrime - ! compute enthalpy prime with phase change - call t2enthalpyChange_addphase(& + ! compute mixture enthalpy prime + call enthalpy2DeltaH(& ! input: data structures - eqns_data%diag_data, & ! intent(in): model diagnostic variables for a local HRU - eqns_data%indx_data, & ! intent(in): model indices - ! input: state variables for the vegetation canopy - eqns_data%scalarCanopyIcePrime, & ! intent(in): prime value for canopy ice content (kg m-2 s-1) - ! input: variables for the snow-soil domain - eqns_data%mLayerVolFracIcePrime, & ! intent(in): prime vector of ice volume fraction (s-1) - ! input/output: enthalpy - scalarCanopyEnthalpyPrime_addphase, & ! intent(inout): prime value for enthalpy of the vegetation canopy (J m-3 s-1) - mLayerEnthalpyPrime_addPhase, & ! intent(inout): prime vector of enthalpy of each snow+soil layer (J m-3 s-1) + eqns_data%diag_data, & ! intent(in): model diagnostic variables for a local HRU + eqns_data%indx_data, & ! intent(in): model indices + ! input: ice content change + eqns_data%scalarCanopyIcePrime, & ! intent(in): prime value for canopy ice content (kg m-2 s-1) + eqns_data%mLayerVolFracIcePrime, & ! intent(in): prime vector of ice volume fraction (s-1) + ! input/output: enthalpy + scalarCanopyHmixPrime, & ! intent(inout): prime value for mixture enthalpy of the vegetation canopy (J m-3 s-1) + mLayerHmixPrime, & ! intent(inout): prime vector of mixture enthalpy of each snow+soil layer (J m-3 s-1) ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! compute energy balance mean if(ixCasNrg/=integerMissing) balance(ixCasNrg) = balance(ixCasNrg) + ( eqns_data%scalarCanairEnthalpyPrime - eqns_data%fluxVec(ixCasNrg) & - + scalarCanairEnthalpyPrimePrev - fluxVecPrev(ixCasNrg) )*dt_diff/2._rkind/dt - !if(ixCasNrg/=integerMissing) balance(ixCasNrg) = balance(ixCasNrg) + ( eqns_data%resVec(ixCasNrg) + resVecPrev(ixCasNrg) )*dt_diff/2._rkind/dt ! should be equivalent to above, use for debugging - if(ixVegNrg/=integerMissing) balance(ixVegNrg) = balance(ixVegNrg) + ( scalarCanopyEnthalpyPrime_addphase - eqns_data%fluxVec(ixVegNrg) & - + scalarCanopyEnthalpyPrime_addphasePrev - fluxVecPrev(ixVegNrg) )*dt_diff/2._rkind/dt + + scalarCanairEnthalpyPrimePrev - fluxVecPrev(ixCasNrg) )*dt_diff/2._rkind/dt + if(ixVegNrg/=integerMissing) balance(ixVegNrg) = balance(ixVegNrg) + ( scalarCanopyHmixPrime - eqns_data%fluxVec(ixVegNrg) & + + scalarCanopyHmixPrimePrev - fluxVecPrev(ixVegNrg) )*dt_diff/2._rkind/dt if(nSnowSoilNrg>0)then do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) - balance(ixSnowSoilNrg(i)) = balance(ixSnowSoilNrg(i)) + ( mLayerEnthalpyPrime_addPhase(i) - eqns_data%fluxVec(ixSnowSoilNrg(i)) & - + mLayerEnthalpyPrime_addPhasePrev(i) - fluxVecPrev(ixSnowSoilNrg(i)) )*dt_diff/2._rkind/dt + balance(ixSnowSoilNrg(i)) = balance(ixSnowSoilNrg(i)) + ( mLayerHmixPrime(i) - eqns_data%fluxVec(ixSnowSoilNrg(i)) & + + mLayerHmixPrimePrev(i) - fluxVecPrev(ixSnowSoilNrg(i)) )*dt_diff/2._rkind/dt enddo endif + ! should be equivalent to above, use for debugging + !if(ixCasNrg/=integerMissing) balance(ixCasNrg) = balance(ixCasNrg) + ( eqns_data%resVec(ixCasNrg) + resVecPrev(ixCasNrg) )*dt_diff/2._rkind/dt + !if(ixVegNrg/=integerMissing) balance(ixVegNrg) = balance(ixVegNrg) + ( eqns_data%resVec(ixVegNrg) + resVecPrev(ixVegNrg) )*dt_diff/2._rkind/dt + !if(nSnowSoilNrg>0)then + ! do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) + ! balance(ixSnowSoilNrg(i)) = balance(ixSnowSoilNrg(i)) + + ( eqns_data%resVec(ixSnowSoilNrg(i)) + resVecPrev(ixSnowSoilNrg(i)) )*dt_diff/2._rkind/dt + ! enddo + !endif endif ! ---- @@ -629,8 +635,8 @@ subroutine summaSolve4ida( & eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) mLayerMatricHeadPrimePrev(:) = eqns_data%mLayerMatricHeadPrime(:) scalarCanairEnthalpyPrimePrev = eqns_data%scalarCanairEnthalpyPrime - scalarCanopyEnthalpyPrime_addphasePrev = scalarCanopyEnthalpyPrime_addphase - mLayerEnthalpyPrime_addphasePrev(:) = mLayerEnthalpyPrime_addphase(:) + scalarCanopyHmixPrimePrev = scalarCanopyHmixPrime + mLayerHmixPrimePrev(:) = mLayerHmixPrime(:) dCompress_dPsiPrev(:) = eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) tretPrev = tret(1) flux_prev = eqns_data%flux_data @@ -691,8 +697,8 @@ subroutine summaSolve4ida( & deallocate( eqns_data%mLayerVolFracIcePrime ) deallocate( eqns_data%mLayerEnthalpyPrime ) deallocate( mLayerMatricHeadPrimePrev ) - deallocate( mLayerEnthalpyPrime_addphase ) - deallocate( mLayerEnthalpyPrime_addphasePrev ) + deallocate( mLayerHmixPrime ) + deallocate( mLayerHmixPrimePrev ) deallocate( dCompress_dPsiPrev ) deallocate( eqns_data%fluxVec ) deallocate( eqns_data%resVec ) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index f1be0868a..9a07abb05 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -628,7 +628,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec #endif USE updateVars_module,only:updateVars ! update prognostic variables USE enthalpyTemp_module,only:t2enthalpy ! compute enthalpy - USE enthalpyTemp_module,only:t2enthalpyChange_addphase ! add phase to enthalpy + USE enthalpyTemp_module,only:enthalpy2DeltaH ! add phase change terms to delta temperature component of enthalpy implicit none ! model control real(rkind) ,intent(in) :: dt ! time step (s) @@ -685,35 +685,35 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial vector for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector for liquid water matric potential (m) + real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial vector of temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector of volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector of total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector of liquid water matric potential (m) real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) - real(rkind) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3) - real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector for enthalpy of snow + soil (J m-3) + real(rkind) :: scalarCanairEnthalpyTrial ! trial value for temperature component of enthalpy of the canopy air space (J m-3) + real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector of temperature component of enthalpy of snow + soil (J m-3) ! diagnostic variables real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric fraction of ice (-) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector of volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector of volumetric fraction of ice (-) ! derivative of state variables real(rkind) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) real(rkind) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) real(rkind) :: scalarCanopyWatPrime ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempPrime ! trial vector for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! trial vector for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! trial vector for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! trial vector for liquid water matric potential (m) + real(rkind),dimension(nLayers) :: mLayerTempPrime ! trial vector of temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! trial vector of volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! trial vector of total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! trial vector of liquid water matric potential (m) real(rkind) :: scalarAquiferStoragePrime ! trial value for storage of water in the aquifer (m) ! diagnostic variables real(rkind) :: scalarCanopyLiqPrime ! trial value for mass of liquid water on the vegetation canopy (kg m-2) real(rkind) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector for volumetric fraction of ice (-) - real(rkind) :: scalarCanopyEnthalpyDelta_addphase ! delta value for enthalpy of the vegetation canopy with change (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthalpyDelta_addphase ! delta vector of enthalpy of snow + soil with phase change (J m-3) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector of volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector of volumetric fraction of ice (-) + real(rkind) :: scalarCanopyHmixDelta ! delta value for mixture enthalpy of the vegetation canopy (J m-3) + real(rkind),dimension(nLayers) :: mLayerHmixDelta ! delta vector of mixture enthalpy of snow + soil (J m-3) ! ------------------------------------------------------------------------------------------------------------------- ! ------------------------------------------------------------------------------------------------------------------- @@ -968,9 +968,9 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) ! output: enthalpy - scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpyTrial, & ! intent(out): temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -983,35 +983,42 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec case(ida); ! do nothing, already computed case(kinsol, numrec) - ! initialize delta enthalpy with phase change, no phase change in canopy air space - scalarCanopyEnthalpyDelta_addphase = scalarCanopyEnthalpyTrial - scalarCanopyEnthalpy - mLayerEnthalpyDelta_addphase = mLayerEnthalpyTrial - mLayerEnthalpy + ! initialize delta mixture enthalpy to delta temperature component of enthalpy, no difference in canopy air space + scalarCanopyHmixDelta = scalarCanopyEnthalpyTrial - scalarCanopyEnthalpy + mLayerHmixDelta = mLayerEnthalpyTrial - mLayerEnthalpy - ! compute enthalpy with phase change for current values, do on delta value so only have to do once - call t2enthalpyChange_addphase(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): model indices - ! input: state variables for the ve getation canopy - scalarCanopyIceTrial - scalarCanopyIce, & ! intent(in): delta value for canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - mLayerVolFracIceTrial - mLayerVolFracIce, & ! intent(in): delta vector of volumetric ice water content (-) - ! input/output: enthalpy - scalarCanopyEnthalpyDelta_addphase, & ! intent(inout): delta value for enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyDelta_addphase, & ! intent(inout): delta vector of enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,message) ! intent(out): error control + ! compute mixture enthalpy for current values, do on delta value so only have to do once + call enthalpy2DeltaH(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): model indices + ! input: ice content change + scalarCanopyIceTrial - scalarCanopyIce, & ! intent(in): delta value for canopy ice content (kg m-2) + mLayerVolFracIceTrial - mLayerVolFracIce, & ! intent(in): delta vector of volumetric ice water content (-) + ! input/output: enthalpy + scalarCanopyHmixDelta, & ! intent(inout): delta value for mixture enthalpy of the vegetation canopy (J m-3) + mLayerHmixDelta, & ! intent(inout): delta vector of mixture enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,message) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! compute energy balance, maybe should use to check for step reduction if(ixCasNrg/=integerMissing) balance(ixCasNrg) = scalarCanairEnthalpyTrial - scalarCanairEnthalpy - fluxVec(ixCasNrg)*dt - !if(ixCasNrg/=integerMissing) balance(ixCasNrg) = resVec(ixCasNrg) ! should be equivalent to above, use for debugging - if(ixVegNrg/=integerMissing) balance(ixVegNrg) = scalarCanopyEnthalpyDelta_addphase - fluxVec(ixVegNrg)*dt + if(ixVegNrg/=integerMissing) balance(ixVegNrg) = scalarCanopyHmixDelta - fluxVec(ixVegNrg)*dt if(nSnowSoilNrg>0)then do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) - balance(ixSnowSoilNrg(i)) = mLayerEnthalpyDelta_addphase(i) - fluxVec(ixSnowSoilNrg(i))*dt + balance(ixSnowSoilNrg(i)) = mLayerHmixDelta(i) - fluxVec(ixSnowSoilNrg(i))*dt enddo endif + ! should be equivalent to above, use for debugging + !!if(ixCasNrg/=integerMissing) balance(ixCasNrg) = resVec(ixCasNrg) + !if(ixVegNrg/=integerMissing) balance(ixVegNrg) = resVec(ixVegNrg) + !if(nSnowSoilNrg>0)then + ! do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) + ! balance(ixSnowSoilNrg(i)) = resVec(ixSnowSoilNrg(i)) + ! enddo + !endif + end select endif ! if checking energy balance From 5e63789d48f6a581ad78f5f828655d3ec3ca465d Mon Sep 17 00:00:00 2001 From: ashleymedin <34691332+ashleymedin@users.noreply.github.com> Date: Fri, 26 Jan 2024 13:38:46 +0900 Subject: [PATCH 1096/1472] Merge pull request #32 from seantrim/develop_AVB_error_msg Hotfix for error message truncation problem --- build/source/dshare/data_types.f90 | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 97607e7b3..7a6c64cb6 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -303,6 +303,8 @@ MODULE data_types type(hru_i),allocatable :: gru(:) ! gru(:)%hru(:) endtype gru_i + integer(i4b),parameter :: len_msg=256 ! length of character string used in class definitions + ! *********************************************************************************************************** ! Define classes used to simplify calls to the subrotuines in computFlux ! *********************************************************************************************************** @@ -355,7 +357,7 @@ MODULE data_types real(rkind) :: dCanopyNetFlux_dCanWat ! intent(out): derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) real(rkind) :: dGroundNetFlux_dCanWat ! intent(out): derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + character(len=len_msg) :: cmessage ! intent(out): error message contains procedure :: finalize => finalize_out_vegNrgFlux end type out_type_vegNrgFlux @@ -390,7 +392,7 @@ MODULE data_types real(rkind), allocatable :: dNrgFlux_dWatAbove(:) ! intent(out): derivatives in the flux w.r.t. water state in the layer above (J m-2 s-1 K-1) real(rkind), allocatable :: dNrgFlux_dWatBelow(:) ! intent(out): derivatives in the flux w.r.t. water state in the layer below (J m-2 s-1 K-1) integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + character(len=len_msg) :: cmessage ! intent(out): error message contains procedure :: finalize => finalize_out_ssdNrgFlux end type out_type_ssdNrgFlux @@ -411,7 +413,7 @@ MODULE data_types real(rkind) :: scalarThroughfallRainDeriv ! intent(out): derivative in throughfall w.r.t. canopy liquid water (s-1) real(rkind) :: scalarCanopyLiqDrainageDeriv ! intent(out): derivative in canopy drainage w.r.t. canopy liquid water (s-1) integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + character(len=len_msg) :: cmessage ! intent(out): error message contains procedure :: finalize => finalize_out_vegLiqFlux end type out_type_vegLiqFlux @@ -439,7 +441,7 @@ MODULE data_types type, public :: out_type_snowLiqFlx ! class for intent(out) arguments in snowLiqFlx call integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + character(len=len_msg) :: cmessage ! intent(out): error message contains procedure :: finalize => finalize_out_snowLiqFlx end type out_type_snowLiqFlx @@ -501,7 +503,7 @@ MODULE data_types type, public :: out_type_soilLiqFlx ! class for intent(out) arguments in soilLiqFlx call integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + character(len=len_msg) :: cmessage ! intent(out): error message contains procedure :: finalize => finalize_out_soilLiqFlx end type out_type_soilLiqFlx @@ -532,7 +534,7 @@ MODULE data_types real(rkind), allocatable :: mLayerBaseflow(:) ! intent(out): baseflow from each soil layer (m s-1) real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! intent(out): derivative in baseflow w.r.t. matric head (s-1) integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + character(len=len_msg) :: cmessage ! intent(out): error message contains procedure :: finalize => finalize_out_groundwatr end type out_type_groundwatr @@ -567,7 +569,7 @@ MODULE data_types real(rkind) :: scalarAquiferBaseflow ! intent(out): total baseflow from the aquifer (m s-1) real(rkind) :: dBaseflow_dAquifer ! intent(out): change in baseflow flux w.r.t. aquifer storage (s-1) integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + character(len=len_msg) :: cmessage ! intent(out): error message contains procedure :: finalize => finalize_out_bigAquifer end type out_type_bigAquifer @@ -588,7 +590,7 @@ MODULE data_types type, public :: out_type_stateFilter ! class for intent(out) arguments in stateFilter call integer(i4b) :: nSubset ! intent(out): number of selected state variables for a given split integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + character(len=len_msg) :: cmessage ! intent(out): error message contains procedure :: finalize => finalize_out_stateFilter end type out_type_stateFilter @@ -606,7 +608,7 @@ MODULE data_types type, public :: out_type_indexSplit ! class for intent(out) arguments in indexSplit call integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + character(len=len_msg) :: cmessage ! intent(out): error message contains procedure :: finalize => finalize_out_indexSplit end type out_type_indexSplit @@ -646,7 +648,7 @@ MODULE data_types logical(lgt) :: reduceCoupledStep ! intent(out): flag to reduce the length of the coupled step logical(lgt) :: tooMuchMelt ! intent(out): flag to denote that ice is insufficient to support melt integer(i4b) :: err ! intent(out): error code - character(:),allocatable :: cmessage ! intent(out): error message + character(len=len_msg) :: cmessage ! intent(out): error message contains procedure :: finalize => finalize_out_varSubstep end type out_type_varSubstep From 9d759463dc21d0f061a56ef7a627f4e29ad1d77f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 26 Jan 2024 15:51:54 +0900 Subject: [PATCH 1097/1472] just correcting comments --- build/source/dshare/type4ida.f90 | 10 +++++----- build/source/engine/summaSolve4ida.f90 | 12 ++++++------ build/source/engine/summaSolve4kinsol.f90 | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/build/source/dshare/type4ida.f90 b/build/source/dshare/type4ida.f90 index a02e253d1..5884353cd 100644 --- a/build/source/dshare/type4ida.f90 +++ b/build/source/dshare/type4ida.f90 @@ -62,11 +62,11 @@ module type4ida real(rkind) :: scalarCanopyTempPrime ! prime value for temperature of the vegetation canopy (K s-1) real(rkind) :: scalarCanopyWatPrime ! prime value for mass of total water on the vegetation canopy (kg m-2 s-1) real(rkind) :: scalarCanopyIcePrime ! prime value for mass of ice on the vegetation canopy (kg m-2 s-1) - real(rkind), allocatable :: mLayerTempPrime(:) ! prime value for temperature of each snow and soil layer (K s-1) - real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! prime value for matric head of each snow and soil layer (m s-1) - real(rkind), allocatable :: mLayerMatricHeadLiqPrime(:) ! prime value for liquid matric head of each snow and soil layer (m s-1) - real(rkind), allocatable :: mLayerVolFracWatPrime(:) ! prime value for volumetric total water content of each snow and soil layer (s-1) - real(rkind), allocatable :: mLayerVolFracIcePrime(:) ! prime value for volumetric fraction of ice (s-1) + real(rkind), allocatable :: mLayerTempPrime(:) ! prime vector of temperature of each snow and soil layer (K s-1) + real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! prime vector of matric head of each snow and soil layer (m s-1) + real(rkind), allocatable :: mLayerMatricHeadLiqPrime(:) ! prime vector of liquid matric head of each snow and soil layer (m s-1) + real(rkind), allocatable :: mLayerVolFracWatPrime(:) ! prime vector of volumetric total water content of each snow and soil layer (s-1) + real(rkind), allocatable :: mLayerVolFracIcePrime(:) ! prime vector of volumetric fraction of ice (s-1) real(rkind) :: scalarCanairEnthalpyPrime ! prime value for temperature component of enthalpy of the canopy air space (J m-3 s-1) real(rkind) :: scalarCanopyEnthalpyPrime ! prime value for temperature component of enthalpy of the vegetation canopy (J m-3 s-1) real(rkind), allocatable :: mLayerEnthalpyPrime(:) ! prime vector of temperature component of enthalpy for snow+soil layers (J m-3 s-1) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 099d40517..2f521dedb 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -109,7 +109,7 @@ subroutine summaSolve4ida( & checkNrgBalance, & ! intent(in): flag to check energy balance ! input: state vectors stateVecInit, & ! intent(in): initial state vector - sMul, & ! intent(inout): state vector multiplier (USEd in the residual calculations) + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout): diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures model_decisions, & ! intent(in): model decisions @@ -548,11 +548,11 @@ subroutine summaSolve4ida( & eqns_data%mpar_data, & ! intent(in): parameter data structure eqns_data%indx_data, & ! intent(in): model indices ! input: state variables for the vegetation canopy - eqns_data%scalarCanairTempPrime, & ! intent(in): prime value of canopy air temperature (K) - eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - eqns_data%scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - eqns_data%scalarCanopyTempPrime, & ! intent(in): prime value of temperature of the vegetation canopy (K) - eqns_data%scalarCanopyWatPrime, & ! intent(in): prime value of total water of the vegetation canopy (kg m-2) + eqns_data%scalarCanairTempPrime, & ! intent(in): prime value for canopy air temperature (K) + eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) + eqns_data%scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) + eqns_data%scalarCanopyTempPrime, & ! intent(in): prime value for temperature of the vegetation canopy (K) + eqns_data%scalarCanopyWatPrime, & ! intent(in): prime value for total water of the vegetation canopy (kg m-2) ! input: variables for the snow-soil domain eqns_data%mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) eqns_data%mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index 49cb8b058..b63c2d281 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -101,7 +101,7 @@ subroutine summaSolve4kinsol(& scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVecInit, & ! intent(in): initial state vector - sMul, & ! intent(inout): state vector multiplier (USEd in the residual calculations) + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout): diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures model_decisions, & ! intent(in): model decisions From 19cb8ff5e748415e0b90745a6d00a2fd709cd4d7 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 26 Jan 2024 15:53:11 +0900 Subject: [PATCH 1098/1472] moving correct terms to residual for enthalpy, removing the computHeatCap call --- build/source/engine/computResid.f90 | 85 ++-- build/source/engine/computResidWithPrime.f90 | 109 ++-- build/source/engine/eval8summa.f90 | 324 ++++++------ build/source/engine/eval8summaWithPrime.f90 | 510 +++++++++---------- 4 files changed, 514 insertions(+), 514 deletions(-) diff --git a/build/source/engine/computResid.f90 b/build/source/engine/computResid.f90 index 7e1134589..b34edaf41 100644 --- a/build/source/engine/computResid.f90 +++ b/build/source/engine/computResid.f90 @@ -76,36 +76,41 @@ module computResid_module ! ********************************************************************************************************** subroutine computResid(& ! input: model control - dt, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers + dt, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + useEnthalpy, & ! intent(in): flag to use enthalpy formulation ! input: flux vectors - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) - fVec, & ! intent(in): flux vector + sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + fVec, & ! intent(in): flux vector ! input: state variables (already disaggregated into scalars and vectors) - scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) - scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) - scalarCanopyWatTrial, & ! intent(in): trial value for the water on the vegetation canopy (kg m-2) - mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) - scalarAquiferStorageTrial, & ! intent(in): trial value of storage of water in the aquifer (m) + scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) + scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) + scalarCanopyWatTrial, & ! intent(in): trial value for the water on the vegetation canopy (kg m-2) + mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) + scalarAquiferStorageTrial, & ! intent(in): trial value of storage of water in the aquifer (m) ! input: diagnostic variables defining the liquid water and ice content (function of state variables) - scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value for the liq on the vegetation canopy (kg m-2) - mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) - mLayerVolFracWatTrial, & ! intent(in): trial value for the volumetric water in each snow and soil layer (-) - mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liq in each snow and soil layer (-) - scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (J kg K-1) - mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (J kg K-1) + scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value for the liq on the vegetation canopy (kg m-2) + mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) + mLayerVolFracWatTrial, & ! intent(in): trial value for the volumetric water in each snow and soil layer (-) + mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liq in each snow and soil layer (-) + ! input: enthalpy terms + scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (J kg K-1) + mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (J kg K-1) + scalarCanairEnthalpyTrial, & ! intent(in): trial value for temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(in): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(in): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) ! input: data structures - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - flux_data, & ! intent(in): model fluxes for a local HRU - indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + flux_data, & ! intent(in): model fluxes for a local HRU + indx_data, & ! intent(in): index data ! output - rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector - err,message) ! intent(out): error control + rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec, & ! intent(out): residual vector + err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- implicit none ! input: model control @@ -113,6 +118,7 @@ subroutine computResid(& integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain + logical(lgt),intent(in) :: useEnthalpy ! flag to use enthalpy formulation ! input: flux vectors real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(rkind),intent(in) :: fVec(:) ! flux vector @@ -128,8 +134,12 @@ subroutine computResid(& real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial value for volumetric fraction of ice (-) real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial value for the volumetric water in each snow and soil layer (-) real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for the volumetric water in each snow and soil layer (-) + ! input: enthalpy terms real(qp),intent(in) :: scalarCanopyCmTrial ! Cm of vegetation canopy (-) real(qp),intent(in) :: mLayerCmTrial(:) ! Cm of each snow and soil layer (-) + real(rkind),intent(in) :: scalarCanairEnthalpyTrial ! trial value for temperature component of enthalpy of the canopy air space (J m-3) + real(rkind),intent(in) :: scalarCanopyEnthalpyTrial ! trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(in) :: mLayerEnthalpyTrial(:) ! trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) ! input: data structures type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU @@ -164,10 +174,14 @@ subroutine computResid(& mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) + ! enthalpy terms + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(in): [dp] temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(in): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(in): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) ! model state variables (aquifer) scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in): [dp] storage of water in the aquifer (m) ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp] canopy depth (m) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp] canopy depth (m) mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) ! model fluxes (sink terms in the soil domain) mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(in): [dp] transpiration loss from each soil layer (m s-1) @@ -233,9 +247,14 @@ subroutine computResid(& ! compute the residual vector for the vegetation canopy ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision ! --> energy balance - if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*( scalarCanairTempTrial - scalarCanairTemp ) - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) - if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg)*( scalarCanopyTempTrial - scalarCanopyTemp ) + scalarCanopyCmTrial*( scalarCanopyWatTrial - scalarCanopyWat )/canopyDepth & - - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) + if(useEnthalpy)then + if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = ( scalarCanairEnthalpyTrial - scalarCanairEnthalpy ) - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = ( scalarCanopyEnthalpyTrial - scalarCanopyEnthalpy ) - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) + else + if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*( scalarCanairTempTrial - scalarCanairTemp ) - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg)*( scalarCanopyTempTrial - scalarCanopyTemp ) + scalarCanopyCmTrial*( scalarCanopyWatTrial - scalarCanopyWat )/canopyDepth & + - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) + endif ! --> mass balance if(ixVegHyd/=integerMissing)then scalarCanopyHydTrial = merge(scalarCanopyWatTrial, scalarCanopyLiqTrial, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) @@ -246,8 +265,12 @@ subroutine computResid(& ! compute the residual vector for the snow and soil sub-domains for energy if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) )*( mLayerTempTrial(iLayer) - mLayerTemp(iLayer) ) + mLayerCmTrial(iLayer)*( mLayerVolFracWatTrial(iLayer) - mLayerVolFracWat(iLayer) ) & - - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) + if(useEnthalpy)then + rVec( ixSnowSoilNrg(iLayer) ) = ( mLayerEnthalpyTrial(iLayer) - mLayerEnthalpy(iLayer) ) - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) + else + rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) )*( mLayerTempTrial(iLayer) - mLayerTemp(iLayer) ) + mLayerCmTrial(iLayer)*( mLayerVolFracWatTrial(iLayer) - mLayerVolFracWat(iLayer) ) & + - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) + endif end do ! looping through non-missing energy state variables in the snow+soil domain endif diff --git a/build/source/engine/computResidWithPrime.f90 b/build/source/engine/computResidWithPrime.f90 index 167728cca..c7c801bc0 100644 --- a/build/source/engine/computResidWithPrime.f90 +++ b/build/source/engine/computResidWithPrime.f90 @@ -58,38 +58,41 @@ module computResidWithPrime_module ! ********************************************************************************************************** subroutine computResidWithPrime(& ! input: model control - dt, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers + dt, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + useEnthalpy, & ! intent(in): flag to use enthalpy formulation ! input: flux vectors - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) - fVec, & ! intent(in): flux vector + sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + fVec, & ! intent(in): flux vector ! input: state variables (already disaggregated into scalars and vectors) - scalarCanopyTempTrial, & ! intent(in):: trial value for the temperature of the vegetation canopy (K) - mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) - scalarCanairTempPrime, & ! intent(in): Prime value for the temperature of the canopy air space (K s-1) - scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K s-1) - scalarCanopyWatPrime, & ! intent(in): Prime value for the water on the vegetation canopy (kg m-2 s-1) - mLayerTempPrime, & ! intent(in): Prime value for the temperature of each snow and soil layer (K s-1) - scalarAquiferStoragePrime, & ! intent(in): Prime value of storage of water in the aquifer (m s-1) + scalarCanairTempPrime, & ! intent(in): prime value for the temperature of the canopy air space (K s-1) + scalarCanopyTempPrime, & ! intent(in): prime value for the temperature of the vegetation canopy (K s-1) + scalarCanopyWatPrime, & ! intent(in): prime value for the water on the vegetation canopy (kg m-2 s-1) + mLayerTempPrime, & ! intent(in): prime vector of the temperature of each snow and soil layer (K s-1) + scalarAquiferStoragePrime, & ! intent(in): prime value for storage of water in the aquifer (m s-1) ! input: diagnostic variables defining the liquid water and ice content (function of state variables) - scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2 s-1) - scalarCanopyLiqPrime, & ! intent(in): Prime value for the liq on the vegetation canopy (kg m-2 s-1) - mLayerVolFracIcePrime, & ! intent(in): Prime value for the volumetric ice in each snow and soil layer (s-1) - mLayerVolFracWatPrime, & ! intent(in): Prime value for the volumetric water in each snow and soil layer (s-1) - mLayerVolFracLiqPrime, & ! intent(in): Prime value for the volumetric liq in each snow and soil layer (s-1) - scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (J kg K-1) - mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (J kg K-1) + scalarCanopyIcePrime, & ! intent(in): prime value for the ice on the vegetation canopy (kg m-2 s-1) + scalarCanopyLiqPrime, & ! intent(in): prime value for the liq on the vegetation canopy (kg m-2 s-1) + mLayerVolFracIcePrime, & ! intent(in): prime vector of the volumetric ice in each snow and soil layer (s-1) + mLayerVolFracWatPrime, & ! intent(in): prime vector of the volumetric water in each snow and soil layer (s-1) + mLayerVolFracLiqPrime, & ! intent(in): prime vector of the volumetric liq in each snow and soil layer (s-1) + ! input: enthalpy terms + scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (J kg K-1) + mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (J kg K-1) + scalarCanairEnthalpyPrime, & ! intent(in): prime value for the temperature component of the enthalpy of the canopy air space (J m-2 s-1) + scalarCanopyEnthalpyPrime, & ! intent(in): prime value for the temperature component of the enthalpy of the vegetation canopy (J m-2 s-1) + mLayerEnthalpyPrime, & ! intent(in): prime vector of the enthalpy of each snow and soil layer (J m-2 s-1) ! input: data structures - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - flux_data, & ! intent(in): model fluxes for a local HRU - indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + flux_data, & ! intent(in): model fluxes for a local HRU + indx_data, & ! intent(in): index data ! output - rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector - err,message) ! intent(out): error control + rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec, & ! intent(out): residual vector + err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- implicit none ! input: model control @@ -97,25 +100,28 @@ subroutine computResidWithPrime(& integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain + logical(lgt),intent(in) :: useEnthalpy ! flag to use enthalpy formulation ! input: flux vectors real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(rkind),intent(in) :: fVec(:) ! flux vector ! input: state variables (already disaggregated into scalars and vectors) - real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind),intent(in) :: mLayerTempTrial(:) ! trial value for the temperature of each snow and soil layer (K) - real(rkind),intent(in) :: scalarCanairTempPrime ! Prime value for temperature of the canopy air space (K s-1) - real(rkind),intent(in) :: scalarCanopyTempPrime ! Prime value for temperature of the vegetation canopy (K s-1) - real(rkind),intent(in) :: scalarCanopyWatPrime ! Prime value for canopy total water content (kg m-2 s-1) - real(rkind),intent(in) :: mLayerTempPrime(:) ! Prime value for temperature of each snow/soil layer (K s-1) content - real(rkind),intent(in) :: scalarAquiferStoragePrime ! Prime value of aquifer storage (m s-1) + real(rkind),intent(in) :: scalarCanairTempPrime ! prime value for temperature of the canopy air space (K s-1) + real(rkind),intent(in) :: scalarCanopyTempPrime ! prime value for temperature of the vegetation canopy (K s-1) + real(rkind),intent(in) :: scalarCanopyWatPrime ! prime value for canopy total water content (kg m-2 s-1) + real(rkind),intent(in) :: mLayerTempPrime(:) ! prime vector of temperature of each snow/soil layer (K s-1) content + real(rkind),intent(in) :: scalarAquiferStoragePrime ! prime value of aquifer storage (m s-1) ! input: diagnostic variables defining the liquid water and ice content (function of state variables) - real(rkind),intent(in) :: scalarCanopyIcePrime ! Prime value for mass of ice on the vegetation canopy (kg m-2 s-1) - real(rkind),intent(in) :: scalarCanopyLiqPrime ! Prime value for the liq on the vegetation canopy (kg m-2 s-1) - real(rkind),intent(in) :: mLayerVolFracIcePrime(:) ! Prime value for volumetric fraction of ice (s-1) - real(rkind),intent(in) :: mLayerVolFracWatPrime(:) ! Prime value for the volumetric water in each snow and soil layer (s-1) - real(rkind),intent(in) :: mLayerVolFracLiqPrime(:) ! Prime value for the volumetric water in each snow and soil layer (s-1) + real(rkind),intent(in) :: scalarCanopyIcePrime ! prime value for mass of ice on the vegetation canopy (kg m-2 s-1) + real(rkind),intent(in) :: scalarCanopyLiqPrime ! prime value for the liq on the vegetation canopy (kg m-2 s-1) + real(rkind),intent(in) :: mLayerVolFracIcePrime(:) ! prime vector of volumetric fraction of ice (s-1) + real(rkind),intent(in) :: mLayerVolFracWatPrime(:) ! prime vector of the volumetric water in each snow and soil layer (s-1) + real(rkind),intent(in) :: mLayerVolFracLiqPrime(:) ! prime vector of the volumetric water in each snow and soil layer (s-1) + ! input: enthalpy terms real(qp),intent(in) :: scalarCanopyCmTrial ! Cm of vegetation canopy (-) real(qp),intent(in) :: mLayerCmTrial(:) ! Cm of each snow and soil layer (-) + real(rkind),intent(in) :: scalarCanairEnthalpyPrime ! prime value for the temperature component of the enthalpy of the canopy air space (J m-2 s-1) + real(rkind),intent(in) :: scalarCanopyEnthalpyPrime ! prime value for the temperature component of the enthalpy of the vegetation canopy (J m-2 s-1) + real(rkind),intent(in) :: mLayerEnthalpyPrime(:) ! prime vector of the enthalpy of each snow and soil layer (J m-2 s-1) ! input: data structures type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU @@ -129,10 +135,10 @@ subroutine computResidWithPrime(& ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) - real(rkind) :: scalarCanopyHydPrime ! trial value for canopy water (kg m-2), either liquid water content or total water content - real(rkind),dimension(nLayers) :: mLayerVolFracHydPrime ! vector of volumetric water content (-), either liquid water content or total water content + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + integer(i4b),parameter :: ixVegVolume=1 ! index of the desired vegetation control volumne (currently only one veg layer) + real(rkind) :: scalarCanopyHydPrime ! trial value for canopy water (kg m-2), either liquid water content or total water content + real(rkind),dimension(nLayers) :: mLayerVolFracHydPrime ! vector of volumetric water content (-), either liquid water content or total water content ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! link to the necessary variables for the residual computations @@ -203,8 +209,14 @@ subroutine computResidWithPrime(& ! compute the residual vector for the vegetation canopy ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision ! --> energy balance - if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg) * scalarCanairTempPrime - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) - if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime + scalarCanopyCmTrial * scalarCanopyWatPrime/canopyDepth - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) + if(useEnthalpy)then + if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = scalarCanairEnthalpyPrime - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = scalarCanopyEnthalpyPrime - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) + else + if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg) * scalarCanairTempPrime - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime + scalarCanopyCmTrial * scalarCanopyWatPrime/canopyDepth & + - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) + endif ! --> mass balance if(ixVegHyd/=integerMissing)then scalarCanopyHydPrime = merge(scalarCanopyWatPrime, scalarCanopyLiqPrime, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) @@ -214,7 +226,12 @@ subroutine computResidWithPrime(& ! compute the residual vector for the snow and soil sub-domains for energy if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) ) * mLayerTempPrime(iLayer) + mLayerCmTrial(iLayer) * mLayerVolFracWatPrime(iLayer) - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) + if(useEnthalpy)then + rVec( ixSnowSoilNrg(iLayer) ) = mLayerEnthalpyPrime(iLayer) - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) + else + rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) ) * mLayerTempPrime(iLayer) + mLayerCmTrial(iLayer) * mLayerVolFracWatPrime(iLayer) & + - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) + endif end do ! looping through non-missing energy state variables in the snow+soil domain endif diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 1b74fb90a..62d3848a9 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -212,74 +212,74 @@ subroutine eval8summa(& character(LEN=256) :: cmessage ! error message of downwind routine real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil - logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step - logical(lgt),parameter :: needCm=.true. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m + logical(lgt) :: updateCp ! flag to indicate if we update Cp at each step + logical(lgt) :: needCm ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m ! -------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- associate(& ! model decisions - ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver - ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy - ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver + ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy + ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) ! soil parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) ! model state variables from the previous solution - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] total water matric potential (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) - scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in): [dp] storage of water in the aquifer (m) + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] total water matric potential (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) + scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in): [dp] storage of water in the aquifer (m) ! model diagnostic variables, will be updated before used - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) - scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) ! enthalpy from the previous solution - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(in): [dp] temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(in): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(in): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(in): [dp] temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(in): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(in): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) ! soil compression - scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2 s-1) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in volumetric water content due to compression of soil (s-1) + scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2 s-1) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in volumetric water content due to compression of soil (s-1) ! derivatives - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential - dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1),& ! intent(out): [dp] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1),&!intent(out): [dp] derivative in bulk heat capacity w.r.t. temperature - dThermalC_dWatAbove => deriv_data%var(iLookDERIV%dThermalC_dWatAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above - dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk)%dat ,& ! intent(out): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) - dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1) ,& ! intent(out): [dp] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1) ,&!intent(out): [dp] derivative in bulk heat capacity w.r.t. temperature + dThermalC_dWatAbove => deriv_data%var(iLookDERIV%dThermalC_dWatAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk)%dat ,& ! intent(out): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) + dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) ! mapping - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)]mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)]index of control volume for different domains (veg, snow, soil) + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)]mapping of full state vector to the state subset + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)]index of control volume for different domains (veg, snow, soil) ! heat capacity - heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(out): [dp] volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(out): [dp(:)] heat capacity for snow and soil + heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(out): [dp] volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(out): [dp(:)] heat capacity for snow and soil ! Cm - canopyCmTrial => diag_data%var(iLookDIAG%scalarCanopyCm)%dat(1) ,& ! intent(out): [dp] Cm of the canopy - mLayerCmTrial => diag_data%var(iLookDIAG%mLayerCm)%dat & ! intent(out): [dp(:)] Cm of snow and soil + canopyCmTrial => diag_data%var(iLookDIAG%scalarCanopyCm)%dat(1) ,& ! intent(out): [dp] Cm of the canopy + mLayerCmTrial => diag_data%var(iLookDIAG%mLayerCm)%dat & ! intent(out): [dp(:)] Cm of snow and soil ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -290,14 +290,14 @@ subroutine eval8summa(& if (.not.insideSUN) then call checkFeas(& ! input - stateVec, & ! intent(in): model state vector (mixed units) - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers + stateVec, & ! intent(in): model state vector (mixed units) + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers ! output: feasibility - feasible, & ! intent(inout): flag to denote the feasibility of the solution + feasible, & ! intent(inout): flag to denote the feasibility of the solution ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control ! early return for non-feasible solutions if(.not.feasible)then @@ -309,6 +309,17 @@ subroutine eval8summa(& end if end if ! ( feasibility check ) + if(ixHowHeatCap == enthalpyFD)then + updateCp = .true. + needCm = .true. + else if(ixHowHeatCap == closedForm)then ! have a choice, should update if checkNrgBalance in varSubstep is turned on + updateCp = .true. + needCm = .true. + else + message=trim(message)//'unknown heat capacity computation' + err=1; return + end if + ! get the start and end indices for the soil compression calculations if(scalarSolution)then jState = pack(ixControlVolume, ixMapFull2Subset/=integerMissing) @@ -383,105 +394,41 @@ subroutine eval8summa(& if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) if(updateCp)then - ! *** compute volumetric heat capacity C_p - if(ixHowHeatCap == enthalpyFD)then - ! compute H_T without phase change - call t2enthalpy(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value for canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) - ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - ! output: enthalpy - scalarCanairEnthalpyTrial, & ! intent(out): trial value for temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(out): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(out): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! *** compute volumetric heat capacity C_p = dH_T/dT - call computHeatCap(& - ! input: control variables - nLayers, & ! intent(in): number of layers (soil+snow) - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) - ! input output data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) - scalarCanopyTempTrial - scalarCanopyTemp, & ! intent(in): delta value for canopy temperature (K) - scalarCanopyEnthalpyTrial - scalarCanopyEnthalpy, & ! intent(in): delta value for temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerVolFracIceTrial, & ! intent(in): trial vector of volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): trial vector of volumetric fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial vector of temperature - mLayerTempTrial - mLayerTemp, & ! intent(in): delta vector of temperature - mLayerEnthalpyTrial - mLayerEnthalpy, & ! intent(in): delta vector of temperature component of enthalpy for snow and soil - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - ! output - heatCapVegTrial, & ! intent(out): trial value for volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(out): trial vector of heat capacity for snow and soil - dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - else if(ixHowHeatCap == closedForm)then - call computHeatCapAnalytic(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) - ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) - ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - ! input output data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output - heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow - dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - ! output: error control - err,cmessage) ! intent(out): error control - endif !(choice of how compute heat capacity) - + ! *** compute volumetric heat capacity C_p + call computHeatCapAnalytic(& + ! input: control variables + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) + ! input: state variables + scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) + ! input: pre-computed derivatives + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + ! input output data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + ! output + heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow + dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + ! compute multiplier of state vector call computStatMult(& ! input @@ -539,21 +486,21 @@ subroutine eval8summa(& if(needCm)then ! compute C_m call computCm(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - ! input: state variables - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (-) - ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - ! output - canopyCmTrial, & ! intent(out): Cm for vegetation (J kg K-1) - mLayerCmTrial, & ! intent(out): Cm for soil and snow (J kg K-1) - dCm_dTk, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) - dCm_dTkCanopy, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) - err,cmessage) ! intent(out): error control + ! input: control variables + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + ! input: state variables + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (-) + ! input data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + ! output + canopyCmTrial, & ! intent(out): Cm for vegetation (J kg K-1) + mLayerCmTrial, & ! intent(out): Cm for soil and snow (J kg K-1) + dCm_dTk, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) + dCm_dTkCanopy, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) + err,cmessage) ! intent(out): error control else canopyCmTrial = 0._qp mLayerCmTrial = 0._qp @@ -561,6 +508,36 @@ subroutine eval8summa(& dCm_dTkCanopy = 0._rkind endif ! needCm + if(ixHowHeatCap == enthalpyFD)then ! use residual as enthalpy_delta - (phase change)_delta + ! compute temperature component of enthalpy + call t2enthalpy(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(in): trial value for canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) + ! input: variables for the snow-soil domain + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + ! output: enthalpy + scalarCanairEnthalpyTrial, & ! intent(out): trial value for temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(out): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(out): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + else if(ixHowHeatCap == closedForm)then ! use residual as (Cp*DeltaTemp + Cm*DeltaTheta)_delta - (phase change)_delta + scalarCanairEnthalpyTrial = realMissing + scalarCanopyEnthalpyTrial = realMissing + mLayerEnthalpyTrial = realMissing + endif !(choice of how compute heat capacity) + ! save the number of flux calls per time step indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) + 1 @@ -644,6 +621,7 @@ subroutine eval8summa(& nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers + ixHowHeatCap==enthalpyFD, & ! intent(in): flag to use enthalpy form of residual ! input: flux vectors sMul, & ! intent(in): state vector multiplier (used in the residual calculations) fluxVec, & ! intent(in): flux vector @@ -659,8 +637,12 @@ subroutine eval8summa(& mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) mLayerVolFracWatTrial, & ! intent(in): trial value for the volumetric water in each snow and soil layer (-) mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liq in each snow and soil layer (-) + ! input: enthalpy terms canopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) + scalarCanairEnthalpyTrial, & ! intent(in): trial value for temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(in): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(in): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 31077a1c7..dd24b6366 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -93,11 +93,11 @@ subroutine eval8summaWithPrime(& scalarCanopyTempPrime, & ! intent(out): prime value for temperature of the vegetation canopy (K s-1) scalarCanopyWatPrime, & ! intent(out): prime value for total water content of the vegetation canopy (kg m-2 s-1) scalarCanopyIcePrime, & ! intent(out): prime value for mass of ice on the vegetation canopy (kg m-2 s-1) - mLayerTempPrime, & ! intent(out): prime value for temperature of each snow and soil layer (K s-1) - mLayerMatricHeadPrime, & ! intent(out): prime value for matric head of each snow and soil layer (m s-1) - mLayerMatricHeadLiqPrime, & ! intent(out): prime value for liquid water matric potential (m s-1) - mLayerVolFracWatPrime, & ! intent(out): prime value for volumetric total water content of each snow and soil layer (s-1) - mLayerVolFracIcePrime, & ! intent(out): prime value for volumetric fraction of ice (s-1) + mLayerTempPrime, & ! intent(out): prime vector of temperature of each snow and soil layer (K s-1) + mLayerMatricHeadPrime, & ! intent(out): prime vector of matric head of each snow and soil layer (m s-1) + mLayerMatricHeadLiqPrime, & ! intent(out): prime vector of liquid water matric potential (m s-1) + mLayerVolFracWatPrime, & ! intent(out): prime vector of volumetric total water content of each snow and soil layer (s-1) + mLayerVolFracIcePrime, & ! intent(out): prime vector of volumetric fraction of ice (s-1) ! output: enthalpy prime values scalarCanairEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the canopy air space (J m-3 s-1) scalarCanopyEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the vegetation canopy (J m-3 s-1) @@ -113,155 +113,155 @@ subroutine eval8summaWithPrime(& err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- ! provide access to subroutines - USE getVectorz_module, only:varExtract ! extract variables from the state vector - USE getVectorz_module, only:checkFeas ! check feasibility of state vector - USE updateVarsWithPrime_module, only:updateVarsWithPrime ! update variables - USE enthalpyTempAddPrime_module, only:t2enthalpyPrime ! compute enthalpy prime - USE computFlux_module, only:soilCmpresPrime ! compute soil compression - USE computFlux_module, only:computFlux ! compute fluxes given a state vector - USE computHeatCap_module,only:computHeatCap ! recompute enthalpy finite difference heat capacity (Cp) and derivatives - USE computHeatCap_module,only:computHeatCapAnalytic ! recompute closed form heat capacity (Cp) and derivatives - USE computHeatCap_module,only:computCm ! compute Cm and derivatives - USE computHeatCap_module, only:computStatMult ! recompute state multiplier - USE computResidWithPrime_module,only:computResidWithPrime ! compute residuals given a state vector - USE computThermConduct_module,only:computThermConduct ! recompute thermal conductivity and derivatives + USE getVectorz_module, only:varExtract ! extract variables from the state vector + USE getVectorz_module, only:checkFeas ! check feasibility of state vector + USE updateVarsWithPrime_module, only:updateVarsWithPrime ! update variables + USE enthalpyTempAddPrime_module, only:t2enthalpyPrime ! compute enthalpy prime + USE computFlux_module, only:soilCmpresPrime ! compute soil compression + USE computFlux_module, only:computFlux ! compute fluxes given a state vector + USE computHeatCap_module,only:computHeatCap ! recompute enthalpy finite difference heat capacity (Cp) and derivatives + USE computHeatCap_module,only:computHeatCapAnalytic ! recompute closed form heat capacity (Cp) and derivatives + USE computHeatCap_module,only:computCm ! compute Cm and derivatives + USE computHeatCap_module, only:computStatMult ! recompute state multiplier + USE computResidWithPrime_module,only:computResidWithPrime ! compute residuals given a state vector + USE computThermConduct_module,only:computThermConduct ! recompute thermal conductivity and derivatives implicit none ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control - real(rkind),intent(in) :: dt ! entire time step for drainage pond rate - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - integer,intent(in) :: nState ! total number of state variables - logical(lgt),intent(in) :: insideSUN ! flag to indicate if we are inside Sundials solver - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt),intent(inout) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + real(rkind),intent(in) :: dt ! entire time step for drainage pond rate + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + integer,intent(in) :: nState ! total number of state variables + logical(lgt),intent(in) :: insideSUN ! flag to indicate if we are inside Sundials solver + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt),intent(inout) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors - real(rkind),intent(in) :: stateVec(:) ! model state vector - real(rkind),intent(in) :: stateVecPrime(:) ! model state vector - real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + real(rkind),intent(in) :: stateVec(:) ! model state vector + real(rkind),intent(in) :: stateVecPrime(:) ! model state vector + real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) ! input: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(var_i), intent(in) :: type_data ! type of vegetation and soil - type(var_d), intent(in) :: attr_data ! spatial attributes - type(var_dlength), intent(in) :: mpar_data ! model parameters - type(var_d), intent(in) :: forc_data ! model forcing data - type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin - type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(var_i), intent(in) :: type_data ! type of vegetation and soil + type(var_d), intent(in) :: attr_data ! spatial attributes + type(var_dlength), intent(in) :: mpar_data ! model parameters + type(var_d), intent(in) :: forc_data ! model forcing data + type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin + type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU ! output: data structures - type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! input: previous values of variables needed in data window outside of internal IDA - real(rkind),intent(in) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) - real(rkind),intent(in) :: mLayerTempPrev(:) ! previous vector of layer temperature (K) - real(rkind),intent(in) :: mLayerMatricHeadPrev(:) ! previous value for total water matric potential (m) + real(rkind),intent(in) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) + real(rkind),intent(in) :: mLayerTempPrev(:) ! previous vector of layer temperature (K) + real(rkind),intent(in) :: mLayerMatricHeadPrev(:) ! previous value for total water matric potential (m) ! output: new values of variables needed in data window outside of internal IDA - real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind),intent(out) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial vector for total water matric potential (m) - real(rkind),intent(out) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind),intent(out) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial vector for total water matric potential (m) + real(rkind),intent(out) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) ! output: new prime values of variables needed in data window outside of internal IDA - real(rkind),intent(out) :: scalarCanairTempPrime ! prime value for temperature of the canopy air space (K s-1) - real(rkind),intent(out) :: scalarCanopyTempPrime ! prime value for temperature of the vegetation canopy (K s-1) - real(rkind),intent(out) :: scalarCanopyWatPrime ! prime value for total water content of the vegetation canopy (kg m-2 s-1) - real(rkind),intent(out) :: scalarCanopyIcePrime ! prime value for mass of ice on the vegetation canopy (kg m-2 s-1) - real(rkind),intent(out) :: mLayerTempPrime(:) ! prime vector of temperature of each snow and soil layer (K s-1) - real(rkind),intent(out) :: mLayerMatricHeadPrime(:) ! prime vector of matric head of each snow and soil layer (m s-1) - real(rkind),intent(out) :: mLayerMatricHeadLiqPrime(:) ! prime vector of liquid water matric potential (m s-1) - real(rkind),intent(out) :: mLayerVolFracWatPrime(:) ! prime vector of volumetric total water content of each snow and soil layer (s-1) - real(rkind),intent(out) :: mLayerVolFracIcePrime(:) ! prime vector of volumetric fraction of ice (s-1) + real(rkind),intent(out) :: scalarCanairTempPrime ! prime value for temperature of the canopy air space (K s-1) + real(rkind),intent(out) :: scalarCanopyTempPrime ! prime value for temperature of the vegetation canopy (K s-1) + real(rkind),intent(out) :: scalarCanopyWatPrime ! prime value for total water content of the vegetation canopy (kg m-2 s-1) + real(rkind),intent(out) :: scalarCanopyIcePrime ! prime value for mass of ice on the vegetation canopy (kg m-2 s-1) + real(rkind),intent(out) :: mLayerTempPrime(:) ! prime vector of temperature of each snow and soil layer (K s-1) + real(rkind),intent(out) :: mLayerMatricHeadPrime(:) ! prime vector of matric head of each snow and soil layer (m s-1) + real(rkind),intent(out) :: mLayerMatricHeadLiqPrime(:) ! prime vector of liquid water matric potential (m s-1) + real(rkind),intent(out) :: mLayerVolFracWatPrime(:) ! prime vector of volumetric total water content of each snow and soil layer (s-1) + real(rkind),intent(out) :: mLayerVolFracIcePrime(:) ! prime vector of volumetric fraction of ice (s-1) ! output: enthalpy prime values - real(rkind),intent(out) :: scalarCanairEnthalpyPrime ! prime value for temperature component of enthalpy of the canopy air space (J m-3 s-1) - real(rkind),intent(out) :: scalarCanopyEnthalpyPrime ! prime value for temperature component of enthalpy of the vegetation canopy (J m-3 s-1) - real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! prime vector of temperature component of enthalpy for snow+soil layers (J m-3 s-1) + real(rkind),intent(out) :: scalarCanairEnthalpyPrime ! prime value for temperature component of enthalpy of the canopy air space (J m-3 s-1) + real(rkind),intent(out) :: scalarCanopyEnthalpyPrime ! prime value for temperature component of enthalpy of the vegetation canopy (J m-3 s-1) + real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! prime vector of temperature component of enthalpy for snow+soil layers (J m-3 s-1) ! input-output: baseflow - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer - real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer + real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors - logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution - real(rkind),intent(out) :: fluxVec(:) ! flux vector - real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation - real(qp),intent(out) :: resVec(:) ! NOTE: qp ! residual vector + logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution + real(rkind),intent(out) :: fluxVec(:) ! flux vector + real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation + real(qp),intent(out) :: resVec(:) ! NOTE: qp ! residual vector ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- - real(rkind) :: dt1 ! residual step size + real(rkind) :: dt1 ! residual step size ! state variables - real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyLiqTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind) :: scalarCanopyIceTrial ! trial value for ice storage in the canopy (kg m-2) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric liquid water content (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric ice content (-) - real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) - real(rkind) :: scalarCanopyLiqPrime ! prime value for liquid water storage in the canopy (kg m-2 s-1) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! prime vector of volumetric liquid water content (s-1) - real(rkind) :: scalarAquiferStoragePrime ! prime value of storage of water in the aquifer (m s-1) + real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyLiqTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind) :: scalarCanopyIceTrial ! trial value for ice storage in the canopy (kg m-2) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric liquid water content (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric ice content (-) + real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + real(rkind) :: scalarCanopyLiqPrime ! prime value for liquid water storage in the canopy (kg m-2 s-1) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! prime vector of volumetric liquid water content (s-1) + real(rkind) :: scalarAquiferStoragePrime ! prime value of storage of water in the aquifer (m s-1) ! other local variables - integer(i4b) :: iLayer ! index of model layer in the snow+soil domain - integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain - integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine - character(LEN=256) :: cmessage ! error message of downwind routine - logical(lgt),parameter :: updateCp=.true. ! flag to indicate if we update Cp at each step - logical(lgt),parameter :: needCm=.true. ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m + integer(i4b) :: iLayer ! index of model layer in the snow+soil domain + integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain + integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine + character(LEN=256) :: cmessage ! error message of downwind routine + logical(lgt) :: updateCp ! flag to indicate if we update Cp at each step + logical(lgt) :: needCm ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m ! -------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- associate(& ! model decisions - ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy - ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation + ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy + ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) ! soil parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) ! model diagnostic variables, will be updated before used - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) - scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) ! soil compression - scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2 s-1) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in volumetric water content due to compression of soil (s-1) + scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2 s-1) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in volumetric water content due to compression of soil (s-1) ! derivatives - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential - dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1) ,& ! intent(out): [dp] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative in bulk heat capacity w.r.t. temperature - dThermalC_dWatAbove => deriv_data%var(iLookDERIV%dThermalC_dWatAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above - dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk)%dat ,& ! intent(out): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) - dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1) ,& ! intent(out): [dp] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative in bulk heat capacity w.r.t. temperature + dThermalC_dWatAbove => deriv_data%var(iLookDERIV%dThermalC_dWatAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk)%dat ,& ! intent(out): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) + dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) ! mapping - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) ! heat capacity - heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(out): [dp] volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(out): [dp(:)] heat capacity for snow and soil + heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(out): [dp] volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(out): [dp(:)] heat capacity for snow and soil ! Cm - canopyCmTrial => diag_data%var(iLookDIAG%scalarCanopyCm)%dat(1) ,& ! intent(out): [dp] Cm of the canopy - mLayerCmTrial => diag_data%var(iLookDIAG%mLayerCm)%dat & ! intent(out): [dp(:)] Cm of snow and soil + canopyCmTrial => diag_data%var(iLookDIAG%scalarCanopyCm)%dat(1) ,& ! intent(out): [dp] Cm of the canopy + mLayerCmTrial => diag_data%var(iLookDIAG%mLayerCm)%dat & ! intent(out): [dp(:)] Cm of snow and soil ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -272,14 +272,14 @@ subroutine eval8summaWithPrime(& if (.not.insideSUN) then call checkFeas(& ! input - stateVec, & ! intent(in): model state vector (mixed units) - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers + stateVec, & ! intent(in): model state vector (mixed units) + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers ! output: feasibility - feasible, & ! intent(inout): flag to denote the feasibility of the solution + feasible, & ! intent(inout): flag to denote the feasibility of the solution ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control ! early return for non-feasible solutions if(.not.feasible)then @@ -290,6 +290,17 @@ subroutine eval8summaWithPrime(& end if end if ! ( feasibility check ) + if(ixHowHeatCap == enthalpyFD)then + updateCp = .true. + needCm = .true. + else if(ixHowHeatCap == closedForm)then ! have a choice, should update if checkNrgBalance in varSubstep is turned on + updateCp = .true. + needCm = .true. + else + message=trim(message)//'unknown heat capacity computation' + err=1; return + end if + ! get the start and end indices for the soil compression calculations if(scalarSolution)then jState = pack(ixControlVolume, ixMapFull2Subset/=integerMissing) @@ -412,113 +423,40 @@ subroutine eval8summaWithPrime(& if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) if(updateCp)then - ! *** compute volumetric heat capacity C_p - if(ixHowHeatCap == enthalpyFD)then - ! compute H_T prime without phase change - call t2enthalpyPrime(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - ! input: state variables for the vegetation canopy - scalarCanairTempPrime, & ! intent(in): prime value for canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) - scalarCanopyTempPrime, & ! intent(in): prime value for canopy temperature (K) - scalarCanopyWatPrime, & ! intent(in): prime value for canopy total water (kg m-2) - ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerTempPrime, & ! intent(in): prime vector of layer temperature (K) - mLayerVolFracWatPrime, & ! intent(in): prime vector of volumetric total water content (-) - mLayerMatricHeadPrime, & ! intent(in): prime vector of total water matric potential (m) - ! input: pre-computed derivatives - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - ! output: enthalpy prime - scalarCanairEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyPrime, & ! intent(out): prime vector of temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyPrime, & ! intent(out): prime vector of temperature component of enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! *** compute volumetric heat capacity C_p = dH_T/dT - call computHeatCap(& - ! input: control variables - nLayers, & ! intent(in): number of layers (soil+snow) - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) - ! input output data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value of mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value of the liquid water on the vegetation canopy (kg m-2) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyTempPrime, & ! intent(in): prime value of canopy temperature (K) - scalarCanopyEnthalpyPrime, & ! intent(in): prime value of temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial vector of temperature - mLayerTempPrime, & ! intent(in): prime vector of temperature - mLayerEnthalpyPrime, & ! intent(in): prime vector of temperature component of enthalpy for snow and soil - mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) - ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - ! output - heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(out): heat capacity for snow and soil - dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - else if(ixHowHeatCap == closedForm)then - call computHeatCapAnalytic(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) - ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiqTrial, & ! intent(in): fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) - ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - ! input output data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output - heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow - dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - endif !(choice of how compute heat capacity) + ! *** compute volumetric heat capacity C_p + call computHeatCapAnalytic(& + ! input: control variables + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) + ! input: state variables + scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiqTrial, & ! intent(in): fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) + ! input: pre-computed derivatives + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + ! input output data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + ! output + heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow + dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! compute multiplier of state vector call computStatMult(& @@ -578,21 +516,21 @@ subroutine eval8summaWithPrime(& if(needCm)then ! compute C_m call computCm(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - ! input: state variables - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (-) - ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - ! output - canopyCmTrial, & ! intent(out): Cm for vegetation (J kg K-1) - mLayerCmTrial, & ! intent(out): Cm for soil and snow (J kg K-1) - dCm_dTk, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) - dCm_dTkCanopy, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) - err,cmessage) ! intent(out): error control + ! input: control variables + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + ! input: state variables + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (-) + ! input data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + ! output + canopyCmTrial, & ! intent(out): Cm for vegetation (J kg K-1) + mLayerCmTrial, & ! intent(out): Cm for soil and snow (J kg K-1) + dCm_dTk, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) + dCm_dTkCanopy, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) + err,cmessage) ! intent(out): error control else canopyCmTrial = 0._qp mLayerCmTrial = 0._qp @@ -600,6 +538,43 @@ subroutine eval8summaWithPrime(& dCm_dTkCanopy = 0._rkind endif ! needCm + if(ixHowHeatCap == enthalpyFD)then ! use residual as enthalpy_prime - (phase change)_prime + ! compute temperature component of enthalpy prime + call t2enthalpyPrime(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + ! input: state variables for the vegetation canopy + scalarCanairTempPrime, & ! intent(in): prime value for canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) + scalarCanopyTempPrime, & ! intent(in): prime value for canopy temperature (K) + scalarCanopyWatPrime, & ! intent(in): prime value for canopy total water (kg m-2) + ! input: variables for the snow-soil domain + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerTempPrime, & ! intent(in): prime vector of layer temperature (K) + mLayerVolFracWatPrime, & ! intent(in): prime vector of volumetric total water content (-) + mLayerMatricHeadPrime, & ! intent(in): prime vector of total water matric potential (m) + ! input: pre-computed derivatives + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + ! output: enthalpy prime + scalarCanairEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyPrime, & ! intent(out): prime vector of temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyPrime, & ! intent(out): prime vector of temperature component of enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + else if(ixHowHeatCap == closedForm)then ! use residual as (Cp*DeltaTemp + Cm*DeltaTheta)_delta - (phase change)_delta + scalarCanairEnthalpyPrime = realMissing + scalarCanopyEnthalpyPrime = realMissing + mLayerEnthalpyPrime = realMissing + endif !(choice of how compute heat capacity) + ! save the number of flux calls per time step indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) + 1 @@ -681,25 +656,28 @@ subroutine eval8summaWithPrime(& nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers + ixHowHeatCap==enthalpyFD, & ! intent(in): flag to use enthalpy form of residual ! input: flux vectors sMul, & ! intent(in): state vector multiplier (used in the residual calculations) fluxVec, & ! intent(in): flux vector ! input: state variables (already disaggregated into scalars and vectors) - scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) - mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) - scalarCanairTempPrime, & ! intent(in): Prime value for the temperature of the canopy air space (K s-1) - scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K s-1) - scalarCanopyWatPrime, & ! intent(in): Prime value for the water on the vegetation canopy (kg m-2 s-1) - mLayerTempPrime, & ! intent(in): Prime value for the temperature of each snow and soil layer (K s-1) - scalarAquiferStoragePrime, & ! intent(in): Prime value of storage of water in the aquifer (m s-1) + scalarCanairTempPrime, & ! intent(in): prime value for the temperature of the canopy air space (K s-1) + scalarCanopyTempPrime, & ! intent(in): prime value for the temperature of the vegetation canopy (K s-1) + scalarCanopyWatPrime, & ! intent(in): prime value for the water on the vegetation canopy (kg m-2 s-1) + mLayerTempPrime, & ! intent(in): prime vector of the temperature of each snow and soil layer (K s-1) + scalarAquiferStoragePrime, & ! intent(in): prime value for storage of water in the aquifer (m s-1) ! input: diagnostic variables defining the liquid water and ice content (function of state variables) - scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2 s-1) - scalarCanopyLiqPrime, & ! intent(in): Prime value for the liq on the vegetation canopy (kg m-2 s-1) - mLayerVolFracIcePrime, & ! intent(in): Prime value for the volumetric ice in each snow and soil layer (s-1) - mLayerVolFracWatPrime, & ! intent(in): Prime value for the volumetric water in each snow and soil layer (s-1) - mLayerVolFracLiqPrime, & ! intent(in): Prime value for the volumetric liq in each snow and soil layer (s-1) + scalarCanopyIcePrime, & ! intent(in): prime value for the ice on the vegetation canopy (kg m-2 s-1) + scalarCanopyLiqPrime, & ! intent(in): prime value for the liq on the vegetation canopy (kg m-2 s-1) + mLayerVolFracIcePrime, & ! intent(in): prime vector of the volumetric ice in each snow and soil layer (s-1) + mLayerVolFracWatPrime, & ! intent(in): prime vector of the volumetric water in each snow and soil layer (s-1) + mLayerVolFracLiqPrime, & ! intent(in): prime vector of the volumetric liq in each snow and soil layer (s-1) + ! input: enthalpy terms canopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) + scalarCanairEnthalpyPrime, & ! intent(in): prime value for the temperature component of the enthalpy of the canopy air space (J m-2 s-1) + scalarCanopyEnthalpyPrime, & ! intent(in): prime value for the temperature component of the enthalpy of the vegetation canopy (J m-2 s-1) + mLayerEnthalpyPrime, & ! intent(in): prime vector of the temperature component of the enthalpy of each snow and soil layer (J m-2 s-1) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -711,7 +689,7 @@ subroutine eval8summaWithPrime(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - else !currently not using residuals outside Sundials! + else ! currently not using residuals outside Sundials! dt1 = 1._qp endif @@ -810,11 +788,11 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%scalarCanopyTempPrime, & ! intent(out): prime value for temperature of the vegetation canopy (K s-1) eqns_data%scalarCanopyWatPrime, & ! intent(out): prime value for total water content of the vegetation canopy (kg m-2 s-1) eqns_data%scalarCanopyIcePrime, & ! intent(out): prime value for mass of ice on the vegetation canopy (kg m-2 s-1) - eqns_data%mLayerTempPrime, & ! intent(out): prime value for temperature of each snow and soil layer (K s-1) - eqns_data%mLayerMatricHeadPrime, & ! intent(out): prime value for matric head of each snow and soil layer (m s-1) - eqns_data%mLayerMatricHeadLiqPrime, & ! intent(out): prime value for liquid water matric potential (m s-1) - eqns_data%mLayerVolFracWatPrime, & ! intent(out): prime value for volumetric total water content of each snow and soil layer (s-1) - eqns_data%mLayerVolFracIcePrime, & ! intent(out): prime value for volumetric fraction of ice (s-1) + eqns_data%mLayerTempPrime, & ! intent(out): prime vector of temperature of each snow and soil layer (K s-1) + eqns_data%mLayerMatricHeadPrime, & ! intent(out): prime vector of matric head of each snow and soil layer (m s-1) + eqns_data%mLayerMatricHeadLiqPrime, & ! intent(out): prime vector of liquid water matric potential (m s-1) + eqns_data%mLayerVolFracWatPrime, & ! intent(out): prime vector of volumetric total water content of each snow and soil layer (s-1) + eqns_data%mLayerVolFracIcePrime, & ! intent(out): prime vector of volumetric fraction of ice (s-1) ! output: enthalpy prime values eqns_data%scalarCanairEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the canopy air space (J m-3 s-1) eqns_data%scalarCanopyEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the vegetation canopy (J m-3 s-1) From 0755a28009f7b00750e0f2abc884dedee2986d19 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 26 Jan 2024 15:56:45 +0900 Subject: [PATCH 1099/1472] remove computHeatCap on FD completely, code not needed --- build/source/engine/computHeatCap.f90 | 182 -------------------- build/source/engine/eval8summa.f90 | 1 - build/source/engine/eval8summaWithPrime.f90 | 1 - 3 files changed, 184 deletions(-) diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index c71b903be..927674c39 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -67,7 +67,6 @@ module computHeatCap_module ! privacy implicit none private -public::computHeatCap public::computStatMult public::computHeatCapAnalytic public::computCm @@ -75,187 +74,6 @@ module computHeatCap_module contains -! ********************************************************************************************************** -! public subroutine computHeatCap: compute diagnostic energy variables (heat capacity) -! ********************************************************************************************************** -subroutine computHeatCap(& - ! input: control variables - nLayers, & ! intent(in): number of layers (soil+snow) - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) - ! input output data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! input: state variables - scalarCanopyIce, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiquid, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyTempDelta, & ! intent(in): delta value of canopy temperature (prime or trial-prev) - scalarCanopyEnthalpyDelta, & ! intent(in): delta enthalpy of the vegetation canopy (prime or trial-prev) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial temperature - mLayerTempDelta, & ! intent(in): delta temperature (prime or trial-prev) - mLayerEnthalpyDelta, & ! intent(in): delta enthalpy for snow and soil (prime or trial-prev) - mLayerMatricHeadTrial, & ! intent(in): trial total water matric potential (m) - ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - ! output - heatCapVeg, & ! intent(out): heat capacity for canopy - mLayerHeatCap, & ! intent(out): heat capacity for snow and soil - dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - ! output: error control - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------------- - ! provide access to external subroutines - USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists - ! -------------------------------------------------------------------------------------------------------------------------------- - ! input: control variables - integer(i4b),intent(in) :: nLayers ! number of layers (soil+snow) - logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) - ! input/output: data structures - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model layer indices - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - ! input: state variables - real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) - real(rkind),intent(in) :: scalarCanopyLiquid ! trial value for the liquid water on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature - real(rkind),intent(in) :: scalarCanopyEnthalpyDelta ! delta enthalpy of the vegetation canopy (prime or trial-prev) - real(rkind),intent(in) :: scalarCanopyTempDelta ! delta of canopy temperature (prime or trial-prev) - real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) - real(rkind),intent(in) :: mLayerTempTrial(:) ! trial temperature (prime or trial-prev) - real(rkind),intent(in) :: mLayerTempDelta(:) ! delta temperature - real(rkind),intent(in) :: mLayerEnthalpyDelta(:) ! delta enthalpy for snow and soil (prime or trial-prev) - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! vector of total water matric potential (m) - ! input: pre-computed derivatives - real(rkind),intent(in) :: dTheta_dTkCanopy ! derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: scalarFracLiqVeg ! fraction of canopy liquid water (-) - real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative of volumetric liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) - real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) - ! output: - real(qp),intent(out) :: heatCapVeg ! heat capacity for canopy - real(qp),intent(out) :: mLayerHeatCap(:) ! heat capacity for snow and soil - real(rkind),intent(out) :: dVolHtCapBulk_dPsi0(:) ! derivative in bulk heat capacity w.r.t. matric potential - real(rkind),intent(out) :: dVolHtCapBulk_dTheta(:) ! derivative in bulk heat capacity w.r.t. volumetric water content - real(rkind),intent(out) :: dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. volumetric water content - real(rkind),intent(out) :: dVolHtCapBulk_dTk(:) ! derivative in bulk heat capacity w.r.t. temperature - real(rkind),intent(out) :: dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: iSoil ! index of soil layer - real(rkind) :: delT ! temperature change - real(rkind) :: delEnt ! enthalpy change - real(rkind) :: fLiq ! fraction of liquid water - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - ! -------------------------------------------------------------------------------------------------------------------------------- - ! associate variables in data structure - associate(& - ! input: coordinate variables - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) - ! input: heat capacity and thermal conductivity - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) - ! input: depth varying soil parameters - iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) - ) ! end associate statemen - ! initialize error control - err=0; message="computHeatCap/" - - ! initialize the soil layer - iSoil=integerMissing - - ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) - if(computeVegFlux)then - if(abs(scalarCanopyTempDelta) <= 1e-14_rkind)then - heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component - Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component - Cp_ice*scalarCanopyIce/canopyDepth ! ice component - else - heatCapVeg = scalarCanopyEnthalpyDelta / scalarCanopyTempDelta - endif - - ! derivatives for Jacobian are from analytical solution - fLiq = scalarFracLiqVeg - dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) - if(scalarCanopyTempTrial < Tfreeze)then - dVolHtCapBulk_dTkCanopy = iden_water * (-Cp_ice + Cp_water) * dTheta_dTkCanopy ! no derivative in air - else - dVolHtCapBulk_dTkCanopy = 0._rkind - endif - endif - - ! loop through layers - do iLayer=1,nLayers - ! get the soil layer - if(iLayer>nSnow) iSoil = iLayer-nSnow - - if(abs(mLayerTempDelta(iLayer)) <= 1e-14_rkind)then - select case(layerType(iLayer)) - ! * soil - case(iname_soil) - mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component - iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component - case(iname_snow) - mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return - end select - else - mLayerHeatCap(iLayer) = mLayerEnthalpyDelta(iLayer) / mLayerTempDelta(iLayer) - endif - - ! derivatives for Jacobian are from analytical solution - select case(layerType(iLayer)) - ! * soil - case(iname_soil) - dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use - Tcrit = crit_soilT( mLayerMatricHeadTrial(iSoil) ) - if( mLayerTempTrial(iLayer) < Tcrit)then - dVolHtCapBulk_dPsi0(iSoil) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(iSoil) - dVolHtCapBulk_dTk(iLayer) = (-iden_ice * Cp_ice + iden_water * Cp_water) * mLayerdTheta_dTk(iLayer) - else - dVolHtCapBulk_dPsi0(iSoil) = (iden_water*Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(iSoil) - dVolHtCapBulk_dTk(iLayer) = 0._rkind - endif - ! * snow - case(iname_snow) - fLiq = mLayerFracLiqSnow(iLayer) - dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air - if( mLayerTempTrial(iLayer) < Tfreeze)then - dVolHtCapBulk_dTk(iLayer) = ( iden_water * (-Cp_ice + Cp_water) + iden_air * (iden_water/iden_ice - 1._rkind) * Cp_air ) * mLayerdTheta_dTk(iLayer) - else - dVolHtCapBulk_dTk(iLayer) = 0._rkind - endif - end select - - end do ! looping through layers - - end associate - -end subroutine computHeatCap - ! ********************************************************************************************************** ! public subroutine computStatMult: get scale factors ! ********************************************************************************************************** diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 62d3848a9..874e42103 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -130,7 +130,6 @@ subroutine eval8summa(& USE enthalpyTemp_module, only:t2enthalpy ! compute enthalpy USE computFlux_module, only:soilCmpres ! compute soil compression USE computFlux_module, only:computFlux ! compute fluxes given a state vector - USE computHeatCap_module,only:computHeatCap ! recompute enthalpy finite difference heat capacity (Cp) and derivatives USE computHeatCap_module,only:computHeatCapAnalytic ! recompute closed form heat capacity (Cp) and derivatives USE computHeatCap_module,only:computCm ! compute Cm and derivatives USE computHeatCap_module, only:computStatMult ! recompute state multiplier diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index dd24b6366..008025e40 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -119,7 +119,6 @@ subroutine eval8summaWithPrime(& USE enthalpyTempAddPrime_module, only:t2enthalpyPrime ! compute enthalpy prime USE computFlux_module, only:soilCmpresPrime ! compute soil compression USE computFlux_module, only:computFlux ! compute fluxes given a state vector - USE computHeatCap_module,only:computHeatCap ! recompute enthalpy finite difference heat capacity (Cp) and derivatives USE computHeatCap_module,only:computHeatCapAnalytic ! recompute closed form heat capacity (Cp) and derivatives USE computHeatCap_module,only:computCm ! compute Cm and derivatives USE computHeatCap_module, only:computStatMult ! recompute state multiplier From 5e2c62c013dc308471c1befa34120f711d87d77b Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 26 Jan 2024 03:53:44 -0600 Subject: [PATCH 1100/1472] The stateSplit loop has been removed from opSplittin; operations completely transferred to the split_select loop. --- build/source/engine/opSplittin.f90 | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index c13dbb5f9..a0b54c8ce 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -420,11 +420,13 @@ subroutine opSplittin(& call split_select % initialize_iStateSplit; split_select % stateSplit=.true.; ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) end if if (split_select % stateSplit.eqv..true.) then - stateSplit: do !iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) +! stateSplit: do !iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) iStateSplit=split_select % iStateSplit if (split_select % iStateSplit > nStateSplit) then - split_select % stateSplit=.false.; exit stateSplit - end if + split_select % stateSplit=.false.; !exit stateSplit + end if + end if + if (split_select % stateSplit.eqv..true.) then ! define state subsets for a given split... call split_select % get_indices(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) ! may be able to remove this once all indices are fully handled by split_select @@ -456,12 +458,16 @@ subroutine opSplittin(& call confirm_variable_updates; if (return_flag.eqv..true.) return ! check that state variables updated - return if error call success_check ! check for success - if (exit_stateThenDomain) then; call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.false.; split_select % domainSplit=.false.; split_select % solution=.false.; split_select % stateSplit=.false.; exit stateSplit; end if ! exit loops if necessary -- exit last available loop to avoid unnecessary operations -- deactivate inner loops - if (exit_solution) then; split_select % solution=.false.; split_select % stateSplit=.false.; exit stateSplit; end if - if (return_flag.eqv..true.) return ! return if error - - call split_select % advance_iStateSplit - end do stateSplit ! solution with split layers + if (exit_stateThenDomain) then; call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.false.; split_select % domainSplit=.false.; split_select % solution=.false.; split_select % stateSplit=.false.; end if ! exit loops if necessary -- exit last available loop to avoid unnecessary operations -- deactivate inner loops + if (split_select % stateThenDomain.eqv..true.) then + if (exit_solution) then; split_select % solution=.false.; split_select % stateSplit=.false.; end if + if (split_select % solution.eqv..true.) then + if (return_flag.eqv..true.) return ! return if error + + call split_select % advance_iStateSplit + end if + end if +! end do stateSplit ! solution with split layers end if ! stateSplit if (split_select % stateSplit.eqv..false.) then if (split_select % solution.eqv..true.) then From 567941f772728aef5658066df6f029e30212b717 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 27 Jan 2024 06:06:04 -0600 Subject: [PATCH 1101/1472] Added class procedures to simplify finalize branches in opSplittin. --- build/source/engine/opSplittin.f90 | 134 +++++++++++++---------------- 1 file changed, 61 insertions(+), 73 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index a0b54c8ce..1cb8e7106 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -168,13 +168,17 @@ module opSplittin_module procedure :: initialize_ixSolution => split_select_initialize_ixSolution ! initialize operator splitting indices procedure :: initialize_iStateSplit => split_select_initialize_iStateSplit ! initialize operator splitting indices procedure :: get_stateMask => split_select_compute_stateMask ! compute stateMask and nSubset and load into class object - procedure :: get_indices => split_select_load_indices ! load operator splitting indices into class object procedure :: advance_ixCoupling => split_select_advance_ixCoupling ! advance coupling loop iterator procedure :: advance_iStateTypeSplit => split_select_advance_iStateTypeSplit ! advance coupling loop iterator procedure :: advance_ixStateThenDomain => split_select_advance_ixStateThenDomain ! advance coupling loop iterator procedure :: advance_iDomainSplit => split_select_advance_iDomainSplit ! advance coupling loop iterator procedure :: advance_ixSolution => split_select_advance_ixSolution ! advance coupling loop iterator procedure :: advance_iStateSplit => split_select_advance_iStateSplit ! advance coupling loop iterator + procedure :: logic_finalize_stateTypeSplitting => split_select_logic_finalize_stateTypeSplitting ! get logical for branch + procedure :: logic_finalize_stateThenDomain => split_select_logic_finalize_stateThenDomain ! get logical for branch + procedure :: logic_finalize_domainSplit => split_select_logic_finalize_domainSplit ! get logical for branch + procedure :: logic_finalize_solution => split_select_logic_finalize_solution ! get logical for branch + procedure :: logic_finalize_stateSplit => split_select_logic_finalize_stateSplit ! get logical for branch end type split_select_type contains @@ -350,7 +354,6 @@ subroutine opSplittin(& call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs call split_select % initialize_flags ! initialize loop control flags split_select_loop: do iSplit=1,500 -!write(*,*) "A:",ixCoupling,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit if (split_select % stateSplit.eqv..false.) then if (split_select % solution.eqv..false.) then if (split_select % domainSplit.eqv..false.) then @@ -362,7 +365,6 @@ subroutine opSplittin(& end if if (split_select % stateTypeSplitting.eqv..true.) then iStateTypeSplit=split_select % iStateTypeSplit; if (iStateTypeSplit.gt.nStateTypeSplit) split_select % stateTypeSplitting=.false. -!wri te(*,*) "B:",iStateTypeSplit,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit end if end if end if @@ -379,7 +381,6 @@ subroutine opSplittin(& end if if (split_select % stateThenDomain.eqv..true.) then ! stateThenDomain loop ixStateThenDomain=split_select % ixStateThenDomain -!write (*,*) "C:",ixStateThenDomain,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit if (ixStateThenDomain > (1+tryDomainSplit)) then ixStateThenDomain=ixStateThenDomain-1; split_select % ixStateThenDomain = ixStateThenDomain ! correct index needed after loop exit split_select % stateThenDomain=.false. ! eqivalent to exiting the stateThenDomain loop @@ -397,7 +398,6 @@ subroutine opSplittin(& end if if (split_select % domainSplit.eqv..true.) then iDomainSplit=split_select % iDomainSplit -!write( *,*) "D:",iDomainSplit,split_select%stateTypeSplitting,split_select%stateThenDomain,split_select%domainSplit if (split_select % iDomainSplit > nDomainSplit) then split_select % domainSplit=.false. end if @@ -419,8 +419,7 @@ subroutine opSplittin(& call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs call split_select % initialize_iStateSplit; split_select % stateSplit=.true.; ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) end if - if (split_select % stateSplit.eqv..true.) then -! stateSplit: do !iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) + if (split_select % stateSplit.eqv..true.) then ! stateSplit begins iStateSplit=split_select % iStateSplit if (split_select % iStateSplit > nStateSplit) then split_select % stateSplit=.false.; !exit stateSplit @@ -429,7 +428,6 @@ subroutine opSplittin(& if (split_select % stateSplit.eqv..true.) then ! define state subsets for a given split... - call split_select % get_indices(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) ! may be able to remove this once all indices are fully handled by split_select call split_select % get_stateMask(indx_data,err,cmessage,message,return_flag) nSubset = split_select % nSubset; stateMask = split_select % stateMask if (return_flag.eqv..true.) return @@ -467,60 +465,32 @@ subroutine opSplittin(& call split_select % advance_iStateSplit end if end if -! end do stateSplit ! solution with split layers - end if ! stateSplit - if (split_select % stateSplit.eqv..false.) then - if (split_select % solution.eqv..true.) then - if (split_select % stateThenDomain.eqv..true.) then - call split_select % advance_ixSolution - end if - end if + end if ! stateSplit ends + if (split_select % logic_finalize_stateSplit()) then + call split_select % advance_ixSolution end if end if ! end solution loop - if (split_select % stateSplit.eqv..false.) then - if (split_select % solution.eqv..false.) then - if (split_select % stateThenDomain.eqv..true.) then - call finalize_solution ! final steps following solution loop - call split_select % advance_iDomainSplit - end if - end if - end if - end if ! domainSplit - if (split_select % stateSplit.eqv..false.) then - if (split_select % solution.eqv..false.) then - if (split_select % domainSplit.eqv..false.) then - if (split_select % stateThenDomain.eqv..true.) call split_select % advance_ixStateThenDomain - end if + if (split_select % logic_finalize_solution()) then + call finalize_solution ! final steps following solution loop + call split_select % advance_iDomainSplit end if + end if ! domainSplit ends + if (split_select % logic_finalize_domainSplit()) then + call split_select % advance_ixStateThenDomain end if - end if ! stateThenDomain - if (split_select % stateSplit.eqv..false.) then - if (split_select % solution.eqv..false.) then - if (split_select % domainSplit.eqv..false.) then - if (split_select % stateThenDomain.eqv..false.) then - call finalize_stateThenDomain; if (return_flag.eqv..true.) return ! final steps following the stateThenDomain loop - call split_select % advance_iStateTypeSplit - end if - end if - end if - end if - end if ! stateTypeSplitting - if (split_select % stateSplit.eqv..false.) then - if (split_select % solution.eqv..false.) then - if (split_select % domainSplit.eqv..false.) then - if (split_select % stateThenDomain.eqv..false.) then - if (split_select % stateTypeSplitting.eqv..false.) then - call finalize_stateTypeSplitting - if (exit_coupling) then - call split_select % initialize_ixCoupling; exit split_select_loop ! success = exit the coupling loop - end if - - call split_select % advance_ixCoupling - end if - end if + end if ! stateThenDomain ends + if (split_select % logic_finalize_stateThenDomain()) then + call finalize_stateThenDomain; if (return_flag.eqv..true.) return ! final steps following the stateThenDomain loop + call split_select % advance_iStateTypeSplit end if + end if ! stateTypeSplitting ends + if (split_select % logic_finalize_stateTypeSplitting()) then + call finalize_stateTypeSplitting + if (exit_coupling) then + call split_select % initialize_ixCoupling; exit split_select_loop ! success = exit the coupling loop end if - end if + call split_select % advance_ixCoupling + end if end do split_select_loop call finalize_coupling ! check variables and fluxes, and apply step halving if needed @@ -1212,23 +1182,6 @@ subroutine split_select_advance_iStateSplit(split_select) split_select % iStateSplit = split_select % iStateSplit + 1 end subroutine split_select_advance_iStateSplit -subroutine split_select_load_indices(split_select,ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) - ! *** load operator splitting indices for split_select_type class *** - class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector - integer(i4b),intent(in) :: ixCoupling - integer(i4b),intent(in) :: ixSolution - integer(i4b),intent(in) :: ixStateThenDomain - integer(i4b),intent(in) :: iStateTypeSplit - integer(i4b),intent(in) :: iDomainSplit - integer(i4b),intent(in) :: iStateSplit - split_select % ixCoupling = ixCoupling - split_select % ixSolution = ixSolution - split_select % ixStateThenDomain = ixStateThenDomain - split_select % iStateTypeSplit = iStateTypeSplit - split_select % iDomainSplit = iDomainSplit - split_select % iStateSplit = iStateSplit -end subroutine split_select_load_indices - subroutine split_select_initialize_ixCoupling(split_select) ! *** initialize operator splitting indices for split_select_type class *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector @@ -1265,6 +1218,41 @@ subroutine split_select_initialize_iStateSplit(split_select) split_select % iStateSplit = 1 end subroutine split_select_initialize_iStateSplit +logical(lgt) function split_select_logic_finalize_stateSplit(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_finalize_stateSplit=& + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..true.).and.(split_select % stateThenDomain.eqv..true.) +end function split_select_logic_finalize_stateSplit + +logical(lgt) function split_select_logic_finalize_solution(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_finalize_solution=& + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.).and.(split_select % stateThenDomain.eqv..true.) +end function split_select_logic_finalize_solution + +logical(lgt) function split_select_logic_finalize_domainSplit(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_finalize_domainSplit=& + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.).and.(split_select % domainSplit.eqv..false.).and.(split_select % stateThenDomain.eqv..true.) +end function split_select_logic_finalize_domainSplit + +logical(lgt) function split_select_logic_finalize_stateThenDomain(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_finalize_stateThenDomain=& + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.).and.(split_select % domainSplit.eqv..false.).and.(split_select % stateThenDomain.eqv..false.) +end function split_select_logic_finalize_stateThenDomain + +logical(lgt) function split_select_logic_finalize_stateTypeSplitting(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_finalize_stateTypeSplitting=& + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.).and.(split_select % domainSplit.eqv..false.).and.(split_select % stateThenDomain.eqv..false.).and.(split_select % stateTypeSplitting.eqv..false.) +end function split_select_logic_finalize_stateTypeSplitting + subroutine split_select_compute_stateMask(split_select,indx_data,err,cmessage,message,return_flag) ! *** Get the mask for the state subset *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector From 5552f4592adeafbeecf85783c974eeb9cd036af3 Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 30 Jan 2024 05:47:53 -0600 Subject: [PATCH 1102/1472] Added class procedures to simplify syntax related to initialize branches in opSplittin. --- build/source/engine/opSplittin.f90 | 128 ++++++++++++++++++----------- 1 file changed, 79 insertions(+), 49 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 1cb8e7106..e62033f14 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -167,18 +167,31 @@ module opSplittin_module procedure :: initialize_iDomainSplit => split_select_initialize_iDomainSplit ! initialize operator splitting indices procedure :: initialize_ixSolution => split_select_initialize_ixSolution ! initialize operator splitting indices procedure :: initialize_iStateSplit => split_select_initialize_iStateSplit ! initialize operator splitting indices + procedure :: get_stateMask => split_select_compute_stateMask ! compute stateMask and nSubset and load into class object + procedure :: advance_ixCoupling => split_select_advance_ixCoupling ! advance coupling loop iterator procedure :: advance_iStateTypeSplit => split_select_advance_iStateTypeSplit ! advance coupling loop iterator procedure :: advance_ixStateThenDomain => split_select_advance_ixStateThenDomain ! advance coupling loop iterator procedure :: advance_iDomainSplit => split_select_advance_iDomainSplit ! advance coupling loop iterator procedure :: advance_ixSolution => split_select_advance_ixSolution ! advance coupling loop iterator procedure :: advance_iStateSplit => split_select_advance_iStateSplit ! advance coupling loop iterator - procedure :: logic_finalize_stateTypeSplitting => split_select_logic_finalize_stateTypeSplitting ! get logical for branch - procedure :: logic_finalize_stateThenDomain => split_select_logic_finalize_stateThenDomain ! get logical for branch - procedure :: logic_finalize_domainSplit => split_select_logic_finalize_domainSplit ! get logical for branch - procedure :: logic_finalize_solution => split_select_logic_finalize_solution ! get logical for branch - procedure :: logic_finalize_stateSplit => split_select_logic_finalize_stateSplit ! get logical for branch + + procedure :: logic_exit_stateTypeSplitting => split_select_logic_exit_stateTypeSplitting ! get logical for branch + !procedure :: logic_exit_stateThenDomain => split_select_logic_exit_stateThenDomain ! get logical for branch + !procedure :: logic_exit_domainSplit => split_select_logic_exit_domainSplit ! get logical for branch + !procedure :: logic_exit_solution => split_select_logic_exit_solution ! get logical for branch + + procedure :: logic_initialize_stateTypeSplitting => split_select_logic_initialize_stateTypeSplitting ! get logical for branch + procedure :: logic_initialize_stateThenDomain => split_select_logic_initialize_stateThenDomain ! get logical for branch + procedure :: logic_initialize_domainSplit => split_select_logic_initialize_domainSplit ! get logical for branch + procedure :: logic_initialize_solution => split_select_logic_initialize_solution ! get logical for branch + + procedure :: logic_finalize_stateTypeSplitting => split_select_logic_finalize_stateTypeSplitting ! get logical for branch + procedure :: logic_finalize_stateThenDomain => split_select_logic_finalize_stateThenDomain ! get logical for branch + procedure :: logic_finalize_domainSplit => split_select_logic_finalize_domainSplit ! get logical for branch + procedure :: logic_finalize_solution => split_select_logic_finalize_solution ! get logical for branch + procedure :: logic_finalize_stateSplit => split_select_logic_finalize_stateSplit ! get logical for branch end type split_select_type contains @@ -354,76 +367,59 @@ subroutine opSplittin(& call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs call split_select % initialize_flags ! initialize loop control flags split_select_loop: do iSplit=1,500 - if (split_select % stateSplit.eqv..false.) then - if (split_select % solution.eqv..false.) then - if (split_select % domainSplit.eqv..false.) then + if (split_select % logic_initialize_stateTypeSplitting()) then + !if (split_select % stateTypeSplitting.eqv..false.) then + ixCoupling=split_select % ixCoupling; if (ixCoupling.gt.nCoupling) exit split_select_loop ! exit if all splits are exhausted + call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs + call split_select % initialize_iStateTypeSplit; split_select % stateTypeSplitting=.true. + !end if + end if + if (split_select % logic_exit_stateTypeSplitting()) then + iStateTypeSplit=split_select % iStateTypeSplit; if (iStateTypeSplit.gt.nStateTypeSplit) split_select % stateTypeSplitting=.false. + end if + if (split_select % stateTypeSplitting.eqv..true.) then + if (split_select % logic_initialize_stateThenDomain()) then if (split_select % stateThenDomain.eqv..false.) then - if (split_select % stateTypeSplitting.eqv..false.) then - ixCoupling=split_select % ixCoupling; if (ixCoupling.gt.nCoupling) exit split_select_loop ! exit if all splits are exhausted - call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs - call split_select % initialize_iStateTypeSplit; split_select % stateTypeSplitting=.true. - end if - if (split_select % stateTypeSplitting.eqv..true.) then - iStateTypeSplit=split_select % iStateTypeSplit; if (iStateTypeSplit.gt.nStateTypeSplit) split_select % stateTypeSplitting=.false. - end if + ! first try the state type split, then try the domain split within a given state type + call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split + call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.true. end if - end if - end if - end if - if (split_select % stateTypeSplitting.eqv..true.) then - if (split_select % stateSplit.eqv..false.) then - if (split_select % solution.eqv..false.) then - if (split_select % domainSplit.eqv..false.) then - if (split_select % stateThenDomain.eqv..false.) then - ! first try the state type split, then try the domain split within a given state type - call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split - call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.true. - end if - if (split_select % stateThenDomain.eqv..true.) then ! stateThenDomain loop - ixStateThenDomain=split_select % ixStateThenDomain - if (ixStateThenDomain > (1+tryDomainSplit)) then - ixStateThenDomain=ixStateThenDomain-1; split_select % ixStateThenDomain = ixStateThenDomain ! correct index needed after loop exit - split_select % stateThenDomain=.false. ! eqivalent to exiting the stateThenDomain loop - end if + if (split_select % stateThenDomain.eqv..true.) then ! stateThenDomain loop + ixStateThenDomain=split_select % ixStateThenDomain + if (ixStateThenDomain > (1+tryDomainSplit)) then + ixStateThenDomain=ixStateThenDomain-1; split_select % ixStateThenDomain = ixStateThenDomain ! correct index needed after loop exit + split_select % stateThenDomain=.false. ! eqivalent to exiting the stateThenDomain loop end if - end if end if end if if (split_select % stateThenDomain.eqv..true.) then - if (split_select % stateSplit.eqv..false.) then - if (split_select % solution.eqv..false.) then + if (split_select % logic_initialize_domainSplit()) then if (split_select % domainSplit.eqv..false.) then call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs call split_select % initialize_iDomainSplit; split_select % domainSplit=.true. end if if (split_select % domainSplit.eqv..true.) then iDomainSplit=split_select % iDomainSplit - if (split_select % iDomainSplit > nDomainSplit) then - split_select % domainSplit=.false. - end if + if (split_select % iDomainSplit > nDomainSplit) split_select % domainSplit=.false. end if - end if end if if (split_select % domainSplit.eqv..true.) then - if (split_select % stateSplit.eqv..false.) then + if (split_select % logic_initialize_solution()) then if (split_select % solution.eqv..false.) then; call split_select % initialize_ixSolution; split_select % solution=.true.; end if if (split_select % solution.eqv..true.) then - ixSolution=split_select % ixSolution - if (split_select % ixSolution > nsolutions) then - split_select % solution=.false. - end if + ixSolution=split_select % ixSolution + if (split_select % ixSolution > nsolutions) split_select % solution=.false. end if end if if (split_select % solution.eqv..true.) then + !logic_initialize_stateSplit= !!! do we need this? if (split_select % stateSplit.eqv..false.) then call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs call split_select % initialize_iStateSplit; split_select % stateSplit=.true.; ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) end if if (split_select % stateSplit.eqv..true.) then ! stateSplit begins iStateSplit=split_select % iStateSplit - if (split_select % iStateSplit > nStateSplit) then - split_select % stateSplit=.false.; !exit stateSplit - end if + if (split_select % iStateSplit > nStateSplit) split_select % stateSplit=.false.; !exit stateSplit end if if (split_select % stateSplit.eqv..true.) then @@ -1218,6 +1214,40 @@ subroutine split_select_initialize_iStateSplit(split_select) split_select % iStateSplit = 1 end subroutine split_select_initialize_iStateSplit +logical(lgt) function split_select_logic_initialize_stateTypeSplitting(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_initialize_stateTypeSplitting=& + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.).and.(split_select % domainSplit.eqv..false.).and.(split_select % stateThenDomain.eqv..false.).and.(split_select % stateTypeSplitting.eqv..false.) +end function split_select_logic_initialize_stateTypeSplitting + +logical(lgt) function split_select_logic_exit_stateTypeSplitting(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_exit_stateTypeSplitting=& + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.).and.(split_select % domainSplit.eqv..false.).and.(split_select % stateThenDomain.eqv..false.).and.(split_select % stateTypeSplitting.eqv..true.) +end function split_select_logic_exit_stateTypeSplitting + +logical(lgt) function split_select_logic_initialize_stateThenDomain(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_initialize_stateThenDomain=& + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.).and.(split_select % domainSplit.eqv..false.) +end function split_select_logic_initialize_stateThenDomain + +logical(lgt) function split_select_logic_initialize_domainSplit(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_initialize_domainSplit=& + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.) +end function split_select_logic_initialize_domainSplit + +logical(lgt) function split_select_logic_initialize_solution(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_initialize_solution=(split_select % stateSplit.eqv..false.) +end function split_select_logic_initialize_solution + logical(lgt) function split_select_logic_finalize_stateSplit(split_select) ! *** Compute logical for branch in split_select loop *** class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector From 4a5fa80805ba2a2c41a0c64236a571a75475d7d3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 30 Jan 2024 22:50:03 +0900 Subject: [PATCH 1103/1472] make constraints impose on whole increment --- build/source/engine/eval8summa.f90 | 77 ++++++++++++++++-------------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 874e42103..a6175d1be 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -801,8 +801,6 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st real(rkind) :: scalarTemp ! temperature of an individual snow layer (K) real(rkind) :: scalarIce ! volumetric ice content of an individual layer (-) real(rkind) :: volFracLiq ! volumetric liquid water content of an individual layer (-) - logical(lgt),dimension(nSoil) :: crosFlag ! flag to denote temperature crossing from unfrozen to frozen (or vice-versa) - logical(lgt) :: crosTempVeg ! flag to denoote where temperature crosses the freezing point real(rkind) :: xPsi00 ! matric head after applying the iteration increment (m) real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) real(rkind) :: critDiff ! temperature difference from critical (K) @@ -879,7 +877,7 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st small_delMatric = .true. ! flag to constain matric head change to be less than zMaxMatricIncrement zMaxMatricIncrement = 10._rkind ! maximum matric head increment (m) detect_events = .true. ! flag to do freezing point event detection and cross-over with epsT, works best if on - epsT = 1.e-3_rkind ! small interval above/below critical (K), works better if larger + epsT = 1.e-7_rkind ! small interval above/below critical (K), works better if larger water_bounds = .true. ! flag to force water bounds, works best if on case(numrec) small_delTemp = .true. ! flag to constain temperature change to be less than zMaxTempIncrement @@ -909,9 +907,10 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st do iState=1,size(ixMatOnly) ! define index of the hydrology state variable within the state subset ixLiq = ixMatOnly(iState) - ! place constraint for matric head + ! place constraint for matric head, scale iterations if(xInc(ixLiq) > zMaxMatricIncrement .and. stateVecPrev(ixLiq) > 0._rkind)then - xInc(ixLiq) = zMaxMatricIncrement + xIncFactor = zMaxMatricIncrement/xInc(ixLiq) ! scaling factor for the iteration increment (-) + xInc = zMaxMatricIncrement endif end do ! (loop through soil layers) endif @@ -923,29 +922,23 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st ! crossing freezing point event for vegetation if(ixVegNrg/=integerMissing)then ! initialize - critDiff = Tfreeze - stateVecPrev(ixVegNrg) - crosTempVeg = .false. + critDiff = Tfreeze - stateVecPrev(ixVegNrg) + cInc = xInc(ixVegNrg) ! initially frozen (T < Tfreeze) - if(critDiff > 0._rkind)then - if(xInc(ixVegNrg) > critDiff)then - crosTempVeg = .true. - cInc = critDiff + epsT ! constrained temperature increment (K) - end if + if(critDiff > 0._rkind)then ! (check crossing above zero) + if(xInc(ixVegNrg) > critDiff) cInc = critDiff + epsT ! constrained temperature increment (K) ! initially unfrozen (T > Tfreeze) - else - if(xInc(ixVegNrg) < critDiff)then - crosTempVeg = .true. - cInc = critDiff - epsT ! constrained temperature increment (K) - end if - end if ! switch between frozen and unfrozen + else ! (check crossing below zero) + if(xInc(ixVegNrg) < critDiff) cInc = critDiff - epsT ! constrained temperature increment (K) + end if ! (switch between initially frozen and initially unfrozen) ! scale iterations - if(crosTempVeg)then + if(xInc(ixVegNrg).ne.0._rkind)then xIncFactor = cInc/xInc(ixVegNrg) ! scaling factor for the iteration increment (-) xInc = xIncFactor*xInc ! scale iteration increments endif endif ! if the state variable for canopy temperature is included within the state subset - ! crossing freezing point event for snow + ! crossing freezing point event for snow, keep it below freezing if(nSnowOnlyNrg > 0)then do iLayer=1,nSnow ! check if energy state is included @@ -979,21 +972,19 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st TcSoil = crit_soilT(xPsi00) ! get the difference from the current state and the crossing point (K) critDiff = TcSoil - stateVecPrev(ixNrg) + cInc = xInc(ixNrg) ! initially frozen (T < TcSoil) - if(critDiff > 0._rkind)then - ! (check crossing above zero) - if(xInc(ixNrg) > critDiff)then - crosFlag(iLayer) = .true. - xInc(ixNrg) = critDiff + epsT ! set iteration increment to slightly above critical temperature - endif + if(critDiff > 0._rkind)then ! (check crossing above zero) + if(xInc(ixNrg) > critDiff) cInc = critDiff + epsT ! set iteration increment to slightly above critical temperature ! initially unfrozen (T > TcSoil) - else - ! (check crossing below zero) - if(xInc(ixNrg) < critDiff)then - crosFlag(iLayer) = .true. - xInc(ixNrg) = critDiff - epsT ! set iteration increment to slightly below critical temperature - endif + else ! (check crossing below zero) + if(xInc(ixNrg) < critDiff) cInc = critDiff - epsT ! set iteration increment to slightly below critical temperature endif ! (switch between initially frozen and initially unfrozen) + ! scale iterations + if(xInc(ixNrg).ne.0._rkind)then + xIncFactor = cInc/xInc(ixNrg) ! scaling factor for the iteration increment (-) + xInc = xIncFactor*xInc ! scale iteration increments + endif end do ! (loop through soil layers) endif ! (if there are both energy and liquid water state variables) @@ -1017,8 +1008,9 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st if(nSnowOnlyHyd>0)then ! loop through snow layers do iLayer=1,nSnow - ! check if the layer is included + ! check if the layer is included if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle + cInc = xInc(ixSnowOnlyHyd(iLayer)) if(ixSnowOnlyNrg(iLayer)/=integerMissing)then ! get the layer temperature (from stateVecPrev if ixSnowOnlyNrg(iLayer) is within the state vector scalarTemp = stateVecPrev( ixSnowOnlyNrg(iLayer) ) @@ -1034,9 +1026,14 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st scalarIce = mLayerVolFracIce(iLayer) ! checking if drain more than what is available or add more than possible if(-xInc(ixSnowOnlyHyd(iLayer)) > volFracLiq)then - xInc(ixSnowOnlyHyd(iLayer)) = -0.5_rkind*volFracLiq + cInc = -0.5_rkind*volFracLiq elseif(xInc(ixSnowOnlyHyd(iLayer)) > 1._rkind - scalarIce - volFracLiq)then - xInc(ixSnowOnlyHyd(iLayer)) = 0.5_rkind*(1._rkind - scalarIce - volFracLiq) + cInc = 0.5_rkind*(1._rkind - scalarIce - volFracLiq) + endif + ! scale iteration increment + if(xInc(ixSnowOnlyHyd(iLayer)).ne.0._rkind)then + xIncFactor = cInc/xInc(ixSnowOnlyHyd(iLayer)) ! scaling factor for the iteration increment (-) + xInc = xIncFactor*xInc ! new iteration increment endif end do ! (looping through snow layers) endif ! (if there are state variables for liquid water in the snow domain) @@ -1047,17 +1044,23 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st do iLayer=1,nSoil ! check if the layer is included if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle + cInc = xInc(ixSoilOnlyHyd(iLayer)) if(ixHydType(iLayer+nSnow)==iname_watLayer .or. ixHydType(iLayer+nSnow)==iname_liqLayer)then ! get the volumetric fraction of liquid water volFracLiq = stateVecPrev(ixSoilOnlyHyd(iLayer)) scalarIce = merge(0._rkind, mLayerVolFracIce(iLayer+nSnow), ixHydType(iLayer+nSnow)==iname_watLayer) ! checking if drain more than what is available or add more than possible if(-xInc(ixSoilOnlyHyd(iLayer)) > volFracLiq - theta_res(iLayer))then - xInc(ixSoilOnlyHyd(iLayer)) = -0.5_rkind*(volFracLiq - theta_res(iLayer)) + cInc = -0.5_rkind*(volFracLiq - theta_res(iLayer)) elseif(xInc(ixSoilOnlyHyd(iLayer)) > theta_sat(iLayer) - scalarIce - volFracLiq)then - xInc(ixSoilOnlyHyd(iLayer)) = -0.5_rkind*(theta_sat(iLayer) - scalarIce - volFracLiq) + cInc = -0.5_rkind*(theta_sat(iLayer) - scalarIce - volFracLiq) endif endif ! (if the state variable is not matric head) + ! scale iteration increment + if(xInc(ixSoilOnlyHyd(iLayer)).ne.0._rkind)then + xIncFactor = cInc/xInc(ixSoilOnlyHyd(iLayer)) ! scaling factor for the iteration increment (-) + xInc = xIncFactor*xInc ! new iteration increment + endif end do ! (looping through soil layers) endif ! (if there are state variables for liquid water in the soil domain) From 9358dab9c35be3340742a16dbb5a4e4ea69764bd Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 30 Jan 2024 19:24:27 -0600 Subject: [PATCH 1104/1472] Created additional class procedures to simplify exit criteria in opSplittin. --- build/source/engine/opSplittin.f90 | 34 +++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index e62033f14..0e98acb42 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -178,9 +178,9 @@ module opSplittin_module procedure :: advance_iStateSplit => split_select_advance_iStateSplit ! advance coupling loop iterator procedure :: logic_exit_stateTypeSplitting => split_select_logic_exit_stateTypeSplitting ! get logical for branch - !procedure :: logic_exit_stateThenDomain => split_select_logic_exit_stateThenDomain ! get logical for branch - !procedure :: logic_exit_domainSplit => split_select_logic_exit_domainSplit ! get logical for branch - !procedure :: logic_exit_solution => split_select_logic_exit_solution ! get logical for branch + procedure :: logic_exit_stateThenDomain => split_select_logic_exit_stateThenDomain ! get logical for branch + procedure :: logic_exit_domainSplit => split_select_logic_exit_domainSplit ! get logical for branch + procedure :: logic_exit_solution => split_select_logic_exit_solution ! get logical for branch procedure :: logic_initialize_stateTypeSplitting => split_select_logic_initialize_stateTypeSplitting ! get logical for branch procedure :: logic_initialize_stateThenDomain => split_select_logic_initialize_stateThenDomain ! get logical for branch @@ -368,11 +368,9 @@ subroutine opSplittin(& call split_select % initialize_flags ! initialize loop control flags split_select_loop: do iSplit=1,500 if (split_select % logic_initialize_stateTypeSplitting()) then - !if (split_select % stateTypeSplitting.eqv..false.) then ixCoupling=split_select % ixCoupling; if (ixCoupling.gt.nCoupling) exit split_select_loop ! exit if all splits are exhausted call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs call split_select % initialize_iStateTypeSplit; split_select % stateTypeSplitting=.true. - !end if end if if (split_select % logic_exit_stateTypeSplitting()) then iStateTypeSplit=split_select % iStateTypeSplit; if (iStateTypeSplit.gt.nStateTypeSplit) split_select % stateTypeSplitting=.false. @@ -1232,22 +1230,42 @@ logical(lgt) function split_select_logic_initialize_stateThenDomain(split_select ! *** Compute logical for branch in split_select loop *** class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector split_select_logic_initialize_stateThenDomain=& - &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.).and.(split_select % domainSplit.eqv..false.) + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.).and.(split_select % domainSplit.eqv..false.).and.(split_select % stateThenDomain.eqv..false.) end function split_select_logic_initialize_stateThenDomain +logical(lgt) function split_select_logic_exit_stateThenDomain(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_exit_stateThenDomain=& + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.).and.(split_select % domainSplit.eqv..false.).and.(split_select % stateThenDomain.eqv..true.) +end function split_select_logic_exit_stateThenDomain + logical(lgt) function split_select_logic_initialize_domainSplit(split_select) ! *** Compute logical for branch in split_select loop *** class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector split_select_logic_initialize_domainSplit=& - &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.) + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.).and.(split_select % domainSplit.eqv..false.) end function split_select_logic_initialize_domainSplit +logical(lgt) function split_select_logic_exit_domainSplit(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_exit_domainSplit=& + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.).and.(split_select % domainSplit.eqv..true.) +end function split_select_logic_exit_domainSplit + logical(lgt) function split_select_logic_initialize_solution(split_select) ! *** Compute logical for branch in split_select loop *** class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector - split_select_logic_initialize_solution=(split_select % stateSplit.eqv..false.) + split_select_logic_initialize_solution=(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.) end function split_select_logic_initialize_solution +logical(lgt) function split_select_logic_exit_solution(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_exit_solution=(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..true.) +end function split_select_logic_exit_solution + logical(lgt) function split_select_logic_finalize_stateSplit(split_select) ! *** Compute logical for branch in split_select loop *** class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector From c5a9f36a4f58526f9b4a16ac7ad09de10c805a65 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 31 Jan 2024 00:23:55 -0600 Subject: [PATCH 1105/1472] Implemented class procedures in split_select loop to simplify exit criteria in opSplittin. --- build/source/engine/opSplittin.f90 | 42 +++++++++++++----------------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 0e98acb42..c64651a1f 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -377,37 +377,31 @@ subroutine opSplittin(& end if if (split_select % stateTypeSplitting.eqv..true.) then if (split_select % logic_initialize_stateThenDomain()) then - if (split_select % stateThenDomain.eqv..false.) then ! first try the state type split, then try the domain split within a given state type call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.true. - end if - if (split_select % stateThenDomain.eqv..true.) then ! stateThenDomain loop - ixStateThenDomain=split_select % ixStateThenDomain - if (ixStateThenDomain > (1+tryDomainSplit)) then - ixStateThenDomain=ixStateThenDomain-1; split_select % ixStateThenDomain = ixStateThenDomain ! correct index needed after loop exit - split_select % stateThenDomain=.false. ! eqivalent to exiting the stateThenDomain loop - end if - end if - end if + end if + if (split_select % logic_exit_stateThenDomain()) then ! stateThenDomain loop + ixStateThenDomain=split_select % ixStateThenDomain + if (ixStateThenDomain > (1+tryDomainSplit)) then + ixStateThenDomain=ixStateThenDomain-1; split_select % ixStateThenDomain = ixStateThenDomain ! correct index needed after loop exit + split_select % stateThenDomain=.false. ! eqivalent to exiting the stateThenDomain loop + end if + end if if (split_select % stateThenDomain.eqv..true.) then if (split_select % logic_initialize_domainSplit()) then - if (split_select % domainSplit.eqv..false.) then - call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs - call split_select % initialize_iDomainSplit; split_select % domainSplit=.true. - end if - if (split_select % domainSplit.eqv..true.) then - iDomainSplit=split_select % iDomainSplit - if (split_select % iDomainSplit > nDomainSplit) split_select % domainSplit=.false. - end if + call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs + call split_select % initialize_iDomainSplit; split_select % domainSplit=.true. + end if + if (split_select % logic_exit_domainSplit()) then + iDomainSplit=split_select % iDomainSplit + if (split_select % iDomainSplit > nDomainSplit) split_select % domainSplit=.false. end if if (split_select % domainSplit.eqv..true.) then - if (split_select % logic_initialize_solution()) then - if (split_select % solution.eqv..false.) then; call split_select % initialize_ixSolution; split_select % solution=.true.; end if - if (split_select % solution.eqv..true.) then - ixSolution=split_select % ixSolution - if (split_select % ixSolution > nsolutions) split_select % solution=.false. - end if + if (split_select % logic_initialize_solution()) then; call split_select % initialize_ixSolution; split_select % solution=.true.; end if + if (split_select % logic_exit_solution()) then + ixSolution=split_select % ixSolution + if (split_select % ixSolution > nsolutions) split_select % solution=.false. end if if (split_select % solution.eqv..true.) then !logic_initialize_stateSplit= !!! do we need this? From 564eae1f5820438a967c0b01c68ecd56c337559d Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 31 Jan 2024 02:52:50 -0600 Subject: [PATCH 1106/1472] Implemented class procedures in split_select loop to simplify stateSplit branch in opSplittin. --- build/source/engine/opSplittin.f90 | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index c64651a1f..33e31b92a 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -181,11 +181,13 @@ module opSplittin_module procedure :: logic_exit_stateThenDomain => split_select_logic_exit_stateThenDomain ! get logical for branch procedure :: logic_exit_domainSplit => split_select_logic_exit_domainSplit ! get logical for branch procedure :: logic_exit_solution => split_select_logic_exit_solution ! get logical for branch + procedure :: logic_exit_stateSplit => split_select_logic_exit_stateSplit ! get logical for branch procedure :: logic_initialize_stateTypeSplitting => split_select_logic_initialize_stateTypeSplitting ! get logical for branch procedure :: logic_initialize_stateThenDomain => split_select_logic_initialize_stateThenDomain ! get logical for branch procedure :: logic_initialize_domainSplit => split_select_logic_initialize_domainSplit ! get logical for branch procedure :: logic_initialize_solution => split_select_logic_initialize_solution ! get logical for branch + procedure :: logic_initialize_stateSplit => split_select_logic_initialize_stateSplit ! get logical for branch procedure :: logic_finalize_stateTypeSplitting => split_select_logic_finalize_stateTypeSplitting ! get logical for branch procedure :: logic_finalize_stateThenDomain => split_select_logic_finalize_stateThenDomain ! get logical for branch @@ -404,12 +406,11 @@ subroutine opSplittin(& if (split_select % ixSolution > nsolutions) split_select % solution=.false. end if if (split_select % solution.eqv..true.) then - !logic_initialize_stateSplit= !!! do we need this? - if (split_select % stateSplit.eqv..false.) then + if (split_select % logic_initialize_stateSplit()) then call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs call split_select % initialize_iStateSplit; split_select % stateSplit=.true.; ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) end if - if (split_select % stateSplit.eqv..true.) then ! stateSplit begins + if (split_select % logic_exit_stateSplit()) then ! stateSplit begins iStateSplit=split_select % iStateSplit if (split_select % iStateSplit > nStateSplit) split_select % stateSplit=.false.; !exit stateSplit end if @@ -1260,6 +1261,18 @@ logical(lgt) function split_select_logic_exit_solution(split_select) split_select_logic_exit_solution=(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..true.) end function split_select_logic_exit_solution +logical(lgt) function split_select_logic_initialize_stateSplit(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_initialize_stateSplit=(split_select % stateSplit.eqv..false.) +end function split_select_logic_initialize_stateSplit + +logical(lgt) function split_select_logic_exit_stateSplit(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_exit_stateSplit=(split_select % stateSplit.eqv..true.) +end function split_select_logic_exit_stateSplit + logical(lgt) function split_select_logic_finalize_stateSplit(split_select) ! *** Compute logical for branch in split_select loop *** class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector From fddc38f1f655e9b53a2e8738622024b79828fb16 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 31 Jan 2024 04:10:29 -0600 Subject: [PATCH 1107/1472] Added error control for split_select loop and performed some additional cleanup in opSplittin. --- build/source/engine/opSplittin.f90 | 115 +++++++++++++---------------- 1 file changed, 51 insertions(+), 64 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 33e31b92a..8f7f5e623 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -349,9 +349,7 @@ subroutine opSplittin(& logical(lgt) :: exit_coupling,exit_stateTypeSplitting,exit_stateThenDomain,exit_domainSplit,exit_solution,exit_stateSplit logical(lgt) :: cycle_coupling,cycle_stateTypeSplitting,cycle_stateThenDomain,cycle_domainSplit,cycle_solution,cycle_stateSplit integer(i4b) :: iSplit,nSplit,itest - ! debug testing - logical(lgt), parameter :: test_flag=.false. ! to create test output - logical(lgt) :: stop_flag ! to control stopping + integer(i4b),parameter :: maxSplit=500 ! >= max number of splitting methods (controls upper limit of split_select loop) ! ------------------------ classes for subroutine arguments (classes defined in data_types module) ------------------------ ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** type(in_type_stateFilter) :: in_stateFilter; type(out_type_stateFilter) :: out_stateFilter; ! stateFilter arguments @@ -359,63 +357,59 @@ subroutine opSplittin(& type(in_type_varSubstep) :: in_varSubstep; type(io_type_varSubstep) :: io_varSubstep; type(out_type_varSubstep) :: out_varSubstep; ! varSubstep arguments ! ------------------------------------------------------------------------------------------------------------------------- type(split_select_type) :: split_select ! class object for selecting operator splitting methods - - ! initialize debug flags - stop_flag=.false. ! *** Initialize Split Selector Object *** call initialize_split_select call split_select % initialize_ixCoupling call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs call split_select % initialize_flags ! initialize loop control flags - split_select_loop: do iSplit=1,500 + split_select_loop: do iSplit=1,maxSplit if (split_select % logic_initialize_stateTypeSplitting()) then - ixCoupling=split_select % ixCoupling; if (ixCoupling.gt.nCoupling) exit split_select_loop ! exit if all splits are exhausted - call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs - call split_select % initialize_iStateTypeSplit; split_select % stateTypeSplitting=.true. + ixCoupling=split_select % ixCoupling; if (ixCoupling.gt.nCoupling) exit split_select_loop ! exit if all splits are exhausted + call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs + call split_select % initialize_iStateTypeSplit; split_select % stateTypeSplitting=.true. end if if (split_select % logic_exit_stateTypeSplitting()) then - iStateTypeSplit=split_select % iStateTypeSplit; if (iStateTypeSplit.gt.nStateTypeSplit) split_select % stateTypeSplitting=.false. + iStateTypeSplit=split_select % iStateTypeSplit; if (iStateTypeSplit.gt.nStateTypeSplit) split_select % stateTypeSplitting=.false. end if if (split_select % stateTypeSplitting.eqv..true.) then if (split_select % logic_initialize_stateThenDomain()) then - ! first try the state type split, then try the domain split within a given state type - call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split - call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.true. + ! first try the state type split, then try the domain split within a given state type + call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split + call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.true. end if if (split_select % logic_exit_stateThenDomain()) then ! stateThenDomain loop - ixStateThenDomain=split_select % ixStateThenDomain - if (ixStateThenDomain > (1+tryDomainSplit)) then - ixStateThenDomain=ixStateThenDomain-1; split_select % ixStateThenDomain = ixStateThenDomain ! correct index needed after loop exit - split_select % stateThenDomain=.false. ! eqivalent to exiting the stateThenDomain loop - end if + ixStateThenDomain=split_select % ixStateThenDomain + if (ixStateThenDomain > (1+tryDomainSplit)) then + ixStateThenDomain=ixStateThenDomain-1; split_select % ixStateThenDomain = ixStateThenDomain ! correct index needed after loop exit + split_select % stateThenDomain=.false. ! eqivalent to exiting the stateThenDomain loop + end if end if if (split_select % stateThenDomain.eqv..true.) then - if (split_select % logic_initialize_domainSplit()) then - call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs - call split_select % initialize_iDomainSplit; split_select % domainSplit=.true. - end if - if (split_select % logic_exit_domainSplit()) then + if (split_select % logic_initialize_domainSplit()) then + call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs + call split_select % initialize_iDomainSplit; split_select % domainSplit=.true. + end if + if (split_select % logic_exit_domainSplit()) then iDomainSplit=split_select % iDomainSplit if (split_select % iDomainSplit > nDomainSplit) split_select % domainSplit=.false. - end if - if (split_select % domainSplit.eqv..true.) then - if (split_select % logic_initialize_solution()) then; call split_select % initialize_ixSolution; split_select % solution=.true.; end if - if (split_select % logic_exit_solution()) then - ixSolution=split_select % ixSolution - if (split_select % ixSolution > nsolutions) split_select % solution=.false. end if - if (split_select % solution.eqv..true.) then - if (split_select % logic_initialize_stateSplit()) then - call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs - call split_select % initialize_iStateSplit; split_select % stateSplit=.true.; ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) - end if - if (split_select % logic_exit_stateSplit()) then ! stateSplit begins - iStateSplit=split_select % iStateSplit - if (split_select % iStateSplit > nStateSplit) split_select % stateSplit=.false.; !exit stateSplit - end if - if (split_select % stateSplit.eqv..true.) then - + if (split_select % domainSplit.eqv..true.) then + if (split_select % logic_initialize_solution()) then; call split_select % initialize_ixSolution; split_select % solution=.true.; end if + if (split_select % logic_exit_solution()) then + ixSolution=split_select % ixSolution + if (split_select % ixSolution > nsolutions) split_select % solution=.false. + end if + if (split_select % solution.eqv..true.) then + if (split_select % logic_initialize_stateSplit()) then + call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs + call split_select % initialize_iStateSplit; split_select % stateSplit=.true.; ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) + end if + if (split_select % logic_exit_stateSplit()) then ! stateSplit begins + iStateSplit=split_select % iStateSplit + if (split_select % iStateSplit > nStateSplit) split_select % stateSplit=.false.; !exit stateSplit + end if + if (split_select % stateSplit.eqv..true.) then ! define state subsets for a given split... call split_select % get_stateMask(indx_data,err,cmessage,message,return_flag) nSubset = split_select % nSubset; stateMask = split_select % stateMask @@ -450,23 +444,22 @@ subroutine opSplittin(& if (exit_solution) then; split_select % solution=.false.; split_select % stateSplit=.false.; end if if (split_select % solution.eqv..true.) then if (return_flag.eqv..true.) return ! return if error - call split_select % advance_iStateSplit end if end if - end if ! stateSplit ends - if (split_select % logic_finalize_stateSplit()) then - call split_select % advance_ixSolution - end if - end if ! end solution loop - if (split_select % logic_finalize_solution()) then - call finalize_solution ! final steps following solution loop - call split_select % advance_iDomainSplit - end if - end if ! domainSplit ends - if (split_select % logic_finalize_domainSplit()) then - call split_select % advance_ixStateThenDomain - end if + end if ! stateSplit ends + if (split_select % logic_finalize_stateSplit()) then + call split_select % advance_ixSolution + end if + end if ! solution ends + if (split_select % logic_finalize_solution()) then + call finalize_solution ! final steps following solution loop + call split_select % advance_iDomainSplit + end if + end if ! domainSplit ends + if (split_select % logic_finalize_domainSplit()) then + call split_select % advance_ixStateThenDomain + end if end if ! stateThenDomain ends if (split_select % logic_finalize_stateThenDomain()) then call finalize_stateThenDomain; if (return_flag.eqv..true.) return ! final steps following the stateThenDomain loop @@ -481,6 +474,9 @@ subroutine opSplittin(& call split_select % advance_ixCoupling end if end do split_select_loop + if (iSplit.gt.maxSplit) then ! check for errors + err=20; message=trim(message)//'split_select loop exceeded max number of iterations'; return_flag=.true.; return + end if call finalize_coupling ! check variables and fluxes, and apply step halving if needed contains @@ -494,15 +490,6 @@ subroutine initialize_split_select ! determine total number of possible splits return_flag=.false. ! initialize flag - call get_nCoupling; if (return_flag.eqv..true.) return ! get nCoupling value - call get_nStateTypeSplit_tryDomainSplit(nCoupling); if (return_flag.eqv..true.) return ! get nStateTypeSplit and tryDomainSplit values - call get_nDomainSplit(1+tryDomainSplit); if (return_flag.eqv..true.) return ! get nDomainSplit value -! add update to stateMask here -! call get_nStateSplit(nSolutions); if (return_flag.eqv..true.) return ! get nStateSplit value -! nSplit=nCoupling*nStateTypeSplit*(1+tryDomainSplit)*nDomainSplit*nSolutions*nStateSplit - nSplit=nCoupling ! temporary definition -- adding one opSplittin loop at a time - if (test_flag.eqv..true.) print *, "Test Ouput: ",nCoupling,nStateTypeSplit,tryDomainSplit,nDomainSplit - end subroutine initialize_split_select From da67c7f82162914c110adef23566e060bcd75221 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 1 Feb 2024 05:50:13 -0600 Subject: [PATCH 1108/1472] Modularized the initial steps of the split_select loop in opSplittin. --- build/source/engine/opSplittin.f90 | 138 ++++++++++++++++++----------- 1 file changed, 85 insertions(+), 53 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 8f7f5e623..10ee87afa 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -346,6 +346,7 @@ subroutine opSplittin(& real(rkind) :: mean_step_solution ! mean step for a solution (scalar or vector) logical(lgt) :: addFirstFlux ! flag to add the first flux to the mask ! loop control + logical(lgt) :: exit_split_select,cycle_split_select ! control for split_select loop logical(lgt) :: exit_coupling,exit_stateTypeSplitting,exit_stateThenDomain,exit_domainSplit,exit_solution,exit_stateSplit logical(lgt) :: cycle_coupling,cycle_stateTypeSplitting,cycle_stateThenDomain,cycle_domainSplit,cycle_solution,cycle_stateSplit integer(i4b) :: iSplit,nSplit,itest @@ -360,56 +361,18 @@ subroutine opSplittin(& ! *** Initialize Split Selector Object *** call initialize_split_select - call split_select % initialize_ixCoupling - call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs - call split_select % initialize_flags ! initialize loop control flags + call initialize_split_coupling; if (return_flag) return split_select_loop: do iSplit=1,maxSplit - if (split_select % logic_initialize_stateTypeSplitting()) then - ixCoupling=split_select % ixCoupling; if (ixCoupling.gt.nCoupling) exit split_select_loop ! exit if all splits are exhausted - call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs - call split_select % initialize_iStateTypeSplit; split_select % stateTypeSplitting=.true. - end if - if (split_select % logic_exit_stateTypeSplitting()) then - iStateTypeSplit=split_select % iStateTypeSplit; if (iStateTypeSplit.gt.nStateTypeSplit) split_select % stateTypeSplitting=.false. - end if - if (split_select % stateTypeSplitting.eqv..true.) then - if (split_select % logic_initialize_stateThenDomain()) then - ! first try the state type split, then try the domain split within a given state type - call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split - call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.true. - end if - if (split_select % logic_exit_stateThenDomain()) then ! stateThenDomain loop - ixStateThenDomain=split_select % ixStateThenDomain - if (ixStateThenDomain > (1+tryDomainSplit)) then - ixStateThenDomain=ixStateThenDomain-1; split_select % ixStateThenDomain = ixStateThenDomain ! correct index needed after loop exit - split_select % stateThenDomain=.false. ! eqivalent to exiting the stateThenDomain loop - end if - end if - if (split_select % stateThenDomain.eqv..true.) then - if (split_select % logic_initialize_domainSplit()) then - call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs - call split_select % initialize_iDomainSplit; split_select % domainSplit=.true. - end if - if (split_select % logic_exit_domainSplit()) then - iDomainSplit=split_select % iDomainSplit - if (split_select % iDomainSplit > nDomainSplit) split_select % domainSplit=.false. - end if - if (split_select % domainSplit.eqv..true.) then - if (split_select % logic_initialize_solution()) then; call split_select % initialize_ixSolution; split_select % solution=.true.; end if - if (split_select % logic_exit_solution()) then - ixSolution=split_select % ixSolution - if (split_select % ixSolution > nsolutions) split_select % solution=.false. - end if - if (split_select % solution.eqv..true.) then - if (split_select % logic_initialize_stateSplit()) then - call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs - call split_select % initialize_iStateSplit; split_select % stateSplit=.true.; ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) - end if - if (split_select % logic_exit_stateSplit()) then ! stateSplit begins - iStateSplit=split_select % iStateSplit - if (split_select % iStateSplit > nStateSplit) split_select % stateSplit=.false.; !exit stateSplit - end if - if (split_select % stateSplit.eqv..true.) then + call initialize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop; if (return_flag) return + if (split_select % stateTypeSplitting) then + call initialize_split_stateThenDomain + if (split_select % stateThenDomain) then + call initialize_split_domainSplit; if (return_flag) return + if (split_select % domainSplit) then + call initialize_split_solution + if (split_select % solution) then + call initialize_split_stateSplit; if (return_flag) return + if (split_select % stateSplit) then ! define state subsets for a given split... call split_select % get_stateMask(indx_data,err,cmessage,message,return_flag) nSubset = split_select % nSubset; stateMask = split_select % stateMask @@ -462,7 +425,7 @@ subroutine opSplittin(& end if end if ! stateThenDomain ends if (split_select % logic_finalize_stateThenDomain()) then - call finalize_stateThenDomain; if (return_flag.eqv..true.) return ! final steps following the stateThenDomain loop + call finalize_stateThenDomain; if (return_flag) return ! final steps following the stateThenDomain loop call split_select % advance_iStateTypeSplit end if end if ! stateTypeSplitting ends @@ -488,11 +451,80 @@ subroutine initialize_split_select ! allocate data components allocate(split_select % stateMask(1:nState)) ! allocate split_select components - ! determine total number of possible splits - return_flag=.false. ! initialize flag - + ! initialize flags + return_flag=.false. + exit_split_select=.false. + cycle_split_select=.false. + call split_select % initialize_flags ! initialize loop control flags end subroutine initialize_split_select + subroutine initialize_split_coupling + ! *** Initialize coupling split method *** + call split_select % initialize_ixCoupling + call initialize_coupling; if (return_flag) return ! select coupling options and allocate memory - return if error occurs + end subroutine initialize_split_coupling + + subroutine initialize_split_stateTypeSplitting + ! *** Initialize stateTypeSplitting split method *** + if (split_select % logic_initialize_stateTypeSplitting()) then + ixCoupling=split_select % ixCoupling + if (ixCoupling.gt.nCoupling) then; exit_split_select=.true.; return; end if ! exit if all splits are exhausted + call initialize_stateTypeSplitting; if (return_flag) return ! setup steps for stateTypeSplitting loop - return if error occurs + call split_select % initialize_iStateTypeSplit; split_select % stateTypeSplitting=.true. + end if + if (split_select % logic_exit_stateTypeSplitting()) then + iStateTypeSplit=split_select % iStateTypeSplit; if (iStateTypeSplit.gt.nStateTypeSplit) split_select % stateTypeSplitting=.false. + end if + end subroutine initialize_split_stateTypeSplitting + + subroutine initialize_split_stateThenDomain + ! *** Initialize stateThenDomain split method *** + if (split_select % logic_initialize_stateThenDomain()) then + ! first try the state type split, then try the domain split within a given state type + call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split + call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.true. + end if + if (split_select % logic_exit_stateThenDomain()) then ! stateThenDomain loop + ixStateThenDomain=split_select % ixStateThenDomain + if (ixStateThenDomain > (1+tryDomainSplit)) then + ixStateThenDomain=ixStateThenDomain-1; split_select % ixStateThenDomain = ixStateThenDomain ! correct index needed after loop exit + split_select % stateThenDomain=.false. ! eqivalent to exiting the stateThenDomain loop + end if + end if + end subroutine initialize_split_stateThenDomain + + subroutine initialize_split_domainSplit + ! *** Initialize domainSplit split method *** + if (split_select % logic_initialize_domainSplit()) then + call initialize_domainSplit; if (return_flag) return ! setup steps for domainSplit loop - return if error occurs + call split_select % initialize_iDomainSplit; split_select % domainSplit=.true. + end if + if (split_select % logic_exit_domainSplit()) then + iDomainSplit=split_select % iDomainSplit + if (split_select % iDomainSplit > nDomainSplit) split_select % domainSplit=.false. + end if + end subroutine initialize_split_domainSplit + + subroutine initialize_split_solution + ! *** Initialize solution split method *** + if (split_select % logic_initialize_solution()) then; call split_select % initialize_ixSolution; split_select % solution=.true.; end if + if (split_select % logic_exit_solution()) then + ixSolution=split_select % ixSolution + if (split_select % ixSolution > nsolutions) split_select % solution=.false. + end if + end subroutine initialize_split_solution + + subroutine initialize_split_stateSplit + ! *** Initialize stateSplit split method *** + if (split_select % logic_initialize_stateSplit()) then + call initialize_stateSplit; if (return_flag) return ! setup steps for stateSplit loop - return if error occurs + call split_select % initialize_iStateSplit; split_select % stateSplit=.true.; ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) + end if + if (split_select % logic_exit_stateSplit()) then ! stateSplit begins + iStateSplit=split_select % iStateSplit + if (split_select % iStateSplit > nStateSplit) split_select % stateSplit=.false.; !exit stateSplit + end if + end subroutine initialize_split_stateSplit subroutine initialize_coupling ! *** initial steps for coupling loop *** From 3e6f56b5cf6d16c12e6e2376e323c45dba242e8e Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 1 Feb 2024 18:01:39 -0600 Subject: [PATCH 1109/1472] Modularized the ending blocks of the split_select loop in opSplittin. --- build/source/engine/opSplittin.f90 | 84 ++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 27 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 10ee87afa..0cf1afab9 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -411,36 +411,17 @@ subroutine opSplittin(& end if end if end if ! stateSplit ends - if (split_select % logic_finalize_stateSplit()) then - call split_select % advance_ixSolution - end if + call finalize_split_stateSplit end if ! solution ends - if (split_select % logic_finalize_solution()) then - call finalize_solution ! final steps following solution loop - call split_select % advance_iDomainSplit - end if + call finalize_split_solution end if ! domainSplit ends - if (split_select % logic_finalize_domainSplit()) then - call split_select % advance_ixStateThenDomain - end if + call finalize_split_domainSplit end if ! stateThenDomain ends - if (split_select % logic_finalize_stateThenDomain()) then - call finalize_stateThenDomain; if (return_flag) return ! final steps following the stateThenDomain loop - call split_select % advance_iStateTypeSplit - end if + call finalize_split_stateThenDomain; if (return_flag) return end if ! stateTypeSplitting ends - if (split_select % logic_finalize_stateTypeSplitting()) then - call finalize_stateTypeSplitting - if (exit_coupling) then - call split_select % initialize_ixCoupling; exit split_select_loop ! success = exit the coupling loop - end if - call split_select % advance_ixCoupling - end if + call finalize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop end do split_select_loop - if (iSplit.gt.maxSplit) then ! check for errors - err=20; message=trim(message)//'split_select loop exceeded max number of iterations'; return_flag=.true.; return - end if - call finalize_coupling ! check variables and fluxes, and apply step halving if needed + call finalize_split_coupling; if (return_flag) return contains @@ -526,6 +507,55 @@ subroutine initialize_split_stateSplit end if end subroutine initialize_split_stateSplit + subroutine finalize_split_stateSplit + ! *** Finalize steps for stateSplit split method *** + if (split_select % logic_finalize_stateSplit()) then + call split_select % advance_ixSolution + end if + end subroutine finalize_split_stateSplit + + subroutine finalize_split_solution + ! *** Finalize steps for solution split method *** + if (split_select % logic_finalize_solution()) then + call finalize_solution ! final steps following solution loop + call split_select % advance_iDomainSplit + end if + end subroutine finalize_split_solution + + subroutine finalize_split_domainSplit + ! *** Finalize steps for domainSplit split method *** + if (split_select % logic_finalize_domainSplit()) then + call split_select % advance_ixStateThenDomain + end if + end subroutine finalize_split_domainSplit + + subroutine finalize_split_stateThenDomain + ! *** Finalize steps for stateThenDomain split method *** + if (split_select % logic_finalize_stateThenDomain()) then + call finalize_stateThenDomain; if (return_flag) return ! final steps following the stateThenDomain loop + call split_select % advance_iStateTypeSplit + end if + end subroutine finalize_split_stateThenDomain + + subroutine finalize_split_stateTypeSplitting + ! *** Finalize steps for stateTypeSplitting split method *** + if (split_select % logic_finalize_stateTypeSplitting()) then + call finalize_stateTypeSplitting + if (exit_coupling) then + call split_select % initialize_ixCoupling; exit_split_select=.true.; return ! success = exit the coupling loop + end if + call split_select % advance_ixCoupling + end if + end subroutine finalize_split_stateTypeSplitting + + subroutine finalize_split_coupling + ! *** Finalize steps for coupling split method *** + if (iSplit.gt.maxSplit) then ! check for errors + err=20; message=trim(message)//'split_select loop exceeded max number of iterations'; return_flag=.true.; return + end if + call finalize_coupling; if (return_flag) return ! check variables and fluxes, and apply step halving if needed + end subroutine finalize_split_coupling + subroutine initialize_coupling ! *** initial steps for coupling loop *** ! initialize error control @@ -632,7 +662,7 @@ subroutine finalize_coupling ! check that all state variables were updated if (any(stateCheck==0)) then message=trim(message)//'some state variables were not updated!' - err=20; return + err=20; return_flag=.true.; return endif ! check that the desired fluxes were computed @@ -640,7 +670,7 @@ subroutine finalize_coupling if (neededFlux(iVar) .and. any(fluxCount%var(iVar)%dat==0)) then print*, 'fluxCount%var(iVar)%dat = ', fluxCount%var(iVar)%dat message=trim(message)//'flux '//trim(flux_meta(iVar)%varname)//' was not computed' - err=20; return + err=20; return_flag=.true.; return end if end do From 4e4c0d65ba84836f02ca18f72051aa92509a213c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 2 Feb 2024 17:35:36 -0600 Subject: [PATCH 1110/1472] fixed the enthalpy in soil, works now --- build/source/engine/enthalpyTemp.f90 | 34 ++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index dd0c6d3e3..279da844c 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -501,13 +501,16 @@ subroutine t2enthalpy(& real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) real(rkind) :: volFracWat ! volumetric fraction of total water, liquid+ice (-) - real(rkind) :: diffT ! temperature difference from Tfreeze + real(rkind) :: diff0 ! temperature difference of Tcrit from Tfreeze + real(rkind) :: diffT ! temperature difference of temp soil from Tfreeze real(rkind) :: integral ! integral of snow freezing curve real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head real(rkind) :: dL ! derivative of enthalpy with temperature at layer temperature real(rkind) :: arg ! argument of hypergeometric function real(rkind) :: gauss_hg_T ! hypergeometric function result - real(rkind) :: integral_psiLiq ! integral of soil mLayerPsiLiq from Tfreeze to layer temperature + real(rkind) :: integral_unf ! integral of unfrozen soil water content (from Tfreeze to Tcrit) + real(rkind) :: integral_frz_low ! lower limit of integral of frozen soil water content (from Tfreeze to Tcrit) + real(rkind) :: integral_frz_upp ! upper limit of integral of frozen soil water content (from Tfreeze to soil temperature) real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) ! enthalpy @@ -630,27 +633,40 @@ subroutine t2enthalpy(& Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) volFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) diffT = mLayerTempTrial(iLayer) - Tfreeze + diff0 = Tcrit - Tfreeze ! *** compute enthalpy of water for unfrozen conditions if(mlayerTempTrial(iLayer)>=Tcrit)then enthWater = iden_water * Cp_water * volFracWat * diffT ! valid for temperatures below freezing also ! *** compute enthalpy of water for frozen conditions else + ! *** compute integral of mLayerPsiLiq from Tfreeze to layer temperature + ! get the unfrozen water content + integral_unf = ( Tcrit - Tfreeze ) * volFracWat + + ! get the frozen water content if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature - call splint(Tk,Ly,L2,mlayerTempTrial(iLayer),integral_psiLiq,dL,err,cmessage) + ! get the lower limit of the integral + call splint(Tk,Ly,L2,Tcrit,integral_frz_low,dL,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + ! get the upper limit of the integral + call splint(Tk,Ly,L2,mlayerTempTrial(iLayer),integral_frz_upp,dL,err,cmessage) if(err/=0) then; message=trim(message)//trim(cmessage); return; end if else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature - ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice - ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations + ! get the lower limit of the integral + arg = (vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n + gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + integral_frz_low = diff0 * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + ! get the upper limit of the integral xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) - mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution + mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution, DIFFERENT from the liquid water matric potential used in the flux calculations arg = (vGn_alpha * mLayerPsiLiq)**vGn_n gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - integral_psiLiq = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + integral_frz_upp = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) endif - enthLiq = iden_water * Cp_water * integral_psiLiq - enthIce = iden_ice * Cp_ice * diffT * volFracWat - iden_ice * Cp_ice * integral_psiLiq + enthLiq = iden_water * Cp_water * (integral_unf + integral_frz_upp - integral_frz_low) + enthIce = iden_ice * Cp_ice * ( volFracWat * diffT - (integral_unf + integral_frz_upp - integral_frz_low) ) enthWater = enthIce + enthLiq endif ! (if frozen conditions) From ecfe9336e435b782ed94808edc82d4945a3c7b20 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 3 Feb 2024 02:19:40 -0600 Subject: [PATCH 1111/1472] Simplified conditional statements in opSplittin_module. --- build/source/engine/opSplittin.f90 | 44 +++++++++++++++--------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 0cf1afab9..2d3804a5b 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -374,23 +374,23 @@ subroutine opSplittin(& call initialize_split_stateSplit; if (return_flag) return if (split_select % stateSplit) then ! define state subsets for a given split... + ! get the mask for the state subset - return for a non-zero error code call split_select % get_stateMask(indx_data,err,cmessage,message,return_flag) nSubset = split_select % nSubset; stateMask = split_select % stateMask - if (return_flag.eqv..true.) return - !call update_stateFilter; if (return_flag.eqv..true.) return ! get the mask for the state subset - return for a non-zero error code + if (return_flag) return call validate_split ! verify that the split is valid if (cycle_domainSplit) then; call split_select % advance_iDomainSplit; split_select % solution=.false.; split_select % stateSplit=.false.; cycle split_select_loop; end if if (cycle_solution) then; call split_select % advance_ixSolution; split_select % stateSplit=.false.; cycle split_select_loop; end if - if (return_flag.eqv..true.) return ! return for a non-zero error code + if (return_flag) return ! return for a non-zero error code call save_recover ! save/recover copies of variables and fluxes ! assemble vectors for a given split... - call get_split_indices; if (return_flag.eqv..true.) return ! get indices for a given split - return for a non-zero error code - call update_fluxMask; if (return_flag.eqv..true.) return ! define the mask of the fluxes used - return for a non-zero error code + call get_split_indices; if (return_flag) return ! get indices for a given split - return for a non-zero error code + call update_fluxMask; if (return_flag) return ! define the mask of the fluxes used - return for a non-zero error code - call solve_subset; if (return_flag.eqv..true.) return ! solve variable subset for one time step - return for a positive error code + call solve_subset; if (return_flag) return ! solve variable subset for one time step - return for a positive error code - call assess_solution; if (return_flag.eqv..true.) return ! is solution a success or failure? - return for a recovering solution + call assess_solution; if (return_flag) return ! is solution a success or failure? - return for a recovering solution call try_other_solution_methods ! if solution failed to converge, try other splitting methods if (cycle_coupling) then @@ -399,14 +399,14 @@ subroutine opSplittin(& if (cycle_stateThenDomain) then; call split_select % advance_ixStateThenDomain; split_select % domainSplit=.false.; split_select % solution=.false.; split_select % stateSplit=.false.; cycle split_select_loop; end if ! deactivate flags for inner loops if (cycle_solution) then; call split_select % advance_ixSolution; split_select % stateSplit=.false.; cycle split_select_loop; end if - call confirm_variable_updates; if (return_flag.eqv..true.) return ! check that state variables updated - return if error + call confirm_variable_updates; if (return_flag) return ! check that state variables updated - return if error call success_check ! check for success if (exit_stateThenDomain) then; call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.false.; split_select % domainSplit=.false.; split_select % solution=.false.; split_select % stateSplit=.false.; end if ! exit loops if necessary -- exit last available loop to avoid unnecessary operations -- deactivate inner loops - if (split_select % stateThenDomain.eqv..true.) then + if (split_select % stateThenDomain) then if (exit_solution) then; split_select % solution=.false.; split_select % stateSplit=.false.; end if - if (split_select % solution.eqv..true.) then - if (return_flag.eqv..true.) return ! return if error + if (split_select % solution) then + if (return_flag) return ! return if error call split_select % advance_iStateSplit end if end if @@ -561,7 +561,7 @@ subroutine initialize_coupling ! initialize error control err=0; message="opSplittin/" - call get_nCoupling; if (return_flag.eqv..true.) return ! get nCoupling value -- return if error + call get_nCoupling; if (return_flag) return ! get nCoupling value -- return if error ! set the global print flag globalPrintFlag=.false. @@ -587,7 +587,7 @@ subroutine initialize_coupling ! allocate local structures based on the number of snow and soil layers call allocate_memory - if (return_flag.eqv..true.) return ! return if an error occurs during memory allocation + if (return_flag) return ! return if an error occurs during memory allocation ! intialize the flux counter do iVar=1,size(flux_meta) ! loop through fluxes @@ -686,7 +686,7 @@ subroutine initialize_stateTypeSplitting dt_min = min(merge(dtmin_coupled, dtmin_split, ixCoupling==fullyCoupled), dt) ! minimum time step ! get nStateTypeSplit and tryDomainSplit values - call get_nStateTypeSplit_tryDomainSplit(ixCoupling); if (return_flag.eqv..true.) return + call get_nStateTypeSplit_tryDomainSplit(ixCoupling); if (return_flag) return mean_step_dt = 0._rkind ! initialize mean step for the time step addFirstFlux = .true. ! flag to add the first flux to the mask @@ -776,7 +776,7 @@ subroutine initialize_domainSplit if (iStateTypeSplit==massSplit .and. ixStateThenDomain==subDomain) numberDomainSplitMass = numberDomainSplitMass + 1 end associate - call get_nDomainSplit(ixStateThenDomain); if (return_flag.eqv..true.) return ! get nDomainSplit value -- return if error occurs + call get_nDomainSplit(ixStateThenDomain); if (return_flag) return ! get nDomainSplit value -- return if error occurs ! check that we haven't split the domain when we are fully coupled if (ixCoupling==fullyCoupled .and. nDomainSplit==nDomains) then @@ -825,7 +825,7 @@ subroutine initialize_stateSplit firstFluxCall=.true. if (.not.firstInnerStep) firstFluxCall=.false. - call get_nStateSplit(ixSolution); if (return_flag.eqv..true.) return ! get nStateSplit value -- return if error occurs + call get_nStateSplit(ixSolution); if (return_flag) return ! get nStateSplit value -- return if error occurs end subroutine initialize_stateSplit subroutine get_nStateSplit(ixSolution_value) @@ -1420,13 +1420,13 @@ subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) case(fullyCoupled); call fullyCoupled_stateMask ! get stateMask for fully coupled method ! *** splitting by state type *** case(stateTypeSplit) ! initial split by state type - call stateTypeSplit_stateMask; if (return_flag.eqv..true.) return ! get stateMask for state split method -- return if error + call stateTypeSplit_stateMask; if (return_flag) return ! get stateMask for state split method -- return if error ! check case default; err=20; message=trim(message)//'unable to identify coupling method'; return end select ! selecting solution method end associate - call identify_scalar_solutions; if (return_flag.eqv..true.) return ! identify scalar solutions -- return if error occurs + call identify_scalar_solutions; if (return_flag) return ! identify scalar solutions -- return if error occurs ! get the number of selected state variables associate(nSubset => out_stateFilter % nSubset) ! intent(out): number of selected state variables for a given split @@ -1450,9 +1450,9 @@ subroutine stateTypeSplit_stateMask message => out_stateFilter % cmessage ) ! intent(out): error message select case(ixStateThenDomain) ! split into energy and mass - case(fullDomain); call stateTypeSplit_fullDomain_stateMask; if (return_flag.eqv..true.) return + case(fullDomain); call stateTypeSplit_fullDomain_stateMask; if (return_flag) return ! split into vegetation, snow, and soil - case(subDomain); call stateTypeSplit_subDomain_stateMask; if (return_flag.eqv..true.) return + case(subDomain); call stateTypeSplit_subDomain_stateMask; if (return_flag) return ! check case default err=20; message=trim(message)//'unable to identify the switch between full domains and sub domains'; return_flag=.true.; return @@ -1499,9 +1499,9 @@ subroutine stateTypeSplit_subDomain_stateMask stateMask=.false. ! initialize state mask select case(iStateTypeSplit) ! define mask for energy - case(nrgSplit); call stateTypeSplit_subDomain_nrgSplit_stateMask; if (return_flag.eqv..true.) return + case(nrgSplit); call stateTypeSplit_subDomain_nrgSplit_stateMask; if (return_flag) return ! define mask for water - case(massSplit); call stateTypeSplit_subDomain_massSplit_stateMask; if (return_flag.eqv..true.) return + case(massSplit); call stateTypeSplit_subDomain_massSplit_stateMask; if (return_flag) return ! check case default; err=20; message=trim(message)//'unable to identify the state type'; return_flag=.true.; return From 06aea40dc1b89aac4bd483b2e96d007ac731cb27 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 3 Feb 2024 04:03:59 -0600 Subject: [PATCH 1112/1472] Implemented subroutines to modularize exit criteria checks in opSplittin. --- build/source/engine/opSplittin.f90 | 31 +++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 2d3804a5b..171cd9cc6 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -401,15 +401,9 @@ subroutine opSplittin(& call confirm_variable_updates; if (return_flag) return ! check that state variables updated - return if error - call success_check ! check for success - if (exit_stateThenDomain) then; call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.false.; split_select % domainSplit=.false.; split_select % solution=.false.; split_select % stateSplit=.false.; end if ! exit loops if necessary -- exit last available loop to avoid unnecessary operations -- deactivate inner loops - if (split_select % stateThenDomain) then - if (exit_solution) then; split_select % solution=.false.; split_select % stateSplit=.false.; end if - if (split_select % solution) then - if (return_flag) return ! return if error - call split_select % advance_iStateSplit - end if - end if + call success_check ! check for success + call check_exit_stateThenDomain ! check exit criterion for stateThenDomain split + call check_exit_solution; if (return_flag) return ! check exit criterion for solution split - return if error end if ! stateSplit ends call finalize_split_stateSplit end if ! solution ends @@ -507,6 +501,25 @@ subroutine initialize_split_stateSplit end if end subroutine initialize_split_stateSplit + subroutine check_exit_stateThenDomain + ! *** check exit criterion for stateThenDomain split *** + if (exit_stateThenDomain) then ! exit stateThenDomain split if necessary -- deactivate flags for inner splits + call split_select % initialize_ixStateThenDomain + split_select % stateThenDomain=.false.; split_select % domainSplit=.false.; split_select % solution=.false.; split_select % stateSplit=.false. + end if + end subroutine check_exit_stateThenDomain + + subroutine check_exit_solution + ! *** Check exit criterion for solution split - return if needed *** + if (split_select % stateThenDomain) then + if (exit_solution) then; split_select % solution=.false.; split_select % stateSplit=.false.; end if + if (split_select % solution) then + if (return_flag) return ! return if error + call split_select % advance_iStateSplit + end if + end if + end subroutine check_exit_solution + subroutine finalize_split_stateSplit ! *** Finalize steps for stateSplit split method *** if (split_select % logic_finalize_stateSplit()) then From 502b73d1228f1ab1fd24b016495fd0037f78e947 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 5 Feb 2024 18:39:32 -0600 Subject: [PATCH 1113/1472] hypergeometric works now --- build/source/engine/enthalpyTemp.f90 | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 279da844c..b68785eaa 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -645,18 +645,24 @@ subroutine t2enthalpy(& integral_unf = ( Tcrit - Tfreeze ) * volFracWat ! get the frozen water content + ! initialize for case Tcrit=Tfreeze, i.e. mLayerMatricHeadTrial(ixControlIndex)>0 + integral_frz_low = 0._rkind if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature ! get the lower limit of the integral - call splint(Tk,Ly,L2,Tcrit,integral_frz_low,dL,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + if(diff0<0._rkind)then + call splint(Tk,Ly,L2,Tcrit,integral_frz_low,dL,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + end if ! get the upper limit of the integral call splint(Tk,Ly,L2,mlayerTempTrial(iLayer),integral_frz_upp,dL,err,cmessage) if(err/=0) then; message=trim(message)//trim(cmessage); return; end if else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature ! get the lower limit of the integral - arg = (vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n - gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - integral_frz_low = diff0 * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + if(diff0<0._rkind)then + arg = (vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n + gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + integral_frz_low = diff0 * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + end if ! get the upper limit of the integral xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution, DIFFERENT from the liquid water matric potential used in the flux calculations From 412f5824be885f691c9c61a75f69e5fe8f3731a6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 5 Feb 2024 21:40:59 -0600 Subject: [PATCH 1114/1472] name of decision from howHeatCap to nrgConserv, and remove all enthalpyPrime stuff --- build/cmake/CMakeLists.txt | 1 - build/source/driver/summa_setup.f90 | 4 +- build/source/dshare/get_ixname.f90 | 2 +- build/source/dshare/type4ida.f90 | 3 - build/source/dshare/var_lookup.f90 | 2 +- build/source/engine/computResidWithPrime.f90 | 27 +- build/source/engine/enthalpyTempAddPrime.f90 | 307 ------------------ build/source/engine/eval8summa.f90 | 70 ++-- build/source/engine/eval8summaWithPrime.f90 | 72 +--- build/source/engine/mDecisions.f90 | 13 +- build/source/engine/summaSolve4ida.f90 | 118 +------ build/source/engine/systemSolv.f90 | 42 +-- build/source/engine/varSubstep.f90 | 6 +- .../celia1990/summa_zDecisions.txt | 6 +- 14 files changed, 98 insertions(+), 575 deletions(-) delete mode 100644 build/source/engine/enthalpyTempAddPrime.f90 diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index dc1a6556c..ea62cf1a0 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -352,7 +352,6 @@ set(MODRUN set(MODRUN_NOT_ACTORS ${ENGINE_DIR}/read_force.f90) set(MODRUN_SUNDIALS - ${ENGINE_DIR}/enthalpyTempAddPrime.f90 ${ENGINE_DIR}/tol4ida.f90 ${ENGINE_DIR}/updateVarsWithPrime.f90) diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index d75bc96cd..55cda9561 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -154,10 +154,10 @@ subroutine summa_paramSetup(summa1_struc, err, message) ! decide if computing enthalpy lookup tables, if need enthalpy and not using hypergeometric function ! NOTE: this should be replaced by a parameter of if want lookup table enthalpy, but for now it is hard-coded - ! need enthalpy if checkNrgBalance in varSubstep is turned on or using howHeatCap=enthalpyFD + ! need enthalpy if checkNrgBalance in varSubstep is turned on or using nrgConserv=enthalpyFD ! then, need lookups if use_lookup=.true. in t2enthalpy (numrec or kin) needLookup = .true. - !if (model_decisions(iLookDECISIONS%howHeatCap)%iDecision == enthalpyFD) needLookup = .true. + !if (model_decisions(iLookDECISIONS%nrgConserv)%iDecision == enthalpyFD) needLookup = .true. ! initialize the start of the initialization call date_and_time(values=startSetup) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 921e6d6ec..7e79faa80 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -95,7 +95,7 @@ function get_ixdecisions(varName) case('subRouting' ); get_ixdecisions=iLookDECISIONS%subRouting ! choice of method for sub-grid routing case('snowDenNew' ); get_ixdecisions=iLookDECISIONS%snowDenNew ! choice of method for new snow density case('snowUnload' ); get_ixdecisions=iLookDECISIONS%snowUnload ! choice of parameterization for snow unloading from canopy - case('howHeatCap' ); get_ixdecisions=iLookDECISIONS%howHeatCap ! how to compute heat capacity in energy equation + case('nrgConserv' ); get_ixdecisions=iLookDECISIONS%nrgConserv ! choice of variable in energy conservation backward Euler residual ! get to here if cannot find the variable case default get_ixdecisions = integerMissing diff --git a/build/source/dshare/type4ida.f90 b/build/source/dshare/type4ida.f90 index 5884353cd..383512e56 100644 --- a/build/source/dshare/type4ida.f90 +++ b/build/source/dshare/type4ida.f90 @@ -67,9 +67,6 @@ module type4ida real(rkind), allocatable :: mLayerMatricHeadLiqPrime(:) ! prime vector of liquid matric head of each snow and soil layer (m s-1) real(rkind), allocatable :: mLayerVolFracWatPrime(:) ! prime vector of volumetric total water content of each snow and soil layer (s-1) real(rkind), allocatable :: mLayerVolFracIcePrime(:) ! prime vector of volumetric fraction of ice (s-1) - real(rkind) :: scalarCanairEnthalpyPrime ! prime value for temperature component of enthalpy of the canopy air space (J m-3 s-1) - real(rkind) :: scalarCanopyEnthalpyPrime ! prime value for temperature component of enthalpy of the vegetation canopy (J m-3 s-1) - real(rkind), allocatable :: mLayerEnthalpyPrime(:) ! prime vector of temperature component of enthalpy for snow+soil layers (J m-3 s-1) end type data4ida diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 5b15e2fe3..a79cc716f 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -74,7 +74,7 @@ MODULE var_lookup integer(i4b) :: spatial_gw = integerMissing ! choice of method for spatial representation of groundwater integer(i4b) :: subRouting = integerMissing ! choice of method for sub-grid routing integer(i4b) :: snowDenNew = integerMissing ! choice of method for new snow density - integer(i4b) :: howHeatCap = integerMissing ! how to compute heat capacity in energy equation + integer(i4b) :: nrgConserv = integerMissing ! choice of variable in energy conservation backward Euler residual endtype iLook_decision ! *********************************************************************************************************** diff --git a/build/source/engine/computResidWithPrime.f90 b/build/source/engine/computResidWithPrime.f90 index c7c801bc0..a8a482ee8 100644 --- a/build/source/engine/computResidWithPrime.f90 +++ b/build/source/engine/computResidWithPrime.f90 @@ -62,7 +62,6 @@ subroutine computResidWithPrime(& nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers - useEnthalpy, & ! intent(in): flag to use enthalpy formulation ! input: flux vectors sMul, & ! intent(in): state vector multiplier (used in the residual calculations) fVec, & ! intent(in): flux vector @@ -81,9 +80,6 @@ subroutine computResidWithPrime(& ! input: enthalpy terms scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (J kg K-1) mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (J kg K-1) - scalarCanairEnthalpyPrime, & ! intent(in): prime value for the temperature component of the enthalpy of the canopy air space (J m-2 s-1) - scalarCanopyEnthalpyPrime, & ! intent(in): prime value for the temperature component of the enthalpy of the vegetation canopy (J m-2 s-1) - mLayerEnthalpyPrime, & ! intent(in): prime vector of the enthalpy of each snow and soil layer (J m-2 s-1) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -100,7 +96,6 @@ subroutine computResidWithPrime(& integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain - logical(lgt),intent(in) :: useEnthalpy ! flag to use enthalpy formulation ! input: flux vectors real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(rkind),intent(in) :: fVec(:) ! flux vector @@ -119,9 +114,6 @@ subroutine computResidWithPrime(& ! input: enthalpy terms real(qp),intent(in) :: scalarCanopyCmTrial ! Cm of vegetation canopy (-) real(qp),intent(in) :: mLayerCmTrial(:) ! Cm of each snow and soil layer (-) - real(rkind),intent(in) :: scalarCanairEnthalpyPrime ! prime value for the temperature component of the enthalpy of the canopy air space (J m-2 s-1) - real(rkind),intent(in) :: scalarCanopyEnthalpyPrime ! prime value for the temperature component of the enthalpy of the vegetation canopy (J m-2 s-1) - real(rkind),intent(in) :: mLayerEnthalpyPrime(:) ! prime vector of the enthalpy of each snow and soil layer (J m-2 s-1) ! input: data structures type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU @@ -209,14 +201,9 @@ subroutine computResidWithPrime(& ! compute the residual vector for the vegetation canopy ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision ! --> energy balance - if(useEnthalpy)then - if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = scalarCanairEnthalpyPrime - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) - if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = scalarCanopyEnthalpyPrime - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) - else - if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg) * scalarCanairTempPrime - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) - if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime + scalarCanopyCmTrial * scalarCanopyWatPrime/canopyDepth & - - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) - endif + if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg) * scalarCanairTempPrime - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime + scalarCanopyCmTrial * scalarCanopyWatPrime/canopyDepth & + - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) ! --> mass balance if(ixVegHyd/=integerMissing)then scalarCanopyHydPrime = merge(scalarCanopyWatPrime, scalarCanopyLiqPrime, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) @@ -226,12 +213,8 @@ subroutine computResidWithPrime(& ! compute the residual vector for the snow and soil sub-domains for energy if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - if(useEnthalpy)then - rVec( ixSnowSoilNrg(iLayer) ) = mLayerEnthalpyPrime(iLayer) - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) - else - rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) ) * mLayerTempPrime(iLayer) + mLayerCmTrial(iLayer) * mLayerVolFracWatPrime(iLayer) & - - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) - endif + rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) ) * mLayerTempPrime(iLayer) + mLayerCmTrial(iLayer) * mLayerVolFracWatPrime(iLayer) & + - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) end do ! looping through non-missing energy state variables in the snow+soil domain endif diff --git a/build/source/engine/enthalpyTempAddPrime.f90 b/build/source/engine/enthalpyTempAddPrime.f90 deleted file mode 100644 index c4d57e0e7..000000000 --- a/build/source/engine/enthalpyTempAddPrime.f90 +++ /dev/null @@ -1,307 +0,0 @@ -! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington -! -! This file is part of SUMMA -! -! For more information see: http://www.ral.ucar.edu/projects/summa -! -! This program is free software: you can redistribute it and/or modify -! it under the terms of the GNU General Public License as published by -! the Free Software Foundation, either version 3 of the License, or -! (at your option) any later version. -! -! This program is distributed in the hope that it will be useful, -! but WITHOUT ANY WARRANTY; without even the implied warranty of -! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -! GNU General Public License for more details. -! -! You should have received a copy of the GNU General Public License -! along with this program. If not, see . - -module enthalpyTempAddPrime_module - -! constants -USE multiconst, only: gravity, & ! gravitational acceleration (m s-1) - Tfreeze, & ! freezing point of water (K) - Cp_soil,Cp_water,Cp_ice,Cp_air,& ! specific heat of soil, water and ice (J kg-1 K-1) - iden_water,iden_ice,iden_air,& ! intrinsic density of water and ice (kg m-3) - LH_fus ! latent heat of fusion (J kg-1) - -! data types -USE nrtype -USE data_types,only:var_iLength ! var(:)%dat(:) -USE data_types,only:var_dLength ! var(:)%dat(:) - -! indices within parameter structure -USE var_lookup,only:iLookPARAM ! named variables to define structure element -USE var_lookup,only:iLookINDEX ! named variables to define structure element -USE var_lookup,only:iLookLOOKUP ! named variables to define structure element -USE var_lookup,only:iLookDIAG ! named variables for structure elements - -! domain types -USE globalData,only:iname_cas ! named variables for canopy air space -USE globalData,only:iname_veg ! named variables for vegetation canopy -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil -USE globalData,only:iname_aquifer ! named variables for the aquifer - -! named variables to describe the state variable type -USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space -USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy -USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers - -! missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing real number - -! privacy -implicit none -public::t2enthalpyPrime - -contains - - -! ************************************************************************************************************************ -! public subroutine t2enthalpyPrime: compute temperature component of enthalpy prime from temperature and total water content -! ************************************************************************************************************************ -subroutine t2enthalpyPrime(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - ! input: state variables for the vegetation canopy - scalarCanairTempPrime, & ! intent(in): prime value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - scalarCanopyTempPrime, & ! intent(in): prime value of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(in): prime value of canopy total water (kg m-2) - ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerTempPrime, & ! intent(in): prime vector of layer temperature (K) - mLayerVolFracWatPrime, & ! intent(in): prime vector of volumetric total water content (-) - mLayerMatricHeadPrime, & ! intent(in): prime vector of total water matric potential (m) - ! input: pre-computed derivatives - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - ! output: enthalpy prime - scalarCanairEnthalpyPrime, & ! intent(out): prime temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyPrime, & ! intent(out): prime temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyPrime, & ! intent(out): prime temperature component of enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,message) ! intent(out): error control - ! ------------------------------------------------------------------------------------------------------------------------- - ! downwind routines - USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water - USE spline_int_module,only:splint ! use for cubic spline interpolation - implicit none - ! delare dummy variables - ! ------------------------------------------------------------------------------------------------------------------------- - ! input: data structures - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model indices - ! input: state variables for the vegetation canopy - real(rkind),intent(in) :: scalarCanairTempPrime ! prime value of canopy air temperature (K) - real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - real(rkind),intent(in) :: scalarCanopyTempPrime ! prime value of canopy temperature (K) - real(rkind),intent(in) :: scalarCanopyWatPrime ! prime value of canopy total water (kg m-2) - ! input: variables for the snow-soil domain - real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind),intent(in) :: mLayerTempPrime(:) ! prime vector of layer temperature (K) - real(rkind),intent(in) :: mLayerVolFracWatPrime(:) ! prime vector of volumetric total water content (-) - real(rkind),intent(in) :: mLayerMatricHeadPrime(:) ! prime vector of total water matric potential (m) - ! input: pre-computed derivatives - real(rkind),intent(in) :: scalarFracLiqVeg ! fraction of canopy liquid water (-) - real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of snow liquid water (-) - real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) - ! output: enthalpy prime - real(rkind),intent(out) :: scalarCanairEnthalpyPrime ! prime temperature component of enthalpy of the canopy air space (J m-3) - real(rkind),intent(out) :: scalarCanopyEnthalpyPrime ! prime temperature component of enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! prime temperature component of enthalpy of each snow+soil layer (J m-3) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ------------------------------------------------------------------------------------------------------------------------- - ! declare local variables - character(len=128) :: cmessage ! error message in downwind routine - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixDomainType ! name of a given model domain - integer(i4b) :: ixControlIndex ! index within a given model domain - real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - real(rkind) :: volFracWat ! volumetric fraction of total water, liquid+ice (-) - real(rkind) :: diffT ! temperature difference from Tfreeze - real(rkind) :: integral ! integral of snow freezing curve - real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) in frozen soil - real(rkind) :: xConst ! constant in the freezing curve function (m K-1) - real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) - ! enthalpy - real(rkind) :: enthVegP ! prime enthalpy of the vegetation (J m-3) - real(rkind) :: enthSoilP ! prime enthalpy of soil particles (J m-3) - real(rkind) :: enthLiqP ! prime enthalpy of the liquid region (J m-3) - real(rkind) :: enthIceP ! prime enthalpy of the ice region (J m-3) - real(rkind) :: enthAirP ! prime enthalpy of air (J m-3) - real(rkind) :: enthPhaseP ! prime enthalpy associated with phase change (J m-3) - real(rkind) :: enthWaterP ! prime enthalpy of total water (J m-3) - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - generalVars: associate(& - ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers - ! mapping between the full state vector and the state subset - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset - ! type of domain, type of state variable, and index of control volume within domain - ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat & ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ) ! end associate statement - ! -------------------------------------------------------------------------------------------------------------------------------- - - ! initialize error control - err=0; message="t2enthalpyPrime/" - - ! loop through model state variables - do iState=1,size(ixMapSubset2Full) - - ! ----- - ! - compute indices... - ! -------------------- - - ! get domain type, and index of the control volume within the domain - ixFullVector = ixMapSubset2Full(iState) ! index within full state vector - ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) - ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain - - ! check an energy state - if(ixStateType(ixFullVector)==iname_nrgCanair .or. ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer)then - - ! get the layer index - select case(ixDomainType) - case(iname_cas); iLayer = integerMissing - case(iname_veg); iLayer = integerMissing - case(iname_snow); iLayer = ixControlIndex - case(iname_soil); iLayer = ixControlIndex + nSnow - case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) - case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return - end select - - ! identify domain - select case(ixDomainType) - case(iname_cas) - scalarCanairEnthalpyPrime = Cp_air * iden_air * scalarCanairTempPrime - - case(iname_veg) - ! association to necessary variables for vegetation - vegVars: associate(& - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! canopy depth (m) - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! maximum mass of vegetation (kg m-2) - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! scaling parameter for the snow freezing curve (K-1) - ) - - diffT = scalarCanopyTempTrial - Tfreeze - enthVegP = specificHeatVeg * maxMassVegetation * scalarCanopyTempPrime / canopyDepth - - if(diffT>=0._rkind)then - enthLiqP = Cp_water * ( scalarCanopyWatPrime * diffT + scalarCanopyWatTrial * scalarCanopyTempPrime )/ canopyDepth - enthIceP = 0._rkind - else - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) ! Note: scalarFracLiqVeg = d_integral_dTk*scalarCanopyWatTrial - enthLiqP = Cp_water * ( scalarCanopyWatPrime*integral + scalarCanopyTempPrime * scalarFracLiqVeg)/ canopyDepth - enthIceP = Cp_ice * ( scalarCanopyWatPrime*( diffT - integral ) + scalarCanopyTempPrime*( scalarCanopyWatTrial - scalarFracLiqVeg ) ) / canopyDepth - endif - - scalarCanopyEnthalpyPrime = enthVegP + enthLiqP + enthIceP - - end associate vegVars - - case(iname_snow) - - ! association to necessary variables for snow - snowVars: associate(& - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! scaling parameter for the snow freezing curve (K-1) - ) - - diffT = mLayerTempTrial(iLayer) - Tfreeze ! diffT<0._rkind because snow is frozen - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) ! Note: mLayerFracLiqSnow = d_integral_dTk*mLayerVolFracWatTrial - enthLiqP = iden_water * Cp_water * ( mLayerVolFracWatPrime(iLayer)*integral + mLayerTempPrime(iLayer)*mLayerFracLiqSnow(iLayer) ) - enthIceP = iden_water * Cp_ice * ( mLayerVolFracWatPrime(iLayer)*( diffT - integral ) & - + mLayerTempPrime(iLayer)*( mLayerVolFracWatTrial(iLayer) - mLayerFracLiqSnow(iLayer) ) ) - enthAirP = iden_air * Cp_air * ( mLayerTempPrime(iLayer) - mLayerVolFracWatPrime(iLayer) * ( (iden_water/iden_ice)*( diffT - integral ) + integral ) & - - mLayerTempPrime(iLayer)*( (iden_water/iden_ice)*( mLayerVolFracWatTrial(iLayer) - mLayerFracLiqSnow(iLayer) ) & - + mLayerFracLiqSnow(iLayer) ) ) - - mLayerEnthalpyPrime(iLayer) = enthLiqP + enthIceP + enthAirP - - end associate snowVars - - case(iname_soil) - ! make association to variables for soil - soilVars: associate(& - soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex) , & ! intrinsic soil density (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex) , & ! soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex) , & ! volumetric residual water content (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) & ! van Genuchten "n" parameter (-) - ) ! end associate statement - - ! diagnostic variables - vGn_m = 1._rkind - 1._rkind/vGn_n - Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) - volFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - diffT = mLayerTempTrial(iLayer) - Tfreeze - ! *** compute enthalpy prime of water for unfrozen conditions - if(mlayerTempTrial(iLayer)>=Tcrit)then - enthWaterP = iden_water * Cp_water * ( mLayerMatricHeadPrime(ixControlIndex)*dVolTot_dPsi0(ixControlIndex)*diffT & - + volFracWat*mLayerTempPrime(iLayer) ) ! valid for temperatures below freezing also - - ! *** compute enthalpy prime of water for frozen conditions - else - ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice - ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations - xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) - mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution - ! NOTE: integral_psiLiq = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) and d_integral_psiLiq_dTk = vFracLiq - vFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - enthLiqP = iden_water * Cp_water * mLayerTempPrime(iLayer)*vFracLiq - enthIceP = iden_ice * Cp_ice * ( mLayerMatricHeadPrime(ixControlIndex)*dVolTot_dPsi0(ixControlIndex)*diffT & - + volFracWat*mLayerTempPrime(iLayer) ) - iden_ice * Cp_ice * mLayerTempPrime(iLayer) * vFracLiq - enthWaterP = enthIceP + enthLiqP - - endif ! (if frozen conditions) - - enthSoilP = soil_dens_intr * Cp_soil * (1._rkind - theta_sat)*mlayerTempPrime(iLayer) - enthAirP = iden_air * Cp_air * ( mLayerMatricHeadPrime(ixControlIndex)*dVolTot_dPsi0(ixControlIndex)*diffT & - + (1._rkind - theta_sat - volFracWat)*mlayerTempPrime(iLayer) ) - - mLayerEnthalpyPrime(iLayer) = enthWaterP + enthSoilP + enthAirP - - end associate soilVars - - ! ----- - ! - checks... - ! ----------- - case(iname_aquifer); cycle ! aquifer: do nothing - case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return - end select - - end if ! if an energy layer - end do ! looping through state variables - - end associate generalVars - -end subroutine t2enthalpyPrime - -end module enthalpyTempAddPrime_module diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index a6175d1be..1cb136ed4 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -185,34 +185,36 @@ subroutine eval8summa(& ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- ! state variables - real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial value for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial value for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial value for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) - real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial value for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial value for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial value for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) + real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) ! diagnostic variables - real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) + real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) ! enthalpy - real(rkind) :: scalarCanairEnthalpyTrial ! trial value for temperature component of enthalpy of the canopy air space (J m-3 - real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for temperature component of enthalpy of the vegetation canopy (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector of temperature component of enthalpy for snow+soil layers (J m-3) + real(rkind) :: scalarCanairEnthalpyTrial ! trial value for temperature component of enthalpy of the canopy air space (J m-3 + real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector of temperature component of enthalpy for snow+soil layers (J m-3) ! other local variables - logical(lgt) :: checkLWBalance ! flag to check longwave balance - integer(i4b) :: iLayer ! index of model layer in the snow+soil domain - integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain - integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine - real(rkind),dimension(nState) :: rVecScaled ! scaled residual vector - character(LEN=256) :: cmessage ! error message of downwind routine - real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy - real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil - logical(lgt) :: updateCp ! flag to indicate if we update Cp at each step - logical(lgt) :: needCm ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m + logical(lgt) :: checkLWBalance ! flag to check longwave balance + integer(i4b) :: iLayer ! index of model layer in the snow+soil domain + integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain + integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine + real(rkind),dimension(nState) :: rVecScaled ! scaled residual vector + character(LEN=256) :: cmessage ! error message of downwind routine + real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy + real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil + logical(lgt) :: updateCp ! flag to indicate if we update Cp at each step, set with nrgConserv choice and updateCp_closedForm flag + logical(lgt) :: needCm ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m,, set with nrgConserv choice and needCm_closedForm flag + logical(lgt),parameter :: updateCp_closedForm=.true. ! nrgConserv = closedForm flag to indicate if we update Cp at each step + logical(lgt),parameter :: needCm_closedForm=.true. ! nrgConserv = closedForm flag to indicate if the energy equation contains Cm = dH_T/dTheta_m ! -------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures @@ -220,7 +222,7 @@ subroutine eval8summa(& associate(& ! model decisions ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver - ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy + ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& ! intent(in): [i4b] choice of variable in energy conservation backward Euler residual ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation ! snow parameters snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) @@ -308,14 +310,14 @@ subroutine eval8summa(& end if end if ! ( feasibility check ) - if(ixHowHeatCap == enthalpyFD)then - updateCp = .true. - needCm = .true. - else if(ixHowHeatCap == closedForm)then ! have a choice, should update if checkNrgBalance in varSubstep is turned on + if(ixNrgConserv == enthalpyFD)then updateCp = .true. needCm = .true. + else if(ixNrgConserv == closedForm)then ! have a choice, should update if checkNrgBalance in varSubstep is turned on + updateCp = updateCp_closedForm + needCm = needCm_closedForm else - message=trim(message)//'unknown heat capacity computation' + message=trim(message)//'unknown choice of variable in energy conservation backward Euler residual' err=1; return end if @@ -507,7 +509,7 @@ subroutine eval8summa(& dCm_dTkCanopy = 0._rkind endif ! needCm - if(ixHowHeatCap == enthalpyFD)then ! use residual as enthalpy_delta - (phase change)_delta + if(ixNrgConserv == enthalpyFD)then ! use residual as enthalpy_delta - (phase change)_delta ! compute temperature component of enthalpy call t2enthalpy(& ! input: data structures @@ -531,7 +533,7 @@ subroutine eval8summa(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - else if(ixHowHeatCap == closedForm)then ! use residual as (Cp*DeltaTemp + Cm*DeltaTheta)_delta - (phase change)_delta + else if(ixNrgConserv == closedForm)then ! use residual as (Cp*DeltaTemp + Cm*DeltaTheta)_delta - (phase change)_delta scalarCanairEnthalpyTrial = realMissing scalarCanopyEnthalpyTrial = realMissing mLayerEnthalpyTrial = realMissing @@ -620,7 +622,7 @@ subroutine eval8summa(& nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers - ixHowHeatCap==enthalpyFD, & ! intent(in): flag to use enthalpy form of residual + ixNrgConserv==enthalpyFD, & ! intent(in): flag to use enthalpy form of residual ! input: flux vectors sMul, & ! intent(in): state vector multiplier (used in the residual calculations) fluxVec, & ! intent(in): flux vector diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 008025e40..5f18c7cbf 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -98,10 +98,6 @@ subroutine eval8summaWithPrime(& mLayerMatricHeadLiqPrime, & ! intent(out): prime vector of liquid water matric potential (m s-1) mLayerVolFracWatPrime, & ! intent(out): prime vector of volumetric total water content of each snow and soil layer (s-1) mLayerVolFracIcePrime, & ! intent(out): prime vector of volumetric fraction of ice (s-1) - ! output: enthalpy prime values - scalarCanairEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the canopy air space (J m-3 s-1) - scalarCanopyEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the vegetation canopy (J m-3 s-1) - mLayerEnthalpyPrime, & ! intent(out): prime vector of temperature component of enthalpy for snow+soil layers (J m-3 s-1) ! input-output: baseflow ixSaturation, & ! intent(inout): index of the lowest saturated layer dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) @@ -116,7 +112,6 @@ subroutine eval8summaWithPrime(& USE getVectorz_module, only:varExtract ! extract variables from the state vector USE getVectorz_module, only:checkFeas ! check feasibility of state vector USE updateVarsWithPrime_module, only:updateVarsWithPrime ! update variables - USE enthalpyTempAddPrime_module, only:t2enthalpyPrime ! compute enthalpy prime USE computFlux_module, only:soilCmpresPrime ! compute soil compression USE computFlux_module, only:computFlux ! compute fluxes given a state vector USE computHeatCap_module,only:computHeatCapAnalytic ! recompute closed form heat capacity (Cp) and derivatives @@ -176,10 +171,6 @@ subroutine eval8summaWithPrime(& real(rkind),intent(out) :: mLayerMatricHeadLiqPrime(:) ! prime vector of liquid water matric potential (m s-1) real(rkind),intent(out) :: mLayerVolFracWatPrime(:) ! prime vector of volumetric total water content of each snow and soil layer (s-1) real(rkind),intent(out) :: mLayerVolFracIcePrime(:) ! prime vector of volumetric fraction of ice (s-1) - ! output: enthalpy prime values - real(rkind),intent(out) :: scalarCanairEnthalpyPrime ! prime value for temperature component of enthalpy of the canopy air space (J m-3 s-1) - real(rkind),intent(out) :: scalarCanopyEnthalpyPrime ! prime value for temperature component of enthalpy of the vegetation canopy (J m-3 s-1) - real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! prime vector of temperature component of enthalpy for snow+soil layers (J m-3 s-1) ! input-output: baseflow integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) @@ -211,15 +202,17 @@ subroutine eval8summaWithPrime(& integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine character(LEN=256) :: cmessage ! error message of downwind routine - logical(lgt) :: updateCp ! flag to indicate if we update Cp at each step - logical(lgt) :: needCm ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m + logical(lgt) :: updateCp ! flag to indicate if we update Cp at each step, set with nrgConserv choice and updateCp_closedForm flag + logical(lgt) :: needCm ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m,, set with nrgConserv choice and needCm_closedForm flag + logical(lgt),parameter :: updateCp_closedForm=.true. ! nrgConserv = closedForm flag to indicate if we update Cp at each step + logical(lgt),parameter :: needCm_closedForm=.true. ! nrgConserv = closedForm flag to indicate if the energy equation contains Cm = dH_T/dTheta_m ! -------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- associate(& ! model decisions - ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy + ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& ! intent(in): [i4b] choice of variable in energy conservation backward Euler residual ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation ! snow parameters snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) @@ -289,14 +282,14 @@ subroutine eval8summaWithPrime(& end if end if ! ( feasibility check ) - if(ixHowHeatCap == enthalpyFD)then - updateCp = .true. - needCm = .true. - else if(ixHowHeatCap == closedForm)then ! have a choice, should update if checkNrgBalance in varSubstep is turned on + if(ixNrgConserv == enthalpyFD)then updateCp = .true. needCm = .true. + else if(ixNrgConserv == closedForm)then ! have a choice, should update if checkNrgBalance in varSubstep is turned on + updateCp = updateCp_closedForm + needCm = needCm_closedForm else - message=trim(message)//'unknown heat capacity computation' + message=trim(message)//'unknown choice of variable in energy conservation backward Euler residual' err=1; return end if @@ -537,43 +530,6 @@ subroutine eval8summaWithPrime(& dCm_dTkCanopy = 0._rkind endif ! needCm - if(ixHowHeatCap == enthalpyFD)then ! use residual as enthalpy_prime - (phase change)_prime - ! compute temperature component of enthalpy prime - call t2enthalpyPrime(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - ! input: state variables for the vegetation canopy - scalarCanairTempPrime, & ! intent(in): prime value for canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) - scalarCanopyTempPrime, & ! intent(in): prime value for canopy temperature (K) - scalarCanopyWatPrime, & ! intent(in): prime value for canopy total water (kg m-2) - ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerTempPrime, & ! intent(in): prime vector of layer temperature (K) - mLayerVolFracWatPrime, & ! intent(in): prime vector of volumetric total water content (-) - mLayerMatricHeadPrime, & ! intent(in): prime vector of total water matric potential (m) - ! input: pre-computed derivatives - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - ! output: enthalpy prime - scalarCanairEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyPrime, & ! intent(out): prime vector of temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyPrime, & ! intent(out): prime vector of temperature component of enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - else if(ixHowHeatCap == closedForm)then ! use residual as (Cp*DeltaTemp + Cm*DeltaTheta)_delta - (phase change)_delta - scalarCanairEnthalpyPrime = realMissing - scalarCanopyEnthalpyPrime = realMissing - mLayerEnthalpyPrime = realMissing - endif !(choice of how compute heat capacity) - ! save the number of flux calls per time step indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) + 1 @@ -655,7 +611,6 @@ subroutine eval8summaWithPrime(& nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers - ixHowHeatCap==enthalpyFD, & ! intent(in): flag to use enthalpy form of residual ! input: flux vectors sMul, & ! intent(in): state vector multiplier (used in the residual calculations) fluxVec, & ! intent(in): flux vector @@ -674,9 +629,6 @@ subroutine eval8summaWithPrime(& ! input: enthalpy terms canopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) - scalarCanairEnthalpyPrime, & ! intent(in): prime value for the temperature component of the enthalpy of the canopy air space (J m-2 s-1) - scalarCanopyEnthalpyPrime, & ! intent(in): prime value for the temperature component of the enthalpy of the vegetation canopy (J m-2 s-1) - mLayerEnthalpyPrime, & ! intent(in): prime vector of the temperature component of the enthalpy of each snow and soil layer (J m-2 s-1) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -792,10 +744,6 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%mLayerMatricHeadLiqPrime, & ! intent(out): prime vector of liquid water matric potential (m s-1) eqns_data%mLayerVolFracWatPrime, & ! intent(out): prime vector of volumetric total water content of each snow and soil layer (s-1) eqns_data%mLayerVolFracIcePrime, & ! intent(out): prime vector of volumetric fraction of ice (s-1) - ! output: enthalpy prime values - eqns_data%scalarCanairEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the canopy air space (J m-3 s-1) - eqns_data%scalarCanopyEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the vegetation canopy (J m-3 s-1) - eqns_data%mLayerEnthalpyPrime, & ! intent(out): prime vector of temperature component of enthalpy for snow+soil layers (J m-3 s-1) ! input-output: baseflow eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index 4dc787a35..b97812611 100644 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -411,15 +411,16 @@ subroutine mDecisions(err,message) endif #endif - ! how to compute heat capacity in energy equation, choice enthalpyFD has better coincidence of energy conservation with IDA tolerance. - select case(trim(model_decisions(iLookDECISIONS%howHeatCap)%cDecision)) - case('enthalpyFD'); model_decisions(iLookDECISIONS%howHeatCap)%iDecision = enthalpyFD ! heat capacity using enthalpy - case('closedForm'); model_decisions(iLookDECISIONS%howHeatCap)%iDecision = closedForm ! heat capacity using closed form, not using enthalpy + ! choice of variable in energy conservation backward Euler residual, choice enthalpyFD has changes the residual computation and has better coincidence of energy conservation for backward Euler solution + ! NOTE: choice does not change the residual for the IDA solution, as they are equivalent + select case(trim(model_decisions(iLookDECISIONS%nrgConserv)%cDecision)) + case('enthalpyFD'); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = enthalpyFD ! enthalpy finite difference in backward Euler residual + case('closedForm'); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = closedForm ! heat capacity closed form in backward Euler residual case default if (trim(model_decisions(iLookDECISIONS%num_method)%cDecision)=='itertive')then - model_decisions(iLookDECISIONS%howHeatCap)%iDecision = closedForm ! included for backwards compatibility + model_decisions(iLookDECISIONS%nrgConserv)%iDecision = closedForm ! included for backwards compatibility else - err=10; message=trim(message)//"unknown Cp computation [option="//trim(model_decisions(iLookDECISIONS%howHeatCap)%cDecision)//"]"; return + err=10; message=trim(message)//"unknown choice of variable in energy conservation backward Euler residual [option="//trim(model_decisions(iLookDECISIONS%nrgConserv)%cDecision)//"]"; return endif end select diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 2f521dedb..01ad40754 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -62,10 +62,6 @@ module summaSolve4ida_module var_dlength, & ! data vector with variable length dimension (rkind) model_options ! defines the model decisions -USE mDecisions_module,only:& - closedForm, & ! heat capacity using closed form, not using enthalpy - enthalpyFD ! heat capacity using enthalpy - ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: & qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization @@ -156,9 +152,6 @@ subroutine summaSolve4ida( & USE computJacobWithPrime_module,only:computJacob4ida ! system Jacobian USE tol4ida_module,only:computWeight4ida ! weight required for tolerances USE var_lookup,only:maxvarDecisions ! maximum number of decisions - USE enthalpyTempAddPrime_module,only:t2enthalpyPrime ! compute enthalpy - USE enthalpyTemp_module,only:enthalpy2DeltaH ! add phase change terms to delta temperature component of enthalpy - !======= Declarations ========= implicit none @@ -241,12 +234,6 @@ subroutine summaSolve4ida( & type(var_dlength) :: flux_prev ! previous model fluxes for a local HRU character(LEN=256) :: cmessage ! error message of downwind routine real(rkind),allocatable :: mLayerMatricHeadPrimePrev(:) ! previous derivative value for total water matric potential (m s-1) - real(rkind) :: scalarCanairEnthalpyPrimePrev ! previous derivative value for canopy air temperature component of enthalpy (J m-3) - real(rkind) :: scalarCanopyHmixPrimePrev ! previous derivative value for canopy mixture enthalpy (J m-3) - real(rkind),allocatable :: mLayerHmixPrime(:) ! derivative value for total water mixture enthalpy (J m-3) - real(rkind) :: scalarCanopyHmixPrime ! derivative value for canopy mixture enthalpy (J m-3) - real(rkind),allocatable :: mLayerHmixPrimePrev(:) ! previous derivative value for total water enthalpy (J m-3) - real(rkind),allocatable :: fluxVecPrev(:) ! previous value for fluxes real(rkind),allocatable :: resVecPrev(:) ! previous value for residuals real(rkind),allocatable :: dCompress_dPsiPrev(:) ! previous derivative value soil compression ! flags @@ -335,31 +322,17 @@ subroutine summaSolve4ida( & allocate( eqns_data%mLayerMatricHeadPrime(nSoil) ) allocate( eqns_data%mLayerVolFracWatPrime(nLayers) ) allocate( eqns_data%mLayerVolFracIcePrime(nLayers) ) - allocate( eqns_data%mLayerEnthalpyPrime(nLayers) ) allocate( mLayerMatricHeadPrimePrev(nSoil) ) - allocate( mLayerHmixPrime(nLayers) ) - allocate( mLayerHmixPrimePrev(nLayers) ) allocate( dCompress_dPsiPrev(nSoil) ) - allocate( eqns_data%fluxVec(nState) ) allocate( eqns_data%resVec(nState) ) allocate( eqns_data%resSink(nState) ) - allocate( fluxVecPrev(nState) ) allocate( resVecPrev(nState) ) - - !initialize - scalarCanopyHmixPrime = 0._rkind - mLayerHmixPrime(:) = 0._rkind ! need the following values for the first substep eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) - mLayerMatricHeadPrimePrev(:) = 0._rkind - scalarCanairEnthalpyPrimePrev = 0._rkind - scalarCanopyHmixPrimePrev = 0._rkind - mLayerHmixPrimePrev(:) = 0._rkind dCompress_dPsiPrev(:) = 0._rkind - fluxVecPrev(:) = 0._rkind resVecPrev(:) = 0._rkind retval = FSUNContext_Create(c_null_ptr, sunctx) @@ -537,80 +510,19 @@ subroutine summaSolve4ida( & + dCompress_dPsiPrev(:) * mLayerMatricHeadPrimePrev(:) ) * dt_diff/2._rkind ! ---- - ! * check energy balance, need to include phase change + ! * check energy balance, from residuals + ! formulation with prime variables would cancel to closedForm version, so does not matter which formulation is used !------------------------ - if(checkNrgBalance)then - - if( model_decisions(iLookDECISIONS%howHeatCap)%iDecision == closedForm)then ! did not compute enthalpy without phase already - call t2enthalpyPrime(& - ! input: data structures - eqns_data%diag_data, & ! intent(in): model diagnostic variables for a local HRU - eqns_data%mpar_data, & ! intent(in): parameter data structure - eqns_data%indx_data, & ! intent(in): model indices - ! input: state variables for the vegetation canopy - eqns_data%scalarCanairTempPrime, & ! intent(in): prime value for canopy air temperature (K) - eqns_data%scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) - eqns_data%scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) - eqns_data%scalarCanopyTempPrime, & ! intent(in): prime value for temperature of the vegetation canopy (K) - eqns_data%scalarCanopyWatPrime, & ! intent(in): prime value for total water of the vegetation canopy (kg m-2) - ! input: variables for the snow-soil domain - eqns_data%mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - eqns_data%mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - eqns_data%mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - eqns_data%mLayerTempPrime, & ! intent(in): prime vector of temperature of each snow+soil layer (K) - eqns_data%mLayerVolFracWatPrime, & ! intent(in): prime vector of volumetric total water content of each snow+soil layer (-) - eqns_data%mLayerMatricHeadPrime, & ! intent(in): prime vector of total water matric potential of each snow+soil layer (m) - ! input: pre-computed derivatives - eqns_data%diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1), & ! intent(in): fraction of canopy liquid water (-) - eqns_data%diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat, & ! intent(in): fraction of liquid water (-) - eqns_data%deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - ! output: enthalpy - eqns_data%scalarCanairEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the canopy air space (J m-3) - eqns_data%scalarCanopyEnthalpyPrime, & ! intent(out): prime value for temperature component of enthalpy of the vegetation canopy (J m-3) - eqns_data%mLayerEnthalpyPrime, & ! intent(out): prime vector of temperature component of enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - endif - - ! initialize mixture enthalpy prime to temperature component of enthalpy, no difference in canopy air space - scalarCanopyHmixPrime = eqns_data%scalarCanopyEnthalpyPrime - mLayerHmixPrime = eqns_data%mLayerEnthalpyPrime - - ! compute mixture enthalpy prime - call enthalpy2DeltaH(& - ! input: data structures - eqns_data%diag_data, & ! intent(in): model diagnostic variables for a local HRU - eqns_data%indx_data, & ! intent(in): model indices - ! input: ice content change - eqns_data%scalarCanopyIcePrime, & ! intent(in): prime value for canopy ice content (kg m-2 s-1) - eqns_data%mLayerVolFracIcePrime, & ! intent(in): prime vector of ice volume fraction (s-1) - ! input/output: enthalpy - scalarCanopyHmixPrime, & ! intent(inout): prime value for mixture enthalpy of the vegetation canopy (J m-3 s-1) - mLayerHmixPrime, & ! intent(inout): prime vector of mixture enthalpy of each snow+soil layer (J m-3 s-1) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + if(checkNrgBalance)then - ! compute energy balance mean - if(ixCasNrg/=integerMissing) balance(ixCasNrg) = balance(ixCasNrg) + ( eqns_data%scalarCanairEnthalpyPrime - eqns_data%fluxVec(ixCasNrg) & - + scalarCanairEnthalpyPrimePrev - fluxVecPrev(ixCasNrg) )*dt_diff/2._rkind/dt - if(ixVegNrg/=integerMissing) balance(ixVegNrg) = balance(ixVegNrg) + ( scalarCanopyHmixPrime - eqns_data%fluxVec(ixVegNrg) & - + scalarCanopyHmixPrimePrev - fluxVecPrev(ixVegNrg) )*dt_diff/2._rkind/dt + ! compute energy balance mean, resVec is the instanteous residual vector from the solver + if(ixCasNrg/=integerMissing) balance(ixCasNrg) = balance(ixCasNrg) + ( eqns_data%resVec(ixCasNrg) + resVecPrev(ixCasNrg) )*dt_diff/2._rkind/dt + if(ixVegNrg/=integerMissing) balance(ixVegNrg) = balance(ixVegNrg) + ( eqns_data%resVec(ixVegNrg) + resVecPrev(ixVegNrg) )*dt_diff/2._rkind/dt if(nSnowSoilNrg>0)then do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) - balance(ixSnowSoilNrg(i)) = balance(ixSnowSoilNrg(i)) + ( mLayerHmixPrime(i) - eqns_data%fluxVec(ixSnowSoilNrg(i)) & - + mLayerHmixPrimePrev(i) - fluxVecPrev(ixSnowSoilNrg(i)) )*dt_diff/2._rkind/dt + balance(ixSnowSoilNrg(i)) = balance(ixSnowSoilNrg(i)) + + ( eqns_data%resVec(ixSnowSoilNrg(i)) + resVecPrev(ixSnowSoilNrg(i)) )*dt_diff/2._rkind/dt enddo endif - ! should be equivalent to above, use for debugging - !if(ixCasNrg/=integerMissing) balance(ixCasNrg) = balance(ixCasNrg) + ( eqns_data%resVec(ixCasNrg) + resVecPrev(ixCasNrg) )*dt_diff/2._rkind/dt - !if(ixVegNrg/=integerMissing) balance(ixVegNrg) = balance(ixVegNrg) + ( eqns_data%resVec(ixVegNrg) + resVecPrev(ixVegNrg) )*dt_diff/2._rkind/dt - !if(nSnowSoilNrg>0)then - ! do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) - ! balance(ixSnowSoilNrg(i)) = balance(ixSnowSoilNrg(i)) + + ( eqns_data%resVec(ixSnowSoilNrg(i)) + resVecPrev(ixSnowSoilNrg(i)) )*dt_diff/2._rkind/dt - ! enddo - !endif endif ! ---- @@ -618,8 +530,7 @@ subroutine summaSolve4ida( & !------------------------ if(checkMassBalance)then - ! compute mass balance mean - ! resVec is the instanteous residual vector from the solver + ! compute mass balance mean, resVec is the instanteous residual vector from the solver if(ixVegHyd/=integerMissing) balance(ixVegHyd) = balance(ixVegHyd) + ( eqns_data%resVec(ixVegHyd) + resVecPrev(ixVegHyd) )*dt_diff/2._rkind/dt if(nSnowSoilHyd>0)then do concurrent (i=1:nLayers,ixSnowSoilHyd(i)/=integerMissing) @@ -633,14 +544,9 @@ subroutine summaSolve4ida( & eqns_data%scalarCanopyTempPrev = eqns_data%scalarCanopyTempTrial eqns_data%mLayerTempPrev(:) = eqns_data%mLayerTempTrial(:) eqns_data%mLayerMatricHeadPrev(:) = eqns_data%mLayerMatricHeadTrial(:) - mLayerMatricHeadPrimePrev(:) = eqns_data%mLayerMatricHeadPrime(:) - scalarCanairEnthalpyPrimePrev = eqns_data%scalarCanairEnthalpyPrime - scalarCanopyHmixPrimePrev = scalarCanopyHmixPrime - mLayerHmixPrimePrev(:) = mLayerHmixPrime(:) + mLayerMatricHeadPrimePrev(:) = eqns_data%mLayerMatricHeadPrime(:) dCompress_dPsiPrev(:) = eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) tretPrev = tret(1) - flux_prev = eqns_data%flux_data - fluxVecPrev(:) = eqns_data%fluxVec(:) resVecPrev(:) = eqns_data%resVec(:) ! Restart for where vegetation and layers cross freezing point @@ -676,8 +582,6 @@ subroutine summaSolve4ida( & indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = eqns_data%indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) !only number of flux calculations changes in indx_data err = eqns_data%err message = eqns_data%message - else - eqns_data%fluxVec(:) = realMissing endif ! free memory @@ -695,12 +599,8 @@ subroutine summaSolve4ida( & deallocate( eqns_data%mLayerMatricHeadLiqPrime) deallocate( eqns_data%mLayerVolFracWatPrime ) deallocate( eqns_data%mLayerVolFracIcePrime ) - deallocate( eqns_data%mLayerEnthalpyPrime ) deallocate( mLayerMatricHeadPrimePrev ) - deallocate( mLayerHmixPrime ) - deallocate( mLayerHmixPrimePrev ) deallocate( dCompress_dPsiPrev ) - deallocate( eqns_data%fluxVec ) deallocate( eqns_data%resVec ) deallocate( eqns_data%resSink ) deallocate( rootsfound ) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 5270b2a17..18d3d9fa4 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -268,28 +268,28 @@ subroutine systemSolv(& globalVars: associate(& ! model decisions ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver - ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy - ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization - ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) + ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& ! intent(in): [i4b] choice of variable in energy conservation backward Euler residual + ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization + ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) ! enthalpy - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) ! derivatives, diagnostic for enthalpy - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) - ! model state variables - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + ! model state variables + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variables (snow and soil domains) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) ! check the need to merge snow layers snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) @@ -394,7 +394,7 @@ subroutine systemSolv(& ! initialize the trial state vectors stateVecTrial = stateVecInit - if(ixHowHeatCap == enthalpyFD)then + if(ixNrgConserv == enthalpyFD .and. ixNumericalMethod .ne. ida)then ! compute H_T at the beginning of the data step call t2enthalpy(& ! input: data structures diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 9a07abb05..d73b71d4b 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -223,7 +223,7 @@ subroutine varSubstep(& ixSaturation => io_varSubstep % ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) ! model decisions ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver - ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy + ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& ! intent(in): [i4b] choice of variable in energy conservation backward Euler residual ! number of layers nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers @@ -275,7 +275,7 @@ subroutine varSubstep(& ! set the flag to compute enthalpy computeEnthalpy = .false. - if(checkNrgBalance .or. ixHowHeatCap==enthalpyFD) computeEnthalpy = .true. ! need to get enthalpy regardles of other decisions + if(checkNrgBalance .or. ixNrgConserv==enthalpyFD) computeEnthalpy = .true. ! need to get enthalpy regardles of other decisions ! initialize the length of the substep dtSubstep = dtInit @@ -1010,7 +1010,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec balance(ixSnowSoilNrg(i)) = mLayerHmixDelta(i) - fluxVec(ixSnowSoilNrg(i))*dt enddo endif - ! should be equivalent to above, use for debugging + ! This is equivalent to above if, and only if, ixNrgConserv = enthalpyFD !!if(ixCasNrg/=integerMissing) balance(ixCasNrg) = resVec(ixCasNrg) !if(ixVegNrg/=integerMissing) balance(ixVegNrg) = resVec(ixVegNrg) !if(nSnowSoilNrg>0)then diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zDecisions.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zDecisions.txt index 4c36cec0c..21ceb93c9 100644 --- a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zDecisions.txt +++ b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zDecisions.txt @@ -36,7 +36,7 @@ thCondSnow jrdn1991 ! (26) choice of thermal conduct thCondSoil mixConstit ! (27) choice of thermal conductivity representation for soil spatial_gw localColumn ! (28) choice of method for the spatial representation of groundwater subRouting timeDlay ! (29) choice of method for sub-grid routing -howHeatCap closedForm ! (30) choice of method to compute heat capacity in energy equation +nrgConserv closedForm ! (30) choice of variable in energy conservation residual ! *********************************************************************************************** ! ***** description of the options available -- nothing below this point is read **************** ! *********************************************************************************************** @@ -166,7 +166,7 @@ howHeatCap closedForm ! (30) choice of method to compu ! timeDlay ! time-delay histogram ! qInstant ! instantaneous routing ! ----------------------------------------------------------------------------------------------- -! (30) choice of method to compute heat capacity in energy equation -! closedForm ! closed form +! (30) choice of variable in energy conservation backward Euler residual +! closedForm ! closed form heat capacity ! enthalpyFD ! enthalpy finite difference, has better coincidence of energy conservation ! *********************************************************************************************** From 8729e3cdfa76b18fdfbe72aedd4b0fc8eeabdb67 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 5 Feb 2024 21:52:05 -0600 Subject: [PATCH 1115/1472] more name changes --- docs/sundials_bmi/flags_params_sundials.txt | 2 +- .../settings/syntheticTestCases/celia1990/summa_zDecisions.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sundials_bmi/flags_params_sundials.txt b/docs/sundials_bmi/flags_params_sundials.txt index 61f73d9c7..3046a16aa 100644 --- a/docs/sundials_bmi/flags_params_sundials.txt +++ b/docs/sundials_bmi/flags_params_sundials.txt @@ -1,7 +1,7 @@ To switch between SUMMA-BE and SUMMA-SUNDIALS, the num_method in the model_decision file can be either one of the values "numrec" (choice "itertive" is backward compatible), "kinsol", or "ida". -In energy equation, the heat capacity is computed using either the analytical (closed) formula or the finite difference formula (dH_T/dT). The "howHeatCap" variable has been added to the var_lookup module to handle such decision. A user should add this variable to the model_decision file with one of the values "closedForm" or "enthalpyFD". Choice of num_method as "itertive" will set num_method=numrec and howHeatCap=closedForm. +In energy conservation residual for backward Euler, either the the analytical (closed form) heat capacity formula or the enthalpy finite difference formula (dH_T/dT) is used. The "nrgConserv" variable has been added to the var_lookup module to handle such decision. A user should add this variable to the model_decision file with one of the values "closedForm" or "enthalpyFD". Choice of num_method as "itertive" will set num_method=numrec and nrgConserv=closedForm. All SUMMA-SUNDIALS files are in build/source/engine. The important ones are as following: diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zDecisions.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zDecisions.txt index 21ceb93c9..90f2397b7 100644 --- a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zDecisions.txt +++ b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zDecisions.txt @@ -36,7 +36,7 @@ thCondSnow jrdn1991 ! (26) choice of thermal conduct thCondSoil mixConstit ! (27) choice of thermal conductivity representation for soil spatial_gw localColumn ! (28) choice of method for the spatial representation of groundwater subRouting timeDlay ! (29) choice of method for sub-grid routing -nrgConserv closedForm ! (30) choice of variable in energy conservation residual +nrgConserv closedForm ! (30) choice of variable in energy conservation backward Euler residual ! *********************************************************************************************** ! ***** description of the options available -- nothing below this point is read **************** ! *********************************************************************************************** From 24450e97fce6359133524bc76094267331d7460b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 5 Feb 2024 22:22:18 -0600 Subject: [PATCH 1116/1472] more decision names and turn off lookup tables since calculating with hypergeometric --- build/source/driver/summa_setup.f90 | 10 +++++----- build/source/engine/eval8summa.f90 | 4 ++-- build/source/engine/eval8summaWithPrime.f90 | 4 ++-- build/source/engine/mDecisions.f90 | 2 +- build/source/engine/systemSolv.f90 | 4 ++-- build/source/engine/varSubstep.f90 | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index 55cda9561..94ac48810 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -44,8 +44,8 @@ module summa_setup ! look-up values for the choice of heat capacity computation USE mDecisions_module,only:& - closedForm,& ! heat capacity using closed form, not using enthalpy - enthalpyFD ! heat capacity using enthalpy + closedForm,& ! heat capacity closed form in backward Euler residual + enthalpyFD ! enthalpy finite difference in backward Euler residual ! named variables to define the decisions for snow layers USE mDecisions_module,only:& @@ -154,9 +154,9 @@ subroutine summa_paramSetup(summa1_struc, err, message) ! decide if computing enthalpy lookup tables, if need enthalpy and not using hypergeometric function ! NOTE: this should be replaced by a parameter of if want lookup table enthalpy, but for now it is hard-coded - ! need enthalpy if checkNrgBalance in varSubstep is turned on or using nrgConserv=enthalpyFD - ! then, need lookups if use_lookup=.true. in t2enthalpy (numrec or kin) - needLookup = .true. + ! needLookup=.true. if checkNrgBalance in varSubstep is turned on or using nrgConserv=enthalpyFD + ! AND use_lookup=.true. in t2enthalpy + needLookup = .false. !if (model_decisions(iLookDECISIONS%nrgConserv)%iDecision == enthalpyFD) needLookup = .true. ! initialize the start of the initialization diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 1cb136ed4..f8f06a753 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -57,8 +57,8 @@ module eval8summa_module ! look-up values for the choice of heat capacity computation USE mDecisions_module,only: & - closedForm, & ! heat capacity using closed form, not using enthalpy - enthalpyFD ! heat capacity using enthalpy + closedForm, & ! heat capacity closed form in backward Euler residual + enthalpyFD ! enthalpy finite difference in backward Euler residual ! look-up values for the numerical method USE mDecisions_module,only: & diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 5f18c7cbf..934eca3ca 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -34,8 +34,8 @@ module eval8summaWithPrime_module ! look-up values for the choice of heat capacity computation USE mDecisions_module,only: & - closedForm, & ! heat capacity using closed form, not using enthalpy - enthalpyFD ! heat capacity using enthalpy + closedForm, & ! heat capacity closed form in backward Euler residual + enthalpyFD ! enthalpy finite difference in backward Euler residual implicit none private diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index b97812611..44dfe9a79 100644 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -147,7 +147,7 @@ module mDecisions_module ! look-up values for the choice of snow unloading from the canopy integer(i4b),parameter,public :: meltDripUnload = 321 ! Hedstrom and Pomeroy (1998), Storck et al 2002 (snowUnloadingCoeff & ratioDrip2Unloading) integer(i4b),parameter,public :: windUnload = 322 ! Roesch et al 2001, formulate unloading based on wind and temperature -! look-up values for the choice of energy equation +! look-up values for the choice of energy conservation residual integer(i4b),parameter,public :: enthalpyFD = 323 ! enthalpyFD integer(i4b),parameter,public :: closedForm = 324 ! closedForm ! ----------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 18d3d9fa4..99f945812 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -80,8 +80,8 @@ module systemSolv_module ! look-up values for the choice of heat capacity computation USE mDecisions_module,only: & - closedForm, & ! heat capacity using closed form, not using enthalpy - enthalpyFD ! heat capacity using enthalpy + closedForm, & ! heat capacity closed form in backward Euler residual + enthalpyFD ! enthalpy finite difference in backward Euler residual ! look-up values for the choice of groundwater representation (local-column, or single-basin) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index d73b71d4b..4b4964352 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -81,8 +81,8 @@ module varSubstep_module ! look-up values for the choice of heat capacity computation USE mDecisions_module,only: & - closedForm, & ! heat capacity using closed form, not using enthalpy - enthalpyFD ! heat capacity using enthalpy + closedForm, & ! heat capacity closed form in backward Euler residual + enthalpyFD ! enthalpy finite difference in backward Euler residual ! safety: set private unless specified otherwise implicit none From 272f43e8014e5d45930b60e83c139f065d94f13c Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 6 Feb 2024 06:54:55 -0600 Subject: [PATCH 1117/1472] Created update_stateMask subroutine and modularized some cycle operations in opSplittin. --- build/source/engine/opSplittin.f90 | 50 ++++++++++++++++++------------ 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 171cd9cc6..1ed83aaed 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -373,15 +373,12 @@ subroutine opSplittin(& if (split_select % solution) then call initialize_split_stateSplit; if (return_flag) return if (split_select % stateSplit) then - ! define state subsets for a given split... - ! get the mask for the state subset - return for a non-zero error code - call split_select % get_stateMask(indx_data,err,cmessage,message,return_flag) - nSubset = split_select % nSubset; stateMask = split_select % stateMask - if (return_flag) return - call validate_split ! verify that the split is valid - if (cycle_domainSplit) then; call split_select % advance_iDomainSplit; split_select % solution=.false.; split_select % stateSplit=.false.; cycle split_select_loop; end if - if (cycle_solution) then; call split_select % advance_ixSolution; split_select % stateSplit=.false.; cycle split_select_loop; end if + call update_stateMask; if (return_flag) return ! get the mask for the state subset - return for a non-zero error code + call validate_split ! verify that the split is valid + if (cycle_domainSplit) cycle split_select_loop ! if needed, proceed to next iteration of domainSplit method + if (cycle_solution) cycle split_select_loop ! if needed, proceed to next iteration of solution method if (return_flag) return ! return for a non-zero error code + call save_recover ! save/recover copies of variables and fluxes ! assemble vectors for a given split... @@ -396,8 +393,14 @@ subroutine opSplittin(& if (cycle_coupling) then call split_select % advance_ixCoupling; call split_select % initialize_flags; cycle split_select_loop ! cycle loops if necessary end if - if (cycle_stateThenDomain) then; call split_select % advance_ixStateThenDomain; split_select % domainSplit=.false.; split_select % solution=.false.; split_select % stateSplit=.false.; cycle split_select_loop; end if ! deactivate flags for inner loops - if (cycle_solution) then; call split_select % advance_ixSolution; split_select % stateSplit=.false.; cycle split_select_loop; end if + if (cycle_stateThenDomain) then + call split_select % advance_ixStateThenDomain + split_select % domainSplit=.false.; split_select % solution=.false.; split_select % stateSplit=.false.; + cycle split_select_loop + end if ! deactivate flags for inner loops + if (cycle_solution) then + call split_select % advance_ixSolution; split_select % stateSplit=.false.; cycle split_select_loop; + end if call confirm_variable_updates; if (return_flag) return ! check that state variables updated - return if error @@ -959,14 +962,12 @@ subroutine try_other_solution_methods end if end subroutine try_other_solution_methods - subroutine update_stateFilter + subroutine update_stateMask ! *** Get the mask for the state subset *** - return_flag=.false. ! initialize flag - call initialize_stateFilter - call stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) - call finalize_stateFilter - if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! error control - end subroutine update_stateFilter + call split_select % get_stateMask(indx_data,err,cmessage,message,return_flag) + nSubset = split_select % nSubset; stateMask = split_select % stateMask + if (return_flag) return + end subroutine update_stateMask subroutine validate_split ! *** Verify that the split is valid *** @@ -974,11 +975,22 @@ subroutine validate_split cycle_domainSplit=.false. cycle_solution=.false. return_flag=.false. + ! check that state variables exist - if (nSubset==0) then; cycle_domainSplit=.true.; return; end if + if (nSubset==0) then + call split_select % advance_iDomainSplit + split_select % solution=.false.; split_select % stateSplit=.false. + cycle_domainSplit=.true. + return + end if ! avoid redundant case where vector solution is of length 1 - if (ixSolution==vector .and. count(stateMask)==1) then; cycle_solution=.true.; return; end if + if (ixSolution==vector .and. count(stateMask)==1) then + call split_select % advance_ixSolution; + split_select % stateSplit=.false.; + cycle_solution=.true. + return + end if ! check that we do not attempt the scalar solution for the fully coupled case if (ixCoupling==fullyCoupled .and. ixSolution==scalar) then From fb2cf5b983df3320c07c37597a6caa3d13c03642 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 6 Feb 2024 15:15:03 -0600 Subject: [PATCH 1118/1472] fix non-lookup version for BE, sundials version not working yet, seg fault --- build/source/engine/enthalpyTemp.f90 | 33 +++++++++++++++++----------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index b68785eaa..af294d2f0 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -621,13 +621,9 @@ subroutine t2enthalpy(& theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex) , & ! soil porosity (-) theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex) , & ! volumetric residual water content (-) vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) - ! associate values in the lookup table - Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup , & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) - L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) & ! van Genuchten "n" parameter (-) ) ! end associate statement - + ! diagnostic variables vGn_m = 1._rkind - 1._rkind/vGn_n Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) @@ -647,15 +643,26 @@ subroutine t2enthalpy(& ! get the frozen water content ! initialize for case Tcrit=Tfreeze, i.e. mLayerMatricHeadTrial(ixControlIndex)>0 integral_frz_low = 0._rkind + if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature - ! get the lower limit of the integral - if(diff0<0._rkind)then - call splint(Tk,Ly,L2,Tcrit,integral_frz_low,dL,err,cmessage) + ! make associate to the the lookup table + lookVars: associate(& + Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) + Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup , & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) + L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + ) ! end associate statement + + ! get the lower limit of the integral + if(diff0<0._rkind)then + call splint(Tk,Ly,L2,Tcrit,integral_frz_low,dL,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + end if + ! get the upper limit of the integral + call splint(Tk,Ly,L2,mlayerTempTrial(iLayer),integral_frz_upp,dL,err,cmessage) if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - end if - ! get the upper limit of the integral - call splint(Tk,Ly,L2,mlayerTempTrial(iLayer),integral_frz_upp,dL,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + end associate lookVars + else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature ! get the lower limit of the integral if(diff0<0._rkind)then From c10b3014b9b3a3766452010aa6dd092eda236823 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 6 Feb 2024 22:33:26 -0600 Subject: [PATCH 1119/1472] fixed segfault in ida --- build/source/engine/summaSolve4ida.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 01ad40754..5dbb37031 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -324,6 +324,7 @@ subroutine summaSolve4ida( & allocate( eqns_data%mLayerVolFracIcePrime(nLayers) ) allocate( mLayerMatricHeadPrimePrev(nSoil) ) allocate( dCompress_dPsiPrev(nSoil) ) + allocate( eqns_data%fluxVec(nState) ) allocate( eqns_data%resVec(nState) ) allocate( eqns_data%resSink(nState) ) allocate( resVecPrev(nState) ) @@ -520,7 +521,7 @@ subroutine summaSolve4ida( & if(ixVegNrg/=integerMissing) balance(ixVegNrg) = balance(ixVegNrg) + ( eqns_data%resVec(ixVegNrg) + resVecPrev(ixVegNrg) )*dt_diff/2._rkind/dt if(nSnowSoilNrg>0)then do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) - balance(ixSnowSoilNrg(i)) = balance(ixSnowSoilNrg(i)) + + ( eqns_data%resVec(ixSnowSoilNrg(i)) + resVecPrev(ixSnowSoilNrg(i)) )*dt_diff/2._rkind/dt + balance(ixSnowSoilNrg(i)) = balance(ixSnowSoilNrg(i)) + ( eqns_data%resVec(ixSnowSoilNrg(i)) + resVecPrev(ixSnowSoilNrg(i)) )*dt_diff/2._rkind/dt enddo endif endif From a7e5f7772082e4b8efb1117c142d9d867049246c Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 7 Feb 2024 00:51:28 -0600 Subject: [PATCH 1120/1472] Modularized remaining cycle related statements in opSplittin and some additional cleanup. --- build/source/engine/opSplittin.f90 | 184 +++++++++++++++-------------- 1 file changed, 93 insertions(+), 91 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 1ed83aaed..ca7064996 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -146,7 +146,7 @@ module opSplittin_module ! class definitions type, public :: split_select_type ! class for selecting operator splitting methods - ! opSplittin loop indices (in order) + ! opSplittin indices (in order) integer(i4b) :: ixCoupling integer(i4b) :: iStateTypeSplit integer(i4b) :: ixStateThenDomain ! 1=state type split; 2=domain split within a given state type @@ -157,10 +157,10 @@ module opSplittin_module integer(i4b) :: nSubset ! number of selected state variables for a given split type(var_flagVec) :: fluxMask ! integer mask defining model fluxes logical(lgt),allocatable :: stateMask(:) ! mask defining desired state variables - ! flags for loop operations + ! flags for splitting method control logical(lgt) :: stateTypeSplitting,stateThenDomain,domainSplit,solution,stateSplit contains - procedure :: initialize_flags => split_select_initialize_flags ! initialize flags that control loop operations + procedure :: initialize_flags => split_select_initialize_flags ! initialize flags that control operations procedure :: initialize_ixCoupling => split_select_initialize_ixCoupling ! initialize operator splitting indices procedure :: initialize_iStateTypeSplit => split_select_initialize_iStateTypeSplit ! initialize operator splitting indices procedure :: initialize_ixStateThenDomain => split_select_initialize_ixStateThenDomain ! initialize operator splitting indices @@ -170,12 +170,12 @@ module opSplittin_module procedure :: get_stateMask => split_select_compute_stateMask ! compute stateMask and nSubset and load into class object - procedure :: advance_ixCoupling => split_select_advance_ixCoupling ! advance coupling loop iterator - procedure :: advance_iStateTypeSplit => split_select_advance_iStateTypeSplit ! advance coupling loop iterator - procedure :: advance_ixStateThenDomain => split_select_advance_ixStateThenDomain ! advance coupling loop iterator - procedure :: advance_iDomainSplit => split_select_advance_iDomainSplit ! advance coupling loop iterator - procedure :: advance_ixSolution => split_select_advance_ixSolution ! advance coupling loop iterator - procedure :: advance_iStateSplit => split_select_advance_iStateSplit ! advance coupling loop iterator + procedure :: advance_ixCoupling => split_select_advance_ixCoupling ! advance coupling iterator + procedure :: advance_iStateTypeSplit => split_select_advance_iStateTypeSplit ! advance stateTypeSplitting iterator + procedure :: advance_ixStateThenDomain => split_select_advance_ixStateThenDomain ! advance stateThenDomain iterator + procedure :: advance_iDomainSplit => split_select_advance_iDomainSplit ! advance domainSplit iterator + procedure :: advance_ixSolution => split_select_advance_ixSolution ! advance solution iterator + procedure :: advance_iStateSplit => split_select_advance_iStateSplit ! advance stateSplit iterator procedure :: logic_exit_stateTypeSplitting => split_select_logic_exit_stateTypeSplitting ! get logical for branch procedure :: logic_exit_stateThenDomain => split_select_logic_exit_stateThenDomain ! get logical for branch @@ -345,11 +345,11 @@ subroutine opSplittin(& real(rkind) :: mean_step_state ! mean step over the state (with or without domain splits) real(rkind) :: mean_step_solution ! mean step for a solution (scalar or vector) logical(lgt) :: addFirstFlux ! flag to add the first flux to the mask - ! loop control + ! splitting method control variables logical(lgt) :: exit_split_select,cycle_split_select ! control for split_select loop logical(lgt) :: exit_coupling,exit_stateTypeSplitting,exit_stateThenDomain,exit_domainSplit,exit_solution,exit_stateSplit logical(lgt) :: cycle_coupling,cycle_stateTypeSplitting,cycle_stateThenDomain,cycle_domainSplit,cycle_solution,cycle_stateSplit - integer(i4b) :: iSplit,nSplit,itest + integer(i4b) :: iSplit,nSplit integer(i4b),parameter :: maxSplit=500 ! >= max number of splitting methods (controls upper limit of split_select loop) ! ------------------------ classes for subroutine arguments (classes defined in data_types module) ------------------------ ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** @@ -362,69 +362,59 @@ subroutine opSplittin(& ! *** Initialize Split Selector Object *** call initialize_split_select call initialize_split_coupling; if (return_flag) return - split_select_loop: do iSplit=1,maxSplit + split_select_loop: do iSplit=1,maxSplit ! coupling begins call initialize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop; if (return_flag) return - if (split_select % stateTypeSplitting) then + if (split_select % stateTypeSplitting) then ! stateTypeSplitting begins call initialize_split_stateThenDomain - if (split_select % stateThenDomain) then + if (split_select % stateThenDomain) then ! stateThenDomain begins call initialize_split_domainSplit; if (return_flag) return - if (split_select % domainSplit) then - call initialize_split_solution - if (split_select % solution) then - call initialize_split_stateSplit; if (return_flag) return - if (split_select % stateSplit) then - call update_stateMask; if (return_flag) return ! get the mask for the state subset - return for a non-zero error code - call validate_split ! verify that the split is valid - if (cycle_domainSplit) cycle split_select_loop ! if needed, proceed to next iteration of domainSplit method - if (cycle_solution) cycle split_select_loop ! if needed, proceed to next iteration of solution method - if (return_flag) return ! return for a non-zero error code - - call save_recover ! save/recover copies of variables and fluxes - - ! assemble vectors for a given split... - call get_split_indices; if (return_flag) return ! get indices for a given split - return for a non-zero error code - call update_fluxMask; if (return_flag) return ! define the mask of the fluxes used - return for a non-zero error code - - call solve_subset; if (return_flag) return ! solve variable subset for one time step - return for a positive error code - - call assess_solution; if (return_flag) return ! is solution a success or failure? - return for a recovering solution - - call try_other_solution_methods ! if solution failed to converge, try other splitting methods - if (cycle_coupling) then - call split_select % advance_ixCoupling; call split_select % initialize_flags; cycle split_select_loop ! cycle loops if necessary - end if - if (cycle_stateThenDomain) then - call split_select % advance_ixStateThenDomain - split_select % domainSplit=.false.; split_select % solution=.false.; split_select % stateSplit=.false.; - cycle split_select_loop - end if ! deactivate flags for inner loops - if (cycle_solution) then - call split_select % advance_ixSolution; split_select % stateSplit=.false.; cycle split_select_loop; - end if - - call confirm_variable_updates; if (return_flag) return ! check that state variables updated - return if error - - call success_check ! check for success - call check_exit_stateThenDomain ! check exit criterion for stateThenDomain split - call check_exit_solution; if (return_flag) return ! check exit criterion for solution split - return if error - end if ! stateSplit ends - call finalize_split_stateSplit - end if ! solution ends - call finalize_split_solution + if (split_select % domainSplit) then ! domainSplit begins + call initialize_split_solution + if (split_select % solution) then ! solution begins + call initialize_split_stateSplit; if (return_flag) return + if (split_select % stateSplit) then ! stateSplit begins + call update_stateMask; if (return_flag) return ! get the mask for the state subset - return for a non-zero error code + call validate_split ! verify that the split is valid + if (cycle_domainSplit) cycle split_select_loop ! if needed, proceed to next iteration of domainSplit method + if (cycle_solution) cycle split_select_loop ! if needed, proceed to next iteration of solution method + if (return_flag) return ! return for a non-zero error code + + call save_recover ! save/recover copies of variables and fluxes + + call get_split_indices; if (return_flag) return ! get indices for a given split - return for a non-zero error code + call update_fluxMask; if (return_flag) return ! define the mask for the fluxes used - return for a non-zero error code + + call solve_subset; if (return_flag) return ! solve variable subset for one time step - return for a positive error code + call assess_solution; if (return_flag) return ! is solution a success or failure? - return for a recovering solution + + call try_other_solution_methods ! if solution failed to converge, try other splitting methods + if (cycle_coupling) cycle split_select_loop ! if needed, proceed to next iteration of coupling method + if (cycle_stateThenDomain) cycle split_select_loop ! if needed, proceed to next iteration of stateThenDomain method + if (cycle_solution) cycle split_select_loop ! if needed, proceed to next iteration of solution method + + call confirm_variable_updates; if (return_flag) return ! check that state variables are updated - return if error + + call success_check ! check for success + call check_exit_stateThenDomain ! check exit criterion for stateThenDomain split + call check_exit_solution; if (return_flag) return ! check exit criterion for solution split - return if error + end if ! stateSplit ends + call finalize_split_stateSplit + end if ! solution ends + call finalize_split_solution end if ! domainSplit ends call finalize_split_domainSplit end if ! stateThenDomain ends call finalize_split_stateThenDomain; if (return_flag) return end if ! stateTypeSplitting ends call finalize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop - end do split_select_loop + end do split_select_loop ! coupling ends call finalize_split_coupling; if (return_flag) return contains subroutine initialize_split_select - ! *** Initialize split_select class object - currently under development *** + ! *** Initialize split_select class object *** ! allocate data components allocate(split_select % stateMask(1:nState)) ! allocate split_select components @@ -433,7 +423,7 @@ subroutine initialize_split_select return_flag=.false. exit_split_select=.false. cycle_split_select=.false. - call split_select % initialize_flags ! initialize loop control flags + call split_select % initialize_flags ! initialize control flags end subroutine initialize_split_select subroutine initialize_split_coupling @@ -447,7 +437,7 @@ subroutine initialize_split_stateTypeSplitting if (split_select % logic_initialize_stateTypeSplitting()) then ixCoupling=split_select % ixCoupling if (ixCoupling.gt.nCoupling) then; exit_split_select=.true.; return; end if ! exit if all splits are exhausted - call initialize_stateTypeSplitting; if (return_flag) return ! setup steps for stateTypeSplitting loop - return if error occurs + call initialize_stateTypeSplitting; if (return_flag) return ! setup steps for stateTypeSplitting split method - return if error occurs call split_select % initialize_iStateTypeSplit; split_select % stateTypeSplitting=.true. end if if (split_select % logic_exit_stateTypeSplitting()) then @@ -459,14 +449,14 @@ subroutine initialize_split_stateThenDomain ! *** Initialize stateThenDomain split method *** if (split_select % logic_initialize_stateThenDomain()) then ! first try the state type split, then try the domain split within a given state type - call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split + call initialize_stateThenDomain ! setup steps for stateThenDomain split method -- identify state-specific variables for a given state split call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.true. end if - if (split_select % logic_exit_stateThenDomain()) then ! stateThenDomain loop + if (split_select % logic_exit_stateThenDomain()) then ! stateThenDomain ixStateThenDomain=split_select % ixStateThenDomain if (ixStateThenDomain > (1+tryDomainSplit)) then - ixStateThenDomain=ixStateThenDomain-1; split_select % ixStateThenDomain = ixStateThenDomain ! correct index needed after loop exit - split_select % stateThenDomain=.false. ! eqivalent to exiting the stateThenDomain loop + ixStateThenDomain=ixStateThenDomain-1; split_select % ixStateThenDomain = ixStateThenDomain ! correct index needed after exit + split_select % stateThenDomain=.false. ! eqivalent to exiting the stateThenDomain method end if end if end subroutine initialize_split_stateThenDomain @@ -474,7 +464,7 @@ end subroutine initialize_split_stateThenDomain subroutine initialize_split_domainSplit ! *** Initialize domainSplit split method *** if (split_select % logic_initialize_domainSplit()) then - call initialize_domainSplit; if (return_flag) return ! setup steps for domainSplit loop - return if error occurs + call initialize_domainSplit; if (return_flag) return ! setup steps for domainSplit split method - return if error occurs call split_select % initialize_iDomainSplit; split_select % domainSplit=.true. end if if (split_select % logic_exit_domainSplit()) then @@ -495,7 +485,7 @@ end subroutine initialize_split_solution subroutine initialize_split_stateSplit ! *** Initialize stateSplit split method *** if (split_select % logic_initialize_stateSplit()) then - call initialize_stateSplit; if (return_flag) return ! setup steps for stateSplit loop - return if error occurs + call initialize_stateSplit; if (return_flag) return ! setup steps for stateSplit split method - return if error occurs call split_select % initialize_iStateSplit; split_select % stateSplit=.true.; ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) end if if (split_select % logic_exit_stateSplit()) then ! stateSplit begins @@ -533,7 +523,7 @@ end subroutine finalize_split_stateSplit subroutine finalize_split_solution ! *** Finalize steps for solution split method *** if (split_select % logic_finalize_solution()) then - call finalize_solution ! final steps following solution loop + call finalize_solution ! final steps following solution split method call split_select % advance_iDomainSplit end if end subroutine finalize_split_solution @@ -548,7 +538,7 @@ end subroutine finalize_split_domainSplit subroutine finalize_split_stateThenDomain ! *** Finalize steps for stateThenDomain split method *** if (split_select % logic_finalize_stateThenDomain()) then - call finalize_stateThenDomain; if (return_flag) return ! final steps following the stateThenDomain loop + call finalize_stateThenDomain; if (return_flag) return ! final steps following the stateThenDomain split method call split_select % advance_iStateTypeSplit end if end subroutine finalize_split_stateThenDomain @@ -558,7 +548,7 @@ subroutine finalize_split_stateTypeSplitting if (split_select % logic_finalize_stateTypeSplitting()) then call finalize_stateTypeSplitting if (exit_coupling) then - call split_select % initialize_ixCoupling; exit_split_select=.true.; return ! success = exit the coupling loop + call split_select % initialize_ixCoupling; exit_split_select=.true.; return ! success = exit the coupling split method (split_select_loop) end if call split_select % advance_ixCoupling end if @@ -573,7 +563,7 @@ subroutine finalize_split_coupling end subroutine finalize_split_coupling subroutine initialize_coupling - ! *** initial steps for coupling loop *** + ! *** initial steps for coupling split method *** ! initialize error control err=0; message="opSplittin/" @@ -674,7 +664,7 @@ subroutine allocate_memory end subroutine allocate_memory subroutine finalize_coupling - ! *** final operations for coupling loop *** + ! *** final operations for coupling split method *** ! check that all state variables were updated if (any(stateCheck==0)) then message=trim(message)//'some state variables were not updated!' @@ -695,7 +685,7 @@ subroutine finalize_coupling end subroutine finalize_coupling subroutine initialize_stateTypeSplitting - ! *** Initial steps to prepare for iterations of the stateTypeSplit loop *** + ! *** Initial steps to prepare for iterations of the stateTypeSplit split method *** return_flag=.false. ! initialize flag ! initialize the time step dtInit = min(merge(dt, dtmin_coupled, ixCoupling==fullyCoupled), dt) ! initial time step @@ -732,9 +722,9 @@ subroutine get_nStateTypeSplit_tryDomainSplit(ixCoupling_value) end subroutine get_nStateTypeSplit_tryDomainSplit subroutine finalize_stateTypeSplitting - ! *** Final operations subsequent to the stateTypeSplitting loop *** - exit_coupling=.false. ! initialize flag for loop control - if (ixCoupling==fullyCoupled .and. .not.failure) then; exit_coupling=.true.; return; end if ! success = exit the coupling loop in opSplittin + ! *** Final operations subsequent to the stateTypeSplitting split method *** + exit_coupling=.false. ! initialize flag for control + if (ixCoupling==fullyCoupled .and. .not.failure) then; exit_coupling=.true.; return; end if ! success = exit the coupling method in opSplittin end subroutine finalize_stateTypeSplitting subroutine initialize_stateThenDomain @@ -757,9 +747,9 @@ subroutine initialize_stateThenDomain end subroutine initialize_stateThenDomain subroutine finalize_stateThenDomain - ! *** Final steps following the stateThenDomain loop *** + ! *** Final steps following the stateThenDomain split method *** ! sum the mean steps for the time step over each state type split - !if (ixStateThenDomain == 2+tryDomainSplit) ixStateThenDomain=1+tryDomainSplit ! correct index value if stateThenDomain loop is completed fully + !if (ixStateThenDomain == 2+tryDomainSplit) ixStateThenDomain=1+tryDomainSplit ! correct index value if stateThenDomain method is completed fully select case(ixStateThenDomain) case(fullDomain); mean_step_dt = mean_step_dt + mean_step_solution/nStateTypeSplit case(subDomain); mean_step_dt = mean_step_dt + mean_step_state/nStateTypeSplit @@ -783,7 +773,7 @@ subroutine finalize_stateThenDomain end subroutine finalize_stateThenDomain subroutine initialize_domainSplit - ! *** initial operations to set up domainSplit loop *** + ! *** initial operations to set up domainSplit split method *** return_flag=.false. ! initialize flag associate(numberDomainSplitNrg => indx_data%var(iLookINDEX%numberDomainSplitNrg )%dat(1),& ! intent(inout): [i4b] number of domain splitting solutions for energy (-) numberDomainSplitMass => indx_data%var(iLookINDEX%numberDomainSplitMass)%dat(1) )! intent(inout): [i4b] number of domain splitting solutions for mass (-) @@ -818,13 +808,13 @@ subroutine get_nDomainSplit(ixStateThenDomain_value) end subroutine get_nDomainSplit subroutine finalize_solution - ! *** final operations following solution loop *** + ! *** final operations following solution split method *** ! sum the mean steps for the state over each domain split mean_step_state = mean_step_state + mean_step_solution/nDomainSplit end subroutine finalize_solution subroutine initialize_stateSplit - ! *** initial operations to set up stateSplit loop *** + ! *** initial operations to set up stateSplit split method *** return_flag=.false. ! initialize flag mean_step_solution = 0._rkind ! initialize mean step for a solution @@ -950,13 +940,25 @@ subroutine try_other_solution_methods cycle_solution=.false. ! try the fully split solution if failed to converge with a minimum time step in the coupled solution - if (ixCoupling==fullyCoupled .and. failure) then; cycle_coupling=.true.; return; end if! return required to execute cycle statement in opSplittin + if (ixCoupling==fullyCoupled .and. failure) then + call split_select % advance_ixCoupling; call split_select % initialize_flags; ! prep for next iteration + cycle_coupling=.true.; return; ! return required to execute cycle statement in opSplittin + end if ! try the scalar solution if failed to converge with a minimum time step in the split solution if (ixCoupling/=fullyCoupled) then select case(ixStateThenDomain) - case(fullDomain); if (failure) cycle_stateThenDomain=.true.; return ! return required to execute cycle statement in opSplittin - case(subDomain); if (failure) cycle_solution=.true.; return + case(fullDomain) + if (failure) then + call split_select % advance_ixStateThenDomain ! prep for next iteration + split_select % domainSplit=.false.; split_select % solution=.false.; split_select % stateSplit=.false.; + cycle_stateThenDomain=.true.; return ! return required to execute cycle statement in opSplittin + end if + case(subDomain) + if (failure) then + call split_select % advance_ixSolution; split_select % stateSplit=.false.; ! prep for next iteration + cycle_solution=.true.; return ! return required to execute cycle statement in opSplittin + end if case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' end select end if @@ -1200,7 +1202,7 @@ end subroutine opSplittin ! ****** Class procedures for split_select_type class ****** subroutine split_select_initialize_flags(split_select) - ! *** Initialize flags for opSplittin loops *** + ! *** Initialize flags for opSplittin split methods *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector split_select % stateTypeSplitting=.false. split_select % stateThenDomain=.false. @@ -1210,37 +1212,37 @@ subroutine split_select_initialize_flags(split_select) end subroutine split_select_initialize_flags subroutine split_select_advance_ixCoupling(split_select) - ! *** Advance index for coupling loop *** + ! *** Advance index for coupling split method *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector split_select % ixCoupling = split_select % ixCoupling + 1 end subroutine split_select_advance_ixCoupling subroutine split_select_advance_iStateTypeSplit(split_select) - ! *** Advance index for stateTypeSplit loop *** + ! *** Advance index for stateTypeSplit split method *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector split_select % iStateTypeSplit = split_select % iStateTypeSplit + 1 end subroutine split_select_advance_iStateTypeSplit subroutine split_select_advance_ixStateThenDomain(split_select) - ! *** Advance index for stateThenDomain loop *** + ! *** Advance index for stateThenDomain split method *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector split_select % ixStateThenDomain = split_select % ixStateThenDomain + 1 end subroutine split_select_advance_ixStateThenDomain subroutine split_select_advance_iDomainSplit(split_select) - ! *** Advance index for domainSplit loop *** + ! *** Advance index for domainSplit split method *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector split_select % iDomainSplit = split_select % iDomainSplit + 1 end subroutine split_select_advance_iDomainSplit subroutine split_select_advance_ixSolution(split_select) - ! *** Advance index for solution loop *** + ! *** Advance index for solution split method *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector split_select % ixSolution = split_select % ixSolution + 1 end subroutine split_select_advance_ixSolution subroutine split_select_advance_iStateSplit(split_select) - ! *** Advance index for stateSplit loop *** + ! *** Advance index for stateSplit split method *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector split_select % iStateSplit = split_select % iStateSplit + 1 end subroutine split_select_advance_iStateSplit From 43eadc81bddfb12385f2005445f7d85c3eb5d69c Mon Sep 17 00:00:00 2001 From: ashleymedin <34691332+ashleymedin@users.noreply.github.com> Date: Wed, 7 Feb 2024 13:20:24 -0600 Subject: [PATCH 1121/1472] Merge pull request #33 from seantrim/develop_opSplittin_algorithm Refactoring opSplittin to use a single loop --- build/source/dshare/data_types.f90 | 3 + build/source/engine/opSplittin.f90 | 1036 +++++++++++++++++++++------- 2 files changed, 801 insertions(+), 238 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 7a6c64cb6..a4c2b068b 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -575,6 +575,9 @@ MODULE data_types end type out_type_bigAquifer ! ** end bigAquifer + ! *********************************************************************************************************** + ! Define classes used to simplify calls to the subrotuines in opSplittin + ! *********************************************************************************************************** ! ** stateFilter type, public :: in_type_stateFilter ! class for intent(in) arguments in stateFilter call integer(i4b) :: ixCoupling ! intent(in): index of coupling method (1,2) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 8a69e7b9c..ca7064996 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -90,7 +90,7 @@ module opSplittin_module USE data_types,only:& var_i, & ! data vector (i4b) var_d, & ! data vector (rkind) - var_flagVec, & ! data vector with variable length dimension (i4b) + var_flagVec, & ! data vector with variable length dimension (lgt) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (rkind) zLookup, & ! lookup tables @@ -143,6 +143,59 @@ module opSplittin_module real(rkind),parameter :: veryBig=1.e+20_rkind ! a very big number real(rkind),parameter :: dx = 1.e-8_rkind ! finite difference increment +! class definitions + +type, public :: split_select_type ! class for selecting operator splitting methods + ! opSplittin indices (in order) + integer(i4b) :: ixCoupling + integer(i4b) :: iStateTypeSplit + integer(i4b) :: ixStateThenDomain ! 1=state type split; 2=domain split within a given state type + integer(i4b) :: iDomainSplit + integer(i4b) :: ixSolution + integer(i4b) :: iStateSplit + ! variables for specifying the split + integer(i4b) :: nSubset ! number of selected state variables for a given split + type(var_flagVec) :: fluxMask ! integer mask defining model fluxes + logical(lgt),allocatable :: stateMask(:) ! mask defining desired state variables + ! flags for splitting method control + logical(lgt) :: stateTypeSplitting,stateThenDomain,domainSplit,solution,stateSplit + contains + procedure :: initialize_flags => split_select_initialize_flags ! initialize flags that control operations + procedure :: initialize_ixCoupling => split_select_initialize_ixCoupling ! initialize operator splitting indices + procedure :: initialize_iStateTypeSplit => split_select_initialize_iStateTypeSplit ! initialize operator splitting indices + procedure :: initialize_ixStateThenDomain => split_select_initialize_ixStateThenDomain ! initialize operator splitting indices + procedure :: initialize_iDomainSplit => split_select_initialize_iDomainSplit ! initialize operator splitting indices + procedure :: initialize_ixSolution => split_select_initialize_ixSolution ! initialize operator splitting indices + procedure :: initialize_iStateSplit => split_select_initialize_iStateSplit ! initialize operator splitting indices + + procedure :: get_stateMask => split_select_compute_stateMask ! compute stateMask and nSubset and load into class object + + procedure :: advance_ixCoupling => split_select_advance_ixCoupling ! advance coupling iterator + procedure :: advance_iStateTypeSplit => split_select_advance_iStateTypeSplit ! advance stateTypeSplitting iterator + procedure :: advance_ixStateThenDomain => split_select_advance_ixStateThenDomain ! advance stateThenDomain iterator + procedure :: advance_iDomainSplit => split_select_advance_iDomainSplit ! advance domainSplit iterator + procedure :: advance_ixSolution => split_select_advance_ixSolution ! advance solution iterator + procedure :: advance_iStateSplit => split_select_advance_iStateSplit ! advance stateSplit iterator + + procedure :: logic_exit_stateTypeSplitting => split_select_logic_exit_stateTypeSplitting ! get logical for branch + procedure :: logic_exit_stateThenDomain => split_select_logic_exit_stateThenDomain ! get logical for branch + procedure :: logic_exit_domainSplit => split_select_logic_exit_domainSplit ! get logical for branch + procedure :: logic_exit_solution => split_select_logic_exit_solution ! get logical for branch + procedure :: logic_exit_stateSplit => split_select_logic_exit_stateSplit ! get logical for branch + + procedure :: logic_initialize_stateTypeSplitting => split_select_logic_initialize_stateTypeSplitting ! get logical for branch + procedure :: logic_initialize_stateThenDomain => split_select_logic_initialize_stateThenDomain ! get logical for branch + procedure :: logic_initialize_domainSplit => split_select_logic_initialize_domainSplit ! get logical for branch + procedure :: logic_initialize_solution => split_select_logic_initialize_solution ! get logical for branch + procedure :: logic_initialize_stateSplit => split_select_logic_initialize_stateSplit ! get logical for branch + + procedure :: logic_finalize_stateTypeSplitting => split_select_logic_finalize_stateTypeSplitting ! get logical for branch + procedure :: logic_finalize_stateThenDomain => split_select_logic_finalize_stateThenDomain ! get logical for branch + procedure :: logic_finalize_domainSplit => split_select_logic_finalize_domainSplit ! get logical for branch + procedure :: logic_finalize_solution => split_select_logic_finalize_solution ! get logical for branch + procedure :: logic_finalize_stateSplit => split_select_logic_finalize_stateSplit ! get logical for branch +end type split_select_type + contains @@ -292,92 +345,229 @@ subroutine opSplittin(& real(rkind) :: mean_step_state ! mean step over the state (with or without domain splits) real(rkind) :: mean_step_solution ! mean step for a solution (scalar or vector) logical(lgt) :: addFirstFlux ! flag to add the first flux to the mask - ! loop control + ! splitting method control variables + logical(lgt) :: exit_split_select,cycle_split_select ! control for split_select loop logical(lgt) :: exit_coupling,exit_stateTypeSplitting,exit_stateThenDomain,exit_domainSplit,exit_solution,exit_stateSplit logical(lgt) :: cycle_coupling,cycle_stateTypeSplitting,cycle_stateThenDomain,cycle_domainSplit,cycle_solution,cycle_stateSplit + integer(i4b) :: iSplit,nSplit + integer(i4b),parameter :: maxSplit=500 ! >= max number of splitting methods (controls upper limit of split_select loop) ! ------------------------ classes for subroutine arguments (classes defined in data_types module) ------------------------ ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** type(in_type_stateFilter) :: in_stateFilter; type(out_type_stateFilter) :: out_stateFilter; ! stateFilter arguments type(in_type_indexSplit) :: in_indexSplit; type(out_type_indexSplit) :: out_indexSplit; ! indexSplit arguments type(in_type_varSubstep) :: in_varSubstep; type(io_type_varSubstep) :: io_varSubstep; type(out_type_varSubstep) :: out_varSubstep; ! varSubstep arguments - ! --------------------------------------------------------------------------------------- - - call initialize_coupling; if (return_flag.eqv..true.) return ! select coupling options and allocate memory - return if error occurs - coupling: do ixCoupling=1,nCoupling ! loop through different coupling strategies - - call initialize_stateTypeSplitting; if (return_flag.eqv..true.) return ! setup steps for stateTypeSplitting loop - return if error occurs - stateTypeSplitting: do iStateTypeSplit=1,nStateTypeSplit ! state splitting loop - - ! first try the state type split, then try the domain split within a given state type - call initialize_stateThenDomain ! setup steps for stateThenDomain loop -- identify state-specific variables for a given state split - stateThenDomain: do ixStateThenDomain=1,1+tryDomainSplit ! 1=state type split; 2=domain split within a given state type - - call initialize_domainSplit; if (return_flag.eqv..true.) return ! setup steps for domainSplit loop - return if error occurs - domainSplit: do iDomainSplit=1,nDomainSplit ! domain splitting loop - - solution: do ixSolution=1,nSolutions ! trial with the vector then scalar solution - - call initialize_stateSplit; if (return_flag.eqv..true.) return ! setup steps for stateSplit loop - return if error occurs - stateSplit: do iStateSplit=1,nStateSplit ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) + ! ------------------------------------------------------------------------------------------------------------------------- + type(split_select_type) :: split_select ! class object for selecting operator splitting methods + + ! *** Initialize Split Selector Object *** + call initialize_split_select + call initialize_split_coupling; if (return_flag) return + split_select_loop: do iSplit=1,maxSplit ! coupling begins + call initialize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop; if (return_flag) return + if (split_select % stateTypeSplitting) then ! stateTypeSplitting begins + call initialize_split_stateThenDomain + if (split_select % stateThenDomain) then ! stateThenDomain begins + call initialize_split_domainSplit; if (return_flag) return + if (split_select % domainSplit) then ! domainSplit begins + call initialize_split_solution + if (split_select % solution) then ! solution begins + call initialize_split_stateSplit; if (return_flag) return + if (split_select % stateSplit) then ! stateSplit begins + call update_stateMask; if (return_flag) return ! get the mask for the state subset - return for a non-zero error code + call validate_split ! verify that the split is valid + if (cycle_domainSplit) cycle split_select_loop ! if needed, proceed to next iteration of domainSplit method + if (cycle_solution) cycle split_select_loop ! if needed, proceed to next iteration of solution method + if (return_flag) return ! return for a non-zero error code + + call save_recover ! save/recover copies of variables and fluxes + + call get_split_indices; if (return_flag) return ! get indices for a given split - return for a non-zero error code + call update_fluxMask; if (return_flag) return ! define the mask for the fluxes used - return for a non-zero error code + + call solve_subset; if (return_flag) return ! solve variable subset for one time step - return for a positive error code + call assess_solution; if (return_flag) return ! is solution a success or failure? - return for a recovering solution + + call try_other_solution_methods ! if solution failed to converge, try other splitting methods + if (cycle_coupling) cycle split_select_loop ! if needed, proceed to next iteration of coupling method + if (cycle_stateThenDomain) cycle split_select_loop ! if needed, proceed to next iteration of stateThenDomain method + if (cycle_solution) cycle split_select_loop ! if needed, proceed to next iteration of solution method + + call confirm_variable_updates; if (return_flag) return ! check that state variables are updated - return if error + + call success_check ! check for success + call check_exit_stateThenDomain ! check exit criterion for stateThenDomain split + call check_exit_solution; if (return_flag) return ! check exit criterion for solution split - return if error + end if ! stateSplit ends + call finalize_split_stateSplit + end if ! solution ends + call finalize_split_solution + end if ! domainSplit ends + call finalize_split_domainSplit + end if ! stateThenDomain ends + call finalize_split_stateThenDomain; if (return_flag) return + end if ! stateTypeSplitting ends + call finalize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop + end do split_select_loop ! coupling ends + call finalize_split_coupling; if (return_flag) return - ! define state subsets for a given split... - call update_stateFilter; if (return_flag.eqv..true.) return ! get the mask for the state subset - return for a non-zero error code - call validate_split ! verify that the split is valid - if (cycle_domainSplit) cycle domainSplit - if (cycle_solution) cycle solution - if (return_flag.eqv..true.) return ! return for a non-zero error code - call save_recover ! save/recover copies of variables and fluxes - - ! assemble vectors for a given split... - call get_split_indices; if (return_flag.eqv..true.) return ! get indices for a given split - return for a non-zero error code - call update_fluxMask; if (return_flag.eqv..true.) return ! define the mask of the fluxes used - return for a non-zero error code - - call solve_subset; if (return_flag.eqv..true.) return ! solve variable subset for one time step - return for a positive error code - - call assess_solution; if (return_flag.eqv..true.) return ! is solution a success or failure? - return for a recovering solution + contains - call try_other_solution_methods ! if solution failed to converge, try other splitting methods - if (cycle_coupling) cycle coupling ! exit loops if necessary - if (cycle_stateThenDomain) cycle stateThenDomain - if (cycle_solution) cycle solution - call confirm_variable_updates; if (return_flag.eqv..true.) return ! check that state variables updated - return if error + subroutine initialize_split_select + ! *** Initialize split_select class object *** - call success_check ! check for success - if (exit_stateThenDomain) exit stateThenDomain ! exit loops if necessary - if (exit_solution) exit solution - if (return_flag.eqv..true.) return ! return if error + ! allocate data components + allocate(split_select % stateMask(1:nState)) ! allocate split_select components - end do stateSplit ! solution with split layers + ! initialize flags + return_flag=.false. + exit_split_select=.false. + cycle_split_select=.false. + call split_select % initialize_flags ! initialize control flags + end subroutine initialize_split_select + + subroutine initialize_split_coupling + ! *** Initialize coupling split method *** + call split_select % initialize_ixCoupling + call initialize_coupling; if (return_flag) return ! select coupling options and allocate memory - return if error occurs + end subroutine initialize_split_coupling + + subroutine initialize_split_stateTypeSplitting + ! *** Initialize stateTypeSplitting split method *** + if (split_select % logic_initialize_stateTypeSplitting()) then + ixCoupling=split_select % ixCoupling + if (ixCoupling.gt.nCoupling) then; exit_split_select=.true.; return; end if ! exit if all splits are exhausted + call initialize_stateTypeSplitting; if (return_flag) return ! setup steps for stateTypeSplitting split method - return if error occurs + call split_select % initialize_iStateTypeSplit; split_select % stateTypeSplitting=.true. + end if + if (split_select % logic_exit_stateTypeSplitting()) then + iStateTypeSplit=split_select % iStateTypeSplit; if (iStateTypeSplit.gt.nStateTypeSplit) split_select % stateTypeSplitting=.false. + end if + end subroutine initialize_split_stateTypeSplitting + + subroutine initialize_split_stateThenDomain + ! *** Initialize stateThenDomain split method *** + if (split_select % logic_initialize_stateThenDomain()) then + ! first try the state type split, then try the domain split within a given state type + call initialize_stateThenDomain ! setup steps for stateThenDomain split method -- identify state-specific variables for a given state split + call split_select % initialize_ixStateThenDomain; split_select % stateThenDomain=.true. + end if + if (split_select % logic_exit_stateThenDomain()) then ! stateThenDomain + ixStateThenDomain=split_select % ixStateThenDomain + if (ixStateThenDomain > (1+tryDomainSplit)) then + ixStateThenDomain=ixStateThenDomain-1; split_select % ixStateThenDomain = ixStateThenDomain ! correct index needed after exit + split_select % stateThenDomain=.false. ! eqivalent to exiting the stateThenDomain method + end if + end if + end subroutine initialize_split_stateThenDomain - end do solution ! trial with the full layer solution then the split layer solution - call finalize_solution ! final steps following solution loop + subroutine initialize_split_domainSplit + ! *** Initialize domainSplit split method *** + if (split_select % logic_initialize_domainSplit()) then + call initialize_domainSplit; if (return_flag) return ! setup steps for domainSplit split method - return if error occurs + call split_select % initialize_iDomainSplit; split_select % domainSplit=.true. + end if + if (split_select % logic_exit_domainSplit()) then + iDomainSplit=split_select % iDomainSplit + if (split_select % iDomainSplit > nDomainSplit) split_select % domainSplit=.false. + end if + end subroutine initialize_split_domainSplit + + subroutine initialize_split_solution + ! *** Initialize solution split method *** + if (split_select % logic_initialize_solution()) then; call split_select % initialize_ixSolution; split_select % solution=.true.; end if + if (split_select % logic_exit_solution()) then + ixSolution=split_select % ixSolution + if (split_select % ixSolution > nsolutions) split_select % solution=.false. + end if + end subroutine initialize_split_solution - end do domainSplit ! domain type splitting loop + subroutine initialize_split_stateSplit + ! *** Initialize stateSplit split method *** + if (split_select % logic_initialize_stateSplit()) then + call initialize_stateSplit; if (return_flag) return ! setup steps for stateSplit split method - return if error occurs + call split_select % initialize_iStateSplit; split_select % stateSplit=.true.; ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) + end if + if (split_select % logic_exit_stateSplit()) then ! stateSplit begins + iStateSplit=split_select % iStateSplit + if (split_select % iStateSplit > nStateSplit) split_select % stateSplit=.false.; !exit stateSplit + end if + end subroutine initialize_split_stateSplit + + subroutine check_exit_stateThenDomain + ! *** check exit criterion for stateThenDomain split *** + if (exit_stateThenDomain) then ! exit stateThenDomain split if necessary -- deactivate flags for inner splits + call split_select % initialize_ixStateThenDomain + split_select % stateThenDomain=.false.; split_select % domainSplit=.false.; split_select % solution=.false.; split_select % stateSplit=.false. + end if + end subroutine check_exit_stateThenDomain + + subroutine check_exit_solution + ! *** Check exit criterion for solution split - return if needed *** + if (split_select % stateThenDomain) then + if (exit_solution) then; split_select % solution=.false.; split_select % stateSplit=.false.; end if + if (split_select % solution) then + if (return_flag) return ! return if error + call split_select % advance_iStateSplit + end if + end if + end subroutine check_exit_solution - end do stateThenDomain ! switch between the state type and domain type splitting - call finalize_stateThenDomain ! final steps following the stateThenDomain loop + subroutine finalize_split_stateSplit + ! *** Finalize steps for stateSplit split method *** + if (split_select % logic_finalize_stateSplit()) then + call split_select % advance_ixSolution + end if + end subroutine finalize_split_stateSplit + + subroutine finalize_split_solution + ! *** Finalize steps for solution split method *** + if (split_select % logic_finalize_solution()) then + call finalize_solution ! final steps following solution split method + call split_select % advance_iDomainSplit + end if + end subroutine finalize_split_solution - end do stateTypeSplitting ! state type splitting loop - call finalize_stateTypeSplitting; if (exit_coupling) exit coupling ! success = exit the coupling loop + subroutine finalize_split_domainSplit + ! *** Finalize steps for domainSplit split method *** + if (split_select % logic_finalize_domainSplit()) then + call split_select % advance_ixStateThenDomain + end if + end subroutine finalize_split_domainSplit - end do coupling ! loop over coupling methods - call finalize_coupling ! check variables and fluxes, and apply step halving if needed + subroutine finalize_split_stateThenDomain + ! *** Finalize steps for stateThenDomain split method *** + if (split_select % logic_finalize_stateThenDomain()) then + call finalize_stateThenDomain; if (return_flag) return ! final steps following the stateThenDomain split method + call split_select % advance_iStateTypeSplit + end if + end subroutine finalize_split_stateThenDomain + + subroutine finalize_split_stateTypeSplitting + ! *** Finalize steps for stateTypeSplitting split method *** + if (split_select % logic_finalize_stateTypeSplitting()) then + call finalize_stateTypeSplitting + if (exit_coupling) then + call split_select % initialize_ixCoupling; exit_split_select=.true.; return ! success = exit the coupling split method (split_select_loop) + end if + call split_select % advance_ixCoupling + end if + end subroutine finalize_split_stateTypeSplitting - contains + subroutine finalize_split_coupling + ! *** Finalize steps for coupling split method *** + if (iSplit.gt.maxSplit) then ! check for errors + err=20; message=trim(message)//'split_select loop exceeded max number of iterations'; return_flag=.true.; return + end if + call finalize_coupling; if (return_flag) return ! check variables and fluxes, and apply step halving if needed + end subroutine finalize_split_coupling subroutine initialize_coupling - ! *** initial steps for coupling loop *** + ! *** initial steps for coupling split method *** ! initialize error control err=0; message="opSplittin/" - associate(ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision) ! intent(in): [i4b] choice of numerical solver - ! we just solve the fully coupled problem if IDA for now, splitting can happen otherwise - select case(ixNumericalMethod) - case(ida); nCoupling = 1 - case(kinsol, numrec); nCoupling = 2 - end select - end associate + call get_nCoupling; if (return_flag) return ! get nCoupling value -- return if error ! set the global print flag globalPrintFlag=.false. @@ -403,7 +593,7 @@ subroutine initialize_coupling ! allocate local structures based on the number of snow and soil layers call allocate_memory - if (return_flag.eqv..true.) return ! return if an error occurs during memory allocation + if (return_flag) return ! return if an error occurs during memory allocation ! intialize the flux counter do iVar=1,size(flux_meta) ! loop through fluxes @@ -424,6 +614,18 @@ subroutine initialize_coupling end do end subroutine initialize_coupling + subroutine get_nCoupling + ! *** Get nCoupling value *** + associate(ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision) ! intent(in): [i4b] choice of numerical solver + ! we just solve the fully coupled problem if IDA for now, splitting can happen otherwise + select case(ixNumericalMethod) + case(ida); nCoupling = 1 + case(kinsol, numrec); nCoupling = 2 + case default; err=20; message=trim(message)//'solver choice not found'; return_flag=.true.; return + end select + end associate + end subroutine get_nCoupling + subroutine allocate_memory ! *** allocate memory for local structures *** return_flag=.false. ! initialize flag @@ -462,11 +664,11 @@ subroutine allocate_memory end subroutine allocate_memory subroutine finalize_coupling - ! *** final operations for coupling loop *** + ! *** final operations for coupling split method *** ! check that all state variables were updated if (any(stateCheck==0)) then message=trim(message)//'some state variables were not updated!' - err=20; return + err=20; return_flag=.true.; return endif ! check that the desired fluxes were computed @@ -474,7 +676,7 @@ subroutine finalize_coupling if (neededFlux(iVar) .and. any(fluxCount%var(iVar)%dat==0)) then print*, 'fluxCount%var(iVar)%dat = ', fluxCount%var(iVar)%dat message=trim(message)//'flux '//trim(flux_meta(iVar)%varname)//' was not computed' - err=20; return + err=20; return_flag=.true.; return end if end do @@ -483,39 +685,46 @@ subroutine finalize_coupling end subroutine finalize_coupling subroutine initialize_stateTypeSplitting - ! *** Initial steps to prepare for iterations of the stateTypeSplit loop *** + ! *** Initial steps to prepare for iterations of the stateTypeSplit split method *** return_flag=.false. ! initialize flag ! initialize the time step dtInit = min(merge(dt, dtmin_coupled, ixCoupling==fullyCoupled), dt) ! initial time step dt_min = min(merge(dtmin_coupled, dtmin_split, ixCoupling==fullyCoupled), dt) ! minimum time step + ! get nStateTypeSplit and tryDomainSplit values + call get_nStateTypeSplit_tryDomainSplit(ixCoupling); if (return_flag) return + + mean_step_dt = 0._rkind ! initialize mean step for the time step + addFirstFlux = .true. ! flag to add the first flux to the mask + end subroutine initialize_stateTypeSplitting + + subroutine get_nStateTypeSplit_tryDomainSplit(ixCoupling_value) + ! *** Get nStateTypeSplit and tryDomainSplit values *** + integer(i4b),intent(in) :: ixCoupling_value ! keep track of the number of state splits associate(numberStateSplit => indx_data%var(iLookINDEX%numberStateSplit)%dat(1)) ! intent(inout): [i4b] number of state splitting solutions if (ixCoupling/=fullyCoupled) numberStateSplit = numberStateSplit + 1 end associate ! define the number of operator splits for the state type - select case(ixCoupling) + select case(ixCoupling_value) case(fullyCoupled); nStateTypeSplit=1 case(stateTypeSplit); nStateTypeSplit=nStateTypes case default; err=20; message=trim(message)//'coupling case not found'; return_flag=.true.; return end select ! operator splitting option ! define if we wish to try the domain split - select case(ixCoupling) + select case(ixCoupling_value) case(fullyCoupled); tryDomainSplit=0 case(stateTypeSplit); tryDomainSplit=1 case default; err=20; message=trim(message)//'coupling case not found'; return_flag=.true.; return end select ! operator splitting option - - mean_step_dt = 0._rkind ! initialize mean step for the time step - addFirstFlux = .true. ! flag to add the first flux to the mask - end subroutine initialize_stateTypeSplitting + end subroutine get_nStateTypeSplit_tryDomainSplit subroutine finalize_stateTypeSplitting - ! *** Final operations subsequent to the stateTypeSplitting loop *** - exit_coupling=.false. ! initialize flag for loop control - if (ixCoupling==fullyCoupled .and. .not.failure) then; exit_coupling=.true.; return; end if ! success = exit the coupling loop in opSplittin + ! *** Final operations subsequent to the stateTypeSplitting split method *** + exit_coupling=.false. ! initialize flag for control + if (ixCoupling==fullyCoupled .and. .not.failure) then; exit_coupling=.true.; return; end if ! success = exit the coupling method in opSplittin end subroutine finalize_stateTypeSplitting subroutine initialize_stateThenDomain @@ -538,11 +747,13 @@ subroutine initialize_stateThenDomain end subroutine initialize_stateThenDomain subroutine finalize_stateThenDomain - ! *** Final steps following the stateThenDomain loop *** + ! *** Final steps following the stateThenDomain split method *** ! sum the mean steps for the time step over each state type split + !if (ixStateThenDomain == 2+tryDomainSplit) ixStateThenDomain=1+tryDomainSplit ! correct index value if stateThenDomain method is completed fully select case(ixStateThenDomain) case(fullDomain); mean_step_dt = mean_step_dt + mean_step_solution/nStateTypeSplit case(subDomain); mean_step_dt = mean_step_dt + mean_step_state/nStateTypeSplit + case default; err=20; message=trim(message)//'ixStateThenDomain case not found'; return_flag=.true.; return end select associate(& ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat, & ! intent(in): [i4b(:)] indices defining the type of the state @@ -562,7 +773,7 @@ subroutine finalize_stateThenDomain end subroutine finalize_stateThenDomain subroutine initialize_domainSplit - ! *** initial operations to set up domainSplit loop *** + ! *** initial operations to set up domainSplit split method *** return_flag=.false. ! initialize flag associate(numberDomainSplitNrg => indx_data%var(iLookINDEX%numberDomainSplitNrg )%dat(1),& ! intent(inout): [i4b] number of domain splitting solutions for energy (-) numberDomainSplitMass => indx_data%var(iLookINDEX%numberDomainSplitMass)%dat(1) )! intent(inout): [i4b] number of domain splitting solutions for mass (-) @@ -571,14 +782,7 @@ subroutine initialize_domainSplit if (iStateTypeSplit==massSplit .and. ixStateThenDomain==subDomain) numberDomainSplitMass = numberDomainSplitMass + 1 end associate - ! define the number of domain splits for the state type - select case(ixStateThenDomain) - case(fullDomain); nDomainSplit=1 - case(subDomain); nDomainSplit=nDomains - case default; err=20; message=trim(message)//'coupling case not found'; - return_flag=.true. ! return statement required in opSplittin - return - end select + call get_nDomainSplit(ixStateThenDomain); if (return_flag) return ! get nDomainSplit value -- return if error occurs ! check that we haven't split the domain when we are fully coupled if (ixCoupling==fullyCoupled .and. nDomainSplit==nDomains) then @@ -590,14 +794,27 @@ subroutine initialize_domainSplit mean_step_state = 0._rkind ! initialize mean step for state end subroutine initialize_domainSplit + subroutine get_nDomainSplit(ixStateThenDomain_value) + ! *** Get nDomainSplit value *** + integer(i4b),intent(in) :: ixStateThenDomain_value + ! define the number of domain splits for the state type + select case(ixStateThenDomain_value) + case(fullDomain); nDomainSplit=1 + case(subDomain); nDomainSplit=nDomains + case default; err=20; message=trim(message)//'coupling case not found'; + return_flag=.true. ! return statement required in opSplittin + return + end select + end subroutine get_nDomainSplit + subroutine finalize_solution - ! *** final operations following solution loop *** + ! *** final operations following solution split method *** ! sum the mean steps for the state over each domain split mean_step_state = mean_step_state + mean_step_solution/nDomainSplit end subroutine finalize_solution subroutine initialize_stateSplit - ! *** initial operations to set up stateSplit loop *** + ! *** initial operations to set up stateSplit split method *** return_flag=.false. ! initialize flag mean_step_solution = 0._rkind ! initialize mean step for a solution @@ -614,15 +831,21 @@ subroutine initialize_stateSplit firstFluxCall=.true. if (.not.firstInnerStep) firstFluxCall=.false. + call get_nStateSplit(ixSolution); if (return_flag) return ! get nStateSplit value -- return if error occurs + end subroutine initialize_stateSplit + + subroutine get_nStateSplit(ixSolution_value) + ! *** Get nStateSplit value *** + integer(i4b),intent(in) :: ixSolution_value ! get the number of split layers - select case(ixSolution) + select case(ixSolution_value) case(vector); nStateSplit=1 case(scalar); nStateSplit=count(stateMask) case default; err=20; message=trim(message)//'unknown solution method'; return_flag=.true. ! return statement required in opSplittin return end select - end subroutine initialize_stateSplit + end subroutine get_nStateSplit ! **** stateFilter **** subroutine initialize_stateFilter @@ -717,26 +940,36 @@ subroutine try_other_solution_methods cycle_solution=.false. ! try the fully split solution if failed to converge with a minimum time step in the coupled solution - if (ixCoupling==fullyCoupled .and. failure) then; cycle_coupling=.true.; return; end if! return required to execute cycle statement in opSplittin + if (ixCoupling==fullyCoupled .and. failure) then + call split_select % advance_ixCoupling; call split_select % initialize_flags; ! prep for next iteration + cycle_coupling=.true.; return; ! return required to execute cycle statement in opSplittin + end if ! try the scalar solution if failed to converge with a minimum time step in the split solution if (ixCoupling/=fullyCoupled) then select case(ixStateThenDomain) - case(fullDomain); if (failure) cycle_stateThenDomain=.true.; return ! return required to execute cycle statement in opSplittin - case(subDomain); if (failure) cycle_solution=.true.; return + case(fullDomain) + if (failure) then + call split_select % advance_ixStateThenDomain ! prep for next iteration + split_select % domainSplit=.false.; split_select % solution=.false.; split_select % stateSplit=.false.; + cycle_stateThenDomain=.true.; return ! return required to execute cycle statement in opSplittin + end if + case(subDomain) + if (failure) then + call split_select % advance_ixSolution; split_select % stateSplit=.false.; ! prep for next iteration + cycle_solution=.true.; return ! return required to execute cycle statement in opSplittin + end if case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' end select end if end subroutine try_other_solution_methods - subroutine update_stateFilter + subroutine update_stateMask ! *** Get the mask for the state subset *** - return_flag=.false. ! initialize flag - call initialize_stateFilter - call stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) - call finalize_stateFilter - if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! error control - end subroutine update_stateFilter + call split_select % get_stateMask(indx_data,err,cmessage,message,return_flag) + nSubset = split_select % nSubset; stateMask = split_select % stateMask + if (return_flag) return + end subroutine update_stateMask subroutine validate_split ! *** Verify that the split is valid *** @@ -744,11 +977,22 @@ subroutine validate_split cycle_domainSplit=.false. cycle_solution=.false. return_flag=.false. + ! check that state variables exist - if (nSubset==0) then; cycle_domainSplit=.true.; return; end if + if (nSubset==0) then + call split_select % advance_iDomainSplit + split_select % solution=.false.; split_select % stateSplit=.false. + cycle_domainSplit=.true. + return + end if ! avoid redundant case where vector solution is of length 1 - if (ixSolution==vector .and. count(stateMask)==1) then; cycle_solution=.true.; return; end if + if (ixSolution==vector .and. count(stateMask)==1) then + call split_select % advance_ixSolution; + split_select % stateSplit=.false.; + cycle_solution=.true. + return + end if ! check that we do not attempt the scalar solution for the fully coupled case if (ixCoupling==fullyCoupled .and. ixSolution==scalar) then @@ -955,150 +1199,466 @@ end subroutine update_fluxMask end subroutine opSplittin +! ****** Class procedures for split_select_type class ****** + +subroutine split_select_initialize_flags(split_select) + ! *** Initialize flags for opSplittin split methods *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % stateTypeSplitting=.false. + split_select % stateThenDomain=.false. + split_select % domainSplit=.false. + split_select % solution=.false. + split_select % stateSplit=.false. +end subroutine split_select_initialize_flags + +subroutine split_select_advance_ixCoupling(split_select) + ! *** Advance index for coupling split method *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % ixCoupling = split_select % ixCoupling + 1 +end subroutine split_select_advance_ixCoupling + +subroutine split_select_advance_iStateTypeSplit(split_select) + ! *** Advance index for stateTypeSplit split method *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % iStateTypeSplit = split_select % iStateTypeSplit + 1 +end subroutine split_select_advance_iStateTypeSplit + +subroutine split_select_advance_ixStateThenDomain(split_select) + ! *** Advance index for stateThenDomain split method *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % ixStateThenDomain = split_select % ixStateThenDomain + 1 +end subroutine split_select_advance_ixStateThenDomain + +subroutine split_select_advance_iDomainSplit(split_select) + ! *** Advance index for domainSplit split method *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % iDomainSplit = split_select % iDomainSplit + 1 +end subroutine split_select_advance_iDomainSplit + +subroutine split_select_advance_ixSolution(split_select) + ! *** Advance index for solution split method *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % ixSolution = split_select % ixSolution + 1 +end subroutine split_select_advance_ixSolution + +subroutine split_select_advance_iStateSplit(split_select) + ! *** Advance index for stateSplit split method *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % iStateSplit = split_select % iStateSplit + 1 +end subroutine split_select_advance_iStateSplit + +subroutine split_select_initialize_ixCoupling(split_select) + ! *** initialize operator splitting indices for split_select_type class *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % ixCoupling = 1 +end subroutine split_select_initialize_ixCoupling + +subroutine split_select_initialize_iStateTypeSplit(split_select) + ! *** initialize operator splitting indices for split_select_type class *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % iStateTypeSplit = 1 +end subroutine split_select_initialize_iStateTypeSplit + +subroutine split_select_initialize_ixStateThenDomain(split_select) + ! *** initialize operator splitting indices for split_select_type class *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % ixStateThenDomain = 1 +end subroutine split_select_initialize_ixStateThenDomain + +subroutine split_select_initialize_iDomainSplit(split_select) + ! *** initialize operator splitting indices for split_select_type class *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % iDomainSplit = 1 +end subroutine split_select_initialize_iDomainSplit + +subroutine split_select_initialize_ixSolution(split_select) + ! *** initialize operator splitting indices for split_select_type class *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % ixSolution = 1 +end subroutine split_select_initialize_ixSolution + +subroutine split_select_initialize_iStateSplit(split_select) + ! *** initialize operator splitting indices for split_select_type class *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % iStateSplit = 1 +end subroutine split_select_initialize_iStateSplit + +logical(lgt) function split_select_logic_initialize_stateTypeSplitting(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_initialize_stateTypeSplitting=& + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.).and.(split_select % domainSplit.eqv..false.).and.(split_select % stateThenDomain.eqv..false.).and.(split_select % stateTypeSplitting.eqv..false.) +end function split_select_logic_initialize_stateTypeSplitting + +logical(lgt) function split_select_logic_exit_stateTypeSplitting(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_exit_stateTypeSplitting=& + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.).and.(split_select % domainSplit.eqv..false.).and.(split_select % stateThenDomain.eqv..false.).and.(split_select % stateTypeSplitting.eqv..true.) +end function split_select_logic_exit_stateTypeSplitting + +logical(lgt) function split_select_logic_initialize_stateThenDomain(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_initialize_stateThenDomain=& + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.).and.(split_select % domainSplit.eqv..false.).and.(split_select % stateThenDomain.eqv..false.) +end function split_select_logic_initialize_stateThenDomain + +logical(lgt) function split_select_logic_exit_stateThenDomain(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_exit_stateThenDomain=& + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.).and.(split_select % domainSplit.eqv..false.).and.(split_select % stateThenDomain.eqv..true.) +end function split_select_logic_exit_stateThenDomain + +logical(lgt) function split_select_logic_initialize_domainSplit(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_initialize_domainSplit=& + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.).and.(split_select % domainSplit.eqv..false.) +end function split_select_logic_initialize_domainSplit + +logical(lgt) function split_select_logic_exit_domainSplit(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_exit_domainSplit=& + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.).and.(split_select % domainSplit.eqv..true.) +end function split_select_logic_exit_domainSplit + +logical(lgt) function split_select_logic_initialize_solution(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_initialize_solution=(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.) +end function split_select_logic_initialize_solution + +logical(lgt) function split_select_logic_exit_solution(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_exit_solution=(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..true.) +end function split_select_logic_exit_solution + +logical(lgt) function split_select_logic_initialize_stateSplit(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_initialize_stateSplit=(split_select % stateSplit.eqv..false.) +end function split_select_logic_initialize_stateSplit + +logical(lgt) function split_select_logic_exit_stateSplit(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_exit_stateSplit=(split_select % stateSplit.eqv..true.) +end function split_select_logic_exit_stateSplit + +logical(lgt) function split_select_logic_finalize_stateSplit(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_finalize_stateSplit=& + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..true.).and.(split_select % stateThenDomain.eqv..true.) +end function split_select_logic_finalize_stateSplit + +logical(lgt) function split_select_logic_finalize_solution(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_finalize_solution=& + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.).and.(split_select % stateThenDomain.eqv..true.) +end function split_select_logic_finalize_solution + +logical(lgt) function split_select_logic_finalize_domainSplit(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_finalize_domainSplit=& + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.).and.(split_select % domainSplit.eqv..false.).and.(split_select % stateThenDomain.eqv..true.) +end function split_select_logic_finalize_domainSplit + +logical(lgt) function split_select_logic_finalize_stateThenDomain(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_finalize_stateThenDomain=& + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.).and.(split_select % domainSplit.eqv..false.).and.(split_select % stateThenDomain.eqv..false.) +end function split_select_logic_finalize_stateThenDomain + +logical(lgt) function split_select_logic_finalize_stateTypeSplitting(split_select) + ! *** Compute logical for branch in split_select loop *** + class(split_select_type),intent(in) :: split_select ! class object for operator splitting selector + split_select_logic_finalize_stateTypeSplitting=& + &(split_select % stateSplit.eqv..false.).and.(split_select % solution.eqv..false.).and.(split_select % domainSplit.eqv..false.).and.(split_select % stateThenDomain.eqv..false.).and.(split_select % stateTypeSplitting.eqv..false.) +end function split_select_logic_finalize_stateTypeSplitting + +subroutine split_select_compute_stateMask(split_select,indx_data,err,cmessage,message,return_flag) + ! *** Get the mask for the state subset *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + type(var_ilength),intent(in) :: indx_data ! indices for a local HRU + integer(i4b),intent(out) :: err ! intent(out): error code + character(*),intent(out) :: cmessage ! intent(out): error message + character(*),intent(out) :: message ! error message + logical(lgt),intent(out) :: return_flag + ! local variables + type(in_type_stateFilter) :: in_stateFilter ! indices + type(out_type_stateFilter) :: out_stateFilter ! number of selected state variables for a given split and error control + + return_flag=.false. ! initialize flag + associate(& + ixCoupling => split_select % ixCoupling ,& + ixSolution => split_select % ixSolution ,& + ixStateThenDomain => split_select % ixStateThenDomain ,& + iStateTypeSplit => split_select % iStateTypeSplit ,& + iDomainSplit => split_select % iDomainSplit ,& + iStateSplit => split_select % iStateSplit ) + call in_stateFilter % initialize(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) + end associate + associate(stateMask => split_select % stateMask) + call stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) + end associate + associate(nSubset => split_select % nSubset) + call out_stateFilter % finalize(nSubset,err,cmessage) + end associate + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! error control +end subroutine split_select_compute_stateMask + ! ********************************************************************************************************** ! private subroutine stateFilter: get a mask for the desired state variables ! ********************************************************************************************************** subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) - USE indexState_module,only:indxSubset ! get state indices - implicit none - ! input - type(in_type_stateFilter),intent(in) :: in_stateFilter ! indices - type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU - ! output - logical(lgt),intent(out) :: stateMask(:) ! mask defining desired state variables - type(out_type_stateFilter),intent(out) :: out_stateFilter ! number of selected state variables for a given split and error control - ! local - integer(i4b),allocatable :: ixSubset(:) ! list of indices in the state subset - character(len=256) :: cmessage ! error message - ! -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ! data structures + USE indexState_module,only:indxSubset ! get state indices + implicit none + ! input + type(in_type_stateFilter),intent(in) :: in_stateFilter ! indices + type(var_ilength),intent(in) :: indx_data ! indices for a local HRU + ! output + logical(lgt),intent(out) :: stateMask(:) ! mask defining desired state variables + type(out_type_stateFilter),intent(out) :: out_stateFilter ! number of selected state variables for a given split and error control + ! local + integer(i4b),allocatable :: ixSubset(:) ! list of indices in the state subset + character(len=256) :: cmessage ! error message + logical(lgt) :: return_flag ! flag to indicate a return + ! ---------------------------------------------------------------------------------------------------------------------------------------------------- + ! data structures + associate(ixCoupling => in_stateFilter % ixCoupling,& ! intent(in): [i4b] index of coupling method (1,2) + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message + + err=0; message='stateFilter/'; return_flag=.false. ! initialize error control + + ! identify splitting option + select case(ixCoupling) + ! *** fully coupled *** + case(fullyCoupled); call fullyCoupled_stateMask ! get stateMask for fully coupled method + ! *** splitting by state type *** + case(stateTypeSplit) ! initial split by state type + call stateTypeSplit_stateMask; if (return_flag) return ! get stateMask for state split method -- return if error + ! check + case default; err=20; message=trim(message)//'unable to identify coupling method'; return + end select ! selecting solution method + end associate + + call identify_scalar_solutions; if (return_flag) return ! identify scalar solutions -- return if error occurs + + ! get the number of selected state variables + associate(nSubset => out_stateFilter % nSubset) ! intent(out): number of selected state variables for a given split + nSubset = count(stateMask) + end associate + +contains + + subroutine fullyCoupled_stateMask + ! *** Get fully coupled stateMask *** + stateMask(:) = .true. ! use all state variables + end subroutine fullyCoupled_stateMask + + subroutine stateTypeSplit_stateMask + ! *** Get state type split stateMask *** + return_flag=.false. ! initialize flag + ! switch between full domain and sub domains associate(& - ! indices for splitting methods - ixCoupling => in_stateFilter % ixCoupling ,& ! intent(in): [i4b] index of coupling method (1,2) - ixSolution => in_stateFilter % ixSolution ,& ! intent(in): [i4b] index of solution method (1,2) - ixStateThenDomain => in_stateFilter % ixStateThenDomain ,& ! intent(in): [i4b] switch between full domain and sub domains - iStateTypeSplit => in_stateFilter % iStateTypeSplit ,& ! intent(in): [i4b] index of the state type split - iDomainSplit => in_stateFilter % iDomainSplit ,& ! intent(in): [i4b] index of the domain split - iStateSplit => in_stateFilter % iStateSplit ,& ! intent(in): [i4b] index of the layer split - ! indices of model state variables - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) - ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain - ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain - ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain - ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain - ixWatAquifer => indx_data%var(iLookINDEX%ixWatAquifer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for water storage in the aquifer - ixAllState => indx_data%var(iLookINDEX%ixAllState)%dat ,& ! intent(in): [i4b(:)] list of indices for all model state variables (1,2,3,...nState) - ! number of layers - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers - ! output - nSubset => out_stateFilter % nSubset ,& ! intent(out): number of selected state variables for a given split - err => out_stateFilter % err ,& ! intent(out): error code - message => out_stateFilter % cmessage & ! intent(out): error message - ) ! data structures - ! -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='stateFilter/' - - ! identify splitting option - select case(ixCoupling) - - ! ----- - ! - fully coupled... - ! ------------------ - - ! use all state variables - case(fullyCoupled); stateMask(:) = .true. - - ! ----- - ! - splitting by state type... - ! ---------------------------- - - ! initial split by state type - case(stateTypeSplit) - - ! switch between full domain and sub domains - select case(ixStateThenDomain) - - ! split into energy and mass - case(fullDomain) - select case(iStateTypeSplit) - case(nrgSplit); stateMask = (ixStateType==iname_nrgCanair .or. ixStateType==iname_nrgCanopy .or. ixStateType==iname_nrgLayer) - case(massSplit); stateMask = (ixStateType==iname_liqCanopy .or. ixStateType==iname_liqLayer .or. ixStateType==iname_lmpLayer .or. ixStateType==iname_watAquifer) - case default; err=20; message=trim(message)//'unable to identify split based on state type'; return - end select + ixStateThenDomain => in_stateFilter % ixStateThenDomain ,& ! intent(in): [i4b] switch between full domain and sub domains + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message + select case(ixStateThenDomain) + ! split into energy and mass + case(fullDomain); call stateTypeSplit_fullDomain_stateMask; if (return_flag) return + ! split into vegetation, snow, and soil + case(subDomain); call stateTypeSplit_subDomain_stateMask; if (return_flag) return + ! check + case default + err=20; message=trim(message)//'unable to identify the switch between full domains and sub domains'; return_flag=.true.; return + end select + end associate + end subroutine stateTypeSplit_stateMask + + subroutine stateTypeSplit_fullDomain_stateMask + ! *** Get full domain stateMask *** + return_flag=.false. ! initialize flag + associate(iStateTypeSplit => in_stateFilter % iStateTypeSplit ,& ! intent(in): [i4b] index of the state type split + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message + select case(iStateTypeSplit) + case(nrgSplit); call stateTypeSplit_fullDomain_nrgSplit_stateMask + case(massSplit); call stateTypeSplit_fullDomain_massSplit_stateMask + case default; err=20; message=trim(message)//'unable to identify split based on state type'; return_flag=.true.; return + end select + end associate + end subroutine stateTypeSplit_fullDomain_stateMask + + subroutine stateTypeSplit_fullDomain_nrgSplit_stateMask + ! *** Get state type full domain energy split stateMask *** + associate(ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat) ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) + stateMask = (ixStateType==iname_nrgCanair .or. ixStateType==iname_nrgCanopy .or. ixStateType==iname_nrgLayer) + end associate + end subroutine stateTypeSplit_fullDomain_nrgSplit_stateMask + + subroutine stateTypeSplit_fullDomain_massSplit_stateMask + ! *** Get state type full domain mass split stateMask *** + associate(ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat) ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) + stateMask = (ixStateType==iname_liqCanopy .or. ixStateType==iname_liqLayer .or. ixStateType==iname_lmpLayer .or. ixStateType==iname_watAquifer) + end associate + end subroutine stateTypeSplit_fullDomain_massSplit_stateMask + + subroutine stateTypeSplit_subDomain_stateMask + ! *** Get subdomain stateMask *** + return_flag=.false. ! initialize flag + ! define state mask + associate(& + iStateTypeSplit => in_stateFilter % iStateTypeSplit ,& ! intent(in): [i4b] index of the state type split + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message + stateMask=.false. ! initialize state mask + select case(iStateTypeSplit) + ! define mask for energy + case(nrgSplit); call stateTypeSplit_subDomain_nrgSplit_stateMask; if (return_flag) return + ! define mask for water + case(massSplit); call stateTypeSplit_subDomain_massSplit_stateMask; if (return_flag) return + + ! check + case default; err=20; message=trim(message)//'unable to identify the state type'; return_flag=.true.; return + end select ! (split based on state type) + end associate + end subroutine stateTypeSplit_subDomain_stateMask + + subroutine stateTypeSplit_subDomain_nrgSplit_stateMask + ! *** Get subdomain energy split stateMask *** + return_flag=.false. ! initialize flag + associate(& + iDomainSplit => in_stateFilter % iDomainSplit ,& ! intent(in): [i4b] index of the domain split + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message + select case(iDomainSplit) + case(vegSplit); call stateTypeSplit_subDomain_nrgSplit_vegSplit_stateMask ! vegetation subdomain + case(snowSplit); call stateTypeSplit_subDomain_nrgSplit_snowSplit_stateMask ! snow subdomain + case(soilSplit); call stateTypeSplit_subDomain_nrgSplit_soilSplit_stateMask ! soil subdomain + case(aquiferSplit) ! do nothing: no energy state variable for the aquifer domain ! aquifer subdomain + case default; err=20; message=trim(message)//'unable to identify model sub-domain'; return_flag=.true.; return + end select + end associate + end subroutine stateTypeSplit_subDomain_nrgSplit_stateMask + + subroutine stateTypeSplit_subDomain_nrgSplit_vegSplit_stateMask + ! *** Get state type subdomain energy vegetation split *** + associate(& + ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain + ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + if (ixNrgCanair(1)/=integerMissing) stateMask(ixNrgCanair) = .true. ! energy of the canopy air space + if (ixNrgCanopy(1)/=integerMissing) stateMask(ixNrgCanopy) = .true. ! energy of the vegetation canopy + stateMask(ixNrgLayer(1)) = .true. ! energy of the upper-most layer in the snow+soil domain + end associate + end subroutine stateTypeSplit_subDomain_nrgSplit_vegSplit_stateMask - ! split into vegetation, snow, and soil - case(subDomain) - - ! define state mask - stateMask=.false. ! (initialize state mask) - select case(iStateTypeSplit) - - ! define mask for energy - case(nrgSplit) - select case(iDomainSplit) - case(vegSplit) - if(ixNrgCanair(1)/=integerMissing) stateMask(ixNrgCanair) = .true. ! energy of the canopy air space - if(ixNrgCanopy(1)/=integerMissing) stateMask(ixNrgCanopy) = .true. ! energy of the vegetation canopy - stateMask(ixNrgLayer(1)) = .true. ! energy of the upper-most layer in the snow+soil domain - case(snowSplit); if(nSnow>1) stateMask(ixNrgLayer(2:nSnow)) = .true. ! NOTE: (2:) because the top layer in the snow+soil domain included in vegSplit - case(soilSplit); stateMask(ixNrgLayer(max(2,nSnow+1):nLayers)) = .true. ! NOTE: max(2,nSnow+1) gives second layer unless more than 2 snow layers - case(aquiferSplit) ! do nothing: no energy state variable for the aquifer domain - case default; err=20; message=trim(message)//'unable to identify model sub-domain'; return - end select - - ! define mask for water - case(massSplit) - select case(iDomainSplit) - case(vegSplit); if(ixHydCanopy(1)/=integerMissing) stateMask(ixHydCanopy) = .true. ! hydrology of the vegetation canopy - case(snowSplit); stateMask(ixHydLayer(1:nSnow)) = .true. ! snow hydrology - case(soilSplit); stateMask(ixHydLayer(nSnow+1:nLayers)) = .true. ! soil hydrology - case(aquiferSplit); if(ixWatAquifer(1)/=integerMissing) stateMask(ixWatAquifer) = .true. ! aquifer storage - case default; err=20; message=trim(message)//'unable to identify model sub-domain'; return - end select - - ! check - case default; err=20; message=trim(message)//'unable to identify the state type'; return - end select ! (split based on state type) - - ! check - case default; err=20; message=trim(message)//'unable to identify the switch between full domains and sub domains'; return - end select ! (switch between full domains and sub domains) - - ! check - case default; err=20; message=trim(message)//'unable to identify coupling method'; return - end select ! (selecting solution method) - - ! identify scalar solutions - if(ixSolution==scalar)then - - ! get the subset of indices - call indxSubset(ixSubset, ixAllState, stateMask, err, cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! get the mask - stateMask(:) = .false. - stateMask( ixSubset(iStateSplit) ) = .true. - - ! check - if(count(stateMask)/=1)then - message=trim(message)//'expect size=1 (scalar)' - err=20; return - endif - - endif - - ! get the number of selected state variables - nSubset = count(stateMask) - - ! end associations + subroutine stateTypeSplit_subDomain_nrgSplit_snowSplit_stateMask + associate(& + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + ! *** Get state type subdomain energy snow split *** + if (nSnow>1) stateMask(ixNrgLayer(2:nSnow)) = .true. ! NOTE: (2:) because the top layer in the snow+soil domain included in vegSplit + end associate + end subroutine stateTypeSplit_subDomain_nrgSplit_snowSplit_stateMask + + subroutine stateTypeSplit_subDomain_nrgSplit_soilSplit_stateMask + ! *** Get state type subdomain energy soil split *** + associate(& + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + stateMask(ixNrgLayer(max(2,nSnow+1):nLayers)) = .true. ! NOTE: max(2,nSnow+1) gives second layer unless more than 2 snow layers + end associate + end subroutine stateTypeSplit_subDomain_nrgSplit_soilSplit_stateMask + + subroutine stateTypeSplit_subDomain_massSplit_stateMask + ! *** Get subdomain mass split stateMask *** + return_flag=.false. ! initialize flag + associate(& + iDomainSplit => in_stateFilter % iDomainSplit ,& ! intent(in): [i4b] index of the domain split + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message + select case(iDomainSplit) + case(vegSplit); call stateTypeSplit_subDomain_massSplit_vegSplit_stateMask ! vegetation subdomain + case(snowSplit); call stateTypeSplit_subDomain_massSplit_snowSplit_stateMask ! snow subdomain + case(soilSplit); call stateTypeSplit_subDomain_massSplit_soilSplit_stateMask ! soil subdomain + case(aquiferSplit); call stateTypeSplit_subDomain_massSplit_aquiferSplit_stateMask ! aquifer subdomain + case default; err=20; message=trim(message)//'unable to identify model sub-domain'; return_flag=.true.; return + end select end associate + end subroutine stateTypeSplit_subDomain_massSplit_stateMask + subroutine stateTypeSplit_subDomain_massSplit_vegSplit_stateMask + ! *** Get mass state vegetation subdomain split stateMask *** + associate(ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain + if (ixHydCanopy(1)/=integerMissing) stateMask(ixHydCanopy) = .true. ! hydrology of the vegetation canopy + end associate + end subroutine stateTypeSplit_subDomain_massSplit_vegSplit_stateMask + + subroutine stateTypeSplit_subDomain_massSplit_snowSplit_stateMask + ! *** Get mass state snow subdomain split stateMask *** + associate(& + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + stateMask(ixHydLayer(1:nSnow)) = .true. ! snow hydrology + end associate + end subroutine stateTypeSplit_subDomain_massSplit_snowSplit_stateMask + + subroutine stateTypeSplit_subDomain_massSplit_soilSplit_stateMask + ! *** Get mass state soil subdomain split stateMask *** + associate(& + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + stateMask(ixHydLayer(nSnow+1:nLayers)) = .true. ! soil hydrology + end associate + end subroutine stateTypeSplit_subDomain_massSplit_soilSplit_stateMask + + subroutine stateTypeSplit_subDomain_massSplit_aquiferSplit_stateMask + ! *** Get mass state aquifer subdomain split stateMask *** + associate(ixWatAquifer => indx_data%var(iLookINDEX%ixWatAquifer)%dat) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for water storage in the aquifer + if (ixWatAquifer(1)/=integerMissing) stateMask(ixWatAquifer) = .true. ! aquifer storage + end associate + end subroutine stateTypeSplit_subDomain_massSplit_aquiferSplit_stateMask + + subroutine identify_scalar_solutions + ! *** Identify scalar solutions *** + return_flag=.false. ! initialize flag + associate(ixAllState => indx_data%var(iLookINDEX%ixAllState)%dat ,& ! intent(in): [i4b(:)] list of indices for all model state variables (1,2,3,...nState) + ixSolution => in_stateFilter % ixSolution ,& ! intent(in): [i4b] index of solution method (1,2) + iStateSplit => in_stateFilter % iStateSplit ,& ! intent(in): [i4b] index of the layer split + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message + if (ixSolution==scalar) then + ! get the subset of indices + call indxSubset(ixSubset, ixAllState, stateMask, err, cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if + ! get the mask + stateMask(:) = .false. + stateMask( ixSubset(iStateSplit) ) = .true. + ! check + if (count(stateMask)/=1) then + message=trim(message)//'expect size=1 (scalar)' + err=20; return_flag=.true.; return + end if + end if + end associate + + end subroutine identify_scalar_solutions + end subroutine stateFilter end module opSplittin_module From 70cac605fdff6d96bcca63c70f7c898a04a5edbc Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 8 Feb 2024 23:19:21 -0600 Subject: [PATCH 1122/1472] add another decision for lookup table enthalpyFDlu and add a unit test in enthalpy soil --- build/source/driver/summa_setup.f90 | 16 +- build/source/engine/enthalpyTemp.f90 | 44 ++--- build/source/engine/eval8summa.f90 | 96 +++++----- build/source/engine/eval8summaWithPrime.f90 | 5 +- build/source/engine/mDecisions.f90 | 12 +- build/source/engine/systemSolv.f90 | 17 +- build/source/engine/varSubstep.f90 | 185 ++++++++++---------- 7 files changed, 190 insertions(+), 185 deletions(-) diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index 94ac48810..5b094dc79 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -45,7 +45,8 @@ module summa_setup ! look-up values for the choice of heat capacity computation USE mDecisions_module,only:& closedForm,& ! heat capacity closed form in backward Euler residual - enthalpyFD ! enthalpy finite difference in backward Euler residual + enthalpyFDlu,& ! enthalpy with lookup tables finite difference in backward Euler residual + enthalpyFD ! enthalpy with hypergeometric function finite difference in backward Euler residual ! named variables to define the decisions for snow layers USE mDecisions_module,only:& @@ -151,14 +152,7 @@ subroutine summa_paramSetup(summa1_struc, err, message) ! --------------------------------------------------------------------------------------- ! initialize error control err=0; message='summa_paramSetup/' - - ! decide if computing enthalpy lookup tables, if need enthalpy and not using hypergeometric function - ! NOTE: this should be replaced by a parameter of if want lookup table enthalpy, but for now it is hard-coded - ! needLookup=.true. if checkNrgBalance in varSubstep is turned on or using nrgConserv=enthalpyFD - ! AND use_lookup=.true. in t2enthalpy - needLookup = .false. - !if (model_decisions(iLookDECISIONS%nrgConserv)%iDecision == enthalpyFD) needLookup = .true. - + ! initialize the start of the initialization call date_and_time(values=startSetup) @@ -182,6 +176,10 @@ subroutine summa_paramSetup(summa1_struc, err, message) call mDecisions(err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + ! decide if computing enthalpy lookup tables, if need enthalpy and not using hypergeometric function + needLookup = .false. + if(model_decisions(iLookDECISIONS%nrgConserv)%iDecision == enthalpyFDlu) needLookup = .true. + ! get the maximum number of snow layers select case(model_decisions(iLookDECISIONS%snowLayers)%iDecision) case(sameRulesAllLayers); maxSnowLayers = 100 diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index af294d2f0..b323753da 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -158,15 +158,12 @@ subroutine T2L_lookup(nSoil, & ! intent(in): number character(*),intent(out) :: message ! error message ! declare local variables character(len=128) :: cmessage ! error message in downwind routine - logical(lgt),parameter :: doTest=.false. ! flag to test - integer(i4b),parameter :: nLook=100 ! number of elements in the lookup table + integer(i4b),parameter :: nLook=500 ! number of elements in the lookup table integer(i4b),parameter :: nIntegr8=10000 ! number of points used in the numerical integration real(rkind),parameter :: T_lower=260.0_rkind ! lowest temperature value where all liquid water is assumed frozen (K) real(rkind),dimension(nLook) :: xTemp ! temporary vector real(rkind) :: xIncr ! temporary increment real(rkind) :: T_incr ! temperature increment - real(rkind),parameter :: T_test=272.9742_rkind! test value for temperature (K) - real(rkind) :: L_test ! test value for integral of mLayerPsiLiq from Tfreeze to T_test (K) real(rkind) :: dL ! derivative of integral with temperature at T_test integer(i4b) :: iVar ! loop through variables integer(i4b) :: iSoil ! loop through soil layers @@ -277,26 +274,6 @@ subroutine T2L_lookup(nSoil, & ! intent(in): number call spline(Tk,Ly,1.e30_rkind,1.e30_rkind,L2,err,cmessage) ! get the second derivatives if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - ! test - if(doTest)then - - ! calculate enthalpy - call splint(Tk,Ly,L2,T_test,L_test,dL,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! write values - print*, 'doTest = ', doTest - print*, 'T_test = ', T_test ! temperature (K) - print*, 'L_test = ', L_test ! integral (K) - print*, 'theta_sat = ', theta_sat ! soil porosity (-) - print*, 'theta_res = ', theta_res ! volumetric residual water content (-) - print*, 'vGn_alpha = ', vGn_alpha ! van Genuchten "alpha" parameter (m-1) - print*, 'vGn_n = ', vGn_n ! van Genuchten "n" parameter (-) - print*, trim(message)//'PAUSE: Set doTest=.false. to complete simulations' - read(*,*) - - endif ! if testing - ! end asssociation to variables in the data structures end associate @@ -443,6 +420,7 @@ end function t2enthalpy_snow ! public subroutine t2enthalpy: compute temperature component of enthalpy from temperature and total water content ! ************************************************************************************************************************ subroutine t2enthalpy(& + use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure @@ -470,6 +448,7 @@ subroutine t2enthalpy(& implicit none ! delare dummy variables ! ------------------------------------------------------------------------------------------------------------------------- + logical(lgt),intent(in) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function ! input: data structures type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(in) :: mpar_data ! model parameters @@ -521,7 +500,7 @@ subroutine t2enthalpy(& real(rkind) :: enthAir ! enthalpy of air (J m-3) real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) real(rkind) :: enthWater ! enthalpy of total water (J m-3) - logical(lgt),parameter :: use_lookup=.false. ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function + logical(lgt),parameter :: doTest=.false. ! flag to run unit test ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures generalVars: associate(& @@ -682,6 +661,21 @@ subroutine t2enthalpy(& enthIce = iden_ice * Cp_ice * ( volFracWat * diffT - (integral_unf + integral_frz_upp - integral_frz_low) ) enthWater = enthIce + enthLiq + if(doTest)then + + ! write values + print*, 'doTest = ', doTest + print*, 'T,Tcrit = ', mlayerTempTrial(iLayer),Tcrit ! temperature (K) + print*, 'integral unf,frz_upp,frz_low = ', integral_unf, integral_frz_upp, integral_frz_low ! integral (K) + print*, 'theta_sat = ', theta_sat ! soil porosity (-) + print*, 'theta_res = ', theta_res ! volumetric residual water content (-) + print*, 'vGn_alpha = ', vGn_alpha ! van Genuchten "alpha" parameter (m-1) + print*, 'vGn_n = ', vGn_n ! van Genuchten "n" parameter (-) + print*, trim(message)//'PAUSE: Set doTest=.false. to complete simulations' + read(*,*) + + endif ! if testing + endif ! (if frozen conditions) enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*diffT diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index f8f06a753..872807c8c 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -58,7 +58,8 @@ module eval8summa_module ! look-up values for the choice of heat capacity computation USE mDecisions_module,only: & closedForm, & ! heat capacity closed form in backward Euler residual - enthalpyFD ! enthalpy finite difference in backward Euler residual + enthalpyFDlu, & ! enthalpy with lookup tables finite difference in backward Euler residual + enthalpyFD ! enthalpy with hypergeometric function finite difference in backward Euler residual ! look-up values for the numerical method USE mDecisions_module,only: & @@ -310,7 +311,7 @@ subroutine eval8summa(& end if end if ! ( feasibility check ) - if(ixNrgConserv == enthalpyFD)then + if(ixNrgConserv == enthalpyFD .or. ixNrgConserv == enthalpyFDlu)then updateCp = .true. needCm = .true. else if(ixNrgConserv == closedForm)then ! have a choice, should update if checkNrgBalance in varSubstep is turned on @@ -509,26 +510,27 @@ subroutine eval8summa(& dCm_dTkCanopy = 0._rkind endif ! needCm - if(ixNrgConserv == enthalpyFD)then ! use residual as enthalpy_delta - (phase change)_delta + if(ixNrgConserv.ne.closedForm)then ! use residual as enthalpy_delta - (phase change)_delta ! compute temperature component of enthalpy call t2enthalpy(& + ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value for canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) + scalarCanairTempTrial, & ! intent(in): trial value for canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) ! output: enthalpy - scalarCanairEnthalpyTrial, & ! intent(out): trial value for temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(out): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(out): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpyTrial, & ! intent(out): trial value for temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(out): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(out): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -618,41 +620,41 @@ subroutine eval8summa(& ! compute the residual vector call computResid(& ! input: model control - dt_cur, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - ixNrgConserv==enthalpyFD, & ! intent(in): flag to use enthalpy form of residual + dt_cur, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + ixNrgConserv.ne.closedForm, & ! intent(in): flag to use enthalpy form of residual ! input: flux vectors - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) - fluxVec, & ! intent(in): flux vector - ! input: state variables (already disaggregated into scalars and vectors) - scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) - scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) - scalarCanopyWatTrial, & ! intent(in): trial value for the water on the vegetation canopy (kg m-2) - mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) - scalarAquiferStorageTrial, & ! intent(in): trial value of storage of water in the aquifer (m) - ! input: diagnostic variables defining the liquid water and ice content (function of state variables) - scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value for the liq on the vegetation canopy (kg m-2) - mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) - mLayerVolFracWatTrial, & ! intent(in): trial value for the volumetric water in each snow and soil layer (-) - mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liq in each snow and soil layer (-) + sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + fluxVec, & ! intent(in): flux vector + ! input: state variables (a lready disaggregated into scalars and vectors) + scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) + scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) + scalarCanopyWatTrial, & ! intent(in): trial value for the water on the vegetation canopy (kg m-2) + mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) + scalarAquiferStorageTrial, & ! intent(in): trial value of storage of water in the aquifer (m) + ! input: diagnostic variabl es defining the liquid water and ice content (function of state variables) + scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(in): trial value for the liq on the vegetation canopy (kg m-2) + mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) + mLayerVolFracWatTrial, & ! intent(in): trial value for the volumetric water in each snow and soil layer (-) + mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liq in each snow and soil layer (-) ! input: enthalpy terms - canopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) - mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) - scalarCanairEnthalpyTrial, & ! intent(in): trial value for temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(in): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(in): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) + canopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) + mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) + scalarCanairEnthalpyTrial, & ! intent(in): trial value for temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(in): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(in): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) ! input: data structures - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - flux_data, & ! intent(in): model fluxes for a local HRU - indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + flux_data, & ! intent(in): model fluxes for a local HRU + indx_data, & ! intent(in): index data ! output - resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - resVec, & ! intent(out): residual vector - err,cmessage) ! intent(out): error control + resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + resVec, & ! intent(out): residual vector + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! compute the function evaluation diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 934eca3ca..156d839ad 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -35,7 +35,8 @@ module eval8summaWithPrime_module ! look-up values for the choice of heat capacity computation USE mDecisions_module,only: & closedForm, & ! heat capacity closed form in backward Euler residual - enthalpyFD ! enthalpy finite difference in backward Euler residual + enthalpyFDlu, & ! enthalpy with lookup tables finite difference in backward Euler residual + enthalpyFD ! enthalpy with hypergeometric function finite difference in backward Euler residual implicit none private @@ -282,7 +283,7 @@ subroutine eval8summaWithPrime(& end if end if ! ( feasibility check ) - if(ixNrgConserv == enthalpyFD)then + if(ixNrgConserv == enthalpyFD .or. ixNrgConserv == enthalpyFDlu)then updateCp = .true. needCm = .true. else if(ixNrgConserv == closedForm)then ! have a choice, should update if checkNrgBalance in varSubstep is turned on diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index 44dfe9a79..0c2eef690 100644 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -143,13 +143,14 @@ module mDecisions_module integer(i4b),parameter,public :: constDens = 311 ! Constant new snow density integer(i4b),parameter,public :: anderson = 312 ! Anderson 1976 integer(i4b),parameter,public :: hedAndPom = 313 ! Hedstrom and Pomeroy (1998), expoential increase -integer(i4b),parameter,public :: pahaut_76 = 314 ! Pahaut 1976, wind speed dependent (derived from Col de Porte, French Alps) +integer(i4b),parameter,public :: pahaut_76 = 314 ! Pahaut 1976, wind speed dependent (derived from Col de Porte, French Alps) ! look-up values for the choice of snow unloading from the canopy integer(i4b),parameter,public :: meltDripUnload = 321 ! Hedstrom and Pomeroy (1998), Storck et al 2002 (snowUnloadingCoeff & ratioDrip2Unloading) integer(i4b),parameter,public :: windUnload = 322 ! Roesch et al 2001, formulate unloading based on wind and temperature ! look-up values for the choice of energy conservation residual -integer(i4b),parameter,public :: enthalpyFD = 323 ! enthalpyFD -integer(i4b),parameter,public :: closedForm = 324 ! closedForm +integer(i4b),parameter,public :: enthalpyFD = 323 ! enthalpy with hypergeometric function finite difference in backward Euler residual +integer(i4b),parameter,public :: closedForm = 324 ! heat capacity closed form in backward Euler residual +integer(i4b),parameter,public :: enthalpyFDlu = 325 ! enthalpy with lookup tables finite difference in backward Euler residual ! ----------------------------------------------------------------------------------------------------------- contains @@ -414,8 +415,9 @@ subroutine mDecisions(err,message) ! choice of variable in energy conservation backward Euler residual, choice enthalpyFD has changes the residual computation and has better coincidence of energy conservation for backward Euler solution ! NOTE: choice does not change the residual for the IDA solution, as they are equivalent select case(trim(model_decisions(iLookDECISIONS%nrgConserv)%cDecision)) - case('enthalpyFD'); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = enthalpyFD ! enthalpy finite difference in backward Euler residual - case('closedForm'); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = closedForm ! heat capacity closed form in backward Euler residual + case('closedForm' ); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = closedForm ! heat capacity closed form in backward Euler residual + case('enthalpyFD' ); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = enthalpyFD ! enthalpy with hypergeometric function finite difference in backward Euler residual + case('enthalpyFDlu'); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = enthalpyFDlu ! enthalpy with lookup tables finite difference in backward Euler residual case default if (trim(model_decisions(iLookDECISIONS%num_method)%cDecision)=='itertive')then model_decisions(iLookDECISIONS%nrgConserv)%iDecision = closedForm ! included for backwards compatibility diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 99f945812..de88c5dcd 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -79,10 +79,10 @@ module systemSolv_module model_options ! defines the model decisions ! look-up values for the choice of heat capacity computation - USE mDecisions_module,only: & +USE mDecisions_module,only: & closedForm, & ! heat capacity closed form in backward Euler residual - enthalpyFD ! enthalpy finite difference in backward Euler residual - + enthalpyFDlu, & ! enthalpy with lookup tables finite difference in backward Euler residual + enthalpyFD ! enthalpy with hypergeometric function finite difference in backward Euler residual ! look-up values for the choice of groundwater representation (local-column, or single-basin) USE mDecisions_module,only:& @@ -97,9 +97,9 @@ module systemSolv_module ! look-up values for the numerical method USE mDecisions_module,only:& - numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes - kinsol ,& ! SUNDIALS backward Euler solution using Kinsol - ida ! SUNDIALS solution using IDA + numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes + kinsol ,& ! SUNDIALS backward Euler solution using Kinsol + ida ! SUNDIALS solution using IDA ! safety: set private unless specified otherwise implicit none @@ -394,9 +394,10 @@ subroutine systemSolv(& ! initialize the trial state vectors stateVecTrial = stateVecInit - if(ixNrgConserv == enthalpyFD .and. ixNumericalMethod .ne. ida)then - ! compute H_T at the beginning of the data step + if((ixNrgConserv.ne.closedForm .or. checkNrgBalance) .and. ixNumericalMethod.ne.ida)then + ! will need enthalpy change, compute H_T at the beginning of the data step call t2enthalpy(& + ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 4b4964352..ec2d229a7 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -75,14 +75,15 @@ module varSubstep_module ! look-up values for the numerical method USE mDecisions_module,only: & - numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes - kinsol ,& ! SUNDIALS backward Euler solution using Kinsol - ida ! SUNDIALS solution using IDA + numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes + kinsol ,& ! SUNDIALS backward Euler solution using Kinsol + ida ! SUNDIALS solution using IDA ! look-up values for the choice of heat capacity computation USE mDecisions_module,only: & closedForm, & ! heat capacity closed form in backward Euler residual - enthalpyFD ! enthalpy finite difference in backward Euler residual + enthalpyFDlu, & ! enthalpy with lookup tables finite difference in backward Euler residual + enthalpyFD ! enthalpy with hypergeometric function finite difference in backward Euler residual ! safety: set private unless specified otherwise implicit none @@ -198,6 +199,7 @@ subroutine varSubstep(& logical(lgt),parameter :: checkMassBalance = .true. ! flag to check the mass balance logical(lgt),parameter :: checkNrgBalance = .true. ! flag to check the energy balance logical(lgt) :: computeEnthalpy ! flag to compute enthalpy regardless of the model decision + logical(lgt) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function ! --------------------------------------------------------------------------------------- ! initialize error control @@ -273,9 +275,11 @@ subroutine varSubstep(& ! initialize flag for the success of the substepping failedMinimumStep=.false. - ! set the flag to compute enthalpy + ! set the flag to compute enthalpy, may want to have this true always if want to output enthalpy computeEnthalpy = .false. - if(checkNrgBalance .or. ixNrgConserv==enthalpyFD) computeEnthalpy = .true. ! need to get enthalpy regardles of other decisions + use_lookup = .false. ! with or without lookup tables + if((ixNrgConserv .ne. closedForm .or. checkNrgBalance) .and. ixNumericalMethod .ne. ida) computeEnthalpy = .true. + if (ixNrgConserv==enthalpyFDlu) use_lookup = .true. ! initialize the length of the substep dtSubstep = dtInit @@ -442,7 +446,7 @@ subroutine varSubstep(& ! update prognostic variables, update balances, and check them for possible step reduction if numrec or kinsol call updateProg(dtSubstep,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states - doAdjustTemp,computeVegFlux,checkMassBalance, checkNrgBalance,computeEnthalpy, & ! input: model control + doAdjustTemp,computeVegFlux,checkMassBalance,checkNrgBalance,computeEnthalpy,use_lookup, & ! input: model control model_decisions,lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! output: balances, flags, and error control if(err/=0)then @@ -619,101 +623,103 @@ end subroutine varSubstep ! private subroutine updateProg: update prognostic variables ! ********************************************************************************************************** subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states - doAdjustTemp,computeVegFlux,checkMassBalance, checkNrgBalance,computeEnthalpy, & ! input: model control + doAdjustTemp,computeVegFlux,checkMassBalance,checkNrgBalance,computeEnthalpy,use_lookup, & ! input: model control model_decisions,lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! input-output: balances, flags, and error control -USE getVectorz_module,only:varExtract ! extract variables from the state vector +USE getVectorz_module,only:varExtract ! extract variables from the state vector #ifdef SUNDIALS_ACTIVE - USE updateVarsWithPrime_module,only:updateVarsWithPrime ! update prognostic variables + USE updateVarsWithPrime_module,only:updateVarsWithPrime ! update prognostic variables #endif - USE updateVars_module,only:updateVars ! update prognostic variables - USE enthalpyTemp_module,only:t2enthalpy ! compute enthalpy - USE enthalpyTemp_module,only:enthalpy2DeltaH ! add phase change terms to delta temperature component of enthalpy + USE updateVars_module,only:updateVars ! update prognostic variables + USE enthalpyTemp_module,only:t2enthalpy ! compute enthalpy + USE enthalpyTemp_module,only:enthalpy2DeltaH ! add phase change terms to delta temperature component of enthalpy implicit none ! model control - real(rkind) ,intent(in) :: dt ! time step (s) - integer(i4b) ,intent(in) :: nSnow ! number of snow layers - integer(i4b) ,intent(in) :: nSoil ! number of soil layers - integer(i4b) ,intent(in) :: nLayers ! total number of layers - logical(lgt) ,intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature - logical(lgt) ,intent(in) :: computeVegFlux ! flag to compute the vegetation flux - real(rkind) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) - real(rkind) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) - real(rkind) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) - logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance - logical(lgt) ,intent(in) :: checkNrgBalance ! flag to check the energy balance - logical(lgt) ,intent(in) :: computeEnthalpy ! flag to compute enthalpy + real(rkind) ,intent(in) :: dt ! time step (s) + integer(i4b) ,intent(in) :: nSnow ! number of snow layers + integer(i4b) ,intent(in) :: nSoil ! number of soil layers + integer(i4b) ,intent(in) :: nLayers ! total number of layers + logical(lgt) ,intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature + logical(lgt) ,intent(in) :: computeVegFlux ! flag to compute the vegetation flux + real(rkind) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) + real(rkind) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) + real(rkind) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) + logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance + logical(lgt) ,intent(in) :: checkNrgBalance ! flag to check the energy balance + logical(lgt) ,intent(in) :: computeEnthalpy ! flag to compute enthalpy + logical(lgt) ,intent(in) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function + ! data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(zLookup),intent(in) :: lookup_data ! lookup tables - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! indices for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! indices for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! balances, flags, and error control - real(rkind) ,intent(in) :: fluxVec(:) ! flux vector (mixed units) - real(qp) ,intent(in) :: resVec(:) ! NOTE: qp ! residual vector - real(rkind) ,intent(inout) :: balance(:) ! balance of energy per domain - logical(lgt) ,intent(out) :: waterBalanceError ! flag to denote that there is a water balance error - logical(lgt) ,intent(out) :: nrgFluxModified ! flag to denote that the energy fluxes were modified - integer(i4b) ,intent(out) :: err ! error code - character(*) ,intent(out) :: message ! error message + real(rkind) ,intent(in) :: fluxVec(:) ! flux vector (mixed units) + real(qp) ,intent(in) :: resVec(:) ! NOTE: qp ! residual vector + real(rkind) ,intent(inout) :: balance(:) ! balance of energy per domain + logical(lgt) ,intent(out) :: waterBalanceError ! flag to denote that there is a water balance error + logical(lgt) ,intent(out) :: nrgFluxModified ! flag to denote that the energy fluxes were modified + integer(i4b) ,intent(out) :: err ! error code + character(*) ,intent(out) :: message ! error message ! ================================================================================================================== ! general - integer(i4b) :: i ! indices - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: ixSubset ! index within the state subset - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixControlIndex ! index within a given domain - real(rkind) :: volMelt ! volumetric melt (kg m-3) - real(rkind),parameter :: verySmall=epsilon(1._rkind) ! a very small number (deal with precision issues) - real(rkind) :: verySmall_veg ! precision needs to vary based on set canopy water tolerance for IDA - real(rkind) :: verySmall_snow ! precision needs to vary based on set snow water tolerance for IDA + integer(i4b) :: i ! indices + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: ixSubset ! index within the state subset + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixControlIndex ! index within a given domain + real(rkind) :: volMelt ! volumetric melt (kg m-3) + real(rkind),parameter :: verySmall=epsilon(1._rkind) ! a very small number (deal with precision issues) + real(rkind) :: verySmall_veg ! precision needs to vary based on set canopy water tolerance for IDA + real(rkind) :: verySmall_snow ! precision needs to vary based on set snow water tolerance for IDA ! mass balance - real(rkind) :: canopyBalance0,canopyBalance1 ! canopy storage at start/end of time step - real(rkind) :: soilBalance0,soilBalance1 ! soil storage at start/end of time step - real(rkind) :: vertFlux ! change in storage due to vertical fluxes - real(rkind) :: tranSink,baseSink,compSink ! change in storage due to sink terms - real(rkind) :: liqError ! water balance error - real(rkind) :: fluxNet ! net water fluxes (kg m-2 s-1) - real(rkind) :: superflousWat ! superflous water used for evaporation (kg m-2 s-1) - real(rkind) :: superflousNrg ! superflous energy that cannot be used for evaporation (W m-2 [J m-2 s-1]) - character(LEN=256) :: cmessage ! error message of downwind routine + real(rkind) :: canopyBalance0,canopyBalance1 ! canopy storage at start/end of time step + real(rkind) :: soilBalance0,soilBalance1 ! soil storage at start/end of time step + real(rkind) :: vertFlux ! change in storage due to vertical fluxes + real(rkind) :: tranSink,baseSink,compSink ! change in storage due to sink terms + real(rkind) :: liqError ! water balance error + real(rkind) :: fluxNet ! net water fluxes (kg m-2 s-1) + real(rkind) :: superflousWat ! superflous water used for evaporation (kg m-2 s-1) + real(rkind) :: superflousNrg ! superflous energy that cannot be used for evaporation (W m-2 [J m-2 s-1]) + character(LEN=256) :: cmessage ! error message of downwind routine ! trial state variables - real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial vector of temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector of volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector of total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector of liquid water matric potential (m) - real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) - real(rkind) :: scalarCanairEnthalpyTrial ! trial value for temperature component of enthalpy of the canopy air space (J m-3) - real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for temperature component of enthalpy of the vegetation canopy (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector of temperature component of enthalpy of snow + soil (J m-3) + real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial vector of temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector of volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector of total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector of liquid water matric potential (m) + real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) + real(rkind) :: scalarCanairEnthalpyTrial ! trial value for temperature component of enthalpy of the canopy air space (J m-3) + real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector of temperature component of enthalpy of snow + soil (J m-3) ! diagnostic variables - real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector of volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector of volumetric fraction of ice (-) + real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector of volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector of volumetric fraction of ice (-) ! derivative of state variables - real(rkind) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatPrime ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempPrime ! trial vector of temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! trial vector of volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! trial vector of total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! trial vector of liquid water matric potential (m) - real(rkind) :: scalarAquiferStoragePrime ! trial value for storage of water in the aquifer (m) + real(rkind) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatPrime ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempPrime ! trial vector of temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! trial vector of volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! trial vector of total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! trial vector of liquid water matric potential (m) + real(rkind) :: scalarAquiferStoragePrime ! trial value for storage of water in the aquifer (m) ! diagnostic variables - real(rkind) :: scalarCanopyLiqPrime ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector of volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector of volumetric fraction of ice (-) - real(rkind) :: scalarCanopyHmixDelta ! delta value for mixture enthalpy of the vegetation canopy (J m-3) - real(rkind),dimension(nLayers) :: mLayerHmixDelta ! delta vector of mixture enthalpy of snow + soil (J m-3) + real(rkind) :: scalarCanopyLiqPrime ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector of volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector of volumetric fraction of ice (-) + real(rkind) :: scalarCanopyHmixDelta ! delta value for mixture enthalpy of the vegetation canopy (J m-3) + real(rkind),dimension(nLayers) :: mLayerHmixDelta ! delta vector of mixture enthalpy of snow + soil (J m-3) ! ------------------------------------------------------------------------------------------------------------------- ! ------------------------------------------------------------------------------------------------------------------- @@ -951,9 +957,10 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! ---- ! * update enthalpy !------------------------ - if(computeEnthalpy)then ! update diagnostic enthalpy variables, don't do if not checking energy balance and closed form enthalpy + if(computeEnthalpy)then ! update diagnostic enthalpy variables if needed ! compute enthalpy at t_{n+1} call t2enthalpy(& + use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure @@ -1010,7 +1017,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec balance(ixSnowSoilNrg(i)) = mLayerHmixDelta(i) - fluxVec(ixSnowSoilNrg(i))*dt enddo endif - ! This is equivalent to above if, and only if, ixNrgConserv = enthalpyFD + ! This is equivalent to above if, and only if, ixNrgConserv.ne.closedForm !!if(ixCasNrg/=integerMissing) balance(ixCasNrg) = resVec(ixCasNrg) !if(ixVegNrg/=integerMissing) balance(ixVegNrg) = resVec(ixVegNrg) !if(nSnowSoilNrg>0)then From 30e5c33abc569b465f00a537855d7b93c555d703 Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 9 Feb 2024 05:35:07 -0600 Subject: [PATCH 1123/1472] Presentation updates and simplified associate block in summaSolve4numrec subroutine. --- build/source/engine/summaSolve4numrec.f90 | 80 +++++++++++------------ 1 file changed, 39 insertions(+), 41 deletions(-) diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 36b7bf29b..089d95e20 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -222,8 +222,6 @@ subroutine summaSolve4numrec(& logical(lgt) :: globalPrintFlagInit ! initial global print flag character(LEN=256) :: cmessage ! error message of downwind routine ! -------------------------------------------------------------------------------------------------------------------------------- - ! associations to information in data structures - associate(ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision) ! intent(in): [i4b] groundwater parameterization ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='summaSolve4numrec/' @@ -249,41 +247,43 @@ subroutine summaSolve4numrec(& ! NOTE: The derivatives were computed in the previous call to computFlux ! This occurred either at the call to eval8summa at the start of systemSolv ! or in the call to eval8summa in the previous iteration (within lineSearchRefinement or trustRegionRefinement) - call computJacob(& - ! input: model control - dt_cur, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - (ixGroundwater==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow - ixMatrix, & ! intent(in): form of the Jacobian matrix - ! input: data structures - indx_data, & ! intent(in): index data - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! input-output: Jacobian and its diagonal - dMat, & ! intent(inout): diagonal of the Jacobian matrix - aJac, & ! intent(out): Jacobian matrix - ! output: error control - err,cmessage) ! intent(out): error code and error message - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + associate(ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision) ! intent(in): [i4b] groundwater parameterization + call computJacob(& + ! input: model control + dt_cur, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + (ixGroundwater==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow + ixMatrix, & ! intent(in): form of the Jacobian matrix + ! input: data structures + indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables + dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! input-output: Jacobian and its diagonal + dMat, & ! intent(inout): diagonal of the Jacobian matrix + aJac, & ! intent(out): Jacobian matrix + ! output: error control + err,cmessage) ! intent(out): error code and error message + end associate ! end association to info in data structures + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! compute the numerical Jacobian matrix - if(doNumJacobian)then + if (doNumJacobian) then globalPrintFlag=.false. call numJacobian(stateVecTrial,dMat,nJac,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) globalPrintFlag=globalPrintFlagInit - endif + end if ! test the band diagonal matrix - if(testBandDiagonal)then + if (testBandDiagonal) then call testBandMat(check=.true.,err=err,message=cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - endif + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + end if ! ----- ! * solve linear system... @@ -294,9 +294,9 @@ subroutine summaSolve4numrec(& ! scale matrices call scaleMatrices(ixMatrix,nState,aJac,fScale,xScale,aJacScaled,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - if(globalPrintFlag .and. ixMatrix==ixBandMatrix)then + if (globalPrintFlag .and. ixMatrix==ixBandMatrix) then print*, '** SCALED banded analytical Jacobian:' write(*,'(a4,1x,100(i17,1x))') 'xCol', (iLayer, iLayer=iJac1,iJac2) do iLayer=kl+1,nBands @@ -309,10 +309,9 @@ subroutine summaSolve4numrec(& ! compute the newton step: use the lapack routines to solve the linear system A.X=B call lapackSolv(ixMatrix,nState,aJacScaledTemp,-rVecScaled,newtStepScaled,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - if(globalPrintFlag)& - write(*,'(a,1x,10(e17.10,1x))') 'newtStepScaled = ', newtStepScaled(min(iJac1,nState):min(iJac2,nState)) + if (globalPrintFlag) write(*,'(a,1x,10(e17.10,1x))') 'newtStepScaled = ', newtStepScaled(min(iJac1,nState):min(iJac2,nState)) ! ----- ! * update, evaluate, and refine the state vector... @@ -324,7 +323,7 @@ subroutine summaSolve4numrec(& ! * case 1: state vector ! compute the flux vector and the residual, and (if necessary) refine the iteration increment ! NOTE: in 99.9% of cases newtStep will be used (no refinement) - if(size(stateVecTrial)>1)then + if (size(stateVecTrial)>1) then ! try to backtrack select case(ixStepRefinement) @@ -335,21 +334,20 @@ subroutine summaSolve4numrec(& ! check warnings: negative error code = warning; in this case back-tracked to the original value ! NOTE: Accept the full newton step if back-tracked to the original value - if(err<0)then - doRefine=.false.; call lineSearchRefinement( doRefine,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,cmessage) + if (err<0) then + doRefine=.false.; + call lineSearchRefinement( doRefine,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,cmessage) end if ! * case 2: scalar else call safeRootfinder(stateVecTrial,rVecScaled,newtStepScaled,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) endif ! check errors - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! end association to info in data structures - end associate contains From c1d727598f8ec7da34abb584ce4538eb616d26fe Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 9 Feb 2024 12:10:33 -0600 Subject: [PATCH 1124/1472] fix allocation error --- build/source/engine/coupled_em.f90 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index e6279644f..fb6b67ea0 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1031,6 +1031,8 @@ subroutine coupled_em(& err=20; return endif ! try again, restart step (at end inner step) + deallocate(innerBalanceLayerNrg) + deallocate(innerBalanceLayerMass) cycle substeps endif From 6be6db315d1bd46d311afcbeb1adfdb8cc4e6a67 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 10 Feb 2024 06:11:56 -0600 Subject: [PATCH 1125/1472] Added class procedures to handle arguments for the computJacob subroutine. --- build/source/dshare/data_types.f90 | 56 ++++++++++++++++++++++- build/source/engine/computJacob.f90 | 10 ++-- build/source/engine/summaSolve4numrec.f90 | 14 +++--- 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index a4c2b068b..1dd37de88 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -617,7 +617,6 @@ MODULE data_types end type out_type_indexSplit ! ** end indexSplit - ! ** varSubstep type, public :: in_type_varSubstep ! class for intent(in) arguments in varSubstep call real(rkind) :: dt ! intent(in): time step (s) @@ -657,6 +656,31 @@ MODULE data_types end type out_type_varSubstep ! ** end varSubstep + ! *********************************************************************************************************** + ! Define classes used to simplify calls to the subrotuines in summaSolve4numrec + ! *********************************************************************************************************** + + type, public :: in_type_computJacob ! class for intent(in) arguments in computFlux call + ! input: model control + real(rkind) :: dt ! intent(in): length of the time step (seconds) + integer(i4b) :: nSnow ! intent(in): number of snow layers + integer(i4b) :: nSoil ! intent(in): number of soil layers + integer(i4b) :: nLayers ! intent(in): total number of layers in the snow+soil domain + logical(lgt) :: computeVegFlux ! intent(in): flag to indicate if computing fluxes over vegetation + logical(lgt) :: computeBaseflow ! intent(in): flag to indicate if computing baseflow + integer(i4b) :: ixMatrix ! intent(in): form of the Jacobian matrix + contains + procedure :: initialize => initialize_in_computJacob + end type in_type_computJacob + + type, public :: out_type_computJacob ! class for intent(in) arguments in computFlux call + ! output: error control + integer(i4b) :: err ! intent(out): error code + character(len=len_msg) :: cmessage ! intent(out): error message + contains + procedure :: finalize => finalize_out_computJacob + end type out_type_computJacob + contains ! **** vegNrgFlux **** @@ -1419,4 +1443,34 @@ subroutine finalize_out_varSubstep(out_varSubstep,dtMultiplier,nSubsteps,failedM end subroutine finalize_out_varSubstep ! **** end varSubstep **** + ! **** computJacob **** + subroutine initialize_in_computJacob(in_computJacob,dt,nSnow,nSoil,nLayers,computeVegFlux,computeBaseflow,ixMatrix) + class(in_type_computJacob),intent(out) :: in_computJacob ! class object for intent(in) computJacob arguments + real(rkind),intent(in) :: dt ! intent(in): length of the time step (seconds) + integer(i4b),intent(in) :: nSnow ! intent(in): number of snow layers + integer(i4b),intent(in) :: nSoil ! intent(in): number of soil layers + integer(i4b),intent(in) :: nLayers ! intent(in): total number of layers in the snow+soil domain + logical(lgt),intent(in) :: computeVegFlux ! intent(in): flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: computeBaseflow ! intent(in): flag to indicate if computing baseflow + integer(i4b),intent(in) :: ixMatrix ! intent(in): form of the Jacobian matrix + + ! intent(in) arguments + in_computJacob % dt = dt ! intent(in): length of the time step (seconds) + in_computJacob % nSnow = nSnow ! intent(in): number of snow layers + in_computJacob % nSoil = nSoil ! intent(in): number of soil layers + in_computJacob % nLayers = nLayers ! intent(in): total number of layers in the snow+soil domain + in_computJacob % computeVegFlux = computeVegFlux ! intent(in): flag to indicate if computing fluxes over vegetation + in_computJacob % computeBaseflow = computeBaseflow ! intent(in): flag to indicate if computing baseflow + in_computJacob % ixMatrix = ixMatrix ! intent(in): form of the Jacobian matrix + end subroutine initialize_in_computJacob + + subroutine finalize_out_computJacob(out_computJacob,err,cmessage) + class(out_type_computJacob),intent(in) :: out_computJacob ! class object for intent(out) computJacob arguments + integer(i4b),intent(out) :: err ! intent(out): error code + character(*),intent(out) :: cmessage ! intent(out): error message + ! intent(out) arguments + err = out_computJacob % err ! intent(out): error code + cmessage = out_computJacob % cmessage ! intent(out): error message + end subroutine finalize_out_computJacob + END MODULE data_types diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index a6c4aab87..8750eb3fb 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -25,9 +25,11 @@ module computJacob_module ! derived types to define the data structures USE data_types,only:& - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - model_options ! defines the model decisions + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (rkind) + model_options, & ! defines the model decisions + in_type_computJacob, & ! class for computJacob arguments + out_type_computJacob ! class for computJacob arguments ! named variables for structure elements USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure @@ -1106,4 +1108,4 @@ function ixOffDiag(jState,iState) ixOffDiag = ixDiag + jState - iState end function ixOffDiag -end module computJacob_module \ No newline at end of file +end module computJacob_module diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 089d95e20..87a8bb668 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -66,12 +66,14 @@ module summaSolve4numrec_module ! provide access to the derived types to define the data structures USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! lookup tables - model_options ! defines the model decisions + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! lookup tables + model_options, & ! defines the model decisions + in_type_computJacob, & ! class for computJacob arguments + out_type_computJacob ! class for computJacob arguments ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: & From 41bcab87d6d921d2c94c8ff6f80f35edf5cf607e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 13 Feb 2024 10:50:37 +0900 Subject: [PATCH 1126/1472] fix cmessage --- build/source/engine/varSubstep.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index ec2d229a7..714aa8e72 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -1006,7 +1006,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec scalarCanopyHmixDelta, & ! intent(inout): delta value for mixture enthalpy of the vegetation canopy (J m-3) mLayerHmixDelta, & ! intent(inout): delta vector of mixture enthalpy of each snow+soil layer (J m-3) ! output: error control - err,message) ! intent(out): error control + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! compute energy balance, maybe should use to check for step reduction From edf8ef827fb2c5b5e37223bf12388457b7d5a51d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 13 Feb 2024 11:54:19 +0900 Subject: [PATCH 1127/1472] initialize converged --- build/source/engine/systemSolv.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index de88c5dcd..a47e36701 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -665,6 +665,7 @@ subroutine systemSolv(& !--------------------------- ! iterate and update trial state vector, fluxes, and derivatives do iter=1,localMaxIter + converged = .false. ! keep track of the number of iterations niter = iter+1 ! +1 because xFluxResid was moved outside the iteration loop (for backwards compatibility) call summaSolve4numrec(& From 489ba4bf7b97b4ff8f8236ce2cb8194ba5de88a9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 13 Feb 2024 12:11:50 +0900 Subject: [PATCH 1128/1472] initializing converge --- build/source/engine/summaSolve4numrec.f90 | 2 ++ build/source/engine/systemSolv.f90 | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 36b7bf29b..fbd9622a1 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -395,6 +395,7 @@ subroutine lineSearchRefinement(doLineSearch,stateVecTrial,newtStepScaled,aJacSc ! -------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='lineSearchRefinement/' + converged=.false. ! check the need to compute the line search if(doLineSearch)then @@ -549,6 +550,7 @@ subroutine trustRegionRefinement(doTrustRefinement,stateVecTrial,newtStepScaled, ! -------------------------------------------------------------------------------------------------------- err=0; message='trustRegionRefinement/' + converged=.false. ! check the need to refine the step if(doTrustRefinement)then diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index a47e36701..de88c5dcd 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -665,7 +665,6 @@ subroutine systemSolv(& !--------------------------- ! iterate and update trial state vector, fluxes, and derivatives do iter=1,localMaxIter - converged = .false. ! keep track of the number of iterations niter = iter+1 ! +1 because xFluxResid was moved outside the iteration loop (for backwards compatibility) call summaSolve4numrec(& From 6b130053902e45630b9e7e65ff20c4e97cf12f47 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 13 Feb 2024 13:03:54 +0900 Subject: [PATCH 1129/1472] initialize convergence --- build/source/engine/summaSolve4numrec.f90 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index fbd9622a1..edcd22c5b 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -395,7 +395,7 @@ subroutine lineSearchRefinement(doLineSearch,stateVecTrial,newtStepScaled,aJacSc ! -------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='lineSearchRefinement/' - converged=.false. + converged =.false. ! check the need to compute the line search if(doLineSearch)then @@ -550,7 +550,7 @@ subroutine trustRegionRefinement(doTrustRefinement,stateVecTrial,newtStepScaled, ! -------------------------------------------------------------------------------------------------------- err=0; message='trustRegionRefinement/' - converged=.false. + converged =.false. ! check the need to refine the step if(doTrustRefinement)then @@ -619,6 +619,7 @@ subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fl real(rkind),parameter :: delX=1._rkind ! trial increment ! -------------------------------------------------------------------------------------------------------- err=0; message='safeRootfinder/' + converged = .false. ! check scalar if(size(stateVecTrial)/=1 .or. size(rVecScaled)/=1 .or. size(newtStepScaled)/=1)then From 85a3e129b81015104e50c213be1a8b1a86cb237d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 13 Feb 2024 14:08:29 +0900 Subject: [PATCH 1130/1472] clarifying names of variables "checkMassBalance" and "checkNrgBalance", should not effect solutions --- build/source/engine/coupled_em.f90 | 12 +++---- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/eval8summaWithPrime.f90 | 2 +- build/source/engine/summaSolve4ida.f90 | 16 ++++----- build/source/engine/summaSolve4numrec.f90 | 38 ++++++++++----------- build/source/engine/systemSolv.f90 | 16 ++++----- build/source/engine/varSubstep.f90 | 38 +++++++++++++++------ 7 files changed, 70 insertions(+), 54 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index fb6b67ea0..f8908a2ef 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -210,7 +210,7 @@ subroutine coupled_em(& logical(lgt) :: doLayerMerge ! flag to denote the need to merge snow layers logical(lgt) :: pauseFlag ! flag to pause execution logical(lgt),parameter :: backwardsCompatibility=.true. ! flag to denote a desire to ensure backwards compatibility with previous branches - logical(lgt) :: checkMassBalance ! flag to check the mass balance + logical(lgt) :: checkMassBalance_ds ! flag to check the mass balance over the data step type(var_ilength) :: indx_temp ! temporary model index variables saved only on outer loop type(var_ilength) :: indx_temp0 ! temporary model index variables saved every time type(var_dlength) :: prog_temp ! temporary model prognostic variables @@ -1276,8 +1276,8 @@ subroutine coupled_em(& ! identify the need to check the mass balance, both methods should work if tolerance coarse enough select case(ixNumericalMethod) - case(ida); checkMassBalance = .false. ! IDA balance agreement levels are controlled by set tolerances - case(kinsol, numrec); checkMassBalance = .true. ! KINSOL or numrec give finite difference dt_sub fluxes and were summed for an average flux + case(ida); checkMassBalance_ds = .false. ! IDA balance agreement levels are controlled by set tolerances + case(kinsol, numrec); checkMassBalance_ds = .true. ! KINSOL or numrec give finite difference dt_sub fluxes and were summed for an average flux case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or numrec (or itertive, which is numrec)'; return end select @@ -1294,7 +1294,7 @@ subroutine coupled_em(& ! NOTE: need to put the balance checks in the sub-step loop so that we can recompute if necessary scalarCanopyWatBalError = balanceCanopyWater1 - (balanceCanopyWater0 + (scalarSnowfall - averageThroughfallSnow)*data_step + (scalarRainfall - averageThroughfallRain)*data_step & - averageCanopySnowUnloading*data_step - averageCanopyLiqDrainage*data_step + averageCanopySublimation*data_step + averageCanopyEvaporation*data_step) - if(abs(scalarCanopyWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then + if(abs(scalarCanopyWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance_ds)then write(*,'(a,1x,f20.10)') 'data_step = ', data_step write(*,'(a,1x,f20.10)') 'balanceCanopyWater0 = ', balanceCanopyWater0 write(*,'(a,1x,f20.10)') 'balanceCanopyWater1 = ', balanceCanopyWater1 @@ -1338,7 +1338,7 @@ subroutine coupled_em(& newSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) delSWE = newSWE - (oldSWE - sfcMeltPond) massBalance = delSWE - (effSnowfall + effRainfall + averageSnowSublimation - averageSnowDrainage*iden_water)*data_step - if(abs(massBalance) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then + if(abs(massBalance) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance_ds)then print*, 'nSnow = ', nSnow print*, 'nSub = ', nSub write(*,'(a,1x,f20.10)') 'data_step = ', data_step @@ -1393,7 +1393,7 @@ subroutine coupled_em(& ! check the soil water balance scalarSoilWatBalError = balanceSoilWater1 - (balanceSoilWater0 + (balanceSoilInflux + balanceSoilET - balanceSoilBaseflow - balanceSoilDrainage - balanceSoilCompress) ) - if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues + if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance_ds)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues write(*,*) 'solution method = ', ixSolution write(*,'(a,1x,f20.10)') 'data_step = ', data_step write(*,'(a,1x,f20.10)') 'balanceSoilCompress = ', balanceSoilCompress diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 872807c8c..a10871d14 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -314,7 +314,7 @@ subroutine eval8summa(& if(ixNrgConserv == enthalpyFD .or. ixNrgConserv == enthalpyFDlu)then updateCp = .true. needCm = .true. - else if(ixNrgConserv == closedForm)then ! have a choice, should update if checkNrgBalance in varSubstep is turned on + else if(ixNrgConserv == closedForm)then ! have a choice updateCp = updateCp_closedForm needCm = needCm_closedForm else diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 156d839ad..ad62a9327 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -286,7 +286,7 @@ subroutine eval8summaWithPrime(& if(ixNrgConserv == enthalpyFD .or. ixNrgConserv == enthalpyFDlu)then updateCp = .true. needCm = .true. - else if(ixNrgConserv == closedForm)then ! have a choice, should update if checkNrgBalance in varSubstep is turned on + else if(ixNrgConserv == closedForm)then ! have a choice updateCp = updateCp_closedForm needCm = needCm_closedForm else diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 5dbb37031..7f826dafe 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -101,8 +101,8 @@ subroutine summaSolve4ida( & firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution - checkMassBalance, & ! intent(in): flag to check mass balance - checkNrgBalance, & ! intent(in): flag to check energy balance + computMassBalance, & ! intent(in): flag to compute mass balance + computNrgBalance, & ! intent(in): flag to compute energy balance ! input: state vectors stateVecInit, & ! intent(in): initial state vector sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) @@ -171,8 +171,8 @@ subroutine summaSolve4ida( & logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - logical(lgt),intent(in) :: checkMassBalance ! flag to check mass balance - logical(lgt),intent(in) :: checkNrgBalance ! flag to check energy balance + logical(lgt),intent(in) :: computMassBalance ! flag to compute mass balance + logical(lgt),intent(in) :: computNrgBalance ! flag to compute energy balance ! input: state vectors real(rkind),intent(in) :: stateVecInit(:) ! model state vector real(qp),intent(in) :: sMul(:) ! state vector multiplier (used in the residual calculations) @@ -511,10 +511,10 @@ subroutine summaSolve4ida( & + dCompress_dPsiPrev(:) * mLayerMatricHeadPrimePrev(:) ) * dt_diff/2._rkind ! ---- - ! * check energy balance, from residuals + ! * compute energy balance, from residuals ! formulation with prime variables would cancel to closedForm version, so does not matter which formulation is used !------------------------ - if(checkNrgBalance)then + if(computNrgBalance)then ! compute energy balance mean, resVec is the instanteous residual vector from the solver if(ixCasNrg/=integerMissing) balance(ixCasNrg) = balance(ixCasNrg) + ( eqns_data%resVec(ixCasNrg) + resVecPrev(ixCasNrg) )*dt_diff/2._rkind/dt @@ -527,9 +527,9 @@ subroutine summaSolve4ida( & endif ! ---- - ! * check mass balance, from residuals + ! * compute mass balance, from residuals !------------------------ - if(checkMassBalance)then + if(computMassBalance)then ! compute mass balance mean, resVec is the instanteous residual vector from the solver if(ixVegHyd/=integerMissing) balance(ixVegHyd) = balance(ixVegHyd) + ( eqns_data%resVec(ixVegHyd) + resVecPrev(ixVegHyd) )*dt_diff/2._rkind/dt diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index edcd22c5b..ac9434c93 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -990,26 +990,26 @@ end subroutine eval8summa_wrapper function checkConv(rVec,xInc,xVec) implicit none ! dummies - real(rkind),intent(in) :: rVec(:) ! residual vector (mixed units) - real(rkind),intent(in) :: xInc(:) ! iteration increment (mixed units) - real(rkind),intent(in) :: xVec(:) ! state vector (mixed units) - logical(lgt) :: checkConv ! flag to denote convergence + real(rkind),intent(in) :: rVec(:) ! residual vector (mixed units) + real(rkind),intent(in) :: xInc(:) ! iteration increment (mixed units) + real(rkind),intent(in) :: xVec(:) ! state vector (mixed units) + logical(lgt) :: checkConv ! flag to denote convergence ! locals - real(rkind),dimension(mSoil) :: psiScale ! scaling factor for matric head - real(rkind),parameter :: xSmall=1.e-0_rkind ! a small offset - real(rkind),parameter :: scalarTighten=0.1_rkind ! scaling factor for the scalar solution - real(rkind) :: soilWatbalErr ! error in the soil water balance - real(rkind) :: canopy_max ! absolute value of the residual in canopy water (kg m-2) - real(rkind),dimension(1) :: energy_max ! maximum absolute value of the energy residual (J m-3) - real(rkind),dimension(1) :: liquid_max ! maximum absolute value of the volumetric liquid water content residual (-) - real(rkind),dimension(1) :: matric_max ! maximum absolute value of the matric head iteration increment (m) - real(rkind) :: aquifer_max ! absolute value of the residual in aquifer water (m) - logical(lgt) :: canopyConv ! flag for canopy water balance convergence - logical(lgt) :: watbalConv ! flag for soil water balance convergence - logical(lgt) :: liquidConv ! flag for residual convergence - logical(lgt) :: matricConv ! flag for matric head convergence - logical(lgt) :: energyConv ! flag for energy convergence - logical(lgt) :: aquiferConv ! flag for aquifer water balance convergence + real(rkind),dimension(mSoil) :: psiScale ! scaling factor for matric head + real(rkind),parameter :: xSmall=1.e-0_rkind ! a small offset + real(rkind),parameter :: scalarTighten=0.1_rkind ! scaling factor for the scalar solution + real(rkind) :: soilWatbalErr ! error in the soil water balance + real(rkind) :: canopy_max ! absolute value of the residual in canopy water (kg m-2) + real(rkind),dimension(1) :: energy_max ! maximum absolute value of the energy residual (J m-3) + real(rkind),dimension(1) :: liquid_max ! maximum absolute value of the volumetric liquid water content residual (-) + real(rkind),dimension(1) :: matric_max ! maximum absolute value of the matric head iteration increment (m) + real(rkind) :: aquifer_max ! absolute value of the residual in aquifer water (m) + logical(lgt) :: canopyConv ! flag for canopy water balance convergence + logical(lgt) :: watbalConv ! flag for soil water balance convergence + logical(lgt) :: liquidConv ! flag for residual convergence + logical(lgt) :: matricConv ! flag for matric head convergence + logical(lgt) :: energyConv ! flag for energy convergence + logical(lgt) :: aquiferConv ! flag for aquifer water balance convergence ! ------------------------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures associate(& diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index de88c5dcd..5fe197661 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -123,8 +123,8 @@ subroutine systemSolv(& firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution - checkMassBalance, & ! intent(in): flag to check mass balance - checkNrgBalance, & ! intent(in): flag to check energy balance + computMassBalance, & ! intent(in): flag to compute mass balance + computNrgBalance, & ! intent(in): flag to compute energy balance ! input/output: data structures lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil @@ -185,8 +185,8 @@ subroutine systemSolv(& logical(lgt),intent(inout) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - logical(lgt),intent(in) :: checkMassBalance ! flag to check mass balance - logical(lgt),intent(in) :: checkNrgBalance ! flag to check energy balance + logical(lgt),intent(in) :: computMassBalance ! flag to compute mass balance + logical(lgt),intent(in) :: computNrgBalance ! flag to compute energy balance ! input/output: data structures type(zLookup),intent(in) :: lookup_data ! lookup tables type(var_i),intent(in) :: type_data ! type of vegetation and soil @@ -394,7 +394,7 @@ subroutine systemSolv(& ! initialize the trial state vectors stateVecTrial = stateVecInit - if((ixNrgConserv.ne.closedForm .or. checkNrgBalance) .and. ixNumericalMethod.ne.ida)then + if((ixNrgConserv.ne.closedForm .or. computNrgBalance) .and. ixNumericalMethod.ne.ida)then ! will need enthalpy change, compute H_T at the beginning of the data step call t2enthalpy(& ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy @@ -534,12 +534,12 @@ subroutine systemSolv(& firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation scalarSolution, & ! intent(in): flag to indicate the scalar solution - checkMassBalance, & ! intent(in): flag to check mass balance - checkNrgBalance, & ! intent(in): flag to check energy balance + computMassBalance, & ! intent(in): flag to compute mass balance + computNrgBalance, & ! intent(in): flag to compute energy balance ! input: state vector stateVecTrial, & ! intent(in): model state vector at the beginning of the data time step sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - dMat, & ! intent(inout) diagonal of the Jacobian matrix (excludes fluxes) + dMat, & ! intent(inout): diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures model_decisions, & ! intent(in): model decisions type_data, & ! intent(in): type of vegetation and soil diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 714aa8e72..925b50131 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -196,8 +196,8 @@ subroutine varSubstep(& real(qp) :: resVec(in_varSubstep % nSubset) ! substep residual vector real(rkind) :: balance(in_varSubstep % nSubset) ! substep balance real(rkind) :: sumBalance(in_varSubstep % nSubset) ! sum of substeps balance - logical(lgt),parameter :: checkMassBalance = .true. ! flag to check the mass balance - logical(lgt),parameter :: checkNrgBalance = .true. ! flag to check the energy balance + logical(lgt),parameter :: computMassBalance = .true. ! flag to compute the mass balance, will affect step length, default true + logical(lgt),parameter :: computNrgBalance = .true. ! flag to compute the energy balance, will not effect solution but will not compute nrg balance if false (saves expense) logical(lgt) :: computeEnthalpy ! flag to compute enthalpy regardless of the model decision logical(lgt) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function @@ -278,7 +278,7 @@ subroutine varSubstep(& ! set the flag to compute enthalpy, may want to have this true always if want to output enthalpy computeEnthalpy = .false. use_lookup = .false. ! with or without lookup tables - if((ixNrgConserv .ne. closedForm .or. checkNrgBalance) .and. ixNumericalMethod .ne. ida) computeEnthalpy = .true. + if((ixNrgConserv .ne. closedForm .or. computNrgBalance) .and. ixNumericalMethod .ne. ida) computeEnthalpy = .true. if (ixNrgConserv==enthalpyFDlu) use_lookup = .true. ! initialize the length of the substep @@ -350,8 +350,8 @@ subroutine varSubstep(& firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution - checkMassBalance, & ! intent(in): flag to check mass balance - checkNrgBalance, & ! intent(in): flag to check energy balance + computMassBalance, & ! intent(in): flag to compute mass balance + computNrgBalance, & ! intent(in): flag to compute energy balance ! input/output: data structures lookup_data, & ! intent(in): lookup tables type_data, & ! intent(in): type of vegetation and soil @@ -446,7 +446,7 @@ subroutine varSubstep(& ! update prognostic variables, update balances, and check them for possible step reduction if numrec or kinsol call updateProg(dtSubstep,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states - doAdjustTemp,computeVegFlux,checkMassBalance,checkNrgBalance,computeEnthalpy,use_lookup, & ! input: model control + doAdjustTemp,computeVegFlux,computMassBalance,computNrgBalance,computeEnthalpy,use_lookup, & ! input: model control model_decisions,lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! output: balances, flags, and error control if(err/=0)then @@ -623,7 +623,7 @@ end subroutine varSubstep ! private subroutine updateProg: update prognostic variables ! ********************************************************************************************************** subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states - doAdjustTemp,computeVegFlux,checkMassBalance,checkNrgBalance,computeEnthalpy,use_lookup, & ! input: model control + doAdjustTemp,computeVegFlux,computMassBalance,computNrgBalance,computeEnthalpy,use_lookup, & ! input: model control model_decisions,lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! input-output: balances, flags, and error control USE getVectorz_module,only:varExtract ! extract variables from the state vector @@ -644,8 +644,8 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec real(rkind) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) real(rkind) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) real(rkind) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) - logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance - logical(lgt) ,intent(in) :: checkNrgBalance ! flag to check the energy balance + logical(lgt) ,intent(in) :: computMassBalance ! flag to check the mass balance + logical(lgt) ,intent(in) :: computNrgBalance ! flag to check the energy balance logical(lgt) ,intent(in) :: computeEnthalpy ! flag to compute enthalpy logical(lgt) ,intent(in) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function @@ -984,7 +984,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec endif - if(checkNrgBalance)then + if(computNrgBalance)then ! compute energy balance if didn't do inside solver substeps select case(ixNumericalMethod) case(ida); ! do nothing, already computed @@ -1027,6 +1027,14 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec !endif end select + else ! if not checking energy balance set balance to missing + if(ixCasNrg/=integerMissing) balance(ixCasNrg) = realMissing + if(ixVegNrg/=integerMissing) balance(ixVegNrg) = realMissing + if(nSnowSoilNrg>0)then + do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) + balance(ixSnowSoilNrg(i)) = realMissing + enddo + endif endif ! if checking energy balance ! ----- @@ -1035,7 +1043,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! NOTE: currently this will only fail with kinsol solver, since mass balance is checked in the numrec solver and not checked for ida solver ! Negative error code will mean step will be failed and retried with smaller step size - if(checkMassBalance)then + if(computMassBalance)then if(ixVegHyd/=integerMissing)then ! check for complete drainage @@ -1137,6 +1145,14 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec if(ixAqWat/=integerMissing) balance(ixAqWat) = resVec(ixAqWat) end select + else ! if not checking mass balance set balance to missing + if(ixVegHyd/=integerMissing) balance(ixVegHyd) = realMissing + if(nSnowSoilHyd>0)then + do concurrent (i=1:nLayers,ixSnowSoilHyd(i)/=integerMissing) + balance(ixSnowSoilHyd(i)) = realMissing + end do + endif + if(ixAqWat/=integerMissing) balance(ixAqWat) = realMissing endif ! if checking the mass balance ! ----- From d9a060ba08cd93c00c37a630da70a63c8286a715 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 13 Feb 2024 16:51:42 +0900 Subject: [PATCH 1131/1472] fixing feasible checking --- build/source/engine/getVectorz.f90 | 50 ++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index 8936c163f..d9bd3bebc 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -459,29 +459,40 @@ subroutine checkFeas(& feasible=.true. ! check that the canopy air space temperature is reasonable if(ixCasNrg/=integerMissing)then - if(stateVec(ixCasNrg) > canopyTempMax) feasible=.false. - if(stateVec(ixCasNrg) > canopyTempMax) message=trim(message)//'canopy air space temp high,' - !if(stateVec(ixCasNrg) > canopyTempMax) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixCasNrg )', feasible, canopyTempMax, stateVec(ixCasNrg) + if(stateVec(ixCasNrg) > canopyTempMax) then + feasible=.false. + message=trim(message)//'canopy air space temp high' + !write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixCasNrg )', feasible, canopyTempMax, stateVec(ixCasNrg) + endif endif ! check that the canopy air space temperature is reasonable if(ixVegNrg/=integerMissing)then - if(stateVec(ixVegNrg) > canopyTempMax) feasible=.false. - !if(stateVec(ixVegNrg) > canopyTempMax) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixVegNrg )', feasible, canopyTempMax, stateVec(ixVegNrg) + if(stateVec(ixVegNrg) > canopyTempMax)then + feasible=.false. + message=trim(message)//'canopy temp high' + !write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixVegNrg )', feasible, canopyTempMax, stateVec(ixVegNrg) + endif endif ! check canopy liquid water is not negative if(ixVegHyd/=integerMissing)then - if(stateVec(ixVegHyd) < 0._rkind) feasible=.false. - !if(stateVec(ixVegHyd) < 0._rkind) write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVec( ixVegHyd )', feasible, 0._rkind, stateVec(ixVegHyd) - end if + if(stateVec(ixVegHyd) < 0._rkind)then + feasible=.false. + message=trim(message)//'canopy liq water neg' + !write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVec( ixVegHyd )', feasible, 0._rkind, stateVec(ixVegHyd) + endif + endif ! check snow temperature is below freezing if(count(ixSnowOnlyNrg/=integerMissing)>0)then - if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze)) feasible=.false. - !do iLayer=1,nSnow - ! if(stateVec(ixSnowOnlyNrg(iLayer)) > Tfreeze) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVec( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVec( ixSnowOnlyNrg(iLayer) ) - !enddo + if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze))then + feasible=.false. + message=trim(message)//'snow temp high' + !do iLayer=1,nSnow + ! if(stateVec(ixSnowOnlyNrg(iLayer)) > Tfreeze) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVec( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVec( ixSnowOnlyNrg(iLayer) ) + !enddo + endif endif ! loop through non-missing hydrology state variables in the snow+soil domain @@ -502,12 +513,17 @@ subroutine checkFeas(& case(iname_snow); xMax = merge(iden_ice, 1._rkind - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) case(iname_soil); xMax = merge(theta_sat(iLayer-nSnow), theta_sat(iLayer-nSnow) - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) end select - + if(iLayer==1)then + write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax + print*,layerType(iLayer),iname_snow,mLayerVolFracIce(iLayer),iLayer, ixHydType(iLayer)==iname_watLayer + endif ! --> check - if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) feasible=.false. - !if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) & - !write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax - + if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax)then + feasible=.false. + message=trim(message)//'layer water out of bounds' + if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) & + write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax + endif endif ! if water states end do ! loop through non-missing hydrology state variables in the snow+soil domain From 5d8db0ab21eb3d5c0da977a13eb7c67c91609fc9 Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 13 Feb 2024 05:48:00 -0600 Subject: [PATCH 1132/1472] The computJacob subroutine now uses the out_computJacob class object for intent(out) arguments. --- build/source/engine/computJacob.f90 | 66 ++++++++++++++--------- build/source/engine/summaSolve4numrec.f90 | 27 +++++++++- 2 files changed, 67 insertions(+), 26 deletions(-) diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 8750eb3fb..3069a8f86 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -86,25 +86,26 @@ module computJacob_module ! public subroutine computJacob: compute the Jacobian matrix ! ********************************************************************************************************** subroutine computJacob(& - ! input: model control - dt, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - computeBaseflow, & ! intent(in): flag to indicate if we need to compute baseflow - ixMatrix, & ! intent(in): form of the Jacobian matrix - ! input: data structures - indx_data, & ! intent(in): index data - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! input-output: Jacobian and its diagonal - dMat, & ! intent(inout): diagonal of the Jacobian matrix - aJac, & ! intent(out): Jacobian matrix - ! output: error control - err,message) ! intent(out): error code and error message + ! input: model control + dt, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + computeBaseflow, & ! intent(in): flag to indicate if we need to compute baseflow + ixMatrix, & ! intent(in): form of the Jacobian matrix + ! input: data structures + indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables + dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! input-output: Jacobian and its diagonal + dMat, & ! intent(inout): diagonal of the Jacobian matrix + aJac, & ! intent(out): Jacobian matrix + ! output: error control + out_computJacob) ! intent(out): error code and error message + !err,message) ! intent(out): error code and error message ! ----------------------------------------------------------------------------------------------------------------- implicit none ! input: model control @@ -125,8 +126,9 @@ subroutine computJacob(& real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix real(rkind),intent(out) :: aJac(:,:) ! Jacobian matrix ! output variables - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + !integer(i4b),intent(out) :: err ! error code + !character(*),intent(out) :: message ! error message + type(out_type_computJacob),intent(out) :: out_computJacob ! error control ! -------------------------------------------------------------- ! * local variables ! -------------------------------------------------------------- @@ -257,7 +259,10 @@ subroutine computJacob(& scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1) ,& ! intent(in): [dp] soil control on infiltration, zero or one ! canopy and layer depth canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth )%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth )%dat & ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth )%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! output variables + err => out_computJacob % err ,& ! error code + message => out_computJacob % cmessage & ! error message ) ! making association with data in structures ! -------------------------------------------------------------- ! initialize error control @@ -1052,6 +1057,9 @@ integer(c_int) function computJacob4kinsol(sunvec_y, sunvec_r, sunmat_J, & ! pointers to data in SUNDIALS vectors real(c_double), pointer :: Jac(:,:) ! Jacobian matrix type(data4kinsol), pointer :: eqns_data ! equations data + + ! class objects for subroutine arguments + type(out_type_computJacob) :: out_computJacob ! intent(out) computJacob arguments ! ---------------------------------------------------------------- ! get equations data from user-defined data @@ -1067,7 +1075,7 @@ integer(c_int) function computJacob4kinsol(sunvec_y, sunvec_r, sunmat_J, & ! or in the call to eval8summa in the previous iteration call computJacob(& ! input: model control - eqns_data%dt_cur, & ! intent(in): length of the time step (seconds) + eqns_data%dt_cur, & ! intent(in): length of the time step (seconds) eqns_data%nSnow, & ! intent(in): number of snow layers eqns_data%nSoil, & ! intent(in): number of soil layers eqns_data%nLayers, & ! intent(in): number of layers @@ -1084,13 +1092,23 @@ integer(c_int) function computJacob4kinsol(sunvec_y, sunvec_r, sunmat_J, & eqns_data%dMat, & ! intent(inout): diagonal of the Jacobian matrix Jac, & ! intent(out): Jacobian matrix ! output: error control - eqns_data%err,eqns_data%message) ! intent(out): error code and error message + out_computJacob) ! intent(out): error code and error message + !eqns_data%err,eqns_data%message) ! intent(out): error code and error message + call finalize_computJacob + !eqns_data % err = out_computJacob % err; eqns_data % message = out_computJacob % cmessage ! finalize if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif ! return success ierr = 0 return + + contains + + subroutine finalize_computJacob + ! *** Transfer out_computJacob class object to local variables *** + eqns_data % err = out_computJacob % err; eqns_data % message = out_computJacob % cmessage ! finalize + end subroutine finalize_computJacob end function computJacob4kinsol #endif diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 87a8bb668..5acadc21d 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -223,6 +223,8 @@ subroutine summaSolve4numrec(& integer(i4b) :: jLayer ! column index logical(lgt) :: globalPrintFlagInit ! initial global print flag character(LEN=256) :: cmessage ! error message of downwind routine + ! class objects for subroutine arguments + type(out_type_computJacob) :: out_computJacob ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -269,8 +271,11 @@ subroutine summaSolve4numrec(& dMat, & ! intent(inout): diagonal of the Jacobian matrix aJac, & ! intent(out): Jacobian matrix ! output: error control - err,cmessage) ! intent(out): error code and error message + out_computJacob) + !err,cmessage) ! intent(out): error code and error message end associate ! end association to info in data structures + call finalize_computJacob_summaSolve4numrec(err,cmessage) + !err = out_computJacob % err; cmessage = out_computJacob % cmessage ! finalize if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! compute the numerical Jacobian matrix @@ -852,6 +857,8 @@ subroutine testBandMat(check,err,message) real(rkind) :: bandJac(nLeadDim,nState) ! band Jacobian matrix integer(i4b) :: iState,jState ! indices of the state vector character(LEN=256) :: cmessage ! error message of downwind routine + ! class objects for subroutine arguments + type(out_type_computJacob) :: out_computJacob ! initialize error control err=0; message='testBandMat/' @@ -882,7 +889,10 @@ subroutine testBandMat(check,err,message) dMat, & ! intent(inout): diagonal of the Jacobian matrix fullJac, & ! intent(out): full Jacobian matrix ! output: error control - err,cmessage) ! intent(out): error code and error message + out_computJacob) + !err,cmessage) ! intent(out): error code and error message + call finalize_computJacob_testBandMat(err,cmessage) + !err = out_computJacob % err; cmessage = out_computJacob % cmessage ! finalize if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! initialize band matrix @@ -908,6 +918,19 @@ subroutine testBandMat(check,err,message) end subroutine testBandMat + subroutine finalize_computJacob_testBandMat(err,cmessage) + ! *** Transfer data from out_computJacob class object to local variables in testBandMat *** + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: cmessage ! error message of downwind routine + err = out_computJacob % err; cmessage = out_computJacob % cmessage ! finalize + end subroutine finalize_computJacob_testBandMat + + subroutine finalize_computJacob_summaSolve4numrec(err,cmessage) + ! *** Transfer data from out_computJacob class object to local variables in summaSolve4numrec *** + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: cmessage ! error message of downwind routine + err = out_computJacob % err; cmessage = out_computJacob % cmessage ! finalize + end subroutine finalize_computJacob_summaSolve4numrec ! ********************************************************************************************************* ! * internal subroutine eval8summa_wrapper: compute the right-hand-side vector From 265721b50ee5a183a752b3941318f34d53046acf Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 13 Feb 2024 21:18:49 +0900 Subject: [PATCH 1133/1472] upper bound on feasible for snow changed --- build/source/engine/getVectorz.f90 | 12 ++- build/source/engine/updatState.f90 | 44 +++++------ build/source/engine/updateVarsWithPrime.f90 | 82 ++++++++++----------- 3 files changed, 68 insertions(+), 70 deletions(-) diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index d9bd3bebc..f31bb766c 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -433,6 +433,7 @@ subroutine checkFeas(& theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) ! model diagnostic variables from the previous solution + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) ! number of model layers, and layer type nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers @@ -510,19 +511,16 @@ subroutine checkFeas(& ! --> maximum select case( layerType(iLayer) ) - case(iname_snow); xMax = merge(iden_ice, 1._rkind - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) + case(iname_snow); xMax = merge((iden_ice/iden_water)*mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer), 1._rkind - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) case(iname_soil); xMax = merge(theta_sat(iLayer-nSnow), theta_sat(iLayer-nSnow) - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) end select - if(iLayer==1)then - write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax - print*,layerType(iLayer),iname_snow,mLayerVolFracIce(iLayer),iLayer, ixHydType(iLayer)==iname_watLayer - endif + ! --> check if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax)then feasible=.false. message=trim(message)//'layer water out of bounds' - if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) & - write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax + !if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) & + !write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax endif endif ! if water states diff --git a/build/source/engine/updatState.f90 b/build/source/engine/updatState.f90 index 7ae216c17..3df71b0bd 100644 --- a/build/source/engine/updatState.f90 +++ b/build/source/engine/updatState.f90 @@ -52,13 +52,13 @@ subroutine updateSnow(& USE snow_utils_module,only:fracliquid ! compute volumetric fraction of liquid water implicit none ! input variables - real(rkind),intent(in) :: mLayerTemp ! temperature (K) - real(rkind),intent(in) :: mLayerTheta ! volume fraction of total water (-) - real(rkind),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) + real(rkind),intent(in) :: mLayerTemp ! temperature (K) + real(rkind),intent(in) :: mLayerTheta ! volume fraction of total water (-) + real(rkind),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) ! output variables - real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) - real(rkind),intent(out) :: fLiq ! fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) + real(rkind),intent(out) :: fLiq ! fraction of liquid water (-) ! error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -93,24 +93,24 @@ subroutine updateSoil(& USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric liquid water content implicit none ! input variables - real(rkind),intent(in) :: mLayerTemp ! estimate of temperature (K) - real(rkind),intent(in) :: mLayerMatricHead ! matric head (m) - real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter - real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter - real(rkind),intent(in) :: theta_sat ! soil porosity (-) - real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) - real(rkind),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) + real(rkind),intent(in) :: mLayerTemp ! estimate of temperature (K) + real(rkind),intent(in) :: mLayerMatricHead ! matric head (m) + real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter + real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter + real(rkind),intent(in) :: theta_sat ! soil porosity (-) + real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) + real(rkind),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) ! output variables - real(rkind),intent(out) :: mLayerVolFracWat ! fractional volume of total water (-) - real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + real(rkind),intent(out) :: mLayerVolFracWat ! fractional volume of total water (-) + real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! define local variables - real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) - real(rkind) :: xConst ! constant in the freezing curve function (m K-1) - real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) - real(rkind),parameter :: tinyVal=epsilon(1._rkind) ! used in balance check + real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) + real(rkind) :: xConst ! constant in the freezing curve function (m K-1) + real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) + real(rkind),parameter :: tinyVal=epsilon(1._rkind) ! used in balance check ! initialize error control err=0; message="updateSoil/" diff --git a/build/source/engine/updateVarsWithPrime.f90 b/build/source/engine/updateVarsWithPrime.f90 index 9ad3aa20c..8fc71206a 100644 --- a/build/source/engine/updateVarsWithPrime.f90 +++ b/build/source/engine/updateVarsWithPrime.f90 @@ -211,59 +211,59 @@ subroutine updateVarsWithPrime(& ! make association with variables in the data structures associate(& ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) ! indices defining model states and layers - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) ! indices in the full vector for specific domains - ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain - ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain - ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain - ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain + ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain ! mapping between the full state vector and the state subset - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset ! type of domain, type of state variable, and index of control volume within domain - ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) ! depth-varying model parameters - vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) + vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) ! model diagnostic variables (heat capacity) - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(in): [dp ] volumetric heat capacity of the vegetation (J m-3 K-1) - mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] volumetric heat capacity in each layer (J m-3 K-1) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(in): [dp ] volumetric heat capacity of the vegetation (J m-3 K-1) + mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] volumetric heat capacity in each layer (J m-3 K-1) ! model diagnostic variables (fraction of liquid water) - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) ! model states from a previous solution - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) ! model diagnostic variables from a previous solution - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) ! derivatives - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential - dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) - dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(out): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature - dFracLiqSnow_dTk => deriv_data%var(iLookDERIV%dFracLiqSnow_dTk )%dat ,& ! intent(out): [dp(:)] derivative in fraction of liquid snow w.r.t. temperature - dFracLiqVeg_dTkCanopy => deriv_data%var(iLookDERIV%dFracLiqVeg_dTkCanopy )%dat(1) ,& ! intent(out): [dp ] derivative in fraction of (throughfall + drainage) w.r.t. temperature + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential + dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0)%dat ,& ! intent(out): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) + dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp)%dat ,& ! intent(out): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature + dFracLiqSnow_dTk => deriv_data%var(iLookDERIV%dFracLiqSnow_dTk)%dat ,& ! intent(out): [dp(:)] derivative in fraction of liquid snow w.r.t. temperature + dFracLiqVeg_dTkCanopy => deriv_data%var(iLookDERIV%dFracLiqVeg_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in fraction of (throughfall + drainage) w.r.t. temperature ! derivatives inside solver for Jacobian only - d2VolTot_dPsi02 => deriv_data%var(iLookDERIV%d2VolTot_dPsi02 )%dat ,& ! intent(out): [dp(:)] second derivative in total water content w.r.t. total water matric potential - mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2 )%dat ,& ! intent(out): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature - d2Theta_dTkCanopy2 => deriv_data%var(iLookDERIV%d2Theta_dTkCanopy2 )%dat(1) & ! intent(out): [dp ] second derivative of volumetric liquid water content w.r.t. temperature + d2VolTot_dPsi02 => deriv_data%var(iLookDERIV%d2VolTot_dPsi02)%dat ,& ! intent(out): [dp(:)] second derivative in total water content w.r.t. total water matric potential + mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2)%dat ,& ! intent(out): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature + d2Theta_dTkCanopy2 => deriv_data%var(iLookDERIV%d2Theta_dTkCanopy2)%dat(1) & ! intent(out): [dp ] second derivative of volumetric liquid water content w.r.t. temperature ) ! association with variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- From e4e52298f992cbc0016af4f000fd88ea56417996 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 14 Feb 2024 14:12:22 +0900 Subject: [PATCH 1134/1472] upper bound in feasibility should be not dependent on liq and ice --- build/source/engine/getVectorz.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index f31bb766c..033ed4c9b 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -511,7 +511,7 @@ subroutine checkFeas(& ! --> maximum select case( layerType(iLayer) ) - case(iname_snow); xMax = merge((iden_ice/iden_water)*mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer), 1._rkind - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) + case(iname_snow); xMax = merge(1._rkind, 1._rkind - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) case(iname_soil); xMax = merge(theta_sat(iLayer-nSnow), theta_sat(iLayer-nSnow) - mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) end select From a85e140a12670f86b0207f02c87dc26bc418994c Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 14 Feb 2024 03:21:50 -0600 Subject: [PATCH 1135/1472] Implemented class objects to handle intent(in) arguments for calls to computJacob. --- build/source/engine/computJacob.f90 | 76 ++++++++++------------- build/source/engine/summaSolve4numrec.f90 | 75 +++++++--------------- 2 files changed, 57 insertions(+), 94 deletions(-) diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 3069a8f86..41fe19c23 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -87,13 +87,7 @@ module computJacob_module ! ********************************************************************************************************** subroutine computJacob(& ! input: model control - dt, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - computeBaseflow, & ! intent(in): flag to indicate if we need to compute baseflow - ixMatrix, & ! intent(in): form of the Jacobian matrix + in_computJacob, & ! intent(in): model control ! input: data structures indx_data, & ! intent(in): index data prog_data, & ! intent(in): model prognostic variables for a local HRU @@ -105,29 +99,20 @@ subroutine computJacob(& aJac, & ! intent(out): Jacobian matrix ! output: error control out_computJacob) ! intent(out): error code and error message - !err,message) ! intent(out): error code and error message ! ----------------------------------------------------------------------------------------------------------------- implicit none ! input: model control - real(rkind),intent(in) :: dt ! length of the time step (seconds) - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: computeBaseflow ! flag to indicate if computing baseflow - integer(i4b),intent(in) :: ixMatrix ! form of the Jacobian matrix + type(in_type_computJacob),intent(in) :: in_computJacob ! model control ! input: data structures - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! input-output: Jacobian and its diagonal - real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix - real(rkind),intent(out) :: aJac(:,:) ! Jacobian matrix + real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix + real(rkind),intent(out) :: aJac(:,:) ! Jacobian matrix ! output variables - !integer(i4b),intent(out) :: err ! error code - !character(*),intent(out) :: message ! error message type(out_type_computJacob),intent(out) :: out_computJacob ! error control ! -------------------------------------------------------------- ! * local variables @@ -147,13 +132,21 @@ subroutine computJacob(& ! -------------------------------------------------------------- ! associate variables from data structures associate(& + ! model control + dt => in_computJacob % dt ,& ! intent(in): length of the time step (seconds) + nSnow => in_computJacob % nSnow ,& ! intent(in): number of snow layers + nSoil => in_computJacob % nSoil ,& ! intent(in): number of soil layers + nLayers => in_computJacob % nLayers ,& ! intent(in): total number of layers in the snow+soil domain + computeVegFlux => in_computJacob % computeVegFlux ,& ! intent(in): flag to indicate if computing fluxes over vegetation + computeBaseflow => in_computJacob % computeBaseflow ,& ! intent(in): flag to indicate if computing baseflow + ixMatrix => in_computJacob % ixMatrix ,& ! intent(in): form of the Jacobian matrix ! indices of model state variables - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain - ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain + ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer ! vectors of indices for specfic state types within specific sub-domains IN THE FULL STATE VECTOR ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain ! vector of energy indices for the snow and soil domains @@ -1059,6 +1052,7 @@ integer(c_int) function computJacob4kinsol(sunvec_y, sunvec_r, sunmat_J, & type(data4kinsol), pointer :: eqns_data ! equations data ! class objects for subroutine arguments + type(in_type_computJacob) :: in_computJacob ! intent(in) computJacob arguments type(out_type_computJacob) :: out_computJacob ! intent(out) computJacob arguments ! ---------------------------------------------------------------- @@ -1073,15 +1067,10 @@ integer(c_int) function computJacob4kinsol(sunvec_y, sunvec_r, sunmat_J, & ! NOTE: The derivatives were computed in the previous call to computFlux ! This occurred either at the call to eval8summa at the start of systemSolv ! or in the call to eval8summa in the previous iteration + call initialize_computJacob ! pack in_computJacob object call computJacob(& ! input: model control - eqns_data%dt_cur, & ! intent(in): length of the time step (seconds) - eqns_data%nSnow, & ! intent(in): number of snow layers - eqns_data%nSoil, & ! intent(in): number of soil layers - eqns_data%nLayers, & ! intent(in): number of layers - eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - (eqns_data%model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow - eqns_data%ixMatrix, & ! intent(in): type of matrix (dense or banded) + in_computJacob, & ! input: data structures eqns_data%indx_data, & ! intent(in): index data eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU @@ -1093,9 +1082,7 @@ integer(c_int) function computJacob4kinsol(sunvec_y, sunvec_r, sunmat_J, & Jac, & ! intent(out): Jacobian matrix ! output: error control out_computJacob) ! intent(out): error code and error message - !eqns_data%err,eqns_data%message) ! intent(out): error code and error message - call finalize_computJacob - !eqns_data % err = out_computJacob % err; eqns_data % message = out_computJacob % cmessage ! finalize + call finalize_computJacob ! unpack out_computJacob object if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif @@ -1105,9 +1092,14 @@ integer(c_int) function computJacob4kinsol(sunvec_y, sunvec_r, sunmat_J, & contains + subroutine initialize_computJacob + ! *** Transfer data to in_computJacob class object from local variables *** + call in_computJacob % initialize(eqns_data%dt_cur,eqns_data%nSnow,eqns_data%nSoil,eqns_data%nLayers,eqns_data%computeVegFlux,(eqns_data%model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel),eqns_data%ixMatrix) + end subroutine initialize_computJacob + subroutine finalize_computJacob - ! *** Transfer out_computJacob class object to local variables *** - eqns_data % err = out_computJacob % err; eqns_data % message = out_computJacob % cmessage ! finalize + ! *** Transfer data from out_computJacob class object to local variables *** + call out_computJacob % finalize(eqns_data % err,eqns_data % message) end subroutine finalize_computJacob end function computJacob4kinsol diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 5acadc21d..114909001 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -224,6 +224,7 @@ subroutine summaSolve4numrec(& logical(lgt) :: globalPrintFlagInit ! initial global print flag character(LEN=256) :: cmessage ! error message of downwind routine ! class objects for subroutine arguments + type(in_type_computJacob) :: in_computJacob type(out_type_computJacob) :: out_computJacob ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- @@ -251,31 +252,9 @@ subroutine summaSolve4numrec(& ! NOTE: The derivatives were computed in the previous call to computFlux ! This occurred either at the call to eval8summa at the start of systemSolv ! or in the call to eval8summa in the previous iteration (within lineSearchRefinement or trustRegionRefinement) - associate(ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision) ! intent(in): [i4b] groundwater parameterization - call computJacob(& - ! input: model control - dt_cur, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - (ixGroundwater==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow - ixMatrix, & ! intent(in): form of the Jacobian matrix - ! input: data structures - indx_data, & ! intent(in): index data - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! input-output: Jacobian and its diagonal - dMat, & ! intent(inout): diagonal of the Jacobian matrix - aJac, & ! intent(out): Jacobian matrix - ! output: error control - out_computJacob) - !err,cmessage) ! intent(out): error code and error message - end associate ! end association to info in data structures - call finalize_computJacob_summaSolve4numrec(err,cmessage) - !err = out_computJacob % err; cmessage = out_computJacob % cmessage ! finalize + call initialize_computJacob_summaSolve4numrec + call computJacob(in_computJacob,indx_data,prog_data,diag_data,deriv_data,dBaseflow_dMatric,dMat,aJac,out_computJacob) + call finalize_computJacob_summaSolve4numrec if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! compute the numerical Jacobian matrix @@ -858,6 +837,7 @@ subroutine testBandMat(check,err,message) integer(i4b) :: iState,jState ! indices of the state vector character(LEN=256) :: cmessage ! error message of downwind routine ! class objects for subroutine arguments + type(in_type_computJacob) :: in_computJacob type(out_type_computJacob) :: out_computJacob ! initialize error control err=0; message='testBandMat/' @@ -870,29 +850,9 @@ subroutine testBandMat(check,err,message) endif ! compute the full Jacobian matrix - call computJacob(& - ! input: model control - dt_cur, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - .false., & ! intent(in): flag to indicate if we need to compute baseflow - ixFullMatrix, & ! intent(in): force full Jacobian matrix - ! input: data structures - indx_data, & ! intent(in): index data - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! input-output: Jacobian and its diagonal - dMat, & ! intent(inout): diagonal of the Jacobian matrix - fullJac, & ! intent(out): full Jacobian matrix - ! output: error control - out_computJacob) - !err,cmessage) ! intent(out): error code and error message + call initialize_computJacob_testBandMat + call computJacob(in_computJacob,indx_data,prog_data,diag_data,deriv_data,dBaseflow_dMatric,dMat,fullJac,out_computJacob) call finalize_computJacob_testBandMat(err,cmessage) - !err = out_computJacob % err; cmessage = out_computJacob % cmessage ! finalize if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! initialize band matrix @@ -918,18 +878,29 @@ subroutine testBandMat(check,err,message) end subroutine testBandMat + subroutine initialize_computJacob_testBandMat + ! *** Transfer data from out_computJacob class object to local variables in testBandMat *** + call in_computJacob % initialize(dt_cur,nSnow,nSoil,nLayers,computeVegFlux,.false.,ixFullMatrix) + end subroutine initialize_computJacob_testBandMat + subroutine finalize_computJacob_testBandMat(err,cmessage) ! *** Transfer data from out_computJacob class object to local variables in testBandMat *** + ! Note: subroutine arguments are needed because testBandMat is an internal procedure integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: cmessage ! error message of downwind routine - err = out_computJacob % err; cmessage = out_computJacob % cmessage ! finalize + call out_computJacob % finalize(err,cmessage) end subroutine finalize_computJacob_testBandMat - subroutine finalize_computJacob_summaSolve4numrec(err,cmessage) + subroutine initialize_computJacob_summaSolve4numrec + ! *** Transfer data to in_computJacob class object from local variables in summaSolve4numrec *** + associate(ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision) ! intent(in): [i4b] groundwater parameterization + call in_computJacob % initialize(dt_cur,nSnow,nSoil,nLayers,computeVegFlux,(ixGroundwater==qbaseTopmodel),ixMatrix) + end associate + end subroutine initialize_computJacob_summaSolve4numrec + + subroutine finalize_computJacob_summaSolve4numrec ! *** Transfer data from out_computJacob class object to local variables in summaSolve4numrec *** - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: cmessage ! error message of downwind routine - err = out_computJacob % err; cmessage = out_computJacob % cmessage ! finalize + call out_computJacob % finalize(err,cmessage) end subroutine finalize_computJacob_summaSolve4numrec ! ********************************************************************************************************* From 523774e1df36b8db9de43c818215a5cb0ae50845 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 15 Feb 2024 14:00:41 +0900 Subject: [PATCH 1136/1472] correcting constraints if soil iname_watLayer, iname_liqLayer, might want to do more on this --- build/source/engine/eval8summa.f90 | 75 +++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 21 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index a10871d14..6e891496e 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -34,8 +34,11 @@ module eval8summa_module ! constants USE multiconst,only:& - Tfreeze, & ! temperature at freezing (K) - iden_water ! intrinsic density of liquid water (kg m-3) + LH_fus, & ! latent heat of fusion (J kg-1) + iden_water, & ! intrinsic density of liquid water (kg m-3) + gravity, & ! gravitational acceleration (m s-2) + Tfreeze ! freezing point of pure water (K) + ! provide access to the derived types to define the data structures USE data_types,only:& @@ -781,8 +784,11 @@ end function eval8summa4kinsol subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, stateVec, stateVecPrev,& nState, nSoil, nSnow, message, err) ! external functions - USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water at a given temperature (snow) - USE soil_utils_module,only:crit_soilT ! compute the critical temperature below which ice exists + USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water at a given temperature (snow) + USE soil_utils_module,only:crit_soilT ! compute the critical temperature below which ice exists + USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water + implicit none type(model_options),intent(in) :: model_decisions(:) ! model decisions @@ -804,13 +810,16 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st integer(i4b) :: iMax(1) ! index of maximum temperature real(rkind) :: scalarTemp ! temperature of an individual snow layer (K) real(rkind) :: scalarIce ! volumetric ice content of an individual layer (-) - real(rkind) :: volFracLiq ! volumetric liquid water content of an individual layer (-) + real(rkind) :: vFracLiq ! volumetric liquid water content of an individual layer (-) real(rkind) :: xPsi00 ! matric head after applying the iteration increment (m) real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) real(rkind) :: critDiff ! temperature difference from critical (K) real(rkind) :: epsT ! small interval above/below critical (K) real(rkind) :: zMaxTempIncrement ! maximum temperature increment (K) real(rkind) :: zMaxMatricIncrement ! maximum matric head increment (m) + real(rkind) :: xConst ! constant in the freezing curve function (m K-1) + real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) + real(rkind) :: vGn_m(nSoil) ! van Genutchen "m" parameter (-) ! indices of model state variables integer(i4b) :: iState ! index of state within a specific variable type integer(i4b) :: ixNrg,ixLiq ! index of energy and mass state variables in full state vector @@ -860,6 +869,8 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st ! soil parameters theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) ! state variables at the start of the time step mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] matric head (m) mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat & ! intent(in): [dp(:)] volumetric fraction of ice (-) @@ -893,7 +904,9 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st water_bounds = .true. ! flag to force water bounds case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or numrec (or itertive, which is numrec)'; return end select - + + vGn_m = 1._rkind - 1._rkind/vGn_n + ! ** limit temperature increment to zMaxTempIncrement ! NOTE: this can cause problems especially from a cold start when far from the solution if(small_delTemp)then @@ -968,7 +981,7 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st ixLiq = ixSoilOnlyHyd(iLayer) ! get the matric potential of total water if(ixLiq/=integerMissing)then - xPsi00 = stateVecPrev(ixLiq) + xInc(ixLiq) + xPsi00 = stateVecPrev(ixLiq) + xInc(ixLiq) ! only true if using iname_matLayer, otherwise may want to fix this else xPsi00 = mLayerMatricHead(iLayer) endif @@ -1023,16 +1036,16 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st endif ! get the volumetric fraction of liquid water and ice select case( ixStateType_subset( ixSnowOnlyHyd(iLayer) ) ) - case(iname_watLayer); volFracLiq = fracliquid(scalarTemp,mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1)) * stateVecPrev(ixSnowOnlyHyd(iLayer)) - case(iname_liqLayer); volFracLiq = stateVecPrev(ixSnowOnlyHyd(iLayer)) + case(iname_watLayer); vFracLiq = fracliquid(scalarTemp,mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1)) * stateVecPrev(ixSnowOnlyHyd(iLayer)) + case(iname_liqLayer); vFracLiq = stateVecPrev(ixSnowOnlyHyd(iLayer)) case default; err=20; message=trim(message)//'expect ixStateType_subset to be iname_watLayer or iname_liqLayer for snow hydrology'; return end select - scalarIce = mLayerVolFracIce(iLayer) + scalarIce = merge(stateVecPrev(ixSnowOnlyHyd(iLayer)) - vFracLiq,mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) ! checking if drain more than what is available or add more than possible - if(-xInc(ixSnowOnlyHyd(iLayer)) > volFracLiq)then - cInc = -0.5_rkind*volFracLiq - elseif(xInc(ixSnowOnlyHyd(iLayer)) > 1._rkind - scalarIce - volFracLiq)then - cInc = 0.5_rkind*(1._rkind - scalarIce - volFracLiq) + if(-xInc(ixSnowOnlyHyd(iLayer)) > vFracLiq)then + cInc = -0.5_rkind*vFracLiq + elseif(xInc(ixSnowOnlyHyd(iLayer)) > 1._rkind - scalarIce - vFracLiq)then + cInc = 0.5_rkind*(1._rkind - scalarIce - vFracLiq) endif ! scale iteration increment if(xInc(ixSnowOnlyHyd(iLayer)).ne.0._rkind)then @@ -1050,14 +1063,34 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle cInc = xInc(ixSoilOnlyHyd(iLayer)) if(ixHydType(iLayer+nSnow)==iname_watLayer .or. ixHydType(iLayer+nSnow)==iname_liqLayer)then - ! get the volumetric fraction of liquid water - volFracLiq = stateVecPrev(ixSoilOnlyHyd(iLayer)) - scalarIce = merge(0._rkind, mLayerVolFracIce(iLayer+nSnow), ixHydType(iLayer+nSnow)==iname_watLayer) + ! get the volumetric fraction of liquid water and ice + select case( ixStateType_subset( ixSnowOnlyHyd(iLayer) ) ) + case(iname_watLayer) + xPsi00 = matricHead(stateVecPrev(ixSoilOnlyHyd(iLayer)),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) + if(ixSoilOnlyNrg(iLayer)/=integerMissing)then + ! get the layer temperature (from stateVecPrev if ixSnowOnlyNrg(iLayer) is within the state vector + scalarTemp = stateVecPrev( ixSoilOnlyNrg(iLayer) ) + else ! get the layer temperature from the last update + scalarTemp = prog_data%var(iLookPROG%mLayerTemp)%dat(iLayer+nSnow) + endif + ! identify the critical point when soil begins to freeze (TcSoil) + TcSoil = crit_soilT(xPsi00) + if(scalarTemp < TcSoil)then + xConst = LH_fus/(gravity*Tfreeze) + mLayerPsiLiq = xConst*(scalarTemp - Tfreeze) + vFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) + else !( mLayerTemp >= TcSoil, all water is unfrozen, mLayerPsiLiq = mLayerMatricHead ) + vFracLiq = stateVecPrev(ixSoilOnlyHyd(iLayer)) + end if ! (check if soil is partially frozen) + case(iname_liqLayer); vFracLiq = stateVecPrev(ixSoilOnlyHyd(iLayer)) + end select + scalarIce = merge(stateVecPrev(ixSnowOnlyHyd(iLayer)) - vFracLiq,mLayerVolFracIce(iLayer+nSnow), ixHydType(iLayer)==iname_watLayer) + ! checking if drain more than what is available or add more than possible - if(-xInc(ixSoilOnlyHyd(iLayer)) > volFracLiq - theta_res(iLayer))then - cInc = -0.5_rkind*(volFracLiq - theta_res(iLayer)) - elseif(xInc(ixSoilOnlyHyd(iLayer)) > theta_sat(iLayer) - scalarIce - volFracLiq)then - cInc = -0.5_rkind*(theta_sat(iLayer) - scalarIce - volFracLiq) + if(-xInc(ixSoilOnlyHyd(iLayer)) > vFracLiq - theta_res(iLayer))then + cInc = -0.5_rkind*(vFracLiq - theta_res(iLayer)) + elseif(xInc(ixSoilOnlyHyd(iLayer)) > theta_sat(iLayer) - scalarIce - vFracLiq)then + cInc = 0.5_rkind*(theta_sat(iLayer) - scalarIce - vFracLiq) endif endif ! (if the state variable is not matric head) ! scale iteration increment From 6abee4f4c0c8d366a80144fa4a786c8fc0ed24be Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 15 Feb 2024 14:18:47 +0900 Subject: [PATCH 1137/1472] ignore file --- .../output/celia1990/celia1990_timestep.nc | Bin 497224 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test_ngen/celia_test/output/celia1990/celia1990_timestep.nc diff --git a/test_ngen/celia_test/output/celia1990/celia1990_timestep.nc b/test_ngen/celia_test/output/celia1990/celia1990_timestep.nc deleted file mode 100644 index ccc1832534e9bcd7cfc178c2445fc49d810a4c09..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 497224 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 Date: Thu, 15 Feb 2024 15:13:50 +0900 Subject: [PATCH 1138/1472] make scaled iteration increment only on the state variable that is violating the constraint, not the whole increment vector --- build/source/engine/eval8summa.f90 | 97 ++++++++++-------------------- 1 file changed, 31 insertions(+), 66 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 6e891496e..12b09449f 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -804,10 +804,7 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st character(len=256),intent(out) :: message ! error message ! ----------------------------------------------------------------------------------------------------- ! temporary variables for model constraints - real(rkind) :: cInc ! constrained temperature increment (K) -- simplified bi-section real(qp),dimension(nState) :: xInc ! iteration increment - real(rkind) :: xIncFactor ! scaling factor for the iteration increment (-) - integer(i4b) :: iMax(1) ! index of maximum temperature real(rkind) :: scalarTemp ! temperature of an individual snow layer (K) real(rkind) :: scalarIce ! volumetric ice content of an individual layer (-) real(rkind) :: vFracLiq ! volumetric liquid water content of an individual layer (-) @@ -910,10 +907,14 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st ! ** limit temperature increment to zMaxTempIncrement ! NOTE: this can cause problems especially from a cold start when far from the solution if(small_delTemp)then - if(any(abs(xInc(ixNrgOnly)) > zMaxTempIncrement))then - iMax = maxloc( abs(xInc(ixNrgOnly)) ) ! index of maximum temperature increment - xIncFactor = abs( zMaxTempIncrement/xInc(ixNrgOnly(iMax(1))) ) ! scaling factor for the iteration increment (-) - xInc = xIncFactor*xInc + if(size(ixNrgOnly)>0)then + ! loop through snow+soil layers + do iState=1,size(ixNrgOnly) + ! define index of the energy state variable within the state subset + ixNrg = ixNrgOnly(iState) + ! place constraint for temperature + if(abs(xInc(ixNrg)) > zMaxTempIncrement) xInc(ixNrg) = sign(zMaxTempIncrement, xInc(ixNrg)) + end do ! (loop through snow+soil layers) endif endif ! (small temperature change) @@ -924,11 +925,8 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st do iState=1,size(ixMatOnly) ! define index of the hydrology state variable within the state subset ixLiq = ixMatOnly(iState) - ! place constraint for matric head, scale iterations - if(xInc(ixLiq) > zMaxMatricIncrement .and. stateVecPrev(ixLiq) > 0._rkind)then - xIncFactor = zMaxMatricIncrement/xInc(ixLiq) ! scaling factor for the iteration increment (-) - xInc = zMaxMatricIncrement - endif + ! place constraint for matric head + if(xInc(ixLiq) > zMaxMatricIncrement .and. stateVecPrev(ixLiq) > 0._rkind) xInc(ixLiq) = zMaxMatricIncrement end do ! (loop through soil layers) endif endif ! (small matric head change) @@ -940,19 +938,13 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st if(ixVegNrg/=integerMissing)then ! initialize critDiff = Tfreeze - stateVecPrev(ixVegNrg) - cInc = xInc(ixVegNrg) ! initially frozen (T < Tfreeze) if(critDiff > 0._rkind)then ! (check crossing above zero) - if(xInc(ixVegNrg) > critDiff) cInc = critDiff + epsT ! constrained temperature increment (K) + if(xInc(ixVegNrg) > critDiff) xInc(ixVegNrg) = critDiff + epsT ! constrained temperature increment (K) ! initially unfrozen (T > Tfreeze) else ! (check crossing below zero) - if(xInc(ixVegNrg) < critDiff) cInc = critDiff - epsT ! constrained temperature increment (K) + if(xInc(ixVegNrg) < critDiff) xInc(ixVegNrg) = critDiff - epsT ! constrained temperature increment (K) end if ! (switch between initially frozen and initially unfrozen) - ! scale iterations - if(xInc(ixVegNrg).ne.0._rkind)then - xIncFactor = cInc/xInc(ixVegNrg) ! scaling factor for the iteration increment (-) - xInc = xIncFactor*xInc ! scale iteration increments - endif endif ! if the state variable for canopy temperature is included within the state subset ! crossing freezing point event for snow, keep it below freezing @@ -962,12 +954,8 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st if(ixSnowOnlyNrg(iLayer)==integerMissing) cycle ! check temperatures, and, if necessary, scale iteration increment iState = ixSnowOnlyNrg(iLayer) - if(stateVecPrev(iState) + xInc(iState) > Tfreeze)then - ! scale iteration increment - cInc = 0.5_rkind*(Tfreeze - stateVecPrev(iState) ) ! constrained temperature increment (K) -- simplified bi-section - xIncFactor = cInc/xInc(iState) ! scaling factor for the iteration increment (-) - xInc = xIncFactor*xInc - endif ! (if snow temperature > freezing) + ! constrained temperature increment (K) -- simplified bi-section + if(stateVecPrev(iState) + xInc(iState) > Tfreeze) xInc(iState) = 0.5_rkind*(Tfreeze - stateVecPrev(iState) ) end do ! (loop through snow layers) endif ! (if there are state variables for energy in the snow domain) @@ -989,19 +977,13 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st TcSoil = crit_soilT(xPsi00) ! get the difference from the current state and the crossing point (K) critDiff = TcSoil - stateVecPrev(ixNrg) - cInc = xInc(ixNrg) ! initially frozen (T < TcSoil) if(critDiff > 0._rkind)then ! (check crossing above zero) - if(xInc(ixNrg) > critDiff) cInc = critDiff + epsT ! set iteration increment to slightly above critical temperature + if(xInc(ixNrg) > critDiff) xInc(ixNrg) = critDiff + epsT ! set iteration increment to slightly above critical temperature ! initially unfrozen (T > TcSoil) else ! (check crossing below zero) - if(xInc(ixNrg) < critDiff) cInc = critDiff - epsT ! set iteration increment to slightly below critical temperature + if(xInc(ixNrg) < critDiff) xInc(ixNrg) = critDiff - epsT ! set iteration increment to slightly below critical temperature endif ! (switch between initially frozen and initially unfrozen) - ! scale iterations - if(xInc(ixNrg).ne.0._rkind)then - xIncFactor = cInc/xInc(ixNrg) ! scaling factor for the iteration increment (-) - xInc = xIncFactor*xInc ! scale iteration increments - endif end do ! (loop through soil layers) endif ! (if there are both energy and liquid water state variables) @@ -1012,13 +994,8 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st ! impose positivity for canopy liquid water if(ixVegHyd/=integerMissing)then - ! check if new value of storage will be negative - if(stateVecPrev(ixVegHyd)+xInc(ixVegHyd) < 0._rkind)then - ! scale iteration increment - cInc = -0.5_rkind*stateVecPrev(ixVegHyd) ! constrained iteration increment (K) -- simplified bi-section - xIncFactor = cInc/xInc(ixVegHyd) ! scaling factor for the iteration increment (-) - xInc = xIncFactor*xInc ! new iteration increment - end if + ! constrained iteration increment (K) -- simplified bi-section + if(stateVecPrev(ixVegHyd) + xInc(ixVegHyd) < 0._rkind) xInc(ixVegHyd) = -0.5_rkind*stateVecPrev(ixVegHyd) endif ! (if the state variable for canopy water is included within the state subset) ! impose bounds for snow water, change in total water is only due to liquid flux @@ -1027,7 +1004,6 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st do iLayer=1,nSnow ! check if the layer is included if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle - cInc = xInc(ixSnowOnlyHyd(iLayer)) if(ixSnowOnlyNrg(iLayer)/=integerMissing)then ! get the layer temperature (from stateVecPrev if ixSnowOnlyNrg(iLayer) is within the state vector scalarTemp = stateVecPrev( ixSnowOnlyNrg(iLayer) ) @@ -1041,16 +1017,11 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st case default; err=20; message=trim(message)//'expect ixStateType_subset to be iname_watLayer or iname_liqLayer for snow hydrology'; return end select scalarIce = merge(stateVecPrev(ixSnowOnlyHyd(iLayer)) - vFracLiq,mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) - ! checking if drain more than what is available or add more than possible - if(-xInc(ixSnowOnlyHyd(iLayer)) > vFracLiq)then - cInc = -0.5_rkind*vFracLiq + ! checking if drain more than what is available or add more than possible, constrained iteration increment -- simplified bi-section + if(-xInc(ixSnowOnlyHyd(iLayer)) > vFracLiq) then + xInc(ixSnowOnlyHyd(iLayer)) = -0.5_rkind*vFracLiq elseif(xInc(ixSnowOnlyHyd(iLayer)) > 1._rkind - scalarIce - vFracLiq)then - cInc = 0.5_rkind*(1._rkind - scalarIce - vFracLiq) - endif - ! scale iteration increment - if(xInc(ixSnowOnlyHyd(iLayer)).ne.0._rkind)then - xIncFactor = cInc/xInc(ixSnowOnlyHyd(iLayer)) ! scaling factor for the iteration increment (-) - xInc = xIncFactor*xInc ! new iteration increment + xInc(ixSnowOnlyHyd(iLayer)) = 0.5_rkind*(1._rkind - scalarIce - vFracLiq) endif end do ! (looping through snow layers) endif ! (if there are state variables for liquid water in the snow domain) @@ -1061,43 +1032,37 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st do iLayer=1,nSoil ! check if the layer is included if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle - cInc = xInc(ixSoilOnlyHyd(iLayer)) if(ixHydType(iLayer+nSnow)==iname_watLayer .or. ixHydType(iLayer+nSnow)==iname_liqLayer)then - ! get the volumetric fraction of liquid water and ice + ! get the volumetric fraction of liquid water and ice select case( ixStateType_subset( ixSnowOnlyHyd(iLayer) ) ) case(iname_watLayer) xPsi00 = matricHead(stateVecPrev(ixSoilOnlyHyd(iLayer)),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) + ! get the layer temperature if(ixSoilOnlyNrg(iLayer)/=integerMissing)then - ! get the layer temperature (from stateVecPrev if ixSnowOnlyNrg(iLayer) is within the state vector scalarTemp = stateVecPrev( ixSoilOnlyNrg(iLayer) ) - else ! get the layer temperature from the last update + else scalarTemp = prog_data%var(iLookPROG%mLayerTemp)%dat(iLayer+nSnow) endif ! identify the critical point when soil begins to freeze (TcSoil) TcSoil = crit_soilT(xPsi00) - if(scalarTemp < TcSoil)then + ! get the volumetric fraction of liquid water and ice + if(scalarTemp < TcSoil)then xConst = LH_fus/(gravity*Tfreeze) mLayerPsiLiq = xConst*(scalarTemp - Tfreeze) vFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) - else !( mLayerTemp >= TcSoil, all water is unfrozen, mLayerPsiLiq = mLayerMatricHead ) + else vFracLiq = stateVecPrev(ixSoilOnlyHyd(iLayer)) end if ! (check if soil is partially frozen) case(iname_liqLayer); vFracLiq = stateVecPrev(ixSoilOnlyHyd(iLayer)) end select scalarIce = merge(stateVecPrev(ixSnowOnlyHyd(iLayer)) - vFracLiq,mLayerVolFracIce(iLayer+nSnow), ixHydType(iLayer)==iname_watLayer) - - ! checking if drain more than what is available or add more than possible + ! checking if drain more than what is available or add more than possible, constrained iteration increment -- simplified bi-section if(-xInc(ixSoilOnlyHyd(iLayer)) > vFracLiq - theta_res(iLayer))then - cInc = -0.5_rkind*(vFracLiq - theta_res(iLayer)) + xInc(ixSoilOnlyHyd(iLayer)) = -0.5_rkind*(vFracLiq - theta_res(iLayer)) elseif(xInc(ixSoilOnlyHyd(iLayer)) > theta_sat(iLayer) - scalarIce - vFracLiq)then - cInc = 0.5_rkind*(theta_sat(iLayer) - scalarIce - vFracLiq) + xInc(ixSoilOnlyHyd(iLayer)) = 0.5_rkind*(theta_sat(iLayer) - scalarIce - vFracLiq) endif endif ! (if the state variable is not matric head) - ! scale iteration increment - if(xInc(ixSoilOnlyHyd(iLayer)).ne.0._rkind)then - xIncFactor = cInc/xInc(ixSoilOnlyHyd(iLayer)) ! scaling factor for the iteration increment (-) - xInc = xIncFactor*xInc ! new iteration increment - endif end do ! (looping through soil layers) endif ! (if there are state variables for liquid water in the soil domain) From 11a47b2bb0310c603b93354187450ccf442b70ce Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 15 Feb 2024 18:57:11 +0900 Subject: [PATCH 1139/1472] fix initialized ixSubset in opSplittin --- build/source/engine/opSplittin.f90 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index ca7064996..1e03b7db6 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -1452,6 +1452,10 @@ subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) case default; err=20; message=trim(message)//'unable to identify coupling method'; return end select ! selecting solution method end associate + + ! initialize ixSubset + allocate(ixSubset(1_i4b)) + ixSubset = 0._rkind call identify_scalar_solutions; if (return_flag) return ! identify scalar solutions -- return if error occurs From eb47078efdd72751079c9a19ff27b7cf076cb6cc Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 15 Feb 2024 18:57:11 +0900 Subject: [PATCH 1140/1472] fix initialized ixSubset in opSplittin --- build/source/engine/opSplittin.f90 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index ca7064996..1e03b7db6 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -1452,6 +1452,10 @@ subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) case default; err=20; message=trim(message)//'unable to identify coupling method'; return end select ! selecting solution method end associate + + ! initialize ixSubset + allocate(ixSubset(1_i4b)) + ixSubset = 0._rkind call identify_scalar_solutions; if (return_flag) return ! identify scalar solutions -- return if error occurs From 9318c5eee23c7f42b254049e06e82dcb0d3b47c6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 13 Feb 2024 13:03:54 +0900 Subject: [PATCH 1141/1472] fix uninitialized converged variable --- build/source/engine/summaSolve4numrec.f90 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 36b7bf29b..edcd22c5b 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -395,6 +395,7 @@ subroutine lineSearchRefinement(doLineSearch,stateVecTrial,newtStepScaled,aJacSc ! -------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='lineSearchRefinement/' + converged =.false. ! check the need to compute the line search if(doLineSearch)then @@ -549,6 +550,7 @@ subroutine trustRegionRefinement(doTrustRefinement,stateVecTrial,newtStepScaled, ! -------------------------------------------------------------------------------------------------------- err=0; message='trustRegionRefinement/' + converged =.false. ! check the need to refine the step if(doTrustRefinement)then @@ -617,6 +619,7 @@ subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fl real(rkind),parameter :: delX=1._rkind ! trial increment ! -------------------------------------------------------------------------------------------------------- err=0; message='safeRootfinder/' + converged = .false. ! check scalar if(size(stateVecTrial)/=1 .or. size(rVecScaled)/=1 .or. size(newtStepScaled)/=1)then From 21f082ad9be303f55d9d30eeda40f49d7113937f Mon Sep 17 00:00:00 2001 From: ashleymedin <34691332+ashleymedin@users.noreply.github.com> Date: Fri, 16 Feb 2024 11:16:05 +0900 Subject: [PATCH 1142/1472] Merge pull request #34 from seantrim/develop_refactor_Newton Refactoring the Newton solver: applied object-oriented methods for calls to computJacob --- build/source/dshare/data_types.f90 | 56 +++++++++- build/source/engine/computJacob.f90 | 120 +++++++++++--------- build/source/engine/summaSolve4numrec.f90 | 128 +++++++++++----------- 3 files changed, 182 insertions(+), 122 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index a4c2b068b..1dd37de88 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -617,7 +617,6 @@ MODULE data_types end type out_type_indexSplit ! ** end indexSplit - ! ** varSubstep type, public :: in_type_varSubstep ! class for intent(in) arguments in varSubstep call real(rkind) :: dt ! intent(in): time step (s) @@ -657,6 +656,31 @@ MODULE data_types end type out_type_varSubstep ! ** end varSubstep + ! *********************************************************************************************************** + ! Define classes used to simplify calls to the subrotuines in summaSolve4numrec + ! *********************************************************************************************************** + + type, public :: in_type_computJacob ! class for intent(in) arguments in computFlux call + ! input: model control + real(rkind) :: dt ! intent(in): length of the time step (seconds) + integer(i4b) :: nSnow ! intent(in): number of snow layers + integer(i4b) :: nSoil ! intent(in): number of soil layers + integer(i4b) :: nLayers ! intent(in): total number of layers in the snow+soil domain + logical(lgt) :: computeVegFlux ! intent(in): flag to indicate if computing fluxes over vegetation + logical(lgt) :: computeBaseflow ! intent(in): flag to indicate if computing baseflow + integer(i4b) :: ixMatrix ! intent(in): form of the Jacobian matrix + contains + procedure :: initialize => initialize_in_computJacob + end type in_type_computJacob + + type, public :: out_type_computJacob ! class for intent(in) arguments in computFlux call + ! output: error control + integer(i4b) :: err ! intent(out): error code + character(len=len_msg) :: cmessage ! intent(out): error message + contains + procedure :: finalize => finalize_out_computJacob + end type out_type_computJacob + contains ! **** vegNrgFlux **** @@ -1419,4 +1443,34 @@ subroutine finalize_out_varSubstep(out_varSubstep,dtMultiplier,nSubsteps,failedM end subroutine finalize_out_varSubstep ! **** end varSubstep **** + ! **** computJacob **** + subroutine initialize_in_computJacob(in_computJacob,dt,nSnow,nSoil,nLayers,computeVegFlux,computeBaseflow,ixMatrix) + class(in_type_computJacob),intent(out) :: in_computJacob ! class object for intent(in) computJacob arguments + real(rkind),intent(in) :: dt ! intent(in): length of the time step (seconds) + integer(i4b),intent(in) :: nSnow ! intent(in): number of snow layers + integer(i4b),intent(in) :: nSoil ! intent(in): number of soil layers + integer(i4b),intent(in) :: nLayers ! intent(in): total number of layers in the snow+soil domain + logical(lgt),intent(in) :: computeVegFlux ! intent(in): flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: computeBaseflow ! intent(in): flag to indicate if computing baseflow + integer(i4b),intent(in) :: ixMatrix ! intent(in): form of the Jacobian matrix + + ! intent(in) arguments + in_computJacob % dt = dt ! intent(in): length of the time step (seconds) + in_computJacob % nSnow = nSnow ! intent(in): number of snow layers + in_computJacob % nSoil = nSoil ! intent(in): number of soil layers + in_computJacob % nLayers = nLayers ! intent(in): total number of layers in the snow+soil domain + in_computJacob % computeVegFlux = computeVegFlux ! intent(in): flag to indicate if computing fluxes over vegetation + in_computJacob % computeBaseflow = computeBaseflow ! intent(in): flag to indicate if computing baseflow + in_computJacob % ixMatrix = ixMatrix ! intent(in): form of the Jacobian matrix + end subroutine initialize_in_computJacob + + subroutine finalize_out_computJacob(out_computJacob,err,cmessage) + class(out_type_computJacob),intent(in) :: out_computJacob ! class object for intent(out) computJacob arguments + integer(i4b),intent(out) :: err ! intent(out): error code + character(*),intent(out) :: cmessage ! intent(out): error message + ! intent(out) arguments + err = out_computJacob % err ! intent(out): error code + cmessage = out_computJacob % cmessage ! intent(out): error message + end subroutine finalize_out_computJacob + END MODULE data_types diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index a6c4aab87..41fe19c23 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -25,9 +25,11 @@ module computJacob_module ! derived types to define the data structures USE data_types,only:& - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - model_options ! defines the model decisions + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (rkind) + model_options, & ! defines the model decisions + in_type_computJacob, & ! class for computJacob arguments + out_type_computJacob ! class for computJacob arguments ! named variables for structure elements USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure @@ -84,47 +86,34 @@ module computJacob_module ! public subroutine computJacob: compute the Jacobian matrix ! ********************************************************************************************************** subroutine computJacob(& - ! input: model control - dt, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - computeBaseflow, & ! intent(in): flag to indicate if we need to compute baseflow - ixMatrix, & ! intent(in): form of the Jacobian matrix - ! input: data structures - indx_data, & ! intent(in): index data - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! input-output: Jacobian and its diagonal - dMat, & ! intent(inout): diagonal of the Jacobian matrix - aJac, & ! intent(out): Jacobian matrix - ! output: error control - err,message) ! intent(out): error code and error message + ! input: model control + in_computJacob, & ! intent(in): model control + ! input: data structures + indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables + dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! input-output: Jacobian and its diagonal + dMat, & ! intent(inout): diagonal of the Jacobian matrix + aJac, & ! intent(out): Jacobian matrix + ! output: error control + out_computJacob) ! intent(out): error code and error message ! ----------------------------------------------------------------------------------------------------------------- implicit none ! input: model control - real(rkind),intent(in) :: dt ! length of the time step (seconds) - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: computeBaseflow ! flag to indicate if computing baseflow - integer(i4b),intent(in) :: ixMatrix ! form of the Jacobian matrix + type(in_type_computJacob),intent(in) :: in_computJacob ! model control ! input: data structures - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + real(rkind),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! input-output: Jacobian and its diagonal - real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix - real(rkind),intent(out) :: aJac(:,:) ! Jacobian matrix + real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix + real(rkind),intent(out) :: aJac(:,:) ! Jacobian matrix ! output variables - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + type(out_type_computJacob),intent(out) :: out_computJacob ! error control ! -------------------------------------------------------------- ! * local variables ! -------------------------------------------------------------- @@ -143,13 +132,21 @@ subroutine computJacob(& ! -------------------------------------------------------------- ! associate variables from data structures associate(& + ! model control + dt => in_computJacob % dt ,& ! intent(in): length of the time step (seconds) + nSnow => in_computJacob % nSnow ,& ! intent(in): number of snow layers + nSoil => in_computJacob % nSoil ,& ! intent(in): number of soil layers + nLayers => in_computJacob % nLayers ,& ! intent(in): total number of layers in the snow+soil domain + computeVegFlux => in_computJacob % computeVegFlux ,& ! intent(in): flag to indicate if computing fluxes over vegetation + computeBaseflow => in_computJacob % computeBaseflow ,& ! intent(in): flag to indicate if computing baseflow + ixMatrix => in_computJacob % ixMatrix ,& ! intent(in): form of the Jacobian matrix ! indices of model state variables - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain - ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain + ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer ! vectors of indices for specfic state types within specific sub-domains IN THE FULL STATE VECTOR ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain ! vector of energy indices for the snow and soil domains @@ -255,7 +252,10 @@ subroutine computJacob(& scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1) ,& ! intent(in): [dp] soil control on infiltration, zero or one ! canopy and layer depth canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth )%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth )%dat & ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth )%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! output variables + err => out_computJacob % err ,& ! error code + message => out_computJacob % cmessage & ! error message ) ! making association with data in structures ! -------------------------------------------------------------- ! initialize error control @@ -1050,6 +1050,10 @@ integer(c_int) function computJacob4kinsol(sunvec_y, sunvec_r, sunmat_J, & ! pointers to data in SUNDIALS vectors real(c_double), pointer :: Jac(:,:) ! Jacobian matrix type(data4kinsol), pointer :: eqns_data ! equations data + + ! class objects for subroutine arguments + type(in_type_computJacob) :: in_computJacob ! intent(in) computJacob arguments + type(out_type_computJacob) :: out_computJacob ! intent(out) computJacob arguments ! ---------------------------------------------------------------- ! get equations data from user-defined data @@ -1063,15 +1067,10 @@ integer(c_int) function computJacob4kinsol(sunvec_y, sunvec_r, sunmat_J, & ! NOTE: The derivatives were computed in the previous call to computFlux ! This occurred either at the call to eval8summa at the start of systemSolv ! or in the call to eval8summa in the previous iteration + call initialize_computJacob ! pack in_computJacob object call computJacob(& ! input: model control - eqns_data%dt_cur, & ! intent(in): length of the time step (seconds) - eqns_data%nSnow, & ! intent(in): number of snow layers - eqns_data%nSoil, & ! intent(in): number of soil layers - eqns_data%nLayers, & ! intent(in): number of layers - eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - (eqns_data%model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow - eqns_data%ixMatrix, & ! intent(in): type of matrix (dense or banded) + in_computJacob, & ! input: data structures eqns_data%indx_data, & ! intent(in): index data eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU @@ -1082,13 +1081,26 @@ integer(c_int) function computJacob4kinsol(sunvec_y, sunvec_r, sunmat_J, & eqns_data%dMat, & ! intent(inout): diagonal of the Jacobian matrix Jac, & ! intent(out): Jacobian matrix ! output: error control - eqns_data%err,eqns_data%message) ! intent(out): error code and error message + out_computJacob) ! intent(out): error code and error message + call finalize_computJacob ! unpack out_computJacob object if(eqns_data%err > 0)then; eqns_data%message=trim(eqns_data%message); ierr=-1; return; endif if(eqns_data%err < 0)then; eqns_data%message=trim(eqns_data%message); ierr=1; return; endif ! return success ierr = 0 return + + contains + + subroutine initialize_computJacob + ! *** Transfer data to in_computJacob class object from local variables *** + call in_computJacob % initialize(eqns_data%dt_cur,eqns_data%nSnow,eqns_data%nSoil,eqns_data%nLayers,eqns_data%computeVegFlux,(eqns_data%model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel),eqns_data%ixMatrix) + end subroutine initialize_computJacob + + subroutine finalize_computJacob + ! *** Transfer data from out_computJacob class object to local variables *** + call out_computJacob % finalize(eqns_data % err,eqns_data % message) + end subroutine finalize_computJacob end function computJacob4kinsol #endif @@ -1106,4 +1118,4 @@ function ixOffDiag(jState,iState) ixOffDiag = ixDiag + jState - iState end function ixOffDiag -end module computJacob_module \ No newline at end of file +end module computJacob_module diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index ac9434c93..22fbff411 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -66,12 +66,14 @@ module summaSolve4numrec_module ! provide access to the derived types to define the data structures USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! lookup tables - model_options ! defines the model decisions + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! lookup tables + model_options, & ! defines the model decisions + in_type_computJacob, & ! class for computJacob arguments + out_type_computJacob ! class for computJacob arguments ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: & @@ -221,9 +223,10 @@ subroutine summaSolve4numrec(& integer(i4b) :: jLayer ! column index logical(lgt) :: globalPrintFlagInit ! initial global print flag character(LEN=256) :: cmessage ! error message of downwind routine + ! class objects for subroutine arguments + type(in_type_computJacob) :: in_computJacob + type(out_type_computJacob) :: out_computJacob ! -------------------------------------------------------------------------------------------------------------------------------- - ! associations to information in data structures - associate(ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision) ! intent(in): [i4b] groundwater parameterization ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='summaSolve4numrec/' @@ -249,41 +252,24 @@ subroutine summaSolve4numrec(& ! NOTE: The derivatives were computed in the previous call to computFlux ! This occurred either at the call to eval8summa at the start of systemSolv ! or in the call to eval8summa in the previous iteration (within lineSearchRefinement or trustRegionRefinement) - call computJacob(& - ! input: model control - dt_cur, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - (ixGroundwater==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow - ixMatrix, & ! intent(in): form of the Jacobian matrix - ! input: data structures - indx_data, & ! intent(in): index data - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! input-output: Jacobian and its diagonal - dMat, & ! intent(inout): diagonal of the Jacobian matrix - aJac, & ! intent(out): Jacobian matrix - ! output: error control - err,cmessage) ! intent(out): error code and error message - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + call initialize_computJacob_summaSolve4numrec + call computJacob(in_computJacob,indx_data,prog_data,diag_data,deriv_data,dBaseflow_dMatric,dMat,aJac,out_computJacob) + call finalize_computJacob_summaSolve4numrec + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! compute the numerical Jacobian matrix - if(doNumJacobian)then + if (doNumJacobian) then globalPrintFlag=.false. call numJacobian(stateVecTrial,dMat,nJac,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) globalPrintFlag=globalPrintFlagInit - endif + end if ! test the band diagonal matrix - if(testBandDiagonal)then + if (testBandDiagonal) then call testBandMat(check=.true.,err=err,message=cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - endif + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + end if ! ----- ! * solve linear system... @@ -294,9 +280,9 @@ subroutine summaSolve4numrec(& ! scale matrices call scaleMatrices(ixMatrix,nState,aJac,fScale,xScale,aJacScaled,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - if(globalPrintFlag .and. ixMatrix==ixBandMatrix)then + if (globalPrintFlag .and. ixMatrix==ixBandMatrix) then print*, '** SCALED banded analytical Jacobian:' write(*,'(a4,1x,100(i17,1x))') 'xCol', (iLayer, iLayer=iJac1,iJac2) do iLayer=kl+1,nBands @@ -309,10 +295,9 @@ subroutine summaSolve4numrec(& ! compute the newton step: use the lapack routines to solve the linear system A.X=B call lapackSolv(ixMatrix,nState,aJacScaledTemp,-rVecScaled,newtStepScaled,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - if(globalPrintFlag)& - write(*,'(a,1x,10(e17.10,1x))') 'newtStepScaled = ', newtStepScaled(min(iJac1,nState):min(iJac2,nState)) + if (globalPrintFlag) write(*,'(a,1x,10(e17.10,1x))') 'newtStepScaled = ', newtStepScaled(min(iJac1,nState):min(iJac2,nState)) ! ----- ! * update, evaluate, and refine the state vector... @@ -324,7 +309,7 @@ subroutine summaSolve4numrec(& ! * case 1: state vector ! compute the flux vector and the residual, and (if necessary) refine the iteration increment ! NOTE: in 99.9% of cases newtStep will be used (no refinement) - if(size(stateVecTrial)>1)then + if (size(stateVecTrial)>1) then ! try to backtrack select case(ixStepRefinement) @@ -335,21 +320,20 @@ subroutine summaSolve4numrec(& ! check warnings: negative error code = warning; in this case back-tracked to the original value ! NOTE: Accept the full newton step if back-tracked to the original value - if(err<0)then - doRefine=.false.; call lineSearchRefinement( doRefine,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,cmessage) + if (err<0) then + doRefine=.false.; + call lineSearchRefinement( doRefine,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,cmessage) end if ! * case 2: scalar else call safeRootfinder(stateVecTrial,rVecScaled,newtStepScaled,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) endif ! check errors - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! end association to info in data structures - end associate contains @@ -855,6 +839,9 @@ subroutine testBandMat(check,err,message) real(rkind) :: bandJac(nLeadDim,nState) ! band Jacobian matrix integer(i4b) :: iState,jState ! indices of the state vector character(LEN=256) :: cmessage ! error message of downwind routine + ! class objects for subroutine arguments + type(in_type_computJacob) :: in_computJacob + type(out_type_computJacob) :: out_computJacob ! initialize error control err=0; message='testBandMat/' @@ -866,26 +853,9 @@ subroutine testBandMat(check,err,message) endif ! compute the full Jacobian matrix - call computJacob(& - ! input: model control - dt_cur, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - .false., & ! intent(in): flag to indicate if we need to compute baseflow - ixFullMatrix, & ! intent(in): force full Jacobian matrix - ! input: data structures - indx_data, & ! intent(in): index data - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! input-output: Jacobian and its diagonal - dMat, & ! intent(inout): diagonal of the Jacobian matrix - fullJac, & ! intent(out): full Jacobian matrix - ! output: error control - err,cmessage) ! intent(out): error code and error message + call initialize_computJacob_testBandMat + call computJacob(in_computJacob,indx_data,prog_data,diag_data,deriv_data,dBaseflow_dMatric,dMat,fullJac,out_computJacob) + call finalize_computJacob_testBandMat(err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! initialize band matrix @@ -911,6 +881,30 @@ subroutine testBandMat(check,err,message) end subroutine testBandMat + subroutine initialize_computJacob_testBandMat + ! *** Transfer data from out_computJacob class object to local variables in testBandMat *** + call in_computJacob % initialize(dt_cur,nSnow,nSoil,nLayers,computeVegFlux,.false.,ixFullMatrix) + end subroutine initialize_computJacob_testBandMat + + subroutine finalize_computJacob_testBandMat(err,cmessage) + ! *** Transfer data from out_computJacob class object to local variables in testBandMat *** + ! Note: subroutine arguments are needed because testBandMat is an internal procedure + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: cmessage ! error message of downwind routine + call out_computJacob % finalize(err,cmessage) + end subroutine finalize_computJacob_testBandMat + + subroutine initialize_computJacob_summaSolve4numrec + ! *** Transfer data to in_computJacob class object from local variables in summaSolve4numrec *** + associate(ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision) ! intent(in): [i4b] groundwater parameterization + call in_computJacob % initialize(dt_cur,nSnow,nSoil,nLayers,computeVegFlux,(ixGroundwater==qbaseTopmodel),ixMatrix) + end associate + end subroutine initialize_computJacob_summaSolve4numrec + + subroutine finalize_computJacob_summaSolve4numrec + ! *** Transfer data from out_computJacob class object to local variables in summaSolve4numrec *** + call out_computJacob % finalize(err,cmessage) + end subroutine finalize_computJacob_summaSolve4numrec ! ********************************************************************************************************* ! * internal subroutine eval8summa_wrapper: compute the right-hand-side vector From 5bd98306cb678ffeba846100cd004366c18b38d9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 16 Feb 2024 20:42:00 +0900 Subject: [PATCH 1143/1472] constraints for all soil formulations --- build/source/engine/computHeatCap.f90 | 88 ++++++++++---------- build/source/engine/eval8summa.f90 | 111 +++++++++++++++----------- build/source/engine/getVectorz.f90 | 105 ++++++++++++------------ 3 files changed, 159 insertions(+), 145 deletions(-) diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index 927674c39..ce930f0e2 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -87,8 +87,8 @@ subroutine computStatMult(& sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- -USE nr_utility_module,only:arth ! get a sequence of numbers arth(start, incr, count) -USE f2008funcs_module,only:findIndex ! finds the index of the first value within a vector +USE nr_utility_module,only:arth ! get a sequence of numbers arth(start, incr, count) +USE f2008funcs_module,only:findIndex ! finds the index of the first value within a vector ! -------------------------------------------------------------------------------------------------------------------------------- ! input: data structures real(qp),intent(in) :: heatCapVeg ! volumetric heat capacity of vegetation (J m-3 K-1) @@ -140,24 +140,24 @@ subroutine computStatMult(& ! define the multiplier for the state vector for residual calculations (vegetation canopy) ! NOTE: Use the "where" statement to generalize to multiple canopy layers (currently one canopy layer) - where(ixStateType_subset==iname_nrgCanair) sMul = Cp_air*iden_air ! volumetric heat capacity of air (J m-3 K-1) - where(ixStateType_subset==iname_nrgCanopy) sMul = heatCapVeg ! volumetric heat capacity of the vegetation (J m-3 K-1) - where(ixStateType_subset==iname_watCanopy) sMul = 1._rkind ! nothing else on the left hand side - where(ixStateType_subset==iname_liqCanopy) sMul = 1._rkind ! nothing else on the left hand side + where(ixStateType_subset==iname_nrgCanair) sMul = Cp_air*iden_air ! volumetric heat capacity of air (J m-3 K-1) + where(ixStateType_subset==iname_nrgCanopy) sMul = heatCapVeg ! volumetric heat capacity of the vegetation (J m-3 K-1) + where(ixStateType_subset==iname_watCanopy) sMul = 1._rkind ! nothing else on the left hand side + where(ixStateType_subset==iname_liqCanopy) sMul = 1._rkind ! nothing else on the left hand side ! define the energy multiplier for the state vector for residual calculations (snow-soil domain) if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - ixStateSubset = ixSnowSoilNrg(iLayer) ! index within the state vector - sMul(ixStateSubset) = mLayerHeatCap(iLayer) ! transfer volumetric heat capacity to the state multiplier + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + ixStateSubset = ixSnowSoilNrg(iLayer) ! index within the state vector + sMul(ixStateSubset) = mLayerHeatCap(iLayer) ! transfer volumetric heat capacity to the state multiplier end do ! looping through non-missing energy state variables in the snow+soil domain endif ! define the hydrology multiplier and diagonal elements for the state vector for residual calculations (snow-soil domain) if(nSnowSoilHyd>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - ixStateSubset = ixSnowSoilHyd(iLayer) ! index within the state vector - sMul(ixStateSubset) = 1._rkind ! state multiplier = 1 (nothing else on the left-hand-side) + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + ixStateSubset = ixSnowSoilHyd(iLayer) ! index within the state vector + sMul(ixStateSubset) = 1._rkind ! state multiplier = 1 (nothing else on the left-hand-side) end do ! looping through non-missing energy state variables in the snow+soil domain endif @@ -176,34 +176,34 @@ end subroutine computStatMult ! ********************************************************************************************************** subroutine computHeatCapAnalytic(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) ! input: state variables - scalarCanopyIce, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiquid, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) - scalarCanopyTemp, & ! intent(in): trial value of canopy temperature (K) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - mLayerTemp, & ! intent(in): trial value of layer temperature (K) - mLayerMatricHead, & ! intent(in): total water matric potential (m) + scalarCanopyIce, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiquid, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) + scalarCanopyTemp, & ! intent(in): trial value of canopy temperature (K) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerTemp, & ! intent(in): trial value of layer temperature (K) + mLayerMatricHead, & ! intent(in): total water matric potential (m) ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) ! input output data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! output - heatCapVeg, & ! intent(out): heat capacity for canopy - mLayerHeatCap, & ! intent(out): heat capacity for snow and soil - dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + heatCapVeg, & ! intent(out): heat capacity for canopy + mLayerHeatCap, & ! intent(out): heat capacity for snow and soil + dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature ! output: error control err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------------- @@ -298,8 +298,8 @@ subroutine computHeatCapAnalytic(& ! * soil case(iname_soil) mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component - iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component ! derivatives @@ -314,9 +314,9 @@ subroutine computHeatCapAnalytic(& endif case(iname_snow) - mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component + mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component ! derivatives fLiq = mLayerFracLiqSnow(iLayer) dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air @@ -389,10 +389,10 @@ subroutine computCm(& ! associate variables in data structure associate(& ! input: coordinate variables - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers - layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers + layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) ) ! end associate statement ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 12b09449f..9dde83d38 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -31,6 +31,8 @@ module eval8summa_module ! named variables to describe the state variable type USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers +USE globalData,only:iname_matLayer ! named variable defining the total water matric potential state variable for soil layers +USE globalData,only:iname_lmpLayer ! named variable defining the liquid water matric potential state variable for soil layers ! constants USE multiconst,only:& @@ -807,7 +809,7 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st real(qp),dimension(nState) :: xInc ! iteration increment real(rkind) :: scalarTemp ! temperature of an individual snow layer (K) real(rkind) :: scalarIce ! volumetric ice content of an individual layer (-) - real(rkind) :: vFracLiq ! volumetric liquid water content of an individual layer (-) + real(rkind) :: scalarLiq ! volumetric liquid water content of an individual layer (-) real(rkind) :: xPsi00 ! matric head after applying the iteration increment (m) real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) real(rkind) :: critDiff ! temperature difference from critical (K) @@ -817,6 +819,8 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) real(rkind) :: vGn_m(nSoil) ! van Genutchen "m" parameter (-) + real(rkind) :: effSat ! effective saturation (-) + real(rkind) :: avPore ! available pore space (-) ! indices of model state variables integer(i4b) :: iState ! index of state within a specific variable type integer(i4b) :: ixNrg,ixLiq ! index of energy and mass state variables in full state vector @@ -832,45 +836,45 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st ! association to variables in the data structures associate(& ! model decisions - ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver ! indices of model state variables - ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for energy states - ixHydOnly => indx_data%var(iLookINDEX%ixHydOnly)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for hydrology states - ixMatOnly => indx_data%var(iLookINDEX%ixMatOnly)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for matric head states - ixMassOnly => indx_data%var(iLookINDEX%ixMassOnly)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for canopy storage states - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain - ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] named variables defining the states in the subset + ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for energy states + ixHydOnly => indx_data%var(iLookINDEX%ixHydOnly)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for hydrology states + ixMatOnly => indx_data%var(iLookINDEX%ixMatOnly)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for matric head states + ixMassOnly => indx_data%var(iLookINDEX%ixMassOnly)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for canopy storage states + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain + ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] named variables defining the states in the subset ! indices for specific state variables - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow-soil subdomain - ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow-soil subdomain + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow-soil subdomain + ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow-soil subdomain ! vector of energy indices for the snow and soil domains ! NOTE: states not in the subset are equal to integerMissing - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain - ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow domain - ixSoilOnlyNrg => indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the soil domain + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow domain + ixSoilOnlyNrg => indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the soil domain ! vector of hydrology indices for the snow and soil domains ! NOTE: states not in the subset are equal to integerMissing - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow domain - ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the soil domain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow domain + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the soil domain ! number of state variables of a specific type - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain - nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain - nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain + nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain ! soil parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) ! state variables at the start of the time step - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] matric head (m) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat & ! intent(in): [dp(:)] volumetric fraction of ice (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] matric head (m) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat & ! intent(in): [dp(:)] volumetric fraction of ice (-) ) ! associating variables with indices of model state variables ! ----------------------------------------------------------------------------------------------------- @@ -969,7 +973,18 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st ixLiq = ixSoilOnlyHyd(iLayer) ! get the matric potential of total water if(ixLiq/=integerMissing)then - xPsi00 = stateVecPrev(ixLiq) + xInc(ixLiq) ! only true if using iname_matLayer, otherwise may want to fix this + select case( ixStateType_subset( ixSoilOnlyHyd(iLayer) ) ) + case(iname_lmpLayer) + effSat = volFracLiq(stateVecPrev(ixLiq) + xInc(ixLiq),vGn_alpha(iLayer),0._rkind,1._rkind,vGn_n(iLayer),vGn_m(iLayer)) ! effective saturation + avPore = theta_sat(iLayer) - mLayerVolFracIce(iLayer+nSnow) - theta_res(iLayer) ! available pore space + scalarLiq = effSat*avPore + theta_res(iLayer) + xPsi00 = matricHead(scalarLiq + mLayerVolFracIce(iLayer+nSnow),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) + case(iname_matLayer); xPsi00 = stateVecPrev(ixLiq) + xInc(ixLiq) ! only true if using iname_matLayer, otherwise may want to fix this + case(iname_watLayer); xPsi00 = matricHead(stateVecPrev(ixLiq) + xInc(ixLiq),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) + case(iname_liqLayer) + xPsi00 = matricHead(mLayerVolFracIce(iLayer+nSnow) + stateVecPrev(ixLiq) + xInc(ixLiq),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) + case default; err=20; message=trim(message)//'expect ixStateType_subset to be iname_matLayer, iname_lmpLayer, iname_watLayer, or iname_liqLayer for soil hydrology'; return + end select else xPsi00 = mLayerMatricHead(iLayer) endif @@ -1012,16 +1027,16 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st endif ! get the volumetric fraction of liquid water and ice select case( ixStateType_subset( ixSnowOnlyHyd(iLayer) ) ) - case(iname_watLayer); vFracLiq = fracliquid(scalarTemp,mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1)) * stateVecPrev(ixSnowOnlyHyd(iLayer)) - case(iname_liqLayer); vFracLiq = stateVecPrev(ixSnowOnlyHyd(iLayer)) + case(iname_watLayer); scalarLiq = fracliquid(scalarTemp,mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1)) * stateVecPrev(ixSnowOnlyHyd(iLayer)) + case(iname_liqLayer); scalarLiq = stateVecPrev(ixSnowOnlyHyd(iLayer)) case default; err=20; message=trim(message)//'expect ixStateType_subset to be iname_watLayer or iname_liqLayer for snow hydrology'; return end select - scalarIce = merge(stateVecPrev(ixSnowOnlyHyd(iLayer)) - vFracLiq,mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) + scalarIce = merge(stateVecPrev(ixSnowOnlyHyd(iLayer)) - scalarLiq,mLayerVolFracIce(iLayer), ixHydType(iLayer)==iname_watLayer) ! checking if drain more than what is available or add more than possible, constrained iteration increment -- simplified bi-section - if(-xInc(ixSnowOnlyHyd(iLayer)) > vFracLiq) then - xInc(ixSnowOnlyHyd(iLayer)) = -0.5_rkind*vFracLiq - elseif(xInc(ixSnowOnlyHyd(iLayer)) > 1._rkind - scalarIce - vFracLiq)then - xInc(ixSnowOnlyHyd(iLayer)) = 0.5_rkind*(1._rkind - scalarIce - vFracLiq) + if(-xInc(ixSnowOnlyHyd(iLayer)) > scalarLiq) then + xInc(ixSnowOnlyHyd(iLayer)) = -0.5_rkind*scalarLiq + elseif(xInc(ixSnowOnlyHyd(iLayer)) > 1._rkind - scalarIce - scalarLiq)then + xInc(ixSnowOnlyHyd(iLayer)) = 0.5_rkind*(1._rkind - scalarIce - scalarLiq) endif end do ! (looping through snow layers) endif ! (if there are state variables for liquid water in the snow domain) @@ -1034,7 +1049,7 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle if(ixHydType(iLayer+nSnow)==iname_watLayer .or. ixHydType(iLayer+nSnow)==iname_liqLayer)then ! get the volumetric fraction of liquid water and ice - select case( ixStateType_subset( ixSnowOnlyHyd(iLayer) ) ) + select case( ixStateType_subset( ixSoilOnlyHyd(iLayer) ) ) case(iname_watLayer) xPsi00 = matricHead(stateVecPrev(ixSoilOnlyHyd(iLayer)),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) ! get the layer temperature @@ -1049,18 +1064,18 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st if(scalarTemp < TcSoil)then xConst = LH_fus/(gravity*Tfreeze) mLayerPsiLiq = xConst*(scalarTemp - Tfreeze) - vFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) + scalarLiq = volFracLiq(mLayerPsiLiq,vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) else - vFracLiq = stateVecPrev(ixSoilOnlyHyd(iLayer)) + scalarLiq = stateVecPrev(ixSoilOnlyHyd(iLayer)) end if ! (check if soil is partially frozen) - case(iname_liqLayer); vFracLiq = stateVecPrev(ixSoilOnlyHyd(iLayer)) + case(iname_liqLayer); scalarLiq = stateVecPrev(ixSoilOnlyHyd(iLayer)) end select - scalarIce = merge(stateVecPrev(ixSnowOnlyHyd(iLayer)) - vFracLiq,mLayerVolFracIce(iLayer+nSnow), ixHydType(iLayer)==iname_watLayer) + scalarIce = merge(stateVecPrev(ixSoilOnlyHyd(iLayer)) - scalarLiq,mLayerVolFracIce(iLayer+nSnow), ixHydType(iLayer)==iname_watLayer) ! checking if drain more than what is available or add more than possible, constrained iteration increment -- simplified bi-section - if(-xInc(ixSoilOnlyHyd(iLayer)) > vFracLiq - theta_res(iLayer))then - xInc(ixSoilOnlyHyd(iLayer)) = -0.5_rkind*(vFracLiq - theta_res(iLayer)) - elseif(xInc(ixSoilOnlyHyd(iLayer)) > theta_sat(iLayer) - scalarIce - vFracLiq)then - xInc(ixSoilOnlyHyd(iLayer)) = 0.5_rkind*(theta_sat(iLayer) - scalarIce - vFracLiq) + if(-xInc(ixSoilOnlyHyd(iLayer)) > scalarLiq - theta_res(iLayer))then + xInc(ixSoilOnlyHyd(iLayer)) = -0.5_rkind*(scalarLiq - theta_res(iLayer)) + elseif(xInc(ixSoilOnlyHyd(iLayer)) > theta_sat(iLayer) - scalarIce - scalarLiq)then + xInc(ixSoilOnlyHyd(iLayer)) = 0.5_rkind*(theta_sat(iLayer) - scalarIce - scalarLiq) endif endif ! (if the state variable is not matric head) end do ! (looping through soil layers) diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index 033ed4c9b..97cfbb8bd 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -135,35 +135,35 @@ subroutine popStateVec(& ! make association with variables in the data structures fixedLength: associate(& ! model states for the vegetation canopy - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in) : [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in) : [dp] temperature of the vegetation canopy (K) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in) : [dp] mass of total water on the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in) : [dp] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in) : [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in) : [dp] temperature of the vegetation canopy (K) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in) : [dp] mass of total water on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in) : [dp] mass of liquid water on the vegetation canopy (kg m-2) ! model state variable vectors for the snow-soil layers - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in) : [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in) : [dp(:)] volumetric fraction of total water (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in) : [dp(:)] volumetric fraction of liquid water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in) : [dp(:)] matric head (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in) : [dp(:)] matric potential of liquid water (m) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in) : [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in) : [dp(:)] volumetric fraction of total water (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in) : [dp(:)] volumetric fraction of liquid water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in) : [dp(:)] matric head (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in) : [dp(:)] matric potential of liquid water (m) ! model state variables for the aquifer - scalarAquiferStorage=> prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in) : [dp] storage of water in the aquifer (m) + scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in) : [dp] storage of water in the aquifer (m) ! indices defining specific model states - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy hydrology state variable (mass) - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of aquifer storage state variable + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy hydrology state variable (mass) + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of aquifer storage state variable ! vector of energy and hydrology indices for the snow and soil domains - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for energy state variables in the snow+soil domain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in) : [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in) : [i4b] number of hydrology state variables in the snow+soil domain + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in) : [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in) : [i4b] number of hydrology state variables in the snow+soil domain ! type of model state variabless - ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in) : [i4b(:)] [state subset] type of desired model state variables - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in) : [i4b(:)] index of the type of hydrology states in snow+soil domain + ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in) : [i4b(:)] [state subset] type of desired model state variables + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in) : [i4b(:)] index of the type of hydrology states in snow+soil domain ! number of layers - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in) : [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in) : [i4b] number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in) : [i4b] total number of layers + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in) : [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in) : [i4b] number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in) : [i4b] total number of layers ) ! end association with variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -179,43 +179,43 @@ subroutine popStateVec(& ! build the state vector for the temperature of the canopy air space ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer canopy do concurrent (iState=1:size(ixCasNrg),ixCasNrg(iState)/=integerMissing) - stateVec( ixCasNrg(iState) ) = scalarCanairTemp ! transfer canopy air temperature to the state vector - stateFlag( ixCasNrg(iState) ) = .true. ! flag to denote that the state is populated + stateVec( ixCasNrg(iState) ) = scalarCanairTemp ! transfer canopy air temperature to the state vector + stateFlag( ixCasNrg(iState) ) = .true. ! flag to denote that the state is populated end do ! build the state vector for the temperature of the vegetation canopy ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer canopy do concurrent (iState=1:size(ixVegNrg),ixVegNrg(iState)/=integerMissing) - stateVec( ixVegNrg(iState) ) = scalarCanopyTemp ! transfer vegetation temperature to the state vector - stateFlag( ixVegNrg(iState) ) = .true. ! flag to denote that the state is populated + stateVec( ixVegNrg(iState) ) = scalarCanopyTemp ! transfer vegetation temperature to the state vector + stateFlag( ixVegNrg(iState) ) = .true. ! flag to denote that the state is populated end do ! build the state vector for the water in the vegetation canopy ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer canopy do concurrent (iState=1:size(ixVegHyd),ixVegHyd(iState)/=integerMissing) - stateFlag( ixVegHyd(iState) ) = .true. ! flag to denote that the state is populated + stateFlag( ixVegHyd(iState) ) = .true. ! flag to denote that the state is populated select case(ixStateType_subset( ixVegHyd(iState) )) - case(iname_watCanopy); stateVec( ixVegHyd(iState) ) = scalarCanopyWat ! transfer total canopy water to the state vector - case(iname_liqCanopy); stateVec( ixVegHyd(iState) ) = scalarCanopyLiq ! transfer liquid canopy water to the state vector + case(iname_watCanopy); stateVec( ixVegHyd(iState) ) = scalarCanopyWat ! transfer total canopy water to the state vector + case(iname_liqCanopy); stateVec( ixVegHyd(iState) ) = scalarCanopyLiq ! transfer liquid canopy water to the state vector case default; stateFlag( ixVegHyd(iState) ) = .false. ! flag to denote that the state is populated end select end do ! build the energy state vector for the snow and soil domain if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - ixStateSubset = ixSnowSoilNrg(iLayer) ! index within the state vector - stateVec(ixStateSubset) = mLayerTemp(iLayer) ! transfer temperature from a layer to the state vector - stateFlag(ixStateSubset) = .true. ! flag to denote that the state is populated + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + ixStateSubset = ixSnowSoilNrg(iLayer) ! index within the state vector + stateVec(ixStateSubset) = mLayerTemp(iLayer) ! transfer temperature from a layer to the state vector + stateFlag(ixStateSubset) = .true. ! flag to denote that the state is populated end do ! looping through non-missing energy state variables in the snow+soil domain endif ! build the hydrology state vector for the snow+soil domains ! NOTE: ixVolFracWat and ixVolFracLiq can also include states in the soil domain, hence enable primary variable switching if(nSnowSoilHyd>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) - ixStateSubset = ixSnowSoilHyd(iLayer) ! index within the state vector - stateFlag(ixStateSubset) = .true. ! flag to denote that the state is populated + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing hydrology state variables in the snow+soil domain) + ixStateSubset = ixSnowSoilHyd(iLayer) ! index within the state vector + stateFlag(ixStateSubset) = .true. ! flag to denote that the state is populated select case( ixHydType(iLayer) ) case(iname_watLayer); stateVec(ixStateSubset) = mLayerVolFracWat(iLayer) ! total water state variable for snow+soil layers case(iname_liqLayer); stateVec(ixStateSubset) = mLayerVolFracLiq(iLayer) ! liquid water state variable for snow+soil layers @@ -430,25 +430,24 @@ subroutine checkFeas(& ! make association with variables in the data structures associate(& ! soil parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] residual volumetric water content (-) ! model diagnostic variables from the previous solution - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers ! indices defining model states and layers - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain - layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow subdomain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain + layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] layer type (iname_soil or iname_snow) )! association with variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- From 1fb6791fd94949347ad9ca8d7264d7912e12ddec Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 16 Feb 2024 21:19:49 +0900 Subject: [PATCH 1144/1472] changed to closedForm does not updateCp or needCm, for backwards compatibility --- build/source/engine/eval8summa.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 9dde83d38..b8403515c 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -219,8 +219,8 @@ subroutine eval8summa(& real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil logical(lgt) :: updateCp ! flag to indicate if we update Cp at each step, set with nrgConserv choice and updateCp_closedForm flag logical(lgt) :: needCm ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m,, set with nrgConserv choice and needCm_closedForm flag - logical(lgt),parameter :: updateCp_closedForm=.true. ! nrgConserv = closedForm flag to indicate if we update Cp at each step - logical(lgt),parameter :: needCm_closedForm=.true. ! nrgConserv = closedForm flag to indicate if the energy equation contains Cm = dH_T/dTheta_m + logical(lgt),parameter :: updateCp_closedForm=.false. ! nrgConserv = closedForm flag to indicate if we update Cp at each step + logical(lgt),parameter :: needCm_closedForm=.false. ! nrgConserv = closedForm flag to indicate if the energy equation contains Cm = dH_T/dTheta_m ! -------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures From fad6b4ef8f2eab64bbb11017ea35e55fdd7b67e6 Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 16 Feb 2024 06:50:59 -0600 Subject: [PATCH 1145/1472] Applied object-oriented methods to simplify handling of scalar intent(out) arguments for lineSearchRefinement. --- build/source/dshare/data_types.f90 | 27 ++- build/source/engine/summaSolve4numrec.f90 | 234 +++++++++++----------- 2 files changed, 147 insertions(+), 114 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 1dd37de88..2ac144672 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -660,7 +660,7 @@ MODULE data_types ! Define classes used to simplify calls to the subrotuines in summaSolve4numrec ! *********************************************************************************************************** - type, public :: in_type_computJacob ! class for intent(in) arguments in computFlux call + type, public :: in_type_computJacob ! class for intent(in) arguments in computJacob call ! input: model control real(rkind) :: dt ! intent(in): length of the time step (seconds) integer(i4b) :: nSnow ! intent(in): number of snow layers @@ -673,7 +673,7 @@ MODULE data_types procedure :: initialize => initialize_in_computJacob end type in_type_computJacob - type, public :: out_type_computJacob ! class for intent(in) arguments in computFlux call + type, public :: out_type_computJacob ! class for intent(out) arguments in computJacob call ! output: error control integer(i4b) :: err ! intent(out): error code character(len=len_msg) :: cmessage ! intent(out): error message @@ -681,6 +681,16 @@ MODULE data_types procedure :: finalize => finalize_out_computJacob end type out_type_computJacob + type, public :: out_type_lineSearchRefinement ! class for intent(out) arguments in lineSearchRefinement call + real(rkind) :: fNew ! intent(out): new function evaluation + logical(lgt) :: converged ! intent(out): convergence flag + ! output: error control + integer(i4b) :: err ! intent(out): error code + character(len=len_msg) :: message ! intent(out): error message + contains + procedure :: finalize => finalize_out_lineSearchRefinement + end type out_type_lineSearchRefinement + contains ! **** vegNrgFlux **** @@ -1473,4 +1483,17 @@ subroutine finalize_out_computJacob(out_computJacob,err,cmessage) cmessage = out_computJacob % cmessage ! intent(out): error message end subroutine finalize_out_computJacob + ! **** lineSearchRefinement **** + subroutine finalize_out_lineSearchRefinement(out_lineSearchRefinement,fNew,converged,err,message) + class(out_type_lineSearchRefinement),intent(in) :: out_lineSearchRefinement ! class object for intent(out) computJacob arguments + real(rkind) :: fNew ! intent(out): new function evaluation + logical(lgt) :: converged ! intent(out): convergence flag + integer(i4b) :: err ! intent(out): error code + character(len=len_msg) :: message ! intent(out): error message + fNew = out_lineSearchRefinement % fNew ! intent(out): new function evaluation + converged = out_lineSearchRefinement % converged ! intent(out): convergence flag + err = out_lineSearchRefinement % err ! intent(out): error code + message = out_lineSearchRefinement % message ! intent(out): error message + end subroutine finalize_out_lineSearchRefinement + END MODULE data_types diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 5a0460e7b..a1a68c96c 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -66,14 +66,15 @@ module summaSolve4numrec_module ! provide access to the derived types to define the data structures USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! lookup tables - model_options, & ! defines the model decisions - in_type_computJacob, & ! class for computJacob arguments - out_type_computJacob ! class for computJacob arguments + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! lookup tables + model_options, & ! defines the model decisions + in_type_computJacob, & ! class for computJacob arguments + out_type_computJacob, & ! class for computJacob arguments + out_type_lineSearchRefinement ! class for lineSearchRefinement arguments ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: & @@ -224,8 +225,9 @@ subroutine summaSolve4numrec(& logical(lgt) :: globalPrintFlagInit ! initial global print flag character(LEN=256) :: cmessage ! error message of downwind routine ! class objects for subroutine arguments - type(in_type_computJacob) :: in_computJacob - type(out_type_computJacob) :: out_computJacob + type(in_type_computJacob) :: in_computJacob + type(out_type_computJacob) :: out_computJacob + type(out_type_lineSearchRefinement) :: out_LSR ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -313,8 +315,11 @@ subroutine summaSolve4numrec(& ! try to backtrack select case(ixStepRefinement) - case(ixLineSearch); call lineSearchRefinement( doRefine,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,cmessage) - case(ixTrustRegion); call trustRegionRefinement(doRefine,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,cmessage) + case(ixLineSearch) + call lineSearchRefinement(doRefine,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,out_LSR) + call out_LSR % finalize(fNew,converged,err,cmessage) + case(ixTrustRegion) + call trustRegionRefinement(doRefine,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,cmessage) case default; err=20; message=trim(message)//'unable to identify numerical solution'; return end select @@ -322,7 +327,8 @@ subroutine summaSolve4numrec(& ! NOTE: Accept the full newton step if back-tracked to the original value if (err<0) then doRefine=.false.; - call lineSearchRefinement( doRefine,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,cmessage) + call lineSearchRefinement(doRefine,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,out_LSR) + call out_LSR % finalize(fNew,converged,err,cmessage) end if ! * case 2: scalar @@ -340,7 +346,7 @@ subroutine summaSolve4numrec(& ! ********************************************************************************************************* ! * internal subroutine lineSearchRefinement: refine the iteration increment using line searches ! ********************************************************************************************************* - subroutine lineSearchRefinement(doLineSearch,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,message) + subroutine lineSearchRefinement(doLineSearch,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,out_LSR) ! provide access to the matrix routines USE matrixOper_module, only: computeGradient implicit none @@ -355,10 +361,7 @@ subroutine lineSearchRefinement(doLineSearch,stateVecTrial,newtStepScaled,aJacSc real(rkind),intent(out) :: stateVecNew(:) ! new state vector real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector - real(rkind),intent(out) :: fNew ! new function evaluation - logical(lgt),intent(out) :: converged ! convergence flag - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + type(out_type_lineSearchRefinement),intent(out) :: out_LSR ! class object for intent(out) arguments ! -------------------------------------------------------------------------------------------------------- ! local character(len=256) :: cmessage ! error message of downwind routine @@ -377,129 +380,136 @@ subroutine lineSearchRefinement(doLineSearch,stateVecTrial,newtStepScaled,aJacSc real(rkind) :: xLambdaPrev ! previous lambda value (used in the cubic) real(rkind) :: fPrev ! previous function evaluation (used in the cubic) ! -------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='lineSearchRefinement/' - converged =.false. - - ! check the need to compute the line search - if(doLineSearch)then - - ! compute the gradient of the function vector - call computeGradient(ixMatrix,nState,aJacScaled,rVecScaled,gradScaled,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + associate(& + fNew => out_LSR % fNew ,& ! new function evaluation + converged => out_LSR % converged ,& ! convergence flag + err => out_LSR % err ,& ! error code + message => out_LSR % message & ! error message + &) + ! initialize error control + err=0; message='lineSearchRefinement/' + converged =.false. + + ! check the need to compute the line search + if(doLineSearch)then + + ! compute the gradient of the function vector + call computeGradient(ixMatrix,nState,aJacScaled,rVecScaled,gradScaled,err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! compute the initial slope - slopeInit = dot_product(gradScaled,newtStepScaled) + ! compute the initial slope + slopeInit = dot_product(gradScaled,newtStepScaled) - end if ! if computing the line search + end if ! if computing the line search - ! initialize lambda - xLambda=1._rkind + ! initialize lambda + xLambda=1._rkind - ! ***** LINE SEARCH LOOP... - lineSearch: do iLine=1,maxLineSearch ! try to refine the function by shrinking the step size + ! ***** LINE SEARCH LOOP... + lineSearch: do iLine=1,maxLineSearch ! try to refine the function by shrinking the step size - ! back-track along the search direction - ! NOTE: start with back-tracking the scaled step - xInc(:) = xLambda*newtStepScaled(:) + ! back-track along the search direction + ! NOTE: start with back-tracking the scaled step + xInc(:) = xLambda*newtStepScaled(:) - ! re-scale the iteration increment - xInc(:) = xInc(:)*xScale(:) + ! re-scale the iteration increment + xInc(:) = xInc(:)*xScale(:) - ! if enthalpy, then need to convert the iteration increment to temperature - !if(nrgFormulation==ix_enthalpy) xInc(ixNrgOnly) = xInc(ixNrgOnly)/dMat(ixNrgOnly) + ! if enthalpy, then need to convert the iteration increment to temperature + !if(nrgFormulation==ix_enthalpy) xInc(ixNrgOnly) = xInc(ixNrgOnly)/dMat(ixNrgOnly) - ! state vector with proposed iteration increment - stateVecNew = stateVecTrial + xInc + ! state vector with proposed iteration increment + stateVecNew = stateVecTrial + xInc - ! impose solution constraints adjusting state vector and iteration increment - ! NOTE: We may not need to do this (or at least, do ALL of this), as we can probably rely on the line search here - call imposeConstraints(model_decisions,indx_data,prog_data,mpar_data,stateVecNew,stateVecTrial,nState,nSoil,nSnow,cmessage,err) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - xInc = stateVecNew - stateVecTrial + ! impose solution constraints adjusting state vector and iteration increment + ! NOTE: We may not need to do this (or at least, do ALL of this), as we can probably rely on the line search here + call imposeConstraints(model_decisions,indx_data,prog_data,mpar_data,stateVecNew,stateVecTrial,nState,nSoil,nSnow,cmessage,err) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + xInc = stateVecNew - stateVecTrial - ! compute the residual vector and function - ! NOTE: This calls eval8summa in an internal subroutine which has access to all data - ! Hence, we only need to include the variables of interest in lineSearch - call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + ! compute the residual vector and function + ! NOTE: This calls eval8summa in an internal subroutine which has access to all data + ! Hence, we only need to include the variables of interest in lineSearch + call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! check line search - if(globalPrintFlag)then - write(*,'(a,1x,i4,1x,e17.10)' ) 'iLine, xLambda = ', iLine, xLambda - write(*,'(a,1x,10(e17.10,1x))') 'fOld,fNew = ', fOld,fNew - write(*,'(a,1x,10(e17.10,1x))') 'fold + alpha*slopeInit*xLambda = ', fold + alpha*slopeInit*xLambda - write(*,'(a,1x,10(e17.10,1x))') 'resVecNew = ', resVecNew(min(iJac1,nState):min(iJac2,nState)) - write(*,'(a,1x,10(e17.10,1x))') 'xInc = ', xInc(min(iJac1,nState):min(iJac2,nState)) - end if + ! check line search + if(globalPrintFlag)then + write(*,'(a,1x,i4,1x,e17.10)' ) 'iLine, xLambda = ', iLine, xLambda + write(*,'(a,1x,10(e17.10,1x))') 'fOld,fNew = ', fOld,fNew + write(*,'(a,1x,10(e17.10,1x))') 'fold + alpha*slopeInit*xLambda = ', fold + alpha*slopeInit*xLambda + write(*,'(a,1x,10(e17.10,1x))') 'resVecNew = ', resVecNew(min(iJac1,nState):min(iJac2,nState)) + write(*,'(a,1x,10(e17.10,1x))') 'xInc = ', xInc(min(iJac1,nState):min(iJac2,nState)) + end if - ! check feasibility - if(.not.feasible) cycle ! go back and impose constraints again + ! check feasibility + if(.not.feasible) cycle ! go back and impose constraints again - ! check convergence - ! NOTE: some efficiency gains possible by scaling the full newton step outside the line search loop - converged = checkConv(resVecNew,newtStepScaled*xScale,stateVecNew) - if(converged) return + ! check convergence + ! NOTE: some efficiency gains possible by scaling the full newton step outside the line search loop + converged = checkConv(resVecNew,newtStepScaled*xScale,stateVecNew) + if(converged) return - ! early return if not computing the line search - if(.not.doLineSearch) return + ! early return if not computing the line search + if(.not.doLineSearch) return - ! check if the function is accepted - if(fNew < fold + alpha*slopeInit*xLambda) return + ! check if the function is accepted + if(fNew < fold + alpha*slopeInit*xLambda) return - ! *** - ! *** IF GET TO HERE WE BACKTRACK - ! --> all remaining code simply computes the restricted step multiplier (xLambda) + ! *** + ! *** IF GET TO HERE WE BACKTRACK + ! --> all remaining code simply computes the restricted step multiplier (xLambda) - ! first backtrack: use quadratic - if(iLine==1)then - xLambdaTemp = -slopeInit / (2._rkind*(fNew - fOld - slopeInit) ) - if(xLambdaTemp > 0.5_rkind*xLambda) xLambdaTemp = 0.5_rkind*xLambda + ! first backtrack: use quadratic + if(iLine==1)then + xLambdaTemp = -slopeInit / (2._rkind*(fNew - fOld - slopeInit) ) + if(xLambdaTemp > 0.5_rkind*xLambda) xLambdaTemp = 0.5_rkind*xLambda - ! subsequent backtracks: use cubic - else + ! subsequent backtracks: use cubic + else - ! check that we did not back-track all the way back to the original value - if(iLine==maxLineSearch)then - message=trim(message)//'backtracked all the way back to the original value' - err=-20; return - end if + ! check that we did not back-track all the way back to the original value + if(iLine==maxLineSearch)then + message=trim(message)//'backtracked all the way back to the original value' + err=-20; return + end if - ! define rhs - rhs1 = fNew - fOld - xLambda*slopeInit - rhs2 = fPrev - fOld - xLambdaPrev*slopeInit + ! define rhs + rhs1 = fNew - fOld - xLambda*slopeInit + rhs2 = fPrev - fOld - xLambdaPrev*slopeInit - ! define coefficients - aCoef = (rhs1/(xLambda*xLambda) - rhs2/(xLambdaPrev*xLambdaPrev))/(xLambda - xLambdaPrev) - bCoef = (-xLambdaPrev*rhs1/(xLambda*xLambda) + xLambda*rhs2/(xLambdaPrev*xLambdaPrev)) / (xLambda - xLambdaPrev) + ! define coefficients + aCoef = (rhs1/(xLambda*xLambda) - rhs2/(xLambdaPrev*xLambdaPrev))/(xLambda - xLambdaPrev) + bCoef = (-xLambdaPrev*rhs1/(xLambda*xLambda) + xLambda*rhs2/(xLambdaPrev*xLambdaPrev)) / (xLambda - xLambdaPrev) - ! check if a quadratic - if(aCoef==0._rkind)then - xLambdaTemp = -slopeInit/(2._rkind*bCoef) + ! check if a quadratic + if(aCoef==0._rkind)then + xLambdaTemp = -slopeInit/(2._rkind*bCoef) - ! calculate cubic - else - disc = bCoef*bCoef - 3._rkind*aCoef*slopeInit - if(disc < 0._rkind)then - xLambdaTemp = 0.5_rkind*xLambda + ! calculate cubic else - xLambdaTemp = (-bCoef + sqrt(disc))/(3._rkind*aCoef) - end if - end if ! calculating cubic + disc = bCoef*bCoef - 3._rkind*aCoef*slopeInit + if(disc < 0._rkind)then + xLambdaTemp = 0.5_rkind*xLambda + else + xLambdaTemp = (-bCoef + sqrt(disc))/(3._rkind*aCoef) + end if + end if ! calculating cubic - ! constrain to <= 0.5*xLambda - if(xLambdaTemp > 0.5_rkind*xLambda) xLambdaTemp=0.5_rkind*xLambda + ! constrain to <= 0.5*xLambda + if(xLambdaTemp > 0.5_rkind*xLambda) xLambdaTemp=0.5_rkind*xLambda - end if ! subsequent backtracks + end if ! subsequent backtracks - ! save results - xLambdaPrev = xLambda - fPrev = fNew + ! save results + xLambdaPrev = xLambda + fPrev = fNew - ! constrain lambda - xLambda = max(xLambdaTemp, 0.1_rkind*xLambda) + ! constrain lambda + xLambda = max(xLambdaTemp, 0.1_rkind*xLambda) - end do lineSearch ! backtrack loop + end do lineSearch ! backtrack loop + end associate end subroutine lineSearchRefinement From 12b924f187ffd30b0b8b09a65b7075f3f57c8d55 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 17 Feb 2024 06:40:24 -0600 Subject: [PATCH 1146/1472] Implemented object-oriented methods to handle intent(in) scalar arguments for lineSearchRefinement. --- build/source/dshare/data_types.f90 | 23 +++++++++++--- build/source/engine/summaSolve4numrec.f90 | 37 ++++++++++++++--------- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 2ac144672..cd8175360 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -681,6 +681,13 @@ MODULE data_types procedure :: finalize => finalize_out_computJacob end type out_type_computJacob + type, public :: in_type_lineSearchRefinement ! class for intent(in) arguments in lineSearchRefinement call + logical(lgt) :: doLineSearch ! intent(in): flag to do the line search + real(rkind) :: fOld ! intent(in): old function value + contains + procedure :: initialize => initialize_in_lineSearchRefinement + end type in_type_lineSearchRefinement + type, public :: out_type_lineSearchRefinement ! class for intent(out) arguments in lineSearchRefinement call real(rkind) :: fNew ! intent(out): new function evaluation logical(lgt) :: converged ! intent(out): convergence flag @@ -1484,12 +1491,20 @@ subroutine finalize_out_computJacob(out_computJacob,err,cmessage) end subroutine finalize_out_computJacob ! **** lineSearchRefinement **** + subroutine initialize_in_lineSearchRefinement(in_lineSearchRefinement,doLineSearch,fOld) + class(in_type_lineSearchRefinement),intent(out) :: in_lineSearchRefinement ! class object for intent(out) computJacob arguments + logical(lgt),intent(in) :: doLineSearch ! intent(in): flag to do the line search + real(rkind) ,intent(in) :: fOld ! intent(in): old function value + in_lineSearchRefinement % doLineSearch = doLineSearch ! intent(in): flag to do the line search + in_lineSearchRefinement % fOld = fOld ! intent(in): old function value + end subroutine initialize_in_lineSearchRefinement + subroutine finalize_out_lineSearchRefinement(out_lineSearchRefinement,fNew,converged,err,message) class(out_type_lineSearchRefinement),intent(in) :: out_lineSearchRefinement ! class object for intent(out) computJacob arguments - real(rkind) :: fNew ! intent(out): new function evaluation - logical(lgt) :: converged ! intent(out): convergence flag - integer(i4b) :: err ! intent(out): error code - character(len=len_msg) :: message ! intent(out): error message + real(rkind) ,intent(out) :: fNew ! intent(out): new function evaluation + logical(lgt),intent(out) :: converged ! intent(out): convergence flag + integer(i4b),intent(out) :: err ! intent(out): error code + character(*),intent(out) :: message ! intent(out): error message fNew = out_lineSearchRefinement % fNew ! intent(out): new function evaluation converged = out_lineSearchRefinement % converged ! intent(out): convergence flag err = out_lineSearchRefinement % err ! intent(out): error code diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index a1a68c96c..78502c18b 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -66,15 +66,16 @@ module summaSolve4numrec_module ! provide access to the derived types to define the data structures USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! lookup tables - model_options, & ! defines the model decisions - in_type_computJacob, & ! class for computJacob arguments - out_type_computJacob, & ! class for computJacob arguments - out_type_lineSearchRefinement ! class for lineSearchRefinement arguments + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! lookup tables + model_options, & ! defines the model decisions + in_type_computJacob, & ! class for computJacob arguments + out_type_computJacob, & ! class for computJacob arguments + in_type_lineSearchRefinement, & ! class for lineSearchRefinement arguments + out_type_lineSearchRefinement ! class for lineSearchRefinement arguments ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: & @@ -227,6 +228,7 @@ subroutine summaSolve4numrec(& ! class objects for subroutine arguments type(in_type_computJacob) :: in_computJacob type(out_type_computJacob) :: out_computJacob + type(in_type_lineSearchRefinement) :: in_LSR type(out_type_lineSearchRefinement) :: out_LSR ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- @@ -316,7 +318,8 @@ subroutine summaSolve4numrec(& ! try to backtrack select case(ixStepRefinement) case(ixLineSearch) - call lineSearchRefinement(doRefine,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,out_LSR) + call in_LSR % initialize(doRefine,fOld) + call lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_LSR) call out_LSR % finalize(fNew,converged,err,cmessage) case(ixTrustRegion) call trustRegionRefinement(doRefine,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,cmessage) @@ -326,8 +329,9 @@ subroutine summaSolve4numrec(& ! check warnings: negative error code = warning; in this case back-tracked to the original value ! NOTE: Accept the full newton step if back-tracked to the original value if (err<0) then - doRefine=.false.; - call lineSearchRefinement(doRefine,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,out_LSR) + doRefine=.false.; + call in_LSR % initialize(doRefine,fOld) + call lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_LSR) call out_LSR % finalize(fNew,converged,err,cmessage) end if @@ -346,17 +350,16 @@ subroutine summaSolve4numrec(& ! ********************************************************************************************************* ! * internal subroutine lineSearchRefinement: refine the iteration increment using line searches ! ********************************************************************************************************* - subroutine lineSearchRefinement(doLineSearch,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,out_LSR) + subroutine lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_LSR) ! provide access to the matrix routines USE matrixOper_module, only: computeGradient implicit none ! input - logical(lgt),intent(in) :: doLineSearch ! flag to do the line search + type(in_type_lineSearchRefinement),intent(in) :: in_LSR ! class object for intent(in) arguments real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector real(rkind),intent(in) :: newtStepScaled(:) ! scaled newton step real(rkind),intent(in) :: aJacScaled(:,:) ! scaled jacobian matrix real(rkind),intent(in) :: rVecScaled(:) ! scaled residual vector - real(rkind),intent(in) :: fOld ! old function value ! output real(rkind),intent(out) :: stateVecNew(:) ! new state vector real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector @@ -381,6 +384,10 @@ subroutine lineSearchRefinement(doLineSearch,stateVecTrial,newtStepScaled,aJacSc real(rkind) :: fPrev ! previous function evaluation (used in the cubic) ! -------------------------------------------------------------------------------------------------------- associate(& + ! intent(in) variables + doLineSearch => in_LSR % doLineSearch ,& ! flag to do the line search + fOld => in_LSR % fOld ,& ! old function value + ! intent(out) variables fNew => out_LSR % fNew ,& ! new function evaluation converged => out_LSR % converged ,& ! convergence flag err => out_LSR % err ,& ! error code From 4af3a5acf3def1636160c574468a7d4990843d5d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 20 Feb 2024 10:48:02 +0900 Subject: [PATCH 1147/1472] should distribute with build on production not debug --- build/cmake/build.mac.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/cmake/build.mac.bash b/build/cmake/build.mac.bash index d620f6dd1..e2a9014f9 100755 --- a/build/cmake/build.mac.bash +++ b/build/cmake/build.mac.bash @@ -10,5 +10,5 @@ export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) #export FLAGS_OPT="-flto=1" # -flto=1 is slow to compile, but might want to use -cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials_Debug +cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials cmake --build ../cmake_build --target all From 6d3ac18000339bb06655b86e546608df4ed23042 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 20 Feb 2024 11:34:49 +0900 Subject: [PATCH 1148/1472] Fixed allocation error in paramCheck that was segfaulting with gcc13. critSoilWilting and critSoilTranspire should be associated as size 1 --- build/source/engine/paramCheck.f90 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build/source/engine/paramCheck.f90 b/build/source/engine/paramCheck.f90 index 96c2905e2..c372f2aa0 100644 --- a/build/source/engine/paramCheck.f90 +++ b/build/source/engine/paramCheck.f90 @@ -117,8 +117,8 @@ subroutine paramCheck(mpar_data,err,message) heightCanopyTop => mpar_data%var(iLookPARAM%heightCanopyTop)%dat(1), & ! intent(in): [dp] height at the top of the vegetation canopy (m) heightCanopyBottom => mpar_data%var(iLookPARAM%heightCanopyBottom)%dat(1),& ! intent(in): [dp] height at the bottom of the vegetation canopy (m) ! transpiration - critSoilWilting => mpar_data%var(iLookPARAM%critSoilWilting)%dat, & ! intent(in): [dp] critical vol. liq. water content when plants are wilting (-) - critSoilTranspire => mpar_data%var(iLookPARAM%critSoilTranspire)%dat, & ! intent(in): [dp] critical vol. liq. water content when transpiration is limited (-) + critSoilWilting => mpar_data%var(iLookPARAM%critSoilWilting)%dat(1), & ! intent(in): [dp] critical vol. liq. water content when plants are wilting (-) + critSoilTranspire => mpar_data%var(iLookPARAM%critSoilTranspire)%dat(1), & ! intent(in): [dp] critical vol. liq. water content when transpiration is limited (-) ! soil properties fieldCapacity => mpar_data%var(iLookPARAM%fieldCapacity)%dat(1), & ! intent(in): [dp] field capacity (-) theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): [dp(:)] soil porosity (-) @@ -132,7 +132,7 @@ subroutine paramCheck(mpar_data,err,message) endif ! check that the maximum transpiration limit is within bounds - if( any(critSoilTranspire(1) > theta_sat) .or. any(critSoilTranspire(1) < theta_res) )then + if( any(critSoilTranspire > theta_sat) .or. any(critSoilTranspire < theta_res) )then print*, 'theta_res = ', theta_res print*, 'theta_sat = ', theta_sat print*, 'critSoilTranspire = ', critSoilTranspire @@ -142,7 +142,7 @@ subroutine paramCheck(mpar_data,err,message) end if ! check that the soil wilting point is within bounds - if( any(critSoilWilting(1) > theta_sat) .or. any(critSoilWilting(1) < theta_res) )then + if( any(critSoilWilting > theta_sat) .or. any(critSoilWilting < theta_res) )then print*, 'theta_res = ', theta_res print*, 'theta_sat = ', theta_sat print*, 'critSoilWilting = ', critSoilWilting @@ -162,7 +162,7 @@ subroutine paramCheck(mpar_data,err,message) end if ! check transpiration - if( any(critSoilTranspire < critSoilWilting) )then + if( critSoilTranspire < critSoilWilting )then write(message,'(a,i0,a)') trim(message)//'critical point for transpiration is less than the wilting point' err=20; return endif From e43c766df3118978e015a8a4785284a4625cb867 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 20 Feb 2024 11:57:36 +0900 Subject: [PATCH 1149/1472] groundwatr missing a theta_sat(1:nSoil) specification that caused segfault in gcc13, also had some extra associations that weren't needed --- build/source/engine/groundwatr.f90 | 43 +++++++++++++----------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/build/source/engine/groundwatr.f90 b/build/source/engine/groundwatr.f90 index c1c87b92c..26dd6ed11 100644 --- a/build/source/engine/groundwatr.f90 +++ b/build/source/engine/groundwatr.f90 @@ -125,34 +125,29 @@ subroutine groundwatr(& allocate(out_groundwatr % mLayerBaseflow(in_groundwatr%nSoil),out_groundwatr % dBaseflow_dMatric(in_groundwatr%nSoil,in_groundwatr%nSoil)) ! allocate intent(out) data structure components associate(& ! input: model control - nSnow => in_groundwatr % nSnow, & ! intent(in): [i4b] number of snow layers - nSoil => in_groundwatr % nSoil, & ! intent(in): [i4b] number of soil layers - nLayers => in_groundwatr % nLayers, & ! intent(in): [i4b] total number of layers - getSatDepth => in_groundwatr % firstFluxCall, & ! intent(in): [lgt] logical flag to compute index of the lowest saturated layer + nSnow => in_groundwatr % nSnow, & ! intent(in): [i4b] number of snow layers + nSoil => in_groundwatr % nSoil, & ! intent(in): [i4b] number of soil layers + nLayers => in_groundwatr % nLayers, & ! intent(in): [i4b] total number of layers + getSatDepth => in_groundwatr % firstFluxCall, & ! intent(in): [lgt] logical flag to compute index of the lowest saturated layer ! input: state and diagnostic variables - mLayerdTheta_dPsi => in_groundwatr % mLayerdTheta_dPsi, & ! intent(in): [dp] derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) - mLayerMatricHeadLiq => in_groundwatr % mLayerMatricHeadLiqTrial, & ! intent(in): [dp] matric head in each layer at the current iteration (m) - mLayerVolFracLiq => in_groundwatr % mLayerVolFracLiqTrial, & ! intent(in): [dp] volumetric fraction of liquid water (-) - mLayerVolFracIce => in_groundwatr % mLayerVolFracIceTrial, & ! intent(in): [dp] volumetric fraction of ice (-) + mLayerdTheta_dPsi => in_groundwatr % mLayerdTheta_dPsi, & ! intent(in): [dp] derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) + mLayerMatricHeadLiq => in_groundwatr % mLayerMatricHeadLiqTrial, & ! intent(in): [dp] matric head in each layer at the current iteration (m) + mLayerVolFracLiq => in_groundwatr % mLayerVolFracLiqTrial, & ! intent(in): [dp] volumetric fraction of liquid water (-) + mLayerVolFracIce => in_groundwatr % mLayerVolFracIceTrial, & ! intent(in): [dp] volumetric fraction of ice (-) ! input: baseflow parameters - fieldCapacity => mpar_data%var(iLookPARAM%fieldCapacity)%dat(1), & ! intent(in): [dp] field capacity (-) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): [dp] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat, & ! intent(in): [dp] residual volumetric water content (-) - ! input: van Genuchten soil parametrers - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat, & ! intent(in): [dp] van Genutchen "alpha" parameter (m-1) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat, & ! intent(in): [dp] van Genutchen "n" parameter (-) - vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat, & ! intent(in): [dp] van Genutchen "m" parameter (-) + fieldCapacity => mpar_data%var(iLookPARAM%fieldCapacity)%dat(1), & ! intent(in): [dp] field capacity (-) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): [dp] soil porosity (-) ! input-output: baseflow - ixSaturation => io_groundwatr % ixSaturation, & ! intent(inout): [i4b] index of lowest saturated layer (NOTE: only computed on the first iteration) + ixSaturation => io_groundwatr % ixSaturation, & ! intent(inout): [i4b] index of lowest saturated layer (NOTE: only computed on the first iteration) ! output: diagnostic variables - scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1), & ! intent(out): [dp] exfiltration from the soil profile (m s-1) - mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat, & ! intent(out): [dp(:)] column outflow from each soil layer (m3 s-1) + scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1), & ! intent(out): [dp] exfiltration from the soil profile (m s-1) + mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat, & ! intent(out): [dp(:)] column outflow from each soil layer (m3 s-1) ! output: baseflow - mLayerBaseflow => out_groundwatr % mLayerBaseflow, & ! intent(out): [dp(:)] baseflow from each soil layer (m s-1) - dBaseflow_dMatric => out_groundwatr % dBaseflow_dMatric, & ! intent(out): [dp(:,:)] derivative in baseflow w.r.t. matric head (s-1) + mLayerBaseflow => out_groundwatr % mLayerBaseflow, & ! intent(out): [dp(:)] baseflow from each soil layer (m s-1) + dBaseflow_dMatric => out_groundwatr % dBaseflow_dMatric, & ! intent(out): [dp(:,:)] derivative in baseflow w.r.t. matric head (s-1) ! output: error control - err => out_groundwatr % err, & ! intent(out): [i4b] error code - message => out_groundwatr % cmessage & ! intent(out): [character] error message + err => out_groundwatr % err, & ! intent(out): [i4b] error code + message => out_groundwatr % cmessage & ! intent(out): [character] error message ) ! end association to variables in data structures ! initialize error control err=0; message='groundwatr/' @@ -348,7 +343,7 @@ subroutine computeBaseflow(& totalColumnOutflow = sum(mLayerColumnOutflow(1:nSoil))/HRUarea ! compute the available storage (m) - availStorage = sum(mLayerDepth(1:nSoil)*(theta_sat - (mLayerVolFracLiq(1:nSoil)+mLayerVolFracIce(1:nSoil)))) + availStorage = sum(mLayerDepth(1:nSoil)*(theta_sat(1:nSoil) - (mLayerVolFracLiq(1:nSoil)+mLayerVolFracIce(1:nSoil)))) ! compute the smoothing function (-) if (availStorage < xMinEval) then @@ -362,7 +357,7 @@ subroutine computeBaseflow(& dLogFunc_dLiq(:) = 0._rkind end if - ! compute the exfiltartion (m s-1) + ! compute the exfiltration (m s-1) if (totalColumnInflow > totalColumnOutflow .and. logF > tiny(1._rkind)) then scalarExfiltration = logF*(totalColumnInflow - totalColumnOutflow) ! m s-1 else From a23ac832462e5d50168c179a6d893ec966b93ea1 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 20 Feb 2024 16:32:01 +0900 Subject: [PATCH 1150/1472] layerDivide need to use indices in count(layerType(1:nLayers+1)) or compiler internal segfault in gcc13 --- build/source/engine/layerDivide.f90 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/build/source/engine/layerDivide.f90 b/build/source/engine/layerDivide.f90 index 256f79b4e..835cb4ffd 100644 --- a/build/source/engine/layerDivide.f90 +++ b/build/source/engine/layerDivide.f90 @@ -222,12 +222,12 @@ subroutine layerDivide(& mLayerTemp(1) = min(maxFrozenSnowTemp,surfaceLayerSoilTemp) ! snow temperature (K) ! compute the fraction of liquid water associated with the layer temperature - fracLiq = fracliquid(mLayerTemp(1),fc_param) + fracLiq = fracliquid(mLayerTemp(1),fc_param) ! compute volumeteric fraction of liquid water and ice volFracWater = (scalarSWE/scalarSnowDepth)/iden_water ! volumetric fraction of total water (liquid and ice) mLayerVolFracIce(1) = (1._rkind - fracLiq)*volFracWater*(iden_water/iden_ice) ! volumetric fraction of ice (-) - mLayerVolFracLiq(1) = fracLiq *volFracWater ! volumetric fraction of liquid water (-) + mLayerVolFracLiq(1) = fracLiq *volFracWater ! volumetric fraction of liquid water (-) ! end association with local variables to the information in the data structures) end associate @@ -331,8 +331,8 @@ subroutine layerDivide(& layerType(nSnow+2:nLayers+1) = iname_soil ! identify the number of snow and soil layers, and check all is a-OK - nSnow = count(layerType==iname_snow) - nSoil = count(layerType==iname_soil) + nSnow = count(layerType(1:nLayers+1)==iname_snow) + nSoil = count(layerType(1:nLayers+1)==iname_soil) nLayers = nSnow + nSoil ! re-set coordinate variables @@ -434,8 +434,8 @@ subroutine addModelLayer(dataStruct,metaStruct,ix_divide,nSnow,nLayers,err,messa if(stateVariable)then if(ix_upper > 0)then ! (only copy data if the vector exists -- can be a variable for snow, with no layers) if(ix_divide > 0)then - dataStruct%var(ivar)%dat(1:ix_divide) = tempVec_rkind(1:ix_divide) ! copy data - dataStruct%var(ivar)%dat(ix_divide+1) = tempVec_rkind(ix_divide) ! repeat data for the sub-divided layer + dataStruct%var(ivar)%dat(1:ix_divide) = tempVec_rkind(1:ix_divide) ! copy data + dataStruct%var(ivar)%dat(ix_divide+1) = tempVec_rkind(ix_divide) ! repeat data for the sub-divided layer end if if(ix_upper > ix_divide) & dataStruct%var(ivar)%dat(ix_divide+2:ix_upper+1) = tempVec_rkind(ix_divide+1:ix_upper) ! copy data @@ -464,8 +464,8 @@ subroutine addModelLayer(dataStruct,metaStruct,ix_divide,nSnow,nLayers,err,messa if(stateVariable)then if(ix_upper > 0)then ! (only copy data if the vector exists -- can be a variable for snow, with no layers) if(ix_divide > 0)then - dataStruct%var(ivar)%dat(1:ix_divide) = tempVec_i4b(1:ix_divide) ! copy data - dataStruct%var(ivar)%dat(ix_divide+1) = tempVec_i4b(ix_divide) ! repeat data for the sub-divided layer + dataStruct%var(ivar)%dat(1:ix_divide) = tempVec_i4b(1:ix_divide) ! copy data + dataStruct%var(ivar)%dat(ix_divide+1) = tempVec_i4b(ix_divide) ! repeat data for the sub-divided layer end if if(ix_upper > ix_divide) & dataStruct%var(ivar)%dat(ix_divide+2:ix_upper+1) = tempVec_i4b(ix_divide+1:ix_upper) ! copy data From 0b921c65c96ef03e388ee7ed0f232588ebedbcbf Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 20 Feb 2024 16:58:34 +0900 Subject: [PATCH 1151/1472] If we do not have fractionFuture(1:nTDH) in var_derive, then we get an internal compiler segment fault with gcc13 --- build/source/engine/qTimeDelay.f90 | 22 +++++++++++----------- build/source/engine/var_derive.f90 | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/build/source/engine/qTimeDelay.f90 b/build/source/engine/qTimeDelay.f90 index 241d99a2e..9bcdc312d 100644 --- a/build/source/engine/qTimeDelay.f90 +++ b/build/source/engine/qTimeDelay.f90 @@ -49,19 +49,19 @@ subroutine qOverland(& err,message) ! error control implicit none ! input - integer(i4b),intent(in) :: ixRouting ! index for routing method - real(rkind),intent(in) :: averageTotalRunoff ! total runoff to the channel from all active components (m s-1) - real(rkind),intent(in) :: fracFuture(:) ! fraction of runoff in future time steps (m s-1) - real(rkind),intent(inout) :: qFuture(:) ! runoff in future time steps (m s-1) + integer(i4b),intent(in) :: ixRouting ! index for routing method + real(rkind),intent(in) :: averageTotalRunoff ! total runoff to the channel from all active components (m s-1) + real(rkind),intent(in) :: fracFuture(:) ! fraction of runoff in future time steps (m s-1) + real(rkind),intent(inout) :: qFuture(:) ! runoff in future time steps (m s-1) ! output - real(rkind),intent(out) :: averageInstantRunoff ! instantaneous runoff (m s-1) - real(rkind),intent(out) :: averageRoutedRunoff ! routed runoff (m s-1) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + real(rkind),intent(out) :: averageInstantRunoff ! instantaneous runoff (m s-1) + real(rkind),intent(out) :: averageRoutedRunoff ! routed runoff (m s-1) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! internal - real(rkind),parameter :: valueMissing=-9999._rkind ! missing value - integer(i4b) :: nTDH ! number of points in the time-delay histogram - integer(i4b) :: iFuture ! index in time delay histogram + real(rkind),parameter :: valueMissing=-9999._rkind ! missing value + integer(i4b) :: nTDH ! number of points in the time-delay histogram + integer(i4b) :: iFuture ! index in time delay histogram ! initialize error control err=0; message='qOverland/' diff --git a/build/source/engine/var_derive.f90 b/build/source/engine/var_derive.f90 index 78786dc6b..ca1b5bbc3 100644 --- a/build/source/engine/var_derive.f90 +++ b/build/source/engine/var_derive.f90 @@ -435,7 +435,7 @@ subroutine fracFuture(bpar_data,bvar_data,err,message) end do ! (looping through future time steps) ! check that we have enough bins - sumFrac = sum(fractionFuture) + sumFrac = sum(fractionFuture(1:nTDH)) if(abs(1._rkind - sumFrac) > tolerFrac)then write(*,*) 'WARNING: The fraction of basin runoff histogram being accounted for by time delay vector is ', sumFrac write(*,*) 'This is less than allowed by tolerFrac = ', tolerFrac @@ -446,7 +446,7 @@ subroutine fracFuture(bpar_data,bvar_data,err,message) write(*,*) ' -- note that nTimeDelay defines the number of time steps in the time delay histogram' end if ! ensure the fraction sums to one - fractionFuture = fractionFuture/sumFrac + fractionFuture(1:nTDH) = fractionFuture(1:nTDH)/sumFrac ! ** error checking case default; err=20; message=trim(message)//'cannot find option for sub-grid routing'; return From b27093881944427867170eb9e7a61ba96573432a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 20 Feb 2024 17:43:50 +0900 Subject: [PATCH 1152/1472] Need mLayerVolFracIceDelta = mLayerVolFracIceTrial - mLayerVolFracIce(1:nLayers) and also with mLayerEnthalpy in varSubstep or internal complier segfault with gcc13 --- build/source/engine/varSubstep.f90 | 35 +++++++++++++++++------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 925b50131..ff69b6fc9 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -704,7 +704,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector of volumetric fraction of liquid water (-) real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector of volumetric fraction of ice (-) - ! derivative of state variables + ! prime state variables real(rkind) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) real(rkind) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) real(rkind) :: scalarCanopyWatPrime ! trial value for liquid water storage in the canopy (kg m-2) @@ -713,12 +713,14 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! trial vector of total water matric potential (m) real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! trial vector of liquid water matric potential (m) real(rkind) :: scalarAquiferStoragePrime ! trial value for storage of water in the aquifer (m) - ! diagnostic variables + ! diagnostic prime or delta variables real(rkind) :: scalarCanopyLiqPrime ! trial value for mass of liquid water on the vegetation canopy (kg m-2) real(rkind) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIceDelta ! delta value for mass of ice on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyHmixDelta ! delta value for mixture enthalpy of the vegetation canopy (J m-3) real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector of volumetric fraction of liquid water (-) real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector of volumetric fraction of ice (-) - real(rkind) :: scalarCanopyHmixDelta ! delta value for mixture enthalpy of the vegetation canopy (J m-3) + real(rkind),dimension(nLayers) :: mLayerVolFracIceDelta ! delta vector volumetric fraction of ice of snow + soil (-) real(rkind),dimension(nLayers) :: mLayerHmixDelta ! delta vector of mixture enthalpy of snow + soil (J m-3) ! ------------------------------------------------------------------------------------------------------------------- @@ -989,24 +991,27 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec select case(ixNumericalMethod) case(ida); ! do nothing, already computed case(kinsol, numrec) + ! calculate delta ice + scalarCanopyIceDelta = scalarCanopyIceTrial - scalarCanopyIce + mLayerVolFracIceDelta = mLayerVolFracIceTrial - mLayerVolFracIce(1:nLayers) ! initialize delta mixture enthalpy to delta temperature component of enthalpy, no difference in canopy air space scalarCanopyHmixDelta = scalarCanopyEnthalpyTrial - scalarCanopyEnthalpy - mLayerHmixDelta = mLayerEnthalpyTrial - mLayerEnthalpy + mLayerHmixDelta = mLayerEnthalpyTrial - mLayerEnthalpy(1:nLayers) ! compute mixture enthalpy for current values, do on delta value so only have to do once call enthalpy2DeltaH(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): model indices - ! input: ice content change - scalarCanopyIceTrial - scalarCanopyIce, & ! intent(in): delta value for canopy ice content (kg m-2) - mLayerVolFracIceTrial - mLayerVolFracIce, & ! intent(in): delta vector of volumetric ice water content (-) - ! input/output: enthalpy - scalarCanopyHmixDelta, & ! intent(inout): delta value for mixture enthalpy of the vegetation canopy (J m-3) - mLayerHmixDelta, & ! intent(inout): delta vector of mixture enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,cmessage) ! intent(out): error control + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): model indices + ! input: ice content change + scalarCanopyIceDelta, & ! intent(in): delta value for canopy ice content (kg m-2) + mLayerVolFracIceDelta, & ! intent(in): delta vector of volumetric ice water content (-) + ! input/output: enthalpy + scalarCanopyHmixDelta, & ! intent(inout): delta value for mixture enthalpy of the vegetation canopy (J m-3) + mLayerHmixDelta, & ! intent(inout): delta vector of mixture enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! compute energy balance, maybe should use to check for step reduction From c0624a68042c34600515f4b212e5104eb3630b5e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 20 Feb 2024 19:24:53 +0900 Subject: [PATCH 1153/1472] indexState has to have (1:nLayers) in ixHydType or internal segfault with gcc13 --- build/source/engine/indexState.f90 | 125 ++++++++++++++--------------- 1 file changed, 61 insertions(+), 64 deletions(-) diff --git a/build/source/engine/indexState.f90 b/build/source/engine/indexState.f90 index be42a07ce..18c2f4ae1 100644 --- a/build/source/engine/indexState.f90 +++ b/build/source/engine/indexState.f90 @@ -287,85 +287,85 @@ subroutine indexSplit(in_indexSplit, & ! intent(in) : number of implicit none ! -------------------------------------------------------------------------------------------------------------------------------- ! input - type(in_type_indexSplit),intent(in) :: in_indexSplit ! number of model layers and states in a subset - logical(lgt),intent(in) :: stateSubsetMask(:) ! logical vector (.true. if state is in the subset) - type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers + type(in_type_indexSplit),intent(in) :: in_indexSplit ! number of model layers and states in a subset + logical(lgt),intent(in) :: stateSubsetMask(:) ! logical vector (.true. if state is in the subset) + type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers ! output - type(out_type_indexSplit),intent(out) :: out_indexSplit ! error control + type(out_type_indexSplit),intent(out) :: out_indexSplit ! error control ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables - integer(i4b) :: iVar ! variable index - integer(i4b) :: ixVegWat ! index of total water in the vegetation canopy - integer(i4b) :: ixVegLiq ! index of liquid water in the vegetation canopy - integer(i4b) :: ixTopWat ! index of upper-most total water state in the snow-soil subdomain - integer(i4b) :: ixTopLiq ! index of upper-most liquid water state in the snow-soil subdomain - integer(i4b) :: ixTopMat ! index of upper-most total water matric potential state in the soil subdomain - integer(i4b) :: ixTopLMP ! index of upper-most liquid water matric potential state in the soil subdomain - integer(i4b),dimension(in_indexSplit % nSubset) :: ixSequence ! sequential index in model state vector - logical(lgt),dimension(in_indexSplit % nSubset) :: stateTypeMask ! mask of state vector for specific state subsets - logical(lgt),dimension(in_indexSplit % nLayers) :: volFracWat_mask ! mask of layers within the snow+soil domain - logical(lgt),dimension(in_indexSplit % nSoil) :: matricHead_mask ! mask of layers within the soil domain - character(len=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iVar ! variable index + integer(i4b) :: ixVegWat ! index of total water in the vegetation canopy + integer(i4b) :: ixVegLiq ! index of liquid water in the vegetation canopy + integer(i4b) :: ixTopWat ! index of upper-most total water state in the snow-soil subdomain + integer(i4b) :: ixTopLiq ! index of upper-most liquid water state in the snow-soil subdomain + integer(i4b) :: ixTopMat ! index of upper-most total water matric potential state in the soil subdomain + integer(i4b) :: ixTopLMP ! index of upper-most liquid water matric potential state in the soil subdomain + integer(i4b),dimension(in_indexSplit % nSubset) :: ixSequence ! sequential index in model state vector + logical(lgt),dimension(in_indexSplit % nSubset) :: stateTypeMask ! mask of state vector for specific state subsets + logical(lgt),dimension(in_indexSplit % nLayers) :: volFracWat_mask ! mask of layers within the snow+soil domain + logical(lgt),dimension(in_indexSplit % nSoil) :: matricHead_mask ! mask of layers within the soil domain + character(len=256) :: cmessage ! error message of downwind routine ! ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ! ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ! make association to variables in the data structures fullState: associate(& ! number of snow and soil layers, total number of layers, and number of states in the subset - nSnow => in_indexSplit % nSnow ,& ! intent(in): [i4b] number of snow layers - nSoil => in_indexSplit % nSoil ,& ! intent(in): [i4b] number of soil layers - nLayers => in_indexSplit % nLayers ,& ! intent(in): [i4b] total number of layers - nSubset => in_indexSplit % nSubset ,& ! intent(in): [i4b] number of states in the subset + nSnow => in_indexSplit % nSnow ,& ! intent(in): [i4b] number of snow layers + nSoil => in_indexSplit % nSoil ,& ! intent(in): [i4b] number of soil layers + nLayers => in_indexSplit % nLayers ,& ! intent(in): [i4b] total number of layers + nSubset => in_indexSplit % nSubset ,& ! intent(in): [i4b] number of states in the subset ! indices of model state variables for the vegetation domain - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) ! indices of the top model state variables in the snow+soil system - ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow-soil subdomain - ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow-soil subdomain + ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow-soil subdomain + ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow-soil subdomain ! index of the storage of water in the aquifer - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of the storage of water in the aquifer + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of the storage of water in the aquifer ! indices of model state variables - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset (missing for values not in the subset) - ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in): [i4b(:)] indices defining the domain of the state (iname_veg, iname_snow, iname_soil) - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) - ixAllState => indx_data%var(iLookINDEX%ixAllState)%dat ,& ! intent(in): [i4b(:)] list of indices for all model state variables (1,2,3,...nState) - ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain - ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset (missing for values not in the subset) + ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in): [i4b(:)] indices defining the domain of the state (iname_veg, iname_snow, iname_soil) + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) + ixAllState => indx_data%var(iLookINDEX%ixAllState)%dat ,& ! intent(in): [i4b(:)] list of indices for all model state variables (1,2,3,...nState) + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain ! indices of the entire state vector, all model layers, and soil layers - ixSoilState => indx_data%var(iLookINDEX%ixSoilState)%dat ,& ! intent(in): [i4b(:)] list of indices for all soil layers - ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat ,& ! intent(in): [i4b(:)] list of indices for all model layers + ixSoilState => indx_data%var(iLookINDEX%ixSoilState)%dat ,& ! intent(in): [i4b(:)] list of indices for all soil layers + ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat ,& ! intent(in): [i4b(:)] list of indices for all model layers ! vector of energy indices for the snow and soil domains ! NOTE: states not in the subset are equal to integerMissing - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain - ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow domain - ixSoilOnlyNrg => indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the soil domain + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow domain + ixSoilOnlyNrg => indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the soil domain ! vector of hydrology indices for the snow and soil domains ! NOTE: states not in the subset are equal to integerMissing - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow domain - ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the soil domain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow domain + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the soil domain ! indices of active model layers - ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat ,& ! intent(in): [i4b(:)] index of active model layers (inactive=integerMissing) + ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat ,& ! intent(in): [i4b(:)] index of active model layers (inactive=integerMissing) ! number of state variables of a specific type - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain - nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain - nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain + nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain + nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain ! error control - err => out_indexSplit % err ,& ! intent(out): [i4b] error code - message => out_indexSplit % cmessage & ! intent(out): [character] error message + err => out_indexSplit % err ,& ! intent(out): [i4b] error code + message => out_indexSplit % cmessage & ! intent(out): [character] error message ) ! association to variables in the data structures ! ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ @@ -388,7 +388,7 @@ subroutine indexSplit(in_indexSplit, & ! intent(in) : number of ! ------------------------------------------- ! get different masks - volFracWat_mask = (ixHydType==iname_watLayer .or. ixHydType==iname_liqLayer) + volFracWat_mask = (ixHydType (1:nLayers)==iname_watLayer .or. ixHydType( 1:nLayers)==iname_liqLayer) matricHead_mask = (ixHydType(nSnow+1:nLayers)==iname_matLayer .or. ixHydType(nSnow+1:nLayers)==iname_lmpLayer) ! get state subsets for desired variables @@ -463,10 +463,10 @@ subroutine indexSplit(in_indexSplit, & ! intent(in) : number of ! define the mask select case(iVar) - case(iLookINDEX%ixNrgOnly); stateTypeMask = (ixStateType_subset==iname_nrgCanair .or. ixStateType_subset==iname_nrgCanopy .or. ixStateType_subset==iname_nrgLayer) ! list of indices for all energy states - case(iLookINDEX%ixHydOnly); stateTypeMask = (ixStateType_subset==iname_watLayer .or. ixStateType_subset==iname_liqLayer .or. ixStateType_subset==iname_matLayer .or. ixStateType_subset==iname_lmpLayer) ! list of indices for all hydrology states - case(iLookINDEX%ixMatOnly); stateTypeMask = (ixStateType_subset==iname_matLayer .or. ixStateType_subset==iname_lmpLayer) ! list of indices for matric head state variables - case(iLookINDEX%ixMassOnly); stateTypeMask = (ixStateType_subset==iname_watCanopy) ! list of indices for hydrology states (mass of water) + case(iLookINDEX%ixNrgOnly); stateTypeMask = (ixStateType_subset==iname_nrgCanair .or. ixStateType_subset==iname_nrgCanopy .or. ixStateType_subset==iname_nrgLayer) ! list of indices for all energy states + case(iLookINDEX%ixHydOnly); stateTypeMask = (ixStateType_subset==iname_watLayer .or. ixStateType_subset==iname_liqLayer .or. ixStateType_subset==iname_matLayer .or. ixStateType_subset==iname_lmpLayer) ! list of indices for all hydrology states + case(iLookINDEX%ixMatOnly); stateTypeMask = (ixStateType_subset==iname_matLayer .or. ixStateType_subset==iname_lmpLayer) ! list of indices for matric head state variables + case(iLookINDEX%ixMassOnly); stateTypeMask = (ixStateType_subset==iname_watCanopy) ! list of indices for hydrology states (mass of water) case default; cycle ! only need to process the above variables end select ! iVar @@ -519,16 +519,16 @@ end subroutine indexSplit subroutine indxSubset(ixSubset,ixMaster,mask,err,message) implicit none ! input-output: subset of indices for allocation/population - integer(i4b),intent(inout),allocatable :: ixSubset(:) ! subset of indices + integer(i4b),intent(inout),allocatable :: ixSubset(:) ! subset of indices ! input - integer(i4b),intent(in) :: ixMaster(:) ! full list of indices - logical(lgt),intent(in) :: mask(:) ! desired indices + integer(i4b),intent(in) :: ixMaster(:) ! full list of indices + logical(lgt),intent(in) :: mask(:) ! desired indices ! error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! ----------------------------------------------------------------------------------------------------------------------------------- ! local variables - integer(i4b) :: nSubset ! length of the subset + integer(i4b) :: nSubset ! length of the subset ! ----------------------------------------------------------------------------------------------------------------------------------- ! initialize errors err=0; message="indxSubset/" @@ -563,9 +563,6 @@ subroutine indxSubset(ixSubset,ixMaster,mask,err,message) end subroutine indxSubset - - - ! ********************************************************************************************************** ! private subroutine resizeIndx: re-size specific index vectors ! ********************************************************************************************************** From a2163807b07a27ef3f3a907a92693d4057d50afd Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 20 Feb 2024 19:28:19 +0900 Subject: [PATCH 1154/1472] spaces from last commit --- build/source/engine/indexState.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/indexState.f90 b/build/source/engine/indexState.f90 index 18c2f4ae1..51e2f9049 100644 --- a/build/source/engine/indexState.f90 +++ b/build/source/engine/indexState.f90 @@ -388,7 +388,7 @@ subroutine indexSplit(in_indexSplit, & ! intent(in) : number of ! ------------------------------------------- ! get different masks - volFracWat_mask = (ixHydType (1:nLayers)==iname_watLayer .or. ixHydType( 1:nLayers)==iname_liqLayer) + volFracWat_mask = (ixHydType( 1:nLayers)==iname_watLayer .or. ixHydType( 1:nLayers)==iname_liqLayer) matricHead_mask = (ixHydType(nSnow+1:nLayers)==iname_matLayer .or. ixHydType(nSnow+1:nLayers)==iname_lmpLayer) ! get state subsets for desired variables From a1a5807c620e46d65fda208d9908f1f30d6a9536 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 21 Feb 2024 10:57:26 +0900 Subject: [PATCH 1155/1472] closedForm does not updateCp or needCm in prime version too --- build/source/engine/eval8summaWithPrime.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index ad62a9327..59639ad94 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -205,8 +205,8 @@ subroutine eval8summaWithPrime(& character(LEN=256) :: cmessage ! error message of downwind routine logical(lgt) :: updateCp ! flag to indicate if we update Cp at each step, set with nrgConserv choice and updateCp_closedForm flag logical(lgt) :: needCm ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m,, set with nrgConserv choice and needCm_closedForm flag - logical(lgt),parameter :: updateCp_closedForm=.true. ! nrgConserv = closedForm flag to indicate if we update Cp at each step - logical(lgt),parameter :: needCm_closedForm=.true. ! nrgConserv = closedForm flag to indicate if the energy equation contains Cm = dH_T/dTheta_m + logical(lgt),parameter :: updateCp_closedForm=.false. ! nrgConserv = closedForm flag to indicate if we update Cp at each step + logical(lgt),parameter :: needCm_closedForm=.false. ! nrgConserv = closedForm flag to indicate if the energy equation contains Cm = dH_T/dTheta_m ! -------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures From 3afcd0efc90994a65ebc9fbd4699d76296657fa6 Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 20 Feb 2024 19:59:05 -0600 Subject: [PATCH 1156/1472] Applied object-oriented methods to simplify call to trustRegionRefinement in summaSolve4numrec.f90. --- build/source/dshare/data_types.f90 | 8 +- build/source/engine/summaSolve4numrec.f90 | 98 +++++++++++++---------- 2 files changed, 60 insertions(+), 46 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index cd8175360..061317e1b 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -682,7 +682,7 @@ MODULE data_types end type out_type_computJacob type, public :: in_type_lineSearchRefinement ! class for intent(in) arguments in lineSearchRefinement call - logical(lgt) :: doLineSearch ! intent(in): flag to do the line search + logical(lgt) :: doSearch ! intent(in): flag to do the line search real(rkind) :: fOld ! intent(in): old function value contains procedure :: initialize => initialize_in_lineSearchRefinement @@ -1491,11 +1491,11 @@ subroutine finalize_out_computJacob(out_computJacob,err,cmessage) end subroutine finalize_out_computJacob ! **** lineSearchRefinement **** - subroutine initialize_in_lineSearchRefinement(in_lineSearchRefinement,doLineSearch,fOld) + subroutine initialize_in_lineSearchRefinement(in_lineSearchRefinement,doSearch,fOld) class(in_type_lineSearchRefinement),intent(out) :: in_lineSearchRefinement ! class object for intent(out) computJacob arguments - logical(lgt),intent(in) :: doLineSearch ! intent(in): flag to do the line search + logical(lgt),intent(in) :: doSearch ! intent(in): flag to do the line search real(rkind) ,intent(in) :: fOld ! intent(in): old function value - in_lineSearchRefinement % doLineSearch = doLineSearch ! intent(in): flag to do the line search + in_lineSearchRefinement % doSearch = doSearch ! intent(in): flag to do the line search in_lineSearchRefinement % fOld = fOld ! intent(in): old function value end subroutine initialize_in_lineSearchRefinement diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 78502c18b..42f4d3b2e 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -230,6 +230,8 @@ subroutine summaSolve4numrec(& type(out_type_computJacob) :: out_computJacob type(in_type_lineSearchRefinement) :: in_LSR type(out_type_lineSearchRefinement) :: out_LSR + type(in_type_lineSearchRefinement) :: in_TRR + type(out_type_lineSearchRefinement) :: out_TRR ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -321,8 +323,11 @@ subroutine summaSolve4numrec(& call in_LSR % initialize(doRefine,fOld) call lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_LSR) call out_LSR % finalize(fNew,converged,err,cmessage) - case(ixTrustRegion) - call trustRegionRefinement(doRefine,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,cmessage) + case(ixTrustRegion) + call in_TRR % initialize(doRefine,fOld) + call trustRegionRefinement(in_TRR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_TRR) + call out_TRR % finalize(fNew,converged,err,cmessage) + !call trustRegionRefinement(doRefine,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,cmessage) case default; err=20; message=trim(message)//'unable to identify numerical solution'; return end select @@ -385,7 +390,7 @@ subroutine lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,r ! -------------------------------------------------------------------------------------------------------- associate(& ! intent(in) variables - doLineSearch => in_LSR % doLineSearch ,& ! flag to do the line search + doLineSearch => in_LSR % doSearch ,& ! flag to do the line search fOld => in_LSR % fOld ,& ! old function value ! intent(out) variables fNew => out_LSR % fNew ,& ! new function evaluation @@ -524,65 +529,74 @@ end subroutine lineSearchRefinement ! ********************************************************************************************************* ! * internal subroutine trustRegionRefinement: refine the iteration increment using trust regions ! ********************************************************************************************************* - subroutine trustRegionRefinement(doTrustRefinement,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,message) + subroutine trustRegionRefinement(in_TRR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_TRR) ! provide access to the matrix routines USE matrixOper_module, only: lapackSolv USE matrixOper_module, only: computeGradient implicit none ! input - logical(lgt),intent(in) :: doTrustRefinement ! flag to refine using trust regions - real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector - real(rkind),intent(in) :: newtStepScaled(:) ! scaled newton step - real(rkind),intent(in) :: aJacScaled(:,:) ! scaled jacobian matrix - real(rkind),intent(in) :: rVecScaled(:) ! scaled residual vector - real(rkind),intent(in) :: fOld ! old function value + type(in_type_lineSearchRefinement),intent(in) :: in_TRR ! object for scalar intent(in) arguments -- reusing line search class + real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector + real(rkind),intent(in) :: newtStepScaled(:) ! scaled newton step + real(rkind),intent(in) :: aJacScaled(:,:) ! scaled jacobian matrix + real(rkind),intent(in) :: rVecScaled(:) ! scaled residual vector ! output - real(rkind),intent(out) :: stateVecNew(:) ! new state vector - real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector - real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector - real(rkind),intent(out) :: fNew ! new function evaluation - logical(lgt),intent(out) :: converged ! convergence flag - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + real(rkind),intent(out) :: stateVecNew(:) ! new state vector + real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector + real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector + type(out_type_lineSearchRefinement) :: out_TRR ! object for scalar intent(in) arguments -- reusing line search class ! -------------------------------------------------------------------------------------------------------- ! local variables ! .. needed .. ! -------------------------------------------------------------------------------------------------------- - err=0; message='trustRegionRefinement/' - converged =.false. + associate(& + ! input + doTrustRefinement => in_TRR % doSearch ,& ! flag to refine using trust regions + fOld => in_TRR % fOld ,& ! old function value + ! output + fNew => out_TRR % fNew ,& ! new function evaluation + converged => out_TRR % converged ,& ! convergence flag + err => out_TRR % err ,& ! error code + message => out_TRR % message & ! error message + &) - ! check the need to refine the step - if(doTrustRefinement)then + err=0; message='trustRegionRefinement/' + converged =.false. - ! (check vectors) - if(size(stateVecTrial)/=nState .or. size(newtStepScaled)/=nState .or. size(rVecScaled)/=nState)then - message=trim(message)//'unexpected size of input vectors' - err=20; return - endif + ! check the need to refine the step + if (doTrustRefinement) then - ! (check matrix) - if(size(aJacScaled,1)/=nState .or. size(aJacScaled,2)/=nState)then - message=trim(message)//'unexpected size of Jacobian matrix' - err=20; return - endif + ! (check vectors) + if (size(stateVecTrial)/=nState .or. size(newtStepScaled)/=nState .or. size(rVecScaled)/=nState)then + message=trim(message)//'unexpected size of input vectors' + err=20; return + end if + + ! (check matrix) + if (size(aJacScaled,1)/=nState .or. size(aJacScaled,2)/=nState) then + message=trim(message)//'unexpected size of Jacobian matrix' + err=20; return + end if - ! dummy check for the function - if(fold==realMissing) print*, 'missing' + ! dummy check for the function + if (fold==realMissing) print*, 'missing' - ! dummy - stateVecNew = realMissing - fluxVecNew = realMissing - resVecNew = quadMissing - fNew = realMissing - converged = .true. + ! dummy + stateVecNew = realMissing + fluxVecNew = realMissing + resVecNew = quadMissing + fNew = realMissing + converged = .true. - endif ! if doing the trust region refinement + end if ! if doing the trust region refinement - message=trim(message)//'routine not implemented yet' - err=20; return + message=trim(message)//'routine not implemented yet' + err=20; return + + end associate end subroutine trustRegionRefinement From 7f2e80cfa1137a0184670e019f2f5f12a080a8d3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 21 Feb 2024 16:14:44 +0900 Subject: [PATCH 1157/1472] update cmake files --- build/cmake/build.mac.bash | 2 +- build/cmake/build_actors.mac.bash | 2 +- build/cmake/build_ngen.mac.bash | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/cmake/build.mac.bash b/build/cmake/build.mac.bash index e2a9014f9..4a62f82c1 100755 --- a/build/cmake/build.mac.bash +++ b/build/cmake/build.mac.bash @@ -4,7 +4,7 @@ # Environment variables that must be set for cmake: FC, LINK_DIRS, INCLUDES_DIRS, and LIBRARY_LINKS # Mac Example using MacPorts: -export FC=gfortran # Fortran compiler family +export FC=/opt/local/bin/gfortran # Fortran compiler family export LINK_DIRS=/opt/local/lib # Link directories for cmake export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) diff --git a/build/cmake/build_actors.mac.bash b/build/cmake/build_actors.mac.bash index fd2624493..2a1e372ef 100755 --- a/build/cmake/build_actors.mac.bash +++ b/build/cmake/build_actors.mac.bash @@ -3,7 +3,7 @@ # build on Mac, from cmake directory run this as ./build_actors.mac.bash # Mac Example using MacPorts: -export FC=gfortran # Fortran compiler family +export FC=/opt/local/bin/gfortran # Fortran compiler family export LINK_DIRS=/opt/local/lib # Link directories for cmake export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) diff --git a/build/cmake/build_ngen.mac.bash b/build/cmake/build_ngen.mac.bash index 5a8b55022..7d52d2535 100755 --- a/build/cmake/build_ngen.mac.bash +++ b/build/cmake/build_ngen.mac.bash @@ -2,7 +2,7 @@ # build nextgen on Mac, from ngen directory put this one directory up and run this as ../build_ngen.mac.bash # Mac Example using MacPorts: -export FC=gfortran # Fortran compiler family +export FC=/opt/local/bin/gfortran # Fortran compiler family export LINK_DIRS=/opt/local/lib # Link directories for cmake export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) @@ -14,7 +14,7 @@ cmake --build extern/iso_c_fortran_bmi/cmake_build --target all cmake -B extern/summa/cmake_build -S extern/summa -DCMAKE_BUILD_TYPE=BE_NexGen cmake --build extern/summa/cmake_build --target all -cmake -B cmake_build -S . -DNGEN_WITH_MPI:BOOL=OFF -DNGEN_WITH_PYTHON:BOOL=OFF -DNGEN_WITH_BMI_C:BOOL=ON -DNGEN_WITH_BMI_FORTRAN:BOOL=ON -DNGEN_WITH_NETCDF:BOOL=OFF +cmake -B cmake_build -S . -DBoost_INCLUDE_DIR=/opt/local/libexec/boost/1.81/include -DPython_NumPy_INCLUDE_DIR=/opt/local/bin/python -DNGEN_WITH_MPI:BOOL=OFF -DNGEN_WITH_PYTHON:BOOL=OFF -DNGEN_WITH_BMI_C:BOOL=ON -DNGEN_WITH_BMI_FORTRAN:BOOL=ON -DNGEN_WITH_NETCDF:BOOL=OFF # can also add -DCMAKE_BUILD_TYPE=Debug to be able to run in gdb # make -j 8 -C cmake_build # build w/ 8 parallel jobs, also turn MPI on make -C cmake_build From 59c35a7bd168802e83c3ffcf47f0c018b6a65bcf Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 21 Feb 2024 16:25:58 +0900 Subject: [PATCH 1158/1472] deleted commented lines in CMakeLists.txt --- build/cmake/CMakeLists.txt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index ea62cf1a0..6afa704eb 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -165,13 +165,6 @@ if(CMAKE_BUILD_TYPE MATCHES Cluster) else() message("\nBuilding for personal computer") - #set(SDKROOT "$(xcrun --show-sdk-path)") # appears to be unused and specifically for MacOS so commented out - - ## Original Block -- possibly for MacOS -- attempted to generalize things for additional platforms in the next block - #link_directories(/opt/local/lib) - #set(INCLUDES /opt/local/include /opt/local/lib) - #set(LIBRARIES SUMMA_NOAHMP -llapack -lgfortran -lnetcdff -lnetcdf) - # Set Links and Libraries using Environment Variables set(LINK_DIRS $ENV{LINK_DIRS}) # set link directories from environment variable set(INCLUDES_DIRS $ENV{INCLUDES_DIRS}) # set directories for INCLUDES from environment variable From 40b9e12beb068163ab3c03a9c948fc3f634427a5 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 21 Feb 2024 18:02:53 -0600 Subject: [PATCH 1159/1472] Applied object-oriented methods to intent(out) arguments of the safeRootFinder routine in summaSolve4numrec.f90. --- build/source/engine/summaSolve4numrec.f90 | 150 ++++++++++++---------- 1 file changed, 79 insertions(+), 71 deletions(-) diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 42f4d3b2e..096d0ac27 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -226,12 +226,13 @@ subroutine summaSolve4numrec(& logical(lgt) :: globalPrintFlagInit ! initial global print flag character(LEN=256) :: cmessage ! error message of downwind routine ! class objects for subroutine arguments - type(in_type_computJacob) :: in_computJacob - type(out_type_computJacob) :: out_computJacob - type(in_type_lineSearchRefinement) :: in_LSR - type(out_type_lineSearchRefinement) :: out_LSR - type(in_type_lineSearchRefinement) :: in_TRR - type(out_type_lineSearchRefinement) :: out_TRR + type(in_type_computJacob) :: in_computJacob ! computJacob + type(out_type_computJacob) :: out_computJacob ! computJacob + type(in_type_lineSearchRefinement) :: in_LSR ! lineSearchRefinement + type(out_type_lineSearchRefinement) :: out_LSR ! lineSearchRefinement + type(in_type_lineSearchRefinement) :: in_TRR ! trustRegionRefinement + type(out_type_lineSearchRefinement) :: out_TRR ! trustRegionRefinement + type(out_type_lineSearchRefinement) :: out_SRF ! safeRootFinder ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -342,12 +343,13 @@ subroutine summaSolve4numrec(& ! * case 2: scalar else - call safeRootfinder(stateVecTrial,rVecScaled,newtStepScaled,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + call safeRootfinder(stateVecTrial,rVecScaled,newtStepScaled,stateVecNew,fluxVecNew,resVecNew,out_SRF) + call out_SRF % finalize(fNew,converged,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors endif ! check errors - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors contains @@ -604,7 +606,7 @@ end subroutine trustRegionRefinement ! ********************************************************************************************************* ! * internal subroutine safeRootfinder: refine the 1-d iteration increment using brackets ! ********************************************************************************************************* - subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,message) + subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fluxVecNew,resVecNew,out_SRF) USE,intrinsic :: ieee_arithmetic,only:ieee_is_nan ! IEEE arithmetic (check NaN) USE globalData,only:dNaN ! double precision NaN implicit none @@ -616,10 +618,7 @@ subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fl real(rkind),intent(out) :: stateVecNew(:) ! new state vector real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector - real(rkind),intent(out) :: fNew ! new function evaluation - logical(lgt),intent(out) :: converged ! convergence flag - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + type(out_type_lineSearchRefinement),intent(out) :: out_SRF ! object for scalar intent(out) arguments (reusing lineSearchRefinement class) ! -------------------------------------------------------------------------------------------------------- ! local variables character(len=256) :: cmessage ! error message of downwind routine @@ -633,78 +632,87 @@ subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fl integer(i4b),parameter :: nCheck=100 ! number of times to check the model state variables real(rkind),parameter :: delX=1._rkind ! trial increment ! -------------------------------------------------------------------------------------------------------- - err=0; message='safeRootfinder/' - converged = .false. + associate(& + fNew => out_SRF % fNew ,& ! new function evaluation + converged => out_SRF % converged ,& ! convergence flag + err => out_SRF % err ,& ! error code + message => out_SRF % message & ! error message + &) - ! check scalar - if(size(stateVecTrial)/=1 .or. size(rVecScaled)/=1 .or. size(newtStepScaled)/=1)then - message=trim(message)//'unexpected size of input vectors' - err=20; return - endif + err=0; message='safeRootfinder/' + converged = .false. - ! initialize brackets to rkind precision NaN - if(iter==1)then - xMax = dNaN - xMin = dNaN - endif + ! check scalar + if (size(stateVecTrial)/=1 .or. size(rVecScaled)/=1 .or. size(newtStepScaled)/=1) then + message=trim(message)//'unexpected size of input vectors' + err=20; return + end if - ! get the residual vector - rVec = real(rVecScaled, rkind)*real(fScale, rkind) + ! initialize brackets to rkind precision NaN + if (iter==1) then + xMax = dNaN + xMin = dNaN + end if - ! update brackets - if(rVec(1)<0._rkind)then - xMin = stateVecTrial(1) - else - xMax = stateVecTrial(1) - endif + ! get the residual vector + rVec = real(rVecScaled, rkind)*real(fScale, rkind) - ! get the iteration increment - xInc = newtStepScaled*xScale + ! update brackets + if (rVec(1)<0._rkind) then + xMin = stateVecTrial(1) + else + xMax = stateVecTrial(1) + end if - ! ***** - ! * case 1: the iteration increment is the same sign as the residual vector - if(xInc(1)*rVec(1) > 0._rkind)then + ! get the iteration increment + xInc = newtStepScaled*xScale - ! get brackets if they do not exist - if( ieee_is_nan(xMin) .or. ieee_is_nan(xMax) )then - call getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - endif + ! ***** + ! * case 1: the iteration increment is the same sign as the residual vector + if (xInc(1)*rVec(1) > 0._rkind) then - ! use bi-section - stateVecNew(1) = 0.5_rkind*(xMin + xMax) + ! get brackets if they do not exist + if ( ieee_is_nan(xMin) .or. ieee_is_nan(xMax) ) then + call getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors + end if - ! ***** - ! * case 2: the iteration increment is the correct sign - else + ! use bi-section + stateVecNew(1) = 0.5_rkind*(xMin + xMax) - ! state vector with proposed iteration increment - stateVecNew = stateVecTrial + xInc - - ! impose solution constraints adjusting state vector and iteration increment - call imposeConstraints(model_decisions,indx_data,prog_data,mpar_data,stateVecNew,stateVecTrial,nState,nSoil,nSnow,cmessage,err) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - xInc = stateVecNew - stateVecTrial + ! ***** + ! * case 2: the iteration increment is the correct sign + else - endif ! if the iteration increment is the same sign as the residual vector + ! state vector with proposed iteration increment + stateVecNew = stateVecTrial + xInc + + ! impose solution constraints adjusting state vector and iteration increment + call imposeConstraints(model_decisions,indx_data,prog_data,mpar_data,stateVecNew,stateVecTrial,nState,nSoil,nSnow,cmessage,err) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors + xInc = stateVecNew - stateVecTrial - ! bi-section - bracketsDefined = ( .not.ieee_is_nan(xMin) .and. .not.ieee_is_nan(xMax) ) ! check that the brackets are defined - if(bracketsDefined)then - xTolerance = relTolerance*(xMax-xMin) - doBisection = (stateVecNew(1)xMax-xTolerance) - if(doBisection) stateVecNew(1) = 0.5_rkind*(xMin+xMax) - endif + end if ! if the iteration increment is the same sign as the residual vector - ! evaluate summa - call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + ! bi-section + bracketsDefined = ( .not.ieee_is_nan(xMin) .and. .not.ieee_is_nan(xMax) ) ! check that the brackets are defined + if (bracketsDefined) then + xTolerance = relTolerance*(xMax-xMin) + doBisection = (stateVecNew(1)xMax-xTolerance) + if (doBisection) stateVecNew(1) = 0.5_rkind*(xMin+xMax) + end if + + ! evaluate summa + call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors + + ! check feasibility (should be feasible because of the call to imposeConstraints, except if canopyTemp>canopyTempMax (500._rkind)) + if (.not.feasible) then; err=20; message=trim(message)//'state vector not feasible'; return; end if - ! check feasibility (should be feasible because of the call to imposeConstraints, except if canopyTemp>canopyTempMax (500._rkind)) - if(.not.feasible)then; err=20; message=trim(message)//'state vector not feasible'; return; endif + ! check convergence + converged = checkConv(resVecNew,xInc,stateVecNew) - ! check convergence - converged = checkConv(resVecNew,xInc,stateVecNew) + end associate end subroutine safeRootfinder From 3b5153faf58264eb3f02884bcdba4a7f7f08b007 Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 23 Feb 2024 04:40:42 -0600 Subject: [PATCH 1160/1472] Created new subroutines to modularize linear system and Newton step refinement operations in summaSolve4numrec. --- build/source/engine/summaSolve4numrec.f90 | 144 +++++++++++----------- 1 file changed, 75 insertions(+), 69 deletions(-) diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 096d0ac27..205a34727 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -224,6 +224,7 @@ subroutine summaSolve4numrec(& integer(i4b) :: iLayer ! row index integer(i4b) :: jLayer ! column index logical(lgt) :: globalPrintFlagInit ! initial global print flag + logical(lgt) :: return_flag ! flag that controls execution of return statements character(LEN=256) :: cmessage ! error message of downwind routine ! class objects for subroutine arguments type(in_type_computJacob) :: in_computJacob ! computJacob @@ -237,6 +238,7 @@ subroutine summaSolve4numrec(& ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='summaSolve4numrec/' + return_flag=.false. ! initialize return flag ! choose Jacobian type select case(model_decisions(iLookDECISIONS%fDerivMeth)%iDecision) @@ -278,82 +280,86 @@ subroutine summaSolve4numrec(& if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) end if - ! ----- - ! * solve linear system... - ! ------------------------ - - ! scale the residual vector - rVecScaled(1:nState) = fScale(:)*real(rVec(:), rkind) ! NOTE: residual vector is in quadruple precision - - ! scale matrices - call scaleMatrices(ixMatrix,nState,aJac,fScale,xScale,aJacScaled,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + call solve_linear_system; if (return_flag) return ! solve the linear system for the Newton step - if (globalPrintFlag .and. ixMatrix==ixBandMatrix) then - print*, '** SCALED banded analytical Jacobian:' - write(*,'(a4,1x,100(i17,1x))') 'xCol', (iLayer, iLayer=iJac1,iJac2) - do iLayer=kl+1,nBands - write(*,'(i4,1x,100(e17.10,1x))') iLayer, (aJacScaled(iLayer,jLayer),jLayer=min(iJac1,nState),min(iJac2,nState)) - end do - end if - - ! copy the scaled matrix, since it is decomposed in lapackSolv - aJacScaledTemp = aJacScaled - - ! compute the newton step: use the lapack routines to solve the linear system A.X=B - call lapackSolv(ixMatrix,nState,aJacScaledTemp,-rVecScaled,newtStepScaled,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - if (globalPrintFlag) write(*,'(a,1x,10(e17.10,1x))') 'newtStepScaled = ', newtStepScaled(min(iJac1,nState):min(iJac2,nState)) - - ! ----- - ! * update, evaluate, and refine the state vector... - ! -------------------------------------------------- - - ! initialize the flag for step refinement - doRefine=.true. - - ! * case 1: state vector - ! compute the flux vector and the residual, and (if necessary) refine the iteration increment - ! NOTE: in 99.9% of cases newtStep will be used (no refinement) - if (size(stateVecTrial)>1) then - - ! try to backtrack - select case(ixStepRefinement) - case(ixLineSearch) - call in_LSR % initialize(doRefine,fOld) - call lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_LSR) - call out_LSR % finalize(fNew,converged,err,cmessage) - case(ixTrustRegion) - call in_TRR % initialize(doRefine,fOld) - call trustRegionRefinement(in_TRR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_TRR) - call out_TRR % finalize(fNew,converged,err,cmessage) - !call trustRegionRefinement(doRefine,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,cmessage) - case default; err=20; message=trim(message)//'unable to identify numerical solution'; return - end select - - ! check warnings: negative error code = warning; in this case back-tracked to the original value - ! NOTE: Accept the full newton step if back-tracked to the original value - if (err<0) then - doRefine=.false.; - call in_LSR % initialize(doRefine,fOld) - call lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_LSR) - call out_LSR % finalize(fNew,converged,err,cmessage) - end if - - ! * case 2: scalar - else - call safeRootfinder(stateVecTrial,rVecScaled,newtStepScaled,stateVecNew,fluxVecNew,resVecNew,out_SRF) - call out_SRF % finalize(fNew,converged,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors - endif + call refine_Newton_step; if (return_flag) return ! if needed, refine Newton step -- return if error ! check errors if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors - contains + subroutine solve_linear_system + ! *** Solve the linear system for the Newton step using LAPACK routines *** + + ! scale the residual vector + rVecScaled(1:nState) = fScale(:)*real(rVec(:), rkind) ! NOTE: residual vector is in quadruple precision + + ! scale matrices + call scaleMatrices(ixMatrix,nState,aJac,fScale,xScale,aJacScaled,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors + + ! debug statement for scaled Jacobian + if (globalPrintFlag .and. ixMatrix==ixBandMatrix) then + print*, '** SCALED banded analytical Jacobian:' + write(*,'(a4,1x,100(i17,1x))') 'xCol', (iLayer, iLayer=iJac1,iJac2) + do iLayer=kl+1,nBands + write(*,'(i4,1x,100(e17.10,1x))') iLayer, (aJacScaled(iLayer,jLayer),jLayer=min(iJac1,nState),min(iJac2,nState)) + end do + end if + + ! copy the scaled matrix, since it is decomposed in lapackSolv + aJacScaledTemp = aJacScaled + + ! compute the newton step: use the lapack routines to solve the linear system A.X=B + call lapackSolv(ixMatrix,nState,aJacScaledTemp,-rVecScaled,newtStepScaled,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors + + ! debug statement for Newton step + if (globalPrintFlag) write(*,'(a,1x,10(e17.10,1x))') 'newtStepScaled = ', newtStepScaled(min(iJac1,nState):min(iJac2,nState)) + end subroutine solve_linear_system + + subroutine refine_Newton_step + ! *** Refine the Newton step if necessary *** + + ! initialize the flag for step refinement + doRefine=.true. + + ! * case 1: state vector + ! compute the flux vector and the residual, and (if necessary) refine the iteration increment + ! NOTE: in 99.9% of cases newtStep will be used (no refinement) + if (size(stateVecTrial)>1) then + + ! try to backtrack + select case(ixStepRefinement) + case(ixLineSearch) + call in_LSR % initialize(doRefine,fOld) + call lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_LSR) + call out_LSR % finalize(fNew,converged,err,cmessage) + case(ixTrustRegion) + call in_TRR % initialize(doRefine,fOld) + call trustRegionRefinement(in_TRR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_TRR) + call out_TRR % finalize(fNew,converged,err,cmessage) + case default; err=20; message=trim(message)//'unable to identify numerical solution'; return_flag=.true.; return + end select + + ! check warnings: negative error code = warning; in this case back-tracked to the original value + ! NOTE: Accept the full newton step if back-tracked to the original value + if (err<0) then + doRefine=.false.; + call in_LSR % initialize(doRefine,fOld) + call lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_LSR) + call out_LSR % finalize(fNew,converged,err,cmessage) + end if + + ! * case 2: scalar + else + call safeRootfinder(stateVecTrial,rVecScaled,newtStepScaled,stateVecNew,fluxVecNew,resVecNew,out_SRF) + call out_SRF % finalize(fNew,converged,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors + end if + end subroutine refine_Newton_step + ! ********************************************************************************************************* ! * internal subroutine lineSearchRefinement: refine the iteration increment using line searches ! ********************************************************************************************************* From 8fdae6df86e79bd639bc5cebdee002528bfc3f70 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 23 Feb 2024 23:58:45 +0900 Subject: [PATCH 1161/1472] error failures in ida needs to be at 50 --- build/source/engine/summaSolve4ida.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 7f826dafe..5ca842400 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -674,7 +674,7 @@ subroutine setSolverParams(dt_cur,ida_mem,retval) integer,parameter :: max_order = 5 ! maximum BDF order, default and max = 5 real(qp),parameter :: coef_nonlin = 0.33 ! coefficient in the nonlinear convergence test, default = 0.33 integer(c_long),parameter :: max_step = 999999 ! maximum number of steps, default = 500 - integer,parameter :: fail_iter = 10 ! maximum number of error test and convergence test failures, default 10 + integer,parameter :: fail_iter = 50 ! maximum number of error test and convergence test failures, default 10 real(qp) :: h_max ! maximum stepsize, default = infinity real(qp),parameter :: h_init = 0 ! initial stepsize From 8b6a9e4e6a648fb0746fb9bdf873a7ee4f0446c5 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 24 Feb 2024 07:28:54 -0600 Subject: [PATCH 1162/1472] Modularized initialize and Jacobian related operations in the summaSolve4numrec subroutine. --- build/source/engine/summaSolve4numrec.f90 | 112 ++++++++++++---------- 1 file changed, 63 insertions(+), 49 deletions(-) diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 205a34727..7cb20f784 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -236,62 +236,76 @@ subroutine summaSolve4numrec(& type(out_type_lineSearchRefinement) :: out_SRF ! safeRootFinder ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='summaSolve4numrec/' - return_flag=.false. ! initialize return flag - -! choose Jacobian type - select case(model_decisions(iLookDECISIONS%fDerivMeth)%iDecision) - case(numerical); err=20; message=trim(message)//'numerical derivatives are not implemented for BE numerical Recipes solver'; return - case(analytical); ! this is fine - case default; err=20; message=trim(message)//'expect choice numericl or analytic to calculate derivatives for Jacobian'; return - end select - - ! get the number of soil layers in the solution vector - mSoil = size(indx_data%var(iLookINDEX%ixMatOnly)%dat) - - ! initialize the global print flag - globalPrintFlagInit=globalPrintFlag - - ! ----- - ! * compute the Jacobian matrix... - ! -------------------------------- - - ! compute the analytical Jacobian matrix - ! NOTE: The derivatives were computed in the previous call to computFlux - ! This occurred either at the call to eval8summa at the start of systemSolv - ! or in the call to eval8summa in the previous iteration (within lineSearchRefinement or trustRegionRefinement) - call initialize_computJacob_summaSolve4numrec - call computJacob(in_computJacob,indx_data,prog_data,diag_data,deriv_data,dBaseflow_dMatric,dMat,aJac,out_computJacob) - call finalize_computJacob_summaSolve4numrec - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! compute the numerical Jacobian matrix - if (doNumJacobian) then - globalPrintFlag=.false. - call numJacobian(stateVecTrial,dMat,nJac,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - globalPrintFlag=globalPrintFlagInit - end if - - ! test the band diagonal matrix - if (testBandDiagonal) then - call testBandMat(check=.true.,err=err,message=cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - end if - - call solve_linear_system; if (return_flag) return ! solve the linear system for the Newton step - - call refine_Newton_step; if (return_flag) return ! if needed, refine Newton step -- return if error - - ! check errors + + ! ***** Compute the Newton Step ***** + + call initialize_summaSolve4numrec; if (return_flag) return ! initial setup -- return if error + + call update_Jacobian; if (return_flag) return ! compute Jacobian for Newton step -- return if error + + call solve_linear_system; if (return_flag) return ! solve the linear system for the Newton step -- return if error + + call refine_Newton_step; if (return_flag) return ! refine Newton step if needed -- return if error + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors contains + subroutine initialize_summaSolve4numrec + ! *** Initial steps for the summaSolve4numrec algorithm (computing the Newton step) *** + + ! initialize error control + err=0; message='summaSolve4numrec/' + return_flag=.false. ! initialize return flag + + ! choose Jacobian type + select case(model_decisions(iLookDECISIONS%fDerivMeth)%iDecision) + case(numerical) + err=20; message=trim(message)//'numerical derivatives are not implemented for BE numerical Recipes solver'; + return_flag=.true.; return + case(analytical); ! this is fine + case default + err=20; message=trim(message)//'expect choice numericl or analytic to calculate derivatives for Jacobian'; + return_flag=.true.; return + end select + + ! get the number of soil layers in the solution vector + mSoil = size(indx_data%var(iLookINDEX%ixMatOnly)%dat) + + ! initialize the global print flag + globalPrintFlagInit=globalPrintFlag + end subroutine initialize_summaSolve4numrec + + subroutine update_Jacobian + ! *** Update Jacobian used for Newton step *** + + ! compute the analytical Jacobian matrix + ! NOTE: The derivatives were computed in the previous call to computFlux + ! This occurred either at the call to eval8summa at the start of systemSolv + ! or in the call to eval8summa in the previous iteration (within lineSearchRefinement or trustRegionRefinement) + call initialize_computJacob_summaSolve4numrec + call computJacob(in_computJacob,indx_data,prog_data,diag_data,deriv_data,dBaseflow_dMatric,dMat,aJac,out_computJacob) + call finalize_computJacob_summaSolve4numrec + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! (check for errors) + + ! compute the numerical Jacobian matrix + if (doNumJacobian) then + globalPrintFlag=.false. + call numJacobian(stateVecTrial,dMat,nJac,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! (check for errors) + globalPrintFlag=globalPrintFlagInit + end if + end subroutine update_Jacobian + subroutine solve_linear_system ! *** Solve the linear system for the Newton step using LAPACK routines *** + ! test the band diagonal matrix + if (testBandDiagonal) then + call testBandMat(check=.true.,err=err,message=cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! (check for errors) + end if + ! scale the residual vector rVecScaled(1:nState) = fScale(:)*real(rVec(:), rkind) ! NOTE: residual vector is in quadruple precision From 97bd2e5501cc764291d9731ef1cf63d8a2c4fd14 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 27 Feb 2024 13:06:18 +0900 Subject: [PATCH 1163/1472] adding some output variables for scaled balance errors --- build/source/dshare/get_ixname.f90 | 42 +- build/source/dshare/popMetadat.f90 | 41 +- build/source/dshare/var_lookup.f90 | 44 +- build/source/engine/computEnthalpy.f90 | 153 ------ build/source/engine/computFlux.f90 | 4 +- build/source/engine/computResid.f90 | 22 +- build/source/engine/coupled_em.f90 | 167 +++++-- build/source/engine/enthalpyTemp.f90 | 97 ++-- build/source/engine/eval8summa.f90 | 34 +- build/source/engine/layerMerge.f90 | 12 +- build/source/engine/systemSolv.f90 | 20 +- build/source/engine/t2enthalpy.f90 | 631 ------------------------- build/source/engine/varSubstep.f90 | 52 +- 13 files changed, 322 insertions(+), 997 deletions(-) delete mode 100644 build/source/engine/computEnthalpy.f90 delete mode 100644 build/source/engine/t2enthalpy.f90 diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 7e79faa80..03c72412f 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -508,9 +508,13 @@ function get_ixdiag(varName) case('mLayerThermalC' ); get_ixdiag = iLookDIAG%mLayerThermalC ! thermal conductivity at the mid-point of each layer (W m-1 K-1) case('iLayerThermalC' ); get_ixdiag = iLookDIAG%iLayerThermalC ! thermal conductivity at the interface of each layer (W m-1 K-1) ! enthalpy - case('scalarCanairEnthalpy' ); get_ixdiag = iLookDIAG%scalarCanairEnthalpy ! temperature component of enthalpy of the canopy air space (J m-3) - case('scalarCanopyEnthalpy' ); get_ixdiag = iLookDIAG%scalarCanopyEnthalpy ! temperature component of enthalpy of the vegetation canopy (J m-3) - case('mLayerEnthalpy' ); get_ixdiag = iLookDIAG%mLayerEnthalpy ! temperature component of enthalpy of the snow+soil layers (J m-3) + case('scalarCanairEnthalpy' ); get_ixdiag = iLookDIAG%scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) + case('scalarCanopyEnthTemp' ); get_ixdiag = iLookDIAG%scalarCanopyEnthTemp ! temperature component of enthalpy of the vegetation canopy (J m-3) + case('scalarCanopyEnthalpy' ); get_ixdiag = iLookDIAG%scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) + case('mLayerEnthTemp' ); get_ixdiag = iLookDIAG%mLayerEnthTemp ! temperature component of enthalpy of the snow+soil layers (J m-3) + case('mLayerEnthalpy' ); get_ixdiag = iLookDIAG%mLayerEnthalpy ! enthalpy of the snow+soil layers (J m-3) + case('scalarTotalSoilEnthalpy' ); get_ixdiag = iLookDIAG%scalarTotalSoilEnthalpy ! total enthalpy of the soil column (J m-3) + case('scalarTotalSnowEnthalpy' ); get_ixdiag = iLookDIAG%scalarTotalSnowEnthalpy ! total enthalpy of the snow column (J m-3) ! forcing case('scalarVPair' ); get_ixdiag = iLookDIAG%scalarVPair ! vapor pressure of the air above the vegetation canopy (Pa) case('scalarVP_CanopyAir' ); get_ixdiag = iLookDIAG%scalarVP_CanopyAir ! vapor pressure of the canopy air space (Pa) @@ -573,8 +577,6 @@ function get_ixdiag(varName) case('scalarSoilCompress' ); get_ixdiag = iLookDIAG%scalarSoilCompress ! change in total soil storage due to compression of the soil matrix (kg m-2 s-1) case('mLayerMatricHeadLiq' ); get_ixdiag = iLookDIAG%mLayerMatricHeadLiq ! matric potential of liquid water (m) ! mass balance check - case('scalarSoilWatBalError' ); get_ixdiag = iLookDIAG%scalarSoilWatBalError ! error in the total soil water balance (kg m-2) - case('scalarAquiferBalError' ); get_ixdiag = iLookDIAG%scalarAquiferBalError ! error in the aquifer water balance (kg m-2) case('scalarTotalSoilLiq' ); get_ixdiag = iLookDIAG%scalarTotalSoilLiq ! total mass of liquid water in the soil (kg m-2) case('scalarTotalSoilIce' ); get_ixdiag = iLookDIAG%scalarTotalSoilIce ! total mass of ice in the soil (kg m-2) case('scalarTotalSoilWat' ); get_ixdiag = iLookDIAG%scalarTotalSoilWat ! total mass of water in the soil (kg m-2) @@ -587,16 +589,26 @@ function get_ixdiag(varName) case('wallClockTime' ); get_ixdiag = iLookDIAG%wallClockTime ! wall clock time (s) case('meanStepSize' ); get_ixdiag = iLookDIAG%meanStepSize ! mean time step size (s) over data window ! balances - case('balanceCasNrg' ); get_ixdiag = iLookDIAG%balanceCasNrg ! balance of energy in the canopy air space - case('balanceVegNrg' ); get_ixdiag = iLookDIAG%balanceVegNrg ! balance of energy in the vegetation - case('balanceLayerNrg' ); get_ixdiag = iLookDIAG%balanceLayerNrg ! balance of energy in each snow+soil layer - case('balanceSnowNrg' ); get_ixdiag = iLookDIAG%balanceSnowNrg ! balance of energy in the snow - case('balanceSoilNrg' ); get_ixdiag = iLookDIAG%balanceSoilNrg ! balance of energy in the soil - case('balanceVegMass' ); get_ixdiag = iLookDIAG%balanceVegMass ! balance of water in the vegetation - case('balanceLayerMass' ); get_ixdiag = iLookDIAG%balanceLayerMass ! balance of water in each snow+soil layer - case('balanceSnowMass' ); get_ixdiag = iLookDIAG%balanceSnowMass ! balance of water in the snow - case('balanceSoilMass' ); get_ixdiag = iLookDIAG%balanceSoilMass ! balance of water in the soil - case('balanceAqMass' ); get_ixdiag = iLookDIAG%balanceAqMass ! balance of water in the aquifer + case('balanceCasNrg' ); get_ixdiag = iLookDIAG%balanceCasNrg ! balance of energy in the canopy air space (W m-3) + case('balanceVegNrg' ); get_ixdiag = iLookDIAG%balanceVegNrg ! balance of energy in the vegetation canopy (W m-3) + case('balanceLayerNrg' ); get_ixdiag = iLookDIAG%balanceLayerNrg ! balance of energy in each snow+soil layer (W m-3) + case('balanceSnowNrg' ); get_ixdiag = iLookDIAG%balanceSnowNrg ! balance of energy in the snow (W m-3) + case('balanceSoilNrg' ); get_ixdiag = iLookDIAG%balanceSoilNrg ! balance of energy in the soil (W m-3) + case('balanceVegMass' ); get_ixdiag = iLookDIAG%balanceVegMass ! balance of water in the vegetation canopy (kg m-2 s-1) + case('balanceLayerMass' ); get_ixdiag = iLookDIAG%balanceLayerMass ! balance of water in each snow+soil layer (kg m-2 s-1) + case('balanceSnowMass' ); get_ixdiag = iLookDIAG%balanceSnowMass ! balance of water in the snow (kg m-2 s-1) + case('balanceSoilMass' ); get_ixdiag = iLookDIAG%balanceSoilMass ! balance of water in the soil (kg m-2 s-1) + case('balanceAqMass' ); get_ixdiag = iLookDIAG%balanceAqMass ! balance of water in the aquifer (kg m-2 s-1) + ! scaled balances + case('scaledBalanceCasNrg' ); get_ixdiag = iLookDIAG%scaledBalanceCasNrg ! scaled balance of energy in the canopy air space (s-1) + case('scaledBalanceVegNrg' ); get_ixdiag = iLookDIAG%scaledBalanceVegNrg ! scaled balance of energy in the vegetation canopy (s-1) + case('scaledBalanceSnowNrg' ); get_ixdiag = iLookDIAG%scaledBalanceSnowNrg ! scaled balance of energy in the snow (s-1) + case('scaledBalanceSoilNrg' ); get_ixdiag = iLookDIAG%scaledBalanceSoilNrg ! scaled balance of energy in the soil (s-1) + case('scaledBalanceVegMass' ); get_ixdiag = iLookDIAG%scaledBalanceVegMass ! scaled balance of water in the vegetation canopy (s-1) + case('scaledBalanceSnowMass' ); get_ixdiag = iLookDIAG%scaledBalanceSnowMass ! scaled balance of water in the snow (s-1) + case('scaledBalanceSoilMass' ); get_ixdiag = iLookDIAG%scaledBalanceSoilMass ! scaled balance of water in the soil (s-1) + case('scaledBalanceAqMass' ); get_ixdiag = iLookDIAG%scaledBalanceAqMass ! scaled balance of water in the aquifer (s-1) + ! get to here if cannot find the variable case default get_ixdiag = integerMissing diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index adc80e7c0..2291afd5a 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -363,9 +363,13 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%mLayerThermalC) = var_info('mLayerThermalC' , 'thermal conductivity at the mid-point of each layer' , 'W m-1 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%iLayerThermalC) = var_info('iLayerThermalC' , 'thermal conductivity at the interface of each layer' , 'W m-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) ! enthalpy - diag_meta(iLookDIAG%scalarCanairEnthalpy) = var_info('scalarCanairEnthalpy' , 'temperature component of enthalpy of the canopy air space' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarCanopyEnthalpy) = var_info('scalarCanopyEnthalpy' , 'temperature component of enthalpy of the vegetation canopy' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%mLayerEnthalpy) = var_info('mLayerEnthalpy' , 'temperature component of enthalpy of the snow+soil layers' , 'J m-3' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarCanairEnthalpy) = var_info('scalarCanairEnthalpy' , 'enthalpy of the canopy air space' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarCanopyEnthTemp) = var_info('scalarCanopyEnthTemp' , 'temperature component of enthalpy of the vegetation canopy' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarCanopyEnthalpy) = var_info('scalarCanopyEnthalpy' , 'enthalpy of the vegetation canopy' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%mLayerEnthTemp) = var_info('mLayerEnthTemp' , 'temperature component of enthalpy of the snow+soil layers' , 'J m-3' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%mLayerEnthalpy) = var_info('mLayerEnthalpy' , 'enthalpy of the snow+soil layers' , 'J m-3' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarTotalSoilEnthalpy) = var_info('scalarTotalSoilEnthalpy' , 'total enthalpy of the soil column' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scalarTotalSnowEnthalpy) = var_info('scalarTotalSnowEnthalpy' , 'total enthalpy of the snow column' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! forcing diag_meta(iLookDIAG%scalarVPair) = var_info('scalarVPair' , 'vapor pressure of the air above the vegetation canopy' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarVP_CanopyAir) = var_info('scalarVP_CanopyAir' , 'vapor pressure of the canopy air space' , 'Pa' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) @@ -428,8 +432,6 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%scalarSoilCompress) = var_info('scalarSoilCompress' , 'change in total soil storage due to compression of soil matrix' , 'kg m-2 s-1 ' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%mLayerMatricHeadLiq) = var_info('mLayerMatricHeadLiq' , 'matric potential of liquid water' , 'm' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) ! mass balance check - diag_meta(iLookDIAG%scalarSoilWatBalError) = var_info('scalarSoilWatBalError' , 'error in the total soil water balance' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarAquiferBalError) = var_info('scalarAquiferBalError' , 'error in the aquifer water balance' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarTotalSoilLiq) = var_info('scalarTotalSoilLiq' , 'total mass of liquid water in the soil' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarTotalSoilIce) = var_info('scalarTotalSoilIce' , 'total mass of ice in the soil' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarTotalSoilWat) = var_info('scalarTotalSoilWat' , 'total mass of water in the soil' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) @@ -442,16 +444,25 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%wallClockTime) = var_info('wallClockTime' , 'wall clock time' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%meanStepSize) = var_info('meanStepSize' , 'mean time step size over data window' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! balances - diag_meta(iLookDIAG%balanceCasNrg) = var_info('balanceCasNrg' , 'balance of energy in the canopy air space on data window' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%balanceVegNrg) = var_info('balanceVegNrg' , 'balance of energy in the vegetation on data window' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%balanceLayerNrg) = var_info('balanceLayerNrg' , 'balance of energy in each snow+soil layer on substep' , 'W m-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%balanceSnowNrg) = var_info('balanceSnowNrg' , 'balance of energy in the snow on data window' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%balanceSoilNrg) = var_info('balanceSoilNrg' , 'balance of energy in the soil on data window' , 'W m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%balanceVegMass) = var_info('balanceVegMass' , 'balance of water in the vegetation on data window' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%balanceLayerMass) = var_info('balanceLayerMass' , 'balance of water in each snow+soil layer on substep' , 'kg m-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%balanceSnowMass) = var_info('balanceSnowMass' , 'balance of water in the snow on data window' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%balanceSoilMass) = var_info('balanceSoilMass' , 'balance of water in the soil on data window' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%balanceAqMass) = var_info('balanceAqMass' , 'balance of water in the aquifer on data window' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceCasNrg) = var_info('balanceCasNrg' , 'balance of energy in the canopy air space on data window' , 'W m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceVegNrg) = var_info('balanceVegNrg' , 'balance of energy in the vegetation on data window' , 'W m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceLayerNrg) = var_info('balanceLayerNrg' , 'balance of energy in each snow+soil layer on substep' , 'W m-3' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceSnowNrg) = var_info('balanceSnowNrg' , 'balance of energy in the snow on data window' , 'W m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceSoilNrg) = var_info('balanceSoilNrg' , 'balance of energy in the soil on data window' , 'W m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceVegMass) = var_info('balanceVegMass' , 'balance of water in the vegetation on data window' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceLayerMass) = var_info('balanceLayerMass' , 'balance of water in each snow+soil layer on substep' , 'kg m-2 s-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceSnowMass) = var_info('balanceSnowMass' , 'balance of water in the snow on data window' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceSoilMass) = var_info('balanceSoilMass' , 'balance of water in the soil on data window' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceAqMass) = var_info('balanceAqMass' , 'balance of water in the aquifer on data window' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! scaled balances + diag_meta(iLookDIAG%scaledBalanceCasNrg) = var_info('scaledBalanceCasNrg' , 'scaled balance of energy in the canopy air space on data window' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scaledBalanceVegNrg) = var_info('scaledBalanceVegNrg' , 'scaled balance of energy in the vegetation on data window' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scaledBalanceSnowNrg) = var_info('scaledBalanceSnowNrg' , 'scaled balance of energy in the snow on data window' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scaledBalanceSoilNrg) = var_info('scaledBalanceSoilNrg' , 'scaled balance of energy in the soil on data window' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scaledBalanceVegMass) = var_info('scaledBalanceVegMass' , 'scaled balance of water in the vegetation on data window' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scaledBalanceSnowMass) = var_info('scaledBalanceSnowMass' , 'scaled balance of water in the snow on data window' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scaledBalanceSoilMass) = var_info('scaledBalanceSoilMass' , 'scaled balance of water in the soil on data window' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%scaledBalanceAqMass) = var_info('scaledBalanceAqMass' , 'scaled balance of water in the aquifer on data window' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! ----- ! * local model fluxes... ! ----------------------- diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index a79cc716f..cf3766c43 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -388,9 +388,13 @@ MODULE var_lookup integer(i4b) :: mLayerThermalC = integerMissing ! thermal conductivity at the mid-point of each layer (W m-1 K-1) integer(i4b) :: iLayerThermalC = integerMissing ! thermal conductivity at the interface of each layer (W m-1 K-1) ! enthalpy - integer(i4b) :: scalarCanairEnthalpy = integerMissing ! temperature component of enthalpy of the canopy air space (J m-3) - integer(i4b) :: scalarCanopyEnthalpy = integerMissing ! temperature component of enthalpy of the vegetation canopy (J m-3) - integer(i4b) :: mLayerEnthalpy = integerMissing ! temperature component of enthalpy of the snow+soil layers (J m-3) + integer(i4b) :: scalarCanairEnthalpy = integerMissing ! enthalpy of the canopy air space (J m-3) + integer(i4b) :: scalarCanopyEnthTemp = integerMissing ! temperature component of enthalpy of the vegetation canopy (J m-3) + integer(i4b) :: scalarCanopyEnthalpy = integerMissing ! enthalpy of the vegetation canopy (J m-3) + integer(i4b) :: mLayerEnthTemp = integerMissing ! temperature component of enthalpy of the snow+soil layers (J m-3) + integer(i4b) :: mLayerEnthalpy = integerMissing ! enthalpy of the snow+soil layers (J m-3) + integer(i4b) :: scalarTotalSoilEnthalpy = integerMissing ! total enthalpy of the soil column (J m-3) + integer(i4b) :: scalarTotalSnowEnthalpy = integerMissing ! total enthalpy of the snow column (J m-3) ! forcing integer(i4b) :: scalarVPair = integerMissing ! vapor pressure of the air above the vegetation canopy (Pa) integer(i4b) :: scalarVP_CanopyAir = integerMissing ! vapor pressure of the canopy air space (Pa) @@ -453,8 +457,6 @@ MODULE var_lookup integer(i4b) :: scalarSoilCompress = integerMissing ! change in total soil storage due to compression of the soil matrix (kg m-2 s-1) integer(i4b) :: mLayerMatricHeadLiq = integerMissing ! matric potential of liquid water (m) ! mass balance check - integer(i4b) :: scalarSoilWatBalError = integerMissing ! error in the total soil water balance (kg m-2) - integer(i4b) :: scalarAquiferBalError = integerMissing ! error in the aquifer water balance (kg m-2) integer(i4b) :: scalarTotalSoilLiq = integerMissing ! total mass of liquid water in the soil (kg m-2) integer(i4b) :: scalarTotalSoilIce = integerMissing ! total mass of ice in the soil (kg m-2) integer(i4b) :: scalarTotalSoilWat = integerMissing ! total mass of water in the soil (kg m-2) @@ -467,16 +469,25 @@ MODULE var_lookup integer(i4b) :: wallClockTime = integerMissing ! wall clock time (s) integer(i4b) :: meanStepSize = integerMissing ! mean time step size over data window (s) ! balances - integer(i4b) :: balanceCasNrg = integerMissing ! balance of energy in the canopy air space (W m-2) - integer(i4b) :: balanceVegNrg = integerMissing ! balance of energy in the vegetation (W m-2) - integer(i4b) :: balanceLayerNrg = integerMissing ! balance of energy in each snow+soil layer (W m-2) - integer(i4b) :: balanceSnowNrg = integerMissing ! balance of energy in the snow (W m-2) - integer(i4b) :: balanceSoilNrg = integerMissing ! balance of energy in the soil (W m-2) - integer(i4b) :: balanceVegMass = integerMissing ! balance of water in the vegetation (kg m-2) - integer(i4b) :: balanceLayerMass = integerMissing ! balance of water in each snow+soil layer (kg m-2) - integer(i4b) :: balanceSnowMass = integerMissing ! balance of water in the snow (kg m-2) - integer(i4b) :: balanceSoilMass = integerMissing ! balance of water in the soil (kg m-2) - integer(i4b) :: balanceAqMass = integerMissing ! balance of water in the aquifer (kg m-2) + integer(i4b) :: balanceCasNrg = integerMissing ! balance of energy in the canopy air space (W m-3) + integer(i4b) :: balanceVegNrg = integerMissing ! balance of energy in the vegetation (W m-3) + integer(i4b) :: balanceLayerNrg = integerMissing ! balance of energy in each snow+soil layer (W m-3) + integer(i4b) :: balanceSnowNrg = integerMissing ! balance of energy in the snow (W m-3) + integer(i4b) :: balanceSoilNrg = integerMissing ! balance of energy in the soil (W m-3) + integer(i4b) :: balanceVegMass = integerMissing ! balance of water in the vegetation (kg m-2 s-1) + integer(i4b) :: balanceLayerMass = integerMissing ! balance of water in each snow+soil layer (kg m-2 s-1) + integer(i4b) :: balanceSnowMass = integerMissing ! balance of water in the snow (kg m-2 s-1) + integer(i4b) :: balanceSoilMass = integerMissing ! balance of water in the soil (kg m-2 s-1) + integer(i4b) :: balanceAqMass = integerMissing ! balance of water in the aquifer (kg m-2 s-1) + ! scaled balances + integer(i4b) :: scaledBalanceCasNrg = integerMissing ! scaled balance of energy in the canopy air space (s-1) + integer(i4b) :: scaledBalanceVegNrg = integerMissing ! scaled balance of energy in the vegetation (s-1) + integer(i4b) :: scaledBalanceSnowNrg = integerMissing ! scaled balance of energy in the snow (s-1) + integer(i4b) :: scaledBalanceSoilNrg = integerMissing ! scaled balance of energy in the soil (s-1) + integer(i4b) :: scaledBalanceVegMass = integerMissing ! scaled balance of water in the vegetation (s-1) + integer(i4b) :: scaledBalanceSnowMass = integerMissing ! scaled balance of water in the snow (s-1) + integer(i4b) :: scaledBalanceSoilMass = integerMissing ! scaled balance of water in the soil (s-1) + integer(i4b) :: scaledBalanceAqMass = integerMissing ! scaled balance of water in the aquifer (s-1) endtype iLook_diag ! *********************************************************************************************************** @@ -906,7 +917,8 @@ MODULE var_lookup 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,& 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,& 91, 92, 93, 94, 95, 96, 97, 98, 99,100,& - 101,102) + 101,102,103,104,105,106,107,108,109,110,& + 111,112) ! named variables: model fluxes type(iLook_flux), public,parameter :: iLookFLUX =iLook_flux ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& diff --git a/build/source/engine/computEnthalpy.f90 b/build/source/engine/computEnthalpy.f90 deleted file mode 100644 index 7b649d538..000000000 --- a/build/source/engine/computEnthalpy.f90 +++ /dev/null @@ -1,153 +0,0 @@ -module computEnthalpy_module - -! data types -USE nrtype - -! derived types to define the data structures -USE data_types,only:& - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (rkind) - -! named variables -USE var_lookup,only:iLookPROG ! named variables for structure elements -USE var_lookup,only:iLookDIAG ! named variables for structure elements -USE var_lookup,only:iLookFLUX ! named variables for structure elements -USE var_lookup,only:iLookINDEX ! named variables for structure elements -USE var_lookup,only:iLookPARAM ! named variables for structure elements - -! access the global print flag -USE globalData,only:globalPrintFlag - -! access missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing real number -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil - -! constants -USE multiconst,only:& - Tfreeze, & ! freezing temperature (K) - LH_fus, & ! latent heat of fusion (J kg-1) - LH_vap, & ! latent heat of vaporization (J kg-1) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water, & ! intrinsic density of liquid water (kg m-3) - iden_air, & - ! specific heat - Cp_air, & ! specific heat of air (J kg-1 K-1) - Cp_ice, & ! specific heat of ice (J kg-1 K-1) - Cp_soil, & ! specific heat of soil (J kg-1 K-1) - Cp_water ! specific heat of liquid water (J kg-1 K-1) -! privacy -implicit none -private -public::computEnthalpy -public::computEnthalpyPrime -contains - -! ********************************************************************************************************** -! public subroutine computEnthalpy -! ********************************************************************************************************** -subroutine computEnthalpy(& - ! input - indx_data, & ! intent(in): indices defining model states and layers - nLayers, & ! intent(in): number of snow layers - mLayerTemp, & ! intent(in): temperature of each snow/soil layer (K) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice (-) - mLayerHeatCap, & ! intent(in): heat capacity of each snow/soil layer (J m-3 K-1) - ! output - mLayerEnthalpy & ! intent(out): enthalpy of each snow/soil layer (J m-3) - ) - ! -------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input: model control - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - integer(i4b),intent(in) :: nLayers ! number of snow layers - real(rkind),intent(in) :: mLayerTemp(:) ! temperature of each snow/soil layer (K) - real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice (-) - real(rkind),intent(in) :: mLayerHeatCap(:) ! heat capacity of each snow/soil layer (J m-3 K-1) - real(rkind),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow/soil layer (J m-3) - ! local variables - integer(i4b) :: iLayer ! loop index - - ! -------------------------------------------------------------------------------------------------------------------------------- - - associate(& - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of layer - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat & ! intent(in): [i4b(:)] indices for energy states - ) - ! (loop through non-missing energy state variables in the snow+soil domain) - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) - select case( layerType(iLayer) ) - case(iname_snow) - mLayerEnthalpy(iLayer) = mLayerHeatCap(iLayer)*mLayerTemp(iLayer) - LH_fus*iden_ice * mLayerVolFracIce(iLayer) - case(iname_soil) - mLayerEnthalpy(iLayer) = mLayerHeatCap(iLayer)*mLayerTemp(iLayer) - LH_fus*iden_water * mLayerVolFracIce(iLayer) - end select - end do ! looping through non-missing energy state variables in the snow+soil domain - - end associate - -end subroutine computEnthalpy - -! ********************************************************************************************************** -! public subroutine computEnthalpyPrime -! ********************************************************************************************************** -subroutine computEnthalpyPrime(& - ! input - computeVegFlux, & ! intent(in): logical flag to denote if computing the vegetation flux - indx_data, & ! intent(in): indices defining model states and layers - nLayers, & ! intent(in): number of snow layers - canopyDepth, & ! intent(in): canopy depth (m) - scalarCanopyTempPrime, & ! intent(in): Prime value for the temperature of the vegetation canopy (K) - scalarCanopyIcePrime, & ! intent(in): Prime value for the ice on the vegetation canopy (kg m-2) - mLayerTempPrime, & ! intent(in): temperature of each snow/soil layer (K) - mLayerVolFracIcePrime, & ! intent(in): Prime value for volumetric fraction of ice (-) - heatCapVeg, & ! intent(in): specific heat of vegetation (J kg-1 K-1) - mLayerHeatCap, & ! intent(in): heat capacity of each snow/soil layer (J m-3 K-1) - ! output - scalarCanopyEnthalpyPrime, & ! intent(out): Prime value for the enthalpy of the vegetation canopy (J m-2) - mLayerEnthalpyPrime & ! intent(out): enthalpy of each snow/soil layer (J m-3) - ) - ! -------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - integer(i4b),intent(in) :: nLayers ! number of snow layers - real(rkind),intent(in) :: canopyDepth ! canopy depth (m) - real(rkind),intent(in) :: scalarCanopyTempPrime ! Prime value for the temperature of the vegetation canopy (K) - real(rkind),intent(in) :: scalarCanopyIcePrime ! Prime value for the ice on the vegetation canopy (kg m-2) - real(rkind),intent(in) :: heatCapVeg ! specific heat of vegetation (J kg-1 K-1) - real(rkind),intent(in) :: mLayerTempPrime(:) ! Prime value for temperature of each snow/soil layer (K) - real(rkind),intent(in) :: mLayerVolFracIcePrime(:) ! Prime value for volumetric fraction of ice (-) - real(rkind),intent(in) :: mLayerHeatCap(:) ! heat capacity of each snow/soil layer (J m-3 K-1) - real(rkind),intent(out) :: scalarCanopyEnthalpyPrime ! Prime value for the enthalpy of the vegetation canopy (J m-2) - real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! enthalpy of each snow/soil layer (J m-3) - ! local variables - integer(i4b) :: iLayer ! loop index - - ! -------------------------------------------------------------------------------------------------------------------------------- - - associate(& - layerType => indx_data%var(iLookINDEX%layerType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of layer - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat & ! intent(in): [i4b(:)] indices for energy states - ) - - if(computeVegFlux)then - scalarCanopyEnthalpyPrime = heatCapVeg * scalarCanopyTempPrime - LH_fus*scalarCanopyIcePrime/canopyDepth - end if - ! (loop through non-missing energy state variables in the snow+soil domain) - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) - select case( layerType(iLayer) ) - case(iname_snow) - mLayerEnthalpyPrime(iLayer) = mLayerHeatCap(iLayer)*mLayerTempPrime(iLayer) - LH_fus*iden_ice * mLayerVolFracIcePrime(iLayer) - case(iname_soil) - mLayerEnthalpyPrime(iLayer) = mLayerHeatCap(iLayer)*mLayerTempPrime(iLayer) - LH_fus*iden_water * mLayerVolFracIcePrime(iLayer) - end select - end do ! looping through non-missing energy state variables in the snow+soil domain - - end associate - -end subroutine computEnthalpyPrime - -end module computEnthalpy_module diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index d0e7471d2..24b94ee74 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -431,7 +431,7 @@ subroutine finalize_computFlux ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1), & ! intent(in): [i4b] index of canopy energy state variable ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1), & ! intent(in): [i4b] index of canopy hydrology state variable (mass) scalarCanairNetNrgFlux => flux_data%var(iLookFLUX%scalarCanairNetNrgFlux)%dat(1), & ! intent(out): [dp] net energy flux for the canopy air space (W m-2) - scalarCanopyNetNrgFlux => flux_data%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1), & ! intent(out): [dp] net energy flux for the vegetation canopy (W m-2) + scalarCanopyNetNrgFlux => flux_data%var(iLookFLUX%scalarCanopyNetNrgFlux)%dat(1), & ! intent(out): [dp] net energy flux for the vegetation canopy (W m-2) scalarCanopyNetLiqFlux => flux_data%var(iLookFLUX%scalarCanopyNetLiqFlux)%dat(1), & ! intent(out): [dp] net liquid water flux for the vegetation canopy (kg m-2 s-1) canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): [dp] canopy depth (m) nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg)%dat(1), & ! intent(in): [i4b] number of energy state variables in the snow+soil domain @@ -456,7 +456,7 @@ subroutine finalize_computFlux layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): [i4b(:)] type of layer (iname_soil or iname_snow) mLayerLiqFluxSnow => flux_data%var(iLookFLUX%mLayerLiqFluxSnow)%dat, & ! intent(out): [dp] net liquid water flux for each snow layer (s-1) mLayerLiqFluxSoil => flux_data%var(iLookFLUX%mLayerLiqFluxSoil)%dat, & ! intent(out): [dp] net liquid water flux for each soil layer (s-1) - scalarAquiferTranspire => flux_data%var(iLookFLUX%scalarAquiferTranspire)%dat(1), & ! intent(out): [dp] transpiration loss from the aquifer (m s-1 + scalarAquiferTranspire => flux_data%var(iLookFLUX%scalarAquiferTranspire)%dat(1), & ! intent(out): [dp] transpiration loss from the aquifer (m s-1) scalarAquiferRecharge => flux_data%var(iLookFLUX%scalarAquiferRecharge)%dat(1), & ! intent(out): [dp] recharge to the aquifer (m s-1) scalarAquiferBaseflow => flux_data%var(iLookFLUX%scalarAquiferBaseflow)%dat(1) ) ! intent(out): [dp] total baseflow from the aquifer (m s-1) ! populate the flux vector for hydrology diff --git a/build/source/engine/computResid.f90 b/build/source/engine/computResid.f90 index b34edaf41..d096470f0 100644 --- a/build/source/engine/computResid.f90 +++ b/build/source/engine/computResid.f90 @@ -99,9 +99,9 @@ subroutine computResid(& ! input: enthalpy terms scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (J kg K-1) mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (J kg K-1) - scalarCanairEnthalpyTrial, & ! intent(in): trial value for temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(in): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(in): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTempTrial, & ! intent(in): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthTempTrial, & ! intent(in): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -137,9 +137,9 @@ subroutine computResid(& ! input: enthalpy terms real(qp),intent(in) :: scalarCanopyCmTrial ! Cm of vegetation canopy (-) real(qp),intent(in) :: mLayerCmTrial(:) ! Cm of each snow and soil layer (-) - real(rkind),intent(in) :: scalarCanairEnthalpyTrial ! trial value for temperature component of enthalpy of the canopy air space (J m-3) - real(rkind),intent(in) :: scalarCanopyEnthalpyTrial ! trial value for temperature component of enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(in) :: mLayerEnthalpyTrial(:) ! trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) + real(rkind),intent(in) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3) + real(rkind),intent(in) :: scalarCanopyEnthTempTrial ! trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(in) :: mLayerEnthTempTrial(:) ! trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) ! input: data structures type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU @@ -175,9 +175,9 @@ subroutine computResid(& mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) ! enthalpy terms - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(in): [dp] temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(in): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(in): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(in): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! intent(in): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthTemp => diag_data%var(iLookDIAG%mLayerEnthTemp)%dat ,& ! intent(in): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) ! model state variables (aquifer) scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in): [dp] storage of water in the aquifer (m) ! canopy and layer depth @@ -249,7 +249,7 @@ subroutine computResid(& ! --> energy balance if(useEnthalpy)then if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = ( scalarCanairEnthalpyTrial - scalarCanairEnthalpy ) - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) - if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = ( scalarCanopyEnthalpyTrial - scalarCanopyEnthalpy ) - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = ( scalarCanopyEnthTempTrial - scalarCanopyEnthTemp ) - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) else if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*( scalarCanairTempTrial - scalarCanairTemp ) - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg)*( scalarCanopyTempTrial - scalarCanopyTemp ) + scalarCanopyCmTrial*( scalarCanopyWatTrial - scalarCanopyWat )/canopyDepth & @@ -266,7 +266,7 @@ subroutine computResid(& if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) if(useEnthalpy)then - rVec( ixSnowSoilNrg(iLayer) ) = ( mLayerEnthalpyTrial(iLayer) - mLayerEnthalpy(iLayer) ) - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) + rVec( ixSnowSoilNrg(iLayer) ) = ( mLayerEnthTempTrial(iLayer) - mLayerEnthTemp(iLayer) ) - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) else rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) )*( mLayerTempTrial(iLayer) - mLayerTemp(iLayer) ) + mLayerCmTrial(iLayer)*( mLayerVolFracWatTrial(iLayer) - mLayerVolFracWat(iLayer) ) & - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index f8908a2ef..0efa05bc7 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -63,6 +63,8 @@ module coupled_em_module USE globalData,only:data_step ! time step of forcing data (s) USE globalData,only:model_decisions ! model decision structure USE globalData,only:globalPrintFlag ! the global print flag +USE globalData,only:realMissing ! missing double precision number + ! look-up values for the maximum interception capacity USE mDecisions_module,only: & @@ -91,10 +93,7 @@ module coupled_em_module private public::coupled_em ! algorithmic parameters -real(rkind),parameter :: valueMissing=-9999._rkind ! missing value, used when diagnostic or state variables are undefined real(rkind),parameter :: verySmall=1.e-6_rkind ! used as an additive constant to check if substantial difference among real numbers -real(rkind),parameter :: mpe=1.e-6_rkind ! prevents overflow error if division by zero -real(rkind),parameter :: dx=1.e-6_rkind ! finite difference increment contains @@ -124,27 +123,28 @@ subroutine coupled_em(& ! error control err,message) ! intent(out): error control ! structure allocations - USE allocspace_module,only:allocLocal ! allocate local data structures - USE allocspace_module,only:resizeData ! clone a data structure + USE allocspace_module,only:allocLocal ! allocate local data structures + USE allocspace_module,only:resizeData ! clone a data structure ! simulation of fluxes and residuals given a trial state vector - USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential + USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential ! preliminary subroutines - USE vegPhenlgy_module,only:vegPhenlgy ! compute vegetation phenology - USE vegNrgFlux_module,only:wettedFrac ! compute wetted fraction of the canopy (used in sw radiation fluxes) - USE snowAlbedo_module,only:snowAlbedo ! compute snow albedo - USE vegSWavRad_module,only:vegSWavRad ! compute canopy sw radiation fluxes - USE canopySnow_module,only:canopySnow ! compute interception and unloading of snow from the vegetation canopy - USE volicePack_module,only:newsnwfall ! compute change in the top snow layer due to throughfall and unloading - USE volicePack_module,only:volicePack ! merge and sub-divide snow layers, if necessary - USE diagn_evar_module,only:diagn_evar ! compute diagnostic energy variables -- thermal conductivity and heat capacity + USE vegPhenlgy_module,only:vegPhenlgy ! compute vegetation phenology + USE vegNrgFlux_module,only:wettedFrac ! compute wetted fraction of the canopy (used in sw radiation fluxes) + USE snowAlbedo_module,only:snowAlbedo ! compute snow albedo + USE vegSWavRad_module,only:vegSWavRad ! compute canopy sw radiation fluxes + USE canopySnow_module,only:canopySnow ! compute interception and unloading of snow from the vegetation canopy + USE volicePack_module,only:newsnwfall ! compute change in the top snow layer due to throughfall and unloading + USE volicePack_module,only:volicePack ! merge and sub-divide snow layers, if necessary + USE diagn_evar_module,only:diagn_evar ! compute diagnostic energy variables -- thermal conductivity and heat capacity ! the model solver - USE indexState_module,only:indexState ! define indices for all model state variables and layers - USE opSplittin_module,only:opSplittin ! solve the system of thermodynamic and hydrology equations for a given substep - USE time_utils_module,only:elapsedSec ! calculate the elapsed time + USE indexState_module,only:indexState ! define indices for all model state variables and layers + USE opSplittin_module,only:opSplittin ! solve the system of thermodynamic and hydrology equations for a given substep + USE time_utils_module,only:elapsedSec ! calculate the elapsed time ! additional subroutines - USE tempAdjust_module,only:tempAdjust ! adjust snow temperature associated with new snowfall - USE var_derive_module,only:calcHeight ! module to calculate height at layer interfaces and layer mid-point - USE computSnowDepth_module,only:computSnowDepth + USE tempAdjust_module,only:tempAdjust ! adjust snow temperature associated with new snowfall + USE var_derive_module,only:calcHeight ! module to calculate height at layer interfaces and layer mid-point + USE computSnowDepth_module,only:computSnowDepth ! compute snow depth + USE enthalpyTemp_module,only:enthTemp2H ! add phase change terms to delta temperature component of enthalpy implicit none @@ -180,6 +180,7 @@ subroutine coupled_em(& real(rkind) :: dtSave ! length of last input model whole sub-step (seconds) real(rkind) :: dt_sub ! length of model sub-step (seconds) real(rkind) :: dt_wght ! weight applied to model sub-step (dt_sub/data_step) + real(rkind) :: lyr_wght ! weight applied to domain layer (layer_depth/domain_depth) real(rkind) :: dt_solv ! seconds in the data step that have been completed real(rkind) :: dtMultiplier ! time step multiplier (-) based on what happenned in "opSplittin" real(rkind) :: minstep,maxstep ! minimum and maximum time step length (seconds) @@ -257,13 +258,14 @@ subroutine coupled_em(& real(rkind) :: balanceAquifer0 ! total aquifer storage at the start of the step (kg m-2) real(rkind) :: balanceAquifer1 ! total aquifer storage at the end of the step (kg m-2) real(rkind) :: innerBalance(4) ! inner step balances for domain with one layer - real(rkind) :: meanBalance(8) ! timestep-average balances for domain with one layer + real(rkind) :: meanBalance(8) ! timestep-average balances for domains + real(rkind) :: divisor(8) ! divisor for scaled the balances real(rkind),allocatable :: innerBalanceLayerMass(:) ! inner step balances for domain with multiple layers real(rkind),allocatable :: innerBalanceLayerNrg(:) ! inner step balances for domain with multiple layers ! test balance checks - logical(lgt), parameter :: printBalance=.false. ! flag to print the balance checks - real(rkind), allocatable :: liqSnowInit(:) ! volumetric liquid water conetnt of snow at the start of the time step - real(rkind), allocatable :: liqSoilInit(:) ! soil moisture at the start of the time step + logical(lgt),parameter :: printBalance=.false. ! flag to print the balance checks + real(rkind),allocatable :: liqSnowInit(:) ! volumetric liquid water conetnt of snow at the start of the time step + real(rkind),allocatable :: liqSoilInit(:) ! soil moisture at the start of the time step ! timing information real(rkind) :: startTime ! start time (used to compute wall clock time) real(rkind) :: endTime ! end time (used to compute wall clock time) @@ -1073,18 +1075,18 @@ subroutine coupled_em(& if (nSnow>0) innerEffRainfall = innerEffRainfall + ( flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) + flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) )*dt_wght ! sum the balance of energy and water per state - innerBalance(1) = innerBalance(1) + diag_data%var(iLookDIAG%balanceCasNrg)%dat(1)*dt_wght - innerBalance(2) = innerBalance(2) + diag_data%var(iLookDIAG%balanceVegNrg)%dat(1)*dt_wght - innerBalance(3) = innerBalance(3) + diag_data%var(iLookDIAG%balanceVegMass)%dat(1)*dt_wght - innerBalance(4) = innerBalance(4) + diag_data%var(iLookDIAG%balanceAqMass)%dat(1)*dt_wght - innerBalanceLayerNrg(:) = innerBalanceLayerNrg(:) + diag_data%var(iLookDIAG%balanceLayerNrg)%dat(:)*dt_wght - innerBalanceLayerMass(:) = innerBalanceLayerMass(:) + diag_data%var(iLookDIAG%balanceLayerMass)%dat(:)*dt_wght + innerBalance(1) = innerBalance(1) + diag_data%var(iLookDIAG%balanceCasNrg)%dat(1)*dt_wght ! W m-3 + innerBalance(2) = innerBalance(2) + diag_data%var(iLookDIAG%balanceVegNrg)%dat(1)*dt_wght ! W m-3 + innerBalance(3) = innerBalance(3) + diag_data%var(iLookDIAG%balanceVegMass)%dat(1)*dt_wght ! kg m-2 s-1 + innerBalance(4) = innerBalance(4) + diag_data%var(iLookDIAG%balanceAqMass)%dat(1)*dt_wght * iden_water ! kg m-2 s-1 + innerBalanceLayerNrg(:) = innerBalanceLayerNrg(:) + diag_data%var(iLookDIAG%balanceLayerNrg)%dat(:)*dt_wght ! W m-3 + innerBalanceLayerMass(:) = innerBalanceLayerMass(:) + diag_data%var(iLookDIAG%balanceLayerMass)%dat(:)*dt_wght * prog_data%var(iLookPROG%mLayerDepth)%dat(:) * iden_water ! kg m-2 s-1 ! save balance of energy and water per snow+soil layer after inner step, since can change nLayers with outer steps diag_data%var(iLookDIAG%balanceLayerNrg)%dat(:) = innerBalanceLayerNrg(:) diag_data%var(iLookDIAG%balanceLayerMass)%dat(:) = innerBalanceLayerMass(:) - ! compute the mean balance of energy and water per entire snow and soil domain + ! compute the balance of energy and water per entire snow and soil domain, in W m-3 and kg m-2 s-1 respsectively diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = 0._rkind diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = 0._rkind diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = 0._rkind @@ -1092,11 +1094,13 @@ subroutine coupled_em(& do iLayer=1,nLayers select case (indx_data%var(iLookINDEX%layerType)%dat(iLayer)) case (iname_snow) - diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) + innerBalanceLayerNrg(iLayer)/nSnow - diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) + innerBalanceLayerMass(iLayer)/nSnow + lyr_wght = prog_data%var(iLookPROG%mLayerDepth)%dat(iLayer) / prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) + diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) + innerBalanceLayerNrg(iLayer)*lyr_wght + diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) + innerBalanceLayerMass(iLayer) case (iname_soil) - diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) + innerBalanceLayerNrg(iLayer)/nSoil - diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) + innerBalanceLayerMass(iLayer)/nSoil + lyr_wght = prog_data%var(iLookPROG%mLayerDepth)%dat(iLayer) / sum( prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers) ) + diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) + innerBalanceLayerNrg(iLayer)*lyr_wght + diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) + innerBalanceLayerMass(iLayer) end select end do if(do_outer)then @@ -1219,16 +1223,6 @@ subroutine coupled_em(& deallocate(innerSoilCompress) deallocate(meanSoilCompress) - ! save balance of energy and water per single layer domain - diag_data%var(iLookDIAG%balanceCasNrg)%dat(1) = meanBalance(1) - diag_data%var(iLookDIAG%balanceVegNrg)%dat(1) = meanBalance(2) - diag_data%var(iLookDIAG%balanceVegMass)%dat(1) = meanBalance(3) - diag_data%var(iLookDIAG%balanceAqMass)%dat(1) = meanBalance(4) - diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = meanBalance(5) - diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = meanBalance(6) - diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = meanBalance(7) - diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = meanBalance(8) - ! *********************************************************************************************************************************** ! --- ! *** balance checks... @@ -1261,12 +1255,21 @@ subroutine coupled_em(& averageSoilCompress => diag_data%var( iLookDIAG%scalarSoilCompress) %dat(1) ,& ! soil compression (kg m-2 s-1) averageGroundEvaporation => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarGroundEvaporation) )%dat(1) ,& ! soil evaporation (kg m-2 s-1) averageCanopyTranspiration => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopyTranspiration))%dat(1) ,& ! canopy transpiration (kg m-2 s-1) + ! state variables in the atmosphere + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! total enthalpy in the atmospheric column (J m-3) ! state variables in the vegetation canopy scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! canopy ice content (kg m-2) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! total enthalpy in the canopy (J m-3) + ! state variables in the snow domain + scalarSWE => prog_data%var(iLookPROG%scalarSWE)%dat(1) ,& ! snow water equivalent (kg m-2) + scalarTotalSnowEnthalpy => diag_data%var(iLookDIAG%scalarTotalSnowEnthalpy)%dat(1) ,& ! total enthalpy in the snow column (J m-3) ! state variables in the soil domain mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers) ,& ! depth of each soil layer (m) mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat(nSnow+1:nLayers) ,& ! volumetric ice content in each soil layer (-) mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(nSnow+1:nLayers) ,& ! volumetric liquid water content in each soil layer (-) + scalarTotalSoilWat => diag_data%var(iLookDIAG%scalarTotalSoilWat)%dat(1) ,& ! total water in the soil column (kg m-2) + scalarTotalSoilEnthalpy => diag_data%var(iLookDIAG%scalarTotalSoilEnthalpy)%dat(1) ,& ! total enthalpy in the soil column (J m-3) + ! state variables in the aquifer scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! aquifer storage (m) ! error tolerance absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1) ,& ! absolute convergence tolerance for vol frac liq water (-) @@ -1274,6 +1277,37 @@ subroutine coupled_em(& scalarTotalSoilLiq => diag_data%var(iLookDIAG%scalarTotalSoilLiq)%dat(1) & ! total liquid water in the soil column (kg m-2) ) ! (association of local variables with information in the data structures + ! save balance of energy and water per single layer domain + diag_data%var(iLookDIAG%balanceCasNrg)%dat(1) = meanBalance(1) ! W m-3 + diag_data%var(iLookDIAG%balanceVegNrg)%dat(1) = meanBalance(2) ! W m-3 + diag_data%var(iLookDIAG%balanceVegMass)%dat(1) = meanBalance(3) ! kg m-2 s-1 + diag_data%var(iLookDIAG%balanceAqMass)%dat(1) = meanBalance(4) ! kg m-2 s-1 + diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = meanBalance(5) ! W m-3 + diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = meanBalance(6) ! W m-3 + diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = meanBalance(7) ! kg m-2 s-1 + diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = meanBalance(8) ! kg m-2 s-1 + + ! scale balance of energy and water per single layer domain by the scalar variable + divisor(1) = scalarCanairEnthalpy ! J m-3 + divisor(2) = scalarCanopyEnthalpy ! J m-3 + divisor(3) = scalarCanopyWat ! kg m-2 + divisor(4) = scalarAquiferStorage * iden_water ! kg m-2 + divisor(5) = scalarTotalSnowEnthalpy ! J m-3 + divisor(6) = scalarTotalSoilEnthalpy ! J m-3 + divisor(7) = scalarSWE ! kg m-2 + divisor(8) = scalarTotalSoilWat ! kg m-2 + meanBalance = merge( realMissing, meanBalance / divisor, divisor == 0._rkind) + + diag_data%var(iLookDIAG%scaledBalanceCasNrg)%dat(1) = meanBalance(1) ! s-1 + diag_data%var(iLookDIAG%scaledBalanceVegNrg)%dat(1) = meanBalance(2) ! s-1 + diag_data%var(iLookDIAG%scaledBalanceVegMass)%dat(1) = meanBalance(3) ! s-1 + diag_data%var(iLookDIAG%scaledBalanceAqMass)%dat(1) = meanBalance(4) ! s-1 + diag_data%var(iLookDIAG%scaledBalanceSnowNrg)%dat(1) = meanBalance(5) ! s-1 + diag_data%var(iLookDIAG%scaledBalanceSoilNrg)%dat(1) = meanBalance(6) ! s-1 + diag_data%var(iLookDIAG%scaledBalanceSnowMass)%dat(1) = meanBalance(7) ! s-1 + diag_data%var(iLookDIAG%scaledBalanceSoilMass)%dat(1) = meanBalance(8) ! s-1 + + ! identify the need to check the mass balance, both methods should work if tolerance coarse enough select case(ixNumericalMethod) case(ida); checkMassBalance_ds = .false. ! IDA balance agreement levels are controlled by set tolerances @@ -1416,8 +1450,49 @@ subroutine coupled_em(& ! end association to canopy depth end associate canopy - ! Save the total soil water (Liquid+Ice) + ! save the total soil water (Liquid+Ice) diag_data%var(iLookDIAG%scalarTotalSoilWat)%dat(1) = balanceSoilWater1 + + ! save the enthalpy and total enthalpy + ! associate local variables with information in the data structures + associate(& + ! canopy enthalpy + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! ice content of the vegetation canopy (kg m-2) + scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! temperature component of enthalpy of the vegetation canopy (K) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! enthalpy of the vegetation canopy (J m-3) + ! snow+soil enthalpy + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! volumetric ice content in each snow+soil layer (-) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! depth of each snow+soil layer (m) + mLayerEnthTemp => diag_data%var(iLookDIAG%mLayerEnthTemp)%dat ,& ! temperature component of enthalpy of each snow+soil layer (K) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! enthalpy of each snow+soil layer (J m-3) + scalarTotalSoilEnthalpy => diag_data%var(iLookDIAG%scalarTotalSoilEnthalpy)%dat(1) ,& ! total enthalpy of the soil column (J m-3) + scalarTotalSnowEnthalpy => diag_data%var(iLookDIAG%scalarTotalSnowEnthalpy)%dat(1) & ! total enthalpy of the snow column (J m-3) + ) ! (association of local variables with information in the data structures + + ! initialize the enthalpy + scalarCanopyEnthalpy = scalarCanopyEnthTemp + mLayerEnthalpy = mLayerEnthTemp + + ! compute enthalpy for current values + call enthTemp2H(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): model indices + ! input: ice content change + scalarCanopyIce, & ! intent(in): value for canopy ice content (kg m-2) + mLayerVolFracIce, & ! intent(in): vector of volumetric ice water content (-) + ! input/output: enthalpy + scalarCanopyEnthalpy, & ! intent(inout): value for enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy, & ! intent(inout): vector of enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + ! save the total soil enthalpy + scalarTotalSoilEnthalpy = sum(mLayerEnthalpy(nSnow+1:nLayers) * mLayerDepth(nSnow+1:nLayers))/sum(mLayerDepth(nSnow+1:nLayers)) + ! save the total snow enthalpy + scalarTotalSnowEnthalpy = sum(mLayerEnthalpy(1:nSnow) * mLayerDepth(1:nSnow))/sum(mLayerDepth(1:nSnow)) + end associate + ! save the surface temperature (just to make things easier to visualize) prog_data%var(iLookPROG%scalarSurfaceTemp)%dat(1) = prog_data%var(iLookPROG%mLayerTemp)%dat(1) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index b323753da..d1e07b870 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -63,10 +63,10 @@ module enthalpyTemp_module implicit none public::T2E_lookup public::T2L_lookup -public::enthalpy2t_snow -public::t2enthalpy_snow -public::t2enthalpy -public::enthalpy2DeltaH +public::enthTemp2T_snow +public::T2enthTemp_snow +public::T2enthTemp +public::enthTemp2H private::hyp_2F1_real ! define the snow look-up table used to compute temperature based on enthalpy @@ -115,7 +115,7 @@ subroutine T2E_lookup(mpar_data, & ! intent(in): paramet ! ***** compute specific enthalpy (NOTE: J m-3 --> J kg-1) ***** do ilook=1,nlook - Ey(ilook) = t2enthalpy_snow(Tk(ilook),waterWght,snowfrz_scale)/waterWght ! (J m-3 --> J kg-1) + Ey(ilook) = T2enthTemp_snow(Tk(ilook),waterWght,snowfrz_scale)/waterWght ! (J m-3 --> J kg-1) end do ! define the final enthalpy vector @@ -282,10 +282,10 @@ end subroutine T2L_lookup ! ************************************************************************************************************************ -! public subroutine enthalpy2t_snow: compute temperature based on specific temperature component of enthalpy +! public subroutine enthTemp2T_snow: compute temperature based on specific temperature component of enthalpy ! appropriate when no dry mass, as in snow ! ************************************************************************************************************************ -subroutine enthalpy2t_snow(Ey,BulkDenWater,fc_param,Tk,err,message) +subroutine enthTemp2T_snow(Ey,BulkDenWater,fc_param,Tk,err,message) ! ------------------------------------------------------------------------------------------------------------------------- implicit none ! ------------------------------------------------------------------------------------------------------------------------- @@ -311,7 +311,7 @@ subroutine enthalpy2t_snow(Ey,BulkDenWater,fc_param,Tk,err,message) real(rkind) :: dT ! temperature increment ! ------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="enthalpy2t_snow/" + err=0; message="enthTemp2T_snow/" ! convert input of total enthalpy (J m-3) to total specific enthalpy (J kg-1) E_spec = Ey/BulkDenWater ! (NOTE: no soil) @@ -321,8 +321,8 @@ subroutine enthalpy2t_snow(Ey,BulkDenWater,fc_param,Tk,err,message) Tg0 = (E_spec - E_lookup(1))/Cp_ice + T_lookup(1) Tg1 = Tg0+dx ! compute enthalpy - Ht0 = t2enthalpy_snow(Tg0,1._rkind,fc_param) - Ht1 = t2enthalpy_snow(Tg1,1._rkind,fc_param) + Ht0 = T2enthTemp_snow(Tg0,1._rkind,fc_param) + Ht1 = T2enthTemp_snow(Tg1,1._rkind,fc_param) ! compute function evaluations f0 = Ht0 - E_spec f1 = Ht1 - E_spec @@ -361,7 +361,7 @@ subroutine enthalpy2t_snow(Ey,BulkDenWater,fc_param,Tk,err,message) ! comute new value of Tg Tg1 = Tg0+dT ! get new function evaluation - Ht1 = t2enthalpy_snow(Tg1,1._rkind,fc_param) + Ht1 = T2enthTemp_snow(Tg1,1._rkind,fc_param) f1 = Ht1 - E_spec ! compute derivative if dT dh = (f1 - f0)/dT @@ -378,22 +378,22 @@ subroutine enthalpy2t_snow(Ey,BulkDenWater,fc_param,Tk,err,message) ! and check for convergence if(iter==niter)then; err=20; message=trim(message)//"failedToConverge"; return; end if end do ! (iteration loop) -end subroutine enthalpy2t_snow +end subroutine enthTemp2T_snow ! ************************************************************************************************************************ -! public function t2enthalpy_snow: compute temperature component of enthalpy based on temperature and mass (J m-3) for a +! public function T2enthTemp_snow: compute temperature component of enthalpy based on temperature and mass (J m-3) for a ! layer only where the layer has no dry mass, as in snow ! NOTE: enthalpy is a relative value, defined as zero at Tfreeze where all water is liquid ! ************************************************************************************************************************ -function t2enthalpy_snow(Tk,BulkDenWater,fc_param) +function T2enthTemp_snow(Tk,BulkDenWater,fc_param) ! ------------------------------------------------------------------------------------------------------------------------- implicit none ! declare dummy variables real(rkind),intent(in) :: Tk ! layer temperature (K) real(rkind),intent(in) :: BulkDenWater ! bulk density of water (kg m-3) real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) - real(rkind) :: t2enthalpy_snow ! return value of the function, total specific enthalpy (J m-3) + real(rkind) :: T2enthTemp_snow ! return value of the function, total specific enthalpy (J m-3) ! declare local variables real(rkind) :: frac_liq ! fraction of liquid water real(rkind) :: enthTempWater ! temperature component of specific enthalpy for total water (liquid and ice) (J kg-1) @@ -412,14 +412,14 @@ function t2enthalpy_snow(Tk,BulkDenWater,fc_param) enthMass = -LH_fus*(1._rkind - frac_liq) ! finally, compute the total enthalpy (J m-3) - t2enthalpy_snow = BulkDenWater*(enthTempWater + enthMass) !+ BulkDenSoil*enthTempSoil -end function t2enthalpy_snow + T2enthTemp_snow = BulkDenWater*(enthTempWater + enthMass) !+ BulkDenSoil*enthTempSoil +end function T2enthTemp_snow ! ************************************************************************************************************************ -! public subroutine t2enthalpy: compute temperature component of enthalpy from temperature and total water content +! public subroutine T2enthTemp: compute temperature component of enthalpy from temperature and total water content ! ************************************************************************************************************************ -subroutine t2enthalpy(& +subroutine T2enthTemp(& use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -435,9 +435,9 @@ subroutine t2enthalpy(& mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTemp, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthTemp, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control err,message) ! intent(out): error control ! ------------------------------------------------------------------------------------------------------------------------- @@ -463,9 +463,9 @@ subroutine t2enthalpy(& real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) ! output: enthalpy - real(rkind),intent(out) :: scalarCanairEnthalpy ! temperature component of enthalpy of the canopy air space (J m-3) - real(rkind),intent(out) :: scalarCanopyEnthalpy ! temperature component of enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(out) :: mLayerEnthalpy(:) ! temperature component of enthalpy of each snow+soil layer (J m-3) + real(rkind),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) + real(rkind),intent(out) :: scalarCanopyEnthTemp ! temperature component of enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(out) :: mLayerEnthTemp(:) ! temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -519,7 +519,7 @@ subroutine t2enthalpy(& ! ------------------------------------------------------------------------------------------------------------------------------ ! initialize error control - err=0; message="t2enthalpy/" + err=0; message="T2enthTemp/" ! loop through model state variables do iState=1,size(ixMapSubset2Full) @@ -572,7 +572,7 @@ subroutine t2enthalpy(& enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth endif - scalarCanopyEnthalpy = enthVeg + enthLiq + enthIce + scalarCanopyEnthTemp = enthVeg + enthLiq + enthIce end associate vegVars @@ -589,7 +589,7 @@ subroutine t2enthalpy(& enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) - mLayerEnthalpy(iLayer) = enthLiq + enthIce + enthAir + mLayerEnthTemp(iLayer) = enthLiq + enthIce + enthAir end associate snowVars @@ -681,7 +681,7 @@ subroutine t2enthalpy(& enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*diffT enthAir = iden_air*Cp_air*(1._rkind - theta_sat - volFracWat)*diffT - mLayerEnthalpy(iLayer) = enthWater + enthSoil + enthAir + mLayerEnthTemp(iLayer) = enthWater + enthSoil + enthAir end associate soilVars @@ -697,25 +697,24 @@ subroutine t2enthalpy(& end associate generalVars -end subroutine t2enthalpy +end subroutine T2enthTemp ! ************************************************************************************************************************ -! public subroutine enthalpy2DeltaH: compute change in mixture enthalpy or mixture enthalpy prime by adding terms of -! phase change from ice content change (= delta H) +! public subroutine enthTemp2H: add energy associated with thaw/freeze to temperature component of enthalpy to get total enthalpy, H ! ************************************************************************************************************************ -subroutine enthalpy2DeltaH(& +subroutine enthTemp2H(& ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): model indices + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): model indices ! input: ice content change - scalarCanopyIceDelta, & ! intent(in): value of canopy ice content (kg m-2) or prime ice content (kg m-2 s-1) - mLayerVolFracIceDelta, & ! intent(in): vector of volumetric fraction of ice (-) or prime volumetric fraction of ice (s-1) + scalarCanopyIce, & ! intent(in): value of canopy ice content (kg m-2) or prime ice content (kg m-2 s-1) + mLayerVolFracIce, & ! intent(in): vector of volumetric fraction of ice (-) or prime volumetric fraction of ice (s-1) ! input/output: enthalpy - scalarCanopyHmixDelta, & ! intent(inout): mixture enthalpy of the vegetation canopy (J m-3) or enthalpy prime (J m-3 s-1) - mLayerHmixDelta, & ! intent(inout): mixture enthalpy of each snow+soil layer (J m-3) or enthalpy prime (J m-3 s-1) + scalarCanopyH, & ! intent(inout): enthalpy of the vegetation canopy (J m-3) + mLayerH, & ! intent(inout): enthalpy of each snow+soil layer (J m-3) ! output: error control - err,message) ! intent(out): error control + err,message) ! intent(out): error control ! ------------------------------------------------------------------------------------------------------------------------- implicit none ! delare dummy variables @@ -724,11 +723,11 @@ subroutine enthalpy2DeltaH(& type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_ilength),intent(in) :: indx_data ! model indices ! input: ice content change - real(rkind),intent(in) :: scalarCanopyIceDelta ! delta value for canopy ice content (kg m-2) or prime ice content (kg m-2 s-1) - real(rkind),intent(in) :: mLayerVolFracIceDelta(:) ! delta vector of volumetric fraction of ice (-) or prime volumetric fraction of ice (s-1) + real(rkind),intent(in) :: scalarCanopyIce ! value for canopy ice content (kg m-2) or prime ice content (kg m-2 s-1) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! vector of volumetric fraction of ice (-) or prime volumetric fraction of ice (s-1) ! input output: enthalpy - real(rkind),intent(inout) :: scalarCanopyHmixDelta ! delta value for mixture enthalpy of the vegetation canopy (J m-3 s-1) - real(rkind),intent(inout) :: mLayerHmixDelta(:) ! delta vector of mixture enthalpy of each snow+soil layer (J m-3 s-1) + real(rkind),intent(inout) :: scalarCanopyH ! value for enthalpy of the vegetation canopy (J m-3 s-1) + real(rkind),intent(inout) :: mLayerH(:) ! vector of enthalpy of each snow+soil layer (J m-3 s-1) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -760,7 +759,7 @@ subroutine enthalpy2DeltaH(& ! ----------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="enthalpy2DeltaH/" + err=0; message="enthTemp2H/" ! loop through model state variables do iState=1,size(ixMapSubset2Full) @@ -781,13 +780,13 @@ subroutine enthalpy2DeltaH(& select case(ixDomainType) case(iname_cas); cycle ! canopy air space: do nothing (no water stored in canopy air space) case(iname_veg) - scalarCanopyHmixDelta = scalarCanopyHmixDelta - LH_fus * scalarCanopyIceDelta / canopyDepth + scalarCanopyH= scalarCanopyH - LH_fus * scalarCanopyIce/ canopyDepth case(iname_snow) iLayer = ixControlIndex - mLayerHmixDelta(iLayer) = mLayerHmixDelta(iLayer) - iden_ice * LH_fus * mLayerVolFracIceDelta(iLayer) + mLayerH(iLayer) = mLayerH(iLayer) - iden_ice * LH_fus * mLayerVolFracIce(iLayer) case(iname_soil) iLayer = ixControlIndex + nSnow - mLayerHmixDelta(iLayer) = mLayerHmixDelta(iLayer) - iden_water * LH_fus * mLayerVolFracIceDelta(iLayer) + mLayerH(iLayer) = mLayerH(iLayer) - iden_water * LH_fus * mLayerVolFracIce(iLayer) case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return end select @@ -797,7 +796,7 @@ subroutine enthalpy2DeltaH(& end associate generalVars -end subroutine enthalpy2DeltaH +end subroutine enthTemp2H !---------------------------------------------------------------------- ! private function: compute hypergeometric function with real arguments into real result diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index b8403515c..59c57f3d5 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -133,7 +133,7 @@ subroutine eval8summa(& USE getVectorz_module, only:varExtract ! extract variables from the state vector USE getVectorz_module, only:checkFeas ! check feasibility of state vector USE updateVars_module, only:updateVars ! update prognostic variables - USE enthalpyTemp_module, only:t2enthalpy ! compute enthalpy + USE enthalpyTemp_module, only:T2enthTemp ! compute enthalpy USE computFlux_module, only:soilCmpres ! compute soil compression USE computFlux_module, only:computFlux ! compute fluxes given a state vector USE computHeatCap_module,only:computHeatCapAnalytic ! recompute closed form heat capacity (Cp) and derivatives @@ -205,9 +205,9 @@ subroutine eval8summa(& real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) ! enthalpy - real(rkind) :: scalarCanairEnthalpyTrial ! trial value for temperature component of enthalpy of the canopy air space (J m-3 - real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for temperature component of enthalpy of the vegetation canopy (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector of temperature component of enthalpy for snow+soil layers (J m-3) + real(rkind) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3 + real(rkind) :: scalarCanopyEnthTempTrial ! trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthTempTrial ! trial vector of temperature component of enthalpy for snow+soil layers (J m-3) ! other local variables logical(lgt) :: checkLWBalance ! flag to check longwave balance integer(i4b) :: iLayer ! index of model layer in the snow+soil domain @@ -256,9 +256,9 @@ subroutine eval8summa(& mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) ! enthalpy from the previous solution - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(in): [dp] temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(in): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(in): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(in): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! intent(in): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthTemp => diag_data%var(iLookDIAG%mLayerEnthTemp)%dat ,& ! intent(in): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) ! soil compression scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2 s-1) mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in volumetric water content due to compression of soil (s-1) @@ -271,7 +271,7 @@ subroutine eval8summa(& dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1) ,& ! intent(out): [dp] derivative in bulk heat capacity w.r.t. volumetric water content dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1) ,&!intent(out): [dp] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative in bulk heat capacity w.r.t. temperature dThermalC_dWatAbove => deriv_data%var(iLookDERIV%dThermalC_dWatAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above @@ -517,7 +517,7 @@ subroutine eval8summa(& if(ixNrgConserv.ne.closedForm)then ! use residual as enthalpy_delta - (phase change)_delta ! compute temperature component of enthalpy - call t2enthalpy(& + call T2enthTemp(& ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -533,17 +533,17 @@ subroutine eval8summa(& mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) ! output: enthalpy - scalarCanairEnthalpyTrial, & ! intent(out): trial value for temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(out): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(out): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpyTrial, & ! intent(out): trial value for enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTempTrial, & ! intent(out): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthTempTrial, & ! intent(out): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif else if(ixNrgConserv == closedForm)then ! use residual as (Cp*DeltaTemp + Cm*DeltaTheta)_delta - (phase change)_delta scalarCanairEnthalpyTrial = realMissing - scalarCanopyEnthalpyTrial = realMissing - mLayerEnthalpyTrial = realMissing + scalarCanopyEnthTempTrial = realMissing + mLayerEnthTempTrial = realMissing endif !(choice of how compute heat capacity) ! save the number of flux calls per time step @@ -648,9 +648,9 @@ subroutine eval8summa(& ! input: enthalpy terms canopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) - scalarCanairEnthalpyTrial, & ! intent(in): trial value for temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(in): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(in): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTempTrial, & ! intent(in): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthTempTrial, & ! intent(in): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU diff --git a/build/source/engine/layerMerge.f90 b/build/source/engine/layerMerge.f90 index 0c828aac1..554690c7a 100644 --- a/build/source/engine/layerMerge.f90 +++ b/build/source/engine/layerMerge.f90 @@ -297,7 +297,7 @@ subroutine layer_combine(mpar_data,prog_data,diag_data,flux_data,indx_data,iSnow USE data_types,only:var_d ! data structures with fixed dimension ! provide access to external modules USE snow_utils_module,only:fracliquid ! compute fraction of liquid water - USE enthalpyTemp_module,only:enthalpy2t_snow,t2enthalpy_snow ! convert temperature to enthalpy for a snow layer + USE enthalpyTemp_module,only:enthTemp2T_snow,T2enthTemp_snow ! convert temperature to enthalpy for a snow layer implicit none ! ------------------------------------------------------------------------------------------------------------ ! input/output: data structures @@ -361,19 +361,19 @@ subroutine layer_combine(mpar_data,prog_data,diag_data,flux_data,indx_data,iSnow cBulkDenWat = (mLayerDepth(isnow)*bulkDenWat(1) + mLayerDepth(isnow+1)*bulkDenWat(2))/cDepth ! compute enthalpy for each layer (J m-3) - l1Enthalpy = t2enthalpy_snow(mLayerTemp(iSnow), BulkDenWat(1),snowfrz_scale) - l2Enthalpy = t2enthalpy_snow(mLayerTemp(iSnow+1),BulkDenWat(2),snowfrz_scale) + l1Enthalpy = T2enthTemp_snow(mLayerTemp(iSnow), BulkDenWat(1),snowfrz_scale) + l2Enthalpy = T2enthTemp_snow(mLayerTemp(iSnow+1),BulkDenWat(2),snowfrz_scale) ! compute combined enthalpy (J m-3) cEnthalpy = (mLayerDepth(isnow)*l1Enthalpy + mLayerDepth(isnow+1)*l2Enthalpy)/cDepth ! convert enthalpy (J m-3) to temperature (K) - call enthalpy2t_snow(cEnthalpy,cBulkDenWat,snowfrz_scale,cTemp,err,cmessage) + call enthTemp2T_snow(cEnthalpy,cBulkDenWat,snowfrz_scale,cTemp,err,cmessage) if(err/=0)then; err=10; message=trim(message)//trim(cmessage); return; end if ! test enthalpy conversion - if(abs(t2enthalpy_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat - cEnthalpy/cBulkDenWat) > eTol)then - write(*,'(a,1x,f12.5,1x,2(e20.10,1x))') 'enthalpy test', cBulkDenWat, t2enthalpy_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat, cEnthalpy/cBulkDenWat + if(abs(T2enthTemp_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat - cEnthalpy/cBulkDenWat) > eTol)then + write(*,'(a,1x,f12.5,1x,2(e20.10,1x))') 'enthalpy test', cBulkDenWat, T2enthTemp_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat, cEnthalpy/cBulkDenWat message=trim(message)//'problem with enthalpy-->temperature conversion' err=20; return end if diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 5fe197661..b07bf5233 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -160,8 +160,8 @@ subroutine systemSolv(& USE allocspace_module,only:allocLocal ! allocate local data structures ! state vector and solver USE getVectorz_module,only:getScaling ! get the scaling vectors - USE enthalpyTemp_module,only:t2enthalpy_snow ! convert temperature to enthalpy for a snow layer - USE enthalpyTemp_module,only:t2enthalpy ! compute enthalpy + USE enthalpyTemp_module,only:T2enthTemp_snow ! convert temperature to enthalpy for a snow layer + USE enthalpyTemp_module,only:T2enthTemp ! compute enthalpy #ifdef SUNDIALS_ACTIVE USE tol4ida_module,only:popTol4ida ! populate tolerances USE eval8summaWithPrime_module,only:eval8summaWithPrime ! get the fluxes and residuals @@ -272,9 +272,9 @@ subroutine systemSolv(& ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) ! enthalpy - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! intent(out): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthTemp => diag_data%var(iLookDIAG%mLayerEnthTemp)%dat ,& ! intent(out): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) ! derivatives, diagnostic for enthalpy dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature @@ -396,7 +396,7 @@ subroutine systemSolv(& if((ixNrgConserv.ne.closedForm .or. computNrgBalance) .and. ixNumericalMethod.ne.ida)then ! will need enthalpy change, compute H_T at the beginning of the data step - call t2enthalpy(& + call T2enthTemp(& ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -412,9 +412,9 @@ subroutine systemSolv(& mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) mLayerMatricHead, & ! intent(in): vector of total water matric potential (m) ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTemp, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthTemp, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -477,7 +477,7 @@ subroutine systemSolv(& if(nSnow>0)then ! compute the energy required to melt the top snow layer (J m-2) bulkDensity = mLayerVolFracIce(1)*iden_ice + mLayerVolFracLiq(1)*iden_water - volEnthalpy = t2enthalpy_snow(mLayerTemp(1),bulkDensity,snowfrz_scale) + volEnthalpy = T2enthTemp_snow(mLayerTemp(1),bulkDensity,snowfrz_scale) ! set flag and error codes for too much melt if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_cur)then tooMuchMelt = .true. diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 deleted file mode 100644 index dd1e85842..000000000 --- a/build/source/engine/t2enthalpy.f90 +++ /dev/null @@ -1,631 +0,0 @@ -! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington -! -! This file is part of SUMMA -! -! For more information see: http://www.ral.ucar.edu/projects/summa -! -! This program is free software: you can redistribute it and/or modify -! it under the terms of the GNU General Public License as published by -! the Free Software Foundation, either version 3 of the License, or -! (at your option) any later version. -! -! This program is distributed in the hope that it will be useful, -! but WITHOUT ANY WARRANTY; without even the implied warranty of -! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -! GNU General Public License for more details. -! -! You should have received a copy of the GNU General Public License -! along with this program. If not, see . - -module t2enthalpy_module - -! constants -USE multiconst, only: gravity, & ! gravitational acceleration (m s-1) - Tfreeze, & ! freezing point of water (K) - Cp_soil,Cp_water,Cp_ice,Cp_air,& ! specific heat of soil, water and ice (J kg-1 K-1) - iden_water,iden_ice,iden_air,& ! intrinsic density of water and ice (kg m-3) - LH_fus ! latent heat of fusion (J kg-1) - -! data types -USE nrtype -USE data_types,only:var_iLength ! var(:)%dat(:) -USE data_types,only:var_dLength ! var(:)%dat(:) -USE data_types,only:zLookup ! z(:)%var(:)%lookup(:) - -! indices within parameter structure -USE var_lookup,only:iLookPARAM ! named variables to define structure element -USE var_lookup,only:iLookINDEX ! named variables to define structure element -USE var_lookup,only:iLookLOOKUP ! named variables to define structure element -USE var_lookup,only:iLookDIAG ! named variables for structure elements - -! data dimensions -USE var_lookup,only:maxvarLookup ! maximum number of variables in the lookup tables - -! domain types -USE globalData,only:iname_cas ! named variables for canopy air space -USE globalData,only:iname_veg ! named variables for vegetation canopy -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil -USE globalData,only:iname_aquifer ! named variables for the aquifer - -! named variables to describe the state variable type -USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space -USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy -USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers - -! missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing real number - -! privacy -implicit none -private -public::T2E_lookup -public::t2enthalpy - -! define the look-up table used to compute temperature based on enthalpy -contains - - -! ************************************************************************************************************************ -! public subroutine T2E_lookup: define a look-up table to compute enthalpy based on temperature -! ************************************************************************************************************************ -subroutine T2E_lookup(nSoil, & ! intent(in): number of soil layers - mpar_data, & ! intent(in): parameter data structure - lookup_data, & ! intent(inout): lookup table data structure - err,message) - USE nr_utility_module,only:arth ! use to build vectors with regular increments - USE spline_int_module,only:spline,splint ! use for cubic spline interpolation - USE soil_utils_module,only:volFracLiq ! use to compute the volumetric fraction of liquid water - implicit none - ! declare dummy variables - integer(i4b),intent(in) :: nSoil - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(zLookup),intent(inout) :: lookup_data ! lookup tables - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! declare local variables - character(len=128) :: cmessage ! error message in downwind routine - logical(lgt),parameter :: doTest=.false. ! flag to test - integer(i4b),parameter :: nLook=100 ! number of elements in the lookup table - integer(i4b),parameter :: nIntegr8=10000 ! number of points used in the numerical integration - real(rkind),parameter :: T_lower=260.0_rkind ! lowest temperature value where all liquid water is assumed frozen (K) - real(rkind),dimension(nLook) :: xTemp ! temporary vector - real(rkind) :: xIncr ! temporary increment - real(rkind) :: T_incr ! temperature increment - real(rkind),parameter :: T_test=272.9742_rkind ! test value for temperature (K) - real(rkind) :: E_test ! test value for enthalpy (J m-3) - real(rkind) :: dE ! derivative of enthalpy with temperature at T_test - integer(i4b) :: iVar ! loop through variables - integer(i4b) :: iSoil ! loop through soil layers - integer(i4b) :: iLook ! loop through lookup table - integer(i4b) :: jIntegr8 ! index for numerical integration - logical(lgt) :: check ! flag to check allocation - real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) - real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) - real(rkind) :: volFracIce ! volumetric fraction of ice (-) - real(rkind) :: matricHead ! matric head (m) - ! initialize error control - err=0; message="T2E_lookup/" - - ! get the values of temperature for the lookup table - xIncr = 1._rkind/real(nLook-1, kind(rkind)) - xTemp = T_lower + (Tfreeze - T_lower)*sqrt(sqrt(arth(0._rkind,xIncr,nLook))) ! use sqrt(sqrt()) to give more values near freezing - - ! ----- - ! * allocate space for the lookup table... - ! ---------------------------------------- - - ! initialize checks - check=.false. - - ! allocate space for soil layers - if(allocated(lookup_data%z))then; check=.true.; else; allocate(lookup_data%z(nSoil), stat=err); endif - if(check) then; err=20; message=trim(message)//'lookup table z dimension was unexpectedly allocated already'; return; end if - if(err/=0)then; err=20; message=trim(message)//'problem allocating lookup table z dimension dimension'; return; end if - - ! allocate space for the variables in the lookup table - do iSoil=1,nSoil - if(allocated(lookup_data%z(iSoil)%var))then; check=.true.; else; allocate(lookup_data%z(iSoil)%var(maxvarLookup), stat=err); endif - if(check) then; err=20; message=trim(message)//'lookup table var dimension was unexpectedly allocated already'; return; end if - if(err/=0)then; err=20; message=trim(message)//'problem allocating lookup table var dimension dimension'; return; end if - - ! allocate space for the values in the lookup table - do iVar=1,maxvarLookup - if(allocated(lookup_data%z(iSoil)%var(iVar)%lookup))then; check=.true.; else; allocate(lookup_data%z(iSoil)%var(iVar)%lookup(nLook), stat=err); endif - if(check) then; err=20; message=trim(message)//'lookup table value dimension was unexpectedly allocated already'; return; end if - if(err/=0)then; err=20; message=trim(message)//'problem allocating lookup table vaule dimension dimension'; return; end if - - end do ! (looping through variables) - end do ! (looping through soil layers) - - ! loop through soil layers - do iSoil=1,nSoil - - ! ----- - ! * make association to variables in the data structures... - ! --------------------------------------------------------- - - associate(& - - ! associate model parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) , & ! scaling parameter for freezing (K-1) - soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(iSoil) , & ! intrinsic soil density (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(iSoil) , & ! soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(iSoil) , & ! volumetric residual water content (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(iSoil) , & ! van Genuchten "alpha" parameter (m-1) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(iSoil) , & ! van Genuchten "n" parameter (-) - - ! associate values in the lookup table - Tk => lookup_data%z(iSoil)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ey => lookup_data%z(iSoil)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) - E2 => lookup_data%z(iSoil)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function - - ) ! end associate statement - - ! compute vGn_m - vGn_m = 1._rkind - 1._rkind/vGn_n - - ! ----- - ! * populate the lookup table... - ! ------------------------------ - - ! initialize temperature and enthalpy - Tk(nLook) = Tfreeze - Ey(nLook) = 0._rkind - - ! loop through lookup table - do iLook=(nLook-1),1,-1 - - ! update temperature and enthalpy - Tk(iLook) = Tk(iLook+1) - Ey(iLook) = Ey(iLook+1) - - ! get the temperature increment for the numerical integration - T_incr = (xTemp(iLook)-xTemp(iLook+1))/real(nIntegr8, kind(rkind)) - - ! numerical integration between different values of the lookup table - do jIntegr8=1,nIntegr8 - - ! update temperature - Tk(iLook) = Tk(iLook) + T_incr - - ! compute the volumetric liquid water and ice content - ! NOTE: assume saturation - matricHead = (LH_fus/gravity)*(Tk(iLook) - Tfreeze)/Tfreeze - vFracLiq = volFracLiq(matricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - volFracIce = theta_sat - vFracLiq - - ! compute enthalpy - ! NOTE: assume intrrinsic density of ice is the intrinsic density of water - ! NOTE: kg m-3 J kg-1 K-1 K - Ey(iLook) = Ey(iLook) + iden_water*Cp_water*vFracLiq*T_incr + iden_water*Cp_ice*volFracIce*T_incr - - end do ! numerical integration - - end do ! loop through lookup table - - ! use cubic spline interpolation to obtain enthalpy values at the desired values of temperature - call spline(Tk,Ey,1.e30_rkind,1.e30_rkind,E2,err,cmessage) ! get the second derivatives - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! test - if(doTest)then - - ! calculate enthalpy - call splint(Tk,Ey,E2,T_test,E_test,dE,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! write values - print*, 'doTest = ', doTest - print*, 'T_test = ', T_test ! temperature (K) - print*, 'E_test = ', E_test ! enthalpy (J m-3) - print*, 'theta_sat = ', theta_sat ! soil porosity (-) - print*, 'theta_res = ', theta_res ! volumetric residual water content (-) - print*, 'vGn_alpha = ', vGn_alpha ! van Genuchten "alpha" parameter (m-1) - print*, 'vGn_n = ', vGn_n ! van Genuchten "n" parameter (-) - print*, trim(message)//'PAUSE: Set doTest=.false. to complete simulations' - read(*,*) - - endif ! if testing - - ! end asssociation to variables in the data structures - end associate - - end do ! (looping through soil layers) -end subroutine T2E_lookup - - -! ************************************************************************************************************************ -! public subroutine t2enthalpy: compute enthalpy from temperature and total water content -! ************************************************************************************************************************ -subroutine t2enthalpy(& - doPhase, & ! intent(in): logical flag to include phase change in enthalpy or not - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in) - ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy, & ! intent(out): enthalpy of each snow+soil layer (J m-3) - dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature - dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state - dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature - dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state - ! output: error control - err,message) ! intent(out): error control - ! ------------------------------------------------------------------------------------------------------------------------- - ! downwind routines - USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water - USE soil_utils_module,only:dTheta_dPsi ! derivative in the soil water characteristic (soil) - USE spline_int_module,only:splint ! use for cubic spline interpolation - implicit none - ! delare dummy variables - ! ------------------------------------------------------------------------------------------------------------------------- - ! input: decisions - logical(lgt),intent(in) :: doPhase ! logical flag to include phase change in enthalpy or not - ! input: data structures - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model indices - type(zLookup),intent(in) :: lookup_data ! lookup tables - ! input: state variables for the vegetation canopy - real(rkind),intent(in) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) - real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric fraction of Ice (-) - ! input: pre-computed derivatives - real(rkind),intent(in) :: dTheta_dTkCanopy ! derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: scalarFracLiqVeg ! fraction of canopy liquid water (-) - real(rkind),intent(in) :: mLayerdTheta_dTk(:) ! derivative of volumetric liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: mLayerFracLiqSnow(:) ! fraction of liquid water (-) - real(rkind),intent(in) :: dVolTot_dPsi0(:) ! derivative in total water content w.r.t. total water matric potential (m-1) - ! output: enthalpy - real(rkind),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - real(rkind),intent(out) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) - ! output: derivatives - real(rkind),intent(out) :: dCanEnthalpy_dTk ! derivatives in canopy enthalpy w.r.t. temperature - real(rkind),intent(out) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state - real(rkind),intent(out) :: dEnthalpy_dTk(:) ! derivatives in layer enthalpy w.r.t. temperature - real(rkind),intent(out) :: dEnthalpy_dWat(:) ! derivatives in layer enthalpy w.r.t. water state - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ------------------------------------------------------------------------------------------------------------------------- - ! declare local variables - character(len=128) :: cmessage ! error message in downwind routine - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixDomainType ! name of a given model domain - integer(i4b) :: ixControlIndex ! index within a given model domain - real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - real(rkind) :: psiLiq ! matric head of liquid water (m) - real(rkind) :: volFracWat ! volumetric fraction of total water, liquid+ice (-) - real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) - real(rkind) :: volFracIce ! volumetric fraction of ice (-) - real(rkind) :: diffT ! temperature difference from Tfreeze - real(rkind) :: integral ! integral of snow freezing curve - real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head - real(rkind) :: dVolFracLiq_dTk ! derivative of volumetric fraction of liquid water with temperature - real(rkind) :: d_integral_dTk ! derivative of integral with temperature - real(rkind) :: dE ! derivative of enthalpy with temperature at layer temperature - real(rkind) :: dEcrit ! derivative of enthalpy with temperature at critical temperature - ! enthalpy - real(rkind) :: enthVeg ! enthalpy of the vegetation (J m-3) - real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) - real(rkind) :: enthMix ! enthalpy of the mixed region, liquid+ice (J m-3) - real(rkind) :: enthLiq ! enthalpy of the liquid region (J m-3) - real(rkind) :: enthIce ! enthalpy of the ice region (J m-3) - real(rkind) :: enthAir ! enthalpy of air (J m-3) - real(rkind) :: enthTemp ! enthalpy at the temperature of the control volume (J m-3) - real(rkind) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) - real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) - real(rkind) :: enthWater ! enthalpy of total water (J m-3) - ! enthalpy derivatives - real(rkind) :: dEnthVeg_dTk ! derivative of enthalpy of the vegetation with temperature - real(rkind) :: dEnthSoil_dTk ! derivative of enthalpy of the soil with temperature - real(rkind) :: dEnthLiq_dTk ! derivative of enthalpy of the liquid with temperature - real(rkind) :: dEnthIce_dTk ! derivative of enthalpy of the ice with temperature - real(rkind) :: dEnthAir_dTk ! derivative of enthalpy of the air with temperature - real(rkind) :: dEnthPhase_dTk ! derivative of enthalpy of the phase change with temperature - real(rkind) :: dEnthWater_dTk ! derivative of enthalpy of the total water with temperature - real(rkind) :: dEnthVeg_dWat ! derivative of enthalpy of the vegetation with water state - real(rkind) :: dEnthSoil_dWat ! derivative of enthalpy of the soil with water state - real(rkind) :: dEnthLiq_dWat ! derivative of enthalpy of the liquid with water state - real(rkind) :: dEnthIce_dWat ! derivative of enthalpy of the ice with water state - real(rkind) :: dEnthAir_dWat ! derivative of enthalpy of the air with water state - real(rkind) :: dEnthPhase_dWat ! derivative of enthalpy of the phase change with water state - real(rkind) :: dEnthWater_dWat ! derivative of enthalpy of the total water with water state - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - generalVars: associate(& - ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers - ! mapping between the full state vector and the state subset - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset - ! type of domain, type of state variable, and index of control volume within domain - ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) ! end associate statement - ! -------------------------------------------------------------------------------------------------------------------------------- - - ! initialize error control - err=0; message="t2enthalpy/" - - ! loop through model state variables - do iState=1,size(ixMapSubset2Full) - - ! ----- - ! - compute indices... - ! -------------------- - - ! get domain type, and index of the control volume within the domain - ixFullVector = ixMapSubset2Full(iState) ! index within full state vector - ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) - ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain - - ! check an energy state - if(ixStateType(ixFullVector)==iname_nrgCanair .or. ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer)then - - ! get the layer index - select case(ixDomainType) - case(iname_cas); iLayer = integerMissing - case(iname_veg); iLayer = integerMissing - case(iname_snow); iLayer = ixControlIndex - case(iname_soil); iLayer = ixControlIndex + nSnow - case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) - case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return - end select - - ! Initialize - dEnthVeg_dTk = 0._rkind - dEnthSoil_dTk = 0._rkind - dEnthLiq_dTk = 0._rkind - dEnthIce_dTk = 0._rkind - dEnthAir_dTk = 0._rkind - dEnthPhase_dTk = 0._rkind - dEnthWater_dTk = 0._rkind - dEnthVeg_dWat = 0._rkind - dEnthSoil_dWat = 0._rkind - dEnthLiq_dWat = 0._rkind - dEnthIce_dWat = 0._rkind - dEnthAir_dWat = 0._rkind - dEnthPhase_dWat = 0._rkind - dEnthWater_dWat = 0._rkind - - ! identify domain - select case(ixDomainType) - case(iname_cas) - scalarCanairEnthalpy = Cp_air * iden_air * (scalarCanairTempTrial - Tfreeze) - - case(iname_veg) - ! association to necessary variables for vegetation - vegVars: associate(& - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): [dp] canopy depth (m) - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) - - diffT = scalarCanopyTempTrial - Tfreeze - enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth - ! enthalpy derivatives - dEnthVeg_dTk = specificHeatVeg * maxMassVegetation / canopyDepth - if(diffT>=0._rkind)then - enthLiq = Cp_water * scalarCanopyWatTrial * diffT / canopyDepth - enthIce = 0._rkind - enthPhase = 0._rkind - ! derivatives - dEnthLiq_dTk = Cp_water * scalarCanopyWatTrial / canopyDepth - dEnthLiq_dWat = Cp_water * diffT / canopyDepth - else - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth - enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth - enthPhase = LH_fus * scalarCanopyIceTrial / canopyDepth - ! derivatives - d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) - ! enthalpy derivatives - dEnthLiq_dTk = Cp_water * scalarCanopyWatTrial * d_integral_dTk / canopyDepth - dEnthIce_dTk = Cp_ice * scalarCanopyWatTrial * ( 1._rkind - d_integral_dTk ) / canopyDepth - dEnthPhase_dTk = -LH_fus * dTheta_dTkCanopy / canopyDepth ! dCanopyIce_dTk = -dTheta_dTkCanopy - dEnthLiq_dWat = Cp_water * integral / canopyDepth - dEnthIce_dWat = Cp_ice * ( diffT - integral ) / canopyDepth - dEnthPhase_dWat = LH_fus * ( 1._rkind - scalarFracLiqVeg ) / canopyDepth ! dCanopyIce_dWat = ( 1._rkind - scalarFracLiqVeg ) - endif - - scalarCanopyEnthalpy = enthVeg + enthLiq + enthIce - dCanEnthalpy_dTk = dEnthVeg_dTk + dEnthLiq_dTk + dEnthIce_dTk - dCanEnthalpy_dWat = dEnthVeg_dWat + dEnthLiq_dWat + dEnthIce_dWat - if (doPhase)then ! only need for calculating energy balance error so shouldn't need derivatives - scalarCanopyEnthalpy = scalarCanopyEnthalpy - enthPhase - dCanEnthalpy_dTk = dCanEnthalpy_dTk - dEnthPhase_dTk - dCanEnthalpy_dWat = dCanEnthalpy_dWat - dEnthPhase_dWat - endif - - end associate vegVars - - case(iname_snow) - - ! association to necessary variables for snow - snowVars: associate(& - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) - - diffT = mLayerTempTrial(iLayer) - Tfreeze - if(diffT>=0._rkind)then - enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * diffT - enthIce = 0._rkind - enthAir = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) ) * diffT - enthPhase = 0._rkind - ! enthalpy derivatives - dEnthLiq_dTk = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) - dEnthAir_dTk = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) ) - dEnthLiq_dWat = iden_water * Cp_water * diffT - dEnthAir_dWat = -iden_air * Cp_air * diffT - else - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral - enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) - enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) - enthPhase = iden_ice * LH_fus * mLayerVolFracIceTrial(iLayer) - ! derivatives - d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) - ! enthalpy derivatives - dEnthLiq_dTk = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * d_integral_dTk - dEnthIce_dTk = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( 1._rkind - d_integral_dTk ) - dEnthAir_dTk = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(1._rkind-d_integral_dTk) + d_integral_dTk ) ) - dEnthPhase_dTk = -iden_water * LH_fus * mLayerdTheta_dTk(iLayer) ! dVolFracIce_dTk = -mLayerdTheta_dTk(iLayer)*(iden_water/iden_ice) - dEnthLiq_dWat = iden_water * Cp_water * integral - dEnthIce_dWat = iden_water * Cp_ice * ( diffT - integral ) - dEnthAir_dWat = -iden_air * Cp_air * ( (iden_water/iden_ice)*(diffT-integral) + integral ) - dEnthPhase_dWat = iden_water * LH_fus * ( 1._rkind - mLayerFracLiqSnow(iLayer) )! dVolFracIce_dWat = ( 1._rkind - mLayerFracLiqSnow(iLayer) )*(iden_water/iden_ice) - endif - - mLayerEnthalpy(iLayer) = enthLiq + enthIce + enthAir - dEnthalpy_dTk(iLayer) = dEnthLiq_dTk + dEnthIce_dTk + dEnthAir_dTk - dEnthalpy_dWat(iLayer) = dEnthLiq_dWat + dEnthIce_dWat + dEnthAir_dWat - if (doPhase)then ! only need for calculating energy balance error so shouldn't need derivatives - mLayerEnthalpy(iLayer) = mLayerEnthalpy(iLayer) - enthPhase - dEnthalpy_dTk(iLayer) = dEnthalpy_dTk(iLayer) - dEnthPhase_dTk - dEnthalpy_dWat(iLayer) = dEnthalpy_dWat(iLayer) - dEnthPhase_dWat - endif - - end associate snowVars - - case(iname_soil) - - ! make association to variables in the data structures... - soilVars: associate(& - - ! associate model parameters - soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex) , & ! intrinsic soil density (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex) , & ! soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex) , & ! volumetric residual water content (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) - - ! associate values in the lookup table - Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) - E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function - - ) ! end associate statement - - ! diagnostic variables - vGn_m = 1._rkind - 1._rkind/vGn_n - Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) - volFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - diffT = mLayerTempTrial(iLayer) - Tfreeze - - ! *** compute enthalpy of water for unfrozen conditions - if(mlayerTempTrial(iLayer)>=Tcrit)then - enthWater = iden_water*Cp_water*volFracWat*diffT ! valid for temperatures below freezing also - enthPhase = 0._rkind - ! enthalpy derivatives - dEnthWater_dTk = iden_water*Cp_water*volFracWat - dEnthWater_dWat = iden_water*Cp_water*dVolTot_dPsi0(ixControlIndex) !dVolFracWat_dWat = dVolTot_dPsi0(ixControlIndex) - - ! *** compute enthalpy of water for frozen conditions - else - ! calculate enthalpy at the temperature (cubic spline interpolation) - call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),enthTemp,dE,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! calculate enthalpy at the critical temperature (cubic spline interpolation) - call splint(Tk,Ey,E2,Tcrit,enthTcrit,dEcrit,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! calculate the enthalpy of water - enthMix = enthTemp - enthTcrit ! enthalpy of the liquid+ice mix - enthLiq = iden_water*Cp_water*volFracWat*(Tcrit - Tfreeze) - enthWater = enthMix + enthLiq - - ! *** compute the enthalpy associated with phase change - psiLiq = diffT*LH_fus/(gravity*Tfreeze) - vFracLiq = volFracLiq(psiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - volFracIce = volFracWat - vFracLiq - enthPhase = iden_water*LH_fus*volFracIce - ! derivatives - dVolFracLiq_dTk = dTheta_dPsi(psiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m)*LH_fus/(gravity*Tfreeze) - dTcrit_dPsi0 = 0._rkind - if (mLayerMatricHeadTrial(ixControlIndex)<0._rkind) dTcrit_dPsi0 = gravity*Tfreeze/LH_fus - ! enthalpy derivatives - dEnthWater_dTk = dE - dEnthPhase_dTk = -iden_water*LH_fus*dVolFracLiq_dTk - dEnthWater_dWat = -dEcrit*dTcrit_dPsi0 + iden_water*Cp_water*dVolTot_dPsi0(ixControlIndex)*(Tcrit - Tfreeze) + iden_water*Cp_water*volFracWat*dTcrit_dPsi0 - dEnthPhase_dWat = iden_water*LH_fus*dVolTot_dPsi0(ixControlIndex) - endif ! (if frozen conditions) - - ! *** compute the enthalpy of soil - enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*diffT - ! enthalpy derivatives - dEnthSoil_dTk = soil_dens_intr*Cp_soil*(1._rkind - theta_sat) - - ! *** compute the enthalpy of air - enthAir = iden_air*Cp_air*(1._rkind - theta_sat - volFracWat)*diffT - ! enthalpy derivatives - dEnthAir_dTk = iden_air*Cp_air*(1._rkind - theta_sat - volFracWat) - dEnthAir_dWat = -iden_air*Cp_air*dVolTot_dPsi0(ixControlIndex)*diffT - - ! *** compute the total enthalpy (J m-3) - mLayerEnthalpy(iLayer) = enthSoil + enthWater + enthAir - dEnthalpy_dTk(iLayer) = dEnthSoil_dTk + dEnthWater_dTk + dEnthAir_dTk - dEnthalpy_dWat(iLayer) = dEnthSoil_dWat + dEnthWater_dWat + dEnthAir_dWat - if (doPhase)then ! only need for calculating energy balance error so shouldn't need derivatives - mLayerEnthalpy(iLayer) = mLayerEnthalpy(iLayer) - enthPhase - dEnthalpy_dTk(iLayer) = dEnthalpy_dTk(iLayer) - dEnthPhase_dTk - dEnthalpy_dWat(iLayer) = dEnthalpy_dWat(iLayer) - dEnthPhase_dWat - endif - - end associate soilVars - - ! ----- - ! - checks... - ! ----------- - case(iname_aquifer); cycle ! aquifer: do nothing - case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return - end select - - end if ! if an energy layer - end do ! looping through state variables - - end associate generalVars - -end subroutine t2enthalpy - -end module t2enthalpy_module diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index ff69b6fc9..a4f28cf0a 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -631,8 +631,8 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec USE updateVarsWithPrime_module,only:updateVarsWithPrime ! update prognostic variables #endif USE updateVars_module,only:updateVars ! update prognostic variables - USE enthalpyTemp_module,only:t2enthalpy ! compute enthalpy - USE enthalpyTemp_module,only:enthalpy2DeltaH ! add phase change terms to delta temperature component of enthalpy + USE enthalpyTemp_module,only:T2enthTemp ! compute enthalpy + USE enthalpyTemp_module,only:enthTemp2H ! add phase change terms to delta temperature component of enthalpy implicit none ! model control real(rkind) ,intent(in) :: dt ! time step (s) @@ -696,9 +696,9 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector of total water matric potential (m) real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector of liquid water matric potential (m) real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) - real(rkind) :: scalarCanairEnthalpyTrial ! trial value for temperature component of enthalpy of the canopy air space (J m-3) - real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for temperature component of enthalpy of the vegetation canopy (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector of temperature component of enthalpy of snow + soil (J m-3) + real(rkind) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3) + real(rkind) :: scalarCanopyEnthTempTrial ! trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthTempTrial ! trial vector of temperature component of enthalpy of snow + soil (J m-3) ! diagnostic variables real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) @@ -717,11 +717,11 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec real(rkind) :: scalarCanopyLiqPrime ! trial value for mass of liquid water on the vegetation canopy (kg m-2) real(rkind) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) real(rkind) :: scalarCanopyIceDelta ! delta value for mass of ice on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyHmixDelta ! delta value for mixture enthalpy of the vegetation canopy (J m-3) + real(rkind) :: scalarCanopyHDelta ! delta value for enthalpy of the vegetation canopy (J m-3) real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector of volumetric fraction of liquid water (-) real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector of volumetric fraction of ice (-) real(rkind),dimension(nLayers) :: mLayerVolFracIceDelta ! delta vector volumetric fraction of ice of snow + soil (-) - real(rkind),dimension(nLayers) :: mLayerHmixDelta ! delta vector of mixture enthalpy of snow + soil (J m-3) + real(rkind),dimension(nLayers) :: mLayerHDelta ! delta vector of enthalpy of snow+soil (J m-3) ! ------------------------------------------------------------------------------------------------------------------- ! ------------------------------------------------------------------------------------------------------------------- @@ -777,9 +777,9 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(inout): [dp(:)] matric potential of liquid water (m) ! enthalpy - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(inout): [dp] temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(inout): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(inout): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! intent(inout): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthTemp => diag_data%var(iLookDIAG%mLayerEnthTemp)%dat ,& ! intent(inout): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) ! model state variables (aquifer) scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(inout): [dp(:)] storage of water in the aquifer (m) ! error tolerance @@ -808,14 +808,14 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec scalarCanopyWatTrial = scalarCanopyWat scalarCanopyLiqTrial = scalarCanopyLiq scalarCanopyIceTrial = scalarCanopyIce - scalarCanopyEnthalpyTrial = scalarCanopyEnthalpy + scalarCanopyEnthTempTrial = scalarCanopyEnthTemp mLayerTempTrial = mLayerTemp mLayerVolFracWatTrial = mLayerVolFracWat mLayerVolFracLiqTrial = mLayerVolFracLiq mLayerVolFracIceTrial = mLayerVolFracIce mLayerMatricHeadTrial = mLayerMatricHead mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq - mLayerEnthalpyTrial = mLayerEnthalpy + mLayerEnthTempTrial = mLayerEnthTemp scalarAquiferStorageTrial = scalarAquiferStorage ! extract states from the state vector @@ -961,7 +961,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec !------------------------ if(computeEnthalpy)then ! update diagnostic enthalpy variables if needed ! compute enthalpy at t_{n+1} - call t2enthalpy(& + call T2enthTemp(& use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -977,9 +977,9 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) ! output: enthalpy - scalarCanairEnthalpyTrial, & ! intent(out): temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTempTrial, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthTempTrial, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -995,12 +995,12 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec scalarCanopyIceDelta = scalarCanopyIceTrial - scalarCanopyIce mLayerVolFracIceDelta = mLayerVolFracIceTrial - mLayerVolFracIce(1:nLayers) - ! initialize delta mixture enthalpy to delta temperature component of enthalpy, no difference in canopy air space - scalarCanopyHmixDelta = scalarCanopyEnthalpyTrial - scalarCanopyEnthalpy - mLayerHmixDelta = mLayerEnthalpyTrial - mLayerEnthalpy(1:nLayers) + ! initialize delta enthalpy (HDelta) to delta temperature component of enthalpy, no difference in canopy air space + scalarCanopyHDelta = scalarCanopyEnthTempTrial - scalarCanopyEnthTemp + mLayerHDelta = mLayerEnthTempTrial - mLayerEnthTemp(1:nLayers) ! compute mixture enthalpy for current values, do on delta value so only have to do once - call enthalpy2DeltaH(& + call enthTemp2H(& ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): model indices @@ -1008,18 +1008,18 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec scalarCanopyIceDelta, & ! intent(in): delta value for canopy ice content (kg m-2) mLayerVolFracIceDelta, & ! intent(in): delta vector of volumetric ice water content (-) ! input/output: enthalpy - scalarCanopyHmixDelta, & ! intent(inout): delta value for mixture enthalpy of the vegetation canopy (J m-3) - mLayerHmixDelta, & ! intent(inout): delta vector of mixture enthalpy of each snow+soil layer (J m-3) + scalarCanopyHDelta, & ! intent(inout): delta value for enthalpy of the vegetation canopy (J m-3) + mLayerHDelta, & ! intent(inout): delta vector of enthalpy of each snow+soil layer (J m-3) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! compute energy balance, maybe should use to check for step reduction if(ixCasNrg/=integerMissing) balance(ixCasNrg) = scalarCanairEnthalpyTrial - scalarCanairEnthalpy - fluxVec(ixCasNrg)*dt - if(ixVegNrg/=integerMissing) balance(ixVegNrg) = scalarCanopyHmixDelta - fluxVec(ixVegNrg)*dt + if(ixVegNrg/=integerMissing) balance(ixVegNrg) = scalarCanopyHDelta - fluxVec(ixVegNrg)*dt if(nSnowSoilNrg>0)then do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) - balance(ixSnowSoilNrg(i)) = mLayerHmixDelta(i) - fluxVec(ixSnowSoilNrg(i))*dt + balance(ixSnowSoilNrg(i)) = mLayerHDelta(i) - fluxVec(ixSnowSoilNrg(i))*dt enddo endif ! This is equivalent to above if, and only if, ixNrgConserv.ne.closedForm @@ -1310,8 +1310,8 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! * update enthalpy as a diagnostic variable... if computeEnthalpy is false this will not change ! -------------------------------- scalarCanairEnthalpy = scalarCanairEnthalpyTrial - scalarCanopyEnthalpy = scalarCanopyEnthalpyTrial - mLayerEnthalpy = mLayerEnthalpyTrial + scalarCanopyEnthTemp = scalarCanopyEnthTempTrial + mLayerEnthTemp = mLayerEnthTempTrial ! ----- ! * update prognostic variables... From 61901b424e2acef989b93b5b9c10819d5efa68b7 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 27 Feb 2024 13:06:57 +0900 Subject: [PATCH 1164/1472] utils --- utils/bal_per_GRU.py | 115 ++++++++++++++ utils/timeseries_to_statistics.py | 254 +++++++++++++++++------------- 2 files changed, 263 insertions(+), 106 deletions(-) create mode 100644 utils/bal_per_GRU.py diff --git a/utils/bal_per_GRU.py b/utils/bal_per_GRU.py new file mode 100644 index 000000000..deaeb1d18 --- /dev/null +++ b/utils/bal_per_GRU.py @@ -0,0 +1,115 @@ +# written by A. Van Beusekom (2023) + +## Visualize statistics per GRU +## Needs: +# SUMMA output statistics + +## Special note +# SUMMA simulations have been preprocessed into single value statistics per model element, using auxiliary scripts in ~/utils +# Run: +# python steps_per_GRU.py [stat] +# where stat is mean or amax + +# modules +import os +import matplotlib +import numpy as np +import xarray as xr +from pathlib import Path +import matplotlib.pyplot as plt +import copy +import pandas as pd + +viz_dir = Path('/home/avanb/scratch/statistics') + +testing = False +if testing: + stat = 'amax' + viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') + method_name=['be1'] #maybe make this an argument +else: + import sys + # The first input argument specifies the run where the files are + stat = sys.argv[1] + method_name=['be1','be1en','be1ln'] #maybe make this an argument + +# Simulation statistics file locations +balssets= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime'] + +viz_fil = method_name.copy() +viz_fl2 = method_name.copy() +for i, m in enumerate(method_name): + viz_fl2[i] = m + '_hrly_diff_bals_{}.nc' + viz_fl2[i] = viz_fl2[i].format(','.join(balssets)) + +# Specify variables of interest +plot_vars = ['balanceVegNrg','balanceSnowNrg','balanceSoilNrg','wallClockTime','balanceCasNrg'] +plot_vars2 = ['balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass'] + +plt_titl = ['(a) Vegetation Energy Balance','(b) Snow Energy Balance','(c) Soil Energy Balance', '(d) Wall Clock Time','e) Canopy Air Space Energy Balance'] +plt_titl2 = ['(a) Vegetation Mass Balance','(b) Snow Mass Balance','(c) Soil Mass Balance', '(d) Aquifer Mass Balance'] +leg_titl = ['$W~m^{-2}$', '$W~m^{-2}$','$W~m^{-2}$','$s$','$W~m^{-2}$'] +leg_titl2 = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}$','$kg~m^{-2}$'] +leg_titl0 = ['$W~m^{-2}$', '$W~m^{-2}$','$W~m^{-2}$','$num$','$W~m^{-2}$'] +leg_titl20 = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}$','$kg~m^{-2}$'] + +#fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' +#fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) +fig_fil = 'Balance_scat_{}_compressed.png' +fig_fil = fig_fil.format(stat) + +summa = {} +wall = {} +for i, m in enumerate(method_name): + # Get the aggregated statistics of SUMMA simulations + summa[m] = xr.open_dataset(viz_dir/viz_fl2[i]) + +##Figure + +if 'compressed' in fig_fil: + plt.rcParams.update({'font.size': 25}) +else: + plt.rcParams.update({'font.size': 100}) + +if 'compressed' in fig_fil: + fig,axs = plt.subplots(3,2,figsize=(35,38)) +else: + fig,axs = plt.subplots(3,2,figsize=(140,160)) +fig.subplots_adjust(hspace=0.24, wspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space +#fig.suptitle('Scatterplot of Hourly Statistics for each GRU', fontsize=40,y=1.0) + +def run_loop(i,var): + r = i//2 + c = i-r*2 + + # Data + for m in method_name: + s = summa[m][var].sel(stat='mean') + + if var == 'wallClockTime': + s0 = summa[m]['numberFluxCalc'].sel(stat='mean') + stat0_word = 'Mean Number Flux Calculations' + stat_word = 'Mean Wallclock Time' + else: + s0 = summa[m]['var'].sel(stat='amax') + stat0_word = 'Max Absolute Value' + stat_word = 'Mean Absolute Value' + + axs[r,c].scatter(x=s.values,y=s0.values,s=10,zorder=0,label=m) + + if stat == 'mean': word = ' mean' + if stat == 'amax': word = ' max' + + lgnd = axs[r,c].legend() + for j, m in enumerate(method_name): + lgnd.legendHandles[j]._sizes = [80] + axs[r,c].set_title(plt_titl[i]) + axs[r,c].set_xlabel(stat_word + word + ' [{}]'.format(leg_titl[i])) + axs[r,c].set_ylabel(stat0_word + word + ' [{}]'.format(leg_titl0[i])) + + +for i,var in enumerate(plot_vars): + run_loop(i,var) + +# Save +plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 98dd7543d..6a3931d48 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -31,6 +31,11 @@ not_parallel = False # should usually be false, runs faster testing = False +# which statistics to compute +do_vars = False +do_steps = False +do_balance = True + if testing: top_fold = '/Users/amedin/Research/USask/test_py/' method_name='be1' @@ -48,13 +53,17 @@ src_pat = 'run1_G*_timestep.nc' des_fil = method_name + '_hrly_diff_stats_{}_{}.nc' des_fl2 = method_name + '_hrly_diff_steps_{}_{}.nc' +des_fl3 = method_name + '_hrly_diff_bals_{}_{}.nc' settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] stepsets= ['numberStateSplit','numberDomainSplitNrg','numberDomainSplitMass','numberScalarSolutions','meanStepSize'] +balssets= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime'] viz_fil = method_name + '_hrly_diff_stats_{}.nc' viz_fil = viz_fil.format(','.join(settings)) viz_fl2 = method_name + '_hrly_diff_steps_{}.nc' viz_fl2 = viz_fl2.format(','.join(stepsets)) +viz_fl3 = method_name + '_hrly_diff_bals_{}.nc' +viz_fl3 = viz_fl2.format(','.join(balssets)) # Make sure we're dealing with the right kind of inputs src_dir = Path(src_dir) @@ -71,8 +80,10 @@ # Get the names of all inputs, assuming folders have same splits of domains and same file names src_files = glob.glob(str( src_dir / src_pat )) src_files.sort() -ben_files = glob.glob(str( ben_dir / src_pat )) -ben_files.sort() +if do_vars: + ben_files = glob.glob(str( ben_dir / src_pat )) + ben_files.sort() + # Load the list of files that have already been processed if os.path.exists(processed_files_path): @@ -84,7 +95,7 @@ # Filter out the files that have already been processed src_files = [f for f in src_files if f not in processed_files] -ben_files = [f for f in ben_files if f not in processed_files] +if do_vars: ben_files = [f for f in ben_files if f not in processed_files] # definitions for KGE computation def covariance(x,y,dims=None): @@ -93,19 +104,20 @@ def covariance(x,y,dims=None): def correlation(x,y,dims=None): return (covariance(x,y,dims)) / (x.std(dims) * y.std(dims)) -assert len(ben_files) == len(src_files), \ +if do_vars: + assert len(ben_files) == len(src_files), \ 'Found {} files but need {}!'.format(len(src_files), len(ben_files)) -# -- test for corruption -#for (file, bench) in zip(src_files,ben_files): -# # open file -# try: -# with xr.open_dataset(file), xr.open_dataset(bench) as ds: -# # Do nothing if the file is successfully opened -# pass -# except: -# # Log the file name or take other appropriate action if the file is corrupted -# print('Error opening file:', file, bench) + # -- test for corruption + #for (file, bench) in zip(src_files,ben_files): + # # open file + # try: + # with xr.open_dataset(file), xr.open_dataset(bench) as ds: + # # Do nothing if the file is successfully opened + # pass + # except: + # # Log the file name or take other appropriate action if the file is corrupted + # print('Error opening file:', file, bench) # -- functions def run_loop(file,bench,processed_files_path): @@ -115,20 +127,19 @@ def run_loop(file,bench,processed_files_path): # acquire the lock before opening the file if not_parallel: - dat, ben = xr.open_dataset(file), xr.open_dataset(bench) + dat = xr.open_dataset(file) + if do_vars: ben = xr.open_dataset(bench) else: import multiprocessing as mp lock = mp.Lock() with lock: - dat, ben = xr.open_dataset(file), xr.open_dataset(bench) + dat = xr.open_dataset(file), + if do_vars: ben = xr.open_dataset(bench) # sometimes gives -9999 the whole run (non-compute), make these nan and plot as lowest value 0 in geographic dat = dat.where(dat!=-9999) - ben = ben.where(ben!=-9999) # some weird negative values in runoff if not routed - dat['averageRoutedRunoff'] = dat['averageRoutedRunoff'].where(dat['averageRoutedRunoff']>=0) - ben['averageRoutedRunoff'] = ben['averageRoutedRunoff'].where(ben['averageRoutedRunoff']>=0) - + if do_vars: dat['averageRoutedRunoff'] = dat['averageRoutedRunoff'].where(dat['averageRoutedRunoff']>=0) # get rid of gru dimension, assuming hru and gru are one to one (everything now as hruId) dat = dat.drop_vars(['hruId','gruId']) m = dat.drop_dims('hru') @@ -137,94 +148,113 @@ def run_loop(file,bench,processed_files_path): dat = xr.merge([dat,m]) dat = dat.where(dat.time!=dat.time[0],drop=True) #first timestep weird - ben = ben.drop_vars(['hruId','gruId']) - m = ben.drop_dims('hru') - m = m.rename({'gru': 'hru'}) - ben = ben.drop_dims('gru') - ben = xr.merge([ben,m]) - ben = ben.where(ben.time!=ben.time[0],drop=True) #first timestep weird - - diff = dat - ben - the_hru = np.array(ben['hru']) - - for var in settings: - mean = dat[var].mean(dim='time') - mean = mean.expand_dims("stat").assign_coords(stat=("stat",["mean"])) - - datnz = dat[var].where(np.logical_and(ben[var] != 0,dat[var] != 0)) # don't include both 0 - mnnz = datnz.mean(dim='time') - mnnz = mnnz.expand_dims("stat").assign_coords(stat=("stat",["mnnz"])) - - mean_ben = ben[var].mean(dim='time') - mean_ben = mean_ben.expand_dims("stat").assign_coords(stat=("stat",["mean_ben"])) - - datnz = ben[var].where(np.logical_and(ben[var] != 0,dat[var] != 0)) # don't include both 0 - mnnz_ben = datnz.mean(dim='time') - mnnz_ben = mnnz_ben.expand_dims("stat").assign_coords(stat=("stat",["mnnz_ben"])) - - na_mx = np.fabs(dat[var]).max()+1 - amx = np.fabs(dat[var].fillna(na_mx)).argmax(dim=['time']) - amax = dat[var].isel(amx).drop_vars('time') - amax = amax.expand_dims("stat").assign_coords(stat=("stat",["amax"])) - - na_mx = np.fabs(ben[var]).max()+1 - amx = np.fabs(ben[var].fillna(na_mx)).argmax(dim=['time']) - amax_ben = ben[var].isel(amx).drop_vars('time') - amax_ben = amax_ben.expand_dims("stat").assign_coords(stat=("stat",["amax_ben"])) - - rmse = (np.square(diff[var]).mean(dim='time'))**(1/2) #RMSE SHOULD THIS BE NORMALIZED? colorbar will normalize - rmse = rmse.expand_dims("stat").assign_coords(stat=("stat",["rmse"])) - - diffnz = diff[var].where(np.logical_and(ben[var] != 0,dat[var] != 0)) # don't include both 0 - rmnz = (np.square(diffnz).mean(dim='time'))**(1/2) - rmnz = rmnz.expand_dims("stat").assign_coords(stat=("stat",["rmnz"])) - - na_mx = np.fabs(diff[var]).max()+1 - amx = np.fabs(diff[var].fillna(na_mx)).argmax(dim=['time']) - maxe = diff[var].isel(amx).drop_vars('time') - maxe = maxe.expand_dims("stat").assign_coords(stat=("stat",["maxe"])) - - r = correlation(dat[var],ben[var],dims='time') - kgem = 1 - np.sqrt( np.square(r-1) - + np.square( dat[var].std(dim='time')/ben[var].std(dim='time') - 1) - + np.square( (dat[var].mean(dim='time')-ben[var].mean(dim='time'))/ben[var].std(dim='time') ) ) - - #if constant and identical, want this as 1.0 -- correlation with a constant = 0 and std dev = 0\n", - for h in the_hru: - ss = dat[var].sel(hru=h) - tt = ben[var].sel(hru=h) - kgem.loc[h] =kgem.sel(hru=h).where(np.allclose(ss,tt, atol = 1e-10)==False, other=1.0) - kgem = kgem/(2.0-kgem) - kgem = kgem.expand_dims("stat").assign_coords(stat=("stat",["kgem"])) - - new = xr.merge([mean,mnnz,amax, mean_ben,mnnz_ben,amax_ben, rmse,rmnz, maxe, kgem]) - new.to_netcdf(des_dir / des_fil.format(var,subset)) - - for var in stepsets: - mean = dat[var].mean(dim='time') - mean = mean.expand_dims("stat").assign_coords(stat=("stat",["mean"])) - - na_mx = np.fabs(dat[var]).max()+1 - amx = np.fabs(dat[var].fillna(na_mx)).argmax(dim=['time']) - amax = dat[var].isel(amx).drop_vars('time') - amax = amax.expand_dims("stat").assign_coords(stat=("stat",["amax"])) - - new = xr.merge([mean,amax]) - new.to_netcdf(des_dir / des_fl2.format(var,subset)) + if do_vars: + ben = ben.where(ben!=-9999) + ben['averageRoutedRunoff'] = ben['averageRoutedRunoff'].where(ben['averageRoutedRunoff']>=0) + ben = ben.drop_vars(['hruId','gruId']) + m = ben.drop_dims('hru') + m = m.rename({'gru': 'hru'}) + ben = ben.drop_dims('gru') + ben = xr.merge([ben,m]) + ben = ben.where(ben.time!=ben.time[0],drop=True) #first timestep weird + + diff = dat - ben + the_hru = np.array(ben['hru']) + + # -- compute statistics + if do_vars: + for var in settings: + mean = dat[var].mean(dim='time') + mean = mean.expand_dims("stat").assign_coords(stat=("stat",["mean"])) + + datnz = dat[var].where(np.logical_and(ben[var] != 0,dat[var] != 0)) # don't include both 0 + mnnz = datnz.mean(dim='time') + mnnz = mnnz.expand_dims("stat").assign_coords(stat=("stat",["mnnz"])) + + mean_ben = ben[var].mean(dim='time') + mean_ben = mean_ben.expand_dims("stat").assign_coords(stat=("stat",["mean_ben"])) + + datnz = ben[var].where(np.logical_and(ben[var] != 0,dat[var] != 0)) # don't include both 0 + mnnz_ben = datnz.mean(dim='time') + mnnz_ben = mnnz_ben.expand_dims("stat").assign_coords(stat=("stat",["mnnz_ben"])) + + na_mx = np.fabs(dat[var]).max()+1 + amx = np.fabs(dat[var].fillna(na_mx)).argmax(dim=['time']) + amax = dat[var].isel(amx).drop_vars('time') + amax = amax.expand_dims("stat").assign_coords(stat=("stat",["amax"])) + + na_mx = np.fabs(ben[var]).max()+1 + amx = np.fabs(ben[var].fillna(na_mx)).argmax(dim=['time']) + amax_ben = ben[var].isel(amx).drop_vars('time') + amax_ben = amax_ben.expand_dims("stat").assign_coords(stat=("stat",["amax_ben"])) + + rmse = (np.square(diff[var]).mean(dim='time'))**(1/2) #RMSE SHOULD THIS BE NORMALIZED? colorbar will normalize + rmse = rmse.expand_dims("stat").assign_coords(stat=("stat",["rmse"])) + + diffnz = diff[var].where(np.logical_and(ben[var] != 0,dat[var] != 0)) # don't include both 0 + rmnz = (np.square(diffnz).mean(dim='time'))**(1/2) + rmnz = rmnz.expand_dims("stat").assign_coords(stat=("stat",["rmnz"])) + + na_mx = np.fabs(diff[var]).max()+1 + amx = np.fabs(diff[var].fillna(na_mx)).argmax(dim=['time']) + maxe = diff[var].isel(amx).drop_vars('time') + maxe = maxe.expand_dims("stat").assign_coords(stat=("stat",["maxe"])) + + r = correlation(dat[var],ben[var],dims='time') + kgem = 1 - np.sqrt( np.square(r-1) + + np.square( dat[var].std(dim='time')/ben[var].std(dim='time') - 1) + + np.square( (dat[var].mean(dim='time')-ben[var].mean(dim='time'))/ben[var].std(dim='time') ) ) + + #if constant and identical, want this as 1.0 -- correlation with a constant = 0 and std dev = 0\n", + for h in the_hru: + ss = dat[var].sel(hru=h) + tt = ben[var].sel(hru=h) + kgem.loc[h] =kgem.sel(hru=h).where(np.allclose(ss,tt, atol = 1e-10)==False, other=1.0) + kgem = kgem/(2.0-kgem) + kgem = kgem.expand_dims("stat").assign_coords(stat=("stat",["kgem"])) + + new = xr.merge([mean,mnnz,amax, mean_ben,mnnz_ben,amax_ben, rmse,rmnz, maxe, kgem]) + new.to_netcdf(des_dir / des_fil.format(var,subset)) + + if do_steps: + for var in stepsets: + mean = dat[var].mean(dim='time') + mean = mean.expand_dims("stat").assign_coords(stat=("stat",["mean"])) + + na_mx = np.fabs(dat[var]).max()+1 + amx = np.fabs(dat[var].fillna(na_mx)).argmax(dim=['time']) + amax = dat[var].isel(amx).drop_vars('time') + amax = amax.expand_dims("stat").assign_coords(stat=("stat",["amax"])) + + new = xr.merge([mean,amax]) + new.to_netcdf(des_dir / des_fl2.format(var,subset)) + + if do_balance: + for var in balssets: + mean = np.fabs(dat[var]).mean(dim='time') # this is actually absolute value mean + mean = mean.expand_dims("stat").assign_coords(stat=("stat",["mean"])) + + na_mx = np.fabs(dat[var]).max()+1 + amx = np.fabs(dat[var].fillna(na_mx)).argmax(dim=['time']) + amax = dat[var].isel(amx).drop_vars('time') + amax = amax.expand_dims("stat").assign_coords(stat=("stat",["amax"])) + + new = xr.merge([mean,amax]) + new.to_netcdf(des_dir / des_fl3.format(var,subset)) # write the name of the processed file to the file list, acquire the lock before opening the file if not_parallel: with open(processed_files_path, 'a') as filew: filew.write(file + '\n') - filew.write(bench + '\n') + if do_vars: filew.write(bench + '\n') else: import multiprocessing as mp lock = mp.Lock() with lock: with open(processed_files_path, 'a') as filew: filew.write(file + '\n') - filew.write(bench + '\n') + if do_vars: filew.write(bench + '\n') filew.close() # close the file after writing to it return #nothing @@ -250,8 +280,12 @@ def merge_subsets_into_one(src,pattern,des,name): if not_parallel: # -- no parallel processing - for (file, bench) in zip(src_files,ben_files): - run_loop(file,bench,processed_files_path) + if do_vars: + for (file, bench) in zip(src_files,ben_files): + run_loop(file,bench,processed_files_path) + else: + for (file, bench) in zip(src_files,src_files): + run_loop(file,bench,processed_files_path) # -- end no parallel processing else: @@ -261,7 +295,10 @@ def merge_subsets_into_one(src,pattern,des,name): import multiprocessing as mp pool = mp.Pool(processes=ncpus) with open(processed_files_path, 'a') as f: - results = [pool.apply_async(run_loop, args=(file,bench,processed_files_path)) for (file, bench) in zip(src_files, ben_files)] + if do_vars: + results = [pool.apply_async(run_loop, args=(file,bench,processed_files_path)) for (file, bench) in zip(src_files, ben_files)] + else: + results = [pool.apply_async(run_loop, args=(file,bench,processed_files_path)) for (file, bench) in zip(src_files, src_files)] for r in results: try: r.get() @@ -273,11 +310,16 @@ def merge_subsets_into_one(src,pattern,des,name): # merge the individual files into one for further vizualization -merge_subsets_into_one(des_dir,des_fil.replace('{}','*'),fnl_dir,viz_fil) -merge_subsets_into_one(des_dir,des_fl2.replace('{}','*'),fnl_dir,viz_fl2) - # remove the individual files for cleanliness -for file in glob.glob(str(des_dir / des_fil.replace('{}','*'))): - os.remove(file) -for file in glob.glob(str(des_dir / des_fl2.replace('{}','*'))): - os.remove(file) +if do_vars: + merge_subsets_into_one(des_dir,des_fil.replace('{}','*'),fnl_dir,viz_fil) + for file in glob.glob(str(des_dir / des_fil.replace('{}','*'))): + os.remove(file) +if do_steps: + merge_subsets_into_one(des_dir,des_fl2.replace('{}','*'),fnl_dir,viz_fl2) + for file in glob.glob(str(des_dir / des_fl2.replace('{}','*'))): + os.remove(file) +if do_balance: + merge_subsets_into_one(des_dir,des_fl3.replace('{}','*'),fnl_dir,viz_fl3) + for file in glob.glob(str(des_dir / des_fl3.replace('{}','*'))): + os.remove(file) \ No newline at end of file From 3536f194cca03dac6d6196a3b2f26f86ba5662ba Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 27 Feb 2024 21:48:47 +0900 Subject: [PATCH 1165/1472] utils --- utils/bal_per_GRU.py | 57 ++++++++++++++++++------------- utils/timeseries_to_statistics.py | 5 +-- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/utils/bal_per_GRU.py b/utils/bal_per_GRU.py index deaeb1d18..a62caae24 100644 --- a/utils/bal_per_GRU.py +++ b/utils/bal_per_GRU.py @@ -31,32 +31,37 @@ import sys # The first input argument specifies the run where the files are stat = sys.argv[1] - method_name=['be1','be1en','be1ln'] #maybe make this an argument + method_name=['be1','be1en','be1lu'] #maybe make this an argument # Simulation statistics file locations -balssets= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime'] +balssets= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime', 'numberFluxCalc', +'scaledBalanceCasNrg','scaledBalanceVegNrg','scaledBalanceSnowNrg','scaledBalanceSoilNrg','scaledBalanceVegMass','scaledBalanceSnowMass','scaledBalanceSoilMass','scaledBalanceAqMass'] viz_fil = method_name.copy() viz_fl2 = method_name.copy() for i, m in enumerate(method_name): viz_fl2[i] = m + '_hrly_diff_bals_{}.nc' - viz_fl2[i] = viz_fl2[i].format(','.join(balssets)) + viz_fl2 = viz_fl2.format(','.join(['balance','scaledBalance'])) # Specify variables of interest -plot_vars = ['balanceVegNrg','balanceSnowNrg','balanceSoilNrg','wallClockTime','balanceCasNrg'] -plot_vars2 = ['balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass'] +plot_vars = ['scaledBalanceCasNrg','scaledBalanceVegNrg','scaledBalanceSnowNrg','scaledBalanceSoilNrg','wallClockTime'] +plot_vars2 =['scaledBalanceVegMass','scaledBalanceSnowMass','scaledBalanceSoilMass','scaledBalanceAqMass'] +comp_vars = ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','numberFluxCald'] +comp_vars2 =['balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass'] -plt_titl = ['(a) Vegetation Energy Balance','(b) Snow Energy Balance','(c) Soil Energy Balance', '(d) Wall Clock Time','e) Canopy Air Space Energy Balance'] +plt_titl = ['(a) Canopy Air Space Energy Balance','(b) Vegetation Energy Balance','(c) Snow Energy Balance','(d) Soil Energy Balance', '(e) Wall Clock Time',] plt_titl2 = ['(a) Vegetation Mass Balance','(b) Snow Mass Balance','(c) Soil Mass Balance', '(d) Aquifer Mass Balance'] -leg_titl = ['$W~m^{-2}$', '$W~m^{-2}$','$W~m^{-2}$','$s$','$W~m^{-2}$'] -leg_titl2 = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}$','$kg~m^{-2}$'] -leg_titl0 = ['$W~m^{-2}$', '$W~m^{-2}$','$W~m^{-2}$','$num$','$W~m^{-2}$'] -leg_titl20 = ['$kg~m^{-2}$', '$kg~m^{-2}$','$kg~m^{-2}$','$kg~m^{-2}$'] +leg_titl0 = ['$s^{-1}$','$s^{-1}$','$s^{-1}$','$s^{-1}$','$s$'] +leg_titl20 =['$s^{-1}$','$s^{-1}$','$s^{-1}$','$s^{-1}$'] +leg_titl0 = ['$W~m^{-3}$','$W~m^{-3}$','$W~m^{-3}$','$W~m^{-3}$','$num$'] +leg_titl20 =['$kg~m^{-2}~s^{-1}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}~s^{-1}$'] #fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' #fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) -fig_fil = 'Balance_scat_{}_compressed.png' +fig_fil = 'BalanceNrg_scat_{}_compressed.png' fig_fil = fig_fil.format(stat) +fig_fil2 ='BalanceMass_scat_{}_compressed.png' +fig_fil2 =fig_fil2.format(stat) summa = {} wall = {} @@ -78,22 +83,22 @@ fig.subplots_adjust(hspace=0.24, wspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space #fig.suptitle('Scatterplot of Hourly Statistics for each GRU', fontsize=40,y=1.0) -def run_loop(i,var): +def run_loop(i,var,comp,leg_t,leg_t0,plt_t): r = i//2 c = i-r*2 # Data for m in method_name: - s = summa[m][var].sel(stat='mean') + s = summa[m][var].sel(stat=stat) + s0 = summa[m][comp].sel(stat=stat) if var == 'wallClockTime': - s0 = summa[m]['numberFluxCalc'].sel(stat='mean') - stat0_word = 'Mean Number Flux Calculations' - stat_word = 'Mean Wallclock Time' + stat0_word = 'Number flux calculations' + stat_word = 'Wallclock time' else: s0 = summa[m]['var'].sel(stat='amax') - stat0_word = 'Max Absolute Value' - stat_word = 'Mean Absolute Value' + stat0_word = 'Absolute value' + stat_word = 'Scaled by state (absolute value)' axs[r,c].scatter(x=s.values,y=s0.values,s=10,zorder=0,label=m) @@ -103,13 +108,19 @@ def run_loop(i,var): lgnd = axs[r,c].legend() for j, m in enumerate(method_name): lgnd.legendHandles[j]._sizes = [80] - axs[r,c].set_title(plt_titl[i]) - axs[r,c].set_xlabel(stat_word + word + ' [{}]'.format(leg_titl[i])) - axs[r,c].set_ylabel(stat0_word + word + ' [{}]'.format(leg_titl0[i])) + axs[r,c].set_title(plt_t) + axs[r,c].set_xlabel(stat_word + word + ' [{}]'.format(leg_t)) + axs[r,c].set_ylabel(stat0_word + word + ' [{}]'.format(leg_t0)) -for i,var in enumerate(plot_vars): - run_loop(i,var) +for i,(var,comp,leg_t,leg_t0,plt_t) in enumerate(zip(plot_vars,comp_vars,leg_titl0,leg_titl0,plt_titl)): + run_loop(i,var,comp,leg_t,leg_t0,plt_t) # Save plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) + +for i,(var,comp,leg_t,leg_t0,plt_t) in enumerate(zip(plot_vars2,comp_vars2,leg_titl20,leg_titl20,plt_titl2)): + run_loop(i,var,comp,leg_t,leg_t0,plt_t) + +# Save +plt.savefig(viz_dir/fig_fil2, bbox_inches='tight', transparent=False) diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 6a3931d48..4e31c04a1 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -56,14 +56,15 @@ des_fl3 = method_name + '_hrly_diff_bals_{}_{}.nc' settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] stepsets= ['numberStateSplit','numberDomainSplitNrg','numberDomainSplitMass','numberScalarSolutions','meanStepSize'] -balssets= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime'] +balssets= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime', 'numberFluxCalc', +'scaledBalanceCasNrg','scaledBalanceVegNrg','scaledBalanceSnowNrg','scaledBalanceSoilNrg','scaledBalanceVegMass','scaledBalanceSnowMass','scaledBalanceSoilMass','scaledBalanceAqMass'] viz_fil = method_name + '_hrly_diff_stats_{}.nc' viz_fil = viz_fil.format(','.join(settings)) viz_fl2 = method_name + '_hrly_diff_steps_{}.nc' viz_fl2 = viz_fl2.format(','.join(stepsets)) viz_fl3 = method_name + '_hrly_diff_bals_{}.nc' -viz_fl3 = viz_fl2.format(','.join(balssets)) +viz_fl3 = viz_fl3.format(','.join(['balance','scaledBalance'])) # Make sure we're dealing with the right kind of inputs src_dir = Path(src_dir) From 67ed30ede7da8dae9acd339fc9ff857fdb629128 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 28 Feb 2024 03:52:56 -0600 Subject: [PATCH 1166/1472] Added classes in the data_types module to simplify the call to summaSolve4numrec in systemSolv. --- build/source/dshare/data_types.f90 | 68 ++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 061317e1b..954806805 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -682,7 +682,7 @@ MODULE data_types end type out_type_computJacob type, public :: in_type_lineSearchRefinement ! class for intent(in) arguments in lineSearchRefinement call - logical(lgt) :: doSearch ! intent(in): flag to do the line search + logical(lgt) :: doSearch ! intent(in): flag to do the line search real(rkind) :: fOld ! intent(in): old function value contains procedure :: initialize => initialize_in_lineSearchRefinement @@ -698,6 +698,46 @@ MODULE data_types procedure :: finalize => finalize_out_lineSearchRefinement end type out_type_lineSearchRefinement + ! *********************************************************************************************************** + ! Define classes used to simplify calls to the subrotuines in systemSolv + ! *********************************************************************************************************** + + type, public :: in_type_summaSolve4numrec ! class for intent(in) arguments in summaSolve4numrec call + real(rkind) :: dt_cur ! intent(in): current stepsize + real(rkind) :: dt ! intent(in): entire time step for drainage pond rate + integer(i4b) :: iter ! intent(in): iteration index + integer(i4b) :: nSnow ! intent(in): number of snow layers + integer(i4b) :: nSoil ! intent(in): number of soil layers + integer(i4b) :: nLayers ! intent(in): total number of layers + integer(i4b) :: nLeadDim ! intent(in): length of the leading dimension of the Jacobian matrix (nBands or nState) + integer(i4b) :: nState ! intent(in): total number of state variables + integer(i4b) :: ixMatrix ! intent(in): type of matrix (full or band diagonal) + logical(lgt) :: firstSubStep ! intent(in): flag to indicate if we are processing the first sub-step + logical(lgt) :: computeVegFlux ! intent(in): flag to indicate if computing fluxes over vegetation + logical(lgt) :: scalarSolution ! intent(in): flag to denote if implementing the scalar solution + real(rkind) :: fOld ! intent(in): old function evaluation + contains + procedure :: initialize => initialize_in_summaSolve4numrec + end type in_type_summaSolve4numrec + + type, public :: io_type_summaSolve4numrec ! class for intent(inout) arguments in summaSolve4numrec call + logical(lgt) :: firstFluxCall ! intent(inout): flag to indicate if we are processing the first flux call + real(rkind) :: xMin,xMax ! intent(inout): brackets of the root + integer(i4b) :: ixSaturation ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + contains + procedure :: initialize => initialize_io_summaSolve4numrec + procedure :: finalize => finalize_io_summaSolve4numrec + end type io_type_summaSolve4numrec + + type, public :: out_type_summaSolve4numrec ! class for intent(out) arguments in summaSolve4numrec call + real(rkind) :: fNew ! intent(out): new function evaluation + logical(lgt) :: converged ! intent(out): convergence flag + integer(i4b) :: err ! intent(out): error code + character(len=len_msg) :: message ! intent(out): error message + contains + procedure :: finalize => finalize_out_summaSolve4numrec + end type out_type_summaSolve4numrec + contains ! **** vegNrgFlux **** @@ -1492,7 +1532,7 @@ end subroutine finalize_out_computJacob ! **** lineSearchRefinement **** subroutine initialize_in_lineSearchRefinement(in_lineSearchRefinement,doSearch,fOld) - class(in_type_lineSearchRefinement),intent(out) :: in_lineSearchRefinement ! class object for intent(out) computJacob arguments + class(in_type_lineSearchRefinement),intent(out) :: in_lineSearchRefinement ! class object for intent(out) arguments logical(lgt),intent(in) :: doSearch ! intent(in): flag to do the line search real(rkind) ,intent(in) :: fOld ! intent(in): old function value in_lineSearchRefinement % doSearch = doSearch ! intent(in): flag to do the line search @@ -1500,7 +1540,7 @@ subroutine initialize_in_lineSearchRefinement(in_lineSearchRefinement,doSearch,f end subroutine initialize_in_lineSearchRefinement subroutine finalize_out_lineSearchRefinement(out_lineSearchRefinement,fNew,converged,err,message) - class(out_type_lineSearchRefinement),intent(in) :: out_lineSearchRefinement ! class object for intent(out) computJacob arguments + class(out_type_lineSearchRefinement),intent(in) :: out_lineSearchRefinement ! class object for intent(out) arguments real(rkind) ,intent(out) :: fNew ! intent(out): new function evaluation logical(lgt),intent(out) :: converged ! intent(out): convergence flag integer(i4b),intent(out) :: err ! intent(out): error code @@ -1511,4 +1551,26 @@ subroutine finalize_out_lineSearchRefinement(out_lineSearchRefinement,fNew,conve message = out_lineSearchRefinement % message ! intent(out): error message end subroutine finalize_out_lineSearchRefinement + ! **** summaSolve4numrec **** + + subroutine initialize_in_summaSolve4numrec(in_SS4NR) + class(in_type_summaSolve4numrec),intent(out) :: in_SS4NR ! class object for intent(out) arguments + + end subroutine initialize_in_summaSolve4numrec + + subroutine initialize_io_summaSolve4numrec(io_SS4NR) + class(io_type_summaSolve4numrec),intent(out) :: io_SS4NR ! class object for intent(inout) arguments + + end subroutine initialize_io_summaSolve4numrec + + subroutine finalize_io_summaSolve4numrec(io_SS4NR) + class(io_type_summaSolve4numrec),intent(in) :: io_SS4NR ! class object for intent(inout) arguments + + end subroutine finalize_io_summaSolve4numrec + + subroutine finalize_out_summaSolve4numrec(out_SS4NR) + class(out_type_summaSolve4numrec),intent(in) :: out_SS4NR ! class object for intent(inout) arguments + + end subroutine finalize_out_summaSolve4numrec + END MODULE data_types From 0af20a618f68e10aadc8ec4258fd79fedd81de98 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 29 Feb 2024 01:44:41 -0600 Subject: [PATCH 1167/1472] Created class procedures for classes related to summaSolve4numrec subroutine arguments. --- build/source/dshare/data_types.f90 | 66 ++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 954806805..374a9e8c9 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -1553,24 +1553,72 @@ end subroutine finalize_out_lineSearchRefinement ! **** summaSolve4numrec **** - subroutine initialize_in_summaSolve4numrec(in_SS4NR) + subroutine initialize_in_summaSolve4numrec(in_SS4NR,dt_cur,dt,iter,nSnow,nSoil,nLayers,nLeadDim,nState,ixMatrix,firstSubStep,computeVegFlux,scalarSolution,fOld) class(in_type_summaSolve4numrec),intent(out) :: in_SS4NR ! class object for intent(out) arguments - + real(rkind) ,intent(in) :: dt_cur ! intent(in): current stepsize + real(rkind) ,intent(in) :: dt ! intent(in): entire time step for drainage pond rate + integer(i4b),intent(in) :: iter ! intent(in): iteration index + integer(i4b),intent(in) :: nSnow ! intent(in): number of snow layers + integer(i4b),intent(in) :: nSoil ! intent(in): number of soil layers + integer(i4b),intent(in) :: nLayers ! intent(in): total number of layers + integer(i4b),intent(in) :: nLeadDim ! intent(in): length of the leading dimension of the Jacobian matrix (nBands or nState) + integer(i4b),intent(in) :: nState ! intent(in): total number of state variables + integer(i4b),intent(in) :: ixMatrix ! intent(in): type of matrix (full or band diagonal) + logical(lgt),intent(in) :: firstSubStep ! intent(in): flag to indicate if we are processing the first sub-step + logical(lgt),intent(in) :: computeVegFlux ! intent(in): flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: scalarSolution ! intent(in): flag to denote if implementing the scalar solution + real(rkind) ,intent(in) :: fOld ! intent(in): old function evaluation + + in_SS4NR % dt_cur = dt_cur + in_SS4NR % dt = dt + in_SS4NR % iter = iter + in_SS4NR % nSnow = nSnow + in_SS4NR % nSoil = nSoil + in_SS4NR % nLayers = nLayers + in_SS4NR % nLeadDim = nLeadDim + in_SS4NR % nState = nState + in_SS4NR % ixMatrix = ixMatrix + in_SS4NR % firstSubStep = firstSubStep + in_SS4NR % computeVegFlux = computeVegFlux + in_SS4NR % scalarSolution = scalarSolution + in_SS4NR % fOld = fOld end subroutine initialize_in_summaSolve4numrec - subroutine initialize_io_summaSolve4numrec(io_SS4NR) + subroutine initialize_io_summaSolve4numrec(io_SS4NR,firstFluxCall,xMin,xMax,ixSaturation) class(io_type_summaSolve4numrec),intent(out) :: io_SS4NR ! class object for intent(inout) arguments - + logical(lgt),intent(in) :: firstFluxCall ! intent(inout): flag to indicate if we are processing the first flux call + real(rkind) ,intent(in) :: xMin,xMax ! intent(inout): brackets of the root + integer(i4b),intent(in) :: ixSaturation ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + + io_SS4NR % firstFluxCall = firstFluxCall + io_SS4NR % xMin = xMin + io_SS4NR % xMax = xMax + io_SS4NR % ixSaturation = ixSaturation end subroutine initialize_io_summaSolve4numrec - subroutine finalize_io_summaSolve4numrec(io_SS4NR) + subroutine finalize_io_summaSolve4numrec(io_SS4NR,firstFluxCall,xMin,xMax,ixSaturation) class(io_type_summaSolve4numrec),intent(in) :: io_SS4NR ! class object for intent(inout) arguments - + logical(lgt),intent(out) :: firstFluxCall ! intent(inout): flag to indicate if we are processing the first flux call + real(rkind) ,intent(out) :: xMin,xMax ! intent(inout): brackets of the root + integer(i4b),intent(out) :: ixSaturation ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + + firstFluxCall = io_SS4NR % firstFluxCall + xMin = io_SS4NR % xMin + xMax = io_SS4NR % xMax + ixSaturation = io_SS4NR % ixSaturation end subroutine finalize_io_summaSolve4numrec - subroutine finalize_out_summaSolve4numrec(out_SS4NR) - class(out_type_summaSolve4numrec),intent(in) :: out_SS4NR ! class object for intent(inout) arguments - + subroutine finalize_out_summaSolve4numrec(out_SS4NR,fNew,converged,err,message) + class(out_type_summaSolve4numrec),intent(in) :: out_SS4NR ! class object for intent(out) arguments + real(rkind) ,intent(out) :: fNew ! intent(out): new function evaluation + logical(lgt),intent(out) :: converged ! intent(out): convergence flag + integer(i4b),intent(out) :: err ! intent(out): error code + character(*),intent(out) :: message ! intent(out): error message + + fNew = out_SS4NR % fNew + converged = out_SS4NR % converged + err = out_SS4NR % err + message = out_SS4NR % message end subroutine finalize_out_summaSolve4numrec END MODULE data_types From 074fcf3e7b917d51b9fb62689a5ce6d9b5bbf06e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 29 Feb 2024 17:45:03 +0900 Subject: [PATCH 1168/1472] utils --- utils/bal_per_GRU.py | 45 +++++++++++++++++++------------ utils/timeseries_to_statistics.py | 2 +- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/utils/bal_per_GRU.py b/utils/bal_per_GRU.py index a62caae24..6e6e9bb03 100644 --- a/utils/bal_per_GRU.py +++ b/utils/bal_per_GRU.py @@ -24,9 +24,9 @@ testing = False if testing: - stat = 'amax' + stat = 'mean' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be1'] #maybe make this an argument + method_name=['be1lu'] #maybe make this an argument else: import sys # The first input argument specifies the run where the files are @@ -41,12 +41,12 @@ viz_fl2 = method_name.copy() for i, m in enumerate(method_name): viz_fl2[i] = m + '_hrly_diff_bals_{}.nc' - viz_fl2 = viz_fl2.format(','.join(['balance','scaledBalance'])) + viz_fl2[i] = viz_fl2[i].format(','.join(['balance','scaledBalance'])) # Specify variables of interest plot_vars = ['scaledBalanceCasNrg','scaledBalanceVegNrg','scaledBalanceSnowNrg','scaledBalanceSoilNrg','wallClockTime'] plot_vars2 =['scaledBalanceVegMass','scaledBalanceSnowMass','scaledBalanceSoilMass','scaledBalanceAqMass'] -comp_vars = ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','numberFluxCald'] +comp_vars = ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','numberFluxCalc'] comp_vars2 =['balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass'] plt_titl = ['(a) Canopy Air Space Energy Balance','(b) Vegetation Energy Balance','(c) Snow Energy Balance','(d) Soil Energy Balance', '(e) Wall Clock Time',] @@ -71,18 +71,6 @@ ##Figure -if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 25}) -else: - plt.rcParams.update({'font.size': 100}) - -if 'compressed' in fig_fil: - fig,axs = plt.subplots(3,2,figsize=(35,38)) -else: - fig,axs = plt.subplots(3,2,figsize=(140,160)) -fig.subplots_adjust(hspace=0.24, wspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space -#fig.suptitle('Scatterplot of Hourly Statistics for each GRU', fontsize=40,y=1.0) - def run_loop(i,var,comp,leg_t,leg_t0,plt_t): r = i//2 c = i-r*2 @@ -96,7 +84,6 @@ def run_loop(i,var,comp,leg_t,leg_t0,plt_t): stat0_word = 'Number flux calculations' stat_word = 'Wallclock time' else: - s0 = summa[m]['var'].sel(stat='amax') stat0_word = 'Absolute value' stat_word = 'Scaled by state (absolute value)' @@ -112,13 +99,37 @@ def run_loop(i,var,comp,leg_t,leg_t0,plt_t): axs[r,c].set_xlabel(stat_word + word + ' [{}]'.format(leg_t)) axs[r,c].set_ylabel(stat0_word + word + ' [{}]'.format(leg_t0)) +if 'compressed' in fig_fil: + plt.rcParams.update({'font.size': 25}) +else: + plt.rcParams.update({'font.size': 100}) +if 'compressed' in fig_fil: + fig,axs = plt.subplots(3,2,figsize=(35,38)) +else: + fig,axs = plt.subplots(3,2,figsize=(140,160)) +fig.subplots_adjust(hspace=0.24, wspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space +#fig.suptitle('Scatterplot of Hourly Statistics for each GRU', fontsize=40,y=1.0) + for i,(var,comp,leg_t,leg_t0,plt_t) in enumerate(zip(plot_vars,comp_vars,leg_titl0,leg_titl0,plt_titl)): run_loop(i,var,comp,leg_t,leg_t0,plt_t) # Save plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) + +if 'compressed' in fig_fil: + plt.rcParams.update({'font.size': 25}) +else: + plt.rcParams.update({'font.size': 100}) + +if 'compressed' in fig_fil: + fig,axs = plt.subplots(3,2,figsize=(35,38)) +else: + fig,axs = plt.subplots(3,2,figsize=(140,160)) +fig.subplots_adjust(hspace=0.24, wspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space +#fig.suptitle('Scatterplot of Hourly Statistics for each GRU', fontsize=40,y=1.0) + for i,(var,comp,leg_t,leg_t0,plt_t) in enumerate(zip(plot_vars2,comp_vars2,leg_titl20,leg_titl20,plt_titl2)): run_loop(i,var,comp,leg_t,leg_t0,plt_t) diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 4e31c04a1..6d6d6901a 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -38,7 +38,7 @@ if testing: top_fold = '/Users/amedin/Research/USask/test_py/' - method_name='be1' + method_name='be1lu' not_parallel = True else: import sys From 010e8a1fdcb5925ddc6eb9b987eacd37c14a4f27 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 1 Mar 2024 16:25:48 +0900 Subject: [PATCH 1169/1472] update docs and utils --- build/cmake_external/build_cmakeSundials.bash | 2 +- {build/cmake => docs}/README_ngen.md | 14 ++- {build/cmake => docs}/README_not_ngen.md | 14 ++- docs/installation/SUMMA_docker.md | 33 ------ docs/installation/SUMMA_installation.md | 101 ------------------ docs/installation/SUMMA_on_OS_X.md | 66 ------------ docs/installation/SUMMA_test_cases.md | 5 - docs/sundials_bmi/bmi_interface.txt | 10 -- docs/sundials_bmi/installation.txt | 74 ------------- docs/sundials_bmi_flags/bmi_interface.txt | 38 +++++++ .../flags_params_sundials.txt | 0 utils/bal_per_GRU.py | 16 +-- utils/timeseries_to_statistics.py | 4 +- 13 files changed, 70 insertions(+), 307 deletions(-) rename {build/cmake => docs}/README_ngen.md (90%) rename {build/cmake => docs}/README_not_ngen.md (81%) delete mode 100644 docs/installation/SUMMA_docker.md delete mode 100644 docs/installation/SUMMA_installation.md delete mode 100755 docs/installation/SUMMA_on_OS_X.md delete mode 100644 docs/installation/SUMMA_test_cases.md delete mode 100644 docs/sundials_bmi/bmi_interface.txt delete mode 100644 docs/sundials_bmi/installation.txt create mode 100644 docs/sundials_bmi_flags/bmi_interface.txt rename docs/{sundials_bmi => sundials_bmi_flags}/flags_params_sundials.txt (100%) diff --git a/build/cmake_external/build_cmakeSundials.bash b/build/cmake_external/build_cmakeSundials.bash index 5cb765d40..d56903efe 100755 --- a/build/cmake_external/build_cmakeSundials.bash +++ b/build/cmake_external/build_cmakeSundials.bash @@ -6,4 +6,4 @@ # run `make`, then `make install` # Note, using -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF, if want to run examples should change -cmake ../../sundials-6.6.0/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=../../sundials/instdir -DEXAMPLES_INSTALL_PATH=../../sundials/instdir/examples +cmake ../../sundials-software/ -DEXAMPLES_ENABLE_C=OFF -DEXAMPLES_ENABLE_F2003=OFF -DBUILD_FORTRAN_MODULE_INTERFACE=ON -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_INSTALL_PREFIX=../../sundials/instdir -DEXAMPLES_INSTALL_PATH=../../sundials/instdir/examples diff --git a/build/cmake/README_ngen.md b/docs/README_ngen.md similarity index 90% rename from build/cmake/README_ngen.md rename to docs/README_ngen.md index 85daf9f8e..5cc0e2afc 100644 --- a/build/cmake/README_ngen.md +++ b/docs/README_ngen.md @@ -52,13 +52,19 @@ First, cd into the outer directory containing the submodule: cd extern/summa If you want to use Sundials IDA or BE Kinsol, set -DCMAKE_BUILD_TYPE=Sundials_NexGen in the build script. Then, before summa can be built, Sundials needs to be installed. -Download the latest release of IDA solver from SUNDIALS package in https://computing.llnl.gov/projects/sundials/sundials-software +Download the latest release of IDA solver from SUNDIALS package in https://computing.llnl.gov/projects/sundials/sundials-software, using X.Y.Z as the latest version - wget "https://github.com/LLNL/sundials/releases/download/v6.3.0/sundials-6.3.0.tar.gz" + wget "https://github.com/LLNL/sundials/releases/download/vX.Y.Z/sundials-X.Y.Z.tar.gz" -Extract the corresponding compressed file +Extract the corresponding compressed file, rename - tar -xzf sundials-6.3.0.tar.gz + tar -xzf sundials-X.Y.Z.tar.gz && mv sundials-X.Y.Z sundials-software + +We suggest you periodically update to the latest version-- you can also install through github + git clone https://github.com/LLNL/sundials.git sundials-software + cd sundials-software + git fetch --all --tags --prune + git checkout tags/vX.Y.Z An example build_cmake file is at summa/build/cmake_external/build_cmakeSundials.bash which you can copy to builddir as build_cmake. Then, enter the buildir and run diff --git a/build/cmake/README_not_ngen.md b/docs/README_not_ngen.md similarity index 81% rename from build/cmake/README_not_ngen.md rename to docs/README_not_ngen.md index 8ad5ed77d..bd0d59057 100644 --- a/build/cmake/README_not_ngen.md +++ b/docs/README_not_ngen.md @@ -32,13 +32,19 @@ or with Actors: cd summa/build/summa/build/cmake If you want to use Sundials IDA or BE Kinsol, set -DCMAKE_BUILD_TYPE=Sundials* in the build script instead of BE*. Then, before summa can be built, Sundials needs to be installed. -Download the latest release of IDA solver from SUNDIALS package in https://computing.llnl.gov/projects/sundials/sundials-software +Download the latest release of IDA solver from SUNDIALS package in https://computing.llnl.gov/projects/sundials/sundials-software, using X.Y.Z as the latest version - wget "https://github.com/LLNL/sundials/releases/download/v6.3.0/sundials-6.3.0.tar.gz" + wget "https://github.com/LLNL/sundials/releases/download/vX.Y.Z/sundials-X.Y.Z.tar.gz" -Extract the corresponding compressed file +Extract the corresponding compressed file, rename - tar -xzf sundials-6.3.0.tar.gz + tar -xzf sundials-X.Y.Z.tar.gz && mv sundials-X.Y.Z sundials-software + +We suggest you periodically update to the latest version-- you can also install through github + git clone https://github.com/LLNL/sundials.git sundials-software + cd sundials-software + git fetch --all --tags --prune + git checkout tags/vX.Y.Z An example build_cmake file is at summa/build/cmake_external/build_cmakeSundials.bash which you can copy to builddir as build_cmake. Then, enter the buildir and run diff --git a/docs/installation/SUMMA_docker.md b/docs/installation/SUMMA_docker.md deleted file mode 100644 index fb958fc4e..000000000 --- a/docs/installation/SUMMA_docker.md +++ /dev/null @@ -1,33 +0,0 @@ -# SUMMA using Docker - -If you are not interested in compiling SUMMA locally on your machine and you are not planning to contribute to SUMMA development, then you may be interested in using a version of SUMMA that runs in a [Docker container](https://www.docker.com/what-docker). - -To run SUMMA using [Docker](https://www.docker.com) you will need to do the following: - - 1. Install Docker for your platform. Note that the [Docker Community Edition (Docker CE)](https://www.docker.com/community-edition) will work just fine (it's what we use) and it's free. There are Docker CE version for Mac, Windows, and a number of \*nix distributions. - - 1. Once you have Docker installed, you can get the Docker containers for different versions of SUMMA from [Docker Hub](https://hub.docker.com). Currently the Docker containers are distributed as part of the [Docker Hub account of bartnijssen](https://hub.docker.com/r/bartnijssen/summa/) and you can find out which version are being built by looking at the [Build Settings](https://hub.docker.com/r/bartnijssen/summa/~/settings/automated-builds/). For example, the Docker Tag Name `latest` reflects the SUMMA version on the master branch in the SUMMA reposistory and `develop` reflects the SUMMA version on the develop branch. There may be a few others as well. - - 1. To download the appropriate container, make sure that Docker is running and open a terminal. Then type - - `docker pull bartnijssen/summa:` - - where `` is equal to `latest`, `develop` or whichever tag you are interested in. This will download the container to your local machine. - - 1. You are now ready to run SUMMA. No further installs are required. You can run SUMMA by typing - - `docker run bartnijssen/summa:` - - 1. You can give SUMMA command-line arguments directly after the name of the docker container you are running. For example, to see which version of SUMMA you are running provide the `-v` command-line option. If you use `develop` as the ``, this would look something like this (the exact message will depend on the SUMMA version you are running) - - `docker run bartnijssen/summa:develop -v` - - SUMMA - Structure for Unifying Multiple Modeling Alternatives - Version: v2.0.0 - Build Time: Fri Sep 1 22:23:26 UTC 2017 - Git Branch: develop-0-gb9515d7 - Git Hash: b9515d7d7f44ae7043eac37caa66e7bc9f946a04 - - 1. To do actual SUMMA runs, you need to provide a bit more information. The main thing is that you will need to provide the path of the master file (`-m`) so that SUMMA knows where to read and write its input and output. The other part is that you need to set up a mapping between your local file paths and the path that SUMMA has access to within the Docker container. This mapping can be set up on the Docker command-line by using the `-v` option to `docker run`. See `docker run --help` for more details. - - The [SUMMA test cases](SUMMA_test_cases.md) include install and run scripts in the top level directory to configure and run the test cases using SUMMA on Docker. We recommend that you start there to understand how to make an actual SUMMA run using Docker. diff --git a/docs/installation/SUMMA_installation.md b/docs/installation/SUMMA_installation.md deleted file mode 100644 index c5521d365..000000000 --- a/docs/installation/SUMMA_installation.md +++ /dev/null @@ -1,101 +0,0 @@ -# SUMMA Installation - -We have successfully installed SUMMA on a number of Unix-like (\*nix) operating systems, including Linux and Darwin (Mac OS X). Since we do a lot of our development on OS X, we have a [separate page](SUMMA_on_OS_X.md) on how to install the necessary tools and libraries on that platform. If you do not want to deal with installing programs and libraries and just want to run SUMMA, then we also have a SUMMA release that uses [Docker](https://www.docker.com). Details can be found on our [SUMMA using Docker](SUMMA_docker.md) page. If you plan to use Docker, then you can skip the rest of this page. - -To compile SUMMA, you will need: - - * a Fortran compiler. We have successfully used the intel Fortran compiler (`ifort`, version 17.x) and the GNU Fortran compiler (`gfortran`, version 6 or higher), the latter of which is freely available. Since we do not use any compiler-specific extensions, you should be able to compile SUMMA with other Fortran compilers as well. - - If you do not have a Fortran compiler, you can install `gfortran` for free. The easiest way is to use a package manager. Note that `gfortran` is installed as part of the `gcc` compiler suite. - - * the NetCDF libraries. [NetCDF](http://www.unidata.ucar.edu/software/netcdf/) or the Network Common Data Format is a set of software libraries and self-describing, machine-independent data formats that support the creation, access, and sharing of array-oriented scientific data. They are widely used in the hydrometeorological community and eventually almost all SUMMA I/O will use NetCDF. Most \*nix package managers include a NetCDF port. Note that you need to ensure that: - - * You have NetCDF version 4.x; - * The NetCDF libraries are compiled with the same compiler as you plan to use for compiling SUMMA; and - * You have the NetCDF Fortran library installed (`libnetcdff.*`) and not just the C-version. - - * the [LAPACK](http://www.netlib.org/lapack/) (Linear Algebra PACKage) library provides a series of routines for linear algebra operations, including matrix solvers. How to install the library depends on your \*nix variant and is not covered here. For example, on OS X you will get all the necessary LAPACK routines by installing the ATLAS software (again, this is easiest using a package manager; note that ATLAS can take many hours to build the first time when you install it). - - * the [ATLAS](http://math-atlas.sourceforge.net/) (Automatically Tuned Linear Algebra Software) library. Note that this is required on OS X using the gfortran compiler to be able to use LAPACK. It's not clear that this is used on other \*nix machines. - - * a copy of the SUMMA source code from [this repo](https://github.com/NCAR/summa). You have a number of options: - - * If you just want to use the latest stable release of SUMMA, then simply look for the [latest release](https://github.com/NCAR/summa/releases); - * If you want the latest and greatest (and potentially erroneous), download a copy of the [development branch](https://github.com/ncar/summa/tree/develop) (or clone it); - * If you may want to do SUMMA development, then fork the repo on github and start editing your own copy. - - Note that you will not be able to contribute to the main SUMMA repo directly. If you are seriously interested in contributing, spend a little time learning git. It will be useful anyway. For more information about working with the SUMMA code, please see the following documents: - - * [SUMMA and Git](../development/SUMMA_and_git.md) - * [Git Workflow for SUMMA](../development/SUMMA_git_workflow.md) - * [SUMMA Coding Conventions](../development/SUMMA_coding_conventions.md) - -Once you have all the above, you can compile SUMMA using the following steps: - - 1. Navigate to your local copy of the SUMMA directory and go to the `build` subdirectory; - - 1. Specify a number of environment variables that are used by the build process. You will need to set the following: - - * `F_MASTER` : This is the parent directory of the `build` directory. - - > Example: Given the following directory structure: - > ``` - > - > summa/ - > ├── build - > ├── ci - > ├── COPYING - > ├── Dockerfile - > ├── docs - > ├── header.license - > ├── LICENSE.txt - > ├── mkdocs.yml - > ├── readme.md -> docs/index.md - > └── utils - > ``` - > - > `export F_MASTER=/summa` - - * `FC` : Define the compiler family. This is only used to determine the compiler flags. If your compiler is not included, you will need to add the relevant section to the `Makefile`. - - > Example: `export FC=gfortran` - - * `FC_EXE` : This is the actual compiler executable that is invoked. - - > Example: `export FC_EXE=gfortran` - - * `INCLUDES`: This is the path to the NetCDF and LAPACK include files. This is typically `/usr/include` or `/usr/local/include`. - - > Example: `export INCLUDES='-I/usr/include -I/usr/local/inclde -I` - - * `LIBRARIES`: This is the path to the NetCDF and LAPACK libraries, typically `/usr/lib`. The following command will help you determine the correct paths: `find / -type f \( -name "libblas*.so*" -o -name "libnetcdf*.so*" \)`. - - > Example: `export LIBRARIES='-L/usr/lib -lnetcdff -L/usr/lib/x86_64-linux-gnu -llapack -lblas'` - - If you are using the `bash` shell, then you would set these environment variables with `export FC=gfortran` for example. You may need to modify the `Makefile` if you are using a different Fortran compiler or your setup is different. If someone wants to contribute an actual `configure` script that would be great. - - 1. Check that all variables in the Makefile are set correctly by typing `make check`. Inspect the variables and make sure that they make sense. If not, modify the Makefile further. - - 1. Type `make` (if you are in the `build` directory). If all goes well, this will build SUMMA and move the executable `summa.exe` to the `bin` directory. You may get some warnings (depending on your compiler settings), but you should not get any errors; - - 1. Pay attention to the `make` output. You may need to set some environment variables (`LD_LIBRARY_PATH` in particular) to support dynamic linking; - - 1. If the code compiles successfully, then the last line of output from the make process will tell you where the SUMMA executable is installed (it goes into `summa/bin`). Run `summa.exe` in that directory (you may need to provide the full path). If all goes well, you should get a usage message that looks something like (depending on the SUMMA version): - -``` - Usage: summa.exe -m master_file [-s fileSuffix] [-g startGRU countGRU] [-h iHRU] [-r freqRestart] [-p freqProgress] [-c] - summa.exe summa executable - - Running options: - -m --master Define path/name of master file (required) - -s --suffix Add fileSuffix to the output files - -g --gru Run a subset of countGRU GRUs starting from index startGRU - -h --hru Run a single HRU with index of iHRU - -r --restart Define frequency [y,m,d,never] to write restart files - -p --progress Define frequency [m,d,h,never] to print progress - -v --version Display version information of the current built -``` - -If you get this far then SUMMA is installed correctly and functional. - -Continue reading [SUMMA configuration](../configuration/SUMMA_configuration.md) to learn more about how to configure SUMMA for your application. We strongly recommend that you get the [test applications](SUMMA_test_cases.md) to help you get started. diff --git a/docs/installation/SUMMA_on_OS_X.md b/docs/installation/SUMMA_on_OS_X.md deleted file mode 100755 index c90c96148..000000000 --- a/docs/installation/SUMMA_on_OS_X.md +++ /dev/null @@ -1,66 +0,0 @@ -# Instructions for installing SUMMA on Mac OS X - -> The following has been tested on a MacBook Pro and iMac, both using OS X v. 10.12.6 (Sierra) and with [MacPorts](http://www.macports.org) as the OS X package manager. It should also work on older versions of OS X or with other package managers such as [fink](http://www.finkproject.org) or [homebrew](http://brew.sh), but you will undoubtedly need to make some modifications. - -This document is mainly for people who want to use SUMMA in their modeling project, rather than contribute or change the SUMMA source code. If you want to contribute to SUMMA, , please see the following documents: - -* [SUMMA and Git](../development/SUMMA_and_git.md) -* [Git Workflow for SUMMA](../development/SUMMA_git_workflow.md) -* [SUMMA Coding Conventions](../development/SUMMA_coding_conventions.md) - - -In the following I will assume that you don't have a Fortran compiler or NetCDF installed. If you do, just scroll down. - - 1. Install [MacPorts](http://www.macports.org). This is a package manager for OS X (Darwin), which will let you install many software packages that are typically used in a Unix-like environment (which is what your Mac provides). MacPorts has detailed instructions on how it should be installed for various versions of OS X. Just follow the instructions (you will be asked to install Xcode, etcetera). If you know nothing about Unix/Linux/Darwin, now is the time to learn. - - 1. Open a terminal (Applications --> Utilities --> Terminal.app) - - 1. Use MacPorts to install NetCDF by typing the following on the command-line - - `sudo port install netcdf` - - You will be asked to provide your password. Note that you can only `sudo` (execute a command as a superuser) if you have administrative privileges for your machine (i.e., you can install software, etc.). If you do not have administrative privileges, talk to the person who maintains your computer. A whole bunch of other packages (dependencies) will be installed as well. Just let MacPorts do its work, this is why you are using a package manager. It may take a while the first time you do this. Make sure that the NetCDF is compiled using `gcc6` or higher. You can specify different variants of the package to be installed. See the MacPorts documentation for details. - - 1. Install the Fortran version of the NetCDF library - - `sudo port install netcdf-fortran` - - Note what version of `gcc` was used to compile the NetCDF Fortran library. If you did not see it, type - - `sudo port info netcdf-fortran` - - The gcc compiler will be listed under `Build Dependencies`. For example, `gcc11`, which is what I'll assume for now. However, make sure to use the correct version of gcc (i.e. the one used to compile the netcdf-fortran library) when you compile SUMMA. - - 1. Install the correct version of gcc (here we'll assume gcc11) - - `sudo port install gcc11` - - 1. Note that MacPorts typically installs everything in the `/opt/local` directory. Make sure you now have NetCDF and gfortran installed: - - `ls /opt/local/lib/*netcdf*` - - You should have one or more `libnetcdf.*` (C-version of NetCDF library) and `libnetcdff.*` (Fortran version of NetCDF library) files - - `ls /opt/local/bin/*fortran*` - - You should have one or more `gfortran-*` files. If you installed `gcc11`, the gfortran executable will be `/opt/local/bin/gfortran-mp-11`. Since MacPorts will have modified your path so that `/opt/local/bin` is part of that, you should be able to invoke the compiler by typing - - `gfortran-mp-11` - - and the results should be - - `gfortran-mp-11: fatal error: no input files` - `compilation terminated.` - - Note that you may also have a symbolic link named `/opt/local/bin/gfortran`. Make sure that that link points to the correct executable (e.g. command `readlink /opt/local/bin/gfortran` should result in `/opt/local/bin/gfortran-mp-11`). If so, then you should be able to invoke the correct version of the fortran compile by simply typing `gfortran`. Or, type `sudo ln -sf /opt/local/bin/gfortran-mp-11 /opt/local/bin/gfortran` to change it. - - - 1. While you are at it, there are a number of other packages that would be useful to install, in particular - - * `sudo port install ncview` : to visualize NetCDF files - * `sudo port install nco` : to manipulate NetCDF files, see the [NCO homepage](http://nco.sourceforge.net) - * `sudo port install cdo` : to manipulate NetCDF files, see the [CDO homepage](https://code.mpimet.mpg.de/projects/cdo/) - - 1. Now obtain the SUMMA source code from the [SUMMA source code repository](https://github.com/NCAR/summa). You may just want to download the latest tagged release. Unless you are planning to contribute to the source code, there is no need to clone or fork the repository. - - 1. Untar or unzip the archive, then go to the `summa/build` directory and follow the instructions in the [SUMMA installation](SUMMA_installation.md) page. If you are using MacPorts, the `FC_ENV` can be set to `gfortran-11-macports`. diff --git a/docs/installation/SUMMA_test_cases.md b/docs/installation/SUMMA_test_cases.md deleted file mode 100644 index b6ce18f8c..000000000 --- a/docs/installation/SUMMA_test_cases.md +++ /dev/null @@ -1,5 +0,0 @@ -# Test Cases for SUMMA - -We have developed a number of test cases that we use during model development to ensure that the model performs as expected after code changes. These test cases are also useful to ensure that your SUMMA installation is working correctly. The test cases consist of synthetic experiments with known solutions as well as test cases that were used in the original SUMMA papers (Clark et al., [2015a](../references.md#clark_2015a);[b](../references.md#clark_2015b);[c](../references.md#clark_2015c)) - -The full suite of SUMMA test cases can be obtained from the [NCAR SUMMA page](https://ral.ucar.edu/projects/summa) under the _Test Cases_ tab. Please make sure you have the correct version of the test cases for your version of the SUMMA code. After you unpack the archive, you can find information about how to install and run the test cases in `readme.md` in the top level directory. diff --git a/docs/sundials_bmi/bmi_interface.txt b/docs/sundials_bmi/bmi_interface.txt deleted file mode 100644 index 05987af0f..000000000 --- a/docs/sundials_bmi/bmi_interface.txt +++ /dev/null @@ -1,10 +0,0 @@ -To run a BMI interface, run the executable /bin/summa_bmi.exe -To run as previously, run the executable /bin/summa_sundials.exe with appropriate command line arguments. - -All BMI files are in build/source/driver. The important ones are as following: - -summa_bmi.f90: this contains the code that was in summa_driver.f90 and adds to it the BMI functions. It is now a module and uses the BMI library. - -summa_driver.f90: this is the main program called by summa_sundials.exe that will call all other modules similar to previous editions of the code. - -summa_driverBMI.f90: this is the main program called by summa_bmi.exe that will call all other modules in the BMI interface mode. \ No newline at end of file diff --git a/docs/sundials_bmi/installation.txt b/docs/sundials_bmi/installation.txt deleted file mode 100644 index ed3226392..000000000 --- a/docs/sundials_bmi/installation.txt +++ /dev/null @@ -1,74 +0,0 @@ - -1. First install BMI. Download the latest release of BMI from https://github.com/csdms/bmi-fortran.git -% git clone https://github.com/csdms/bmi-fortran.git - -2. Make a directory outside the bmi-fortran folder and enter it, and make the install and build dirs, enter the build dir. -% export BMIF_VERSION=2.0 -% mkdir bmi -% cd bmi -% mkdir instdir -% mkdir buildir -% cd /buildir -NOTE if you need to recompile after a system upgrade, delete the contents of these directories before running cmake in the next step! - -3. The install directory needs to be set while running the cmake inside the builddir, using home directory as $(YOUR_HOME) -% cmake ../../bmi-fortran/ -DCMAKE_INSTALL_PREFIX=$(YOUR_HOME)/bmi/instdir - -4. The default compiler is gfortran. To change it (it should be the same as the netcdf build and the later summa build), you could also add the following option to cmake with your $(YOUR_GFORTRAN) - -DCMAKE_Fortran_COMPILER=$(YOUR_GFORTRAN) - -You can do 3 and 4 by running from inside buildir: -cp ../../summa/build/makefiles/build_cmakeBMI build_cmake -./build_cmake - - -5. If the above went well, staying in the buildir directory run: -% make -% make install - -6. If you are using Sundials, now install that. Download the latest release of IDA solver from SUNDIALS package in https://computing.llnl.gov/projects/sundials/sundials-software -% wget "https://github.com/LLNL/sundials/releases/download/v6.6.0/sundials-6.6.0.tar.gz" - -7. Extract the corresponding compressed file -% tar -xzf sundials-6.6.0.tar.gz - -8. Read INSTALL_GUIDE.pdf and follow the installation instructions. Roughly, do the following: -Make a directory outside the sundials-6.3.0 folder and enter it, and make the install and build dirs, enter the build dir. -% mkdir sundials -% cd sundials -% mkdir instdir -% mkdir buildir -% cd /buildir -NOTE if you need to recompile after a system upgrade, delete the contents of these directories before running cmake in the next step! - -9. Since in SUMMA-SUNDIALS utilizes the Fortran/C interface FIDA, the following setting needs to be enabled, as well as the setting the install directory, while running the cmake inside the builddir, using your home directory as $(YOUR_HOME) -% cmake ../sundials-6.6.0/ -DBUILD_FORTRAN_MODULE_INTERFACE=ON DCMAKE_INSTALL_PREFIX=$(YOUR_HOME)/sundials/instdir -DEXAMPLES_INSTALL_PATH=$(YOUR_HOME)/sundials/instdir/examples - -10. The default compiler is gfortan. To change it (it should be the same as the netcdf build and the later summa build), you could also add the following option to cmake with your $(YOUR_GFORTRAN) - -DCMAKE_Fortran_COMPILER=$(YOUR_GFORTRAN) - -You can do 9 and 10 by running from inside buildir: -cp ../../summa/build/makefiles/build_cmakeSundials build_cmake -./build_cmake - -11. If the above went well, staying in the buildir directory run -% make -% make install - -12. Enter summa directory and build summa as in /docs/SUMMA_installation.md. You will have to edit the SUMMA Makefile as below additionally (as well as install required libraries and link them as directed in the Makefile). You need to clone summa before you can change the makefile - git clone https://git.cs.usask.ca/numerical_simulations_lab/summa.git - -13. In SUMMA Makefile, define the FC_EXE to be the same compiler as you used for BMI and netcdf - -14. In SUMMA Makefile, define the SUNDIALS installation directory (and all the folders as required in the SUMMA instructions. - DIR_SUNDIALS=$(YOUR_HOME)/sundials/instdir - -15. Inside the /summa/build/ directory run -% make -or % source build_summa_* if you are using a specific * compiler - -16. On linux installations you may need to add to your .bashrc file (and run source $(YOUR_HOME)/.bashrc) -export LD_LIBRARY_PATH=$(YOUR_HOME)/sundials/instdir/lib64 -export LD_LIBRARY_PATH=$(YOUR_HOME)/bmi/instdir/lib64 - - diff --git a/docs/sundials_bmi_flags/bmi_interface.txt b/docs/sundials_bmi_flags/bmi_interface.txt new file mode 100644 index 000000000..7767e6814 --- /dev/null +++ b/docs/sundials_bmi_flags/bmi_interface.txt @@ -0,0 +1,38 @@ +Note, if you want to run with just BMI, not NexGen, do the following +First install BMI. Download the latest release of BMI from https://github.com/csdms/bmi-fortran.git +% git clone https://github.com/csdms/bmi-fortran.git +% git fetch --all --tags --prune +% git checkout tags/vX.Y.Z + +Make a directory outside the bmi-fortran folder and enter it, and make the install and build dirs, enter the build dir. +% export BMIF_VERSION=2.0 +% mkdir bmi +% cd bmi +% mkdir instdir +% mkdir buildir +% cd /buildir + +The install directory needs to be set while running the cmake inside the builddir, using home directory as $(YOUR_HOME) +% cmake ../../bmi-fortran/ -DCMAKE_INSTALL_PREFIX=$(YOUR_HOME)/bmi/instdir + +The default compiler is gfortran. To change it (it should be the same as the netcdf build and the later summa build), you could also add the following option to cmake with your $(YOUR_GFORTRAN) + -DCMAKE_Fortran_COMPILER=$(YOUR_GFORTRAN) + +You can do the last two steps by running from inside buildir: +cp ../../summa/build/makefiles/build_cmakeBMI build_cmake +./build_cmake + +If the above went well, staying in the buildir directory run: +% make +% make install + +To run a BMI interface, run the executable /bin/summa_bmi.exe +To run as previously, run the executable /bin/summa_sundials.exe with appropriate command line arguments. + +All BMI files are in build/source/driver. The important ones are as following: + +summa_bmi.f90: this contains the code that was in summa_driver.f90 and adds to it the BMI functions. It is now a module and uses the BMI library. + +summa_driver.f90: this is the main program called by summa_sundials.exe that will call all other modules similar to previous editions of the code. + +summa_driverBMI.f90: this is the main program called by summa_bmi.exe that will call all other modules in the BMI interface mode. \ No newline at end of file diff --git a/docs/sundials_bmi/flags_params_sundials.txt b/docs/sundials_bmi_flags/flags_params_sundials.txt similarity index 100% rename from docs/sundials_bmi/flags_params_sundials.txt rename to docs/sundials_bmi_flags/flags_params_sundials.txt diff --git a/utils/bal_per_GRU.py b/utils/bal_per_GRU.py index 6e6e9bb03..77f3eced1 100644 --- a/utils/bal_per_GRU.py +++ b/utils/bal_per_GRU.py @@ -22,11 +22,11 @@ viz_dir = Path('/home/avanb/scratch/statistics') -testing = False +testing = True if testing: stat = 'mean' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be1lu'] #maybe make this an argument + method_name=['be1cm','be1en','be1lu'] #maybe make this an argument else: import sys # The first input argument specifies the run where the files are @@ -51,10 +51,10 @@ plt_titl = ['(a) Canopy Air Space Energy Balance','(b) Vegetation Energy Balance','(c) Snow Energy Balance','(d) Soil Energy Balance', '(e) Wall Clock Time',] plt_titl2 = ['(a) Vegetation Mass Balance','(b) Snow Mass Balance','(c) Soil Mass Balance', '(d) Aquifer Mass Balance'] -leg_titl0 = ['$s^{-1}$','$s^{-1}$','$s^{-1}$','$s^{-1}$','$s$'] -leg_titl20 =['$s^{-1}$','$s^{-1}$','$s^{-1}$','$s^{-1}$'] leg_titl0 = ['$W~m^{-3}$','$W~m^{-3}$','$W~m^{-3}$','$W~m^{-3}$','$num$'] -leg_titl20 =['$kg~m^{-2}~s^{-1}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}~s^{-1}$'] +leg_titl02 =['$kg~m^{-2}~s^{-1}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}~s^{-1}$'] +leg_titl = ['$s^{-1}$','$s^{-1}$','$s^{-1}$','$s^{-1}$','$s$'] +leg_titl2 =['$s^{-1}$','$s^{-1}$','$s^{-1}$','$s^{-1}$'] #fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' #fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) @@ -96,6 +96,8 @@ def run_loop(i,var,comp,leg_t,leg_t0,plt_t): for j, m in enumerate(method_name): lgnd.legendHandles[j]._sizes = [80] axs[r,c].set_title(plt_t) + axs[r,c].set_xscale('log') + axs[r,c].set_yscale('log') axs[r,c].set_xlabel(stat_word + word + ' [{}]'.format(leg_t)) axs[r,c].set_ylabel(stat0_word + word + ' [{}]'.format(leg_t0)) @@ -111,7 +113,7 @@ def run_loop(i,var,comp,leg_t,leg_t0,plt_t): fig.subplots_adjust(hspace=0.24, wspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space #fig.suptitle('Scatterplot of Hourly Statistics for each GRU', fontsize=40,y=1.0) -for i,(var,comp,leg_t,leg_t0,plt_t) in enumerate(zip(plot_vars,comp_vars,leg_titl0,leg_titl0,plt_titl)): +for i,(var,comp,leg_t,leg_t0,plt_t) in enumerate(zip(plot_vars,comp_vars,leg_titl,leg_titl0,plt_titl)): run_loop(i,var,comp,leg_t,leg_t0,plt_t) # Save @@ -130,7 +132,7 @@ def run_loop(i,var,comp,leg_t,leg_t0,plt_t): fig.subplots_adjust(hspace=0.24, wspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space #fig.suptitle('Scatterplot of Hourly Statistics for each GRU', fontsize=40,y=1.0) -for i,(var,comp,leg_t,leg_t0,plt_t) in enumerate(zip(plot_vars2,comp_vars2,leg_titl20,leg_titl20,plt_titl2)): +for i,(var,comp,leg_t,leg_t0,plt_t) in enumerate(zip(plot_vars2,comp_vars2,leg_titl2,leg_titl02,plt_titl2)): run_loop(i,var,comp,leg_t,leg_t0,plt_t) # Save diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 6d6d6901a..46e9b1325 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -38,7 +38,7 @@ if testing: top_fold = '/Users/amedin/Research/USask/test_py/' - method_name='be1lu' + method_name='be1en' not_parallel = True else: import sys @@ -134,7 +134,7 @@ def run_loop(file,bench,processed_files_path): import multiprocessing as mp lock = mp.Lock() with lock: - dat = xr.open_dataset(file), + dat = xr.open_dataset(file) if do_vars: ben = xr.open_dataset(bench) # sometimes gives -9999 the whole run (non-compute), make these nan and plot as lowest value 0 in geographic From 846510f72956dc41a4cf6db27ada560d55fc040b Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 1 Mar 2024 03:20:19 -0600 Subject: [PATCH 1170/1472] Applied object-oriented methods to the intent(out) arguments of summaSolve4numrec subroutine. --- build/source/engine/summaSolve4numrec.f90 | 209 +++++++++++++--------- build/source/engine/systemSolv.f90 | 27 ++- 2 files changed, 138 insertions(+), 98 deletions(-) diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 7cb20f784..3ef22531e 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -75,7 +75,11 @@ module summaSolve4numrec_module in_type_computJacob, & ! class for computJacob arguments out_type_computJacob, & ! class for computJacob arguments in_type_lineSearchRefinement, & ! class for lineSearchRefinement arguments - out_type_lineSearchRefinement ! class for lineSearchRefinement arguments + out_type_lineSearchRefinement,& ! class for lineSearchRefinement arguments + in_type_summaSolve4numrec, & ! class for summaSolve4numrec arguments + io_type_summaSolve4numrec, & ! class for summaSolve4numrec arguments + out_type_summaSolve4numrec ! class for summaSolve4numrec arguments + ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: & @@ -142,15 +146,15 @@ subroutine summaSolve4numrec(& fluxVecNew, & ! intent(out): new flux vector resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equation resVecNew, & ! intent(out): new residual vector - fNew, & ! intent(out): new function evaluation - converged, & ! intent(out): convergence flag - err,message) ! intent(out): error control + out_SS4NR) ! intent(out): new function evaluation, convergence flag, and error control USE computJacob_module, only: computJacob USE eval8summa_module, only: imposeConstraints USE matrixOper_module, only: lapackSolv USE matrixOper_module, only: scaleMatrices implicit none ! -------------------------------------------------------------------------------------------------------------------------------- + type(in_type_summaSolve4numrec) :: in_SS4NR ! model control variables and previous function evaluation + type(io_type_summaSolve4numrec) :: io_SS4NR ! first flux call flag and baseflow variables ! input: model control real(rkind),intent(in) :: dt_cur ! current stepsize real(rkind),intent(in) :: dt ! entire time step for drainage pond rate @@ -196,11 +200,7 @@ subroutine summaSolve4numrec(& real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector real(rkind),intent(out) :: resSinkNew(:) ! sink terms on the RHS of the flux equation real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector - real(rkind),intent(out) :: fNew ! new function evaluation - logical(lgt),intent(out) :: converged ! convergence flag - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + type(out_type_summaSolve4numrec),intent(out) :: out_SS4NR ! new function evaluation, convergence flag, and error control ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- @@ -247,28 +247,36 @@ subroutine summaSolve4numrec(& call refine_Newton_step; if (return_flag) return ! refine Newton step if needed -- return if error - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors + associate(err => out_SS4NR % err,message => out_SS4NR % message) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors + end associate contains subroutine initialize_summaSolve4numrec ! *** Initial steps for the summaSolve4numrec algorithm (computing the Newton step) *** - ! initialize error control - err=0; message='summaSolve4numrec/' - return_flag=.false. ! initialize return flag - - ! choose Jacobian type - select case(model_decisions(iLookDECISIONS%fDerivMeth)%iDecision) - case(numerical) - err=20; message=trim(message)//'numerical derivatives are not implemented for BE numerical Recipes solver'; - return_flag=.true.; return - case(analytical); ! this is fine - case default - err=20; message=trim(message)//'expect choice numericl or analytic to calculate derivatives for Jacobian'; - return_flag=.true.; return - end select + associate(& + err => out_SS4NR % err ,& + message => out_SS4NR % message & + &) + ! initialize error control + err=0; message='summaSolve4numrec/' + return_flag=.false. ! initialize return flag + + ! choose Jacobian type + select case(model_decisions(iLookDECISIONS%fDerivMeth)%iDecision) + case(numerical) + err=20; message=trim(message)//'numerical derivatives are not implemented for BE numerical Recipes solver'; + return_flag=.true.; return + case(analytical); ! this is fine + case default + err=20; message=trim(message)//'expect choice numericl or analytic to calculate derivatives for Jacobian'; + return_flag=.true.; return + end select + end associate + ! get the number of soil layers in the solution vector mSoil = size(indx_data%var(iLookINDEX%ixMatOnly)%dat) @@ -283,51 +291,62 @@ subroutine update_Jacobian ! NOTE: The derivatives were computed in the previous call to computFlux ! This occurred either at the call to eval8summa at the start of systemSolv ! or in the call to eval8summa in the previous iteration (within lineSearchRefinement or trustRegionRefinement) - call initialize_computJacob_summaSolve4numrec - call computJacob(in_computJacob,indx_data,prog_data,diag_data,deriv_data,dBaseflow_dMatric,dMat,aJac,out_computJacob) - call finalize_computJacob_summaSolve4numrec - if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! (check for errors) - - ! compute the numerical Jacobian matrix - if (doNumJacobian) then - globalPrintFlag=.false. - call numJacobian(stateVecTrial,dMat,nJac,err,cmessage) + associate(& + err => out_SS4NR % err ,& + message => out_SS4NR % message & + &) + call initialize_computJacob_summaSolve4numrec + call computJacob(in_computJacob,indx_data,prog_data,diag_data,deriv_data,dBaseflow_dMatric,dMat,aJac,out_computJacob) + call finalize_computJacob_summaSolve4numrec if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! (check for errors) - globalPrintFlag=globalPrintFlagInit - end if + + ! compute the numerical Jacobian matrix + if (doNumJacobian) then + globalPrintFlag=.false. + call numJacobian(stateVecTrial,dMat,nJac,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! (check for errors) + globalPrintFlag=globalPrintFlagInit + end if + end associate end subroutine update_Jacobian subroutine solve_linear_system ! *** Solve the linear system for the Newton step using LAPACK routines *** - ! test the band diagonal matrix - if (testBandDiagonal) then - call testBandMat(check=.true.,err=err,message=cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! (check for errors) - end if - - ! scale the residual vector - rVecScaled(1:nState) = fScale(:)*real(rVec(:), rkind) ! NOTE: residual vector is in quadruple precision - - ! scale matrices - call scaleMatrices(ixMatrix,nState,aJac,fScale,xScale,aJacScaled,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors + associate(& + err => out_SS4NR % err ,& + message => out_SS4NR % message & + &) + ! test the band diagonal matrix + if (testBandDiagonal) then + call testBandMat(check=.true.,err=err,message=cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! (check for errors) + end if - ! debug statement for scaled Jacobian - if (globalPrintFlag .and. ixMatrix==ixBandMatrix) then - print*, '** SCALED banded analytical Jacobian:' - write(*,'(a4,1x,100(i17,1x))') 'xCol', (iLayer, iLayer=iJac1,iJac2) - do iLayer=kl+1,nBands - write(*,'(i4,1x,100(e17.10,1x))') iLayer, (aJacScaled(iLayer,jLayer),jLayer=min(iJac1,nState),min(iJac2,nState)) - end do - end if - - ! copy the scaled matrix, since it is decomposed in lapackSolv - aJacScaledTemp = aJacScaled + ! scale the residual vector + rVecScaled(1:nState) = fScale(:)*real(rVec(:), rkind) ! NOTE: residual vector is in quadruple precision + + ! scale matrices + call scaleMatrices(ixMatrix,nState,aJac,fScale,xScale,aJacScaled,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors - ! compute the newton step: use the lapack routines to solve the linear system A.X=B - call lapackSolv(ixMatrix,nState,aJacScaledTemp,-rVecScaled,newtStepScaled,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors + ! debug statement for scaled Jacobian + if (globalPrintFlag .and. ixMatrix==ixBandMatrix) then + print*, '** SCALED banded analytical Jacobian:' + write(*,'(a4,1x,100(i17,1x))') 'xCol', (iLayer, iLayer=iJac1,iJac2) + do iLayer=kl+1,nBands + write(*,'(i4,1x,100(e17.10,1x))') iLayer, (aJacScaled(iLayer,jLayer),jLayer=min(iJac1,nState),min(iJac2,nState)) + end do + end if + + ! copy the scaled matrix, since it is decomposed in lapackSolv + aJacScaledTemp = aJacScaled + + ! compute the newton step: use the lapack routines to solve the linear system A.X=B + call lapackSolv(ixMatrix,nState,aJacScaledTemp,-rVecScaled,newtStepScaled,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors + + end associate ! debug statement for Newton step if (globalPrintFlag) write(*,'(a,1x,10(e17.10,1x))') 'newtStepScaled = ', newtStepScaled(min(iJac1,nState):min(iJac2,nState)) @@ -342,36 +361,44 @@ subroutine refine_Newton_step ! * case 1: state vector ! compute the flux vector and the residual, and (if necessary) refine the iteration increment ! NOTE: in 99.9% of cases newtStep will be used (no refinement) - if (size(stateVecTrial)>1) then + + associate(& + fnew => out_SS4NR % fnew ,& + converged => out_SS4NR % converged,& + err => out_SS4NR % err ,& + message => out_SS4NR % message & + &) + if (size(stateVecTrial)>1) then + + ! try to backtrack + select case(ixStepRefinement) + case(ixLineSearch) + call in_LSR % initialize(doRefine,fOld) + call lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_LSR) + call out_LSR % finalize(fNew,converged,err,cmessage) + case(ixTrustRegion) + call in_TRR % initialize(doRefine,fOld) + call trustRegionRefinement(in_TRR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_TRR) + call out_TRR % finalize(fNew,converged,err,cmessage) + case default; err=20; message=trim(message)//'unable to identify numerical solution'; return_flag=.true.; return + end select - ! try to backtrack - select case(ixStepRefinement) - case(ixLineSearch) + ! check warnings: negative error code = warning; in this case back-tracked to the original value + ! NOTE: Accept the full newton step if back-tracked to the original value + if (err<0) then + doRefine=.false.; call in_LSR % initialize(doRefine,fOld) call lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_LSR) call out_LSR % finalize(fNew,converged,err,cmessage) - case(ixTrustRegion) - call in_TRR % initialize(doRefine,fOld) - call trustRegionRefinement(in_TRR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_TRR) - call out_TRR % finalize(fNew,converged,err,cmessage) - case default; err=20; message=trim(message)//'unable to identify numerical solution'; return_flag=.true.; return - end select + end if - ! check warnings: negative error code = warning; in this case back-tracked to the original value - ! NOTE: Accept the full newton step if back-tracked to the original value - if (err<0) then - doRefine=.false.; - call in_LSR % initialize(doRefine,fOld) - call lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_LSR) - call out_LSR % finalize(fNew,converged,err,cmessage) + ! * case 2: scalar + else + call safeRootfinder(stateVecTrial,rVecScaled,newtStepScaled,stateVecNew,fluxVecNew,resVecNew,out_SRF) + call out_SRF % finalize(fNew,converged,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors end if - - ! * case 2: scalar - else - call safeRootfinder(stateVecTrial,rVecScaled,newtStepScaled,stateVecNew,fluxVecNew,resVecNew,out_SRF) - call out_SRF % finalize(fNew,converged,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors - end if + end associate end subroutine refine_Newton_step ! ********************************************************************************************************* @@ -777,7 +804,9 @@ subroutine getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,message) xIncrement = stateVecNew - stateVecPrev ! evaluate summa - call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) + associate(fnew => out_SS4NR % fnew) + call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) + end associate if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! check feasibility (should be feasible because of the call to imposeConstraints, except if canopyTemp>canopyTempMax (500._rkind)) @@ -962,7 +991,9 @@ end subroutine initialize_computJacob_summaSolve4numrec subroutine finalize_computJacob_summaSolve4numrec ! *** Transfer data from out_computJacob class object to local variables in summaSolve4numrec *** - call out_computJacob % finalize(err,cmessage) + associate(err => out_SS4NR % err) + call out_computJacob % finalize(err,cmessage) + end associate end subroutine finalize_computJacob_summaSolve4numrec ! ********************************************************************************************************* @@ -1080,8 +1111,8 @@ function checkConv(rVec,xInc,xVec) ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for all energy states ixHydOnly => indx_data%var(iLookINDEX%ixHydOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for all hydrology states ixMatOnly => indx_data%var(iLookINDEX%ixMatOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for matric head state variables in the state vector - ixMatricHead => indx_data%var(iLookINDEX%ixMatricHead)%dat & ! intent(in): [i4b(:)] list of indices for matric head in the soil vector - + ixMatricHead => indx_data%var(iLookINDEX%ixMatricHead)%dat ,& ! intent(in): [i4b(:)] list of indices for matric head in the soil vector + fnew => out_SS4NR % fnew & ! intent(in): [dp] new function evaluations ) ! making associations with variables in the data structures ! ------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 39f6a268d..d3807234b 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -76,12 +76,15 @@ module systemSolv_module ! provide access to the derived types to define the data structures USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! lookup tables - model_options ! defines the model decisions + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! lookup tables + model_options, & ! defines the model decisions + in_type_summaSolve4numrec, & ! class for summaSolve4numrec arguments + io_type_summaSolve4numrec, & ! class for summaSolve4numrec arguments + out_type_summaSolve4numrec ! class for summaSolve4numrec arguments ! look-up values for the choice of heat capacity computation USE mDecisions_module,only:& @@ -257,6 +260,10 @@ subroutine systemSolv(& real(rkind) :: dCanEnthalpy_dWat ! derivatives in canopy enthalpy w.r.t. water state real(rkind) :: dEnthalpy_dTk(nLayers) ! derivatives in layer enthalpy w.r.t. temperature real(rkind) :: dEnthalpy_dWat(nLayers) ! derivatives in layer enthalpy w.r.t. water state + ! class objects for call to summaSolve4numrec + type(in_type_summaSolve4numrec) :: in_SS4NR ! object for intent(in) summaSolve4numrec arguments + type(io_type_summaSolve4numrec) :: io_SS4NR ! object for intent(io) summaSolve4numrec arguments + type(out_type_summaSolve4numrec) :: out_SS4NR ! object for intent(out) summaSolve4numrec arguments ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- @@ -710,9 +717,11 @@ subroutine systemSolv(& fluxVecNew, & ! intent(out): new flux vector resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equa resVecNew, & ! intent(out): new residual vector - fNew, & ! intent(out): new function evaluation - converged, & ! intent(out): convergence flag - err,cmessage) ! intent(out): error control + out_SS4NR) + !fNew, & ! intent(out): new function evaluation + !converged, & ! intent(out): convergence flag + !err,cmessage) ! intent(out): error control + call out_SS4NR % finalize(fNew,converged,err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) ! save the computed functions, residuals, and solution From 720456f5b073a452231df62f87ef961efca62ad4 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 1 Mar 2024 19:49:31 +0900 Subject: [PATCH 1171/1472] changes to allow for sundials v7.0.0 --- build/cmake/CMakeLists.txt | 2 +- build/source/engine/computJacob.f90 | 3 +-- build/source/engine/computJacobWithPrime.f90 | 3 +-- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/eval8summaWithPrime.f90 | 2 +- build/source/engine/summaSolve4ida.f90 | 17 +++++++---------- build/source/engine/summaSolve4kinsol.f90 | 12 +++++------- build/source/engine/tol4ida.f90 | 2 +- 8 files changed, 18 insertions(+), 25 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 6afa704eb..2d23be96a 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -213,7 +213,7 @@ endif() if(CMAKE_BUILD_TYPE MATCHES Sundials) message("\nUsing SUNDIALS libraries for IDA and Kinsol, should have been installed previously") - set(LIB_SUNDIALS -lsundials_fida_mod -lsundials_fnvecmanyvector_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod -lsundials_fkinsol_mod -lsundials_fsunnonlinsolnewton_mod) + set(LIB_SUNDIALS -lsundials_fcore_mod -lsundials_fnvecmanyvector_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod -lsundials_fida_mod -lsundials_fsunnonlinsolnewton_mod -lsundials_fkinsol_mod) elseif(CMAKE_BUILD_TYPE MATCHES BE) message("\nUsing Backward Euler based off free versions of Numerical Recipes only") else() diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 41fe19c23..40ed8aa66 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -1029,8 +1029,7 @@ integer(c_int) function computJacob4kinsol(sunvec_y, sunvec_r, sunmat_J, & !======= Inclusions =========== use, intrinsic :: iso_c_binding - use fsundials_nvector_mod - use fsundials_matrix_mod + use fsundials_core_mod use fnvector_serial_mod use fsunmatrix_band_mod use fsunmatrix_dense_mod diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index e25b46dee..c04f65790 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -1074,8 +1074,7 @@ integer(c_int) function computJacob4ida(t, cj, sunvec_y, sunvec_yp, sunvec_r, & !======= Inclusions =========== use, intrinsic :: iso_c_binding - use fsundials_nvector_mod - use fsundials_matrix_mod + use fsundials_core_mod use fnvector_serial_mod use fsunmatrix_band_mod use fsunmatrix_dense_mod diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 59c57f3d5..afaff5d1e 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -685,7 +685,7 @@ integer(c_int) function eval8summa4kinsol(sunvec_y, sunvec_r, user_data) & !======= Inclusions =========== use, intrinsic :: iso_c_binding - use fsundials_nvector_mod + use fsundials_core_mod use fnvector_serial_mod use type4kinsol diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 59639ad94..76b236c86 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -664,7 +664,7 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user !======= Inclusions =========== use, intrinsic :: iso_c_binding - use fsundials_nvector_mod + use fsundials_core_mod use fnvector_serial_mod use type4ida diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 5ca842400..6fe1543e7 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -134,17 +134,13 @@ subroutine summaSolve4ida( & !======= Inclusions =========== USE fida_mod ! Fortran interface to IDA - USE fsundials_context_mod ! Fortran interface to SUNContext + USE fsundials_core_mod ! Fortran interface to SUNContext USE fnvector_serial_mod ! Fortran interface to serial N_Vector - USE fsundials_nvector_mod ! Fortran interface to generic N_Vector USE fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix USE fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix - USE fsundials_matrix_mod ! Fortran interface to generic SUNMatrix USE fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver USE fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver - USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver USE fsunnonlinsol_newton_mod ! Fortran interface to Newton SUNNonlinearSolver - USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver USE allocspace_module,only:allocLocal ! allocate local data structures USE getVectorz_module, only:checkFeas ! check feasibility of state vector USE eval8summaWithPrime_module,only:eval8summa4ida ! DAE/ODE functions @@ -336,7 +332,7 @@ subroutine summaSolve4ida( & dCompress_dPsiPrev(:) = 0._rkind resVecPrev(:) = 0._rkind - retval = FSUNContext_Create(c_null_ptr, sunctx) + retval = FSUNContext_Create(SUN_COMM_NULL, sunctx) ! create serial vectors sunvec_y => FN_VMake_Serial(nState, stateVec, sunctx) @@ -437,7 +433,8 @@ subroutine summaSolve4ida( & ! Disable error messages and warnings if(offErrWarnMessage) then - retval = FIDASetErrFile(ida_mem, c_null_ptr) + retval = FSUNLogger_SetErrorFilename(ida_mem, c_null_char) + retval = FSUNLogger_SetWarningFilename(ida_mem, c_null_char) retval = FIDASetNoInactiveRootWarn(ida_mem) endif @@ -627,7 +624,7 @@ subroutine setInitialCondition(neq, y, sunvec_u, sunvec_up) !======= Inclusions =========== USE, intrinsic :: iso_c_binding - USE fsundials_nvector_mod + USE fsundials_core_mod USE fnvector_serial_mod !======= Declarations ========= @@ -722,7 +719,7 @@ subroutine find_rootdir(eqns_data,rootdir) !======= Inclusions =========== use, intrinsic :: iso_c_binding - use fsundials_nvector_mod + use fsundials_core_mod use fnvector_serial_mod use soil_utils_module,only:crit_soilT ! compute the critical temperature below which ice exists use globalData,only:integerMissing ! missing integer @@ -805,7 +802,7 @@ integer(c_int) function layerDisCont4ida(t, sunvec_u, sunvec_up, gout, user_data !======= Inclusions =========== use, intrinsic :: iso_c_binding - use fsundials_nvector_mod + use fsundials_core_mod use fnvector_serial_mod use soil_utils_module,only:crit_soilT ! compute the critical temperature below which ice exists use globalData,only:integerMissing ! missing integer diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index b63c2d281..3b5269f56 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -129,15 +129,12 @@ subroutine summaSolve4kinsol(& !======= Inclusions =========== USE fkinsol_mod ! Fortran interface to KINSOL - USE fsundials_context_mod ! Fortran interface to SUNContext + USE fsundials_core_mod ! Fortran interface to SUNContext USE fnvector_serial_mod ! Fortran interface to serial N_Vector - USE fsundials_nvector_mod ! Fortran interface to generic N_Vector USE fsunmatrix_dense_mod ! Fortran interface to dense SUNMatrix USE fsunmatrix_band_mod ! Fortran interface to banded SUNMatrix - USE fsundials_matrix_mod ! Fortran interface to generic SUNMatrix USE fsunlinsol_dense_mod ! Fortran interface to dense SUNLinearSolver USE fsunlinsol_band_mod ! Fortran interface to banded SUNLinearSolver - USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver USE allocspace_module,only:allocLocal ! allocate local data structures USE getVectorz_module,only:checkFeas ! check feasibility of state vector USE eval8summa_module,only:eval8summa4kinsol ! DAE/ODE functions @@ -273,7 +270,7 @@ subroutine summaSolve4kinsol(& allocate( eqns_data%resVec(nState) ) allocate( eqns_data%resSink(nState) ) - retval = FSUNContext_Create(c_null_ptr, sunctx) + retval = FSUNContext_Create(SUN_COMM_NULL, sunctx) ! create serial vectors sunvec_y => FN_VMake_Serial(nState, stateVec, sunctx) @@ -342,7 +339,8 @@ subroutine summaSolve4kinsol(& ! Disable error messages and warnings if(offErrWarnMessage) then - retval = FKINSetErrFile(kinsol_mem, c_null_ptr) + retval = FSUNLogger_SetErrorFilename(kinsol_mem, c_null_char) + retval = FSUNLogger_SetWarningFilename(kinsol_mem, c_null_char) endif !****************************** Main Solver ********************************************** @@ -424,7 +422,7 @@ subroutine setInitialCondition(neq, y, sunvec_u) !======= Inclusions =========== USE, intrinsic :: iso_c_binding - USE fsundials_nvector_mod + USE fsundials_core_mod USE fnvector_serial_mod !======= Declarations ========= diff --git a/build/source/engine/tol4ida.f90 b/build/source/engine/tol4ida.f90 index c3cc7fc7c..45d126060 100644 --- a/build/source/engine/tol4ida.f90 +++ b/build/source/engine/tol4ida.f90 @@ -79,7 +79,7 @@ integer(c_int) function computWeight4ida(sunvec_y, sunvec_ewt, user_data) & !======= Inclusions =========== use, intrinsic :: iso_c_binding - use fsundials_nvector_mod + use fsundials_core_mod use fnvector_serial_mod use nrtype use type4ida From 970c8695351ab089a59bdf1fcaf375f70064c867 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 2 Mar 2024 03:07:15 -0600 Subject: [PATCH 1172/1472] Applied object-oriented methods to handle scalar intent(in) summaSolve4numrec arguments. --- build/source/engine/summaSolve4numrec.f90 | 355 +++++++++++++--------- build/source/engine/systemSolv.f90 | 18 +- 2 files changed, 206 insertions(+), 167 deletions(-) diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 3ef22531e..01380aba6 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -101,20 +101,21 @@ module summaSolve4numrec_module ! public subroutine summaSolve4numrec: calculate the iteration increment, evaluate the new state, and refine if necessary ! *********************************************************************************************************************** subroutine summaSolve4numrec(& + in_SS4NR, & ! intent(in): model control and previous function value ! input: model control - dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate - iter, & ! intent(in): iteration index - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - nLeadDim, & ! intent(in): length of the leading dimension of the Jacobian matrix (either nBands or nState) - nState, & ! intent(in): total number of state variables - ixMatrix, & ! intent(in): type of matrix (full or band diagonal) - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + ! dt_cur, & ! intent(in): current stepsize + ! dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate + ! iter, & ! intent(in): iteration index + ! nSnow, & ! intent(in): number of snow layers + ! nSoil, & ! intent(in): number of soil layers + ! nLayers, & ! intent(in): total number of layers + ! nLeadDim, & ! intent(in): length of the leading dimension of the Jacobian matrix (either nBands or nState) + ! nState, & ! intent(in): total number of state variables + ! ixMatrix, & ! intent(in): type of matrix (full or band diagonal) + ! firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + ! scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVecTrial, & ! intent(in): trial state vector xMin,xMax, & ! intent(inout): brackets of the root @@ -123,7 +124,7 @@ subroutine summaSolve4numrec(& rVec, & ! intent(in): residual vector sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout): diagonal matrix (excludes flux derivatives) - fOld, & ! intent(in): old function evaluation + ! fOld, & ! intent(in): old function evaluation ! input: data structures model_decisions, & ! intent(in): model decisions lookup_data, & ! intent(in): lookup tables @@ -153,22 +154,22 @@ subroutine summaSolve4numrec(& USE matrixOper_module, only: scaleMatrices implicit none ! -------------------------------------------------------------------------------------------------------------------------------- - type(in_type_summaSolve4numrec) :: in_SS4NR ! model control variables and previous function evaluation + type(in_type_summaSolve4numrec),intent(in) :: in_SS4NR ! model control variables and previous function evaluation type(io_type_summaSolve4numrec) :: io_SS4NR ! first flux call flag and baseflow variables ! input: model control - real(rkind),intent(in) :: dt_cur ! current stepsize - real(rkind),intent(in) :: dt ! entire time step for drainage pond rate - integer(i4b),intent(in) :: iter ! iteration index - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - integer(i4b),intent(in) :: nLeadDim ! length of the leading dimension of the Jacobian matrix (nBands or nState) - integer(i4b),intent(in) :: nState ! total number of state variables - integer(i4b),intent(in) :: ixMatrix ! type of matrix (full or band diagonal) - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step +! real(rkind),intent(in) :: dt_cur ! current stepsize +! real(rkind),intent(in) :: dt ! entire time step for drainage pond rate +! integer(i4b),intent(in) :: iter ! iteration index +! integer(i4b),intent(in) :: nSnow ! number of snow layers +! integer(i4b),intent(in) :: nSoil ! number of soil layers +! integer(i4b),intent(in) :: nLayers ! total number of layers +! integer(i4b),intent(in) :: nLeadDim ! length of the leading dimension of the Jacobian matrix (nBands or nState) +! integer(i4b),intent(in) :: nState ! total number of state variables +! integer(i4b),intent(in) :: ixMatrix ! type of matrix (full or band diagonal) +! logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution +! logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation +! logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector real(rkind),intent(inout) :: xMin,xMax ! brackets of the root @@ -177,7 +178,7 @@ subroutine summaSolve4numrec(& real(qp),intent(in) :: rVec(:) ! NOTE: qp ! residual vector real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(rkind),intent(inout) :: dMat(:) ! diagonal matrix (excludes flux derivatives) - real(rkind),intent(in) :: fOld ! old function evaluation +! real(rkind),intent(in) :: fOld ! old function evaluation ! input: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions type(zLookup), intent(in) :: lookup_data ! lookup tables @@ -207,13 +208,13 @@ subroutine summaSolve4numrec(& ! Jacobian matrix logical(lgt),parameter :: doNumJacobian=.false. ! flag to compute the numerical Jacobian matrix logical(lgt),parameter :: testBandDiagonal=.false. ! flag to test the band diagonal Jacobian matrix - real(rkind) :: nJac(nState,nState) ! numerical Jacobian matrix - real(rkind) :: aJac(nLeadDim,nState) ! Jacobian matrix - real(rkind) :: aJacScaled(nLeadDim,nState) ! Jacobian matrix (scaled) - real(rkind) :: aJacScaledTemp(nLeadDim,nState) ! Jacobian matrix (scaled) -- temporary copy since decomposed in lapack + real(rkind) :: nJac(in_SS4NR % nState,in_SS4NR % nState) ! numerical Jacobian matrix + real(rkind) :: aJac(in_SS4NR % nLeadDim,in_SS4NR % nState) ! Jacobian matrix + real(rkind) :: aJacScaled(in_SS4NR % nLeadDim,in_SS4NR % nState) ! Jacobian matrix (scaled) + real(rkind) :: aJacScaledTemp(in_SS4NR % nLeadDim,in_SS4NR % nState) ! Jacobian matrix (scaled) -- temporary copy since decomposed in lapack ! solution/step vectors - real(rkind),dimension(nState) :: rVecScaled ! residual vector (scaled) - real(rkind),dimension(nState) :: newtStepScaled ! full newton step (scaled) + real(rkind),dimension(in_SS4NR % nState) :: rVecScaled ! residual vector (scaled) + real(rkind),dimension(in_SS4NR % nState) :: newtStepScaled ! full newton step (scaled) ! step size refinement logical(lgt) :: doRefine ! flag for step refinement integer(i4b),parameter :: ixLineSearch=1001 ! step refinement = line search @@ -314,8 +315,10 @@ subroutine solve_linear_system ! *** Solve the linear system for the Newton step using LAPACK routines *** associate(& - err => out_SS4NR % err ,& - message => out_SS4NR % message & + nState => in_SS4NR % nState ,& ! intent(in): total number of state variables + ixMatrix => in_SS4NR % ixMatrix ,& ! intent(in): type of matrix (full or band diagonal) + err => out_SS4NR % err ,& ! intent(out): error code + message => out_SS4NR % message & ! intent(out): error message &) ! test the band diagonal matrix if (testBandDiagonal) then @@ -346,10 +349,10 @@ subroutine solve_linear_system call lapackSolv(ixMatrix,nState,aJacScaledTemp,-rVecScaled,newtStepScaled,err,cmessage) if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors + ! debug statement for Newton step + if (globalPrintFlag) write(*,'(a,1x,10(e17.10,1x))') 'newtStepScaled = ', newtStepScaled(min(iJac1,nState):min(iJac2,nState)) + end associate - - ! debug statement for Newton step - if (globalPrintFlag) write(*,'(a,1x,10(e17.10,1x))') 'newtStepScaled = ', newtStepScaled(min(iJac1,nState):min(iJac2,nState)) end subroutine solve_linear_system subroutine refine_Newton_step @@ -363,6 +366,7 @@ subroutine refine_Newton_step ! NOTE: in 99.9% of cases newtStep will be used (no refinement) associate(& + fOld => in_SS4NR % fOld ,& fnew => out_SS4NR % fnew ,& converged => out_SS4NR % converged,& err => out_SS4NR % err ,& @@ -421,31 +425,36 @@ subroutine lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,r type(out_type_lineSearchRefinement),intent(out) :: out_LSR ! class object for intent(out) arguments ! -------------------------------------------------------------------------------------------------------- ! local - character(len=256) :: cmessage ! error message of downwind routine - real(rkind) :: gradScaled(nState) ! scaled gradient - real(rkind) :: xInc(nState) ! iteration increment (re-scaled to original units of the state vector) - logical(lgt) :: feasible ! flag to denote the feasibility of the solution - integer(i4b) :: iLine ! line search index - integer(i4b),parameter :: maxLineSearch=5 ! maximum number of backtracks - real(rkind),parameter :: alpha=1.e-4_rkind ! check on gradient - real(rkind) :: xLambda ! backtrack magnitude - real(rkind) :: xLambdaTemp ! temporary backtrack magnitude - real(rkind) :: slopeInit ! initial slope - real(rkind) :: rhs1,rhs2 ! rhs used to compute the cubic - real(rkind) :: aCoef,bCoef ! coefficients in the cubic - real(rkind) :: disc ! temporary variable used in cubic - real(rkind) :: xLambdaPrev ! previous lambda value (used in the cubic) - real(rkind) :: fPrev ! previous function evaluation (used in the cubic) + character(len=256) :: cmessage ! error message of downwind routine + real(rkind) :: gradScaled(in_SS4NR % nState) ! scaled gradient + real(rkind) :: xInc(in_SS4NR % nState) ! iteration increment (re-scaled to original units of the state vector) + logical(lgt) :: feasible ! flag to denote the feasibility of the solution + integer(i4b) :: iLine ! line search index + integer(i4b),parameter :: maxLineSearch=5 ! maximum number of backtracks + real(rkind),parameter :: alpha=1.e-4_rkind ! check on gradient + real(rkind) :: xLambda ! backtrack magnitude + real(rkind) :: xLambdaTemp ! temporary backtrack magnitude + real(rkind) :: slopeInit ! initial slope + real(rkind) :: rhs1,rhs2 ! rhs used to compute the cubic + real(rkind) :: aCoef,bCoef ! coefficients in the cubic + real(rkind) :: disc ! temporary variable used in cubic + real(rkind) :: xLambdaPrev ! previous lambda value (used in the cubic) + real(rkind) :: fPrev ! previous function evaluation (used in the cubic) ! -------------------------------------------------------------------------------------------------------- associate(& - ! intent(in) variables - doLineSearch => in_LSR % doSearch ,& ! flag to do the line search - fOld => in_LSR % fOld ,& ! old function value - ! intent(out) variables - fNew => out_LSR % fNew ,& ! new function evaluation - converged => out_LSR % converged ,& ! convergence flag - err => out_LSR % err ,& ! error code - message => out_LSR % message & ! error message + ! intent(in) variables + doLineSearch => in_LSR % doSearch ,& ! flag to do the line search + fOld => in_LSR % fOld ,& ! old function value + ! local variables + nSnow => in_SS4NR % nSnow ,& ! number of snow layers + nSoil => in_SS4NR % nSoil ,& ! number of soil layers + nState => in_SS4NR % nState ,& ! total number of state variables + ixMatrix => in_SS4NR % ixMatrix ,& ! type of matrix (full or band diagonal) + ! intent(out) variables + fNew => out_LSR % fNew ,& ! new function evaluation + converged => out_LSR % converged ,& ! convergence flag + err => out_LSR % err ,& ! error code + message => out_LSR % message & ! error message &) ! initialize error control err=0; message='lineSearchRefinement/' @@ -604,6 +613,7 @@ subroutine trustRegionRefinement(in_TRR,stateVecTrial,newtStepScaled,aJacScaled, ! input doTrustRefinement => in_TRR % doSearch ,& ! flag to refine using trust regions fOld => in_TRR % fOld ,& ! old function value + nState => in_SS4NR % nState ,& ! total number of state variables ! output fNew => out_TRR % fNew ,& ! new function evaluation converged => out_TRR % converged ,& ! convergence flag @@ -671,8 +681,8 @@ subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fl character(len=256) :: cmessage ! error message of downwind routine real(rkind),parameter :: relTolerance=0.005_rkind ! force bi-section if trial is slightly larger than (smaller than) xmin (xmax) real(rkind) :: xTolerance ! relTolerance*(xmax-xmin) - real(rkind) :: xInc(nState) ! iteration increment (re-scaled to original units of the state vector) - real(rkind) :: rVec(nState) ! residual vector (re-scaled to original units of the state equation) + real(rkind) :: xInc(in_SS4NR % nState) ! iteration increment (re-scaled to original units of the state vector) + real(rkind) :: rVec(in_SS4NR % nState) ! residual vector (re-scaled to original units of the state equation) logical(lgt) :: feasible ! feasibility of the solution logical(lgt) :: doBisection ! flag to do the bi-section logical(lgt) :: bracketsDefined ! flag to define if the brackets are defined @@ -680,10 +690,14 @@ subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fl real(rkind),parameter :: delX=1._rkind ! trial increment ! -------------------------------------------------------------------------------------------------------- associate(& - fNew => out_SRF % fNew ,& ! new function evaluation - converged => out_SRF % converged ,& ! convergence flag - err => out_SRF % err ,& ! error code - message => out_SRF % message & ! error message + iter => in_SS4NR % iter ,& ! intent(in): iteration index + nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers + nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers + nState => in_SS4NR % nState ,& ! intent(in): total number of state + fNew => out_SRF % fNew ,& ! intent(out): new function evaluation + converged => out_SRF % converged ,& ! intent(out): convergence flag + err => out_SRF % err ,& ! intent(out): error code + message => out_SRF % message & ! intent(out): error message &) err=0; message='safeRootfinder/' @@ -776,12 +790,12 @@ subroutine getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,message) integer(i4b),intent(inout) :: err ! error code character(*),intent(out) :: message ! error message ! locals - real(rkind) :: stateVecPrev(nState) ! iteration state vector - integer(i4b) :: iCheck ! check the model state variables - integer(i4b),parameter :: nCheck=100 ! number of times to check the model state variables - logical(lgt) :: feasible ! feasibility of the solution - real(rkind),parameter :: delX=1._rkind ! trial increment - real(rkind) :: xIncrement(nState) ! trial increment + real(rkind) :: stateVecPrev(in_SS4NR % nState) ! iteration state vector + integer(i4b) :: iCheck ! check the model state variables + integer(i4b),parameter :: nCheck=100 ! number of times to check the model state variables + logical(lgt) :: feasible ! feasibility of the solution + real(rkind),parameter :: delX=1._rkind ! trial increment + real(rkind) :: xIncrement(in_SS4NR % nState) ! trial increment ! initialize err=0; message='getBrackets/' @@ -799,7 +813,13 @@ subroutine getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,message) stateVecNew = stateVecPrev + xIncrement ! impose solution constraints adjusting state vector and iteration increment - call imposeConstraints(model_decisions,indx_data,prog_data,mpar_data,stateVecNew,stateVecPrev,nState,nSoil,nSnow,cmessage,err) + associate(& + nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers + nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers + nState => in_SS4NR % nState & ! intent(in): total number of state variables + &) + call imposeConstraints(model_decisions,indx_data,prog_data,mpar_data,stateVecNew,stateVecPrev,nState,nSoil,nSnow,cmessage,err) + end associate if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) xIncrement = stateVecNew - stateVecPrev @@ -852,9 +872,9 @@ subroutine numJacobian(stateVec,dMat,nJac,err,message) ! local character(len=256) :: cmessage ! error message of downwind routine real(rkind),parameter :: dx=1.e-8_rkind ! finite difference increment - real(rkind),dimension(nState) :: stateVecPerturbed ! perturbed state vector - real(rkind),dimension(nState) :: fluxVecInit,fluxVecJac ! flux vector (mized units) - real(qp),dimension(nState) :: resVecInit,resVecJac ! qp ! residual vector (mixed units) + real(rkind),dimension(in_SS4NR % nState) :: stateVecPerturbed ! perturbed state vector + real(rkind),dimension(in_SS4NR % nState) :: fluxVecInit,fluxVecJac ! flux vector (mized units) + real(qp),dimension(in_SS4NR % nState) :: resVecInit,resVecJac ! qp ! residual vector (mixed units) real(rkind) :: func ! function value logical(lgt) :: feasible ! flag to denote the feasibility of the solution integer(i4b) :: iJac ! index of row of the Jacobian matrix @@ -874,7 +894,7 @@ subroutine numJacobian(stateVec,dMat,nJac,err,message) stateVecPerturbed(:) = stateVec(:) ! loop through state variables - do iJac=1,nState + do iJac=1,in_SS4NR % nState !print*, 'iJac = ', iJac !globalPrintFlag = merge(.true.,.false., iJac==1) @@ -891,7 +911,7 @@ subroutine numJacobian(stateVec,dMat,nJac,err,message) ! compute the row of the Jacobian matrix select case(ixNumType) case(ixNumRes); nJac(:,iJac) = real(resVecJac - resVecInit, kind(rkind) )/dx ! Jacobian based on residuals - case(ixNumFlux); nJac(:,iJac) = -dt_cur*(fluxVecJac(:) - fluxVecInit(:))/dx ! Jacobian based on fluxes + case(ixNumFlux); nJac(:,iJac) = -in_SS4NR % dt_cur*(fluxVecJac(:) - fluxVecInit(:))/dx ! Jacobian based on fluxes case default; err=20; message=trim(message)//'Jacobian option not found'; return end select @@ -905,9 +925,9 @@ subroutine numJacobian(stateVec,dMat,nJac,err,message) ! print the Jacobian print*, '** numerical Jacobian:', ixNumType==ixNumRes - write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) - do iLayer=min(iJac1,nState),min(iJac2,nState) - write(*,'(i4,1x,100(e12.5,1x))') iLayer, nJac(min(iJac1,nState):min(iJac2,nState),iLayer) + write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=min(iJac1,in_SS4NR % nState),min(iJac2,in_SS4NR % nState)) + do iLayer=min(iJac1,in_SS4NR % nState),min(iJac2,in_SS4NR % nState) + write(*,'(i4,1x,100(e12.5,1x))') iLayer, nJac(min(iJac1,in_SS4NR % nState):min(iJac2,in_SS4NR % nState),iLayer) end do !print*, 'PAUSE: testing Jacobian'; read(*,*) @@ -923,8 +943,8 @@ subroutine testBandMat(check,err,message) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local variables - real(rkind) :: fullJac(nState,nState) ! full Jacobian matrix - real(rkind) :: bandJac(nLeadDim,nState) ! band Jacobian matrix + real(rkind) :: fullJac(in_SS4NR % nState,in_SS4NR % nState) ! full Jacobian matrix + real(rkind) :: bandJac(in_SS4NR % nLeadDim,in_SS4NR % nState) ! band Jacobian matrix integer(i4b) :: iState,jState ! indices of the state vector character(LEN=256) :: cmessage ! error message of downwind routine ! class objects for subroutine arguments @@ -934,24 +954,24 @@ subroutine testBandMat(check,err,message) err=0; message='testBandMat/' ! check - if(nLeadDim==nState)then + if (in_SS4NR % nLeadDim == in_SS4NR % nState) then message=trim(message)//'do not expect nLeadDim==nState: check that are computing the band diagonal matrix'//& ' (is forceFullMatrix==.true.?)' err=20; return - endif + end if ! compute the full Jacobian matrix call initialize_computJacob_testBandMat call computJacob(in_computJacob,indx_data,prog_data,diag_data,deriv_data,dBaseflow_dMatric,dMat,fullJac,out_computJacob) call finalize_computJacob_testBandMat(err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! initialize band matrix bandJac(:,:) = 0._rkind ! transfer into the lapack band diagonal structure - do iState=1,nState - do jState=max(1,iState-ku),min(nState,iState+kl) + do iState=1,in_SS4NR % nState + do jState=max(1,iState-ku),min(in_SS4NR % nState,iState+kl) bandJac(kl + ku + 1 + jState - iState, iState) = fullJac(jState,iState) end do end do @@ -959,7 +979,7 @@ subroutine testBandMat(check,err,message) ! print results print*, '** test banded analytical Jacobian:' write(*,'(a4,1x,100(i17,1x))') 'xCol', (iState, iState=iJac1,iJac2) - do iState=kl+1,nLeadDim; write(*,'(i4,1x,100(e17.10,1x))') iState, bandJac(iState,iJac1:iJac2); end do + do iState=kl+1,in_SS4NR % nLeadDim; write(*,'(i4,1x,100(e17.10,1x))') iState, bandJac(iState,iJac1:iJac2); end do ! check if the need to pause if(check)then @@ -971,7 +991,15 @@ end subroutine testBandMat subroutine initialize_computJacob_testBandMat ! *** Transfer data from out_computJacob class object to local variables in testBandMat *** - call in_computJacob % initialize(dt_cur,nSnow,nSoil,nLayers,computeVegFlux,.false.,ixFullMatrix) + associate(& + dt_cur => in_SS4NR % dt_cur ,& ! intent(in): current stepsize + nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers + nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers + nLayers => in_SS4NR % nLayers ,& ! intent(in): total number of layers + computeVegFlux => in_SS4NR % computeVegFlux & ! intent(in): flag to indicate if computing fluxes over vegetation + &) + call in_computJacob % initialize(dt_cur,nSnow,nSoil,nLayers,computeVegFlux,.false.,ixFullMatrix) + end associate end subroutine initialize_computJacob_testBandMat subroutine finalize_computJacob_testBandMat(err,cmessage) @@ -984,7 +1012,15 @@ end subroutine finalize_computJacob_testBandMat subroutine initialize_computJacob_summaSolve4numrec ! *** Transfer data to in_computJacob class object from local variables in summaSolve4numrec *** - associate(ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision) ! intent(in): [i4b] groundwater parameterization + associate(& + ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision,& ! intent(in): [i4b] groundwater parameterization + dt_cur => in_SS4NR % dt_cur ,& ! intent(in): current stepsize + nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers + nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers + nLayers => in_SS4NR % nLayers ,& ! intent(in): total number of layers + ixMatrix => in_SS4NR % ixMatrix ,& ! intent(in): type of matrix (full or band diagonal) + computeVegFlux => in_SS4NR % computeVegFlux & ! intent(in): flag to indicate if computing fluxes over vegetation + &) call in_computJacob % initialize(dt_cur,nSnow,nSoil,nLayers,computeVegFlux,(ixGroundwater==qbaseTopmodel),ixMatrix) end associate end subroutine initialize_computJacob_summaSolve4numrec @@ -1020,51 +1056,64 @@ subroutine eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err ! initialize error control err=0; message='eval8summa_wrapper/' + associate(& + dt_cur => in_SS4NR % dt_cur ,& ! intent(in): current stepsize + dt => in_SS4NR % dt ,& ! intent(in): entire time step for drainage pond rate + nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers + nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers + nLayers => in_SS4NR % nLayers ,& ! intent(in): total number of layers + nState => in_SS4NR % nState ,& ! intent(in): total number of state variables + firstSubStep => in_SS4NR % firstSubStep ,& ! intent(in): flag to indicate if we are processing the first sub-step + computeVegFlux => in_SS4NR % computeVegFlux ,& ! intent(in): flag to indicate if computing fluxes over vegetation + scalarSolution => in_SS4NR % scalarSolution & ! intent(in): flag to denote if implementing the scalar solution + &) ! compute the flux and the residual vector for a given state vector - call eval8summa(& - ! input: model control - dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - nState, & ! intent(in): total number of state variables - .false., & ! intent(in): not inside Sundials solver - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - .false., & ! intent(in): not processing the first iteration in a splitting operation - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVecNew, & ! intent(in): updated model state vector - fScale, & ! intent(in): characteristic scale of the function evaluations - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - ! output - feasible, & ! intent(out): flag to denote the feasibility of the solution - fluxVecNew, & ! intent(out): new flux vector - resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equation - resVecNew, & ! intent(out): new residual vector - fNew, & ! intent(out): new function evaluation - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + call eval8summa(& + ! input: model control + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + nState, & ! intent(in): total number of state variables + .false., & ! intent(in): not inside Sundials solver + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + .false., & ! intent(in): not processing the first iteration in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVecNew, & ! intent(in): updated model state vector + fScale, & ! intent(in): characteristic scale of the function evaluations + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + indx_data, & ! intent(inout): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + ! output + feasible, & ! intent(out): flag to denote the feasibility of the solution + fluxVecNew, & ! intent(out): new flux vector + resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equation + resVecNew, & ! intent(out): new residual vector + fNew, & ! intent(out): new function evaluation + err,cmessage) ! intent(out): error control + end associate + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + end subroutine eval8summa_wrapper @@ -1097,22 +1146,26 @@ function checkConv(rVec,xInc,xVec) ! ------------------------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures associate(& - ! convergence parameters - absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1) ,& ! intent(in): [dp] absolute convergence tolerance for vol frac liq water (-) - absConvTol_matric => mpar_data%var(iLookPARAM%absConvTol_matric)%dat(1) ,& ! intent(in): [dp] absolute convergence tolerance for matric head (m) - absConvTol_energy => mpar_data%var(iLookPARAM%absConvTol_energy)%dat(1) ,& ! intent(in): [dp] absolute convergence tolerance for energy (J m-3) - ! layer depth - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! model indices - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of aquifer storage state variable - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for all energy states - ixHydOnly => indx_data%var(iLookINDEX%ixHydOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for all hydrology states - ixMatOnly => indx_data%var(iLookINDEX%ixMatOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for matric head state variables in the state vector - ixMatricHead => indx_data%var(iLookINDEX%ixMatricHead)%dat ,& ! intent(in): [i4b(:)] list of indices for matric head in the soil vector - fnew => out_SS4NR % fnew & ! intent(in): [dp] new function evaluations + ! model control + iter => in_SS4NR % iter ,& ! intent(in): iteration index + nsnow => in_SS4NR % nsnow ,& ! intent(in): number of snow layers + scalarSolution => in_SS4NR % scalarSolution ,& ! intent(in): flag to denote if implementing the scalar solution + ! convergence parameters + absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1),& ! intent(in): [dp] absolute convergence tolerance for vol frac liq water (-) + absConvTol_matric => mpar_data%var(iLookPARAM%absConvTol_matric)%dat(1),& ! intent(in): [dp] absolute convergence tolerance for matric head (m) + absConvTol_energy => mpar_data%var(iLookPARAM%absConvTol_energy)%dat(1),& ! intent(in): [dp] absolute convergence tolerance for energy (J m-3) + ! layer depth + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! model indices + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of aquifer storage state variable + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for all energy states + ixHydOnly => indx_data%var(iLookINDEX%ixHydOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for all hydrology states + ixMatOnly => indx_data%var(iLookINDEX%ixMatOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for matric head state variables in the state vector + ixMatricHead => indx_data%var(iLookINDEX%ixMatricHead)%dat ,& ! intent(in): [i4b(:)] list of indices for matric head in the soil vector + fnew => out_SS4NR % fnew & ! intent(in): [dp] new function evaluations ) ! making associations with variables in the data structures ! ------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index d3807234b..b6fff93ba 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -671,21 +671,11 @@ subroutine systemSolv(& do iter=1,localMaxIter ! keep track of the number of iterations niter = iter+1 ! +1 because xFluxResid was moved outside the iteration loop (for backwards compatibility) + call in_SS4NR % initialize(dt_cur,dt,iter,nSnow,nSoil,nLayers,nLeadDim,nState,ixMatrix,firstSubStep,computeVegFlux,scalarSolution,fOld) call summaSolve4numrec(& + in_SS4NR,& ! input: model control - dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate - iter, & ! intent(in): iteration index - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - nLeadDim, & ! intent(in): length of the leading dimension of the Jacobian matrix (either nBands or nState) - nState, & ! intent(in): total number of state variables - ixMatrix, & ! intent(in): type of matrix (full or band diagonal) - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution ! input: state vectors stateVecTrial, & ! intent(in): trial state vector xMin,xMax, & ! intent(inout): state maximum and minimum @@ -694,7 +684,6 @@ subroutine systemSolv(& rVec, & ! intent(in): residual vector sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout): diagonal matrix (excludes flux derivatives) - fOld, & ! intent(in): old function evaluation ! input: data structures model_decisions, & ! intent(in): model decisions lookup_data, & ! intent(in): lookup tables @@ -718,9 +707,6 @@ subroutine systemSolv(& resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equa resVecNew, & ! intent(out): new residual vector out_SS4NR) - !fNew, & ! intent(out): new function evaluation - !converged, & ! intent(out): convergence flag - !err,cmessage) ! intent(out): error control call out_SS4NR % finalize(fNew,converged,err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) From 5eccc1137b3424ac4fb0b310f465758b6e999547 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 4 Mar 2024 21:37:43 +0900 Subject: [PATCH 1173/1472] utils and typo in summaSolve4ida, no code changes --- build/source/dshare/type4cvode.f90 | 78 +++++++++++ build/source/engine/summaSolve4ida.f90 | 9 +- utils/bal_per_GRU.py | 2 +- utils/process_concat.sh | 5 - utils/process_diff.sh | 5 - utils/process_hist.sh | 5 - utils/process_plot.sh | 7 - utils/process_scat.sh | 5 - utils/process_stat.sh | 17 ++- utils/process_statBatch.sh | 30 ++++ utils/timeseries_to_statistics.py | 185 ++++++++++++++++--------- 11 files changed, 237 insertions(+), 111 deletions(-) create mode 100644 build/source/dshare/type4cvode.f90 create mode 100755 utils/process_statBatch.sh diff --git a/build/source/dshare/type4cvode.f90 b/build/source/dshare/type4cvode.f90 new file mode 100644 index 000000000..93e3dafd5 --- /dev/null +++ b/build/source/dshare/type4cvode.f90 @@ -0,0 +1,78 @@ +module type4cvode + +! data types +USE nrtype +USE, intrinsic :: iso_c_binding + +! provide access to the derived types to define the data structures +USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! data vector with variable length dimension (rkind) + model_options ! defines the model decisions + +implicit none + +type data4cvode + type(c_ptr) :: cvode_mem ! CVode memory + real(rkind) :: dt ! data step + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nSoil ! number of soil layers + integer(i4b) :: nLayers ! total number of layers + integer(i4b) :: nState ! total number of state variables + integer(i4b) :: ixMatrix ! form of matrix (dense or banded) + logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution + type(model_options),allocatable :: model_decisions(:) ! model decisions + type(var_i) :: type_data ! type of vegetation and soil + type(var_d) :: attr_data ! spatial attributes + type(var_dlength) :: mpar_data ! model parameters + type(var_d) :: forc_data ! model forcing data + type(var_dlength) :: bvar_data ! model variables for the local basin + type(var_dlength) :: prog_data ! prognostic variables for a local HRU + type(var_ilength) :: indx_data ! indices defining model states and layers + type(var_dlength) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength) :: flux_data ! model fluxes for a local HRU + type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + real(qp), allocatable :: sMul(:) ! state vector multiplier (used in the residual calculations) + real(rkind), allocatable :: dMat(:) ! diagonal of the Jacobian matrix + real(rkind), allocatable :: fluxVec(:) ! flux vector + real(qp), allocatable :: resVec(:) ! residual vector + real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation + real(rkind), allocatable :: atol(:) ! vector of absolute tolerances + real(rkind), allocatable :: rtol(:) ! vector of relative tolerances + integer(i4b) :: ixSaturation ! index of the lowest saturated layer + real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + integer(i4b) :: err ! error code + character(len=50) :: message ! error message + real(rkind) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) + real(rkind), allocatable :: mLayerTempPrev(:) ! previous vector of layer temperature (K) + real(rkind), allocatable :: mLayerMatricHeadPrev(:) ! previous value for total water matric potential (m) + real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatTrial ! trial value for mass of total water on the vegetation canopy (kg m-2) + real(rkind), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind), allocatable :: mLayerMatricHeadTrial(:) ! trial value for total water matric potential (m) + real(rkind), allocatable :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(rkind) :: scalarCanairTempPrime ! prime value for temperature of the canopy air space (K s-1) + real(rkind) :: scalarCanopyTempPrime ! prime value for temperature of the vegetation canopy (K s-1) + real(rkind) :: scalarCanopyWatPrime ! prime value for mass of total water on the vegetation canopy (kg m-2 s-1) + real(rkind) :: scalarCanopyIcePrime ! prime value for mass of ice on the vegetation canopy (kg m-2 s-1) + real(rkind), allocatable :: mLayerTempPrime(:) ! prime vector of temperature of each snow and soil layer (K s-1) + real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! prime vector of matric head of each snow and soil layer (m s-1) + real(rkind), allocatable :: mLayerMatricHeadLiqPrime(:) ! prime vector of liquid matric head of each snow and soil layer (m s-1) + real(rkind), allocatable :: mLayerVolFracWatPrime(:) ! prime vector of volumetric total water content of each snow and soil layer (s-1) + real(rkind), allocatable :: mLayerVolFracIcePrime(:) ! prime vector of volumetric fraction of ice (s-1) + end type data4cvode + + +end module type4cvode + + + + + diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 6fe1543e7..e7df99cdc 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -144,7 +144,6 @@ subroutine summaSolve4ida( & USE allocspace_module,only:allocLocal ! allocate local data structures USE getVectorz_module, only:checkFeas ! check feasibility of state vector USE eval8summaWithPrime_module,only:eval8summa4ida ! DAE/ODE functions - USE eval8summaWithPrime_module,only:eval8summaWithPrime ! residual of DAE USE computJacobWithPrime_module,only:computJacob4ida ! system Jacobian USE tol4ida_module,only:computWeight4ida ! weight required for tolerances USE var_lookup,only:maxvarDecisions ! maximum number of decisions @@ -268,7 +267,7 @@ subroutine summaSolve4ida( & nState = nStat ! total number of state variables in SUNDIALS type idaSucceeds = .true. - ! fill eqns_data which will be required later to call eval8summaWithPrime + ! fill eqns_data which will be required later to call eval8summa4ida eqns_data%dt = dt eqns_data%nSnow = nSnow eqns_data%nSoil = nSoil @@ -513,7 +512,7 @@ subroutine summaSolve4ida( & !------------------------ if(computNrgBalance)then - ! compute energy balance mean, resVec is the instanteous residual vector from the solver + ! compute energy balance mean, resVec is the instantaneous residual vector from the solver if(ixCasNrg/=integerMissing) balance(ixCasNrg) = balance(ixCasNrg) + ( eqns_data%resVec(ixCasNrg) + resVecPrev(ixCasNrg) )*dt_diff/2._rkind/dt if(ixVegNrg/=integerMissing) balance(ixVegNrg) = balance(ixVegNrg) + ( eqns_data%resVec(ixVegNrg) + resVecPrev(ixVegNrg) )*dt_diff/2._rkind/dt if(nSnowSoilNrg>0)then @@ -528,7 +527,7 @@ subroutine summaSolve4ida( & !------------------------ if(computMassBalance)then - ! compute mass balance mean, resVec is the instanteous residual vector from the solver + ! compute mass balance mean, resVec is the instantaneous residual vector from the solver if(ixVegHyd/=integerMissing) balance(ixVegHyd) = balance(ixVegHyd) + ( eqns_data%resVec(ixVegHyd) + resVecPrev(ixVegHyd) )*dt_diff/2._rkind/dt if(nSnowSoilHyd>0)then do concurrent (i=1:nLayers,ixSnowSoilHyd(i)/=integerMissing) @@ -549,7 +548,7 @@ subroutine summaSolve4ida( & ! Restart for where vegetation and layers cross freezing point if(detect_events)then - if (retvalr .eq. IDA_ROOT_RETURN) then !IDASolve succeeded and found one or more roots at tret(1) + if (retvalr .eq. IDA_ROOT_RETURN) then ! IDASolve succeeded and found one or more roots at tret(1) ! rootsfound[i]= +1 indicates that gi is increasing, -1 g[i] decreasing, 0 no root !retval = FIDAGetRootInfo(ida_mem, rootsfound) !if (retval < 0) then; err=20; message=trim(message)//'error in FIDAGetRootInfo'; return; endif diff --git a/utils/bal_per_GRU.py b/utils/bal_per_GRU.py index 77f3eced1..54dea2f64 100644 --- a/utils/bal_per_GRU.py +++ b/utils/bal_per_GRU.py @@ -22,7 +22,7 @@ viz_dir = Path('/home/avanb/scratch/statistics') -testing = True +testing = False if testing: stat = 'mean' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') diff --git a/utils/process_concat.sh b/utils/process_concat.sh index c27aa669c..2e9681583 100755 --- a/utils/process_concat.sh +++ b/utils/process_concat.sh @@ -13,10 +13,5 @@ module load gcc/9.3.0 module load geo-stack virtualenv --no-download $SLURM_TMPDIR/env source $SLURM_TMPDIR/env/bin/activate -pip install --no-index --upgrade pip -pip install --no-index h5netcdf -pip install --no-index h5py -pip install --no-index xarray -pip install --no-index netCDF4 python concat_groups_split_summa.py sundials_1en8 diff --git a/utils/process_diff.sh b/utils/process_diff.sh index 13ea8b667..b87378487 100755 --- a/utils/process_diff.sh +++ b/utils/process_diff.sh @@ -12,10 +12,5 @@ module load gcc/9.3.0 module load geo-stack virtualenv --no-download $SLURM_TMPDIR/env source $SLURM_TMPDIR/env/bin/activate -pip install --no-index --upgrade pip -pip install --no-index h5netcdf -pip install --no-index h5py -pip install --no-index xarray -pip install --no-index netCDF4 python check_bit_4_bit_withTol.py /home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/summa-sundials_be_noJac/ /home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/summa-be/ 0.1 > out.txt diff --git a/utils/process_hist.sh b/utils/process_hist.sh index 50797616a..f1fe485c6 100755 --- a/utils/process_hist.sh +++ b/utils/process_hist.sh @@ -13,11 +13,6 @@ module load gcc/9.3.0 module load geo-stack virtualenv --no-download $SLURM_TMPDIR/env source $SLURM_TMPDIR/env/bin/activate -pip install --no-index --upgrade pip -pip install --no-index h5netcdf -pip install --no-index h5py -pip install --no-index xarray -pip install --no-index netCDF4 python hist_per_GRU.py rmse python hist_per_GRU.py maxe diff --git a/utils/process_plot.sh b/utils/process_plot.sh index de600efef..3ce813a7f 100755 --- a/utils/process_plot.sh +++ b/utils/process_plot.sh @@ -13,13 +13,6 @@ module load gcc/9.3.0 module load geo-stack virtualenv --no-download $SLURM_TMPDIR/env source $SLURM_TMPDIR/env/bin/activate -pip install --no-index --upgrade pip -pip install --no-index pyproj -pip install --no-index geopandas -pip install --no-index h5netcdf -pip install --no-index h5py -pip install --no-index xarray -pip install --no-index netCDF4 python plot_per_GRU.py sundials_1en6 rmse python plot_per_GRU.py sundials_1en6 maxe diff --git a/utils/process_scat.sh b/utils/process_scat.sh index 545704344..713eef1b6 100755 --- a/utils/process_scat.sh +++ b/utils/process_scat.sh @@ -13,11 +13,6 @@ module load gcc/9.3.0 module load geo-stack virtualenv --no-download $SLURM_TMPDIR/env source $SLURM_TMPDIR/env/bin/activate -pip install --no-index --upgrade pip -pip install --no-index h5netcdf -pip install --no-index h5py -pip install --no-index xarray -pip install --no-index netCDF4 python scat_per_GRU.py rmse python scat_per_GRU.py maxe diff --git a/utils/process_stat.sh b/utils/process_stat.sh index 249c1de80..77b9e08ec 100755 --- a/utils/process_stat.sh +++ b/utils/process_stat.sh @@ -13,13 +13,12 @@ module load gcc/9.3.0 module load geo-stack virtualenv --no-download $SLURM_TMPDIR/env source $SLURM_TMPDIR/env/bin/activate -pip install --no-index --upgrade pip -pip install --no-index h5netcdf -pip install --no-index h5py -pip install --no-index xarray -pip install --no-index netCDF4 -python timeseries_to_statistics.py sundials_1en6 -python timeseries_to_statistics.py be1 -python timeseries_to_statistics.py be32 -python timeseries_to_statistics.py be16 +python timeseries_to_statistics.py sundials_1en6 1 1 +python timeseries_to_statistics.py sundials_1en6 2 1 +python timeseries_to_statistics.py be1 1 1 +python timeseries_to_statistics.py be1 2 1 +python timeseries_to_statistics.py be32 1 1 +python timeseries_to_statistics.py be32 2 1 +python timeseries_to_statistics.py be16 1 1 +python timeseries_to_statistics.py be16 2 1 diff --git a/utils/process_statBatch.sh b/utils/process_statBatch.sh new file mode 100755 index 000000000..add1b197a --- /dev/null +++ b/utils/process_statBatch.sh @@ -0,0 +1,30 @@ +#!/bin/bash +#SBATCH --ntasks-per-node=1 +#SBATCH --cpus-per-task=1 +#SBATCH --mem-per-cpu=1GB +#SBATCH --time=0-01:00 +#SBATCH --job-name=STATB +#SBATCH --mail-user=gwu479@usask.ca +#SBATCH --mail-type=ALL +#SBATCH --account=rpp-kshook +#SBATCH --output=/home/avanb/TestScripts/output/slurm-%A_%a.out +echo "$SLURM_ARRAY_TASK_ID" + +# ---------------------------------------------------------------------------------------------- +# RUN WITH: +# sbatch --array1-[number of jobs] [script name] +# sbatch --array=1-200 process_statBatch.sh +# sbatch --array=201-201 process_statBatch.sh +# ---------------------------------------------------------------------------------------------- + +module load gcc/9.3.0 +module load geo-stack +virtualenv --no-download $SLURM_TMPDIR/env +source $SLURM_TMPDIR/env/bin/activate + +ibatch=$SLURM_ARRAY_TASK_ID + +python timeseries_to_statistics.py sundials_1en6 ibatch 100 +python timeseries_to_statistics.py be1 ibatch 200 +python timeseries_to_statistics.py be32 ibatch 200 +python timeseries_to_statistics.py be16 ibatch 200 diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 46e9b1325..4c00698f7 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -14,7 +14,8 @@ # This results in KGE values that range between -1 and 1, with lower KGE values indicating larger differences from bench. # Run: -# python timeseries_to_statistics.py sundials_1en6 +# python timeseries_to_statistics.py sundials_1en6 [1-101] 100 +# and run 100 times with different batch numbers 1-100, and then merge the files with 101 import os import glob @@ -28,7 +29,7 @@ # Settings bench_name = 'sundials_1en8' -not_parallel = False # should usually be false, runs faster +not_parallel = True # run as true with batch mode, or false, with `python timeseries_to_statistics.py sundials_1en6 1 1` for single batch, and `python timeseries_to_statistics.py sundials_1en6 2 1` to merge testing = False # which statistics to compute @@ -36,14 +37,18 @@ do_steps = False do_balance = True -if testing: +if testing: + not_parallel = True + method_name ='be1en' + ibatch = 1 + nbatch = 2 top_fold = '/Users/amedin/Research/USask/test_py/' - method_name='be1en' - not_parallel = True else: import sys # The first input argument specifies the run where the files are method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) + ibatch = sys.argv[2] + nbatch = sys.argv[3] top_fold = '/home/avanb/scratch/' des_dir = top_fold + 'statistics_temp' @@ -77,6 +82,7 @@ # Construct the path to the processed_files.txt file processed_files_path = os.path.join(des_dir, 'processed_files.txt') +processed_files_path0 = os.path.join(des_dir, 'processed_files' + str(ibatch) + '.txt') # Get the names of all inputs, assuming folders have same splits of domains and same file names src_files = glob.glob(str( src_dir / src_pat )) @@ -85,7 +91,6 @@ ben_files = glob.glob(str( ben_dir / src_pat )) ben_files.sort() - # Load the list of files that have already been processed if os.path.exists(processed_files_path): with open(processed_files_path, 'r') as f: @@ -93,18 +98,10 @@ else: processed_files = [] - # Filter out the files that have already been processed src_files = [f for f in src_files if f not in processed_files] if do_vars: ben_files = [f for f in ben_files if f not in processed_files] -# definitions for KGE computation -def covariance(x,y,dims=None): - return xr.dot(x-x.mean(dims), y-y.mean(dims), dims=dims) / x.count(dims) - -def correlation(x,y,dims=None): - return (covariance(x,y,dims)) / (x.std(dims) * y.std(dims)) - if do_vars: assert len(ben_files) == len(src_files), \ 'Found {} files but need {}!'.format(len(src_files), len(ben_files)) @@ -121,6 +118,14 @@ def correlation(x,y,dims=None): # print('Error opening file:', file, bench) # -- functions + +# definitions for KGE computation +def covariance(x,y,dims=None): + return xr.dot(x-x.mean(dims), y-y.mean(dims), dims=dims) / x.count(dims) + +def correlation(x,y,dims=None): + return (covariance(x,y,dims)) / (x.std(dims) * y.std(dims)) + def run_loop(file,bench,processed_files_path): # extract the subset IDs @@ -179,13 +184,11 @@ def run_loop(file,bench,processed_files_path): mnnz_ben = datnz.mean(dim='time') mnnz_ben = mnnz_ben.expand_dims("stat").assign_coords(stat=("stat",["mnnz_ben"])) - na_mx = np.fabs(dat[var]).max()+1 - amx = np.fabs(dat[var].fillna(na_mx)).argmax(dim=['time']) + amx = np.fabs(dat[var]).fillna(-1).argmax(dim=['time']) # fill nan with neg value so will not choose amax = dat[var].isel(amx).drop_vars('time') amax = amax.expand_dims("stat").assign_coords(stat=("stat",["amax"])) - na_mx = np.fabs(ben[var]).max()+1 - amx = np.fabs(ben[var].fillna(na_mx)).argmax(dim=['time']) + amx = np.fabs(ben[var]).fillna(-1).argmax(dim=['time']) # fill nan with neg value so will not choose amax_ben = ben[var].isel(amx).drop_vars('time') amax_ben = amax_ben.expand_dims("stat").assign_coords(stat=("stat",["amax_ben"])) @@ -196,8 +199,7 @@ def run_loop(file,bench,processed_files_path): rmnz = (np.square(diffnz).mean(dim='time'))**(1/2) rmnz = rmnz.expand_dims("stat").assign_coords(stat=("stat",["rmnz"])) - na_mx = np.fabs(diff[var]).max()+1 - amx = np.fabs(diff[var].fillna(na_mx)).argmax(dim=['time']) + amx = np.fabs(diff[var]).fillna(-1).argmax(dim=['time']) # fill nan with neg value so will not choose maxe = diff[var].isel(amx).drop_vars('time') maxe = maxe.expand_dims("stat").assign_coords(stat=("stat",["maxe"])) @@ -222,8 +224,7 @@ def run_loop(file,bench,processed_files_path): mean = dat[var].mean(dim='time') mean = mean.expand_dims("stat").assign_coords(stat=("stat",["mean"])) - na_mx = np.fabs(dat[var]).max()+1 - amx = np.fabs(dat[var].fillna(na_mx)).argmax(dim=['time']) + amx = np.fabs(dat[var]).fillna(-1).argmax(dim=['time']) # fill nan with neg value so will not choose amax = dat[var].isel(amx).drop_vars('time') amax = amax.expand_dims("stat").assign_coords(stat=("stat",["amax"])) @@ -235,28 +236,25 @@ def run_loop(file,bench,processed_files_path): mean = np.fabs(dat[var]).mean(dim='time') # this is actually absolute value mean mean = mean.expand_dims("stat").assign_coords(stat=("stat",["mean"])) - na_mx = np.fabs(dat[var]).max()+1 - amx = np.fabs(dat[var].fillna(na_mx)).argmax(dim=['time']) + amx = np.fabs(dat[var]).fillna(-1).argmax(dim=['time']) # fill nan with neg value so will not choose amax = dat[var].isel(amx).drop_vars('time') amax = amax.expand_dims("stat").assign_coords(stat=("stat",["amax"])) new = xr.merge([mean,amax]) new.to_netcdf(des_dir / des_fl3.format(var,subset)) - - # write the name of the processed file to the file list, acquire the lock before opening the file + # write the name of the processed file to the file list, acquire the lock before opening the file if not_parallel: - with open(processed_files_path, 'a') as filew: + with open(processed_files_path0, 'a') as filew: filew.write(file + '\n') if do_vars: filew.write(bench + '\n') else: import multiprocessing as mp lock = mp.Lock() with lock: - with open(processed_files_path, 'a') as filew: + with open(processed_files_path0, 'a') as filew: filew.write(file + '\n') if do_vars: filew.write(bench + '\n') - filew.close() # close the file after writing to it return #nothing @@ -278,49 +276,98 @@ def merge_subsets_into_one(src,pattern,des,name): return #nothing # -- end functions +# do batches +nf = len(src_files) +start = min((int(ibatch)-1)*np.ceil(nf/nbatch), nf) +end = min(int(ibatch)*np.ceil(nf/nbatch), nf) +do_f = range(int(start), int(end)) +if ibatch == nbatch: + do_f = range(int(start), nf) + +if ibatch > nbatch: + print('Batch number greater than number of batches, doing file merge') + # Initialize an empty list to store the file contents + contents = [] + + # Loop over the batch numbers + for iibatch in range(1, nbatch + 1): + # Construct the file path + processed_files_path0 = os.path.join(des_dir, 'processed_files' + str(iibatch) + '.txt') + + # Check if the file exists + if os.path.exists(processed_files_path0): + # Open the file and read its contents + with open(processed_files_path0, 'r') as file: + contents.append(file.read()) -if not_parallel: - # -- no parallel processing - if do_vars: - for (file, bench) in zip(src_files,ben_files): - run_loop(file,bench,processed_files_path) + # Join the contents into a single string + contents = '\n'.join(contents) + + # Append the contents to processed_files_path + with open(processed_files_path, 'a') as filew: + filew.write(contents + '\n') + + with open(processed_files_path, 'r') as f: + processed_files = f.read().splitlines() + + # Filter out the files that have already been processed + src_files = [f for f in src_files if f not in processed_files] + if do_vars: ben_files = [f for f in ben_files if f not in processed_files] + + # merge the individual files into one for further vizualization + # remove the individual files for cleanliness + if len(src_files) != 0: + print('Some files have not been processed') + print(src_files) + exit() else: - for (file, bench) in zip(src_files,src_files): - run_loop(file,bench,processed_files_path) - # -- end no parallel processing + if do_vars: + merge_subsets_into_one(des_dir,des_fil.replace('{}','*'),fnl_dir,viz_fil) + for file in glob.glob(str(des_dir / des_fil.replace('{}','*'))): + os.remove(file) + if do_steps: + merge_subsets_into_one(des_dir,des_fl2.replace('{}','*'),fnl_dir,viz_fl2) + for file in glob.glob(str(des_dir / des_fl2.replace('{}','*'))): + os.remove(file) + if do_balance: + merge_subsets_into_one(des_dir,des_fl3.replace('{}','*'),fnl_dir,viz_fl3) + for file in glob.glob(str(des_dir / des_fl3.replace('{}','*'))): + os.remove(file) else: - # -- start parallel processing - ncpus = int(os.environ.get('SLURM_CPUS_PER_TASK',default=1)) - if __name__ == "__main__": - import multiprocessing as mp - pool = mp.Pool(processes=ncpus) - with open(processed_files_path, 'a') as f: + # do the batch + src_files = [src_files[i] for i in do_f] + if do_vars: ben_files = [ben_files[i] for i in do_f] + + if len(do_f) > 0: + if not_parallel: + # -- no parallel processing if do_vars: - results = [pool.apply_async(run_loop, args=(file,bench,processed_files_path)) for (file, bench) in zip(src_files, ben_files)] + for (file, bench) in zip(src_files,ben_files): + run_loop(file,bench,processed_files_path) else: - results = [pool.apply_async(run_loop, args=(file,bench,processed_files_path)) for (file, bench) in zip(src_files, src_files)] - for r in results: - try: - r.get() - except Exception as e: - print(f"Error processing file: {e}") - raise e - pool.close() - # -- end parallel processing - - -# merge the individual files into one for further vizualization -# remove the individual files for cleanliness -if do_vars: - merge_subsets_into_one(des_dir,des_fil.replace('{}','*'),fnl_dir,viz_fil) - for file in glob.glob(str(des_dir / des_fil.replace('{}','*'))): - os.remove(file) -if do_steps: - merge_subsets_into_one(des_dir,des_fl2.replace('{}','*'),fnl_dir,viz_fl2) - for file in glob.glob(str(des_dir / des_fl2.replace('{}','*'))): - os.remove(file) -if do_balance: - merge_subsets_into_one(des_dir,des_fl3.replace('{}','*'),fnl_dir,viz_fl3) - for file in glob.glob(str(des_dir / des_fl3.replace('{}','*'))): - os.remove(file) \ No newline at end of file + for (file, bench) in zip(src_files,src_files): + run_loop(file,bench,processed_files_path) + # -- end no parallel processing + + else: + # -- start parallel processing + ncpus = int(os.environ.get('SLURM_CPUS_PER_TASK',default=1)) + if __name__ == "__main__": + import multiprocessing as mp + pool = mp.Pool(processes=ncpus) + with open(processed_files_path, 'a') as f: + if do_vars: + results = [pool.apply_async(run_loop, args=(file,bench,processed_files_path)) for (file, bench) in zip(src_files, ben_files)] + else: + results = [pool.apply_async(run_loop, args=(file,bench,processed_files_path)) for (file, bench) in zip(src_files, src_files)] + for r in results: + try: + r.get() + except Exception as e: + print(f"Error processing file: {e}") + raise e + pool.close() + # -- end parallel processing + +# -- end script \ No newline at end of file From dce7f40986b67faa69b8e84db18ac465d925068f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 4 Mar 2024 21:41:27 +0900 Subject: [PATCH 1174/1472] remove file that should not be added, comment clean up. no code changes --- build/source/dshare/type4cvode.f90 | 78 ----------------------- build/source/engine/summaSolve4ida.f90 | 2 +- build/source/engine/summaSolve4kinsol.f90 | 2 +- 3 files changed, 2 insertions(+), 80 deletions(-) delete mode 100644 build/source/dshare/type4cvode.f90 diff --git a/build/source/dshare/type4cvode.f90 b/build/source/dshare/type4cvode.f90 deleted file mode 100644 index 93e3dafd5..000000000 --- a/build/source/dshare/type4cvode.f90 +++ /dev/null @@ -1,78 +0,0 @@ -module type4cvode - -! data types -USE nrtype -USE, intrinsic :: iso_c_binding - -! provide access to the derived types to define the data structures -USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! data vector with variable length dimension (rkind) - model_options ! defines the model decisions - -implicit none - -type data4cvode - type(c_ptr) :: cvode_mem ! CVode memory - real(rkind) :: dt ! data step - integer(i4b) :: nSnow ! number of snow layers - integer(i4b) :: nSoil ! number of soil layers - integer(i4b) :: nLayers ! total number of layers - integer(i4b) :: nState ! total number of state variables - integer(i4b) :: ixMatrix ! form of matrix (dense or banded) - logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution - type(model_options),allocatable :: model_decisions(:) ! model decisions - type(var_i) :: type_data ! type of vegetation and soil - type(var_d) :: attr_data ! spatial attributes - type(var_dlength) :: mpar_data ! model parameters - type(var_d) :: forc_data ! model forcing data - type(var_dlength) :: bvar_data ! model variables for the local basin - type(var_dlength) :: prog_data ! prognostic variables for a local HRU - type(var_ilength) :: indx_data ! indices defining model states and layers - type(var_dlength) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength) :: flux_data ! model fluxes for a local HRU - type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - real(qp), allocatable :: sMul(:) ! state vector multiplier (used in the residual calculations) - real(rkind), allocatable :: dMat(:) ! diagonal of the Jacobian matrix - real(rkind), allocatable :: fluxVec(:) ! flux vector - real(qp), allocatable :: resVec(:) ! residual vector - real(qp), allocatable :: resSink(:) ! additional (sink) terms on the RHS of the state equation - real(rkind), allocatable :: atol(:) ! vector of absolute tolerances - real(rkind), allocatable :: rtol(:) ! vector of relative tolerances - integer(i4b) :: ixSaturation ! index of the lowest saturated layer - real(rkind), allocatable :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - integer(i4b) :: err ! error code - character(len=50) :: message ! error message - real(rkind) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) - real(rkind), allocatable :: mLayerTempPrev(:) ! previous vector of layer temperature (K) - real(rkind), allocatable :: mLayerMatricHeadPrev(:) ! previous value for total water matric potential (m) - real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatTrial ! trial value for mass of total water on the vegetation canopy (kg m-2) - real(rkind), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind), allocatable :: mLayerMatricHeadTrial(:) ! trial value for total water matric potential (m) - real(rkind), allocatable :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(rkind) :: scalarCanairTempPrime ! prime value for temperature of the canopy air space (K s-1) - real(rkind) :: scalarCanopyTempPrime ! prime value for temperature of the vegetation canopy (K s-1) - real(rkind) :: scalarCanopyWatPrime ! prime value for mass of total water on the vegetation canopy (kg m-2 s-1) - real(rkind) :: scalarCanopyIcePrime ! prime value for mass of ice on the vegetation canopy (kg m-2 s-1) - real(rkind), allocatable :: mLayerTempPrime(:) ! prime vector of temperature of each snow and soil layer (K s-1) - real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! prime vector of matric head of each snow and soil layer (m s-1) - real(rkind), allocatable :: mLayerMatricHeadLiqPrime(:) ! prime vector of liquid matric head of each snow and soil layer (m s-1) - real(rkind), allocatable :: mLayerVolFracWatPrime(:) ! prime vector of volumetric total water content of each snow and soil layer (s-1) - real(rkind), allocatable :: mLayerVolFracIcePrime(:) ! prime vector of volumetric fraction of ice (s-1) - end type data4cvode - - -end module type4cvode - - - - - diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index e7df99cdc..49ef600c1 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -88,7 +88,7 @@ module summaSolve4ida_module ! ************************************************************************************ ! * public subroutine summaSolve4ida: solve F(y,y') = 0 by IDA (y is the state vector) ! ************************************************************************************ -subroutine summaSolve4ida( & +subroutine summaSolve4ida(& dt_cur, & ! intent(in): current stepsize dt, & ! intent(in): data time step atol, & ! intent(in): absolute tolerance diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index 3b5269f56..bea546ad3 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -113,7 +113,7 @@ subroutine summaSolve4kinsol(& bvar_data, & ! intent(in): average model variables for the entire basin prog_data, & ! intent(in): model prognostic variables for a local HRU ! input-output: data structures - indx_data, & ! intent(inout): index data diag_data, & ! intent(inout): model diagnostic variables for a local HRU + indx_data, & ! intent(inout): index data diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables From 1daa0a38a93aee1f35a00dd4f301199ab79171fd Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 4 Mar 2024 23:44:50 +0900 Subject: [PATCH 1175/1472] utils --- utils/process_statBatch.sh | 12 +++++------- utils/timeseries_to_statistics.py | 23 ++++++++++++++--------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/utils/process_statBatch.sh b/utils/process_statBatch.sh index add1b197a..772dafee6 100755 --- a/utils/process_statBatch.sh +++ b/utils/process_statBatch.sh @@ -1,7 +1,7 @@ #!/bin/bash #SBATCH --ntasks-per-node=1 #SBATCH --cpus-per-task=1 -#SBATCH --mem-per-cpu=1GB +#SBATCH --mem-per-cpu=12GB #SBATCH --time=0-01:00 #SBATCH --job-name=STATB #SBATCH --mail-user=gwu479@usask.ca @@ -22,9 +22,7 @@ module load geo-stack virtualenv --no-download $SLURM_TMPDIR/env source $SLURM_TMPDIR/env/bin/activate -ibatch=$SLURM_ARRAY_TASK_ID - -python timeseries_to_statistics.py sundials_1en6 ibatch 100 -python timeseries_to_statistics.py be1 ibatch 200 -python timeseries_to_statistics.py be32 ibatch 200 -python timeseries_to_statistics.py be16 ibatch 200 +python timeseries_to_statistics.py sundials_1en6 $SLURM_ARRAY_TASK_ID 200 +python timeseries_to_statistics.py be1 $SLURM_ARRAY_TASK_ID 200 +python timeseries_to_statistics.py be32 $SLURM_ARRAY_TASK_ID 200 +python timeseries_to_statistics.py be16 $SLURM_ARRAY_TASK_ID 200 diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 4c00698f7..53ef57242 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -47,11 +47,16 @@ import sys # The first input argument specifies the run where the files are method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) - ibatch = sys.argv[2] - nbatch = sys.argv[3] + ibatch = int(sys.argv[2]) + nbatch = int(sys.argv[3]) top_fold = '/home/avanb/scratch/' -des_dir = top_fold + 'statistics_temp' +des_dir = top_fold + 'statistics_temp_' + method_name +# Check if the directory exists +if not os.path.exists(des_dir): + # If not, create the directory + os.mkdir(des_dir) + fnl_dir = top_fold + 'statistics' src_dir = top_fold + 'summa-' + method_name ben_dir = top_fold + 'summa-' + bench_name @@ -126,7 +131,7 @@ def covariance(x,y,dims=None): def correlation(x,y,dims=None): return (covariance(x,y,dims)) / (x.std(dims) * y.std(dims)) -def run_loop(file,bench,processed_files_path): +def run_loop(file,bench,processed_files_path0): # extract the subset IDs subset = file.split('/')[-1].split('_')[1] @@ -344,10 +349,10 @@ def merge_subsets_into_one(src,pattern,des,name): # -- no parallel processing if do_vars: for (file, bench) in zip(src_files,ben_files): - run_loop(file,bench,processed_files_path) + run_loop(file,bench,processed_files_path0) else: for (file, bench) in zip(src_files,src_files): - run_loop(file,bench,processed_files_path) + run_loop(file,bench,processed_files_path0) # -- end no parallel processing else: @@ -356,11 +361,11 @@ def merge_subsets_into_one(src,pattern,des,name): if __name__ == "__main__": import multiprocessing as mp pool = mp.Pool(processes=ncpus) - with open(processed_files_path, 'a') as f: + with open(processed_files_path0, 'a') as f: if do_vars: - results = [pool.apply_async(run_loop, args=(file,bench,processed_files_path)) for (file, bench) in zip(src_files, ben_files)] + results = [pool.apply_async(run_loop, args=(file,bench,processed_files_path0)) for (file, bench) in zip(src_files, ben_files)] else: - results = [pool.apply_async(run_loop, args=(file,bench,processed_files_path)) for (file, bench) in zip(src_files, src_files)] + results = [pool.apply_async(run_loop, args=(file,bench,processed_files_path0)) for (file, bench) in zip(src_files, src_files)] for r in results: try: r.get() From 1906a78badcb8f958d6a1b2deac0fbfe9824c177 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 5 Mar 2024 11:22:10 +0900 Subject: [PATCH 1176/1472] utils --- utils/process_statBatch.sh | 2 +- utils/timeseries_to_statistics.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/utils/process_statBatch.sh b/utils/process_statBatch.sh index 772dafee6..c91057a18 100755 --- a/utils/process_statBatch.sh +++ b/utils/process_statBatch.sh @@ -2,7 +2,7 @@ #SBATCH --ntasks-per-node=1 #SBATCH --cpus-per-task=1 #SBATCH --mem-per-cpu=12GB -#SBATCH --time=0-01:00 +#SBATCH --time=0-05:00 #SBATCH --job-name=STATB #SBATCH --mail-user=gwu479@usask.ca #SBATCH --mail-type=ALL diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 53ef57242..1ba678415 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -304,13 +304,17 @@ def merge_subsets_into_one(src,pattern,des,name): # Open the file and read its contents with open(processed_files_path0, 'r') as file: contents.append(file.read()) + os.remove(processed_files_path0) # Join the contents into a single string contents = '\n'.join(contents) + # Remove blank lines + contents = '\n'.join(line for line in contents.split('\n') if line.strip()) + # Append the contents to processed_files_path with open(processed_files_path, 'a') as filew: - filew.write(contents + '\n') + filew.write(contents) with open(processed_files_path, 'r') as f: processed_files = f.read().splitlines() @@ -324,7 +328,7 @@ def merge_subsets_into_one(src,pattern,des,name): if len(src_files) != 0: print('Some files have not been processed') print(src_files) - exit() + else: if do_vars: merge_subsets_into_one(des_dir,des_fil.replace('{}','*'),fnl_dir,viz_fil) From b25de3ffff7c6c1518056fdb3b21ee25a4f58dd4 Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 5 Mar 2024 06:50:15 -0600 Subject: [PATCH 1177/1472] Applied intent(inout) summaSolve4numrec class to systemSolv. --- build/source/engine/summaSolve4numrec.f90 | 45 ++++++----------------- build/source/engine/systemSolv.f90 | 9 +++-- 2 files changed, 18 insertions(+), 36 deletions(-) diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 01380aba6..cac0cdd1c 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -103,28 +103,15 @@ module summaSolve4numrec_module subroutine summaSolve4numrec(& in_SS4NR, & ! intent(in): model control and previous function value ! input: model control - ! dt_cur, & ! intent(in): current stepsize - ! dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate - ! iter, & ! intent(in): iteration index - ! nSnow, & ! intent(in): number of snow layers - ! nSoil, & ! intent(in): number of soil layers - ! nLayers, & ! intent(in): total number of layers - ! nLeadDim, & ! intent(in): length of the leading dimension of the Jacobian matrix (either nBands or nState) - ! nState, & ! intent(in): total number of state variables - ! ixMatrix, & ! intent(in): type of matrix (full or band diagonal) - ! firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - ! computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - ! scalarSolution, & ! intent(in): flag to indicate the scalar solution + !firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call ! input: state vectors stateVecTrial, & ! intent(in): trial state vector - xMin,xMax, & ! intent(inout): brackets of the root + !xMin,xMax, & ! intent(inout): brackets of the root fScale, & ! intent(in): characteristic scale of the function evaluations xScale, & ! intent(in): characteristic scale of the state vector rVec, & ! intent(in): residual vector sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout): diagonal matrix (excludes flux derivatives) - ! fOld, & ! intent(in): old function evaluation ! input: data structures model_decisions, & ! intent(in): model decisions lookup_data, & ! intent(in): lookup tables @@ -140,8 +127,9 @@ subroutine summaSolve4numrec(& flux_data, & ! intent(inout): model fluxes for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + !ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) dBaseflow_dMatric, & ! intent(inout): derivative in baseflow w.r.t. matric head (s-1) + io_SS4NR, & ! intent(inout): first flux call flag, root brackets, index of lowest saturated layer ! output stateVecNew, & ! intent(out): new state vector fluxVecNew, & ! intent(out): new flux vector @@ -157,28 +145,15 @@ subroutine summaSolve4numrec(& type(in_type_summaSolve4numrec),intent(in) :: in_SS4NR ! model control variables and previous function evaluation type(io_type_summaSolve4numrec) :: io_SS4NR ! first flux call flag and baseflow variables ! input: model control -! real(rkind),intent(in) :: dt_cur ! current stepsize -! real(rkind),intent(in) :: dt ! entire time step for drainage pond rate -! integer(i4b),intent(in) :: iter ! iteration index -! integer(i4b),intent(in) :: nSnow ! number of snow layers -! integer(i4b),intent(in) :: nSoil ! number of soil layers -! integer(i4b),intent(in) :: nLayers ! total number of layers -! integer(i4b),intent(in) :: nLeadDim ! length of the leading dimension of the Jacobian matrix (nBands or nState) -! integer(i4b),intent(in) :: nState ! total number of state variables -! integer(i4b),intent(in) :: ixMatrix ! type of matrix (full or band diagonal) -! logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call -! logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation -! logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution +! logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call ! input: state vectors real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector - real(rkind),intent(inout) :: xMin,xMax ! brackets of the root +! real(rkind),intent(inout) :: xMin,xMax ! brackets of the root real(rkind),intent(in) :: fScale(:) ! characteristic scale of the function evaluations real(rkind),intent(in) :: xScale(:) ! characteristic scale of the state vector real(qp),intent(in) :: rVec(:) ! NOTE: qp ! residual vector real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(rkind),intent(inout) :: dMat(:) ! diagonal matrix (excludes flux derivatives) -! real(rkind),intent(in) :: fOld ! old function evaluation ! input: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions type(zLookup), intent(in) :: lookup_data ! lookup tables @@ -194,7 +169,7 @@ subroutine summaSolve4numrec(& type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) +! integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) real(rkind),intent(inout) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors real(rkind),intent(out) :: stateVecNew(:) ! new state vector @@ -694,6 +669,8 @@ subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fl nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers nState => in_SS4NR % nState ,& ! intent(in): total number of state + xMin => io_SS4NR % xMin ,& ! intent(inout): bracket of the root + xMax => io_SS4NR % xMax ,& ! intent(inout): bracket of the root fNew => out_SRF % fNew ,& ! intent(out): new function evaluation converged => out_SRF % converged ,& ! intent(out): convergence flag err => out_SRF % err ,& ! intent(out): error code @@ -1065,7 +1042,9 @@ subroutine eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err nState => in_SS4NR % nState ,& ! intent(in): total number of state variables firstSubStep => in_SS4NR % firstSubStep ,& ! intent(in): flag to indicate if we are processing the first sub-step computeVegFlux => in_SS4NR % computeVegFlux ,& ! intent(in): flag to indicate if computing fluxes over vegetation - scalarSolution => in_SS4NR % scalarSolution & ! intent(in): flag to denote if implementing the scalar solution + scalarSolution => in_SS4NR % scalarSolution ,& ! intent(in): flag to denote if implementing the scalar solution + firstFluxCall => io_SS4NR % firstFluxCall ,& ! intent(inout): flag to indicate if we are processing the first flux call + ixSaturation => io_SS4NR % ixSaturation & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) &) ! compute the flux and the residual vector for a given state vector call eval8summa(& diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index b6fff93ba..f1df17573 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -672,13 +672,14 @@ subroutine systemSolv(& ! keep track of the number of iterations niter = iter+1 ! +1 because xFluxResid was moved outside the iteration loop (for backwards compatibility) call in_SS4NR % initialize(dt_cur,dt,iter,nSnow,nSoil,nLayers,nLeadDim,nState,ixMatrix,firstSubStep,computeVegFlux,scalarSolution,fOld) + call io_SS4NR % initialize(firstFluxCall,xMin,xMax,ixSaturation) call summaSolve4numrec(& in_SS4NR,& ! input: model control - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + !firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call ! input: state vectors stateVecTrial, & ! intent(in): trial state vector - xMin,xMax, & ! intent(inout): state maximum and minimum + !xMin,xMax, & ! intent(inout): state maximum and minimum fScale, & ! intent(in): characteristic scale of the function evaluations xScale, & ! intent(in): characteristic scale of the state vector rVec, & ! intent(in): residual vector @@ -699,14 +700,16 @@ subroutine systemSolv(& flux_temp, & ! intent(inout): model fluxes for a local HRU (temporary structure) deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + !ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) dBaseflow_dMatric, & ! intent(inout): derivative in baseflow w.r.t. matric head (s-1) + io_SS4NR,& ! output stateVecNew, & ! intent(out): new state vector fluxVecNew, & ! intent(out): new flux vector resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equa resVecNew, & ! intent(out): new residual vector out_SS4NR) + call io_SS4NR % finalize(firstFluxCall,xMin,xMax,ixSaturation) call out_SS4NR % finalize(fNew,converged,err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) From 550942fda54df6aa9c6aacd5e49bcea3ed0c8e62 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 6 Mar 2024 18:10:05 +0900 Subject: [PATCH 1178/1472] utils --- utils/bal_per_GRU.py | 11 +- utils/plot_per_GRUMult.py | 4 +- utils/plot_per_GRUMultBal.py | 282 ++++++++++++++++++++++++++++++ utils/timeseries_to_statistics.py | 2 +- 4 files changed, 288 insertions(+), 11 deletions(-) create mode 100644 utils/plot_per_GRUMultBal.py diff --git a/utils/bal_per_GRU.py b/utils/bal_per_GRU.py index 54dea2f64..487cb01ae 100644 --- a/utils/bal_per_GRU.py +++ b/utils/bal_per_GRU.py @@ -34,9 +34,6 @@ method_name=['be1','be1en','be1lu'] #maybe make this an argument # Simulation statistics file locations -balssets= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime', 'numberFluxCalc', -'scaledBalanceCasNrg','scaledBalanceVegNrg','scaledBalanceSnowNrg','scaledBalanceSoilNrg','scaledBalanceVegMass','scaledBalanceSnowMass','scaledBalanceSoilMass','scaledBalanceAqMass'] - viz_fil = method_name.copy() viz_fl2 = method_name.copy() for i, m in enumerate(method_name): @@ -51,10 +48,10 @@ plt_titl = ['(a) Canopy Air Space Energy Balance','(b) Vegetation Energy Balance','(c) Snow Energy Balance','(d) Soil Energy Balance', '(e) Wall Clock Time',] plt_titl2 = ['(a) Vegetation Mass Balance','(b) Snow Mass Balance','(c) Soil Mass Balance', '(d) Aquifer Mass Balance'] -leg_titl0 = ['$W~m^{-3}$','$W~m^{-3}$','$W~m^{-3}$','$W~m^{-3}$','$num$'] -leg_titl02 =['$kg~m^{-2}~s^{-1}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}~s^{-1}$','$kg~m^{-2}~s^{-1}$'] -leg_titl = ['$s^{-1}$','$s^{-1}$','$s^{-1}$','$s^{-1}$','$s$'] -leg_titl2 =['$s^{-1}$','$s^{-1}$','$s^{-1}$','$s^{-1}$'] +leg_titl0 = ['$W~m^{-3}$'] * 4 +['$num$'] +leg_titl02 =['$kg~m^{-2}~s^{-1}$'] * 4 +leg_titl = ['$s^{-1}$'] * 4 +['$s$'] +leg_titl2 =['$s^{-1}$'] * 4 #fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' #fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) diff --git a/utils/plot_per_GRUMult.py b/utils/plot_per_GRUMult.py index 8a0c84b3d..e21a127e7 100644 --- a/utils/plot_per_GRUMult.py +++ b/utils/plot_per_GRUMult.py @@ -53,7 +53,6 @@ plot_vars = settings.copy() plt_titl = ['Snow Water Equivalent','Total soil water content','Total evapotranspiration', 'Total water on the vegetation canopy','Average routed runoff','Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] -leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$','$s$'] fig_fil= '_hrly_diff_stats_{}_compressed.png' if do_rel: fig_fil = '_hrly_diff_stats_{}_rel_compressed.png' @@ -312,8 +311,7 @@ def run_loop(j,var,the_max): sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) sm._A = [] cbr = fig.colorbar(sm, cax=cax) #, extend='max') #if max extend can't get title right - if stat == 'rmse' or stat == 'rmnz' or stat == 'mean': cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titl[j]), labelpad=40, rotation=270) - if stat == 'maxe' or stat == 'amax': cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titlm[j]), labelpad=40, rotation=270) + if stat == 'rmse' or stat == 'rmnz' or stat == 'mean' or stat == 'maxe' or stat == 'amax': cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titl[j]), labelpad=40, rotation=270) if stat == 'kgem': cbr.ax.set_ylabel(stat_word, labelpad=40, rotation=270) #if do_rel and var!='wallClockTime': cbr.ax.set_ylabel(stat_word + ' rel to bench ' + statr_word, labelpad=40, rotation=270) if do_rel and var!='wallClockTime': cbr.ax.set_ylabel('relative '+ stat_word, labelpad=40, rotation=270) diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py new file mode 100644 index 000000000..fed58f6f6 --- /dev/null +++ b/utils/plot_per_GRUMultBal.py @@ -0,0 +1,282 @@ +# written originally by W. Knoben, modified by A. Van Beusekom (2023) + +## Visualize statistics per GRU +## Needs: +# Catchment shapefile with GRU delineation +# SUMMA output statistics + +## Special note +# SUMMA simulations have been preprocessed into single value statistics per model element, using auxiliary scripts in ~/utils +# To improve visualization of large lakes, HydroLAKES lake delineations are plotted on top of the catchment GRUs and river segments. +# Dealing with HydroLAKES inputs is not considered within scope of the workflow and therefore requires some manual downloading and preprocessing of this data for those who wish to reproduce this step. +# The relevant code is easily disabled by switching the plot_lakes = True flag to False. + +# Run: +# python plot_per_GRUMult.py [stat] +# where stat is rmse or maxe or kgem + + +# modules +import sys +import os +import matplotlib +import numpy as np +import xarray as xr +from pathlib import Path +import matplotlib.pyplot as plt +import copy +import pyproj +import fiona +import geopandas as gpd +import pandas as pd + +# plot all runs, pick statistic +stat = sys.argv[1] +method_name=['be1','be1en','be1lu'] #maybe make this an argument +plt_name=['(a) SUMMA-BE1','(b) SUMMA-BE1 with enthalpy','(c) SUMMA-BE1 with enthalpy lookup'] #maybe make this an argument + +# Simulation statistics file locations +settings= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime'] + +viz_dir = Path('/home/avanb/scratch/statistics') +viz_fil = method_name.copy() +for i, m in enumerate(method_name): + viz_fil[i] = m + '_hrly_diff_bals_{}.nc' + viz_fil[i] = viz_fil[i].format(','.join(['balance','scaledBalance'])) +do_rel = True # use scaled values +nbatch_hrus = 518 # number of HRUs per batch + +# Specify variables of interest +plot_vars = settings.copy() +plt_titl = ['Canopy Air Space Energy Balance','Vegetation Energy Balance','Snow Energy Balance','Soil Energy Balance','Vegetation Mass Balance','Snow Mass Balance','Soil Mass Balance','Aquifer Mass Balance', 'Wall Clock Time'] +leg_titl = ['$W~m^{-3}$'] * 4 +['$num$'] + ['$kg~m^{-2}~s^{-1}$'] * 4 + +fig_fil= '_hrly_diff_stats_{}_compressed.png' +if do_rel: + fig_fil = '_hrly_diff_stats_{}_rel_compressed.png' + for i in range(8): + settings[i] = 'scaledB' + settings[i][1:] + plt_title[i] = 'Scaled ' + plt_title[i] + leg_titl = ['$s^{-1}$'] * 8 + ['$s$'] + +if stat == 'mean': + maxes = [1e-1,1e3,1e3,1e3]+[1e-9,1e-6,1e-6,1e-8] + [30] + if do_rel: maxes = [1e-2,1e0,1e-2,1e-3]+[1e-7,1e-7,1e-10,1e-7] + [3e-3] +if stat == 'amax': + maxes = [1e0,1e6,1e6,1e5]+[1e-8,1e-3,1e-4,1e-5] + [1e4] + if do_rel: maxes = [1e1,1e3,1e-1,1e1]+[1e-4,1e-5,1e-7,1e-3] + [1e0] + +# Get the albers shapes +main = Path('/home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/albers_projection') + +# Plot lakes? +plot_lakes = True +# lakes shapefile WHERE IS THIS +#lake_path = Path('C:/Globus endpoint/HydroLAKES/HydroLAKES_polys_v10_shp') +#lake_name = 'HydroLAKES_polys_v10_subset_NA.shp' + +## Control file handling +# Store the name of the 'active' file in a variable +controlFile = 'plot_control_NorthAmerica.txt' + +# Function to extract a given setting from the control file +def read_from_control( file, setting ): + + # Open controlFile and ... + with open(file) as contents: + for line in contents: + + # ... find the line with the requested setting + if setting in line and not line.startswith('#'): + break + + # Extract the setting's value + substring = line.split('|',1)[1] # Remove the setting's name (split into 2 based on '|', keep only 2nd part) + substring = substring.split('#',1)[0] # Remove comments, does nothing if no '#' is found + substring = substring.strip() # Remove leading and trailing whitespace, tabs, newlines + + # Return this value + return substring + +# Function to specify a default path +def make_default_path(suffix): + + # Get the root path + rootPath = Path( read_from_control(controlFile,'root_path') ) + + # Get the domain folder + domainName = read_from_control(controlFile,'domain_name') + domainFolder = 'domain_' + domainName + + # Specify the forcing path + defaultPath = rootPath / domainFolder / suffix + + return defaultPath + +## Catchment shapefile location and variable names +# HM catchment shapefile path & name +hm_catchment_path = read_from_control(controlFile,'catchment_shp_path') +hm_catchment_name = read_from_control(controlFile,'catchment_shp_name') +# Specify default path if needed +if hm_catchment_path == 'default': + hm_catchment_path = make_default_path('shapefiles/catchment') # outputs a Path() +else: + hm_catchment_path = Path(hm_catchment_path) # make sure a user-specified path is a Path() + +# Find the GRU and HRU identifiers +hm_hruid = read_from_control(controlFile,'catchment_shp_hruid') + +## River network shapefile location and variable names +# Plot rivers? +plot_rivers = False +# River network path & name +river_network_path = read_from_control(controlFile,'river_network_shp_path') +river_network_name = read_from_control(controlFile,'river_network_shp_name') +# Specify default path if needed +if river_network_path == 'default': + river_network_path = make_default_path('shapefiles/river_network') # outputs a Path() +else: + river_network_path = Path(river_network_path) # make sure a user-specified path is a Path() + +# Find the segment ID +seg_id = read_from_control(controlFile,'river_network_shp_segid') + +## Load all shapefiles and project to Albers Conformal Conic and reproject +# Set the target CRS +acc = 'ESRI:102008' + +# catchment shapefile, first 2 lines throw error so cutting them +#bas = gpd.read_file(hm_catchment_path/hm_catchment_name) +#bas_albers = bas.to_crs(acc) +bas_albers = gpd.read_file(main/'basin.shp') +xmin, ymin, xmax, ymax = bas_albers.total_bounds + +# river network shapefile, first 2 lines throw error so cutting them +if plot_rivers: + #riv = gpd.read_file(river_network_path/river_network_name) + #riv_albers = riv.to_crs(acc) + riv_albers = gpd.read_file(main/'river.shp') + +# lakes shapefile, first 2 lines throw error so cutting them +if plot_lakes: + #lakes = gpd.read_file(lake_path/lake_name) + #lak_albers = lakes.to_crs(acc) + lak_albers = gpd.read_file(main/'lakes.shp') + +## Pre-processing, map SUMMA sims to catchment shapes +# Get the aggregated statistics of SUMMA simulations +summa = {} +eff = {} +for i, m in enumerate(method_name): + # Get the aggregated statistics of SUMMA simulations + summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) + +# Match the accummulated values to the correct HRU IDs in the shapefile +hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile +for plot_var in plot_vars: + stat0 = stat + if stat == 'rmse' or stat == 'kgem' or stat == 'mean': + if plot_var == 'wallClockTime': stat0 = 'mean' + statr = 'mean_ben' + if stat == 'rmnz' or stat == 'mnnz': + if plot_var == 'wallClockTime': stat0 = 'mnnz' + statr = 'mnnz_ben' + if stat == 'maxe' or stat == 'amax': + if plot_var == 'wallClockTime': stat0 = 'amax' + statr = 'amax_ben' + + if do_rel: s_rel = summa[method_name[0]][plot_var].sel(stat=statr) + for m in method_name: + s = summa[m][plot_var].sel(stat=stat0) + + # Make absolute value norm, not all positive + s = np.fabs(s) + + # Replace inf values with NaN in the s DataArray + s = s.where(~np.isinf(s), np.nan) + + bas_albers[plot_var+m] = s.sel(hru=hru_ids_shp.values) + +# Select lakes of a certain size for plotting +if plot_lakes: + minSize = 1000 # km2 + in_domain = (lak_albers['Country'] == 'Canada') | \ + (lak_albers['Country'] == 'United States of America') | \ + (lak_albers['Country'] == 'Mexico') + out_domain = (lak_albers['Pour_long'] > -80) & (lak_albers['Pour_lat'] > 65) # Exclude Baffin Island + large_lakes_albers = lak_albers.loc[(lak_albers['Lake_area'] > minSize) & in_domain & (~out_domain) ] + lake_col = (8/255,81/255,156/255) + +##Figure + +def run_loop(j,var,the_max): + stat0 = stat + + my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap + my_cmap.set_bad(color='white') #nan color white + vmin,vmax = 0, the_max + #if stat =='mean' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 700, the_max + #if stat =='amax' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 1000, the_max + #if var!='wallClockTime' and do_rel: vmin,vmax = 0.9, the_max + + norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) + + if stat0 == 'mean': stat_word = 'mean' + if stat0 == 'amax': stat_word = 'max' + + if statr == 'mean_ben': statr_word = 'mean' + if statr == 'mnnz_ben': statr_word = 'mean' # no 0s' + if statr == 'amax_ben': statr_word = 'max' + + # colorbar axes + f_x_mat = [0.46,0.96,0.46,0.96] + f_y_mat = [0.55,0.55,0.07,0.07] + + for i,(m,f_x,f_y) in enumerate(zip(method_name,f_x_mat,f_y_mat)): + r = i//2 + c = i-r*2 + + # Plot the data with the full extent of the bas_albers shape + bas_albers.plot(ax=axs[r,c], column=var+m, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) + + axs[r,c].set_title(plt_name[i]) + axs[r,c].axis('off') + axs[r,c].set_xlim(xmin, xmax) + axs[r,c].set_ylim(ymin, ymax) + + # Custom colorbar + cax = fig.add_axes([f_x,f_y,0.02,0.375]) + sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) + sm._A = [] + cbr = fig.colorbar(sm, cax=cax) #, extend='max') #if max extend can't get title right + cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titl[j]), labelpad=40, rotation=270) + if do_rel and var!='wallClockTime': cbr.ax.set_ylabel('scaled '+ stat_word, labelpad=40, rotation=270) + + #cbr.ax.yaxis.set_offset_position('right') + + # lakes + if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) + +for i,(var,the_max) in enumerate(zip(plot_vars,maxes)): + + # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug + if 'compressed' in fig_fil: + plt.rcParams.update({'font.size': 25}) + else: + plt.rcParams.update({'font.size': 100}) + + if 'compressed' in fig_fil: + fig,axs = plt.subplots(2,2,figsize=(35,28)) + else: + fig,axs = plt.subplots(2,2,figsize=(140,133)) + fig.suptitle('{} Hourly Statistics'.format(plt_titl[i]), fontsize=40,y=1.05) + + plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines + + plt.tight_layout() + + run_loop(i,var,the_max) + + fig_fil1 = (var+fig_fil).format(stat) + # Save + plt.savefig(viz_dir/fig_fil1, bbox_inches='tight', transparent=True) diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 1ba678415..229a146ba 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -314,7 +314,7 @@ def merge_subsets_into_one(src,pattern,des,name): # Append the contents to processed_files_path with open(processed_files_path, 'a') as filew: - filew.write(contents) + filew.write('\n' + contents) with open(processed_files_path, 'r') as f: processed_files = f.read().splitlines() From ca0d6a361364870a2ae8eade8b51885a575391f7 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 6 Mar 2024 07:08:14 -0600 Subject: [PATCH 1179/1472] Added contains block to systemSolv -- added new internal subroutines to modularize key parts of the numerical recipes Newton solver. --- build/source/engine/systemSolv.f90 | 117 ++++++++++++++--------------- 1 file changed, 55 insertions(+), 62 deletions(-) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index f1df17573..e3c876618 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -264,6 +264,9 @@ subroutine systemSolv(& type(in_type_summaSolve4numrec) :: in_SS4NR ! object for intent(in) summaSolve4numrec arguments type(io_type_summaSolve4numrec) :: io_SS4NR ! object for intent(io) summaSolve4numrec arguments type(out_type_summaSolve4numrec) :: out_SS4NR ! object for intent(out) summaSolve4numrec arguments + ! flags + logical(lgt) :: return_flag ! flag for handling systemSolv returns trigerred from internal subroutines + logical(lgt) :: exit_flag ! flag for handling loop exit statements trigerred from internal subroutines ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- @@ -309,6 +312,7 @@ subroutine systemSolv(& ! --------------------------------------------------------------------------------------- ! initialize error control err=0; message="systemSolv/" + return_flag=.false. ! initialize return flag nSteps = 0 ! initialize number of time steps taken in solver ! ***** @@ -668,68 +672,14 @@ subroutine systemSolv(& ! * solving F(y) = 0 by Backward Euler with free numerical recipes routines, y is the state vector !--------------------------- ! iterate and update trial state vector, fluxes, and derivatives - do iter=1,localMaxIter - ! keep track of the number of iterations - niter = iter+1 ! +1 because xFluxResid was moved outside the iteration loop (for backwards compatibility) - call in_SS4NR % initialize(dt_cur,dt,iter,nSnow,nSoil,nLayers,nLeadDim,nState,ixMatrix,firstSubStep,computeVegFlux,scalarSolution,fOld) - call io_SS4NR % initialize(firstFluxCall,xMin,xMax,ixSaturation) - call summaSolve4numrec(& - in_SS4NR,& - ! input: model control - !firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - ! input: state vectors - stateVecTrial, & ! intent(in): trial state vector - !xMin,xMax, & ! intent(inout): state maximum and minimum - fScale, & ! intent(in): characteristic scale of the function evaluations - xScale, & ! intent(in): characteristic scale of the state vector - rVec, & ! intent(in): residual vector - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - dMat, & ! intent(inout): diagonal matrix (excludes flux derivatives) - ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_temp, & ! intent(inout): model fluxes for a local HRU (temporary structure) - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - !ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(inout): derivative in baseflow w.r.t. matric head (s-1) - io_SS4NR,& - ! output - stateVecNew, & ! intent(out): new state vector - fluxVecNew, & ! intent(out): new flux vector - resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equa - resVecNew, & ! intent(out): new residual vector - out_SS4NR) - call io_SS4NR % finalize(firstFluxCall,xMin,xMax,ixSaturation) - call out_SS4NR % finalize(fNew,converged,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - ! save the computed functions, residuals, and solution - fOld = fNew - rVec = resVecNew - stateVecTrial = stateVecNew - stateVecPrime = stateVecTrial !prime values not used here, dummy - nSteps = 1 ! number of time steps taken in solver - - ! exit iteration loop if converged - if(converged) exit - - ! check convergence - if(iter==localMaxiter)then - message=trim(message)//'failed to converge' - err=-20; return - endif - - end do ! iterating + exit_flag=.false. ! initialize exit flag + do iter=1,localMaxIter ! begin Newton iterations + niter = iter+1 ! # of iterations -- +1 because xFluxResid was moved outside the iteration loop (for backwards compatibility) + call Newton_step; if (return_flag) return ! compute Newton step -- return if error + call check_Newton_convergence ! check current Newton step for convergence + if (exit_flag) exit ! exit loop if convereged + if (return_flag) return ! return if error + end do ! ----- ! * update states... @@ -776,6 +726,49 @@ subroutine systemSolv(& ! end associate statements end associate globalVars +contains + + subroutine Newton_step + ! ** Compute the Newton step using numerical recipes ** + associate(& + ! layer geometry + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) & ! intent(in): [i4b] number of soil layers + ) + call in_SS4NR % initialize(dt_cur,dt,iter,nSnow,nSoil,nLayers,nLeadDim,nState,ixMatrix,firstSubStep,computeVegFlux,scalarSolution,fOld) + call io_SS4NR % initialize(firstFluxCall,xMin,xMax,ixSaturation) + call summaSolve4numrec(in_SS4NR,& ! input: model control + &stateVecTrial,fScale,xScale,rVec,sMul,dMat,& ! input: state vectors + &model_decisions,lookup_data,type_data,attr_data,mpar_data,forc_data,bvar_data,prog_data,& ! input: data structures + &indx_data,diag_data,flux_temp,deriv_data,& ! input-output: data structures + &dBaseflow_dMatric,io_SS4NR,& ! input-output: baseflow + &stateVecNew,fluxVecNew,resSinkNew,resVecNew,out_SS4NR) ! output + call io_SS4NR % finalize(firstFluxCall,xMin,xMax,ixSaturation) + call out_SS4NR % finalize(fNew,converged,err,cmessage) + end associate + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors + + ! save the computed functions, residuals, and solution + fOld = fNew + rVec = resVecNew + stateVecTrial = stateVecNew + stateVecPrime = stateVecTrial !prime values not used here, dummy + nSteps = 1 ! number of time steps taken in solver + end subroutine Newton_step + + subroutine check_Newton_convergence + ! ** Check for convergence of current Newton step ** + + ! exit iteration loop if converged + if (converged) then; exit_flag=.true.; return; end if + + ! check convergence + if (iter==localMaxiter) then + message=trim(message)//'failed to converge' + err=-20; return_flag=.true.; return + end if + end subroutine check_Newton_convergence + end subroutine systemSolv end module systemSolv_module From 95d5f574382dd0967bfe0c3546fe307a3906b05d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 7 Mar 2024 12:16:48 +0900 Subject: [PATCH 1180/1472] utils --- utils/bal_per_GRU.py | 4 ++-- utils/plot_per_GRUMultBal.py | 27 +++++++-------------------- utils/timeseries_to_statistics.py | 2 +- 3 files changed, 10 insertions(+), 23 deletions(-) diff --git a/utils/bal_per_GRU.py b/utils/bal_per_GRU.py index 487cb01ae..5a4282f2c 100644 --- a/utils/bal_per_GRU.py +++ b/utils/bal_per_GRU.py @@ -74,8 +74,8 @@ def run_loop(i,var,comp,leg_t,leg_t0,plt_t): # Data for m in method_name: - s = summa[m][var].sel(stat=stat) - s0 = summa[m][comp].sel(stat=stat) + s = np.fabs(summa[m][var].sel(stat=stat)) + s0 = np.fabs(summa[m][comp].sel(stat=stat)) if var == 'wallClockTime': stat0_word = 'Number flux calculations' diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index fed58f6f6..bfc4e6be6 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -51,12 +51,12 @@ plt_titl = ['Canopy Air Space Energy Balance','Vegetation Energy Balance','Snow Energy Balance','Soil Energy Balance','Vegetation Mass Balance','Snow Mass Balance','Soil Mass Balance','Aquifer Mass Balance', 'Wall Clock Time'] leg_titl = ['$W~m^{-3}$'] * 4 +['$num$'] + ['$kg~m^{-2}~s^{-1}$'] * 4 -fig_fil= '_hrly_diff_stats_{}_compressed.png' +fig_fil= '_hrly_balance_{}_compressed.png' if do_rel: - fig_fil = '_hrly_diff_stats_{}_rel_compressed.png' + fig_fil = '_hrly_scaledBalance_{}_rel_compressed.png' for i in range(8): settings[i] = 'scaledB' + settings[i][1:] - plt_title[i] = 'Scaled ' + plt_title[i] + plt_titl[i] = 'Scaled ' + plt_titl[i] leg_titl = ['$s^{-1}$'] * 8 + ['$s$'] if stat == 'mean': @@ -166,7 +166,6 @@ def make_default_path(suffix): ## Pre-processing, map SUMMA sims to catchment shapes # Get the aggregated statistics of SUMMA simulations summa = {} -eff = {} for i, m in enumerate(method_name): # Get the aggregated statistics of SUMMA simulations summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) @@ -175,17 +174,7 @@ def make_default_path(suffix): hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile for plot_var in plot_vars: stat0 = stat - if stat == 'rmse' or stat == 'kgem' or stat == 'mean': - if plot_var == 'wallClockTime': stat0 = 'mean' - statr = 'mean_ben' - if stat == 'rmnz' or stat == 'mnnz': - if plot_var == 'wallClockTime': stat0 = 'mnnz' - statr = 'mnnz_ben' - if stat == 'maxe' or stat == 'amax': - if plot_var == 'wallClockTime': stat0 = 'amax' - statr = 'amax_ben' - - if do_rel: s_rel = summa[method_name[0]][plot_var].sel(stat=statr) + for m in method_name: s = summa[m][plot_var].sel(stat=stat0) @@ -195,7 +184,9 @@ def make_default_path(suffix): # Replace inf values with NaN in the s DataArray s = s.where(~np.isinf(s), np.nan) - bas_albers[plot_var+m] = s.sel(hru=hru_ids_shp.values) + hru_ids = [hru_id for hru_id in hru_ids_shp.values if hru_id in s.hru.values] #if some missing + bas_albers[plot_var+m] = s.sel(hru=hru_ids) + #bas_albers[plot_var+m] = s.sel(hru=hru_ids_shp.values) # Select lakes of a certain size for plotting if plot_lakes: @@ -224,10 +215,6 @@ def run_loop(j,var,the_max): if stat0 == 'mean': stat_word = 'mean' if stat0 == 'amax': stat_word = 'max' - if statr == 'mean_ben': statr_word = 'mean' - if statr == 'mnnz_ben': statr_word = 'mean' # no 0s' - if statr == 'amax_ben': statr_word = 'max' - # colorbar axes f_x_mat = [0.46,0.96,0.46,0.96] f_y_mat = [0.55,0.55,0.07,0.07] diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 229a146ba..b63e9da19 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -30,7 +30,7 @@ bench_name = 'sundials_1en8' not_parallel = True # run as true with batch mode, or false, with `python timeseries_to_statistics.py sundials_1en6 1 1` for single batch, and `python timeseries_to_statistics.py sundials_1en6 2 1` to merge -testing = False +testing = True # which statistics to compute do_vars = False From 291d675f8dc41a98ecd0af4b0cea01c71db56c9d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 7 Mar 2024 14:15:44 +0900 Subject: [PATCH 1181/1472] fix balance calculation and utils (should not effect solution) --- build/source/engine/coupled_em.f90 | 4 ++-- utils/bal_per_GRU.py | 7 ++++--- utils/plot_per_GRUMultBal.py | 6 +++--- utils/timeseries_to_statistics.py | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 0efa05bc7..f06c08c3b 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1096,11 +1096,11 @@ subroutine coupled_em(& case (iname_snow) lyr_wght = prog_data%var(iLookPROG%mLayerDepth)%dat(iLayer) / prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) + innerBalanceLayerNrg(iLayer)*lyr_wght - diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) + innerBalanceLayerMass(iLayer) + diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) + innerBalanceLayerMass(iLayer)*lyr_wght case (iname_soil) lyr_wght = prog_data%var(iLookPROG%mLayerDepth)%dat(iLayer) / sum( prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers) ) diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) + innerBalanceLayerNrg(iLayer)*lyr_wght - diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) + innerBalanceLayerMass(iLayer) + diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) + innerBalanceLayerMass(iLayer)*lyr_wght end select end do if(do_outer)then diff --git a/utils/bal_per_GRU.py b/utils/bal_per_GRU.py index 5a4282f2c..ec8643088 100644 --- a/utils/bal_per_GRU.py +++ b/utils/bal_per_GRU.py @@ -26,7 +26,7 @@ if testing: stat = 'mean' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be1cm','be1en','be1lu'] #maybe make this an argument + method_name=['be1'] #cm','be1en','be1lu'] #maybe make this an argument else: import sys # The first input argument specifies the run where the files are @@ -74,8 +74,9 @@ def run_loop(i,var,comp,leg_t,leg_t0,plt_t): # Data for m in method_name: - s = np.fabs(summa[m][var].sel(stat=stat)) - s0 = np.fabs(summa[m][comp].sel(stat=stat)) + # Get the statistics, remove 9999 (should be nan, but just in case) + s = np.fabs(summa[m][var].sel(stat=stat)).where(lambda x: x != 9999) + s0 = np.fabs(summa[m][comp].sel(stat=stat)).where(lambda x: x != 9999) if var == 'wallClockTime': stat0_word = 'Number flux calculations' diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index bfc4e6be6..e6e47b00d 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -181,9 +181,9 @@ def make_default_path(suffix): # Make absolute value norm, not all positive s = np.fabs(s) - # Replace inf values with NaN in the s DataArray - s = s.where(~np.isinf(s), np.nan) - + # Replace inf and 9999 values with NaN in the s DataArray + s = s.where(~np.isinf(s), np.nan).where(lambda x: x != 9999, np.nan) + hru_ids = [hru_id for hru_id in hru_ids_shp.values if hru_id in s.hru.values] #if some missing bas_albers[plot_var+m] = s.sel(hru=hru_ids) #bas_albers[plot_var+m] = s.sel(hru=hru_ids_shp.values) diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index b63e9da19..229a146ba 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -30,7 +30,7 @@ bench_name = 'sundials_1en8' not_parallel = True # run as true with batch mode, or false, with `python timeseries_to_statistics.py sundials_1en6 1 1` for single batch, and `python timeseries_to_statistics.py sundials_1en6 2 1` to merge -testing = True +testing = False # which statistics to compute do_vars = False From cc4ff87dfb4b368552a62695a0faf0618f69a7e8 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 7 Mar 2024 06:44:52 -0600 Subject: [PATCH 1182/1472] Modularized operations that follow the numerical recipes Newton solver in systemSolv. --- build/source/engine/systemSolv.f90 | 81 ++++++++++++++++-------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index e3c876618..be521ce6f 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -686,45 +686,12 @@ subroutine systemSolv(& ! Post processing step to “perfectly” conserve mass by pushing the errors into the state variables ! NOTE: if the residual is large this will cause the state variables to be pushed outside of their bounds ! ------------------ - if (post_massCons)then - layerVars: associate(& - ! vector of energy and hydrology indices for the snow and soil domains - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) & ! intent(in): [i4b] number of hydrology state variables in the snow+soil domain - ) - - ! update temperatures (ensure new temperature is consistent with the fluxes) - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - iState = ixSnowSoilNrg(iLayer) - stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState))/real(sMul(iState), rkind) - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! update volumetric water content in the snow (ensure change in state is consistent with the fluxes) - ! NOTE: for soil water balance is constrained within the iteration loop - if(nSnowSoilHyd>0)then - do concurrent (iLayer=1:nSnow,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing water state variables in the snow domain) - iState = ixSnowSoilHyd(iLayer) - stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState)) - end do ! looping through non-missing water state variables in the soil domain - endif - end associate layerVars - endif + if (post_massCons) call enforce_mass_conservation ! enforce mass conservation if desired end select - ! set untapped melt energy to zero - untappedMelt(:) = 0._rkind - - ! ************************** - ! free memory - deallocate(mLayerCmpress_sum) - deallocate(dBaseflow_dMatric) - - ! end associate statements - end associate globalVars + end associate globalVars ! end associate statements + + call finalize_systemSolv ! set untapped melt to zero and deallocate arrays contains @@ -769,6 +736,46 @@ subroutine check_Newton_convergence end if end subroutine check_Newton_convergence + subroutine enforce_mass_conservation + ! Post processing step to “perfectly” conserve mass by pushing the errors into the state variables + ! NOTE: if the residual is large this will cause the state variables to be pushed outside of their bounds + + layerVars: associate(& + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1),& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1),& ! intent(in): [i4b] number of hydrology state variables in the snow+soil domain + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) & ! intent(in): [i4b] number of snow layers + ) + + ! update temperatures (ensure new temperature is consistent with the fluxes) + if (nSnowSoilNrg>0) then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! loop through non-missing energy state variables in the snow+soil domain + iState = ixSnowSoilNrg(iLayer) + stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState))/real(sMul(iState), rkind) + end do ! looping through non-missing energy state variables in the snow+soil domain + end if + + ! update volumetric water content in the snow (ensure change in state is consistent with the fluxes) + ! NOTE: for soil water balance is constrained within the iteration loop + if (nSnowSoilHyd>0) then + do concurrent (iLayer=1:nSnow,ixSnowSoilHyd(iLayer)/=integerMissing) ! loop through non-missing water state variables in the snow domain + iState = ixSnowSoilHyd(iLayer) + stateVecTrial(iState) = stateVecInit(iState) + (fluxVecNew(iState)*dt_cur + resSinkNew(iState)) + end do + end if + end associate layerVars + end subroutine enforce_mass_conservation + + subroutine finalize_systemSolv + ! set untapped melt energy to zero + untappedMelt(:) = 0._rkind + + ! free memory + deallocate(mLayerCmpress_sum) + deallocate(dBaseflow_dMatric) + end subroutine finalize_systemSolv + end subroutine systemSolv end module systemSolv_module From 0058576fb56c7093e72c30248e436aa6178cd48e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 7 Mar 2024 22:37:54 +0900 Subject: [PATCH 1183/1472] utils, and corrected BE balances to be per second, maybe should be per timestep, ?? --- build/source/engine/coupled_em.f90 | 2 +- build/source/engine/varSubstep.f90 | 22 +++++++++++----------- utils/plot_per_GRUMultBal.py | 14 +++++++++----- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index f06c08c3b..49461b40f 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1125,7 +1125,7 @@ subroutine coupled_em(& meanCanopySublimation = meanCanopySublimation + sumCanopySublimation/data_step meanLatHeatCanopyEvap = meanLatHeatCanopyEvap + sumLatHeatCanopyEvap/data_step meanSenHeatCanopy = meanSenHeatCanopy + sumSenHeatCanopy/data_step - meanSoilCompress(:) = meanSoilCompress(:) + innerSoilCompress(:)*dt_wght + meanSoilCompress(:) = meanSoilCompress(:) + innerSoilCompress(:)*dt_wght meanBalance(1) = meanBalance(1) + innerBalance(1)*dt_wght meanBalance(2) = meanBalance(2) + innerBalance(2)*dt_wght meanBalance(3) = meanBalance(3) + innerBalance(3)*dt_wght diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index a4f28cf0a..f077a5d9f 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -194,7 +194,7 @@ subroutine varSubstep(& real(rkind) :: fluxVec(in_varSubstep % nSubset) ! substep flux vector (mixed units) real(rkind) :: resSink(in_varSubstep % nSubset) ! substep sink terms on the RHS of the state equation real(qp) :: resVec(in_varSubstep % nSubset) ! substep residual vector - real(rkind) :: balance(in_varSubstep % nSubset) ! substep balance + real(rkind) :: balance(in_varSubstep % nSubset) ! substep balance per second real(rkind) :: sumBalance(in_varSubstep % nSubset) ! sum of substeps balance logical(lgt),parameter :: computMassBalance = .true. ! flag to compute the mass balance, will affect step length, default true logical(lgt),parameter :: computNrgBalance = .true. ! flag to compute the energy balance, will not effect solution but will not compute nrg balance if false (saves expense) @@ -661,7 +661,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! balances, flags, and error control real(rkind) ,intent(in) :: fluxVec(:) ! flux vector (mixed units) real(qp) ,intent(in) :: resVec(:) ! NOTE: qp ! residual vector - real(rkind) ,intent(inout) :: balance(:) ! balance of energy per domain + real(rkind) ,intent(inout) :: balance(:) ! balance of energy per domain per second logical(lgt) ,intent(out) :: waterBalanceError ! flag to denote that there is a water balance error logical(lgt) ,intent(out) :: nrgFluxModified ! flag to denote that the energy fluxes were modified integer(i4b) ,intent(out) :: err ! error code @@ -1015,19 +1015,19 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! compute energy balance, maybe should use to check for step reduction - if(ixCasNrg/=integerMissing) balance(ixCasNrg) = scalarCanairEnthalpyTrial - scalarCanairEnthalpy - fluxVec(ixCasNrg)*dt - if(ixVegNrg/=integerMissing) balance(ixVegNrg) = scalarCanopyHDelta - fluxVec(ixVegNrg)*dt + if(ixCasNrg/=integerMissing) balance(ixCasNrg) = (scalarCanairEnthalpyTrial - scalarCanairEnthalpy)/dt - fluxVec(ixCasNrg) + if(ixVegNrg/=integerMissing) balance(ixVegNrg) = scalarCanopyHDelta/dt - fluxVec(ixVegNrg) if(nSnowSoilNrg>0)then do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) - balance(ixSnowSoilNrg(i)) = mLayerHDelta(i) - fluxVec(ixSnowSoilNrg(i))*dt + balance(ixSnowSoilNrg(i)) = mLayerHDelta(i)/dt - fluxVec(ixSnowSoilNrg(i)) enddo endif ! This is equivalent to above if, and only if, ixNrgConserv.ne.closedForm - !!if(ixCasNrg/=integerMissing) balance(ixCasNrg) = resVec(ixCasNrg) - !if(ixVegNrg/=integerMissing) balance(ixVegNrg) = resVec(ixVegNrg) + !!if(ixCasNrg/=integerMissing) balance(ixCasNrg) = resVec(ixCasNrg)/dt + !if(ixVegNrg/=integerMissing) balance(ixVegNrg) = resVec(ixVegNrg)/dt !if(nSnowSoilNrg>0)then ! do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) - ! balance(ixSnowSoilNrg(i)) = resVec(ixSnowSoilNrg(i)) + ! balance(ixSnowSoilNrg(i)) = resVec(ixSnowSoilNrg(i))/dt ! enddo !endif @@ -1141,13 +1141,13 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! compute mass balance, maybe should use to check for step reduction ! resVec is the residual vector from the solver over dt - if(ixVegHyd/=integerMissing) balance(ixVegHyd) = resVec(ixVegHyd) + if(ixVegHyd/=integerMissing) balance(ixVegHyd) = resVec(ixVegHyd)/dt if(nSnowSoilHyd>0)then do concurrent (i=1:nLayers,ixSnowSoilHyd(i)/=integerMissing) - balance(ixSnowSoilHyd(i)) = resVec(ixSnowSoilHyd(i)) + balance(ixSnowSoilHyd(i)) = resVec(ixSnowSoilHyd(i))/dt end do endif - if(ixAqWat/=integerMissing) balance(ixAqWat) = resVec(ixAqWat) + if(ixAqWat/=integerMissing) balance(ixAqWat) = resVec(ixAqWat)/dt end select else ! if not checking mass balance set balance to missing diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index e6e47b00d..d3d159bf3 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -60,11 +60,11 @@ leg_titl = ['$s^{-1}$'] * 8 + ['$s$'] if stat == 'mean': - maxes = [1e-1,1e3,1e3,1e3]+[1e-9,1e-6,1e-6,1e-8] + [30] - if do_rel: maxes = [1e-2,1e0,1e-2,1e-3]+[1e-7,1e-7,1e-10,1e-7] + [3e-3] + maxes = [1e-4,1e0,1e0,1e0]+[1e-12,1e-9,1e-9,1e-11] + [30] + if do_rel: maxes = [1e-5,1e-3,1e-5,1e-6]+[1e-10,1e-10,1e-13,1e-10] + [3e-3] if stat == 'amax': - maxes = [1e0,1e6,1e6,1e5]+[1e-8,1e-3,1e-4,1e-5] + [1e4] - if do_rel: maxes = [1e1,1e3,1e-1,1e1]+[1e-4,1e-5,1e-7,1e-3] + [1e0] + maxes = [1e-3,1e3,1e3,1e2]+[1e-11,1e-6,1e-7,1e-8] + [1e4] + if do_rel: maxes = [1e-2,1e0,1e-4,1e-2]+[1e-7,1e-8,1e-10,1e-6] + [1e0] # Get the albers shapes main = Path('/home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/albers_projection') @@ -184,8 +184,12 @@ def make_default_path(suffix): # Replace inf and 9999 values with NaN in the s DataArray s = s.where(~np.isinf(s), np.nan).where(lambda x: x != 9999, np.nan) + bas_albers[plot_var+m] = np.nan hru_ids = [hru_id for hru_id in hru_ids_shp.values if hru_id in s.hru.values] #if some missing - bas_albers[plot_var+m] = s.sel(hru=hru_ids) + s_new = xr.DataArray(bas_albers[plot_var+m].values, coords=[bas_albers.index], dims=["hru"]) + hru_ids = [hru_id for hru_id in hru_ids if hru_id in s_new.hru.values] #if some missing in s_new + s_new.loc[hru_ids] = s.sel(hru=hru_ids).values + bas_albers[plot_var+m] = s_new.values #bas_albers[plot_var+m] = s.sel(hru=hru_ids_shp.values) # Select lakes of a certain size for plotting From bcf00c3caed08bb377eb39188cac22d91f36275a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 8 Mar 2024 11:47:49 +0900 Subject: [PATCH 1184/1472] utils --- utils/plot_per_GRUMultBal.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index d3d159bf3..f499e201b 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -184,13 +184,11 @@ def make_default_path(suffix): # Replace inf and 9999 values with NaN in the s DataArray s = s.where(~np.isinf(s), np.nan).where(lambda x: x != 9999, np.nan) + # Create a new column in the shapefile for each method, and fill it with the statistics bas_albers[plot_var+m] = np.nan - hru_ids = [hru_id for hru_id in hru_ids_shp.values if hru_id in s.hru.values] #if some missing - s_new = xr.DataArray(bas_albers[plot_var+m].values, coords=[bas_albers.index], dims=["hru"]) - hru_ids = [hru_id for hru_id in hru_ids if hru_id in s_new.hru.values] #if some missing in s_new - s_new.loc[hru_ids] = s.sel(hru=hru_ids).values - bas_albers[plot_var+m] = s_new.values - #bas_albers[plot_var+m] = s.sel(hru=hru_ids_shp.values) + hru_ind = [i for i, hru_id in enumerate(hru_ids_shp.values) if hru_id in s.hru.values] # if some missing + bas_albers.loc[hru_ind, plot_var+m] = s.sel(hru=hru_ids_shp.values[hru_ind]).values + #bas_albers[plot_var+m]= s.sel(hru=hru_ids_shp.values) # Select lakes of a certain size for plotting if plot_lakes: From 9e0269221851accdb77a115706ff71c3d03032a2 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 8 Mar 2024 20:13:25 +0900 Subject: [PATCH 1185/1472] utils --- utils/bal_per_GRU.py | 2 +- utils/plot_per_GRUMultBal.py | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/utils/bal_per_GRU.py b/utils/bal_per_GRU.py index ec8643088..34fdb95f8 100644 --- a/utils/bal_per_GRU.py +++ b/utils/bal_per_GRU.py @@ -22,7 +22,7 @@ viz_dir = Path('/home/avanb/scratch/statistics') -testing = False +testing = True if testing: stat = 'mean' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index f499e201b..e6930c384 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -61,7 +61,7 @@ if stat == 'mean': maxes = [1e-4,1e0,1e0,1e0]+[1e-12,1e-9,1e-9,1e-11] + [30] - if do_rel: maxes = [1e-5,1e-3,1e-5,1e-6]+[1e-10,1e-10,1e-13,1e-10] + [3e-3] + if do_rel: maxes = [1e-5,1e-3,1e-6,1e-6]+[1e-10,1e-10,1e-13,1e-10] + [3e-3] if stat == 'amax': maxes = [1e-3,1e3,1e3,1e2]+[1e-11,1e-6,1e-7,1e-8] + [1e4] if do_rel: maxes = [1e-2,1e0,1e-4,1e-2]+[1e-7,1e-8,1e-10,1e-6] + [1e0] @@ -207,12 +207,12 @@ def run_loop(j,var,the_max): my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap my_cmap.set_bad(color='white') #nan color white - vmin,vmax = 0, the_max + vmin,vmax = 1e-16, the_max #if stat =='mean' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 700, the_max #if stat =='amax' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 1000, the_max #if var!='wallClockTime' and do_rel: vmin,vmax = 0.9, the_max - norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) + norm = matplotlib.colors.LogNorm(vmin=vmin, vmax=vmax) if stat0 == 'mean': stat_word = 'mean' if stat0 == 'amax': stat_word = 'max' @@ -258,6 +258,10 @@ def run_loop(j,var,the_max): fig,axs = plt.subplots(2,2,figsize=(35,28)) else: fig,axs = plt.subplots(2,2,figsize=(140,133)) + + # Remove the fourth subplot + fig.delaxes(axs[1, 1]) + fig.suptitle('{} Hourly Statistics'.format(plt_titl[i]), fontsize=40,y=1.05) plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines From 019ffa71c7ee57fe0f648e5b30a8ebd4fd5a72f3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 8 Mar 2024 20:15:32 +0900 Subject: [PATCH 1186/1472] utils --- utils/bal_per_GRU.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/bal_per_GRU.py b/utils/bal_per_GRU.py index 34fdb95f8..ec8643088 100644 --- a/utils/bal_per_GRU.py +++ b/utils/bal_per_GRU.py @@ -22,7 +22,7 @@ viz_dir = Path('/home/avanb/scratch/statistics') -testing = True +testing = False if testing: stat = 'mean' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') From 26ced0afeaca9b90ec3e816eb90730de06c69b72 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 8 Mar 2024 20:27:38 +0900 Subject: [PATCH 1187/1472] redoing eval8 to get enthalpyprime, through updatevars line 398 --- build/source/engine/eval8summaWithPrime.f90 | 33 +++++++++++++++------ 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 76b236c86..4444993ef 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -79,22 +79,22 @@ subroutine eval8summaWithPrime(& diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input: previous values of variables needed in data window outside of internal IDA + ! input: previous values of variables needed in data window outside of internal IDA, for root finding scalarCanopyTempPrev, & ! intent(in): previous value for temperature of the vegetation canopy (K) mLayerTempPrev, & ! intent(in): previous vector of layer temperature (K) mLayerMatricHeadPrev, & ! intent(in): previous value for total water matric potential (m) - ! output: new values of variables needed in data window outside of internal IDA + ! output: new values of variables needed in data window outside of internal IDA, for Jacobian scalarCanopyTempTrial, & ! intent(out): trial value for temperature of the vegetation canopy (K) scalarCanopyWatTrial, & ! intent(out): trial value for mass of total water on the vegetation canopy (kg m-2) mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - ! output: new prime values of variables needed in data window outside of internal IDA - scalarCanairTempPrime, & ! intent(out): prime value for temperature of the canopy air space (K s-1) - scalarCanopyTempPrime, & ! intent(out): prime value for temperature of the vegetation canopy (K s-1) + ! output: new prime values of variables needed in data window outside of internal IDA for Jacobian + scalarCanairEnthalpyPrime, & ! intent(out): prime value for enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTempPrime, & ! intent(out): prime value for temperature component of enthalpy of the vegetation canopy (J m-3) scalarCanopyWatPrime, & ! intent(out): prime value for total water content of the vegetation canopy (kg m-2 s-1) scalarCanopyIcePrime, & ! intent(out): prime value for mass of ice on the vegetation canopy (kg m-2 s-1) - mLayerTempPrime, & ! intent(out): prime vector of temperature of each snow and soil layer (K s-1) + mLayerEnthTempPrime, & ! intent(out): prime vector of temperature component of enthalpy of each snow and soil layer (J m-3) mLayerMatricHeadPrime, & ! intent(out): prime vector of matric head of each snow and soil layer (m s-1) mLayerMatricHeadLiqPrime, & ! intent(out): prime vector of liquid water matric potential (m s-1) mLayerVolFracWatPrime, & ! intent(out): prime vector of volumetric total water content of each snow and soil layer (s-1) @@ -163,11 +163,11 @@ subroutine eval8summaWithPrime(& real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial vector for total water matric potential (m) real(rkind),intent(out) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) ! output: new prime values of variables needed in data window outside of internal IDA - real(rkind),intent(out) :: scalarCanairTempPrime ! prime value for temperature of the canopy air space (K s-1) - real(rkind),intent(out) :: scalarCanopyTempPrime ! prime value for temperature of the vegetation canopy (K s-1) + real(rkind),intent(out) :: scalarCanairEnthTempPrime ! prime value for temperature of the canopy air space (K s-1) + real(rkind),intent(out) :: scalarCanopyEnthTempPrime ! prime value for temperature of the vegetation canopy (K s-1) real(rkind),intent(out) :: scalarCanopyWatPrime ! prime value for total water content of the vegetation canopy (kg m-2 s-1) real(rkind),intent(out) :: scalarCanopyIcePrime ! prime value for mass of ice on the vegetation canopy (kg m-2 s-1) - real(rkind),intent(out) :: mLayerTempPrime(:) ! prime vector of temperature of each snow and soil layer (K s-1) + real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! prime vector of temperature of each snow and soil layer (K s-1) real(rkind),intent(out) :: mLayerMatricHeadPrime(:) ! prime vector of matric head of each snow and soil layer (m s-1) real(rkind),intent(out) :: mLayerMatricHeadLiqPrime(:) ! prime vector of liquid water matric potential (m s-1) real(rkind),intent(out) :: mLayerVolFracWatPrime(:) ! prime vector of volumetric total water content of each snow and soil layer (s-1) @@ -189,9 +189,12 @@ subroutine eval8summaWithPrime(& real(rkind) :: dt1 ! residual step size ! state variables real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3) + real(rkind) :: scalarCanopyEnthTempTrial ! trial value for temperature component of enthalpy of the vegetation canopy (J m-3) real(rkind) :: scalarCanopyLiqTrial ! trial value for liquid water storage in the canopy (kg m-2) real(rkind) :: scalarCanopyIceTrial ! trial value for ice storage in the canopy (kg m-2) real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) + real(rkind),dimension(nLayers) :: mLayerEnthTempTrial ! trial vector of temperature component of enthalpy of each snow and soil layer (J m-3) real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric liquid water content (-) real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric ice content (-) real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) @@ -305,11 +308,14 @@ subroutine eval8summaWithPrime(& endif ! Placeholder: if we decide to use splitting, we need to pass all the previous values of the state variables + scalarCanairEnthalpyTrial = realMissing scalarCanairTempTrial = realMissing + scalarCanopyEnthTempTrial = realMissing scalarCanopyTempTrial = scalarCanopyTempPrev scalarCanopyWatTrial = realMissing scalarCanopyLiqTrial = realMissing scalarCanopyIceTrial = realMissing + mLayerEnthTempTrial = realMissing mLayerTempTrial = mLayerTempPrev mLayerVolFracWatTrial = realMissing mLayerVolFracLiqTrial = realMissing @@ -326,11 +332,14 @@ subroutine eval8summaWithPrime(& prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy + scalarCanairEnthalpyTrial,& ! intent(inout): trial value of enthalpy of the canopy air space (J m-3) scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) + scalarCanopyEnthTempTrial,& ! intent(inout): trial value of temperature component of enthalpy of the vegetation canopy (J m-3) scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain + mLayerEnthTempTrial, & ! intent(inout): trial vector of temperature component of enthalpy of each snow and soil layer (J m-3) mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) @@ -343,11 +352,14 @@ subroutine eval8summaWithPrime(& if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! Placeholder: if we decide to use splitting, we need to pass all the previous values of the state variables + scalarCanairEnthalpyPrime = realMissing scalarCanairTempPrime = realMissing + scalarCanopyEnthTempPrime = realMissing scalarCanopyTempPrime = realMissing scalarCanopyWatPrime = realMissing scalarCanopyLiqPrime = realMissing scalarCanopyIcePrime = realMissing + mLayerEnthTempPrime = realMissing mLayerTempPrime = realMissing mLayerVolFracWatPrime = realMissing mLayerVolFracLiqPrime = realMissing @@ -363,11 +375,14 @@ subroutine eval8summaWithPrime(& prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy + scalarCanairEnthalpyPrime, & ! intent(inout): derivative of enthalpy of the canopy air space (J m-3) scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K) + scalarCanopyEnthTempPrime, & ! intent(inout): derivative of temperature component of enthalpy of the vegetation canopy (J m-3) scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K) scalarCanopyWatPrime, & ! intent(inout): derivative of canopy total water (kg m-2) scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain + mLayerEnthTempPrime, & ! intent(inout): derivative of temperature component of enthalpy of each snow and soil layer (J m-3) mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K) mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (-) mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (-) From dbb40c4e38a5a22ca6281474304cc4296b4c4843 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 8 Mar 2024 20:28:23 +0900 Subject: [PATCH 1188/1472] utils --- utils/plot_per_GRUMultBal.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index f499e201b..e6930c384 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -61,7 +61,7 @@ if stat == 'mean': maxes = [1e-4,1e0,1e0,1e0]+[1e-12,1e-9,1e-9,1e-11] + [30] - if do_rel: maxes = [1e-5,1e-3,1e-5,1e-6]+[1e-10,1e-10,1e-13,1e-10] + [3e-3] + if do_rel: maxes = [1e-5,1e-3,1e-6,1e-6]+[1e-10,1e-10,1e-13,1e-10] + [3e-3] if stat == 'amax': maxes = [1e-3,1e3,1e3,1e2]+[1e-11,1e-6,1e-7,1e-8] + [1e4] if do_rel: maxes = [1e-2,1e0,1e-4,1e-2]+[1e-7,1e-8,1e-10,1e-6] + [1e0] @@ -207,12 +207,12 @@ def run_loop(j,var,the_max): my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap my_cmap.set_bad(color='white') #nan color white - vmin,vmax = 0, the_max + vmin,vmax = 1e-16, the_max #if stat =='mean' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 700, the_max #if stat =='amax' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 1000, the_max #if var!='wallClockTime' and do_rel: vmin,vmax = 0.9, the_max - norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) + norm = matplotlib.colors.LogNorm(vmin=vmin, vmax=vmax) if stat0 == 'mean': stat_word = 'mean' if stat0 == 'amax': stat_word = 'max' @@ -258,6 +258,10 @@ def run_loop(j,var,the_max): fig,axs = plt.subplots(2,2,figsize=(35,28)) else: fig,axs = plt.subplots(2,2,figsize=(140,133)) + + # Remove the fourth subplot + fig.delaxes(axs[1, 1]) + fig.suptitle('{} Hourly Statistics'.format(plt_titl[i]), fontsize=40,y=1.05) plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines From e86e90952194edd4d3c1b7a84f646e6a5809dd7b Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 8 Mar 2024 06:23:19 -0600 Subject: [PATCH 1189/1472] Modularization of initial setup steps in systemSolv. --- build/source/engine/systemSolv.f90 | 126 +++++++++++++++++------------ 1 file changed, 75 insertions(+), 51 deletions(-) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index be521ce6f..60ddf1c80 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -319,57 +319,7 @@ subroutine systemSolv(& ! (0) PRELIMINARIES... ! ******************** - ! check - if(dt_cur < tinyStep)then - message=trim(message)//'dt is tiny' - err=20; return - endif - - ! initialize the flags - tooMuchMelt = .false. ! too much melt - reduceCoupledStep = .false. ! need to reduce the length of the coupled step - - ! modify the groundwater representation for this single-column implementation - select case(ixSpatialGroundwater) - case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale - case(localColumn); local_ixGroundwater = ixGroundwater ! go with the specified decision - case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return - end select ! (modify the groundwater representation for this single-column implementation) - - ! allocate space for the model fluxes at the start of the time step - call allocLocal(flux_meta(:),flux_init,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! allocate space for mLayerCmpress_sum at the start of the time step - if(ixNumericalMethod==ida)then - allocate( mLayerCmpress_sum(nSoil) ) - else - allocate( mLayerCmpress_sum(0) ) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix - end if - - ! allocate space for the baseflow derivatives - ! NOTE: needs allocation because only used when baseflow sinks are active - if(ixGroundwater==qbaseTopmodel)then - allocate(dBaseflow_dMatric(nSoil,nSoil),stat=err) ! baseflow depends on total storage in the soil column, hence on matric head in every soil layer - else - allocate(dBaseflow_dMatric(0,0),stat=err) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix - end if - if(err/=0)then; err=20; message=trim(message)//'unable to allocate space for the baseflow derivatives'; return; end if - - ! identify the matrix solution method, using the full matrix can be slow in many-layered systems - ! (the type of matrix used to solve the linear system A.X=B) - if(local_ixGroundwater==qbaseTopmodel .or. scalarSolution .or. forceFullMatrix .or. computeVegFlux)then - nLeadDim=nState ! length of the leading dimension - ixMatrix=ixFullMatrix ! named variable to denote the full Jacobian matrix - else - nLeadDim=nBands ! length of the leading dimension - ixMatrix=ixBandMatrix ! named variable to denote the band-diagonal matrix - endif - - ! initialize the model fluxes (some model fluxes are not computed in the iterations) - do iVar=1,size(flux_temp%var) - flux_init%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) - end do + call initialize_systemSolv; if (return_flag) return ! ----- ! * get scaling vectors... @@ -695,6 +645,80 @@ subroutine systemSolv(& contains + subroutine initialize_systemSolv + ! *** Initial setup operations for the systemSolv subroutine *** + associate(& + ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) + ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision & ! intent(in): [i4b] groundwater parameterization + &) + + ! check time step size + if (dt_cur < tinyStep) then + message=trim(message)//'dt is tiny' + err=20; return_flag=.true.; return + end if + + ! initialize the flags + tooMuchMelt = .false. ! too much melt + reduceCoupledStep = .false. ! need to reduce the length of the coupled step + + ! modify the groundwater representation for this single-column implementation + select case(ixSpatialGroundwater) + case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale + case(localColumn); local_ixGroundwater = ixGroundwater ! go with the specified decision + case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; + return_flag=.true.; return + end select + + call allocate_memory; if (return_flag) return + + ! identify the matrix solution method, using the full matrix can be slow in many-layered systems + ! (the type of matrix used to solve the linear system A.X=B) + if (local_ixGroundwater==qbaseTopmodel .or. scalarSolution .or. forceFullMatrix .or. computeVegFlux) then + nLeadDim=nState ! length of the leading dimension + ixMatrix=ixFullMatrix ! named variable to denote the full Jacobian matrix + else + nLeadDim=nBands ! length of the leading dimension + ixMatrix=ixBandMatrix ! named variable to denote the band-diagonal matrix + end if + + ! initialize the model fluxes (some model fluxes are not computed in the iterations) + do iVar=1,size(flux_temp%var) + flux_init%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) + end do + end associate + end subroutine initialize_systemSolv + + subroutine allocate_memory + ! ** Allocate arrays used in systemSolv subroutine ** + associate(& + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision,& ! intent(in): [i4b] choice of numerical solver + ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision & ! intent(in): [i4b] groundwater parameterization + &) + ! allocate space for the model fluxes at the start of the time step + call allocLocal(flux_meta(:),flux_init,nSnow,nSoil,err,cmessage) + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if + + ! allocate space for mLayerCmpress_sum at the start of the time step + if (ixNumericalMethod==ida) then + allocate( mLayerCmpress_sum(nSoil) ) + else + allocate( mLayerCmpress_sum(0) ) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix + end if + + ! allocate space for the baseflow derivatives + ! NOTE: needs allocation because only used when baseflow sinks are active + if (ixGroundwater==qbaseTopmodel) then + allocate(dBaseflow_dMatric(nSoil,nSoil),stat=err) ! baseflow depends on total storage in the soil column, hence on matric head in every soil layer + else + allocate(dBaseflow_dMatric(0,0),stat=err) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix + end if + if (err/=0) then; err=20; message=trim(message)//'unable to allocate space for the baseflow derivatives'; return_flag=.true.; return; end if + end associate + end subroutine allocate_memory + subroutine Newton_step ! ** Compute the Newton step using numerical recipes ** associate(& From 504d04fd862fdd582532684a4d6703bba3c1c251 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 8 Mar 2024 22:01:47 +0900 Subject: [PATCH 1190/1472] utils --- utils/plot_per_GRUMultBal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index e6930c384..d4fac24d3 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -236,7 +236,7 @@ def run_loop(j,var,the_max): # Custom colorbar cax = fig.add_axes([f_x,f_y,0.02,0.375]) sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) - sm._A = [] + sm.set_array([]) cbr = fig.colorbar(sm, cax=cax) #, extend='max') #if max extend can't get title right cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titl[j]), labelpad=40, rotation=270) if do_rel and var!='wallClockTime': cbr.ax.set_ylabel('scaled '+ stat_word, labelpad=40, rotation=270) From 90b0398bd1e3d0370fa8f45f41175d384778bb55 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 9 Mar 2024 04:19:27 -0600 Subject: [PATCH 1191/1472] Additional modularization of initial steps in systemSolv. --- build/source/engine/systemSolv.f90 | 349 ++++++++++++++++------------- 1 file changed, 188 insertions(+), 161 deletions(-) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 60ddf1c80..18cae53f4 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -310,144 +310,11 @@ subroutine systemSolv(& nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) & ! intent(in): [i4b] number of soil layers ) ! --------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="systemSolv/" - return_flag=.false. ! initialize return flag - nSteps = 0 ! initialize number of time steps taken in solver - - ! ***** - ! (0) PRELIMINARIES... - ! ******************** - - call initialize_systemSolv; if (return_flag) return - - ! ----- - ! * get scaling vectors... - ! ------------------------ - - ! initialize state vectors - call getScaling(& - ! input - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output - fScale, & ! intent(out): characteristic scale of the function evaluations (mixed units) - xScale, & ! intent(out): variable scaling vector (mixed units) - sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) - dMat, & ! intent(out): diagonal of the Jacobian matrix (excludes fluxes) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - ! ----- - ! * compute the initial function evaluation... - ! -------------------------------------------- - - ! initialize the trial state vectors - stateVecTrial = stateVecInit - - if(ixHowHeatCap == enthalpyFD)then - ! compute H_T at the beginning of the data step without phase change - call t2enthalpy(& - .false., & ! intent(in): logical flag to not include phase change in enthalpy - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTemp, & ! intent(in): value of canopy air temperature (K) - scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) - scalarCanopyWat, & ! intent(in): value of canopy total water (kg m-2) - scalarCanopyIce, & ! intent(in): value for canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - mLayerTemp, & ! intent(in): vector of layer temperature (K) - mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) - mLayerMatricHead, & ! intent(in): vector of total water matric potential (m) - mLayerVolFracIce, & ! intent(in): vector of volumetric fraction of ice (-) - ! input: pre-computed derivatives - dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) - scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) - mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) - mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) - dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) - ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): temperature component of enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) - dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature - dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state - dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature - dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - endif - - ! compute the initial flux and the residual vector, also gets values needed for the Jacobian matrix - ! (prime initial values are 0 so it's fine to run the regular eval8summa with every solver choice) - call eval8summa(& - ! input: model control - dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): number of layers - nState, & ! intent(in): number of state variables in the current subset - .false., & ! intent(in): not inside Sundials solver - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVecTrial, & ! intent(in): model state vector - fScale, & ! intent(in): characteristic scale of the function evaluations - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - ! output - feasible, & ! intent(out): flag to denote the feasibility of the solution - fluxVec0, & ! intent(out): flux vector - rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec, & ! intent(out): residual vector - fOld, & ! intent(out): function evaluation - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif + call initialize_systemSolv; if (return_flag) return ! initialize variables and allocate arrays -- return if error - ! copy over the initial flux structure since some model fluxes are not computed in the iterations - do concurrent ( iVar=1:size(flux_meta) ) - flux_temp%var(iVar)%dat(:) = flux_init%var(iVar)%dat(:) - end do + call initial_function_evaluations; if (return_flag) return ! initial function evaluations -- retrun if error - ! check the need to merge snow layers - if(nSnow>0)then - ! compute the energy required to melt the top snow layer (J m-2) - bulkDensity = mLayerVolFracIce(1)*iden_ice + mLayerVolFracLiq(1)*iden_water - volEnthalpy = temp2ethpy(mLayerTemp(1),bulkDensity,snowfrz_scale) - ! set flag and error codes for too much melt - if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_cur)then - tooMuchMelt = .true. - message=trim(message)//'net flux in the top snow layer can melt all the snow in the top layer' - err=-20; return ! negative error code to denote a warning - endif - endif ! ************************** ! * Solving the System @@ -631,11 +498,6 @@ subroutine systemSolv(& if (return_flag) return ! return if error end do - ! ----- - ! * update states... - ! Post processing step to “perfectly” conserve mass by pushing the errors into the state variables - ! NOTE: if the residual is large this will cause the state variables to be pushed outside of their bounds - ! ------------------ if (post_massCons) call enforce_mass_conservation ! enforce mass conservation if desired end select @@ -647,27 +509,33 @@ subroutine systemSolv(& subroutine initialize_systemSolv ! *** Initial setup operations for the systemSolv subroutine *** + + ! initialize error control + err=0; message="systemSolv/" + return_flag=.false. ! initialize return flag + nSteps = 0 ! initialize number of time steps taken in solver + + ! check time step size + if (dt_cur < tinyStep) then + message=trim(message)//'dt is tiny' + err=20; return_flag=.true.; return + end if + + ! initialize the flags + tooMuchMelt = .false. ! too much melt + reduceCoupledStep = .false. ! need to reduce the length of the coupled step + associate(& ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision & ! intent(in): [i4b] groundwater parameterization - &) - - ! check time step size - if (dt_cur < tinyStep) then - message=trim(message)//'dt is tiny' - err=20; return_flag=.true.; return - end if - - ! initialize the flags - tooMuchMelt = .false. ! too much melt - reduceCoupledStep = .false. ! need to reduce the length of the coupled step + &) ! modify the groundwater representation for this single-column implementation select case(ixSpatialGroundwater) - case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale - case(localColumn); local_ixGroundwater = ixGroundwater ! go with the specified decision - case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; - return_flag=.true.; return + case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale + case(localColumn); local_ixGroundwater = ixGroundwater ! go with the specified decision + case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; + return_flag=.true.; return end select call allocate_memory; if (return_flag) return @@ -681,12 +549,16 @@ subroutine initialize_systemSolv nLeadDim=nBands ! length of the leading dimension ixMatrix=ixBandMatrix ! named variable to denote the band-diagonal matrix end if - - ! initialize the model fluxes (some model fluxes are not computed in the iterations) - do iVar=1,size(flux_temp%var) - flux_init%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) - end do end associate + + ! initialize the model fluxes (some model fluxes are not computed in the iterations) + do iVar=1,size(flux_temp%var) + flux_init%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) + end do + + ! initialize state vectors -- get scaling vectors + call getScaling(diag_data,indx_data,fScale,xScale,sMul,dMat,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors end subroutine initialize_systemSolv subroutine allocate_memory @@ -696,7 +568,7 @@ subroutine allocate_memory nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision,& ! intent(in): [i4b] choice of numerical solver ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision & ! intent(in): [i4b] groundwater parameterization - &) + &) ! allocate space for the model fluxes at the start of the time step call allocLocal(flux_meta(:),flux_init,nSnow,nSoil,err,cmessage) if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if @@ -719,6 +591,161 @@ subroutine allocate_memory end associate end subroutine allocate_memory + subroutine initial_function_evaluations + ! ** Compute initial function evaluations ** + + ! initialize the trial state vectors + stateVecTrial = stateVecInit + + associate(& + ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision) ! intent(in): [i4b] heat capacity computation, with or without enthalpy + if (ixHowHeatCap == enthalpyFD) then + call enthalpy_function_evaluations; if (return_flag) return + end if + end associate + + ! compute the initial flux and the residual vector, also gets values needed for the Jacobian matrix + call initial_flux_and_residual_vectors; if (return_flag) return + + if (.not.feasible) then; message=trim(message)//'state vector not feasible'; err=20; return_flag=.true.; return; end if + + ! copy over the initial flux structure since some model fluxes are not computed in the iterations + do concurrent ( iVar=1:size(flux_meta) ) + flux_temp%var(iVar)%dat(:) = flux_init%var(iVar)%dat(:) + end do + + ! check the need to merge snow layers + associate(& + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + &) + if (nSnow>0) then + ! compute the energy required to melt the top snow layer (J m-2) + bulkDensity = mLayerVolFracIce(1)*iden_ice + mLayerVolFracLiq(1)*iden_water + volEnthalpy = temp2ethpy(mLayerTemp(1),bulkDensity,snowfrz_scale) + ! set flag and error codes for too much melt + if (-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_cur) then + tooMuchMelt = .true. + message=trim(message)//'net flux in the top snow layer can melt all the snow in the top layer' + err=-20; return_flag=.true.; return ! negative error code to denote a warning + end if + end if + end associate + end subroutine initial_function_evaluations + + subroutine enthalpy_function_evaluations + ! ** Compute H_T at the beginning of the data step without phase change ** + associate(& + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1),& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1),& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat & ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) + &) + call t2enthalpy(& + .false., & ! intent(in): logical flag to not include phase change in enthalpy + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTemp, & ! intent(in): value of canopy air temperature (K) + scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) + scalarCanopyWat, & ! intent(in): value of canopy total water (kg m-2) + scalarCanopyIce, & ! intent(in): value for canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTemp, & ! intent(in): vector of layer temperature (K) + mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) + mLayerMatricHead, & ! intent(in): vector of total water matric potential (m) + mLayerVolFracIce, & ! intent(in): vector of volumetric fraction of ice (-) + ! input: pre-computed derivatives + dTheta_dTkCanopy, & ! intent(in): derivative in canopy volumetric liquid water content w.r.t. temperature (K-1) + scalarFracLiqVeg, & ! intent(in): fraction of canopy liquid water (-) + mLayerdTheta_dTk, & ! intent(in): derivative of volumetric liquid water content w.r.t. temperature (K-1) + mLayerFracLiqSnow, & ! intent(in): fraction of liquid water (-) + dVolTot_dPsi0, & ! intent(in): derivative in total water content w.r.t. total water matric potential (m-1) + ! output: enthalpy + scalarCanairEnthalpy, & ! intent(out): temperature component of enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) + dCanEnthalpy_dTk, & ! intent(out): derivatives in canopy enthalpy w.r.t. temperature + dCanEnthalpy_dWat, & ! intent(out): derivatives in canopy enthalpy w.r.t. water state + dEnthalpy_dTk, & ! intent(out): derivatives in layer enthalpy w.r.t. temperature + dEnthalpy_dWat, & ! intent(out): derivatives in layer enthalpy w.r.t. water state + ! output: error control + err,cmessage) ! intent(out): error control + end associate + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if + end subroutine enthalpy_function_evaluations + + subroutine initial_flux_and_residual_vectors + ! ** Compute initial flux and residual vectors ** + ! Note: prime initial values are 0 so it's fine to run the regular eval8summa with every solver choice + associate(& + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1),& ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) & ! intent(in): [i4b] number of soil layers + &) + call eval8summa(& + ! input: model control + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): number of layers + nState, & ! intent(in): number of state variables in the current subset + .false., & ! intent(in): not inside Sundials solver + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVecTrial, & ! intent(in): model state vector + fScale, & ! intent(in): characteristic scale of the function evaluations + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + indx_data, & ! intent(inout): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + ! output + feasible, & ! intent(out): flag to denote the feasibility of the solution + fluxVec0, & ! intent(out): flux vector + rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation + rVec, & ! intent(out): residual vector + fOld, & ! intent(out): function evaluation + err,cmessage) ! intent(out): error control + end associate + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors + end subroutine initial_flux_and_residual_vectors + subroutine Newton_step ! ** Compute the Newton step using numerical recipes ** associate(& From 4da2bc6aeeb5f676c4f0a53129d64f5fb7199d3b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 11 Mar 2024 17:40:21 +0900 Subject: [PATCH 1192/1472] utils --- utils/plot_per_GRUMultBal.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index d4fac24d3..5491f2202 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -47,7 +47,6 @@ nbatch_hrus = 518 # number of HRUs per batch # Specify variables of interest -plot_vars = settings.copy() plt_titl = ['Canopy Air Space Energy Balance','Vegetation Energy Balance','Snow Energy Balance','Soil Energy Balance','Vegetation Mass Balance','Snow Mass Balance','Soil Mass Balance','Aquifer Mass Balance', 'Wall Clock Time'] leg_titl = ['$W~m^{-3}$'] * 4 +['$num$'] + ['$kg~m^{-2}~s^{-1}$'] * 4 @@ -59,9 +58,11 @@ plt_titl[i] = 'Scaled ' + plt_titl[i] leg_titl = ['$s^{-1}$'] * 8 + ['$s$'] +plot_vars = settings.copy() + if stat == 'mean': - maxes = [1e-4,1e0,1e0,1e0]+[1e-12,1e-9,1e-9,1e-11] + [30] - if do_rel: maxes = [1e-5,1e-3,1e-6,1e-6]+[1e-10,1e-10,1e-13,1e-10] + [3e-3] + maxes = [1e-4,1e-2,1e-3,1e-2]+[1e-12,1e-9,1e-10,1e-11] + [30] + if do_rel: maxes = [1e-6,1e-5,1e-8,1e-8]+[1e-10,1e-11,1e-13,1e-11] + [3e-3] if stat == 'amax': maxes = [1e-3,1e3,1e3,1e2]+[1e-11,1e-6,1e-7,1e-8] + [1e4] if do_rel: maxes = [1e-2,1e0,1e-4,1e-2]+[1e-7,1e-8,1e-10,1e-6] + [1e0] @@ -207,7 +208,7 @@ def run_loop(j,var,the_max): my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap my_cmap.set_bad(color='white') #nan color white - vmin,vmax = 1e-16, the_max + vmin,vmax = the_max*1e-4, the_max #if stat =='mean' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 700, the_max #if stat =='amax' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 1000, the_max #if var!='wallClockTime' and do_rel: vmin,vmax = 0.9, the_max From b5e787b6d54886f57718018afcf5b389398342bf Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 11 Mar 2024 17:41:13 +0900 Subject: [PATCH 1193/1472] utils --- utils/plot_per_GRUMultBal.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index e6930c384..5491f2202 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -47,7 +47,6 @@ nbatch_hrus = 518 # number of HRUs per batch # Specify variables of interest -plot_vars = settings.copy() plt_titl = ['Canopy Air Space Energy Balance','Vegetation Energy Balance','Snow Energy Balance','Soil Energy Balance','Vegetation Mass Balance','Snow Mass Balance','Soil Mass Balance','Aquifer Mass Balance', 'Wall Clock Time'] leg_titl = ['$W~m^{-3}$'] * 4 +['$num$'] + ['$kg~m^{-2}~s^{-1}$'] * 4 @@ -59,9 +58,11 @@ plt_titl[i] = 'Scaled ' + plt_titl[i] leg_titl = ['$s^{-1}$'] * 8 + ['$s$'] +plot_vars = settings.copy() + if stat == 'mean': - maxes = [1e-4,1e0,1e0,1e0]+[1e-12,1e-9,1e-9,1e-11] + [30] - if do_rel: maxes = [1e-5,1e-3,1e-6,1e-6]+[1e-10,1e-10,1e-13,1e-10] + [3e-3] + maxes = [1e-4,1e-2,1e-3,1e-2]+[1e-12,1e-9,1e-10,1e-11] + [30] + if do_rel: maxes = [1e-6,1e-5,1e-8,1e-8]+[1e-10,1e-11,1e-13,1e-11] + [3e-3] if stat == 'amax': maxes = [1e-3,1e3,1e3,1e2]+[1e-11,1e-6,1e-7,1e-8] + [1e4] if do_rel: maxes = [1e-2,1e0,1e-4,1e-2]+[1e-7,1e-8,1e-10,1e-6] + [1e0] @@ -207,7 +208,7 @@ def run_loop(j,var,the_max): my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap my_cmap.set_bad(color='white') #nan color white - vmin,vmax = 1e-16, the_max + vmin,vmax = the_max*1e-4, the_max #if stat =='mean' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 700, the_max #if stat =='amax' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 1000, the_max #if var!='wallClockTime' and do_rel: vmin,vmax = 0.9, the_max @@ -236,7 +237,7 @@ def run_loop(j,var,the_max): # Custom colorbar cax = fig.add_axes([f_x,f_y,0.02,0.375]) sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) - sm._A = [] + sm.set_array([]) cbr = fig.colorbar(sm, cax=cax) #, extend='max') #if max extend can't get title right cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titl[j]), labelpad=40, rotation=270) if do_rel and var!='wallClockTime': cbr.ax.set_ylabel('scaled '+ stat_word, labelpad=40, rotation=270) From 09dcc34b844c0f310ddad7a5c49d3951ec0efc6a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 11 Mar 2024 23:05:05 +0900 Subject: [PATCH 1194/1472] settting up temp enthalpy conversion --- build/source/driver/summa_setup.f90 | 51 ++++++--- build/source/engine/enthalpyTemp.f90 | 117 ++++++++++---------- build/source/engine/eval8summa.f90 | 4 +- build/source/engine/eval8summaWithPrime.f90 | 89 ++++++++++----- 4 files changed, 156 insertions(+), 105 deletions(-) diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index 5b094dc79..9503b5bee 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -80,8 +80,9 @@ subroutine summa_paramSetup(summa1_struc, err, message) USE paramCheck_module,only:paramCheck ! module to check consistency of model parameters USE pOverwrite_module,only:pOverwrite ! module to overwrite default parameter values with info from the Noah tables USE read_param_module,only:read_param ! module to read model parameter sets - USE enthalpyTemp_module,only:T2E_lookup ! module to calculate a look-up table for the snow temperature-enthalpy conversion - USE enthalpyTemp_module,only:T2L_lookup ! module to calculate a look-up table for the soil temperature-enthalpy conversion + USE enthalpyTemp_module,only:T2E_lookup_snow ! module to calculate a look-up table for the snow temperature-enthalpy conversion + USE enthalpyTemp_module,only:T2L_lookup_soil ! module to calculate a look-up table for the soil temperature-enthalpy conversion + USE enthalpyTemp_module,only:T2E_lookup_veg ! module to calculate a look-up table for the vegetation temperature-enthalpy conversion USE var_derive_module,only:fracFuture ! module to calculate the fraction of runoff in future time steps (time delay histogram) USE module_sf_noahmplsm,only:read_mp_veg_parameters ! module to read NOAH vegetation tables ! global data structures @@ -122,7 +123,8 @@ subroutine summa_paramSetup(summa1_struc, err, message) integer(i4b) :: jHRU,kHRU ! HRU indices integer(i4b) :: iGRU,iHRU ! looping variables integer(i4b) :: iVar ! looping variables - logical :: needLookup ! logical to decide if computing enthalpy lookup tables + logical :: needLookup_soil ! logical to decide if computing soil enthalpy lookup tables + logical :: needLookup_veg ! logical to decide if computing vegetation enthalpy lookup tables ! --------------------------------------------------------------------------------------- ! associate to elements in the data structure summaVars: associate(& @@ -176,9 +178,19 @@ subroutine summa_paramSetup(summa1_struc, err, message) call mDecisions(err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! decide if computing enthalpy lookup tables, if need enthalpy and not using hypergeometric function - needLookup = .false. - if(model_decisions(iLookDECISIONS%nrgConserv)%iDecision == enthalpyFDlu) needLookup = .true. + ! decide if computing soil enthalpy lookup tables and vegetation enthalpy lookup tables + needLookup_soil = .false. + needLookup_veg = .false. + ! if need enthalpy for energy conservation residual form and not using soil enthalpy hypergeometric function + if(model_decisions(iLookDECISIONS%nrgConserv)%iDecision == enthalpyFDlu) needLookup_soil = .true. + ! if using IDA and enthalpy as a state variable, need temperature-enthalpy lookup tables for soil and vegetation + if(model_decisions(iLookDECISIONS%num_method)%iDecision == ida) then + if (model_decisions(iLookDECISIONS%nrgConserv)%iDecision == enthalpyFD .or. & + model_decisions(iLookDECISIONS%nrgConserv)%iDecision == enthalpyFDlu) then + needLookup_soil = .true. + needLookup_veg = .true. + endif + endif ! get the maximum number of snow layers select case(model_decisions(iLookDECISIONS%snowLayers)%iDecision) @@ -307,22 +319,27 @@ subroutine summa_paramSetup(summa1_struc, err, message) call paramCheck(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! calculate a look-up table for the temperature-enthalpy conversion of snow + ! calculate a look-up table for the temperature-enthalpy conversion of snow for future snow layer merging ! NOTE1: might be able to make this more efficient by only doing this for the HRUs that have snow - ! NOTE2: this does not actually need to be called for each HRU and GRU - call T2E_lookup(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) + ! NOTE2: H is the mixture enthalpy of snow liquid and ice + call T2H_lookup_snow(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! calculate a lookup table for the temperature-enthalpy conversion of soil - ! NOTE3: L is the integral of soil Clapeyron equation liquid water matric potential from temperature, multiply by Cp_liq*iden_water to get enthalpy - if(needLookup)then - call T2L_lookup(gru_struc(iGRU)%hruInfo(iHRU)%nSoil, & ! intent(in): number of soil layers - mparStruct%gru(iGRU)%hru(iHRU), & ! intent(in): parameter data structure - lookupStruct%gru(iGRU)%hru(iHRU), & ! intent(inout): lookup table data structure - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + ! calculate a lookup table for the temperature-enthalpy conversion of soil + ! NOTE: L is the integral of soil Clapeyron equation liquid water matric potential from temperature + ! multiply by Cp_liq*iden_water to get temperature component of enthalpy + if(needLookup_soil)then + call T2L_lookup_soil(gru_struc(iGRU)%hruInfo(iHRU)%nSoil, & ! intent(in): number of soil layers + mparStruct%gru(iGRU)%hru(iHRU), & ! intent(in): parameter data structure + lookupStruct%gru(iGRU)%hru(iHRU), & ! intent(inout): lookup table data structure + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif endif + ! calculate a lookup table for the temperature-enthalpy conversion of canopy (vegetation) + call T2E_lookup_veg(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + ! overwrite the vegetation height HVT(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex)) = mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%heightCanopyTop)%dat(1) HVB(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex)) = mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%heightCanopyBottom)%dat(1) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index d1e07b870..6dcfd48cd 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -61,27 +61,28 @@ module enthalpyTemp_module ! privacy implicit none -public::T2E_lookup -public::T2L_lookup -public::enthTemp2T_snow -public::T2enthTemp_snow +public::T2H_lookup_snow +public::T2L_lookup_soil +public::T2H_lookup_veg +public::H2T_snow +public::T2H_snow public::T2enthTemp public::enthTemp2H private::hyp_2F1_real ! define the snow look-up table used to compute temperature based on enthalpy integer(i4b),parameter :: nlook=10001 ! number of elements in the lookup table -real(rkind),dimension(nlook),public :: E_lookup ! enthalpy values (J kg-1) +real(rkind),dimension(nlook),public :: H_lookup ! enthalpy values (J kg-1) real(rkind),dimension(nlook),public :: T_lookup ! temperature values (K) contains ! ************************************************************************************************************************ -! public subroutine T2E_lookup: define a look-up table to compute temperature component of enthalpy based on temperature -! appropriate when no dry mass, as in snow +! public subroutine T2H_lookup_snow:: define a look-up table to mixture enthalpy based on temperature +! appropriate when no dry mass, as in snow ! ************************************************************************************************************************ -subroutine T2E_lookup(mpar_data, & ! intent(in): parameter data structure - err,message) +subroutine T2H_lookup_snow(mpar_data, & ! intent(in): parameter data structure + err,message) ! ------------------------------------------------------------------------------------------------------------------------- ! downwind routines USE nr_utility_module,only:arth ! use to build vectors with regular increments @@ -95,16 +96,16 @@ subroutine T2E_lookup(mpar_data, & ! intent(in): paramet ! declare local variables character(len=128) :: cmessage ! error message in downwind routine real(rkind),parameter :: T_start=260.0_rkind ! start temperature value where all liquid water is assumed frozen (K) - real(rkind) :: T_incr,E_incr ! temperature/enthalpy increments + real(rkind) :: T_incr,H_incr ! temperature/enthalpy increments real(rkind),dimension(nlook) :: Tk ! initial temperature vector - real(rkind),dimension(nlook) :: Ey ! initial enthalpy vector + real(rkind),dimension(nlook) :: Hy ! initial enthalpy vector real(rkind),parameter :: waterWght=1._rkind ! weight applied to total water (kg m-3) --- cancels out - real(rkind),dimension(nlook) :: E2 ! 2nd derivatives of the interpolating function at tabulated points - real(rkind) :: dT ! derivative of temperature with enthalpy at E_lookup + real(rkind),dimension(nlook) :: H2 ! 2nd derivatives of the interpolating function at tabulated points + real(rkind) :: dT ! derivative of temperature with enthalpy at H_lookup integer(i4b) :: ilook ! loop through lookup table ! ------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="T2E_lookup/" + err=0; message="T2H_lookup_snow/" ! associate associate( snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ) @@ -115,34 +116,34 @@ subroutine T2E_lookup(mpar_data, & ! intent(in): paramet ! ***** compute specific enthalpy (NOTE: J m-3 --> J kg-1) ***** do ilook=1,nlook - Ey(ilook) = T2enthTemp_snow(Tk(ilook),waterWght,snowfrz_scale)/waterWght ! (J m-3 --> J kg-1) + Hy(ilook) = T2H_snow(Tk(ilook),waterWght,snowfrz_scale)/waterWght ! (J m-3 --> J kg-1) end do ! define the final enthalpy vector - E_incr = (-Ey(1)) / real(nlook-1, kind(rkind)) ! enthalpy increment - E_lookup = arth(Ey(1),E_incr,nlook) + H_incr = (-Hy(1)) / real(nlook-1, kind(rkind)) ! enthalpy increment + H_lookup = arth(Hy(1),H_incr,nlook) ! use cubic spline interpolation to obtain temperature values at the desired values of enthalpy - call spline(Ey,Tk,1.e30_rkind,1.e30_rkind,E2,err,cmessage) ! get the second derivatives + call spline(Hy,Tk,1.e30_rkind,1.e30_rkind,H2,err,cmessage) ! get the second derivatives if(err/=0) then; message=trim(message)//trim(cmessage); return; end if do ilook=1,nlook - call splint(Ey,Tk,E2,E_lookup(ilook),T_lookup(ilook),dT,err,cmessage) + call splint(Hy,Tk,H2,H_lookup(ilook),T_lookup(ilook),dT,err,cmessage) if(err/=0) then; message=trim(message)//trim(cmessage); return; end if end do end associate - end subroutine T2E_lookup + end subroutine T2H_lookup_snow ! ************************************************************************************************************************ -! public subroutine T2L_lookup: define a look-up table to compute integral of soil Clapeyron equation liquid water -! matric potential from temperature +! public subroutine T2L_lookup_soil: define a look-up table to compute integral of soil Clapeyron equation liquid water +! matric potential from temperature ! ************************************************************************************************************************ -subroutine T2L_lookup(nSoil, & ! intent(in): number of soil layers - mpar_data, & ! intent(in): parameter data structure - lookup_data, & ! intent(inout): lookup table data structure - err,message) +subroutine T2L_lookup_soil(nSoil, & ! intent(in): number of soil layers + mpar_data, & ! intent(in): parameter data structure + lookup_data, & ! intent(inout): lookup table data structure + err,message) ! ------------------------------------------------------------------------------------------------------------------------- ! downwind routines USE nr_utility_module,only:arth ! use to build vectors with regular increments @@ -175,7 +176,7 @@ subroutine T2L_lookup(nSoil, & ! intent(in): number real(rkind) :: matricHead ! matric head (m) ! ------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="T2L_lookup/" + err=0; message="T2L_lookup_soil/" ! get the values of temperature for the lookup table xIncr = 1._rkind/real(nLook-1, kind(rkind)) @@ -278,19 +279,19 @@ subroutine T2L_lookup(nSoil, & ! intent(in): number end associate end do ! (looping through soil layers) -end subroutine T2L_lookup +end subroutine T2L_lookup_soil ! ************************************************************************************************************************ -! public subroutine enthTemp2T_snow: compute temperature based on specific temperature component of enthalpy -! appropriate when no dry mass, as in snow +! public subroutine H2T_snow: compute temperature based on specific temperature component of enthalpy +! appropriate when no dry mass, as in snow ! ************************************************************************************************************************ -subroutine enthTemp2T_snow(Ey,BulkDenWater,fc_param,Tk,err,message) +subroutine H2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) ! ------------------------------------------------------------------------------------------------------------------------- implicit none ! ------------------------------------------------------------------------------------------------------------------------- ! declare dummy variables - real(rkind),intent(in) :: Ey ! total temperature component of enthalpy (J m-3) + real(rkind),intent(in) :: Hy ! total temperature component of enthalpy (J m-3) real(rkind),intent(in) :: BulkDenWater ! bulk density of water (kg m-3) real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) real(rkind),intent(out) :: Tk ! initial temperature guess / final temperature value (K) @@ -299,8 +300,8 @@ subroutine enthTemp2T_snow(Ey,BulkDenWater,fc_param,Tk,err,message) ! declare local variables real(rkind),parameter :: dx=1.d-8 ! finite difference increment (J kg-1) real(rkind),parameter :: atol=1.d-12 ! convergence criteria (J kg-1) - real(rkind) :: E_spec ! specific enthalpy (J kg-1) - real(rkind) :: E_incr ! enthalpy increment + real(rkind) :: H_spec ! specific enthalpy (J kg-1) + real(rkind) :: H_incr ! enthalpy increment integer(i4b) :: niter=15 ! maximum number of iterations integer(i4b) :: iter ! iteration index integer(i4b) :: i0 ! position in lookup table @@ -311,30 +312,30 @@ subroutine enthTemp2T_snow(Ey,BulkDenWater,fc_param,Tk,err,message) real(rkind) :: dT ! temperature increment ! ------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="enthTemp2T_snow/" + err=0; message="H2T_snow/" ! convert input of total enthalpy (J m-3) to total specific enthalpy (J kg-1) - E_spec = Ey/BulkDenWater ! (NOTE: no soil) + H_spec = Hy/BulkDenWater ! (NOTE: no soil) ! ***** get initial guess and derivative assuming all water is frozen - if(E_spec E_lookup(i0+1) .or. & + if(H_spec < H_lookup(i0) .or. H_spec > H_lookup(i0+1) .or. & i0 < 1 .or. i0+1 > nlook)then err=10; message=trim(message)//'problem finding appropriate value in lookup table'; return end if @@ -342,8 +343,8 @@ subroutine enthTemp2T_snow(Ey,BulkDenWater,fc_param,Tk,err,message) Tg0 = T_lookup(i0) Tg1 = T_lookup(i0+1) ! compute function evaluations - f0 = E_lookup(i0) - E_spec - f1 = E_lookup(i0+1) - E_spec + f0 = H_lookup(i0) - H_spec + f1 = H_lookup(i0+1) - H_spec end if ! compute initial derivative @@ -361,8 +362,8 @@ subroutine enthTemp2T_snow(Ey,BulkDenWater,fc_param,Tk,err,message) ! comute new value of Tg Tg1 = Tg0+dT ! get new function evaluation - Ht1 = T2enthTemp_snow(Tg1,1._rkind,fc_param) - f1 = Ht1 - E_spec + Ht1 = T2H_snow(Tg1,1._rkind,fc_param) + f1 = Ht1 - H_spec ! compute derivative if dT dh = (f1 - f0)/dT ! compute change in T @@ -378,22 +379,22 @@ subroutine enthTemp2T_snow(Ey,BulkDenWater,fc_param,Tk,err,message) ! and check for convergence if(iter==niter)then; err=20; message=trim(message)//"failedToConverge"; return; end if end do ! (iteration loop) -end subroutine enthTemp2T_snow +end subroutine H2T_snow ! ************************************************************************************************************************ -! public function T2enthTemp_snow: compute temperature component of enthalpy based on temperature and mass (J m-3) for a -! layer only where the layer has no dry mass, as in snow -! NOTE: enthalpy is a relative value, defined as zero at Tfreeze where all water is liquid +! public function T2H_snow: compute liquid and ice mixture enthalpy based on temperature and mass (J m-3) for a +! layer only where the layer has no dry mass, as in snow +! NOTE: enthalpy is a relative value, defined as zero at Tfreeze where all water is liquid ! ************************************************************************************************************************ -function T2enthTemp_snow(Tk,BulkDenWater,fc_param) +function T2H_snow(Tk,BulkDenWater,fc_param) ! ------------------------------------------------------------------------------------------------------------------------- implicit none ! declare dummy variables real(rkind),intent(in) :: Tk ! layer temperature (K) real(rkind),intent(in) :: BulkDenWater ! bulk density of water (kg m-3) real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) - real(rkind) :: T2enthTemp_snow ! return value of the function, total specific enthalpy (J m-3) + real(rkind) :: T2H_snow ! return value of the function, total specific enthalpy (J m-3) ! declare local variables real(rkind) :: frac_liq ! fraction of liquid water real(rkind) :: enthTempWater ! temperature component of specific enthalpy for total water (liquid and ice) (J kg-1) @@ -404,7 +405,7 @@ function T2enthTemp_snow(Tk,BulkDenWater,fc_param) ! compute the temperature component of enthalpy for total water (J kg-1) ! NOTE: negative enthalpy means require energy to bring to Tfreeze - if(Tk< Tfreeze) enthTempWater = Cp_ice*(Tk - Tfreeze) - (Cp_water - Cp_ice)*(atan(fc_param*(Tfreeze - Tk))/fc_param) + if(Tk< Tfreeze) enthTempWater = Cp_ice*(Tk - Tfreeze) - (Cp_water - Cp_ice)*(atan(fc_param*(Tfreeze - Tk))/fc_param) if(Tk>=Tfreeze) enthTempWater = Cp_water*(Tk - Tfreeze) ! compute the mass component of enthalpy -- energy required to melt ice (J kg-1) @@ -412,8 +413,8 @@ function T2enthTemp_snow(Tk,BulkDenWater,fc_param) enthMass = -LH_fus*(1._rkind - frac_liq) ! finally, compute the total enthalpy (J m-3) - T2enthTemp_snow = BulkDenWater*(enthTempWater + enthMass) !+ BulkDenSoil*enthTempSoil -end function T2enthTemp_snow + T2H_snow = BulkDenWater*(enthTempWater + enthMass) !+ BulkDenSoil*enthTempSoil +end function T2H_snow ! ************************************************************************************************************************ diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index afaff5d1e..89b71fbcf 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -540,11 +540,11 @@ subroutine eval8summa(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - else if(ixNrgConserv == closedForm)then ! use residual as (Cp*DeltaTemp + Cm*DeltaTheta)_delta - (phase change)_delta + else ! use residual as (Cp*DeltaTemp + Cm*DeltaTheta)_delta - (phase change)_delta scalarCanairEnthalpyTrial = realMissing scalarCanopyEnthTempTrial = realMissing mLayerEnthTempTrial = realMissing - endif !(choice of how compute heat capacity) + endif !(choice of how conservation of energy is implemented) ! save the number of flux calls per time step indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) + 1 diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 4444993ef..3fc51e960 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -91,10 +91,10 @@ subroutine eval8summaWithPrime(& mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) ! output: new prime values of variables needed in data window outside of internal IDA for Jacobian scalarCanairEnthalpyPrime, & ! intent(out): prime value for enthalpy of the canopy air space (J m-3) - scalarCanopyEnthTempPrime, & ! intent(out): prime value for temperature component of enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthTempPrime, & ! intent(out): prime value for temperature component of enthalpy of the vegetation canopy (W m-3) scalarCanopyWatPrime, & ! intent(out): prime value for total water content of the vegetation canopy (kg m-2 s-1) scalarCanopyIcePrime, & ! intent(out): prime value for mass of ice on the vegetation canopy (kg m-2 s-1) - mLayerEnthTempPrime, & ! intent(out): prime vector of temperature component of enthalpy of each snow and soil layer (J m-3) + mLayerEnthTempPrime, & ! intent(out): prime vector of temperature component of enthalpy of each snow and soil layer (W m-3) mLayerMatricHeadPrime, & ! intent(out): prime vector of matric head of each snow and soil layer (m s-1) mLayerMatricHeadLiqPrime, & ! intent(out): prime vector of liquid water matric potential (m s-1) mLayerVolFracWatPrime, & ! intent(out): prime vector of volumetric total water content of each snow and soil layer (s-1) @@ -163,11 +163,11 @@ subroutine eval8summaWithPrime(& real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial vector for total water matric potential (m) real(rkind),intent(out) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) ! output: new prime values of variables needed in data window outside of internal IDA - real(rkind),intent(out) :: scalarCanairEnthTempPrime ! prime value for temperature of the canopy air space (K s-1) - real(rkind),intent(out) :: scalarCanopyEnthTempPrime ! prime value for temperature of the vegetation canopy (K s-1) + real(rkind),intent(out) :: scalarCanairEnthTempPrime ! prime value for temperature component of enthalpy of the canopy air space (W m-3) + real(rkind),intent(out) :: scalarCanopyEnthTempPrime ! prime value for temperature component of enthalpy of the vegetation canopy (W m-3) real(rkind),intent(out) :: scalarCanopyWatPrime ! prime value for total water content of the vegetation canopy (kg m-2 s-1) real(rkind),intent(out) :: scalarCanopyIcePrime ! prime value for mass of ice on the vegetation canopy (kg m-2 s-1) - real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! prime vector of temperature of each snow and soil layer (K s-1) + real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! prime vector of temperature component of enthalpy of each snow and soil layer (W m-3) real(rkind),intent(out) :: mLayerMatricHeadPrime(:) ! prime vector of matric head of each snow and soil layer (m s-1) real(rkind),intent(out) :: mLayerMatricHeadLiqPrime(:) ! prime vector of liquid water matric potential (m s-1) real(rkind),intent(out) :: mLayerVolFracWatPrime(:) ! prime vector of volumetric total water content of each snow and soil layer (s-1) @@ -375,25 +375,55 @@ subroutine eval8summaWithPrime(& prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy - scalarCanairEnthalpyPrime, & ! intent(inout): derivative of enthalpy of the canopy air space (J m-3) - scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K) - scalarCanopyEnthTempPrime, & ! intent(inout): derivative of temperature component of enthalpy of the vegetation canopy (J m-3) - scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): derivative of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2) + scalarCanairEnthalpyPrime, & ! intent(inout): derivative of enthalpy of the canopy air space (W m-3) + scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K s-1) + scalarCanopyEnthTempPrime, & ! intent(inout): derivative of temperature component of enthalpy of the vegetation canopy (W m-3) + scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K s-1) + scalarCanopyWatPrime, & ! intent(inout): derivative of canopy total water (kg m-2 s-1) + scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2 s-1) ! output: variables for the snow-soil domain - mLayerEnthTempPrime, & ! intent(inout): derivative of temperature component of enthalpy of each snow and soil layer (J m-3) - mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K) - mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (-) - mLayerMatricHeadPrime, & ! intent(inout): derivative of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): derivative of liquid water matric potential (m) + mLayerEnthTempPrime, & ! intent(inout): derivative of temperature component of enthalpy of each snow and soil layer (W m-3) + mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K s-1) + mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (s-1) + mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (s-1) + mLayerMatricHeadPrime, & ! intent(inout): derivative of total water matric potential (m s-1) + mLayerMatricHeadLiqPrime, & ! intent(inout): derivative of liquid water matric potential (m s-1) ! output: variables for the aquifer - scalarAquiferStoragePrime, & ! intent(inout): derivative of storage of water in the aquifer (m) + scalarAquiferStoragePrime, & ! intent(inout): derivative of storage of water in the aquifer (m s-1) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + if(ixNrgConserv.ne.closedForm)then ! use state variable as enthalpy, need to compute temperature + ! compute temperature component of enthalpy + call enthTemp2T(& + ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: enthalpy state variables + scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTempTrial, & ! intent(in): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthTempTrial, & ! intent(in): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpyPrime, & ! intent(in): derivative of temperature component of enthalpy of the vegetation canopy (W m-3) + scalarCanopyEnthTempPrime, & ! intent(in): derivative of temperature component of enthalpy of the vegetation canopy (W m-3) + mLayerEnthTempPrime, & ! intent(in): derivative of temperature component of enthalpy of each snow+soil layer (W m-3) + ! output: temperature for the vegetation canopy + scalarCanairTempTrial, & ! intent(out): trial value for canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(out): trial value for canopy temperature (K) + scalarCanairTempPrime, & ! intent(out): derivative of canopy air temperature (K s-1) + scalarCanopyTempPrime, & ! intent(out): derivative of canopy temperature (K s-1) + ! output: temperature for the snow-soil domain + mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + mLayerTempPrime, & ! intent(out): derivative of layer temperature (K s-1) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + endif !(choice of how conservation of energy is implemented) + ! update diagnostic variables and derivatives call updateVarsWithPrime(& ! input @@ -409,10 +439,10 @@ subroutine eval8summaWithPrime(& scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - scalarCanopyTempPrime, & ! intent(inout): trial value of time derivative canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): trial value of time derivative canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): trial value of time derivative canopy liquid water (kg m-2) - scalarCanopyIcePrime, & ! intent(inout): trial value of time derivative canopy ice content (kg m-2) + scalarCanopyTempPrime, & ! intent(inout): trial value of time derivative canopy temperature (K s-1) + scalarCanopyWatPrime, & ! intent(inout): trial value of time derivative canopy total water (kg m-2 s-1) + scalarCanopyLiqPrime, & ! intent(inout): trial value of time derivative canopy liquid water (kg m-2 s-1) + scalarCanopyIcePrime, & ! intent(inout): trial value of time derivative canopy ice content (kg m-2 s-1) ! output: variables for th snow-soil domain mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) @@ -420,12 +450,12 @@ subroutine eval8summaWithPrime(& mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! intent(inout): trial vector of time derivative layer temperature (K) - mLayerVolFracWatPrime, & ! intent(inout): trial vector of time derivative volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): trial vector of time derivative volumetric liquid water content (-) - mLayerVolFracIcePrime, & ! intent(inout): trial vector of time derivative volumetric ice water content (-) - mLayerMatricHeadPrime, & ! intent(inout): trial vector of time derivative total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): trial vector of time derivative liquid water matric potential (m) + mLayerTempPrime, & ! intent(inout): trial vector of time derivative layer temperature (K s-1) + mLayerVolFracWatPrime, & ! intent(inout): trial vector of time derivative volumetric total water content (s-1) + mLayerVolFracLiqPrime, & ! intent(inout): trial vector of time derivative volumetric liquid water content (s-1) + mLayerVolFracIcePrime, & ! intent(inout): trial vector of time derivative volumetric ice water content (s-1) + mLayerMatricHeadPrime, & ! intent(inout): trial vector of time derivative total water matric potential (m s-1) + mLayerMatricHeadLiqPrime, & ! intent(inout): trial vector of time derivative liquid water matric potential (m s-1) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) @@ -645,6 +675,9 @@ subroutine eval8summaWithPrime(& ! input: enthalpy terms canopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) + scalarCanairEnthalpyPrime, & ! intent(in): prime value for the enthalpy of the canopy air space (W m-3) + scalarCanopyEnthTempPrime, & ! intent(in): prime value for the temperature component of enthalpy of the vegetation canopy (W m-3) + mLayerEnthTempPrime, & ! intent(in): prime vector of the temperature component of enthalpy of each snow and soil layer (W m-3) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU From eef3ba20dff5f86df40083fda7ae348f359acf46 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 11 Mar 2024 23:13:38 +0900 Subject: [PATCH 1195/1472] fixing names --- build/source/driver/summa_setup.f90 | 16 ++++++++-------- build/source/engine/enthalpyTemp.f90 | 20 ++++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index 5b094dc79..81a805de1 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -80,8 +80,8 @@ subroutine summa_paramSetup(summa1_struc, err, message) USE paramCheck_module,only:paramCheck ! module to check consistency of model parameters USE pOverwrite_module,only:pOverwrite ! module to overwrite default parameter values with info from the Noah tables USE read_param_module,only:read_param ! module to read model parameter sets - USE enthalpyTemp_module,only:T2E_lookup ! module to calculate a look-up table for the snow temperature-enthalpy conversion - USE enthalpyTemp_module,only:T2L_lookup ! module to calculate a look-up table for the soil temperature-enthalpy conversion + USE enthalpyTemp_module,only:T2H_lookup_snow ! module to calculate a look-up table for the snow temperature-enthalpy conversion + USE enthalpyTemp_module,only:T2L_lookup_soil ! module to calculate a look-up table for the soil temperature-enthalpy conversion USE var_derive_module,only:fracFuture ! module to calculate the fraction of runoff in future time steps (time delay histogram) USE module_sf_noahmplsm,only:read_mp_veg_parameters ! module to read NOAH vegetation tables ! global data structures @@ -307,16 +307,16 @@ subroutine summa_paramSetup(summa1_struc, err, message) call paramCheck(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! calculate a look-up table for the temperature-enthalpy conversion of snow - ! NOTE1: might be able to make this more efficient by only doing this for the HRUs that have snow - ! NOTE2: this does not actually need to be called for each HRU and GRU - call T2E_lookup(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) + ! calculate a look-up table for the temperature-enthalpy conversion of snow for future snow layer merging + ! NOTE2: H is the mixture enthalpy of snow liquid and ice + call T2H_lookup_snow(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! calculate a lookup table for the temperature-enthalpy conversion of soil - ! NOTE3: L is the integral of soil Clapeyron equation liquid water matric potential from temperature, multiply by Cp_liq*iden_water to get enthalpy + ! NOTE: L is the integral of soil Clapeyron equation liquid water matric potential from temperature + ! multiply by Cp_liq*iden_water to get temperature component of enthalpy if(needLookup)then - call T2L_lookup(gru_struc(iGRU)%hruInfo(iHRU)%nSoil, & ! intent(in): number of soil layers + call T2L_lookup_soil(gru_struc(iGRU)%hruInfo(iHRU)%nSoil, & ! intent(in): number of soil layers mparStruct%gru(iGRU)%hru(iHRU), & ! intent(in): parameter data structure lookupStruct%gru(iGRU)%hru(iHRU), & ! intent(inout): lookup table data structure err,cmessage) ! intent(out): error control diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index d1e07b870..829c3f86d 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -61,8 +61,8 @@ module enthalpyTemp_module ! privacy implicit none -public::T2E_lookup -public::T2L_lookup +public::T2H_lookup_snow +public::T2L_lookup_soil public::enthTemp2T_snow public::T2enthTemp_snow public::T2enthTemp @@ -77,10 +77,10 @@ module enthalpyTemp_module ! ************************************************************************************************************************ -! public subroutine T2E_lookup: define a look-up table to compute temperature component of enthalpy based on temperature +! public subroutine T2H_lookup_snow: define a look-up table to compute temperature component of enthalpy based on temperature ! appropriate when no dry mass, as in snow ! ************************************************************************************************************************ -subroutine T2E_lookup(mpar_data, & ! intent(in): parameter data structure +subroutine T2H_lookup_snow(mpar_data, & ! intent(in): parameter data structure err,message) ! ------------------------------------------------------------------------------------------------------------------------- ! downwind routines @@ -104,7 +104,7 @@ subroutine T2E_lookup(mpar_data, & ! intent(in): paramet integer(i4b) :: ilook ! loop through lookup table ! ------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="T2E_lookup/" + err=0; message="T2H_lookup_snow/" ! associate associate( snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ) @@ -133,13 +133,13 @@ subroutine T2E_lookup(mpar_data, & ! intent(in): paramet end associate - end subroutine T2E_lookup + end subroutine T2H_lookup_snow ! ************************************************************************************************************************ -! public subroutine T2L_lookup: define a look-up table to compute integral of soil Clapeyron equation liquid water +! public subroutine T2L_lookup_soil: define a look-up table to compute integral of soil Clapeyron equation liquid water ! matric potential from temperature ! ************************************************************************************************************************ -subroutine T2L_lookup(nSoil, & ! intent(in): number of soil layers +subroutine T2L_lookup_soil(nSoil, & ! intent(in): number of soil layers mpar_data, & ! intent(in): parameter data structure lookup_data, & ! intent(inout): lookup table data structure err,message) @@ -175,7 +175,7 @@ subroutine T2L_lookup(nSoil, & ! intent(in): number real(rkind) :: matricHead ! matric head (m) ! ------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="T2L_lookup/" + err=0; message="T2L_lookup_soil/" ! get the values of temperature for the lookup table xIncr = 1._rkind/real(nLook-1, kind(rkind)) @@ -278,7 +278,7 @@ subroutine T2L_lookup(nSoil, & ! intent(in): number end associate end do ! (looping through soil layers) -end subroutine T2L_lookup +end subroutine T2L_lookup_soil ! ************************************************************************************************************************ From ffb3bafa6817f06039f004e1fa007038cacb566e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 11 Mar 2024 23:24:00 +0900 Subject: [PATCH 1196/1472] fixing some names and comments --- build/source/engine/enthalpyTemp.f90 | 98 ++++++++++----------- build/source/engine/eval8summaWithPrime.f90 | 46 +++++----- build/source/engine/layerMerge.f90 | 12 +-- build/source/engine/systemSolv.f90 | 4 +- 4 files changed, 80 insertions(+), 80 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 829c3f86d..178c2814a 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -63,25 +63,25 @@ module enthalpyTemp_module implicit none public::T2H_lookup_snow public::T2L_lookup_soil -public::enthTemp2T_snow -public::T2enthTemp_snow +public::H2T_snow +public::T2H_snow public::T2enthTemp public::enthTemp2H private::hyp_2F1_real ! define the snow look-up table used to compute temperature based on enthalpy integer(i4b),parameter :: nlook=10001 ! number of elements in the lookup table -real(rkind),dimension(nlook),public :: E_lookup ! enthalpy values (J kg-1) +real(rkind),dimension(nlook),public :: H_lookup ! enthalpy values (J kg-1) real(rkind),dimension(nlook),public :: T_lookup ! temperature values (K) contains ! ************************************************************************************************************************ -! public subroutine T2H_lookup_snow: define a look-up table to compute temperature component of enthalpy based on temperature -! appropriate when no dry mass, as in snow +! public subroutine T2H_lookup_snow:: define a look-up table to mixture enthalpy based on temperature +! appropriate when no dry mass, as in snow ! ************************************************************************************************************************ subroutine T2H_lookup_snow(mpar_data, & ! intent(in): parameter data structure - err,message) + err,message) ! ------------------------------------------------------------------------------------------------------------------------- ! downwind routines USE nr_utility_module,only:arth ! use to build vectors with regular increments @@ -95,12 +95,12 @@ subroutine T2H_lookup_snow(mpar_data, & ! intent(in): pa ! declare local variables character(len=128) :: cmessage ! error message in downwind routine real(rkind),parameter :: T_start=260.0_rkind ! start temperature value where all liquid water is assumed frozen (K) - real(rkind) :: T_incr,E_incr ! temperature/enthalpy increments + real(rkind) :: T_incr,H_incr ! temperature/enthalpy increments real(rkind),dimension(nlook) :: Tk ! initial temperature vector - real(rkind),dimension(nlook) :: Ey ! initial enthalpy vector + real(rkind),dimension(nlook) :: Hy ! initial enthalpy vector real(rkind),parameter :: waterWght=1._rkind ! weight applied to total water (kg m-3) --- cancels out - real(rkind),dimension(nlook) :: E2 ! 2nd derivatives of the interpolating function at tabulated points - real(rkind) :: dT ! derivative of temperature with enthalpy at E_lookup + real(rkind),dimension(nlook) :: H2 ! 2nd derivatives of the interpolating function at tabulated points + real(rkind) :: dT ! derivative of temperature with enthalpy at H_lookup integer(i4b) :: ilook ! loop through lookup table ! ------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -115,19 +115,19 @@ subroutine T2H_lookup_snow(mpar_data, & ! intent(in): pa ! ***** compute specific enthalpy (NOTE: J m-3 --> J kg-1) ***** do ilook=1,nlook - Ey(ilook) = T2enthTemp_snow(Tk(ilook),waterWght,snowfrz_scale)/waterWght ! (J m-3 --> J kg-1) + Hy(ilook) = T2H_snow(Tk(ilook),waterWght,snowfrz_scale)/waterWght ! (J m-3 --> J kg-1) end do ! define the final enthalpy vector - E_incr = (-Ey(1)) / real(nlook-1, kind(rkind)) ! enthalpy increment - E_lookup = arth(Ey(1),E_incr,nlook) + H_incr = (-Hy(1)) / real(nlook-1, kind(rkind)) ! enthalpy increment + H_lookup = arth(Hy(1),H_incr,nlook) ! use cubic spline interpolation to obtain temperature values at the desired values of enthalpy - call spline(Ey,Tk,1.e30_rkind,1.e30_rkind,E2,err,cmessage) ! get the second derivatives + call spline(Hy,Tk,1.e30_rkind,1.e30_rkind,H2,err,cmessage) ! get the second derivatives if(err/=0) then; message=trim(message)//trim(cmessage); return; end if do ilook=1,nlook - call splint(Ey,Tk,E2,E_lookup(ilook),T_lookup(ilook),dT,err,cmessage) + call splint(Hy,Tk,H2,H_lookup(ilook),T_lookup(ilook),dT,err,cmessage) if(err/=0) then; message=trim(message)//trim(cmessage); return; end if end do @@ -137,12 +137,12 @@ end subroutine T2H_lookup_snow ! ************************************************************************************************************************ ! public subroutine T2L_lookup_soil: define a look-up table to compute integral of soil Clapeyron equation liquid water -! matric potential from temperature +! matric potential from temperature ! ************************************************************************************************************************ subroutine T2L_lookup_soil(nSoil, & ! intent(in): number of soil layers - mpar_data, & ! intent(in): parameter data structure - lookup_data, & ! intent(inout): lookup table data structure - err,message) + mpar_data, & ! intent(in): parameter data structure + lookup_data, & ! intent(inout): lookup table data structure + err,message) ! ------------------------------------------------------------------------------------------------------------------------- ! downwind routines USE nr_utility_module,only:arth ! use to build vectors with regular increments @@ -282,15 +282,15 @@ end subroutine T2L_lookup_soil ! ************************************************************************************************************************ -! public subroutine enthTemp2T_snow: compute temperature based on specific temperature component of enthalpy -! appropriate when no dry mass, as in snow +! public subroutine H2T_snow: compute temperature based on specific temperature component of enthalpy +! appropriate when no dry mass, as in snow ! ************************************************************************************************************************ -subroutine enthTemp2T_snow(Ey,BulkDenWater,fc_param,Tk,err,message) +subroutine H2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) ! ------------------------------------------------------------------------------------------------------------------------- implicit none ! ------------------------------------------------------------------------------------------------------------------------- ! declare dummy variables - real(rkind),intent(in) :: Ey ! total temperature component of enthalpy (J m-3) + real(rkind),intent(in) :: Hy ! total temperature component of enthalpy (J m-3) real(rkind),intent(in) :: BulkDenWater ! bulk density of water (kg m-3) real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) real(rkind),intent(out) :: Tk ! initial temperature guess / final temperature value (K) @@ -299,8 +299,8 @@ subroutine enthTemp2T_snow(Ey,BulkDenWater,fc_param,Tk,err,message) ! declare local variables real(rkind),parameter :: dx=1.d-8 ! finite difference increment (J kg-1) real(rkind),parameter :: atol=1.d-12 ! convergence criteria (J kg-1) - real(rkind) :: E_spec ! specific enthalpy (J kg-1) - real(rkind) :: E_incr ! enthalpy increment + real(rkind) :: H_spec ! specific enthalpy (J kg-1) + real(rkind) :: H_incr ! enthalpy increment integer(i4b) :: niter=15 ! maximum number of iterations integer(i4b) :: iter ! iteration index integer(i4b) :: i0 ! position in lookup table @@ -311,30 +311,30 @@ subroutine enthTemp2T_snow(Ey,BulkDenWater,fc_param,Tk,err,message) real(rkind) :: dT ! temperature increment ! ------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="enthTemp2T_snow/" + err=0; message="H2T_snow/" ! convert input of total enthalpy (J m-3) to total specific enthalpy (J kg-1) - E_spec = Ey/BulkDenWater ! (NOTE: no soil) + H_spec = Hy/BulkDenWater ! (NOTE: no soil) ! ***** get initial guess and derivative assuming all water is frozen - if(E_spec E_lookup(i0+1) .or. & + if(H_spec < H_lookup(i0) .or. H_spec > H_lookup(i0+1) .or. & i0 < 1 .or. i0+1 > nlook)then err=10; message=trim(message)//'problem finding appropriate value in lookup table'; return end if @@ -342,8 +342,8 @@ subroutine enthTemp2T_snow(Ey,BulkDenWater,fc_param,Tk,err,message) Tg0 = T_lookup(i0) Tg1 = T_lookup(i0+1) ! compute function evaluations - f0 = E_lookup(i0) - E_spec - f1 = E_lookup(i0+1) - E_spec + f0 = H_lookup(i0) - H_spec + f1 = H_lookup(i0+1) - H_spec end if ! compute initial derivative @@ -361,8 +361,8 @@ subroutine enthTemp2T_snow(Ey,BulkDenWater,fc_param,Tk,err,message) ! comute new value of Tg Tg1 = Tg0+dT ! get new function evaluation - Ht1 = T2enthTemp_snow(Tg1,1._rkind,fc_param) - f1 = Ht1 - E_spec + Ht1 = T2H_snow(Tg1,1._rkind,fc_param) + f1 = Ht1 - H_spec ! compute derivative if dT dh = (f1 - f0)/dT ! compute change in T @@ -378,22 +378,22 @@ subroutine enthTemp2T_snow(Ey,BulkDenWater,fc_param,Tk,err,message) ! and check for convergence if(iter==niter)then; err=20; message=trim(message)//"failedToConverge"; return; end if end do ! (iteration loop) -end subroutine enthTemp2T_snow +end subroutine H2T_snow ! ************************************************************************************************************************ -! public function T2enthTemp_snow: compute temperature component of enthalpy based on temperature and mass (J m-3) for a -! layer only where the layer has no dry mass, as in snow -! NOTE: enthalpy is a relative value, defined as zero at Tfreeze where all water is liquid +! public function T2H_snow: compute liquid and ice mixture enthalpy based on temperature and mass (J m-3) for a +! layer only where the layer has no dry mass, as in snow +! NOTE: enthalpy is a relative value, defined as zero at Tfreeze where all water is liquid ! ************************************************************************************************************************ -function T2enthTemp_snow(Tk,BulkDenWater,fc_param) +function T2H_snow(Tk,BulkDenWater,fc_param) ! ------------------------------------------------------------------------------------------------------------------------- implicit none ! declare dummy variables real(rkind),intent(in) :: Tk ! layer temperature (K) real(rkind),intent(in) :: BulkDenWater ! bulk density of water (kg m-3) real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) - real(rkind) :: T2enthTemp_snow ! return value of the function, total specific enthalpy (J m-3) + real(rkind) :: T2H_snow ! return value of the function, total specific enthalpy (J m-3) ! declare local variables real(rkind) :: frac_liq ! fraction of liquid water real(rkind) :: enthTempWater ! temperature component of specific enthalpy for total water (liquid and ice) (J kg-1) @@ -404,7 +404,7 @@ function T2enthTemp_snow(Tk,BulkDenWater,fc_param) ! compute the temperature component of enthalpy for total water (J kg-1) ! NOTE: negative enthalpy means require energy to bring to Tfreeze - if(Tk< Tfreeze) enthTempWater = Cp_ice*(Tk - Tfreeze) - (Cp_water - Cp_ice)*(atan(fc_param*(Tfreeze - Tk))/fc_param) + if(Tk< Tfreeze) enthTempWater = Cp_ice*(Tk - Tfreeze) - (Cp_water - Cp_ice)*(atan(fc_param*(Tfreeze - Tk))/fc_param) if(Tk>=Tfreeze) enthTempWater = Cp_water*(Tk - Tfreeze) ! compute the mass component of enthalpy -- energy required to melt ice (J kg-1) @@ -412,8 +412,8 @@ function T2enthTemp_snow(Tk,BulkDenWater,fc_param) enthMass = -LH_fus*(1._rkind - frac_liq) ! finally, compute the total enthalpy (J m-3) - T2enthTemp_snow = BulkDenWater*(enthTempWater + enthMass) !+ BulkDenSoil*enthTempSoil -end function T2enthTemp_snow + T2H_snow = BulkDenWater*(enthTempWater + enthMass) !+ BulkDenSoil*enthTempSoil +end function T2H_snow ! ************************************************************************************************************************ diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 76b236c86..52c67c608 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -363,20 +363,20 @@ subroutine eval8summaWithPrime(& prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy - scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K) - scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): derivative of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2) + scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K s-1) + scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K s-1) + scalarCanopyWatPrime, & ! intent(inout): derivative of canopy total water (kg m-2 s-1) + scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2 s-1) ! output: variables for the snow-soil domain - mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K) - mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (-) - mLayerMatricHeadPrime, & ! intent(inout): derivative of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): derivative of liquid water matric potential (m) + mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K s-1) + mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (s-1) + mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (s-1) + mLayerMatricHeadPrime, & ! intent(inout): derivative of total water matric potential (m s-1) + mLayerMatricHeadLiqPrime, & ! intent(inout): derivative of liquid water matric potential (m s-1) ! output: variables for the aquifer - scalarAquiferStoragePrime, & ! intent(inout): derivative of storage of water in the aquifer (m) + scalarAquiferStoragePrime, & ! intent(inout): derivative of storage of water in the aquifer (m s-1) ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! update diagnostic variables and derivatives @@ -394,10 +394,10 @@ subroutine eval8summaWithPrime(& scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - scalarCanopyTempPrime, & ! intent(inout): trial value of time derivative canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): trial value of time derivative canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): trial value of time derivative canopy liquid water (kg m-2) - scalarCanopyIcePrime, & ! intent(inout): trial value of time derivative canopy ice content (kg m-2) + scalarCanopyTempPrime, & ! intent(inout): trial value of time derivative canopy temperature (K s-1) + scalarCanopyWatPrime, & ! intent(inout): trial value of time derivative canopy total water (kg m-2 s-1) + scalarCanopyLiqPrime, & ! intent(inout): trial value of time derivative canopy liquid water (kg m-2 s-1) + scalarCanopyIcePrime, & ! intent(inout): trial value of time derivative canopy ice content (kg m-2 s-1) ! output: variables for th snow-soil domain mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) @@ -405,14 +405,14 @@ subroutine eval8summaWithPrime(& mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! intent(inout): trial vector of time derivative layer temperature (K) - mLayerVolFracWatPrime, & ! intent(inout): trial vector of time derivative volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): trial vector of time derivative volumetric liquid water content (-) - mLayerVolFracIcePrime, & ! intent(inout): trial vector of time derivative volumetric ice water content (-) - mLayerMatricHeadPrime, & ! intent(inout): trial vector of time derivative total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): trial vector of time derivative liquid water matric potential (m) + mLayerTempPrime, & ! intent(inout): trial vector of time derivative layer temperature (K s-1) + mLayerVolFracWatPrime, & ! intent(inout): trial vector of time derivative volumetric total water content (s-1) + mLayerVolFracLiqPrime, & ! intent(inout): trial vector of time derivative volumetric liquid water content (s-1) + mLayerVolFracIcePrime, & ! intent(inout): trial vector of time derivative volumetric ice water content (s-1) + mLayerMatricHeadPrime, & ! intent(inout): trial vector of time derivative total water matric potential (m s-1) + mLayerMatricHeadLiqPrime, & ! intent(inout): trial vector of time derivative liquid water matric potential (m s-1) ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) if(updateCp)then @@ -588,7 +588,7 @@ subroutine eval8summaWithPrime(& ! input: ixRichards, & ! intent(in): choice of option for Richards' equation ixBeg,ixEnd, & ! intent(in): start and end indices defining desired layers - mLayerMatricHeadPrime(1:nSoil), & ! intent(in): matric head at the start of the time step (m) + mLayerMatricHeadPrime(1:nSoil), & ! intent(in): matric head at the start of the time step (m s-1) mLayerVolFracLiqTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric liquid water content in each soil layer (-) mLayerVolFracIceTrial(nSnow+1:nLayers), & ! intent(in): trial value for the volumetric ice content in each soil layer (-) specificStorage, & ! intent(in): specific storage coefficient (m-1) diff --git a/build/source/engine/layerMerge.f90 b/build/source/engine/layerMerge.f90 index 554690c7a..ec65f087c 100644 --- a/build/source/engine/layerMerge.f90 +++ b/build/source/engine/layerMerge.f90 @@ -297,7 +297,7 @@ subroutine layer_combine(mpar_data,prog_data,diag_data,flux_data,indx_data,iSnow USE data_types,only:var_d ! data structures with fixed dimension ! provide access to external modules USE snow_utils_module,only:fracliquid ! compute fraction of liquid water - USE enthalpyTemp_module,only:enthTemp2T_snow,T2enthTemp_snow ! convert temperature to enthalpy for a snow layer + USE enthalpyTemp_module,only:H2T_snow,T2H_snow ! convert temperature to enthalpy for a snow layer implicit none ! ------------------------------------------------------------------------------------------------------------ ! input/output: data structures @@ -361,19 +361,19 @@ subroutine layer_combine(mpar_data,prog_data,diag_data,flux_data,indx_data,iSnow cBulkDenWat = (mLayerDepth(isnow)*bulkDenWat(1) + mLayerDepth(isnow+1)*bulkDenWat(2))/cDepth ! compute enthalpy for each layer (J m-3) - l1Enthalpy = T2enthTemp_snow(mLayerTemp(iSnow), BulkDenWat(1),snowfrz_scale) - l2Enthalpy = T2enthTemp_snow(mLayerTemp(iSnow+1),BulkDenWat(2),snowfrz_scale) + l1Enthalpy = T2H_snow(mLayerTemp(iSnow), BulkDenWat(1),snowfrz_scale) + l2Enthalpy = T2H_snow(mLayerTemp(iSnow+1),BulkDenWat(2),snowfrz_scale) ! compute combined enthalpy (J m-3) cEnthalpy = (mLayerDepth(isnow)*l1Enthalpy + mLayerDepth(isnow+1)*l2Enthalpy)/cDepth ! convert enthalpy (J m-3) to temperature (K) - call enthTemp2T_snow(cEnthalpy,cBulkDenWat,snowfrz_scale,cTemp,err,cmessage) + call H2T_snow(cEnthalpy,cBulkDenWat,snowfrz_scale,cTemp,err,cmessage) if(err/=0)then; err=10; message=trim(message)//trim(cmessage); return; end if ! test enthalpy conversion - if(abs(T2enthTemp_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat - cEnthalpy/cBulkDenWat) > eTol)then - write(*,'(a,1x,f12.5,1x,2(e20.10,1x))') 'enthalpy test', cBulkDenWat, T2enthTemp_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat, cEnthalpy/cBulkDenWat + if(abs(T2H_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat - cEnthalpy/cBulkDenWat) > eTol)then + write(*,'(a,1x,f12.5,1x,2(e20.10,1x))') 'enthalpy test', cBulkDenWat, T2H_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat, cEnthalpy/cBulkDenWat message=trim(message)//'problem with enthalpy-->temperature conversion' err=20; return end if diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index b07bf5233..d481c38a5 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -160,7 +160,7 @@ subroutine systemSolv(& USE allocspace_module,only:allocLocal ! allocate local data structures ! state vector and solver USE getVectorz_module,only:getScaling ! get the scaling vectors - USE enthalpyTemp_module,only:T2enthTemp_snow ! convert temperature to enthalpy for a snow layer + USE enthalpyTemp_module,only:T2H_snow ! convert temperature to enthalpy for a snow layer USE enthalpyTemp_module,only:T2enthTemp ! compute enthalpy #ifdef SUNDIALS_ACTIVE USE tol4ida_module,only:popTol4ida ! populate tolerances @@ -477,7 +477,7 @@ subroutine systemSolv(& if(nSnow>0)then ! compute the energy required to melt the top snow layer (J m-2) bulkDensity = mLayerVolFracIce(1)*iden_ice + mLayerVolFracLiq(1)*iden_water - volEnthalpy = T2enthTemp_snow(mLayerTemp(1),bulkDensity,snowfrz_scale) + volEnthalpy = T2H_snow(mLayerTemp(1),bulkDensity,snowfrz_scale) ! set flag and error codes for too much melt if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_cur)then tooMuchMelt = .true. From 96571a874aba2dbba6945b5c598bec13c97f172b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 11 Mar 2024 23:28:10 +0900 Subject: [PATCH 1197/1472] fix function call names --- build/source/engine/layerMerge.f90 | 12 ++++++------ build/source/engine/systemSolv.f90 | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build/source/engine/layerMerge.f90 b/build/source/engine/layerMerge.f90 index 554690c7a..f14a2c572 100644 --- a/build/source/engine/layerMerge.f90 +++ b/build/source/engine/layerMerge.f90 @@ -297,7 +297,7 @@ subroutine layer_combine(mpar_data,prog_data,diag_data,flux_data,indx_data,iSnow USE data_types,only:var_d ! data structures with fixed dimension ! provide access to external modules USE snow_utils_module,only:fracliquid ! compute fraction of liquid water - USE enthalpyTemp_module,only:enthTemp2T_snow,T2enthTemp_snow ! convert temperature to enthalpy for a snow layer + USE enthalpyTemp_module,only:H2T_snow,T2H_snow ! convert temperature to enthalpy for a snow layer implicit none ! ------------------------------------------------------------------------------------------------------------ ! input/output: data structures @@ -361,19 +361,19 @@ subroutine layer_combine(mpar_data,prog_data,diag_data,flux_data,indx_data,iSnow cBulkDenWat = (mLayerDepth(isnow)*bulkDenWat(1) + mLayerDepth(isnow+1)*bulkDenWat(2))/cDepth ! compute enthalpy for each layer (J m-3) - l1Enthalpy = T2enthTemp_snow(mLayerTemp(iSnow), BulkDenWat(1),snowfrz_scale) - l2Enthalpy = T2enthTemp_snow(mLayerTemp(iSnow+1),BulkDenWat(2),snowfrz_scale) + l1Enthalpy = T2H_snow(mLayerTemp(iSnow), BulkDenWat(1),snowfrz_scale) + l2Enthalpy = T2H_snow(mLayerTemp(iSnow+1),BulkDenWat(2),snowfrz_scale) ! compute combined enthalpy (J m-3) cEnthalpy = (mLayerDepth(isnow)*l1Enthalpy + mLayerDepth(isnow+1)*l2Enthalpy)/cDepth ! convert enthalpy (J m-3) to temperature (K) - call enthTemp2T_snow(cEnthalpy,cBulkDenWat,snowfrz_scale,cTemp,err,cmessage) + call H2T_snow(cEnthalpy,cBulkDenWat,snowfrz_scale,cTemp,err,cmessage) if(err/=0)then; err=10; message=trim(message)//trim(cmessage); return; end if ! test enthalpy conversion - if(abs(T2enthTemp_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat - cEnthalpy/cBulkDenWat) > eTol)then - write(*,'(a,1x,f12.5,1x,2(e20.10,1x))') 'enthalpy test', cBulkDenWat, T2enthTemp_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat, cEnthalpy/cBulkDenWat + if(abs(T2H_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat - cEnthalpy/cBulkDenWat) > eTol)then + write(*,'(a,1x,f12.5,1x,2(e20.10,1x))') 'enthalpy test', cBulkDenWat, T2H_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat, cEnthalpy/cBulkDenWat message=trim(message)//'problem with enthalpy-->temperature conversion' err=20; return end if diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index b07bf5233..25d8bc14f 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -160,7 +160,7 @@ subroutine systemSolv(& USE allocspace_module,only:allocLocal ! allocate local data structures ! state vector and solver USE getVectorz_module,only:getScaling ! get the scaling vectors - USE enthalpyTemp_module,only:T2enthTemp_snow ! convert temperature to enthalpy for a snow layer + USE enthalpyTemp_module,only:T2H_snow ! convert temperature to enthalpy for a snow layer USE enthalpyTemp_module,only:T2enthTemp ! compute enthalpy #ifdef SUNDIALS_ACTIVE USE tol4ida_module,only:popTol4ida ! populate tolerances @@ -477,7 +477,7 @@ subroutine systemSolv(& if(nSnow>0)then ! compute the energy required to melt the top snow layer (J m-2) bulkDensity = mLayerVolFracIce(1)*iden_ice + mLayerVolFracLiq(1)*iden_water - volEnthalpy = T2enthTemp_snow(mLayerTemp(1),bulkDensity,snowfrz_scale) + volEnthalpy = T2H_snow(mLayerTemp(1),bulkDensity,snowfrz_scale) ! set flag and error codes for too much melt if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_cur)then tooMuchMelt = .true. From 42473652281a858915815090cf0c29dc39354a59 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 11 Mar 2024 23:29:05 +0900 Subject: [PATCH 1198/1472] spaces --- build/source/engine/layerMerge.f90 | 2 +- build/source/engine/systemSolv.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/layerMerge.f90 b/build/source/engine/layerMerge.f90 index ec65f087c..f14a2c572 100644 --- a/build/source/engine/layerMerge.f90 +++ b/build/source/engine/layerMerge.f90 @@ -297,7 +297,7 @@ subroutine layer_combine(mpar_data,prog_data,diag_data,flux_data,indx_data,iSnow USE data_types,only:var_d ! data structures with fixed dimension ! provide access to external modules USE snow_utils_module,only:fracliquid ! compute fraction of liquid water - USE enthalpyTemp_module,only:H2T_snow,T2H_snow ! convert temperature to enthalpy for a snow layer + USE enthalpyTemp_module,only:H2T_snow,T2H_snow ! convert temperature to enthalpy for a snow layer implicit none ! ------------------------------------------------------------------------------------------------------------ ! input/output: data structures diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index d481c38a5..25d8bc14f 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -160,7 +160,7 @@ subroutine systemSolv(& USE allocspace_module,only:allocLocal ! allocate local data structures ! state vector and solver USE getVectorz_module,only:getScaling ! get the scaling vectors - USE enthalpyTemp_module,only:T2H_snow ! convert temperature to enthalpy for a snow layer + USE enthalpyTemp_module,only:T2H_snow ! convert temperature to enthalpy for a snow layer USE enthalpyTemp_module,only:T2enthTemp ! compute enthalpy #ifdef SUNDIALS_ACTIVE USE tol4ida_module,only:popTol4ida ! populate tolerances From b068c42318b21d1f2f18bf29eae5c08064cd707b Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 12 Mar 2024 07:23:47 -0600 Subject: [PATCH 1199/1472] Simplified associate block in systemSolv and modularized numrec case branch. --- build/source/engine/systemSolv.f90 | 95 +++++++++++------------------- 1 file changed, 33 insertions(+), 62 deletions(-) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 18cae53f4..7afa22901 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -267,54 +267,19 @@ subroutine systemSolv(& ! flags logical(lgt) :: return_flag ! flag for handling systemSolv returns trigerred from internal subroutines logical(lgt) :: exit_flag ! flag for handling loop exit statements trigerred from internal subroutines - ! --------------------------------------------------------------------------------------- - ! point to variables in the data structures - ! --------------------------------------------------------------------------------------- + ! ----------------------------------------------------------------------------------------------------------- + + call initialize_systemSolv; if (return_flag) return ! initialize variables and allocate arrays -- return if error + + call initial_function_evaluations; if (return_flag) return ! initial function evaluations -- return if error + globalVars: associate(& ! model decisions ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver - ixHowHeatCap => model_decisions(iLookDECISIONS%howHeatCap)%iDecision ,& ! intent(in): [i4b] heat capacity computation, with or without enthalpy - ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization - ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) - ! enthalpy - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(out): [dp(:)] enthalpy of the snow+soil layers (J m-3) - ! derivatives, diagnostic for enthalpy - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) - ! model state variables - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) - ! check the need to merge snow layers - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ! mapping from full domain to the sub-domain - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b] mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b] index of control volume for different domains (veg, snow, soil) - ! type of state and domain for a given variable - ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] type of desired model state variables - ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] domain for desired model state variables ! layer geometry nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) & ! intent(in): [i4b] number of soil layers ) - ! --------------------------------------------------------------------------------------- - - call initialize_systemSolv; if (return_flag) return ! initialize variables and allocate arrays -- return if error - - call initial_function_evaluations; if (return_flag) return ! initial function evaluations -- retrun if error - ! ************************** ! * Solving the System @@ -424,7 +389,7 @@ subroutine systemSolv(& case(kinsol) !--------------------------- - ! * solving F(y) = 0 by Backward Euler with KINSOL, y is the state vector + ! * solving F(y) = 0 from Backward Euler with KINSOL, y is the state vector !--------------------------- ! iterations and updates to trial state vector, fluxes, and derivatives are done inside IDA solver call summaSolve4kinsol(& @@ -479,26 +444,7 @@ subroutine systemSolv(& #endif case(numrec) - ! define maximum number of iterations - maxiter = nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)) - - ! correct the number of iterations - localMaxIter = merge(scalarMaxIter, maxIter, scalarSolution) - - !--------------------------- - ! * solving F(y) = 0 by Backward Euler with free numerical recipes routines, y is the state vector - !--------------------------- - ! iterate and update trial state vector, fluxes, and derivatives - exit_flag=.false. ! initialize exit flag - do iter=1,localMaxIter ! begin Newton iterations - niter = iter+1 ! # of iterations -- +1 because xFluxResid was moved outside the iteration loop (for backwards compatibility) - call Newton_step; if (return_flag) return ! compute Newton step -- return if error - call check_Newton_convergence ! check current Newton step for convergence - if (exit_flag) exit ! exit loop if convereged - if (return_flag) return ! return if error - end do - - if (post_massCons) call enforce_mass_conservation ! enforce mass conservation if desired + call Newton_iterations_numrec; if (return_flag) return ! Newton iterations using numerical recipes -- return if error end select end associate globalVars ! end associate statements @@ -818,6 +764,31 @@ subroutine enforce_mass_conservation end associate layerVars end subroutine enforce_mass_conservation + subroutine Newton_iterations_numrec + ! ** Compute the backward Euler solution using Newton iterations from numerical recipes ** + + ! define maximum number of iterations + maxiter = nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)) + + ! correct the number of iterations + localMaxIter = merge(scalarMaxIter, maxIter, scalarSolution) + + !--------------------------- + ! * solving F(y) = 0 from Backward Euler with free numerical recipes routines, y is the state vector + !--------------------------- + ! iterate and update trial state vector, fluxes, and derivatives + exit_flag=.false. ! initialize exit flag + do iter=1,localMaxIter ! begin Newton iterations + niter = iter+1 ! # of iterations -- +1 because xFluxResid was moved outside the iteration loop (for backwards compatibility) + call Newton_step; if (return_flag) return ! compute Newton step -- return if error + call check_Newton_convergence ! check current Newton step for convergence + if (exit_flag) exit ! exit loop if convereged + if (return_flag) return ! return if error + end do + + if (post_massCons) call enforce_mass_conservation ! enforce mass conservation if desired + end subroutine Newton_iterations_numrec + subroutine finalize_systemSolv ! set untapped melt energy to zero untappedMelt(:) = 0._rkind From 644e2d5853a4d830d4f0208d407fbf7e3851b94a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 12 Mar 2024 23:04:03 +0900 Subject: [PATCH 1200/1472] utils and spaces --- build/source/engine/enthalpyTemp.f90 | 16 ++++++++-------- utils/bal_per_GRU.py | 2 -- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 6dcfd48cd..c1c5f57e6 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -597,11 +597,11 @@ subroutine T2enthTemp(& case(iname_soil) ! make association to variables for soil soilVars: associate(& - soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex) , & ! intrinsic soil density (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex) , & ! soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex) , & ! volumetric residual water content (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) & ! van Genuchten "n" parameter (-) + soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex), & ! intrinsic soil density (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex), & ! soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex), & ! volumetric residual water content (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex), & ! van Genuchten "alpha" parameter (m-1) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) & ! van Genuchten "n" parameter (-) ) ! end associate statement ! diagnostic variables @@ -627,9 +627,9 @@ subroutine T2enthTemp(& if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature ! make associate to the the lookup table lookVars: associate(& - Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup , & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) - L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup, & ! temperature (K) + Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup, & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) + L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function ) ! end associate statement ! get the lower limit of the integral diff --git a/utils/bal_per_GRU.py b/utils/bal_per_GRU.py index ec8643088..9ce495f0f 100644 --- a/utils/bal_per_GRU.py +++ b/utils/bal_per_GRU.py @@ -34,7 +34,6 @@ method_name=['be1','be1en','be1lu'] #maybe make this an argument # Simulation statistics file locations -viz_fil = method_name.copy() viz_fl2 = method_name.copy() for i, m in enumerate(method_name): viz_fl2[i] = m + '_hrly_diff_bals_{}.nc' @@ -61,7 +60,6 @@ fig_fil2 =fig_fil2.format(stat) summa = {} -wall = {} for i, m in enumerate(method_name): # Get the aggregated statistics of SUMMA simulations summa[m] = xr.open_dataset(viz_dir/viz_fl2[i]) From a30f66c6f10e6a7b35cd3cc53acdccedddcf2ddd Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 12 Mar 2024 23:04:48 +0900 Subject: [PATCH 1201/1472] enthTemp2T start --- build/source/engine/enthalpyTemp.f90 | 311 ++++++++++++++++++++ build/source/engine/eval8summaWithPrime.f90 | 9 + 2 files changed, 320 insertions(+) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index c1c5f57e6..f74e8f8d5 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -799,6 +799,317 @@ subroutine enthTemp2H(& end subroutine enthTemp2H + +! ************************************************************************************************************************ +! public subroutine enthTemp2T: compute temperature component of enthalpy from temperature and total water content +! ************************************************************************************************************************ +subroutine enthTemp2T(& + use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: enthalpy state variables + scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTempTrial, & ! intent(in): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthTempTrial, & ! intent(in): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpyPrime, & ! intent(in): derivative of temperature component of enthalpy of the vegetation canopy (W m-3) + scalarCanopyEnthTempPrime, & ! intent(in): derivative of temperature component of enthalpy of the vegetation canopy (W m-3) + mLayerEnthTempPrime, & ! intent(in): derivative of temperature component of enthalpy of each snow+soil layer (W m-3) + ! input: water state variables + scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + scalarCanopyWatPrime, & ! intent(in): derivative of canopy total water (kg m-2 s-1) + mLayerVolFracWatPrime, & ! intent(in): derivative of volumetric total water content (s-1) + mLayerMatricHeadPrime, & ! intent(in): derivative of total water matric potential (m s-1) + ! output: temperature for the vegetation canopy + scalarCanairTempTrial, & ! intent(out): trial value for canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(out): trial value for canopy temperature (K) + scalarCanairTempPrime, & ! intent(out): derivative of canopy air temperature (K s-1) + scalarCanopyTempPrime, & ! intent(out): derivative of canopy temperature (K s-1) + ! output: temperature for the snow-soil domain + mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + mLayerTempPrime, & ! intent(out): derivative of layer temperature (K s-1) + ! output: error control + err,cmessage) ! intent(out): error control + ! ------------------------------------------------------------------------------------------------------------------------- + ! downwind routines + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water + USE spline_int_module,only:splint ! use for cubic spline interpolation + implicit none + ! delare dummy variables + ! ------------------------------------------------------------------------------------------------------------------------- + logical(lgt),intent(in) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function + ! input: data structures + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model indices + type(zLookup),intent(in) :: lookup_data ! lookup tables + ! output: enthalpy state variables + real(rkind),intent(in) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) + real(rkind),intent(in) :: scalarCanopyEnthTemp ! temperature component of enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(in) :: mLayerEnthTemp(:) ! temperature component of enthalpy of each snow+soil layer (J m-3) + real(rkind),intent(in) :: scalarCanairEnthalpyPrime ! derivative of temperature component of enthalpy of the vegetation canopy (W m-3) + real(rkind),intent(in) :: scalarCanopyEnthTempPrime ! derivative of temperature component of enthalpy of the vegetation canopy (W m-3) + real(rkind),intent(in) :: mLayerEnthTempPrime(:) ! derivative of temperature component of enthalpy of each snow+soil layer (W m-3) + ! input: water state variables + real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind),intent(in) :: scalarCanopyWatPrime ! derivative of canopy total water (kg m-2 s-1) + real(rkind),intent(in) :: mLayerVolFracWatPrime(:) ! derivative of volumetric total water content (s-1) + real(rkind),intent(in) :: mLayerMatricHeadPrime(:) ! derivative of total water matric potential (m s-1) + ! output: temperature for the vegetation canopy + real(rkind),intent(out) :: scalarCanairTempTrial ! trial value for canopy air temperature (K) + real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value for canopy temperature (K) + real(rkind),intent(out) :: scalarCanairTempPrime ! derivative of canopy air temperature (K s-1) + real(rkind),intent(out) :: scalarCanopyTempPrime ! derivative of canopy temperature (K s-1) + ! output: temperature for the snow-soil domain + real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(out) :: mLayerTempPrime(:) ! derivative of layer temperature (K s-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------------------------------- + ! declare local variables + character(len=128) :: cmessage ! error message in downwind routine + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixDomainType ! name of a given model domain + integer(i4b) :: ixControlIndex ! index within a given model domain + real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind) :: volFracWat ! volumetric fraction of total water, liquid+ice (-) + real(rkind) :: diff0 ! temperature difference of Tcrit from Tfreeze + real(rkind) :: diffT ! temperature difference of temp soil from Tfreeze + real(rkind) :: integral ! integral of snow freezing curve + real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head + real(rkind) :: dL ! derivative of enthalpy with temperature at layer temperature + real(rkind) :: arg ! argument of hypergeometric function + real(rkind) :: gauss_hg_T ! hypergeometric function result + real(rkind) :: integral_unf ! integral of unfrozen soil water content (from Tfreeze to Tcrit) + real(rkind) :: integral_frz_low ! lower limit of integral of frozen soil water content (from Tfreeze to Tcrit) + real(rkind) :: integral_frz_upp ! upper limit of integral of frozen soil water content (from Tfreeze to soil temperature) + real(rkind) :: xConst ! constant in the freezing curve function (m K-1) + real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) + ! enthalpy + real(rkind) :: enthVeg ! enthalpy of the vegetation (J m-3) + real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) + real(rkind) :: enthLiq ! enthalpy of the liquid region (J m-3) + real(rkind) :: enthIce ! enthalpy of the ice region (J m-3) + real(rkind) :: enthAir ! enthalpy of air (J m-3) + real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) + real(rkind) :: enthWater ! enthalpy of total water (J m-3) + logical(lgt),parameter :: doTest=.false. ! flag to run unit test + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + generalVars: associate(& + ! number of model layers, and layer type + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + ! mapping between the full state vector and the state subset + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ! type of domain, type of state variable, and index of control volume within domain + ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat & ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ) ! end associate statement + ! ------------------------------------------------------------------------------------------------------------------------------ + + ! initialize error control + err=0; message="enthTemp2T/" + + ! loop through model state variables + do iState=1,size(ixMapSubset2Full) + + ! ----- + ! - compute indices... + ! -------------------- + + ! get domain type, and index of the control volume within the domain + ixFullVector = ixMapSubset2Full(iState) ! index within full state vector + ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) + ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain + + ! check an energy state + if(ixStateType(ixFullVector)==iname_nrgCanair .or. ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer)then + + ! get the layer index + select case(ixDomainType) + case(iname_cas); iLayer = integerMissing + case(iname_veg); iLayer = integerMissing + case(iname_snow); iLayer = ixControlIndex + case(iname_soil); iLayer = ixControlIndex + nSnow + case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + + ! identify domain + select case(ixDomainType) + case(iname_cas) + scalarCanairTempTrial = scalarCanairEnthalpy / ( Cp_air*iden_air ) + Tfreeze + scalarCanairTempPrime = scalarCanairEnthalpyPrime / ( Cp_air*iden_air ) + + case(iname_veg) + ! association to necessary variables for vegetation + vegVars: associate(& + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! canopy depth (m) + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! maximum mass of vegetation (kg m-2) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! scaling parameter for the snow freezing curve (K-1) + ) + + diffT = scalarCanopyTempTrial - Tfreeze + enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth + + if(diffT>=0._rkind)then + enthLiq = Cp_water * scalarCanopyWatTrial * diffT / canopyDepth + enthIce = 0._rkind + else + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth + enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth + endif + + ! diffT>=0._rkind + scalarCanopyTempTrial = scalarCanopyEnthTemp * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial ) + Tfreeze + scalarCanopyTempPrime = scalarCanopyEnthTempPrime * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial ) + +diffT<0._rkind scalarCanopyEnthTemp * canopyDepth = (specificHeatVeg * maxMassVegetation + Cp_ice * scalarCanopyWatTrial )* diffT + (Cp_water - Cp_ice)* scalarCanopyWatTrial * integral + + + end associate vegVars + + case(iname_snow) + + ! association to necessary variables for snow + snowVars: associate(& + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) + + diffT = mLayerTempTrial(iLayer) - Tfreeze ! diffT<0._rkind because snow is frozen + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral + enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) + enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) + + mLayerEnthTemp(iLayer) = enthLiq + enthIce + enthAir + + end associate snowVars + + case(iname_soil) + ! make association to variables for soil + soilVars: associate(& + soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex), & ! intrinsic soil density (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex), & ! soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex), & ! volumetric residual water content (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex), & ! van Genuchten "alpha" parameter (m-1) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) & ! van Genuchten "n" parameter (-) + ) ! end associate statement + + ! diagnostic variables + vGn_m = 1._rkind - 1._rkind/vGn_n + Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) + volFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + diffT = mLayerTempTrial(iLayer) - Tfreeze + diff0 = Tcrit - Tfreeze + ! *** compute enthalpy of water for unfrozen conditions + if(mlayerTempTrial(iLayer)>=Tcrit)then + enthWater = iden_water * Cp_water * volFracWat * diffT ! valid for temperatures below freezing also + + ! *** compute enthalpy of water for frozen conditions + else + ! *** compute integral of mLayerPsiLiq from Tfreeze to layer temperature + ! get the unfrozen water content + integral_unf = ( Tcrit - Tfreeze ) * volFracWat + + ! get the frozen water content + ! initialize for case Tcrit=Tfreeze, i.e. mLayerMatricHeadTrial(ixControlIndex)>0 + integral_frz_low = 0._rkind + + if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature + ! make associate to the the lookup table + lookVars: associate(& + Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup, & ! temperature (K) + Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup, & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) + L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + ) ! end associate statement + + ! get the lower limit of the integral + if(diff0<0._rkind)then + call splint(Tk,Ly,L2,Tcrit,integral_frz_low,dL,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + end if + ! get the upper limit of the integral + call splint(Tk,Ly,L2,mlayerTempTrial(iLayer),integral_frz_upp,dL,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + end associate lookVars + + else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature + ! get the lower limit of the integral + if(diff0<0._rkind)then + arg = (vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n + gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + integral_frz_low = diff0 * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + end if + ! get the upper limit of the integral + xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) + mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution, DIFFERENT from the liquid water matric potential used in the flux calculations + arg = (vGn_alpha * mLayerPsiLiq)**vGn_n + gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + integral_frz_upp = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + endif + + enthLiq = iden_water * Cp_water * (integral_unf + integral_frz_upp - integral_frz_low) + enthIce = iden_ice * Cp_ice * ( volFracWat * diffT - (integral_unf + integral_frz_upp - integral_frz_low) ) + enthWater = enthIce + enthLiq + + if(doTest)then + + ! write values + print*, 'doTest = ', doTest + print*, 'T,Tcrit = ', mlayerTempTrial(iLayer),Tcrit ! temperature (K) + print*, 'integral unf,frz_upp,frz_low = ', integral_unf, integral_frz_upp, integral_frz_low ! integral (K) + print*, 'theta_sat = ', theta_sat ! soil porosity (-) + print*, 'theta_res = ', theta_res ! volumetric residual water content (-) + print*, 'vGn_alpha = ', vGn_alpha ! van Genuchten "alpha" parameter (m-1) + print*, 'vGn_n = ', vGn_n ! van Genuchten "n" parameter (-) + print*, trim(message)//'PAUSE: Set doTest=.false. to complete simulations' + read(*,*) + + endif ! if testing + + endif ! (if frozen conditions) + + enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*diffT + enthAir = iden_air*Cp_air*(1._rkind - theta_sat - volFracWat)*diffT + + mLayerEnthTemp(iLayer) = enthWater + enthSoil + enthAir + + end associate soilVars + + ! ----- + ! - checks... + ! ----------- + case(iname_aquifer); cycle ! aquifer: do nothing + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + + end if ! if an energy layer + end do ! looping through state variables + + end associate generalVars + +end subroutine T2enthTemp + + !---------------------------------------------------------------------- ! private function: compute hypergeometric function with real arguments into real result !---------------------------------------------------------------------- diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 3fc51e960..93b5a2fbf 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -395,6 +395,8 @@ subroutine eval8summaWithPrime(& if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) if(ixNrgConserv.ne.closedForm)then ! use state variable as enthalpy, need to compute temperature + + NEED TO UPDATE WATER STATE FIRST TO MAKE SURE HAVE ALL THESE WATER STATE VARIABLES, SEE updateVarsWithPrime ! compute temperature component of enthalpy call enthTemp2T(& ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy @@ -410,6 +412,13 @@ subroutine eval8summaWithPrime(& scalarCanairEnthalpyPrime, & ! intent(in): derivative of temperature component of enthalpy of the vegetation canopy (W m-3) scalarCanopyEnthTempPrime, & ! intent(in): derivative of temperature component of enthalpy of the vegetation canopy (W m-3) mLayerEnthTempPrime, & ! intent(in): derivative of temperature component of enthalpy of each snow+soil layer (W m-3) + ! input: water state variables + scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + scalarCanopyWatPrime, & ! intent(in): derivative of canopy total water (kg m-2 s-1) + mLayerVolFracWatPrime, & ! intent(in): derivative of volumetric total water content (s-1) + mLayerMatricHeadPrime, & ! intent(in): derivative of total water matric potential (m s-1) ! output: temperature for the vegetation canopy scalarCanairTempTrial, & ! intent(out): trial value for canopy air temperature (K) scalarCanopyTempTrial, & ! intent(out): trial value for canopy temperature (K) From 88d0cc4e9154447e924b767eaedba91fd0f35b8a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 12 Mar 2024 23:04:03 +0900 Subject: [PATCH 1202/1472] utils and spaces --- build/source/engine/enthalpyTemp.f90 | 16 ++++++++-------- utils/bal_per_GRU.py | 2 -- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 178c2814a..4b21707ec 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -596,11 +596,11 @@ subroutine T2enthTemp(& case(iname_soil) ! make association to variables for soil soilVars: associate(& - soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex) , & ! intrinsic soil density (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex) , & ! soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex) , & ! volumetric residual water content (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) & ! van Genuchten "n" parameter (-) + soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex), & ! intrinsic soil density (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex), & ! soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex), & ! volumetric residual water content (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex), & ! van Genuchten "alpha" parameter (m-1) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) & ! van Genuchten "n" parameter (-) ) ! end associate statement ! diagnostic variables @@ -626,9 +626,9 @@ subroutine T2enthTemp(& if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature ! make associate to the the lookup table lookVars: associate(& - Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup , & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) - L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup, & ! temperature (K) + Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup, & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) + L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function ) ! end associate statement ! get the lower limit of the integral diff --git a/utils/bal_per_GRU.py b/utils/bal_per_GRU.py index ec8643088..9ce495f0f 100644 --- a/utils/bal_per_GRU.py +++ b/utils/bal_per_GRU.py @@ -34,7 +34,6 @@ method_name=['be1','be1en','be1lu'] #maybe make this an argument # Simulation statistics file locations -viz_fil = method_name.copy() viz_fl2 = method_name.copy() for i, m in enumerate(method_name): viz_fl2[i] = m + '_hrly_diff_bals_{}.nc' @@ -61,7 +60,6 @@ fig_fil2 =fig_fil2.format(stat) summa = {} -wall = {} for i, m in enumerate(method_name): # Get the aggregated statistics of SUMMA simulations summa[m] = xr.open_dataset(viz_dir/viz_fl2[i]) From 8eb6718bab227d550572211b99bc2d3abc5764f7 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 13 Mar 2024 00:00:28 +0900 Subject: [PATCH 1203/1472] name changes for clarity --- build/source/driver/summa_setup.f90 | 4 +- build/source/engine/coupled_em.f90 | 4 +- build/source/engine/enthalpyTemp.f90 | 75 +++++++++-------- build/source/engine/eval8summaWithPrime.f90 | 89 +++++++++++---------- build/source/engine/layerMerge.f90 | 12 +-- build/source/engine/systemSolv.f90 | 4 +- build/source/engine/varSubstep.f90 | 4 +- 7 files changed, 99 insertions(+), 93 deletions(-) diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index 9503b5bee..fc40cfac3 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -80,9 +80,9 @@ subroutine summa_paramSetup(summa1_struc, err, message) USE paramCheck_module,only:paramCheck ! module to check consistency of model parameters USE pOverwrite_module,only:pOverwrite ! module to overwrite default parameter values with info from the Noah tables USE read_param_module,only:read_param ! module to read model parameter sets - USE enthalpyTemp_module,only:T2E_lookup_snow ! module to calculate a look-up table for the snow temperature-enthalpy conversion + USE enthalpyTemp_module,only:T2H_lookup_snow ! module to calculate a look-up table for the snow temperature-enthalpy conversion USE enthalpyTemp_module,only:T2L_lookup_soil ! module to calculate a look-up table for the soil temperature-enthalpy conversion - USE enthalpyTemp_module,only:T2E_lookup_veg ! module to calculate a look-up table for the vegetation temperature-enthalpy conversion + USE enthalpyTemp_module,only:T2H_lookup_veg ! module to calculate a look-up table for the vegetation temperature-enthalpy conversion USE var_derive_module,only:fracFuture ! module to calculate the fraction of runoff in future time steps (time delay histogram) USE module_sf_noahmplsm,only:read_mp_veg_parameters ! module to read NOAH vegetation tables ! global data structures diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 49461b40f..34f8ae3a3 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -144,7 +144,7 @@ subroutine coupled_em(& USE tempAdjust_module,only:tempAdjust ! adjust snow temperature associated with new snowfall USE var_derive_module,only:calcHeight ! module to calculate height at layer interfaces and layer mid-point USE computSnowDepth_module,only:computSnowDepth ! compute snow depth - USE enthalpyTemp_module,only:enthTemp2H ! add phase change terms to delta temperature component of enthalpy + USE enthalpyTemp_module,only:enthTemp2enthalpy ! add phase change terms to delta temperature component of enthalpy implicit none @@ -1474,7 +1474,7 @@ subroutine coupled_em(& mLayerEnthalpy = mLayerEnthTemp ! compute enthalpy for current values - call enthTemp2H(& + call enthTemp2enthalpy(& ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): model indices diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index f74e8f8d5..ee13c0e1c 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -64,10 +64,10 @@ module enthalpyTemp_module public::T2H_lookup_snow public::T2L_lookup_soil public::T2H_lookup_veg -public::H2T_snow -public::T2H_snow -public::T2enthTemp -public::enthTemp2H +public::enthalpy2T_snow +public::T2enthalpy_snow +public::ethalpy2T +public::enthTemp2enthalpy private::hyp_2F1_real ! define the snow look-up table used to compute temperature based on enthalpy @@ -116,7 +116,7 @@ subroutine T2H_lookup_snow(mpar_data, & ! intent(in): pa ! ***** compute specific enthalpy (NOTE: J m-3 --> J kg-1) ***** do ilook=1,nlook - Hy(ilook) = T2H_snow(Tk(ilook),waterWght,snowfrz_scale)/waterWght ! (J m-3 --> J kg-1) + Hy(ilook) = T2enthalpy_snow(Tk(ilook),waterWght,snowfrz_scale)/waterWght ! (J m-3 --> J kg-1) end do ! define the final enthalpy vector @@ -283,10 +283,10 @@ end subroutine T2L_lookup_soil ! ************************************************************************************************************************ -! public subroutine H2T_snow: compute temperature based on specific temperature component of enthalpy +! public subroutine enthalpy2T_snow: compute temperature based on specific temperature component of enthalpy ! appropriate when no dry mass, as in snow ! ************************************************************************************************************************ -subroutine H2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) +subroutine enthalpy2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) ! ------------------------------------------------------------------------------------------------------------------------- implicit none ! ------------------------------------------------------------------------------------------------------------------------- @@ -312,7 +312,7 @@ subroutine H2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) real(rkind) :: dT ! temperature increment ! ------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="H2T_snow/" + err=0; message="enthalpy2T_snow/" ! convert input of total enthalpy (J m-3) to total specific enthalpy (J kg-1) H_spec = Hy/BulkDenWater ! (NOTE: no soil) @@ -322,8 +322,8 @@ subroutine H2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) Tg0 = (H_spec - H_lookup(1))/Cp_ice + T_lookup(1) Tg1 = Tg0+dx ! compute enthalpy - Ht0 = T2H_snow(Tg0,1._rkind,fc_param) - Ht1 = T2H_snow(Tg1,1._rkind,fc_param) + Ht0 = T2enthalpy_snow(Tg0,1._rkind,fc_param) + Ht1 = T2enthalpy_snow(Tg1,1._rkind,fc_param) ! compute function evaluations f0 = Ht0 - H_spec f1 = Ht1 - H_spec @@ -362,7 +362,7 @@ subroutine H2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) ! comute new value of Tg Tg1 = Tg0+dT ! get new function evaluation - Ht1 = T2H_snow(Tg1,1._rkind,fc_param) + Ht1 = T2enthalpy_snow(Tg1,1._rkind,fc_param) f1 = Ht1 - H_spec ! compute derivative if dT dh = (f1 - f0)/dT @@ -379,22 +379,22 @@ subroutine H2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) ! and check for convergence if(iter==niter)then; err=20; message=trim(message)//"failedToConverge"; return; end if end do ! (iteration loop) -end subroutine H2T_snow +end subroutine enthalpy2T_snow ! ************************************************************************************************************************ -! public function T2H_snow: compute liquid and ice mixture enthalpy based on temperature and mass (J m-3) for a +! public function T2enthalpy_snow: compute liquid and ice mixture enthalpy based on temperature and mass (J m-3) for a ! layer only where the layer has no dry mass, as in snow ! NOTE: enthalpy is a relative value, defined as zero at Tfreeze where all water is liquid ! ************************************************************************************************************************ -function T2H_snow(Tk,BulkDenWater,fc_param) +function T2enthalpy_snow(Tk,BulkDenWater,fc_param) ! ------------------------------------------------------------------------------------------------------------------------- implicit none ! declare dummy variables real(rkind),intent(in) :: Tk ! layer temperature (K) real(rkind),intent(in) :: BulkDenWater ! bulk density of water (kg m-3) real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) - real(rkind) :: T2H_snow ! return value of the function, total specific enthalpy (J m-3) + real(rkind) :: T2enthalpy_snow ! return value of the function, total specific enthalpy (J m-3) ! declare local variables real(rkind) :: frac_liq ! fraction of liquid water real(rkind) :: enthTempWater ! temperature component of specific enthalpy for total water (liquid and ice) (J kg-1) @@ -413,8 +413,8 @@ function T2H_snow(Tk,BulkDenWater,fc_param) enthMass = -LH_fus*(1._rkind - frac_liq) ! finally, compute the total enthalpy (J m-3) - T2H_snow = BulkDenWater*(enthTempWater + enthMass) !+ BulkDenSoil*enthTempSoil -end function T2H_snow + T2enthalpy_snow = BulkDenWater*(enthTempWater + enthMass) !+ BulkDenSoil*enthTempSoil +end function T2enthalpy_snow ! ************************************************************************************************************************ @@ -702,9 +702,9 @@ end subroutine T2enthTemp ! ************************************************************************************************************************ -! public subroutine enthTemp2H: add energy associated with thaw/freeze to temperature component of enthalpy to get total enthalpy, H +! public subroutine enthTemp2enthalpy: add energy associated with thaw/freeze to temperature component of enthalpy to get total enthalpy, H ! ************************************************************************************************************************ -subroutine enthTemp2H(& +subroutine enthTemp2enthalpy(& ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): model indices @@ -760,7 +760,7 @@ subroutine enthTemp2H(& ! ----------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="enthTemp2H/" + err=0; message="enthTemp2enthalpy/" ! loop through model state variables do iState=1,size(ixMapSubset2Full) @@ -797,13 +797,13 @@ subroutine enthTemp2H(& end associate generalVars -end subroutine enthTemp2H +end subroutine enthTemp2enthalpy ! ************************************************************************************************************************ -! public subroutine enthTemp2T: compute temperature component of enthalpy from temperature and total water content +! public subroutine enthalpy2T: compute temperature from enthalpy and total water content ! ************************************************************************************************************************ -subroutine enthTemp2T(& +subroutine enthalpy2T(& use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -812,11 +812,11 @@ subroutine enthTemp2T(& lookup_data, & ! intent(in): lookup table data structure ! input: enthalpy state variables scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) - scalarCanopyEnthTempTrial, & ! intent(in): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthTempTrial, & ! intent(in): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) - scalarCanairEnthalpyPrime, & ! intent(in): derivative of temperature component of enthalpy of the vegetation canopy (W m-3) - scalarCanopyEnthTempPrime, & ! intent(in): derivative of temperature component of enthalpy of the vegetation canopy (W m-3) - mLayerEnthTempPrime, & ! intent(in): derivative of temperature component of enthalpy of each snow+soil layer (W m-3) + scalarCanopyEnthalpyTrial, & ! intent(in): trial value for enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(in): trial vector of enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpyPrime, & ! intent(in): derivative of enthalpy of the vegetation canopy (W m-3) + scalarCanopyEnthalpyPrime, & ! intent(in): derivative of enthalpy of the vegetation canopy (W m-3) + mLayerEnthalpyPrime, & ! intent(in): derivative of enthalpy of each snow+soil layer (W m-3) ! input: water state variables scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) @@ -850,11 +850,11 @@ subroutine enthTemp2T(& type(zLookup),intent(in) :: lookup_data ! lookup tables ! output: enthalpy state variables real(rkind),intent(in) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - real(rkind),intent(in) :: scalarCanopyEnthTemp ! temperature component of enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(in) :: mLayerEnthTemp(:) ! temperature component of enthalpy of each snow+soil layer (J m-3) - real(rkind),intent(in) :: scalarCanairEnthalpyPrime ! derivative of temperature component of enthalpy of the vegetation canopy (W m-3) - real(rkind),intent(in) :: scalarCanopyEnthTempPrime ! derivative of temperature component of enthalpy of the vegetation canopy (W m-3) - real(rkind),intent(in) :: mLayerEnthTempPrime(:) ! derivative of temperature component of enthalpy of each snow+soil layer (W m-3) + real(rkind),intent(in) :: scalarCanopyEnthTemp ! enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(in) :: mLayerEnthTemp(:) ! enthalpy of each snow+soil layer (J m-3) + real(rkind),intent(in) :: scalarCanairEnthalpyPrime ! derivative of enthalpy of the vegetation canopy (W m-3) + real(rkind),intent(in) :: scalarCanopyEnthalpyPrime ! derivative of enthalpy of the vegetation canopy (W m-3) + real(rkind),intent(in) :: mLayerEnthalpyPrime(:) ! derivative of enthalpy of each snow+soil layer (W m-3) ! input: water state variables real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) @@ -973,6 +973,10 @@ subroutine enthTemp2T(& enthIce = 0._rkind else integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + ! Taylor-Maclaurin series for arctan + integral = (1._rkind/snowfrz_scale) * (snowfrz_scale * diffT - (snowfrz_scale * diffT)**3_i4b/3._rkind + (snowfrz_scale * diffT)**5_i4b/5._rkind - (snowfrz_scale * diffT)**7_i4b/7._rkind + (snowfrz_scale * diffT)**9_i4b/9._rkind) + + enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth endif @@ -981,8 +985,9 @@ subroutine enthTemp2T(& scalarCanopyTempTrial = scalarCanopyEnthTemp * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial ) + Tfreeze scalarCanopyTempPrime = scalarCanopyEnthTempPrime * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial ) -diffT<0._rkind scalarCanopyEnthTemp * canopyDepth = (specificHeatVeg * maxMassVegetation + Cp_ice * scalarCanopyWatTrial )* diffT + (Cp_water - Cp_ice)* scalarCanopyWatTrial * integral - + ! diffT<0._rkind + scalarCanopyEnthalpy * canopyDepth = (specificHeatVeg * maxMassVegetation + Cp_ice * scalarCanopyWatTrial )* diffT + (Cp_water - Cp_ice)* scalarCanopyWatTrial * integral + + LH_fus * scalarCanopyIce end associate vegVars diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 93b5a2fbf..9d4d03bfe 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -120,6 +120,7 @@ subroutine eval8summaWithPrime(& USE computHeatCap_module, only:computStatMult ! recompute state multiplier USE computResidWithPrime_module,only:computResidWithPrime ! compute residuals given a state vector USE computThermConduct_module,only:computThermConduct ! recompute thermal conductivity and derivatives + USE enthalpyTemp_module,only:enthalpy2T ! compute temperature from enthalpy and water content implicit none ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- @@ -190,14 +191,14 @@ subroutine eval8summaWithPrime(& ! state variables real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) real(rkind) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3) - real(rkind) :: scalarCanopyEnthTempTrial ! trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) real(rkind) :: scalarCanopyLiqTrial ! trial value for liquid water storage in the canopy (kg m-2) real(rkind) :: scalarCanopyIceTrial ! trial value for ice storage in the canopy (kg m-2) real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) - real(rkind),dimension(nLayers) :: mLayerEnthTempTrial ! trial vector of temperature component of enthalpy of each snow and soil layer (J m-3) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric liquid water content (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric ice content (-) - real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector of enthalpy of each snow and soil layer (J m-3) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector of volumetric liquid water content (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector of volumetric ice content (-) + real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) real(rkind) :: scalarCanopyLiqPrime ! prime value for liquid water storage in the canopy (kg m-2 s-1) real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! prime vector of volumetric liquid water content (s-1) real(rkind) :: scalarAquiferStoragePrime ! prime value of storage of water in the aquifer (m s-1) @@ -310,12 +311,12 @@ subroutine eval8summaWithPrime(& ! Placeholder: if we decide to use splitting, we need to pass all the previous values of the state variables scalarCanairEnthalpyTrial = realMissing scalarCanairTempTrial = realMissing - scalarCanopyEnthTempTrial = realMissing + scalarCanopyEnthalpyTrial = realMissing scalarCanopyTempTrial = scalarCanopyTempPrev scalarCanopyWatTrial = realMissing scalarCanopyLiqTrial = realMissing scalarCanopyIceTrial = realMissing - mLayerEnthTempTrial = realMissing + mLayerEnthalpyTrial = realMissing mLayerTempTrial = mLayerTempPrev mLayerVolFracWatTrial = realMissing mLayerVolFracLiqTrial = realMissing @@ -334,12 +335,12 @@ subroutine eval8summaWithPrime(& ! output: variables for the vegetation canopy scalarCanairEnthalpyTrial,& ! intent(inout): trial value of enthalpy of the canopy air space (J m-3) scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) - scalarCanopyEnthTempTrial,& ! intent(inout): trial value of temperature component of enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpyTrial,& ! intent(inout): trial value of enthalpy of the vegetation canopy (J m-3) scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - mLayerEnthTempTrial, & ! intent(inout): trial vector of temperature component of enthalpy of each snow and soil layer (J m-3) + mLayerEnthalpyTrial, & ! intent(inout): trial vector of enthalpy of each snow and soil layer (J m-3) mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) @@ -354,12 +355,12 @@ subroutine eval8summaWithPrime(& ! Placeholder: if we decide to use splitting, we need to pass all the previous values of the state variables scalarCanairEnthalpyPrime = realMissing scalarCanairTempPrime = realMissing - scalarCanopyEnthTempPrime = realMissing + scalarCanopyEnthalpyPrime = realMissing scalarCanopyTempPrime = realMissing scalarCanopyWatPrime = realMissing scalarCanopyLiqPrime = realMissing scalarCanopyIcePrime = realMissing - mLayerEnthTempPrime = realMissing + mLayerEnthalpyPrime = realMissing mLayerTempPrime = realMissing mLayerVolFracWatPrime = realMissing mLayerVolFracLiqPrime = realMissing @@ -377,12 +378,12 @@ subroutine eval8summaWithPrime(& ! output: variables for the vegetation canopy scalarCanairEnthalpyPrime, & ! intent(inout): derivative of enthalpy of the canopy air space (W m-3) scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K s-1) - scalarCanopyEnthTempPrime, & ! intent(inout): derivative of temperature component of enthalpy of the vegetation canopy (W m-3) + scalarCanopyEnthalpyPrime, & ! intent(inout): derivative of enthalpy of the vegetation canopy (W m-3) scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K s-1) scalarCanopyWatPrime, & ! intent(inout): derivative of canopy total water (kg m-2 s-1) scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2 s-1) ! output: variables for the snow-soil domain - mLayerEnthTempPrime, & ! intent(inout): derivative of temperature component of enthalpy of each snow and soil layer (W m-3) + mLayerEnthalpyPrime, & ! intent(inout): derivative of enthalpy of each snow and soil layer (W m-3) mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K s-1) mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (s-1) mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (s-1) @@ -398,37 +399,37 @@ subroutine eval8summaWithPrime(& NEED TO UPDATE WATER STATE FIRST TO MAKE SURE HAVE ALL THESE WATER STATE VARIABLES, SEE updateVarsWithPrime ! compute temperature component of enthalpy - call enthTemp2T(& - ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: enthalpy state variables - scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) - scalarCanopyEnthTempTrial, & ! intent(in): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthTempTrial, & ! intent(in): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) - scalarCanairEnthalpyPrime, & ! intent(in): derivative of temperature component of enthalpy of the vegetation canopy (W m-3) - scalarCanopyEnthTempPrime, & ! intent(in): derivative of temperature component of enthalpy of the vegetation canopy (W m-3) - mLayerEnthTempPrime, & ! intent(in): derivative of temperature component of enthalpy of each snow+soil layer (W m-3) - ! input: water state variables - scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - scalarCanopyWatPrime, & ! intent(in): derivative of canopy total water (kg m-2 s-1) - mLayerVolFracWatPrime, & ! intent(in): derivative of volumetric total water content (s-1) - mLayerMatricHeadPrime, & ! intent(in): derivative of total water matric potential (m s-1) - ! output: temperature for the vegetation canopy - scalarCanairTempTrial, & ! intent(out): trial value for canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(out): trial value for canopy temperature (K) - scalarCanairTempPrime, & ! intent(out): derivative of canopy air temperature (K s-1) - scalarCanopyTempPrime, & ! intent(out): derivative of canopy temperature (K s-1) - ! output: temperature for the snow-soil domain - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - mLayerTempPrime, & ! intent(out): derivative of layer temperature (K s-1) - ! output: error control - err,cmessage) ! intent(out): error control + call enthalpy2T(& + ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: enthalpy state variables + scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(in): trial value for enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(in): trial vector of enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpyPrime, & ! intent(in): derivative of enthalpy of the vegetation canopy (W m-3) + scalarCanopyEnthalpyPrime, & ! intent(in): derivative of enthalpy of the vegetation canopy (W m-3) + mLayerEnthalpyPrime, & ! intent(in): derivative of enthalpy of each snow+soil layer (W m-3) + ! input: water state variables + scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + scalarCanopyWatPrime, & ! intent(in): derivative of canopy total water (kg m-2 s-1) + mLayerVolFracWatPrime, & ! intent(in): derivative of volumetric total water content (s-1) + mLayerMatricHeadPrime, & ! intent(in): derivative of total water matric potential (m s-1) + ! output: temperature for the vegetation canopy + scalarCanairTempTrial, & ! intent(out): trial value for canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(out): trial value for canopy temperature (K) + scalarCanairTempPrime, & ! intent(out): derivative of canopy air temperature (K s-1) + scalarCanopyTempPrime, & ! intent(out): derivative of canopy temperature (K s-1) + ! output: temperature for the snow-soil domain + mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) + mLayerTempPrime, & ! intent(out): derivative of layer temperature (K s-1) + ! output: error control + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif endif !(choice of how conservation of energy is implemented) diff --git a/build/source/engine/layerMerge.f90 b/build/source/engine/layerMerge.f90 index f14a2c572..0741970c0 100644 --- a/build/source/engine/layerMerge.f90 +++ b/build/source/engine/layerMerge.f90 @@ -297,7 +297,7 @@ subroutine layer_combine(mpar_data,prog_data,diag_data,flux_data,indx_data,iSnow USE data_types,only:var_d ! data structures with fixed dimension ! provide access to external modules USE snow_utils_module,only:fracliquid ! compute fraction of liquid water - USE enthalpyTemp_module,only:H2T_snow,T2H_snow ! convert temperature to enthalpy for a snow layer + USE enthalpyTemp_module,only:ethalpy2T_snow,T2enthalpy_snow ! convert temperature to enthalpy for a snow layer implicit none ! ------------------------------------------------------------------------------------------------------------ ! input/output: data structures @@ -361,19 +361,19 @@ subroutine layer_combine(mpar_data,prog_data,diag_data,flux_data,indx_data,iSnow cBulkDenWat = (mLayerDepth(isnow)*bulkDenWat(1) + mLayerDepth(isnow+1)*bulkDenWat(2))/cDepth ! compute enthalpy for each layer (J m-3) - l1Enthalpy = T2H_snow(mLayerTemp(iSnow), BulkDenWat(1),snowfrz_scale) - l2Enthalpy = T2H_snow(mLayerTemp(iSnow+1),BulkDenWat(2),snowfrz_scale) + l1Enthalpy = T2enthalpy_snow(mLayerTemp(iSnow), BulkDenWat(1),snowfrz_scale) + l2Enthalpy = T2enthalpy_snow(mLayerTemp(iSnow+1),BulkDenWat(2),snowfrz_scale) ! compute combined enthalpy (J m-3) cEnthalpy = (mLayerDepth(isnow)*l1Enthalpy + mLayerDepth(isnow+1)*l2Enthalpy)/cDepth ! convert enthalpy (J m-3) to temperature (K) - call H2T_snow(cEnthalpy,cBulkDenWat,snowfrz_scale,cTemp,err,cmessage) + call ethalpy2T_snow(cEnthalpy,cBulkDenWat,snowfrz_scale,cTemp,err,cmessage) if(err/=0)then; err=10; message=trim(message)//trim(cmessage); return; end if ! test enthalpy conversion - if(abs(T2H_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat - cEnthalpy/cBulkDenWat) > eTol)then - write(*,'(a,1x,f12.5,1x,2(e20.10,1x))') 'enthalpy test', cBulkDenWat, T2H_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat, cEnthalpy/cBulkDenWat + if(abs(T2enthalpy_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat - cEnthalpy/cBulkDenWat) > eTol)then + write(*,'(a,1x,f12.5,1x,2(e20.10,1x))') 'enthalpy test', cBulkDenWat, T2enthalpy_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat, cEnthalpy/cBulkDenWat message=trim(message)//'problem with enthalpy-->temperature conversion' err=20; return end if diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 25d8bc14f..2be2dbde4 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -160,7 +160,7 @@ subroutine systemSolv(& USE allocspace_module,only:allocLocal ! allocate local data structures ! state vector and solver USE getVectorz_module,only:getScaling ! get the scaling vectors - USE enthalpyTemp_module,only:T2H_snow ! convert temperature to enthalpy for a snow layer + USE enthalpyTemp_module,only:T2enthalpy_snow ! convert temperature to enthalpy for a snow layer USE enthalpyTemp_module,only:T2enthTemp ! compute enthalpy #ifdef SUNDIALS_ACTIVE USE tol4ida_module,only:popTol4ida ! populate tolerances @@ -477,7 +477,7 @@ subroutine systemSolv(& if(nSnow>0)then ! compute the energy required to melt the top snow layer (J m-2) bulkDensity = mLayerVolFracIce(1)*iden_ice + mLayerVolFracLiq(1)*iden_water - volEnthalpy = T2H_snow(mLayerTemp(1),bulkDensity,snowfrz_scale) + volEnthalpy = T2enthalpy_snow(mLayerTemp(1),bulkDensity,snowfrz_scale) ! set flag and error codes for too much melt if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_cur)then tooMuchMelt = .true. diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index f077a5d9f..167c13c01 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -632,7 +632,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec #endif USE updateVars_module,only:updateVars ! update prognostic variables USE enthalpyTemp_module,only:T2enthTemp ! compute enthalpy - USE enthalpyTemp_module,only:enthTemp2H ! add phase change terms to delta temperature component of enthalpy + USE enthalpyTemp_module,only:enthTemp2enthalpy ! add phase change terms to delta temperature component of enthalpy implicit none ! model control real(rkind) ,intent(in) :: dt ! time step (s) @@ -1000,7 +1000,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec mLayerHDelta = mLayerEnthTempTrial - mLayerEnthTemp(1:nLayers) ! compute mixture enthalpy for current values, do on delta value so only have to do once - call enthTemp2H(& + call enthTemp2enthalpy(& ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): model indices From 02db92db27335b8fd483183be07f61888b0230fb Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 13 Mar 2024 00:07:33 +0900 Subject: [PATCH 1204/1472] function name changes for clarity, no code changes --- build/source/engine/coupled_em.f90 | 4 +-- build/source/engine/enthalpyTemp.f90 | 46 ++++++++++++++-------------- build/source/engine/layerMerge.f90 | 12 ++++---- build/source/engine/systemSolv.f90 | 4 +-- build/source/engine/varSubstep.f90 | 4 +-- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 49461b40f..34f8ae3a3 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -144,7 +144,7 @@ subroutine coupled_em(& USE tempAdjust_module,only:tempAdjust ! adjust snow temperature associated with new snowfall USE var_derive_module,only:calcHeight ! module to calculate height at layer interfaces and layer mid-point USE computSnowDepth_module,only:computSnowDepth ! compute snow depth - USE enthalpyTemp_module,only:enthTemp2H ! add phase change terms to delta temperature component of enthalpy + USE enthalpyTemp_module,only:enthTemp2enthalpy ! add phase change terms to delta temperature component of enthalpy implicit none @@ -1474,7 +1474,7 @@ subroutine coupled_em(& mLayerEnthalpy = mLayerEnthTemp ! compute enthalpy for current values - call enthTemp2H(& + call enthTemp2enthalpy(& ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): model indices diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 4b21707ec..7b7cbb934 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -63,10 +63,10 @@ module enthalpyTemp_module implicit none public::T2H_lookup_snow public::T2L_lookup_soil -public::H2T_snow -public::T2H_snow +public::enthalpy2T_snow +public::T2enthalpy_snow public::T2enthTemp -public::enthTemp2H +public::enthTemp2enthalpy private::hyp_2F1_real ! define the snow look-up table used to compute temperature based on enthalpy @@ -115,7 +115,7 @@ subroutine T2H_lookup_snow(mpar_data, & ! intent(in): pa ! ***** compute specific enthalpy (NOTE: J m-3 --> J kg-1) ***** do ilook=1,nlook - Hy(ilook) = T2H_snow(Tk(ilook),waterWght,snowfrz_scale)/waterWght ! (J m-3 --> J kg-1) + Hy(ilook) = T2enthalpy_snow(Tk(ilook),waterWght,snowfrz_scale)/waterWght ! (J m-3 --> J kg-1) end do ! define the final enthalpy vector @@ -282,10 +282,10 @@ end subroutine T2L_lookup_soil ! ************************************************************************************************************************ -! public subroutine H2T_snow: compute temperature based on specific temperature component of enthalpy -! appropriate when no dry mass, as in snow +! public subroutine enthalpy2T_snow: compute temperature based on specific temperature component of enthalpy +! appropriate when no dry mass, as in snow ! ************************************************************************************************************************ -subroutine H2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) +subroutine enthalpy2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) ! ------------------------------------------------------------------------------------------------------------------------- implicit none ! ------------------------------------------------------------------------------------------------------------------------- @@ -311,7 +311,7 @@ subroutine H2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) real(rkind) :: dT ! temperature increment ! ------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="H2T_snow/" + err=0; message="enthalpy2T_snow/" ! convert input of total enthalpy (J m-3) to total specific enthalpy (J kg-1) H_spec = Hy/BulkDenWater ! (NOTE: no soil) @@ -321,8 +321,8 @@ subroutine H2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) Tg0 = (H_spec - H_lookup(1))/Cp_ice + T_lookup(1) Tg1 = Tg0+dx ! compute enthalpy - Ht0 = T2H_snow(Tg0,1._rkind,fc_param) - Ht1 = T2H_snow(Tg1,1._rkind,fc_param) + Ht0 = T2enthalpy_snow(Tg0,1._rkind,fc_param) + Ht1 = T2enthalpy_snow(Tg1,1._rkind,fc_param) ! compute function evaluations f0 = Ht0 - H_spec f1 = Ht1 - H_spec @@ -361,7 +361,7 @@ subroutine H2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) ! comute new value of Tg Tg1 = Tg0+dT ! get new function evaluation - Ht1 = T2H_snow(Tg1,1._rkind,fc_param) + Ht1 = T2enthalpy_snow(Tg1,1._rkind,fc_param) f1 = Ht1 - H_spec ! compute derivative if dT dh = (f1 - f0)/dT @@ -378,22 +378,22 @@ subroutine H2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) ! and check for convergence if(iter==niter)then; err=20; message=trim(message)//"failedToConverge"; return; end if end do ! (iteration loop) -end subroutine H2T_snow +end subroutine enthalpy2T_snow ! ************************************************************************************************************************ -! public function T2H_snow: compute liquid and ice mixture enthalpy based on temperature and mass (J m-3) for a -! layer only where the layer has no dry mass, as in snow -! NOTE: enthalpy is a relative value, defined as zero at Tfreeze where all water is liquid +! public function T2enthalpy_snow: compute liquid and ice mixture enthalpy based on temperature and mass (J m-3) for a +! layer only where the layer has no dry mass, as in snow +! NOTE: enthalpy is a relative value, defined as zero at Tfreeze where all water is liquid ! ************************************************************************************************************************ -function T2H_snow(Tk,BulkDenWater,fc_param) +function T2enthalpy_snow(Tk,BulkDenWater,fc_param) ! ------------------------------------------------------------------------------------------------------------------------- implicit none ! declare dummy variables real(rkind),intent(in) :: Tk ! layer temperature (K) real(rkind),intent(in) :: BulkDenWater ! bulk density of water (kg m-3) real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) - real(rkind) :: T2H_snow ! return value of the function, total specific enthalpy (J m-3) + real(rkind) :: T2enthalpy_snow ! return value of the function, total specific enthalpy (J m-3) ! declare local variables real(rkind) :: frac_liq ! fraction of liquid water real(rkind) :: enthTempWater ! temperature component of specific enthalpy for total water (liquid and ice) (J kg-1) @@ -412,8 +412,8 @@ function T2H_snow(Tk,BulkDenWater,fc_param) enthMass = -LH_fus*(1._rkind - frac_liq) ! finally, compute the total enthalpy (J m-3) - T2H_snow = BulkDenWater*(enthTempWater + enthMass) !+ BulkDenSoil*enthTempSoil -end function T2H_snow + T2enthalpy_snow = BulkDenWater*(enthTempWater + enthMass) !+ BulkDenSoil*enthTempSoil +end function T2enthalpy_snow ! ************************************************************************************************************************ @@ -701,9 +701,9 @@ end subroutine T2enthTemp ! ************************************************************************************************************************ -! public subroutine enthTemp2H: add energy associated with thaw/freeze to temperature component of enthalpy to get total enthalpy, H +! public subroutine enthTemp2enthalpy: add energy associated with thaw/freeze to temperature component of enthalpy to get total enthalpy, H ! ************************************************************************************************************************ -subroutine enthTemp2H(& +subroutine enthTemp2enthalpy(& ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): model indices @@ -759,7 +759,7 @@ subroutine enthTemp2H(& ! ----------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="enthTemp2H/" + err=0; message="enthTemp2enthalpy/" ! loop through model state variables do iState=1,size(ixMapSubset2Full) @@ -796,7 +796,7 @@ subroutine enthTemp2H(& end associate generalVars -end subroutine enthTemp2H +end subroutine enthTemp2enthalpy !---------------------------------------------------------------------- ! private function: compute hypergeometric function with real arguments into real result diff --git a/build/source/engine/layerMerge.f90 b/build/source/engine/layerMerge.f90 index f14a2c572..311445281 100644 --- a/build/source/engine/layerMerge.f90 +++ b/build/source/engine/layerMerge.f90 @@ -297,7 +297,7 @@ subroutine layer_combine(mpar_data,prog_data,diag_data,flux_data,indx_data,iSnow USE data_types,only:var_d ! data structures with fixed dimension ! provide access to external modules USE snow_utils_module,only:fracliquid ! compute fraction of liquid water - USE enthalpyTemp_module,only:H2T_snow,T2H_snow ! convert temperature to enthalpy for a snow layer + USE enthalpyTemp_module,only:enthalpy2T_snow,T2enthalpy_snow ! convert temperature to enthalpy for a snow layer implicit none ! ------------------------------------------------------------------------------------------------------------ ! input/output: data structures @@ -361,19 +361,19 @@ subroutine layer_combine(mpar_data,prog_data,diag_data,flux_data,indx_data,iSnow cBulkDenWat = (mLayerDepth(isnow)*bulkDenWat(1) + mLayerDepth(isnow+1)*bulkDenWat(2))/cDepth ! compute enthalpy for each layer (J m-3) - l1Enthalpy = T2H_snow(mLayerTemp(iSnow), BulkDenWat(1),snowfrz_scale) - l2Enthalpy = T2H_snow(mLayerTemp(iSnow+1),BulkDenWat(2),snowfrz_scale) + l1Enthalpy = T2enthalpy_snow(mLayerTemp(iSnow), BulkDenWat(1),snowfrz_scale) + l2Enthalpy = T2enthalpy_snow(mLayerTemp(iSnow+1),BulkDenWat(2),snowfrz_scale) ! compute combined enthalpy (J m-3) cEnthalpy = (mLayerDepth(isnow)*l1Enthalpy + mLayerDepth(isnow+1)*l2Enthalpy)/cDepth ! convert enthalpy (J m-3) to temperature (K) - call H2T_snow(cEnthalpy,cBulkDenWat,snowfrz_scale,cTemp,err,cmessage) + call enthalpy2T_snow(cEnthalpy,cBulkDenWat,snowfrz_scale,cTemp,err,cmessage) if(err/=0)then; err=10; message=trim(message)//trim(cmessage); return; end if ! test enthalpy conversion - if(abs(T2H_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat - cEnthalpy/cBulkDenWat) > eTol)then - write(*,'(a,1x,f12.5,1x,2(e20.10,1x))') 'enthalpy test', cBulkDenWat, T2H_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat, cEnthalpy/cBulkDenWat + if(abs(T2enthalpy_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat - cEnthalpy/cBulkDenWat) > eTol)then + write(*,'(a,1x,f12.5,1x,2(e20.10,1x))') 'enthalpy test', cBulkDenWat, T2enthalpy_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat, cEnthalpy/cBulkDenWat message=trim(message)//'problem with enthalpy-->temperature conversion' err=20; return end if diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 25d8bc14f..2be2dbde4 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -160,7 +160,7 @@ subroutine systemSolv(& USE allocspace_module,only:allocLocal ! allocate local data structures ! state vector and solver USE getVectorz_module,only:getScaling ! get the scaling vectors - USE enthalpyTemp_module,only:T2H_snow ! convert temperature to enthalpy for a snow layer + USE enthalpyTemp_module,only:T2enthalpy_snow ! convert temperature to enthalpy for a snow layer USE enthalpyTemp_module,only:T2enthTemp ! compute enthalpy #ifdef SUNDIALS_ACTIVE USE tol4ida_module,only:popTol4ida ! populate tolerances @@ -477,7 +477,7 @@ subroutine systemSolv(& if(nSnow>0)then ! compute the energy required to melt the top snow layer (J m-2) bulkDensity = mLayerVolFracIce(1)*iden_ice + mLayerVolFracLiq(1)*iden_water - volEnthalpy = T2H_snow(mLayerTemp(1),bulkDensity,snowfrz_scale) + volEnthalpy = T2enthalpy_snow(mLayerTemp(1),bulkDensity,snowfrz_scale) ! set flag and error codes for too much melt if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_cur)then tooMuchMelt = .true. diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index f077a5d9f..167c13c01 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -632,7 +632,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec #endif USE updateVars_module,only:updateVars ! update prognostic variables USE enthalpyTemp_module,only:T2enthTemp ! compute enthalpy - USE enthalpyTemp_module,only:enthTemp2H ! add phase change terms to delta temperature component of enthalpy + USE enthalpyTemp_module,only:enthTemp2enthalpy ! add phase change terms to delta temperature component of enthalpy implicit none ! model control real(rkind) ,intent(in) :: dt ! time step (s) @@ -1000,7 +1000,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec mLayerHDelta = mLayerEnthTempTrial - mLayerEnthTemp(1:nLayers) ! compute mixture enthalpy for current values, do on delta value so only have to do once - call enthTemp2H(& + call enthTemp2enthalpy(& ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): model indices From 6b0e069cb19bca308485a3902d95dc2e3a932233 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 13 Mar 2024 00:22:43 +0900 Subject: [PATCH 1205/1472] solve on enthalpy not enthTemp --- build/source/engine/enthalpyTemp.f90 | 15 ++++++++------- build/source/engine/eval8summaWithPrime.f90 | 16 ++++++++-------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index ee13c0e1c..d2cf29525 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -78,7 +78,7 @@ module enthalpyTemp_module ! ************************************************************************************************************************ -! public subroutine T2H_lookup_snow:: define a look-up table to mixture enthalpy based on temperature +! public subroutine T2H_lookup_snow: define a look-up table to mixture enthalpy based on temperature ! appropriate when no dry mass, as in snow ! ************************************************************************************************************************ subroutine T2H_lookup_snow(mpar_data, & ! intent(in): parameter data structure @@ -284,7 +284,7 @@ end subroutine T2L_lookup_soil ! ************************************************************************************************************************ ! public subroutine enthalpy2T_snow: compute temperature based on specific temperature component of enthalpy -! appropriate when no dry mass, as in snow +! appropriate when no dry mass, as in snow ! ************************************************************************************************************************ subroutine enthalpy2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) ! ------------------------------------------------------------------------------------------------------------------------- @@ -384,8 +384,8 @@ end subroutine enthalpy2T_snow ! ************************************************************************************************************************ ! public function T2enthalpy_snow: compute liquid and ice mixture enthalpy based on temperature and mass (J m-3) for a -! layer only where the layer has no dry mass, as in snow -! NOTE: enthalpy is a relative value, defined as zero at Tfreeze where all water is liquid +! layer only where the layer has no dry mass, as in snow +! NOTE: enthalpy is a relative value, defined as zero at Tfreeze where all water is liquid ! ************************************************************************************************************************ function T2enthalpy_snow(Tk,BulkDenWater,fc_param) ! ------------------------------------------------------------------------------------------------------------------------- @@ -976,18 +976,19 @@ subroutine enthalpy2T(& ! Taylor-Maclaurin series for arctan integral = (1._rkind/snowfrz_scale) * (snowfrz_scale * diffT - (snowfrz_scale * diffT)**3_i4b/3._rkind + (snowfrz_scale * diffT)**5_i4b/5._rkind - (snowfrz_scale * diffT)**7_i4b/7._rkind + (snowfrz_scale * diffT)**9_i4b/9._rkind) + fLiq = 1._rkind / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b ) enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth endif ! diffT>=0._rkind - scalarCanopyTempTrial = scalarCanopyEnthTemp * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial ) + Tfreeze - scalarCanopyTempPrime = scalarCanopyEnthTempPrime * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial ) + scalarCanopyTempTrial = scalarCanopyEnthalpy * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial ) + Tfreeze + scalarCanopyTempPrime = scalarCanopyEnthalpyPrime * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial ) ! diffT<0._rkind scalarCanopyEnthalpy * canopyDepth = (specificHeatVeg * maxMassVegetation + Cp_ice * scalarCanopyWatTrial )* diffT + (Cp_water - Cp_ice)* scalarCanopyWatTrial * integral - + LH_fus * scalarCanopyIce + + LH_fus * (1._rkind - fLiq) * scalarCanopyWatTrial end associate vegVars diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 9d4d03bfe..3ca88b7de 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -90,11 +90,11 @@ subroutine eval8summaWithPrime(& mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) ! output: new prime values of variables needed in data window outside of internal IDA for Jacobian - scalarCanairEnthalpyPrime, & ! intent(out): prime value for enthalpy of the canopy air space (J m-3) - scalarCanopyEnthTempPrime, & ! intent(out): prime value for temperature component of enthalpy of the vegetation canopy (W m-3) + scalarCanairEnthalpyPrime, & ! intent(out): prime value for enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyPrime, & ! intent(out): prime value for enthalpy of the vegetation canopy (W m-3) scalarCanopyWatPrime, & ! intent(out): prime value for total water content of the vegetation canopy (kg m-2 s-1) scalarCanopyIcePrime, & ! intent(out): prime value for mass of ice on the vegetation canopy (kg m-2 s-1) - mLayerEnthTempPrime, & ! intent(out): prime vector of temperature component of enthalpy of each snow and soil layer (W m-3) + mLayerEnthalpyPrime, & ! intent(out): prime vector of enthalpy of each snow and soil layer (W m-3) mLayerMatricHeadPrime, & ! intent(out): prime vector of matric head of each snow and soil layer (m s-1) mLayerMatricHeadLiqPrime, & ! intent(out): prime vector of liquid water matric potential (m s-1) mLayerVolFracWatPrime, & ! intent(out): prime vector of volumetric total water content of each snow and soil layer (s-1) @@ -164,11 +164,11 @@ subroutine eval8summaWithPrime(& real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial vector for total water matric potential (m) real(rkind),intent(out) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) ! output: new prime values of variables needed in data window outside of internal IDA - real(rkind),intent(out) :: scalarCanairEnthTempPrime ! prime value for temperature component of enthalpy of the canopy air space (W m-3) - real(rkind),intent(out) :: scalarCanopyEnthTempPrime ! prime value for temperature component of enthalpy of the vegetation canopy (W m-3) + real(rkind),intent(out) :: scalarCanairEnthalpyPrime ! prime value for enthalpy of the canopy air space (W m-3) + real(rkind),intent(out) :: scalarCanopyEnthalpyPrime ! prime value for enthalpy of the vegetation canopy (W m-3) real(rkind),intent(out) :: scalarCanopyWatPrime ! prime value for total water content of the vegetation canopy (kg m-2 s-1) real(rkind),intent(out) :: scalarCanopyIcePrime ! prime value for mass of ice on the vegetation canopy (kg m-2 s-1) - real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! prime vector of temperature component of enthalpy of each snow and soil layer (W m-3) + real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! prime vector of enthalpy of each snow and soil layer (W m-3) real(rkind),intent(out) :: mLayerMatricHeadPrime(:) ! prime vector of matric head of each snow and soil layer (m s-1) real(rkind),intent(out) :: mLayerMatricHeadLiqPrime(:) ! prime vector of liquid water matric potential (m s-1) real(rkind),intent(out) :: mLayerVolFracWatPrime(:) ! prime vector of volumetric total water content of each snow and soil layer (s-1) @@ -686,8 +686,8 @@ subroutine eval8summaWithPrime(& canopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) scalarCanairEnthalpyPrime, & ! intent(in): prime value for the enthalpy of the canopy air space (W m-3) - scalarCanopyEnthTempPrime, & ! intent(in): prime value for the temperature component of enthalpy of the vegetation canopy (W m-3) - mLayerEnthTempPrime, & ! intent(in): prime vector of the temperature component of enthalpy of each snow and soil layer (W m-3) + scalarCanopyEnthalpyPrime, & ! intent(in): prime value for the of enthalpy of the vegetation canopy (W m-3) + mLayerEnthalpyPrime, & ! intent(in): prime vector of the of enthalpy of each snow and soil layer (W m-3) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU From 4477c8e536ac7d72378bea9cd356763fc9fc607f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 13 Mar 2024 14:47:41 +0900 Subject: [PATCH 1206/1472] utils --- utils/plot_per_GRUMultBal.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index 5491f2202..633008731 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -61,10 +61,10 @@ plot_vars = settings.copy() if stat == 'mean': - maxes = [1e-4,1e-2,1e-3,1e-2]+[1e-12,1e-9,1e-10,1e-11] + [30] + maxes = [1e-4,1e-2,1e-3,1e-2]+[1e-12,1e-9,1e-10,1e-11] + [3e-3] if do_rel: maxes = [1e-6,1e-5,1e-8,1e-8]+[1e-10,1e-11,1e-13,1e-11] + [3e-3] if stat == 'amax': - maxes = [1e-3,1e3,1e3,1e2]+[1e-11,1e-6,1e-7,1e-8] + [1e4] + maxes = [1e-3,1e3,1e3,1e2]+[1e-11,1e-6,1e-7,1e-8] + [1e0] if do_rel: maxes = [1e-2,1e0,1e-4,1e-2]+[1e-7,1e-8,1e-10,1e-6] + [1e0] # Get the albers shapes @@ -209,9 +209,9 @@ def run_loop(j,var,the_max): my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap my_cmap.set_bad(color='white') #nan color white vmin,vmax = the_max*1e-4, the_max - #if stat =='mean' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 700, the_max - #if stat =='amax' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 1000, the_max - #if var!='wallClockTime' and do_rel: vmin,vmax = 0.9, the_max + if any(substring in var for substring in ['VegNrg', 'SnowNrg', 'SoilNrg']): + vmin, vmax = the_max * 1e-6, the_max + if var in ['wallClockTime',]: vmin,vmax = the_max*1e-1, the_max norm = matplotlib.colors.LogNorm(vmin=vmin, vmax=vmax) From 28e1964844021041c6f6f340fb91aea8c859f64b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 13 Mar 2024 14:47:41 +0900 Subject: [PATCH 1207/1472] utils --- utils/plot_per_GRUMultBal.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index 5491f2202..633008731 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -61,10 +61,10 @@ plot_vars = settings.copy() if stat == 'mean': - maxes = [1e-4,1e-2,1e-3,1e-2]+[1e-12,1e-9,1e-10,1e-11] + [30] + maxes = [1e-4,1e-2,1e-3,1e-2]+[1e-12,1e-9,1e-10,1e-11] + [3e-3] if do_rel: maxes = [1e-6,1e-5,1e-8,1e-8]+[1e-10,1e-11,1e-13,1e-11] + [3e-3] if stat == 'amax': - maxes = [1e-3,1e3,1e3,1e2]+[1e-11,1e-6,1e-7,1e-8] + [1e4] + maxes = [1e-3,1e3,1e3,1e2]+[1e-11,1e-6,1e-7,1e-8] + [1e0] if do_rel: maxes = [1e-2,1e0,1e-4,1e-2]+[1e-7,1e-8,1e-10,1e-6] + [1e0] # Get the albers shapes @@ -209,9 +209,9 @@ def run_loop(j,var,the_max): my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap my_cmap.set_bad(color='white') #nan color white vmin,vmax = the_max*1e-4, the_max - #if stat =='mean' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 700, the_max - #if stat =='amax' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 1000, the_max - #if var!='wallClockTime' and do_rel: vmin,vmax = 0.9, the_max + if any(substring in var for substring in ['VegNrg', 'SnowNrg', 'SoilNrg']): + vmin, vmax = the_max * 1e-6, the_max + if var in ['wallClockTime',]: vmin,vmax = the_max*1e-1, the_max norm = matplotlib.colors.LogNorm(vmin=vmin, vmax=vmax) From 0de68d1761ea048deed1ffd4ba2d9bc7089c90a3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 13 Mar 2024 17:10:59 +0900 Subject: [PATCH 1208/1472] derivatives --- build/source/engine/enthalpyTemp.f90 | 32 +++++++++++++++------ build/source/engine/eval8summaWithPrime.f90 | 12 ++++++-- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index d2cf29525..a5c328151 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -394,7 +394,7 @@ function T2enthalpy_snow(Tk,BulkDenWater,fc_param) real(rkind),intent(in) :: Tk ! layer temperature (K) real(rkind),intent(in) :: BulkDenWater ! bulk density of water (kg m-3) real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) - real(rkind) :: T2enthalpy_snow ! return value of the function, total specific enthalpy (J m-3) + real(rkind) :: T2enthalpy_snow ! return value of the function, total specific enthalpy (J m-3) ! declare local variables real(rkind) :: frac_liq ! fraction of liquid water real(rkind) :: enthTempWater ! temperature component of specific enthalpy for total water (liquid and ice) (J kg-1) @@ -824,14 +824,20 @@ subroutine enthalpy2T(& scalarCanopyWatPrime, & ! intent(in): derivative of canopy total water (kg m-2 s-1) mLayerVolFracWatPrime, & ! intent(in): derivative of volumetric total water content (s-1) mLayerMatricHeadPrime, & ! intent(in): derivative of total water matric potential (m s-1) - ! output: temperature for the vegetation canopy + ! output: temperature scalarCanairTempTrial, & ! intent(out): trial value for canopy air temperature (K) scalarCanopyTempTrial, & ! intent(out): trial value for canopy temperature (K) + mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) scalarCanairTempPrime, & ! intent(out): derivative of canopy air temperature (K s-1) scalarCanopyTempPrime, & ! intent(out): derivative of canopy temperature (K s-1) - ! output: temperature for the snow-soil domain - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) mLayerTempPrime, & ! intent(out): derivative of layer temperature (K s-1) + ! output: derivatives + dCanairTemp_dEnthalpy, & ! derivative of canopy air temperature with enthalpy + dCanopyTemp_dEnthalpy, & ! derivative of canopy temperature with enthalpy + dTemp_dEnthalpy, & ! derivative of layer temperature with enthalpy + dTkCanairPrime_dEnthPrime, & ! derivative of canopy air temperature prime with enthalpy prime + dTkCanopyPrime_dEnthPrime, & ! derivative of canopy temperature prime with enthalpy prime + dTkPrime_dEnthPrime, & ! derivative of layer temperature prime with enthalpy prime ! output: error control err,cmessage) ! intent(out): error control ! ------------------------------------------------------------------------------------------------------------------------- @@ -856,20 +862,26 @@ subroutine enthalpy2T(& real(rkind),intent(in) :: scalarCanopyEnthalpyPrime ! derivative of enthalpy of the vegetation canopy (W m-3) real(rkind),intent(in) :: mLayerEnthalpyPrime(:) ! derivative of enthalpy of each snow+soil layer (W m-3) ! input: water state variables - real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value for canopy total water (kg m-2) real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) real(rkind),intent(in) :: scalarCanopyWatPrime ! derivative of canopy total water (kg m-2 s-1) real(rkind),intent(in) :: mLayerVolFracWatPrime(:) ! derivative of volumetric total water content (s-1) real(rkind),intent(in) :: mLayerMatricHeadPrime(:) ! derivative of total water matric potential (m s-1) - ! output: temperature for the vegetation canopy + ! output: temperature diagnostic variables real(rkind),intent(out) :: scalarCanairTempTrial ! trial value for canopy air temperature (K) real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value for canopy temperature (K) + real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) real(rkind),intent(out) :: scalarCanairTempPrime ! derivative of canopy air temperature (K s-1) real(rkind),intent(out) :: scalarCanopyTempPrime ! derivative of canopy temperature (K s-1) - ! output: temperature for the snow-soil domain - real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) real(rkind),intent(out) :: mLayerTempPrime(:) ! derivative of layer temperature (K s-1) + ! output: derivatives + real(rkind),intent(out) :: dCanairTemp_dEnthalpy ! derivative of canopy air temperature with enthalpy + real(rkind),intent(out) :: dCanopyTemp_dEnthalpy ! derivative of canopy temperature with enthalpy + real(rkind),intent(out) :: dTemp_dEnthalpy(:) ! derivative of layer temperature with enthalpy + real(rkind),intent(out) :: dTkCanairPrime_dEnthPrime ! derivative of canopy air temperature prime with enthalpy prime + real(rkind),intent(out) :: dTkCanopyPrime_dEnthPrime ! derivative of canopy temperature prime with enthalpy prime + real(rkind),intent(out) :: dTkPrime_dEnthPrime(:) ! derivative of layer temperature prime with enthalpy prime ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -955,6 +967,8 @@ subroutine enthalpy2T(& case(iname_cas) scalarCanairTempTrial = scalarCanairEnthalpy / ( Cp_air*iden_air ) + Tfreeze scalarCanairTempPrime = scalarCanairEnthalpyPrime / ( Cp_air*iden_air ) + dCanairTemp_dEnthalpy = 1._rkind / ( Cp_air*iden_air ) + dTkCanairPrime_dEnthPrime = 1._rkind / ( Cp_air*iden_air ) case(iname_veg) ! association to necessary variables for vegetation @@ -974,7 +988,7 @@ subroutine enthalpy2T(& else integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) ! Taylor-Maclaurin series for arctan - integral = (1._rkind/snowfrz_scale) * (snowfrz_scale * diffT - (snowfrz_scale * diffT)**3_i4b/3._rkind + (snowfrz_scale * diffT)**5_i4b/5._rkind - (snowfrz_scale * diffT)**7_i4b/7._rkind + (snowfrz_scale * diffT)**9_i4b/9._rkind) + !integral = (1._rkind/snowfrz_scale) * (snowfrz_scale * diffT - (snowfrz_scale * diffT)**3_i4b/3._rkind + (snowfrz_scale * diffT)**5_i4b/5._rkind - (snowfrz_scale * diffT)**7_i4b/7._rkind + (snowfrz_scale * diffT)**9_i4b/9._rkind) fLiq = 1._rkind / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b ) diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 3ca88b7de..e939a03dd 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -420,14 +420,20 @@ subroutine eval8summaWithPrime(& scalarCanopyWatPrime, & ! intent(in): derivative of canopy total water (kg m-2 s-1) mLayerVolFracWatPrime, & ! intent(in): derivative of volumetric total water content (s-1) mLayerMatricHeadPrime, & ! intent(in): derivative of total water matric potential (m s-1) - ! output: temperature for the vegetation canopy + ! output: temperature scalarCanairTempTrial, & ! intent(out): trial value for canopy air temperature (K) scalarCanopyTempTrial, & ! intent(out): trial value for canopy temperature (K) + mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) scalarCanairTempPrime, & ! intent(out): derivative of canopy air temperature (K s-1) scalarCanopyTempPrime, & ! intent(out): derivative of canopy temperature (K s-1) - ! output: temperature for the snow-soil domain - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) mLayerTempPrime, & ! intent(out): derivative of layer temperature (K s-1) + ! output: derivatives + dCanairTemp_dEnthalpy, & ! derivative of canopy air temperature with enthalpy + dCanopyTemp_dEnthalpy, & ! derivative of canopy temperature with enthalpy + dTemp_dEnthalpy, & ! derivative of layer temperature with enthalpy + dTkCanairPrime_dEnthPrime, & ! derivative of canopy air temperature prime with enthalpy prime + dTkCanopyPrime_dEnthPrime, & ! derivative of canopy temperature prime with enthalpy prime + dTkPrime_dEnthPrime, & ! derivative of layer temperature prime with enthalpy prime ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif From 18773f46f7f746bbbf5e28e7f9c0603ef8988ce0 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 13 Mar 2024 17:17:49 +0900 Subject: [PATCH 1209/1472] spaces, comment changes, no code changes --- build/source/engine/enthalpyTemp.f90 | 4 ++-- build/source/engine/layerMerge.f90 | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 7b7cbb934..81008c851 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -77,7 +77,7 @@ module enthalpyTemp_module ! ************************************************************************************************************************ -! public subroutine T2H_lookup_snow:: define a look-up table to mixture enthalpy based on temperature +! public subroutine T2H_lookup_snow: define a look-up table to mixture enthalpy based on temperature ! appropriate when no dry mass, as in snow ! ************************************************************************************************************************ subroutine T2H_lookup_snow(mpar_data, & ! intent(in): parameter data structure @@ -290,7 +290,7 @@ subroutine enthalpy2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) implicit none ! ------------------------------------------------------------------------------------------------------------------------- ! declare dummy variables - real(rkind),intent(in) :: Hy ! total temperature component of enthalpy (J m-3) + real(rkind),intent(in) :: Hy ! total enthalpy (J m-3) real(rkind),intent(in) :: BulkDenWater ! bulk density of water (kg m-3) real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) real(rkind),intent(out) :: Tk ! initial temperature guess / final temperature value (K) diff --git a/build/source/engine/layerMerge.f90 b/build/source/engine/layerMerge.f90 index 311445281..0ab8e2a4d 100644 --- a/build/source/engine/layerMerge.f90 +++ b/build/source/engine/layerMerge.f90 @@ -314,18 +314,18 @@ subroutine layer_combine(mpar_data,prog_data,diag_data,flux_data,indx_data,iSnow ! ------------------------------------------------------------------------------------------------------------ ! local variables character(len=256) :: cmessage ! error message for downwind routine - real(rkind) :: massIce(2) ! mass of ice in the two layers identified for combination (kg m-2) - real(rkind) :: massLiq(2) ! mass of liquid water in the two layers identified for combination (kg m-2) - real(rkind) :: bulkDenWat(2) ! bulk density if total water (liquid water plus ice) in the two layers identified for combination (kg m-3) - real(rkind) :: cBulkDenWat ! combined bulk density of total water (liquid water plus ice) in the two layers identified for combination (kg m-3) - real(rkind) :: cTemp ! combined layer temperature - real(rkind) :: cDepth ! combined layer depth - real(rkind) :: cVolFracIce ! combined layer volumetric fraction of ice - real(rkind) :: cVolFracLiq ! combined layer volumetric fraction of liquid water - real(rkind) :: l1Enthalpy,l2Enthalpy ! enthalpy in the two layers identified for combination (J m-3) - real(rkind) :: cEnthalpy ! combined layer enthalpy (J m-3) - real(rkind) :: fLiq ! fraction of liquid water at the combined temperature cTemp - real(rkind),parameter :: eTol=1.e-1_rkind ! tolerance for the enthalpy-->temperature conversion (J m-3) + real(rkind) :: massIce(2) ! mass of ice in the two layers identified for combination (kg m-2) + real(rkind) :: massLiq(2) ! mass of liquid water in the two layers identified for combination (kg m-2) + real(rkind) :: bulkDenWat(2) ! bulk density if total water (liquid water plus ice) in the two layers identified for combination (kg m-3) + real(rkind) :: cBulkDenWat ! combined bulk density of total water (liquid water plus ice) in the two layers identified for combination (kg m-3) + real(rkind) :: cTemp ! combined layer temperature + real(rkind) :: cDepth ! combined layer depth + real(rkind) :: cVolFracIce ! combined layer volumetric fraction of ice + real(rkind) :: cVolFracLiq ! combined layer volumetric fraction of liquid water + real(rkind) :: l1Enthalpy,l2Enthalpy ! enthalpy in the two layers identified for combination (J m-3) + real(rkind) :: cEnthalpy ! combined layer enthalpy (J m-3) + real(rkind) :: fLiq ! fraction of liquid water at the combined temperature cTemp + real(rkind),parameter :: eTol=1.e-1_rkind ! tolerance for the enthalpy-->temperature conversion (J m-3) integer(i4b) :: nSnow ! number of snow layers integer(i4b) :: nSoil ! number of soil layers integer(i4b) :: nLayers ! total number of layers From 6c67b474f496be9f28e84b0ab840f05cfd726359 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 14 Mar 2024 12:07:37 +0900 Subject: [PATCH 1210/1472] utils --- utils/plot_per_GRUMultBal.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index 633008731..434b40f3b 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -61,8 +61,8 @@ plot_vars = settings.copy() if stat == 'mean': - maxes = [1e-4,1e-2,1e-3,1e-2]+[1e-12,1e-9,1e-10,1e-11] + [3e-3] - if do_rel: maxes = [1e-6,1e-5,1e-8,1e-8]+[1e-10,1e-11,1e-13,1e-11] + [3e-3] + maxes = [1e-4,1e0,1e0,1e0]+[1e-12,1e-11,1e-10,1e-13] + [3e-3] + if do_rel: maxes = [1e-6,1e-4,1e-6,1e-7]+[1e-10,1e-11,1e-13,1e-11] + [3e-3] if stat == 'amax': maxes = [1e-3,1e3,1e3,1e2]+[1e-11,1e-6,1e-7,1e-8] + [1e0] if do_rel: maxes = [1e-2,1e0,1e-4,1e-2]+[1e-7,1e-8,1e-10,1e-6] + [1e0] @@ -210,7 +210,7 @@ def run_loop(j,var,the_max): my_cmap.set_bad(color='white') #nan color white vmin,vmax = the_max*1e-4, the_max if any(substring in var for substring in ['VegNrg', 'SnowNrg', 'SoilNrg']): - vmin, vmax = the_max * 1e-6, the_max + vmin, vmax = the_max * 1e-7, the_max if var in ['wallClockTime',]: vmin,vmax = the_max*1e-1, the_max norm = matplotlib.colors.LogNorm(vmin=vmin, vmax=vmax) From 46e25611bf13d848d99d625189abe7c004971ce5 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 14 Mar 2024 12:08:09 +0900 Subject: [PATCH 1211/1472] starting temp solving --- build/source/engine/enthalpyTemp.f90 | 82 +++++++++++---------- build/source/engine/eval8summaWithPrime.f90 | 23 ++---- build/source/engine/layerMerge.f90 | 28 +++---- 3 files changed, 63 insertions(+), 70 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index a5c328151..4f006e766 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -291,7 +291,7 @@ subroutine enthalpy2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) implicit none ! ------------------------------------------------------------------------------------------------------------------------- ! declare dummy variables - real(rkind),intent(in) :: Hy ! total temperature component of enthalpy (J m-3) + real(rkind),intent(in) :: Hy ! total enthalpy (J m-3) real(rkind),intent(in) :: BulkDenWater ! bulk density of water (kg m-3) real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) real(rkind),intent(out) :: Tk ! initial temperature guess / final temperature value (K) @@ -814,30 +814,21 @@ subroutine enthalpy2T(& scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpyTrial, & ! intent(in): trial value for enthalpy of the vegetation canopy (J m-3) mLayerEnthalpyTrial, & ! intent(in): trial vector of enthalpy of each snow+soil layer (J m-3) - scalarCanairEnthalpyPrime, & ! intent(in): derivative of enthalpy of the vegetation canopy (W m-3) - scalarCanopyEnthalpyPrime, & ! intent(in): derivative of enthalpy of the vegetation canopy (W m-3) - mLayerEnthalpyPrime, & ! intent(in): derivative of enthalpy of each snow+soil layer (W m-3) ! input: water state variables scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - scalarCanopyWatPrime, & ! intent(in): derivative of canopy total water (kg m-2 s-1) - mLayerVolFracWatPrime, & ! intent(in): derivative of volumetric total water content (s-1) - mLayerMatricHeadPrime, & ! intent(in): derivative of total water matric potential (m s-1) ! output: temperature scalarCanairTempTrial, & ! intent(out): trial value for canopy air temperature (K) scalarCanopyTempTrial, & ! intent(out): trial value for canopy temperature (K) mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - scalarCanairTempPrime, & ! intent(out): derivative of canopy air temperature (K s-1) - scalarCanopyTempPrime, & ! intent(out): derivative of canopy temperature (K s-1) - mLayerTempPrime, & ! intent(out): derivative of layer temperature (K s-1) ! output: derivatives - dCanairTemp_dEnthalpy, & ! derivative of canopy air temperature with enthalpy - dCanopyTemp_dEnthalpy, & ! derivative of canopy temperature with enthalpy - dTemp_dEnthalpy, & ! derivative of layer temperature with enthalpy - dTkCanairPrime_dEnthPrime, & ! derivative of canopy air temperature prime with enthalpy prime - dTkCanopyPrime_dEnthPrime, & ! derivative of canopy temperature prime with enthalpy prime - dTkPrime_dEnthPrime, & ! derivative of layer temperature prime with enthalpy prime + dCanairTemp_dEnthalpy, & ! intent(out): derivative of canopy air temperature with enthalpy + dCanopyTemp_dEnthalpy, & ! intent(out): derivative of canopy temperature with enthalpy + dTemp_dEnthalpy, & ! intent(out): derivative of layer temperature with enthalpy + dCanopyTemp_dCanWat, & ! intent(out): derivative of canopy temperature with canopy water + dTemp_dTheta, & ! intent(out): derivative of layer temperature with volumetric total water content + dTemp_dPsi0, & ! intent(out): derivative of layer temperature with total water matric potential ! output: error control err,cmessage) ! intent(out): error control ! ------------------------------------------------------------------------------------------------------------------------- @@ -856,32 +847,23 @@ subroutine enthalpy2T(& type(zLookup),intent(in) :: lookup_data ! lookup tables ! output: enthalpy state variables real(rkind),intent(in) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - real(rkind),intent(in) :: scalarCanopyEnthTemp ! enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(in) :: mLayerEnthTemp(:) ! enthalpy of each snow+soil layer (J m-3) - real(rkind),intent(in) :: scalarCanairEnthalpyPrime ! derivative of enthalpy of the vegetation canopy (W m-3) - real(rkind),intent(in) :: scalarCanopyEnthalpyPrime ! derivative of enthalpy of the vegetation canopy (W m-3) - real(rkind),intent(in) :: mLayerEnthalpyPrime(:) ! derivative of enthalpy of each snow+soil layer (W m-3) + real(rkind),intent(in) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(in) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) ! input: water state variables real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value for canopy total water (kg m-2) real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind),intent(in) :: scalarCanopyWatPrime ! derivative of canopy total water (kg m-2 s-1) - real(rkind),intent(in) :: mLayerVolFracWatPrime(:) ! derivative of volumetric total water content (s-1) - real(rkind),intent(in) :: mLayerMatricHeadPrime(:) ! derivative of total water matric potential (m s-1) ! output: temperature diagnostic variables real(rkind),intent(out) :: scalarCanairTempTrial ! trial value for canopy air temperature (K) real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value for canopy temperature (K) real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(out) :: scalarCanairTempPrime ! derivative of canopy air temperature (K s-1) - real(rkind),intent(out) :: scalarCanopyTempPrime ! derivative of canopy temperature (K s-1) - real(rkind),intent(out) :: mLayerTempPrime(:) ! derivative of layer temperature (K s-1) ! output: derivatives real(rkind),intent(out) :: dCanairTemp_dEnthalpy ! derivative of canopy air temperature with enthalpy real(rkind),intent(out) :: dCanopyTemp_dEnthalpy ! derivative of canopy temperature with enthalpy real(rkind),intent(out) :: dTemp_dEnthalpy(:) ! derivative of layer temperature with enthalpy - real(rkind),intent(out) :: dTkCanairPrime_dEnthPrime ! derivative of canopy air temperature prime with enthalpy prime - real(rkind),intent(out) :: dTkCanopyPrime_dEnthPrime ! derivative of canopy temperature prime with enthalpy prime - real(rkind),intent(out) :: dTkPrime_dEnthPrime(:) ! derivative of layer temperature prime with enthalpy prime + real(rkind),intent(out) :: dCanopyTemp_dCanWat ! derivative of canopy temperature with canopy water + real(rkind),intent(out) :: dTemp_dTheta(:) ! derivative of layer temperature with volumetric total water content + real(rkind),intent(out) :: dTemp_dPsi0(:) ! derivative of layer temperature with total water matric potential ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -908,14 +890,6 @@ subroutine enthalpy2T(& real(rkind) :: integral_frz_upp ! upper limit of integral of frozen soil water content (from Tfreeze to soil temperature) real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) - ! enthalpy - real(rkind) :: enthVeg ! enthalpy of the vegetation (J m-3) - real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) - real(rkind) :: enthLiq ! enthalpy of the liquid region (J m-3) - real(rkind) :: enthIce ! enthalpy of the ice region (J m-3) - real(rkind) :: enthAir ! enthalpy of air (J m-3) - real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) - real(rkind) :: enthWater ! enthalpy of total water (J m-3) logical(lgt),parameter :: doTest=.false. ! flag to run unit test ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures @@ -966,9 +940,7 @@ subroutine enthalpy2T(& select case(ixDomainType) case(iname_cas) scalarCanairTempTrial = scalarCanairEnthalpy / ( Cp_air*iden_air ) + Tfreeze - scalarCanairTempPrime = scalarCanairEnthalpyPrime / ( Cp_air*iden_air ) dCanairTemp_dEnthalpy = 1._rkind / ( Cp_air*iden_air ) - dTkCanairPrime_dEnthPrime = 1._rkind / ( Cp_air*iden_air ) case(iname_veg) ! association to necessary variables for vegetation @@ -995,6 +967,36 @@ subroutine enthalpy2T(& enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth endif + + + ! ***** get initial guess + T = scalarCanopyEnthalpy * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial ) + Tfreeze + dT_dE = canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial ) + dT_dW = -Cp_water * scalarCanopyEnthalpy * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial )**2_i4b + diffT = T - Tfreeze + + if(diffT>=0._rkind)then + scalarCanopyTempTrial = T + dCanopyTemp_dEnthalpy = dT_dE + dCanopyTemp_dCanWat = dT_dW + else + ! ***** iterate to find temperature + + do while(abs(scalarCanopyEnthalpyTrial-H)>1.e-6_rkind) + diffT = T - Tfreeze + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + fLiq = 1._rkind / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b ) + H = ( (specificHeatVeg * maxMassVegetation + Cp_ice * scalarCanopyWatTrial )* diffT + (Cp_water - Cp_ice)* scalarCanopyWatTrial * integral & + + LH_fus * (1._rkind - fLiq) * scalarCanopyWatTrial )/ canopyDepth + + + + + + + end do + ! ***** iterate to find temperature + ! diffT>=0._rkind scalarCanopyTempTrial = scalarCanopyEnthalpy * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial ) + Tfreeze diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index e939a03dd..4c46492c6 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -410,30 +410,21 @@ subroutine eval8summaWithPrime(& scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpyTrial, & ! intent(in): trial value for enthalpy of the vegetation canopy (J m-3) mLayerEnthalpyTrial, & ! intent(in): trial vector of enthalpy of each snow+soil layer (J m-3) - scalarCanairEnthalpyPrime, & ! intent(in): derivative of enthalpy of the vegetation canopy (W m-3) - scalarCanopyEnthalpyPrime, & ! intent(in): derivative of enthalpy of the vegetation canopy (W m-3) - mLayerEnthalpyPrime, & ! intent(in): derivative of enthalpy of each snow+soil layer (W m-3) ! input: water state variables scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - scalarCanopyWatPrime, & ! intent(in): derivative of canopy total water (kg m-2 s-1) - mLayerVolFracWatPrime, & ! intent(in): derivative of volumetric total water content (s-1) - mLayerMatricHeadPrime, & ! intent(in): derivative of total water matric potential (m s-1) - ! output: temperature + ! output: temperature scalarCanairTempTrial, & ! intent(out): trial value for canopy air temperature (K) scalarCanopyTempTrial, & ! intent(out): trial value for canopy temperature (K) mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - scalarCanairTempPrime, & ! intent(out): derivative of canopy air temperature (K s-1) - scalarCanopyTempPrime, & ! intent(out): derivative of canopy temperature (K s-1) - mLayerTempPrime, & ! intent(out): derivative of layer temperature (K s-1) ! output: derivatives - dCanairTemp_dEnthalpy, & ! derivative of canopy air temperature with enthalpy - dCanopyTemp_dEnthalpy, & ! derivative of canopy temperature with enthalpy - dTemp_dEnthalpy, & ! derivative of layer temperature with enthalpy - dTkCanairPrime_dEnthPrime, & ! derivative of canopy air temperature prime with enthalpy prime - dTkCanopyPrime_dEnthPrime, & ! derivative of canopy temperature prime with enthalpy prime - dTkPrime_dEnthPrime, & ! derivative of layer temperature prime with enthalpy prime + dCanairTemp_dEnthalpy, & ! intent(out): derivative of canopy air temperature with enthalpy + dCanopyTemp_dEnthalpy, & ! intent(out): derivative of canopy temperature with enthalpy + dTemp_dEnthalpy, & ! intent(out): derivative of layer temperature with enthalpy + dCanopyTemp_dCanWat, & ! intent(out): derivative of canopy temperature with canopy water + dTemp_dTheta, & ! intent(out): derivative of layer temperature with volumetric total water content + dTemp_dPsi0, & ! intent(out): derivative of layer temperature with total water matric potential ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif diff --git a/build/source/engine/layerMerge.f90 b/build/source/engine/layerMerge.f90 index 0741970c0..0ab8e2a4d 100644 --- a/build/source/engine/layerMerge.f90 +++ b/build/source/engine/layerMerge.f90 @@ -297,7 +297,7 @@ subroutine layer_combine(mpar_data,prog_data,diag_data,flux_data,indx_data,iSnow USE data_types,only:var_d ! data structures with fixed dimension ! provide access to external modules USE snow_utils_module,only:fracliquid ! compute fraction of liquid water - USE enthalpyTemp_module,only:ethalpy2T_snow,T2enthalpy_snow ! convert temperature to enthalpy for a snow layer + USE enthalpyTemp_module,only:enthalpy2T_snow,T2enthalpy_snow ! convert temperature to enthalpy for a snow layer implicit none ! ------------------------------------------------------------------------------------------------------------ ! input/output: data structures @@ -314,18 +314,18 @@ subroutine layer_combine(mpar_data,prog_data,diag_data,flux_data,indx_data,iSnow ! ------------------------------------------------------------------------------------------------------------ ! local variables character(len=256) :: cmessage ! error message for downwind routine - real(rkind) :: massIce(2) ! mass of ice in the two layers identified for combination (kg m-2) - real(rkind) :: massLiq(2) ! mass of liquid water in the two layers identified for combination (kg m-2) - real(rkind) :: bulkDenWat(2) ! bulk density if total water (liquid water plus ice) in the two layers identified for combination (kg m-3) - real(rkind) :: cBulkDenWat ! combined bulk density of total water (liquid water plus ice) in the two layers identified for combination (kg m-3) - real(rkind) :: cTemp ! combined layer temperature - real(rkind) :: cDepth ! combined layer depth - real(rkind) :: cVolFracIce ! combined layer volumetric fraction of ice - real(rkind) :: cVolFracLiq ! combined layer volumetric fraction of liquid water - real(rkind) :: l1Enthalpy,l2Enthalpy ! enthalpy in the two layers identified for combination (J m-3) - real(rkind) :: cEnthalpy ! combined layer enthalpy (J m-3) - real(rkind) :: fLiq ! fraction of liquid water at the combined temperature cTemp - real(rkind),parameter :: eTol=1.e-1_rkind ! tolerance for the enthalpy-->temperature conversion (J m-3) + real(rkind) :: massIce(2) ! mass of ice in the two layers identified for combination (kg m-2) + real(rkind) :: massLiq(2) ! mass of liquid water in the two layers identified for combination (kg m-2) + real(rkind) :: bulkDenWat(2) ! bulk density if total water (liquid water plus ice) in the two layers identified for combination (kg m-3) + real(rkind) :: cBulkDenWat ! combined bulk density of total water (liquid water plus ice) in the two layers identified for combination (kg m-3) + real(rkind) :: cTemp ! combined layer temperature + real(rkind) :: cDepth ! combined layer depth + real(rkind) :: cVolFracIce ! combined layer volumetric fraction of ice + real(rkind) :: cVolFracLiq ! combined layer volumetric fraction of liquid water + real(rkind) :: l1Enthalpy,l2Enthalpy ! enthalpy in the two layers identified for combination (J m-3) + real(rkind) :: cEnthalpy ! combined layer enthalpy (J m-3) + real(rkind) :: fLiq ! fraction of liquid water at the combined temperature cTemp + real(rkind),parameter :: eTol=1.e-1_rkind ! tolerance for the enthalpy-->temperature conversion (J m-3) integer(i4b) :: nSnow ! number of snow layers integer(i4b) :: nSoil ! number of soil layers integer(i4b) :: nLayers ! total number of layers @@ -368,7 +368,7 @@ subroutine layer_combine(mpar_data,prog_data,diag_data,flux_data,indx_data,iSnow cEnthalpy = (mLayerDepth(isnow)*l1Enthalpy + mLayerDepth(isnow+1)*l2Enthalpy)/cDepth ! convert enthalpy (J m-3) to temperature (K) - call ethalpy2T_snow(cEnthalpy,cBulkDenWat,snowfrz_scale,cTemp,err,cmessage) + call enthalpy2T_snow(cEnthalpy,cBulkDenWat,snowfrz_scale,cTemp,err,cmessage) if(err/=0)then; err=10; message=trim(message)//trim(cmessage); return; end if ! test enthalpy conversion From 08332e4a5e83d5f3ae51278298662ad15ddb80dc Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 14 Mar 2024 12:12:19 +0900 Subject: [PATCH 1212/1472] Merge pull request #35 from seantrim/develop_refactor_Newton --- build/source/dshare/data_types.f90 | 152 ++- build/source/engine/summaSolve4numrec.f90 | 1048 ++++++++++++--------- build/source/engine/systemSolv.f90 | 707 +++++++------- 3 files changed, 1096 insertions(+), 811 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 1dd37de88..374a9e8c9 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -660,7 +660,7 @@ MODULE data_types ! Define classes used to simplify calls to the subrotuines in summaSolve4numrec ! *********************************************************************************************************** - type, public :: in_type_computJacob ! class for intent(in) arguments in computFlux call + type, public :: in_type_computJacob ! class for intent(in) arguments in computJacob call ! input: model control real(rkind) :: dt ! intent(in): length of the time step (seconds) integer(i4b) :: nSnow ! intent(in): number of snow layers @@ -673,7 +673,7 @@ MODULE data_types procedure :: initialize => initialize_in_computJacob end type in_type_computJacob - type, public :: out_type_computJacob ! class for intent(in) arguments in computFlux call + type, public :: out_type_computJacob ! class for intent(out) arguments in computJacob call ! output: error control integer(i4b) :: err ! intent(out): error code character(len=len_msg) :: cmessage ! intent(out): error message @@ -681,6 +681,63 @@ MODULE data_types procedure :: finalize => finalize_out_computJacob end type out_type_computJacob + type, public :: in_type_lineSearchRefinement ! class for intent(in) arguments in lineSearchRefinement call + logical(lgt) :: doSearch ! intent(in): flag to do the line search + real(rkind) :: fOld ! intent(in): old function value + contains + procedure :: initialize => initialize_in_lineSearchRefinement + end type in_type_lineSearchRefinement + + type, public :: out_type_lineSearchRefinement ! class for intent(out) arguments in lineSearchRefinement call + real(rkind) :: fNew ! intent(out): new function evaluation + logical(lgt) :: converged ! intent(out): convergence flag + ! output: error control + integer(i4b) :: err ! intent(out): error code + character(len=len_msg) :: message ! intent(out): error message + contains + procedure :: finalize => finalize_out_lineSearchRefinement + end type out_type_lineSearchRefinement + + ! *********************************************************************************************************** + ! Define classes used to simplify calls to the subrotuines in systemSolv + ! *********************************************************************************************************** + + type, public :: in_type_summaSolve4numrec ! class for intent(in) arguments in summaSolve4numrec call + real(rkind) :: dt_cur ! intent(in): current stepsize + real(rkind) :: dt ! intent(in): entire time step for drainage pond rate + integer(i4b) :: iter ! intent(in): iteration index + integer(i4b) :: nSnow ! intent(in): number of snow layers + integer(i4b) :: nSoil ! intent(in): number of soil layers + integer(i4b) :: nLayers ! intent(in): total number of layers + integer(i4b) :: nLeadDim ! intent(in): length of the leading dimension of the Jacobian matrix (nBands or nState) + integer(i4b) :: nState ! intent(in): total number of state variables + integer(i4b) :: ixMatrix ! intent(in): type of matrix (full or band diagonal) + logical(lgt) :: firstSubStep ! intent(in): flag to indicate if we are processing the first sub-step + logical(lgt) :: computeVegFlux ! intent(in): flag to indicate if computing fluxes over vegetation + logical(lgt) :: scalarSolution ! intent(in): flag to denote if implementing the scalar solution + real(rkind) :: fOld ! intent(in): old function evaluation + contains + procedure :: initialize => initialize_in_summaSolve4numrec + end type in_type_summaSolve4numrec + + type, public :: io_type_summaSolve4numrec ! class for intent(inout) arguments in summaSolve4numrec call + logical(lgt) :: firstFluxCall ! intent(inout): flag to indicate if we are processing the first flux call + real(rkind) :: xMin,xMax ! intent(inout): brackets of the root + integer(i4b) :: ixSaturation ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + contains + procedure :: initialize => initialize_io_summaSolve4numrec + procedure :: finalize => finalize_io_summaSolve4numrec + end type io_type_summaSolve4numrec + + type, public :: out_type_summaSolve4numrec ! class for intent(out) arguments in summaSolve4numrec call + real(rkind) :: fNew ! intent(out): new function evaluation + logical(lgt) :: converged ! intent(out): convergence flag + integer(i4b) :: err ! intent(out): error code + character(len=len_msg) :: message ! intent(out): error message + contains + procedure :: finalize => finalize_out_summaSolve4numrec + end type out_type_summaSolve4numrec + contains ! **** vegNrgFlux **** @@ -1473,4 +1530,95 @@ subroutine finalize_out_computJacob(out_computJacob,err,cmessage) cmessage = out_computJacob % cmessage ! intent(out): error message end subroutine finalize_out_computJacob + ! **** lineSearchRefinement **** + subroutine initialize_in_lineSearchRefinement(in_lineSearchRefinement,doSearch,fOld) + class(in_type_lineSearchRefinement),intent(out) :: in_lineSearchRefinement ! class object for intent(out) arguments + logical(lgt),intent(in) :: doSearch ! intent(in): flag to do the line search + real(rkind) ,intent(in) :: fOld ! intent(in): old function value + in_lineSearchRefinement % doSearch = doSearch ! intent(in): flag to do the line search + in_lineSearchRefinement % fOld = fOld ! intent(in): old function value + end subroutine initialize_in_lineSearchRefinement + + subroutine finalize_out_lineSearchRefinement(out_lineSearchRefinement,fNew,converged,err,message) + class(out_type_lineSearchRefinement),intent(in) :: out_lineSearchRefinement ! class object for intent(out) arguments + real(rkind) ,intent(out) :: fNew ! intent(out): new function evaluation + logical(lgt),intent(out) :: converged ! intent(out): convergence flag + integer(i4b),intent(out) :: err ! intent(out): error code + character(*),intent(out) :: message ! intent(out): error message + fNew = out_lineSearchRefinement % fNew ! intent(out): new function evaluation + converged = out_lineSearchRefinement % converged ! intent(out): convergence flag + err = out_lineSearchRefinement % err ! intent(out): error code + message = out_lineSearchRefinement % message ! intent(out): error message + end subroutine finalize_out_lineSearchRefinement + + ! **** summaSolve4numrec **** + + subroutine initialize_in_summaSolve4numrec(in_SS4NR,dt_cur,dt,iter,nSnow,nSoil,nLayers,nLeadDim,nState,ixMatrix,firstSubStep,computeVegFlux,scalarSolution,fOld) + class(in_type_summaSolve4numrec),intent(out) :: in_SS4NR ! class object for intent(out) arguments + real(rkind) ,intent(in) :: dt_cur ! intent(in): current stepsize + real(rkind) ,intent(in) :: dt ! intent(in): entire time step for drainage pond rate + integer(i4b),intent(in) :: iter ! intent(in): iteration index + integer(i4b),intent(in) :: nSnow ! intent(in): number of snow layers + integer(i4b),intent(in) :: nSoil ! intent(in): number of soil layers + integer(i4b),intent(in) :: nLayers ! intent(in): total number of layers + integer(i4b),intent(in) :: nLeadDim ! intent(in): length of the leading dimension of the Jacobian matrix (nBands or nState) + integer(i4b),intent(in) :: nState ! intent(in): total number of state variables + integer(i4b),intent(in) :: ixMatrix ! intent(in): type of matrix (full or band diagonal) + logical(lgt),intent(in) :: firstSubStep ! intent(in): flag to indicate if we are processing the first sub-step + logical(lgt),intent(in) :: computeVegFlux ! intent(in): flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: scalarSolution ! intent(in): flag to denote if implementing the scalar solution + real(rkind) ,intent(in) :: fOld ! intent(in): old function evaluation + + in_SS4NR % dt_cur = dt_cur + in_SS4NR % dt = dt + in_SS4NR % iter = iter + in_SS4NR % nSnow = nSnow + in_SS4NR % nSoil = nSoil + in_SS4NR % nLayers = nLayers + in_SS4NR % nLeadDim = nLeadDim + in_SS4NR % nState = nState + in_SS4NR % ixMatrix = ixMatrix + in_SS4NR % firstSubStep = firstSubStep + in_SS4NR % computeVegFlux = computeVegFlux + in_SS4NR % scalarSolution = scalarSolution + in_SS4NR % fOld = fOld + end subroutine initialize_in_summaSolve4numrec + + subroutine initialize_io_summaSolve4numrec(io_SS4NR,firstFluxCall,xMin,xMax,ixSaturation) + class(io_type_summaSolve4numrec),intent(out) :: io_SS4NR ! class object for intent(inout) arguments + logical(lgt),intent(in) :: firstFluxCall ! intent(inout): flag to indicate if we are processing the first flux call + real(rkind) ,intent(in) :: xMin,xMax ! intent(inout): brackets of the root + integer(i4b),intent(in) :: ixSaturation ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + + io_SS4NR % firstFluxCall = firstFluxCall + io_SS4NR % xMin = xMin + io_SS4NR % xMax = xMax + io_SS4NR % ixSaturation = ixSaturation + end subroutine initialize_io_summaSolve4numrec + + subroutine finalize_io_summaSolve4numrec(io_SS4NR,firstFluxCall,xMin,xMax,ixSaturation) + class(io_type_summaSolve4numrec),intent(in) :: io_SS4NR ! class object for intent(inout) arguments + logical(lgt),intent(out) :: firstFluxCall ! intent(inout): flag to indicate if we are processing the first flux call + real(rkind) ,intent(out) :: xMin,xMax ! intent(inout): brackets of the root + integer(i4b),intent(out) :: ixSaturation ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + + firstFluxCall = io_SS4NR % firstFluxCall + xMin = io_SS4NR % xMin + xMax = io_SS4NR % xMax + ixSaturation = io_SS4NR % ixSaturation + end subroutine finalize_io_summaSolve4numrec + + subroutine finalize_out_summaSolve4numrec(out_SS4NR,fNew,converged,err,message) + class(out_type_summaSolve4numrec),intent(in) :: out_SS4NR ! class object for intent(out) arguments + real(rkind) ,intent(out) :: fNew ! intent(out): new function evaluation + logical(lgt),intent(out) :: converged ! intent(out): convergence flag + integer(i4b),intent(out) :: err ! intent(out): error code + character(*),intent(out) :: message ! intent(out): error message + + fNew = out_SS4NR % fNew + converged = out_SS4NR % converged + err = out_SS4NR % err + message = out_SS4NR % message + end subroutine finalize_out_summaSolve4numrec + END MODULE data_types diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 index 22fbff411..c62c2e869 100644 --- a/build/source/engine/summaSolve4numrec.f90 +++ b/build/source/engine/summaSolve4numrec.f90 @@ -66,14 +66,20 @@ module summaSolve4numrec_module ! provide access to the derived types to define the data structures USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! lookup tables - model_options, & ! defines the model decisions - in_type_computJacob, & ! class for computJacob arguments - out_type_computJacob ! class for computJacob arguments + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! lookup tables + model_options, & ! defines the model decisions + in_type_computJacob, & ! class for computJacob arguments + out_type_computJacob, & ! class for computJacob arguments + in_type_lineSearchRefinement, & ! class for lineSearchRefinement arguments + out_type_lineSearchRefinement,& ! class for lineSearchRefinement arguments + in_type_summaSolve4numrec, & ! class for summaSolve4numrec arguments + io_type_summaSolve4numrec, & ! class for summaSolve4numrec arguments + out_type_summaSolve4numrec ! class for summaSolve4numrec arguments + ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: & @@ -95,29 +101,17 @@ module summaSolve4numrec_module ! public subroutine summaSolve4numrec: calculate the iteration increment, evaluate the new state, and refine if necessary ! *********************************************************************************************************************** subroutine summaSolve4numrec(& + in_SS4NR, & ! intent(in): model control and previous function value ! input: model control - dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate - iter, & ! intent(in): iteration index - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - nLeadDim, & ! intent(in): length of the leading dimension of the Jacobian matrix (either nBands or nState) - nState, & ! intent(in): total number of state variables - ixMatrix, & ! intent(in): type of matrix (full or band diagonal) - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution + !firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call ! input: state vectors stateVecTrial, & ! intent(in): trial state vector - xMin,xMax, & ! intent(inout): brackets of the root + !xMin,xMax, & ! intent(inout): brackets of the root fScale, & ! intent(in): characteristic scale of the function evaluations xScale, & ! intent(in): characteristic scale of the state vector rVec, & ! intent(in): residual vector sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) dMat, & ! intent(inout): diagonal matrix (excludes flux derivatives) - fOld, & ! intent(in): old function evaluation ! input: data structures model_decisions, & ! intent(in): model decisions lookup_data, & ! intent(in): lookup tables @@ -133,45 +127,33 @@ subroutine summaSolve4numrec(& flux_data, & ! intent(inout): model fluxes for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + !ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) dBaseflow_dMatric, & ! intent(inout): derivative in baseflow w.r.t. matric head (s-1) + io_SS4NR, & ! intent(inout): first flux call flag, root brackets, index of lowest saturated layer ! output stateVecNew, & ! intent(out): new state vector fluxVecNew, & ! intent(out): new flux vector resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equation resVecNew, & ! intent(out): new residual vector - fNew, & ! intent(out): new function evaluation - converged, & ! intent(out): convergence flag - err,message) ! intent(out): error control + out_SS4NR) ! intent(out): new function evaluation, convergence flag, and error control USE computJacob_module, only: computJacob USE eval8summa_module, only: imposeConstraints USE matrixOper_module, only: lapackSolv USE matrixOper_module, only: scaleMatrices implicit none ! -------------------------------------------------------------------------------------------------------------------------------- + type(in_type_summaSolve4numrec),intent(in) :: in_SS4NR ! model control variables and previous function evaluation + type(io_type_summaSolve4numrec) :: io_SS4NR ! first flux call flag and baseflow variables ! input: model control - real(rkind),intent(in) :: dt_cur ! current stepsize - real(rkind),intent(in) :: dt ! entire time step for drainage pond rate - integer(i4b),intent(in) :: iter ! iteration index - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - integer(i4b),intent(in) :: nLeadDim ! length of the leading dimension of the Jacobian matrix (nBands or nState) - integer(i4b),intent(in) :: nState ! total number of state variables - integer(i4b),intent(in) :: ixMatrix ! type of matrix (full or band diagonal) - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution +! logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call ! input: state vectors real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector - real(rkind),intent(inout) :: xMin,xMax ! brackets of the root +! real(rkind),intent(inout) :: xMin,xMax ! brackets of the root real(rkind),intent(in) :: fScale(:) ! characteristic scale of the function evaluations real(rkind),intent(in) :: xScale(:) ! characteristic scale of the state vector real(qp),intent(in) :: rVec(:) ! NOTE: qp ! residual vector real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(rkind),intent(inout) :: dMat(:) ! diagonal matrix (excludes flux derivatives) - real(rkind),intent(in) :: fOld ! old function evaluation ! input: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions type(zLookup), intent(in) :: lookup_data ! lookup tables @@ -187,31 +169,27 @@ subroutine summaSolve4numrec(& type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) +! integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) real(rkind),intent(inout) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors real(rkind),intent(out) :: stateVecNew(:) ! new state vector real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector real(rkind),intent(out) :: resSinkNew(:) ! sink terms on the RHS of the flux equation real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector - real(rkind),intent(out) :: fNew ! new function evaluation - logical(lgt),intent(out) :: converged ! convergence flag - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + type(out_type_summaSolve4numrec),intent(out) :: out_SS4NR ! new function evaluation, convergence flag, and error control ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- ! Jacobian matrix logical(lgt),parameter :: doNumJacobian=.false. ! flag to compute the numerical Jacobian matrix logical(lgt),parameter :: testBandDiagonal=.false. ! flag to test the band diagonal Jacobian matrix - real(rkind) :: nJac(nState,nState) ! numerical Jacobian matrix - real(rkind) :: aJac(nLeadDim,nState) ! Jacobian matrix - real(rkind) :: aJacScaled(nLeadDim,nState) ! Jacobian matrix (scaled) - real(rkind) :: aJacScaledTemp(nLeadDim,nState) ! Jacobian matrix (scaled) -- temporary copy since decomposed in lapack + real(rkind) :: nJac(in_SS4NR % nState,in_SS4NR % nState) ! numerical Jacobian matrix + real(rkind) :: aJac(in_SS4NR % nLeadDim,in_SS4NR % nState) ! Jacobian matrix + real(rkind) :: aJacScaled(in_SS4NR % nLeadDim,in_SS4NR % nState) ! Jacobian matrix (scaled) + real(rkind) :: aJacScaledTemp(in_SS4NR % nLeadDim,in_SS4NR % nState) ! Jacobian matrix (scaled) -- temporary copy since decomposed in lapack ! solution/step vectors - real(rkind),dimension(nState) :: rVecScaled ! residual vector (scaled) - real(rkind),dimension(nState) :: newtStepScaled ! full newton step (scaled) + real(rkind),dimension(in_SS4NR % nState) :: rVecScaled ! residual vector (scaled) + real(rkind),dimension(in_SS4NR % nState) :: newtStepScaled ! full newton step (scaled) ! step size refinement logical(lgt) :: doRefine ! flag for step refinement integer(i4b),parameter :: ixLineSearch=1001 ! step refinement = line search @@ -222,284 +200,361 @@ subroutine summaSolve4numrec(& integer(i4b) :: iLayer ! row index integer(i4b) :: jLayer ! column index logical(lgt) :: globalPrintFlagInit ! initial global print flag + logical(lgt) :: return_flag ! flag that controls execution of return statements character(LEN=256) :: cmessage ! error message of downwind routine ! class objects for subroutine arguments - type(in_type_computJacob) :: in_computJacob - type(out_type_computJacob) :: out_computJacob + type(in_type_computJacob) :: in_computJacob ! computJacob + type(out_type_computJacob) :: out_computJacob ! computJacob + type(in_type_lineSearchRefinement) :: in_LSR ! lineSearchRefinement + type(out_type_lineSearchRefinement) :: out_LSR ! lineSearchRefinement + type(in_type_lineSearchRefinement) :: in_TRR ! trustRegionRefinement + type(out_type_lineSearchRefinement) :: out_TRR ! trustRegionRefinement + type(out_type_lineSearchRefinement) :: out_SRF ! safeRootFinder ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='summaSolve4numrec/' - -! choose Jacobian type - select case(model_decisions(iLookDECISIONS%fDerivMeth)%iDecision) - case(numerical); err=20; message=trim(message)//'numerical derivatives are not implemented for BE numerical Recipes solver'; return - case(analytical); ! this is fine - case default; err=20; message=trim(message)//'expect choice numericl or analytic to calculate derivatives for Jacobian'; return - end select - - ! get the number of soil layers in the solution vector - mSoil = size(indx_data%var(iLookINDEX%ixMatOnly)%dat) - - ! initialize the global print flag - globalPrintFlagInit=globalPrintFlag - - ! ----- - ! * compute the Jacobian matrix... - ! -------------------------------- - - ! compute the analytical Jacobian matrix - ! NOTE: The derivatives were computed in the previous call to computFlux - ! This occurred either at the call to eval8summa at the start of systemSolv - ! or in the call to eval8summa in the previous iteration (within lineSearchRefinement or trustRegionRefinement) - call initialize_computJacob_summaSolve4numrec - call computJacob(in_computJacob,indx_data,prog_data,diag_data,deriv_data,dBaseflow_dMatric,dMat,aJac,out_computJacob) - call finalize_computJacob_summaSolve4numrec - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! compute the numerical Jacobian matrix - if (doNumJacobian) then - globalPrintFlag=.false. - call numJacobian(stateVecTrial,dMat,nJac,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - globalPrintFlag=globalPrintFlagInit - end if - - ! test the band diagonal matrix - if (testBandDiagonal) then - call testBandMat(check=.true.,err=err,message=cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - end if - - ! ----- - ! * solve linear system... - ! ------------------------ - - ! scale the residual vector - rVecScaled(1:nState) = fScale(:)*real(rVec(:), rkind) ! NOTE: residual vector is in quadruple precision - - ! scale matrices - call scaleMatrices(ixMatrix,nState,aJac,fScale,xScale,aJacScaled,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - if (globalPrintFlag .and. ixMatrix==ixBandMatrix) then - print*, '** SCALED banded analytical Jacobian:' - write(*,'(a4,1x,100(i17,1x))') 'xCol', (iLayer, iLayer=iJac1,iJac2) - do iLayer=kl+1,nBands - write(*,'(i4,1x,100(e17.10,1x))') iLayer, (aJacScaled(iLayer,jLayer),jLayer=min(iJac1,nState),min(iJac2,nState)) - end do - end if - - ! copy the scaled matrix, since it is decomposed in lapackSolv - aJacScaledTemp = aJacScaled - - ! compute the newton step: use the lapack routines to solve the linear system A.X=B - call lapackSolv(ixMatrix,nState,aJacScaledTemp,-rVecScaled,newtStepScaled,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - if (globalPrintFlag) write(*,'(a,1x,10(e17.10,1x))') 'newtStepScaled = ', newtStepScaled(min(iJac1,nState):min(iJac2,nState)) - ! ----- - ! * update, evaluate, and refine the state vector... - ! -------------------------------------------------- + ! ***** Compute the Newton Step ***** - ! initialize the flag for step refinement - doRefine=.true. + call initialize_summaSolve4numrec; if (return_flag) return ! initial setup -- return if error - ! * case 1: state vector - ! compute the flux vector and the residual, and (if necessary) refine the iteration increment - ! NOTE: in 99.9% of cases newtStep will be used (no refinement) - if (size(stateVecTrial)>1) then + call update_Jacobian; if (return_flag) return ! compute Jacobian for Newton step -- return if error - ! try to backtrack - select case(ixStepRefinement) - case(ixLineSearch); call lineSearchRefinement( doRefine,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,cmessage) - case(ixTrustRegion); call trustRegionRefinement(doRefine,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,cmessage) - case default; err=20; message=trim(message)//'unable to identify numerical solution'; return - end select + call solve_linear_system; if (return_flag) return ! solve the linear system for the Newton step -- return if error - ! check warnings: negative error code = warning; in this case back-tracked to the original value - ! NOTE: Accept the full newton step if back-tracked to the original value - if (err<0) then - doRefine=.false.; - call lineSearchRefinement( doRefine,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,cmessage) - end if + call refine_Newton_step; if (return_flag) return ! refine Newton step if needed -- return if error - ! * case 2: scalar - else - call safeRootfinder(stateVecTrial,rVecScaled,newtStepScaled,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - endif + associate(err => out_SS4NR % err,message => out_SS4NR % message) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors + end associate - ! check errors - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + contains + subroutine initialize_summaSolve4numrec + ! *** Initial steps for the summaSolve4numrec algorithm (computing the Newton step) *** + + associate(& + err => out_SS4NR % err ,& + message => out_SS4NR % message & + &) + ! initialize error control + err=0; message='summaSolve4numrec/' + return_flag=.false. ! initialize return flag + + ! choose Jacobian type + select case(model_decisions(iLookDECISIONS%fDerivMeth)%iDecision) + case(numerical) + err=20; message=trim(message)//'numerical derivatives are not implemented for BE numerical Recipes solver'; + return_flag=.true.; return + case(analytical); ! this is fine + case default + err=20; message=trim(message)//'expect choice numericl or analytic to calculate derivatives for Jacobian'; + return_flag=.true.; return + end select + + end associate + + ! get the number of soil layers in the solution vector + mSoil = size(indx_data%var(iLookINDEX%ixMatOnly)%dat) + + ! initialize the global print flag + globalPrintFlagInit=globalPrintFlag + end subroutine initialize_summaSolve4numrec + + subroutine update_Jacobian + ! *** Update Jacobian used for Newton step *** + + ! compute the analytical Jacobian matrix + ! NOTE: The derivatives were computed in the previous call to computFlux + ! This occurred either at the call to eval8summa at the start of systemSolv + ! or in the call to eval8summa in the previous iteration (within lineSearchRefinement or trustRegionRefinement) + associate(& + err => out_SS4NR % err ,& + message => out_SS4NR % message & + &) + call initialize_computJacob_summaSolve4numrec + call computJacob(in_computJacob,indx_data,prog_data,diag_data,deriv_data,dBaseflow_dMatric,dMat,aJac,out_computJacob) + call finalize_computJacob_summaSolve4numrec + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! (check for errors) + + ! compute the numerical Jacobian matrix + if (doNumJacobian) then + globalPrintFlag=.false. + call numJacobian(stateVecTrial,dMat,nJac,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! (check for errors) + globalPrintFlag=globalPrintFlagInit + end if + end associate + end subroutine update_Jacobian + + subroutine solve_linear_system + ! *** Solve the linear system for the Newton step using LAPACK routines *** + + associate(& + nState => in_SS4NR % nState ,& ! intent(in): total number of state variables + ixMatrix => in_SS4NR % ixMatrix ,& ! intent(in): type of matrix (full or band diagonal) + err => out_SS4NR % err ,& ! intent(out): error code + message => out_SS4NR % message & ! intent(out): error message + &) + ! test the band diagonal matrix + if (testBandDiagonal) then + call testBandMat(check=.true.,err=err,message=cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! (check for errors) + end if + + ! scale the residual vector + rVecScaled(1:nState) = fScale(:)*real(rVec(:), rkind) ! NOTE: residual vector is in quadruple precision + + ! scale matrices + call scaleMatrices(ixMatrix,nState,aJac,fScale,xScale,aJacScaled,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors + + ! debug statement for scaled Jacobian + if (globalPrintFlag .and. ixMatrix==ixBandMatrix) then + print*, '** SCALED banded analytical Jacobian:' + write(*,'(a4,1x,100(i17,1x))') 'xCol', (iLayer, iLayer=iJac1,iJac2) + do iLayer=kl+1,nBands + write(*,'(i4,1x,100(e17.10,1x))') iLayer, (aJacScaled(iLayer,jLayer),jLayer=min(iJac1,nState),min(iJac2,nState)) + end do + end if + + ! copy the scaled matrix, since it is decomposed in lapackSolv + aJacScaledTemp = aJacScaled + + ! compute the newton step: use the lapack routines to solve the linear system A.X=B + call lapackSolv(ixMatrix,nState,aJacScaledTemp,-rVecScaled,newtStepScaled,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors + + ! debug statement for Newton step + if (globalPrintFlag) write(*,'(a,1x,10(e17.10,1x))') 'newtStepScaled = ', newtStepScaled(min(iJac1,nState):min(iJac2,nState)) - contains + end associate + end subroutine solve_linear_system + + subroutine refine_Newton_step + ! *** Refine the Newton step if necessary *** + + ! initialize the flag for step refinement + doRefine=.true. + + ! * case 1: state vector + ! compute the flux vector and the residual, and (if necessary) refine the iteration increment + ! NOTE: in 99.9% of cases newtStep will be used (no refinement) + + associate(& + fOld => in_SS4NR % fOld ,& + fnew => out_SS4NR % fnew ,& + converged => out_SS4NR % converged,& + err => out_SS4NR % err ,& + message => out_SS4NR % message & + &) + if (size(stateVecTrial)>1) then + + ! try to backtrack + select case(ixStepRefinement) + case(ixLineSearch) + call in_LSR % initialize(doRefine,fOld) + call lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_LSR) + call out_LSR % finalize(fNew,converged,err,cmessage) + case(ixTrustRegion) + call in_TRR % initialize(doRefine,fOld) + call trustRegionRefinement(in_TRR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_TRR) + call out_TRR % finalize(fNew,converged,err,cmessage) + case default; err=20; message=trim(message)//'unable to identify numerical solution'; return_flag=.true.; return + end select + + ! check warnings: negative error code = warning; in this case back-tracked to the original value + ! NOTE: Accept the full newton step if back-tracked to the original value + if (err<0) then + doRefine=.false.; + call in_LSR % initialize(doRefine,fOld) + call lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_LSR) + call out_LSR % finalize(fNew,converged,err,cmessage) + end if + + ! * case 2: scalar + else + call safeRootfinder(stateVecTrial,rVecScaled,newtStepScaled,stateVecNew,fluxVecNew,resVecNew,out_SRF) + call out_SRF % finalize(fNew,converged,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors + end if + end associate + end subroutine refine_Newton_step ! ********************************************************************************************************* ! * internal subroutine lineSearchRefinement: refine the iteration increment using line searches ! ********************************************************************************************************* - subroutine lineSearchRefinement(doLineSearch,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,message) + subroutine lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_LSR) ! provide access to the matrix routines USE matrixOper_module, only: computeGradient implicit none ! input - logical(lgt),intent(in) :: doLineSearch ! flag to do the line search + type(in_type_lineSearchRefinement),intent(in) :: in_LSR ! class object for intent(in) arguments real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector real(rkind),intent(in) :: newtStepScaled(:) ! scaled newton step real(rkind),intent(in) :: aJacScaled(:,:) ! scaled jacobian matrix real(rkind),intent(in) :: rVecScaled(:) ! scaled residual vector - real(rkind),intent(in) :: fOld ! old function value ! output real(rkind),intent(out) :: stateVecNew(:) ! new state vector real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector - real(rkind),intent(out) :: fNew ! new function evaluation - logical(lgt),intent(out) :: converged ! convergence flag - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + type(out_type_lineSearchRefinement),intent(out) :: out_LSR ! class object for intent(out) arguments ! -------------------------------------------------------------------------------------------------------- ! local - character(len=256) :: cmessage ! error message of downwind routine - real(rkind) :: gradScaled(nState) ! scaled gradient - real(rkind) :: xInc(nState) ! iteration increment (re-scaled to original units of the state vector) - logical(lgt) :: feasible ! flag to denote the feasibility of the solution - integer(i4b) :: iLine ! line search index - integer(i4b),parameter :: maxLineSearch=5 ! maximum number of backtracks - real(rkind),parameter :: alpha=1.e-4_rkind ! check on gradient - real(rkind) :: xLambda ! backtrack magnitude - real(rkind) :: xLambdaTemp ! temporary backtrack magnitude - real(rkind) :: slopeInit ! initial slope - real(rkind) :: rhs1,rhs2 ! rhs used to compute the cubic - real(rkind) :: aCoef,bCoef ! coefficients in the cubic - real(rkind) :: disc ! temporary variable used in cubic - real(rkind) :: xLambdaPrev ! previous lambda value (used in the cubic) - real(rkind) :: fPrev ! previous function evaluation (used in the cubic) + character(len=256) :: cmessage ! error message of downwind routine + real(rkind) :: gradScaled(in_SS4NR % nState) ! scaled gradient + real(rkind) :: xInc(in_SS4NR % nState) ! iteration increment (re-scaled to original units of the state vector) + logical(lgt) :: feasible ! flag to denote the feasibility of the solution + integer(i4b) :: iLine ! line search index + integer(i4b),parameter :: maxLineSearch=5 ! maximum number of backtracks + real(rkind),parameter :: alpha=1.e-4_rkind ! check on gradient + real(rkind) :: xLambda ! backtrack magnitude + real(rkind) :: xLambdaTemp ! temporary backtrack magnitude + real(rkind) :: slopeInit ! initial slope + real(rkind) :: rhs1,rhs2 ! rhs used to compute the cubic + real(rkind) :: aCoef,bCoef ! coefficients in the cubic + real(rkind) :: disc ! temporary variable used in cubic + real(rkind) :: xLambdaPrev ! previous lambda value (used in the cubic) + real(rkind) :: fPrev ! previous function evaluation (used in the cubic) ! -------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='lineSearchRefinement/' - converged =.false. - - ! check the need to compute the line search - if(doLineSearch)then + associate(& + ! intent(in) variables + doLineSearch => in_LSR % doSearch ,& ! flag to do the line search + fOld => in_LSR % fOld ,& ! old function value + ! local variables + nSnow => in_SS4NR % nSnow ,& ! number of snow layers + nSoil => in_SS4NR % nSoil ,& ! number of soil layers + nState => in_SS4NR % nState ,& ! total number of state variables + ixMatrix => in_SS4NR % ixMatrix ,& ! type of matrix (full or band diagonal) + ! intent(out) variables + fNew => out_LSR % fNew ,& ! new function evaluation + converged => out_LSR % converged ,& ! convergence flag + err => out_LSR % err ,& ! error code + message => out_LSR % message & ! error message + &) + ! initialize error control + err=0; message='lineSearchRefinement/' + converged =.false. + + ! check the need to compute the line search + if(doLineSearch)then + + ! compute the gradient of the function vector + call computeGradient(ixMatrix,nState,aJacScaled,rVecScaled,gradScaled,err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! compute the gradient of the function vector - call computeGradient(ixMatrix,nState,aJacScaled,rVecScaled,gradScaled,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + ! compute the initial slope + slopeInit = dot_product(gradScaled,newtStepScaled) - ! compute the initial slope - slopeInit = dot_product(gradScaled,newtStepScaled) + end if ! if computing the line search - end if ! if computing the line search + ! initialize lambda + xLambda=1._rkind - ! initialize lambda - xLambda=1._rkind + ! ***** LINE SEARCH LOOP... + lineSearch: do iLine=1,maxLineSearch ! try to refine the function by shrinking the step size - ! ***** LINE SEARCH LOOP... - lineSearch: do iLine=1,maxLineSearch ! try to refine the function by shrinking the step size + ! back-track along the search direction + ! NOTE: start with back-tracking the scaled step + xInc(:) = xLambda*newtStepScaled(:) - ! back-track along the search direction - ! NOTE: start with back-tracking the scaled step - xInc(:) = xLambda*newtStepScaled(:) + ! re-scale the iteration increment + xInc(:) = xInc(:)*xScale(:) - ! re-scale the iteration increment - xInc(:) = xInc(:)*xScale(:) + ! if enthalpy, then need to convert the iteration increment to temperature + !if(nrgFormulation==ix_enthalpy) xInc(ixNrgOnly) = xInc(ixNrgOnly)/dMat(ixNrgOnly) - ! if enthalpy, then need to convert the iteration increment to temperature - !if(nrgFormulation==ix_enthalpy) xInc(ixNrgOnly) = xInc(ixNrgOnly)/dMat(ixNrgOnly) + ! state vector with proposed iteration increment + stateVecNew = stateVecTrial + xInc - ! state vector with proposed iteration increment - stateVecNew = stateVecTrial + xInc + ! impose solution constraints adjusting state vector and iteration increment + ! NOTE: We may not need to do this (or at least, do ALL of this), as we can probably rely on the line search here + call imposeConstraints(model_decisions,indx_data,prog_data,mpar_data,stateVecNew,stateVecTrial,nState,nSoil,nSnow,cmessage,err) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + xInc = stateVecNew - stateVecTrial - ! impose solution constraints adjusting state vector and iteration increment - ! NOTE: We may not need to do this (or at least, do ALL of this), as we can probably rely on the line search here - call imposeConstraints(model_decisions,indx_data,prog_data,mpar_data,stateVecNew,stateVecTrial,nState,nSoil,nSnow,cmessage,err) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - xInc = stateVecNew - stateVecTrial + ! compute the residual vector and function + ! NOTE: This calls eval8summa in an internal subroutine which has access to all data + ! Hence, we only need to include the variables of interest in lineSearch + call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! compute the residual vector and function - ! NOTE: This calls eval8summa in an internal subroutine which has access to all data - ! Hence, we only need to include the variables of interest in lineSearch - call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + ! check line search + if(globalPrintFlag)then + write(*,'(a,1x,i4,1x,e17.10)' ) 'iLine, xLambda = ', iLine, xLambda + write(*,'(a,1x,10(e17.10,1x))') 'fOld,fNew = ', fOld,fNew + write(*,'(a,1x,10(e17.10,1x))') 'fold + alpha*slopeInit*xLambda = ', fold + alpha*slopeInit*xLambda + write(*,'(a,1x,10(e17.10,1x))') 'resVecNew = ', resVecNew(min(iJac1,nState):min(iJac2,nState)) + write(*,'(a,1x,10(e17.10,1x))') 'xInc = ', xInc(min(iJac1,nState):min(iJac2,nState)) + end if - ! check line search - if(globalPrintFlag)then - write(*,'(a,1x,i4,1x,e17.10)' ) 'iLine, xLambda = ', iLine, xLambda - write(*,'(a,1x,10(e17.10,1x))') 'fOld,fNew = ', fOld,fNew - write(*,'(a,1x,10(e17.10,1x))') 'fold + alpha*slopeInit*xLambda = ', fold + alpha*slopeInit*xLambda - write(*,'(a,1x,10(e17.10,1x))') 'resVecNew = ', resVecNew(min(iJac1,nState):min(iJac2,nState)) - write(*,'(a,1x,10(e17.10,1x))') 'xInc = ', xInc(min(iJac1,nState):min(iJac2,nState)) - end if + ! check feasibility + if(.not.feasible) cycle ! go back and impose constraints again - ! check feasibility - if(.not.feasible) cycle ! go back and impose constraints again + ! check convergence + ! NOTE: some efficiency gains possible by scaling the full newton step outside the line search loop + converged = checkConv(resVecNew,newtStepScaled*xScale,stateVecNew) + if(converged) return - ! check convergence - ! NOTE: some efficiency gains possible by scaling the full newton step outside the line search loop - converged = checkConv(resVecNew,newtStepScaled*xScale,stateVecNew) - if(converged) return + ! early return if not computing the line search + if(.not.doLineSearch) return - ! early return if not computing the line search - if(.not.doLineSearch) return + ! check if the function is accepted + if(fNew < fold + alpha*slopeInit*xLambda) return - ! check if the function is accepted - if(fNew < fold + alpha*slopeInit*xLambda) return + ! *** + ! *** IF GET TO HERE WE BACKTRACK + ! --> all remaining code simply computes the restricted step multiplier (xLambda) - ! *** - ! *** IF GET TO HERE WE BACKTRACK - ! --> all remaining code simply computes the restricted step multiplier (xLambda) + ! first backtrack: use quadratic + if(iLine==1)then + xLambdaTemp = -slopeInit / (2._rkind*(fNew - fOld - slopeInit) ) + if(xLambdaTemp > 0.5_rkind*xLambda) xLambdaTemp = 0.5_rkind*xLambda - ! first backtrack: use quadratic - if(iLine==1)then - xLambdaTemp = -slopeInit / (2._rkind*(fNew - fOld - slopeInit) ) - if(xLambdaTemp > 0.5_rkind*xLambda) xLambdaTemp = 0.5_rkind*xLambda - - ! subsequent backtracks: use cubic - else + ! subsequent backtracks: use cubic + else - ! check that we did not back-track all the way back to the original value - if(iLine==maxLineSearch)then - message=trim(message)//'backtracked all the way back to the original value' - err=-20; return - end if + ! check that we did not back-track all the way back to the original value + if(iLine==maxLineSearch)then + message=trim(message)//'backtracked all the way back to the original value' + err=-20; return + end if - ! define rhs - rhs1 = fNew - fOld - xLambda*slopeInit - rhs2 = fPrev - fOld - xLambdaPrev*slopeInit + ! define rhs + rhs1 = fNew - fOld - xLambda*slopeInit + rhs2 = fPrev - fOld - xLambdaPrev*slopeInit - ! define coefficients - aCoef = (rhs1/(xLambda*xLambda) - rhs2/(xLambdaPrev*xLambdaPrev))/(xLambda - xLambdaPrev) - bCoef = (-xLambdaPrev*rhs1/(xLambda*xLambda) + xLambda*rhs2/(xLambdaPrev*xLambdaPrev)) / (xLambda - xLambdaPrev) + ! define coefficients + aCoef = (rhs1/(xLambda*xLambda) - rhs2/(xLambdaPrev*xLambdaPrev))/(xLambda - xLambdaPrev) + bCoef = (-xLambdaPrev*rhs1/(xLambda*xLambda) + xLambda*rhs2/(xLambdaPrev*xLambdaPrev)) / (xLambda - xLambdaPrev) - ! check if a quadratic - if(aCoef==0._rkind)then - xLambdaTemp = -slopeInit/(2._rkind*bCoef) + ! check if a quadratic + if(aCoef==0._rkind)then + xLambdaTemp = -slopeInit/(2._rkind*bCoef) - ! calculate cubic - else - disc = bCoef*bCoef - 3._rkind*aCoef*slopeInit - if(disc < 0._rkind)then - xLambdaTemp = 0.5_rkind*xLambda + ! calculate cubic else - xLambdaTemp = (-bCoef + sqrt(disc))/(3._rkind*aCoef) - end if - end if ! calculating cubic + disc = bCoef*bCoef - 3._rkind*aCoef*slopeInit + if(disc < 0._rkind)then + xLambdaTemp = 0.5_rkind*xLambda + else + xLambdaTemp = (-bCoef + sqrt(disc))/(3._rkind*aCoef) + end if + end if ! calculating cubic - ! constrain to <= 0.5*xLambda - if(xLambdaTemp > 0.5_rkind*xLambda) xLambdaTemp=0.5_rkind*xLambda + ! constrain to <= 0.5*xLambda + if(xLambdaTemp > 0.5_rkind*xLambda) xLambdaTemp=0.5_rkind*xLambda - end if ! subsequent backtracks + end if ! subsequent backtracks - ! save results - xLambdaPrev = xLambda - fPrev = fNew + ! save results + xLambdaPrev = xLambda + fPrev = fNew - ! constrain lambda - xLambda = max(xLambdaTemp, 0.1_rkind*xLambda) + ! constrain lambda + xLambda = max(xLambdaTemp, 0.1_rkind*xLambda) - end do lineSearch ! backtrack loop + end do lineSearch ! backtrack loop + end associate end subroutine lineSearchRefinement @@ -507,65 +562,75 @@ end subroutine lineSearchRefinement ! ********************************************************************************************************* ! * internal subroutine trustRegionRefinement: refine the iteration increment using trust regions ! ********************************************************************************************************* - subroutine trustRegionRefinement(doTrustRefinement,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,fOld,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,message) + subroutine trustRegionRefinement(in_TRR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_TRR) ! provide access to the matrix routines USE matrixOper_module, only: lapackSolv USE matrixOper_module, only: computeGradient implicit none ! input - logical(lgt),intent(in) :: doTrustRefinement ! flag to refine using trust regions - real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector - real(rkind),intent(in) :: newtStepScaled(:) ! scaled newton step - real(rkind),intent(in) :: aJacScaled(:,:) ! scaled jacobian matrix - real(rkind),intent(in) :: rVecScaled(:) ! scaled residual vector - real(rkind),intent(in) :: fOld ! old function value + type(in_type_lineSearchRefinement),intent(in) :: in_TRR ! object for scalar intent(in) arguments -- reusing line search class + real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector + real(rkind),intent(in) :: newtStepScaled(:) ! scaled newton step + real(rkind),intent(in) :: aJacScaled(:,:) ! scaled jacobian matrix + real(rkind),intent(in) :: rVecScaled(:) ! scaled residual vector ! output - real(rkind),intent(out) :: stateVecNew(:) ! new state vector - real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector - real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector - real(rkind),intent(out) :: fNew ! new function evaluation - logical(lgt),intent(out) :: converged ! convergence flag - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + real(rkind),intent(out) :: stateVecNew(:) ! new state vector + real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector + real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector + type(out_type_lineSearchRefinement) :: out_TRR ! object for scalar intent(in) arguments -- reusing line search class ! -------------------------------------------------------------------------------------------------------- ! local variables ! .. needed .. ! -------------------------------------------------------------------------------------------------------- - err=0; message='trustRegionRefinement/' - converged =.false. - - ! check the need to refine the step - if(doTrustRefinement)then + associate(& + ! input + doTrustRefinement => in_TRR % doSearch ,& ! flag to refine using trust regions + fOld => in_TRR % fOld ,& ! old function value + nState => in_SS4NR % nState ,& ! total number of state variables + ! output + fNew => out_TRR % fNew ,& ! new function evaluation + converged => out_TRR % converged ,& ! convergence flag + err => out_TRR % err ,& ! error code + message => out_TRR % message & ! error message + &) + + err=0; message='trustRegionRefinement/' + converged =.false. + + ! check the need to refine the step + if (doTrustRefinement) then + + ! (check vectors) + if (size(stateVecTrial)/=nState .or. size(newtStepScaled)/=nState .or. size(rVecScaled)/=nState)then + message=trim(message)//'unexpected size of input vectors' + err=20; return + end if - ! (check vectors) - if(size(stateVecTrial)/=nState .or. size(newtStepScaled)/=nState .or. size(rVecScaled)/=nState)then - message=trim(message)//'unexpected size of input vectors' - err=20; return - endif + ! (check matrix) + if (size(aJacScaled,1)/=nState .or. size(aJacScaled,2)/=nState) then + message=trim(message)//'unexpected size of Jacobian matrix' + err=20; return + end if - ! (check matrix) - if(size(aJacScaled,1)/=nState .or. size(aJacScaled,2)/=nState)then - message=trim(message)//'unexpected size of Jacobian matrix' - err=20; return - endif + ! dummy check for the function + if (fold==realMissing) print*, 'missing' - ! dummy check for the function - if(fold==realMissing) print*, 'missing' + ! dummy + stateVecNew = realMissing + fluxVecNew = realMissing + resVecNew = quadMissing + fNew = realMissing + converged = .true. - ! dummy - stateVecNew = realMissing - fluxVecNew = realMissing - resVecNew = quadMissing - fNew = realMissing - converged = .true. + end if ! if doing the trust region refinement - endif ! if doing the trust region refinement + message=trim(message)//'routine not implemented yet' + err=20; return - message=trim(message)//'routine not implemented yet' - err=20; return + end associate end subroutine trustRegionRefinement @@ -573,7 +638,7 @@ end subroutine trustRegionRefinement ! ********************************************************************************************************* ! * internal subroutine safeRootfinder: refine the 1-d iteration increment using brackets ! ********************************************************************************************************* - subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fluxVecNew,resVecNew,fNew,converged,err,message) + subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fluxVecNew,resVecNew,out_SRF) USE,intrinsic :: ieee_arithmetic,only:ieee_is_nan ! IEEE arithmetic (check NaN) USE globalData,only:dNaN ! double precision NaN implicit none @@ -585,95 +650,107 @@ subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fl real(rkind),intent(out) :: stateVecNew(:) ! new state vector real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector - real(rkind),intent(out) :: fNew ! new function evaluation - logical(lgt),intent(out) :: converged ! convergence flag - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + type(out_type_lineSearchRefinement),intent(out) :: out_SRF ! object for scalar intent(out) arguments (reusing lineSearchRefinement class) ! -------------------------------------------------------------------------------------------------------- ! local variables character(len=256) :: cmessage ! error message of downwind routine real(rkind),parameter :: relTolerance=0.005_rkind ! force bi-section if trial is slightly larger than (smaller than) xmin (xmax) real(rkind) :: xTolerance ! relTolerance*(xmax-xmin) - real(rkind) :: xInc(nState) ! iteration increment (re-scaled to original units of the state vector) - real(rkind) :: rVec(nState) ! residual vector (re-scaled to original units of the state equation) + real(rkind) :: xInc(in_SS4NR % nState) ! iteration increment (re-scaled to original units of the state vector) + real(rkind) :: rVec(in_SS4NR % nState) ! residual vector (re-scaled to original units of the state equation) logical(lgt) :: feasible ! feasibility of the solution logical(lgt) :: doBisection ! flag to do the bi-section logical(lgt) :: bracketsDefined ! flag to define if the brackets are defined integer(i4b),parameter :: nCheck=100 ! number of times to check the model state variables real(rkind),parameter :: delX=1._rkind ! trial increment ! -------------------------------------------------------------------------------------------------------- - err=0; message='safeRootfinder/' - converged = .false. - - ! check scalar - if(size(stateVecTrial)/=1 .or. size(rVecScaled)/=1 .or. size(newtStepScaled)/=1)then - message=trim(message)//'unexpected size of input vectors' - err=20; return - endif - - ! initialize brackets to rkind precision NaN - if(iter==1)then - xMax = dNaN - xMin = dNaN - endif + associate(& + iter => in_SS4NR % iter ,& ! intent(in): iteration index + nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers + nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers + nState => in_SS4NR % nState ,& ! intent(in): total number of state + xMin => io_SS4NR % xMin ,& ! intent(inout): bracket of the root + xMax => io_SS4NR % xMax ,& ! intent(inout): bracket of the root + fNew => out_SRF % fNew ,& ! intent(out): new function evaluation + converged => out_SRF % converged ,& ! intent(out): convergence flag + err => out_SRF % err ,& ! intent(out): error code + message => out_SRF % message & ! intent(out): error message + &) + + err=0; message='safeRootfinder/' + converged = .false. + + ! check scalar + if (size(stateVecTrial)/=1 .or. size(rVecScaled)/=1 .or. size(newtStepScaled)/=1) then + message=trim(message)//'unexpected size of input vectors' + err=20; return + end if - ! get the residual vector - rVec = real(rVecScaled, rkind)*real(fScale, rkind) + ! initialize brackets to rkind precision NaN + if (iter==1) then + xMax = dNaN + xMin = dNaN + end if - ! update brackets - if(rVec(1)<0._rkind)then - xMin = stateVecTrial(1) - else - xMax = stateVecTrial(1) - endif + ! get the residual vector + rVec = real(rVecScaled, rkind)*real(fScale, rkind) - ! get the iteration increment - xInc = newtStepScaled*xScale + ! update brackets + if (rVec(1)<0._rkind) then + xMin = stateVecTrial(1) + else + xMax = stateVecTrial(1) + end if - ! ***** - ! * case 1: the iteration increment is the same sign as the residual vector - if(xInc(1)*rVec(1) > 0._rkind)then + ! get the iteration increment + xInc = newtStepScaled*xScale - ! get brackets if they do not exist - if( ieee_is_nan(xMin) .or. ieee_is_nan(xMax) )then - call getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - endif + ! ***** + ! * case 1: the iteration increment is the same sign as the residual vector + if (xInc(1)*rVec(1) > 0._rkind) then - ! use bi-section - stateVecNew(1) = 0.5_rkind*(xMin + xMax) + ! get brackets if they do not exist + if ( ieee_is_nan(xMin) .or. ieee_is_nan(xMax) ) then + call getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors + end if - ! ***** - ! * case 2: the iteration increment is the correct sign - else + ! use bi-section + stateVecNew(1) = 0.5_rkind*(xMin + xMax) - ! state vector with proposed iteration increment - stateVecNew = stateVecTrial + xInc - - ! impose solution constraints adjusting state vector and iteration increment - call imposeConstraints(model_decisions,indx_data,prog_data,mpar_data,stateVecNew,stateVecTrial,nState,nSoil,nSnow,cmessage,err) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - xInc = stateVecNew - stateVecTrial + ! ***** + ! * case 2: the iteration increment is the correct sign + else - endif ! if the iteration increment is the same sign as the residual vector + ! state vector with proposed iteration increment + stateVecNew = stateVecTrial + xInc + + ! impose solution constraints adjusting state vector and iteration increment + call imposeConstraints(model_decisions,indx_data,prog_data,mpar_data,stateVecNew,stateVecTrial,nState,nSoil,nSnow,cmessage,err) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors + xInc = stateVecNew - stateVecTrial + + end if ! if the iteration increment is the same sign as the residual vector + + ! bi-section + bracketsDefined = ( .not.ieee_is_nan(xMin) .and. .not.ieee_is_nan(xMax) ) ! check that the brackets are defined + if (bracketsDefined) then + xTolerance = relTolerance*(xMax-xMin) + doBisection = (stateVecNew(1)xMax-xTolerance) + if (doBisection) stateVecNew(1) = 0.5_rkind*(xMin+xMax) + end if - ! bi-section - bracketsDefined = ( .not.ieee_is_nan(xMin) .and. .not.ieee_is_nan(xMax) ) ! check that the brackets are defined - if(bracketsDefined)then - xTolerance = relTolerance*(xMax-xMin) - doBisection = (stateVecNew(1)xMax-xTolerance) - if(doBisection) stateVecNew(1) = 0.5_rkind*(xMin+xMax) - endif + ! evaluate summa + call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors - ! evaluate summa - call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + ! check feasibility (should be feasible because of the call to imposeConstraints, except if canopyTemp>canopyTempMax (500._rkind)) + if (.not.feasible) then; err=20; message=trim(message)//'state vector not feasible'; return; end if - ! check feasibility (should be feasible because of the call to imposeConstraints, except if canopyTemp>canopyTempMax (500._rkind)) - if(.not.feasible)then; err=20; message=trim(message)//'state vector not feasible'; return; endif + ! check convergence + converged = checkConv(resVecNew,xInc,stateVecNew) - ! check convergence - converged = checkConv(resVecNew,xInc,stateVecNew) + end associate end subroutine safeRootfinder @@ -690,12 +767,12 @@ subroutine getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,message) integer(i4b),intent(inout) :: err ! error code character(*),intent(out) :: message ! error message ! locals - real(rkind) :: stateVecPrev(nState) ! iteration state vector - integer(i4b) :: iCheck ! check the model state variables - integer(i4b),parameter :: nCheck=100 ! number of times to check the model state variables - logical(lgt) :: feasible ! feasibility of the solution - real(rkind),parameter :: delX=1._rkind ! trial increment - real(rkind) :: xIncrement(nState) ! trial increment + real(rkind) :: stateVecPrev(in_SS4NR % nState) ! iteration state vector + integer(i4b) :: iCheck ! check the model state variables + integer(i4b),parameter :: nCheck=100 ! number of times to check the model state variables + logical(lgt) :: feasible ! feasibility of the solution + real(rkind),parameter :: delX=1._rkind ! trial increment + real(rkind) :: xIncrement(in_SS4NR % nState) ! trial increment ! initialize err=0; message='getBrackets/' @@ -713,12 +790,20 @@ subroutine getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,message) stateVecNew = stateVecPrev + xIncrement ! impose solution constraints adjusting state vector and iteration increment - call imposeConstraints(model_decisions,indx_data,prog_data,mpar_data,stateVecNew,stateVecPrev,nState,nSoil,nSnow,cmessage,err) + associate(& + nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers + nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers + nState => in_SS4NR % nState & ! intent(in): total number of state variables + &) + call imposeConstraints(model_decisions,indx_data,prog_data,mpar_data,stateVecNew,stateVecPrev,nState,nSoil,nSnow,cmessage,err) + end associate if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) xIncrement = stateVecNew - stateVecPrev ! evaluate summa - call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) + associate(fnew => out_SS4NR % fnew) + call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) + end associate if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! check feasibility (should be feasible because of the call to imposeConstraints, except if canopyTemp>canopyTempMax (500._rkind)) @@ -764,9 +849,9 @@ subroutine numJacobian(stateVec,dMat,nJac,err,message) ! local character(len=256) :: cmessage ! error message of downwind routine real(rkind),parameter :: dx=1.e-8_rkind ! finite difference increment - real(rkind),dimension(nState) :: stateVecPerturbed ! perturbed state vector - real(rkind),dimension(nState) :: fluxVecInit,fluxVecJac ! flux vector (mized units) - real(qp),dimension(nState) :: resVecInit,resVecJac ! qp ! residual vector (mixed units) + real(rkind),dimension(in_SS4NR % nState) :: stateVecPerturbed ! perturbed state vector + real(rkind),dimension(in_SS4NR % nState) :: fluxVecInit,fluxVecJac ! flux vector (mized units) + real(qp),dimension(in_SS4NR % nState) :: resVecInit,resVecJac ! qp ! residual vector (mixed units) real(rkind) :: func ! function value logical(lgt) :: feasible ! flag to denote the feasibility of the solution integer(i4b) :: iJac ! index of row of the Jacobian matrix @@ -786,7 +871,7 @@ subroutine numJacobian(stateVec,dMat,nJac,err,message) stateVecPerturbed(:) = stateVec(:) ! loop through state variables - do iJac=1,nState + do iJac=1,in_SS4NR % nState !print*, 'iJac = ', iJac !globalPrintFlag = merge(.true.,.false., iJac==1) @@ -803,7 +888,7 @@ subroutine numJacobian(stateVec,dMat,nJac,err,message) ! compute the row of the Jacobian matrix select case(ixNumType) case(ixNumRes); nJac(:,iJac) = real(resVecJac - resVecInit, kind(rkind) )/dx ! Jacobian based on residuals - case(ixNumFlux); nJac(:,iJac) = -dt_cur*(fluxVecJac(:) - fluxVecInit(:))/dx ! Jacobian based on fluxes + case(ixNumFlux); nJac(:,iJac) = -in_SS4NR % dt_cur*(fluxVecJac(:) - fluxVecInit(:))/dx ! Jacobian based on fluxes case default; err=20; message=trim(message)//'Jacobian option not found'; return end select @@ -817,9 +902,9 @@ subroutine numJacobian(stateVec,dMat,nJac,err,message) ! print the Jacobian print*, '** numerical Jacobian:', ixNumType==ixNumRes - write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) - do iLayer=min(iJac1,nState),min(iJac2,nState) - write(*,'(i4,1x,100(e12.5,1x))') iLayer, nJac(min(iJac1,nState):min(iJac2,nState),iLayer) + write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=min(iJac1,in_SS4NR % nState),min(iJac2,in_SS4NR % nState)) + do iLayer=min(iJac1,in_SS4NR % nState),min(iJac2,in_SS4NR % nState) + write(*,'(i4,1x,100(e12.5,1x))') iLayer, nJac(min(iJac1,in_SS4NR % nState):min(iJac2,in_SS4NR % nState),iLayer) end do !print*, 'PAUSE: testing Jacobian'; read(*,*) @@ -835,8 +920,8 @@ subroutine testBandMat(check,err,message) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local variables - real(rkind) :: fullJac(nState,nState) ! full Jacobian matrix - real(rkind) :: bandJac(nLeadDim,nState) ! band Jacobian matrix + real(rkind) :: fullJac(in_SS4NR % nState,in_SS4NR % nState) ! full Jacobian matrix + real(rkind) :: bandJac(in_SS4NR % nLeadDim,in_SS4NR % nState) ! band Jacobian matrix integer(i4b) :: iState,jState ! indices of the state vector character(LEN=256) :: cmessage ! error message of downwind routine ! class objects for subroutine arguments @@ -846,24 +931,24 @@ subroutine testBandMat(check,err,message) err=0; message='testBandMat/' ! check - if(nLeadDim==nState)then + if (in_SS4NR % nLeadDim == in_SS4NR % nState) then message=trim(message)//'do not expect nLeadDim==nState: check that are computing the band diagonal matrix'//& ' (is forceFullMatrix==.true.?)' err=20; return - endif + end if ! compute the full Jacobian matrix call initialize_computJacob_testBandMat call computJacob(in_computJacob,indx_data,prog_data,diag_data,deriv_data,dBaseflow_dMatric,dMat,fullJac,out_computJacob) call finalize_computJacob_testBandMat(err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! initialize band matrix bandJac(:,:) = 0._rkind ! transfer into the lapack band diagonal structure - do iState=1,nState - do jState=max(1,iState-ku),min(nState,iState+kl) + do iState=1,in_SS4NR % nState + do jState=max(1,iState-ku),min(in_SS4NR % nState,iState+kl) bandJac(kl + ku + 1 + jState - iState, iState) = fullJac(jState,iState) end do end do @@ -871,7 +956,7 @@ subroutine testBandMat(check,err,message) ! print results print*, '** test banded analytical Jacobian:' write(*,'(a4,1x,100(i17,1x))') 'xCol', (iState, iState=iJac1,iJac2) - do iState=kl+1,nLeadDim; write(*,'(i4,1x,100(e17.10,1x))') iState, bandJac(iState,iJac1:iJac2); end do + do iState=kl+1,in_SS4NR % nLeadDim; write(*,'(i4,1x,100(e17.10,1x))') iState, bandJac(iState,iJac1:iJac2); end do ! check if the need to pause if(check)then @@ -883,7 +968,15 @@ end subroutine testBandMat subroutine initialize_computJacob_testBandMat ! *** Transfer data from out_computJacob class object to local variables in testBandMat *** - call in_computJacob % initialize(dt_cur,nSnow,nSoil,nLayers,computeVegFlux,.false.,ixFullMatrix) + associate(& + dt_cur => in_SS4NR % dt_cur ,& ! intent(in): current stepsize + nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers + nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers + nLayers => in_SS4NR % nLayers ,& ! intent(in): total number of layers + computeVegFlux => in_SS4NR % computeVegFlux & ! intent(in): flag to indicate if computing fluxes over vegetation + &) + call in_computJacob % initialize(dt_cur,nSnow,nSoil,nLayers,computeVegFlux,.false.,ixFullMatrix) + end associate end subroutine initialize_computJacob_testBandMat subroutine finalize_computJacob_testBandMat(err,cmessage) @@ -896,14 +989,24 @@ end subroutine finalize_computJacob_testBandMat subroutine initialize_computJacob_summaSolve4numrec ! *** Transfer data to in_computJacob class object from local variables in summaSolve4numrec *** - associate(ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision) ! intent(in): [i4b] groundwater parameterization + associate(& + ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision,& ! intent(in): [i4b] groundwater parameterization + dt_cur => in_SS4NR % dt_cur ,& ! intent(in): current stepsize + nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers + nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers + nLayers => in_SS4NR % nLayers ,& ! intent(in): total number of layers + ixMatrix => in_SS4NR % ixMatrix ,& ! intent(in): type of matrix (full or band diagonal) + computeVegFlux => in_SS4NR % computeVegFlux & ! intent(in): flag to indicate if computing fluxes over vegetation + &) call in_computJacob % initialize(dt_cur,nSnow,nSoil,nLayers,computeVegFlux,(ixGroundwater==qbaseTopmodel),ixMatrix) end associate end subroutine initialize_computJacob_summaSolve4numrec subroutine finalize_computJacob_summaSolve4numrec ! *** Transfer data from out_computJacob class object to local variables in summaSolve4numrec *** - call out_computJacob % finalize(err,cmessage) + associate(err => out_SS4NR % err) + call out_computJacob % finalize(err,cmessage) + end associate end subroutine finalize_computJacob_summaSolve4numrec ! ********************************************************************************************************* @@ -930,51 +1033,66 @@ subroutine eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err ! initialize error control err=0; message='eval8summa_wrapper/' + associate(& + dt_cur => in_SS4NR % dt_cur ,& ! intent(in): current stepsize + dt => in_SS4NR % dt ,& ! intent(in): entire time step for drainage pond rate + nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers + nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers + nLayers => in_SS4NR % nLayers ,& ! intent(in): total number of layers + nState => in_SS4NR % nState ,& ! intent(in): total number of state variables + firstSubStep => in_SS4NR % firstSubStep ,& ! intent(in): flag to indicate if we are processing the first sub-step + computeVegFlux => in_SS4NR % computeVegFlux ,& ! intent(in): flag to indicate if computing fluxes over vegetation + scalarSolution => in_SS4NR % scalarSolution ,& ! intent(in): flag to denote if implementing the scalar solution + firstFluxCall => io_SS4NR % firstFluxCall ,& ! intent(inout): flag to indicate if we are processing the first flux call + ixSaturation => io_SS4NR % ixSaturation & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + &) ! compute the flux and the residual vector for a given state vector - call eval8summa(& - ! input: model control - dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - nState, & ! intent(in): total number of state variables - .false., & ! intent(in): not inside Sundials solver - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - .false., & ! intent(in): not processing the first iteration in a splitting operation - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVecNew, & ! intent(in): updated model state vector - fScale, & ! intent(in): characteristic scale of the function evaluations - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - ! output - feasible, & ! intent(out): flag to denote the feasibility of the solution - fluxVecNew, & ! intent(out): new flux vector - resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equation - resVecNew, & ! intent(out): new residual vector - fNew, & ! intent(out): new function evaluation - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + call eval8summa(& + ! input: model control + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + nState, & ! intent(in): total number of state variables + .false., & ! intent(in): not inside Sundials solver + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + .false., & ! intent(in): not processing the first iteration in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVecNew, & ! intent(in): updated model state vector + fScale, & ! intent(in): characteristic scale of the function evaluations + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + indx_data, & ! intent(inout): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + ! output + feasible, & ! intent(out): flag to denote the feasibility of the solution + fluxVecNew, & ! intent(out): new flux vector + resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equation + resVecNew, & ! intent(out): new residual vector + fNew, & ! intent(out): new function evaluation + err,cmessage) ! intent(out): error control + end associate + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + end subroutine eval8summa_wrapper @@ -1007,22 +1125,26 @@ function checkConv(rVec,xInc,xVec) ! ------------------------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures associate(& - ! convergence parameters - absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1) ,& ! intent(in): [dp] absolute convergence tolerance for vol frac liq water (-) - absConvTol_matric => mpar_data%var(iLookPARAM%absConvTol_matric)%dat(1) ,& ! intent(in): [dp] absolute convergence tolerance for matric head (m) - absConvTol_energy => mpar_data%var(iLookPARAM%absConvTol_energy)%dat(1) ,& ! intent(in): [dp] absolute convergence tolerance for energy (J m-3) - ! layer depth - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! model indices - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of aquifer storage state variable - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for all energy states - ixHydOnly => indx_data%var(iLookINDEX%ixHydOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for all hydrology states - ixMatOnly => indx_data%var(iLookINDEX%ixMatOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for matric head state variables in the state vector - ixMatricHead => indx_data%var(iLookINDEX%ixMatricHead)%dat & ! intent(in): [i4b(:)] list of indices for matric head in the soil vector - + ! model control + iter => in_SS4NR % iter ,& ! intent(in): iteration index + nsnow => in_SS4NR % nsnow ,& ! intent(in): number of snow layers + scalarSolution => in_SS4NR % scalarSolution ,& ! intent(in): flag to denote if implementing the scalar solution + ! convergence parameters + absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1),& ! intent(in): [dp] absolute convergence tolerance for vol frac liq water (-) + absConvTol_matric => mpar_data%var(iLookPARAM%absConvTol_matric)%dat(1),& ! intent(in): [dp] absolute convergence tolerance for matric head (m) + absConvTol_energy => mpar_data%var(iLookPARAM%absConvTol_energy)%dat(1),& ! intent(in): [dp] absolute convergence tolerance for energy (J m-3) + ! layer depth + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! model indices + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of aquifer storage state variable + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for all energy states + ixHydOnly => indx_data%var(iLookINDEX%ixHydOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for all hydrology states + ixMatOnly => indx_data%var(iLookINDEX%ixMatOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for matric head state variables in the state vector + ixMatricHead => indx_data%var(iLookINDEX%ixMatricHead)%dat ,& ! intent(in): [i4b(:)] list of indices for matric head in the soil vector + fnew => out_SS4NR % fnew & ! intent(in): [dp] new function evaluations ) ! making associations with variables in the data structures ! ------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 2be2dbde4..1116a580e 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -71,12 +71,15 @@ module systemSolv_module ! provide access to the derived types to define the data structures USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! lookup tables - model_options ! defines the model decisions + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! lookup tables + model_options, & ! defines the model decisions + in_type_summaSolve4numrec, & ! class for summaSolve4numrec arguments + io_type_summaSolve4numrec, & ! class for summaSolve4numrec arguments + out_type_summaSolve4numrec ! class for summaSolve4numrec arguments ! look-up values for the choice of heat capacity computation USE mDecisions_module,only: & @@ -262,229 +265,26 @@ subroutine systemSolv(& integer(i4b), parameter :: scalarMaxIter=100 ! maximum number of iterations for the scalar solution numrec logical(lgt) :: converged ! convergence flag numrec logical(lgt), parameter :: post_massCons=.false. ! “perfectly” conserve mass by pushing the errors into the states, turn off for now to agree with SUNDIALS - ! --------------------------------------------------------------------------------------- - ! point to variables in the data structures - ! --------------------------------------------------------------------------------------- + ! class objects for call to summaSolve4numrec + type(in_type_summaSolve4numrec) :: in_SS4NR ! object for intent(in) summaSolve4numrec arguments + type(io_type_summaSolve4numrec) :: io_SS4NR ! object for intent(io) summaSolve4numrec arguments + type(out_type_summaSolve4numrec) :: out_SS4NR ! object for intent(out) summaSolve4numrec arguments + ! flags + logical(lgt) :: return_flag ! flag for handling systemSolv returns trigerred from internal subroutines + logical(lgt) :: exit_flag ! flag for handling loop exit statements trigerred from internal subroutines + ! ----------------------------------------------------------------------------------------------------------- + + call initialize_systemSolv; if (return_flag) return ! initialize variables and allocate arrays -- return if error + + call initial_function_evaluations; if (return_flag) return ! initial function evaluations -- return if error + globalVars: associate(& ! model decisions ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver - ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& ! intent(in): [i4b] choice of variable in energy conservation backward Euler residual - ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization - ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) - ! enthalpy - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) - scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! intent(out): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthTemp => diag_data%var(iLookDIAG%mLayerEnthTemp)%dat ,& ! intent(out): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) - ! derivatives, diagnostic for enthalpy - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) - ! model state variables - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp] mass of ice on the vegetation canopy (kg m-2) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variables (snow and soil domains) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) - ! check the need to merge snow layers - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ! mapping from full domain to the sub-domain - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b] mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b] index of control volume for different domains (veg, snow, soil) - ! type of state and domain for a given variable - ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] type of desired model state variables - ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] domain for desired model state variables ! layer geometry nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) & ! intent(in): [i4b] number of soil layers ) - ! --------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="systemSolv/" - nSteps = 0 ! initialize number of time steps taken in solver - - ! initialize the residual parts and balances - fluxVec = 0._rkind - resSink = 0._rkind - resVec = 0._rkind - balance = 0._rkind - - ! ***** - ! (0) PRELIMINARIES... - ! ******************** - - ! check - if(dt_cur < tinyStep)then - message=trim(message)//'dt is tiny' - err=20; return - endif - - ! initialize the flags - tooMuchMelt = .false. ! too much melt - reduceCoupledStep = .false. ! need to reduce the length of the coupled step - - ! modify the groundwater representation for this single-column implementation - select case(ixSpatialGroundwater) - case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale - case(localColumn); local_ixGroundwater = ixGroundwater ! go with the specified decision - case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; return - end select ! (modify the groundwater representation for this single-column implementation) - - ! allocate space for the model fluxes at the start of the time step - call allocLocal(flux_meta(:),flux_init,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! allocate space for mLayerCmpress_sum at the start of the time step - if(ixNumericalMethod==ida)then - allocate( mLayerCmpress_sum(nSoil) ) - else - allocate( mLayerCmpress_sum(0) ) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix - end if - - ! allocate space for the baseflow derivatives - ! NOTE: needs allocation because only used when baseflow sinks are active - if(ixGroundwater==qbaseTopmodel)then - allocate(dBaseflow_dMatric(nSoil,nSoil),stat=err) ! baseflow depends on total storage in the soil column, hence on matric head in every soil layer - else - allocate(dBaseflow_dMatric(0,0),stat=err) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix - end if - if(err/=0)then; err=20; message=trim(message)//'unable to allocate space for the baseflow derivatives'; return; end if - - ! identify the matrix solution method, using the full matrix can be slow in many-layered systems - ! (the type of matrix used to solve the linear system A.X=B) - if(local_ixGroundwater==qbaseTopmodel .or. scalarSolution .or. forceFullMatrix .or. computeVegFlux)then - nLeadDim=nState ! length of the leading dimension - ixMatrix=ixFullMatrix ! named variable to denote the full Jacobian matrix - else - nLeadDim=nBands ! length of the leading dimension - ixMatrix=ixBandMatrix ! named variable to denote the band-diagonal matrix - endif - - ! initialize the model fluxes (some model fluxes are not computed in the iterations) - do iVar=1,size(flux_temp%var) - flux_init%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) - end do - - ! ----- - ! * get scaling vectors... - ! ------------------------ - - ! initialize state vectors - call getScaling(& - ! input - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output - fScale, & ! intent(out): characteristic scale of the function evaluations (mixed units) - xScale, & ! intent(out): variable scaling vector (mixed units) - sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) - dMat, & ! intent(out): diagonal of the Jacobian matrix (excludes fluxes) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - ! ----- - ! * compute the initial function evaluation... - ! -------------------------------------------- - - ! initialize the trial state vectors - stateVecTrial = stateVecInit - - if((ixNrgConserv.ne.closedForm .or. computNrgBalance) .and. ixNumericalMethod.ne.ida)then - ! will need enthalpy change, compute H_T at the beginning of the data step - call T2enthTemp(& - ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTemp, & ! intent(in): value of canopy air temperature (K) - scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) - scalarCanopyWat, & ! intent(in): value of canopy total water (kg m-2) - ! input: variables for the snow-soil domain - mLayerTemp, & ! intent(in): vector of layer temperature (K) - mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) - mLayerMatricHead, & ! intent(in): vector of total water matric potential (m) - ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthTemp, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthTemp, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - endif - - ! compute the initial flux and the residual vector, also gets values needed for the Jacobian matrix - ! (prime initial values are 0 so it's fine to run the regular eval8summa with every solver choice) - call eval8summa(& - ! input: model control - dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): number of layers - nState, & ! intent(in): number of state variables in the current subset - .false., & ! intent(in): not inside Sundials solver - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVecTrial, & ! intent(in): model state vector - fScale, & ! intent(in): characteristic scale of the function evaluations - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - ! output - feasible, & ! intent(out): flag to denote the feasibility of the solution - fluxVec0, & ! intent(out): flux vector - rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation - resVec, & ! intent(out): residual vector - fOld, & ! intent(out): function evaluation - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif - - ! copy over the initial flux structure since some model fluxes are not computed in the iterations - do concurrent ( iVar=1:size(flux_meta) ) - flux_temp%var(iVar)%dat(:) = flux_init%var(iVar)%dat(:) - end do - - ! check the need to merge snow layers - if(nSnow>0)then - ! compute the energy required to melt the top snow layer (J m-2) - bulkDensity = mLayerVolFracIce(1)*iden_ice + mLayerVolFracLiq(1)*iden_water - volEnthalpy = T2enthalpy_snow(mLayerTemp(1),bulkDensity,snowfrz_scale) - ! set flag and error codes for too much melt - if(-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_cur)then - tooMuchMelt = .true. - message=trim(message)//'net flux in the top snow layer can melt all the snow in the top layer' - err=-20; return ! negative error code to denote a warning - endif - endif ! ************************** ! * Solving the System @@ -596,7 +396,7 @@ subroutine systemSolv(& case(kinsol) !--------------------------- - ! * solving F(y) = 0 by Backward Euler with KINSOL, y is the state vector + ! * solving F(y) = 0 from Backward Euler with KINSOL, y is the state vector !--------------------------- ! iterations and updates to trial state vector, fluxes, and derivatives are done inside IDA solver call summaSolve4kinsol(& @@ -654,134 +454,349 @@ subroutine systemSolv(& #endif case(numrec) - ! define maximum number of iterations - maxiter = nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)) - - ! correct the number of iterations - localMaxIter = merge(scalarMaxIter, maxIter, scalarSolution) + call Newton_iterations_numrec; if (return_flag) return ! Newton iterations using numerical recipes -- return if error + end select - !--------------------------- - ! * solving F(y) = 0 by Backward Euler with free numerical recipes routines, y is the state vector - !--------------------------- - ! iterate and update trial state vector, fluxes, and derivatives - do iter=1,localMaxIter - ! keep track of the number of iterations - niter = iter+1 ! +1 because xFluxResid was moved outside the iteration loop (for backwards compatibility) - call summaSolve4numrec(& - ! input: model control - dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate - iter, & ! intent(in): iteration index - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - nLeadDim, & ! intent(in): length of the leading dimension of the Jacobian matrix (either nBands or nState) - nState, & ! intent(in): total number of state variables - ixMatrix, & ! intent(in): type of matrix (full or band diagonal) - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVecTrial, & ! intent(in): trial state vector - xMin,xMax, & ! intent(inout): state maximum and minimum - fScale, & ! intent(in): characteristic scale of the function evaluations - xScale, & ! intent(in): characteristic scale of the state vector - resVec, & ! intent(in): residual vector - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - dMat, & ! intent(inout): diagonal matrix (excludes flux derivatives) - fOld, & ! intent(in): old function evaluation - ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_temp, & ! intent(inout): model fluxes for a local HRU (temporary structure) - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(inout): derivative in baseflow w.r.t. matric head (s-1) - ! output - stateVecNew, & ! intent(out): new state vector - fluxVec, & ! intent(out): new flux vector - resSink, & ! intent(out): additional (sink) terms on the RHS of the state equa - resVecNew, & ! intent(out): new residual vector - fNew, & ! intent(out): new function evaluation - converged, & ! intent(out): convergence flag - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + end associate globalVars ! end associate statements - ! save the computed functions, residuals, and solution - fOld = fNew - resVec = resVecNew - stateVecTrial = stateVecNew - stateVecPrime = stateVecTrial !prime values not used here, dummy - nSteps = 1 ! number of time steps taken in solver - - ! exit iteration loop if converged - if(converged) exit - - ! check convergence - if(iter==localMaxiter)then - message=trim(message)//'failed to converge' - err=-20; return - endif - - end do ! iterating - - ! ----- - ! * update states... - ! Post processing step to “perfectly” conserve mass by pushing the errors into the state variables - ! NOTE: if the residual is large this will cause the state variables to be pushed outside of their bounds - ! ------------------ - if (post_massCons)then - layerVars: associate(& - ! vector of energy and hydrology indices for the snow and soil domains - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) & ! intent(in): [i4b] number of hydrology state variables in the snow+soil domain - ) - - ! update temperatures (ensure new temperature is consistent with the fluxes) - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - iState = ixSnowSoilNrg(iLayer) - stateVecTrial(iState) = stateVecInit(iState) + (fluxVec(iState)*dt_cur + resSink(iState))/real(sMul(iState), rkind) - resVec(iState) = 0._qp - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! update volumetric water content in the snow (ensure change in state is consistent with the fluxes) - ! NOTE: for soil water balance is constrained within the iteration loop - if(nSnowSoilHyd>0)then - do concurrent (iLayer=1:nSnow,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing water state variables in the snow domain) - iState = ixSnowSoilHyd(iLayer) - stateVecTrial(iState) = stateVecInit(iState) + (fluxVec(iState)*dt_cur + resSink(iState)) - resVec(iState) = 0._qp - end do ! looping through non-missing water state variables in the soil domain - endif - end associate layerVars - endif - end select + call finalize_systemSolv ! set untapped melt to zero and deallocate arrays - ! set untapped melt energy to zero - untappedMelt(:) = 0._rkind +contains - ! ************************** - ! free memory - deallocate(mLayerCmpress_sum) - deallocate(dBaseflow_dMatric) + subroutine initialize_systemSolv + ! *** Initial setup operations for the systemSolv subroutine *** + + ! initialize error control + err=0; message="systemSolv/" + return_flag=.false. ! initialize return flag + nSteps = 0 ! initialize number of time steps taken in solver + + ! check time step size + if (dt_cur < tinyStep) then + message=trim(message)//'dt is tiny' + err=20; return_flag=.true.; return + end if - ! end associate statements - end associate globalVars + ! initialize the flags + tooMuchMelt = .false. ! too much melt + reduceCoupledStep = .false. ! need to reduce the length of the coupled step + + associate(& + ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) + ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision & ! intent(in): [i4b] groundwater parameterization + &) + + ! modify the groundwater representation for this single-column implementation + select case(ixSpatialGroundwater) + case(singleBasin); local_ixGroundwater = noExplicit ! force no explicit representation of groundwater at the local scale + case(localColumn); local_ixGroundwater = ixGroundwater ! go with the specified decision + case default; err=20; message=trim(message)//'unable to identify spatial representation of groundwater'; + return_flag=.true.; return + end select + + call allocate_memory; if (return_flag) return + + ! identify the matrix solution method, using the full matrix can be slow in many-layered systems + ! (the type of matrix used to solve the linear system A.X=B) + if (local_ixGroundwater==qbaseTopmodel .or. scalarSolution .or. forceFullMatrix .or. computeVegFlux) then + nLeadDim=nState ! length of the leading dimension + ixMatrix=ixFullMatrix ! named variable to denote the full Jacobian matrix + else + nLeadDim=nBands ! length of the leading dimension + ixMatrix=ixBandMatrix ! named variable to denote the band-diagonal matrix + end if + end associate + + ! initialize the model fluxes (some model fluxes are not computed in the iterations) + do iVar=1,size(flux_temp%var) + flux_init%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) + end do + + ! initialize state vectors -- get scaling vectors + call getScaling(diag_data,indx_data,fScale,xScale,sMul,dMat,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors + end subroutine initialize_systemSolv + + subroutine allocate_memory + ! ** Allocate arrays used in systemSolv subroutine ** + associate(& + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision,& ! intent(in): [i4b] choice of numerical solver + ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision & ! intent(in): [i4b] groundwater parameterization + &) + ! allocate space for the model fluxes at the start of the time step + call allocLocal(flux_meta(:),flux_init,nSnow,nSoil,err,cmessage) + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if + + ! allocate space for mLayerCmpress_sum at the start of the time step + if (ixNumericalMethod==ida) then + allocate( mLayerCmpress_sum(nSoil) ) + else + allocate( mLayerCmpress_sum(0) ) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix + end if + + ! allocate space for the baseflow derivatives + ! NOTE: needs allocation because only used when baseflow sinks are active + if (ixGroundwater==qbaseTopmodel) then + allocate(dBaseflow_dMatric(nSoil,nSoil),stat=err) ! baseflow depends on total storage in the soil column, hence on matric head in every soil layer + else + allocate(dBaseflow_dMatric(0,0),stat=err) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix + end if + if (err/=0) then; err=20; message=trim(message)//'unable to allocate space for the baseflow derivatives'; return_flag=.true.; return; end if + end associate + + end subroutine allocate_memory + + subroutine initial_function_evaluations + ! ** Compute initial function evaluations ** + + ! initialize the trial state vectors + stateVecTrial = stateVecInit + + associate(& + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision,& ! intent(in): [i4b] choice of numerical solver + ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision & ! intent(in): [i4b] choice of variable in energy conservation backward Euler residual + &) + if ((ixNrgConserv.ne.closedForm .or. computNrgBalance) .and. ixNumericalMethod.ne.ida) then + call enthalpy_function_evaluations; if (return_flag) return + end if + end associate + + ! compute the initial flux and the residual vector, also gets values needed for the Jacobian matrix + call initial_flux_and_residual_vectors; if (return_flag) return + + if (.not.feasible) then; message=trim(message)//'state vector not feasible'; err=20; return_flag=.true.; return; end if + + ! copy over the initial flux structure since some model fluxes are not computed in the iterations + do concurrent ( iVar=1:size(flux_meta) ) + flux_temp%var(iVar)%dat(:) = flux_init%var(iVar)%dat(:) + end do + + ! check the need to merge snow layers + associate(& + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + &) + ! check the need to merge snow layers + if (nSnow>0) then + ! compute the energy required to melt the top snow layer (J m-2) + bulkDensity = mLayerVolFracIce(1)*iden_ice + mLayerVolFracLiq(1)*iden_water + volEnthalpy = T2enthalpy_snow(mLayerTemp(1),bulkDensity,snowfrz_scale) + ! set flag and error codes for too much melt + if (-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_cur) then + tooMuchMelt = .true. + message=trim(message)//'net flux in the top snow layer can melt all the snow in the top layer' + err=-20; return ! negative error code to denote a warning + end if + end if + end associate + end subroutine initial_function_evaluations + + subroutine enthalpy_function_evaluations + ! ** Compute H_T at the beginning of the data step without phase change ** + associate(& + ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision,& ! intent(in): [i4b] choice of variable in energy conservation backward Euler residual + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1),& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1),& ! intent(out): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthTemp => diag_data%var(iLookDIAG%mLayerEnthTemp)%dat & ! intent(out): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) + &) + + ! will need enthalpy change, compute H_T at the beginning of the data step + call T2enthTemp(& + ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTemp, & ! intent(in): value of canopy air temperature (K) + scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) + scalarCanopyWat, & ! intent(in): value of canopy total water (kg m-2) + ! input: variables for the snow-soil domain + mLayerTemp, & ! intent(in): vector of layer temperature (K) + mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) + mLayerMatricHead, & ! intent(in): vector of total water matric potential (m) + ! output: enthalpy + scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTemp, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthTemp, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,cmessage) ! intent(out): error control + end associate + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if + end subroutine enthalpy_function_evaluations + + subroutine initial_flux_and_residual_vectors + ! ** Compute initial flux and residual vectors ** + ! Note: prime initial values are 0 so it's fine to run the regular eval8summa with every solver choice + associate(& + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1),& ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) & ! intent(in): [i4b] number of soil layers + &) + call eval8summa(& + ! input: model control + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): number of layers + nState, & ! intent(in): number of state variables in the current subset + .false., & ! intent(in): not inside Sundials solver + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVecTrial, & ! intent(in): model state vector + fScale, & ! intent(in): characteristic scale of the function evaluations + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + indx_data, & ! intent(inout): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_init, & ! intent(inout): model fluxes for a local HRU (initial flux structure) + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + ! output + feasible, & ! intent(out): flag to denote the feasibility of the solution + fluxVec0, & ! intent(out): flux vector + rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation + resVec, & ! intent(out): residual vector + fOld, & ! intent(out): function evaluation + err,cmessage) ! intent(out): error control + end associate + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors + end subroutine initial_flux_and_residual_vectors + + subroutine Newton_step + ! ** Compute the Newton step using numerical recipes ** + associate(& + ! layer geometry + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) & ! intent(in): [i4b] number of soil layers + ) + call in_SS4NR % initialize(dt_cur,dt,iter,nSnow,nSoil,nLayers,nLeadDim,nState,ixMatrix,firstSubStep,computeVegFlux,scalarSolution,fOld) + call io_SS4NR % initialize(firstFluxCall,xMin,xMax,ixSaturation) + call summaSolve4numrec(in_SS4NR,& ! input: model control + &stateVecTrial,fScale,xScale,resVec,sMul,dMat,& ! input: state vectors + &model_decisions,lookup_data,type_data,attr_data,mpar_data,forc_data,bvar_data,prog_data,& ! input: data structures + &indx_data,diag_data,flux_temp,deriv_data,& ! input-output: data structures + &dBaseflow_dMatric,io_SS4NR,& ! input-output: baseflow + &stateVecNew,fluxVec,resSink,resVecNew,out_SS4NR) ! output + call io_SS4NR % finalize(firstFluxCall,xMin,xMax,ixSaturation) + call out_SS4NR % finalize(fNew,converged,err,cmessage) + end associate + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors + + ! save the computed functions, residuals, and solution + fOld = fNew + resVec = resVecNew + stateVecTrial = stateVecNew + stateVecPrime = stateVecTrial !prime values not used here, dummy + nSteps = 1 ! number of time steps taken in solver + end subroutine Newton_step + + subroutine check_Newton_convergence + ! ** Check for convergence of current Newton step ** + + ! exit iteration loop if converged + if (converged) then; exit_flag=.true.; return; end if + + ! check convergence + if (iter==localMaxiter) then + message=trim(message)//'failed to converge' + err=-20; return_flag=.true.; return + end if + end subroutine check_Newton_convergence + + subroutine enforce_mass_conservation + ! Post processing step to “perfectly” conserve mass by pushing the errors into the state variables + ! NOTE: if the residual is large this will cause the state variables to be pushed outside of their bounds + layerVars: associate(& + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + ! vector of energy and hydrology indices for the snow and soil domains + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1),& ! intent(in): [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) & ! intent(in): [i4b] number of hydrology state variables in the snow+soil domain + ) + + ! update temperatures (ensure new temperature is consistent with the fluxes) + if (nSnowSoilNrg>0) then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! loop through non-missing energy state variables in the snow+soil domain + iState = ixSnowSoilNrg(iLayer) + stateVecTrial(iState) = stateVecInit(iState) + (fluxVec(iState)*dt_cur + resSink(iState))/real(sMul(iState), rkind) + resVec(iState) = 0._qp + end do ! looping through non-missing energy state variables in the snow+soil domain + end if + + ! update volumetric water content in the snow (ensure change in state is consistent with the fluxes) + ! NOTE: for soil water balance is constrained within the iteration loop + if (nSnowSoilHyd>0) then + do concurrent (iLayer=1:nSnow,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing water state variables in the snow domain) + iState = ixSnowSoilHyd(iLayer) + stateVecTrial(iState) = stateVecInit(iState) + (fluxVec(iState)*dt_cur + resSink(iState)) + resVec(iState) = 0._qp + end do ! looping through non-missing water state variables in the soil domain + end if + end associate layerVars + end subroutine enforce_mass_conservation + + subroutine Newton_iterations_numrec + ! ** Compute the backward Euler solution using Newton iterations from numerical recipes ** + + ! define maximum number of iterations + maxiter = nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)) + + ! correct the number of iterations + localMaxIter = merge(scalarMaxIter, maxIter, scalarSolution) + + !--------------------------- + ! * solving F(y) = 0 from Backward Euler with free numerical recipes routines, y is the state vector + !--------------------------- + ! iterate and update trial state vector, fluxes, and derivatives + exit_flag=.false. ! initialize exit flag + do iter=1,localMaxIter ! begin Newton iterations + niter = iter+1 ! # of iterations -- +1 because xFluxResid was moved outside the iteration loop (for backwards compatibility) + call Newton_step; if (return_flag) return ! compute Newton step -- return if error + call check_Newton_convergence ! check current Newton step for convergence + if (exit_flag) exit ! exit loop if convereged + if (return_flag) return ! return if error + end do + + if (post_massCons) call enforce_mass_conservation ! enforce mass conservation if desired + end subroutine Newton_iterations_numrec + + subroutine finalize_systemSolv + ! set untapped melt energy to zero + untappedMelt(:) = 0._rkind + + ! free memory + deallocate(mLayerCmpress_sum) + deallocate(dBaseflow_dMatric) + end subroutine finalize_systemSolv end subroutine systemSolv From 0e57e9864532cd2010d8361fc2a3183bdac5c25a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 14 Mar 2024 12:07:37 +0900 Subject: [PATCH 1213/1472] utils --- utils/plot_per_GRUMultBal.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index 633008731..434b40f3b 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -61,8 +61,8 @@ plot_vars = settings.copy() if stat == 'mean': - maxes = [1e-4,1e-2,1e-3,1e-2]+[1e-12,1e-9,1e-10,1e-11] + [3e-3] - if do_rel: maxes = [1e-6,1e-5,1e-8,1e-8]+[1e-10,1e-11,1e-13,1e-11] + [3e-3] + maxes = [1e-4,1e0,1e0,1e0]+[1e-12,1e-11,1e-10,1e-13] + [3e-3] + if do_rel: maxes = [1e-6,1e-4,1e-6,1e-7]+[1e-10,1e-11,1e-13,1e-11] + [3e-3] if stat == 'amax': maxes = [1e-3,1e3,1e3,1e2]+[1e-11,1e-6,1e-7,1e-8] + [1e0] if do_rel: maxes = [1e-2,1e0,1e-4,1e-2]+[1e-7,1e-8,1e-10,1e-6] + [1e0] @@ -210,7 +210,7 @@ def run_loop(j,var,the_max): my_cmap.set_bad(color='white') #nan color white vmin,vmax = the_max*1e-4, the_max if any(substring in var for substring in ['VegNrg', 'SnowNrg', 'SoilNrg']): - vmin, vmax = the_max * 1e-6, the_max + vmin, vmax = the_max * 1e-7, the_max if var in ['wallClockTime',]: vmin,vmax = the_max*1e-1, the_max norm = matplotlib.colors.LogNorm(vmin=vmin, vmax=vmax) From fe13e10cf6a3af6a9cd46e771afcedbf316bdb72 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 14 Mar 2024 05:56:18 -0600 Subject: [PATCH 1214/1472] Modularized IDA and KINSOL case statement branches in systemSolv using internal subroutines. --- build/source/engine/systemSolv.f90 | 368 +++++++++++++++-------------- 1 file changed, 190 insertions(+), 178 deletions(-) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 1116a580e..d3d3782be 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -278,186 +278,19 @@ subroutine systemSolv(& call initial_function_evaluations; if (return_flag) return ! initial function evaluations -- return if error - globalVars: associate(& - ! model decisions - ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver - ! layer geometry - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) & ! intent(in): [i4b] number of soil layers - ) - - ! ************************** - ! * Solving the System - ! ************************** + ! ************************** + ! * Solving the System + ! ************************** + associate(ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision) ! intent(in): [i4b] choice of numerical solver select case(ixNumericalMethod) -#ifdef SUNDIALS_ACTIVE - case(ida) - ! get tolerance vectors - call popTol4ida(& - ! input - nState, & ! intent(in): number of desired state variables - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - mpar_data, & ! intent(in): model parameters - ! output - atol, & ! intent(out): absolute tolerances vector (mixed units) - rtol, & ! intent(out): relative tolerances vector (mixed units) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - ! allocate space for the temporary flux_sum structure - call allocLocal(flux_meta(:),flux_sum,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! initialize flux_sum - do concurrent ( iVar=1:size(flux_meta) ) - flux_sum%var(iVar)%dat(:) = 0._rkind - end do - ! initialize sum of compression of the soil matrix - mLayerCmpress_sum(:) = 0._rkind - - !--------------------------- - ! * solving F(y,y') = 0 by IDA, y is the state vector and y' is the time derivative vector dy/dt - !--------------------------- - ! iterations and updates to trial state vector, fluxes, and derivatives are done inside IDA solver - call summaSolve4ida(& - dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): entire time step for drainage pond rate - atol, & ! intent(in): absolute tolerance - rtol, & ! intent(in): relative tolerance - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): number of snow+soil layers - nState, & ! intent(in): number of state variables in the current subset - ixMatrix, & ! intent(in): type of matrix (dense or banded) - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - computMassBalance, & ! intent(in): flag to compute mass balance - computNrgBalance, & ! intent(in): flag to compute energy balance - ! input: state vector - stateVecTrial, & ! intent(in): model state vector at the beginning of the data time step - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - dMat, & ! intent(inout): diagonal of the Jacobian matrix (excludes fluxes) - ! input: data structures - model_decisions, & ! intent(in): model decisions - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_temp, & ! intent(inout): model fluxes for a local HRU - flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - mLayerCmpress_sum, & ! intent(inout): sum of compression of the soil matrix - ! output - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - sunSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step - tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt - nSteps, & ! intent(out): number of time steps taken in solver - stateVecNew, & ! intent(inout): model state vector (y) at the end of the data time step - stateVecPrime, & ! intent(inout): derivative of model state vector (y') at the end of the data time step - balance, & ! intent(inout): balance per state - err,cmessage) ! intent(out): error control - ! check if IDA is successful, only fail outright in the case of a non-recoverable error - if( .not.sunSucceeds )then - message=trim(message)//trim(cmessage) - !if(err.ne.-20 .or. err=0) err = 20 ! 0 if infeasible solution, could happen since not using imposeConstraints - if(err.ne.-20) err = 20 ! -20 is a recoverable error - return - else - if (tooMuchMelt) return !exit to start same step over after merge - endif - niter = 0 ! iterations are counted inside IDA solver - - ! save the computed solution - stateVecTrial = stateVecNew - - ! compute average flux - do iVar=1,size(flux_meta) - flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / dt_cur - end do - - ! compute the total change in storage associated with compression of the soil matrix (kg m-2) - soilVars: associate(& - ! layer geometry - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! depth of each layer in the snow-soil sub-domain (m) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! change in storage associated with compression of the soil matrix (-) - scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) & ! total change in storage associated with compression of the soil matrix (kg m-2 s-1) - ) - mLayerCompress = mLayerCmpress_sum / dt_cur - scalarSoilCompress = sum(mLayerCompress(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water - end associate soilVars - - case(kinsol) - !--------------------------- - ! * solving F(y) = 0 from Backward Euler with KINSOL, y is the state vector - !--------------------------- - ! iterations and updates to trial state vector, fluxes, and derivatives are done inside IDA solver - call summaSolve4kinsol(& - dt_cur, & ! intent(in): data time step - dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate - fScale, & ! intent(in): characteristic scale of the function evaluations - xScale, & ! intent(in): characteristic scale of the state vector - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): number of snow+soil layers - nState, & ! intent(in): number of state variables in the current subset - ixMatrix, & ! intent(in): type of matrix (dense or banded) - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vector - stateVecTrial, & ! intent(in): model state vector at the beginning of the data time step - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - dMat, & ! intent(inout) diagonal of the Jacobian matrix (excludes fluxes) - ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_temp, & ! intent(inout): model fluxes for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - sunSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step - stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step - fluxVec, & ! intent(out): new flux vector - resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - resVec, & ! intent(out): new residual vector - err,cmessage) ! intent(out): error control - ! check if KINSOL is successful, only fail outright in the case of a non-recoverable error - if( .not.sunSucceeds )then - message=trim(message)//trim(cmessage) - !if(err.ne.-20 .or. err=0) err = 20 ! 0 if infeasible solution, should not happen with imposeConstraints - if(err.ne.-20) err = 20 ! -20 if hit maximum iterations - return - endif - niter = 0 ! iterations are counted inside KINSOL solver - nSteps = 1 ! number of time steps taken in solver - - ! save the computed solution - stateVecTrial = stateVecNew - stateVecPrime = stateVecTrial ! prime values not used here, dummy - -#endif - case(numrec) + case(ida) ! solve for general time step using IDA + call solve_with_IDA; if (return_flag) return ! solve using IDA -- return if error + case(kinsol) ! solve for BE time step using KINSOL + call solve_with_KINSOL; if (return_flag) return ! solve using KINSOL -- return if error + case(numrec) ! solve for BE time step using Newton iterations call Newton_iterations_numrec; if (return_flag) return ! Newton iterations using numerical recipes -- return if error end select - - end associate globalVars ! end associate statements + end associate call finalize_systemSolv ! set untapped melt to zero and deallocate arrays @@ -735,7 +568,7 @@ subroutine enforce_mass_conservation ! Post processing step to “perfectly” conserve mass by pushing the errors into the state variables ! NOTE: if the residual is large this will cause the state variables to be pushed outside of their bounds layerVars: associate(& - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers ! vector of energy and hydrology indices for the snow and soil domains ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain @@ -764,6 +597,185 @@ subroutine enforce_mass_conservation end associate layerVars end subroutine enforce_mass_conservation + subroutine solve_with_IDA +#ifdef SUNDIALS_ACTIVE + ! get tolerance vectors + call popTol4ida(& + ! input + nState, & ! intent(in): number of desired state variables + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + mpar_data, & ! intent(in): model parameters + ! output + atol, & ! intent(out): absolute tolerances vector (mixed units) + rtol, & ! intent(out): relative tolerances vector (mixed units) + err,cmessage) ! intent(out): error control + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors + + layerGeometry: associate(& + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1),& ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) & ! intent(in): [i4b] number of soil layers + ) + + ! allocate space for the temporary flux_sum structure + call allocLocal(flux_meta(:),flux_sum,nSnow,nSoil,err,cmessage) + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if + + ! initialize flux_sum + do concurrent ( iVar=1:size(flux_meta) ) + flux_sum%var(iVar)%dat(:) = 0._rkind + end do + ! initialize sum of compression of the soil matrix + mLayerCmpress_sum(:) = 0._rkind + + !--------------------------- + ! * solving F(y,y') = 0 by IDA, y is the state vector and y' is the time derivative vector dy/dt + !--------------------------- + ! iterations and updates to trial state vector, fluxes, and derivatives are done inside IDA solver + call summaSolve4ida(& + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): entire time step for drainage pond rate + atol, & ! intent(in): absolute tolerance + rtol, & ! intent(in): relative tolerance + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): number of snow+soil layers + nState, & ! intent(in): number of state variables in the current subset + ixMatrix, & ! intent(in): type of matrix (dense or banded) + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + computMassBalance, & ! intent(in): flag to compute mass balance + computNrgBalance, & ! intent(in): flag to compute energy balance + ! input: state vector + stateVecTrial, & ! intent(in): model state vector at the beginning of the data time step + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + dMat, & ! intent(inout): diagonal of the Jacobian matrix (excludes fluxes) + ! input: data structures + model_decisions, & ! intent(in): model decisions + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + indx_data, & ! intent(inout): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_temp, & ! intent(inout): model fluxes for a local HRU + flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + mLayerCmpress_sum, & ! intent(inout): sum of compression of the soil matrix + ! output + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + sunSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step + tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt + nSteps, & ! intent(out): number of time steps taken in solver + stateVecNew, & ! intent(inout): model state vector (y) at the end of the data time step + stateVecPrime, & ! intent(inout): derivative of model state vector (y') at the end of the data time step + balance, & ! intent(inout): balance per state + err,cmessage) ! intent(out): error control + ! check if IDA is successful, only fail outright in the case of a non-recoverable error + if ( .not.sunSucceeds ) then + message=trim(message)//trim(cmessage) + !if (err.ne.-20 .or. err=0) err = 20 ! 0 if infeasible solution, could happen since not using imposeConstraints + if (err.ne.-20) err = 20 ! -20 is a recoverable error + return + else + if (tooMuchMelt) return !exit to start same step over after merge + end if + niter = 0 ! iterations are counted inside IDA solver + + ! save the computed solution + stateVecTrial = stateVecNew + + ! compute average flux + do iVar=1,size(flux_meta) + flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / dt_cur + end do + + ! compute the total change in storage associated with compression of the soil matrix (kg m-2) + soilVars: associate(& + ! layer geometry + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! depth of each layer in the snow-soil sub-domain (m) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! change in storage associated with compression of the soil matrix (-) + scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) & ! total change in storage associated with compression of the soil matrix (kg m-2 s-1) + ) + mLayerCompress = mLayerCmpress_sum / dt_cur + scalarSoilCompress = sum(mLayerCompress(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water + end associate soilVars + end associate layerGeometry +#endif + end subroutine solve_with_IDA + + subroutine solve_with_KINSOL +#ifdef SUNDIALS_ACTIVE + associate(& + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1),& ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) & ! intent(in): [i4b] number of soil layers + ) + !--------------------------- + ! * solving F(y) = 0 from Backward Euler with KINSOL, y is the state vector + !--------------------------- + ! iterations and updates to trial state vector, fluxes, and derivatives are done inside IDA solver + call summaSolve4kinsol(& + dt_cur, & ! intent(in): data time step + dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate + fScale, & ! intent(in): characteristic scale of the function evaluations + xScale, & ! intent(in): characteristic scale of the state vector + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): number of snow+soil layers + nState, & ! intent(in): number of state variables in the current subset + ixMatrix, & ! intent(in): type of matrix (dense or banded) + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vector + stateVecTrial, & ! intent(in): model state vector at the beginning of the data time step + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + dMat, & ! intent(inout) diagonal of the Jacobian matrix (excludes fluxes) + ! input: data structures + model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + indx_data, & ! intent(inout): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_temp, & ! intent(inout): model fluxes for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + sunSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step + stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step + fluxVec, & ! intent(out): new flux vector + resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + resVec, & ! intent(out): new residual vector + err,cmessage) ! intent(out): error control + end associate + + ! check if KINSOL is successful, only fail outright in the case of a non-recoverable error + if ( .not.sunSucceeds ) then + message=trim(message)//trim(cmessage) + !if(err.ne.-20 .or. err=0) err = 20 ! 0 if infeasible solution, should not happen with imposeConstraints + if (err.ne.-20) err = 20 ! -20 if hit maximum iterations + return + end if + niter = 0 ! iterations are counted inside KINSOL solver + nSteps = 1 ! number of time steps taken in solver + + ! save the computed solution + stateVecTrial = stateVecNew + stateVecPrime = stateVecTrial ! prime values not used here, dummy +#endif + end subroutine solve_with_KINSOL + subroutine Newton_iterations_numrec ! ** Compute the backward Euler solution using Newton iterations from numerical recipes ** From 57125e9dc20e4c2d753675fe1743fc4f5b6ae27c Mon Sep 17 00:00:00 2001 From: Kyle Klenk Date: Fri, 15 Mar 2024 21:40:27 +0000 Subject: [PATCH 1215/1472] Update cmakelists to include c netcdf library for actors. Add new batch actor to cmakelists.txt --- build/cmake/CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 2d23be96a..5ba465ea1 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -159,7 +159,7 @@ if(CMAKE_BUILD_TYPE MATCHES Cluster) if(CMAKE_BUILD_TYPE MATCHES Actors) find_package(CAF REQUIRED) set(INC_ACTORS ${CAF_INCLUDES} ${PARENT_DIR}/build/includes/global ${PARENT_DIR}/build/includes/summa_actor ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) - set(LIB_ACTORS ${CAF_LIBRARIES} -lcaf_core -lcaf_io) + set(LIB_ACTORS ${CAF_LIBRARIES} -lcaf_core -lcaf_io -lnetcdf) endif() else() @@ -205,7 +205,7 @@ else() ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) - set(LIB_ACTORS ${DIR_ACTORS}/lib -lcaf_core -lcaf_io) + set(LIB_ACTORS ${DIR_ACTORS}/lib -lcaf_core -lcaf_io -lnetcdf) endif() endif() @@ -442,7 +442,8 @@ set(JOB_ACTOR ${ACTORS_DIR}/job_actor/GRU.cpp ${ACTORS_DIR}/job_actor/job_actor.cpp) set(HRU_ACTOR - ${ACTORS_DIR}/hru_actor/hru_actor.cpp) + ${ACTORS_DIR}/hru_actor/hru_actor.cpp + ${ACTORS_DIR}/hru_actor/hru_batch_actor.cpp) #========================================================================================= From 749ab67de6896e085733ab08eee8fbec7084e363 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 16 Mar 2024 11:31:30 +0900 Subject: [PATCH 1216/1472] canopy T to enthalpy, working on snow --- build/source/engine/enthalpyTemp.f90 | 119 ++++++++++++++++++++------- 1 file changed, 91 insertions(+), 28 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 4f006e766..70c5b727d 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -364,7 +364,7 @@ subroutine enthalpy2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) ! get new function evaluation Ht1 = T2enthalpy_snow(Tg1,1._rkind,fc_param) f1 = Ht1 - H_spec - ! compute derivative if dT + ! compute derivative of dT dh = (f1 - f0)/dT ! compute change in T dT = -f1/dh @@ -969,51 +969,114 @@ subroutine enthalpy2T(& endif - ! ***** get initial guess + ! ***** get temperature if unfrozen vegetation T = scalarCanopyEnthalpy * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial ) + Tfreeze - dT_dE = canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial ) + dT_dH = canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial ) dT_dW = -Cp_water * scalarCanopyEnthalpy * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial )**2_i4b - diffT = T - Tfreeze - if(diffT>=0._rkind)then - scalarCanopyTempTrial = T - dCanopyTemp_dEnthalpy = dT_dE - dCanopyTemp_dCanWat = dT_dW - else - ! ***** iterate to find temperature + ! ***** iterate to find temperature if ice exists + if( T1.e-6_rkind) - diffT = T - Tfreeze + ! compute enthalpy function, H + diffT = T - Tfreeze integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - fLiq = 1._rkind / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b ) - H = ( (specificHeatVeg * maxMassVegetation + Cp_ice * scalarCanopyWatTrial )* diffT + (Cp_water - Cp_ice)* scalarCanopyWatTrial * integral & - + LH_fus * (1._rkind - fLiq) * scalarCanopyWatTrial )/ canopyDepth + fLiq = 1._rkind / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b ) + H = ( (specificHeatVeg * maxMassVegetation + Cp_ice * scalarCanopyWatTrial )* diffT + (Cp_water - Cp_ice)* scalarCanopyWatTrial * integral & + + LH_fus * (1._rkind - fLiq) * scalarCanopyWatTrial )/ canopyDepth + + ! compute derivative of H with respect to T + dintegral_dT = (1._rkind/snowfrz_scale) / (1._rkind + (snowfrz_scale * diffT)**2_i4b) + d2integral_dT2 = -2._rkind * snowfrz_scale * diffT / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**2_i4b + dfLiq_dT = -2._rkind * snowfrz_scale * diffT / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**2_i4b + d2fLiq_dT2 = 2._rkind * snowfrz_scale**2_i4b * ( 3._rkind * diffT**2_i4b - 1._rkind ) / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**3_i4b + dH_dT = ( specificHeatVeg * maxMassVegetation + Cp_ice * scalarCanopyWatTrial + (Cp_water - Cp_ice)* scalarCanopyWatTrial * dintegral_dT & + - LH_fus * dfLiq_dT * scalarCanopyWatTrial )/ canopyDepth + d2H_dT2 = ( (Cp_water - Cp_ice)* scalarCanopyWatTrial * d2integral_dT2 - LH_fus * d2fLiq_dT2 * scalarCanopyWatTrial )/ canopyDepth + + ! compute derivative of H with respect to canopy enthalpy + dH_dH = dH_dT * dT_dH + dH_dT__dH = d2H_dT2 * dT_dH + + ! compute derivative of H with respect to canopy water + dH_dW = dH_dT * dT_dW + ( Cp_ice * diffT + (Cp_water - Cp_ice) * integral + LH_fus * (1._rkind - fLiq) )/ canopyDepth + dH_dT__dW = d2H_dT2 * dT_dW + ( Cp_ice * (1.0_rkind - dintegral_dT) - LH_fus * dfLiq )/ canopyDepth + + ! compute change in T and update, including derivatives + T1 = T - (H - scalarCanopyEnthalpyTrial)/dH_dT + dT_dH = dT_dH - ( dH_dH - 1._rkind - dH_dT__dH * (H - scalarCanopyEnthalpyTrial)/dH_dT ) /dH_dT + dT_dW = dT_dW - ( dH_dW - dH_dT__dW * (H - scalarCanopyEnthalpyTrial)/dH_dT ) /dH_dT + end do + endif ! (if ice exists) + + ! update temperature and derivatives + scalarCanopyTempTrial = T + dCanopyTemp_dEnthalpy = dT_dH + dCanopyTemp_dCanWat = dT_dW + + end associate vegVars + + case(iname_snow) + + ! association to necessary variables for snow + snowVars: associate(& + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) + + ! ***** iterate to find temperature, ice always exists + T = 260._rkind ! initial guess, very cold + H = mLayerEnthalpy(iLayer) + 1._rkind ! to start the iteration + dT_dH = 0._rkind + dT_dW = 0._rkind + + + do while(abs(mLayerEnthalpy(iLayer)-H)>1.e-6_rkind) + ! compute enthalpy function, H + diffT = T - Tfreeze + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + fLiq = 1._rkind / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b ) + H = (specificHeatVeg * maxMassVegetation + Cp_ice * mLayerVolFracWatTrial(iLayer) )* diffT + (Cp_water - Cp_ice)* mLayerVolFracWatTrial(iLayer) * integral & + + LH_fus * (1._rkind - fLiq) * mLayerVolFracWatTrial(iLayer) + + ! compute derivative of H with respect to T + dintegral_dT = (1._rkind/snowfrz_scale) / (1._rkind + (snowfrz_scale * diffT)**2_i4b) + d2integral_dT2 = -2._rkind * snowfrz_scale * diffT / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**2_i4b + dfLiq_dT = -2._rkind * snowfrz_scale * diffT / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**2_i4b + d2fLiq_dT2 = 2._rkind * snowfrz_scale**2_i4b * ( 3._rkind * diffT**2_i4b - 1._rkind ) / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**3_i4b + dH_dT = specificHeatVeg * maxMassVegetation + Cp_ice * mLayerVolFracWatTrial(iLayer) + (Cp_water - Cp_ice)* mLayerVolFracWatTrial(iLayer) * dintegral_dT & + - LH_fus * dfLiq_dT * mLayerVolFracWatTrial(iLayer) + d2H_dT2 = ( (Cp_water - Cp_ice)* mLayerVolFracWatTrial(iLayer) * d2integral_dT2 - LH_fus * d2fLiq_dT2 * mLayerVolFracWatTrial(iLayer) )/ canopyDepth + + ! compute derivative of H with respect to layer enthalpy + dH_dH = dH_dT * dT_dH + dH_dT__dH = d2H_dT2 * dT_dH + + ! compute derivative of H with respect to layer water content + dH_dW = dH_dT * dT_dW + Cp_ice * diffT + (Cp_water - Cp_ice) * integral + LH_fus * (1._rkind - fLiq) + dH_dT__dW = d2H_dT2 * dT_dW + Cp_ice * (1.0_rkind - dintegral_dT) - LH_fus * dfLiq + + ! compute change in T and update, including derivatives + T1 = T - (H - mLayerEnthalpy(iLayer))/dH_dT + dT_dH = dT_dH - ( dH_dH - 1._rkind - dH_dT__dH * (H - mLayerEnthalpy(iLayer))/dH_dT ) /dH_dT + dT_dW = dT_dW - ( dH_dW - dH_dT__dW * (H - mLayerEnthalpy(iLayer))/dH_dT ) /dH_dT + end do + - end do - ! ***** iterate to find temperature - ! diffT>=0._rkind - scalarCanopyTempTrial = scalarCanopyEnthalpy * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial ) + Tfreeze - scalarCanopyTempPrime = scalarCanopyEnthalpyPrime * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial ) - ! diffT<0._rkind - scalarCanopyEnthalpy * canopyDepth = (specificHeatVeg * maxMassVegetation + Cp_ice * scalarCanopyWatTrial )* diffT + (Cp_water - Cp_ice)* scalarCanopyWatTrial * integral - + LH_fus * (1._rkind - fLiq) * scalarCanopyWatTrial - end associate vegVars - case(iname_snow) - ! association to necessary variables for snow - snowVars: associate(& - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) diffT = mLayerTempTrial(iLayer) - Tfreeze ! diffT<0._rkind because snow is frozen integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) From 8f03ec88793f8a0b5654f4ee4342f3dc1d363ece Mon Sep 17 00:00:00 2001 From: ashleymedin <34691332+ashleymedin@users.noreply.github.com> Date: Sat, 16 Mar 2024 11:30:24 +0900 Subject: [PATCH 1217/1472] Merge pull request #36 from seantrim/develop_refactor_Newton Organizational updates in systemSolv --- build/source/engine/systemSolv.f90 | 368 +++++++++++++++-------------- 1 file changed, 190 insertions(+), 178 deletions(-) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 1116a580e..d3d3782be 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -278,186 +278,19 @@ subroutine systemSolv(& call initial_function_evaluations; if (return_flag) return ! initial function evaluations -- return if error - globalVars: associate(& - ! model decisions - ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver - ! layer geometry - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) & ! intent(in): [i4b] number of soil layers - ) - - ! ************************** - ! * Solving the System - ! ************************** + ! ************************** + ! * Solving the System + ! ************************** + associate(ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision) ! intent(in): [i4b] choice of numerical solver select case(ixNumericalMethod) -#ifdef SUNDIALS_ACTIVE - case(ida) - ! get tolerance vectors - call popTol4ida(& - ! input - nState, & ! intent(in): number of desired state variables - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - mpar_data, & ! intent(in): model parameters - ! output - atol, & ! intent(out): absolute tolerances vector (mixed units) - rtol, & ! intent(out): relative tolerances vector (mixed units) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - - ! allocate space for the temporary flux_sum structure - call allocLocal(flux_meta(:),flux_sum,nSnow,nSoil,err,cmessage) - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - - ! initialize flux_sum - do concurrent ( iVar=1:size(flux_meta) ) - flux_sum%var(iVar)%dat(:) = 0._rkind - end do - ! initialize sum of compression of the soil matrix - mLayerCmpress_sum(:) = 0._rkind - - !--------------------------- - ! * solving F(y,y') = 0 by IDA, y is the state vector and y' is the time derivative vector dy/dt - !--------------------------- - ! iterations and updates to trial state vector, fluxes, and derivatives are done inside IDA solver - call summaSolve4ida(& - dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): entire time step for drainage pond rate - atol, & ! intent(in): absolute tolerance - rtol, & ! intent(in): relative tolerance - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): number of snow+soil layers - nState, & ! intent(in): number of state variables in the current subset - ixMatrix, & ! intent(in): type of matrix (dense or banded) - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - computMassBalance, & ! intent(in): flag to compute mass balance - computNrgBalance, & ! intent(in): flag to compute energy balance - ! input: state vector - stateVecTrial, & ! intent(in): model state vector at the beginning of the data time step - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - dMat, & ! intent(inout): diagonal of the Jacobian matrix (excludes fluxes) - ! input: data structures - model_decisions, & ! intent(in): model decisions - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_temp, & ! intent(inout): model fluxes for a local HRU - flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - mLayerCmpress_sum, & ! intent(inout): sum of compression of the soil matrix - ! output - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - sunSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step - tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt - nSteps, & ! intent(out): number of time steps taken in solver - stateVecNew, & ! intent(inout): model state vector (y) at the end of the data time step - stateVecPrime, & ! intent(inout): derivative of model state vector (y') at the end of the data time step - balance, & ! intent(inout): balance per state - err,cmessage) ! intent(out): error control - ! check if IDA is successful, only fail outright in the case of a non-recoverable error - if( .not.sunSucceeds )then - message=trim(message)//trim(cmessage) - !if(err.ne.-20 .or. err=0) err = 20 ! 0 if infeasible solution, could happen since not using imposeConstraints - if(err.ne.-20) err = 20 ! -20 is a recoverable error - return - else - if (tooMuchMelt) return !exit to start same step over after merge - endif - niter = 0 ! iterations are counted inside IDA solver - - ! save the computed solution - stateVecTrial = stateVecNew - - ! compute average flux - do iVar=1,size(flux_meta) - flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / dt_cur - end do - - ! compute the total change in storage associated with compression of the soil matrix (kg m-2) - soilVars: associate(& - ! layer geometry - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! depth of each layer in the snow-soil sub-domain (m) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! change in storage associated with compression of the soil matrix (-) - scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) & ! total change in storage associated with compression of the soil matrix (kg m-2 s-1) - ) - mLayerCompress = mLayerCmpress_sum / dt_cur - scalarSoilCompress = sum(mLayerCompress(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water - end associate soilVars - - case(kinsol) - !--------------------------- - ! * solving F(y) = 0 from Backward Euler with KINSOL, y is the state vector - !--------------------------- - ! iterations and updates to trial state vector, fluxes, and derivatives are done inside IDA solver - call summaSolve4kinsol(& - dt_cur, & ! intent(in): data time step - dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate - fScale, & ! intent(in): characteristic scale of the function evaluations - xScale, & ! intent(in): characteristic scale of the state vector - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): number of snow+soil layers - nState, & ! intent(in): number of state variables in the current subset - ixMatrix, & ! intent(in): type of matrix (dense or banded) - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vector - stateVecTrial, & ! intent(in): model state vector at the beginning of the data time step - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - dMat, & ! intent(inout) diagonal of the Jacobian matrix (excludes fluxes) - ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_temp, & ! intent(inout): model fluxes for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - sunSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step - stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step - fluxVec, & ! intent(out): new flux vector - resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - resVec, & ! intent(out): new residual vector - err,cmessage) ! intent(out): error control - ! check if KINSOL is successful, only fail outright in the case of a non-recoverable error - if( .not.sunSucceeds )then - message=trim(message)//trim(cmessage) - !if(err.ne.-20 .or. err=0) err = 20 ! 0 if infeasible solution, should not happen with imposeConstraints - if(err.ne.-20) err = 20 ! -20 if hit maximum iterations - return - endif - niter = 0 ! iterations are counted inside KINSOL solver - nSteps = 1 ! number of time steps taken in solver - - ! save the computed solution - stateVecTrial = stateVecNew - stateVecPrime = stateVecTrial ! prime values not used here, dummy - -#endif - case(numrec) + case(ida) ! solve for general time step using IDA + call solve_with_IDA; if (return_flag) return ! solve using IDA -- return if error + case(kinsol) ! solve for BE time step using KINSOL + call solve_with_KINSOL; if (return_flag) return ! solve using KINSOL -- return if error + case(numrec) ! solve for BE time step using Newton iterations call Newton_iterations_numrec; if (return_flag) return ! Newton iterations using numerical recipes -- return if error end select - - end associate globalVars ! end associate statements + end associate call finalize_systemSolv ! set untapped melt to zero and deallocate arrays @@ -735,7 +568,7 @@ subroutine enforce_mass_conservation ! Post processing step to “perfectly” conserve mass by pushing the errors into the state variables ! NOTE: if the residual is large this will cause the state variables to be pushed outside of their bounds layerVars: associate(& - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers ! vector of energy and hydrology indices for the snow and soil domains ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain @@ -764,6 +597,185 @@ subroutine enforce_mass_conservation end associate layerVars end subroutine enforce_mass_conservation + subroutine solve_with_IDA +#ifdef SUNDIALS_ACTIVE + ! get tolerance vectors + call popTol4ida(& + ! input + nState, & ! intent(in): number of desired state variables + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + mpar_data, & ! intent(in): model parameters + ! output + atol, & ! intent(out): absolute tolerances vector (mixed units) + rtol, & ! intent(out): relative tolerances vector (mixed units) + err,cmessage) ! intent(out): error control + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors + + layerGeometry: associate(& + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1),& ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) & ! intent(in): [i4b] number of soil layers + ) + + ! allocate space for the temporary flux_sum structure + call allocLocal(flux_meta(:),flux_sum,nSnow,nSoil,err,cmessage) + if (err/=0) then; err=20; message=trim(message)//trim(cmessage); return; end if + + ! initialize flux_sum + do concurrent ( iVar=1:size(flux_meta) ) + flux_sum%var(iVar)%dat(:) = 0._rkind + end do + ! initialize sum of compression of the soil matrix + mLayerCmpress_sum(:) = 0._rkind + + !--------------------------- + ! * solving F(y,y') = 0 by IDA, y is the state vector and y' is the time derivative vector dy/dt + !--------------------------- + ! iterations and updates to trial state vector, fluxes, and derivatives are done inside IDA solver + call summaSolve4ida(& + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): entire time step for drainage pond rate + atol, & ! intent(in): absolute tolerance + rtol, & ! intent(in): relative tolerance + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): number of snow+soil layers + nState, & ! intent(in): number of state variables in the current subset + ixMatrix, & ! intent(in): type of matrix (dense or banded) + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + computMassBalance, & ! intent(in): flag to compute mass balance + computNrgBalance, & ! intent(in): flag to compute energy balance + ! input: state vector + stateVecTrial, & ! intent(in): model state vector at the beginning of the data time step + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + dMat, & ! intent(inout): diagonal of the Jacobian matrix (excludes fluxes) + ! input: data structures + model_decisions, & ! intent(in): model decisions + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + indx_data, & ! intent(inout): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_temp, & ! intent(inout): model fluxes for a local HRU + flux_sum, & ! intent(inout): sum of fluxes model fluxes for a local HRU over a data step + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + mLayerCmpress_sum, & ! intent(inout): sum of compression of the soil matrix + ! output + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + sunSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step + tooMuchMelt, & ! intent(inout): flag to denote that there was too much melt + nSteps, & ! intent(out): number of time steps taken in solver + stateVecNew, & ! intent(inout): model state vector (y) at the end of the data time step + stateVecPrime, & ! intent(inout): derivative of model state vector (y') at the end of the data time step + balance, & ! intent(inout): balance per state + err,cmessage) ! intent(out): error control + ! check if IDA is successful, only fail outright in the case of a non-recoverable error + if ( .not.sunSucceeds ) then + message=trim(message)//trim(cmessage) + !if (err.ne.-20 .or. err=0) err = 20 ! 0 if infeasible solution, could happen since not using imposeConstraints + if (err.ne.-20) err = 20 ! -20 is a recoverable error + return + else + if (tooMuchMelt) return !exit to start same step over after merge + end if + niter = 0 ! iterations are counted inside IDA solver + + ! save the computed solution + stateVecTrial = stateVecNew + + ! compute average flux + do iVar=1,size(flux_meta) + flux_temp%var(iVar)%dat(:) = ( flux_sum%var(iVar)%dat(:) ) / dt_cur + end do + + ! compute the total change in storage associated with compression of the soil matrix (kg m-2) + soilVars: associate(& + ! layer geometry + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! depth of each layer in the snow-soil sub-domain (m) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! change in storage associated with compression of the soil matrix (-) + scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) & ! total change in storage associated with compression of the soil matrix (kg m-2 s-1) + ) + mLayerCompress = mLayerCmpress_sum / dt_cur + scalarSoilCompress = sum(mLayerCompress(1:nSoil)*mLayerDepth(nSnow+1:nLayers))*iden_water + end associate soilVars + end associate layerGeometry +#endif + end subroutine solve_with_IDA + + subroutine solve_with_KINSOL +#ifdef SUNDIALS_ACTIVE + associate(& + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1),& ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) & ! intent(in): [i4b] number of soil layers + ) + !--------------------------- + ! * solving F(y) = 0 from Backward Euler with KINSOL, y is the state vector + !--------------------------- + ! iterations and updates to trial state vector, fluxes, and derivatives are done inside IDA solver + call summaSolve4kinsol(& + dt_cur, & ! intent(in): data time step + dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate + fScale, & ! intent(in): characteristic scale of the function evaluations + xScale, & ! intent(in): characteristic scale of the state vector + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): number of snow+soil layers + nState, & ! intent(in): number of state variables in the current subset + ixMatrix, & ! intent(in): type of matrix (dense or banded) + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vector + stateVecTrial, & ! intent(in): model state vector at the beginning of the data time step + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + dMat, & ! intent(inout) diagonal of the Jacobian matrix (excludes fluxes) + ! input: data structures + model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + indx_data, & ! intent(inout): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_temp, & ! intent(inout): model fluxes for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + sunSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step + stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step + fluxVec, & ! intent(out): new flux vector + resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + resVec, & ! intent(out): new residual vector + err,cmessage) ! intent(out): error control + end associate + + ! check if KINSOL is successful, only fail outright in the case of a non-recoverable error + if ( .not.sunSucceeds ) then + message=trim(message)//trim(cmessage) + !if(err.ne.-20 .or. err=0) err = 20 ! 0 if infeasible solution, should not happen with imposeConstraints + if (err.ne.-20) err = 20 ! -20 if hit maximum iterations + return + end if + niter = 0 ! iterations are counted inside KINSOL solver + nSteps = 1 ! number of time steps taken in solver + + ! save the computed solution + stateVecTrial = stateVecNew + stateVecPrime = stateVecTrial ! prime values not used here, dummy +#endif + end subroutine solve_with_KINSOL + subroutine Newton_iterations_numrec ! ** Compute the backward Euler solution using Newton iterations from numerical recipes ** From 9bc09165fdef2f295b75a7202a34d4bafa988af4 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 16 Mar 2024 02:41:14 -0600 Subject: [PATCH 1218/1472] Update to use SUNDIALS build by default. --- build/cmake/build.pc.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/cmake/build.pc.bash b/build/cmake/build.pc.bash index ff2c882ae..ef9262986 100755 --- a/build/cmake/build.pc.bash +++ b/build/cmake/build.pc.bash @@ -22,5 +22,5 @@ #export FLAGS_OPT="-m64;-I"${MKLROOT}/include";-flto=1;-fuse-linker-plugin" # optional compiler flags -- Intel oneMKL builds # CMake Commands (build type controlled using DCMAKE_BUILD_TYPE) -cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=BE +cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials cmake --build ../cmake_build --target all From 8763def73df32d525d71a49662cfdbdf3fc68fa9 Mon Sep 17 00:00:00 2001 From: Sean Trim <44306317+seantrim@users.noreply.github.com> Date: Sat, 16 Mar 2024 05:27:34 -0600 Subject: [PATCH 1219/1472] Update README_not_ngen.md Expanded the details for SUNDIALS installation to clarify directory structure and to avoid having to read the SUNDIALS documentation. --- docs/README_not_ngen.md | 60 ++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/docs/README_not_ngen.md b/docs/README_not_ngen.md index bd0d59057..5401239ec 100644 --- a/docs/README_not_ngen.md +++ b/docs/README_not_ngen.md @@ -23,6 +23,7 @@ To fetch and check out the latest revision (for the [currently used branch](#vie ## Building Libraries +### Solver options in SUMMA build scripts First, cd into the cmake directory in summa, without Actors: cd summa/build/cmake @@ -31,36 +32,53 @@ or with Actors: cd summa/build/summa/build/cmake -If you want to use Sundials IDA or BE Kinsol, set -DCMAKE_BUILD_TYPE=Sundials* in the build script instead of BE*. Then, before summa can be built, Sundials needs to be installed. -Download the latest release of IDA solver from SUNDIALS package in https://computing.llnl.gov/projects/sundials/sundials-software, using X.Y.Z as the latest version +If you want to use Sundials IDA or BE Kinsol, set -DCMAKE_BUILD_TYPE=Sundials* in the build script instead of BE*. Then, before summa can be built, Sundials needs to be installed. - wget "https://github.com/LLNL/sundials/releases/download/vX.Y.Z/sundials-X.Y.Z.tar.gz" - -Extract the corresponding compressed file, rename - - tar -xzf sundials-X.Y.Z.tar.gz && mv sundials-X.Y.Z sundials-software - -We suggest you periodically update to the latest version-- you can also install through github - git clone https://github.com/LLNL/sundials.git sundials-software - cd sundials-software - git fetch --all --tags --prune - git checkout tags/vX.Y.Z +### Installing SUNDIALS +0. For reference, let the main summa directory be contained in the folder `top_dir` + - e.g., summa exectuables would be located within `top_dir/summa/bin` -An example build_cmake file is at summa/build/cmake_external/build_cmakeSundials.bash which you can copy to builddir as build_cmake. Then, enter the buildir and run - - cd sundials/buildir - ./build_cmake - make - make install +2. Download the file `sundials-X.Y.Z.tar.gz` (where X.Y.Z is the latest SUNDIALS version) at https://github.com/LLNL/sundials/releases/latest + +3. Move `sundials-X.Y.Z.tar.gz` into `top_dir` + - `$ ls top_dir` should include `summa sundials-X.Y.Z.tar.gz` + +4. Extract the corresponding compressed file and rename + 1. `$ cd top_dir` + 2. `$ tar -xzf sundials-X.Y.Z.tar.gz && mv sundials-X.Y.Z sundials-software` + 3. `sundials-X.Y.Z.tar.gz` can now be removed to save space if desired + +5. Create new empty directories to prep for SUNDIALS installation + 1. within `top_dir`: `$ mkdir sundials` + 2. `$ cd sundials` + 3. `$ mkdir builddir instdir` + +6. Copy CMake build script from SUMMA files to properly configure SUNDIALS + 1. `$ cd builddir` + 2. `$ cp ../../summa/build/cmake_external/build_cmakeSundials.bash .` + +7. Build SUNDIALS configured for SUMMA + 1. within `builddir`: `$ ./build_cmakeSundials.bash` + 2. `$ make` + 3. `$ make install` + + +We suggest you periodically update to the latest version. It is also possible to install using Git: + + $ git clone https://github.com/LLNL/sundials.git sundials-software + $ cd sundials-software + $ git fetch --all --tags --prune + $ git checkout tags/vX.Y.Z Note if you need to recompile after a system upgrade, delete the contents of sundials/instdir and sundials/buildir EXCEPT sundials/buildir/build_cmake before building and installing. - Note that when there is an existing directory, it may sometimes be necessary to clear it and regenerate, especially if any changes were made to the CMakeLists.txt file. +### Building SUMMA + After there is build system directory, the shared library can be built using the `summabmi` CMake target. For example, the SummaSundials shared library file (i.e., the build config's `summabmi` target) can be built using: - cmake --build ../cmake_build --target all + $ cmake --build ../cmake_build --target all This will build a `cmake_build/libsummabmi..` file, where the version is configured within the CMake config, and the extension depends on the local machine's operating system. From 0ab9b5a9b4ecb8e2b8a32fe59b9aa139bf47e35b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 20 Mar 2024 01:52:40 +0900 Subject: [PATCH 1220/1472] enthalpy2T is done, but need to call it by layer inside updateVarsWithPrime so will redo --- build/source/dshare/type4ida.f90 | 7 +- build/source/engine/computJacobWithPrime.f90 | 3 - build/source/engine/enthalpyTemp.f90 | 651 ++++++++++--------- build/source/engine/eval8summa.f90 | 268 ++++---- build/source/engine/eval8summaWithPrime.f90 | 245 ++++--- build/source/engine/getVectorz.f90 | 18 +- build/source/engine/layerMerge.f90 | 22 +- build/source/engine/soil_utilsAddPrime.f90 | 30 +- build/source/engine/summaSolve4ida.f90 | 11 +- build/source/engine/summaSolve4kinsol.f90 | 1 - build/source/engine/systemSolv.f90 | 4 +- build/source/engine/updatStateWithPrime.f90 | 144 ++-- build/source/engine/updateVars.f90 | 11 +- build/source/engine/updateVarsWithPrime.f90 | 221 +++++-- 14 files changed, 881 insertions(+), 755 deletions(-) diff --git a/build/source/dshare/type4ida.f90 b/build/source/dshare/type4ida.f90 index 383512e56..8fb6f63af 100644 --- a/build/source/dshare/type4ida.f90 +++ b/build/source/dshare/type4ida.f90 @@ -29,6 +29,7 @@ module type4ida logical(lgt) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt) :: scalarSolution ! flag to denote if implementing the scalar solution type(model_options),allocatable :: model_decisions(:) ! model decisions + type(zLookup) :: lookup_data ! lookup tables type(var_i) :: type_data ! type of vegetation and soil type(var_d) :: attr_data ! spatial attributes type(var_dlength) :: mpar_data ! model parameters @@ -54,19 +55,13 @@ module type4ida real(rkind), allocatable :: mLayerTempPrev(:) ! previous vector of layer temperature (K) real(rkind), allocatable :: mLayerMatricHeadPrev(:) ! previous value for total water matric potential (m) real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatTrial ! trial value for mass of total water on the vegetation canopy (kg m-2) real(rkind), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) real(rkind), allocatable :: mLayerMatricHeadTrial(:) ! trial value for total water matric potential (m) - real(rkind), allocatable :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(rkind) :: scalarCanairTempPrime ! prime value for temperature of the canopy air space (K s-1) real(rkind) :: scalarCanopyTempPrime ! prime value for temperature of the vegetation canopy (K s-1) real(rkind) :: scalarCanopyWatPrime ! prime value for mass of total water on the vegetation canopy (kg m-2 s-1) - real(rkind) :: scalarCanopyIcePrime ! prime value for mass of ice on the vegetation canopy (kg m-2 s-1) real(rkind), allocatable :: mLayerTempPrime(:) ! prime vector of temperature of each snow and soil layer (K s-1) real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! prime vector of matric head of each snow and soil layer (m s-1) - real(rkind), allocatable :: mLayerMatricHeadLiqPrime(:) ! prime vector of liquid matric head of each snow and soil layer (m s-1) real(rkind), allocatable :: mLayerVolFracWatPrime(:) ! prime vector of volumetric total water content of each snow and soil layer (s-1) - real(rkind), allocatable :: mLayerVolFracIcePrime(:) ! prime vector of volumetric fraction of ice (s-1) end type data4ida diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index c04f65790..8ab4abe96 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -113,7 +113,6 @@ subroutine computJacobWithPrime(& ! input: state variables mLayerTempPrime, & ! intent(in): vector of derivative value for layer temperature (K) mLayerMatricHeadPrime, & ! intent(in): vector of derivative value for layer matric head - mLayerMatricHeadLiqPrime, & ! intent(in): vector of derivative value for layer liquid matric head mLayerVolFracWatPrime, & ! intent(in): vector of derivative value for layer water volume fraction scalarCanopyTempPrime, & ! intent(in): derivative value for temperature of the vegetation canopy (K) scalarCanopyWatPrime, & ! intent(in): derivative value for water content of the vegetation canopy @@ -146,7 +145,6 @@ subroutine computJacobWithPrime(& ! input: state variables real(rkind),intent(in) :: mLayerTempPrime(:) ! vector of derivative value for layer temperature real(rkind),intent(in) :: mLayerMatricHeadPrime(:) ! vector of derivative value for layer matric head - real(rkind),intent(in) :: mLayerMatricHeadLiqPrime(:)! vector of derivative value for layer liquid matric head real(rkind),intent(in) :: mLayerVolFracWatPrime(:) ! vector of derivative value for layer water volume fraction real(rkind),intent(in) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) real(rkind),intent(in) :: scalarCanopyWatPrime ! derivative value for water content of the vegetation canopy @@ -1134,7 +1132,6 @@ integer(c_int) function computJacob4ida(t, cj, sunvec_y, sunvec_yp, sunvec_r, & ! input: state variables eqns_data%mLayerTempPrime, & ! intent(in): derivative value for temperature of each snow and soil layer (K) eqns_data%mLayerMatricHeadPrime, & ! intent(in): derivative value for matric head of each snow and soil layer (m) - eqns_data%mLayerMatricHeadLiqPrime, & ! intent(in): derivative value for liquid water matric head of each snow and soil layer (m) eqns_data%mLayerVolFracWatPrime, & ! intent(in): derivative value for volumetric total water content of each snow and soil layer (-) eqns_data%scalarCanopyTempPrime, & ! intent(in): derivative value for temperature of the vegetation canopy (K) eqns_data%scalarCanopyWatPrime, & ! intent(in): derivative value for total water content of the vegetation canopy (kg m-2) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 70c5b727d..5b2cb7f66 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -58,14 +58,11 @@ module enthalpyTemp_module USE globalData,only:integerMissing ! missing integer USE globalData,only:realMissing ! missing real number -! privacy - implicit none public::T2H_lookup_snow public::T2L_lookup_soil -public::T2H_lookup_veg -public::enthalpy2T_snow -public::T2enthalpy_snow +public::enthalpy2T_snowlu +public::T2enthalpy_snowlu public::ethalpy2T public::enthTemp2enthalpy private::hyp_2F1_real @@ -116,7 +113,7 @@ subroutine T2H_lookup_snow(mpar_data, & ! intent(in): pa ! ***** compute specific enthalpy (NOTE: J m-3 --> J kg-1) ***** do ilook=1,nlook - Hy(ilook) = T2enthalpy_snow(Tk(ilook),waterWght,snowfrz_scale)/waterWght ! (J m-3 --> J kg-1) + Hy(ilook) = T2enthalpy_snowlu(Tk(ilook),waterWght,snowfrz_scale)/waterWght ! (J m-3 --> J kg-1) end do ! define the final enthalpy vector @@ -283,10 +280,10 @@ end subroutine T2L_lookup_soil ! ************************************************************************************************************************ -! public subroutine enthalpy2T_snow: compute temperature based on specific temperature component of enthalpy -! appropriate when no dry mass, as in snow +! public subroutine enthalpy2T_snowlu: compute temperature based on specific temperature component of enthalpy +! appropriate when no dry mass, as in snow. Uses look-up table for snow enthalpy ! ************************************************************************************************************************ -subroutine enthalpy2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) +subroutine enthalpy2T_snowlu(Hy,BulkDenWater,fc_param,Tk,err,message) ! ------------------------------------------------------------------------------------------------------------------------- implicit none ! ------------------------------------------------------------------------------------------------------------------------- @@ -312,7 +309,7 @@ subroutine enthalpy2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) real(rkind) :: dT ! temperature increment ! ------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="enthalpy2T_snow/" + err=0; message="enthalpy2T_snowlu/" ! convert input of total enthalpy (J m-3) to total specific enthalpy (J kg-1) H_spec = Hy/BulkDenWater ! (NOTE: no soil) @@ -322,8 +319,8 @@ subroutine enthalpy2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) Tg0 = (H_spec - H_lookup(1))/Cp_ice + T_lookup(1) Tg1 = Tg0+dx ! compute enthalpy - Ht0 = T2enthalpy_snow(Tg0,1._rkind,fc_param) - Ht1 = T2enthalpy_snow(Tg1,1._rkind,fc_param) + Ht0 = T2enthalpy_snowlu(Tg0,1._rkind,fc_param) + Ht1 = T2enthalpy_snowlu(Tg1,1._rkind,fc_param) ! compute function evaluations f0 = Ht0 - H_spec f1 = Ht1 - H_spec @@ -362,7 +359,7 @@ subroutine enthalpy2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) ! comute new value of Tg Tg1 = Tg0+dT ! get new function evaluation - Ht1 = T2enthalpy_snow(Tg1,1._rkind,fc_param) + Ht1 = T2enthalpy_snowlu(Tg1,1._rkind,fc_param) f1 = Ht1 - H_spec ! compute derivative of dT dh = (f1 - f0)/dT @@ -379,26 +376,26 @@ subroutine enthalpy2T_snow(Hy,BulkDenWater,fc_param,Tk,err,message) ! and check for convergence if(iter==niter)then; err=20; message=trim(message)//"failedToConverge"; return; end if end do ! (iteration loop) -end subroutine enthalpy2T_snow +end subroutine enthalpy2T_snowlu ! ************************************************************************************************************************ -! public function T2enthalpy_snow: compute liquid and ice mixture enthalpy based on temperature and mass (J m-3) for a -! layer only where the layer has no dry mass, as in snow -! NOTE: enthalpy is a relative value, defined as zero at Tfreeze where all water is liquid +! public function T2enthalpy_snowlu: compute liquid and ice mixture enthalpy based on temperature and mass (J m-3) for a +! layer only where the layer has no dry mass, as in snow. Uses look-up table for snow enthalpy. +! NOTE: enthalpy is a relative value, defined as zero at Tfreeze where all water is liquid ! ************************************************************************************************************************ -function T2enthalpy_snow(Tk,BulkDenWater,fc_param) +function T2enthalpy_snowlu(Tk,BulkDenWater,fc_param) ! ------------------------------------------------------------------------------------------------------------------------- implicit none ! declare dummy variables - real(rkind),intent(in) :: Tk ! layer temperature (K) - real(rkind),intent(in) :: BulkDenWater ! bulk density of water (kg m-3) - real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) - real(rkind) :: T2enthalpy_snow ! return value of the function, total specific enthalpy (J m-3) + real(rkind),intent(in) :: Tk ! layer temperature (K) + real(rkind),intent(in) :: BulkDenWater ! bulk density of water (kg m-3) + real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) + real(rkind) :: T2enthalpy_snowlu ! return value of the function, total specific enthalpy (J m-3) ! declare local variables - real(rkind) :: frac_liq ! fraction of liquid water - real(rkind) :: enthTempWater ! temperature component of specific enthalpy for total water (liquid and ice) (J kg-1) - real(rkind) :: enthMass ! mass component of specific enthalpy (J kg-1) + real(rkind) :: frac_liq ! fraction of liquid water + real(rkind) :: enthTempWater ! temperature component of specific enthalpy for total water (liquid and ice) (J kg-1) + real(rkind) :: enthMass ! mass component of specific enthalpy (J kg-1) ! ------------------------------------------------------------------------------------------------------------------------- ! compute the fraction of liquid water in the given layer frac_liq = 1._rkind / ( 1._rkind + ( fc_param*( Tfreeze - min(Tk,Tfreeze) ) )**2_i4b ) @@ -413,8 +410,8 @@ function T2enthalpy_snow(Tk,BulkDenWater,fc_param) enthMass = -LH_fus*(1._rkind - frac_liq) ! finally, compute the total enthalpy (J m-3) - T2enthalpy_snow = BulkDenWater*(enthTempWater + enthMass) !+ BulkDenSoil*enthTempSoil -end function T2enthalpy_snow + T2enthalpy_snowlu = BulkDenWater*(enthTempWater + enthMass) !+ BulkDenSoil*enthTempSoil +end function T2enthalpy_snowlu ! ************************************************************************************************************************ @@ -482,12 +479,11 @@ subroutine T2enthTemp(& real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) real(rkind) :: volFracWat ! volumetric fraction of total water, liquid+ice (-) real(rkind) :: diff0 ! temperature difference of Tcrit from Tfreeze - real(rkind) :: diffT ! temperature difference of temp soil from Tfreeze + real(rkind) :: diffT ! temperature difference of temp from Tfreeze real(rkind) :: integral ! integral of snow freezing curve - real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head - real(rkind) :: dL ! derivative of enthalpy with temperature at layer temperature - real(rkind) :: arg ! argument of hypergeometric function - real(rkind) :: gauss_hg_T ! hypergeometric function result + real(rkind) :: dL ! derivative of soil lookup table with temperature at layer temperature + real(rkind) :: arg ! argument of soil hypergeometric function + real(rkind) :: gauss_hg_T ! soil hypergeometric function result real(rkind) :: integral_unf ! integral of unfrozen soil water content (from Tfreeze to Tcrit) real(rkind) :: integral_frz_low ! lower limit of integral of frozen soil water content (from Tfreeze to Tcrit) real(rkind) :: integral_frz_upp ! upper limit of integral of frozen soil water content (from Tfreeze to soil temperature) @@ -507,8 +503,6 @@ subroutine T2enthTemp(& generalVars: associate(& ! number of model layers, and layer type nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers ! mapping between the full state vector and the state subset ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset @@ -561,7 +555,7 @@ subroutine T2enthTemp(& snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! scaling parameter for the snow freezing curve (K-1) ) - diffT = scalarCanopyTempTrial - Tfreeze + diffT = scalarCanopyTempTrial - Tfreeze enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth if(diffT>=0._rkind)then @@ -569,8 +563,8 @@ subroutine T2enthTemp(& enthIce = 0._rkind else integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth - enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth + enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth + enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth endif scalarCanopyEnthTemp = enthVeg + enthLiq + enthIce @@ -584,11 +578,11 @@ subroutine T2enthTemp(& snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) ) - diffT = mLayerTempTrial(iLayer) - Tfreeze ! diffT<0._rkind because snow is frozen + diffT = mLayerTempTrial(iLayer) - Tfreeze ! diffT<0._rkind because snow is frozen integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral - enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) - enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) + enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral + enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) + enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) mLayerEnthTemp(iLayer) = enthLiq + enthIce + enthAir @@ -605,11 +599,11 @@ subroutine T2enthTemp(& ) ! end associate statement ! diagnostic variables - vGn_m = 1._rkind - 1._rkind/vGn_n - Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) + vGn_m = 1._rkind - 1._rkind/vGn_n + Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) volFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - diffT = mLayerTempTrial(iLayer) - Tfreeze - diff0 = Tcrit - Tfreeze + diffT = mLayerTempTrial(iLayer) - Tfreeze + diff0 = Tcrit - Tfreeze ! *** compute enthalpy of water for unfrozen conditions if(mlayerTempTrial(iLayer)>=Tcrit)then enthWater = iden_water * Cp_water * volFracWat * diffT ! valid for temperatures below freezing also @@ -620,10 +614,7 @@ subroutine T2enthTemp(& ! get the unfrozen water content integral_unf = ( Tcrit - Tfreeze ) * volFracWat - ! get the frozen water content - ! initialize for case Tcrit=Tfreeze, i.e. mLayerMatricHeadTrial(ixControlIndex)>0 - integral_frz_low = 0._rkind - + ! get the frozen water content if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature ! make associate to the the lookup table lookVars: associate(& @@ -636,6 +627,8 @@ subroutine T2enthTemp(& if(diff0<0._rkind)then call splint(Tk,Ly,L2,Tcrit,integral_frz_low,dL,err,cmessage) if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + else ! Tcrit=Tfreeze, i.e. mLayerMatricHeadTrial(ixControlIndex)>0 + integral_frz_low = 0._rkind end if ! get the upper limit of the integral call splint(Tk,Ly,L2,mlayerTempTrial(iLayer),integral_frz_upp,dL,err,cmessage) @@ -646,21 +639,23 @@ subroutine T2enthTemp(& else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature ! get the lower limit of the integral if(diff0<0._rkind)then - arg = (vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n - gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + arg = (vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n + gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) integral_frz_low = diff0 * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + else ! Tcrit=Tfreeze, i.e. mLayerMatricHeadTrial(ixControlIndex)>0 + integral_frz_low = 0._rkind end if ! get the upper limit of the integral - xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) - mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution, DIFFERENT from the liquid water matric potential used in the flux calculations - arg = (vGn_alpha * mLayerPsiLiq)**vGn_n - gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) + mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution, DIFFERENT from the liquid water matric potential used in the flux calculations + arg = (vGn_alpha * mLayerPsiLiq)**vGn_n + gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) integral_frz_upp = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) endif - enthLiq = iden_water * Cp_water * (integral_unf + integral_frz_upp - integral_frz_low) - enthIce = iden_ice * Cp_ice * ( volFracWat * diffT - (integral_unf + integral_frz_upp - integral_frz_low) ) - enthWater = enthIce + enthLiq + enthLiq = iden_water * Cp_water * (integral_unf + integral_frz_upp - integral_frz_low) + enthIce = iden_ice * Cp_ice * ( volFracWat * diffT - (integral_unf + integral_frz_upp - integral_frz_low) ) + enthWater = enthLiq + enthIce if(doTest)then @@ -679,8 +674,8 @@ subroutine T2enthTemp(& endif ! (if frozen conditions) - enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*diffT - enthAir = iden_air*Cp_air*(1._rkind - theta_sat - volFracWat)*diffT + enthSoil = soil_dens_intr * Cp_soil * ( 1._rkind - theta_sat ) * diffT + enthAir = iden_air * Cp_air * ( 1._rkind - theta_sat - volFracWat ) * diffT mLayerEnthTemp(iLayer) = enthWater + enthSoil + enthAir @@ -742,11 +737,9 @@ subroutine enthTemp2enthalpy(& integer(i4b) :: ixControlIndex ! index within a given model domain ! ------------------------------------------------------------------------------------------------------------------------ ! make association with variables in the data structures - generalVars: associate(& + associate(& ! number of model layers, and layer type nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers ! mapping between the full state vector and the state subset ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset @@ -784,7 +777,7 @@ subroutine enthTemp2enthalpy(& scalarCanopyH= scalarCanopyH - LH_fus * scalarCanopyIce/ canopyDepth case(iname_snow) iLayer = ixControlIndex - mLayerH(iLayer) = mLayerH(iLayer) - iden_ice * LH_fus * mLayerVolFracIce(iLayer) + mLayerH(iLayer) = mLayerH(iLayer) - iden_ice * LH_fus * mLayerVolFracIce(iLayer) case(iname_soil) iLayer = ixControlIndex + nSnow mLayerH(iLayer) = mLayerH(iLayer) - iden_water * LH_fus * mLayerVolFracIce(iLayer) @@ -795,7 +788,7 @@ subroutine enthTemp2enthalpy(& end if ! if an energy layer end do ! looping through state variables - end associate generalVars + end associate end subroutine enthTemp2enthalpy @@ -808,7 +801,6 @@ subroutine enthalpy2T(& ! input: data structures diag_data, & ! intent(in): model diagnostic variables for a local HRU mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices lookup_data, & ! intent(in): lookup table data structure ! input: enthalpy state variables scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) @@ -833,18 +825,29 @@ subroutine enthalpy2T(& err,cmessage) ! intent(out): error control ! ------------------------------------------------------------------------------------------------------------------------- ! downwind routines - USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water - USE spline_int_module,only:splint ! use for cubic spline interpolation + USE spline_int_module,only:splint ! use for cubic spline interpolation + USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) + USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) + USE soil_utils_module,only:dTheta_dTk ! differentiate the freezing curve w.r.t. temperature (soil) + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water + USE soil_utils_module,only:dTheta_dPsi ! compute derivative of the soil moisture characteristic w.r.t. psi (m-1) + USE soil_utilsAddPrime_module,only:d2Theta_dTk2 ! second derivative in the freezing curve w.r.t. temperature (soil) + implicit none ! delare dummy variables ! ------------------------------------------------------------------------------------------------------------------------- logical(lgt),intent(in) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function + ixDomainType, & ! intent(in): named variables defining the domain (iname_cas, iname_veg, etc.) + iLayer, & ! intent(in): index of layer within the snow+soil domain + ixControlIndex, & ! intent(in): index within a given domain ! input: data structures type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model indices type(zLookup),intent(in) :: lookup_data ! lookup tables + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: ixDomainType ! name of a given model domain + integer(i4b) :: ixControlIndex ! index within a given model domain ! output: enthalpy state variables real(rkind),intent(in) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) real(rkind),intent(in) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) @@ -870,71 +873,68 @@ subroutine enthalpy2T(& ! ------------------------------------------------------------------------------------------------------------------------- ! declare local variables character(len=128) :: cmessage ! error message in downwind routine - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixDomainType ! name of a given model domain - integer(i4b) :: ixControlIndex ! index within a given model domain real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) real(rkind) :: volFracWat ! volumetric fraction of total water, liquid+ice (-) real(rkind) :: diff0 ! temperature difference of Tcrit from Tfreeze - real(rkind) :: diffT ! temperature difference of temp soil from Tfreeze - real(rkind) :: integral ! integral of snow freezing curve real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head - real(rkind) :: dL ! derivative of enthalpy with temperature at layer temperature - real(rkind) :: arg ! argument of hypergeometric function - real(rkind) :: gauss_hg_T ! hypergeometric function result + real(rkind) :: dL ! derivative of soil lookup table with temperature at layer temperature real(rkind) :: integral_unf ! integral of unfrozen soil water content (from Tfreeze to Tcrit) real(rkind) :: integral_frz_low ! lower limit of integral of frozen soil water content (from Tfreeze to Tcrit) - real(rkind) :: integral_frz_upp ! upper limit of integral of frozen soil water content (from Tfreeze to soil temperature) real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) - logical(lgt),parameter :: doTest=.false. ! flag to run unit test + real(rkind) :: dvolFracWat_dPsi0 ! derivative of the soil water content w.r.t. matric head + real(rkind) :: dintegral_unf_dWat ! derivative of integral of unfrozen soil water content with water content + real(rkind) :: dintegral_frz_low_dWat ! derivative of integral of frozen soil water content with water content + ! iteration variables + real(rkind) :: T ! iteration temperature (K) + real(rkind) :: H ! iteration enthalpy (J m-3) + real(rkind) :: diffT ! temperature difference of iteration temp from Tfreeze + real(rkind) :: integral ! iteration integral of snow freezing curve + real(rkind) :: fLiq ! iteration fraction liquid water + real(rkind) :: integral_frz_upp ! upper limit of iteration integral of frozen soil water content (from Tfreeze to soil temperature) + real(rkind) :: arg ! argument of iteration soil hypergeometric function + real(rkind) :: gauss_hg_T ! iteration soil hypergeometric function result + real(rkind) :: enthVeg ! iteration enthalpy of the vegetation (J m-3) + real(rkind) :: enthSoil ! iteration enthalpy of soil particles (J m-3) + real(rkind) :: enthLiq ! iteration enthalpy of the liquid region (J m-3) + real(rkind) :: enthIce ! iteration enthalpy of the ice region (J m-3) + real(rkind) :: enthAir ! iteration enthalpy of air (J m-3) + ! iteration variable derivatives + real(rkind) :: dT_dEnthalpy ! derivative of iteration temperature with enthalpy state variable + real(rkind) :: dT_dWat ! derivative of iteration temperature with water state variable + real(rkind) :: dH_dT ! derivative of iteration enthalpy with iteration temperature + real(rkind) :: d2H_dT2 ! second derivative of iteration enthalpy with iteration temperature + real(rkind) :: dH_dEnthalpy ! derivative of iteration enthalpy with enthalpy state variable + real(rkind) :: dH_dT__dEnthalpy ! derivative of iteration enthalpy with iteration temperature with enthalpy + real(rkind) :: dH_dWat ! derivative of iteration enthalpy with water state variable + real(rkind) :: dH_dT__dWat ! derivative of iteration enthalpy with iteration temperature with water state variable + real(rkind) :: dfLiq_dT ! derivative of iteration fraction liquid water with iteration temperature + real(rkind) :: denthVeg_dT ! derivative of iteration enthalpy of vegetation with iteration temperature + real(rkind) :: denthSoil_dT ! derivative of iteration enthalpy of soil with iteration temperature + real(rkind) :: denthIce_dT ! derivative of iteration enthalpy of ice with iteration temperature + real(rkind) :: denthLiq_dT ! derivative of iteration enthalpy of liquid water with iteration temperature + real(rkind) :: denthAir_dT ! derivative of iteration enthalpy of air with temperature + real(rkind) :: d2fLiq_dT2 ! second derivative of iteration fraction liquid water with iteration temperature + real(rkind) :: d2enthVeg_dT2 ! second derivative of iteration enthalpy of vegetation with iteration temperature + real(rkind) :: d2enthSoil_dT2 ! second derivative of iteration enthalpy of soil with iteration temperature + real(rkind) :: d2enthLiq_dT2 ! second derivative of iteration enthalpy of liquid water with iteration temperature + real(rkind) :: d2enthIce_dT2 ! second derivative of iteration enthalpy of ice with iteration temperature + real(rkind) :: d2enthAir_dT2 ! second derivative of iteration enthalpy of air with iteration temperature + real(rkind) :: denthVeg_dWat ! derivative of iteration enthalpy of vegetation with water state variable + real(rkind) :: denthSoil_dWat ! derivative of iteration enthalpy of soil with water state variable + real(rkind) :: denthIce_dWat ! derivative of iteration enthalpy of ice with water state variable + real(rkind) :: denthLiq_dWat ! derivative of iteration enthalpy of liquid water with water state variable + real(rkind) :: denthAir_dWat ! derivative of iteration enthalpy of air with water state variable + real(rkind) :: denthVeg_dT__dWat ! derivative of iteration enthalpy of vegetation with iteration temperature and water state variable + real(rkind) :: denthSoil_dT__dWat ! derivative of iteration enthalpy of soil with iteration temperature and water state variable + real(rkind) :: denthIce_dT__dWat ! derivative of iteration enthalpy of ice with iteration temperaturewith water state variable + real(rkind) :: denthLiq_dT__dWat ! derivative of iteration enthalpy of liquid water with iteration temperature and water state variable + real(rkind) :: denthAir_dT__dWat ! derivative of iteration enthalpy of air with iteration temperature with water state variable + ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - generalVars: associate(& - ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers - ! mapping between the full state vector and the state subset - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset - ! type of domain, type of state variable, and index of control volume within domain - ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat & ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ) ! end associate statement - ! ------------------------------------------------------------------------------------------------------------------------------ - ! initialize error control - err=0; message="enthTemp2T/" - - ! loop through model state variables - do iState=1,size(ixMapSubset2Full) - - ! ----- - ! - compute indices... - ! -------------------- - - ! get domain type, and index of the control volume within the domain - ixFullVector = ixMapSubset2Full(iState) ! index within full state vector - ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) - ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain - - ! check an energy state - if(ixStateType(ixFullVector)==iname_nrgCanair .or. ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer)then - - ! get the layer index - select case(ixDomainType) - case(iname_cas); iLayer = integerMissing - case(iname_veg); iLayer = integerMissing - case(iname_snow); iLayer = ixControlIndex - case(iname_soil); iLayer = ixControlIndex + nSnow - case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) - case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return - end select + err=0; message="enthalpy2T/" ! identify domain select case(ixDomainType) @@ -949,74 +949,70 @@ subroutine enthalpy2T(& specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! specific heat of vegetation (J kg-1 K-1) maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! maximum mass of vegetation (kg m-2) snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! scaling parameter for the snow freezing curve (K-1) - ) - - diffT = scalarCanopyTempTrial - Tfreeze - enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth - - if(diffT>=0._rkind)then - enthLiq = Cp_water * scalarCanopyWatTrial * diffT / canopyDepth - enthIce = 0._rkind - else - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - ! Taylor-Maclaurin series for arctan - !integral = (1._rkind/snowfrz_scale) * (snowfrz_scale * diffT - (snowfrz_scale * diffT)**3_i4b/3._rkind + (snowfrz_scale * diffT)**5_i4b/5._rkind - (snowfrz_scale * diffT)**7_i4b/7._rkind + (snowfrz_scale * diffT)**9_i4b/9._rkind) - - fLiq = 1._rkind / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b ) - - enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth - enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth - endif - + ) ! ***** get temperature if unfrozen vegetation - T = scalarCanopyEnthalpy * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial ) + Tfreeze - dT_dH = canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial ) - dT_dW = -Cp_water * scalarCanopyEnthalpy * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial )**2_i4b + T = scalarCanopyEnthalpy * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial ) + Tfreeze + dT_dEnthalpy = canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial ) + dT_dWat = -Cp_water * scalarCanopyEnthalpy * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial )**2_i4b ! ***** iterate to find temperature if ice exists if( T1.e-6_rkind) - ! compute enthalpy function, H + do while( abs(scalarCanopyEnthalpyTrial-H)>1.e-6_rkind ) + ! compute iteration enthalpy function, H diffT = T - Tfreeze integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - fLiq = 1._rkind / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b ) - H = ( (specificHeatVeg * maxMassVegetation + Cp_ice * scalarCanopyWatTrial )* diffT + (Cp_water - Cp_ice)* scalarCanopyWatTrial * integral & - + LH_fus * (1._rkind - fLiq) * scalarCanopyWatTrial )/ canopyDepth - - ! compute derivative of H with respect to T - dintegral_dT = (1._rkind/snowfrz_scale) / (1._rkind + (snowfrz_scale * diffT)**2_i4b) - d2integral_dT2 = -2._rkind * snowfrz_scale * diffT / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**2_i4b - dfLiq_dT = -2._rkind * snowfrz_scale * diffT / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**2_i4b - d2fLiq_dT2 = 2._rkind * snowfrz_scale**2_i4b * ( 3._rkind * diffT**2_i4b - 1._rkind ) / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**3_i4b - dH_dT = ( specificHeatVeg * maxMassVegetation + Cp_ice * scalarCanopyWatTrial + (Cp_water - Cp_ice)* scalarCanopyWatTrial * dintegral_dT & - - LH_fus * dfLiq_dT * scalarCanopyWatTrial )/ canopyDepth - d2H_dT2 = ( (Cp_water - Cp_ice)* scalarCanopyWatTrial * d2integral_dT2 - LH_fus * d2fLiq_dT2 * scalarCanopyWatTrial )/ canopyDepth - - ! compute derivative of H with respect to canopy enthalpy - dH_dH = dH_dT * dT_dH - dH_dT__dH = d2H_dT2 * dT_dH - - ! compute derivative of H with respect to canopy water - dH_dW = dH_dT * dT_dW + ( Cp_ice * diffT + (Cp_water - Cp_ice) * integral + LH_fus * (1._rkind - fLiq) )/ canopyDepth - dH_dT__dW = d2H_dT2 * dT_dW + ( Cp_ice * (1.0_rkind - dintegral_dT) - LH_fus * dfLiq )/ canopyDepth - + fLiq = fracLiquid(T, snowfrz_scale) + enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth + enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth + enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth + H = enthVeg + enthLiq + enthIce - LH_fus * (1._rkind - fLiq) * scalarCanopyWatTrial / canopyDepth + + ! compute derivative of iteration H with respect to iteration T + ! NOTE: dintegral_dT = fLiq, d2integral_dT2 = dfLiq_dT + dfLiq_dT = dFracLiq_dTk(T,snowfrz_scale) + denthVeg_dT = specificHeatVeg * maxMassVegetation / canopyDepth + denthLiq_dT = Cp_water * scalarCanopyWatTrial * fLiq / canopyDepth + denthIce_dT = Cp_ice * scalarCanopyWatTrial * (1._rkind - fLiq) / canopyDepth + dH_dT = denthVeg_dT + denthLiq_dT + denthIce_dT + LH_fus * dfLiq_dT * scalarCanopyWatTrial / canopyDepth + + d2fLiq_dT2 = 2._rkind * snowfrz_scale**2_i4b * ( 3._rkind * diffT**2_i4b - 1._rkind ) / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**3_i4b + d2enthVeg_dT2 = 0._rkind + d2enthLiq_dT2 = Cp_water * scalarCanopyWatTrial * dfLiq_dT / canopyDepth + d2enthIce_dT2 = -Cp_ice * scalarCanopyWatTrial * dfLiq_dT / canopyDepth + d2H_dT2 = d2enthVeg_dT2 + d2enthLiq_dT2 + d2enthIce_dT2 + LH_fus * d2fLiq_dT2 * scalarCanopyWatTrial / canopyDepth + + ! compute derivative of iteration H with respect to canopy enthalpy + dH_dEnthalpy = dH_dT * dT_dEnthalpy + dH_dT__dEnthalpy = d2H_dT2 * dT_dEnthalpy + + ! compute derivative of iteration H with respect to canopy water + denthVeg_dWat = 0._rkind + denthLiq_dWat = Cp_water * integral / canopyDepth + denthIce_dWat = Cp_ice * ( diffT - integral ) / canopyDepth + dH_dWat = dH_dT * dT_dWat + denthVeg_dWat + denthLiq_dWat + denthIce_dWat - LH_fus * (1._rkind - fLiq) / canopyDepth + + denthVeg_dT__dWat = 0._rkind + denthLiq_dT__dWat = Cp_water * fLiq / canopyDepth + denthIce_dT__dWat = Cp_ice * (1._rkind - fLiq) / canopyDepth + dH_dT__dWat = d2H_dT2 * dT_dWat + denthVeg_dT__dWat + denthLiq_dT__dWat + denthIce_dT__dWat + LH_fus * dfLiq_dT / canopyDepth + ! compute change in T and update, including derivatives - T1 = T - (H - scalarCanopyEnthalpyTrial)/dH_dT - dT_dH = dT_dH - ( dH_dH - 1._rkind - dH_dT__dH * (H - scalarCanopyEnthalpyTrial)/dH_dT ) /dH_dT - dT_dW = dT_dW - ( dH_dW - dH_dT__dW * (H - scalarCanopyEnthalpyTrial)/dH_dT ) /dH_dT + T = T - (H - scalarCanopyEnthalpyTrial)/dH_dT + dT_dEnthalpy = dT_dEnthalpy - ( dH_dEnthalpy - 1._rkind - dH_dT__dEnthalpy * (H - scalarCanopyEnthalpyTrial)/dH_dT ) / dH_dT + dT_dWat = dT_dWat - ( dH_dWat - dH_dT__dWat * (H - scalarCanopyEnthalpyTrial)/dH_dT ) / dH_dT end do endif ! (if ice exists) ! update temperature and derivatives scalarCanopyTempTrial = T - dCanopyTemp_dEnthalpy = dT_dH - dCanopyTemp_dCanWat = dT_dW + dCanopyTemp_dEnthalpy = dT_dEnthalpy + dCanopyTemp_dCanWat = dT_dWat end associate vegVars @@ -1028,63 +1024,60 @@ subroutine enthalpy2T(& ) ! ***** iterate to find temperature, ice always exists - T = 260._rkind ! initial guess, very cold - H = mLayerEnthalpy(iLayer) + 1._rkind ! to start the iteration - dT_dH = 0._rkind - dT_dW = 0._rkind + T = 260._rkind ! initial guess, very cold + H = mLayerEnthalpy(iLayer) + 1._rkind ! to start the iteration + dT_dEnthalpy = 0._rkind + dT_dWat = 0._rkind - - do while(abs(mLayerEnthalpy(iLayer)-H)>1.e-6_rkind) - ! compute enthalpy function, H + do while( abs(mLayerEnthalpy(iLayer)-H)>1.e-6_rkind ) + ! compute iteration enthalpy function, H diffT = T - Tfreeze integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - fLiq = 1._rkind / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b ) - H = (specificHeatVeg * maxMassVegetation + Cp_ice * mLayerVolFracWatTrial(iLayer) )* diffT + (Cp_water - Cp_ice)* mLayerVolFracWatTrial(iLayer) * integral & - + LH_fus * (1._rkind - fLiq) * mLayerVolFracWatTrial(iLayer) - - ! compute derivative of H with respect to T - dintegral_dT = (1._rkind/snowfrz_scale) / (1._rkind + (snowfrz_scale * diffT)**2_i4b) - d2integral_dT2 = -2._rkind * snowfrz_scale * diffT / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**2_i4b - dfLiq_dT = -2._rkind * snowfrz_scale * diffT / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**2_i4b - d2fLiq_dT2 = 2._rkind * snowfrz_scale**2_i4b * ( 3._rkind * diffT**2_i4b - 1._rkind ) / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**3_i4b - dH_dT = specificHeatVeg * maxMassVegetation + Cp_ice * mLayerVolFracWatTrial(iLayer) + (Cp_water - Cp_ice)* mLayerVolFracWatTrial(iLayer) * dintegral_dT & - - LH_fus * dfLiq_dT * mLayerVolFracWatTrial(iLayer) - d2H_dT2 = ( (Cp_water - Cp_ice)* mLayerVolFracWatTrial(iLayer) * d2integral_dT2 - LH_fus * d2fLiq_dT2 * mLayerVolFracWatTrial(iLayer) )/ canopyDepth - - ! compute derivative of H with respect to layer enthalpy - dH_dH = dH_dT * dT_dH - dH_dT__dH = d2H_dT2 * dT_dH - - ! compute derivative of H with respect to layer water content - dH_dW = dH_dT * dT_dW + Cp_ice * diffT + (Cp_water - Cp_ice) * integral + LH_fus * (1._rkind - fLiq) - dH_dT__dW = d2H_dT2 * dT_dW + Cp_ice * (1.0_rkind - dintegral_dT) - LH_fus * dfLiq + fLiq = fracLiquid(T, snowfrz_scale) + enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral + enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) + enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) + H = enthLiq + enthIce + enthAir - iden_water * LH_fus * (1._rkind - fLiq) * mLayerVolFracWatTrial(iLayer) + + ! compute derivative of iteration H with respect to iteration T + ! NOTE: dintegral_dT = fLiq, d2integral_dT2 = dfLiq_dT + dfLiq_dT = dFracLiq_dTk(T,snowfrz_scale) + denthLiq_dT = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * fLiq + denthIce_dT = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * (1._rkind - fLiq) + denthAir_dT = iden_air * Cp_air * (1._rkind - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(1._rkind-fLiq) + fLiq ) ) + dH_dT = denthLiq_dT + denthIce_dT + denthAir_dT + iden_water * LH_fus * dfLiq_dT * mLayerVolFracWatTrial(iLayer) + + d2fLiq_dT2 = 2._rkind * snowfrz_scale**2_i4b * ( 3._rkind * diffT**2_i4b - 1._rkind ) / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**3_i4b + d2enthLiq_dT2 = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * dfLiq_dT + d2enthIce_dT2 = -iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * dfLiq_dT + d2enthAir_dT2 = iden_air * Cp_air * ( mLayerVolFracWatTrial(iLayer) * dfLiq_dT *( (iden_water/iden_ice) - 1._rkind ) ) + d2H_dT2 = d2enthLiq_dT2 + d2enthIce_dT2 + d2enthAir_dT2 + iden_water * LH_fus * d2fLiq_dT2 * mLayerVolFracWatTrial(iLayer) + + ! compute derivative of iteration H with respect to layer enthalpy + dH_dEnthalpy = dH_dT * dT_dEnthalpy + dH_dT__dEnthalpy = d2H_dT2 * dT_dEnthalpy + + ! compute derivative ofiteration H with respect to layer water content + denthLiq_dWat = iden_water * Cp_water * integral + denthIce_dWat = iden_water * Cp_ice * ( diffT - integral ) + denthAir_dWat = -iden_air * Cp_air * ( (iden_water/iden_ice)*(diffT-integral) + integral ) + dH_dWat = dH_dT * dT_dWat + denthLiq_dWat + denthIce_dWat + denthAir_dWat - iden_water * LH_fus * (1._rkind - fLiq) + + denthLiq_dT__dWat = iden_water * Cp_water * fLiq + denthIce_dT__dWat = iden_water * Cp_ice * (1._rkind - fLiq) + denthAir_dT__dWat = -iden_air * Cp_air * ( (iden_water/iden_ice)*(1._rkind-fLiq) + fLiq ) + dH_dT__dWat = d2H_dT2 * dT_dWat + denthLiq_dT__dWat + denthIce_dT__dWat + denthAir_dT__dWat + iden_water * LH_fus * dfLiq_dT ! compute change in T and update, including derivatives - T1 = T - (H - mLayerEnthalpy(iLayer))/dH_dT - dT_dH = dT_dH - ( dH_dH - 1._rkind - dH_dT__dH * (H - mLayerEnthalpy(iLayer))/dH_dT ) /dH_dT - dT_dW = dT_dW - ( dH_dW - dH_dT__dW * (H - mLayerEnthalpy(iLayer))/dH_dT ) /dH_dT + T = T - (H - mLayerEnthalpy(iLayer))/dH_dT + dT_dEnthalpy = dT_dEnthalpy - ( dH_dEnthalpy - 1._rkind - dH_dT__dEnthalpy * (H - mLayerEnthalpy(iLayer))/dH_dT ) / dH_dT + dT_dWat = dT_dWat - ( dH_dWat - dH_dT__dWat * (H - mLayerEnthalpy(iLayer))/dH_dT ) / dH_dT end do - - - - - - - - - - - - - - diffT = mLayerTempTrial(iLayer) - Tfreeze ! diffT<0._rkind because snow is frozen - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral - enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) - enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) - - mLayerEnthTemp(iLayer) = enthLiq + enthIce + enthAir + ! update temperature and derivatives + mLayerTemp(iLayer) = T + dTemp_dEnthalpy(iLayer) = dT_dEnthalpy + dTemp_dTheta(iLayer) = dT_dWat end associate snowVars @@ -1099,84 +1092,141 @@ subroutine enthalpy2T(& ) ! end associate statement ! diagnostic variables - vGn_m = 1._rkind - 1._rkind/vGn_n - Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) - volFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - diffT = mLayerTempTrial(iLayer) - Tfreeze - diff0 = Tcrit - Tfreeze - ! *** compute enthalpy of water for unfrozen conditions - if(mlayerTempTrial(iLayer)>=Tcrit)then - enthWater = iden_water * Cp_water * volFracWat * diffT ! valid for temperatures below freezing also + vGn_m = 1._rkind - 1._rkind/vGn_n + Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) + volFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + dTcrit_dPsi0 = merge(gravity*Tfreeze/LH_fus,0._rkind,mLayerMatricHeadTrial(ixControlIndex)<=0._rkind) + dvolFracWat_dPsi0 = dTheta_dPsi(mLayerMatricHead(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - ! *** compute enthalpy of water for frozen conditions - else - ! *** compute integral of mLayerPsiLiq from Tfreeze to layer temperature - ! get the unfrozen water content - integral_unf = ( Tcrit - Tfreeze ) * volFracWat + ! ***** get temperature if unfrozen soil + T = mLayerEnthalpy(iLayer) / ( iden_water * Cp_water * volFracWat + soil_dens_intr * Cp_soil * (1._rkind - theta_sat) + iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) ) + Tfreeze + dT_dEnthalpy = 1._rkind / ( iden_water * Cp_water * volFracWat + soil_dens_intr*Cp_soil*(1._rkind - theta_sat) + iden_air*Cp_air*(1._rkind - theta_sat - volFracWat) ) + dT_dWat = -iden_water * Cp_water * dTheta_dWat * mLayerEnthalpy(iLayer) / ( iden_water * Cp_water * volFracWat + soil_dens_intr * Cp_soil * (1._rkind - theta_sat) + iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) )**2_i4b - ! get the frozen water content - ! initialize for case Tcrit=Tfreeze, i.e. mLayerMatricHeadTrial(ixControlIndex)>0 - integral_frz_low = 0._rkind + ! ***** iterate to find temperature if ice exists + if( T lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup, & ! temperature (K) - Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup, & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) - L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function - ) ! end associate statement + ! *** compute integral of mLayerPsiLiq from Tfreeze to layer temperature + ! get the unfrozen water content of enthalpy + integral_unf = ( Tcrit - Tfreeze ) * volFracWat ! unfrozen water content + dintegral_unf_dWat = dTcrit_dPsi0 * volFracWat + ( Tcrit - Tfreeze ) * dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + + ! get the frozen water content of enthalpy, statrt with lower limit of the integral + diff0 = Tcrit - Tfreeze + if(diff0<0._rkind)then + + if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature + ! make associate to the the lookup table + lookVars: associate(& + Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup, & ! temperature (K) + Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup, & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) + L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + ) ! end associate statement - ! get the lower limit of the integral - if(diff0<0._rkind)then call splint(Tk,Ly,L2,Tcrit,integral_frz_low,dL,err,cmessage) if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - end if - ! get the upper limit of the integral - call splint(Tk,Ly,L2,mlayerTempTrial(iLayer),integral_frz_upp,dL,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - end associate lookVars + dintegral_frz_low_dWat = dL * dTcrit_dPsi0 + + end associate lookVars + + else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature + arg = (vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n + gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + dgauss_hg_T_dT = ( (1._rkind + arg)**(-vGn_m) - gauss_hg_T ) / vGn_n*(-arg) + dintegral_frz_low_dWat = volFracWat * dTcrit_dPsi0 + endif + else ! Tcrit=Tfreeze, i.e. mLayerMatricHeadTrial(ixControlIndex)>0 + integral_frz_low = 0._rkind + dintegral_frz_low_dWat = 0._rkind + end if + + ! get the upper limit of the integral + xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) + do while( abs(mLayerEnthalpy(iLayer)-H)>1.e-6_rkind ) + diffT = T - Tfreeze + mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution, DIFFERENT from the liquid water matric potential used in the flux calculations + arg = (vGn_alpha * mLayerPsiLiq)**vGn_n + + if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature + ! make associate to the the lookup table + lookVars: associate(& + Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup, & ! temperature (K) + Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup, & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) + L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + ) ! end associate statement + + ! get the upper limit of the integral + call splint(Tk,Ly,L2,T,integral_frz_upp,dL,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + dintegral_frz_upp_dT = dL + d2integral_frz_upp_dT2 = 0._rkind - else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature - ! get the lower limit of the integral - if(diff0<0._rkind)then - arg = (vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n - gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - integral_frz_low = diff0 * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + end associate lookVars + + else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature + gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + integral_frz_upp = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + dintegral_frz_upp_dT = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) ! fLiq + d2integral_frz_upp_dT2 = dTheta_dTk(T,theta_res,theta_sat,vGn_alpha,vGn_n,vGn_m) ! dfLiq_dT end if - ! get the upper limit of the integral - xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) - mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution, DIFFERENT from the liquid water matric potential used in the flux calculations - arg = (vGn_alpha * mLayerPsiLiq)**vGn_n - gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - integral_frz_upp = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) - endif - - enthLiq = iden_water * Cp_water * (integral_unf + integral_frz_upp - integral_frz_low) - enthIce = iden_ice * Cp_ice * ( volFracWat * diffT - (integral_unf + integral_frz_upp - integral_frz_low) ) - enthWater = enthIce + enthLiq - if(doTest)then - - ! write values - print*, 'doTest = ', doTest - print*, 'T,Tcrit = ', mlayerTempTrial(iLayer),Tcrit ! temperature (K) - print*, 'integral unf,frz_upp,frz_low = ', integral_unf, integral_frz_upp, integral_frz_low ! integral (K) - print*, 'theta_sat = ', theta_sat ! soil porosity (-) - print*, 'theta_res = ', theta_res ! volumetric residual water content (-) - print*, 'vGn_alpha = ', vGn_alpha ! van Genuchten "alpha" parameter (m-1) - print*, 'vGn_n = ', vGn_n ! van Genuchten "n" parameter (-) - print*, trim(message)//'PAUSE: Set doTest=.false. to complete simulations' - read(*,*) - - endif ! if testing - - endif ! (if frozen conditions) - - enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*diffT - enthAir = iden_air*Cp_air*(1._rkind - theta_sat - volFracWat)*diffT - - mLayerEnthTemp(iLayer) = enthWater + enthSoil + enthAir + ! compute iteration enthalpy function, H + ! NOTE: here fLiq is the total liquid fraction, not fraction of water fraction that is liquid + fLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + enthLiq = iden_water * Cp_water * (integral_unf + integral_frz_upp - integral_frz_low) + enthIce = iden_ice * Cp_ice * ( volFracWat * diffT - (integral_unf + integral_frz_upp - integral_frz_low) ) + enthSoil = soil_dens_intr * Cp_soil * ( 1._rkind - theta_sat ) * diffT + enthAir = iden_air * Cp_air * ( 1._rkind - theta_sat - volFracWat ) * diffT + H = enthLiq + enthIce + enthSoil + enthAir - iden_water * LH_fus * (volFracWat - fLiq) + + ! compute derivative of iteration H with respect to iteration T + dfLiq_dT = dTheta_dTk(T,theta_res,theta_sat,vGn_alpha,vGn_n,vGn_m) + denthLiq_dT = iden_water * Cp_water * dintegral_frz_upp_dT + denthIce_dT = iden_ice * Cp_ice * ( volFracWat - dintegral_frz_upp_dT ) + denthSoil_dT = soil_dens_intr * Cp_soil * ( 1._rkind - theta_sat ) + denthAir_dT = iden_air * Cp_air * ( 1._rkind - theta_sat - volFracWat ) + dH_dT = denthLiq_dT + denthIce_dT + denthSoil_dT + denthAir_dT + iden_water * LH_fus * dfLiq_dT + + d2fLiq_dT2 = d2Theta_dTk2(T,theta_res,theta_sat,vGn_alpha,vGn_n,vGn_m) + d2enthLiq_dT2 = iden_water * Cp_water * d2integral_frz_upp_dT2 + d2enthIce_dT2 = -iden_ice * Cp_ice * d2integral_frz_upp_dT2 + d2enthSoil_dT2 = 0._rkind + d2enthAir_dT2 = 0._rkind + d2H_dT2 = d2enthLiq_dT2 + d2enthIce_dT2 + d2enthSoil_dT2 + d2enthAir_dT2 + iden_water * LH_fus * d2fLiq_dT2 + + ! compute derivative of iteration H with respect to layer enthalpy + dH_dEnthalpy = dH_dT * dT_dEnthalpy + dH_dT__dEnthalpy = d2H_dT2 * dT_dEnthalpy + + ! compute derivative ofiteration H with respect to layer water content + denthLiq_dWat = iden_water * Cp_water * (dintegral_unf_dWat - dintegral_frz_low_dWat) + denthIce_dWat = iden_ice * Cp_ice * ( dvolFracWat_dPsi0 * diffT - (dintegral_unf_dWat - dintegral_frz_low_dWat) ) + denthSoil_dWat = 0._rkind + denthAir_dWat = -iden_air * Cp_air * dvolFracWat_dPsi0 * diffT + dH_dWat = dH_dT * dT_dWat + denthLiq_dWat + denthIce_dWat + denthAir_dWat - iden_water * LH_fus * dvolFracWat_dPsi0 + + denthLiq_dT__dWat = 0._rkind + denthIce_dT__dWat = iden_ice * Cp_ice * dvolFracWat_dPsi0 + denthSoil_dT__dWat = 0._rkind + denthAir_dT__dWat = -iden_air * Cp_air * dvolFracWat_dPsi0 + dH_dT__dWat = d2H_dT2 * dT_dWat + denthLiq_dT__dWat + denthIce_dT__dWat + denthSoil_dT__dWat + denthAir_dT__dWat + + ! compute change in T and update, including derivatives + T = T - (H - mLayerEnthalpy(iLayer))/dH_dT + dT_dEnthalpy = dT_dEnthalpy - ( dH_dEnthalpy - 1._rkind - dH_dT__dEnthalpy * (H - mLayerEnthalpy(iLayer))/dH_dT ) / dH_dT + dT_dWat = dT_dWat - ( dH_dWat - dH_dT__dWat * (H - mLayerEnthalpy(iLayer))/dH_dT ) / dH_dT + end do + end if ! (if ice exists) + + ! update temperature and derivatives + mLayerTemp(iLayer) = T + dTemp_dEnthalpy(iLayer) = dT_dEnthalpy + dTemp_dTheta(iLayer) = realMissing ! do not use + dTemp_dPsi(ixControlIndex) = dT_dWat end associate soilVars @@ -1187,12 +1237,7 @@ subroutine enthalpy2T(& case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return end select - end if ! if an energy layer - end do ! looping through state variables - - end associate generalVars - -end subroutine T2enthTemp +end subroutine enthalpy2T !---------------------------------------------------------------------- diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 89b71fbcf..d3334170b 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -145,148 +145,149 @@ subroutine eval8summa(& ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- ! input: model control - real(rkind),intent(in) :: dt_cur ! current stepsize - real(rkind),intent(in) :: dt ! entire time step for drainage pond rate - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - integer(i4b),intent(in) :: nState ! total number of state variables - logical(lgt),intent(in) :: insideSUN ! flag to indicate if we are inside Sundials solver - logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step - logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call - logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation - logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + real(rkind),intent(in) :: dt_cur ! current stepsize + real(rkind),intent(in) :: dt ! entire time step for drainage pond rate + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + integer(i4b),intent(in) :: nState ! total number of state variables + logical(lgt),intent(in) :: insideSUN ! flag to indicate if we are inside Sundials solver + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call + logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution ! input: state vectors - real(rkind),intent(in) :: stateVec(:) ! model state vector - real(rkind),intent(in) :: fScale(:) ! characteristic scale of the function evaluations - real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + real(rkind),intent(in) :: stateVec(:) ! model state vector + real(rkind),intent(in) :: fScale(:) ! characteristic scale of the function evaluations + real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) ! input: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(zLookup), intent(in) :: lookup_data ! lookup tables - type(var_i), intent(in) :: type_data ! type of vegetation and soil - type(var_d), intent(in) :: attr_data ! spatial attributes - type(var_dlength), intent(in) :: mpar_data ! model parameters - type(var_d), intent(in) :: forc_data ! model forcing data - type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin - type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup), intent(in) :: lookup_data ! lookup tables + type(var_i), intent(in) :: type_data ! type of vegetation and soil + type(var_d), intent(in) :: attr_data ! spatial attributes + type(var_dlength), intent(in) :: mpar_data ! model parameters + type(var_d), intent(in) :: forc_data ! model forcing data + type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin + type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU ! output: data structures - type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! input-output: baseflow - integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! output: flux and residual vectors - logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution - real(rkind),intent(out) :: fluxVec(:) ! flux vector - real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation - real(qp),intent(out) :: resVec(:) ! NOTE: qp ! residual vector - real(rkind),intent(out) :: fEval ! function evaluation + logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution + real(rkind),intent(out) :: fluxVec(:) ! flux vector + real(rkind),intent(out) :: resSink(:) ! sink terms on the RHS of the flux equation + real(qp),intent(out) :: resVec(:) ! NOTE: qp ! residual vector + real(rkind),intent(out) :: fEval ! function evaluation ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- ! state variables - real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial value for temperature of layers in the snow and soil domains (K) - real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial value for volumetric fraction of total water (-) - real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial value for total water matric potential (m) - real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) - real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) + real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial value for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial value for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial value for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) + real(rkind) :: scalarAquiferStorageTrial ! trial value of storage of water in the aquifer (m) ! diagnostic variables - real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) - real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) + real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial value for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial value for volumetric fraction of ice (-) ! enthalpy - real(rkind) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3 - real(rkind) :: scalarCanopyEnthTempTrial ! trial value for temperature component of enthalpy of the vegetation canopy (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthTempTrial ! trial vector of temperature component of enthalpy for snow+soil layers (J m-3) + real(rkind) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3 + real(rkind) :: scalarCanopyEnthTempTrial ! trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthTempTrial ! trial vector of temperature component of enthalpy for snow+soil layers (J m-3) ! other local variables - logical(lgt) :: checkLWBalance ! flag to check longwave balance - integer(i4b) :: iLayer ! index of model layer in the snow+soil domain - integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain - integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine - real(rkind),dimension(nState) :: rVecScaled ! scaled residual vector - character(LEN=256) :: cmessage ! error message of downwind routine - real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy - real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil - logical(lgt) :: updateCp ! flag to indicate if we update Cp at each step, set with nrgConserv choice and updateCp_closedForm flag - logical(lgt) :: needCm ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m,, set with nrgConserv choice and needCm_closedForm flag - logical(lgt),parameter :: updateCp_closedForm=.false. ! nrgConserv = closedForm flag to indicate if we update Cp at each step - logical(lgt),parameter :: needCm_closedForm=.false. ! nrgConserv = closedForm flag to indicate if the energy equation contains Cm = dH_T/dTheta_m + logical(lgt) :: checkLWBalance ! flag to check longwave balance + integer(i4b) :: iLayer ! index of model layer in the snow+soil domain + integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain + integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine + real(rkind),dimension(nState) :: rVecScaled ! scaled residual vector + character(LEN=256) :: cmessage ! error message of downwind routine + real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy + real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil + logical(lgt) :: updateStateCp ! flag to indicate if we update Cp at each step for LHS, set with nrgConserv choice and updateCp_closedForm flag + logical(lgt) :: updateFluxCp ! flag to indicate if we update Cp at each step for RHS, set with nrgConserv choice and updateCp_closedForm flag + logical(lgt) :: needStateCm ! flag to indicate if the energy equation contains LHS Cm = dH_T/dTheta_m,, set with nrgConserv choice and needStateCm_closedForm flag + logical(lgt),parameter :: updateCp_closedForm=.false. ! nrgConserv = closedForm flag to indicate if we update Cp at each step + logical(lgt),parameter :: needCm_closedForm=.false. ! nrgConserv = closedForm flag to indicate if the energy equation contains Cm = dH_T/dTheta_m ! -------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- associate(& ! model decisions - ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver - ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& ! intent(in): [i4b] choice of variable in energy conservation backward Euler residual - ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver + ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& ! intent(in): [i4b] choice of variable in energy conservation backward Euler residual + ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) ! soil parameters - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) - ! canopy and layer depth - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-1) + ! canopy and layer depth + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) ! model state variables from the previous solution - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] total water matric potential (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) - scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in): [dp] storage of water in the aquifer (m) + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] total water matric potential (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) + scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(in): [dp] storage of water in the aquifer (m) ! model diagnostic variables, will be updated before used - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) - scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) + scalarSfcMeltPond => prog_data%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& ! intent(in): [dp] ponded water caused by melt of the "snow without a layer" (kg m-2) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) ! enthalpy from the previous solution - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(in): [dp] enthalpy of the canopy air space (J m-3) - scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! intent(in): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthTemp => diag_data%var(iLookDIAG%mLayerEnthTemp)%dat ,& ! intent(in): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(in): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! intent(in): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthTemp => diag_data%var(iLookDIAG%mLayerEnthTemp)%dat ,& ! intent(in): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) ! soil compression - scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2 s-1) - mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in volumetric water content due to compression of soil (s-1) + scalarSoilCompress => diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ,& ! intent(in): [dp] total change in storage associated with compression of the soil matrix (kg m-2 s-1) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in): [dp(:)] change in volumetric water content due to compression of soil (s-1) ! derivatives - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential - dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1) ,& ! intent(out): [dp] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative in bulk heat capacity w.r.t. temperature - dThermalC_dWatAbove => deriv_data%var(iLookDERIV%dThermalC_dWatAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above - dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above - dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above - dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk)%dat ,& ! intent(out): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) - dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential + dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi)%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t. matric head (m-1) + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1) ,& ! intent(out): [dp] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk)%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative in bulk heat capacity w.r.t. temperature + dThermalC_dWatAbove => deriv_data%var(iLookDERIV%dThermalC_dWatAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above + dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk)%dat ,& ! intent(out): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) + dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) ! mapping - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)]mapping of full state vector to the state subset - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)]index of control volume for different domains (veg, snow, soil) + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)]mapping of full state vector to the state subset + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)]index of control volume for different domains (veg, snow, soil) ! heat capacity - heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(out): [dp] volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(out): [dp(:)] heat capacity for snow and soil + heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(out): [dp] volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(out): [dp(:)] heat capacity for snow and soil ! Cm - canopyCmTrial => diag_data%var(iLookDIAG%scalarCanopyCm)%dat(1) ,& ! intent(out): [dp] Cm of the canopy - mLayerCmTrial => diag_data%var(iLookDIAG%mLayerCm)%dat & ! intent(out): [dp(:)] Cm of snow and soil + canopyCmTrial => diag_data%var(iLookDIAG%scalarCanopyCm)%dat(1) ,& ! intent(out): [dp] Cm of the canopy + mLayerCmTrial => diag_data%var(iLookDIAG%mLayerCm)%dat & ! intent(out): [dp(:)] Cm of snow and soil ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control @@ -317,11 +318,14 @@ subroutine eval8summa(& end if ! ( feasibility check ) if(ixNrgConserv == enthalpyFD .or. ixNrgConserv == enthalpyFDlu)then - updateCp = .true. - needCm = .true. - else if(ixNrgConserv == closedForm)then ! have a choice - updateCp = updateCp_closedForm - needCm = needCm_closedForm + ! use mixed form of energy equation, need these true + updateStateCp = .true. + updateFluxCp = .true. + needStateCm = .true. + else if(ixNrgConserv == closedForm)then ! have a choice, temperature the state variable + updateStateCp = updateCp_closedForm + updateFluxCp = updateCp_closedForm + needStateCm = needCm_closedForm else message=trim(message)//'unknown choice of variable in energy conservation backward Euler residual' err=1; return @@ -400,7 +404,7 @@ subroutine eval8summa(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - if(updateCp)then + if(updateStateCp)then ! *** compute volumetric heat capacity C_p call computHeatCapAnalytic(& ! input: control variables @@ -447,7 +451,16 @@ subroutine eval8summa(& sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) - + else + ! set state heat capacity derivatives to 0 for constant through step + dVolHtCapBulk_dPsi0 = 0._rkind + dVolHtCapBulk_dTheta = 0._rkind + dVolHtCapBulk_dCanWat = 0._rkind + dVolHtCapBulk_dTk = 0._rkind + dVolHtCapBulk_dTkCanopy = 0._rkind + endif ! updateStateCp + + if(updateFluxCp)then ! update thermal conductivity call computThermConduct(& ! input: control variables @@ -478,19 +491,14 @@ subroutine eval8summa(& err,cmessage) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if else - ! set heat capacity derivatives to 0 for constant through step - dVolHtCapBulk_dPsi0 = 0._rkind - dVolHtCapBulk_dTheta = 0._rkind - dVolHtCapBulk_dCanWat = 0._rkind - dVolHtCapBulk_dTk = 0._rkind - dVolHtCapBulk_dTkCanopy = 0._rkind + ! set flux heat capacity derivatives to 0 for constant through step dThermalC_dWatAbove = 0._rkind dThermalC_dWatBelow = 0._rkind dThermalC_dTempAbove = 0._rkind dThermalC_dTempBelow = 0._rkind - endif ! updateCp + endif ! updateFluxCp - if(needCm)then + if(needStateCm)then ! compute C_m call computCm(& ! input: control variables @@ -513,9 +521,9 @@ subroutine eval8summa(& mLayerCmTrial = 0._qp dCm_dTk = 0._rkind dCm_dTkCanopy = 0._rkind - endif ! needCm + endif ! needStateCm - if(ixNrgConserv.ne.closedForm)then ! use residual as enthalpy_delta - (phase change)_delta + if(ixNrgConserv== enthalpyFD .or. ixNrgConserv == enthalpyFDlu)then ! use residual as enthalpy_delta - (phase change)_delta ! compute temperature component of enthalpy call T2enthTemp(& ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 4c46492c6..b95889fae 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -68,6 +68,7 @@ subroutine eval8summaWithPrime(& sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup table data structure type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes mpar_data, & ! intent(in): model parameters @@ -79,26 +80,16 @@ subroutine eval8summaWithPrime(& diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input: previous values of variables needed in data window outside of internal IDA, for root finding - scalarCanopyTempPrev, & ! intent(in): previous value for temperature of the vegetation canopy (K) - mLayerTempPrev, & ! intent(in): previous vector of layer temperature (K) - mLayerMatricHeadPrev, & ! intent(in): previous value for total water matric potential (m) - ! output: new values of variables needed in data window outside of internal IDA, for Jacobian + ! output: new values of variables needed in data window outside of internal IDA, for root finding scalarCanopyTempTrial, & ! intent(out): trial value for temperature of the vegetation canopy (K) - scalarCanopyWatTrial, & ! intent(out): trial value for mass of total water on the vegetation canopy (kg m-2) mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) - mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) ! output: new prime values of variables needed in data window outside of internal IDA for Jacobian - scalarCanairEnthalpyPrime, & ! intent(out): prime value for enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyPrime, & ! intent(out): prime value for enthalpy of the vegetation canopy (W m-3) + scalarCanopyTempPrime, & ! intent(out): prime value for temperature of the vegetation canopy (K s-1) scalarCanopyWatPrime, & ! intent(out): prime value for total water content of the vegetation canopy (kg m-2 s-1) - scalarCanopyIcePrime, & ! intent(out): prime value for mass of ice on the vegetation canopy (kg m-2 s-1) - mLayerEnthalpyPrime, & ! intent(out): prime vector of enthalpy of each snow and soil layer (W m-3) + mLayerTempPrime, & ! intent(out): prime vector of temperature of each snow and soil layer (K s-1) mLayerMatricHeadPrime, & ! intent(out): prime vector of matric head of each snow and soil layer (m s-1) - mLayerMatricHeadLiqPrime, & ! intent(out): prime vector of liquid water matric potential (m s-1) mLayerVolFracWatPrime, & ! intent(out): prime vector of volumetric total water content of each snow and soil layer (s-1) - mLayerVolFracIcePrime, & ! intent(out): prime vector of volumetric fraction of ice (s-1) ! input-output: baseflow ixSaturation, & ! intent(inout): index of the lowest saturated layer dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) @@ -120,7 +111,6 @@ subroutine eval8summaWithPrime(& USE computHeatCap_module, only:computStatMult ! recompute state multiplier USE computResidWithPrime_module,only:computResidWithPrime ! compute residuals given a state vector USE computThermConduct_module,only:computThermConduct ! recompute thermal conductivity and derivatives - USE enthalpyTemp_module,only:enthalpy2T ! compute temperature from enthalpy and water content implicit none ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- @@ -142,37 +132,28 @@ subroutine eval8summaWithPrime(& real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) ! input: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup), intent(in) :: lookup_data ! lookup tables type(var_i), intent(in) :: type_data ! type of vegetation and soil type(var_d), intent(in) :: attr_data ! spatial attributes type(var_dlength), intent(in) :: mpar_data ! model parameters type(var_d), intent(in) :: forc_data ! model forcing data type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU - ! output: data structures + ! output: data structures type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! input: previous values of variables needed in data window outside of internal IDA - real(rkind),intent(in) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) - real(rkind),intent(in) :: mLayerTempPrev(:) ! previous vector of layer temperature (K) - real(rkind),intent(in) :: mLayerMatricHeadPrev(:) ! previous value for total water matric potential (m) - ! output: new values of variables needed in data window outside of internal IDA + ! output: new values of variables needed in data window outside of internal IDA for root real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind),intent(out) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial vector for total water matric potential (m) - real(rkind),intent(out) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - ! output: new prime values of variables needed in data window outside of internal IDA - real(rkind),intent(out) :: scalarCanairEnthalpyPrime ! prime value for enthalpy of the canopy air space (W m-3) - real(rkind),intent(out) :: scalarCanopyEnthalpyPrime ! prime value for enthalpy of the vegetation canopy (W m-3) + ! output: new prime values of variables needed in data window outside of internal IDA for Jacobian + real(rkind),intent(out) :: scalarCanopyTempPrime ! prime value for temperature of the vegetation canopy (K s-1) real(rkind),intent(out) :: scalarCanopyWatPrime ! prime value for total water content of the vegetation canopy (kg m-2 s-1) - real(rkind),intent(out) :: scalarCanopyIcePrime ! prime value for mass of ice on the vegetation canopy (kg m-2 s-1) - real(rkind),intent(out) :: mLayerEnthalpyPrime(:) ! prime vector of enthalpy of each snow and soil layer (W m-3) + real(rkind),intent(out) :: mLayerTempPrime(:) ! prime vector of temperature of each snow and soil layer (K s-1) real(rkind),intent(out) :: mLayerMatricHeadPrime(:) ! prime vector of matric head of each snow and soil layer (m s-1) - real(rkind),intent(out) :: mLayerMatricHeadLiqPrime(:) ! prime vector of liquid water matric potential (m s-1) real(rkind),intent(out) :: mLayerVolFracWatPrime(:) ! prime vector of volumetric total water content of each snow and soil layer (s-1) - real(rkind),intent(out) :: mLayerVolFracIcePrime(:) ! prime vector of volumetric fraction of ice (s-1) ! input-output: baseflow integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) @@ -189,26 +170,37 @@ subroutine eval8summaWithPrime(& ! -------------------------------------------------------------------------------------------------------------------------------- real(rkind) :: dt1 ! residual step size ! state variables - real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) real(rkind) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3) + real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) + real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) real(rkind) :: scalarCanopyLiqTrial ! trial value for liquid water storage in the canopy (kg m-2) real(rkind) :: scalarCanopyIceTrial ! trial value for ice storage in the canopy (kg m-2) real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector of enthalpy of each snow and soil layer (J m-3) + real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector of volumetric total water content (-) real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector of volumetric liquid water content (-) real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector of volumetric ice content (-) real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) + + real(rkind) :: scalarCanairEnthalpyPrime ! prime value for enthalpy of the canopy air space (W m-3) + real(rkind) :: scalarCanairTempPrime ! prime value for temperature of the canopy air space (K s-1) + real(rkind) :: scalarCanopyEnthalpyPrime ! prime value for enthalpy of the vegetation canopy (W m-3) real(rkind) :: scalarCanopyLiqPrime ! prime value for liquid water storage in the canopy (kg m-2 s-1) + real(rkind) :: scalarCanopyIcePrime ! prime value for mass of ice on the vegetation canopy (kg m-2 s-1) + real(rkind),dimension(nLayers) :: mLayerEnthalpyPrime ! prime vector of enthalpy of each snow and soil layer (W m-3) real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! prime vector of volumetric liquid water content (s-1) + real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! prime vector of volumetric fraction of ice (s-1) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! prime vector of liquid water matric potential (m s-1) real(rkind) :: scalarAquiferStoragePrime ! prime value of storage of water in the aquifer (m s-1) ! other local variables integer(i4b) :: iLayer ! index of model layer in the snow+soil domain integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine character(LEN=256) :: cmessage ! error message of downwind routine - logical(lgt) :: updateCp ! flag to indicate if we update Cp at each step, set with nrgConserv choice and updateCp_closedForm flag - logical(lgt) :: needCm ! flag to indicate if the energy equation contains Cm = dH_T/dTheta_m,, set with nrgConserv choice and needCm_closedForm flag + logical(lgt) :: updateStateCp ! flag to indicate if we update Cp at each step for LHS, set with nrgConserv choice and updateCp_closedForm flag + logical(lgt) :: updateFluxCp ! flag to indicate if we update Cp at each step for RHS, set with nrgConserv choice and updateCp_closedForm flag + logical(lgt) :: needStateCm ! flag to indicate if the energy equation contains LHS Cm = dH_T/dTheta_m,, set with nrgConserv choice and needStateCm_closedForm flag logical(lgt),parameter :: updateCp_closedForm=.false. ! nrgConserv = closedForm flag to indicate if we update Cp at each step logical(lgt),parameter :: needCm_closedForm=.false. ! nrgConserv = closedForm flag to indicate if the energy equation contains Cm = dH_T/dTheta_m @@ -288,11 +280,14 @@ subroutine eval8summaWithPrime(& end if ! ( feasibility check ) if(ixNrgConserv == enthalpyFD .or. ixNrgConserv == enthalpyFDlu)then - updateCp = .true. - needCm = .true. - else if(ixNrgConserv == closedForm)then ! have a choice - updateCp = updateCp_closedForm - needCm = needCm_closedForm + ! use enthalpy as state variable, do not need state terms but do need flux term + updateStateCp = .false. + updateFluxCp = .true. + needStateCm = .false. + else if(ixNrgConserv == closedForm)then ! have a choice, temperature the state variable + updateStateCp = updateCp_closedForm + updateFluxCp = updateCp_closedForm + needStateCm = needCm_closedForm else message=trim(message)//'unknown choice of variable in energy conservation backward Euler residual' err=1; return @@ -309,59 +304,50 @@ subroutine eval8summaWithPrime(& endif ! Placeholder: if we decide to use splitting, we need to pass all the previous values of the state variables - scalarCanairEnthalpyTrial = realMissing - scalarCanairTempTrial = realMissing - scalarCanopyEnthalpyTrial = realMissing - scalarCanopyTempTrial = scalarCanopyTempPrev + scalarCanairNrgTrial = realMissing + scalarCanopyNrgTrial = realMissing scalarCanopyWatTrial = realMissing scalarCanopyLiqTrial = realMissing scalarCanopyIceTrial = realMissing - mLayerEnthalpyTrial = realMissing - mLayerTempTrial = mLayerTempPrev + mLayerNrgTrial = realMissing mLayerVolFracWatTrial = realMissing mLayerVolFracLiqTrial = realMissing mLayerVolFracIceTrial = realMissing - mLayerMatricHeadTrial = mLayerMatricHeadPrev + mLayerMatricHeadTrial = realMissing mLayerMatricHeadLiqTrial = realMissing scalarAquiferStorageTrial = realMissing ! extract states from the state vector call varExtract(& ! input - stateVec, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers + stateVec, & ! intent(in): model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy - scalarCanairEnthalpyTrial,& ! intent(inout): trial value of enthalpy of the canopy air space (J m-3) - scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) - scalarCanopyEnthalpyTrial,& ! intent(inout): trial value of enthalpy of the vegetation canopy (J m-3) - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanairNrgTrial, & ! intent(inout): trial value of energy of the canopy air space, temperature (K) or enthalpy (J m-3) + scalarCanopyNrgTrial, & ! intent(inout): trial value of energy of the vegetation canopy, temperature (K) or enthalpy (J m-3) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - mLayerEnthalpyTrial, & ! intent(inout): trial vector of enthalpy of each snow and soil layer (J m-3) - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerNrgTrial, & ! intent(inout): trial vector of energy, temperature (K) or enthalpy (J m-3) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(inout): trial value of storage of water in the aquifer (m) + scalarAquiferStorageTrial, & ! intent(inout): trial value of storage of water in the aquifer (m) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) ! Placeholder: if we decide to use splitting, we need to pass all the previous values of the state variables - scalarCanairEnthalpyPrime = realMissing - scalarCanairTempPrime = realMissing - scalarCanopyEnthalpyPrime = realMissing - scalarCanopyTempPrime = realMissing + scalarCanairNrgPrime = realMissing + scalarCanopyNrgPrime = realMissing scalarCanopyWatPrime = realMissing scalarCanopyLiqPrime = realMissing scalarCanopyIcePrime = realMissing - mLayerEnthalpyPrime = realMissing - mLayerTempPrime = realMissing + mLayerNrgPrime = realMissing mLayerVolFracWatPrime = realMissing mLayerVolFracLiqPrime = realMissing mLayerVolFracIcePrime = realMissing @@ -376,15 +362,12 @@ subroutine eval8summaWithPrime(& prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy - scalarCanairEnthalpyPrime, & ! intent(inout): derivative of enthalpy of the canopy air space (W m-3) - scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K s-1) - scalarCanopyEnthalpyPrime, & ! intent(inout): derivative of enthalpy of the vegetation canopy (W m-3) - scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K s-1) + scalarCanairNrgPrime, & ! intent(inout): derivative of energy of the canopy air space, temperature (K s-1) or enthalpy (W m-3) + scalarCanopyNrgPrime, & ! intent(inout): derivative of energy of the vegetation canopy, temperature (K s-1) or enthalpy (W m-3) scalarCanopyWatPrime, & ! intent(inout): derivative of canopy total water (kg m-2 s-1) scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2 s-1) ! output: variables for the snow-soil domain - mLayerEnthalpyPrime, & ! intent(inout): derivative of enthalpy of each snow and soil layer (W m-3) - mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K s-1) + mLayerNrgPrime, & ! intent(inout): derivative of energy of each snow and soil layer, temperature (K s-1) or enthalpy (W m-3) mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (s-1) mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (s-1) mLayerMatricHeadPrime, & ! intent(inout): derivative of total water matric potential (m s-1) @@ -395,52 +378,51 @@ subroutine eval8summaWithPrime(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - if(ixNrgConserv.ne.closedForm)then ! use state variable as enthalpy, need to compute temperature - - NEED TO UPDATE WATER STATE FIRST TO MAKE SURE HAVE ALL THESE WATER STATE VARIABLES, SEE updateVarsWithPrime - ! compute temperature component of enthalpy - call enthalpy2T(& - ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: enthalpy state variables - scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(in): trial value for enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(in): trial vector of enthalpy of each snow+soil layer (J m-3) - ! input: water state variables - scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - ! output: temperature - scalarCanairTempTrial, & ! intent(out): trial value for canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(out): trial value for canopy temperature (K) - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - ! output: derivatives - dCanairTemp_dEnthalpy, & ! intent(out): derivative of canopy air temperature with enthalpy - dCanopyTemp_dEnthalpy, & ! intent(out): derivative of canopy temperature with enthalpy - dTemp_dEnthalpy, & ! intent(out): derivative of layer temperature with enthalpy - dCanopyTemp_dCanWat, & ! intent(out): derivative of canopy temperature with canopy water - dTemp_dTheta, & ! intent(out): derivative of layer temperature with volumetric total water content - dTemp_dPsi0, & ! intent(out): derivative of layer temperature with total water matric potential - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - + if(ixNrgConserv== enthalpyFD .or. ixNrgConserv == enthalpyFDlu)then ! use state variable as enthalpy, need to compute temperature + scalarCanairEnthalpyTrial = scalarCanairNrgTrial + scalarCanopyEnthalpyTrial = scalarCanopyNrgTrial + mLayerEnthalpyTrial = mLayerNrgTrial + scalarCanairEnthalpyPrime = scalarCanairNrgPrime + scalarCanopyEnthalpyPrime = scalarCanopyNrgPrime + mLayerEnthalpyPrime = mLayerNrgPrime + ! do not need these variables + scalarCanairTempPrime = realMissing + scalarCanopyTempPrime = realMissing + mLayerTempPrime = realMissing + else ! use state variable as temperature + scalarCanairTempTrial = scalarCanairNrgTrial + scalarCanopyTempTrial = scalarCanopyNrgTrial + mLayerTempTrial = mLayerNrgTrial + scalarCanairTempPrime = scalarCanairNrgPrime + scalarCanopyTempPrime = scalarCanopyNrgPrime + mLayerTempPrime = mLayerNrgPrime + ! do not need these variables + scalarCanairEnthalpyTrial = realMissing + scalarCanopyEnthalpyTrial = realMissing + mLayerEnthalpyTrial = realMissing + scalarCanairEnthalpyPrime = realMissing + scalarCanopyEnthalpyPrime = realMissing + mLayerEnthalpyPrime = realMissing endif !(choice of how conservation of energy is implemented) ! update diagnostic variables and derivatives + ! NOTE: if we are using enthalpy as a state variable, currently all *TempPrime, *IcePrime, and *LiqPrime are set to realMissing + ! This possibly could cause problems if we use splitting, but we are not using splitting at the moment call updateVarsWithPrime(& ! input - .false., & ! intent(in): logical flag if computing for Jacobian update - .false., & ! intent(in): logical flag to adjust temperature to account for the energy + ixNrgConserv.ne.closedForm, & ! intent(in): flag if need to update temperature from enthalpy + .true., & ! intent(in): flag if computing for Jacobian update + .false., & ! intent(in): flag to adjust temperature to account for the energy mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(inout): model diagnostic variables for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + lookup_data, & ! intent(in): lookup table data structure + ! input: enthalpy state variables + scalarCanairEnthalpyTrial,& ! intent(in): trial value for enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial,& ! intent(in): trial value for enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(in): trial vector of enthalpy of each snow+soil layer (J m-3) ! output: variables for the vegetation canopy scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) @@ -467,7 +449,7 @@ subroutine eval8summaWithPrime(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - if(updateCp)then + if(updateStateCp)then ! *** compute volumetric heat capacity C_p call computHeatCapAnalytic(& ! input: control variables @@ -514,7 +496,16 @@ subroutine eval8summaWithPrime(& sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) + else + ! set state heat capacity derivatives to 0 for constant through step + dVolHtCapBulk_dPsi0 = 0._rkind + dVolHtCapBulk_dTheta = 0._rkind + dVolHtCapBulk_dCanWat = 0._rkind + dVolHtCapBulk_dTk = 0._rkind + dVolHtCapBulk_dTkCanopy = 0._rkind + endif ! updateStateCp + if(updateFluxCp)then ! update thermal conductivity call computThermConduct(& ! input: control variables @@ -546,19 +537,14 @@ subroutine eval8summaWithPrime(& err,cmessage) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if else - ! set heat capacity derivatives to 0 for constant through step - dVolHtCapBulk_dPsi0 = 0._rkind - dVolHtCapBulk_dTheta = 0._rkind - dVolHtCapBulk_dCanWat = 0._rkind - dVolHtCapBulk_dTk = 0._rkind - dVolHtCapBulk_dTkCanopy = 0._rkind - dThermalC_dWatAbove = 0._rkind - dThermalC_dWatBelow = 0._rkind - dThermalC_dTempAbove = 0._rkind - dThermalC_dTempBelow = 0._rkind - endif ! updateCp - - if(needCm)then + ! set flux heat capacity derivatives to 0 for constant through step + dThermalC_dWatAbove = 0._rkind + dThermalC_dWatBelow = 0._rkind + dThermalC_dTempAbove = 0._rkind + dThermalC_dTempBelow = 0._rkind + endif ! updateFluxCp + + if(needStateCm)then ! compute C_m call computCm(& ! input: control variables @@ -581,7 +567,7 @@ subroutine eval8summaWithPrime(& mLayerCmTrial = 0._qp dCm_dTk = 0._rkind dCm_dTkCanopy = 0._rkind - endif ! needCm + endif ! needStateCm ! save the number of flux calls per time step indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) + 1 @@ -769,6 +755,7 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) ! input: data structures eqns_data%model_decisions, & ! intent(in): model decisions + eqns_data%lookup_data, & ! intent(in): lookup data eqns_data%type_data, & ! intent(in): type of vegetation and soil eqns_data%attr_data, & ! intent(in): spatial attributes eqns_data%mpar_data, & ! intent(in): model parameters @@ -780,26 +767,16 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input: previous values of variables needed in data window outside of internal IDA - eqns_data%scalarCanopyTempPrev, & ! intent(in): previous value for temperature of the vegetation canopy (K) - eqns_data%mLayerTempPrev, & ! intent(in): previous vector of layer temperature (K) - eqns_data%mLayerMatricHeadPrev, & ! intent(in): previous value for total water matric potential (m) ! output: new values of variables needed in data window outside of internal IDA eqns_data%scalarCanopyTempTrial, & ! intent(out): trial value for temperature of the vegetation canopy (K) - eqns_data%scalarCanopyWatTrial, & ! intent(out): trial value for mass of total water on the vegetation canopy (kg m-2) eqns_data%mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) eqns_data%mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) - eqns_data%mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) ! output: new prime values of variables needed in data window outside of internal IDA - eqns_data%scalarCanairTempPrime, & ! intent(out): prime value for temperature of the canopy air space (K s-1) eqns_data%scalarCanopyTempPrime, & ! intent(out): prime value for temperature of the vegetation canopy (K s-1) eqns_data%scalarCanopyWatPrime, & ! intent(out): prime value for total water content of the vegetation canopy (kg m-2 s-1) - eqns_data%scalarCanopyIcePrime, & ! intent(out): prime value for mass of ice on the vegetation canopy (kg m-2 s-1) eqns_data%mLayerTempPrime, & ! intent(out): prime vector of temperature of each snow and soil layer (K s-1) eqns_data%mLayerMatricHeadPrime, & ! intent(out): prime vector of matric head of each snow and soil layer (m s-1) - eqns_data%mLayerMatricHeadLiqPrime, & ! intent(out): prime vector of liquid water matric potential (m s-1) eqns_data%mLayerVolFracWatPrime, & ! intent(out): prime vector of volumetric total water content of each snow and soil layer (s-1) - eqns_data%mLayerVolFracIcePrime, & ! intent(out): prime vector of volumetric fraction of ice (s-1) ! input-output: baseflow eqns_data%ixSaturation, & ! intent(inout): index of the lowest saturated layer eqns_data%dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index 97cfbb8bd..495ec90b9 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -540,12 +540,12 @@ subroutine varExtract(& prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanairNrgTrial, & ! intent(inout): trial value of canopy air energy, temperature (K) or enthalpy (J m-3) + scalarCanopyNrgTrial, & ! intent(inout): trial value of canopy energy, temperature (K) or enthalpy (J m-3) scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerNrgTrial, & ! intent(inout): trial vector of layer energy, temperature (K) or enthalpy (J m-3) mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) @@ -563,12 +563,12 @@ subroutine varExtract(& type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers ! output: variables for the vegetation canopy - real(rkind),intent(inout) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) - real(rkind),intent(inout) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind),intent(inout) :: scalarCanairNrgTrial ! trial value of canopy air energy, temperature (K) or enthalpy (J m-3) + real(rkind),intent(inout) :: scalarCanopyNrgTrial ! trial value of canopy energy, temperature (K) or enthalpy (J m-3) real(rkind),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) real(rkind),intent(inout) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - real(rkind),intent(inout) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(inout) :: mLayerNrgTrial(:) ! trial vector of layer energy, temperature (K) or enthalpy (J m-3) real(rkind),intent(inout) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) real(rkind),intent(inout) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) real(rkind),intent(inout) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) @@ -614,10 +614,10 @@ subroutine varExtract(& if(ixCasNrg/=integerMissing .or. ixVegNrg/=integerMissing .or. ixVegHyd/=integerMissing)then ! extract temperature of the canopy air space - if(ixCasNrg/=integerMissing) scalarCanairTempTrial = stateVec(ixCasNrg) + if(ixCasNrg/=integerMissing) scalarCanairNrgTrial = stateVec(ixCasNrg) ! extract canopy temperature - if(ixVegNrg/=integerMissing) scalarCanopyTempTrial = stateVec(ixVegNrg) + if(ixVegNrg/=integerMissing) scalarCanopyNrgTrial = stateVec(ixVegNrg) ! extract intercepted water if(ixVegHyd/=integerMissing)then @@ -636,7 +636,7 @@ subroutine varExtract(& ! overwrite with the energy values from the state vector if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - mLayerTempTrial(iLayer) = stateVec( ixSnowSoilNrg(iLayer) ) + mLayerNrgTrial(iLayer) = stateVec( ixSnowSoilNrg(iLayer) ) end do ! looping through non-missing energy state variables in the snow+soil domain endif diff --git a/build/source/engine/layerMerge.f90 b/build/source/engine/layerMerge.f90 index 0ab8e2a4d..12d5710af 100644 --- a/build/source/engine/layerMerge.f90 +++ b/build/source/engine/layerMerge.f90 @@ -291,13 +291,13 @@ end subroutine layerMerge ! *********************************************************************************************************** subroutine layer_combine(mpar_data,prog_data,diag_data,flux_data,indx_data,iSnow,err,message) ! provide access to variables in the data structures - USE var_lookup,only:iLookPARAM,iLookPROG,iLookINDEX ! named variables for structure elements - USE globalData,only:prog_meta,diag_meta,flux_meta,indx_meta ! metadata - USE data_types,only:var_ilength,var_dlength ! data vectors with variable length dimension - USE data_types,only:var_d ! data structures with fixed dimension + USE var_lookup,only:iLookPARAM,iLookPROG,iLookINDEX ! named variables for structure elements + USE globalData,only:prog_meta,diag_meta,flux_meta,indx_meta ! metadata + USE data_types,only:var_ilength,var_dlength ! data vectors with variable length dimension + USE data_types,only:var_d ! data structures with fixed dimension ! provide access to external modules - USE snow_utils_module,only:fracliquid ! compute fraction of liquid water - USE enthalpyTemp_module,only:enthalpy2T_snow,T2enthalpy_snow ! convert temperature to enthalpy for a snow layer + USE snow_utils_module,only:fracliquid ! compute fraction of liquid water + USE enthalpyTemp_module,only:enthalpy2T_snowlu,T2enthalpy_snowlu ! convert temperature to enthalpy for a snow layer with lookup tables implicit none ! ------------------------------------------------------------------------------------------------------------ ! input/output: data structures @@ -361,19 +361,19 @@ subroutine layer_combine(mpar_data,prog_data,diag_data,flux_data,indx_data,iSnow cBulkDenWat = (mLayerDepth(isnow)*bulkDenWat(1) + mLayerDepth(isnow+1)*bulkDenWat(2))/cDepth ! compute enthalpy for each layer (J m-3) - l1Enthalpy = T2enthalpy_snow(mLayerTemp(iSnow), BulkDenWat(1),snowfrz_scale) - l2Enthalpy = T2enthalpy_snow(mLayerTemp(iSnow+1),BulkDenWat(2),snowfrz_scale) + l1Enthalpy = T2enthalpy_snowlu(mLayerTemp(iSnow), BulkDenWat(1),snowfrz_scale) + l2Enthalpy = T2enthalpy_snowlu(mLayerTemp(iSnow+1),BulkDenWat(2),snowfrz_scale) ! compute combined enthalpy (J m-3) cEnthalpy = (mLayerDepth(isnow)*l1Enthalpy + mLayerDepth(isnow+1)*l2Enthalpy)/cDepth ! convert enthalpy (J m-3) to temperature (K) - call enthalpy2T_snow(cEnthalpy,cBulkDenWat,snowfrz_scale,cTemp,err,cmessage) + call enthalpy2T_snowlu(cEnthalpy,cBulkDenWat,snowfrz_scale,cTemp,err,cmessage) if(err/=0)then; err=10; message=trim(message)//trim(cmessage); return; end if ! test enthalpy conversion - if(abs(T2enthalpy_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat - cEnthalpy/cBulkDenWat) > eTol)then - write(*,'(a,1x,f12.5,1x,2(e20.10,1x))') 'enthalpy test', cBulkDenWat, T2enthalpy_snow(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat, cEnthalpy/cBulkDenWat + if(abs(T2enthalpy_snowlu(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat - cEnthalpy/cBulkDenWat) > eTol)then + write(*,'(a,1x,f12.5,1x,2(e20.10,1x))') 'enthalpy test', cBulkDenWat, T2enthalpy_snowlu(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat, cEnthalpy/cBulkDenWat message=trim(message)//'problem with enthalpy-->temperature conversion' err=20; return end if diff --git a/build/source/engine/soil_utilsAddPrime.f90 b/build/source/engine/soil_utilsAddPrime.f90 index 49aca0dc8..dceb711c2 100644 --- a/build/source/engine/soil_utilsAddPrime.f90 +++ b/build/source/engine/soil_utilsAddPrime.f90 @@ -27,6 +27,10 @@ module soil_utilsAddPrime_module Tfreeze, & ! temperature at freezing (K) LH_fus, & ! latent heat of fusion (J kg-1, or m2 s-2) R_wv ! gas constant for water vapor (J kg-1 K-1; [J = Pa m3]) + +! missing values +USE globalData,only:realMissing ! missing real number + USE soil_utils_module,only:matricHead USE soil_utils_module,only:dPsi_dTheta USE soil_utils_module,only:volFracLiq @@ -53,18 +57,17 @@ module soil_utilsAddPrime_module subroutine liquidHeadPrime(& ! input matricHeadTotal ,& ! intent(in) : total water matric potential (m) - matricHeadTotalPrime ,& ! intent(in) + matricHeadTotalPrime ,& ! intent(in) : total water matric potential time derivative (m s-1) volFracLiq ,& ! intent(in) : volumetric fraction of liquid water (-) volFracIce ,& ! intent(in) : volumetric fraction of ice (-) vGn_alpha,vGn_n,theta_sat,theta_res,vGn_m,& ! intent(in) : soil parameters dVolTot_dPsi0 ,& ! intent(in) : derivative in the soil water characteristic (m-1) dTheta_dT ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) - tempPrime ,& ! intent(in) - volFracLiqPrime ,& ! intent(in) - volFracIcePrime ,& ! intent(in) + volFracLiqPrime ,& ! intent(in) : volumetric fraction of liquid water time derivative (-) + volFracIcePrime ,& ! intent(in) : volumetric fraction of ice time derivative (-) ! output matricHeadLiq ,& ! intent(out) : liquid water matric potential (m) - matricHeadLiqPrime ,& ! intent(out) + matricHeadLiqPrime ,& ! intent(out) : liquid water matric potential time derivative (m s-1) dPsiLiq_dPsi0 ,& ! intent(out) : derivative in the liquid water matric potential w.r.t. the total water matric potential (-) dPsiLiq_dTemp ,& ! intent(out) : derivative in the liquid water matric potential w.r.t. temperature (m K-1) err,message) ! intent(out) : error control @@ -72,18 +75,17 @@ subroutine liquidHeadPrime(& implicit none ! input real(rkind),intent(in) :: matricHeadTotal ! total water matric potential (m) - real(rkind),intent(in) :: matricHeadTotalPrime + real(rkind),intent(in) :: matricHeadTotalPrime ! total water matric potential time derivative (m s-1) real(rkind),intent(in) :: volFracLiq ! volumetric fraction of liquid water (-) real(rkind),intent(in) :: volFracIce ! volumetric fraction of ice (-) real(rkind),intent(in) :: vGn_alpha,vGn_n,theta_sat,theta_res,vGn_m ! soil parameters real(rkind),intent(in) ,optional :: dVolTot_dPsi0 ! derivative in the soil water characteristic (m-1) real(rkind),intent(in) ,optional :: dTheta_dT ! derivative in volumetric total water w.r.t. temperature (K-1) - real(rkind),intent(in) :: TempPrime - real(rkind),intent(in) :: volFracLiqPrime - real(rkind),intent(in) :: volFracIcePrime + real(rkind),intent(in) :: volFracLiqPrime ! volumetric fraction of liquid water time derivative () + real(rkind),intent(in) :: volFracIcePrime ! volumetric fraction of ice time derivative () ! output real(rkind),intent(out) :: matricHeadLiq ! liquid water matric potential (m) - real(rkind),intent(out) :: matricHeadLiqPrime + real(rkind),intent(out) :: matricHeadLiqPrime ! liquid water matric potential time derivative (m s-1) real(rkind),intent(out) ,optional :: dPsiLiq_dPsi0 ! derivative in the liquid water matric potential w.r.t. the total water matric potential (-) real(rkind),intent(out) ,optional :: dPsiLiq_dTemp ! derivative in the liquid water matric potential w.r.t. temperature (m K-1) ! output: error control @@ -94,8 +96,8 @@ subroutine liquidHeadPrime(& real(rkind) :: effSat ! effective saturation (-) real(rkind) :: dPsiLiq_dEffSat ! derivative in liquid water matric potential w.r.t. effective saturation (m) real(rkind) :: dEffSat_dTemp ! derivative in effective saturation w.r.t. temperature (K-1) - real(rkind) :: dEffSat_dFracLiq - real(rkind) :: effSatPrime + real(rkind) :: dEffSat_dFracLiq ! derivative in effective saturation w.r.t. liquid water fraction (-) + real(rkind) :: effSatPrime ! effective saturation time derivative (-) ! ------------------------------------------------------------------------------------------------------------------------------ ! initialize error control err=0; message='liquidHeadPrime/' @@ -117,14 +119,13 @@ subroutine liquidHeadPrime(& ! - matric head associated with liquid water matricHeadLiq = matricHead(effSat,vGn_alpha,0._rkind,1._rkind,vGn_n,vGn_m) ! argument is effective saturation, so theta_res=0 and theta_sat=1 - if (effSat < 1._rkind .and. effSat > 0._rkind)then + if (effSat < 1._rkind .and. effSat > 0._rkind .and. volFracLiqPrime.ne.realMissing .and. volFracIcePrime.ne.realMissing)then effSatPrime = (volFracLiqPrime * xDen + volFracIcePrime * xNum) / xDen**2_i4b matricHeadLiqPrime = -( 1._rkind/(vGn_alpha*vGn_n*vGn_m) ) * effSat**(-1._rkind-1._rkind/vGn_m) * ( effSat**(-1._rkind/vGn_m) - 1._rkind )**(-1._rkind+1._rkind/vGn_n) * effSatPrime else matricHeadLiqPrime = 0._rkind endif - ! compute derivative in liquid water matric potential w.r.t. effective saturation (m) if(present(dPsiLiq_dPsi0).or.present(dPsiLiq_dTemp))then dPsiLiq_dEffSat = dPsi_dTheta(effSat,vGn_alpha,0._rkind,1._rkind,vGn_n,vGn_m) @@ -171,6 +172,7 @@ subroutine liquidHeadPrime(& if(present(dPsiLiq_dTemp)) dPsiLiq_dPsi0 = 1._rkind ! derivative=1 because values are identical if(present(dPsiLiq_dTemp)) dPsiLiq_dTemp = 0._rkind ! derivative=0 because no impact of temperature for unfrozen conditions end if ! (if ice exists) + if(volFracLiqPrime==realMissing .or. volFracIcePrime==realMissing) matricHeadLiqPrime = realMissing end subroutine liquidHeadPrime diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 49ef600c1..8830b87de 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -109,6 +109,7 @@ subroutine summaSolve4ida(& dMat, & ! intent(inout): diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup data type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes mpar_data, & ! intent(in): model parameters @@ -174,6 +175,7 @@ subroutine summaSolve4ida(& real(rkind), intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup), intent(in) :: lookup_data ! lookup tables type(var_i), intent(in) :: type_data ! type of vegetation and soil type(var_d), intent(in) :: attr_data ! spatial attributes type(var_dlength), intent(in) :: mpar_data ! model parameters @@ -277,6 +279,8 @@ subroutine summaSolve4ida(& eqns_data%firstSubStep = firstSubStep eqns_data%computeVegFlux = computeVegFlux eqns_data%scalarSolution = scalarSolution + eqns_data%deriv_data = deriv_data + eqns_data%lookup_data = lookup_data eqns_data%type_data = type_data eqns_data%attr_data = attr_data eqns_data%mpar_data = mpar_data @@ -286,7 +290,6 @@ subroutine summaSolve4ida(& eqns_data%indx_data = indx_data eqns_data%diag_data = diag_data eqns_data%flux_data = flux_data - eqns_data%deriv_data = deriv_data eqns_data%ixSaturation = ixSaturation ! allocate space and fill @@ -311,12 +314,9 @@ subroutine summaSolve4ida(& allocate( eqns_data%mLayerMatricHeadPrev(nSoil) ) allocate( eqns_data%mLayerTempTrial(nLayers) ) allocate( eqns_data%mLayerMatricHeadTrial(nSoil) ) - allocate( eqns_data%mLayerMatricHeadLiqPrime(nSoil) ) - allocate( eqns_data%mLayerVolFracWatTrial(nLayers) ) allocate( eqns_data%mLayerTempPrime(nLayers) ) allocate( eqns_data%mLayerMatricHeadPrime(nSoil) ) allocate( eqns_data%mLayerVolFracWatPrime(nLayers) ) - allocate( eqns_data%mLayerVolFracIcePrime(nLayers) ) allocate( mLayerMatricHeadPrimePrev(nSoil) ) allocate( dCompress_dPsiPrev(nSoil) ) allocate( eqns_data%fluxVec(nState) ) @@ -590,12 +590,9 @@ subroutine summaSolve4ida(& deallocate( eqns_data%mLayerMatricHeadPrev ) deallocate( eqns_data%mLayerTempTrial ) deallocate( eqns_data%mLayerMatricHeadTrial ) - deallocate( eqns_data%mLayerVolFracWatTrial ) deallocate( eqns_data%mLayerTempPrime ) deallocate( eqns_data%mLayerMatricHeadPrime ) - deallocate( eqns_data%mLayerMatricHeadLiqPrime) deallocate( eqns_data%mLayerVolFracWatPrime ) - deallocate( eqns_data%mLayerVolFracIcePrime ) deallocate( mLayerMatricHeadPrimePrev ) deallocate( dCompress_dPsiPrev ) deallocate( eqns_data%resVec ) diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index bea546ad3..6761c7c06 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -248,7 +248,6 @@ subroutine summaSolve4kinsol(& eqns_data%indx_data = indx_data eqns_data%diag_data = diag_data eqns_data%flux_data = flux_data - eqns_data%deriv_data = deriv_data eqns_data%ixSaturation = ixSaturation eqns_data%firstStateIteration = .true. diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index d3d3782be..c3407bed5 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -163,7 +163,7 @@ subroutine systemSolv(& USE allocspace_module,only:allocLocal ! allocate local data structures ! state vector and solver USE getVectorz_module,only:getScaling ! get the scaling vectors - USE enthalpyTemp_module,only:T2enthalpy_snow ! convert temperature to enthalpy for a snow layer + USE enthalpyTemp_module,only:T2enthalpy_snowlu ! convert temperature to enthalpy for a snow layer USE enthalpyTemp_module,only:T2enthTemp ! compute enthalpy #ifdef SUNDIALS_ACTIVE USE tol4ida_module,only:popTol4ida ! populate tolerances @@ -418,7 +418,7 @@ subroutine initial_function_evaluations if (nSnow>0) then ! compute the energy required to melt the top snow layer (J m-2) bulkDensity = mLayerVolFracIce(1)*iden_ice + mLayerVolFracLiq(1)*iden_water - volEnthalpy = T2enthalpy_snow(mLayerTemp(1),bulkDensity,snowfrz_scale) + volEnthalpy = T2enthalpy_snowlu(mLayerTemp(1),bulkDensity,snowfrz_scale) ! set flag and error codes for too much melt if (-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_cur) then tooMuchMelt = .true. diff --git a/build/source/engine/updatStateWithPrime.f90 b/build/source/engine/updatStateWithPrime.f90 index c60fe4371..2ea8c72cd 100644 --- a/build/source/engine/updatStateWithPrime.f90 +++ b/build/source/engine/updatStateWithPrime.f90 @@ -8,6 +8,10 @@ module updatStateWithPrime_module iden_water, & ! intrinsic density of water (kg m-3) gravity, & ! gravitational acceleteration (m s-2) LH_fus ! latent heat of fusion (J kg-1) + +! missing values +USE globalData,only:realMissing ! missing real number + implicit none private public::updateSnowPrime @@ -23,37 +27,37 @@ module updatStateWithPrime_module ! ************************************************************************************************************* subroutine updateSnowPrime(& ! input - mLayerTemp ,& ! intent(in): temperature (K) - mLayerTheta ,& ! intent(in): volume fraction of total water (-) - snowfrz_scale ,& ! intent(in): scaling parameter for the snow freezing curve (K-1) - mLayerTempPrime ,& ! intent(in): temperature (K) - mLayerThetaPrime ,& ! intent(in): volume fraction of total water (-) + mLayerTemp ,& ! intent(in): temperature (K) + mLayerTheta ,& ! intent(in): volume fraction of total water (-) + snowfrz_scale ,& ! intent(in): scaling parameter for the snow freezing curve (K-1) + mLayerTempPrime ,& ! intent(in): temperature (K) + mLayerThetaPrime ,& ! intent(in): volume fraction of total water (-) ! output - mLayerVolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) - mLayerVolFracIce ,& ! intent(out): volumetric fraction of ice (-) - mLayerVolFracLiqPrime ,& ! intent(out): volumetric fraction of liquid water (-) - mLayerVolFracIcePrime ,& ! intent(out): volumetric fraction of ice (-) - fLiq ,& ! intent(out): fraction of liquid water (-) - err,message) ! intent(out): error control + mLayerVolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) + mLayerVolFracIce ,& ! intent(out): volumetric fraction of ice (-) + mLayerVolFracLiqPrime ,& ! intent(out): volumetric fraction of liquid water (-) + mLayerVolFracIcePrime ,& ! intent(out): volumetric fraction of ice (-) + fLiq ,& ! intent(out): fraction of liquid water (-) + err,message) ! intent(out): error control ! utility routines - USE snow_utils_module,only:fracliquid ! compute volumetric fraction of liquid water - USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) + USE snow_utils_module,only:fracliquid ! compute volumetric fraction of liquid water + USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) implicit none ! input variables - real(rkind),intent(in) :: mLayerTemp ! temperature (K) - real(rkind),intent(in) :: mLayerTheta ! volume fraction of total water (-) - real(rkind),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) - real(rkind),intent(in) :: mLayerTempPrime ! temperature (K) - real(rkind),intent(in) :: mLayerThetaPrime ! volume fraction of total water (-) + real(rkind),intent(in) :: mLayerTemp ! temperature (K) + real(rkind),intent(in) :: mLayerTheta ! volume fraction of total water (-) + real(rkind),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) + real(rkind),intent(in) :: mLayerTempPrime ! temperature (K) + real(rkind),intent(in) :: mLayerThetaPrime ! volume fraction of total water (-) ! output variables - real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) - real(rkind),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice (-) - real(rkind),intent(out) :: fLiq ! fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) + real(rkind),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice (-) + real(rkind),intent(out) :: fLiq ! fraction of liquid water (-) ! error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! initialize error control err=0; message="updateSnowPrime/" @@ -64,6 +68,12 @@ subroutine updateSnowPrime(& mLayerVolFracLiqPrime = fLiq * mLayerThetaPrime + dFracLiq_dTk(mLayerTemp,snowfrz_scale) * mLayerTheta * mLayerTempPrime mLayerVolFracIcePrime = ( mLayerThetaPrime - mLayerVolFracLiqPrime ) * (iden_water/iden_ice) + ! set primes to missing if the temperature prime is missing (enthalpy is state variable) + if(mLayerTempPrime==realMissing)then + mLayerVolFracLiqPrime=realMissing + mLayerVolFracIcePrime=realMissing + end if + end subroutine updateSnowPrime ! *********************************************************************************************************************************** @@ -71,52 +81,52 @@ end subroutine updateSnowPrime ! *********************************************************************************************************************************** subroutine updateSoilPrime(& ! input - mLayerTemp ,& ! intent(in): temperature (K) - mLayerMatricHead ,& ! intent(in): total water matric potential (m) - mLayerTempPrime ,& ! intent(in): temperature time derivative (K/s) - mLayerMatricHeadPrime, & ! intent(in): total water matric potential time derivative (m/s) - vGn_alpha ,& ! intent(in): van Genutchen "alpha" parameter - vGn_n ,& ! intent(in): van Genutchen "n" parameter - theta_sat ,& ! intent(in): soil porosity (-) - theta_res ,& ! intent(in): soil residual volumetric water content (-) - vGn_m ,& ! intent(in): van Genutchen "m" parameter (-) + mLayerTemp ,& ! intent(in): temperature (K) + mLayerMatricHead ,& ! intent(in): total water matric potential (m) + mLayerTempPrime ,& ! intent(in): temperature time derivative (K/s) + mLayerMatricHeadPrime ,& ! intent(in): total water matric potential time derivative (m/s) + vGn_alpha ,& ! intent(in): van Genutchen "alpha" parameter + vGn_n ,& ! intent(in): van Genutchen "n" parameter + theta_sat ,& ! intent(in): soil porosity (-) + theta_res ,& ! intent(in): soil residual volumetric water content (-) + vGn_m ,& ! intent(in): van Genutchen "m" parameter (-) ! output - mLayerVolFracWat ,& ! intent(out): volumetric fraction of total water (-) - mLayerVolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) - mLayerVolFracIce ,& ! intent(out): volumetric fraction of ice (-) - mLayerVolFracWatPrime ,& ! intent(out): volumetric fraction of total water time derivative (-) - mLayerVolFracLiqPrime ,& ! intent(out): volumetric fraction of liquid water time derivative (-) - mLayerVolFracIcePrime ,& ! intent(out): volumetric fraction of ice time derivative (-) - err,message) ! intent(out): error control + mLayerVolFracWat ,& ! intent(out): volumetric fraction of total water (-) + mLayerVolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) + mLayerVolFracIce ,& ! intent(out): volumetric fraction of ice (-) + mLayerVolFracWatPrime ,& ! intent(out): volumetric fraction of total water time derivative (-) + mLayerVolFracLiqPrime ,& ! intent(out): volumetric fraction of liquid water time derivative (-) + mLayerVolFracIcePrime ,& ! intent(out): volumetric fraction of ice time derivative (-) + err,message) ! intent(out): error control ! utility routines - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water based on matric head - USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric liquid water content + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water based on matric head + USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric liquid water content USE soil_utils_module,only:dTheta_dPsi implicit none ! input variables - real(rkind),intent(in) :: mLayerTemp ! estimate of temperature (K) - real(rkind),intent(in) :: mLayerMatricHead ! matric head (m) - real(rkind),intent(in) :: mLayerTempPrime ! temperature time derivative (K/s) - real(rkind),intent(in) :: mLayerMatricHeadPrime ! matric head time derivative (m/s) - real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter - real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter - real(rkind),intent(in) :: theta_sat ! soil porosity (-) - real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) - real(rkind),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) + real(rkind),intent(in) :: mLayerTemp ! estimate of temperature (K) + real(rkind),intent(in) :: mLayerMatricHead ! matric head (m) + real(rkind),intent(in) :: mLayerTempPrime ! temperature time derivative (K/s) + real(rkind),intent(in) :: mLayerMatricHeadPrime ! matric head time derivative (m/s) + real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter + real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter + real(rkind),intent(in) :: theta_sat ! soil porosity (-) + real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) + real(rkind),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) ! output variables - real(rkind),intent(out) :: mLayerVolFracWat ! fractional volume of total water (-) - real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) - real(rkind),intent(out) :: mLayerVolFracWatPrime ! fractional volume of total water (-) - real(rkind),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice (-) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + real(rkind),intent(out) :: mLayerVolFracWat ! fractional volume of total water (-) + real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) + real(rkind),intent(out) :: mLayerVolFracWatPrime ! fractional volume of total water (-) + real(rkind),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice (-) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! define local variables - real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) - real(rkind) :: xConst ! constant in the freezing curve function (m K-1) - real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) - real(rkind),parameter :: tinyVal=epsilon(1._rkind) ! used in balance check + real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) + real(rkind) :: xConst ! constant in the freezing curve function (m K-1) + real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) + real(rkind),parameter :: tinyVal=epsilon(1._rkind) ! used in balance check ! initialize error control err=0; message="updateSoilPrime/" @@ -150,6 +160,12 @@ subroutine updateSoilPrime(& mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime + ! set primes to missing if the temperature prime is missing (enthalpy is state variable) + if(mLayerTempPrime==realMissing)then + mLayerVolFracLiqPrime=realMissing + mLayerVolFracIcePrime=realMissing + end if + end subroutine updateSoilPrime end module updatStateWithPrime_module diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 index e2295aaf1..e58ff7805 100644 --- a/build/source/engine/updateVars.f90 +++ b/build/source/engine/updateVars.f90 @@ -81,14 +81,13 @@ module updateVars_module USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) USE soil_utils_module,only:dTheta_dTk ! differentiate the freezing curve w.r.t. temperature (soil) USE soil_utils_module,only:dTheta_dPsi ! derivative in the soil water characteristic (soil) -USE soil_utils_module,only:dPsi_dTheta ! derivative in the soil water characteristic (soil) USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential ! IEEE check -USE, intrinsic :: ieee_arithmetic ! check values (NaN, etc.) +USE, intrinsic :: ieee_arithmetic ! check values (NaN, etc.) implicit none private @@ -174,13 +173,13 @@ subroutine updateVars(& integer(i4b) :: iter ! iteration index integer(i4b) :: niter ! number of iterations integer(i4b),parameter :: maxiter=100 ! maximum number of iterations - real(rkind),parameter :: nrgConvTol=1.e-4_rkind ! convergence tolerance for energy (J m-3) - real(rkind),parameter :: tempConvTol=1.e-6_rkind ! convergence tolerance for temperature (K) + real(rkind),parameter :: nrgConvTol=1.e-4_rkind ! convergence tolerance for energy (J m-3) + real(rkind),parameter :: tempConvTol=1.e-6_rkind ! convergence tolerance for temperature (K) real(rkind) :: critDiff ! temperature difference from critical (K) real(rkind) :: tempMin ! minimum bracket for temperature (K) real(rkind) :: tempMax ! maximum bracket for temperature (K) logical(lgt) :: bFlag ! flag to denote that iteration increment was constrained using bi-section - real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical temperature (K) + real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical temperature (K) ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures associate(& @@ -236,7 +235,7 @@ subroutine updateVars(& dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature ! derivatives inside solver for Jacobian only mLayerdTemp_dt => deriv_data%var(iLookDERIV%mLayerdTemp_dt )%dat ,& ! intent(out): [dp(:)] timestep change in layer temperature - scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt )%dat(1),& ! intent(out): [dp ] timestep change in canopy temperature + scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt)%dat(1) ,& ! intent(out): [dp ] timestep change in canopy temperature mLayerdWat_dt => deriv_data%var(iLookDERIV%mLayerdWat_dt)%dat ,& ! intent(out): [dp(:)] timestep change in layer volumetric fraction of total water scalarCanopydWat_dt => deriv_data%var(iLookDERIV%scalarCanopydWat_dt)%dat(1) & ! intent(out): [dp ] timestep change in canopy total water ) ! association with variables in the data structures diff --git a/build/source/engine/updateVarsWithPrime.f90 b/build/source/engine/updateVarsWithPrime.f90 index 8fc71206a..6b3d31a76 100644 --- a/build/source/engine/updateVarsWithPrime.f90 +++ b/build/source/engine/updateVarsWithPrime.f90 @@ -77,17 +77,18 @@ module updateVarsWithPrime_module USE updatStateWithPrime_module,only:updateSoilPrime ! update soil states ! provide access to functions for the constitutive functions and derivatives -USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) -USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) -USE soil_utils_module,only:dTheta_dTk ! differentiate the freezing curve w.r.t. temperature (soil) -USE soil_utils_module,only:dTheta_dPsi ! derivative in the soil water characteristic (soil) -USE soil_utils_module,only:dPsi_dTheta ! derivative in the soil water characteristic (soil) -USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content -USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water -USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists -USE soil_utilsAddPrime_module,only:liquidHeadPrime ! compute the liquid water matric potential -USE soil_utilsAddPrime_module,only:d2Theta_dPsi2 -USE soil_utilsAddPrime_module,only:d2Theta_dTk2 +USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) +USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) +USE soil_utils_module,only:dTheta_dTk ! differentiate the freezing curve w.r.t. temperature (soil) +USE soil_utils_module,only:dTheta_dPsi ! derivative in the soil water characteristic (soil) +USE soil_utils_module,only:dPsi_dTheta ! derivative in the soil water characteristic (soil) +USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content +USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water +USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists +USE soil_utilsAddPrime_module,only:liquidHeadPrime ! compute the liquid water matric potential +USE soil_utilsAddPrime_module,only:d2Theta_dPsi2 ! second derivative in the soil water characteristic (soil) +USE soil_utilsAddPrime_module,only:d2Theta_dTk2 ! second derivative in the freezing curve w.r.t. temperature (soil) +USE enthalpyTemp_module,only:enthalpy2T ! compute temperature from enthalpy and water content ! IEEE checks USE, intrinsic :: ieee_arithmetic ! check values (NaN, etc.) @@ -103,13 +104,19 @@ module updateVarsWithPrime_module ! ********************************************************************************************************** subroutine updateVarsWithPrime(& ! input - computJac, & ! intent(in): logical flag if computing for Jacobian update - do_adjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + updateT, & ! intent(in): flag if need to update temperature from enthalpy + computJac, & ! intent(in): flag if computing for Jacobian update + do_adjustTemp, & ! intent(in): flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(inout): model diagnostic variables for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + lookup_data, & ! intent(in): lookup table data structure + ! input: enthalpy state variables + scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(in): trial value for enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(in): trial vector of enthalpy of each snow+soil layer (J m-3) ! output: variables for the vegetation canopy scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) @@ -138,6 +145,7 @@ subroutine updateVarsWithPrime(& ! -------------------------------------------------------------------------------------------------------------------------------- implicit none ! input + logical(lgt) ,intent(in) :: updateT ! flag if need to update temperature from enthalpy logical(lgt) ,intent(in) :: computJac ! flag if computing for Jacobian update logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU @@ -145,6 +153,11 @@ subroutine updateVarsWithPrime(& type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + type(zLookup) ,intent(in) :: lookup_data ! lookup tables + ! input: enthalpy state variables + real(rkind),intent(in) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3) + real(rkind),intent(in) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(in) :: mLayerEnthalpyTrial ! trial vector of enthalpy of each snow+soil layer (J m-3) ! output: variables for the vegetation canopy real(rkind),intent(inout) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) real(rkind),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) @@ -219,51 +232,61 @@ subroutine updateVarsWithPrime(& ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) ! indices in the full vector for specific domains - ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain - ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain - ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain - ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain - ! mapping between the full state vector and the state subset - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain + ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + ! mapping between the full state vector and the state subset + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset ! type of domain, type of state variable, and index of control volume within domain - ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp ] scaling parameter for the snow freezing curve (K-1) ! depth-varying model parameters - vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) - ! model diagnostic variables (heat capacity) - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(in): [dp ] volumetric heat capacity of the vegetation (J m-3 K-1) - mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] volumetric heat capacity in each layer (J m-3 K-1) + soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat ,& ! intent(in): [dp(:)] intrinsic soil density (kg m-3) + vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) + ! model diagnostic variables (heat capacity, enthalpy) + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1) ,& ! intent(in): [dp ] specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1) ,& ! intent(in): [dp ] maximum mass of vegetation (kg m-2) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(in): [dp ] volumetric heat capacity of the vegetation (J m-3 K-1) + mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] volumetric heat capacity in each layer (J m-3 K-1) ! model diagnostic variables (fraction of liquid water) - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) ! model states from a previous solution - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) ! model diagnostic variables from a previous solution - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) ! derivatives - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential - dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0)%dat ,& ! intent(out): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) - dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp)%dat ,& ! intent(out): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature - dFracLiqSnow_dTk => deriv_data%var(iLookDERIV%dFracLiqSnow_dTk)%dat ,& ! intent(out): [dp(:)] derivative in fraction of liquid snow w.r.t. temperature - dFracLiqVeg_dTkCanopy => deriv_data%var(iLookDERIV%dFracLiqVeg_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in fraction of (throughfall + drainage) w.r.t. temperature + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0)%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential + dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0)%dat ,& ! intent(out): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) + dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp)%dat ,& ! intent(out): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature + dFracLiqSnow_dTk => deriv_data%var(iLookDERIV%dFracLiqSnow_dTk)%dat ,& ! intent(out): [dp(:)] derivative in fraction of liquid snow w.r.t. temperature + dFracLiqVeg_dTkCanopy => deriv_data%var(iLookDERIV%dFracLiqVeg_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in fraction of (throughfall + drainage) w.r.t. temperature ! derivatives inside solver for Jacobian only - d2VolTot_dPsi02 => deriv_data%var(iLookDERIV%d2VolTot_dPsi02)%dat ,& ! intent(out): [dp(:)] second derivative in total water content w.r.t. total water matric potential - mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2)%dat ,& ! intent(out): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature - d2Theta_dTkCanopy2 => deriv_data%var(iLookDERIV%d2Theta_dTkCanopy2)%dat(1) & ! intent(out): [dp ] second derivative of volumetric liquid water content w.r.t. temperature + d2VolTot_dPsi02 => deriv_data%var(iLookDERIV%d2VolTot_dPsi02)%dat ,& ! intent(out): [dp(:)] second derivative in total water content w.r.t. total water matric potential + mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2)%dat ,& ! intent(out): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature + d2Theta_dTkCanopy2 => deriv_data%var(iLookDERIV%d2Theta_dTkCanopy2)%dat(1) ,& ! intent(out): [dp ] second derivative of volumetric liquid water content w.r.t. temperature + ! derivatives of temperature if enthalpy is the state variable, could probably turn off if outside of solver + dCanairTemp_dEnthalpy => deriv_data%var(iLookDERIV%dCanairTemp_dEnthalpy)%dat(1) ,& ! intent(out): [dp] derivative of canopy air temperature w.r.t. enthalpy + dCanopyTemp_dEnthalpy => deriv_data%var(iLookDERIV%dCanopyTemp_dEnthalpy)%dat(1) ,& ! intent(out): [dp] derivative of canopy temperature w.r.t. enthalpy + dTemp_dEnthalpy => deriv_data%var(iLookDERIV%dTemp_dEnthalpy)%dat ,& ! intent(out): [dp(:)] derivative of temperature w.r.t. enthalpy + dCanopyTemp_dCanWat => deriv_data%var(iLookDERIV%dCanopyTemp_dCanWat)%dat(1) ,& ! intent(out): [dp] derivative of canopy temperature w.r.t. volumetric water content + dTemp_dTheta => deriv_data%var(iLookDERIV%dTemp_dTheta)%dat ,& ! intent(out): [dp(:)] derivative of temperature w.r.t. volumetric water content + dTemp_dPsi0 => deriv_data%var(iLookDERIV%dTemp_dPsi0)%dat & ! intent(out): [dp(:)] derivative of temperature w.r.t. total water matric potential ) ! association with variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- @@ -376,6 +399,75 @@ subroutine updateVarsWithPrime(& endif ! if hydrology state variable or uncoupled solution + ! compute temperature from enthalpy and water content, enthalpy is the state variable + if(ixStateType(ixFullVector)==iname_nrgCanair)then + if(updateT)then + call enthalpy2T_cas(& + scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) + scalarCanairTempTrial, & ! intent(out): trial value for canopy air temperature (K) + dCanairTemp_dEnthalpy, & ! intent(out): derivative of canopy air temperature with enthalpy + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + else + dCanairTemp_dEnthalpy = 1._rkind + endif + else if(ixStateType(ixFullVector)==iname_nrgCanopy)then + if(updateT)then + call enthalpy2T_veg(& + canopyDepth, & ! intent(in): canopy depth (m) + specificHeatVeg, & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation, & ! intent(in): maximum mass of vegetation (kg m-2) + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + scalarCanopyEnthalpyTrial, & ! intent(in): trial value for enthalpy of the vegetation canopy (J m-3) + scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) + scalarCanopyTempTrial, & ! intent(out): trial value for canopy temperature (K) + dCanopyTemp_dEnthalpy, & ! intent(out): derivative of canopy temperature with enthalpy + dCanopyTemp_dCanWat, & ! intent(out): derivative of canopy temperature with canopy water + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + else + dCanopyTemp_dEnthalpy = 1._rkind + dCanopyTemp_dCanWat = 1._rkind + endif + else if(ixStateType(ixFullVector)==iname_nrgLayer)then + if(updateT)then + if(ixDomainType==iname_snow)then + call enthalpy2T_snow(& + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + mLayerEnthalpyTrial(iLayer), & ! intent(in): enthalpy of snow+soil layer (J m-3) + mLayerVolFracWatTrial(iLayer), & ! intent(in): volumetric total water content (-) + mLayerTempTrial(iLayer), & ! intent(out): layer temperature (K) + dTemp_dEnthalpy(iLayer), & ! intent(out): derivative of layer temperature with enthalpy + dTemp_dTheta(iLayer), & ! intent(out): derivative of layer temperature with volumetric total water content + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + elseif(ixDomainType==iname_soil)then + call enthalpy2T_soil(& + ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy + soil_dens_intr(ixControlIndex), & ! intent(in): intrinsic soil density (kg m-3) + vGn_alpha(ixControlIndex), & ! intent(in): van Genutchen "alpha" parameter + vGn_n(ixControlIndex), & ! intent(in): van Genutchen "n" parameter + theta_sat(ixControlIndex), & ! intent(in): soil porosity (-) + theta_res(ixControlIndex), & ! intent(in): soil residual volumetric water content (-) + vGn_m(ixControlIndex), & ! intent(in): van Genutchen "m" parameter (-) + ixControlIndex, & ! intent(in): index of the control volume within the domain + lookup_data, & ! intent(in): lookup table data structure + mLayerEnthalpyTrial(iLayer), & ! intent(in): trial vector of enthalpy of each snow+soil layer (J m-3) + mLayerMatricHeadTrial(ixControlIndex), & ! intent(in): trial vector of total water matric potential (m) + mLayerTempTrial(iLayer), & ! intent(out): trial vector of layer temperature (K) + dTemp_dEnthalpy(iLayer), & ! intent(out): derivative of layer temperature with enthalpy + dTemp_dTheta(iLayer), & ! intent(out): derivative of layer temperature with volumetric total water content + dTemp_dPsi0(ixControlIndex), & ! intent(out): derivative of layer temperature with total water matric potential + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + endif + else + dTemp_dEnthalpy(iLayer) = 1._rkind + dTemp_dTheta(iLayer) = 1._rkind + if(ixDomainType==iname_soil) dTemp_dPsi0(ixControlIndex) = 1._rkind + endif + + ! compute the critical soil temperature below which ice exists select case(ixDomainType) case(iname_veg, iname_snow); Tcrit = Tfreeze @@ -713,22 +805,21 @@ subroutine updateVarsWithPrime(& ! compute the liquid matric potential (and the derivatives w.r.t. total matric potential and temperature) call liquidHeadPrime(& ! input - mLayerMatricHeadTrial(ixControlIndex) ,& ! intent(in) : total water matric potential (m) - mLayerMatricHeadPrime(ixControlIndex) ,& ! - mLayerVolFracLiqTrial(iLayer) ,& ! intent(in) : volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : volumetric fraction of ice (-) - vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in) : soil parameters - dVolTot_dPsi0(ixControlIndex) ,& ! intent(in) : derivative in the soil water characteristic (m-1) - mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) - mLayerTempPrime(ixControlIndex) ,& - mLayerVolFracLiqPrime(iLayer) ,& - mLayerVolFracIcePrime(iLayer) ,& + mLayerMatricHeadTrial(ixControlIndex) ,& ! intent(in) : total water matric potential (m) + mLayerMatricHeadPrime(ixControlIndex) ,& ! intent(in) : total water matric potential time derivative (m s-1) + mLayerVolFracLiqTrial(iLayer) ,& ! intent(in) : volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : volumetric fraction of ice (-) + vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex),& ! intent(in) : soil parameters + dVolTot_dPsi0(ixControlIndex) ,& ! intent(in) : derivative in the soil water characteristic (m-1) + mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) + mLayerVolFracLiqPrime(iLayer) ,& ! intent(in) : volumetric fraction of liquid water time derivative (-) + mLayerVolFracIcePrime(iLayer) ,& ! intent(in) : volumetric fraction of ice time derivative (-) ! output - mLayerMatricHeadLiqTrial(ixControlIndex) ,& ! intent(out): liquid water matric potential (m) - mLayerMatricHeadLiqPrime(ixControlIndex) ,& ! - dPsiLiq_dPsi0(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. the total water matric potential (-) - dPsiLiq_dTemp(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. temperature (m K-1) - err,cmessage) ! intent(out): error control + mLayerMatricHeadLiqTrial(ixControlIndex) ,& ! intent(out): liquid water matric potential (m) + mLayerMatricHeadLiqPrime(ixControlIndex) ,& ! intent(out): liquid water matric potential time derivative (m s-1) + dPsiLiq_dPsi0(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. the total water matric potential (-) + dPsiLiq_dTemp(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. temperature (m K-1) + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif endif ! switch between hydrology and energy state From f6e082b977cddbe99ff647a9a35f858013372149 Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Tue, 19 Mar 2024 11:24:45 -0600 Subject: [PATCH 1221/1472] Add thread-safe timing measurements --- build/cmake/build_actors.cluster.bash | 1 + build/source/engine/coupled_em.f90 | 14 +++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/build/cmake/build_actors.cluster.bash b/build/cmake/build_actors.cluster.bash index 72cb6a90a..6046e7840 100755 --- a/build/cmake/build_actors.cluster.bash +++ b/build/cmake/build_actors.cluster.bash @@ -11,6 +11,7 @@ module load netcdf-fortran/4.5.2 module load caf export FLAGS_OPT="-flto=1;-fuse-linker-plugin" +export SUNDIALS_PATH=/globalhome/kck540/HPC/Libraries/sundials/v7.0/instdir cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials_Actors_Cluster cmake --build ../cmake_build --target all diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 34f8ae3a3..0bc136bcc 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -267,8 +267,9 @@ subroutine coupled_em(& real(rkind),allocatable :: liqSnowInit(:) ! volumetric liquid water conetnt of snow at the start of the time step real(rkind),allocatable :: liqSoilInit(:) ! soil moisture at the start of the time step ! timing information - real(rkind) :: startTime ! start time (used to compute wall clock time) - real(rkind) :: endTime ! end time (used to compute wall clock time) + integer(kind=8) :: count_rate + integer(kind=8) :: i_start, i_end + real :: elapsed_time real(rkind) :: mean_step_dt_sub ! mean solution step for the sub-step real(rkind) :: sumStepSize ! sum solution step for the data step ! outer loop control @@ -284,7 +285,9 @@ subroutine coupled_em(& ! This is the start of a data step for a local HRU ! get the start time - call cpu_time(startTime) + ! get the start time + CALL system_clock(count_rate=count_rate) + CALL system_clock(i_start) ! check that the decision is supported if(model_decisions(iLookDECISIONS%groundwatr)%iDecision==bigBucket .and. & @@ -1510,10 +1513,11 @@ subroutine coupled_em(& end if ! get the end time - call cpu_time(endTime) + CALL system_clock(i_end) + elapsed_time = REAL(i_end - i_start) / REAL(count_rate) ! get the elapsed time - diag_data%var(iLookDIAG%wallClockTime)%dat(1) = endTime - startTime + diag_data%var(iLookDIAG%wallClockTime)%dat(1) = elapsed_time end subroutine coupled_em From 98a388750bcd2639d65e1bb9877670578af31055 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 20 Mar 2024 14:52:31 +0900 Subject: [PATCH 1222/1472] enthalpy_2T now by state type, done inside updateVarPrime --- build/source/engine/enthalpyTemp.f90 | 1011 +++++++++++-------- build/source/engine/eval8summaWithPrime.f90 | 69 +- build/source/engine/layerMerge.f90 | 12 +- build/source/engine/systemSolv.f90 | 4 +- build/source/engine/updateVarsWithPrime.f90 | 208 ++-- 5 files changed, 728 insertions(+), 576 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 5b2cb7f66..c341ae39c 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -61,10 +61,13 @@ module enthalpyTemp_module implicit none public::T2H_lookup_snow public::T2L_lookup_soil -public::enthalpy2T_snowlu -public::T2enthalpy_snowlu -public::ethalpy2T +public::enthalpy2T_snwWat +public::T2enthalpy_snwWat public::enthTemp2enthalpy +public::ethalpy2T_cas +public::ethalpy2T_veg +public::ethalpy2T_snow +public::ethalpy2T_soil private::hyp_2F1_real ! define the snow look-up table used to compute temperature based on enthalpy @@ -113,7 +116,7 @@ subroutine T2H_lookup_snow(mpar_data, & ! intent(in): pa ! ***** compute specific enthalpy (NOTE: J m-3 --> J kg-1) ***** do ilook=1,nlook - Hy(ilook) = T2enthalpy_snowlu(Tk(ilook),waterWght,snowfrz_scale)/waterWght ! (J m-3 --> J kg-1) + Hy(ilook) = T2enthalpy_snwWat(Tk(ilook),waterWght,snowfrz_scale)/waterWght ! (J m-3 --> J kg-1) end do ! define the final enthalpy vector @@ -280,10 +283,10 @@ end subroutine T2L_lookup_soil ! ************************************************************************************************************************ -! public subroutine enthalpy2T_snowlu: compute temperature based on specific temperature component of enthalpy -! appropriate when no dry mass, as in snow. Uses look-up table for snow enthalpy +! public subroutine enthalpy2T_snwWat: compute temperature based on specific temperature component of liquid + ice enthalpy +! appropriate when no dry mass, as in snow. Uses look-up table for enthalpy ! ************************************************************************************************************************ -subroutine enthalpy2T_snowlu(Hy,BulkDenWater,fc_param,Tk,err,message) +subroutine enthalpy2T_snwWat(Hy,BulkDenWater,fc_param,Tk,err,message) ! ------------------------------------------------------------------------------------------------------------------------- implicit none ! ------------------------------------------------------------------------------------------------------------------------- @@ -309,7 +312,7 @@ subroutine enthalpy2T_snowlu(Hy,BulkDenWater,fc_param,Tk,err,message) real(rkind) :: dT ! temperature increment ! ------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="enthalpy2T_snowlu/" + err=0; message="enthalpy2T_snwWat/" ! convert input of total enthalpy (J m-3) to total specific enthalpy (J kg-1) H_spec = Hy/BulkDenWater ! (NOTE: no soil) @@ -319,8 +322,8 @@ subroutine enthalpy2T_snowlu(Hy,BulkDenWater,fc_param,Tk,err,message) Tg0 = (H_spec - H_lookup(1))/Cp_ice + T_lookup(1) Tg1 = Tg0+dx ! compute enthalpy - Ht0 = T2enthalpy_snowlu(Tg0,1._rkind,fc_param) - Ht1 = T2enthalpy_snowlu(Tg1,1._rkind,fc_param) + Ht0 = T2enthalpy_snwWat(Tg0,1._rkind,fc_param) + Ht1 = T2enthalpy_snwWat(Tg1,1._rkind,fc_param) ! compute function evaluations f0 = Ht0 - H_spec f1 = Ht1 - H_spec @@ -359,7 +362,7 @@ subroutine enthalpy2T_snowlu(Hy,BulkDenWater,fc_param,Tk,err,message) ! comute new value of Tg Tg1 = Tg0+dT ! get new function evaluation - Ht1 = T2enthalpy_snowlu(Tg1,1._rkind,fc_param) + Ht1 = T2enthalpy_snwWat(Tg1,1._rkind,fc_param) f1 = Ht1 - H_spec ! compute derivative of dT dh = (f1 - f0)/dT @@ -376,22 +379,22 @@ subroutine enthalpy2T_snowlu(Hy,BulkDenWater,fc_param,Tk,err,message) ! and check for convergence if(iter==niter)then; err=20; message=trim(message)//"failedToConverge"; return; end if end do ! (iteration loop) -end subroutine enthalpy2T_snowlu +end subroutine enthalpy2T_snwWat ! ************************************************************************************************************************ -! public function T2enthalpy_snowlu: compute liquid and ice mixture enthalpy based on temperature and mass (J m-3) for a -! layer only where the layer has no dry mass, as in snow. Uses look-up table for snow enthalpy. +! public function T2enthalpy_snwWat: compute liquid and ice mixture enthalpy based on temperature and mass (J m-3) for a +! layer only where the layer has no dry mass, as in snow. ! NOTE: enthalpy is a relative value, defined as zero at Tfreeze where all water is liquid ! ************************************************************************************************************************ -function T2enthalpy_snowlu(Tk,BulkDenWater,fc_param) +function T2enthalpy_snwWat(Tk,BulkDenWater,fc_param) ! ------------------------------------------------------------------------------------------------------------------------- implicit none ! declare dummy variables real(rkind),intent(in) :: Tk ! layer temperature (K) real(rkind),intent(in) :: BulkDenWater ! bulk density of water (kg m-3) real(rkind),intent(in) :: fc_param ! freezing curve parameter (K-1) - real(rkind) :: T2enthalpy_snowlu ! return value of the function, total specific enthalpy (J m-3) + real(rkind) :: T2enthalpy_snwWat ! return value of the function, total specific enthalpy (J m-3) ! declare local variables real(rkind) :: frac_liq ! fraction of liquid water real(rkind) :: enthTempWater ! temperature component of specific enthalpy for total water (liquid and ice) (J kg-1) @@ -410,8 +413,8 @@ function T2enthalpy_snowlu(Tk,BulkDenWater,fc_param) enthMass = -LH_fus*(1._rkind - frac_liq) ! finally, compute the total enthalpy (J m-3) - T2enthalpy_snowlu = BulkDenWater*(enthTempWater + enthMass) !+ BulkDenSoil*enthTempSoil -end function T2enthalpy_snowlu + T2enthalpy_snwWat = BulkDenWater*(enthTempWater + enthMass) !+ BulkDenSoil*enthTempSoil +end function T2enthalpy_snwWat ! ************************************************************************************************************************ @@ -792,452 +795,588 @@ subroutine enthTemp2enthalpy(& end subroutine enthTemp2enthalpy +! ************************************************************************************************************************ +! public subroutine enthalpy2T_cas: compute temperature from enthalpy, canopy air space +! ************************************************************************************************************************ +subroutine enthalpy2T_cas(& + computJac, & ! intent(in): flag if computing for Jacobian update + scalarCanairEnthalpy, & ! intent(in): enthalpy of the canopy air space (J m-3) + scalarCanairTemp, & ! intent(out): canopy air temperature (K) + dCanairTemp_dEnthalpy, & ! intent(inout): derivative of canopy air temperature with enthalpy + err,message) ! intent(out): error control + ! ------------------------------------------------------------------------------------------------------------------------- + implicit none + ! delare dummy variables + ! ------------------------------------------------------------------------------------------------------------------------- + logical(lgt),intent(in) :: computJac ! flag if computing for Jacobian update + ! input: enthalpy state variables + real(rkind),intent(in) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) + ! output: temperature diagnostic variables + real(rkind),intent(out) :: scalarCanairTemp ! canopy air temperature (K) + ! output: derivatives + real(rkind),intent(inout) :: dCanairTemp_dEnthalpy ! derivative of canopy air temperature with enthalpy + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="enthalpy2T_cas/" + + scalarCanairTemp = scalarCanairEnthalpy / ( Cp_air*iden_air ) + Tfreeze + if(computJac) dCanairTemp_dEnthalpy = 1._rkind / ( Cp_air*iden_air ) + +end subroutine enthalpy2T_cas + ! ************************************************************************************************************************ -! public subroutine enthalpy2T: compute temperature from enthalpy and total water content +! public subroutine enthalpy2T_veg: compute temperature from enthalpy and total water content, canopy ! ************************************************************************************************************************ -subroutine enthalpy2T(& - use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - lookup_data, & ! intent(in): lookup table data structure - ! input: enthalpy state variables - scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(in): trial value for enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(in): trial vector of enthalpy of each snow+soil layer (J m-3) - ! input: water state variables - scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - ! output: temperature - scalarCanairTempTrial, & ! intent(out): trial value for canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(out): trial value for canopy temperature (K) - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - ! output: derivatives - dCanairTemp_dEnthalpy, & ! intent(out): derivative of canopy air temperature with enthalpy - dCanopyTemp_dEnthalpy, & ! intent(out): derivative of canopy temperature with enthalpy - dTemp_dEnthalpy, & ! intent(out): derivative of layer temperature with enthalpy - dCanopyTemp_dCanWat, & ! intent(out): derivative of canopy temperature with canopy water - dTemp_dTheta, & ! intent(out): derivative of layer temperature with volumetric total water content - dTemp_dPsi0, & ! intent(out): derivative of layer temperature with total water matric potential - ! output: error control - err,cmessage) ! intent(out): error control +subroutine enthalpy2T_veg(& + computJac, & ! intent(in): flag if computing for Jacobian update + canopyDepth, & ! intent(in): canopy depth (m) + specificHeatVeg, & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation, & ! intent(in): maximum mass of vegetation (kg m-2) + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + scalarCanopyEnthalpy, & ! intent(in): enthalpy of the vegetation canopy (J m-3) + scalarCanopyWat, & ! intent(in): canopy total water (kg m-2) + scalarCanopyTemp, & ! intent(out): canopy temperature (K) + dCanopyTemp_dEnthalpy, & ! intent(inout): derivative of canopy temperature with enthalpy + dCanopyTemp_dCanWat, & ! intent(inout): derivative of canopy temperature with canopy water + err,message) ! intent(out): error control ! ------------------------------------------------------------------------------------------------------------------------- ! downwind routines - USE spline_int_module,only:splint ! use for cubic spline interpolation - USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) - USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) - USE soil_utils_module,only:dTheta_dTk ! differentiate the freezing curve w.r.t. temperature (soil) - USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water - USE soil_utils_module,only:dTheta_dPsi ! compute derivative of the soil moisture characteristic w.r.t. psi (m-1) - USE soil_utilsAddPrime_module,only:d2Theta_dTk2 ! second derivative in the freezing curve w.r.t. temperature (soil) - + USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) + USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) implicit none ! delare dummy variables ! ------------------------------------------------------------------------------------------------------------------------- - logical(lgt),intent(in) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function - ixDomainType, & ! intent(in): named variables defining the domain (iname_cas, iname_veg, etc.) - iLayer, & ! intent(in): index of layer within the snow+soil domain - ixControlIndex, & ! intent(in): index within a given domain + logical(lgt),intent(in) :: computJac ! flag if computing for Jacobian update ! input: data structures - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(zLookup),intent(in) :: lookup_data ! lookup tables - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: ixDomainType ! name of a given model domain - integer(i4b) :: ixControlIndex ! index within a given model domain - ! output: enthalpy state variables - real(rkind),intent(in) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - real(rkind),intent(in) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(in) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) + real(rkind),intent(in) :: canopyDepth ! canopy depth (m) + real(rkind),intent(in) :: specificHeatVeg ! specific heat of vegetation (J kg-1 K-1) + real(rkind),intent(in) :: maxMassVegetation ! maximum mass of vegetation (kg m-2) + real(rkind),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) + ! input: enthalpy state variables + real(rkind),intent(in) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) ! input: water state variables - real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value for canopy total water (kg m-2) - real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - ! output: temperature diagnostic variables - real(rkind),intent(out) :: scalarCanairTempTrial ! trial value for canopy air temperature (K) - real(rkind),intent(out) :: scalarCanopyTempTrial ! trial value for canopy temperature (K) - real(rkind),intent(out) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(in) :: scalarCanopyWat ! trial value for canopy total water (kg m-2) + ! output: temperature diagnostic variables + real(rkind),intent(out) :: scalarCanopyTemp ! trial value for canopy temperature (K) ! output: derivatives - real(rkind),intent(out) :: dCanairTemp_dEnthalpy ! derivative of canopy air temperature with enthalpy - real(rkind),intent(out) :: dCanopyTemp_dEnthalpy ! derivative of canopy temperature with enthalpy - real(rkind),intent(out) :: dTemp_dEnthalpy(:) ! derivative of layer temperature with enthalpy - real(rkind),intent(out) :: dCanopyTemp_dCanWat ! derivative of canopy temperature with canopy water - real(rkind),intent(out) :: dTemp_dTheta(:) ! derivative of layer temperature with volumetric total water content - real(rkind),intent(out) :: dTemp_dPsi0(:) ! derivative of layer temperature with total water matric potential + real(rkind),intent(inout) :: dCanopyTemp_dEnthalpy ! derivative of canopy temperature with enthalpy + real(rkind),intent(inout) :: dCanopyTemp_dCanWat ! derivative of canopy temperature with canopy water ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------------------------------- - ! declare local variables - character(len=128) :: cmessage ! error message in downwind routine - real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - real(rkind) :: volFracWat ! volumetric fraction of total water, liquid+ice (-) - real(rkind) :: diff0 ! temperature difference of Tcrit from Tfreeze - real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head - real(rkind) :: dL ! derivative of soil lookup table with temperature at layer temperature - real(rkind) :: integral_unf ! integral of unfrozen soil water content (from Tfreeze to Tcrit) - real(rkind) :: integral_frz_low ! lower limit of integral of frozen soil water content (from Tfreeze to Tcrit) - real(rkind) :: xConst ! constant in the freezing curve function (m K-1) - real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) - real(rkind) :: dvolFracWat_dPsi0 ! derivative of the soil water content w.r.t. matric head - real(rkind) :: dintegral_unf_dWat ! derivative of integral of unfrozen soil water content with water content - real(rkind) :: dintegral_frz_low_dWat ! derivative of integral of frozen soil water content with water content - ! iteration variables - real(rkind) :: T ! iteration temperature (K) - real(rkind) :: H ! iteration enthalpy (J m-3) - real(rkind) :: diffT ! temperature difference of iteration temp from Tfreeze - real(rkind) :: integral ! iteration integral of snow freezing curve - real(rkind) :: fLiq ! iteration fraction liquid water - real(rkind) :: integral_frz_upp ! upper limit of iteration integral of frozen soil water content (from Tfreeze to soil temperature) - real(rkind) :: arg ! argument of iteration soil hypergeometric function - real(rkind) :: gauss_hg_T ! iteration soil hypergeometric function result - real(rkind) :: enthVeg ! iteration enthalpy of the vegetation (J m-3) - real(rkind) :: enthSoil ! iteration enthalpy of soil particles (J m-3) - real(rkind) :: enthLiq ! iteration enthalpy of the liquid region (J m-3) - real(rkind) :: enthIce ! iteration enthalpy of the ice region (J m-3) - real(rkind) :: enthAir ! iteration enthalpy of air (J m-3) + ! declare local variables, iteration variables + real(rkind) :: T ! iteration temperature (K) + real(rkind) :: H ! iteration enthalpy (J m-3) + real(rkind) :: diffT ! temperature difference of iteration temp from Tfreeze + real(rkind) :: integral ! iteration integral of snow freezing curve + real(rkind) :: fLiq ! iteration fraction liquid water + real(rkind) :: enthVeg ! iteration enthalpy of the vegetation (J m-3) + real(rkind) :: enthLiq ! iteration enthalpy of the liquid region (J m-3) + real(rkind) :: enthIce ! iteration enthalpy of the ice region (J m-3) ! iteration variable derivatives ! iteration variable derivatives - real(rkind) :: dT_dEnthalpy ! derivative of iteration temperature with enthalpy state variable - real(rkind) :: dT_dWat ! derivative of iteration temperature with water state variable - real(rkind) :: dH_dT ! derivative of iteration enthalpy with iteration temperature - real(rkind) :: d2H_dT2 ! second derivative of iteration enthalpy with iteration temperature - real(rkind) :: dH_dEnthalpy ! derivative of iteration enthalpy with enthalpy state variable - real(rkind) :: dH_dT__dEnthalpy ! derivative of iteration enthalpy with iteration temperature with enthalpy - real(rkind) :: dH_dWat ! derivative of iteration enthalpy with water state variable - real(rkind) :: dH_dT__dWat ! derivative of iteration enthalpy with iteration temperature with water state variable - real(rkind) :: dfLiq_dT ! derivative of iteration fraction liquid water with iteration temperature - real(rkind) :: denthVeg_dT ! derivative of iteration enthalpy of vegetation with iteration temperature - real(rkind) :: denthSoil_dT ! derivative of iteration enthalpy of soil with iteration temperature - real(rkind) :: denthIce_dT ! derivative of iteration enthalpy of ice with iteration temperature - real(rkind) :: denthLiq_dT ! derivative of iteration enthalpy of liquid water with iteration temperature - real(rkind) :: denthAir_dT ! derivative of iteration enthalpy of air with temperature - real(rkind) :: d2fLiq_dT2 ! second derivative of iteration fraction liquid water with iteration temperature - real(rkind) :: d2enthVeg_dT2 ! second derivative of iteration enthalpy of vegetation with iteration temperature - real(rkind) :: d2enthSoil_dT2 ! second derivative of iteration enthalpy of soil with iteration temperature - real(rkind) :: d2enthLiq_dT2 ! second derivative of iteration enthalpy of liquid water with iteration temperature - real(rkind) :: d2enthIce_dT2 ! second derivative of iteration enthalpy of ice with iteration temperature - real(rkind) :: d2enthAir_dT2 ! second derivative of iteration enthalpy of air with iteration temperature - real(rkind) :: denthVeg_dWat ! derivative of iteration enthalpy of vegetation with water state variable - real(rkind) :: denthSoil_dWat ! derivative of iteration enthalpy of soil with water state variable - real(rkind) :: denthIce_dWat ! derivative of iteration enthalpy of ice with water state variable - real(rkind) :: denthLiq_dWat ! derivative of iteration enthalpy of liquid water with water state variable - real(rkind) :: denthAir_dWat ! derivative of iteration enthalpy of air with water state variable - real(rkind) :: denthVeg_dT__dWat ! derivative of iteration enthalpy of vegetation with iteration temperature and water state variable - real(rkind) :: denthSoil_dT__dWat ! derivative of iteration enthalpy of soil with iteration temperature and water state variable - real(rkind) :: denthIce_dT__dWat ! derivative of iteration enthalpy of ice with iteration temperaturewith water state variable - real(rkind) :: denthLiq_dT__dWat ! derivative of iteration enthalpy of liquid water with iteration temperature and water state variable - real(rkind) :: denthAir_dT__dWat ! derivative of iteration enthalpy of air with iteration temperature with water state variable - + real(rkind) :: dT_dEnthalpy ! derivative of iteration temperature with enthalpy state variable + real(rkind) :: dT_dWat ! derivative of iteration temperature with water state variable + real(rkind) :: dH_dT ! derivative of iteration enthalpy with iteration temperature + real(rkind) :: d2H_dT2 ! second derivative of iteration enthalpy with iteration temperature + real(rkind) :: dH_dEnthalpy ! derivative of iteration enthalpy with enthalpy state variable + real(rkind) :: dH_dT__dEnthalpy ! derivative of iteration enthalpy with iteration temperature with enthalpy + real(rkind) :: dH_dWat ! derivative of iteration enthalpy with water state variable + real(rkind) :: dH_dT__dWat ! derivative of iteration enthalpy with iteration temperature with water state variable + real(rkind) :: dfLiq_dT ! derivative of iteration fraction liquid water with iteration temperature + real(rkind) :: denthVeg_dT ! derivative of iteration enthalpy of vegetation with iteration temperature + real(rkind) :: denthIce_dT ! derivative of iteration enthalpy of ice with iteration temperature + real(rkind) :: denthLiq_dT ! derivative of iteration enthalpy of liquid water with iteration temperature + real(rkind) :: d2fLiq_dT2 ! second derivative of iteration fraction liquid water with iteration temperature + real(rkind) :: d2enthVeg_dT2 ! second derivative of iteration enthalpy of vegetation with iteration temperature + real(rkind) :: d2enthLiq_dT2 ! second derivative of iteration enthalpy of liquid water with iteration temperature + real(rkind) :: d2enthIce_dT2 ! second derivative of iteration enthalpy of ice with iteration temperature + real(rkind) :: d2enthAir_dT2 ! second derivative of iteration enthalpy of air with iteration temperature + real(rkind) :: denthVeg_dWat ! derivative of iteration enthalpy of vegetation with water state variable + real(rkind) :: denthIce_dWat ! derivative of iteration enthalpy of ice with water state variable + real(rkind) :: denthLiq_dWat ! derivative of iteration enthalpy of liquid water with water state variable + real(rkind) :: denthVeg_dT__dWat ! derivative of iteration enthalpy of vegetation with iteration temperature and water state variable + real(rkind) :: denthIce_dT__dWat ! derivative of iteration enthalpy of ice with iteration temperaturewith water state variable + real(rkind) :: denthLiq_dT__dWat ! derivative of iteration enthalpy of liquid water with iteration temperature and water state variable ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="enthalpy2T/" - - ! identify domain - select case(ixDomainType) - case(iname_cas) - scalarCanairTempTrial = scalarCanairEnthalpy / ( Cp_air*iden_air ) + Tfreeze - dCanairTemp_dEnthalpy = 1._rkind / ( Cp_air*iden_air ) - - case(iname_veg) - ! association to necessary variables for vegetation - vegVars: associate(& - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! canopy depth (m) - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! maximum mass of vegetation (kg m-2) - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! scaling parameter for the snow freezing curve (K-1) - ) - - ! ***** get temperature if unfrozen vegetation - T = scalarCanopyEnthalpy * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial ) + Tfreeze - dT_dEnthalpy = canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial ) - dT_dWat = -Cp_water * scalarCanopyEnthalpy * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWatTrial )**2_i4b - - ! ***** iterate to find temperature if ice exists - if( T1.e-6_rkind ) - ! compute iteration enthalpy function, H - diffT = T - Tfreeze - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - fLiq = fracLiquid(T, snowfrz_scale) - enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth - enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth - enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth - H = enthVeg + enthLiq + enthIce - LH_fus * (1._rkind - fLiq) * scalarCanopyWatTrial / canopyDepth - - ! compute derivative of iteration H with respect to iteration T - ! NOTE: dintegral_dT = fLiq, d2integral_dT2 = dfLiq_dT - dfLiq_dT = dFracLiq_dTk(T,snowfrz_scale) - denthVeg_dT = specificHeatVeg * maxMassVegetation / canopyDepth - denthLiq_dT = Cp_water * scalarCanopyWatTrial * fLiq / canopyDepth - denthIce_dT = Cp_ice * scalarCanopyWatTrial * (1._rkind - fLiq) / canopyDepth - dH_dT = denthVeg_dT + denthLiq_dT + denthIce_dT + LH_fus * dfLiq_dT * scalarCanopyWatTrial / canopyDepth - - d2fLiq_dT2 = 2._rkind * snowfrz_scale**2_i4b * ( 3._rkind * diffT**2_i4b - 1._rkind ) / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**3_i4b - d2enthVeg_dT2 = 0._rkind - d2enthLiq_dT2 = Cp_water * scalarCanopyWatTrial * dfLiq_dT / canopyDepth - d2enthIce_dT2 = -Cp_ice * scalarCanopyWatTrial * dfLiq_dT / canopyDepth - d2H_dT2 = d2enthVeg_dT2 + d2enthLiq_dT2 + d2enthIce_dT2 + LH_fus * d2fLiq_dT2 * scalarCanopyWatTrial / canopyDepth - - ! compute derivative of iteration H with respect to canopy enthalpy - dH_dEnthalpy = dH_dT * dT_dEnthalpy - dH_dT__dEnthalpy = d2H_dT2 * dT_dEnthalpy - - ! compute derivative of iteration H with respect to canopy water - denthVeg_dWat = 0._rkind - denthLiq_dWat = Cp_water * integral / canopyDepth - denthIce_dWat = Cp_ice * ( diffT - integral ) / canopyDepth - dH_dWat = dH_dT * dT_dWat + denthVeg_dWat + denthLiq_dWat + denthIce_dWat - LH_fus * (1._rkind - fLiq) / canopyDepth - - denthVeg_dT__dWat = 0._rkind - denthLiq_dT__dWat = Cp_water * fLiq / canopyDepth - denthIce_dT__dWat = Cp_ice * (1._rkind - fLiq) / canopyDepth - dH_dT__dWat = d2H_dT2 * dT_dWat + denthVeg_dT__dWat + denthLiq_dT__dWat + denthIce_dT__dWat + LH_fus * dfLiq_dT / canopyDepth + ! initialize error control + err=0; message="enthalpy2T_veg/" - ! compute change in T and update, including derivatives - T = T - (H - scalarCanopyEnthalpyTrial)/dH_dT - dT_dEnthalpy = dT_dEnthalpy - ( dH_dEnthalpy - 1._rkind - dH_dT__dEnthalpy * (H - scalarCanopyEnthalpyTrial)/dH_dT ) / dH_dT - dT_dWat = dT_dWat - ( dH_dWat - dH_dT__dWat * (H - scalarCanopyEnthalpyTrial)/dH_dT ) / dH_dT - end do - endif ! (if ice exists) - - ! update temperature and derivatives - scalarCanopyTempTrial = T - dCanopyTemp_dEnthalpy = dT_dEnthalpy - dCanopyTemp_dCanWat = dT_dWat + ! ***** get temperature if unfrozen vegetation + T = scalarCanopyEnthalpy * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWat ) + Tfreeze + dT_dEnthalpy = canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWat ) + dT_dWat = -Cp_water * scalarCanopyEnthalpy * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWat )**2_i4b + + ! ***** iterate to find temperature if ice exists + if( T1.e-6_rkind ) + ! compute iteration enthalpy function, H + diffT = T - Tfreeze + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + fLiq = fracLiquid(T, snowfrz_scale) + enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth + enthLiq = Cp_water * scalarCanopyWat * integral / canopyDepth + enthIce = Cp_ice * scalarCanopyWat * ( diffT - integral ) / canopyDepth + H = enthVeg + enthLiq + enthIce - LH_fus * (1._rkind - fLiq) * scalarCanopyWat / canopyDepth + + ! compute derivative of iteration H with respect to iteration T + ! NOTE: dintegral_dT = fLiq, d2integral_dT2 = dfLiq_dT + dfLiq_dT = dFracLiq_dTk(T,snowfrz_scale) + denthVeg_dT = specificHeatVeg * maxMassVegetation / canopyDepth + denthLiq_dT = Cp_water * scalarCanopyWat * fLiq / canopyDepth + denthIce_dT = Cp_ice * scalarCanopyWat * (1._rkind - fLiq) / canopyDepth + dH_dT = denthVeg_dT + denthLiq_dT + denthIce_dT + LH_fus * dfLiq_dT * scalarCanopyWat / canopyDepth + + ! compute change in T and update + T = T - (H - scalarCanopyEnthalpy)/dH_dT + + if(computJac)then + ! compute second derivatives + ! NOTE: d2integral_dT2 = dfLiq_dT + d2fLiq_dT2 = 2._rkind * snowfrz_scale**2_i4b * ( 3._rkind * diffT**2_i4b - 1._rkind ) / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**3_i4b + d2enthVeg_dT2 = 0._rkind + d2enthLiq_dT2 = Cp_water * scalarCanopyWat * dfLiq_dT / canopyDepth + d2enthIce_dT2 = -Cp_ice * scalarCanopyWat * dfLiq_dT / canopyDepth + d2H_dT2 = d2enthVeg_dT2 + d2enthLiq_dT2 + d2enthIce_dT2 + LH_fus * d2fLiq_dT2 * scalarCanopyWat / canopyDepth + + ! compute derivative of iteration H with respect to canopy enthalpy + dH_dEnthalpy = dH_dT * dT_dEnthalpy + dH_dT__dEnthalpy = d2H_dT2 * dT_dEnthalpy + + ! compute derivative of iteration H with respect to canopy water + denthVeg_dWat = 0._rkind + denthLiq_dWat = Cp_water * integral / canopyDepth + denthIce_dWat = Cp_ice * ( diffT - integral ) / canopyDepth + dH_dWat = dH_dT * dT_dWat + denthVeg_dWat + denthLiq_dWat + denthIce_dWat - LH_fus * (1._rkind - fLiq) / canopyDepth + + denthVeg_dT__dWat = 0._rkind + denthLiq_dT__dWat = Cp_water * fLiq / canopyDepth + denthIce_dT__dWat = Cp_ice * (1._rkind - fLiq) / canopyDepth + dH_dT__dWat = d2H_dT2 * dT_dWat + denthVeg_dT__dWat + denthLiq_dT__dWat + denthIce_dT__dWat + LH_fus * dfLiq_dT / canopyDepth + + ! update derivatives + dT_dEnthalpy = dT_dEnthalpy - ( dH_dEnthalpy - 1._rkind - dH_dT__dEnthalpy * (H - scalarCanopyEnthalpy)/dH_dT ) / dH_dT + dT_dWat = dT_dWat - ( dH_dWat - dH_dT__dWat * (H - scalarCanopyEnthalpy)/dH_dT ) / dH_dT + endif + end do - end associate vegVars + endif ! (if ice exists) - case(iname_snow) + ! update temperature and derivatives + scalarCanopyTemp = T + if(computJac)then + dCanopyTemp_dEnthalpy = dT_dEnthalpy + dCanopyTemp_dCanWat = dT_dWat + endif - ! association to necessary variables for snow - snowVars: associate(& - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) +end subroutine enthalpy2T_veg - ! ***** iterate to find temperature, ice always exists - T = 260._rkind ! initial guess, very cold - H = mLayerEnthalpy(iLayer) + 1._rkind ! to start the iteration - dT_dEnthalpy = 0._rkind - dT_dWat = 0._rkind - do while( abs(mLayerEnthalpy(iLayer)-H)>1.e-6_rkind ) - ! compute iteration enthalpy function, H - diffT = T - Tfreeze - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - fLiq = fracLiquid(T, snowfrz_scale) - enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral - enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) - enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) - H = enthLiq + enthIce + enthAir - iden_water * LH_fus * (1._rkind - fLiq) * mLayerVolFracWatTrial(iLayer) - - ! compute derivative of iteration H with respect to iteration T - ! NOTE: dintegral_dT = fLiq, d2integral_dT2 = dfLiq_dT - dfLiq_dT = dFracLiq_dTk(T,snowfrz_scale) - denthLiq_dT = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * fLiq - denthIce_dT = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * (1._rkind - fLiq) - denthAir_dT = iden_air * Cp_air * (1._rkind - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(1._rkind-fLiq) + fLiq ) ) - dH_dT = denthLiq_dT + denthIce_dT + denthAir_dT + iden_water * LH_fus * dfLiq_dT * mLayerVolFracWatTrial(iLayer) - - d2fLiq_dT2 = 2._rkind * snowfrz_scale**2_i4b * ( 3._rkind * diffT**2_i4b - 1._rkind ) / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**3_i4b - d2enthLiq_dT2 = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * dfLiq_dT - d2enthIce_dT2 = -iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * dfLiq_dT - d2enthAir_dT2 = iden_air * Cp_air * ( mLayerVolFracWatTrial(iLayer) * dfLiq_dT *( (iden_water/iden_ice) - 1._rkind ) ) - d2H_dT2 = d2enthLiq_dT2 + d2enthIce_dT2 + d2enthAir_dT2 + iden_water * LH_fus * d2fLiq_dT2 * mLayerVolFracWatTrial(iLayer) - - ! compute derivative of iteration H with respect to layer enthalpy - dH_dEnthalpy = dH_dT * dT_dEnthalpy - dH_dT__dEnthalpy = d2H_dT2 * dT_dEnthalpy - - ! compute derivative ofiteration H with respect to layer water content - denthLiq_dWat = iden_water * Cp_water * integral - denthIce_dWat = iden_water * Cp_ice * ( diffT - integral ) - denthAir_dWat = -iden_air * Cp_air * ( (iden_water/iden_ice)*(diffT-integral) + integral ) - dH_dWat = dH_dT * dT_dWat + denthLiq_dWat + denthIce_dWat + denthAir_dWat - iden_water * LH_fus * (1._rkind - fLiq) - - denthLiq_dT__dWat = iden_water * Cp_water * fLiq - denthIce_dT__dWat = iden_water * Cp_ice * (1._rkind - fLiq) - denthAir_dT__dWat = -iden_air * Cp_air * ( (iden_water/iden_ice)*(1._rkind-fLiq) + fLiq ) - dH_dT__dWat = d2H_dT2 * dT_dWat + denthLiq_dT__dWat + denthIce_dT__dWat + denthAir_dT__dWat + iden_water * LH_fus * dfLiq_dT - - ! compute change in T and update, including derivatives - T = T - (H - mLayerEnthalpy(iLayer))/dH_dT - dT_dEnthalpy = dT_dEnthalpy - ( dH_dEnthalpy - 1._rkind - dH_dT__dEnthalpy * (H - mLayerEnthalpy(iLayer))/dH_dT ) / dH_dT - dT_dWat = dT_dWat - ( dH_dWat - dH_dT__dWat * (H - mLayerEnthalpy(iLayer))/dH_dT ) / dH_dT - end do - - ! update temperature and derivatives - mLayerTemp(iLayer) = T - dTemp_dEnthalpy(iLayer) = dT_dEnthalpy - dTemp_dTheta(iLayer) = dT_dWat +! ************************************************************************************************************************ +! public subroutine enthalpy2T_snow: compute temperature from enthalpy and total water content, snow layer +! ************************************************************************************************************************ +subroutine enthalpy2T_snow(& + computJac, & ! intent(in): flag if computing for Jacobian update + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + mLayerEnthalpy, & ! intent(in): enthalpy of snow+soil layer (J m-3) + mLayerVolFracWat, & ! intent(in): volumetric total water content (-) + mLayerTemp, & ! intent(out): layer temperature (K) + dTemp_dEnthalpy, & ! intent(inout): derivative of layer temperature with enthalpy + dTemp_dTheta, & ! intent(inout): derivative of layer temperature with volumetric total water content + err,message) ! intent(out): error control + ! ------------------------------------------------------------------------------------------------------------------------- + ! downwind routines + USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) + USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) + + implicit none + ! delare dummy variables + ! ------------------------------------------------------------------------------------------------------------------------- + logical(lgt),intent(in) :: computJac ! flag if computing for Jacobian update + ! input: data structures + real(rkind),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) + ! input: enthalpy state variables + real(rkind),intent(in) :: mLayerEnthalpy ! enthalpy of each snow+soil layer (J m-3) + ! input: water state variables + real(rkind),intent(in) :: mLayerVolFracWat ! volumetric total water content (-) + ! output: temperature diagnostic variables + real(rkind),intent(out) :: mLayerTemp ! layer temperature (K) + ! output: derivatives + real(rkind),intent(inout) :: dTemp_dEnthalpy ! derivative of layer temperature with enthalpy + real(rkind),intent(inout) :: dTemp_dTheta ! derivative of layer temperature with volumetric total water content + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------------------------------- + ! declare local variables, iteration variables + real(rkind) :: T ! iteration temperature (K) + real(rkind) :: H ! iteration enthalpy (J m-3) + real(rkind) :: diffT ! temperature difference of iteration temp from Tfreeze + real(rkind) :: fLiq ! iteration fraction liquid water + real(rkind) :: enthLiq ! iteration enthalpy of the liquid region (J m-3) + real(rkind) :: enthIce ! iteration enthalpy of the ice region (J m-3) + real(rkind) :: enthAir ! iteration enthalpy of air (J m-3) + ! iteration variable derivatives + real(rkind) :: dT_dEnthalpy ! derivative of iteration temperature with enthalpy state variable + real(rkind) :: dT_dWat ! derivative of iteration temperature with water state variable + real(rkind) :: dH_dT ! derivative of iteration enthalpy with iteration temperature + real(rkind) :: d2H_dT2 ! second derivative of iteration enthalpy with iteration temperature + real(rkind) :: dH_dEnthalpy ! derivative of iteration enthalpy with enthalpy state variable + real(rkind) :: dH_dT__dEnthalpy ! derivative of iteration enthalpy with iteration temperature with enthalpy + real(rkind) :: dH_dWat ! derivative of iteration enthalpy with water state variable + real(rkind) :: dH_dT__dWat ! derivative of iteration enthalpy with iteration temperature with water state variable + real(rkind) :: dfLiq_dT ! derivative of iteration fraction liquid water with iteration temperature + real(rkind) :: denthIce_dT ! derivative of iteration enthalpy of ice with iteration temperature + real(rkind) :: denthLiq_dT ! derivative of iteration enthalpy of liquid water with iteration temperature + real(rkind) :: denthAir_dT ! derivative of iteration enthalpy of air with temperature + real(rkind) :: d2fLiq_dT2 ! second derivative of iteration fraction liquid water with iteration temperature + real(rkind) :: d2enthLiq_dT2 ! second derivative of iteration enthalpy of liquid water with iteration temperature + real(rkind) :: d2enthIce_dT2 ! second derivative of iteration enthalpy of ice with iteration temperature + real(rkind) :: d2enthAir_dT2 ! second derivative of iteration enthalpy of air with iteration temperature + real(rkind) :: denthIce_dWat ! derivative of iteration enthalpy of ice with water state variable + real(rkind) :: denthLiq_dWat ! derivative of iteration enthalpy of liquid water with water state variable + real(rkind) :: denthAir_dWat ! derivative of iteration enthalpy of air with water state variable + real(rkind) :: denthIce_dT__dWat ! derivative of iteration enthalpy of ice with iteration temperaturewith water state variable + real(rkind) :: denthLiq_dT__dWat ! derivative of iteration enthalpy of liquid water with iteration temperature and water state variable + real(rkind) :: denthAir_dT__dWat ! derivative of iteration enthalpy of air with iteration temperature with water state variable + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="enthalpy2T_snow/" + + ! ***** iterate to find temperature, ice always exists + T = 260._rkind ! initial guess, very cold + H = mLayerEnthalpy + 1._rkind ! to start the iteration + dT_dEnthalpy = 0._rkind + dT_dWat = 0._rkind + + do while( abs(mLayerEnthalpy-H)>1.e-6_rkind ) + ! compute iteration enthalpy function, H + diffT = T - Tfreeze + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + fLiq = fracLiquid(T, snowfrz_scale) + enthLiq = iden_water * Cp_water * mLayerVolFracWat * integral + enthIce = iden_water * Cp_ice * mLayerVolFracWat * ( diffT - integral ) + enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWat * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) + H = enthLiq + enthIce + enthAir - iden_water * LH_fus * (1._rkind - fLiq) * mLayerVolFracWat + + ! compute derivative of iteration H with respect to iteration T + ! NOTE: dintegral_dT = fLiq + dfLiq_dT = dFracLiq_dTk(T,snowfrz_scale) + denthLiq_dT = iden_water * Cp_water * mLayerVolFracWat * fLiq + denthIce_dT = iden_water * Cp_ice * mLayerVolFracWat * (1._rkind - fLiq) + denthAir_dT = iden_air * Cp_air * (1._rkind - mLayerVolFracWat * ( (iden_water/iden_ice)*(1._rkind-fLiq) + fLiq ) ) + dH_dT = denthLiq_dT + denthIce_dT + denthAir_dT + iden_water * LH_fus * dfLiq_dT * mLayerVolFracWat + + ! compute change in T and update + T = T - (H - mLayerEnthalpy)/dH_dT + + if(computJac)then + ! compute second derivatives + ! NOTE: d2integral_dT2 = dfLiq_dT + d2fLiq_dT2 = 2._rkind * snowfrz_scale**2_i4b * ( 3._rkind * diffT**2_i4b - 1._rkind ) / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**3_i4b + d2enthLiq_dT2 = iden_water * Cp_water * mLayerVolFracWat * dfLiq_dT + d2enthIce_dT2 = -iden_water * Cp_ice * mLayerVolFracWat * dfLiq_dT + d2enthAir_dT2 = iden_air * Cp_air * ( mLayerVolFracWat * dfLiq_dT *( (iden_water/iden_ice) - 1._rkind ) ) + d2H_dT2 = d2enthLiq_dT2 + d2enthIce_dT2 + d2enthAir_dT2 + iden_water * LH_fus * d2fLiq_dT2 * mLayerVolFracWat + + ! compute derivative of iteration H with respect to layer enthalpy + dH_dEnthalpy = dH_dT * dT_dEnthalpy + dH_dT__dEnthalpy = d2H_dT2 * dT_dEnthalpy + + ! compute derivative ofiteration H with respect to layer water content + denthLiq_dWat = iden_water * Cp_water * integral + denthIce_dWat = iden_water * Cp_ice * ( diffT - integral ) + denthAir_dWat = -iden_air * Cp_air * ( (iden_water/iden_ice)*(diffT-integral) + integral ) + dH_dWat = dH_dT * dT_dWat + denthLiq_dWat + denthIce_dWat + denthAir_dWat - iden_water * LH_fus * (1._rkind - fLiq) + + denthLiq_dT__dWat = iden_water * Cp_water * fLiq + denthIce_dT__dWat = iden_water * Cp_ice * (1._rkind - fLiq) + denthAir_dT__dWat = -iden_air * Cp_air * ( (iden_water/iden_ice)*(1._rkind-fLiq) + fLiq ) + dH_dT__dWat = d2H_dT2 * dT_dWat + denthLiq_dT__dWat + denthIce_dT__dWat + denthAir_dT__dWat + iden_water * LH_fus * dfLiq_dT + + ! update derivatives + dT_dEnthalpy = dT_dEnthalpy - ( dH_dEnthalpy - 1._rkind - dH_dT__dEnthalpy * (H - mLayerEnthalpy)/dH_dT ) / dH_dT + dT_dWat = dT_dWat - ( dH_dWat - dH_dT__dWat * (H - mLayerEnthalpy)/dH_dT ) / dH_dT + endif + end do + + ! update temperature and derivatives + mLayerTemp = T + if(computJac)then + dTemp_dEnthalpy = dT_dEnthalpy + dTemp_dTheta = dT_dWat + endif + +end subroutine enthalpy2T_snow - end associate snowVars - case(iname_soil) - ! make association to variables for soil - soilVars: associate(& - soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex), & ! intrinsic soil density (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex), & ! soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex), & ! volumetric residual water content (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex), & ! van Genuchten "alpha" parameter (m-1) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) & ! van Genuchten "n" parameter (-) - ) ! end associate statement - - ! diagnostic variables - vGn_m = 1._rkind - 1._rkind/vGn_n - Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) - volFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - dTcrit_dPsi0 = merge(gravity*Tfreeze/LH_fus,0._rkind,mLayerMatricHeadTrial(ixControlIndex)<=0._rkind) - dvolFracWat_dPsi0 = dTheta_dPsi(mLayerMatricHead(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - - ! ***** get temperature if unfrozen soil - T = mLayerEnthalpy(iLayer) / ( iden_water * Cp_water * volFracWat + soil_dens_intr * Cp_soil * (1._rkind - theta_sat) + iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) ) + Tfreeze - dT_dEnthalpy = 1._rkind / ( iden_water * Cp_water * volFracWat + soil_dens_intr*Cp_soil*(1._rkind - theta_sat) + iden_air*Cp_air*(1._rkind - theta_sat - volFracWat) ) - dT_dWat = -iden_water * Cp_water * dTheta_dWat * mLayerEnthalpy(iLayer) / ( iden_water * Cp_water * volFracWat + soil_dens_intr * Cp_soil * (1._rkind - theta_sat) + iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) )**2_i4b - - ! ***** iterate to find temperature if ice exists - if( T lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup, & ! temperature (K) - Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup, & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) - L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function - ) ! end associate statement + implicit none + ! delare dummy variables + ! ------------------------------------------------------------------------------------------------------------------------- + logical(lgt),intent(in) :: computJac ! flag if computing for Jacobian update + logical(lgt),intent(in) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function + ! input: data structures + real(rkind),intent(in) :: soil_dens_intr ! intrinsic soil density (kg m-3) + real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter + real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter + real(rkind),intent(in) :: theta_sat ! soil porosity (-) + real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) + real(rkind),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) + integer(i4b),intent(in) :: ixControlIndex ! index within a given model domain + type(zLookup),intent(in) :: lookup_data ! lookup tables + ! input: enthalpy state variables + real(rkind),intent(in) :: mLayerEnthalpy ! enthalpy of each snow+soil layer (J m-3) + ! input: water state variables + real(rkind),intent(in) :: mLayerMatricHead ! total water matric potential (m) + ! output: temperature diagnostic variables + real(rkind),intent(out) :: mLayerTemp ! layer temperature (K) + ! output: derivatives + real(rkind),intent(inout) :: dTemp_dEnthalpy ! derivative of layer temperature with enthalpy + real(rkind),intent(inout) :: dTemp_dTheta ! derivative of layer temperature with volumetric total water content + real(rkind),intent(inout) :: dTemp_dPsi0 ! derivative of layer temperature with total water matric potential + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------------------------------- + ! declare local variables + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind) :: volFracWat ! volumetric fraction of total water, liquid+ice (-) + real(rkind) :: diff0 ! temperature difference of Tcrit from Tfreeze + real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head + real(rkind) :: dL ! derivative of soil lookup table with temperature at layer temperature + real(rkind) :: integral_unf ! integral of unfrozen soil water content (from Tfreeze to Tcrit) + real(rkind) :: integral_frz_low ! lower limit of integral of frozen soil water content (from Tfreeze to Tcrit) + real(rkind) :: xConst ! constant in the freezing curve function (m K-1) + real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) + real(rkind) :: dvolFracWat_dPsi0 ! derivative of the soil water content w.r.t. matric head + real(rkind) :: dintegral_unf_dWat ! derivative of integral of unfrozen soil water content with water content + real(rkind) :: dintegral_frz_low_dWat ! derivative of integral of frozen soil water content with water content + ! iteration variables + real(rkind) :: T ! iteration temperature (K) + real(rkind) :: H ! iteration enthalpy (J m-3) + real(rkind) :: diffT ! temperature difference of iteration temp from Tfreeze + real(rkind) :: integral ! iteration integral of snow freezing curve + real(rkind) :: fLiq ! iteration fraction liquid water + real(rkind) :: integral_frz_upp ! upper limit of iteration integral of frozen soil water content (from Tfreeze to soil temperature) + real(rkind) :: arg ! argument of iteration soil hypergeometric function + real(rkind) :: gauss_hg_T ! iteration soil hypergeometric function result + real(rkind) :: enthSoil ! iteration enthalpy of soil particles (J m-3) + real(rkind) :: enthLiq ! iteration enthalpy of the liquid region (J m-3) + real(rkind) :: enthIce ! iteration enthalpy of the ice region (J m-3) + real(rkind) :: enthAir ! iteration enthalpy of air (J m-3) + ! iteration variable derivatives + real(rkind) :: dT_dEnthalpy ! derivative of iteration temperature with enthalpy state variable + real(rkind) :: dT_dWat ! derivative of iteration temperature with water state variable + real(rkind) :: dH_dT ! derivative of iteration enthalpy with iteration temperature + real(rkind) :: d2H_dT2 ! second derivative of iteration enthalpy with iteration temperature + real(rkind) :: dH_dEnthalpy ! derivative of iteration enthalpy with enthalpy state variable + real(rkind) :: dH_dT__dEnthalpy ! derivative of iteration enthalpy with iteration temperature with enthalpy + real(rkind) :: dH_dWat ! derivative of iteration enthalpy with water state variable + real(rkind) :: dH_dT__dWat ! derivative of iteration enthalpy with iteration temperature with water state variable + real(rkind) :: dfLiq_dT ! derivative of iteration fraction liquid water with iteration temperature + real(rkind) :: denthSoil_dT ! derivative of iteration enthalpy of soil with iteration temperature + real(rkind) :: denthIce_dT ! derivative of iteration enthalpy of ice with iteration temperature + real(rkind) :: denthLiq_dT ! derivative of iteration enthalpy of liquid water with iteration temperature + real(rkind) :: denthAir_dT ! derivative of iteration enthalpy of air with temperature + real(rkind) :: d2fLiq_dT2 ! second derivative of iteration fraction liquid water with iteration temperature + real(rkind) :: d2enthSoil_dT2 ! second derivative of iteration enthalpy of soil with iteration temperature + real(rkind) :: d2enthLiq_dT2 ! second derivative of iteration enthalpy of liquid water with iteration temperature + real(rkind) :: d2enthIce_dT2 ! second derivative of iteration enthalpy of ice with iteration temperature + real(rkind) :: d2enthAir_dT2 ! second derivative of iteration enthalpy of air with iteration temperature + real(rkind) :: denthSoil_dWat ! derivative of iteration enthalpy of soil with water state variable + real(rkind) :: denthIce_dWat ! derivative of iteration enthalpy of ice with water state variable + real(rkind) :: denthLiq_dWat ! derivative of iteration enthalpy of liquid water with water state variable + real(rkind) :: denthAir_dWat ! derivative of iteration enthalpy of air with water state variable + real(rkind) :: denthSoil_dT__dWat ! derivative of iteration enthalpy of soil with iteration temperature and water state variable + real(rkind) :: denthIce_dT__dWat ! derivative of iteration enthalpy of ice with iteration temperaturewith water state variable + real(rkind) :: denthLiq_dT__dWat ! derivative of iteration enthalpy of liquid water with iteration temperature and water state variable + real(rkind) :: denthAir_dT__dWat ! derivative of iteration enthalpy of air with iteration temperature with water state variable + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="enthalpy2T_soil/" + + Tcrit = crit_soilT( mLayerMatricHead ) + volFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + dTcrit_dPsi0 = merge(gravity*Tfreeze/LH_fus,0._rkind,mLayerMatricHead<=0._rkind) + dvolFracWat_dPsi0 = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + + ! ***** get temperature if unfrozen soil + T = mLayerEnthalpy / ( iden_water * Cp_water * volFracWat + soil_dens_intr * Cp_soil * (1._rkind - theta_sat) + iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) ) + Tfreeze + dT_dEnthalpy = 1._rkind / ( iden_water * Cp_water * volFracWat + soil_dens_intr*Cp_soil*(1._rkind - theta_sat) + iden_air*Cp_air*(1._rkind - theta_sat - volFracWat) ) + dT_dWat = -iden_water * Cp_water * dTheta_dWat * mLayerEnthalpy / ( iden_water * Cp_water * volFracWat + soil_dens_intr * Cp_soil * (1._rkind - theta_sat) + iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) )**2_i4b + + ! ***** iterate to find temperature if ice exists + if( T lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup, & ! temperature (K) + Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup, & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) + L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + ) ! end associate statement + + call splint(Tk,Ly,L2,Tcrit,integral_frz_low,dL,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + if(computJac) dintegral_frz_low_dWat = dL * dTcrit_dPsi0 + + end associate lookVars + + else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature + arg = (vGn_alpha * mLayerMatricHead)**vGn_n + gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + integral_frz_low = diff0 * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + if(computJac) dintegral_frz_low_dWat = volFracWat * dTcrit_dPsi0 + endif + else ! Tcrit=Tfreeze, i.e. mLayerMatricHead>0 + integral_frz_low = 0._rkind + dintegral_frz_low_dWat = 0._rkind + end if - call splint(Tk,Ly,L2,Tcrit,integral_frz_low,dL,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - dintegral_frz_low_dWat = dL * dTcrit_dPsi0 - - end associate lookVars - - else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature - arg = (vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n - gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - dgauss_hg_T_dT = ( (1._rkind + arg)**(-vGn_m) - gauss_hg_T ) / vGn_n*(-arg) - dintegral_frz_low_dWat = volFracWat * dTcrit_dPsi0 - endif - else ! Tcrit=Tfreeze, i.e. mLayerMatricHeadTrial(ixControlIndex)>0 - integral_frz_low = 0._rkind - dintegral_frz_low_dWat = 0._rkind - end if - - ! get the upper limit of the integral - xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) - do while( abs(mLayerEnthalpy(iLayer)-H)>1.e-6_rkind ) - diffT = T - Tfreeze - mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution, DIFFERENT from the liquid water matric potential used in the flux calculations - arg = (vGn_alpha * mLayerPsiLiq)**vGn_n + ! get the upper limit of the integral + xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) + do while( abs(mLayerEnthalpy-H)>1.e-6_rkind ) + diffT = T - Tfreeze + mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution, DIFFERENT from the liquid water matric potential used in the flux calculations + arg = (vGn_alpha * mLayerPsiLiq)**vGn_n + + if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature + ! make associate to the the lookup table + lookVars: associate(& + Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup, & ! temperature (K) + Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup, & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) + L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + ) ! end associate statement + + ! get the upper limit of the integral + call splint(Tk,Ly,L2,T,integral_frz_upp,dL,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + dintegral_frz_upp_dT = dL + if(computJac) d2integral_frz_upp_dT2 = 0._rkind + + end associate lookVars + + else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature + gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + integral_frz_upp = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + dintegral_frz_upp_dT = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) ! fLiq + if(computJac) d2integral_frz_upp_dT2 = dTheta_dTk(T,theta_res,theta_sat,vGn_alpha,vGn_n,vGn_m) ! dfLiq_dT + end if + + ! compute iteration enthalpy function, H + ! NOTE: here fLiq is the total liquid fraction, not fraction of water fraction that is liquid + fLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + enthLiq = iden_water * Cp_water * (integral_unf + integral_frz_upp - integral_frz_low) + enthIce = iden_ice * Cp_ice * ( volFracWat * diffT - (integral_unf + integral_frz_upp - integral_frz_low) ) + enthSoil = soil_dens_intr * Cp_soil * ( 1._rkind - theta_sat ) * diffT + enthAir = iden_air * Cp_air * ( 1._rkind - theta_sat - volFracWat ) * diffT + H = enthLiq + enthIce + enthSoil + enthAir - iden_water * LH_fus * (volFracWat - fLiq) + + ! compute derivative of iteration H with respect to iteration T + dfLiq_dT = dTheta_dTk(T,theta_res,theta_sat,vGn_alpha,vGn_n,vGn_m) + denthLiq_dT = iden_water * Cp_water * dintegral_frz_upp_dT + denthIce_dT = iden_ice * Cp_ice * ( volFracWat - dintegral_frz_upp_dT ) + denthSoil_dT = soil_dens_intr * Cp_soil * ( 1._rkind - theta_sat ) + denthAir_dT = iden_air * Cp_air * ( 1._rkind - theta_sat - volFracWat ) + dH_dT = denthLiq_dT + denthIce_dT + denthSoil_dT + denthAir_dT + iden_water * LH_fus * dfLiq_dT - if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature - ! make associate to the the lookup table - lookVars: associate(& - Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup, & ! temperature (K) - Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup, & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) - L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function - ) ! end associate statement - - ! get the upper limit of the integral - call splint(Tk,Ly,L2,T,integral_frz_upp,dL,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - dintegral_frz_upp_dT = dL - d2integral_frz_upp_dT2 = 0._rkind - - end associate lookVars - - else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature - gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - integral_frz_upp = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) - dintegral_frz_upp_dT = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) ! fLiq - d2integral_frz_upp_dT2 = dTheta_dTk(T,theta_res,theta_sat,vGn_alpha,vGn_n,vGn_m) ! dfLiq_dT - end if - - ! compute iteration enthalpy function, H - ! NOTE: here fLiq is the total liquid fraction, not fraction of water fraction that is liquid - fLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - enthLiq = iden_water * Cp_water * (integral_unf + integral_frz_upp - integral_frz_low) - enthIce = iden_ice * Cp_ice * ( volFracWat * diffT - (integral_unf + integral_frz_upp - integral_frz_low) ) - enthSoil = soil_dens_intr * Cp_soil * ( 1._rkind - theta_sat ) * diffT - enthAir = iden_air * Cp_air * ( 1._rkind - theta_sat - volFracWat ) * diffT - H = enthLiq + enthIce + enthSoil + enthAir - iden_water * LH_fus * (volFracWat - fLiq) - - ! compute derivative of iteration H with respect to iteration T - dfLiq_dT = dTheta_dTk(T,theta_res,theta_sat,vGn_alpha,vGn_n,vGn_m) - denthLiq_dT = iden_water * Cp_water * dintegral_frz_upp_dT - denthIce_dT = iden_ice * Cp_ice * ( volFracWat - dintegral_frz_upp_dT ) - denthSoil_dT = soil_dens_intr * Cp_soil * ( 1._rkind - theta_sat ) - denthAir_dT = iden_air * Cp_air * ( 1._rkind - theta_sat - volFracWat ) - dH_dT = denthLiq_dT + denthIce_dT + denthSoil_dT + denthAir_dT + iden_water * LH_fus * dfLiq_dT - - d2fLiq_dT2 = d2Theta_dTk2(T,theta_res,theta_sat,vGn_alpha,vGn_n,vGn_m) - d2enthLiq_dT2 = iden_water * Cp_water * d2integral_frz_upp_dT2 - d2enthIce_dT2 = -iden_ice * Cp_ice * d2integral_frz_upp_dT2 - d2enthSoil_dT2 = 0._rkind - d2enthAir_dT2 = 0._rkind - d2H_dT2 = d2enthLiq_dT2 + d2enthIce_dT2 + d2enthSoil_dT2 + d2enthAir_dT2 + iden_water * LH_fus * d2fLiq_dT2 - - ! compute derivative of iteration H with respect to layer enthalpy - dH_dEnthalpy = dH_dT * dT_dEnthalpy - dH_dT__dEnthalpy = d2H_dT2 * dT_dEnthalpy - - ! compute derivative ofiteration H with respect to layer water content - denthLiq_dWat = iden_water * Cp_water * (dintegral_unf_dWat - dintegral_frz_low_dWat) - denthIce_dWat = iden_ice * Cp_ice * ( dvolFracWat_dPsi0 * diffT - (dintegral_unf_dWat - dintegral_frz_low_dWat) ) - denthSoil_dWat = 0._rkind - denthAir_dWat = -iden_air * Cp_air * dvolFracWat_dPsi0 * diffT - dH_dWat = dH_dT * dT_dWat + denthLiq_dWat + denthIce_dWat + denthAir_dWat - iden_water * LH_fus * dvolFracWat_dPsi0 - - denthLiq_dT__dWat = 0._rkind - denthIce_dT__dWat = iden_ice * Cp_ice * dvolFracWat_dPsi0 - denthSoil_dT__dWat = 0._rkind - denthAir_dT__dWat = -iden_air * Cp_air * dvolFracWat_dPsi0 - dH_dT__dWat = d2H_dT2 * dT_dWat + denthLiq_dT__dWat + denthIce_dT__dWat + denthSoil_dT__dWat + denthAir_dT__dWat - - ! compute change in T and update, including derivatives - T = T - (H - mLayerEnthalpy(iLayer))/dH_dT - dT_dEnthalpy = dT_dEnthalpy - ( dH_dEnthalpy - 1._rkind - dH_dT__dEnthalpy * (H - mLayerEnthalpy(iLayer))/dH_dT ) / dH_dT - dT_dWat = dT_dWat - ( dH_dWat - dH_dT__dWat * (H - mLayerEnthalpy(iLayer))/dH_dT ) / dH_dT - end do - end if ! (if ice exists) - - ! update temperature and derivatives - mLayerTemp(iLayer) = T - dTemp_dEnthalpy(iLayer) = dT_dEnthalpy - dTemp_dTheta(iLayer) = realMissing ! do not use - dTemp_dPsi(ixControlIndex) = dT_dWat - - end associate soilVars + ! compute change in T and update + T = T - (H - mLayerEnthalpy)/dH_dT + + if(computJac)then + ! compute second derivatives + d2fLiq_dT2 = d2Theta_dTk2(T,theta_res,theta_sat,vGn_alpha,vGn_n,vGn_m) + d2enthLiq_dT2 = iden_water * Cp_water * d2integral_frz_upp_dT2 + d2enthIce_dT2 = -iden_ice * Cp_ice * d2integral_frz_upp_dT2 + d2enthSoil_dT2 = 0._rkind + d2enthAir_dT2 = 0._rkind + d2H_dT2 = d2enthLiq_dT2 + d2enthIce_dT2 + d2enthSoil_dT2 + d2enthAir_dT2 + iden_water * LH_fus * d2fLiq_dT2 + + ! compute derivative of iteration H with respect to layer enthalpy + dH_dEnthalpy = dH_dT * dT_dEnthalpy + dH_dT__dEnthalpy = d2H_dT2 * dT_dEnthalpy + + ! compute derivative ofiteration H with respect to layer water content + denthLiq_dWat = iden_water * Cp_water * (dintegral_unf_dWat - dintegral_frz_low_dWat) + denthIce_dWat = iden_ice * Cp_ice * ( dvolFracWat_dPsi0 * diffT - (dintegral_unf_dWat - dintegral_frz_low_dWat) ) + denthSoil_dWat = 0._rkind + denthAir_dWat = -iden_air * Cp_air * dvolFracWat_dPsi0 * diffT + dH_dWat = dH_dT * dT_dWat + denthLiq_dWat + denthIce_dWat + denthAir_dWat - iden_water * LH_fus * dvolFracWat_dPsi0 + + denthLiq_dT__dWat = 0._rkind + denthIce_dT__dWat = iden_ice * Cp_ice * dvolFracWat_dPsi0 + denthSoil_dT__dWat = 0._rkind + denthAir_dT__dWat = -iden_air * Cp_air * dvolFracWat_dPsi0 + dH_dT__dWat = d2H_dT2 * dT_dWat + denthLiq_dT__dWat + denthIce_dT__dWat + denthSoil_dT__dWat + denthAir_dT__dWat + + ! update derivatives + dT_dEnthalpy = dT_dEnthalpy - ( dH_dEnthalpy - 1._rkind - dH_dT__dEnthalpy * (H - mLayerEnthalpy)/dH_dT ) / dH_dT + dT_dWat = dT_dWat - ( dH_dWat - dH_dT__dWat * (H - mLayerEnthalpy)/dH_dT ) / dH_dT + endif + end do + end if ! (if ice exists) - ! ----- - ! - checks... - ! ----------- - case(iname_aquifer); cycle ! aquifer: do nothing - case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return - end select + ! update temperature and derivatives + mLayerTemp = T + if(computJac)then + dTemp_dEnthalpy = dT_dEnthalpy + dTemp_dTheta = realMissing ! do not use + dTemp_dPsi = dT_dWat + endif -end subroutine enthalpy2T +end subroutine enthalpy2T_soil !---------------------------------------------------------------------- diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index b95889fae..584cad06e 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -407,46 +407,47 @@ subroutine eval8summaWithPrime(& ! update diagnostic variables and derivatives ! NOTE: if we are using enthalpy as a state variable, currently all *TempPrime, *IcePrime, and *LiqPrime are set to realMissing - ! This possibly could cause problems if we use splitting, but we are not using splitting at the moment + ! This possibly could cause problems (?) if we use splitting, but we are not using splitting at the moment call updateVarsWithPrime(& ! input - ixNrgConserv.ne.closedForm, & ! intent(in): flag if need to update temperature from enthalpy - .true., & ! intent(in): flag if computing for Jacobian update - .false., & ! intent(in): flag to adjust temperature to account for the energy - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - lookup_data, & ! intent(in): lookup table data structure + ixNrgConserv.ne.closedForm, & ! intent(in): flag if need to update temperature from enthalpy + ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy + .true., & ! intent(in): flag if computing for Jacobian update + .false., & ! intent(in): flag to adjust temperature to account for the energy + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + lookup_data, & ! intent(in): lookup table data structure ! input: enthalpy state variables - scalarCanairEnthalpyTrial,& ! intent(in): trial value for enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial,& ! intent(in): trial value for enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(in): trial vector of enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(in): trial value for enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(in): trial vector of enthalpy of each snow+soil layer (J m-3) ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - scalarCanopyTempPrime, & ! intent(inout): trial value of time derivative canopy temperature (K s-1) - scalarCanopyWatPrime, & ! intent(inout): trial value of time derivative canopy total water (kg m-2 s-1) - scalarCanopyLiqPrime, & ! intent(inout): trial value of time derivative canopy liquid water (kg m-2 s-1) - scalarCanopyIcePrime, & ! intent(inout): trial value of time derivative canopy ice content (kg m-2 s-1) + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + scalarCanopyTempPrime, & ! intent(inout): trial value of time derivative canopy temperature (K s-1) + scalarCanopyWatPrime, & ! intent(inout): trial value of time derivative canopy total water (kg m-2 s-1) + scalarCanopyLiqPrime, & ! intent(inout): trial value of time derivative canopy liquid water (kg m-2 s-1) + scalarCanopyIcePrime, & ! intent(inout): trial value of time derivative canopy ice content (kg m-2 s-1) ! output: variables for th snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! intent(inout): trial vector of time derivative layer temperature (K s-1) - mLayerVolFracWatPrime, & ! intent(inout): trial vector of time derivative volumetric total water content (s-1) - mLayerVolFracLiqPrime, & ! intent(inout): trial vector of time derivative volumetric liquid water content (s-1) - mLayerVolFracIcePrime, & ! intent(inout): trial vector of time derivative volumetric ice water content (s-1) - mLayerMatricHeadPrime, & ! intent(inout): trial vector of time derivative total water matric potential (m s-1) - mLayerMatricHeadLiqPrime, & ! intent(inout): trial vector of time derivative liquid water matric potential (m s-1) + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempPrime, & ! intent(inout): trial vector of time derivative layer temperature (K s-1) + mLayerVolFracWatPrime, & ! intent(inout): trial vector of time derivative volumetric total water content (s-1) + mLayerVolFracLiqPrime, & ! intent(inout): trial vector of time derivative volumetric liquid water content (s-1) + mLayerVolFracIcePrime, & ! intent(inout): trial vector of time derivative volumetric ice water content (s-1) + mLayerMatricHeadPrime, & ! intent(inout): trial vector of time derivative total water matric potential (m s-1) + mLayerMatricHeadLiqPrime, & ! intent(inout): trial vector of time derivative liquid water matric potential (m s-1) ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) if(updateStateCp)then diff --git a/build/source/engine/layerMerge.f90 b/build/source/engine/layerMerge.f90 index 12d5710af..6f8a8c083 100644 --- a/build/source/engine/layerMerge.f90 +++ b/build/source/engine/layerMerge.f90 @@ -297,7 +297,7 @@ subroutine layer_combine(mpar_data,prog_data,diag_data,flux_data,indx_data,iSnow USE data_types,only:var_d ! data structures with fixed dimension ! provide access to external modules USE snow_utils_module,only:fracliquid ! compute fraction of liquid water - USE enthalpyTemp_module,only:enthalpy2T_snowlu,T2enthalpy_snowlu ! convert temperature to enthalpy for a snow layer with lookup tables + USE enthalpyTemp_module,only:enthalpy2T_snwWat,T2enthalpy_snwWat ! convert temperature to liq+ice enthalpy for a snow layer implicit none ! ------------------------------------------------------------------------------------------------------------ ! input/output: data structures @@ -361,19 +361,19 @@ subroutine layer_combine(mpar_data,prog_data,diag_data,flux_data,indx_data,iSnow cBulkDenWat = (mLayerDepth(isnow)*bulkDenWat(1) + mLayerDepth(isnow+1)*bulkDenWat(2))/cDepth ! compute enthalpy for each layer (J m-3) - l1Enthalpy = T2enthalpy_snowlu(mLayerTemp(iSnow), BulkDenWat(1),snowfrz_scale) - l2Enthalpy = T2enthalpy_snowlu(mLayerTemp(iSnow+1),BulkDenWat(2),snowfrz_scale) + l1Enthalpy = T2enthalpy_snwWat(mLayerTemp(iSnow), BulkDenWat(1),snowfrz_scale) + l2Enthalpy = T2enthalpy_snwWat(mLayerTemp(iSnow+1),BulkDenWat(2),snowfrz_scale) ! compute combined enthalpy (J m-3) cEnthalpy = (mLayerDepth(isnow)*l1Enthalpy + mLayerDepth(isnow+1)*l2Enthalpy)/cDepth ! convert enthalpy (J m-3) to temperature (K) - call enthalpy2T_snowlu(cEnthalpy,cBulkDenWat,snowfrz_scale,cTemp,err,cmessage) + call enthalpy2T_snwWat(cEnthalpy,cBulkDenWat,snowfrz_scale,cTemp,err,cmessage) if(err/=0)then; err=10; message=trim(message)//trim(cmessage); return; end if ! test enthalpy conversion - if(abs(T2enthalpy_snowlu(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat - cEnthalpy/cBulkDenWat) > eTol)then - write(*,'(a,1x,f12.5,1x,2(e20.10,1x))') 'enthalpy test', cBulkDenWat, T2enthalpy_snowlu(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat, cEnthalpy/cBulkDenWat + if(abs(T2enthalpy_snwWat(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat - cEnthalpy/cBulkDenWat) > eTol)then + write(*,'(a,1x,f12.5,1x,2(e20.10,1x))') 'enthalpy test', cBulkDenWat, T2enthalpy_snwWat(cTemp,cBulkDenWat,snowfrz_scale)/cBulkDenWat, cEnthalpy/cBulkDenWat message=trim(message)//'problem with enthalpy-->temperature conversion' err=20; return end if diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index c3407bed5..4ffc5065e 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -163,7 +163,7 @@ subroutine systemSolv(& USE allocspace_module,only:allocLocal ! allocate local data structures ! state vector and solver USE getVectorz_module,only:getScaling ! get the scaling vectors - USE enthalpyTemp_module,only:T2enthalpy_snowlu ! convert temperature to enthalpy for a snow layer + USE enthalpyTemp_module,only:T2enthalpy_snwWat ! convert temperature to liq+ice enthalpy for a snow layer USE enthalpyTemp_module,only:T2enthTemp ! compute enthalpy #ifdef SUNDIALS_ACTIVE USE tol4ida_module,only:popTol4ida ! populate tolerances @@ -418,7 +418,7 @@ subroutine initial_function_evaluations if (nSnow>0) then ! compute the energy required to melt the top snow layer (J m-2) bulkDensity = mLayerVolFracIce(1)*iden_ice + mLayerVolFracLiq(1)*iden_water - volEnthalpy = T2enthalpy_snowlu(mLayerTemp(1),bulkDensity,snowfrz_scale) + volEnthalpy = T2enthalpy_snwWat(mLayerTemp(1),bulkDensity,snowfrz_scale) ! set flag and error codes for too much melt if (-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_cur) then tooMuchMelt = .true. diff --git a/build/source/engine/updateVarsWithPrime.f90 b/build/source/engine/updateVarsWithPrime.f90 index 6b3d31a76..fc0805784 100644 --- a/build/source/engine/updateVarsWithPrime.f90 +++ b/build/source/engine/updateVarsWithPrime.f90 @@ -105,6 +105,7 @@ module updateVarsWithPrime_module subroutine updateVarsWithPrime(& ! input updateT, & ! intent(in): flag if need to update temperature from enthalpy + use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy computJac, & ! intent(in): flag if computing for Jacobian update do_adjustTemp, & ! intent(in): flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU @@ -146,6 +147,7 @@ subroutine updateVarsWithPrime(& implicit none ! input logical(lgt) ,intent(in) :: updateT ! flag if need to update temperature from enthalpy + logical(lgt) ,intent(in) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function logical(lgt) ,intent(in) :: computJac ! flag if computing for Jacobian update logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU @@ -317,7 +319,7 @@ subroutine updateVarsWithPrime(& ! get the layer index select case(ixDomainType) - case(iname_cas); cycle ! canopy air space: do nothing + case(iname_cas); iLayer = 0 case(iname_veg); iLayer = 0 case(iname_snow); iLayer = ixControlIndex case(iname_soil); iLayer = ixControlIndex + nSnow @@ -327,9 +329,10 @@ subroutine updateVarsWithPrime(& ! get the index of the other (energy or mass) state variable within the full state vector select case(ixDomainType) + case(iname_cas) ; ixOther = 0 case(iname_veg) ; ixOther = merge(ixHydCanopy(1), ixNrgCanopy(1), ixStateType(ixFullVector)==iname_nrgCanopy) case(iname_snow, iname_soil); ixOther = merge(ixHydLayer(iLayer),ixNrgLayer(iLayer),ixStateType(ixFullVector)==iname_nrgLayer) - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil'; return end select ! get the index in the local state vector @@ -340,7 +343,7 @@ subroutine updateVarsWithPrime(& isCoupled = (ixOtherLocal/=integerMissing) ! check if we are an energy state - isNrgState = (ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer) + isNrgState = (ixStateType(ixFullVector)==iname_nrgCanair .or. ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer) if(printFlag)then print*, 'iState = ', iState, size(ixMapSubset2Full) @@ -399,10 +402,12 @@ subroutine updateVarsWithPrime(& endif ! if hydrology state variable or uncoupled solution - ! compute temperature from enthalpy and water content, enthalpy is the state variable - if(ixStateType(ixFullVector)==iname_nrgCanair)then + ! compute temperature from enthalpy and water content + ! NOTE: always do no matter coupling state if enthalpy is the state variable (updateT==.true.) + if(ixDomainType==iname_cas)then if(updateT)then call enthalpy2T_cas(& + computJac, & ! intent(in): flag if computing for Jacobian update scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) scalarCanairTempTrial, & ! intent(out): trial value for canopy air temperature (K) dCanairTemp_dEnthalpy, & ! intent(out): derivative of canopy air temperature with enthalpy @@ -411,62 +416,69 @@ subroutine updateVarsWithPrime(& else dCanairTemp_dEnthalpy = 1._rkind endif - else if(ixStateType(ixFullVector)==iname_nrgCanopy)then + cycle ! no more to do on canopy air space + elseif(ixDomainType==iname_veg)then if(updateT)then call enthalpy2T_veg(& - canopyDepth, & ! intent(in): canopy depth (m) - specificHeatVeg, & ! intent(in): specific heat of vegetation (J kg-1 K-1) - maxMassVegetation, & ! intent(in): maximum mass of vegetation (kg m-2) - snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) - scalarCanopyEnthalpyTrial, & ! intent(in): trial value for enthalpy of the vegetation canopy (J m-3) - scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) - scalarCanopyTempTrial, & ! intent(out): trial value for canopy temperature (K) - dCanopyTemp_dEnthalpy, & ! intent(out): derivative of canopy temperature with enthalpy - dCanopyTemp_dCanWat, & ! intent(out): derivative of canopy temperature with canopy water - err,cmessage) ! intent(out): error control + computJac, & ! intent(in): flag if computing for Jacobian update + canopyDepth, & ! intent(in): canopy depth (m) + specificHeatVeg, & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation, & ! intent(in): maximum mass of vegetation (kg m-2) + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + scalarCanopyEnthalpyTrial, & ! intent(in): trial value for enthalpy of the vegetation canopy (J m-3) + scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) + scalarCanopyTempTrial, & ! intent(out): trial value for canopy temperature (K) + dCanopyTemp_dEnthalpy, & ! intent(inout): derivative of canopy temperature with enthalpy + dCanopyTemp_dCanWat, & ! intent(inout): derivative of canopy temperature with canopy water + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif else dCanopyTemp_dEnthalpy = 1._rkind dCanopyTemp_dCanWat = 1._rkind endif - else if(ixStateType(ixFullVector)==iname_nrgLayer)then + elseif(ixDomainType==iname_snow)then if(updateT)then - if(ixDomainType==iname_snow)then - call enthalpy2T_snow(& - snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) - mLayerEnthalpyTrial(iLayer), & ! intent(in): enthalpy of snow+soil layer (J m-3) - mLayerVolFracWatTrial(iLayer), & ! intent(in): volumetric total water content (-) - mLayerTempTrial(iLayer), & ! intent(out): layer temperature (K) - dTemp_dEnthalpy(iLayer), & ! intent(out): derivative of layer temperature with enthalpy - dTemp_dTheta(iLayer), & ! intent(out): derivative of layer temperature with volumetric total water content - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - elseif(ixDomainType==iname_soil)then - call enthalpy2T_soil(& - ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy - soil_dens_intr(ixControlIndex), & ! intent(in): intrinsic soil density (kg m-3) - vGn_alpha(ixControlIndex), & ! intent(in): van Genutchen "alpha" parameter - vGn_n(ixControlIndex), & ! intent(in): van Genutchen "n" parameter - theta_sat(ixControlIndex), & ! intent(in): soil porosity (-) - theta_res(ixControlIndex), & ! intent(in): soil residual volumetric water content (-) - vGn_m(ixControlIndex), & ! intent(in): van Genutchen "m" parameter (-) - ixControlIndex, & ! intent(in): index of the control volume within the domain - lookup_data, & ! intent(in): lookup table data structure - mLayerEnthalpyTrial(iLayer), & ! intent(in): trial vector of enthalpy of each snow+soil layer (J m-3) - mLayerMatricHeadTrial(ixControlIndex), & ! intent(in): trial vector of total water matric potential (m) - mLayerTempTrial(iLayer), & ! intent(out): trial vector of layer temperature (K) - dTemp_dEnthalpy(iLayer), & ! intent(out): derivative of layer temperature with enthalpy - dTemp_dTheta(iLayer), & ! intent(out): derivative of layer temperature with volumetric total water content - dTemp_dPsi0(ixControlIndex), & ! intent(out): derivative of layer temperature with total water matric potential - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - endif + call enthalpy2T_snow(& + computJac, & ! intent(in): flag if computing for Jacobian update + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + mLayerEnthalpyTrial(iLayer), & ! intent(in): enthalpy of snow+soil layer (J m-3) + mLayerVolFracWatTrial(iLayer), & ! intent(in): volumetric total water content (-) + mLayerTempTrial(iLayer), & ! intent(out): layer temperature (K) + dTemp_dEnthalpy(iLayer), & ! intent(inout): derivative of layer temperature with enthalpy + dTemp_dTheta(iLayer), & ! intent(inout): derivative of layer temperature with volumetric total water content + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif else dTemp_dEnthalpy(iLayer) = 1._rkind dTemp_dTheta(iLayer) = 1._rkind - if(ixDomainType==iname_soil) dTemp_dPsi0(ixControlIndex) = 1._rkind endif - + elseif(ixDomainType==iname_soil)then + if(updateT)then + call enthalpy2T_soil(& + computJac, & ! intent(in): flag if computing for Jacobian update + use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy + soil_dens_intr(ixControlIndex), & ! intent(in): intrinsic soil density (kg m-3) + vGn_alpha(ixControlIndex), & ! intent(in): van Genutchen "alpha" parameter + vGn_n(ixControlIndex), & ! intent(in): van Genutchen "n" parameter + theta_sat(ixControlIndex), & ! intent(in): soil porosity (-) + theta_res(ixControlIndex), & ! intent(in): soil residual volumetric water content (-) + vGn_m(ixControlIndex), & ! intent(in): van Genutchen "m" parameter (-) + ixControlIndex, & ! intent(in): index of the control volume within the domain + lookup_data, & ! intent(in): lookup table data structure + mLayerEnthalpyTrial(iLayer), & ! intent(in): trial vector of enthalpy of each snow+soil layer (J m-3) + mLayerMatricHeadTrial(ixControlIndex), & ! intent(in): trial vector of total water matric potential (m) + mLayerTempTrial(iLayer), & ! intent(out): trial vector of layer temperature (K) + dTemp_dEnthalpy(iLayer), & ! intent(inout): derivative of layer temperature with enthalpy + dTemp_dTheta(iLayer), & ! intent(inout): derivative of layer temperature with volumetric total water content + dTemp_dPsi0(ixControlIndex), & ! intent(inout): derivative of layer temperature with total water matric potential + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + else + dTemp_dEnthalpy(iLayer) = 1._rkind + dTemp_dTheta(iLayer) = 1._rkind + dTemp_dPsi0(ixControlIndex) = 1._rkind + endif + endif ! compute the critical soil temperature below which ice exists select case(ixDomainType) @@ -612,17 +624,17 @@ subroutine updateVarsWithPrime(& ! compute volumetric fraction of liquid water and ice call updateSnowPrime(& - xTemp, & ! intent(in) : temperature (K) - mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) - snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) - mLayerTempPrime(iLayer), & ! intent(in) : temperature time derivative (K/s) - mLayerVolFracWatPrime(iLayer), & ! intent(in) : volumetric fraction of total water time derivative (-) - mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) - mLayerVolFracLiqPrime(iLayer), & ! intent(out) : volumetric fraction of liquid water time derivative (-) - mLayerVolFracIcePrime(iLayer), & ! intent(out) : volumetric fraction of ice time derivative (-) - mLayerFracLiqSnow(iLayer), & ! intent(out) : fraction of liquid water (-) - err,cmessage) ! intent(out) : error control + xTemp, & ! intent(in) : temperature (K) + mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) + snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) + mLayerTempPrime(iLayer), & ! intent(in) : temperature time derivative (K/s) + mLayerVolFracWatPrime(iLayer), & ! intent(in) : volumetric fraction of total water time derivative (-) + mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) + mLayerVolFracLiqPrime(iLayer), & ! intent(out) : volumetric fraction of liquid water time derivative (-) + mLayerVolFracIcePrime(iLayer), & ! intent(out) : volumetric fraction of ice time derivative (-) + mLayerFracLiqSnow(iLayer), & ! intent(out) : fraction of liquid water (-) + err,cmessage) ! intent(out) : error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! *** soil layers @@ -630,22 +642,22 @@ subroutine updateVarsWithPrime(& ! compute volumetric fraction of liquid water and ice call updateSoilPrime(& - xTemp, & ! intent(in) : temperature (K) - mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) - mLayerTempPrime(iLayer), & ! intent(in) : temperature time derivative (K/s) - mLayerMatricHeadPrime(ixControlIndex), & ! intent(in) : total water matric potential time derivative (m/s) - vGn_alpha(ixControlIndex), & ! intent(in) : van Genutchen "alpha" parameter - vGn_n(ixControlIndex), & ! intent(in) : van Genutchen "n" parameter - theta_sat(ixControlIndex), & ! intent(in) : soil porosity (-) - theta_res(ixControlIndex), & ! intent(in) : soil residual volumetric water content (-) - vGn_m(ixControlIndex), & ! intent(in) : van Genutchen "m" parameter (-) - mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) - mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) - mLayerVolFracWatPrime(iLayer), & ! intent(out) : volumetric fraction of total water time derivative (-) - mLayerVolFracLiqPrime(iLayer), & ! intent(out) : volumetric fraction of liquid water time derivative (-) - mLayerVolFracIcePrime(iLayer), & ! intent(out) : volumetric fraction of ice time derivative (-) - err,cmessage) ! intent(out) : error control + xTemp, & ! intent(in) : temperature (K) + mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) + mLayerTempPrime(iLayer), & ! intent(in) : temperature time derivative (K/s) + mLayerMatricHeadPrime(ixControlIndex), & ! intent(in) : total water matric potential time derivative (m/s) + vGn_alpha(ixControlIndex), & ! intent(in) : van Genutchen "alpha" parameter + vGn_n(ixControlIndex), & ! intent(in) : van Genutchen "n" parameter + theta_sat(ixControlIndex), & ! intent(in) : soil porosity (-) + theta_res(ixControlIndex), & ! intent(in) : soil residual volumetric water content (-) + vGn_m(ixControlIndex), & ! intent(in) : van Genutchen "m" parameter (-) + mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) + mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) + mLayerVolFracWatPrime(iLayer), & ! intent(out) : volumetric fraction of total water time derivative (-) + mLayerVolFracLiqPrime(iLayer), & ! intent(out) : volumetric fraction of liquid water time derivative (-) + mLayerVolFracIcePrime(iLayer), & ! intent(out) : volumetric fraction of ice time derivative (-) + err,cmessage) ! intent(out) : error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! check @@ -700,17 +712,17 @@ subroutine updateVarsWithPrime(& case(iname_snow, iname_soil) call xTempSolve(& ! constant over iterations - meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) - heatCap = mLayerVolHtCapBulk(iLayer) ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) - tempInit = mLayerTemp(iLayer) ,& ! intent(in) : initial temperature (K) - volFracIceInit = mLayerVolFracIce(iLayer) ,& ! intent(in) : initial volumetric fraction of ice (-) + meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) + heatCap = mLayerVolHtCapBulk(iLayer) ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) + tempInit = mLayerTemp(iLayer) ,& ! intent(in) : initial temperature (K) + volFracIceInit = mLayerVolFracIce(iLayer) ,& ! intent(in) : initial volumetric fraction of ice (-) ! trial values - xTemp = xTemp ,& ! intent(inout) : trial value of temperature - dLiq_dT = mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) - volFracIceTrial = mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : trial value for volumetric fraction of ice + xTemp = xTemp ,& ! intent(inout) : trial value of temperature + dLiq_dT = mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) + volFracIceTrial = mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : trial value for volumetric fraction of ice ! residual and derivative - residual = residual ,& ! intent(out) : residual (J m-3) - derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) + residual = residual ,& ! intent(out) : residual (J m-3) + derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) ! * check case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return @@ -805,21 +817,21 @@ subroutine updateVarsWithPrime(& ! compute the liquid matric potential (and the derivatives w.r.t. total matric potential and temperature) call liquidHeadPrime(& ! input - mLayerMatricHeadTrial(ixControlIndex) ,& ! intent(in) : total water matric potential (m) - mLayerMatricHeadPrime(ixControlIndex) ,& ! intent(in) : total water matric potential time derivative (m s-1) - mLayerVolFracLiqTrial(iLayer) ,& ! intent(in) : volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : volumetric fraction of ice (-) + mLayerMatricHeadTrial(ixControlIndex) ,& ! intent(in) : total water matric potential (m) + mLayerMatricHeadPrime(ixControlIndex) ,& ! intent(in) : total water matric potential time derivative (m s-1) + mLayerVolFracLiqTrial(iLayer) ,& ! intent(in) : volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : volumetric fraction of ice (-) vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex),& ! intent(in) : soil parameters - dVolTot_dPsi0(ixControlIndex) ,& ! intent(in) : derivative in the soil water characteristic (m-1) - mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) - mLayerVolFracLiqPrime(iLayer) ,& ! intent(in) : volumetric fraction of liquid water time derivative (-) - mLayerVolFracIcePrime(iLayer) ,& ! intent(in) : volumetric fraction of ice time derivative (-) + dVolTot_dPsi0(ixControlIndex) ,& ! intent(in) : derivative in the soil water characteristic (m-1) + mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) + mLayerVolFracLiqPrime(iLayer) ,& ! intent(in) : volumetric fraction of liquid water time derivative (-) + mLayerVolFracIcePrime(iLayer) ,& ! intent(in) : volumetric fraction of ice time derivative (-) ! output - mLayerMatricHeadLiqTrial(ixControlIndex) ,& ! intent(out): liquid water matric potential (m) - mLayerMatricHeadLiqPrime(ixControlIndex) ,& ! intent(out): liquid water matric potential time derivative (m s-1) - dPsiLiq_dPsi0(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. the total water matric potential (-) - dPsiLiq_dTemp(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. temperature (m K-1) - err,cmessage) ! intent(out): error control + mLayerMatricHeadLiqTrial(ixControlIndex) ,& ! intent(out): liquid water matric potential (m) + mLayerMatricHeadLiqPrime(ixControlIndex) ,& ! intent(out): liquid water matric potential time derivative (m s-1) + dPsiLiq_dPsi0(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. the total water matric potential (-) + dPsiLiq_dTemp(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. temperature (m K-1) + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif endif ! switch between hydrology and energy state From 4ab975fa9ce2aedb3a1810deef582b15601f5aeb Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 20 Mar 2024 15:09:50 +0900 Subject: [PATCH 1223/1472] spaces --- build/source/engine/updateVars.f90 | 130 ++++++++-------- build/source/engine/updateVarsWithPrime.f90 | 159 ++++++++++---------- 2 files changed, 143 insertions(+), 146 deletions(-) diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 index e58ff7805..6a28c0100 100644 --- a/build/source/engine/updateVars.f90 +++ b/build/source/engine/updateVars.f90 @@ -444,13 +444,13 @@ subroutine updateVars(& case(iname_veg) ! compute volumetric fraction of liquid water and ice - call updateSnow(xTemp, & ! intent(in) : temperature (K) - scalarCanopyWatTrial/(iden_water*canopyDepth),& ! intent(in) : volumetric fraction of total water (-) - snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) - scalarVolFracLiq, & ! intent(out) : trial volumetric fraction of liquid water (-) - scalarVolFracIce, & ! intent(out) : trial volumetric fraction if ice (-) - scalarFracLiqVeg, & ! intent(out) : fraction of liquid water (-) - err,cmessage) ! intent(out) : error control + call updateSnow(xTemp, & ! intent(in): temperature (K) + scalarCanopyWatTrial/(iden_water*canopyDepth),& ! intent(in): volumetric fraction of total water (-) + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + scalarVolFracLiq, & ! intent(out): trial volumetric fraction of liquid water (-) + scalarVolFracIce, & ! intent(out): trial volumetric fraction if ice (-) + scalarFracLiqVeg, & ! intent(out): fraction of liquid water (-) + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! compute mass of water on the canopy @@ -462,26 +462,26 @@ subroutine updateVars(& case(iname_snow) ! compute volumetric fraction of liquid water and ice - call updateSnow(xTemp, & ! intent(in) : temperature (K) - mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) - snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) - mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) - mLayerFracLiqSnow(iLayer), & ! intent(out) : fraction of liquid water (-) - err,cmessage) ! intent(out) : error control + call updateSnow(xTemp, & ! intent(in): temperature (K) + mLayerVolFracWatTrial(iLayer), & ! intent(in): mass state variable = trial volumetric fraction of water (-) + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + mLayerVolFracLiqTrial(iLayer), & ! intent(out): trial volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer), & ! intent(out): trial volumetric fraction if ice (-) + mLayerFracLiqSnow(iLayer), & ! intent(out): fraction of liquid water (-) + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! *** soil layers case(iname_soil) ! compute volumetric fraction of liquid water and ice - call updateSoil(xTemp, & ! intent(in) : temperature (K) - mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) - vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in) : soil parameters - mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) - mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) - err,cmessage) ! intent(out) : error control + call updateSoil(xTemp, & ! intent(in): temperature (K) + mLayerMatricHeadTrial(ixControlIndex), & ! intent(in): total water matric potential (m) + vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in): soil parameters + mLayerVolFracWatTrial(iLayer), & ! intent(in): mass state variable = trial volumetric fraction of water (-) + mLayerVolFracLiqTrial(iLayer), & ! intent(out): trial volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer), & ! intent(out): trial volumetric fraction if ice (-) + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! check @@ -526,33 +526,33 @@ subroutine updateVars(& case(iname_veg) call xTempSolve(& ! constant over iterations - meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) - heatCap = scalarBulkVolHeatCapVeg ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) - tempInit = scalarCanopyTemp ,& ! intent(in) : initial temperature (K) - volFracIceInit = scalarCanopyIce/(iden_water*canopyDepth),& ! intent(in) : initial volumetric fraction of ice (-) + meltNrg = meltNrg ,& ! intent(in): energy for melt+freeze (J m-3) + heatCap = scalarBulkVolHeatCapVeg ,& ! intent(in): volumetric heat capacity (J m-3 K-1) + tempInit = scalarCanopyTemp ,& ! intent(in): initial temperature (K) + volFracIceInit = scalarCanopyIce/(iden_water*canopyDepth),& ! intent(in): initial volumetric fraction of ice (-) ! trial values - xTemp = xTemp ,& ! intent(inout) : trial value of temperature - dLiq_dT = dTheta_dTkCanopy ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) - volFracIceTrial = scalarVolFracIce ,& ! intent(in) : trial value for volumetric fraction of ice + xTemp = xTemp ,& ! intent(inout): trial value of temperature + dLiq_dT = dTheta_dTkCanopy ,& ! intent(in): derivative in liquid water content w.r.t. temperature (K-1) + volFracIceTrial = scalarVolFracIce ,& ! intent(in): trial value for volumetric fraction of ice ! residual and derivative - residual = residual ,& ! intent(out) : residual (J m-3) - derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) + residual = residual ,& ! intent(out): residual (J m-3) + derivative = derivative ) ! intent(out): derivative (J m-3 K-1) ! * snow and soil case(iname_snow, iname_soil) call xTempSolve(& ! constant over iterations - meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) - heatCap = mLayerVolHtCapBulk(iLayer) ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) - tempInit = mLayerTemp(iLayer) ,& ! intent(in) : initial temperature (K) - volFracIceInit = mLayerVolFracIce(iLayer) ,& ! intent(in) : initial volumetric fraction of ice (-) + meltNrg = meltNrg ,& ! intent(in): energy for melt+freeze (J m-3) + heatCap = mLayerVolHtCapBulk(iLayer) ,& ! intent(in): volumetric heat capacity (J m-3 K-1) + tempInit = mLayerTemp(iLayer) ,& ! intent(in): initial temperature (K) + volFracIceInit = mLayerVolFracIce(iLayer) ,& ! intent(in): initial volumetric fraction of ice (-) ! trial values - xTemp = xTemp ,& ! intent(inout) : trial value of temperature - dLiq_dT = mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) - volFracIceTrial = mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : trial value for volumetric fraction of ice + xTemp = xTemp ,& ! intent(inout): trial value of temperature + dLiq_dT = mLayerdTheta_dTk(iLayer) ,& ! intent(in): derivative in liquid water content w.r.t. temperature (K-1) + volFracIceTrial = mLayerVolFracIceTrial(iLayer) ,& ! intent(in): trial value for volumetric fraction of ice ! residual and derivative - residual = residual ,& ! intent(out) : residual (J m-3) - derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) + residual = residual ,& ! intent(out): residual (J m-3) + derivative = derivative ) ! intent(out): derivative (J m-3 K-1) ! * check case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return @@ -653,17 +653,17 @@ subroutine updateVars(& ! compute the liquid matric potential (and the derivatives w.r.t. total matric potential and temperature) call liquidHead(& ! input - mLayerMatricHeadTrial(ixControlIndex) ,& ! intent(in) : total water matric potential (m) - mLayerVolFracLiqTrial(iLayer) ,& ! intent(in) : volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : volumetric fraction of ice (-) - vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in) : soil parameters - dVolTot_dPsi0(ixControlIndex) ,& ! intent(in) : derivative in the soil water characteristic (m-1) - mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) + mLayerMatricHeadTrial(ixControlIndex) ,& ! intent(in): total water matric potential (m) + mLayerVolFracLiqTrial(iLayer) ,& ! intent(in): volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer) ,& ! intent(in): volumetric fraction of ice (-) + vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in): soil parameters + dVolTot_dPsi0(ixControlIndex) ,& ! intent(in): derivative in the soil water characteristic (m-1) + mLayerdTheta_dTk(iLayer) ,& ! intent(in): derivative in volumetric total water w.r.t. temperature (K-1) ! output - mLayerMatricHeadLiqTrial(ixControlIndex) ,& ! intent(out): liquid water matric potential (m) - dPsiLiq_dPsi0(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. the total water matric potential (-) - dPsiLiq_dTemp(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. temperature (m K-1) - err,cmessage) ! intent(out): error control + mLayerMatricHeadLiqTrial(ixControlIndex) ,& ! intent(out): liquid water matric potential (m) + dPsiLiq_dPsi0(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. the total water matric potential (-) + dPsiLiq_dTemp(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. temperature (m K-1) + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif endif ! switch between hydrology and energy state @@ -685,27 +685,27 @@ end subroutine updateVars ! ********************************************************************************************************** subroutine xTempSolve(& ! input: constant over iterations - meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) - heatCap ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) - tempInit ,& ! intent(in) : initial temperature (K) - volFracIceInit ,& ! intent(in) : initial volumetric fraction of ice (-) + meltNrg ,& ! intent(in): energy for melt+freeze (J m-3) + heatCap ,& ! intent(in): volumetric heat capacity (J m-3 K-1) + tempInit ,& ! intent(in): initial temperature (K) + volFracIceInit ,& ! intent(in): initial volumetric fraction of ice (-) ! input-output: trial values - xTemp ,& ! intent(inout) : trial value of temperature - dLiq_dT ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) - volFracIceTrial ,& ! intent(in) : trial value for volumetric fraction of ice + xTemp ,& ! intent(inout): trial value of temperature + dLiq_dT ,& ! intent(in): derivative in liquid water content w.r.t. temperature (K-1) + volFracIceTrial ,& ! intent(in): trial value for volumetric fraction of ice ! output: residual and derivative - residual ,& ! intent(out) : residual (J m-3) - derivative ) ! intent(out) : derivative (J m-3 K-1) + residual ,& ! intent(out): residual (J m-3) + derivative ) ! intent(out): derivative (J m-3 K-1) implicit none ! input: constant over iterations - real(rkind),intent(in) :: meltNrg ! energy for melt+freeze (J m-3) - real(rkind),intent(in) :: heatCap ! volumetric heat capacity (J m-3 K-1) - real(rkind),intent(in) :: tempInit ! initial temperature (K) - real(rkind),intent(in) :: volFracIceInit ! initial volumetric fraction of ice (-) + real(rkind),intent(in) :: meltNrg ! energy for melt+freeze (J m-3) + real(rkind),intent(in) :: heatCap ! volumetric heat capacity (J m-3 K-1) + real(rkind),intent(in) :: tempInit ! initial temperature (K) + real(rkind),intent(in) :: volFracIceInit ! initial volumetric fraction of ice (-) ! input-output: trial values - real(rkind),intent(inout) :: xTemp ! trial value for temperature - real(rkind),intent(in) :: dLiq_dT ! derivative in liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: volFracIceTrial ! trial value for the volumetric fraction of ice (-) + real(rkind),intent(inout) :: xTemp ! trial value for temperature + real(rkind),intent(in) :: dLiq_dT ! derivative in liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: volFracIceTrial ! trial value for the volumetric fraction of ice (-) ! output: residual and derivative real(rkind),intent(out) :: residual ! residual (J m-3) real(rkind),intent(out) :: derivative ! derivative (J m-3 K-1) diff --git a/build/source/engine/updateVarsWithPrime.f90 b/build/source/engine/updateVarsWithPrime.f90 index fc0805784..911919bd8 100644 --- a/build/source/engine/updateVarsWithPrime.f90 +++ b/build/source/engine/updateVarsWithPrime.f90 @@ -599,17 +599,17 @@ subroutine updateVarsWithPrime(& ! compute volumetric fraction of liquid water and ice call updateSnowPrime(& - xTemp, & ! intent(in) : temperature (K) - scalarCanopyWatTrial/(iden_water*canopyDepth),& ! intent(in) : volumetric fraction of total water (-) - snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) - scalarCanopyTempPrime, & ! intent(in) : canopy temperature time derivative (K/s) - scalarCanopyWatPrime/(iden_water*canopyDepth),& ! intent(in) : volumetric fraction of total water time derivative (-) - scalarVolFracLiq, & ! intent(out) : trial canopy liquid water (-) - scalarVolFracIce, & ! intent(out) : trial volumetric canopy ice (-) - scalarVolFracLiqPrime, & ! intent(out) : trial volumetric canopy liquid water (-) - scalarVolFracIcePrime, & ! intent(out) : trial volumetric canopy ice (-) - scalarFracLiqVeg, & ! intent(out) : fraction of liquid water (-) - err,cmessage) ! intent(out) : error control + xTemp, & ! intent(in): temperature (K) + scalarCanopyWatTrial/(iden_water*canopyDepth),& ! intent(in): volumetric fraction of total water (-) + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + scalarCanopyTempPrime, & ! intent(in): canopy temperature time derivative (K/s) + scalarCanopyWatPrime/(iden_water*canopyDepth),& ! intent(in): volumetric fraction of total water time derivative (-) + scalarVolFracLiq, & ! intent(out): trial canopy liquid water (-) + scalarVolFracIce, & ! intent(out): trial volumetric canopy ice (-) + scalarVolFracLiqPrime, & ! intent(out): trial volumetric canopy liquid water (-) + scalarVolFracIcePrime, & ! intent(out): trial volumetric canopy ice (-) + scalarFracLiqVeg, & ! intent(out): fraction of liquid water (-) + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! compute mass of water on the canopy @@ -624,17 +624,17 @@ subroutine updateVarsWithPrime(& ! compute volumetric fraction of liquid water and ice call updateSnowPrime(& - xTemp, & ! intent(in) : temperature (K) - mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) - snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) - mLayerTempPrime(iLayer), & ! intent(in) : temperature time derivative (K/s) - mLayerVolFracWatPrime(iLayer), & ! intent(in) : volumetric fraction of total water time derivative (-) - mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) - mLayerVolFracLiqPrime(iLayer), & ! intent(out) : volumetric fraction of liquid water time derivative (-) - mLayerVolFracIcePrime(iLayer), & ! intent(out) : volumetric fraction of ice time derivative (-) - mLayerFracLiqSnow(iLayer), & ! intent(out) : fraction of liquid water (-) - err,cmessage) ! intent(out) : error control + xTemp, & ! intent(in): temperature (K) + mLayerVolFracWatTrial(iLayer), & ! intent(in): mass state variable = trial volumetric fraction of water (-) + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + mLayerTempPrime(iLayer), & ! intent(in): temperature time derivative (K/s) + mLayerVolFracWatPrime(iLayer), & ! intent(in): volumetric fraction of total water time derivative (-) + mLayerVolFracLiqTrial(iLayer), & ! intent(out): trial volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer), & ! intent(out): trial volumetric fraction if ice (-) + mLayerVolFracLiqPrime(iLayer), & ! intent(out): volumetric fraction of liquid water time derivative (-) + mLayerVolFracIcePrime(iLayer), & ! intent(out): volumetric fraction of ice time derivative (-) + mLayerFracLiqSnow(iLayer), & ! intent(out): fraction of liquid water (-) + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! *** soil layers @@ -642,22 +642,18 @@ subroutine updateVarsWithPrime(& ! compute volumetric fraction of liquid water and ice call updateSoilPrime(& - xTemp, & ! intent(in) : temperature (K) - mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) - mLayerTempPrime(iLayer), & ! intent(in) : temperature time derivative (K/s) - mLayerMatricHeadPrime(ixControlIndex), & ! intent(in) : total water matric potential time derivative (m/s) - vGn_alpha(ixControlIndex), & ! intent(in) : van Genutchen "alpha" parameter - vGn_n(ixControlIndex), & ! intent(in) : van Genutchen "n" parameter - theta_sat(ixControlIndex), & ! intent(in) : soil porosity (-) - theta_res(ixControlIndex), & ! intent(in) : soil residual volumetric water content (-) - vGn_m(ixControlIndex), & ! intent(in) : van Genutchen "m" parameter (-) - mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) - mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) - mLayerVolFracWatPrime(iLayer), & ! intent(out) : volumetric fraction of total water time derivative (-) - mLayerVolFracLiqPrime(iLayer), & ! intent(out) : volumetric fraction of liquid water time derivative (-) - mLayerVolFracIcePrime(iLayer), & ! intent(out) : volumetric fraction of ice time derivative (-) - err,cmessage) ! intent(out) : error control + xTemp, & ! intent(in): temperature (K) + mLayerMatricHeadTrial(ixControlIndex), & ! intent(in): total water matric potential (m) + mLayerTempPrime(iLayer), & ! intent(in): temperature time derivative (K/s) + mLayerMatricHeadPrime(ixControlIndex), & ! intent(in): total water matric potential time derivative (m/s) + vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in): soil parameters + mLayerVolFracWatTrial(iLayer), & ! intent(in): mass state variable = trial volumetric fraction of water (-) + mLayerVolFracLiqTrial(iLayer), & ! intent(out): trial volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer), & ! intent(out): trial volumetric fraction if ice (-) + mLayerVolFracWatPrime(iLayer), & ! intent(out): volumetric fraction of total water time derivative (-) + mLayerVolFracLiqPrime(iLayer), & ! intent(out): volumetric fraction of liquid water time derivative (-) + mLayerVolFracIcePrime(iLayer), & ! intent(out): volumetric fraction of ice time derivative (-) + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! check @@ -684,6 +680,7 @@ subroutine updateVarsWithPrime(& ! check the need to adjust temperature (will always be false if inside solver) ! can be true if inside varSubstep, outside solver, if in a splitting case + ! NOTE: should be adjusting enthalpy if that is the state variabls if(do_adjustTemp)then ! get the melt energy @@ -696,33 +693,33 @@ subroutine updateVarsWithPrime(& case(iname_veg) call xTempSolve(& ! constant over iterations - meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) - heatCap = scalarBulkVolHeatCapVeg ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) - tempInit = scalarCanopyTemp ,& ! intent(in) : initial temperature (K) - volFracIceInit = scalarCanopyIce/(iden_water*canopyDepth),& ! intent(in) : initial volumetric fraction of ice (-) + meltNrg = meltNrg ,& ! intent(in): energy for melt+freeze (J m-3) + heatCap = scalarBulkVolHeatCapVeg ,& ! intent(in): volumetric heat capacity (J m-3 K-1) + tempInit = scalarCanopyTemp ,& ! intent(in): initial temperature (K) + volFracIceInit = scalarCanopyIce/(iden_water*canopyDepth),& ! intent(in): initial volumetric fraction of ice (-) ! trial values - xTemp = xTemp ,& ! intent(inout) : trial value of temperature - dLiq_dT = dTheta_dTkCanopy ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) - volFracIceTrial = scalarVolFracIce ,& ! intent(in) : trial value for volumetric fraction of ice + xTemp = xTemp ,& ! intent(inout): trial value of temperature + dLiq_dT = dTheta_dTkCanopy ,& ! intent(in): derivative in liquid water content w.r.t. temperature (K-1) + volFracIceTrial = scalarVolFracIce ,& ! intent(in): trial value for volumetric fraction of ice ! residual and derivative - residual = residual ,& ! intent(out) : residual (J m-3) - derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) + residual = residual ,& ! intent(out): residual (J m-3) + derivative = derivative ) ! intent(out): derivative (J m-3 K-1) ! * snow and soil case(iname_snow, iname_soil) call xTempSolve(& ! constant over iterations - meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) - heatCap = mLayerVolHtCapBulk(iLayer) ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) - tempInit = mLayerTemp(iLayer) ,& ! intent(in) : initial temperature (K) - volFracIceInit = mLayerVolFracIce(iLayer) ,& ! intent(in) : initial volumetric fraction of ice (-) + meltNrg = meltNrg ,& ! intent(in): energy for melt+freeze (J m-3) + heatCap = mLayerVolHtCapBulk(iLayer) ,& ! intent(in): volumetric heat capacity (J m-3 K-1) + tempInit = mLayerTemp(iLayer) ,& ! intent(in): initial temperature (K) + volFracIceInit = mLayerVolFracIce(iLayer) ,& ! intent(in): initial volumetric fraction of ice (-) ! trial values - xTemp = xTemp ,& ! intent(inout) : trial value of temperature - dLiq_dT = mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) - volFracIceTrial = mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : trial value for volumetric fraction of ice + xTemp = xTemp ,& ! intent(inout): trial value of temperature + dLiq_dT = mLayerdTheta_dTk(iLayer) ,& ! intent(in): derivative in liquid water content w.r.t. temperature (K-1) + volFracIceTrial = mLayerVolFracIceTrial(iLayer) ,& ! intent(in): trial value for volumetric fraction of ice ! residual and derivative - residual = residual ,& ! intent(out) : residual (J m-3) - derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) + residual = residual ,& ! intent(out): residual (J m-3) + derivative = derivative ) ! intent(out): derivative (J m-3 K-1) ! * check case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return @@ -817,15 +814,15 @@ subroutine updateVarsWithPrime(& ! compute the liquid matric potential (and the derivatives w.r.t. total matric potential and temperature) call liquidHeadPrime(& ! input - mLayerMatricHeadTrial(ixControlIndex) ,& ! intent(in) : total water matric potential (m) - mLayerMatricHeadPrime(ixControlIndex) ,& ! intent(in) : total water matric potential time derivative (m s-1) - mLayerVolFracLiqTrial(iLayer) ,& ! intent(in) : volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : volumetric fraction of ice (-) - vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex),& ! intent(in) : soil parameters - dVolTot_dPsi0(ixControlIndex) ,& ! intent(in) : derivative in the soil water characteristic (m-1) - mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) - mLayerVolFracLiqPrime(iLayer) ,& ! intent(in) : volumetric fraction of liquid water time derivative (-) - mLayerVolFracIcePrime(iLayer) ,& ! intent(in) : volumetric fraction of ice time derivative (-) + mLayerMatricHeadTrial(ixControlIndex) ,& ! intent(in): total water matric potential (m) + mLayerMatricHeadPrime(ixControlIndex) ,& ! intent(in): total water matric potential time derivative (m s-1) + mLayerVolFracLiqTrial(iLayer) ,& ! intent(in): volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer) ,& ! intent(in): volumetric fraction of ice (-) + vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex) ,& ! intent(in): soil parameters + dVolTot_dPsi0(ixControlIndex) ,& ! intent(in): derivative in the soil water characteristic (m-1) + mLayerdTheta_dTk(iLayer) ,& ! intent(in): derivative in volumetric total water w.r.t. temperature (K-1) + mLayerVolFracLiqPrime(iLayer) ,& ! intent(in): volumetric fraction of liquid water time derivative (-) + mLayerVolFracIcePrime(iLayer) ,& ! intent(in): volumetric fraction of ice time derivative (-) ! output mLayerMatricHeadLiqTrial(ixControlIndex) ,& ! intent(out): liquid water matric potential (m) mLayerMatricHeadLiqPrime(ixControlIndex) ,& ! intent(out): liquid water matric potential time derivative (m s-1) @@ -855,27 +852,27 @@ end subroutine updateVarsWithPrime ! ********************************************************************************************************** subroutine xTempSolve(& ! input: constant over iterations - meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) - heatCap ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) - tempInit ,& ! intent(in) : initial temperature (K) - volFracIceInit ,& ! intent(in) : initial volumetric fraction of ice (-) + meltNrg ,& ! intent(in): energy for melt+freeze (J m-3) + heatCap ,& ! intent(in): volumetric heat capacity (J m-3 K-1) + tempInit ,& ! intent(in): initial temperature (K) + volFracIceInit ,& ! intent(in): initial volumetric fraction of ice (-) ! input-output: trial values - xTemp ,& ! intent(inout) : trial value of temperature - dLiq_dT ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) - volFracIceTrial ,& ! intent(in) : trial value for volumetric fraction of ice + xTemp ,& ! intent(inout): trial value of temperature + dLiq_dT ,& ! intent(in): derivative in liquid water content w.r.t. temperature (K-1) + volFracIceTrial ,& ! intent(in): trial value for volumetric fraction of ice ! output: residual and derivative - residual ,& ! intent(out) : residual (J m-3) - derivative ) ! intent(out) : derivative (J m-3 K-1) + residual ,& ! intent(out): residual (J m-3) + derivative ) ! intent(out): derivative (J m-3 K-1) implicit none ! input: constant over iterations - real(rkind),intent(in) :: meltNrg ! energy for melt+freeze (J m-3) - real(rkind),intent(in) :: heatCap ! volumetric heat capacity (J m-3 K-1) - real(rkind),intent(in) :: tempInit ! initial temperature (K) - real(rkind),intent(in) :: volFracIceInit ! initial volumetric fraction of ice (-) + real(rkind),intent(in) :: meltNrg ! energy for melt+freeze (J m-3) + real(rkind),intent(in) :: heatCap ! volumetric heat capacity (J m-3 K-1) + real(rkind),intent(in) :: tempInit ! initial temperature (K) + real(rkind),intent(in) :: volFracIceInit ! initial volumetric fraction of ice (-) ! input-output: trial values - real(rkind),intent(inout) :: xTemp ! trial value for temperature - real(rkind),intent(in) :: dLiq_dT ! derivative in liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: volFracIceTrial ! trial value for the volumetric fraction of ice (-) + real(rkind),intent(inout) :: xTemp ! trial value for temperature + real(rkind),intent(in) :: dLiq_dT ! derivative in liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: volFracIceTrial ! trial value for the volumetric fraction of ice (-) ! output: residual and derivative real(rkind),intent(out) :: residual ! residual (J m-3) real(rkind),intent(out) :: derivative ! derivative (J m-3 K-1) From 166727d8d18cdfc7e79c51e67a269a971703e845 Mon Sep 17 00:00:00 2001 From: ashleymedin <34691332+ashleymedin@users.noreply.github.com> Date: Mon, 18 Mar 2024 10:03:48 +0900 Subject: [PATCH 1224/1472] Merge pull request #38 from seantrim/develop_SUNDIALS_instructions Expanded the instructions for the installation of SUNDIALS --- build/cmake/build.pc.bash | 2 +- docs/README_not_ngen.md | 60 +++++++++++++++++++++++++-------------- 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/build/cmake/build.pc.bash b/build/cmake/build.pc.bash index ff2c882ae..ef9262986 100755 --- a/build/cmake/build.pc.bash +++ b/build/cmake/build.pc.bash @@ -22,5 +22,5 @@ #export FLAGS_OPT="-m64;-I"${MKLROOT}/include";-flto=1;-fuse-linker-plugin" # optional compiler flags -- Intel oneMKL builds # CMake Commands (build type controlled using DCMAKE_BUILD_TYPE) -cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=BE +cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials cmake --build ../cmake_build --target all diff --git a/docs/README_not_ngen.md b/docs/README_not_ngen.md index bd0d59057..5401239ec 100644 --- a/docs/README_not_ngen.md +++ b/docs/README_not_ngen.md @@ -23,6 +23,7 @@ To fetch and check out the latest revision (for the [currently used branch](#vie ## Building Libraries +### Solver options in SUMMA build scripts First, cd into the cmake directory in summa, without Actors: cd summa/build/cmake @@ -31,36 +32,53 @@ or with Actors: cd summa/build/summa/build/cmake -If you want to use Sundials IDA or BE Kinsol, set -DCMAKE_BUILD_TYPE=Sundials* in the build script instead of BE*. Then, before summa can be built, Sundials needs to be installed. -Download the latest release of IDA solver from SUNDIALS package in https://computing.llnl.gov/projects/sundials/sundials-software, using X.Y.Z as the latest version +If you want to use Sundials IDA or BE Kinsol, set -DCMAKE_BUILD_TYPE=Sundials* in the build script instead of BE*. Then, before summa can be built, Sundials needs to be installed. - wget "https://github.com/LLNL/sundials/releases/download/vX.Y.Z/sundials-X.Y.Z.tar.gz" - -Extract the corresponding compressed file, rename - - tar -xzf sundials-X.Y.Z.tar.gz && mv sundials-X.Y.Z sundials-software - -We suggest you periodically update to the latest version-- you can also install through github - git clone https://github.com/LLNL/sundials.git sundials-software - cd sundials-software - git fetch --all --tags --prune - git checkout tags/vX.Y.Z +### Installing SUNDIALS +0. For reference, let the main summa directory be contained in the folder `top_dir` + - e.g., summa exectuables would be located within `top_dir/summa/bin` -An example build_cmake file is at summa/build/cmake_external/build_cmakeSundials.bash which you can copy to builddir as build_cmake. Then, enter the buildir and run - - cd sundials/buildir - ./build_cmake - make - make install +2. Download the file `sundials-X.Y.Z.tar.gz` (where X.Y.Z is the latest SUNDIALS version) at https://github.com/LLNL/sundials/releases/latest + +3. Move `sundials-X.Y.Z.tar.gz` into `top_dir` + - `$ ls top_dir` should include `summa sundials-X.Y.Z.tar.gz` + +4. Extract the corresponding compressed file and rename + 1. `$ cd top_dir` + 2. `$ tar -xzf sundials-X.Y.Z.tar.gz && mv sundials-X.Y.Z sundials-software` + 3. `sundials-X.Y.Z.tar.gz` can now be removed to save space if desired + +5. Create new empty directories to prep for SUNDIALS installation + 1. within `top_dir`: `$ mkdir sundials` + 2. `$ cd sundials` + 3. `$ mkdir builddir instdir` + +6. Copy CMake build script from SUMMA files to properly configure SUNDIALS + 1. `$ cd builddir` + 2. `$ cp ../../summa/build/cmake_external/build_cmakeSundials.bash .` + +7. Build SUNDIALS configured for SUMMA + 1. within `builddir`: `$ ./build_cmakeSundials.bash` + 2. `$ make` + 3. `$ make install` + + +We suggest you periodically update to the latest version. It is also possible to install using Git: + + $ git clone https://github.com/LLNL/sundials.git sundials-software + $ cd sundials-software + $ git fetch --all --tags --prune + $ git checkout tags/vX.Y.Z Note if you need to recompile after a system upgrade, delete the contents of sundials/instdir and sundials/buildir EXCEPT sundials/buildir/build_cmake before building and installing. - Note that when there is an existing directory, it may sometimes be necessary to clear it and regenerate, especially if any changes were made to the CMakeLists.txt file. +### Building SUMMA + After there is build system directory, the shared library can be built using the `summabmi` CMake target. For example, the SummaSundials shared library file (i.e., the build config's `summabmi` target) can be built using: - cmake --build ../cmake_build --target all + $ cmake --build ../cmake_build --target all This will build a `cmake_build/libsummabmi..` file, where the version is configured within the CMake config, and the extension depends on the local machine's operating system. From c227b1425ba2b834bcd1294a7b735e1a77891267 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 20 Mar 2024 16:56:24 +0900 Subject: [PATCH 1225/1472] computHeatCapAnalytic and computCm only on state subset, not all states note, computThermConduct should really only be done if state or state+1, or veg in subset, but currently does on all --- build/source/engine/computHeatCap.f90 | 390 ++++++++++---------- build/source/engine/computThermConduct.f90 | 3 +- build/source/engine/enthalpyTemp.f90 | 42 +-- build/source/engine/eval8summa.f90 | 154 ++++---- build/source/engine/eval8summaWithPrime.f90 | 62 ++-- 5 files changed, 327 insertions(+), 324 deletions(-) diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index ce930f0e2..fb577c860 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -81,7 +81,6 @@ subroutine computStatMult(& heatCapVeg, & ! intent(in): heat capacity for canopy mLayerHeatCap, & ! intent(in): heat capacity for snow and soil ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) @@ -93,10 +92,9 @@ subroutine computStatMult(& ! input: data structures real(qp),intent(in) :: heatCapVeg ! volumetric heat capacity of vegetation (J m-3 K-1) real(qp),intent(in) :: mLayerHeatCap(:) ! volumetric heat capacity of snow and soil (J m-3 K-1) - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers ! output: state vectors - real(qp),intent(out) :: sMul(:) ! NOTE: qp ! multiplier for state vector (used in the residual calculations) + real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! multiplier for state vector (used in the residual calculations) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -110,13 +108,6 @@ subroutine computStatMult(& ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures associate(& - ! model diagnostic variables - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp] canopy depth (m) - volHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(in) : [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) - ! indices defining specific model states - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy hydrology state variable (mass) ! vector of energy and hydrology indices for the snow and soil domains ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for energy state variables in the snow+soil domain ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain @@ -125,8 +116,6 @@ subroutine computStatMult(& ! type of model state variabless ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in) : [i4b(:)] [state subset] type of desired model state variables ! number of layers - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in) : [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in) : [i4b] number of soil layers nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) & ! intent(in) : [i4b] total number of layers ) ! end association with variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- @@ -139,7 +128,6 @@ subroutine computStatMult(& ! define the multiplier for the state vector for residual calculations (vegetation canopy) ! NOTE: Use the "where" statement to generalize to multiple canopy layers (currently one canopy layer) - where(ixStateType_subset==iname_nrgCanair) sMul = Cp_air*iden_air ! volumetric heat capacity of air (J m-3 K-1) where(ixStateType_subset==iname_nrgCanopy) sMul = heatCapVeg ! volumetric heat capacity of the vegetation (J m-3 K-1) where(ixStateType_subset==iname_watCanopy) sMul = 1._rkind ! nothing else on the left hand side @@ -173,12 +161,11 @@ end subroutine computStatMult ! ********************************************************************************************************** ! public subroutine computHeatCapAnalytic: compute diagnostic energy variables (heat capacity) +! NOTE: computing on whole vector, could just compute on state subset ! ********************************************************************************************************** subroutine computHeatCapAnalytic(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) ! input: state variables + canopyDepth, & ! intent(in): canopy depth (m) scalarCanopyIce, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) scalarCanopyLiquid, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) scalarCanopyTemp, & ! intent(in): trial value of canopy temperature (K) @@ -195,15 +182,14 @@ subroutine computHeatCapAnalytic(& ! input output data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices - diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! output - heatCapVeg, & ! intent(out): heat capacity for canopy - mLayerHeatCap, & ! intent(out): heat capacity for snow and soil - dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + heatCapVeg, & ! intent(inout): heat capacity for canopy + mLayerHeatCap, & ! intent(inout): heat capacity for snow and soil + dVolHtCapBulk_dPsi0, & ! intent(inout): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(inout): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(inout): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(inout): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(inout): derivative in bulk heat capacity w.r.t. temperature ! output: error control err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------------- @@ -211,10 +197,9 @@ subroutine computHeatCapAnalytic(& USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) - real(rkind),intent(in) :: scalarCanopyLiquid + real(rkind),intent(in) :: scalarCanopyLiquid ! trial value of canopy liquid content (kg m-2) real(rkind),intent(in) :: scalarCanopyTemp ! value of canopy temperature (kg m-2) real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) @@ -229,119 +214,133 @@ subroutine computHeatCapAnalytic(& ! input/output: data structures type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model layer indices - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU ! output - real(qp),intent(out) :: heatCapVeg ! heat capacity for canopy - real(qp),intent(out) :: mLayerHeatCap(:) ! heat capacity for snow and soil - real(rkind),intent(out) :: dVolHtCapBulk_dPsi0(:) ! derivative in bulk heat capacity w.r.t. matric potential - real(rkind),intent(out) :: dVolHtCapBulk_dTheta(:) ! derivative in bulk heat capacity w.r.t. volumetric water content - real(rkind),intent(out) :: dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. volumetric water content - real(rkind),intent(out) :: dVolHtCapBulk_dTk(:) ! derivative in bulk heat capacity w.r.t. temperature - real(rkind),intent(out) :: dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature + real(qp),intent(inout) :: heatCapVeg ! heat capacity for canopy + real(qp),intent(inout) :: mLayerHeatCap(:) ! heat capacity for snow and soil + real(rkind),intent(inout) :: dVolHtCapBulk_dPsi0(:) ! derivative in bulk heat capacity w.r.t. matric potential + real(rkind),intent(inout) :: dVolHtCapBulk_dTheta(:) ! derivative in bulk heat capacity w.r.t. volumetric water content + real(rkind),intent(inout) :: dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. volumetric water content + real(rkind),intent(inout) :: dVolHtCapBulk_dTk(:) ! derivative in bulk heat capacity w.r.t. temperature + real(rkind),intent(inout) :: dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. temperature ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! -------------------------------------------------------- ------------------------------------------------------------------------ ! local variables - character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iState ! index of model state variable integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: iSoil ! index of soil layer + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixDomainType ! name of a given model domain + integer(i4b) :: ixControlIndex ! index within a given model domain real(rkind) :: fLiq ! fraction of liquid water real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) ! -------------------------------------------------------------------------------------------------------------------------------- ! associate variables in data structure associate(& ! input: coordinate variables - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers - layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): number of snow layers + ! mapping between the full state vector and the state subset + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ! type of domain, type of state variable, and index of control volume within domain + ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) ! input: heat capacity and thermal conductivity - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1) ,& ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1) ,& ! intent(in): maximum mass of vegetation (kg m-2) ! input: depth varying soil parameters - iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) + iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat ,& ! intent(in): intrinsic density of soil (kg m-3) theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) ) ! end associate statement ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="computHeatCapAnalytic/" - ! initialize the soil layer - iSoil=integerMissing - - ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) - if(computeVegFlux)then - heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component - Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component - Cp_ice*scalarCanopyIce/canopyDepth ! ice component - - ! derivatives - fLiq = scalarFracLiqVeg - dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) - if(scalarCanopyTemp < Tfreeze)then - dVolHtCapBulk_dTkCanopy = iden_water * (-Cp_ice + Cp_water) * dTheta_dTkCanopy ! no derivative in air - else - dVolHtCapBulk_dTkCanopy = 0._rkind - endif - end if - - ! loop through layers - do iLayer=1,nLayers - - ! get the soil layer - if(iLayer>nSnow) iSoil = iLayer-nSnow - - ! ***** - ! * compute the volumetric heat capacity of each layer (J m-3 K-1)... - ! ******************************************************************* - select case(layerType(iLayer)) - ! * soil - case(iname_soil) - mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component - iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component - - ! derivatives - dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use - Tcrit = crit_soilT( mLayerMatricHead(iSoil) ) - if( mLayerTemp(iLayer) < Tcrit)then - dVolHtCapBulk_dPsi0(iSoil) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(iSoil) - dVolHtCapBulk_dTk(iLayer) = (-iden_ice * Cp_ice + iden_water * Cp_water) * mLayerdTheta_dTk(iLayer) - else - dVolHtCapBulk_dPsi0(iSoil) = (iden_water*Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(iSoil) - dVolHtCapBulk_dTk(iLayer) = 0._rkind - endif - - case(iname_snow) - mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component - ! derivatives - fLiq = mLayerFracLiqSnow(iLayer) - dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air - if( mLayerTemp(iLayer) < Tfreeze)then - dVolHtCapBulk_dTk(iLayer) = ( iden_water * (-Cp_ice + Cp_water) + iden_air * (iden_water/iden_ice - 1._rkind) * Cp_air ) * mLayerdTheta_dTk(iLayer) - else - dVolHtCapBulk_dTk(iLayer) = 0._rkind - endif - - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return - end select - - end do ! looping through layers - !pause - - ! end association to variables in the data structure + ! loop through model state variables + do iState=1,size(ixMapSubset2Full) + + ! ----- + ! - compute indices... + ! -------------------- + + ! get domain type, and index of the control volume within the domain + ixFullVector = ixMapSubset2Full(iState) ! index within full state vector + ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) + ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain + + ! check an energy state, since only need for energy state equations + if(ixStateType(ixFullVector)==iname_nrgCanair .or. ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer)then + + ! get the layer index + select case(ixDomainType) + case(iname_cas); cycle ! canopy air space, do nothing (no water stored in canopy air space) + case(iname_veg); iLayer = integerMissing + case(iname_snow); iLayer = ixControlIndex + case(iname_soil); iLayer = ixControlIndex + nSnow + case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + + ! identify domain + select case(ixDomainType) + + case(iname_veg) + heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component + Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component + Cp_ice*scalarCanopyIce/canopyDepth ! ice component + + ! derivatives + fLiq = scalarFracLiqVeg + dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) + if(scalarCanopyTemp < Tfreeze)then + dVolHtCapBulk_dTkCanopy = iden_water * (-Cp_ice + Cp_water) * dTheta_dTkCanopy ! no derivative in air + else + dVolHtCapBulk_dTkCanopy = 0._rkind + endif + + case(iname_snow) + mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component + ! derivatives + fLiq = mLayerFracLiqSnow(iLayer) + dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air + if( mLayerTemp(iLayer) < Tfreeze)then + dVolHtCapBulk_dTk(iLayer) = ( iden_water * (-Cp_ice + Cp_water) + iden_air * (iden_water/iden_ice - 1._rkind) * Cp_air ) * mLayerdTheta_dTk(iLayer) + else + dVolHtCapBulk_dTk(iLayer) = 0._rkind + endif + + case(iname_soil) + mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component + iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component + ! derivatives + dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use + Tcrit = crit_soilT( mLayerMatricHead(ixControlIndex) ) + if( mLayerTemp(iLayer) < Tcrit)then + dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) + dVolHtCapBulk_dTk(iLayer) = (-iden_ice * Cp_ice + iden_water * Cp_water) * mLayerdTheta_dTk(iLayer) + else + dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_water*Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) + dVolHtCapBulk_dTk(iLayer) = 0._rkind + endif + end select + + end if ! if an energy layer + end do ! looping through state variables + end associate + end subroutine computHeatCapAnalytic ! ********************************************************************************************************** -! public subroutine computCm +! public subroutine computCm: compute diagnostic energy variables (change in enthTemp with water) +! NOTE: computing on whole vector, could just compute on state subset ! ********************************************************************************************************** subroutine computCm(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux ! input: state variables scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) mLayerTemp, & ! intent(in): vector of temperature (K) @@ -350,18 +349,17 @@ subroutine computCm(& mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices ! output - scalarCanopyCm, & ! intent(out): Cm for vegetation (J kg K-1) - mLayerCm, & ! intent(out): Cm for soil and snow (J kg K-1) - dCm_dTk, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) - dCm_dTkCanopy, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) + scalarCanopyCm, & ! intent(inout): Cm for vegetation (J kg K-1) + mLayerCm, & ! intent(inout): Cm for soil and snow (J kg K-1) + dCm_dTk, & ! intent(inout): derivative in Cm w.r.t. temperature (J kg K-2) + dCm_dTkCanopy, & ! intent(inout): derivative in Cm w.r.t. temperature (J kg K-2) ! output: error control err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------------- ! provide access to external subroutines USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists ! -------------------------------------------------------------------------------------------------------------------------------------- - ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + ! input: state variables real(rkind),intent(in) :: scalarCanopyTemp ! value of canopy temperature (K) real(rkind),intent(in) :: mLayerTemp(:) ! vector of temperature (K) real(rkind),intent(in) :: mLayerMatricHead(:) ! vector of total water matric potential (-) @@ -369,18 +367,20 @@ subroutine computCm(& type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model layer indices ! output: Cm and derivatives - real(qp),intent(out) :: scalarCanopyCm ! Cm for vegetation (J kg K-1) - real(qp),intent(out) :: mLayerCm(:) ! Cm for soil and snow (J kg K-1) - real(rkind),intent(out) :: dCm_dTk(:) ! derivative in Cm w.r.t. temperature (J kg K-2) - real(rkind),intent(out) :: dCm_dTkCanopy ! derivative in Cm w.r.t. temperature (J kg K-2) + real(qp),intent(inout) :: scalarCanopyCm ! Cm for vegetation (J kg K-1) + real(qp),intent(inout) :: mLayerCm(:) ! Cm for soil and snow (J kg K-1) + real(rkind),intent(inout) :: dCm_dTk(:) ! derivative in Cm w.r.t. temperature (J kg K-2) + real(rkind),intent(inout) :: dCm_dTkCanopy ! derivative in Cm w.r.t. temperature (J kg K-2) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables - character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iState ! index of model state variable integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: iSoil ! index of soil layer + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixDomainType ! name of a given model domain + integer(i4b) :: ixControlIndex ! index within a given model domain real(rkind) :: diffT ! temperature difference from Tfreeze real(rkind) :: integral ! integral of snow freezing curve real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) @@ -389,76 +389,90 @@ subroutine computCm(& ! associate variables in data structure associate(& ! input: coordinate variables - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers - layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): number of snow layers + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ! mapping between the full state vector and the state subset + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ! type of domain, type of state variable, and index of control volume within domain + ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat & ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) ) ! end associate statement ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="computCm/" - ! initialize the soil layer - iSoil=integerMissing - - ! compute Cm of vegetation - ! Note that scalarCanopyCm/iden_water is computed - if(computeVegFlux)then - diffT = scalarCanopyTemp - Tfreeze - if(diffT>=0._rkind)then - scalarCanopyCm = Cp_water * diffT - ! derivatives - dCm_dTkCanopy = Cp_water - else - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - scalarCanopyCm = Cp_water * integral + Cp_ice * (diffT - integral) - ! derivatives - d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) - dCm_dTkCanopy = Cp_water * d_integral_dTk + Cp_ice * (1._rkind - d_integral_dTk) - end if - end if - - ! loop through layers - do iLayer=1,nLayers - - ! get the soil layer - if(iLayer>nSnow) iSoil = iLayer-nSnow - - ! ***** - ! * compute Cm of of each layer - ! ******************************************************************* - select case(layerType(iLayer)) - ! * soil - case(iname_soil) - diffT = mLayerTemp(iLayer) - Tfreeze - Tcrit = crit_soilT( mLayerMatricHead(iSoil) ) - if( mLayerTemp(iLayer)>=Tcrit)then - mLayerCm(iLayer) = (iden_water * Cp_water - iden_air * Cp_air) * diffT + ! loop through model state variables + do iState=1,size(ixMapSubset2Full) + + ! ----- + ! - compute indices... + ! -------------------- + + ! get domain type, and index of the control volume within the domain + ixFullVector = ixMapSubset2Full(iState) ! index within full state vector + ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) + ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain + + ! check an energy state, since only need for energy state equations + if(ixStateType(ixFullVector)==iname_nrgCanair .or. ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer)then + + ! get the layer index + select case(ixDomainType) + case(iname_cas); cycle ! canopy air space, do nothing (no water stored in canopy air space) + case(iname_veg); iLayer = integerMissing + case(iname_snow); iLayer = ixControlIndex + case(iname_soil); iLayer = ixControlIndex + nSnow + case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + + ! identify domain + select case(ixDomainType) + + case(iname_veg) + ! Note that scalarCanopyCm/iden_water is computed + diffT = scalarCanopyTemp - Tfreeze + if(diffT>=0._rkind)then + scalarCanopyCm = Cp_water * diffT + ! derivatives + dCm_dTkCanopy = Cp_water + else + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + scalarCanopyCm = Cp_water * integral + Cp_ice * (diffT - integral) + ! derivatives + d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) + dCm_dTkCanopy = Cp_water * d_integral_dTk + Cp_ice * (1._rkind - d_integral_dTk) + end if + + case(iname_snow) + diffT = mLayerTemp(iLayer) - Tfreeze + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + mLayerCm(iLayer) = (iden_water * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( diffT - integral ) & + + (iden_water * Cp_water - iden_air * Cp_air) * integral ! derivatives - dCm_dTk(iLayer) = (iden_water * Cp_water - iden_air * Cp_air) - else - mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air) * diffT - ! derivatives - dCm_dTk(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air) - endif - - case(iname_snow) - diffT = mLayerTemp(iLayer) - Tfreeze - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - mLayerCm(iLayer) = (iden_water * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( diffT - integral ) & - + (iden_water * Cp_water - iden_air * Cp_air) * integral - ! derivatives - d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) - dCm_dTk(iLayer) = (iden_water * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( 1._rkind - d_integral_dTk ) & - + (iden_water * Cp_water - iden_air * Cp_air) * d_integral_dTk - - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute Cm'; return - end select - - end do ! looping through layers - !pause - - ! end association to variables in the data structure + d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) + dCm_dTk(iLayer) = (iden_water * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( 1._rkind - d_integral_dTk ) & + + (iden_water * Cp_water - iden_air * Cp_air) * d_integral_dTk + + case(iname_soil) + diffT = mLayerTemp(iLayer) - Tfreeze + Tcrit = crit_soilT( mLayerMatricHead(ixControlIndex) ) + if( mLayerTemp(iLayer)>=Tcrit)then + mLayerCm(iLayer) = (iden_water * Cp_water - iden_air * Cp_air) * diffT + ! derivatives + dCm_dTk(iLayer) = (iden_water * Cp_water - iden_air * Cp_air) + else + mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air) * diffT + ! derivatives + dCm_dTk(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air) + endif + end select + + end if ! if an energy layer + end do ! looping through state variables + end associate end subroutine computCm diff --git a/build/source/engine/computThermConduct.f90 b/build/source/engine/computThermConduct.f90 index eb0621402..334d6cd97 100644 --- a/build/source/engine/computThermConduct.f90 +++ b/build/source/engine/computThermConduct.f90 @@ -67,7 +67,8 @@ module computThermConduct_module contains ! ********************************************************************************************************** -! public subroutine computThermConduct: recompute diagnostic energy variables (thermal conductivity and heat capacity) +! public subroutine computThermConduct: recompute diagnostic energy variables (thermal conductivity) +! NOTE: does every layer regardless if layer or layer+1 is in state subset, could fix for speedup ! ********************************************************************************************************** subroutine computThermConduct(& ! input: control variables diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index c341ae39c..8c6040415 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -419,28 +419,29 @@ end function T2enthalpy_snwWat ! ************************************************************************************************************************ ! public subroutine T2enthTemp: compute temperature component of enthalpy from temperature and total water content +! NOTE: only computes the states in the state subset ! ************************************************************************************************************************ subroutine T2enthTemp(& - use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy + use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthTemp, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthTemp, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpy, & ! intent(inout): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTemp, & ! intent(inout): temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthTemp, & ! intent(inout): temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control - err,message) ! intent(out): error control + err,message) ! intent(out): error control ! ------------------------------------------------------------------------------------------------------------------------- ! downwind routines USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists @@ -464,9 +465,9 @@ subroutine T2enthTemp(& real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) ! output: enthalpy - real(rkind),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - real(rkind),intent(out) :: scalarCanopyEnthTemp ! temperature component of enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(out) :: mLayerEnthTemp(:) ! temperature component of enthalpy of each snow+soil layer (J m-3) + real(rkind),intent(inout) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) + real(rkind),intent(inout) :: scalarCanopyEnthTemp ! temperature component of enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(inout) :: mLayerEnthTemp(:) ! temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -531,7 +532,7 @@ subroutine T2enthTemp(& ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain - ! check an energy state + ! check an energy state, since only need for energy state residuals if(ixStateType(ixFullVector)==iname_nrgCanair .or. ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer)then ! get the layer index @@ -732,7 +733,6 @@ subroutine enthTemp2enthalpy(& character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------------------------------- ! declare local variables - character(len=128) :: cmessage ! error message in downwind routine integer(i4b) :: iState ! index of model state variable integer(i4b) :: iLayer ! index of model layer integer(i4b) :: ixFullVector ! index within full state vector @@ -775,7 +775,7 @@ subroutine enthTemp2enthalpy(& ! get the layer index select case(ixDomainType) - case(iname_cas); cycle ! canopy air space: do nothing (no water stored in canopy air space) + case(iname_cas); cycle ! canopy air space: do nothing (no water stored in canopy air space) case(iname_veg) scalarCanopyH= scalarCanopyH - LH_fus * scalarCanopyIce/ canopyDepth case(iname_snow) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index d3334170b..807691059 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -358,23 +358,23 @@ subroutine eval8summa(& ! extract variables from the model state vector call varExtract(& ! input - stateVec, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers + stateVec, & ! intent(in): model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(inout): trial value of storage of water in the aquifer (m) + scalarAquiferStorageTrial, & ! intent(inout): trial value of storage of water in the aquifer (m) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) @@ -382,35 +382,33 @@ subroutine eval8summa(& ! update diagnostic variables and derivatives call updateVars(& ! input - .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value for canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value for canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value for canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value for canopy ice content (kg m-2) + scalarCanopyTempTrial, & ! intent(inout): trial value for canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value for canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value for canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value for canopy ice content (kg m-2) ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) if(updateStateCp)then ! *** compute volumetric heat capacity C_p call computHeatCapAnalytic(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + ! input: state variables canopyDepth, & ! intent(in): canopy depth (m) - ! input: state variables scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) @@ -427,15 +425,14 @@ subroutine eval8summa(& ! input output data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output - heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow - dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + ! output + heatCapVegTrial, & ! intent(inout): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(inout): volumetric heat capacity of soil and snow + dVolHtCapBulk_dPsi0, & ! intent(inout): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(inout): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(inout): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(inout): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(inout): derivative in bulk heat capacity w.r.t. temperature ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -445,7 +442,6 @@ subroutine eval8summa(& ! input heatCapVegTrial, & ! intent(in): volumetric heat capacity of vegetation canopy mLayerHeatCapTrial, & ! intent(in): volumetric heat capacity of soil and snow - diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) @@ -453,11 +449,11 @@ subroutine eval8summa(& if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) else ! set state heat capacity derivatives to 0 for constant through step - dVolHtCapBulk_dPsi0 = 0._rkind - dVolHtCapBulk_dTheta = 0._rkind - dVolHtCapBulk_dCanWat = 0._rkind - dVolHtCapBulk_dTk = 0._rkind - dVolHtCapBulk_dTkCanopy = 0._rkind + dVolHtCapBulk_dPsi0 = 0._rkind + dVolHtCapBulk_dTheta = 0._rkind + dVolHtCapBulk_dCanWat = 0._rkind + dVolHtCapBulk_dTk = 0._rkind + dVolHtCapBulk_dTkCanopy = 0._rkind endif ! updateStateCp if(updateFluxCp)then @@ -492,30 +488,28 @@ subroutine eval8summa(& if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if else ! set flux heat capacity derivatives to 0 for constant through step - dThermalC_dWatAbove = 0._rkind - dThermalC_dWatBelow = 0._rkind - dThermalC_dTempAbove = 0._rkind - dThermalC_dTempBelow = 0._rkind + dThermalC_dWatAbove = 0._rkind + dThermalC_dWatBelow = 0._rkind + dThermalC_dTempAbove = 0._rkind + dThermalC_dTempBelow = 0._rkind endif ! updateFluxCp if(needStateCm)then ! compute C_m call computCm(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux ! input: state variables - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (-) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (-) ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices ! output - canopyCmTrial, & ! intent(out): Cm for vegetation (J kg K-1) - mLayerCmTrial, & ! intent(out): Cm for soil and snow (J kg K-1) - dCm_dTk, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) - dCm_dTkCanopy, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) - err,cmessage) ! intent(out): error control + canopyCmTrial, & ! intent(inout): Cm for vegetation (J kg K-1) + mLayerCmTrial, & ! intent(inout): Cm for soil and snow (J kg K-1) + dCm_dTk, & ! intent(inout): derivative in Cm w.r.t. temperature (J kg K-2) + dCm_dTkCanopy, & ! intent(inout): derivative in Cm w.r.t. temperature (J kg K-2) + err,cmessage) ! intent(inout): error control else canopyCmTrial = 0._qp mLayerCmTrial = 0._qp @@ -526,26 +520,26 @@ subroutine eval8summa(& if(ixNrgConserv== enthalpyFD .or. ixNrgConserv == enthalpyFDlu)then ! use residual as enthalpy_delta - (phase change)_delta ! compute temperature component of enthalpy call T2enthTemp(& - ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy + ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value for canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) + scalarCanairTempTrial, & ! intent(in): trial value for canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) ! output: enthalpy - scalarCanairEnthalpyTrial, & ! intent(out): trial value for enthalpy of the canopy air space (J m-3) - scalarCanopyEnthTempTrial, & ! intent(out): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthTempTrial, & ! intent(out): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpyTrial, & ! intent(inout): trial value for enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTempTrial, & ! intent(inout): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthTempTrial, & ! intent(inout): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif else ! use residual as (Cp*DeltaTemp + Cm*DeltaTheta)_delta - (phase change)_delta diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 584cad06e..d8e6b0ed0 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -375,7 +375,7 @@ subroutine eval8summaWithPrime(& ! output: variables for the aquifer scalarAquiferStoragePrime, & ! intent(inout): derivative of storage of water in the aquifer (m s-1) ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) if(ixNrgConserv== enthalpyFD .or. ixNrgConserv == enthalpyFDlu)then ! use state variable as enthalpy, need to compute temperature @@ -453,10 +453,8 @@ subroutine eval8summaWithPrime(& if(updateStateCp)then ! *** compute volumetric heat capacity C_p call computHeatCapAnalytic(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) ! input: state variables + canopyDepth, & ! intent(in): canopy depth (m) scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) scalarCanopyLiqTrial, & ! intent(in): trial value for the liquid water on the vegetation canopy (kg m-2) scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) @@ -473,15 +471,14 @@ subroutine eval8summaWithPrime(& ! input output data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices - diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! output - heatCapVegTrial, & ! intent(out): volumetric heat capacity of vegetation canopy - mLayerHeatCapTrial, & ! intent(out): volumetric heat capacity of soil and snow - dVolHtCapBulk_dPsi0, & ! intent(out): derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat, & ! intent(out): derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy, & ! intent(out): derivative in bulk heat capacity w.r.t. temperature + heatCapVegTrial, & ! intent(inout): volumetric heat capacity of vegetation canopy + mLayerHeatCapTrial, & ! intent(inout): volumetric heat capacity of soil and snow + dVolHtCapBulk_dPsi0, & ! intent(inout): derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta, & ! intent(inout): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat, & ! intent(inout): derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk, & ! intent(inout): derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy, & ! intent(inout): derivative in bulk heat capacity w.r.t. temperature ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -491,7 +488,6 @@ subroutine eval8summaWithPrime(& ! input heatCapVegTrial, & ! intent(in): volumetric heat capacity of vegetation canopy mLayerHeatCapTrial, & ! intent(in): volumetric heat capacity of soil and snow - diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) @@ -499,11 +495,11 @@ subroutine eval8summaWithPrime(& if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) else ! set state heat capacity derivatives to 0 for constant through step - dVolHtCapBulk_dPsi0 = 0._rkind - dVolHtCapBulk_dTheta = 0._rkind - dVolHtCapBulk_dCanWat = 0._rkind - dVolHtCapBulk_dTk = 0._rkind - dVolHtCapBulk_dTkCanopy = 0._rkind + dVolHtCapBulk_dPsi0 = 0._rkind + dVolHtCapBulk_dTheta = 0._rkind + dVolHtCapBulk_dCanWat = 0._rkind + dVolHtCapBulk_dTk = 0._rkind + dVolHtCapBulk_dTkCanopy = 0._rkind endif ! updateStateCp if(updateFluxCp)then @@ -539,30 +535,28 @@ subroutine eval8summaWithPrime(& if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if else ! set flux heat capacity derivatives to 0 for constant through step - dThermalC_dWatAbove = 0._rkind - dThermalC_dWatBelow = 0._rkind - dThermalC_dTempAbove = 0._rkind - dThermalC_dTempBelow = 0._rkind + dThermalC_dWatAbove = 0._rkind + dThermalC_dWatBelow = 0._rkind + dThermalC_dTempAbove = 0._rkind + dThermalC_dTempBelow = 0._rkind endif ! updateFluxCp if(needStateCm)then ! compute C_m call computCm(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux ! input: state variables - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (-) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (-) ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices ! output - canopyCmTrial, & ! intent(out): Cm for vegetation (J kg K-1) - mLayerCmTrial, & ! intent(out): Cm for soil and snow (J kg K-1) - dCm_dTk, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) - dCm_dTkCanopy, & ! intent(out): derivative in Cm w.r.t. temperature (J kg K-2) - err,cmessage) ! intent(out): error control + canopyCmTrial, & ! intent(inout): Cm for vegetation (J kg K-1) + mLayerCmTrial, & ! intent(inout): Cm for soil and snow (J kg K-1) + dCm_dTk, & ! intent(inout): derivative in Cm w.r.t. temperature (J kg K-2) + dCm_dTkCanopy, & ! intent(inout): derivative in Cm w.r.t. temperature (J kg K-2) + err,cmessage) ! intent(inout): error control else canopyCmTrial = 0._qp mLayerCmTrial = 0._qp From e828000596ed4bde47c4005845d996df79642b49 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 20 Mar 2024 17:52:44 +0900 Subject: [PATCH 1226/1472] changed residual to use enthalpy put useEnthalpy flag in Jacobian, need to port in derivatives and change Jacobian corrected calls to updateVarsWithPrime in varSubstep, not sure what to do there yet --- build/source/engine/computJacobWithPrime.f90 | 31 ++-- build/source/engine/computResidWithPrime.f90 | 141 +++++-------------- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/eval8summaWithPrime.f90 | 59 ++++---- build/source/engine/varSubstep.f90 | 6 + 5 files changed, 90 insertions(+), 149 deletions(-) diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index 8ab4abe96..12da8b280 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -66,15 +66,21 @@ module computJacobWithPrime_module iden_water ! intrinsic density of liquid water (kg m-3) ! look-up values for the choice of groundwater parameterization -USE mDecisions_module,only: & - qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization - bigBucket, & ! a big bucket (lumped aquifer model) - noExplicit ! no explicit groundwater parameterization +USE mDecisions_module,only: & + qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization + bigBucket, & ! a big bucket (lumped aquifer model) + noExplicit ! no explicit groundwater parameterization ! look-up values for the form of Richards' equation -USE mDecisions_module,only: & - moisture, & ! moisture-based form of Richards' equation - mixdform ! mixed form of Richards' equation +USE mDecisions_module,only: & + moisture, & ! moisture-based form of Richards' equation + mixdform ! mixed form of Richards' equation + +! look-up values for the choice of heat capacity computation +USE mDecisions_module,only: & + closedForm, & ! heat capacity closed form in backward Euler residual + enthalpyFDlu, & ! enthalpy with lookup tables finite difference in backward Euler residual + enthalpyFD ! enthalpy with hypergeometric function finite difference in backward Euler residual implicit none ! define constants @@ -103,6 +109,7 @@ subroutine computJacobWithPrime(& specificStorage, & ! intent(in): specific storage coefficient (m-1) theta_sat, & ! intent(in): soil porosity (-) ixRichards, & ! intent(in): choice of option for Richards' equation + useEnthalpy, & ! intent(in): flag if enthalpy is state variable ! input: data structures model_decisions, & ! intent(in): model decisions indx_data, & ! intent(in): index data @@ -135,6 +142,7 @@ subroutine computJacobWithPrime(& real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation + logical(lgt),intent(in) :: useEnthalpy ! flag if enthalpy is state variable ! input: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers @@ -1117,11 +1125,12 @@ integer(c_int) function computJacob4ida(t, cj, sunvec_y, sunvec_yp, sunvec_r, & eqns_data%nSoil, & ! intent(in): number of soil layers eqns_data%nLayers, & ! intent(in): total number of layers eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - (eqns_data%model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow + (eqns_data%model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow eqns_data%ixMatrix, & ! intent(in): form of the Jacobian matrix - eqns_data%mpar_data%var(iLookPARAM%specificStorage)%dat(1) , & ! intent(in): specific storage coefficient (m-1) - eqns_data%mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): soil porosity (-) - eqns_data%model_decisions(iLookDECISIONS%f_Richards)%iDecision, & ! intent(in): choice of option for Richards' equation + eqns_data%mpar_data%var(iLookPARAM%specificStorage)%dat(1) , & ! intent(in): specific storage coefficient (m-1) + eqns_data%mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): soil porosity (-) + eqns_data%model_decisions(iLookDECISIONS%f_Richards)%iDecision, & ! intent(in): choice of option for Richards' equation + (eqns_data%model_decisions(iLookDECISIONS%nrgConserv)%iDecision.ne.closedForm), & ! intent(in): flag if enthalpy is state variable ! input: data structures eqns_data%model_decisions, & ! intent(in): model decisions eqns_data%indx_data, & ! intent(in): index data diff --git a/build/source/engine/computResidWithPrime.f90 b/build/source/engine/computResidWithPrime.f90 index a8a482ee8..96ad01bce 100644 --- a/build/source/engine/computResidWithPrime.f90 +++ b/build/source/engine/computResidWithPrime.f90 @@ -49,7 +49,6 @@ module computResidWithPrime_module iden_water ! intrinsic density of liquid water (kg m-3) ! privacy implicit none -private::printResidDAE public::computResidWithPrime contains @@ -62,6 +61,7 @@ subroutine computResidWithPrime(& nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers + useEnthalpy, & ! intent(in): flag if enthalpy is state variable ! input: flux vectors sMul, & ! intent(in): state vector multiplier (used in the residual calculations) fVec, & ! intent(in): flux vector @@ -96,6 +96,7 @@ subroutine computResidWithPrime(& integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain + logical(lgt),intent(in) :: useEnthalpy ! flag if enthalpy is state variable ! input: flux vectors real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(rkind),intent(in) :: fVec(:) ! flux vector @@ -170,17 +171,23 @@ subroutine computResidWithPrime(& ! intialize additional terms on the RHS as zero rAdd(:) = 0._rkind - ! compute energy associated with melt freeze for the vegetation canopy - if(ixVegNrg/=integerMissing) rAdd(ixVegNrg) = rAdd(ixVegNrg) + LH_fus*scalarCanopyIcePrime/canopyDepth ! energy associated with melt/freeze (J m-3) - ! compute energy associated with melt/freeze for snow - ! NOTE: allow expansion of ice during melt-freeze for snow; deny expansion of ice during melt-freeze for soil - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - select case( layerType(iLayer) ) - case(iname_snow); rAdd( ixSnowSoilNrg(iLayer) ) = rAdd( ixSnowSoilNrg(iLayer) ) + LH_fus*iden_ice * mLayerVolFracIcePrime(iLayer) - case(iname_soil); rAdd( ixSnowSoilNrg(iLayer) ) = rAdd( ixSnowSoilNrg(iLayer) ) + LH_fus*iden_water * mLayerVolFracIcePrime(iLayer) - end select - end do ! looping through non-missing energy state variables in the snow+soil domain + ! add melt freeze terms only if not using enthalpy terms + ! NOTE: would need to use these if were using enthTemp terms + if(.not.useEnthalpy)then + ! compute energy associated with melt freeze for the vegetation canopy + if(ixVegNrg/=integerMissing) rAdd(ixVegNrg) = rAdd(ixVegNrg) + LH_fus*scalarCanopyIcePrime/canopyDepth ! energy associated with melt/freeze (J m-3) + + ! compute energy associated with melt/freeze for snow + ! NOTE: allow expansion of ice during melt-freeze for snow; deny expansion of ice during melt-freeze for soil + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + select case( layerType(iLayer) ) + case(iname_snow); rAdd( ixSnowSoilNrg(iLayer) ) = rAdd( ixSnowSoilNrg(iLayer) ) + LH_fus*iden_ice * mLayerVolFracIcePrime(iLayer) + case(iname_soil); rAdd( ixSnowSoilNrg(iLayer) ) = rAdd( ixSnowSoilNrg(iLayer) ) + LH_fus*iden_water * mLayerVolFracIcePrime(iLayer) + end select + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + endif ! sink terms soil hydrology (-) @@ -201,9 +208,14 @@ subroutine computResidWithPrime(& ! compute the residual vector for the vegetation canopy ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision ! --> energy balance - if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg) * scalarCanairTempPrime - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) - if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime + scalarCanopyCmTrial * scalarCanopyWatPrime/canopyDepth & - - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) + if(useEnthalpy)then + if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = scalarCanairEnthalpyPrime - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = scalarCanopyEnthalpyPrime - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) + else + if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg) * scalarCanairTempPrime - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime + scalarCanopyCmTrial * scalarCanopyWatPrime/canopyDepth & + - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) + endif ! --> mass balance if(ixVegHyd/=integerMissing)then scalarCanopyHydPrime = merge(scalarCanopyWatPrime, scalarCanopyLiqPrime, (ixStateType( ixHydCanopy(ixVegVolume) )==iname_watCanopy) ) @@ -213,8 +225,12 @@ subroutine computResidWithPrime(& ! compute the residual vector for the snow and soil sub-domains for energy if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) ) * mLayerTempPrime(iLayer) + mLayerCmTrial(iLayer) * mLayerVolFracWatPrime(iLayer) & - - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) + if(useEnthalpy)then + rVec( ixSnowSoilNrg(iLayer) ) = mLayerEnthalpyPrime(iLayer) - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) + else + rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) ) * mLayerTempPrime(iLayer) + mLayerCmTrial(iLayer) * mLayerVolFracWatPrime(iLayer) & + - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) + endif end do ! looping through non-missing energy state variables in the snow+soil domain endif @@ -250,95 +266,4 @@ subroutine computResidWithPrime(& end subroutine computResidWithPrime -! ********************************************************************************************************** -! private subroutine printResidDAE: print the residual vector mainly for debugging -! ********************************************************************************************************** -subroutine printResidDAE( & - ! input: model control - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - ! input: data structures - indx_data, & ! intent(in): index data - ! output - rAdd, & ! intent(out): additional (sink) terms on the RHS of the state equation - rVec) ! intent(out): residual vector - - ! -------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input: model control - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - ! output - real(rkind),intent(in) :: rAdd(:) ! additional (sink) terms on the RHS of the state equation - real(qp),intent(in) :: rVec(:) ! NOTE: qp ! residual vector - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - ! -------------------------------------------------------------------------------------------------------------------------------- - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - ! -------------------------------------------------------------------------------------------------------------------------------- - ! link to the necessary variables for the residual computations - associate(& - ! number of state variables of a specific type - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain - nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain - ! model indices - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] indices for energy states in the snow+soil subdomain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the snow+soil subdomain - ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] indices for hydrology states in the soil subdomain - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] index of the hydrology states in the canopy domain - ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] named variables defining the type of hydrology states in snow+soil domain - layerType => indx_data%var(iLookINDEX%layerType)%dat & ! intent(in): [i4b(:)] named variables defining the type of layer in snow+soil domain - ) ! association to necessary variables for the residual computations - ! -------------------------------------------------------------------------------------------------------------------------------- - - if(ixVegNrg/=integerMissing) print *, 'rAdd(ixVegNrg) = ', rAdd(ixVegNrg) - - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) - select case( layerType(iLayer) ) - case(iname_snow); print *, 'rAdd( ixSnowSoilNrg(iLayer) ) = ', rAdd( ixSnowSoilNrg(iLayer) ) - case(iname_soil); print *, 'rAdd( ixSnowSoilNrg(iLayer) ) = ', rAdd( ixSnowSoilNrg(iLayer) ) - end select - end do - endif - - if(nSoilOnlyHyd>0)then - do concurrent (iLayer=1:nSoil,ixSoilOnlyHyd(iLayer)/=integerMissing) - print *, 'rAdd( ixSoilOnlyHyd(iLayer) ) = ', rAdd( ixSoilOnlyHyd(iLayer) ) - end do - endif - - if(ixCasNrg/=integerMissing) print *, 'rVec(ixCasNrg) = ', rVec(ixCasNrg) - if(ixVegNrg/=integerMissing) print *, 'rVec(ixVegNrg) = ', rVec(ixVegNrg) - if(ixVegHyd/=integerMissing)then - print *, 'rVec(ixVegHyd) = ', rVec(ixVegHyd) - endif - - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) - print *, 'rVec( ixSnowSoilNrg(iLayer) ) = ', rVec( ixSnowSoilNrg(iLayer) ) - end do - endif - - if(nSnowSoilHyd>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) - print *, 'rVec( ixSnowSoilHyd(iLayer) ) = ', rVec( ixSnowSoilHyd(iLayer) ) - end do - endif - - if(ixAqWat/=integerMissing) print *, ' rVec(ixAqWat) = ', rVec(ixAqWat) - - end associate - -end subroutine printResidDAE - end module computResidWithPrime_module diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 807691059..cf60a37b7 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -318,7 +318,7 @@ subroutine eval8summa(& end if ! ( feasibility check ) if(ixNrgConserv == enthalpyFD .or. ixNrgConserv == enthalpyFDlu)then - ! use mixed form of energy equation, need these true + ! use mixed form of energy equation, need these true to use for Jacobian updateStateCp = .true. updateFluxCp = .true. needStateCm = .true. diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index d8e6b0ed0..1ff70d0d0 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -640,41 +640,42 @@ subroutine eval8summaWithPrime(& dt1 = 1._qp ! always 1 for IDA since using Prime derivatives call computResidWithPrime(& - ! input: model control - dt1, & ! intent(in): length of the residual time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers + ! input: model control + dt1, & ! intent(in): length of the residual time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + ixNrgConserv.ne.closedForm, & ! intent(in): flag if enthalpy is state variable ! input: flux vectors - sMul, & ! intent(in): state vector multiplier (used in the residual calculations) - fluxVec, & ! intent(in): flux vector + sMul, & ! intent(in): state vector multiplier (used in the residual calculations) + fluxVec, & ! intent(in): flux vector ! input: state variables (already disaggregated into scalars and vectors) - scalarCanairTempPrime, & ! intent(in): prime value for the temperature of the canopy air space (K s-1) - scalarCanopyTempPrime, & ! intent(in): prime value for the temperature of the vegetation canopy (K s-1) - scalarCanopyWatPrime, & ! intent(in): prime value for the water on the vegetation canopy (kg m-2 s-1) - mLayerTempPrime, & ! intent(in): prime vector of the temperature of each snow and soil layer (K s-1) - scalarAquiferStoragePrime, & ! intent(in): prime value for storage of water in the aquifer (m s-1) + scalarCanairTempPrime, & ! intent(in): prime value for the temperature of the canopy air space (K s-1) + scalarCanopyTempPrime, & ! intent(in): prime value for the temperature of the vegetation canopy (K s-1) + scalarCanopyWatPrime, & ! intent(in): prime value for the water on the vegetation canopy (kg m-2 s-1) + mLayerTempPrime, & ! intent(in): prime vector of the temperature of each snow and soil layer (K s-1) + scalarAquiferStoragePrime, & ! intent(in): prime value for storage of water in the aquifer (m s-1) ! input: diagnostic variables defining the liquid water and ice content (function of state variables) - scalarCanopyIcePrime, & ! intent(in): prime value for the ice on the vegetation canopy (kg m-2 s-1) - scalarCanopyLiqPrime, & ! intent(in): prime value for the liq on the vegetation canopy (kg m-2 s-1) - mLayerVolFracIcePrime, & ! intent(in): prime vector of the volumetric ice in each snow and soil layer (s-1) - mLayerVolFracWatPrime, & ! intent(in): prime vector of the volumetric water in each snow and soil layer (s-1) - mLayerVolFracLiqPrime, & ! intent(in): prime vector of the volumetric liq in each snow and soil layer (s-1) + scalarCanopyIcePrime, & ! intent(in): prime value for the ice on the vegetation canopy (kg m-2 s-1) + scalarCanopyLiqPrime, & ! intent(in): prime value for the liq on the vegetation canopy (kg m-2 s-1) + mLayerVolFracIcePrime, & ! intent(in): prime vector of the volumetric ice in each snow and soil layer (s-1) + mLayerVolFracWatPrime, & ! intent(in): prime vector of the volumetric water in each snow and soil layer (s-1) + mLayerVolFracLiqPrime, & ! intent(in): prime vector of the volumetric liq in each snow and soil layer (s-1) ! input: enthalpy terms - canopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) - mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) - scalarCanairEnthalpyPrime, & ! intent(in): prime value for the enthalpy of the canopy air space (W m-3) - scalarCanopyEnthalpyPrime, & ! intent(in): prime value for the of enthalpy of the vegetation canopy (W m-3) - mLayerEnthalpyPrime, & ! intent(in): prime vector of the of enthalpy of each snow and soil layer (W m-3) + canopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) + mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) + scalarCanairEnthalpyPrime, & ! intent(in): prime value for the enthalpy of the canopy air space (W m-3) + scalarCanopyEnthalpyPrime, & ! intent(in): prime value for the of enthalpy of the vegetation canopy (W m-3) + mLayerEnthalpyPrime, & ! intent(in): prime vector of the of enthalpy of each snow and soil layer (W m-3) ! input: data structures - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - flux_data, & ! intent(in): model fluxes for a local HRU - indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + flux_data, & ! intent(in): model fluxes for a local HRU + indx_data, & ! intent(in): index data ! output - resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation - resVec, & ! intent(out): residual vector - err,cmessage) ! intent(out): error control + resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation + resVec, & ! intent(out): residual vector + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) else ! currently not using residuals outside Sundials! diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 167c13c01..c0d9e7d39 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -895,6 +895,8 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! update diagnostic variables call updateVarsWithPrime(& ! input + ixNrgConserv.ne.closedForm, & ! intent(in): flag if need to update temperature from enthalpy + ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy .false., & ! intent(in): logical flag if computing for Jacobian update doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU @@ -902,6 +904,10 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(inout): model diagnostic variables for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input: enthalpy state variables + scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(in): trial value for enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(in): trial vector of enthalpy of each snow+soil layer (J m-3) ! output: variables for the vegetation canopy scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) From 1dc4a1f6655d1c0ba23682ce48107a12fab56e85 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 20 Mar 2024 18:14:37 +0900 Subject: [PATCH 1227/1472] added derivatives to structures --- build/source/dshare/get_ixname.f90 | 17 ++++++++++++----- build/source/dshare/popMetadat.f90 | 17 +++++++++++++---- build/source/dshare/var_lookup.f90 | 10 +++++++++- build/source/engine/computJacobWithPrime.f90 | 7 +++++++ build/source/engine/updateVarsWithPrime.f90 | 2 +- 5 files changed, 42 insertions(+), 11 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 03c72412f..7f05d04c8 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -831,11 +831,18 @@ function get_ixderiv(varName) case('dFracLiqSnow_dTk' ); get_ixderiv = iLookDERIV%dFracLiqSnow_dTk ! derivative in fraction of liquid snow w.r.t. temperature case('mLayerdTheta_dTk' ); get_ixderiv = iLookDERIV%mLayerdTheta_dTk ! derivative of volumetric liquid water content w.r.t. temperature (K-1) case('mLayerd2Theta_dTk2' ); get_ixderiv = iLookDERIV%mLayerd2Theta_dTk2 ! second derivative of volumetric liquid water content w.r.t. temperature - ! derivatives in time - case( 'mLayerdTemp_dt' ); get_ixderiv = iLookDERIV%mLayerdTemp_dt ! timestep change in layer temperature - case( 'scalarCanopydTemp_dt' ); get_ixderiv = iLookDERIV%scalarCanopydTemp_dt ! timestep change in canopy temperature - case( 'mLayerdWat_dt' ); get_ixderiv = iLookDERIV%mLayerdWat_dt ! timestep change in layer volumetric fraction of total water - case( 'scalarCanopydWat_dt' ); get_ixderiv = iLookDERIV%scalarCanopydWat_dt ! timestep change in canopy water content + ! derivatives in time + case('mLayerdTemp_dt' ); get_ixderiv = iLookDERIV%mLayerdTemp_dt ! timestep change in layer temperature + case('scalarCanopydTemp_dt' ); get_ixderiv = iLookDERIV%scalarCanopydTemp_dt ! timestep change in canopy temperature + case('mLayerdWat_dt' ); get_ixderiv = iLookDERIV%mLayerdWat_dt ! timestep change in layer volumetric fraction of total water + case('scalarCanopydWat_dt' ); get_ixderiv = iLookDERIV%scalarCanopydWat_dt ! timestep change in canopy water content + ! derivatives of temperature if enthalpy is the state variable + case('dCanairTemp_dEnthalpy' ); get_ixderiv = iLookDERIV%dCanairTemp_dEnthalpy ! derivative of canopy air temperature w.r.t. enthalpy + case('dCanopyTemp_dEnthalpy' ); get_ixderiv = iLookDERIV%dCanopyTemp_dEnthalpy ! derivative of canopy temperature w.r.t. enthalpy + case('dTemp_dEnthalpy' ); get_ixderiv = iLookDERIV%dTemp_dEnthalpy ! derivative of temperature w.r.t. enthalpy + case('dCanopyTemp_dCanWat' ); get_ixderiv = iLookDERIV%dCanopyTemp_dCanWat ! derivative of canopy temperature w.r.t. volumetric water content + case('dTemp_dTheta' ); get_ixderiv = iLookDERIV%dTemp_dTheta ! derivative of temperature w.r.t. volumetric water content + case('dTemp_dPsi0' ); get_ixderiv = iLookDERIV%dTemp_dPsi0 ! derivative of temperature w.r.t. total water matric potential case default get_ixderiv = integerMissing diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 2291afd5a..c903d35bb 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -657,10 +657,19 @@ subroutine popMetadat(err,message) deriv_meta(iLookDERIV%mLayerdTheta_dTk) = var_info('mLayerdTheta_dTk' , 'derivative of volumetric liquid water content w.r.t. temperature' , 'K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%mLayerd2Theta_dTk2) = var_info('mLayerd2Theta_dTk2' , 'second derivative of volumetric liquid water content w.r.t. temperature','K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) ! derivatives in time - deriv_meta(iLookDERIV%mLayerdTemp_dt) = var_info('mLayerdTemp_dt' , 'timestep change in layer temperature' , 'K' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%scalarCanopydTemp_dt) = var_info('scalarCanopydTemp_dt' , 'timestep change in canopy temperature' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%mLayerdWat_dt) = var_info('mLayerdWat_dt' , 'timestep change in layer volumetric fraction of total water' , '-' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - deriv_meta(iLookDERIV%scalarCanopydWat_dt) = var_info('scalarCanopydWat_dt' , 'timestep change in canopy water content' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%mLayerdTemp_dt) = var_info('mLayerdTemp_dt' , 'timestep change in layer temperature' , 'K s-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%scalarCanopydTemp_dt) = var_info('scalarCanopydTemp_dt' , 'timestep change in canopy temperature' , 'K s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%mLayerdWat_dt) = var_info('mLayerdWat_dt' , 'timestep change in layer volumetric fraction of total water' , 's-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%scalarCanopydWat_dt) = var_info('scalarCanopydWat_dt' , 'timestep change in canopy water content' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! derivatives of temperature if enthalpy is the state variable + deriv_meta(iLookDERIV%dCanairTemp_dEnthalpy) = var_info('dCanairTemp_dEnthalpy' , 'derivative of canopy air temperature w.r.t. enthalpy' , 'K J-1 m3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCanopyTemp_dEnthalpy) = var_info('dCanopyTemp_dEnthalpy' , 'derivative of canopy temperature w.r.t. enthalpy' , 'K J-1 m3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dTemp_dEnthalpy) = var_info('dTemp_dEnthalpy' , 'derivative of temperature w.r.t. enthalpy' , 'K J-1 m3' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dCanopyTemp_dCanWat) = var_info('dCanopyTemp_dCanWat' , 'derivative of canopy temperature w.r.t. volumetric water content' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dTemp_dTheta) = var_info('dTemp_dTheta' , 'derivative of temperature w.r.t. volumetric water content' , 'K' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + deriv_meta(iLookDERIV%dTemp_dPsi0) = var_info('dTemp_dPsi0' , 'derivative of temperature w.r.t. total water matric potential' , 'K m-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + + ! ----- ! * basin-wide runoff and aquifer fluxes... ! ----------------------------------------- diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index cf3766c43..bef532542 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -692,6 +692,13 @@ MODULE var_lookup integer(i4b) :: scalarCanopydTemp_dt = integerMissing ! timestep change in canopy temperature integer(i4b) :: mLayerdWat_dt = integerMissing ! timestep change in layer volumetric fraction of total water integer(i4b) :: scalarCanopydWat_dt = integerMissing ! timestep change in canopy water content + ! derivatives of temperature if enthalpy is the state variable + integer(i4b) :: dCanairTemp_dEnthalpy = integerMissing ! derivative of canopy air temperature w.r.t. enthalpy + integer(i4b) :: dCanopyTemp_dEnthalpy = integerMissing ! derivative of canopy temperature w.r.t. enthalpy + integer(i4b) :: dTemp_dEnthalpy = integerMissing ! derivative of temperature w.r.t. enthalpy + integer(i4b) :: dCanopyTemp_dCanWat = integerMissing ! derivative of canopy temperature w.r.t. volumetric water content + integer(i4b) :: dTemp_dTheta = integerMissing ! derivative of temperature w.r.t. volumetric water content + integer(i4b) :: dTemp_dPsi0 = integerMissing ! derivative of temperature w.r.t. total water matric potential endtype iLook_deriv @@ -937,7 +944,8 @@ MODULE var_lookup 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,& 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,& 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,& - 71, 72, 73, 74, 75) + 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,& + 81) ! named variables: model indices type(iLook_index), public,parameter :: iLookINDEX =ilook_index ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index 12da8b280..be3da7242 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -282,6 +282,13 @@ subroutine computJacobWithPrime(& ! derivative in Cm w.r.t. relevant state variables dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk )%dat ,& ! intent(in): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) + ! derivatives of temperature if enthalpy is the state variable + dCanairTemp_dEnthalpy => deriv_data%var(iLookDERIV%dCanairTemp_dEnthalpy )%dat(1) ,& ! intent(in): [dp] derivative of canopy air temperature w.r.t. enthalpy + dCanopyTemp_dEnthalpy => deriv_data%var(iLookDERIV%dCanopyTemp_dEnthalpy )%dat(1) ,& ! intent(in): [dp] derivative of canopy temperature w.r.t. enthalpy + dTemp_dEnthalpy => deriv_data%var(iLookDERIV%dTemp_dEnthalpy )%dat ,& ! intent(in): [dp(:)] derivative of temperature w.r.t. enthalpy + dCanopyTemp_dCanWat => deriv_data%var(iLookDERIV%dCanopyTemp_dCanWat )%dat(1) ,& ! intent(in): [dp] derivative of canopy temperature w.r.t. volumetric water content + dTemp_dTheta => deriv_data%var(iLookDERIV%dTemp_dTheta )%dat ,& ! intent(in): [dp(:)] derivative of temperature w.r.t. volumetric water content + dTemp_dPsi0 => deriv_data%var(iLookDERIV%dTemp_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative of temperature w.r.t. total water matric potential ! diagnostic variables scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg )%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg )%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) diff --git a/build/source/engine/updateVarsWithPrime.f90 b/build/source/engine/updateVarsWithPrime.f90 index 911919bd8..ff610ce00 100644 --- a/build/source/engine/updateVarsWithPrime.f90 +++ b/build/source/engine/updateVarsWithPrime.f90 @@ -282,7 +282,7 @@ subroutine updateVarsWithPrime(& d2VolTot_dPsi02 => deriv_data%var(iLookDERIV%d2VolTot_dPsi02)%dat ,& ! intent(out): [dp(:)] second derivative in total water content w.r.t. total water matric potential mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2)%dat ,& ! intent(out): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature d2Theta_dTkCanopy2 => deriv_data%var(iLookDERIV%d2Theta_dTkCanopy2)%dat(1) ,& ! intent(out): [dp ] second derivative of volumetric liquid water content w.r.t. temperature - ! derivatives of temperature if enthalpy is the state variable, could probably turn off if outside of solver + ! derivatives of temperature if enthalpy is the state variable for Jacobian only dCanairTemp_dEnthalpy => deriv_data%var(iLookDERIV%dCanairTemp_dEnthalpy)%dat(1) ,& ! intent(out): [dp] derivative of canopy air temperature w.r.t. enthalpy dCanopyTemp_dEnthalpy => deriv_data%var(iLookDERIV%dCanopyTemp_dEnthalpy)%dat(1) ,& ! intent(out): [dp] derivative of canopy temperature w.r.t. enthalpy dTemp_dEnthalpy => deriv_data%var(iLookDERIV%dTemp_dEnthalpy)%dat ,& ! intent(out): [dp(:)] derivative of temperature w.r.t. enthalpy From ae39b4101f1566441bff389177612bf452bd0818 Mon Sep 17 00:00:00 2001 From: Kyle Date: Wed, 20 Mar 2024 18:49:32 +0000 Subject: [PATCH 1228/1472] Update ACTORS_DIR path in CMakeLists.txt --- build/cmake/CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 5ba465ea1..f07470249 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -237,7 +237,7 @@ set(NETCDF_DIR ${F_MASTER}/build/source/netcdf) set(NOAHMP_DIR ${F_MASTER}/build/source/noah-mp) # Define Actors specific directories -set(ACTORS_DIR ${PARENT_DIR}/build/source/actors) +set(ACTORS_DIR ${PARENT_DIR}/build/source) set(FILE_ACCESS_DIR ${ACTORS_DIR}/file_access_actor) set(JOB_ACTOR_DIR ${ACTORS_DIR}/job_actor) set(HRU_ACTOR_DIR ${ACTORS_DIR}/hru_actor) @@ -440,7 +440,9 @@ set(FILE_ACCESS_ACTOR ${ACTORS_DIR}/file_access_actor/output_container.cpp) set(JOB_ACTOR ${ACTORS_DIR}/job_actor/GRU.cpp - ${ACTORS_DIR}/job_actor/job_actor.cpp) + ${ACTORS_DIR}/job_actor/job_actor.cpp + ${ACTORS_DIR}/job_actor/distributed_job_actor.cpp + ${ACTORS_DIR}/job_actor/node_actor.cpp) set(HRU_ACTOR ${ACTORS_DIR}/hru_actor/hru_actor.cpp ${ACTORS_DIR}/hru_actor/hru_batch_actor.cpp) From 3ab52e9c85d5d1c0520e5b307cd7f59ba39bf440 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 21 Mar 2024 11:30:21 +0900 Subject: [PATCH 1229/1472] removing unneeded constants, starting enthalpy jacobian --- build/source/engine/computFlux.f90 | 11 +------ build/source/engine/computJacob.f90 | 26 ++++++++-------- build/source/engine/computJacobWithPrime.f90 | 16 +++++++--- build/source/engine/computSnowDepth.f90 | 3 -- build/source/engine/getVectorz.f90 | 3 -- build/source/engine/opSplittin.f90 | 14 +-------- build/source/engine/soilLiqFlx.f90 | 10 +------ build/source/engine/systemSolv.f90 | 2 -- build/source/engine/updatState.f90 | 3 +- build/source/engine/updatStateWithPrime.f90 | 3 +- build/source/engine/updateVarsWithPrime.f90 | 2 +- build/source/engine/vegNrgFlux.f90 | 31 +++++++++----------- build/source/engine/volicePack.f90 | 5 ---- 13 files changed, 45 insertions(+), 84 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 24b94ee74..9b53ce37d 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -65,16 +65,7 @@ module computFlux_module USE globalData,only:dx ! finite difference increment ! constants -USE multiconst,only:& - gravity, & ! acceleration of gravity (m s-2) - Tfreeze, & ! temperature at freezing (K) - LH_fus, & ! latent heat of fusion (J kg-1) - LH_vap, & ! latent heat of vaporization (J kg-1) - LH_sub, & ! latent heat of sublimation (J kg-1) - Cp_air, & ! specific heat of air (J kg-1 K-1) - iden_air, & ! intrinsic density of air (kg m-3) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water ! intrinsic density of liquid water (kg m-3) +USE multiconst,only:iden_water ! intrinsic density of liquid water (kg m-3) ! look-up values for the choice of groundwater representation (local-column, or single-basin) USE mDecisions_module,only: & diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 40ed8aa66..1bccdd247 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -86,19 +86,19 @@ module computJacob_module ! public subroutine computJacob: compute the Jacobian matrix ! ********************************************************************************************************** subroutine computJacob(& - ! input: model control - in_computJacob, & ! intent(in): model control - ! input: data structures - indx_data, & ! intent(in): index data - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables - dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) - ! input-output: Jacobian and its diagonal - dMat, & ! intent(inout): diagonal of the Jacobian matrix - aJac, & ! intent(out): Jacobian matrix - ! output: error control - out_computJacob) ! intent(out): error code and error message + ! input: model control + in_computJacob, & ! intent(in): model control + ! input: data structures + indx_data, & ! intent(in): index data + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables + dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) + ! input-output: Jacobian and its diagonal + dMat, & ! intent(inout): diagonal of the Jacobian matrix + aJac, & ! intent(out): Jacobian matrix + ! output: error control + out_computJacob) ! intent(out): error code and error message ! ----------------------------------------------------------------------------------------------------------------- implicit none ! input: model control diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index be3da7242..bba2f2774 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -157,11 +157,11 @@ subroutine computJacobWithPrime(& real(rkind),intent(in) :: scalarCanopyTempPrime ! derivative value for temperature of the vegetation canopy (K) real(rkind),intent(in) :: scalarCanopyWatPrime ! derivative value for water content of the vegetation canopy ! input-output: Jacobian and its diagonal - real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix - real(rkind),intent(out) :: aJac(:,:) ! Jacobian matrix + real(rkind),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix + real(rkind),intent(out) :: aJac(:,:) ! Jacobian matrix ! output variables - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! -------------------------------------------------------------- ! * local variables ! -------------------------------------------------------------- @@ -339,6 +339,14 @@ subroutine computJacobWithPrime(& endif end do + if(useEnthalpy)then + dMat(ixCasNrg) = 1._rkind ! gets multiplied later by cj + dMat(ixVegNrg) = cj + do iLayer=1,nLayers + if(ixSnowSoilNrg(iLayer)/=integerMissing) dMat(ixSnowSoilNrg(iLayer)) = cj + end do + end do + ! compute additional terms for the Jacobian for the soil domain (excluding fluxes) do iLayer=1,nSoil if(ixSoilOnlyHyd(iLayer)/=integerMissing)then diff --git a/build/source/engine/computSnowDepth.f90 b/build/source/engine/computSnowDepth.f90 index 568294aa0..ff4215068 100644 --- a/build/source/engine/computSnowDepth.f90 +++ b/build/source/engine/computSnowDepth.f90 @@ -5,9 +5,6 @@ module computSnowDepth_module ! physical constants USE multiconst,only:& - Tfreeze, & ! temperature at freezing (K) - LH_fus, & ! latent heat of fusion (J kg-1) - LH_sub, & ! latent heat of sublimation (J kg-1) iden_ice, & ! intrinsic density of ice (kg m-3) iden_water ! intrinsic density of liquid water (kg m-3) diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index 495ec90b9..5365abc9c 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -53,12 +53,9 @@ module getVectorz_module ! constants USE multiconst,only:& - gravity, & ! acceleration of gravity (m s-2) Tfreeze, & ! temperature at freezing (K) Cp_air, & ! specific heat of air (J kg-1 K-1) - LH_fus, & ! latent heat of fusion (J kg-1) iden_air, & ! intrinsic density of air (kg m-3) - iden_ice, & ! intrinsic density of ice (kg m-3) iden_water ! intrinsic density of liquid water (kg m-3) ! provide access to the derived types to define the data structures diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 1e03b7db6..a17db25b5 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -56,19 +56,7 @@ module opSplittin_module USE globalData,only:deriv_meta ! metadata on the model derivatives USE globalData,only:flux2state_orig ! metadata on flux-to-state mapping (original state variables) USE globalData,only:flux2state_liq ! metadata on flux-to-state mapping (liquid water state variables) - -! constants -USE multiconst,only:& - gravity, & ! acceleration of gravity (m s-2) - Tfreeze, & ! temperature at freezing (K) - LH_fus, & ! latent heat of fusion (J kg-1) - LH_vap, & ! latent heat of vaporization (J kg-1) - LH_sub, & ! latent heat of sublimation (J kg-1) - Cp_air, & ! specific heat of air (J kg-1 K-1) - iden_air, & ! intrinsic density of air (kg m-3) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water ! intrinsic density of liquid water (kg m-3) - + ! provide access to indices that define elements of the data structures USE var_lookup,only:iLookATTR ! named variables for structure elements USE var_lookup,only:iLookTYPE ! named variables for structure elements diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index bf0f97687..a63b41031 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -35,15 +35,7 @@ module soilLiqFlx_module USE globalData,only:realMissing ! missing real number ! physical constants -USE multiconst,only:& - LH_fus, & ! latent heat of fusion (J kg-1) - LH_vap, & ! latent heat of vaporization (J kg-1) - LH_sub, & ! latent heat of sublimation (J kg-1) - gravity, & ! gravitational acceleteration (m s-2) - Tfreeze, & ! freezing point of pure water (K) - iden_air,& ! intrinsic density of air (kg m-3) - iden_ice,& ! intrinsic density of ice (kg m-3) - iden_water ! intrinsic density of water (kg m-3) +USE multiconst,only:iden_water ! intrinsic density of water (kg m-3) ! named variables USE var_lookup,only:iLookPROG ! named variables for structure elements diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 4ffc5065e..4d0a03461 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -54,8 +54,6 @@ module systemSolv_module ! constants USE multiconst,only:& - LH_fus, & ! latent heat of fusion (J K-1) - Tfreeze, & ! temperature at freezing (K) iden_ice, & ! intrinsic density of ice (kg m-3) iden_water ! intrinsic density of liquid water (kg m-3) diff --git a/build/source/engine/updatState.f90 b/build/source/engine/updatState.f90 index 3df71b0bd..e7b25eacd 100644 --- a/build/source/engine/updatState.f90 +++ b/build/source/engine/updatState.f90 @@ -23,10 +23,9 @@ module updatState_module ! physical constants USE multiconst,only:& Tfreeze, & ! freezing point of pure water (K) - iden_air, & ! intrinsic density of air (kg m-3) iden_ice, & ! intrinsic density of ice (kg m-3) iden_water, & ! intrinsic density of water (kg m-3) - gravity, & ! gravitational acceleteration (m s-2) + gravity, & ! gravitational acceleration (m s-2) LH_fus ! latent heat of fusion (J kg-1) implicit none private diff --git a/build/source/engine/updatStateWithPrime.f90 b/build/source/engine/updatStateWithPrime.f90 index 2ea8c72cd..be2bb3450 100644 --- a/build/source/engine/updatStateWithPrime.f90 +++ b/build/source/engine/updatStateWithPrime.f90 @@ -3,10 +3,9 @@ module updatStateWithPrime_module ! physical constants USE multiconst,only:& Tfreeze, & ! freezing point of pure water (K) - iden_air, & ! intrinsic density of air (kg m-3) iden_ice, & ! intrinsic density of ice (kg m-3) iden_water, & ! intrinsic density of water (kg m-3) - gravity, & ! gravitational acceleteration (m s-2) + gravity, & ! gravitational acceleration (m s-2) LH_fus ! latent heat of fusion (J kg-1) ! missing values diff --git a/build/source/engine/updateVarsWithPrime.f90 b/build/source/engine/updateVarsWithPrime.f90 index ff610ce00..c023bcbea 100644 --- a/build/source/engine/updateVarsWithPrime.f90 +++ b/build/source/engine/updateVarsWithPrime.f90 @@ -282,7 +282,7 @@ subroutine updateVarsWithPrime(& d2VolTot_dPsi02 => deriv_data%var(iLookDERIV%d2VolTot_dPsi02)%dat ,& ! intent(out): [dp(:)] second derivative in total water content w.r.t. total water matric potential mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2)%dat ,& ! intent(out): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature d2Theta_dTkCanopy2 => deriv_data%var(iLookDERIV%d2Theta_dTkCanopy2)%dat(1) ,& ! intent(out): [dp ] second derivative of volumetric liquid water content w.r.t. temperature - ! derivatives of temperature if enthalpy is the state variable for Jacobian only + ! derivatives of temperature if enthalpy is the state variable dCanairTemp_dEnthalpy => deriv_data%var(iLookDERIV%dCanairTemp_dEnthalpy)%dat(1) ,& ! intent(out): [dp] derivative of canopy air temperature w.r.t. enthalpy dCanopyTemp_dEnthalpy => deriv_data%var(iLookDERIV%dCanopyTemp_dEnthalpy)%dat(1) ,& ! intent(out): [dp] derivative of canopy temperature w.r.t. enthalpy dTemp_dEnthalpy => deriv_data%var(iLookDERIV%dTemp_dEnthalpy)%dat ,& ! intent(out): [dp(:)] derivative of temperature w.r.t. enthalpy diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index b497d7841..ba1613eba 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -45,22 +45,19 @@ module vegNrgFlux_module USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure ! constants -USE multiconst,only:gravity ! acceleration of gravity (m s-2) -USE multiconst,only:vkc ! von Karman's constant (-) -USE multiconst,only:w_ratio ! molecular ratio water to dry air (-) -USE multiconst,only:R_wv ! gas constant for water vapor (Pa K-1 m3 kg-1; J kg-1 K-1) -USE multiconst,only:Cp_air ! specific heat of air (J kg-1 K-1) -USE multiconst,only:Cp_ice ! specific heat of ice (J kg-1 K-1) -USE multiconst,only:Cp_soil ! specific heat of soil (J kg-1 K-1) -USE multiconst,only:Cp_water ! specific heat of liquid water (J kg-1 K-1) -USE multiconst,only:Tfreeze ! temperature at freezing (K) -USE multiconst,only:LH_fus ! latent heat of fusion (J kg-1) -USE multiconst,only:LH_vap ! latent heat of vaporization (J kg-1) -USE multiconst,only:LH_sub ! latent heat of sublimation (J kg-1) -USE multiconst,only:sb ! Stefan Boltzman constant (W m-2 K-4) -USE multiconst,only:iden_air ! intrinsic density of air (kg m-3) -USE multiconst,only:iden_ice ! intrinsic density of ice (kg m-3) -USE multiconst,only:iden_water ! intrinsic density of liquid water (kg m-3) +USE multiconst,only:& + gravity, & ! acceleration of gravity (m s-2) + vkc, & ! von Karman's constant (-) + w_ratio, & ! molecular ratio water to dry air (-) + R_wv, & ! gas constant for water vapor (Pa K-1 m3 kg-1; J kg-1 K-1) + Cp_air, & ! specific heat of air (J kg-1 K-1) + Cp_ice, & ! specific heat of ice (J kg-1 K-1) + Cp_water, & ! specific heat of liquid water (J kg-1 K-1) + Tfreeze, & ! temperature at freezing (K) + LH_vap, & ! latent heat of vaporization (J kg-1) + LH_sub, & ! latent heat of sublimation (J kg-1) + sb, & ! Stefan Boltzman constant (W m-2 K-4) + iden_air ! intrinsic density of air (kg m-3) ! look-up values for method used to compute derivative USE mDecisions_module,only: & @@ -2111,7 +2108,7 @@ subroutine turbFluxes(& err=0; message='turbFluxes/' ! compute constants - volHeatCapacityAir = iden_air*cp_air ! volumetric heat capacity of air (J m-3) + volHeatCapacityAir = iden_air*Cp_air ! volumetric heat capacity of air (J m-3) latentHeatConstant = iden_air*w_ratio/airpres ! latent heat constant for (kg m-3 Pa-1) ! ***** diff --git a/build/source/engine/volicePack.f90 b/build/source/engine/volicePack.f90 index c9bb0b967..4aef78867 100644 --- a/build/source/engine/volicePack.f90 +++ b/build/source/engine/volicePack.f90 @@ -39,11 +39,6 @@ module volicePack_module ! physical constants USE multiconst,only:& - Tfreeze, & ! freezing point (K) - LH_fus, & ! latent heat of fusion (J kg-1) - LH_vap, & ! latent heat of vaporization (J kg-1) - LH_sub, & ! latent heat of sublimation (J kg-1) - iden_air, & ! intrinsic density of air (kg m-3) iden_ice, & ! intrinsic density of ice (kg m-3) iden_water ! intrinsic density of water (kg m-3) From d034de3a474190285dec976c0deb11f9acd16250 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 21 Mar 2024 11:31:34 +0900 Subject: [PATCH 1230/1472] part of previous commit --- build/source/engine/computJacobWithPrime.f90 | 2 +- build/source/engine/tol4ida.f90 | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index bba2f2774..49ced3b5b 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -514,7 +514,7 @@ subroutine computJacobWithPrime(& ! - include derivatives of water fluxes w.r.t energy fluxes for current layer aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) - ! (cross-derivative terms for the layer below) + ! (cross-derivative terms for the layer be_low) if(iLayer Date: Thu, 21 Mar 2024 15:06:39 +0900 Subject: [PATCH 1231/1472] put in Jacobian mod --- build/source/engine/computJacob.f90 | 1 - build/source/engine/computJacobWithPrime.f90 | 81 ++++++++++++++------ 2 files changed, 58 insertions(+), 24 deletions(-) diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 1bccdd247..ae1736617 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -1111,7 +1111,6 @@ function ixOffDiag(jState,iState) implicit none integer(i4b),intent(in) :: jState ! off-diagonal state integer(i4b),intent(in) :: iState ! diagonal state - integer(i4b),parameter :: ixDiag=kl+ku+1 ! index for the diagonal, the offset in the band Jacobian matrix integer(i4b) :: ixOffDiag ! off-diagonal index in the band-diagonal matrix ixOffDiag = ixDiag + jState - iState diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index 49ced3b5b..82893ed57 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -176,6 +176,7 @@ subroutine computJacobWithPrime(& integer(i4b) :: jLayer ! index of model layer within the full state vector (hydrology) integer(i4b) :: pLayer ! indices of soil layers (used for the baseflow derivatives) ! conversion factors + real(rkind) :: LHfu0 ! latent heat of fusion, modified to be 0 if using enthalpy formulation and not using real(rkind) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative ! -------------------------------------------------------------- ! associate variables from data structures @@ -339,14 +340,6 @@ subroutine computJacobWithPrime(& endif end do - if(useEnthalpy)then - dMat(ixCasNrg) = 1._rkind ! gets multiplied later by cj - dMat(ixVegNrg) = cj - do iLayer=1,nLayers - if(ixSnowSoilNrg(iLayer)/=integerMissing) dMat(ixSnowSoilNrg(iLayer)) = cj - end do - end do - ! compute additional terms for the Jacobian for the soil domain (excluding fluxes) do iLayer=1,nSoil if(ixSoilOnlyHyd(iLayer)/=integerMissing)then @@ -359,6 +352,18 @@ subroutine computJacobWithPrime(& endif end do + ! if using enthalpy as a state variable, zero out usual RHS terms and add them end of the iteration loop + if(useEnthalpy)then + dMat(ixCasNrg) = 0._rkind + dMat(ixVegNrg) = 0._rkind + do iLayer=1,nLayers + if(ixSnowSoilNrg(iLayer)/=integerMissing) dMat(ixSnowSoilNrg(iLayer)) = 0._rkind + end do + LH_fu0 = 0._rkind ! set to 0 to not use RHS terms + else + LH_fu0 = LH_fus ! use regular value + endif + ! define the form of the matrix select case(ixMatrix) ! ********************************************************************************************************************************************************* @@ -391,11 +396,11 @@ subroutine computJacobWithPrime(& if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water ! * cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) - ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth * cj & + ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fu0/canopyDepth = J m-3; dLiq = kg m-2 + if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fu0/canopyDepth * cj & + dVolHtCapBulk_dCanWat * scalarCanopyTempPrime + scalarCanopyCm/canopyDepth * cj & - (dt/canopyDepth) * dCanopyNetFlux_dCanWat & - + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth + + LH_fu0 * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) endif @@ -506,10 +511,10 @@ subroutine computJacobWithPrime(& if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) ! - include derivatives of energy fluxes w.r.t water fluxes for current layer - aJac(ixOffDiag(nrgState,watState),watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & + aJac(ixOffDiag(nrgState,watState),watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fu0*iden_water * cj & + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) + mLayerCm(iLayer) * cj & + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) & - + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) + + LH_fu0*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) ! - include derivatives of water fluxes w.r.t energy fluxes for current layer aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) @@ -680,8 +685,8 @@ subroutine computJacobWithPrime(& aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) * cj & + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + mLayerCm(jLayer) * d2VolTot_dPsi02(iLayer) * mLayerMatricHeadPrime(iLayer) if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present - aJac(ixOffDiag(nrgState,watState),watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) * cj & - - LH_fus*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_dPsi02(iLayer) + aJac(ixOffDiag(nrgState,watState),watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + aJac(ixOffDiag(nrgState,watState),watState) = -LH_fu0*iden_water * dVolTot_dPsi0(iLayer) * cj & + - LH_fu0*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_dPsi02(iLayer) + aJac(ixOffDiag(nrgState,watState),watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content endif ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above @@ -745,11 +750,11 @@ subroutine computJacobWithPrime(& if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water ! * cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) - ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 - if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fus/canopyDepth * cj & + ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fu0/canopyDepth = J m-3; dLiq = kg m-2 + if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (-1._rkind + scalarFracLiqVeg)*LH_fu0/canopyDepth * cj & + dVolHtCapBulk_dCanWat * scalarCanopyTempPrime + scalarCanopyCm/canopyDepth * cj& - (dt/canopyDepth) * dCanopyNetFlux_dCanWat & - + LH_fus * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth + + LH_fu0 * scalarCanopyTempPrime * dFracLiqVeg_dTkCanopy / canopyDepth if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanWat) endif @@ -860,10 +865,10 @@ subroutine computJacobWithPrime(& if(watstate/=integerMissing)then ! (energy state for the current layer is within the state subset) ! - include derivatives of energy fluxes w.r.t water fluxes for current layer - aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fus*iden_water * cj & + aJac(nrgState,watState) = (-1._rkind + mLayerFracLiqSnow(iLayer))*LH_fu0*iden_water * cj & + dVolHtCapBulk_dTheta(iLayer) * mLayerTempPrime(iLayer) + mLayerCm(iLayer) * cj & + (dt/mLayerDepth(iLayer))*(-dNrgFlux_dWatBelow(iLayer-1) + dNrgFlux_dWatAbove(iLayer)) & - + LH_fus*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) + + LH_fu0*iden_water * mLayerTempPrime(iLayer) * dFracLiqSnow_dTk(iLayer) ! (dF/dLiq) ! - include derivatives of water fluxes w.r.t energy fluxes for current layer aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) @@ -1013,8 +1018,8 @@ subroutine computJacobWithPrime(& aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) * cj & + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + mLayerCm(jLayer) * d2VolTot_dPsi02(iLayer) * mLayerMatricHeadPrime(iLayer) if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present - aJac(nrgState,watState) = -LH_fus*iden_water * dVolTot_dPsi0(iLayer) * cj & - - LH_fus*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_dPsi02(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + aJac(nrgState,watState) = -LH_fu0*iden_water * dVolTot_dPsi0(iLayer) * cj & + - LH_fu0*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_dPsi02(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content endif ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above @@ -1051,6 +1056,36 @@ subroutine computJacobWithPrime(& end select ! type of matrix ! ********************************************************************************************************************************************************* + ! ----- + ! * if desired, modify to use enthalpy as a state variable instead of temperature + ! NOTE, dMat has been set to 0 and now 1._rkind * cj is added instead + ! ---------------------------------------- + if(useEnthalpy)then + aJac(:,ixCasNrg) = aJac(:,ixCasNrg) * dCanairTemp_dEnthalpy + if(ixMatrix==ixBandMatrix) aJac(ixDiag, ixCasNrg) = aJac(ixDiag, ixCasNrg) + 1._rkind * cj + if(ixMatrix==ixFullMatrix) aJac(ixCasNrg, ixCasNrg) = aJac(ixCasNrg, ixCasNrg) + 1._rkind * cj + + aJac(:,ixVegHyd) = aJac(:,ixVegHyd) + aJac(:,ixVegNrg) * dCanopyTemp_dCanWat + aJac(:,ixVegNrg) = aJac(:,ixVegNrg) * dCanopyTemp_dEnthalpy + if(ixMatrix==ixBandMatrix) aJac(ixDiag, ixVegNrg) = aJac(ixDiag, ixVegNrg) + 1._rkind * cj + if(ixMatrix==ixFullMatrix) aJac(ixVegNrg, ixVegNrg) = aJac(ixVegNrg, ixVegNrg) + 1._rkind * cj + + if(nSnowSoilNrg>0)then + do iLayer=1,nLayers + nrgState = ixSnowSoilNrg(iLayer) + if(nrgState==integerMissing) cycle + watState = ixSnowSoilHyd(iLayer) + if(watstate/=integerMissing)then + if(nLayer<=nSnow) aJac(:,watState) = aJac(:,watState) + aJac(:,nrgState) * dTemp_dTheta(iLayer) + if(nLayer>nSnow) aJac(:,watState) = aJac(:,watState) + aJac(:,nrgState) * dTemp_dPsi0(iLayer) + endif + aJac(:,nrgState) = aJac(:,nrgState) * dTemp_dEnthalpy(iLayer) + if(ixMatrix==ixBandMatrix) aJac(ixDiag, nrgState) = aJac(ixDiag, nrgState) + 1._rkind * cj + if(ixMatrix==ixFullMatrix) aJac(nrgState, nrgState) = aJac(nrgState, nrgState) + 1._rkind * cj + enddo + endif + endif + ! print the Jacobian if(globalPrintFlag)then select case(ixMatrix) @@ -1180,7 +1215,7 @@ function ixOffDiag(jState,iState) implicit none integer(i4b),intent(in) :: jState ! off-diagonal state integer(i4b),intent(in) :: iState ! diagonal state - integer(i4b) :: ixOffDiag ! off-diagonal index in gthe band-diagonal matrix + integer(i4b) :: ixOffDiag ! off-diagonal index in the band-diagonal matrix ixOffDiag = ixDiag + jState - iState end function ixOffDiag From a73a45dc17b4616535f9c592d5ea3470bfa1f7a2 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 21 Mar 2024 22:22:03 +0900 Subject: [PATCH 1232/1472] everything in, need to debug --- build/source/engine/eval8summaWithPrime.f90 | 13 ++- build/source/engine/systemSolv.f90 | 5 +- build/source/engine/varSubstep.f90 | 115 +++++++++++++------- 3 files changed, 91 insertions(+), 42 deletions(-) diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 1ff70d0d0..13a6b8e1d 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -182,7 +182,7 @@ subroutine eval8summaWithPrime(& real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector of volumetric liquid water content (-) real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector of volumetric ice content (-) real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) - + ! prime state variables real(rkind) :: scalarCanairEnthalpyPrime ! prime value for enthalpy of the canopy air space (W m-3) real(rkind) :: scalarCanairTempPrime ! prime value for temperature of the canopy air space (K s-1) real(rkind) :: scalarCanopyEnthalpyPrime ! prime value for enthalpy of the vegetation canopy (W m-3) @@ -193,6 +193,13 @@ subroutine eval8summaWithPrime(& real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! prime vector of volumetric fraction of ice (s-1) real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! prime vector of liquid water matric potential (m s-1) real(rkind) :: scalarAquiferStoragePrime ! prime value of storage of water in the aquifer (m s-1) + ! dummy state variables + real(rkind) :: scalarCanairNrgTrial ! trial value for energy of the canopy air space + real(rkind) :: scalarCanopyNrgTrial ! trial value for energy of the vegetation canopy + real(rkind),dimension(nLayers) :: mLayerNrgTrial ! trial vector of energy of each snow and soil layer + real(rkind) :: scalarCanairNrgPrime ! prime value for energy of the canopy air space + real(rkind) :: scalarCanopyNrgPrime ! prime value for energy of the vegetation canopy + real(rkind),dimension(nLayers) :: mLayerNrgPrime ! prime vector of energy of each snow and soil layer ! other local variables integer(i4b) :: iLayer ! index of model layer in the snow+soil domain integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain @@ -385,7 +392,7 @@ subroutine eval8summaWithPrime(& scalarCanairEnthalpyPrime = scalarCanairNrgPrime scalarCanopyEnthalpyPrime = scalarCanopyNrgPrime mLayerEnthalpyPrime = mLayerNrgPrime - ! do not need these variables + ! do not use these variables scalarCanairTempPrime = realMissing scalarCanopyTempPrime = realMissing mLayerTempPrime = realMissing @@ -396,7 +403,7 @@ subroutine eval8summaWithPrime(& scalarCanairTempPrime = scalarCanairNrgPrime scalarCanopyTempPrime = scalarCanopyNrgPrime mLayerTempPrime = mLayerNrgPrime - ! do not need these variables + ! do not use these variables scalarCanairEnthalpyTrial = realMissing scalarCanopyEnthalpyTrial = realMissing mLayerEnthalpyTrial = realMissing diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 4d0a03461..fc2523dc7 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -626,6 +626,8 @@ subroutine solve_with_IDA end do ! initialize sum of compression of the soil matrix mLayerCmpress_sum(:) = 0._rkind + stateVecNew(:) = 0._rkind + stateVecPrime(:) = 0._rkind !--------------------------- ! * solving F(y,y') = 0 by IDA, y is the state vector and y' is the time derivative vector dy/dt @@ -716,6 +718,7 @@ subroutine solve_with_KINSOL !--------------------------- ! * solving F(y) = 0 from Backward Euler with KINSOL, y is the state vector !--------------------------- + stateVecNew(:) = 0._rkind ! iterations and updates to trial state vector, fluxes, and derivatives are done inside IDA solver call summaSolve4kinsol(& dt_cur, & ! intent(in): data time step @@ -751,7 +754,7 @@ subroutine solve_with_KINSOL ! output ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) sunSucceeds, & ! intent(out): flag to indicate if ida successfully solved the problem in current data step - stateVecNew, & ! intent(out): model state vector (y) at the end of the data time step + stateVecNew, & ! intent(inout): model state vector (y) at the end of the data time step fluxVec, & ! intent(out): new flux vector resSink, & ! intent(out): additional (sink) terms on the RHS of the state equation resVec, & ! intent(out): new residual vector diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index c0d9e7d39..9d95448f7 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -699,6 +699,8 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec real(rkind) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3) real(rkind) :: scalarCanopyEnthTempTrial ! trial value for temperature component of enthalpy of the vegetation canopy (J m-3) real(rkind),dimension(nLayers) :: mLayerEnthTempTrial ! trial vector of temperature component of enthalpy of snow + soil (J m-3) + real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector of enthalpy of each snow and soil layer (J m-3) ! diagnostic variables real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) @@ -722,6 +724,13 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector of volumetric fraction of ice (-) real(rkind),dimension(nLayers) :: mLayerVolFracIceDelta ! delta vector volumetric fraction of ice of snow + soil (-) real(rkind),dimension(nLayers) :: mLayerHDelta ! delta vector of enthalpy of snow+soil (J m-3) + ! dummy state variables + real(rkind) :: scalarCanairNrgTrial ! trial value for energy of the canopy air space + real(rkind) :: scalarCanopyNrgTrial ! trial value for energy of the vegetation canopy + real(rkind),dimension(nLayers) :: mLayerNrgTrial ! trial vector of energy of each snow and soil layer + real(rkind) :: scalarCanairNrgPrime ! prime value for energy of the canopy air space + real(rkind) :: scalarCanopyNrgPrime ! prime value for energy of the vegetation canopy + real(rkind),dimension(nLayers) :: mLayerNrgPrime ! prime vector of energy of each snow and soil layer ! ------------------------------------------------------------------------------------------------------------------- ! ------------------------------------------------------------------------------------------------------------------- @@ -802,54 +811,73 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! ------------------ ! initialize to state variable from the last update - scalarCanairTempTrial = scalarCanairTemp scalarCanairEnthalpyTrial = scalarCanairEnthalpy - scalarCanopyTempTrial = scalarCanopyTemp + scalarCanopyEnthTempTrial = scalarCanopyEnthTemp scalarCanopyWatTrial = scalarCanopyWat scalarCanopyLiqTrial = scalarCanopyLiq scalarCanopyIceTrial = scalarCanopyIce - scalarCanopyEnthTempTrial = scalarCanopyEnthTemp - mLayerTempTrial = mLayerTemp + mLayerEnthTempTrial = mLayerEnthTemp mLayerVolFracWatTrial = mLayerVolFracWat mLayerVolFracLiqTrial = mLayerVolFracLiq mLayerVolFracIceTrial = mLayerVolFracIce mLayerMatricHeadTrial = mLayerMatricHead mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq - mLayerEnthTempTrial = mLayerEnthTemp scalarAquiferStorageTrial = scalarAquiferStorage + if(ixNumericalMethod==ida .and. (ixNrgConserv==enthalpyFD .or. ixNrgConserv==enthalpyFDlu))then ! use state variable as enthalpy + scalarCanairNrgTrial = scalarCanairEnthalpy + scalarCanopyNrgTrial = realMissing ! currently not splitting in ida so no need to update + mLayerNrgTrial = realMissing ! currently not splitting in ida so no need to update + else + scalarCanairNrgTrial = scalarCanairTemp + scalarCanopyNrgTrial = scalarCanopyTemp + mLayerNrgTrial = mLayerTemp + endif + ! extract states from the state vector call varExtract(& ! input - stateVecTrial, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers + stateVecTrial, & ! intent(in): model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanairNrgTrial, & ! intent(inout): trial value of energy of the canopy air space, temperature (K) or enthalpy (J m-3) + scalarCanopyNrgTrial, & ! intent(inout): trial value of energy of the vegetation canopy, temperature (K) or enthalpy (J m-3) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerNrgTrial, & ! intent(inout): trial vector of energy, temperature (K) or enthalpy (J m-3) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(inout): trial value of storage of water in the aquifer (m) + scalarAquiferStorageTrial, & ! intent(inout): trial value of storage of water in the aquifer (m) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + if(ixNumericalMethod==ida .and. (ixNrgConserv==enthalpyFD .or. ixNrgConserv==enthalpyFDlu))then ! use state variable as enthalpy + scalarCanairEnthalpyTrial = scalarCanairNrgTrial + scalarCanopyEnthalpyTrial = scalarCanopyNrgTrial + mLayerEnthalpyTrial = mLayerNrgTrial + else + scalarCanairTempTrial = scalarCanairNrgTrial + scalarCanopyTempTrial = scalarCanopyNrgTrial + mLayerTempTrial = mLayerNrgTrial + ! do not need these variables, not saved out of the subroutine + scalarCanopyEnthalpyTrial = realMissing + mLayerEnthalpyTrial = realMissing + endif - ! initialize to state variable from the last update - ! should all be set to previous values if splits, but for now operator splitting is not hooked up - scalarCanairTempPrime = realMissing - scalarCanopyTempPrime = realMissing + ! Placeholder: if we decide to use splitting, we need to pass all the previous values of the state variables + scalarCanairNrgPrime = realMissing + scalarCanopyNrgPrime = realMissing scalarCanopyWatPrime = realMissing scalarCanopyLiqPrime = realMissing scalarCanopyIcePrime = realMissing - mLayerTempPrime = realMissing + mLayerNrgPrime = realMissing mLayerVolFracWatPrime = realMissing mLayerVolFracLiqPrime = realMissing mLayerVolFracIcePrime = realMissing @@ -871,27 +899,38 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! extract the derivatives from the state vector call varExtract(& ! input - stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers + stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy - scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K) - scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): derivative of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2) + scalarCanairNrgPrime, & ! intent(inout): derivative of energy of the canopy air space, temperature (K s-1) or enthalpy (W m-3) + scalarCanopyNrgPrime, & ! intent(inout): derivative of energy of the vegetation canopy, temperature (K s-1) or enthalpy (W m-3) + scalarCanopyWatPrime, & ! intent(inout): derivative of canopy total water (kg m-2 s-1) + scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (kg m-2 s-1) ! output: variables for the snow-soil domain - mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K) - mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (-) - mLayerMatricHeadPrime, & ! intent(inout): derivative of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): derivative of liquid water matric potential (m) + mLayerNrgPrime, & ! intent(inout): derivative of energy of each snow and soil layer, temperature (K s-1) or enthalpy (W m-3) + mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (-) + mLayerMatricHeadPrime, & ! intent(inout): derivative of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): derivative of liquid water matric potential (m) ! output: variables for the aquifer - scalarAquiferStoragePrime,& ! intent(inout): derivative of storage of water in the aquifer (m) + scalarAquiferStoragePrime, & ! intent(inout): derivative of storage of water in the aquifer (m) ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + if(ixNrgConserv== enthalpyFD .or. ixNrgConserv == enthalpyFDlu)then ! use state variable as enthalpy, need to compute temperature + ! do not use these variables + scalarCanairTempPrime = realMissing + scalarCanopyTempPrime = realMissing + mLayerTempPrime = realMissing + else ! use state variable as temperature + scalarCanairTempPrime = scalarCanairNrgPrime + scalarCanopyTempPrime = scalarCanopyNrgPrime + mLayerTempPrime = mLayerNrgPrime + endif !(choice of how conservation of energy is implemented) + ! update diagnostic variables call updateVarsWithPrime(& ! input From f773be71e95df5759a63bb4bb022952d69dda35a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 22 Mar 2024 12:54:53 +0900 Subject: [PATCH 1233/1472] compiles --- build/source/driver/summa_setup.f90 | 18 ++-------- build/source/engine/computHeatCap.f90 | 22 ++++++------ build/source/engine/computJacobWithPrime.f90 | 6 ++-- build/source/engine/computResidWithPrime.f90 | 14 +++++--- build/source/engine/enthalpyTemp.f90 | 35 +++++++++++--------- build/source/engine/eval8summaWithPrime.f90 | 4 ++- build/source/engine/systemSolv.f90 | 1 + build/source/engine/updateVarsWithPrime.f90 | 11 ++++-- build/source/engine/varSubstep.f90 | 3 ++ 9 files changed, 62 insertions(+), 52 deletions(-) diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index fc40cfac3..f7612b23c 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -80,9 +80,8 @@ subroutine summa_paramSetup(summa1_struc, err, message) USE paramCheck_module,only:paramCheck ! module to check consistency of model parameters USE pOverwrite_module,only:pOverwrite ! module to overwrite default parameter values with info from the Noah tables USE read_param_module,only:read_param ! module to read model parameter sets - USE enthalpyTemp_module,only:T2H_lookup_snow ! module to calculate a look-up table for the snow temperature-enthalpy conversion + USE enthalpyTemp_module,only:T2H_lookup_snWat ! module to calculate a look-up table for the snow temperature-enthalpy conversion USE enthalpyTemp_module,only:T2L_lookup_soil ! module to calculate a look-up table for the soil temperature-enthalpy conversion - USE enthalpyTemp_module,only:T2H_lookup_veg ! module to calculate a look-up table for the vegetation temperature-enthalpy conversion USE var_derive_module,only:fracFuture ! module to calculate the fraction of runoff in future time steps (time delay histogram) USE module_sf_noahmplsm,only:read_mp_veg_parameters ! module to read NOAH vegetation tables ! global data structures @@ -124,7 +123,6 @@ subroutine summa_paramSetup(summa1_struc, err, message) integer(i4b) :: iGRU,iHRU ! looping variables integer(i4b) :: iVar ! looping variables logical :: needLookup_soil ! logical to decide if computing soil enthalpy lookup tables - logical :: needLookup_veg ! logical to decide if computing vegetation enthalpy lookup tables ! --------------------------------------------------------------------------------------- ! associate to elements in the data structure summaVars: associate(& @@ -180,17 +178,9 @@ subroutine summa_paramSetup(summa1_struc, err, message) ! decide if computing soil enthalpy lookup tables and vegetation enthalpy lookup tables needLookup_soil = .false. - needLookup_veg = .false. ! if need enthalpy for energy conservation residual form and not using soil enthalpy hypergeometric function if(model_decisions(iLookDECISIONS%nrgConserv)%iDecision == enthalpyFDlu) needLookup_soil = .true. ! if using IDA and enthalpy as a state variable, need temperature-enthalpy lookup tables for soil and vegetation - if(model_decisions(iLookDECISIONS%num_method)%iDecision == ida) then - if (model_decisions(iLookDECISIONS%nrgConserv)%iDecision == enthalpyFD .or. & - model_decisions(iLookDECISIONS%nrgConserv)%iDecision == enthalpyFDlu) then - needLookup_soil = .true. - needLookup_veg = .true. - endif - endif ! get the maximum number of snow layers select case(model_decisions(iLookDECISIONS%snowLayers)%iDecision) @@ -322,7 +312,7 @@ subroutine summa_paramSetup(summa1_struc, err, message) ! calculate a look-up table for the temperature-enthalpy conversion of snow for future snow layer merging ! NOTE1: might be able to make this more efficient by only doing this for the HRUs that have snow ! NOTE2: H is the mixture enthalpy of snow liquid and ice - call T2H_lookup_snow(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) + call T2H_lookup_snWat(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! calculate a lookup table for the temperature-enthalpy conversion of soil @@ -336,10 +326,6 @@ subroutine summa_paramSetup(summa1_struc, err, message) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif endif - ! calculate a lookup table for the temperature-enthalpy conversion of canopy (vegetation) - call T2E_lookup_veg(mparStruct%gru(iGRU)%hru(iHRU),err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! overwrite the vegetation height HVT(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex)) = mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%heightCanopyTop)%dat(1) HVB(typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex)) = mparStruct%gru(iGRU)%hru(iHRU)%var(iLookPARAM%heightCanopyBottom)%dat(1) diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index fb577c860..8d74b5146 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -56,13 +56,15 @@ module computHeatCap_module USE globalData,only:iname_watAquifer ! named variable defining the water storage in the aquifer ! missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing real - -! named variables that define the layer type -USE globalData,only:iname_snow ! snow -USE globalData,only:iname_soil ! soil +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing real +! domain types +USE globalData,only:iname_cas ! named variables for canopy air space +USE globalData,only:iname_veg ! named variables for vegetation canopy +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil +USE globalData,only:iname_aquifer ! named variables for the aquifer ! privacy implicit none @@ -313,10 +315,10 @@ subroutine computHeatCapAnalytic(& endif case(iname_soil) - mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component - iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component + mLayerHeatCap(iLayer) = iden_soil(ixControlIndex) * Cp_soil * ( 1._rkind - theta_sat(ixControlIndex) ) + & ! soil component + iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( theta_sat(ixControlIndex) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component ! derivatives dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use Tcrit = crit_soilT( mLayerMatricHead(ixControlIndex) ) diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index 82893ed57..e54f62498 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -176,7 +176,7 @@ subroutine computJacobWithPrime(& integer(i4b) :: jLayer ! index of model layer within the full state vector (hydrology) integer(i4b) :: pLayer ! indices of soil layers (used for the baseflow derivatives) ! conversion factors - real(rkind) :: LHfu0 ! latent heat of fusion, modified to be 0 if using enthalpy formulation and not using + real(rkind) :: LH_fu0 ! latent heat of fusion, modified to be 0 if using enthalpy formulation and not using real(rkind) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative ! -------------------------------------------------------------- ! associate variables from data structures @@ -1076,8 +1076,8 @@ subroutine computJacobWithPrime(& if(nrgState==integerMissing) cycle watState = ixSnowSoilHyd(iLayer) if(watstate/=integerMissing)then - if(nLayer<=nSnow) aJac(:,watState) = aJac(:,watState) + aJac(:,nrgState) * dTemp_dTheta(iLayer) - if(nLayer>nSnow) aJac(:,watState) = aJac(:,watState) + aJac(:,nrgState) * dTemp_dPsi0(iLayer) + if(iLayer<=nSnow) aJac(:,watState) = aJac(:,watState) + aJac(:,nrgState) * dTemp_dTheta(iLayer) + if(iLayer>nSnow) aJac(:,watState) = aJac(:,watState) + aJac(:,nrgState) * dTemp_dPsi0(iLayer) endif aJac(:,nrgState) = aJac(:,nrgState) * dTemp_dEnthalpy(iLayer) if(ixMatrix==ixBandMatrix) aJac(ixDiag, nrgState) = aJac(ixDiag, nrgState) + 1._rkind * cj diff --git a/build/source/engine/computResidWithPrime.f90 b/build/source/engine/computResidWithPrime.f90 index 96ad01bce..65afbe7fa 100644 --- a/build/source/engine/computResidWithPrime.f90 +++ b/build/source/engine/computResidWithPrime.f90 @@ -80,6 +80,9 @@ subroutine computResidWithPrime(& ! input: enthalpy terms scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (J kg K-1) mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (J kg K-1) + scalarCanairEnthalpyPrime, & ! intent(in): prime value for the enthalpy of the canopy air space (W m-3) + scalarCanopyEnthalpyPrime, & ! intent(in): prime value for the of enthalpy of the vegetation canopy (W m-3) + mLayerEnthalpyPrime, & ! intent(in): prime vector of the of enthalpy of each snow and soil layer (W m-3) ! input: data structures prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -115,6 +118,9 @@ subroutine computResidWithPrime(& ! input: enthalpy terms real(qp),intent(in) :: scalarCanopyCmTrial ! Cm of vegetation canopy (-) real(qp),intent(in) :: mLayerCmTrial(:) ! Cm of each snow and soil layer (-) + real(rkind),intent(in) :: scalarCanairEnthalpyPrime ! prime value for enthalpy of the canopy air space (W m-3) + real(rkind),intent(in) :: scalarCanopyEnthalpyPrime ! prime value for enthalpy of the vegetation canopy (W m-3) + real(rkind),intent(in) :: mLayerEnthalpyPrime(:) ! prime vector of enthalpy of each snow and soil layer (W m-3) ! input: data structures type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU @@ -256,12 +262,12 @@ subroutine computResidWithPrime(& ! check if(any(isNan(rVec)))then - call printResidDAE(nSnow,nSoil,nLayers,indx_data,rAdd,rVec) - message=trim(message)//'we found NaN' + message=trim(message)//'vector of residuals contains NaN value(s) ' ! formerly known as the Indian bread error + write(*,'(a,1x,100(e12.5,1x))') 'rVec = ', rVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) + write(*,'(a,1x,100(e12.5,1x))') 'fVec = ', fVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) err=20; return endif - - ! end association with the necessary variabiles for the residual calculations + end associate end subroutine computResidWithPrime diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 8c6040415..93d42fa0c 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -59,15 +59,15 @@ module enthalpyTemp_module USE globalData,only:realMissing ! missing real number implicit none -public::T2H_lookup_snow +public::T2H_lookup_snWat public::T2L_lookup_soil public::enthalpy2T_snwWat public::T2enthalpy_snwWat public::enthTemp2enthalpy -public::ethalpy2T_cas -public::ethalpy2T_veg -public::ethalpy2T_snow -public::ethalpy2T_soil +public::enthalpy2T_cas +public::enthalpy2T_veg +public::enthalpy2T_snow +public::enthalpy2T_soil private::hyp_2F1_real ! define the snow look-up table used to compute temperature based on enthalpy @@ -78,10 +78,10 @@ module enthalpyTemp_module ! ************************************************************************************************************************ -! public subroutine T2H_lookup_snow: define a look-up table to mixture enthalpy based on temperature -! appropriate when no dry mass, as in snow +! public subroutine T2H_lookup_snWat: define a look-up table to liquid + ice enthalpy based on temperature +! appropriate when no dry mass, as in snow ! ************************************************************************************************************************ -subroutine T2H_lookup_snow(mpar_data, & ! intent(in): parameter data structure +subroutine T2H_lookup_snWat(mpar_data, & ! intent(in): parameter data structure err,message) ! ------------------------------------------------------------------------------------------------------------------------- ! downwind routines @@ -105,7 +105,7 @@ subroutine T2H_lookup_snow(mpar_data, & ! intent(in): pa integer(i4b) :: ilook ! loop through lookup table ! ------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="T2H_lookup_snow/" + err=0; message="T2H_lookup_snWat/" ! associate associate( snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ) @@ -134,7 +134,7 @@ subroutine T2H_lookup_snow(mpar_data, & ! intent(in): pa end associate - end subroutine T2H_lookup_snow + end subroutine T2H_lookup_snWat ! ************************************************************************************************************************ ! public subroutine T2L_lookup_soil: define a look-up table to compute integral of soil Clapeyron equation liquid water @@ -1021,7 +1021,8 @@ subroutine enthalpy2T_snow(& real(rkind) :: T ! iteration temperature (K) real(rkind) :: H ! iteration enthalpy (J m-3) real(rkind) :: diffT ! temperature difference of iteration temp from Tfreeze - real(rkind) :: fLiq ! iteration fraction liquid water + real(rkind) :: integral ! iteration integral of snow freezing curve + real(rkind) :: fLiq ! iteration fraction liquid real(rkind) :: enthLiq ! iteration enthalpy of the liquid region (J m-3) real(rkind) :: enthIce ! iteration enthalpy of the ice region (J m-3) real(rkind) :: enthAir ! iteration enthalpy of air (J m-3) @@ -1178,6 +1179,7 @@ subroutine enthalpy2T_soil(& character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------------------------------- ! declare local variables + character(len=128) :: cmessage ! error message in downwind routine real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) real(rkind) :: volFracWat ! volumetric fraction of total water, liquid+ice (-) real(rkind) :: diff0 ! temperature difference of Tcrit from Tfreeze @@ -1194,7 +1196,6 @@ subroutine enthalpy2T_soil(& real(rkind) :: T ! iteration temperature (K) real(rkind) :: H ! iteration enthalpy (J m-3) real(rkind) :: diffT ! temperature difference of iteration temp from Tfreeze - real(rkind) :: integral ! iteration integral of snow freezing curve real(rkind) :: fLiq ! iteration fraction liquid water real(rkind) :: integral_frz_upp ! upper limit of iteration integral of frozen soil water content (from Tfreeze to soil temperature) real(rkind) :: arg ! argument of iteration soil hypergeometric function @@ -1213,11 +1214,13 @@ subroutine enthalpy2T_soil(& real(rkind) :: dH_dWat ! derivative of iteration enthalpy with water state variable real(rkind) :: dH_dT__dWat ! derivative of iteration enthalpy with iteration temperature with water state variable real(rkind) :: dfLiq_dT ! derivative of iteration fraction liquid water with iteration temperature + real(rkind) :: dintegral_frz_upp_dT ! derivative of iteration integral of frozen soil water content with iteration temperature real(rkind) :: denthSoil_dT ! derivative of iteration enthalpy of soil with iteration temperature real(rkind) :: denthIce_dT ! derivative of iteration enthalpy of ice with iteration temperature real(rkind) :: denthLiq_dT ! derivative of iteration enthalpy of liquid water with iteration temperature real(rkind) :: denthAir_dT ! derivative of iteration enthalpy of air with temperature real(rkind) :: d2fLiq_dT2 ! second derivative of iteration fraction liquid water with iteration temperature + real(rkind) :: d2integral_frz_upp_dT2 ! second derivative of iteration integral of frozen soil water content with iteration temperature real(rkind) :: d2enthSoil_dT2 ! second derivative of iteration enthalpy of soil with iteration temperature real(rkind) :: d2enthLiq_dT2 ! second derivative of iteration enthalpy of liquid water with iteration temperature real(rkind) :: d2enthIce_dT2 ! second derivative of iteration enthalpy of ice with iteration temperature @@ -1242,7 +1245,7 @@ subroutine enthalpy2T_soil(& ! ***** get temperature if unfrozen soil T = mLayerEnthalpy / ( iden_water * Cp_water * volFracWat + soil_dens_intr * Cp_soil * (1._rkind - theta_sat) + iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) ) + Tfreeze dT_dEnthalpy = 1._rkind / ( iden_water * Cp_water * volFracWat + soil_dens_intr*Cp_soil*(1._rkind - theta_sat) + iden_air*Cp_air*(1._rkind - theta_sat - volFracWat) ) - dT_dWat = -iden_water * Cp_water * dTheta_dWat * mLayerEnthalpy / ( iden_water * Cp_water * volFracWat + soil_dens_intr * Cp_soil * (1._rkind - theta_sat) + iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) )**2_i4b + dT_dWat = -iden_water * Cp_water * dvolFracWat_dPsi0 * mLayerEnthalpy / ( iden_water * Cp_water * volFracWat + soil_dens_intr * Cp_soil * (1._rkind - theta_sat) + iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) )**2_i4b ! ***** iterate to find temperature if ice exists if( T lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup, & ! temperature (K) Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup, & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function @@ -1306,7 +1309,7 @@ subroutine enthalpy2T_soil(& dintegral_frz_upp_dT = dL if(computJac) d2integral_frz_upp_dT2 = 0._rkind - end associate lookVars + end associate lookVars2 else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) @@ -1373,7 +1376,7 @@ subroutine enthalpy2T_soil(& if(computJac)then dTemp_dEnthalpy = dT_dEnthalpy dTemp_dTheta = realMissing ! do not use - dTemp_dPsi = dT_dWat + dTemp_dPsi0 = dT_dWat endif end subroutine enthalpy2T_soil diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 13a6b8e1d..76ce82f28 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -21,6 +21,7 @@ module eval8summaWithPrime_module var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! lookup tables model_options ! defines the model decisions ! indices that define elements of the data structures @@ -188,7 +189,7 @@ subroutine eval8summaWithPrime(& real(rkind) :: scalarCanopyEnthalpyPrime ! prime value for enthalpy of the vegetation canopy (W m-3) real(rkind) :: scalarCanopyLiqPrime ! prime value for liquid water storage in the canopy (kg m-2 s-1) real(rkind) :: scalarCanopyIcePrime ! prime value for mass of ice on the vegetation canopy (kg m-2 s-1) - real(rkind),dimension(nLayers) :: mLayerEnthalpyPrime ! prime vector of enthalpy of each snow and soil layer (W m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpyPrime ! prime vector of enthalpy of each snow and soil layer (W m-3) real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! prime vector of volumetric liquid water content (s-1) real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! prime vector of volumetric fraction of ice (s-1) real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! prime vector of liquid water matric potential (m s-1) @@ -432,6 +433,7 @@ subroutine eval8summaWithPrime(& scalarCanopyEnthalpyTrial, & ! intent(in): trial value for enthalpy of the vegetation canopy (J m-3) mLayerEnthalpyTrial, & ! intent(in): trial vector of enthalpy of each snow+soil layer (J m-3) ! output: variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(inout): trial value of canopy air space temperature (K) scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index fc2523dc7..56dfcf5ae 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -654,6 +654,7 @@ subroutine solve_with_IDA dMat, & ! intent(inout): diagonal of the Jacobian matrix (excludes fluxes) ! input: data structures model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup data type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes mpar_data, & ! intent(in): model parameters diff --git a/build/source/engine/updateVarsWithPrime.f90 b/build/source/engine/updateVarsWithPrime.f90 index c023bcbea..a86ba4547 100644 --- a/build/source/engine/updateVarsWithPrime.f90 +++ b/build/source/engine/updateVarsWithPrime.f90 @@ -22,6 +22,8 @@ module updateVarsWithPrime_module ! data types USE nrtype +USE data_types,only:zLookup ! z(:)%var(:)%lookup(:) + ! missing values USE globalData,only:integerMissing ! missing integer @@ -88,7 +90,10 @@ module updateVarsWithPrime_module USE soil_utilsAddPrime_module,only:liquidHeadPrime ! compute the liquid water matric potential USE soil_utilsAddPrime_module,only:d2Theta_dPsi2 ! second derivative in the soil water characteristic (soil) USE soil_utilsAddPrime_module,only:d2Theta_dTk2 ! second derivative in the freezing curve w.r.t. temperature (soil) -USE enthalpyTemp_module,only:enthalpy2T ! compute temperature from enthalpy and water content +USE enthalpyTemp_module,only:enthalpy2T_cas ! compute canopy air space temperature from enthalpy +USE enthalpyTemp_module,only:enthalpy2T_veg ! compute canopy temperature from enthalpy and water content +USE enthalpyTemp_module,only:enthalpy2T_snow ! compute snow layer temperature from enthalpy and water content +USE enthalpyTemp_module,only:enthalpy2T_soil ! compute soil layer temperature from enthalpy and matric potential ! IEEE checks USE, intrinsic :: ieee_arithmetic ! check values (NaN, etc.) @@ -119,6 +124,7 @@ subroutine updateVarsWithPrime(& scalarCanopyEnthalpyTrial, & ! intent(in): trial value for enthalpy of the vegetation canopy (J m-3) mLayerEnthalpyTrial, & ! intent(in): trial vector of enthalpy of each snow+soil layer (J m-3) ! output: variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(inout): trial value of canopy air space temperature (K) scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) @@ -159,8 +165,9 @@ subroutine updateVarsWithPrime(& ! input: enthalpy state variables real(rkind),intent(in) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3) real(rkind),intent(in) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(in) :: mLayerEnthalpyTrial ! trial vector of enthalpy of each snow+soil layer (J m-3) + real(rkind),intent(in) :: mLayerEnthalpyTrial(:) ! trial vector of enthalpy of each snow+soil layer (J m-3) ! output: variables for the vegetation canopy + real(rkind),intent(inout) :: scalarCanairTempTrial ! trial value of canopy air space temperature (K) real(rkind),intent(inout) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) real(rkind),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) real(rkind),intent(inout) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 9d95448f7..ba045ffe0 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -738,6 +738,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec associate(& ! model decisions ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver + ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& ! intent(in): [i4b] choice of variable in energy conservation backward Euler residual ! get indices for balances ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in) : [i4b] index of canopy air space energy state variable ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in) : [i4b] index of canopy energy state variable @@ -943,11 +944,13 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(inout): model diagnostic variables for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + lookup_data, & ! intent(in): lookup table data structure ! input: enthalpy state variables scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) scalarCanopyEnthalpyTrial, & ! intent(in): trial value for enthalpy of the vegetation canopy (J m-3) mLayerEnthalpyTrial, & ! intent(in): trial vector of enthalpy of each snow+soil layer (J m-3) ! output: variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(inout): trial value of canopy air space temperature (K) scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) From 1c4e10564ffb820c30a3ba50fe378a553beaa7cd Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 22 Mar 2024 13:19:07 +0900 Subject: [PATCH 1234/1472] no hydrology index with cas --- build/source/engine/updateVarsWithPrime.f90 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/build/source/engine/updateVarsWithPrime.f90 b/build/source/engine/updateVarsWithPrime.f90 index a86ba4547..8c2f6fa20 100644 --- a/build/source/engine/updateVarsWithPrime.f90 +++ b/build/source/engine/updateVarsWithPrime.f90 @@ -336,14 +336,18 @@ subroutine updateVarsWithPrime(& ! get the index of the other (energy or mass) state variable within the full state vector select case(ixDomainType) - case(iname_cas) ; ixOther = 0 + case(iname_cas) ; ixOther = integerMissing case(iname_veg) ; ixOther = merge(ixHydCanopy(1), ixNrgCanopy(1), ixStateType(ixFullVector)==iname_nrgCanopy) case(iname_snow, iname_soil); ixOther = merge(ixHydLayer(iLayer),ixNrgLayer(iLayer),ixStateType(ixFullVector)==iname_nrgLayer) case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil'; return end select ! get the index in the local state vector - ixOtherLocal = ixMapFull2Subset(ixOther) ! ixOtherLocal could equal integerMissing + if(ixDomainType==iname_cas)then + ixOtherLocal = integerMissing + else + ixOtherLocal = ixMapFull2Subset(ixOther) ! ixOtherLocal could equal integerMissing + endif if(ixOtherLocal/=integerMissing) computedCoupling(ixOtherLocal)=.true. ! check if we have a coupled solution From fb7118a8e4a63c535e3f8637f43c9e929c1cea21 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 1 Apr 2024 13:28:57 +0900 Subject: [PATCH 1235/1472] just spaces --- build/source/engine/computResid.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/source/engine/computResid.f90 b/build/source/engine/computResid.f90 index d096470f0..855ac9f07 100644 --- a/build/source/engine/computResid.f90 +++ b/build/source/engine/computResid.f90 @@ -288,6 +288,7 @@ subroutine computResid(& ! compute the residual vector for the aquifer if(ixAqWat/=integerMissing) rVec(ixAqWat) = sMul(ixAqWat)*( scalarAquiferStorageTrial - scalarAquiferStorage ) - ( fVec(ixAqWat)*dt + rAdd(ixAqWat) ) + if(globalPrintFlag)then write(*,'(a,1x,100(e12.5,1x))') 'rVec = ', rVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) write(*,'(a,1x,100(e12.5,1x))') 'fVec = ', fVec(min(iJac1,size(rVec)):min(iJac2,size(rVec))) From b8f70ca727e8efd7fc1e0a29c665a905e165487b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 2 Apr 2024 12:21:05 +0900 Subject: [PATCH 1236/1472] make H continuous on other side of freezing --- build/source/engine/enthalpyTemp.f90 | 286 ++++++++++++++++++--------- 1 file changed, 192 insertions(+), 94 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 93d42fa0c..af2edb609 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -500,7 +500,6 @@ subroutine T2enthTemp(& real(rkind) :: enthIce ! enthalpy of the ice region (J m-3) real(rkind) :: enthAir ! enthalpy of air (J m-3) real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) - real(rkind) :: enthWater ! enthalpy of total water (J m-3) logical(lgt),parameter :: doTest=.false. ! flag to run unit test ! -------------------------------------------------------------------------------------------------------------------------------- ! make association with variables in the data structures @@ -610,7 +609,8 @@ subroutine T2enthTemp(& diff0 = Tcrit - Tfreeze ! *** compute enthalpy of water for unfrozen conditions if(mlayerTempTrial(iLayer)>=Tcrit)then - enthWater = iden_water * Cp_water * volFracWat * diffT ! valid for temperatures below freezing also + enthLiq= iden_water * Cp_water * volFracWat * diffT + enthIce= 0._rkind ! *** compute enthalpy of water for frozen conditions else @@ -659,7 +659,6 @@ subroutine T2enthTemp(& enthLiq = iden_water * Cp_water * (integral_unf + integral_frz_upp - integral_frz_low) enthIce = iden_ice * Cp_ice * ( volFracWat * diffT - (integral_unf + integral_frz_upp - integral_frz_low) ) - enthWater = enthLiq + enthIce if(doTest)then @@ -681,7 +680,7 @@ subroutine T2enthTemp(& enthSoil = soil_dens_intr * Cp_soil * ( 1._rkind - theta_sat ) * diffT enthAir = iden_air * Cp_air * ( 1._rkind - theta_sat - volFracWat ) * diffT - mLayerEnthTemp(iLayer) = enthWater + enthSoil + enthAir + mLayerEnthTemp(iLayer) = enthLiq + enthIce + enthSoil + enthAir end associate soilVars @@ -918,34 +917,71 @@ subroutine enthalpy2T_veg(& dT_dEnthalpy = 0._rkind dT_dWat = 0._rkind - do while( abs(scalarCanopyEnthalpy-H)>1.e-6_rkind ) - ! compute iteration enthalpy function, H + do while( abs((H - scalarCanopyEnthalpy)/dH_dT)>1.e-6_rkind ) diffT = T - Tfreeze - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - fLiq = fracLiquid(T, snowfrz_scale) + if(T>=Tfreeze)then + ! compute iteration enthalpy function + fLiq = 1._rkind + enthLiq = Cp_water * scalarCanopyWat * diffT / canopyDepth + enthIce = 0._rkind + + ! compute derivative of iteration with respect to iteration T + dfLiq_dT = 0._rkind + denthLiq_dT = Cp_water * scalarCanopyWat / canopyDepth + denthIce_dT = 0._rkind + else + ! compute iteration enthalpy function + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + fLiq = fracLiquid(T, snowfrz_scale) + enthLiq = Cp_water * scalarCanopyWat * integral / canopyDepth + enthIce = Cp_ice * scalarCanopyWat * ( diffT - integral ) / canopyDepth + + ! compute derivative of iteration with respect to iteration T + ! NOTE: dintegral_dT = fLiq + dfLiq_dT = dFracLiq_dTk(T,snowfrz_scale) + denthLiq_dT = Cp_water * scalarCanopyWat * fLiq / canopyDepth + denthIce_dT = Cp_ice * scalarCanopyWat * (1._rkind - fLiq) / canopyDepth + endif + + ! compute iteration enthalpy function, H enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth - enthLiq = Cp_water * scalarCanopyWat * integral / canopyDepth - enthIce = Cp_ice * scalarCanopyWat * ( diffT - integral ) / canopyDepth H = enthVeg + enthLiq + enthIce - LH_fus * (1._rkind - fLiq) * scalarCanopyWat / canopyDepth ! compute derivative of iteration H with respect to iteration T - ! NOTE: dintegral_dT = fLiq, d2integral_dT2 = dfLiq_dT - dfLiq_dT = dFracLiq_dTk(T,snowfrz_scale) denthVeg_dT = specificHeatVeg * maxMassVegetation / canopyDepth - denthLiq_dT = Cp_water * scalarCanopyWat * fLiq / canopyDepth - denthIce_dT = Cp_ice * scalarCanopyWat * (1._rkind - fLiq) / canopyDepth dH_dT = denthVeg_dT + denthLiq_dT + denthIce_dT + LH_fus * dfLiq_dT * scalarCanopyWat / canopyDepth ! compute change in T and update T = T - (H - scalarCanopyEnthalpy)/dH_dT if(computJac)then + if(T>=Tfreeze)then + ! compute second derivatives + d2fLiq_dT2 = 0._rkind + d2enthLiq_dT2 = 0._rkind + d2enthIce_dT2 = 0._rkind + + ! compute derivative of iteration with respect to canopy water + denthLiq_dWat = Cp_water * diffT / canopyDepth + denthIce_dWat = 0._rkind + denthLiq_dT__dWat = Cp_water / canopyDepth + denthIce_dT__dWat = 0._rkind + else + ! compute second derivatives + ! NOTE: d2integral_dT2 = dfLiq_dT + d2fLiq_dT2 = 2._rkind * snowfrz_scale**2_i4b * ( 3._rkind * diffT**2_i4b - 1._rkind ) / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**3_i4b + d2enthLiq_dT2 = Cp_water * scalarCanopyWat * dfLiq_dT / canopyDepth + d2enthIce_dT2 = -Cp_ice * scalarCanopyWat * dfLiq_dT / canopyDepth + + ! compute derivative of iteration H with respect to canopy water + denthLiq_dWat = Cp_water * integral / canopyDepth + denthIce_dWat = Cp_ice * ( diffT - integral ) / canopyDepth + denthLiq_dT__dWat = Cp_water * fLiq / canopyDepth + denthIce_dT__dWat = Cp_ice * (1._rkind - fLiq) / canopyDepth + endif + ! compute second derivatives - ! NOTE: d2integral_dT2 = dfLiq_dT - d2fLiq_dT2 = 2._rkind * snowfrz_scale**2_i4b * ( 3._rkind * diffT**2_i4b - 1._rkind ) / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**3_i4b d2enthVeg_dT2 = 0._rkind - d2enthLiq_dT2 = Cp_water * scalarCanopyWat * dfLiq_dT / canopyDepth - d2enthIce_dT2 = -Cp_ice * scalarCanopyWat * dfLiq_dT / canopyDepth d2H_dT2 = d2enthVeg_dT2 + d2enthLiq_dT2 + d2enthIce_dT2 + LH_fus * d2fLiq_dT2 * scalarCanopyWat / canopyDepth ! compute derivative of iteration H with respect to canopy enthalpy @@ -954,13 +990,8 @@ subroutine enthalpy2T_veg(& ! compute derivative of iteration H with respect to canopy water denthVeg_dWat = 0._rkind - denthLiq_dWat = Cp_water * integral / canopyDepth - denthIce_dWat = Cp_ice * ( diffT - integral ) / canopyDepth dH_dWat = dH_dT * dT_dWat + denthVeg_dWat + denthLiq_dWat + denthIce_dWat - LH_fus * (1._rkind - fLiq) / canopyDepth - denthVeg_dT__dWat = 0._rkind - denthLiq_dT__dWat = Cp_water * fLiq / canopyDepth - denthIce_dT__dWat = Cp_ice * (1._rkind - fLiq) / canopyDepth dH_dT__dWat = d2H_dT2 * dT_dWat + denthVeg_dT__dWat + denthLiq_dT__dWat + denthIce_dT__dWat + LH_fus * dfLiq_dT / canopyDepth ! update derivatives @@ -1059,50 +1090,82 @@ subroutine enthalpy2T_snow(& dT_dEnthalpy = 0._rkind dT_dWat = 0._rkind - do while( abs(mLayerEnthalpy-H)>1.e-6_rkind ) - ! compute iteration enthalpy function, H + do while( abs((H - mLayerEnthalpy)/dH_dT)>1.e-6_rkind ) diffT = T - Tfreeze - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - fLiq = fracLiquid(T, snowfrz_scale) - enthLiq = iden_water * Cp_water * mLayerVolFracWat * integral - enthIce = iden_water * Cp_ice * mLayerVolFracWat * ( diffT - integral ) + if(T>=Tfreeze)then + ! compute iteration enthalpy function + integral = diffT + fLiq = 1._rkind + enthLiq = iden_water * Cp_water * mLayerVolFracWat * diffT + enthIce = 0._rkind + + ! compute derivative of iteration with respect to iteration T + dfLiq_dT = 0._rkind + denthLiq_dT = iden_water * Cp_water * mLayerVolFracWat + denthIce_dT = 0._rkind + else + ! compute iteration enthalpy function + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + fLiq = fracLiquid(T, snowfrz_scale) + enthLiq = iden_water * Cp_water * mLayerVolFracWat * integral + enthIce = iden_water * Cp_ice * mLayerVolFracWat * ( diffT - integral ) + + ! compute derivative of iteration with respect to iteration T + ! NOTE: dintegral_dT = fLiq + dfLiq_dT = dFracLiq_dTk(T,snowfrz_scale) + denthLiq_dT = iden_water * Cp_water * mLayerVolFracWat * fLiq + denthIce_dT = iden_water * Cp_ice * mLayerVolFracWat * (1._rkind - fLiq) + endif + ! compute iteration enthalpy function, H enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWat * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) - H = enthLiq + enthIce + enthAir - iden_water * LH_fus * (1._rkind - fLiq) * mLayerVolFracWat + H = enthLiq + enthIce + enthAir - iden_ice * LH_fus * (1._rkind - fLiq) * mLayerVolFracWat ! compute derivative of iteration H with respect to iteration T - ! NOTE: dintegral_dT = fLiq - dfLiq_dT = dFracLiq_dTk(T,snowfrz_scale) - denthLiq_dT = iden_water * Cp_water * mLayerVolFracWat * fLiq - denthIce_dT = iden_water * Cp_ice * mLayerVolFracWat * (1._rkind - fLiq) denthAir_dT = iden_air * Cp_air * (1._rkind - mLayerVolFracWat * ( (iden_water/iden_ice)*(1._rkind-fLiq) + fLiq ) ) - dH_dT = denthLiq_dT + denthIce_dT + denthAir_dT + iden_water * LH_fus * dfLiq_dT * mLayerVolFracWat + dH_dT = denthLiq_dT + denthIce_dT + denthAir_dT + iden_ice * LH_fus * dfLiq_dT * mLayerVolFracWat ! compute change in T and update T = T - (H - mLayerEnthalpy)/dH_dT if(computJac)then + if(T>=Tfreeze)then + ! compute second derivatives + d2fLiq_dT2 = 0._rkind + d2enthLiq_dT2 = 0._rkind + d2enthIce_dT2 = 0._rkind + + ! compute derivative of iteration with respect to layer water content + denthLiq_dWat = iden_water * Cp_water * diffT + denthIce_dWat = 0._rkind + denthLiq_dT__dWat = iden_water * Cp_water + denthIce_dT__dWat = 0._rkind + else + ! compute second derivatives + ! NOTE: d2integral_dT2 = dfLiq_dT + d2fLiq_dT2 = 2._rkind * snowfrz_scale**2_i4b * ( 3._rkind * diffT**2_i4b - 1._rkind ) / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**3_i4b + d2enthLiq_dT2 = iden_water * Cp_water * mLayerVolFracWat * dfLiq_dT + d2enthIce_dT2 = -iden_water * Cp_ice * mLayerVolFracWat * dfLiq_dT + + ! compute derivative of iteration with respect to layer water content + denthLiq_dWat = iden_water * Cp_water * integral + denthIce_dWat = iden_water * Cp_ice * ( diffT - integral ) + denthLiq_dT__dWat = iden_water * Cp_water * fLiq + denthIce_dT__dWat = iden_water * Cp_ice * (1._rkind - fLiq) + endif + ! compute second derivatives - ! NOTE: d2integral_dT2 = dfLiq_dT - d2fLiq_dT2 = 2._rkind * snowfrz_scale**2_i4b * ( 3._rkind * diffT**2_i4b - 1._rkind ) / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**3_i4b - d2enthLiq_dT2 = iden_water * Cp_water * mLayerVolFracWat * dfLiq_dT - d2enthIce_dT2 = -iden_water * Cp_ice * mLayerVolFracWat * dfLiq_dT d2enthAir_dT2 = iden_air * Cp_air * ( mLayerVolFracWat * dfLiq_dT *( (iden_water/iden_ice) - 1._rkind ) ) - d2H_dT2 = d2enthLiq_dT2 + d2enthIce_dT2 + d2enthAir_dT2 + iden_water * LH_fus * d2fLiq_dT2 * mLayerVolFracWat + d2H_dT2 = d2enthLiq_dT2 + d2enthIce_dT2 + d2enthAir_dT2 + iden_ice * LH_fus * d2fLiq_dT2 * mLayerVolFracWat ! compute derivative of iteration H with respect to layer enthalpy dH_dEnthalpy = dH_dT * dT_dEnthalpy dH_dT__dEnthalpy = d2H_dT2 * dT_dEnthalpy - ! compute derivative ofiteration H with respect to layer water content - denthLiq_dWat = iden_water * Cp_water * integral - denthIce_dWat = iden_water * Cp_ice * ( diffT - integral ) + ! compute derivative of iteration H with respect to layer water content denthAir_dWat = -iden_air * Cp_air * ( (iden_water/iden_ice)*(diffT-integral) + integral ) - dH_dWat = dH_dT * dT_dWat + denthLiq_dWat + denthIce_dWat + denthAir_dWat - iden_water * LH_fus * (1._rkind - fLiq) - - denthLiq_dT__dWat = iden_water * Cp_water * fLiq - denthIce_dT__dWat = iden_water * Cp_ice * (1._rkind - fLiq) + dH_dWat = dH_dT * dT_dWat + denthLiq_dWat + denthIce_dWat + denthAir_dWat - iden_ice * LH_fus * (1._rkind - fLiq) denthAir_dT__dWat = -iden_air * Cp_air * ( (iden_water/iden_ice)*(1._rkind-fLiq) + fLiq ) - dH_dT__dWat = d2H_dT2 * dT_dWat + denthLiq_dT__dWat + denthIce_dT__dWat + denthAir_dT__dWat + iden_water * LH_fus * dfLiq_dT + dH_dT__dWat = d2H_dT2 * dT_dWat + denthLiq_dT__dWat + denthIce_dT__dWat + denthAir_dT__dWat + iden_ice * LH_fus * dfLiq_dT ! update derivatives dT_dEnthalpy = dT_dEnthalpy - ( dH_dEnthalpy - 1._rkind - dH_dT__dEnthalpy * (H - mLayerEnthalpy)/dH_dT ) / dH_dT @@ -1237,7 +1300,7 @@ subroutine enthalpy2T_soil(& ! initialize error control err=0; message="enthalpy2T_soil/" - Tcrit = crit_soilT( mLayerMatricHead ) + Tcrit = crit_soilT(mLayerMatricHead) volFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) dTcrit_dPsi0 = merge(gravity*Tfreeze/LH_fus,0._rkind,mLayerMatricHead<=0._rkind) dvolFracWat_dPsi0 = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) @@ -1288,61 +1351,101 @@ subroutine enthalpy2T_soil(& dintegral_frz_low_dWat = 0._rkind end if - ! get the upper limit of the integral xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) - do while( abs(mLayerEnthalpy-H)>1.e-6_rkind ) + do while( abs((H - mLayerEnthalpy)/dH_dT)>1.e-6_rkind ) diffT = T - Tfreeze - mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution, DIFFERENT from the liquid water matric potential used in the flux calculations - arg = (vGn_alpha * mLayerPsiLiq)**vGn_n - - if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature - ! make associate to the the lookup table - lookVars2: associate(& - Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup, & ! temperature (K) - Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup, & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) - L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function - ) ! end associate statement - ! get the upper limit of the integral - call splint(Tk,Ly,L2,T,integral_frz_upp,dL,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - dintegral_frz_upp_dT = dL - if(computJac) d2integral_frz_upp_dT2 = 0._rkind - - end associate lookVars2 - - else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature - gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - integral_frz_upp = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) - dintegral_frz_upp_dT = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) ! fLiq - if(computJac) d2integral_frz_upp_dT2 = dTheta_dTk(T,theta_res,theta_sat,vGn_alpha,vGn_n,vGn_m) ! dfLiq_dT - end if + if(T>=Tcrit)then + ! compute iteration enthalpy function + fLiq = volFracWat + enthLiq = iden_water * Cp_water * volFracWat * diffT + enthIce = 0._rkind + + ! compute derivative of iteration with respect to iteration T + dfLiq_dT = 0._rkind + denthLiq_dT = iden_water * Cp_water * volFracWat + denthIce_dT = 0._rkind + else + mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution, DIFFERENT from the liquid water matric potential used in the flux calculations + arg = (vGn_alpha * mLayerPsiLiq)**vGn_n + + ! get the upper limit of the integral + if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature + ! make associate to the the lookup table + lookVars2: associate(& + Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup, & ! temperature (K) + Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup, & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) + L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + ) ! end associate statement + + ! integral of mLayerPsiLiq from Tfreeze to layer temperature + call splint(Tk,Ly,L2,T,integral_frz_upp,dL,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + dintegral_frz_upp_dT = dL + if(computJac) d2integral_frz_upp_dT2 = 0._rkind + + end associate lookVars2 + + else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature + gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + integral_frz_upp = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + dintegral_frz_upp_dT = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) ! fLiq + if(computJac) d2integral_frz_upp_dT2 = dTheta_dTk(T,theta_res,theta_sat,vGn_alpha,vGn_n,vGn_m) ! dfLiq_dT + end if + + ! compute iteration enthalpy function + ! NOTE: here fLiq is the total liquid fraction, not fraction of water fraction that is liquid + fLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + enthLiq = iden_water * Cp_water * (integral_unf + integral_frz_upp - integral_frz_low) + enthIce = iden_ice * Cp_ice * ( volFracWat * diffT - (integral_unf + integral_frz_upp - integral_frz_low) ) + + ! compute derivative of iteration with respect to iteration T + dfLiq_dT = dTheta_dTk(T,theta_res,theta_sat,vGn_alpha,vGn_n,vGn_m) + denthLiq_dT = iden_water * Cp_water * dintegral_frz_upp_dT + denthIce_dT = iden_ice * Cp_ice * ( volFracWat - dintegral_frz_upp_dT ) + endif ! compute iteration enthalpy function, H - ! NOTE: here fLiq is the total liquid fraction, not fraction of water fraction that is liquid - fLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - enthLiq = iden_water * Cp_water * (integral_unf + integral_frz_upp - integral_frz_low) - enthIce = iden_ice * Cp_ice * ( volFracWat * diffT - (integral_unf + integral_frz_upp - integral_frz_low) ) enthSoil = soil_dens_intr * Cp_soil * ( 1._rkind - theta_sat ) * diffT enthAir = iden_air * Cp_air * ( 1._rkind - theta_sat - volFracWat ) * diffT H = enthLiq + enthIce + enthSoil + enthAir - iden_water * LH_fus * (volFracWat - fLiq) ! compute derivative of iteration H with respect to iteration T - dfLiq_dT = dTheta_dTk(T,theta_res,theta_sat,vGn_alpha,vGn_n,vGn_m) - denthLiq_dT = iden_water * Cp_water * dintegral_frz_upp_dT - denthIce_dT = iden_ice * Cp_ice * ( volFracWat - dintegral_frz_upp_dT ) denthSoil_dT = soil_dens_intr * Cp_soil * ( 1._rkind - theta_sat ) denthAir_dT = iden_air * Cp_air * ( 1._rkind - theta_sat - volFracWat ) dH_dT = denthLiq_dT + denthIce_dT + denthSoil_dT + denthAir_dT + iden_water * LH_fus * dfLiq_dT - + ! compute change in T and update + print*,enthLiq, enthIce, enthSoil,enthAir,- iden_water * LH_fus * (volFracWat - fLiq), 'enthLiq, enthIce, enthSoil, enthAir, phaseChange' + print*,T,H,mLayerEnthalpy,dH_dT,-(H - mLayerEnthalpy)/dH_dT,Tcrit,'T, H, mLayerEnthalpy, dH_dT, dT, Tcrit' T = T - (H - mLayerEnthalpy)/dH_dT if(computJac)then + if(T>=Tcrit)then + ! compute second derivatives + d2fLiq_dT2 = 0._rkind + d2enthLiq_dT2 = 0._rkind + d2enthIce_dT2 = 0._rkind + + ! compute derivative of iteration with respect to layer water content + denthLiq_dWat = iden_water * Cp_water * diffT + denthIce_dWat = 0._rkind + denthLiq_dT__dWat = iden_water * Cp_water + denthIce_dT__dWat = 0._rkind + else + ! compute second derivatives + d2fLiq_dT2 = d2Theta_dTk2(T,theta_res,theta_sat,vGn_alpha,vGn_n,vGn_m) + d2enthLiq_dT2 = iden_water * Cp_water * d2integral_frz_upp_dT2 + d2enthIce_dT2 = -iden_ice * Cp_ice * d2integral_frz_upp_dT2 + + ! compute derivative of iteration with respect to layer water content + denthLiq_dWat = iden_water * Cp_water * (dintegral_unf_dWat - dintegral_frz_low_dWat) + denthIce_dWat = iden_ice * Cp_ice * ( dvolFracWat_dPsi0 * diffT - (dintegral_unf_dWat - dintegral_frz_low_dWat) ) + denthLiq_dT__dWat = 0._rkind + denthIce_dT__dWat = iden_ice * Cp_ice * dvolFracWat_dPsi0 + endif + ! compute second derivatives - d2fLiq_dT2 = d2Theta_dTk2(T,theta_res,theta_sat,vGn_alpha,vGn_n,vGn_m) - d2enthLiq_dT2 = iden_water * Cp_water * d2integral_frz_upp_dT2 - d2enthIce_dT2 = -iden_ice * Cp_ice * d2integral_frz_upp_dT2 d2enthSoil_dT2 = 0._rkind d2enthAir_dT2 = 0._rkind d2H_dT2 = d2enthLiq_dT2 + d2enthIce_dT2 + d2enthSoil_dT2 + d2enthAir_dT2 + iden_water * LH_fus * d2fLiq_dT2 @@ -1351,15 +1454,10 @@ subroutine enthalpy2T_soil(& dH_dEnthalpy = dH_dT * dT_dEnthalpy dH_dT__dEnthalpy = d2H_dT2 * dT_dEnthalpy - ! compute derivative ofiteration H with respect to layer water content - denthLiq_dWat = iden_water * Cp_water * (dintegral_unf_dWat - dintegral_frz_low_dWat) - denthIce_dWat = iden_ice * Cp_ice * ( dvolFracWat_dPsi0 * diffT - (dintegral_unf_dWat - dintegral_frz_low_dWat) ) + ! compute derivative of iteration H with respect to layer water content denthSoil_dWat = 0._rkind - denthAir_dWat = -iden_air * Cp_air * dvolFracWat_dPsi0 * diffT - dH_dWat = dH_dT * dT_dWat + denthLiq_dWat + denthIce_dWat + denthAir_dWat - iden_water * LH_fus * dvolFracWat_dPsi0 - - denthLiq_dT__dWat = 0._rkind - denthIce_dT__dWat = iden_ice * Cp_ice * dvolFracWat_dPsi0 + denthAir_dWat = -iden_air * Cp_air * dvolFracWat_dPsi0 * diffT + dH_dWat = dH_dT * dT_dWat + denthLiq_dWat + denthIce_dWat + denthAir_dWat - iden_water * LH_fus * dvolFracWat_dPsi0 denthSoil_dT__dWat = 0._rkind denthAir_dT__dWat = -iden_air * Cp_air * dvolFracWat_dPsi0 dH_dT__dWat = d2H_dT2 * dT_dWat + denthLiq_dT__dWat + denthIce_dT__dWat + denthSoil_dT__dWat + denthAir_dT__dWat From 1cbecec9a7195c71fe21a71b3aedcc6837718132 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 2 Apr 2024 12:21:47 +0900 Subject: [PATCH 1237/1472] remove prints --- build/source/engine/enthalpyTemp.f90 | 2 -- 1 file changed, 2 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index af2edb609..adf2f9b5d 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -1416,8 +1416,6 @@ subroutine enthalpy2T_soil(& dH_dT = denthLiq_dT + denthIce_dT + denthSoil_dT + denthAir_dT + iden_water * LH_fus * dfLiq_dT ! compute change in T and update - print*,enthLiq, enthIce, enthSoil,enthAir,- iden_water * LH_fus * (volFracWat - fLiq), 'enthLiq, enthIce, enthSoil, enthAir, phaseChange' - print*,T,H,mLayerEnthalpy,dH_dT,-(H - mLayerEnthalpy)/dH_dT,Tcrit,'T, H, mLayerEnthalpy, dH_dT, dT, Tcrit' T = T - (H - mLayerEnthalpy)/dH_dT if(computJac)then From c3198e22a6bc255edf8c5df0a11f340cc6d94b1a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 3 Apr 2024 18:04:39 +0900 Subject: [PATCH 1238/1472] smoothed, but does not work still. Note, theta_ice is smoothed but E is now not smooth, maybe should leave E and just smooth theta_ice --- build/source/engine/enthalpyTemp.f90 | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index adf2f9b5d..4bf96ee01 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -1355,16 +1355,18 @@ subroutine enthalpy2T_soil(& do while( abs((H - mLayerEnthalpy)/dH_dT)>1.e-6_rkind ) diffT = T - Tfreeze - if(T>=Tcrit)then + ! NOTE: this should be T>=Tcrit, but we need to smooth the function from Tcrit to Tfreeze for the Newton-Raphson method + ! Newton-Raphson step should not find a solution of T>Tcrit so does not affect the solution + if(T>=Tfreeze)then ! compute iteration enthalpy function - fLiq = volFracWat - enthLiq = iden_water * Cp_water * volFracWat * diffT - enthIce = 0._rkind + fLiq = theta_sat ! should be volFracWat if were not smoothing the function + enthLiq = iden_water * Cp_water * fLiq * diffT + enthIce = iden_ice * Cp_ice * (volFracWat - fLiq) * diffT ! compute derivative of iteration with respect to iteration T dfLiq_dT = 0._rkind - denthLiq_dT = iden_water * Cp_water * volFracWat - denthIce_dT = 0._rkind + denthLiq_dT = iden_water * Cp_water * fLiq + denthIce_dT = iden_ice * Cp_ice * (volFracWat - fLiq) else mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution, DIFFERENT from the liquid water matric potential used in the flux calculations arg = (vGn_alpha * mLayerPsiLiq)**vGn_n @@ -1419,17 +1421,17 @@ subroutine enthalpy2T_soil(& T = T - (H - mLayerEnthalpy)/dH_dT if(computJac)then - if(T>=Tcrit)then + if(T>=Tfreeze)then ! should be T>=Tcrit if were not smoothing the function ! compute second derivatives d2fLiq_dT2 = 0._rkind d2enthLiq_dT2 = 0._rkind d2enthIce_dT2 = 0._rkind ! compute derivative of iteration with respect to layer water content - denthLiq_dWat = iden_water * Cp_water * diffT - denthIce_dWat = 0._rkind - denthLiq_dT__dWat = iden_water * Cp_water - denthIce_dT__dWat = 0._rkind + denthLiq_dWat = 0._rkind ! should be iden_water * Cp_water * diffT if were not smoothing the function + denthIce_dWat = iden_ice * Cp_ice * diffT ! should be 0._rkind if were not smoothing the function + denthLiq_dT__dWat = 0._rkind ! should be iden_water * Cp_water if were not smoothing the function + denthIce_dT__dWat = iden_ice * Cp_ice ! should be 0._rkind if were not smoothing the function else ! compute second derivatives d2fLiq_dT2 = d2Theta_dTk2(T,theta_res,theta_sat,vGn_alpha,vGn_n,vGn_m) From b191659f3549daedb05ed3e297534f79664da521 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 4 Apr 2024 21:35:36 +0900 Subject: [PATCH 1239/1472] fixing the initial state vector assignment, clarifying some subroutine and variable names --- build/source/engine/computJacobWithPrime.f90 | 16 +- build/source/engine/computResid.f90 | 8 +- build/source/engine/computResidWithPrime.f90 | 10 +- build/source/engine/coupled_em.f90 | 124 +++++++++------ build/source/engine/enthalpyTemp.f90 | 52 +++--- build/source/engine/eval8summa.f90 | 8 +- build/source/engine/eval8summaWithPrime.f90 | 8 +- build/source/engine/getVectorz.f90 | 37 +++-- build/source/engine/mDecisions.f90 | 19 +-- build/source/engine/systemSolv.f90 | 125 ++++++++++++--- build/source/engine/updateVarsWithPrime.f90 | 14 +- build/source/engine/varSubstep.f90 | 159 ++++++++++--------- 12 files changed, 366 insertions(+), 214 deletions(-) diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index e54f62498..c73169034 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -76,11 +76,11 @@ module computJacobWithPrime_module moisture, & ! moisture-based form of Richards' equation mixdform ! mixed form of Richards' equation -! look-up values for the choice of heat capacity computation +! look-up values for the choice of variable in energy equations (BE residual or IDA state variable) USE mDecisions_module,only: & - closedForm, & ! heat capacity closed form in backward Euler residual - enthalpyFDlu, & ! enthalpy with lookup tables finite difference in backward Euler residual - enthalpyFD ! enthalpy with hypergeometric function finite difference in backward Euler residual + closedForm, & ! use temperature + enthalpyFDlu, & ! use enthalpy with lookup tables + enthalpyFD ! use enthalpy with analytical solution implicit none ! define constants @@ -109,7 +109,7 @@ subroutine computJacobWithPrime(& specificStorage, & ! intent(in): specific storage coefficient (m-1) theta_sat, & ! intent(in): soil porosity (-) ixRichards, & ! intent(in): choice of option for Richards' equation - useEnthalpy, & ! intent(in): flag if enthalpy is state variable + enthalpyStateVec, & ! intent(in): flag if enthalpy is state variable ! input: data structures model_decisions, & ! intent(in): model decisions indx_data, & ! intent(in): index data @@ -142,7 +142,7 @@ subroutine computJacobWithPrime(& real(rkind),intent(in) :: specificStorage ! specific storage coefficient (m-1) real(rkind),intent(in) :: theta_sat(:) ! soil porosity (-) integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation - logical(lgt),intent(in) :: useEnthalpy ! flag if enthalpy is state variable + logical(lgt),intent(in) :: enthalpyStateVec ! flag if enthalpy is state variable ! input: data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers @@ -353,7 +353,7 @@ subroutine computJacobWithPrime(& end do ! if using enthalpy as a state variable, zero out usual RHS terms and add them end of the iteration loop - if(useEnthalpy)then + if(enthalpyStateVec)then dMat(ixCasNrg) = 0._rkind dMat(ixVegNrg) = 0._rkind do iLayer=1,nLayers @@ -1060,7 +1060,7 @@ subroutine computJacobWithPrime(& ! * if desired, modify to use enthalpy as a state variable instead of temperature ! NOTE, dMat has been set to 0 and now 1._rkind * cj is added instead ! ---------------------------------------- - if(useEnthalpy)then + if(enthalpyStateVec)then aJac(:,ixCasNrg) = aJac(:,ixCasNrg) * dCanairTemp_dEnthalpy if(ixMatrix==ixBandMatrix) aJac(ixDiag, ixCasNrg) = aJac(ixDiag, ixCasNrg) + 1._rkind * cj if(ixMatrix==ixFullMatrix) aJac(ixCasNrg, ixCasNrg) = aJac(ixCasNrg, ixCasNrg) + 1._rkind * cj diff --git a/build/source/engine/computResid.f90 b/build/source/engine/computResid.f90 index 855ac9f07..ecb818c1a 100644 --- a/build/source/engine/computResid.f90 +++ b/build/source/engine/computResid.f90 @@ -80,7 +80,7 @@ subroutine computResid(& nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers - useEnthalpy, & ! intent(in): flag to use enthalpy formulation + enthalpyStateVec, & ! intent(in): flag to use enthalpy formulation ! input: flux vectors sMul, & ! intent(in): state vector multiplier (used in the residual calculations) fVec, & ! intent(in): flux vector @@ -118,7 +118,7 @@ subroutine computResid(& integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain - logical(lgt),intent(in) :: useEnthalpy ! flag to use enthalpy formulation + logical(lgt),intent(in) :: enthalpyStateVec ! flag to use enthalpy formulation ! input: flux vectors real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(rkind),intent(in) :: fVec(:) ! flux vector @@ -247,7 +247,7 @@ subroutine computResid(& ! compute the residual vector for the vegetation canopy ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision ! --> energy balance - if(useEnthalpy)then + if(enthalpyStateVec)then if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = ( scalarCanairEnthalpyTrial - scalarCanairEnthalpy ) - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = ( scalarCanopyEnthTempTrial - scalarCanopyEnthTemp ) - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) else @@ -265,7 +265,7 @@ subroutine computResid(& ! compute the residual vector for the snow and soil sub-domains for energy if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - if(useEnthalpy)then + if(enthalpyStateVec)then rVec( ixSnowSoilNrg(iLayer) ) = ( mLayerEnthTempTrial(iLayer) - mLayerEnthTemp(iLayer) ) - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) else rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) )*( mLayerTempTrial(iLayer) - mLayerTemp(iLayer) ) + mLayerCmTrial(iLayer)*( mLayerVolFracWatTrial(iLayer) - mLayerVolFracWat(iLayer) ) & diff --git a/build/source/engine/computResidWithPrime.f90 b/build/source/engine/computResidWithPrime.f90 index 65afbe7fa..aac4f5e3a 100644 --- a/build/source/engine/computResidWithPrime.f90 +++ b/build/source/engine/computResidWithPrime.f90 @@ -61,7 +61,7 @@ subroutine computResidWithPrime(& nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers - useEnthalpy, & ! intent(in): flag if enthalpy is state variable + enthalpyStateVec, & ! intent(in): flag if enthalpy is state variable ! input: flux vectors sMul, & ! intent(in): state vector multiplier (used in the residual calculations) fVec, & ! intent(in): flux vector @@ -99,7 +99,7 @@ subroutine computResidWithPrime(& integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain - logical(lgt),intent(in) :: useEnthalpy ! flag if enthalpy is state variable + logical(lgt),intent(in) :: enthalpyStateVec ! flag if enthalpy is state variable ! input: flux vectors real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(rkind),intent(in) :: fVec(:) ! flux vector @@ -179,7 +179,7 @@ subroutine computResidWithPrime(& ! add melt freeze terms only if not using enthalpy terms ! NOTE: would need to use these if were using enthTemp terms - if(.not.useEnthalpy)then + if(.not.enthalpyStateVec)then ! compute energy associated with melt freeze for the vegetation canopy if(ixVegNrg/=integerMissing) rAdd(ixVegNrg) = rAdd(ixVegNrg) + LH_fus*scalarCanopyIcePrime/canopyDepth ! energy associated with melt/freeze (J m-3) @@ -214,7 +214,7 @@ subroutine computResidWithPrime(& ! compute the residual vector for the vegetation canopy ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision ! --> energy balance - if(useEnthalpy)then + if(enthalpyStateVec)then if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = scalarCanairEnthalpyPrime - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = scalarCanopyEnthalpyPrime - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) else @@ -231,7 +231,7 @@ subroutine computResidWithPrime(& ! compute the residual vector for the snow and soil sub-domains for energy if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - if(useEnthalpy)then + if(enthalpyStateVec)then rVec( ixSnowSoilNrg(iLayer) ) = mLayerEnthalpyPrime(iLayer) - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) else rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) ) * mLayerTempPrime(iLayer) + mLayerCmTrial(iLayer) * mLayerVolFracWatPrime(iLayer) & diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 34f8ae3a3..12c924e2a 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -88,6 +88,13 @@ module coupled_em_module kinsol ,& ! SUNDIALS backward Euler solution using Kinsol ida ! SUNDIALS solution using IDA +! look-up values for the choice of variable in energy equations (BE residual or IDA state variable) +USE mDecisions_module,only: & + closedForm, & ! use temperature + enthalpyFDlu, & ! use enthalpy with lookup tables + enthalpyFD ! use enthalpy with analytical solution + + ! privacy implicit none private @@ -123,28 +130,28 @@ subroutine coupled_em(& ! error control err,message) ! intent(out): error control ! structure allocations - USE allocspace_module,only:allocLocal ! allocate local data structures - USE allocspace_module,only:resizeData ! clone a data structure + USE allocspace_module,only:allocLocal ! allocate local data structures + USE allocspace_module,only:resizeData ! clone a data structure ! simulation of fluxes and residuals given a trial state vector - USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential + USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential ! preliminary subroutines - USE vegPhenlgy_module,only:vegPhenlgy ! compute vegetation phenology - USE vegNrgFlux_module,only:wettedFrac ! compute wetted fraction of the canopy (used in sw radiation fluxes) - USE snowAlbedo_module,only:snowAlbedo ! compute snow albedo - USE vegSWavRad_module,only:vegSWavRad ! compute canopy sw radiation fluxes - USE canopySnow_module,only:canopySnow ! compute interception and unloading of snow from the vegetation canopy - USE volicePack_module,only:newsnwfall ! compute change in the top snow layer due to throughfall and unloading - USE volicePack_module,only:volicePack ! merge and sub-divide snow layers, if necessary - USE diagn_evar_module,only:diagn_evar ! compute diagnostic energy variables -- thermal conductivity and heat capacity + USE vegPhenlgy_module,only:vegPhenlgy ! compute vegetation phenology + USE vegNrgFlux_module,only:wettedFrac ! compute wetted fraction of the canopy (used in sw radiation fluxes) + USE snowAlbedo_module,only:snowAlbedo ! compute snow albedo + USE vegSWavRad_module,only:vegSWavRad ! compute canopy sw radiation fluxes + USE canopySnow_module,only:canopySnow ! compute interception and unloading of snow from the vegetation canopy + USE volicePack_module,only:newsnwfall ! compute change in the top snow layer due to throughfall and unloading + USE volicePack_module,only:volicePack ! merge and sub-divide snow layers, if necessary + USE diagn_evar_module,only:diagn_evar ! compute diagnostic energy variables -- thermal conductivity and heat capacity ! the model solver - USE indexState_module,only:indexState ! define indices for all model state variables and layers - USE opSplittin_module,only:opSplittin ! solve the system of thermodynamic and hydrology equations for a given substep - USE time_utils_module,only:elapsedSec ! calculate the elapsed time + USE indexState_module,only:indexState ! define indices for all model state variables and layers + USE opSplittin_module,only:opSplittin ! solve the system of thermodynamic and hydrology equations for a given substep + USE time_utils_module,only:elapsedSec ! calculate the elapsed time ! additional subroutines - USE tempAdjust_module,only:tempAdjust ! adjust snow temperature associated with new snowfall - USE var_derive_module,only:calcHeight ! module to calculate height at layer interfaces and layer mid-point - USE computSnowDepth_module,only:computSnowDepth ! compute snow depth - USE enthalpyTemp_module,only:enthTemp2enthalpy ! add phase change terms to delta temperature component of enthalpy + USE tempAdjust_module,only:tempAdjust ! adjust snow temperature associated with new snowfall + USE var_derive_module,only:calcHeight ! module to calculate height at layer interfaces and layer mid-point + USE computSnowDepth_module,only:computSnowDepth ! compute snow depth + USE enthalpyTemp_module,only:enthTemp_or_enthalpy ! add phase change terms to delta temperature component of enthalpy or vice versa implicit none @@ -276,7 +283,7 @@ subroutine coupled_em(& logical(lgt) :: lastInnerStep ! flag to denote if the last time step in maxstep subStep logical(lgt) :: do_outer ! flag to denote if doing the outer steps surrounding the call to opSplittin real(rkind) :: dt_solvInner ! seconds in the maxstep subStep that have been completed - + logical(lgt),parameter :: computNrgBalance_var=.true. ! flag to compute enthalpy, must have computNrgBalance true in varSubStep (will compute enthalpy for BE even if not using enthalpy formulation) ! ---------------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="coupled_em/" @@ -1453,40 +1460,63 @@ subroutine coupled_em(& ! save the total soil water (Liquid+Ice) diag_data%var(iLookDIAG%scalarTotalSoilWat)%dat(1) = balanceSoilWater1 - ! save the enthalpy and total enthalpy + ! save the enthalpy or temperature component of enthalpy, and total enthalpy ! associate local variables with information in the data structures associate(& + ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& ! choice of variable in energy conservation backward Euler residual + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! choice of numerical solver ! canopy enthalpy - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! ice content of the vegetation canopy (kg m-2) - scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! temperature component of enthalpy of the vegetation canopy (K) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! enthalpy of the vegetation canopy (J m-3) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! ice content of the vegetation canopy (kg m-2) + scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! temperature component of enthalpy of the vegetation canopy (K) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! enthalpy of the vegetation canopy (J m-3) ! snow+soil enthalpy - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! volumetric ice content in each snow+soil layer (-) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! depth of each snow+soil layer (m) - mLayerEnthTemp => diag_data%var(iLookDIAG%mLayerEnthTemp)%dat ,& ! temperature component of enthalpy of each snow+soil layer (K) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! enthalpy of each snow+soil layer (J m-3) - scalarTotalSoilEnthalpy => diag_data%var(iLookDIAG%scalarTotalSoilEnthalpy)%dat(1) ,& ! total enthalpy of the soil column (J m-3) - scalarTotalSnowEnthalpy => diag_data%var(iLookDIAG%scalarTotalSnowEnthalpy)%dat(1) & ! total enthalpy of the snow column (J m-3) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! volumetric ice content in each snow+soil layer (-) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! depth of each snow+soil layer (m) + mLayerEnthTemp => diag_data%var(iLookDIAG%mLayerEnthTemp)%dat ,& ! temperature component of enthalpy of each snow+soil layer (K) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! enthalpy of each snow+soil layer (J m-3) + scalarTotalSoilEnthalpy => diag_data%var(iLookDIAG%scalarTotalSoilEnthalpy)%dat(1) ,& ! total enthalpy of the soil column (J m-3) + scalarTotalSnowEnthalpy => diag_data%var(iLookDIAG%scalarTotalSnowEnthalpy)%dat(1) & ! total enthalpy of the snow column (J m-3) ) ! (association of local variables with information in the data structures - ! initialize the enthalpy - scalarCanopyEnthalpy = scalarCanopyEnthTemp - mLayerEnthalpy = mLayerEnthTemp - - ! compute enthalpy for current values - call enthTemp2enthalpy(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): model indices - ! input: ice content change - scalarCanopyIce, & ! intent(in): value for canopy ice content (kg m-2) - mLayerVolFracIce, & ! intent(in): vector of volumetric ice water content (-) - ! input/output: enthalpy - scalarCanopyEnthalpy, & ! intent(inout): value for enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy, & ! intent(inout): vector of enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + if((ixNrgConserv .ne. closedForm .or. computNrgBalance_var) .and. ixNumericalMethod .ne. ida)then ! computeEnthalpy = .true., use enthTemp to conserve energy or compute energy balance + ! initialize the enthalpy + scalarCanopyEnthalpy = scalarCanopyEnthTemp + mLayerEnthalpy = mLayerEnthTemp + ! compute enthalpy for current values + call enthTemp_or_enthalpy(& + ! input: data structures + .true., & ! intent(in): flag to convert enthTemp to enthalpy + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): model indices + ! input: ice content change + scalarCanopyIce, & ! intent(in): value for canopy ice content (kg m-2) + mLayerVolFracIce, & ! intent(in): vector of volumetric ice water content (-) + ! input/output: enthalpy + scalarCanopyEnthalpy, & ! intent(inout): enthTemp to enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy, & ! intent(inout): enthTemp to enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + end if + if(ixNrgConserv .ne. closedForm .and. ixNumericalMethod==ida) then ! enthalpyStateVec = .true., enthalpy as state variable + ! initialize the temperature component of enthalpy + scalarCanopyEnthTemp = scalarCanopyEnthalpy + mLayerEnthTemp = mLayerEnthalpy + call enthTemp_or_enthalpy(& + ! input: data structures + .false., & ! intent(in): flag to convert enthalpy to enthTemp + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): model indices + ! input: ice content change + scalarCanopyIce, & ! intent(in): value for canopy ice content (kg m-2) + mLayerVolFracIce, & ! intent(in): vector of volumetric ice water content (-) + ! input/output: enthalpy + scalarCanopyEnthTemp, & ! intent(inout): enthTemp to enthalpy of the vegetation canopy (J m-3) + mLayerEnthTemp, & ! intent(inout): enthTemp to enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + endif ! save the total soil enthalpy scalarTotalSoilEnthalpy = sum(mLayerEnthalpy(nSnow+1:nLayers) * mLayerDepth(nSnow+1:nLayers))/sum(mLayerDepth(nSnow+1:nLayers)) ! save the total snow enthalpy diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 4bf96ee01..7bc5b4d43 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -63,7 +63,7 @@ module enthalpyTemp_module public::T2L_lookup_soil public::enthalpy2T_snwWat public::T2enthalpy_snwWat -public::enthTemp2enthalpy +public::enthTemp_or_enthalpy public::enthalpy2T_cas public::enthalpy2T_veg public::enthalpy2T_snow @@ -700,18 +700,19 @@ end subroutine T2enthTemp ! ************************************************************************************************************************ -! public subroutine enthTemp2enthalpy: add energy associated with thaw/freeze to temperature component of enthalpy to get total enthalpy, H +! public subroutine enthTemp_or_enthalpy: add energy associated with thaw/freeze to temperature component of enthalpy to get total enthalpy, H, or vice versa ! ************************************************************************************************************************ -subroutine enthTemp2enthalpy(& +subroutine enthTemp_or_enthalpy(& ! input: data structures + do_enthTemp2enthalpy, & ! intent(in): flag if enthalpy is to be computed from temperature component of enthalpy, or vice versa if false diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): model indices ! input: ice content change scalarCanopyIce, & ! intent(in): value of canopy ice content (kg m-2) or prime ice content (kg m-2 s-1) mLayerVolFracIce, & ! intent(in): vector of volumetric fraction of ice (-) or prime volumetric fraction of ice (s-1) ! input/output: enthalpy - scalarCanopyH, & ! intent(inout): enthalpy of the vegetation canopy (J m-3) - mLayerH, & ! intent(inout): enthalpy of each snow+soil layer (J m-3) + scalarCanopyH, & ! intent(inout): enthTemp to enthalpy of the vegetation canopy (J m-3), or vice versa if do_enthTemp2enthalpy false + mLayerH, & ! intent(inout): enthTemp to enthalpy of each snow+soil layer (J m-3), or vice versa if do_enthTemp2enthalpy false ! output: error control err,message) ! intent(out): error control ! ------------------------------------------------------------------------------------------------------------------------- @@ -719,14 +720,15 @@ subroutine enthTemp2enthalpy(& ! delare dummy variables ! ------------------------------------------------------------------------------------------------------------------------- ! input: data structures + logical(lgt),intent(in) :: do_enthTemp2enthalpy ! flag if enthalpy is to be computed from temperature component of enthalpy, or vice versa if false type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_ilength),intent(in) :: indx_data ! model indices ! input: ice content change real(rkind),intent(in) :: scalarCanopyIce ! value for canopy ice content (kg m-2) or prime ice content (kg m-2 s-1) real(rkind),intent(in) :: mLayerVolFracIce(:) ! vector of volumetric fraction of ice (-) or prime volumetric fraction of ice (s-1) ! input output: enthalpy - real(rkind),intent(inout) :: scalarCanopyH ! value for enthalpy of the vegetation canopy (J m-3 s-1) - real(rkind),intent(inout) :: mLayerH(:) ! vector of enthalpy of each snow+soil layer (J m-3 s-1) + real(rkind),intent(inout) :: scalarCanopyH ! enthTemp to enthalpy of the vegetation canopy (J m-3), or vice versa if do_enthTemp2enthalpy false + real(rkind),intent(inout) :: mLayerH(:) ! enthTemp to enthalpy of each snow+soil layer (J m-3), or vice versa if do_enthTemp2enthalpy false ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -755,7 +757,7 @@ subroutine enthTemp2enthalpy(& ! ----------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="enthTemp2enthalpy/" + err=0; message="enthTemp_or_enthalpy/" ! loop through model state variables do iState=1,size(ixMapSubset2Full) @@ -776,13 +778,25 @@ subroutine enthTemp2enthalpy(& select case(ixDomainType) case(iname_cas); cycle ! canopy air space: do nothing (no water stored in canopy air space) case(iname_veg) - scalarCanopyH= scalarCanopyH - LH_fus * scalarCanopyIce/ canopyDepth + if (do_enthTemp2enthalpy)then + scalarCanopyH = scalarCanopyH - LH_fus * scalarCanopyIce/ canopyDepth + else + scalarCanopyH = scalarCanopyH + LH_fus * scalarCanopyIce/ canopyDepth + end if case(iname_snow) iLayer = ixControlIndex - mLayerH(iLayer) = mLayerH(iLayer) - iden_ice * LH_fus * mLayerVolFracIce(iLayer) + if (do_enthTemp2enthalpy)then + mLayerH(iLayer) = mLayerH(iLayer) - iden_ice * LH_fus * mLayerVolFracIce(iLayer) + else + mLayerH(iLayer) = mLayerH(iLayer) + iden_ice * LH_fus * mLayerVolFracIce(iLayer) + end if case(iname_soil) iLayer = ixControlIndex + nSnow - mLayerH(iLayer) = mLayerH(iLayer) - iden_water * LH_fus * mLayerVolFracIce(iLayer) + if (do_enthTemp2enthalpy)then + mLayerH(iLayer) = mLayerH(iLayer) - iden_water * LH_fus * mLayerVolFracIce(iLayer) + else + mLayerH(iLayer) = mLayerH(iLayer) + iden_water * LH_fus * mLayerVolFracIce(iLayer) + end if case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return end select @@ -792,7 +806,7 @@ subroutine enthTemp2enthalpy(& end associate -end subroutine enthTemp2enthalpy +end subroutine enthTemp_or_enthalpy ! ************************************************************************************************************************ ! public subroutine enthalpy2T_cas: compute temperature from enthalpy, canopy air space @@ -912,7 +926,7 @@ subroutine enthalpy2T_veg(& ! ***** iterate to find temperature if ice exists if( T=Tfreeze)then ! compute iteration enthalpy function fLiq = theta_sat ! should be volFracWat if were not smoothing the function - enthLiq = iden_water * Cp_water * fLiq * diffT - enthIce = iden_ice * Cp_ice * (volFracWat - fLiq) * diffT + enthLiq = iden_water * Cp_water * (fLiq * diffT + integral_unf - integral_frz_low) ! should be iden_water*Cp_water*fLiq*diffT if were not smoothing the function + enthIce = iden_ice * Cp_ice * ( (volFracWat - fLiq) * diffT - (integral_unf - integral_frz_low) ) ! should be iden_ice*Cp_ice*(volFracWat - fLiq)*diffT if were not smoothing the function ! compute derivative of iteration with respect to iteration T dfLiq_dT = 0._rkind @@ -1428,8 +1442,8 @@ subroutine enthalpy2T_soil(& d2enthIce_dT2 = 0._rkind ! compute derivative of iteration with respect to layer water content - denthLiq_dWat = 0._rkind ! should be iden_water * Cp_water * diffT if were not smoothing the function - denthIce_dWat = iden_ice * Cp_ice * diffT ! should be 0._rkind if were not smoothing the function + denthLiq_dWat = iden_water * Cp_water * (dintegral_unf_dWat - dintegral_frz_low_dWat) ! should be iden_water * Cp_water * diffT if were not smoothing the function + denthIce_dWat = iden_ice * Cp_ice * ( diffT - (dintegral_unf_dWat - dintegral_frz_low_dWat) ) ! should be 0._rkind if were not smoothing the function denthLiq_dT__dWat = 0._rkind ! should be iden_water * Cp_water if were not smoothing the function denthIce_dT__dWat = iden_ice * Cp_ice ! should be 0._rkind if were not smoothing the function else diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index cf60a37b7..f63329e11 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -60,11 +60,11 @@ module eval8summa_module USE var_lookup,only:iLookFLUX ! named variables for structure elements USE var_lookup,only:iLookDERIV ! named variables for structure elements -! look-up values for the choice of heat capacity computation +! look-up values for the choice of variable in energy equations (BE residual or IDA state variable) USE mDecisions_module,only: & - closedForm, & ! heat capacity closed form in backward Euler residual - enthalpyFDlu, & ! enthalpy with lookup tables finite difference in backward Euler residual - enthalpyFD ! enthalpy with hypergeometric function finite difference in backward Euler residual + closedForm, & ! use temperature + enthalpyFDlu, & ! use enthalpy with lookup tables + enthalpyFD ! use enthalpy with analytical solution ! look-up values for the numerical method USE mDecisions_module,only: & diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 76ce82f28..a94176e8a 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -33,11 +33,11 @@ module eval8summaWithPrime_module USE var_lookup,only:iLookFLUX ! named variables for structure elements USE var_lookup,only:iLookDERIV ! named variables for structure elements -! look-up values for the choice of heat capacity computation +! look-up values for the choice of variable in energy equations (BE residual or IDA state variable) USE mDecisions_module,only: & - closedForm, & ! heat capacity closed form in backward Euler residual - enthalpyFDlu, & ! enthalpy with lookup tables finite difference in backward Euler residual - enthalpyFD ! enthalpy with hypergeometric function finite difference in backward Euler residual + closedForm, & ! use temperature + enthalpyFDlu, & ! use enthalpy with lookup tables + enthalpyFD ! use enthalpy with analytical solution implicit none private diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index 5365abc9c..e7ea46f74 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -102,21 +102,23 @@ module getVectorz_module ! ********************************************************************************************************** subroutine popStateVec(& ! input: data structures - nState, & ! intent(in): number of desired state variables - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers + nState, & ! intent(in): number of desired state variables + enthalpyStateVec, & ! intent(in): flag if enthalpy is state variable + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers ! output - stateVec, & ! intent(out): model state vector - err,message) ! intent(out): error control + stateVec, & ! intent(out): model state vector + err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- ! input: data structures integer(i4b),intent(in) :: nState ! number of desired state variables + logical(lgt),intent(in) :: enthalpyStateVec ! flag if enthalpy is state variable type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers ! output - real(rkind),intent(out) :: stateVec(:) ! model state vector (mixed units) + real(rkind),intent(out) :: stateVec(:) ! model state vector (mixed units) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- @@ -133,11 +135,14 @@ subroutine popStateVec(& fixedLength: associate(& ! model states for the vegetation canopy scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in) : [dp] temperature of the canopy air space (K) + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(in) : [dp] enthalpy of the canopy air space (J m-3) scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in) : [dp] temperature of the vegetation canopy (K) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(in) : [dp] enthalpy of the vegetation canopy (J m-3) scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in) : [dp] mass of total water on the vegetation canopy (kg m-2) scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in) : [dp] mass of liquid water on the vegetation canopy (kg m-2) ! model state variable vectors for the snow-soil layers mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in) : [dp(:)] temperature of each snow/soil layer (K) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(in) : [dp(:)] enthalpy of each snow+soil layer (J m-3) mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in) : [dp(:)] volumetric fraction of total water (-) mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in) : [dp(:)] volumetric fraction of liquid water (-) mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in) : [dp(:)] matric head (m) @@ -176,14 +181,22 @@ subroutine popStateVec(& ! build the state vector for the temperature of the canopy air space ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer canopy do concurrent (iState=1:size(ixCasNrg),ixCasNrg(iState)/=integerMissing) - stateVec( ixCasNrg(iState) ) = scalarCanairTemp ! transfer canopy air temperature to the state vector + if(enthalpyStateVec)then + stateVec( ixCasNrg(iState) ) = scalarCanairEnthalpy ! transfer canopy air enthalpy to the state vector + else + stateVec( ixCasNrg(iState) ) = scalarCanairTemp ! transfer canopy air temperature to the state vector + endif stateFlag( ixCasNrg(iState) ) = .true. ! flag to denote that the state is populated end do ! build the state vector for the temperature of the vegetation canopy ! NOTE: currently vector length=1, and use "do concurrent" to generalize to a multi-layer canopy do concurrent (iState=1:size(ixVegNrg),ixVegNrg(iState)/=integerMissing) - stateVec( ixVegNrg(iState) ) = scalarCanopyTemp ! transfer vegetation temperature to the state vector + if(enthalpyStateVec)then + stateVec( ixVegNrg(iState) ) = scalarCanopyEnthalpy ! transfer vegetation enthalpy to the state vector + else + stateVec( ixVegNrg(iState) ) = scalarCanopyTemp ! transfer vegetation temperature to the state vector + endif stateFlag( ixVegNrg(iState) ) = .true. ! flag to denote that the state is populated end do @@ -202,7 +215,11 @@ subroutine popStateVec(& if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) ixStateSubset = ixSnowSoilNrg(iLayer) ! index within the state vector - stateVec(ixStateSubset) = mLayerTemp(iLayer) ! transfer temperature from a layer to the state vector + if(enthalpyStateVec)then + stateVec(ixStateSubset) = mLayerEnthalpy(iLayer) ! transfer enthalpy from a layer to the state vector + else + stateVec(ixStateSubset) = mLayerTemp(iLayer) ! transfer temperature from a layer to the state vector + endif stateFlag(ixStateSubset) = .true. ! flag to denote that the state is populated end do ! looping through non-missing energy state variables in the snow+soil domain endif diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index 0c2eef690..cc90c10c2 100644 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -147,10 +147,10 @@ module mDecisions_module ! look-up values for the choice of snow unloading from the canopy integer(i4b),parameter,public :: meltDripUnload = 321 ! Hedstrom and Pomeroy (1998), Storck et al 2002 (snowUnloadingCoeff & ratioDrip2Unloading) integer(i4b),parameter,public :: windUnload = 322 ! Roesch et al 2001, formulate unloading based on wind and temperature -! look-up values for the choice of energy conservation residual -integer(i4b),parameter,public :: enthalpyFD = 323 ! enthalpy with hypergeometric function finite difference in backward Euler residual -integer(i4b),parameter,public :: closedForm = 324 ! heat capacity closed form in backward Euler residual -integer(i4b),parameter,public :: enthalpyFDlu = 325 ! enthalpy with lookup tables finite difference in backward Euler residual +! look-up values for the choice of variable in energy equations (BE residual or IDA state variable) +integer(i4b),parameter,public :: enthalpyFD = 323 ! use enthalpy with analytical solution +integer(i4b),parameter,public :: closedForm = 324 ! use temperature +integer(i4b),parameter,public :: enthalpyFDlu = 325 ! use enthalpy with lookup tables ! ----------------------------------------------------------------------------------------------------------- contains @@ -412,12 +412,13 @@ subroutine mDecisions(err,message) endif #endif - ! choice of variable in energy conservation backward Euler residual, choice enthalpyFD has changes the residual computation and has better coincidence of energy conservation for backward Euler solution - ! NOTE: choice does not change the residual for the IDA solution, as they are equivalent + ! choice of variable in either energy backward Euler residual (only, mixed form of equation) or IDA state variable + ! for backward Euler solution, enthalpyFD has better coincidence of energy conservation + ! in IDA solution, enthalpyFD makes the state variables to be enthalpy and the residual is computed in enthalpy space select case(trim(model_decisions(iLookDECISIONS%nrgConserv)%cDecision)) - case('closedForm' ); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = closedForm ! heat capacity closed form in backward Euler residual - case('enthalpyFD' ); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = enthalpyFD ! enthalpy with hypergeometric function finite difference in backward Euler residual - case('enthalpyFDlu'); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = enthalpyFDlu ! enthalpy with lookup tables finite difference in backward Euler residual + case('closedForm' ); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = closedForm ! use temperature + case('enthalpyFD' ); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = enthalpyFD ! use enthalpy with analytical solution + case('enthalpyFDlu'); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = enthalpyFDlu ! use enthalpy with lookup tables case default if (trim(model_decisions(iLookDECISIONS%num_method)%cDecision)=='itertive')then model_decisions(iLookDECISIONS%nrgConserv)%iDecision = closedForm ! included for backwards compatibility diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 56dfcf5ae..4a7127e76 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -79,11 +79,11 @@ module systemSolv_module io_type_summaSolve4numrec, & ! class for summaSolve4numrec arguments out_type_summaSolve4numrec ! class for summaSolve4numrec arguments -! look-up values for the choice of heat capacity computation +! look-up values for the choice of variable in energy equations (BE residual or IDA state variable) USE mDecisions_module,only: & - closedForm, & ! heat capacity closed form in backward Euler residual - enthalpyFDlu, & ! enthalpy with lookup tables finite difference in backward Euler residual - enthalpyFD ! enthalpy with hypergeometric function finite difference in backward Euler residual + closedForm, & ! use temperature + enthalpyFDlu, & ! use enthalpy with lookup tables + enthalpyFD ! use enthalpy with analytical solution ! look-up values for the choice of groundwater representation (local-column, or single-basin) USE mDecisions_module,only:& @@ -121,7 +121,7 @@ subroutine systemSolv(& nLayers, & ! intent(in): total number of layers firstSubStep, & ! intent(in): flag to denote first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution computMassBalance, & ! intent(in): flag to compute mass balance @@ -183,7 +183,7 @@ subroutine systemSolv(& integer(i4b),intent(in) :: nLayers ! total number of layers logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call - logical(lgt),intent(inout) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt),intent(in) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution logical(lgt),intent(in) :: computMassBalance ! flag to compute mass balance @@ -251,6 +251,13 @@ subroutine systemSolv(& real(rkind) :: rtol(nState) ! relative tolerance ida type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt_cur real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix + ! ida variables outputted if use eval8summaWithPrime (not used here, just inside ida solver) + logical(lgt) :: firstSplitOper0 ! flag to indicate if we are processing the first flux call in a splitting operation, changed inside eval8summaWithPrime + real(rkind) :: scalarCanopyTempPrime ! prime value for temperature of the vegetation canopy (K s-1) + real(rkind) :: scalarCanopyWatPrime ! prime value for total water content of the vegetation canopy (kg m-2 s-1) + real(rkind) :: mLayerTempPrime(nLayers) ! prime vector of temperature of each snow and soil layer (K s-1) + real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! prime vector of matric head of each snow and soil layer (m s-1) + real(rkind) :: mLayerVolFracWatPrime(nLayers)! prime vector of volumetric total water content of each snow and soil layer (s-1) ! kinsol and numrec variables real(rkind) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) real(rkind) :: xScale(nState) ! characteristic scale of the state vector (mixed units) @@ -363,8 +370,10 @@ subroutine allocate_memory ! allocate space for mLayerCmpress_sum at the start of the time step if (ixNumericalMethod==ida) then allocate( mLayerCmpress_sum(nSoil) ) + allocate( mLayerMatricHeadPrime(nSoil) ) else - allocate( mLayerCmpress_sum(0) ) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix + allocate( mLayerCmpress_sum(0) ) ! allocate zero-length dimensions to avoid passing around an unallocated matrix + allocate( mLayerMatricHeadPrime(0) ) ! allocate zero-length dimensions to avoid passing around an unallocated matrix end if ! allocate space for the baseflow derivatives @@ -372,7 +381,7 @@ subroutine allocate_memory if (ixGroundwater==qbaseTopmodel) then allocate(dBaseflow_dMatric(nSoil,nSoil),stat=err) ! baseflow depends on total storage in the soil column, hence on matric head in every soil layer else - allocate(dBaseflow_dMatric(0,0),stat=err) ! allocate zero-length dimnensions to avoid passing around an unallocated matrix + allocate(dBaseflow_dMatric(0,0),stat=err) ! allocate zero-length dimensions to avoid passing around an unallocated matrix end if if (err/=0) then; err=20; message=trim(message)//'unable to allocate space for the baseflow derivatives'; return_flag=.true.; return; end if end associate @@ -392,10 +401,14 @@ subroutine initial_function_evaluations if ((ixNrgConserv.ne.closedForm .or. computNrgBalance) .and. ixNumericalMethod.ne.ida) then call enthalpy_function_evaluations; if (return_flag) return end if - end associate ! compute the initial flux and the residual vector, also gets values needed for the Jacobian matrix - call initial_flux_and_residual_vectors; if (return_flag) return + if (ixNrgConserv.ne.closedForm .and. ixNumericalMethod==ida) then + call initial_flux_and_residual_vectors_prime; if (return_flag) return ! if we add enthalpy state variable option to non-ida, we do not need this anymore + else + call initial_flux_and_residual_vectors; if (return_flag) return + end if + end associate if (.not.feasible) then; message=trim(message)//'state vector not feasible'; err=20; return_flag=.true.; return; end if @@ -521,12 +534,81 @@ subroutine initial_flux_and_residual_vectors if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors end subroutine initial_flux_and_residual_vectors + subroutine initial_flux_and_residual_vectors_prime +#ifdef SUNDIALS_ACTIVE + ! ** Compute initial flux and residual vectors ** + ! Note: Need this extra subroutine to handle the case of enthalpy as a state variable, currently only implemented in the prime version + ! If we implement it in the regular version, we can remove this subroutine + associate(& + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1),& ! intent(out): [dp] temperature of the vegetation canopy (K) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(out): [dp(:)] temperature of each snow/soil layer (K) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat & ! intent(out): [dp(:)] matric head (m) + &) + stateVecPrime(:) = 0._rkind ! prime initial values are 0 + firstSplitOper0 = firstSplitOper ! set the flag for the first split operation, do not want to reset it here + call eval8summaWithPrime(& + ! input: model control + dt, & ! intent(in): length of the entire time step (seconds) for drainage pond rate + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + nState, & ! intent(in): total number of state variables in the current subset + .false., & ! intent(in): not inside Sundials solver + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper0, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVecTrial, & ! intent(in): model state vector + stateVecPrime, & ! intent(in): derivative of model state vector + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup table data structure + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data stuctures + indx_data, & ! intent(inout): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_init, & ! intent(inout): model fluxes for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output: new values of variables needed in data window outside of internal IDA, for root finding + scalarCanopyTemp, & ! intent(out): value for temperature of the vegetation canopy (K) + mLayerTemp, & ! intent(out): vector of layer temperature (K) + mLayerMatricHead, & ! intent(out): value for total water matric potential (m) + ! output: new prime values of variables needed in data window outside of internal IDA for Jacobian + scalarCanopyTempPrime, & ! intent(out): prime value for temperature of the vegetation canopy (K s-1) + scalarCanopyWatPrime, & ! intent(out): prime value for total water content of the vegetation canopy (kg m-2 s-1) + mLayerTempPrime, & ! intent(out): prime vector of temperature of each snow and soil layer (K s-1) + mLayerMatricHeadPrime, & ! intent(out): prime vector of matric head of each snow and soil layer (m s-1) + mLayerVolFracWatPrime, & ! intent(out): prime vector of volumetric total water content of each snow and soil layer (s-1) + ! input-output: baseflow + ixSaturation, & ! intent(inout): index of the lowest saturated layer + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + ! output: flux and residual vectors + feasible, & ! intent(out): flag to denote the feasibility of the solution + fluxVec0, & ! intent(out): flux vector + rAdd, & ! intent(out): sink terms on the RHS of the flux equation + resVec, & ! intent(out): residual vector + err,cmessage) ! intent(out): error control + end associate + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors +#endif + end subroutine initial_flux_and_residual_vectors_prime + subroutine Newton_step ! ** Compute the Newton step using numerical recipes ** associate(& ! layer geometry - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) & ! intent(in): [i4b] number of soil layers + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1),& ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) & ! intent(in): [i4b] number of soil layers ) call in_SS4NR % initialize(dt_cur,dt,iter,nSnow,nSoil,nLayers,nLeadDim,nState,ixMatrix,firstSubStep,computeVegFlux,scalarSolution,fOld) call io_SS4NR % initialize(firstFluxCall,xMin,xMax,ixSaturation) @@ -566,7 +648,7 @@ subroutine enforce_mass_conservation ! Post processing step to “perfectly” conserve mass by pushing the errors into the state variables ! NOTE: if the residual is large this will cause the state variables to be pushed outside of their bounds layerVars: associate(& - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers ! vector of energy and hydrology indices for the snow and soil domains ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain @@ -600,15 +682,15 @@ subroutine solve_with_IDA ! get tolerance vectors call popTol4ida(& ! input - nState, & ! intent(in): number of desired state variables - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - mpar_data, & ! intent(in): model parameters + nState, & ! intent(in): number of desired state variables + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + mpar_data, & ! intent(in): model parameters ! output - atol, & ! intent(out): absolute tolerances vector (mixed units) - rtol, & ! intent(out): relative tolerances vector (mixed units) - err,cmessage) ! intent(out): error control + atol, & ! intent(out): absolute tolerances vector (mixed units) + rtol, & ! intent(out): relative tolerances vector (mixed units) + err,cmessage) ! intent(out): error control if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors layerGeometry: associate(& @@ -809,6 +891,7 @@ subroutine finalize_systemSolv ! free memory deallocate(mLayerCmpress_sum) + deallocate(mLayerMatricHeadPrime) deallocate(dBaseflow_dMatric) end subroutine finalize_systemSolv diff --git a/build/source/engine/updateVarsWithPrime.f90 b/build/source/engine/updateVarsWithPrime.f90 index 8c2f6fa20..c82873c9f 100644 --- a/build/source/engine/updateVarsWithPrime.f90 +++ b/build/source/engine/updateVarsWithPrime.f90 @@ -109,7 +109,7 @@ module updateVarsWithPrime_module ! ********************************************************************************************************** subroutine updateVarsWithPrime(& ! input - updateT, & ! intent(in): flag if need to update temperature from enthalpy + enthalpyStateVec, & ! intent(in): flag if enthalpy is the state variable use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy computJac, & ! intent(in): flag if computing for Jacobian update do_adjustTemp, & ! intent(in): flag to adjust temperature to account for the energy used in melt+freeze @@ -152,7 +152,7 @@ subroutine updateVarsWithPrime(& ! -------------------------------------------------------------------------------------------------------------------------------- implicit none ! input - logical(lgt) ,intent(in) :: updateT ! flag if need to update temperature from enthalpy + logical(lgt) ,intent(in) :: enthalpyStateVec ! flag if enthalpy is the state variable logical(lgt) ,intent(in) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function logical(lgt) ,intent(in) :: computJac ! flag if computing for Jacobian update logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze @@ -414,9 +414,9 @@ subroutine updateVarsWithPrime(& endif ! if hydrology state variable or uncoupled solution ! compute temperature from enthalpy and water content - ! NOTE: always do no matter coupling state if enthalpy is the state variable (updateT==.true.) + ! NOTE: always do no matter coupling state if enthalpy is the state variable if(ixDomainType==iname_cas)then - if(updateT)then + if(enthalpyStateVec)then call enthalpy2T_cas(& computJac, & ! intent(in): flag if computing for Jacobian update scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) @@ -429,7 +429,7 @@ subroutine updateVarsWithPrime(& endif cycle ! no more to do on canopy air space elseif(ixDomainType==iname_veg)then - if(updateT)then + if(enthalpyStateVec)then call enthalpy2T_veg(& computJac, & ! intent(in): flag if computing for Jacobian update canopyDepth, & ! intent(in): canopy depth (m) @@ -448,7 +448,7 @@ subroutine updateVarsWithPrime(& dCanopyTemp_dCanWat = 1._rkind endif elseif(ixDomainType==iname_snow)then - if(updateT)then + if(enthalpyStateVec)then call enthalpy2T_snow(& computJac, & ! intent(in): flag if computing for Jacobian update snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) @@ -464,7 +464,7 @@ subroutine updateVarsWithPrime(& dTemp_dTheta(iLayer) = 1._rkind endif elseif(ixDomainType==iname_soil)then - if(updateT)then + if(enthalpyStateVec)then call enthalpy2T_soil(& computJac, & ! intent(in): flag if computing for Jacobian update use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index ba045ffe0..595e9f638 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -79,11 +79,11 @@ module varSubstep_module kinsol ,& ! SUNDIALS backward Euler solution using Kinsol ida ! SUNDIALS solution using IDA -! look-up values for the choice of heat capacity computation +! look-up values for the choice of variable in energy equations (BE residual or IDA state variable) USE mDecisions_module,only: & - closedForm, & ! heat capacity closed form in backward Euler residual - enthalpyFDlu, & ! enthalpy with lookup tables finite difference in backward Euler residual - enthalpyFD ! enthalpy with hypergeometric function finite difference in backward Euler residual + closedForm, & ! use temperature + enthalpyFDlu, & ! use enthalpy with lookup tables + enthalpyFD ! use enthalpy with analytical solution ! safety: set private unless specified otherwise implicit none @@ -199,7 +199,8 @@ subroutine varSubstep(& logical(lgt),parameter :: computMassBalance = .true. ! flag to compute the mass balance, will affect step length, default true logical(lgt),parameter :: computNrgBalance = .true. ! flag to compute the energy balance, will not effect solution but will not compute nrg balance if false (saves expense) logical(lgt) :: computeEnthalpy ! flag to compute enthalpy regardless of the model decision - logical(lgt) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function + logical(lgt) :: enthalpyStateVec ! flag if enthalpy is a state variable (ida) + logical(lgt) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use analytical solution ! --------------------------------------------------------------------------------------- ! initialize error control @@ -276,10 +277,12 @@ subroutine varSubstep(& failedMinimumStep=.false. ! set the flag to compute enthalpy, may want to have this true always if want to output enthalpy - computeEnthalpy = .false. - use_lookup = .false. ! with or without lookup tables - if((ixNrgConserv .ne. closedForm .or. computNrgBalance) .and. ixNumericalMethod .ne. ida) computeEnthalpy = .true. - if (ixNrgConserv==enthalpyFDlu) use_lookup = .true. + computeEnthalpy = .false. + enthalpyStateVec = .false. + use_lookup = .false. + if((ixNrgConserv .ne. closedForm .or. computNrgBalance) .and. ixNumericalMethod .ne. ida) computeEnthalpy = .true. ! use enthTemp to conserve energy or compute energy balance + if(ixNrgConserv .ne. closedForm .and. ixNumericalMethod==ida) enthalpyStateVec = .true. ! enthalpy as state variable + if(ixNrgConserv==enthalpyFDlu) use_lookup = .true. ! use lookup tables for soil enthalpy instead of analytical solution ! initialize the length of the substep dtSubstep = dtInit @@ -326,13 +329,14 @@ subroutine varSubstep(& ! initialize state vectors call popStateVec(& ! input - nState, & ! intent(in): number of desired state variables - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers + nState, & ! intent(in): number of desired state variables + enthalpyStateVec, & ! intent(in): flag to use enthalpy as a state variable + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers ! output - stateVecInit, & ! intent(out): initial model state vector (mixed units) - err,cmessage) ! intent(out): error control + stateVecInit, & ! intent(out): initial model state vector (mixed units) + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) ! ----- @@ -347,7 +351,7 @@ subroutine varSubstep(& nLayers, & ! intent(in): total number of layers firstSubStep, & ! intent(in): flag to denote first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - firstSplitOper, & ! intent(inout): flag to indicate if we are processing the first flux call in a splitting operation + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution computMassBalance, & ! intent(in): flag to compute mass balance @@ -445,10 +449,10 @@ subroutine varSubstep(& endif ! update prognostic variables, update balances, and check them for possible step reduction if numrec or kinsol - call updateProg(dtSubstep,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states - doAdjustTemp,computeVegFlux,computMassBalance,computNrgBalance,computeEnthalpy,use_lookup, & ! input: model control - model_decisions,lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures - fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! output: balances, flags, and error control + call updateProg(dtSubstep,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states + doAdjustTemp,computeVegFlux,computMassBalance,computNrgBalance,computeEnthalpy,enthalpyStateVec,use_lookup,& ! input: model control + model_decisions,lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures + fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! output: balances, flags, and error control if(err/=0)then message=trim(message)//trim(cmessage) if(err>0) return @@ -622,17 +626,17 @@ end subroutine varSubstep ! ********************************************************************************************************** ! private subroutine updateProg: update prognostic variables ! ********************************************************************************************************** -subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states - doAdjustTemp,computeVegFlux,computMassBalance,computNrgBalance,computeEnthalpy,use_lookup, & ! input: model control - model_decisions,lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures - fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! input-output: balances, flags, and error control +subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states + doAdjustTemp,computeVegFlux,computMassBalance,computNrgBalance,computeEnthalpy,enthalpyStateVec,use_lookup,& ! input: model control + model_decisions,lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures + fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! input-output: balances, flags, and error control USE getVectorz_module,only:varExtract ! extract variables from the state vector #ifdef SUNDIALS_ACTIVE USE updateVarsWithPrime_module,only:updateVarsWithPrime ! update prognostic variables #endif USE updateVars_module,only:updateVars ! update prognostic variables USE enthalpyTemp_module,only:T2enthTemp ! compute enthalpy - USE enthalpyTemp_module,only:enthTemp2enthalpy ! add phase change terms to delta temperature component of enthalpy + USE enthalpyTemp_module,only:enthTemp_or_enthalpy ! add phase change terms to delta temperature component of enthalpy implicit none ! model control real(rkind) ,intent(in) :: dt ! time step (s) @@ -644,11 +648,11 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec real(rkind) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) real(rkind) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) real(rkind) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) - logical(lgt) ,intent(in) :: computMassBalance ! flag to check the mass balance - logical(lgt) ,intent(in) :: computNrgBalance ! flag to check the energy balance + logical(lgt) ,intent(in) :: computMassBalance ! flag to check the mass balance + logical(lgt) ,intent(in) :: computNrgBalance ! flag to check the energy balance logical(lgt) ,intent(in) :: computeEnthalpy ! flag to compute enthalpy - logical(lgt) ,intent(in) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function - + logical(lgt) ,intent(in) :: enthalpyStateVec ! flag if enthalpy is a state variable (ida) + logical(lgt) ,intent(in) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use analytical solution ! data structures type(model_options),intent(in) :: model_decisions(:) ! model decisions type(zLookup),intent(in) :: lookup_data ! lookup tables @@ -700,7 +704,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec real(rkind) :: scalarCanopyEnthTempTrial ! trial value for temperature component of enthalpy of the vegetation canopy (J m-3) real(rkind),dimension(nLayers) :: mLayerEnthTempTrial ! trial vector of temperature component of enthalpy of snow + soil (J m-3) real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) - real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector of enthalpy of each snow and soil layer (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector of enthalpy of each snow and soil layer (J m-3) ! diagnostic variables real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) @@ -731,14 +735,12 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec real(rkind) :: scalarCanairNrgPrime ! prime value for energy of the canopy air space real(rkind) :: scalarCanopyNrgPrime ! prime value for energy of the vegetation canopy real(rkind),dimension(nLayers) :: mLayerNrgPrime ! prime vector of energy of each snow and soil layer - ! ------------------------------------------------------------------------------------------------------------------- ! ------------------------------------------------------------------------------------------------------------------- ! point to flux variables in the data structure associate(& ! model decisions ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver - ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& ! intent(in): [i4b] choice of variable in energy conservation backward Euler residual ! get indices for balances ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in) : [i4b] index of canopy air space energy state variable ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in) : [i4b] index of canopy energy state variable @@ -788,7 +790,9 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(inout): [dp(:)] matric potential of liquid water (m) ! enthalpy scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-3) scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! intent(inout): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(inout): [dp(:)] enthalpy of the snow+soil layers (J m-3) mLayerEnthTemp => diag_data%var(iLookDIAG%mLayerEnthTemp)%dat ,& ! intent(inout): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) ! model state variables (aquifer) scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(inout): [dp(:)] storage of water in the aquifer (m) @@ -813,10 +817,12 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! initialize to state variable from the last update scalarCanairEnthalpyTrial = scalarCanairEnthalpy + scalarCanopyEnthalpyTrial = scalarCanopyEnthalpy scalarCanopyEnthTempTrial = scalarCanopyEnthTemp scalarCanopyWatTrial = scalarCanopyWat scalarCanopyLiqTrial = scalarCanopyLiq scalarCanopyIceTrial = scalarCanopyIce + mLayerEnthalpyTrial = mLayerEnthalpy mLayerEnthTempTrial = mLayerEnthTemp mLayerVolFracWatTrial = mLayerVolFracWat mLayerVolFracLiqTrial = mLayerVolFracLiq @@ -825,7 +831,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq scalarAquiferStorageTrial = scalarAquiferStorage - if(ixNumericalMethod==ida .and. (ixNrgConserv==enthalpyFD .or. ixNrgConserv==enthalpyFDlu))then ! use state variable as enthalpy + if(enthalpyStateVec)then ! use state variable as enthalpy scalarCanairNrgTrial = scalarCanairEnthalpy scalarCanopyNrgTrial = realMissing ! currently not splitting in ida so no need to update mLayerNrgTrial = realMissing ! currently not splitting in ida so no need to update @@ -859,7 +865,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - if(ixNumericalMethod==ida .and. (ixNrgConserv==enthalpyFD .or. ixNrgConserv==enthalpyFDlu))then ! use state variable as enthalpy + if(enthalpyStateVec)then ! use state variable as enthalpy scalarCanairEnthalpyTrial = scalarCanairNrgTrial scalarCanopyEnthalpyTrial = scalarCanopyNrgTrial mLayerEnthalpyTrial = mLayerNrgTrial @@ -867,9 +873,6 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec scalarCanairTempTrial = scalarCanairNrgTrial scalarCanopyTempTrial = scalarCanopyNrgTrial mLayerTempTrial = mLayerNrgTrial - ! do not need these variables, not saved out of the subroutine - scalarCanopyEnthalpyTrial = realMissing - mLayerEnthalpyTrial = realMissing endif ! Placeholder: if we decide to use splitting, we need to pass all the previous values of the state variables @@ -921,7 +924,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - if(ixNrgConserv== enthalpyFD .or. ixNrgConserv == enthalpyFDlu)then ! use state variable as enthalpy, need to compute temperature + if(enthalpyStateVec)then ! use state variable as enthalpy, need to compute temperature ! do not use these variables scalarCanairTempPrime = realMissing scalarCanopyTempPrime = realMissing @@ -935,45 +938,45 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! update diagnostic variables call updateVarsWithPrime(& ! input - ixNrgConserv.ne.closedForm, & ! intent(in): flag if need to update temperature from enthalpy - ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy - .false., & ! intent(in): logical flag if computing for Jacobian update - doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - lookup_data, & ! intent(in): lookup table data structure + enthalpyStateVec, & ! intent(in): flag if enthalpy is used as state variable + use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy + .false., & ! intent(in): logical flag if computing for Jacobian update + doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + lookup_data, & ! intent(in): lookup table data structure ! input: enthalpy state variables - scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(in): trial value for enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(in): trial vector of enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(in): trial value for enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(in): trial vector of enthalpy of each snow+soil layer (J m-3) ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(inout): trial value of canopy air space temperature (K) - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2) + scalarCanairTempTrial, & ! intent(inout): trial value of canopy air space temperature (K) + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIcePrime, & ! intent(inout): trial value of canopy ice content (kg m-2) ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! - mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) - mLayerVolFracIcePrime, & ! - mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempPrime, & ! + mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) + mLayerVolFracIcePrime, & ! + mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control #endif case(kinsol, numrec) @@ -1048,8 +1051,9 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec mLayerHDelta = mLayerEnthTempTrial - mLayerEnthTemp(1:nLayers) ! compute mixture enthalpy for current values, do on delta value so only have to do once - call enthTemp2enthalpy(& + call enthTemp_or_enthalpy(& ! input: data structures + .true., & ! intent(in): flag to convert enthTemp to enthalpy diag_data, & ! intent(in): model diagnostic variables for a local HRU indx_data, & ! intent(in): model indices ! input: ice content change @@ -1355,11 +1359,14 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec endif ! (if energy state variables exist) ! ----- - ! * update enthalpy as a diagnostic variable... if computeEnthalpy is false this will not change + ! * update enthalpy as a diagnostic variable... + ! if computeEnthalpy then enthTemp will change, if enthalpyStateVec then enthalpy will change ! -------------------------------- - scalarCanairEnthalpy = scalarCanairEnthalpyTrial + scalarCanairEnthalpy = scalarCanairEnthalpyTrial ! equivalent to scalarCanairEnthTemp scalarCanopyEnthTemp = scalarCanopyEnthTempTrial + scalarCanopyEnthalpy = scalarCanopyEnthalpyTrial mLayerEnthTemp = mLayerEnthTempTrial + mLayerEnthalpy = mLayerEnthalpyTrial ! ----- ! * update prognostic variables... From 788fe24dc3d1d8a003c400f159ccdd02b6e92a66 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 5 Apr 2024 12:07:39 +0900 Subject: [PATCH 1240/1472] utils --- utils/timeseries_to_statistics.py | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 229a146ba..d80c41810 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -1,4 +1,3 @@ -# SUMMA can produce split-domain output files if the model is used with the -g command line option. '''Loads timeseries of simulated variables and computes a variety of statistics.''' # This script analyzes the resulting files and summarizes the timeseries of a (set of) variable(s) into a statistical value. @@ -35,12 +34,13 @@ # which statistics to compute do_vars = False do_steps = False -do_balance = True +do_balance = False +do_wall = True if testing: not_parallel = True method_name ='be1en' - ibatch = 1 + ibatch = 1 # Run as 1, 2, and then 3 to fully test nbatch = 2 top_fold = '/Users/amedin/Research/USask/test_py/' else: @@ -64,10 +64,12 @@ des_fil = method_name + '_hrly_diff_stats_{}_{}.nc' des_fl2 = method_name + '_hrly_diff_steps_{}_{}.nc' des_fl3 = method_name + '_hrly_diff_bals_{}_{}.nc' +des_fl4 = method_name + '_hrly_diff_wall_{}_{}.nc' settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] stepsets= ['numberStateSplit','numberDomainSplitNrg','numberDomainSplitMass','numberScalarSolutions','meanStepSize'] balssets= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime', 'numberFluxCalc', 'scaledBalanceCasNrg','scaledBalanceVegNrg','scaledBalanceSnowNrg','scaledBalanceSoilNrg','scaledBalanceVegMass','scaledBalanceSnowMass','scaledBalanceSoilMass','scaledBalanceAqMass'] +wallsets= ['wallClockTime'] viz_fil = method_name + '_hrly_diff_stats_{}.nc' viz_fil = viz_fil.format(','.join(settings)) @@ -75,6 +77,8 @@ viz_fl2 = viz_fl2.format(','.join(stepsets)) viz_fl3 = method_name + '_hrly_diff_bals_{}.nc' viz_fl3 = viz_fl3.format(','.join(['balance','scaledBalance'])) +viz_fl4 = method_name + '_hrly_diff_wals_{}.nc' +viz_fl4 = viz_fl4.format(','.join(['wallclock'])) # Make sure we're dealing with the right kind of inputs src_dir = Path(src_dir) @@ -248,6 +252,21 @@ def run_loop(file,bench,processed_files_path0): new = xr.merge([mean,amax]) new.to_netcdf(des_dir / des_fl3.format(var,subset)) + if do_wall: + for var in wallsets: + mean = dat[var].mean(dim='time') + mean = mean.expand_dims("stat").assign_coords(stat=("stat",["mean"])) + + amx = np.fabs(dat[var]).fillna(-1).argmax(dim=['time']) # fill nan with neg value so will not choose + amax = dat[var].isel(amx).drop_vars('time') + amax = amax.expand_dims("stat").assign_coords(stat=("stat",["amax"])) + + std = dat[var].std(dim='time') + std = std.expand_dims("stat").assign_coords(stat=("stat",["std"])) + + new = xr.merge([mean,amax,std]) + new.to_netcdf(des_dir / des_fl4.format(var,subset)) + # write the name of the processed file to the file list, acquire the lock before opening the file if not_parallel: with open(processed_files_path0, 'a') as filew: @@ -342,6 +361,10 @@ def merge_subsets_into_one(src,pattern,des,name): merge_subsets_into_one(des_dir,des_fl3.replace('{}','*'),fnl_dir,viz_fl3) for file in glob.glob(str(des_dir / des_fl3.replace('{}','*'))): os.remove(file) + if do_wall: + merge_subsets_into_one(des_dir,des_fl4.replace('{}','*'),fnl_dir,viz_fl4) + for file in glob.glob(str(des_dir / des_fl4.replace('{}','*'))): + os.remove(file) else: # do the batch From 48dbf01532f450ff630500e22aff698aaf90965f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 5 Apr 2024 19:09:35 +0900 Subject: [PATCH 1241/1472] utils, spaces --- build/source/engine/check_icond.f90 | 2 +- utils/bal_per_GRU.py | 2 +- utils/wallStd_per_GRU.py | 104 ++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 utils/wallStd_per_GRU.py diff --git a/build/source/engine/check_icond.f90 b/build/source/engine/check_icond.f90 index d99114cb0..fa8cd3170 100644 --- a/build/source/engine/check_icond.f90 +++ b/build/source/engine/check_icond.f90 @@ -200,7 +200,7 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU if(mLayerVolFracLiq(iLayer) < theta_res(iSoil)-xTol)then; write(message,'(a,1x,i0)') trim(message)//'cannot initialize the model with volumetric fraction of liquid water < theta_res: layer = ',iLayer; err=20; return; end if if(mLayerVolFracLiq(iLayer) > theta_sat(iSoil)+xTol)then; write(message,'(a,1x,i0)') trim(message)//'cannot initialize the model with volumetric fraction of liquid water > theta_sat: layer = ',iLayer; err=20; return; end if ! (check ice) - if(mLayerVolFracIce(iLayer) < 0._rkind )then; write(message,'(a,1x,i0)') trim(message)//'cannot initialize the model with volumetric fraction of ice < 0: layer = ' ,iLayer; err=20; return; end if + if(mLayerVolFracIce(iLayer) < 0._rkind )then; write(message,'(a,1x,i0)') trim(message)//'cannot initialize the model with volumetric fraction of ice < 0: layer = ' ,iLayer; err=20; return; end if if(mLayerVolFracIce(iLayer) > theta_sat(iSoil)+xTol)then; write(message,'(a,1x,i0)') trim(message)//'cannot initialize the model with volumetric fraction of ice > theta_sat: layer = ',iLayer; err=20; return; end if ! check total water if(scalarTheta < theta_res(iSoil)-xTol)then; write(message,'(a,1x,i0)') trim(message)//'cannot initialize the model with total water fraction [liquid + ice] < theta_res: layer = ',iLayer; err=20; return; end if diff --git a/utils/bal_per_GRU.py b/utils/bal_per_GRU.py index 9ce495f0f..ba22cd6fb 100644 --- a/utils/bal_per_GRU.py +++ b/utils/bal_per_GRU.py @@ -1,4 +1,4 @@ -# written by A. Van Beusekom (2023) +# written by A. Van Beusekom (2024) ## Visualize statistics per GRU ## Needs: diff --git a/utils/wallStd_per_GRU.py b/utils/wallStd_per_GRU.py new file mode 100644 index 000000000..f350e0591 --- /dev/null +++ b/utils/wallStd_per_GRU.py @@ -0,0 +1,104 @@ +# written by A. Van Beusekom (2024) + +## Visualize statistics per GRU +## Needs: +# SUMMA output statistics + +## Special note +# SUMMA simulations have been preprocessed into single value statistics per model element, using auxiliary scripts in ~/utils +# Run: +# python wallStd_per_GRU.py + +# modules +import os +import matplotlib +import numpy as np +import xarray as xr +from pathlib import Path +import matplotlib.pyplot as plt +import copy +import pandas as pd + +viz_dir = Path('/home/avanb/scratch/statistics') + +testing = False +if testing: + viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') + method_name=['be1en'] #cm','be1en','be1lu'] #maybe make this an argument +else: + import sys + # The first input argument specifies the run where the files are + method_name=['be1','be1en','be1lu'] #maybe make this an argument + +# Simulation statistics file locations +viz_fl2 = method_name.copy() +for i, m in enumerate(method_name): + viz_fl2[i] = m + '_hrly_diff_wals_{}.nc' + viz_fl2[i] = viz_fl2[i].format(','.join(['wallclock'])) + +# Specify variables of interest +stats = ['mean','amax'] +plot_vars = ['wallClockTime']*2 +comp_vars = ['wallClockTime']*2 + +plt_titl = ['(a) Wall Clock Time Mean','(b) Wall Clock Time Max'] +leg_titl0 = ['$s$']*2 +leg_titl = ['$s$']*2 + +#fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' +#fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) +fig_fil = 'WallStd_scat_mean_max_compressed.png' + +summa = {} +for i, m in enumerate(method_name): + # Get the aggregated statistics of SUMMA simulations + summa[m] = xr.open_dataset(viz_dir/viz_fl2[i]) + +##Figure + +def run_loop(i,var,comp,leg_t,leg_t0,plt_t,stat): + r = i//2 + c = i-r*2 + + # Data + for m in method_name: + # Get the statistics, remove 9999 (should be nan, but just in case) + s = np.fabs(summa[m][var].sel(stat=stat)).where(lambda x: x != 9999) + s0 = np.fabs(summa[m][comp].sel(stat='std')).where(lambda x: x != 9999) + + stat_word = 'Time' + stat0_word = 'Time std dev' + + axs[c].scatter(x=s.values,y=s0.values,s=10,zorder=0,label=m) + + if stat == 'mean': word = ' mean' + if stat == 'amax': word = ' max' + + lgnd = axs[c].legend() + for j, m in enumerate(method_name): + lgnd.legendHandles[j]._sizes = [80] + axs[c].set_title(plt_t) + #axs[c].set_xscale('log') + #axs[c].set_yscale('log') + + axs[c].set_xlabel(stat_word + word + ' [{}]'.format(leg_t)) + axs[c].set_ylabel(stat0_word + ' [{}]'.format(leg_t0)) + +if 'compressed' in fig_fil: + plt.rcParams.update({'font.size': 25}) +else: + plt.rcParams.update({'font.size': 100}) + +if 'compressed' in fig_fil: + fig,axs = plt.subplots(2,figsize=(35,38)) +else: + fig,axs = plt.subplots(2,figsize=(140,160)) +fig.subplots_adjust(hspace=0.24, wspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space +#fig.suptitle('Scatterplot of Hourly Statistics for each GRU', fontsize=40,y=1.0) + +for i,(var,comp,leg_t,leg_t0,plt_t,stat) in enumerate(zip(plot_vars,comp_vars,leg_titl,leg_titl0,plt_titl,stats)): + run_loop(i,var,comp,leg_t,leg_t0,plt_t,stat) + +# Save +plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) + From 90ed9667f32323f6764777d3037b8ffe64163505 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 5 Apr 2024 20:30:28 +0900 Subject: [PATCH 1242/1472] utils --- utils/wallStd_per_GRU.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/utils/wallStd_per_GRU.py b/utils/wallStd_per_GRU.py index f350e0591..0355ae316 100644 --- a/utils/wallStd_per_GRU.py +++ b/utils/wallStd_per_GRU.py @@ -24,11 +24,10 @@ testing = False if testing: viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be1en'] #cm','be1en','be1lu'] #maybe make this an argument + method_name=['be1en','be1en'] #cm','be1en','be1lu'] #maybe make this an argument else: import sys - # The first input argument specifies the run where the files are - method_name=['be1','be1en','be1lu'] #maybe make this an argument + method_name=['sundials_1en4','sundials_1en6','sundials_1en8'] #maybe make this an argument # Simulation statistics file locations viz_fl2 = method_name.copy() @@ -61,7 +60,7 @@ def run_loop(i,var,comp,leg_t,leg_t0,plt_t,stat): c = i-r*2 # Data - for m in method_name: + for mm,m in enumerate(method_name): # Get the statistics, remove 9999 (should be nan, but just in case) s = np.fabs(summa[m][var].sel(stat=stat)).where(lambda x: x != 9999) s0 = np.fabs(summa[m][comp].sel(stat='std')).where(lambda x: x != 9999) @@ -71,6 +70,26 @@ def run_loop(i,var,comp,leg_t,leg_t0,plt_t,stat): axs[c].scatter(x=s.values,y=s0.values,s=10,zorder=0,label=m) + # Create a mask that is True where `s.values` and `s0.values` are not NaN + mask = ~np.isnan(s.values) & ~np.isnan(s0.values) + + # Use the mask to filter `s.values` and `s0.values` + filtered_s_values = s.values[mask] + filtered_s0_values = s0.values[mask] + + # Fit a linear regression model + coefficients = np.polyfit(filtered_s_values, filtered_s0_values, 1) + polynomial = np.poly1d(coefficients) + + # Calculate the R-squared value + correlation_matrix = np.corrcoef(filtered_s_values, filtered_s0_values) + correlation_xy = correlation_matrix[0,1] + r_squared = correlation_xy**2 + # Add the R-squared value to the plot + axs[c].text(0.05, 0.9-0.03*mm, f'{m} R² = {r_squared:.2f}', transform=axs[c].transAxes) + print(m,stat,'Coefficients:', coefficients, 'R-squared:', r_squared) + + if stat == 'mean': word = ' mean' if stat == 'amax': word = ' max' @@ -78,8 +97,8 @@ def run_loop(i,var,comp,leg_t,leg_t0,plt_t,stat): for j, m in enumerate(method_name): lgnd.legendHandles[j]._sizes = [80] axs[c].set_title(plt_t) - #axs[c].set_xscale('log') - #axs[c].set_yscale('log') + axs[c].set_xscale('log') + axs[c].set_yscale('log') axs[c].set_xlabel(stat_word + word + ' [{}]'.format(leg_t)) axs[c].set_ylabel(stat0_word + ' [{}]'.format(leg_t0)) From c8bdbf42b75a9b73efa3e7d6fc9bb5f980c2730b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 8 Apr 2024 16:21:08 +0900 Subject: [PATCH 1243/1472] some variable capital letter name switches, make enthalpy start at the value of initial temp's enthalpy --- build/source/driver/summa_alarms.f90 | 10 +- build/source/driver/summa_defineOutput.f90 | 2 +- build/source/driver/summa_restart.f90 | 40 +++-- build/source/driver/summa_setup.f90 | 8 +- build/source/driver/summa_writeOutput.f90 | 4 +- build/source/dshare/get_ixname.f90 | 52 +++---- build/source/dshare/outpt_stat.f90 | 38 ++--- build/source/dshare/popMetadat.f90 | 4 +- build/source/dshare/var_lookup.f90 | 8 +- build/source/engine/check_icond.f90 | 150 ++++++++++++++----- build/source/engine/computJacobWithPrime.f90 | 2 +- build/source/engine/coupled_em.f90 | 1 + build/source/engine/enthalpyTemp.f90 | 2 +- build/source/engine/eval8summa.f90 | 13 +- build/source/engine/eval8summaWithPrime.f90 | 16 +- build/source/engine/getVectorz.f90 | 8 +- build/source/engine/opSplittin.f90 | 20 +-- build/source/engine/summaSolve4ida.f90 | 19 ++- build/source/engine/summaSolve4kinsol.f90 | 1 + build/source/engine/updateVarsWithPrime.f90 | 4 + build/source/engine/varSubstep.f90 | 2 +- build/source/netcdf/def_output.f90 | 6 +- build/source/netcdf/modelwrite.f90 | 46 +++--- build/source/netcdf/read_icond.f90 | 6 +- utils/wallStd_per_GRU.py | 2 +- 25 files changed, 292 insertions(+), 172 deletions(-) diff --git a/build/source/driver/summa_alarms.f90 b/build/source/driver/summa_alarms.f90 index 572a2a1f6..1ee5d1564 100644 --- a/build/source/driver/summa_alarms.f90 +++ b/build/source/driver/summa_alarms.f90 @@ -41,7 +41,7 @@ module summa_alarms ! named variable for time structures USE var_lookup,only:iLookTIME ! named variables for time data structure -USE var_lookup,only:iLookFreq ! named variables for the frequency structure +USE var_lookup,only:iLookFREQ ! named variables for the frequency structure ! structure dimensions USE var_lookup,only:maxvarFreq ! maximum number of output files @@ -150,10 +150,10 @@ subroutine summa_setWriteAlarms(oldTime, newTime, endTime, & ! time vector ! define the need to finalize statistics ! NOTE: time vector is configured so that ih=0 at the start of the day, hence day in oldTime and timeStruct%var differ select case(iFreq) - case(iLookFreq%day ); finalizeStats(iFreq)=(oldTime(iLookTIME%id )/=newTime(iLookTIME%id )) ! daily aggregation - case(iLookFreq%month ); finalizeStats(iFreq)=(oldTime(iLookTIME%im )/=newTime(iLookTIME%im )) ! monthly aggregation - case(iLookFreq%annual ); finalizeStats(iFreq)=(oldTime(iLookTIME%iyyy)/=newTime(iLookTIME%iyyy)) ! yearly (annual) aggregation - case(iLookFreq%timestep); finalizeStats(iFreq)=.true. ! timestep-level output (no temporal aggregation) + case(iLookFREQ%day ); finalizeStats(iFreq)=(oldTime(iLookTIME%id )/=newTime(iLookTIME%id )) ! daily aggregation + case(iLookFREQ%month ); finalizeStats(iFreq)=(oldTime(iLookTIME%im )/=newTime(iLookTIME%im )) ! monthly aggregation + case(iLookFREQ%annual ); finalizeStats(iFreq)=(oldTime(iLookTIME%iyyy)/=newTime(iLookTIME%iyyy)) ! yearly (annual) aggregation + case(iLookFREQ%timestep); finalizeStats(iFreq)=.true. ! timestep-level output (no temporal aggregation) case default; err=20; message=trim(message)//'unable to identify output frequency'; return end select diff --git a/build/source/driver/summa_defineOutput.f90 b/build/source/driver/summa_defineOutput.f90 index 27092834c..671d17b7f 100644 --- a/build/source/driver/summa_defineOutput.f90 +++ b/build/source/driver/summa_defineOutput.f90 @@ -38,7 +38,7 @@ module summa_defineOutput ! used to define model output file ! named variables USE var_lookup,only:maxvarFreq ! maximum number of output files USE var_lookup,only:iLookTIME ! named variables for time data structure -USE var_lookup,only:iLookFreq ! named variables for the frequency structure +USE var_lookup,only:iLookFREQ ! named variables for the frequency structure ! safety: set private unless specified otherwise implicit none diff --git a/build/source/driver/summa_restart.f90 b/build/source/driver/summa_restart.f90 index 2b9e09a75..70036e4bf 100644 --- a/build/source/driver/summa_restart.f90 +++ b/build/source/driver/summa_restart.f90 @@ -68,6 +68,16 @@ subroutine summa_readRestart(summa1_struc, err, message) USE mDecisions_module,only:& ! look-up values for the choice of method for the spatial representation of groundwater localColumn, & ! separate groundwater representation in each local soil column singleBasin ! single groundwater store over the entire basin +! look-up values for the numerical method +USE mDecisions_module,only:& + numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes + kinsol ,& ! SUNDIALS backward Euler solution using Kinsol + ida ! SUNDIALS solution using IDA + ! look-up values for the choice of variable in energy equations (BE residual or IDA state variable) + USE mDecisions_module,only:& + closedForm, & ! use temperature + enthalpyFDlu, & ! use enthalpy with lookup tables + enthalpyFD ! use enthalpy with analytical solution ! --------------------------------------------------------------------------------------- ! * variables ! --------------------------------------------------------------------------------------- @@ -80,26 +90,30 @@ subroutine summa_readRestart(summa1_struc, err, message) character(LEN=256) :: cmessage ! error message of downwind routine character(LEN=256) :: restartFile ! restart file name integer(i4b) :: iGRU,iHRU ! looping variables + logical(lgt) :: enthalpyStateVec ! flag if enthalpy is a state variable (ida) + logical(lgt) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use analytical solution ! --------------------------------------------------------------------------------------- ! associate to elements in the data structure - summaVars: associate(& - + summaVars: associate(& + ! model decisions + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& !choice of numerical solver + ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& !choice of variable in energy conservation backward Euler residual + spatial_gw => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& !choice of method for the spatial representation of groundwater + ! lookup table data structure + lookupStruct => summa1_struc%lookupStruct , & ! x%gru(:)%hru(:)%z(:)%var(:)%lookup(:) -- lookup tables ! primary data structures (variable length vectors) indxStruct => summa1_struc%indxStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model indices mparStruct => summa1_struc%mparStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model parameters progStruct => summa1_struc%progStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model prognostic (state) variables diagStruct => summa1_struc%diagStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model diagnostic variables fluxStruct => summa1_struc%fluxStruct , & ! x%gru(:)%hru(:)%var(:)%dat -- model fluxes - ! basin-average structures bparStruct => summa1_struc%bparStruct , & ! x%gru(:)%var(:) -- basin-average parameters bvarStruct => summa1_struc%bvarStruct , & ! x%gru(:)%var(:)%dat -- basin-average variables - ! miscellaneous variables dt_init => summa1_struc%dt_init , & ! used to initialize the length of the sub-step for each HRU nGRU => summa1_struc%nGRU , & ! number of grouped response units nHRU => summa1_struc%nHRU & ! number of global hydrologic response units - ) ! assignment to variables in the data structures ! --------------------------------------------------------------------------------------- @@ -130,11 +144,19 @@ subroutine summa_readRestart(summa1_struc, err, message) err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - ! check initial conditions +! check initial conditions + enthalpyStateVec = .false. + use_lookup = .false. + if(ixNrgConserv .ne. closedForm .and. ixNumericalMethod==ida) enthalpyStateVec = .true. ! enthalpy as state variable + if(ixNrgConserv==enthalpyFDlu) use_lookup = .true. ! use lookup tables for soil enthalpy instead of analytical solution call check_icond(nGRU, & ! intent(in): number of response units - progStruct, & ! intent(in): model prognostic (state) variables + progStruct, & ! intent(inout): model prognostic variables + diagStruct, & ! intent(inout): model diagnostic variables mparStruct, & ! intent(in): model parameters - indxStruct, & ! intent(in): layer indexes + indxStruct, & ! intent(inout): layer indexes + lookupStruct, & ! intent(in): lookup tables + enthalpyStateVec, & ! intent(in): flag if enthalpy is the state variable + use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -196,7 +218,7 @@ subroutine summa_readRestart(summa1_struc, err, message) ! and ensure that basin-average aquifer storage is zero when groundwater is included in the local columns (localColumn). ! select groundwater option - select case(model_decisions(iLookDECISIONS%spatial_gw)%iDecision) + select case(spatial_gw) ! the basin-average aquifer storage is not used if the groundwater is included in the local column case(localColumn) diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index f7612b23c..5432d874b 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -42,11 +42,11 @@ module summa_setup ! metadata structures USE globalData,only:mpar_meta,bpar_meta ! parameter metadata structures -! look-up values for the choice of heat capacity computation +! look-up values for the choice of variable in energy equations (BE residual or IDA state variable) USE mDecisions_module,only:& - closedForm,& ! heat capacity closed form in backward Euler residual - enthalpyFDlu,& ! enthalpy with lookup tables finite difference in backward Euler residual - enthalpyFD ! enthalpy with hypergeometric function finite difference in backward Euler residual + closedForm,& ! use temperature + enthalpyFDlu,& ! use enthalpy with lookup tables + enthalpyFD ! use enthalpy with analytical solution ! named variables to define the decisions for snow layers USE mDecisions_module,only:& diff --git a/build/source/driver/summa_writeOutput.f90 b/build/source/driver/summa_writeOutput.f90 index 32ef3d466..e57309f67 100644 --- a/build/source/driver/summa_writeOutput.f90 +++ b/build/source/driver/summa_writeOutput.f90 @@ -56,7 +56,7 @@ module summa_writeOutput USE var_lookup,only:iLookDIAG ! named variables for local column model diagnostic variables USE var_lookup,only:iLookPROG ! named variables for local column model prognostic variables USE var_lookup,only:iLookINDEX ! named variables for local column index variables -USE var_lookup,only:iLookFreq ! named variables for the frequency structure +USE var_lookup,only:iLookFREQ ! named variables for the frequency structure ! safety: set private unless specified otherwise implicit none @@ -176,7 +176,7 @@ subroutine summa_writeOutputFiles(modelTimeStep, summa1_struc, err, message) finalizeStats(:) = .false. ! do not finalize stats on the first time step ! set stats flag for the timestep-level output - finalizeStats(iLookFreq%timestep)=.true. + finalizeStats(iLookFREQ%timestep)=.true. ! initialize number of hru and gru in global data nGRUrun = nGRU diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 7f05d04c8..71aab348f 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -1129,17 +1129,17 @@ end function get_ixLookup ! public function get_ixfreq: get the index of the named variables for the output frequencies ! ******************************************************************************************************************* function get_ixfreq(varName) - USE var_lookup,only:iLookFreq ! indices of the named variables + USE var_lookup,only:iLookFREQ ! indices of the named variables implicit none ! define dummy variables character(*), intent(in) :: varName ! variable name integer(i4b) :: get_ixfreq ! index of the named variable ! get the index of the named variables select case(trim(varName)) - case('day' ); get_ixFreq = iLookFreq%day ! daily aggregation - case('month' ); get_ixFreq = iLookFreq%month ! monthly aggregation - case('annual' ); get_ixFreq = iLookFreq%annual ! yearly (annual) aggregation - case('timestep'); get_ixFreq = iLookFreq%timestep ! timestep-level output (no temporal aggregation) + case('day' ); get_ixFreq = iLookFREQ%day ! daily aggregation + case('month' ); get_ixFreq = iLookFREQ%month ! monthly aggregation + case('annual' ); get_ixFreq = iLookFREQ%annual ! yearly (annual) aggregation + case('timestep'); get_ixFreq = iLookFREQ%timestep ! timestep-level output (no temporal aggregation) ! get to here if cannot find the variable case default get_ixfreq = integerMissing @@ -1150,20 +1150,20 @@ end function get_ixfreq ! public function get_ixStat: get the named variables for the statistics ! *************************************************************************************************************** function get_ixStat(varName) - USE var_lookup,only:iLookStat ! indices of the possible output statistics + USE var_lookup,only:iLookSTAT ! indices of the possible output statistics implicit none ! define dummy variables character(*), intent(in) :: varName ! variable name integer(i4b) :: get_ixStat ! index of the named variable ! get the index of the named variables select case(trim(varName)) - case('total' ); get_ixStat = iLookStat%totl - case('instant' ); get_ixStat = iLookStat%inst - case('mean' ); get_ixStat = iLookStat%mean - case('variance'); get_ixStat = iLookStat%vari - case('minimum' ); get_ixStat = iLookStat%mini - case('maximum' ); get_ixStat = iLookStat%maxi - case('mode' ); get_ixStat = iLookStat%mode + case('total' ); get_ixStat = iLookSTAT%totl + case('instant' ); get_ixStat = iLookSTAT%inst + case('mean' ); get_ixStat = iLookSTAT%mean + case('variance'); get_ixStat = iLookSTAT%vari + case('minimum' ); get_ixStat = iLookSTAT%mini + case('maximum' ); get_ixStat = iLookSTAT%maxi + case('mode' ); get_ixStat = iLookSTAT%mode ! get to here if cannot find the variable case default get_ixStat = integerMissing @@ -1174,17 +1174,17 @@ end function get_ixStat ! public function get_freqName: get the name of the output frequency type ! *************************************************************************************************************** function get_freqName(ifreq) - USE var_lookup,only:iLookFreq ! indices of the possible output frequencies + USE var_lookup,only:iLookFREQ ! indices of the possible output frequencies implicit none ! define dummy variables integer(i4b), intent(in) :: ifreq ! output frequency index character(LEN=10) :: get_freqName ! name of the output frequency ! get the index of the named variables select case(ifreq) - case(iLookFreq%day); get_freqName='day' - case(iLookFreq%month); get_freqName='month' - case(iLookFreq%annual); get_freqName='annual' - case(iLookFreq%timestep); get_freqName='timestep' + case(iLookFREQ%day); get_freqName='day' + case(iLookFREQ%month); get_freqName='month' + case(iLookFREQ%annual); get_freqName='annual' + case(iLookFREQ%timestep); get_freqName='timestep' ! get to here if cannot find the variable case default get_freqName = 'unknown' @@ -1195,20 +1195,20 @@ end function get_freqName ! public function get_statName: get the name of the output statistics type ! *************************************************************************************************************** function get_statName(istat) - USE var_lookup,only:iLookStat ! indices of the possible output statistics + USE var_lookup,only:iLookSTAT ! indices of the possible output statistics implicit none ! define dummy variables integer(i4b), intent(in) :: istat ! stat type name character(LEN=10) :: get_statName ! name of the statistic ! get the index of the named variables select case(istat) - case(iLookStat%totl);get_statName='total' - case(iLookStat%inst);get_statName='instant' - case(iLookStat%mean);get_statName='mean' - case(iLookStat%vari);get_statName='variance' - case(iLookStat%mini);get_statName='minimum' - case(iLookStat%maxi);get_statName='maximum' - case(iLookStat%mode);get_statName='mode' + case(iLookSTAT%totl);get_statName='total' + case(iLookSTAT%inst);get_statName='instant' + case(iLookSTAT%mean);get_statName='mean' + case(iLookSTAT%vari);get_statName='variance' + case(iLookSTAT%mini);get_statName='minimum' + case(iLookSTAT%maxi);get_statName='maximum' + case(iLookSTAT%mode);get_statName='mode' ! get to here if cannot find the variable case default get_statName = 'unknown' diff --git a/build/source/dshare/outpt_stat.f90 b/build/source/dshare/outpt_stat.f90 index 9bae9b83b..56fafbd3a 100644 --- a/build/source/dshare/outpt_stat.f90 +++ b/build/source/dshare/outpt_stat.f90 @@ -35,7 +35,7 @@ subroutine calcStats(stat,dat,meta,resetStats,finalizeStats,statCounter,err,mess USE nrtype USE data_types,only:extended_info,dlength,ilength ! metadata structure type USE var_lookup,only:iLookVarType ! named variables for variable types - USE var_lookup,only:iLookStat ! named variables for output statistics types + USE var_lookup,only:iLookSTAT ! named variables for output statistics types implicit none ! input variables @@ -81,7 +81,7 @@ subroutine calcStats(stat,dat,meta,resetStats,finalizeStats,statCounter,err,mess ! calculate statistics if (trim(meta(iVar)%varName)=='time') then - stat(iVar)%dat(iLookStat%inst) = tdata + stat(iVar)%dat(iLookSTAT%inst) = tdata else call calc_stats(meta(iVar),stat(iVar),tdata,resetStats,finalizeStats,statCounter,err,cmessage) end if @@ -107,7 +107,7 @@ subroutine calc_stats(meta,stat,tdata,resetStats,finalizeStats,statCounter,err,m USE globalData,only:data_step ! forcing timestep ! structures of named variables USE var_lookup,only:iLookVarType ! named variables for variable types - USE var_lookup,only:iLookFreq ! named variables for output frequency + USE var_lookup,only:iLookFREQ ! named variables for output frequency USE var_lookup,only:iLookSTAT ! named variables for output statistics USE var_lookup,only:iLookTIME ! named variables for time information implicit none @@ -143,20 +143,20 @@ subroutine calc_stats(meta,stat,tdata,resetStats,finalizeStats,statCounter,err,m if(meta%varType/=iLookVarType%outstat) cycle ! only calculate stats for scalars select case(meta%statIndex(iFreq)) ! act depending on the statistic ! ------------------------------------------------------------------------------------- - case (iLookStat%totl) ! * summation over period + case (iLookSTAT%totl) ! * summation over period tstat(iFreq) = 0._rkind ! - resets stat at beginning of period - case (iLookStat%mean) ! * mean over period + case (iLookSTAT%mean) ! * mean over period tstat(iFreq) = 0._rkind ! - resets stat at beginning of period - case (iLookStat%vari) ! * variance over period + case (iLookSTAT%vari) ! * variance over period tstat(iFreq) = 0._rkind ! - resets E[X^2] term in var calc tstat(maxVarFreq+iFreq) = 0._rkind ! - resets E[X]^2 term - case (iLookStat%mini) ! * minimum over period + case (iLookSTAT%mini) ! * minimum over period tstat(iFreq) = huge(tstat(iFreq)) ! - resets stat at beginning of period - case (iLookStat%maxi) ! * maximum over period + case (iLookSTAT%maxi) ! * maximum over period tstat(iFreq) = -huge(tstat(iFreq)) ! - resets stat at beginning of period - case (iLookStat%mode) ! * mode over period + case (iLookSTAT%mode) ! * mode over period tstat(iFreq) = realMissing ! - does not work - case (iLookStat%inst) ! * instantaneous -- no need to reset + case (iLookSTAT%inst) ! * instantaneous -- no need to reset case default message=trim(message)//'unable to identify type of statistic [reset]' err=20; return @@ -173,20 +173,20 @@ subroutine calc_stats(meta,stat,tdata,resetStats,finalizeStats,statCounter,err,m if(meta%varType/=iLookVarType%outstat) cycle ! only calculate stats for scalars select case(meta%statIndex(iFreq)) ! act depending on the statistic ! ------------------------------------------------------------------------------------- - case (iLookStat%inst) ! * instantaneous value + case (iLookSTAT%inst) ! * instantaneous value tstat(iFreq) = tdata ! - data at a given time - case (iLookStat%totl) ! * summation over period + case (iLookSTAT%totl) ! * summation over period tstat(iFreq) = tstat(iFreq) + tdata*data_step ! - increment data - case (iLookStat%mean) ! * mean over period + case (iLookSTAT%mean) ! * mean over period tstat(iFreq) = tstat(iFreq) + tdata ! - increment data - case (iLookStat%vari) ! * variance over period + case (iLookSTAT%vari) ! * variance over period tstat(iFreq) = tstat(iFreq) + tdata**2 ! - E[X^2] term in var calc tstat(maxVarFreq+iFreq) = tstat(maxVarFreq+iFreq) + tdata ! - E[X]^2 term - case (iLookStat%mini) ! * minimum over period + case (iLookSTAT%mini) ! * minimum over period if (tdatatstat(iFreq)) tstat(iFreq) = tdata ! - check value - case (iLookStat%mode) ! * mode over period (does not workind) + case (iLookSTAT%mode) ! * mode over period (does not workind) tstat(iFreq) = realMissing case default message=trim(message)//'unable to identify type of statistic [calculating stats]' @@ -204,9 +204,9 @@ subroutine calc_stats(meta,stat,tdata,resetStats,finalizeStats,statCounter,err,m if(meta%varType/=iLookVarType%outstat) cycle ! only calculate stats for scalars select case(meta%statIndex(iFreq)) ! act depending on the statistic ! ------------------------------------------------------------------------------------- - case (iLookStat%mean) ! * mean over period + case (iLookSTAT%mean) ! * mean over period tstat(iFreq) = tstat(iFreq)/statCounter(iFreq) ! - normalize sum into mean - case (iLookStat%vari) ! * variance over period + case (iLookSTAT%vari) ! * variance over period tstat(maxVarFreq+iFreq) = tstat(maxVarFreq+1)/statCounter(iFreq) ! E[X] term tstat(iFreq) = tstat(iFreq)/statCounter(iFreq) - tstat(maxVarFreq+iFreq)**2 ! full variance case default ! do nothing -- don't need finalization for most stats diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index c903d35bb..47a680306 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -945,8 +945,8 @@ subroutine read_output_file(err,message) ! * identify index in vector select case(freqName) ! define cases where temporal aggregation is numeric - case('1'); iFreq = iLookFreq%timestep ! assume timestep-level output - case('24'); iFreq = iLookFreq%day ! assume daily output + case('1'); iFreq = iLookFREQ%timestep ! assume timestep-level output + case('24'); iFreq = iLookFREQ%day ! assume daily output ! define cases where temporal aggregation is defined using a text string case default; iFreq = get_ixfreq(freqName) end select diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index bef532542..7ed25a1f3 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -962,9 +962,9 @@ MODULE var_lookup type(iLook_varType), public,parameter :: iLookVarType =ilook_varType ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12) ! number of possible output statistics - type(iLook_stat), public,parameter :: iLookStat =ilook_stat ( 1, 2, 3, 4, 5, 6, 7) + type(iLook_stat), public,parameter :: iLookSTAT =ilook_stat ( 1, 2, 3, 4, 5, 6, 7) ! number of possible output frequencies - type(iLook_freq), public,parameter :: iLookFreq =ilook_freq ( 1, 2, 3, 4) + type(iLook_freq), public,parameter :: iLookFREQ =ilook_freq ( 1, 2, 3, 4) ! named variables in the lookup table structure type(iLook_vLookup), public,parameter :: iLookLOOKUP =ilook_vLookup ( 1, 2, 3) ! define maximum number of variables of each type @@ -983,8 +983,8 @@ MODULE var_lookup integer(i4b),parameter,public :: maxvarBpar = storage_size(iLookBPAR)/iLength integer(i4b),parameter,public :: maxvarBvar = storage_size(iLookBVAR)/iLength integer(i4b),parameter,public :: maxvarVarType = storage_size(iLookVarType)/iLength - integer(i4b),parameter,public :: maxvarStat = storage_size(iLookStat)/iLength - integer(i4b),parameter,public :: maxvarFreq = storage_size(iLookFreq)/iLength + integer(i4b),parameter,public :: maxvarStat = storage_size(iLookSTAT)/iLength + integer(i4b),parameter,public :: maxvarFreq = storage_size(iLookFREQ)/iLength integer(i4b),parameter,public :: maxvarLookup = storage_size(iLookLOOKUP)/iLength ! *********************************************************************************************************** diff --git a/build/source/engine/check_icond.f90 b/build/source/engine/check_icond.f90 index fa8cd3170..1e413e0d6 100644 --- a/build/source/engine/check_icond.f90 +++ b/build/source/engine/check_icond.f90 @@ -25,11 +25,6 @@ module check_icond_module USE globalData,only:integerMissing ! missing integer USE globalData,only:realMissing ! missing double precision number -! define modeling decisions -USE mDecisions_module,only: & - moisture, & ! moisture-based form of Richards' equation - mixdform ! mixed form of Richards' equation - implicit none private public::check_icond @@ -40,18 +35,24 @@ module check_icond_module ! ************************************************************************************************ subroutine check_icond(nGRU, & ! number of GRUs and HRUs progData, & ! model prognostic (state) variables + diagData, & ! model diagnostic variables mparData, & ! model parameters indxData, & ! layer index data + lookupData, & ! lookup table data + enthalpyStateVec, & ! flag if enthalpy is the state variable + use_lookup, & ! flag to use the lookup table for soil enthalpy err,message) ! error control ! -------------------------------------------------------------------------------------------------------- ! modules USE nrtype - USE var_lookup,only:iLookParam ! variable lookup structure - USE var_lookup,only:iLookProg ! variable lookup structure - USE var_lookup,only:iLookIndex ! variable lookup structure + USE var_lookup,only:iLookPARAM ! variable lookup structure + USE var_lookup,only:iLookPROG ! variable lookup structure + USE var_lookup,only:iLookDIAG ! variable lookup structure + USE var_lookup,only:iLookINDEX ! variable lookup structure USE globalData,only:gru_struc ! gru-hru mapping structures USE data_types,only:gru_hru_doubleVec ! actual data USE data_types,only:gru_hru_intVec ! actual data + USE data_types,only:gru_hru_z_vLookup ! actual data USE globaldata,only:iname_soil,iname_snow ! named variables to describe the type of layer USE multiconst,only:& LH_fus, & ! latent heat of fusion (J kg-1) @@ -62,23 +63,28 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU USE snow_utils_module,only:fracliquid ! compute volumetric fraction of liquid water in snow based on temperature USE updatState_module,only:updateSnow ! update snow states USE updatState_module,only:updateSoil ! update soil states + USE indexState_module,only:indexState ! index the state variables + USE enthalpyTemp_module,only:T2enthTemp ! convert temperature to enthalpy + USE enthalpyTemp_module,only:enthTemp_or_enthalpy ! add phase change terms to delta temperature component of enthalpy or vice versa + implicit none ! -------------------------------------------------------------------------------------------------------- ! variable declarations ! dummies - integer(i4b) ,intent(in) :: nGRU ! number of grouped response units - type(gru_hru_doubleVec),intent(inout) :: progData ! prognostic vars - type(gru_hru_doubleVec),intent(in) :: mparData ! parameters - type(gru_hru_intVec) ,intent(in) :: indxData ! layer indexes - integer(i4b) ,intent(out) :: err ! error code - character(*) ,intent(out) :: message ! returned error message - + integer(i4b),intent(in) :: nGRU ! number of grouped response units + type(gru_hru_doubleVec),intent(inout) :: diagData ! diagnostic vars + type(gru_hru_doubleVec),intent(inout) :: progData ! prognostic vars + type(gru_hru_doubleVec),intent(in) :: mparData ! parameters + type(gru_hru_intVec),intent(inout) :: indxData ! layer indexes + type(gru_hru_z_vLookup),intent(in) :: lookupData ! lookup table data + logical(lgt),intent(in) :: enthalpyStateVec ! flag if enthalpy is the state variable + logical(lgt),intent(in) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! returned error message ! locals - character(len=256) :: cmessage ! downstream error message - integer(i4b) :: iGRU ! loop index - integer(i4b) :: iHRU ! loop index - + character(len=256) :: cmessage ! downstream error message + integer(i4b) :: i,iGRU,iHRU ! loop index ! temporary variables for realism checks integer(i4b) :: iLayer ! index of model layer integer(i4b) :: iSoil ! index of soil layer @@ -87,9 +93,11 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU real(rkind) :: tWat ! total water on the vegetation canopy (kg m-2) real(rkind) :: scalarTheta ! liquid water equivalent of total water [liquid water + ice] (-) real(rkind) :: h1,h2 ! used to check depth and height are consistent - integer(i4b) :: nLayers ! total number of layers real(rkind) :: kappa ! constant in the freezing curve function (m K-1) + integer(i4b) :: nSoil ! number of soil layers integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nLayers ! total number of layers + integer(i4b) :: nState ! total number of states real(rkind),parameter :: xTol=1.e-10_rkind ! small tolerance to address precision issues real(rkind),parameter :: canIceTol=1.e-3_rkind ! small tolerance to allow existence of canopy ice for above-freezing temperatures (kg m-2) ! -------------------------------------------------------------------------------------------------------- @@ -126,23 +134,32 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU ! associate local variables with variables in the data structures associate(& + ! state variables in the canopy air space + scalarCanairTemp => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanairTemp)%dat(1) , & ! canopy air temperature (K) + scalarCanairEnthalpy => diagData%gru(iGRU)%hru(iHRU)%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) , & ! canopy air enthalpy (J m-3) ! state variables in the vegetation canopy - scalarCanopyTemp => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyTemp)%dat(1) , & ! canopy temperature - scalarCanopyIce => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyIce)%dat(1) , & ! mass of ice on the vegetation canopy (kg m-2) + scalarCanopyTemp => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyTemp)%dat(1) , & ! canopy temperature (K) + scalarCanopyEnthTemp => diagData%gru(iGRU)%hru(iHRU)%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) , & ! canopy temperature component of enthalpy (J m-3) + scalarCanopyEnthalpy => diagData%gru(iGRU)%hru(iHRU)%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) , & ! canopy enthalpy (J m-3) + scalarCanopyWat => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyWat)%dat(1) , & ! mass of water on the vegetation canopy (kg m-2) + scalarCanopyIce => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyIce)%dat(1) , & ! mass of ice on the vegetation canopy (kg m-2) ! state variables in the snow+soil domain - mLayerTemp => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerTemp)%dat , & ! temperature (K) - mLayerVolFracLiq => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracLiq)%dat , & ! volumetric fraction of liquid water in each snow layer (-) - mLayerVolFracIce => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracIce)%dat , & ! volumetric fraction of ice in each snow layer (-) - mLayerMatricHead => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerMatricHead)%dat , & ! matric head (m) - mLayerLayerType => indxData%gru(iGRU)%hru(iHRU)%var(iLookINDEX%layerType)%dat , & ! type of layer (ix_soil or ix_snow) + mLayerTemp => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerTemp)%dat , & ! temperature (K) + mLayerEnthTemp => diagData%gru(iGRU)%hru(iHRU)%var(iLookDIAG%mLayerEnthTemp)%dat , & ! temperature component of enthalpy (J m-3) + mLayerEnthalpy => diagData%gru(iGRU)%hru(iHRU)%var(iLookDIAG%mLayerEnthalpy)%dat , & ! enthalpy (J m-3) + mLayerVolFracWat => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracWat)%dat , & ! volumetric fraction of total water in each snow layer (-) + mLayerVolFracLiq => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracLiq)%dat , & ! volumetric fraction of liquid water in each snow layer (-) + mLayerVolFracIce => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracIce)%dat , & ! volumetric fraction of ice in each snow layer (-) + mLayerMatricHead => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerMatricHead)%dat , & ! matric head (m) + mLayerLayerType => indxData%gru(iGRU)%hru(iHRU)%var(iLookINDEX%layerType)%dat , & ! type of layer (ix_soil or ix_snow) ! depth varying soil properties - vGn_alpha => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%vGn_alpha)%dat , & ! van Genutchen "alpha" parameter (m-1) - vGn_n => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%vGn_n)%dat , & ! van Genutchen "n" parameter (-) - theta_sat => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%theta_sat)%dat , & ! soil porosity (-) - theta_res => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%theta_res)%dat , & ! soil residual volumetric water content (-) + vGn_alpha => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%vGn_alpha)%dat , & ! van Genutchen "alpha" parameter (m-1) + vGn_n => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%vGn_n)%dat , & ! van Genutchen "n" parameter (-) + theta_sat => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%theta_sat)%dat , & ! soil porosity (-) + theta_res => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%theta_res)%dat , & ! soil residual volumetric water content (-) ! snow parameters - snowfrz_scale => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%snowfrz_scale)%dat(1) , & ! scaling parameter for the snow freezing curve (K-1) - FCapil => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%FCapil)%dat(1) & ! fraction of pore space in tension storage (-) + snowfrz_scale => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%snowfrz_scale)%dat(1) , & ! scaling parameter for the snow freezing curve (K-1) + FCapil => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%FCapil)%dat(1) & ! fraction of pore space in tension storage (-) ) ! (associate local variables with model parameters) ! compute the constant in the freezing curve function (m K-1) @@ -159,8 +176,9 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU end if ! number of layers - nLayers = gru_struc(iGRU)%hruInfo(iHRU)%nSnow + gru_struc(iGRU)%hruInfo(iHRU)%nSoil + nSoil = gru_struc(iGRU)%hruInfo(iHRU)%nSoil nSnow = gru_struc(iGRU)%hruInfo(iHRU)%nSnow + nLayers = nSoil + nSnow ! loop through all layers do iLayer=1,nLayers @@ -259,6 +277,70 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU end do ! (looping through layers) + ! ***** + ! if the model is using enthalpy as a state variable, compute the enthalpy starting point (cold start often only has temperature) + ! *********************************************************************************************************************** + if(enthalpyStateVec)then ! enthalpy as state variable + + ! initialize indices + call indexState(.true., & ! intent(in): compute enthalpy in canopy + .false., & ! intent(in): do not compute enthalpy in aquifer + nSnow,nSoil,nLayers, & ! intent(in): number of snow and soil layers, and total number of layers + indxData%gru(iGRU)%hru(iHRU), & ! intent(inout): indices defining model states and layers + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + ! initialize state subset as full state vector + nState = indxData%gru(iGRU)%hru(iHRU)%var(iLookINDEX%nState)%dat(1) + indxData%gru(iGRU)%hru(iHRU)%var(iLookINDEX%ixMapFull2Subset)%dat = (/ (i,i=1,nState) /) + indxData%gru(iGRU)%hru(iHRU)%var(iLookINDEX%ixMapSubset2Full)%dat = (/ (i,i=1,nState) /) + indxData%gru(iGRU)%hru(iHRU)%var(iLookINDEX%ixDomainType_subset)%dat = indxData%gru(iGRU)%hru(iHRU)%var(iLookINDEX%ixDomainType)%dat + + ! compute temperature component of enthalpy from temperature + call T2enthTemp(& + use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy + ! input: data structures + diagData%gru(iGRU)%hru(iHRU), & ! intent(in): model diagnostic variables for a local HRU + mparData%gru(iGRU)%hru(iHRU), & ! intent(in): parameter data structure + indxData%gru(iGRU)%hru(iHRU), & ! intent(in): model indices + lookupData%gru(iGRU)%hru(iHRU), & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTemp, & ! intent(in): value of canopy air temperature (K) + scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) + scalarCanopyWat, & ! intent(in): value of canopy total water (kg m-2) + ! input: variables for the snow-soil domain + mLayerTemp, & ! intent(in): vector of layer temperature (K) + mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) + mLayerMatricHead, & ! intent(in): vector of total water matric potential (m) + ! output: enthalpy + scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTemp, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthTemp, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! initialize the enthalpy + scalarCanopyEnthalpy = scalarCanopyEnthTemp + mLayerEnthalpy = mLayerEnthTemp + ! compute enthalpy for current values + call enthTemp_or_enthalpy(& + ! input: data structures + .true., & ! intent(in): flag to convert enthTemp to enthalpy + diagData%gru(iGRU)%hru(iHRU), & ! intent(in): model diagnostic variables for a local HRU + indxData%gru(iGRU)%hru(iHRU), & ! intent(in): model indices + ! input: ice content change + scalarCanopyIce, & ! intent(in): value for canopy ice content (kg m-2) + mLayerVolFracIce, & ! intent(in): vector of volumetric ice water content (-) + ! input/output: enthalpy + scalarCanopyEnthalpy, & ! intent(inout): enthTemp to enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy, & ! intent(inout): enthTemp to enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + print*,scalarCanairTemp,scalarCanairEnthalpy + + end if ! (if enthalpy is the state variable) + ! end association to variables in the data structures end associate diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index c73169034..cd3e3acb9 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -1077,7 +1077,7 @@ subroutine computJacobWithPrime(& watState = ixSnowSoilHyd(iLayer) if(watstate/=integerMissing)then if(iLayer<=nSnow) aJac(:,watState) = aJac(:,watState) + aJac(:,nrgState) * dTemp_dTheta(iLayer) - if(iLayer>nSnow) aJac(:,watState) = aJac(:,watState) + aJac(:,nrgState) * dTemp_dPsi0(iLayer) + if(iLayer>nSnow) aJac(:,watState) = aJac(:,watState) + aJac(:,nrgState) * dTemp_dPsi0(iLayer-nSnow) endif aJac(:,nrgState) = aJac(:,nrgState) * dTemp_dEnthalpy(iLayer) if(ixMatrix==ixBandMatrix) aJac(ixDiag, nrgState) = aJac(ixDiag, nrgState) + 1._rkind * cj diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 12c924e2a..0c8b454f4 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1498,6 +1498,7 @@ subroutine coupled_em(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif end if + if(ixNrgConserv .ne. closedForm .and. ixNumericalMethod==ida) then ! enthalpyStateVec = .true., enthalpy as state variable ! initialize the temperature component of enthalpy scalarCanopyEnthTemp = scalarCanopyEnthalpy diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 7bc5b4d43..69d22364f 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -518,7 +518,7 @@ subroutine T2enthTemp(& ! initialize error control err=0; message="T2enthTemp/" - + ! loop through model state variables do iState=1,size(ixMapSubset2Full) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index f63329e11..d0feebdff 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -298,14 +298,15 @@ subroutine eval8summa(& if (.not.insideSUN) then call checkFeas(& ! input - stateVec, & ! intent(in): model state vector (mixed units) - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers + stateVec, & ! intent(in): model state vector (mixed units) + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + .false., & ! intent(in): currently never using enthalpy as state vector in BE ! output: feasibility - feasible, & ! intent(inout): flag to denote the feasibility of the solution + feasible, & ! intent(inout): flag to denote the feasibility of the solution ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control ! early return for non-feasible solutions if(.not.feasible)then diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index a94176e8a..98380419e 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -269,14 +269,15 @@ subroutine eval8summaWithPrime(& if (.not.insideSUN) then call checkFeas(& ! input - stateVec, & ! intent(in): model state vector (mixed units) - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers + stateVec, & ! intent(in): model state vector (mixed units) + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ixNrgConserv.ne.closedForm, & ! intent(in): flag to indicate if we are using enthalpy as state variable ! output: feasibility - feasible, & ! intent(inout): flag to denote the feasibility of the solution + feasible, & ! intent(inout): flag to denote the feasibility of the solution ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control ! early return for non-feasible solutions if(.not.feasible)then @@ -287,8 +288,7 @@ subroutine eval8summaWithPrime(& end if end if ! ( feasibility check ) - if(ixNrgConserv == enthalpyFD .or. ixNrgConserv == enthalpyFDlu)then - ! use enthalpy as state variable, do not need state terms but do need flux term + if(ixNrgConserv == enthalpyFD .or. ixNrgConserv == enthalpyFDlu)then ! use enthalpy as state variable, do not need state terms but do need flux term updateStateCp = .false. updateFluxCp = .true. needStateCm = .false. diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index e7ea46f74..53d50a906 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -418,6 +418,7 @@ subroutine checkFeas(& mpar_data, & ! intent(in): model parameters prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers + enthalpyStateVec, & ! intent(in): flag if enthalpy is state variable ! output: feasibility feasible, & ! intent(inout): flag to denote the feasibility of the solution ! output: error control @@ -430,6 +431,7 @@ subroutine checkFeas(& type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + logical(lgt),intent(in) :: enthalpyStateVec ! flag if enthalpy is state variable ! output: feasibility logical(lgt),intent(inout) :: feasible ! flag to denote the feasibility of the solution ! output: error control @@ -473,7 +475,7 @@ subroutine checkFeas(& feasible=.true. ! check that the canopy air space temperature is reasonable if(ixCasNrg/=integerMissing)then - if(stateVec(ixCasNrg) > canopyTempMax) then + if(stateVec(ixCasNrg) > canopyTempMax .and. .not.enthalpyStateVec)then feasible=.false. message=trim(message)//'canopy air space temp high' !write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixCasNrg )', feasible, canopyTempMax, stateVec(ixCasNrg) @@ -482,7 +484,7 @@ subroutine checkFeas(& ! check that the canopy air space temperature is reasonable if(ixVegNrg/=integerMissing)then - if(stateVec(ixVegNrg) > canopyTempMax)then + if(stateVec(ixVegNrg) > canopyTempMax .and. .not.enthalpyStateVec)then feasible=.false. message=trim(message)//'canopy temp high' !write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixVegNrg )', feasible, canopyTempMax, stateVec(ixVegNrg) @@ -500,7 +502,7 @@ subroutine checkFeas(& ! check snow temperature is below freezing if(count(ixSnowOnlyNrg/=integerMissing)>0)then - if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze))then + if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze) .and. .not.enthalpyStateVec)then feasible=.false. message=trim(message)//'snow temp high' !do iLayer=1,nSnow diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index a17db25b5..af33e2f79 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -1087,11 +1087,11 @@ subroutine update_fluxMask ! make sure firstFluxCall fluxes are included in the mask if (firstFluxCall .and. addFirstFlux) then - if (iVar==iLookFlux%scalarSoilResistance) desiredFlux = .true. - if (iVar==iLookFlux%scalarStomResistSunlit) desiredFlux = .true. - if (iVar==iLookFlux%scalarStomResistShaded) desiredFlux = .true. - if (iVar==iLookFlux%scalarPhotosynthesisSunlit) desiredFlux = .true. - if (iVar==iLookFlux%scalarPhotosynthesisShaded) desiredFlux = .true. + if (iVar==iLookFLUX%scalarSoilResistance) desiredFlux = .true. + if (iVar==iLookFLUX%scalarStomResistSunlit) desiredFlux = .true. + if (iVar==iLookFLUX%scalarStomResistShaded) desiredFlux = .true. + if (iVar==iLookFLUX%scalarPhotosynthesisSunlit) desiredFlux = .true. + if (iVar==iLookFLUX%scalarPhotosynthesisShaded) desiredFlux = .true. end if fluxMask%var(iVar)%dat = desiredFlux @@ -1108,11 +1108,11 @@ subroutine update_fluxMask ! make sure firstFluxCall fluxes are included in the mask if (firstFluxCall .and. addFirstFlux) then - if (iVar==iLookFlux%scalarSoilResistance) desiredFlux = .true. - if (iVar==iLookFlux%scalarStomResistSunlit) desiredFlux = .true. - if (iVar==iLookFlux%scalarStomResistShaded) desiredFlux = .true. - if (iVar==iLookFlux%scalarPhotosynthesisSunlit) desiredFlux = .true. - if (iVar==iLookFlux%scalarPhotosynthesisShaded) desiredFlux = .true. + if (iVar==iLookFLUX%scalarSoilResistance) desiredFlux = .true. + if (iVar==iLookFLUX%scalarStomResistSunlit) desiredFlux = .true. + if (iVar==iLookFLUX%scalarStomResistShaded) desiredFlux = .true. + if (iVar==iLookFLUX%scalarPhotosynthesisSunlit) desiredFlux = .true. + if (iVar==iLookFLUX%scalarPhotosynthesisShaded) desiredFlux = .true. end if if (nDomains==1) then ! no domain splitting diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 8830b87de..c4e665f46 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -67,6 +67,12 @@ module summaSolve4ida_module qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization bigBucket, & ! a big bucket (lumped aquifer model) noExplicit ! no explicit groundwater parameterization + +! look-up values for the choice of variable in energy equations (BE residual or IDA state variable) +USE mDecisions_module,only: & + closedForm, & ! use temperature + enthalpyFDlu, & ! use enthalpy with lookup tables + enthalpyFD ! use enthalpy with analytical solution ! look-up values for method used to compute derivative USE mDecisions_module,only: & @@ -482,14 +488,15 @@ subroutine summaSolve4ida(& feasible=.true. call checkFeas(& ! input - stateVec, & ! intent(in): model state vector (mixed units) - eqns_data%mpar_data, & ! intent(in): model parameters - eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU - eqns_data%indx_data, & ! intent(in): indices defining model states and layers + stateVec, & ! intent(in): model state vector (mixed units) + eqns_data%mpar_data, & ! intent(in): model parameters + eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU + eqns_data%indx_data, & ! intent(in): indices defining model states and layers + model_decisions(iLookDECISIONS%nrgConserv)%iDecision.ne.closedForm, & ! intent(in): flag to indicate if we are using enthalpy as state variable ! output: feasibility - feasible, & ! intent(inout): flag to denote the feasibility of the solution + feasible, & ! intent(inout): flag to denote the feasibility of the solution ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control ! early return for non-feasible solutions, right now will just fail if goes infeasible if(.not.feasible)then diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index 6761c7c06..be45a0714 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -362,6 +362,7 @@ subroutine summaSolve4kinsol(& eqns_data%mpar_data, & ! intent(in): model parameters eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU eqns_data%indx_data, & ! intent(in): indices defining model states and layers + .false., & ! intent(in): currently never using enthalpy as state vector in BE ! output: feasibility feasible, & ! intent(inout): flag to denote the feasibility of the solution ! output: error control diff --git a/build/source/engine/updateVarsWithPrime.f90 b/build/source/engine/updateVarsWithPrime.f90 index c82873c9f..30a5b0889 100644 --- a/build/source/engine/updateVarsWithPrime.f90 +++ b/build/source/engine/updateVarsWithPrime.f90 @@ -427,6 +427,7 @@ subroutine updateVarsWithPrime(& else dCanairTemp_dEnthalpy = 1._rkind endif + !print*, scalarCanairEnthalpyTrial, scalarCanairTempTrial, 'scalarCanairTempTrial' cycle ! no more to do on canopy air space elseif(ixDomainType==iname_veg)then if(enthalpyStateVec)then @@ -443,6 +444,7 @@ subroutine updateVarsWithPrime(& dCanopyTemp_dCanWat, & ! intent(inout): derivative of canopy temperature with canopy water err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + !print*, scalarCanopyEnthalpyTrial, scalarCanopyTempTrial, 'scalarCanopyTempTrial' else dCanopyTemp_dEnthalpy = 1._rkind dCanopyTemp_dCanWat = 1._rkind @@ -459,6 +461,7 @@ subroutine updateVarsWithPrime(& dTemp_dTheta(iLayer), & ! intent(inout): derivative of layer temperature with volumetric total water content err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + !print*, mLayerEnthalpyTrial(iLayer), mLayerTempTrial(iLayer), 'mLayerTempTrial snow' else dTemp_dEnthalpy(iLayer) = 1._rkind dTemp_dTheta(iLayer) = 1._rkind @@ -484,6 +487,7 @@ subroutine updateVarsWithPrime(& dTemp_dPsi0(ixControlIndex), & ! intent(inout): derivative of layer temperature with total water matric potential err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + !print*, mLayerEnthalpyTrial(iLayer), mLayerTempTrial(iLayer), 'mLayerTempTrial soil' else dTemp_dEnthalpy(iLayer) = 1._rkind dTemp_dTheta(iLayer) = 1._rkind diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 595e9f638..c01e0a47c 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -547,7 +547,7 @@ subroutine varSubstep(& do ixLayer=ixMin(1),ixMax(1) if(fluxMask%var(iVar)%dat(ixLayer)) then ! special case of the transpiration sink from soil layers: only computed for the top soil layer - if(iVar==iLookFlux%mLayerTranspire)then + if(iVar==iLookFLUX%mLayerTranspire)then if(ixLayer==1) flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght ! standard case else diff --git a/build/source/netcdf/def_output.f90 b/build/source/netcdf/def_output.f90 index 7305e0c46..95c33fd97 100644 --- a/build/source/netcdf/def_output.f90 +++ b/build/source/netcdf/def_output.f90 @@ -269,7 +269,7 @@ end subroutine put_attrib subroutine def_variab(ncid,iFreq,spatialDesire,timeDesire,metaData,ivtype,err,message) USE var_lookup,only:iLookvarType ! look up structure for variable typed USE data_types,only:var_info ! derived type for metaData - USE var_lookup,only:iLookStat ! index into stats structure + USE var_lookup,only:iLookSTAT ! index into stats structure USE var_lookup,only:maxVarFreq ! # of available output frequencies USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages USE get_ixname_module,only:get_statName ! statistics names for variable defs in output file @@ -366,7 +366,7 @@ subroutine def_variab(ncid,iFreq,spatialDesire,timeDesire,metaData,ivtype,err,me iStat = metaData(iVar)%statIndex(iFreq) ! create full variable name (append statistics info) - if(iStat==iLookStat%inst)then + if(iStat==iLookSTAT%inst)then catName = trim(metaData(iVar)%varName) else catName = trim(metaData(iVar)%varName)//'_'//trim(get_statName(iStat)) @@ -386,7 +386,7 @@ subroutine def_variab(ncid,iFreq,spatialDesire,timeDesire,metaData,ivtype,err,me ! modify units for the summation catName = trim(metaData(iVar)%varunit) - if (iStat==iLookStat%totl) then + if (iStat==iLookSTAT%totl) then ! make sure that the units of this variable allow for integration if ((index(catName,'s-1')<=0).and.(index(catName,'s-2')<=0).and.(index(catName,'W m-2')<=0)) then diff --git a/build/source/netcdf/modelwrite.f90 b/build/source/netcdf/modelwrite.f90 index d66e0df02..d6e236c84 100644 --- a/build/source/netcdf/modelwrite.f90 +++ b/build/source/netcdf/modelwrite.f90 @@ -86,8 +86,8 @@ module modelwrite_module subroutine writeParm(ispatial,struct,meta,err,message) USE globalData,only:ncid ! netcdf file ids USE data_types,only:var_info ! metadata info - USE var_lookup,only:iLookStat ! index in statistics vector - USE var_lookup,only:iLookFreq ! index in vector of model output frequencies + USE var_lookup,only:iLookSTAT ! index in statistics vector + USE var_lookup,only:iLookFREQ ! index in vector of model output frequencies implicit none ! declare input variables @@ -115,13 +115,13 @@ subroutine writeParm(ispatial,struct,meta,err,message) if (iSpatial/=integerMissing) then select type (struct) class is (var_i) - err = nf90_put_var(ncid(iLookFreq%timestep),meta(iVar)%ncVarID(iLookFreq%timestep),(/struct%var(iVar)/),start=(/iSpatial/),count=(/1/)) + err = nf90_put_var(ncid(iLookFREQ%timestep),meta(iVar)%ncVarID(iLookFREQ%timestep),(/struct%var(iVar)/),start=(/iSpatial/),count=(/1/)) class is (var_i8) - err = nf90_put_var(ncid(iLookFreq%timestep),meta(iVar)%ncVarID(iLookFreq%timestep),(/struct%var(iVar)/),start=(/iSpatial/),count=(/1/)) + err = nf90_put_var(ncid(iLookFREQ%timestep),meta(iVar)%ncVarID(iLookFREQ%timestep),(/struct%var(iVar)/),start=(/iSpatial/),count=(/1/)) class is (var_d) - err = nf90_put_var(ncid(iLookFreq%timestep),meta(iVar)%ncVarID(iLookFreq%timestep),(/struct%var(iVar)/),start=(/iSpatial/),count=(/1/)) + err = nf90_put_var(ncid(iLookFREQ%timestep),meta(iVar)%ncVarID(iLookFREQ%timestep),(/struct%var(iVar)/),start=(/iSpatial/),count=(/1/)) class is (var_dlength) - err = nf90_put_var(ncid(iLookFreq%timestep),meta(iVar)%ncVarID(iLookFreq%timestep),(/struct%var(iVar)%dat/),start=(/iSpatial,1/),count=(/1,size(struct%var(iVar)%dat)/)) + err = nf90_put_var(ncid(iLookFREQ%timestep),meta(iVar)%ncVarID(iLookFREQ%timestep),(/struct%var(iVar)%dat/),start=(/iSpatial,1/),count=(/1,size(struct%var(iVar)%dat)/)) class default; err=20; message=trim(message)//'unknown variable type (with HRU)'; return end select call netcdf_err(err,message); if (err/=0) return @@ -130,9 +130,9 @@ subroutine writeParm(ispatial,struct,meta,err,message) else select type (struct) class is (var_d) - err = nf90_put_var(ncid(iLookFreq%timestep),meta(iVar)%ncVarID(iLookFreq%timestep),(/struct%var(iVar)/),start=(/1/),count=(/1/)) + err = nf90_put_var(ncid(iLookFREQ%timestep),meta(iVar)%ncVarID(iLookFREQ%timestep),(/struct%var(iVar)/),start=(/1/),count=(/1/)) class is (var_i8) - err = nf90_put_var(ncid(iLookFreq%timestep),meta(iVar)%ncVarID(iLookFreq%timestep),(/struct%var(iVar)/),start=(/1/),count=(/1/)) + err = nf90_put_var(ncid(iLookFREQ%timestep),meta(iVar)%ncVarID(iLookFREQ%timestep),(/struct%var(iVar)/),start=(/1/),count=(/1/)) class default; err=20; message=trim(message)//'unknown variable type (no HRU)'; return end select end if @@ -151,8 +151,8 @@ subroutine writeData(finalizeStats,outputTimestep,nHRUrun,maxLayers,meta,stat,da USE data_types,only:var_info ! metadata type USE var_lookup,only:maxVarStat ! index into stats structure USE var_lookup,only:iLookVarType ! index into type structure - USE var_lookup,only:iLookIndex ! index into index structure - USE var_lookup,only:iLookStat ! index into stat structure + USE var_lookup,only:iLookINDEX ! index into index structure + USE var_lookup,only:iLookSTAT ! index into stat structure USE globalData,only:outFreq,ncid ! output file information USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages USE get_ixName_module,only:get_statName ! to access type strings for error messages @@ -259,9 +259,9 @@ subroutine writeData(finalizeStats,outputTimestep,nHRUrun,maxLayers,meta,stat,da do iHRU=1,gru_struc(iGRU)%hruCount ! get the model layers - nSoil = indx%gru(iGRU)%hru(iHRU)%var(iLookIndex%nSoil)%dat(1) - nSnow = indx%gru(iGRU)%hru(iHRU)%var(iLookIndex%nSnow)%dat(1) - nLayers = indx%gru(iGRU)%hru(iHRU)%var(iLookIndex%nLayers)%dat(1) + nSoil = indx%gru(iGRU)%hru(iHRU)%var(iLookINDEX%nSoil)%dat(1) + nSnow = indx%gru(iGRU)%hru(iHRU)%var(iLookINDEX%nSnow)%dat(1) + nLayers = indx%gru(iGRU)%hru(iHRU)%var(iLookINDEX%nLayers)%dat(1) ! get the length of each data vector select case (meta(iVar)%varType) @@ -392,7 +392,7 @@ end subroutine writeBasin subroutine writeTime(finalizeStats,outputTimestep,meta,dat,err,message) USE data_types,only:var_info ! metadata type USE globalData,only:ncid ! output file IDs - USE var_lookup,only:iLookStat ! index into stat structure + USE var_lookup,only:iLookSTAT ! index into stat structure implicit none ! declare dummy variables @@ -419,7 +419,7 @@ subroutine writeTime(finalizeStats,outputTimestep,meta,dat,err,message) do iVar = 1,size(meta) ! check instantaneous - if (meta(iVar)%statIndex(iFreq)/=iLookStat%inst) cycle + if (meta(iVar)%statIndex(iFreq)/=iLookSTAT%inst) cycle ! get variable id in file err = nf90_inq_varid(ncid(iFreq),trim(meta(iVar)%varName),ncVarID) @@ -605,14 +605,14 @@ subroutine writeRestart(filename, & ! intent(in): name of restart file err = nf90_put_att(ncid,ncVarID(nProgVars+1),'units' ,trim(bvar_meta(iLookBVAR%routingRunoffFuture)%varunit)); call netcdf_err(err,message) ! define index variables - snow - err = nf90_def_var(ncid,trim(indx_meta(iLookIndex%nSnow)%varName),nf90_int,(/hruDimID/),ncSnowID); call netcdf_err(err,message) - err = nf90_put_att(ncid,ncSnowID,'long_name',trim(indx_meta(iLookIndex%nSnow)%vardesc)); call netcdf_err(err,message) - err = nf90_put_att(ncid,ncSnowID,'units' ,trim(indx_meta(iLookIndex%nSnow)%varunit)); call netcdf_err(err,message) + err = nf90_def_var(ncid,trim(indx_meta(iLookINDEX%nSnow)%varName),nf90_int,(/hruDimID/),ncSnowID); call netcdf_err(err,message) + err = nf90_put_att(ncid,ncSnowID,'long_name',trim(indx_meta(iLookINDEX%nSnow)%vardesc)); call netcdf_err(err,message) + err = nf90_put_att(ncid,ncSnowID,'units' ,trim(indx_meta(iLookINDEX%nSnow)%varunit)); call netcdf_err(err,message) ! define index variables - soil - err = nf90_def_var(ncid,trim(indx_meta(iLookIndex%nSoil)%varName),nf90_int,(/hruDimID/),ncSoilID); call netcdf_err(err,message) - err = nf90_put_att(ncid,ncSoilID,'long_name',trim(indx_meta(iLookIndex%nSoil)%vardesc)); call netcdf_err(err,message) - err = nf90_put_att(ncid,ncSoilID,'units' ,trim(indx_meta(iLookIndex%nSoil)%varunit)); call netcdf_err(err,message) + err = nf90_def_var(ncid,trim(indx_meta(iLookINDEX%nSoil)%varName),nf90_int,(/hruDimID/),ncSoilID); call netcdf_err(err,message) + err = nf90_put_att(ncid,ncSoilID,'long_name',trim(indx_meta(iLookINDEX%nSoil)%vardesc)); call netcdf_err(err,message) + err = nf90_put_att(ncid,ncSoilID,'units' ,trim(indx_meta(iLookINDEX%nSoil)%varunit)); call netcdf_err(err,message) ! end definition phase err = nf90_enddef(ncid); call netcdf_err(err,message); if (err/=0) return @@ -673,8 +673,8 @@ subroutine writeRestart(filename, & ! intent(in): name of restart file end do ! iVar loop ! write index variables - err=nf90_put_var(ncid,ncSnowID,(/indx_data%gru(iGRU)%hru(iHRU)%var(iLookIndex%nSnow)%dat/),start=(/cHRU/),count=(/1/)) - err=nf90_put_var(ncid,ncSoilID,(/indx_data%gru(iGRU)%hru(iHRU)%var(iLookIndex%nSoil)%dat/),start=(/cHRU/),count=(/1/)) + err=nf90_put_var(ncid,ncSnowID,(/indx_data%gru(iGRU)%hru(iHRU)%var(iLookINDEX%nSnow)%dat/),start=(/cHRU/),count=(/1/)) + err=nf90_put_var(ncid,ncSoilID,(/indx_data%gru(iGRU)%hru(iHRU)%var(iLookINDEX%nSoil)%dat/),start=(/cHRU/),count=(/1/)) end do ! iHRU loop diff --git a/build/source/netcdf/read_icond.f90 b/build/source/netcdf/read_icond.f90 index d7634b8bb..0724dc074 100644 --- a/build/source/netcdf/read_icond.f90 +++ b/build/source/netcdf/read_icond.f90 @@ -40,7 +40,7 @@ subroutine read_icond_nlayers(iconFile,nGRU,indx_meta,err,message) ! -------------------------------------------------------------------------------------------------------- ! modules USE nrtype - USE var_lookup,only:iLookIndex ! variable lookup structure + USE var_lookup,only:iLookINDEX ! variable lookup structure USE globalData,only:gru_struc ! gru-hru mapping structures USE globalData,only:startGRU ! index of first gru for parallel runs USE netcdf_util_module,only:nc_file_close ! close netcdf file @@ -90,8 +90,8 @@ subroutine read_icond_nlayers(iconFile,nGRU,indx_meta,err,message) soilData = 0 ! get netcdf ids for the variables holding number of snow and soil layers in each hru - err = nf90_inq_varid(ncid,trim(indx_meta(iLookIndex%nSnow)%varName),snowid); call netcdf_err(err,message) - err = nf90_inq_varid(ncid,trim(indx_meta(iLookIndex%nSoil)%varName),soilid); call netcdf_err(err,message) + err = nf90_inq_varid(ncid,trim(indx_meta(iLookINDEX%nSnow)%varName),snowid); call netcdf_err(err,message) + err = nf90_inq_varid(ncid,trim(indx_meta(iLookINDEX%nSoil)%varName),soilid); call netcdf_err(err,message) ! get nSnow and nSoil data (reads entire state file) err = nf90_get_var(ncid,snowid,snowData); call netcdf_err(err,message) diff --git a/utils/wallStd_per_GRU.py b/utils/wallStd_per_GRU.py index 0355ae316..0e52ec402 100644 --- a/utils/wallStd_per_GRU.py +++ b/utils/wallStd_per_GRU.py @@ -86,7 +86,7 @@ def run_loop(i,var,comp,leg_t,leg_t0,plt_t,stat): correlation_xy = correlation_matrix[0,1] r_squared = correlation_xy**2 # Add the R-squared value to the plot - axs[c].text(0.05, 0.9-0.03*mm, f'{m} R² = {r_squared:.2f}', transform=axs[c].transAxes) + axs[c].text(0.8, 0.5-0.03*mm, f'{m} R² = {r_squared:.2f}', transform=axs[c].transAxes) print(m,stat,'Coefficients:', coefficients, 'R-squared:', r_squared) From 42fa045e91d48249816538a1910fbf190f780c0a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 8 Apr 2024 16:27:47 +0900 Subject: [PATCH 1244/1472] capital letter mismatches, no code changes --- build/source/dshare/get_ixname.f90 | 1156 ++++++++++++++-------------- build/source/dshare/popMetadat.f90 | 2 +- build/source/engine/ffile_info.f90 | 4 +- build/source/engine/read_force.f90 | 2 +- build/source/engine/read_param.f90 | 6 +- 5 files changed, 585 insertions(+), 585 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 71aab348f..889ed195e 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -24,19 +24,19 @@ module get_ixname_module implicit none private public::get_ixdecisions -public::get_ixtime -public::get_ixattr -public::get_ixtype -public::get_ixid -public::get_ixforce -public::get_ixparam -public::get_ixprog -public::get_ixdiag -public::get_ixflux -public::get_ixderiv -public::get_ixindex -public::get_ixbpar -public::get_ixbvar +public::get_ixTime +public::get_ixAttr +public::get_ixType +public::get_ixId +public::get_ixForce +public::get_ixParam +public::get_ixProg +public::get_ixDiag +public::get_ixFlux +public::get_ixDeriv +public::get_ixIndex +public::get_ixBpar +public::get_ixBvar public::get_ixVarType public::get_varTypeName public::get_ixUnknown @@ -104,54 +104,54 @@ end function get_ixdecisions ! ******************************************************************************************************************* - ! public function get_ixtime: get the index of the named variables for the model time + ! public function get_ixTime: get the index of the named variables for the model time ! ******************************************************************************************************************* - function get_ixtime(varName) + function get_ixTime(varName) USE var_lookup,only:iLookTIME ! indices of the named variables implicit none ! define dummy variables character(*), intent(in) :: varName ! variable name - integer(i4b) :: get_ixtime ! index of the named variable + integer(i4b) :: get_ixTime ! index of the named variable ! get the index of the named variables select case(trim(varName)) - case('iyyy' ); get_ixtime = iLookTIME%iyyy ! year - case('im' ); get_ixtime = iLookTIME%im ! month - case('id' ); get_ixtime = iLookTIME%id ! day - case('ih' ); get_ixtime = iLookTIME%ih ! hour - case('imin' ); get_ixtime = iLookTIME%imin ! minute - case('ih_tz' ); get_ixtime = iLookTIME%ih_tz ! hour for time zone offset - case('imin_tz' ); get_ixtime = iLookTIME%imin_tz ! minute for time zone offset + case('iyyy' ); get_ixTime = iLookTIME%iyyy ! year + case('im' ); get_ixTime = iLookTIME%im ! month + case('id' ); get_ixTime = iLookTIME%id ! day + case('ih' ); get_ixTime = iLookTIME%ih ! hour + case('imin' ); get_ixTime = iLookTIME%imin ! minute + case('ih_tz' ); get_ixTime = iLookTIME%ih_tz ! hour for time zone offset + case('imin_tz' ); get_ixTime = iLookTIME%imin_tz ! minute for time zone offset ! get to here if cannot find the variable case default - get_ixtime = integerMissing + get_ixTime = integerMissing end select - end function get_ixtime + end function get_ixTime ! ******************************************************************************************************************* - ! public function get_ixforce: get the index of the named variables for the model forcing data + ! public function get_ixForce: get the index of the named variables for the model forcing data ! ******************************************************************************************************************* - function get_ixforce(varName) + function get_ixForce(varName) USE var_lookup,only:iLookFORCE ! indices of the named variables implicit none ! define dummy variables character(*), intent(in) :: varName ! variable name - integer(i4b) :: get_ixforce ! index of the named variable + integer(i4b) :: get_ixForce ! index of the named variable ! get the index of the named variables select case(trim(varName)) - case('time' ); get_ixforce = iLookFORCE%time ! time since time reference (s) - case('pptrate' ); get_ixforce = iLookFORCE%pptrate ! precipitation rate (kg m-2 s-1) - case('airtemp' ); get_ixforce = iLookFORCE%airtemp ! air temperature (K) - case('spechum' ); get_ixforce = iLookFORCE%spechum ! specific humidity (g/g) - case('windspd' ); get_ixforce = iLookFORCE%windspd ! windspeed (m/s) - case('SWRadAtm' ); get_ixforce = iLookFORCE%SWRadAtm ! downwelling shortwave radiaiton (W m-2) - case('LWRadAtm' ); get_ixforce = iLookFORCE%LWRadAtm ! downwelling longwave radiation (W m-2) - case('airpres' ); get_ixforce = iLookFORCE%airpres ! pressure (Pa) + case('time' ); get_ixForce = iLookFORCE%time ! time since time reference (s) + case('pptrate' ); get_ixForce = iLookFORCE%pptrate ! precipitation rate (kg m-2 s-1) + case('airtemp' ); get_ixForce = iLookFORCE%airtemp ! air temperature (K) + case('spechum' ); get_ixForce = iLookFORCE%spechum ! specific humidity (g/g) + case('windspd' ); get_ixForce = iLookFORCE%windspd ! windspeed (m/s) + case('SWRadAtm' ); get_ixForce = iLookFORCE%SWRadAtm ! downwelling shortwave radiaiton (W m-2) + case('LWRadAtm' ); get_ixForce = iLookFORCE%LWRadAtm ! downwelling longwave radiation (W m-2) + case('airpres' ); get_ixForce = iLookFORCE%airpres ! pressure (Pa) ! get to here if cannot find the variable case default - get_ixforce = integerMissing + get_ixForce = integerMissing end select - end function get_ixforce + end function get_ixForce ! ******************************************************************************************************************* @@ -222,638 +222,638 @@ end function get_ixId ! ******************************************************************************************************************* - ! public function get_ixparam: get the index of the named variables for the model parameters + ! public function get_ixParam: get the index of the named variables for the model parameters ! ******************************************************************************************************************* - function get_ixparam(varName) + function get_ixParam(varName) USE var_lookup,only:iLookPARAM ! indices of the named variables implicit none ! define dummy variables character(*), intent(in) :: varName ! variable name - integer(i4b) :: get_ixparam ! index of the named variable + integer(i4b) :: get_ixParam ! index of the named variable ! get the index of the named variables select case(trim(varName)) ! boundary conditions - case('upperBoundHead' ); get_ixparam = iLookPARAM%upperBoundHead ! matric head of the upper boundary (m) - case('lowerBoundHead' ); get_ixparam = iLookPARAM%lowerBoundHead ! matric head of the lower boundary (m) - case('upperBoundTheta' ); get_ixparam = iLookPARAM%upperBoundTheta ! volumetric liquid water content at the upper boundary (-) - case('lowerBoundTheta' ); get_ixparam = iLookPARAM%lowerBoundTheta ! volumetric liquid water content at the lower boundary (-) - case('upperBoundTemp' ); get_ixparam = iLookPARAM%upperBoundTemp ! temperature of the upper boundary (K) - case('lowerBoundTemp' ); get_ixparam = iLookPARAM%lowerBoundTemp ! temperature of the lower boundary (K) + case('upperBoundHead' ); get_ixParam = iLookPARAM%upperBoundHead ! matric head of the upper boundary (m) + case('lowerBoundHead' ); get_ixParam = iLookPARAM%lowerBoundHead ! matric head of the lower boundary (m) + case('upperBoundTheta' ); get_ixParam = iLookPARAM%upperBoundTheta ! volumetric liquid water content at the upper boundary (-) + case('lowerBoundTheta' ); get_ixParam = iLookPARAM%lowerBoundTheta ! volumetric liquid water content at the lower boundary (-) + case('upperBoundTemp' ); get_ixParam = iLookPARAM%upperBoundTemp ! temperature of the upper boundary (K) + case('lowerBoundTemp' ); get_ixParam = iLookPARAM%lowerBoundTemp ! temperature of the lower boundary (K) ! precipitation partitioning - case('tempCritRain' ); get_ixparam = iLookPARAM%tempCritRain ! critical temperature where precipitation is rain (K) - case('tempRangeTimestep' ); get_ixparam = iLookPARAM%tempRangeTimestep ! temperature range over the time step (K) - case('frozenPrecipMultip' ); get_ixparam = iLookPARAM%frozenPrecipMultip ! frozen precipitation multiplier (-) + case('tempCritRain' ); get_ixParam = iLookPARAM%tempCritRain ! critical temperature where precipitation is rain (K) + case('tempRangeTimestep' ); get_ixParam = iLookPARAM%tempRangeTimestep ! temperature range over the time step (K) + case('frozenPrecipMultip' ); get_ixParam = iLookPARAM%frozenPrecipMultip ! frozen precipitation multiplier (-) ! freezing curve for snow - case('snowfrz_scale' ); get_ixparam = iLookPARAM%snowfrz_scale ! scaling parameter for the freezing curve for snow (K-1) - case('fixedThermalCond_snow' ); get_ixparam = iLookPARAM%fixedThermalCond_snow ! temporally constant thermal conductivity for snow (W m-1 K-1) + case('snowfrz_scale' ); get_ixParam = iLookPARAM%snowfrz_scale ! scaling parameter for the freezing curve for snow (K-1) + case('fixedThermalCond_snow' ); get_ixParam = iLookPARAM%fixedThermalCond_snow ! temporally constant thermal conductivity for snow (W m-1 K-1) ! snow albedo - case('albedoMax' ); get_ixparam = iLookPARAM%albedoMax ! maximum snow albedo for a single spectral band (-) - case('albedoMinWinter' ); get_ixparam = iLookPARAM%albedoMinWinter ! minimum snow albedo during winter for a single spectral band (-) - case('albedoMinSpring' ); get_ixparam = iLookPARAM%albedoMinSpring ! minimum snow albedo during spring for a single spectral band (-) - case('albedoMaxVisible' ); get_ixparam = iLookPARAM%albedoMaxVisible ! maximum snow albedo in the visible part of the spectrum (-) - case('albedoMinVisible' ); get_ixparam = iLookPARAM%albedoMinVisible ! minimum snow albedo in the visible part of the spectrum (-) - case('albedoMaxNearIR' ); get_ixparam = iLookPARAM%albedoMaxNearIR ! maximum snow albedo in the near infra-red part of the spectrum (-) - case('albedoMinNearIR' ); get_ixparam = iLookPARAM%albedoMinNearIR ! minimum snow albedo in the near infra-red part of the spectrum (-) - case('albedoDecayRate' ); get_ixparam = iLookPARAM%albedoDecayRate ! albedo decay rate (s) - case('albedoSootLoad' ); get_ixparam = iLookPARAM%albedoSootLoad ! soot load factor (-) - case('albedoRefresh' ); get_ixparam = iLookPARAM%albedoRefresh ! critical mass necessary for albedo refreshment (kg m-2) + case('albedoMax' ); get_ixParam = iLookPARAM%albedoMax ! maximum snow albedo for a single spectral band (-) + case('albedoMinWinter' ); get_ixParam = iLookPARAM%albedoMinWinter ! minimum snow albedo during winter for a single spectral band (-) + case('albedoMinSpring' ); get_ixParam = iLookPARAM%albedoMinSpring ! minimum snow albedo during spring for a single spectral band (-) + case('albedoMaxVisible' ); get_ixParam = iLookPARAM%albedoMaxVisible ! maximum snow albedo in the visible part of the spectrum (-) + case('albedoMinVisible' ); get_ixParam = iLookPARAM%albedoMinVisible ! minimum snow albedo in the visible part of the spectrum (-) + case('albedoMaxNearIR' ); get_ixParam = iLookPARAM%albedoMaxNearIR ! maximum snow albedo in the near infra-red part of the spectrum (-) + case('albedoMinNearIR' ); get_ixParam = iLookPARAM%albedoMinNearIR ! minimum snow albedo in the near infra-red part of the spectrum (-) + case('albedoDecayRate' ); get_ixParam = iLookPARAM%albedoDecayRate ! albedo decay rate (s) + case('albedoSootLoad' ); get_ixParam = iLookPARAM%albedoSootLoad ! soot load factor (-) + case('albedoRefresh' ); get_ixParam = iLookPARAM%albedoRefresh ! critical mass necessary for albedo refreshment (kg m-2) ! radiation transfer - case('radExt_snow' ); get_ixparam = iLookPARAM%radExt_snow ! extinction coefficient for radiation penetration within the snowpack (m-1) - case('directScale' ); get_ixparam = iLookPARAM%directScale ! scaling factor for fractional driect radiaion parameterization (-) - case('Frad_direct' ); get_ixparam = iLookPARAM%Frad_direct ! maximum fraction of direct radiation (-) - case('Frad_vis' ); get_ixparam = iLookPARAM%Frad_vis ! fraction of radiation in the visible part of the spectrum (-) + case('radExt_snow' ); get_ixParam = iLookPARAM%radExt_snow ! extinction coefficient for radiation penetration within the snowpack (m-1) + case('directScale' ); get_ixParam = iLookPARAM%directScale ! scaling factor for fractional driect radiaion parameterization (-) + case('Frad_direct' ); get_ixParam = iLookPARAM%Frad_direct ! maximum fraction of direct radiation (-) + case('Frad_vis' ); get_ixParam = iLookPARAM%Frad_vis ! fraction of radiation in the visible part of the spectrum (-) ! new snow density - case('newSnowDenMin' ); get_ixparam = iLookPARAM%newSnowDenMin ! minimum new snow density (kg m-3) - case('newSnowDenMult' ); get_ixparam = iLookPARAM%newSnowDenMult ! multiplier for new snow density (kg m-3) - case('newSnowDenScal' ); get_ixparam = iLookPARAM%newSnowDenScal ! scaling factor for new snow density (K) - case('constSnowDen' ); get_ixparam = iLookPARAM%constSnowDen ! Constant new snow density (kg m-3) - case('newSnowDenAdd' ); get_ixparam = iLookPARAM%newSnowDenAdd ! Pahaut 1976, additive factor for new snow density (kg m-3) - case('newSnowDenMultTemp' ); get_ixparam = iLookPARAM%newSnowDenMultTemp ! Pahaut 1976, multiplier for new snow density applied to air temperature (kg m-3 K-1) - case('newSnowDenMultWind' ); get_ixparam = iLookPARAM%newSnowDenMultWind ! Pahaut 1976, multiplier for new snow density applied to wind speed (kg m-7/2 s-1/2) - case('newSnowDenMultAnd' ); get_ixparam = iLookPARAM%newSnowDenMultAnd ! Anderson 1976, multiplier for new snow density for Anderson function (K-1) - case('newSnowDenBase' ); get_ixparam = iLookPARAM%newSnowDenBase ! Anderson 1976, base value that is rasied to the (3/2) power (K) + case('newSnowDenMin' ); get_ixParam = iLookPARAM%newSnowDenMin ! minimum new snow density (kg m-3) + case('newSnowDenMult' ); get_ixParam = iLookPARAM%newSnowDenMult ! multiplier for new snow density (kg m-3) + case('newSnowDenScal' ); get_ixParam = iLookPARAM%newSnowDenScal ! scaling factor for new snow density (K) + case('constSnowDen' ); get_ixParam = iLookPARAM%constSnowDen ! Constant new snow density (kg m-3) + case('newSnowDenAdd' ); get_ixParam = iLookPARAM%newSnowDenAdd ! Pahaut 1976, additive factor for new snow density (kg m-3) + case('newSnowDenMultTemp' ); get_ixParam = iLookPARAM%newSnowDenMultTemp ! Pahaut 1976, multiplier for new snow density applied to air temperature (kg m-3 K-1) + case('newSnowDenMultWind' ); get_ixParam = iLookPARAM%newSnowDenMultWind ! Pahaut 1976, multiplier for new snow density applied to wind speed (kg m-7/2 s-1/2) + case('newSnowDenMultAnd' ); get_ixParam = iLookPARAM%newSnowDenMultAnd ! Anderson 1976, multiplier for new snow density for Anderson function (K-1) + case('newSnowDenBase' ); get_ixParam = iLookPARAM%newSnowDenBase ! Anderson 1976, base value that is rasied to the (3/2) power (K) ! snow compaction - case('densScalGrowth' ); get_ixparam = iLookPARAM%densScalGrowth ! density scaling factor for grain growth (kg-1 m3) - case('tempScalGrowth' ); get_ixparam = iLookPARAM%tempScalGrowth ! temperature scaling factor for grain growth (K-1) - case('grainGrowthRate' ); get_ixparam = iLookPARAM%grainGrowthRate ! rate of grain growth (s-1) - case('densScalOvrbdn' ); get_ixparam = iLookPARAM%densScalOvrbdn ! density scaling factor for overburden pressure (kg-1 m3) - case('tempScalOvrbdn' ); get_ixparam = iLookPARAM%tempScalOvrbdn ! temperature scaling factor for overburden pressure (K-1) - case('baseViscosity' ); get_ixparam = iLookPARAM%baseViscosity ! viscosity coefficient at T=T_frz and snow density=0 (kg s m-2) + case('densScalGrowth' ); get_ixParam = iLookPARAM%densScalGrowth ! density scaling factor for grain growth (kg-1 m3) + case('tempScalGrowth' ); get_ixParam = iLookPARAM%tempScalGrowth ! temperature scaling factor for grain growth (K-1) + case('grainGrowthRate' ); get_ixParam = iLookPARAM%grainGrowthRate ! rate of grain growth (s-1) + case('densScalOvrbdn' ); get_ixParam = iLookPARAM%densScalOvrbdn ! density scaling factor for overburden pressure (kg-1 m3) + case('tempScalOvrbdn' ); get_ixParam = iLookPARAM%tempScalOvrbdn ! temperature scaling factor for overburden pressure (K-1) + case('baseViscosity' ); get_ixParam = iLookPARAM%baseViscosity ! viscosity coefficient at T=T_frz and snow density=0 (kg s m-2) ! water flow through snow - case('Fcapil' ); get_ixparam = iLookPARAM%Fcapil ! capillary retention as a fraction of the total pore volume (-) - case('k_snow' ); get_ixparam = iLookPARAM%k_snow ! hydraulic conductivity of snow (m s-1), 0.0055 = approx. 20 m/hr, from UEB - case('mw_exp' ); get_ixparam = iLookPARAM%mw_exp ! exponent for meltwater flow (-) + case('Fcapil' ); get_ixParam = iLookPARAM%Fcapil ! capillary retention as a fraction of the total pore volume (-) + case('k_snow' ); get_ixParam = iLookPARAM%k_snow ! hydraulic conductivity of snow (m s-1), 0.0055 = approx. 20 m/hr, from UEB + case('mw_exp' ); get_ixParam = iLookPARAM%mw_exp ! exponent for meltwater flow (-) ! turbulent heat fluxes - case('z0Snow' ); get_ixparam = iLookPARAM%z0Snow ! roughness length of snow (m) - case('z0Soil' ); get_ixparam = iLookPARAM%z0Soil ! roughness length of bare soil below the canopy (m) - case('z0Canopy' ); get_ixparam = iLookPARAM%z0Canopy ! roughness length of the canopy (m) - case('zpdFraction' ); get_ixparam = iLookPARAM%zpdFraction ! zero plane displacement / canopy height (-) - case('critRichNumber' ); get_ixparam = iLookPARAM%critRichNumber ! critical value for the bulk Richardson number (-) - case('Louis79_bparam' ); get_ixparam = iLookPARAM%Louis79_bparam ! parameter in Louis (1979) stability function (-) - case('Louis79_cStar' ); get_ixparam = iLookPARAM%Louis79_cStar ! parameter in Louis (1979) stability function (-) - case('Mahrt87_eScale' ); get_ixparam = iLookPARAM%Mahrt87_eScale ! exponential scaling factor in the Mahrt (1987) stability function (-) - case('leafExchangeCoeff' ); get_ixparam = iLookPARAM%leafExchangeCoeff ! turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) - case('windReductionParam' ); get_ixparam = iLookPARAM%windReductionParam ! canopy wind reduction parameter (-) + case('z0Snow' ); get_ixParam = iLookPARAM%z0Snow ! roughness length of snow (m) + case('z0Soil' ); get_ixParam = iLookPARAM%z0Soil ! roughness length of bare soil below the canopy (m) + case('z0Canopy' ); get_ixParam = iLookPARAM%z0Canopy ! roughness length of the canopy (m) + case('zpdFraction' ); get_ixParam = iLookPARAM%zpdFraction ! zero plane displacement / canopy height (-) + case('critRichNumber' ); get_ixParam = iLookPARAM%critRichNumber ! critical value for the bulk Richardson number (-) + case('Louis79_bparam' ); get_ixParam = iLookPARAM%Louis79_bparam ! parameter in Louis (1979) stability function (-) + case('Louis79_cStar' ); get_ixParam = iLookPARAM%Louis79_cStar ! parameter in Louis (1979) stability function (-) + case('Mahrt87_eScale' ); get_ixParam = iLookPARAM%Mahrt87_eScale ! exponential scaling factor in the Mahrt (1987) stability function (-) + case('leafExchangeCoeff' ); get_ixParam = iLookPARAM%leafExchangeCoeff ! turbulent exchange coeff between canopy surface and canopy air ( m s-(1/2) ) + case('windReductionParam' ); get_ixParam = iLookPARAM%windReductionParam ! canopy wind reduction parameter (-) ! stomatal conductance - case('Kc25' ); get_ixparam = iLookPARAM%Kc25 ! Michaelis-Menten constant for CO2 at 25 degrees C (umol mol-1) - case('Ko25' ); get_ixparam = iLookPARAM%Ko25 ! Michaelis-Menten constant for O2 at 25 degrees C (mol mol-1) - case('Kc_qFac' ); get_ixparam = iLookPARAM%Kc_qFac ! factor in the q10 function defining temperature controls on Kc (-) - case('Ko_qFac' ); get_ixparam = iLookPARAM%Ko_qFac ! factor in the q10 function defining temperature controls on Ko (-) - case('kc_Ha' ); get_ixparam = iLookPARAM%kc_Ha ! activation energy for the Michaelis-Menten constant for CO2 (J mol-1) - case('ko_Ha' ); get_ixparam = iLookPARAM%ko_Ha ! activation energy for the Michaelis-Menten constant for CO2 (J mol-1) - case('vcmax25_canopyTop' ); get_ixparam = iLookPARAM%vcmax25_canopyTop ! potential carboxylation rate at 25 degrees C at the canopy top (umol co2 m-2 s-1) - case('vcmax_qFac' ); get_ixparam = iLookPARAM%vcmax_qFac ! factor in the q10 function defining temperature controls on vcmax (-) - case('vcmax_Ha' ); get_ixparam = iLookPARAM%vcmax_Ha ! activation energy in the vcmax function (J mol-1) - case('vcmax_Hd' ); get_ixparam = iLookPARAM%vcmax_Hd ! deactivation energy in the vcmax function (J mol-1) - case('vcmax_Sv' ); get_ixparam = iLookPARAM%vcmax_Sv ! entropy term in the vcmax function (J mol-1 K-1) - case('vcmax_Kn' ); get_ixparam = iLookPARAM%vcmax_Kn ! foliage nitrogen decay coefficient (-) - case('jmax25_scale' ); get_ixparam = iLookPARAM%jmax25_scale ! scaling factor to relate jmax25 to vcmax25 (-) - case('jmax_Ha' ); get_ixparam = iLookPARAM%jmax_Ha ! activation energy in the jmax function (J mol-1) - case('jmax_Hd' ); get_ixparam = iLookPARAM%jmax_Hd ! deactivation energy in the jmax function (J mol-1) - case('jmax_Sv' ); get_ixparam = iLookPARAM%jmax_Sv ! entropy term in the jmax function (J mol-1 K-1) - case('fractionJ' ); get_ixparam = iLookPARAM%fractionJ ! fraction of light lost by other than the chloroplast lamellae (-) - case('quantamYield' ); get_ixparam = iLookPARAM%quantamYield ! quantam yield (mol e mol-1 quanta) - case('vpScaleFactor' ); get_ixparam = iLookPARAM%vpScaleFactor ! vapor pressure scaling factor in stomatal conductance function (Pa) - case('cond2photo_slope' ); get_ixparam = iLookPARAM%cond2photo_slope ! slope of conductance-photosynthesis relationship (-) - case('minStomatalConductance' ); get_ixparam = iLookPARAM%minStomatalConductance ! minimum stomatal conductance (umol H2O m-2 s-1) + case('Kc25' ); get_ixParam = iLookPARAM%Kc25 ! Michaelis-Menten constant for CO2 at 25 degrees C (umol mol-1) + case('Ko25' ); get_ixParam = iLookPARAM%Ko25 ! Michaelis-Menten constant for O2 at 25 degrees C (mol mol-1) + case('Kc_qFac' ); get_ixParam = iLookPARAM%Kc_qFac ! factor in the q10 function defining temperature controls on Kc (-) + case('Ko_qFac' ); get_ixParam = iLookPARAM%Ko_qFac ! factor in the q10 function defining temperature controls on Ko (-) + case('kc_Ha' ); get_ixParam = iLookPARAM%kc_Ha ! activation energy for the Michaelis-Menten constant for CO2 (J mol-1) + case('ko_Ha' ); get_ixParam = iLookPARAM%ko_Ha ! activation energy for the Michaelis-Menten constant for CO2 (J mol-1) + case('vcmax25_canopyTop' ); get_ixParam = iLookPARAM%vcmax25_canopyTop ! potential carboxylation rate at 25 degrees C at the canopy top (umol co2 m-2 s-1) + case('vcmax_qFac' ); get_ixParam = iLookPARAM%vcmax_qFac ! factor in the q10 function defining temperature controls on vcmax (-) + case('vcmax_Ha' ); get_ixParam = iLookPARAM%vcmax_Ha ! activation energy in the vcmax function (J mol-1) + case('vcmax_Hd' ); get_ixParam = iLookPARAM%vcmax_Hd ! deactivation energy in the vcmax function (J mol-1) + case('vcmax_Sv' ); get_ixParam = iLookPARAM%vcmax_Sv ! entropy term in the vcmax function (J mol-1 K-1) + case('vcmax_Kn' ); get_ixParam = iLookPARAM%vcmax_Kn ! foliage nitrogen decay coefficient (-) + case('jmax25_scale' ); get_ixParam = iLookPARAM%jmax25_scale ! scaling factor to relate jmax25 to vcmax25 (-) + case('jmax_Ha' ); get_ixParam = iLookPARAM%jmax_Ha ! activation energy in the jmax function (J mol-1) + case('jmax_Hd' ); get_ixParam = iLookPARAM%jmax_Hd ! deactivation energy in the jmax function (J mol-1) + case('jmax_Sv' ); get_ixParam = iLookPARAM%jmax_Sv ! entropy term in the jmax function (J mol-1 K-1) + case('fractionJ' ); get_ixParam = iLookPARAM%fractionJ ! fraction of light lost by other than the chloroplast lamellae (-) + case('quantamYield' ); get_ixParam = iLookPARAM%quantamYield ! quantam yield (mol e mol-1 quanta) + case('vpScaleFactor' ); get_ixParam = iLookPARAM%vpScaleFactor ! vapor pressure scaling factor in stomatal conductance function (Pa) + case('cond2photo_slope' ); get_ixParam = iLookPARAM%cond2photo_slope ! slope of conductance-photosynthesis relationship (-) + case('minStomatalConductance' ); get_ixParam = iLookPARAM%minStomatalConductance ! minimum stomatal conductance (umol H2O m-2 s-1) ! vegetation properties - case('winterSAI' ); get_ixparam = iLookPARAM%winterSAI ! stem area index prior to the start of the growing season (m2 m-2) - case('summerLAI' ); get_ixparam = iLookPARAM%summerLAI ! maximum leaf area index at the peak of the growing season (m2 m-2) - case('rootScaleFactor1' ); get_ixparam = iLookPARAM%rootScaleFactor1 ! 1st scaling factor (a) in Y = 1 - 0.5*( exp(-aZ) + exp(-bZ) ) (m-1) - case('rootScaleFactor2' ); get_ixparam = iLookPARAM%rootScaleFactor2 ! 2nd scaling factor (b) in Y = 1 - 0.5*( exp(-aZ) + exp(-bZ) ) (m-1) - case('rootingDepth' ); get_ixparam = iLookPARAM%rootingDepth ! rooting depth (m) - case('rootDistExp' ); get_ixparam = iLookPARAM%rootDistExp ! exponent for the vertical distriution of root density (-) - case('plantWiltPsi' ); get_ixparam = iLookPARAM%plantWiltPsi ! matric head at wilting point (m) - case('soilStressParam' ); get_ixparam = iLookPARAM%soilStressParam ! parameter in the exponential soil stress function - case('critSoilWilting' ); get_ixparam = iLookPARAM%critSoilWilting ! critical vol. liq. water content when plants are wilting (-) - case('critSoilTranspire' ); get_ixparam = iLookPARAM%critSoilTranspire ! critical vol. liq. water content when transpiration is limited (-) - case('critAquiferTranspire' ); get_ixparam = iLookPARAM%critAquiferTranspire ! critical aquifer storage value when transpiration is limited (m) - case('minStomatalResistance' ); get_ixparam = iLookPARAM%minStomatalResistance ! minimum canopy resistance (s m-1) - case('leafDimension' ); get_ixparam = iLookPARAM%leafDimension ! characteristic leaf dimension (m) - case('heightCanopyTop' ); get_ixparam = iLookPARAM%heightCanopyTop ! height of top of the vegetation canopy above ground surface (m) - case('heightCanopyBottom' ); get_ixparam = iLookPARAM%heightCanopyBottom ! height of bottom of the vegetation canopy above ground surface (m) - case('specificHeatVeg' ); get_ixparam = iLookPARAM%specificHeatVeg ! specific heat of vegetation (J kg-1 K-1) - case('maxMassVegetation' ); get_ixparam = iLookPARAM%maxMassVegetation ! maximum mass of vegetation (full foliage) (kg m-2) - case('throughfallScaleSnow' ); get_ixparam = iLookPARAM%throughfallScaleSnow ! scaling factor for throughfall (snow) (-) - case('throughfallScaleRain' ); get_ixparam = iLookPARAM%throughfallScaleRain ! scaling factor for throughfall (rain) (-) - case('refInterceptCapSnow' ); get_ixparam = iLookPARAM%refInterceptCapSnow ! reference canopy interception capacity per unit leaf area (snow) (kg m-2) - case('refInterceptCapRain' ); get_ixparam = iLookPARAM%refInterceptCapRain ! canopy interception capacity per unit leaf area (rain) (kg m-2) - case('snowUnloadingCoeff' ); get_ixparam = iLookPARAM%snowUnloadingCoeff ! time constant for unloading of snow from the forest canopy (s-1) - case('canopyDrainageCoeff' ); get_ixparam = iLookPARAM%canopyDrainageCoeff ! time constant for drainage of liquid water from the forest canopy (s-1) - case('ratioDrip2Unloading' ); get_ixparam = iLookPARAM%ratioDrip2Unloading ! ratio of canopy drip to unloading of snow from the forest canopy (-) - case('canopyWettingFactor' ); get_ixparam = iLookPARAM%canopyWettingFactor ! maximum wetted fraction of the canopy (-) - case('canopyWettingExp' ); get_ixparam = iLookPARAM%canopyWettingExp ! exponent in canopy wetting function (-) - case('minTempUnloading' ); get_ixparam = iLookPARAM%minTempUnloading ! min temp for unloading in windySnow (K) - case('rateTempUnloading' ); get_ixparam = iLookPARAM%rateTempUnloading ! how quickly to unload due to temperature (K s) - case('minWindUnloading' ); get_ixparam = iLookPARAM%minWindUnloading ! min wind speed for unloading in windySnow (m s-1) - case('rateWindUnloading' ); get_ixparam = iLookPARAM%rateWindUnloading ! how quickly to unload due to wind (m) + case('winterSAI' ); get_ixParam = iLookPARAM%winterSAI ! stem area index prior to the start of the growing season (m2 m-2) + case('summerLAI' ); get_ixParam = iLookPARAM%summerLAI ! maximum leaf area index at the peak of the growing season (m2 m-2) + case('rootScaleFactor1' ); get_ixParam = iLookPARAM%rootScaleFactor1 ! 1st scaling factor (a) in Y = 1 - 0.5*( exp(-aZ) + exp(-bZ) ) (m-1) + case('rootScaleFactor2' ); get_ixParam = iLookPARAM%rootScaleFactor2 ! 2nd scaling factor (b) in Y = 1 - 0.5*( exp(-aZ) + exp(-bZ) ) (m-1) + case('rootingDepth' ); get_ixParam = iLookPARAM%rootingDepth ! rooting depth (m) + case('rootDistExp' ); get_ixParam = iLookPARAM%rootDistExp ! exponent for the vertical distriution of root density (-) + case('plantWiltPsi' ); get_ixParam = iLookPARAM%plantWiltPsi ! matric head at wilting point (m) + case('soilStressParam' ); get_ixParam = iLookPARAM%soilStressParam ! parameter in the exponential soil stress function + case('critSoilWilting' ); get_ixParam = iLookPARAM%critSoilWilting ! critical vol. liq. water content when plants are wilting (-) + case('critSoilTranspire' ); get_ixParam = iLookPARAM%critSoilTranspire ! critical vol. liq. water content when transpiration is limited (-) + case('critAquiferTranspire' ); get_ixParam = iLookPARAM%critAquiferTranspire ! critical aquifer storage value when transpiration is limited (m) + case('minStomatalResistance' ); get_ixParam = iLookPARAM%minStomatalResistance ! minimum canopy resistance (s m-1) + case('leafDimension' ); get_ixParam = iLookPARAM%leafDimension ! characteristic leaf dimension (m) + case('heightCanopyTop' ); get_ixParam = iLookPARAM%heightCanopyTop ! height of top of the vegetation canopy above ground surface (m) + case('heightCanopyBottom' ); get_ixParam = iLookPARAM%heightCanopyBottom ! height of bottom of the vegetation canopy above ground surface (m) + case('specificHeatVeg' ); get_ixParam = iLookPARAM%specificHeatVeg ! specific heat of vegetation (J kg-1 K-1) + case('maxMassVegetation' ); get_ixParam = iLookPARAM%maxMassVegetation ! maximum mass of vegetation (full foliage) (kg m-2) + case('throughfallScaleSnow' ); get_ixParam = iLookPARAM%throughfallScaleSnow ! scaling factor for throughfall (snow) (-) + case('throughfallScaleRain' ); get_ixParam = iLookPARAM%throughfallScaleRain ! scaling factor for throughfall (rain) (-) + case('refInterceptCapSnow' ); get_ixParam = iLookPARAM%refInterceptCapSnow ! reference canopy interception capacity per unit leaf area (snow) (kg m-2) + case('refInterceptCapRain' ); get_ixParam = iLookPARAM%refInterceptCapRain ! canopy interception capacity per unit leaf area (rain) (kg m-2) + case('snowUnloadingCoeff' ); get_ixParam = iLookPARAM%snowUnloadingCoeff ! time constant for unloading of snow from the forest canopy (s-1) + case('canopyDrainageCoeff' ); get_ixParam = iLookPARAM%canopyDrainageCoeff ! time constant for drainage of liquid water from the forest canopy (s-1) + case('ratioDrip2Unloading' ); get_ixParam = iLookPARAM%ratioDrip2Unloading ! ratio of canopy drip to unloading of snow from the forest canopy (-) + case('canopyWettingFactor' ); get_ixParam = iLookPARAM%canopyWettingFactor ! maximum wetted fraction of the canopy (-) + case('canopyWettingExp' ); get_ixParam = iLookPARAM%canopyWettingExp ! exponent in canopy wetting function (-) + case('minTempUnloading' ); get_ixParam = iLookPARAM%minTempUnloading ! min temp for unloading in windySnow (K) + case('rateTempUnloading' ); get_ixParam = iLookPARAM%rateTempUnloading ! how quickly to unload due to temperature (K s) + case('minWindUnloading' ); get_ixParam = iLookPARAM%minWindUnloading ! min wind speed for unloading in windySnow (m s-1) + case('rateWindUnloading' ); get_ixParam = iLookPARAM%rateWindUnloading ! how quickly to unload due to wind (m) ! soil properties - case('soil_dens_intr' ); get_ixparam = iLookPARAM%soil_dens_intr ! intrinsic soil density (kg m-3) - case('thCond_soil' ); get_ixparam = iLookPARAM%thCond_soil ! thermal conductivity of soil (W m-1 K-1) - case('frac_sand' ); get_ixparam = iLookPARAM%frac_sand ! fraction of sand (-) - case('frac_silt' ); get_ixparam = iLookPARAM%frac_silt ! fraction of silt (-) - case('frac_clay' ); get_ixparam = iLookPARAM%frac_clay ! fraction of clay (-) - case('fieldCapacity' ); get_ixparam = iLookPARAM%fieldCapacity ! field capacity (-) - case('wettingFrontSuction' ); get_ixparam = iLookPARAM%wettingFrontSuction ! Green-Ampt wetting front suction (m) - case('theta_mp' ); get_ixparam = iLookPARAM%theta_mp ! volumetric liquid water content when macropore flow begins (-) - case('theta_sat' ); get_ixparam = iLookPARAM%theta_sat ! soil porosity (-) - case('theta_res' ); get_ixparam = iLookPARAM%theta_res ! volumetric residual water content (-) - case('vGn_alpha' ); get_ixparam = iLookPARAM%vGn_alpha ! van Genuchten "alpha" parameter (m-1) - case('vGn_n' ); get_ixparam = iLookPARAM%vGn_n ! van Genuchten "n" parameter (-) - case('mpExp' ); get_ixparam = iLookPARAM%mpExp ! empirical exponent in macropore flow equation (-) - case('k_soil' ); get_ixparam = iLookPARAM%k_soil ! saturated hydraulic conductivity (m s-1) - case('k_macropore' ); get_ixparam = iLookPARAM%k_macropore ! saturated hydraulic conductivity for the macropores (m s-1) - case('kAnisotropic' ); get_ixparam = iLookPARAM%kAnisotropic ! anisotropy factor for lateral hydraulic conductivity (-) - case('zScale_TOPMODEL' ); get_ixparam = iLookPARAM%zScale_TOPMODEL ! TOPMODEL scaling factor used in lower boundary condition for soil (m) - case('compactedDepth' ); get_ixparam = iLookPARAM%compactedDepth ! depth where k_soil reaches the compacted value given by CH78 (m) - case('aquiferBaseflowRate' ); get_ixparam = iLookPARAM%aquiferBaseflowRate ! baseflow rate when aquifer storage = aquiferScaleFactor (m s-1) - case('aquiferScaleFactor' ); get_ixparam = iLookPARAM%aquiferScaleFactor ! scaling factor for aquifer storage in the big bucket (m) - case('aquiferBaseflowExp' ); get_ixparam = iLookPARAM%aquiferBaseflowExp ! baseflow exponent (-) - case('qSurfScale' ); get_ixparam = iLookPARAM%qSurfScale ! scaling factor in the surface runoff parameterization (-) - case('specificYield' ); get_ixparam = iLookPARAM%specificYield ! specific yield (-) - case('specificStorage' ); get_ixparam = iLookPARAM%specificStorage ! specific storage coefficient (m-1) - case('f_impede' ); get_ixparam = iLookPARAM%f_impede ! ice impedence factor (-) - case('soilIceScale' ); get_ixparam = iLookPARAM%soilIceScale ! scaling factor for depth of soil ice, used to get frozen fraction (m) - case('soilIceCV' ); get_ixparam = iLookPARAM%soilIceCV ! CV of depth of soil ice, used to get frozen fraction (-) + case('soil_dens_intr' ); get_ixParam = iLookPARAM%soil_dens_intr ! intrinsic soil density (kg m-3) + case('thCond_soil' ); get_ixParam = iLookPARAM%thCond_soil ! thermal conductivity of soil (W m-1 K-1) + case('frac_sand' ); get_ixParam = iLookPARAM%frac_sand ! fraction of sand (-) + case('frac_silt' ); get_ixParam = iLookPARAM%frac_silt ! fraction of silt (-) + case('frac_clay' ); get_ixParam = iLookPARAM%frac_clay ! fraction of clay (-) + case('fieldCapacity' ); get_ixParam = iLookPARAM%fieldCapacity ! field capacity (-) + case('wettingFrontSuction' ); get_ixParam = iLookPARAM%wettingFrontSuction ! Green-Ampt wetting front suction (m) + case('theta_mp' ); get_ixParam = iLookPARAM%theta_mp ! volumetric liquid water content when macropore flow begins (-) + case('theta_sat' ); get_ixParam = iLookPARAM%theta_sat ! soil porosity (-) + case('theta_res' ); get_ixParam = iLookPARAM%theta_res ! volumetric residual water content (-) + case('vGn_alpha' ); get_ixParam = iLookPARAM%vGn_alpha ! van Genuchten "alpha" parameter (m-1) + case('vGn_n' ); get_ixParam = iLookPARAM%vGn_n ! van Genuchten "n" parameter (-) + case('mpExp' ); get_ixParam = iLookPARAM%mpExp ! empirical exponent in macropore flow equation (-) + case('k_soil' ); get_ixParam = iLookPARAM%k_soil ! saturated hydraulic conductivity (m s-1) + case('k_macropore' ); get_ixParam = iLookPARAM%k_macropore ! saturated hydraulic conductivity for the macropores (m s-1) + case('kAnisotropic' ); get_ixParam = iLookPARAM%kAnisotropic ! anisotropy factor for lateral hydraulic conductivity (-) + case('zScale_TOPMODEL' ); get_ixParam = iLookPARAM%zScale_TOPMODEL ! TOPMODEL scaling factor used in lower boundary condition for soil (m) + case('compactedDepth' ); get_ixParam = iLookPARAM%compactedDepth ! depth where k_soil reaches the compacted value given by CH78 (m) + case('aquiferBaseflowRate' ); get_ixParam = iLookPARAM%aquiferBaseflowRate ! baseflow rate when aquifer storage = aquiferScaleFactor (m s-1) + case('aquiferScaleFactor' ); get_ixParam = iLookPARAM%aquiferScaleFactor ! scaling factor for aquifer storage in the big bucket (m) + case('aquiferBaseflowExp' ); get_ixParam = iLookPARAM%aquiferBaseflowExp ! baseflow exponent (-) + case('qSurfScale' ); get_ixParam = iLookPARAM%qSurfScale ! scaling factor in the surface runoff parameterization (-) + case('specificYield' ); get_ixParam = iLookPARAM%specificYield ! specific yield (-) + case('specificStorage' ); get_ixParam = iLookPARAM%specificStorage ! specific storage coefficient (m-1) + case('f_impede' ); get_ixParam = iLookPARAM%f_impede ! ice impedence factor (-) + case('soilIceScale' ); get_ixParam = iLookPARAM%soilIceScale ! scaling factor for depth of soil ice, used to get frozen fraction (m) + case('soilIceCV' ); get_ixParam = iLookPARAM%soilIceCV ! CV of depth of soil ice, used to get frozen fraction (-) ! algorithmic control parameters - case('minwind' ); get_ixparam = iLookPARAM%minwind ! minimum wind speed (m s-1) - case('minstep' ); get_ixparam = iLookPARAM%minstep ! minimum length of the time step numrec, not currently used - case('maxstep' ); get_ixparam = iLookPARAM%maxstep ! maximum length of the time step numrec - case('be_steps' ); get_ixparam = iLookPARAM%be_steps ! minimum number of substeps to take in a maxstep numrec - case('wimplicit' ); get_ixparam = iLookPARAM%wimplicit ! weight assigned to start-of-step fluxes numrec, not currently used - case('maxiter' ); get_ixparam = iLookPARAM%maxiter ! maximum number of iterations numrec and kinsol - case('relConvTol_liquid' ); get_ixparam = iLookPARAM%relConvTol_liquid ! relative convergence tolerance for vol frac liq water (-) numrec - case('absConvTol_liquid' ); get_ixparam = iLookPARAM%absConvTol_liquid ! absolute convergence tolerance for vol frac liq water (-) numrec - case('relConvTol_matric' ); get_ixparam = iLookPARAM%relConvTol_matric ! relative convergence tolerance for matric head (-) numrec - case('absConvTol_matric' ); get_ixparam = iLookPARAM%absConvTol_matric ! absolute convergence tolerance for matric head (m) numrec - case('relConvTol_energy' ); get_ixparam = iLookPARAM%relConvTol_energy ! relative convergence tolerance for energy (-) numrec - case('absConvTol_energy' ); get_ixparam = iLookPARAM%absConvTol_energy ! absolute convergence tolerance for energy (J m-3) numrec - case('relConvTol_aquifr' ); get_ixparam = iLookPARAM%relConvTol_aquifr ! relative convergence tolerance for aquifer storage (-) numrec - case('absConvTol_aquifr' ); get_ixparam = iLookPARAM%absConvTol_aquifr ! absolute convergence tolerance for aquifer storage (m) numrec - case('relTolTempCas' ); get_ixparam = iLookPARAM%relTolTempCas ! relative error tolerance for canopy temperature state variable - case('absTolTempCas' ); get_ixparam = iLookPARAM%absTolTempCas ! absolute error tolerance for canopy temperature state variable - case('relTolTempVeg' ); get_ixparam = iLookPARAM%relTolTempVeg ! relative error tolerance for vegitation temp state var - case('absTolTempVeg' ); get_ixparam = iLookPARAM%absTolTempVeg ! absolute error tolerance for vegitation temp state var - case('relTolWatVeg' ); get_ixparam = iLookPARAM%relTolWatVeg ! relative error tolerance for vegitation hydrology - case('absTolWatVeg' ); get_ixparam = iLookPARAM%absTolWatVeg ! absolute error tolerance for vegitation hydrology - case('relTolTempSoilSnow' ); get_ixparam = iLookPARAM%relTolTempSoilSnow ! relative error tolerance for snow+soil energy - case('absTolTempSoilSnow' ); get_ixparam = iLookPARAM%absTolTempSoilSnow ! absolute error tolerance for snow+soil energy - case('relTolWatSnow' ); get_ixparam = iLookPARAM%relTolWatSnow ! relative error tolerance for snow hydrology - case('absTolWatSnow' ); get_ixparam = iLookPARAM%absTolWatSnow ! absolute error tolerance for snow hydrology - case('relTolMatric' ); get_ixparam = iLookPARAM%relTolMatric ! relative error tolerance for matric head - case('absTolMatric' ); get_ixparam = iLookPARAM%absTolMatric ! absolute error tolerance for matric head - case('relTolAquifr' ); get_ixparam = iLookPARAM%relTolAquifr ! relative error tolerance for aquifer hydrology - case('absTolAquifr' ); get_ixparam = iLookPARAM%absTolAquifr ! absolute error tolerance for aquifer hydrology - case('zmin' ); get_ixparam = iLookPARAM%zmin ! minimum layer depth (m) - case('zmax' ); get_ixparam = iLookPARAM%zmax ! maximum layer depth (m) - case('zminLayer1' ); get_ixparam = iLookPARAM%zminLayer1 ! minimum layer depth for the 1st (top) layer (m) - case('zminLayer2' ); get_ixparam = iLookPARAM%zminLayer2 ! minimum layer depth for the 2nd layer (m) - case('zminLayer3' ); get_ixparam = iLookPARAM%zminLayer3 ! minimum layer depth for the 3rd layer (m) - case('zminLayer4' ); get_ixparam = iLookPARAM%zminLayer4 ! minimum layer depth for the 4th layer (m) - case('zminLayer5' ); get_ixparam = iLookPARAM%zminLayer5 ! minimum layer depth for the 5th (bottom) layer (m) - case('zmaxLayer1_lower' ); get_ixparam = iLookPARAM%zmaxLayer1_lower ! maximum layer depth for the 1st (top) layer when only 1 layer (m) - case('zmaxLayer2_lower' ); get_ixparam = iLookPARAM%zmaxLayer2_lower ! maximum layer depth for the 2nd layer when only 2 layers (m) - case('zmaxLayer3_lower' ); get_ixparam = iLookPARAM%zmaxLayer3_lower ! maximum layer depth for the 3rd layer when only 3 layers (m) - case('zmaxLayer4_lower' ); get_ixparam = iLookPARAM%zmaxLayer4_lower ! maximum layer depth for the 4th layer when only 4 layers (m) - case('zmaxLayer1_upper' ); get_ixparam = iLookPARAM%zmaxLayer1_upper ! maximum layer depth for the 1st (top) layer when > 1 layer (m) - case('zmaxLayer2_upper' ); get_ixparam = iLookPARAM%zmaxLayer2_upper ! maximum layer depth for the 2nd layer when > 2 layers (m) - case('zmaxLayer3_upper' ); get_ixparam = iLookPARAM%zmaxLayer3_upper ! maximum layer depth for the 3rd layer when > 3 layers (m) - case('zmaxLayer4_upper' ); get_ixparam = iLookPARAM%zmaxLayer4_upper ! maximum layer depth for the 4th layer when > 4 layers (m) + case('minwind' ); get_ixParam = iLookPARAM%minwind ! minimum wind speed (m s-1) + case('minstep' ); get_ixParam = iLookPARAM%minstep ! minimum length of the time step numrec, not currently used + case('maxstep' ); get_ixParam = iLookPARAM%maxstep ! maximum length of the time step numrec + case('be_steps' ); get_ixParam = iLookPARAM%be_steps ! minimum number of substeps to take in a maxstep numrec + case('wimplicit' ); get_ixParam = iLookPARAM%wimplicit ! weight assigned to start-of-step fluxes numrec, not currently used + case('maxiter' ); get_ixParam = iLookPARAM%maxiter ! maximum number of iterations numrec and kinsol + case('relConvTol_liquid' ); get_ixParam = iLookPARAM%relConvTol_liquid ! relative convergence tolerance for vol frac liq water (-) numrec + case('absConvTol_liquid' ); get_ixParam = iLookPARAM%absConvTol_liquid ! absolute convergence tolerance for vol frac liq water (-) numrec + case('relConvTol_matric' ); get_ixParam = iLookPARAM%relConvTol_matric ! relative convergence tolerance for matric head (-) numrec + case('absConvTol_matric' ); get_ixParam = iLookPARAM%absConvTol_matric ! absolute convergence tolerance for matric head (m) numrec + case('relConvTol_energy' ); get_ixParam = iLookPARAM%relConvTol_energy ! relative convergence tolerance for energy (-) numrec + case('absConvTol_energy' ); get_ixParam = iLookPARAM%absConvTol_energy ! absolute convergence tolerance for energy (J m-3) numrec + case('relConvTol_aquifr' ); get_ixParam = iLookPARAM%relConvTol_aquifr ! relative convergence tolerance for aquifer storage (-) numrec + case('absConvTol_aquifr' ); get_ixParam = iLookPARAM%absConvTol_aquifr ! absolute convergence tolerance for aquifer storage (m) numrec + case('relTolTempCas' ); get_ixParam = iLookPARAM%relTolTempCas ! relative error tolerance for canopy temperature state variable + case('absTolTempCas' ); get_ixParam = iLookPARAM%absTolTempCas ! absolute error tolerance for canopy temperature state variable + case('relTolTempVeg' ); get_ixParam = iLookPARAM%relTolTempVeg ! relative error tolerance for vegitation temp state var + case('absTolTempVeg' ); get_ixParam = iLookPARAM%absTolTempVeg ! absolute error tolerance for vegitation temp state var + case('relTolWatVeg' ); get_ixParam = iLookPARAM%relTolWatVeg ! relative error tolerance for vegitation hydrology + case('absTolWatVeg' ); get_ixParam = iLookPARAM%absTolWatVeg ! absolute error tolerance for vegitation hydrology + case('relTolTempSoilSnow' ); get_ixParam = iLookPARAM%relTolTempSoilSnow ! relative error tolerance for snow+soil energy + case('absTolTempSoilSnow' ); get_ixParam = iLookPARAM%absTolTempSoilSnow ! absolute error tolerance for snow+soil energy + case('relTolWatSnow' ); get_ixParam = iLookPARAM%relTolWatSnow ! relative error tolerance for snow hydrology + case('absTolWatSnow' ); get_ixParam = iLookPARAM%absTolWatSnow ! absolute error tolerance for snow hydrology + case('relTolMatric' ); get_ixParam = iLookPARAM%relTolMatric ! relative error tolerance for matric head + case('absTolMatric' ); get_ixParam = iLookPARAM%absTolMatric ! absolute error tolerance for matric head + case('relTolAquifr' ); get_ixParam = iLookPARAM%relTolAquifr ! relative error tolerance for aquifer hydrology + case('absTolAquifr' ); get_ixParam = iLookPARAM%absTolAquifr ! absolute error tolerance for aquifer hydrology + case('zmin' ); get_ixParam = iLookPARAM%zmin ! minimum layer depth (m) + case('zmax' ); get_ixParam = iLookPARAM%zmax ! maximum layer depth (m) + case('zminLayer1' ); get_ixParam = iLookPARAM%zminLayer1 ! minimum layer depth for the 1st (top) layer (m) + case('zminLayer2' ); get_ixParam = iLookPARAM%zminLayer2 ! minimum layer depth for the 2nd layer (m) + case('zminLayer3' ); get_ixParam = iLookPARAM%zminLayer3 ! minimum layer depth for the 3rd layer (m) + case('zminLayer4' ); get_ixParam = iLookPARAM%zminLayer4 ! minimum layer depth for the 4th layer (m) + case('zminLayer5' ); get_ixParam = iLookPARAM%zminLayer5 ! minimum layer depth for the 5th (bottom) layer (m) + case('zmaxLayer1_lower' ); get_ixParam = iLookPARAM%zmaxLayer1_lower ! maximum layer depth for the 1st (top) layer when only 1 layer (m) + case('zmaxLayer2_lower' ); get_ixParam = iLookPARAM%zmaxLayer2_lower ! maximum layer depth for the 2nd layer when only 2 layers (m) + case('zmaxLayer3_lower' ); get_ixParam = iLookPARAM%zmaxLayer3_lower ! maximum layer depth for the 3rd layer when only 3 layers (m) + case('zmaxLayer4_lower' ); get_ixParam = iLookPARAM%zmaxLayer4_lower ! maximum layer depth for the 4th layer when only 4 layers (m) + case('zmaxLayer1_upper' ); get_ixParam = iLookPARAM%zmaxLayer1_upper ! maximum layer depth for the 1st (top) layer when > 1 layer (m) + case('zmaxLayer2_upper' ); get_ixParam = iLookPARAM%zmaxLayer2_upper ! maximum layer depth for the 2nd layer when > 2 layers (m) + case('zmaxLayer3_upper' ); get_ixParam = iLookPARAM%zmaxLayer3_upper ! maximum layer depth for the 3rd layer when > 3 layers (m) + case('zmaxLayer4_upper' ); get_ixParam = iLookPARAM%zmaxLayer4_upper ! maximum layer depth for the 4th layer when > 4 layers (m) ! get to here if cannot find the variable case default - get_ixparam = integerMissing + get_ixParam = integerMissing end select - end function get_ixparam + end function get_ixParam ! ******************************************************************************************************************* - ! public function get_ixprog: get the index of the named variables for the prognostic (state) variables + ! public function get_ixProg: get the index of the named variables for the prognostic (state) variables ! ******************************************************************************************************************* - function get_ixprog(varName) + function get_ixProg(varName) USE var_lookup,only:iLookPROG ! indices of the named variables implicit none ! define dummy variables character(*), intent(in) :: varName ! variable name - integer(i4b) :: get_ixprog ! index of the named variable + integer(i4b) :: get_ixProg ! index of the named variable ! get the index of the named variables select case(trim(varName)) ! variables for time stepping - case('dt_init' ); get_ixprog = iLookPROG%dt_init ! length of initial time step at start of next data interval (s) + case('dt_init' ); get_ixProg = iLookPROG%dt_init ! length of initial time step at start of next data interval (s) ! state variables for vegetation - case('scalarCanopyIce' ); get_ixprog = iLookPROG%scalarCanopyIce ! mass of ice on the vegetation canopy (kg m-2) - case('scalarCanopyLiq' ); get_ixprog = iLookPROG%scalarCanopyLiq ! mass of liquid water on the vegetation canopy (kg m-2) - case('scalarCanopyWat' ); get_ixprog = iLookPROG%scalarCanopyWat ! mass of total water on the vegetation canopy (kg m-2) - case('scalarCanairTemp' ); get_ixprog = iLookPROG%scalarCanairTemp ! temperature of the canopy air space (K) - case('scalarCanopyTemp' ); get_ixprog = iLookPROG%scalarCanopyTemp ! temperature of the vegetation canopy (K) + case('scalarCanopyIce' ); get_ixProg = iLookPROG%scalarCanopyIce ! mass of ice on the vegetation canopy (kg m-2) + case('scalarCanopyLiq' ); get_ixProg = iLookPROG%scalarCanopyLiq ! mass of liquid water on the vegetation canopy (kg m-2) + case('scalarCanopyWat' ); get_ixProg = iLookPROG%scalarCanopyWat ! mass of total water on the vegetation canopy (kg m-2) + case('scalarCanairTemp' ); get_ixProg = iLookPROG%scalarCanairTemp ! temperature of the canopy air space (K) + case('scalarCanopyTemp' ); get_ixProg = iLookPROG%scalarCanopyTemp ! temperature of the vegetation canopy (K) ! state variables for snow - case('spectralSnowAlbedoDiffuse' ); get_ixprog = iLookPROG%spectralSnowAlbedoDiffuse ! diffuse snow albedo for individual spectral bands (-) - case('scalarSnowAlbedo' ); get_ixprog = iLookPROG%scalarSnowAlbedo ! snow albedo for the entire spectral band (-) - case('scalarSnowDepth' ); get_ixprog = iLookPROG%scalarSnowDepth ! total snow depth (m) - case('scalarSWE' ); get_ixprog = iLookPROG%scalarSWE ! snow water equivalent (kg m-2) - case('scalarSfcMeltPond' ); get_ixprog = iLookPROG%scalarSfcMeltPond ! ponded water caused by melt of the "snow without a layer" (kg m-2) + case('spectralSnowAlbedoDiffuse' ); get_ixProg = iLookPROG%spectralSnowAlbedoDiffuse ! diffuse snow albedo for individual spectral bands (-) + case('scalarSnowAlbedo' ); get_ixProg = iLookPROG%scalarSnowAlbedo ! snow albedo for the entire spectral band (-) + case('scalarSnowDepth' ); get_ixProg = iLookPROG%scalarSnowDepth ! total snow depth (m) + case('scalarSWE' ); get_ixProg = iLookPROG%scalarSWE ! snow water equivalent (kg m-2) + case('scalarSfcMeltPond' ); get_ixProg = iLookPROG%scalarSfcMeltPond ! ponded water caused by melt of the "snow without a layer" (kg m-2) ! state variables for the snow+soil domain - case('mLayerTemp' ); get_ixprog = iLookPROG%mLayerTemp ! temperature of each layer (K) - case('mLayerVolFracIce' ); get_ixprog = iLookPROG%mLayerVolFracIce ! volumetric fraction of icein each layer (-) - case('mLayerVolFracLiq' ); get_ixprog = iLookPROG%mLayerVolFracLiq ! volumetric fraction of liquid water in each layer (-) - case('mLayerVolFracWat' ); get_ixprog = iLookPROG%mLayerVolFracWat ! volumetric fraction of total water in each layer (-) - case('mLayerMatricHead' ); get_ixprog = iLookPROG%mLayerMatricHead ! matric head of water in the soil (m) + case('mLayerTemp' ); get_ixProg = iLookPROG%mLayerTemp ! temperature of each layer (K) + case('mLayerVolFracIce' ); get_ixProg = iLookPROG%mLayerVolFracIce ! volumetric fraction of icein each layer (-) + case('mLayerVolFracLiq' ); get_ixProg = iLookPROG%mLayerVolFracLiq ! volumetric fraction of liquid water in each layer (-) + case('mLayerVolFracWat' ); get_ixProg = iLookPROG%mLayerVolFracWat ! volumetric fraction of total water in each layer (-) + case('mLayerMatricHead' ); get_ixProg = iLookPROG%mLayerMatricHead ! matric head of water in the soil (m) ! other state variables - case('scalarAquiferStorage' ); get_ixprog = iLookPROG%scalarAquiferStorage ! relative aquifer storage -- above bottom of the soil profile (m) - case('scalarSurfaceTemp' ); get_ixprog = iLookPROG%scalarSurfaceTemp ! surface temperature (K) + case('scalarAquiferStorage' ); get_ixProg = iLookPROG%scalarAquiferStorage ! relative aquifer storage -- above bottom of the soil profile (m) + case('scalarSurfaceTemp' ); get_ixProg = iLookPROG%scalarSurfaceTemp ! surface temperature (K) ! coordinate variables - case('mLayerDepth' ); get_ixprog = iLookPROG%mLayerDepth ! depth of each layer (m) - case('mLayerHeight' ); get_ixprog = iLookPROG%mLayerHeight ! height at the midpoint of each layer (m) - case('iLayerHeight' ); get_ixprog = iLookPROG%iLayerHeight ! height at the interface of each layer (m) + case('mLayerDepth' ); get_ixProg = iLookPROG%mLayerDepth ! depth of each layer (m) + case('mLayerHeight' ); get_ixProg = iLookPROG%mLayerHeight ! height at the midpoint of each layer (m) + case('iLayerHeight' ); get_ixProg = iLookPROG%iLayerHeight ! height at the interface of each layer (m) ! get to here if cannot find the variable case default - get_ixprog = integerMissing + get_ixProg = integerMissing end select - end function get_ixprog + end function get_ixProg ! ******************************************************************************************************************* - ! public function get_ixdiag: get the index of the named variables for the diagnostic variables + ! public function get_ixDiag: get the index of the named variables for the diagnostic variables ! ******************************************************************************************************************* - function get_ixdiag(varName) + function get_ixDiag(varName) USE var_lookup,only:iLookDIAG ! indices of the named variables implicit none ! define dummy variables character(*), intent(in) :: varName ! variable name - integer(i4b) :: get_ixdiag ! index of the named variable + integer(i4b) :: get_ixDiag ! index of the named variable ! get the index of the named variables select case(trim(varName)) ! local properties - case('scalarCanopyDepth' ); get_ixdiag = iLookDIAG%scalarCanopyDepth ! canopy depth (m) - case('scalarGreenVegFraction' ); get_ixdiag = iLookDIAG%scalarGreenVegFraction ! green vegetation fraction used to compute LAI (-) - case('scalarBulkVolHeatCapVeg' ); get_ixdiag = iLookDIAG%scalarBulkVolHeatCapVeg ! bulk volumetric heat capacity of vegetation (J m-3 K-1) - case('scalarCanopyCm' ); get_ixdiag = iLookDIAG%scalarCanopyCm ! Cm of canopy (J kg-1 K-1) - case('scalarCanopyEmissivity' ); get_ixdiag = iLookDIAG%scalarCanopyEmissivity ! effective canopy emissivity (-) - case('scalarRootZoneTemp' ); get_ixdiag = iLookDIAG%scalarRootZoneTemp ! average temperature of the root zone (K) - case('scalarLAI' ); get_ixdiag = iLookDIAG%scalarLAI ! one-sided leaf area index (m2 m-2) - case('scalarSAI' ); get_ixdiag = iLookDIAG%scalarSAI ! one-sided stem area index (m2 m-2) - case('scalarExposedLAI' ); get_ixdiag = iLookDIAG%scalarExposedLAI ! exposed leaf area index after burial by snow (m2 m-2) - case('scalarExposedSAI' ); get_ixdiag = iLookDIAG%scalarExposedSAI ! exposed stem area index after burial by snow (m2 m-2) - case('scalarAdjMeasHeight' ); get_ixdiag = iLookDIAG%scalarAdjMeasHeight ! adjusted measurement height for cases snowDepth>mHeight (m) - case('scalarCanopyIceMax' ); get_ixdiag = iLookDIAG%scalarCanopyIceMax ! maximum interception storage capacity for ice (kg m-2) - case('scalarCanopyLiqMax' ); get_ixdiag = iLookDIAG%scalarCanopyLiqMax ! maximum interception storage capacity for liquid water (kg m-2) - case('scalarGrowingSeasonIndex' ); get_ixdiag = iLookDIAG%scalarGrowingSeasonIndex ! growing season index (0=off, 1=on) - case('scalarVolHtCap_air' ); get_ixdiag = iLookDIAG%scalarVolHtCap_air ! volumetric heat capacity air (J m-3 K-1) - case('scalarVolHtCap_ice' ); get_ixdiag = iLookDIAG%scalarVolHtCap_ice ! volumetric heat capacity ice (J m-3 K-1) - case('scalarVolHtCap_soil' ); get_ixdiag = iLookDIAG%scalarVolHtCap_soil ! volumetric heat capacity dry soil (J m-3 K-1) - case('scalarVolHtCap_water' ); get_ixdiag = iLookDIAG%scalarVolHtCap_water ! volumetric heat capacity liquid wat (J m-3 K-1) - case('mLayerVolHtCapBulk' ); get_ixdiag = iLookDIAG%mLayerVolHtCapBulk ! volumetric heat capacity in each layer (J m-3 K-1) - case('mLayerCm' ); get_ixdiag = iLookDIAG%mLayerCm ! Cm of each layer (J kg-1 K-1) - case('scalarLambda_drysoil' ); get_ixdiag = iLookDIAG%scalarLambda_drysoil ! thermal conductivity of dry soil (W m-1) - case('scalarLambda_wetsoil' ); get_ixdiag = iLookDIAG%scalarLambda_wetsoil ! thermal conductivity of wet soil (W m-1) - case('mLayerThermalC' ); get_ixdiag = iLookDIAG%mLayerThermalC ! thermal conductivity at the mid-point of each layer (W m-1 K-1) - case('iLayerThermalC' ); get_ixdiag = iLookDIAG%iLayerThermalC ! thermal conductivity at the interface of each layer (W m-1 K-1) + case('scalarCanopyDepth' ); get_ixDiag = iLookDIAG%scalarCanopyDepth ! canopy depth (m) + case('scalarGreenVegFraction' ); get_ixDiag = iLookDIAG%scalarGreenVegFraction ! green vegetation fraction used to compute LAI (-) + case('scalarBulkVolHeatCapVeg' ); get_ixDiag = iLookDIAG%scalarBulkVolHeatCapVeg ! bulk volumetric heat capacity of vegetation (J m-3 K-1) + case('scalarCanopyCm' ); get_ixDiag = iLookDIAG%scalarCanopyCm ! Cm of canopy (J kg-1 K-1) + case('scalarCanopyEmissivity' ); get_ixDiag = iLookDIAG%scalarCanopyEmissivity ! effective canopy emissivity (-) + case('scalarRootZoneTemp' ); get_ixDiag = iLookDIAG%scalarRootZoneTemp ! average temperature of the root zone (K) + case('scalarLAI' ); get_ixDiag = iLookDIAG%scalarLAI ! one-sided leaf area index (m2 m-2) + case('scalarSAI' ); get_ixDiag = iLookDIAG%scalarSAI ! one-sided stem area index (m2 m-2) + case('scalarExposedLAI' ); get_ixDiag = iLookDIAG%scalarExposedLAI ! exposed leaf area index after burial by snow (m2 m-2) + case('scalarExposedSAI' ); get_ixDiag = iLookDIAG%scalarExposedSAI ! exposed stem area index after burial by snow (m2 m-2) + case('scalarAdjMeasHeight' ); get_ixDiag = iLookDIAG%scalarAdjMeasHeight ! adjusted measurement height for cases snowDepth>mHeight (m) + case('scalarCanopyIceMax' ); get_ixDiag = iLookDIAG%scalarCanopyIceMax ! maximum interception storage capacity for ice (kg m-2) + case('scalarCanopyLiqMax' ); get_ixDiag = iLookDIAG%scalarCanopyLiqMax ! maximum interception storage capacity for liquid water (kg m-2) + case('scalarGrowingSeasonIndex' ); get_ixDiag = iLookDIAG%scalarGrowingSeasonIndex ! growing season index (0=off, 1=on) + case('scalarVolHtCap_air' ); get_ixDiag = iLookDIAG%scalarVolHtCap_air ! volumetric heat capacity air (J m-3 K-1) + case('scalarVolHtCap_ice' ); get_ixDiag = iLookDIAG%scalarVolHtCap_ice ! volumetric heat capacity ice (J m-3 K-1) + case('scalarVolHtCap_soil' ); get_ixDiag = iLookDIAG%scalarVolHtCap_soil ! volumetric heat capacity dry soil (J m-3 K-1) + case('scalarVolHtCap_water' ); get_ixDiag = iLookDIAG%scalarVolHtCap_water ! volumetric heat capacity liquid wat (J m-3 K-1) + case('mLayerVolHtCapBulk' ); get_ixDiag = iLookDIAG%mLayerVolHtCapBulk ! volumetric heat capacity in each layer (J m-3 K-1) + case('mLayerCm' ); get_ixDiag = iLookDIAG%mLayerCm ! Cm of each layer (J kg-1 K-1) + case('scalarLambda_drysoil' ); get_ixDiag = iLookDIAG%scalarLambda_drysoil ! thermal conductivity of dry soil (W m-1) + case('scalarLambda_wetsoil' ); get_ixDiag = iLookDIAG%scalarLambda_wetsoil ! thermal conductivity of wet soil (W m-1) + case('mLayerThermalC' ); get_ixDiag = iLookDIAG%mLayerThermalC ! thermal conductivity at the mid-point of each layer (W m-1 K-1) + case('iLayerThermalC' ); get_ixDiag = iLookDIAG%iLayerThermalC ! thermal conductivity at the interface of each layer (W m-1 K-1) ! enthalpy - case('scalarCanairEnthalpy' ); get_ixdiag = iLookDIAG%scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - case('scalarCanopyEnthTemp' ); get_ixdiag = iLookDIAG%scalarCanopyEnthTemp ! temperature component of enthalpy of the vegetation canopy (J m-3) - case('scalarCanopyEnthalpy' ); get_ixdiag = iLookDIAG%scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) - case('mLayerEnthTemp' ); get_ixdiag = iLookDIAG%mLayerEnthTemp ! temperature component of enthalpy of the snow+soil layers (J m-3) - case('mLayerEnthalpy' ); get_ixdiag = iLookDIAG%mLayerEnthalpy ! enthalpy of the snow+soil layers (J m-3) - case('scalarTotalSoilEnthalpy' ); get_ixdiag = iLookDIAG%scalarTotalSoilEnthalpy ! total enthalpy of the soil column (J m-3) - case('scalarTotalSnowEnthalpy' ); get_ixdiag = iLookDIAG%scalarTotalSnowEnthalpy ! total enthalpy of the snow column (J m-3) + case('scalarCanairEnthalpy' ); get_ixDiag = iLookDIAG%scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) + case('scalarCanopyEnthTemp' ); get_ixDiag = iLookDIAG%scalarCanopyEnthTemp ! temperature component of enthalpy of the vegetation canopy (J m-3) + case('scalarCanopyEnthalpy' ); get_ixDiag = iLookDIAG%scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) + case('mLayerEnthTemp' ); get_ixDiag = iLookDIAG%mLayerEnthTemp ! temperature component of enthalpy of the snow+soil layers (J m-3) + case('mLayerEnthalpy' ); get_ixDiag = iLookDIAG%mLayerEnthalpy ! enthalpy of the snow+soil layers (J m-3) + case('scalarTotalSoilEnthalpy' ); get_ixDiag = iLookDIAG%scalarTotalSoilEnthalpy ! total enthalpy of the soil column (J m-3) + case('scalarTotalSnowEnthalpy' ); get_ixDiag = iLookDIAG%scalarTotalSnowEnthalpy ! total enthalpy of the snow column (J m-3) ! forcing - case('scalarVPair' ); get_ixdiag = iLookDIAG%scalarVPair ! vapor pressure of the air above the vegetation canopy (Pa) - case('scalarVP_CanopyAir' ); get_ixdiag = iLookDIAG%scalarVP_CanopyAir ! vapor pressure of the canopy air space (Pa) - case('scalarTwetbulb' ); get_ixdiag = iLookDIAG%scalarTwetbulb ! wetbulb temperature (K) - case('scalarSnowfallTemp' ); get_ixdiag = iLookDIAG%scalarSnowfallTemp ! temperature of fresh snow (K) - case('scalarNewSnowDensity' ); get_ixdiag = iLookDIAG%scalarNewSnowDensity ! density of fresh snow, should snow be falling in this time step (kg m-3) - case('scalarO2air' ); get_ixdiag = iLookDIAG%scalarO2air ! atmospheric o2 concentration (Pa) - case('scalarCO2air' ); get_ixdiag = iLookDIAG%scalarCO2air ! atmospheric co2 concentration (Pa) - case('windspd_x' ); get_ixdiag = iLookDIAG%windspd_x ! wind speed at 10 meter height in x-direction (m s-1) - case('windspd_y' ); get_ixdiag = iLookDIAG%windspd_y ! wind speed at 10 meter height in y-direction (m s-1) + case('scalarVPair' ); get_ixDiag = iLookDIAG%scalarVPair ! vapor pressure of the air above the vegetation canopy (Pa) + case('scalarVP_CanopyAir' ); get_ixDiag = iLookDIAG%scalarVP_CanopyAir ! vapor pressure of the canopy air space (Pa) + case('scalarTwetbulb' ); get_ixDiag = iLookDIAG%scalarTwetbulb ! wetbulb temperature (K) + case('scalarSnowfallTemp' ); get_ixDiag = iLookDIAG%scalarSnowfallTemp ! temperature of fresh snow (K) + case('scalarNewSnowDensity' ); get_ixDiag = iLookDIAG%scalarNewSnowDensity ! density of fresh snow, should snow be falling in this time step (kg m-3) + case('scalarO2air' ); get_ixDiag = iLookDIAG%scalarO2air ! atmospheric o2 concentration (Pa) + case('scalarCO2air' ); get_ixDiag = iLookDIAG%scalarCO2air ! atmospheric co2 concentration (Pa) + case('windspd_x' ); get_ixDiag = iLookDIAG%windspd_x ! wind speed at 10 meter height in x-direction (m s-1) + case('windspd_y' ); get_ixDiag = iLookDIAG%windspd_y ! wind speed at 10 meter height in y-direction (m s-1) ! shortwave radiation - case('scalarCosZenith' ); get_ixdiag = iLookDIAG%scalarCosZenith ! cosine of the solar zenith angle (0-1) - case('scalarFractionDirect' ); get_ixdiag = iLookDIAG%scalarFractionDirect ! fraction of direct radiation (0-1) - case('scalarCanopySunlitFraction' ); get_ixdiag = iLookDIAG%scalarCanopySunlitFraction ! sunlit fraction of canopy (-) - case('scalarCanopySunlitLAI' ); get_ixdiag = iLookDIAG%scalarCanopySunlitLAI ! sunlit leaf area (-) - case('scalarCanopyShadedLAI' ); get_ixdiag = iLookDIAG%scalarCanopyShadedLAI ! shaded leaf area (-) - case('spectralAlbGndDirect' ); get_ixdiag = iLookDIAG%spectralAlbGndDirect ! direct albedo of underlying surface for each spectral band (-) - case('spectralAlbGndDiffuse' ); get_ixdiag = iLookDIAG%spectralAlbGndDiffuse ! diffuse albedo of underlying surface for each spectral band (-) - case('scalarGroundAlbedo' ); get_ixdiag = iLookDIAG%scalarGroundAlbedo ! albedo of the ground surface (-) + case('scalarCosZenith' ); get_ixDiag = iLookDIAG%scalarCosZenith ! cosine of the solar zenith angle (0-1) + case('scalarFractionDirect' ); get_ixDiag = iLookDIAG%scalarFractionDirect ! fraction of direct radiation (0-1) + case('scalarCanopySunlitFraction' ); get_ixDiag = iLookDIAG%scalarCanopySunlitFraction ! sunlit fraction of canopy (-) + case('scalarCanopySunlitLAI' ); get_ixDiag = iLookDIAG%scalarCanopySunlitLAI ! sunlit leaf area (-) + case('scalarCanopyShadedLAI' ); get_ixDiag = iLookDIAG%scalarCanopyShadedLAI ! shaded leaf area (-) + case('spectralAlbGndDirect' ); get_ixDiag = iLookDIAG%spectralAlbGndDirect ! direct albedo of underlying surface for each spectral band (-) + case('spectralAlbGndDiffuse' ); get_ixDiag = iLookDIAG%spectralAlbGndDiffuse ! diffuse albedo of underlying surface for each spectral band (-) + case('scalarGroundAlbedo' ); get_ixDiag = iLookDIAG%scalarGroundAlbedo ! albedo of the ground surface (-) ! turbulent heat transfer - case('scalarLatHeatSubVapCanopy' ); get_ixdiag = iLookDIAG%scalarLatHeatSubVapCanopy ! latent heat of sublimation/vaporization used for veg canopy (J kg-1) - case('scalarLatHeatSubVapGround' ); get_ixdiag = iLookDIAG%scalarLatHeatSubVapGround ! latent heat of sublimation/vaporization used for ground surface (J kg-1) - case('scalarSatVP_CanopyTemp' ); get_ixdiag = iLookDIAG%scalarSatVP_CanopyTemp ! saturation vapor pressure at the temperature of vegetation canopy (Pa) - case('scalarSatVP_GroundTemp' ); get_ixdiag = iLookDIAG%scalarSatVP_GroundTemp ! saturation vapor pressure at the temperature of the ground (Pa) - case('scalarZ0Canopy' ); get_ixdiag = iLookDIAG%scalarZ0Canopy ! roughness length of the canopy (m) - case('scalarWindReductionFactor' ); get_ixdiag = iLookDIAG%scalarWindReductionFactor ! canopy wind reduction factor (-) - case('scalarZeroPlaneDisplacement' ); get_ixdiag = iLookDIAG%scalarZeroPlaneDisplacement ! zero plane displacement (m) - case('scalarRiBulkCanopy' ); get_ixdiag = iLookDIAG%scalarRiBulkCanopy ! bulk Richardson number for the canopy (-) - case('scalarRiBulkGround' ); get_ixdiag = iLookDIAG%scalarRiBulkGround ! bulk Richardson number for the ground surface (-) - case('scalarCanopyStabilityCorrection'); get_ixdiag = iLookDIAG%scalarCanopyStabilityCorrection ! stability correction for the canopy (-) - case('scalarGroundStabilityCorrection'); get_ixdiag = iLookDIAG%scalarGroundStabilityCorrection ! stability correction for the ground surface (-) + case('scalarLatHeatSubVapCanopy' ); get_ixDiag = iLookDIAG%scalarLatHeatSubVapCanopy ! latent heat of sublimation/vaporization used for veg canopy (J kg-1) + case('scalarLatHeatSubVapGround' ); get_ixDiag = iLookDIAG%scalarLatHeatSubVapGround ! latent heat of sublimation/vaporization used for ground surface (J kg-1) + case('scalarSatVP_CanopyTemp' ); get_ixDiag = iLookDIAG%scalarSatVP_CanopyTemp ! saturation vapor pressure at the temperature of vegetation canopy (Pa) + case('scalarSatVP_GroundTemp' ); get_ixDiag = iLookDIAG%scalarSatVP_GroundTemp ! saturation vapor pressure at the temperature of the ground (Pa) + case('scalarZ0Canopy' ); get_ixDiag = iLookDIAG%scalarZ0Canopy ! roughness length of the canopy (m) + case('scalarWindReductionFactor' ); get_ixDiag = iLookDIAG%scalarWindReductionFactor ! canopy wind reduction factor (-) + case('scalarZeroPlaneDisplacement' ); get_ixDiag = iLookDIAG%scalarZeroPlaneDisplacement ! zero plane displacement (m) + case('scalarRiBulkCanopy' ); get_ixDiag = iLookDIAG%scalarRiBulkCanopy ! bulk Richardson number for the canopy (-) + case('scalarRiBulkGround' ); get_ixDiag = iLookDIAG%scalarRiBulkGround ! bulk Richardson number for the ground surface (-) + case('scalarCanopyStabilityCorrection'); get_ixDiag = iLookDIAG%scalarCanopyStabilityCorrection ! stability correction for the canopy (-) + case('scalarGroundStabilityCorrection'); get_ixDiag = iLookDIAG%scalarGroundStabilityCorrection ! stability correction for the ground surface (-) ! evapotranspiration - case('scalarIntercellularCO2Sunlit' ); get_ixdiag = iLookDIAG%scalarIntercellularCO2Sunlit ! carbon dioxide partial pressure of leaf interior (sunlit leaves) (Pa) - case('scalarIntercellularCO2Shaded' ); get_ixdiag = iLookDIAG%scalarIntercellularCO2Shaded ! carbon dioxide partial pressure of leaf interior (shaded leaves) (Pa) - case('scalarTranspireLim' ); get_ixdiag = iLookDIAG%scalarTranspireLim ! aggregate soil moisture and aquifer storage limit on transpiration (-) - case('scalarTranspireLimAqfr' ); get_ixdiag = iLookDIAG%scalarTranspireLimAqfr ! aquifer storage limit on transpiration (-) - case('scalarFoliageNitrogenFactor' ); get_ixdiag = iLookDIAG%scalarFoliageNitrogenFactor ! foliage nitrogen concentration, 1=saturated (-) - case('scalarSoilRelHumidity' ); get_ixdiag = iLookDIAG%scalarSoilRelHumidity ! relative humidity in the soil pores in the upper-most soil layer (-) - case('mLayerTranspireLim' ); get_ixdiag = iLookDIAG%mLayerTranspireLim ! moisture avail factor limiting transpiration in each layer (-) - case('mLayerRootDensity' ); get_ixdiag = iLookDIAG%mLayerRootDensity ! fraction of roots in each soil layer (-) - case('scalarAquiferRootFrac' ); get_ixdiag = iLookDIAG%scalarAquiferRootFrac ! fraction of roots below the soil profile (-) + case('scalarIntercellularCO2Sunlit' ); get_ixDiag = iLookDIAG%scalarIntercellularCO2Sunlit ! carbon dioxide partial pressure of leaf interior (sunlit leaves) (Pa) + case('scalarIntercellularCO2Shaded' ); get_ixDiag = iLookDIAG%scalarIntercellularCO2Shaded ! carbon dioxide partial pressure of leaf interior (shaded leaves) (Pa) + case('scalarTranspireLim' ); get_ixDiag = iLookDIAG%scalarTranspireLim ! aggregate soil moisture and aquifer storage limit on transpiration (-) + case('scalarTranspireLimAqfr' ); get_ixDiag = iLookDIAG%scalarTranspireLimAqfr ! aquifer storage limit on transpiration (-) + case('scalarFoliageNitrogenFactor' ); get_ixDiag = iLookDIAG%scalarFoliageNitrogenFactor ! foliage nitrogen concentration, 1=saturated (-) + case('scalarSoilRelHumidity' ); get_ixDiag = iLookDIAG%scalarSoilRelHumidity ! relative humidity in the soil pores in the upper-most soil layer (-) + case('mLayerTranspireLim' ); get_ixDiag = iLookDIAG%mLayerTranspireLim ! moisture avail factor limiting transpiration in each layer (-) + case('mLayerRootDensity' ); get_ixDiag = iLookDIAG%mLayerRootDensity ! fraction of roots in each soil layer (-) + case('scalarAquiferRootFrac' ); get_ixDiag = iLookDIAG%scalarAquiferRootFrac ! fraction of roots below the soil profile (-) ! canopy hydrology - case('scalarFracLiqVeg' ); get_ixdiag = iLookDIAG%scalarFracLiqVeg ! fraction of liquid water on vegetation (-) - case('scalarCanopyWetFraction' ); get_ixdiag = iLookDIAG%scalarCanopyWetFraction ! fraction of canopy that is wet + case('scalarFracLiqVeg' ); get_ixDiag = iLookDIAG%scalarFracLiqVeg ! fraction of liquid water on vegetation (-) + case('scalarCanopyWetFraction' ); get_ixDiag = iLookDIAG%scalarCanopyWetFraction ! fraction of canopy that is wet ! snow hydrology - case('scalarSnowAge' ); get_ixdiag = iLookDIAG%scalarSnowAge ! non-dimensional snow age (-) - case('scalarGroundSnowFraction' ); get_ixdiag = iLookDIAG%scalarGroundSnowFraction ! fraction of ground that is covered with snow (-) - case('spectralSnowAlbedoDirect' ); get_ixdiag = iLookDIAG%spectralSnowAlbedoDirect ! direct snow albedo for individual spectral bands (-) - case('mLayerFracLiqSnow' ); get_ixdiag = iLookDIAG%mLayerFracLiqSnow ! fraction of liquid water in each snow layer (-) - case('mLayerThetaResid' ); get_ixdiag = iLookDIAG%mLayerThetaResid ! residual volumetric water content in each snow layer (-) - case('mLayerPoreSpace' ); get_ixdiag = iLookDIAG%mLayerPoreSpace ! total pore space in each snow layer (-) - case('mLayerMeltFreeze' ); get_ixdiag = iLookDIAG%mLayerMeltFreeze ! ice content change from melt/freeze in each layer (kg m-3) + case('scalarSnowAge' ); get_ixDiag = iLookDIAG%scalarSnowAge ! non-dimensional snow age (-) + case('scalarGroundSnowFraction' ); get_ixDiag = iLookDIAG%scalarGroundSnowFraction ! fraction of ground that is covered with snow (-) + case('spectralSnowAlbedoDirect' ); get_ixDiag = iLookDIAG%spectralSnowAlbedoDirect ! direct snow albedo for individual spectral bands (-) + case('mLayerFracLiqSnow' ); get_ixDiag = iLookDIAG%mLayerFracLiqSnow ! fraction of liquid water in each snow layer (-) + case('mLayerThetaResid' ); get_ixDiag = iLookDIAG%mLayerThetaResid ! residual volumetric water content in each snow layer (-) + case('mLayerPoreSpace' ); get_ixDiag = iLookDIAG%mLayerPoreSpace ! total pore space in each snow layer (-) + case('mLayerMeltFreeze' ); get_ixDiag = iLookDIAG%mLayerMeltFreeze ! ice content change from melt/freeze in each layer (kg m-3) ! soil hydrology - case('scalarInfilArea' ); get_ixdiag = iLookDIAG%scalarInfilArea ! fraction of unfrozen area where water can infiltrate (-) - case('scalarFrozenArea' ); get_ixdiag = iLookDIAG%scalarFrozenArea ! fraction of area that is considered impermeable due to soil ice (-) - case('scalarSoilControl' ); get_ixdiag = iLookDIAG%scalarSoilControl ! soil control on infiltration: 1=controlling; 0=not (-) - case('mLayerVolFracAir' ); get_ixdiag = iLookDIAG%mLayerVolFracAir ! volumetric fraction of air in each layer (-) - case('mLayerTcrit' ); get_ixdiag = iLookDIAG%mLayerTcrit ! critical soil temperature above which all water is unfrozen (K) - case('mLayerCompress' ); get_ixdiag = iLookDIAG%mLayerCompress ! change in volumetric water content due to compression of soil (s-1) - case('scalarSoilCompress' ); get_ixdiag = iLookDIAG%scalarSoilCompress ! change in total soil storage due to compression of the soil matrix (kg m-2 s-1) - case('mLayerMatricHeadLiq' ); get_ixdiag = iLookDIAG%mLayerMatricHeadLiq ! matric potential of liquid water (m) + case('scalarInfilArea' ); get_ixDiag = iLookDIAG%scalarInfilArea ! fraction of unfrozen area where water can infiltrate (-) + case('scalarFrozenArea' ); get_ixDiag = iLookDIAG%scalarFrozenArea ! fraction of area that is considered impermeable due to soil ice (-) + case('scalarSoilControl' ); get_ixDiag = iLookDIAG%scalarSoilControl ! soil control on infiltration: 1=controlling; 0=not (-) + case('mLayerVolFracAir' ); get_ixDiag = iLookDIAG%mLayerVolFracAir ! volumetric fraction of air in each layer (-) + case('mLayerTcrit' ); get_ixDiag = iLookDIAG%mLayerTcrit ! critical soil temperature above which all water is unfrozen (K) + case('mLayerCompress' ); get_ixDiag = iLookDIAG%mLayerCompress ! change in volumetric water content due to compression of soil (s-1) + case('scalarSoilCompress' ); get_ixDiag = iLookDIAG%scalarSoilCompress ! change in total soil storage due to compression of the soil matrix (kg m-2 s-1) + case('mLayerMatricHeadLiq' ); get_ixDiag = iLookDIAG%mLayerMatricHeadLiq ! matric potential of liquid water (m) ! mass balance check - case('scalarTotalSoilLiq' ); get_ixdiag = iLookDIAG%scalarTotalSoilLiq ! total mass of liquid water in the soil (kg m-2) - case('scalarTotalSoilIce' ); get_ixdiag = iLookDIAG%scalarTotalSoilIce ! total mass of ice in the soil (kg m-2) - case('scalarTotalSoilWat' ); get_ixdiag = iLookDIAG%scalarTotalSoilWat ! total mass of water in the soil (kg m-2) + case('scalarTotalSoilLiq' ); get_ixDiag = iLookDIAG%scalarTotalSoilLiq ! total mass of liquid water in the soil (kg m-2) + case('scalarTotalSoilIce' ); get_ixDiag = iLookDIAG%scalarTotalSoilIce ! total mass of ice in the soil (kg m-2) + case('scalarTotalSoilWat' ); get_ixDiag = iLookDIAG%scalarTotalSoilWat ! total mass of water in the soil (kg m-2) ! variable shortcuts - case('scalarVGn_m' ); get_ixdiag = iLookDIAG%scalarVGn_m ! van Genuchten "m" parameter (-) - case('scalarKappa' ); get_ixdiag = iLookDIAG%scalarKappa ! constant in the freezing curve function (m K-1) - case('scalarVolLatHt_fus' ); get_ixdiag = iLookDIAG%scalarVolLatHt_fus ! volumetric latent heat of fusion (J m-3) + case('scalarVGn_m' ); get_ixDiag = iLookDIAG%scalarVGn_m ! van Genuchten "m" parameter (-) + case('scalarKappa' ); get_ixDiag = iLookDIAG%scalarKappa ! constant in the freezing curve function (m K-1) + case('scalarVolLatHt_fus' ); get_ixDiag = iLookDIAG%scalarVolLatHt_fus ! volumetric latent heat of fusion (J m-3) ! timing information - case('numFluxCalls' ); get_ixdiag = iLookDIAG%numFluxCalls ! number of flux calls (-) - case('wallClockTime' ); get_ixdiag = iLookDIAG%wallClockTime ! wall clock time (s) - case('meanStepSize' ); get_ixdiag = iLookDIAG%meanStepSize ! mean time step size (s) over data window + case('numFluxCalls' ); get_ixDiag = iLookDIAG%numFluxCalls ! number of flux calls (-) + case('wallClockTime' ); get_ixDiag = iLookDIAG%wallClockTime ! wall clock time (s) + case('meanStepSize' ); get_ixDiag = iLookDIAG%meanStepSize ! mean time step size (s) over data window ! balances - case('balanceCasNrg' ); get_ixdiag = iLookDIAG%balanceCasNrg ! balance of energy in the canopy air space (W m-3) - case('balanceVegNrg' ); get_ixdiag = iLookDIAG%balanceVegNrg ! balance of energy in the vegetation canopy (W m-3) - case('balanceLayerNrg' ); get_ixdiag = iLookDIAG%balanceLayerNrg ! balance of energy in each snow+soil layer (W m-3) - case('balanceSnowNrg' ); get_ixdiag = iLookDIAG%balanceSnowNrg ! balance of energy in the snow (W m-3) - case('balanceSoilNrg' ); get_ixdiag = iLookDIAG%balanceSoilNrg ! balance of energy in the soil (W m-3) - case('balanceVegMass' ); get_ixdiag = iLookDIAG%balanceVegMass ! balance of water in the vegetation canopy (kg m-2 s-1) - case('balanceLayerMass' ); get_ixdiag = iLookDIAG%balanceLayerMass ! balance of water in each snow+soil layer (kg m-2 s-1) - case('balanceSnowMass' ); get_ixdiag = iLookDIAG%balanceSnowMass ! balance of water in the snow (kg m-2 s-1) - case('balanceSoilMass' ); get_ixdiag = iLookDIAG%balanceSoilMass ! balance of water in the soil (kg m-2 s-1) - case('balanceAqMass' ); get_ixdiag = iLookDIAG%balanceAqMass ! balance of water in the aquifer (kg m-2 s-1) + case('balanceCasNrg' ); get_ixDiag = iLookDIAG%balanceCasNrg ! balance of energy in the canopy air space (W m-3) + case('balanceVegNrg' ); get_ixDiag = iLookDIAG%balanceVegNrg ! balance of energy in the vegetation canopy (W m-3) + case('balanceLayerNrg' ); get_ixDiag = iLookDIAG%balanceLayerNrg ! balance of energy in each snow+soil layer (W m-3) + case('balanceSnowNrg' ); get_ixDiag = iLookDIAG%balanceSnowNrg ! balance of energy in the snow (W m-3) + case('balanceSoilNrg' ); get_ixDiag = iLookDIAG%balanceSoilNrg ! balance of energy in the soil (W m-3) + case('balanceVegMass' ); get_ixDiag = iLookDIAG%balanceVegMass ! balance of water in the vegetation canopy (kg m-2 s-1) + case('balanceLayerMass' ); get_ixDiag = iLookDIAG%balanceLayerMass ! balance of water in each snow+soil layer (kg m-2 s-1) + case('balanceSnowMass' ); get_ixDiag = iLookDIAG%balanceSnowMass ! balance of water in the snow (kg m-2 s-1) + case('balanceSoilMass' ); get_ixDiag = iLookDIAG%balanceSoilMass ! balance of water in the soil (kg m-2 s-1) + case('balanceAqMass' ); get_ixDiag = iLookDIAG%balanceAqMass ! balance of water in the aquifer (kg m-2 s-1) ! scaled balances - case('scaledBalanceCasNrg' ); get_ixdiag = iLookDIAG%scaledBalanceCasNrg ! scaled balance of energy in the canopy air space (s-1) - case('scaledBalanceVegNrg' ); get_ixdiag = iLookDIAG%scaledBalanceVegNrg ! scaled balance of energy in the vegetation canopy (s-1) - case('scaledBalanceSnowNrg' ); get_ixdiag = iLookDIAG%scaledBalanceSnowNrg ! scaled balance of energy in the snow (s-1) - case('scaledBalanceSoilNrg' ); get_ixdiag = iLookDIAG%scaledBalanceSoilNrg ! scaled balance of energy in the soil (s-1) - case('scaledBalanceVegMass' ); get_ixdiag = iLookDIAG%scaledBalanceVegMass ! scaled balance of water in the vegetation canopy (s-1) - case('scaledBalanceSnowMass' ); get_ixdiag = iLookDIAG%scaledBalanceSnowMass ! scaled balance of water in the snow (s-1) - case('scaledBalanceSoilMass' ); get_ixdiag = iLookDIAG%scaledBalanceSoilMass ! scaled balance of water in the soil (s-1) - case('scaledBalanceAqMass' ); get_ixdiag = iLookDIAG%scaledBalanceAqMass ! scaled balance of water in the aquifer (s-1) + case('scaledBalanceCasNrg' ); get_ixDiag = iLookDIAG%scaledBalanceCasNrg ! scaled balance of energy in the canopy air space (s-1) + case('scaledBalanceVegNrg' ); get_ixDiag = iLookDIAG%scaledBalanceVegNrg ! scaled balance of energy in the vegetation canopy (s-1) + case('scaledBalanceSnowNrg' ); get_ixDiag = iLookDIAG%scaledBalanceSnowNrg ! scaled balance of energy in the snow (s-1) + case('scaledBalanceSoilNrg' ); get_ixDiag = iLookDIAG%scaledBalanceSoilNrg ! scaled balance of energy in the soil (s-1) + case('scaledBalanceVegMass' ); get_ixDiag = iLookDIAG%scaledBalanceVegMass ! scaled balance of water in the vegetation canopy (s-1) + case('scaledBalanceSnowMass' ); get_ixDiag = iLookDIAG%scaledBalanceSnowMass ! scaled balance of water in the snow (s-1) + case('scaledBalanceSoilMass' ); get_ixDiag = iLookDIAG%scaledBalanceSoilMass ! scaled balance of water in the soil (s-1) + case('scaledBalanceAqMass' ); get_ixDiag = iLookDIAG%scaledBalanceAqMass ! scaled balance of water in the aquifer (s-1) ! get to here if cannot find the variable case default - get_ixdiag = integerMissing + get_ixDiag = integerMissing end select - end function get_ixdiag + end function get_ixDiag ! ******************************************************************************************************************* - ! public function get_ixdiag: get the index of the named variables for the fluxes + ! public function get_ixDiag: get the index of the named variables for the fluxes ! ******************************************************************************************************************* - function get_ixflux(varName) + function get_ixFlux(varName) USE var_lookup,only:iLookFLUX ! indices of the named variables implicit none ! define dummy variables character(*), intent(in) :: varName ! variable name - integer(i4b) :: get_ixflux ! index of the named variable + integer(i4b) :: get_ixFlux ! index of the named variable ! get the index of the named variables select case(trim(varName)) ! net energy and mass fluxes for the vegetation domain - case('scalarCanairNetNrgFlux' ); get_ixflux = iLookFLUX%scalarCanairNetNrgFlux ! net energy flux for the canopy air space (W m-2) - case('scalarCanopyNetNrgFlux' ); get_ixflux = iLookFLUX%scalarCanopyNetNrgFlux ! net energy flux for the vegetation canopy (W m-2) - case('scalarGroundNetNrgFlux' ); get_ixflux = iLookFLUX%scalarGroundNetNrgFlux ! net energy flux for the ground surface (W m-2) - case('scalarCanopyNetLiqFlux' ); get_ixflux = iLookFLUX%scalarCanopyNetLiqFlux ! net liquid water flux for the vegetation canopy (kg m-2 s-1) + case('scalarCanairNetNrgFlux' ); get_ixFlux = iLookFLUX%scalarCanairNetNrgFlux ! net energy flux for the canopy air space (W m-2) + case('scalarCanopyNetNrgFlux' ); get_ixFlux = iLookFLUX%scalarCanopyNetNrgFlux ! net energy flux for the vegetation canopy (W m-2) + case('scalarGroundNetNrgFlux' ); get_ixFlux = iLookFLUX%scalarGroundNetNrgFlux ! net energy flux for the ground surface (W m-2) + case('scalarCanopyNetLiqFlux' ); get_ixFlux = iLookFLUX%scalarCanopyNetLiqFlux ! net liquid water flux for the vegetation canopy (kg m-2 s-1) ! forcing - case('scalarRainfall' ); get_ixflux = iLookFLUX%scalarRainfall ! computed rainfall rate (kg m-2 s-1) - case('scalarSnowfall' ); get_ixflux = iLookFLUX%scalarSnowfall ! computed snowfall rate (kg m-2 s-1) + case('scalarRainfall' ); get_ixFlux = iLookFLUX%scalarRainfall ! computed rainfall rate (kg m-2 s-1) + case('scalarSnowfall' ); get_ixFlux = iLookFLUX%scalarSnowfall ! computed snowfall rate (kg m-2 s-1) ! shortwave radiation - case('spectralIncomingDirect' ); get_ixflux = iLookFLUX%spectralIncomingDirect ! incoming direct solar radiation in each wave band (W m-2) - case('spectralIncomingDiffuse' ); get_ixflux = iLookFLUX%spectralIncomingDiffuse ! incoming diffuse solar radiation in each wave band (W m-2) - case('scalarCanopySunlitPAR' ); get_ixflux = iLookFLUX%scalarCanopySunlitPAR ! average absorbed par for sunlit leaves (w m-2) - case('scalarCanopyShadedPAR' ); get_ixflux = iLookFLUX%scalarCanopyShadedPAR ! average absorbed par for shaded leaves (w m-2) - case('spectralBelowCanopyDirect' ); get_ixflux = iLookFLUX%spectralBelowCanopyDirect ! downward direct flux below veg layer for each spectral band W m-2) - case('spectralBelowCanopyDiffuse' ); get_ixflux = iLookFLUX%spectralBelowCanopyDiffuse ! downward diffuse flux below veg layer for each spectral band (W m-2) - case('scalarBelowCanopySolar' ); get_ixflux = iLookFLUX%scalarBelowCanopySolar ! solar radiation transmitted below the canopy (W m-2) - case('scalarCanopyAbsorbedSolar' ); get_ixflux = iLookFLUX%scalarCanopyAbsorbedSolar ! solar radiation absorbed by canopy (W m-2) - case('scalarGroundAbsorbedSolar' ); get_ixflux = iLookFLUX%scalarGroundAbsorbedSolar ! solar radiation absorbed by ground (W m-2) + case('spectralIncomingDirect' ); get_ixFlux = iLookFLUX%spectralIncomingDirect ! incoming direct solar radiation in each wave band (W m-2) + case('spectralIncomingDiffuse' ); get_ixFlux = iLookFLUX%spectralIncomingDiffuse ! incoming diffuse solar radiation in each wave band (W m-2) + case('scalarCanopySunlitPAR' ); get_ixFlux = iLookFLUX%scalarCanopySunlitPAR ! average absorbed par for sunlit leaves (w m-2) + case('scalarCanopyShadedPAR' ); get_ixFlux = iLookFLUX%scalarCanopyShadedPAR ! average absorbed par for shaded leaves (w m-2) + case('spectralBelowCanopyDirect' ); get_ixFlux = iLookFLUX%spectralBelowCanopyDirect ! downward direct flux below veg layer for each spectral band W m-2) + case('spectralBelowCanopyDiffuse' ); get_ixFlux = iLookFLUX%spectralBelowCanopyDiffuse ! downward diffuse flux below veg layer for each spectral band (W m-2) + case('scalarBelowCanopySolar' ); get_ixFlux = iLookFLUX%scalarBelowCanopySolar ! solar radiation transmitted below the canopy (W m-2) + case('scalarCanopyAbsorbedSolar' ); get_ixFlux = iLookFLUX%scalarCanopyAbsorbedSolar ! solar radiation absorbed by canopy (W m-2) + case('scalarGroundAbsorbedSolar' ); get_ixFlux = iLookFLUX%scalarGroundAbsorbedSolar ! solar radiation absorbed by ground (W m-2) ! longwave radiation - case('scalarLWRadCanopy' ); get_ixflux = iLookFLUX%scalarLWRadCanopy ! longwave radiation emitted from the canopy (W m-2) - case('scalarLWRadGround' ); get_ixflux = iLookFLUX%scalarLWRadGround ! longwave radiation emitted at the ground surface (W m-2) - case('scalarLWRadUbound2Canopy' ); get_ixflux = iLookFLUX%scalarLWRadUbound2Canopy ! downward atmospheric longwave radiation absorbed by the canopy (W m-2) - case('scalarLWRadUbound2Ground' ); get_ixflux = iLookFLUX%scalarLWRadUbound2Ground ! downward atmospheric longwave radiation absorbed by the ground (W m-2) - case('scalarLWRadUbound2Ubound' ); get_ixflux = iLookFLUX%scalarLWRadUbound2Ubound ! atmospheric radiation refl by ground + lost thru upper boundary (W m-2) - case('scalarLWRadCanopy2Ubound' ); get_ixflux = iLookFLUX%scalarLWRadCanopy2Ubound ! longwave radiation emitted from canopy lost thru upper boundary (W m-2) - case('scalarLWRadCanopy2Ground' ); get_ixflux = iLookFLUX%scalarLWRadCanopy2Ground ! longwave radiation emitted from canopy absorbed by the ground (W m-2) - case('scalarLWRadCanopy2Canopy' ); get_ixflux = iLookFLUX%scalarLWRadCanopy2Canopy ! canopy longwave reflected from ground and absorbed by the canopy (W m-2) - case('scalarLWRadGround2Ubound' ); get_ixflux = iLookFLUX%scalarLWRadGround2Ubound ! longwave radiation emitted from ground lost thru upper boundary (W m-2) - case('scalarLWRadGround2Canopy' ); get_ixflux = iLookFLUX%scalarLWRadGround2Canopy ! longwave radiation emitted from ground and absorbed by the canopy (W m-2) - case('scalarLWNetCanopy' ); get_ixflux = iLookFLUX%scalarLWNetCanopy ! net longwave radiation at the canopy (W m-2) - case('scalarLWNetGround' ); get_ixflux = iLookFLUX%scalarLWNetGround ! net longwave radiation at the ground surface (W m-2) - case('scalarLWNetUbound' ); get_ixflux = iLookFLUX%scalarLWNetUbound ! net longwave radiation at the upper atmospheric boundary (W m-2) + case('scalarLWRadCanopy' ); get_ixFlux = iLookFLUX%scalarLWRadCanopy ! longwave radiation emitted from the canopy (W m-2) + case('scalarLWRadGround' ); get_ixFlux = iLookFLUX%scalarLWRadGround ! longwave radiation emitted at the ground surface (W m-2) + case('scalarLWRadUbound2Canopy' ); get_ixFlux = iLookFLUX%scalarLWRadUbound2Canopy ! downward atmospheric longwave radiation absorbed by the canopy (W m-2) + case('scalarLWRadUbound2Ground' ); get_ixFlux = iLookFLUX%scalarLWRadUbound2Ground ! downward atmospheric longwave radiation absorbed by the ground (W m-2) + case('scalarLWRadUbound2Ubound' ); get_ixFlux = iLookFLUX%scalarLWRadUbound2Ubound ! atmospheric radiation refl by ground + lost thru upper boundary (W m-2) + case('scalarLWRadCanopy2Ubound' ); get_ixFlux = iLookFLUX%scalarLWRadCanopy2Ubound ! longwave radiation emitted from canopy lost thru upper boundary (W m-2) + case('scalarLWRadCanopy2Ground' ); get_ixFlux = iLookFLUX%scalarLWRadCanopy2Ground ! longwave radiation emitted from canopy absorbed by the ground (W m-2) + case('scalarLWRadCanopy2Canopy' ); get_ixFlux = iLookFLUX%scalarLWRadCanopy2Canopy ! canopy longwave reflected from ground and absorbed by the canopy (W m-2) + case('scalarLWRadGround2Ubound' ); get_ixFlux = iLookFLUX%scalarLWRadGround2Ubound ! longwave radiation emitted from ground lost thru upper boundary (W m-2) + case('scalarLWRadGround2Canopy' ); get_ixFlux = iLookFLUX%scalarLWRadGround2Canopy ! longwave radiation emitted from ground and absorbed by the canopy (W m-2) + case('scalarLWNetCanopy' ); get_ixFlux = iLookFLUX%scalarLWNetCanopy ! net longwave radiation at the canopy (W m-2) + case('scalarLWNetGround' ); get_ixFlux = iLookFLUX%scalarLWNetGround ! net longwave radiation at the ground surface (W m-2) + case('scalarLWNetUbound' ); get_ixFlux = iLookFLUX%scalarLWNetUbound ! net longwave radiation at the upper atmospheric boundary (W m-2) ! turbulent heat transfer - case('scalarEddyDiffusCanopyTop' ); get_ixflux = iLookFLUX%scalarEddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) - case('scalarFrictionVelocity' ); get_ixflux = iLookFLUX%scalarFrictionVelocity ! friction velocity - canopy momentum sink (m s-1) - case('scalarWindspdCanopyTop' ); get_ixflux = iLookFLUX%scalarWindspdCanopyTop ! windspeed at the top of the canopy (m s-1) - case('scalarWindspdCanopyBottom' ); get_ixflux = iLookFLUX%scalarWindspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) - case('scalarGroundResistance' ); get_ixflux = iLookFLUX%scalarGroundResistance ! below canopy aerodynamic resistance (s m-1) - case('scalarCanopyResistance' ); get_ixflux = iLookFLUX%scalarCanopyResistance ! above canopy aerodynamic resistance (s m-1) - case('scalarLeafResistance' ); get_ixflux = iLookFLUX%scalarLeafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) - case('scalarSoilResistance' ); get_ixflux = iLookFLUX%scalarSoilResistance ! soil surface resistance (s m-1) - case('scalarSenHeatTotal' ); get_ixflux = iLookFLUX%scalarSenHeatTotal ! sensible heat from the canopy air space to the atmosphere (W m-2) - case('scalarSenHeatCanopy' ); get_ixflux = iLookFLUX%scalarSenHeatCanopy ! sensible heat from the canopy to the canopy air space (W m-2) - case('scalarSenHeatGround' ); get_ixflux = iLookFLUX%scalarSenHeatGround ! sensible heat from the ground (below canopy or non-vegetated) (W m-2) - case('scalarLatHeatTotal' ); get_ixflux = iLookFLUX%scalarLatHeatTotal ! latent heat from the canopy air space to the atmosphere (W m-2) - case('scalarLatHeatCanopyEvap' ); get_ixflux = iLookFLUX%scalarLatHeatCanopyEvap ! evaporation latent heat from the canopy to the canopy air space (W m-2) - case('scalarLatHeatCanopyTrans' ); get_ixflux = iLookFLUX%scalarLatHeatCanopyTrans ! transpiration latent heat from the canopy to the canopy air space (W m-2) - case('scalarLatHeatGround' ); get_ixflux = iLookFLUX%scalarLatHeatGround ! latent heat from the ground (below canopy or non-vegetated) (W m-2) - case('scalarCanopyAdvectiveHeatFlux' ); get_ixflux = iLookFLUX%scalarCanopyAdvectiveHeatFlux ! heat advected to the canopy surface with rain + snow (W m-2) - case('scalarGroundAdvectiveHeatFlux' ); get_ixflux = iLookFLUX%scalarGroundAdvectiveHeatFlux ! heat advected to the ground surface with throughfall and unloading/drainage (W m-2) - case('scalarCanopySublimation' ); get_ixflux = iLookFLUX%scalarCanopySublimation ! canopy sublimation/frost (kg m-2 s-1) - case('scalarSnowSublimation' ); get_ixflux = iLookFLUX%scalarSnowSublimation ! snow sublimation/frost (below canopy or non-vegetated) (kg m-2 s-1) + case('scalarEddyDiffusCanopyTop' ); get_ixFlux = iLookFLUX%scalarEddyDiffusCanopyTop ! eddy diffusivity for heat at the top of the canopy (m2 s-1) + case('scalarFrictionVelocity' ); get_ixFlux = iLookFLUX%scalarFrictionVelocity ! friction velocity - canopy momentum sink (m s-1) + case('scalarWindspdCanopyTop' ); get_ixFlux = iLookFLUX%scalarWindspdCanopyTop ! windspeed at the top of the canopy (m s-1) + case('scalarWindspdCanopyBottom' ); get_ixFlux = iLookFLUX%scalarWindspdCanopyBottom ! windspeed at the height of the bottom of the canopy (m s-1) + case('scalarGroundResistance' ); get_ixFlux = iLookFLUX%scalarGroundResistance ! below canopy aerodynamic resistance (s m-1) + case('scalarCanopyResistance' ); get_ixFlux = iLookFLUX%scalarCanopyResistance ! above canopy aerodynamic resistance (s m-1) + case('scalarLeafResistance' ); get_ixFlux = iLookFLUX%scalarLeafResistance ! mean leaf boundary layer resistance per unit leaf area (s m-1) + case('scalarSoilResistance' ); get_ixFlux = iLookFLUX%scalarSoilResistance ! soil surface resistance (s m-1) + case('scalarSenHeatTotal' ); get_ixFlux = iLookFLUX%scalarSenHeatTotal ! sensible heat from the canopy air space to the atmosphere (W m-2) + case('scalarSenHeatCanopy' ); get_ixFlux = iLookFLUX%scalarSenHeatCanopy ! sensible heat from the canopy to the canopy air space (W m-2) + case('scalarSenHeatGround' ); get_ixFlux = iLookFLUX%scalarSenHeatGround ! sensible heat from the ground (below canopy or non-vegetated) (W m-2) + case('scalarLatHeatTotal' ); get_ixFlux = iLookFLUX%scalarLatHeatTotal ! latent heat from the canopy air space to the atmosphere (W m-2) + case('scalarLatHeatCanopyEvap' ); get_ixFlux = iLookFLUX%scalarLatHeatCanopyEvap ! evaporation latent heat from the canopy to the canopy air space (W m-2) + case('scalarLatHeatCanopyTrans' ); get_ixFlux = iLookFLUX%scalarLatHeatCanopyTrans ! transpiration latent heat from the canopy to the canopy air space (W m-2) + case('scalarLatHeatGround' ); get_ixFlux = iLookFLUX%scalarLatHeatGround ! latent heat from the ground (below canopy or non-vegetated) (W m-2) + case('scalarCanopyAdvectiveHeatFlux' ); get_ixFlux = iLookFLUX%scalarCanopyAdvectiveHeatFlux ! heat advected to the canopy surface with rain + snow (W m-2) + case('scalarGroundAdvectiveHeatFlux' ); get_ixFlux = iLookFLUX%scalarGroundAdvectiveHeatFlux ! heat advected to the ground surface with throughfall and unloading/drainage (W m-2) + case('scalarCanopySublimation' ); get_ixFlux = iLookFLUX%scalarCanopySublimation ! canopy sublimation/frost (kg m-2 s-1) + case('scalarSnowSublimation' ); get_ixFlux = iLookFLUX%scalarSnowSublimation ! snow sublimation/frost (below canopy or non-vegetated) (kg m-2 s-1) ! liquid water fluxes associated with evapotranspiration - case('scalarStomResistSunlit' ); get_ixflux = iLookFLUX%scalarStomResistSunlit ! stomatal resistance for sunlit leaves (s m-1) - case('scalarStomResistShaded' ); get_ixflux = iLookFLUX%scalarStomResistShaded ! stomatal resistance for shaded leaves (s m-1) - case('scalarPhotosynthesisSunlit' ); get_ixflux = iLookFLUX%scalarPhotosynthesisSunlit ! sunlit photosynthesis (umolco2 m-2 s-1) - case('scalarPhotosynthesisShaded' ); get_ixflux = iLookFLUX%scalarPhotosynthesisShaded ! shaded photosynthesis (umolco2 m-2 s-1) - case('scalarCanopyTranspiration' ); get_ixflux = iLookFLUX%scalarCanopyTranspiration ! canopy transpiration (kg m-2 s-1) - case('scalarCanopyEvaporation' ); get_ixflux = iLookFLUX%scalarCanopyEvaporation ! canopy evaporation/condensation (kg m-2 s-1) - case('scalarGroundEvaporation' ); get_ixflux = iLookFLUX%scalarGroundEvaporation ! ground evaporation/condensation (below canopy or non-vegetated) (kg m-2 s-1) - case('mLayerTranspire' ); get_ixflux = iLookFLUX%mLayerTranspire ! transpiration loss from each soil layer (kg m-2 s-1) + case('scalarStomResistSunlit' ); get_ixFlux = iLookFLUX%scalarStomResistSunlit ! stomatal resistance for sunlit leaves (s m-1) + case('scalarStomResistShaded' ); get_ixFlux = iLookFLUX%scalarStomResistShaded ! stomatal resistance for shaded leaves (s m-1) + case('scalarPhotosynthesisSunlit' ); get_ixFlux = iLookFLUX%scalarPhotosynthesisSunlit ! sunlit photosynthesis (umolco2 m-2 s-1) + case('scalarPhotosynthesisShaded' ); get_ixFlux = iLookFLUX%scalarPhotosynthesisShaded ! shaded photosynthesis (umolco2 m-2 s-1) + case('scalarCanopyTranspiration' ); get_ixFlux = iLookFLUX%scalarCanopyTranspiration ! canopy transpiration (kg m-2 s-1) + case('scalarCanopyEvaporation' ); get_ixFlux = iLookFLUX%scalarCanopyEvaporation ! canopy evaporation/condensation (kg m-2 s-1) + case('scalarGroundEvaporation' ); get_ixFlux = iLookFLUX%scalarGroundEvaporation ! ground evaporation/condensation (below canopy or non-vegetated) (kg m-2 s-1) + case('mLayerTranspire' ); get_ixFlux = iLookFLUX%mLayerTranspire ! transpiration loss from each soil layer (kg m-2 s-1) ! liquid and solid water fluxes through the canopy - case('scalarThroughfallSnow' ); get_ixflux = iLookFLUX%scalarThroughfallSnow ! snow that reaches the ground without ever touching the canopy (kg m-2 s-1) - case('scalarThroughfallRain' ); get_ixflux = iLookFLUX%scalarThroughfallRain ! rain that reaches the ground without ever touching the canopy (kg m-2 s-1) - case('scalarCanopySnowUnloading' ); get_ixflux = iLookFLUX%scalarCanopySnowUnloading ! unloading of snow from the vegetion canopy (kg m-2 s-1) - case('scalarCanopyLiqDrainage' ); get_ixflux = iLookFLUX%scalarCanopyLiqDrainage ! drainage of liquid water from the vegetation canopy (kg m-2 s-1) - case('scalarCanopyMeltFreeze' ); get_ixflux = iLookFLUX%scalarCanopyMeltFreeze ! melt/freeze of water stored in the canopy (kg m-2 s-1) + case('scalarThroughfallSnow' ); get_ixFlux = iLookFLUX%scalarThroughfallSnow ! snow that reaches the ground without ever touching the canopy (kg m-2 s-1) + case('scalarThroughfallRain' ); get_ixFlux = iLookFLUX%scalarThroughfallRain ! rain that reaches the ground without ever touching the canopy (kg m-2 s-1) + case('scalarCanopySnowUnloading' ); get_ixFlux = iLookFLUX%scalarCanopySnowUnloading ! unloading of snow from the vegetion canopy (kg m-2 s-1) + case('scalarCanopyLiqDrainage' ); get_ixFlux = iLookFLUX%scalarCanopyLiqDrainage ! drainage of liquid water from the vegetation canopy (kg m-2 s-1) + case('scalarCanopyMeltFreeze' ); get_ixFlux = iLookFLUX%scalarCanopyMeltFreeze ! melt/freeze of water stored in the canopy (kg m-2 s-1) ! energy fluxes and for the snow and soil domains - case('iLayerConductiveFlux' ); get_ixflux = iLookFLUX%iLayerConductiveFlux ! conductive energy flux at layer interfaces at end of time step (W m-2) - case('iLayerAdvectiveFlux' ); get_ixflux = iLookFLUX%iLayerAdvectiveFlux ! advective energy flux at layer interfaces at end of time step (W m-2) - case('iLayerNrgFlux' ); get_ixflux = iLookFLUX%iLayerNrgFlux ! energy flux at layer interfaces at the end of the time step (W m-2) - case('mLayerNrgFlux' ); get_ixflux = iLookFLUX%mLayerNrgFlux ! net energy flux for each layer in the snow+soil domain (J m-3 s-1) + case('iLayerConductiveFlux' ); get_ixFlux = iLookFLUX%iLayerConductiveFlux ! conductive energy flux at layer interfaces at end of time step (W m-2) + case('iLayerAdvectiveFlux' ); get_ixFlux = iLookFLUX%iLayerAdvectiveFlux ! advective energy flux at layer interfaces at end of time step (W m-2) + case('iLayerNrgFlux' ); get_ixFlux = iLookFLUX%iLayerNrgFlux ! energy flux at layer interfaces at the end of the time step (W m-2) + case('mLayerNrgFlux' ); get_ixFlux = iLookFLUX%mLayerNrgFlux ! net energy flux for each layer in the snow+soil domain (J m-3 s-1) ! liquid water fluxes for the snow domain - case('scalarSnowDrainage' ); get_ixflux = iLookFLUX%scalarSnowDrainage ! drainage from the bottom of the snow profile (m s-1) - case('iLayerLiqFluxSnow' ); get_ixflux = iLookFLUX%iLayerLiqFluxSnow ! liquid flux at snow layer interfaces at the end of the time step (m s-1) - case('mLayerLiqFluxSnow' ); get_ixflux = iLookFLUX%mLayerLiqFluxSnow ! net liquid water flux for each snow layer (s-1) + case('scalarSnowDrainage' ); get_ixFlux = iLookFLUX%scalarSnowDrainage ! drainage from the bottom of the snow profile (m s-1) + case('iLayerLiqFluxSnow' ); get_ixFlux = iLookFLUX%iLayerLiqFluxSnow ! liquid flux at snow layer interfaces at the end of the time step (m s-1) + case('mLayerLiqFluxSnow' ); get_ixFlux = iLookFLUX%mLayerLiqFluxSnow ! net liquid water flux for each snow layer (s-1) ! liquid water fluxes for the soil domain - case('scalarRainPlusMelt' ); get_ixflux = iLookFLUX%scalarRainPlusMelt ! rain plus melt, as input to soil before calculating surface runoff (m s-1) - case('scalarMaxInfilRate' ); get_ixflux = iLookFLUX%scalarMaxInfilRate ! maximum infiltration rate (m s-1) - case('scalarInfiltration' ); get_ixflux = iLookFLUX%scalarInfiltration ! infiltration of water into the soil profile (m s-1) - case('scalarExfiltration' ); get_ixflux = iLookFLUX%scalarExfiltration ! exfiltration of water from the top of the soil profile (m s-1) - case('scalarSurfaceRunoff' ); get_ixflux = iLookFLUX%scalarSurfaceRunoff ! surface runoff (m s-1) - case('mLayerSatHydCondMP' ); get_ixflux = iLookFLUX%mLayerSatHydCondMP ! saturated hydraulic conductivity of macropores in each layer (m s-1) - case('mLayerSatHydCond' ); get_ixflux = iLookFLUX%mLayerSatHydCond ! saturated hydraulic conductivity in each layer (m s-1) - case('iLayerSatHydCond' ); get_ixflux = iLookFLUX%iLayerSatHydCond ! saturated hydraulic conductivity in each layer interface (m s-1) - case('mLayerHydCond' ); get_ixflux = iLookFLUX%mLayerHydCond ! hydraulic conductivity in each layer (m s-1) - case('iLayerLiqFluxSoil' ); get_ixflux = iLookFLUX%iLayerLiqFluxSoil ! liquid flux at soil layer interfaces at the end of the time step (m s-1) - case('mLayerLiqFluxSoil' ); get_ixflux = iLookFLUX%mLayerLiqFluxSoil ! net liquid water flux for each soil layer (s-1) - case('mLayerBaseflow' ); get_ixflux = iLookFLUX%mLayerBaseflow ! baseflow from each soil layer (m s-1) - case('mLayerColumnInflow' ); get_ixflux = iLookFLUX%mLayerColumnInflow ! total inflow to each layer in a given soil column (m3 s-1) - case('mLayerColumnOutflow' ); get_ixflux = iLookFLUX%mLayerColumnOutflow ! total outflow from each layer in a given soil column (m3 s-1) - case('scalarSoilBaseflow' ); get_ixflux = iLookFLUX%scalarSoilBaseflow ! total baseflow from throughout the soil profile (m s-1) - case('scalarSoilDrainage' ); get_ixflux = iLookFLUX%scalarSoilDrainage ! drainage from the bottom of the soil profile (m s-1) - case('scalarAquiferRecharge' ); get_ixflux = iLookFLUX%scalarAquiferRecharge ! recharge to the aquifer (m s-1) - case('scalarAquiferTranspire' ); get_ixflux = iLookFLUX%scalarAquiferTranspire ! transpiration from the aquifer (m s-1) - case('scalarAquiferBaseflow' ); get_ixflux = iLookFLUX%scalarAquiferBaseflow ! baseflow from the aquifer (m s-1) + case('scalarRainPlusMelt' ); get_ixFlux = iLookFLUX%scalarRainPlusMelt ! rain plus melt, as input to soil before calculating surface runoff (m s-1) + case('scalarMaxInfilRate' ); get_ixFlux = iLookFLUX%scalarMaxInfilRate ! maximum infiltration rate (m s-1) + case('scalarInfiltration' ); get_ixFlux = iLookFLUX%scalarInfiltration ! infiltration of water into the soil profile (m s-1) + case('scalarExfiltration' ); get_ixFlux = iLookFLUX%scalarExfiltration ! exfiltration of water from the top of the soil profile (m s-1) + case('scalarSurfaceRunoff' ); get_ixFlux = iLookFLUX%scalarSurfaceRunoff ! surface runoff (m s-1) + case('mLayerSatHydCondMP' ); get_ixFlux = iLookFLUX%mLayerSatHydCondMP ! saturated hydraulic conductivity of macropores in each layer (m s-1) + case('mLayerSatHydCond' ); get_ixFlux = iLookFLUX%mLayerSatHydCond ! saturated hydraulic conductivity in each layer (m s-1) + case('iLayerSatHydCond' ); get_ixFlux = iLookFLUX%iLayerSatHydCond ! saturated hydraulic conductivity in each layer interface (m s-1) + case('mLayerHydCond' ); get_ixFlux = iLookFLUX%mLayerHydCond ! hydraulic conductivity in each layer (m s-1) + case('iLayerLiqFluxSoil' ); get_ixFlux = iLookFLUX%iLayerLiqFluxSoil ! liquid flux at soil layer interfaces at the end of the time step (m s-1) + case('mLayerLiqFluxSoil' ); get_ixFlux = iLookFLUX%mLayerLiqFluxSoil ! net liquid water flux for each soil layer (s-1) + case('mLayerBaseflow' ); get_ixFlux = iLookFLUX%mLayerBaseflow ! baseflow from each soil layer (m s-1) + case('mLayerColumnInflow' ); get_ixFlux = iLookFLUX%mLayerColumnInflow ! total inflow to each layer in a given soil column (m3 s-1) + case('mLayerColumnOutflow' ); get_ixFlux = iLookFLUX%mLayerColumnOutflow ! total outflow from each layer in a given soil column (m3 s-1) + case('scalarSoilBaseflow' ); get_ixFlux = iLookFLUX%scalarSoilBaseflow ! total baseflow from throughout the soil profile (m s-1) + case('scalarSoilDrainage' ); get_ixFlux = iLookFLUX%scalarSoilDrainage ! drainage from the bottom of the soil profile (m s-1) + case('scalarAquiferRecharge' ); get_ixFlux = iLookFLUX%scalarAquiferRecharge ! recharge to the aquifer (m s-1) + case('scalarAquiferTranspire' ); get_ixFlux = iLookFLUX%scalarAquiferTranspire ! transpiration from the aquifer (m s-1) + case('scalarAquiferBaseflow' ); get_ixFlux = iLookFLUX%scalarAquiferBaseflow ! baseflow from the aquifer (m s-1) ! derived variables - case('scalarTotalET' ); get_ixflux = iLookFLUX%scalarTotalET ! total ET (kg m-2 s-1) - case('scalarTotalRunoff' ); get_ixflux = iLookFLUX%scalarTotalRunoff ! total runoff (m s-1) - case('scalarNetRadiation' ); get_ixflux = iLookFLUX%scalarNetRadiation ! net radiation (W m-2) + case('scalarTotalET' ); get_ixFlux = iLookFLUX%scalarTotalET ! total ET (kg m-2 s-1) + case('scalarTotalRunoff' ); get_ixFlux = iLookFLUX%scalarTotalRunoff ! total runoff (m s-1) + case('scalarNetRadiation' ); get_ixFlux = iLookFLUX%scalarNetRadiation ! net radiation (W m-2) ! return missing if variable not found case default - get_ixflux = integerMissing + get_ixFlux = integerMissing end select - end function get_ixflux + end function get_ixFlux ! ******************************************************************************************************************* - ! public function get_ixderiv: get the index of the named variables for the model derivatives + ! public function get_ixDeriv: get the index of the named variables for the model derivatives ! ******************************************************************************************************************* - function get_ixderiv(varName) + function get_ixDeriv(varName) USE var_lookup,only:iLookDERIV ! indices of the named variables implicit none ! define dummy variables character(*), intent(in) :: varName ! parameter name - integer(i4b) :: get_ixderiv ! index of the named variable + integer(i4b) :: get_ixDeriv ! index of the named variable ! get the index of the named variables select case(trim(varName)) ! derivatives in net vegetation energy fluxes w.r.t. relevant state variables - case('dCanairNetFlux_dCanairTemp' ); get_ixderiv = iLookDERIV%dCanairNetFlux_dCanairTemp ! derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) - case('dCanairNetFlux_dCanopyTemp' ); get_ixderiv = iLookDERIV%dCanairNetFlux_dCanopyTemp ! derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) - case('dCanairNetFlux_dGroundTemp' ); get_ixderiv = iLookDERIV%dCanairNetFlux_dGroundTemp ! derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) - case('dCanopyNetFlux_dCanairTemp' ); get_ixderiv = iLookDERIV%dCanopyNetFlux_dCanairTemp ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) - case('dCanopyNetFlux_dCanopyTemp' ); get_ixderiv = iLookDERIV%dCanopyNetFlux_dCanopyTemp ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) - case('dCanopyNetFlux_dGroundTemp' ); get_ixderiv = iLookDERIV%dCanopyNetFlux_dGroundTemp ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) - case('dCanopyNetFlux_dCanWat' ); get_ixderiv = iLookDERIV%dCanopyNetFlux_dCanWat ! derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) - case('dGroundNetFlux_dCanairTemp' ); get_ixderiv = iLookDERIV%dGroundNetFlux_dCanairTemp ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) - case('dGroundNetFlux_dCanopyTemp' ); get_ixderiv = iLookDERIV%dGroundNetFlux_dCanopyTemp ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) - case('dGroundNetFlux_dGroundTemp' ); get_ixderiv = iLookDERIV%dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) - case('dGroundNetFlux_dCanWat' ); get_ixderiv = iLookDERIV%dGroundNetFlux_dCanWat ! derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) + case('dCanairNetFlux_dCanairTemp' ); get_ixDeriv = iLookDERIV%dCanairNetFlux_dCanairTemp ! derivative in net canopy air space flux w.r.t. canopy air temperature (W m-2 K-1) + case('dCanairNetFlux_dCanopyTemp' ); get_ixDeriv = iLookDERIV%dCanairNetFlux_dCanopyTemp ! derivative in net canopy air space flux w.r.t. canopy temperature (W m-2 K-1) + case('dCanairNetFlux_dGroundTemp' ); get_ixDeriv = iLookDERIV%dCanairNetFlux_dGroundTemp ! derivative in net canopy air space flux w.r.t. ground temperature (W m-2 K-1) + case('dCanopyNetFlux_dCanairTemp' ); get_ixDeriv = iLookDERIV%dCanopyNetFlux_dCanairTemp ! derivative in net canopy flux w.r.t. canopy air temperature (W m-2 K-1) + case('dCanopyNetFlux_dCanopyTemp' ); get_ixDeriv = iLookDERIV%dCanopyNetFlux_dCanopyTemp ! derivative in net canopy flux w.r.t. canopy temperature (W m-2 K-1) + case('dCanopyNetFlux_dGroundTemp' ); get_ixDeriv = iLookDERIV%dCanopyNetFlux_dGroundTemp ! derivative in net canopy flux w.r.t. ground temperature (W m-2 K-1) + case('dCanopyNetFlux_dCanWat' ); get_ixDeriv = iLookDERIV%dCanopyNetFlux_dCanWat ! derivative in net canopy fluxes w.r.t. canopy total water content (J kg-1 s-1) + case('dGroundNetFlux_dCanairTemp' ); get_ixDeriv = iLookDERIV%dGroundNetFlux_dCanairTemp ! derivative in net ground flux w.r.t. canopy air temperature (W m-2 K-1) + case('dGroundNetFlux_dCanopyTemp' ); get_ixDeriv = iLookDERIV%dGroundNetFlux_dCanopyTemp ! derivative in net ground flux w.r.t. canopy temperature (W m-2 K-1) + case('dGroundNetFlux_dGroundTemp' ); get_ixDeriv = iLookDERIV%dGroundNetFlux_dGroundTemp ! derivative in net ground flux w.r.t. ground temperature (W m-2 K-1) + case('dGroundNetFlux_dCanWat' ); get_ixDeriv = iLookDERIV%dGroundNetFlux_dCanWat ! derivative in net ground fluxes w.r.t. canopy total water content (J kg-1 s-1) ! derivatives in evaporative fluxes w.r.t. relevant state variables - case('dCanopyEvaporation_dTCanair' ); get_ixderiv = iLookDERIV%dCanopyEvaporation_dTCanair ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - case('dCanopyEvaporation_dTCanopy' ); get_ixderiv = iLookDERIV%dCanopyEvaporation_dTCanopy ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - case('dCanopyEvaporation_dTGround' ); get_ixderiv = iLookDERIV%dCanopyEvaporation_dTGround ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - case('dCanopyEvaporation_dCanWat' ); get_ixderiv = iLookDERIV%dCanopyEvaporation_dCanWat ! derivative in canopy evaporation w.r.t. canopy total water content (s-1) - case('dGroundEvaporation_dTCanair' ); get_ixderiv = iLookDERIV%dGroundEvaporation_dTCanair ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) - case('dGroundEvaporation_dTCanopy' ); get_ixderiv = iLookDERIV%dGroundEvaporation_dTCanopy ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) - case('dGroundEvaporation_dTGround' ); get_ixderiv = iLookDERIV%dGroundEvaporation_dTGround ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) - case('dGroundEvaporation_dCanWat' ); get_ixderiv = iLookDERIV%dGroundEvaporation_dCanWat ! derivative in ground evaporation w.r.t. canopy total water content (s-1) + case('dCanopyEvaporation_dTCanair' ); get_ixDeriv = iLookDERIV%dCanopyEvaporation_dTCanair ! derivative in canopy evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + case('dCanopyEvaporation_dTCanopy' ); get_ixDeriv = iLookDERIV%dCanopyEvaporation_dTCanopy ! derivative in canopy evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + case('dCanopyEvaporation_dTGround' ); get_ixDeriv = iLookDERIV%dCanopyEvaporation_dTGround ! derivative in canopy evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + case('dCanopyEvaporation_dCanWat' ); get_ixDeriv = iLookDERIV%dCanopyEvaporation_dCanWat ! derivative in canopy evaporation w.r.t. canopy total water content (s-1) + case('dGroundEvaporation_dTCanair' ); get_ixDeriv = iLookDERIV%dGroundEvaporation_dTCanair ! derivative in ground evaporation w.r.t. canopy air temperature (kg m-2 s-1 K-1) + case('dGroundEvaporation_dTCanopy' ); get_ixDeriv = iLookDERIV%dGroundEvaporation_dTCanopy ! derivative in ground evaporation w.r.t. canopy temperature (kg m-2 s-1 K-1) + case('dGroundEvaporation_dTGround' ); get_ixDeriv = iLookDERIV%dGroundEvaporation_dTGround ! derivative in ground evaporation w.r.t. ground temperature (kg m-2 s-1 K-1) + case('dGroundEvaporation_dCanWat' ); get_ixDeriv = iLookDERIV%dGroundEvaporation_dCanWat ! derivative in ground evaporation w.r.t. canopy total water content (s-1) ! derivatives in transpiration - case('dCanopyTrans_dTCanair' ); get_ixderiv = iLookDERIV%dCanopyTrans_dTCanair ! derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) - case('dCanopyTrans_dTCanopy' ); get_ixderiv = iLookDERIV%dCanopyTrans_dTCanopy ! derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) - case('dCanopyTrans_dTGround' ); get_ixderiv = iLookDERIV%dCanopyTrans_dTGround ! derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) - case('dCanopyTrans_dCanWat' ); get_ixderiv = iLookDERIV%dCanopyTrans_dCanWat ! derivative in canopy transpiration w.r.t. canopy total water content (s-1) + case('dCanopyTrans_dTCanair' ); get_ixDeriv = iLookDERIV%dCanopyTrans_dTCanair ! derivative in canopy transpiration w.r.t. canopy air temperature (kg m-2 s-1 K-1) + case('dCanopyTrans_dTCanopy' ); get_ixDeriv = iLookDERIV%dCanopyTrans_dTCanopy ! derivative in canopy transpiration w.r.t. canopy temperature (kg m-2 s-1 K-1) + case('dCanopyTrans_dTGround' ); get_ixDeriv = iLookDERIV%dCanopyTrans_dTGround ! derivative in canopy transpiration w.r.t. ground temperature (kg m-2 s-1 K-1) + case('dCanopyTrans_dCanWat' ); get_ixDeriv = iLookDERIV%dCanopyTrans_dCanWat ! derivative in canopy transpiration w.r.t. canopy total water content (s-1) ! derivatives in canopy water w.r.t canopy temperature - case('dTheta_dTkCanopy' ); get_ixderiv = iLookDERIV%dTheta_dTkCanopy ! derivative of volumetric liquid water content w.r.t. temperature (K-1) - case('d2Theta_dTkCanopy2' ); get_ixderiv = iLookDERIV%d2Theta_dTkCanopy2 ! second derivative of volumetric liquid water content w.r.t. temperature - case('dCanLiq_dTcanopy' ); get_ixderiv = iLookDERIV%dCanLiq_dTcanopy ! derivative of canopy liquid storage w.r.t. temperature (kg m-2 K-1) - case('dFracLiqVeg_dTkCanopy' ); get_ixderiv = iLookDERIV%dFracLiqVeg_dTkCanopy ! derivative in fraction of (throughfall + drainage) w.r.t. temperature + case('dTheta_dTkCanopy' ); get_ixDeriv = iLookDERIV%dTheta_dTkCanopy ! derivative of volumetric liquid water content w.r.t. temperature (K-1) + case('d2Theta_dTkCanopy2' ); get_ixDeriv = iLookDERIV%d2Theta_dTkCanopy2 ! second derivative of volumetric liquid water content w.r.t. temperature + case('dCanLiq_dTcanopy' ); get_ixDeriv = iLookDERIV%dCanLiq_dTcanopy ! derivative of canopy liquid storage w.r.t. temperature (kg m-2 K-1) + case('dFracLiqVeg_dTkCanopy' ); get_ixDeriv = iLookDERIV%dFracLiqVeg_dTkCanopy ! derivative in fraction of (throughfall + drainage) w.r.t. temperature ! derivatives in canopy liquid fluxes w.r.t. canopy water - case('scalarCanopyLiqDeriv' ); get_ixderiv = iLookDERIV%scalarCanopyLiqDeriv ! derivative in (throughfall + canopy drainage) w.r.t. canopy liquid water (s-1) - case('scalarThroughfallRainDeriv' ); get_ixderiv = iLookDERIV%scalarThroughfallRainDeriv ! derivative in throughfall w.r.t. canopy liquid water (s-1) - case('scalarCanopyLiqDrainageDeriv' ); get_ixderiv = iLookDERIV%scalarCanopyLiqDrainageDeriv ! derivative in canopy drainage w.r.t. canopy liquid water (s-1) + case('scalarCanopyLiqDeriv' ); get_ixDeriv = iLookDERIV%scalarCanopyLiqDeriv ! derivative in (throughfall + canopy drainage) w.r.t. canopy liquid water (s-1) + case('scalarThroughfallRainDeriv' ); get_ixDeriv = iLookDERIV%scalarThroughfallRainDeriv ! derivative in throughfall w.r.t. canopy liquid water (s-1) + case('scalarCanopyLiqDrainageDeriv' ); get_ixDeriv = iLookDERIV%scalarCanopyLiqDrainageDeriv ! derivative in canopy drainage w.r.t. canopy liquid water (s-1) ! energy derivatives that might be treated as constant if heat capacity and thermal conductivity not updated - case('dVolHtCapBulk_dPsi0' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dPsi0 ! derivative in bulk heat capacity w.r.t. matric potential - case('dVolHtCapBulk_dTheta' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTheta ! derivative in bulk heat capacity w.r.t. volumetric water content - case('dVolHtCapBulk_dCanWat' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. canopy volumetric water content - case('dVolHtCapBulk_dTk' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTk ! derivative in bulk heat capacity w.r.t. temperature - case('dVolHtCapBulk_dTkCanopy' ); get_ixderiv = iLookDERIV%dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. canopy temperature - case('dThermalC_dTempAbove' ); get_ixderiv = iLookDERIV%dThermalC_dTempAbove ! derivative in the thermal conductivity w.r.t. energy state in the layer above - case('dThermalC_dTempBelow' ); get_ixderiv = iLookDERIV%dThermalC_dTempBelow ! derivative in the thermal conductivity w.r.t. energy state in the layer above - case('dThermalC_dWatAbove' ); get_ixderiv = iLookDERIV%dThermalC_dWatAbove ! derivative in the thermal conductivity w.r.t. water state in the layer above - case('dThermalC_dWatBelow' ); get_ixderiv = iLookDERIV%dThermalC_dWatBelow ! derivative in the thermal conductivity w.r.t. water state in the layer above - case('dNrgFlux_dTempAbove' ); get_ixderiv = iLookDERIV%dNrgFlux_dTempAbove ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) - case('dNrgFlux_dTempBelow' ); get_ixderiv = iLookDERIV%dNrgFlux_dTempBelow ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) + case('dVolHtCapBulk_dPsi0' ); get_ixDeriv = iLookDERIV%dVolHtCapBulk_dPsi0 ! derivative in bulk heat capacity w.r.t. matric potential + case('dVolHtCapBulk_dTheta' ); get_ixDeriv = iLookDERIV%dVolHtCapBulk_dTheta ! derivative in bulk heat capacity w.r.t. volumetric water content + case('dVolHtCapBulk_dCanWat' ); get_ixDeriv = iLookDERIV%dVolHtCapBulk_dCanWat ! derivative in bulk heat capacity w.r.t. canopy volumetric water content + case('dVolHtCapBulk_dTk' ); get_ixDeriv = iLookDERIV%dVolHtCapBulk_dTk ! derivative in bulk heat capacity w.r.t. temperature + case('dVolHtCapBulk_dTkCanopy' ); get_ixDeriv = iLookDERIV%dVolHtCapBulk_dTkCanopy ! derivative in bulk heat capacity w.r.t. canopy temperature + case('dThermalC_dTempAbove' ); get_ixDeriv = iLookDERIV%dThermalC_dTempAbove ! derivative in the thermal conductivity w.r.t. energy state in the layer above + case('dThermalC_dTempBelow' ); get_ixDeriv = iLookDERIV%dThermalC_dTempBelow ! derivative in the thermal conductivity w.r.t. energy state in the layer above + case('dThermalC_dWatAbove' ); get_ixDeriv = iLookDERIV%dThermalC_dWatAbove ! derivative in the thermal conductivity w.r.t. water state in the layer above + case('dThermalC_dWatBelow' ); get_ixDeriv = iLookDERIV%dThermalC_dWatBelow ! derivative in the thermal conductivity w.r.t. water state in the layer above + case('dNrgFlux_dTempAbove' ); get_ixDeriv = iLookDERIV%dNrgFlux_dTempAbove ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) + case('dNrgFlux_dTempBelow' ); get_ixDeriv = iLookDERIV%dNrgFlux_dTempBelow ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) ! energy derivatives that might be treated as constant if Cm not updated - case('dCm_dTk' ); get_ixderiv = iLookDERIV%dCm_dTk ! derivative in Cm w.r.t. temperature (J kg K-2) - case('dCm_dTkCanopy' ); get_ixderiv = iLookDERIV%dCm_dTkCanopy ! derivative in Cm w.r.t. canopy temperature (J kg K-2) + case('dCm_dTk' ); get_ixDeriv = iLookDERIV%dCm_dTk ! derivative in Cm w.r.t. temperature (J kg K-2) + case('dCm_dTkCanopy' ); get_ixDeriv = iLookDERIV%dCm_dTkCanopy ! derivative in Cm w.r.t. canopy temperature (J kg K-2) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below - case('dNrgFlux_dWatAbove' ); get_ixderiv = iLookDERIV%dNrgFlux_dWatAbove ! derivatives in the flux w.r.t. water state temperature in the layer above - case('dNrgFlux_dWatBelow' ); get_ixderiv = iLookDERIV%dNrgFlux_dWatBelow ! derivatives in the flux w.r.t. water state in the layer below + case('dNrgFlux_dWatAbove' ); get_ixDeriv = iLookDERIV%dNrgFlux_dWatAbove ! derivatives in the flux w.r.t. water state temperature in the layer above + case('dNrgFlux_dWatBelow' ); get_ixDeriv = iLookDERIV%dNrgFlux_dWatBelow ! derivatives in the flux w.r.t. water state in the layer below ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above - case('iLayerLiqFluxSnowDeriv' ); get_ixderiv = iLookDERIV%iLayerLiqFluxSnowDeriv ! derivative in vertical liquid water flux at layer interfaces (m s-1) + case('iLayerLiqFluxSnowDeriv' ); get_ixDeriv = iLookDERIV%iLayerLiqFluxSnowDeriv ! derivative in vertical liquid water flux at layer interfaces (m s-1) ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables - case('dVolTot_dPsi0' ); get_ixderiv = iLookDERIV%dVolTot_dPsi0 ! derivative in total water content w.r.t. total water matric potential (m-1) - case('d2VolTot_dPsi02' ); get_ixderiv = iLookDERIV%d2VolTot_dPsi02 ! second derivative in total water content w.r.t. total water matric potential - case('dq_dHydStateAbove' ); get_ixderiv = iLookDERIV%dq_dHydStateAbove ! change in the flux in layer interfaces w.r.t. state variables in the layer above - case('dq_dHydStateBelow' ); get_ixderiv = iLookDERIV%dq_dHydStateBelow ! change in the flux in layer interfaces w.r.t. state variables in the layer below - case('dq_dHydStateLayerSurfVec' ); get_ixderiv = iLookDERIV%dq_dHydStateLayerSurfVec ! change in the flux in soil surface interface w.r.t. state variables in layer above and below - case('mLayerdTheta_dPsi' ); get_ixderiv = iLookDERIV%mLayerdTheta_dPsi ! derivative in the soil water characteristic w.r.t. psi (m-1) - case('mLayerdPsi_dTheta' ); get_ixderiv = iLookDERIV%mLayerdPsi_dTheta ! derivative in the soil water characteristic w.r.t. theta (m) - case('dCompress_dPsi' ); get_ixderiv = iLookDERIV%dCompress_dPsi ! derivative in compressibility w.r.t matric head (m-1) + case('dVolTot_dPsi0' ); get_ixDeriv = iLookDERIV%dVolTot_dPsi0 ! derivative in total water content w.r.t. total water matric potential (m-1) + case('d2VolTot_dPsi02' ); get_ixDeriv = iLookDERIV%d2VolTot_dPsi02 ! second derivative in total water content w.r.t. total water matric potential + case('dq_dHydStateAbove' ); get_ixDeriv = iLookDERIV%dq_dHydStateAbove ! change in the flux in layer interfaces w.r.t. state variables in the layer above + case('dq_dHydStateBelow' ); get_ixDeriv = iLookDERIV%dq_dHydStateBelow ! change in the flux in layer interfaces w.r.t. state variables in the layer below + case('dq_dHydStateLayerSurfVec' ); get_ixDeriv = iLookDERIV%dq_dHydStateLayerSurfVec ! change in the flux in soil surface interface w.r.t. state variables in layer above and below + case('mLayerdTheta_dPsi' ); get_ixDeriv = iLookDERIV%mLayerdTheta_dPsi ! derivative in the soil water characteristic w.r.t. psi (m-1) + case('mLayerdPsi_dTheta' ); get_ixDeriv = iLookDERIV%mLayerdPsi_dTheta ! derivative in the soil water characteristic w.r.t. theta (m) + case('dCompress_dPsi' ); get_ixDeriv = iLookDERIV%dCompress_dPsi ! derivative in compressibility w.r.t matric head (m-1) ! derivative in baseflow flux w.r.t. aquifer storage - case('dBaseflow_dAquifer' ); get_ixderiv = iLookDERIV%dBaseflow_dAquifer ! derivative in baseflow flux w.r.t. aquifer storage (s-1) + case('dBaseflow_dAquifer' ); get_ixDeriv = iLookDERIV%dBaseflow_dAquifer ! derivative in baseflow flux w.r.t. aquifer storage (s-1) ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables - case('dq_dNrgStateAbove' ); get_ixderiv = iLookDERIV%dq_dNrgStateAbove ! change in the flux in layer interfaces w.r.t. state variables in the layer above - case('dq_dNrgStateBelow' ); get_ixderiv = iLookDERIV%dq_dNrgStateBelow ! change in the flux in layer interfaces w.r.t. state variables in the layer below - case('dq_dNrgStateLayerSurfVec' ); get_ixderiv = iLookDERIV%dq_dNrgStateLayerSurfVec ! change in the flux in soil surface interface w.r.t. state variables in layer above and below - case('dPsiLiq_dTemp' ); get_ixderiv = iLookDERIV%dPsiLiq_dTemp ! derivative in the liquid water matric potential w.r.t. temperature (m K-1) - case('dPsiLiq_dPsi0' ); get_ixderiv = iLookDERIV%dPsiLiq_dPsi0 ! derivative in liquid matric potential w.r.t. total matric potential (-) + case('dq_dNrgStateAbove' ); get_ixDeriv = iLookDERIV%dq_dNrgStateAbove ! change in the flux in layer interfaces w.r.t. state variables in the layer above + case('dq_dNrgStateBelow' ); get_ixDeriv = iLookDERIV%dq_dNrgStateBelow ! change in the flux in layer interfaces w.r.t. state variables in the layer below + case('dq_dNrgStateLayerSurfVec' ); get_ixDeriv = iLookDERIV%dq_dNrgStateLayerSurfVec ! change in the flux in soil surface interface w.r.t. state variables in layer above and below + case('dPsiLiq_dTemp' ); get_ixDeriv = iLookDERIV%dPsiLiq_dTemp ! derivative in the liquid water matric potential w.r.t. temperature (m K-1) + case('dPsiLiq_dPsi0' ); get_ixDeriv = iLookDERIV%dPsiLiq_dPsi0 ! derivative in liquid matric potential w.r.t. total matric potential (-) ! derivatives in soil transpiration w.r.t. canopy state variables - case('mLayerdTrans_dTCanair' ); get_ixderiv = iLookDERIV%mLayerdTrans_dTCanair ! derivatives in the soil layer transpiration flux w.r.t. canopy air temperature - case('mLayerdTrans_dTCanopy' ); get_ixderiv = iLookDERIV%mLayerdTrans_dTCanopy ! derivatives in the soil layer transpiration flux w.r.t. canopy temperature - case('mLayerdTrans_dTGround' ); get_ixderiv = iLookDERIV%mLayerdTrans_dTGround ! derivatives in the soil layer transpiration flux w.r.t. ground temperature - case('mLayerdTrans_dCanWat' ); get_ixderiv = iLookDERIV%mLayerdTrans_dCanWat ! derivatives in the soil layer transpiration flux w.r.t. canopy total water + case('mLayerdTrans_dTCanair' ); get_ixDeriv = iLookDERIV%mLayerdTrans_dTCanair ! derivatives in the soil layer transpiration flux w.r.t. canopy air temperature + case('mLayerdTrans_dTCanopy' ); get_ixDeriv = iLookDERIV%mLayerdTrans_dTCanopy ! derivatives in the soil layer transpiration flux w.r.t. canopy temperature + case('mLayerdTrans_dTGround' ); get_ixDeriv = iLookDERIV%mLayerdTrans_dTGround ! derivatives in the soil layer transpiration flux w.r.t. ground temperature + case('mLayerdTrans_dCanWat' ); get_ixDeriv = iLookDERIV%mLayerdTrans_dCanWat ! derivatives in the soil layer transpiration flux w.r.t. canopy total water ! derivatives in aquifer transpiration w.r.t. canopy state variables - case('dAquiferTrans_dTCanair' ); get_ixderiv = iLookDERIV%dAquiferTrans_dTCanair ! derivative in the aquifer transpiration flux w.r.t. canopy air temperature - case('dAquiferTrans_dTCanopy' ); get_ixderiv = iLookDERIV%dAquiferTrans_dTCanopy ! derivative in the aquifer transpiration flux w.r.t. canopy temperature - case('dAquiferTrans_dTGround' ); get_ixderiv = iLookDERIV%dAquiferTrans_dTGround ! derivative in the aquifer transpiration flux w.r.t. ground temperature - case('dAquiferTrans_dCanWat' ); get_ixderiv = iLookDERIV%dAquiferTrans_dCanWat ! derivative in the aquifer transpiration flux w.r.t. canopy total water + case('dAquiferTrans_dTCanair' ); get_ixDeriv = iLookDERIV%dAquiferTrans_dTCanair ! derivative in the aquifer transpiration flux w.r.t. canopy air temperature + case('dAquiferTrans_dTCanopy' ); get_ixDeriv = iLookDERIV%dAquiferTrans_dTCanopy ! derivative in the aquifer transpiration flux w.r.t. canopy temperature + case('dAquiferTrans_dTGround' ); get_ixDeriv = iLookDERIV%dAquiferTrans_dTGround ! derivative in the aquifer transpiration flux w.r.t. ground temperature + case('dAquiferTrans_dCanWat' ); get_ixDeriv = iLookDERIV%dAquiferTrans_dCanWat ! derivative in the aquifer transpiration flux w.r.t. canopy total water ! derivative in liquid water fluxes for the soil and snow domain w.r.t temperature - case('dFracLiqSnow_dTk' ); get_ixderiv = iLookDERIV%dFracLiqSnow_dTk ! derivative in fraction of liquid snow w.r.t. temperature - case('mLayerdTheta_dTk' ); get_ixderiv = iLookDERIV%mLayerdTheta_dTk ! derivative of volumetric liquid water content w.r.t. temperature (K-1) - case('mLayerd2Theta_dTk2' ); get_ixderiv = iLookDERIV%mLayerd2Theta_dTk2 ! second derivative of volumetric liquid water content w.r.t. temperature + case('dFracLiqSnow_dTk' ); get_ixDeriv = iLookDERIV%dFracLiqSnow_dTk ! derivative in fraction of liquid snow w.r.t. temperature + case('mLayerdTheta_dTk' ); get_ixDeriv = iLookDERIV%mLayerdTheta_dTk ! derivative of volumetric liquid water content w.r.t. temperature (K-1) + case('mLayerd2Theta_dTk2' ); get_ixDeriv = iLookDERIV%mLayerd2Theta_dTk2 ! second derivative of volumetric liquid water content w.r.t. temperature ! derivatives in time - case('mLayerdTemp_dt' ); get_ixderiv = iLookDERIV%mLayerdTemp_dt ! timestep change in layer temperature - case('scalarCanopydTemp_dt' ); get_ixderiv = iLookDERIV%scalarCanopydTemp_dt ! timestep change in canopy temperature - case('mLayerdWat_dt' ); get_ixderiv = iLookDERIV%mLayerdWat_dt ! timestep change in layer volumetric fraction of total water - case('scalarCanopydWat_dt' ); get_ixderiv = iLookDERIV%scalarCanopydWat_dt ! timestep change in canopy water content + case('mLayerdTemp_dt' ); get_ixDeriv = iLookDERIV%mLayerdTemp_dt ! timestep change in layer temperature + case('scalarCanopydTemp_dt' ); get_ixDeriv = iLookDERIV%scalarCanopydTemp_dt ! timestep change in canopy temperature + case('mLayerdWat_dt' ); get_ixDeriv = iLookDERIV%mLayerdWat_dt ! timestep change in layer volumetric fraction of total water + case('scalarCanopydWat_dt' ); get_ixDeriv = iLookDERIV%scalarCanopydWat_dt ! timestep change in canopy water content ! derivatives of temperature if enthalpy is the state variable - case('dCanairTemp_dEnthalpy' ); get_ixderiv = iLookDERIV%dCanairTemp_dEnthalpy ! derivative of canopy air temperature w.r.t. enthalpy - case('dCanopyTemp_dEnthalpy' ); get_ixderiv = iLookDERIV%dCanopyTemp_dEnthalpy ! derivative of canopy temperature w.r.t. enthalpy - case('dTemp_dEnthalpy' ); get_ixderiv = iLookDERIV%dTemp_dEnthalpy ! derivative of temperature w.r.t. enthalpy - case('dCanopyTemp_dCanWat' ); get_ixderiv = iLookDERIV%dCanopyTemp_dCanWat ! derivative of canopy temperature w.r.t. volumetric water content - case('dTemp_dTheta' ); get_ixderiv = iLookDERIV%dTemp_dTheta ! derivative of temperature w.r.t. volumetric water content - case('dTemp_dPsi0' ); get_ixderiv = iLookDERIV%dTemp_dPsi0 ! derivative of temperature w.r.t. total water matric potential + case('dCanairTemp_dEnthalpy' ); get_ixDeriv = iLookDERIV%dCanairTemp_dEnthalpy ! derivative of canopy air temperature w.r.t. enthalpy + case('dCanopyTemp_dEnthalpy' ); get_ixDeriv = iLookDERIV%dCanopyTemp_dEnthalpy ! derivative of canopy temperature w.r.t. enthalpy + case('dTemp_dEnthalpy' ); get_ixDeriv = iLookDERIV%dTemp_dEnthalpy ! derivative of temperature w.r.t. enthalpy + case('dCanopyTemp_dCanWat' ); get_ixDeriv = iLookDERIV%dCanopyTemp_dCanWat ! derivative of canopy temperature w.r.t. volumetric water content + case('dTemp_dTheta' ); get_ixDeriv = iLookDERIV%dTemp_dTheta ! derivative of temperature w.r.t. volumetric water content + case('dTemp_dPsi0' ); get_ixDeriv = iLookDERIV%dTemp_dPsi0 ! derivative of temperature w.r.t. total water matric potential case default - get_ixderiv = integerMissing + get_ixDeriv = integerMissing end select - end function get_ixderiv + end function get_ixDeriv ! ******************************************************************************************************************* - ! public function get_ixindex: get the index of the named variables for the model indices + ! public function get_ixIndex: get the index of the named variables for the model indices ! ******************************************************************************************************************* - function get_ixindex(varName) + function get_ixIndex(varName) USE var_lookup,only:iLookINDEX ! indices of the named variables implicit none ! define dummy variables @@ -936,68 +936,68 @@ function get_ixindex(varName) case('numberScalarSolutions'); get_ixINDEX = iLookINDEX%numberScalarSolutions ! number of scalar solutions (-) ! default case default - get_ixindex = integerMissing + get_ixIndex = integerMissing end select - end function get_ixindex + end function get_ixIndex ! ******************************************************************************************************************* - ! public function get_ixbpar: get the index of the named variables for the basin-average variables + ! public function get_ixBpar: get the index of the named variables for the basin-average variables ! ******************************************************************************************************************* - function get_ixbpar(varName) + function get_ixBpar(varName) USE var_lookup,only:iLookBPAR ! indices of the named variables implicit none ! define dummy variables character(*), intent(in) :: varName ! parameter name - integer(i4b) :: get_ixbpar ! index of the named variable + integer(i4b) :: get_ixBpar ! index of the named variable ! get the index of the named variables select case(trim(varName)) ! baseflow - case('basin__aquiferHydCond' ); get_ixbpar = iLookBPAR%basin__aquiferHydCond ! hydraulic conductivity of the basin aquifer (m s-1) - case('basin__aquiferScaleFactor'); get_ixbpar = iLookBPAR%basin__aquiferScaleFactor ! scaling factor for aquifer storage in the big bucket (m) - case('basin__aquiferBaseflowExp'); get_ixbpar = iLookBPAR%basin__aquiferBaseflowExp ! baseflow exponent for the big bucket (-) + case('basin__aquiferHydCond' ); get_ixBpar = iLookBPAR%basin__aquiferHydCond ! hydraulic conductivity of the basin aquifer (m s-1) + case('basin__aquiferScaleFactor'); get_ixBpar = iLookBPAR%basin__aquiferScaleFactor ! scaling factor for aquifer storage in the big bucket (m) + case('basin__aquiferBaseflowExp'); get_ixBpar = iLookBPAR%basin__aquiferBaseflowExp ! baseflow exponent for the big bucket (-) ! sub-grid routing - case('routingGammaShape' ); get_ixbpar = iLookBPAR%routingGammaShape ! shape parameter in Gamma distribution used for sub-grid routing (-) - case('routingGammaScale' ); get_ixbpar = iLookBPAR%routingGammaScale ! scale parameter in Gamma distribution used for sub-grid routing (s) + case('routingGammaShape' ); get_ixBpar = iLookBPAR%routingGammaShape ! shape parameter in Gamma distribution used for sub-grid routing (-) + case('routingGammaScale' ); get_ixBpar = iLookBPAR%routingGammaScale ! scale parameter in Gamma distribution used for sub-grid routing (s) ! get to here if cannot find the variable case default - get_ixbpar = integerMissing + get_ixBpar = integerMissing end select - end function get_ixbpar + end function get_ixBpar ! ******************************************************************************************************************* - ! public function get_ixbvar: get the index of the named variables for the basin-average variables + ! public function get_ixBvar: get the index of the named variables for the basin-average variables ! ******************************************************************************************************************* - function get_ixbvar(varName) + function get_ixBvar(varName) USE var_lookup,only:iLookBVAR ! indices of the named variables implicit none ! define dummy variables character(*), intent(in) :: varName ! parameter name - integer(i4b) :: get_ixbvar ! index of the named variable + integer(i4b) :: get_ixBvar ! index of the named variable ! get the index of the named variables select case(trim(varName)) ! derived variables - case('basin__TotalArea' ); get_ixbvar = iLookBVAR%basin__totalArea ! total basin area (m2) + case('basin__TotalArea' ); get_ixBvar = iLookBVAR%basin__totalArea ! total basin area (m2) ! scalar variables -- basin-average runoff and aquifer fluxes - case('basin__SurfaceRunoff' ); get_ixbvar = iLookBVAR%basin__SurfaceRunoff ! surface runoff (m s-1) - case('basin__ColumnOutflow' ); get_ixbvar = iLookBVAR%basin__ColumnOutflow ! outflow from all "outlet" HRUs (those with no downstream HRU) - case('basin__AquiferStorage' ); get_ixbvar = iLookBVAR%basin__AquiferStorage ! aquifer storage (m s-1) - case('basin__AquiferRecharge' ); get_ixbvar = iLookBVAR%basin__AquiferRecharge ! recharge to the aquifer (m s-1) - case('basin__AquiferBaseflow' ); get_ixbvar = iLookBVAR%basin__AquiferBaseflow ! baseflow from the aquifer (m s-1) - case('basin__AquiferTranspire' ); get_ixbvar = iLookBVAR%basin__AquiferTranspire ! transpiration from the aquifer (m s-1) - case('basin__TotalRunoff' ); get_ixbvar = iLookBVAR%basin__TotalRunoff ! total runoff to channel from all active components (m s-1) - case('basin__SoilDrainage' ); get_ixbvar = iLookBVAR%basin__SoilDrainage ! soil drainage (m s-1) + case('basin__SurfaceRunoff' ); get_ixBvar = iLookBVAR%basin__SurfaceRunoff ! surface runoff (m s-1) + case('basin__ColumnOutflow' ); get_ixBvar = iLookBVAR%basin__ColumnOutflow ! outflow from all "outlet" HRUs (those with no downstream HRU) + case('basin__AquiferStorage' ); get_ixBvar = iLookBVAR%basin__AquiferStorage ! aquifer storage (m s-1) + case('basin__AquiferRecharge' ); get_ixBvar = iLookBVAR%basin__AquiferRecharge ! recharge to the aquifer (m s-1) + case('basin__AquiferBaseflow' ); get_ixBvar = iLookBVAR%basin__AquiferBaseflow ! baseflow from the aquifer (m s-1) + case('basin__AquiferTranspire' ); get_ixBvar = iLookBVAR%basin__AquiferTranspire ! transpiration from the aquifer (m s-1) + case('basin__TotalRunoff' ); get_ixBvar = iLookBVAR%basin__TotalRunoff ! total runoff to channel from all active components (m s-1) + case('basin__SoilDrainage' ); get_ixBvar = iLookBVAR%basin__SoilDrainage ! soil drainage (m s-1) ! variables to compute runoff - case('routingRunoffFuture' ); get_ixbvar = iLookBVAR%routingRunoffFuture ! runoff in future time steps (m s-1) - case('routingFractionFuture' ); get_ixbvar = iLookBVAR%routingFractionFuture ! fraction of runoff in future time steps (-) - case('averageInstantRunoff' ); get_ixbvar = iLookBVAR%averageInstantRunoff ! instantaneous runoff (m s-1) - case('averageRoutedRunoff' ); get_ixbvar = iLookBVAR%averageRoutedRunoff ! routed runoff (m s-1) + case('routingRunoffFuture' ); get_ixBvar = iLookBVAR%routingRunoffFuture ! runoff in future time steps (m s-1) + case('routingFractionFuture' ); get_ixBvar = iLookBVAR%routingFractionFuture ! fraction of runoff in future time steps (-) + case('averageInstantRunoff' ); get_ixBvar = iLookBVAR%averageInstantRunoff ! instantaneous runoff (m s-1) + case('averageRoutedRunoff' ); get_ixBvar = iLookBVAR%averageRoutedRunoff ! routed runoff (m s-1) ! get to here if cannot find the variable case default - get_ixbvar = integerMissing + get_ixBvar = integerMissing end select - end function get_ixbvar + end function get_ixBvar ! ********************************************************************************************************* ! public function get_ixVarType: get the index of the named variable type @@ -1106,7 +1106,7 @@ subroutine get_ixUnknown(varName,typeName,vDex,err,message) end subroutine get_ixUnknown ! ******************************************************************************************************************* - ! public function get_ixfreq: get the index of the named variables for the output frequencies + ! public function get_ixFreq: get the index of the named variables for the output frequencies ! ******************************************************************************************************************* function get_ixLookup(varName) USE var_lookup,only:iLookLOOKUP ! indices of the named variables @@ -1126,14 +1126,14 @@ function get_ixLookup(varName) end function get_ixLookup ! ******************************************************************************************************************* - ! public function get_ixfreq: get the index of the named variables for the output frequencies + ! public function get_ixFreq: get the index of the named variables for the output frequencies ! ******************************************************************************************************************* - function get_ixfreq(varName) + function get_ixFreq(varName) USE var_lookup,only:iLookFREQ ! indices of the named variables implicit none ! define dummy variables character(*), intent(in) :: varName ! variable name - integer(i4b) :: get_ixfreq ! index of the named variable + integer(i4b) :: get_ixFreq ! index of the named variable ! get the index of the named variables select case(trim(varName)) case('day' ); get_ixFreq = iLookFREQ%day ! daily aggregation @@ -1142,9 +1142,9 @@ function get_ixfreq(varName) case('timestep'); get_ixFreq = iLookFREQ%timestep ! timestep-level output (no temporal aggregation) ! get to here if cannot find the variable case default - get_ixfreq = integerMissing + get_ixFreq = integerMissing end select - end function get_ixfreq + end function get_ixFreq ! *************************************************************************************************************** ! public function get_ixStat: get the named variables for the statistics diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 47a680306..ae33148db 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -948,7 +948,7 @@ subroutine read_output_file(err,message) case('1'); iFreq = iLookFREQ%timestep ! assume timestep-level output case('24'); iFreq = iLookFREQ%day ! assume daily output ! define cases where temporal aggregation is defined using a text string - case default; iFreq = get_ixfreq(freqName) + case default; iFreq = get_ixFreq(freqName) end select ! * check that we could find the index diff --git a/build/source/engine/ffile_info.f90 b/build/source/engine/ffile_info.f90 index eb1938f92..e0e7dea11 100644 --- a/build/source/engine/ffile_info.f90 +++ b/build/source/engine/ffile_info.f90 @@ -43,7 +43,7 @@ subroutine ffile_info(nGRU,err,message) USE summaFileManager,only:FORCING_FILELIST ! list of model forcing files USE globalData,only:forcFileInfo,data_step ! info on model forcing file USE globalData,only:forc_meta ! forcing metadata - USE get_ixname_module,only:get_ixtime,get_ixforce ! identify index of named variable + USE get_ixname_module,only:get_ixTime,get_ixForce ! identify index of named variable USE ascii_util_module,only:get_vlines ! get a vector of non-comment lines USE ascii_util_module,only:split_line ! split a line into words USE globalData,only:gru_struc ! gru-hru mapping structure @@ -185,7 +185,7 @@ subroutine ffile_info(nGRU,err,message) case('time','pptrate','SWRadAtm','LWRadAtm','airtemp','windspd','airpres','spechum') ! get variable index - ivar = get_ixforce(trim(varname)) + ivar = get_ixForce(trim(varname)) if(ivar < 0)then; err=40; message=trim(message)//"variableNotFound[var="//trim(varname)//"]"; return; end if if(ivar>size(forcFileInfo(iFile)%data_id))then; err=35; message=trim(message)//"indexOutOfRange[var="//trim(varname)//"]"; return; end if diff --git a/build/source/engine/read_force.f90 b/build/source/engine/read_force.f90 index 40cc70b84..8f50f05b3 100644 --- a/build/source/engine/read_force.f90 +++ b/build/source/engine/read_force.f90 @@ -418,7 +418,7 @@ subroutine readForcingData(currentJulDay,ncId,iFile,iRead,nHRUlocal,time_data,fo USE netcdf ! netcdf capability USE time_utils_module,only:compcalday ! convert julian day to calendar date USE time_utils_module,only:compJulDay ! convert calendar date to julian day - USE get_ixname_module,only:get_ixforce ! identify index of named variable + USE get_ixname_module,only:get_ixForce ! identify index of named variable ! dummy variables real(rkind),intent(in) :: currentJulDay ! Julian day of current time step integer(i4b) ,intent(in) :: ncId ! NetCDF ID diff --git a/build/source/engine/read_param.f90 b/build/source/engine/read_param.f90 index 7a9cbb273..110bf0352 100644 --- a/build/source/engine/read_param.f90 +++ b/build/source/engine/read_param.f90 @@ -52,7 +52,7 @@ subroutine read_param(iRunMode,checkHRU,startGRU,nHRU,nGRU,idStruct,mparStruct,b ! used to read model initial conditions USE summaFileManager,only:SETTINGS_PATH ! path for metadata files USE summaFileManager,only:PARAMETER_TRIAL ! file with parameter trial values - USE get_ixname_module,only:get_ixparam,get_ixbpar ! access function to find index of elements in structure + USE get_ixname_module,only:get_ixParam,get_ixBpar ! access function to find index of elements in structure USE globalData,only:index_map,gru_struc ! mapping from global HRUs to the elements in the data structures USE var_lookup,only:iLookPARAM,iLookTYPE,iLookID ! named variables to index elements of the data vectors implicit none @@ -231,7 +231,7 @@ subroutine read_param(iRunMode,checkHRU,startGRU,nHRU,nGRU,idStruct,mparStruct,b call netcdf_err(err,message); if (err/=0) then; err=20; return; end if ! get the local parameters - ixParam = get_ixparam( trim(parName) ) + ixParam = get_ixParam( trim(parName) ) if(ixParam/=integerMissing)then ! ********************************************************************************************** @@ -317,7 +317,7 @@ subroutine read_param(iRunMode,checkHRU,startGRU,nHRU,nGRU,idStruct,mparStruct,b else ! get the parameter index - ixParam = get_ixbpar( trim(parName) ) + ixParam = get_ixBpar( trim(parName) ) ! allow extra variables in the file that are not used if(ixParam==integerMissing) cycle From 289874a85339914e19458708d73eb10e8d3dca12 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 8 Apr 2024 16:48:46 +0900 Subject: [PATCH 1245/1472] remove print statements, no code changes --- build/source/engine/updateVarsWithPrime.f90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/source/engine/updateVarsWithPrime.f90 b/build/source/engine/updateVarsWithPrime.f90 index 30a5b0889..6fb10555b 100644 --- a/build/source/engine/updateVarsWithPrime.f90 +++ b/build/source/engine/updateVarsWithPrime.f90 @@ -427,7 +427,7 @@ subroutine updateVarsWithPrime(& else dCanairTemp_dEnthalpy = 1._rkind endif - !print*, scalarCanairEnthalpyTrial, scalarCanairTempTrial, 'scalarCanairTempTrial' + cycle ! no more to do on canopy air space elseif(ixDomainType==iname_veg)then if(enthalpyStateVec)then @@ -444,7 +444,7 @@ subroutine updateVarsWithPrime(& dCanopyTemp_dCanWat, & ! intent(inout): derivative of canopy temperature with canopy water err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - !print*, scalarCanopyEnthalpyTrial, scalarCanopyTempTrial, 'scalarCanopyTempTrial' + else dCanopyTemp_dEnthalpy = 1._rkind dCanopyTemp_dCanWat = 1._rkind @@ -461,7 +461,7 @@ subroutine updateVarsWithPrime(& dTemp_dTheta(iLayer), & ! intent(inout): derivative of layer temperature with volumetric total water content err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - !print*, mLayerEnthalpyTrial(iLayer), mLayerTempTrial(iLayer), 'mLayerTempTrial snow' + else dTemp_dEnthalpy(iLayer) = 1._rkind dTemp_dTheta(iLayer) = 1._rkind @@ -487,7 +487,7 @@ subroutine updateVarsWithPrime(& dTemp_dPsi0(ixControlIndex), & ! intent(inout): derivative of layer temperature with total water matric potential err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - !print*, mLayerEnthalpyTrial(iLayer), mLayerTempTrial(iLayer), 'mLayerTempTrial soil' + else dTemp_dEnthalpy(iLayer) = 1._rkind dTemp_dTheta(iLayer) = 1._rkind From a03dd7454fc429b0a99685876597b142ccf83eeb Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 8 Apr 2024 20:30:58 +0900 Subject: [PATCH 1246/1472] remove print statement --- build/source/engine/check_icond.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/build/source/engine/check_icond.f90 b/build/source/engine/check_icond.f90 index 1e413e0d6..2c4901bed 100644 --- a/build/source/engine/check_icond.f90 +++ b/build/source/engine/check_icond.f90 @@ -337,7 +337,6 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - print*,scalarCanairTemp,scalarCanairEnthalpy end if ! (if enthalpy is the state variable) From 4e4d235e38634c6ee9f1176cd0bd1c96bc82e8da Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 9 Apr 2024 11:32:31 +0900 Subject: [PATCH 1247/1472] divide T2enthTemp into domain subroutines, and call all of them inside updateVars instead of outside --- build/source/driver/summa_restart.f90 | 2 +- build/source/engine/check_icond.f90 | 204 ++++---- build/source/engine/coupled_em.f90 | 217 +++++++-- build/source/engine/enthalpyTemp.f90 | 490 ++++++++++---------- build/source/engine/eval8summa.f90 | 74 +-- build/source/engine/systemSolv.f90 | 61 +-- build/source/engine/updateVars.f90 | 119 ++++- build/source/engine/updateVarsWithPrime.f90 | 59 ++- build/source/engine/varSubstep.f90 | 93 ++-- 9 files changed, 717 insertions(+), 602 deletions(-) diff --git a/build/source/driver/summa_restart.f90 b/build/source/driver/summa_restart.f90 index 70036e4bf..e1bf3b438 100644 --- a/build/source/driver/summa_restart.f90 +++ b/build/source/driver/summa_restart.f90 @@ -153,7 +153,7 @@ subroutine summa_readRestart(summa1_struc, err, message) progStruct, & ! intent(inout): model prognostic variables diagStruct, & ! intent(inout): model diagnostic variables mparStruct, & ! intent(in): model parameters - indxStruct, & ! intent(inout): layer indexes + indxStruct, & ! intent(in): layer indexes lookupStruct, & ! intent(in): lookup tables enthalpyStateVec, & ! intent(in): flag if enthalpy is the state variable use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy diff --git a/build/source/engine/check_icond.f90 b/build/source/engine/check_icond.f90 index 2c4901bed..579121469 100644 --- a/build/source/engine/check_icond.f90 +++ b/build/source/engine/check_icond.f90 @@ -64,9 +64,11 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU USE updatState_module,only:updateSnow ! update snow states USE updatState_module,only:updateSoil ! update soil states USE indexState_module,only:indexState ! index the state variables - USE enthalpyTemp_module,only:T2enthTemp ! convert temperature to enthalpy - USE enthalpyTemp_module,only:enthTemp_or_enthalpy ! add phase change terms to delta temperature component of enthalpy or vice versa - + USE enthalpyTemp_module,only:T2enthTemp_cas ! convert temperature to enthalpy for canopy air space + USE enthalpyTemp_module,only:T2enthTemp_veg ! convert temperature to enthalpy for vegetation + USE enthalpyTemp_module,only:T2enthTemp_snow ! convert temperature to enthalpy for snow + USE enthalpyTemp_module,only:T2enthTemp_soil ! convert temperature to enthalpy for soil + implicit none ! -------------------------------------------------------------------------------------------------------- @@ -76,7 +78,7 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU type(gru_hru_doubleVec),intent(inout) :: diagData ! diagnostic vars type(gru_hru_doubleVec),intent(inout) :: progData ! prognostic vars type(gru_hru_doubleVec),intent(in) :: mparData ! parameters - type(gru_hru_intVec),intent(inout) :: indxData ! layer indexes + type(gru_hru_intVec),intent(in) :: indxData ! layer indexes type(gru_hru_z_vLookup),intent(in) :: lookupData ! lookup table data logical(lgt),intent(in) :: enthalpyStateVec ! flag if enthalpy is the state variable logical(lgt),intent(in) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function @@ -135,31 +137,35 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU ! associate local variables with variables in the data structures associate(& ! state variables in the canopy air space - scalarCanairTemp => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanairTemp)%dat(1) , & ! canopy air temperature (K) - scalarCanairEnthalpy => diagData%gru(iGRU)%hru(iHRU)%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) , & ! canopy air enthalpy (J m-3) + scalarCanairTemp => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! canopy air temperature (K) + scalarCanairEnthalpy => diagData%gru(iGRU)%hru(iHRU)%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! canopy air enthalpy (J m-3) ! state variables in the vegetation canopy - scalarCanopyTemp => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyTemp)%dat(1) , & ! canopy temperature (K) - scalarCanopyEnthTemp => diagData%gru(iGRU)%hru(iHRU)%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) , & ! canopy temperature component of enthalpy (J m-3) - scalarCanopyEnthalpy => diagData%gru(iGRU)%hru(iHRU)%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) , & ! canopy enthalpy (J m-3) - scalarCanopyWat => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyWat)%dat(1) , & ! mass of water on the vegetation canopy (kg m-2) - scalarCanopyIce => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyIce)%dat(1) , & ! mass of ice on the vegetation canopy (kg m-2) + scalarCanopyTemp => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! canopy temperature (K) + scalarCanopyEnthTemp => diagData%gru(iGRU)%hru(iHRU)%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! canopy temperature component of enthalpy (J m-3) + scalarCanopyEnthalpy => diagData%gru(iGRU)%hru(iHRU)%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! canopy enthalpy (J m-3) + scalarCanopyLiq => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyIce => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! mass of ice on the vegetation canopy (kg m-2) + heightCanopyTop => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%heightCanopyTop)%dat(1) ,& ! height of the top of the canopy layer (m) + heightCanopyBottom => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%heightCanopyBottom)%dat(1) ,& ! height of the bottom of the canopy layer (m) + specificHeatVeg => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%specificHeatVeg)%dat(1) ,& ! specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%maxMassVegetation)%dat(1) ,& ! maximum mass of vegetation (kg m-2) ! state variables in the snow+soil domain - mLayerTemp => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerTemp)%dat , & ! temperature (K) - mLayerEnthTemp => diagData%gru(iGRU)%hru(iHRU)%var(iLookDIAG%mLayerEnthTemp)%dat , & ! temperature component of enthalpy (J m-3) - mLayerEnthalpy => diagData%gru(iGRU)%hru(iHRU)%var(iLookDIAG%mLayerEnthalpy)%dat , & ! enthalpy (J m-3) - mLayerVolFracWat => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracWat)%dat , & ! volumetric fraction of total water in each snow layer (-) - mLayerVolFracLiq => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracLiq)%dat , & ! volumetric fraction of liquid water in each snow layer (-) - mLayerVolFracIce => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracIce)%dat , & ! volumetric fraction of ice in each snow layer (-) - mLayerMatricHead => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerMatricHead)%dat , & ! matric head (m) - mLayerLayerType => indxData%gru(iGRU)%hru(iHRU)%var(iLookINDEX%layerType)%dat , & ! type of layer (ix_soil or ix_snow) + mLayerTemp => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerTemp)%dat ,& ! temperature (K) + mLayerEnthTemp => diagData%gru(iGRU)%hru(iHRU)%var(iLookDIAG%mLayerEnthTemp)%dat ,& ! temperature component of enthalpy (J m-3) + mLayerEnthalpy => diagData%gru(iGRU)%hru(iHRU)%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! enthalpy (J m-3) + mLayerVolFracLiq => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! volumetric fraction of liquid water in each snow layer (-) + mLayerVolFracIce => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracIce)%dat ,& ! volumetric fraction of ice in each snow layer (-) + mLayerMatricHead => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerMatricHead)%dat ,& ! matric head (m) + mLayerLayerType => indxData%gru(iGRU)%hru(iHRU)%var(iLookINDEX%layerType)%dat ,& ! type of layer (ix_soil or ix_snow) ! depth varying soil properties - vGn_alpha => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%vGn_alpha)%dat , & ! van Genutchen "alpha" parameter (m-1) - vGn_n => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%vGn_n)%dat , & ! van Genutchen "n" parameter (-) - theta_sat => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%theta_sat)%dat , & ! soil porosity (-) - theta_res => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%theta_res)%dat , & ! soil residual volumetric water content (-) + soil_dens_intr => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%soil_dens_intr)%dat ,& ! intrinsic soil density (kg m-3) + vGn_alpha => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%vGn_alpha)%dat ,& ! van Genutchen "alpha" parameter (m-1) + vGn_n => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%vGn_n)%dat ,& ! van Genutchen "n" parameter (-) + theta_sat => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%theta_sat)%dat ,& ! soil porosity (-) + theta_res => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%theta_res)%dat ,& ! soil residual volumetric water content (-) ! snow parameters - snowfrz_scale => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%snowfrz_scale)%dat(1) , & ! scaling parameter for the snow freezing curve (K-1) - FCapil => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%FCapil)%dat(1) & ! fraction of pore space in tension storage (-) + snowfrz_scale => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! scaling parameter for the snow freezing curve (K-1) + FCapil => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%FCapil)%dat(1) & ! fraction of pore space in tension storage (-) ) ! (associate local variables with model parameters) ! compute the constant in the freezing curve function (m K-1) @@ -172,7 +178,32 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU err=20; return else if(scalarCanopyIce > 0._rkind .and. scalarCanopyTemp > Tfreeze)then ! if here, ice content < threshold. Could be sublimation on previous timestep or simply wrong input. Print a warning - write(*,'(A,E22.16,A,F7.3,A,F7.3,A)') 'Warning: canopy ice content in restart file (=',scalarCanopyIce,') > 0 when canopy temperature (=',scalarCanopyTemp,') > Tfreeze (=',Tfreeze,'). Continuing.',NEW_LINE('a') + write(*,'(A,E22.16,A,F7.3,A,F7.3,A)') 'Warning: canopy ice content in restart file (=',scalarCanopyIce,') > 0 when canopy temperature (=',scalarCanopyTemp,') > Tfreeze (=',Tfreeze,'). Continuing.',NEW_LINE('a') + end if + scalarTheta = scalarCanopyIce + scalarCanopyLiq + + if(enthalpyStateVec)then ! enthalpy as state variable (cold start often only has temperature) + call T2enthTemp_cas(& + ! input + scalarCanairTemp, & ! intent(in): canopy air temperature (K) + ! output + scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + call T2enthTemp_veg(& + ! input + (heightCanopyTop-heightCanopyBottom), & ! intent(in): canopy depth (m) + specificHeatVeg, & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation, & ! intent(in): maximum mass of vegetation (kg m-2) + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + scalarCanopyTemp, & ! intent(in): canopy temperature (K) + scalarTheta, & ! intent(in): canopy water content (kg m-2) + ! output + scalarCanopyEnthTemp, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + scalarCanopyEnthalpy = scalarCanopyEnthTemp - LH_fus * scalarCanopyIce/ (heightCanopyTop-heightCanopyBottom) end if ! number of layers @@ -246,99 +277,66 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU ! ensure consistency among state variables call updateSnow(& ! input - mLayerTemp(iLayer), & ! intent(in): temperature (K) - scalarTheta, & ! intent(in): mass fraction of total water (-) - snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + mLayerTemp(iLayer), & ! intent(in): temperature (K) + scalarTheta, & ! intent(in): volumetric fraction of total water (-) + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) ! output - mLayerVolFracLiq(iLayer), & ! intent(out): volumetric fraction of liquid water (-) - mLayerVolFracIce(iLayer), & ! intent(out): volumetric fraction of ice (-) - fLiq, & ! intent(out): fraction of liquid water (-) - err,cmessage) ! intent(out): error control + mLayerVolFracLiq(iLayer), & ! intent(out): volumetric fraction of liquid water (-) + mLayerVolFracIce(iLayer), & ! intent(out): volumetric fraction of ice (-) + fLiq, & ! intent(out): fraction of liquid water (-) + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + if(enthalpyStateVec)then ! enthalpy as state variable (cold start often only has temperature) + call T2enthTemp_snow(& + ! input + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + mLayerTemp(iLayer), & ! intent(in): layer temperature (K) + scalarTheta, & ! intent(in): volumetric total water content (-) + ! output + mLayerEnthTemp(iLayer), & ! intent(out): temperature component of enthalpy of each snow layer (J m-3) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + mLayerEnthalpy(iLayer) = mLayerEnthTemp(iLayer) - iden_ice * LH_fus * mLayerVolFracIce(iLayer) + endif + ! ** soil case(iname_soil) ! ensure consistency among state variables call updateSoil(& ! input - mLayerTemp(iLayer), & ! intent(in): layer temperature (K) - mLayerMatricHead(iLayer-nSnow), & ! intent(in): matric head (m) + mLayerTemp(iLayer), & ! intent(in): layer temperature (K) + mLayerMatricHead(iLayer-nSnow), & ! intent(in): matric head (m) vGn_alpha(iSoil),vGn_n(iSoil),theta_sat(iSoil),theta_res(iSoil),vGn_m, & ! intent(in): van Genutchen soil parameters ! output - scalarTheta, & ! intent(out): volumetric fraction of total water (-) - mLayerVolFracLiq(iLayer), & ! intent(out): volumetric fraction of liquid water (-) - mLayerVolFracIce(iLayer), & ! intent(out): volumetric fraction of ice (-) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + scalarTheta, & ! intent(out): volumetric fraction of total water (-) + mLayerVolFracLiq(iLayer), & ! intent(out): volumetric fraction of liquid water (-) + mLayerVolFracIce(iLayer), & ! intent(out): volumetric fraction of ice (-) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(cmessage)//trim(cmessage); return; end if ! (check for errors) + + if(enthalpyStateVec)then ! enthalpy as state variable (cold start often only has temperature) + call T2enthTemp_soil(& + ! input + use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy + soil_dens_intr(iSoil), & ! intent(in): intrinsic soil density (kg m-3) + vGn_alpha(iSoil),vGn_n(iSoil),theta_sat(iSoil),theta_res(iSoil),vGn_m, & ! intent(in): van Genutchen soil parameters + iSoil, & ! intent(in): index of the control volume within the domain + lookupData%gru(iGRU)%hru(iHRU), & ! intent(in): lookup table data structure + mLayerTemp(iLayer), & ! intent(in): layer temperature (K) + mLayerMatricHead(iLayer-nSnow), & ! intent(in): matric head (m) + ! output + mLayerEnthTemp(iLayer), & ! intent(out): temperature component of enthalpy soil layer (J m-3) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(cmessage)//trim(cmessage); return; end if ! (check for errors) + mLayerEnthalpy(iLayer) = mLayerEnthTemp(iLayer) - iden_water * LH_fus * mLayerVolFracIce(iLayer) + endif case default; err=10; message=trim(message)//'unknown case for model layer'; return end select end do ! (looping through layers) - - ! ***** - ! if the model is using enthalpy as a state variable, compute the enthalpy starting point (cold start often only has temperature) - ! *********************************************************************************************************************** - if(enthalpyStateVec)then ! enthalpy as state variable - - ! initialize indices - call indexState(.true., & ! intent(in): compute enthalpy in canopy - .false., & ! intent(in): do not compute enthalpy in aquifer - nSnow,nSoil,nLayers, & ! intent(in): number of snow and soil layers, and total number of layers - indxData%gru(iGRU)%hru(iHRU), & ! intent(inout): indices defining model states and layers - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - ! initialize state subset as full state vector - nState = indxData%gru(iGRU)%hru(iHRU)%var(iLookINDEX%nState)%dat(1) - indxData%gru(iGRU)%hru(iHRU)%var(iLookINDEX%ixMapFull2Subset)%dat = (/ (i,i=1,nState) /) - indxData%gru(iGRU)%hru(iHRU)%var(iLookINDEX%ixMapSubset2Full)%dat = (/ (i,i=1,nState) /) - indxData%gru(iGRU)%hru(iHRU)%var(iLookINDEX%ixDomainType_subset)%dat = indxData%gru(iGRU)%hru(iHRU)%var(iLookINDEX%ixDomainType)%dat - - ! compute temperature component of enthalpy from temperature - call T2enthTemp(& - use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy - ! input: data structures - diagData%gru(iGRU)%hru(iHRU), & ! intent(in): model diagnostic variables for a local HRU - mparData%gru(iGRU)%hru(iHRU), & ! intent(in): parameter data structure - indxData%gru(iGRU)%hru(iHRU), & ! intent(in): model indices - lookupData%gru(iGRU)%hru(iHRU), & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTemp, & ! intent(in): value of canopy air temperature (K) - scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) - scalarCanopyWat, & ! intent(in): value of canopy total water (kg m-2) - ! input: variables for the snow-soil domain - mLayerTemp, & ! intent(in): vector of layer temperature (K) - mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) - mLayerMatricHead, & ! intent(in): vector of total water matric potential (m) - ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthTemp, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthTemp, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! initialize the enthalpy - scalarCanopyEnthalpy = scalarCanopyEnthTemp - mLayerEnthalpy = mLayerEnthTemp - ! compute enthalpy for current values - call enthTemp_or_enthalpy(& - ! input: data structures - .true., & ! intent(in): flag to convert enthTemp to enthalpy - diagData%gru(iGRU)%hru(iHRU), & ! intent(in): model diagnostic variables for a local HRU - indxData%gru(iGRU)%hru(iHRU), & ! intent(in): model indices - ! input: ice content change - scalarCanopyIce, & ! intent(in): value for canopy ice content (kg m-2) - mLayerVolFracIce, & ! intent(in): vector of volumetric ice water content (-) - ! input/output: enthalpy - scalarCanopyEnthalpy, & ! intent(inout): enthTemp to enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy, & ! intent(inout): enthTemp to enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - end if ! (if enthalpy is the state variable) ! end association to variables in the data structures end associate diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 0c8b454f4..bd1abd0a2 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -151,6 +151,9 @@ subroutine coupled_em(& USE tempAdjust_module,only:tempAdjust ! adjust snow temperature associated with new snowfall USE var_derive_module,only:calcHeight ! module to calculate height at layer interfaces and layer mid-point USE computSnowDepth_module,only:computSnowDepth ! compute snow depth + USE enthalpyTemp_module,only:T2enthTemp_veg ! convert temperature to enthalpy for vegetation + USE enthalpyTemp_module,only:T2enthTemp_snow ! convert temperature to enthalpy for snow + USE enthalpyTemp_module,only:T2enthTemp_soil ! convert temperature to enthalpy for soil USE enthalpyTemp_module,only:enthTemp_or_enthalpy ! add phase change terms to delta temperature component of enthalpy or vice versa implicit none @@ -284,6 +287,10 @@ subroutine coupled_em(& logical(lgt) :: do_outer ! flag to denote if doing the outer steps surrounding the call to opSplittin real(rkind) :: dt_solvInner ! seconds in the maxstep subStep that have been completed logical(lgt),parameter :: computNrgBalance_var=.true. ! flag to compute enthalpy, must have computNrgBalance true in varSubStep (will compute enthalpy for BE even if not using enthalpy formulation) + logical(lgt) :: computeEnthalpy ! flag to compute enthalpy regardless of the model decision + logical(lgt) :: enthalpyStateVec ! flag if enthalpy is a state variable (ida) + logical(lgt) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use analytical solution + ! ---------------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="coupled_em/" @@ -311,7 +318,12 @@ subroutine coupled_em(& indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1) = 0 ! number of scalar solutions (-) ! link canopy depth to the information in the data structure - canopy: associate(canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ) ! intent(out): [dp] canopy depth (m) + canopy: associate(& + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! scaling parameter for the snow freezing curve (K-1) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! depth of the vegetation canopy (m) + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1) ,& ! specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1) & ! maximum mass of vegetation (kg m-2) + ) ! start by NOT pausing pauseFlag=.false. @@ -382,19 +394,38 @@ subroutine coupled_em(& ! associate local variables with information in the data structures associate(& + ! model decisions + ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! choice of numerical solver + ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& ! choice of variable in energy conservation backward Euler residual ! state variables in the vegetation canopy - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! canopy liquid water (kg m-2) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! canopy ice content (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! canopy liquid water (kg m-2) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! canopy ice content (kg m-2) ! state variables in the soil domain - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers) ,& ! depth of each soil layer (m) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat(nSnow+1:nLayers) ,& ! volumetric ice content in each soil layer (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(nSnow+1:nLayers) ,& ! volumetric liquid water content in each soil layer (-) - scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! aquifer storage (m) - scalarTotalSoilIce => diag_data%var(iLookDIAG%scalarTotalSoilIce)%dat(1) ,& ! total ice in the soil column (kg m-2) - scalarTotalSoilLiq => diag_data%var(iLookDIAG%scalarTotalSoilLiq)%dat(1) ,& ! total liquid water in the soil column (kg m-2) - scalarTotalSoilWat => diag_data%var(iLookDIAG%scalarTotalSoilWat)%dat(1) & ! total water in the soil column (kg m-2) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers) ,& ! depth of each soil layer (m) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat(nSnow+1:nLayers) ,& ! volumetric ice content in each soil layer (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(nSnow+1:nLayers) ,& ! volumetric liquid water content in each soil layer (-) + scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! aquifer storage (m) + scalarTotalSoilIce => diag_data%var(iLookDIAG%scalarTotalSoilIce)%dat(1) ,& ! total ice in the soil column (kg m-2) + scalarTotalSoilLiq => diag_data%var(iLookDIAG%scalarTotalSoilLiq)%dat(1) ,& ! total liquid water in the soil column (kg m-2) + scalarTotalSoilWat => diag_data%var(iLookDIAG%scalarTotalSoilWat)%dat(1) & ! total water in the soil column (kg m-2) ) ! (association of local variables with information in the data structures + + ! identify the need to check the mass balance, both methods should work if tolerance coarse enough + select case(ixNumericalMethod) + case(ida); checkMassBalance_ds = .false. ! IDA balance agreement levels are controlled by set tolerances + case(kinsol, numrec); checkMassBalance_ds = .true. ! KINSOL or numrec give finite difference dt_sub fluxes and were summed for an average flux + case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or numrec (or itertive, which is numrec)'; return + end select + + ! set the flag to compute enthalpy, may want to have this true always if want to output enthalpy + computeEnthalpy = .false. + enthalpyStateVec = .false. + use_lookup = .false. + if((ixNrgConserv .ne. closedForm .or. computNrgBalance_var) .and. ixNumericalMethod .ne. ida) computeEnthalpy = .true. ! use enthTemp to conserve energy or compute energy balance + if(ixNrgConserv .ne. closedForm .and. ixNumericalMethod==ida) enthalpyStateVec = .true. ! enthalpy as state variable + if(ixNrgConserv==enthalpyFDlu) use_lookup = .true. ! use lookup tables for soil enthalpy instead of analytical solution + ! save the liquid water and ice on the vegetation canopy scalarInitCanopyLiq = scalarCanopyLiq ! initial liquid water on the vegetation canopy (kg m-2) scalarInitCanopyIce = scalarCanopyIce ! initial ice on the vegetation canopy (kg m-2) @@ -596,8 +627,39 @@ subroutine coupled_em(& diag_data, & ! intent(inout): model diagnostic variables for a local HRU ! output: error control err,cmessage) ! intent(out): error control - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - endif ! if computing fluxes over vegetation + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + end if ! if computing fluxes over vegetation + + ! change enthalpy based on new canopy temperature + if(enthalpyStateVec)then + ! associate local variables with variables in the data structures + enthalpyVeg: associate(& + ! state variables in the vegetation canopy + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! canopy temperature (K) + scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! canopy temperature component of enthalpy (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! enthalpy of the vegetation canopy (J m-3) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) & ! mass of ice on the vegetation canopy (kg m-2) + ) ! (associate local variables with model parameters) + + if(computeVegFlux)then ! new temperature + call T2enthTemp_veg(& + ! input + canopyDepth, & ! intent(in): canopy depth (m) + specificHeatVeg, & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation, & ! intent(in): maximum mass of vegetation (kg m-2) + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + scalarCanopyTemp, & ! intent(in): canopy temperature (K) + (scalarCanopyLiq+scalarCanopyIce), & ! intent(in): canopy water content (kg m-2) + ! output + scalarCanopyEnthTemp, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) + err,cmessage) ! intent(out): error control + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + end if + + scalarCanopyEnthalpy = scalarCanopyEnthTemp - LH_fus * scalarCanopyIce/ canopyDepth ! new ice and/or temperature + end associate enthalpyVeg + end if ! (need to recalculate enthalpy state variable) ! initialize drainage and throughfall ! NOTE 1: this needs to be done before solving the energy and liquid water equations, to account for the heat advected with precipitation @@ -709,7 +771,7 @@ subroutine coupled_em(& flux_data, & ! intent(inout): model fluxes for a local HRU ! output modifiedLayers, & ! intent(out): flag to denote that layers were modified - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control if(err/=0)then; err=55; message=trim(message)//trim(cmessage); return; end if ! save the number of snow and soil layers @@ -725,9 +787,65 @@ subroutine coupled_em(& indx_data, & ! intent(inout): indices defining model states and layers err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - end if + ! get enthalpy from temperature if new layering + if( enthalpyStateVec .and. modifiedLayers )then + ! associate local variables with variables in the data structures + enthalpySnow: associate(& + ! variables in the snow and soil domains + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! temperature (K) + mLayerEnthTemp => diag_data%var(iLookDIAG%mLayerEnthTemp)%dat ,& ! temperature component of enthalpy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! enthalpy (J m-3) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! volumetric fraction of total water in each snow layer (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! volumetric fraction of ice in each snow layer (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] matric head (m) + ! depth-varying soil parameters + soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat ,& ! intent(in): [dp] surface layer intrinsic soil density (kg m-3) + vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat & ! intent(in): [dp(:)] soil residual volumetric water content (-) + ) ! (associate local variables with model parameters) + + if(nSnow>0)then + do iLayer=1,nSnow + mLayerVolFracWat(iLayer) = mLayerVolFracLiq(iLayer) + mLayerVolFracIce(iLayer)*(iden_ice/iden_water) + ! compute enthalpy for snow layers + call T2enthTemp_snow(& + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + mLayerTemp(iLayer), & ! intent(in): layer temperature (K) + mLayerVolFracWat(iLayer), & ! intent(in): volumetric total water content (-) + mLayerEnthTemp(iLayer), & ! intent(out): temperature component of enthalpy of each snow layer (J m-3) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + mLayerEnthalpy(iLayer) = mLayerEnthTemp(iLayer) - iden_ice * LH_fus * mLayerVolFracIce(iLayer) + end do ! looping through snow layers + endif + do iLayer=nSnow+1,nLayers + mLayerVolFracWat(iLayer) = mLayerVolFracLiq(iLayer) + mLayerVolFracIce(iLayer) + ! compute enthalpy for soil layers + iSoil = iLayer - nSnow + call T2enthTemp_soil(& + ! input + use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy + soil_dens_intr(iSoil), & ! intent(in): intrinsic soil density (kg m-3) + vGn_alpha(iSoil),vGn_n(iSoil),theta_sat(iSoil),theta_res(iSoil),vGn_m(iSoil), & ! intent(in): soil parameters + iSoil, & ! intent(in): index of the control volume within the domain + lookup_data, & ! intent(in): lookup table data structure + mLayerTemp(iLayer), & ! intent(in): layer temperature (K) + mLayerMatricHead(iSoil), & ! intent(in): matric head (m) + ! output + mLayerEnthTemp(iLayer), & ! intent(out): temperature component of enthalpy soil layer (J m-3) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + mLayerEnthalpy(iLayer) = mLayerEnthTemp(iLayer) - iden_water * LH_fus * mLayerVolFracIce(iLayer) + end do ! looping through soil layers + end associate enthalpySnow + end if ! (need to recalculate enthalpy state variable) + ! recreate the temporary data structures ! NOTE: resizeData(meta, old, new, ..) if(modifiedVegState .or. modifiedLayers)then @@ -762,7 +880,7 @@ subroutine coupled_em(& call diagn_evar(& ! input: control variables computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): canopy depth (m) + canopyDepth, & ! intent(in): canopy depth (m) ! input/output: data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices @@ -800,16 +918,17 @@ subroutine coupled_em(& ! associate local variables with information in the data structures init: associate(& ! depth-varying soil parameters + soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(1) ,& ! intent(in): [dp] surface layer intrinsic soil density (kg m-3) vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) - ! state variables in the vegetation canopy + ! variables in the vegetation canopy scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp] mass of ice on the vegetation canopy (kg m-2) scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp] mass of liquid water on the vegetation canopy (kg m-2) scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(out): [dp] mass of total water on the vegetation canopy (kg m-2) - ! state variables in the snow and soil domains + ! variables in the snow and soil domains mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(out): [dp(:)] volumetric fraction of total water (-) @@ -823,9 +942,26 @@ subroutine coupled_em(& ! compute the total water content in snow and soil ! NOTE: no ice expansion allowed for soil if(nSnow>0)& - mLayerVolFracWat( 1:nSnow ) = mLayerVolFracLiq( 1:nSnow ) + mLayerVolFracIce( 1:nSnow )*(iden_ice/iden_water) - mLayerVolFracWat(nSnow+1:nLayers) = mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) - + mLayerVolFracWat( 1:nSnow ) = mLayerVolFracLiq( 1:nSnow ) + mLayerVolFracIce( 1:nSnow )*(iden_ice/iden_water) + mLayerVolFracWat(nSnow+1:nLayers) = mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) + + if(enthalpyStateVec .and. nSnow==0 .and. prog_data%var(iLookPROG%scalarSWE)%dat(1)>0._rkind)then ! compute enthalpy of the top soil layer if changed with surface melt pond + call T2enthTemp_soil(& + ! input + use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy + soil_dens_intr, & ! intent(in): intrinsic soil density (kg m-3) + vGn_alpha(1),vGn_n(1),theta_sat(1),theta_res(1),vGn_m(1), & ! intent(in): van Genutchen soil parameters + 1_i4b, & ! intent(in): index of the control volume within the domain + lookup_data, & ! intent(in): lookup table data structure + prog_data%var(iLookPROG%mLayerTemp)%dat(nSnow+1), & ! intent(in): surface layer temperature (K) + mLayerMatricHead(1), & ! intent(in): surface layer matric head (m) + ! output + diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(nSnow+1), & ! intent(out): temperature component of enthalpy soil layer (J m-3) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(nSnow+1) = diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(nSnow+1) - iden_water * LH_fus * mLayerVolFracIce(nSnow+1) + end if + ! compute the liquid water matric potential (m) ! NOTE: include ice content as part of the solid porosity - major effect of ice is to reduce the pore size; ensure that effSat=1 at saturation ! (from Zhao et al., J. Hydrol., 1997: Numerical analysis of simultaneous heat and mass transfer...) @@ -966,7 +1102,7 @@ subroutine coupled_em(& ! compute the melt in each snow and soil layer if(nSnow>0)& - mLayerMeltFreeze(1:nSnow) = -( mLayerVolFracIce(1:nSnow) - mLayerVolFracIceInit(1:nSnow) ) * iden_ice + mLayerMeltFreeze(1:nSnow) = -( mLayerVolFracIce(1:nSnow) - mLayerVolFracIceInit(1:nSnow) ) *iden_ice mLayerMeltFreeze(nSnow+1:nLayers) = -( mLayerVolFracIce(nSnow+1:nLayers) - mLayerVolFracIceInit(nSnow+1:nLayers) )*iden_water deallocate(mLayerVolFracIceInit) @@ -998,6 +1134,9 @@ subroutine coupled_em(& ! update water scalarCanopyWat = scalarCanopyLiq + scalarCanopyIce + if(enthalpyStateVec)& ! recompute enthalpy of the canopy if changed ice content + diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) = diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) - LH_fus * scalarCanopyIce/ canopyDepth + end if ! (if computing the vegetation flux) ! * compute change in ice content of the top snow layer due to sublimation @@ -1016,7 +1155,7 @@ subroutine coupled_em(& tooMuchSublim, & ! intent(out): flag to denote that there was too much sublimation in a given time step mLayerDepth, & ! intent(inout) ! error control - err,message) ! intent(out): error control + err,message) ! intent(out): error control if(err/=0)then; err=55; return; end if ! process the flag for too much sublimation @@ -1060,6 +1199,10 @@ subroutine coupled_em(& prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (mLayerVolFracLiq(1:nSnow)*iden_water & + mLayerVolFracIce(1:nSnow)*iden_ice) * mLayerDepth(1:nSnow) ) mLayerVolFracWat(1) = mLayerVolFracLiq(1) + mLayerVolFracIce(1)*iden_ice/iden_water + + if(enthalpyStateVec)& ! recompute enthalpy of the top snow layer if changed ice content + diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(1) = diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(1) - iden_ice * LH_fus * mLayerVolFracIce(1) + endif end associate sublime @@ -1178,7 +1321,7 @@ subroutine coupled_em(& ! input: model control data_step, & ! time step (seconds) (nSnow > 0), & ! logical flag if snow layers exist - mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1), & ! freeezing curve parameter for snow (K-1) + snowfrz_scale, & ! freeezing curve parameter for snow (K-1) ! input: diagnostic scalar variables diag_data%var(iLookDIAG%scalarSnowfallTemp)%dat(1), & ! computed temperature of fresh snow (K) diag_data%var(iLookDIAG%scalarNewSnowDensity)%dat(1), & ! computed density of new snow (kg m-3) @@ -1203,6 +1346,16 @@ subroutine coupled_em(& * prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) prog_data%var(iLookPROG%mLayerVolFracWat)%dat(1) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1) & + prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1)*iden_ice/iden_water + if(enthalpyStateVec)then ! compute enthalpy of the top snow layer + call T2enthTemp_snow(& + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + prog_data%var(iLookPROG%mLayerTemp)%dat(1), & ! temperature of the top layer (K) + prog_data%var(iLookPROG%mLayerVolFracWat)%dat(1), & ! intent(in): volumetric total water content (-) + diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(1), & ! intent(out): temperature component of enthalpy of each snow layer (J m-3) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(1) = diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(1) - iden_ice * LH_fus * prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1) + end if end if ! re-assign dimension lengths @@ -1240,8 +1393,6 @@ subroutine coupled_em(& ! associate local variables with information in the data structures associate(& - ! model decisions - ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! choice of numerical solver ! model forcing scalarSnowfall => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarSnowfall) )%dat(1) ,& ! computed snowfall rate (kg m-2 s-1) scalarRainfall => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarRainfall) )%dat(1) ,& ! computed rainfall rate (kg m-2 s-1) @@ -1314,14 +1465,6 @@ subroutine coupled_em(& diag_data%var(iLookDIAG%scaledBalanceSnowMass)%dat(1) = meanBalance(7) ! s-1 diag_data%var(iLookDIAG%scaledBalanceSoilMass)%dat(1) = meanBalance(8) ! s-1 - - ! identify the need to check the mass balance, both methods should work if tolerance coarse enough - select case(ixNumericalMethod) - case(ida); checkMassBalance_ds = .false. ! IDA balance agreement levels are controlled by set tolerances - case(kinsol, numrec); checkMassBalance_ds = .true. ! KINSOL or numrec give finite difference dt_sub fluxes and were summed for an average flux - case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or numrec (or itertive, which is numrec)'; return - end select - ! ----- ! * balance checks for the canopy... ! ---------------------------------- @@ -1454,7 +1597,7 @@ subroutine coupled_em(& ! end association of local variables with information in the data structures end associate - ! end association to canopy depth + ! end association to canopy parameters end associate canopy ! save the total soil water (Liquid+Ice) @@ -1463,8 +1606,6 @@ subroutine coupled_em(& ! save the enthalpy or temperature component of enthalpy, and total enthalpy ! associate local variables with information in the data structures associate(& - ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& ! choice of variable in energy conservation backward Euler residual - ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! choice of numerical solver ! canopy enthalpy scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! ice content of the vegetation canopy (kg m-2) scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! temperature component of enthalpy of the vegetation canopy (K) @@ -1478,7 +1619,7 @@ subroutine coupled_em(& scalarTotalSnowEnthalpy => diag_data%var(iLookDIAG%scalarTotalSnowEnthalpy)%dat(1) & ! total enthalpy of the snow column (J m-3) ) ! (association of local variables with information in the data structures - if((ixNrgConserv .ne. closedForm .or. computNrgBalance_var) .and. ixNumericalMethod .ne. ida)then ! computeEnthalpy = .true., use enthTemp to conserve energy or compute energy balance + if(computeEnthalpy)then ! use enthTemp to conserve energy or compute energy balance ! initialize the enthalpy scalarCanopyEnthalpy = scalarCanopyEnthTemp mLayerEnthalpy = mLayerEnthTemp @@ -1499,7 +1640,7 @@ subroutine coupled_em(& if(err/=0)then; message=trim(message)//trim(cmessage); return; endif end if - if(ixNrgConserv .ne. closedForm .and. ixNumericalMethod==ida) then ! enthalpyStateVec = .true., enthalpy as state variable + if(enthalpyStateVec)then ! enthalpy as state variable ! initialize the temperature component of enthalpy scalarCanopyEnthTemp = scalarCanopyEnthalpy mLayerEnthTemp = mLayerEnthalpy diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 69d22364f..141ccfa80 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -63,6 +63,10 @@ module enthalpyTemp_module public::T2L_lookup_soil public::enthalpy2T_snwWat public::T2enthalpy_snwWat +public::T2enthTemp_cas +public::T2enthTemp_veg +public::T2enthTemp_snow +public::T2enthTemp_soil public::enthTemp_or_enthalpy public::enthalpy2T_cas public::enthalpy2T_veg @@ -418,286 +422,266 @@ end function T2enthalpy_snwWat ! ************************************************************************************************************************ -! public subroutine T2enthTemp: compute temperature component of enthalpy from temperature and total water content -! NOTE: only computes the states in the state subset +! public subroutine T2enthTemp_cas: compute temperature component of enthalpy from temperature and total water content, canopy air space ! ************************************************************************************************************************ -subroutine T2enthTemp(& - use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - ! output: enthalpy - scalarCanairEnthalpy, & ! intent(inout): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthTemp, & ! intent(inout): temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthTemp, & ! intent(inout): temperature component of enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,message) ! intent(out): error control +subroutine T2enthTemp_cas(& + scalarCanairTemp, & ! intent(in): canopy air temperature (K) + scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) + err,message) ! intent(out): error control ! ------------------------------------------------------------------------------------------------------------------------- - ! downwind routines - USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water - USE spline_int_module,only:splint ! use for cubic spline interpolation implicit none ! delare dummy variables ! ------------------------------------------------------------------------------------------------------------------------- - logical(lgt),intent(in) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function - ! input: data structures - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model indices - type(zLookup),intent(in) :: lookup_data ! lookup tables - ! input: state variables for the vegetation canopy - real(rkind),intent(in) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) - real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - ! input: variables for the snow-soil domain - real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + ! input: variables for the canopy air space + real(rkind),intent(in) :: scalarCanairTemp ! canopy air temperature (K) ! output: enthalpy - real(rkind),intent(inout) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - real(rkind),intent(inout) :: scalarCanopyEnthTemp ! temperature component of enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(inout) :: mLayerEnthTemp(:) ! temperature component of enthalpy of each snow+soil layer (J m-3) + real(rkind),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="T2enthTemp_cas/" + + scalarCanairEnthalpy = Cp_air * iden_air * (scalarCanairTemp - Tfreeze) + +end subroutine T2enthTemp_cas + +! ************************************************************************************************************************ +! public subroutine T2enthTemp_veg: compute temperature component of enthalpy from temperature and total water content, canopy +! ************************************************************************************************************************ +subroutine T2enthTemp_veg(& + canopyDepth, & ! intent(in): canopy depth (m) + specificHeatVeg, & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation, & ! intent(in): maximum mass of vegetation (kg m-2) + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + scalarCanopyTemp, & ! intent(in): canopy temperature (K) + scalarCanopyWat, & ! intent(in): canopy total water (kg m-2) + scalarCanopyEnthTemp, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) + err,message) ! intent(out): error control + ! ------------------------------------------------------------------------------------------------------------------------- + implicit none + ! delare dummy variables + ! ------------------------------------------------------------------------------------------------------------------------- + real(rkind),intent(in) :: canopyDepth ! canopy depth (m) + real(rkind),intent(in) :: specificHeatVeg ! specific heat of vegetation (J kg-1 K-1) + real(rkind),intent(in) :: maxMassVegetation ! maximum mass of vegetation (kg m-2) + real(rkind),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) + ! input: variables for the vegetation canopy + real(rkind),intent(in) :: scalarCanopyTemp ! canopy temperature (K) + real(rkind),intent(in) :: scalarCanopyWat ! canopy total water (kg m-2) + ! output: enthalpy + real(rkind),intent(out) :: scalarCanopyEnthTemp ! temperature component of enthalpy of the vegetation canopy (J m-3) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------------------------------- ! declare local variables - character(len=128) :: cmessage ! error message in downwind routine - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixDomainType ! name of a given model domain - integer(i4b) :: ixControlIndex ! index within a given model domain - real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - real(rkind) :: volFracWat ! volumetric fraction of total water, liquid+ice (-) - real(rkind) :: diff0 ! temperature difference of Tcrit from Tfreeze - real(rkind) :: diffT ! temperature difference of temp from Tfreeze - real(rkind) :: integral ! integral of snow freezing curve - real(rkind) :: dL ! derivative of soil lookup table with temperature at layer temperature - real(rkind) :: arg ! argument of soil hypergeometric function - real(rkind) :: gauss_hg_T ! soil hypergeometric function result - real(rkind) :: integral_unf ! integral of unfrozen soil water content (from Tfreeze to Tcrit) - real(rkind) :: integral_frz_low ! lower limit of integral of frozen soil water content (from Tfreeze to Tcrit) - real(rkind) :: integral_frz_upp ! upper limit of integral of frozen soil water content (from Tfreeze to soil temperature) - real(rkind) :: xConst ! constant in the freezing curve function (m K-1) - real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) + real(rkind) :: diffT ! temperature difference of temp from Tfreeze + real(rkind) :: integral ! integral of snow freezing curve ! enthalpy - real(rkind) :: enthVeg ! enthalpy of the vegetation (J m-3) - real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) - real(rkind) :: enthLiq ! enthalpy of the liquid region (J m-3) - real(rkind) :: enthIce ! enthalpy of the ice region (J m-3) - real(rkind) :: enthAir ! enthalpy of air (J m-3) - real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) - logical(lgt),parameter :: doTest=.false. ! flag to run unit test + real(rkind) :: enthVeg ! enthalpy of the vegetation (J m-3) + real(rkind) :: enthLiq ! enthalpy of the liquid region (J m-3) + real(rkind) :: enthIce ! enthalpy of the ice region (J m-3) ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - generalVars: associate(& - ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - ! mapping between the full state vector and the state subset - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset - ! type of domain, type of state variable, and index of control volume within domain - ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat & ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ) ! end associate statement - ! ------------------------------------------------------------------------------------------------------------------------------ + ! initialize error control + err=0; message="T2enthTemp_veg/" - ! initialize error control - err=0; message="T2enthTemp/" - - ! loop through model state variables - do iState=1,size(ixMapSubset2Full) + diffT = scalarCanopyTemp - Tfreeze + enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth - ! ----- - ! - compute indices... - ! -------------------- + if(diffT>=0._rkind)then + enthLiq = Cp_water * scalarCanopyWat * diffT / canopyDepth + enthIce = 0._rkind + else + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + enthLiq = Cp_water * scalarCanopyWat * integral / canopyDepth + enthIce = Cp_ice * scalarCanopyWat * ( diffT - integral ) / canopyDepth + endif - ! get domain type, and index of the control volume within the domain - ixFullVector = ixMapSubset2Full(iState) ! index within full state vector - ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) - ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain + scalarCanopyEnthTemp = enthVeg + enthLiq + enthIce + print*,scalarCanopyEnthTemp - ! check an energy state, since only need for energy state residuals - if(ixStateType(ixFullVector)==iname_nrgCanair .or. ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer)then +end subroutine T2enthTemp_veg - ! get the layer index - select case(ixDomainType) - case(iname_cas); iLayer = integerMissing - case(iname_veg); iLayer = integerMissing - case(iname_snow); iLayer = ixControlIndex - case(iname_soil); iLayer = ixControlIndex + nSnow - case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) - case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return - end select +! ************************************************************************************************************************ +! public subroutine T2enthTemp_snow: compute temperature component of enthalpy from temperature and total water content, snow layer +! ************************************************************************************************************************ +subroutine T2enthTemp_snow(& + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + mLayerTemp, & ! intent(in): layer temperature (K) + mLayerVolFracWat, & ! intent(in): volumetric total water content (-) + mLayerEnthTemp, & ! intent(out): temperature component of enthalpy of each snow layer (J m-3) + err,message) ! intent(out): error control + ! ------------------------------------------------------------------------------------------------------------------------- + implicit none + ! delare dummy variables + ! ------------------------------------------------------------------------------------------------------------------------- + real(rkind),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) + ! input: variables for the snow domain + real(rkind),intent(in) :: mLayerTemp ! layer temperature (K) + real(rkind),intent(in) :: mLayerVolFracWat ! volumetric total water content (-) + ! output: enthalpy + real(rkind),intent(out) :: mLayerEnthTemp ! temperature component of enthalpy of each snow layer (J m-3) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------------------------------- + ! declare local variables + real(rkind) :: diffT ! temperature difference of temp from Tfreeze + real(rkind) :: integral ! integral of snow freezing curve + ! enthalpy + real(rkind) :: enthLiq ! enthalpy of the liquid region (J m-3) + real(rkind) :: enthIce ! enthalpy of the ice region (J m-3) + real(rkind) :: enthAir ! enthalpy of air (J m-3) + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="T2enthTemp_snow/" - ! identify domain - select case(ixDomainType) - case(iname_cas) - scalarCanairEnthalpy = Cp_air * iden_air * (scalarCanairTempTrial - Tfreeze) + diffT = mLayerTemp - Tfreeze ! diffT<0._rkind because snow is frozen + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + enthLiq = iden_water * Cp_water * mLayerVolFracWat * integral + enthIce = iden_water * Cp_ice * mLayerVolFracWat * ( diffT - integral ) + enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWat * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) - case(iname_veg) - ! association to necessary variables for vegetation - vegVars: associate(& - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! canopy depth (m) - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! maximum mass of vegetation (kg m-2) - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! scaling parameter for the snow freezing curve (K-1) - ) - - diffT = scalarCanopyTempTrial - Tfreeze - enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth - - if(diffT>=0._rkind)then - enthLiq = Cp_water * scalarCanopyWatTrial * diffT / canopyDepth - enthIce = 0._rkind - else - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth - enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth - endif - - scalarCanopyEnthTemp = enthVeg + enthLiq + enthIce - - end associate vegVars + mLayerEnthTemp = enthLiq + enthIce + enthAir - case(iname_snow) +end subroutine T2enthTemp_snow - ! association to necessary variables for snow - snowVars: associate(& - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) - diffT = mLayerTempTrial(iLayer) - Tfreeze ! diffT<0._rkind because snow is frozen - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral - enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) - enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) - - mLayerEnthTemp(iLayer) = enthLiq + enthIce + enthAir +! ************************************************************************************************************************ +! public subroutine T2enthTemp_soil: compute temperature component of enthalpy from temperature and total water content, soil layer +! ************************************************************************************************************************ +subroutine T2enthTemp_soil(& + use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy + soil_dens_intr, & ! intent(in): intrinsic soil density (kg m-3) + vGn_alpha, & ! intent(in): van Genutchen "alpha" parameter + vGn_n, & ! intent(in): van Genutchen "n" parameter + theta_sat, & ! intent(in): soil porosity (-) + theta_res, & ! intent(in): soil residual volumetric water content (-) + vGn_m, & ! intent(in): van Genutchen "m" parameter (-) + ixControlIndex, & ! intent(in): index of the control volume within the domain + lookup_data, & ! intent(in): lookup table data structure + mLayerTemp, & ! intent(in): layer temperature (K) + mLayerMatricHead, & ! intent(in): total water matric potential (m) + mLayerEnthTemp, & ! intent(out): temperature component of enthalpy soil layer (J m-3) + err,message) ! intent(out): error control + ! ------------------------------------------------------------------------------------------------------------------------- + ! downwind routines + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water + USE spline_int_module,only:splint ! use for cubic spline interpolation + implicit none + ! delare dummy variables + ! ------------------------------------------------------------------------------------------------------------------------- + logical(lgt),intent(in) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function + ! input: data structures + real(rkind),intent(in) :: soil_dens_intr ! intrinsic soil density (kg m-3) + real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter + real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter + real(rkind),intent(in) :: theta_sat ! soil porosity (-) + real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) + real(rkind),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) + integer(i4b),intent(in) :: ixControlIndex ! index within a given model domain + type(zLookup),intent(in) :: lookup_data ! lookup tables + ! input: variables for the soil domain + real(rkind),intent(in) :: mLayerTemp ! layer temperature (K) + real(rkind),intent(in) :: mLayerMatricHead ! total water matric potential (m) + ! output: enthalpy + real(rkind),intent(out) :: mLayerEnthTemp ! temperature component of enthalpy of soil layer (J m-3) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------------------------------- + ! declare local variables + character(len=128) :: cmessage ! error message in downwind routine + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind) :: volFracWat ! volumetric fraction of total water, liquid+ice (-) + real(rkind) :: diff0 ! temperature difference of Tcrit from Tfreeze + real(rkind) :: diffT ! temperature difference of temp from Tfreeze + real(rkind) :: dL ! derivative of soil lookup table with temperature at layer temperature + real(rkind) :: arg ! argument of soil hypergeometric function + real(rkind) :: gauss_hg_T ! soil hypergeometric function result + real(rkind) :: integral_unf ! integral of unfrozen soil water content (from Tfreeze to Tcrit) + real(rkind) :: integral_frz_low ! lower limit of integral of frozen soil water content (from Tfreeze to Tcrit) + real(rkind) :: integral_frz_upp ! upper limit of integral of frozen soil water content (from Tfreeze to soil temperature) + real(rkind) :: xConst ! constant in the freezing curve function (m K-1) + real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) + ! enthalpy + real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) + real(rkind) :: enthLiq ! enthalpy of the liquid region (J m-3) + real(rkind) :: enthIce ! enthalpy of the ice region (J m-3) + real(rkind) :: enthAir ! enthalpy of air (J m-3) + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="T2enthTemp_soil/" - end associate snowVars + Tcrit = crit_soilT( mLayerMatricHead ) + volFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + diffT = mLayerTemp - Tfreeze + diff0 = Tcrit - Tfreeze - case(iname_soil) - ! make association to variables for soil - soilVars: associate(& - soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex), & ! intrinsic soil density (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex), & ! soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex), & ! volumetric residual water content (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex), & ! van Genuchten "alpha" parameter (m-1) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) & ! van Genuchten "n" parameter (-) - ) ! end associate statement - - ! diagnostic variables - vGn_m = 1._rkind - 1._rkind/vGn_n - Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) - volFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - diffT = mLayerTempTrial(iLayer) - Tfreeze - diff0 = Tcrit - Tfreeze - ! *** compute enthalpy of water for unfrozen conditions - if(mlayerTempTrial(iLayer)>=Tcrit)then - enthLiq= iden_water * Cp_water * volFracWat * diffT - enthIce= 0._rkind - - ! *** compute enthalpy of water for frozen conditions - else - ! *** compute integral of mLayerPsiLiq from Tfreeze to layer temperature - ! get the unfrozen water content - integral_unf = ( Tcrit - Tfreeze ) * volFracWat - - ! get the frozen water content - if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature - ! make associate to the the lookup table - lookVars: associate(& - Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup, & ! temperature (K) - Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup, & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) - L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function - ) ! end associate statement - - ! get the lower limit of the integral - if(diff0<0._rkind)then - call splint(Tk,Ly,L2,Tcrit,integral_frz_low,dL,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - else ! Tcrit=Tfreeze, i.e. mLayerMatricHeadTrial(ixControlIndex)>0 - integral_frz_low = 0._rkind - end if - ! get the upper limit of the integral - call splint(Tk,Ly,L2,mlayerTempTrial(iLayer),integral_frz_upp,dL,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - end associate lookVars - - else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature - ! get the lower limit of the integral - if(diff0<0._rkind)then - arg = (vGn_alpha * mLayerMatricHeadTrial(ixControlIndex))**vGn_n - gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - integral_frz_low = diff0 * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) - else ! Tcrit=Tfreeze, i.e. mLayerMatricHeadTrial(ixControlIndex)>0 - integral_frz_low = 0._rkind - end if - ! get the upper limit of the integral - xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) - mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution, DIFFERENT from the liquid water matric potential used in the flux calculations - arg = (vGn_alpha * mLayerPsiLiq)**vGn_n - gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - integral_frz_upp = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) - endif - - enthLiq = iden_water * Cp_water * (integral_unf + integral_frz_upp - integral_frz_low) - enthIce = iden_ice * Cp_ice * ( volFracWat * diffT - (integral_unf + integral_frz_upp - integral_frz_low) ) - - if(doTest)then - - ! write values - print*, 'doTest = ', doTest - print*, 'T,Tcrit = ', mlayerTempTrial(iLayer),Tcrit ! temperature (K) - print*, 'integral unf,frz_upp,frz_low = ', integral_unf, integral_frz_upp, integral_frz_low ! integral (K) - print*, 'theta_sat = ', theta_sat ! soil porosity (-) - print*, 'theta_res = ', theta_res ! volumetric residual water content (-) - print*, 'vGn_alpha = ', vGn_alpha ! van Genuchten "alpha" parameter (m-1) - print*, 'vGn_n = ', vGn_n ! van Genuchten "n" parameter (-) - print*, trim(message)//'PAUSE: Set doTest=.false. to complete simulations' - read(*,*) - - endif ! if testing - - endif ! (if frozen conditions) - - enthSoil = soil_dens_intr * Cp_soil * ( 1._rkind - theta_sat ) * diffT - enthAir = iden_air * Cp_air * ( 1._rkind - theta_sat - volFracWat ) * diffT - - mLayerEnthTemp(iLayer) = enthLiq + enthIce + enthSoil + enthAir - - end associate soilVars - - ! ----- - ! - checks... - ! ----------- - case(iname_aquifer); cycle ! aquifer: do nothing - case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return - end select + ! *** compute enthalpy of water for unfrozen conditions + if(mlayerTemp>=Tcrit)then + enthLiq= iden_water * Cp_water * volFracWat * diffT + enthIce= 0._rkind - end if ! if an energy layer - end do ! looping through state variables + ! *** compute enthalpy of water for frozen conditions + else + ! *** compute integral of mLayerPsiLiq from Tfreeze to layer temperature + ! get the unfrozen water content + integral_unf = ( Tcrit - Tfreeze ) * volFracWat + + ! get the frozen water content + if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature + ! make associate to the the lookup table + lookVars: associate(& + Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup, & ! temperature (K) + Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup, & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) + L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + ) ! end associate statement + + ! get the lower limit of the integral + if(diff0<0._rkind)then + call splint(Tk,Ly,L2,Tcrit,integral_frz_low,dL,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + else ! Tcrit=Tfreeze, i.e. mLayerMatricHeadTrial(ixControlIndex)>0 + integral_frz_low = 0._rkind + end if + ! get the upper limit of the integral + call splint(Tk,Ly,L2,mlayerTemp,integral_frz_upp,dL,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + end associate lookVars + + else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature + ! get the lower limit of the integral + if(diff0<0._rkind)then + arg = (vGn_alpha * mLayerMatricHead)**vGn_n + gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + integral_frz_low = diff0 * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + else ! Tcrit=Tfreeze, i.e. mLayerMatricHeadTrial(ixControlIndex)>0 + integral_frz_low = 0._rkind + end if + ! get the upper limit of the integral + xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) + mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution, DIFFERENT from the liquid water matric potential used in the flux calculations + arg = (vGn_alpha * mLayerPsiLiq)**vGn_n + gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + integral_frz_upp = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + endif + + enthLiq = iden_water * Cp_water * (integral_unf + integral_frz_upp - integral_frz_low) + enthIce = iden_ice * Cp_ice * ( volFracWat * diffT - (integral_unf + integral_frz_upp - integral_frz_low) ) + + endif ! (if frozen conditions) - end associate generalVars + enthSoil = soil_dens_intr * Cp_soil * ( 1._rkind - theta_sat ) * diffT + enthAir = iden_air * Cp_air * ( 1._rkind - theta_sat - volFracWat ) * diffT -end subroutine T2enthTemp + mLayerEnthTemp = enthLiq + enthIce + enthSoil + enthAir +end subroutine T2enthTemp_soil ! ************************************************************************************************************************ ! public subroutine enthTemp_or_enthalpy: add energy associated with thaw/freeze to temperature component of enthalpy to get total enthalpy, H, or vice versa diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index d0feebdff..5c08109cc 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -133,7 +133,6 @@ subroutine eval8summa(& USE getVectorz_module, only:varExtract ! extract variables from the state vector USE getVectorz_module, only:checkFeas ! check feasibility of state vector USE updateVars_module, only:updateVars ! update prognostic variables - USE enthalpyTemp_module, only:T2enthTemp ! compute enthalpy USE computFlux_module, only:soilCmpres ! compute soil compression USE computFlux_module, only:computFlux ! compute fluxes given a state vector USE computHeatCap_module,only:computHeatCapAnalytic ! recompute closed form heat capacity (Cp) and derivatives @@ -383,26 +382,34 @@ subroutine eval8summa(& ! update diagnostic variables and derivatives call updateVars(& ! input - .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ixNrgConserv.ne.closedForm, & ! intent(in): flag if computing temperature compoment of enthalpy + ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy + .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + lookup_data, & ! intent(in): lookup table data structure + scalarCanairTempTrial, & ! intent(in): trial value of canopy air space temperature (K) ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value for canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value for canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value for canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value for canopy ice content (kg m-2) + scalarCanopyTempTrial, & ! intent(inout): trial value for canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value for canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value for canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value for canopy ice content (kg m-2) ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + ! output: enthalpy state variables + scalarCanairEnthalpyTrial, & ! intent(inout): trial value for enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTempTrial, & ! intent(inout): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthTempTrial, & ! intent(inout): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) if(updateStateCp)then @@ -518,37 +525,6 @@ subroutine eval8summa(& dCm_dTkCanopy = 0._rkind endif ! needStateCm - if(ixNrgConserv== enthalpyFD .or. ixNrgConserv == enthalpyFDlu)then ! use residual as enthalpy_delta - (phase change)_delta - ! compute temperature component of enthalpy - call T2enthTemp(& - ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value for canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value for canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) - ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - ! output: enthalpy - scalarCanairEnthalpyTrial, & ! intent(inout): trial value for enthalpy of the canopy air space (J m-3) - scalarCanopyEnthTempTrial, & ! intent(inout): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthTempTrial, & ! intent(inout): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,cmessage) ! intent(out): control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - else ! use residual as (Cp*DeltaTemp + Cm*DeltaTheta)_delta - (phase change)_delta - scalarCanairEnthalpyTrial = realMissing - scalarCanopyEnthTempTrial = realMissing - mLayerEnthTempTrial = realMissing - endif !(choice of how conservation of energy is implemented) - ! save the number of flux calls per time step indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) = indx_data%var(iLookINDEX%numberFluxCalc)%dat(1) + 1 diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 4a7127e76..214913261 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -79,12 +79,6 @@ module systemSolv_module io_type_summaSolve4numrec, & ! class for summaSolve4numrec arguments out_type_summaSolve4numrec ! class for summaSolve4numrec arguments -! look-up values for the choice of variable in energy equations (BE residual or IDA state variable) -USE mDecisions_module,only: & - closedForm, & ! use temperature - enthalpyFDlu, & ! use enthalpy with lookup tables - enthalpyFD ! use enthalpy with analytical solution - ! look-up values for the choice of groundwater representation (local-column, or single-basin) USE mDecisions_module,only:& localColumn, & ! separate groundwater representation in each local soil column @@ -162,7 +156,6 @@ subroutine systemSolv(& ! state vector and solver USE getVectorz_module,only:getScaling ! get the scaling vectors USE enthalpyTemp_module,only:T2enthalpy_snwWat ! convert temperature to liq+ice enthalpy for a snow layer - USE enthalpyTemp_module,only:T2enthTemp ! compute enthalpy #ifdef SUNDIALS_ACTIVE USE tol4ida_module,only:popTol4ida ! populate tolerances USE eval8summaWithPrime_module,only:eval8summaWithPrime ! get the fluxes and residuals @@ -394,17 +387,10 @@ subroutine initial_function_evaluations ! initialize the trial state vectors stateVecTrial = stateVecInit - associate(& - ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision,& ! intent(in): [i4b] choice of numerical solver - ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision & ! intent(in): [i4b] choice of variable in energy conservation backward Euler residual - &) - if ((ixNrgConserv.ne.closedForm .or. computNrgBalance) .and. ixNumericalMethod.ne.ida) then - call enthalpy_function_evaluations; if (return_flag) return - end if - ! compute the initial flux and the residual vector, also gets values needed for the Jacobian matrix - if (ixNrgConserv.ne.closedForm .and. ixNumericalMethod==ida) then - call initial_flux_and_residual_vectors_prime; if (return_flag) return ! if we add enthalpy state variable option to non-ida, we do not need this anymore + associate(ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision) ! intent(in): [i4b] choice of numerical solver + if (ixNumericalMethod==ida) then + call initial_flux_and_residual_vectors_prime; if (return_flag) return else call initial_flux_and_residual_vectors; if (return_flag) return end if @@ -440,47 +426,6 @@ subroutine initial_function_evaluations end associate end subroutine initial_function_evaluations - subroutine enthalpy_function_evaluations - ! ** Compute H_T at the beginning of the data step without phase change ** - associate(& - ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision,& ! intent(in): [i4b] choice of variable in energy conservation backward Euler residual - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1),& ! intent(out): [dp] enthalpy of the canopy air space (J m-3) - scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1),& ! intent(out): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthTemp => diag_data%var(iLookDIAG%mLayerEnthTemp)%dat & ! intent(out): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) - &) - - ! will need enthalpy change, compute H_T at the beginning of the data step - call T2enthTemp(& - ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTemp, & ! intent(in): value of canopy air temperature (K) - scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) - scalarCanopyWat, & ! intent(in): value of canopy total water (kg m-2) - ! input: variables for the snow-soil domain - mLayerTemp, & ! intent(in): vector of layer temperature (K) - mLayerVolFracWat, & ! intent(in): vector of volumetric total water content (-) - mLayerMatricHead, & ! intent(in): vector of total water matric potential (m) - ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthTemp, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthTemp, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,cmessage) ! intent(out): error control - end associate - if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if - end subroutine enthalpy_function_evaluations - subroutine initial_flux_and_residual_vectors ! ** Compute initial flux and residual vectors ** ! Note: prime initial values are 0 so it's fine to run the regular eval8summa with every solver choice diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 index 6a28c0100..c149c003a 100644 --- a/build/source/engine/updateVars.f90 +++ b/build/source/engine/updateVars.f90 @@ -63,6 +63,7 @@ module updateVars_module var_i, & ! data vector (i4b) var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) + zLookup, & ! data vector with variable length dimension (rkind) var_dlength ! data vector with variable length dimension (rkind) ! provide access to indices that define elements of the data structures @@ -77,14 +78,18 @@ module updateVars_module USE updatState_module,only:updateSoil ! update soil states ! provide access to functions for the constitutive functions and derivatives -USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) -USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) -USE soil_utils_module,only:dTheta_dTk ! differentiate the freezing curve w.r.t. temperature (soil) -USE soil_utils_module,only:dTheta_dPsi ! derivative in the soil water characteristic (soil) -USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content -USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water -USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists -USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential +USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) +USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) +USE soil_utils_module,only:dTheta_dTk ! differentiate the freezing curve w.r.t. temperature (soil) +USE soil_utils_module,only:dTheta_dPsi ! derivative in the soil water characteristic (soil) +USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content +USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water +USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists +USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential +USE enthalpyTemp_module,only:T2enthTemp_cas ! convert temperature to enthalpy for canopy air space +USE enthalpyTemp_module,only:T2enthTemp_veg ! convert temperature to enthalpy for vegetation +USE enthalpyTemp_module,only:T2enthTemp_snow ! convert temperature to enthalpy for snow +USE enthalpyTemp_module,only:T2enthTemp_soil ! convert temperature to enthalpy for soil ! IEEE check USE, intrinsic :: ieee_arithmetic ! check values (NaN, etc.) @@ -100,12 +105,16 @@ module updateVars_module ! ********************************************************************************************************** subroutine updateVars(& ! input + computeEnthTemp, & ! intent(in): flag if computing temperature compoment of enthalpy + use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy do_adjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU indx_data, & ! intent(in): indices defining model states and layers prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(inout): model diagnostic variables for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + lookup_data, & ! intent(in): lookup table data structure + scalarCanairTempTrial, & ! intent(in): trial value of canopy air space temperature (K) ! output: variables for the vegetation canopy scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) @@ -118,18 +127,26 @@ subroutine updateVars(& mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + ! output: enthalpy variables + scalarCanairEnthalpyTrial, & ! intent(inout): trial value for enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTempTrial, & ! intent(inout): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthTempTrial, & ! intent(inout): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------- ! -------------------------------------------------------------------------------------------------------------------------------- implicit none ! input + logical(lgt) ,intent(in) :: computeEnthTemp ! flag if computing temperature compoment of enthalpy + logical(lgt) ,intent(in) :: use_lookup ! flag to use the lookup table for soil enthalpy logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + type(zLookup) ,intent(in) :: lookup_data ! lookup tables + real(rkind),intent(in) :: scalarCanairTempTrial ! trial value of canopy air space temperature (K) ! output: variables for the vegetation canopy real(rkind),intent(inout) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) real(rkind),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) @@ -142,6 +159,10 @@ subroutine updateVars(& real(rkind),intent(inout) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric ice water content (-) real(rkind),intent(inout) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) real(rkind),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) + ! output: enthalpy variables + real(rkind),intent(inout) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3) + real(rkind),intent(inout) :: scalarCanopyEnthTempTrial ! trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(inout) :: mLayerEnthTempTrial(:) ! trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message @@ -206,7 +227,10 @@ subroutine updateVars(& ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) ! snow parameters snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ! depth-varying model parameters + ! depth-varying model parameters (heat capacity, enthalpy) + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1) ,& ! intent(in): [dp ] specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1) ,& ! intent(in): [dp ] maximum mass of vegetation (kg m-2) + soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat ,& ! intent(in): [dp(:)] intrinsic soil density (kg m-3) vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) @@ -268,7 +292,7 @@ subroutine updateVars(& ! get the layer index select case(ixDomainType) - case(iname_cas); cycle ! canopy air space: do nothing + case(iname_cas); iLayer = 0 case(iname_veg); iLayer = 0 case(iname_snow); iLayer = ixControlIndex case(iname_soil); iLayer = ixControlIndex + nSnow @@ -278,13 +302,18 @@ subroutine updateVars(& ! get the index of the other (energy or mass) state variable within the full state vector select case(ixDomainType) + case(iname_cas) ; ixOther = integerMissing case(iname_veg) ; ixOther = merge(ixHydCanopy(1), ixNrgCanopy(1), ixStateType(ixFullVector)==iname_nrgCanopy) case(iname_snow, iname_soil); ixOther = merge(ixHydLayer(iLayer),ixNrgLayer(iLayer),ixStateType(ixFullVector)==iname_nrgLayer) case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return end select ! get the index in the local state vector - ixOtherLocal = ixMapFull2Subset(ixOther) ! ixOtherLocal could equal integerMissing + if(ixDomainType==iname_cas)then + ixOtherLocal = integerMissing + else + ixOtherLocal = ixMapFull2Subset(ixOther) ! ixOtherLocal could equal integerMissing + endif if(ixOtherLocal/=integerMissing) computedCoupling(ixOtherLocal)=.true. ! check if we have a coupled solution @@ -305,6 +334,22 @@ subroutine updateVars(& print*, 'isNrgState = ', isNrgState endif + ! calculate temperature component of enthalpy for canopy air space + if(ixDomainType==iname_cas)then + if(computeEnthTemp)then + call T2enthTemp_cas(& + ! input + scalarCanairTempTrial, & ! intent(in): canopy air temperature (K) + ! output + scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + else + scalarCanairEnthalpyTrial = realMissing + endif + cycle ! no more to do on canopy air space + endif + ! update hydrology state variables for the uncoupled solution if(.not.isNrgState .and. .not.isCoupled)then @@ -619,6 +664,58 @@ subroutine updateVars(& case(iname_snow, iname_soil); mLayerTempTrial(iLayer) = xTemp end select + ! calculate temperature component of enthalpy for remaining domains + if(ixDomainType==iname_veg)then + if(computeEnthTemp)then + call T2enthTemp_veg(& + ! input + canopyDepth, & ! intent(in): canopy depth (m) + specificHeatVeg, & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation, & ! intent(in): maximum mass of vegetation (kg m-2) + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + scalarCanopyTempTrial, & ! intent(in): canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): canopy water content (kg m-2) + ! output + scalarCanopyEnthTempTrial, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + else + scalarCanopyEnthTempTrial = realMissing + endif + elseif(ixDomainType==iname_snow)then + if(computeEnthTemp)then + call T2enthTemp_snow(& + ! input + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + mLayerTempTrial(iLayer), & ! intent(in): layer temperature (K) + mLayerVolFracWatTrial(iLayer), & ! intent(in): volumetric total water content (-) + ! output + mLayerEnthTempTrial(iLayer), & ! intent(out): temperature component of enthalpy of each snow layer (J m-3) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + else + mLayerEnthTempTrial(iLayer) = realMissing + endif + elseif(ixDomainType==iname_soil)then + if(computeEnthTemp)then + call T2enthTemp_soil(& + ! input + use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy + soil_dens_intr(ixControlIndex), & ! intent(in): intrinsic soil density (kg m-3) + vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in): soil parameters + ixControlIndex, & ! intent(in): index of the control volume within the domain + lookup_data, & ! intent(in): lookup table data structure + mLayerTempTrial(iLayer), & ! intent(in): layer temperature (K) + mLayerMatricHeadTrial(ixControlIndex), & ! intent(in): matric head (m) + ! output + mLayerEnthTempTrial(iLayer), & ! intent(out): temperature component of enthalpy soil layer (J m-3) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + else + mLayerEnthTempTrial(iLayer) = realMissing + endif + endif + ! ======================================================================================================================================= ! ======================================================================================================================================= diff --git a/build/source/engine/updateVarsWithPrime.f90 b/build/source/engine/updateVarsWithPrime.f90 index 6fb10555b..6a17eea0b 100644 --- a/build/source/engine/updateVarsWithPrime.f90 +++ b/build/source/engine/updateVarsWithPrime.f90 @@ -22,8 +22,6 @@ module updateVarsWithPrime_module ! data types USE nrtype -USE data_types,only:zLookup ! z(:)%var(:)%lookup(:) - ! missing values USE globalData,only:integerMissing ! missing integer @@ -65,6 +63,7 @@ module updateVarsWithPrime_module var_i, & ! data vector (i4b) var_d, & ! data vector (rkind) var_ilength, & ! data vector with variable length dimension (i4b) + zLookup, & ! data vector with variable length dimension (rkind) var_dlength ! data vector with variable length dimension (rkind) ! provide access to indices that define elements of the data structures @@ -263,8 +262,8 @@ subroutine updateVarsWithPrime(& theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) ! model diagnostic variables (heat capacity, enthalpy) - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1) ,& ! intent(in): [dp ] specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1) ,& ! intent(in): [dp ] maximum mass of vegetation (kg m-2) + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1) ,& ! intent(in): [dp ] specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1) ,& ! intent(in): [dp ] maximum mass of vegetation (kg m-2) canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(in): [dp ] volumetric heat capacity of the vegetation (J m-3 K-1) mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] volumetric heat capacity in each layer (J m-3 K-1) @@ -368,6 +367,22 @@ subroutine updateVarsWithPrime(& print*, 'isNrgState = ', isNrgState endif + ! compute temperature from enthalpy for canopy air space + if(ixDomainType==iname_cas)then + if(enthalpyStateVec)then + call enthalpy2T_cas(& + computJac, & ! intent(in): flag if computing for Jacobian update + scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) + scalarCanairTempTrial, & ! intent(out): trial value for canopy air temperature (K) + dCanairTemp_dEnthalpy, & ! intent(out): derivative of canopy air temperature with enthalpy + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + else + dCanairTemp_dEnthalpy = 0._rkind + endif + cycle ! no more to do on canopy air space + end if + ! update hydrology state variables for the uncoupled solution if(.not.isNrgState .and. .not.isCoupled)then @@ -413,23 +428,8 @@ subroutine updateVarsWithPrime(& endif ! if hydrology state variable or uncoupled solution - ! compute temperature from enthalpy and water content - ! NOTE: always do no matter coupling state if enthalpy is the state variable - if(ixDomainType==iname_cas)then - if(enthalpyStateVec)then - call enthalpy2T_cas(& - computJac, & ! intent(in): flag if computing for Jacobian update - scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) - scalarCanairTempTrial, & ! intent(out): trial value for canopy air temperature (K) - dCanairTemp_dEnthalpy, & ! intent(out): derivative of canopy air temperature with enthalpy - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - else - dCanairTemp_dEnthalpy = 1._rkind - endif - - cycle ! no more to do on canopy air space - elseif(ixDomainType==iname_veg)then + ! compute temperature from enthalpy and water content for remaining domains + if(ixDomainType==iname_veg)then if(enthalpyStateVec)then call enthalpy2T_veg(& computJac, & ! intent(in): flag if computing for Jacobian update @@ -444,10 +444,9 @@ subroutine updateVarsWithPrime(& dCanopyTemp_dCanWat, & ! intent(inout): derivative of canopy temperature with canopy water err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - else - dCanopyTemp_dEnthalpy = 1._rkind - dCanopyTemp_dCanWat = 1._rkind + dCanopyTemp_dEnthalpy = 0._rkind + dCanopyTemp_dCanWat = 0._rkind endif elseif(ixDomainType==iname_snow)then if(enthalpyStateVec)then @@ -461,10 +460,9 @@ subroutine updateVarsWithPrime(& dTemp_dTheta(iLayer), & ! intent(inout): derivative of layer temperature with volumetric total water content err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - else - dTemp_dEnthalpy(iLayer) = 1._rkind - dTemp_dTheta(iLayer) = 1._rkind + dTemp_dEnthalpy(iLayer) = 0._rkind + dTemp_dTheta(iLayer) = 0._rkind endif elseif(ixDomainType==iname_soil)then if(enthalpyStateVec)then @@ -487,11 +485,10 @@ subroutine updateVarsWithPrime(& dTemp_dPsi0(ixControlIndex), & ! intent(inout): derivative of layer temperature with total water matric potential err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - else - dTemp_dEnthalpy(iLayer) = 1._rkind - dTemp_dTheta(iLayer) = 1._rkind - dTemp_dPsi0(ixControlIndex) = 1._rkind + dTemp_dEnthalpy(iLayer) = 0._rkind + dTemp_dTheta(iLayer) = 0._rkind + dTemp_dPsi0(ixControlIndex) = 0._rkind endif endif diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index c01e0a47c..502c986b4 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -198,7 +198,7 @@ subroutine varSubstep(& real(rkind) :: sumBalance(in_varSubstep % nSubset) ! sum of substeps balance logical(lgt),parameter :: computMassBalance = .true. ! flag to compute the mass balance, will affect step length, default true logical(lgt),parameter :: computNrgBalance = .true. ! flag to compute the energy balance, will not effect solution but will not compute nrg balance if false (saves expense) - logical(lgt) :: computeEnthalpy ! flag to compute enthalpy regardless of the model decision + logical(lgt) :: computeEnthTemp ! flag to compute enthalpy regardless of the model decision logical(lgt) :: enthalpyStateVec ! flag if enthalpy is a state variable (ida) logical(lgt) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use analytical solution @@ -277,10 +277,10 @@ subroutine varSubstep(& failedMinimumStep=.false. ! set the flag to compute enthalpy, may want to have this true always if want to output enthalpy - computeEnthalpy = .false. + computeEnthTemp = .false. enthalpyStateVec = .false. use_lookup = .false. - if((ixNrgConserv .ne. closedForm .or. computNrgBalance) .and. ixNumericalMethod .ne. ida) computeEnthalpy = .true. ! use enthTemp to conserve energy or compute energy balance + if((ixNrgConserv .ne. closedForm .or. computNrgBalance) .and. ixNumericalMethod .ne. ida) computeEnthTemp = .true. ! use enthTemp to conserve energy or compute energy balance if(ixNrgConserv .ne. closedForm .and. ixNumericalMethod==ida) enthalpyStateVec = .true. ! enthalpy as state variable if(ixNrgConserv==enthalpyFDlu) use_lookup = .true. ! use lookup tables for soil enthalpy instead of analytical solution @@ -450,7 +450,7 @@ subroutine varSubstep(& ! update prognostic variables, update balances, and check them for possible step reduction if numrec or kinsol call updateProg(dtSubstep,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states - doAdjustTemp,computeVegFlux,computMassBalance,computNrgBalance,computeEnthalpy,enthalpyStateVec,use_lookup,& ! input: model control + doAdjustTemp,computeVegFlux,computMassBalance,computNrgBalance,computeEnthTemp,enthalpyStateVec,use_lookup,& ! input: model control model_decisions,lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! output: balances, flags, and error control if(err/=0)then @@ -627,7 +627,7 @@ end subroutine varSubstep ! private subroutine updateProg: update prognostic variables ! ********************************************************************************************************** subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states - doAdjustTemp,computeVegFlux,computMassBalance,computNrgBalance,computeEnthalpy,enthalpyStateVec,use_lookup,& ! input: model control + doAdjustTemp,computeVegFlux,computMassBalance,computNrgBalance,computeEnthTemp,enthalpyStateVec,use_lookup,& ! input: model control model_decisions,lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! input-output: balances, flags, and error control USE getVectorz_module,only:varExtract ! extract variables from the state vector @@ -635,8 +635,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec USE updateVarsWithPrime_module,only:updateVarsWithPrime ! update prognostic variables #endif USE updateVars_module,only:updateVars ! update prognostic variables - USE enthalpyTemp_module,only:T2enthTemp ! compute enthalpy - USE enthalpyTemp_module,only:enthTemp_or_enthalpy ! add phase change terms to delta temperature component of enthalpy + USE enthalpyTemp_module,only:enthTemp_or_enthalpy ! add phase change terms to delta temperature component of enthalpy implicit none ! model control real(rkind) ,intent(in) :: dt ! time step (s) @@ -650,7 +649,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec real(rkind) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) logical(lgt) ,intent(in) :: computMassBalance ! flag to check the mass balance logical(lgt) ,intent(in) :: computNrgBalance ! flag to check the energy balance - logical(lgt) ,intent(in) :: computeEnthalpy ! flag to compute enthalpy + logical(lgt) ,intent(in) :: computeEnthTemp ! flag to compute enthalpy logical(lgt) ,intent(in) :: enthalpyStateVec ! flag if enthalpy is a state variable (ida) logical(lgt) ,intent(in) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use analytical solution ! data structures @@ -980,63 +979,41 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec #endif case(kinsol, numrec) - ! update diagnostic variables + ! update diagnostic variables call updateVars(& ! input - doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + computeEnthTemp, & ! intent(in): flag if computing temperature compoment of enthalpy + use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy + doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + lookup_data, & ! intent(in): lookup table data structure + scalarCanairTempTrial, & ! intent(in): trial value of canopy air space temperature (K) ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + ! output: enthalpy state variables + scalarCanairEnthalpyTrial, & ! intent(inout): trial value for enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTempTrial, & ! intent(inout): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthTempTrial, & ! intent(inout): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control - err,cmessage) ! intent(out): error control - end select + err,cmessage) ! intent(out): error control + end select if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - ! ---- - ! * update enthalpy - !------------------------ - if(computeEnthalpy)then ! update diagnostic enthalpy variables if needed - ! compute enthalpy at t_{n+1} - call T2enthTemp(& - use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - ! output: enthalpy - scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthTempTrial, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthTempTrial, & ! intent(out): temperature component of enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - endif - if(computNrgBalance)then ! compute energy balance if didn't do inside solver substeps select case(ixNumericalMethod) @@ -1360,7 +1337,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! ----- ! * update enthalpy as a diagnostic variable... - ! if computeEnthalpy then enthTemp will change, if enthalpyStateVec then enthalpy will change + ! if computeEnthTemp then enthTemp will change, if enthalpyStateVec then enthalpy will change ! -------------------------------- scalarCanairEnthalpy = scalarCanairEnthalpyTrial ! equivalent to scalarCanairEnthTemp scalarCanopyEnthTemp = scalarCanopyEnthTempTrial From 594e6e233d6555d7066f6aadd2bdb8a25ccc6a8c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 9 Apr 2024 11:34:02 +0900 Subject: [PATCH 1248/1472] remove print statement --- build/source/engine/enthalpyTemp.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 141ccfa80..be8f83423 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -500,7 +500,6 @@ subroutine T2enthTemp_veg(& endif scalarCanopyEnthTemp = enthVeg + enthLiq + enthIce - print*,scalarCanopyEnthTemp end subroutine T2enthTemp_veg From 5c19aba6c7723206c8e74972930469c418468e0c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 9 Apr 2024 12:56:02 +0900 Subject: [PATCH 1249/1472] get initial enthalpy in check_icond if using mixdForm of Nrg equation --- build/source/engine/check_icond.f90 | 10 +++++----- build/source/engine/computResid.f90 | 8 ++++---- build/source/engine/coupled_em.f90 | 20 +++++++++++++------- build/source/engine/varSubstep.f90 | 3 +-- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/build/source/engine/check_icond.f90 b/build/source/engine/check_icond.f90 index 579121469..81a831288 100644 --- a/build/source/engine/check_icond.f90 +++ b/build/source/engine/check_icond.f90 @@ -39,7 +39,7 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU mparData, & ! model parameters indxData, & ! layer index data lookupData, & ! lookup table data - enthalpyStateVec, & ! flag if enthalpy is the state variable + checkEnthalpy, & ! flag if to check enthalpy for consistency use_lookup, & ! flag to use the lookup table for soil enthalpy err,message) ! error control ! -------------------------------------------------------------------------------------------------------- @@ -80,7 +80,7 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU type(gru_hru_doubleVec),intent(in) :: mparData ! parameters type(gru_hru_intVec),intent(in) :: indxData ! layer indexes type(gru_hru_z_vLookup),intent(in) :: lookupData ! lookup table data - logical(lgt),intent(in) :: enthalpyStateVec ! flag if enthalpy is the state variable + logical(lgt),intent(in) :: checkEnthalpy ! if true either need enthTemp as starting residual value, or for state variable initialization logical(lgt),intent(in) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! returned error message @@ -182,7 +182,7 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU end if scalarTheta = scalarCanopyIce + scalarCanopyLiq - if(enthalpyStateVec)then ! enthalpy as state variable (cold start often only has temperature) + if(checkEnthalpy)then ! enthalpy as state variable (cold start often only has temperature) call T2enthTemp_cas(& ! input scalarCanairTemp, & ! intent(in): canopy air temperature (K) @@ -287,7 +287,7 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - if(enthalpyStateVec)then ! enthalpy as state variable (cold start often only has temperature) + if(checkEnthalpy)then ! enthalpy as state variable (cold start often only has temperature) call T2enthTemp_snow(& ! input snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) @@ -316,7 +316,7 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(cmessage)//trim(cmessage); return; end if ! (check for errors) - if(enthalpyStateVec)then ! enthalpy as state variable (cold start often only has temperature) + if(checkEnthalpy)then ! enthalpy as state variable (cold start often only has temperature) call T2enthTemp_soil(& ! input use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy diff --git a/build/source/engine/computResid.f90 b/build/source/engine/computResid.f90 index ecb818c1a..60063bb59 100644 --- a/build/source/engine/computResid.f90 +++ b/build/source/engine/computResid.f90 @@ -80,7 +80,7 @@ subroutine computResid(& nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers - enthalpyStateVec, & ! intent(in): flag to use enthalpy formulation + mixdFormNrg, & ! intent(in): flag to use enthalpy formulation ! input: flux vectors sMul, & ! intent(in): state vector multiplier (used in the residual calculations) fVec, & ! intent(in): flux vector @@ -118,7 +118,7 @@ subroutine computResid(& integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain - logical(lgt),intent(in) :: enthalpyStateVec ! flag to use enthalpy formulation + logical(lgt),intent(in) :: mixdFormNrg ! flag to use enthalpy formulation ! input: flux vectors real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(rkind),intent(in) :: fVec(:) ! flux vector @@ -247,7 +247,7 @@ subroutine computResid(& ! compute the residual vector for the vegetation canopy ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision ! --> energy balance - if(enthalpyStateVec)then + if(mixdFormNrg)then if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = ( scalarCanairEnthalpyTrial - scalarCanairEnthalpy ) - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = ( scalarCanopyEnthTempTrial - scalarCanopyEnthTemp ) - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) else @@ -265,7 +265,7 @@ subroutine computResid(& ! compute the residual vector for the snow and soil sub-domains for energy if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - if(enthalpyStateVec)then + if(mixdFormNrg)then rVec( ixSnowSoilNrg(iLayer) ) = ( mLayerEnthTempTrial(iLayer) - mLayerEnthTemp(iLayer) ) - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) else rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) )*( mLayerTempTrial(iLayer) - mLayerTemp(iLayer) ) + mLayerCmTrial(iLayer)*( mLayerVolFracWatTrial(iLayer) - mLayerVolFracWat(iLayer) ) & diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index bd1abd0a2..2e0ef4705 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -288,7 +288,8 @@ subroutine coupled_em(& real(rkind) :: dt_solvInner ! seconds in the maxstep subStep that have been completed logical(lgt),parameter :: computNrgBalance_var=.true. ! flag to compute enthalpy, must have computNrgBalance true in varSubStep (will compute enthalpy for BE even if not using enthalpy formulation) logical(lgt) :: computeEnthalpy ! flag to compute enthalpy regardless of the model decision - logical(lgt) :: enthalpyStateVec ! flag if enthalpy is a state variable (ida) + logical(lgt) :: mixdFormNrg ! flag to use temperature compenent of enthalpy in residual to conserve energy (BE only) + logical(lgt) :: enthalpyStateVec ! flag if enthalpy is a state variable (IDA) logical(lgt) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use analytical solution ! ---------------------------------------------------------------------------------------------------------------------------------------------- @@ -421,9 +422,14 @@ subroutine coupled_em(& ! set the flag to compute enthalpy, may want to have this true always if want to output enthalpy computeEnthalpy = .false. enthalpyStateVec = .false. + mixdFormNrg = .false. use_lookup = .false. - if((ixNrgConserv .ne. closedForm .or. computNrgBalance_var) .and. ixNumericalMethod .ne. ida) computeEnthalpy = .true. ! use enthTemp to conserve energy or compute energy balance - if(ixNrgConserv .ne. closedForm .and. ixNumericalMethod==ida) enthalpyStateVec = .true. ! enthalpy as state variable + if(ixNumericalMethod.ne.ida)then + if(ixNrgConserv.ne.closedForm .or. computNrgBalance_var) computeEnthalpy = .true. ! compute energy balance + if(ixNrgConserv.ne.closedForm) mixdFormNrg = .true. ! use enthTemp to conserve energy + else ! enthalpy state variable only implemented for IDA, energy conserved in IDA without using enthTemp + if(ixNrgConserv.ne.closedForm) enthalpyStateVec = .true. ! enthalpy as state variable + endif if(ixNrgConserv==enthalpyFDlu) use_lookup = .true. ! use lookup tables for soil enthalpy instead of analytical solution ! save the liquid water and ice on the vegetation canopy @@ -631,7 +637,7 @@ subroutine coupled_em(& end if ! if computing fluxes over vegetation ! change enthalpy based on new canopy temperature - if(enthalpyStateVec)then + if(enthalpyStateVec .or. mixdFormNrg)then ! associate local variables with variables in the data structures enthalpyVeg: associate(& ! state variables in the vegetation canopy @@ -790,7 +796,7 @@ subroutine coupled_em(& end if ! get enthalpy from temperature if new layering - if( enthalpyStateVec .and. modifiedLayers )then + if( (enthalpyStateVec .or. mixdFormNrg) .and. modifiedLayers )then ! associate local variables with variables in the data structures enthalpySnow: associate(& ! variables in the snow and soil domains @@ -945,7 +951,7 @@ subroutine coupled_em(& mLayerVolFracWat( 1:nSnow ) = mLayerVolFracLiq( 1:nSnow ) + mLayerVolFracIce( 1:nSnow )*(iden_ice/iden_water) mLayerVolFracWat(nSnow+1:nLayers) = mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) - if(enthalpyStateVec .and. nSnow==0 .and. prog_data%var(iLookPROG%scalarSWE)%dat(1)>0._rkind)then ! compute enthalpy of the top soil layer if changed with surface melt pond + if( (enthalpyStateVec .or. mixdFormNrg) .and. nSnow==0 .and. prog_data%var(iLookPROG%scalarSWE)%dat(1)>0._rkind )then ! compute enthalpy of the top soil layer if changed with surface melt pond call T2enthTemp_soil(& ! input use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy @@ -1346,7 +1352,7 @@ subroutine coupled_em(& * prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) prog_data%var(iLookPROG%mLayerVolFracWat)%dat(1) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1) & + prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1)*iden_ice/iden_water - if(enthalpyStateVec)then ! compute enthalpy of the top snow layer + if(enthalpyStateVec .or. mixdFormNrg)then ! compute enthalpy of the top snow layer call T2enthTemp_snow(& snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) prog_data%var(iLookPROG%mLayerTemp)%dat(1), & ! temperature of the top layer (K) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 502c986b4..bb654b27b 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -978,11 +978,10 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec err,cmessage) ! intent(out): error control #endif case(kinsol, numrec) - ! update diagnostic variables call updateVars(& ! input - computeEnthTemp, & ! intent(in): flag if computing temperature compoment of enthalpy + computeEnthTemp, & ! intent(in): flag if computing temperature component of enthalpy use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze mpar_data, & ! intent(in): model parameters for a local HRU From 28ec2aaeee950f9359346e612cb25ecfa18537b9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 9 Apr 2024 14:51:38 +0900 Subject: [PATCH 1250/1472] need to recalculate enthalpy every time water changes; this makes the wallClock longer-- might want to just do at beginning and end of data window --- build/source/engine/computResid.f90 | 8 +-- build/source/engine/coupled_em.f90 | 83 +++++++++++++++++------------ build/source/engine/snwCompact.f90 | 62 ++++++++++----------- 3 files changed, 81 insertions(+), 72 deletions(-) diff --git a/build/source/engine/computResid.f90 b/build/source/engine/computResid.f90 index 60063bb59..3e756d675 100644 --- a/build/source/engine/computResid.f90 +++ b/build/source/engine/computResid.f90 @@ -80,7 +80,7 @@ subroutine computResid(& nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers - mixdFormNrg, & ! intent(in): flag to use enthalpy formulation + mixdformNrg, & ! intent(in): flag to use enthalpy formulation ! input: flux vectors sMul, & ! intent(in): state vector multiplier (used in the residual calculations) fVec, & ! intent(in): flux vector @@ -118,7 +118,7 @@ subroutine computResid(& integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain - logical(lgt),intent(in) :: mixdFormNrg ! flag to use enthalpy formulation + logical(lgt),intent(in) :: mixdformNrg ! flag to use enthalpy formulation ! input: flux vectors real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(rkind),intent(in) :: fVec(:) ! flux vector @@ -247,7 +247,7 @@ subroutine computResid(& ! compute the residual vector for the vegetation canopy ! NOTE: sMul(ixVegHyd) = 1, but include as it converts all variables to quadruple precision ! --> energy balance - if(mixdFormNrg)then + if(mixdformNrg)then if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = ( scalarCanairEnthalpyTrial - scalarCanairEnthalpy ) - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = ( scalarCanopyEnthTempTrial - scalarCanopyEnthTemp ) - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) else @@ -265,7 +265,7 @@ subroutine computResid(& ! compute the residual vector for the snow and soil sub-domains for energy if(nSnowSoilNrg>0)then do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - if(mixdFormNrg)then + if(mixdformNrg)then rVec( ixSnowSoilNrg(iLayer) ) = ( mLayerEnthTempTrial(iLayer) - mLayerEnthTemp(iLayer) ) - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) else rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) )*( mLayerTempTrial(iLayer) - mLayerTemp(iLayer) ) + mLayerCmTrial(iLayer)*( mLayerVolFracWatTrial(iLayer) - mLayerVolFracWat(iLayer) ) & diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 2e0ef4705..609bbd022 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -288,7 +288,6 @@ subroutine coupled_em(& real(rkind) :: dt_solvInner ! seconds in the maxstep subStep that have been completed logical(lgt),parameter :: computNrgBalance_var=.true. ! flag to compute enthalpy, must have computNrgBalance true in varSubStep (will compute enthalpy for BE even if not using enthalpy formulation) logical(lgt) :: computeEnthalpy ! flag to compute enthalpy regardless of the model decision - logical(lgt) :: mixdFormNrg ! flag to use temperature compenent of enthalpy in residual to conserve energy (BE only) logical(lgt) :: enthalpyStateVec ! flag if enthalpy is a state variable (IDA) logical(lgt) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use analytical solution @@ -422,11 +421,9 @@ subroutine coupled_em(& ! set the flag to compute enthalpy, may want to have this true always if want to output enthalpy computeEnthalpy = .false. enthalpyStateVec = .false. - mixdFormNrg = .false. use_lookup = .false. if(ixNumericalMethod.ne.ida)then - if(ixNrgConserv.ne.closedForm .or. computNrgBalance_var) computeEnthalpy = .true. ! compute energy balance - if(ixNrgConserv.ne.closedForm) mixdFormNrg = .true. ! use enthTemp to conserve energy + if(ixNrgConserv.ne.closedForm .or. computNrgBalance_var) computeEnthalpy = .true. ! compute energy balance or need enthalpy in residual else ! enthalpy state variable only implemented for IDA, energy conserved in IDA without using enthTemp if(ixNrgConserv.ne.closedForm) enthalpyStateVec = .true. ! enthalpy as state variable endif @@ -634,21 +631,18 @@ subroutine coupled_em(& ! output: error control err,cmessage) ! intent(out): error control if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - end if ! if computing fluxes over vegetation - ! change enthalpy based on new canopy temperature - if(enthalpyStateVec .or. mixdFormNrg)then - ! associate local variables with variables in the data structures - enthalpyVeg: associate(& - ! state variables in the vegetation canopy - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! canopy temperature (K) - scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! canopy temperature component of enthalpy (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! enthalpy of the vegetation canopy (J m-3) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) & ! mass of ice on the vegetation canopy (kg m-2) - ) ! (associate local variables with model parameters) - - if(computeVegFlux)then ! new temperature + ! change enthalpy based on new canopy temperature and water content, only if will need enthalpy for energy balance + if(enthalpyStateVec .or. computeEnthalpy)then + ! associate local variables with variables in the data structures + enthalpyVeg: associate(& + ! state variables in the vegetation canopy + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! canopy temperature (K) + scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! canopy temperature component of enthalpy (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! enthalpy of the vegetation canopy (J m-3) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) & ! mass of ice on the vegetation canopy (kg m-2) + ) ! (associate local variables with model parameters) call T2enthTemp_veg(& ! input canopyDepth, & ! intent(in): canopy depth (m) @@ -661,11 +655,10 @@ subroutine coupled_em(& scalarCanopyEnthTemp, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) err,cmessage) ! intent(out): error control if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - end if - - scalarCanopyEnthalpy = scalarCanopyEnthTemp - LH_fus * scalarCanopyIce/ canopyDepth ! new ice and/or temperature - end associate enthalpyVeg - end if ! (need to recalculate enthalpy state variable) + scalarCanopyEnthalpy = scalarCanopyEnthTemp - LH_fus * scalarCanopyIce/ canopyDepth ! new ice and/or temperature + end associate enthalpyVeg + end if ! (need to recalculate enthalpy state variable) + end if ! if computing fluxes over vegetation ! initialize drainage and throughfall ! NOTE 1: this needs to be done before solving the energy and liquid water equations, to account for the heat advected with precipitation @@ -796,7 +789,7 @@ subroutine coupled_em(& end if ! get enthalpy from temperature if new layering - if( (enthalpyStateVec .or. mixdFormNrg) .and. modifiedLayers )then + if( (enthalpyStateVec .or. computeEnthalpy) .and. modifiedLayers )then ! associate local variables with variables in the data structures enthalpySnow: associate(& ! variables in the snow and soil domains @@ -951,7 +944,8 @@ subroutine coupled_em(& mLayerVolFracWat( 1:nSnow ) = mLayerVolFracLiq( 1:nSnow ) + mLayerVolFracIce( 1:nSnow )*(iden_ice/iden_water) mLayerVolFracWat(nSnow+1:nLayers) = mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) - if( (enthalpyStateVec .or. mixdFormNrg) .and. nSnow==0 .and. prog_data%var(iLookPROG%scalarSWE)%dat(1)>0._rkind )then ! compute enthalpy of the top soil layer if changed with surface melt pond + ! compute enthalpy of the top soil layer if changed with surface melt pond + if( (enthalpyStateVec .or. computeEnthalpy) .and. nSnow==0 .and. prog_data%var(iLookPROG%scalarSWE)%dat(1)>0._rkind )then call T2enthTemp_soil(& ! input use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy @@ -1139,10 +1133,21 @@ subroutine coupled_em(& ! update water scalarCanopyWat = scalarCanopyLiq + scalarCanopyIce - - if(enthalpyStateVec)& ! recompute enthalpy of the canopy if changed ice content + if(enthalpyStateVec .or. computeEnthalpy)then ! recompute enthalpy of the canopy if changed water and ice content + call T2enthTemp_veg(& + ! input + canopyDepth, & ! intent(in): canopy depth (m) + specificHeatVeg, & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation, & ! intent(in): maximum mass of vegetation (kg m-2) + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1), & ! intent(in): canopy temperature (K) + scalarCanopyWat, & ! intent(in): canopy water content (kg m-2) + ! output + diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1), & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) = diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) - LH_fus * scalarCanopyIce/ canopyDepth - + endif end if ! (if computing the vegetation flux) ! * compute change in ice content of the top snow layer due to sublimation @@ -1199,16 +1204,24 @@ subroutine coupled_em(& err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - ! recompute snow depth, SWE, and top layer water + ! recompute snow depth, SWE, and layer water if(nSnow > 0)then prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) = sum( mLayerDepth(1:nSnow) ) prog_data%var(iLookPROG%scalarSWE)%dat(1) = sum( (mLayerVolFracLiq(1:nSnow)*iden_water & + mLayerVolFracIce(1:nSnow)*iden_ice) * mLayerDepth(1:nSnow) ) - mLayerVolFracWat(1) = mLayerVolFracLiq(1) + mLayerVolFracIce(1)*iden_ice/iden_water - - if(enthalpyStateVec)& ! recompute enthalpy of the top snow layer if changed ice content - diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(1) = diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(1) - iden_ice * LH_fus * mLayerVolFracIce(1) - + mLayerVolFracWat(1:nSnow) = mLayerVolFracLiq(1:nSnow) + mLayerVolFracIce(1:nSnow)*iden_ice/iden_water + if(enthalpyStateVec .or. computeEnthalpy)then ! recompute enthalpy of layers if changed water and ice content + do iLayer=1,nSnow + call T2enthTemp_snow(& + snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) + prog_data%var(iLookPROG%mLayerTemp)%dat(iLayer), & ! intent(in): layer temperature (K) + mLayerVolFracWat(iLayer), & ! intent(in): volumetric total water content (-) + diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(iLayer), & ! intent(out): temperature component of enthalpy of each snow layer (J m-3) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(iLayer) = diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(iLayer) - iden_ice * LH_fus * mLayerVolFracIce(iLayer) + end do ! looping through snow layers + endif endif end associate sublime @@ -1352,7 +1365,7 @@ subroutine coupled_em(& * prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) prog_data%var(iLookPROG%mLayerVolFracWat)%dat(1) = prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1) & + prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1)*iden_ice/iden_water - if(enthalpyStateVec .or. mixdFormNrg)then ! compute enthalpy of the top snow layer + if(enthalpyStateVec .or. computeEnthalpy)then ! compute enthalpy of the top snow layer call T2enthTemp_snow(& snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) prog_data%var(iLookPROG%mLayerTemp)%dat(1), & ! temperature of the top layer (K) diff --git a/build/source/engine/snwCompact.f90 b/build/source/engine/snwCompact.f90 index ebce64574..7804eabc4 100644 --- a/build/source/engine/snwCompact.f90 +++ b/build/source/engine/snwCompact.f90 @@ -39,13 +39,11 @@ module snwDensify_module ! public subroutine snwDensify: compute change in snow density over the time step ! ************************************************************************************************ subroutine snwDensify(& - ! intent(in): variables dt, & ! intent(in): time step (s) nSnow, & ! intent(in): number of snow layers mLayerTemp, & ! intent(in): temperature of each layer (K) mLayerMeltFreeze, & ! intent(in): volumnetric melt in each layer (kg m-3) - ! intent(in): parameters densScalGrowth, & ! intent(in): density scaling factor for grain growth (kg-1 m3) tempScalGrowth, & ! intent(in): temperature scaling factor for grain growth (K-1) @@ -53,55 +51,53 @@ subroutine snwDensify(& densScalOvrbdn, & ! intent(in): density scaling factor for overburden pressure (kg-1 m3) tempScalOvrbdn, & ! intent(in): temperature scaling factor for overburden pressure (K-1) baseViscosity, & ! intent(in): viscosity coefficient at T=T_frz and snow density=0 (kg m-2 s) - ! intent(inout): state variables mLayerDepth, & ! intent(inout): depth of each layer (m) mLayerVolFracLiqNew, & ! intent(inout): volumetric fraction of liquid water after itertations (-) mLayerVolFracIceNew, & ! intent(inout): volumetric fraction of ice after itertations (-) - ! output: error control err,message) ! intent(out): error control ! ----------------------------------------------------------------------------------------------------------------------------------------- ! compute change in snow density over the time step implicit none ! intent(in): variables - real(rkind),intent(in) :: dt ! time step (seconds) + real(rkind),intent(in) :: dt ! time step (seconds) integer(i4b),intent(in) :: nSnow ! number of snow layers - real(rkind),intent(in) :: mLayerTemp(:) ! temperature of each snow layer after iterations (K) - real(rkind),intent(in) :: mLayerMeltFreeze(:) ! volumetric melt in each layer (kg m-3) + real(rkind),intent(in) :: mLayerTemp(:) ! temperature of each snow layer after iterations (K) + real(rkind),intent(in) :: mLayerMeltFreeze(:) ! volumetric melt in each layer (kg m-3) ! intent(in): parameters - real(rkind),intent(in) :: densScalGrowth ! density scaling factor for grain growth (kg-1 m3) - real(rkind),intent(in) :: tempScalGrowth ! temperature scaling factor for grain growth (K-1) - real(rkind),intent(in) :: grainGrowthRate ! rate of grain growth (s-1) - real(rkind),intent(in) :: densScalOvrbdn ! density scaling factor for overburden pressure (kg-1 m3) - real(rkind),intent(in) :: tempScalOvrbdn ! temperature scaling factor for overburden pressure (K-1) - real(rkind),intent(in) :: baseViscosity ! viscosity coefficient at T=T_frz and snow density=0 (kg m-2 s) + real(rkind),intent(in) :: densScalGrowth ! density scaling factor for grain growth (kg-1 m3) + real(rkind),intent(in) :: tempScalGrowth ! temperature scaling factor for grain growth (K-1) + real(rkind),intent(in) :: grainGrowthRate ! rate of grain growth (s-1) + real(rkind),intent(in) :: densScalOvrbdn ! density scaling factor for overburden pressure (kg-1 m3) + real(rkind),intent(in) :: tempScalOvrbdn ! temperature scaling factor for overburden pressure (K-1) + real(rkind),intent(in) :: baseViscosity ! viscosity coefficient at T=T_frz and snow density=0 (kg m-2 s) ! intent(inout): state variables - real(rkind),intent(inout) :: mLayerDepth(:) ! depth of each layer (m) - real(rkind),intent(inout) :: mLayerVolFracLiqNew(:) ! volumetric fraction of liquid water in each snow layer after iterations (-) - real(rkind),intent(inout) :: mLayerVolFracIceNew(:) ! volumetric fraction of ice in each snow layer after iterations (-) + real(rkind),intent(inout) :: mLayerDepth(:) ! depth of each layer (m) + real(rkind),intent(inout) :: mLayerVolFracLiqNew(:) ! volumetric fraction of liquid water in each snow layer after iterations (-) + real(rkind),intent(inout) :: mLayerVolFracIceNew(:) ! volumetric fraction of ice in each snow layer after iterations (-) ! intent(out): error control integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! ----------------------------------------------------------------------------------------------------------------------------------------- ! define local variables - integer(i4b) :: iSnow ! index of snow layers - real(rkind) :: chi1,chi2,chi3,chi4,chi5 ! multipliers in the densification algorithm (-) - real(rkind) :: halfWeight ! half of the weight of the current snow layer (kg m-2) - real(rkind) :: weightSnow ! total weight of snow above the current snow layer (kg m-2) - real(rkind) :: CR_grainGrowth ! compaction rate for grain growth (s-1) - real(rkind) :: CR_ovrvdnPress ! compaction rate associated with over-burden pressure (s-1) - real(rkind) :: CR_metamorph ! compaction rate for metamorphism (s-1) - real(rkind) :: massIceOld ! mass of ice in the snow layer (kg m-2) - real(rkind) :: massLiqOld ! mass of liquid water in the snow layer (kg m-2) - real(rkind) :: scalarDepthNew ! updated layer depth (m) - real(rkind) :: scalarDepthMin ! minimum layer depth (m) - real(rkind) :: volFracIceLoss ! volumetric fraction of ice lost due to melt and sublimation (-) - real(rkind), dimension(nSnow) :: mLayerVolFracAirNew ! volumetric fraction of air in each layer after compaction (-) - real(rkind),parameter :: snwden_min=100._rkind ! minimum snow density for reducing metamorphism rate (kg m-3) - real(rkind),parameter :: snwDensityMax=550._rkind ! maximum snow density for collapse under melt (kg m-3) - real(rkind),parameter :: wetSnowThresh=0.01_rkind ! threshold to discriminate between "wet" and "dry" snow - real(rkind),parameter :: minLayerDensity=40._rkind ! minimum snow density allowed for any layer (kg m-3) + integer(i4b) :: iSnow ! index of snow layers + real(rkind) :: chi1,chi2,chi3,chi4,chi5 ! multipliers in the densification algorithm (-) + real(rkind) :: halfWeight ! half of the weight of the current snow layer (kg m-2) + real(rkind) :: weightSnow ! total weight of snow above the current snow layer (kg m-2) + real(rkind) :: CR_grainGrowth ! compaction rate for grain growth (s-1) + real(rkind) :: CR_ovrvdnPress ! compaction rate associated with over-burden pressure (s-1) + real(rkind) :: CR_metamorph ! compaction rate for metamorphism (s-1) + real(rkind) :: massIceOld ! mass of ice in the snow layer (kg m-2) + real(rkind) :: massLiqOld ! mass of liquid water in the snow layer (kg m-2) + real(rkind) :: scalarDepthNew ! updated layer depth (m) + real(rkind) :: scalarDepthMin ! minimum layer depth (m) + real(rkind) :: volFracIceLoss ! volumetric fraction of ice lost due to melt and sublimation (-) + real(rkind), dimension(nSnow) :: mLayerVolFracAirNew ! volumetric fraction of air in each layer after compaction (-) + real(rkind),parameter :: snwden_min=100._rkind ! minimum snow density for reducing metamorphism rate (kg m-3) + real(rkind),parameter :: snwDensityMax=550._rkind ! maximum snow density for collapse under melt (kg m-3) + real(rkind),parameter :: wetSnowThresh=0.01_rkind ! threshold to discriminate between "wet" and "dry" snow + real(rkind),parameter :: minLayerDensity=40._rkind ! minimum snow density allowed for any layer (kg m-3) ! ----------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="snwDensify/" From fbe59fd200544de7601b71299617732539cff137 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 9 Apr 2024 19:05:19 +0900 Subject: [PATCH 1251/1472] spaces --- build/source/engine/systemSolv.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 214913261..5504408b2 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -390,9 +390,9 @@ subroutine initial_function_evaluations ! compute the initial flux and the residual vector, also gets values needed for the Jacobian matrix associate(ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision) ! intent(in): [i4b] choice of numerical solver if (ixNumericalMethod==ida) then - call initial_flux_and_residual_vectors_prime; if (return_flag) return + call initial_flux_and_residual_vectors_prime; if (return_flag) return else - call initial_flux_and_residual_vectors; if (return_flag) return + call initial_flux_and_residual_vectors; if (return_flag) return end if end associate From aa9405463ab927d773a0f9e3d4c810a85b8df114 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 9 Apr 2024 20:08:56 +0900 Subject: [PATCH 1252/1472] start with last solution for temperature from enthalpy --- build/source/engine/enthalpyTemp.f90 | 18 +++++++++--------- build/source/engine/eval8summaWithPrime.f90 | 18 +++++++++--------- build/source/engine/summaSolve4ida.f90 | 2 ++ build/source/engine/systemSolv.f90 | 16 ++++++++-------- build/source/engine/updateVarsWithPrime.f90 | 6 +++--- build/source/engine/varSubstep.f90 | 3 +++ 6 files changed, 34 insertions(+), 29 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index be8f83423..2a429162f 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -835,7 +835,7 @@ subroutine enthalpy2T_veg(& snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) scalarCanopyEnthalpy, & ! intent(in): enthalpy of the vegetation canopy (J m-3) scalarCanopyWat, & ! intent(in): canopy total water (kg m-2) - scalarCanopyTemp, & ! intent(out): canopy temperature (K) + scalarCanopyTemp, & ! intent(inout): canopy temperature (K) dCanopyTemp_dEnthalpy, & ! intent(inout): derivative of canopy temperature with enthalpy dCanopyTemp_dCanWat, & ! intent(inout): derivative of canopy temperature with canopy water err,message) ! intent(out): error control @@ -857,7 +857,7 @@ subroutine enthalpy2T_veg(& ! input: water state variables real(rkind),intent(in) :: scalarCanopyWat ! trial value for canopy total water (kg m-2) ! output: temperature diagnostic variables - real(rkind),intent(out) :: scalarCanopyTemp ! trial value for canopy temperature (K) + real(rkind),intent(inout) :: scalarCanopyTemp ! trial value for canopy temperature (K) ! output: derivatives real(rkind),intent(inout) :: dCanopyTemp_dEnthalpy ! derivative of canopy temperature with enthalpy real(rkind),intent(inout) :: dCanopyTemp_dCanWat ! derivative of canopy temperature with canopy water @@ -909,7 +909,7 @@ subroutine enthalpy2T_veg(& ! ***** iterate to find temperature if ice exists if( T indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1),& ! intent(out): [dp] temperature of the vegetation canopy (K) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(out): [dp(:)] temperature of each snow/soil layer (K) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat & ! intent(out): [dp(:)] matric head (m) + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1),& ! intent(inout): [dp] temperature of the vegetation canopy (K) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout): [dp(:)] temperature of each snow/soil layer (K) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat & ! intent(out): [dp(:)] matric head (m) &) stateVecPrime(:) = 0._rkind ! prime initial values are 0 firstSplitOper0 = firstSplitOper ! set the flag for the first split operation, do not want to reset it here @@ -524,9 +524,9 @@ subroutine initial_flux_and_residual_vectors_prime diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_init, & ! intent(inout): model fluxes for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output: new values of variables needed in data window outside of internal IDA, for root finding - scalarCanopyTemp, & ! intent(out): value for temperature of the vegetation canopy (K) - mLayerTemp, & ! intent(out): vector of layer temperature (K) + ! output: new values of variables needed in data window outside of internal IDA for rootfinding and to start enthalpy calculations + scalarCanopyTemp, & ! intent(inout): value for temperature of the vegetation canopy (K) + mLayerTemp, & ! intent(inout): vector of layer temperature (K) mLayerMatricHead, & ! intent(out): value for total water matric potential (m) ! output: new prime values of variables needed in data window outside of internal IDA for Jacobian scalarCanopyTempPrime, & ! intent(out): prime value for temperature of the vegetation canopy (K s-1) diff --git a/build/source/engine/updateVarsWithPrime.f90 b/build/source/engine/updateVarsWithPrime.f90 index 6a17eea0b..34a67dc80 100644 --- a/build/source/engine/updateVarsWithPrime.f90 +++ b/build/source/engine/updateVarsWithPrime.f90 @@ -439,7 +439,7 @@ subroutine updateVarsWithPrime(& snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) scalarCanopyEnthalpyTrial, & ! intent(in): trial value for enthalpy of the vegetation canopy (J m-3) scalarCanopyWatTrial, & ! intent(in): trial value for canopy total water (kg m-2) - scalarCanopyTempTrial, & ! intent(out): trial value for canopy temperature (K) + scalarCanopyTempTrial, & ! intent(inout): trial value for canopy temperature (K) dCanopyTemp_dEnthalpy, & ! intent(inout): derivative of canopy temperature with enthalpy dCanopyTemp_dCanWat, & ! intent(inout): derivative of canopy temperature with canopy water err,cmessage) ! intent(out): error control @@ -455,7 +455,7 @@ subroutine updateVarsWithPrime(& snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) mLayerEnthalpyTrial(iLayer), & ! intent(in): enthalpy of snow+soil layer (J m-3) mLayerVolFracWatTrial(iLayer), & ! intent(in): volumetric total water content (-) - mLayerTempTrial(iLayer), & ! intent(out): layer temperature (K) + mLayerTempTrial(iLayer), & ! intent(inout): layer temperature (K) dTemp_dEnthalpy(iLayer), & ! intent(inout): derivative of layer temperature with enthalpy dTemp_dTheta(iLayer), & ! intent(inout): derivative of layer temperature with volumetric total water content err,cmessage) ! intent(out): error control @@ -479,7 +479,7 @@ subroutine updateVarsWithPrime(& lookup_data, & ! intent(in): lookup table data structure mLayerEnthalpyTrial(iLayer), & ! intent(in): trial vector of enthalpy of each snow+soil layer (J m-3) mLayerMatricHeadTrial(ixControlIndex), & ! intent(in): trial vector of total water matric potential (m) - mLayerTempTrial(iLayer), & ! intent(out): trial vector of layer temperature (K) + mLayerTempTrial(iLayer), & ! intent(inout): trial vector of layer temperature (K) dTemp_dEnthalpy(iLayer), & ! intent(inout): derivative of layer temperature with enthalpy dTemp_dTheta(iLayer), & ! intent(inout): derivative of layer temperature with volumetric total water content dTemp_dPsi0(ixControlIndex), & ! intent(inout): derivative of layer temperature with total water matric potential diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index bb654b27b..e9707bff3 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -815,12 +815,15 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! ------------------ ! initialize to state variable from the last update + scalarCanairTempTrial = scalarCanairTemp scalarCanairEnthalpyTrial = scalarCanairEnthalpy + scalarCanopyTempTrial = scalarCanopyTemp scalarCanopyEnthalpyTrial = scalarCanopyEnthalpy scalarCanopyEnthTempTrial = scalarCanopyEnthTemp scalarCanopyWatTrial = scalarCanopyWat scalarCanopyLiqTrial = scalarCanopyLiq scalarCanopyIceTrial = scalarCanopyIce + mLayerTempTrial = mLayerTemp mLayerEnthalpyTrial = mLayerEnthalpy mLayerEnthTempTrial = mLayerEnthTemp mLayerVolFracWatTrial = mLayerVolFracWat From 8f96aa8c3e031f1cda653b24fe1d4277ade9960a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 11 Apr 2024 16:06:12 +0900 Subject: [PATCH 1253/1472] putting in Brent function instead of Newton --- build/source/engine/check_icond.f90 | 5 +- build/source/engine/coupled_em.f90 | 2 + build/source/engine/enthalpyTemp.f90 | 820 ++++++++++++++------------- build/source/engine/updateVars.f90 | 5 +- 4 files changed, 438 insertions(+), 394 deletions(-) diff --git a/build/source/engine/check_icond.f90 b/build/source/engine/check_icond.f90 index 81a831288..abd548472 100644 --- a/build/source/engine/check_icond.f90 +++ b/build/source/engine/check_icond.f90 @@ -324,8 +324,9 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU vGn_alpha(iSoil),vGn_n(iSoil),theta_sat(iSoil),theta_res(iSoil),vGn_m, & ! intent(in): van Genutchen soil parameters iSoil, & ! intent(in): index of the control volume within the domain lookupData%gru(iGRU)%hru(iHRU), & ! intent(in): lookup table data structure - mLayerTemp(iLayer), & ! intent(in): layer temperature (K) - mLayerMatricHead(iLayer-nSnow), & ! intent(in): matric head (m) + realMissing, & ! intent(in): lower value of integral (not computed) + mLayerTemp(iLayer), & ! intent(in): layer temperature (K) + mLayerMatricHead(iLayer-nSnow), & ! intent(in): matric head (m) ! output mLayerEnthTemp(iLayer), & ! intent(out): temperature component of enthalpy soil layer (J m-3) err,cmessage) ! intent(out): error control diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 609bbd022..7bac609e3 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -834,6 +834,7 @@ subroutine coupled_em(& vGn_alpha(iSoil),vGn_n(iSoil),theta_sat(iSoil),theta_res(iSoil),vGn_m(iSoil), & ! intent(in): soil parameters iSoil, & ! intent(in): index of the control volume within the domain lookup_data, & ! intent(in): lookup table data structure + realMissing, & ! intent(in): lower value of integral (not computed) mLayerTemp(iLayer), & ! intent(in): layer temperature (K) mLayerMatricHead(iSoil), & ! intent(in): matric head (m) ! output @@ -953,6 +954,7 @@ subroutine coupled_em(& vGn_alpha(1),vGn_n(1),theta_sat(1),theta_res(1),vGn_m(1), & ! intent(in): van Genutchen soil parameters 1_i4b, & ! intent(in): index of the control volume within the domain lookup_data, & ! intent(in): lookup table data structure + realMissing, & ! intent(in): lower value of integral (not computed) prog_data%var(iLookPROG%mLayerTemp)%dat(nSnow+1), & ! intent(in): surface layer temperature (K) mLayerMatricHead(1), & ! intent(in): surface layer matric head (m) ! output diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 2a429162f..85a879454 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -73,6 +73,7 @@ module enthalpyTemp_module public::enthalpy2T_snow public::enthalpy2T_soil private::hyp_2F1_real +private::brent, H_veg, H_snow, H_soil ! define the snow look-up table used to compute temperature based on enthalpy integer(i4b),parameter :: nlook=10001 ! number of elements in the lookup table @@ -561,6 +562,7 @@ subroutine T2enthTemp_soil(& vGn_m, & ! intent(in): van Genutchen "m" parameter (-) ixControlIndex, & ! intent(in): index of the control volume within the domain lookup_data, & ! intent(in): lookup table data structure + integral_frz_low0, & ! intent(in): integral_frz_low if computed outside, else realMissing mLayerTemp, & ! intent(in): layer temperature (K) mLayerMatricHead, & ! intent(in): total water matric potential (m) mLayerEnthTemp, & ! intent(out): temperature component of enthalpy soil layer (J m-3) @@ -583,6 +585,7 @@ subroutine T2enthTemp_soil(& real(rkind),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) integer(i4b),intent(in) :: ixControlIndex ! index within a given model domain type(zLookup),intent(in) :: lookup_data ! lookup tables + real(rkind),intent(in) :: integral_frz_low0 ! integral_frz_low if computed outside, else realMissing ! input: variables for the soil domain real(rkind),intent(in) :: mLayerTemp ! layer temperature (K) real(rkind),intent(in) :: mLayerMatricHead ! total water matric potential (m) @@ -642,8 +645,12 @@ subroutine T2enthTemp_soil(& ! get the lower limit of the integral if(diff0<0._rkind)then - call splint(Tk,Ly,L2,Tcrit,integral_frz_low,dL,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + if(integral_frz_low0>=0)then ! = realMissing if non-compute + integral_frz_low = integral_frz_low0 + else + call splint(Tk,Ly,L2,Tcrit,integral_frz_low,dL,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + endif else ! Tcrit=Tfreeze, i.e. mLayerMatricHeadTrial(ixControlIndex)>0 integral_frz_low = 0._rkind end if @@ -656,9 +663,13 @@ subroutine T2enthTemp_soil(& else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature ! get the lower limit of the integral if(diff0<0._rkind)then - arg = (vGn_alpha * mLayerMatricHead)**vGn_n - gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - integral_frz_low = diff0 * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + if(integral_frz_low0>=0)then ! = realMissing if non-compute + integral_frz_low = integral_frz_low0 + else + arg = (vGn_alpha * mLayerMatricHead)**vGn_n + gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + integral_frz_low = diff0 * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + endif else ! Tcrit=Tfreeze, i.e. mLayerMatricHeadTrial(ixControlIndex)>0 integral_frz_low = 0._rkind end if @@ -865,39 +876,25 @@ subroutine enthalpy2T_veg(& integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------------------------------- - ! declare local variables, iteration variables - real(rkind) :: T ! iteration temperature (K) - real(rkind) :: H ! iteration enthalpy (J m-3) - real(rkind) :: diffT ! temperature difference of iteration temp from Tfreeze - real(rkind) :: integral ! iteration integral of snow freezing curve - real(rkind) :: fLiq ! iteration fraction liquid water - real(rkind) :: enthVeg ! iteration enthalpy of the vegetation (J m-3) - real(rkind) :: enthLiq ! iteration enthalpy of the liquid region (J m-3) - real(rkind) :: enthIce ! iteration enthalpy of the ice region (J m-3) ! iteration variable derivatives - ! iteration variable derivatives - real(rkind) :: dT_dEnthalpy ! derivative of iteration temperature with enthalpy state variable - real(rkind) :: dT_dWat ! derivative of iteration temperature with water state variable - real(rkind) :: dH_dT ! derivative of iteration enthalpy with iteration temperature - real(rkind) :: d2H_dT2 ! second derivative of iteration enthalpy with iteration temperature - real(rkind) :: dH_dEnthalpy ! derivative of iteration enthalpy with enthalpy state variable - real(rkind) :: dH_dT__dEnthalpy ! derivative of iteration enthalpy with iteration temperature with enthalpy - real(rkind) :: dH_dWat ! derivative of iteration enthalpy with water state variable - real(rkind) :: dH_dT__dWat ! derivative of iteration enthalpy with iteration temperature with water state variable - real(rkind) :: dfLiq_dT ! derivative of iteration fraction liquid water with iteration temperature - real(rkind) :: denthVeg_dT ! derivative of iteration enthalpy of vegetation with iteration temperature - real(rkind) :: denthIce_dT ! derivative of iteration enthalpy of ice with iteration temperature - real(rkind) :: denthLiq_dT ! derivative of iteration enthalpy of liquid water with iteration temperature - real(rkind) :: d2fLiq_dT2 ! second derivative of iteration fraction liquid water with iteration temperature - real(rkind) :: d2enthVeg_dT2 ! second derivative of iteration enthalpy of vegetation with iteration temperature - real(rkind) :: d2enthLiq_dT2 ! second derivative of iteration enthalpy of liquid water with iteration temperature - real(rkind) :: d2enthIce_dT2 ! second derivative of iteration enthalpy of ice with iteration temperature - real(rkind) :: d2enthAir_dT2 ! second derivative of iteration enthalpy of air with iteration temperature - real(rkind) :: denthVeg_dWat ! derivative of iteration enthalpy of vegetation with water state variable - real(rkind) :: denthIce_dWat ! derivative of iteration enthalpy of ice with water state variable - real(rkind) :: denthLiq_dWat ! derivative of iteration enthalpy of liquid water with water state variable - real(rkind) :: denthVeg_dT__dWat ! derivative of iteration enthalpy of vegetation with iteration temperature and water state variable - real(rkind) :: denthIce_dT__dWat ! derivative of iteration enthalpy of ice with iteration temperaturewith water state variable - real(rkind) :: denthLiq_dT__dWat ! derivative of iteration enthalpy of liquid water with iteration temperature and water state variable + ! declare local variables + real(rkind) :: T ! temperature (K) + real(rkind) :: H ! enthalpy (J m-3) + real(rkind) :: diffT ! temperature difference of temp from Tfreeze + real(rkind) :: integral ! integral of snow freezing curve + real(rkind) :: fLiq ! fraction liquid + real(rkind) :: vec(8) ! vector of parameters for the enthalpy function + ! variable derivatives + real(rkind) :: dT_dEnthalpy ! derivative of temperature with enthalpy state variable + real(rkind) :: dT_dWat ! derivative of temperature with water state variable + real(rkind) :: dH_dT ! derivative of enthalpy with temperature + real(rkind) :: dH_dWat ! derivative of enthalpy with water state variable + real(rkind) :: dfLiq_dT ! derivative of fraction liquid water with temperature + real(rkind) :: denthIce_dT ! derivative of enthalpy of ice with temperature + real(rkind) :: denthLiq_dT ! derivative of enthalpy of liquid water with temperature + real(rkind) :: denthVeg_dT ! derivative of enthalpy of vegetation with temperature + real(rkind) :: denthIce_dWat ! derivative of enthalpy of ice with water state variable + real(rkind) :: denthLiq_dWat ! derivative of enthalpy of liquid water with water state variable + real(rkind) :: denthVeg_dWat ! derivative of enthalpy of vegetation with water state variable ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="enthalpy2T_veg/" @@ -909,94 +906,38 @@ subroutine enthalpy2T_veg(& ! ***** iterate to find temperature if ice exists if( T1.e-6_rkind ) - diffT = T - Tfreeze - if(T>=Tfreeze)then - ! compute iteration enthalpy function - fLiq = 1._rkind - enthLiq = Cp_water * scalarCanopyWat * diffT / canopyDepth - enthIce = 0._rkind - - ! compute derivative of iteration with respect to iteration T - dfLiq_dT = 0._rkind - denthLiq_dT = Cp_water * scalarCanopyWat / canopyDepth - denthIce_dT = 0._rkind - else - ! compute iteration enthalpy function - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - fLiq = fracLiquid(T, snowfrz_scale) - enthLiq = Cp_water * scalarCanopyWat * integral / canopyDepth - enthIce = Cp_ice * scalarCanopyWat * ( diffT - integral ) / canopyDepth - - ! compute derivative of iteration with respect to iteration T - ! NOTE: dintegral_dT = fLiq - dfLiq_dT = dFracLiq_dTk(T,snowfrz_scale) - denthLiq_dT = Cp_water * scalarCanopyWat * fLiq / canopyDepth - denthIce_dT = Cp_ice * scalarCanopyWat * (1._rkind - fLiq) / canopyDepth - endif + ! find the root of the function + ! inputs = function, lower bound, upper bound, initial point, tolerance, integer flag if want detail + ! and the vector of parameters + vec = 0._rkind + vec(1:5) = (/canopyDepth, specificHeatVeg, maxMassVegetation, snowfrz_scale, scalarCanopyWat/) + T = brent(H_veg,-200._rkind,Tfreeze, T, 1.e-6_rkind, 0, vec) - ! compute iteration enthalpy function, H - enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth - H = enthVeg + enthLiq + enthIce - LH_fus * (1._rkind - fLiq) * scalarCanopyWat / canopyDepth + ! compute Jacobian terms + if(computJac)then + ! NOTE: dintegral_dT = fLiq + diffT = T - Tfreeze + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + fLiq = fracLiquid(T, snowfrz_scale) - ! compute derivative of iteration H with respect to iteration T + ! w.r.t. temperature, NOTE: dintegral_dT = fLiq + dfLiq_dT = dFracLiq_dTk(T,snowfrz_scale) + denthLiq_dT = Cp_water * scalarCanopyWat * fLiq / canopyDepth + denthIce_dT = Cp_ice * scalarCanopyWat * (1._rkind - fLiq) / canopyDepth denthVeg_dT = specificHeatVeg * maxMassVegetation / canopyDepth dH_dT = denthVeg_dT + denthLiq_dT + denthIce_dT + LH_fus * dfLiq_dT * scalarCanopyWat / canopyDepth - ! compute change in T and update - T = T - (H - scalarCanopyEnthalpy)/dH_dT - - if(computJac)then - if(T>=Tfreeze)then - ! compute second derivatives - d2fLiq_dT2 = 0._rkind - d2enthLiq_dT2 = 0._rkind - d2enthIce_dT2 = 0._rkind - - ! compute derivative of iteration with respect to canopy water - denthLiq_dWat = Cp_water * diffT / canopyDepth - denthIce_dWat = 0._rkind - denthLiq_dT__dWat = Cp_water / canopyDepth - denthIce_dT__dWat = 0._rkind - else - ! compute second derivatives - ! NOTE: d2integral_dT2 = dfLiq_dT - d2fLiq_dT2 = 2._rkind * snowfrz_scale**2_i4b * ( 3._rkind * diffT**2_i4b - 1._rkind ) / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**3_i4b - d2enthLiq_dT2 = Cp_water * scalarCanopyWat * dfLiq_dT / canopyDepth - d2enthIce_dT2 = -Cp_ice * scalarCanopyWat * dfLiq_dT / canopyDepth - - ! compute derivative of iteration H with respect to canopy water - denthLiq_dWat = Cp_water * integral / canopyDepth - denthIce_dWat = Cp_ice * ( diffT - integral ) / canopyDepth - denthLiq_dT__dWat = Cp_water * fLiq / canopyDepth - denthIce_dT__dWat = Cp_ice * (1._rkind - fLiq) / canopyDepth - endif - - ! compute second derivatives - d2enthVeg_dT2 = 0._rkind - d2H_dT2 = d2enthVeg_dT2 + d2enthLiq_dT2 + d2enthIce_dT2 + LH_fus * d2fLiq_dT2 * scalarCanopyWat / canopyDepth - - ! compute derivative of iteration H with respect to canopy enthalpy - dH_dEnthalpy = dH_dT * dT_dEnthalpy - dH_dT__dEnthalpy = d2H_dT2 * dT_dEnthalpy - - ! compute derivative of iteration H with respect to canopy water - denthVeg_dWat = 0._rkind - dH_dWat = dH_dT * dT_dWat + denthVeg_dWat + denthLiq_dWat + denthIce_dWat - LH_fus * (1._rkind - fLiq) / canopyDepth - denthVeg_dT__dWat = 0._rkind - dH_dT__dWat = d2H_dT2 * dT_dWat + denthVeg_dT__dWat + denthLiq_dT__dWat + denthIce_dT__dWat + LH_fus * dfLiq_dT / canopyDepth - - ! update derivatives - dT_dEnthalpy = dT_dEnthalpy - ( dH_dEnthalpy - 1._rkind - dH_dT__dEnthalpy * (H - scalarCanopyEnthalpy)/dH_dT ) / dH_dT - dT_dWat = dT_dWat - ( dH_dWat - dH_dT__dWat * (H - scalarCanopyEnthalpy)/dH_dT ) / dH_dT - endif - end do + ! w.r.t. layer water content + denthLiq_dWat = Cp_water * diffT / canopyDepth + denthIce_dWat = 0._rkind + denthVeg_dWat = 0._rkind + dH_dWat = dH_dT * dT_dWat + denthVeg_dWat + denthLiq_dWat + denthIce_dWat - LH_fus * (1._rkind - fLiq) / canopyDepth + dT_dEnthalpy = 1._rkind / dH_dT + dT_dWat = dH_dWat / dH_dT + endif endif ! (if ice exists) ! update temperature and derivatives @@ -1008,7 +949,6 @@ subroutine enthalpy2T_veg(& end subroutine enthalpy2T_veg - ! ************************************************************************************************************************ ! public subroutine enthalpy2T_snow: compute temperature from enthalpy and total water content, snow layer ! ************************************************************************************************************************ @@ -1045,141 +985,68 @@ subroutine enthalpy2T_snow(& integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------------------------------- - ! declare local variables, iteration variables - real(rkind) :: T ! iteration temperature (K) - real(rkind) :: H ! iteration enthalpy (J m-3) - real(rkind) :: diffT ! temperature difference of iteration temp from Tfreeze - real(rkind) :: integral ! iteration integral of snow freezing curve - real(rkind) :: fLiq ! iteration fraction liquid - real(rkind) :: enthLiq ! iteration enthalpy of the liquid region (J m-3) - real(rkind) :: enthIce ! iteration enthalpy of the ice region (J m-3) - real(rkind) :: enthAir ! iteration enthalpy of air (J m-3) - ! iteration variable derivatives - real(rkind) :: dT_dEnthalpy ! derivative of iteration temperature with enthalpy state variable - real(rkind) :: dT_dWat ! derivative of iteration temperature with water state variable - real(rkind) :: dH_dT ! derivative of iteration enthalpy with iteration temperature - real(rkind) :: d2H_dT2 ! second derivative of iteration enthalpy with iteration temperature - real(rkind) :: dH_dEnthalpy ! derivative of iteration enthalpy with enthalpy state variable - real(rkind) :: dH_dT__dEnthalpy ! derivative of iteration enthalpy with iteration temperature with enthalpy - real(rkind) :: dH_dWat ! derivative of iteration enthalpy with water state variable - real(rkind) :: dH_dT__dWat ! derivative of iteration enthalpy with iteration temperature with water state variable - real(rkind) :: dfLiq_dT ! derivative of iteration fraction liquid water with iteration temperature - real(rkind) :: denthIce_dT ! derivative of iteration enthalpy of ice with iteration temperature - real(rkind) :: denthLiq_dT ! derivative of iteration enthalpy of liquid water with iteration temperature - real(rkind) :: denthAir_dT ! derivative of iteration enthalpy of air with temperature - real(rkind) :: d2fLiq_dT2 ! second derivative of iteration fraction liquid water with iteration temperature - real(rkind) :: d2enthLiq_dT2 ! second derivative of iteration enthalpy of liquid water with iteration temperature - real(rkind) :: d2enthIce_dT2 ! second derivative of iteration enthalpy of ice with iteration temperature - real(rkind) :: d2enthAir_dT2 ! second derivative of iteration enthalpy of air with iteration temperature - real(rkind) :: denthIce_dWat ! derivative of iteration enthalpy of ice with water state variable - real(rkind) :: denthLiq_dWat ! derivative of iteration enthalpy of liquid water with water state variable - real(rkind) :: denthAir_dWat ! derivative of iteration enthalpy of air with water state variable - real(rkind) :: denthIce_dT__dWat ! derivative of iteration enthalpy of ice with iteration temperaturewith water state variable - real(rkind) :: denthLiq_dT__dWat ! derivative of iteration enthalpy of liquid water with iteration temperature and water state variable - real(rkind) :: denthAir_dT__dWat ! derivative of iteration enthalpy of air with iteration temperature with water state variable + ! declare local variables + real(rkind) :: T ! temperature (K) + real(rkind) :: H ! enthalpy (J m-3) + real(rkind) :: diffT ! temperature difference of temp from Tfreeze + real(rkind) :: integral ! integral of snow freezing curve + real(rkind) :: fLiq ! fraction liquid + real(rkind) :: vec(8) ! vector of parameters for the enthalpy function + ! variable derivatives + real(rkind) :: dT_dEnthalpy ! derivative of temperature with enthalpy state variable + real(rkind) :: dT_dWat ! derivative of temperature with water state variable + real(rkind) :: dH_dT ! derivative of enthalpy with temperature + real(rkind) :: dH_dWat ! derivative of enthalpy with water state variable + real(rkind) :: dfLiq_dT ! derivative of fraction liquid water with temperature + real(rkind) :: denthIce_dT ! derivative of enthalpy of ice with temperature + real(rkind) :: denthLiq_dT ! derivative of enthalpy of liquid water with temperature + real(rkind) :: denthAir_dT ! derivative of enthalpy of air with temperature + real(rkind) :: denthIce_dWat ! derivative of enthalpy of ice with water state variable + real(rkind) :: denthLiq_dWat ! derivative of enthalpy of liquid water with water state variable + real(rkind) :: denthAir_dWat ! derivative of enthalpy of air with water state variable ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="enthalpy2T_snow/" ! ***** iterate to find temperature, ice always exists - T = mLayerTemp ! initial guess, will be less than Tfreeze since was a solution - H = mLayerEnthalpy + 1._rkind ! to start the iteration - dT_dEnthalpy = 0._rkind - dT_dWat = 0._rkind - - do while( abs((H - mLayerEnthalpy)/dH_dT)>1.e-6_rkind ) - diffT = T - Tfreeze - if(T>=Tfreeze)then - ! compute iteration enthalpy function - integral = diffT - fLiq = 1._rkind - enthLiq = iden_water * Cp_water * mLayerVolFracWat * diffT - enthIce = 0._rkind - - ! compute derivative of iteration with respect to iteration T - dfLiq_dT = 0._rkind - denthLiq_dT = iden_water * Cp_water * mLayerVolFracWat - denthIce_dT = 0._rkind - else - ! compute iteration enthalpy function - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - fLiq = fracLiquid(T, snowfrz_scale) - enthLiq = iden_water * Cp_water * mLayerVolFracWat * integral - enthIce = iden_water * Cp_ice * mLayerVolFracWat * ( diffT - integral ) + T = mLayerTemp ! initial guess, will be less than Tfreeze since was a solution - ! compute derivative of iteration with respect to iteration T - ! NOTE: dintegral_dT = fLiq - dfLiq_dT = dFracLiq_dTk(T,snowfrz_scale) - denthLiq_dT = iden_water * Cp_water * mLayerVolFracWat * fLiq - denthIce_dT = iden_water * Cp_ice * mLayerVolFracWat * (1._rkind - fLiq) - endif - ! compute iteration enthalpy function, H - enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWat * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) - H = enthLiq + enthIce + enthAir - iden_ice * LH_fus * (1._rkind - fLiq) * mLayerVolFracWat + ! find the root of the function + ! inputs = function, lower bound, upper bound, initial point, tolerance, integer flag if want detail + ! and the vector of parameters + vec = 0._rkind + vec(1:2) = (/snowfrz_scale, mLayerVolFracWat/) + T = brent(H_snow,-200._rkind,Tfreeze, T, 1.e-6_rkind, 0, vec) - ! compute derivative of iteration H with respect to iteration T + ! compute Jacobian terms + if(computJac)then + + ! w.r.t. temperature, NOTE: dintegral_dT = fLiq + dfLiq_dT = dFracLiq_dTk(T,snowfrz_scale) + denthLiq_dT = iden_water * Cp_water * mLayerVolFracWat * fLiq + denthIce_dT = iden_water * Cp_ice * mLayerVolFracWat * (1._rkind - fLiq) denthAir_dT = iden_air * Cp_air * (1._rkind - mLayerVolFracWat * ( (iden_water/iden_ice)*(1._rkind-fLiq) + fLiq ) ) dH_dT = denthLiq_dT + denthIce_dT + denthAir_dT + iden_ice * LH_fus * dfLiq_dT * mLayerVolFracWat - ! compute change in T and update - T = T - (H - mLayerEnthalpy)/dH_dT - - if(computJac)then - if(T>=Tfreeze)then - ! compute second derivatives - d2fLiq_dT2 = 0._rkind - d2enthLiq_dT2 = 0._rkind - d2enthIce_dT2 = 0._rkind - - ! compute derivative of iteration with respect to layer water content - denthLiq_dWat = iden_water * Cp_water * diffT - denthIce_dWat = 0._rkind - denthLiq_dT__dWat = iden_water * Cp_water - denthIce_dT__dWat = 0._rkind - else - ! compute second derivatives - ! NOTE: d2integral_dT2 = dfLiq_dT - d2fLiq_dT2 = 2._rkind * snowfrz_scale**2_i4b * ( 3._rkind * diffT**2_i4b - 1._rkind ) / ( 1._rkind + (snowfrz_scale * diffT)**2_i4b )**3_i4b - d2enthLiq_dT2 = iden_water * Cp_water * mLayerVolFracWat * dfLiq_dT - d2enthIce_dT2 = -iden_water * Cp_ice * mLayerVolFracWat * dfLiq_dT - - ! compute derivative of iteration with respect to layer water content - denthLiq_dWat = iden_water * Cp_water * integral - denthIce_dWat = iden_water * Cp_ice * ( diffT - integral ) - denthLiq_dT__dWat = iden_water * Cp_water * fLiq - denthIce_dT__dWat = iden_water * Cp_ice * (1._rkind - fLiq) - endif - - ! compute second derivatives - d2enthAir_dT2 = iden_air * Cp_air * ( mLayerVolFracWat * dfLiq_dT *( (iden_water/iden_ice) - 1._rkind ) ) - d2H_dT2 = d2enthLiq_dT2 + d2enthIce_dT2 + d2enthAir_dT2 + iden_ice * LH_fus * d2fLiq_dT2 * mLayerVolFracWat + ! w.r.t. layer water content + denthLiq_dWat = iden_water * Cp_water * integral + denthIce_dWat = iden_water * Cp_ice * ( diffT - integral ) + denthAir_dWat = -iden_air * Cp_air * ( (iden_water/iden_ice)*(diffT-integral) + integral ) + dH_dWat = dH_dT * dT_dWat + denthLiq_dWat + denthIce_dWat + denthAir_dWat - iden_ice * LH_fus * (1._rkind - fLiq) - ! compute derivative of iteration H with respect to layer enthalpy - dH_dEnthalpy = dH_dT * dT_dEnthalpy - dH_dT__dEnthalpy = d2H_dT2 * dT_dEnthalpy - - ! compute derivative of iteration H with respect to layer water content - denthAir_dWat = -iden_air * Cp_air * ( (iden_water/iden_ice)*(diffT-integral) + integral ) - dH_dWat = dH_dT * dT_dWat + denthLiq_dWat + denthIce_dWat + denthAir_dWat - iden_ice * LH_fus * (1._rkind - fLiq) - denthAir_dT__dWat = -iden_air * Cp_air * ( (iden_water/iden_ice)*(1._rkind-fLiq) + fLiq ) - dH_dT__dWat = d2H_dT2 * dT_dWat + denthLiq_dT__dWat + denthIce_dT__dWat + denthAir_dT__dWat + iden_ice * LH_fus * dfLiq_dT - - ! update derivatives - dT_dEnthalpy = dT_dEnthalpy - ( dH_dEnthalpy - 1._rkind - dH_dT__dEnthalpy * (H - mLayerEnthalpy)/dH_dT ) / dH_dT - dT_dWat = dT_dWat - ( dH_dWat - dH_dT__dWat * (H - mLayerEnthalpy)/dH_dT ) / dH_dT - endif - end do + dT_dEnthalpy = 1._rkind / dH_dT + dT_dWat = dH_dWat / dH_dT + endif ! update temperature and derivatives mLayerTemp = T - if(computJac)then + if(computJac)then dTemp_dEnthalpy = dT_dEnthalpy dTemp_dTheta = dT_dWat endif end subroutine enthalpy2T_snow - ! ************************************************************************************************************************ ! public subroutine enthalpy2T_soil: compute temperature from enthalpy and total water content, soil layer ! ************************************************************************************************************************ @@ -1249,50 +1116,32 @@ subroutine enthalpy2T_soil(& real(rkind) :: integral_frz_low ! lower limit of integral of frozen soil water content (from Tfreeze to Tcrit) real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) + real(rkind) :: T ! temperature (K) + real(rkind) :: H ! enthalpy (J m-3) + real(rkind) :: diffT ! temperature difference of temp from Tfreeze + real(rkind) :: fLiq ! fraction liquid water + real(rkind) :: integral_frz_upp ! upper limit of integral of frozen soil water content (from Tfreeze to soil temperature) + real(rkind) :: arg ! argument of soil hypergeometric function + real(rkind) :: gauss_hg_T ! soil hypergeometric function result + real(rkind) :: vec(8) ! vector of parameters for the enthalpy function + ! variable derivatives real(rkind) :: dvolFracWat_dPsi0 ! derivative of the soil water content w.r.t. matric head real(rkind) :: dintegral_unf_dWat ! derivative of integral of unfrozen soil water content with water content real(rkind) :: dintegral_frz_low_dWat ! derivative of integral of frozen soil water content with water content - ! iteration variables - real(rkind) :: T ! iteration temperature (K) - real(rkind) :: H ! iteration enthalpy (J m-3) - real(rkind) :: diffT ! temperature difference of iteration temp from Tfreeze - real(rkind) :: fLiq ! iteration fraction liquid water - real(rkind) :: integral_frz_upp ! upper limit of iteration integral of frozen soil water content (from Tfreeze to soil temperature) - real(rkind) :: arg ! argument of iteration soil hypergeometric function - real(rkind) :: gauss_hg_T ! iteration soil hypergeometric function result - real(rkind) :: enthSoil ! iteration enthalpy of soil particles (J m-3) - real(rkind) :: enthLiq ! iteration enthalpy of the liquid region (J m-3) - real(rkind) :: enthIce ! iteration enthalpy of the ice region (J m-3) - real(rkind) :: enthAir ! iteration enthalpy of air (J m-3) - ! iteration variable derivatives - real(rkind) :: dT_dEnthalpy ! derivative of iteration temperature with enthalpy state variable - real(rkind) :: dT_dWat ! derivative of iteration temperature with water state variable - real(rkind) :: dH_dT ! derivative of iteration enthalpy with iteration temperature - real(rkind) :: d2H_dT2 ! second derivative of iteration enthalpy with iteration temperature - real(rkind) :: dH_dEnthalpy ! derivative of iteration enthalpy with enthalpy state variable - real(rkind) :: dH_dT__dEnthalpy ! derivative of iteration enthalpy with iteration temperature with enthalpy - real(rkind) :: dH_dWat ! derivative of iteration enthalpy with water state variable - real(rkind) :: dH_dT__dWat ! derivative of iteration enthalpy with iteration temperature with water state variable - real(rkind) :: dfLiq_dT ! derivative of iteration fraction liquid water with iteration temperature - real(rkind) :: dintegral_frz_upp_dT ! derivative of iteration integral of frozen soil water content with iteration temperature - real(rkind) :: denthSoil_dT ! derivative of iteration enthalpy of soil with iteration temperature - real(rkind) :: denthIce_dT ! derivative of iteration enthalpy of ice with iteration temperature - real(rkind) :: denthLiq_dT ! derivative of iteration enthalpy of liquid water with iteration temperature - real(rkind) :: denthAir_dT ! derivative of iteration enthalpy of air with temperature - real(rkind) :: d2fLiq_dT2 ! second derivative of iteration fraction liquid water with iteration temperature - real(rkind) :: d2integral_frz_upp_dT2 ! second derivative of iteration integral of frozen soil water content with iteration temperature - real(rkind) :: d2enthSoil_dT2 ! second derivative of iteration enthalpy of soil with iteration temperature - real(rkind) :: d2enthLiq_dT2 ! second derivative of iteration enthalpy of liquid water with iteration temperature - real(rkind) :: d2enthIce_dT2 ! second derivative of iteration enthalpy of ice with iteration temperature - real(rkind) :: d2enthAir_dT2 ! second derivative of iteration enthalpy of air with iteration temperature - real(rkind) :: denthSoil_dWat ! derivative of iteration enthalpy of soil with water state variable - real(rkind) :: denthIce_dWat ! derivative of iteration enthalpy of ice with water state variable - real(rkind) :: denthLiq_dWat ! derivative of iteration enthalpy of liquid water with water state variable - real(rkind) :: denthAir_dWat ! derivative of iteration enthalpy of air with water state variable - real(rkind) :: denthSoil_dT__dWat ! derivative of iteration enthalpy of soil with iteration temperature and water state variable - real(rkind) :: denthIce_dT__dWat ! derivative of iteration enthalpy of ice with iteration temperaturewith water state variable - real(rkind) :: denthLiq_dT__dWat ! derivative of iteration enthalpy of liquid water with iteration temperature and water state variable - real(rkind) :: denthAir_dT__dWat ! derivative of iteration enthalpy of air with iteration temperature with water state variable + real(rkind) :: dT_dEnthalpy ! derivative of temperature with enthalpy state variable + real(rkind) :: dT_dWat ! derivative of temperature with water state variable + real(rkind) :: dH_dT ! derivative of enthalpy with temperature + real(rkind) :: dH_dWat ! derivative of enthalpy with water state variable + real(rkind) :: dfLiq_dT ! derivative of fraction liquid water with temperature + real(rkind) :: dintegral_frz_upp_dT ! derivative of integral of frozen soil water content with temperature + real(rkind) :: denthSoil_dT ! derivative of enthalpy of soil with temperature + real(rkind) :: denthIce_dT ! derivative of enthalpy of ice with temperature + real(rkind) :: denthLiq_dT ! derivative of enthalpy of liquid water with temperature + real(rkind) :: denthAir_dT ! derivative of enthalpy of air with temperature + real(rkind) :: denthSoil_dWat ! derivative of enthalpy of soil with water state variable + real(rkind) :: denthIce_dWat ! derivative of enthalpy of ice with water state variable + real(rkind) :: denthLiq_dWat ! derivative of enthalpy of liquid water with water state variable + real(rkind) :: denthAir_dWat ! derivative of enthalpy of air with water state variable ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="enthalpy2T_soil/" @@ -1309,19 +1158,16 @@ subroutine enthalpy2T_soil(& ! ***** iterate to find temperature if ice exists if( T1.e-6_rkind ) - diffT = T - Tfreeze - - ! NOTE: this should be T>=Tcrit, but we need to smooth the function from Tcrit to Tfreeze for the Newton-Raphson method - ! Newton-Raphson step should not find a solution of T>Tcrit so does not affect the solution - if(T>=Tfreeze)then - ! compute iteration enthalpy function - fLiq = theta_sat ! should be volFracWat if were not smoothing the function - enthLiq = iden_water * Cp_water * (fLiq * diffT + integral_unf - integral_frz_low) ! should be iden_water*Cp_water*fLiq*diffT if were not smoothing the function - enthIce = iden_ice * Cp_ice * ( (volFracWat - fLiq) * diffT - (integral_unf - integral_frz_low) ) ! should be iden_ice*Cp_ice*(volFracWat - fLiq)*diffT if were not smoothing the function - - ! compute derivative of iteration with respect to iteration T - dfLiq_dT = 0._rkind - denthLiq_dT = iden_water * Cp_water * fLiq - denthIce_dT = iden_ice * Cp_ice * (volFracWat - fLiq) - else - mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution, DIFFERENT from the liquid water matric potential used in the flux calculations - arg = (vGn_alpha * mLayerPsiLiq)**vGn_n - - ! get the upper limit of the integral - if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature - ! make associate to the the lookup table - lookVars2: associate(& - Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup, & ! temperature (K) - Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup, & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) - L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function - ) ! end associate statement - - ! integral of mLayerPsiLiq from Tfreeze to layer temperature - call splint(Tk,Ly,L2,T,integral_frz_upp,dL,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - dintegral_frz_upp_dT = dL - if(computJac) d2integral_frz_upp_dT2 = 0._rkind + ! find the root of the function + ! inputs = function, lower bound, upper bound, initial point, tolerance, integer flag if want detail + ! and the vector of parameters + vec(1:8) = (/soil_dens_intr, vGn_alpha, vGn_n, theta_sat, theta_res, vGn_m, integral_frz_low, mLayerMatricHead/) + T = brent(H_soil,-200._rkind,Tcrit, T, 1.e-6_rkind, 0, vec, use_lookup, lookup_data, ixControlIndex) - end associate lookVars2 + ! compute Jacobian terms + if(computJac)then + ! NOTE: here fLiq is the total liquid fraction, not fraction of water fraction that is liquid + xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) + diffT = T - Tfreeze + mLayerPsiLiq = xConst*diffT ! liquid water matric potential from the Clapeyron eqution, DIFFERENT from the liquid water matric potential used in the flux calculations + arg = (vGn_alpha * mLayerPsiLiq)**vGn_n + fLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature - gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) - integral_frz_upp = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) - dintegral_frz_upp_dT = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) ! fLiq - if(computJac) d2integral_frz_upp_dT2 = dTheta_dTk(T,theta_res,theta_sat,vGn_alpha,vGn_n,vGn_m) ! dfLiq_dT - end if + ! get the upper limit of the integral + if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature + ! make associate to the the lookup table + lookVars2: associate(& + Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup, & ! temperature (K) + Ly => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%psiLiq_int)%lookup, & ! integral of mLayerPsiLiq from Tfreeze to Tk (K) + L2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + ) ! end associate statement - ! compute iteration enthalpy function - ! NOTE: here fLiq is the total liquid fraction, not fraction of water fraction that is liquid - fLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - enthLiq = iden_water * Cp_water * (integral_unf + integral_frz_upp - integral_frz_low) - enthIce = iden_ice * Cp_ice * ( volFracWat * diffT - (integral_unf + integral_frz_upp - integral_frz_low) ) + ! integral of mLayerPsiLiq from Tfreeze to layer temperature + call splint(Tk,Ly,L2,T,integral_frz_upp,dL,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + dintegral_frz_upp_dT = dL - ! compute derivative of iteration with respect to iteration T - dfLiq_dT = dTheta_dTk(T,theta_res,theta_sat,vGn_alpha,vGn_n,vGn_m) - denthLiq_dT = iden_water * Cp_water * dintegral_frz_upp_dT - denthIce_dT = iden_ice * Cp_ice * ( volFracWat - dintegral_frz_upp_dT ) - endif + end associate lookVars2 - ! compute iteration enthalpy function, H - enthSoil = soil_dens_intr * Cp_soil * ( 1._rkind - theta_sat ) * diffT - enthAir = iden_air * Cp_air * ( 1._rkind - theta_sat - volFracWat ) * diffT - H = enthLiq + enthIce + enthSoil + enthAir - iden_water * LH_fus * (volFracWat - fLiq) + else ! hypergeometric function for integral of mLayerPsiLiq from Tfreeze to layer temperature + gauss_hg_T = hyp_2F1_real(vGn_m,1._rkind/vGn_n,1._rkind + 1._rkind/vGn_n,-arg) + integral_frz_upp = diffT * ( (theta_sat - theta_res)*gauss_hg_T + theta_res ) + dintegral_frz_upp_dT = fLiq + end if - ! compute derivative of iteration H with respect to iteration T + ! w.r.t. temperature + dfLiq_dT = dTheta_dTk(T,theta_res,theta_sat,vGn_alpha,vGn_n,vGn_m) + denthLiq_dT = iden_water * Cp_water * dintegral_frz_upp_dT + denthIce_dT = iden_ice * Cp_ice * ( volFracWat - dintegral_frz_upp_dT ) denthSoil_dT = soil_dens_intr * Cp_soil * ( 1._rkind - theta_sat ) denthAir_dT = iden_air * Cp_air * ( 1._rkind - theta_sat - volFracWat ) dH_dT = denthLiq_dT + denthIce_dT + denthSoil_dT + denthAir_dT + iden_water * LH_fus * dfLiq_dT - ! compute change in T and update - T = T - (H - mLayerEnthalpy)/dH_dT - - if(computJac)then - if(T>=Tfreeze)then ! should be T>=Tcrit if were not smoothing the function - ! compute second derivatives - d2fLiq_dT2 = 0._rkind - d2enthLiq_dT2 = 0._rkind - d2enthIce_dT2 = 0._rkind - - ! compute derivative of iteration with respect to layer water content - denthLiq_dWat = iden_water * Cp_water * (dintegral_unf_dWat - dintegral_frz_low_dWat) ! should be iden_water * Cp_water * diffT if were not smoothing the function - denthIce_dWat = iden_ice * Cp_ice * ( diffT - (dintegral_unf_dWat - dintegral_frz_low_dWat) ) ! should be 0._rkind if were not smoothing the function - denthLiq_dT__dWat = 0._rkind ! should be iden_water * Cp_water if were not smoothing the function - denthIce_dT__dWat = iden_ice * Cp_ice ! should be 0._rkind if were not smoothing the function - else - ! compute second derivatives - d2fLiq_dT2 = d2Theta_dTk2(T,theta_res,theta_sat,vGn_alpha,vGn_n,vGn_m) - d2enthLiq_dT2 = iden_water * Cp_water * d2integral_frz_upp_dT2 - d2enthIce_dT2 = -iden_ice * Cp_ice * d2integral_frz_upp_dT2 - - ! compute derivative of iteration with respect to layer water content - denthLiq_dWat = iden_water * Cp_water * (dintegral_unf_dWat - dintegral_frz_low_dWat) - denthIce_dWat = iden_ice * Cp_ice * ( dvolFracWat_dPsi0 * diffT - (dintegral_unf_dWat - dintegral_frz_low_dWat) ) - denthLiq_dT__dWat = 0._rkind - denthIce_dT__dWat = iden_ice * Cp_ice * dvolFracWat_dPsi0 - endif - - ! compute second derivatives - d2enthSoil_dT2 = 0._rkind - d2enthAir_dT2 = 0._rkind - d2H_dT2 = d2enthLiq_dT2 + d2enthIce_dT2 + d2enthSoil_dT2 + d2enthAir_dT2 + iden_water * LH_fus * d2fLiq_dT2 - - ! compute derivative of iteration H with respect to layer enthalpy - dH_dEnthalpy = dH_dT * dT_dEnthalpy - dH_dT__dEnthalpy = d2H_dT2 * dT_dEnthalpy - - ! compute derivative of iteration H with respect to layer water content - denthSoil_dWat = 0._rkind - denthAir_dWat = -iden_air * Cp_air * dvolFracWat_dPsi0 * diffT - dH_dWat = dH_dT * dT_dWat + denthLiq_dWat + denthIce_dWat + denthAir_dWat - iden_water * LH_fus * dvolFracWat_dPsi0 - denthSoil_dT__dWat = 0._rkind - denthAir_dT__dWat = -iden_air * Cp_air * dvolFracWat_dPsi0 - dH_dT__dWat = d2H_dT2 * dT_dWat + denthLiq_dT__dWat + denthIce_dT__dWat + denthSoil_dT__dWat + denthAir_dT__dWat - - ! update derivatives - dT_dEnthalpy = dT_dEnthalpy - ( dH_dEnthalpy - 1._rkind - dH_dT__dEnthalpy * (H - mLayerEnthalpy)/dH_dT ) / dH_dT - dT_dWat = dT_dWat - ( dH_dWat - dH_dT__dWat * (H - mLayerEnthalpy)/dH_dT ) / dH_dT - endif - end do + ! w.r.t. layer water content + denthLiq_dWat = iden_water * Cp_water * (dintegral_unf_dWat - dintegral_frz_low_dWat) + denthIce_dWat = iden_ice * Cp_ice * ( dvolFracWat_dPsi0 * diffT - (dintegral_unf_dWat - dintegral_frz_low_dWat) ) + denthSoil_dWat = 0._rkind + denthAir_dWat = -iden_air * Cp_air * dvolFracWat_dPsi0 * diffT + dH_dWat = dH_dT * dT_dWat + denthLiq_dWat + denthIce_dWat + denthAir_dWat - iden_water * LH_fus * dvolFracWat_dPsi0 + endif end if ! (if ice exists) ! update temperature and derivatives @@ -1476,7 +1258,6 @@ subroutine enthalpy2T_soil(& end subroutine enthalpy2T_soil - !---------------------------------------------------------------------- ! private function: compute hypergeometric function with real arguments into real result !---------------------------------------------------------------------- @@ -1497,4 +1278,263 @@ function hyp_2F1_real(a_real, b_real, c_real, z_real) end function hyp_2F1_real -end module enthalpyTemp_module +!---------------------------------------------------------------------- +! private function: Brent's method to find a root of a function +!---------------------------------------------------------------------- +function brent (fun, x1, x2, x0, tol, detail, vec, use_lookup, lookup_data, ixControlIndex) + ! + ! Description of algorithm: + ! Find a root of function f(x) given intial bracketing interval [a,b] + ! where f(a) and f(b) must have opposite signs. At a typical step we have + ! three points a, b, and c such that f(b)f(c)<0, and a may coincide with + ! c. The points a, b, and c change during the algorithm, and the root + ! always lies in either [b,c] or [c, b]. The value b is the best + ! approximation to the root and a is the previous value of b. + ! + ! The iteration uses following selection of algorithms + ! when bracket shrinks reasonablly fast, + ! - Linear interporation if a == b + ! - Quadratic interporation if a != b and the point is in the bracket. + ! othrwise + ! - Bisection. + ! + ! Inputs: + ! fun: function to be solved + ! x1, x2: Upper bound and lower bound for a function + ! x0: initial guess for the root + ! tol: error tolerance. + ! detail (optional): output result of iteration if detail is some value. + ! + ! Based on zeroin.f in netlib + + implicit none + real(rkind) :: brent + integer, parameter :: d = rkind + real(rkind), intent(IN) :: x1, x2, x0, tol, vec(8) + real(rkind), external :: fun + integer, intent(IN) :: detail + logical(lgt), intent(in), optional :: use_lookup + type(zLookup),intent(in), optional :: lookup_data + integer(i4b), intent(in), optional :: ixControlIndex + + integer :: i, exitflag, disp + real(rkind) :: a, b, c, diff,e, fa, fb, fc, p, q, r, s, tol1, xm, tmp, fx1, fx2 + real(rkind), parameter :: EPS = epsilon(a) + integer, parameter :: imax = 100 ! maximum number of iteration + + exitflag = 0 + if (detail /= 0) then + disp = 1 + else + disp = 0 + end if + + ! intialize values + a = x1 + b = x2 + c = x0 + if(present(use_lookup))then + fa = fun(a, vec, use_lookup, lookup_data, ixControlIndex) + fb = fun(b, vec, use_lookup, lookup_data, ixControlIndex) + fc = fun(c, vec, use_lookup, lookup_data, ixControlIndex) + else + fa = fun(a, vec) + fb = fun(b, vec) + fc = fun(c, vec) + end if + fx1 = fa + fx2 = fb + + ! check sign + if ( (fa>0. .and. fb>0. ) .or. (fa>0. .and. fb>0. )) then + write(*,*) 'Error (brent.f90): Root must be bracked by two inputs' + write(*, "('f(x1) = ', 1F8.4, ' f(x2) = ', 1F8.4)") fa,fb + write(*,*) 'press any key to halt the program' + read(*,*) + stop + end if + + if (disp == 1 ) then + write(*,*) 'Brents method to find a root of f(x)' + write(*,*) ' ' + write(*,*) ' i x bracketsize f(x)' + end if + + ! main iteration + do i = 1, imax + ! rename c and adjust bounding interval if both a(=b) and c are same sign + if ((fb > 0. .and. fc > 0) .or. (fb <0. .and. fc < 0. ) ) then + c = a + fc = fa + e = b-a + diff = e + end if + + ! if c is better guess than b, use it. + if (abs(fc) < abs(fb) ) then + a=b + b=c + c=a + fa=fb + fb=fc + fc= fa + end if + + ! convergence check + tol1=2.0_rkind* EPS * abs(b) + 0.5_rkind*tol + xm = 0.5_rkind * (c - b) + if (abs(xm) < tol1 .or. fb == 0.0_rkind ) then + exitflag = 1 + exit + end if + + if (disp == 1) then + tmp = c-b + write(*,"(' ', 1I2, 3F16.6)") i, b, abs(b-c), fb + end if + + ! try inverse quadratic interpolation + if (abs(e) >= tol1 .and. abs(fa) > abs(fb) ) then + s = fb/fa + if (abs(a - c) < EPS) then + p = 2.0_rkind *xm * s + q = 1.0_rkind - s + else + q = fa/fc + r = fb/fc + p = s * (2.0_rkind * xm * q * (q -r ) - (b - a) * (r - 1.0_rkind)) + q = (q - 1.0_rkind ) * (r - 1.0_rkind) * (s - 1.0_rkind) + end if + + ! accept if q is not too small to stay in bound + if (p > 0.0_rkind) q = -q + p = abs(p) + if (2.0 * p < min(3.0 * xm * q - abs(tol1* q), abs(e *q))) then + e = d + diff = p / q + else ! interpolation failed. use bisection + diff= xm + e = d + end if + else ! quadratic interpolation bounds moves too slowly, use bisection + diff = xm + e = d + end if + + ! update last bound + a = b + fa = fb + + ! move the best guess + if (abs(d) > tol1) then + b = b + diff + else + b = b + sign(tol1, xm) + end if + + ! evaluate new trial root + if(present(use_lookup))then + fb = fun(b, vec, use_lookup, lookup_data, ixControlIndex) + else + fb = fun(b, vec) + end if + + end do + + ! case for non convergence + if (exitflag /= 1 ) then + write(*,*) 'Error (brent.f90) : convergence was not attained' + write(*,*) 'Initial value:' + write(*,"(4F10.5)" ) x1, x2, fx1, fx2 + write(*,*) ' ' + write(*,*) 'final value:' + write(*,"('x = ' ,1F6.4, ': f(x1) = ' , 1F6.4 )" ) b, fb + else if( disp == 1) then + write(*,*) 'Brents method was converged.' + write(*,*) '' + end if + brent = b + return + + end function brent + + !---------------------------------------------------------------------- + ! private functions for temperature to enthalpy conversion for Brent's method + !---------------------------------------------------------------------- + function H_veg ( scalarCanopyTemp, vec) + USE snow_utils_module,only:fracliquid ! compute volumetric fraction of liquid water + implicit none + real(rkind) :: H_veg + real(rkind) , intent(IN) :: scalarCanopyTemp, vec(8) + real(rkind) :: scalarCanopyEnthTemp, scalarCanopyWat, scalarCanopyIce + real(rkind) :: canopyDepth, specificHeatVeg, maxMassVegetation, snowfrz_scale, fLiq + integer(i4b) :: err + character(256) :: cmessage + + canopyDepth = vec(1) + specificHeatVeg = vec(2) + maxMassVegetation = vec(3) + snowfrz_scale = vec(4) + scalarCanopyWat = vec(5) + + call T2enthTemp_veg(canopyDepth, specificHeatVeg, maxMassVegetation, snowfrz_scale, scalarCanopyTemp, & + scalarCanopyWat, scalarCanopyEnthTemp, err, cmessage) + fLiq = fracliquid(scalarCanopyTemp, snowfrz_scale) + H_veg = scalarCanopyEnthTemp - LH_fus * scalarCanopyWat* (1._rkind - fLiq)/ canopyDepth + + end function H_veg + !---------------------------------------------------------------------- + function H_snow ( mLayerTemp, vec) + USE snow_utils_module,only:fracliquid ! compute volumetric fraction of liquid water + implicit none + real(rkind) :: H_snow + real(rkind) , intent(IN) :: mLayerTemp, vec(8) + real(rkind) :: mLayerEnthTemp, mLayerVolFracWat, mLayerVolFracIce, snowfrz_scale, fLiq + integer(i4b) :: err + character(256) :: cmessage + + snowfrz_scale = vec(1) + mLayerVolFracWat = vec(2) + + call T2enthTemp_snow(snowfrz_scale, mLayerTemp, mLayerVolFracWat, mLayerEnthTemp, err, cmessage) + fLiq = fracliquid(mLayerTemp, snowfrz_scale) + H_snow = mLayerEnthTemp - iden_ice * LH_fus * (1._rkind - fLiq) + + end function H_snow + !---------------------------------------------------------------------- + function H_soil ( mLayerTemp, vec, use_lookup, lookup_data, ixControlIndex) + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water based on matric head + implicit none + real(rkind) :: H_soil + real(rkind) , intent(in) :: mLayerTemp, vec(8) + logical(lgt), intent(in) :: use_lookup + type(zLookup),intent(in) :: lookup_data + integer(i4b), intent(in) :: ixControlIndex + real(rkind) :: mLayerEnthTemp, mLayerMatricHead, volFracWat, xConst, mLayerPsiLiq, fLiq + real(rkind) :: soil_dens_intr, vGn_alpha, vGn_n, theta_sat, theta_res, vGn_m, integral_frz_low + integer(i4b) :: err + character(256) :: cmessage + + soil_dens_intr = vec(1) + vGn_alpha = vec(2) + vGn_n = vec(3) + theta_sat = vec(4) + theta_res = vec(5) + vGn_m = vec(6) + integral_frz_low = vec(7) + mLayerMatricHead = vec(8) + + call T2enthTemp_soil(use_lookup, soil_dens_intr, vGn_alpha, vGn_n, theta_sat, theta_res, vGn_m, & + ixControlIndex, lookup_data, integral_frz_low, mLayerTemp, mLayerMatricHead, & + mLayerEnthTemp, err, cmessage) + + volFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) + mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution + fLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) ! here fLiq is the total liquid fraction, not fraction of water fraction that is liquid + H_soil = mLayerEnthTemp - iden_water * LH_fus * (volFracWat - fLiq) + + end function H_soil + + +end module enthalpyTemp_module \ No newline at end of file diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 index c149c003a..d9d0e6518 100644 --- a/build/source/engine/updateVars.f90 +++ b/build/source/engine/updateVars.f90 @@ -705,8 +705,9 @@ subroutine updateVars(& vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in): soil parameters ixControlIndex, & ! intent(in): index of the control volume within the domain lookup_data, & ! intent(in): lookup table data structure - mLayerTempTrial(iLayer), & ! intent(in): layer temperature (K) - mLayerMatricHeadTrial(ixControlIndex), & ! intent(in): matric head (m) + realMissing, & ! intent(in): lower value of integral (not computed) + mLayerTempTrial(iLayer), & ! intent(in): layer temperature (K) + mLayerMatricHeadTrial(ixControlIndex), & ! intent(in): matric head (m) ! output mLayerEnthTempTrial(iLayer), & ! intent(out): temperature component of enthalpy soil layer (J m-3) err,cmessage) ! intent(out): error control From 9e4b18a165438cea76ad919ba6da7190f6551678 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 12 Apr 2024 12:05:37 +0900 Subject: [PATCH 1254/1472] fix inputs enthalpy --- build/source/engine/coupled_em.f90 | 4 +- build/source/engine/enthalpyTemp.f90 | 91 ++++++++++++++-------------- 2 files changed, 49 insertions(+), 46 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 7bac609e3..d8b43329e 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -876,11 +876,11 @@ subroutine coupled_em(& ! *** compute diagnostic variables for each layer... ! -------------------------------------------------- - ! NOTE: this needs to be done AFTER volicePack, since layers may have been sub-divided and/or merged + ! NOTE: this needs to be done AFTER volicePack, since layers may have been sub-divided and/or merged, and need to specifically send in canopy depth call diagn_evar(& ! input: control variables computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) + diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): canopy depth (m) ! input/output: data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 85a879454..e0f9f2908 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -73,7 +73,7 @@ module enthalpyTemp_module public::enthalpy2T_snow public::enthalpy2T_soil private::hyp_2F1_real -private::brent, H_veg, H_snow, H_soil +private::brent, diff_H_veg, diff_H_snow, diff_H_soil ! define the snow look-up table used to compute temperature based on enthalpy integer(i4b),parameter :: nlook=10001 ! number of elements in the lookup table @@ -882,7 +882,7 @@ subroutine enthalpy2T_veg(& real(rkind) :: diffT ! temperature difference of temp from Tfreeze real(rkind) :: integral ! integral of snow freezing curve real(rkind) :: fLiq ! fraction liquid - real(rkind) :: vec(8) ! vector of parameters for the enthalpy function + real(rkind) :: vec(9) ! vector of parameters for the enthalpy function ! variable derivatives real(rkind) :: dT_dEnthalpy ! derivative of temperature with enthalpy state variable real(rkind) :: dT_dWat ! derivative of temperature with water state variable @@ -912,8 +912,8 @@ subroutine enthalpy2T_veg(& ! inputs = function, lower bound, upper bound, initial point, tolerance, integer flag if want detail ! and the vector of parameters vec = 0._rkind - vec(1:5) = (/canopyDepth, specificHeatVeg, maxMassVegetation, snowfrz_scale, scalarCanopyWat/) - T = brent(H_veg,-200._rkind,Tfreeze, T, 1.e-6_rkind, 0, vec) + vec(1:6) = (/scalarCanopyEnthalpy, canopyDepth, specificHeatVeg, maxMassVegetation, snowfrz_scale, scalarCanopyWat/) + T = brent(diff_H_veg,200._rkind,Tfreeze, T, 1.e-6_rkind, 0, vec) ! compute Jacobian terms if(computJac)then @@ -991,7 +991,7 @@ subroutine enthalpy2T_snow(& real(rkind) :: diffT ! temperature difference of temp from Tfreeze real(rkind) :: integral ! integral of snow freezing curve real(rkind) :: fLiq ! fraction liquid - real(rkind) :: vec(8) ! vector of parameters for the enthalpy function + real(rkind) :: vec(9) ! vector of parameters for the enthalpy function ! variable derivatives real(rkind) :: dT_dEnthalpy ! derivative of temperature with enthalpy state variable real(rkind) :: dT_dWat ! derivative of temperature with water state variable @@ -1015,8 +1015,8 @@ subroutine enthalpy2T_snow(& ! inputs = function, lower bound, upper bound, initial point, tolerance, integer flag if want detail ! and the vector of parameters vec = 0._rkind - vec(1:2) = (/snowfrz_scale, mLayerVolFracWat/) - T = brent(H_snow,-200._rkind,Tfreeze, T, 1.e-6_rkind, 0, vec) + vec(1:3) = (/mLayerEnthalpy, snowfrz_scale, mLayerVolFracWat/) + T = brent(diff_H_snow,200._rkind,Tfreeze, T, 1.e-6_rkind, 0, vec) ! compute Jacobian terms if(computJac)then @@ -1123,7 +1123,7 @@ subroutine enthalpy2T_soil(& real(rkind) :: integral_frz_upp ! upper limit of integral of frozen soil water content (from Tfreeze to soil temperature) real(rkind) :: arg ! argument of soil hypergeometric function real(rkind) :: gauss_hg_T ! soil hypergeometric function result - real(rkind) :: vec(8) ! vector of parameters for the enthalpy function + real(rkind) :: vec(9) ! vector of parameters for the enthalpy function ! variable derivatives real(rkind) :: dvolFracWat_dPsi0 ! derivative of the soil water content w.r.t. matric head real(rkind) :: dintegral_unf_dWat ! derivative of integral of unfrozen soil water content with water content @@ -1197,8 +1197,8 @@ subroutine enthalpy2T_soil(& ! find the root of the function ! inputs = function, lower bound, upper bound, initial point, tolerance, integer flag if want detail ! and the vector of parameters - vec(1:8) = (/soil_dens_intr, vGn_alpha, vGn_n, theta_sat, theta_res, vGn_m, integral_frz_low, mLayerMatricHead/) - T = brent(H_soil,-200._rkind,Tcrit, T, 1.e-6_rkind, 0, vec, use_lookup, lookup_data, ixControlIndex) + vec(1:9) = (/mLayerEnthalpy, soil_dens_intr, vGn_alpha, vGn_n, theta_sat, theta_res, vGn_m, integral_frz_low, mLayerMatricHead/) + T = brent(diff_H_soil,200._rkind,Tcrit, T, 1.e-6_rkind, 0, vec, use_lookup, lookup_data, ixControlIndex) ! compute Jacobian terms if(computJac)then @@ -1310,7 +1310,7 @@ function brent (fun, x1, x2, x0, tol, detail, vec, use_lookup, lookup_data, ixCo implicit none real(rkind) :: brent integer, parameter :: d = rkind - real(rkind), intent(IN) :: x1, x2, x0, tol, vec(8) + real(rkind), intent(IN) :: x1, x2, x0, tol, vec(9) real(rkind), external :: fun integer, intent(IN) :: detail logical(lgt), intent(in), optional :: use_lookup @@ -1348,7 +1348,7 @@ function brent (fun, x1, x2, x0, tol, detail, vec, use_lookup, lookup_data, ixCo ! check sign if ( (fa>0. .and. fb>0. ) .or. (fa>0. .and. fb>0. )) then write(*,*) 'Error (brent.f90): Root must be bracked by two inputs' - write(*, "('f(x1) = ', 1F8.4, ' f(x2) = ', 1F8.4)") fa,fb + write(*, "(' x1 = ', 1F8.4, ' x2 = ', 1F8.4, ' f(x1) = ', 1F8.4, ' f(x2) = ', 1F8.4)") a,b,fa,fb write(*,*) 'press any key to halt the program' read(*,*) stop @@ -1461,68 +1461,71 @@ end function brent !---------------------------------------------------------------------- ! private functions for temperature to enthalpy conversion for Brent's method !---------------------------------------------------------------------- - function H_veg ( scalarCanopyTemp, vec) + function diff_H_veg ( scalarCanopyTemp, vec) USE snow_utils_module,only:fracliquid ! compute volumetric fraction of liquid water implicit none - real(rkind) :: H_veg + real(rkind) :: diff_H_veg real(rkind) , intent(IN) :: scalarCanopyTemp, vec(8) - real(rkind) :: scalarCanopyEnthTemp, scalarCanopyWat, scalarCanopyIce + real(rkind) :: scalarCanopyEnthalpy, scalarCanopyEnthTemp, scalarCanopyWat, scalarCanopyIce real(rkind) :: canopyDepth, specificHeatVeg, maxMassVegetation, snowfrz_scale, fLiq integer(i4b) :: err character(256) :: cmessage - canopyDepth = vec(1) - specificHeatVeg = vec(2) - maxMassVegetation = vec(3) - snowfrz_scale = vec(4) - scalarCanopyWat = vec(5) + scalarCanopyEnthalpy = vec(1) + canopyDepth = vec(2) + specificHeatVeg = vec(3) + maxMassVegetation = vec(4) + snowfrz_scale = vec(5) + scalarCanopyWat = vec(6) call T2enthTemp_veg(canopyDepth, specificHeatVeg, maxMassVegetation, snowfrz_scale, scalarCanopyTemp, & scalarCanopyWat, scalarCanopyEnthTemp, err, cmessage) fLiq = fracliquid(scalarCanopyTemp, snowfrz_scale) - H_veg = scalarCanopyEnthTemp - LH_fus * scalarCanopyWat* (1._rkind - fLiq)/ canopyDepth + diff_H_veg = scalarCanopyEnthTemp - LH_fus * scalarCanopyWat* (1._rkind - fLiq)/ canopyDepth - scalarCanopyEnthalpy - end function H_veg + end function diff_H_veg !---------------------------------------------------------------------- - function H_snow ( mLayerTemp, vec) + function diff_H_snow ( mLayerTemp, vec) USE snow_utils_module,only:fracliquid ! compute volumetric fraction of liquid water implicit none - real(rkind) :: H_snow - real(rkind) , intent(IN) :: mLayerTemp, vec(8) - real(rkind) :: mLayerEnthTemp, mLayerVolFracWat, mLayerVolFracIce, snowfrz_scale, fLiq + real(rkind) :: diff_H_snow + real(rkind) , intent(IN) :: mLayerTemp, vec(9) + real(rkind) :: mLayerEnthalpy, mLayerEnthTemp, mLayerVolFracWat, mLayerVolFracIce, snowfrz_scale, fLiq integer(i4b) :: err character(256) :: cmessage - snowfrz_scale = vec(1) - mLayerVolFracWat = vec(2) + mLayerEnthalpy = vec(1) + snowfrz_scale = vec(2) + mLayerVolFracWat = vec(3) call T2enthTemp_snow(snowfrz_scale, mLayerTemp, mLayerVolFracWat, mLayerEnthTemp, err, cmessage) fLiq = fracliquid(mLayerTemp, snowfrz_scale) - H_snow = mLayerEnthTemp - iden_ice * LH_fus * (1._rkind - fLiq) + diff_H_snow = mLayerEnthTemp - iden_ice * LH_fus * (1._rkind - fLiq) - mLayerEnthalpy - end function H_snow + end function diff_H_snow !---------------------------------------------------------------------- - function H_soil ( mLayerTemp, vec, use_lookup, lookup_data, ixControlIndex) + function diff_H_soil ( mLayerTemp, vec, use_lookup, lookup_data, ixControlIndex) USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water based on matric head implicit none - real(rkind) :: H_soil - real(rkind) , intent(in) :: mLayerTemp, vec(8) + real(rkind) :: diff_H_soil + real(rkind) , intent(in) :: mLayerTemp, vec(9) logical(lgt), intent(in) :: use_lookup type(zLookup),intent(in) :: lookup_data integer(i4b), intent(in) :: ixControlIndex - real(rkind) :: mLayerEnthTemp, mLayerMatricHead, volFracWat, xConst, mLayerPsiLiq, fLiq + real(rkind) :: mLayerEnthalpy, mLayerEnthTemp, mLayerMatricHead, volFracWat, xConst, mLayerPsiLiq, fLiq real(rkind) :: soil_dens_intr, vGn_alpha, vGn_n, theta_sat, theta_res, vGn_m, integral_frz_low integer(i4b) :: err character(256) :: cmessage - soil_dens_intr = vec(1) - vGn_alpha = vec(2) - vGn_n = vec(3) - theta_sat = vec(4) - theta_res = vec(5) - vGn_m = vec(6) - integral_frz_low = vec(7) - mLayerMatricHead = vec(8) + mLayerEnthalpy = vec(1) + soil_dens_intr = vec(2) + vGn_alpha = vec(3) + vGn_n = vec(4) + theta_sat = vec(5) + theta_res = vec(6) + vGn_m = vec(7) + integral_frz_low = vec(8) + mLayerMatricHead = vec(9) call T2enthTemp_soil(use_lookup, soil_dens_intr, vGn_alpha, vGn_n, theta_sat, theta_res, vGn_m, & ixControlIndex, lookup_data, integral_frz_low, mLayerTemp, mLayerMatricHead, & @@ -1532,9 +1535,9 @@ function H_soil ( mLayerTemp, vec, use_lookup, lookup_data, ixControlIndex) xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution fLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) ! here fLiq is the total liquid fraction, not fraction of water fraction that is liquid - H_soil = mLayerEnthTemp - iden_water * LH_fus * (volFracWat - fLiq) + diff_H_soil = mLayerEnthTemp - iden_water * LH_fus * (volFracWat - fLiq) - mLayerEnthalpy - end function H_soil + end function diff_H_soil end module enthalpyTemp_module \ No newline at end of file From 03c79be68eebd7f74cd3072961e26b952aeac72e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 12 Apr 2024 14:35:17 +0900 Subject: [PATCH 1255/1472] send in specific canopyDepth since diag_data may have changed --- build/source/engine/coupled_em.f90 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index d8b43329e..cdacb9a79 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -319,8 +319,8 @@ subroutine coupled_em(& ! link canopy depth to the information in the data structure canopy: associate(& - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! scaling parameter for the snow freezing curve (K-1) - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! depth of the vegetation canopy (m) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! scaling parameter for the snow freezing curve (K-1) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! depth of the vegetation canopy (m) specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1) ,& ! specific heat of vegetation (J kg-1 K-1) maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1) & ! maximum mass of vegetation (kg m-2) ) @@ -880,7 +880,7 @@ subroutine coupled_em(& call diagn_evar(& ! input: control variables computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): canopy depth (m) + diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): canopy depth (m), send in specific value since diag_data may have changed ! input/output: data structures mpar_data, & ! intent(in): model parameters indx_data, & ! intent(in): model layer indices @@ -1138,7 +1138,7 @@ subroutine coupled_em(& if(enthalpyStateVec .or. computeEnthalpy)then ! recompute enthalpy of the canopy if changed water and ice content call T2enthTemp_veg(& ! input - canopyDepth, & ! intent(in): canopy depth (m) + diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): canopy depth (m), send in specific value since diag_data may have changed specificHeatVeg, & ! intent(in): specific heat of vegetation (J kg-1 K-1) maxMassVegetation, & ! intent(in): maximum mass of vegetation (kg m-2) snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) @@ -1148,7 +1148,7 @@ subroutine coupled_em(& diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1), & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) = diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) - LH_fus * scalarCanopyIce/ canopyDepth + diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) = diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) - LH_fus * scalarCanopyIce/ diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) endif end if ! (if computing the vegetation flux) From 712a0cb3c143fb17014c2afa4d43e97d6ce63554 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 12 Apr 2024 21:13:38 +0900 Subject: [PATCH 1256/1472] check enthalpy for consistency if ida or BE --- build/source/driver/summa_restart.f90 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build/source/driver/summa_restart.f90 b/build/source/driver/summa_restart.f90 index e1bf3b438..8b93ca40d 100644 --- a/build/source/driver/summa_restart.f90 +++ b/build/source/driver/summa_restart.f90 @@ -90,7 +90,7 @@ subroutine summa_readRestart(summa1_struc, err, message) character(LEN=256) :: cmessage ! error message of downwind routine character(LEN=256) :: restartFile ! restart file name integer(i4b) :: iGRU,iHRU ! looping variables - logical(lgt) :: enthalpyStateVec ! flag if enthalpy is a state variable (ida) + logical(lgt) :: checkEnthalpy ! flag if checking enthalpy for consistency logical(lgt) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use analytical solution ! --------------------------------------------------------------------------------------- ! associate to elements in the data structure @@ -145,9 +145,9 @@ subroutine summa_readRestart(summa1_struc, err, message) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! check initial conditions - enthalpyStateVec = .false. - use_lookup = .false. - if(ixNrgConserv .ne. closedForm .and. ixNumericalMethod==ida) enthalpyStateVec = .true. ! enthalpy as state variable + checkEnthalpy = .false. + use_lookup = .false. + if(ixNrgConserv .ne. closedForm) checkEnthalpy = .true. ! check enthalpy either for mixed form energy equation or enthalpy state variable if(ixNrgConserv==enthalpyFDlu) use_lookup = .true. ! use lookup tables for soil enthalpy instead of analytical solution call check_icond(nGRU, & ! intent(in): number of response units progStruct, & ! intent(inout): model prognostic variables @@ -155,7 +155,7 @@ subroutine summa_readRestart(summa1_struc, err, message) mparStruct, & ! intent(in): model parameters indxStruct, & ! intent(in): layer indexes lookupStruct, & ! intent(in): lookup tables - enthalpyStateVec, & ! intent(in): flag if enthalpy is the state variable + checkEnthalpy, & ! intent(in): flag if need to start with consistent enthalpy use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif From 033e3abdb675a38e0f701d2448d5faa1436b011d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 12 Apr 2024 23:15:38 +0900 Subject: [PATCH 1257/1472] snow enthalpy was incorrect --- build/source/engine/coupled_em.f90 | 16 ++++++++-------- build/source/engine/enthalpyTemp.f90 | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index cdacb9a79..0d548bce4 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -797,16 +797,16 @@ subroutine coupled_em(& mLayerEnthTemp => diag_data%var(iLookDIAG%mLayerEnthTemp)%dat ,& ! temperature component of enthalpy (J m-3) mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! enthalpy (J m-3) mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! volumetric fraction of total water in each snow layer (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! volumetric fraction of liquid water (-) mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! volumetric fraction of ice in each snow layer (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] matric head (m) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! matric head (m) ! depth-varying soil parameters - soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat ,& ! intent(in): [dp] surface layer intrinsic soil density (kg m-3) - vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat & ! intent(in): [dp(:)] soil residual volumetric water content (-) + soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat ,& ! surface layer intrinsic soil density (kg m-3) + vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! van Genutchen "m" parameter (-) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! van Genutchen "n" parameter (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! van Genutchen "alpha" parameter (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat & ! soil residual volumetric water content (-) ) ! (associate local variables with model parameters) if(nSnow>0)then diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index e0f9f2908..c403636c2 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -1026,13 +1026,13 @@ subroutine enthalpy2T_snow(& denthLiq_dT = iden_water * Cp_water * mLayerVolFracWat * fLiq denthIce_dT = iden_water * Cp_ice * mLayerVolFracWat * (1._rkind - fLiq) denthAir_dT = iden_air * Cp_air * (1._rkind - mLayerVolFracWat * ( (iden_water/iden_ice)*(1._rkind-fLiq) + fLiq ) ) - dH_dT = denthLiq_dT + denthIce_dT + denthAir_dT + iden_ice * LH_fus * dfLiq_dT * mLayerVolFracWat + dH_dT = denthLiq_dT + denthIce_dT + denthAir_dT + iden_water * LH_fus * dfLiq_dT * mLayerVolFracWat ! w.r.t. layer water content denthLiq_dWat = iden_water * Cp_water * integral denthIce_dWat = iden_water * Cp_ice * ( diffT - integral ) denthAir_dWat = -iden_air * Cp_air * ( (iden_water/iden_ice)*(diffT-integral) + integral ) - dH_dWat = dH_dT * dT_dWat + denthLiq_dWat + denthIce_dWat + denthAir_dWat - iden_ice * LH_fus * (1._rkind - fLiq) + dH_dWat = dH_dT * dT_dWat + denthLiq_dWat + denthIce_dWat + denthAir_dWat - iden_water * LH_fus * (1._rkind - fLiq) dT_dEnthalpy = 1._rkind / dH_dT dT_dWat = dH_dWat / dH_dT @@ -1500,7 +1500,7 @@ function diff_H_snow ( mLayerTemp, vec) call T2enthTemp_snow(snowfrz_scale, mLayerTemp, mLayerVolFracWat, mLayerEnthTemp, err, cmessage) fLiq = fracliquid(mLayerTemp, snowfrz_scale) - diff_H_snow = mLayerEnthTemp - iden_ice * LH_fus * (1._rkind - fLiq) - mLayerEnthalpy + diff_H_snow = mLayerEnthTemp - iden_water * LH_fus * mLayerVolFracWat * (1._rkind - fLiq) - mLayerEnthalpy end function diff_H_snow !---------------------------------------------------------------------- From 5978a607e7bfd859c927ba344da4fd4facd73848 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 13 Apr 2024 02:42:00 +0900 Subject: [PATCH 1258/1472] trigger the snow layer merge in enthalpy state vec --- build/source/engine/enthalpyTemp.f90 | 25 ++++++++++++++++--------- build/source/engine/summaSolve4ida.f90 | 10 +++++++--- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index c403636c2..f551d6693 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -910,10 +910,10 @@ subroutine enthalpy2T_veg(& ! find the root of the function ! inputs = function, lower bound, upper bound, initial point, tolerance, integer flag if want detail - ! and the vector of parameters + ! and the vector of parameters, not.snow_layers vec = 0._rkind vec(1:6) = (/scalarCanopyEnthalpy, canopyDepth, specificHeatVeg, maxMassVegetation, snowfrz_scale, scalarCanopyWat/) - T = brent(diff_H_veg,200._rkind,Tfreeze, T, 1.e-6_rkind, 0, vec) + T = brent(diff_H_veg,200._rkind,Tfreeze, T, 1.e-6_rkind, 0, vec, .false.) ! compute Jacobian terms if(computJac)then @@ -1013,10 +1013,10 @@ subroutine enthalpy2T_snow(& ! find the root of the function ! inputs = function, lower bound, upper bound, initial point, tolerance, integer flag if want detail - ! and the vector of parameters + ! and the vector of parameters, snow_layer vec = 0._rkind vec(1:3) = (/mLayerEnthalpy, snowfrz_scale, mLayerVolFracWat/) - T = brent(diff_H_snow,200._rkind,Tfreeze, T, 1.e-6_rkind, 0, vec) + T = brent(diff_H_snow,200._rkind,Tfreeze, T, 1.e-6_rkind, 0, vec, .true.) ! compute Jacobian terms if(computJac)then @@ -1196,9 +1196,9 @@ subroutine enthalpy2T_soil(& ! find the root of the function ! inputs = function, lower bound, upper bound, initial point, tolerance, integer flag if want detail - ! and the vector of parameters + ! and the vector of parameters, not.snow_layer, lookup data vec(1:9) = (/mLayerEnthalpy, soil_dens_intr, vGn_alpha, vGn_n, theta_sat, theta_res, vGn_m, integral_frz_low, mLayerMatricHead/) - T = brent(diff_H_soil,200._rkind,Tcrit, T, 1.e-6_rkind, 0, vec, use_lookup, lookup_data, ixControlIndex) + T = brent(diff_H_soil,200._rkind,Tcrit, T, 1.e-6_rkind, 0, vec, .false., use_lookup, lookup_data, ixControlIndex) ! compute Jacobian terms if(computJac)then @@ -1281,7 +1281,7 @@ end function hyp_2F1_real !---------------------------------------------------------------------- ! private function: Brent's method to find a root of a function !---------------------------------------------------------------------- -function brent (fun, x1, x2, x0, tol, detail, vec, use_lookup, lookup_data, ixControlIndex) +function brent (fun, x1, x2, x0, tol, detail, vec, snow, use_lookup, lookup_data, ixControlIndex) ! ! Description of algorithm: ! Find a root of function f(x) given intial bracketing interval [a,b] @@ -1313,6 +1313,7 @@ function brent (fun, x1, x2, x0, tol, detail, vec, use_lookup, lookup_data, ixCo real(rkind), intent(IN) :: x1, x2, x0, tol, vec(9) real(rkind), external :: fun integer, intent(IN) :: detail + logical(lgt), intent(in) :: snow logical(lgt), intent(in), optional :: use_lookup type(zLookup),intent(in), optional :: lookup_data integer(i4b), intent(in), optional :: ixControlIndex @@ -1347,8 +1348,13 @@ function brent (fun, x1, x2, x0, tol, detail, vec, use_lookup, lookup_data, ixCo ! check sign if ( (fa>0. .and. fb>0. ) .or. (fa>0. .and. fb>0. )) then - write(*,*) 'Error (brent.f90): Root must be bracked by two inputs' - write(*, "(' x1 = ', 1F8.4, ' x2 = ', 1F8.4, ' f(x1) = ', 1F8.4, ' f(x2) = ', 1F8.4)") a,b,fa,fb + if (snow) then + brent = Tfreeze+ 0.1_rkind ! need to merge layers, trigger the merge + return + endif + write(*,*) 'Error (brent.f90): Root must be bracketed by two inputs' + write(*, "(' x1 = ', 1F8.4, ' x2 = ', 1F8.4, ' f(x1) = ', 1F15.4, ' f(x2) = ', 1F15.4)") a,b,fa,fb + print*,vec write(*,*) 'press any key to halt the program' read(*,*) stop @@ -1500,6 +1506,7 @@ function diff_H_snow ( mLayerTemp, vec) call T2enthTemp_snow(snowfrz_scale, mLayerTemp, mLayerVolFracWat, mLayerEnthTemp, err, cmessage) fLiq = fracliquid(mLayerTemp, snowfrz_scale) + print*,mLayerTemp,mLayerVolFracWat * (1._rkind - fLiq)*iden_water/iden_ice,mLayerVolFracWat,mLayerEnthTemp,mLayerEnthTemp - iden_water * LH_fus * mLayerVolFracWat * (1._rkind - fLiq) diff_H_snow = mLayerEnthTemp - iden_water * LH_fus * mLayerVolFracWat * (1._rkind - fLiq) - mLayerEnthalpy end function diff_H_snow diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index b9a515485..0100c02f2 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -450,7 +450,7 @@ subroutine summaSolve4ida(& tret(1) = t0 ! initial time tretPrev = tret(1) nSteps = 0 ! initialize number of time steps taken in solver - + do while(tret(1) < dt_cur) ! call this at beginning of step to reduce root bouncing (only looking in one direction) @@ -462,7 +462,7 @@ subroutine summaSolve4ida(& eqns_data%firstFluxCall = .false. ! already called for initial eqns_data%firstSplitOper = .true. ! always true at start of dt_cur since no splitting - + ! call IDASolve, advance solver just one internal step retvalr = FIDASolve(ida_mem, dt_cur, tret, sunvec_y, sunvec_yp, IDA_ONE_STEP) ! early return if IDASolve failed @@ -477,7 +477,11 @@ subroutine summaSolve4ida(& tooMuchMelt = .false. ! loop through non-missing energy state variables in the snow domain to see if need to merge do concurrent (i=1:nSnow,ixSnowOnlyNrg(i)/=integerMissing) - if (stateVec(ixSnowOnlyNrg(i)) > Tfreeze) tooMuchMelt = .true. !need to merge + if(model_decisions(iLookDECISIONS%nrgConserv)%iDecision.ne.closedForm)then !using enthalpy as state variable + if (eqns_data%mLayerTempTrial(i) > Tfreeze) tooMuchMelt = .true. !need to merge + else + if (stateVec(ixSnowOnlyNrg(i)) > Tfreeze) tooMuchMelt = .true. !need to merge + endif enddo if(tooMuchMelt)exit From 6e7eef0e64323529732723e45a1084923733137f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 13 Apr 2024 02:44:39 +0900 Subject: [PATCH 1259/1472] remove prints --- build/source/engine/enthalpyTemp.f90 | 2 -- 1 file changed, 2 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index f551d6693..329dc8ee3 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -1354,7 +1354,6 @@ function brent (fun, x1, x2, x0, tol, detail, vec, snow, use_lookup, lookup_data endif write(*,*) 'Error (brent.f90): Root must be bracketed by two inputs' write(*, "(' x1 = ', 1F8.4, ' x2 = ', 1F8.4, ' f(x1) = ', 1F15.4, ' f(x2) = ', 1F15.4)") a,b,fa,fb - print*,vec write(*,*) 'press any key to halt the program' read(*,*) stop @@ -1506,7 +1505,6 @@ function diff_H_snow ( mLayerTemp, vec) call T2enthTemp_snow(snowfrz_scale, mLayerTemp, mLayerVolFracWat, mLayerEnthTemp, err, cmessage) fLiq = fracliquid(mLayerTemp, snowfrz_scale) - print*,mLayerTemp,mLayerVolFracWat * (1._rkind - fLiq)*iden_water/iden_ice,mLayerVolFracWat,mLayerEnthTemp,mLayerEnthTemp - iden_water * LH_fus * mLayerVolFracWat * (1._rkind - fLiq) diff_H_snow = mLayerEnthTemp - iden_water * LH_fus * mLayerVolFracWat * (1._rkind - fLiq) - mLayerEnthalpy end function diff_H_snow From 777230d925f187bddb9e9851562e097fbfebab00 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 15 Apr 2024 11:57:44 +0900 Subject: [PATCH 1260/1472] add a decision for starting with empty aquifer, defaults to full if do not set --- build/source/driver/summa_restart.f90 | 10 +++++++-- build/source/driver/summa_setup.f90 | 2 +- build/source/dshare/get_ixname.f90 | 3 ++- build/source/dshare/var_lookup.f90 | 6 ++++-- build/source/engine/coupled_em.f90 | 2 +- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/eval8summaWithPrime.f90 | 2 +- build/source/engine/mDecisions.f90 | 24 +++++++++++++++------ build/source/engine/varSubstep.f90 | 2 +- 9 files changed, 37 insertions(+), 16 deletions(-) diff --git a/build/source/driver/summa_restart.f90 b/build/source/driver/summa_restart.f90 index 8b93ca40d..9faa6fbbd 100644 --- a/build/source/driver/summa_restart.f90 +++ b/build/source/driver/summa_restart.f90 @@ -78,6 +78,10 @@ subroutine summa_readRestart(summa1_struc, err, message) closedForm, & ! use temperature enthalpyFDlu, & ! use enthalpy with lookup tables enthalpyFD ! use enthalpy with analytical solution + ! look-up values for the choice of full or empty aquifer at start + USE mDecisions_module,only:& + fullStart, & ! start with full aquifer + emptyStart ! start with empty aquifer ! --------------------------------------------------------------------------------------- ! * variables ! --------------------------------------------------------------------------------------- @@ -97,8 +101,9 @@ subroutine summa_readRestart(summa1_struc, err, message) summaVars: associate(& ! model decisions ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& !choice of numerical solver - ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& !choice of variable in energy conservation backward Euler residual + ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& !choice of variable in either energy backward Euler residual or IDA state variable spatial_gw => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& !choice of method for the spatial representation of groundwater + aquiferIni => model_decisions(iLookDECISIONS%aquiferIni)%iDecision ,& !choice of full or empty aquifer at start ! lookup table data structure lookupStruct => summa1_struc%lookupStruct , & ! x%gru(:)%hru(:)%z(:)%var(:)%lookup(:) -- lookup tables ! primary data structures (variable length vectors) @@ -227,7 +232,8 @@ subroutine summa_readRestart(summa1_struc, err, message) ! the local column aquifer storage is not used if the groundwater is basin-average ! (i.e., where multiple HRUs drain to a basin-average aquifer) case(singleBasin) - bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 1._rkind ! Start with this full, since easier to spin up by draining than filling (filling we need to wait for precip). + bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 1._rkind ! Start with this full, since easier to spin up by draining than filling (filling we need to wait for precipitation) + if (aquiferIni==emptyStart) bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 0._rkind ! If want to compare model method outputs, empty start leads to quicker equilibrium do iHRU=1,gru_struc(iGRU)%hruCount progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarAquiferStorage)%dat(1) = 0._rkind ! set to zero to be clear that there is no local aquifer storage in this configuration end do diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index 5432d874b..0abf01521 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -178,7 +178,7 @@ subroutine summa_paramSetup(summa1_struc, err, message) ! decide if computing soil enthalpy lookup tables and vegetation enthalpy lookup tables needLookup_soil = .false. - ! if need enthalpy for energy conservation residual form and not using soil enthalpy hypergeometric function + ! if need enthalpy for either energy backward Euler residual or IDA state variable and not using soil enthalpy hypergeometric function if(model_decisions(iLookDECISIONS%nrgConserv)%iDecision == enthalpyFDlu) needLookup_soil = .true. ! if using IDA and enthalpy as a state variable, need temperature-enthalpy lookup tables for soil and vegetation diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 889ed195e..11c115c2d 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -95,7 +95,8 @@ function get_ixdecisions(varName) case('subRouting' ); get_ixdecisions=iLookDECISIONS%subRouting ! choice of method for sub-grid routing case('snowDenNew' ); get_ixdecisions=iLookDECISIONS%snowDenNew ! choice of method for new snow density case('snowUnload' ); get_ixdecisions=iLookDECISIONS%snowUnload ! choice of parameterization for snow unloading from canopy - case('nrgConserv' ); get_ixdecisions=iLookDECISIONS%nrgConserv ! choice of variable in energy conservation backward Euler residual + case('nrgConserv' ); get_ixdecisions=iLookDECISIONS%nrgConserv ! choice of variable in either energy backward Euler residual or IDA state variable + case('aquiferIni' ); get_ixdecisions=iLookDECISIONS%aquiferIni ! choice of full or empty aquifer at start ! get to here if cannot find the variable case default get_ixdecisions = integerMissing diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 7ed25a1f3..554b1c84b 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -74,7 +74,9 @@ MODULE var_lookup integer(i4b) :: spatial_gw = integerMissing ! choice of method for spatial representation of groundwater integer(i4b) :: subRouting = integerMissing ! choice of method for sub-grid routing integer(i4b) :: snowDenNew = integerMissing ! choice of method for new snow density - integer(i4b) :: nrgConserv = integerMissing ! choice of variable in energy conservation backward Euler residual + integer(i4b) :: nrgConserv = integerMissing ! choice of variable in either energy backward Euler residual or IDA state variable + integer(i4b) :: aquiferIni = integerMissing ! choice of full or empty aquifer at start + endtype iLook_decision ! *********************************************************************************************************** @@ -879,7 +881,7 @@ MODULE var_lookup type(iLook_decision),public,parameter :: iLookDECISIONS=iLook_decision( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,& - 31, 32, 33, 34, 35, 36, 37, 38, 39) + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40) ! named variables: model time type(iLook_time), public,parameter :: iLookTIME =iLook_time ( 1, 2, 3, 4, 5, 6, 7) ! named variables: model forcing data diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 0d548bce4..15e07a779 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -396,7 +396,7 @@ subroutine coupled_em(& associate(& ! model decisions ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! choice of numerical solver - ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& ! choice of variable in energy conservation backward Euler residual + ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& ! choice of variable in either energy backward Euler residual or IDA state variable ! state variables in the vegetation canopy scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! canopy liquid water (kg m-2) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! canopy ice content (kg m-2) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 5c08109cc..3b528bd94 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -228,7 +228,7 @@ subroutine eval8summa(& associate(& ! model decisions ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver - ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& ! intent(in): [i4b] choice of variable in energy conservation backward Euler residual + ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& ! intent(in): [i4b] choice of variable in either energy backward Euler residual or IDA state variable ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation ! snow parameters snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 009c96b0f..0ebcf602c 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -217,7 +217,7 @@ subroutine eval8summaWithPrime(& ! -------------------------------------------------------------------------------------------------------------------------------- associate(& ! model decisions - ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& ! intent(in): [i4b] choice of variable in energy conservation backward Euler residual + ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& ! intent(in): [i4b] choice of variable in either energy backward Euler residual or IDA state variable ixRichards => model_decisions(iLookDECISIONS%f_Richards)%iDecision ,& ! intent(in): [i4b] index of the form of Richards' equation ! snow parameters snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index cc90c10c2..7de765644 100644 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -143,7 +143,7 @@ module mDecisions_module integer(i4b),parameter,public :: constDens = 311 ! Constant new snow density integer(i4b),parameter,public :: anderson = 312 ! Anderson 1976 integer(i4b),parameter,public :: hedAndPom = 313 ! Hedstrom and Pomeroy (1998), expoential increase -integer(i4b),parameter,public :: pahaut_76 = 314 ! Pahaut 1976, wind speed dependent (derived from Col de Porte, French Alps) +integer(i4b),parameter,public :: pahaut_76 = 314 ! Pahaut 1976, wind speed dependent (derived from Col de Porte, French Alps) ! look-up values for the choice of snow unloading from the canopy integer(i4b),parameter,public :: meltDripUnload = 321 ! Hedstrom and Pomeroy (1998), Storck et al 2002 (snowUnloadingCoeff & ratioDrip2Unloading) integer(i4b),parameter,public :: windUnload = 322 ! Roesch et al 2001, formulate unloading based on wind and temperature @@ -151,6 +151,9 @@ module mDecisions_module integer(i4b),parameter,public :: enthalpyFD = 323 ! use enthalpy with analytical solution integer(i4b),parameter,public :: closedForm = 324 ! use temperature integer(i4b),parameter,public :: enthalpyFDlu = 325 ! use enthalpy with lookup tables +! look-up values for the choice of choice of full or empty aquifer at start +integer(i4b),parameter,public :: fullStart = 326 ! full aquifer at start +integer(i4b),parameter,public :: emptyStart = 327 ! empty aquifer at start ! ----------------------------------------------------------------------------------------------------------- contains @@ -412,21 +415,30 @@ subroutine mDecisions(err,message) endif #endif - ! choice of variable in either energy backward Euler residual (only, mixed form of equation) or IDA state variable + ! choice of variable in either energy backward Euler residual or IDA state variable ! for backward Euler solution, enthalpyFD has better coincidence of energy conservation ! in IDA solution, enthalpyFD makes the state variables to be enthalpy and the residual is computed in enthalpy space select case(trim(model_decisions(iLookDECISIONS%nrgConserv)%cDecision)) - case('closedForm' ); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = closedForm ! use temperature - case('enthalpyFD' ); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = enthalpyFD ! use enthalpy with analytical solution - case('enthalpyFDlu'); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = enthalpyFDlu ! use enthalpy with lookup tables + case('closedForm' ); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = closedForm ! use temperature + case('enthalpyFD' ); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = enthalpyFD ! use enthalpy with analytical solution + case('enthalpyFDlu'); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = enthalpyFDlu ! use enthalpy with lookup tables case default if (trim(model_decisions(iLookDECISIONS%num_method)%cDecision)=='itertive')then model_decisions(iLookDECISIONS%nrgConserv)%iDecision = closedForm ! included for backwards compatibility else - err=10; message=trim(message)//"unknown choice of variable in energy conservation backward Euler residual [option="//trim(model_decisions(iLookDECISIONS%nrgConserv)%cDecision)//"]"; return + err=10; message=trim(message)//"unknown choice of variable in either energy backward Euler residual or IDA state variable [option="//trim(model_decisions(iLookDECISIONS%nrgConserv)%cDecision)//"]"; return endif end select + ! choice of choice of full or empty aquifer at start + ! conventionally, start with this full, since easier to spin up by draining than filling (filling we need to wait for precipitation) + ! but, if want to compare model method outputs, empty start leads to quicker equilibrium + select case(trim(model_decisions(iLookDECISIONS%aquiferIni)%cDecision)) + case('fullStart' ); model_decisions(iLookDECISIONS%aquiferIni)%iDecision = fullStart ! start with full aquifer + case('emptyStart'); model_decisions(iLookDECISIONS%aquiferIni)%iDecision = emptyStart ! start with empty aquifer + case default; model_decisions(iLookDECISIONS%aquiferIni)%iDecision = fullStart ! most users will want to start with full aquifer, make this decision on their behalf +end select + ! identify the method used to calculate flux derivatives select case(trim(model_decisions(iLookDECISIONS%fDerivMeth)%cDecision)) case('numericl'); model_decisions(iLookDECISIONS%fDerivMeth)%iDecision = numerical ! numerical diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index e9707bff3..f26027801 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -226,7 +226,7 @@ subroutine varSubstep(& ixSaturation => io_varSubstep % ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) ! model decisions ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision ,& ! intent(in): [i4b] choice of numerical solver - ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& ! intent(in): [i4b] choice of variable in energy conservation backward Euler residual + ixNrgConserv => model_decisions(iLookDECISIONS%nrgConserv)%iDecision ,& ! intent(in): [i4b] choice of variable in either energy backward Euler residual or IDA state variable ! number of layers nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers From ab4d33ac16df920af4f0cf5687b1af06e00aa490 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 15 Apr 2024 14:36:42 +0900 Subject: [PATCH 1261/1472] need to change root finding to be on enthalpy --- build/source/engine/summaSolve4ida.f90 | 29 ++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 0100c02f2..7662d7b24 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -479,6 +479,7 @@ subroutine summaSolve4ida(& do concurrent (i=1:nSnow,ixSnowOnlyNrg(i)/=integerMissing) if(model_decisions(iLookDECISIONS%nrgConserv)%iDecision.ne.closedForm)then !using enthalpy as state variable if (eqns_data%mLayerTempTrial(i) > Tfreeze) tooMuchMelt = .true. !need to merge + if (eqns_data%mLayerTempTrial(i) > Tfreeze) print*, 'merged' else if (stateVec(ixSnowOnlyNrg(i)) > Tfreeze) tooMuchMelt = .true. !need to merge endif @@ -498,7 +499,7 @@ subroutine summaSolve4ida(& eqns_data%mpar_data, & ! intent(in): model parameters eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU eqns_data%indx_data, & ! intent(in): indices defining model states and layers - model_decisions(iLookDECISIONS%nrgConserv)%iDecision.ne.closedForm, & ! intent(in): flag to indicate if we are using enthalpy as state variable + model_decisions(iLookDECISIONS%nrgConserv)%iDecision.ne.closedForm, & ! intent(in): flag to indicate if we are using enthalpy as state variable ! output: feasibility feasible, & ! intent(inout): flag to denote the feasibility of the solution ! output: error control @@ -723,6 +724,7 @@ end subroutine setSolverParams ! find_rootdir: private routine to determine which direction to look for the root, by ! determining if the variable is greater or less than the root. Need to do this to prevent ! bouncing around solution +! Note: do not need to change if using enthalpy as state variable or not ! ---------------------------------------------------------------------------------------- subroutine find_rootdir(eqns_data,rootdir) @@ -754,7 +756,7 @@ subroutine find_rootdir(eqns_data,rootdir) nState = eqns_data%nState nSnow = eqns_data%nSnow nSoil = eqns_data%nSoil - + ! initialize ind = 0 @@ -833,6 +835,7 @@ integer(c_int) function layerDisCont4ida(t, sunvec_u, sunvec_up, gout, user_data integer(i4b) :: nState ! number of states integer(i4b) :: nSnow ! number of snow layers integer(i4b) :: nSoil ! number of soil layers + logical(lgt) :: enthalpyStateVec ! flag to indicate if we are using enthalpy as state variable real(rkind) :: xPsi ! matric head at layer (m) real(rkind) :: TcSoil ! critical point when soil begins to freeze (K) @@ -846,6 +849,8 @@ integer(c_int) function layerDisCont4ida(t, sunvec_u, sunvec_up, gout, user_data nState = eqns_data%nState nSnow = eqns_data%nSnow nSoil = eqns_data%nSoil + enthalpyStateVec = eqns_data%model_decisions(iLookDECISIONS%nrgConserv)%iDecision.ne.closedForm + ! get data array from SUNDIALS vector uu(1:nState) => FN_VGetArrayPointer(sunvec_u) @@ -856,7 +861,11 @@ integer(c_int) function layerDisCont4ida(t, sunvec_u, sunvec_up, gout, user_data ! identify the critical point when vegetation begins to freeze if(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then ind = ind+1 - gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)) - Tfreeze + if(enthalpyStateVec)then + gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)) + else + gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixVegNrg)%dat(1)) - Tfreeze + end if endif if(nSnow>0)then @@ -864,7 +873,11 @@ integer(c_int) function layerDisCont4ida(t, sunvec_u, sunvec_up, gout, user_data ! identify the critical point when the snow layer begins to freeze if(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)/=integerMissing)then ind = ind+1 - gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)) - Tfreeze + if(enthalpyStateVec)then + gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)) + else + gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat(i)) - Tfreeze + end if endif end do endif @@ -882,8 +895,12 @@ integer(c_int) function layerDisCont4ida(t, sunvec_u, sunvec_up, gout, user_data ! identify the critical point when the soil layer begins to freeze if(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)/=integerMissing)then ind = ind+1 - TcSoil = crit_soilT(xPsi) - gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) - TcSoil + if(enthalpyStateVec)then + gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) + else + TcSoil = crit_soilT(xPsi) + gout(ind) = uu(eqns_data%indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat(i)) - TcSoil + end if endif end do endif From ab34c15b4f10a06fddb02dde166eec842d35d8fe Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 15 Apr 2024 14:37:48 +0900 Subject: [PATCH 1262/1472] remove print statement --- build/source/engine/summaSolve4ida.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 7662d7b24..ae174ffc3 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -479,7 +479,6 @@ subroutine summaSolve4ida(& do concurrent (i=1:nSnow,ixSnowOnlyNrg(i)/=integerMissing) if(model_decisions(iLookDECISIONS%nrgConserv)%iDecision.ne.closedForm)then !using enthalpy as state variable if (eqns_data%mLayerTempTrial(i) > Tfreeze) tooMuchMelt = .true. !need to merge - if (eqns_data%mLayerTempTrial(i) > Tfreeze) print*, 'merged' else if (stateVec(ixSnowOnlyNrg(i)) > Tfreeze) tooMuchMelt = .true. !need to merge endif From 4602138cf255f2d7f633be6cb0460743a393e2ea Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 15 Apr 2024 16:22:45 +0900 Subject: [PATCH 1263/1472] need to put snow merge condition on enthalpy as well as temperature --- build/source/engine/summaSolve4ida.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index ae174ffc3..5115d4d68 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -478,7 +478,7 @@ subroutine summaSolve4ida(& ! loop through non-missing energy state variables in the snow domain to see if need to merge do concurrent (i=1:nSnow,ixSnowOnlyNrg(i)/=integerMissing) if(model_decisions(iLookDECISIONS%nrgConserv)%iDecision.ne.closedForm)then !using enthalpy as state variable - if (eqns_data%mLayerTempTrial(i) > Tfreeze) tooMuchMelt = .true. !need to merge + if (eqns_data%mLayerTempTrial(i) > Tfreeze .or. stateVec(ixSnowOnlyNrg(i)) > 0._rkind) tooMuchMelt = .true. !need to merge else if (stateVec(ixSnowOnlyNrg(i)) > Tfreeze) tooMuchMelt = .true. !need to merge endif From 59ec2d811cc6fd0f889cf3cef78ea1fdcf11a573 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 16 Apr 2024 15:28:07 +0900 Subject: [PATCH 1264/1472] better bounds for brent function --- build/source/engine/enthalpyTemp.f90 | 198 +++++++++++++++++++++++---- 1 file changed, 168 insertions(+), 30 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 329dc8ee3..2b6c643aa 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -73,7 +73,7 @@ module enthalpyTemp_module public::enthalpy2T_snow public::enthalpy2T_soil private::hyp_2F1_real -private::brent, diff_H_veg, diff_H_snow, diff_H_soil +private::brent, brent0, diff_H_veg, diff_H_snow, diff_H_soil ! define the snow look-up table used to compute temperature based on enthalpy integer(i4b),parameter :: nlook=10001 ! number of elements in the lookup table @@ -913,7 +913,7 @@ subroutine enthalpy2T_veg(& ! and the vector of parameters, not.snow_layers vec = 0._rkind vec(1:6) = (/scalarCanopyEnthalpy, canopyDepth, specificHeatVeg, maxMassVegetation, snowfrz_scale, scalarCanopyWat/) - T = brent(diff_H_veg,200._rkind,Tfreeze, T, 1.e-6_rkind, 0, vec, .false.) + T = brent(diff_H_veg, T, 200._rkind, Tfreeze, vec, .false.) ! compute Jacobian terms if(computJac)then @@ -1016,7 +1016,7 @@ subroutine enthalpy2T_snow(& ! and the vector of parameters, snow_layer vec = 0._rkind vec(1:3) = (/mLayerEnthalpy, snowfrz_scale, mLayerVolFracWat/) - T = brent(diff_H_snow,200._rkind,Tfreeze, T, 1.e-6_rkind, 0, vec, .true.) + T = brent(diff_H_snow, T, 200._rkind, Tfreeze, vec, .true.) ! compute Jacobian terms if(computJac)then @@ -1198,7 +1198,7 @@ subroutine enthalpy2T_soil(& ! inputs = function, lower bound, upper bound, initial point, tolerance, integer flag if want detail ! and the vector of parameters, not.snow_layer, lookup data vec(1:9) = (/mLayerEnthalpy, soil_dens_intr, vGn_alpha, vGn_n, theta_sat, theta_res, vGn_m, integral_frz_low, mLayerMatricHead/) - T = brent(diff_H_soil,200._rkind,Tcrit, T, 1.e-6_rkind, 0, vec, .false., use_lookup, lookup_data, ixControlIndex) + T = brent(diff_H_soil, T, 200._rkind, Tcrit, vec, .false., use_lookup, lookup_data, ixControlIndex) ! compute Jacobian terms if(computJac)then @@ -1281,7 +1281,7 @@ end function hyp_2F1_real !---------------------------------------------------------------------- ! private function: Brent's method to find a root of a function !---------------------------------------------------------------------- -function brent (fun, x1, x2, x0, tol, detail, vec, snow, use_lookup, lookup_data, ixControlIndex) +function brent0 (fun, x1, x2, tol_x, tol_f, detail, vec, use_lookup, lookup_data, ixControlIndex) ! ! Description of algorithm: ! Find a root of function f(x) given intial bracketing interval [a,b] @@ -1300,20 +1300,20 @@ function brent (fun, x1, x2, x0, tol, detail, vec, snow, use_lookup, lookup_data ! ! Inputs: ! fun: function to be solved + ! tol_x: tolerance for x + ! tol_f: tolerance for f(x) ! x1, x2: Upper bound and lower bound for a function - ! x0: initial guess for the root - ! tol: error tolerance. - ! detail (optional): output result of iteration if detail is some value. + ! detail: output result of iteration if detail is >0 ! ! Based on zeroin.f in netlib + ! modified from fzero.f90 by Yoki Okawa, Jan 30, 2009 implicit none - real(rkind) :: brent + real(rkind) :: brent0 integer, parameter :: d = rkind - real(rkind), intent(IN) :: x1, x2, x0, tol, vec(9) + real(rkind), intent(IN) :: x1, x2, vec(9), tol_x, tol_f real(rkind), external :: fun integer, intent(IN) :: detail - logical(lgt), intent(in) :: snow logical(lgt), intent(in), optional :: use_lookup type(zLookup),intent(in), optional :: lookup_data integer(i4b), intent(in), optional :: ixControlIndex @@ -1333,26 +1333,21 @@ function brent (fun, x1, x2, x0, tol, detail, vec, snow, use_lookup, lookup_data ! intialize values a = x1 b = x2 - c = x0 + c = x2 if(present(use_lookup))then fa = fun(a, vec, use_lookup, lookup_data, ixControlIndex) fb = fun(b, vec, use_lookup, lookup_data, ixControlIndex) - fc = fun(c, vec, use_lookup, lookup_data, ixControlIndex) else fa = fun(a, vec) fb = fun(b, vec) - fc = fun(c, vec) end if + fc = fb fx1 = fa fx2 = fb ! check sign if ( (fa>0. .and. fb>0. ) .or. (fa>0. .and. fb>0. )) then - if (snow) then - brent = Tfreeze+ 0.1_rkind ! need to merge layers, trigger the merge - return - endif - write(*,*) 'Error (brent.f90): Root must be bracketed by two inputs' + write(*,*) 'Error (brent0.f90): Root must be bracketed by two inputs' write(*, "(' x1 = ', 1F8.4, ' x2 = ', 1F8.4, ' f(x1) = ', 1F15.4, ' f(x2) = ', 1F15.4)") a,b,fa,fb write(*,*) 'press any key to halt the program' read(*,*) @@ -1362,7 +1357,7 @@ function brent (fun, x1, x2, x0, tol, detail, vec, snow, use_lookup, lookup_data if (disp == 1 ) then write(*,*) 'Brents method to find a root of f(x)' write(*,*) ' ' - write(*,*) ' i x bracketsize f(x)' + write(*,*) ' i x bracketsize f(x)' end if ! main iteration @@ -1384,19 +1379,20 @@ function brent (fun, x1, x2, x0, tol, detail, vec, snow, use_lookup, lookup_data fb=fc fc= fa end if + + if (disp == 1) then + tmp = c-b + write(*,"(' ', 1I2, 3F16.6)") i, b, abs(b-c), fb + end if ! convergence check - tol1=2.0_rkind* EPS * abs(b) + 0.5_rkind*tol + tol1=2.0_rkind* EPS * abs(b) + 0.5_rkind*tol_x xm = 0.5_rkind * (c - b) - if (abs(xm) < tol1 .or. fb == 0.0_rkind ) then + if (abs(xm) < tol1 .or. abs(fb) <= tol_f ) then + print*, (c - b), tol1, fb exitflag = 1 exit end if - - if (disp == 1) then - tmp = c-b - write(*,"(' ', 1I2, 3F16.6)") i, b, abs(b-c), fb - end if ! try inverse quadratic interpolation if (abs(e) >= tol1 .and. abs(fa) > abs(fb) ) then @@ -1448,7 +1444,7 @@ function brent (fun, x1, x2, x0, tol, detail, vec, snow, use_lookup, lookup_data ! case for non convergence if (exitflag /= 1 ) then - write(*,*) 'Error (brent.f90) : convergence was not attained' + write(*,*) 'Error (brent0.f90) : convergence was not attained' write(*,*) 'Initial value:' write(*,"(4F10.5)" ) x1, x2, fx1, fx2 write(*,*) ' ' @@ -1458,10 +1454,152 @@ function brent (fun, x1, x2, x0, tol, detail, vec, snow, use_lookup, lookup_data write(*,*) 'Brents method was converged.' write(*,*) '' end if - brent = b + brent0 = b return - end function brent + end function brent0 + +!---------------------------------------------------------------------- +! private function: Find an initial guess of bracket and call brent0 +!---------------------------------------------------------------------- + function brent (fun, x0, LowerBound, UpperBound, vec, snow, use_lookup, lookup_data, ixControlIndex) + ! + ! Inputs + ! fun: function to evaluate + ! x0: Initial guess + ! LowerBound, UpperBound : Lower and upper bound of the function + + implicit none + real(rkind) :: brent + integer, parameter :: d = rkind + real(rkind), intent(IN) :: x0, vec(9) + real(rkind), external :: fun + real(rkind), intent(IN) :: LowerBound, UpperBound + logical(lgt), intent(in) :: snow + logical(lgt), intent(in), optional :: use_lookup + type(zLookup),intent(in), optional :: lookup_data + integer(i4b), intent(in), optional :: ixControlIndex + + real(rkind) :: a , b , olda, oldb, fa, fb + real(rkind), parameter :: sqrt2 = sqrt(2.0_d)! change in dx + integer, parameter :: maxiter = 40, detail = 0 + real(rkind) :: dx ! change in bracket + integer :: iter, exitflag, disp + real(rkind) :: sgn + real(rkind), parameter :: tol_x = 1.e-5_rkind, tol_f = 1.e-1_rkind + + if (snow) then + fb = fun(LowerBound, vec) + if (fb > 0.0_rkind) then + brent = Tfreeze+ 0.1_rkind ! need to merge layers, trigger the merge + return + end if + endif + + a = x0 ! lower bracket + b = x0 ! upper bracket + olda = a + oldb = b + exitflag = 0 ! flag to see we found the bracket + if(present(use_lookup))then + sgn = fun(x0, vec, use_lookup, lookup_data, ixControlIndex) ! sign of initial guess + else + sgn = fun(x0, vec) + endif + ! set disp variable + if (detail /= 0) then + disp = 1 + else + disp = 0 + end if + + if(abs(sgn) <= tol_f ) then + brent = x0 + return + end if + + ! set initial change dx + if (abs(x0)<0.00000002_rkind) then + dx = 1.0_rkind/50.0_rkind + else + dx = 1.0_rkind/50.0_rkind * x0 + end if + + if (disp == 1) then + write(*,*) 'Search for initial guess for Brents method' + write(*,*) 'find two points whose sign for f(x) is different ' + write(*,*) 'x1 searches downwards, x2 searches upwards with increasing increment' + write(*,*) ' ' + write(*,*) ' i x1 x2 f(x1) f(x2)' + end if + + + ! main loop to extend a and b + do iter = 1, maxiter + if(present(use_lookup))then + fa = fun(a, vec, use_lookup, lookup_data, ixControlIndex) + fb = fun(b, vec, use_lookup, lookup_data, ixControlIndex) + else + fa = fun(a, vec) + fb = fun(b, vec) + end if + + if (disp == 1) write(*,"(1I4,4F17.6)") iter, a, b, fa, fb + + ! check if sign of functions changed or not + if ( (sgn >= 0 ) .and. (fa <= 0) ) then ! sign of a changed + ! use a and olda as bracket + b = olda + exitflag = 1 + exit + else if ( (sgn <= 0 ) .and. (fa >= 0 ) ) then ! sign of b changed + b = olda + exitflag = 1 + exit + else if ( (sgn >= 0 ) .and. (fb <= 0 ) ) then ! sign of a changed + a = oldb + exitflag = 1 + exit + else if ( (sgn <= 0 ) .and. (fb >= 0 ) ) then ! sign of a changed + a = oldb + exitflag = 1 + exit + end if + + ! update boundary + olda = a + oldb = b + a = a - dx + b = b+ dx + dx = dx * sqrt2 + + ! boundary check + if (a < LowerBound ) a = LowerBound + if (b > UpperBound ) b = UpperBound + end do + + + if (exitflag /= 1 ) then + write(*,*) ' Error (brent2) : Proper initial value for Brents method could not be found' + write(*,*) ' Change initial guess and try again. ' + write(*,*) ' You might want to try disp = 1 option too' + write(*,*) ' i x1 x2 f(x1) f(x2)' + write(*,"(1I4,4F17.6)") iter, a, b, fa, fb + write(*,*) ' press any key to abort the program' + read(*,*) + stop + else if (disp == 1) then + write(*,*) ' Initial guess was found.' + write(*,*) '' + end if + + if(present(use_lookup))then + brent = brent0(fun, a, b, tol_x, tol_f, detail, vec, use_lookup, lookup_data, ixControlIndex) + else + brent = brent0(fun, a, b, tol_x, tol_f, detail, vec) + end if + + end function brent !---------------------------------------------------------------------- ! private functions for temperature to enthalpy conversion for Brent's method From 8de636a80a70ce9c569cc22223e75e451bc8b03f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 16 Apr 2024 15:29:00 +0900 Subject: [PATCH 1265/1472] remove print statement --- build/source/engine/enthalpyTemp.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 2b6c643aa..6e4735150 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -1389,7 +1389,6 @@ function brent0 (fun, x1, x2, tol_x, tol_f, detail, vec, use_lookup, lookup_data tol1=2.0_rkind* EPS * abs(b) + 0.5_rkind*tol_x xm = 0.5_rkind * (c - b) if (abs(xm) < tol1 .or. abs(fb) <= tol_f ) then - print*, (c - b), tol1, fb exitflag = 1 exit end if From 45fa87507e76d6578f0f9d16bed55b79118539c8 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 16 Apr 2024 15:55:26 +0900 Subject: [PATCH 1266/1472] calculate snow merge detection outside of brent --- build/source/engine/enthalpyTemp.f90 | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 6e4735150..d7f7b0ae6 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -913,7 +913,7 @@ subroutine enthalpy2T_veg(& ! and the vector of parameters, not.snow_layers vec = 0._rkind vec(1:6) = (/scalarCanopyEnthalpy, canopyDepth, specificHeatVeg, maxMassVegetation, snowfrz_scale, scalarCanopyWat/) - T = brent(diff_H_veg, T, 200._rkind, Tfreeze, vec, .false.) + T = brent(diff_H_veg, T, 200._rkind, Tfreeze, vec) ! compute Jacobian terms if(computJac)then @@ -992,6 +992,7 @@ subroutine enthalpy2T_snow(& real(rkind) :: integral ! integral of snow freezing curve real(rkind) :: fLiq ! fraction liquid real(rkind) :: vec(9) ! vector of parameters for the enthalpy function + real(rkind) :: l_bound ! lower bound for the enthalpy function ! variable derivatives real(rkind) :: dT_dEnthalpy ! derivative of temperature with enthalpy state variable real(rkind) :: dT_dWat ! derivative of temperature with water state variable @@ -1016,7 +1017,16 @@ subroutine enthalpy2T_snow(& ! and the vector of parameters, snow_layer vec = 0._rkind vec(1:3) = (/mLayerEnthalpy, snowfrz_scale, mLayerVolFracWat/) - T = brent(diff_H_snow, T, 200._rkind, Tfreeze, vec, .true.) + if(mLayerEnthalpy>0._rkind)then + T = Tfreeze+ 0.1_rkind ! need to merge layers, trigger the merge + else + l_bound = diff_H_snow(200._rkind, vec) + if (l_bound > 0._rkind) then + T = Tfreeze + 0.1_rkind ! need to merge layers, trigger the merge + else + T = brent(diff_H_snow, T, 200._rkind, Tfreeze, vec) + end if + endif ! compute Jacobian terms if(computJac)then @@ -1198,7 +1208,7 @@ subroutine enthalpy2T_soil(& ! inputs = function, lower bound, upper bound, initial point, tolerance, integer flag if want detail ! and the vector of parameters, not.snow_layer, lookup data vec(1:9) = (/mLayerEnthalpy, soil_dens_intr, vGn_alpha, vGn_n, theta_sat, theta_res, vGn_m, integral_frz_low, mLayerMatricHead/) - T = brent(diff_H_soil, T, 200._rkind, Tcrit, vec, .false., use_lookup, lookup_data, ixControlIndex) + T = brent(diff_H_soil, T, 200._rkind, Tcrit, vec, use_lookup, lookup_data, ixControlIndex) ! compute Jacobian terms if(computJac)then @@ -1461,7 +1471,7 @@ end function brent0 !---------------------------------------------------------------------- ! private function: Find an initial guess of bracket and call brent0 !---------------------------------------------------------------------- - function brent (fun, x0, LowerBound, UpperBound, vec, snow, use_lookup, lookup_data, ixControlIndex) + function brent (fun, x0, LowerBound, UpperBound, vec, use_lookup, lookup_data, ixControlIndex) ! ! Inputs ! fun: function to evaluate @@ -1474,7 +1484,6 @@ function brent (fun, x0, LowerBound, UpperBound, vec, snow, use_lookup, lookup_d real(rkind), intent(IN) :: x0, vec(9) real(rkind), external :: fun real(rkind), intent(IN) :: LowerBound, UpperBound - logical(lgt), intent(in) :: snow logical(lgt), intent(in), optional :: use_lookup type(zLookup),intent(in), optional :: lookup_data integer(i4b), intent(in), optional :: ixControlIndex @@ -1486,14 +1495,6 @@ function brent (fun, x0, LowerBound, UpperBound, vec, snow, use_lookup, lookup_d integer :: iter, exitflag, disp real(rkind) :: sgn real(rkind), parameter :: tol_x = 1.e-5_rkind, tol_f = 1.e-1_rkind - - if (snow) then - fb = fun(LowerBound, vec) - if (fb > 0.0_rkind) then - brent = Tfreeze+ 0.1_rkind ! need to merge layers, trigger the merge - return - end if - endif a = x0 ! lower bracket b = x0 ! upper bracket From e42e6651dc87da53753bcdaec293f3fb73aaadbe Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 16 Apr 2024 23:00:44 +0900 Subject: [PATCH 1267/1472] a bit more Brent speed up --- build/source/engine/enthalpyTemp.f90 | 97 ++++++++++++++-------------- 1 file changed, 49 insertions(+), 48 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index d7f7b0ae6..2ca4f562d 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -539,10 +539,17 @@ subroutine T2enthTemp_snow(& err=0; message="T2enthTemp_snow/" diffT = mLayerTemp - Tfreeze ! diffT<0._rkind because snow is frozen - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - enthLiq = iden_water * Cp_water * mLayerVolFracWat * integral - enthIce = iden_water * Cp_ice * mLayerVolFracWat * ( diffT - integral ) - enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWat * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) + + if(diffT==0._rkind)then ! only need for upper bound + enthLiq = 0._rkind + enthIce = 0._rkind + enthAir = 0._rkind + else + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + enthLiq = iden_water * Cp_water * mLayerVolFracWat * integral + enthIce = iden_water * Cp_ice * mLayerVolFracWat * ( diffT - integral ) + enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWat * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) + endif mLayerEnthTemp = enthLiq + enthIce + enthAir @@ -1291,7 +1298,7 @@ end function hyp_2F1_real !---------------------------------------------------------------------- ! private function: Brent's method to find a root of a function !---------------------------------------------------------------------- -function brent0 (fun, x1, x2, tol_x, tol_f, detail, vec, use_lookup, lookup_data, ixControlIndex) +function brent0 (fun, x1, x2, fx1, fx2, tol_x, tol_f, detail, vec, use_lookup, lookup_data, ixControlIndex) ! ! Description of algorithm: ! Find a root of function f(x) given intial bracketing interval [a,b] @@ -1321,7 +1328,7 @@ function brent0 (fun, x1, x2, tol_x, tol_f, detail, vec, use_lookup, lookup_data implicit none real(rkind) :: brent0 integer, parameter :: d = rkind - real(rkind), intent(IN) :: x1, x2, vec(9), tol_x, tol_f + real(rkind), intent(IN) :: x1, x2, fx1, fx2, vec(9), tol_x, tol_f real(rkind), external :: fun integer, intent(IN) :: detail logical(lgt), intent(in), optional :: use_lookup @@ -1329,7 +1336,7 @@ function brent0 (fun, x1, x2, tol_x, tol_f, detail, vec, use_lookup, lookup_data integer(i4b), intent(in), optional :: ixControlIndex integer :: i, exitflag, disp - real(rkind) :: a, b, c, diff,e, fa, fb, fc, p, q, r, s, tol1, xm, tmp, fx1, fx2 + real(rkind) :: a, b, c, diff,e, fa, fb, fc, p, q, r, s, tol1, xm, tmp real(rkind), parameter :: EPS = epsilon(a) integer, parameter :: imax = 100 ! maximum number of iteration @@ -1344,16 +1351,9 @@ function brent0 (fun, x1, x2, tol_x, tol_f, detail, vec, use_lookup, lookup_data a = x1 b = x2 c = x2 - if(present(use_lookup))then - fa = fun(a, vec, use_lookup, lookup_data, ixControlIndex) - fb = fun(b, vec, use_lookup, lookup_data, ixControlIndex) - else - fa = fun(a, vec) - fb = fun(b, vec) - end if - fc = fb - fx1 = fa - fx2 = fb + fa = fx1 + fb = fx2 + fc = fx2 ! check sign if ( (fa>0. .and. fb>0. ) .or. (fa>0. .and. fb>0. )) then @@ -1387,12 +1387,12 @@ function brent0 (fun, x1, x2, tol_x, tol_f, detail, vec, use_lookup, lookup_data c=a fa=fb fb=fc - fc= fa + fc=fa end if if (disp == 1) then tmp = c-b - write(*,"(' ', 1I2, 3F16.6)") i, b, abs(b-c), fb + write(*,"(' ', 1I2, 3F16.10)") i, b, abs(b-c), fb end if ! convergence check @@ -1488,19 +1488,18 @@ function brent (fun, x0, LowerBound, UpperBound, vec, use_lookup, lookup_data, i type(zLookup),intent(in), optional :: lookup_data integer(i4b), intent(in), optional :: ixControlIndex - real(rkind) :: a , b , olda, oldb, fa, fb + real(rkind) :: a , b , olda, oldb, fa, fb, folda, foldb real(rkind), parameter :: sqrt2 = sqrt(2.0_d)! change in dx integer, parameter :: maxiter = 40, detail = 0 real(rkind) :: dx ! change in bracket integer :: iter, exitflag, disp real(rkind) :: sgn - real(rkind), parameter :: tol_x = 1.e-5_rkind, tol_f = 1.e-1_rkind + real(rkind), parameter :: tol_x = 1.e-5_rkind, tol_f = 1.e0_rkind a = x0 ! lower bracket b = x0 ! upper bracket - olda = a - oldb = b exitflag = 0 ! flag to see we found the bracket + if(present(use_lookup))then sgn = fun(x0, vec, use_lookup, lookup_data, ixControlIndex) ! sign of initial guess else @@ -1512,8 +1511,10 @@ function brent (fun, x0, LowerBound, UpperBound, vec, use_lookup, lookup_data, i else disp = 0 end if + fa = sgn + fb = sgn - if(abs(sgn) <= tol_f ) then + if(abs(sgn) <= tol_f ) then ! if solution didn't change, initial guess is the solution brent = x0 return end if @@ -1531,11 +1532,24 @@ function brent (fun, x0, LowerBound, UpperBound, vec, use_lookup, lookup_data, i write(*,*) 'x1 searches downwards, x2 searches upwards with increasing increment' write(*,*) ' ' write(*,*) ' i x1 x2 f(x1) f(x2)' + write(*,"(1I4,4F17.6)") 0, a, b, fa, fb end if - ! main loop to extend a and b do iter = 1, maxiter + ! update boundary + olda = a + oldb = b + folda = fa + foldb = fb + a = a - dx + b = b + dx + dx = dx * sqrt2 + + ! boundary check + if (a < LowerBound ) a = LowerBound + if (b > UpperBound ) b = UpperBound + if(present(use_lookup))then fa = fun(a, vec, use_lookup, lookup_data, ixControlIndex) fb = fun(b, vec, use_lookup, lookup_data, ixControlIndex) @@ -1547,38 +1561,24 @@ function brent (fun, x0, LowerBound, UpperBound, vec, use_lookup, lookup_data, i if (disp == 1) write(*,"(1I4,4F17.6)") iter, a, b, fa, fb ! check if sign of functions changed or not - if ( (sgn >= 0 ) .and. (fa <= 0) ) then ! sign of a changed + if (( (sgn >= 0 ) .and. (fa <= 0) ) .or. & + ( (sgn <= 0 ) .and. (fa >= 0 ) ))then ! sign of a changed ! use a and olda as bracket b = olda + fb = folda exitflag = 1 exit - else if ( (sgn <= 0 ) .and. (fa >= 0 ) ) then ! sign of b changed - b = olda - exitflag = 1 - exit - else if ( (sgn >= 0 ) .and. (fb <= 0 ) ) then ! sign of a changed - a = oldb - exitflag = 1 - exit - else if ( (sgn <= 0 ) .and. (fb >= 0 ) ) then ! sign of a changed + else if (( (sgn >= 0 ) .and. (fb <= 0 ) ) .or. & + ( (sgn <= 0 ) .and. (fb >= 0 ) )) then ! sign of b changed a = oldb + fa = foldb exitflag = 1 exit end if - ! update boundary - olda = a - oldb = b - a = a - dx - b = b+ dx - dx = dx * sqrt2 - - ! boundary check - if (a < LowerBound ) a = LowerBound - if (b > UpperBound ) b = UpperBound end do - + ! case for non convergence if (exitflag /= 1 ) then write(*,*) ' Error (brent2) : Proper initial value for Brents method could not be found' write(*,*) ' Change initial guess and try again. ' @@ -1593,10 +1593,11 @@ function brent (fun, x0, LowerBound, UpperBound, vec, use_lookup, lookup_data, i write(*,*) '' end if + ! call brent0 if(present(use_lookup))then - brent = brent0(fun, a, b, tol_x, tol_f, detail, vec, use_lookup, lookup_data, ixControlIndex) + brent = brent0(fun, a, b, fa, fb, tol_x, tol_f, detail, vec, use_lookup, lookup_data, ixControlIndex) else - brent = brent0(fun, a, b, tol_x, tol_f, detail, vec) + brent = brent0(fun, a, b, fa, fb, tol_x, tol_f, detail, vec) end if end function brent From d16bc16701160b592715329d0b7a9ee523eb1f1c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 17 Apr 2024 14:51:04 +0900 Subject: [PATCH 1268/1472] account for missing vegetation in jacobian --- build/source/engine/computJacobWithPrime.f90 | 22 ++++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index cd3e3acb9..236d9c446 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -354,8 +354,8 @@ subroutine computJacobWithPrime(& ! if using enthalpy as a state variable, zero out usual RHS terms and add them end of the iteration loop if(enthalpyStateVec)then - dMat(ixCasNrg) = 0._rkind - dMat(ixVegNrg) = 0._rkind + if(ixCasNrg/=integerMissing) dMat(ixCasNrg) = 0._rkind + if(ixVegNrg/=integerMissing) dMat(ixVegNrg) = 0._rkind do iLayer=1,nLayers if(ixSnowSoilNrg(iLayer)/=integerMissing) dMat(ixSnowSoilNrg(iLayer)) = 0._rkind end do @@ -1061,14 +1061,18 @@ subroutine computJacobWithPrime(& ! NOTE, dMat has been set to 0 and now 1._rkind * cj is added instead ! ---------------------------------------- if(enthalpyStateVec)then - aJac(:,ixCasNrg) = aJac(:,ixCasNrg) * dCanairTemp_dEnthalpy - if(ixMatrix==ixBandMatrix) aJac(ixDiag, ixCasNrg) = aJac(ixDiag, ixCasNrg) + 1._rkind * cj - if(ixMatrix==ixFullMatrix) aJac(ixCasNrg, ixCasNrg) = aJac(ixCasNrg, ixCasNrg) + 1._rkind * cj + if(ixCasNrg/=integerMissing)then + aJac(:,ixCasNrg) = aJac(:,ixCasNrg) * dCanairTemp_dEnthalpy + if(ixMatrix==ixBandMatrix) aJac(ixDiag, ixCasNrg) = aJac(ixDiag, ixCasNrg) + 1._rkind * cj + if(ixMatrix==ixFullMatrix) aJac(ixCasNrg, ixCasNrg) = aJac(ixCasNrg, ixCasNrg) + 1._rkind * cj + endif - aJac(:,ixVegHyd) = aJac(:,ixVegHyd) + aJac(:,ixVegNrg) * dCanopyTemp_dCanWat - aJac(:,ixVegNrg) = aJac(:,ixVegNrg) * dCanopyTemp_dEnthalpy - if(ixMatrix==ixBandMatrix) aJac(ixDiag, ixVegNrg) = aJac(ixDiag, ixVegNrg) + 1._rkind * cj - if(ixMatrix==ixFullMatrix) aJac(ixVegNrg, ixVegNrg) = aJac(ixVegNrg, ixVegNrg) + 1._rkind * cj + if(ixVegNrg/=integerMissing)then + if(ixVegHyd/=integerMissing) aJac(:,ixVegHyd) = aJac(:,ixVegHyd) + aJac(:,ixVegNrg) * dCanopyTemp_dCanWat + aJac(:,ixVegNrg) = aJac(:,ixVegNrg) * dCanopyTemp_dEnthalpy + if(ixMatrix==ixBandMatrix) aJac(ixDiag, ixVegNrg) = aJac(ixDiag, ixVegNrg) + 1._rkind * cj + if(ixMatrix==ixFullMatrix) aJac(ixVegNrg, ixVegNrg) = aJac(ixVegNrg, ixVegNrg) + 1._rkind * cj + endif if(nSnowSoilNrg>0)then do iLayer=1,nLayers From 8d69cbec3daf0c5a831dcddadd1a38b0a2ea3c8c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 17 Apr 2024 17:58:41 +0900 Subject: [PATCH 1269/1472] banded enthalpy Jacobian needed different index --- build/source/engine/computJacobWithPrime.f90 | 32 +++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index 236d9c446..456109eb3 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -171,6 +171,8 @@ subroutine computJacobWithPrime(& integer(i4b) :: nrgState ! energy state variable integer(i4b) :: watState ! hydrology state variable integer(i4b) :: nState ! number of state variables + integer(i4b),allocatable :: nrgRows(:) ! indices of rows for energy column in banded matrix + integer(i4b),allocatable :: watRows(:) ! indices of rows for hydrology column in banded matrix ! indices of model layers integer(i4b) :: iLayer ! index of model layer integer(i4b) :: jLayer ! index of model layer within the full state vector (hydrology) @@ -1055,12 +1057,20 @@ subroutine computJacobWithPrime(& end select ! type of matrix ! ********************************************************************************************************************************************************* - ! ----- ! * if desired, modify to use enthalpy as a state variable instead of temperature ! NOTE, dMat has been set to 0 and now 1._rkind * cj is added instead ! ---------------------------------------- if(enthalpyStateVec)then + + allocate(watRows(nBands),nrgRows(nBands)) + do jLayer=1,nBands-1 + watRows(jLayer) = jLayer + nrgRows(jLayer) = jLayer + 1 + end do + watRows(nBands) = nBands + nrgRows(nBands) = nBands + if(ixCasNrg/=integerMissing)then aJac(:,ixCasNrg) = aJac(:,ixCasNrg) * dCanairTemp_dEnthalpy if(ixMatrix==ixBandMatrix) aJac(ixDiag, ixCasNrg) = aJac(ixDiag, ixCasNrg) + 1._rkind * cj @@ -1068,7 +1078,11 @@ subroutine computJacobWithPrime(& endif if(ixVegNrg/=integerMissing)then - if(ixVegHyd/=integerMissing) aJac(:,ixVegHyd) = aJac(:,ixVegHyd) + aJac(:,ixVegNrg) * dCanopyTemp_dCanWat + if(ixMatrix==ixBandMatrix)then + if(ixVegHyd/=integerMissing) aJac(watRows,ixVegHyd) = aJac(watRows,ixVegHyd) + aJac(nrgRows,ixVegNrg) * dCanopyTemp_dCanWat + else if(ixMatrix==ixFullMatrix)then + if(ixVegHyd/=integerMissing) aJac(:,ixVegHyd) = aJac(:,ixVegHyd) + aJac(:,ixVegNrg) * dCanopyTemp_dCanWat + endif aJac(:,ixVegNrg) = aJac(:,ixVegNrg) * dCanopyTemp_dEnthalpy if(ixMatrix==ixBandMatrix) aJac(ixDiag, ixVegNrg) = aJac(ixDiag, ixVegNrg) + 1._rkind * cj if(ixMatrix==ixFullMatrix) aJac(ixVegNrg, ixVegNrg) = aJac(ixVegNrg, ixVegNrg) + 1._rkind * cj @@ -1080,16 +1094,24 @@ subroutine computJacobWithPrime(& if(nrgState==integerMissing) cycle watState = ixSnowSoilHyd(iLayer) if(watstate/=integerMissing)then - if(iLayer<=nSnow) aJac(:,watState) = aJac(:,watState) + aJac(:,nrgState) * dTemp_dTheta(iLayer) - if(iLayer>nSnow) aJac(:,watState) = aJac(:,watState) + aJac(:,nrgState) * dTemp_dPsi0(iLayer-nSnow) + if(ixMatrix==ixBandMatrix)then + if(iLayer<=nSnow) aJac(watRows,watState) = aJac(watRows,watState) + aJac(nrgRows,nrgState) * dTemp_dTheta(iLayer) + if(iLayer>nSnow) aJac(watRows,watState) = aJac(watRows,watState) + aJac(nrgRows,nrgState) * dTemp_dPsi0(iLayer-nSnow) + else if(ixMatrix==ixFullMatrix)then + if(iLayer<=nSnow) aJac(:,watState) = aJac(:,watState) + aJac(:,nrgState) * dTemp_dTheta(iLayer) + if(iLayer>nSnow) aJac(:,watState) = aJac(:,watState) + aJac(:,nrgState) * dTemp_dPsi0(iLayer-nSnow) + endif endif aJac(:,nrgState) = aJac(:,nrgState) * dTemp_dEnthalpy(iLayer) if(ixMatrix==ixBandMatrix) aJac(ixDiag, nrgState) = aJac(ixDiag, nrgState) + 1._rkind * cj if(ixMatrix==ixFullMatrix) aJac(nrgState, nrgState) = aJac(nrgState, nrgState) + 1._rkind * cj enddo endif + else + allocate(watRows(0),nrgRows(0)) ! dummy allocation to avoid compiler warning endif - + deallocate(watRows,nrgRows) + ! print the Jacobian if(globalPrintFlag)then select case(ixMatrix) From 64969dd0ef9d3961003764d6c6c05f15bfcd26f8 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 18 Apr 2024 15:36:13 +0900 Subject: [PATCH 1270/1472] remove scaledBalance variable, decision name clarify-- now closedForm, enthalpyFormLU, or enthalpyForm --- build/source/driver/summa_restart.f90 | 24 +- build/source/driver/summa_setup.f90 | 8 +- build/source/dshare/get_ixname.f90 | 10 - build/source/dshare/popMetadat.f90 | 9 - build/source/dshare/var_lookup.f90 | 12 +- build/source/engine/computJacobWithPrime.f90 | 18 +- build/source/engine/coupled_em.f90 | 216 +++++++----------- build/source/engine/eval8summa.f90 | 56 ++--- build/source/engine/eval8summaWithPrime.f90 | 80 +++---- build/source/engine/mDecisions.f90 | 18 +- build/source/engine/summaSolve4ida.f90 | 6 +- build/source/engine/varSubstep.f90 | 28 +-- .../celia1990/summa_zDecisions.txt | 9 +- utils/plot_per_GRUMultBal.py | 2 +- utils/timeseries_to_statistics.py | 5 +- 15 files changed, 213 insertions(+), 288 deletions(-) diff --git a/build/source/driver/summa_restart.f90 b/build/source/driver/summa_restart.f90 index 9faa6fbbd..06fb11892 100644 --- a/build/source/driver/summa_restart.f90 +++ b/build/source/driver/summa_restart.f90 @@ -66,22 +66,22 @@ subroutine summa_readRestart(summa1_struc, err, message) USE globalData,only:elapsedRestart ! elapsed time to read model restart files ! model decisions USE mDecisions_module,only:& ! look-up values for the choice of method for the spatial representation of groundwater - localColumn, & ! separate groundwater representation in each local soil column - singleBasin ! single groundwater store over the entire basin + localColumn, & ! separate groundwater representation in each local soil column + singleBasin ! single groundwater store over the entire basin ! look-up values for the numerical method USE mDecisions_module,only:& - numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes - kinsol ,& ! SUNDIALS backward Euler solution using Kinsol - ida ! SUNDIALS solution using IDA + numrec, & ! home-grown backward Euler solution using free versions of Numerical recipes + kinsol, & ! SUNDIALS backward Euler solution using Kinsol + ida ! SUNDIALS solution using IDA ! look-up values for the choice of variable in energy equations (BE residual or IDA state variable) USE mDecisions_module,only:& - closedForm, & ! use temperature - enthalpyFDlu, & ! use enthalpy with lookup tables - enthalpyFD ! use enthalpy with analytical solution - ! look-up values for the choice of full or empty aquifer at start + closedForm, & ! use temperature with closed form heat capacity + enthalpyFormLU, & ! use enthalpy with soil temperature-enthalpy lookup tables + enthalpyForm ! use enthalpy with soil temperature-enthalpy analytical solution +! look-up values for the choice of full or empty aquifer at start USE mDecisions_module,only:& - fullStart, & ! start with full aquifer - emptyStart ! start with empty aquifer + fullStart, & ! start with full aquifer + emptyStart ! start with empty aquifer ! --------------------------------------------------------------------------------------- ! * variables ! --------------------------------------------------------------------------------------- @@ -153,7 +153,7 @@ subroutine summa_readRestart(summa1_struc, err, message) checkEnthalpy = .false. use_lookup = .false. if(ixNrgConserv .ne. closedForm) checkEnthalpy = .true. ! check enthalpy either for mixed form energy equation or enthalpy state variable - if(ixNrgConserv==enthalpyFDlu) use_lookup = .true. ! use lookup tables for soil enthalpy instead of analytical solution + if(ixNrgConserv==enthalpyFormLU) use_lookup = .true. ! use lookup tables for soil temperature-enthalpy instead of analytical solution call check_icond(nGRU, & ! intent(in): number of response units progStruct, & ! intent(inout): model prognostic variables diagStruct, & ! intent(inout): model diagnostic variables diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index 0abf01521..787b3a1f1 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -44,9 +44,9 @@ module summa_setup ! look-up values for the choice of variable in energy equations (BE residual or IDA state variable) USE mDecisions_module,only:& - closedForm,& ! use temperature - enthalpyFDlu,& ! use enthalpy with lookup tables - enthalpyFD ! use enthalpy with analytical solution + closedForm, & ! use temperature with closed form heat capacity + enthalpyFormLU,& ! use enthalpy with soil temperature-enthalpy lookup tables + enthalpyForm ! use enthalpy with soil temperature-enthalpy analytical solution ! named variables to define the decisions for snow layers USE mDecisions_module,only:& @@ -179,7 +179,7 @@ subroutine summa_paramSetup(summa1_struc, err, message) ! decide if computing soil enthalpy lookup tables and vegetation enthalpy lookup tables needLookup_soil = .false. ! if need enthalpy for either energy backward Euler residual or IDA state variable and not using soil enthalpy hypergeometric function - if(model_decisions(iLookDECISIONS%nrgConserv)%iDecision == enthalpyFDlu) needLookup_soil = .true. + if(model_decisions(iLookDECISIONS%nrgConserv)%iDecision == enthalpyFormLU) needLookup_soil = .true. ! if using IDA and enthalpy as a state variable, need temperature-enthalpy lookup tables for soil and vegetation ! get the maximum number of snow layers diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 11c115c2d..029798f2e 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -600,16 +600,6 @@ function get_ixDiag(varName) case('balanceSnowMass' ); get_ixDiag = iLookDIAG%balanceSnowMass ! balance of water in the snow (kg m-2 s-1) case('balanceSoilMass' ); get_ixDiag = iLookDIAG%balanceSoilMass ! balance of water in the soil (kg m-2 s-1) case('balanceAqMass' ); get_ixDiag = iLookDIAG%balanceAqMass ! balance of water in the aquifer (kg m-2 s-1) - ! scaled balances - case('scaledBalanceCasNrg' ); get_ixDiag = iLookDIAG%scaledBalanceCasNrg ! scaled balance of energy in the canopy air space (s-1) - case('scaledBalanceVegNrg' ); get_ixDiag = iLookDIAG%scaledBalanceVegNrg ! scaled balance of energy in the vegetation canopy (s-1) - case('scaledBalanceSnowNrg' ); get_ixDiag = iLookDIAG%scaledBalanceSnowNrg ! scaled balance of energy in the snow (s-1) - case('scaledBalanceSoilNrg' ); get_ixDiag = iLookDIAG%scaledBalanceSoilNrg ! scaled balance of energy in the soil (s-1) - case('scaledBalanceVegMass' ); get_ixDiag = iLookDIAG%scaledBalanceVegMass ! scaled balance of water in the vegetation canopy (s-1) - case('scaledBalanceSnowMass' ); get_ixDiag = iLookDIAG%scaledBalanceSnowMass ! scaled balance of water in the snow (s-1) - case('scaledBalanceSoilMass' ); get_ixDiag = iLookDIAG%scaledBalanceSoilMass ! scaled balance of water in the soil (s-1) - case('scaledBalanceAqMass' ); get_ixDiag = iLookDIAG%scaledBalanceAqMass ! scaled balance of water in the aquifer (s-1) - ! get to here if cannot find the variable case default get_ixDiag = integerMissing diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index ae33148db..1279b9fc3 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -454,15 +454,6 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%balanceSnowMass) = var_info('balanceSnowMass' , 'balance of water in the snow on data window' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%balanceSoilMass) = var_info('balanceSoilMass' , 'balance of water in the soil on data window' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%balanceAqMass) = var_info('balanceAqMass' , 'balance of water in the aquifer on data window' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - ! scaled balances - diag_meta(iLookDIAG%scaledBalanceCasNrg) = var_info('scaledBalanceCasNrg' , 'scaled balance of energy in the canopy air space on data window' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scaledBalanceVegNrg) = var_info('scaledBalanceVegNrg' , 'scaled balance of energy in the vegetation on data window' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scaledBalanceSnowNrg) = var_info('scaledBalanceSnowNrg' , 'scaled balance of energy in the snow on data window' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scaledBalanceSoilNrg) = var_info('scaledBalanceSoilNrg' , 'scaled balance of energy in the soil on data window' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scaledBalanceVegMass) = var_info('scaledBalanceVegMass' , 'scaled balance of water in the vegetation on data window' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scaledBalanceSnowMass) = var_info('scaledBalanceSnowMass' , 'scaled balance of water in the snow on data window' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scaledBalanceSoilMass) = var_info('scaledBalanceSoilMass' , 'scaled balance of water in the soil on data window' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scaledBalanceAqMass) = var_info('scaledBalanceAqMass' , 'scaled balance of water in the aquifer on data window' , 's-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! ----- ! * local model fluxes... ! ----------------------- diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 554b1c84b..fc4b145c9 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -481,15 +481,6 @@ MODULE var_lookup integer(i4b) :: balanceSnowMass = integerMissing ! balance of water in the snow (kg m-2 s-1) integer(i4b) :: balanceSoilMass = integerMissing ! balance of water in the soil (kg m-2 s-1) integer(i4b) :: balanceAqMass = integerMissing ! balance of water in the aquifer (kg m-2 s-1) - ! scaled balances - integer(i4b) :: scaledBalanceCasNrg = integerMissing ! scaled balance of energy in the canopy air space (s-1) - integer(i4b) :: scaledBalanceVegNrg = integerMissing ! scaled balance of energy in the vegetation (s-1) - integer(i4b) :: scaledBalanceSnowNrg = integerMissing ! scaled balance of energy in the snow (s-1) - integer(i4b) :: scaledBalanceSoilNrg = integerMissing ! scaled balance of energy in the soil (s-1) - integer(i4b) :: scaledBalanceVegMass = integerMissing ! scaled balance of water in the vegetation (s-1) - integer(i4b) :: scaledBalanceSnowMass = integerMissing ! scaled balance of water in the snow (s-1) - integer(i4b) :: scaledBalanceSoilMass = integerMissing ! scaled balance of water in the soil (s-1) - integer(i4b) :: scaledBalanceAqMass = integerMissing ! scaled balance of water in the aquifer (s-1) endtype iLook_diag ! *********************************************************************************************************** @@ -926,8 +917,7 @@ MODULE var_lookup 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,& 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,& 91, 92, 93, 94, 95, 96, 97, 98, 99,100,& - 101,102,103,104,105,106,107,108,109,110,& - 111,112) + 101,102,103,104) ! named variables: model fluxes type(iLook_flux), public,parameter :: iLookFLUX =iLook_flux ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index 456109eb3..c8d6e9937 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -78,9 +78,9 @@ module computJacobWithPrime_module ! look-up values for the choice of variable in energy equations (BE residual or IDA state variable) USE mDecisions_module,only: & - closedForm, & ! use temperature - enthalpyFDlu, & ! use enthalpy with lookup tables - enthalpyFD ! use enthalpy with analytical solution + closedForm, & ! use temperature with closed form heat capacity + enthalpyFormLU, & ! use enthalpy with soil temperature-enthalpy lookup tables + enthalpyForm ! use enthalpy with soil temperature-enthalpy analytical solution implicit none ! define constants @@ -1201,12 +1201,12 @@ integer(c_int) function computJacob4ida(t, cj, sunvec_y, sunvec_yp, sunvec_r, & eqns_data%nSoil, & ! intent(in): number of soil layers eqns_data%nLayers, & ! intent(in): total number of layers eqns_data%computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - (eqns_data%model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel), & ! intent(in): flag to indicate if we need to compute baseflow - eqns_data%ixMatrix, & ! intent(in): form of the Jacobian matrix - eqns_data%mpar_data%var(iLookPARAM%specificStorage)%dat(1) , & ! intent(in): specific storage coefficient (m-1) - eqns_data%mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): soil porosity (-) - eqns_data%model_decisions(iLookDECISIONS%f_Richards)%iDecision, & ! intent(in): choice of option for Richards' equation - (eqns_data%model_decisions(iLookDECISIONS%nrgConserv)%iDecision.ne.closedForm), & ! intent(in): flag if enthalpy is state variable + eqns_data%model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel, & ! intent(in): flag to indicate if we need to compute baseflow + eqns_data%ixMatrix, & ! intent(in): form of the Jacobian matrix + eqns_data%mpar_data%var(iLookPARAM%specificStorage)%dat(1), & ! intent(in): specific storage coefficient (m-1) + eqns_data%mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): soil porosity (-) + eqns_data%model_decisions(iLookDECISIONS%f_Richards)%iDecision, & ! intent(in): choice of option for Richards' equation + eqns_data%model_decisions(iLookDECISIONS%nrgConserv)%iDecision.ne.closedForm, & ! intent(in): flag if enthalpy is state variable ! input: data structures eqns_data%model_decisions, & ! intent(in): model decisions eqns_data%indx_data, & ! intent(in): index data diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 15e07a779..7fc598fac 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -89,10 +89,10 @@ module coupled_em_module ida ! SUNDIALS solution using IDA ! look-up values for the choice of variable in energy equations (BE residual or IDA state variable) -USE mDecisions_module,only: & - closedForm, & ! use temperature - enthalpyFDlu, & ! use enthalpy with lookup tables - enthalpyFD ! use enthalpy with analytical solution +USE mDecisions_module,only: & + closedForm, & ! use temperature with closed form heat capacity + enthalpyFormLU, & ! use enthalpy with soil temperature-enthalpy lookup tables + enthalpyForm ! use enthalpy with soil temperature-enthalpy analytical solution ! privacy @@ -229,7 +229,6 @@ subroutine coupled_em(& real(rkind),allocatable :: mLayerVolFracIceInit(:)! initial vector for volumetric fraction of ice (-) ! check SWE real(rkind) :: oldSWE ! SWE at the start of the substep - real(rkind) :: newSWE ! SWE at the end of the substep real(rkind) :: delSWE ! change in SWE over the subtep real(rkind) :: innerEffRainfall ! inner step average effective rainfall into snow (kg m-2 s-1) real(rkind) :: effRainfall ! timestep-average effective rainfall into snow (kg m-2 s-1) @@ -258,9 +257,7 @@ subroutine coupled_em(& real(rkind) :: scalarInitCanopyLiq ! initial liquid water on the vegetation canopy (kg m-2) real(rkind) :: scalarInitCanopyIce ! initial ice on the vegetation canopy (kg m-2) real(rkind) :: balanceCanopyWater0 ! total water stored in the vegetation canopy at the start of the step (kg m-2) - real(rkind) :: balanceCanopyWater1 ! total water stored in the vegetation canopy at the end of the step (kg m-2) real(rkind) :: balanceSoilWater0 ! total soil storage at the start of the step (kg m-2) - real(rkind) :: balanceSoilWater1 ! total soil storage at the end of the step (kg m-2) real(rkind) :: balanceSoilInflux ! input to the soil zone real(rkind) :: balanceSoilBaseflow ! output from the soil zone real(rkind) :: balanceSoilDrainage ! output from the soil zone @@ -269,7 +266,6 @@ subroutine coupled_em(& real(rkind) :: balanceAquifer1 ! total aquifer storage at the end of the step (kg m-2) real(rkind) :: innerBalance(4) ! inner step balances for domain with one layer real(rkind) :: meanBalance(8) ! timestep-average balances for domains - real(rkind) :: divisor(8) ! divisor for scaled the balances real(rkind),allocatable :: innerBalanceLayerMass(:) ! inner step balances for domain with multiple layers real(rkind),allocatable :: innerBalanceLayerNrg(:) ! inner step balances for domain with multiple layers ! test balance checks @@ -427,7 +423,7 @@ subroutine coupled_em(& else ! enthalpy state variable only implemented for IDA, energy conserved in IDA without using enthTemp if(ixNrgConserv.ne.closedForm) enthalpyStateVec = .true. ! enthalpy as state variable endif - if(ixNrgConserv==enthalpyFDlu) use_lookup = .true. ! use lookup tables for soil enthalpy instead of analytical solution + if(ixNrgConserv==enthalpyFormLU) use_lookup = .true. ! use lookup tables for soil temperature-enthalpy instead of analytical solution ! save the liquid water and ice on the vegetation canopy scalarInitCanopyLiq = scalarCanopyLiq ! initial liquid water on the vegetation canopy (kg m-2) @@ -1406,7 +1402,7 @@ subroutine coupled_em(& ! *********************************************************************************************************************************** ! --- - ! *** balance checks... + ! *** balance checks and summary variable saving... ! --------------------- ! save the average compression and melt pond storage in the data structures @@ -1434,26 +1430,27 @@ subroutine coupled_em(& averageSoilCompress => diag_data%var( iLookDIAG%scalarSoilCompress) %dat(1) ,& ! soil compression (kg m-2 s-1) averageGroundEvaporation => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarGroundEvaporation) )%dat(1) ,& ! soil evaporation (kg m-2 s-1) averageCanopyTranspiration => flux_mean%var(childFLUX_MEAN(iLookFLUX%scalarCanopyTranspiration))%dat(1) ,& ! canopy transpiration (kg m-2 s-1) - ! state variables in the atmosphere - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! total enthalpy in the atmospheric column (J m-3) ! state variables in the vegetation canopy scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! canopy ice content (kg m-2) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! total enthalpy in the canopy (J m-3) - ! state variables in the snow domain + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! ice content of the vegetation canopy (kg m-2) + scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! temperature component of enthalpy of the vegetation canopy (K) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! enthalpy of the vegetation canopy (J m-3) + ! state variables in the snow+soil domains scalarSWE => prog_data%var(iLookPROG%scalarSWE)%dat(1) ,& ! snow water equivalent (kg m-2) - scalarTotalSnowEnthalpy => diag_data%var(iLookDIAG%scalarTotalSnowEnthalpy)%dat(1) ,& ! total enthalpy in the snow column (J m-3) - ! state variables in the soil domain - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers) ,& ! depth of each soil layer (m) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat(nSnow+1:nLayers) ,& ! volumetric ice content in each soil layer (-) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(nSnow+1:nLayers) ,& ! volumetric liquid water content in each soil layer (-) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! depth of each layer (m) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! volumetric ice content in each layer (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! volumetric liquid water content in each layer (-) scalarTotalSoilWat => diag_data%var(iLookDIAG%scalarTotalSoilWat)%dat(1) ,& ! total water in the soil column (kg m-2) - scalarTotalSoilEnthalpy => diag_data%var(iLookDIAG%scalarTotalSoilEnthalpy)%dat(1) ,& ! total enthalpy in the soil column (J m-3) + scalarTotalSoilIce => diag_data%var(iLookDIAG%scalarTotalSoilIce)%dat(1) ,& ! total ice in the soil column (kg m-2) + scalarTotalSoilLiq => diag_data%var(iLookDIAG%scalarTotalSoilLiq)%dat(1) ,& ! total liquid water in the soil column (kg m-2) + mLayerEnthTemp => diag_data%var(iLookDIAG%mLayerEnthTemp)%dat ,& ! temperature component of enthalpy of each snow+soil layer (K) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! enthalpy of each snow+soil layer (J m-3) + scalarTotalSoilEnthalpy => diag_data%var(iLookDIAG%scalarTotalSoilEnthalpy)%dat(1) ,& ! total enthalpy of the soil column (J m-3) + scalarTotalSnowEnthalpy => diag_data%var(iLookDIAG%scalarTotalSnowEnthalpy)%dat(1) ,& ! total enthalpy of the snow column (J m-3) ! state variables in the aquifer scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! aquifer storage (m) ! error tolerance - absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1) ,& ! absolute convergence tolerance for vol frac liq water (-) - scalarTotalSoilIce => diag_data%var(iLookDIAG%scalarTotalSoilIce)%dat(1) ,& ! total ice in the soil column (kg m-2) - scalarTotalSoilLiq => diag_data%var(iLookDIAG%scalarTotalSoilLiq)%dat(1) & ! total liquid water in the soil column (kg m-2) + absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1) & ! absolute convergence tolerance for vol frac liq water (-) ) ! (association of local variables with information in the data structures ! save balance of energy and water per single layer domain @@ -1465,26 +1462,6 @@ subroutine coupled_em(& diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = meanBalance(6) ! W m-3 diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = meanBalance(7) ! kg m-2 s-1 diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = meanBalance(8) ! kg m-2 s-1 - - ! scale balance of energy and water per single layer domain by the scalar variable - divisor(1) = scalarCanairEnthalpy ! J m-3 - divisor(2) = scalarCanopyEnthalpy ! J m-3 - divisor(3) = scalarCanopyWat ! kg m-2 - divisor(4) = scalarAquiferStorage * iden_water ! kg m-2 - divisor(5) = scalarTotalSnowEnthalpy ! J m-3 - divisor(6) = scalarTotalSoilEnthalpy ! J m-3 - divisor(7) = scalarSWE ! kg m-2 - divisor(8) = scalarTotalSoilWat ! kg m-2 - meanBalance = merge( realMissing, meanBalance / divisor, divisor == 0._rkind) - - diag_data%var(iLookDIAG%scaledBalanceCasNrg)%dat(1) = meanBalance(1) ! s-1 - diag_data%var(iLookDIAG%scaledBalanceVegNrg)%dat(1) = meanBalance(2) ! s-1 - diag_data%var(iLookDIAG%scaledBalanceVegMass)%dat(1) = meanBalance(3) ! s-1 - diag_data%var(iLookDIAG%scaledBalanceAqMass)%dat(1) = meanBalance(4) ! s-1 - diag_data%var(iLookDIAG%scaledBalanceSnowNrg)%dat(1) = meanBalance(5) ! s-1 - diag_data%var(iLookDIAG%scaledBalanceSoilNrg)%dat(1) = meanBalance(6) ! s-1 - diag_data%var(iLookDIAG%scaledBalanceSnowMass)%dat(1) = meanBalance(7) ! s-1 - diag_data%var(iLookDIAG%scaledBalanceSoilMass)%dat(1) = meanBalance(8) ! s-1 ! ----- ! * balance checks for the canopy... @@ -1492,17 +1469,14 @@ subroutine coupled_em(& ! if computing the vegetation flux if(computeVegFlux)then - ! get the canopy water balance at the end of the time step - balanceCanopyWater1 = scalarCanopyWat - ! balance checks for the canopy ! NOTE: need to put the balance checks in the sub-step loop so that we can recompute if necessary - scalarCanopyWatBalError = balanceCanopyWater1 - (balanceCanopyWater0 + (scalarSnowfall - averageThroughfallSnow)*data_step + (scalarRainfall - averageThroughfallRain)*data_step & + scalarCanopyWatBalError = scalarCanopyWat - (balanceCanopyWater0 + (scalarSnowfall - averageThroughfallSnow)*data_step + (scalarRainfall - averageThroughfallRain)*data_step & - averageCanopySnowUnloading*data_step - averageCanopyLiqDrainage*data_step + averageCanopySublimation*data_step + averageCanopyEvaporation*data_step) if(abs(scalarCanopyWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance_ds)then write(*,'(a,1x,f20.10)') 'data_step = ', data_step write(*,'(a,1x,f20.10)') 'balanceCanopyWater0 = ', balanceCanopyWater0 - write(*,'(a,1x,f20.10)') 'balanceCanopyWater1 = ', balanceCanopyWater1 + write(*,'(a,1x,f20.10)') 'balanceCanopyWater1 = ', scalarCanopyWat write(*,'(a,1x,f20.10)') 'snowfall = ', scalarSnowfall*data_step write(*,'(a,1x,f20.10)') 'rainfall = ', scalarRainfall*data_step write(*,'(a,1x,f20.10)') '(snowfall - throughfallSnow) = ', (scalarSnowfall - averageThroughfallSnow)*data_step @@ -1515,7 +1489,6 @@ subroutine coupled_em(& message=trim(message)//'canopy hydrology does not balance' err=20; return end if - endif ! if computing the vegetation flux ! ----- @@ -1525,10 +1498,10 @@ subroutine coupled_em(& ! check the individual layers if(printBalance .and. nSnow>0)then write(*,'(a,1x,10(f12.8,1x))') 'liqSnowInit = ', liqSnowInit - write(*,'(a,1x,10(f12.8,1x))') 'volFracLiq = ', prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow) + write(*,'(a,1x,10(f12.8,1x))') 'volFracLiq = ', mLayerVolFracLiq(1:nSnow) write(*,'(a,1x,10(f12.8,1x))') 'iLayerLiqFluxSnow = ', flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat*iden_water*data_step write(*,'(a,1x,10(f12.8,1x))') 'mLayerLiqFluxSnow = ', flux_data%var(iLookFLUX%mLayerLiqFluxSnow)%dat*data_step - write(*,'(a,1x,10(f12.8,1x))') 'change volFracLiq = ', prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow) - liqSnowInit + write(*,'(a,1x,10(f12.8,1x))') 'change volFracLiq = ', mLayerVolFracLiq(1:nSnow) - liqSnowInit deallocate(liqSnowInit, stat=err) if(err/=0)then message=trim(message)//'unable to deallocate space for the initial volumetric liquid water content of snow' @@ -1540,15 +1513,14 @@ subroutine coupled_em(& if(nSnow>0)then effSnowfall = averageThroughfallSnow + averageCanopySnowUnloading ! effRainfall is averageThroughfallRain + averageCanopyLiqDrainage only over snow - newSWE = prog_data%var(iLookPROG%scalarSWE)%dat(1) - delSWE = newSWE - (oldSWE - sfcMeltPond) + delSWE = scalarSWE - (oldSWE - sfcMeltPond) massBalance = delSWE - (effSnowfall + effRainfall + averageSnowSublimation - averageSnowDrainage*iden_water)*data_step if(abs(massBalance) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance_ds)then print*, 'nSnow = ', nSnow print*, 'nSub = ', nSub write(*,'(a,1x,f20.10)') 'data_step = ', data_step write(*,'(a,1x,f20.10)') 'oldSWE = ', oldSWE - write(*,'(a,1x,f20.10)') 'newSWE = ', newSWE + write(*,'(a,1x,f20.10)') 'newSWE = ', scalarSWE write(*,'(a,1x,f20.10)') 'delSWE = ', delSWE write(*,'(a,1x,f20.10)') 'effRainfall = ', effRainfall*data_step write(*,'(a,1x,f20.10)') 'effSnowfall = ', effSnowfall*data_step @@ -1566,11 +1538,11 @@ subroutine coupled_em(& ! ---------------------------- ! compute the liquid water and ice content at the end of the time step - scalarTotalSoilLiq = sum(iden_water*mLayerVolFracLiq(1:nSoil)*mLayerDepth(1:nSoil)) - scalarTotalSoilIce = sum(iden_water*mLayerVolFracIce(1:nSoil)*mLayerDepth(1:nSoil)) ! NOTE: no expansion of soil, hence use iden_water + scalarTotalSoilLiq = sum(iden_water*mLayerVolFracLiq(nSnow+1:nLayers)*mLayerDepth(nSnow+1:nLayers)) + scalarTotalSoilIce = sum(iden_water*mLayerVolFracIce(nSnow+1:nLayers)*mLayerDepth(nSnow+1:nLayers)) ! NOTE: no expansion of soil, hence use iden_water ! get the total water in the soil (liquid plus ice) at the end of the time step (kg m-2) - balanceSoilWater1 = scalarTotalSoilLiq + scalarTotalSoilIce + scalarTotalSoilWat = scalarTotalSoilLiq + scalarTotalSoilIce ! get the total aquifer storage at the start of the time step (kg m-2) balanceAquifer1 = scalarAquiferStorage*iden_water @@ -1585,10 +1557,10 @@ subroutine coupled_em(& ! check the individual layers if(printBalance)then write(*,'(a,1x,10(f12.8,1x))') 'liqSoilInit = ', liqSoilInit - write(*,'(a,1x,10(f12.8,1x))') 'volFracLiq = ', mLayerVolFracLiq + write(*,'(a,1x,10(f12.8,1x))') 'volFracLiq = ', mLayerVolFracLiq(nSnow+1:nLayers) write(*,'(a,1x,10(f12.8,1x))') 'iLayerLiqFluxSoil = ', flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat*iden_water*data_step write(*,'(a,1x,10(f12.8,1x))') 'mLayerLiqFluxSoil = ', flux_data%var(iLookFLUX%mLayerLiqFluxSoil)%dat*data_step - write(*,'(a,1x,10(f12.8,1x))') 'change volFracLiq = ', mLayerVolFracLiq - liqSoilInit + write(*,'(a,1x,10(f12.8,1x))') 'change volFracLiq = ', mLayerVolFracLiq(nSnow+1:nLayers) - liqSoilInit deallocate(liqSoilInit, stat=err) if(err/=0)then message=trim(message)//'unable to deallocate space for the initial soil moisture' @@ -1597,7 +1569,7 @@ subroutine coupled_em(& endif ! check the soil water balance - scalarSoilWatBalError = balanceSoilWater1 - (balanceSoilWater0 + (balanceSoilInflux + balanceSoilET - balanceSoilBaseflow - balanceSoilDrainage - balanceSoilCompress) ) + scalarSoilWatBalError = scalarTotalSoilWat - (balanceSoilWater0 + (balanceSoilInflux + balanceSoilET - balanceSoilBaseflow - balanceSoilDrainage - balanceSoilCompress) ) if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance_ds)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues write(*,*) 'solution method = ', ixSolution write(*,'(a,1x,f20.10)') 'data_step = ', data_step @@ -1605,7 +1577,7 @@ subroutine coupled_em(& write(*,'(a,1x,f20.10)') 'scalarTotalSoilLiq = ', scalarTotalSoilLiq write(*,'(a,1x,f20.10)') 'scalarTotalSoilIce = ', scalarTotalSoilIce write(*,'(a,1x,f20.10)') 'balanceSoilWater0 = ', balanceSoilWater0 - write(*,'(a,1x,f20.10)') 'balanceSoilWater1 = ', balanceSoilWater1 + write(*,'(a,1x,f20.10)') 'balanceSoilWater1 = ', scalarTotalSoilWat write(*,'(a,1x,f20.10)') 'balanceSoilInflux = ', balanceSoilInflux write(*,'(a,1x,f20.10)') 'balanceSoilBaseflow = ', balanceSoilBaseflow write(*,'(a,1x,f20.10)') 'balanceSoilDrainage = ', balanceSoilDrainage @@ -1615,79 +1587,62 @@ subroutine coupled_em(& err=20; return end if - ! end association of local variables with information in the data structures - end associate + ! ----- + ! save the enthalpy or temperature component of enthalpy, and total enthalpy + ! ---------------------------- - ! end association to canopy parameters - end associate canopy - - ! save the total soil water (Liquid+Ice) - diag_data%var(iLookDIAG%scalarTotalSoilWat)%dat(1) = balanceSoilWater1 - - ! save the enthalpy or temperature component of enthalpy, and total enthalpy - ! associate local variables with information in the data structures - associate(& - ! canopy enthalpy - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! ice content of the vegetation canopy (kg m-2) - scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! temperature component of enthalpy of the vegetation canopy (K) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! enthalpy of the vegetation canopy (J m-3) - ! snow+soil enthalpy - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! volumetric ice content in each snow+soil layer (-) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! depth of each snow+soil layer (m) - mLayerEnthTemp => diag_data%var(iLookDIAG%mLayerEnthTemp)%dat ,& ! temperature component of enthalpy of each snow+soil layer (K) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! enthalpy of each snow+soil layer (J m-3) - scalarTotalSoilEnthalpy => diag_data%var(iLookDIAG%scalarTotalSoilEnthalpy)%dat(1) ,& ! total enthalpy of the soil column (J m-3) - scalarTotalSnowEnthalpy => diag_data%var(iLookDIAG%scalarTotalSnowEnthalpy)%dat(1) & ! total enthalpy of the snow column (J m-3) - ) ! (association of local variables with information in the data structures + if(computeEnthalpy)then ! use enthTemp to conserve energy or compute energy balance + ! initialize the enthalpy + scalarCanopyEnthalpy = scalarCanopyEnthTemp + mLayerEnthalpy = mLayerEnthTemp + ! compute enthalpy for current values + call enthTemp_or_enthalpy(& + ! input: data structures + .true., & ! intent(in): flag to convert enthTemp to enthalpy + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): model indices + ! input: ice content change + scalarCanopyIce, & ! intent(in): value for canopy ice content (kg m-2) + mLayerVolFracIce, & ! intent(in): vector of volumetric ice water content (-) + ! input/output: enthalpy + scalarCanopyEnthalpy, & ! intent(inout): enthTemp to enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy, & ! intent(inout): enthTemp to enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + end if - if(computeEnthalpy)then ! use enthTemp to conserve energy or compute energy balance - ! initialize the enthalpy - scalarCanopyEnthalpy = scalarCanopyEnthTemp - mLayerEnthalpy = mLayerEnthTemp - ! compute enthalpy for current values - call enthTemp_or_enthalpy(& - ! input: data structures - .true., & ! intent(in): flag to convert enthTemp to enthalpy - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): model indices - ! input: ice content change - scalarCanopyIce, & ! intent(in): value for canopy ice content (kg m-2) - mLayerVolFracIce, & ! intent(in): vector of volumetric ice water content (-) - ! input/output: enthalpy - scalarCanopyEnthalpy, & ! intent(inout): enthTemp to enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy, & ! intent(inout): enthTemp to enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - end if + if(enthalpyStateVec)then ! enthalpy as state variable + ! initialize the temperature component of enthalpy + scalarCanopyEnthTemp = scalarCanopyEnthalpy + mLayerEnthTemp = mLayerEnthalpy + ! compute temperature component of enthalpy for current values + call enthTemp_or_enthalpy(& + ! input: data structures + .false., & ! intent(in): flag to convert enthalpy to enthTemp + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): model indices + ! input: ice content change + scalarCanopyIce, & ! intent(in): value for canopy ice content (kg m-2) + mLayerVolFracIce, & ! intent(in): vector of volumetric ice water content (-) + ! input/output: enthalpy + scalarCanopyEnthTemp, & ! intent(inout): enthalpy to enthTemp of the vegetation canopy (J m-3) + mLayerEnthTemp, & ! intent(inout): enthalpy to enthTemp of each snow+soil layer (J m-3) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + endif + ! save the total soil enthalpy + scalarTotalSoilEnthalpy = sum(mLayerEnthalpy(nSnow+1:nLayers) * mLayerDepth(nSnow+1:nLayers))/sum(mLayerDepth(nSnow+1:nLayers)) + ! save the total snow enthalpy + scalarTotalSnowEnthalpy = sum(mLayerEnthalpy(1:nSnow) * mLayerDepth(1:nSnow))/sum(mLayerDepth(1:nSnow)) - if(enthalpyStateVec)then ! enthalpy as state variable - ! initialize the temperature component of enthalpy - scalarCanopyEnthTemp = scalarCanopyEnthalpy - mLayerEnthTemp = mLayerEnthalpy - call enthTemp_or_enthalpy(& - ! input: data structures - .false., & ! intent(in): flag to convert enthalpy to enthTemp - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): model indices - ! input: ice content change - scalarCanopyIce, & ! intent(in): value for canopy ice content (kg m-2) - mLayerVolFracIce, & ! intent(in): vector of volumetric ice water content (-) - ! input/output: enthalpy - scalarCanopyEnthTemp, & ! intent(inout): enthTemp to enthalpy of the vegetation canopy (J m-3) - mLayerEnthTemp, & ! intent(inout): enthTemp to enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - endif - ! save the total soil enthalpy - scalarTotalSoilEnthalpy = sum(mLayerEnthalpy(nSnow+1:nLayers) * mLayerDepth(nSnow+1:nLayers))/sum(mLayerDepth(nSnow+1:nLayers)) - ! save the total snow enthalpy - scalarTotalSnowEnthalpy = sum(mLayerEnthalpy(1:nSnow) * mLayerDepth(1:nSnow))/sum(mLayerDepth(1:nSnow)) - end associate + ! save the surface temperature (just to make things easier to visualize) + prog_data%var(iLookPROG%scalarSurfaceTemp)%dat(1) = prog_data%var(iLookPROG%mLayerTemp)%dat(1) + + end associate ! end association of local variables with information in the data structures - ! save the surface temperature (just to make things easier to visualize) - prog_data%var(iLookPROG%scalarSurfaceTemp)%dat(1) = prog_data%var(iLookPROG%mLayerTemp)%dat(1) + end associate canopy ! end association to canopy parameters ! overwrite flux data with timestep-average value for all flux_mean vars, hard-coded to not happen if(.not.backwardsCompatibility)then @@ -1696,7 +1651,6 @@ subroutine coupled_em(& end do end if - iLayer = nSnow+1 if(nsub>50000)then write(message,'(a,i0)') trim(cmessage)//'number of sub-steps > 50000 for HRU ', hruId err=20; return diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 3b528bd94..34799af7d 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -62,9 +62,9 @@ module eval8summa_module ! look-up values for the choice of variable in energy equations (BE residual or IDA state variable) USE mDecisions_module,only: & - closedForm, & ! use temperature - enthalpyFDlu, & ! use enthalpy with lookup tables - enthalpyFD ! use enthalpy with analytical solution + closedForm, & ! use temperature with closed form heat capacity + enthalpyFormLU, & ! use enthalpy with soil temperature-enthalpy lookup tables + enthalpyForm ! use enthalpy with soil temperature-enthalpy analytical solution ! look-up values for the numerical method USE mDecisions_module,only: & @@ -317,7 +317,7 @@ subroutine eval8summa(& end if end if ! ( feasibility check ) - if(ixNrgConserv == enthalpyFD .or. ixNrgConserv == enthalpyFDlu)then + if(ixNrgConserv == enthalpyForm .or. ixNrgConserv == enthalpyFormLU)then ! use mixed form of energy equation, need these true to use for Jacobian updateStateCp = .true. updateFluxCp = .true. @@ -382,34 +382,34 @@ subroutine eval8summa(& ! update diagnostic variables and derivatives call updateVars(& ! input - ixNrgConserv.ne.closedForm, & ! intent(in): flag if computing temperature compoment of enthalpy - ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy - .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - lookup_data, & ! intent(in): lookup table data structure - scalarCanairTempTrial, & ! intent(in): trial value of canopy air space temperature (K) + ixNrgConserv.ne.closedForm, & ! intent(in): flag if computing temperature compoment of enthalpy + ixNrgConserv==enthalpyFormLU, & ! intent(in): flag to use the lookup table for soil temperature-enthalpy + .false., & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + lookup_data, & ! intent(in): lookup table data structure + scalarCanairTempTrial, & ! intent(in): trial value of canopy air space temperature (K) ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value for canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value for canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value for canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value for canopy ice content (kg m-2) + scalarCanopyTempTrial, & ! intent(inout): trial value for canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value for canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value for canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value for canopy ice content (kg m-2) ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) ! output: enthalpy state variables - scalarCanairEnthalpyTrial, & ! intent(inout): trial value for enthalpy of the canopy air space (J m-3) - scalarCanopyEnthTempTrial, & ! intent(inout): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthTempTrial, & ! intent(inout): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpyTrial, & ! intent(inout): trial value for enthalpy of the canopy air space (J m-3) + scalarCanopyEnthTempTrial, & ! intent(inout): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) + mLayerEnthTempTrial, & ! intent(inout): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) if(updateStateCp)then diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 0ebcf602c..322fd0af3 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -35,9 +35,9 @@ module eval8summaWithPrime_module ! look-up values for the choice of variable in energy equations (BE residual or IDA state variable) USE mDecisions_module,only: & - closedForm, & ! use temperature - enthalpyFDlu, & ! use enthalpy with lookup tables - enthalpyFD ! use enthalpy with analytical solution + closedForm, & ! use temperature with closed form heat capacity + enthalpyFormLU, & ! use enthalpy with soil temperature-enthalpy lookup tables + enthalpyForm ! use enthalpy with soil temperature-enthalpy analytical solution implicit none private @@ -288,7 +288,7 @@ subroutine eval8summaWithPrime(& end if end if ! ( feasibility check ) - if(ixNrgConserv == enthalpyFD .or. ixNrgConserv == enthalpyFDlu)then ! use enthalpy as state variable, do not need state terms but do need flux term + if(ixNrgConserv == enthalpyForm .or. ixNrgConserv == enthalpyFormLU)then ! use enthalpy as state variable, do not need state terms but do need flux term updateStateCp = .false. updateFluxCp = .true. needStateCm = .false. @@ -386,7 +386,7 @@ subroutine eval8summaWithPrime(& err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - if(ixNrgConserv== enthalpyFD .or. ixNrgConserv == enthalpyFDlu)then ! use state variable as enthalpy, need to compute temperature + if(ixNrgConserv== enthalpyForm .or. ixNrgConserv == enthalpyFormLU)then ! use state variable as enthalpy, need to compute temperature scalarCanairEnthalpyTrial = scalarCanairNrgTrial scalarCanopyEnthalpyTrial = scalarCanopyNrgTrial mLayerEnthalpyTrial = mLayerNrgTrial @@ -418,45 +418,45 @@ subroutine eval8summaWithPrime(& ! This possibly could cause problems (?) if we use splitting, but we are not using splitting at the moment call updateVarsWithPrime(& ! input - ixNrgConserv.ne.closedForm, & ! intent(in): flag if need to update temperature from enthalpy - ixNrgConserv==enthalpyFDlu, & ! intent(in): flag to use the lookup table for soil enthalpy - .true., & ! intent(in): flag if computing for Jacobian update - .false., & ! intent(in): flag to adjust temperature to account for the energy - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - lookup_data, & ! intent(in): lookup table data structure + ixNrgConserv.ne.closedForm, & ! intent(in): flag if need to update temperature from enthalpy + ixNrgConserv==enthalpyFormLU, & ! intent(in): flag to use the lookup table for soil temperature-enthalpy + .true., & ! intent(in): flag if computing for Jacobian update + .false., & ! intent(in): flag to adjust temperature to account for the energy + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + lookup_data, & ! intent(in): lookup table data structure ! input: enthalpy state variables - scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpyTrial, & ! intent(in): trial value for enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpyTrial, & ! intent(in): trial vector of enthalpy of each snow+soil layer (J m-3) + scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(in): trial value for enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(in): trial vector of enthalpy of each snow+soil layer (J m-3) ! output: variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(inout): trial value of canopy air space temperature (K) - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - scalarCanopyTempPrime, & ! intent(inout): trial value of time derivative canopy temperature (K s-1) - scalarCanopyWatPrime, & ! intent(inout): trial value of time derivative canopy total water (kg m-2 s-1) - scalarCanopyLiqPrime, & ! intent(inout): trial value of time derivative canopy liquid water (kg m-2 s-1) - scalarCanopyIcePrime, & ! intent(inout): trial value of time derivative canopy ice content (kg m-2 s-1) + scalarCanairTempTrial, & ! intent(inout): trial value of canopy air space temperature (K) + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + scalarCanopyTempPrime, & ! intent(inout): trial value of time derivative canopy temperature (K s-1) + scalarCanopyWatPrime, & ! intent(inout): trial value of time derivative canopy total water (kg m-2 s-1) + scalarCanopyLiqPrime, & ! intent(inout): trial value of time derivative canopy liquid water (kg m-2 s-1) + scalarCanopyIcePrime, & ! intent(inout): trial value of time derivative canopy ice content (kg m-2 s-1) ! output: variables for th snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! intent(inout): trial vector of time derivative layer temperature (K s-1) - mLayerVolFracWatPrime, & ! intent(inout): trial vector of time derivative volumetric total water content (s-1) - mLayerVolFracLiqPrime, & ! intent(inout): trial vector of time derivative volumetric liquid water content (s-1) - mLayerVolFracIcePrime, & ! intent(inout): trial vector of time derivative volumetric ice water content (s-1) - mLayerMatricHeadPrime, & ! intent(inout): trial vector of time derivative total water matric potential (m s-1) - mLayerMatricHeadLiqPrime, & ! intent(inout): trial vector of time derivative liquid water matric potential (m s-1) + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempPrime, & ! intent(inout): trial vector of time derivative layer temperature (K s-1) + mLayerVolFracWatPrime, & ! intent(inout): trial vector of time derivative volumetric total water content (s-1) + mLayerVolFracLiqPrime, & ! intent(inout): trial vector of time derivative volumetric liquid water content (s-1) + mLayerVolFracIcePrime, & ! intent(inout): trial vector of time derivative volumetric ice water content (s-1) + mLayerMatricHeadPrime, & ! intent(inout): trial vector of time derivative total water matric potential (m s-1) + mLayerMatricHeadLiqPrime, & ! intent(inout): trial vector of time derivative liquid water matric potential (m s-1) ! output: error control - err,cmessage) ! intent(out): error control + err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) if(updateStateCp)then diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index 7de765644..6ac85744f 100644 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -148,13 +148,13 @@ module mDecisions_module integer(i4b),parameter,public :: meltDripUnload = 321 ! Hedstrom and Pomeroy (1998), Storck et al 2002 (snowUnloadingCoeff & ratioDrip2Unloading) integer(i4b),parameter,public :: windUnload = 322 ! Roesch et al 2001, formulate unloading based on wind and temperature ! look-up values for the choice of variable in energy equations (BE residual or IDA state variable) -integer(i4b),parameter,public :: enthalpyFD = 323 ! use enthalpy with analytical solution -integer(i4b),parameter,public :: closedForm = 324 ! use temperature -integer(i4b),parameter,public :: enthalpyFDlu = 325 ! use enthalpy with lookup tables +integer(i4b),parameter,public :: closedForm = 323 ! use temperature with closed form heat capacity +integer(i4b),parameter,public :: enthalpyFormLU = 324 ! use enthalpy with soil temperature-enthalpy lookup tables +integer(i4b),parameter,public :: enthalpyForm = 325 ! use enthalpy with soil temperature-enthalpy analytical solution ! look-up values for the choice of choice of full or empty aquifer at start integer(i4b),parameter,public :: fullStart = 326 ! full aquifer at start integer(i4b),parameter,public :: emptyStart = 327 ! empty aquifer at start -! ----------------------------------------------------------------------------------------------------------- +! ----------------------------------------------------------------------------------------------------------- contains @@ -416,12 +416,12 @@ subroutine mDecisions(err,message) #endif ! choice of variable in either energy backward Euler residual or IDA state variable - ! for backward Euler solution, enthalpyFD has better coincidence of energy conservation - ! in IDA solution, enthalpyFD makes the state variables to be enthalpy and the residual is computed in enthalpy space + ! for backward Euler solution, enthalpyForm has better coincidence of energy conservation + ! in IDA solution, enthalpyForm makes the state variables to be enthalpy and the residual is computed in enthalpy space select case(trim(model_decisions(iLookDECISIONS%nrgConserv)%cDecision)) - case('closedForm' ); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = closedForm ! use temperature - case('enthalpyFD' ); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = enthalpyFD ! use enthalpy with analytical solution - case('enthalpyFDlu'); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = enthalpyFDlu ! use enthalpy with lookup tables + case('closedForm' ); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = closedForm ! use temperature with closed form heat capacity + case('enthalpyFormLU'); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = enthalpyFormLU ! use enthalpy with soil temperature-enthalpy lookup tables + case('enthalpyForm' ); model_decisions(iLookDECISIONS%nrgConserv)%iDecision = enthalpyForm ! use enthalpy with soil temperature-enthalpy analytical solution case default if (trim(model_decisions(iLookDECISIONS%num_method)%cDecision)=='itertive')then model_decisions(iLookDECISIONS%nrgConserv)%iDecision = closedForm ! included for backwards compatibility diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 5115d4d68..977282fad 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -70,9 +70,9 @@ module summaSolve4ida_module ! look-up values for the choice of variable in energy equations (BE residual or IDA state variable) USE mDecisions_module,only: & - closedForm, & ! use temperature - enthalpyFDlu, & ! use enthalpy with lookup tables - enthalpyFD ! use enthalpy with analytical solution + closedForm, & ! use temperature with closed form heat capacity + enthalpyFormLU, & ! use enthalpy with soil temperature-enthalpy lookup tables + enthalpyForm ! use enthalpy with soil temperature-enthalpy analytical solution ! look-up values for method used to compute derivative USE mDecisions_module,only: & diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index f26027801..12363c19d 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -67,23 +67,23 @@ module varSubstep_module ! constants USE multiconst,only:& - Tfreeze, & ! freezing temperature (K) - LH_fus, & ! latent heat of fusion (J kg-1) - LH_vap, & ! latent heat of vaporization (J kg-1) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water ! intrinsic density of liquid water (kg m-3) + Tfreeze, & ! freezing temperature (K) + LH_fus, & ! latent heat of fusion (J kg-1) + LH_vap, & ! latent heat of vaporization (J kg-1) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water ! intrinsic density of liquid water (kg m-3) ! look-up values for the numerical method -USE mDecisions_module,only: & - numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes - kinsol ,& ! SUNDIALS backward Euler solution using Kinsol - ida ! SUNDIALS solution using IDA +USE mDecisions_module,only: & + numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes + kinsol ,& ! SUNDIALS backward Euler solution using Kinsol + ida ! SUNDIALS solution using IDA ! look-up values for the choice of variable in energy equations (BE residual or IDA state variable) -USE mDecisions_module,only: & - closedForm, & ! use temperature - enthalpyFDlu, & ! use enthalpy with lookup tables - enthalpyFD ! use enthalpy with analytical solution +USE mDecisions_module,only: & + closedForm, & ! use temperature with closed form heat capacity + enthalpyFormLU, & ! use enthalpy with soil temperature-enthalpy lookup tables + enthalpyForm ! use enthalpy with soil temperature-enthalpy analytical solution ! safety: set private unless specified otherwise implicit none @@ -282,7 +282,7 @@ subroutine varSubstep(& use_lookup = .false. if((ixNrgConserv .ne. closedForm .or. computNrgBalance) .and. ixNumericalMethod .ne. ida) computeEnthTemp = .true. ! use enthTemp to conserve energy or compute energy balance if(ixNrgConserv .ne. closedForm .and. ixNumericalMethod==ida) enthalpyStateVec = .true. ! enthalpy as state variable - if(ixNrgConserv==enthalpyFDlu) use_lookup = .true. ! use lookup tables for soil enthalpy instead of analytical solution + if(ixNrgConserv==enthalpyFormLU) use_lookup = .true. ! use lookup tables for soil enthalpy instead of analytical solution ! initialize the length of the substep dtSubstep = dtInit diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zDecisions.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zDecisions.txt index 90f2397b7..d03f84873 100644 --- a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zDecisions.txt +++ b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zDecisions.txt @@ -36,7 +36,7 @@ thCondSnow jrdn1991 ! (26) choice of thermal conduct thCondSoil mixConstit ! (27) choice of thermal conductivity representation for soil spatial_gw localColumn ! (28) choice of method for the spatial representation of groundwater subRouting timeDlay ! (29) choice of method for sub-grid routing -nrgConserv closedForm ! (30) choice of variable in energy conservation backward Euler residual +nrgConserv closedForm ! (30) choice of variable in energy equations (BE residual or IDA state variable) ! *********************************************************************************************** ! ***** description of the options available -- nothing below this point is read **************** ! *********************************************************************************************** @@ -166,7 +166,8 @@ nrgConserv closedForm ! (30) choice of variable in ene ! timeDlay ! time-delay histogram ! qInstant ! instantaneous routing ! ----------------------------------------------------------------------------------------------- -! (30) choice of variable in energy conservation backward Euler residual -! closedForm ! closed form heat capacity -! enthalpyFD ! enthalpy finite difference, has better coincidence of energy conservation +! (30) choice of variable in energy equations (BE residual or IDA state variable) +! closedForm ! use temperature with closed form heat capacity +! enthalpyFormLU ! use enthalpy with soil temperature-enthalpy lookup tables +! enthalpyForm ! use enthalpy with soil temperature-enthalpy analytical solutions ! *********************************************************************************************** diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index 434b40f3b..597ea6fe4 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -43,7 +43,7 @@ for i, m in enumerate(method_name): viz_fil[i] = m + '_hrly_diff_bals_{}.nc' viz_fil[i] = viz_fil[i].format(','.join(['balance','scaledBalance'])) -do_rel = True # use scaled values +do_rel = False # use scaled values nbatch_hrus = 518 # number of HRUs per batch # Specify variables of interest diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index d80c41810..c56c623fb 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -67,8 +67,7 @@ des_fl4 = method_name + '_hrly_diff_wall_{}_{}.nc' settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] stepsets= ['numberStateSplit','numberDomainSplitNrg','numberDomainSplitMass','numberScalarSolutions','meanStepSize'] -balssets= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime', 'numberFluxCalc', -'scaledBalanceCasNrg','scaledBalanceVegNrg','scaledBalanceSnowNrg','scaledBalanceSoilNrg','scaledBalanceVegMass','scaledBalanceSnowMass','scaledBalanceSoilMass','scaledBalanceAqMass'] +balssets= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime', 'numberFluxCalc'] wallsets= ['wallClockTime'] viz_fil = method_name + '_hrly_diff_stats_{}.nc' @@ -76,7 +75,7 @@ viz_fl2 = method_name + '_hrly_diff_steps_{}.nc' viz_fl2 = viz_fl2.format(','.join(stepsets)) viz_fl3 = method_name + '_hrly_diff_bals_{}.nc' -viz_fl3 = viz_fl3.format(','.join(['balance','scaledBalance'])) +viz_fl3 = viz_fl3.format(','.join(['balance'])) viz_fl4 = method_name + '_hrly_diff_wals_{}.nc' viz_fl4 = viz_fl4.format(','.join(['wallclock'])) From da75a57e73b14b08d323b1b7fcde4c619fc4fae0 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 18 Apr 2024 17:07:52 +0900 Subject: [PATCH 1271/1472] make balance for snow realMissing, not 0, if end with no snow --- build/source/engine/coupled_em.f90 | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 7fc598fac..e0652b8f2 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1253,7 +1253,7 @@ subroutine coupled_em(& diag_data%var(iLookDIAG%balanceLayerNrg)%dat(:) = innerBalanceLayerNrg(:) diag_data%var(iLookDIAG%balanceLayerMass)%dat(:) = innerBalanceLayerMass(:) - ! compute the balance of energy and water per entire snow and soil domain, in W m-3 and kg m-2 s-1 respsectively + ! compute the balance of energy and water per entire snow and soil domain, in W m-3 and kg m-2 s-1 respectively diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = 0._rkind diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = 0._rkind diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = 0._rkind @@ -1270,6 +1270,11 @@ subroutine coupled_em(& diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) + innerBalanceLayerMass(iLayer)*lyr_wght end select end do + if(nSnow==0)then + diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = realMissing + diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = realMissing + endif + if(do_outer)then deallocate(innerBalanceLayerNrg) deallocate(innerBalanceLayerMass) @@ -1455,13 +1460,17 @@ subroutine coupled_em(& ! save balance of energy and water per single layer domain diag_data%var(iLookDIAG%balanceCasNrg)%dat(1) = meanBalance(1) ! W m-3 - diag_data%var(iLookDIAG%balanceVegNrg)%dat(1) = meanBalance(2) ! W m-3 - diag_data%var(iLookDIAG%balanceVegMass)%dat(1) = meanBalance(3) ! kg m-2 s-1 - diag_data%var(iLookDIAG%balanceAqMass)%dat(1) = meanBalance(4) ! kg m-2 s-1 - diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = meanBalance(5) ! W m-3 - diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = meanBalance(6) ! W m-3 - diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = meanBalance(7) ! kg m-2 s-1 + diag_data%var(iLookDIAG%balanceVegNrg)%dat(1) = meanBalance(2) ! W m-3 will be realMissing if computeVegFlux is false + diag_data%var(iLookDIAG%balanceVegMass)%dat(1) = meanBalance(3) ! kg m-2 s-1 will be realMissing if computeVegFlux is false + diag_data%var(iLookDIAG%balanceAqMass)%dat(1) = meanBalance(4) ! kg m-2 s-1 will be realMissing if no aquifer + diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = meanBalance(5) ! W m-3 will be realMissing if no snow at end of data step + diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = meanBalance(6) ! W m-3 + diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = meanBalance(7) ! kg m-2 s-1 will be realMissing if no snow at end of data step diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = meanBalance(8) ! kg m-2 s-1 + if (nSnow==0)then ! will be 0, or a balance computed on part of the data step if snow went to zero during the data step + diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = realMissing + diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = realMissing + endif ! ----- ! * balance checks for the canopy... @@ -1635,7 +1644,7 @@ subroutine coupled_em(& ! save the total soil enthalpy scalarTotalSoilEnthalpy = sum(mLayerEnthalpy(nSnow+1:nLayers) * mLayerDepth(nSnow+1:nLayers))/sum(mLayerDepth(nSnow+1:nLayers)) ! save the total snow enthalpy - scalarTotalSnowEnthalpy = sum(mLayerEnthalpy(1:nSnow) * mLayerDepth(1:nSnow))/sum(mLayerDepth(1:nSnow)) + if(nSnow>0) scalarTotalSnowEnthalpy = sum(mLayerEnthalpy(1:nSnow) * mLayerDepth(1:nSnow))/sum(mLayerDepth(1:nSnow)) ! save the surface temperature (just to make things easier to visualize) prog_data%var(iLookPROG%scalarSurfaceTemp)%dat(1) = prog_data%var(iLookPROG%mLayerTemp)%dat(1) From 12da1b1b947c579871163dff59df61958bca801b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 18 Apr 2024 17:35:05 +0900 Subject: [PATCH 1272/1472] change to snow balance is realMissing if never a snow state in the data window, not just at end --- build/source/engine/coupled_em.f90 | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index e0652b8f2..7c97ca747 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -250,6 +250,7 @@ subroutine coupled_em(& real(rkind) :: meanLatHeatCanopyEvap ! timestep-average latent heat flux for evaporation from the canopy to the canopy air space (W m-2) real(rkind) :: meanSenHeatCanopy ! timestep-average sensible heat flux from the canopy to the canopy air space (W m-2) ! balance checks + logical(lgt) :: bal_snow ! flag to denote if computed a snow balance integer(i4b) :: iVar ! loop through model variables real(rkind) :: balanceSoilCompress ! total soil compression (kg m-2) real(rkind) :: scalarCanopyWatBalError! water balance error for the vegetation canopy (kg m-2) @@ -682,6 +683,9 @@ subroutine coupled_em(& nsub = 0 nsub_success = 0 + ! initialize if used a snow balance + bal_snow = .false. + ! loop through sub-steps substeps: do ! continuous do statement with exit clause (alternative to "while") @@ -1264,6 +1268,7 @@ subroutine coupled_em(& lyr_wght = prog_data%var(iLookPROG%mLayerDepth)%dat(iLayer) / prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) + innerBalanceLayerNrg(iLayer)*lyr_wght diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) + innerBalanceLayerMass(iLayer)*lyr_wght + bal_snow = .true. case (iname_soil) lyr_wght = prog_data%var(iLookPROG%mLayerDepth)%dat(iLayer) / sum( prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers) ) diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) + innerBalanceLayerNrg(iLayer)*lyr_wght @@ -1463,11 +1468,11 @@ subroutine coupled_em(& diag_data%var(iLookDIAG%balanceVegNrg)%dat(1) = meanBalance(2) ! W m-3 will be realMissing if computeVegFlux is false diag_data%var(iLookDIAG%balanceVegMass)%dat(1) = meanBalance(3) ! kg m-2 s-1 will be realMissing if computeVegFlux is false diag_data%var(iLookDIAG%balanceAqMass)%dat(1) = meanBalance(4) ! kg m-2 s-1 will be realMissing if no aquifer - diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = meanBalance(5) ! W m-3 will be realMissing if no snow at end of data step + diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = meanBalance(5) ! W m-3 will be realMissing if no snow during data step diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = meanBalance(6) ! W m-3 - diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = meanBalance(7) ! kg m-2 s-1 will be realMissing if no snow at end of data step + diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = meanBalance(7) ! kg m-2 s-1 will be realMissing if no snow during data step diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = meanBalance(8) ! kg m-2 s-1 - if (nSnow==0)then ! will be 0, or a balance computed on part of the data step if snow went to zero during the data step + if (.not.bal_snow)then ! will be 0, make realMissing diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = realMissing diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = realMissing endif From 2d1dfa7bbd266f90b96cffb8da0f891a3725726c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 18 Apr 2024 17:40:12 +0900 Subject: [PATCH 1273/1472] should have removed these lines for last commit --- build/source/engine/coupled_em.f90 | 4 ---- 1 file changed, 4 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 7c97ca747..e6d607dbd 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1275,10 +1275,6 @@ subroutine coupled_em(& diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) + innerBalanceLayerMass(iLayer)*lyr_wght end select end do - if(nSnow==0)then - diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = realMissing - diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = realMissing - endif if(do_outer)then deallocate(innerBalanceLayerNrg) From 31faaa4b19fc1256ce59882bb4cd448341f3a1c0 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 18 Apr 2024 20:46:34 +0900 Subject: [PATCH 1274/1472] change lyr_wght --- build/source/engine/coupled_em.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index e6d607dbd..eec577fd2 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1265,7 +1265,7 @@ subroutine coupled_em(& do iLayer=1,nLayers select case (indx_data%var(iLookINDEX%layerType)%dat(iLayer)) case (iname_snow) - lyr_wght = prog_data%var(iLookPROG%mLayerDepth)%dat(iLayer) / prog_data%var(iLookPROG%scalarSnowDepth)%dat(1) + lyr_wght = prog_data%var(iLookPROG%mLayerDepth)%dat(iLayer) / sum( prog_data%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) + innerBalanceLayerNrg(iLayer)*lyr_wght diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) + innerBalanceLayerMass(iLayer)*lyr_wght bal_snow = .true. From 065e1376ce79251c6aaac05d04c3cd4a90dc67fa Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 19 Apr 2024 07:46:39 +0900 Subject: [PATCH 1275/1472] clean up aquiferIni decision --- build/source/driver/summa_restart.f90 | 18 ++++++++++++++++-- build/source/engine/mDecisions.f90 | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/build/source/driver/summa_restart.f90 b/build/source/driver/summa_restart.f90 index 06fb11892..bf5ba7026 100644 --- a/build/source/driver/summa_restart.f90 +++ b/build/source/driver/summa_restart.f90 @@ -96,6 +96,7 @@ subroutine summa_readRestart(summa1_struc, err, message) integer(i4b) :: iGRU,iHRU ! looping variables logical(lgt) :: checkEnthalpy ! flag if checking enthalpy for consistency logical(lgt) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use analytical solution + real(rkind) :: aquifer_start ! initial aquifer storage ! --------------------------------------------------------------------------------------- ! associate to elements in the data structure summaVars: associate(& @@ -222,18 +223,31 @@ subroutine summa_readRestart(summa1_struc, err, message) ! For water balance calculations it is important to ensure that the local aquifer storage is zero if groundwater is treated as a basin-average state variable (singleBasin); ! and ensure that basin-average aquifer storage is zero when groundwater is included in the local columns (localColumn). + ! select aquifer option + select case(aquiferIni) + case(fullStart) + aquifer_start = 1._rkind ! Start with full aquifer, since easier to spin up by draining than filling (filling we need to wait for precipitation) + case(emptyStart) + aquifer_start = 0._rkind ! Start with empty aquifer ! If want to compare model method outputs, empty start leads to quicker equilibrium + case default + message=trim(message)//'unable to identify decision for initial aquifer storage' + return + end select ! aquifer option + ! select groundwater option select case(spatial_gw) ! the basin-average aquifer storage is not used if the groundwater is included in the local column case(localColumn) bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 0._rkind ! set to zero to be clear that there is no basin-average aquifer storage in this configuration + do iHRU=1,gru_struc(iGRU)%hruCount + progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarAquiferStorage)%dat(1) = aquifer_start + end do ! the local column aquifer storage is not used if the groundwater is basin-average ! (i.e., where multiple HRUs drain to a basin-average aquifer) case(singleBasin) - bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 1._rkind ! Start with this full, since easier to spin up by draining than filling (filling we need to wait for precipitation) - if (aquiferIni==emptyStart) bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 0._rkind ! If want to compare model method outputs, empty start leads to quicker equilibrium + bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = aquifer_start do iHRU=1,gru_struc(iGRU)%hruCount progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarAquiferStorage)%dat(1) = 0._rkind ! set to zero to be clear that there is no local aquifer storage in this configuration end do diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index 6ac85744f..f482848fd 100644 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -437,7 +437,7 @@ subroutine mDecisions(err,message) case('fullStart' ); model_decisions(iLookDECISIONS%aquiferIni)%iDecision = fullStart ! start with full aquifer case('emptyStart'); model_decisions(iLookDECISIONS%aquiferIni)%iDecision = emptyStart ! start with empty aquifer case default; model_decisions(iLookDECISIONS%aquiferIni)%iDecision = fullStart ! most users will want to start with full aquifer, make this decision on their behalf -end select + end select ! identify the method used to calculate flux derivatives select case(trim(model_decisions(iLookDECISIONS%fDerivMeth)%cDecision)) From 38c053d59bf602a66bad27ba708dcdae30165238 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 19 Apr 2024 19:24:11 +0900 Subject: [PATCH 1276/1472] part of last commit --- build/source/driver/summa_restart.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/driver/summa_restart.f90 b/build/source/driver/summa_restart.f90 index bf5ba7026..bcff5833e 100644 --- a/build/source/driver/summa_restart.f90 +++ b/build/source/driver/summa_restart.f90 @@ -241,7 +241,7 @@ subroutine summa_readRestart(summa1_struc, err, message) case(localColumn) bvarStruct%gru(iGRU)%var(iLookBVAR%basin__AquiferStorage)%dat(1) = 0._rkind ! set to zero to be clear that there is no basin-average aquifer storage in this configuration do iHRU=1,gru_struc(iGRU)%hruCount - progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarAquiferStorage)%dat(1) = aquifer_start + if(aquiferIni==emptyStart) progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarAquiferStorage)%dat(1) = aquifer_start ! leave at initialized values if fullStart end do ! the local column aquifer storage is not used if the groundwater is basin-average From b3dfc19caf030d1097af9d5302ccb026557aac70 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sun, 21 Apr 2024 23:16:42 +0900 Subject: [PATCH 1277/1472] utils --- utils/process_concat.sh | 3 ++- utils/process_diff.sh | 3 ++- utils/process_hist.sh | 3 ++- utils/process_plot.sh | 3 ++- utils/process_scat.sh | 3 ++- utils/process_stat.sh | 3 ++- utils/process_statBatch.sh | 3 ++- 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/utils/process_concat.sh b/utils/process_concat.sh index 2e9681583..f7699e68b 100755 --- a/utils/process_concat.sh +++ b/utils/process_concat.sh @@ -9,8 +9,9 @@ #SBATCH --account=rpp-kshook #SBATCH --output=/home/avanb/TestScripts/output/slurm-%A_%a.out +module load StdEnv/2020 module load gcc/9.3.0 -module load geo-stack +module load geo-stack/2022c virtualenv --no-download $SLURM_TMPDIR/env source $SLURM_TMPDIR/env/bin/activate diff --git a/utils/process_diff.sh b/utils/process_diff.sh index b87378487..ca910f272 100755 --- a/utils/process_diff.sh +++ b/utils/process_diff.sh @@ -8,8 +8,9 @@ #SBATCH --account=rpp-kshook #SBATCH --output=/home/avanb/TestScripts/output/slurm-%A_%a.out +module load StdEnv/2020 module load gcc/9.3.0 -module load geo-stack +module load geo-stack/2022c virtualenv --no-download $SLURM_TMPDIR/env source $SLURM_TMPDIR/env/bin/activate diff --git a/utils/process_hist.sh b/utils/process_hist.sh index f1fe485c6..522ea48b6 100755 --- a/utils/process_hist.sh +++ b/utils/process_hist.sh @@ -9,8 +9,9 @@ #SBATCH --account=rpp-kshook #SBATCH --output=/home/avanb/TestScripts/output/slurm-%A_%a.out +module load StdEnv/2020 module load gcc/9.3.0 -module load geo-stack +module load geo-stack/2022c virtualenv --no-download $SLURM_TMPDIR/env source $SLURM_TMPDIR/env/bin/activate diff --git a/utils/process_plot.sh b/utils/process_plot.sh index 3ce813a7f..654cac53d 100755 --- a/utils/process_plot.sh +++ b/utils/process_plot.sh @@ -9,8 +9,9 @@ #SBATCH --account=rpp-kshook #SBATCH --output=/home/avanb/TestScripts/output/slurm-%A_%a.out +module load StdEnv/2020 module load gcc/9.3.0 -module load geo-stack +module load geo-stack/2022c virtualenv --no-download $SLURM_TMPDIR/env source $SLURM_TMPDIR/env/bin/activate diff --git a/utils/process_scat.sh b/utils/process_scat.sh index 713eef1b6..ff24504f1 100755 --- a/utils/process_scat.sh +++ b/utils/process_scat.sh @@ -9,8 +9,9 @@ #SBATCH --account=rpp-kshook #SBATCH --output=/home/avanb/TestScripts/output/slurm-%A_%a.out +module load StdEnv/2020 module load gcc/9.3.0 -module load geo-stack +module load geo-stack/2022c virtualenv --no-download $SLURM_TMPDIR/env source $SLURM_TMPDIR/env/bin/activate diff --git a/utils/process_stat.sh b/utils/process_stat.sh index 77b9e08ec..a21915fd8 100755 --- a/utils/process_stat.sh +++ b/utils/process_stat.sh @@ -9,8 +9,9 @@ #SBATCH --account=rpp-kshook #SBATCH --output=/home/avanb/TestScripts/output/slurm-%A_%a.out +module load StdEnv/2020 module load gcc/9.3.0 -module load geo-stack +module load geo-stack/2022c virtualenv --no-download $SLURM_TMPDIR/env source $SLURM_TMPDIR/env/bin/activate diff --git a/utils/process_statBatch.sh b/utils/process_statBatch.sh index c91057a18..cda320437 100755 --- a/utils/process_statBatch.sh +++ b/utils/process_statBatch.sh @@ -17,8 +17,9 @@ echo "$SLURM_ARRAY_TASK_ID" # sbatch --array=201-201 process_statBatch.sh # ---------------------------------------------------------------------------------------------- +module load StdEnv/2020 module load gcc/9.3.0 -module load geo-stack +module load geo-stack/2022c virtualenv --no-download $SLURM_TMPDIR/env source $SLURM_TMPDIR/env/bin/activate From d9fcaee218654eba29c5a169b4967e3df26c76d2 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 22 Apr 2024 19:49:02 +0900 Subject: [PATCH 1278/1472] utils --- utils/bal_per_GRU.py | 50 ++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/utils/bal_per_GRU.py b/utils/bal_per_GRU.py index ba22cd6fb..01bb4a7a6 100644 --- a/utils/bal_per_GRU.py +++ b/utils/bal_per_GRU.py @@ -26,22 +26,26 @@ if testing: stat = 'mean' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be1'] #cm','be1en','be1lu'] #maybe make this an argument + method_name=['be1en'] #cm','be1en','be1lu'] #maybe make this an argument else: import sys # The first input argument specifies the run where the files are stat = sys.argv[1] - method_name=['be1','be1en','be1lu'] #maybe make this an argument + method_name=['be1','be1en'] #,'be1lu'] #maybe make this an argument # Simulation statistics file locations +settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] +viz_fil = method_name.copy() viz_fl2 = method_name.copy() for i, m in enumerate(method_name): viz_fl2[i] = m + '_hrly_diff_bals_{}.nc' - viz_fl2[i] = viz_fl2[i].format(','.join(['balance','scaledBalance'])) + viz_fl2[i] = viz_fl2[i].format(','.join(['balance'])) + viz_fil[i] = m + '_hrly_diff_stats_{}.nc' + viz_fil[i] = viz_fil[i].format(','.join(settings)) # Specify variables of interest -plot_vars = ['scaledBalanceCasNrg','scaledBalanceVegNrg','scaledBalanceSnowNrg','scaledBalanceSoilNrg','wallClockTime'] -plot_vars2 =['scaledBalanceVegMass','scaledBalanceSnowMass','scaledBalanceSoilMass','scaledBalanceAqMass'] +plot_vars = ['scalarTotalET','scalarCanopyWat','scalarSWE','scalarTotalSoilWat','wallClockTime'] +plot_vars2 =['scalarCanopyWat','scalarSWE','scalarTotalSoilWat','averageRoutedRunoff'] comp_vars = ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','numberFluxCalc'] comp_vars2 =['balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass'] @@ -49,8 +53,12 @@ plt_titl2 = ['(a) Vegetation Mass Balance','(b) Snow Mass Balance','(c) Soil Mass Balance', '(d) Aquifer Mass Balance'] leg_titl0 = ['$W~m^{-3}$'] * 4 +['$num$'] leg_titl02 =['$kg~m^{-2}~s^{-1}$'] * 4 -leg_titl = ['$s^{-1}$'] * 4 +['$s$'] -leg_titl2 =['$s^{-1}$'] * 4 + +leg_titl = ['mm~y^{-1}$','$kg~m^{-2}$','$kg~m^{-2}$','$kg~m^{-2}$','$s$'] +leg_word = ['Total evapotranspiration', 'Total water on the vegetation canopy','Snow water equivalent','Total soil water content','Wall clock time'] +leg_titl2 =['$kg~m^{-2}$','$kg~m^{-2}$','$kg~m^{-2}$','mm~y^{-1}$'] +leg_word2 = ['Total water on the vegetation canopy','Snow water equivalent','Total soil water content','Average routed runoff'] + #fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' #fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) @@ -60,30 +68,32 @@ fig_fil2 =fig_fil2.format(stat) summa = {} +summa1 = {} for i, m in enumerate(method_name): # Get the aggregated statistics of SUMMA simulations summa[m] = xr.open_dataset(viz_dir/viz_fl2[i]) + summa1[m] = xr.open_dataset(viz_dir/viz_fil[i]) ##Figure -def run_loop(i,var,comp,leg_t,leg_t0,plt_t): +def run_loop(i,var,comp,leg_t,leg_t0,plt_t,leg_w): r = i//2 c = i-r*2 # Data for m in method_name: # Get the statistics, remove 9999 (should be nan, but just in case) - s = np.fabs(summa[m][var].sel(stat=stat)).where(lambda x: x != 9999) s0 = np.fabs(summa[m][comp].sel(stat=stat)).where(lambda x: x != 9999) - - if var == 'wallClockTime': - stat0_word = 'Number flux calculations' - stat_word = 'Wallclock time' - else: - stat0_word = 'Absolute value' - stat_word = 'Scaled by state (absolute value)' + s = np.fabs(summa1[m][var].sel(stat=stat)).where(lambda x: x != 9999) axs[r,c].scatter(x=s.values,y=s0.values,s=10,zorder=0,label=m) + + if comp == 'numberFluxCalc': + stat0_word = 'Number flux calculations' + else: + stat0_word = 'Balance absolute value' + + stat_word = leg_w if stat == 'mean': word = ' mean' if stat == 'amax': word = ' max' @@ -109,8 +119,8 @@ def run_loop(i,var,comp,leg_t,leg_t0,plt_t): fig.subplots_adjust(hspace=0.24, wspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space #fig.suptitle('Scatterplot of Hourly Statistics for each GRU', fontsize=40,y=1.0) -for i,(var,comp,leg_t,leg_t0,plt_t) in enumerate(zip(plot_vars,comp_vars,leg_titl,leg_titl0,plt_titl)): - run_loop(i,var,comp,leg_t,leg_t0,plt_t) +for i,(var,comp,leg_t,leg_t0,plt_t,leg_w) in enumerate(zip(plot_vars,comp_vars,leg_titl,leg_titl0,plt_titl,leg_word)): + run_loop(i,var,comp,leg_t,leg_t0,plt_t,leg_w) # Save plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) @@ -128,8 +138,8 @@ def run_loop(i,var,comp,leg_t,leg_t0,plt_t): fig.subplots_adjust(hspace=0.24, wspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space #fig.suptitle('Scatterplot of Hourly Statistics for each GRU', fontsize=40,y=1.0) -for i,(var,comp,leg_t,leg_t0,plt_t) in enumerate(zip(plot_vars2,comp_vars2,leg_titl2,leg_titl02,plt_titl2)): - run_loop(i,var,comp,leg_t,leg_t0,plt_t) +for i,(var,comp,leg_t,leg_t0,plt_t,leg_w) in enumerate(zip(plot_vars2,comp_vars2,leg_titl2,leg_titl02,plt_titl2,leg_word2)): + run_loop(i,var,comp,leg_t,leg_t0,plt_t,leg_w) # Save plt.savefig(viz_dir/fig_fil2, bbox_inches='tight', transparent=False) From 5dfabefe9288444832a4d37b15c4041903fc1a9c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 22 Apr 2024 19:54:07 +0900 Subject: [PATCH 1279/1472] utils --- utils/bal_per_GRU.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/bal_per_GRU.py b/utils/bal_per_GRU.py index 01bb4a7a6..f75a146fd 100644 --- a/utils/bal_per_GRU.py +++ b/utils/bal_per_GRU.py @@ -31,7 +31,7 @@ import sys # The first input argument specifies the run where the files are stat = sys.argv[1] - method_name=['be1','be1en'] #,'be1lu'] #maybe make this an argument + method_name=['be1','be1en','be1lu'] #maybe make this an argument # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] From a9e519e49e0c75acc918ce2039b756e90e5a148f Mon Sep 17 00:00:00 2001 From: ashleymedin <34691332+ashleymedin@users.noreply.github.com> Date: Mon, 1 Apr 2024 11:54:10 +0900 Subject: [PATCH 1280/1472] Merge pull request #37 from KyleKlenk/develop Update cmakelists to include c netcdf library for actors. --- build/cmake/CMakeLists.txt | 13 ++++++++----- build/cmake/build_actors.cluster.bash | 1 + build/source/engine/coupled_em.f90 | 14 +++++++++----- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 2d23be96a..f07470249 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -159,7 +159,7 @@ if(CMAKE_BUILD_TYPE MATCHES Cluster) if(CMAKE_BUILD_TYPE MATCHES Actors) find_package(CAF REQUIRED) set(INC_ACTORS ${CAF_INCLUDES} ${PARENT_DIR}/build/includes/global ${PARENT_DIR}/build/includes/summa_actor ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) - set(LIB_ACTORS ${CAF_LIBRARIES} -lcaf_core -lcaf_io) + set(LIB_ACTORS ${CAF_LIBRARIES} -lcaf_core -lcaf_io -lnetcdf) endif() else() @@ -205,7 +205,7 @@ else() ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) - set(LIB_ACTORS ${DIR_ACTORS}/lib -lcaf_core -lcaf_io) + set(LIB_ACTORS ${DIR_ACTORS}/lib -lcaf_core -lcaf_io -lnetcdf) endif() endif() @@ -237,7 +237,7 @@ set(NETCDF_DIR ${F_MASTER}/build/source/netcdf) set(NOAHMP_DIR ${F_MASTER}/build/source/noah-mp) # Define Actors specific directories -set(ACTORS_DIR ${PARENT_DIR}/build/source/actors) +set(ACTORS_DIR ${PARENT_DIR}/build/source) set(FILE_ACCESS_DIR ${ACTORS_DIR}/file_access_actor) set(JOB_ACTOR_DIR ${ACTORS_DIR}/job_actor) set(HRU_ACTOR_DIR ${ACTORS_DIR}/hru_actor) @@ -440,9 +440,12 @@ set(FILE_ACCESS_ACTOR ${ACTORS_DIR}/file_access_actor/output_container.cpp) set(JOB_ACTOR ${ACTORS_DIR}/job_actor/GRU.cpp - ${ACTORS_DIR}/job_actor/job_actor.cpp) + ${ACTORS_DIR}/job_actor/job_actor.cpp + ${ACTORS_DIR}/job_actor/distributed_job_actor.cpp + ${ACTORS_DIR}/job_actor/node_actor.cpp) set(HRU_ACTOR - ${ACTORS_DIR}/hru_actor/hru_actor.cpp) + ${ACTORS_DIR}/hru_actor/hru_actor.cpp + ${ACTORS_DIR}/hru_actor/hru_batch_actor.cpp) #========================================================================================= diff --git a/build/cmake/build_actors.cluster.bash b/build/cmake/build_actors.cluster.bash index 72cb6a90a..6046e7840 100755 --- a/build/cmake/build_actors.cluster.bash +++ b/build/cmake/build_actors.cluster.bash @@ -11,6 +11,7 @@ module load netcdf-fortran/4.5.2 module load caf export FLAGS_OPT="-flto=1;-fuse-linker-plugin" +export SUNDIALS_PATH=/globalhome/kck540/HPC/Libraries/sundials/v7.0/instdir cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials_Actors_Cluster cmake --build ../cmake_build --target all diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index eec577fd2..22a4aeda4 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -274,8 +274,9 @@ subroutine coupled_em(& real(rkind),allocatable :: liqSnowInit(:) ! volumetric liquid water conetnt of snow at the start of the time step real(rkind),allocatable :: liqSoilInit(:) ! soil moisture at the start of the time step ! timing information - real(rkind) :: startTime ! start time (used to compute wall clock time) - real(rkind) :: endTime ! end time (used to compute wall clock time) + integer(kind=8) :: count_rate + integer(kind=8) :: i_start, i_end + real :: elapsed_time real(rkind) :: mean_step_dt_sub ! mean solution step for the sub-step real(rkind) :: sumStepSize ! sum solution step for the data step ! outer loop control @@ -295,7 +296,9 @@ subroutine coupled_em(& ! This is the start of a data step for a local HRU ! get the start time - call cpu_time(startTime) + ! get the start time + CALL system_clock(count_rate=count_rate) + CALL system_clock(i_start) ! check that the decision is supported if(model_decisions(iLookDECISIONS%groundwatr)%iDecision==bigBucket .and. & @@ -1667,10 +1670,11 @@ subroutine coupled_em(& end if ! get the end time - call cpu_time(endTime) + CALL system_clock(i_end) + elapsed_time = REAL(i_end - i_start) / REAL(count_rate) ! get the elapsed time - diag_data%var(iLookDIAG%wallClockTime)%dat(1) = endTime - startTime + diag_data%var(iLookDIAG%wallClockTime)%dat(1) = elapsed_time end subroutine coupled_em From d3bf8c8076d14d5517f96d397829c8e1a7ee1c73 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 23 Apr 2024 15:44:47 +0900 Subject: [PATCH 1281/1472] clean up cmakelists --- build/cmake/CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index f07470249..43a2ad480 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -213,7 +213,10 @@ endif() if(CMAKE_BUILD_TYPE MATCHES Sundials) message("\nUsing SUNDIALS libraries for IDA and Kinsol, should have been installed previously") - set(LIB_SUNDIALS -lsundials_fcore_mod -lsundials_fnvecmanyvector_mod -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod -lsundials_fida_mod -lsundials_fsunnonlinsolnewton_mod -lsundials_fkinsol_mod) + set(LIB_SUNDIALS -lsundials_fcore_mod -lsundials_fnvecmanyvector_mod -lsundials_fnvecserial_mod + -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod + -lsundials_fsunlinsolband_mod -lsundials_fsunmatrixband_mod + -lsundials_fsunnonlinsolnewton_mod -lsundials_fida_mod -lsundials_fkinsol_mod) elseif(CMAKE_BUILD_TYPE MATCHES BE) message("\nUsing Backward Euler based off free versions of Numerical Recipes only") else() From aaed30b75d8115f4688aceb11a111e9a46fff3bc Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 23 Apr 2024 16:33:27 +0900 Subject: [PATCH 1282/1472] cleanup cmakelist --- build/cmake/CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 43a2ad480..596385c3c 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -213,10 +213,12 @@ endif() if(CMAKE_BUILD_TYPE MATCHES Sundials) message("\nUsing SUNDIALS libraries for IDA and Kinsol, should have been installed previously") - set(LIB_SUNDIALS -lsundials_fcore_mod -lsundials_fnvecmanyvector_mod -lsundials_fnvecserial_mod + set(LIB_SUNDIALS -lsundials_fcore_mod + -lsundials_fnvecserial_mod -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod -lsundials_fsunlinsolband_mod -lsundials_fsunmatrixband_mod - -lsundials_fsunnonlinsolnewton_mod -lsundials_fida_mod -lsundials_fkinsol_mod) + -lsundials_fsunnonlinsolnewton_mod + -lsundials_fida_mod -lsundials_fkinsol_mod) elseif(CMAKE_BUILD_TYPE MATCHES BE) message("\nUsing Backward Euler based off free versions of Numerical Recipes only") else() From 6ee8edc9ddc210d32781f2b858b26c1221e33783 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 25 Apr 2024 20:49:08 +0900 Subject: [PATCH 1283/1472] utils --- utils/bal_per_GRU.py | 26 +++++++++++++++++--------- utils/plot_per_GRUMultBal.py | 6 +++--- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/utils/bal_per_GRU.py b/utils/bal_per_GRU.py index f75a146fd..fcf2b0e0f 100644 --- a/utils/bal_per_GRU.py +++ b/utils/bal_per_GRU.py @@ -79,32 +79,40 @@ def run_loop(i,var,comp,leg_t,leg_t0,plt_t,leg_w): r = i//2 c = i-r*2 + if stat == 'mean': + word = ' mean' + stat1 = 'rmnz' + word1 = ' relative RMSE' + if stat == 'amax': + word = ' max' + stat1 = 'maxe' + word1 = ' max abs error' + if comp == 'numberFluxCalc': + stat1 = stat + word1 = word # Data for m in method_name: # Get the statistics, remove 9999 (should be nan, but just in case) s0 = np.fabs(summa[m][comp].sel(stat=stat)).where(lambda x: x != 9999) - s = np.fabs(summa1[m][var].sel(stat=stat)).where(lambda x: x != 9999) + s = np.fabs(summa1[m][var].sel(stat=stat1)).where(lambda x: x != 9999) axs[r,c].scatter(x=s.values,y=s0.values,s=10,zorder=0,label=m) if comp == 'numberFluxCalc': stat0_word = 'Number flux calculations' else: - stat0_word = 'Balance absolute value' + stat0_word = 'Balance abs value' stat_word = leg_w - if stat == 'mean': word = ' mean' - if stat == 'amax': word = ' max' - lgnd = axs[r,c].legend() for j, m in enumerate(method_name): lgnd.legendHandles[j]._sizes = [80] - axs[r,c].set_title(plt_t) - axs[r,c].set_xscale('log') - axs[r,c].set_yscale('log') - axs[r,c].set_xlabel(stat_word + word + ' [{}]'.format(leg_t)) + #axs[r,c].set_title(plt_t) + #axs[r,c].set_xscale('log') + if comp != 'numberFluxCalc': axs[r,c].set_yscale('log') + axs[r,c].set_xlabel(stat_word + word1 + ' [{}]'.format(leg_t)) axs[r,c].set_ylabel(stat0_word + word + ' [{}]'.format(leg_t0)) if 'compressed' in fig_fil: diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index 597ea6fe4..4439cf448 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -32,8 +32,8 @@ # plot all runs, pick statistic stat = sys.argv[1] -method_name=['be1','be1en','be1lu'] #maybe make this an argument -plt_name=['(a) SUMMA-BE1','(b) SUMMA-BE1 with enthalpy','(c) SUMMA-BE1 with enthalpy lookup'] #maybe make this an argument +method_name=['be1','be1en']#,'be1lu'] #maybe make this an argument +plt_name=['(a) SUMMA-BE1 Energy Temperature Form','(b) SUMMA-BE1 Energy Mixed Form']#,'(c) SUMMA-BE1 with enthalpy lookup'] #maybe make this an argument # Simulation statistics file locations settings= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime'] @@ -42,7 +42,7 @@ viz_fil = method_name.copy() for i, m in enumerate(method_name): viz_fil[i] = m + '_hrly_diff_bals_{}.nc' - viz_fil[i] = viz_fil[i].format(','.join(['balance','scaledBalance'])) + viz_fil[i] = viz_fil[i].format(','.join(['balance'])) do_rel = False # use scaled values nbatch_hrus = 518 # number of HRUs per batch From a7ed31bac14878c0523947aaa16127f18ba67fe2 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 25 Apr 2024 22:34:52 +0900 Subject: [PATCH 1284/1472] utils --- utils/bal_per_GRU.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/utils/bal_per_GRU.py b/utils/bal_per_GRU.py index fcf2b0e0f..27634e477 100644 --- a/utils/bal_per_GRU.py +++ b/utils/bal_per_GRU.py @@ -21,6 +21,7 @@ import pandas as pd viz_dir = Path('/home/avanb/scratch/statistics') +do_rel = True # plot relative to the benchmark simulation testing = False if testing: @@ -63,8 +64,10 @@ #fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' #fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) fig_fil = 'BalanceNrg_scat_{}_compressed.png' +if do_rel: fig_fil = 'BalanceNrg_scat__{}_rel_compressed.png' fig_fil = fig_fil.format(stat) fig_fil2 ='BalanceMass_scat_{}_compressed.png' +if do_rel: fig_fil2 = 'BalanceMass_scat__{}_rel_compressed.png' fig_fil2 =fig_fil2.format(stat) summa = {} @@ -82,20 +85,33 @@ def run_loop(i,var,comp,leg_t,leg_t0,plt_t,leg_w): if stat == 'mean': word = ' mean' stat1 = 'rmnz' - word1 = ' relative RMSE' + statr = 'mnnz_ben' + word1 = ' RMSE' if stat == 'amax': word = ' max' stat1 = 'maxe' + statr = 'amax_ben' word1 = ' max abs error' if comp == 'numberFluxCalc': stat1 = stat word1 = word # Data + if do_rel: s_rel = summa1[method_name[0]][var].sel(stat=statr) for m in method_name: # Get the statistics, remove 9999 (should be nan, but just in case) s0 = np.fabs(summa[m][comp].sel(stat=stat)).where(lambda x: x != 9999) s = np.fabs(summa1[m][var].sel(stat=stat1)).where(lambda x: x != 9999) + if do_rel and var != 'wallClockTime': s = s/s_rel + + if var == 'scalarTotalET' and not do_rel: + if stat1 =='rmse' or stat1 =='rmnz' : s = s*31557600 # make annual total + if stat1 =='maxe': s = s*3600 # make hourly max + if var == 'averageRoutedRunoff'and not do_rel: + if stat1 =='rmse' or stat1 =='rmnz' : s = s*31557600*1000 # make annual total + if stat1 =='maxe': s = s*3600*1000 # make hourly max + if stat1 == 'maxe': s.loc[dict(stat='maxe')] = np.fabs(s.loc[dict(stat='maxe')]) # make absolute value norm + axs[r,c].scatter(x=s.values,y=s0.values,s=10,zorder=0,label=m) @@ -113,6 +129,7 @@ def run_loop(i,var,comp,leg_t,leg_t0,plt_t,leg_w): #axs[r,c].set_xscale('log') if comp != 'numberFluxCalc': axs[r,c].set_yscale('log') axs[r,c].set_xlabel(stat_word + word1 + ' [{}]'.format(leg_t)) + if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + ' relative' + word1) axs[r,c].set_ylabel(stat0_word + word + ' [{}]'.format(leg_t0)) if 'compressed' in fig_fil: From 0f075cdcae4a91d4f2f442dc75684c6f399481b6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 25 Apr 2024 22:54:59 +0900 Subject: [PATCH 1285/1472] utils --- utils/bal_per_GRU.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/bal_per_GRU.py b/utils/bal_per_GRU.py index 27634e477..0d17f33b9 100644 --- a/utils/bal_per_GRU.py +++ b/utils/bal_per_GRU.py @@ -110,7 +110,7 @@ def run_loop(i,var,comp,leg_t,leg_t0,plt_t,leg_w): if var == 'averageRoutedRunoff'and not do_rel: if stat1 =='rmse' or stat1 =='rmnz' : s = s*31557600*1000 # make annual total if stat1 =='maxe': s = s*3600*1000 # make hourly max - if stat1 == 'maxe': s.loc[dict(stat='maxe')] = np.fabs(s.loc[dict(stat='maxe')]) # make absolute value norm + if stat1 == 'maxe': s.loc[dict(stat1='maxe')] = np.fabs(s.loc[dict(stat1='maxe')]) # make absolute value norm axs[r,c].scatter(x=s.values,y=s0.values,s=10,zorder=0,label=m) From 75070149935acbc5bd8457e81726fa5fb58dd2e0 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 25 Apr 2024 23:18:53 +0900 Subject: [PATCH 1286/1472] utils --- utils/bal_per_GRU.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/utils/bal_per_GRU.py b/utils/bal_per_GRU.py index 0d17f33b9..05bb5107d 100644 --- a/utils/bal_per_GRU.py +++ b/utils/bal_per_GRU.py @@ -25,7 +25,7 @@ testing = False if testing: - stat = 'mean' + stat = 'amax' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') method_name=['be1en'] #cm','be1en','be1lu'] #maybe make this an argument else: @@ -102,16 +102,16 @@ def run_loop(i,var,comp,leg_t,leg_t0,plt_t,leg_w): # Get the statistics, remove 9999 (should be nan, but just in case) s0 = np.fabs(summa[m][comp].sel(stat=stat)).where(lambda x: x != 9999) s = np.fabs(summa1[m][var].sel(stat=stat1)).where(lambda x: x != 9999) - if do_rel and var != 'wallClockTime': s = s/s_rel + if do_rel and var != 'wallClockTime': s = s/s_rel + if var == 'scalarTotalET' and not do_rel: if stat1 =='rmse' or stat1 =='rmnz' : s = s*31557600 # make annual total if stat1 =='maxe': s = s*3600 # make hourly max if var == 'averageRoutedRunoff'and not do_rel: if stat1 =='rmse' or stat1 =='rmnz' : s = s*31557600*1000 # make annual total if stat1 =='maxe': s = s*3600*1000 # make hourly max - if stat1 == 'maxe': s.loc[dict(stat1='maxe')] = np.fabs(s.loc[dict(stat1='maxe')]) # make absolute value norm - + if stat1 == 'maxe': s = np.fabs(s) # make absolute value norm axs[r,c].scatter(x=s.values,y=s0.values,s=10,zorder=0,label=m) From 510ada878bd772b080dac138ca23585a961d163d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 25 Apr 2024 23:24:13 +0900 Subject: [PATCH 1287/1472] utils --- utils/bal_per_GRU.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/bal_per_GRU.py b/utils/bal_per_GRU.py index 05bb5107d..9f6479a29 100644 --- a/utils/bal_per_GRU.py +++ b/utils/bal_per_GRU.py @@ -32,7 +32,7 @@ import sys # The first input argument specifies the run where the files are stat = sys.argv[1] - method_name=['be1','be1en','be1lu'] #maybe make this an argument + method_name=['be1','be1en']#,'be1lu'] #maybe make this an argument # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] @@ -57,7 +57,7 @@ leg_titl = ['mm~y^{-1}$','$kg~m^{-2}$','$kg~m^{-2}$','$kg~m^{-2}$','$s$'] leg_word = ['Total evapotranspiration', 'Total water on the vegetation canopy','Snow water equivalent','Total soil water content','Wall clock time'] -leg_titl2 =['$kg~m^{-2}$','$kg~m^{-2}$','$kg~m^{-2}$','mm~y^{-1}$'] +leg_titl2 =['$kg~m^{-2}$','$kg~m^{-2}$','$kg~m^{-2}$','$mm~y^{-1}$'] leg_word2 = ['Total water on the vegetation canopy','Snow water equivalent','Total soil water content','Average routed runoff'] From 4acf2710399858984d7b2d72e839e42906414b5c Mon Sep 17 00:00:00 2001 From: Kyle Klenk Date: Thu, 25 Apr 2024 21:28:05 +0000 Subject: [PATCH 1288/1472] Edit cmake lists file to reflect updates to summa-actors, and adjust parameters to match V3. I have added some new files to Summa-Actors and now leverage the restart and setup files from summa. I have also edited the order of paremeters for vegPhenlgy to match what is now a pull request in V3 --- build/cmake/CMakeLists.txt | 52 ++++++++++++++++---------- build/source/driver/summa_modelRun.f90 | 2 +- build/source/engine/coupled_em.f90 | 4 +- build/source/engine/vegPhenlgy.f90 | 6 +-- 4 files changed, 39 insertions(+), 25 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 596385c3c..2b392a2ee 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -80,7 +80,7 @@ else() # CMakeLists in ${summa-actors GIT directory}/build/summa/build/cmake set(PARENT_DIR ${F_MASTER}../../) # directory of summa actors source code - set(EXEC_DIR ${PARENT_DIR}/bin) + set(EXEC_DIR ${PARENT_DIR}/Summa-Actors/bin) else() project(summa DESCRIPTION "Summa-Sundials-BE") @@ -107,7 +107,7 @@ if(CMAKE_BUILD_TYPE MATCHES Debug) add_compile_definitions(DEBUG) set(FLAGS_NOAH -g -O0 -fbacktrace -fbounds-check -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) set(FLAGS_ALL -g -O0 -fbacktrace -fbounds-check -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp ${FLAGS_OPT}) - set(FLAGS_CXX -g -O0 -fbacktrace -fbounds-check -Wfatal-errors -std=c++17 ${FLAGS_OPT}) + set(FLAGS_CXX -g -O0 -fbounds-check -Wfatal-errors -std=c++17 ${FLAGS_OPT}) else() message("\nSetting Release Options") set(FLAGS_NOAH -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) @@ -243,6 +243,7 @@ set(NOAHMP_DIR ${F_MASTER}/build/source/noah-mp) # Define Actors specific directories set(ACTORS_DIR ${PARENT_DIR}/build/source) +set(SYS_INIT_DIR ${ACTORS_DIR}/system_initialization) set(FILE_ACCESS_DIR ${ACTORS_DIR}/file_access_actor) set(JOB_ACTOR_DIR ${ACTORS_DIR}/job_actor) set(HRU_ACTOR_DIR ${ACTORS_DIR}/hru_actor) @@ -323,7 +324,7 @@ set(PRELIM ${ENGINE_DIR}/checkStruc.f90 ${ENGINE_DIR}/childStruc.f90 ${ENGINE_DIR}/conv_funcs.f90 - ${SUB_ENGINE_DIR}/ffile_info.f90 + ${ENGINE_DIR}/ffile_info.f90 ${ENGINE_DIR}/read_pinit.f90 ${ENGINE_DIR}/read_attrb.f90 ${ENGINE_DIR}/paramCheck.f90 @@ -390,17 +391,17 @@ set(SOLVER_SUNDIALS # Driver support modules set(DRIVER + ${DRIVER_DIR}/summa_type.f90 + ${DRIVER_DIR}/summa_setup.f90 + ${DRIVER_DIR}/summa_restart.f90 ${DRIVER_DIR}/summa_alarms.f90 ${DRIVER_DIR}/summa_globalData.f90) set(DRIVER_NOT_ACTORS ${DRIVER_DIR}/summa_util.f90 - ${DRIVER_DIR}/summa_type.f90 ${DRIVER_DIR}/summa_defineOutput.f90 ${DRIVER_DIR}/summa_init.f90 ${DRIVER_DIR}/summa_forcing.f90 ${DRIVER_DIR}/summa_modelRun.f90 - ${DRIVER_DIR}/summa_restart.f90 - ${DRIVER_DIR}/summa_setup.f90 ${DRIVER_DIR}/summa_writeOutput.f90) set(DRIVER_NEXGEN ${DRIVER_DIR}/summa_bmi.f90) @@ -409,11 +410,16 @@ set(DRIVER_NEXGEN set(INTERFACE ${ACTORS_DIR}/global/cppwrap_auxiliary.f90 ${ACTORS_DIR}/global/cppwrap_datatypes.f90 - ${ACTORS_DIR}/global/cppwrap_metadata.f90) + ${ACTORS_DIR}/global/cppwrap_metadata.f90 + ${ACTORS_DIR}/global/c_interface_module.f90 + ${ACTORS_DIR}/global/gru_struc.f90) +set(SYS_INIT_INTERFACE + ${SYS_INIT_DIR}/batch_distributer_actor.f90) set(FILE_ACCESS_INTERFACE - ${FILE_ACCESS_DIR}/cppwrap_fileAccess.f90 + ${FILE_ACCESS_DIR}/summa_init_struc.f90 + ${FILE_ACCESS_DIR}/forcing_file_info.f90 + ${FILE_ACCESS_DIR}/file_access_actor.f90 ${FILE_ACCESS_DIR}/output_structure.f90 - ${FILE_ACCESS_DIR}/read_force.f90 ${FILE_ACCESS_DIR}/fileAccess_writeOutput.f90) set(JOB_INTERFACE ${JOB_ACTOR_DIR}/job_actor.f90) @@ -427,25 +433,32 @@ set(HRU_INTERFACE set(ACTORS_GLOBAL ${ACTORS_DIR}/global/auxiliary.cpp ${ACTORS_DIR}/global/global.cpp + ${ACTORS_DIR}/global/fileManager.cpp + ${ACTORS_DIR}/global/gru_struc.cpp ${ACTORS_DIR}/global/message_atoms.cpp ${ACTORS_DIR}/global/settings_functions.cpp ${ACTORS_DIR}/global/timing_info.cpp) -set(SUMMA_ACTOR - ${ACTORS_DIR}/summa_actor/batch.cpp - ${ACTORS_DIR}/summa_actor/batch_container.cpp - ${ACTORS_DIR}/summa_actor/client.cpp - ${ACTORS_DIR}/summa_actor/client_container.cpp - ${ACTORS_DIR}/summa_actor/summa_actor.cpp - ${ACTORS_DIR}/summa_actor/summa_backup_server.cpp - ${ACTORS_DIR}/summa_actor/summa_client.cpp - ${ACTORS_DIR}/summa_actor/summa_server.cpp) +set(SYS_INIT + ${SYS_INIT_DIR}/batch.cpp + ${SYS_INIT_DIR}/batch_container.cpp + ${SYS_INIT_DIR}/client.cpp + ${SYS_INIT_DIR}/client_container.cpp + ${SYS_INIT_DIR}/summa_global_data.cpp + ${SYS_INIT_DIR}/summa_actor.cpp + ${SYS_INIT_DIR}/summa_backup_server.cpp + ${SYS_INIT_DIR}/summa_client.cpp + ${SYS_INIT_DIR}/summa_server.cpp) set(FILE_ACCESS_ACTOR + ${FILE_ACCESS_DIR}/summa_init_struc.cpp ${ACTORS_DIR}/file_access_actor/file_access_actor.cpp ${ACTORS_DIR}/file_access_actor/forcing_file_info.cpp ${ACTORS_DIR}/file_access_actor/output_container.cpp) set(JOB_ACTOR ${ACTORS_DIR}/job_actor/GRU.cpp ${ACTORS_DIR}/job_actor/job_actor.cpp + ${ACTORS_DIR}/job_actor/async_mode.cpp + ${ACTORS_DIR}/job_actor/data_assimilation_mode.cpp + ${ACTORS_DIR}/job_actor/job_utils.cpp ${ACTORS_DIR}/job_actor/distributed_job_actor.cpp ${ACTORS_DIR}/job_actor/node_actor.cpp) set(HRU_ACTOR @@ -478,6 +491,7 @@ set(SUMMA_ALL if(CMAKE_BUILD_TYPE MATCHES Actors) set(SUMMA_ALL ${SUMMA_ALL} + ${SYS_INIT_INTERFACE} ${FILE_ACCESS_INTERFACE} ${JOB_INTERFACE} ${HRU_INTERFACE}) @@ -575,7 +589,7 @@ elseif(CMAKE_BUILD_TYPE MATCHES Actors) ${FILE_ACCESS_ACTOR} ${JOB_ACTOR} ${HRU_ACTOR} - ${SUMMA_ACTOR} + ${SYS_INIT} ${SUMMA_CLIENT} ${SUMMA_SERVER}) set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) diff --git a/build/source/driver/summa_modelRun.f90 b/build/source/driver/summa_modelRun.f90 index dae3e4b1b..d5c227b5a 100644 --- a/build/source/driver/summa_modelRun.f90 +++ b/build/source/driver/summa_modelRun.f90 @@ -132,10 +132,10 @@ subroutine summa_runPhysics(modelTimeStep, summa1_struc, err, message) ! (compute the exposed LAI and SAI and whether veg is buried by snow) call vegPhenlgy(& ! model control + model_decisions, & ! intent(in): model decisions fracJulDay, & ! intent(in): fractional julian days since the start of year yearLength, & ! intent(in): number of days in the current year ! input/output: data structures - model_decisions, & ! intent(in): model decisions typeStruct%gru(iGRU)%hru(iHRU), & ! intent(in): type of vegetation and soil attrStruct%gru(iGRU)%hru(iHRU), & ! intent(in): spatial attributes mparStruct%gru(iGRU)%hru(iHRU), & ! intent(in): model parameters diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 22a4aeda4..50cbc9438 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -491,10 +491,10 @@ subroutine coupled_em(& ! compute the exposed LAI and SAI and whether veg is buried by snow call vegPhenlgy(& ! model control + model_decisions, & ! intent(in): model decisions + ! input/output: data structures fracJulDay, & ! intent(in): fractional julian days since the start of year yearLength, & ! intent(in): number of days in the current year - ! input/output: data structures - model_decisions, & ! intent(in): model decisions type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes mpar_data, & ! intent(in): model parameters diff --git a/build/source/engine/vegPhenlgy.f90 b/build/source/engine/vegPhenlgy.f90 index 27ac7651d..12a483532 100644 --- a/build/source/engine/vegPhenlgy.f90 +++ b/build/source/engine/vegPhenlgy.f90 @@ -66,10 +66,10 @@ module vegPhenlgy_module ! ************************************************************************************************ subroutine vegPhenlgy(& ! model control + model_decisions, & ! intent(in): model decisions fracJulDay, & ! intent(in): fractional julian days since the start of year yearLength, & ! intent(in): number of days in the current year ! input/output: data structures - model_decisions, & ! intent(in): model decisions type_data, & ! intent(in): type of vegetation and soil attr_data, & ! intent(in): spatial attributes mpar_data, & ! intent(in): model parameters @@ -88,6 +88,8 @@ subroutine vegPhenlgy(& ! ------------------------------------------------------------------------------------------------- ! input/output type(model_options),intent(in) :: model_decisions(:) ! model decisions + real(rkind),intent(in) :: fracJulDay ! fractional julian days since the start of year + integer(i4b),intent(in) :: yearLength ! number of days in the current year type(var_i),intent(in) :: type_data ! type of vegetation and soil type(var_d),intent(in) :: attr_data ! spatial attributes type(var_dlength),intent(in) :: mpar_data ! model parameters @@ -97,8 +99,6 @@ subroutine vegPhenlgy(& logical(lgt),intent(out) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) real(rkind),intent(out) :: canopyDepth ! canopy depth (m) real(rkind),intent(out) :: exposedVAI ! exposed vegetation area index (LAI + SAI) - real(rkind),intent(in) :: fracJulDay ! fractional julian days since the start of year - integer(i4b),intent(in) :: yearLength ! number of days in the current year integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------- From 28be39aa6f9409abaa5b6ca240872bd8b566cbae Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 26 Apr 2024 10:52:17 +0900 Subject: [PATCH 1289/1472] utils --- utils/bal_per_GRU.py | 170 --------------------------------- utils/scat_per_GRU.py | 214 +++++++++++++++++++++++++----------------- 2 files changed, 130 insertions(+), 254 deletions(-) delete mode 100644 utils/bal_per_GRU.py diff --git a/utils/bal_per_GRU.py b/utils/bal_per_GRU.py deleted file mode 100644 index 9f6479a29..000000000 --- a/utils/bal_per_GRU.py +++ /dev/null @@ -1,170 +0,0 @@ -# written by A. Van Beusekom (2024) - -## Visualize statistics per GRU -## Needs: -# SUMMA output statistics - -## Special note -# SUMMA simulations have been preprocessed into single value statistics per model element, using auxiliary scripts in ~/utils -# Run: -# python steps_per_GRU.py [stat] -# where stat is mean or amax - -# modules -import os -import matplotlib -import numpy as np -import xarray as xr -from pathlib import Path -import matplotlib.pyplot as plt -import copy -import pandas as pd - -viz_dir = Path('/home/avanb/scratch/statistics') -do_rel = True # plot relative to the benchmark simulation - -testing = False -if testing: - stat = 'amax' - viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be1en'] #cm','be1en','be1lu'] #maybe make this an argument -else: - import sys - # The first input argument specifies the run where the files are - stat = sys.argv[1] - method_name=['be1','be1en']#,'be1lu'] #maybe make this an argument - -# Simulation statistics file locations -settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] -viz_fil = method_name.copy() -viz_fl2 = method_name.copy() -for i, m in enumerate(method_name): - viz_fl2[i] = m + '_hrly_diff_bals_{}.nc' - viz_fl2[i] = viz_fl2[i].format(','.join(['balance'])) - viz_fil[i] = m + '_hrly_diff_stats_{}.nc' - viz_fil[i] = viz_fil[i].format(','.join(settings)) - -# Specify variables of interest -plot_vars = ['scalarTotalET','scalarCanopyWat','scalarSWE','scalarTotalSoilWat','wallClockTime'] -plot_vars2 =['scalarCanopyWat','scalarSWE','scalarTotalSoilWat','averageRoutedRunoff'] -comp_vars = ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','numberFluxCalc'] -comp_vars2 =['balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass'] - -plt_titl = ['(a) Canopy Air Space Energy Balance','(b) Vegetation Energy Balance','(c) Snow Energy Balance','(d) Soil Energy Balance', '(e) Wall Clock Time',] -plt_titl2 = ['(a) Vegetation Mass Balance','(b) Snow Mass Balance','(c) Soil Mass Balance', '(d) Aquifer Mass Balance'] -leg_titl0 = ['$W~m^{-3}$'] * 4 +['$num$'] -leg_titl02 =['$kg~m^{-2}~s^{-1}$'] * 4 - -leg_titl = ['mm~y^{-1}$','$kg~m^{-2}$','$kg~m^{-2}$','$kg~m^{-2}$','$s$'] -leg_word = ['Total evapotranspiration', 'Total water on the vegetation canopy','Snow water equivalent','Total soil water content','Wall clock time'] -leg_titl2 =['$kg~m^{-2}$','$kg~m^{-2}$','$kg~m^{-2}$','$mm~y^{-1}$'] -leg_word2 = ['Total water on the vegetation canopy','Snow water equivalent','Total soil water content','Average routed runoff'] - - -#fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' -#fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) -fig_fil = 'BalanceNrg_scat_{}_compressed.png' -if do_rel: fig_fil = 'BalanceNrg_scat__{}_rel_compressed.png' -fig_fil = fig_fil.format(stat) -fig_fil2 ='BalanceMass_scat_{}_compressed.png' -if do_rel: fig_fil2 = 'BalanceMass_scat__{}_rel_compressed.png' -fig_fil2 =fig_fil2.format(stat) - -summa = {} -summa1 = {} -for i, m in enumerate(method_name): - # Get the aggregated statistics of SUMMA simulations - summa[m] = xr.open_dataset(viz_dir/viz_fl2[i]) - summa1[m] = xr.open_dataset(viz_dir/viz_fil[i]) - -##Figure - -def run_loop(i,var,comp,leg_t,leg_t0,plt_t,leg_w): - r = i//2 - c = i-r*2 - if stat == 'mean': - word = ' mean' - stat1 = 'rmnz' - statr = 'mnnz_ben' - word1 = ' RMSE' - if stat == 'amax': - word = ' max' - stat1 = 'maxe' - statr = 'amax_ben' - word1 = ' max abs error' - if comp == 'numberFluxCalc': - stat1 = stat - word1 = word - - # Data - if do_rel: s_rel = summa1[method_name[0]][var].sel(stat=statr) - for m in method_name: - # Get the statistics, remove 9999 (should be nan, but just in case) - s0 = np.fabs(summa[m][comp].sel(stat=stat)).where(lambda x: x != 9999) - s = np.fabs(summa1[m][var].sel(stat=stat1)).where(lambda x: x != 9999) - - if do_rel and var != 'wallClockTime': s = s/s_rel - - if var == 'scalarTotalET' and not do_rel: - if stat1 =='rmse' or stat1 =='rmnz' : s = s*31557600 # make annual total - if stat1 =='maxe': s = s*3600 # make hourly max - if var == 'averageRoutedRunoff'and not do_rel: - if stat1 =='rmse' or stat1 =='rmnz' : s = s*31557600*1000 # make annual total - if stat1 =='maxe': s = s*3600*1000 # make hourly max - if stat1 == 'maxe': s = np.fabs(s) # make absolute value norm - - axs[r,c].scatter(x=s.values,y=s0.values,s=10,zorder=0,label=m) - - if comp == 'numberFluxCalc': - stat0_word = 'Number flux calculations' - else: - stat0_word = 'Balance abs value' - - stat_word = leg_w - - lgnd = axs[r,c].legend() - for j, m in enumerate(method_name): - lgnd.legendHandles[j]._sizes = [80] - #axs[r,c].set_title(plt_t) - #axs[r,c].set_xscale('log') - if comp != 'numberFluxCalc': axs[r,c].set_yscale('log') - axs[r,c].set_xlabel(stat_word + word1 + ' [{}]'.format(leg_t)) - if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + ' relative' + word1) - axs[r,c].set_ylabel(stat0_word + word + ' [{}]'.format(leg_t0)) - -if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 25}) -else: - plt.rcParams.update({'font.size': 100}) - -if 'compressed' in fig_fil: - fig,axs = plt.subplots(3,2,figsize=(35,38)) -else: - fig,axs = plt.subplots(3,2,figsize=(140,160)) -fig.subplots_adjust(hspace=0.24, wspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space -#fig.suptitle('Scatterplot of Hourly Statistics for each GRU', fontsize=40,y=1.0) - -for i,(var,comp,leg_t,leg_t0,plt_t,leg_w) in enumerate(zip(plot_vars,comp_vars,leg_titl,leg_titl0,plt_titl,leg_word)): - run_loop(i,var,comp,leg_t,leg_t0,plt_t,leg_w) - -# Save -plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) - - -if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 25}) -else: - plt.rcParams.update({'font.size': 100}) - -if 'compressed' in fig_fil: - fig,axs = plt.subplots(3,2,figsize=(35,38)) -else: - fig,axs = plt.subplots(3,2,figsize=(140,160)) -fig.subplots_adjust(hspace=0.24, wspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space -#fig.suptitle('Scatterplot of Hourly Statistics for each GRU', fontsize=40,y=1.0) - -for i,(var,comp,leg_t,leg_t0,plt_t,leg_w) in enumerate(zip(plot_vars2,comp_vars2,leg_titl2,leg_titl02,plt_titl2,leg_word2)): - run_loop(i,var,comp,leg_t,leg_t0,plt_t,leg_w) - -# Save -plt.savefig(viz_dir/fig_fil2, bbox_inches='tight', transparent=False) diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index 32b5852fc..09e50f0be 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -22,87 +22,68 @@ viz_dir = Path('/home/avanb/scratch/statistics') nbatch_hrus = 518 # number of HRUs per batch -use_eff = False # use efficiency in wall clock time, still need files for the node number do_rel = True # plot relative to the benchmark simulation +# which statistics to plot +do_vars = True +do_balance = True + testing = False if testing: - stat = 'maxe' + stat = 'rmnz' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be1','sundials_1en6'] #maybe make this an argument - plt_name=['BE1','IDAe-6'] #maybe make this an argument + method_name=['be1en'] else: import sys # The first input argument specifies the run where the files are stat = sys.argv[1] #method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument #plt_name=['BE1','IDAe-4','BE4','BE8','BE16','BE32','IDAe-6'] #maybe make this an argument - method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument - plt_name=['BE1','BE16','BE32','IDAe-6'] #maybe make this an argument + #method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument + #plt_name=['BE1','BE16','BE32','IDAe-6'] #maybe make this an argument + method_name=['be1','be1en'] + plt_name=['BE1 temp','BE1 mixed'] if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] viz_fil = method_name.copy() -eff_fil = method_name.copy() +viz_fl2 = method_name.copy() for i, m in enumerate(method_name): viz_fil[i] = m + '_hrly_diff_stats_{}.nc' viz_fil[i] = viz_fil[i].format(','.join(settings)) - eff_fil[i] = 'eff_' + m + '.txt' - -# Specify variables of interest -plot_vars = settings.copy() -plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] -leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$num$'] -leg_titl0 = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] -leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$','$num$'] - -fig_fil = 'Hrly_diff_scat_{}_{}_compressed.png' -if do_rel: fig_fil = 'Hrly_diff_scat_{}_{}_rel_compressed.png' -fig_fil = fig_fil.format(','.join(settings),stat) + viz_fl2[i] = m + '_hrly_diff_bals_{}.nc' + viz_fl2[i] = viz_fl2[i].format(','.join(['balance'])) summa = {} -eff = {} +summa1 = {} for i, m in enumerate(method_name): # Get the aggregated statistics of SUMMA simulations summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) - # Read the data from the eff.txt file into a DataFrame - eff[m] = pd.read_csv(viz_dir/eff_fil[i], sep=',', header=None, names=['CPU Efficiency', 'Array ID', 'Job Wall-clock time', 'Node Number']) - # Extract only the values after the ':' character in the 'CPU Efficiency', 'Job Wall-clock time', and 'Node Number' columns - eff[m]['CPU Efficiency'] = eff[m]['CPU Efficiency'].str.split(':').str[1].astype(float) - eff[m]['Array ID'] = eff[m]['Array ID'].str.split(':').str[1].astype(int) - eff[m]['Job Wall-clock time'] = eff[m]['Job Wall-clock time'].str.split(':').str[1].astype(float) - eff[m]['Node Number'] = eff[m]['Node Number'].str.split(':').str[1].astype(int) + summa1[m] = xr.open_dataset(viz_dir/viz_fl2[i]) -##Figure - -# Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug -if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 25}) -else: - plt.rcParams.update({'font.size': 100}) - -if 'compressed' in fig_fil: - fig,axs = plt.subplots(3,2,figsize=(31,33)) -else: - fig,axs = plt.subplots(3,2,figsize=(140,133)) -#fig.suptitle('Hourly Errors and Values for each GRU', fontsize=40) -fig.subplots_adjust(hspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space +def run_loop(i,var,plt_t,leg_t,leg_t0,leg_tm): - -def run_loop(i,var): r = i//2 c = i-r*2 if stat == 'rmse' or stat == 'kgem': stat0 = 'mean' + stat0_word = 'mean' statr = 'mean_ben' + stat_word = 'RMSE' + if stat == 'kgem': stat_word = 'KGE"' + if stat == 'rmnz': stat0 = 'mnnz' + stat0_word = 'mean' # no 0s' statr = 'mnnz_ben' + stat_word = 'RMSE' # no 0s' if stat == 'maxe': stat0 = 'amax' + stat0_word = 'max' statr = 'amax_ben' + stat_word = 'max abs error' # Data if do_rel: s_rel = summa[method_name[0]][var].sel(stat=statr) @@ -118,55 +99,120 @@ def run_loop(i,var): if stat =='maxe': s = s*3600*1000 # make hourly max if stat == 'maxe': s.loc[dict(stat='maxe')] = np.fabs(s.loc[dict(stat='maxe')]) # make absolute value norm - if var == 'wallClockTime': - batch = np.floor(np.arange(len(s.indexes['hru'])) /nbatch_hrus) - #basin_num = np.arange(len(s.indexes['hru'])) % nbatch_hrus #not currently using - # Create a dictionary to store the values for each batch - efficiency = {} - node = {} - # Iterate over the rows in the data DataFrame - for index, row in eff[m].iterrows(): - # Extract the values from the row - batch0 = int(row['Array ID']) - eff0 = row['CPU Efficiency'] - node0 = row['Node Number'] - # Store the value for the current batch in the dictionary - efficiency[batch0] = eff0 - node[batch0] = node0 - # Select the values for the current batch using boolean indexing - eff_batch = np.array([efficiency[b] for b in batch]) - node_batch = np.array([node[b] for b in batch]) #not currently using - # Multiply the s values by efficiency - if use_eff: s = s*eff_batch - axs[r,c].scatter(x=node_batch,y=s.sel(stat=stat0),s=1,zorder=0,label=m) - stat_word = 'Node number' - else: - axs[r,c].scatter(x=np.fabs(s.sel(stat=stat).values),y=s.sel(stat=stat0).values,s=1,zorder=0,label=m) - if stat == 'rmse': stat_word = 'RMSE' - if stat == 'rmnz': stat_word = 'RMSE' # no 0s' - if stat == 'maxe': stat_word = 'max abs error' - if stat == 'kgem': stat_word = 'KGE"' + axs[r,c].scatter(x=np.fabs(s.sel(stat=stat).values),y=s.sel(stat=stat0).values,s=1,zorder=0,label=m) - if stat0 == 'mean': stat0_word = 'mean' - if stat0 == 'mnnz': stat0_word = 'mean' # no 0s' - if stat0 == 'amax': stat0_word = 'max' - - lgnd = axs[r,c].legend(plt_name) + lgnd = axs[r,c].legend() for j, m in enumerate(method_name): lgnd.legendHandles[j]._sizes = [80] - axs[r,c].set_title(plt_titl[i]) - if stat == 'rmse' or stat == 'rmnz': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl[i])) - if stat == 'maxe': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titlm[i])) + axs[r,c].set_title(plt_t) + if stat == 'rmse' or stat == 'rmnz': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_t)) + if stat == 'maxe': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_tm)) if stat == 'kgem': axs[r,c].set_xlabel(stat_word) #if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + ' rel to bench ' + stat0_word) if do_rel and var!='wallClockTime': axs[r,c].set_xlabel('relative '+ stat_word) - axs[r,c].set_ylabel(stat0_word + ' [{}]'.format(leg_titl0[i])) + axs[r,c].set_ylabel(stat0_word + ' [{}]'.format(leg_t0)) #if do_rel and var!='wallClockTime': axs[r,c].set_ylabel(stat0_word + ' rel to bench ' + stat0_word) if do_rel and var!='wallClockTime': axs[r,c].set_ylabel('relative '+ stat0_word) -for i,var in enumerate(plot_vars): - run_loop(i,var) -# Save -plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) +def run_loopb(i,var,comp,leg_t,leg_t0,plt_t): + r = i//2 + c = i-r*2 + + if stat == 'rmse' or stat == 'kgem': + stat0 = 'mean' + word = ' mean' + if stat == 'rmnz': + stat0 = 'mean' + word = ' mean' # no 0s' + if stat == 'maxe': + stat0 = 'amax' + word = ' max' + + # Data + for m in method_name: + # Get the statistics, remove 9999 (should be nan, but just in case) + s0 = np.fabs(summa1[m][comp].sel(stat=stat0)).where(lambda x: x != 9999) + s = np.fabs(summa1[m][var].sel(stat=stat0)).where(lambda x: x != 9999) + + axs[r,c].scatter(x=s.values,y=s0.values,s=10,zorder=0,label=m) + + if comp == 'numberFluxCalc': + stat0_word = 'Number flux calculations' + stat_word = 'Wall clock time' + else: + stat0_word = 'Balance abs value' + stat_word = 'Balance abs value' + + lgnd = axs[r,c].legend() + for j, m in enumerate(method_name): + lgnd.legendHandles[j]._sizes = [80] + axs[r,c].set_title(plt_t) + axs[r,c].set_xscale('log') + if comp != 'numberFluxCalc': axs[r,c].set_yscale('log') + axs[r,c].set_xlabel(stat_word + word + ' [{}]'.format(leg_t)) + axs[r,c].set_ylabel(stat0_word + word + ' [{}]'.format(leg_t0)) + + +if do_vars: + + plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff'] + leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$'] + leg_titl0 = ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$'] + leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$'] + + fig_fil = 'Hrly_diff_scat_{}_{}_compressed.png' + fig_fil = fig_fil.format(','.join(settings),stat) + + # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug + if 'compressed' in fig_fil: + plt.rcParams.update({'font.size': 25}) + else: + plt.rcParams.update({'font.size': 100}) + + if 'compressed' in fig_fil: + fig,axs = plt.subplots(3,2,figsize=(31,33)) + else: + fig,axs = plt.subplots(3,2,figsize=(140,133)) + #fig.suptitle('Hourly Errors and Values for each GRU', fontsize=40) + fig.subplots_adjust(hspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space + + # Specify variables of interest + plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff'] + for i,(var,plt_t,leg_t,leg_t0,leg_tm) in enumerate(zip(plot_vars,plt_titl,leg_titl,leg_titl0,leg_titlm)): + run_loop(i,var,plt_t,leg_t,leg_t0,leg_tm) + + # Save + plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) + +if do_balance: +# Specify variables of interest + plot_vars = ['balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceCasNrg','wallClockTime'] + comp_vars = ['balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','numberFluxCalc'] + + plt_titl = ['(a) Vegetation Balance','(b) Snow Balance','(c) Soil Balance', '(d) Canopy Air Space and Aquifer Balance', '(f) Wall Clock Time'] + leg_titl = ['$W~m^{-3}$'] * 4 + ['$s$'] + leg_titl0 =['$kg~m^{-2}~s^{-1}$'] * 4 + ['$num$'] + + fig_fil = 'balance_scat_{}_compressed.png' + if do_rel: fig_fil = 'balance_scat_{}_rel_compressed.png' + fig_fil = fig_fil.format(stat) + + if 'compressed' in fig_fil: + plt.rcParams.update({'font.size': 25}) + else: + plt.rcParams.update({'font.size': 100}) + + if 'compressed' in fig_fil: + fig,axs = plt.subplots(3,2,figsize=(35,38)) + else: + fig,axs = plt.subplots(3,2,figsize=(140,160)) + fig.subplots_adjust(hspace=0.24, wspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space + #fig.suptitle('Scatterplot of Hourly Statistics for each GRU', fontsize=40,y=1.0) + + for i,(var,comp,leg_t,leg_t0,plt_t) in enumerate(zip(plot_vars,comp_vars,leg_titl,leg_titl0,plt_titl)): + run_loopb(i,var,comp,leg_t,leg_t0,plt_t) + + # Save + plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) From d0044eb59d18e3e04fa4d73fe454793dacff6987 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 27 Apr 2024 00:11:52 +0900 Subject: [PATCH 1290/1472] utils --- utils/hist_per_GRU.py | 49 ++++----------------- utils/largest_error_attrib.py | 20 ++++++--- utils/plot_per_GRUMult.py | 82 ++++++++++++++--------------------- utils/scat_per_GRU.py | 3 +- 4 files changed, 57 insertions(+), 97 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 8489038ba..4f7ed473d 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -23,7 +23,6 @@ viz_dir = Path('/home/avanb/scratch/statistics') nbatch_hrus = 518 # number of HRUs per batch num_bins = 1000 -use_eff = False # use efficiency in wall clock time do_rel = True # plot relative to the benchmark simulation testing = False @@ -31,16 +30,18 @@ if testing: stat = 'rmnz' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be1','sundials_1en6'] #maybe make this an argument - plt_name=['BE1','IDAe-6'] #maybe make this an argument + method_name=['be1en'] + plt_name=['BE1 mixed'] else: import sys # The first input argument specifies the run where the files are stat = sys.argv[1] #method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument #plt_name=['BE1','IDAe-4','BE4','BE8','BE16','BE32','IDAe-6'] #maybe make this an argument - method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument - plt_name=['BE1','BE16','BE32','SUNDIALS'] #maybe make this an argument + #method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument + #plt_name=['BE1','BE16','BE32','SUNDIALS'] #maybe make this an argument + method_name=['be1','be1en'] + plt_name=['BE1 temp','BE1 mixed'] if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE # Define the power transformation function @@ -48,16 +49,14 @@ def power_transform(x): return x ** 0.5 # Adjust the exponent as needed # Simulation statistics file locations -use_vars = [1,2,4,5] +use_vars = [1,2,3,4,5] settings0= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] settings = [settings0[i] for i in use_vars] viz_fil = method_name.copy() -eff_fil = method_name.copy() for i, m in enumerate(method_name): viz_fil[i] = m + '_hrly_diff_stats_{}.nc' viz_fil[i] = viz_fil[i].format(','.join(settings0)) - eff_fil[i] = 'eff_' + m + '.txt' # Specify variables of interest plot_vars = settings.copy() @@ -90,18 +89,9 @@ def power_transform(x): maxes = [maxes[i] for i in use_vars] summa = {} -eff = {} for i, m in enumerate(method_name): # Get the aggregated statistics of SUMMA simulations summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) - if use_eff: - # Read the data from the eff.txt file into a DataFrame - eff[m] = pd.read_csv(viz_dir/eff_fil[i], sep=',', header=None, names=['CPU Efficiency', 'Array ID', 'Job Wall-clock time', 'Node Number']) - # Extract only the values after the ':' character in the 'CPU Efficiency', 'Job Wall-clock time', and 'Node Number' columns - eff[m]['CPU Efficiency'] = eff[m]['CPU Efficiency'].str.split(':').str[1].astype(float) - eff[m]['Array ID'] = eff[m]['Array ID'].str.split(':').str[1].astype(int) - eff[m]['Job Wall-clock time'] = eff[m]['Job Wall-clock time'].str.split(':').str[1].astype(float) - eff[m]['Node Number'] = eff[m]['Node Number'].str.split(':').str[1].astype(int) ##Figure @@ -151,27 +141,6 @@ def run_loop(i,var,mx): s = summa[m][var].sel(stat=stat0) if do_rel and var != 'wallClockTime': s = s/s_rel - if var == 'wallClockTime' and use_eff: - batch = np.floor(np.arange(len(s.indexes['hru'])) /nbatch_hrus) - #basin_num = np.arange(len(s.indexes['hru'])) % nbatch_hrus #not currently using - # Create a dictionary to store the values for each batch - efficiency = {} - node = {} - # Iterate over the rows in the data DataFrame - for index, row in eff[m].iterrows(): - # Extract the values from the row - batch0 = int(row['Array ID']) - eff0 = row['CPU Efficiency'] - node0 = row['Node Number'] - # Store the value for the current batch in the dictionary - efficiency[batch0] = eff0 - node[batch0] = node0 - # Select the values for the current batch using boolean indexing - eff_batch = np.array([efficiency[b] for b in batch]) - #node_batch = np.array([node[b] for b in batch]) #not currently using - # Multiply the s values by efficiency - s = s*eff_batch - if var == 'scalarTotalET' and not do_rel: if stat =='rmse' or stat =='rmnz' : s = s*31557600 # make annual total if stat =='maxe': s = s*3600 # make hourly max @@ -208,14 +177,12 @@ def run_loop(i,var,mx): if stat == 'rmse' or stat == 'rmnz': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl[i])) if stat == 'maxe': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titlm[i])) if stat == 'kgem': axs[r,c].set_xlabel(stat_word) - #if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + ' rel to bench ' + statr_word) if do_rel and var!='wallClockTime': axs[r,c].set_xlabel('relative '+ stat_word) if do_hist: axs[r,c].set_ylabel('GRU count') if var != 'wallClockTime' and not testing: axs[r,c].set_ylim([0, 25000]) - #if var == 'wallClockTime': axs[r,c].set_yscale('log') #log y axis for wall clock time to exaggerate peaks - + else: axs[r,c].set_ylabel('cumulative distribution') axs[r,c].set_ylim([0.0, 1.0]) diff --git a/utils/largest_error_attrib.py b/utils/largest_error_attrib.py index 37ad450ad..92ea8a89e 100644 --- a/utils/largest_error_attrib.py +++ b/utils/largest_error_attrib.py @@ -95,16 +95,24 @@ # Mask the HRU variable from the netCDF file mask = attr['hruId'].isin(hru_big) + # Filtered HRU IDs + filtered_hru_ids = attr['hruId'][mask] + + # Determine the indices that would sort filtered_hru_ids to match the order of hru_big + h_ind = [filtered_hru_ids.values.tolist().index(hru_id) for hru_id in hru_big if hru_id in filtered_hru_ids.values] + h = attr['hruId'][mask].values[h_ind] + # Get the vegTypeIndex, lat, lon variables from the netCDF file - vegType_big = attr['vegTypeIndex'][mask] - lat_big = attr['latitude'][mask] - lon_big = attr['longitude'][mask] + vegType_big = attr['vegTypeIndex'][mask].values[h_ind] + lat_big = attr['latitude'][mask].values[h_ind] + lon_big = attr['longitude'][mask].values[h_ind] # Print the attributes of the largest nBig values + print(" hryhhh : [", " ".join([f"{val:8d}" for val in h]), "]", sep="") print("HRU vals: [", " ".join([f"{val:8d}" for val in hru_big]), "]", sep="") - print("vegType : [", " ".join([f"{val:8d}" for val in vegType_big.values]), "]", sep="") - print("latitude: [", " ".join([f"{val:8.2f}" for val in lat_big.values]), "]", sep="") - print("longitud: [", " ".join([f"{val:8.2f}" for val in lon_big.values]), "]", sep="") + print("vegType : [", " ".join([f"{val:8d}" for val in vegType_big]), "]", sep="") + print("latitude: [", " ".join([f"{val:8.2f}" for val in lat_big]), "]", sep="") + print("longitud: [", " ".join([f"{val:8.2f}" for val in lon_big]), "]", sep="") # Print the values of the largest nBig values, bench will be the mean, mnnz, or amax and err will be the rmse, rmnz, or maxe if do_rel: diff --git a/utils/plot_per_GRUMult.py b/utils/plot_per_GRUMult.py index e21a127e7..9b1ad0874 100644 --- a/utils/plot_per_GRUMult.py +++ b/utils/plot_per_GRUMult.py @@ -32,20 +32,21 @@ # plot all runs, pick statistic stat = sys.argv[1] -method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument -plt_name=['(a) SUMMA-BE1','(b) SUMMA-BE16','(c) SUMMA-BE32','(d) SUMMA-SUNDIALS'] #maybe make this an argument +#method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument +#plt_name=['(a) SUMMA-BE1','(b) SUMMA-BE16','(c) SUMMA-BE32','(d) SUMMA-SUNDIALS'] #maybe make this an argument +method_name=['be1','be1en','sundials_1en6','diff']#,'be1lu'] #maybe make this an argument +plt_name=['(a) SUMMA-BE1 Energy Temperature Form','(b) SUMMA-BE1 Energy Mixed Form','(c) SUMMA-SUNDIALS','(d) SUMMA-BE1 Mixed Form - Temperature Form']#, #maybe make this an argument +from_meth = 'be1en' # index of the first simulation in the difference simulation, only used if a method_name is 'diff' +sub_meth = 'be1' # index of the simulation to subtract in the difference simulation, only used if a method_name is 'diff' # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] viz_dir = Path('/home/avanb/scratch/statistics') viz_fil = method_name.copy() -eff_fil = method_name.copy() for i, m in enumerate(method_name): viz_fil[i] = m + '_hrly_diff_stats_{}.nc' viz_fil[i] = viz_fil[i].format(','.join(settings)) - eff_fil[i] = 'eff_' + m + '.txt' nbatch_hrus = 518 # number of HRUs per batch -use_eff = False # use efficiency in wall clock time do_rel = True # plot relative to the benchmark simulation if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE @@ -176,18 +177,9 @@ def make_default_path(suffix): ## Pre-processing, map SUMMA sims to catchment shapes # Get the aggregated statistics of SUMMA simulations summa = {} -eff = {} for i, m in enumerate(method_name): # Get the aggregated statistics of SUMMA simulations - summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) - if use_eff: - # Read the data from the eff.txt file into a DataFrame - eff[m] = pd.read_csv(viz_dir/eff_fil[i], sep=',', header=None, names=['CPU Efficiency', 'Array ID', 'Job Wall-clock time', 'Node Number']) - # Extract only the values after the ':' character in the 'CPU Efficiency', 'Job Wall-clock time', and 'Node Number' columns - eff[m]['CPU Efficiency'] = eff[m]['CPU Efficiency'].str.split(':').str[1].astype(float) - eff[m]['Array ID'] = eff[m]['Array ID'].str.split(':').str[1].astype(int) - eff[m]['Job Wall-clock time'] = eff[m]['Job Wall-clock time'].str.split(':').str[1].astype(float) - eff[m]['Node Number'] = eff[m]['Node Number'].str.split(':').str[1].astype(int) + if m!='diff': summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) # Match the accummulated values to the correct HRU IDs in the shapefile hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile @@ -203,32 +195,14 @@ def make_default_path(suffix): if plot_var == 'wallClockTime': stat0 = 'amax' statr = 'amax_ben' - if do_rel: s_rel = summa[method_name[0]][plot_var].sel(stat=statr) + if do_rel: s_rel = np.fabs(summa[method_name[0]][plot_var].sel(stat=statr)) for m in method_name: - s = summa[m][plot_var].sel(stat=stat0) + if m=='diff': + s = np.fabs(summa[from_meth][plot_var].sel(stat=stat0)) - np.fabs(summa[sub_meth][plot_var].sel(stat=stat0)) + else: + s = np.fabs(summa[m][plot_var].sel(stat=stat0)) if do_rel and plot_var != 'wallClockTime': s = s/s_rel - if plot_var == 'wallClockTime' and use_eff: - batch = np.floor(np.arange(len(s.indexes['hru'])) /nbatch_hrus) - #basin_num = np.arange(len(s.indexes['hru'])) % nbatch_hrus #not currently using - # Create a dictionary to store the values for each batch - efficiency = {} - # Iterate over the rows in the data DataFrame - for index, row in eff.iterrows(): - # Extract the values from the row - batch0 = int(row['Array ID']) - eff0 = row['CPU Efficiency'] - # Store the value for the current batch in the dictionary - efficiency[batch0] = eff0 - # Select the values for the current batch using boolean indexing - eff_batch = np.array([efficiency[b] for b in batch]) - #node_batch = np.array([node[b] for b in batch]) #not currently using - # Multiply the s values by efficiency - s = s*eff_batch - - # Make absolute value norm, not all positive - s = np.fabs(s) - # Replace inf values with NaN in the s DataArray s = s.where(~np.isinf(s), np.nan) @@ -270,7 +244,7 @@ def run_loop(j,var,the_max): if stat =='mean' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 700, the_max if stat =='amax' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 1000, the_max if (stat == 'mean' or stat == 'mnnz' or stat == 'amax') and var!='wallClockTime' and do_rel: vmin,vmax = 0.9, the_max - + norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) if stat =='kgem' and var!='wallClockTime': my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno')) # copy the default cmap @@ -278,6 +252,11 @@ def run_loop(j,var,the_max): vmin,vmax = the_max, 1.0 norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=1.5) + my_cmap2 = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap + my_cmap2.set_bad(color='white') #nan color white + vmin,vmax = -the_max/10, the_max/4 + norm2 = matplotlib.colors.TwoSlopeNorm(vmin=vmin, vcenter=0, vmax=vmax) + if stat0 == 'rmse': stat_word = 'RMSE' if stat0 == 'rmnz': stat_word = 'RMSE' # no 0s' if stat0 == 'maxe': stat_word = 'max abs error' @@ -289,7 +268,7 @@ def run_loop(j,var,the_max): if statr == 'mean_ben': statr_word = 'mean' if statr == 'mnnz_ben': statr_word = 'mean' # no 0s' if statr == 'amax_ben': statr_word = 'max' - + # colorbar axes f_x_mat = [0.46,0.96,0.46,0.96] f_y_mat = [0.55,0.55,0.07,0.07] @@ -299,7 +278,12 @@ def run_loop(j,var,the_max): c = i-r*2 # Plot the data with the full extent of the bas_albers shape - bas_albers.plot(ax=axs[r,c], column=var+m, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) + if m=='diff': + bas_albers.plot(ax=axs[r,c], column=var+m, edgecolor='none', legend=False, cmap=my_cmap2, norm=norm2,zorder=0) + stat_word0 = stat_word+' difference' + else: + bas_albers.plot(ax=axs[r,c], column=var+m, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) + stat_word0 = stat_word axs[r,c].set_title(plt_name[i]) axs[r,c].axis('off') @@ -308,15 +292,15 @@ def run_loop(j,var,the_max): # Custom colorbar cax = fig.add_axes([f_x,f_y,0.02,0.375]) - sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) + if m=='diff': + sm = matplotlib.cm.ScalarMappable(cmap=my_cmap2, norm=norm2) + else: + sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) sm._A = [] - cbr = fig.colorbar(sm, cax=cax) #, extend='max') #if max extend can't get title right - if stat == 'rmse' or stat == 'rmnz' or stat == 'mean' or stat == 'maxe' or stat == 'amax': cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titl[j]), labelpad=40, rotation=270) - if stat == 'kgem': cbr.ax.set_ylabel(stat_word, labelpad=40, rotation=270) - #if do_rel and var!='wallClockTime': cbr.ax.set_ylabel(stat_word + ' rel to bench ' + statr_word, labelpad=40, rotation=270) - if do_rel and var!='wallClockTime': cbr.ax.set_ylabel('relative '+ stat_word, labelpad=40, rotation=270) - - #cbr.ax.yaxis.set_offset_position('right') + cbr = fig.colorbar(sm, cax=cax) + if stat == 'rmse' or stat == 'rmnz' or stat == 'mean' or stat == 'maxe' or stat == 'amax': cbr.ax.set_ylabel(stat_word0 + ' [{}]'.format(leg_titl[j]), labelpad=40, rotation=270) + if stat == 'kgem': cbr.ax.set_ylabel(stat_word0, labelpad=40, rotation=270) + if do_rel and var!='wallClockTime': cbr.ax.set_ylabel('relative '+ stat_word0, labelpad=40, rotation=270) # lakes if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index 09e50f0be..68bad159d 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -22,7 +22,7 @@ viz_dir = Path('/home/avanb/scratch/statistics') nbatch_hrus = 518 # number of HRUs per batch -do_rel = True # plot relative to the benchmark simulation +do_rel = False # plot relative to the benchmark simulation # which statistics to plot do_vars = True @@ -33,6 +33,7 @@ stat = 'rmnz' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') method_name=['be1en'] + plt_name=['BE1 mixed'] else: import sys # The first input argument specifies the run where the files are From 2b8fee55fbe8ddb89af5fab5725238e79956fd53 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 29 Apr 2024 17:02:48 +0900 Subject: [PATCH 1291/1472] comment clafication --- build/source/engine/summaSolve4ida.f90 | 1 + build/source/engine/varSubstep.f90 | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 977282fad..76c31cf8f 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -526,6 +526,7 @@ subroutine summaSolve4ida(& if(computNrgBalance)then ! compute energy balance mean, resVec is the instantaneous residual vector from the solver + ! note, if needCm and/or updateCp are false in eval8summaWithPrime, then the energy balance is not accurate if(ixCasNrg/=integerMissing) balance(ixCasNrg) = balance(ixCasNrg) + ( eqns_data%resVec(ixCasNrg) + resVecPrev(ixCasNrg) )*dt_diff/2._rkind/dt if(ixVegNrg/=integerMissing) balance(ixVegNrg) = balance(ixVegNrg) + ( eqns_data%resVec(ixVegNrg) + resVecPrev(ixVegNrg) )*dt_diff/2._rkind/dt if(nSnowSoilNrg>0)then diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 12363c19d..5a163c5f0 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -197,7 +197,7 @@ subroutine varSubstep(& real(rkind) :: balance(in_varSubstep % nSubset) ! substep balance per second real(rkind) :: sumBalance(in_varSubstep % nSubset) ! sum of substeps balance logical(lgt),parameter :: computMassBalance = .true. ! flag to compute the mass balance, will affect step length, default true - logical(lgt),parameter :: computNrgBalance = .true. ! flag to compute the energy balance, will not effect solution but will not compute nrg balance if false (saves expense) + logical(lgt),parameter :: computNrgBalance = .true. ! flag to compute the energy balance, will not effect solution but will not compute energy balance if false (saves expense) logical(lgt) :: computeEnthTemp ! flag to compute enthalpy regardless of the model decision logical(lgt) :: enthalpyStateVec ! flag if enthalpy is a state variable (ida) logical(lgt) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use analytical solution From 00bcd1a574f44be9c9f8232e44ca96ebb6e7b020 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 29 Apr 2024 19:33:45 +0900 Subject: [PATCH 1292/1472] jacobian terms for water enthalpy state vec were incorrect --- build/source/engine/enthalpyTemp.f90 | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 2ca4f562d..5e44af96d 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -940,7 +940,7 @@ subroutine enthalpy2T_veg(& denthLiq_dWat = Cp_water * diffT / canopyDepth denthIce_dWat = 0._rkind denthVeg_dWat = 0._rkind - dH_dWat = dH_dT * dT_dWat + denthVeg_dWat + denthLiq_dWat + denthIce_dWat - LH_fus * (1._rkind - fLiq) / canopyDepth + dH_dWat = denthVeg_dWat + denthLiq_dWat + denthIce_dWat - LH_fus * (1._rkind - fLiq) / canopyDepth dT_dEnthalpy = 1._rkind / dH_dT dT_dWat = dH_dWat / dH_dT @@ -1049,7 +1049,7 @@ subroutine enthalpy2T_snow(& denthLiq_dWat = iden_water * Cp_water * integral denthIce_dWat = iden_water * Cp_ice * ( diffT - integral ) denthAir_dWat = -iden_air * Cp_air * ( (iden_water/iden_ice)*(diffT-integral) + integral ) - dH_dWat = dH_dT * dT_dWat + denthLiq_dWat + denthIce_dWat + denthAir_dWat - iden_water * LH_fus * (1._rkind - fLiq) + dH_dWat = denthLiq_dWat + denthIce_dWat + denthAir_dWat - iden_water * LH_fus * (1._rkind - fLiq) dT_dEnthalpy = 1._rkind / dH_dT dT_dWat = dH_dWat / dH_dT @@ -1169,9 +1169,12 @@ subroutine enthalpy2T_soil(& dvolFracWat_dPsi0 = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) ! ***** get temperature if unfrozen soil - T = mLayerEnthalpy / ( iden_water * Cp_water * volFracWat + soil_dens_intr * Cp_soil * (1._rkind - theta_sat) + iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) ) + Tfreeze - dT_dEnthalpy = 1._rkind / ( iden_water * Cp_water * volFracWat + soil_dens_intr*Cp_soil*(1._rkind - theta_sat) + iden_air*Cp_air*(1._rkind - theta_sat - volFracWat) ) - dT_dWat = -iden_water * Cp_water * dvolFracWat_dPsi0 * mLayerEnthalpy / ( iden_water * Cp_water * volFracWat + soil_dens_intr * Cp_soil * (1._rkind - theta_sat) + iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) )**2_i4b + T = mLayerEnthalpy / ( iden_water * Cp_water * volFracWat + soil_dens_intr * Cp_soil * (1._rkind - theta_sat) & + + iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) ) + Tfreeze + dT_dEnthalpy = 1._rkind / ( iden_water * Cp_water * volFracWat + soil_dens_intr*Cp_soil*(1._rkind - theta_sat) & + + iden_air*Cp_air*(1._rkind - theta_sat - volFracWat) ) + dT_dWat = -iden_water * Cp_water * dvolFracWat_dPsi0 * mLayerEnthalpy / ( iden_water * Cp_water * volFracWat & + + soil_dens_intr * Cp_soil * (1._rkind - theta_sat) iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) )**2_i4b ! ***** iterate to find temperature if ice exists if( T Date: Mon, 29 Apr 2024 19:40:05 +0900 Subject: [PATCH 1293/1472] typo --- build/source/engine/enthalpyTemp.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 5e44af96d..196cccb55 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -1174,7 +1174,7 @@ subroutine enthalpy2T_soil(& dT_dEnthalpy = 1._rkind / ( iden_water * Cp_water * volFracWat + soil_dens_intr*Cp_soil*(1._rkind - theta_sat) & + iden_air*Cp_air*(1._rkind - theta_sat - volFracWat) ) dT_dWat = -iden_water * Cp_water * dvolFracWat_dPsi0 * mLayerEnthalpy / ( iden_water * Cp_water * volFracWat & - + soil_dens_intr * Cp_soil * (1._rkind - theta_sat) iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) )**2_i4b + + soil_dens_intr * Cp_soil * (1._rkind - theta_sat) + iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) )**2_i4b ! ***** iterate to find temperature if ice exists if( T Date: Mon, 29 Apr 2024 20:01:11 +0900 Subject: [PATCH 1294/1472] something got screwed up in merge, jacobian enthalpy state now correct --- build/source/engine/enthalpyTemp.f90 | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 196cccb55..5509088bc 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -908,8 +908,10 @@ subroutine enthalpy2T_veg(& ! ***** get temperature if unfrozen vegetation T = scalarCanopyEnthalpy * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWat ) + Tfreeze - dT_dEnthalpy = canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWat ) - dT_dWat = -Cp_water * scalarCanopyEnthalpy * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWat )**2_i4b + if(computJac)then + dT_dEnthalpy = canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWat ) + dT_dWat = -Cp_water * scalarCanopyEnthalpy * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWat )**2_i4b + endif ! ***** iterate to find temperature if ice exists if( T Date: Mon, 29 Apr 2024 23:46:01 +0900 Subject: [PATCH 1295/1472] valgrind detected error --- build/source/engine/summaSolve4ida.f90 | 1 + build/source/engine/systemSolv.f90 | 2 ++ build/source/engine/varSubstep.f90 | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 76c31cf8f..52684c79c 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -338,6 +338,7 @@ subroutine summaSolve4ida(& eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) dCompress_dPsiPrev(:) = 0._rkind resVecPrev(:) = 0._rkind + balance(:) = 0._rkind retval = FSUNContext_Create(SUN_COMM_NULL, sunctx) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 2c6e394cf..aec8072ab 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -311,6 +311,8 @@ subroutine initialize_systemSolv ! initialize the flags tooMuchMelt = .false. ! too much melt reduceCoupledStep = .false. ! need to reduce the length of the coupled step + ! initialize balances + balance(:) = realMissing associate(& ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 5a163c5f0..ef3cebaae 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -452,7 +452,7 @@ subroutine varSubstep(& call updateProg(dtSubstep,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states doAdjustTemp,computeVegFlux,computMassBalance,computNrgBalance,computeEnthTemp,enthalpyStateVec,use_lookup,& ! input: model control model_decisions,lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures - fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! output: balances, flags, and error control + fluxVec,resVec,balance,waterBalanceError,nrgFluxModified,err,message) ! input-output: balances, flags, and error control if(err/=0)then message=trim(message)//trim(cmessage) if(err>0) return From 8d4bc05944966a649305463617ffdfbd35eca631 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 30 Apr 2024 13:49:04 +0900 Subject: [PATCH 1296/1472] fix an error message, no code change --- build/source/engine/getVectorz.f90 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index 53d50a906..12c911d2e 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -477,7 +477,7 @@ subroutine checkFeas(& if(ixCasNrg/=integerMissing)then if(stateVec(ixCasNrg) > canopyTempMax .and. .not.enthalpyStateVec)then feasible=.false. - message=trim(message)//'canopy air space temp high' + message=trim(message)//'canopy air space temp high/' !write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixCasNrg )', feasible, canopyTempMax, stateVec(ixCasNrg) endif endif @@ -486,7 +486,7 @@ subroutine checkFeas(& if(ixVegNrg/=integerMissing)then if(stateVec(ixVegNrg) > canopyTempMax .and. .not.enthalpyStateVec)then feasible=.false. - message=trim(message)//'canopy temp high' + message=trim(message)//'canopy temp high/' !write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, max, stateVec( ixVegNrg )', feasible, canopyTempMax, stateVec(ixVegNrg) endif endif @@ -495,7 +495,7 @@ subroutine checkFeas(& if(ixVegHyd/=integerMissing)then if(stateVec(ixVegHyd) < 0._rkind)then feasible=.false. - message=trim(message)//'canopy liq water neg' + message=trim(message)//'canopy liq water neg/' !write(*,'(a,1x,L1,1x,10(f20.10,1x))') 'feasible, min, stateVec( ixVegHyd )', feasible, 0._rkind, stateVec(ixVegHyd) endif endif @@ -504,7 +504,7 @@ subroutine checkFeas(& if(count(ixSnowOnlyNrg/=integerMissing)>0)then if(any(stateVec( pack(ixSnowOnlyNrg,ixSnowOnlyNrg/=integerMissing) ) > Tfreeze) .and. .not.enthalpyStateVec)then feasible=.false. - message=trim(message)//'snow temp high' + message=trim(message)//'snow temp high/' !do iLayer=1,nSnow ! if(stateVec(ixSnowOnlyNrg(iLayer)) > Tfreeze) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, max, stateVec( ixSnowOnlyNrg(iLayer) )', iLayer, feasible, Tfreeze, stateVec( ixSnowOnlyNrg(iLayer) ) !enddo @@ -533,7 +533,7 @@ subroutine checkFeas(& ! --> check if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax)then feasible=.false. - message=trim(message)//'layer water out of bounds' + message=trim(message)//'layer water out of bounds/' !if(stateVec( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVec( ixSnowSoilHyd(iLayer) ) > xMax) & !write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVec( ixSnowSoilHyd(iLayer) ), xMin, xMax endif From 23508f0c807459de0d17b8968c1c8f21e6454bd0 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 30 Apr 2024 19:49:14 +0900 Subject: [PATCH 1297/1472] have to add canopy values to ida call because even though does not split, can lose them in the middle of a run when snow buries the canopy --- build/source/dshare/type4ida.f90 | 4 +++ build/source/engine/eval8summaWithPrime.f90 | 33 ++++++++++++++------- build/source/engine/systemSolv.f90 | 8 +++++ 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/build/source/dshare/type4ida.f90 b/build/source/dshare/type4ida.f90 index 8fb6f63af..8388b0de0 100644 --- a/build/source/dshare/type4ida.f90 +++ b/build/source/dshare/type4ida.f90 @@ -54,7 +54,11 @@ module type4ida real(rkind) :: scalarCanopyTempPrev ! previous value for temperature of the vegetation canopy (K) real(rkind), allocatable :: mLayerTempPrev(:) ! previous vector of layer temperature (K) real(rkind), allocatable :: mLayerMatricHeadPrev(:) ! previous value for total water matric potential (m) + real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-2) real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatTrial ! trial value for mass of total water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) real(rkind), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) real(rkind), allocatable :: mLayerMatricHeadTrial(:) ! trial value for total water matric potential (m) real(rkind) :: scalarCanopyTempPrime ! prime value for temperature of the vegetation canopy (K s-1) diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 7c041b34e..9fdd7a47a 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -82,7 +82,11 @@ subroutine eval8summaWithPrime(& flux_data, & ! intent(inout): model fluxes for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output: new values of variables needed in data window outside of internal IDA for rootfinding and to start enthalpy calculations + scalarCanopyEnthalpyTrial, & ! intent(inout): trial value for enthalpy of the vegetation canopy (J m-3) scalarCanopyTempTrial, & ! intent(inout): trial value for temperature of the vegetation canopy (K) + scalarCanopyWatTrial, & ! intent(inout): trial value for total water content of the vegetation canopy (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value for liquid water storage in the canopy (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value for ice storage in the canopy (kg m-2) mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) ! output: new prime values of variables needed in data window outside of internal IDA for Jacobian @@ -146,7 +150,11 @@ subroutine eval8summaWithPrime(& type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables ! output: new values of variables needed in data window outside of internal IDA for rootfinding and to start enthalpy calculations + real(rkind),intent(inout) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) real(rkind),intent(inout) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind),intent(inout) :: scalarCanopyWatTrial ! trial value for total water content of the vegetation canopy (kg m-2) + real(rkind),intent(inout) :: scalarCanopyLiqTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),intent(inout) :: scalarCanopyIceTrial ! trial value for ice storage in the canopy (kg m-2) real(rkind),intent(inout) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial vector for total water matric potential (m) ! output: new prime values of variables needed in data window outside of internal IDA for Jacobian @@ -173,10 +181,6 @@ subroutine eval8summaWithPrime(& ! state variables real(rkind) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3) real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) - real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) - real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind) :: scalarCanopyLiqTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind) :: scalarCanopyIceTrial ! trial value for ice storage in the canopy (kg m-2) real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector of enthalpy of each snow and soil layer (J m-3) real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector of volumetric total water content (-) @@ -311,12 +315,17 @@ subroutine eval8summaWithPrime(& ixEnd = nSoil endif - ! Placeholder: if we decide to use splitting, we need to pass all the previous values of the state variables + ! Canopy layer can disappear even without splitting (snow burial), so need to take last values + if(ixNrgConserv== enthalpyForm .or. ixNrgConserv == enthalpyFormLU)then ! use state variable as enthalpy, need to compute temperature + !scalarCanairNrgTrial = scalarCanairEnthalpyTrial + scalarCanopyNrgTrial = scalarCanopyEnthalpyTrial + else ! use state variable as temperature + !scalarCanairNrgTrial = scalarCanairTempTrial + scalarCanopyNrgTrial = scalarCanopyTempTrial + endif !(choice of how conservation of energy is implemented) + + ! Placeholder: if we decide to use splitting, we need to pass all the previous values of the state variables scalarCanairNrgTrial = realMissing - scalarCanopyNrgTrial = realMissing - scalarCanopyWatTrial = realMissing - scalarCanopyLiqTrial = realMissing - scalarCanopyIceTrial = realMissing mLayerNrgTrial = realMissing mLayerVolFracWatTrial = realMissing mLayerVolFracLiqTrial = realMissing @@ -772,8 +781,12 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output: new values of variables needed in data window outside of internal IDA for rootfinding and to start enthalpy calculations + ! output: new values of variables needed in data window outside of internal IDA for rootfinding and to start enthalpy calculations + eqns_data%scalarCanopyEnthalpyTrial, & ! intent(inout): trial value for enthalpy of the vegetation canopy (J m-3) eqns_data%scalarCanopyTempTrial, & ! intent(inout): trial value for temperature of the vegetation canopy (K) + eqns_data%scalarCanopyWatTrial, & ! intent(inout): trial value for total water content of the vegetation canopy (kg m-2) + eqns_data%scalarCanopyLiqTrial, & ! intent(inout): trial value for liquid water storage in the canopy (kg m-2) + eqns_data%scalarCanopyIceTrial, & ! intent(inout): trial value for ice storage in the canopy (kg m-2) eqns_data%mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) eqns_data%mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) ! output: new prime values of variables needed in data window outside of internal IDA diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index aec8072ab..a74a64f58 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -489,7 +489,11 @@ subroutine initial_flux_and_residual_vectors_prime associate(& nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1),& ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-2) scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1),& ! intent(inout): [dp] temperature of the vegetation canopy (K) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout): [dp] total water content of the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout): [dp] total liquid water content of the vegetation canopy (kg m-2) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout): [dp] total ice content of the vegetation canopy (kg m-2) mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout): [dp(:)] temperature of each snow/soil layer (K) mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat & ! intent(out): [dp(:)] matric head (m) &) @@ -527,7 +531,11 @@ subroutine initial_flux_and_residual_vectors_prime flux_init, & ! intent(inout): model fluxes for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables ! output: new values of variables needed in data window outside of internal IDA for rootfinding and to start enthalpy calculations + scalarCanopyEnthalpy, & ! intent(inout): value for enthalpy of the vegetation canopy (J m-2) scalarCanopyTemp, & ! intent(inout): value for temperature of the vegetation canopy (K) + scalarCanopyWat, & ! intent(inout): value for total water content of the vegetation canopy (kg m-2) + scalarCanopyLiq, & ! intent(inout): value for total liquid water content of the vegetation canopy (kg m-2) + scalarCanopyIce, & ! intent(inout): value for total ice content of the vegetation canopy (kg m-2) mLayerTemp, & ! intent(inout): vector of layer temperature (K) mLayerMatricHead, & ! intent(out): value for total water matric potential (m) ! output: new prime values of variables needed in data window outside of internal IDA for Jacobian From 5ff8a023aeae63375169259ef3cb370a9d94fbe1 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 30 Apr 2024 20:28:09 +0900 Subject: [PATCH 1298/1472] do not need to hold on to liquid or ice because we aren't currently splitting with IDA --- build/source/dshare/type4ida.f90 | 2 -- build/source/engine/eval8summaWithPrime.f90 | 29 ++++++++++----------- build/source/engine/systemSolv.f90 | 27 +++++++++---------- 3 files changed, 26 insertions(+), 32 deletions(-) diff --git a/build/source/dshare/type4ida.f90 b/build/source/dshare/type4ida.f90 index 8388b0de0..e6d6ab185 100644 --- a/build/source/dshare/type4ida.f90 +++ b/build/source/dshare/type4ida.f90 @@ -57,8 +57,6 @@ module type4ida real(rkind) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-2) real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) real(rkind) :: scalarCanopyWatTrial ! trial value for mass of total water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) - real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) real(rkind), allocatable :: mLayerTempTrial(:) ! trial vector of layer temperature (K) real(rkind), allocatable :: mLayerMatricHeadTrial(:) ! trial value for total water matric potential (m) real(rkind) :: scalarCanopyTempPrime ! prime value for temperature of the vegetation canopy (K s-1) diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 9fdd7a47a..ebaf4c4b0 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -81,12 +81,11 @@ subroutine eval8summaWithPrime(& diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_data, & ! intent(inout): model fluxes for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output: new values of variables needed in data window outside of internal IDA for rootfinding and to start enthalpy calculations + ! input-output: values needed in case canopy gets buried scalarCanopyEnthalpyTrial, & ! intent(inout): trial value for enthalpy of the vegetation canopy (J m-3) - scalarCanopyTempTrial, & ! intent(inout): trial value for temperature of the vegetation canopy (K) + scalarCanopyTempTrial, & ! intent(inout): trial value for temperature of the vegetation canopy (K), also used to start enthalpy calculations scalarCanopyWatTrial, & ! intent(inout): trial value for total water content of the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value for liquid water storage in the canopy (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value for ice storage in the canopy (kg m-2) + ! output: new values of variables needed in data window outside of internal IDA for rootfinding and to start enthalpy calculations mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) ! output: new prime values of variables needed in data window outside of internal IDA for Jacobian @@ -149,12 +148,11 @@ subroutine eval8summaWithPrime(& type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! output: new values of variables needed in data window outside of internal IDA for rootfinding and to start enthalpy calculations + ! input-output: values needed in case canopy gets buried real(rkind),intent(inout) :: scalarCanopyEnthalpyTrial ! trial value for enthalpy of the vegetation canopy (J m-3) real(rkind),intent(inout) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) - real(rkind),intent(inout) :: scalarCanopyWatTrial ! trial value for total water content of the vegetation canopy (kg m-2) - real(rkind),intent(inout) :: scalarCanopyLiqTrial ! trial value for liquid water storage in the canopy (kg m-2) - real(rkind),intent(inout) :: scalarCanopyIceTrial ! trial value for ice storage in the canopy (kg m-2) + real(rkind),intent(inout) :: scalarCanopyWatTrial ! trial value for total water content of the vegetation canopy (kg m-2), also used to start enthalpy calculations + ! output: new values of variables needed in data window outside of internal IDA for rootfinding and to start enthalpy calculations real(rkind),intent(inout) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) real(rkind),intent(out) :: mLayerMatricHeadTrial(:) ! trial vector for total water matric potential (m) ! output: new prime values of variables needed in data window outside of internal IDA for Jacobian @@ -181,11 +179,13 @@ subroutine eval8summaWithPrime(& ! state variables real(rkind) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3) real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyLiqTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind) :: scalarCanopyIceTrial ! trial value for ice storage in the canopy (kg m-2) real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial value for liquid water matric potential (m) real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! trial vector of enthalpy of each snow and soil layer (J m-3) real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector of volumetric total water content (-) real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector of volumetric liquid water content (-) - real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector of volumetric ice content (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector of volumetric fraction of ice (-) real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) ! prime state variables real(rkind) :: scalarCanairEnthalpyPrime ! prime value for enthalpy of the canopy air space (W m-3) @@ -317,15 +317,15 @@ subroutine eval8summaWithPrime(& ! Canopy layer can disappear even without splitting (snow burial), so need to take last values if(ixNrgConserv== enthalpyForm .or. ixNrgConserv == enthalpyFormLU)then ! use state variable as enthalpy, need to compute temperature - !scalarCanairNrgTrial = scalarCanairEnthalpyTrial scalarCanopyNrgTrial = scalarCanopyEnthalpyTrial else ! use state variable as temperature - !scalarCanairNrgTrial = scalarCanairTempTrial scalarCanopyNrgTrial = scalarCanopyTempTrial endif !(choice of how conservation of energy is implemented) ! Placeholder: if we decide to use splitting, we need to pass all the previous values of the state variables scalarCanairNrgTrial = realMissing + scalarCanopyLiqTrial = realMissing + scalarCanopyIceTrial = realMissing mLayerNrgTrial = realMissing mLayerVolFracWatTrial = realMissing mLayerVolFracLiqTrial = realMissing @@ -781,12 +781,11 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%diag_data, & ! intent(inout): model diagnostic variables for a local HRU eqns_data%flux_data, & ! intent(inout): model fluxes for a local HRU (initial flux structure) eqns_data%deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output: new values of variables needed in data window outside of internal IDA for rootfinding and to start enthalpy calculations + ! input-output: values needed in case canopy gets buried eqns_data%scalarCanopyEnthalpyTrial, & ! intent(inout): trial value for enthalpy of the vegetation canopy (J m-3) - eqns_data%scalarCanopyTempTrial, & ! intent(inout): trial value for temperature of the vegetation canopy (K) + eqns_data%scalarCanopyTempTrial, & ! intent(inout): trial value for temperature of the vegetation canopy (K), also used to start enthalpy calculations eqns_data%scalarCanopyWatTrial, & ! intent(inout): trial value for total water content of the vegetation canopy (kg m-2) - eqns_data%scalarCanopyLiqTrial, & ! intent(inout): trial value for liquid water storage in the canopy (kg m-2) - eqns_data%scalarCanopyIceTrial, & ! intent(inout): trial value for ice storage in the canopy (kg m-2) + ! output: new values of variables needed in data window outside of internal IDA for rootfinding and to start enthalpy calculations eqns_data%mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) eqns_data%mLayerMatricHeadTrial, & ! intent(out): trial value for total water matric potential (m) ! output: new prime values of variables needed in data window outside of internal IDA diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index a74a64f58..b8f4df6b7 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -487,15 +487,13 @@ subroutine initial_flux_and_residual_vectors_prime ! Note: Need this extra subroutine to handle the case of enthalpy as a state variable, currently only implemented in the prime version ! If we implement it in the regular version, we can remove this subroutine associate(& - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1),& ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-2) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1),& ! intent(inout): [dp] temperature of the vegetation canopy (K) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout): [dp] total water content of the vegetation canopy (kg m-2) - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout): [dp] total liquid water content of the vegetation canopy (kg m-2) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout): [dp] total ice content of the vegetation canopy (kg m-2) - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout): [dp(:)] temperature of each snow/soil layer (K) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat & ! intent(out): [dp(:)] matric head (m) + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) , & ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) , & ! intent(in): [i4b] number of soil layers + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1), & ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-2) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) , & ! intent(inout): [dp] temperature of the vegetation canopy (K) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) , & ! intent(inout): [dp] total water content of the vegetation canopy (kg m-2) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat , & ! intent(inout): [dp(:)] temperature of each snow/soil layer (K) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat & ! intent(out): [dp(:)] matric head (m) &) stateVecPrime(:) = 0._rkind ! prime initial values are 0 firstSplitOper0 = firstSplitOper ! set the flag for the first split operation, do not want to reset it here @@ -530,15 +528,14 @@ subroutine initial_flux_and_residual_vectors_prime diag_data, & ! intent(inout): model diagnostic variables for a local HRU flux_init, & ! intent(inout): model fluxes for a local HRU deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output: new values of variables needed in data window outside of internal IDA for rootfinding and to start enthalpy calculations - scalarCanopyEnthalpy, & ! intent(inout): value for enthalpy of the vegetation canopy (J m-2) - scalarCanopyTemp, & ! intent(inout): value for temperature of the vegetation canopy (K) + ! input-output: values needed in case canopy gets buried + scalarCanopyEnthalpy, & ! intent(inout): value for enthalpy of the vegetation canopy (J m-3) + scalarCanopyTemp, & ! intent(inout): value for temperature of the vegetation canopy (K), also used to start enthalpy calculations scalarCanopyWat, & ! intent(inout): value for total water content of the vegetation canopy (kg m-2) - scalarCanopyLiq, & ! intent(inout): value for total liquid water content of the vegetation canopy (kg m-2) - scalarCanopyIce, & ! intent(inout): value for total ice content of the vegetation canopy (kg m-2) + ! output: new values of variables needed in data window outside of internal IDA for rootfinding and to start enthalpy calculations mLayerTemp, & ! intent(inout): vector of layer temperature (K) mLayerMatricHead, & ! intent(out): value for total water matric potential (m) - ! output: new prime values of variables needed in data window outside of internal IDA for Jacobian + ! output: new prime values of variables needed in data window outside of internal IDA for Jacobian scalarCanopyTempPrime, & ! intent(out): prime value for temperature of the vegetation canopy (K s-1) scalarCanopyWatPrime, & ! intent(out): prime value for total water content of the vegetation canopy (kg m-2 s-1) mLayerTempPrime, & ! intent(out): prime vector of temperature of each snow and soil layer (K s-1) From 6de4cca3d8b31f24ebd211a608dd040778ff0fc8 Mon Sep 17 00:00:00 2001 From: Kyle Klenk Date: Tue, 30 Apr 2024 14:58:56 +0000 Subject: [PATCH 1299/1472] Adjustments to cmakelists --- build/cmake/CMakeLists.txt | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 2b392a2ee..76c49dbb0 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -411,8 +411,7 @@ set(INTERFACE ${ACTORS_DIR}/global/cppwrap_auxiliary.f90 ${ACTORS_DIR}/global/cppwrap_datatypes.f90 ${ACTORS_DIR}/global/cppwrap_metadata.f90 - ${ACTORS_DIR}/global/c_interface_module.f90 - ${ACTORS_DIR}/global/gru_struc.f90) + ${ACTORS_DIR}/global/c_interface_module.f90) set(SYS_INIT_INTERFACE ${SYS_INIT_DIR}/batch_distributer_actor.f90) set(FILE_ACCESS_INTERFACE @@ -422,7 +421,7 @@ set(FILE_ACCESS_INTERFACE ${FILE_ACCESS_DIR}/output_structure.f90 ${FILE_ACCESS_DIR}/fileAccess_writeOutput.f90) set(JOB_INTERFACE - ${JOB_ACTOR_DIR}/job_actor.f90) + ${JOB_ACTOR_DIR}/gru_struc.f90) set(HRU_INTERFACE ${HRU_ACTOR_DIR}/hru_init.f90 ${HRU_ACTOR_DIR}/hru_read.f90 @@ -434,7 +433,6 @@ set(ACTORS_GLOBAL ${ACTORS_DIR}/global/auxiliary.cpp ${ACTORS_DIR}/global/global.cpp ${ACTORS_DIR}/global/fileManager.cpp - ${ACTORS_DIR}/global/gru_struc.cpp ${ACTORS_DIR}/global/message_atoms.cpp ${ACTORS_DIR}/global/settings_functions.cpp ${ACTORS_DIR}/global/timing_info.cpp) @@ -450,20 +448,22 @@ set(SYS_INIT ${SYS_INIT_DIR}/summa_server.cpp) set(FILE_ACCESS_ACTOR ${FILE_ACCESS_DIR}/summa_init_struc.cpp - ${ACTORS_DIR}/file_access_actor/file_access_actor.cpp - ${ACTORS_DIR}/file_access_actor/forcing_file_info.cpp - ${ACTORS_DIR}/file_access_actor/output_container.cpp) + ${FILE_ACCESS_DIR}/file_access_actor.cpp + ${FILE_ACCESS_DIR}/forcing_file_info.cpp + ${FILE_ACCESS_DIR}/output_container.cpp) set(JOB_ACTOR - ${ACTORS_DIR}/job_actor/GRU.cpp - ${ACTORS_DIR}/job_actor/job_actor.cpp - ${ACTORS_DIR}/job_actor/async_mode.cpp - ${ACTORS_DIR}/job_actor/data_assimilation_mode.cpp - ${ACTORS_DIR}/job_actor/job_utils.cpp - ${ACTORS_DIR}/job_actor/distributed_job_actor.cpp - ${ACTORS_DIR}/job_actor/node_actor.cpp) + ${JOB_ACTOR_DIR}/GRU.cpp + ${JOB_ACTOR_DIR}/gru_struc.cpp + ${JOB_ACTOR_DIR}/job_actor.cpp + ${JOB_ACTOR_DIR}/async_mode.cpp + ${JOB_ACTOR_DIR}/data_assimilation_mode.cpp + ${JOB_ACTOR_DIR}/job_utils.cpp + ${JOB_ACTOR_DIR}/distributed_job_actor.cpp + ${JOB_ACTOR_DIR}/node_actor.cpp) set(HRU_ACTOR - ${ACTORS_DIR}/hru_actor/hru_actor.cpp - ${ACTORS_DIR}/hru_actor/hru_batch_actor.cpp) + ${HRU_ACTOR_DIR}/hru_utils.cpp + ${HRU_ACTOR_DIR}/hru_actor.cpp + ${HRU_ACTOR_DIR}/hru_batch_actor.cpp) #========================================================================================= From 9609fb4c32f82c124b86a8048285514462b9892c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 7 May 2024 13:18:06 +0900 Subject: [PATCH 1300/1472] utils for latest paper tag --- utils/hist_per_GRU.py | 6 +++--- utils/plot_per_GRUMult.py | 4 ++-- utils/plot_per_GRUMultBal.py | 4 ++-- utils/scat_per_GRU.py | 4 ++-- utils/timeseries_to_statistics.py | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 4f7ed473d..cbf7f93f8 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -40,8 +40,8 @@ #plt_name=['BE1','IDAe-4','BE4','BE8','BE16','BE32','IDAe-6'] #maybe make this an argument #method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument #plt_name=['BE1','BE16','BE32','SUNDIALS'] #maybe make this an argument - method_name=['be1','be1en'] - plt_name=['BE1 temp','BE1 mixed'] + method_name=['be1','be1cm','be1en','sundials_1en6cm'] + plt_name=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp'] if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE # Define the power transformation function @@ -49,7 +49,7 @@ def power_transform(x): return x ** 0.5 # Adjust the exponent as needed # Simulation statistics file locations -use_vars = [1,2,3,4,5] +use_vars = [1,5] settings0= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] settings = [settings0[i] for i in use_vars] diff --git a/utils/plot_per_GRUMult.py b/utils/plot_per_GRUMult.py index 9b1ad0874..f10bb6179 100644 --- a/utils/plot_per_GRUMult.py +++ b/utils/plot_per_GRUMult.py @@ -34,8 +34,8 @@ stat = sys.argv[1] #method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument #plt_name=['(a) SUMMA-BE1','(b) SUMMA-BE16','(c) SUMMA-BE32','(d) SUMMA-SUNDIALS'] #maybe make this an argument -method_name=['be1','be1en','sundials_1en6','diff']#,'be1lu'] #maybe make this an argument -plt_name=['(a) SUMMA-BE1 Energy Temperature Form','(b) SUMMA-BE1 Energy Mixed Form','(c) SUMMA-SUNDIALS','(d) SUMMA-BE1 Mixed Form - Temperature Form']#, #maybe make this an argument +method_name=['be1','be1cm','be1en','sundials_1en6cm']#,'diff']#,'be1lu'] #maybe make this an argument +plt_name=['(a) SUMMA-BE1 Common Form of Heat Eq.','(b) SUMMA-BE1 Temperature Form of Heat Eq.','(c) SUMMA-BE1 Mixed Form of Heat Eq.','(d) SUMMA-SUNDIALS Temperature Form of Heat Eq.'] #,'(d) SUMMA-BE1 Mixed Form - Temperature Form']#, #maybe make this an argumentt from_meth = 'be1en' # index of the first simulation in the difference simulation, only used if a method_name is 'diff' sub_meth = 'be1' # index of the simulation to subtract in the difference simulation, only used if a method_name is 'diff' diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index 4439cf448..3ab72469a 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -32,8 +32,8 @@ # plot all runs, pick statistic stat = sys.argv[1] -method_name=['be1','be1en']#,'be1lu'] #maybe make this an argument -plt_name=['(a) SUMMA-BE1 Energy Temperature Form','(b) SUMMA-BE1 Energy Mixed Form']#,'(c) SUMMA-BE1 with enthalpy lookup'] #maybe make this an argument +method_name=['be1','be1cm','be1en','sundials_1en6cm'] #maybe make this an argument +plt_name=['(a) SUMMA-BE1 Common Form of Heat Eq.','(b) SUMMA-BE1 Temperature Form of Heat Eq.','(c) SUMMA-BE1 Mixed Form of Heat Eq.','(d) SUMMA-SUNDIALS Temperature Form of Heat Eq.'] # Simulation statistics file locations settings= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime'] diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index 68bad159d..f803fc46f 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -42,8 +42,8 @@ #plt_name=['BE1','IDAe-4','BE4','BE8','BE16','BE32','IDAe-6'] #maybe make this an argument #method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument #plt_name=['BE1','BE16','BE32','IDAe-6'] #maybe make this an argument - method_name=['be1','be1en'] - plt_name=['BE1 temp','BE1 mixed'] + method_name=['be1','be1cm','be1en','sundials_1en6cm'] + plt_name=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp'] if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index c56c623fb..56efd74c3 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -26,7 +26,7 @@ warnings.simplefilter("ignore") #deal with correlation warnings from variance 0 in kgem, both have no snow # Settings -bench_name = 'sundials_1en8' +bench_name = 'sundials_1en8cm' not_parallel = True # run as true with batch mode, or false, with `python timeseries_to_statistics.py sundials_1en6 1 1` for single batch, and `python timeseries_to_statistics.py sundials_1en6 2 1` to merge testing = False From 43fdee0616170a53d7f139d69f1f8a377b5103ac Mon Sep 17 00:00:00 2001 From: Kyle Klenk Date: Tue, 7 May 2024 22:43:26 -0600 Subject: [PATCH 1301/1472] Updated CMakeLists.txt --- build/cmake/CMakeLists.txt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 76c49dbb0..03cbc229d 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -204,6 +204,7 @@ else() ${PARENT_DIR}/build/includes/summa_actor ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor + ${PARENT_DIR}/build/includes/gru_actor ${PARENT_DIR}/build/includes/hru_actor) set(LIB_ACTORS ${DIR_ACTORS}/lib -lcaf_core -lcaf_io -lnetcdf) endif() @@ -246,6 +247,7 @@ set(ACTORS_DIR ${PARENT_DIR}/build/source) set(SYS_INIT_DIR ${ACTORS_DIR}/system_initialization) set(FILE_ACCESS_DIR ${ACTORS_DIR}/file_access_actor) set(JOB_ACTOR_DIR ${ACTORS_DIR}/job_actor) +set(GRU_ACTOR_DIR ${ACTORS_DIR}/gru_actor) set(HRU_ACTOR_DIR ${ACTORS_DIR}/hru_actor) if(CMAKE_BUILD_TYPE MATCHES Actors) @@ -422,18 +424,19 @@ set(FILE_ACCESS_INTERFACE ${FILE_ACCESS_DIR}/fileAccess_writeOutput.f90) set(JOB_INTERFACE ${JOB_ACTOR_DIR}/gru_struc.f90) +set(GRU_INTERFACE + ${GRU_ACTOR_DIR}/gru_interface.f90) set(HRU_INTERFACE ${HRU_ACTOR_DIR}/hru_init.f90 ${HRU_ACTOR_DIR}/hru_read.f90 ${HRU_ACTOR_DIR}/hru_modelRun.f90 - ${HRU_ACTOR_DIR}/hru_writeOutput.f90) + ${HRU_ACTOR_DIR}/hru_writeOutput.f90 + ${HRU_ACTOR_DIR}/hru_interface.f90) # Actors actual actor modules set(ACTORS_GLOBAL ${ACTORS_DIR}/global/auxiliary.cpp - ${ACTORS_DIR}/global/global.cpp ${ACTORS_DIR}/global/fileManager.cpp - ${ACTORS_DIR}/global/message_atoms.cpp ${ACTORS_DIR}/global/settings_functions.cpp ${ACTORS_DIR}/global/timing_info.cpp) set(SYS_INIT @@ -460,6 +463,8 @@ set(JOB_ACTOR ${JOB_ACTOR_DIR}/job_utils.cpp ${JOB_ACTOR_DIR}/distributed_job_actor.cpp ${JOB_ACTOR_DIR}/node_actor.cpp) +set(GRU_ACTOR + ${GRU_ACTOR_DIR}/gru_actor.cpp) set(HRU_ACTOR ${HRU_ACTOR_DIR}/hru_utils.cpp ${HRU_ACTOR_DIR}/hru_actor.cpp @@ -494,6 +499,7 @@ if(CMAKE_BUILD_TYPE MATCHES Actors) ${SYS_INIT_INTERFACE} ${FILE_ACCESS_INTERFACE} ${JOB_INTERFACE} + ${GRU_INTERFACE} ${HRU_INTERFACE}) else() set(SUMMA_ALL ${SUMMA_ALL} @@ -588,6 +594,7 @@ elseif(CMAKE_BUILD_TYPE MATCHES Actors) ${ACTORS_GLOBAL} ${FILE_ACCESS_ACTOR} ${JOB_ACTOR} + ${GRU_ACTOR} ${HRU_ACTOR} ${SYS_INIT} ${SUMMA_CLIENT} From d8855f68879374cc4d073b294878ea8bda2c5083 Mon Sep 17 00:00:00 2001 From: Kyle Klenk Date: Fri, 10 May 2024 09:21:44 -0600 Subject: [PATCH 1302/1472] Fix issue where sundials cannot be found --- build/cmake/CMakeLists.txt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 03cbc229d..2740f190e 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -119,12 +119,7 @@ endif() if(CMAKE_BUILD_TYPE MATCHES Sundials) set(DIR_SUNDIALS "") foreach(dir IN ITEMS - "${F_MASTER}../sundials/instdir" - "${PARENT_DIR}../sundials/instdir" - "${F_MASTER}../../SummaSundials/sundials/instdir" - "${PARENT_DIR}../../SummaSundials/sundials/instdir" - $ENV{SUNDIALS_PATH} - "../../../../SummaSundials/sundials/instdir") #look in same head directory as summa or up to another installation + $ENV{SUNDIALS_PATH}) #look in same head directory as summa or up to another installation if(EXISTS ${dir}) set(DIR_SUNDIALS ${dir}) message("\nFound Sundials directory at ${DIR_SUNDIALS}") From 7f2257c7bed492eb4d93cf5c51bce7e13897f99d Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Fri, 10 May 2024 15:22:27 -0600 Subject: [PATCH 1303/1472] Fix inclusion of gru actor --- build/cmake/CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 2740f190e..449cb7e45 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -153,7 +153,12 @@ if(CMAKE_BUILD_TYPE MATCHES Cluster) if(CMAKE_BUILD_TYPE MATCHES Actors) find_package(CAF REQUIRED) - set(INC_ACTORS ${CAF_INCLUDES} ${PARENT_DIR}/build/includes/global ${PARENT_DIR}/build/includes/summa_actor ${PARENT_DIR}/build/includes/job_actor ${PARENT_DIR}/build/includes/file_access_actor ${PARENT_DIR}/build/includes/hru_actor) + set(INC_ACTORS ${CAF_INCLUDES} ${PARENT_DIR}/build/includes/global + ${PARENT_DIR}/build/includes/summa_actor + ${PARENT_DIR}/build/includes/job_actor + ${PARENT_DIR}/build/includes/gru_actor + ${PARENT_DIR}/build/includes/file_access_actor + ${PARENT_DIR}/build/includes/hru_actor) set(LIB_ACTORS ${CAF_LIBRARIES} -lcaf_core -lcaf_io -lnetcdf) endif() From 35e6f1308e466fc975b585409490214d9b4baa31 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 15 May 2024 03:13:39 -0600 Subject: [PATCH 1304/1472] Renamed summaSolve4numrec.f90 to summaSolve4homegrown.f90. --- build/cmake/CMakeLists.txt | 2 +- build/source/engine/summaSolve4numrec.f90 | 1229 --------------------- 2 files changed, 1 insertion(+), 1230 deletions(-) delete mode 100644 build/source/engine/summaSolve4numrec.f90 diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 03cbc229d..7c707293f 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -374,7 +374,7 @@ set(SOLVER ${ENGINE_DIR}/soilLiqFlx.f90 ${ENGINE_DIR}/ssdNrgFlux.f90 ${ENGINE_DIR}/stomResist.f90 - ${ENGINE_DIR}/summaSolve4numrec.f90 + ${ENGINE_DIR}/summaSolve4homegrown.f90 ${ENGINE_DIR}/systemSolv.f90 ${ENGINE_DIR}/varSubstep.f90 ${ENGINE_DIR}/vegLiqFlux.f90 diff --git a/build/source/engine/summaSolve4numrec.f90 b/build/source/engine/summaSolve4numrec.f90 deleted file mode 100644 index c62c2e869..000000000 --- a/build/source/engine/summaSolve4numrec.f90 +++ /dev/null @@ -1,1229 +0,0 @@ -! SUMMA - Structure for Unifying Multiple Modeling Alternatives -! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington -! -! This file is part of SUMMA -! -! For more information see: http://www.ral.ucar.edu/projects/summa -! -! This program is free software: you can redistribute it and/or modify -! it under the terms of the GNU General Public License as published by -! the Free Software Foundation, either version 3 of the License, or -! (at your option) any later version. -! -! This program is distributed in the hope that it will be useful, -! but WITHOUT ANY WARRANTY; without even the implied warranty of -! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -! GNU General Public License for more details. -! -! You should have received a copy of the GNU General Public License -! along with this program. If not, see . - -module summaSolve4numrec_module - -! data types -USE nrtype - -! access the global print flag -USE globalData,only:globalPrintFlag - -! domain types -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil - -! access missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing double precision number -USE globalData,only:quadMissing ! missing quadruple precision number - -! access named variables to describe the form and structure of the matrices used in the numerical solver -USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix -USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix -USE globalData,only: ku ! number of super-diagonal bands -USE globalData,only: kl ! number of sub-diagonal bands -USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix -USE globalData,only: iJac1 ! first layer of the Jacobian to print -USE globalData,only: iJac2 ! last layer of the Jacobian to print - -! named variables to describe the state variable type -USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space -USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy -USE globalData,only:iname_watCanopy ! named variable defining the mass of water on the vegetation canopy -USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers -USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers -USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers -USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers -USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers - -! indices of elements of data structure -USE var_lookup,only:iLookFLUX ! named variables for structure elements -USE var_lookup,only:iLookPROG ! named variables for structure elements -USE var_lookup,only:iLookPARAM ! named variables for structure elements -USE var_lookup,only:iLookINDEX ! named variables for structure elements -USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure - -USE multiconst,only:& - iden_water ! intrinsic density of liquid water (kg m-3) - -! provide access to the derived types to define the data structures -USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! lookup tables - model_options, & ! defines the model decisions - in_type_computJacob, & ! class for computJacob arguments - out_type_computJacob, & ! class for computJacob arguments - in_type_lineSearchRefinement, & ! class for lineSearchRefinement arguments - out_type_lineSearchRefinement,& ! class for lineSearchRefinement arguments - in_type_summaSolve4numrec, & ! class for summaSolve4numrec arguments - io_type_summaSolve4numrec, & ! class for summaSolve4numrec arguments - out_type_summaSolve4numrec ! class for summaSolve4numrec arguments - - -! look-up values for the choice of groundwater parameterization -USE mDecisions_module,only: & - qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization - bigBucket, & ! a big bucket (lumped aquifer model) - noExplicit ! no explicit groundwater parameterization - -! look-up values for method used to compute derivative -USE mDecisions_module,only: & - numerical, & ! numerical solution - analytical ! analytical solution - -implicit none -private -public::summaSolve4numrec -contains - - ! *********************************************************************************************************************** - ! public subroutine summaSolve4numrec: calculate the iteration increment, evaluate the new state, and refine if necessary - ! *********************************************************************************************************************** - subroutine summaSolve4numrec(& - in_SS4NR, & ! intent(in): model control and previous function value - ! input: model control - !firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - ! input: state vectors - stateVecTrial, & ! intent(in): trial state vector - !xMin,xMax, & ! intent(inout): brackets of the root - fScale, & ! intent(in): characteristic scale of the function evaluations - xScale, & ! intent(in): characteristic scale of the state vector - rVec, & ! intent(in): residual vector - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - dMat, & ! intent(inout): diagonal matrix (excludes flux derivatives) - ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - !ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(inout): derivative in baseflow w.r.t. matric head (s-1) - io_SS4NR, & ! intent(inout): first flux call flag, root brackets, index of lowest saturated layer - ! output - stateVecNew, & ! intent(out): new state vector - fluxVecNew, & ! intent(out): new flux vector - resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equation - resVecNew, & ! intent(out): new residual vector - out_SS4NR) ! intent(out): new function evaluation, convergence flag, and error control - USE computJacob_module, only: computJacob - USE eval8summa_module, only: imposeConstraints - USE matrixOper_module, only: lapackSolv - USE matrixOper_module, only: scaleMatrices - implicit none - ! -------------------------------------------------------------------------------------------------------------------------------- - type(in_type_summaSolve4numrec),intent(in) :: in_SS4NR ! model control variables and previous function evaluation - type(io_type_summaSolve4numrec) :: io_SS4NR ! first flux call flag and baseflow variables - ! input: model control -! logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call - ! input: state vectors - real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector -! real(rkind),intent(inout) :: xMin,xMax ! brackets of the root - real(rkind),intent(in) :: fScale(:) ! characteristic scale of the function evaluations - real(rkind),intent(in) :: xScale(:) ! characteristic scale of the state vector - real(qp),intent(in) :: rVec(:) ! NOTE: qp ! residual vector - real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) - real(rkind),intent(inout) :: dMat(:) ! diagonal matrix (excludes flux derivatives) - ! input: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions - type(zLookup), intent(in) :: lookup_data ! lookup tables - type(var_i), intent(in) :: type_data ! type of vegetation and soil - type(var_d), intent(in) :: attr_data ! spatial attributes - type(var_dlength), intent(in) :: mpar_data ! model parameters - type(var_d), intent(in) :: forc_data ! model forcing data - type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin - type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU - ! output: data structures - type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow -! integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) - real(rkind),intent(inout) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - ! output: flux and residual vectors - real(rkind),intent(out) :: stateVecNew(:) ! new state vector - real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector - real(rkind),intent(out) :: resSinkNew(:) ! sink terms on the RHS of the flux equation - real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector - type(out_type_summaSolve4numrec),intent(out) :: out_SS4NR ! new function evaluation, convergence flag, and error control - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - ! -------------------------------------------------------------------------------------------------------------------------------- - ! Jacobian matrix - logical(lgt),parameter :: doNumJacobian=.false. ! flag to compute the numerical Jacobian matrix - logical(lgt),parameter :: testBandDiagonal=.false. ! flag to test the band diagonal Jacobian matrix - real(rkind) :: nJac(in_SS4NR % nState,in_SS4NR % nState) ! numerical Jacobian matrix - real(rkind) :: aJac(in_SS4NR % nLeadDim,in_SS4NR % nState) ! Jacobian matrix - real(rkind) :: aJacScaled(in_SS4NR % nLeadDim,in_SS4NR % nState) ! Jacobian matrix (scaled) - real(rkind) :: aJacScaledTemp(in_SS4NR % nLeadDim,in_SS4NR % nState) ! Jacobian matrix (scaled) -- temporary copy since decomposed in lapack - ! solution/step vectors - real(rkind),dimension(in_SS4NR % nState) :: rVecScaled ! residual vector (scaled) - real(rkind),dimension(in_SS4NR % nState) :: newtStepScaled ! full newton step (scaled) - ! step size refinement - logical(lgt) :: doRefine ! flag for step refinement - integer(i4b),parameter :: ixLineSearch=1001 ! step refinement = line search - integer(i4b),parameter :: ixTrustRegion=1002 ! step refinement = trust region - integer(i4b),parameter :: ixStepRefinement=ixLineSearch ! decision for the numerical solution - ! general - integer(i4b) :: mSoil ! number of soil layers in solution vector - integer(i4b) :: iLayer ! row index - integer(i4b) :: jLayer ! column index - logical(lgt) :: globalPrintFlagInit ! initial global print flag - logical(lgt) :: return_flag ! flag that controls execution of return statements - character(LEN=256) :: cmessage ! error message of downwind routine - ! class objects for subroutine arguments - type(in_type_computJacob) :: in_computJacob ! computJacob - type(out_type_computJacob) :: out_computJacob ! computJacob - type(in_type_lineSearchRefinement) :: in_LSR ! lineSearchRefinement - type(out_type_lineSearchRefinement) :: out_LSR ! lineSearchRefinement - type(in_type_lineSearchRefinement) :: in_TRR ! trustRegionRefinement - type(out_type_lineSearchRefinement) :: out_TRR ! trustRegionRefinement - type(out_type_lineSearchRefinement) :: out_SRF ! safeRootFinder - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - - ! ***** Compute the Newton Step ***** - - call initialize_summaSolve4numrec; if (return_flag) return ! initial setup -- return if error - - call update_Jacobian; if (return_flag) return ! compute Jacobian for Newton step -- return if error - - call solve_linear_system; if (return_flag) return ! solve the linear system for the Newton step -- return if error - - call refine_Newton_step; if (return_flag) return ! refine Newton step if needed -- return if error - - associate(err => out_SS4NR % err,message => out_SS4NR % message) - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors - end associate - - contains - - subroutine initialize_summaSolve4numrec - ! *** Initial steps for the summaSolve4numrec algorithm (computing the Newton step) *** - - associate(& - err => out_SS4NR % err ,& - message => out_SS4NR % message & - &) - ! initialize error control - err=0; message='summaSolve4numrec/' - return_flag=.false. ! initialize return flag - - ! choose Jacobian type - select case(model_decisions(iLookDECISIONS%fDerivMeth)%iDecision) - case(numerical) - err=20; message=trim(message)//'numerical derivatives are not implemented for BE numerical Recipes solver'; - return_flag=.true.; return - case(analytical); ! this is fine - case default - err=20; message=trim(message)//'expect choice numericl or analytic to calculate derivatives for Jacobian'; - return_flag=.true.; return - end select - - end associate - - ! get the number of soil layers in the solution vector - mSoil = size(indx_data%var(iLookINDEX%ixMatOnly)%dat) - - ! initialize the global print flag - globalPrintFlagInit=globalPrintFlag - end subroutine initialize_summaSolve4numrec - - subroutine update_Jacobian - ! *** Update Jacobian used for Newton step *** - - ! compute the analytical Jacobian matrix - ! NOTE: The derivatives were computed in the previous call to computFlux - ! This occurred either at the call to eval8summa at the start of systemSolv - ! or in the call to eval8summa in the previous iteration (within lineSearchRefinement or trustRegionRefinement) - associate(& - err => out_SS4NR % err ,& - message => out_SS4NR % message & - &) - call initialize_computJacob_summaSolve4numrec - call computJacob(in_computJacob,indx_data,prog_data,diag_data,deriv_data,dBaseflow_dMatric,dMat,aJac,out_computJacob) - call finalize_computJacob_summaSolve4numrec - if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! (check for errors) - - ! compute the numerical Jacobian matrix - if (doNumJacobian) then - globalPrintFlag=.false. - call numJacobian(stateVecTrial,dMat,nJac,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! (check for errors) - globalPrintFlag=globalPrintFlagInit - end if - end associate - end subroutine update_Jacobian - - subroutine solve_linear_system - ! *** Solve the linear system for the Newton step using LAPACK routines *** - - associate(& - nState => in_SS4NR % nState ,& ! intent(in): total number of state variables - ixMatrix => in_SS4NR % ixMatrix ,& ! intent(in): type of matrix (full or band diagonal) - err => out_SS4NR % err ,& ! intent(out): error code - message => out_SS4NR % message & ! intent(out): error message - &) - ! test the band diagonal matrix - if (testBandDiagonal) then - call testBandMat(check=.true.,err=err,message=cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! (check for errors) - end if - - ! scale the residual vector - rVecScaled(1:nState) = fScale(:)*real(rVec(:), rkind) ! NOTE: residual vector is in quadruple precision - - ! scale matrices - call scaleMatrices(ixMatrix,nState,aJac,fScale,xScale,aJacScaled,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors - - ! debug statement for scaled Jacobian - if (globalPrintFlag .and. ixMatrix==ixBandMatrix) then - print*, '** SCALED banded analytical Jacobian:' - write(*,'(a4,1x,100(i17,1x))') 'xCol', (iLayer, iLayer=iJac1,iJac2) - do iLayer=kl+1,nBands - write(*,'(i4,1x,100(e17.10,1x))') iLayer, (aJacScaled(iLayer,jLayer),jLayer=min(iJac1,nState),min(iJac2,nState)) - end do - end if - - ! copy the scaled matrix, since it is decomposed in lapackSolv - aJacScaledTemp = aJacScaled - - ! compute the newton step: use the lapack routines to solve the linear system A.X=B - call lapackSolv(ixMatrix,nState,aJacScaledTemp,-rVecScaled,newtStepScaled,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors - - ! debug statement for Newton step - if (globalPrintFlag) write(*,'(a,1x,10(e17.10,1x))') 'newtStepScaled = ', newtStepScaled(min(iJac1,nState):min(iJac2,nState)) - - end associate - end subroutine solve_linear_system - - subroutine refine_Newton_step - ! *** Refine the Newton step if necessary *** - - ! initialize the flag for step refinement - doRefine=.true. - - ! * case 1: state vector - ! compute the flux vector and the residual, and (if necessary) refine the iteration increment - ! NOTE: in 99.9% of cases newtStep will be used (no refinement) - - associate(& - fOld => in_SS4NR % fOld ,& - fnew => out_SS4NR % fnew ,& - converged => out_SS4NR % converged,& - err => out_SS4NR % err ,& - message => out_SS4NR % message & - &) - if (size(stateVecTrial)>1) then - - ! try to backtrack - select case(ixStepRefinement) - case(ixLineSearch) - call in_LSR % initialize(doRefine,fOld) - call lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_LSR) - call out_LSR % finalize(fNew,converged,err,cmessage) - case(ixTrustRegion) - call in_TRR % initialize(doRefine,fOld) - call trustRegionRefinement(in_TRR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_TRR) - call out_TRR % finalize(fNew,converged,err,cmessage) - case default; err=20; message=trim(message)//'unable to identify numerical solution'; return_flag=.true.; return - end select - - ! check warnings: negative error code = warning; in this case back-tracked to the original value - ! NOTE: Accept the full newton step if back-tracked to the original value - if (err<0) then - doRefine=.false.; - call in_LSR % initialize(doRefine,fOld) - call lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_LSR) - call out_LSR % finalize(fNew,converged,err,cmessage) - end if - - ! * case 2: scalar - else - call safeRootfinder(stateVecTrial,rVecScaled,newtStepScaled,stateVecNew,fluxVecNew,resVecNew,out_SRF) - call out_SRF % finalize(fNew,converged,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors - end if - end associate - end subroutine refine_Newton_step - - ! ********************************************************************************************************* - ! * internal subroutine lineSearchRefinement: refine the iteration increment using line searches - ! ********************************************************************************************************* - subroutine lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_LSR) - ! provide access to the matrix routines - USE matrixOper_module, only: computeGradient - implicit none - ! input - type(in_type_lineSearchRefinement),intent(in) :: in_LSR ! class object for intent(in) arguments - real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector - real(rkind),intent(in) :: newtStepScaled(:) ! scaled newton step - real(rkind),intent(in) :: aJacScaled(:,:) ! scaled jacobian matrix - real(rkind),intent(in) :: rVecScaled(:) ! scaled residual vector - ! output - real(rkind),intent(out) :: stateVecNew(:) ! new state vector - real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector - real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector - type(out_type_lineSearchRefinement),intent(out) :: out_LSR ! class object for intent(out) arguments - ! -------------------------------------------------------------------------------------------------------- - ! local - character(len=256) :: cmessage ! error message of downwind routine - real(rkind) :: gradScaled(in_SS4NR % nState) ! scaled gradient - real(rkind) :: xInc(in_SS4NR % nState) ! iteration increment (re-scaled to original units of the state vector) - logical(lgt) :: feasible ! flag to denote the feasibility of the solution - integer(i4b) :: iLine ! line search index - integer(i4b),parameter :: maxLineSearch=5 ! maximum number of backtracks - real(rkind),parameter :: alpha=1.e-4_rkind ! check on gradient - real(rkind) :: xLambda ! backtrack magnitude - real(rkind) :: xLambdaTemp ! temporary backtrack magnitude - real(rkind) :: slopeInit ! initial slope - real(rkind) :: rhs1,rhs2 ! rhs used to compute the cubic - real(rkind) :: aCoef,bCoef ! coefficients in the cubic - real(rkind) :: disc ! temporary variable used in cubic - real(rkind) :: xLambdaPrev ! previous lambda value (used in the cubic) - real(rkind) :: fPrev ! previous function evaluation (used in the cubic) - ! -------------------------------------------------------------------------------------------------------- - associate(& - ! intent(in) variables - doLineSearch => in_LSR % doSearch ,& ! flag to do the line search - fOld => in_LSR % fOld ,& ! old function value - ! local variables - nSnow => in_SS4NR % nSnow ,& ! number of snow layers - nSoil => in_SS4NR % nSoil ,& ! number of soil layers - nState => in_SS4NR % nState ,& ! total number of state variables - ixMatrix => in_SS4NR % ixMatrix ,& ! type of matrix (full or band diagonal) - ! intent(out) variables - fNew => out_LSR % fNew ,& ! new function evaluation - converged => out_LSR % converged ,& ! convergence flag - err => out_LSR % err ,& ! error code - message => out_LSR % message & ! error message - &) - ! initialize error control - err=0; message='lineSearchRefinement/' - converged =.false. - - ! check the need to compute the line search - if(doLineSearch)then - - ! compute the gradient of the function vector - call computeGradient(ixMatrix,nState,aJacScaled,rVecScaled,gradScaled,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! compute the initial slope - slopeInit = dot_product(gradScaled,newtStepScaled) - - end if ! if computing the line search - - ! initialize lambda - xLambda=1._rkind - - ! ***** LINE SEARCH LOOP... - lineSearch: do iLine=1,maxLineSearch ! try to refine the function by shrinking the step size - - ! back-track along the search direction - ! NOTE: start with back-tracking the scaled step - xInc(:) = xLambda*newtStepScaled(:) - - ! re-scale the iteration increment - xInc(:) = xInc(:)*xScale(:) - - ! if enthalpy, then need to convert the iteration increment to temperature - !if(nrgFormulation==ix_enthalpy) xInc(ixNrgOnly) = xInc(ixNrgOnly)/dMat(ixNrgOnly) - - ! state vector with proposed iteration increment - stateVecNew = stateVecTrial + xInc - - ! impose solution constraints adjusting state vector and iteration increment - ! NOTE: We may not need to do this (or at least, do ALL of this), as we can probably rely on the line search here - call imposeConstraints(model_decisions,indx_data,prog_data,mpar_data,stateVecNew,stateVecTrial,nState,nSoil,nSnow,cmessage,err) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - xInc = stateVecNew - stateVecTrial - - ! compute the residual vector and function - ! NOTE: This calls eval8summa in an internal subroutine which has access to all data - ! Hence, we only need to include the variables of interest in lineSearch - call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! check line search - if(globalPrintFlag)then - write(*,'(a,1x,i4,1x,e17.10)' ) 'iLine, xLambda = ', iLine, xLambda - write(*,'(a,1x,10(e17.10,1x))') 'fOld,fNew = ', fOld,fNew - write(*,'(a,1x,10(e17.10,1x))') 'fold + alpha*slopeInit*xLambda = ', fold + alpha*slopeInit*xLambda - write(*,'(a,1x,10(e17.10,1x))') 'resVecNew = ', resVecNew(min(iJac1,nState):min(iJac2,nState)) - write(*,'(a,1x,10(e17.10,1x))') 'xInc = ', xInc(min(iJac1,nState):min(iJac2,nState)) - end if - - ! check feasibility - if(.not.feasible) cycle ! go back and impose constraints again - - ! check convergence - ! NOTE: some efficiency gains possible by scaling the full newton step outside the line search loop - converged = checkConv(resVecNew,newtStepScaled*xScale,stateVecNew) - if(converged) return - - ! early return if not computing the line search - if(.not.doLineSearch) return - - ! check if the function is accepted - if(fNew < fold + alpha*slopeInit*xLambda) return - - ! *** - ! *** IF GET TO HERE WE BACKTRACK - ! --> all remaining code simply computes the restricted step multiplier (xLambda) - - ! first backtrack: use quadratic - if(iLine==1)then - xLambdaTemp = -slopeInit / (2._rkind*(fNew - fOld - slopeInit) ) - if(xLambdaTemp > 0.5_rkind*xLambda) xLambdaTemp = 0.5_rkind*xLambda - - ! subsequent backtracks: use cubic - else - - ! check that we did not back-track all the way back to the original value - if(iLine==maxLineSearch)then - message=trim(message)//'backtracked all the way back to the original value' - err=-20; return - end if - - ! define rhs - rhs1 = fNew - fOld - xLambda*slopeInit - rhs2 = fPrev - fOld - xLambdaPrev*slopeInit - - ! define coefficients - aCoef = (rhs1/(xLambda*xLambda) - rhs2/(xLambdaPrev*xLambdaPrev))/(xLambda - xLambdaPrev) - bCoef = (-xLambdaPrev*rhs1/(xLambda*xLambda) + xLambda*rhs2/(xLambdaPrev*xLambdaPrev)) / (xLambda - xLambdaPrev) - - ! check if a quadratic - if(aCoef==0._rkind)then - xLambdaTemp = -slopeInit/(2._rkind*bCoef) - - ! calculate cubic - else - disc = bCoef*bCoef - 3._rkind*aCoef*slopeInit - if(disc < 0._rkind)then - xLambdaTemp = 0.5_rkind*xLambda - else - xLambdaTemp = (-bCoef + sqrt(disc))/(3._rkind*aCoef) - end if - end if ! calculating cubic - - ! constrain to <= 0.5*xLambda - if(xLambdaTemp > 0.5_rkind*xLambda) xLambdaTemp=0.5_rkind*xLambda - - end if ! subsequent backtracks - - ! save results - xLambdaPrev = xLambda - fPrev = fNew - - ! constrain lambda - xLambda = max(xLambdaTemp, 0.1_rkind*xLambda) - - end do lineSearch ! backtrack loop - end associate - - end subroutine lineSearchRefinement - - - ! ********************************************************************************************************* - ! * internal subroutine trustRegionRefinement: refine the iteration increment using trust regions - ! ********************************************************************************************************* - subroutine trustRegionRefinement(in_TRR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_TRR) - ! provide access to the matrix routines - USE matrixOper_module, only: lapackSolv - USE matrixOper_module, only: computeGradient - implicit none - ! input - type(in_type_lineSearchRefinement),intent(in) :: in_TRR ! object for scalar intent(in) arguments -- reusing line search class - real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector - real(rkind),intent(in) :: newtStepScaled(:) ! scaled newton step - real(rkind),intent(in) :: aJacScaled(:,:) ! scaled jacobian matrix - real(rkind),intent(in) :: rVecScaled(:) ! scaled residual vector - ! output - real(rkind),intent(out) :: stateVecNew(:) ! new state vector - real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector - real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector - type(out_type_lineSearchRefinement) :: out_TRR ! object for scalar intent(in) arguments -- reusing line search class - ! -------------------------------------------------------------------------------------------------------- - ! local variables - - ! .. needed .. - - ! -------------------------------------------------------------------------------------------------------- - associate(& - ! input - doTrustRefinement => in_TRR % doSearch ,& ! flag to refine using trust regions - fOld => in_TRR % fOld ,& ! old function value - nState => in_SS4NR % nState ,& ! total number of state variables - ! output - fNew => out_TRR % fNew ,& ! new function evaluation - converged => out_TRR % converged ,& ! convergence flag - err => out_TRR % err ,& ! error code - message => out_TRR % message & ! error message - &) - - err=0; message='trustRegionRefinement/' - converged =.false. - - ! check the need to refine the step - if (doTrustRefinement) then - - ! (check vectors) - if (size(stateVecTrial)/=nState .or. size(newtStepScaled)/=nState .or. size(rVecScaled)/=nState)then - message=trim(message)//'unexpected size of input vectors' - err=20; return - end if - - ! (check matrix) - if (size(aJacScaled,1)/=nState .or. size(aJacScaled,2)/=nState) then - message=trim(message)//'unexpected size of Jacobian matrix' - err=20; return - end if - - ! dummy check for the function - if (fold==realMissing) print*, 'missing' - - ! dummy - stateVecNew = realMissing - fluxVecNew = realMissing - resVecNew = quadMissing - fNew = realMissing - converged = .true. - - - end if ! if doing the trust region refinement - - message=trim(message)//'routine not implemented yet' - err=20; return - - end associate - - end subroutine trustRegionRefinement - - - ! ********************************************************************************************************* - ! * internal subroutine safeRootfinder: refine the 1-d iteration increment using brackets - ! ********************************************************************************************************* - subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fluxVecNew,resVecNew,out_SRF) - USE,intrinsic :: ieee_arithmetic,only:ieee_is_nan ! IEEE arithmetic (check NaN) - USE globalData,only:dNaN ! double precision NaN - implicit none - ! input - real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector - real(rkind),intent(in) :: rVecScaled(:) ! scaled residual vector - real(rkind),intent(in) :: newtStepScaled(:) ! scaled newton step - ! output - real(rkind),intent(out) :: stateVecNew(:) ! new state vector - real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector - real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector - type(out_type_lineSearchRefinement),intent(out) :: out_SRF ! object for scalar intent(out) arguments (reusing lineSearchRefinement class) - ! -------------------------------------------------------------------------------------------------------- - ! local variables - character(len=256) :: cmessage ! error message of downwind routine - real(rkind),parameter :: relTolerance=0.005_rkind ! force bi-section if trial is slightly larger than (smaller than) xmin (xmax) - real(rkind) :: xTolerance ! relTolerance*(xmax-xmin) - real(rkind) :: xInc(in_SS4NR % nState) ! iteration increment (re-scaled to original units of the state vector) - real(rkind) :: rVec(in_SS4NR % nState) ! residual vector (re-scaled to original units of the state equation) - logical(lgt) :: feasible ! feasibility of the solution - logical(lgt) :: doBisection ! flag to do the bi-section - logical(lgt) :: bracketsDefined ! flag to define if the brackets are defined - integer(i4b),parameter :: nCheck=100 ! number of times to check the model state variables - real(rkind),parameter :: delX=1._rkind ! trial increment - ! -------------------------------------------------------------------------------------------------------- - associate(& - iter => in_SS4NR % iter ,& ! intent(in): iteration index - nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers - nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers - nState => in_SS4NR % nState ,& ! intent(in): total number of state - xMin => io_SS4NR % xMin ,& ! intent(inout): bracket of the root - xMax => io_SS4NR % xMax ,& ! intent(inout): bracket of the root - fNew => out_SRF % fNew ,& ! intent(out): new function evaluation - converged => out_SRF % converged ,& ! intent(out): convergence flag - err => out_SRF % err ,& ! intent(out): error code - message => out_SRF % message & ! intent(out): error message - &) - - err=0; message='safeRootfinder/' - converged = .false. - - ! check scalar - if (size(stateVecTrial)/=1 .or. size(rVecScaled)/=1 .or. size(newtStepScaled)/=1) then - message=trim(message)//'unexpected size of input vectors' - err=20; return - end if - - ! initialize brackets to rkind precision NaN - if (iter==1) then - xMax = dNaN - xMin = dNaN - end if - - ! get the residual vector - rVec = real(rVecScaled, rkind)*real(fScale, rkind) - - ! update brackets - if (rVec(1)<0._rkind) then - xMin = stateVecTrial(1) - else - xMax = stateVecTrial(1) - end if - - ! get the iteration increment - xInc = newtStepScaled*xScale - - ! ***** - ! * case 1: the iteration increment is the same sign as the residual vector - if (xInc(1)*rVec(1) > 0._rkind) then - - ! get brackets if they do not exist - if ( ieee_is_nan(xMin) .or. ieee_is_nan(xMax) ) then - call getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors - end if - - ! use bi-section - stateVecNew(1) = 0.5_rkind*(xMin + xMax) - - ! ***** - ! * case 2: the iteration increment is the correct sign - else - - ! state vector with proposed iteration increment - stateVecNew = stateVecTrial + xInc - - ! impose solution constraints adjusting state vector and iteration increment - call imposeConstraints(model_decisions,indx_data,prog_data,mpar_data,stateVecNew,stateVecTrial,nState,nSoil,nSnow,cmessage,err) - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors - xInc = stateVecNew - stateVecTrial - - end if ! if the iteration increment is the same sign as the residual vector - - ! bi-section - bracketsDefined = ( .not.ieee_is_nan(xMin) .and. .not.ieee_is_nan(xMax) ) ! check that the brackets are defined - if (bracketsDefined) then - xTolerance = relTolerance*(xMax-xMin) - doBisection = (stateVecNew(1)xMax-xTolerance) - if (doBisection) stateVecNew(1) = 0.5_rkind*(xMin+xMax) - end if - - ! evaluate summa - call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors - - ! check feasibility (should be feasible because of the call to imposeConstraints, except if canopyTemp>canopyTempMax (500._rkind)) - if (.not.feasible) then; err=20; message=trim(message)//'state vector not feasible'; return; end if - - ! check convergence - converged = checkConv(resVecNew,xInc,stateVecNew) - - end associate - - end subroutine safeRootfinder - - ! ********************************************************************************************************* - ! * internal subroutine getBrackets: get the brackets - ! ********************************************************************************************************* - subroutine getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,message) - USE,intrinsic :: ieee_arithmetic,only:ieee_is_nan ! IEEE arithmetic (check NaN) - implicit none - ! dummies - real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector - real(rkind),intent(out) :: stateVecNew(:) ! new state vector - real(rkind),intent(out) :: xMin,xMax ! constraints - integer(i4b),intent(inout) :: err ! error code - character(*),intent(out) :: message ! error message - ! locals - real(rkind) :: stateVecPrev(in_SS4NR % nState) ! iteration state vector - integer(i4b) :: iCheck ! check the model state variables - integer(i4b),parameter :: nCheck=100 ! number of times to check the model state variables - logical(lgt) :: feasible ! feasibility of the solution - real(rkind),parameter :: delX=1._rkind ! trial increment - real(rkind) :: xIncrement(in_SS4NR % nState) ! trial increment - ! initialize - err=0; message='getBrackets/' - - ! initialize state vector - stateVecNew = stateVecTrial - stateVecPrev = stateVecNew - - ! get xIncrement - xIncrement = -sign((/delX/),rVec) - - ! try the increment a few times - do iCheck=1,nCheck - - ! state vector with proposed iteration increment - stateVecNew = stateVecPrev + xIncrement - - ! impose solution constraints adjusting state vector and iteration increment - associate(& - nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers - nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers - nState => in_SS4NR % nState & ! intent(in): total number of state variables - &) - call imposeConstraints(model_decisions,indx_data,prog_data,mpar_data,stateVecNew,stateVecPrev,nState,nSoil,nSnow,cmessage,err) - end associate - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - xIncrement = stateVecNew - stateVecPrev - - ! evaluate summa - associate(fnew => out_SS4NR % fnew) - call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) - end associate - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! check feasibility (should be feasible because of the call to imposeConstraints, except if canopyTemp>canopyTempMax (500._rkind)) - if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif - - ! update brackets - if(real(resVecNew(1), rkind)<0._rkind)then - xMin = stateVecNew(1) - else - xMax = stateVecNew(1) - endif - - ! check that the brackets are defined - if( .not.ieee_is_nan(xMin) .and. .not.ieee_is_nan(xMax) ) exit - - ! check that we found the brackets - if(iCheck==nCheck)then - message=trim(message)//'could not fix the problem where residual and iteration increment are of the same sign' - err=20; return - endif - - ! Save the state vector - stateVecPrev = stateVecNew - - end do ! multiple checks - - end subroutine getBrackets - - - ! ********************************************************************************************************* - ! * internal subroutine numJacobian: compute the numerical Jacobian matrix - ! ********************************************************************************************************* - subroutine numJacobian(stateVec,dMat,nJac,err,message) - implicit none - ! dummies - real(rkind),intent(in) :: stateVec(:) ! trial state vector - real(rkind),intent(in) :: dMat(:) ! diagonal matrix - ! output - real(rkind),intent(out) :: nJac(:,:) ! numerical Jacobian - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ---------------------------------------------------------------------------------------------------------- - ! local - character(len=256) :: cmessage ! error message of downwind routine - real(rkind),parameter :: dx=1.e-8_rkind ! finite difference increment - real(rkind),dimension(in_SS4NR % nState) :: stateVecPerturbed ! perturbed state vector - real(rkind),dimension(in_SS4NR % nState) :: fluxVecInit,fluxVecJac ! flux vector (mized units) - real(qp),dimension(in_SS4NR % nState) :: resVecInit,resVecJac ! qp ! residual vector (mixed units) - real(rkind) :: func ! function value - logical(lgt) :: feasible ! flag to denote the feasibility of the solution - integer(i4b) :: iJac ! index of row of the Jacobian matrix - integer(i4b),parameter :: ixNumFlux=1001 ! named variable for the flux-based form of the numerical Jacobian - integer(i4b),parameter :: ixNumRes=1002 ! named variable for the residual-based form of the numerical Jacobian - integer(i4b) :: ixNumType=ixNumRes ! method used to calculate the numerical Jacobian - ! ---------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='numJacobian/' - - ! compute initial function evaluation - call eval8summa_wrapper(stateVec,fluxVecInit,resVecInit,func,feasible,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - if(.not.feasible)then; message=trim(message)//'initial state vector not feasible'; err=20; return; endif - - ! get a copy of the state vector to perturb - stateVecPerturbed(:) = stateVec(:) - - ! loop through state variables - do iJac=1,in_SS4NR % nState - - !print*, 'iJac = ', iJac - !globalPrintFlag = merge(.true.,.false., iJac==1) - - ! perturb state vector - stateVecPerturbed(iJac) = stateVec(iJac) + dx - - ! compute function evaluation - call eval8summa_wrapper(stateVecPerturbed,fluxVecJac,resVecJac,func,feasible,err,cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif - !write(*,'(a,1x,2(f30.20,1x))') 'resVecJac(101:102) = ', resVecJac(101:102) - - ! compute the row of the Jacobian matrix - select case(ixNumType) - case(ixNumRes); nJac(:,iJac) = real(resVecJac - resVecInit, kind(rkind) )/dx ! Jacobian based on residuals - case(ixNumFlux); nJac(:,iJac) = -in_SS4NR % dt_cur*(fluxVecJac(:) - fluxVecInit(:))/dx ! Jacobian based on fluxes - case default; err=20; message=trim(message)//'Jacobian option not found'; return - end select - - ! if flux option then add in the diagonal matrix - if(ixNumType==ixNumFlux) nJac(iJac,iJac) = nJac(iJac,iJac) + dMat(iJac) - - ! set the state back to the input value - stateVecPerturbed(iJac) = stateVec(iJac) - - end do ! (looping through state variables) - - ! print the Jacobian - print*, '** numerical Jacobian:', ixNumType==ixNumRes - write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=min(iJac1,in_SS4NR % nState),min(iJac2,in_SS4NR % nState)) - do iLayer=min(iJac1,in_SS4NR % nState),min(iJac2,in_SS4NR % nState) - write(*,'(i4,1x,100(e12.5,1x))') iLayer, nJac(min(iJac1,in_SS4NR % nState):min(iJac2,in_SS4NR % nState),iLayer) - end do - !print*, 'PAUSE: testing Jacobian'; read(*,*) - - end subroutine numJacobian - - ! ********************************************************************************************************* - ! * internal subroutine testBandMat: compute the full Jacobian matrix and decompose into a band matrix - ! ********************************************************************************************************* - - subroutine testBandMat(check,err,message) - ! dummy variables - logical(lgt),intent(in) :: check ! flag to pause - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! local variables - real(rkind) :: fullJac(in_SS4NR % nState,in_SS4NR % nState) ! full Jacobian matrix - real(rkind) :: bandJac(in_SS4NR % nLeadDim,in_SS4NR % nState) ! band Jacobian matrix - integer(i4b) :: iState,jState ! indices of the state vector - character(LEN=256) :: cmessage ! error message of downwind routine - ! class objects for subroutine arguments - type(in_type_computJacob) :: in_computJacob - type(out_type_computJacob) :: out_computJacob - ! initialize error control - err=0; message='testBandMat/' - - ! check - if (in_SS4NR % nLeadDim == in_SS4NR % nState) then - message=trim(message)//'do not expect nLeadDim==nState: check that are computing the band diagonal matrix'//& - ' (is forceFullMatrix==.true.?)' - err=20; return - end if - - ! compute the full Jacobian matrix - call initialize_computJacob_testBandMat - call computJacob(in_computJacob,indx_data,prog_data,diag_data,deriv_data,dBaseflow_dMatric,dMat,fullJac,out_computJacob) - call finalize_computJacob_testBandMat(err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - ! initialize band matrix - bandJac(:,:) = 0._rkind - - ! transfer into the lapack band diagonal structure - do iState=1,in_SS4NR % nState - do jState=max(1,iState-ku),min(in_SS4NR % nState,iState+kl) - bandJac(kl + ku + 1 + jState - iState, iState) = fullJac(jState,iState) - end do - end do - - ! print results - print*, '** test banded analytical Jacobian:' - write(*,'(a4,1x,100(i17,1x))') 'xCol', (iState, iState=iJac1,iJac2) - do iState=kl+1,in_SS4NR % nLeadDim; write(*,'(i4,1x,100(e17.10,1x))') iState, bandJac(iState,iJac1:iJac2); end do - - ! check if the need to pause - if(check)then - print*, 'PAUSE: testing banded analytical Jacobian' - read(*,*) - endif - - end subroutine testBandMat - - subroutine initialize_computJacob_testBandMat - ! *** Transfer data from out_computJacob class object to local variables in testBandMat *** - associate(& - dt_cur => in_SS4NR % dt_cur ,& ! intent(in): current stepsize - nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers - nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers - nLayers => in_SS4NR % nLayers ,& ! intent(in): total number of layers - computeVegFlux => in_SS4NR % computeVegFlux & ! intent(in): flag to indicate if computing fluxes over vegetation - &) - call in_computJacob % initialize(dt_cur,nSnow,nSoil,nLayers,computeVegFlux,.false.,ixFullMatrix) - end associate - end subroutine initialize_computJacob_testBandMat - - subroutine finalize_computJacob_testBandMat(err,cmessage) - ! *** Transfer data from out_computJacob class object to local variables in testBandMat *** - ! Note: subroutine arguments are needed because testBandMat is an internal procedure - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: cmessage ! error message of downwind routine - call out_computJacob % finalize(err,cmessage) - end subroutine finalize_computJacob_testBandMat - - subroutine initialize_computJacob_summaSolve4numrec - ! *** Transfer data to in_computJacob class object from local variables in summaSolve4numrec *** - associate(& - ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision,& ! intent(in): [i4b] groundwater parameterization - dt_cur => in_SS4NR % dt_cur ,& ! intent(in): current stepsize - nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers - nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers - nLayers => in_SS4NR % nLayers ,& ! intent(in): total number of layers - ixMatrix => in_SS4NR % ixMatrix ,& ! intent(in): type of matrix (full or band diagonal) - computeVegFlux => in_SS4NR % computeVegFlux & ! intent(in): flag to indicate if computing fluxes over vegetation - &) - call in_computJacob % initialize(dt_cur,nSnow,nSoil,nLayers,computeVegFlux,(ixGroundwater==qbaseTopmodel),ixMatrix) - end associate - end subroutine initialize_computJacob_summaSolve4numrec - - subroutine finalize_computJacob_summaSolve4numrec - ! *** Transfer data from out_computJacob class object to local variables in summaSolve4numrec *** - associate(err => out_SS4NR % err) - call out_computJacob % finalize(err,cmessage) - end associate - end subroutine finalize_computJacob_summaSolve4numrec - - ! ********************************************************************************************************* - ! * internal subroutine eval8summa_wrapper: compute the right-hand-side vector - ! ********************************************************************************************************* - ! NOTE: This is simply a wrapper routine for eval8summa, to reduce the number of calling arguments - ! An internal subroutine, so have access to all data in the main subroutine - subroutine eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,message) - USE eval8summa_module,only:eval8summa ! simulation of fluxes and residuals given a trial state vector - implicit none - ! input - real(rkind),intent(in) :: stateVecNew(:) ! updated state vector - ! output - real(rkind),intent(out) :: fluxVecNew(:) ! updated flux vector - real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! updated residual vector - real(rkind),intent(out) :: fNew ! new function value - logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ---------------------------------------------------------------------------------------------------------- - ! local - character(len=256) :: cmessage ! error message of downwind routine - ! ---------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='eval8summa_wrapper/' - - associate(& - dt_cur => in_SS4NR % dt_cur ,& ! intent(in): current stepsize - dt => in_SS4NR % dt ,& ! intent(in): entire time step for drainage pond rate - nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers - nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers - nLayers => in_SS4NR % nLayers ,& ! intent(in): total number of layers - nState => in_SS4NR % nState ,& ! intent(in): total number of state variables - firstSubStep => in_SS4NR % firstSubStep ,& ! intent(in): flag to indicate if we are processing the first sub-step - computeVegFlux => in_SS4NR % computeVegFlux ,& ! intent(in): flag to indicate if computing fluxes over vegetation - scalarSolution => in_SS4NR % scalarSolution ,& ! intent(in): flag to denote if implementing the scalar solution - firstFluxCall => io_SS4NR % firstFluxCall ,& ! intent(inout): flag to indicate if we are processing the first flux call - ixSaturation => io_SS4NR % ixSaturation & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - &) - ! compute the flux and the residual vector for a given state vector - call eval8summa(& - ! input: model control - dt_cur, & ! intent(in): current stepsize - dt, & ! intent(in): length of the time step (seconds) - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - nState, & ! intent(in): total number of state variables - .false., & ! intent(in): not inside Sundials solver - firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step - firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call - .false., & ! intent(in): not processing the first iteration in a splitting operation - computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: state vectors - stateVecNew, & ! intent(in): updated model state vector - fScale, & ! intent(in): characteristic scale of the function evaluations - sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) - ! input: data structures - model_decisions, & ! intent(in): model decisions - lookup_data, & ! intent(in): lookup tables - type_data, & ! intent(in): type of vegetation and soil - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - forc_data, & ! intent(in): model forcing data - bvar_data, & ! intent(in): average model variables for the entire basin - prog_data, & ! intent(in): model prognostic variables for a local HRU - ! input-output: data structures - indx_data, & ! intent(inout): index data - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! input-output: baseflow - ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - ! output - feasible, & ! intent(out): flag to denote the feasibility of the solution - fluxVecNew, & ! intent(out): new flux vector - resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equation - resVecNew, & ! intent(out): new residual vector - fNew, & ! intent(out): new function evaluation - err,cmessage) ! intent(out): error control - end associate - - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - - end subroutine eval8summa_wrapper - - - ! ********************************************************************************************************* - ! internal function checkConv: check convergence based on the residual vector - ! ********************************************************************************************************* - function checkConv(rVec,xInc,xVec) - implicit none - ! dummies - real(rkind),intent(in) :: rVec(:) ! residual vector (mixed units) - real(rkind),intent(in) :: xInc(:) ! iteration increment (mixed units) - real(rkind),intent(in) :: xVec(:) ! state vector (mixed units) - logical(lgt) :: checkConv ! flag to denote convergence - ! locals - real(rkind),dimension(mSoil) :: psiScale ! scaling factor for matric head - real(rkind),parameter :: xSmall=1.e-0_rkind ! a small offset - real(rkind),parameter :: scalarTighten=0.1_rkind ! scaling factor for the scalar solution - real(rkind) :: soilWatbalErr ! error in the soil water balance - real(rkind) :: canopy_max ! absolute value of the residual in canopy water (kg m-2) - real(rkind),dimension(1) :: energy_max ! maximum absolute value of the energy residual (J m-3) - real(rkind),dimension(1) :: liquid_max ! maximum absolute value of the volumetric liquid water content residual (-) - real(rkind),dimension(1) :: matric_max ! maximum absolute value of the matric head iteration increment (m) - real(rkind) :: aquifer_max ! absolute value of the residual in aquifer water (m) - logical(lgt) :: canopyConv ! flag for canopy water balance convergence - logical(lgt) :: watbalConv ! flag for soil water balance convergence - logical(lgt) :: liquidConv ! flag for residual convergence - logical(lgt) :: matricConv ! flag for matric head convergence - logical(lgt) :: energyConv ! flag for energy convergence - logical(lgt) :: aquiferConv ! flag for aquifer water balance convergence - ! ------------------------------------------------------------------------------------------------------------------------------------------------- - ! association to variables in the data structures - associate(& - ! model control - iter => in_SS4NR % iter ,& ! intent(in): iteration index - nsnow => in_SS4NR % nsnow ,& ! intent(in): number of snow layers - scalarSolution => in_SS4NR % scalarSolution ,& ! intent(in): flag to denote if implementing the scalar solution - ! convergence parameters - absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1),& ! intent(in): [dp] absolute convergence tolerance for vol frac liq water (-) - absConvTol_matric => mpar_data%var(iLookPARAM%absConvTol_matric)%dat(1),& ! intent(in): [dp] absolute convergence tolerance for matric head (m) - absConvTol_energy => mpar_data%var(iLookPARAM%absConvTol_energy)%dat(1),& ! intent(in): [dp] absolute convergence tolerance for energy (J m-3) - ! layer depth - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! model indices - ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of aquifer storage state variable - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for all energy states - ixHydOnly => indx_data%var(iLookINDEX%ixHydOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for all hydrology states - ixMatOnly => indx_data%var(iLookINDEX%ixMatOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for matric head state variables in the state vector - ixMatricHead => indx_data%var(iLookINDEX%ixMatricHead)%dat ,& ! intent(in): [i4b(:)] list of indices for matric head in the soil vector - fnew => out_SS4NR % fnew & ! intent(in): [dp] new function evaluations - ) ! making associations with variables in the data structures - ! ------------------------------------------------------------------------------------------------------------------------------------------------- - - ! check convergence based on the canopy water balance - if(ixVegHyd/=integerMissing)then - canopy_max = real(abs(rVec(ixVegHyd)), rkind)*iden_water - canopyConv = (canopy_max < absConvTol_liquid) ! absolute error in canopy water balance (mm) - else - canopy_max = realMissing - canopyConv = .true. - endif - - ! check convergence based on the residuals for energy (J m-3) - if(size(ixNrgOnly)>0)then - energy_max = real(maxval(abs( rVec(ixNrgOnly) )), rkind) - energyConv = (energy_max(1) < absConvTol_energy) ! (based on the residual) - else - energy_max = realMissing - energyConv = .true. - endif - - ! check convergence based on the residuals for volumetric liquid water content (-) - if(size(ixHydOnly)>0)then - liquid_max = real(maxval(abs( rVec(ixHydOnly) ) ), rkind) - ! (tighter convergence for the scalar solution) - if(scalarSolution)then - liquidConv = (liquid_max(1) < absConvTol_liquid*scalarTighten) ! (based on the residual) - else - liquidConv = (liquid_max(1) < absConvTol_liquid) ! (based on the residual) - endif - else - liquid_max = realMissing - liquidConv = .true. - endif - - ! check convergence based on the iteration increment for matric head - ! NOTE: scale by matric head to avoid unnecessairly tight convergence when there is no water - if(size(ixMatOnly)>0)then - psiScale = abs( xVec(ixMatOnly) ) + xSmall ! avoid divide by zero - matric_max = maxval(abs( xInc(ixMatOnly)/psiScale ) ) - matricConv = (matric_max(1) < absConvTol_matric) ! NOTE: based on iteration increment - else - matric_max = realMissing - matricConv = .true. - endif - - ! check convergence based on the soil water balance error (m) - if(size(ixMatOnly)>0)then - soilWatBalErr = sum( real(rVec(ixMatOnly), rkind)*mLayerDepth(nSnow+ixMatricHead) ) - watbalConv = (abs(soilWatbalErr) < absConvTol_liquid) ! absolute error in total soil water balance (m) - else - soilWatbalErr = realMissing - watbalConv = .true. - endif - - ! check convergence based on the aquifer storage - if(ixAqWat/=integerMissing)then - aquifer_max = real(abs(rVec(ixAqWat)), rkind)*iden_water - aquiferConv = (aquifer_max < absConvTol_liquid) ! absolute error in aquifer water balance (mm) - else - aquifer_max = realMissing - aquiferConv = .true. - endif - - ! final convergence check - checkConv = (canopyConv .and. watbalConv .and. matricConv .and. liquidConv .and. energyConv .and. aquiferConv) - - ! print progress towards solution - if(globalPrintFlag)then - write(*,'(a,1x,i4,1x,7(e15.5,1x),7(L1,1x))') 'check convergence: ', iter, & - fNew, matric_max(1), liquid_max(1), energy_max(1), canopy_max, aquifer_max, soilWatBalErr, matricConv, liquidConv, energyConv, watbalConv, canopyConv, aquiferConv, watbalConv - endif - - ! end associations with variables in the data structures - end associate - - end function checkConv - - end subroutine summaSolve4numrec - - -end module summaSolve4numrec_module From df3cd1277e61b4fb10c651b9072185de7ba27b2c Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 15 May 2024 03:14:55 -0600 Subject: [PATCH 1305/1472] Added summaSolve4homegrown.f90 to git tracking. --- build/source/engine/summaSolve4homegrown.f90 | 1229 ++++++++++++++++++ 1 file changed, 1229 insertions(+) create mode 100644 build/source/engine/summaSolve4homegrown.f90 diff --git a/build/source/engine/summaSolve4homegrown.f90 b/build/source/engine/summaSolve4homegrown.f90 new file mode 100644 index 000000000..c62c2e869 --- /dev/null +++ b/build/source/engine/summaSolve4homegrown.f90 @@ -0,0 +1,1229 @@ +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . + +module summaSolve4numrec_module + +! data types +USE nrtype + +! access the global print flag +USE globalData,only:globalPrintFlag + +! domain types +USE globalData,only:iname_snow ! named variables for snow +USE globalData,only:iname_soil ! named variables for soil + +! access missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing double precision number +USE globalData,only:quadMissing ! missing quadruple precision number + +! access named variables to describe the form and structure of the matrices used in the numerical solver +USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix +USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix +USE globalData,only: ku ! number of super-diagonal bands +USE globalData,only: kl ! number of sub-diagonal bands +USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix +USE globalData,only: iJac1 ! first layer of the Jacobian to print +USE globalData,only: iJac2 ! last layer of the Jacobian to print + +! named variables to describe the state variable type +USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space +USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy +USE globalData,only:iname_watCanopy ! named variable defining the mass of water on the vegetation canopy +USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers +USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers +USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers +USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers +USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers + +! indices of elements of data structure +USE var_lookup,only:iLookFLUX ! named variables for structure elements +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookPARAM ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements +USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure + +USE multiconst,only:& + iden_water ! intrinsic density of liquid water (kg m-3) + +! provide access to the derived types to define the data structures +USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! lookup tables + model_options, & ! defines the model decisions + in_type_computJacob, & ! class for computJacob arguments + out_type_computJacob, & ! class for computJacob arguments + in_type_lineSearchRefinement, & ! class for lineSearchRefinement arguments + out_type_lineSearchRefinement,& ! class for lineSearchRefinement arguments + in_type_summaSolve4numrec, & ! class for summaSolve4numrec arguments + io_type_summaSolve4numrec, & ! class for summaSolve4numrec arguments + out_type_summaSolve4numrec ! class for summaSolve4numrec arguments + + +! look-up values for the choice of groundwater parameterization +USE mDecisions_module,only: & + qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization + bigBucket, & ! a big bucket (lumped aquifer model) + noExplicit ! no explicit groundwater parameterization + +! look-up values for method used to compute derivative +USE mDecisions_module,only: & + numerical, & ! numerical solution + analytical ! analytical solution + +implicit none +private +public::summaSolve4numrec +contains + + ! *********************************************************************************************************************** + ! public subroutine summaSolve4numrec: calculate the iteration increment, evaluate the new state, and refine if necessary + ! *********************************************************************************************************************** + subroutine summaSolve4numrec(& + in_SS4NR, & ! intent(in): model control and previous function value + ! input: model control + !firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + ! input: state vectors + stateVecTrial, & ! intent(in): trial state vector + !xMin,xMax, & ! intent(inout): brackets of the root + fScale, & ! intent(in): characteristic scale of the function evaluations + xScale, & ! intent(in): characteristic scale of the state vector + rVec, & ! intent(in): residual vector + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + dMat, & ! intent(inout): diagonal matrix (excludes flux derivatives) + ! input: data structures + model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + indx_data, & ! intent(inout): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + !ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(inout): derivative in baseflow w.r.t. matric head (s-1) + io_SS4NR, & ! intent(inout): first flux call flag, root brackets, index of lowest saturated layer + ! output + stateVecNew, & ! intent(out): new state vector + fluxVecNew, & ! intent(out): new flux vector + resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equation + resVecNew, & ! intent(out): new residual vector + out_SS4NR) ! intent(out): new function evaluation, convergence flag, and error control + USE computJacob_module, only: computJacob + USE eval8summa_module, only: imposeConstraints + USE matrixOper_module, only: lapackSolv + USE matrixOper_module, only: scaleMatrices + implicit none + ! -------------------------------------------------------------------------------------------------------------------------------- + type(in_type_summaSolve4numrec),intent(in) :: in_SS4NR ! model control variables and previous function evaluation + type(io_type_summaSolve4numrec) :: io_SS4NR ! first flux call flag and baseflow variables + ! input: model control +! logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call + ! input: state vectors + real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector +! real(rkind),intent(inout) :: xMin,xMax ! brackets of the root + real(rkind),intent(in) :: fScale(:) ! characteristic scale of the function evaluations + real(rkind),intent(in) :: xScale(:) ! characteristic scale of the state vector + real(qp),intent(in) :: rVec(:) ! NOTE: qp ! residual vector + real(qp),intent(inout) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) + real(rkind),intent(inout) :: dMat(:) ! diagonal matrix (excludes flux derivatives) + ! input: data structures + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup), intent(in) :: lookup_data ! lookup tables + type(var_i), intent(in) :: type_data ! type of vegetation and soil + type(var_d), intent(in) :: attr_data ! spatial attributes + type(var_dlength), intent(in) :: mpar_data ! model parameters + type(var_d), intent(in) :: forc_data ! model forcing data + type(var_dlength), intent(in) :: bvar_data ! model variables for the local basin + type(var_dlength), intent(in) :: prog_data ! prognostic variables for a local HRU + ! output: data structures + type(var_ilength),intent(inout) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow +! integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + real(rkind),intent(inout) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + ! output: flux and residual vectors + real(rkind),intent(out) :: stateVecNew(:) ! new state vector + real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector + real(rkind),intent(out) :: resSinkNew(:) ! sink terms on the RHS of the flux equation + real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector + type(out_type_summaSolve4numrec),intent(out) :: out_SS4NR ! new function evaluation, convergence flag, and error control + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + ! -------------------------------------------------------------------------------------------------------------------------------- + ! Jacobian matrix + logical(lgt),parameter :: doNumJacobian=.false. ! flag to compute the numerical Jacobian matrix + logical(lgt),parameter :: testBandDiagonal=.false. ! flag to test the band diagonal Jacobian matrix + real(rkind) :: nJac(in_SS4NR % nState,in_SS4NR % nState) ! numerical Jacobian matrix + real(rkind) :: aJac(in_SS4NR % nLeadDim,in_SS4NR % nState) ! Jacobian matrix + real(rkind) :: aJacScaled(in_SS4NR % nLeadDim,in_SS4NR % nState) ! Jacobian matrix (scaled) + real(rkind) :: aJacScaledTemp(in_SS4NR % nLeadDim,in_SS4NR % nState) ! Jacobian matrix (scaled) -- temporary copy since decomposed in lapack + ! solution/step vectors + real(rkind),dimension(in_SS4NR % nState) :: rVecScaled ! residual vector (scaled) + real(rkind),dimension(in_SS4NR % nState) :: newtStepScaled ! full newton step (scaled) + ! step size refinement + logical(lgt) :: doRefine ! flag for step refinement + integer(i4b),parameter :: ixLineSearch=1001 ! step refinement = line search + integer(i4b),parameter :: ixTrustRegion=1002 ! step refinement = trust region + integer(i4b),parameter :: ixStepRefinement=ixLineSearch ! decision for the numerical solution + ! general + integer(i4b) :: mSoil ! number of soil layers in solution vector + integer(i4b) :: iLayer ! row index + integer(i4b) :: jLayer ! column index + logical(lgt) :: globalPrintFlagInit ! initial global print flag + logical(lgt) :: return_flag ! flag that controls execution of return statements + character(LEN=256) :: cmessage ! error message of downwind routine + ! class objects for subroutine arguments + type(in_type_computJacob) :: in_computJacob ! computJacob + type(out_type_computJacob) :: out_computJacob ! computJacob + type(in_type_lineSearchRefinement) :: in_LSR ! lineSearchRefinement + type(out_type_lineSearchRefinement) :: out_LSR ! lineSearchRefinement + type(in_type_lineSearchRefinement) :: in_TRR ! trustRegionRefinement + type(out_type_lineSearchRefinement) :: out_TRR ! trustRegionRefinement + type(out_type_lineSearchRefinement) :: out_SRF ! safeRootFinder + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! ***** Compute the Newton Step ***** + + call initialize_summaSolve4numrec; if (return_flag) return ! initial setup -- return if error + + call update_Jacobian; if (return_flag) return ! compute Jacobian for Newton step -- return if error + + call solve_linear_system; if (return_flag) return ! solve the linear system for the Newton step -- return if error + + call refine_Newton_step; if (return_flag) return ! refine Newton step if needed -- return if error + + associate(err => out_SS4NR % err,message => out_SS4NR % message) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors + end associate + + contains + + subroutine initialize_summaSolve4numrec + ! *** Initial steps for the summaSolve4numrec algorithm (computing the Newton step) *** + + associate(& + err => out_SS4NR % err ,& + message => out_SS4NR % message & + &) + ! initialize error control + err=0; message='summaSolve4numrec/' + return_flag=.false. ! initialize return flag + + ! choose Jacobian type + select case(model_decisions(iLookDECISIONS%fDerivMeth)%iDecision) + case(numerical) + err=20; message=trim(message)//'numerical derivatives are not implemented for BE numerical Recipes solver'; + return_flag=.true.; return + case(analytical); ! this is fine + case default + err=20; message=trim(message)//'expect choice numericl or analytic to calculate derivatives for Jacobian'; + return_flag=.true.; return + end select + + end associate + + ! get the number of soil layers in the solution vector + mSoil = size(indx_data%var(iLookINDEX%ixMatOnly)%dat) + + ! initialize the global print flag + globalPrintFlagInit=globalPrintFlag + end subroutine initialize_summaSolve4numrec + + subroutine update_Jacobian + ! *** Update Jacobian used for Newton step *** + + ! compute the analytical Jacobian matrix + ! NOTE: The derivatives were computed in the previous call to computFlux + ! This occurred either at the call to eval8summa at the start of systemSolv + ! or in the call to eval8summa in the previous iteration (within lineSearchRefinement or trustRegionRefinement) + associate(& + err => out_SS4NR % err ,& + message => out_SS4NR % message & + &) + call initialize_computJacob_summaSolve4numrec + call computJacob(in_computJacob,indx_data,prog_data,diag_data,deriv_data,dBaseflow_dMatric,dMat,aJac,out_computJacob) + call finalize_computJacob_summaSolve4numrec + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! (check for errors) + + ! compute the numerical Jacobian matrix + if (doNumJacobian) then + globalPrintFlag=.false. + call numJacobian(stateVecTrial,dMat,nJac,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! (check for errors) + globalPrintFlag=globalPrintFlagInit + end if + end associate + end subroutine update_Jacobian + + subroutine solve_linear_system + ! *** Solve the linear system for the Newton step using LAPACK routines *** + + associate(& + nState => in_SS4NR % nState ,& ! intent(in): total number of state variables + ixMatrix => in_SS4NR % ixMatrix ,& ! intent(in): type of matrix (full or band diagonal) + err => out_SS4NR % err ,& ! intent(out): error code + message => out_SS4NR % message & ! intent(out): error message + &) + ! test the band diagonal matrix + if (testBandDiagonal) then + call testBandMat(check=.true.,err=err,message=cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! (check for errors) + end if + + ! scale the residual vector + rVecScaled(1:nState) = fScale(:)*real(rVec(:), rkind) ! NOTE: residual vector is in quadruple precision + + ! scale matrices + call scaleMatrices(ixMatrix,nState,aJac,fScale,xScale,aJacScaled,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors + + ! debug statement for scaled Jacobian + if (globalPrintFlag .and. ixMatrix==ixBandMatrix) then + print*, '** SCALED banded analytical Jacobian:' + write(*,'(a4,1x,100(i17,1x))') 'xCol', (iLayer, iLayer=iJac1,iJac2) + do iLayer=kl+1,nBands + write(*,'(i4,1x,100(e17.10,1x))') iLayer, (aJacScaled(iLayer,jLayer),jLayer=min(iJac1,nState),min(iJac2,nState)) + end do + end if + + ! copy the scaled matrix, since it is decomposed in lapackSolv + aJacScaledTemp = aJacScaled + + ! compute the newton step: use the lapack routines to solve the linear system A.X=B + call lapackSolv(ixMatrix,nState,aJacScaledTemp,-rVecScaled,newtStepScaled,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors + + ! debug statement for Newton step + if (globalPrintFlag) write(*,'(a,1x,10(e17.10,1x))') 'newtStepScaled = ', newtStepScaled(min(iJac1,nState):min(iJac2,nState)) + + end associate + end subroutine solve_linear_system + + subroutine refine_Newton_step + ! *** Refine the Newton step if necessary *** + + ! initialize the flag for step refinement + doRefine=.true. + + ! * case 1: state vector + ! compute the flux vector and the residual, and (if necessary) refine the iteration increment + ! NOTE: in 99.9% of cases newtStep will be used (no refinement) + + associate(& + fOld => in_SS4NR % fOld ,& + fnew => out_SS4NR % fnew ,& + converged => out_SS4NR % converged,& + err => out_SS4NR % err ,& + message => out_SS4NR % message & + &) + if (size(stateVecTrial)>1) then + + ! try to backtrack + select case(ixStepRefinement) + case(ixLineSearch) + call in_LSR % initialize(doRefine,fOld) + call lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_LSR) + call out_LSR % finalize(fNew,converged,err,cmessage) + case(ixTrustRegion) + call in_TRR % initialize(doRefine,fOld) + call trustRegionRefinement(in_TRR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_TRR) + call out_TRR % finalize(fNew,converged,err,cmessage) + case default; err=20; message=trim(message)//'unable to identify numerical solution'; return_flag=.true.; return + end select + + ! check warnings: negative error code = warning; in this case back-tracked to the original value + ! NOTE: Accept the full newton step if back-tracked to the original value + if (err<0) then + doRefine=.false.; + call in_LSR % initialize(doRefine,fOld) + call lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_LSR) + call out_LSR % finalize(fNew,converged,err,cmessage) + end if + + ! * case 2: scalar + else + call safeRootfinder(stateVecTrial,rVecScaled,newtStepScaled,stateVecNew,fluxVecNew,resVecNew,out_SRF) + call out_SRF % finalize(fNew,converged,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors + end if + end associate + end subroutine refine_Newton_step + + ! ********************************************************************************************************* + ! * internal subroutine lineSearchRefinement: refine the iteration increment using line searches + ! ********************************************************************************************************* + subroutine lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_LSR) + ! provide access to the matrix routines + USE matrixOper_module, only: computeGradient + implicit none + ! input + type(in_type_lineSearchRefinement),intent(in) :: in_LSR ! class object for intent(in) arguments + real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector + real(rkind),intent(in) :: newtStepScaled(:) ! scaled newton step + real(rkind),intent(in) :: aJacScaled(:,:) ! scaled jacobian matrix + real(rkind),intent(in) :: rVecScaled(:) ! scaled residual vector + ! output + real(rkind),intent(out) :: stateVecNew(:) ! new state vector + real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector + real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector + type(out_type_lineSearchRefinement),intent(out) :: out_LSR ! class object for intent(out) arguments + ! -------------------------------------------------------------------------------------------------------- + ! local + character(len=256) :: cmessage ! error message of downwind routine + real(rkind) :: gradScaled(in_SS4NR % nState) ! scaled gradient + real(rkind) :: xInc(in_SS4NR % nState) ! iteration increment (re-scaled to original units of the state vector) + logical(lgt) :: feasible ! flag to denote the feasibility of the solution + integer(i4b) :: iLine ! line search index + integer(i4b),parameter :: maxLineSearch=5 ! maximum number of backtracks + real(rkind),parameter :: alpha=1.e-4_rkind ! check on gradient + real(rkind) :: xLambda ! backtrack magnitude + real(rkind) :: xLambdaTemp ! temporary backtrack magnitude + real(rkind) :: slopeInit ! initial slope + real(rkind) :: rhs1,rhs2 ! rhs used to compute the cubic + real(rkind) :: aCoef,bCoef ! coefficients in the cubic + real(rkind) :: disc ! temporary variable used in cubic + real(rkind) :: xLambdaPrev ! previous lambda value (used in the cubic) + real(rkind) :: fPrev ! previous function evaluation (used in the cubic) + ! -------------------------------------------------------------------------------------------------------- + associate(& + ! intent(in) variables + doLineSearch => in_LSR % doSearch ,& ! flag to do the line search + fOld => in_LSR % fOld ,& ! old function value + ! local variables + nSnow => in_SS4NR % nSnow ,& ! number of snow layers + nSoil => in_SS4NR % nSoil ,& ! number of soil layers + nState => in_SS4NR % nState ,& ! total number of state variables + ixMatrix => in_SS4NR % ixMatrix ,& ! type of matrix (full or band diagonal) + ! intent(out) variables + fNew => out_LSR % fNew ,& ! new function evaluation + converged => out_LSR % converged ,& ! convergence flag + err => out_LSR % err ,& ! error code + message => out_LSR % message & ! error message + &) + ! initialize error control + err=0; message='lineSearchRefinement/' + converged =.false. + + ! check the need to compute the line search + if(doLineSearch)then + + ! compute the gradient of the function vector + call computeGradient(ixMatrix,nState,aJacScaled,rVecScaled,gradScaled,err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! compute the initial slope + slopeInit = dot_product(gradScaled,newtStepScaled) + + end if ! if computing the line search + + ! initialize lambda + xLambda=1._rkind + + ! ***** LINE SEARCH LOOP... + lineSearch: do iLine=1,maxLineSearch ! try to refine the function by shrinking the step size + + ! back-track along the search direction + ! NOTE: start with back-tracking the scaled step + xInc(:) = xLambda*newtStepScaled(:) + + ! re-scale the iteration increment + xInc(:) = xInc(:)*xScale(:) + + ! if enthalpy, then need to convert the iteration increment to temperature + !if(nrgFormulation==ix_enthalpy) xInc(ixNrgOnly) = xInc(ixNrgOnly)/dMat(ixNrgOnly) + + ! state vector with proposed iteration increment + stateVecNew = stateVecTrial + xInc + + ! impose solution constraints adjusting state vector and iteration increment + ! NOTE: We may not need to do this (or at least, do ALL of this), as we can probably rely on the line search here + call imposeConstraints(model_decisions,indx_data,prog_data,mpar_data,stateVecNew,stateVecTrial,nState,nSoil,nSnow,cmessage,err) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + xInc = stateVecNew - stateVecTrial + + ! compute the residual vector and function + ! NOTE: This calls eval8summa in an internal subroutine which has access to all data + ! Hence, we only need to include the variables of interest in lineSearch + call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! check line search + if(globalPrintFlag)then + write(*,'(a,1x,i4,1x,e17.10)' ) 'iLine, xLambda = ', iLine, xLambda + write(*,'(a,1x,10(e17.10,1x))') 'fOld,fNew = ', fOld,fNew + write(*,'(a,1x,10(e17.10,1x))') 'fold + alpha*slopeInit*xLambda = ', fold + alpha*slopeInit*xLambda + write(*,'(a,1x,10(e17.10,1x))') 'resVecNew = ', resVecNew(min(iJac1,nState):min(iJac2,nState)) + write(*,'(a,1x,10(e17.10,1x))') 'xInc = ', xInc(min(iJac1,nState):min(iJac2,nState)) + end if + + ! check feasibility + if(.not.feasible) cycle ! go back and impose constraints again + + ! check convergence + ! NOTE: some efficiency gains possible by scaling the full newton step outside the line search loop + converged = checkConv(resVecNew,newtStepScaled*xScale,stateVecNew) + if(converged) return + + ! early return if not computing the line search + if(.not.doLineSearch) return + + ! check if the function is accepted + if(fNew < fold + alpha*slopeInit*xLambda) return + + ! *** + ! *** IF GET TO HERE WE BACKTRACK + ! --> all remaining code simply computes the restricted step multiplier (xLambda) + + ! first backtrack: use quadratic + if(iLine==1)then + xLambdaTemp = -slopeInit / (2._rkind*(fNew - fOld - slopeInit) ) + if(xLambdaTemp > 0.5_rkind*xLambda) xLambdaTemp = 0.5_rkind*xLambda + + ! subsequent backtracks: use cubic + else + + ! check that we did not back-track all the way back to the original value + if(iLine==maxLineSearch)then + message=trim(message)//'backtracked all the way back to the original value' + err=-20; return + end if + + ! define rhs + rhs1 = fNew - fOld - xLambda*slopeInit + rhs2 = fPrev - fOld - xLambdaPrev*slopeInit + + ! define coefficients + aCoef = (rhs1/(xLambda*xLambda) - rhs2/(xLambdaPrev*xLambdaPrev))/(xLambda - xLambdaPrev) + bCoef = (-xLambdaPrev*rhs1/(xLambda*xLambda) + xLambda*rhs2/(xLambdaPrev*xLambdaPrev)) / (xLambda - xLambdaPrev) + + ! check if a quadratic + if(aCoef==0._rkind)then + xLambdaTemp = -slopeInit/(2._rkind*bCoef) + + ! calculate cubic + else + disc = bCoef*bCoef - 3._rkind*aCoef*slopeInit + if(disc < 0._rkind)then + xLambdaTemp = 0.5_rkind*xLambda + else + xLambdaTemp = (-bCoef + sqrt(disc))/(3._rkind*aCoef) + end if + end if ! calculating cubic + + ! constrain to <= 0.5*xLambda + if(xLambdaTemp > 0.5_rkind*xLambda) xLambdaTemp=0.5_rkind*xLambda + + end if ! subsequent backtracks + + ! save results + xLambdaPrev = xLambda + fPrev = fNew + + ! constrain lambda + xLambda = max(xLambdaTemp, 0.1_rkind*xLambda) + + end do lineSearch ! backtrack loop + end associate + + end subroutine lineSearchRefinement + + + ! ********************************************************************************************************* + ! * internal subroutine trustRegionRefinement: refine the iteration increment using trust regions + ! ********************************************************************************************************* + subroutine trustRegionRefinement(in_TRR,stateVecTrial,newtStepScaled,aJacScaled,rVecScaled,stateVecNew,fluxVecNew,resVecNew,out_TRR) + ! provide access to the matrix routines + USE matrixOper_module, only: lapackSolv + USE matrixOper_module, only: computeGradient + implicit none + ! input + type(in_type_lineSearchRefinement),intent(in) :: in_TRR ! object for scalar intent(in) arguments -- reusing line search class + real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector + real(rkind),intent(in) :: newtStepScaled(:) ! scaled newton step + real(rkind),intent(in) :: aJacScaled(:,:) ! scaled jacobian matrix + real(rkind),intent(in) :: rVecScaled(:) ! scaled residual vector + ! output + real(rkind),intent(out) :: stateVecNew(:) ! new state vector + real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector + real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector + type(out_type_lineSearchRefinement) :: out_TRR ! object for scalar intent(in) arguments -- reusing line search class + ! -------------------------------------------------------------------------------------------------------- + ! local variables + + ! .. needed .. + + ! -------------------------------------------------------------------------------------------------------- + associate(& + ! input + doTrustRefinement => in_TRR % doSearch ,& ! flag to refine using trust regions + fOld => in_TRR % fOld ,& ! old function value + nState => in_SS4NR % nState ,& ! total number of state variables + ! output + fNew => out_TRR % fNew ,& ! new function evaluation + converged => out_TRR % converged ,& ! convergence flag + err => out_TRR % err ,& ! error code + message => out_TRR % message & ! error message + &) + + err=0; message='trustRegionRefinement/' + converged =.false. + + ! check the need to refine the step + if (doTrustRefinement) then + + ! (check vectors) + if (size(stateVecTrial)/=nState .or. size(newtStepScaled)/=nState .or. size(rVecScaled)/=nState)then + message=trim(message)//'unexpected size of input vectors' + err=20; return + end if + + ! (check matrix) + if (size(aJacScaled,1)/=nState .or. size(aJacScaled,2)/=nState) then + message=trim(message)//'unexpected size of Jacobian matrix' + err=20; return + end if + + ! dummy check for the function + if (fold==realMissing) print*, 'missing' + + ! dummy + stateVecNew = realMissing + fluxVecNew = realMissing + resVecNew = quadMissing + fNew = realMissing + converged = .true. + + + end if ! if doing the trust region refinement + + message=trim(message)//'routine not implemented yet' + err=20; return + + end associate + + end subroutine trustRegionRefinement + + + ! ********************************************************************************************************* + ! * internal subroutine safeRootfinder: refine the 1-d iteration increment using brackets + ! ********************************************************************************************************* + subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fluxVecNew,resVecNew,out_SRF) + USE,intrinsic :: ieee_arithmetic,only:ieee_is_nan ! IEEE arithmetic (check NaN) + USE globalData,only:dNaN ! double precision NaN + implicit none + ! input + real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector + real(rkind),intent(in) :: rVecScaled(:) ! scaled residual vector + real(rkind),intent(in) :: newtStepScaled(:) ! scaled newton step + ! output + real(rkind),intent(out) :: stateVecNew(:) ! new state vector + real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector + real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector + type(out_type_lineSearchRefinement),intent(out) :: out_SRF ! object for scalar intent(out) arguments (reusing lineSearchRefinement class) + ! -------------------------------------------------------------------------------------------------------- + ! local variables + character(len=256) :: cmessage ! error message of downwind routine + real(rkind),parameter :: relTolerance=0.005_rkind ! force bi-section if trial is slightly larger than (smaller than) xmin (xmax) + real(rkind) :: xTolerance ! relTolerance*(xmax-xmin) + real(rkind) :: xInc(in_SS4NR % nState) ! iteration increment (re-scaled to original units of the state vector) + real(rkind) :: rVec(in_SS4NR % nState) ! residual vector (re-scaled to original units of the state equation) + logical(lgt) :: feasible ! feasibility of the solution + logical(lgt) :: doBisection ! flag to do the bi-section + logical(lgt) :: bracketsDefined ! flag to define if the brackets are defined + integer(i4b),parameter :: nCheck=100 ! number of times to check the model state variables + real(rkind),parameter :: delX=1._rkind ! trial increment + ! -------------------------------------------------------------------------------------------------------- + associate(& + iter => in_SS4NR % iter ,& ! intent(in): iteration index + nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers + nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers + nState => in_SS4NR % nState ,& ! intent(in): total number of state + xMin => io_SS4NR % xMin ,& ! intent(inout): bracket of the root + xMax => io_SS4NR % xMax ,& ! intent(inout): bracket of the root + fNew => out_SRF % fNew ,& ! intent(out): new function evaluation + converged => out_SRF % converged ,& ! intent(out): convergence flag + err => out_SRF % err ,& ! intent(out): error code + message => out_SRF % message & ! intent(out): error message + &) + + err=0; message='safeRootfinder/' + converged = .false. + + ! check scalar + if (size(stateVecTrial)/=1 .or. size(rVecScaled)/=1 .or. size(newtStepScaled)/=1) then + message=trim(message)//'unexpected size of input vectors' + err=20; return + end if + + ! initialize brackets to rkind precision NaN + if (iter==1) then + xMax = dNaN + xMin = dNaN + end if + + ! get the residual vector + rVec = real(rVecScaled, rkind)*real(fScale, rkind) + + ! update brackets + if (rVec(1)<0._rkind) then + xMin = stateVecTrial(1) + else + xMax = stateVecTrial(1) + end if + + ! get the iteration increment + xInc = newtStepScaled*xScale + + ! ***** + ! * case 1: the iteration increment is the same sign as the residual vector + if (xInc(1)*rVec(1) > 0._rkind) then + + ! get brackets if they do not exist + if ( ieee_is_nan(xMin) .or. ieee_is_nan(xMax) ) then + call getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors + end if + + ! use bi-section + stateVecNew(1) = 0.5_rkind*(xMin + xMax) + + ! ***** + ! * case 2: the iteration increment is the correct sign + else + + ! state vector with proposed iteration increment + stateVecNew = stateVecTrial + xInc + + ! impose solution constraints adjusting state vector and iteration increment + call imposeConstraints(model_decisions,indx_data,prog_data,mpar_data,stateVecNew,stateVecTrial,nState,nSoil,nSnow,cmessage,err) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors + xInc = stateVecNew - stateVecTrial + + end if ! if the iteration increment is the same sign as the residual vector + + ! bi-section + bracketsDefined = ( .not.ieee_is_nan(xMin) .and. .not.ieee_is_nan(xMax) ) ! check that the brackets are defined + if (bracketsDefined) then + xTolerance = relTolerance*(xMax-xMin) + doBisection = (stateVecNew(1)xMax-xTolerance) + if (doBisection) stateVecNew(1) = 0.5_rkind*(xMin+xMax) + end if + + ! evaluate summa + call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors + + ! check feasibility (should be feasible because of the call to imposeConstraints, except if canopyTemp>canopyTempMax (500._rkind)) + if (.not.feasible) then; err=20; message=trim(message)//'state vector not feasible'; return; end if + + ! check convergence + converged = checkConv(resVecNew,xInc,stateVecNew) + + end associate + + end subroutine safeRootfinder + + ! ********************************************************************************************************* + ! * internal subroutine getBrackets: get the brackets + ! ********************************************************************************************************* + subroutine getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,message) + USE,intrinsic :: ieee_arithmetic,only:ieee_is_nan ! IEEE arithmetic (check NaN) + implicit none + ! dummies + real(rkind),intent(in) :: stateVecTrial(:) ! trial state vector + real(rkind),intent(out) :: stateVecNew(:) ! new state vector + real(rkind),intent(out) :: xMin,xMax ! constraints + integer(i4b),intent(inout) :: err ! error code + character(*),intent(out) :: message ! error message + ! locals + real(rkind) :: stateVecPrev(in_SS4NR % nState) ! iteration state vector + integer(i4b) :: iCheck ! check the model state variables + integer(i4b),parameter :: nCheck=100 ! number of times to check the model state variables + logical(lgt) :: feasible ! feasibility of the solution + real(rkind),parameter :: delX=1._rkind ! trial increment + real(rkind) :: xIncrement(in_SS4NR % nState) ! trial increment + ! initialize + err=0; message='getBrackets/' + + ! initialize state vector + stateVecNew = stateVecTrial + stateVecPrev = stateVecNew + + ! get xIncrement + xIncrement = -sign((/delX/),rVec) + + ! try the increment a few times + do iCheck=1,nCheck + + ! state vector with proposed iteration increment + stateVecNew = stateVecPrev + xIncrement + + ! impose solution constraints adjusting state vector and iteration increment + associate(& + nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers + nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers + nState => in_SS4NR % nState & ! intent(in): total number of state variables + &) + call imposeConstraints(model_decisions,indx_data,prog_data,mpar_data,stateVecNew,stateVecPrev,nState,nSoil,nSnow,cmessage,err) + end associate + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + xIncrement = stateVecNew - stateVecPrev + + ! evaluate summa + associate(fnew => out_SS4NR % fnew) + call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) + end associate + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! check feasibility (should be feasible because of the call to imposeConstraints, except if canopyTemp>canopyTempMax (500._rkind)) + if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif + + ! update brackets + if(real(resVecNew(1), rkind)<0._rkind)then + xMin = stateVecNew(1) + else + xMax = stateVecNew(1) + endif + + ! check that the brackets are defined + if( .not.ieee_is_nan(xMin) .and. .not.ieee_is_nan(xMax) ) exit + + ! check that we found the brackets + if(iCheck==nCheck)then + message=trim(message)//'could not fix the problem where residual and iteration increment are of the same sign' + err=20; return + endif + + ! Save the state vector + stateVecPrev = stateVecNew + + end do ! multiple checks + + end subroutine getBrackets + + + ! ********************************************************************************************************* + ! * internal subroutine numJacobian: compute the numerical Jacobian matrix + ! ********************************************************************************************************* + subroutine numJacobian(stateVec,dMat,nJac,err,message) + implicit none + ! dummies + real(rkind),intent(in) :: stateVec(:) ! trial state vector + real(rkind),intent(in) :: dMat(:) ! diagonal matrix + ! output + real(rkind),intent(out) :: nJac(:,:) ! numerical Jacobian + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ---------------------------------------------------------------------------------------------------------- + ! local + character(len=256) :: cmessage ! error message of downwind routine + real(rkind),parameter :: dx=1.e-8_rkind ! finite difference increment + real(rkind),dimension(in_SS4NR % nState) :: stateVecPerturbed ! perturbed state vector + real(rkind),dimension(in_SS4NR % nState) :: fluxVecInit,fluxVecJac ! flux vector (mized units) + real(qp),dimension(in_SS4NR % nState) :: resVecInit,resVecJac ! qp ! residual vector (mixed units) + real(rkind) :: func ! function value + logical(lgt) :: feasible ! flag to denote the feasibility of the solution + integer(i4b) :: iJac ! index of row of the Jacobian matrix + integer(i4b),parameter :: ixNumFlux=1001 ! named variable for the flux-based form of the numerical Jacobian + integer(i4b),parameter :: ixNumRes=1002 ! named variable for the residual-based form of the numerical Jacobian + integer(i4b) :: ixNumType=ixNumRes ! method used to calculate the numerical Jacobian + ! ---------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='numJacobian/' + + ! compute initial function evaluation + call eval8summa_wrapper(stateVec,fluxVecInit,resVecInit,func,feasible,err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + if(.not.feasible)then; message=trim(message)//'initial state vector not feasible'; err=20; return; endif + + ! get a copy of the state vector to perturb + stateVecPerturbed(:) = stateVec(:) + + ! loop through state variables + do iJac=1,in_SS4NR % nState + + !print*, 'iJac = ', iJac + !globalPrintFlag = merge(.true.,.false., iJac==1) + + ! perturb state vector + stateVecPerturbed(iJac) = stateVec(iJac) + dx + + ! compute function evaluation + call eval8summa_wrapper(stateVecPerturbed,fluxVecJac,resVecJac,func,feasible,err,cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + if(.not.feasible)then; message=trim(message)//'state vector not feasible'; err=20; return; endif + !write(*,'(a,1x,2(f30.20,1x))') 'resVecJac(101:102) = ', resVecJac(101:102) + + ! compute the row of the Jacobian matrix + select case(ixNumType) + case(ixNumRes); nJac(:,iJac) = real(resVecJac - resVecInit, kind(rkind) )/dx ! Jacobian based on residuals + case(ixNumFlux); nJac(:,iJac) = -in_SS4NR % dt_cur*(fluxVecJac(:) - fluxVecInit(:))/dx ! Jacobian based on fluxes + case default; err=20; message=trim(message)//'Jacobian option not found'; return + end select + + ! if flux option then add in the diagonal matrix + if(ixNumType==ixNumFlux) nJac(iJac,iJac) = nJac(iJac,iJac) + dMat(iJac) + + ! set the state back to the input value + stateVecPerturbed(iJac) = stateVec(iJac) + + end do ! (looping through state variables) + + ! print the Jacobian + print*, '** numerical Jacobian:', ixNumType==ixNumRes + write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=min(iJac1,in_SS4NR % nState),min(iJac2,in_SS4NR % nState)) + do iLayer=min(iJac1,in_SS4NR % nState),min(iJac2,in_SS4NR % nState) + write(*,'(i4,1x,100(e12.5,1x))') iLayer, nJac(min(iJac1,in_SS4NR % nState):min(iJac2,in_SS4NR % nState),iLayer) + end do + !print*, 'PAUSE: testing Jacobian'; read(*,*) + + end subroutine numJacobian + + ! ********************************************************************************************************* + ! * internal subroutine testBandMat: compute the full Jacobian matrix and decompose into a band matrix + ! ********************************************************************************************************* + + subroutine testBandMat(check,err,message) + ! dummy variables + logical(lgt),intent(in) :: check ! flag to pause + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local variables + real(rkind) :: fullJac(in_SS4NR % nState,in_SS4NR % nState) ! full Jacobian matrix + real(rkind) :: bandJac(in_SS4NR % nLeadDim,in_SS4NR % nState) ! band Jacobian matrix + integer(i4b) :: iState,jState ! indices of the state vector + character(LEN=256) :: cmessage ! error message of downwind routine + ! class objects for subroutine arguments + type(in_type_computJacob) :: in_computJacob + type(out_type_computJacob) :: out_computJacob + ! initialize error control + err=0; message='testBandMat/' + + ! check + if (in_SS4NR % nLeadDim == in_SS4NR % nState) then + message=trim(message)//'do not expect nLeadDim==nState: check that are computing the band diagonal matrix'//& + ' (is forceFullMatrix==.true.?)' + err=20; return + end if + + ! compute the full Jacobian matrix + call initialize_computJacob_testBandMat + call computJacob(in_computJacob,indx_data,prog_data,diag_data,deriv_data,dBaseflow_dMatric,dMat,fullJac,out_computJacob) + call finalize_computJacob_testBandMat(err,cmessage) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! initialize band matrix + bandJac(:,:) = 0._rkind + + ! transfer into the lapack band diagonal structure + do iState=1,in_SS4NR % nState + do jState=max(1,iState-ku),min(in_SS4NR % nState,iState+kl) + bandJac(kl + ku + 1 + jState - iState, iState) = fullJac(jState,iState) + end do + end do + + ! print results + print*, '** test banded analytical Jacobian:' + write(*,'(a4,1x,100(i17,1x))') 'xCol', (iState, iState=iJac1,iJac2) + do iState=kl+1,in_SS4NR % nLeadDim; write(*,'(i4,1x,100(e17.10,1x))') iState, bandJac(iState,iJac1:iJac2); end do + + ! check if the need to pause + if(check)then + print*, 'PAUSE: testing banded analytical Jacobian' + read(*,*) + endif + + end subroutine testBandMat + + subroutine initialize_computJacob_testBandMat + ! *** Transfer data from out_computJacob class object to local variables in testBandMat *** + associate(& + dt_cur => in_SS4NR % dt_cur ,& ! intent(in): current stepsize + nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers + nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers + nLayers => in_SS4NR % nLayers ,& ! intent(in): total number of layers + computeVegFlux => in_SS4NR % computeVegFlux & ! intent(in): flag to indicate if computing fluxes over vegetation + &) + call in_computJacob % initialize(dt_cur,nSnow,nSoil,nLayers,computeVegFlux,.false.,ixFullMatrix) + end associate + end subroutine initialize_computJacob_testBandMat + + subroutine finalize_computJacob_testBandMat(err,cmessage) + ! *** Transfer data from out_computJacob class object to local variables in testBandMat *** + ! Note: subroutine arguments are needed because testBandMat is an internal procedure + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: cmessage ! error message of downwind routine + call out_computJacob % finalize(err,cmessage) + end subroutine finalize_computJacob_testBandMat + + subroutine initialize_computJacob_summaSolve4numrec + ! *** Transfer data to in_computJacob class object from local variables in summaSolve4numrec *** + associate(& + ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision,& ! intent(in): [i4b] groundwater parameterization + dt_cur => in_SS4NR % dt_cur ,& ! intent(in): current stepsize + nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers + nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers + nLayers => in_SS4NR % nLayers ,& ! intent(in): total number of layers + ixMatrix => in_SS4NR % ixMatrix ,& ! intent(in): type of matrix (full or band diagonal) + computeVegFlux => in_SS4NR % computeVegFlux & ! intent(in): flag to indicate if computing fluxes over vegetation + &) + call in_computJacob % initialize(dt_cur,nSnow,nSoil,nLayers,computeVegFlux,(ixGroundwater==qbaseTopmodel),ixMatrix) + end associate + end subroutine initialize_computJacob_summaSolve4numrec + + subroutine finalize_computJacob_summaSolve4numrec + ! *** Transfer data from out_computJacob class object to local variables in summaSolve4numrec *** + associate(err => out_SS4NR % err) + call out_computJacob % finalize(err,cmessage) + end associate + end subroutine finalize_computJacob_summaSolve4numrec + + ! ********************************************************************************************************* + ! * internal subroutine eval8summa_wrapper: compute the right-hand-side vector + ! ********************************************************************************************************* + ! NOTE: This is simply a wrapper routine for eval8summa, to reduce the number of calling arguments + ! An internal subroutine, so have access to all data in the main subroutine + subroutine eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,message) + USE eval8summa_module,only:eval8summa ! simulation of fluxes and residuals given a trial state vector + implicit none + ! input + real(rkind),intent(in) :: stateVecNew(:) ! updated state vector + ! output + real(rkind),intent(out) :: fluxVecNew(:) ! updated flux vector + real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! updated residual vector + real(rkind),intent(out) :: fNew ! new function value + logical(lgt),intent(out) :: feasible ! flag to denote the feasibility of the solution + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ---------------------------------------------------------------------------------------------------------- + ! local + character(len=256) :: cmessage ! error message of downwind routine + ! ---------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='eval8summa_wrapper/' + + associate(& + dt_cur => in_SS4NR % dt_cur ,& ! intent(in): current stepsize + dt => in_SS4NR % dt ,& ! intent(in): entire time step for drainage pond rate + nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers + nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers + nLayers => in_SS4NR % nLayers ,& ! intent(in): total number of layers + nState => in_SS4NR % nState ,& ! intent(in): total number of state variables + firstSubStep => in_SS4NR % firstSubStep ,& ! intent(in): flag to indicate if we are processing the first sub-step + computeVegFlux => in_SS4NR % computeVegFlux ,& ! intent(in): flag to indicate if computing fluxes over vegetation + scalarSolution => in_SS4NR % scalarSolution ,& ! intent(in): flag to denote if implementing the scalar solution + firstFluxCall => io_SS4NR % firstFluxCall ,& ! intent(inout): flag to indicate if we are processing the first flux call + ixSaturation => io_SS4NR % ixSaturation & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + &) + ! compute the flux and the residual vector for a given state vector + call eval8summa(& + ! input: model control + dt_cur, & ! intent(in): current stepsize + dt, & ! intent(in): length of the time step (seconds) + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + nState, & ! intent(in): total number of state variables + .false., & ! intent(in): not inside Sundials solver + firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + .false., & ! intent(in): not processing the first iteration in a splitting operation + computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: state vectors + stateVecNew, & ! intent(in): updated model state vector + fScale, & ! intent(in): characteristic scale of the function evaluations + sMul, & ! intent(inout): state vector multiplier (used in the residual calculations) + ! input: data structures + model_decisions, & ! intent(in): model decisions + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + forc_data, & ! intent(in): model forcing data + bvar_data, & ! intent(in): average model variables for the entire basin + prog_data, & ! intent(in): model prognostic variables for a local HRU + ! input-output: data structures + indx_data, & ! intent(inout): index data + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! input-output: baseflow + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + ! output + feasible, & ! intent(out): flag to denote the feasibility of the solution + fluxVecNew, & ! intent(out): new flux vector + resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equation + resVecNew, & ! intent(out): new residual vector + fNew, & ! intent(out): new function evaluation + err,cmessage) ! intent(out): error control + end associate + + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + end subroutine eval8summa_wrapper + + + ! ********************************************************************************************************* + ! internal function checkConv: check convergence based on the residual vector + ! ********************************************************************************************************* + function checkConv(rVec,xInc,xVec) + implicit none + ! dummies + real(rkind),intent(in) :: rVec(:) ! residual vector (mixed units) + real(rkind),intent(in) :: xInc(:) ! iteration increment (mixed units) + real(rkind),intent(in) :: xVec(:) ! state vector (mixed units) + logical(lgt) :: checkConv ! flag to denote convergence + ! locals + real(rkind),dimension(mSoil) :: psiScale ! scaling factor for matric head + real(rkind),parameter :: xSmall=1.e-0_rkind ! a small offset + real(rkind),parameter :: scalarTighten=0.1_rkind ! scaling factor for the scalar solution + real(rkind) :: soilWatbalErr ! error in the soil water balance + real(rkind) :: canopy_max ! absolute value of the residual in canopy water (kg m-2) + real(rkind),dimension(1) :: energy_max ! maximum absolute value of the energy residual (J m-3) + real(rkind),dimension(1) :: liquid_max ! maximum absolute value of the volumetric liquid water content residual (-) + real(rkind),dimension(1) :: matric_max ! maximum absolute value of the matric head iteration increment (m) + real(rkind) :: aquifer_max ! absolute value of the residual in aquifer water (m) + logical(lgt) :: canopyConv ! flag for canopy water balance convergence + logical(lgt) :: watbalConv ! flag for soil water balance convergence + logical(lgt) :: liquidConv ! flag for residual convergence + logical(lgt) :: matricConv ! flag for matric head convergence + logical(lgt) :: energyConv ! flag for energy convergence + logical(lgt) :: aquiferConv ! flag for aquifer water balance convergence + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + ! association to variables in the data structures + associate(& + ! model control + iter => in_SS4NR % iter ,& ! intent(in): iteration index + nsnow => in_SS4NR % nsnow ,& ! intent(in): number of snow layers + scalarSolution => in_SS4NR % scalarSolution ,& ! intent(in): flag to denote if implementing the scalar solution + ! convergence parameters + absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1),& ! intent(in): [dp] absolute convergence tolerance for vol frac liq water (-) + absConvTol_matric => mpar_data%var(iLookPARAM%absConvTol_matric)%dat(1),& ! intent(in): [dp] absolute convergence tolerance for matric head (m) + absConvTol_energy => mpar_data%var(iLookPARAM%absConvTol_energy)%dat(1),& ! intent(in): [dp] absolute convergence tolerance for energy (J m-3) + ! layer depth + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! model indices + ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of aquifer storage state variable + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for all energy states + ixHydOnly => indx_data%var(iLookINDEX%ixHydOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for all hydrology states + ixMatOnly => indx_data%var(iLookINDEX%ixMatOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for matric head state variables in the state vector + ixMatricHead => indx_data%var(iLookINDEX%ixMatricHead)%dat ,& ! intent(in): [i4b(:)] list of indices for matric head in the soil vector + fnew => out_SS4NR % fnew & ! intent(in): [dp] new function evaluations + ) ! making associations with variables in the data structures + ! ------------------------------------------------------------------------------------------------------------------------------------------------- + + ! check convergence based on the canopy water balance + if(ixVegHyd/=integerMissing)then + canopy_max = real(abs(rVec(ixVegHyd)), rkind)*iden_water + canopyConv = (canopy_max < absConvTol_liquid) ! absolute error in canopy water balance (mm) + else + canopy_max = realMissing + canopyConv = .true. + endif + + ! check convergence based on the residuals for energy (J m-3) + if(size(ixNrgOnly)>0)then + energy_max = real(maxval(abs( rVec(ixNrgOnly) )), rkind) + energyConv = (energy_max(1) < absConvTol_energy) ! (based on the residual) + else + energy_max = realMissing + energyConv = .true. + endif + + ! check convergence based on the residuals for volumetric liquid water content (-) + if(size(ixHydOnly)>0)then + liquid_max = real(maxval(abs( rVec(ixHydOnly) ) ), rkind) + ! (tighter convergence for the scalar solution) + if(scalarSolution)then + liquidConv = (liquid_max(1) < absConvTol_liquid*scalarTighten) ! (based on the residual) + else + liquidConv = (liquid_max(1) < absConvTol_liquid) ! (based on the residual) + endif + else + liquid_max = realMissing + liquidConv = .true. + endif + + ! check convergence based on the iteration increment for matric head + ! NOTE: scale by matric head to avoid unnecessairly tight convergence when there is no water + if(size(ixMatOnly)>0)then + psiScale = abs( xVec(ixMatOnly) ) + xSmall ! avoid divide by zero + matric_max = maxval(abs( xInc(ixMatOnly)/psiScale ) ) + matricConv = (matric_max(1) < absConvTol_matric) ! NOTE: based on iteration increment + else + matric_max = realMissing + matricConv = .true. + endif + + ! check convergence based on the soil water balance error (m) + if(size(ixMatOnly)>0)then + soilWatBalErr = sum( real(rVec(ixMatOnly), rkind)*mLayerDepth(nSnow+ixMatricHead) ) + watbalConv = (abs(soilWatbalErr) < absConvTol_liquid) ! absolute error in total soil water balance (m) + else + soilWatbalErr = realMissing + watbalConv = .true. + endif + + ! check convergence based on the aquifer storage + if(ixAqWat/=integerMissing)then + aquifer_max = real(abs(rVec(ixAqWat)), rkind)*iden_water + aquiferConv = (aquifer_max < absConvTol_liquid) ! absolute error in aquifer water balance (mm) + else + aquifer_max = realMissing + aquiferConv = .true. + endif + + ! final convergence check + checkConv = (canopyConv .and. watbalConv .and. matricConv .and. liquidConv .and. energyConv .and. aquiferConv) + + ! print progress towards solution + if(globalPrintFlag)then + write(*,'(a,1x,i4,1x,7(e15.5,1x),7(L1,1x))') 'check convergence: ', iter, & + fNew, matric_max(1), liquid_max(1), energy_max(1), canopy_max, aquifer_max, soilWatBalErr, matricConv, liquidConv, energyConv, watbalConv, canopyConv, aquiferConv, watbalConv + endif + + ! end associations with variables in the data structures + end associate + + end function checkConv + + end subroutine summaSolve4numrec + + +end module summaSolve4numrec_module From b0cb684eb1adbc7f56658404a91429e188b3516e Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 15 May 2024 03:56:49 -0600 Subject: [PATCH 1306/1472] Updates to systemSolv and summaSolv4homegrown to reflect new homegrown label for Newton solver based on numerical recipes. --- build/source/engine/summaSolve4homegrown.f90 | 206 +++++++++---------- build/source/engine/systemSolv.f90 | 64 +++--- 2 files changed, 135 insertions(+), 135 deletions(-) diff --git a/build/source/engine/summaSolve4homegrown.f90 b/build/source/engine/summaSolve4homegrown.f90 index c62c2e869..195c78565 100644 --- a/build/source/engine/summaSolve4homegrown.f90 +++ b/build/source/engine/summaSolve4homegrown.f90 @@ -18,7 +18,7 @@ ! You should have received a copy of the GNU General Public License ! along with this program. If not, see . -module summaSolve4numrec_module +module summaSolve4homegrown_module ! data types USE nrtype @@ -76,9 +76,9 @@ module summaSolve4numrec_module out_type_computJacob, & ! class for computJacob arguments in_type_lineSearchRefinement, & ! class for lineSearchRefinement arguments out_type_lineSearchRefinement,& ! class for lineSearchRefinement arguments - in_type_summaSolve4numrec, & ! class for summaSolve4numrec arguments - io_type_summaSolve4numrec, & ! class for summaSolve4numrec arguments - out_type_summaSolve4numrec ! class for summaSolve4numrec arguments + in_type_summaSolve4numrec, & ! class for summaSolve4homegrown arguments + io_type_summaSolve4numrec, & ! class for summaSolve4homegrown arguments + out_type_summaSolve4numrec ! class for summaSolve4homegrown arguments ! look-up values for the choice of groundwater parameterization @@ -94,14 +94,14 @@ module summaSolve4numrec_module implicit none private -public::summaSolve4numrec +public::summaSolve4homegrown contains - ! *********************************************************************************************************************** - ! public subroutine summaSolve4numrec: calculate the iteration increment, evaluate the new state, and refine if necessary - ! *********************************************************************************************************************** - subroutine summaSolve4numrec(& - in_SS4NR, & ! intent(in): model control and previous function value + ! ************************************************************************************************************************** + ! public subroutine summaSolve4homegrown: calculate the iteration increment, evaluate the new state, and refine if necessary + ! ************************************************************************************************************************** + subroutine summaSolve4homegrown(& + in_SS4HG, & ! intent(in): model control and previous function value ! input: model control !firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call ! input: state vectors @@ -129,21 +129,21 @@ subroutine summaSolve4numrec(& ! input-output: baseflow !ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) dBaseflow_dMatric, & ! intent(inout): derivative in baseflow w.r.t. matric head (s-1) - io_SS4NR, & ! intent(inout): first flux call flag, root brackets, index of lowest saturated layer + io_SS4HG, & ! intent(inout): first flux call flag, root brackets, index of lowest saturated layer ! output stateVecNew, & ! intent(out): new state vector fluxVecNew, & ! intent(out): new flux vector resSinkNew, & ! intent(out): additional (sink) terms on the RHS of the state equation resVecNew, & ! intent(out): new residual vector - out_SS4NR) ! intent(out): new function evaluation, convergence flag, and error control + out_SS4HG) ! intent(out): new function evaluation, convergence flag, and error control USE computJacob_module, only: computJacob USE eval8summa_module, only: imposeConstraints USE matrixOper_module, only: lapackSolv USE matrixOper_module, only: scaleMatrices implicit none ! -------------------------------------------------------------------------------------------------------------------------------- - type(in_type_summaSolve4numrec),intent(in) :: in_SS4NR ! model control variables and previous function evaluation - type(io_type_summaSolve4numrec) :: io_SS4NR ! first flux call flag and baseflow variables + type(in_type_summaSolve4numrec),intent(in) :: in_SS4HG ! model control variables and previous function evaluation + type(io_type_summaSolve4numrec),intent(inout) :: io_SS4HG ! first flux call flag and baseflow variables ! input: model control ! logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call ! input: state vectors @@ -176,20 +176,20 @@ subroutine summaSolve4numrec(& real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector real(rkind),intent(out) :: resSinkNew(:) ! sink terms on the RHS of the flux equation real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector - type(out_type_summaSolve4numrec),intent(out) :: out_SS4NR ! new function evaluation, convergence flag, and error control + type(out_type_summaSolve4numrec),intent(out) :: out_SS4HG ! new function evaluation, convergence flag, and error control ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- ! Jacobian matrix logical(lgt),parameter :: doNumJacobian=.false. ! flag to compute the numerical Jacobian matrix logical(lgt),parameter :: testBandDiagonal=.false. ! flag to test the band diagonal Jacobian matrix - real(rkind) :: nJac(in_SS4NR % nState,in_SS4NR % nState) ! numerical Jacobian matrix - real(rkind) :: aJac(in_SS4NR % nLeadDim,in_SS4NR % nState) ! Jacobian matrix - real(rkind) :: aJacScaled(in_SS4NR % nLeadDim,in_SS4NR % nState) ! Jacobian matrix (scaled) - real(rkind) :: aJacScaledTemp(in_SS4NR % nLeadDim,in_SS4NR % nState) ! Jacobian matrix (scaled) -- temporary copy since decomposed in lapack + real(rkind) :: nJac(in_SS4HG % nState,in_SS4HG % nState) ! numerical Jacobian matrix + real(rkind) :: aJac(in_SS4HG % nLeadDim,in_SS4HG % nState) ! Jacobian matrix + real(rkind) :: aJacScaled(in_SS4HG % nLeadDim,in_SS4HG % nState) ! Jacobian matrix (scaled) + real(rkind) :: aJacScaledTemp(in_SS4HG % nLeadDim,in_SS4HG % nState) ! Jacobian matrix (scaled) -- temporary copy since decomposed in lapack ! solution/step vectors - real(rkind),dimension(in_SS4NR % nState) :: rVecScaled ! residual vector (scaled) - real(rkind),dimension(in_SS4NR % nState) :: newtStepScaled ! full newton step (scaled) + real(rkind),dimension(in_SS4HG % nState) :: rVecScaled ! residual vector (scaled) + real(rkind),dimension(in_SS4HG % nState) :: newtStepScaled ! full newton step (scaled) ! step size refinement logical(lgt) :: doRefine ! flag for step refinement integer(i4b),parameter :: ixLineSearch=1001 ! step refinement = line search @@ -223,21 +223,21 @@ subroutine summaSolve4numrec(& call refine_Newton_step; if (return_flag) return ! refine Newton step if needed -- return if error - associate(err => out_SS4NR % err,message => out_SS4NR % message) + associate(err => out_SS4HG % err,message => out_SS4HG % message) if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors end associate contains subroutine initialize_summaSolve4numrec - ! *** Initial steps for the summaSolve4numrec algorithm (computing the Newton step) *** + ! *** Initial steps for the summaSolve4homegrown algorithm (computing the Newton step) *** associate(& - err => out_SS4NR % err ,& - message => out_SS4NR % message & + err => out_SS4HG % err ,& + message => out_SS4HG % message & &) ! initialize error control - err=0; message='summaSolve4numrec/' + err=0; message='summaSolve4homegrown/' return_flag=.false. ! initialize return flag ! choose Jacobian type @@ -268,8 +268,8 @@ subroutine update_Jacobian ! This occurred either at the call to eval8summa at the start of systemSolv ! or in the call to eval8summa in the previous iteration (within lineSearchRefinement or trustRegionRefinement) associate(& - err => out_SS4NR % err ,& - message => out_SS4NR % message & + err => out_SS4HG % err ,& + message => out_SS4HG % message & &) call initialize_computJacob_summaSolve4numrec call computJacob(in_computJacob,indx_data,prog_data,diag_data,deriv_data,dBaseflow_dMatric,dMat,aJac,out_computJacob) @@ -290,10 +290,10 @@ subroutine solve_linear_system ! *** Solve the linear system for the Newton step using LAPACK routines *** associate(& - nState => in_SS4NR % nState ,& ! intent(in): total number of state variables - ixMatrix => in_SS4NR % ixMatrix ,& ! intent(in): type of matrix (full or band diagonal) - err => out_SS4NR % err ,& ! intent(out): error code - message => out_SS4NR % message & ! intent(out): error message + nState => in_SS4HG % nState ,& ! intent(in): total number of state variables + ixMatrix => in_SS4HG % ixMatrix ,& ! intent(in): type of matrix (full or band diagonal) + err => out_SS4HG % err ,& ! intent(out): error code + message => out_SS4HG % message & ! intent(out): error message &) ! test the band diagonal matrix if (testBandDiagonal) then @@ -341,11 +341,11 @@ subroutine refine_Newton_step ! NOTE: in 99.9% of cases newtStep will be used (no refinement) associate(& - fOld => in_SS4NR % fOld ,& - fnew => out_SS4NR % fnew ,& - converged => out_SS4NR % converged,& - err => out_SS4NR % err ,& - message => out_SS4NR % message & + fOld => in_SS4HG % fOld ,& + fnew => out_SS4HG % fnew ,& + converged => out_SS4HG % converged,& + err => out_SS4HG % err ,& + message => out_SS4HG % message & &) if (size(stateVecTrial)>1) then @@ -401,8 +401,8 @@ subroutine lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,r ! -------------------------------------------------------------------------------------------------------- ! local character(len=256) :: cmessage ! error message of downwind routine - real(rkind) :: gradScaled(in_SS4NR % nState) ! scaled gradient - real(rkind) :: xInc(in_SS4NR % nState) ! iteration increment (re-scaled to original units of the state vector) + real(rkind) :: gradScaled(in_SS4HG % nState) ! scaled gradient + real(rkind) :: xInc(in_SS4HG % nState) ! iteration increment (re-scaled to original units of the state vector) logical(lgt) :: feasible ! flag to denote the feasibility of the solution integer(i4b) :: iLine ! line search index integer(i4b),parameter :: maxLineSearch=5 ! maximum number of backtracks @@ -421,10 +421,10 @@ subroutine lineSearchRefinement(in_LSR,stateVecTrial,newtStepScaled,aJacScaled,r doLineSearch => in_LSR % doSearch ,& ! flag to do the line search fOld => in_LSR % fOld ,& ! old function value ! local variables - nSnow => in_SS4NR % nSnow ,& ! number of snow layers - nSoil => in_SS4NR % nSoil ,& ! number of soil layers - nState => in_SS4NR % nState ,& ! total number of state variables - ixMatrix => in_SS4NR % ixMatrix ,& ! type of matrix (full or band diagonal) + nSnow => in_SS4HG % nSnow ,& ! number of snow layers + nSoil => in_SS4HG % nSoil ,& ! number of soil layers + nState => in_SS4HG % nState ,& ! total number of state variables + ixMatrix => in_SS4HG % ixMatrix ,& ! type of matrix (full or band diagonal) ! intent(out) variables fNew => out_LSR % fNew ,& ! new function evaluation converged => out_LSR % converged ,& ! convergence flag @@ -588,7 +588,7 @@ subroutine trustRegionRefinement(in_TRR,stateVecTrial,newtStepScaled,aJacScaled, ! input doTrustRefinement => in_TRR % doSearch ,& ! flag to refine using trust regions fOld => in_TRR % fOld ,& ! old function value - nState => in_SS4NR % nState ,& ! total number of state variables + nState => in_SS4HG % nState ,& ! total number of state variables ! output fNew => out_TRR % fNew ,& ! new function evaluation converged => out_TRR % converged ,& ! convergence flag @@ -656,8 +656,8 @@ subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fl character(len=256) :: cmessage ! error message of downwind routine real(rkind),parameter :: relTolerance=0.005_rkind ! force bi-section if trial is slightly larger than (smaller than) xmin (xmax) real(rkind) :: xTolerance ! relTolerance*(xmax-xmin) - real(rkind) :: xInc(in_SS4NR % nState) ! iteration increment (re-scaled to original units of the state vector) - real(rkind) :: rVec(in_SS4NR % nState) ! residual vector (re-scaled to original units of the state equation) + real(rkind) :: xInc(in_SS4HG % nState) ! iteration increment (re-scaled to original units of the state vector) + real(rkind) :: rVec(in_SS4HG % nState) ! residual vector (re-scaled to original units of the state equation) logical(lgt) :: feasible ! feasibility of the solution logical(lgt) :: doBisection ! flag to do the bi-section logical(lgt) :: bracketsDefined ! flag to define if the brackets are defined @@ -665,12 +665,12 @@ subroutine safeRootfinder(stateVecTrial,rVecscaled,newtStepScaled,stateVecNew,fl real(rkind),parameter :: delX=1._rkind ! trial increment ! -------------------------------------------------------------------------------------------------------- associate(& - iter => in_SS4NR % iter ,& ! intent(in): iteration index - nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers - nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers - nState => in_SS4NR % nState ,& ! intent(in): total number of state - xMin => io_SS4NR % xMin ,& ! intent(inout): bracket of the root - xMax => io_SS4NR % xMax ,& ! intent(inout): bracket of the root + iter => in_SS4HG % iter ,& ! intent(in): iteration index + nSnow => in_SS4HG % nSnow ,& ! intent(in): number of snow layers + nSoil => in_SS4HG % nSoil ,& ! intent(in): number of soil layers + nState => in_SS4HG % nState ,& ! intent(in): total number of state + xMin => io_SS4HG % xMin ,& ! intent(inout): bracket of the root + xMax => io_SS4HG % xMax ,& ! intent(inout): bracket of the root fNew => out_SRF % fNew ,& ! intent(out): new function evaluation converged => out_SRF % converged ,& ! intent(out): convergence flag err => out_SRF % err ,& ! intent(out): error code @@ -767,12 +767,12 @@ subroutine getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,message) integer(i4b),intent(inout) :: err ! error code character(*),intent(out) :: message ! error message ! locals - real(rkind) :: stateVecPrev(in_SS4NR % nState) ! iteration state vector + real(rkind) :: stateVecPrev(in_SS4HG % nState) ! iteration state vector integer(i4b) :: iCheck ! check the model state variables integer(i4b),parameter :: nCheck=100 ! number of times to check the model state variables logical(lgt) :: feasible ! feasibility of the solution real(rkind),parameter :: delX=1._rkind ! trial increment - real(rkind) :: xIncrement(in_SS4NR % nState) ! trial increment + real(rkind) :: xIncrement(in_SS4HG % nState) ! trial increment ! initialize err=0; message='getBrackets/' @@ -791,9 +791,9 @@ subroutine getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,message) ! impose solution constraints adjusting state vector and iteration increment associate(& - nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers - nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers - nState => in_SS4NR % nState & ! intent(in): total number of state variables + nSnow => in_SS4HG % nSnow ,& ! intent(in): number of snow layers + nSoil => in_SS4HG % nSoil ,& ! intent(in): number of soil layers + nState => in_SS4HG % nState & ! intent(in): total number of state variables &) call imposeConstraints(model_decisions,indx_data,prog_data,mpar_data,stateVecNew,stateVecPrev,nState,nSoil,nSnow,cmessage,err) end associate @@ -801,7 +801,7 @@ subroutine getBrackets(stateVecTrial,stateVecNew,xMin,xMax,err,message) xIncrement = stateVecNew - stateVecPrev ! evaluate summa - associate(fnew => out_SS4NR % fnew) + associate(fnew => out_SS4HG % fnew) call eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err,cmessage) end associate if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) @@ -849,9 +849,9 @@ subroutine numJacobian(stateVec,dMat,nJac,err,message) ! local character(len=256) :: cmessage ! error message of downwind routine real(rkind),parameter :: dx=1.e-8_rkind ! finite difference increment - real(rkind),dimension(in_SS4NR % nState) :: stateVecPerturbed ! perturbed state vector - real(rkind),dimension(in_SS4NR % nState) :: fluxVecInit,fluxVecJac ! flux vector (mized units) - real(qp),dimension(in_SS4NR % nState) :: resVecInit,resVecJac ! qp ! residual vector (mixed units) + real(rkind),dimension(in_SS4HG % nState) :: stateVecPerturbed ! perturbed state vector + real(rkind),dimension(in_SS4HG % nState) :: fluxVecInit,fluxVecJac ! flux vector (mized units) + real(qp),dimension(in_SS4HG % nState) :: resVecInit,resVecJac ! qp ! residual vector (mixed units) real(rkind) :: func ! function value logical(lgt) :: feasible ! flag to denote the feasibility of the solution integer(i4b) :: iJac ! index of row of the Jacobian matrix @@ -871,7 +871,7 @@ subroutine numJacobian(stateVec,dMat,nJac,err,message) stateVecPerturbed(:) = stateVec(:) ! loop through state variables - do iJac=1,in_SS4NR % nState + do iJac=1,in_SS4HG % nState !print*, 'iJac = ', iJac !globalPrintFlag = merge(.true.,.false., iJac==1) @@ -888,7 +888,7 @@ subroutine numJacobian(stateVec,dMat,nJac,err,message) ! compute the row of the Jacobian matrix select case(ixNumType) case(ixNumRes); nJac(:,iJac) = real(resVecJac - resVecInit, kind(rkind) )/dx ! Jacobian based on residuals - case(ixNumFlux); nJac(:,iJac) = -in_SS4NR % dt_cur*(fluxVecJac(:) - fluxVecInit(:))/dx ! Jacobian based on fluxes + case(ixNumFlux); nJac(:,iJac) = -in_SS4HG % dt_cur*(fluxVecJac(:) - fluxVecInit(:))/dx ! Jacobian based on fluxes case default; err=20; message=trim(message)//'Jacobian option not found'; return end select @@ -902,9 +902,9 @@ subroutine numJacobian(stateVec,dMat,nJac,err,message) ! print the Jacobian print*, '** numerical Jacobian:', ixNumType==ixNumRes - write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=min(iJac1,in_SS4NR % nState),min(iJac2,in_SS4NR % nState)) - do iLayer=min(iJac1,in_SS4NR % nState),min(iJac2,in_SS4NR % nState) - write(*,'(i4,1x,100(e12.5,1x))') iLayer, nJac(min(iJac1,in_SS4NR % nState):min(iJac2,in_SS4NR % nState),iLayer) + write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=min(iJac1,in_SS4HG % nState),min(iJac2,in_SS4HG % nState)) + do iLayer=min(iJac1,in_SS4HG % nState),min(iJac2,in_SS4HG % nState) + write(*,'(i4,1x,100(e12.5,1x))') iLayer, nJac(min(iJac1,in_SS4HG % nState):min(iJac2,in_SS4HG % nState),iLayer) end do !print*, 'PAUSE: testing Jacobian'; read(*,*) @@ -920,8 +920,8 @@ subroutine testBandMat(check,err,message) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local variables - real(rkind) :: fullJac(in_SS4NR % nState,in_SS4NR % nState) ! full Jacobian matrix - real(rkind) :: bandJac(in_SS4NR % nLeadDim,in_SS4NR % nState) ! band Jacobian matrix + real(rkind) :: fullJac(in_SS4HG % nState,in_SS4HG % nState) ! full Jacobian matrix + real(rkind) :: bandJac(in_SS4HG % nLeadDim,in_SS4HG % nState) ! band Jacobian matrix integer(i4b) :: iState,jState ! indices of the state vector character(LEN=256) :: cmessage ! error message of downwind routine ! class objects for subroutine arguments @@ -931,7 +931,7 @@ subroutine testBandMat(check,err,message) err=0; message='testBandMat/' ! check - if (in_SS4NR % nLeadDim == in_SS4NR % nState) then + if (in_SS4HG % nLeadDim == in_SS4HG % nState) then message=trim(message)//'do not expect nLeadDim==nState: check that are computing the band diagonal matrix'//& ' (is forceFullMatrix==.true.?)' err=20; return @@ -947,8 +947,8 @@ subroutine testBandMat(check,err,message) bandJac(:,:) = 0._rkind ! transfer into the lapack band diagonal structure - do iState=1,in_SS4NR % nState - do jState=max(1,iState-ku),min(in_SS4NR % nState,iState+kl) + do iState=1,in_SS4HG % nState + do jState=max(1,iState-ku),min(in_SS4HG % nState,iState+kl) bandJac(kl + ku + 1 + jState - iState, iState) = fullJac(jState,iState) end do end do @@ -956,7 +956,7 @@ subroutine testBandMat(check,err,message) ! print results print*, '** test banded analytical Jacobian:' write(*,'(a4,1x,100(i17,1x))') 'xCol', (iState, iState=iJac1,iJac2) - do iState=kl+1,in_SS4NR % nLeadDim; write(*,'(i4,1x,100(e17.10,1x))') iState, bandJac(iState,iJac1:iJac2); end do + do iState=kl+1,in_SS4HG % nLeadDim; write(*,'(i4,1x,100(e17.10,1x))') iState, bandJac(iState,iJac1:iJac2); end do ! check if the need to pause if(check)then @@ -969,11 +969,11 @@ end subroutine testBandMat subroutine initialize_computJacob_testBandMat ! *** Transfer data from out_computJacob class object to local variables in testBandMat *** associate(& - dt_cur => in_SS4NR % dt_cur ,& ! intent(in): current stepsize - nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers - nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers - nLayers => in_SS4NR % nLayers ,& ! intent(in): total number of layers - computeVegFlux => in_SS4NR % computeVegFlux & ! intent(in): flag to indicate if computing fluxes over vegetation + dt_cur => in_SS4HG % dt_cur ,& ! intent(in): current stepsize + nSnow => in_SS4HG % nSnow ,& ! intent(in): number of snow layers + nSoil => in_SS4HG % nSoil ,& ! intent(in): number of soil layers + nLayers => in_SS4HG % nLayers ,& ! intent(in): total number of layers + computeVegFlux => in_SS4HG % computeVegFlux & ! intent(in): flag to indicate if computing fluxes over vegetation &) call in_computJacob % initialize(dt_cur,nSnow,nSoil,nLayers,computeVegFlux,.false.,ixFullMatrix) end associate @@ -988,23 +988,23 @@ subroutine finalize_computJacob_testBandMat(err,cmessage) end subroutine finalize_computJacob_testBandMat subroutine initialize_computJacob_summaSolve4numrec - ! *** Transfer data to in_computJacob class object from local variables in summaSolve4numrec *** + ! *** Transfer data to in_computJacob class object from local variables in summaSolve4homegrown *** associate(& ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision,& ! intent(in): [i4b] groundwater parameterization - dt_cur => in_SS4NR % dt_cur ,& ! intent(in): current stepsize - nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers - nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers - nLayers => in_SS4NR % nLayers ,& ! intent(in): total number of layers - ixMatrix => in_SS4NR % ixMatrix ,& ! intent(in): type of matrix (full or band diagonal) - computeVegFlux => in_SS4NR % computeVegFlux & ! intent(in): flag to indicate if computing fluxes over vegetation + dt_cur => in_SS4HG % dt_cur ,& ! intent(in): current stepsize + nSnow => in_SS4HG % nSnow ,& ! intent(in): number of snow layers + nSoil => in_SS4HG % nSoil ,& ! intent(in): number of soil layers + nLayers => in_SS4HG % nLayers ,& ! intent(in): total number of layers + ixMatrix => in_SS4HG % ixMatrix ,& ! intent(in): type of matrix (full or band diagonal) + computeVegFlux => in_SS4HG % computeVegFlux & ! intent(in): flag to indicate if computing fluxes over vegetation &) call in_computJacob % initialize(dt_cur,nSnow,nSoil,nLayers,computeVegFlux,(ixGroundwater==qbaseTopmodel),ixMatrix) end associate end subroutine initialize_computJacob_summaSolve4numrec subroutine finalize_computJacob_summaSolve4numrec - ! *** Transfer data from out_computJacob class object to local variables in summaSolve4numrec *** - associate(err => out_SS4NR % err) + ! *** Transfer data from out_computJacob class object to local variables in summaSolve4homegrown *** + associate(err => out_SS4HG % err) call out_computJacob % finalize(err,cmessage) end associate end subroutine finalize_computJacob_summaSolve4numrec @@ -1034,17 +1034,17 @@ subroutine eval8summa_wrapper(stateVecNew,fluxVecNew,resVecNew,fNew,feasible,err err=0; message='eval8summa_wrapper/' associate(& - dt_cur => in_SS4NR % dt_cur ,& ! intent(in): current stepsize - dt => in_SS4NR % dt ,& ! intent(in): entire time step for drainage pond rate - nSnow => in_SS4NR % nSnow ,& ! intent(in): number of snow layers - nSoil => in_SS4NR % nSoil ,& ! intent(in): number of soil layers - nLayers => in_SS4NR % nLayers ,& ! intent(in): total number of layers - nState => in_SS4NR % nState ,& ! intent(in): total number of state variables - firstSubStep => in_SS4NR % firstSubStep ,& ! intent(in): flag to indicate if we are processing the first sub-step - computeVegFlux => in_SS4NR % computeVegFlux ,& ! intent(in): flag to indicate if computing fluxes over vegetation - scalarSolution => in_SS4NR % scalarSolution ,& ! intent(in): flag to denote if implementing the scalar solution - firstFluxCall => io_SS4NR % firstFluxCall ,& ! intent(inout): flag to indicate if we are processing the first flux call - ixSaturation => io_SS4NR % ixSaturation & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + dt_cur => in_SS4HG % dt_cur ,& ! intent(in): current stepsize + dt => in_SS4HG % dt ,& ! intent(in): entire time step for drainage pond rate + nSnow => in_SS4HG % nSnow ,& ! intent(in): number of snow layers + nSoil => in_SS4HG % nSoil ,& ! intent(in): number of soil layers + nLayers => in_SS4HG % nLayers ,& ! intent(in): total number of layers + nState => in_SS4HG % nState ,& ! intent(in): total number of state variables + firstSubStep => in_SS4HG % firstSubStep ,& ! intent(in): flag to indicate if we are processing the first sub-step + computeVegFlux => in_SS4HG % computeVegFlux ,& ! intent(in): flag to indicate if computing fluxes over vegetation + scalarSolution => in_SS4HG % scalarSolution ,& ! intent(in): flag to denote if implementing the scalar solution + firstFluxCall => io_SS4HG % firstFluxCall ,& ! intent(inout): flag to indicate if we are processing the first flux call + ixSaturation => io_SS4HG % ixSaturation & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) &) ! compute the flux and the residual vector for a given state vector call eval8summa(& @@ -1126,9 +1126,9 @@ function checkConv(rVec,xInc,xVec) ! association to variables in the data structures associate(& ! model control - iter => in_SS4NR % iter ,& ! intent(in): iteration index - nsnow => in_SS4NR % nsnow ,& ! intent(in): number of snow layers - scalarSolution => in_SS4NR % scalarSolution ,& ! intent(in): flag to denote if implementing the scalar solution + iter => in_SS4HG % iter ,& ! intent(in): iteration index + nsnow => in_SS4HG % nsnow ,& ! intent(in): number of snow layers + scalarSolution => in_SS4HG % scalarSolution ,& ! intent(in): flag to denote if implementing the scalar solution ! convergence parameters absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1),& ! intent(in): [dp] absolute convergence tolerance for vol frac liq water (-) absConvTol_matric => mpar_data%var(iLookPARAM%absConvTol_matric)%dat(1),& ! intent(in): [dp] absolute convergence tolerance for matric head (m) @@ -1144,7 +1144,7 @@ function checkConv(rVec,xInc,xVec) ixHydOnly => indx_data%var(iLookINDEX%ixHydOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for all hydrology states ixMatOnly => indx_data%var(iLookINDEX%ixMatOnly)%dat ,& ! intent(in): [i4b(:)] list of indices for matric head state variables in the state vector ixMatricHead => indx_data%var(iLookINDEX%ixMatricHead)%dat ,& ! intent(in): [i4b(:)] list of indices for matric head in the soil vector - fnew => out_SS4NR % fnew & ! intent(in): [dp] new function evaluations + fnew => out_SS4HG % fnew & ! intent(in): [dp] new function evaluations ) ! making associations with variables in the data structures ! ------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1223,7 +1223,7 @@ function checkConv(rVec,xInc,xVec) end function checkConv - end subroutine summaSolve4numrec + end subroutine summaSolve4homegrown -end module summaSolve4numrec_module +end module summaSolve4homegrown_module diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index b8f4df6b7..2b4ef69c9 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -92,7 +92,7 @@ module systemSolv_module ! look-up values for the numerical method USE mDecisions_module,only:& - numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes + numrec ,& ! homegrown backward Euler solution based on concepts from numerical recipes kinsol ,& ! SUNDIALS backward Euler solution using Kinsol ida ! SUNDIALS solution using IDA @@ -152,18 +152,18 @@ subroutine systemSolv(& err,message) ! intent(out): error code and error message ! --------------------------------------------------------------------------------------- ! structure allocations - USE allocspace_module,only:allocLocal ! allocate local data structures + USE allocspace_module,only:allocLocal ! allocate local data structures ! state vector and solver - USE getVectorz_module,only:getScaling ! get the scaling vectors - USE enthalpyTemp_module,only:T2enthalpy_snwWat ! convert temperature to liq+ice enthalpy for a snow layer + USE getVectorz_module,only:getScaling ! get the scaling vectors + USE enthalpyTemp_module,only:T2enthalpy_snwWat ! convert temperature to liq+ice enthalpy for a snow layer #ifdef SUNDIALS_ACTIVE - USE tol4ida_module,only:popTol4ida ! populate tolerances - USE eval8summaWithPrime_module,only:eval8summaWithPrime ! get the fluxes and residuals - USE summaSolve4ida_module,only:summaSolve4ida ! solve DAE by IDA - USE summaSolve4kinsol_module,only:summaSolve4kinsol ! solve DAE by KINSOL + USE tol4ida_module,only:popTol4ida ! populate tolerances + USE eval8summaWithPrime_module,only:eval8summaWithPrime ! get the fluxes and residuals + USE summaSolve4ida_module,only:summaSolve4ida ! solve DAE by IDA + USE summaSolve4kinsol_module,only:summaSolve4kinsol ! solve DAE by KINSOL #endif - USE eval8summa_module,only:eval8summa ! get the fluxes and residuals - USE summaSolve4numrec_module,only:summaSolve4numrec ! solve DAE by numerical recipes + USE eval8summa_module,only:eval8summa ! get the fluxes and residuals + USE summaSolve4homegrown_module,only:summaSolve4homegrown ! solve DAE using homegrown solver implicit none ! --------------------------------------------------------------------------------------- @@ -263,10 +263,10 @@ subroutine systemSolv(& integer(i4b), parameter :: scalarMaxIter=100 ! maximum number of iterations for the scalar solution numrec logical(lgt) :: converged ! convergence flag numrec logical(lgt), parameter :: post_massCons=.false. ! “perfectly” conserve mass by pushing the errors into the states, turn off for now to agree with SUNDIALS - ! class objects for call to summaSolve4numrec - type(in_type_summaSolve4numrec) :: in_SS4NR ! object for intent(in) summaSolve4numrec arguments - type(io_type_summaSolve4numrec) :: io_SS4NR ! object for intent(io) summaSolve4numrec arguments - type(out_type_summaSolve4numrec) :: out_SS4NR ! object for intent(out) summaSolve4numrec arguments + ! class objects for call to summaSolve4homegrown + type(in_type_summaSolve4numrec) :: in_SS4HG ! object for intent(in) summaSolve4numrec arguments + type(io_type_summaSolve4numrec) :: io_SS4HG ! object for intent(io) summaSolve4numrec arguments + type(out_type_summaSolve4numrec) :: out_SS4HG ! object for intent(out) summaSolve4numrec arguments ! flags logical(lgt) :: return_flag ! flag for handling systemSolv returns trigerred from internal subroutines logical(lgt) :: exit_flag ! flag for handling loop exit statements trigerred from internal subroutines @@ -282,11 +282,11 @@ subroutine systemSolv(& associate(ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision) ! intent(in): [i4b] choice of numerical solver select case(ixNumericalMethod) case(ida) ! solve for general time step using IDA - call solve_with_IDA; if (return_flag) return ! solve using IDA -- return if error + call solve_with_IDA; if (return_flag) return ! solve using IDA -- return if error case(kinsol) ! solve for BE time step using KINSOL - call solve_with_KINSOL; if (return_flag) return ! solve using KINSOL -- return if error + call solve_with_KINSOL; if (return_flag) return ! solve using KINSOL -- return if error case(numrec) ! solve for BE time step using Newton iterations - call Newton_iterations_numrec; if (return_flag) return ! Newton iterations using numerical recipes -- return if error + call Newton_iterations_homegrown; if (return_flag) return ! Newton iterations using homegrown solver -- return if error end select end associate @@ -556,22 +556,22 @@ subroutine initial_flux_and_residual_vectors_prime end subroutine initial_flux_and_residual_vectors_prime subroutine Newton_step - ! ** Compute the Newton step using numerical recipes ** + ! ** Compute the Newton step using concepts from numerical recipes ** associate(& ! layer geometry nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1),& ! intent(in): [i4b] number of snow layers nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) & ! intent(in): [i4b] number of soil layers ) - call in_SS4NR % initialize(dt_cur,dt,iter,nSnow,nSoil,nLayers,nLeadDim,nState,ixMatrix,firstSubStep,computeVegFlux,scalarSolution,fOld) - call io_SS4NR % initialize(firstFluxCall,xMin,xMax,ixSaturation) - call summaSolve4numrec(in_SS4NR,& ! input: model control - &stateVecTrial,fScale,xScale,resVec,sMul,dMat,& ! input: state vectors - &model_decisions,lookup_data,type_data,attr_data,mpar_data,forc_data,bvar_data,prog_data,& ! input: data structures - &indx_data,diag_data,flux_temp,deriv_data,& ! input-output: data structures - &dBaseflow_dMatric,io_SS4NR,& ! input-output: baseflow - &stateVecNew,fluxVec,resSink,resVecNew,out_SS4NR) ! output - call io_SS4NR % finalize(firstFluxCall,xMin,xMax,ixSaturation) - call out_SS4NR % finalize(fNew,converged,err,cmessage) + call in_SS4HG % initialize(dt_cur,dt,iter,nSnow,nSoil,nLayers,nLeadDim,nState,ixMatrix,firstSubStep,computeVegFlux,scalarSolution,fOld) + call io_SS4HG % initialize(firstFluxCall,xMin,xMax,ixSaturation) + call summaSolve4homegrown(in_SS4HG,& ! input: model control + &stateVecTrial,fScale,xScale,resVec,sMul,dMat,& ! input: state vectors + &model_decisions,lookup_data,type_data,attr_data,mpar_data,forc_data,bvar_data,prog_data,& ! input: data structures + &indx_data,diag_data,flux_temp,deriv_data,& ! input-output: data structures + &dBaseflow_dMatric,io_SS4HG,& ! input-output: baseflow + &stateVecNew,fluxVec,resSink,resVecNew,out_SS4HG) ! output + call io_SS4HG % finalize(firstFluxCall,xMin,xMax,ixSaturation) + call out_SS4HG % finalize(fNew,converged,err,cmessage) end associate if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! check for errors @@ -812,8 +812,8 @@ subroutine solve_with_KINSOL #endif end subroutine solve_with_KINSOL - subroutine Newton_iterations_numrec - ! ** Compute the backward Euler solution using Newton iterations from numerical recipes ** + subroutine Newton_iterations_homegrown + ! ** Compute the backward Euler solution using Newton iterations from homegrown solver ** ! define maximum number of iterations maxiter = nint(mpar_data%var(iLookPARAM%maxiter)%dat(1)) @@ -822,7 +822,7 @@ subroutine Newton_iterations_numrec localMaxIter = merge(scalarMaxIter, maxIter, scalarSolution) !--------------------------- - ! * solving F(y) = 0 from Backward Euler with free numerical recipes routines, y is the state vector + ! * solving F(y) = 0 from Backward Euler using concepts from numerical recipes, y is the state vector !--------------------------- ! iterate and update trial state vector, fluxes, and derivatives exit_flag=.false. ! initialize exit flag @@ -835,7 +835,7 @@ subroutine Newton_iterations_numrec end do if (post_massCons) call enforce_mass_conservation ! enforce mass conservation if desired - end subroutine Newton_iterations_numrec + end subroutine Newton_iterations_homegrown subroutine finalize_systemSolv ! set untapped melt energy to zero From 50c396a0c2a2a6bda7b053273b356f9b5fff7805 Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Wed, 15 May 2024 16:25:45 -0600 Subject: [PATCH 1307/1472] Add extra output variables for sundials functions --- build/source/dshare/get_ixname.f90 | 11 ++++++++++ build/source/dshare/popMetadat.f90 | 12 +++++++++++ build/source/dshare/var_lookup.f90 | 14 ++++++++++++- build/source/engine/summaSolve4ida.f90 | 29 ++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 029798f2e..2a2ff3d0c 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -600,6 +600,17 @@ function get_ixDiag(varName) case('balanceSnowMass' ); get_ixDiag = iLookDIAG%balanceSnowMass ! balance of water in the snow (kg m-2 s-1) case('balanceSoilMass' ); get_ixDiag = iLookDIAG%balanceSoilMass ! balance of water in the soil (kg m-2 s-1) case('balanceAqMass' ); get_ixDiag = iLookDIAG%balanceAqMass ! balance of water in the aquifer (kg m-2 s-1) + ! sundials integrator stats + case('numSteps' ); get_ixDiag = iLookDIAG%numSteps + case('numResEvals' ); get_ixDiag = iLookDIAG%numResEvals + case('numLinSolvSetups' ); get_ixDiag = iLookDIAG%numLinSolvSetups + case('numErrTestFails' ); get_ixDiag = iLookDIAG%numErrTestFails + case('kLast' ); get_ixDiag = iLookDIAG%kLast + case('kCur' ); get_ixDiag = iLookDIAG%kCur + case('hInitUsed' ); get_ixDiag = iLookDIAG%hInitUsed + case('hLast' ); get_ixDiag = iLookDIAG%hLast + case('hCur' ); get_ixDiag = iLookDIAG%hCur + case('tCur' ); get_ixDiag = iLookDIAG%tCur ! get to here if cannot find the variable case default get_ixDiag = integerMissing diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 1279b9fc3..7af003eba 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -454,6 +454,18 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%balanceSnowMass) = var_info('balanceSnowMass' , 'balance of water in the snow on data window' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%balanceSoilMass) = var_info('balanceSoilMass' , 'balance of water in the soil on data window' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%balanceAqMass) = var_info('balanceAqMass' , 'balance of water in the aquifer on data window' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! sundials integrator stats + diag_meta(iLookDIAG%numSteps) = var_info('numSteps' , 'number of steps taken by the integrator' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%numResEvals) = var_info('numResEvals' , 'number of residual evaluations' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%numLinSolvSetups) = var_info('numLinSolvSetups' , 'number of linear solver setups' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%numErrTestFails) = var_info('numErrTestFails' , 'number of error test failures' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%kLast) = var_info('kLast' , 'method order used on the last internal step' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%kCur) = var_info('kCur' , 'method order to be used on the next internal step' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%hInitUsed) = var_info('hInitUsed' , 'step size used on the first internal step' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%hLast) = var_info('hLast' , 'step size used on the last internal step' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%hCur) = var_info('hCur' , 'step size to be used on the next internal step' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%tCur) = var_info('tCur' , 'current time reached by the integrator' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + ! ----- ! * local model fluxes... ! ----------------------- diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index fc4b145c9..46dab0ad3 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -481,6 +481,17 @@ MODULE var_lookup integer(i4b) :: balanceSnowMass = integerMissing ! balance of water in the snow (kg m-2 s-1) integer(i4b) :: balanceSoilMass = integerMissing ! balance of water in the soil (kg m-2 s-1) integer(i4b) :: balanceAqMass = integerMissing ! balance of water in the aquifer (kg m-2 s-1) + ! sundials integrator stats + integer(i4b) :: numSteps = integerMissing ! + integer(i4b) :: numResEvals = integerMissing ! + integer(i4b) :: numLinSolvSetups = integerMissing ! + integer(i4b) :: numErrTestFails = integerMissing ! + integer(i4b) :: kLast = integerMissing ! + integer(i4b) :: kCur = integerMissing ! + integer(i4b) :: hInitUsed = integerMissing ! + integer(i4b) :: hLast = integerMissing ! + integer(i4b) :: hCur = integerMissing ! + integer(i4b) :: tCur = integerMissing ! endtype iLook_diag ! *********************************************************************************************************** @@ -917,7 +928,8 @@ MODULE var_lookup 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,& 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,& 91, 92, 93, 94, 95, 96, 97, 98, 99,100,& - 101,102,103,104) + 101,102,103,104,105,106,107,108,109,110,& + 111,112,113,114) ! named variables: model fluxes type(iLook_flux), public,parameter :: iLookFLUX =iLook_flux ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 52684c79c..29b744a1f 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -239,6 +239,16 @@ subroutine summaSolve4ida(& real(rkind),allocatable :: mLayerMatricHeadPrimePrev(:) ! previous derivative value for total water matric potential (m s-1) real(rkind),allocatable :: resVecPrev(:) ! previous value for residuals real(rkind),allocatable :: dCompress_dPsiPrev(:) ! previous derivative value soil compression + integer(c_long) :: nStepsSun(1) + integer(c_long) :: nREvals(1) + integer(c_long) :: nLinSetups(1) + integer(c_long) :: netFails(1) + integer(c_int) :: qLast(1) + integer(c_int) :: qCur(1) + real(c_double) :: hInitUsed(1) + real(c_double) :: hLast(1) + real(c_double) :: hCur(1) + real(c_double) :: tCur(1) ! flags logical(lgt) :: use_fdJac ! flag to use finite difference Jacobian, controlled by decision fDerivMeth logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true @@ -614,7 +624,26 @@ subroutine summaSolve4ida(& deallocate( eqns_data%resSink ) deallocate( rootsfound ) deallocate( rootdir ) + + ! Get Stats from IDA + retval = FIDAGetIntegratorStats(ida_mem, nStepsSun, nREvals, nLinSetups, & + netFails, qLast, qCur, hInitUsed, hLast, & + hCur, tCur) + diag_data%var(iLookDIAG%numSteps)%dat(1) = nStepsSun(1) + diag_data%var(iLookDIAG%numResEvals)%dat(1) = nREvals(1) + diag_data%var(iLookDIAG%numLinSolvSetups)%dat(1) = nLinSetups(1) + diag_data%var(iLookDIAG%numErrTestFails)%dat(1) = netFails(1) + diag_data%var(iLookDIAG%kLast)%dat(1) = qLast(1) + diag_data%var(iLookDIAG%kCur)%dat(1) = qCur(1) + diag_data%var(iLookDIAG%hInitUsed)%dat(1) = hInitUsed(1) + diag_data%var(iLookDIAG%hLast)%dat(1) = hLast(1) + diag_data%var(iLookDIAG%hCur)%dat(1) = hCur(1) + diag_data%var(iLookDIAG%tCur)%dat(1) = tCur(1) + + + + call FIDAFree(ida_mem) retval = FSUNLinSolFree(sunlinsol_LS) if(retval /= 0)then; err=20; message=trim(message)//'unable to free the linear solver'; return; endif From 05d462a7644bb8917cd8f040ce63e2a2d160ecd7 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 16 May 2024 03:14:56 -0600 Subject: [PATCH 1308/1472] Applied the homegrown label to class definitions and procedures used for the numerical recipes Newton solver in data_types module. --- build/source/dshare/data_types.f90 | 48 ++++++++++---------- build/source/engine/summaSolve4homegrown.f90 | 36 +++++++-------- build/source/engine/systemSolv.f90 | 24 +++++----- 3 files changed, 54 insertions(+), 54 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 374a9e8c9..fdf00eedf 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -657,7 +657,7 @@ MODULE data_types ! ** end varSubstep ! *********************************************************************************************************** - ! Define classes used to simplify calls to the subrotuines in summaSolve4numrec + ! Define classes used to simplify calls to the subrotuines in summaSolve4homegrown ! *********************************************************************************************************** type, public :: in_type_computJacob ! class for intent(in) arguments in computJacob call @@ -702,7 +702,7 @@ MODULE data_types ! Define classes used to simplify calls to the subrotuines in systemSolv ! *********************************************************************************************************** - type, public :: in_type_summaSolve4numrec ! class for intent(in) arguments in summaSolve4numrec call + type, public :: in_type_summaSolve4homegrown ! class for intent(in) arguments in summaSolve4homegrown call real(rkind) :: dt_cur ! intent(in): current stepsize real(rkind) :: dt ! intent(in): entire time step for drainage pond rate integer(i4b) :: iter ! intent(in): iteration index @@ -717,26 +717,26 @@ MODULE data_types logical(lgt) :: scalarSolution ! intent(in): flag to denote if implementing the scalar solution real(rkind) :: fOld ! intent(in): old function evaluation contains - procedure :: initialize => initialize_in_summaSolve4numrec - end type in_type_summaSolve4numrec + procedure :: initialize => initialize_in_summaSolve4homegrown + end type in_type_summaSolve4homegrown - type, public :: io_type_summaSolve4numrec ! class for intent(inout) arguments in summaSolve4numrec call + type, public :: io_type_summaSolve4homegrown ! class for intent(inout) arguments in summaSolve4homegrown call logical(lgt) :: firstFluxCall ! intent(inout): flag to indicate if we are processing the first flux call real(rkind) :: xMin,xMax ! intent(inout): brackets of the root integer(i4b) :: ixSaturation ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) contains - procedure :: initialize => initialize_io_summaSolve4numrec - procedure :: finalize => finalize_io_summaSolve4numrec - end type io_type_summaSolve4numrec + procedure :: initialize => initialize_io_summaSolve4homegrown + procedure :: finalize => finalize_io_summaSolve4homegrown + end type io_type_summaSolve4homegrown - type, public :: out_type_summaSolve4numrec ! class for intent(out) arguments in summaSolve4numrec call + type, public :: out_type_summaSolve4homegrown ! class for intent(out) arguments in summaSolve4homegrown call real(rkind) :: fNew ! intent(out): new function evaluation logical(lgt) :: converged ! intent(out): convergence flag integer(i4b) :: err ! intent(out): error code character(len=len_msg) :: message ! intent(out): error message contains - procedure :: finalize => finalize_out_summaSolve4numrec - end type out_type_summaSolve4numrec + procedure :: finalize => finalize_out_summaSolve4homegrown + end type out_type_summaSolve4homegrown contains @@ -1551,10 +1551,10 @@ subroutine finalize_out_lineSearchRefinement(out_lineSearchRefinement,fNew,conve message = out_lineSearchRefinement % message ! intent(out): error message end subroutine finalize_out_lineSearchRefinement - ! **** summaSolve4numrec **** + ! **** summaSolve4homegrown **** - subroutine initialize_in_summaSolve4numrec(in_SS4NR,dt_cur,dt,iter,nSnow,nSoil,nLayers,nLeadDim,nState,ixMatrix,firstSubStep,computeVegFlux,scalarSolution,fOld) - class(in_type_summaSolve4numrec),intent(out) :: in_SS4NR ! class object for intent(out) arguments + subroutine initialize_in_summaSolve4homegrown(in_SS4NR,dt_cur,dt,iter,nSnow,nSoil,nLayers,nLeadDim,nState,ixMatrix,firstSubStep,computeVegFlux,scalarSolution,fOld) + class(in_type_summaSolve4homegrown),intent(out) :: in_SS4NR ! class object for intent(out) arguments real(rkind) ,intent(in) :: dt_cur ! intent(in): current stepsize real(rkind) ,intent(in) :: dt ! intent(in): entire time step for drainage pond rate integer(i4b),intent(in) :: iter ! intent(in): iteration index @@ -1582,10 +1582,10 @@ subroutine initialize_in_summaSolve4numrec(in_SS4NR,dt_cur,dt,iter,nSnow,nSoil,n in_SS4NR % computeVegFlux = computeVegFlux in_SS4NR % scalarSolution = scalarSolution in_SS4NR % fOld = fOld - end subroutine initialize_in_summaSolve4numrec + end subroutine initialize_in_summaSolve4homegrown - subroutine initialize_io_summaSolve4numrec(io_SS4NR,firstFluxCall,xMin,xMax,ixSaturation) - class(io_type_summaSolve4numrec),intent(out) :: io_SS4NR ! class object for intent(inout) arguments + subroutine initialize_io_summaSolve4homegrown(io_SS4NR,firstFluxCall,xMin,xMax,ixSaturation) + class(io_type_summaSolve4homegrown),intent(out) :: io_SS4NR ! class object for intent(inout) arguments logical(lgt),intent(in) :: firstFluxCall ! intent(inout): flag to indicate if we are processing the first flux call real(rkind) ,intent(in) :: xMin,xMax ! intent(inout): brackets of the root integer(i4b),intent(in) :: ixSaturation ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) @@ -1594,10 +1594,10 @@ subroutine initialize_io_summaSolve4numrec(io_SS4NR,firstFluxCall,xMin,xMax,ixSa io_SS4NR % xMin = xMin io_SS4NR % xMax = xMax io_SS4NR % ixSaturation = ixSaturation - end subroutine initialize_io_summaSolve4numrec + end subroutine initialize_io_summaSolve4homegrown - subroutine finalize_io_summaSolve4numrec(io_SS4NR,firstFluxCall,xMin,xMax,ixSaturation) - class(io_type_summaSolve4numrec),intent(in) :: io_SS4NR ! class object for intent(inout) arguments + subroutine finalize_io_summaSolve4homegrown(io_SS4NR,firstFluxCall,xMin,xMax,ixSaturation) + class(io_type_summaSolve4homegrown),intent(in) :: io_SS4NR ! class object for intent(inout) arguments logical(lgt),intent(out) :: firstFluxCall ! intent(inout): flag to indicate if we are processing the first flux call real(rkind) ,intent(out) :: xMin,xMax ! intent(inout): brackets of the root integer(i4b),intent(out) :: ixSaturation ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) @@ -1606,10 +1606,10 @@ subroutine finalize_io_summaSolve4numrec(io_SS4NR,firstFluxCall,xMin,xMax,ixSatu xMin = io_SS4NR % xMin xMax = io_SS4NR % xMax ixSaturation = io_SS4NR % ixSaturation - end subroutine finalize_io_summaSolve4numrec + end subroutine finalize_io_summaSolve4homegrown - subroutine finalize_out_summaSolve4numrec(out_SS4NR,fNew,converged,err,message) - class(out_type_summaSolve4numrec),intent(in) :: out_SS4NR ! class object for intent(out) arguments + subroutine finalize_out_summaSolve4homegrown(out_SS4NR,fNew,converged,err,message) + class(out_type_summaSolve4homegrown),intent(in) :: out_SS4NR ! class object for intent(out) arguments real(rkind) ,intent(out) :: fNew ! intent(out): new function evaluation logical(lgt),intent(out) :: converged ! intent(out): convergence flag integer(i4b),intent(out) :: err ! intent(out): error code @@ -1619,6 +1619,6 @@ subroutine finalize_out_summaSolve4numrec(out_SS4NR,fNew,converged,err,message) converged = out_SS4NR % converged err = out_SS4NR % err message = out_SS4NR % message - end subroutine finalize_out_summaSolve4numrec + end subroutine finalize_out_summaSolve4homegrown END MODULE data_types diff --git a/build/source/engine/summaSolve4homegrown.f90 b/build/source/engine/summaSolve4homegrown.f90 index 195c78565..000e8893c 100644 --- a/build/source/engine/summaSolve4homegrown.f90 +++ b/build/source/engine/summaSolve4homegrown.f90 @@ -76,9 +76,9 @@ module summaSolve4homegrown_module out_type_computJacob, & ! class for computJacob arguments in_type_lineSearchRefinement, & ! class for lineSearchRefinement arguments out_type_lineSearchRefinement,& ! class for lineSearchRefinement arguments - in_type_summaSolve4numrec, & ! class for summaSolve4homegrown arguments - io_type_summaSolve4numrec, & ! class for summaSolve4homegrown arguments - out_type_summaSolve4numrec ! class for summaSolve4homegrown arguments + in_type_summaSolve4homegrown, & ! class for summaSolve4homegrown arguments + io_type_summaSolve4homegrown, & ! class for summaSolve4homegrown arguments + out_type_summaSolve4homegrown ! class for summaSolve4homegrown arguments ! look-up values for the choice of groundwater parameterization @@ -142,8 +142,8 @@ subroutine summaSolve4homegrown(& USE matrixOper_module, only: scaleMatrices implicit none ! -------------------------------------------------------------------------------------------------------------------------------- - type(in_type_summaSolve4numrec),intent(in) :: in_SS4HG ! model control variables and previous function evaluation - type(io_type_summaSolve4numrec),intent(inout) :: io_SS4HG ! first flux call flag and baseflow variables + type(in_type_summaSolve4homegrown),intent(in) :: in_SS4HG ! model control variables and previous function evaluation + type(io_type_summaSolve4homegrown),intent(inout) :: io_SS4HG ! first flux call flag and baseflow variables ! input: model control ! logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call ! input: state vectors @@ -176,7 +176,7 @@ subroutine summaSolve4homegrown(& real(rkind),intent(out) :: fluxVecNew(:) ! new flux vector real(rkind),intent(out) :: resSinkNew(:) ! sink terms on the RHS of the flux equation real(qp),intent(out) :: resVecNew(:) ! NOTE: qp ! new residual vector - type(out_type_summaSolve4numrec),intent(out) :: out_SS4HG ! new function evaluation, convergence flag, and error control + type(out_type_summaSolve4homegrown),intent(out) :: out_SS4HG ! new function evaluation, convergence flag, and error control ! -------------------------------------------------------------------------------------------------------------------------------- ! local variables ! -------------------------------------------------------------------------------------------------------------------------------- @@ -215,13 +215,13 @@ subroutine summaSolve4homegrown(& ! ***** Compute the Newton Step ***** - call initialize_summaSolve4numrec; if (return_flag) return ! initial setup -- return if error + call initialize_summaSolve4homegrown; if (return_flag) return ! initial setup -- return if error - call update_Jacobian; if (return_flag) return ! compute Jacobian for Newton step -- return if error + call update_Jacobian; if (return_flag) return ! compute Jacobian for Newton step -- return if error - call solve_linear_system; if (return_flag) return ! solve the linear system for the Newton step -- return if error + call solve_linear_system; if (return_flag) return ! solve the linear system for the Newton step -- return if error - call refine_Newton_step; if (return_flag) return ! refine Newton step if needed -- return if error + call refine_Newton_step; if (return_flag) return ! refine Newton step if needed -- return if error associate(err => out_SS4HG % err,message => out_SS4HG % message) if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors @@ -229,7 +229,7 @@ subroutine summaSolve4homegrown(& contains - subroutine initialize_summaSolve4numrec + subroutine initialize_summaSolve4homegrown ! *** Initial steps for the summaSolve4homegrown algorithm (computing the Newton step) *** associate(& @@ -258,7 +258,7 @@ subroutine initialize_summaSolve4numrec ! initialize the global print flag globalPrintFlagInit=globalPrintFlag - end subroutine initialize_summaSolve4numrec + end subroutine initialize_summaSolve4homegrown subroutine update_Jacobian ! *** Update Jacobian used for Newton step *** @@ -271,9 +271,9 @@ subroutine update_Jacobian err => out_SS4HG % err ,& message => out_SS4HG % message & &) - call initialize_computJacob_summaSolve4numrec + call initialize_computJacob_summaSolve4homegrown call computJacob(in_computJacob,indx_data,prog_data,diag_data,deriv_data,dBaseflow_dMatric,dMat,aJac,out_computJacob) - call finalize_computJacob_summaSolve4numrec + call finalize_computJacob_summaSolve4homegrown if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! (check for errors) ! compute the numerical Jacobian matrix @@ -987,7 +987,7 @@ subroutine finalize_computJacob_testBandMat(err,cmessage) call out_computJacob % finalize(err,cmessage) end subroutine finalize_computJacob_testBandMat - subroutine initialize_computJacob_summaSolve4numrec + subroutine initialize_computJacob_summaSolve4homegrown ! *** Transfer data to in_computJacob class object from local variables in summaSolve4homegrown *** associate(& ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision,& ! intent(in): [i4b] groundwater parameterization @@ -1000,14 +1000,14 @@ subroutine initialize_computJacob_summaSolve4numrec &) call in_computJacob % initialize(dt_cur,nSnow,nSoil,nLayers,computeVegFlux,(ixGroundwater==qbaseTopmodel),ixMatrix) end associate - end subroutine initialize_computJacob_summaSolve4numrec + end subroutine initialize_computJacob_summaSolve4homegrown - subroutine finalize_computJacob_summaSolve4numrec + subroutine finalize_computJacob_summaSolve4homegrown ! *** Transfer data from out_computJacob class object to local variables in summaSolve4homegrown *** associate(err => out_SS4HG % err) call out_computJacob % finalize(err,cmessage) end associate - end subroutine finalize_computJacob_summaSolve4numrec + end subroutine finalize_computJacob_summaSolve4homegrown ! ********************************************************************************************************* ! * internal subroutine eval8summa_wrapper: compute the right-hand-side vector diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 2b4ef69c9..617b15296 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -69,15 +69,15 @@ module systemSolv_module ! provide access to the derived types to define the data structures USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength, & ! data vector with variable length dimension (rkind) - zLookup, & ! lookup tables - model_options, & ! defines the model decisions - in_type_summaSolve4numrec, & ! class for summaSolve4numrec arguments - io_type_summaSolve4numrec, & ! class for summaSolve4numrec arguments - out_type_summaSolve4numrec ! class for summaSolve4numrec arguments + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! lookup tables + model_options, & ! defines the model decisions + in_type_summaSolve4homegrown, & ! class for summaSolve4homegrown arguments + io_type_summaSolve4homegrown, & ! class for summaSolve4homegrown arguments + out_type_summaSolve4homegrown ! class for summaSolve4homegrown arguments ! look-up values for the choice of groundwater representation (local-column, or single-basin) USE mDecisions_module,only:& @@ -264,9 +264,9 @@ subroutine systemSolv(& logical(lgt) :: converged ! convergence flag numrec logical(lgt), parameter :: post_massCons=.false. ! “perfectly” conserve mass by pushing the errors into the states, turn off for now to agree with SUNDIALS ! class objects for call to summaSolve4homegrown - type(in_type_summaSolve4numrec) :: in_SS4HG ! object for intent(in) summaSolve4numrec arguments - type(io_type_summaSolve4numrec) :: io_SS4HG ! object for intent(io) summaSolve4numrec arguments - type(out_type_summaSolve4numrec) :: out_SS4HG ! object for intent(out) summaSolve4numrec arguments + type(in_type_summaSolve4homegrown) :: in_SS4HG ! object for intent(in) summaSolve4homegrown arguments + type(io_type_summaSolve4homegrown) :: io_SS4HG ! object for intent(io) summaSolve4homegrown arguments + type(out_type_summaSolve4homegrown) :: out_SS4HG ! object for intent(out) summaSolve4homegrown arguments ! flags logical(lgt) :: return_flag ! flag for handling systemSolv returns trigerred from internal subroutines logical(lgt) :: exit_flag ! flag for handling loop exit statements trigerred from internal subroutines From 5f7750d8497ed6396d293858748122b546f9ed5a Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 16 May 2024 03:35:21 -0600 Subject: [PATCH 1309/1472] Updated the comments in multiple routines to clarify references to numerical recipes. --- build/source/driver/summa_restart.f90 | 2 +- build/source/engine/bigAquifer.f90 | 2 +- build/source/engine/coupled_em.f90 | 4 ++-- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/expIntegral.f90 | 2 +- build/source/engine/mDecisions.f90 | 8 ++++---- build/source/engine/opSplittin.f90 | 2 +- build/source/engine/summaSolve4homegrown.f90 | 2 +- build/source/engine/varSubstep.f90 | 4 ++-- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/build/source/driver/summa_restart.f90 b/build/source/driver/summa_restart.f90 index bcff5833e..a66fee30a 100644 --- a/build/source/driver/summa_restart.f90 +++ b/build/source/driver/summa_restart.f90 @@ -70,7 +70,7 @@ subroutine summa_readRestart(summa1_struc, err, message) singleBasin ! single groundwater store over the entire basin ! look-up values for the numerical method USE mDecisions_module,only:& - numrec, & ! home-grown backward Euler solution using free versions of Numerical recipes + numrec, & ! homegrown backward Euler solution using concepts from numerical recipes kinsol, & ! SUNDIALS backward Euler solution using Kinsol ida ! SUNDIALS solution using IDA ! look-up values for the choice of variable in energy equations (BE residual or IDA state variable) diff --git a/build/source/engine/bigAquifer.f90 b/build/source/engine/bigAquifer.f90 index 07e955902..19ea9093b 100644 --- a/build/source/engine/bigAquifer.f90 +++ b/build/source/engine/bigAquifer.f90 @@ -20,7 +20,7 @@ module bigAquifer_module ! ----------------------------------------------------------------------------------------------------------- -! numerical recipes data types +! homegrown solver data types USE nrtype ! access missing values diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 50cbc9438..f2ccf01dd 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -20,7 +20,7 @@ module coupled_em_module -! numerical recipes data types +! homegrown solver data types USE nrtype ! physical constants @@ -84,7 +84,7 @@ module coupled_em_module ! look-up values for the numerical method USE mDecisions_module,only: & - numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes + numrec ,& ! homegrown backward Euler solution based on concepts from numerical recipes kinsol ,& ! SUNDIALS backward Euler solution using Kinsol ida ! SUNDIALS solution using IDA diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 34799af7d..1a5ec2f80 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -68,7 +68,7 @@ module eval8summa_module ! look-up values for the numerical method USE mDecisions_module,only: & - numrec, & ! home-grown backward Euler solution using free versions of Numerical recipes + numrec, & ! homegrown backward Euler solution based on concepts from numerical recipes kinsol, & ! SUNDIALS backward Euler solution using Kinsol ida ! SUNDIALS solution using IDA diff --git a/build/source/engine/expIntegral.f90 b/build/source/engine/expIntegral.f90 index 6061643b9..abda91617 100644 --- a/build/source/engine/expIntegral.f90 +++ b/build/source/engine/expIntegral.f90 @@ -5,7 +5,7 @@ module expIntegral_module public::expint contains - ! Numerical recipes routines removed; use code from UEB-Veg + ! Homegrown routines removed; use code from UEB-Veg ! ****************** EXPONENTIAL INTEGRAL FUNCTION ***************************************** ! From UEB-Veg diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index f482848fd..f93b200a1 100644 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -57,7 +57,7 @@ module mDecisions_module integer(i4b),parameter,public :: constantScaling = 71 ! constant scaling factor integer(i4b),parameter,public :: laiScaling = 72 ! exponential function of LAI (Leuning, Plant Cell Env 1995: "Scaling from..." [eq 9]) ! look-up values for the choice of numerical method -integer(i4b),parameter,public :: numrec = 81 ! home-grown backward Euler solution using free versions of Numerical recipes +integer(i4b),parameter,public :: numrec = 81 ! homegrown backward Euler solution using concepts from numerical recipes integer(i4b),parameter,public :: kinsol = 82 ! SUNDIALS backward Euler solution using Kinsol integer(i4b),parameter,public :: ida = 83 ! SUNDIALS solution using IDA ! look-up values for method used to compute derivative @@ -399,8 +399,8 @@ subroutine mDecisions(err,message) ! identify the numerical method select case(trim(model_decisions(iLookDECISIONS%num_method)%cDecision)) - case('numrec' ); model_decisions(iLookDECISIONS%num_method)%iDecision = numrec ! home-grown backward Euler solution using free versions of Numerical recipes - case('itertive'); model_decisions(iLookDECISIONS%num_method)%iDecision = numrec ! home-grown backward Euler solution (included for backwards compatibility) + case('numrec' ); model_decisions(iLookDECISIONS%num_method)%iDecision = numrec ! homegrown backward Euler solution using concepts from numerical recipes + case('itertive'); model_decisions(iLookDECISIONS%num_method)%iDecision = numrec ! homegrown backward Euler solution (included for backwards compatibility) case('kinsol' ); model_decisions(iLookDECISIONS%num_method)%iDecision = kinsol ! SUNDIALS backward Euler solution using Kinsol case('sundials'); model_decisions(iLookDECISIONS%num_method)%iDecision = ida ! SUNDIALS solution using IDA case('ida' ); model_decisions(iLookDECISIONS%num_method)%iDecision = ida ! SUNDIALS solution using IDA @@ -752,4 +752,4 @@ subroutine readoption(err,message) model_decisions(iVar)%cDecision = trim(decision) end do end subroutine readoption -end module mDecisions_module \ No newline at end of file +end module mDecisions_module diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index af33e2f79..9afc7d9f7 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -89,7 +89,7 @@ module opSplittin_module ! look-up values for the numerical method USE mDecisions_module,only: & - numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes + numrec ,& ! home-grown backward Euler solution using concepts from numerical recipes kinsol ,& ! SUNDIALS backward Euler solution using Kinsol ida ! SUNDIALS solution using IDA diff --git a/build/source/engine/summaSolve4homegrown.f90 b/build/source/engine/summaSolve4homegrown.f90 index 000e8893c..062d86367 100644 --- a/build/source/engine/summaSolve4homegrown.f90 +++ b/build/source/engine/summaSolve4homegrown.f90 @@ -243,7 +243,7 @@ subroutine initialize_summaSolve4homegrown ! choose Jacobian type select case(model_decisions(iLookDECISIONS%fDerivMeth)%iDecision) case(numerical) - err=20; message=trim(message)//'numerical derivatives are not implemented for BE numerical Recipes solver'; + err=20; message=trim(message)//'numerical derivatives are not implemented for BE homegrown solver'; return_flag=.true.; return case(analytical); ! this is fine case default diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index ef3cebaae..cba563974 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -75,7 +75,7 @@ module varSubstep_module ! look-up values for the numerical method USE mDecisions_module,only: & - numrec ,& ! home-grown backward Euler solution using free versions of Numerical recipes + numrec ,& ! homegrown backward Euler solution using concepts from numerical recipes kinsol ,& ! SUNDIALS backward Euler solution using Kinsol ida ! SUNDIALS solution using IDA @@ -1077,7 +1077,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! * check mass balance... ! ----------------------- - ! NOTE: currently this will only fail with kinsol solver, since mass balance is checked in the numrec solver and not checked for ida solver + ! NOTE: currently this will only fail with kinsol solver, since mass balance is checked in the homegrown numrec solver and not checked for ida solver ! Negative error code will mean step will be failed and retried with smaller step size if(computMassBalance)then From cdc41c85bf6a6be3a9f9ce56b7b37e51ebc822ef Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 17 May 2024 12:57:11 +0900 Subject: [PATCH 1310/1472] get rid of numrec decision and homegrown --- build/source/driver/summa_restart.f90 | 2 +- build/source/dshare/get_ixname.f90 | 26 ++++++++--------- build/source/dshare/popMetadat.f90 | 26 ++++++++--------- build/source/dshare/var_lookup.f90 | 4 +-- build/source/engine/coupled_em.f90 | 8 +++--- build/source/engine/eval8summa.f90 | 10 +++---- build/source/engine/mDecisions.f90 | 11 ++++---- build/source/engine/opSplittin.f90 | 6 ++-- build/source/engine/systemSolv.f90 | 28 +++++++++---------- build/source/engine/varSubstep.f90 | 14 +++++----- .../flags_params_sundials.txt | 4 +-- .../celia1990/summa_zDecisions.txt | 4 +-- 12 files changed, 71 insertions(+), 72 deletions(-) diff --git a/build/source/driver/summa_restart.f90 b/build/source/driver/summa_restart.f90 index a66fee30a..9196207c5 100644 --- a/build/source/driver/summa_restart.f90 +++ b/build/source/driver/summa_restart.f90 @@ -70,7 +70,7 @@ subroutine summa_readRestart(summa1_struc, err, message) singleBasin ! single groundwater store over the entire basin ! look-up values for the numerical method USE mDecisions_module,only:& - numrec, & ! homegrown backward Euler solution using concepts from numerical recipes + homegrown, & ! homegrown backward Euler solution using concepts from numerical recipes kinsol, & ! SUNDIALS backward Euler solution using Kinsol ida ! SUNDIALS solution using IDA ! look-up values for the choice of variable in energy equations (BE residual or IDA state variable) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 029798f2e..3a417341c 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -378,19 +378,19 @@ function get_ixParam(varName) case('soilIceCV' ); get_ixParam = iLookPARAM%soilIceCV ! CV of depth of soil ice, used to get frozen fraction (-) ! algorithmic control parameters case('minwind' ); get_ixParam = iLookPARAM%minwind ! minimum wind speed (m s-1) - case('minstep' ); get_ixParam = iLookPARAM%minstep ! minimum length of the time step numrec, not currently used - case('maxstep' ); get_ixParam = iLookPARAM%maxstep ! maximum length of the time step numrec - case('be_steps' ); get_ixParam = iLookPARAM%be_steps ! minimum number of substeps to take in a maxstep numrec - case('wimplicit' ); get_ixParam = iLookPARAM%wimplicit ! weight assigned to start-of-step fluxes numrec, not currently used - case('maxiter' ); get_ixParam = iLookPARAM%maxiter ! maximum number of iterations numrec and kinsol - case('relConvTol_liquid' ); get_ixParam = iLookPARAM%relConvTol_liquid ! relative convergence tolerance for vol frac liq water (-) numrec - case('absConvTol_liquid' ); get_ixParam = iLookPARAM%absConvTol_liquid ! absolute convergence tolerance for vol frac liq water (-) numrec - case('relConvTol_matric' ); get_ixParam = iLookPARAM%relConvTol_matric ! relative convergence tolerance for matric head (-) numrec - case('absConvTol_matric' ); get_ixParam = iLookPARAM%absConvTol_matric ! absolute convergence tolerance for matric head (m) numrec - case('relConvTol_energy' ); get_ixParam = iLookPARAM%relConvTol_energy ! relative convergence tolerance for energy (-) numrec - case('absConvTol_energy' ); get_ixParam = iLookPARAM%absConvTol_energy ! absolute convergence tolerance for energy (J m-3) numrec - case('relConvTol_aquifr' ); get_ixParam = iLookPARAM%relConvTol_aquifr ! relative convergence tolerance for aquifer storage (-) numrec - case('absConvTol_aquifr' ); get_ixParam = iLookPARAM%absConvTol_aquifr ! absolute convergence tolerance for aquifer storage (m) numrec + case('minstep' ); get_ixParam = iLookPARAM%minstep ! minimum length of the time step homegrown, not currently used + case('maxstep' ); get_ixParam = iLookPARAM%maxstep ! maximum length of the time step homegrown + case('be_steps' ); get_ixParam = iLookPARAM%be_steps ! minimum number of substeps to take in a maxstep homegrown + case('wimplicit' ); get_ixParam = iLookPARAM%wimplicit ! weight assigned to start-of-step fluxes homegrown, not currently used + case('maxiter' ); get_ixParam = iLookPARAM%maxiter ! maximum number of iterations homegrown and kinsol + case('relConvTol_liquid' ); get_ixParam = iLookPARAM%relConvTol_liquid ! relative convergence tolerance for vol frac liq water (-) homegrown + case('absConvTol_liquid' ); get_ixParam = iLookPARAM%absConvTol_liquid ! absolute convergence tolerance for vol frac liq water (-) homegrown + case('relConvTol_matric' ); get_ixParam = iLookPARAM%relConvTol_matric ! relative convergence tolerance for matric head (-) homegrown + case('absConvTol_matric' ); get_ixParam = iLookPARAM%absConvTol_matric ! absolute convergence tolerance for matric head (m) homegrown + case('relConvTol_energy' ); get_ixParam = iLookPARAM%relConvTol_energy ! relative convergence tolerance for energy (-) homegrown + case('absConvTol_energy' ); get_ixParam = iLookPARAM%absConvTol_energy ! absolute convergence tolerance for energy (J m-3) homegrown + case('relConvTol_aquifr' ); get_ixParam = iLookPARAM%relConvTol_aquifr ! relative convergence tolerance for aquifer storage (-) homegrown + case('absConvTol_aquifr' ); get_ixParam = iLookPARAM%absConvTol_aquifr ! absolute convergence tolerance for aquifer storage (m) homegrown case('relTolTempCas' ); get_ixParam = iLookPARAM%relTolTempCas ! relative error tolerance for canopy temperature state variable case('absTolTempCas' ); get_ixParam = iLookPARAM%absTolTempCas ! absolute error tolerance for canopy temperature state variable case('relTolTempVeg' ); get_ixParam = iLookPARAM%relTolTempVeg ! relative error tolerance for vegitation temp state var diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 1279b9fc3..73cec5cae 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -254,19 +254,19 @@ subroutine popMetadat(err,message) mpar_meta(iLookPARAM%soilIceCV) = var_info('soilIceCV' , 'CV of depth of soil ice, used to get frozen fraction' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! algorithmic control parameters mpar_meta(iLookPARAM%minwind) = var_info('minwind' , 'minimum wind speed' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%minstep) = var_info('minstep' , 'minimum length of the time step numrec, not currently used' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%maxstep) = var_info('maxstep' , 'maximum length of the time step numrec' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%be_steps) = var_info('be_steps' , 'minimum number of substeps to take in a maxstep numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%wimplicit) = var_info('wimplicit' , 'weight assigned to the start-of-step fluxes ,numrec, not currently used', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%maxiter) = var_info('maxiter' , 'maximum number of iterations numrec and kinsol' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_liquid) = var_info('relConvTol_liquid' , 'relative convergence tolerance for vol frac liq water numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_liquid) = var_info('absConvTol_liquid' , 'absolute convergence tolerance for vol frac liq water numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_matric) = var_info('relConvTol_matric' , 'relative convergence tolerance for matric head numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_matric) = var_info('absConvTol_matric' , 'absolute convergence tolerance for matric head numrec' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_energy) = var_info('relConvTol_energy' , 'relative convergence tolerance for energy numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_energy) = var_info('absConvTol_energy' , 'absolute convergence tolerance for energy numrec' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_aquifr) = var_info('relConvTol_aquifr' , 'relative convergence tolerance for aquifer storage numrec' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_aquifr) = var_info('absConvTol_aquifr' , 'absolute convergence tolerance for aquifer storage numrec' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%minstep) = var_info('minstep' , 'minimum length of the time step homegrown, not currently used' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%maxstep) = var_info('maxstep' , 'maximum length of the time step homegrown' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%be_steps) = var_info('be_steps' , 'minimum number of substeps to take in a maxstep homegrown' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%wimplicit) = var_info('wimplicit' , 'weight assigned to the start-of-step fluxes ,homegrown, not currently used', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%maxiter) = var_info('maxiter' , 'maximum number of iterations homegrown and kinsol' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_liquid) = var_info('relConvTol_liquid' , 'relative convergence tolerance for vol frac liq water homegrown' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_liquid) = var_info('absConvTol_liquid' , 'absolute convergence tolerance for vol frac liq water homegrown' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_matric) = var_info('relConvTol_matric' , 'relative convergence tolerance for matric head homegrown' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_matric) = var_info('absConvTol_matric' , 'absolute convergence tolerance for matric head homegrown' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_energy) = var_info('relConvTol_energy' , 'relative convergence tolerance for energy homegrown' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_energy) = var_info('absConvTol_energy' , 'absolute convergence tolerance for energy homegrown' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_aquifr) = var_info('relConvTol_aquifr' , 'relative convergence tolerance for aquifer storage homegrown' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_aquifr) = var_info('absConvTol_aquifr' , 'absolute convergence tolerance for aquifer storage homegrown' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%relTolTempCas) = var_info('relTolTempCas' , 'relative error tolerance for canopy temperature state variable' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%absTolTempCas) = var_info('absTolTempCas' , 'absolute error tolerance for canopy temperature state variable' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%relTolTempVeg) = var_info('relTolTempVeg' , 'relative error tolerance for vegitation temp state var' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index fc4b145c9..67a007a78 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -285,9 +285,9 @@ MODULE var_lookup integer(i4b) :: minwind = integerMissing ! minimum wind speed (m s-1) integer(i4b) :: minstep = integerMissing ! minimum length of the time step integer(i4b) :: maxstep = integerMissing ! maximum length of the time step - integer(i4b) :: be_steps = integerMissing ! minimum number of substeps to take in a maxstep numrec + integer(i4b) :: be_steps = integerMissing ! minimum number of substeps to take in a maxstep homegrown integer(i4b) :: wimplicit = integerMissing ! weight assigned to the start-of-step fluxes - integer(i4b) :: maxiter = integerMissing ! maximum number of iterations numrec and kinsol + integer(i4b) :: maxiter = integerMissing ! maximum number of iterations homegrown and kinsol integer(i4b) :: relConvTol_liquid = integerMissing ! relative convergence tolerance for vol frac liq water (-) integer(i4b) :: absConvTol_liquid = integerMissing ! absolute convergence tolerance for vol frac liq water (-) integer(i4b) :: relConvTol_matric = integerMissing ! relative convergence tolerance for matric head (-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index f2ccf01dd..98fdd7eda 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -84,7 +84,7 @@ module coupled_em_module ! look-up values for the numerical method USE mDecisions_module,only: & - numrec ,& ! homegrown backward Euler solution based on concepts from numerical recipes + homegrown ,& ! homegrown backward Euler solution based on concepts from numerical recipes kinsol ,& ! SUNDIALS backward Euler solution using Kinsol ida ! SUNDIALS solution using IDA @@ -413,9 +413,9 @@ subroutine coupled_em(& ! identify the need to check the mass balance, both methods should work if tolerance coarse enough select case(ixNumericalMethod) - case(ida); checkMassBalance_ds = .false. ! IDA balance agreement levels are controlled by set tolerances - case(kinsol, numrec); checkMassBalance_ds = .true. ! KINSOL or numrec give finite difference dt_sub fluxes and were summed for an average flux - case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or numrec (or itertive, which is numrec)'; return + case(ida); checkMassBalance_ds = .false. ! IDA balance agreement levels are controlled by set tolerances + case(kinsol, homegrown); checkMassBalance_ds = .true. ! KINSOL or homegrown give finite difference dt_sub fluxes and were summed for an average flux + case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or homegrown (or itertive, which is homegrown)'; return end select ! set the flag to compute enthalpy, may want to have this true always if want to output enthalpy diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 1a5ec2f80..94560a772 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -68,7 +68,7 @@ module eval8summa_module ! look-up values for the numerical method USE mDecisions_module,only: & - numrec, & ! homegrown backward Euler solution based on concepts from numerical recipes + homegrown, & ! homegrown backward Euler solution based on concepts from numerical recipes kinsol, & ! SUNDIALS backward Euler solution using Kinsol ida ! SUNDIALS solution using IDA @@ -292,7 +292,7 @@ subroutine eval8summa(& ! initialize error control err=0; message="eval8summa/" - ! check the feasibility of the solution always with BE numrec but not inside Sundials solver + ! check the feasibility of the solution always with BE homegrown but not inside Sundials solver feasible=.true. if (.not.insideSUN) then call checkFeas(& @@ -530,7 +530,7 @@ subroutine eval8summa(& ! only need to check longwave balance with numerical recipes solver checkLWBalance = .false. - if(ixNumericalMethod==numrec) checkLWBalance = .true. + if(ixNumericalMethod==homegrown) checkLWBalance = .true. ! compute the fluxes for a given state vector call computFlux(& @@ -874,7 +874,7 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st detect_events = .true. ! flag to do freezing point event detection and cross-over with epsT, works best if on epsT = 1.e-7_rkind ! small interval above/below critical (K), works better if larger water_bounds = .true. ! flag to force water bounds, works best if on - case(numrec) + case(homegrown) small_delTemp = .true. ! flag to constain temperature change to be less than zMaxTempIncrement zMaxTempIncrement = 10._rkind ! maximum temperature increment (K) small_delMatric = .true. ! flag to constain matric head change to be less than zMaxMatricIncrement @@ -882,7 +882,7 @@ subroutine imposeConstraints(model_decisions,indx_data, prog_data, mpar_data, st detect_events = .true. ! flag to do freezing point event detection and cross-over with epsT epsT = 1.e-7_rkind ! small interval above/below critical (K) water_bounds = .true. ! flag to force water bounds - case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or numrec (or itertive, which is numrec)'; return + case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or homegrown (or itertive, which is homegrown)'; return end select vGn_m = 1._rkind - 1._rkind/vGn_n diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index f93b200a1..2205715ac 100644 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -57,7 +57,7 @@ module mDecisions_module integer(i4b),parameter,public :: constantScaling = 71 ! constant scaling factor integer(i4b),parameter,public :: laiScaling = 72 ! exponential function of LAI (Leuning, Plant Cell Env 1995: "Scaling from..." [eq 9]) ! look-up values for the choice of numerical method -integer(i4b),parameter,public :: numrec = 81 ! homegrown backward Euler solution using concepts from numerical recipes +integer(i4b),parameter,public :: homegrown = 81 ! homegrown backward Euler solution using concepts from numerical recipes integer(i4b),parameter,public :: kinsol = 82 ! SUNDIALS backward Euler solution using Kinsol integer(i4b),parameter,public :: ida = 83 ! SUNDIALS solution using IDA ! look-up values for method used to compute derivative @@ -399,11 +399,10 @@ subroutine mDecisions(err,message) ! identify the numerical method select case(trim(model_decisions(iLookDECISIONS%num_method)%cDecision)) - case('numrec' ); model_decisions(iLookDECISIONS%num_method)%iDecision = numrec ! homegrown backward Euler solution using concepts from numerical recipes - case('itertive'); model_decisions(iLookDECISIONS%num_method)%iDecision = numrec ! homegrown backward Euler solution (included for backwards compatibility) - case('kinsol' ); model_decisions(iLookDECISIONS%num_method)%iDecision = kinsol ! SUNDIALS backward Euler solution using Kinsol - case('sundials'); model_decisions(iLookDECISIONS%num_method)%iDecision = ida ! SUNDIALS solution using IDA - case('ida' ); model_decisions(iLookDECISIONS%num_method)%iDecision = ida ! SUNDIALS solution using IDA + case('homegrown'); model_decisions(iLookDECISIONS%num_method)%iDecision = homegrown ! homegrown backward Euler solution using concepts from numerical recipes + case('itertive' ); model_decisions(iLookDECISIONS%num_method)%iDecision = homegrown ! homegrown backward Euler solution (included for backwards compatibility) + case('kinsol' ); model_decisions(iLookDECISIONS%num_method)%iDecision = kinsol ! SUNDIALS backward Euler solution using Kinsol + case('ida' ); model_decisions(iLookDECISIONS%num_method)%iDecision = ida ! SUNDIALS solution using IDA case default err=10; message=trim(message)//"unknown numerical method [option="//trim(model_decisions(iLookDECISIONS%num_method)%cDecision)//"]"; return end select diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 9afc7d9f7..0c73a4019 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -89,7 +89,7 @@ module opSplittin_module ! look-up values for the numerical method USE mDecisions_module,only: & - numrec ,& ! home-grown backward Euler solution using concepts from numerical recipes + homegrown ,& ! home-grown backward Euler solution using concepts from numerical recipes kinsol ,& ! SUNDIALS backward Euler solution using Kinsol ida ! SUNDIALS solution using IDA @@ -607,8 +607,8 @@ subroutine get_nCoupling associate(ixNumericalMethod => model_decisions(iLookDECISIONS%num_method)%iDecision) ! intent(in): [i4b] choice of numerical solver ! we just solve the fully coupled problem if IDA for now, splitting can happen otherwise select case(ixNumericalMethod) - case(ida); nCoupling = 1 - case(kinsol, numrec); nCoupling = 2 + case(ida); nCoupling = 1 + case(kinsol, homegrown); nCoupling = 2 case default; err=20; message=trim(message)//'solver choice not found'; return_flag=.true.; return end select end associate diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 617b15296..908f32a83 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -92,7 +92,7 @@ module systemSolv_module ! look-up values for the numerical method USE mDecisions_module,only:& - numrec ,& ! homegrown backward Euler solution based on concepts from numerical recipes + homegrown ,& ! homegrown backward Euler solution based on concepts from numerical recipes kinsol ,& ! SUNDIALS backward Euler solution using Kinsol ida ! SUNDIALS solution using IDA @@ -145,7 +145,7 @@ subroutine systemSolv(& ! output: balances (only computed at this level for ida) balance, & ! intent(out): balance of energy per state ! output: model control - niter, & ! intent(out): number of iterations taken (numrec) + niter, & ! intent(out): number of iterations taken (homegrown) nSteps, & ! intent(out): number of time steps taken in solver reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out): flag to denote that there was too much melt @@ -200,7 +200,7 @@ subroutine systemSolv(& real(rkind),intent(out) :: stateVecTrial(:) ! trial state vector (mixed units) real(rkind),intent(out) :: stateVecPrime(:) ! trial state vector (mixed units) real(rkind),intent(out) :: fluxVec(nState) ! flux vector (mixed units) - real(rkind),intent(out) :: resSink(nState) ! additional terms in the residual vector numrec + real(rkind),intent(out) :: resSink(nState) ! additional terms in the residual vector homegrown solver real(qp),intent(out) :: resVec(nState) ! NOTE: qp ! residual vector real(rkind),intent(out) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) ! output: balances (only computed at this level for ida) @@ -244,24 +244,24 @@ subroutine systemSolv(& real(rkind) :: rtol(nState) ! relative tolerance ida type(var_dlength) :: flux_sum ! sum of fluxes model fluxes for a local HRU over a dt_cur real(rkind), allocatable :: mLayerCmpress_sum(:) ! sum of compression of the soil matrix - ! ida variables outputted if use eval8summaWithPrime (not used here, just inside ida solver) + ! ida solver variables outputted if use eval8summaWithPrime (not used here, just inside ida solver) logical(lgt) :: firstSplitOper0 ! flag to indicate if we are processing the first flux call in a splitting operation, changed inside eval8summaWithPrime real(rkind) :: scalarCanopyTempPrime ! prime value for temperature of the vegetation canopy (K s-1) real(rkind) :: scalarCanopyWatPrime ! prime value for total water content of the vegetation canopy (kg m-2 s-1) real(rkind) :: mLayerTempPrime(nLayers) ! prime vector of temperature of each snow and soil layer (K s-1) real(rkind), allocatable :: mLayerMatricHeadPrime(:) ! prime vector of matric head of each snow and soil layer (m s-1) real(rkind) :: mLayerVolFracWatPrime(nLayers)! prime vector of volumetric total water content of each snow and soil layer (s-1) - ! kinsol and numrec variables + ! kinsol and homegrown solver variables real(rkind) :: fScale(nState) ! characteristic scale of the function evaluations (mixed units) real(rkind) :: xScale(nState) ! characteristic scale of the state vector (mixed units) - real(qp) :: resVecNew(nState) ! NOTE: qp ! new residual vector numrec - ! numrec variables - real(rkind) :: fOld,fNew ! function values (-); NOTE: dimensionless because scaled numrec - real(rkind) :: xMin,xMax ! state minimum and maximum (mixed units) numrec - integer(i4b) :: maxiter ! maximum number of iterations numrec - integer(i4b) :: localMaxIter ! maximum number of iterations (depends on solution type) numrec - integer(i4b), parameter :: scalarMaxIter=100 ! maximum number of iterations for the scalar solution numrec - logical(lgt) :: converged ! convergence flag numrec + real(qp) :: resVecNew(nState) ! NOTE: qp ! new residual vector homegrown solver + ! homegrown solver variables + real(rkind) :: fOld,fNew ! function values (-); NOTE: dimensionless because scaled homegrown solver + real(rkind) :: xMin,xMax ! state minimum and maximum (mixed units) homegrown solver + integer(i4b) :: maxiter ! maximum number of iterations homegrown solver + integer(i4b) :: localMaxIter ! maximum number of iterations (depends on solution type) homegrown solver + integer(i4b), parameter :: scalarMaxIter=100 ! maximum number of iterations for the scalar solution homegrown solver + logical(lgt) :: converged ! convergence flag homegrown solver logical(lgt), parameter :: post_massCons=.false. ! “perfectly” conserve mass by pushing the errors into the states, turn off for now to agree with SUNDIALS ! class objects for call to summaSolve4homegrown type(in_type_summaSolve4homegrown) :: in_SS4HG ! object for intent(in) summaSolve4homegrown arguments @@ -285,7 +285,7 @@ subroutine systemSolv(& call solve_with_IDA; if (return_flag) return ! solve using IDA -- return if error case(kinsol) ! solve for BE time step using KINSOL call solve_with_KINSOL; if (return_flag) return ! solve using KINSOL -- return if error - case(numrec) ! solve for BE time step using Newton iterations + case(homegrown) ! solve for BE time step using Newton iterations call Newton_iterations_homegrown; if (return_flag) return ! Newton iterations using homegrown solver -- return if error end select end associate diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index cba563974..05221beac 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -75,7 +75,7 @@ module varSubstep_module ! look-up values for the numerical method USE mDecisions_module,only: & - numrec ,& ! homegrown backward Euler solution using concepts from numerical recipes + homegrown ,& ! homegrown backward Euler solution using concepts from numerical recipes kinsol ,& ! SUNDIALS backward Euler solution using Kinsol ida ! SUNDIALS solution using IDA @@ -381,7 +381,7 @@ subroutine varSubstep(& ! output: balances (only computed at this level for ida) balance, & ! intent(out): balance per state variable ! output model control - niter, & ! intent(out): number of iterations taken (numrec) + niter, & ! intent(out): number of iterations taken (homegrown solver) nSteps, & ! intent(out): number of time steps taken in solver reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt @@ -448,7 +448,7 @@ subroutine varSubstep(& return endif - ! update prognostic variables, update balances, and check them for possible step reduction if numrec or kinsol + ! update prognostic variables, update balances, and check them for possible step reduction if homegrown or kinsol solver call updateProg(dtSubstep,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVecPrime, & ! input: states doAdjustTemp,computeVegFlux,computMassBalance,computNrgBalance,computeEnthTemp,enthalpyStateVec,use_lookup,& ! input: model control model_decisions,lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures @@ -980,7 +980,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! output: error control err,cmessage) ! intent(out): error control #endif - case(kinsol, numrec) + case(kinsol, homegrown) ! update diagnostic variables call updateVars(& ! input @@ -1020,7 +1020,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! compute energy balance if didn't do inside solver substeps select case(ixNumericalMethod) case(ida); ! do nothing, already computed - case(kinsol, numrec) + case(kinsol, homegrown) ! calculate delta ice scalarCanopyIceDelta = scalarCanopyIceTrial - scalarCanopyIce mLayerVolFracIceDelta = mLayerVolFracIceTrial - mLayerVolFracIce(1:nLayers) @@ -1077,7 +1077,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! * check mass balance... ! ----------------------- - ! NOTE: currently this will only fail with kinsol solver, since mass balance is checked in the homegrown numrec solver and not checked for ida solver + ! NOTE: currently this will only fail with kinsol solver, since mass balance is checked in the homegrown solver and not checked for ida solver ! Negative error code will mean step will be failed and retried with smaller step size if(computMassBalance)then @@ -1127,7 +1127,7 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec ! compute mass balance if didn't do inside solver substeps select case(ixNumericalMethod) case(ida); ! do nothing - case(kinsol, numrec) + case(kinsol, homegrown) ! old mass balance checks if(ixVegHyd/=integerMissing)then ! check the mass balance for the canopy for step reduction (ida and kinsol should have done this already unless modified canopy water above) diff --git a/docs/sundials_bmi_flags/flags_params_sundials.txt b/docs/sundials_bmi_flags/flags_params_sundials.txt index 3046a16aa..91f184dae 100644 --- a/docs/sundials_bmi_flags/flags_params_sundials.txt +++ b/docs/sundials_bmi_flags/flags_params_sundials.txt @@ -1,7 +1,7 @@ -To switch between SUMMA-BE and SUMMA-SUNDIALS, the num_method in the model_decision file can be either one of the values "numrec" (choice "itertive" is backward compatible), "kinsol", or "ida". +To switch between SUMMA-BE and SUMMA-SUNDIALS, the num_method in the model_decision file can be either one of the values "homegrown" (choice "itertive" is backward compatible), "kinsol", or "ida". -In energy conservation residual for backward Euler, either the the analytical (closed form) heat capacity formula or the enthalpy finite difference formula (dH_T/dT) is used. The "nrgConserv" variable has been added to the var_lookup module to handle such decision. A user should add this variable to the model_decision file with one of the values "closedForm" or "enthalpyFD". Choice of num_method as "itertive" will set num_method=numrec and nrgConserv=closedForm. +In energy conservation residual for backward Euler, either the the analytical (closed form) heat capacity formula or the enthalpy finite difference formula (dH_T/dT) is used. The "nrgConserv" variable has been added to the var_lookup module to handle such decision. A user should add this variable to the model_decision file with one of the values "closedForm" or "enthalpyFD". Choice of num_method as "itertive" will set num_method=homegrown and nrgConserv=closedForm. All SUMMA-SUNDIALS files are in build/source/engine. The important ones are as following: diff --git a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zDecisions.txt b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zDecisions.txt index d03f84873..a4555f6dd 100644 --- a/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zDecisions.txt +++ b/test_ngen/celia_test/settings/syntheticTestCases/celia1990/summa_zDecisions.txt @@ -13,7 +13,7 @@ vegeParTbl USGS ! (04) vegetation category datas soilStress NoahType ! (05) choice of function for the soil moisture control on stomatal resistance stomResist BallBerry ! (06) choice of function for stomatal resistance ! *********************************************************************************************************************** -num_method numrec ! (07) choice of numerical method +num_method homegrown ! (07) choice of numerical method fDerivMeth analytic ! (08) method used to calculate Jacobian LAI_method monTable ! (09) method used to determine LAI and SAI f_Richards mixdform ! (10) form of Richard's equation @@ -63,7 +63,7 @@ nrgConserv closedForm ! (30) choice of variable in ene ! Jarvis ! Jarvis ! ----------------------------------------------------------------------------------------------- ! (07) choice of numerical method -! numrec ! home-grown backward Euler +! homegrown ! home-grown backward Euler ! kinsol ! SUNDIALS backward Euler solution using Kinsol ! ida ! SUNDIALS solution using IDA ! ----------------------------------------------------------------------------------------------- From 1c4531d4baf19d96edd6a4be2bf31576c33f5b60 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 17 May 2024 13:09:15 +0900 Subject: [PATCH 1311/1472] numrec decision still in there for backwards comp, remove if okay to --- build/source/engine/mDecisions.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index 2205715ac..fc4ff535e 100644 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -400,6 +400,7 @@ subroutine mDecisions(err,message) ! identify the numerical method select case(trim(model_decisions(iLookDECISIONS%num_method)%cDecision)) case('homegrown'); model_decisions(iLookDECISIONS%num_method)%iDecision = homegrown ! homegrown backward Euler solution using concepts from numerical recipes + case('numrec' ); model_decisions(iLookDECISIONS%num_method)%iDecision = homegrown ! homegrown backward Euler solution using concepts from numerical recipes case('itertive' ); model_decisions(iLookDECISIONS%num_method)%iDecision = homegrown ! homegrown backward Euler solution (included for backwards compatibility) case('kinsol' ); model_decisions(iLookDECISIONS%num_method)%iDecision = kinsol ! SUNDIALS backward Euler solution using Kinsol case('ida' ); model_decisions(iLookDECISIONS%num_method)%iDecision = ida ! SUNDIALS solution using IDA From 2755c68a6687a2e9cb3ba95e2d1065fc3f3307d3 Mon Sep 17 00:00:00 2001 From: Kyle Klenk Date: Mon, 20 May 2024 12:44:18 -0600 Subject: [PATCH 1312/1472] adjusted cmakelist --- build/cmake/CMakeLists.txt | 3 ++- build/source/engine/summaSolve4ida.f90 | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 449cb7e45..5dad739cb 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -438,7 +438,8 @@ set(ACTORS_GLOBAL ${ACTORS_DIR}/global/auxiliary.cpp ${ACTORS_DIR}/global/fileManager.cpp ${ACTORS_DIR}/global/settings_functions.cpp - ${ACTORS_DIR}/global/timing_info.cpp) + ${ACTORS_DIR}/global/timing_info.cpp + ${ACTORS_DIR}/global/logger.cpp) set(SYS_INIT ${SYS_INIT_DIR}/batch.cpp ${SYS_INIT_DIR}/batch_container.cpp diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 29b744a1f..cfd263871 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -713,7 +713,7 @@ subroutine setSolverParams(dt_cur,ida_mem,retval) integer(c_long),parameter :: max_step = 999999 ! maximum number of steps, default = 500 integer,parameter :: fail_iter = 50 ! maximum number of error test and convergence test failures, default 10 real(qp) :: h_max ! maximum stepsize, default = infinity - real(qp),parameter :: h_init = 0 ! initial stepsize + real(qp),parameter :: h_init = 3600 ! initial stepsize ! Set the maximum BDF order retval = FIDASetMaxOrd(ida_mem, max_order) From 522db23b7ec0ba0a4cf176943bd93b9e879d96a1 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 22 May 2024 16:24:27 +0900 Subject: [PATCH 1313/1472] remove numrec decision, now use homegrown (or itertive) --- build/source/engine/mDecisions.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index fc4ff535e..2205715ac 100644 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -400,7 +400,6 @@ subroutine mDecisions(err,message) ! identify the numerical method select case(trim(model_decisions(iLookDECISIONS%num_method)%cDecision)) case('homegrown'); model_decisions(iLookDECISIONS%num_method)%iDecision = homegrown ! homegrown backward Euler solution using concepts from numerical recipes - case('numrec' ); model_decisions(iLookDECISIONS%num_method)%iDecision = homegrown ! homegrown backward Euler solution using concepts from numerical recipes case('itertive' ); model_decisions(iLookDECISIONS%num_method)%iDecision = homegrown ! homegrown backward Euler solution (included for backwards compatibility) case('kinsol' ); model_decisions(iLookDECISIONS%num_method)%iDecision = kinsol ! SUNDIALS backward Euler solution using Kinsol case('ida' ); model_decisions(iLookDECISIONS%num_method)%iDecision = ida ! SUNDIALS solution using IDA From f71ff4653edec7ff29450056f70afd44fd897c16 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 22 May 2024 16:27:43 +0900 Subject: [PATCH 1314/1472] update builds to StdEnv 2023 --- build/cmake/build.cluster.bash | 9 +++++---- build/cmake/build_actors.cluster.bash | 9 +++++---- build/cmake/build_ngen.cluster.bash | 9 +++++---- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/build/cmake/build.cluster.bash b/build/cmake/build.cluster.bash index dc985bd38..3c47adbe3 100755 --- a/build/cmake/build.cluster.bash +++ b/build/cmake/build.cluster.bash @@ -2,10 +2,11 @@ # build on Copernicus or Graham, from cmake directory run this as ./build.cluster.bash # for Summa -module load StdEnv/2020 -module load gcc/9.3.0 -module load openblas/0.3.17 -module load netcdf-fortran/4.5.2 +module load StdEnv/2023 +module load gcc/12.3 +module load openblas/0.3.24 +module load openmpi/4.1.5 +module load netcdf-fortran/4.6.1 export FLAGS_OPT="-flto=1;-fuse-linker-plugin" diff --git a/build/cmake/build_actors.cluster.bash b/build/cmake/build_actors.cluster.bash index 6046e7840..fb145e895 100755 --- a/build/cmake/build_actors.cluster.bash +++ b/build/cmake/build_actors.cluster.bash @@ -2,10 +2,11 @@ # build on Copernicus or Graham, from cmake directory run this as ./build_actors.cluster.bash # for Summa -module load StdEnv/2020 -module load gcc/9.3.0 -module load openblas/0.3.17 -module load netcdf-fortran/4.5.2 +module load StdEnv/2023 +module load gcc/12.3 +module load openblas/0.3.24 +module load openmpi/4.1.5 +module load netcdf-fortran/4.6.1 # for Actors module load caf diff --git a/build/cmake/build_ngen.cluster.bash b/build/cmake/build_ngen.cluster.bash index 745464b8d..e05ebdab2 100755 --- a/build/cmake/build_ngen.cluster.bash +++ b/build/cmake/build_ngen.cluster.bash @@ -2,10 +2,11 @@ # build nextgen on Copernicus, from ngen directory put this one directory up and run this as ../build_ngen.cluster.bash # for Summa -module load StdEnv/2020 -module load gcc/9.3.0 -module load openblas/0.3.17 -module load netcdf-fortran/4.5.2 +module load StdEnv/2023 +module load gcc/12.3 +module load openblas/0.3.24 +module load openmpi/4.1.5 +module load netcdf-fortran/4.6.1 export FLAGS_OPT="-flto=1;-fuse-linker-plugin" From 440e5f7aeecd836218c46c412bdc17266ae3525e Mon Sep 17 00:00:00 2001 From: Kyle Klenk Date: Wed, 22 May 2024 15:15:17 -0600 Subject: [PATCH 1315/1472] Set up parameters controled by summa param txt file for sundials solver --- build/source/dshare/get_ixname.f90 | 6 ++ build/source/dshare/popMetadat.f90 | 6 ++ build/source/dshare/var_lookup.f90 | 8 +- build/source/engine/read_pinit.f90 | 105 +++++++++++++++---------- build/source/engine/summaSolve4ida.f90 | 98 ++++++++++++----------- 5 files changed, 134 insertions(+), 89 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 2a2ff3d0c..1fe13c266 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -405,6 +405,12 @@ function get_ixParam(varName) case('absTolMatric' ); get_ixParam = iLookPARAM%absTolMatric ! absolute error tolerance for matric head case('relTolAquifr' ); get_ixParam = iLookPARAM%relTolAquifr ! relative error tolerance for aquifer hydrology case('absTolAquifr' ); get_ixParam = iLookPARAM%absTolAquifr ! absolute error tolerance for aquifer hydrology + case('idaMaxOrder' ); get_ixParam = iLookPARAM%idaMaxOrder ! maximum order for IDA + case('idaMaxInternalSteps' ); get_ixParam = iLookPARAM%idaMaxInternalSteps ! maximum number of internal steps for IDA before tout + case('idaInitStepSize' ); get_ixParam = iLookPARAM%idaInitStepSize ! initial step size for IDA + case('idaMinStepSize' ); get_ixParam = iLookPARAM%idaMinStepSize ! minimum step size for IDA + case('idaMaxStepSize' ); get_ixParam = iLookPARAM%idaMaxStepSize ! maximum step size for IDA + case('idaMaxErrTestFail' ); get_ixParam = iLookPARAM%idaMaxErrTestFail ! maximum number of error test failures for IDA case('zmin' ); get_ixParam = iLookPARAM%zmin ! minimum layer depth (m) case('zmax' ); get_ixParam = iLookPARAM%zmax ! maximum layer depth (m) case('zminLayer1' ); get_ixParam = iLookPARAM%zminLayer1 ! minimum layer depth for the 1st (top) layer (m) diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 7af003eba..b56437314 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -281,6 +281,12 @@ subroutine popMetadat(err,message) mpar_meta(iLookPARAM%absTolMatric) = var_info('absTolMatric' , 'absolute error tolerance for matric head' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%relTolAquifr) = var_info('relTolAquifr' , 'relative error tolerance for aquifer hydrology' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%absTolAquifr) = var_info('absTolAquifr' , 'absolute error tolerance for aquifer hydrology' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%idaMaxOrder) = var_info('idaMaxOrder' , 'maximum order for IDA' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%idaMaxInternalSteps) = var_info('idaMaxInternalSteps' , 'maximum number of internal steps for IDA before tout' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%idaInitStepSize) = var_info('idaInitStepSize' , 'initial step size for IDA' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%idaMinStepSize) = var_info('idaMinStepSize' , 'minimum step size for IDA' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%idaMaxStepSize) = var_info('idaMaxStepSize' , 'maximum step size for IDA' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%idaMaxErrTestFail) = var_info('idaMaxErrTestFail' , 'maximum number of error test failures for IDA' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%zmin) = var_info('zmin' , 'minimum layer depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%zmax) = var_info('zmax' , 'maximum layer depth' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%zminLayer1) = var_info('zminLayer1' , 'minimum layer depth for the 1st (top) layer' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 46dab0ad3..28a770e7e 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -310,6 +310,12 @@ MODULE var_lookup integer(i4b) :: absTolMatric = integerMissing ! absolute error tolerance for matric head integer(i4b) :: relTolAquifr = integerMissing ! relative error tolerance for aquifer hydrology integer(i4b) :: absTolAquifr = integerMissing ! absolute error tolerance for aquifer hydrology + integer(i4b) :: idaMaxOrder = integerMissing ! maximum order for IDA + integer(i4b) :: idaMaxInternalSteps = integerMissing ! maximum number of internal steps for IDA before tout + integer(i4b) :: idaInitStepSize = integerMissing ! initial step size for IDA + integer(i4b) :: idaMinStepSize = integerMissing ! minimum step size for IDA + integer(i4b) :: idaMaxStepSize = integerMissing ! maximum step size for IDA + integer(i4b) :: idaMaxErrTestFail = integerMissing ! maximum number of error test failures for IDA integer(i4b) :: zmin = integerMissing ! minimum layer depth (m) integer(i4b) :: zmax = integerMissing ! maximum layer depth (m) integer(i4b) :: zminLayer1 = integerMissing ! minimum layer depth for the 1st (top) layer (m) @@ -912,7 +918,7 @@ MODULE var_lookup 141,142,143,144,145,146,147,148,149,150,& 151,152,153,154,155,156,157,158,159,160,& 161,162,163,164,165,166,167,168,169,170,& - 171,172,173,174) + 171,172,173,174,175,176,177,178,179,180) ! named variables: model prognostic (state) variables type(iLook_prog), public,parameter :: iLookPROG =iLook_prog ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& diff --git a/build/source/engine/read_pinit.f90 b/build/source/engine/read_pinit.f90 index 7231492cb..a9dd7217a 100644 --- a/build/source/engine/read_pinit.f90 +++ b/build/source/engine/read_pinit.f90 @@ -135,48 +135,10 @@ subroutine read_pinit(filenm,isLocal,mpar_meta,parFallback,err,message) if (parFallback(iLookPARAM%be_steps)%default_val < 0.99_rkind*realMissing) then parFallback(iLookPARAM%be_steps)%default_val = 1._rkind end if - if (parFallback(iLookPARAM%relTolTempCas)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%relTolTempCas)%default_val = 1.e-6_rkind - end if - if (parFallback(iLookPARAM%absTolTempCas)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%absTolTempCas)%default_val = 1.e-6_rkind - end if - if (parFallback(iLookPARAM%relTolTempVeg)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%relTolTempVeg)%default_val = 1.e-6_rkind - end if - if (parFallback(iLookPARAM%absTolTempVeg)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%absTolTempVeg)%default_val = 1.e-6_rkind - end if - if (parFallback(iLookPARAM%relTolWatVeg)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%relTolWatVeg)%default_val = 1.e-6_rkind - end if - if (parFallback(iLookPARAM%absTolWatVeg)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%absTolWatVeg)%default_val = 1.e-6_rkind - end if - if (parFallback(iLookPARAM%relTolTempSoilSnow)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%relTolTempSoilSnow)%default_val = 1.e-6_rkind - end if - if (parFallback(iLookPARAM%absTolTempSoilSnow)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%absTolTempSoilSnow)%default_val = 1.e-6_rkind - end if - if (parFallback(iLookPARAM%relTolWatSnow)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%relTolWatSnow)%default_val = 1.e-6_rkind - end if - if (parFallback(iLookPARAM%absTolWatSnow)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%absTolWatSnow)%default_val = 1.e-6_rkind - end if - if (parFallback(iLookPARAM%relTolMatric)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%relTolMatric)%default_val = 1.e-6_rkind - end if - if (parFallback(iLookPARAM%absTolMatric)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%absTolMatric)%default_val = 1.e-6_rkind - end if - if (parFallback(iLookPARAM%relTolAquifr)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%relTolAquifr)%default_val = 1.e-6_rkind - end if - if (parFallback(iLookPARAM%absTolAquifr)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%absTolAquifr)%default_val = 1.e-6_rkind - end if + + call set_ida_defaults(parFallback, err, cmessage) + if (err /= 0) then; message = trim(message)//trim(cmessage); return; end if + end if ! check we have populated all variables @@ -203,5 +165,64 @@ subroutine read_pinit(filenm,isLocal,mpar_meta,parFallback,err,message) close(unt) end subroutine read_pinit + ! ************************************************************************************************ + ! Subroutine to separate the default settings of the IDA solver from the rest of the model parameters + ! ************************************************************************************************ + subroutine set_ida_defaults(parFallback, err, message) + USE data_types,only:par_info ! data type for parameter constraints + implicit none + ! define input + type(par_info),intent(out) :: parFallback(:) ! default values and constraints of model parameters + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local varaibles + integer(i4b) :: i + real(rkind) :: default_relTol = 1.e-6_rkind + real(rkind) :: default_absTol = 1.e-6_rkind + integer(i4b), dimension(7) :: relTol_paramIndx = [iLookPARAM%relTolTempCas, iLookPARAM%relTolTempVeg, iLookPARAM%relTolWatVeg, & + iLookPARAM%relTolTempSoilSnow, iLookPARAM%relTolWatSnow, iLookPARAM%relTolMatric, & + iLookPARAM%relTolAquifr] + integer(i4b), dimension(7) :: absTol_paramIndx = [iLookPARAM%absTolTempCas, iLookPARAM%absTolTempVeg, iLookPARAM%absTolWatVeg, & + iLookPARAM%absTolTempSoilSnow, iLookPARAM%absTolWatSnow, iLookPARAM%absTolMatric, & + iLookPARAM%absTolAquifr] + message="set_ida_defaults/" + + ! Relative Tolerances + do i = 1, size(relTol_paramIndx) + if (parFallback(relTol_paramIndx(i))%default_val < 0.99_rkind*realMissing) then + parFallback(relTol_paramIndx(i))%default_val = default_relTol + end if + end do + + ! Absolute Tolerances + do i = 1, size(absTol_paramIndx) + if (parFallback(absTol_paramIndx(i))%default_val < 0.99_rkind*realMissing) then + parFallback(absTol_paramIndx(i))%default_val = default_absTol + end if + end do + + ! IDA Solver Parameters + if (parFallback(iLookPARAM%idaMaxOrder)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%idaMaxOrder)%default_val = 5 + end if + if (parFallback(iLookPARAM%idaMaxInternalSteps)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%idaMaxInternalSteps)%default_val = 500 + end if + if (parFallback(iLookPARAM%idaInitStepSize)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%idaInitStepSize)%default_val = 0 + end if + if (parFallback(iLookPARAM%idaMinStepSize)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%idaMinStepSize)%default_val = 0 + end if + if (parFallback(iLookPARAM%idaMaxStepSize)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%idaMaxStepSize)%default_val = 0 ! 0 means ida's default ofinfinity + end if + if (parFallback(iLookPARAM%idaMaxErrTestFail)%default_val < 0.99_rkind*realMissing) then + parFallback(iLookPARAM%idaMaxErrTestFail)%default_val = 10 ! IDA default is 10 + end if + end subroutine set_ida_defaults + + + end module read_pinit_module diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index cfd263871..d833e4461 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -446,7 +446,7 @@ subroutine summaSolve4ida(& if (retval /= 0) then; err=20; message=trim(message)//'error in FIDASetStopTime'; return; endif ! Set solver parameters at end of setup - call setSolverParams(dt_cur, ida_mem, retval) + call setSolverParams(dt_cur, eqns_data%mpar_data, ida_mem, retval) if (retval /= 0) then; err=20; message=trim(message)//'error in setSolverParams'; return; endif ! Disable error messages and warnings @@ -641,9 +641,6 @@ subroutine summaSolve4ida(& diag_data%var(iLookDIAG%hCur)%dat(1) = hCur(1) diag_data%var(iLookDIAG%tCur)%dat(1) = tCur(1) - - - call FIDAFree(ida_mem) retval = FSUNLinSolFree(sunlinsol_LS) if(retval /= 0)then; err=20; message=trim(message)//'unable to free the linear solver'; return; endif @@ -692,61 +689,70 @@ end subroutine setInitialCondition ! ---------------------------------------------------------------- ! setSolverParams: private routine to set parameters in IDA solver ! ---------------------------------------------------------------- -subroutine setSolverParams(dt_cur,ida_mem,retval) +subroutine setSolverParams(dt_cur,mpar_data,ida_mem,retval) !======= Inclusions =========== USE, intrinsic :: iso_c_binding USE fida_mod ! Fortran interface to IDA - + USE data_types,only:var_dlength !======= Declarations ========= implicit none ! calling variables - real(rkind),intent(in) :: dt_cur ! current whole time step - type(c_ptr),intent(inout) :: ida_mem ! IDA memory - integer(i4b),intent(out) :: retval ! return value + real(rkind),intent(in) :: dt_cur ! current whole time step + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(c_ptr),intent(inout) :: ida_mem ! IDA memory + integer(i4b),intent(out) :: retval ! return value !======= Internals ============ integer,parameter :: nonlin_iter = 4 ! maximum number of nonlinear iterations before reducing step size, default = 4 - integer,parameter :: max_order = 5 ! maximum BDF order, default and max = 5 real(qp),parameter :: coef_nonlin = 0.33 ! coefficient in the nonlinear convergence test, default = 0.33 - integer(c_long),parameter :: max_step = 999999 ! maximum number of steps, default = 500 integer,parameter :: fail_iter = 50 ! maximum number of error test and convergence test failures, default 10 - real(qp) :: h_max ! maximum stepsize, default = infinity - real(qp),parameter :: h_init = 3600 ! initial stepsize - - ! Set the maximum BDF order - retval = FIDASetMaxOrd(ida_mem, max_order) - if (retval /= 0) return - - ! Set coefficient in the nonlinear convergence test - retval = FIDASetNonlinConvCoef(ida_mem, coef_nonlin) - if (retval /= 0) return - - ! Set maximun number of nonliear iterations, maybe should just make 4 (instead of SUMMA parameter) - retval = FIDASetMaxNonlinIters(ida_mem, nonlin_iter) - if (retval /= 0) return - - ! Set maximum number of convergence test failures - retval = FIDASetMaxConvFails(ida_mem, fail_iter) - if (retval /= 0) return - - ! Set maximum number of error test failures - retval = FIDASetMaxErrTestFails(ida_mem, fail_iter) - if (retval /= 0) return - - ! Set maximum number of steps - retval = FIDASetMaxNumSteps(ida_mem, max_step) - if (retval /= 0) return - - ! Set maximum stepsize - h_max = dt_cur - retval = FIDASetMaxStep(ida_mem, h_max) - if (retval /= 0) return - - ! Set initial stepsize - retval = FIDASetInitStep(ida_mem, h_init) - if (retval /= 0) return + + associate(& + max_order => mpar_data%var(iLookPARAM%idaMaxOrder)%dat(1), & ! maximum BDF order + max_err_test_fail => mpar_data%var(iLookPARAM%idaMaxErrTestFail)%dat(1), & ! maximum number of error test failures + max_steps => mpar_data%var(iLookPARAM%idaMaxInternalSteps)%dat(1), & ! maximum number of steps + h_init => mpar_data%var(iLookPARAM%idaInitStepSize)%dat(1), & ! initial stepsize + h_min => mpar_data%var(iLookPARAM%idaMinStepSize)%dat(1) & ! minimum stepsize + ) + + ! Set the maximum BDF order + retval = FIDASetMaxOrd(ida_mem, int(max_order)) + if (retval /= 0) return + + ! Set coefficient in the nonlinear convergence test + retval = FIDASetNonlinConvCoef(ida_mem, coef_nonlin) + if (retval /= 0) return + + ! Set maximun number of nonliear iterations, maybe should just make 4 (instead of SUMMA parameter) + retval = FIDASetMaxNonlinIters(ida_mem, nonlin_iter) + if (retval /= 0) return + + ! Set maximum number of convergence test failures + retval = FIDASetMaxConvFails(ida_mem, fail_iter) + if (retval /= 0) return + + ! Set maximum number of error test failures + retval = FIDASetMaxErrTestFails(ida_mem, int(max_err_test_fail)) + if (retval /= 0) return + + ! Set maximum number of steps + retval = FIDASetMaxNumSteps(ida_mem, int(max_steps, kind=8)) + if (retval /= 0) return + + ! Set maximum stepsize + retval = FIDASetMaxStep(ida_mem, dt_cur) + if (retval /= 0) return + + ! Set initial stepsize + retval = FIDASetInitStep(ida_mem, h_init) + if (retval /= 0) return + + ! Set minimum stepsize + retval = FIDASetMinStep(ida_mem, h_min) + if (retval /= 0) return + end associate ! end association to variables in the data structure end subroutine setSolverParams From 6ccaaa4c10b1d733c157bbdcf70bfb3c18a3b643 Mon Sep 17 00:00:00 2001 From: Kyle Klenk Date: Thu, 23 May 2024 16:42:04 -0600 Subject: [PATCH 1316/1472] Reworked CMakeList.txt files to avoid hardcoded paths --- build/CMakeLists.txt | 254 +++++++++++ build/cmake/CMakeLists.txt | 624 --------------------------- build/cmake/FindNetCDF.cmake | 27 ++ build/cmake/FindOpenBLAS.cmake | 14 + build/cmake/build.pc.bash | 26 +- build/source/CMakeLists.txt | 247 +++++++++++ build/source/engine/enthalpyTemp.f90 | 1 - 7 files changed, 544 insertions(+), 649 deletions(-) create mode 100644 build/CMakeLists.txt delete mode 100644 build/cmake/CMakeLists.txt create mode 100644 build/cmake/FindNetCDF.cmake create mode 100644 build/cmake/FindOpenBLAS.cmake create mode 100644 build/source/CMakeLists.txt diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt new file mode 100644 index 000000000..d0dbba1a8 --- /dev/null +++ b/build/CMakeLists.txt @@ -0,0 +1,254 @@ +cmake_minimum_required(VERSION 3.21 FATAL_ERROR) +project(summa VERSION 4.0.0 LANGUAGES Fortran C CXX) + +if(DEFINED ENV{FC}) + set (CMAKE_Fortran_COMPILER $ENV{FC}) #for NexGen need, works for others +else() + set (CMAKE_Fortran_COMPILER gfortran) +endif() +#========================================================================================= +# When compiling Summa each configuration can be compiled in a Cluster or Debug mode, +# which is set by the CMAKE_BUILD_TYPES variables. The default is BE, release mode no +# cluster no Sundials no Actors no NexGen which runs the code with BE based off Numerical +# Recipes only. +# Note, Sundials, NexGen, and Actors will all require extra libraries to be downloaded +# and compiled. Currently, NexGen cannot work with Actors. +# If you install Sundials, you can compile Summa with Sundials, which allows you to run +# Summa with a solver of BE based off Numerical Recipes, Kinsol, or IDA. The solver choice +# is then set in the code decisions file. +# Other options include how to drive the code, NexGen (with BMI) or Actors (without BMI) +# framework or no extra framework (without BMI). +# If you change the build type, you will need to delete the CMakeCache.txt file and +# rerun cmake. +#========================================================================================= + + +# Add options for build type +set(CMAKE_CONFIGURATION_TYPES Release Debug) +message("\nSelected Bulid Type: ${CMAKE_BUILD_TYPE}\n") + + +# Options: Enable each with cmake -DOPTION=ON +option(USE_SUNDIALS "Use IDA solver from sundilas suite" OFF) +option(USE_ACTORS "Use Actors Framework" OFF) +option(USE_NEXGEN "Use NexGen Framework" OFF) +set(EXT_TARGETS) # list of external targets to link to + +# Set Default Executable Name +set(EXEC_NAME summa.exe) +# Set top-level directory +set(F_MASTER "${CMAKE_CURRENT_SOURCE_DIR}/..") +set(PARENT_DIR "${F_MASTER}") +set(EXEC_DIR "${F_MASTER}/bin") + +# Handle optional external libraries +if (USE_SUNDIALS) + message("ENABLING SUNDIALS") + find_package(SUNDIALS REQUIRED) + list(APPEND EXT_TARGETS SUNDIALS::fida_mod_shared + SUNDIALS::fkinsol_mod_shared) + add_compile_definitions(SUNDIALS_ACTIVE) + set(EXEC_NAME summa_sundials.exe) +endif() + +if (USE_ACTORS) + message("ENABLING ACTORS") + find_package(CAF 0.18.3 QUIET) # Cluster has 0.18.3, but we can use 0.18.6 + if(NOT CAF_FOUND) + find_package(CAF 0.18.6 REQUIRED) + endif() + list(APPEND EXT_TARGETS CAF::core CAF::io) + add_compile_definitions(ACTORS_ACTIVE V4_ACTIVE) + + include(FortranCInterface) + FortranCInterface_VERIFY(CXX) + + set(PARENT_DIR "${F_MASTER}/..") + set(EXEC_DIR "${PARENT_DIR}/../bin") + set(EXEC_NAME summa_actors.exe) +endif() + +if (USE_NEXTGEN) + message("ENABLING NEXGEN") + if (USE_ACTORS) + message(FATAL_ERROR "NexGen incompatible with Summa-Actors") + endif() + add_compile_definitions(NGEN_ACTIVE BMI_ACTIVE NGEN_FORCING_ACTIVE + NGEN_OUTPUT_ACTIVE) + + add_subdirectory(../iso_c_fortran_bmi ${CMAKE_BINARY_DIR}/iso_c_bmi) + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../../cmake/") + set( CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran) + set(SUMMA_LIB_NAME_CMAKE summabmi) + set(SUMMA_LIB_DESC_CMAKE "Summa-Sundials BMI Module Shared Library") +endif() + +get_filename_component(F_MASTER "${F_MASTER}" REALPATH) +get_filename_component(PARENT_DIR "${PARENT_DIR}" REALPATH) +get_filename_component(EXEC_DIR "${EXEC_DIR}" REALPATH) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXEC_DIR}) + +#========================================================================================= +# SET THE PATHS TO THE LIBRARIES AND INCLUDE FILES +#========================================================================================= + +# NetCDF is found with a custom FindNetCDF.cmake file +LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/") +LIST(APPEND CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/cmake/") +find_package(NetCDF REQUIRED) +list(APPEND EXT_TARGETS NetCDF::NetCDF) + +# OpenBLAS +set(BLA_VENDOR OpenBLAS) # MKL +find_package(OpenBLAS REQUIRED) +list(APPEND EXT_TARGETS OpenBLAS::OpenBLAS) + +# Set compiler flags +set(FLAGS_OPT $ENV{FLAGS_OPT}) # get optional user-specified flags from environment variables +if(CMAKE_BUILD_TYPE MATCHES Debug) + message("\nSetting Debug Options") + add_compile_definitions(DEBUG) + set(FLAGS_NOAH -g -O0 -fbacktrace -fbounds-check -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) + set(FLAGS_ALL -g -O0 -fbacktrace -fbounds-check -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp ${FLAGS_OPT}) + set(FLAGS_CXX -g -O0 -fbounds-check -Wfatal-errors -std=c++17 ${FLAGS_OPT}) +else() + message("\nSetting Release Options") + set(FLAGS_NOAH -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) + set(FLAGS_ALL -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp ${FLAGS_OPT}) + set(FLAGS_CXX -O3 -Wfatal-errors -std=c++17 ${FLAGS_OPT}) +endif() + +#========================================================================================= +# COMPILE PART 1: Define directory paths +#========================================================================================= + +# Define directories that contains source code +set(DRIVER_DIR ${F_MASTER}/build/source/driver) +set(DSHARE_DIR ${F_MASTER}/build/source/dshare) +set(ENGINE_DIR ${F_MASTER}/build/source/engine) +set(HOOKUP_DIR ${F_MASTER}/build/source/hookup) +set(NETCDF_DIR ${F_MASTER}/build/source/netcdf) +set(NOAHMP_DIR ${F_MASTER}/build/source/noah-mp) + +# Define Actors specific directories +set(ACTORS_DIR ${PARENT_DIR}/source) +set(SYS_INIT_DIR ${ACTORS_DIR}/system_initialization) +set(FILE_ACCESS_DIR ${ACTORS_DIR}/file_access_actor) +set(JOB_ACTOR_DIR ${ACTORS_DIR}/job_actor) +set(GRU_ACTOR_DIR ${ACTORS_DIR}/gru_actor) +set(HRU_ACTOR_DIR ${ACTORS_DIR}/hru_actor) + +#========================================================================================= +# COMPILE PART 2: Assemble all of the SUMMA sub-routines +#========================================================================================= + +# SUMMA Source Files are defined in the CMakeLists.txt file in the subdirectory +add_subdirectory(${F_MASTER}/build/source/) + + +#========================================================================================= +# COMPILE PART 3: Collect the subroutines into build groups depending on build type +#========================================================================================= + +set(COMM_ALL ${NRPROC} ${HOOKUP} ${DATAMS} ${UTILMS}) + +set(SUMMA_ALL ${NETCDF} ${PRELIM} ${MODRUN} ${SOLVER} ${DRIVER}) + +if (USE_ACTORS) + set(COMM_ALL ${COMM_ALL} ${DATAMS_ACTORS} ${INTERFACE}) + set(SUMMA_ALL ${SUMMA_ALL} ${SYS_INIT_INTERFACE} ${FILE_ACCESS_INTERFACE} + ${JOB_INTERFACE} ${GRU_INTERFACE} ${HRU_INTERFACE}) + set(MAIN_SUMMA ${ACTORS_DIR}/main.cpp) +else() + set(SUMMA_ALL ${SUMMA_ALL} ${PRELIM_NOT_ACTORS} ${MODRUN_NOT_ACTORS} + ${SOLVER_NOT_ACTORS} ${DRIVER_NOT_ACTORS}) + set(MAIN_SUMMA ${DRIVER_DIR}/summa_driver.f90) +endif() + +if (USE_NEXGEN) + set(SUMMA_ALL ${SUMMA_ALL} ${DRIVER_NEXGEN}) +endif() + +if (USE_SUNDIALS) + set(COMM_ALL ${COMM_ALL} ${DATAMS_SUNDIALS} ${UTILMS_SUNDIALS}) + set(SUMMA_ALL ${SUMMA_ALL} ${MODRUN_SUNDIALS} ${SOLVER_SUNDIALS}) +endif() + + +# Define version number, not working correctly +set(VERSIONFILE ${DRIVER_DIR}/summaversion.inc) +execute_process(COMMAND " ${GIT_EXECUTABLE} tag | tail -n 1" OUTPUT_VARIABLE VERSION) +execute_process(COMMAND "date" OUTPUT_VARIABLE BULTTIM) +execute_process(COMMAND " ${GIT_EXECUTABLE} describe --long --all --always | sed -e's/heads\///'" OUTPUT_VARIABLE GITBRCH) +execute_process(COMMAND " ${GIT_EXECUTABLE} rev-parse HEAD" OUTPUT_VARIABLE GITHASH) + + +#========================================================================================= +# COMPILE PART 4: Do the compilation +#========================================================================================= + +# update version information, not working correctly +file(WRITE ${VERSIONFILE} "character(len=64), parameter :: summaVersion = '${VERSION}'\n") +file(APPEND ${VERSIONFILE} "character(len=64), parameter :: buildTime = ''\n") +file(APPEND ${VERSIONFILE} "character(len=64), parameter :: gitBranch = '${GITBRCH}'\n") +file(APPEND ${VERSIONFILE} "character(len=64), parameter :: gitHash = '${GITHASH}'") + +# Build SUMMA_NOAHMP Object +add_library(SUMMA_NOAHMP OBJECT ${NOAHMP} ${NRUTIL}) +target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) + +# Build SUMMA_COMM Object +add_library(SUMMA_COMM OBJECT ${COMM_ALL}) +target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) +# target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES}) +target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP ${EXT_TARGETS}) # added flags to the link step + +# For NexGen, build SUMMA Shared Library and add the outside BMI libraries +if(USE_NEXTGEN) + if(WIN32) + add_library(summabmi ${SUMMA_ALL}) + else() + add_library(summabmi SHARED ${SUMMA_ALL}) + endif() + target_compile_options(summabmi PRIVATE ${FLAGS_ALL}) + + target_link_libraries(summabmi PUBLIC iso_c_bmi) + set_target_properties(summabmi PROPERTIES VERSION ${PROJECT_VERSION}) + include(GNUInstallDirs) + + install(TARGETS summabmi + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + configure_file(summabmi.pc.in summabmi.pc @ONLY) + install(FILES ${CMAKE_BINARY_DIR}/summabmi.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) + +endif() + + +add_library(summa SHARED ${SUMMA_ALL}) +target_compile_options(summa PRIVATE ${FLAGS_ALL}) +target_link_libraries(summa PUBLIC ${EXT_TARGETS} SUMMA_NOAHMP SUMMA_COMM) + +# For Actors, build SUMMA Shared Library add the actors libraries and add the executable +if(USE_ACTORS) + add_executable(${EXEC_NAME} ${MAIN_SUMMA} ${ACTORS_GLOBAL} + ${FILE_ACCESS_ACTOR} ${JOB_ACTOR} ${GRU_ACTOR} ${HRU_ACTOR} + ${SYS_INIT} ${SUMMA_CLIENT} ${SUMMA_SERVER}) + + set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) + target_compile_options(${EXEC_NAME} PUBLIC ${FLAGS_CXX}) + target_include_directories(${EXEC_NAME} PUBLIC + ${PARENT_DIR}/includes/global + ${PARENT_DIR}/includes/summa_actor + ${PARENT_DIR}/includes/job_actor + ${PARENT_DIR}/includes/file_access_actor + ${PARENT_DIR}/includes/gru_actor + ${PARENT_DIR}/includes/hru_actor) + target_link_libraries( ${EXEC_NAME} ${EXT_TARGETS} summa) + +# For other cases, build SUMMA Shared Library and add the executable +elseif(NOT USE_NEXTGEN) + add_executable(${EXEC_NAME} ${MAIN_SUMMA}) + set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) + target_link_libraries(${EXEC_NAME} summa ${EXT_TARGETS}) # added flags to the link step +endif() diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt deleted file mode 100644 index 50a0c11bd..000000000 --- a/build/cmake/CMakeLists.txt +++ /dev/null @@ -1,624 +0,0 @@ -cmake_minimum_required(VERSION 3.10 FATAL_ERROR) -enable_language(C Fortran) -set (CMAKE_Fortran_COMPILER $ENV{FC}) #for NexGen need, works for others -#set (CMAKE_Fortran_COMPILER gfortran) # for others may work - - -#========================================================================================= -# When compiling Summa each configuration can be compiled in a Cluster or Debug mode, -# which is set by the CMAKE_BUILD_TYPES variables. The default is BE, release mode no -# cluster no Sundials no Actors no NexGen which runs the code with BE based off Numerical -# Recipes only. -# Note, Sundials, NexGen, and Actors will all require extra libraries to be downloaded -# and compiled. Currently, NexGen cannot work with Actors. -# If you install Sundials, you can compile Summa with Sundials, which allows you to run -# Summa with a solver of BE based off Numerical Recipes, Kinsol, or IDA. The solver choice -# is then set in the code decisions file. -# Other options include how to drive the code, NexGen (with BMI) or Actors (without BMI) -# framework or no extra framework (without BMI). -# If you change the build type, you will need to delete the CMakeCache.txt file and -# rerun cmake. -#========================================================================================= - -# Set default configuration type to Release -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE BE) -endif() - -# Add options for build type -set(CMAKE_CONFIGURATION_TYPES BE BE_Debug BE_Cluster BE_Cluster_Debug - BE_NexGen BE_NexGen_Debug BE_NexGen_Cluster BE_NexGen_Cluster_Debug - BE_Actors BE_Actors_Debug BE_Actors_Cluster BE_Actors_Cluster_Debug - Sundials Sundials_Debug Sundials_Cluster Sundials_Cluster_Debug - Sundials_NexGen Sundials_NexGen_Debug Sundials_NexGen_Cluster Sundials_NexGen_Cluster_Debug - Sundials_Actors Sundials_Actors_Debug Sundials_Actors_Cluster Sundials_Actors_Cluster_Debug) -message("\nSelected Bulid Type: ${CMAKE_BUILD_TYPE}\n") - -# Give executable names to be backwards compatible with old versions -if(CMAKE_BUILD_TYPE MATCHES Sundials) - add_compile_definitions(SUNDIALS_ACTIVE) - set(EXEC_NAME summa_sundials.exe) -elseif(CMAKE_BUILD_TYPE MATCHES BE) - set(EXEC_NAME summa.exe) -endif() -if(CMAKE_BUILD_TYPE MATCHES Actors) - set(EXEC_NAME summa_actors) -endif() - -if(CMAKE_BUILD_TYPE MATCHES NexGen) - message("\nUsing NexGen Framework, should have been installed previously") - add_compile_definitions(NGEN_ACTIVE BMI_ACTIVE NGEN_FORCING_ACTIVE NGEN_OUTPUT_ACTIVE) - - project(summabmi VERSION 1.0.0 DESCRIPTION "Summa-Sundials-BE BMI Module Shared Library") - - # NexGen needs to have CMakeLists one directory above summa submodule GIT, - # at ${NexGen GIT directory}/extern/summa - set(F_MASTER ./summa/) # directory of summa source code - set(PARENT_DIR ${F_MASTER}) # directory of summa source code - - # Get the iso_c_fortran binding module to build as part of this build - add_subdirectory(../iso_c_fortran_bmi ${CMAKE_BINARY_DIR}/iso_c_bmi) - - list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../../cmake/") - set( CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran) - - #### Add variables for individual libraries that are used within the *.pc.in files - set(SUMMA_LIB_NAME_CMAKE summabmi) - set(SUMMA_LIB_DESC_CMAKE "Summa-Sundials BMI Module Shared Library") - -else() - set(F_MASTER ../../) # directory of summa source code - - if(CMAKE_BUILD_TYPE MATCHES Actors) - message("\nUsing Actors Framework, should have been installed previously") - add_compile_definitions(ACTORS_ACTIVE) - add_compile_definitions(V4_ACTIVE) - project(summaactors DESCRIPTION "Summa-Sundials-BE Actors") - - include(FortranCInterface) - FortranCInterface_VERIFY(CXX) - - # CMakeLists in ${summa-actors GIT directory}/build/summa/build/cmake - set(PARENT_DIR ${F_MASTER}../../) # directory of summa actors source code - set(EXEC_DIR ${PARENT_DIR}/Summa-Actors/bin) - - else() - project(summa DESCRIPTION "Summa-Sundials-BE") - - # CMakeLists in ${summa-actors GIT directory}/build/cmake - set(PARENT_DIR ${F_MASTER}) # directory of summa source code - set(EXEC_DIR ${F_MASTER}/bin) - - endif() - - # set the output directory for executables - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXEC_DIR}) - -endif() - -#========================================================================================= -# SET THE PATHS TO THE LIBRARIES AND INCLUDE FILES -#========================================================================================= - -# Set compiler flags -set(FLAGS_OPT $ENV{FLAGS_OPT}) # get optional user-specified flags from environment variables -if(CMAKE_BUILD_TYPE MATCHES Debug) - message("\nSetting Debug Options") - add_compile_definitions(DEBUG) - set(FLAGS_NOAH -g -O0 -fbacktrace -fbounds-check -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) - set(FLAGS_ALL -g -O0 -fbacktrace -fbounds-check -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp ${FLAGS_OPT}) - set(FLAGS_CXX -g -O0 -fbounds-check -Wfatal-errors -std=c++17 ${FLAGS_OPT}) -else() - message("\nSetting Release Options") - set(FLAGS_NOAH -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) - set(FLAGS_ALL -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp ${FLAGS_OPT}) - set(FLAGS_CXX -O3 -Wfatal-errors -std=c++17 ${FLAGS_OPT}) -endif() - -# Find Sundials if exists, add path here if FATAL_ERROR "Did not find Sundials directory" -if(CMAKE_BUILD_TYPE MATCHES Sundials) - set(DIR_SUNDIALS "") - foreach(dir IN ITEMS - $ENV{SUNDIALS_PATH}) #look in same head directory as summa or up to another installation - if(EXISTS ${dir}) - set(DIR_SUNDIALS ${dir}) - message("\nFound Sundials directory at ${DIR_SUNDIALS}") - break() - endif() - endforeach() - if(NOT DIR_SUNDIALS) - message(FATAL_ERROR "Did not find Sundials directory, edit CMakeLists.txt to add path") - endif() -endif() - -# Set libraries for all builds -if(CMAKE_BUILD_TYPE MATCHES Cluster) - message("\nBuilding for cluster") - - # MKL needs BLA_VENDOR set with cmake - set(BLA_VENDOR OpenBLAS) - - # Packages that are required - find_package(LAPACK REQUIRED) - - # Set include directories - set(INCLUDES $ENV{EBROOTNETCDFMINFORTRAN}/include ${netCDF_INCLUDES} ${LAPACK_INCLUDES}) - set(LIBRARIES SUMMA_NOAHMP ${netCDF_LIBRARIES} ${LAPACK_LIBRARIES} -lnetcdff -lopenblas) - - if(CMAKE_BUILD_TYPE MATCHES Sundials) - link_directories(${DIR_SUNDIALS}/lib64) - set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib64") - set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran) - endif() - - if(CMAKE_BUILD_TYPE MATCHES Actors) - find_package(CAF REQUIRED) - set(INC_ACTORS ${CAF_INCLUDES} ${PARENT_DIR}/build/includes/global - ${PARENT_DIR}/build/includes/summa_actor - ${PARENT_DIR}/build/includes/job_actor - ${PARENT_DIR}/build/includes/gru_actor - ${PARENT_DIR}/build/includes/file_access_actor - ${PARENT_DIR}/build/includes/hru_actor) - set(LIB_ACTORS ${CAF_LIBRARIES} -lcaf_core -lcaf_io -lnetcdf) - endif() - -else() - message("\nBuilding for personal computer") - - # Set Links and Libraries using Environment Variables - set(LINK_DIRS $ENV{LINK_DIRS}) # set link directories from environment variable - set(INCLUDES_DIRS $ENV{INCLUDES_DIRS}) # set directories for INCLUDES from environment variable - set(LIBRARY_LINKS $ENV{LIBRARY_LINKS}) # set list of library links from environment variable - link_directories(${LINK_DIRS}) - set(INCLUDES ${INCLUDES_DIRS}) - set(LIBRARIES SUMMA_NOAHMP ${LIBRARY_LINKS}) - - if(CMAKE_BUILD_TYPE MATCHES Sundials) - link_directories(${DIR_SUNDIALS}/lib) - set(CMAKE_BUILD_RPATH "${DIR_SUNDIALS}/lib") - set(INC_SUNDIALS ${DIR_SUNDIALS}/include ${DIR_SUNDIALS}/fortran) - endif() - - # If using on personal computer need to link directory-- not working on Mac right now to compile with Actors - if(CMAKE_BUILD_TYPE MATCHES Actors) - set(DIR_ACTORS "") - foreach(dir IN ITEMS - "${F_MASTER}../actor-framework/install" - "${PARENT_DIR}../actor-framework/install" - "${F_MASTER}../../SummaActors/actor-framework/install" - "${PARENT_DIR}../../SummaActors/actor-framework/install" - $ENV{ACTOR_FRAMEWORK_PATH} - "../../../../SummaActors/actor-framework/install") #look in same head directory as summa or up to another installation - if(EXISTS ${dir}) - set(DIR_ACTORS ${dir}) - message("\nFound Actors directory at ${DIR_ACTORS}") - break() - endif() - endforeach() - if(NOT DIR_ACTORS) - message(FATAL_ERROR "Did not find Actors directory, edit CMakeLists.txt to add path") - endif() - link_directories(${DIR_ACTORS}/lib) - set(INC_ACTORS ${DIR_ACTORS}/include - ${PARENT_DIR}/build/includes/global - ${PARENT_DIR}/build/includes/summa_actor - ${PARENT_DIR}/build/includes/job_actor - ${PARENT_DIR}/build/includes/file_access_actor - ${PARENT_DIR}/build/includes/gru_actor - ${PARENT_DIR}/build/includes/hru_actor) - set(LIB_ACTORS ${DIR_ACTORS}/lib -lcaf_core -lcaf_io -lnetcdf) - endif() - -endif() - - -if(CMAKE_BUILD_TYPE MATCHES Sundials) - message("\nUsing SUNDIALS libraries for IDA and Kinsol, should have been installed previously") - set(LIB_SUNDIALS -lsundials_fcore_mod - -lsundials_fnvecserial_mod - -lsundials_fsunlinsoldense_mod -lsundials_fsunmatrixdense_mod - -lsundials_fsunlinsolband_mod -lsundials_fsunmatrixband_mod - -lsundials_fsunnonlinsolnewton_mod - -lsundials_fida_mod -lsundials_fkinsol_mod) -elseif(CMAKE_BUILD_TYPE MATCHES BE) - message("\nUsing Backward Euler based off free versions of Numerical Recipes only") -else() - message("\nValid build types are: ${CMAKE_CONFIGURATION_TYPES}") - message(FATAL_ERROR "\nUnknown build type: ${CMAKE_BUILD_TYPE}") -endif() - -message("\nBuilding SUMMA\n") - - -#========================================================================================= -# COMPILE PART 1: Define directory paths -#========================================================================================= - -# Define directories that contains source code -set(DRIVER_DIR ${F_MASTER}/build/source/driver) -set(DSHARE_DIR ${F_MASTER}/build/source/dshare) -set(ENGINE_DIR ${F_MASTER}/build/source/engine) -set(HOOKUP_DIR ${F_MASTER}/build/source/hookup) -set(NETCDF_DIR ${F_MASTER}/build/source/netcdf) -set(NOAHMP_DIR ${F_MASTER}/build/source/noah-mp) - -# Define Actors specific directories -set(ACTORS_DIR ${PARENT_DIR}/build/source) -set(SYS_INIT_DIR ${ACTORS_DIR}/system_initialization) -set(FILE_ACCESS_DIR ${ACTORS_DIR}/file_access_actor) -set(JOB_ACTOR_DIR ${ACTORS_DIR}/job_actor) -set(GRU_ACTOR_DIR ${ACTORS_DIR}/gru_actor) -set(HRU_ACTOR_DIR ${ACTORS_DIR}/hru_actor) - -if(CMAKE_BUILD_TYPE MATCHES Actors) - # Define directories for source files that might be replaced by actors (identical if not using actors) - set(SUB_ENGINE_DIR ${FILE_ACCESS_DIR}) -else() - set(SUB_ENGINE_DIR ${PARENT_DIR}/build/source/engine) -endif() -#========================================================================================= -# COMPILE PART 2: Assemble all of the SUMMA sub-routines -#========================================================================================= - -# NOAHMP modules -set(NOAHMP - ${NOAHMP_DIR}/module_model_constants.F - ${NOAHMP_DIR}/module_sf_noahutl.F - ${NOAHMP_DIR}/module_sf_noahlsm.F - ${NOAHMP_DIR}/module_sf_noahmplsm.F) - -# Free versions of numerical recipes utilities for NOAH-MP modules -set(NRUTIL - ${ENGINE_DIR}/f2008funcs.f90 - ${ENGINE_DIR}/nr_utility.f90 - ${ENGINE_DIR}/nrtype.f90) - -# Free versions of numerical recipes procedures for SUMMA modules -set(NRPROC - ${ENGINE_DIR}/expIntegral.f90 - ${ENGINE_DIR}/spline_int.f90 - ${ENGINE_DIR}/hyp_2F1.f90) - -# Hook-up modules -set(HOOKUP - ${HOOKUP_DIR}/ascii_util.f90 - ${HOOKUP_DIR}/summaFileManager.f90) - -# Data modules -set(DATAMS - ${DSHARE_DIR}/data_types.f90 - ${DSHARE_DIR}/flxMapping.f90 - ${DSHARE_DIR}/get_ixname.f90 - ${DSHARE_DIR}/globalData.f90 - ${DSHARE_DIR}/multiconst.f90 - ${DSHARE_DIR}/outpt_stat.f90 - ${DSHARE_DIR}/popMetadat.f90 - ${DSHARE_DIR}/var_lookup.f90) -set(DATAMS_SUNDIALS - ${DSHARE_DIR}/type4ida.f90 - ${DSHARE_DIR}/type4kinsol.f90) -set(DATAMS_ACTORS - ${ACTORS_DIR}/global/actor_data_types.f90) - -# Utility modules -set(UTILMS - ${ENGINE_DIR}/matrixOper.f90 - ${ENGINE_DIR}/mDecisions.f90 - ${ENGINE_DIR}/snow_utils.f90 - ${ENGINE_DIR}/soil_utils.f90 - ${ENGINE_DIR}/time_utils.f90 - ${ENGINE_DIR}/updatState.f90) -set(UTILMS_SUNDIALS - ${ENGINE_DIR}/soil_utilsAddPrime.f90 - ${ENGINE_DIR}/updatStateWithPrime.f90) - -# NetCDF routines -set(NETCDF - ${NETCDF_DIR}/def_output.f90 - ${NETCDF_DIR}/modelwrite.f90 - ${NETCDF_DIR}/netcdf_util.f90 - ${NETCDF_DIR}/read_icond.f90) - -# Preliminary modules -set(PRELIM - ${ENGINE_DIR}/allocspace.f90 - ${ENGINE_DIR}/check_icond.f90 - ${ENGINE_DIR}/checkStruc.f90 - ${ENGINE_DIR}/childStruc.f90 - ${ENGINE_DIR}/conv_funcs.f90 - ${ENGINE_DIR}/ffile_info.f90 - ${ENGINE_DIR}/read_pinit.f90 - ${ENGINE_DIR}/read_attrb.f90 - ${ENGINE_DIR}/paramCheck.f90 - ${ENGINE_DIR}/pOverwrite.f90 - ${ENGINE_DIR}/sunGeomtry.f90 - ${ENGINE_DIR}/read_param.f90) - -# Model run support modules -set(MODRUN - ${ENGINE_DIR}/canopySnow.f90 - ${ENGINE_DIR}/derivforce.f90 - ${ENGINE_DIR}/enthalpyTemp.f90 - ${ENGINE_DIR}/getVectorz.f90 - ${ENGINE_DIR}/indexState.f90 - ${ENGINE_DIR}/layerMerge.f90 - ${ENGINE_DIR}/layerDivide.f90 - ${ENGINE_DIR}/qTimeDelay.f90 - ${ENGINE_DIR}/snowAlbedo.f90 - ${ENGINE_DIR}/snwCompact.f90 - ${ENGINE_DIR}/tempAdjust.f90 - ${ENGINE_DIR}/updateVars.f90 - ${ENGINE_DIR}/var_derive.f90 - ${ENGINE_DIR}/volicePack.f90) -set(MODRUN_NOT_ACTORS - ${ENGINE_DIR}/read_force.f90) -set(MODRUN_SUNDIALS - ${ENGINE_DIR}/tol4ida.f90 - ${ENGINE_DIR}/updateVarsWithPrime.f90) - -# Solver main modules -set(SOLVER - ${ENGINE_DIR}/bigAquifer.f90 - ${ENGINE_DIR}/computFlux.f90 - ${ENGINE_DIR}/computHeatCap.f90 - ${ENGINE_DIR}/computJacob.f90 - ${ENGINE_DIR}/computResid.f90 - ${ENGINE_DIR}/computSnowDepth.f90 - ${ENGINE_DIR}/computThermConduct.f90 - ${ENGINE_DIR}/coupled_em.f90 - ${ENGINE_DIR}/diagn_evar.f90 - ${ENGINE_DIR}/eval8summa.f90 - ${ENGINE_DIR}/groundwatr.f90 - ${ENGINE_DIR}/opSplittin.f90 - ${ENGINE_DIR}/snowLiqFlx.f90 - ${ENGINE_DIR}/soilLiqFlx.f90 - ${ENGINE_DIR}/ssdNrgFlux.f90 - ${ENGINE_DIR}/stomResist.f90 - ${ENGINE_DIR}/summaSolve4homegrown.f90 - ${ENGINE_DIR}/systemSolv.f90 - ${ENGINE_DIR}/varSubstep.f90 - ${ENGINE_DIR}/vegLiqFlux.f90 - ${ENGINE_DIR}/vegNrgFlux.f90 - ${ENGINE_DIR}/vegPhenlgy.f90 - ${ENGINE_DIR}/vegSWavRad.f90) -set(SOLVER_NOT_ACTORS - ${ENGINE_DIR}/run_oneGRU.f90 - ${ENGINE_DIR}/run_oneHRU.f90) -set(SOLVER_SUNDIALS - ${ENGINE_DIR}/computJacobWithPrime.f90 - ${ENGINE_DIR}/computResidWithPrime.f90 - ${ENGINE_DIR}/eval8summaWithPrime.f90 - ${ENGINE_DIR}/summaSolve4ida.f90 - ${ENGINE_DIR}/summaSolve4kinsol.f90) - -# Driver support modules -set(DRIVER - ${DRIVER_DIR}/summa_type.f90 - ${DRIVER_DIR}/summa_setup.f90 - ${DRIVER_DIR}/summa_restart.f90 - ${DRIVER_DIR}/summa_alarms.f90 - ${DRIVER_DIR}/summa_globalData.f90) -set(DRIVER_NOT_ACTORS - ${DRIVER_DIR}/summa_util.f90 - ${DRIVER_DIR}/summa_defineOutput.f90 - ${DRIVER_DIR}/summa_init.f90 - ${DRIVER_DIR}/summa_forcing.f90 - ${DRIVER_DIR}/summa_modelRun.f90 - ${DRIVER_DIR}/summa_writeOutput.f90) -set(DRIVER_NEXGEN - ${DRIVER_DIR}/summa_bmi.f90) - -# Actors interface modules -set(INTERFACE - ${ACTORS_DIR}/global/cppwrap_auxiliary.f90 - ${ACTORS_DIR}/global/cppwrap_datatypes.f90 - ${ACTORS_DIR}/global/cppwrap_metadata.f90 - ${ACTORS_DIR}/global/c_interface_module.f90) -set(SYS_INIT_INTERFACE - ${SYS_INIT_DIR}/batch_distributer_actor.f90) -set(FILE_ACCESS_INTERFACE - ${FILE_ACCESS_DIR}/summa_init_struc.f90 - ${FILE_ACCESS_DIR}/forcing_file_info.f90 - ${FILE_ACCESS_DIR}/file_access_actor.f90 - ${FILE_ACCESS_DIR}/output_structure.f90 - ${FILE_ACCESS_DIR}/fileAccess_writeOutput.f90) -set(JOB_INTERFACE - ${JOB_ACTOR_DIR}/gru_struc.f90) -set(GRU_INTERFACE - ${GRU_ACTOR_DIR}/gru_interface.f90) -set(HRU_INTERFACE - ${HRU_ACTOR_DIR}/hru_init.f90 - ${HRU_ACTOR_DIR}/hru_read.f90 - ${HRU_ACTOR_DIR}/hru_modelRun.f90 - ${HRU_ACTOR_DIR}/hru_writeOutput.f90 - ${HRU_ACTOR_DIR}/hru_interface.f90) - -# Actors actual actor modules -set(ACTORS_GLOBAL - ${ACTORS_DIR}/global/auxiliary.cpp - ${ACTORS_DIR}/global/fileManager.cpp - ${ACTORS_DIR}/global/settings_functions.cpp - ${ACTORS_DIR}/global/timing_info.cpp - ${ACTORS_DIR}/global/logger.cpp) -set(SYS_INIT - ${SYS_INIT_DIR}/batch.cpp - ${SYS_INIT_DIR}/batch_container.cpp - ${SYS_INIT_DIR}/client.cpp - ${SYS_INIT_DIR}/client_container.cpp - ${SYS_INIT_DIR}/summa_global_data.cpp - ${SYS_INIT_DIR}/summa_actor.cpp - ${SYS_INIT_DIR}/summa_backup_server.cpp - ${SYS_INIT_DIR}/summa_client.cpp - ${SYS_INIT_DIR}/summa_server.cpp) -set(FILE_ACCESS_ACTOR - ${FILE_ACCESS_DIR}/summa_init_struc.cpp - ${FILE_ACCESS_DIR}/file_access_actor.cpp - ${FILE_ACCESS_DIR}/forcing_file_info.cpp - ${FILE_ACCESS_DIR}/output_container.cpp) -set(JOB_ACTOR - ${JOB_ACTOR_DIR}/GRU.cpp - ${JOB_ACTOR_DIR}/gru_struc.cpp - ${JOB_ACTOR_DIR}/job_actor.cpp - ${JOB_ACTOR_DIR}/async_mode.cpp - ${JOB_ACTOR_DIR}/data_assimilation_mode.cpp - ${JOB_ACTOR_DIR}/job_utils.cpp - ${JOB_ACTOR_DIR}/distributed_job_actor.cpp - ${JOB_ACTOR_DIR}/node_actor.cpp) -set(GRU_ACTOR - ${GRU_ACTOR_DIR}/gru_actor.cpp) -set(HRU_ACTOR - ${HRU_ACTOR_DIR}/hru_utils.cpp - ${HRU_ACTOR_DIR}/hru_actor.cpp - ${HRU_ACTOR_DIR}/hru_batch_actor.cpp) - - -#========================================================================================= -# COMPILE PART 3: Collect the subroutines into build groups depending on build type -#========================================================================================= - -set(COMM_ALL - ${NRPROC} - ${HOOKUP} - ${DATAMS} - ${UTILMS}) -if(CMAKE_BUILD_TYPE MATCHES Actors) - set(COMM_ALL ${COMM_ALL} ${DATAMS_ACTORS} ${INTERFACE}) -endif() -if(CMAKE_BUILD_TYPE MATCHES Sundials) - set(COMM_ALL ${COMM_ALL} ${DATAMS_SUNDIALS} ${UTILMS_SUNDIALS}) -endif() - -set(SUMMA_ALL - ${NETCDF} - ${PRELIM} - ${MODRUN} - ${SOLVER} - ${DRIVER}) - -if(CMAKE_BUILD_TYPE MATCHES Actors) - set(SUMMA_ALL ${SUMMA_ALL} - ${SYS_INIT_INTERFACE} - ${FILE_ACCESS_INTERFACE} - ${JOB_INTERFACE} - ${GRU_INTERFACE} - ${HRU_INTERFACE}) -else() - set(SUMMA_ALL ${SUMMA_ALL} - ${PRELIM_NOT_ACTORS} - ${MODRUN_NOT_ACTORS} - ${SOLVER_NOT_ACTORS} - ${DRIVER_NOT_ACTORS}) -endif() -if(CMAKE_BUILD_TYPE MATCHES NexGen) - set(SUMMA_ALL ${SUMMA_ALL} - ${DRIVER_NEXGEN}) -endif() -if(CMAKE_BUILD_TYPE MATCHES Sundials) - set(SUMMA_ALL ${SUMMA_ALL} - ${MODRUN_SUNDIALS} - ${SOLVER_SUNDIALS}) -endif() - -# Run program files -set(MAIN_ACTOR ${ACTORS_DIR}/main.cpp) -#set(MAIN_BMI ${DRIVER_DIR}/summa_driver4bmi.f90) # do not need a driver in NexGen so do not use -set(MAIN_SUMMA ${DRIVER_DIR}/summa_driver.f90) - -# Define version number, not working correctly -set(VERSIONFILE ${DRIVER_DIR}/summaversion.inc) -execute_process(COMMAND " ${GIT_EXECUTABLE} tag | tail -n 1" OUTPUT_VARIABLE VERSION) -execute_process(COMMAND "date" OUTPUT_VARIABLE BULTTIM) -execute_process(COMMAND " ${GIT_EXECUTABLE} describe --long --all --always | sed -e's/heads\///'" OUTPUT_VARIABLE GITBRCH) -execute_process(COMMAND " ${GIT_EXECUTABLE} rev-parse HEAD" OUTPUT_VARIABLE GITHASH) - - -#========================================================================================= -# COMPILE PART 4: Do the compilation -#========================================================================================= - -# update version information, not working correctly -file(WRITE ${VERSIONFILE} "character(len=64), parameter :: summaVersion = '${VERSION}'\n") -file(APPEND ${VERSIONFILE} "character(len=64), parameter :: buildTime = ''\n") -file(APPEND ${VERSIONFILE} "character(len=64), parameter :: gitBranch = '${GITBRCH}'\n") -file(APPEND ${VERSIONFILE} "character(len=64), parameter :: gitHash = '${GITHASH}'") - -# Build SUMMA_NOAHMP Object -add_library(SUMMA_NOAHMP OBJECT ${NOAHMP} ${NRUTIL}) -target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) - -# Build SUMMA_COMM Object -add_library(SUMMA_COMM OBJECT ${COMM_ALL}) -target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) -target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES}) -target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP ${FLAGS_ALL}) # added flags to the link step - -# For NexGen, build SUMMA Shared Library and add the outside BMI libraries -if(CMAKE_BUILD_TYPE MATCHES NexGen) - if(WIN32) - add_library(summabmi ${SUMMA_ALL}) - else() - add_library(summabmi SHARED ${SUMMA_ALL}) - endif() - target_compile_options(summabmi PRIVATE ${FLAGS_ALL}) - - if(CMAKE_BUILD_TYPE MATCHES Sundials) - target_include_directories(summabmi PUBLIC ${INCLUDES} ${INC_SUNDIALS}) - target_link_libraries(summabmi PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_NOAHMP SUMMA_COMM) - else() - target_include_directories(summabmi PUBLIC ${INCLUDES}) - target_link_libraries(summabmi PUBLIC ${LIBRARIES} SUMMA_NOAHMP SUMMA_COMM) - endif() - target_link_libraries(summabmi PUBLIC iso_c_bmi) - set_target_properties(summabmi PROPERTIES VERSION ${PROJECT_VERSION}) - include(GNUInstallDirs) - - install(TARGETS summabmi - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - configure_file(summabmi.pc.in summabmi.pc @ONLY) - install(FILES ${CMAKE_BINARY_DIR}/summabmi.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) - -# For Actors, build SUMMA Shared Library add the actors libraries and add the executable -elseif(CMAKE_BUILD_TYPE MATCHES Actors) - add_library(summaactors SHARED ${SUMMA_ALL}) - target_compile_options(summaactors PRIVATE ${FLAGS_ALL}) - - if(CMAKE_BUILD_TYPE MATCHES Sundials) - target_include_directories(summaactors PUBLIC ${INCLUDES} ${INC_SUNDIALS}) - target_link_libraries(summaactors PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_NOAHMP SUMMA_COMM) - else() - target_include_directories(summaactors PUBLIC ${INCLUDES}) - target_link_libraries(summaactors PUBLIC ${LIBRARIES} SUMMA_NOAHMP SUMMA_COMM) - endif() - - add_executable(${EXEC_NAME} ${MAIN_ACTOR} - ${ACTORS_GLOBAL} - ${FILE_ACCESS_ACTOR} - ${JOB_ACTOR} - ${GRU_ACTOR} - ${HRU_ACTOR} - ${SYS_INIT} - ${SUMMA_CLIENT} - ${SUMMA_SERVER}) - set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) - target_compile_options(${EXEC_NAME} PUBLIC ${FLAGS_CXX}) - target_include_directories(${EXEC_NAME} PUBLIC ${INC_ACTORS}) - target_link_libraries( ${EXEC_NAME} ${LIB_ACTORS} summaactors) - -# For other cases, build SUMMA Shared Library and add the executable -else() - add_library(summa SHARED ${SUMMA_ALL}) - target_compile_options(summa PRIVATE ${FLAGS_ALL}) - - if(CMAKE_BUILD_TYPE MATCHES Sundials) - target_include_directories(summa PUBLIC ${INCLUDES} ${INC_SUNDIALS}) - target_link_libraries(summa PUBLIC ${LIBRARIES} ${LIB_SUNDIALS} SUMMA_NOAHMP SUMMA_COMM) - else() - target_include_directories(summa PUBLIC ${INCLUDES}) - target_link_libraries(summa PUBLIC ${LIBRARIES} SUMMA_NOAHMP SUMMA_COMM ${FLAGS_ALL}) # added flags to the link step - endif() - - add_executable(${EXEC_NAME} ${MAIN_SUMMA}) - set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) - target_link_libraries(${EXEC_NAME} summa ${FLAGS_ALL}) # added flags to the link step -endif() diff --git a/build/cmake/FindNetCDF.cmake b/build/cmake/FindNetCDF.cmake new file mode 100644 index 000000000..bba1a44a4 --- /dev/null +++ b/build/cmake/FindNetCDF.cmake @@ -0,0 +1,27 @@ +include(FindPackageHandleStandardArgs) + +# Find the NetCDF C library and include directory +find_library(NetCDF_C_LIBRARY NAMES netcdf) +find_path(NetCDF_C_INCLUDE_DIR NAMES netcdf.h) + +# Find the NetCDF Fortran library +find_library(NetCDF_F90_LIBRARY NAMES netcdff) + +set (NetCDF_LIBRARIES ${NetCDF_C_LIBRARY} ${NetCDF_F90_LIBRARY}) +set (NetCDF_INCLUDE_DIRS ${NetCDF_C_INCLUDE_DIR}) + +find_package_handle_standard_args(NetCDF DEFAULT_MSG + NetCDF_LIBRARIES NetCDF_INCLUDE_DIRS) + +if(NetCDF_FOUND) + mark_as_advanced (NetCDF_C_LIBRARY NetCDF_C_INCLUDE_DIR NetCDF_F90_LIBRARY NetCDF_DIR) + + add_library(NetCDF::NetCDF INTERFACE IMPORTED) + + set_target_properties(NetCDF::NetCDF PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${NetCDF_INCLUDE_DIRS}" + INTERFACE_LINK_LIBRARIES "${NetCDF_LIBRARIES}") + + message(STATUS "NetCDF incl for all components -- ${NetCDF_INCLUDE_DIRS}") + message(STATUS "NetCDF lib for all components -- ${NetCDF_LIBRARIES}") +endif() \ No newline at end of file diff --git a/build/cmake/FindOpenBLAS.cmake b/build/cmake/FindOpenBLAS.cmake new file mode 100644 index 000000000..38ad49332 --- /dev/null +++ b/build/cmake/FindOpenBLAS.cmake @@ -0,0 +1,14 @@ +# Find the OpenBLAS library +find_library(OpenBLAS_LIBRARY NAMES openblas) + +# Find the OpenBLAS include directory +find_path(OpenBLAS_INCLUDE_DIR NAMES cblas.h) + +if(OpenBLAS_LIBRARY AND OpenBLAS_INCLUDE_DIR) + add_library(OpenBLAS::OpenBLAS INTERFACE IMPORTED) + set_target_properties(OpenBLAS::OpenBLAS PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${OpenBLAS_INCLUDE_DIR}" + INTERFACE_LINK_LIBRARIES "${OpenBLAS_LIBRARY}") +else() + message(FATAL_ERROR "OpenBLAS not found") +endif() \ No newline at end of file diff --git a/build/cmake/build.pc.bash b/build/cmake/build.pc.bash index ef9262986..12d85f330 100755 --- a/build/cmake/build.pc.bash +++ b/build/cmake/build.pc.bash @@ -1,26 +1,4 @@ #!/bin/bash -# Build SUMMA on a PC using Bash, from cmake directory run this as ./build.pc.bash -# Environment variables that must be set for cmake: FC, LINK_DIRS, INCLUDES_DIRS, and LIBRARY_LINKS -# Environment variables may be set within this script (see examples below) or in the terminal environment before executing this script -# Actual settings may vary - -# PC Example using Ubuntu: LAPACK Builds -#export FC=gfortran # Fortran compiler family -#export LINK_DIRS=/usr/local/lib # Link directories for cmake -#export INCLUDES_DIRS="/usr/include;/usr/local/include" # directories for INCLUDES cmake variable (cmake uses semicolons as separators) -#export LIBRARY_LINKS="-llapack;-lnetcdff;-lnetcdf" # list of library links -- LAPACK builds -#export FLAGS_OPT="-flto=1;-fuse-linker-plugin" # optional compiler flags -- LAPACK builds - -# PC Example using Ubuntu: Intel oneMKL builds (see https://www.intel.com/content/www/us/en/developer/tools/oneapi/onemkl-link-line-advisor.html) -#oneAPI_dir=/opt/intel/oneapi # Intel oneAPI main directory -#source $oneAPI_dir/setvars.sh # initialize environment variables for Intel oneAPI -#export FC=gfortran # Fortran compiler family -#export LINK_DIRS=/usr/local/lib # Link directories for cmake -#export INCLUDES_DIRS="/usr/include;/usr/local/include" # directories for INCLUDES cmake variable (cmake uses semicolons as separators) -#export LIBRARY_LINKS="-lnetcdff;-lnetcdf;-Wl,--start-group ${MKLROOT}/lib/intel64/libmkl_gf_lp64.a ${MKLROOT}/lib/intel64/libmkl_gnu_thread.a ${MKLROOT}/lib/intel64/libmkl_core.a -Wl,--end-group;-lgomp;-lpthread;-lm;-ldl" # list of library links -- Intel oneMKL builds -#export FLAGS_OPT="-m64;-I"${MKLROOT}/include";-flto=1;-fuse-linker-plugin" # optional compiler flags -- Intel oneMKL builds - -# CMake Commands (build type controlled using DCMAKE_BUILD_TYPE) -cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials -cmake --build ../cmake_build --target all +cmake -B ../cmake_build -S ../. +cmake --build ../cmake_build --target all -j diff --git a/build/source/CMakeLists.txt b/build/source/CMakeLists.txt new file mode 100644 index 000000000..b30b782d1 --- /dev/null +++ b/build/source/CMakeLists.txt @@ -0,0 +1,247 @@ +#=============================================================================== +# List of Source Files for SUMMA +# All Variables are reachable from the parent scope +#=============================================================================== + +# NOAHMP modules +set(NOAHMP + ${NOAHMP_DIR}/module_model_constants.F + ${NOAHMP_DIR}/module_sf_noahutl.F + ${NOAHMP_DIR}/module_sf_noahlsm.F + ${NOAHMP_DIR}/module_sf_noahmplsm.F + CACHE INTERNAL "NOAHMP") + +# Free versions of numerical recipes utilities for NOAH-MP modules +set(NRUTIL + ${ENGINE_DIR}/f2008funcs.f90 + ${ENGINE_DIR}/nr_utility.f90 + ${ENGINE_DIR}/nrtype.f90 + CACHE INTERNAL "NRUTIL") + +# Free versions of numerical recipes procedures for SUMMA modules +set(NRPROC + ${ENGINE_DIR}/expIntegral.f90 + ${ENGINE_DIR}/spline_int.f90 + ${ENGINE_DIR}/hyp_2F1.f90 + CACHE INTERNAL "NRPROC") + +# Hook-up modules +set(HOOKUP + ${HOOKUP_DIR}/ascii_util.f90 + ${HOOKUP_DIR}/summaFileManager.f90 + CACHE INTERNAL "HOOKUP") + +# Data modules +set(DATAMS + ${DSHARE_DIR}/data_types.f90 + ${DSHARE_DIR}/flxMapping.f90 + ${DSHARE_DIR}/get_ixname.f90 + ${DSHARE_DIR}/globalData.f90 + ${DSHARE_DIR}/multiconst.f90 + ${DSHARE_DIR}/outpt_stat.f90 + ${DSHARE_DIR}/popMetadat.f90 + ${DSHARE_DIR}/var_lookup.f90 + CACHE INTERNAL "DATAMS") +set(DATAMS_SUNDIALS + ${DSHARE_DIR}/type4ida.f90 + ${DSHARE_DIR}/type4kinsol.f90 + CACHE INTERNAL "DATAMS_SUNDIALS") +set(DATAMS_ACTORS + ${ACTORS_DIR}/global/actor_data_types.f90 + CACHE INTERNAL "DATAMS_ACTORS") + +# Utility modules +set(UTILMS + ${ENGINE_DIR}/matrixOper.f90 + ${ENGINE_DIR}/mDecisions.f90 + ${ENGINE_DIR}/snow_utils.f90 + ${ENGINE_DIR}/soil_utils.f90 + ${ENGINE_DIR}/time_utils.f90 + ${ENGINE_DIR}/updatState.f90 + CACHE INTERNAL "UTILMS") +set(UTILMS_SUNDIALS + ${ENGINE_DIR}/soil_utilsAddPrime.f90 + ${ENGINE_DIR}/updatStateWithPrime.f90 + CACHE INTERNAL "UTILMS_SUNDIALS") + +# NetCDF routines +set(NETCDF + ${NETCDF_DIR}/def_output.f90 + ${NETCDF_DIR}/modelwrite.f90 + ${NETCDF_DIR}/netcdf_util.f90 + ${NETCDF_DIR}/read_icond.f90 + CACHE INTERNAL "NETCDF") + +# Preliminary modules +set(PRELIM + ${ENGINE_DIR}/allocspace.f90 + ${ENGINE_DIR}/check_icond.f90 + ${ENGINE_DIR}/checkStruc.f90 + ${ENGINE_DIR}/childStruc.f90 + ${ENGINE_DIR}/conv_funcs.f90 + ${ENGINE_DIR}/ffile_info.f90 + ${ENGINE_DIR}/read_pinit.f90 + ${ENGINE_DIR}/read_attrb.f90 + ${ENGINE_DIR}/paramCheck.f90 + ${ENGINE_DIR}/pOverwrite.f90 + ${ENGINE_DIR}/sunGeomtry.f90 + ${ENGINE_DIR}/read_param.f90 + CACHE INTERNAL "PRELIM") + +# Model run support modules +set(MODRUN + ${ENGINE_DIR}/canopySnow.f90 + ${ENGINE_DIR}/derivforce.f90 + ${ENGINE_DIR}/enthalpyTemp.f90 + ${ENGINE_DIR}/getVectorz.f90 + ${ENGINE_DIR}/indexState.f90 + ${ENGINE_DIR}/layerMerge.f90 + ${ENGINE_DIR}/layerDivide.f90 + ${ENGINE_DIR}/qTimeDelay.f90 + ${ENGINE_DIR}/snowAlbedo.f90 + ${ENGINE_DIR}/snwCompact.f90 + ${ENGINE_DIR}/tempAdjust.f90 + ${ENGINE_DIR}/updateVars.f90 + ${ENGINE_DIR}/var_derive.f90 + ${ENGINE_DIR}/volicePack.f90 + CACHE INTERNAL "MODRUN") +set(MODRUN_NOT_ACTORS + ${ENGINE_DIR}/read_force.f90 + CACHE INTERNAL "MODRUN_NOT_ACTORS") +set(MODRUN_SUNDIALS + ${ENGINE_DIR}/tol4ida.f90 + ${ENGINE_DIR}/updateVarsWithPrime.f90 + CACHE INTERNAL "MODRUN_SUNDIALS") + +# Solver main modules +set(SOLVER + ${ENGINE_DIR}/bigAquifer.f90 + ${ENGINE_DIR}/computFlux.f90 + ${ENGINE_DIR}/computHeatCap.f90 + ${ENGINE_DIR}/computJacob.f90 + ${ENGINE_DIR}/computResid.f90 + ${ENGINE_DIR}/computSnowDepth.f90 + ${ENGINE_DIR}/computThermConduct.f90 + ${ENGINE_DIR}/coupled_em.f90 + ${ENGINE_DIR}/diagn_evar.f90 + ${ENGINE_DIR}/eval8summa.f90 + ${ENGINE_DIR}/groundwatr.f90 + ${ENGINE_DIR}/opSplittin.f90 + ${ENGINE_DIR}/snowLiqFlx.f90 + ${ENGINE_DIR}/soilLiqFlx.f90 + ${ENGINE_DIR}/ssdNrgFlux.f90 + ${ENGINE_DIR}/stomResist.f90 + ${ENGINE_DIR}/summaSolve4homegrown.f90 + ${ENGINE_DIR}/systemSolv.f90 + ${ENGINE_DIR}/varSubstep.f90 + ${ENGINE_DIR}/vegLiqFlux.f90 + ${ENGINE_DIR}/vegNrgFlux.f90 + ${ENGINE_DIR}/vegPhenlgy.f90 + ${ENGINE_DIR}/vegSWavRad.f90 + CACHE INTERNAL "SOLVER") +set(SOLVER_NOT_ACTORS + ${ENGINE_DIR}/run_oneGRU.f90 + ${ENGINE_DIR}/run_oneHRU.f90 + CACHE INTERNAL "SOLVER_NOT_ACTORS") +set(SOLVER_SUNDIALS + ${ENGINE_DIR}/computJacobWithPrime.f90 + ${ENGINE_DIR}/computResidWithPrime.f90 + ${ENGINE_DIR}/eval8summaWithPrime.f90 + ${ENGINE_DIR}/summaSolve4ida.f90 + ${ENGINE_DIR}/summaSolve4kinsol.f90 + CACHE INTERNAL "SOLVER_SUNDIALS") + +# Driver support modules +set(DRIVER + ${DRIVER_DIR}/summa_type.f90 + ${DRIVER_DIR}/summa_setup.f90 + ${DRIVER_DIR}/summa_restart.f90 + ${DRIVER_DIR}/summa_alarms.f90 + ${DRIVER_DIR}/summa_globalData.f90 + CACHE INTERNAL "DRIVER") +set(DRIVER_NOT_ACTORS + ${DRIVER_DIR}/summa_util.f90 + ${DRIVER_DIR}/summa_defineOutput.f90 + ${DRIVER_DIR}/summa_init.f90 + ${DRIVER_DIR}/summa_forcing.f90 + ${DRIVER_DIR}/summa_modelRun.f90 + ${DRIVER_DIR}/summa_writeOutput.f90 + CACHE INTERNAL "DRIVER_NOT_ACTORS") +set(DRIVER_NEXGEN + ${DRIVER_DIR}/summa_bmi.f90 + CACHE INTERNAL "DRIVER_NEXGEN") + +# Actors interface modules +set(INTERFACE + ${ACTORS_DIR}/global/cppwrap_auxiliary.f90 + ${ACTORS_DIR}/global/cppwrap_datatypes.f90 + ${ACTORS_DIR}/global/cppwrap_metadata.f90 + ${ACTORS_DIR}/global/c_interface_module.f90 + CACHE INTERNAL "INTERFACE") +set(SYS_INIT_INTERFACE + ${SYS_INIT_DIR}/batch_distributer_actor.f90 + CACHE INTERNAL "SYS_INIT_INTERFACE") +set(FILE_ACCESS_INTERFACE + ${FILE_ACCESS_DIR}/summa_init_struc.f90 + ${FILE_ACCESS_DIR}/forcing_file_info.f90 + ${FILE_ACCESS_DIR}/file_access_actor.f90 + ${FILE_ACCESS_DIR}/output_structure.f90 + ${FILE_ACCESS_DIR}/fileAccess_writeOutput.f90 + CACHE INTERNAL "FILE_ACCESS_INTERFACE") +set(JOB_INTERFACE + ${JOB_ACTOR_DIR}/gru_struc.f90 + CACHE INTERNAL "JOB_INTERFACE") +set(GRU_INTERFACE + ${GRU_ACTOR_DIR}/gru_interface.f90 + CACHE INTERNAL "GRU_INTERFACE") +set(HRU_INTERFACE + ${HRU_ACTOR_DIR}/hru_init.f90 + ${HRU_ACTOR_DIR}/hru_read.f90 + ${HRU_ACTOR_DIR}/hru_modelRun.f90 + ${HRU_ACTOR_DIR}/hru_writeOutput.f90 + ${HRU_ACTOR_DIR}/hru_interface.f90 + CACHE INTERNAL "HRU_INTERFACE") + +# Actors actual actor modules +set(ACTORS_GLOBAL + ${ACTORS_DIR}/global/auxiliary.cpp + ${ACTORS_DIR}/global/fileManager.cpp + ${ACTORS_DIR}/global/settings_functions.cpp + ${ACTORS_DIR}/global/timing_info.cpp + ${ACTORS_DIR}/global/logger.cpp + CACHE INTERNAL "ACTORS_GLOBAL") +set(SYS_INIT + ${SYS_INIT_DIR}/batch.cpp + ${SYS_INIT_DIR}/batch_container.cpp + ${SYS_INIT_DIR}/client.cpp + ${SYS_INIT_DIR}/client_container.cpp + ${SYS_INIT_DIR}/summa_global_data.cpp + ${SYS_INIT_DIR}/summa_actor.cpp + ${SYS_INIT_DIR}/summa_backup_server.cpp + ${SYS_INIT_DIR}/summa_client.cpp + ${SYS_INIT_DIR}/summa_server.cpp + CACHE INTERNAL "SYS_INIT") +set(FILE_ACCESS_ACTOR + ${FILE_ACCESS_DIR}/summa_init_struc.cpp + ${FILE_ACCESS_DIR}/file_access_actor.cpp + ${FILE_ACCESS_DIR}/forcing_file_info.cpp + ${FILE_ACCESS_DIR}/output_container.cpp + CACHE INTERNAL "FILE_ACCESS_ACTOR") +set(JOB_ACTOR + ${JOB_ACTOR_DIR}/GRU.cpp + ${JOB_ACTOR_DIR}/gru_struc.cpp + ${JOB_ACTOR_DIR}/job_actor.cpp + ${JOB_ACTOR_DIR}/async_mode.cpp + ${JOB_ACTOR_DIR}/data_assimilation_mode.cpp + ${JOB_ACTOR_DIR}/job_utils.cpp + ${JOB_ACTOR_DIR}/distributed_job_actor.cpp + ${JOB_ACTOR_DIR}/node_actor.cpp + CACHE INTERNAL "JOB_ACTOR") +set(GRU_ACTOR + ${GRU_ACTOR_DIR}/gru_actor.cpp + CACHE INTERNAL "GRU_ACTOR") +set(HRU_ACTOR + ${HRU_ACTOR_DIR}/hru_utils.cpp + ${HRU_ACTOR_DIR}/hru_actor.cpp + ${HRU_ACTOR_DIR}/hru_batch_actor.cpp + CACHE INTERNAL "HRU_ACTOR") \ No newline at end of file diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 5509088bc..e682186fc 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -1098,7 +1098,6 @@ subroutine enthalpy2T_soil(& USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water USE soil_utils_module,only:dTheta_dPsi ! compute derivative of the soil moisture characteristic w.r.t. psi (m-1) - USE soil_utilsAddPrime_module,only:d2Theta_dTk2 ! second derivative in the freezing curve w.r.t. temperature (soil) implicit none ! delare dummy variables From e1abf614f68333d61ec7125d4591507124c87502 Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Thu, 23 May 2024 16:54:01 -0600 Subject: [PATCH 1317/1472] Add ability to find path on cluster --- build/cmake/FindNetCDF.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/cmake/FindNetCDF.cmake b/build/cmake/FindNetCDF.cmake index bba1a44a4..1f49cb485 100644 --- a/build/cmake/FindNetCDF.cmake +++ b/build/cmake/FindNetCDF.cmake @@ -6,9 +6,10 @@ find_path(NetCDF_C_INCLUDE_DIR NAMES netcdf.h) # Find the NetCDF Fortran library find_library(NetCDF_F90_LIBRARY NAMES netcdff) +find_path(NetCDF_F90_INCLUDE_DIR NAMES netcdf.mod) set (NetCDF_LIBRARIES ${NetCDF_C_LIBRARY} ${NetCDF_F90_LIBRARY}) -set (NetCDF_INCLUDE_DIRS ${NetCDF_C_INCLUDE_DIR}) +set (NetCDF_INCLUDE_DIRS ${NetCDF_C_INCLUDE_DIR} ${NetCDF_F90_INCLUDE_DIR}) find_package_handle_standard_args(NetCDF DEFAULT_MSG NetCDF_LIBRARIES NetCDF_INCLUDE_DIRS) From ba75a7efe540cff7025057d80f028b95e2da5db3 Mon Sep 17 00:00:00 2001 From: Kyle Klenk Date: Thu, 23 May 2024 17:15:54 -0600 Subject: [PATCH 1318/1472] Cleaned up documentation in CMakeLists.txt file --- build/CMakeLists.txt | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index d0dbba1a8..a53a08c27 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -6,23 +6,21 @@ if(DEFINED ENV{FC}) else() set (CMAKE_Fortran_COMPILER gfortran) endif() + #========================================================================================= -# When compiling Summa each configuration can be compiled in a Cluster or Debug mode, -# which is set by the CMAKE_BUILD_TYPES variables. The default is BE, release mode no -# cluster no Sundials no Actors no NexGen which runs the code with BE based off Numerical -# Recipes only. -# Note, Sundials, NexGen, and Actors will all require extra libraries to be downloaded -# and compiled. Currently, NexGen cannot work with Actors. -# If you install Sundials, you can compile Summa with Sundials, which allows you to run -# Summa with a solver of BE based off Numerical Recipes, Kinsol, or IDA. The solver choice -# is then set in the code decisions file. -# Other options include how to drive the code, NexGen (with BMI) or Actors (without BMI) -# framework or no extra framework (without BMI). -# If you change the build type, you will need to delete the CMakeCache.txt file and -# rerun cmake. +# SUMMA can be compiled with debug flags by specifiying -DCMAKE_BUILD_TYPE=Debug +# +# There are multiple options for building SUMMA, including using the +# Sundials suite of solvers, the C++ Actor framework, and the NexGen framework. +# The options are set by specifying -DOPTION=ON when running cmake. +# For example to compile SUMMA with Sundials, use +# cmake -B cmake_build -S ../. -DUSE_SUNDILAS=ON +# cmake --build ../cmake_build --target all -j +# +# To compile SUMMA with Actors, see the Summa-Actors repo: +# https://github.com/uofs-simlab/Summa-Actors #========================================================================================= - # Add options for build type set(CMAKE_CONFIGURATION_TYPES Release Debug) message("\nSelected Bulid Type: ${CMAKE_BUILD_TYPE}\n") From c8d308bcbe8b9eb78f44f70f0de735d64b4ce951 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 May 2024 16:41:32 +0900 Subject: [PATCH 1319/1472] update make --- build/CMakeLists.txt | 26 +++++----- build/cmake/build.cluster.bash | 6 +-- build/cmake/build.mac.bash | 10 ++-- build/cmake/build.pc.bash | 14 +++++ build/cmake/build_actors.cluster.bash | 4 +- build/cmake/build_actors.mac.bash | 9 ++-- build/cmake/build_ngen.cluster.bash | 4 +- build/cmake/build_ngen.mac.bash | 10 ++-- docs/README_ngen.md | 73 ++++++++++++++------------- docs/README_not_ngen.md | 69 ++++++++++--------------- 10 files changed, 113 insertions(+), 112 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index a53a08c27..72b88d0df 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -10,13 +10,13 @@ endif() #========================================================================================= # SUMMA can be compiled with debug flags by specifiying -DCMAKE_BUILD_TYPE=Debug # -# There are multiple options for building SUMMA, including using the +# There are multiple options for building SUMMA, including using the # Sundials suite of solvers, the C++ Actor framework, and the NexGen framework. # The options are set by specifying -DOPTION=ON when running cmake. # For example to compile SUMMA with Sundials, use -# cmake -B cmake_build -S ../. -DUSE_SUNDILAS=ON -# cmake --build ../cmake_build --target all -j -# +# cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON +# cmake --build ../cmake_build --target all -j +# # To compile SUMMA with Actors, see the Summa-Actors repo: # https://github.com/uofs-simlab/Summa-Actors #========================================================================================= @@ -43,7 +43,7 @@ set(EXEC_DIR "${F_MASTER}/bin") if (USE_SUNDIALS) message("ENABLING SUNDIALS") find_package(SUNDIALS REQUIRED) - list(APPEND EXT_TARGETS SUNDIALS::fida_mod_shared + list(APPEND EXT_TARGETS SUNDIALS::fida_mod_shared SUNDIALS::fkinsol_mod_shared) add_compile_definitions(SUNDIALS_ACTIVE) set(EXEC_NAME summa_sundials.exe) @@ -71,7 +71,7 @@ if (USE_NEXTGEN) if (USE_ACTORS) message(FATAL_ERROR "NexGen incompatible with Summa-Actors") endif() - add_compile_definitions(NGEN_ACTIVE BMI_ACTIVE NGEN_FORCING_ACTIVE + add_compile_definitions(NGEN_ACTIVE BMI_ACTIVE NGEN_FORCING_ACTIVE NGEN_OUTPUT_ACTIVE) add_subdirectory(../iso_c_fortran_bmi ${CMAKE_BINARY_DIR}/iso_c_bmi) @@ -105,7 +105,7 @@ list(APPEND EXT_TARGETS OpenBLAS::OpenBLAS) set(FLAGS_OPT $ENV{FLAGS_OPT}) # get optional user-specified flags from environment variables if(CMAKE_BUILD_TYPE MATCHES Debug) message("\nSetting Debug Options") - add_compile_definitions(DEBUG) + add_compile_definitions(DEBUG) set(FLAGS_NOAH -g -O0 -fbacktrace -fbounds-check -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) set(FLAGS_ALL -g -O0 -fbacktrace -fbounds-check -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp ${FLAGS_OPT}) set(FLAGS_CXX -g -O0 -fbounds-check -Wfatal-errors -std=c++17 ${FLAGS_OPT}) @@ -229,17 +229,17 @@ target_link_libraries(summa PUBLIC ${EXT_TARGETS} SUMMA_NOAHMP SUMMA_COMM) # For Actors, build SUMMA Shared Library add the actors libraries and add the executable if(USE_ACTORS) - add_executable(${EXEC_NAME} ${MAIN_SUMMA} ${ACTORS_GLOBAL} + add_executable(${EXEC_NAME} ${MAIN_SUMMA} ${ACTORS_GLOBAL} ${FILE_ACCESS_ACTOR} ${JOB_ACTOR} ${GRU_ACTOR} ${HRU_ACTOR} ${SYS_INIT} ${SUMMA_CLIENT} ${SUMMA_SERVER}) set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) target_compile_options(${EXEC_NAME} PUBLIC ${FLAGS_CXX}) - target_include_directories(${EXEC_NAME} PUBLIC - ${PARENT_DIR}/includes/global - ${PARENT_DIR}/includes/summa_actor - ${PARENT_DIR}/includes/job_actor - ${PARENT_DIR}/includes/file_access_actor + target_include_directories(${EXEC_NAME} PUBLIC + ${PARENT_DIR}/includes/global + ${PARENT_DIR}/includes/summa_actor + ${PARENT_DIR}/includes/job_actor + ${PARENT_DIR}/includes/file_access_actor ${PARENT_DIR}/includes/gru_actor ${PARENT_DIR}/includes/hru_actor) target_link_libraries( ${EXEC_NAME} ${EXT_TARGETS} summa) diff --git a/build/cmake/build.cluster.bash b/build/cmake/build.cluster.bash index 3c47adbe3..c87cdf18e 100755 --- a/build/cmake/build.cluster.bash +++ b/build/cmake/build.cluster.bash @@ -1,7 +1,7 @@ #!/bin/bash # build on Copernicus or Graham, from cmake directory run this as ./build.cluster.bash -# for Summa + module load StdEnv/2023 module load gcc/12.3 module load openblas/0.3.24 @@ -10,5 +10,5 @@ module load netcdf-fortran/4.6.1 export FLAGS_OPT="-flto=1;-fuse-linker-plugin" -cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials_Cluster -cmake --build ../cmake_build --target all +cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON +cmake --build ../cmake_build --target all -j diff --git a/build/cmake/build.mac.bash b/build/cmake/build.mac.bash index 4a62f82c1..68b5dd5e3 100755 --- a/build/cmake/build.mac.bash +++ b/build/cmake/build.mac.bash @@ -1,14 +1,12 @@ #!/bin/bash # build SUMMA on a Mac using Bash, from cmake directory run this as ./build.mac.bash -# Environment variables that must be set for cmake: FC, LINK_DIRS, INCLUDES_DIRS, and LIBRARY_LINKS +# Environment variables may be set within this script (see examples below) or in the terminal environment before executing this script +# Actual settings may vary # Mac Example using MacPorts: export FC=/opt/local/bin/gfortran # Fortran compiler family -export LINK_DIRS=/opt/local/lib # Link directories for cmake -export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) -export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) #export FLAGS_OPT="-flto=1" # -flto=1 is slow to compile, but might want to use -cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials -cmake --build ../cmake_build --target all +cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON +cmake --build ../cmake_build --target all -j diff --git a/build/cmake/build.pc.bash b/build/cmake/build.pc.bash index 12d85f330..a8a5e857b 100755 --- a/build/cmake/build.pc.bash +++ b/build/cmake/build.pc.bash @@ -1,4 +1,18 @@ #!/bin/bash +# Build SUMMA on a PC using Bash, from cmake directory run this as ./build.pc.bash +# Environment variables may be set within this script (see examples below) or in the terminal environment before executing this script +# Actual settings may vary + +# PC Example using Ubuntu: LAPACK Builds +#export FC=gfortran # Fortran compiler family +#export FLAGS_OPT="-flto=1;-fuse-linker-plugin" # optional compiler flags -- LAPACK builds + +# PC Example using Ubuntu: Intel oneMKL builds (see https://www.intel.com/content/www/us/en/developer/tools/oneapi/onemkl-link-line-advisor.html) +#oneAPI_dir=/opt/intel/oneapi # Intel oneAPI main directory +#source $oneAPI_dir/setvars.sh # initialize environment variables for Intel oneAPI +#export FC=gfortran # Fortran compiler family +#export FLAGS_OPT="-m64;-I"${MKLROOT}/include";-flto=1;-fuse-linker-plugin" # optional compiler flags -- Intel oneMKL builds + cmake -B ../cmake_build -S ../. cmake --build ../cmake_build --target all -j diff --git a/build/cmake/build_actors.cluster.bash b/build/cmake/build_actors.cluster.bash index fb145e895..df20ad6d6 100755 --- a/build/cmake/build_actors.cluster.bash +++ b/build/cmake/build_actors.cluster.bash @@ -14,5 +14,5 @@ module load caf export FLAGS_OPT="-flto=1;-fuse-linker-plugin" export SUNDIALS_PATH=/globalhome/kck540/HPC/Libraries/sundials/v7.0/instdir -cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials_Actors_Cluster -cmake --build ../cmake_build --target all +cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DUSE_ACTORS=ON +cmake --build ../cmake_build --target all -j diff --git a/build/cmake/build_actors.mac.bash b/build/cmake/build_actors.mac.bash index 2a1e372ef..cc617457b 100755 --- a/build/cmake/build_actors.mac.bash +++ b/build/cmake/build_actors.mac.bash @@ -1,13 +1,12 @@ #!/bin/bash # build on Mac, from cmake directory run this as ./build_actors.mac.bash +# Environment variables may be set within this script (see examples below) or in the terminal environment before executing this script +# Actual settings may vary # Mac Example using MacPorts: export FC=/opt/local/bin/gfortran # Fortran compiler family -export LINK_DIRS=/opt/local/lib # Link directories for cmake -export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) -export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) #export FLAGS_OPT="-flto=1" # -flto=1 is slow to compile, but might want to use -cmake -B ../cmake_build -S . -DCMAKE_BUILD_TYPE=Sundials_Actors -cmake --build ../cmake_build --target all +cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DUSE_ACTORS=ON +cmake --build ../cmake_build --target all -j diff --git a/build/cmake/build_ngen.cluster.bash b/build/cmake/build_ngen.cluster.bash index e05ebdab2..83676f1ec 100755 --- a/build/cmake/build_ngen.cluster.bash +++ b/build/cmake/build_ngen.cluster.bash @@ -17,8 +17,8 @@ module load udunits/2.2.28 cmake -B extern/iso_c_fortran_bmi/cmake_build -S extern/iso_c_fortran_bmi cmake --build extern/iso_c_fortran_bmi/cmake_build --target all -cmake -B extern/summa/cmake_build -S extern/summa -DCMAKE_BUILD_TYPE=BE_NexGen_Cluster -cmake --build extern/summa/cmake_build --target all +cmake -B extern/summa/cmake_build -S extern/summa -DUSE_NEXGEN=ON +cmake --build extern/summa/cmake_build --target all -j cmake -B cmake_build -S . -DNGEN_WITH_MPI:BOOL=OFF -DNGEN_WITH_PYTHON:BOOL=OFF -DNGEN_WITH_BMI_C:BOOL=ON -DNGEN_WITH_BMI_FORTRAN:BOOL=ON -DNGEN_WITH_NETCDF:BOOL=OFF # can also add -DCMAKE_BUILD_TYPE=Debug to be able to run in gdb diff --git a/build/cmake/build_ngen.mac.bash b/build/cmake/build_ngen.mac.bash index 7d52d2535..1da63213a 100755 --- a/build/cmake/build_ngen.mac.bash +++ b/build/cmake/build_ngen.mac.bash @@ -1,18 +1,18 @@ #!/bin/bash # build nextgen on Mac, from ngen directory put this one directory up and run this as ../build_ngen.mac.bash +# Environment variables may be set within this script (see examples below) or in the terminal environment before executing this script +# Actual settings may vary + # Mac Example using MacPorts: export FC=/opt/local/bin/gfortran # Fortran compiler family -export LINK_DIRS=/opt/local/lib # Link directories for cmake -export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for INCLUDES cmake variable (cmake uses semicolons as separators) -export LIBRARY_LINKS='-llapack;-lgfortran;-lnetcdff;-lnetcdf' # list of library links (cmake uses semicolons as separators) #export FLAGS_OPT="-flto=1" # -flto=1 is slow to compile, but might want to use cmake -B extern/iso_c_fortran_bmi/cmake_build -S extern/iso_c_fortran_bmi cmake --build extern/iso_c_fortran_bmi/cmake_build --target all -cmake -B extern/summa/cmake_build -S extern/summa -DCMAKE_BUILD_TYPE=BE_NexGen -cmake --build extern/summa/cmake_build --target all +cmake -B extern/summa/cmake_build -S extern/summa -DUSE_NEXGEN=ON +cmake --build extern/summa/cmake_build --target all -j cmake -B cmake_build -S . -DBoost_INCLUDE_DIR=/opt/local/libexec/boost/1.81/include -DPython_NumPy_INCLUDE_DIR=/opt/local/bin/python -DNGEN_WITH_MPI:BOOL=OFF -DNGEN_WITH_PYTHON:BOOL=OFF -DNGEN_WITH_BMI_C:BOOL=ON -DNGEN_WITH_BMI_FORTRAN:BOOL=ON -DNGEN_WITH_NETCDF:BOOL=OFF # can also add -DCMAKE_BUILD_TYPE=Debug to be able to run in gdb diff --git a/docs/README_ngen.md b/docs/README_ngen.md index 5cc0e2afc..3d72072c3 100644 --- a/docs/README_ngen.md +++ b/docs/README_ngen.md @@ -47,54 +47,59 @@ Note that this will be done in the NGen repo configuration, so it can then be co ## Building Libraries -First, cd into the outer directory containing the submodule: - - cd extern/summa - -If you want to use Sundials IDA or BE Kinsol, set -DCMAKE_BUILD_TYPE=Sundials_NexGen in the build script. Then, before summa can be built, Sundials needs to be installed. -Download the latest release of IDA solver from SUNDIALS package in https://computing.llnl.gov/projects/sundials/sundials-software, using X.Y.Z as the latest version - - wget "https://github.com/LLNL/sundials/releases/download/vX.Y.Z/sundials-X.Y.Z.tar.gz" - -Extract the corresponding compressed file, rename - - tar -xzf sundials-X.Y.Z.tar.gz && mv sundials-X.Y.Z sundials-software - -We suggest you periodically update to the latest version-- you can also install through github - git clone https://github.com/LLNL/sundials.git sundials-software - cd sundials-software - git fetch --all --tags --prune - git checkout tags/vX.Y.Z - -An example build_cmake file is at summa/build/cmake_external/build_cmakeSundials.bash which you can copy to builddir as build_cmake. Then, enter the buildir and run - - cd sundials/buildir - ./build_cmake - make - make install +If you want to use Sundials IDA or BE Kinsol, set -DUSE_SUNDIALS=ON in the build script. Then, before summa can be built, Sundials needs to be installed. + +### Installing SUNDIALS +Download the file `sundials-X.Y.Z.tar.gz` (where X.Y.Z is the latest SUNDIALS version) at https://github.com/LLNL/sundials/releases/latest. Move `sundials-X.Y.Z.tar.gz` into `top_dir`. In other words, `$ ls top_dir` should include `summa sundials-X.Y.Z.tar.gz. Download this to the folder your preferred ${SUN_DIR}. + +Extract the corresponding compressed file and rename + $ cd ${SUN_DIR} + $ tar -xzf sundials-X.Y.Z.tar.gz && mv sundials-X.Y.Z sundials-software + $ rm sundials-X.Y.Z.tar.gz + +Create new empty directories to prep for SUNDIALS installation, within ${SUN_DIR}: + $ mkdir sundials + $ cd sundials + $ mkdir builddir instdir + +Copy CMake build script from SUMMA files to properly configure SUNDIALS from your chosen ${NGEN_DIR} + $ cd builddir + $ cp ${NGEN_DIR}/ngen/external/summa/summa/build/cmake_external/build_cmakeSundials.bash . + +Build SUNDIALS configured for SUMMA, within `builddir`: + $ ./build_cmakeSundials.bash + $ make + $ make install + +We suggest you periodically update to the latest version. It is also possible to install using Git: + $ git clone https://github.com/LLNL/sundials.git sundials-software + $ cd sundials-software + $ git fetch --all --tags --prune + $ git checkout tags/vX.Y.Z -Note if you need to recompile after a system upgrade, delete the contents of sundials/instdir and sundials/buildir EXCEPT sundials/buildir/build_cmake before building and installing. +Note if you need to recompile after a system upgrade, delete the contents of sundials/instdir and sundials/buildir EXCEPT sundials/buildir/build_cmakeSundials.bash before building and installing. -Before ngen library files can be built, a CMake build system must be generated. E.g.: +### Building and installing SUMMA within NexGen +First, you will need to tell CMake where Sundials is if you installed it and plan to use it: + $ export CMAKE_PREFIX_PATH=/sun_dir/sundials/instdir - cmake -B cmake_build -S . +Then, a CMake build system must be generated. E.g., from the top `ngen` directory: + $ cmake -B extern/summa/cmake_build -S extern/summa -DUSE_NEXGEN=ON Note (similar to above) that when there is an existing directory, it may sometimes be necessary to clear it and regenerate, especially if any changes were made to the CMakeLists.txt file. After there is build system directory, the shared library can be built using the `summabmi` CMake target. For example, the SummaSundials shared library file (i.e., the build config's `summabmi` target) can be built using: - - cmake --build extern/summa/cmake_build --target all + $ cmake --build extern/summa/cmake_build --target all -j This will build a `cmake_build/libsummabmi..` file, where the version is configured within the CMake config, and the extension depends on the local machine's operating system. There is an example of a bash script to build the ngen libraries at ngen/extern/summa/summa/build/cmake/build_ngen.[system_type].bash. Sundials is turned off here. These need to be run in the main ngen directory. To run a test basin, in the main ngen directory, run - - cmake_build/ngen data/catchment_data.geojson "celia" ./data/nexus_data.geojson "nex-26" ./extern/summa/summa/test_ngen/example_realization_config_w_summa_bmi.json + $ cmake_build/ngen data/catchment_data.geojson "celia" ./data/nexus_data.geojson "nex-26" ./extern/summa/summa/test_ngen/example_realization_config_w_summa_bmi.json or - cmake_build/ngen data/catchment_data.geojson "cat-27" ./data/nexus_data.geojson "nex-26" ./extern/summa/summa/test_ngen/example_realization_config_w_summa_bmi__mac.json + $ cmake_build/ngen data/catchment_data.geojson "cat-27" ./data/nexus_data.geojson "nex-26" ./extern/summa/summa/test_ngen/example_realization_config_w_summa_bmi__mac.json -This command can be run as ./extern/summa/summa/test_ngen/example_run_celia.[system_type].sh also. +This command can be run as `./extern/summa/summa/test_ngen/example_run_celia.[system_type].sh` also. This test is currently the Celia Laugh test, not the cat-27 ngen example catchment. diff --git a/docs/README_not_ngen.md b/docs/README_not_ngen.md index 5401239ec..954086fe8 100644 --- a/docs/README_not_ngen.md +++ b/docs/README_not_ngen.md @@ -23,64 +23,49 @@ To fetch and check out the latest revision (for the [currently used branch](#vie ## Building Libraries -### Solver options in SUMMA build scripts -First, cd into the cmake directory in summa, without Actors: - - cd summa/build/cmake - -or with Actors: - - cd summa/build/summa/build/cmake - -If you want to use Sundials IDA or BE Kinsol, set -DCMAKE_BUILD_TYPE=Sundials* in the build script instead of BE*. Then, before summa can be built, Sundials needs to be installed. +If you want to use Sundials IDA or BE Kinsol, set -DUSE_SUNDIALS=ON in the build script. Then, before summa can be built, Sundials needs to be installed. ### Installing SUNDIALS -0. For reference, let the main summa directory be contained in the folder `top_dir` - - e.g., summa exectuables would be located within `top_dir/summa/bin` - -2. Download the file `sundials-X.Y.Z.tar.gz` (where X.Y.Z is the latest SUNDIALS version) at https://github.com/LLNL/sundials/releases/latest - -3. Move `sundials-X.Y.Z.tar.gz` into `top_dir` - - `$ ls top_dir` should include `summa sundials-X.Y.Z.tar.gz` - -4. Extract the corresponding compressed file and rename - 1. `$ cd top_dir` - 2. `$ tar -xzf sundials-X.Y.Z.tar.gz && mv sundials-X.Y.Z sundials-software` - 3. `sundials-X.Y.Z.tar.gz` can now be removed to save space if desired +Download the file `sundials-X.Y.Z.tar.gz` (where X.Y.Z is the latest SUNDIALS version) at https://github.com/LLNL/sundials/releases/latest. Move `sundials-X.Y.Z.tar.gz` into `top_dir`. In other words, `$ ls top_dir` should include `summa sundials-X.Y.Z.tar.gz. Download this to the folder your preferred ${SUN_DIR}. -5. Create new empty directories to prep for SUNDIALS installation - 1. within `top_dir`: `$ mkdir sundials` - 2. `$ cd sundials` - 3. `$ mkdir builddir instdir` +Extract the corresponding compressed file and rename + $ cd sun_dir + $ tar -xzf sundials-X.Y.Z.tar.gz && mv sundials-X.Y.Z sundials-software + $ rm sundials-X.Y.Z.tar.gz -6. Copy CMake build script from SUMMA files to properly configure SUNDIALS - 1. `$ cd builddir` - 2. `$ cp ../../summa/build/cmake_external/build_cmakeSundials.bash .` +Create new empty directories to prep for SUNDIALS installation, within ${SUN_DIR}: + $ mkdir sundials + $ cd sundials + $ mkdir builddir instdir -7. Build SUNDIALS configured for SUMMA - 1. within `builddir`: `$ ./build_cmakeSundials.bash` - 2. `$ make` - 3. `$ make install` +Copy CMake build script from SUMMA files to properly configure SUNDIALS from your chosen ${SUMMA_DIR} + $ cd builddir + $ cp ${SUMMA_DIR}/summa/build/cmake_external/build_cmakeSundials.bash . +Build SUNDIALS configured for SUMMA, within `builddir`: + $ ./build_cmakeSundials.bash + $ make + $ make install -We suggest you periodically update to the latest version. It is also possible to install using Git: - +We suggest you periodically update to the latest version. It is also possible to install using Git: $ git clone https://github.com/LLNL/sundials.git sundials-software $ cd sundials-software $ git fetch --all --tags --prune $ git checkout tags/vX.Y.Z -Note if you need to recompile after a system upgrade, delete the contents of sundials/instdir and sundials/buildir EXCEPT sundials/buildir/build_cmake before building and installing. +Note if you need to recompile after a system upgrade, delete the contents of sundials/instdir and sundials/buildir EXCEPT sundials/buildir/build_cmakeSundials.bash before building and installing. -Note that when there is an existing directory, it may sometimes be necessary to clear it and regenerate, especially if any changes were made to the CMakeLists.txt file. +### Building and installing SUMMA +First, you will need to tell CMake where Sundials is if you installed it and plan to use it: + $ export CMAKE_PREFIX_PATH=/sun_dir/sundials/instdir -### Building SUMMA +Then, a CMake build system must be generated. E.g., from the top `summa/build/cmake` directory, using SUNDIALS: + $ cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON After there is build system directory, the shared library can be built using the `summabmi` CMake target. For example, the SummaSundials shared library file (i.e., the build config's `summabmi` target) can be built using: + $ cmake --build ../cmake_build --target all -j - $ cmake --build ../cmake_build --target all - -This will build a `cmake_build/libsummabmi..` file, where the version is configured within the CMake config, and the extension depends on the local machine's operating system. +This will build a `cmake_build/libsumma..` file, where the version is configured within the CMake config, and the extension depends on the local machine's operating system. -There is an example of a bash script to build the summa libraries at /build/cmake/build[_actors].[system_type].bash. Sundials is turned on here. These need to be run in the cmake directory. +There is an example of a bash script to build the summa libraries at /build/cmake/build[_actors].[system_type].bash. SUNDIALS is turned on here. These need to be run in the cmake directory. From 58e8d94a2d15147baef91c9eb69ade9ad2d5771c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 May 2024 18:20:20 +0900 Subject: [PATCH 1320/1472] name is NextGen not NexGen --- build/CMakeLists.txt | 31 +++++++++++++---------- build/cmake/build.pc.bash | 2 -- build/cmake/build_ngen.cluster.bash | 4 +-- build/cmake/build_ngen.mac.bash | 2 +- build/source/CMakeLists.txt | 4 +-- build/source/driver/summa_bmi.f90 | 2 +- docs/README_ngen.md | 4 +-- docs/sundials_bmi_flags/bmi_interface.txt | 2 +- 8 files changed, 26 insertions(+), 25 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 72b88d0df..699560dca 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.21 FATAL_ERROR) project(summa VERSION 4.0.0 LANGUAGES Fortran C CXX) if(DEFINED ENV{FC}) - set (CMAKE_Fortran_COMPILER $ENV{FC}) #for NexGen need, works for others + set (CMAKE_Fortran_COMPILER $ENV{FC}) #for NextGen need, works for others else() set (CMAKE_Fortran_COMPILER gfortran) endif() @@ -11,7 +11,7 @@ endif() # SUMMA can be compiled with debug flags by specifiying -DCMAKE_BUILD_TYPE=Debug # # There are multiple options for building SUMMA, including using the -# Sundials suite of solvers, the C++ Actor framework, and the NexGen framework. +# Sundials suite of solvers, the C++ Actor framework, and the NextGen framework. # The options are set by specifying -DOPTION=ON when running cmake. # For example to compile SUMMA with Sundials, use # cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON @@ -23,19 +23,22 @@ endif() # Add options for build type set(CMAKE_CONFIGURATION_TYPES Release Debug) -message("\nSelected Bulid Type: ${CMAKE_BUILD_TYPE}\n") - # Options: Enable each with cmake -DOPTION=ON -option(USE_SUNDIALS "Use IDA solver from sundilas suite" OFF) +option(USE_SUNDIALS "Use IDA solver from SUNDIALS suite" OFF) option(USE_ACTORS "Use Actors Framework" OFF) -option(USE_NEXGEN "Use NexGen Framework" OFF) +option(USE_NEXTGEN "Use NextGen Framework" OFF) set(EXT_TARGETS) # list of external targets to link to # Set Default Executable Name set(EXEC_NAME summa.exe) # Set top-level directory -set(F_MASTER "${CMAKE_CURRENT_SOURCE_DIR}/..") +if (USE_NEXTGEN) + set(F_MASTER "${CMAKE_CURRENT_SOURCE_DIR}/summa") +else() + set(F_MASTER "${CMAKE_CURRENT_SOURCE_DIR}/..") +endif() + set(PARENT_DIR "${F_MASTER}") set(EXEC_DIR "${F_MASTER}/bin") @@ -67,9 +70,9 @@ if (USE_ACTORS) endif() if (USE_NEXTGEN) - message("ENABLING NEXGEN") + message("ENABLING NEXTGEN") if (USE_ACTORS) - message(FATAL_ERROR "NexGen incompatible with Summa-Actors") + message(FATAL_ERROR "NextGen incompatible with Summa-Actors") endif() add_compile_definitions(NGEN_ACTIVE BMI_ACTIVE NGEN_FORCING_ACTIVE NGEN_OUTPUT_ACTIVE) @@ -104,13 +107,13 @@ list(APPEND EXT_TARGETS OpenBLAS::OpenBLAS) # Set compiler flags set(FLAGS_OPT $ENV{FLAGS_OPT}) # get optional user-specified flags from environment variables if(CMAKE_BUILD_TYPE MATCHES Debug) - message("\nSetting Debug Options") + message("\nSetting SUMMA Debug Options") add_compile_definitions(DEBUG) set(FLAGS_NOAH -g -O0 -fbacktrace -fbounds-check -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) set(FLAGS_ALL -g -O0 -fbacktrace -fbounds-check -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp ${FLAGS_OPT}) set(FLAGS_CXX -g -O0 -fbounds-check -Wfatal-errors -std=c++17 ${FLAGS_OPT}) else() - message("\nSetting Release Options") + message("\nSetting SUMMA Release Options") set(FLAGS_NOAH -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) set(FLAGS_ALL -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp ${FLAGS_OPT}) set(FLAGS_CXX -O3 -Wfatal-errors -std=c++17 ${FLAGS_OPT}) @@ -163,8 +166,8 @@ else() set(MAIN_SUMMA ${DRIVER_DIR}/summa_driver.f90) endif() -if (USE_NEXGEN) - set(SUMMA_ALL ${SUMMA_ALL} ${DRIVER_NEXGEN}) +if (USE_NEXTGEN) + set(SUMMA_ALL ${SUMMA_ALL} ${DRIVER_NEXTGEN}) endif() if (USE_SUNDIALS) @@ -201,7 +204,7 @@ target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) # target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES}) target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP ${EXT_TARGETS}) # added flags to the link step -# For NexGen, build SUMMA Shared Library and add the outside BMI libraries +# For NextGen, build SUMMA Shared Library and add the outside BMI libraries if(USE_NEXTGEN) if(WIN32) add_library(summabmi ${SUMMA_ALL}) diff --git a/build/cmake/build.pc.bash b/build/cmake/build.pc.bash index a8a5e857b..8576c3e74 100755 --- a/build/cmake/build.pc.bash +++ b/build/cmake/build.pc.bash @@ -5,13 +5,11 @@ # Actual settings may vary # PC Example using Ubuntu: LAPACK Builds -#export FC=gfortran # Fortran compiler family #export FLAGS_OPT="-flto=1;-fuse-linker-plugin" # optional compiler flags -- LAPACK builds # PC Example using Ubuntu: Intel oneMKL builds (see https://www.intel.com/content/www/us/en/developer/tools/oneapi/onemkl-link-line-advisor.html) #oneAPI_dir=/opt/intel/oneapi # Intel oneAPI main directory #source $oneAPI_dir/setvars.sh # initialize environment variables for Intel oneAPI -#export FC=gfortran # Fortran compiler family #export FLAGS_OPT="-m64;-I"${MKLROOT}/include";-flto=1;-fuse-linker-plugin" # optional compiler flags -- Intel oneMKL builds cmake -B ../cmake_build -S ../. diff --git a/build/cmake/build_ngen.cluster.bash b/build/cmake/build_ngen.cluster.bash index 83676f1ec..99b615087 100755 --- a/build/cmake/build_ngen.cluster.bash +++ b/build/cmake/build_ngen.cluster.bash @@ -10,14 +10,14 @@ module load netcdf-fortran/4.6.1 export FLAGS_OPT="-flto=1;-fuse-linker-plugin" -# for NexGen +# for NextGen module load boost module load udunits/2.2.28 cmake -B extern/iso_c_fortran_bmi/cmake_build -S extern/iso_c_fortran_bmi cmake --build extern/iso_c_fortran_bmi/cmake_build --target all -cmake -B extern/summa/cmake_build -S extern/summa -DUSE_NEXGEN=ON +cmake -B extern/summa/cmake_build -S extern/summa -DUSE_NEXTGEN=ON cmake --build extern/summa/cmake_build --target all -j cmake -B cmake_build -S . -DNGEN_WITH_MPI:BOOL=OFF -DNGEN_WITH_PYTHON:BOOL=OFF -DNGEN_WITH_BMI_C:BOOL=ON -DNGEN_WITH_BMI_FORTRAN:BOOL=ON -DNGEN_WITH_NETCDF:BOOL=OFF diff --git a/build/cmake/build_ngen.mac.bash b/build/cmake/build_ngen.mac.bash index 1da63213a..4db51f884 100755 --- a/build/cmake/build_ngen.mac.bash +++ b/build/cmake/build_ngen.mac.bash @@ -11,7 +11,7 @@ export FC=/opt/local/bin/gfortran # Fortran compiler cmake -B extern/iso_c_fortran_bmi/cmake_build -S extern/iso_c_fortran_bmi cmake --build extern/iso_c_fortran_bmi/cmake_build --target all -cmake -B extern/summa/cmake_build -S extern/summa -DUSE_NEXGEN=ON +cmake -B extern/summa/cmake_build -S extern/summa -DUSE_NEXTGEN=ON cmake --build extern/summa/cmake_build --target all -j cmake -B cmake_build -S . -DBoost_INCLUDE_DIR=/opt/local/libexec/boost/1.81/include -DPython_NumPy_INCLUDE_DIR=/opt/local/bin/python -DNGEN_WITH_MPI:BOOL=OFF -DNGEN_WITH_PYTHON:BOOL=OFF -DNGEN_WITH_BMI_C:BOOL=ON -DNGEN_WITH_BMI_FORTRAN:BOOL=ON -DNGEN_WITH_NETCDF:BOOL=OFF diff --git a/build/source/CMakeLists.txt b/build/source/CMakeLists.txt index b30b782d1..a2fcc034a 100644 --- a/build/source/CMakeLists.txt +++ b/build/source/CMakeLists.txt @@ -167,9 +167,9 @@ set(DRIVER_NOT_ACTORS ${DRIVER_DIR}/summa_modelRun.f90 ${DRIVER_DIR}/summa_writeOutput.f90 CACHE INTERNAL "DRIVER_NOT_ACTORS") -set(DRIVER_NEXGEN +set(DRIVER_NEXTGEN ${DRIVER_DIR}/summa_bmi.f90 - CACHE INTERNAL "DRIVER_NEXGEN") + CACHE INTERNAL "DRIVER_NEXTGEN") # Actors interface modules set(INTERFACE diff --git a/build/source/driver/summa_bmi.f90 b/build/source/driver/summa_bmi.f90 index cd7ae5c24..0d3623f41 100644 --- a/build/source/driver/summa_bmi.f90 +++ b/build/source/driver/summa_bmi.f90 @@ -29,7 +29,7 @@ module summabmi ! NGEN_ACTIVE is to be set when running in the Nextgen framework ! https://github.com/NOAA-OWP/ngen #ifdef NGEN_ACTIVE - use bmif_2_0_iso ! BMI libraries NexGen + use bmif_2_0_iso ! BMI libraries NextGen #else use bmif_2_0 ! BMI libraries standard #endif diff --git a/docs/README_ngen.md b/docs/README_ngen.md index 3d72072c3..6c8578096 100644 --- a/docs/README_ngen.md +++ b/docs/README_ngen.md @@ -79,12 +79,12 @@ We suggest you periodically update to the latest version. It is also possible to Note if you need to recompile after a system upgrade, delete the contents of sundials/instdir and sundials/buildir EXCEPT sundials/buildir/build_cmakeSundials.bash before building and installing. -### Building and installing SUMMA within NexGen +### Building and installing SUMMA within NextGen First, you will need to tell CMake where Sundials is if you installed it and plan to use it: $ export CMAKE_PREFIX_PATH=/sun_dir/sundials/instdir Then, a CMake build system must be generated. E.g., from the top `ngen` directory: - $ cmake -B extern/summa/cmake_build -S extern/summa -DUSE_NEXGEN=ON + $ cmake -B extern/summa/cmake_build -S extern/summa -DUSE_NEXTGEN=ON Note (similar to above) that when there is an existing directory, it may sometimes be necessary to clear it and regenerate, especially if any changes were made to the CMakeLists.txt file. diff --git a/docs/sundials_bmi_flags/bmi_interface.txt b/docs/sundials_bmi_flags/bmi_interface.txt index 7767e6814..f15db32e6 100644 --- a/docs/sundials_bmi_flags/bmi_interface.txt +++ b/docs/sundials_bmi_flags/bmi_interface.txt @@ -1,4 +1,4 @@ -Note, if you want to run with just BMI, not NexGen, do the following +Note, if you want to run with just BMI, not NextGen, do the following First install BMI. Download the latest release of BMI from https://github.com/csdms/bmi-fortran.git % git clone https://github.com/csdms/bmi-fortran.git % git fetch --all --tags --prune From 54a02d8bd991a05e63fc5b0b93fb51762e624025 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 May 2024 19:58:53 +0900 Subject: [PATCH 1321/1472] capital letter --- build/source/engine/check_icond.f90 | 2 +- build/source/engine/mDecisions.f90 | 2 +- build/source/netcdf/read_icond.f90 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/source/engine/check_icond.f90 b/build/source/engine/check_icond.f90 index abd548472..7d82843b4 100644 --- a/build/source/engine/check_icond.f90 +++ b/build/source/engine/check_icond.f90 @@ -53,7 +53,7 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU USE data_types,only:gru_hru_doubleVec ! actual data USE data_types,only:gru_hru_intVec ! actual data USE data_types,only:gru_hru_z_vLookup ! actual data - USE globaldata,only:iname_soil,iname_snow ! named variables to describe the type of layer + USE globalData,only:iname_soil,iname_snow ! named variables to describe the type of layer USE multiconst,only:& LH_fus, & ! latent heat of fusion (J kg-1) iden_ice, & ! intrinsic density of ice (kg m-3) diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index 2205715ac..bf9f3398c 100644 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -173,7 +173,7 @@ subroutine mDecisions(err,message) USE globalData,only:data_step ! length of data step (s) USE globalData,only:numtim ! number of time steps in the simulation ! model decision structures - USE globaldata,only:model_decisions ! model decision structure + USE globalData,only:model_decisions ! model decision structure USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure ! forcing metadata USE globalData,only:forc_meta ! metadata structures diff --git a/build/source/netcdf/read_icond.f90 b/build/source/netcdf/read_icond.f90 index 0724dc074..5b60bf47d 100644 --- a/build/source/netcdf/read_icond.f90 +++ b/build/source/netcdf/read_icond.f90 @@ -155,7 +155,7 @@ subroutine read_icond(iconFile, & ! intent(in): name of USE globalData,only:bvar_meta ! metadata for basin (GRU) variables USE globalData,only:gru_struc ! gru-hru mapping structures USE globalData,only:startGRU ! index of first gru for parallel runs - USE globaldata,only:iname_soil,iname_snow ! named variables to describe the type of layer + USE globalData,only:iname_soil,iname_snow ! named variables to describe the type of layer USE netcdf_util_module,only:nc_file_open ! open netcdf file USE netcdf_util_module,only:nc_file_close ! close netcdf file USE netcdf_util_module,only:netcdf_err ! netcdf error handling From 558311c717ebf18363ba1011b81444f6e40c712e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 May 2024 20:51:09 +0900 Subject: [PATCH 1322/1472] fixing Cmake --- build/CMakeLists.txt | 60 +++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 699560dca..d54afb14e 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -76,7 +76,6 @@ if (USE_NEXTGEN) endif() add_compile_definitions(NGEN_ACTIVE BMI_ACTIVE NGEN_FORCING_ACTIVE NGEN_OUTPUT_ACTIVE) - add_subdirectory(../iso_c_fortran_bmi ${CMAKE_BINARY_DIR}/iso_c_bmi) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../../cmake/") set( CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran) @@ -201,7 +200,6 @@ target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) # Build SUMMA_COMM Object add_library(SUMMA_COMM OBJECT ${COMM_ALL}) target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) -# target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES}) target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP ${EXT_TARGETS}) # added flags to the link step # For NextGen, build SUMMA Shared Library and add the outside BMI libraries @@ -212,6 +210,7 @@ if(USE_NEXTGEN) add_library(summabmi SHARED ${SUMMA_ALL}) endif() target_compile_options(summabmi PRIVATE ${FLAGS_ALL}) + target_link_libraries(summa PUBLIC ${EXT_TARGETS} SUMMA_NOAHMP SUMMA_COMM) target_link_libraries(summabmi PUBLIC iso_c_bmi) set_target_properties(summabmi PROPERTIES VERSION ${PROJECT_VERSION}) @@ -223,33 +222,32 @@ if(USE_NEXTGEN) configure_file(summabmi.pc.in summabmi.pc @ONLY) install(FILES ${CMAKE_BINARY_DIR}/summabmi.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) -endif() - - -add_library(summa SHARED ${SUMMA_ALL}) -target_compile_options(summa PRIVATE ${FLAGS_ALL}) -target_link_libraries(summa PUBLIC ${EXT_TARGETS} SUMMA_NOAHMP SUMMA_COMM) - -# For Actors, build SUMMA Shared Library add the actors libraries and add the executable -if(USE_ACTORS) - add_executable(${EXEC_NAME} ${MAIN_SUMMA} ${ACTORS_GLOBAL} - ${FILE_ACCESS_ACTOR} ${JOB_ACTOR} ${GRU_ACTOR} ${HRU_ACTOR} - ${SYS_INIT} ${SUMMA_CLIENT} ${SUMMA_SERVER}) - - set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) - target_compile_options(${EXEC_NAME} PUBLIC ${FLAGS_CXX}) - target_include_directories(${EXEC_NAME} PUBLIC - ${PARENT_DIR}/includes/global - ${PARENT_DIR}/includes/summa_actor - ${PARENT_DIR}/includes/job_actor - ${PARENT_DIR}/includes/file_access_actor - ${PARENT_DIR}/includes/gru_actor - ${PARENT_DIR}/includes/hru_actor) - target_link_libraries( ${EXEC_NAME} ${EXT_TARGETS} summa) - -# For other cases, build SUMMA Shared Library and add the executable -elseif(NOT USE_NEXTGEN) - add_executable(${EXEC_NAME} ${MAIN_SUMMA}) - set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) - target_link_libraries(${EXEC_NAME} summa ${EXT_TARGETS}) # added flags to the link step +else() + add_library(summa SHARED ${SUMMA_ALL}) + target_compile_options(summa PRIVATE ${FLAGS_ALL}) + target_link_libraries(summa PUBLIC ${EXT_TARGETS} SUMMA_NOAHMP SUMMA_COMM) + + # For Actors, build SUMMA Shared Library add the actors libraries and add the executable + if(USE_ACTORS) + add_executable(${EXEC_NAME} ${MAIN_SUMMA} ${ACTORS_GLOBAL} + ${FILE_ACCESS_ACTOR} ${JOB_ACTOR} ${GRU_ACTOR} ${HRU_ACTOR} + ${SYS_INIT} ${SUMMA_CLIENT} ${SUMMA_SERVER}) + + set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) + target_compile_options(${EXEC_NAME} PUBLIC ${FLAGS_CXX}) + target_include_directories(${EXEC_NAME} PUBLIC + ${PARENT_DIR}/includes/global + ${PARENT_DIR}/includes/summa_actor + ${PARENT_DIR}/includes/job_actor + ${PARENT_DIR}/includes/file_access_actor + ${PARENT_DIR}/includes/gru_actor + ${PARENT_DIR}/includes/hru_actor) + target_link_libraries( ${EXEC_NAME} ${EXT_TARGETS} summa) + + # For other cases, build SUMMA Shared Library and add the executable + else() + add_executable(${EXEC_NAME} ${MAIN_SUMMA}) + set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) + target_link_libraries(${EXEC_NAME} summa ${EXT_TARGETS}) # added flags to the link step + endif() endif() From 3f0ca58d01e997343dd3a2a17ba924de7ec50c7a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 May 2024 21:08:43 +0900 Subject: [PATCH 1323/1472] linking --- build/CMakeLists.txt | 61 +++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 699560dca..7faad0d02 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -76,7 +76,6 @@ if (USE_NEXTGEN) endif() add_compile_definitions(NGEN_ACTIVE BMI_ACTIVE NGEN_FORCING_ACTIVE NGEN_OUTPUT_ACTIVE) - add_subdirectory(../iso_c_fortran_bmi ${CMAKE_BINARY_DIR}/iso_c_bmi) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../../cmake/") set( CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran) @@ -201,7 +200,6 @@ target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) # Build SUMMA_COMM Object add_library(SUMMA_COMM OBJECT ${COMM_ALL}) target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) -# target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES}) target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP ${EXT_TARGETS}) # added flags to the link step # For NextGen, build SUMMA Shared Library and add the outside BMI libraries @@ -212,8 +210,8 @@ if(USE_NEXTGEN) add_library(summabmi SHARED ${SUMMA_ALL}) endif() target_compile_options(summabmi PRIVATE ${FLAGS_ALL}) + target_link_libraries(summabmi PUBLIC ${EXT_TARGETS} iso_c_bmi SUMMA_NOAHMP SUMMA_COMM) - target_link_libraries(summabmi PUBLIC iso_c_bmi) set_target_properties(summabmi PROPERTIES VERSION ${PROJECT_VERSION}) include(GNUInstallDirs) @@ -223,33 +221,32 @@ if(USE_NEXTGEN) configure_file(summabmi.pc.in summabmi.pc @ONLY) install(FILES ${CMAKE_BINARY_DIR}/summabmi.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) -endif() - - -add_library(summa SHARED ${SUMMA_ALL}) -target_compile_options(summa PRIVATE ${FLAGS_ALL}) -target_link_libraries(summa PUBLIC ${EXT_TARGETS} SUMMA_NOAHMP SUMMA_COMM) - -# For Actors, build SUMMA Shared Library add the actors libraries and add the executable -if(USE_ACTORS) - add_executable(${EXEC_NAME} ${MAIN_SUMMA} ${ACTORS_GLOBAL} - ${FILE_ACCESS_ACTOR} ${JOB_ACTOR} ${GRU_ACTOR} ${HRU_ACTOR} - ${SYS_INIT} ${SUMMA_CLIENT} ${SUMMA_SERVER}) - - set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) - target_compile_options(${EXEC_NAME} PUBLIC ${FLAGS_CXX}) - target_include_directories(${EXEC_NAME} PUBLIC - ${PARENT_DIR}/includes/global - ${PARENT_DIR}/includes/summa_actor - ${PARENT_DIR}/includes/job_actor - ${PARENT_DIR}/includes/file_access_actor - ${PARENT_DIR}/includes/gru_actor - ${PARENT_DIR}/includes/hru_actor) - target_link_libraries( ${EXEC_NAME} ${EXT_TARGETS} summa) - -# For other cases, build SUMMA Shared Library and add the executable -elseif(NOT USE_NEXTGEN) - add_executable(${EXEC_NAME} ${MAIN_SUMMA}) - set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) - target_link_libraries(${EXEC_NAME} summa ${EXT_TARGETS}) # added flags to the link step +else() + add_library(summa SHARED ${SUMMA_ALL}) + target_compile_options(summa PRIVATE ${FLAGS_ALL}) + target_link_libraries(summa PUBLIC ${EXT_TARGETS} SUMMA_NOAHMP SUMMA_COMM) + + # For Actors, build SUMMA Shared Library add the actors libraries and add the executable + if(USE_ACTORS) + add_executable(${EXEC_NAME} ${MAIN_SUMMA} ${ACTORS_GLOBAL} + ${FILE_ACCESS_ACTOR} ${JOB_ACTOR} ${GRU_ACTOR} ${HRU_ACTOR} + ${SYS_INIT} ${SUMMA_CLIENT} ${SUMMA_SERVER}) + + set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) + target_compile_options(${EXEC_NAME} PUBLIC ${FLAGS_CXX}) + target_include_directories(${EXEC_NAME} PUBLIC + ${PARENT_DIR}/includes/global + ${PARENT_DIR}/includes/summa_actor + ${PARENT_DIR}/includes/job_actor + ${PARENT_DIR}/includes/file_access_actor + ${PARENT_DIR}/includes/gru_actor + ${PARENT_DIR}/includes/hru_actor) + target_link_libraries( ${EXEC_NAME} ${EXT_TARGETS} summa) + + # For other cases, build SUMMA Shared Library and add the executable + else() + add_executable(${EXEC_NAME} ${MAIN_SUMMA}) + set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) + target_link_libraries(${EXEC_NAME} summa ${EXT_TARGETS}) # added flags to the link step + endif() endif() From eb3ddd1860de8fa6724cb5b75465debfd341b9bb Mon Sep 17 00:00:00 2001 From: Kyle Klenk Date: Fri, 24 May 2024 13:09:42 -0600 Subject: [PATCH 1324/1472] remove unnecessary module import --- build/source/engine/check_icond.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/build/source/engine/check_icond.f90 b/build/source/engine/check_icond.f90 index abd548472..2d3e31e38 100644 --- a/build/source/engine/check_icond.f90 +++ b/build/source/engine/check_icond.f90 @@ -63,7 +63,6 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU USE snow_utils_module,only:fracliquid ! compute volumetric fraction of liquid water in snow based on temperature USE updatState_module,only:updateSnow ! update snow states USE updatState_module,only:updateSoil ! update soil states - USE indexState_module,only:indexState ! index the state variables USE enthalpyTemp_module,only:T2enthTemp_cas ! convert temperature to enthalpy for canopy air space USE enthalpyTemp_module,only:T2enthTemp_veg ! convert temperature to enthalpy for vegetation USE enthalpyTemp_module,only:T2enthTemp_snow ! convert temperature to enthalpy for snow From 85559284c71dbacb659f6ef9be9f9eecbcb7b65b Mon Sep 17 00:00:00 2001 From: Kyle Klenk Date: Wed, 29 May 2024 15:59:20 -0600 Subject: [PATCH 1325/1472] Adjust default value for errTestFailures: MaxErrTest failures set to 10 was too low. Setting this value to 50 allows GRUs a better chance to succeed on the first attempt --- build/source/engine/read_pinit.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/read_pinit.f90 b/build/source/engine/read_pinit.f90 index a9dd7217a..88497a219 100644 --- a/build/source/engine/read_pinit.f90 +++ b/build/source/engine/read_pinit.f90 @@ -218,7 +218,7 @@ subroutine set_ida_defaults(parFallback, err, message) parFallback(iLookPARAM%idaMaxStepSize)%default_val = 0 ! 0 means ida's default ofinfinity end if if (parFallback(iLookPARAM%idaMaxErrTestFail)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%idaMaxErrTestFail)%default_val = 10 ! IDA default is 10 + parFallback(iLookPARAM%idaMaxErrTestFail)%default_val = 50 ! IDA default is 10 end if end subroutine set_ida_defaults From 8472fadca4490952e87135b3ea5b4c12dacf3913 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 29 May 2024 16:21:52 -0600 Subject: [PATCH 1326/1472] fix for if canopyLiq goes negative inside sundials solver, will give nan for rawCanopyWetFraction. This happens because the value of rawWetFractionDeriv is blowing up, even with smoothing on -- the smoothing function does not do enough when canopyWettingExp gets smaller. If tie smoothing to canopyWettingExp would probably speed up the code. --- build/source/engine/vegNrgFlux.f90 | 9 ++++++--- utils/hist_per_GRU.py | 6 +++--- utils/plot_per_GRUMult.py | 14 +++++++------- utils/plot_per_GRUMultBal.py | 6 +----- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index ba1613eba..beb548fe7 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -1123,14 +1123,17 @@ subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFact ! compute an initial value of the canopy wet fraction ! - canopy below value where canopy is 100% wet - if (relativeCanopyWater < 1._rkind) then + if (relativeCanopyWater < 0._rkind) then ! will only happen inside Sundials Solver + rawCanopyWetFraction = 0._rkind + rawWetFractionDeriv = 0._rkind + ! - canopy is at capacity (canopyWettingFactor) + elseif (relativeCanopyWater < 1._rkind) then rawCanopyWetFraction = canopyWettingFactor*(relativeCanopyWater**canopyWettingExp) if (derDesire .and. relativeCanopyWater>verySmall) then rawWetFractionDeriv = (canopyWettingFactor*canopyWettingExp/canopyMax)*relativeCanopyWater**(canopyWettingExp - 1._rkind) else rawWetFractionDeriv = 0._rkind end if - ! - canopy is at capacity (canopyWettingFactor) else rawCanopyWetFraction = canopyWettingFactor rawWetFractionDeriv = 0._rkind @@ -1143,7 +1146,7 @@ subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFact else canopyWetFraction = rawCanopyWetFraction end if - + ! compute derivative (product rule) if (derDesire) then if (smoothing) then diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index cbf7f93f8..cb269ed2e 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -77,13 +77,13 @@ def power_transform(x): if stat == 'rmse': maxes = [2,15,250,0.08,200,10e-3] - if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] + if do_rel: maxes = [0.6,0.02,0.6,0.3,1.0,10e-3] if stat == 'rmnz': maxes = [2,15,250,0.08,200,10e-3] - if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] + if do_rel: maxes = [0.6,0.02,0.6,0.3,1.0,10e-3] if stat == 'maxe': maxes = [15,25,0.8,2,0.3,0.2] - if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,0.2] + if do_rel: maxes = [0.6,0.02,0.6,0.3,1.0,0.2] if stat == 'kgem': maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] maxes = [maxes[i] for i in use_vars] diff --git a/utils/plot_per_GRUMult.py b/utils/plot_per_GRUMult.py index f10bb6179..6f243b2f2 100644 --- a/utils/plot_per_GRUMult.py +++ b/utils/plot_per_GRUMult.py @@ -61,13 +61,13 @@ if stat == 'rmse': maxes = [2,15,250,0.08,200,10e-3] #[2,15,8e-6,0.08,6e-9,10e-3] #maxes = [0.25,2,30,0.01,30,2e-3] #[0.25,2,1e-6,0.01,1e-9,2e-3] - if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] + if do_rel: maxes = [0.6,0.02,0.6,0.3,1.0,10e-3] if stat == 'rmnz': maxes = [2,15,250,0.08,200,10e-3] - if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] + if do_rel: maxes = [0.6,0.02,0.6,0.3,1.0,10e-3] if stat == 'maxe': maxes = [15,25,0.8,2,0.3,0.2] #[15,25,25e-5,2,1e-7,0.2] - if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,0.2] + if do_rel: maxes = [0.6,0.02,0.6,0.3,1.0,0.2] if stat == 'kgem': maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] if stat == 'mean': @@ -317,14 +317,14 @@ def run_loop(j,var,the_max): fig,axs = plt.subplots(2,2,figsize=(35,28)) else: fig,axs = plt.subplots(2,2,figsize=(140,133)) - fig.suptitle('{} Hourly Statistics'.format(plt_titl[i]), fontsize=40,y=1.05) - plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines + # Remove the fourth subplot + #fig.delaxes(axs[1, 1]) + fig.suptitle('{} Hourly Statistics'.format(plt_titl[i]), fontsize=40,y=1.05) + plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines plt.tight_layout() - run_loop(i,var,the_max) - fig_fil1 = (var+fig_fil).format(stat) # Save plt.savefig(viz_dir/fig_fil1, bbox_inches='tight', transparent=True) diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index 3ab72469a..b7ac5876a 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -261,16 +261,12 @@ def run_loop(j,var,the_max): fig,axs = plt.subplots(2,2,figsize=(140,133)) # Remove the fourth subplot - fig.delaxes(axs[1, 1]) + #fig.delaxes(axs[1, 1]) fig.suptitle('{} Hourly Statistics'.format(plt_titl[i]), fontsize=40,y=1.05) - plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines - plt.tight_layout() - run_loop(i,var,the_max) - fig_fil1 = (var+fig_fil).format(stat) # Save plt.savefig(viz_dir/fig_fil1, bbox_inches='tight', transparent=True) From a543572a418bca452cabb9abb306f20a8610a5bc Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 29 May 2024 16:22:13 -0600 Subject: [PATCH 1327/1472] add comments to last bug fix --- build/source/engine/vegNrgFlux.f90 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index beb548fe7..1e9a789ae 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -1123,7 +1123,9 @@ subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFact ! compute an initial value of the canopy wet fraction ! - canopy below value where canopy is 100% wet - if (relativeCanopyWater < 0._rkind) then ! will only happen inside Sundials Solver + if (relativeCanopyWater < 0._rkind) then ! will only happen inside Sundials Solver, happens because rawWetFractionDeriv is blowing up + ! Note, this even with smoothing on -- the smoothing function does not do enough when canopyWettingExp gets smaller + ! FIX: tie smoothing to canopyWettingExp (would speed up the code) rawCanopyWetFraction = 0._rkind rawWetFractionDeriv = 0._rkind ! - canopy is at capacity (canopyWettingFactor) From b65a0ed7a0dee4ee9c01af53ebd0f63ff5037ba7 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 30 May 2024 13:08:10 -0600 Subject: [PATCH 1328/1472] utils --- utils/hist_per_GRU.py | 109 +++++++++++++++++++++++++++++++++++++++--- utils/scat_per_GRU.py | 25 +++++++--- 2 files changed, 121 insertions(+), 13 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index cb269ed2e..9600a4f1d 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -32,6 +32,8 @@ viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') method_name=['be1en'] plt_name=['BE1 mixed'] + method_name2=method_name + plt_name2=plt_name else: import sys # The first input argument specifies the run where the files are @@ -42,6 +44,8 @@ #plt_name=['BE1','BE16','BE32','SUNDIALS'] #maybe make this an argument method_name=['be1','be1cm','be1en','sundials_1en6cm'] plt_name=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp'] + method_name2=method_name+['sundials_1en8cm'] + plt_name2=plt_name+['Reference Solution'] if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE # Define the power transformation function @@ -49,23 +53,35 @@ def power_transform(x): return x ** 0.5 # Adjust the exponent as needed # Simulation statistics file locations -use_vars = [1,5] +use_vars = [1] settings0= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] settings = [settings0[i] for i in use_vars] +use_vars2 = [3,8] +settings20= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime'] +settings2 = [settings20[i] for i in use_vars2] + viz_fil = method_name.copy() +viz_fl2 = method_name2.copy() for i, m in enumerate(method_name): viz_fil[i] = m + '_hrly_diff_stats_{}.nc' viz_fil[i] = viz_fil[i].format(','.join(settings0)) +for i, m in enumerate(method_name2): + viz_fl2[i] = m + '_hrly_diff_bals_{}.nc' + viz_fl2[i] = viz_fl2[i].format(','.join(['balance'])) # Specify variables of interest plot_vars = settings.copy() plt_titl = ['Snow Water Equivalent','Total soil water content','Total evapotranspiration', 'Total water on the vegetation canopy','Average routed runoff','Wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] -leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$','$s$'] plt_titl = [f"({chr(97+n)}) {plt_titl[i]}" for n,i in enumerate(use_vars)] leg_titl = [leg_titl[i] for i in use_vars] -leg_titlm= [leg_titlm[i] for i in use_vars] + +plot_vars2 = settings2.copy() +plt_titl2 = ['Canopy Air Space Energy Balance','Vegetation Energy Balance','Snow Energy Balance','Soil Energy Balance','Vegetation Mass Balance','Snow Mass Balance','Soil Mass Balance','Aquifer Mass Balance', 'Wall Clock Time'] +leg_titl2 = ['$W~m^{-3}$'] * 4 +['$num$'] + ['$kg~m^{-2}~s^{-1}$'] * 4 +plt_titl2 = [f"({chr(97+n + len(use_vars))}) {plt_titl2[i]}" for n,i in enumerate(use_vars2)] +leg_titl2 = [leg_titl2[i] for i in use_vars2] if do_hist: fig_fil = 'Hrly_diff_hist_{}_{}_zoom_compressed.png' @@ -76,22 +92,37 @@ def power_transform(x): fig_fil = fig_fil.format(','.join(settings),stat) if stat == 'rmse': + stat2 = 'mean' maxes = [2,15,250,0.08,200,10e-3] if do_rel: maxes = [0.6,0.02,0.6,0.3,1.0,10e-3] if stat == 'rmnz': + stat2 = 'mean' maxes = [2,15,250,0.08,200,10e-3] if do_rel: maxes = [0.6,0.02,0.6,0.3,1.0,10e-3] if stat == 'maxe': + stat2 = 'amax' maxes = [15,25,0.8,2,0.3,0.2] if do_rel: maxes = [0.6,0.02,0.6,0.3,1.0,0.2] if stat == 'kgem': + stat2 = 'mean' maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] maxes = [maxes[i] for i in use_vars] +if stat2 == 'mean': + maxes2 = [1e-4,1e0,1e0,1e0]+[1e-12,1e-11,1e-10,1e-13] + [3e-3] + if do_rel: maxes2 = [1e-6,1e-4,1e-6,1e-7]+[1e-10,1e-11,1e-13,1e-11] + [10e-3] +if stat2 == 'amax': + maxes2 = [1e-3,1e3,1e3,1e2]+[1e-11,1e-6,1e-7,1e-8] + [1e0] + if do_rel: maxes2 = [1e-2,1e0,1e-4,1e-2]+[1e-7,1e-8,1e-10,1e-6] + [0.2] +maxes2 = [maxes2[i] for i in use_vars2] + summa = {} +summa1 = {} for i, m in enumerate(method_name): # Get the aggregated statistics of SUMMA simulations summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) +for i, m in enumerate(method_name2): + summa1[m] = xr.open_dataset(viz_dir/viz_fl2[i]) ##Figure @@ -174,8 +205,7 @@ def run_loop(i,var,mx): axs[r,c].legend(plt_name) axs[r,c].set_title(plt_titl[i]) - if stat == 'rmse' or stat == 'rmnz': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl[i])) - if stat == 'maxe': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titlm[i])) + if stat == 'rmse' or stat == 'rmnz' or stat == 'maxe': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl[i])) if stat == 'kgem': axs[r,c].set_xlabel(stat_word) if do_rel and var!='wallClockTime': axs[r,c].set_xlabel('relative '+ stat_word) @@ -190,8 +220,73 @@ def run_loop(i,var,mx): if var=='scalarTotalSoilWat': # Rotate x-axis labels for axs[2, 1] subplot axs[r, c].tick_params(axis='x', rotation=45) -for i,(var,mx) in enumerate(zip(plot_vars,maxes)): - run_loop(i,var,mx) +def run_loopb(i,var,mx): + r = (i+len(use_vars))//2 + c = (i+len(use_vars))-r*2 + stat0 = stat2 + + if 'zoom' in fig_fil: + mx = mx + mn = mx*1e-4 + if any(substring in var for substring in ['VegNrg', 'SnowNrg', 'SoilNrg']): + mn = mx*1e-7 + if var=='wallClockTime': mn = 0.0 + else: + mx = 0.0 + mn = 1.0 + for m in method_name2: + # Get the statistics, remove 9999 (should be nan, but just in case) + s = summa1[m][var].sel(stat=stat0).where(lambda x: x != 9999) + mx = max(s.max(),mx) + mn = min(s.min(),mn) + + # Data + for m in method_name2: + s = summa1[m][var].sel(stat=stat0).where(lambda x: x != 9999) + + range = (mn,mx) + if do_hist: + np.fabs(s).plot.hist(ax=axs[r,c], bins=num_bins,histtype='step',zorder=0,label=m,linewidth=2.0,range=range) + else: #cdf + sorted_data = np.sort(np.fabs(s)) + valid_data = sorted_data[~np.isnan(sorted_data)] + yvals = np.arange(len(valid_data)) / float(len(valid_data) - 1) + axs[r,c].plot(valid_data, yvals, zorder=0, label=m, linewidth=2.0) + axs[r,c].set_xlim(range) # Replace xmin and xmax with the desired limits + + + if stat0 == 'mean': stat_word = 'mean' + if stat0 == 'amax': stat_word = 'max' + + axs[r,c].legend(plt_name2) + axs[r,c].set_title(plt_titl2[i]) + axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl2[i])) + + if do_hist: + axs[r,c].set_ylabel('GRU count') + if var != 'wallClockTime' and not testing: axs[r,c].set_ylim([0, 25000]) + + else: + axs[r,c].set_ylabel('cumulative distribution') + axs[r,c].set_ylim([0.0, 1.0]) + axs[r,c].set_xscale('log') #log x axis + if var=='wallClockTime': axs[r,c].set_xscale('function', functions=(power_transform, np.power)) #log x axis + #if var=='scalarTotalSoilWat': # Rotate x-axis labels for axs[2, 1] subplot + # axs[r, c].tick_params(axis='x', rotation=45) + +if len(use_vars) > 0: + for i,(var,mx) in enumerate(zip(plot_vars,maxes)): + run_loop(i,var,mx) +if len(use_vars2) > 0: + for i,(var,mx) in enumerate(zip(plot_vars2,maxes2)): + run_loopb(i,var,mx) + +# Remove the extra subplots +if (len(plot_vars)+len(plot_vars2)) < 6: + for i in range((len(plot_vars)+len(plot_vars2)),6): + r = i//2 + c = i-r*2 + fig.delaxes(axs[r, c]) # Save plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index f803fc46f..e1d6f88a8 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -34,6 +34,9 @@ viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') method_name=['be1en'] plt_name=['BE1 mixed'] + method_name2=method_name + plt_name2=plt_name + else: import sys # The first input argument specifies the run where the files are @@ -44,16 +47,19 @@ #plt_name=['BE1','BE16','BE32','IDAe-6'] #maybe make this an argument method_name=['be1','be1cm','be1en','sundials_1en6cm'] plt_name=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp'] + method_name2=method_name+['sundials_1en8cm'] + plt_name2=plt_name+['Reference Solution'] if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] viz_fil = method_name.copy() -viz_fl2 = method_name.copy() +viz_fl2 = method_name2.copy() for i, m in enumerate(method_name): viz_fil[i] = m + '_hrly_diff_stats_{}.nc' viz_fil[i] = viz_fil[i].format(','.join(settings)) +for i, m in enumerate(method_name2): viz_fl2[i] = m + '_hrly_diff_bals_{}.nc' viz_fl2[i] = viz_fl2[i].format(','.join(['balance'])) @@ -62,6 +68,7 @@ for i, m in enumerate(method_name): # Get the aggregated statistics of SUMMA simulations summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) +for i, m in enumerate(method_name2): summa1[m] = xr.open_dataset(viz_dir/viz_fl2[i]) def run_loop(i,var,plt_t,leg_t,leg_t0,leg_tm): @@ -102,8 +109,8 @@ def run_loop(i,var,plt_t,leg_t,leg_t0,leg_tm): axs[r,c].scatter(x=np.fabs(s.sel(stat=stat).values),y=s.sel(stat=stat0).values,s=1,zorder=0,label=m) - lgnd = axs[r,c].legend() - for j, m in enumerate(method_name): + lgnd = axs[r,c].legend(plt_name) + for j, m in enumerate(plt_name): lgnd.legendHandles[j]._sizes = [80] axs[r,c].set_title(plt_t) if stat == 'rmse' or stat == 'rmnz': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_t)) @@ -132,7 +139,7 @@ def run_loopb(i,var,comp,leg_t,leg_t0,plt_t): word = ' max' # Data - for m in method_name: + for m in method_name2: # Get the statistics, remove 9999 (should be nan, but just in case) s0 = np.fabs(summa1[m][comp].sel(stat=stat0)).where(lambda x: x != 9999) s = np.fabs(summa1[m][var].sel(stat=stat0)).where(lambda x: x != 9999) @@ -146,8 +153,8 @@ def run_loopb(i,var,comp,leg_t,leg_t0,plt_t): stat0_word = 'Balance abs value' stat_word = 'Balance abs value' - lgnd = axs[r,c].legend() - for j, m in enumerate(method_name): + lgnd = axs[r,c].legend(plt_name2) + for j, m in enumerate(plt_name2): lgnd.legendHandles[j]._sizes = [80] axs[r,c].set_title(plt_t) axs[r,c].set_xscale('log') @@ -184,6 +191,9 @@ def run_loopb(i,var,comp,leg_t,leg_t0,plt_t): for i,(var,plt_t,leg_t,leg_t0,leg_tm) in enumerate(zip(plot_vars,plt_titl,leg_titl,leg_titl0,leg_titlm)): run_loop(i,var,plt_t,leg_t,leg_t0,leg_tm) + # Remove the sixth subplot + fig.delaxes(axs[2, 1]) + # Save plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) @@ -215,5 +225,8 @@ def run_loopb(i,var,comp,leg_t,leg_t0,plt_t): for i,(var,comp,leg_t,leg_t0,plt_t) in enumerate(zip(plot_vars,comp_vars,leg_titl,leg_titl0,plt_titl)): run_loopb(i,var,comp,leg_t,leg_t0,plt_t) + # Remove the sixth subplot + fig.delaxes(axs[2, 1]) + # Save plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) From d1350c2ea460e63865df2ff1900ade6ac05d482e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 1 Jun 2024 16:37:35 -0600 Subject: [PATCH 1329/1472] utils --- utils/hist_per_GRU.py | 47 +++++++++++++++++++++------------------ utils/plot_per_GRUMult.py | 6 ++--- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 9600a4f1d..e3045af69 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -54,10 +54,13 @@ def power_transform(x): # Simulation statistics file locations use_vars = [1] +#use_vars = [0,1,2,3,4] + settings0= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] settings = [settings0[i] for i in use_vars] use_vars2 = [3,8] +#use_vars2 = [8] settings20= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime'] settings2 = [settings20[i] for i in use_vars2] @@ -91,29 +94,29 @@ def power_transform(x): if do_rel: fig_fil = 'Hrly_diff_cdf_{}_{}_zoom_rel_compressed.png' fig_fil = fig_fil.format(','.join(settings),stat) -if stat == 'rmse': +if stat == 'rmse': stat2 = 'mean' - maxes = [2,15,250,0.08,200,10e-3] - if do_rel: maxes = [0.6,0.02,0.6,0.3,1.0,10e-3] -if stat == 'rmnz': + maxes = [2,15,250,0.08,200,20e-3] + if do_rel: maxes = [0.6,0.02,0.6,0.3,3.0,20e-3] +if stat == 'rmnz': stat2 = 'mean' - maxes = [2,15,250,0.08,200,10e-3] - if do_rel: maxes = [0.6,0.02,0.6,0.3,1.0,10e-3] -if stat == 'maxe': + maxes = [2,15,250,0.08,200,20e-3] + if do_rel: maxes = [0.6,0.02,0.6,0.3,3.0,20e-3] +if stat == 'maxe': stat2 = 'amax' - maxes = [15,25,0.8,2,0.3,0.2] - if do_rel: maxes = [0.6,0.02,0.6,0.3,1.0,0.2] -if stat == 'kgem': + maxes = [15,25,0.8,2,0.3,2.0] + if do_rel: maxes = [0.6,0.02,0.6,0.3,3.0,2.0] +if stat == 'kgem': stat2 = 'mean' - maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] + maxes = [0.9,0.9,0.9,0.9,0.9,20e-3] maxes = [maxes[i] for i in use_vars] -if stat2 == 'mean': - maxes2 = [1e-4,1e0,1e0,1e0]+[1e-12,1e-11,1e-10,1e-13] + [3e-3] - if do_rel: maxes2 = [1e-6,1e-4,1e-6,1e-7]+[1e-10,1e-11,1e-13,1e-11] + [10e-3] -if stat2 == 'amax': - maxes2 = [1e-3,1e3,1e3,1e2]+[1e-11,1e-6,1e-7,1e-8] + [1e0] - if do_rel: maxes2 = [1e-2,1e0,1e-4,1e-2]+[1e-7,1e-8,1e-10,1e-6] + [0.2] +if stat2 == 'mean': + maxes2 = [1e-3,1e1,1e1,1e1]+[1e-12,1e-11,1e-10,1e-13] + [20e-3] + #if do_rel: maxes2 = [1e-6,1e-4,1e-6,1e-7]+[1e-10,1e-11,1e-13,1e-11] + [10e-3] +if stat2 == 'amax': + maxes2 = [1e-2,1e4,1e4,1e3]+[1e-11,1e-6,1e-7,1e-8] + [2.0] + #if do_rel: maxes2 = [1e-2,1e0,1e-4,1e-2]+[1e-7,1e-8,1e-10,1e-6] + [0.2] maxes2 = [maxes2[i] for i in use_vars2] summa = {} @@ -217,7 +220,7 @@ def run_loop(i,var,mx): axs[r,c].set_ylabel('cumulative distribution') axs[r,c].set_ylim([0.0, 1.0]) axs[r,c].set_xscale('function', functions=(power_transform, np.power)) #log x axis - if var=='scalarTotalSoilWat': # Rotate x-axis labels for axs[2, 1] subplot + if var=='scalarTotalSoilWat' or var=='wallClockTime': # Rotate x-axis labels for axs[2, 1] subplot axs[r, c].tick_params(axis='x', rotation=45) def run_loopb(i,var,mx): @@ -229,7 +232,7 @@ def run_loopb(i,var,mx): mx = mx mn = mx*1e-4 if any(substring in var for substring in ['VegNrg', 'SnowNrg', 'SoilNrg']): - mn = mx*1e-7 + mn = mx*1e-9 if var=='wallClockTime': mn = 0.0 else: mx = 0.0 @@ -270,9 +273,9 @@ def run_loopb(i,var,mx): axs[r,c].set_ylabel('cumulative distribution') axs[r,c].set_ylim([0.0, 1.0]) axs[r,c].set_xscale('log') #log x axis - if var=='wallClockTime': axs[r,c].set_xscale('function', functions=(power_transform, np.power)) #log x axis - #if var=='scalarTotalSoilWat': # Rotate x-axis labels for axs[2, 1] subplot - # axs[r, c].tick_params(axis='x', rotation=45) + if var=='wallClockTime': + axs[r,c].set_xscale('function', functions=(power_transform, np.power)) #log x axis + axs[r, c].tick_params(axis='x', rotation=45) # Rotate x-axis labels for subplot if len(use_vars) > 0: for i,(var,mx) in enumerate(zip(plot_vars,maxes)): diff --git a/utils/plot_per_GRUMult.py b/utils/plot_per_GRUMult.py index 6f243b2f2..0275b5efa 100644 --- a/utils/plot_per_GRUMult.py +++ b/utils/plot_per_GRUMult.py @@ -61,13 +61,13 @@ if stat == 'rmse': maxes = [2,15,250,0.08,200,10e-3] #[2,15,8e-6,0.08,6e-9,10e-3] #maxes = [0.25,2,30,0.01,30,2e-3] #[0.25,2,1e-6,0.01,1e-9,2e-3] - if do_rel: maxes = [0.6,0.02,0.6,0.3,1.0,10e-3] + if do_rel: maxes = [0.6,0.02,0.6,0.3,3.0,10e-3] if stat == 'rmnz': maxes = [2,15,250,0.08,200,10e-3] - if do_rel: maxes = [0.6,0.02,0.6,0.3,1.0,10e-3] + if do_rel: maxes = [0.6,0.02,0.6,0.3,3.0,10e-3] if stat == 'maxe': maxes = [15,25,0.8,2,0.3,0.2] #[15,25,25e-5,2,1e-7,0.2] - if do_rel: maxes = [0.6,0.02,0.6,0.3,1.0,0.2] + if do_rel: maxes = [0.6,0.02,0.6,0.3,3.0,0.2] if stat == 'kgem': maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] if stat == 'mean': From f03d59604f411275494db654cf9201d89aa0baf2 Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 3 Jun 2024 11:54:45 -0600 Subject: [PATCH 1330/1472] Fix cmake lists for nextGEN: The findCmake files were not being read by SUMMA, and the ngen ones were being used. However, SUMMA could not use these paths. By setting the CMAKE_MODULE_PATH and PREFIX_PATH variables to include the summa specific finds first the problem goes away. --- build/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 7faad0d02..e9b31dd2c 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -93,8 +93,8 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXEC_DIR}) #========================================================================================= # NetCDF is found with a custom FindNetCDF.cmake file -LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/") -LIST(APPEND CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/cmake/") +LIST(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake/") +LIST(INSERT CMAKE_PREFIX_PATH 0 "${CMAKE_SOURCE_DIR}/cmake/") find_package(NetCDF REQUIRED) list(APPEND EXT_TARGETS NetCDF::NetCDF) From aa8ef3db1a273e466536bb5481075442ac1fd82e Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 3 Jun 2024 11:59:54 -0600 Subject: [PATCH 1331/1472] Add file for nextGen compiling: summabmi.pc.in is needed to compile nextGen --- build/cmake/summabmi.pc.in | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 build/cmake/summabmi.pc.in diff --git a/build/cmake/summabmi.pc.in b/build/cmake/summabmi.pc.in new file mode 100644 index 000000000..cbbadcf8a --- /dev/null +++ b/build/cmake/summabmi.pc.in @@ -0,0 +1,10 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=@CMAKE_INSTALL_PREFIX@ +libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ +Name: @SUMMA_LIB_NAME_CMAKE@ +Description: @SUMMA_DESC_CMAKE@ +Version: @PROJECT_VERSION@ +Requires: +Libs: -L${libdir} -lmylib +Cflags: -I${includedir} From e182fd1a66d12f0174901d9311f9136ab767ab8a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 4 Jun 2024 19:16:17 +0900 Subject: [PATCH 1332/1472] fixed CMake --- build/CMakeLists.txt | 7 +++---- build/cmake/build_ngen.mac.bash | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index e9b31dd2c..69e8e0290 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -77,8 +77,9 @@ if (USE_NEXTGEN) add_compile_definitions(NGEN_ACTIVE BMI_ACTIVE NGEN_FORCING_ACTIVE NGEN_OUTPUT_ACTIVE) add_subdirectory(../iso_c_fortran_bmi ${CMAKE_BINARY_DIR}/iso_c_bmi) - list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../../cmake/") - set( CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran) + list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_LIST_DIR}/summa/build/cmake/") + list(INSERT CMAKE_PREFIX_PATH 0 "${CMAKE_CURRENT_LIST_DIR}/summa/build/cmake/") + set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran) set(SUMMA_LIB_NAME_CMAKE summabmi) set(SUMMA_LIB_DESC_CMAKE "Summa-Sundials BMI Module Shared Library") endif() @@ -93,8 +94,6 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXEC_DIR}) #========================================================================================= # NetCDF is found with a custom FindNetCDF.cmake file -LIST(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake/") -LIST(INSERT CMAKE_PREFIX_PATH 0 "${CMAKE_SOURCE_DIR}/cmake/") find_package(NetCDF REQUIRED) list(APPEND EXT_TARGETS NetCDF::NetCDF) diff --git a/build/cmake/build_ngen.mac.bash b/build/cmake/build_ngen.mac.bash index 4db51f884..5158134f7 100755 --- a/build/cmake/build_ngen.mac.bash +++ b/build/cmake/build_ngen.mac.bash @@ -7,14 +7,13 @@ # Mac Example using MacPorts: export FC=/opt/local/bin/gfortran # Fortran compiler family #export FLAGS_OPT="-flto=1" # -flto=1 is slow to compile, but might want to use - -cmake -B extern/iso_c_fortran_bmi/cmake_build -S extern/iso_c_fortran_bmi -cmake --build extern/iso_c_fortran_bmi/cmake_build --target all +export C_INCLUDE_PATH=/opt/local/include +export CPLUS_INCLUDE_PATH=/opt/local/include cmake -B extern/summa/cmake_build -S extern/summa -DUSE_NEXTGEN=ON cmake --build extern/summa/cmake_build --target all -j -cmake -B cmake_build -S . -DBoost_INCLUDE_DIR=/opt/local/libexec/boost/1.81/include -DPython_NumPy_INCLUDE_DIR=/opt/local/bin/python -DNGEN_WITH_MPI:BOOL=OFF -DNGEN_WITH_PYTHON:BOOL=OFF -DNGEN_WITH_BMI_C:BOOL=ON -DNGEN_WITH_BMI_FORTRAN:BOOL=ON -DNGEN_WITH_NETCDF:BOOL=OFF +cmake -B cmake_build -S . -DBoost_INCLUDE_DIR=/opt/local/libexec/boost/1.81/include -DNGEN_WITH_BMI_FORTRAN=ON -DPython_NumPy_INCLUDE_DIR=/opt/local/bin/python -DNGEN_WITH_MPI:BOOL=OFF -DNGEN_WITH_PYTHON:BOOL=OFF -DNGEN_WITH_BMI_C:BOOL=ON -DNGEN_WITH_BMI_FORTRAN:BOOL=ON -DNGEN_WITH_NETCDF:BOOL=OFF # can also add -DCMAKE_BUILD_TYPE=Debug to be able to run in gdb # make -j 8 -C cmake_build # build w/ 8 parallel jobs, also turn MPI on make -C cmake_build From 82664843f611a1c43b3b5146864245630623ccae Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 6 Jun 2024 11:33:02 +0900 Subject: [PATCH 1333/1472] cmake, need non-ngen bit --- build/CMakeLists.txt | 3 +++ utils/hist_per_GRU.py | 37 +++++++++++++++++++++++------------- utils/plot_per_GRUMult.py | 2 +- utils/plot_per_GRUMultBal.py | 4 ++-- utils/scat_per_GRU.py | 29 +++++++++++++++------------- 5 files changed, 46 insertions(+), 29 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 69e8e0290..b07532515 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -82,6 +82,9 @@ if (USE_NEXTGEN) set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran) set(SUMMA_LIB_NAME_CMAKE summabmi) set(SUMMA_LIB_DESC_CMAKE "Summa-Sundials BMI Module Shared Library") +else() + LIST(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake/") + LIST(INSERT CMAKE_PREFIX_PATH 0 "${CMAKE_SOURCE_DIR}/cmake/") endif() get_filename_component(F_MASTER "${F_MASTER}" REALPATH) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index e3045af69..01fb2ec07 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -25,7 +25,7 @@ num_bins = 1000 do_rel = True # plot relative to the benchmark simulation -testing = False +testing = True do_hist = False # plot histogram instead of CDF if testing: stat = 'rmnz' @@ -45,7 +45,7 @@ method_name=['be1','be1cm','be1en','sundials_1en6cm'] plt_name=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp'] method_name2=method_name+['sundials_1en8cm'] - plt_name2=plt_name+['Reference Solution'] + plt_name2=plt_name+['reference solution'] if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE # Define the power transformation function @@ -75,14 +75,14 @@ def power_transform(x): # Specify variables of interest plot_vars = settings.copy() -plt_titl = ['Snow Water Equivalent','Total soil water content','Total evapotranspiration', 'Total water on the vegetation canopy','Average routed runoff','Wall clock time'] +plt_titl = ['snow Water Equivalent','total soil water content','total evapotranspiration', 'total water on the vegetation canopy','average routed runoff','wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] plt_titl = [f"({chr(97+n)}) {plt_titl[i]}" for n,i in enumerate(use_vars)] leg_titl = [leg_titl[i] for i in use_vars] plot_vars2 = settings2.copy() -plt_titl2 = ['Canopy Air Space Energy Balance','Vegetation Energy Balance','Snow Energy Balance','Soil Energy Balance','Vegetation Mass Balance','Snow Mass Balance','Soil Mass Balance','Aquifer Mass Balance', 'Wall Clock Time'] -leg_titl2 = ['$W~m^{-3}$'] * 4 +['$num$'] + ['$kg~m^{-2}~s^{-1}$'] * 4 +plt_titl2 = ['canopy air space energy balance','vegetation energy balance','snow energy balance','soil energy balance','vegetation mass balance','snow mass balance','soil mass malance','aquifer mass balance', 'wall clock time'] +leg_titl2 = ['$W~m^{-3}$'] * 4 + ['$kg~m^{-2}~s^{-1}$'] * 4 + ['$s$'] plt_titl2 = [f"({chr(97+n + len(use_vars))}) {plt_titl2[i]}" for n,i in enumerate(use_vars2)] leg_titl2 = [leg_titl2[i] for i in use_vars2] @@ -121,16 +121,23 @@ def power_transform(x): summa = {} summa1 = {} -for i, m in enumerate(method_name): - # Get the aggregated statistics of SUMMA simulations - summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) -for i, m in enumerate(method_name2): - summa1[m] = xr.open_dataset(viz_dir/viz_fl2[i]) +if len(use_vars)>0: + for i, m in enumerate(method_name): + # Get the aggregated statistics of SUMMA simulations + summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) +if len(use_vars2)>0: + for i, m in enumerate(method_name2): + summa1[m] = xr.open_dataset(viz_dir/viz_fl2[i]) ##Figure +plt.rcParams['xtick.color'] = 'black' +plt.rcParams['xtick.major.width'] = 2 +plt.rcParams['ytick.color'] = 'black' +plt.rcParams['ytick.major.width'] = 2 + if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 25}) + plt.rcParams.update({'font.size': 27}) else: plt.rcParams.update({'font.size': 100}) @@ -138,7 +145,7 @@ def power_transform(x): fig,axs = plt.subplots(3,2,figsize=(35,38)) else: fig,axs = plt.subplots(3,2,figsize=(140,160)) -fig.subplots_adjust(hspace=0.24, wspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space +fig.subplots_adjust(hspace=0.33, wspace=0.17) # Adjust the bottom margin, vertical space, and horizontal space #fig.suptitle('Histograms of Hourly Statistics for each GRU', fontsize=40,y=1.0) def run_loop(i,var,mx): @@ -205,7 +212,7 @@ def run_loop(i,var,mx): if statr == 'mean_ben': statr_word = 'mean' if statr == 'mnnz_ben': statr_word = 'mean' # no 0s' if statr == 'amax_ben': statr_word = 'max' - + axs[r,c].legend(plt_name) axs[r,c].set_title(plt_titl[i]) if stat == 'rmse' or stat == 'rmnz' or stat == 'maxe': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl[i])) @@ -218,6 +225,7 @@ def run_loop(i,var,mx): else: axs[r,c].set_ylabel('cumulative distribution') + if(c==1): axs[r, c].set_ylabel('') axs[r,c].set_ylim([0.0, 1.0]) axs[r,c].set_xscale('function', functions=(power_transform, np.power)) #log x axis if var=='scalarTotalSoilWat' or var=='wallClockTime': # Rotate x-axis labels for axs[2, 1] subplot @@ -226,6 +234,7 @@ def run_loop(i,var,mx): def run_loopb(i,var,mx): r = (i+len(use_vars))//2 c = (i+len(use_vars))-r*2 + print(c) stat0 = stat2 if 'zoom' in fig_fil: @@ -267,10 +276,12 @@ def run_loopb(i,var,mx): if do_hist: axs[r,c].set_ylabel('GRU count') + if(c==1): axs[r, c].set_ylabel('') if var != 'wallClockTime' and not testing: axs[r,c].set_ylim([0, 25000]) else: axs[r,c].set_ylabel('cumulative distribution') + if(c==1): axs[r, c].set_ylabel('') axs[r,c].set_ylim([0.0, 1.0]) axs[r,c].set_xscale('log') #log x axis if var=='wallClockTime': diff --git a/utils/plot_per_GRUMult.py b/utils/plot_per_GRUMult.py index 0275b5efa..41c7a8c14 100644 --- a/utils/plot_per_GRUMult.py +++ b/utils/plot_per_GRUMult.py @@ -309,7 +309,7 @@ def run_loop(j,var,the_max): # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 25}) + plt.rcParams.update({'font.size': 27}) else: plt.rcParams.update({'font.size': 100}) diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index b7ac5876a..d67788287 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -13,7 +13,7 @@ # Run: # python plot_per_GRUMult.py [stat] -# where stat is rmse or maxe or kgem +# where stat is mean or amax # modules @@ -251,7 +251,7 @@ def run_loop(j,var,the_max): # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 25}) + plt.rcParams.update({'font.size': 27}) else: plt.rcParams.update({'font.size': 100}) diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index e1d6f88a8..2b6994bb5 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -48,7 +48,7 @@ method_name=['be1','be1cm','be1en','sundials_1en6cm'] plt_name=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp'] method_name2=method_name+['sundials_1en8cm'] - plt_name2=plt_name+['Reference Solution'] + plt_name2=plt_name+['reference solution'] if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE @@ -147,11 +147,11 @@ def run_loopb(i,var,comp,leg_t,leg_t0,plt_t): axs[r,c].scatter(x=s.values,y=s0.values,s=10,zorder=0,label=m) if comp == 'numberFluxCalc': - stat0_word = 'Number flux calculations' - stat_word = 'Wall clock time' + stat0_word = 'number flux calculations' + stat_word = 'wall clock time' else: - stat0_word = 'Balance abs value' - stat_word = 'Balance abs value' + stat0_word = 'balance abs value' + stat_word = 'balance abs value' lgnd = axs[r,c].legend(plt_name2) for j, m in enumerate(plt_name2): @@ -162,10 +162,13 @@ def run_loopb(i,var,comp,leg_t,leg_t0,plt_t): axs[r,c].set_xlabel(stat_word + word + ' [{}]'.format(leg_t)) axs[r,c].set_ylabel(stat0_word + word + ' [{}]'.format(leg_t0)) - +plt.rcParams['xtick.color'] = 'black' +plt.rcParams['xtick.major.width'] = 2 +plt.rcParams['ytick.color'] = 'black' +plt.rcParams['ytick.major.width'] = 2 if do_vars: - plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff'] + plt_titl = ['(a) snow water equivalent','(b) total soil water content','(c) total evapotranspiration', '(d) total water on the vegetation canopy','(e) average routed runoff'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$'] leg_titl0 = ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$'] leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$'] @@ -175,16 +178,16 @@ def run_loopb(i,var,comp,leg_t,leg_t0,plt_t): # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 25}) + plt.rcParams.update({'font.size': 27}) else: plt.rcParams.update({'font.size': 100}) if 'compressed' in fig_fil: - fig,axs = plt.subplots(3,2,figsize=(31,33)) + fig,axs = plt.subplots(3,2,figsize=(35,38)) else: fig,axs = plt.subplots(3,2,figsize=(140,133)) #fig.suptitle('Hourly Errors and Values for each GRU', fontsize=40) - fig.subplots_adjust(hspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space + fig.subplots_adjust(hspace=0.33, wspace=0.17) # Adjust the bottom margin, vertical space, and horizontal space # Specify variables of interest plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff'] @@ -202,7 +205,7 @@ def run_loopb(i,var,comp,leg_t,leg_t0,plt_t): plot_vars = ['balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceCasNrg','wallClockTime'] comp_vars = ['balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','numberFluxCalc'] - plt_titl = ['(a) Vegetation Balance','(b) Snow Balance','(c) Soil Balance', '(d) Canopy Air Space and Aquifer Balance', '(f) Wall Clock Time'] + plt_titl = ['(a) vegetation balance','(b) snow balance','(c) soil balance', '(d) canopy air space and aquifer balance', '(f) wall clock time'] leg_titl = ['$W~m^{-3}$'] * 4 + ['$s$'] leg_titl0 =['$kg~m^{-2}~s^{-1}$'] * 4 + ['$num$'] @@ -211,7 +214,7 @@ def run_loopb(i,var,comp,leg_t,leg_t0,plt_t): fig_fil = fig_fil.format(stat) if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 25}) + plt.rcParams.update({'font.size': 27}) else: plt.rcParams.update({'font.size': 100}) @@ -219,7 +222,7 @@ def run_loopb(i,var,comp,leg_t,leg_t0,plt_t): fig,axs = plt.subplots(3,2,figsize=(35,38)) else: fig,axs = plt.subplots(3,2,figsize=(140,160)) - fig.subplots_adjust(hspace=0.24, wspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space + fig.subplots_adjust(hspace=0.33, wspace=0.17) # Adjust the bottom margin, vertical space, and horizontal space #fig.suptitle('Scatterplot of Hourly Statistics for each GRU', fontsize=40,y=1.0) for i,(var,comp,leg_t,leg_t0,plt_t) in enumerate(zip(plot_vars,comp_vars,leg_titl,leg_titl0,plt_titl)): From d3a243a17d52aae0e2c5d964fcf23acbdf4aec4f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 6 Jun 2024 12:48:02 +0900 Subject: [PATCH 1334/1472] quadratic smoother in canopy wetting, may work better? --- build/source/engine/vegNrgFlux.f90 | 50 ++++++++++++------------------ 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 1e9a789ae..9f39886bb 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -1143,8 +1143,8 @@ subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFact ! smooth canopy wetted fraction if (smoothing) then - call logisticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) - canopyWetFraction = rawCanopyWetFraction*smoothFunc ! logistic smoother + call quadraticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) + canopyWetFraction = rawCanopyWetFraction*smoothFunc ! quadratic smoother, works better than old logistic smoother else canopyWetFraction = rawCanopyWetFraction end if @@ -1163,44 +1163,32 @@ subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFact end subroutine wetFraction ! ******************************************************************************************************* -! private subroutine logisticSmoother: compute the smoothing function +! private subroutine quadraticSmoother: compute the smoothing function ! ******************************************************************************************************* -subroutine logisticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) +subroutine quadraticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) implicit none ! dummy variables - logical(lgt),intent(in) :: derDesire ! flag to denote if analytical derivatives are desired - real(rkind),intent(in) :: canopyLiq ! liquid water content (kg m-2) - real(rkind),intent(out) :: smoothFunc ! smoothing function (-) - real(rkind),intent(out) :: smoothFuncDeriv ! derivative in smoothing function (kg-1 m-2) - ! local variables - real(rkind) :: xArg ! argument used in the smoothing function (-) - real(rkind) :: expX ! exp(-xArg) -- used multiple times - real(rkind),parameter :: smoothThresh=0.01_rkind ! mid-point of the smoothing function (kg m-2) - real(rkind),parameter :: smoothScale=0.001_rkind ! scaling factor for the smoothing function (kg m-2) - real(rkind),parameter :: xLimit=50._rkind ! don't compute exponents for > xLimit + logical(lgt),intent(in) :: derDesire ! flag to denote if analytical derivatives are desired + real(rkind),intent(in) :: canopyLiq ! liquid water content (kg m-2) + real(rkind),intent(out) :: smoothFunc ! smoothing function (-) + real(rkind),intent(out) :: smoothFuncDeriv ! derivative in smoothing function (kg-1 m-2) + ! local variables + real(rkind) :: xArg ! argument used in the smoothing function (-) + real(rkind),parameter :: smoothThresh=0.01_rkind ! mid-point of smoothing function (kg m-2) + real(rkind),parameter :: smoothScale=0.001_rkind ! width of smoothing function (kg m-2) ! -------------------------------------------------------------------------------------------------------------- ! compute argument in the smoothing function xArg = (canopyLiq - smoothThresh)/smoothScale - ! only compute smoothing function for small exponents - if (xArg > -xLimit .and. xArg < xLimit) then ! avoid huge exponents - expX = exp(-xarg) ! also used in the derivative - smoothFunc = 1._rkind / (1._rkind + expX) ! logistic smoother - if (derDesire) then - smoothFuncDeriv = expX / (smoothScale * (1._rkind + expX)**2_i4b) ! derivative in the smoothing function - else - smoothFuncDeriv = 0._rkind - end if - - ! outside limits: special case of smooth exponents - else - if (xArg < 0._rkind) then; smoothFunc = 0._rkind ! xArg < -xLimit - else; smoothFunc = 1._rkind ! xArg > xLimit - end if + smoothFunc = 0.5_rkind * xArg/sqrt((1. + xArg**2_i4b)) + 0.5_rkind ! quadratic smoother + if (derDesire)then + smoothFuncDeriv = 0.5_rkind * (1._rkind / sqrt((1._rkind + xArg**2_i4b)) & + - (xArg**2_i4b) / (1._rkind + xArg**2_i4b)**1.5_rkind) / smoothScale ! derivative in the smoothing function + else smoothFuncDeriv = 0._rkind - end if ! check for huge exponents + endif -end subroutine logisticSmoother +end subroutine quadraticSmoother ! ******************************************************************************************************* ! private subroutine longwaveBal: compute longwave radiation balance at the canopy and ground surface From 3608dc999e69a84ed08d9f2c72559a2ffed16b13 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 6 Jun 2024 17:10:48 +0900 Subject: [PATCH 1335/1472] logistic smoother on theta instead of function, then do not have to cut off derivative --- build/source/engine/vegNrgFlux.f90 | 58 ++++++++++++++---------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 9f39886bb..7daa675ea 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -1114,18 +1114,21 @@ subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFact real(rkind) :: relativeCanopyWater ! water stored on vegetation canopy, expressed as a fraction of maximum storage (-) real(rkind) :: rawCanopyWetFraction ! initial value of the canopy wet fraction (before smoothing) real(rkind) :: rawWetFractionDeriv ! derivative in canopy wet fraction w.r.t. storage (kg-1 m2) - real(rkind) :: smoothFunc ! smoothing function used to improve numerical stability at times with limited water storage (-) - real(rkind) :: smoothFuncDeriv ! derivative in the smoothing function w.r.t.canopy storage (kg-1 m2) + real(rkind) :: smoothTheta ! smoothing function of water used to improve numerical stability at times with limited water storage (-) + real(rkind) :: smoothThetaDeriv ! derivative in the smoothing water w.r.t.canopy storage (kg-1 m2) real(rkind) :: verySmall=epsilon(1._rkind) ! a very small number ! -------------------------------------------------------------------------------------------------------------- ! compute relative canopy water - relativeCanopyWater = canopyLiq/canopyMax + if (smoothing) then ! smooth canopy wetted fraction by smoothing canopy liquid water content as in Kavetski and Kuczera (2007) + call logisticSmoother(derDesire,canopyLiq,smoothTheta,smoothThetaDeriv) + relativeCanopyWater = smoothTheta/canopyMax + else + relativeCanopyWater = canopyLiq/canopyMax + end if ! compute an initial value of the canopy wet fraction ! - canopy below value where canopy is 100% wet - if (relativeCanopyWater < 0._rkind) then ! will only happen inside Sundials Solver, happens because rawWetFractionDeriv is blowing up - ! Note, this even with smoothing on -- the smoothing function does not do enough when canopyWettingExp gets smaller - ! FIX: tie smoothing to canopyWettingExp (would speed up the code) + if (relativeCanopyWater < 0._rkind .and. .not.smoothing) then ! will only happen inside Sundials Solver, otherwise would be infeasible rawCanopyWetFraction = 0._rkind rawWetFractionDeriv = 0._rkind ! - canopy is at capacity (canopyWettingFactor) @@ -1140,19 +1143,12 @@ subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFact rawCanopyWetFraction = canopyWettingFactor rawWetFractionDeriv = 0._rkind end if - - ! smooth canopy wetted fraction - if (smoothing) then - call quadraticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) - canopyWetFraction = rawCanopyWetFraction*smoothFunc ! quadratic smoother, works better than old logistic smoother - else - canopyWetFraction = rawCanopyWetFraction - end if + canopyWetFraction = rawCanopyWetFraction - ! compute derivative (product rule) + ! compute derivative if (derDesire) then if (smoothing) then - canopyWetFractionDeriv = rawWetFractionDeriv*smoothFunc + rawCanopyWetFraction*smoothFuncDeriv + canopyWetFractionDeriv = rawWetFractionDeriv * smoothThetaDeriv else ! raw derivative is used if not smoothing canopyWetFractionDeriv = rawWetFractionDeriv end if @@ -1163,32 +1159,32 @@ subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFact end subroutine wetFraction ! ******************************************************************************************************* -! private subroutine quadraticSmoother: compute the smoothing function +! private subroutine logisticSmoother: compute the smoothed canopy liquid water content as in Kavetski and Kuczera (2007) ! ******************************************************************************************************* -subroutine quadraticSmoother(derDesire,canopyLiq,smoothFunc,smoothFuncDeriv) +subroutine logisticSmoother(derDesire,canopyLiq,smoothTheta,smoothThetaDeriv) implicit none ! dummy variables logical(lgt),intent(in) :: derDesire ! flag to denote if analytical derivatives are desired real(rkind),intent(in) :: canopyLiq ! liquid water content (kg m-2) - real(rkind),intent(out) :: smoothFunc ! smoothing function (-) - real(rkind),intent(out) :: smoothFuncDeriv ! derivative in smoothing function (kg-1 m-2) + real(rkind),intent(out) :: smoothTheta ! smoothed function of water(-) + real(rkind),intent(out) :: smoothThetaDeriv ! derivative in smoothed function (kg-1 m-2) ! local variables real(rkind) :: xArg ! argument used in the smoothing function (-) - real(rkind),parameter :: smoothThresh=0.01_rkind ! mid-point of smoothing function (kg m-2) - real(rkind),parameter :: smoothScale=0.001_rkind ! width of smoothing function (kg m-2) + real(rkind) :: expX ! exponential term used in the smoothing function (-) + real(rkind),parameter :: smoothThresh=0.01_rkind ! midpoint of smoothing function, move the discontiunity a bit away from 0 (kg m-2) + real(rkind),parameter :: smoothScale=0.0001_rkind ! width of smoothing function (kg m-2) ! -------------------------------------------------------------------------------------------------------------- ! compute argument in the smoothing function xArg = (canopyLiq - smoothThresh)/smoothScale + expX = exp(-xArg) ! also used in the derivative + smoothTheta = smoothScale * (xArg + log((1._rkind + expX))) ! logistic smoother + if (derDesire) then + smoothThetaDeriv = 1._rkind/(1._rkind + expX) ! derivative in the smoothing function + else + smoothThetaDeriv = 0._rkind + end if - smoothFunc = 0.5_rkind * xArg/sqrt((1. + xArg**2_i4b)) + 0.5_rkind ! quadratic smoother - if (derDesire)then - smoothFuncDeriv = 0.5_rkind * (1._rkind / sqrt((1._rkind + xArg**2_i4b)) & - - (xArg**2_i4b) / (1._rkind + xArg**2_i4b)**1.5_rkind) / smoothScale ! derivative in the smoothing function - else - smoothFuncDeriv = 0._rkind - endif - -end subroutine quadraticSmoother +end subroutine logisticSmoother ! ******************************************************************************************************* ! private subroutine longwaveBal: compute longwave radiation balance at the canopy and ground surface From df52b67d2c7508851d1f8edf33bd85a70b5109cb Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 7 Jun 2024 13:20:06 +0900 Subject: [PATCH 1336/1472] option to use smoother as logistic or quadratic, setting to logistic for now because I think it works better --- build/source/engine/vegNrgFlux.f90 | 31 ++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 7daa675ea..6f21e19e4 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -1120,7 +1120,7 @@ subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFact ! -------------------------------------------------------------------------------------------------------------- ! compute relative canopy water if (smoothing) then ! smooth canopy wetted fraction by smoothing canopy liquid water content as in Kavetski and Kuczera (2007) - call logisticSmoother(derDesire,canopyLiq,smoothTheta,smoothThetaDeriv) + call thetaSmoother(derDesire,canopyLiq,smoothTheta,smoothThetaDeriv) relativeCanopyWater = smoothTheta/canopyMax else relativeCanopyWater = canopyLiq/canopyMax @@ -1159,9 +1159,9 @@ subroutine wetFraction(derDesire,smoothing,canopyLiq,canopyMax,canopyWettingFact end subroutine wetFraction ! ******************************************************************************************************* -! private subroutine logisticSmoother: compute the smoothed canopy liquid water content as in Kavetski and Kuczera (2007) +! private subroutine thetaSmoother: compute the smoothed canopy liquid water content as in Kavetski and Kuczera (2007) ! ******************************************************************************************************* -subroutine logisticSmoother(derDesire,canopyLiq,smoothTheta,smoothThetaDeriv) +subroutine thetaSmoother(derDesire,canopyLiq,smoothTheta,smoothThetaDeriv) implicit none ! dummy variables logical(lgt),intent(in) :: derDesire ! flag to denote if analytical derivatives are desired @@ -1172,19 +1172,30 @@ subroutine logisticSmoother(derDesire,canopyLiq,smoothTheta,smoothThetaDeriv) real(rkind) :: xArg ! argument used in the smoothing function (-) real(rkind) :: expX ! exponential term used in the smoothing function (-) real(rkind),parameter :: smoothThresh=0.01_rkind ! midpoint of smoothing function, move the discontiunity a bit away from 0 (kg m-2) - real(rkind),parameter :: smoothScale=0.0001_rkind ! width of smoothing function (kg m-2) + real(rkind),parameter :: smoothScale=0.0001_rkind ! width of smoothing function (kg m-2) + logical(lgt),parameter :: use_logistic=.true. ! flag to denote if using logistic smoother if true, quadratic smoother if false ! -------------------------------------------------------------------------------------------------------------- ! compute argument in the smoothing function xArg = (canopyLiq - smoothThresh)/smoothScale - expX = exp(-xArg) ! also used in the derivative - smoothTheta = smoothScale * (xArg + log((1._rkind + expX))) ! logistic smoother - if (derDesire) then - smoothThetaDeriv = 1._rkind/(1._rkind + expX) ! derivative in the smoothing function - else + + if (use_logistic) then + expX = exp(-xArg) ! also used in the derivative + smoothTheta = smoothScale * (xArg + log((1._rkind + expX))) ! logistic smoother + if (derDesire) then + smoothThetaDeriv = 1._rkind/(1._rkind + expX) ! derivative in the smoothing function + else + smoothThetaDeriv = 0._rkind + end if + else ! use quadratic smoother instead + smoothTheta = 0.5_rkind * smoothScale * (xArg + sqrt((1._rkind + xArg**2_i4b))) ! quadratic smoother + if (derDesire) then + smoothThetaDeriv = 0.5_rkind * (1._rkind + xArg/sqrt((1._rkind + xArg**2_i4b))) ! derivative in the smoothing function + else smoothThetaDeriv = 0._rkind + end if end if -end subroutine logisticSmoother +end subroutine thetaSmoother ! ******************************************************************************************************* ! private subroutine longwaveBal: compute longwave radiation balance at the canopy and ground surface From c7fd3fcf370c47d267ae13160215af61b66dbe7e Mon Sep 17 00:00:00 2001 From: Kyle Date: Fri, 7 Jun 2024 17:24:45 +0000 Subject: [PATCH 1337/1472] Starting conversion to V1 of CAF --- build/CMakeLists.txt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index a53a08c27..0a91c4b63 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -51,10 +51,7 @@ endif() if (USE_ACTORS) message("ENABLING ACTORS") - find_package(CAF 0.18.3 QUIET) # Cluster has 0.18.3, but we can use 0.18.6 - if(NOT CAF_FOUND) - find_package(CAF 0.18.6 REQUIRED) - endif() + find_package(CAF REQUIRED) # Cluster has 0.18.3, but we can use 0.18.6 list(APPEND EXT_TARGETS CAF::core CAF::io) add_compile_definitions(ACTORS_ACTIVE V4_ACTIVE) @@ -229,9 +226,10 @@ target_link_libraries(summa PUBLIC ${EXT_TARGETS} SUMMA_NOAHMP SUMMA_COMM) # For Actors, build SUMMA Shared Library add the actors libraries and add the executable if(USE_ACTORS) - add_executable(${EXEC_NAME} ${MAIN_SUMMA} ${ACTORS_GLOBAL} - ${FILE_ACCESS_ACTOR} ${JOB_ACTOR} ${GRU_ACTOR} ${HRU_ACTOR} - ${SYS_INIT} ${SUMMA_CLIENT} ${SUMMA_SERVER}) + add_executable(${EXEC_NAME} ${MAIN_SUMMA} ) + # ${ACTORS_GLOBAL} + # ${FILE_ACCESS_ACTOR} ${JOB_ACTOR} ${GRU_ACTOR} ${HRU_ACTOR} + # ${SYS_INIT} ${SUMMA_CLIENT} ${SUMMA_SERVER}) set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) target_compile_options(${EXEC_NAME} PUBLIC ${FLAGS_CXX}) From fae58e8569aa66f69b92caa41b73e167fe1167d6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 11 Jun 2024 17:42:57 +0900 Subject: [PATCH 1338/1472] Bug fix: with the addition of the Cm term, the solution became more unstable and it was clear that the current calculation of the mean fluxes was incorrect in IDA (this only affects IDA flux output, uses instantaneous flux in solver). Does not affect BE solver. --- build/source/engine/eval8summaWithPrime.f90 | 2 +- build/source/engine/summaSolve4ida.f90 | 33 ++++++++++++++------- utils/hist_per_GRU.py | 4 +-- utils/plot_per_GRUMultBal.py | 8 ++--- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index ebaf4c4b0..d7a99940c 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -631,7 +631,7 @@ subroutine eval8summaWithPrime(& err,cmessage) ! intent(out): error code and error message if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - firstSplitOper = .false. ! after call computeFlux once in dt, no longer firstSplitOper + firstSplitOper = .false. ! after call computFlux once in dt, no longer firstSplitOper ! compute soil compressibility (-) and its derivative w.r.t. matric head (m) ! NOTE: we already extracted trial matrix head and volumetric liquid water as part of the flux calculations diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index d833e4461..67117a9ee 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -236,6 +236,7 @@ subroutine summaSolve4ida(& logical(lgt) :: tinystep ! if step goes below small size type(var_dlength) :: flux_prev ! previous model fluxes for a local HRU character(LEN=256) :: cmessage ! error message of downwind routine + real(rkind) :: dt_mult ! multiplier for time step average values real(rkind),allocatable :: mLayerMatricHeadPrimePrev(:) ! previous derivative value for total water matric potential (m s-1) real(rkind),allocatable :: resVecPrev(:) ! previous value for residuals real(rkind),allocatable :: dCompress_dPsiPrev(:) ! previous derivative value soil compression @@ -318,7 +319,6 @@ subroutine summaSolve4ida(& ! allocate space for the to save previous fluxes call allocLocal(flux_meta(:),flux_prev,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - flux_prev = eqns_data%flux_data ! allocate space for other variables if(model_decisions(iLookDECISIONS%groundwatr)%iDecision==qbaseTopmodel)then @@ -341,11 +341,15 @@ subroutine summaSolve4ida(& allocate( resVecPrev(nState) ) ! need the following values for the first substep + do iVar=1,size(flux_meta) ! loop through fluxes + flux_prev%var(iVar)%dat(:) = 0._rkind + end do eqns_data%scalarCanopyTempPrev = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) eqns_data%mLayerTempPrev(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) eqns_data%scalarCanopyTempTrial = prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) eqns_data%mLayerTempTrial(:) = prog_data%var(iLookPROG%mLayerTemp)%dat(:) eqns_data%mLayerMatricHeadPrev(:) = prog_data%var(iLookPROG%mLayerMatricHead)%dat(:) + mLayerMatricHeadPrimePrev = 0._rkind dCompress_dPsiPrev(:) = 0._rkind resVecPrev(:) = 0._rkind balance(:) = 0._rkind @@ -523,26 +527,32 @@ subroutine summaSolve4ida(& end if ! sum of fluxes smoothed over the time step, average from instantaneous values + if (nSteps>1) then + dt_mult = dt_diff/2._rkind + else ! first step no averaging + dt_mult = dt_diff + end if + do iVar=1,size(flux_meta) flux_sum%var(iVar)%dat(:) = flux_sum%var(iVar)%dat(:) + ( eqns_data%flux_data%var(iVar)%dat(:) & - + flux_prev%var(iVar)%dat(:) ) *dt_diff/2._rkind + + flux_prev%var(iVar)%dat(:) ) * dt_mult end do mLayerCmpress_sum(:) = mLayerCmpress_sum(:) + ( eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) * eqns_data%mLayerMatricHeadPrime(:) & - + dCompress_dPsiPrev(:) * mLayerMatricHeadPrimePrev(:) ) * dt_diff/2._rkind - + + dCompress_dPsiPrev(:) * mLayerMatricHeadPrimePrev(:) ) * dt_mult + ! ---- ! * compute energy balance, from residuals ! formulation with prime variables would cancel to closedForm version, so does not matter which formulation is used !------------------------ if(computNrgBalance)then - + ! compute energy balance mean, resVec is the instantaneous residual vector from the solver ! note, if needCm and/or updateCp are false in eval8summaWithPrime, then the energy balance is not accurate - if(ixCasNrg/=integerMissing) balance(ixCasNrg) = balance(ixCasNrg) + ( eqns_data%resVec(ixCasNrg) + resVecPrev(ixCasNrg) )*dt_diff/2._rkind/dt - if(ixVegNrg/=integerMissing) balance(ixVegNrg) = balance(ixVegNrg) + ( eqns_data%resVec(ixVegNrg) + resVecPrev(ixVegNrg) )*dt_diff/2._rkind/dt + if(ixCasNrg/=integerMissing) balance(ixCasNrg) = balance(ixCasNrg) + ( eqns_data%resVec(ixCasNrg) + resVecPrev(ixCasNrg) )*dt_mult/dt + if(ixVegNrg/=integerMissing) balance(ixVegNrg) = balance(ixVegNrg) + ( eqns_data%resVec(ixVegNrg) + resVecPrev(ixVegNrg) )*dt_mult/dt if(nSnowSoilNrg>0)then do concurrent (i=1:nLayers,ixSnowSoilNrg(i)/=integerMissing) - balance(ixSnowSoilNrg(i)) = balance(ixSnowSoilNrg(i)) + ( eqns_data%resVec(ixSnowSoilNrg(i)) + resVecPrev(ixSnowSoilNrg(i)) )*dt_diff/2._rkind/dt + balance(ixSnowSoilNrg(i)) = balance(ixSnowSoilNrg(i)) + ( eqns_data%resVec(ixSnowSoilNrg(i)) + resVecPrev(ixSnowSoilNrg(i)) )*dt_mult/dt enddo endif endif @@ -553,13 +563,13 @@ subroutine summaSolve4ida(& if(computMassBalance)then ! compute mass balance mean, resVec is the instantaneous residual vector from the solver - if(ixVegHyd/=integerMissing) balance(ixVegHyd) = balance(ixVegHyd) + ( eqns_data%resVec(ixVegHyd) + resVecPrev(ixVegHyd) )*dt_diff/2._rkind/dt + if(ixVegHyd/=integerMissing) balance(ixVegHyd) = balance(ixVegHyd) + ( eqns_data%resVec(ixVegHyd) + resVecPrev(ixVegHyd) )*dt_mult/dt if(nSnowSoilHyd>0)then do concurrent (i=1:nLayers,ixSnowSoilHyd(i)/=integerMissing) - balance(ixSnowSoilHyd(i)) = balance(ixSnowSoilHyd(i)) + ( eqns_data%resVec(ixSnowSoilHyd(i)) + resVecPrev(ixSnowSoilHyd(i)) )*dt_diff/2._rkind/dt + balance(ixSnowSoilHyd(i)) = balance(ixSnowSoilHyd(i)) + ( eqns_data%resVec(ixSnowSoilHyd(i)) + resVecPrev(ixSnowSoilHyd(i)) )*dt_mult/dt enddo endif - if(ixAqWat/=integerMissing) balance(ixAqWat) = balance(ixAqWat) + ( eqns_data%resVec(ixAqWat) + resVecPrev(ixAqWat) )*dt_diff/2._rkind/dt + if(ixAqWat/=integerMissing) balance(ixAqWat) = balance(ixAqWat) + ( eqns_data%resVec(ixAqWat) + resVecPrev(ixAqWat) )*dt_mult/dt endif ! save required quantities for next step @@ -570,6 +580,7 @@ subroutine summaSolve4ida(& dCompress_dPsiPrev(:) = eqns_data%deriv_data%var(iLookDERIV%dCompress_dPsi)%dat(:) tretPrev = tret(1) resVecPrev(:) = eqns_data%resVec(:) + flux_prev = eqns_data%flux_data ! Restart for where vegetation and layers cross freezing point if(detect_events)then diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 01fb2ec07..b5cdc92ae 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -75,7 +75,7 @@ def power_transform(x): # Specify variables of interest plot_vars = settings.copy() -plt_titl = ['snow Water Equivalent','total soil water content','total evapotranspiration', 'total water on the vegetation canopy','average routed runoff','wall clock time'] +plt_titl = ['snow water equivalent','total soil water content','total evapotranspiration', 'total water on the vegetation canopy','average routed runoff','wall clock time'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] plt_titl = [f"({chr(97+n)}) {plt_titl[i]}" for n,i in enumerate(use_vars)] leg_titl = [leg_titl[i] for i in use_vars] @@ -113,10 +113,8 @@ def power_transform(x): if stat2 == 'mean': maxes2 = [1e-3,1e1,1e1,1e1]+[1e-12,1e-11,1e-10,1e-13] + [20e-3] - #if do_rel: maxes2 = [1e-6,1e-4,1e-6,1e-7]+[1e-10,1e-11,1e-13,1e-11] + [10e-3] if stat2 == 'amax': maxes2 = [1e-2,1e4,1e4,1e3]+[1e-11,1e-6,1e-7,1e-8] + [2.0] - #if do_rel: maxes2 = [1e-2,1e0,1e-4,1e-2]+[1e-7,1e-8,1e-10,1e-6] + [0.2] maxes2 = [maxes2[i] for i in use_vars2] summa = {} diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index d67788287..0e026ba63 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -61,11 +61,9 @@ plot_vars = settings.copy() if stat == 'mean': - maxes = [1e-4,1e0,1e0,1e0]+[1e-12,1e-11,1e-10,1e-13] + [3e-3] - if do_rel: maxes = [1e-6,1e-4,1e-6,1e-7]+[1e-10,1e-11,1e-13,1e-11] + [3e-3] + maxes = [1e-3,1e1,1e1,1e1]+[1e-12,1e-11,1e-10,1e-13] + [20e-3] if stat == 'amax': - maxes = [1e-3,1e3,1e3,1e2]+[1e-11,1e-6,1e-7,1e-8] + [1e0] - if do_rel: maxes = [1e-2,1e0,1e-4,1e-2]+[1e-7,1e-8,1e-10,1e-6] + [1e0] + maxes = [1e-2,1e4,1e4,1e3]+[1e-11,1e-6,1e-7,1e-8] + [2.0] # Get the albers shapes main = Path('/home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/albers_projection') @@ -210,7 +208,7 @@ def run_loop(j,var,the_max): my_cmap.set_bad(color='white') #nan color white vmin,vmax = the_max*1e-4, the_max if any(substring in var for substring in ['VegNrg', 'SnowNrg', 'SoilNrg']): - vmin, vmax = the_max * 1e-7, the_max + vmin, vmax = the_max * 1e-9, the_max if var in ['wallClockTime',]: vmin,vmax = the_max*1e-1, the_max norm = matplotlib.colors.LogNorm(vmin=vmin, vmax=vmax) From 57f1ba208d46225bd2a9298593eafcba860e3643 Mon Sep 17 00:00:00 2001 From: Kyle Klenk Date: Wed, 12 Jun 2024 16:42:48 -0600 Subject: [PATCH 1339/1472] Current cmake_lists file represents what has been converted to CAF version 1 --- build/CMakeLists.txt | 8 ++++---- build/source/CMakeLists.txt | 18 +++++++----------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 0a91c4b63..93e743566 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -226,10 +226,10 @@ target_link_libraries(summa PUBLIC ${EXT_TARGETS} SUMMA_NOAHMP SUMMA_COMM) # For Actors, build SUMMA Shared Library add the actors libraries and add the executable if(USE_ACTORS) - add_executable(${EXEC_NAME} ${MAIN_SUMMA} ) - # ${ACTORS_GLOBAL} - # ${FILE_ACCESS_ACTOR} ${JOB_ACTOR} ${GRU_ACTOR} ${HRU_ACTOR} - # ${SYS_INIT} ${SUMMA_CLIENT} ${SUMMA_SERVER}) + add_executable(${EXEC_NAME} ${MAIN_SUMMA} ${ACTORS_GLOBAL} + ${FILE_ACCESS_ACTOR} ${JOB_ACTOR} ${GRU_ACTOR} ${SYS_INIT}) + # ${HRU_ACTOR} + # ${SUMMA_CLIENT} ${SUMMA_SERVER}) set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) target_compile_options(${EXEC_NAME} PUBLIC ${FLAGS_CXX}) diff --git a/build/source/CMakeLists.txt b/build/source/CMakeLists.txt index b30b782d1..2399953c7 100644 --- a/build/source/CMakeLists.txt +++ b/build/source/CMakeLists.txt @@ -213,13 +213,13 @@ set(ACTORS_GLOBAL set(SYS_INIT ${SYS_INIT_DIR}/batch.cpp ${SYS_INIT_DIR}/batch_container.cpp - ${SYS_INIT_DIR}/client.cpp - ${SYS_INIT_DIR}/client_container.cpp + # ${SYS_INIT_DIR}/client.cpp + # ${SYS_INIT_DIR}/client_container.cpp ${SYS_INIT_DIR}/summa_global_data.cpp ${SYS_INIT_DIR}/summa_actor.cpp - ${SYS_INIT_DIR}/summa_backup_server.cpp - ${SYS_INIT_DIR}/summa_client.cpp - ${SYS_INIT_DIR}/summa_server.cpp + # ${SYS_INIT_DIR}/summa_backup_server.cpp + # ${SYS_INIT_DIR}/summa_client.cpp + # ${SYS_INIT_DIR}/summa_server.cpp CACHE INTERNAL "SYS_INIT") set(FILE_ACCESS_ACTOR ${FILE_ACCESS_DIR}/summa_init_struc.cpp @@ -228,14 +228,10 @@ set(FILE_ACCESS_ACTOR ${FILE_ACCESS_DIR}/output_container.cpp CACHE INTERNAL "FILE_ACCESS_ACTOR") set(JOB_ACTOR - ${JOB_ACTOR_DIR}/GRU.cpp ${JOB_ACTOR_DIR}/gru_struc.cpp ${JOB_ACTOR_DIR}/job_actor.cpp - ${JOB_ACTOR_DIR}/async_mode.cpp - ${JOB_ACTOR_DIR}/data_assimilation_mode.cpp - ${JOB_ACTOR_DIR}/job_utils.cpp - ${JOB_ACTOR_DIR}/distributed_job_actor.cpp - ${JOB_ACTOR_DIR}/node_actor.cpp + # ${JOB_ACTOR_DIR}/distributed_job_actor.cpp + # ${JOB_ACTOR_DIR}/node_actor.cpp CACHE INTERNAL "JOB_ACTOR") set(GRU_ACTOR ${GRU_ACTOR_DIR}/gru_actor.cpp From fedff34df45dc6f7dd921da4c2039ae63e5287c4 Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 17 Jun 2024 15:18:36 -0600 Subject: [PATCH 1340/1472] Remove Summa-Actors specific files from cmakelist.txt for summa non-actors --- build/source/CMakeLists.txt | 138 ++++++++++++++++++------------------ 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/build/source/CMakeLists.txt b/build/source/CMakeLists.txt index 2399953c7..43473bd38 100644 --- a/build/source/CMakeLists.txt +++ b/build/source/CMakeLists.txt @@ -171,73 +171,73 @@ set(DRIVER_NEXGEN ${DRIVER_DIR}/summa_bmi.f90 CACHE INTERNAL "DRIVER_NEXGEN") -# Actors interface modules -set(INTERFACE - ${ACTORS_DIR}/global/cppwrap_auxiliary.f90 - ${ACTORS_DIR}/global/cppwrap_datatypes.f90 - ${ACTORS_DIR}/global/cppwrap_metadata.f90 - ${ACTORS_DIR}/global/c_interface_module.f90 - CACHE INTERNAL "INTERFACE") -set(SYS_INIT_INTERFACE - ${SYS_INIT_DIR}/batch_distributer_actor.f90 - CACHE INTERNAL "SYS_INIT_INTERFACE") -set(FILE_ACCESS_INTERFACE - ${FILE_ACCESS_DIR}/summa_init_struc.f90 - ${FILE_ACCESS_DIR}/forcing_file_info.f90 - ${FILE_ACCESS_DIR}/file_access_actor.f90 - ${FILE_ACCESS_DIR}/output_structure.f90 - ${FILE_ACCESS_DIR}/fileAccess_writeOutput.f90 - CACHE INTERNAL "FILE_ACCESS_INTERFACE") -set(JOB_INTERFACE - ${JOB_ACTOR_DIR}/gru_struc.f90 - CACHE INTERNAL "JOB_INTERFACE") -set(GRU_INTERFACE - ${GRU_ACTOR_DIR}/gru_interface.f90 - CACHE INTERNAL "GRU_INTERFACE") -set(HRU_INTERFACE - ${HRU_ACTOR_DIR}/hru_init.f90 - ${HRU_ACTOR_DIR}/hru_read.f90 - ${HRU_ACTOR_DIR}/hru_modelRun.f90 - ${HRU_ACTOR_DIR}/hru_writeOutput.f90 - ${HRU_ACTOR_DIR}/hru_interface.f90 - CACHE INTERNAL "HRU_INTERFACE") +# # Actors interface modules +# set(INTERFACE +# ${ACTORS_DIR}/global/cppwrap_auxiliary.f90 +# ${ACTORS_DIR}/global/cppwrap_datatypes.f90 +# ${ACTORS_DIR}/global/cppwrap_metadata.f90 +# ${ACTORS_DIR}/global/c_interface_module.f90 +# CACHE INTERNAL "INTERFACE") +# set(SYS_INIT_INTERFACE +# ${SYS_INIT_DIR}/batch_distributer_actor.f90 +# CACHE INTERNAL "SYS_INIT_INTERFACE") +# set(FILE_ACCESS_INTERFACE +# ${FILE_ACCESS_DIR}/summa_init_struc.f90 +# ${FILE_ACCESS_DIR}/forcing_file_info.f90 +# ${FILE_ACCESS_DIR}/file_access_actor.f90 +# ${FILE_ACCESS_DIR}/output_structure.f90 +# ${FILE_ACCESS_DIR}/fileAccess_writeOutput.f90 +# CACHE INTERNAL "FILE_ACCESS_INTERFACE") +# set(JOB_INTERFACE +# ${JOB_ACTOR_DIR}/gru_struc.f90 +# CACHE INTERNAL "JOB_INTERFACE") +# set(GRU_INTERFACE +# ${GRU_ACTOR_DIR}/gru_interface.f90 +# CACHE INTERNAL "GRU_INTERFACE") +# set(HRU_INTERFACE +# ${HRU_ACTOR_DIR}/hru_init.f90 +# ${HRU_ACTOR_DIR}/hru_read.f90 +# ${HRU_ACTOR_DIR}/hru_modelRun.f90 +# ${HRU_ACTOR_DIR}/hru_writeOutput.f90 +# ${HRU_ACTOR_DIR}/hru_interface.f90 +# CACHE INTERNAL "HRU_INTERFACE") -# Actors actual actor modules -set(ACTORS_GLOBAL - ${ACTORS_DIR}/global/auxiliary.cpp - ${ACTORS_DIR}/global/fileManager.cpp - ${ACTORS_DIR}/global/settings_functions.cpp - ${ACTORS_DIR}/global/timing_info.cpp - ${ACTORS_DIR}/global/logger.cpp - CACHE INTERNAL "ACTORS_GLOBAL") -set(SYS_INIT - ${SYS_INIT_DIR}/batch.cpp - ${SYS_INIT_DIR}/batch_container.cpp - # ${SYS_INIT_DIR}/client.cpp - # ${SYS_INIT_DIR}/client_container.cpp - ${SYS_INIT_DIR}/summa_global_data.cpp - ${SYS_INIT_DIR}/summa_actor.cpp - # ${SYS_INIT_DIR}/summa_backup_server.cpp - # ${SYS_INIT_DIR}/summa_client.cpp - # ${SYS_INIT_DIR}/summa_server.cpp - CACHE INTERNAL "SYS_INIT") -set(FILE_ACCESS_ACTOR - ${FILE_ACCESS_DIR}/summa_init_struc.cpp - ${FILE_ACCESS_DIR}/file_access_actor.cpp - ${FILE_ACCESS_DIR}/forcing_file_info.cpp - ${FILE_ACCESS_DIR}/output_container.cpp - CACHE INTERNAL "FILE_ACCESS_ACTOR") -set(JOB_ACTOR - ${JOB_ACTOR_DIR}/gru_struc.cpp - ${JOB_ACTOR_DIR}/job_actor.cpp - # ${JOB_ACTOR_DIR}/distributed_job_actor.cpp - # ${JOB_ACTOR_DIR}/node_actor.cpp - CACHE INTERNAL "JOB_ACTOR") -set(GRU_ACTOR - ${GRU_ACTOR_DIR}/gru_actor.cpp - CACHE INTERNAL "GRU_ACTOR") -set(HRU_ACTOR - ${HRU_ACTOR_DIR}/hru_utils.cpp - ${HRU_ACTOR_DIR}/hru_actor.cpp - ${HRU_ACTOR_DIR}/hru_batch_actor.cpp - CACHE INTERNAL "HRU_ACTOR") \ No newline at end of file +# # Actors actual actor modules +# set(ACTORS_GLOBAL +# ${ACTORS_DIR}/global/auxiliary.cpp +# ${ACTORS_DIR}/global/fileManager.cpp +# ${ACTORS_DIR}/global/settings_functions.cpp +# ${ACTORS_DIR}/global/timing_info.cpp +# ${ACTORS_DIR}/global/logger.cpp +# CACHE INTERNAL "ACTORS_GLOBAL") +# set(SYS_INIT +# ${SYS_INIT_DIR}/batch.cpp +# ${SYS_INIT_DIR}/batch_container.cpp +# # ${SYS_INIT_DIR}/client.cpp +# # ${SYS_INIT_DIR}/client_container.cpp +# ${SYS_INIT_DIR}/summa_global_data.cpp +# ${SYS_INIT_DIR}/summa_actor.cpp +# # ${SYS_INIT_DIR}/summa_backup_server.cpp +# # ${SYS_INIT_DIR}/summa_client.cpp +# # ${SYS_INIT_DIR}/summa_server.cpp +# CACHE INTERNAL "SYS_INIT") +# set(FILE_ACCESS_ACTOR +# ${FILE_ACCESS_DIR}/summa_init_struc.cpp +# ${FILE_ACCESS_DIR}/file_access_actor.cpp +# ${FILE_ACCESS_DIR}/forcing_file_info.cpp +# ${FILE_ACCESS_DIR}/output_container.cpp +# CACHE INTERNAL "FILE_ACCESS_ACTOR") +# set(JOB_ACTOR +# ${JOB_ACTOR_DIR}/gru_struc.cpp +# ${JOB_ACTOR_DIR}/job_actor.cpp +# # ${JOB_ACTOR_DIR}/distributed_job_actor.cpp +# # ${JOB_ACTOR_DIR}/node_actor.cpp +# CACHE INTERNAL "JOB_ACTOR") +# set(GRU_ACTOR +# ${GRU_ACTOR_DIR}/gru_actor.cpp +# CACHE INTERNAL "GRU_ACTOR") +# set(HRU_ACTOR +# ${HRU_ACTOR_DIR}/hru_utils.cpp +# ${HRU_ACTOR_DIR}/hru_actor.cpp +# ${HRU_ACTOR_DIR}/hru_batch_actor.cpp +# CACHE INTERNAL "HRU_ACTOR") \ No newline at end of file From 0d0953d8bc94dfed881d1c6359d4898e06f38c1b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 20 Jun 2024 14:33:26 +0900 Subject: [PATCH 1341/1472] cmake example --- build/cmake/build.cluster.bash | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/cmake/build.cluster.bash b/build/cmake/build.cluster.bash index c87cdf18e..4a1b4f1bf 100755 --- a/build/cmake/build.cluster.bash +++ b/build/cmake/build.cluster.bash @@ -8,6 +8,8 @@ module load openblas/0.3.24 module load openmpi/4.1.5 module load netcdf-fortran/4.6.1 +export CMAKE_PREFIX_PATH=../../../sundials/instdir/ + export FLAGS_OPT="-flto=1;-fuse-linker-plugin" cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON From da875afcf3a461aaa24ebba708c9a61902de98d3 Mon Sep 17 00:00:00 2001 From: Kyle Klenk Date: Thu, 20 Jun 2024 11:31:19 -0600 Subject: [PATCH 1342/1472] Remove left over actors commands from CMake. Left instructions and link to Summa-Actors --- build/CMakeLists.txt | 9 +-------- build/source/CMakeLists.txt | 17 ++++------------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 138c5c362..e13e37942 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -11,7 +11,7 @@ endif() # SUMMA can be compiled with debug flags by specifiying -DCMAKE_BUILD_TYPE=Debug # # There are multiple options for building SUMMA, including using the -# Sundials suite of solvers, the C++ Actor framework, and the NextGen framework. +# Sundials suite of solvers and the NextGen framework. # The options are set by specifying -DOPTION=ON when running cmake. # For example to compile SUMMA with Sundials, use # cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON @@ -54,9 +54,6 @@ endif() if (USE_NEXTGEN) message("ENABLING NEXTGEN") - if (USE_ACTORS) - message(FATAL_ERROR "NextGen incompatible with Summa-Actors") - endif() add_compile_definitions(NGEN_ACTIVE BMI_ACTIVE NGEN_FORCING_ACTIVE NGEN_OUTPUT_ACTIVE) add_subdirectory(../iso_c_fortran_bmi ${CMAKE_BINARY_DIR}/iso_c_bmi) @@ -128,11 +125,7 @@ add_subdirectory(${F_MASTER}/build/source/) #========================================================================================= set(COMM_ALL ${NRPROC} ${HOOKUP} ${DATAMS} ${UTILMS}) - set(SUMMA_ALL ${NETCDF} ${PRELIM} ${MODRUN} ${SOLVER} ${DRIVER}) - -set(SUMMA_ALL ${SUMMA_ALL} ${PRELIM_NOT_ACTORS} ${MODRUN_NOT_ACTORS} - ${SOLVER_NOT_ACTORS} ${DRIVER_NOT_ACTORS}) set(MAIN_SUMMA ${DRIVER_DIR}/summa_driver.f90) if (USE_NEXTGEN) diff --git a/build/source/CMakeLists.txt b/build/source/CMakeLists.txt index ddf4d84a7..a9f885857 100644 --- a/build/source/CMakeLists.txt +++ b/build/source/CMakeLists.txt @@ -46,9 +46,6 @@ set(DATAMS_SUNDIALS ${DSHARE_DIR}/type4ida.f90 ${DSHARE_DIR}/type4kinsol.f90 CACHE INTERNAL "DATAMS_SUNDIALS") -set(DATAMS_ACTORS - ${ACTORS_DIR}/global/actor_data_types.f90 - CACHE INTERNAL "DATAMS_ACTORS") # Utility modules set(UTILMS @@ -98,6 +95,7 @@ set(MODRUN ${ENGINE_DIR}/layerMerge.f90 ${ENGINE_DIR}/layerDivide.f90 ${ENGINE_DIR}/qTimeDelay.f90 + ${ENGINE_DIR}/read_force.f90 ${ENGINE_DIR}/snowAlbedo.f90 ${ENGINE_DIR}/snwCompact.f90 ${ENGINE_DIR}/tempAdjust.f90 @@ -105,9 +103,6 @@ set(MODRUN ${ENGINE_DIR}/var_derive.f90 ${ENGINE_DIR}/volicePack.f90 CACHE INTERNAL "MODRUN") -set(MODRUN_NOT_ACTORS - ${ENGINE_DIR}/read_force.f90 - CACHE INTERNAL "MODRUN_NOT_ACTORS") set(MODRUN_SUNDIALS ${ENGINE_DIR}/tol4ida.f90 ${ENGINE_DIR}/updateVarsWithPrime.f90 @@ -127,6 +122,8 @@ set(SOLVER ${ENGINE_DIR}/eval8summa.f90 ${ENGINE_DIR}/groundwatr.f90 ${ENGINE_DIR}/opSplittin.f90 + ${ENGINE_DIR}/run_oneGRU.f90 + ${ENGINE_DIR}/run_oneHRU.f90 ${ENGINE_DIR}/snowLiqFlx.f90 ${ENGINE_DIR}/soilLiqFlx.f90 ${ENGINE_DIR}/ssdNrgFlux.f90 @@ -139,10 +136,6 @@ set(SOLVER ${ENGINE_DIR}/vegPhenlgy.f90 ${ENGINE_DIR}/vegSWavRad.f90 CACHE INTERNAL "SOLVER") -set(SOLVER_NOT_ACTORS - ${ENGINE_DIR}/run_oneGRU.f90 - ${ENGINE_DIR}/run_oneHRU.f90 - CACHE INTERNAL "SOLVER_NOT_ACTORS") set(SOLVER_SUNDIALS ${ENGINE_DIR}/computJacobWithPrime.f90 ${ENGINE_DIR}/computResidWithPrime.f90 @@ -158,15 +151,13 @@ set(DRIVER ${DRIVER_DIR}/summa_restart.f90 ${DRIVER_DIR}/summa_alarms.f90 ${DRIVER_DIR}/summa_globalData.f90 - CACHE INTERNAL "DRIVER") -set(DRIVER_NOT_ACTORS ${DRIVER_DIR}/summa_util.f90 ${DRIVER_DIR}/summa_defineOutput.f90 ${DRIVER_DIR}/summa_init.f90 ${DRIVER_DIR}/summa_forcing.f90 ${DRIVER_DIR}/summa_modelRun.f90 ${DRIVER_DIR}/summa_writeOutput.f90 - CACHE INTERNAL "DRIVER_NOT_ACTORS") + CACHE INTERNAL "DRIVER") set(DRIVER_NEXTGEN ${DRIVER_DIR}/summa_bmi.f90 CACHE INTERNAL "DRIVER_NEXTGEN") From e4b226f7e057088ce9c5cd5a09cc938f9be1d769 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 28 Jun 2024 11:09:52 +0900 Subject: [PATCH 1343/1472] just utils --- utils/hist_per_GRU.py | 36 ++++++++++++++++++++++++------------ utils/plot_per_GRUMult.py | 6 +++--- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index b5cdc92ae..5f8c2b4dc 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -25,7 +25,7 @@ num_bins = 1000 do_rel = True # plot relative to the benchmark simulation -testing = True +testing = False do_hist = False # plot histogram instead of CDF if testing: stat = 'rmnz' @@ -54,13 +54,17 @@ def power_transform(x): # Simulation statistics file locations use_vars = [1] +rep = [0] # mark the repeats #use_vars = [0,1,2,3,4] +#rep = [0,0,0,0,0] # mark the repeats settings0= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] settings = [settings0[i] for i in use_vars] -use_vars2 = [3,8] +use_vars2 = [3,3,8] +rep2 = [1,2,0] # mark the repeats #use_vars2 = [8] +#rep2 = [0] # mark the repeats settings20= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime'] settings2 = [settings20[i] for i in use_vars2] @@ -97,15 +101,15 @@ def power_transform(x): if stat == 'rmse': stat2 = 'mean' maxes = [2,15,250,0.08,200,20e-3] - if do_rel: maxes = [0.6,0.02,0.6,0.3,3.0,20e-3] + if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,20e-3] if stat == 'rmnz': stat2 = 'mean' maxes = [2,15,250,0.08,200,20e-3] - if do_rel: maxes = [0.6,0.02,0.6,0.3,3.0,20e-3] + if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,20e-3] if stat == 'maxe': stat2 = 'amax' maxes = [15,25,0.8,2,0.3,2.0] - if do_rel: maxes = [0.6,0.02,0.6,0.3,3.0,2.0] + if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,2.0] if stat == 'kgem': stat2 = 'mean' maxes = [0.9,0.9,0.9,0.9,0.9,20e-3] @@ -116,6 +120,9 @@ def power_transform(x): if stat2 == 'amax': maxes2 = [1e-2,1e4,1e4,1e3]+[1e-11,1e-6,1e-7,1e-8] + [2.0] maxes2 = [maxes2[i] for i in use_vars2] +for i in range(len(maxes2)): + if rep2[i]==2: maxes2[i] = maxes2[i]*1e2 #clunky way to increase the range for the second repeat + summa = {} summa1 = {} @@ -146,10 +153,12 @@ def power_transform(x): fig.subplots_adjust(hspace=0.33, wspace=0.17) # Adjust the bottom margin, vertical space, and horizontal space #fig.suptitle('Histograms of Hourly Statistics for each GRU', fontsize=40,y=1.0) -def run_loop(i,var,mx): +def run_loop(i,var,mx,rep): r = i//2 c = i-r*2 stat0 = stat + if rep == 1: stat0 = 'rmnz' + if rep == 2: stat0 = 'maxe' if stat == 'rmse' or stat == 'kgem': if var == 'wallClockTime': stat0 = 'mean' statr = 'mean_ben' @@ -213,6 +222,7 @@ def run_loop(i,var,mx): axs[r,c].legend(plt_name) axs[r,c].set_title(plt_titl[i]) + if rep>0: axs[r,c].set_title(plt_titl[i] + ' '+ stat_word) if stat == 'rmse' or stat == 'rmnz' or stat == 'maxe': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl[i])) if stat == 'kgem': axs[r,c].set_xlabel(stat_word) if do_rel and var!='wallClockTime': axs[r,c].set_xlabel('relative '+ stat_word) @@ -229,11 +239,12 @@ def run_loop(i,var,mx): if var=='scalarTotalSoilWat' or var=='wallClockTime': # Rotate x-axis labels for axs[2, 1] subplot axs[r, c].tick_params(axis='x', rotation=45) -def run_loopb(i,var,mx): +def run_loopb(i,var,mx,rep): r = (i+len(use_vars))//2 c = (i+len(use_vars))-r*2 - print(c) stat0 = stat2 + if rep == 1: stat0 = 'mean' + if rep == 2: stat0 = 'amax' if 'zoom' in fig_fil: mx = mx @@ -270,6 +281,7 @@ def run_loopb(i,var,mx): axs[r,c].legend(plt_name2) axs[r,c].set_title(plt_titl2[i]) + if rep>0: axs[r,c].set_title(plt_titl2[i] + ' '+ stat_word) axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_titl2[i])) if do_hist: @@ -287,11 +299,11 @@ def run_loopb(i,var,mx): axs[r, c].tick_params(axis='x', rotation=45) # Rotate x-axis labels for subplot if len(use_vars) > 0: - for i,(var,mx) in enumerate(zip(plot_vars,maxes)): - run_loop(i,var,mx) + for i,(var,mx,rep) in enumerate(zip(plot_vars,maxes,rep)): + run_loop(i,var,mx,rep) if len(use_vars2) > 0: - for i,(var,mx) in enumerate(zip(plot_vars2,maxes2)): - run_loopb(i,var,mx) + for i,(var,mx,rep) in enumerate(zip(plot_vars2,maxes2,rep2)): + run_loopb(i,var,mx,rep) # Remove the extra subplots if (len(plot_vars)+len(plot_vars2)) < 6: diff --git a/utils/plot_per_GRUMult.py b/utils/plot_per_GRUMult.py index 41c7a8c14..bef1f2df0 100644 --- a/utils/plot_per_GRUMult.py +++ b/utils/plot_per_GRUMult.py @@ -61,13 +61,13 @@ if stat == 'rmse': maxes = [2,15,250,0.08,200,10e-3] #[2,15,8e-6,0.08,6e-9,10e-3] #maxes = [0.25,2,30,0.01,30,2e-3] #[0.25,2,1e-6,0.01,1e-9,2e-3] - if do_rel: maxes = [0.6,0.02,0.6,0.3,3.0,10e-3] + if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] if stat == 'rmnz': maxes = [2,15,250,0.08,200,10e-3] - if do_rel: maxes = [0.6,0.02,0.6,0.3,3.0,10e-3] + if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] if stat == 'maxe': maxes = [15,25,0.8,2,0.3,0.2] #[15,25,25e-5,2,1e-7,0.2] - if do_rel: maxes = [0.6,0.02,0.6,0.3,3.0,0.2] + if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,0.2] if stat == 'kgem': maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] if stat == 'mean': From 1d8e2fbe2954af2879384bafb2d67de8f35078e4 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 28 Jun 2024 23:41:08 +0900 Subject: [PATCH 1344/1472] turn on logical(lgt),parameter :: updateCp_closedForm=.true. ! nrgConserv = closedForm flag to indicate if we update Cp at each step logical(lgt),parameter :: needCm_closedForm=.true. ! nrgConserv = closedForm flag to indicate if the energy equation contains Cm = dH_T/dTheta_m --- build/source/engine/eval8summa.f90 | 4 ++-- build/source/engine/eval8summaWithPrime.f90 | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 94560a772..cecedf6d5 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -219,8 +219,8 @@ subroutine eval8summa(& logical(lgt) :: updateStateCp ! flag to indicate if we update Cp at each step for LHS, set with nrgConserv choice and updateCp_closedForm flag logical(lgt) :: updateFluxCp ! flag to indicate if we update Cp at each step for RHS, set with nrgConserv choice and updateCp_closedForm flag logical(lgt) :: needStateCm ! flag to indicate if the energy equation contains LHS Cm = dH_T/dTheta_m,, set with nrgConserv choice and needStateCm_closedForm flag - logical(lgt),parameter :: updateCp_closedForm=.false. ! nrgConserv = closedForm flag to indicate if we update Cp at each step - logical(lgt),parameter :: needCm_closedForm=.false. ! nrgConserv = closedForm flag to indicate if the energy equation contains Cm = dH_T/dTheta_m + logical(lgt),parameter :: updateCp_closedForm=.true. ! nrgConserv = closedForm flag to indicate if we update Cp at each step + logical(lgt),parameter :: needCm_closedForm=.true. ! nrgConserv = closedForm flag to indicate if the energy equation contains Cm = dH_T/dTheta_m ! -------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index d7a99940c..fdf1875c3 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -213,8 +213,8 @@ subroutine eval8summaWithPrime(& logical(lgt) :: updateStateCp ! flag to indicate if we update Cp at each step for LHS, set with nrgConserv choice and updateCp_closedForm flag logical(lgt) :: updateFluxCp ! flag to indicate if we update Cp at each step for RHS, set with nrgConserv choice and updateCp_closedForm flag logical(lgt) :: needStateCm ! flag to indicate if the energy equation contains LHS Cm = dH_T/dTheta_m,, set with nrgConserv choice and needStateCm_closedForm flag - logical(lgt),parameter :: updateCp_closedForm=.false. ! nrgConserv = closedForm flag to indicate if we update Cp at each step - logical(lgt),parameter :: needCm_closedForm=.false. ! nrgConserv = closedForm flag to indicate if the energy equation contains Cm = dH_T/dTheta_m + logical(lgt),parameter :: updateCp_closedForm=.true. ! nrgConserv = closedForm flag to indicate if we update Cp at each step + logical(lgt),parameter :: needCm_closedForm=.true. ! nrgConserv = closedForm flag to indicate if the energy equation contains Cm = dH_T/dTheta_m ! -------------------------------------------------------------------------------------------------------------------------------- ! association to variables in the data structures From 56164f4d9c3d37543f6de693266a67d621cb74dd Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 1 Jul 2024 22:36:59 +0900 Subject: [PATCH 1345/1472] utils --- utils/scat_per_GRU.py | 57 ++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index 2b6994bb5..973dafc15 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -65,11 +65,13 @@ summa = {} summa1 = {} -for i, m in enumerate(method_name): - # Get the aggregated statistics of SUMMA simulations - summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) -for i, m in enumerate(method_name2): - summa1[m] = xr.open_dataset(viz_dir/viz_fl2[i]) +if do_vars: + for i, m in enumerate(method_name): + # Get the aggregated statistics of SUMMA simulations + summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) +if do_balance: + for i, m in enumerate(method_name2): + summa1[m] = xr.open_dataset(viz_dir/viz_fl2[i]) def run_loop(i,var,plt_t,leg_t,leg_t0,leg_tm): @@ -87,41 +89,61 @@ def run_loop(i,var,plt_t,leg_t,leg_t0,leg_tm): stat0_word = 'mean' # no 0s' statr = 'mnnz_ben' stat_word = 'RMSE' # no 0s' + if stat == 'maxe': stat0 = 'amax' stat0_word = 'max' statr = 'amax_ben' stat_word = 'max abs error' + if stat == 'mnnz': + stat0 = 'mnnz_ben' + stat0_word = 'mean - mean reference' # no 0s' + statr = 'mnnz_ben' + stat_word = 'mean' # no 0s' + + if stat == 'mean': + stat0 = 'mean_ben' + stat0_word = 'mean - mean reference' # no 0s' + statr = 'mean_ben' + stat_word = 'mean' # no 0s' + + if stat == 'amax': + stat0 = 'amax_ben' + stat0_word = 'max abs - max abs reference' + statr = 'amax_ben' + stat_word = 'max abs error' + # Data if do_rel: s_rel = summa[method_name[0]][var].sel(stat=statr) for m in method_name: s = summa[m][var].sel(stat=[stat,stat0]) - if do_rel and var != 'wallClockTime': s = s/s_rel + if do_rel and var != 'wallClockTime': s.loc[dict(stat=stat)] = s.loc[dict(stat=stat)]/s_rel if var == 'scalarTotalET' and not do_rel: - if stat =='rmse' or stat =='rmnz' : s = s*31557600 # make annual total - if stat =='maxe': s = s*3600 # make hourly max + if stat =='rmse' or stat =='rmnz' or stat=='mnnz' or stat=='mean': s = s*31557600 # make annual total + if stat =='maxe' or stat=='amax': s = s*3600 # make hourly max if var == 'averageRoutedRunoff'and not do_rel: - if stat =='rmse' or stat =='rmnz' : s = s*31557600*1000 # make annual total - if stat =='maxe': s = s*3600*1000 # make hourly max + if stat =='rmse' or stat =='rmnz' or stat=='mnnz 'or stat=='mean': s = s*31557600*1000 # make annual total + if stat =='maxe' or stat=='amax': s = s*3600*1000 # make hourly max if stat == 'maxe': s.loc[dict(stat='maxe')] = np.fabs(s.loc[dict(stat='maxe')]) # make absolute value norm - axs[r,c].scatter(x=np.fabs(s.sel(stat=stat).values),y=s.sel(stat=stat0).values,s=1,zorder=0,label=m) - + if stat=='mnnz' or stat=='mean' or stat=='amax': + axs[r,c].scatter(x=s.sel(stat=stat).values,y=s.sel(stat=stat).values-s.sel(stat=stat0).values,s=1,zorder=0,label=m) + else: + axs[r,c].scatter(x=np.fabs(s.sel(stat=stat).values),y=s.sel(stat=stat0).values,s=1,zorder=0,label=m) + lgnd = axs[r,c].legend(plt_name) for j, m in enumerate(plt_name): lgnd.legendHandles[j]._sizes = [80] axs[r,c].set_title(plt_t) - if stat == 'rmse' or stat == 'rmnz': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_t)) - if stat == 'maxe': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_tm)) + if stat == 'rmse' or stat == 'rmnz' or stat=='mnnz' or stat=='mean': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_t)) + if stat == 'maxe' or stat=='amax': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_tm)) if stat == 'kgem': axs[r,c].set_xlabel(stat_word) - #if do_rel and var!='wallClockTime': axs[r,c].set_xlabel(stat_word + ' rel to bench ' + stat0_word) if do_rel and var!='wallClockTime': axs[r,c].set_xlabel('relative '+ stat_word) axs[r,c].set_ylabel(stat0_word + ' [{}]'.format(leg_t0)) - #if do_rel and var!='wallClockTime': axs[r,c].set_ylabel(stat0_word + ' rel to bench ' + stat0_word) - if do_rel and var!='wallClockTime': axs[r,c].set_ylabel('relative '+ stat0_word) + #if do_rel and var!='wallClockTime': axs[r,c].set_ylabel('relative '+ stat0_word) def run_loopb(i,var,comp,leg_t,leg_t0,plt_t): @@ -174,6 +196,7 @@ def run_loopb(i,var,comp,leg_t,leg_t0,plt_t): leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$'] fig_fil = 'Hrly_diff_scat_{}_{}_compressed.png' + if do_rel: fig_fil = 'Hrly_diff_scat_{}_{}_rel_compressed.png' fig_fil = fig_fil.format(','.join(settings),stat) # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug From 5fd0a52c77bbfc0f63edb26c5a2d002e2b916eb6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 2 Jul 2024 23:47:08 +0900 Subject: [PATCH 1346/1472] utils --- utils/scat_per_GRU.py | 281 ++++++++++++++++++++++++++++++++---------- 1 file changed, 216 insertions(+), 65 deletions(-) diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index 973dafc15..0f7bf164a 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -19,12 +19,20 @@ import matplotlib.pyplot as plt import copy import pandas as pd +from matplotlib.colors import LogNorm +from matplotlib.colors import ListedColormap viz_dir = Path('/home/avanb/scratch/statistics') nbatch_hrus = 518 # number of HRUs per batch do_rel = False # plot relative to the benchmark simulation - -# which statistics to plot +do_heat = True # plot heatmaps instead of scatterplots +rainbow_cmap = plt.cm.get_cmap('rainbow', 256) # Get the rainbow colormap +rainbow_colors = rainbow_cmap(np.linspace(0, 1, 256)) +rainbow_colors_with_white = np.vstack((rainbow_colors, [1, 1, 1, 1])) +custom_cmap = ListedColormap(rainbow_colors_with_white, name='rainbow_white') +custom_cmap.set_under('white') # Ensure that values under the lower bound are white + +# which statistics to plot, can do both do_vars = True do_balance = True @@ -73,7 +81,7 @@ for i, m in enumerate(method_name2): summa1[m] = xr.open_dataset(viz_dir/viz_fl2[i]) -def run_loop(i,var,plt_t,leg_t,leg_t0,leg_tm): +def run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm): r = i//2 c = i-r*2 @@ -116,57 +124,167 @@ def run_loop(i,var,plt_t,leg_t,leg_t0,leg_tm): # Data if do_rel: s_rel = summa[method_name[0]][var].sel(stat=statr) - for m in method_name: + for j, m in enumerate(method_name): s = summa[m][var].sel(stat=[stat,stat0]) - if do_rel and var != 'wallClockTime': s.loc[dict(stat=stat)] = s.loc[dict(stat=stat)]/s_rel - - if var == 'scalarTotalET' and not do_rel: - if stat =='rmse' or stat =='rmnz' or stat=='mnnz' or stat=='mean': s = s*31557600 # make annual total - if stat =='maxe' or stat=='amax': s = s*3600 # make hourly max - if var == 'averageRoutedRunoff'and not do_rel: - if stat =='rmse' or stat =='rmnz' or stat=='mnnz 'or stat=='mean': s = s*31557600*1000 # make annual total - if stat =='maxe' or stat=='amax': s = s*3600*1000 # make hourly max + if var == 'scalarTotalET': + if stat =='rmse' or stat =='rmnz' or stat=='mnnz' or stat=='mean': + s = s*31557600 # make annual total + if do_rel: s_rel = s_rel*31557600 + if stat =='maxe' or stat=='amax': + s = s*3600 # make hourly max + if do_rel: s_rel = s_rel*3600 + if var == 'averageRoutedRunoff': + if stat =='rmse' or stat =='rmnz' or stat=='mnnz'or stat=='mean': + s = s*31557600*1000 # make annual total + if do_rel: s_rel = s_rel*31557600*1000 + if stat =='maxe' or stat=='amax': + s = s*3600*1000 # make hourly max + if do_rel: s_rel = s_rel*3600*1000 if stat == 'maxe': s.loc[dict(stat='maxe')] = np.fabs(s.loc[dict(stat='maxe')]) # make absolute value norm - if stat=='mnnz' or stat=='mean' or stat=='amax': - axs[r,c].scatter(x=s.sel(stat=stat).values,y=s.sel(stat=stat).values-s.sel(stat=stat0).values,s=1,zorder=0,label=m) - else: - axs[r,c].scatter(x=np.fabs(s.sel(stat=stat).values),y=s.sel(stat=stat0).values,s=1,zorder=0,label=m) - - lgnd = axs[r,c].legend(plt_name) - for j, m in enumerate(plt_name): - lgnd.legendHandles[j]._sizes = [80] - axs[r,c].set_title(plt_t) - if stat == 'rmse' or stat == 'rmnz' or stat=='mnnz' or stat=='mean': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_t)) - if stat == 'maxe' or stat=='amax': axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_tm)) + if do_rel and var != 'wallClockTime': + s.loc[dict(stat=stat)] = s.loc[dict(stat=stat)]/s_rel + stat_word = 'relative '+ stat_word + + if do_heat and j==0: + x_points = [] + y_points = [] + if stat=='mnnz' or stat=='mean' or stat=='amax': + x = s.sel(stat=stat).values + y = s.sel(stat=stat).values-s.sel(stat=stat0).values + else: + x = np.fabs(s.sel(stat=stat).values) + y = s.sel(stat=stat0).values + if lx: + x = np.where(x > 0, np.log10(x), np.nan) + stat_word = 'log10 ' + stat_word + if ly: + if stat!='mnnz' and stat!='mean': + if var == 'scalarTotalET': + y = np.where(-y > 0, np.log10(-y), np.nan) + stat0_word = 'log10 negative ' + stat0_word + else: + y = np.where(y > 0, np.log10(y), np.nan) + stat0_word = 'log10 ' + stat0_word + x_points.extend(x) + y_points.extend(y) + x_points = np.array(x_points) # Convert lists to numpy arrays + y_points = np.array(y_points) + + # Ensure no NaNs or infs before proceeding + is_valid = ~np.isnan(x_points) & ~np.isnan(y_points) & ~np.isinf(x_points) & ~np.isinf(y_points) + x_points = x_points[is_valid] + y_points = y_points[is_valid] + if not is_valid.any(): + print('no valid values') + continue + numbin = int(np.sqrt(x_points.size)/2) + 1 + print(var,'numbin', numbin) + + # Define the bins for the histogram and calculate + x_edges = np.linspace(x_points.min(), x_points.max(), num=numbin) + y_edges = np.linspace(y_points.min(), y_points.max(), num=numbin) + zi, _, _ = np.histogram2d(x_points, y_points, bins=[x_edges, y_edges]) + + # Calculate bin centers from edges for X and Y + x_centers = (x_edges[:-1] + x_edges[1:]) / 2 + y_centers = (y_edges[:-1] + y_edges[1:]) / 2 + X, Y = np.meshgrid(x_centers, y_centers) + + # Adjust the pcolormesh call to use the centers and compatible shading + norm = LogNorm(vmin=np.min(zi[zi > 0]), vmax=np.max(zi)) + mesh = axs[r, c].pcolormesh(X, Y, zi.T, shading='gouraud', cmap=custom_cmap, zorder=0,norm=norm) + fig.colorbar(mesh, ax=axs[r, c], label='GRU count') + + elif not do_heat: + if stat=='mnnz' or stat=='mean' or stat=='amax': + axs[r,c].scatter(x=s.sel(stat=stat).values,y=s.sel(stat=stat).values-s.sel(stat=stat0).values,s=1,zorder=0,label=m) + else: + axs[r,c].scatter(x=np.fabs(s.sel(stat=stat).values),y=s.sel(stat=stat0).values,s=1,zorder=0,label=m) + + if do_heat: + axs[r,c].set_title(plt_t + ' '+ plt_name[0] + ' heatmap') + elif not(do_heat): + lgnd = axs[r,c].legend(plt_name) + for j, m in enumerate(plt_name): + lgnd.legendHandles[j]._sizes = [80] + axs[r,c].set_title(plt_t) + if do_rel: + axs[r,c].set_xlabel(stat_word) + else: + if stat == 'rmse' or stat == 'rmnz' or stat=='mnnz' or stat=='mean': + axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_t)) + if stat == 'maxe' or stat=='amax': + axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_tm)) if stat == 'kgem': axs[r,c].set_xlabel(stat_word) - if do_rel and var!='wallClockTime': axs[r,c].set_xlabel('relative '+ stat_word) - axs[r,c].set_ylabel(stat0_word + ' [{}]'.format(leg_t0)) - #if do_rel and var!='wallClockTime': axs[r,c].set_ylabel('relative '+ stat0_word) - -def run_loopb(i,var,comp,leg_t,leg_t0,plt_t): +def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t): r = i//2 c = i-r*2 if stat == 'rmse' or stat == 'kgem': stat0 = 'mean' - word = ' mean' + wordx = ' mean' if stat == 'rmnz': stat0 = 'mean' - word = ' mean' # no 0s' + wordx = ' mean' # no 0s' if stat == 'maxe': stat0 = 'amax' - word = ' max' + wordx = ' max' + wordy = np.copy(wordx) # Data - for m in method_name2: + for j, m in enumerate(method_name2): # Get the statistics, remove 9999 (should be nan, but just in case) s0 = np.fabs(summa1[m][comp].sel(stat=stat0)).where(lambda x: x != 9999) s = np.fabs(summa1[m][var].sel(stat=stat0)).where(lambda x: x != 9999) - axs[r,c].scatter(x=s.values,y=s0.values,s=10,zorder=0,label=m) + if do_heat and j==0: + x_points = [] + y_points = [] + x = s.values + y = s0.values + x_points.extend(x) + y_points.extend(y) + x_points = np.array(x_points) # Convert lists to numpy arrays + y_points = np.array(y_points) + if lx: + x = np.log10(x[x > 0]) + wordx = 'log10 ' + wordx + if ly: + y = np.log10(y[y > 0]) + wordy = 'log10 ' + wordy + + x_points.extend(x) + y_points.extend(y) + x_points = np.array(x_points) # Convert lists to numpy arrays + y_points = np.array(y_points) + + # Ensure no NaNs or infs before proceeding + is_valid = ~np.isnan(x_points) & ~np.isnan(y_points) & ~np.isinf(x_points) & ~np.isinf(y_points) + x_points = x_points[is_valid] + y_points = y_points[is_valid] + numbin = int(np.sqrt(x_points.size)/2) + 1 + print(var,'numbin', numbin) + + # Define the bins for the histogram and calculate + x_edges = np.linspace(x_points.min(), x_points.max(), num=numbin) + y_edges = np.linspace(y_points.min(), y_points.max(), num=numbin) + zi, _, _ = np.histogram2d(x_points, y_points, bins=[x_edges, y_edges]) + + # Calculate bin centers from edges for X and Y + x_centers = (x_edges[:-1] + x_edges[1:]) / 2 + y_centers = (y_edges[:-1] + y_edges[1:]) / 2 + X, Y = np.meshgrid(x_centers, y_centers) + + # Adjust the pcolormesh call to use the centers and compatible shading + norm = LogNorm(vmin=np.min(zi[zi > 0]), vmax=np.max(zi)) + mesh = axs[r, c].pcolormesh(X, Y, zi.T, shading='gouraud', cmap=custom_cmap, zorder=0,norm=norm) + fig.colorbar(mesh, ax=axs[r, c], label='GRU count') + + elif not do_heat: + axs[r,c].scatter(x=s.values,y=s0.values,s=10,zorder=0,label=m) if comp == 'numberFluxCalc': stat0_word = 'number flux calculations' @@ -174,29 +292,48 @@ def run_loopb(i,var,comp,leg_t,leg_t0,plt_t): else: stat0_word = 'balance abs value' stat_word = 'balance abs value' - - lgnd = axs[r,c].legend(plt_name2) - for j, m in enumerate(plt_name2): - lgnd.legendHandles[j]._sizes = [80] - axs[r,c].set_title(plt_t) - axs[r,c].set_xscale('log') - if comp != 'numberFluxCalc': axs[r,c].set_yscale('log') - axs[r,c].set_xlabel(stat_word + word + ' [{}]'.format(leg_t)) - axs[r,c].set_ylabel(stat0_word + word + ' [{}]'.format(leg_t0)) + + if do_heat: + axs[r,c].set_title(plt_t + ' '+ plt_name[0] + ' heatmap') + elif not(do_heat): + lgnd = axs[r,c].legend(plt_name2) + for j, m in enumerate(plt_name2): + lgnd.legendHandles[j]._sizes = [80] + axs[r,c].set_title(plt_t) + axs[r,c].set_xscale('log') + if comp != 'numberFluxCalc': axs[r,c].set_yscale('log') + axs[r,c].set_xlabel(stat_word + wordx + ' [{}]'.format(leg_t)) + axs[r,c].set_ylabel(stat0_word + wordy + ' [{}]'.format(leg_t0)) plt.rcParams['xtick.color'] = 'black' plt.rcParams['xtick.major.width'] = 2 plt.rcParams['ytick.color'] = 'black' plt.rcParams['ytick.major.width'] = 2 if do_vars: - - plt_titl = ['(a) snow water equivalent','(b) total soil water content','(c) total evapotranspiration', '(d) total water on the vegetation canopy','(e) average routed runoff'] - leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$'] - leg_titl0 = ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$'] - leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$'] - - fig_fil = 'Hrly_diff_scat_{}_{}_compressed.png' - if do_rel: fig_fil = 'Hrly_diff_scat_{}_{}_rel_compressed.png' + # Specify variables of interest + use_vars = [1,2,4] + #logx = [0,0,0] # log scale x axis + #logy = [0,0,1] # log scale y axis + use_vars = [0,1,2,3,4] + logx = np.ones(len(use_vars)) # log scale x axis + logy = np.ones(len(use_vars)) # log scale y axis + + plot_vars = settings + plt_titl = ['snow water equivalent','total soil water content','total evapotranspiration', 'total water on the vegetation canopy','average routed runoff'] + leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$mm~y^{-1}$'] + leg_titl0 = ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$mm~y^{-1}$'] + leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$','$mm~h^{-1}$'] + + plot_vars = [plot_vars[i] for i in use_vars] + plt_titl = [f"({chr(97+n)}) {plt_titl[i]}" for n,i in enumerate(use_vars)] + leg_titl = [leg_titl[i] for i in use_vars] + leg_titl0 = [leg_titl0[i] for i in use_vars] + leg_titlm = [leg_titlm[i] for i in use_vars] + + fig_fil = 'Hrly_diff_scat_{}_{}' + if do_rel: fig_fil = fig_fil + '_rel' + if do_heat: fig_fil = method_name[0]+fig_fil + '_heat' + fig_fil = fig_fil + '_compressed.png' fig_fil = fig_fil.format(','.join(settings),stat) # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug @@ -212,28 +349,40 @@ def run_loopb(i,var,comp,leg_t,leg_t0,plt_t): #fig.suptitle('Hourly Errors and Values for each GRU', fontsize=40) fig.subplots_adjust(hspace=0.33, wspace=0.17) # Adjust the bottom margin, vertical space, and horizontal space - # Specify variables of interest - plot_vars = ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff'] - for i,(var,plt_t,leg_t,leg_t0,leg_tm) in enumerate(zip(plot_vars,plt_titl,leg_titl,leg_titl0,leg_titlm)): - run_loop(i,var,plt_t,leg_t,leg_t0,leg_tm) + for i,(var,lx,ly,plt_t,leg_t,leg_t0,leg_tm) in enumerate(zip(plot_vars,logx,logy,plt_titl,leg_titl,leg_titl0,leg_titlm)): + run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm) - # Remove the sixth subplot - fig.delaxes(axs[2, 1]) + # Remove the extra subplots + if (len(plot_vars)) < 6: + for i in range(len(plot_vars),6): + r = i//2 + c = i-r*2 + fig.delaxes(axs[r, c]) # Save plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) if do_balance: # Specify variables of interest + use_vars = [0,1,2,3] + logx = np.ones(len(use_vars)) # log scale x axis + logy = np.ones(len(use_vars)) # log scale y axis + plot_vars = ['balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceCasNrg','wallClockTime'] comp_vars = ['balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','numberFluxCalc'] - - plt_titl = ['(a) vegetation balance','(b) snow balance','(c) soil balance', '(d) canopy air space and aquifer balance', '(f) wall clock time'] + plt_titl = ['vegetation balance','snow balance','soil balance', 'canopy air space and aquifer balance', 'wall clock time'] leg_titl = ['$W~m^{-3}$'] * 4 + ['$s$'] leg_titl0 =['$kg~m^{-2}~s^{-1}$'] * 4 + ['$num$'] - fig_fil = 'balance_scat_{}_compressed.png' - if do_rel: fig_fil = 'balance_scat_{}_rel_compressed.png' + plot_vars = [plot_vars[i] for i in use_vars] + comp_vars = [comp_vars[i] for i in use_vars] + plt_titl = [f"({chr(97+n)}) {plt_titl[i]}" for n,i in enumerate(use_vars)] + leg_titl = [leg_titl[i] for i in use_vars] + leg_titl0 = [leg_titl0[i] for i in use_vars] + + fig_fil = 'balance_scat_{}' + if do_heat: fig_fil = method_name[0]+fig_fil + '_heat' + fig_fil = fig_fil + '_compressed.png' fig_fil = fig_fil.format(stat) if 'compressed' in fig_fil: @@ -248,11 +397,13 @@ def run_loopb(i,var,comp,leg_t,leg_t0,plt_t): fig.subplots_adjust(hspace=0.33, wspace=0.17) # Adjust the bottom margin, vertical space, and horizontal space #fig.suptitle('Scatterplot of Hourly Statistics for each GRU', fontsize=40,y=1.0) - for i,(var,comp,leg_t,leg_t0,plt_t) in enumerate(zip(plot_vars,comp_vars,leg_titl,leg_titl0,plt_titl)): - run_loopb(i,var,comp,leg_t,leg_t0,plt_t) - - # Remove the sixth subplot - fig.delaxes(axs[2, 1]) + for i,(var,comp,lx,ly,leg_t,leg_t0,plt_t) in enumerate(zip(plot_vars,comp_vars,logx,logy,leg_titl,leg_titl0,plt_titl)): + run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t) + if (len(plot_vars)) < 6: + for i in range(len(plot_vars),6): + r = i//2 + c = i-r*2 + fig.delaxes(axs[r, c]) # Save plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) From c463f4c41d7943fb9c80c43953e14201f24497ca Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 4 Jul 2024 13:39:34 +0900 Subject: [PATCH 1347/1472] utils --- utils/concat_groups_split_summa.py | 6 +- utils/hist_per_GRU.py | 9 +- utils/largest_error_attrib.py | 4 +- utils/scat_per_GRU.py | 292 +++++++++++++++++++++++------ utils/steps_per_GRU.py | 4 +- utils/timeseries_to_statistics.py | 4 +- utils/wallClock_per_GRU.py | 4 +- utils/wallStd_per_GRU.py | 4 +- 8 files changed, 247 insertions(+), 80 deletions(-) diff --git a/utils/concat_groups_split_summa.py b/utils/concat_groups_split_summa.py index 1a182ed9e..b14700e93 100644 --- a/utils/concat_groups_split_summa.py +++ b/utils/concat_groups_split_summa.py @@ -18,8 +18,8 @@ missgru = 72055933 # batch 205 summa-be32 value misshru = missgru # could be different -testing = False -if testing: +run_local = False +if run_local: top_fold = '/Users/amedin/Research/USask/test_py/' method_name = 'sundials_1en8' else: @@ -138,7 +138,7 @@ def get_stat(g,catby_num,outfilelist0,ctdir): # -- end functions -if testing: +if run_local: # -- no parallel processing for g in range(0,int(len(outfilelist0)/catby_num)): get_stat(g,catby_num,outfilelist0,ctdir) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 5f8c2b4dc..99a52a4fc 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -25,9 +25,9 @@ num_bins = 1000 do_rel = True # plot relative to the benchmark simulation -testing = False +run_local = False do_hist = False # plot histogram instead of CDF -if testing: +if run_local: stat = 'rmnz' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') method_name=['be1en'] @@ -123,7 +123,6 @@ def power_transform(x): for i in range(len(maxes2)): if rep2[i]==2: maxes2[i] = maxes2[i]*1e2 #clunky way to increase the range for the second repeat - summa = {} summa1 = {} if len(use_vars)>0: @@ -229,7 +228,7 @@ def run_loop(i,var,mx,rep): if do_hist: axs[r,c].set_ylabel('GRU count') - if var != 'wallClockTime' and not testing: axs[r,c].set_ylim([0, 25000]) + if var != 'wallClockTime' and not run_local: axs[r,c].set_ylim([0, 25000]) else: axs[r,c].set_ylabel('cumulative distribution') @@ -287,7 +286,7 @@ def run_loopb(i,var,mx,rep): if do_hist: axs[r,c].set_ylabel('GRU count') if(c==1): axs[r, c].set_ylabel('') - if var != 'wallClockTime' and not testing: axs[r,c].set_ylim([0, 25000]) + if var != 'wallClockTime' and not run_local: axs[r,c].set_ylim([0, 25000]) else: axs[r,c].set_ylabel('cumulative distribution') diff --git a/utils/largest_error_attrib.py b/utils/largest_error_attrib.py index 92ea8a89e..202a66409 100644 --- a/utils/largest_error_attrib.py +++ b/utils/largest_error_attrib.py @@ -11,8 +11,8 @@ nBig = 10 do_rel = True # plot relative to the benchmark simulation -testing = False -if testing: +run_local = False +if run_local: top_fold = '/Users/amedin/Research/USask/test_py/' attr_fold = '/Users/amedin/Research/USask/test_py/settings/' method_name= 'be1' diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index 0f7bf164a..6b6d2d903 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -34,14 +34,14 @@ # which statistics to plot, can do both do_vars = True -do_balance = True +do_balance = False -testing = False -if testing: +run_local = True +if run_local: stat = 'rmnz' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be1en'] - plt_name=['BE1 mixed'] + method_name=['be1','sundials_1en6'] + plt_name=['BE1','SUNDIALS'] method_name2=method_name plt_name2=plt_name @@ -52,7 +52,7 @@ #method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument #plt_name=['BE1','IDAe-4','BE4','BE8','BE16','BE32','IDAe-6'] #maybe make this an argument #method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument - #plt_name=['BE1','BE16','BE32','IDAe-6'] #maybe make this an argument + #plt_name=['BE1','BE16','BE32','SUNDIALS'] #maybe make this an argument method_name=['be1','be1cm','be1en','sundials_1en6cm'] plt_name=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp'] method_name2=method_name+['sundials_1en8cm'] @@ -60,6 +60,12 @@ if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE +# Copy the original method names and plot names +method_name0 = np.copy(method_name) +plt_name0 = np.copy(plt_name) +method_name20 = np.copy(method_name2) +plt_name20 = np.copy(plt_name2) + # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] viz_fil = method_name.copy() @@ -77,14 +83,24 @@ for i, m in enumerate(method_name): # Get the aggregated statistics of SUMMA simulations summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) + hru_size = summa[m].sizes['hru'] if do_balance: for i, m in enumerate(method_name2): summa1[m] = xr.open_dataset(viz_dir/viz_fl2[i]) - -def run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm): + hru_size = summa1[m].sizes['hru'] +numbin = int(np.sqrt(hru_size/10)) +maxcol = numbin**2/75 +do_clip = True # choose if want the heat values clipped (True) or plotted as white if over maxcol (False) + +def run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm,rep,mx): r = i//2 c = i-r*2 + global method_name # Declare method_name as global to modify its global value + global plt_name # Declare plt_name as global to modify its global value + method_name = np.copy(method_name0) + plt_name = np.copy(plt_name0) + if stat == 'rmse' or stat == 'kgem': stat0 = 'mean' stat0_word = 'mean' @@ -122,31 +138,94 @@ def run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm): statr = 'amax_ben' stat_word = 'max abs error' - # Data + # Data if do_rel: s_rel = summa[method_name[0]][var].sel(stat=statr) + # make the axes the same + if do_heat: + do_same = False + if len(method_name)>1: + do_same = True + mxx = 0.0 + mnx = 1.0 + for m in method_name: + # Get the statistics, remove 9999 (should be nan, but just in case) + s0 = summa[m][var].sel(stat=stat).where(lambda x: x != 9999) + if do_rel: s0=s0/s_rel + if stat=='mnnz' or stat=='mean' or stat=='amax': + s = s0 + else: + s = np.fabs(s0) + mxx = max(s.max(),mxx) + mnx = min(s.min(),mnx) + mxy = 0.0 + mny = 1.0 + for m in method_name: + # Get the statistics, remove 9999 (should be nan, but just in case) + s0 = summa[m][var].sel(stat=[stat,stat0]).where(lambda x: x != 9999) + if stat=='mnnz' or stat=='mean' or stat=='amax': + s = s0.sel(stat=stat) - s0.sel(stat=stat0) + else: + s = s0.sel(stat=stat0) + mxy = max(s.max(),mxy) + mny = min(s.min(),mny) + if rep: + method_name = [method_name0[1]] + plt_name = [plt_name0[1]] + else: + method_name = [method_name0[0]] + plt_name = [plt_name0[0]] + else: # only one method + mxx = 0.0 + mnx = 1.0 + mxy = 0.0 + mny = 1.0 for j, m in enumerate(method_name): s = summa[m][var].sel(stat=[stat,stat0]) if var == 'scalarTotalET': if stat =='rmse' or stat =='rmnz' or stat=='mnnz' or stat=='mean': s = s*31557600 # make annual total - if do_rel: s_rel = s_rel*31557600 + if do_rel: + s_rel = s_rel*31557600 + else: + mnx = mnx*31557600 + mxx = mxx*31557600 + mny = mny*31557600 + mxy = mxy*31557600 if stat =='maxe' or stat=='amax': s = s*3600 # make hourly max - if do_rel: s_rel = s_rel*3600 + if do_rel: + s_rel = s_rel*3600 + else: + mnx = mnx*3600 + mxx = mxx*3600 + mny = mny*3600 + mxy = mxy*3600 if var == 'averageRoutedRunoff': if stat =='rmse' or stat =='rmnz' or stat=='mnnz'or stat=='mean': s = s*31557600*1000 # make annual total - if do_rel: s_rel = s_rel*31557600*1000 + if do_rel: + s_rel = s_rel*31557600*1000 + else: + mnx = mnx*31557600*1000 + mxx = mxx*31557600*1000 + mny = mny*31557600*1000 + mxy = mxy*31557600*1000 if stat =='maxe' or stat=='amax': s = s*3600*1000 # make hourly max - if do_rel: s_rel = s_rel*3600*1000 + if do_rel: + s_rel = s_rel*3600*1000 + else: + mnx = mnx*3600*1000 + mxx = mxx*3600*1000 + mny = mny*3600*1000 + mxy = mxy*3600*1000 if stat == 'maxe': s.loc[dict(stat='maxe')] = np.fabs(s.loc[dict(stat='maxe')]) # make absolute value norm if do_rel and var != 'wallClockTime': s.loc[dict(stat=stat)] = s.loc[dict(stat=stat)]/s_rel stat_word = 'relative '+ stat_word - if do_heat and j==0: + if do_heat: x_points = [] y_points = [] if stat=='mnnz' or stat=='mean' or stat=='amax': @@ -157,15 +236,18 @@ def run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm): y = s.sel(stat=stat0).values if lx: x = np.where(x > 0, np.log10(x), np.nan) - stat_word = 'log10 ' + stat_word + mnx = np.log10(mnx+1e-30) + mxx = np.log10(mxx+1e-30) if ly: if stat!='mnnz' and stat!='mean': if var == 'scalarTotalET': y = np.where(-y > 0, np.log10(-y), np.nan) - stat0_word = 'log10 negative ' + stat0_word + mny = np.log10(-mny+1e-30) + mxy = np.log10(-mxy+1e-30) else: y = np.where(y > 0, np.log10(y), np.nan) - stat0_word = 'log10 ' + stat0_word + mny = np.log10(mny) + mxy = np.log10(mxy) x_points.extend(x) y_points.extend(y) x_points = np.array(x_points) # Convert lists to numpy arrays @@ -178,22 +260,41 @@ def run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm): if not is_valid.any(): print('no valid values') continue - numbin = int(np.sqrt(x_points.size)/2) + 1 - print(var,'numbin', numbin) # Define the bins for the histogram and calculate - x_edges = np.linspace(x_points.min(), x_points.max(), num=numbin) - y_edges = np.linspace(y_points.min(), y_points.max(), num=numbin) + if not do_same: + mnx = x_points.min() + mxx = x_points.max() + mny = y_points.min() + mxy = y_points.max() + else: + if mx: + mnx = 0.0 + mxx = mx + x_edges = np.linspace(mnx,mxx, num=numbin) + y_edges = np.linspace(mny,mxy, num=numbin) zi, _, _ = np.histogram2d(x_points, y_points, bins=[x_edges, y_edges]) + zi = zi.T # Transpose the histogram to match the pcolormesh orientation + if do_clip: + zi_clipped = np.where(zi > 0.95*maxcol, 0.95*maxcol, zi) # Clip zi.T so that max values are maxcol with a little buffer + else: + zi_clipped = zi # don't clip # Calculate bin centers from edges for X and Y x_centers = (x_edges[:-1] + x_edges[1:]) / 2 y_centers = (y_edges[:-1] + y_edges[1:]) / 2 X, Y = np.meshgrid(x_centers, y_centers) + if lx: X = 10**X + if ly: + if stat!='mnnz' and stat!='mean': + if var == 'scalarTotalET': + Y = -10**Y + else: + Y = 10**Y # Adjust the pcolormesh call to use the centers and compatible shading - norm = LogNorm(vmin=np.min(zi[zi > 0]), vmax=np.max(zi)) - mesh = axs[r, c].pcolormesh(X, Y, zi.T, shading='gouraud', cmap=custom_cmap, zorder=0,norm=norm) + norm = LogNorm(vmin=1, vmax=maxcol) + mesh = axs[r, c].pcolormesh(X, Y, zi_clipped, shading='gouraud', cmap=custom_cmap, zorder=0,norm=norm) fig.colorbar(mesh, ax=axs[r, c], label='GRU count') elif not do_heat: @@ -209,6 +310,8 @@ def run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm): for j, m in enumerate(plt_name): lgnd.legendHandles[j]._sizes = [80] axs[r,c].set_title(plt_t) + if lx: axs[r,c].set_xscale('log') + if ly: axs[r,c].set_yscale('log') if do_rel: axs[r,c].set_xlabel(stat_word) else: @@ -219,9 +322,13 @@ def run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm): if stat == 'kgem': axs[r,c].set_xlabel(stat_word) axs[r,c].set_ylabel(stat0_word + ' [{}]'.format(leg_t0)) -def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t): +def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): r = i//2 c = i-r*2 + global method_name2 # Declare method_name as global to modify its global value + global plt_name2 # Declare plt_name as global to modify its global value + method_name2 = np.copy(method_name20) + plt_name2 = np.copy(plt_name20) if stat == 'rmse' or stat == 'kgem': stat0 = 'mean' @@ -232,30 +339,57 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t): if stat == 'maxe': stat0 = 'amax' wordx = ' max' - wordy = np.copy(wordx) + wordy = wordx # Data + if do_heat: + # make the axes the same + do_same = False + if len(method_name2)>1: + do_same = True + mxx = 0.0 + mnx = 1.0 + for m in method_name2: + # Get the statistics, remove 9999 (should be nan, but just in case) + s = summa1[m][var].sel(stat=stat0).where(lambda x: x != 9999) + mxx = max(s.max(),mxx) + mnx = min(s.min(),mnx) + mxy = 0.0 + mny = 1.0 + for m in method_name2: + # Get the statistics, remove 9999 (should be nan, but just in case) + s = summa1[m][comp].sel(stat=stat0).where(lambda x: x != 9999) + mxy = max(s.max(),mxy) + mny = min(s.min(),mny) + if rep: + method_name2 = [method_name20[1]] + plt_name2 = [plt_name0[1]] + else: + method_name2 = [method_name20[0]] + plt_name2 = [plt_name20[0]] + else: # only one method + mxx = 0.0 + mnx = 1.0 + mxy = 0.0 + mny = 1.0 for j, m in enumerate(method_name2): # Get the statistics, remove 9999 (should be nan, but just in case) - s0 = np.fabs(summa1[m][comp].sel(stat=stat0)).where(lambda x: x != 9999) - s = np.fabs(summa1[m][var].sel(stat=stat0)).where(lambda x: x != 9999) + s = np.fabs(summa1[m][var].sel(stat=stat0)).where(lambda x: x != 9999, np.nan) + s0 = np.fabs(summa1[m][comp].sel(stat=stat0)).where(lambda x: x != 9999, np.nan) - if do_heat and j==0: + if do_heat: x_points = [] y_points = [] x = s.values y = s0.values - x_points.extend(x) - y_points.extend(y) - x_points = np.array(x_points) # Convert lists to numpy arrays - y_points = np.array(y_points) if lx: - x = np.log10(x[x > 0]) - wordx = 'log10 ' + wordx + x = np.where(x > 0, np.log10(x), np.nan) + mxx = np.log10(mxx+1e-30) + mnx = np.log10(mnx+1e-30) if ly: - y = np.log10(y[y > 0]) - wordy = 'log10 ' + wordy - + y = np.where(y > 0, np.log10(y), np.nan) + mxy = np.log10(mxy+1e-30) + mny = np.log10(mny+1e-30) x_points.extend(x) y_points.extend(y) x_points = np.array(x_points) # Convert lists to numpy arrays @@ -265,22 +399,31 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t): is_valid = ~np.isnan(x_points) & ~np.isnan(y_points) & ~np.isinf(x_points) & ~np.isinf(y_points) x_points = x_points[is_valid] y_points = y_points[is_valid] - numbin = int(np.sqrt(x_points.size)/2) + 1 - print(var,'numbin', numbin) # Define the bins for the histogram and calculate - x_edges = np.linspace(x_points.min(), x_points.max(), num=numbin) - y_edges = np.linspace(y_points.min(), y_points.max(), num=numbin) + if not do_same: + mnx = x_points.min() + mxx = x_points.max() + mny = y_points.min() + mxy = y_points.max() + x_edges = np.linspace(mnx,mxx, num=numbin) + y_edges = np.linspace(mny,mxy, num=numbin) zi, _, _ = np.histogram2d(x_points, y_points, bins=[x_edges, y_edges]) - + zi = zi.T # Transpose the histogram to match the pcolormesh orientation + if do_clip: + zi_clipped = np.where(zi > 0.95*maxcol, 0.95*maxcol, zi) # Clip zi.T so that max values are maxcol with a little buffer + else: + zi_clipped = zi # don't clip # Calculate bin centers from edges for X and Y x_centers = (x_edges[:-1] + x_edges[1:]) / 2 y_centers = (y_edges[:-1] + y_edges[1:]) / 2 X, Y = np.meshgrid(x_centers, y_centers) + if lx: X = 10**X + if ly: Y = 10**Y # Adjust the pcolormesh call to use the centers and compatible shading - norm = LogNorm(vmin=np.min(zi[zi > 0]), vmax=np.max(zi)) - mesh = axs[r, c].pcolormesh(X, Y, zi.T, shading='gouraud', cmap=custom_cmap, zorder=0,norm=norm) + norm = LogNorm(vmin=1, vmax=maxcol) + mesh = axs[r, c].pcolormesh(X, Y, zi_clipped, shading='gouraud', cmap=custom_cmap, zorder=0,norm=norm) fig.colorbar(mesh, ax=axs[r, c], label='GRU count') elif not do_heat: @@ -300,8 +443,8 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t): for j, m in enumerate(plt_name2): lgnd.legendHandles[j]._sizes = [80] axs[r,c].set_title(plt_t) - axs[r,c].set_xscale('log') - if comp != 'numberFluxCalc': axs[r,c].set_yscale('log') + if lx: axs[r,c].set_xscale('log') + if ly: axs[r,c].set_yscale('log') axs[r,c].set_xlabel(stat_word + wordx + ' [{}]'.format(leg_t)) axs[r,c].set_ylabel(stat0_word + wordy + ' [{}]'.format(leg_t0)) @@ -311,30 +454,49 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t): plt.rcParams['ytick.major.width'] = 2 if do_vars: # Specify variables of interest - use_vars = [1,2,4] - #logx = [0,0,0] # log scale x axis - #logy = [0,0,1] # log scale y axis - use_vars = [0,1,2,3,4] - logx = np.ones(len(use_vars)) # log scale x axis - logy = np.ones(len(use_vars)) # log scale y axis + use_vars = [1,1,2,2,4,4 ] + logx = np.zeros(len(use_vars)) # no log scale x axis + logy = [0,0,0,0,1,1] # log scale y axis + rep = [0,1,0,1,0,1] + #use_vars = [0,1,2,3,4] + #logx = np.ones(len(use_vars)) # log scale x axis + #logy = np.ones(len(use_vars)) # log scale y axis + #rep = np.zeros(len(use_vars)) plot_vars = settings plt_titl = ['snow water equivalent','total soil water content','total evapotranspiration', 'total water on the vegetation canopy','average routed runoff'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$mm~y^{-1}$'] leg_titl0 = ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$mm~y^{-1}$'] leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$','$mm~h^{-1}$'] + + #to zoom the heat x axis set these + maxes = [0.0,0.0,0.0,0.0,0.0,0.0] #initialize + if stat == 'rmse' or stat=='rmnz': + maxes = [60,15,250,0.5,200,20e-3] + if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,20e-3] + if stat == 'maxe': + maxes = [60,20,250,0.5,200,2.0] + if do_rel: maxesx = [0.6,0.02,0.6,0.3,0.6,2.0] + if stat == 'kgem': + maxes = [0.9,0.9,0.9,0.9,0.9,20e-3] + #maxes = [0.0,0.0,0.0,0.0,0.0,0.0] # turn off zoom plot_vars = [plot_vars[i] for i in use_vars] plt_titl = [f"({chr(97+n)}) {plt_titl[i]}" for n,i in enumerate(use_vars)] leg_titl = [leg_titl[i] for i in use_vars] leg_titl0 = [leg_titl0[i] for i in use_vars] leg_titlm = [leg_titlm[i] for i in use_vars] + maxes = [maxes[i] for i in use_vars] fig_fil = 'Hrly_diff_scat_{}_{}' if do_rel: fig_fil = fig_fil + '_rel' - if do_heat: fig_fil = method_name[0]+fig_fil + '_heat' + if do_heat: + fig_fil = '{}'+fig_fil + '_heat' + if sum(maxes)>0: fig_fil = fig_fil + '_zoom' + fig_fil = fig_fil.format(','.join(method_name),','.join(plot_vars),stat) + else: + fig_fil = fig_fil.format(','.join(plot_vars),stat) fig_fil = fig_fil + '_compressed.png' - fig_fil = fig_fil.format(','.join(settings),stat) # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug if 'compressed' in fig_fil: @@ -349,8 +511,8 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t): #fig.suptitle('Hourly Errors and Values for each GRU', fontsize=40) fig.subplots_adjust(hspace=0.33, wspace=0.17) # Adjust the bottom margin, vertical space, and horizontal space - for i,(var,lx,ly,plt_t,leg_t,leg_t0,leg_tm) in enumerate(zip(plot_vars,logx,logy,plt_titl,leg_titl,leg_titl0,leg_titlm)): - run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm) + for i,(var,lx,ly,plt_t,leg_t,leg_t0,leg_tm,rep,mx) in enumerate(zip(plot_vars,logx,logy,plt_titl,leg_titl,leg_titl0,leg_titlm,rep,maxes)): + run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm,rep,mx) # Remove the extra subplots if (len(plot_vars)) < 6: @@ -364,9 +526,12 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t): if do_balance: # Specify variables of interest - use_vars = [0,1,2,3] + use_vars = [0,0,1,1,2,2] + rep = [0,1,0,1,0,1] + #use_vars = [0,1,2,3] logx = np.ones(len(use_vars)) # log scale x axis logy = np.ones(len(use_vars)) # log scale y axis + #rep = np.zeros(len(use_vars)) plot_vars = ['balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceCasNrg','wallClockTime'] comp_vars = ['balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','numberFluxCalc'] @@ -380,10 +545,13 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t): leg_titl = [leg_titl[i] for i in use_vars] leg_titl0 = [leg_titl0[i] for i in use_vars] - fig_fil = 'balance_scat_{}' - if do_heat: fig_fil = method_name[0]+fig_fil + '_heat' + fig_fil = 'Hrly_balance_scat_{}' + if do_heat: + fig_fil = '{}'+fig_fil + '_heat' + fig_fil = fig_fil.format(','.join(method_name),','.join(plot_vars),stat) + else: + fig_fil = fig_fil.format(','.join(plot_vars),stat) fig_fil = fig_fil + '_compressed.png' - fig_fil = fig_fil.format(stat) if 'compressed' in fig_fil: plt.rcParams.update({'font.size': 27}) @@ -397,8 +565,8 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t): fig.subplots_adjust(hspace=0.33, wspace=0.17) # Adjust the bottom margin, vertical space, and horizontal space #fig.suptitle('Scatterplot of Hourly Statistics for each GRU', fontsize=40,y=1.0) - for i,(var,comp,lx,ly,leg_t,leg_t0,plt_t) in enumerate(zip(plot_vars,comp_vars,logx,logy,leg_titl,leg_titl0,plt_titl)): - run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t) + for i,(var,comp,lx,ly,leg_t,leg_t0,plt_t,rep) in enumerate(zip(plot_vars,comp_vars,logx,logy,leg_titl,leg_titl0,plt_titl,rep)): + run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,rep) if (len(plot_vars)) < 6: for i in range(len(plot_vars),6): diff --git a/utils/steps_per_GRU.py b/utils/steps_per_GRU.py index a3cb23ad4..2478ad6a4 100644 --- a/utils/steps_per_GRU.py +++ b/utils/steps_per_GRU.py @@ -22,8 +22,8 @@ viz_dir = Path('/home/avanb/scratch/statistics') -testing = False -if testing: +run_local = False +if run_local: stat = 'amax' viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') method_name=['be1'] #maybe make this an argument diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index 56efd74c3..b38e586fc 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -29,7 +29,7 @@ bench_name = 'sundials_1en8cm' not_parallel = True # run as true with batch mode, or false, with `python timeseries_to_statistics.py sundials_1en6 1 1` for single batch, and `python timeseries_to_statistics.py sundials_1en6 2 1` to merge -testing = False +run_local = False # which statistics to compute do_vars = False @@ -37,7 +37,7 @@ do_balance = False do_wall = True -if testing: +if run_local: not_parallel = True method_name ='be1en' ibatch = 1 # Run as 1, 2, and then 3 to fully test diff --git a/utils/wallClock_per_GRU.py b/utils/wallClock_per_GRU.py index 6636b8916..23d6c2f4c 100644 --- a/utils/wallClock_per_GRU.py +++ b/utils/wallClock_per_GRU.py @@ -22,8 +22,8 @@ viz_dir = Path('/home/avanb/scratch/statistics') nbatch_hrus = 518 # number of HRUs per batch -testing = False -if testing: +run_local = False +if run_local: viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') method_name=['be1'] #maybe make this an argument else: diff --git a/utils/wallStd_per_GRU.py b/utils/wallStd_per_GRU.py index 0e52ec402..17781b15d 100644 --- a/utils/wallStd_per_GRU.py +++ b/utils/wallStd_per_GRU.py @@ -21,8 +21,8 @@ viz_dir = Path('/home/avanb/scratch/statistics') -testing = False -if testing: +run_local = False +if run_local: viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') method_name=['be1en','be1en'] #cm','be1en','be1lu'] #maybe make this an argument else: From a072af1f02ba3f5b63e1964247dd1a1771cdd476 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 4 Jul 2024 23:56:58 +0900 Subject: [PATCH 1348/1472] utils --- utils/concat_groups_split_summa.py | 3 +- utils/hist_per_GRU.py | 45 ++-- utils/largest_error_attrib.py | 4 +- utils/plot_per_GRUMult.py | 328 +++++++++++++++++++---------- utils/plot_per_GRUMultBal.py | 269 ++++++++++++++--------- utils/scat_per_GRU.py | 201 ++++++++++-------- utils/steps_per_GRU.py | 3 +- utils/wallClock_per_GRU.py | 5 +- utils/wallStd_per_GRU.py | 3 +- 9 files changed, 528 insertions(+), 333 deletions(-) diff --git a/utils/concat_groups_split_summa.py b/utils/concat_groups_split_summa.py index b14700e93..e51c02fa4 100644 --- a/utils/concat_groups_split_summa.py +++ b/utils/concat_groups_split_summa.py @@ -12,7 +12,6 @@ import numpy as np catby_num = 2 #number of files to cat into one, if had to divide runs from regular batches into sub-batches to finish in 7 days -top_fold = '/home/avanb/scratch/' missing = False # if appending nan hrus to batch because failed missgru = 72055933 # batch 205 summa-be32 value @@ -25,7 +24,7 @@ else: import multiprocessing as mp import sys - # The first input argument specifies the run where the files are + top_fold = '/home/avanb/scratch/' method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en8 or be64) ncdir = top_fold + 'summa-' + method_name + '_nocat' diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 99a52a4fc..86a5bd92d 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -20,32 +20,30 @@ import copy import pandas as pd -viz_dir = Path('/home/avanb/scratch/statistics') -nbatch_hrus = 518 # number of HRUs per batch -num_bins = 1000 -do_rel = True # plot relative to the benchmark simulation +do_rel = True # true is plot relative to the benchmark simulation +do_hist = False # true is plot histogram instead of CDF +run_local = True # true is run on local machine, false is run on cluster -run_local = False -do_hist = False # plot histogram instead of CDF if run_local: stat = 'rmnz' - viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be1en'] - plt_name=['BE1 mixed'] - method_name2=method_name - plt_name2=plt_name + viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics_en') else: import sys - # The first input argument specifies the run where the files are stat = sys.argv[1] - #method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument - #plt_name=['BE1','IDAe-4','BE4','BE8','BE16','BE32','IDAe-6'] #maybe make this an argument - #method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument - #plt_name=['BE1','BE16','BE32','SUNDIALS'] #maybe make this an argument - method_name=['be1','be1cm','be1en','sundials_1en6cm'] - plt_name=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp'] - method_name2=method_name+['sundials_1en8cm'] - plt_name2=plt_name+['reference solution'] + viz_dir = Path('/home/avanb/scratch/statistics') + + +#method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument +#plt_name=['BE1','IDAe-4','BE4','BE8','BE16','BE32','IDAe-6'] #maybe make this an argument +#method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument +#plt_name=['BE1','BE16','BE32','SUNDIALS'] #maybe make this an argument +method_name=['be1','be1cm','be1en','sundials_1en6cm'] +plt_name=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp'] +method_name2=method_name+['sundials_1en8cm'] +plt_name2=plt_name+['reference solution'] + +num_bins = 1000 + if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE # Define the power transformation function @@ -57,7 +55,6 @@ def power_transform(x): rep = [0] # mark the repeats #use_vars = [0,1,2,3,4] #rep = [0,0,0,0,0] # mark the repeats - settings0= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] settings = [settings0[i] for i in use_vars] @@ -98,11 +95,7 @@ def power_transform(x): if do_rel: fig_fil = 'Hrly_diff_cdf_{}_{}_zoom_rel_compressed.png' fig_fil = fig_fil.format(','.join(settings),stat) -if stat == 'rmse': - stat2 = 'mean' - maxes = [2,15,250,0.08,200,20e-3] - if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,20e-3] -if stat == 'rmnz': +if stat == 'rmse' or stat=='rmnz': stat2 = 'mean' maxes = [2,15,250,0.08,200,20e-3] if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,20e-3] diff --git a/utils/largest_error_attrib.py b/utils/largest_error_attrib.py index 202a66409..11921804b 100644 --- a/utils/largest_error_attrib.py +++ b/utils/largest_error_attrib.py @@ -6,8 +6,6 @@ import xarray as xr from pathlib import Path -top_fold = '/home/avanb/scratch/' -attr_fold = '/home/avanb/TestScripts/settings/' nBig = 10 do_rel = True # plot relative to the benchmark simulation @@ -19,6 +17,8 @@ stat = 'rmnz' else: import sys + top_fold = '/home/avanb/scratch/' + attr_fold = '/home/avanb/TestScripts/settings/' method_name = sys.argv[1] stat = sys.argv[2] diff --git a/utils/plot_per_GRUMult.py b/utils/plot_per_GRUMult.py index bef1f2df0..233b9467a 100644 --- a/utils/plot_per_GRUMult.py +++ b/utils/plot_per_GRUMult.py @@ -13,8 +13,7 @@ # Run: # python plot_per_GRUMult.py [stat] -# where stat is rmse or maxe or kgem - +# where stat is rmse or maxe or kgem or mean or amax # modules import sys @@ -29,66 +28,72 @@ import fiona import geopandas as gpd import pandas as pd +import matplotlib.ticker as ticker + + +do_rel = False # true is plot relative to the benchmark simulation +one_plot = True # true is one plot, false is multiple plots (one per variable) +run_local = False # true is run on local machine (only does testing), false is run on cluster + +if run_local: + stat = 'mnnz' + viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') +else: + import sys + stat = sys.argv[1] + viz_dir = Path('/home/avanb/scratch/statistics') + + +# NOTE: method_name 'ref' will plot the reference solution, 'diff' will plot the difference between two simulations +# method_name 'diff' requires the specification of the two simulations to subtract in the variables from_meth and sub_meth -# plot all runs, pick statistic -stat = sys.argv[1] -#method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument -#plt_name=['(a) SUMMA-BE1','(b) SUMMA-BE16','(c) SUMMA-BE32','(d) SUMMA-SUNDIALS'] #maybe make this an argument -method_name=['be1','be1cm','be1en','sundials_1en6cm']#,'diff']#,'be1lu'] #maybe make this an argument -plt_name=['(a) SUMMA-BE1 Common Form of Heat Eq.','(b) SUMMA-BE1 Temperature Form of Heat Eq.','(c) SUMMA-BE1 Mixed Form of Heat Eq.','(d) SUMMA-SUNDIALS Temperature Form of Heat Eq.'] #,'(d) SUMMA-BE1 Mixed Form - Temperature Form']#, #maybe make this an argumentt -from_meth = 'be1en' # index of the first simulation in the difference simulation, only used if a method_name is 'diff' -sub_meth = 'be1' # index of the simulation to subtract in the difference simulation, only used if a method_name is 'diff' +method_name=['be1','be16','be32','sundials_1en6','ref'] +plt_name0=['SUMMA-BE1','SUMMA-BE16','SUMMA-BE32','SUMMA-SUNDIALS','reference solution'] +#method_name=['be1','be1cm','be1en','sundials_1en6cm','diff'] +#plt_name0=['SUMMA-BE1 Common Form of Heat Eq.','SUMMA-BE1 Temperature Form of Heat Eq.','SUMMA-BE1 Mixed Form of Heat Eq.','SUMMA-SUNDIALS Temperature Form of Heat Eq.','SUMMA-BE1 Mixed Form - Temperature Form'] +from_meth = 'be1en' # name of the first simulation in the difference simulation, only used if a method_name is 'diff' +sub_meth = 'be1' # name of the simulation to subtract in the difference simulation, only used if a method_name is 'diff' # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] -viz_dir = Path('/home/avanb/scratch/statistics') + viz_fil = method_name.copy() for i, m in enumerate(method_name): viz_fil[i] = m + '_hrly_diff_stats_{}.nc' viz_fil[i] = viz_fil[i].format(','.join(settings)) nbatch_hrus = 518 # number of HRUs per batch -do_rel = True # plot relative to the benchmark simulation if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE -# Specify variables of interest +# Specify variables in files plot_vars = settings.copy() -plt_titl = ['Snow Water Equivalent','Total soil water content','Total evapotranspiration', 'Total water on the vegetation canopy','Average routed runoff','Wall clock time'] -leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] +plt_titl = ['snow water equivalent','total soil water content','total evapotranspiration', 'total water on the vegetation canopy','average routed runoff','wall clock time'] +leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] fig_fil= '_hrly_diff_stats_{}_compressed.png' if do_rel: fig_fil = '_hrly_diff_stats_{}_rel_compressed.png' -if stat == 'rmse': - maxes = [2,15,250,0.08,200,10e-3] #[2,15,8e-6,0.08,6e-9,10e-3] - #maxes = [0.25,2,30,0.01,30,2e-3] #[0.25,2,1e-6,0.01,1e-9,2e-3] - if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] -if stat == 'rmnz': - maxes = [2,15,250,0.08,200,10e-3] +if stat == 'rmse' or stat == 'rmnz': + maxes = [2,15,250,0.08,200,10e-3] if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] if stat == 'maxe': maxes = [15,25,0.8,2,0.3,0.2] #[15,25,25e-5,2,1e-7,0.2] if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,0.2] if stat == 'kgem': maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] -if stat == 'mean': - maxes = [80,1500,1500,3000,10e-3] #[80,1500,5e-5,8,1e-7,10e-3] +if stat == 'mean' or stat == 'mnnz': + maxes = [80,1600,1500,8,3000,10e-3] #[80,1500,5e-5,8,1e-7,10e-3] if do_rel: maxes = [1.1,1.1,1.1,1.1,1.1,10e-3] if stat == 'amax': maxes = [240,1800,3.5,25,7.5,0.2] #[240,1800,1e-3,25,2e-6,0.2] if do_rel: maxes = [1.1,1.1,1.1,1.1,1.1,0.2] -# Get the albers shapes -main = Path('/home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/albers_projection') +# Get simulation statistics +summa = {} +for i, m in enumerate(method_name): + # Get the aggregated statistics of SUMMA simulations + if m!='diff' and m!='ref': summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) -# Plot lakes? -plot_lakes = True -# lakes shapefile WHERE IS THIS -#lake_path = Path('C:/Globus endpoint/HydroLAKES/HydroLAKES_polys_v10_shp') -#lake_name = 'HydroLAKES_polys_v10_subset_NA.shp' -## Control file handling -# Store the name of the 'active' file in a variable -controlFile = 'plot_control_NorthAmerica.txt' # Function to extract a given setting from the control file def read_from_control( file, setting ): @@ -124,62 +129,66 @@ def make_default_path(suffix): return defaultPath -## Catchment shapefile location and variable names -# HM catchment shapefile path & name -hm_catchment_path = read_from_control(controlFile,'catchment_shp_path') -hm_catchment_name = read_from_control(controlFile,'catchment_shp_name') -# Specify default path if needed -if hm_catchment_path == 'default': - hm_catchment_path = make_default_path('shapefiles/catchment') # outputs a Path() -else: - hm_catchment_path = Path(hm_catchment_path) # make sure a user-specified path is a Path() - -# Find the GRU and HRU identifiers -hm_hruid = read_from_control(controlFile,'catchment_shp_hruid') - -## River network shapefile location and variable names -# Plot rivers? -plot_rivers = False -# River network path & name -river_network_path = read_from_control(controlFile,'river_network_shp_path') -river_network_name = read_from_control(controlFile,'river_network_shp_name') -# Specify default path if needed -if river_network_path == 'default': - river_network_path = make_default_path('shapefiles/river_network') # outputs a Path() +if run_local: + # Make stubs to check if the plots set up properly + plot_lakes = False + plot_rivers = False + + # Create a mock DataFrame + from shapely.geometry import Point + + s = summa[method_name[0]][plot_vars[0]].sel(stat=stat) + mock_data = { + 'hm_hruid': s.hru.values[range(100)], # Example HRU IDs + 'geometry': [Point(x, y) for x, y in zip(range(100), range(100))] # Simple geometries + } + bas_albers = gpd.GeoDataFrame(mock_data, geometry='geometry') + hm_hruid = 'hm_hruid' # Correctly define the variable name in the shapefile + xmin, ymin, xmax, ymax = bas_albers.total_bounds + else: - river_network_path = Path(river_network_path) # make sure a user-specified path is a Path() + # Get the albers shapes + main = Path('/home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/albers_projection') + plot_lakes = True + plot_rivers = False + + # Control file handling + controlFile = 'plot_control_NorthAmerica.txt' + + # HM catchment shapefile path & name + hm_catchment_path = read_from_control(controlFile,'catchment_shp_path') + hm_catchment_name = read_from_control(controlFile,'catchment_shp_name') + # Specify default path if needed + if hm_catchment_path == 'default': + hm_catchment_path = make_default_path('shapefiles/catchment') # outputs a Path() + else: + hm_catchment_path = Path(hm_catchment_path) # make sure a user-specified path is a Path() -# Find the segment ID -seg_id = read_from_control(controlFile,'river_network_shp_segid') + # Find the GRU and HRU identifiers + hm_hruid = read_from_control(controlFile,'catchment_shp_hruid') -## Load all shapefiles and project to Albers Conformal Conic and reproject -# Set the target CRS -acc = 'ESRI:102008' + ## River network shapefile location and variable names + river_network_path = read_from_control(controlFile,'river_network_shp_path') + river_network_name = read_from_control(controlFile,'river_network_shp_name') + # Specify default path if needed + if river_network_path == 'default': + river_network_path = make_default_path('shapefiles/river_network') # outputs a Path() + else: + river_network_path = Path(river_network_path) # make sure a user-specified path is a Path() -# catchment shapefile, first 2 lines throw error so cutting them -#bas = gpd.read_file(hm_catchment_path/hm_catchment_name) -#bas_albers = bas.to_crs(acc) -bas_albers = gpd.read_file(main/'basin.shp') -xmin, ymin, xmax, ymax = bas_albers.total_bounds + # Find the segment ID + seg_id = read_from_control(controlFile,'river_network_shp_segid') -# river network shapefile, first 2 lines throw error so cutting them -if plot_rivers: - #riv = gpd.read_file(river_network_path/river_network_name) - #riv_albers = riv.to_crs(acc) - riv_albers = gpd.read_file(main/'river.shp') + ## Load all shapefiles and project to Albers Conformal Conic and reproject + acc = 'ESRI:102008' # Set the target CRS + + bas_albers = gpd.read_file(main/'basin.shp') + xmin, ymin, xmax, ymax = bas_albers.total_bounds + + if plot_rivers: riv_albers = gpd.read_file(main/'river.shp') + if plot_lakes: lak_albers = gpd.read_file(main/'lakes.shp') -# lakes shapefile, first 2 lines throw error so cutting them -if plot_lakes: - #lakes = gpd.read_file(lake_path/lake_name) - #lak_albers = lakes.to_crs(acc) - lak_albers = gpd.read_file(main/'lakes.shp') -## Pre-processing, map SUMMA sims to catchment shapes -# Get the aggregated statistics of SUMMA simulations -summa = {} -for i, m in enumerate(method_name): - # Get the aggregated statistics of SUMMA simulations - if m!='diff': summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) # Match the accummulated values to the correct HRU IDs in the shapefile hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile @@ -199,20 +208,26 @@ def make_default_path(suffix): for m in method_name: if m=='diff': s = np.fabs(summa[from_meth][plot_var].sel(stat=stat0)) - np.fabs(summa[sub_meth][plot_var].sel(stat=stat0)) + elif m=='ref': + s = np.fabs(summa[method_name[0]][plot_var].sel(stat=statr)) else: s = np.fabs(summa[m][plot_var].sel(stat=stat0)) if do_rel and plot_var != 'wallClockTime': s = s/s_rel - # Replace inf values with NaN in the s DataArray - s = s.where(~np.isinf(s), np.nan) + # Replace inf and 9999 values with NaN in the s DataArray + s = s.where(~np.isinf(s), np.nan).where(lambda x: x != 9999, np.nan) if plot_var == 'scalarTotalET' and not do_rel: - if stat =='rmse' or stat =='rmnz' : s = s*31557600 # make annual total - if stat =='maxe': s = s*3600 # make hourly max + if stat =='rmse' or stat =='rmnz' or stat=='mnnz' or stat=='mean': s = s*31557600 # make annual total + if stat =='maxe' or stat=='amax': s = s*3600 # make hourly max if plot_var == 'averageRoutedRunoff' and not do_rel: - if stat =='rmse' or stat =='rmnz' : s = s*31557600*1000 # make annual total - if stat =='maxe': s = s*3600*1000 # make hourly max - bas_albers[plot_var+m] = s.sel(hru=hru_ids_shp.values) + if stat =='rmse' or stat =='rmnz' or stat=='mnnz' or stat=='mean': s = s*31557600*1000 # make annual total + if stat =='maxe' or stat=='amax': s = s*3600*1000 # make hourly max + + # Create a new column in the shapefile for each method, and fill it with the statistics + bas_albers[plot_var+m] = np.nan + hru_ind = [i for i, hru_id in enumerate(hru_ids_shp.values) if hru_id in s.hru.values] # if some missing + bas_albers.loc[hru_ind, plot_var+m] = s.sel(hru=hru_ids_shp.values[hru_ind]).values # Select lakes of a certain size for plotting if plot_lakes: @@ -224,8 +239,9 @@ def make_default_path(suffix): large_lakes_albers = lak_albers.loc[(lak_albers['Lake_area'] > minSize) & in_domain & (~out_domain) ] lake_col = (8/255,81/255,156/255) -##Figure + +# Figure def run_loop(j,var,the_max): stat0 = stat if stat == 'rmse' or stat == 'kgem' or stat == 'mean': @@ -241,7 +257,7 @@ def run_loop(j,var,the_max): my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap my_cmap.set_bad(color='white') #nan color white vmin,vmax = 0, the_max - if stat =='mean' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 700, the_max + if stat =='mean' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 600, the_max if stat =='amax' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 1000, the_max if (stat == 'mean' or stat == 'mnnz' or stat == 'amax') and var!='wallClockTime' and do_rel: vmin,vmax = 0.9, the_max @@ -269,13 +285,9 @@ def run_loop(j,var,the_max): if statr == 'mnnz_ben': statr_word = 'mean' # no 0s' if statr == 'amax_ben': statr_word = 'max' - # colorbar axes - f_x_mat = [0.46,0.96,0.46,0.96] - f_y_mat = [0.55,0.55,0.07,0.07] - - for i,(m,f_x,f_y) in enumerate(zip(method_name,f_x_mat,f_y_mat)): - r = i//2 - c = i-r*2 + for i,m in enumerate(method_name): + r = i//ncol + base_row + c = i - (r-base_row)*ncol # Plot the data with the full extent of the bas_albers shape if m=='diff': @@ -291,22 +303,55 @@ def run_loop(j,var,the_max): axs[r,c].set_ylim(ymin, ymax) # Custom colorbar - cax = fig.add_axes([f_x,f_y,0.02,0.375]) + f_x = base_f_x + (subplot_width + margin_x) * (c + 1) + f_y = base_f_y + (subplot_height + margin_y) * (nrow - 1 - r) # Subtract from nrow - 1 because y increases upwards + + cax = fig.add_axes([f_x,f_y,0.04/nrow,0.75/ncol]) if m=='diff': sm = matplotlib.cm.ScalarMappable(cmap=my_cmap2, norm=norm2) else: sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) sm._A = [] cbr = fig.colorbar(sm, cax=cax) - if stat == 'rmse' or stat == 'rmnz' or stat == 'mean' or stat == 'maxe' or stat == 'amax': cbr.ax.set_ylabel(stat_word0 + ' [{}]'.format(leg_titl[j]), labelpad=40, rotation=270) - if stat == 'kgem': cbr.ax.set_ylabel(stat_word0, labelpad=40, rotation=270) - if do_rel and var!='wallClockTime': cbr.ax.set_ylabel('relative '+ stat_word0, labelpad=40, rotation=270) + if stat == 'kgem': + cbr.ax.set_ylabel(stat_word0, labelpad=40, rotation=270) + else: + if do_rel and var!='wallClockTime': + cbr.ax.set_ylabel('relative '+ stat_word0, labelpad=40, rotation=270) + else: + cbr.ax.set_ylabel(stat_word0 + ' [{}]'.format(leg_titl[j]), labelpad=40, rotation=270) + + if var=='scalarTotalSoilWat' and (stat =='mean' or stat =='mnnz'): + # Customizing the tick labels to include negative signs + def format_tick(value, tick_number): + rounded_value = int(round(value,-2)) + return f"-{rounded_value}" + cbr.ax.yaxis.set_major_formatter(ticker.FuncFormatter(format_tick)) # lakes if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) -for i,(var,the_max) in enumerate(zip(plot_vars,maxes)): - + + +# Specify plotting options +if one_plot: + use_vars = [1,2,4] + use_meth = [0,3,4] +else: + use_vars = [0,1,2,3,4,5] + use_meth = [0,1,2,3] +plot_vars = [plot_vars[i] for i in use_vars] +plt_titl = [plt_titl[i] for i in use_vars] +leg_titl = [leg_titl[i] for i in use_vars] +maxes = [maxes[i] for i in use_vars] +method_name = [method_name[i] for i in use_meth] + +if one_plot: + ncol = len(use_meth) + nrow = 3 + if len(use_meth)!=3: + print('Assuming 3 methods for plot ncol in code') + sys.exit() # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug if 'compressed' in fig_fil: plt.rcParams.update({'font.size': 27}) @@ -314,17 +359,76 @@ def run_loop(j,var,the_max): plt.rcParams.update({'font.size': 100}) if 'compressed' in fig_fil: - fig,axs = plt.subplots(2,2,figsize=(35,28)) + fig,axs = plt.subplots(nrow,ncol,figsize=(17*ncol,14*nrow)) else: - fig,axs = plt.subplots(2,2,figsize=(140,133)) - - # Remove the fourth subplot - #fig.delaxes(axs[1, 1]) + fig,axs = plt.subplots(nrow,ncol,figsize=(70*ncol,58*nrow)) - fig.suptitle('{} Hourly Statistics'.format(plt_titl[i]), fontsize=40,y=1.05) + fig.suptitle('hourly statistics', fontsize=40,y=1.05) plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines plt.tight_layout() + + subplot_width = 1.0 / ncol + subplot_height = 1.0 / nrow + margin_x = 0.00 # Adjust this value as needed + margin_y = -0.03/nrow # Adjust this value as needed + base_f_x = -0.08/ncol # Adjust this value as needed + base_f_y = 0.14/nrow # Adjust this value as needed + +else: + #size hardwired to 2x2 for now + ncol = 2 + nrow = 2 + if len(method_name)>4: + print('Too many methods for 2x2 plot') + sys.exit() + + base_row = 0 + plt_name = [f"({chr(97+n)}) {plt_name0[i]}" for n,i in enumerate(use_meth)] + + subplot_width = 1.0 / ncol + subplot_height = 1.0 / nrow + margin_x = 0.0 # Adjust this value as needed + margin_y = -0.03/nrow # Adjust this value as needed + base_f_x = -0.08/ncol # Adjust this value as needed + base_f_y = 0.14/nrow # Adjust this value as needed + +for i,(var,the_max) in enumerate(zip(plot_vars,maxes)): + + if one_plot: + # Reset the names + base_row = i + plt_name = [f"({chr(97+n+i*len(use_meth))}) {plt_titl[i] + ' ' + plt_name0[j]}" for n,j in enumerate(use_meth)] + else: + # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug + if 'compressed' in fig_fil: + plt.rcParams.update({'font.size': 27}) + else: + plt.rcParams.update({'font.size': 100}) + + if 'compressed' in fig_fil: + fig,axs = plt.subplots(nrow,ncol,figsize=(17*ncol,14*nrow)) + else: + fig,axs = plt.subplots(nrow,ncol,figsize=(70*ncol,58*nrow)) + + # Remove the extra subplots + if len(method_name) < nrow*ncol: + for j in range(len(method_name),nrow*ncol): + r = j//ncol + c = j-r*ncol + fig.delaxes(axs[r, c]) + + fig.suptitle('{} hourly statistics'.format(plt_titl[i]), fontsize=40,y=1.05) + plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines + plt.tight_layout() + run_loop(i,var,the_max) - fig_fil1 = (var+fig_fil).format(stat) - # Save + + if not one_plot: + # Save the figure + fig_fil1 = (var+fig_fil).format(stat) + plt.savefig(viz_dir/fig_fil1, bbox_inches='tight', transparent=True) + +if one_plot: + # Save the figure + fig_fil1 = ('all'+fig_fil).format(stat) plt.savefig(viz_dir/fig_fil1, bbox_inches='tight', transparent=True) diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index 0e026ba63..7a0ca0f82 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -30,34 +30,36 @@ import geopandas as gpd import pandas as pd -# plot all runs, pick statistic -stat = sys.argv[1] -method_name=['be1','be1cm','be1en','sundials_1en6cm'] #maybe make this an argument -plt_name=['(a) SUMMA-BE1 Common Form of Heat Eq.','(b) SUMMA-BE1 Temperature Form of Heat Eq.','(c) SUMMA-BE1 Mixed Form of Heat Eq.','(d) SUMMA-SUNDIALS Temperature Form of Heat Eq.'] + +one_plot = False # true is one plot, false is multiple plots (one per variable) +run_local = False # true is run on local machine (only does testing), false is run on cluster + +if run_local: + stat = 'mean' + viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics_en') +else: + import sys + stat = sys.argv[1] + viz_dir = Path('/home/avanb/scratch/statistics') + + +method_name=['be1','be1cm','be1en','sundials_1en6cm','sundials_1en8cm'] #maybe make this an argument +plt_name0=['SUMMA-BE1 common form of heat eq.','SUMMA-BE1 temperature form of heat eq.','SUMMA-BE1 mixed form of heat eq.','SUMMA-SUNDIALS temperature form of heat eq.','reference solution'] # Simulation statistics file locations settings= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime'] -viz_dir = Path('/home/avanb/scratch/statistics') viz_fil = method_name.copy() for i, m in enumerate(method_name): viz_fil[i] = m + '_hrly_diff_bals_{}.nc' viz_fil[i] = viz_fil[i].format(','.join(['balance'])) -do_rel = False # use scaled values nbatch_hrus = 518 # number of HRUs per batch -# Specify variables of interest -plt_titl = ['Canopy Air Space Energy Balance','Vegetation Energy Balance','Snow Energy Balance','Soil Energy Balance','Vegetation Mass Balance','Snow Mass Balance','Soil Mass Balance','Aquifer Mass Balance', 'Wall Clock Time'] +# Specify variables in files +plt_titl = ['canopy air space energy balance','vegetation energy balance','snow energy balance','soil energy balance','vegetation mass balance','snow mass balance','soil mass balance','aquifer mass balance', 'wall clock time'] leg_titl = ['$W~m^{-3}$'] * 4 +['$num$'] + ['$kg~m^{-2}~s^{-1}$'] * 4 fig_fil= '_hrly_balance_{}_compressed.png' -if do_rel: - fig_fil = '_hrly_scaledBalance_{}_rel_compressed.png' - for i in range(8): - settings[i] = 'scaledB' + settings[i][1:] - plt_titl[i] = 'Scaled ' + plt_titl[i] - leg_titl = ['$s^{-1}$'] * 8 + ['$s$'] - plot_vars = settings.copy() if stat == 'mean': @@ -65,18 +67,13 @@ if stat == 'amax': maxes = [1e-2,1e4,1e4,1e3]+[1e-11,1e-6,1e-7,1e-8] + [2.0] -# Get the albers shapes -main = Path('/home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/albers_projection') +# Get simulation statistics +summa = {} +for i, m in enumerate(method_name): + # Get the aggregated statistics of SUMMA simulations + summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) -# Plot lakes? -plot_lakes = True -# lakes shapefile WHERE IS THIS -#lake_path = Path('C:/Globus endpoint/HydroLAKES/HydroLAKES_polys_v10_shp') -#lake_name = 'HydroLAKES_polys_v10_subset_NA.shp' -## Control file handling -# Store the name of the 'active' file in a variable -controlFile = 'plot_control_NorthAmerica.txt' # Function to extract a given setting from the control file def read_from_control( file, setting ): @@ -112,62 +109,66 @@ def make_default_path(suffix): return defaultPath -## Catchment shapefile location and variable names -# HM catchment shapefile path & name -hm_catchment_path = read_from_control(controlFile,'catchment_shp_path') -hm_catchment_name = read_from_control(controlFile,'catchment_shp_name') -# Specify default path if needed -if hm_catchment_path == 'default': - hm_catchment_path = make_default_path('shapefiles/catchment') # outputs a Path() -else: - hm_catchment_path = Path(hm_catchment_path) # make sure a user-specified path is a Path() - -# Find the GRU and HRU identifiers -hm_hruid = read_from_control(controlFile,'catchment_shp_hruid') - -## River network shapefile location and variable names -# Plot rivers? -plot_rivers = False -# River network path & name -river_network_path = read_from_control(controlFile,'river_network_shp_path') -river_network_name = read_from_control(controlFile,'river_network_shp_name') -# Specify default path if needed -if river_network_path == 'default': - river_network_path = make_default_path('shapefiles/river_network') # outputs a Path() +if run_local: + # Make stubs to check if the plots set up properly + plot_lakes = False + plot_rivers = False + + # Create a mock DataFrame + from shapely.geometry import Point + + s = summa[method_name[0]][plot_vars[0]].sel(stat=stat) + mock_data = { + 'hm_hruid': s.hru.values[range(100)], # Example HRU IDs + 'geometry': [Point(x, y) for x, y in zip(range(100), range(100))] # Simple geometries + } + bas_albers = gpd.GeoDataFrame(mock_data, geometry='geometry') + hm_hruid = 'hm_hruid' # Correctly define the variable name in the shapefile + xmin, ymin, xmax, ymax = bas_albers.total_bounds + else: - river_network_path = Path(river_network_path) # make sure a user-specified path is a Path() + # Get the albers shapes + main = Path('/home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/albers_projection') + plot_lakes = True + plot_rivers = False + + # Control file handling + controlFile = 'plot_control_NorthAmerica.txt' + + # HM catchment shapefile path & name + hm_catchment_path = read_from_control(controlFile,'catchment_shp_path') + hm_catchment_name = read_from_control(controlFile,'catchment_shp_name') + # Specify default path if needed + if hm_catchment_path == 'default': + hm_catchment_path = make_default_path('shapefiles/catchment') # outputs a Path() + else: + hm_catchment_path = Path(hm_catchment_path) # make sure a user-specified path is a Path() -# Find the segment ID -seg_id = read_from_control(controlFile,'river_network_shp_segid') + # Find the GRU and HRU identifiers + hm_hruid = read_from_control(controlFile,'catchment_shp_hruid') -## Load all shapefiles and project to Albers Conformal Conic and reproject -# Set the target CRS -acc = 'ESRI:102008' + ## River network shapefile location and variable names + river_network_path = read_from_control(controlFile,'river_network_shp_path') + river_network_name = read_from_control(controlFile,'river_network_shp_name') + # Specify default path if needed + if river_network_path == 'default': + river_network_path = make_default_path('shapefiles/river_network') # outputs a Path() + else: + river_network_path = Path(river_network_path) # make sure a user-specified path is a Path() -# catchment shapefile, first 2 lines throw error so cutting them -#bas = gpd.read_file(hm_catchment_path/hm_catchment_name) -#bas_albers = bas.to_crs(acc) -bas_albers = gpd.read_file(main/'basin.shp') -xmin, ymin, xmax, ymax = bas_albers.total_bounds + # Find the segment ID + seg_id = read_from_control(controlFile,'river_network_shp_segid') -# river network shapefile, first 2 lines throw error so cutting them -if plot_rivers: - #riv = gpd.read_file(river_network_path/river_network_name) - #riv_albers = riv.to_crs(acc) - riv_albers = gpd.read_file(main/'river.shp') + ## Load all shapefiles and project to Albers Conformal Conic and reproject + acc = 'ESRI:102008' # Set the target CRS + + bas_albers = gpd.read_file(main/'basin.shp') + xmin, ymin, xmax, ymax = bas_albers.total_bounds + + if plot_rivers: riv_albers = gpd.read_file(main/'river.shp') + if plot_lakes: lak_albers = gpd.read_file(main/'lakes.shp') -# lakes shapefile, first 2 lines throw error so cutting them -if plot_lakes: - #lakes = gpd.read_file(lake_path/lake_name) - #lak_albers = lakes.to_crs(acc) - lak_albers = gpd.read_file(main/'lakes.shp') -## Pre-processing, map SUMMA sims to catchment shapes -# Get the aggregated statistics of SUMMA simulations -summa = {} -for i, m in enumerate(method_name): - # Get the aggregated statistics of SUMMA simulations - summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) # Match the accummulated values to the correct HRU IDs in the shapefile hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile @@ -187,7 +188,6 @@ def make_default_path(suffix): bas_albers[plot_var+m] = np.nan hru_ind = [i for i, hru_id in enumerate(hru_ids_shp.values) if hru_id in s.hru.values] # if some missing bas_albers.loc[hru_ind, plot_var+m] = s.sel(hru=hru_ids_shp.values[hru_ind]).values - #bas_albers[plot_var+m]= s.sel(hru=hru_ids_shp.values) # Select lakes of a certain size for plotting if plot_lakes: @@ -199,8 +199,9 @@ def make_default_path(suffix): large_lakes_albers = lak_albers.loc[(lak_albers['Lake_area'] > minSize) & in_domain & (~out_domain) ] lake_col = (8/255,81/255,156/255) -##Figure + +# Figure def run_loop(j,var,the_max): stat0 = stat @@ -216,13 +217,9 @@ def run_loop(j,var,the_max): if stat0 == 'mean': stat_word = 'mean' if stat0 == 'amax': stat_word = 'max' - # colorbar axes - f_x_mat = [0.46,0.96,0.46,0.96] - f_y_mat = [0.55,0.55,0.07,0.07] - - for i,(m,f_x,f_y) in enumerate(zip(method_name,f_x_mat,f_y_mat)): - r = i//2 - c = i-r*2 + for i,m in enumerate(method_name): + r = i//ncol + base_row + c = i - (r-base_row)*ncol # Plot the data with the full extent of the bas_albers shape bas_albers.plot(ax=axs[r,c], column=var+m, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) @@ -233,20 +230,39 @@ def run_loop(j,var,the_max): axs[r,c].set_ylim(ymin, ymax) # Custom colorbar - cax = fig.add_axes([f_x,f_y,0.02,0.375]) + f_x = base_f_x + (subplot_width + margin_x) * (c + 1) + f_y = base_f_y + (subplot_height + margin_y) * (nrow - 1 - r) # Subtract from nrow - 1 because y increases upwards + + cax = fig.add_axes([f_x,f_y,0.04/nrow,0.75/ncol]) sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) sm.set_array([]) cbr = fig.colorbar(sm, cax=cax) #, extend='max') #if max extend can't get title right cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titl[j]), labelpad=40, rotation=270) - if do_rel and var!='wallClockTime': cbr.ax.set_ylabel('scaled '+ stat_word, labelpad=40, rotation=270) - - #cbr.ax.yaxis.set_offset_position('right') # lakes if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) -for i,(var,the_max) in enumerate(zip(plot_vars,maxes)): - + + +# Specify plotting options +if one_plot: + use_vars = [1,2,3] + use_meth = [0,2,4] +else: + use_vars = [0,1,2,3,4,5,6,7,8] + use_meth = [0,1,2,3] +plot_vars = [plot_vars[i] for i in use_vars] +plt_titl = [plt_titl[i] for i in use_vars] +leg_titl = [leg_titl[i] for i in use_vars] +maxes = [maxes[i] for i in use_vars] +method_name = [method_name[i] for i in use_meth] + +if one_plot: + ncol = len(use_meth) + nrow = 3 + if len(use_meth)!=3: + print('Assuming 3 methods for plot ncol in code') + sys.exit() # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug if 'compressed' in fig_fil: plt.rcParams.update({'font.size': 27}) @@ -254,17 +270,76 @@ def run_loop(j,var,the_max): plt.rcParams.update({'font.size': 100}) if 'compressed' in fig_fil: - fig,axs = plt.subplots(2,2,figsize=(35,28)) + fig,axs = plt.subplots(nrow,ncol,figsize=(17*ncol,14*nrow)) else: - fig,axs = plt.subplots(2,2,figsize=(140,133)) + fig,axs = plt.subplots(nrow,ncol,figsize=(70*ncol,58*nrow)) - # Remove the fourth subplot - #fig.delaxes(axs[1, 1]) - - fig.suptitle('{} Hourly Statistics'.format(plt_titl[i]), fontsize=40,y=1.05) + fig.suptitle('hourly statistics', fontsize=40,y=1.05) plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines plt.tight_layout() + + subplot_width = 1.0 / ncol + subplot_height = 1.0 / nrow + margin_x = 0.0 # Adjust this value as needed + margin_y = -0.03/nrow # Adjust this value as needed + base_f_x = -0.08/ncol # Adjust this value as needed + base_f_y = 0.14/nrow # Adjust this value as needed + +else: + #size hardwired to 2x2 for now + ncol = 2 + nrow = 2 + if len(method_name)>4: + print('Too many methods for 2x2 plot') + sys.exit() + + base_row = 0 + plt_name = [f"({chr(97+n)}) {plt_name0[i]}" for n,i in enumerate(use_meth)] + + subplot_width = 1.0 / ncol + subplot_height = 1.0 / nrow + margin_x = 0.0 # Adjust this value as needed + margin_y = -0.03/nrow # Adjust this value as needed + base_f_x = -0.08/ncol # Adjust this value as needed + base_f_y = 0.14/nrow # Adjust this value as needed + +for i,(var,the_max) in enumerate(zip(plot_vars,maxes)): + + if one_plot: + # Reset the names + base_row = i + plt_name = [f"({chr(97+n+i*len(use_meth))}) {plt_titl[i] + ' ' + plt_name0[j]}" for n,j in enumerate(use_meth)] + else: + # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug + if 'compressed' in fig_fil: + plt.rcParams.update({'font.size': 27}) + else: + plt.rcParams.update({'font.size': 100}) + + if 'compressed' in fig_fil: + fig,axs = plt.subplots(nrow,ncol,figsize=(17*ncol,14*nrow)) + else: + fig,axs = plt.subplots(nrow,ncol,figsize=(70*ncol,58*nrow)) + + # Remove the extra subplots + if len(method_name) < nrow*ncol: + for j in range(len(method_name),nrow*ncol): + r = j//ncol + c = j-r*ncol + fig.delaxes(axs[r, c]) + + fig.suptitle('{} hourly statistics'.format(plt_titl[i]), fontsize=40,y=1.05) + plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines + plt.tight_layout() + run_loop(i,var,the_max) - fig_fil1 = (var+fig_fil).format(stat) - # Save + + if not one_plot: + # Save the figure + fig_fil1 = (var+fig_fil).format(stat) + plt.savefig(viz_dir/fig_fil1, bbox_inches='tight', transparent=True) + +if one_plot: + # Save the figure + fig_fil1 = ('all'+fig_fil).format(stat) plt.savefig(viz_dir/fig_fil1, bbox_inches='tight', transparent=True) diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index 6b6d2d903..ac808fcf9 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -22,49 +22,40 @@ from matplotlib.colors import LogNorm from matplotlib.colors import ListedColormap -viz_dir = Path('/home/avanb/scratch/statistics') -nbatch_hrus = 518 # number of HRUs per batch -do_rel = False # plot relative to the benchmark simulation -do_heat = True # plot heatmaps instead of scatterplots -rainbow_cmap = plt.cm.get_cmap('rainbow', 256) # Get the rainbow colormap -rainbow_colors = rainbow_cmap(np.linspace(0, 1, 256)) -rainbow_colors_with_white = np.vstack((rainbow_colors, [1, 1, 1, 1])) -custom_cmap = ListedColormap(rainbow_colors_with_white, name='rainbow_white') -custom_cmap.set_under('white') # Ensure that values under the lower bound are white +do_rel = False # true is plot relative to the benchmark simulation +do_heat = False # true is plot heatmaps instead of scatterplots +run_local = True # true is run on local machine, false is run on cluster # which statistics to plot, can do both do_vars = True -do_balance = False +do_balance = True -run_local = True if run_local: stat = 'rmnz' - viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be1','sundials_1en6'] - plt_name=['BE1','SUNDIALS'] - method_name2=method_name - plt_name2=plt_name - + viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics_en') else: import sys - # The first input argument specifies the run where the files are stat = sys.argv[1] - #method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument - #plt_name=['BE1','IDAe-4','BE4','BE8','BE16','BE32','IDAe-6'] #maybe make this an argument - #method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument - #plt_name=['BE1','BE16','BE32','SUNDIALS'] #maybe make this an argument - method_name=['be1','be1cm','be1en','sundials_1en6cm'] - plt_name=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp'] - method_name2=method_name+['sundials_1en8cm'] - plt_name2=plt_name+['reference solution'] + viz_dir = Path('/home/avanb/scratch/statistics') -if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE -# Copy the original method names and plot names -method_name0 = np.copy(method_name) -plt_name0 = np.copy(plt_name) -method_name20 = np.copy(method_name2) -plt_name20 = np.copy(plt_name2) +#method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] +#plt_name=['BE1','IDAe-4','BE4','BE8','BE16','BE32','IDAe-6'] +method_name=['be1','be16','be32','sundials_1en6'] +plt_name=['BE1','BE16','BE32','SUNDIALS'] +method_name=['be1','be1cm','be1en','sundials_1en6cm'] +plt_name=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp'] +method_name2=method_name+['sundials_1en8cm'] +plt_name2=plt_name+['reference solution'] + +rainbow_cmap = plt.cm.get_cmap('rainbow', 256) # Get the rainbow colormap +rainbow_colors = rainbow_cmap(np.linspace(0, 1, 256)) +rainbow_colors_with_white = np.vstack((rainbow_colors, [1, 1, 1, 1])) +custom_cmap = ListedColormap(rainbow_colors_with_white, name='rainbow_white') +custom_cmap.set_under('white') # Ensure that values under the lower bound are white + + +if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] @@ -90,12 +81,12 @@ hru_size = summa1[m].sizes['hru'] numbin = int(np.sqrt(hru_size/10)) -maxcol = numbin**2/75 -do_clip = True # choose if want the heat values clipped (True) or plotted as white if over maxcol (False) +maxcolor = numbin**2/75 +do_clip = True # choose if want the heat values clipped (True) or plotted as white if over maxcolor (False) def run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm,rep,mx): - r = i//2 - c = i-r*2 + r = i//ncol + c = i-r*ncol global method_name # Declare method_name as global to modify its global value global plt_name # Declare plt_name as global to modify its global value method_name = np.copy(method_name0) @@ -141,9 +132,8 @@ def run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm,rep,mx): # Data if do_rel: s_rel = summa[method_name[0]][var].sel(stat=statr) # make the axes the same - if do_heat: - do_same = False - if len(method_name)>1: + do_same = False + if do_heat and len(method_name)>1: do_same = True mxx = 0.0 mnx = 1.0 @@ -174,11 +164,11 @@ def run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm,rep,mx): else: method_name = [method_name0[0]] plt_name = [plt_name0[0]] - else: # only one method - mxx = 0.0 - mnx = 1.0 - mxy = 0.0 - mny = 1.0 + else: # only one method + mxx = 0.0 + mnx = 1.0 + mxy = 0.0 + mny = 1.0 for j, m in enumerate(method_name): s = summa[m][var].sel(stat=[stat,stat0]) if var == 'scalarTotalET': @@ -276,7 +266,7 @@ def run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm,rep,mx): zi, _, _ = np.histogram2d(x_points, y_points, bins=[x_edges, y_edges]) zi = zi.T # Transpose the histogram to match the pcolormesh orientation if do_clip: - zi_clipped = np.where(zi > 0.95*maxcol, 0.95*maxcol, zi) # Clip zi.T so that max values are maxcol with a little buffer + zi_clipped = np.where(zi > 0.95*maxcolor, 0.95*maxcolor, zi) # Clip zi.T so that max values are maxcolor with a little buffer else: zi_clipped = zi # don't clip @@ -293,7 +283,7 @@ def run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm,rep,mx): Y = 10**Y # Adjust the pcolormesh call to use the centers and compatible shading - norm = LogNorm(vmin=1, vmax=maxcol) + norm = LogNorm(vmin=1, vmax=maxcolor) mesh = axs[r, c].pcolormesh(X, Y, zi_clipped, shading='gouraud', cmap=custom_cmap, zorder=0,norm=norm) fig.colorbar(mesh, ax=axs[r, c], label='GRU count') @@ -323,8 +313,8 @@ def run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm,rep,mx): axs[r,c].set_ylabel(stat0_word + ' [{}]'.format(leg_t0)) def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): - r = i//2 - c = i-r*2 + r = i//ncol + c = i-r*ncol global method_name2 # Declare method_name as global to modify its global value global plt_name2 # Declare plt_name as global to modify its global value method_name2 = np.copy(method_name20) @@ -342,9 +332,8 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): wordy = wordx # Data - if do_heat: - # make the axes the same - do_same = False + do_same = False + if do_heat and len(method_name)>1: if len(method_name2)>1: do_same = True mxx = 0.0 @@ -367,11 +356,11 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): else: method_name2 = [method_name20[0]] plt_name2 = [plt_name20[0]] - else: # only one method - mxx = 0.0 - mnx = 1.0 - mxy = 0.0 - mny = 1.0 + else: # only one method + mxx = 0.0 + mnx = 1.0 + mxy = 0.0 + mny = 1.0 for j, m in enumerate(method_name2): # Get the statistics, remove 9999 (should be nan, but just in case) s = np.fabs(summa1[m][var].sel(stat=stat0)).where(lambda x: x != 9999, np.nan) @@ -411,7 +400,7 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): zi, _, _ = np.histogram2d(x_points, y_points, bins=[x_edges, y_edges]) zi = zi.T # Transpose the histogram to match the pcolormesh orientation if do_clip: - zi_clipped = np.where(zi > 0.95*maxcol, 0.95*maxcol, zi) # Clip zi.T so that max values are maxcol with a little buffer + zi_clipped = np.where(zi > 0.95*maxcolor, 0.95*maxcolor, zi) # Clip zi.T so that max values are maxcolor with a little buffer else: zi_clipped = zi # don't clip # Calculate bin centers from edges for X and Y @@ -422,7 +411,7 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): if ly: Y = 10**Y # Adjust the pcolormesh call to use the centers and compatible shading - norm = LogNorm(vmin=1, vmax=maxcol) + norm = LogNorm(vmin=1, vmax=maxcolor) mesh = axs[r, c].pcolormesh(X, Y, zi_clipped, shading='gouraud', cmap=custom_cmap, zorder=0,norm=norm) fig.colorbar(mesh, ax=axs[r, c], label='GRU count') @@ -454,21 +443,25 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): plt.rcParams['ytick.major.width'] = 2 if do_vars: # Specify variables of interest - use_vars = [1,1,2,2,4,4 ] - logx = np.zeros(len(use_vars)) # no log scale x axis - logy = [0,0,0,0,1,1] # log scale y axis - rep = [0,1,0,1,0,1] - #use_vars = [0,1,2,3,4] - #logx = np.ones(len(use_vars)) # log scale x axis - #logy = np.ones(len(use_vars)) # log scale y axis - #rep = np.zeros(len(use_vars)) + if do_heat: + use_vars = [1,1,2,2,4,4] + logx = np.zeros(len(use_vars)) # no log scale x axis + logy = [0,0,0,0,1,1] # log scale y axis + rep = [0,1,0,1,0,1] + use_meth = [0,3] + else: + use_vars = [0,1,2,3,4] + logx = np.ones(len(use_vars)) # log scale x axis + logy = np.ones(len(use_vars)) # log scale y axis + rep = np.zeros(len(use_vars)) + use_meth = [0,1,2,3] plot_vars = settings plt_titl = ['snow water equivalent','total soil water content','total evapotranspiration', 'total water on the vegetation canopy','average routed runoff'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$mm~y^{-1}$'] leg_titl0 = ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$mm~y^{-1}$'] leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$','$mm~h^{-1}$'] - + #to zoom the heat x axis set these maxes = [0.0,0.0,0.0,0.0,0.0,0.0] #initialize if stat == 'rmse' or stat=='rmnz': @@ -488,6 +481,18 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): leg_titlm = [leg_titlm[i] for i in use_vars] maxes = [maxes[i] for i in use_vars] + method_name = [method_name[i] for i in use_meth] + plt_name = [plt_name[i] for i in use_meth] + method_name0 = np.copy(method_name) + plt_name0 = np.copy(plt_name) + + if do_heat: + ncol = 2 # assumes to 2 methods + nrow = int(plot_vars/ncol) + else: + ncol = 2 + nrow = len(plot_vars)//ncol + 1 + fig_fil = 'Hrly_diff_scat_{}_{}' if do_rel: fig_fil = fig_fil + '_rel' if do_heat: @@ -504,10 +509,11 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): else: plt.rcParams.update({'font.size': 100}) - if 'compressed' in fig_fil: - fig,axs = plt.subplots(3,2,figsize=(35,38)) + if 'compressed' in fig_fil: + fig,axs = plt.subplots(nrow,ncol,figsize=(17*ncol,13*nrow)) else: - fig,axs = plt.subplots(3,2,figsize=(140,133)) + fig,axs = plt.subplots(nrow,ncol,figsize=(70*ncol,54*nrow)) + #fig.suptitle('Hourly Errors and Values for each GRU', fontsize=40) fig.subplots_adjust(hspace=0.33, wspace=0.17) # Adjust the bottom margin, vertical space, and horizontal space @@ -515,10 +521,10 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm,rep,mx) # Remove the extra subplots - if (len(plot_vars)) < 6: - for i in range(len(plot_vars),6): - r = i//2 - c = i-r*2 + if not do_heat and (len(plot_vars)) < nrow*ncol: + for i in range(len(plot_vars),nrow*ncol): + r = i//ncol + c = i-r*ncol fig.delaxes(axs[r, c]) # Save @@ -526,12 +532,19 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): if do_balance: # Specify variables of interest - use_vars = [0,0,1,1,2,2] - rep = [0,1,0,1,0,1] - #use_vars = [0,1,2,3] - logx = np.ones(len(use_vars)) # log scale x axis - logy = np.ones(len(use_vars)) # log scale y axis - #rep = np.zeros(len(use_vars)) + if do_heat: + use_vars = [0,0,1,1,2,2] + rep = [0,1,0,1,0,1] + logx = np.ones(len(use_vars)) # log scale x axis + logy = np.ones(len(use_vars)) # log scale y axis + use_meth = [0,3] + else: + use_vars = [0,1,2,3] + logx = np.zeros(len(use_vars)) # no log scale x axis + logx = np.ones(len(use_vars)) # log scale x axis + logy = np.ones(len(use_vars)) # log scale y axis + rep = np.zeros(len(use_vars)) + use_meth = [0,1,2,3,4] plot_vars = ['balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceCasNrg','wallClockTime'] comp_vars = ['balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','numberFluxCalc'] @@ -544,6 +557,18 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): plt_titl = [f"({chr(97+n)}) {plt_titl[i]}" for n,i in enumerate(use_vars)] leg_titl = [leg_titl[i] for i in use_vars] leg_titl0 = [leg_titl0[i] for i in use_vars] + method_name2 = [method_name2[i] for i in use_meth] + plt_name2 = [plt_name2[i] for i in use_meth] + + method_name20 = np.copy(method_name2) + plt_name20 = np.copy(plt_name2) + + if do_heat: + ncol = 2 # assumes to 2 methods + nrow = int(plot_vars/ncol) + else: + ncol = 2 + nrow = len(plot_vars)//ncol + 1 fig_fil = 'Hrly_balance_scat_{}' if do_heat: @@ -558,20 +583,22 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): else: plt.rcParams.update({'font.size': 100}) - if 'compressed' in fig_fil: - fig,axs = plt.subplots(3,2,figsize=(35,38)) + if 'compressed' in fig_fil: + fig,axs = plt.subplots(nrow,ncol,figsize=(17*ncol,13*nrow)) else: - fig,axs = plt.subplots(3,2,figsize=(140,160)) + fig,axs = plt.subplots(nrow,ncol,figsize=(70*ncol,54*nrow)) + fig.subplots_adjust(hspace=0.33, wspace=0.17) # Adjust the bottom margin, vertical space, and horizontal space #fig.suptitle('Scatterplot of Hourly Statistics for each GRU', fontsize=40,y=1.0) for i,(var,comp,lx,ly,leg_t,leg_t0,plt_t,rep) in enumerate(zip(plot_vars,comp_vars,logx,logy,leg_titl,leg_titl0,plt_titl,rep)): run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,rep) - if (len(plot_vars)) < 6: - for i in range(len(plot_vars),6): - r = i//2 - c = i-r*2 + # Remove the extra subplots + if not do_heat and (len(plot_vars)) < nrow*ncol: + for i in range(len(plot_vars),nrow*ncol): + r = i//ncol + c = i-r*ncol fig.delaxes(axs[r, c]) # Save plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) diff --git a/utils/steps_per_GRU.py b/utils/steps_per_GRU.py index 2478ad6a4..c7ef39065 100644 --- a/utils/steps_per_GRU.py +++ b/utils/steps_per_GRU.py @@ -20,8 +20,6 @@ import copy import pandas as pd -viz_dir = Path('/home/avanb/scratch/statistics') - run_local = False if run_local: stat = 'amax' @@ -31,6 +29,7 @@ import sys # The first input argument specifies the run where the files are stat = sys.argv[1] + viz_dir = Path('/home/avanb/scratch/statistics') method_name=['be1','be4','be8','be16','be32'] #maybe make this an argument method_name=['be1'] #maybe make this an argument #method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument diff --git a/utils/wallClock_per_GRU.py b/utils/wallClock_per_GRU.py index 23d6c2f4c..1402b690a 100644 --- a/utils/wallClock_per_GRU.py +++ b/utils/wallClock_per_GRU.py @@ -19,17 +19,16 @@ import copy import pandas as pd -viz_dir = Path('/home/avanb/scratch/statistics') -nbatch_hrus = 518 # number of HRUs per batch - run_local = False if run_local: viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') method_name=['be1'] #maybe make this an argument else: import sys + viz_dir = Path('/home/avanb/scratch/statistics') method_name=['be1','be4','be8','be16','be32'] #sundials will not show node differences as much +nbatch_hrus = 518 # number of HRUs per batch # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] viz_fil = method_name.copy() diff --git a/utils/wallStd_per_GRU.py b/utils/wallStd_per_GRU.py index 17781b15d..779b4f3be 100644 --- a/utils/wallStd_per_GRU.py +++ b/utils/wallStd_per_GRU.py @@ -19,14 +19,13 @@ import copy import pandas as pd -viz_dir = Path('/home/avanb/scratch/statistics') - run_local = False if run_local: viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') method_name=['be1en','be1en'] #cm','be1en','be1lu'] #maybe make this an argument else: import sys + viz_dir = Path('/home/avanb/scratch/statistics') method_name=['sundials_1en4','sundials_1en6','sundials_1en8'] #maybe make this an argument # Simulation statistics file locations From 49733d9fd94e3368a64ed7fdb331ee9e05ff56bb Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 8 Jul 2024 16:08:36 +0900 Subject: [PATCH 1349/1472] utils --- utils/plot_per_GRUMult.py | 96 ++++++++------------- utils/plot_per_GRUMultBal.py | 67 +++++---------- utils/scat_per_GRU.py | 159 ++++++++++++++++++----------------- 3 files changed, 144 insertions(+), 178 deletions(-) diff --git a/utils/plot_per_GRUMult.py b/utils/plot_per_GRUMult.py index 233b9467a..3efa3947c 100644 --- a/utils/plot_per_GRUMult.py +++ b/utils/plot_per_GRUMult.py @@ -81,7 +81,7 @@ if stat == 'kgem': maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] if stat == 'mean' or stat == 'mnnz': - maxes = [80,1600,1500,8,3000,10e-3] #[80,1500,5e-5,8,1e-7,10e-3] + maxes = [80,1700,2000,8,5000,10e-3] #[80,1500,5e-5,8,1e-7,10e-3] if do_rel: maxes = [1.1,1.1,1.1,1.1,1.1,10e-3] if stat == 'amax': maxes = [240,1800,3.5,25,7.5,0.2] #[240,1800,1e-3,25,2e-6,0.2] @@ -257,7 +257,7 @@ def run_loop(j,var,the_max): my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap my_cmap.set_bad(color='white') #nan color white vmin,vmax = 0, the_max - if stat =='mean' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 600, the_max + if (stat =='mean' or stat=='mnnz') and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 400, the_max if stat =='amax' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 1000, the_max if (stat == 'mean' or stat == 'mnnz' or stat == 'amax') and var!='wallClockTime' and do_rel: vmin,vmax = 0.9, the_max @@ -303,30 +303,30 @@ def run_loop(j,var,the_max): axs[r,c].set_ylim(ymin, ymax) # Custom colorbar - f_x = base_f_x + (subplot_width + margin_x) * (c + 1) - f_y = base_f_y + (subplot_height + margin_y) * (nrow - 1 - r) # Subtract from nrow - 1 because y increases upwards - - cax = fig.add_axes([f_x,f_y,0.04/nrow,0.75/ncol]) - if m=='diff': - sm = matplotlib.cm.ScalarMappable(cmap=my_cmap2, norm=norm2) - else: - sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) - sm._A = [] - cbr = fig.colorbar(sm, cax=cax) - if stat == 'kgem': - cbr.ax.set_ylabel(stat_word0, labelpad=40, rotation=270) - else: - if do_rel and var!='wallClockTime': - cbr.ax.set_ylabel('relative '+ stat_word0, labelpad=40, rotation=270) + if i==len(method_name)-1: + if m=='diff': + sm = matplotlib.cm.ScalarMappable(cmap=my_cmap2, norm=norm2) else: - cbr.ax.set_ylabel(stat_word0 + ' [{}]'.format(leg_titl[j]), labelpad=40, rotation=270) - - if var=='scalarTotalSoilWat' and (stat =='mean' or stat =='mnnz'): - # Customizing the tick labels to include negative signs - def format_tick(value, tick_number): - rounded_value = int(round(value,-2)) - return f"-{rounded_value}" - cbr.ax.yaxis.set_major_formatter(ticker.FuncFormatter(format_tick)) + sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) + sm._A = [] + if one_plot: + cbr = fig.colorbar(sm, ax=axs_list[r*len(method_name):(r+1)*len(method_name)],aspect=27/3) + else: + cbr = fig.colorbar(sm, ax=axs_list,aspect=27/3*nrow) + if stat == 'kgem': + cbr.ax.set_ylabel(stat_word0) + else: + if do_rel and var!='wallClockTime': + cbr.ax.set_ylabel('relative '+ stat_word0) + else: + cbr.ax.set_ylabel(stat_word0 + ' [{}]'.format(leg_titl[j])) + + if var=='scalarTotalET' and (stat =='mean' or stat =='mnnz'): + # Customizing the tick labels to include negative signs + def format_tick(value, tick_number): + rounded_value = int(round(value,-2)) + return f"-{rounded_value}" + cbr.ax.yaxis.set_major_formatter(ticker.FuncFormatter(format_tick)) # lakes if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) @@ -348,31 +348,19 @@ def format_tick(value, tick_number): if one_plot: ncol = len(use_meth) - nrow = 3 - if len(use_meth)!=3: - print('Assuming 3 methods for plot ncol in code') - sys.exit() - # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug - if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 27}) - else: - plt.rcParams.update({'font.size': 100}) + nrow = len(use_vars) + # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug if 'compressed' in fig_fil: - fig,axs = plt.subplots(nrow,ncol,figsize=(17*ncol,14*nrow)) + plt.rcParams.update({'font.size': 33}) + fig,axs = plt.subplots(nrow,ncol,figsize=(15*ncol,14*nrow),constrained_layout=True) else: - fig,axs = plt.subplots(nrow,ncol,figsize=(70*ncol,58*nrow)) + plt.rcParams.update({'font.size': 120}) + fig,axs = plt.subplots(nrow,ncol,figsize=(62*ncol,58*nrow),constrained_layout=True) + axs_list = axs.ravel().tolist() fig.suptitle('hourly statistics', fontsize=40,y=1.05) plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines - plt.tight_layout() - - subplot_width = 1.0 / ncol - subplot_height = 1.0 / nrow - margin_x = 0.00 # Adjust this value as needed - margin_y = -0.03/nrow # Adjust this value as needed - base_f_x = -0.08/ncol # Adjust this value as needed - base_f_y = 0.14/nrow # Adjust this value as needed else: #size hardwired to 2x2 for now @@ -385,13 +373,6 @@ def format_tick(value, tick_number): base_row = 0 plt_name = [f"({chr(97+n)}) {plt_name0[i]}" for n,i in enumerate(use_meth)] - subplot_width = 1.0 / ncol - subplot_height = 1.0 / nrow - margin_x = 0.0 # Adjust this value as needed - margin_y = -0.03/nrow # Adjust this value as needed - base_f_x = -0.08/ncol # Adjust this value as needed - base_f_y = 0.14/nrow # Adjust this value as needed - for i,(var,the_max) in enumerate(zip(plot_vars,maxes)): if one_plot: @@ -401,14 +382,11 @@ def format_tick(value, tick_number): else: # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 27}) - else: - plt.rcParams.update({'font.size': 100}) - - if 'compressed' in fig_fil: - fig,axs = plt.subplots(nrow,ncol,figsize=(17*ncol,14*nrow)) + plt.rcParams.update({'font.size': 33}) + fig,axs = plt.subplots(nrow,ncol,figsize=(15*ncol,14*nrow),constrained_layout=True) else: - fig,axs = plt.subplots(nrow,ncol,figsize=(70*ncol,58*nrow)) + plt.rcParams.update({'font.size': 120}) + fig,axs = plt.subplots(nrow,ncol,figsize=(62*ncol,58*nrow),constrained_layout=True) # Remove the extra subplots if len(method_name) < nrow*ncol: @@ -417,9 +395,9 @@ def format_tick(value, tick_number): c = j-r*ncol fig.delaxes(axs[r, c]) + axs_list = axs.ravel().tolist() fig.suptitle('{} hourly statistics'.format(plt_titl[i]), fontsize=40,y=1.05) plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines - plt.tight_layout() run_loop(i,var,the_max) diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index 7a0ca0f82..c5ebdb888 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -230,14 +230,15 @@ def run_loop(j,var,the_max): axs[r,c].set_ylim(ymin, ymax) # Custom colorbar - f_x = base_f_x + (subplot_width + margin_x) * (c + 1) - f_y = base_f_y + (subplot_height + margin_y) * (nrow - 1 - r) # Subtract from nrow - 1 because y increases upwards - - cax = fig.add_axes([f_x,f_y,0.04/nrow,0.75/ncol]) - sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) - sm.set_array([]) - cbr = fig.colorbar(sm, cax=cax) #, extend='max') #if max extend can't get title right - cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titl[j]), labelpad=40, rotation=270) + # Custom colorbar + if i==len(method_name)-1: + sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) + sm._A = [] + if one_plot: + cbr = fig.colorbar(sm, ax=axs_list[r*len(method_name):(r+1)*len(method_name)],aspect=27/3) + else: + cbr = fig.colorbar(sm, ax=axs_list,aspect=27/3*nrow) + cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titl[j])) # lakes if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) @@ -249,7 +250,7 @@ def run_loop(j,var,the_max): use_vars = [1,2,3] use_meth = [0,2,4] else: - use_vars = [0,1,2,3,4,5,6,7,8] + use_vars = [0,1,2,3,4,5,6,7] use_meth = [0,1,2,3] plot_vars = [plot_vars[i] for i in use_vars] plt_titl = [plt_titl[i] for i in use_vars] @@ -259,31 +260,19 @@ def run_loop(j,var,the_max): if one_plot: ncol = len(use_meth) - nrow = 3 - if len(use_meth)!=3: - print('Assuming 3 methods for plot ncol in code') - sys.exit() - # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug - if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 27}) - else: - plt.rcParams.update({'font.size': 100}) + nrow = len(use_vars) + # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug if 'compressed' in fig_fil: - fig,axs = plt.subplots(nrow,ncol,figsize=(17*ncol,14*nrow)) + plt.rcParams.update({'font.size': 33}) + fig,axs = plt.subplots(nrow,ncol,figsize=(15*ncol,14*nrow),constrained_layout=True) else: - fig,axs = plt.subplots(nrow,ncol,figsize=(70*ncol,58*nrow)) + plt.rcParams.update({'font.size': 120}) + fig,axs = plt.subplots(nrow,ncol,figsize=(62*ncol,58*nrow),constrained_layout=True) + axs_list = axs.ravel().tolist() fig.suptitle('hourly statistics', fontsize=40,y=1.05) plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines - plt.tight_layout() - - subplot_width = 1.0 / ncol - subplot_height = 1.0 / nrow - margin_x = 0.0 # Adjust this value as needed - margin_y = -0.03/nrow # Adjust this value as needed - base_f_x = -0.08/ncol # Adjust this value as needed - base_f_y = 0.14/nrow # Adjust this value as needed else: #size hardwired to 2x2 for now @@ -296,13 +285,6 @@ def run_loop(j,var,the_max): base_row = 0 plt_name = [f"({chr(97+n)}) {plt_name0[i]}" for n,i in enumerate(use_meth)] - subplot_width = 1.0 / ncol - subplot_height = 1.0 / nrow - margin_x = 0.0 # Adjust this value as needed - margin_y = -0.03/nrow # Adjust this value as needed - base_f_x = -0.08/ncol # Adjust this value as needed - base_f_y = 0.14/nrow # Adjust this value as needed - for i,(var,the_max) in enumerate(zip(plot_vars,maxes)): if one_plot: @@ -312,14 +294,11 @@ def run_loop(j,var,the_max): else: # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 27}) + plt.rcParams.update({'font.size': 33}) + fig,axs = plt.subplots(nrow,ncol,figsize=(15*ncol,14*nrow),constrained_layout=True) else: - plt.rcParams.update({'font.size': 100}) - - if 'compressed' in fig_fil: - fig,axs = plt.subplots(nrow,ncol,figsize=(17*ncol,14*nrow)) - else: - fig,axs = plt.subplots(nrow,ncol,figsize=(70*ncol,58*nrow)) + plt.rcParams.update({'font.size': 120}) + fig,axs = plt.subplots(nrow,ncol,figsize=(62*ncol,58*nrow),constrained_layout=True) # Remove the extra subplots if len(method_name) < nrow*ncol: @@ -327,10 +306,10 @@ def run_loop(j,var,the_max): r = j//ncol c = j-r*ncol fig.delaxes(axs[r, c]) - + + axs_list = axs.ravel().tolist() fig.suptitle('{} hourly statistics'.format(plt_titl[i]), fontsize=40,y=1.05) plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines - plt.tight_layout() run_loop(i,var,the_max) diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index ac808fcf9..b1e7865ed 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -23,16 +23,17 @@ from matplotlib.colors import ListedColormap do_rel = False # true is plot relative to the benchmark simulation -do_heat = False # true is plot heatmaps instead of scatterplots +do_heat = True # true is plot heatmaps instead of scatterplots run_local = True # true is run on local machine, false is run on cluster +inferno_col= True # Set to True if want to match geographic plots, False if want rainbow colormap (does not matter if do_heat is False) # which statistics to plot, can do both do_vars = True -do_balance = True +do_balance = False if run_local: stat = 'rmnz' - viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics_en') + viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') else: import sys stat = sys.argv[1] @@ -43,17 +44,20 @@ #plt_name=['BE1','IDAe-4','BE4','BE8','BE16','BE32','IDAe-6'] method_name=['be1','be16','be32','sundials_1en6'] plt_name=['BE1','BE16','BE32','SUNDIALS'] -method_name=['be1','be1cm','be1en','sundials_1en6cm'] -plt_name=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp'] +#method_name=['be1','be1cm','be1en','sundials_1en6cm'] +#plt_name=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp'] method_name2=method_name+['sundials_1en8cm'] plt_name2=plt_name+['reference solution'] -rainbow_cmap = plt.cm.get_cmap('rainbow', 256) # Get the rainbow colormap -rainbow_colors = rainbow_cmap(np.linspace(0, 1, 256)) -rainbow_colors_with_white = np.vstack((rainbow_colors, [1, 1, 1, 1])) -custom_cmap = ListedColormap(rainbow_colors_with_white, name='rainbow_white') -custom_cmap.set_under('white') # Ensure that values under the lower bound are white - +if inferno_col: + custom_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap + custom_cmap.set_bad(color='white') #nan color white +else: # use rainbow colormap, I think looks better + rainbow_cmap = plt.cm.get_cmap('rainbow', 256) # Get the rainbow colormap + rainbow_colors = rainbow_cmap(np.linspace(0, 1, 256)) + rainbow_colors_with_white = np.vstack((rainbow_colors, [1, 1, 1, 1])) + custom_cmap = ListedColormap(rainbow_colors_with_white, name='rainbow_white') + custom_cmap.set_under('white') # Ensure that values under the lower bound are white if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE @@ -158,12 +162,8 @@ def run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm,rep,mx): s = s0.sel(stat=stat0) mxy = max(s.max(),mxy) mny = min(s.min(),mny) - if rep: - method_name = [method_name0[1]] - plt_name = [plt_name0[1]] - else: - method_name = [method_name0[0]] - plt_name = [plt_name0[0]] + method_name = [method_name0[rep]] + plt_name = [plt_name0[rep]] else: # only one method mxx = 0.0 mnx = 1.0 @@ -226,18 +226,18 @@ def run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm,rep,mx): y = s.sel(stat=stat0).values if lx: x = np.where(x > 0, np.log10(x), np.nan) - mnx = np.log10(mnx+1e-30) - mxx = np.log10(mxx+1e-30) + mnx = np.log10(np.where(mnx <= 0, 1e-30, mnx)) + mxx = np.log10(np.where(mxx <= 0, 1e-30, mxx)) if ly: if stat!='mnnz' and stat!='mean': if var == 'scalarTotalET': y = np.where(-y > 0, np.log10(-y), np.nan) - mny = np.log10(-mny+1e-30) - mxy = np.log10(-mxy+1e-30) + mny = np.log10(np.where(-mny <= 0, 1e-30, -mny)) + mxy = np.log10(np.where(-mxy <= 0, 1e-30, -mxy)) else: y = np.where(y > 0, np.log10(y), np.nan) - mny = np.log10(mny) - mxy = np.log10(mxy) + mny = np.log10(np.where(mny <= 0, 1e-30, mny)) + mxy = np.log10(np.where(mxy <= 0, 1e-30, mxy)) x_points.extend(x) y_points.extend(y) x_points = np.array(x_points) # Convert lists to numpy arrays @@ -285,7 +285,7 @@ def run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm,rep,mx): # Adjust the pcolormesh call to use the centers and compatible shading norm = LogNorm(vmin=1, vmax=maxcolor) mesh = axs[r, c].pcolormesh(X, Y, zi_clipped, shading='gouraud', cmap=custom_cmap, zorder=0,norm=norm) - fig.colorbar(mesh, ax=axs[r, c], label='GRU count') + if r==1 and c==len(method_name)-1: fig.colorbar(mesh, ax=axs.ravel().tolist(), label='GRU count',aspect=20/3*nrow) elif not do_heat: if stat=='mnnz' or stat=='mean' or stat=='amax': @@ -311,6 +311,7 @@ def run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm,rep,mx): axs[r,c].set_xlabel(stat_word + ' [{}]'.format(leg_tm)) if stat == 'kgem': axs[r,c].set_xlabel(stat_word) axs[r,c].set_ylabel(stat0_word + ' [{}]'.format(leg_t0)) + if do_heat and c>0: axs[r, c].set_ylabel('') def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): r = i//ncol @@ -333,7 +334,7 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): # Data do_same = False - if do_heat and len(method_name)>1: + if do_heat and len(method_name2)>1: if len(method_name2)>1: do_same = True mxx = 0.0 @@ -350,12 +351,8 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): s = summa1[m][comp].sel(stat=stat0).where(lambda x: x != 9999) mxy = max(s.max(),mxy) mny = min(s.min(),mny) - if rep: - method_name2 = [method_name20[1]] - plt_name2 = [plt_name0[1]] - else: - method_name2 = [method_name20[0]] - plt_name2 = [plt_name20[0]] + method_name2 = [method_name20[rep]] + plt_name2 = [plt_name20[rep]] else: # only one method mxx = 0.0 mnx = 1.0 @@ -373,12 +370,12 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): y = s0.values if lx: x = np.where(x > 0, np.log10(x), np.nan) - mxx = np.log10(mxx+1e-30) - mnx = np.log10(mnx+1e-30) + mxx = np.log10(np.where(mxx <= 0, 1e-30, mxx)) + mnx = np.log10(np.where(mnx <= 0, 1e-30, mnx)) if ly: y = np.where(y > 0, np.log10(y), np.nan) - mxy = np.log10(mxy+1e-30) - mny = np.log10(mny+1e-30) + mxy = np.log10(np.where(mxy <= 0, 1e-30, mxy)) + mny = np.log10(np.where(mny <= 0, 1e-30, mny)) x_points.extend(x) y_points.extend(y) x_points = np.array(x_points) # Convert lists to numpy arrays @@ -413,8 +410,7 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): # Adjust the pcolormesh call to use the centers and compatible shading norm = LogNorm(vmin=1, vmax=maxcolor) mesh = axs[r, c].pcolormesh(X, Y, zi_clipped, shading='gouraud', cmap=custom_cmap, zorder=0,norm=norm) - fig.colorbar(mesh, ax=axs[r, c], label='GRU count') - + if r==1 and c==len(method_name2)-1: fig.colorbar(mesh, ax=axs.ravel().tolist(), label='GRU count',aspect=20/3*nrow) elif not do_heat: axs[r,c].scatter(x=s.values,y=s0.values,s=10,zorder=0,label=m) @@ -436,6 +432,7 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): if ly: axs[r,c].set_yscale('log') axs[r,c].set_xlabel(stat_word + wordx + ' [{}]'.format(leg_t)) axs[r,c].set_ylabel(stat0_word + wordy + ' [{}]'.format(leg_t0)) + if do_heat and c>0: axs[r, c].set_ylabel('') plt.rcParams['xtick.color'] = 'black' plt.rcParams['xtick.major.width'] = 2 @@ -444,18 +441,23 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): if do_vars: # Specify variables of interest if do_heat: - use_vars = [1,1,2,2,4,4] - logx = np.zeros(len(use_vars)) # no log scale x axis - logy = [0,0,0,0,1,1] # log scale y axis - rep = [0,1,0,1,0,1] + use_vars = [1,2,4] use_meth = [0,3] + logx = np.zeros(len(use_vars)) # no log scale x axis + logy = [0,0,1] # log scale y axis else: use_vars = [0,1,2,3,4] + use_meth = [0,1,2,3] logx = np.ones(len(use_vars)) # log scale x axis logy = np.ones(len(use_vars)) # log scale y axis - rep = np.zeros(len(use_vars)) - use_meth = [0,1,2,3] + rep = np.zeros(len(use_vars)) + if do_heat: + use_vars = [val for val in use_vars for _ in range(len(use_meth))] + logy = [val for val in logy for _ in range(len(use_meth))] + logx = [val for val in logx for _ in range(len(use_meth))] + rep = [int(val+_) for val in rep for _ in range(len(use_meth))] + plot_vars = settings plt_titl = ['snow water equivalent','total soil water content','total evapotranspiration', 'total water on the vegetation canopy','average routed runoff'] leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$mm~y^{-1}$'] @@ -487,8 +489,8 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): plt_name0 = np.copy(plt_name) if do_heat: - ncol = 2 # assumes to 2 methods - nrow = int(plot_vars/ncol) + ncol = len(use_meth) + nrow = len(plot_vars)//ncol else: ncol = 2 nrow = len(plot_vars)//ncol + 1 @@ -503,20 +505,19 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): fig_fil = fig_fil.format(','.join(plot_vars),stat) fig_fil = fig_fil + '_compressed.png' - # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug - if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 27}) - else: - plt.rcParams.update({'font.size': 100}) - if 'compressed' in fig_fil: - fig,axs = plt.subplots(nrow,ncol,figsize=(17*ncol,13*nrow)) + if do_heat: + plt.rcParams.update({'font.size': 33}) + else: + plt.rcParams.update({'font.size': 27}) + fig,axs = plt.subplots(nrow,ncol,figsize=(17*ncol,13*nrow),constrained_layout=do_heat) else: - fig,axs = plt.subplots(nrow,ncol,figsize=(70*ncol,54*nrow)) + if do_heat: + plt.rcParams.update({'font.size': 120}) + else: + plt.rcParams.update({'font.size': 100}) + if not do_heat: fig.subplots_adjust(hspace=0.33, wspace=0.17) # Adjust the bottom margin, vertical space, and horizontal space - #fig.suptitle('Hourly Errors and Values for each GRU', fontsize=40) - fig.subplots_adjust(hspace=0.33, wspace=0.17) # Adjust the bottom margin, vertical space, and horizontal space - for i,(var,lx,ly,plt_t,leg_t,leg_t0,leg_tm,rep,mx) in enumerate(zip(plot_vars,logx,logy,plt_titl,leg_titl,leg_titl0,leg_titlm,rep,maxes)): run_loop(i,var,lx,ly,plt_t,leg_t,leg_t0,leg_tm,rep,mx) @@ -530,21 +531,28 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): # Save plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) + if do_balance: -# Specify variables of interest + + # Specify variables of interest if do_heat: - use_vars = [0,0,1,1,2,2] - rep = [0,1,0,1,0,1] + use_vars = [0,1,2] + use_meth = [0,1,3] logx = np.ones(len(use_vars)) # log scale x axis logy = np.ones(len(use_vars)) # log scale y axis - use_meth = [0,3] else: use_vars = [0,1,2,3] + use_meth = [0,1,2,3,4] logx = np.zeros(len(use_vars)) # no log scale x axis logx = np.ones(len(use_vars)) # log scale x axis logy = np.ones(len(use_vars)) # log scale y axis - rep = np.zeros(len(use_vars)) - use_meth = [0,1,2,3,4] + + rep = np.zeros(len(use_vars)) + if do_heat: + use_vars = [val for val in use_vars for _ in range(len(use_meth))] + logy = [val for val in logy for _ in range(len(use_meth))] + logx = [val for val in logx for _ in range(len(use_meth))] + rep = [int(val+_) for val in rep for _ in range(len(use_meth))] plot_vars = ['balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceCasNrg','wallClockTime'] comp_vars = ['balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','numberFluxCalc'] @@ -564,8 +572,8 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): plt_name20 = np.copy(plt_name2) if do_heat: - ncol = 2 # assumes to 2 methods - nrow = int(plot_vars/ncol) + ncol = len(use_meth) + nrow = len(plot_vars)//ncol else: ncol = 2 nrow = len(plot_vars)//ncol + 1 @@ -573,23 +581,24 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): fig_fil = 'Hrly_balance_scat_{}' if do_heat: fig_fil = '{}'+fig_fil + '_heat' - fig_fil = fig_fil.format(','.join(method_name),','.join(plot_vars),stat) + fig_fil = fig_fil.format(','.join(method_name2),','.join(plot_vars),stat) else: fig_fil = fig_fil.format(','.join(plot_vars),stat) fig_fil = fig_fil + '_compressed.png' - if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 27}) - else: - plt.rcParams.update({'font.size': 100}) - if 'compressed' in fig_fil: - fig,axs = plt.subplots(nrow,ncol,figsize=(17*ncol,13*nrow)) + if do_heat: + plt.rcParams.update({'font.size': 33}) + else: + plt.rcParams.update({'font.size': 27}) + fig,axs = plt.subplots(nrow,ncol,figsize=(17*ncol,13*nrow),constrained_layout=do_heat) else: - fig,axs = plt.subplots(nrow,ncol,figsize=(70*ncol,54*nrow)) - - fig.subplots_adjust(hspace=0.33, wspace=0.17) # Adjust the bottom margin, vertical space, and horizontal space - #fig.suptitle('Scatterplot of Hourly Statistics for each GRU', fontsize=40,y=1.0) + if do_heat: + plt.rcParams.update({'font.size': 120}) + else: + plt.rcParams.update({'font.size': 100}) + fig,axs = plt.subplots(nrow,ncol,figsize=(70*ncol,54*nrow),constrained_layout=do_heat) + if not do_heat: fig.subplots_adjust(hspace=0.33, wspace=0.17) # Adjust the bottom margin, vertical space, and horizontal space for i,(var,comp,lx,ly,leg_t,leg_t0,plt_t,rep) in enumerate(zip(plot_vars,comp_vars,logx,logy,leg_titl,leg_titl0,plt_titl,rep)): run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,rep) From 2e05f0d06d75b7fa8b22ad852e03a9acd695045d Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 8 Jul 2024 23:27:08 +0900 Subject: [PATCH 1350/1472] utils --- utils/plot_per_GRUMult.py | 12 ++++++------ utils/plot_per_GRUMultBal.py | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/utils/plot_per_GRUMult.py b/utils/plot_per_GRUMult.py index 3efa3947c..146a2b889 100644 --- a/utils/plot_per_GRUMult.py +++ b/utils/plot_per_GRUMult.py @@ -257,7 +257,7 @@ def run_loop(j,var,the_max): my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap my_cmap.set_bad(color='white') #nan color white vmin,vmax = 0, the_max - if (stat =='mean' or stat=='mnnz') and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 400, the_max + if (stat =='mean' or stat=='mnnz') and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 600, the_max if stat =='amax' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 1000, the_max if (stat == 'mean' or stat == 'mnnz' or stat == 'amax') and var!='wallClockTime' and do_rel: vmin,vmax = 0.9, the_max @@ -308,7 +308,7 @@ def run_loop(j,var,the_max): sm = matplotlib.cm.ScalarMappable(cmap=my_cmap2, norm=norm2) else: sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) - sm._A = [] + sm.set_array([]) if one_plot: cbr = fig.colorbar(sm, ax=axs_list[r*len(method_name):(r+1)*len(method_name)],aspect=27/3) else: @@ -353,10 +353,10 @@ def format_tick(value, tick_number): # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug if 'compressed' in fig_fil: plt.rcParams.update({'font.size': 33}) - fig,axs = plt.subplots(nrow,ncol,figsize=(15*ncol,14*nrow),constrained_layout=True) + fig,axs = plt.subplots(nrow,ncol,figsize=(15*ncol,13*nrow),constrained_layout=True) else: plt.rcParams.update({'font.size': 120}) - fig,axs = plt.subplots(nrow,ncol,figsize=(62*ncol,58*nrow),constrained_layout=True) + fig,axs = plt.subplots(nrow,ncol,figsize=(67*ncol,58*nrow),constrained_layout=True) axs_list = axs.ravel().tolist() fig.suptitle('hourly statistics', fontsize=40,y=1.05) @@ -383,10 +383,10 @@ def format_tick(value, tick_number): # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug if 'compressed' in fig_fil: plt.rcParams.update({'font.size': 33}) - fig,axs = plt.subplots(nrow,ncol,figsize=(15*ncol,14*nrow),constrained_layout=True) + fig,axs = plt.subplots(nrow,ncol,figsize=(15*ncol,13*nrow),constrained_layout=True) else: plt.rcParams.update({'font.size': 120}) - fig,axs = plt.subplots(nrow,ncol,figsize=(62*ncol,58*nrow),constrained_layout=True) + fig,axs = plt.subplots(nrow,ncol,figsize=(67*ncol,58*nrow),constrained_layout=True) # Remove the extra subplots if len(method_name) < nrow*ncol: diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index c5ebdb888..d2a1d62e9 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -233,7 +233,7 @@ def run_loop(j,var,the_max): # Custom colorbar if i==len(method_name)-1: sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) - sm._A = [] + sm.set_array([]) if one_plot: cbr = fig.colorbar(sm, ax=axs_list[r*len(method_name):(r+1)*len(method_name)],aspect=27/3) else: @@ -265,10 +265,10 @@ def run_loop(j,var,the_max): # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug if 'compressed' in fig_fil: plt.rcParams.update({'font.size': 33}) - fig,axs = plt.subplots(nrow,ncol,figsize=(15*ncol,14*nrow),constrained_layout=True) + fig,axs = plt.subplots(nrow,ncol,figsize=(15*ncol,13*nrow),constrained_layout=True) else: plt.rcParams.update({'font.size': 120}) - fig,axs = plt.subplots(nrow,ncol,figsize=(62*ncol,58*nrow),constrained_layout=True) + fig,axs = plt.subplots(nrow,ncol,figsize=(67*ncol,58*nrow),constrained_layout=True) axs_list = axs.ravel().tolist() fig.suptitle('hourly statistics', fontsize=40,y=1.05) @@ -295,10 +295,10 @@ def run_loop(j,var,the_max): # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug if 'compressed' in fig_fil: plt.rcParams.update({'font.size': 33}) - fig,axs = plt.subplots(nrow,ncol,figsize=(15*ncol,14*nrow),constrained_layout=True) + fig,axs = plt.subplots(nrow,ncol,figsize=(15*ncol,13*nrow),constrained_layout=True) else: plt.rcParams.update({'font.size': 120}) - fig,axs = plt.subplots(nrow,ncol,figsize=(62*ncol,58*nrow),constrained_layout=True) + fig,axs = plt.subplots(nrow,ncol,figsize=(67*ncol,58*nrow),constrained_layout=True) # Remove the extra subplots if len(method_name) < nrow*ncol: From 088943ff0ceefb61d06caeac4b11dfdf33e3ebe5 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 19 Jul 2024 17:35:28 +0900 Subject: [PATCH 1351/1472] utils --- utils/hist_per_GRU.py | 39 ++-- utils/plot_per_GRUMult.py | 308 +++++++++++++++++++++--------- utils/plot_per_GRUMultBal.py | 24 ++- utils/scat_per_GRU.py | 19 +- utils/timeseries_to_statistics.py | 5 +- 5 files changed, 277 insertions(+), 118 deletions(-) diff --git a/utils/hist_per_GRU.py b/utils/hist_per_GRU.py index 86a5bd92d..34ac70583 100644 --- a/utils/hist_per_GRU.py +++ b/utils/hist_per_GRU.py @@ -23,6 +23,7 @@ do_rel = True # true is plot relative to the benchmark simulation do_hist = False # true is plot histogram instead of CDF run_local = True # true is run on local machine, false is run on cluster +fixed_Mass_units = False # true is convert mass balance units to kg m-2 s-1, if ran new code with depth in calculation if run_local: stat = 'rmnz' @@ -51,6 +52,8 @@ def power_transform(x): return x ** 0.5 # Adjust the exponent as needed # Simulation statistics file locations +use_vars = [] +rep = [] # mark the repeats use_vars = [1] rep = [0] # mark the repeats #use_vars = [0,1,2,3,4] @@ -58,8 +61,13 @@ def power_transform(x): settings0= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] settings = [settings0[i] for i in use_vars] -use_vars2 = [3,3,8] -rep2 = [1,2,0] # mark the repeats +#use_vars2 = [4,4,5,5,6,6,7,7] +#use_vars2 = [0,0,1,1,2,2,3,3] +#rep2 = [1,2,1,2,1,2,1,2] # mark the repeats +use_vars2 = [0,0,1,1,2,2] +rep2 = [1,2,1,2,1,2] # mark the repeats +use_vars2 = [8,3,3] +rep2 = [0,1,2] # mark the repeats #use_vars2 = [8] #rep2 = [0] # mark the repeats settings20= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime'] @@ -82,8 +90,9 @@ def power_transform(x): leg_titl = [leg_titl[i] for i in use_vars] plot_vars2 = settings2.copy() -plt_titl2 = ['canopy air space energy balance','vegetation energy balance','snow energy balance','soil energy balance','vegetation mass balance','snow mass balance','soil mass malance','aquifer mass balance', 'wall clock time'] +plt_titl2 = ['canopy air space energy balance','vegetation energy balance','snow energy balance','soil energy balance','vegetation mass balance','snow mass balance','soil mass balance','aquifer mass balance', 'wall clock time'] leg_titl2 = ['$W~m^{-3}$'] * 4 + ['$kg~m^{-2}~s^{-1}$'] * 4 + ['$s$'] +if fixed_Mass_units: leg_titl2 = ['$W~m^{-3}$'] * 4 + ['s^{-1}$'] * 3 + ['m~s^{-1}$'] + ['$s$'] plt_titl2 = [f"({chr(97+n + len(use_vars))}) {plt_titl2[i]}" for n,i in enumerate(use_vars2)] leg_titl2 = [leg_titl2[i] for i in use_vars2] @@ -97,21 +106,21 @@ def power_transform(x): if stat == 'rmse' or stat=='rmnz': stat2 = 'mean' - maxes = [2,15,250,0.08,200,20e-3] - if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,20e-3] + maxes = [2,15,250,0.08,200,10e-3] + if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] if stat == 'maxe': stat2 = 'amax' maxes = [15,25,0.8,2,0.3,2.0] if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,2.0] if stat == 'kgem': stat2 = 'mean' - maxes = [0.9,0.9,0.9,0.9,0.9,20e-3] + maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] maxes = [maxes[i] for i in use_vars] if stat2 == 'mean': - maxes2 = [1e-3,1e1,1e1,1e1]+[1e-12,1e-11,1e-10,1e-13] + [20e-3] + maxes2 = [1e-1,1e1,1e1,1e1]+[1e-7,1e-7,1e-7,1e-9] + [20e-3] if stat2 == 'amax': - maxes2 = [1e-2,1e4,1e4,1e3]+[1e-11,1e-6,1e-7,1e-8] + [2.0] + maxes2 = [1e1,1e3,1e3,1e3]+[1e-5,1e-5,1e-5,1e-7] + [2.0] maxes2 = [maxes2[i] for i in use_vars2] for i in range(len(maxes2)): if rep2[i]==2: maxes2[i] = maxes2[i]*1e2 #clunky way to increase the range for the second repeat @@ -139,9 +148,9 @@ def power_transform(x): plt.rcParams.update({'font.size': 100}) if 'compressed' in fig_fil: - fig,axs = plt.subplots(3,2,figsize=(35,38)) + fig,axs = plt.subplots(4,2,figsize=(35,52)) else: - fig,axs = plt.subplots(3,2,figsize=(140,160)) + fig,axs = plt.subplots(4,2,figsize=(140,160)) fig.subplots_adjust(hspace=0.33, wspace=0.17) # Adjust the bottom margin, vertical space, and horizontal space #fig.suptitle('Histograms of Hourly Statistics for each GRU', fontsize=40,y=1.0) @@ -240,10 +249,13 @@ def run_loopb(i,var,mx,rep): if 'zoom' in fig_fil: mx = mx - mn = mx*1e-4 + mn = mx*1e-9 if any(substring in var for substring in ['VegNrg', 'SnowNrg', 'SoilNrg']): mn = mx*1e-9 if var=='wallClockTime': mn = 0.0 + if fixed_Mass_units and 'Mass' in var: # /density for mass balance + mn = mn/1000 + mx = mx/1000 else: mx = 0.0 mn = 1.0 @@ -256,6 +268,7 @@ def run_loopb(i,var,mx,rep): # Data for m in method_name2: s = summa1[m][var].sel(stat=stat0).where(lambda x: x != 9999) + if fixed_Mass_units and 'Mass' in var: s = s/1000 # /density for mass balance range = (mn,mx) if do_hist: @@ -298,8 +311,8 @@ def run_loopb(i,var,mx,rep): run_loopb(i,var,mx,rep) # Remove the extra subplots -if (len(plot_vars)+len(plot_vars2)) < 6: - for i in range((len(plot_vars)+len(plot_vars2)),6): +if (len(plot_vars)+len(plot_vars2)) < 8: + for i in range((len(plot_vars)+len(plot_vars2)),8): r = i//2 c = i-r*2 fig.delaxes(axs[r, c]) diff --git a/utils/plot_per_GRUMult.py b/utils/plot_per_GRUMult.py index 146a2b889..61be8fdab 100644 --- a/utils/plot_per_GRUMult.py +++ b/utils/plot_per_GRUMult.py @@ -29,15 +29,18 @@ import geopandas as gpd import pandas as pd import matplotlib.ticker as ticker +import matplotlib.colors as mcolors +from matplotlib.ticker import ScalarFormatter do_rel = False # true is plot relative to the benchmark simulation one_plot = True # true is one plot, false is multiple plots (one per variable) run_local = False # true is run on local machine (only does testing), false is run on cluster +more_mean = True # true is plot mean/amax extra variables in a balance file if run_local: - stat = 'mnnz' - viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') + stat = 'mean' + viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics_en') else: import sys stat = sys.argv[1] @@ -49,10 +52,15 @@ method_name=['be1','be16','be32','sundials_1en6','ref'] plt_name0=['SUMMA-BE1','SUMMA-BE16','SUMMA-BE32','SUMMA-SUNDIALS','reference solution'] -#method_name=['be1','be1cm','be1en','sundials_1en6cm','diff'] -#plt_name0=['SUMMA-BE1 Common Form of Heat Eq.','SUMMA-BE1 Temperature Form of Heat Eq.','SUMMA-BE1 Mixed Form of Heat Eq.','SUMMA-SUNDIALS Temperature Form of Heat Eq.','SUMMA-BE1 Mixed Form - Temperature Form'] -from_meth = 'be1en' # name of the first simulation in the difference simulation, only used if a method_name is 'diff' -sub_meth = 'be1' # name of the simulation to subtract in the difference simulation, only used if a method_name is 'diff' +plt_nameshort=plt_name0 +method_name=['be1','be1cm','be1en','sundials_1en6cm','diff','ref'] +plt_name0=['BE1 common heat eq.','SUMMA-BE1 temperature heat eq.','SUMMA-BE1 mixed heat eq.','SUMMA-SUNDIALS temperature heat eq.','SUMMA-BE1 common - mixed','reference solution'] +plt_nameshort=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp','BE1 common - mixed','reference soln'] + +if one_plot: plt_name0 = plt_nameshort + +from_meth = 'be1' # name of the first simulation in the difference simulation, only used if a method_name is 'diff' +sub_meth = 'be1en' # name of the simulation to subtract in the difference simulation, only used if a method_name is 'diff' # Simulation statistics file locations settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] @@ -64,28 +72,40 @@ nbatch_hrus = 518 # number of HRUs per batch if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE +if more_mean: # extra vars in a balance file + plot_vars_exVar = ['scalarRainPlusMelt','scalarRootZoneTemp','airtemp','scalarSWE'] + viz_file_exVar = 'exVar_hrly_diff_bals_balance.nc' + plt_name0_exVar = 'SUMMA-BE1 temperature heat eq.' + plt_nameshort_exVar = 'BE1 temp' # identify method here + plt_titl_exVar = ['rain plus melt','root zone temperature','air temperature','snow water equivalent'] + leg_titl_exVar = ['$mm~y^{-1}$','$K$','$K$','$kg~m^{-2}$'] + maxes_exVar = [5000,280,280,100] + if one_plot: plt_name0_exVar = plt_nameshort_exVar + # Specify variables in files -plot_vars = settings.copy() -plt_titl = ['snow water equivalent','total soil water content','total evapotranspiration', 'total water on the vegetation canopy','average routed runoff','wall clock time'] -leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] +plot_vars = settings.copy() + ['scalarSWE'] +plt_titl = ['snow water equivalent','total soil water content','total evapotranspiration', 'total water on the vegetation canopy','average routed runoff','wall clock time', 'melt with seasonal snow'] +leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','$mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$','$kg~m^{-2}$'] +calc = [0,0,0,0,0,0,1] # 1 if variable needs to be calculated from other variables +melt_thresh = 1/(0.75) # threshold for melt water calculation (divisor is percentage of year no snow, if only melts once) fig_fil= '_hrly_diff_stats_{}_compressed.png' if do_rel: fig_fil = '_hrly_diff_stats_{}_rel_compressed.png' if stat == 'rmse' or stat == 'rmnz': - maxes = [2,15,250,0.08,200,10e-3] - if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] + maxes = [2,15,250,0.08,200,10e-3,2] + if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3,0.6] if stat == 'maxe': - maxes = [15,25,0.8,2,0.3,0.2] #[15,25,25e-5,2,1e-7,0.2] - if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,0.2] + maxes = [15,25,0.8,2,0.3,0.2,15] #[15,25,25e-5,2,1e-7,0.2] + if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,0.2,0.6] if stat == 'kgem': - maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] + maxes = [0.9,0.9,0.9,0.9,0.9,10e-3,0.9] if stat == 'mean' or stat == 'mnnz': - maxes = [80,1700,2000,8,5000,10e-3] #[80,1500,5e-5,8,1e-7,10e-3] - if do_rel: maxes = [1.1,1.1,1.1,1.1,1.1,10e-3] + maxes = [100,1700,2000,8,5000,10e-3,100] #[80,1500,5e-5,8,1e-7,10e-3] + if do_rel: maxes = [1.1,1.1,1.1,1.1,1.1,10e-3,1.1] if stat == 'amax': - maxes = [240,1800,3.5,25,7.5,0.2] #[240,1800,1e-3,25,2e-6,0.2] - if do_rel: maxes = [1.1,1.1,1.1,1.1,1.1,0.2] + maxes = [240,1800,3.5,25,7.5,0.2,240] #[240,1800,1e-3,25,2e-6,0.2] + if do_rel: maxes = [1.1,1.1,1.1,1.1,1.1,0.2,1.1] # Get simulation statistics summa = {} @@ -93,6 +113,8 @@ # Get the aggregated statistics of SUMMA simulations if m!='diff' and m!='ref': summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) +if more_mean: summa['exVar'] = xr.open_dataset(viz_dir/viz_file_exVar) + # Function to extract a given setting from the control file @@ -139,8 +161,8 @@ def make_default_path(suffix): s = summa[method_name[0]][plot_vars[0]].sel(stat=stat) mock_data = { - 'hm_hruid': s.hru.values[range(100)], # Example HRU IDs - 'geometry': [Point(x, y) for x, y in zip(range(100), range(100))] # Simple geometries + 'hm_hruid': np.concatenate(([81029662], s.hru.values[-100:])), #s.hru.values[-100:], # Example HRU IDs + 'geometry': [Point(x, y) for x, y in zip(range(101), range(101))] # Simple geometries } bas_albers = gpd.GeoDataFrame(mock_data, geometry='geometry') hm_hruid = 'hm_hruid' # Correctly define the variable name in the shapefile @@ -192,7 +214,7 @@ def make_default_path(suffix): # Match the accummulated values to the correct HRU IDs in the shapefile hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile -for plot_var in plot_vars: +for i,plot_var in enumerate(plot_vars): stat0 = stat if stat == 'rmse' or stat == 'kgem' or stat == 'mean': if plot_var == 'wallClockTime': stat0 = 'mean' @@ -205,13 +227,32 @@ def make_default_path(suffix): statr = 'amax_ben' if do_rel: s_rel = np.fabs(summa[method_name[0]][plot_var].sel(stat=statr)) + + if calc[i]: + if stat != 'mean' and stat != 'mnnz': + print('Only mean and mnnz are supported for calculated variables') + sys.exit() + if do_rel: s_rel = s_rel.where(summa[method_name[0]][plot_var].sel(stat='mnnz_ben') > melt_thresh*summa[method_name[0]][plot_var].sel(stat='mean_ben')) + + for m in method_name: if m=='diff': - s = np.fabs(summa[from_meth][plot_var].sel(stat=stat0)) - np.fabs(summa[sub_meth][plot_var].sel(stat=stat0)) + s = summa[from_meth][plot_var].sel(stat=stat0) - summa[sub_meth][plot_var].sel(stat=stat0) elif m=='ref': s = np.fabs(summa[method_name[0]][plot_var].sel(stat=statr)) else: s = np.fabs(summa[m][plot_var].sel(stat=stat0)) + if calc[i]: + if m=='diff': + s_from = summa[from_meth][plot_var].sel(stat=stat0) + s_from = s_from.where(summa[from_meth][plot_var].sel(stat='mnnz') > melt_thresh*summa[from_meth][plot_var].sel(stat='mean')) + s_sub = summa[sub_meth][plot_var].sel(stat=stat0) + s_sub = s_sub.where(summa[sub_meth][plot_var].sel(stat='mnnz') > melt_thresh*summa[sub_meth][plot_var].sel(stat='mean')) + s = s_from - s_sub + elif m=='ref': + s =s.where(summa[method_name[0]][plot_var].sel(stat='mnnz_ben') > melt_thresh*summa[method_name[0]][plot_var].sel(stat='mean_ben')) + else: + s = s.where(summa[m][plot_var].sel(stat='mnnz') > melt_thresh*summa[m][plot_var].sel(stat='mean')) if do_rel and plot_var != 'wallClockTime': s = s/s_rel # Replace inf and 9999 values with NaN in the s DataArray @@ -225,9 +266,39 @@ def make_default_path(suffix): if stat =='maxe' or stat=='amax': s = s*3600*1000 # make hourly max # Create a new column in the shapefile for each method, and fill it with the statistics - bas_albers[plot_var+m] = np.nan + if calc[i]: + plot_var1 = plot_var + '_calc' + else: + plot_var1 = plot_var + bas_albers[plot_var1+m] = np.nan + hru_ind = [i for i, hru_id in enumerate(hru_ids_shp.values) if hru_id in s.hru.values] # if some missing + bas_albers.loc[hru_ind, plot_var1+m] = s.sel(hru=hru_ids_shp.values[hru_ind]).values + +if more_mean: # extra mean/amax variables + for i,plot_var in enumerate(plot_vars_exVar): + stat0 = stat + + if stat != 'mean' and stat != 'amax': + print('Only mean and amax are supported for extra variables') + sys.exit() + + m = 'exVar' + s = np.fabs(summa[m][plot_var].sel(stat=stat0)) + + # Replace inf and 9999 values with NaN in the s DataArray + s = s.where(~np.isinf(s), np.nan).where(lambda x: x != 9999, np.nan) + + if plot_var == 'scalarRainPlusMelt': + if stat=='mean': s = s*31557600*1000 # make annual total + if stat=='amax': s = s*3600*1000 # make hourly max + + # Create a new column in the shapefile for each method, and fill it with the statistics + plot_var1 = plot_var + bas_albers[plot_var1+m] = np.nan hru_ind = [i for i, hru_id in enumerate(hru_ids_shp.values) if hru_id in s.hru.values] # if some missing - bas_albers.loc[hru_ind, plot_var+m] = s.sel(hru=hru_ids_shp.values[hru_ind]).values + bas_albers.loc[hru_ind, plot_var1+m] = s.sel(hru=hru_ids_shp.values[hru_ind]).values + + # Select lakes of a certain size for plotting if plot_lakes: @@ -238,7 +309,7 @@ def make_default_path(suffix): out_domain = (lak_albers['Pour_long'] > -80) & (lak_albers['Pour_lat'] > 65) # Exclude Baffin Island large_lakes_albers = lak_albers.loc[(lak_albers['Lake_area'] > minSize) & in_domain & (~out_domain) ] lake_col = (8/255,81/255,156/255) - + # Figure @@ -254,25 +325,7 @@ def run_loop(j,var,the_max): if var == 'wallClockTime': stat0 = 'amax' statr = 'amax_ben' - my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap - my_cmap.set_bad(color='white') #nan color white - vmin,vmax = 0, the_max - if (stat =='mean' or stat=='mnnz') and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 600, the_max - if stat =='amax' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 1000, the_max - if (stat == 'mean' or stat == 'mnnz' or stat == 'amax') and var!='wallClockTime' and do_rel: vmin,vmax = 0.9, the_max - - norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) - if stat =='kgem' and var!='wallClockTime': - my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno')) # copy the default cmap - my_cmap.set_bad(color='white') #nan color white - vmin,vmax = the_max, 1.0 - norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=1.5) - - my_cmap2 = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap - my_cmap2.set_bad(color='white') #nan color white - vmin,vmax = -the_max/10, the_max/4 - norm2 = matplotlib.colors.TwoSlopeNorm(vmin=vmin, vcenter=0, vmax=vmax) - + if stat0 == 'rmse': stat_word = 'RMSE' if stat0 == 'rmnz': stat_word = 'RMSE' # no 0s' if stat0 == 'maxe': stat_word = 'max abs error' @@ -284,49 +337,110 @@ def run_loop(j,var,the_max): if statr == 'mean_ben': statr_word = 'mean' if statr == 'mnnz_ben': statr_word = 'mean' # no 0s' if statr == 'amax_ben': statr_word = 'max' - - for i,m in enumerate(method_name): - r = i//ncol + base_row - c = i - (r-base_row)*ncol - - # Plot the data with the full extent of the bas_albers shape - if m=='diff': - bas_albers.plot(ax=axs[r,c], column=var+m, edgecolor='none', legend=False, cmap=my_cmap2, norm=norm2,zorder=0) - stat_word0 = stat_word+' difference' - else: - bas_albers.plot(ax=axs[r,c], column=var+m, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) - stat_word0 = stat_word - axs[r,c].set_title(plt_name[i]) - axs[r,c].axis('off') - axs[r,c].set_xlim(xmin, xmax) - axs[r,c].set_ylim(ymin, ymax) + my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap + my_cmap.set_bad(color='white') #nan color white - # Custom colorbar - if i==len(method_name)-1: + if var!='exVar': + vmin,vmax = 0, the_max + if (stat =='mean' or stat=='mnnz') and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 600, the_max + if stat =='amax' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 1000, the_max + if (stat == 'mean' or stat == 'mnnz' or stat == 'amax') and var!='wallClockTime' and do_rel: vmin,vmax = 0.9, the_max + norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) + + if stat =='kgem' and var!='wallClockTime': + my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno')) # copy the default cmap + my_cmap.set_bad(color='white') #nan color white + vmin,vmax = the_max, 1.0 + norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=1.5) + + my_cmap2 = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap + my_cmap2.set_bad(color='white') #nan color white + vmin,vmax = -the_max/250,the_max/250, + norm2 = matplotlib.colors.TwoSlopeNorm(vmin=vmin, vcenter=0, vmax=vmax) + #norm2 = matplotlib.colors.SymLogNorm(vmin=vmin,vmax=vmax,linthresh=0.01,base=1.1) + + for i,m in enumerate(method_name): + r = i//ncol + base_row + c = i - (r-base_row)*ncol + + # Plot the data with the full extent of the bas_albers shape if m=='diff': - sm = matplotlib.cm.ScalarMappable(cmap=my_cmap2, norm=norm2) + bas_albers.plot(ax=axs[r,c], column=var+m, edgecolor='none', legend=False, cmap=my_cmap2, norm=norm2,zorder=0) + stat_word0 = stat_word+' difference' + stat_word2 = stat_word else: - sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) + bas_albers.plot(ax=axs[r,c], column=var+m, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) + stat_word0 = stat_word + print(f"{'all HRU mean for '}{var+m:<35}{np.nanmean(bas_albers[var+m].values):<10.5f}{' max: '}{np.nanmax(bas_albers[var+m].values):<10.5f}") + axs[r,c].set_title(plt_name[i]) + axs[r,c].axis('off') + axs[r,c].set_xlim(xmin, xmax) + axs[r,c].set_ylim(ymin, ymax) + + # Custom colorbar + if i==len(method_name)-1: + if m=='diff': + sm = matplotlib.cm.ScalarMappable(cmap=my_cmap2, norm=norm2) + sm2 = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) + else: + sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) + sm.set_array([]) + if one_plot: + if m=='diff': + cbr = fig.colorbar(sm, ax=axs_list[r*len(method_name):(r+1)*len(method_name)],aspect=27/nrow,location='right') + cbr2 = fig.colorbar(sm2, ax=axs_list[(r+1)*len(method_name)-1:(r+1)*len(method_name)],aspect=27/nrow,location='left') + cbr2.ax.yaxis.set_ticks_position('right') + cbr2.ax.yaxis.set_label_position('right') + if m!='diff': cbr = fig.colorbar(sm, ax=axs_list[r*len(method_name):(r+1)*len(method_name)],aspect=27/nrow,location='right') + else: + # will be wonky with m=='diff' choice + cbr = fig.colorbar(sm, ax=axs_list,aspect=27/3*nrow) + if stat == 'kgem': + cbr.ax.set_ylabel(stat_word0) + else: + if do_rel and var!='wallClockTime': + cbr.ax.set_ylabel('relative '+ stat_word0) + if m=='diff': cbr2.ax.set_ylabel('relative '+ stat_word2) + else: + cbr.ax.set_ylabel(stat_word0 + ' [{}]'.format(leg_titl[j])) + if m=='diff': cbr2.ax.set_ylabel(stat_word2 + ' [{}]'.format(leg_titl[j])) + + if var=='scalarTotalET' and (stat =='mean' or stat =='mnnz') and m!='diff': + # Customizing the tick labels to include negative signs + def format_tick(value, tick_number): + rounded_value = int(round(value,-2)) + return f"-{rounded_value}" + cbr.ax.yaxis.set_major_formatter(ticker.FuncFormatter(format_tick)) + if m=='diff': + # Customizing the tick labels + cbr.ax.yaxis.set_major_formatter(ScalarFormatter()) + else: # extra mean/amax variables + for i,v in enumerate(plot_vars_exVar): + vmin,vmax = 0, maxes_exVar[i] + if (v=='airtemp' or v== 'scalarRootZoneTemp'): vmin,vmax = 260, maxes_exVar[i] + norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) + + r = i//ncol + base_row + c = i - (r-base_row)*ncol + m = 'exVar' + + # Plot the data with the full extent of the bas_albers shape + bas_albers.plot(ax=axs[r,c], column=v+m, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) + stat_word0 = stat_word + print(f"{'all HRU mean for '}{v+m:<35}{np.nanmean(bas_albers[v+m].values):<10.5f}{' max: '}{np.nanmax(bas_albers[v+m].values):<10.5f}") + axs[r,c].set_title(plt_name[i]) + axs[r,c].axis('off') + axs[r,c].set_xlim(xmin, xmax) + axs[r,c].set_ylim(ymin, ymax) + + sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) sm.set_array([]) if one_plot: - cbr = fig.colorbar(sm, ax=axs_list[r*len(method_name):(r+1)*len(method_name)],aspect=27/3) - else: - cbr = fig.colorbar(sm, ax=axs_list,aspect=27/3*nrow) - if stat == 'kgem': - cbr.ax.set_ylabel(stat_word0) + cbr = fig.colorbar(sm,ax=axs_list[r*ncol:(c+1)],aspect=27/nrow) else: - if do_rel and var!='wallClockTime': - cbr.ax.set_ylabel('relative '+ stat_word0) - else: - cbr.ax.set_ylabel(stat_word0 + ' [{}]'.format(leg_titl[j])) - - if var=='scalarTotalET' and (stat =='mean' or stat =='mnnz'): - # Customizing the tick labels to include negative signs - def format_tick(value, tick_number): - rounded_value = int(round(value,-2)) - return f"-{rounded_value}" - cbr.ax.yaxis.set_major_formatter(ticker.FuncFormatter(format_tick)) + cbr = fig.colorbar(sm,ax=axs_list[r*ncol:(i+1)],aspect=27/nrow) + cbr.ax.set_ylabel(stat_word0 + ' [{}]'.format(leg_titl_exVar[i])) # lakes if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) @@ -335,20 +449,34 @@ def format_tick(value, tick_number): # Specify plotting options if one_plot: - use_vars = [1,2,4] - use_meth = [0,3,4] + use_vars = [1] + use_meth = [0,2,4] + use_vars_exVar = [3,0,1] else: use_vars = [0,1,2,3,4,5] + use_vars = [1,5] use_meth = [0,1,2,3] -plot_vars = [plot_vars[i] for i in use_vars] -plt_titl = [plt_titl[i] for i in use_vars] -leg_titl = [leg_titl[i] for i in use_vars] -maxes = [maxes[i] for i in use_vars] + use_vars_exVar = [3,0,2,1] +if more_mean: + use_vars = ['exVar'] + use_vars # 'exVar' is the extra variables in a balance file, all same method + if len(use_meth) < len(use_vars_exVar): use_vars_exVar = use_vars_exVar[:len(use_meth)] # chop if longer + plot_vars_exVar = [plot_vars_exVar[i] for i in use_vars_exVar] + leg_titl_exVar = [leg_titl_exVar[i] for i in use_vars_exVar] + maxes_exVar = [maxes_exVar[i] for i in use_vars_exVar] + #plt_name_exVar = [f"({chr(97+n)}) {plt_titl_exVar[i]+ ' ' + plt_name0_exVar}" for n,i in enumerate(use_vars_exVar)] + plt_name_exVar = [f"({chr(97+n)}) {plt_titl_exVar[i]}" for n,i in enumerate(use_vars_exVar)] + +plot_vars = [var + '_calc' if c == 1 else var for var, c in zip(plot_vars, calc)] +plot_vars = [plot_vars[i] if i != 'exVar' else 'exVar' for i in use_vars] +plt_titl = [plt_titl[i] if i != 'exVar' else 'exVar' for i in use_vars] +leg_titl = [leg_titl[i] if i != 'exVar' else 'exVar' for i in use_vars] +maxes = [maxes[i] if i != 'exVar' else 'exVar' for i in use_vars] method_name = [method_name[i] for i in use_meth] if one_plot: ncol = len(use_meth) nrow = len(use_vars) + print(ncol,nrow) # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug if 'compressed' in fig_fil: @@ -371,15 +499,19 @@ def format_tick(value, tick_number): sys.exit() base_row = 0 - plt_name = [f"({chr(97+n)}) {plt_name0[i]}" for n,i in enumerate(use_meth)] + plt_name_orig = [f"({chr(97+n)}) {plt_name0[i]}" for n,i in enumerate(use_meth)] for i,(var,the_max) in enumerate(zip(plot_vars,maxes)): if one_plot: # Reset the names base_row = i - plt_name = [f"({chr(97+n+i*len(use_meth))}) {plt_titl[i] + ' ' + plt_name0[j]}" for n,j in enumerate(use_meth)] + if (len(use_vars)>1): plt_name = [f"({chr(97+n+i*len(use_meth))}) {plt_titl[i] + ' ' + plt_name0[j]}" for n,j in enumerate(use_meth)] + if (len(use_vars)==1): plt_name = [f"({chr(97+n+i*len(use_meth))}) {plt_name0[j]}" for n,j in enumerate(use_meth)] + if(var=='exVar'): plt_name = plt_name_exVar else: + plt_name = plt_name_orig + if(var=='exVar'): plt_name = plt_name_exVar # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug if 'compressed' in fig_fil: plt.rcParams.update({'font.size': 33}) diff --git a/utils/plot_per_GRUMultBal.py b/utils/plot_per_GRUMultBal.py index d2a1d62e9..144d67ee1 100644 --- a/utils/plot_per_GRUMultBal.py +++ b/utils/plot_per_GRUMultBal.py @@ -33,6 +33,8 @@ one_plot = False # true is one plot, false is multiple plots (one per variable) run_local = False # true is run on local machine (only does testing), false is run on cluster +fixed_Mass_units = False # true is convert mass balance units to kg m-2 s-1, if ran new code with depth in calculation + if run_local: stat = 'mean' @@ -44,7 +46,7 @@ method_name=['be1','be1cm','be1en','sundials_1en6cm','sundials_1en8cm'] #maybe make this an argument -plt_name0=['SUMMA-BE1 common form of heat eq.','SUMMA-BE1 temperature form of heat eq.','SUMMA-BE1 mixed form of heat eq.','SUMMA-SUNDIALS temperature form of heat eq.','reference solution'] +plt_name0=['SUMMA-BE1 common heat eq.','SUMMA-BE1 temperature heat eq.','SUMMA-BE1 mixed heat eq.','SUMMA-SUNDIALS temperature heat eq.','reference solution'] # Simulation statistics file locations settings= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime'] @@ -57,15 +59,16 @@ # Specify variables in files plt_titl = ['canopy air space energy balance','vegetation energy balance','snow energy balance','soil energy balance','vegetation mass balance','snow mass balance','soil mass balance','aquifer mass balance', 'wall clock time'] -leg_titl = ['$W~m^{-3}$'] * 4 +['$num$'] + ['$kg~m^{-2}~s^{-1}$'] * 4 +leg_titl = ['$W~m^{-3}$'] * 4 + ['$kg~m^{-2}~s^{-1}$'] * 4 + ['$s$'] +if fixed_Mass_units: leg_titl = ['$W~m^{-3}$'] * 4 + ['s^{-1}$'] * 3 + ['m~s^{-1}$'] + ['$s$'] fig_fil= '_hrly_balance_{}_compressed.png' plot_vars = settings.copy() if stat == 'mean': - maxes = [1e-3,1e1,1e1,1e1]+[1e-12,1e-11,1e-10,1e-13] + [20e-3] -if stat == 'amax': - maxes = [1e-2,1e4,1e4,1e3]+[1e-11,1e-6,1e-7,1e-8] + [2.0] + maxes = [1e-1,1e1,1e1,1e1]+[1e-7,1e-7,1e-7,1e-9] + [20e-3] +if stat == 'amax': + maxes = [1e1,1e3,1e3,1e3]+[1e-5,1e-5,1e-5,1e-7] + [2.0] # Get simulation statistics summa = {} @@ -177,6 +180,7 @@ def make_default_path(suffix): for m in method_name: s = summa[m][plot_var].sel(stat=stat0) + if fixed_Mass_units and 'Mass' in plot_var: s = s/1000 # /density for mass balance # Make absolute value norm, not all positive s = np.fabs(s) @@ -207,10 +211,13 @@ def run_loop(j,var,the_max): my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap my_cmap.set_bad(color='white') #nan color white - vmin,vmax = the_max*1e-4, the_max + vmin,vmax = the_max*1e-9, the_max if any(substring in var for substring in ['VegNrg', 'SnowNrg', 'SoilNrg']): vmin, vmax = the_max * 1e-9, the_max if var in ['wallClockTime',]: vmin,vmax = the_max*1e-1, the_max + if fixed_Mass_units and 'Mass' in var: # / density for mass balance + vmin = vmin/1000 + vmax = vmax/1000 norm = matplotlib.colors.LogNorm(vmin=vmin, vmax=vmax) @@ -223,7 +230,7 @@ def run_loop(j,var,the_max): # Plot the data with the full extent of the bas_albers shape bas_albers.plot(ax=axs[r,c], column=var+m, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) - + print(f"{'all HRU mean for '}{var+m:<35}{np.nanmean(bas_albers[var+m].values):<10.5f}{' max: '}{np.nanmax(bas_albers[var+m].values):<10.5f}") axs[r,c].set_title(plt_name[i]) axs[r,c].axis('off') axs[r,c].set_xlim(xmin, xmax) @@ -235,7 +242,7 @@ def run_loop(j,var,the_max): sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) sm.set_array([]) if one_plot: - cbr = fig.colorbar(sm, ax=axs_list[r*len(method_name):(r+1)*len(method_name)],aspect=27/3) + cbr = fig.colorbar(sm, ax=axs_list[r*len(method_name):(r+1)*len(method_name)],aspect=27/nrow) else: cbr = fig.colorbar(sm, ax=axs_list,aspect=27/3*nrow) cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titl[j])) @@ -251,6 +258,7 @@ def run_loop(j,var,the_max): use_meth = [0,2,4] else: use_vars = [0,1,2,3,4,5,6,7] + use_vars = [3] use_meth = [0,1,2,3] plot_vars = [plot_vars[i] for i in use_vars] plt_titl = [plt_titl[i] for i in use_vars] diff --git a/utils/scat_per_GRU.py b/utils/scat_per_GRU.py index b1e7865ed..5daf64ec5 100644 --- a/utils/scat_per_GRU.py +++ b/utils/scat_per_GRU.py @@ -26,14 +26,16 @@ do_heat = True # true is plot heatmaps instead of scatterplots run_local = True # true is run on local machine, false is run on cluster inferno_col= True # Set to True if want to match geographic plots, False if want rainbow colormap (does not matter if do_heat is False) +fixed_Mass_units = False # true is convert mass balance units to kg m-2 s-1, if ran new code with depth in calculation + # which statistics to plot, can do both do_vars = True do_balance = False if run_local: - stat = 'rmnz' - viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') + stat = 'mnnz' + viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics_en') else: import sys stat = sys.argv[1] @@ -44,8 +46,8 @@ #plt_name=['BE1','IDAe-4','BE4','BE8','BE16','BE32','IDAe-6'] method_name=['be1','be16','be32','sundials_1en6'] plt_name=['BE1','BE16','BE32','SUNDIALS'] -#method_name=['be1','be1cm','be1en','sundials_1en6cm'] -#plt_name=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp'] +method_name=['be1','be1cm','be1en','sundials_1en6cm'] +plt_name=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp'] method_name2=method_name+['sundials_1en8cm'] plt_name2=plt_name+['reference solution'] @@ -362,6 +364,7 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): # Get the statistics, remove 9999 (should be nan, but just in case) s = np.fabs(summa1[m][var].sel(stat=stat0)).where(lambda x: x != 9999, np.nan) s0 = np.fabs(summa1[m][comp].sel(stat=stat0)).where(lambda x: x != 9999, np.nan) + if fixed_Mass_units and 'Mass' in comp: s0 = s0/1000 # / density for mass balance if do_heat: x_points = [] @@ -441,10 +444,10 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): if do_vars: # Specify variables of interest if do_heat: - use_vars = [1,2,4] - use_meth = [0,3] + use_vars = [0,1,2,3,4] + use_meth = [0,2] logx = np.zeros(len(use_vars)) # no log scale x axis - logy = [0,0,1] # log scale y axis + logy = np.zeros(len(use_vars)) # log scale y axis else: use_vars = [0,1,2,3,4] use_meth = [0,1,2,3] @@ -559,6 +562,8 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): plt_titl = ['vegetation balance','snow balance','soil balance', 'canopy air space and aquifer balance', 'wall clock time'] leg_titl = ['$W~m^{-3}$'] * 4 + ['$s$'] leg_titl0 =['$kg~m^{-2}~s^{-1}$'] * 4 + ['$num$'] + if fixed_Mass_units: leg_titl0 = ['s^{-1}$'] * 3 + ['m~s^{-1}$'] + ['$num$'] + plot_vars = [plot_vars[i] for i in use_vars] comp_vars = [comp_vars[i] for i in use_vars] diff --git a/utils/timeseries_to_statistics.py b/utils/timeseries_to_statistics.py index b38e586fc..4c5b44a69 100644 --- a/utils/timeseries_to_statistics.py +++ b/utils/timeseries_to_statistics.py @@ -34,8 +34,8 @@ # which statistics to compute do_vars = False do_steps = False -do_balance = False -do_wall = True +do_balance = True +do_wall = False if run_local: not_parallel = True @@ -68,6 +68,7 @@ settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] stepsets= ['numberStateSplit','numberDomainSplitNrg','numberDomainSplitMass','numberScalarSolutions','meanStepSize'] balssets= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime', 'numberFluxCalc'] +#balssets= ['scalarRainPlusMelt','scalarRootZoneTemp','airtemp','scalarSWE'] wallsets= ['wallClockTime'] viz_fil = method_name + '_hrly_diff_stats_{}.nc' From 4285fc6d2b96607c7ab79f5a78658c214a0ef5de Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 19 Jul 2024 17:42:12 +0900 Subject: [PATCH 1352/1472] change mass balance to be kg/m3 if possible, so just need to divide by 1000 (density of water) if want in stateVariable units. Aquifer is in kg/m2 since there is no depth, so will be in units of 1/m if divide. --- build/source/dshare/get_ixname.f90 | 10 +++++----- build/source/dshare/popMetadat.f90 | 8 ++++---- build/source/dshare/var_lookup.f90 | 10 +++++----- build/source/engine/coupled_em.f90 | 12 ++++++------ 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 84d514675..1145d5893 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -601,11 +601,11 @@ function get_ixDiag(varName) case('balanceLayerNrg' ); get_ixDiag = iLookDIAG%balanceLayerNrg ! balance of energy in each snow+soil layer (W m-3) case('balanceSnowNrg' ); get_ixDiag = iLookDIAG%balanceSnowNrg ! balance of energy in the snow (W m-3) case('balanceSoilNrg' ); get_ixDiag = iLookDIAG%balanceSoilNrg ! balance of energy in the soil (W m-3) - case('balanceVegMass' ); get_ixDiag = iLookDIAG%balanceVegMass ! balance of water in the vegetation canopy (kg m-2 s-1) - case('balanceLayerMass' ); get_ixDiag = iLookDIAG%balanceLayerMass ! balance of water in each snow+soil layer (kg m-2 s-1) - case('balanceSnowMass' ); get_ixDiag = iLookDIAG%balanceSnowMass ! balance of water in the snow (kg m-2 s-1) - case('balanceSoilMass' ); get_ixDiag = iLookDIAG%balanceSoilMass ! balance of water in the soil (kg m-2 s-1) - case('balanceAqMass' ); get_ixDiag = iLookDIAG%balanceAqMass ! balance of water in the aquifer (kg m-2 s-1) + case('balanceVegMass' ); get_ixDiag = iLookDIAG%balanceVegMass ! balance of water in the vegetation canopy (kg m-3 s-1) + case('balanceLayerMass' ); get_ixDiag = iLookDIAG%balanceLayerMass ! balance of water in each snow+soil layer (kg m-3 s-1) + case('balanceSnowMass' ); get_ixDiag = iLookDIAG%balanceSnowMass ! balance of water in the snow (kg m-3 s-1) + case('balanceSoilMass' ); get_ixDiag = iLookDIAG%balanceSoilMass ! balance of water in the soil (kg m-3 s-1) + case('balanceAqMass' ); get_ixDiag = iLookDIAG%balanceAqMass ! balance of water in the aquifer (kg m-2 s-1) (no depth to aquifer) ! sundials integrator stats case('numSteps' ); get_ixDiag = iLookDIAG%numSteps case('numResEvals' ); get_ixDiag = iLookDIAG%numResEvals diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 4e17757ae..22f94e1d2 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -455,10 +455,10 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%balanceLayerNrg) = var_info('balanceLayerNrg' , 'balance of energy in each snow+soil layer on substep' , 'W m-3' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%balanceSnowNrg) = var_info('balanceSnowNrg' , 'balance of energy in the snow on data window' , 'W m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%balanceSoilNrg) = var_info('balanceSoilNrg' , 'balance of energy in the soil on data window' , 'W m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%balanceVegMass) = var_info('balanceVegMass' , 'balance of water in the vegetation on data window' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%balanceLayerMass) = var_info('balanceLayerMass' , 'balance of water in each snow+soil layer on substep' , 'kg m-2 s-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%balanceSnowMass) = var_info('balanceSnowMass' , 'balance of water in the snow on data window' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%balanceSoilMass) = var_info('balanceSoilMass' , 'balance of water in the soil on data window' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceVegMass) = var_info('balanceVegMass' , 'balance of water in the vegetation on data window' , 'kg m-3 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceLayerMass) = var_info('balanceLayerMass' , 'balance of water in each snow+soil layer on substep' , 'kg m-3 s-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceSnowMass) = var_info('balanceSnowMass' , 'balance of water in the snow on data window' , 'kg m-3 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + diag_meta(iLookDIAG%balanceSoilMass) = var_info('balanceSoilMass' , 'balance of water in the soil on data window' , 'kg m-3 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%balanceAqMass) = var_info('balanceAqMass' , 'balance of water in the aquifer on data window' , 'kg m-2 s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! sundials integrator stats diag_meta(iLookDIAG%numSteps) = var_info('numSteps' , 'number of steps taken by the integrator' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index ae97dfef5..fe6660336 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -482,11 +482,11 @@ MODULE var_lookup integer(i4b) :: balanceLayerNrg = integerMissing ! balance of energy in each snow+soil layer (W m-3) integer(i4b) :: balanceSnowNrg = integerMissing ! balance of energy in the snow (W m-3) integer(i4b) :: balanceSoilNrg = integerMissing ! balance of energy in the soil (W m-3) - integer(i4b) :: balanceVegMass = integerMissing ! balance of water in the vegetation (kg m-2 s-1) - integer(i4b) :: balanceLayerMass = integerMissing ! balance of water in each snow+soil layer (kg m-2 s-1) - integer(i4b) :: balanceSnowMass = integerMissing ! balance of water in the snow (kg m-2 s-1) - integer(i4b) :: balanceSoilMass = integerMissing ! balance of water in the soil (kg m-2 s-1) - integer(i4b) :: balanceAqMass = integerMissing ! balance of water in the aquifer (kg m-2 s-1) + integer(i4b) :: balanceVegMass = integerMissing ! balance of water in the vegetation (kg m-3 s-1) + integer(i4b) :: balanceLayerMass = integerMissing ! balance of water in each snow+soil layer (kg m-3 s-1) + integer(i4b) :: balanceSnowMass = integerMissing ! balance of water in the snow (kg m-3 s-1) + integer(i4b) :: balanceSoilMass = integerMissing ! balance of water in the soil (kg m-3 s-1) + integer(i4b) :: balanceAqMass = integerMissing ! balance of water in the aquifer (kg m-2 s-1) (no depth to aquifer) ! sundials integrator stats integer(i4b) :: numSteps = integerMissing ! integer(i4b) :: numResEvals = integerMissing ! diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 98fdd7eda..626e21f5f 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1251,10 +1251,10 @@ subroutine coupled_em(& ! sum the balance of energy and water per state innerBalance(1) = innerBalance(1) + diag_data%var(iLookDIAG%balanceCasNrg)%dat(1)*dt_wght ! W m-3 innerBalance(2) = innerBalance(2) + diag_data%var(iLookDIAG%balanceVegNrg)%dat(1)*dt_wght ! W m-3 - innerBalance(3) = innerBalance(3) + diag_data%var(iLookDIAG%balanceVegMass)%dat(1)*dt_wght ! kg m-2 s-1 - innerBalance(4) = innerBalance(4) + diag_data%var(iLookDIAG%balanceAqMass)%dat(1)*dt_wght * iden_water ! kg m-2 s-1 + innerBalance(3) = innerBalance(3) + diag_data%var(iLookDIAG%balanceVegMass)%dat(1)*dt_wght/diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ! kg m-3 s-1 + innerBalance(4) = innerBalance(4) + diag_data%var(iLookDIAG%balanceAqMass)%dat(1)*dt_wght * iden_water ! kg m-2 s-1 (no depth to aquifer) innerBalanceLayerNrg(:) = innerBalanceLayerNrg(:) + diag_data%var(iLookDIAG%balanceLayerNrg)%dat(:)*dt_wght ! W m-3 - innerBalanceLayerMass(:) = innerBalanceLayerMass(:) + diag_data%var(iLookDIAG%balanceLayerMass)%dat(:)*dt_wght * prog_data%var(iLookPROG%mLayerDepth)%dat(:) * iden_water ! kg m-2 s-1 + innerBalanceLayerMass(:) = innerBalanceLayerMass(:) + diag_data%var(iLookDIAG%balanceLayerMass)%dat(:)*dt_wght * iden_water ! kg m-3 s-1 ! save balance of energy and water per snow+soil layer after inner step, since can change nLayers with outer steps diag_data%var(iLookDIAG%balanceLayerNrg)%dat(:) = innerBalanceLayerNrg(:) @@ -1465,12 +1465,12 @@ subroutine coupled_em(& ! save balance of energy and water per single layer domain diag_data%var(iLookDIAG%balanceCasNrg)%dat(1) = meanBalance(1) ! W m-3 diag_data%var(iLookDIAG%balanceVegNrg)%dat(1) = meanBalance(2) ! W m-3 will be realMissing if computeVegFlux is false - diag_data%var(iLookDIAG%balanceVegMass)%dat(1) = meanBalance(3) ! kg m-2 s-1 will be realMissing if computeVegFlux is false + diag_data%var(iLookDIAG%balanceVegMass)%dat(1) = meanBalance(3) ! kg m-3 s-1 will be realMissing if computeVegFlux is false diag_data%var(iLookDIAG%balanceAqMass)%dat(1) = meanBalance(4) ! kg m-2 s-1 will be realMissing if no aquifer diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = meanBalance(5) ! W m-3 will be realMissing if no snow during data step diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = meanBalance(6) ! W m-3 - diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = meanBalance(7) ! kg m-2 s-1 will be realMissing if no snow during data step - diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = meanBalance(8) ! kg m-2 s-1 + diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = meanBalance(7) ! kg m-3 s-1 will be realMissing if no snow during data step + diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = meanBalance(8) ! kg m-3 s-1 if (.not.bal_snow)then ! will be 0, make realMissing diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = realMissing diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = realMissing From 7bdd10742c8d4d5a0899cb8440c51c5f109775c6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 24 Jul 2024 14:12:17 +0900 Subject: [PATCH 1353/1472] fix error messages --- build/source/dshare/popMetadat.f90 | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 22f94e1d2..ae150953e 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -437,7 +437,6 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%mLayerCompress) = var_info('mLayerCompress' , 'change in volumetric water content due to compression of soil' , 's-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarSoilCompress) = var_info('scalarSoilCompress' , 'change in total soil storage due to compression of soil matrix' , 'kg m-2 s-1 ' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%mLayerMatricHeadLiq) = var_info('mLayerMatricHeadLiq' , 'matric potential of liquid water' , 'm' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) - ! mass balance check diag_meta(iLookDIAG%scalarTotalSoilLiq) = var_info('scalarTotalSoilLiq' , 'total mass of liquid water in the soil' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarTotalSoilIce) = var_info('scalarTotalSoilIce' , 'total mass of ice in the soil' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarTotalSoilWat) = var_info('scalarTotalSoilWat' , 'total mass of water in the soil' , 'kg m-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) @@ -928,7 +927,7 @@ subroutine read_output_file(err,message) ! identify the data structure for the given variable (structName) and the variable index (vDex) call get_ixUnknown(trim(varName),structName,vDex,err,cmessage) - if (err/=0) then; message=trim(message)//trim(cmessage)//trim(varName); return; end if; + if (err/=0) then; message=trim(message)//trim(cmessage)// ': deprecated variable name, remove from output file'; return; end if; ! id variables should not be specified in output control file if (trim(structName)=='id')then @@ -969,9 +968,8 @@ subroutine read_output_file(err,message) ! temporally constant variables use timestep-level output (no aggregation) case default - message=trim(message)//'unable to identify desired output frequency for variable '//trim(varName)& - //' [entered "'//trim(freqName)//'"];'& - //' outputting variable in timestep file' + freqName = trim(lineWords(freqIndex)) + write(*,*)'WARNING: temporally constant variable '//trim(varName)//': outputting variable in timestep file and will be the same value throughout file' iFreq = iLookFREQ%timestep freqName = 'timestep' end select From 6ba6c50bb0cf8e28a9155b83234a463f1906ab71 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 26 Jul 2024 07:17:34 +0900 Subject: [PATCH 1354/1472] conductance error bug --- build/source/engine/vegNrgFlux.f90 | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 6f21e19e4..bfcb5ceba 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -2127,6 +2127,8 @@ subroutine turbFluxes(& groundConductanceSH = 1._rkind/groundResistance ! compute total conductance for sensible heat + if(groundConductanceSH < 0._rkind) groundConductanceSH = 0._rkind ! to avoid negative conductance, will make large residual error instead of old version where failed outright + if(canopyConductance < 0._rkind) canopyConductance = 0._rkind ! to avoid negative conductance, will make large residual error instead of old version where failed outright totalConductanceSH = leafConductance + groundConductanceSH + canopyConductance ! compute conductances for latent heat (m s-1) @@ -2138,25 +2140,9 @@ subroutine turbFluxes(& transConductance = 0._rkind end if groundConductanceLH = 1._rkind/(groundResistance + soilResistance) ! NOTE: soilResistance accounts for fractional snow, and =0 when snow cover is 100% + if(groundConductanceLH < 0._rkind) groundConductanceLH = 0._rkind ! to avoid negative conductance, will make large residual error instead of old version where failed outright totalConductanceLH = evapConductance + transConductance + groundConductanceLH + canopyConductance - - ! check sensible heat conductance - if(totalConductanceSH < tinyVal .or. groundConductanceSH < -tinyVal .or. canopyConductance < -tinyVal)then - if(groundConductanceSH < -tinyVal) groundConductanceSH = 0._rkind - if(canopyConductance < -tinyVal) canopyConductance = 0._rkind - totalConductanceSH = leafConductance + groundConductanceSH + canopyConductance - if(totalConductanceSH < tinyVal) totalConductanceSH = 0._rkind - message=trim(message)//'negative conductance for sensible heat' - !err=20; return - endif - ! check latent heat conductance - if(totalConductanceLH < tinyVal .or. groundConductanceLH < -tinyVal)then - if(groundConductanceLH < -tinyVal) groundConductanceLH = 0._rkind - totalConductanceLH = evapConductance + transConductance + groundConductanceLH + canopyConductance - if(totalConductanceLH < tinyVal) totalConductanceLH = 0._rkind - message=trim(message)//'negative conductance for latent heat' - !err=20; return - endif + if(totalConductanceLH < 0._rkind) totalConductanceLH = tinyVal ! to avoid division by zero, will make large residual error instead of old version where failed outright ! compute derivatives in individual conductances for sensible heat w.r.t. canopy temperature (m s-1 K-1) ! NOTE: it may be more efficient to compute these derivatives when computing resistances From f55056ad692fe9f81b309ddd37010e184c31be9e Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 29 Jul 2024 10:45:13 -0500 Subject: [PATCH 1355/1472] remove some utils --- utils/plot_per_GRU.py | 336 ---------------------------------- utils/process_concat.sh | 18 -- utils/process_diff.sh | 17 -- utils/process_hist.sh | 20 -- utils/process_plot.sh | 20 -- utils/process_scat.sh | 20 -- utils/process_stat.sh | 25 --- utils/process_statBatch.sh | 29 --- utils/stepsMap_per_GRU.py | 246 ------------------------- utils/steps_per_GRU.py | 120 ------------ utils/wallClockMap_per_GRU.py | 266 --------------------------- utils/wallClock_per_GRU.py | 129 ------------- utils/wallStd_per_GRU.py | 122 ------------ utils/write_seff.bash | 54 ------ 14 files changed, 1422 deletions(-) delete mode 100644 utils/plot_per_GRU.py delete mode 100755 utils/process_concat.sh delete mode 100755 utils/process_diff.sh delete mode 100755 utils/process_hist.sh delete mode 100755 utils/process_plot.sh delete mode 100755 utils/process_scat.sh delete mode 100755 utils/process_stat.sh delete mode 100755 utils/process_statBatch.sh delete mode 100644 utils/stepsMap_per_GRU.py delete mode 100644 utils/steps_per_GRU.py delete mode 100644 utils/wallClockMap_per_GRU.py delete mode 100644 utils/wallClock_per_GRU.py delete mode 100644 utils/wallStd_per_GRU.py delete mode 100755 utils/write_seff.bash diff --git a/utils/plot_per_GRU.py b/utils/plot_per_GRU.py deleted file mode 100644 index fe7c370bd..000000000 --- a/utils/plot_per_GRU.py +++ /dev/null @@ -1,336 +0,0 @@ -# written originally by W. Knoben, modified by A. Van Beusekom (2023) - -## Visualize statistics per GRU -## Needs: -# Catchment shapefile with GRU delineation -# SUMMA output statistics - -## Special note -# SUMMA simulations have been preprocessed into single value statistics per model element, using auxiliary scripts in ~/utils -# To improve visualization of large lakes, HydroLAKES lake delineations are plotted on top of the catchment GRUs and river segments. -# Dealing with HydroLAKES inputs is not considered within scope of the workflow and therefore requires some manual downloading and preprocessing of this data for those who wish to reproduce this step. -# The relevant code is easily disabled by switching the plot_lakes = True flag to False. - -# Run: -# python plot_per_GRU.py sundials_1en6 [stat] -# where stat is rmse or maxe or kgem - - -# modules -import sys -import os -import matplotlib -import numpy as np -import xarray as xr -from pathlib import Path -import matplotlib.pyplot as plt -import copy -import pyproj -import fiona -import geopandas as gpd -import pandas as pd - -# The first input argument specifies the run where the files are -method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) -stat = sys.argv[2] - -# Simulation statistics file locations -settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] -viz_dir = Path('/home/avanb/scratch/statistics') -viz_fil = method_name + '_hrly_diff_stats_{}.nc' -viz_fil = viz_fil.format(','.join(settings)) -eff_fil = 'eff_' + method_name + '.txt' -nbatch_hrus = 518 # number of HRUs per batch -use_eff = False # use efficiency in wall clock time -do_rel = True # plot relative to the benchmark simulation -if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE - -# Specify variables of interest -plot_vars = settings.copy() -plt_titl = ['(a) Snow Water Equivalent','(b) Total soil water content','(c) Total evapotranspiration', '(d) Total water on the vegetation canopy','(e) Average routed runoff','(f) Wall clock time'] -leg_titl = ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~y^{-1}$','$kg~m^{-2}$','$mm~y^{-1}$','$s$'] -leg_titlm= ['$kg~m^{-2}$', '$kg~m^{-2}$','mm~h^{-1}$','$kg~m^{-2}$','$mm~h^{-1}$','$s$'] - -if stat == 'rmse': - maxes = [2,15,250,0.08,200,10e-3] #[2,15,8e-6,0.08,6e-9,10e-3] - #maxes = [0.25,2,30,0.01,30,2e-3] #[0.25,2,1e-6,0.01,1e-9,2e-3] - if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] -if stat == 'rmnz': - maxes = [2,15,250,0.08,200,10e-3] - if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,10e-3] -if stat == 'maxe': - maxes = [15,25,0.8,2,0.3,0.2] #[15,25,25e-5,2,1e-7,0.2] - if do_rel: maxes = [0.6,0.02,0.6,0.3,0.6,0.2] -if stat == 'kgem': - maxes = [0.9,0.9,0.9,0.9,0.9,10e-3] -if stat == 'mean': - maxes = [80,1500,1500,3000,10e-3] #[80,1500,5e-5,8,1e-7,10e-3] - if do_rel: maxes = [1.1,1.1,1.1,1.1,1.1,10e-3] -if stat == 'amax': - maxes = [240,1800,3.5,25,7.5,0.2] #[240,1800,1e-3,25,2e-6,0.2] - if do_rel: maxes = [1.1,1.1,1.1,1.1,1.1,0.2] - -fig_fil = method_name + '_hrly_diff_stats_{}_{}_compressed.png' -if do_rel: fig_fil = method_name + '_hrly_diff_stats_{}_{}_rel_compressed.png' -fig_fil = fig_fil.format(','.join(settings),stat) - -# Get the albers shapes -main = Path('/home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/albers_projection') - -# Plot lakes? -plot_lakes = True -# lakes shapefile WHERE IS THIS -#lake_path = Path('C:/Globus endpoint/HydroLAKES/HydroLAKES_polys_v10_shp') -#lake_name = 'HydroLAKES_polys_v10_subset_NA.shp' - -## Control file handling -# Store the name of the 'active' file in a variable -controlFile = 'plot_control_NorthAmerica.txt' - -# Function to extract a given setting from the control file -def read_from_control( file, setting ): - - # Open controlFile and ... - with open(file) as contents: - for line in contents: - - # ... find the line with the requested setting - if setting in line and not line.startswith('#'): - break - - # Extract the setting's value - substring = line.split('|',1)[1] # Remove the setting's name (split into 2 based on '|', keep only 2nd part) - substring = substring.split('#',1)[0] # Remove comments, does nothing if no '#' is found - substring = substring.strip() # Remove leading and trailing whitespace, tabs, newlines - - # Return this value - return substring - -# Function to specify a default path -def make_default_path(suffix): - - # Get the root path - rootPath = Path( read_from_control(controlFile,'root_path') ) - - # Get the domain folder - domainName = read_from_control(controlFile,'domain_name') - domainFolder = 'domain_' + domainName - - # Specify the forcing path - defaultPath = rootPath / domainFolder / suffix - - return defaultPath - -## Catchment shapefile location and variable names -# HM catchment shapefile path & name -hm_catchment_path = read_from_control(controlFile,'catchment_shp_path') -hm_catchment_name = read_from_control(controlFile,'catchment_shp_name') -# Specify default path if needed -if hm_catchment_path == 'default': - hm_catchment_path = make_default_path('shapefiles/catchment') # outputs a Path() -else: - hm_catchment_path = Path(hm_catchment_path) # make sure a user-specified path is a Path() - -# Find the GRU and HRU identifiers -hm_hruid = read_from_control(controlFile,'catchment_shp_hruid') - -## River network shapefile location and variable names -# Plot rivers? -plot_rivers = False -# River network path & name -river_network_path = read_from_control(controlFile,'river_network_shp_path') -river_network_name = read_from_control(controlFile,'river_network_shp_name') -# Specify default path if needed -if river_network_path == 'default': - river_network_path = make_default_path('shapefiles/river_network') # outputs a Path() -else: - river_network_path = Path(river_network_path) # make sure a user-specified path is a Path() - -# Find the segment ID -seg_id = read_from_control(controlFile,'river_network_shp_segid') - -## Load all shapefiles and project to Albers Conformal Conic and reproject -# Set the target CRS -acc = 'ESRI:102008' - -# catchment shapefile, first 2 lines throw error so cutting them -#bas = gpd.read_file(hm_catchment_path/hm_catchment_name) -#bas_albers = bas.to_crs(acc) -bas_albers = gpd.read_file(main/'basin.shp') -xmin, ymin, xmax, ymax = bas_albers.total_bounds - -# river network shapefile, first 2 lines throw error so cutting them -if plot_rivers: - #riv = gpd.read_file(river_network_path/river_network_name) - #riv_albers = riv.to_crs(acc) - riv_albers = gpd.read_file(main/'river.shp') - -# lakes shapefile, first 2 lines throw error so cutting them -if plot_lakes: - #lakes = gpd.read_file(lake_path/lake_name) - #lak_albers = lakes.to_crs(acc) - lak_albers = gpd.read_file(main/'lakes.shp') - -## Pre-processing, map SUMMA sims to catchment shapes -# Get the aggregated statistics of SUMMA simulations -summa = xr.open_dataset(viz_dir/viz_fil) -if use_eff: - # Read the data from the eff.txt file into a DataFrame - eff = pd.read_csv(viz_dir/eff_fil, sep=',', header=None, names=['CPU Efficiency', 'Array ID', 'Job Wall-clock time', 'Node Number']) - # Extract only the values after the ':' character in the 'CPU Efficiency', 'Job Wall-clock time', and 'Node Number' columns - eff['CPU Efficiency'] = eff['CPU Efficiency'].str.split(':').str[1].astype(float) - eff['Array ID'] = eff['Array ID'].str.split(':').str[1].astype(int) - eff['Job Wall-clock time'] = eff['Job Wall-clock time'].str.split(':').str[1].astype(float) - eff['Node Number'] = eff['Node Number'].str.split(':').str[1].astype(int) - -# Match the accummulated values to the correct HRU IDs in the shapefile -hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile -for plot_var in plot_vars: - stat0 = stat - if stat == 'rmse' or stat == 'kgem' or stat == 'mean': - if plot_var == 'wallClockTime': stat0 = 'mean' - statr = 'mean_ben' - if stat == 'rmnz' or stat == 'mnnz': - if plot_var == 'wallClockTime': stat0 = 'mnnz' - statr = 'mnnz_ben' - if stat == 'maxe' or stat == 'amax': - if plot_var == 'wallClockTime': stat0 = 'amax' - statr = 'amax_ben' - - if do_rel: s_rel = summa[plot_var].sel(stat=statr) - s = summa[plot_var].sel(stat=stat0) - if do_rel and plot_var != 'wallClockTime': s = s/s_rel - - if plot_var == 'wallClockTime' and use_eff: - batch = np.floor(np.arange(len(s.indexes['hru'])) /nbatch_hrus) - #basin_num = np.arange(len(s.indexes['hru'])) % nbatch_hrus #not currently using - # Create a dictionary to store the values for each batch - efficiency = {} - # Iterate over the rows in the data DataFrame - for index, row in eff.iterrows(): - # Extract the values from the row - batch0 = int(row['Array ID']) - eff0 = row['CPU Efficiency'] - # Store the value for the current batch in the dictionary - efficiency[batch0] = eff0 - # Select the values for the current batch using boolean indexing - eff_batch = np.array([efficiency[b] for b in batch]) - #node_batch = np.array([node[b] for b in batch]) #not currently using - # Multiply the s values by efficiency - s = s*eff_batch - - # Make absolute value norm, not all positive - s = np.fabs(s) - - # Replace inf values with NaN in the s DataArray - s = s.where(~np.isinf(s), np.nan) - - if plot_var == 'scalarTotalET' and not do_rel: - if stat =='rmse' or stat =='rmnz' : s = s*31557600 # make annual total - if stat =='maxe': s = s*3600 # make hourly max - if plot_var == 'averageRoutedRunoff' and not do_rel: - if stat =='rmse' or stat =='rmnz' : s = s*31557600*1000 # make annual total - if stat =='maxe': s = s*3600*1000 # make hourly max - bas_albers[plot_var] = s.sel(hru=hru_ids_shp.values) - -# Select lakes of a certain size for plotting -if plot_lakes: - minSize = 1000 # km2 - in_domain = (lak_albers['Country'] == 'Canada') | \ - (lak_albers['Country'] == 'United States of America') | \ - (lak_albers['Country'] == 'Mexico') - out_domain = (lak_albers['Pour_long'] > -80) & (lak_albers['Pour_lat'] > 65) # Exclude Baffin Island - large_lakes_albers = lak_albers.loc[(lak_albers['Lake_area'] > minSize) & in_domain & (~out_domain) ] - lake_col = (8/255,81/255,156/255) - -##Figure - -# Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug -if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 25}) -else: - plt.rcParams.update({'font.size': 100}) - -if 'compressed' in fig_fil: - fig,axs = plt.subplots(3,2,figsize=(35,33)) -else: - fig,axs = plt.subplots(3,2,figsize=(140,133)) -fig.suptitle('{} Hourly Statistics'.format(method_name), fontsize=40,y=1.05) - -plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines - -# colorbar axes -f_x_mat = [0.443,0.94,0.443,0.94,0.443,0.94] -f_y_mat = [0.71,0.71,0.38,0.38,0.047,0.047] - -plt.tight_layout() - -def run_loop(i,var,the_max,f_x,f_y): - stat0 = stat - if stat == 'rmse' or stat == 'kgem' or stat == 'mean': - if plot_var == 'wallClockTime': stat0 = 'mean' - statr = 'mean_ben' - if stat == 'rmnz' or stat == 'mnnz': - if plot_var == 'wallClockTime': stat0 = 'mnnz' - statr = 'mnnz_ben' - if stat == 'maxe' or stat == 'amax': - if plot_var == 'wallClockTime': stat0 = 'amax' - statr = 'amax_ben' - - my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap - my_cmap.set_bad(color='white') #nan color white - vmin,vmax = 0, the_max - if stat =='mean' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 700, the_max - if stat =='amax' and var=='scalarTotalSoilWat' and not do_rel: vmin,vmax = 1000, the_max - if (stat == 'mean' or stat == 'mnnz' or stat == 'amax') and var!='wallClockTime' and do_rel: vmin,vmax = 0.9, the_max - - norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) - if stat =='kgem' and var!='wallClockTime': - my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno')) # copy the default cmap - my_cmap.set_bad(color='white') #nan color white - vmin,vmax = the_max, 1.0 - norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=1.5) - r = i//2 - c = i-r*2 - - # Plot the data with the full extent of the bas_albers shape - bas_albers.plot(ax=axs[r,c], column=var, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) - - if stat0 == 'rmse': stat_word = 'RMSE' - if stat0 == 'rmnz': stat_word = 'RMSE' # no 0s' - if stat0 == 'maxe': stat_word = 'max abs error' - if stat0 == 'kgem': stat_word = 'KGE"' - if stat0 == 'mean': stat_word = 'mean' - if stat0 == 'mnnz': stat_word = 'mean' # no 0s' - if stat0 == 'amax': stat_word = 'max' - - if statr == 'mean_ben': statr_word = 'mean' - if statr == 'mnnz_ben': statr_word = 'mean' # no 0s' - if statr == 'amax_ben': statr_word = 'max' - - axs[r,c].set_title(plt_titl[i]) - axs[r,c].axis('off') - axs[r,c].set_xlim(xmin, xmax) - axs[r,c].set_ylim(ymin, ymax) - - # Custom colorbar - cax = fig.add_axes([f_x,f_y,0.02,0.25]) - sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) - sm._A = [] - cbr = fig.colorbar(sm, cax=cax) #, extend='max') #if max extend can't get title right - if stat == 'rmse' or stat == 'rmnz' or stat == 'mean': cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titl[i]), labelpad=40, rotation=270) - if stat == 'maxe' or stat == 'amax': cbr.ax.set_ylabel(stat_word + ' [{}]'.format(leg_titlm[i]), labelpad=40, rotation=270) - if stat == 'kgem': cbr.ax.set_ylabel(stat_word, labelpad=40, rotation=270) - #if do_rel and var!='wallClockTime': cbr.ax.set_ylabel(stat_word + ' rel to bench ' + statr_word, labelpad=40, rotation=270) - if do_rel and var!='wallClockTime': cbr.ax.set_ylabel('relative '+ stat_word, labelpad=40, rotation=270) - - #cbr.ax.yaxis.set_offset_position('right') - - # lakes - if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) - -for i,(var,the_max,f_x,f_y) in enumerate(zip(plot_vars,maxes,f_x_mat,f_y_mat)): - run_loop(i,var,the_max,f_x,f_y) - -# Save -plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=True) diff --git a/utils/process_concat.sh b/utils/process_concat.sh deleted file mode 100755 index f7699e68b..000000000 --- a/utils/process_concat.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -#SBATCH --ntasks-per-node=1 -#SBATCH --cpus-per-task=32 -#SBATCH --mem-per-cpu=4GB -#SBATCH --time=1-00:00 -#SBATCH --job-name=CONCAT -#SBATCH --mail-user=gwu479@usask.ca -#SBATCH --mail-type=ALL -#SBATCH --account=rpp-kshook -#SBATCH --output=/home/avanb/TestScripts/output/slurm-%A_%a.out - -module load StdEnv/2020 -module load gcc/9.3.0 -module load geo-stack/2022c -virtualenv --no-download $SLURM_TMPDIR/env -source $SLURM_TMPDIR/env/bin/activate - -python concat_groups_split_summa.py sundials_1en8 diff --git a/utils/process_diff.sh b/utils/process_diff.sh deleted file mode 100755 index ca910f272..000000000 --- a/utils/process_diff.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -#SBATCH --ntasks-per-node=1 -#SBATCH --cpus-per-task=32 -#SBATCH --time=1-00:00 -#SBATCH --job-name=DIFF -#SBATCH --mail-user=gwu479@usask.ca -#SBATCH --mail-type=ALL -#SBATCH --account=rpp-kshook -#SBATCH --output=/home/avanb/TestScripts/output/slurm-%A_%a.out - -module load StdEnv/2020 -module load gcc/9.3.0 -module load geo-stack/2022c -virtualenv --no-download $SLURM_TMPDIR/env -source $SLURM_TMPDIR/env/bin/activate - -python check_bit_4_bit_withTol.py /home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/summa-sundials_be_noJac/ /home/avanb/projects/rpp-kshook/avanb/summaWorkflow_data/domain_NorthAmerica/summa-be/ 0.1 > out.txt diff --git a/utils/process_hist.sh b/utils/process_hist.sh deleted file mode 100755 index 522ea48b6..000000000 --- a/utils/process_hist.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -#SBATCH --ntasks-per-node=1 -#SBATCH --cpus-per-task=1 -#SBATCH --mem-per-cpu=2GB -#SBATCH --time=0-01:00 -#SBATCH --job-name=HIST -#SBATCH --mail-user=gwu479@usask.ca -#SBATCH --mail-type=ALL -#SBATCH --account=rpp-kshook -#SBATCH --output=/home/avanb/TestScripts/output/slurm-%A_%a.out - -module load StdEnv/2020 -module load gcc/9.3.0 -module load geo-stack/2022c -virtualenv --no-download $SLURM_TMPDIR/env -source $SLURM_TMPDIR/env/bin/activate - -python hist_per_GRU.py rmse -python hist_per_GRU.py maxe -python hist_per_GRU.py kgem diff --git a/utils/process_plot.sh b/utils/process_plot.sh deleted file mode 100755 index 654cac53d..000000000 --- a/utils/process_plot.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -#SBATCH --ntasks-per-node=1 -#SBATCH --cpus-per-task=1 -#SBATCH --mem-per-cpu=50GB -#SBATCH --time=0-04:00 -#SBATCH --job-name=GDB -#SBATCH --mail-user=gwu479@usask.ca -#SBATCH --mail-type=ALL -#SBATCH --account=rpp-kshook -#SBATCH --output=/home/avanb/TestScripts/output/slurm-%A_%a.out - -module load StdEnv/2020 -module load gcc/9.3.0 -module load geo-stack/2022c -virtualenv --no-download $SLURM_TMPDIR/env -source $SLURM_TMPDIR/env/bin/activate - -python plot_per_GRU.py sundials_1en6 rmse -python plot_per_GRU.py sundials_1en6 maxe -python plot_per_GRU.py sundials_1en6 kgem diff --git a/utils/process_scat.sh b/utils/process_scat.sh deleted file mode 100755 index ff24504f1..000000000 --- a/utils/process_scat.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -#SBATCH --ntasks-per-node=1 -#SBATCH --cpus-per-task=1 -#SBATCH --mem-per-cpu=2GB -#SBATCH --time=0-01:00 -#SBATCH --job-name=SCAT -#SBATCH --mail-user=gwu479@usask.ca -#SBATCH --mail-type=ALL -#SBATCH --account=rpp-kshook -#SBATCH --output=/home/avanb/TestScripts/output/slurm-%A_%a.out - -module load StdEnv/2020 -module load gcc/9.3.0 -module load geo-stack/2022c -virtualenv --no-download $SLURM_TMPDIR/env -source $SLURM_TMPDIR/env/bin/activate - -python scat_per_GRU.py rmse -python scat_per_GRU.py maxe -python scat_per_GRU.py kgem diff --git a/utils/process_stat.sh b/utils/process_stat.sh deleted file mode 100755 index a21915fd8..000000000 --- a/utils/process_stat.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -#SBATCH --ntasks-per-node=1 -#SBATCH --cpus-per-task=20 -#SBATCH --mem-per-cpu=6GB -#SBATCH --time=1-00:00 -#SBATCH --job-name=STAT -#SBATCH --mail-user=gwu479@usask.ca -#SBATCH --mail-type=ALL -#SBATCH --account=rpp-kshook -#SBATCH --output=/home/avanb/TestScripts/output/slurm-%A_%a.out - -module load StdEnv/2020 -module load gcc/9.3.0 -module load geo-stack/2022c -virtualenv --no-download $SLURM_TMPDIR/env -source $SLURM_TMPDIR/env/bin/activate - -python timeseries_to_statistics.py sundials_1en6 1 1 -python timeseries_to_statistics.py sundials_1en6 2 1 -python timeseries_to_statistics.py be1 1 1 -python timeseries_to_statistics.py be1 2 1 -python timeseries_to_statistics.py be32 1 1 -python timeseries_to_statistics.py be32 2 1 -python timeseries_to_statistics.py be16 1 1 -python timeseries_to_statistics.py be16 2 1 diff --git a/utils/process_statBatch.sh b/utils/process_statBatch.sh deleted file mode 100755 index cda320437..000000000 --- a/utils/process_statBatch.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -#SBATCH --ntasks-per-node=1 -#SBATCH --cpus-per-task=1 -#SBATCH --mem-per-cpu=12GB -#SBATCH --time=0-05:00 -#SBATCH --job-name=STATB -#SBATCH --mail-user=gwu479@usask.ca -#SBATCH --mail-type=ALL -#SBATCH --account=rpp-kshook -#SBATCH --output=/home/avanb/TestScripts/output/slurm-%A_%a.out -echo "$SLURM_ARRAY_TASK_ID" - -# ---------------------------------------------------------------------------------------------- -# RUN WITH: -# sbatch --array1-[number of jobs] [script name] -# sbatch --array=1-200 process_statBatch.sh -# sbatch --array=201-201 process_statBatch.sh -# ---------------------------------------------------------------------------------------------- - -module load StdEnv/2020 -module load gcc/9.3.0 -module load geo-stack/2022c -virtualenv --no-download $SLURM_TMPDIR/env -source $SLURM_TMPDIR/env/bin/activate - -python timeseries_to_statistics.py sundials_1en6 $SLURM_ARRAY_TASK_ID 200 -python timeseries_to_statistics.py be1 $SLURM_ARRAY_TASK_ID 200 -python timeseries_to_statistics.py be32 $SLURM_ARRAY_TASK_ID 200 -python timeseries_to_statistics.py be16 $SLURM_ARRAY_TASK_ID 200 diff --git a/utils/stepsMap_per_GRU.py b/utils/stepsMap_per_GRU.py deleted file mode 100644 index d1304693c..000000000 --- a/utils/stepsMap_per_GRU.py +++ /dev/null @@ -1,246 +0,0 @@ -# written originally by W. Knoben, modified by A. Van Beusekom (2023) - -## Visualize batch number and wallClocktime per GRU, (could be modified to visualize other attributes or forcing per GRU) -## Needs: -# Catchment shapefile with GRU delineation -# ? SUMMA forcing folder and attribute folder -# SUMMA stats file for wall clock times - -## Special note -# To improve visualization of large lakes, HydroLAKES lake delineations are plotted on top of the catchment GRUs and river segments. -# Dealing with HydroLAKES inputs is not considered within scope of the workflow and therefore requires some manual downloading and preprocessing of this data for those who wish to reproduce this step. -# The relevant code is easily disabled by switching the plot_lakes = True flag to False. - -# Run: -# python stepsMap_per_GRU.py be32 - - -# modules -import sys -import os -import matplotlib -import numpy as np -import xarray as xr -from pathlib import Path -import matplotlib.pyplot as plt -import copy -import pyproj -import fiona -import geopandas as gpd -import pandas as pd - -# The first input argument specifies the run where the files are -method_name = sys.argv[1] # sys.argv values are strings by default so this is fine -stat = sys.argv[2] - -# Simulation statistics file locations -stepsets= ['numberStateSplit','numberDomainSplitNrg','numberDomainSplitMass','numberScalarSolutions','meanStepSize'] -settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] - -viz_dir = Path('/home/avanb/scratch/statistics') -viz_fil = method_name + '_hrly_diff_stats_{}.nc' -viz_fil = viz_fil.format(','.join(settings)) -viz_fl2 = method_name + '_hrly_diff_steps_{}.nc' -viz_fl2 = viz_fl2.format(','.join(stepsets)) - -# Specify variables of interest -# Note, max for nodes is based off Graham which has 1185 nodes -plot_vars = ['numberStateSplit','numberDomainSplitNrg','numberDomainSplitMass','numberScalarSolutions','meanStepSize','wallClockTime'] -plt_titl = ['(a) State Splits','(b) Energy Domain Splits','(c) Mass Domain Splits','(d) Scalar Solutions', '(e) Mean Step Size','(f) Wallclock Time'] -leg_titl = ['$num$','$num$','$num$','$num$','$s$','$s$'] -if stat == 'mean': maxes = [0.06,0.0004,0.0004,0.0015,3600,10e-3] -if stat == 'amax': maxes = [2,1,1,10,3600,0.2] - -fig_fil = method_name + '_splits_steps_{}_compressed.png' -fig_fil = fig_fil.format(stat) - -# Get the albers shapes -main = Path('/home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/albers_projection') - -# Plot lakes? -plot_lakes = True -# lakes shapefile WHERE IS THIS -#lake_path = Path('C:/Globus endpoint/HydroLAKES/HydroLAKES_polys_v10_shp') -#lake_name = 'HydroLAKES_polys_v10_subset_NA.shp' - -## Control file handling -# Store the name of the 'active' file in a variable -controlFile = 'plot_control_NorthAmerica.txt' - -# Function to extract a given setting from the control file -def read_from_control( file, setting ): - - # Open controlFile and ... - with open(file) as contents: - for line in contents: - - # ... find the line with the requested setting - if setting in line and not line.startswith('#'): - break - - # Extract the setting's value - substring = line.split('|',1)[1] # Remove the setting's name (split into 2 based on '|', keep only 2nd part) - substring = substring.split('#',1)[0] # Remove comments, does nothing if no '#' is found - substring = substring.strip() # Remove leading and trailing whitespace, tabs, newlines - - # Return this value - return substring - -# Function to specify a default path -def make_default_path(suffix): - - # Get the root path - rootPath = Path( read_from_control(controlFile,'root_path') ) - - # Get the domain folder - domainName = read_from_control(controlFile,'domain_name') - domainFolder = 'domain_' + domainName - - # Specify the forcing path - defaultPath = rootPath / domainFolder / suffix - - return defaultPath - - -## Catchment shapefile location and variable names -# HM catchment shapefile path & name -hm_catchment_path = read_from_control(controlFile,'catchment_shp_path') -hm_catchment_name = read_from_control(controlFile,'catchment_shp_name') -# Specify default path if needed -if hm_catchment_path == 'default': - hm_catchment_path = make_default_path('shapefiles/catchment') # outputs a Path() -else: - hm_catchment_path = Path(hm_catchment_path) # make sure a user-specified path is a Path() - -# Find the GRU and HRU identifiers -hm_hruid = read_from_control(controlFile,'catchment_shp_hruid') - - -## River network shapefile location and variable names -# Plot rivers? -plot_rivers = False -# River network path & name -river_network_path = read_from_control(controlFile,'river_network_shp_path') -river_network_name = read_from_control(controlFile,'river_network_shp_name') -# Specify default path if needed -if river_network_path == 'default': - river_network_path = make_default_path('shapefiles/river_network') # outputs a Path() -else: - river_network_path = Path(river_network_path) # make sure a user-specified path is a Path() - -# Find the segment ID -seg_id = read_from_control(controlFile,'river_network_shp_segid') - -## Load all shapefiles and project to Albers Conformal Conic and reproject -# Set the target CRS -acc = 'ESRI:102008' - -# catchment shapefile, first 2 lines throw error so cutting them -#bas = gpd.read_file(hm_catchment_path/hm_catchment_name) -#bas_albers = bas.to_crs(acc) -bas_albers = gpd.read_file(main/'basin.shp') -xmin, ymin, xmax, ymax = bas_albers.total_bounds - -# river network shapefile, first 2 lines throw error so cutting them -if plot_rivers: - #riv = gpd.read_file(river_network_path/river_network_name) - #riv_albers = riv.to_crs(acc) - riv_albers = gpd.read_file(main/'river.shp') - -# lakes shapefile, first 2 lines throw error so cutting them -if plot_lakes: - #lakes = gpd.read_file(lake_path/lake_name) - #lak_albers = lakes.to_crs(acc) - lak_albers = gpd.read_file(main/'lakes.shp') - -## Pre-processing, map SUMMA sims to catchment shapes -# Get the aggregated statistics of SUMMA simulations -summa = xr.open_dataset(viz_dir/viz_fl2) -wall = xr.open_dataset(viz_dir/viz_fil) - -# Match the accummulated values to the correct HRU IDs in the shapefile -hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile -for plot_var in plot_vars: - stat0 = stat - - if plot_var != 'wallClockTime': - s = summa[plot_var].sel(stat=stat0) - else: - s = wall[plot_var].sel(stat=stat0) - - s = np.fabs(s) # make absolute value norm, not all positive - bas_albers[plot_var] = s.sel(hru=hru_ids_shp.values) - -# Select lakes of a certain size for plotting -if plot_lakes: - minSize = 1000 # km2 - in_domain = (lak_albers['Country'] == 'Canada') | \ - (lak_albers['Country'] == 'United States of America') | \ - (lak_albers['Country'] == 'Mexico') - out_domain = (lak_albers['Pour_long'] > -80) & (lak_albers['Pour_lat'] > 65) # Exclude Baffin Island - large_lakes_albers = lak_albers.loc[(lak_albers['Lake_area'] > minSize) & in_domain & (~out_domain) ] - lake_col = (8/255,81/255,156/255) - -##Figure - -# Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug -if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 25}) -else: - plt.rcParams.update({'font.size': 100}) - -if 'compressed' in fig_fil: - fig,axs = plt.subplots(3,2,figsize=(35,33)) -else: - fig,axs = plt.subplots(3,2,figsize=(140,133)) -fig.suptitle('{} Hourly Statistics'.format(method_name), fontsize=40) - -plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines - -# colorbar axes -f_x_mat = [0.443,0.94,0.443,0.94,0.443,0.94] -f_y_mat = [0.71,0.71,0.38,0.38,0.047,0.047] - -plt.tight_layout() - -def run_loop(i,var,the_max,f_x,f_y): - my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap - my_cmap.set_bad(color='white') #nan color white - vmin,vmax = 0, the_max - norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) - if var=='meanStepSize': - my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno')) # copy the default cmap - my_cmap.set_bad(color='white') #nan color white - vmin,vmax = 0, the_max - norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) - r = i//2 - c = i-r*2 - - # Data - bas_albers.plot(ax=axs[r,c], column=var, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) - - if stat == 'mean': word = 'mean' - if stat == 'amax': word = 'max' - - axs[r,c].set_title(plt_titl[i]) - axs[r,c].axis('off') - axs[r,c].set_xlim(xmin, xmax) - axs[r,c].set_ylim(ymin, ymax) - - # Custom colorbar - cax = fig.add_axes([f_x,f_y,0.02,0.25]) - sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) - sm._A = [] - cbr = fig.colorbar(sm, cax=cax) #, extend='max') #if max extend can't get title right - cbr.ax.set_ylabel(word + ' [{}]'.format(leg_titl[i]), labelpad=40, rotation=270) - - #cbr.ax.yaxis.set_offset_position('right') - - # lakes - if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) - -for i,(var,the_max,f_x,f_y) in enumerate(zip(plot_vars,maxes,f_x_mat,f_y_mat)): - run_loop(i,var,the_max,f_x,f_y) - -# Save -plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=True) \ No newline at end of file diff --git a/utils/steps_per_GRU.py b/utils/steps_per_GRU.py deleted file mode 100644 index c7ef39065..000000000 --- a/utils/steps_per_GRU.py +++ /dev/null @@ -1,120 +0,0 @@ -# written by A. Van Beusekom (2023) - -## Visualize statistics per GRU -## Needs: -# SUMMA output statistics - -## Special note -# SUMMA simulations have been preprocessed into single value statistics per model element, using auxiliary scripts in ~/utils -# Run: -# python steps_per_GRU.py [stat] -# where stat is mean or amax - -# modules -import os -import matplotlib -import numpy as np -import xarray as xr -from pathlib import Path -import matplotlib.pyplot as plt -import copy -import pandas as pd - -run_local = False -if run_local: - stat = 'amax' - viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be1'] #maybe make this an argument -else: - import sys - # The first input argument specifies the run where the files are - stat = sys.argv[1] - viz_dir = Path('/home/avanb/scratch/statistics') - method_name=['be1','be4','be8','be16','be32'] #maybe make this an argument - method_name=['be1'] #maybe make this an argument - #method_name=['be1','sundials_1en4','be4','be8','be16','be32','sundials_1en6'] #maybe make this an argument - -# Simulation statistics file locations -settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] -stepsets= ['numberStateSplit','numberDomainSplitNrg','numberDomainSplitMass','numberScalarSolutions','meanStepSize'] - -viz_fil = method_name.copy() -viz_fl2 = method_name.copy() -for i, m in enumerate(method_name): - viz_fil[i] = m + '_hrly_diff_stats_{}.nc' - viz_fil[i] = viz_fil[i].format(','.join(settings)) - viz_fl2[i] = m + '_hrly_diff_steps_{}.nc' - viz_fl2[i] = viz_fl2[i].format(','.join(stepsets)) - -# Specify variables of interest -plot_vars = ['numberDomainSplitNrg','numberDomainSplitMass','numberScalarSolutions','meanStepSize'] -plt_titl = ['(a) Energy Domain Splits','(b) Mass Domain Splits','(c) Scalar Solutions', '(d) Mean Step Size'] -leg_titl = ['$num$', '$num$','$num$','$s$'] -leg_titl0 = ['$num$', '$num$','$num$','$s$'] - -#fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' -#fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) -fig_fil = 'Splits_steps_scat_{}_compressed.png' -fig_fil = fig_fil.format(stat) - -summa = {} -wall = {} -for i, m in enumerate(method_name): - # Get the aggregated statistics of SUMMA simulations - summa[m] = xr.open_dataset(viz_dir/viz_fl2[i]) - wall[m] = xr.open_dataset(viz_dir/viz_fil[i]) - -##Figure - -# Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug -if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 25}) -else: - plt.rcParams.update({'font.size': 100}) - -if 'compressed' in fig_fil: - fig,axs = plt.subplots(2,2,figsize=(35,33)) -else: - fig,axs = plt.subplots(2,2,figsize=(140,133)) -fig.suptitle('Hourly Splits and Time Steps for each GRU', fontsize=40) - -def run_loop(i,var): - r = i//2 - c = i-r*2 - - # Data - for m in method_name: - s = summa[m][var].sel(stat=stat) - - if var == 'numberDomainSplitNrg' or var == 'numberDomainSplitMass': - s0 = summa[m]['numberStateSplit'].sel(stat=stat) - stat0_word = 'State Splits' - if var == 'numberDomainSplitNrg': stat_word = 'Energy Domain Splits' - if var == 'numberDomainSplitMass': stat_word = 'Mass Domain Splits' - if var == 'numberScalarSolutions': - s0 = summa[m]['numberDomainSplitMass'].sel(stat=stat) + summa[m]['numberDomainSplitNrg'].sel(stat=stat) - stat0_word = 'Domain Splits' - stat_word = 'Scalar Solutions' - if var == 'meanStepSize': - s0 = wall[m]['wallClockTime'].sel(stat=stat) - stat0_word = 'Wallclock Time' - stat_word = 'Mean Step Size' - - axs[r,c].scatter(x=s.values,y=s0.values,s=10,zorder=0,label=m) - - if stat == 'mean': word = ' mean' - if stat == 'amax': word = ' max' - - lgnd = axs[r,c].legend() - for j, m in enumerate(method_name): - lgnd.legendHandles[j]._sizes = [80] - axs[r,c].set_title(plt_titl[i]) - axs[r,c].set_xlabel(stat_word + word + ' [{}]'.format(leg_titl[i])) - axs[r,c].set_ylabel(stat0_word + word + ' [{}]'.format(leg_titl0[i])) - - -for i,var in enumerate(plot_vars): - run_loop(i,var) - -# Save -plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) diff --git a/utils/wallClockMap_per_GRU.py b/utils/wallClockMap_per_GRU.py deleted file mode 100644 index 6881bd9b1..000000000 --- a/utils/wallClockMap_per_GRU.py +++ /dev/null @@ -1,266 +0,0 @@ -# written originally by W. Knoben, modified by A. Van Beusekom (2023) - -## Visualize batch number and wallClocktime per GRU, (could be modified to visualize other attributes or forcing per GRU) -## Needs: -# Catchment shapefile with GRU delineation -# ? SUMMA forcing folder and attribute folder -# SUMMA stats file for wall clock times - -## Special note -# To improve visualization of large lakes, HydroLAKES lake delineations are plotted on top of the catchment GRUs and river segments. -# Dealing with HydroLAKES inputs is not considered within scope of the workflow and therefore requires some manual downloading and preprocessing of this data for those who wish to reproduce this step. -# The relevant code is easily disabled by switching the plot_lakes = True flag to False. - -# Run: -# python wallClockMap_per_GRU.py be32 - - -# modules -import sys -import os -import matplotlib -import numpy as np -import xarray as xr -from pathlib import Path -import matplotlib.pyplot as plt -import copy -import pyproj -import fiona -import geopandas as gpd -import pandas as pd - -# The first input argument specifies the run where the files are -method_name = sys.argv[1] # sys.argv values are strings by default so this is fine (sundials_1en6 or be1) - -# Simulation statistics file locations -settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] -viz_dir = Path('/home/avanb/scratch/statistics') -viz_fil = method_name + '_hrly_diff_stats_{}.nc' -viz_fil = viz_fil.format(','.join(settings)) -eff_fil = 'eff_' + method_name + '.txt' -nbatch_hrus = 518 # number of HRUs per batch - -# Specify variables of interest -# Note, max for nodes is based off Graham which has 1185 nodes -plot_vars = ['batch','node','effMultWallClockTime','wallClockTime','effMultWallClockMax','wallClockMax'] -plt_titl = ['(a) Batch number','(b) Node number','(c) Efficiency * Wall clock mean time','(d) Wall clock mean time','(e) Efficiency * Wall clock max time','(f) Wall clock max time'] -leg_titl = ['$num$','$num$','$s$','$s$','$s$','$s$'] -maxes = [998,1185,10e-3,10e-3,0.2,0.2] - -fig_fil = method_name + '_wallClockTime_batchNum_compressed.png' - -# Get the albers shapes -main = Path('/home/avanb/projects/rpp-kshook/wknoben/CWARHM_data/domain_NorthAmerica/shapefiles/albers_projection') - -# Plot lakes? -plot_lakes = True -# lakes shapefile WHERE IS THIS -#lake_path = Path('C:/Globus endpoint/HydroLAKES/HydroLAKES_polys_v10_shp') -#lake_name = 'HydroLAKES_polys_v10_subset_NA.shp' - -## Control file handling -# Store the name of the 'active' file in a variable -controlFile = 'plot_control_NorthAmerica.txt' - -# Function to extract a given setting from the control file -def read_from_control( file, setting ): - - # Open controlFile and ... - with open(file) as contents: - for line in contents: - - # ... find the line with the requested setting - if setting in line and not line.startswith('#'): - break - - # Extract the setting's value - substring = line.split('|',1)[1] # Remove the setting's name (split into 2 based on '|', keep only 2nd part) - substring = substring.split('#',1)[0] # Remove comments, does nothing if no '#' is found - substring = substring.strip() # Remove leading and trailing whitespace, tabs, newlines - - # Return this value - return substring - -# Function to specify a default path -def make_default_path(suffix): - - # Get the root path - rootPath = Path( read_from_control(controlFile,'root_path') ) - - # Get the domain folder - domainName = read_from_control(controlFile,'domain_name') - domainFolder = 'domain_' + domainName - - # Specify the forcing path - defaultPath = rootPath / domainFolder / suffix - - return defaultPath - - -## Catchment shapefile location and variable names -# HM catchment shapefile path & name -hm_catchment_path = read_from_control(controlFile,'catchment_shp_path') -hm_catchment_name = read_from_control(controlFile,'catchment_shp_name') -# Specify default path if needed -if hm_catchment_path == 'default': - hm_catchment_path = make_default_path('shapefiles/catchment') # outputs a Path() -else: - hm_catchment_path = Path(hm_catchment_path) # make sure a user-specified path is a Path() - -# Find the GRU and HRU identifiers -hm_hruid = read_from_control(controlFile,'catchment_shp_hruid') - - -## River network shapefile location and variable names -# Plot rivers? -plot_rivers = False -# River network path & name -river_network_path = read_from_control(controlFile,'river_network_shp_path') -river_network_name = read_from_control(controlFile,'river_network_shp_name') -# Specify default path if needed -if river_network_path == 'default': - river_network_path = make_default_path('shapefiles/river_network') # outputs a Path() -else: - river_network_path = Path(river_network_path) # make sure a user-specified path is a Path() - -# Find the segment ID -seg_id = read_from_control(controlFile,'river_network_shp_segid') - -## Load all shapefiles and project to Albers Conformal Conic and reproject -# Set the target CRS -acc = 'ESRI:102008' - -# catchment shapefile, first 2 lines throw error so cutting them -#bas = gpd.read_file(hm_catchment_path/hm_catchment_name) -#bas_albers = bas.to_crs(acc) -bas_albers = gpd.read_file(main/'basin.shp') -xmin, ymin, xmax, ymax = bas_albers.total_bounds - -# river network shapefile, first 2 lines throw error so cutting them -if plot_rivers: - #riv = gpd.read_file(river_network_path/river_network_name) - #riv_albers = riv.to_crs(acc) - riv_albers = gpd.read_file(main/'river.shp') - -# lakes shapefile, first 2 lines throw error so cutting them -if plot_lakes: - #lakes = gpd.read_file(lake_path/lake_name) - #lak_albers = lakes.to_crs(acc) - lak_albers = gpd.read_file(main/'lakes.shp') - -## Pre-processing, map SUMMA sims to catchment shapes -# Get the aggregated statistics of SUMMA simulations -summa = xr.open_dataset(viz_dir/viz_fil) -# Read the data from the eff.txt file into a DataFrame -eff = pd.read_csv(viz_dir/eff_fil, sep=',', header=None, names=['CPU Efficiency', 'Array ID', 'Job Wall-clock time', 'Node Number']) -# Extract only the values after the ':' character in the 'CPU Efficiency', 'Job Wall-clock time', and 'Node Number' columns -eff['CPU Efficiency'] = eff['CPU Efficiency'].str.split(':').str[1].astype(float) -eff['Array ID'] = eff['Array ID'].str.split(':').str[1].astype(int) -eff['Job Wall-clock time'] = eff['Job Wall-clock time'].str.split(':').str[1].astype(float) -eff['Node Number'] = eff['Node Number'].str.split(':').str[1].astype(int) - -# Match the accummulated values to the correct HRU IDs in the shapefile -hru_ids_shp = bas_albers[hm_hruid].astype(int) # hru order in shapefile -s0 = summa['wallClockTime'].sel(stat='mean') -s1 = summa['wallClockTime'].sel(stat='amax') -batch = np.floor(np.arange(len(s0.indexes['hru'])) /nbatch_hrus) -#basin_num = np.arange(len(s0.indexes['hru'])) % nbatch_hrus #not currently using -# Create a dictionary to store the values for each batch -efficiency = {} -node = {} -# Iterate over the rows in the data DataFrame -for index, row in eff.iterrows(): - # Extract the values from the row - batch0 = int(row['Array ID']) - eff = row['CPU Efficiency'] - nod = row['Node Number'] - # Store the value for the current batch in the dictionary - efficiency[batch0] = eff - node[batch0] = nod -# Select the values for the current batch using boolean indexing -eff_batch = np.array([efficiency[b] for b in batch]) -node_batch = np.array([node[b] for b in batch]) - -for plot_var in plot_vars: - if plot_var == 'batch': - s = s0*batch/s0 - if plot_var == 'node': - s = s0*node_batch/s0 - if plot_var == 'effMultWallClockTime': - s = s0*eff_batch - if plot_var == 'wallClockTime': - s = s0 - if plot_var == 'effMultWallClockMax': - s = s1*eff_batch - if plot_var == 'wallClockMax': - s = s1 - bas_albers[plot_var] = s.sel(hru=hru_ids_shp.values) - -# Select lakes of a certain size for plotting -if plot_lakes: - minSize = 1000 # km2 - in_domain = (lak_albers['Country'] == 'Canada') | \ - (lak_albers['Country'] == 'United States of America') | \ - (lak_albers['Country'] == 'Mexico') - out_domain = (lak_albers['Pour_long'] > -80) & (lak_albers['Pour_lat'] > 65) # Exclude Baffin Island - large_lakes_albers = lak_albers.loc[(lak_albers['Lake_area'] > minSize) & in_domain & (~out_domain) ] - lake_col = (8/255,81/255,156/255) - -##Figure - -# Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug -if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 25}) -else: - plt.rcParams.update({'font.size': 100}) - -# Flip the evaporation values so that they become positive, not if plotting diffs -#bas_albers['plot_ET'] = bas_albers['scalarTotalET'] * -1 -#bas_albers['plot_ET'] = bas_albers['plot_ET'].where(bas_albers['scalarTotalET'] != -9999, np.nan) - -if 'compressed' in fig_fil: - fig,axs = plt.subplots(3,2,figsize=(35,33)) -else: - fig,axs = plt.subplots(3,2,figsize=(140,133)) -fig.suptitle('{} Wallclock, Batch, and Node'.format(method_name), fontsize=40) - -plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines - -# colorbar axes -f_x_mat = [0.443,0.94,0.443,0.94,0.443,0.94] -f_y_mat = [0.71,0.71,0.38,0.38,0.047,0.047] - -plt.tight_layout() - -def run_loop(i,var,the_max,f_x,f_y): - my_cmap = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap - my_cmap.set_bad(color='white') #nan color white - vmin,vmax = 0, the_max - norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) - r = i//2 - c = i-r*2 - - # Data - bas_albers.plot(ax=axs[r,c], column=var, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) - - # Custom colorbar - cax = fig.add_axes([f_x,f_y,0.02,0.25]) - sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) - sm._A = [] - cbr = fig.colorbar(sm, cax=cax) #, extend='max') #if max extend can't get title right - cbr.ax.set_ylabel('[{}]'.format(leg_titl[i]), labelpad=40, rotation=270) - #cbr.ax.yaxis.set_offset_position('right') - - axs[r,c].set_title(plt_titl[i]) - axs[r,c].axis('off') - axs[r,c].set_xlim(xmin, xmax) - axs[r,c].set_ylim(ymin, ymax) - - # lakes - if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) - -for i,(var,the_max,f_x,f_y) in enumerate(zip(plot_vars,maxes,f_x_mat,f_y_mat)): - run_loop(i,var,the_max,f_x,f_y) - -# Save -plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=True) diff --git a/utils/wallClock_per_GRU.py b/utils/wallClock_per_GRU.py deleted file mode 100644 index 1402b690a..000000000 --- a/utils/wallClock_per_GRU.py +++ /dev/null @@ -1,129 +0,0 @@ -# written by A. Van Beusekom (2023) - -## Visualize batch number and wallClocktime per GRU, (could be modified to visualize other attributes or forcing per GRU) -## Needs: -# SUMMA output statistics - -## Special note -# SUMMA simulations have been preprocessed into single value statistics per model element, using auxiliary scripts in ~/utils -# Run: -# python wallClock_per_GRU.py - -# modules -import os -import matplotlib -import numpy as np -import xarray as xr -from pathlib import Path -import matplotlib.pyplot as plt -import copy -import pandas as pd - -run_local = False -if run_local: - viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be1'] #maybe make this an argument -else: - import sys - viz_dir = Path('/home/avanb/scratch/statistics') - method_name=['be1','be4','be8','be16','be32'] #sundials will not show node differences as much - -nbatch_hrus = 518 # number of HRUs per batch -# Simulation statistics file locations -settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] -viz_fil = method_name.copy() -eff_fil = method_name.copy() -for i, m in enumerate(method_name): - viz_fil[i] = m + '_hrly_diff_stats_{}.nc' - viz_fil[i] = viz_fil[i].format(','.join(settings)) - eff_fil[i] = 'eff_' + m + '.txt' - -# Specify variables of interest -plt_titl = ['(a) Mean time vs basin in batch','(b) Max time vs basin in batch','(c) Mean time vs node','(d) Max time vs node'] -leg_titl = ['$s$','$s$','$s$','$s$'] - -#fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' -#fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) -fig_fil = 'WallClockTime_batchNum_scat_compressed.png' - -summa = {} -eff = {} -for i, m in enumerate(method_name): - # Get the aggregated statistics of SUMMA simulations - summa[m] = xr.open_dataset(viz_dir/viz_fil[i]) - # Read the data from the eff.txt file into a DataFrame - eff[m] = pd.read_csv(viz_dir/eff_fil[i], sep=',', header=None, names=['CPU Efficiency', 'Array ID', 'Job Wall-clock time', 'Node Number']) - # Extract only the values after the ':' character in the 'CPU Efficiency', 'Job Wall-clock time', and 'Node Number' columns - eff[m]['CPU Efficiency'] = eff[m]['CPU Efficiency'].str.split(':').str[1].astype(float) - eff[m]['Array ID'] = eff[m]['Array ID'].str.split(':').str[1].astype(int) - eff[m]['Job Wall-clock time'] = eff[m]['Job Wall-clock time'].str.split(':').str[1].astype(float) - eff[m]['Node Number'] = eff[m]['Node Number'].str.split(':').str[1].astype(int) - -##Figure - -# Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug -if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 25}) -else: - plt.rcParams.update({'font.size': 100}) - -# Flip the evaporation values so that they become positive, not if plotting diffs -#bas_albers['plot_ET'] = bas_albers['scalarTotalET'] * -1 -#bas_albers['plot_ET'] = bas_albers['plot_ET'].where(bas_albers['scalarTotalET'] != -9999, np.nan) - -if 'compressed' in fig_fil: - fig,axs = plt.subplots(2,2,figsize=(35,33)) -else: - fig,axs = plt.subplots(2,2,figsize=(140,133)) -fig.suptitle('BE Hourly Wallclock for each GRU', fontsize=40) - -def run_loop(c,stat): - - # Data - for m in method_name: - s = summa[m]['wallClockTime'].sel(stat=stat) - batch = np.floor(np.arange(len(s.indexes['hru'])) /nbatch_hrus) - basin_num = np.arange(len(s.indexes['hru'])) % nbatch_hrus - # Create a dictionary to store the values for each batch - efficiency = {} - node = {} - # Iterate over the rows in the data DataFrame - for index, row in eff[m].iterrows(): - # Extract the values from the row - batch0 = int(row['Array ID']) - eff0 = row['CPU Efficiency'] - node0 = row['Node Number'] - # Store the value for the current batch in the dictionary - efficiency[batch0] = eff0 - node[batch0] = node0 - # Select the values for the current batch using boolean indexing - eff_batch = np.array([efficiency[b] for b in batch]) - node_batch = np.array([node[b] for b in batch]) - # Multiply the efficiency values by the s values - x = eff_batch * s.values - - if stat == 'mean': stat_word = 'Wall clock time hourly mean * efficiency ' - if stat == 'amax': stat_word = 'Wall clock time hourly max * efficiency ' - for r in range(2): - if r == 0: - axs[r,c].scatter(x=x,y=basin_num,s=1,zorder=0,label=m) - stat0_word ='Basin number in batch' - lgnd0 = axs[r,c].legend() - if r == 1: - axs[r,c].scatter(x=x,y=node_batch,s=1,zorder=0,label=m) - stat0_word ='Node number' - lgnd1 = axs[r,c].legend() - axs[r,c].set_title(plt_titl[r*2+c]) - axs[r,c].set_xlabel(stat_word + '[{}]'.format(leg_titl[r*2+c])) - axs[r,c].set_ylabel(stat0_word) - - for j, m in enumerate(method_name): - lgnd0.legendHandles[j]._sizes = [80] - lgnd1.legendHandles[j]._sizes = [80] - - -for i,stat in enumerate(['mean','amax']): - run_loop(i,stat) - -# Save -plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) diff --git a/utils/wallStd_per_GRU.py b/utils/wallStd_per_GRU.py deleted file mode 100644 index 779b4f3be..000000000 --- a/utils/wallStd_per_GRU.py +++ /dev/null @@ -1,122 +0,0 @@ -# written by A. Van Beusekom (2024) - -## Visualize statistics per GRU -## Needs: -# SUMMA output statistics - -## Special note -# SUMMA simulations have been preprocessed into single value statistics per model element, using auxiliary scripts in ~/utils -# Run: -# python wallStd_per_GRU.py - -# modules -import os -import matplotlib -import numpy as np -import xarray as xr -from pathlib import Path -import matplotlib.pyplot as plt -import copy -import pandas as pd - -run_local = False -if run_local: - viz_dir = Path('/Users/amedin/Research/USask/test_py/statistics') - method_name=['be1en','be1en'] #cm','be1en','be1lu'] #maybe make this an argument -else: - import sys - viz_dir = Path('/home/avanb/scratch/statistics') - method_name=['sundials_1en4','sundials_1en6','sundials_1en8'] #maybe make this an argument - -# Simulation statistics file locations -viz_fl2 = method_name.copy() -for i, m in enumerate(method_name): - viz_fl2[i] = m + '_hrly_diff_wals_{}.nc' - viz_fl2[i] = viz_fl2[i].format(','.join(['wallclock'])) - -# Specify variables of interest -stats = ['mean','amax'] -plot_vars = ['wallClockTime']*2 -comp_vars = ['wallClockTime']*2 - -plt_titl = ['(a) Wall Clock Time Mean','(b) Wall Clock Time Max'] -leg_titl0 = ['$s$']*2 -leg_titl = ['$s$']*2 - -#fig_fil = '{}_hrly_diff_scat_{}_{}_compressed.png' -#fig_fil = fig_fil.format(','.join(method_name),','.join(settings),stat) -fig_fil = 'WallStd_scat_mean_max_compressed.png' - -summa = {} -for i, m in enumerate(method_name): - # Get the aggregated statistics of SUMMA simulations - summa[m] = xr.open_dataset(viz_dir/viz_fl2[i]) - -##Figure - -def run_loop(i,var,comp,leg_t,leg_t0,plt_t,stat): - r = i//2 - c = i-r*2 - - # Data - for mm,m in enumerate(method_name): - # Get the statistics, remove 9999 (should be nan, but just in case) - s = np.fabs(summa[m][var].sel(stat=stat)).where(lambda x: x != 9999) - s0 = np.fabs(summa[m][comp].sel(stat='std')).where(lambda x: x != 9999) - - stat_word = 'Time' - stat0_word = 'Time std dev' - - axs[c].scatter(x=s.values,y=s0.values,s=10,zorder=0,label=m) - - # Create a mask that is True where `s.values` and `s0.values` are not NaN - mask = ~np.isnan(s.values) & ~np.isnan(s0.values) - - # Use the mask to filter `s.values` and `s0.values` - filtered_s_values = s.values[mask] - filtered_s0_values = s0.values[mask] - - # Fit a linear regression model - coefficients = np.polyfit(filtered_s_values, filtered_s0_values, 1) - polynomial = np.poly1d(coefficients) - - # Calculate the R-squared value - correlation_matrix = np.corrcoef(filtered_s_values, filtered_s0_values) - correlation_xy = correlation_matrix[0,1] - r_squared = correlation_xy**2 - # Add the R-squared value to the plot - axs[c].text(0.8, 0.5-0.03*mm, f'{m} R² = {r_squared:.2f}', transform=axs[c].transAxes) - print(m,stat,'Coefficients:', coefficients, 'R-squared:', r_squared) - - - if stat == 'mean': word = ' mean' - if stat == 'amax': word = ' max' - - lgnd = axs[c].legend() - for j, m in enumerate(method_name): - lgnd.legendHandles[j]._sizes = [80] - axs[c].set_title(plt_t) - axs[c].set_xscale('log') - axs[c].set_yscale('log') - - axs[c].set_xlabel(stat_word + word + ' [{}]'.format(leg_t)) - axs[c].set_ylabel(stat0_word + ' [{}]'.format(leg_t0)) - -if 'compressed' in fig_fil: - plt.rcParams.update({'font.size': 25}) -else: - plt.rcParams.update({'font.size': 100}) - -if 'compressed' in fig_fil: - fig,axs = plt.subplots(2,figsize=(35,38)) -else: - fig,axs = plt.subplots(2,figsize=(140,160)) -fig.subplots_adjust(hspace=0.24, wspace=0.24) # Adjust the bottom margin, vertical space, and horizontal space -#fig.suptitle('Scatterplot of Hourly Statistics for each GRU', fontsize=40,y=1.0) - -for i,(var,comp,leg_t,leg_t0,plt_t,stat) in enumerate(zip(plot_vars,comp_vars,leg_titl,leg_titl0,plt_titl,stats)): - run_loop(i,var,comp,leg_t,leg_t0,plt_t,stat) - -# Save -plt.savefig(viz_dir/fig_fil, bbox_inches='tight', transparent=False) - diff --git a/utils/write_seff.bash b/utils/write_seff.bash deleted file mode 100755 index 0b9295843..000000000 --- a/utils/write_seff.bash +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash -# run as write_seff.bash /path/to/log.txt /path/to/output.txt - -# Set the path to the log file -LOG_FILE="$1" - -# Set the path to the output file -OUTPUT_FILE="$2" - -# Create the output file if it doesn't exist, and overwrite it with the new output -touch "$OUTPUT_FILE" -> "$OUTPUT_FILE" - -# Loop through each line in the log file with "log_slurmId_" - grep "log_slurmId_" "$LOG_FILE" | while read -r line ; do - - # Extract the job ID from the line - job_id=$(echo "$line" | grep -oP 'log_slurmId_\K\d+(?=_arrayId_)') - - # Call the seff command on the job ID and store the output in a variable - seff_output=$(seff "$job_id") - - # Extract the CPU efficiency percentage from the seff output and convert it to a decimal format - cpu_efficiency=$(echo "$seff_output" | grep -oP 'CPU Efficiency: \K\d+\.\d+') - cpu_efficiency=$(echo "scale=4; $cpu_efficiency/100" | bc) - - # Extract the Array Job ID from the seff output - array_id=$(echo "$seff_output" | grep -oP 'Array Job ID: \K\d+_\d+' | awk -F'_' '{print $2}') - - # Extract the Job Wall-clock time from the seff output, and convert to hours - wall_clock=$(echo "$seff_output" | grep -oP 'Job Wall-clock time: \K.*') - if [[ "$wall_clock" == *-* ]]; then - days=$(echo "$wall_clock" | awk -F'-' '{print $1}') - hours=$(echo "$wall_clock" | awk -F'-' '{print $2}' | awk -F':' '{print $1}') - minutes=$(echo "$wall_clock" | awk -F'-' '{print $2}' | awk -F':' '{print $2}') - else - days=0 - hours=$(echo "$wall_clock" | awk -F':' '{print $1}') - minutes=$(echo "$wall_clock" | awk -F':' '{print $2}') - fi - total_hours=$(echo "scale=2; ($days*24)+$hours+($minutes/60)" | bc) - - # Call the sacct command on the job ID and store the output in a variable - # Note, the user has to be changed to the user who ran the job - sacct_output=$(sacct -j "$job_id" -u avanb -o NodeList) - - # Extract the node number from the NodeList field - # Note, this assumes it is running on graXXXX - node_list=$(echo "$sacct_output" | tail -n 1 | sed 's/^[[:space:]]*//') - node_number=$(echo "$node_list" | sed 's/gra//') - - # Write the CPU efficiency, Array Job ID, Job Wall-clock time, and the node number to the output file - echo "CPU Efficiency: $cpu_efficiency, Array ID: $array_id, Job Wall-clock time: $total_hours, Node Number: $node_number" >> "$OUTPUT_FILE" - done From 3173cdab7b37616b5702ace8dced76016b5cb49b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 29 Jul 2024 10:57:31 -0500 Subject: [PATCH 1356/1472] Got rid of a bunch of stuff we were not using --- Dockerfile | 36 --- case_study/base_settings/GENPARM.TBL | 36 --- case_study/base_settings/MPTABLE.TBL | 288 -------------------- case_study/base_settings/SOILPARM.TBL | 59 ---- case_study/base_settings/VEGPARM.TBL | 211 -------------- case_study/base_settings/localParamInfo.txt | 245 ----------------- case_study/base_settings/readme.md | 37 --- case_study/readme.md | 18 -- ci/summa_install_utils | 102 ------- docs/{ => installation}/README_ngen.md | 0 docs/{ => installation}/README_not_ngen.md | 0 test_ngen/readme.md | 13 + 12 files changed, 13 insertions(+), 1032 deletions(-) delete mode 100644 Dockerfile delete mode 100644 case_study/base_settings/GENPARM.TBL delete mode 100644 case_study/base_settings/MPTABLE.TBL delete mode 100644 case_study/base_settings/SOILPARM.TBL delete mode 100644 case_study/base_settings/VEGPARM.TBL delete mode 100644 case_study/base_settings/localParamInfo.txt delete mode 100644 case_study/base_settings/readme.md delete mode 100644 case_study/readme.md delete mode 100644 ci/summa_install_utils rename docs/{ => installation}/README_ngen.md (100%) rename docs/{ => installation}/README_not_ngen.md (100%) create mode 100644 test_ngen/readme.md diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index b736ce13f..000000000 --- a/Dockerfile +++ /dev/null @@ -1,36 +0,0 @@ -FROM ubuntu:xenial - -# install only the packages that are needed -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - software-properties-common python-software-properties \ - ca-certificates \ - git \ - make \ - libnetcdff-dev \ - liblapack-dev \ - vim - -# install gfortran-6 -RUN add-apt-repository ppa:ubuntu-toolchain-r/test -y \ - && apt-get update \ - && apt-get install -y --no-install-recommends gfortran-6 \ - && apt-get clean - -# set environment variables for docker build -ENV F_MASTER /code -ENV FC gfortran -ENV FC_EXE gfortran -ENV INCLUDES -I/usr/include -ENV LIBRARIES '-L/usr/lib -lnetcdff -llapack -lblas' - -# add code directory -WORKDIR /code -ADD . /code - -# fetch tags and build summa -RUN git fetch --tags && make -C build/ -f Makefile - -# run summa when running the docker image -WORKDIR bin -ENTRYPOINT ["./summa.exe"] diff --git a/case_study/base_settings/GENPARM.TBL b/case_study/base_settings/GENPARM.TBL deleted file mode 100644 index 05c460196..000000000 --- a/case_study/base_settings/GENPARM.TBL +++ /dev/null @@ -1,36 +0,0 @@ -General Parameters -SLOPE_DATA -9 -0.1 -0.6 -1.0 -0.35 -0.55 -0.8 -0.63 -0.0 -0.0 -SBETA_DATA --2.0 -FXEXP_DATA -2.0 -CSOIL_DATA -2.00E+6 -SALP_DATA -2.6 -REFDK_DATA -2.0E-6 -REFKDT_DATA -1.0 -FRZK_DATA -0.15 -ZBOT_DATA --8.0 -CZIL_DATA -0.1 -SMLOW_DATA -0.5 -SMHIGH_DATA -3.0 -LVCOEF_DATA -0.5 diff --git a/case_study/base_settings/MPTABLE.TBL b/case_study/base_settings/MPTABLE.TBL deleted file mode 100644 index 97990c7e4..000000000 --- a/case_study/base_settings/MPTABLE.TBL +++ /dev/null @@ -1,288 +0,0 @@ -&noah_mp_usgs_veg_categories - VEG_DATASET_DESCRIPTION = "USGS" - NVEG = 27 -/ -&noah_mp_usgs_parameters - ! NVEG = 27 - ! 1: Urban and Built-Up Land - ! 2: Dryland Cropland and Pasture - ! 3: Irrigated Cropland and Pasture - ! 4: Mixed Dryland/Irrigated Cropland and Pasture - ! 5: Cropland/Grassland Mosaic - ! 6: Cropland/Woodland Mosaic - ! 7: Grassland - ! 8: Shrubland - ! 9: Mixed Shrubland/Grassland - ! 10: Savanna - ! 11: Deciduous Broadleaf Forest - ! 12: Deciduous Needleleaf Forest - ! 13: Evergreen Broadleaf Forest - ! 14: Evergreen Needleleaf Forest - ! 15: Mixed Forest - ! 16: Water Bodies - ! 17: Herbaceous Wetland - ! 18: Wooded Wetland - ! 19: Barren or Sparsely Vegetated - ! 20: Herbaceous Tundra - ! 21: Wooded Tundra - ! 22: Mixed Tundra - ! 23: Bare Ground Tundra - ! 24: Snow or Ice - ! 25: Playa - ! 26: Lava - ! 27: White Sand - - ISURBAN = 1 - ISWATER = 16 - ISBARREN = 19 - ISSNOW = 24 - EBLFOREST = 13 - - !--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ! 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 - !--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - CH2OP = 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, - DLEAF = 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, - Z0MVT = 1.00, 0.15, 0.15, 0.15, 0.14, 0.50, 0.12, 0.06, 0.09, 0.50, 0.80, 0.85, 1.10, 1.09, 0.80, 0.00, 0.12, 0.50, 0.00, 0.10, 0.30, 0.20, 0.03, 0.00, 0.01, 0.00, 0.00, - HVT = 15.0, 2.00, 2.00, 2.00, 1.50, 8.00, 1.00, 1.10, 1.10, 10.0, 16.0, 18.0, 20.0, 20.0, 16.0, 0.00, 0.50, 10.0, 0.00, 0.50, 4.00, 2.00, 0.50, 0.00, 0.10, 0.00, 0.00, - HVB = 1.00, 0.10, 0.10, 0.10, 0.10, 0.15, 0.05, 0.10, 0.10, 0.10, 11.5, 7.00, 8.00, 8.50, 10.0, 0.00, 0.05, 0.10, 0.00, 0.10, 0.10, 0.10, 0.10, 0.00, 0.10, 0.00, 0.00, - DEN = 0.01, 25.0, 25.0, 25.0, 25.0, 25.0, 100., 10.0, 10.0, 0.02, 0.10, 0.28, 0.02, 0.28, 0.10, 0.01, 10.0, 0.10, 0.01, 1.00, 1.00, 1.00, 1.00, 0.00, 0.01, 0.01, 0.01, - RC = 1.00, 0.08, 0.08, 0.08, 0.08, 0.08, 0.03, 0.12, 0.12, 3.00, 1.40, 1.20, 3.60, 1.20, 1.40, 0.01, 0.10, 1.40, 0.01, 0.30, 0.30, 0.30, 0.30, 0.00, 0.01, 0.01, 0.01, - - ! Row 1: Vis - ! Row 2: Near IR - RHOL = 0.00, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.07, 0.10, 0.10, 0.10, 0.07, 0.10, 0.07, 0.10, 0.00, 0.11, 0.10, 0.00, 0.10, 0.10, 0.10, 0.10, 0.00, 0.10, 0.00, 0.00, - 0.00, 0.58, 0.58, 0.58, 0.58, 0.58, 0.58, 0.35, 0.45, 0.45, 0.45, 0.35, 0.45, 0.35, 0.45, 0.00, 0.58, 0.45, 0.00, 0.45, 0.45, 0.45, 0.45, 0.00, 0.45, 0.00, 0.00, - - ! Row 1: Vis - ! Row 2: Near IR - RHOS = 0.00, 0.36, 0.36, 0.36, 0.36, 0.36, 0.36, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.00, 0.36, 0.16, 0.00, 0.16, 0.16, 0.16, 0.16, 0.00, 0.16, 0.00, 0.00, - 0.00, 0.58, 0.58, 0.58, 0.58, 0.58, 0.58, 0.39, 0.39, 0.39, 0.39, 0.39, 0.39, 0.39, 0.39, 0.00, 0.58, 0.39, 0.00, 0.39, 0.39, 0.39, 0.39, 0.00, 0.39, 0.00, 0.00, - - ! Row 1: Vis - ! Row 2: Near IR - TAUL = 0.00, 0.07, 0.07, 0.07, 0.07, 0.07, 0.07, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.00, 0.07, 0.05, 0.00, 0.05, 0.05, 0.05, 0.05, 0.00, 0.05, 0.00, 0.00, - 0.00, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.10, 0.10, 0.25, 0.25, 0.10, 0.25, 0.10, 0.25, 0.00, 0.25, 0.25, 0.00, 0.25, 0.25, 0.25, 0.25, 0.00, 0.25, 0.00, 0.00, - - ! Row 1: Vis - ! Row 2: Near IR - TAUS = 0.000, 0.220, 0.220, 0.220, 0.220, 0.220, 0.220, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.000, 0.220, 0.001, 0.000, 0.220, 0.001, 0.001, 0.001, 0.000, 0.001, 0.000, 0.000, - 0.000, 0.380, 0.380, 0.380, 0.380, 0.380, 0.380, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.000, 0.380, 0.001, 0.000, 0.380, 0.001, 0.001, 0.001, 0.000, 0.001, 0.000, 0.000, - - XL = 0.000, -0.30, -0.30, -0.30, -0.30, -0.30, -0.30, 0.010, 0.250, 0.010, 0.250, 0.010, 0.010, 0.010, 0.250, 0.000, -0.30, 0.250, 0.000, -0.30, 0.250, 0.250, 0.250, 0.000, 0.250, 0.000, 0.000, -! CWPVT = 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, - CWPVT = 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, - C3PSN = 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, - KC25 = 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, - AKC = 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, - KO25 = 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, - AKO = 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, - AVCMX = 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, - AQE = 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, - - LTOVRC= 0.0, 1.2, 1.2, 1.2, 1.2, 1.30, 0.50, 0.65, 0.70, 0.65, 0.55, 0.2, 0.55, 0.5, 0.5, 0.0, 1.4, 1.4, 0.0, 1.2, 1.3, 1.4, 1.0, 0.0, 1.0, 0.0, 0.0, - DILEFC= 0.00, 0.50, 0.50, 0.50, 0.35, 0.20, 0.20, 0.20, 0.50, 0.50, 0.60, 1.80, 0.50, 1.20, 0.80, 0.00, 0.40, 0.40, 0.00, 0.40, 0.30, 0.40, 0.30, 0.00, 0.30, 0.00, 0.00, - DILEFW= 0.00, 0.20, 0.20, 0.20, 0.20, 0.20, 0.10, 0.20, 0.20, 0.50, 0.20, 0.20, 4.00, 0.20, 0.20, 0.00, 0.20, 0.20, 0.00, 0.20, 0.20, 0.20, 0.20, 0.00, 0.20, 0.00, 0.00, - RMF25 = 0.00, 1.00, 1.40, 1.45, 1.45, 1.45, 1.80, 0.26, 0.26, 0.80, 3.00, 4.00, 0.65, 3.00, 3.00, 0.00, 3.20, 3.20, 0.00, 3.20, 3.00, 3.00, 3.00, 0.00, 3.00, 0.00, 0.00, - SLA = 60, 80, 80, 80, 80, 80, 60, 60, 60, 50, 80, 80, 80, 80, 80, 0, 80, 80, 0, 80, 80, 80, 80, 0, 80, 0, 0, - FRAGR = 0.00, 0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.10, 0.20, 0.10, 0.10, 0.00, 0.10, 0.10, 0.10, 0.10, 0.10, 0.10, 0.10, 0.00, 0.10, 0.00, 0.00, - TMIN = 0, 273, 273, 273, 273, 273, 273, 273, 273, 273, 273, 268, 273, 265, 268, 0, 268, 268, 0, 268, 268, 268, 268, 0, 268, 0, 0, - VCMX25= 0.00, 80.0, 80.0, 80.0, 60.0, 70.0, 40.0, 40.0, 40.0, 40.0, 60.0, 60.0, 60.0, 50.0, 55.0, 0.00, 50.0, 50.0, 0.00, 50.0, 50.0, 50.0, 50.0, 0.00, 50.0, 0.00, 0.00, - TDLEF = 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 268, 278, 278, 268, 0, 268, 268, 0, 268, 268, 268, 268, 0, 268, 0, 0, - BP = 1.E15, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 1.E15, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 1.E15, 2.E3, 1.E15, 1.E15, - MP = 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 6., 9., 6., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., - QE25 = 0., 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.00, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.00, 0.06, 0.00, 0.00, - RMS25 = 0.00, 0.10, 0.10, 0.10, 0.10, 0.10, 0.10, 0.10, 0.10, 0.32, 0.10, 0.64, 0.30, 0.90, 0.80, 0.00, 0.10, 0.10, 0.00, 0.10, 0.10, 0.10, 0.00, 0.00, 0.00, 0.00, 0.00, - RMR25 = 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 1.20, 0.00, 0.00, 0.01, 0.01, 0.05, 0.05, 0.36, 0.03, 0.00, 0.00, 0.00, 0.00, 2.11, 2.11, 2.11, 0.00, 0.00, 0.00, 0.00, 0.00, - ARM = 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, - FOLNMX= 0.00, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 0.00, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 0.00, 1.5, 0.00, 0.00, - WDPOOL= 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 0.00, 0.00, 1.00, 0.00, 0.00, 1.00, 1.00, 0.00, 0.00, 0.00, 0.00, 0.00, - WRRAT = 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 3.00, 3.00, 3.00, 30.0, 30.0, 30.0, 30.0, 30.0, 0.00, 0.00, 30.0, 0.00, 0.00, 3.00, 3.00, 0.00, 0.00, 0.00, 0.00, 0.00, - MRP = 0.00, 0.23, 0.23, 0.23, 0.23, 0.23, 0.17, 0.19, 0.19, 0.40, 0.40, 0.37, 0.23, 0.37, 0.30, 0.00, 0.17, 0.40, 0.00, 0.17, 0.23, 0.20, 0.00, 0.00, 0.20, 0.00, 0.00, - -! Monthly values, one row for each month: - SAIM = 0.0, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.2, 0.2, 0.3, 0.4, 0.3, 0.5, 0.4, 0.4, 0.0, 0.2, 0.3, 0.0, 0.1, 0.2, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.2, 0.2, 0.3, 0.4, 0.3, 0.5, 0.4, 0.4, 0.0, 0.2, 0.3, 0.0, 0.1, 0.2, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.2, 0.2, 0.3, 0.4, 0.3, 0.5, 0.4, 0.4, 0.0, 0.2, 0.3, 0.0, 0.1, 0.2, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.2, 0.2, 0.3, 0.4, 0.4, 0.5, 0.3, 0.4, 0.0, 0.2, 0.3, 0.0, 0.1, 0.2, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.2, 0.2, 0.2, 0.3, 0.3, 0.3, 0.2, 0.2, 0.3, 0.4, 0.4, 0.5, 0.4, 0.4, 0.0, 0.3, 0.3, 0.0, 0.1, 0.2, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.3, 0.3, 0.3, 0.4, 0.4, 0.4, 0.2, 0.3, 0.4, 0.4, 0.7, 0.5, 0.5, 0.4, 0.0, 0.4, 0.4, 0.0, 0.2, 0.2, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.4, 0.4, 0.4, 0.6, 0.6, 0.8, 0.4, 0.6, 0.8, 0.9, 1.3, 0.5, 0.5, 0.7, 0.0, 0.6, 0.6, 0.0, 0.4, 0.4, 0.4, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.5, 0.5, 0.5, 0.9, 0.9, 1.3, 0.6, 0.9, 1.2, 1.2, 1.2, 0.5, 0.6, 0.8, 0.0, 0.9, 0.9, 0.0, 0.6, 0.6, 0.6, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.4, 0.4, 0.4, 0.7, 1.0, 1.1, 0.8, 1.0, 1.3, 1.6, 1.0, 0.5, 0.6, 1.0, 0.0, 0.7, 1.0, 0.0, 0.7, 0.8, 0.7, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.3, 0.3, 0.3, 0.3, 0.8, 0.4, 0.7, 0.6, 0.7, 1.4, 0.8, 0.5, 0.7, 1.0, 0.0, 0.3, 0.8, 0.0, 0.5, 0.7, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.3, 0.3, 0.3, 0.3, 0.4, 0.4, 0.3, 0.3, 0.4, 0.6, 0.6, 0.5, 0.6, 0.5, 0.0, 0.3, 0.4, 0.0, 0.3, 0.3, 0.3, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.3, 0.3, 0.3, 0.3, 0.3, 0.4, 0.2, 0.3, 0.4, 0.4, 0.5, 0.5, 0.5, 0.4, 0.0, 0.3, 0.4, 0.0, 0.2, 0.2, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, - - LAIM = 0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.4, 0.0, 0.2, 0.3, 0.0, 0.0, 4.5, 4.0, 2.0, 0.0, 0.2, 0.2, 0.0, 0.2, 1.0, 0.6, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.3, 0.0, 0.5, 0.0, 0.3, 0.3, 0.0, 0.0, 4.5, 4.0, 2.0, 0.0, 0.3, 0.3, 0.0, 0.3, 1.0, 0.6, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.3, 0.2, 0.6, 0.2, 0.4, 0.5, 0.3, 0.0, 4.5, 4.0, 2.2, 0.0, 0.3, 0.3, 0.0, 0.3, 1.1, 0.7, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.4, 0.6, 0.7, 0.6, 0.7, 0.8, 1.2, 0.6, 4.5, 4.0, 2.6, 0.0, 0.4, 0.6, 0.0, 0.4, 1.3, 0.8, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 1.0, 1.0, 1.1, 2.0, 1.2, 1.5, 1.4, 1.8, 3.0, 1.2, 4.5, 4.0, 3.5, 0.0, 1.1, 2.0, 0.0, 0.6, 1.7, 1.2, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 2.0, 2.0, 2.0, 2.5, 3.3, 3.0, 2.3, 2.6, 3.6, 4.7, 2.0, 4.5, 4.0, 4.3, 0.0, 2.5, 3.3, 0.0, 1.5, 2.1, 1.8, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 3.0, 3.0, 3.0, 3.2, 3.7, 3.5, 2.3, 2.9, 3.8, 4.5, 2.6, 4.5, 4.0, 4.3, 0.0, 3.2, 3.7, 0.0, 1.7, 2.1, 1.8, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 3.0, 3.0, 3.0, 2.2, 3.2, 1.5, 1.7, 1.6, 2.1, 3.4, 1.7, 4.5, 4.0, 3.7, 0.0, 2.2, 3.2, 0.0, 0.8, 1.8, 1.3, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 1.5, 1.5, 1.5, 1.1, 1.3, 0.7, 0.6, 0.7, 0.9, 1.2, 1.0, 4.5, 4.0, 2.6, 0.0, 1.1, 1.3, 0.0, 0.4, 1.3, 0.8, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.3, 0.2, 0.6, 0.2, 0.4, 0.5, 0.3, 0.5, 4.5, 4.0, 2.2, 0.0, 0.3, 0.3, 0.0, 0.3, 1.1, 0.7, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.3, 0.0, 0.5, 0.0, 0.3, 0.3, 0.0, 0.2, 4.5, 4.0, 2.0, 0.0, 0.3, 0.3, 0.0, 0.2, 1.0, 0.6, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.4, 0.0, 0.2, 0.3, 0.0, 0.0, 4.5, 4.0, 2.0, 0.0, 0.2, 0.2, 0.0, 0.2, 1.0, 0.6, 0.0, 0.0, 0.0, 0.0, 0.0, - - SLAREA=0.0228,0.0200,0.0200,0.0295,0.0223,0.0277,0.0060,0.0227,0.0188,0.0236,0.0258,0.0200,0.0200,0.0090,0.0223,0.0422,0.0390, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, - -! Five types, one row for each type. - EPS = 41.87, 0.00, 0.00, 2.52, 0.04, 17.11, 0.02, 21.62, 0.11, 22.80, 46.86, 0.00, 0.00, 0.46, 30.98, 2.31, 1.63, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.98, 0.00, 0.00, 0.16, 0.09, 0.28, 0.05, 0.92, 0.22, 0.59, 0.38, 0.00, 0.00, 3.34, 0.96, 1.47, 1.07, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 1.82, 0.00, 0.00, 0.23, 0.05, 0.81, 0.03, 1.73, 1.26, 1.37, 1.84, 0.00, 0.00, 1.85, 1.84, 1.70, 1.21, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -/ - -&noah_mp_modis_veg_categories - VEG_DATASET_DESCRIPTION = "modified igbp modis noah" - NVEG = 20 -/ - -&noah_mp_modis_veg_categories - VEG_DATASET_DESCRIPTION = "modified igbp modis noah" - NVEG = 20 -/ - -&noah_mp_modis_parameters -! 1 'Evergreen Needleleaf Forest' -> USGS 14 -! 2, 'Evergreen Broadleaf Forest' -> USGS 13 -! 3, 'Deciduous Needleleaf Forest' -> USGS 12 -! 4, 'Deciduous Broadleaf Forest' -> USGS 11 -! 5, 'Mixed Forests' -> USGS 15 -! 6, 'Closed Shrublands' -> USGS 8 "shrubland" -! 7, 'Open Shrublands' -> USGS 9 "shrubland/grassland" -! 8, 'Woody Savannas' -> USGS 8 "shrubland" -! 9, 'Savannas' -> USGS 10 -! 10, 'Grasslands' -> USGS 7 -! 11 'Permanent wetlands' -> avg of USGS 17 and 18 (herb. wooded wetland) -! 12, 'Croplands' -> USGS 2 "dryland cropland" -! 13, 'Urban and Built-Up' -> USGS 1 -! 14 'cropland/natural vegetation mosaic' -> USGS 5 "cropland/grassland" -! 15, 'Snow and Ice' -> USGS 24 -! 16, 'Barren or Sparsely Vegetated' -> USGS 19 -! 17, 'Water' -> USGS 16 -! 18, 'Wooded Tundra' -> USGS 21 -! 19, 'Mixed Tundra' -> USGS 22 -! 20, 'Barren Tundra' -> USGS 23 - - ISURBAN = 13 - ISWATER = 17 - ISBARREN = 16 - ISSNOW = 15 - EBLFOREST = 2 - - !--------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ! 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 - !--------------------------------------------------------------------------------------------------------------------------------------------------------------------- - CH2OP = 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, - DLEAF = 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, - Z0MVT = 1.09, 1.10, 0.85, 0.80, 0.80, 0.20, 0.06, 0.60, 0.50, 0.12, 0.30, 0.15, 1.00, 0.14, 0.00, 0.00, 0.00, 0.30, 0.20, 0.03, - HVT = 20.0, 20.0, 18.0, 16.0, 16.0, 1.10, 1.10, 13.0, 10.0, 1.00, 5.00, 2.00, 15.0, 1.50, 0.00, 0.00, 0.00, 4.00, 2.00, 0.50, - HVB = 8.50, 8.00, 7.00, 11.5, 10.0, 0.10, 0.10, 0.10, 0.10, 0.05, 0.10, 0.10, 1.00, 0.10, 0.00, 0.00, 0.00, 0.30, 0.20, 0.10, - DEN = 0.28, 0.02, 0.28, 0.10, 0.10, 10.0, 10.0, 10.0, 0.02, 100., 5.05, 25.0, 0.01, 25.0, 0.00, 0.01, 0.01, 1.00, 1.00, 1.00, - RC = 1.20, 3.60, 1.20, 1.40, 1.40, 0.12, 0.12, 0.12, 3.00, 0.03, 0.75, 0.08, 1.00, 0.08, 0.00, 0.01, 0.01, 0.30, 0.30, 0.30, - - ! Row 1: Vis - ! Row 2: Near IR - RHOL = 0.07, 0.10, 0.07, 0.10, 0.10, 0.07, 0.07, 0.07, 0.10, 0.11, 0.105, 0.11, 0.00, 0.11, 0.00, 0.00, 0.00, 0.10, 0.10, 0.10, - 0.35, 0.45, 0.35, 0.45, 0.45, 0.35, 0.35, 0.35, 0.45, 0.58, 0.515, 0.58, 0.00, 0.58, 0.00, 0.00, 0.00, 0.45, 0.45, 0.45, - - ! Row 1: Vis - ! Row 2: Near IR - RHOS = 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.36, 0.26, 0.36, 0.00, 0.36, 0.00, 0.00, 0.00, 0.16, 0.16, 0.16, - 0.39, 0.39, 0.39, 0.39, 0.39, 0.39, 0.39, 0.39, 0.39, 0.58, 0.485, 0.58, 0.00, 0.58, 0.00, 0.00, 0.00, 0.39, 0.39, 0.39, - - ! Row 1: Vis - ! Row 2: Near IR - TAUL = 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.07, 0.06, 0.07, 0.00, 0.07, 0.00, 0.00, 0.00, 0.05, 0.05, 0.05, - 0.10, 0.25, 0.10, 0.25, 0.25, 0.10, 0.10, 0.10, 0.25, 0.25, 0.25, 0.25, 0.00, 0.25, 0.00, 0.00, 0.00, 0.25, 0.25, 0.25, - - ! Row 1: Vis - ! Row 2: Near IR - TAUS = 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.220, 0.1105, 0.220, 0.000, 0.220, 0.000, 0.000, 0.000, 0.001, 0.001, 0.001, - 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.380, 0.1905, 0.380, 0.000, 0.380, 0.000, 0.000, 0.000, 0.001, 0.001, 0.001, - - XL = 0.010, 0.010, 0.010, 0.250, 0.250, 0.010, 0.010, 0.010, 0.010, -0.30, -0.025, -0.30, 0.000, -0.30, 0.000, 0.000, 0.000, 0.250, 0.250, 0.250, -! CWPVT = 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, - CWPVT = 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, - C3PSN = 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, - KC25 = 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, - AKC = 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, - KO25 = 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, 3.E4, - AKO = 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, - AVCMX = 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, 2.4, - AQE = 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, - - LTOVRC= 0.5, 0.55, 0.2, 0.55, 0.5, 0.65, 0.65, 0.65, 0.65, 0.50, 1.4, 1.6, 0.0, 1.2, 0.0, 0.0, 0.0, 1.3, 1.4, 1.0, - DILEFC= 1.20, 0.50, 1.80, 0.60, 0.80, 0.20, 0.20, 0.20, 0.50, 0.20, 0.4, 0.50, 0.00, 0.35, 0.00, 0.00, 0.00, 0.30, 0.40, 0.30, - DILEFW= 0.20, 4.00, 0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.50, 0.10, 0.2, 0.20, 0.00, 0.20, 0.00, 0.00, 0.00, 0.20, 0.20, 0.20, - RMF25 = 3.00, 0.65, 4.00, 3.00, 3.00, 0.26, 0.26, 0.26, 0.80, 1.80, 3.2, 1.00, 0.00, 1.45, 0.00, 0.00, 0.00, 3.00, 3.00, 3.00, - SLA = 80, 80, 80, 80, 80, 60, 60, 60, 50, 60, 80, 80, 60, 80, 0, 0, 0, 80, 80, 80, - FRAGR = 0.10, 0.20, 0.10, 0.20, 0.10, 0.20, 0.20, 0.20, 0.20, 0.20, 0.1, 0.20, 0.00, 0.20, 0.00, 0.10, 0.00, 0.10, 0.10, 0.10, - TMIN = 265, 273, 268, 273, 268, 273, 273, 273, 273, 273, 268, 273, 0, 273, 0, 0, 0, 268, 268, 268, - VCMX25= 50.0, 60.0, 60.0, 60.0, 55.0, 40.0, 40.0, 40.0, 40.0, 40.0, 50.0, 80.0, 0.00, 60.0, 0.00, 0.00, 0.00, 50.0, 50.0, 50.0, - TDLEF = 278, 278, 268, 278, 268, 278, 278, 278, 278, 278, 268, 278, 278, 278, 0, 0, 0, 268, 268, 268, - BP = 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 2.E3, 1.E15, 2.E3, 1.E15, 2.E3, 1.E15, 2.E3, 2.E3, 2.E3, - MP = 6., 9., 6., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., 9., - QE25 = 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.00, 0.06, 0.00, 0.06, 0.00, 0.06, 0.06, 0.06, - RMS25 = 0.90, 0.30, 0.64, 0.10, 0.80, 0.10, 0.10, 0.10, 0.32, 0.10, 0.10, 0.10, 0.00, 0.10, 0.00, 0.00, 0.00, 0.10, 0.10, 0.00, - RMR25 = 0.36, 0.05, 0.05, 0.01, 0.03, 0.00, 0.00, 0.00, 0.01, 1.20, 0.0, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 2.11, 2.11, 0.00, - ARM = 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, - FOLNMX= 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 0.00, 1.5, 0.00, 1.5, 0.00, 1.5, 1.5, 1.5, - WDPOOL= 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 0.00, 0.5, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 1.00, 1.00, 0.00, - WRRAT = 30.0, 30.0, 30.0, 30.0, 30.0, 3.00, 3.00, 3.00, 3.00, 0.00, 15.0, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 3.00, 3.00, 0.00, - MRP = 0.37, 0.23, 0.37, 0.40, 0.30, 0.19, 0.19, 0.19, 0.40, 0.17, 0.285, 0.23, 0.00, 0.23, 0.00, 0.00, 0.00, 0.23, 0.20, 0.00, - -! Monthly values, one row for each month: - SAIM = 0.4, 0.5, 0.3, 0.4, 0.4, 0.3, 0.2, 0.4, 0.3, 0.3, 0.3, 0.3, 0.0, 0.3, 0.0, 0.0, 0.0, 0.2, 0.1, 0.0, - 0.4, 0.5, 0.3, 0.4, 0.4, 0.3, 0.2, 0.4, 0.3, 0.3, 0.3, 0.3, 0.0, 0.3, 0.0, 0.0, 0.0, 0.2, 0.1, 0.0, - 0.4, 0.5, 0.3, 0.4, 0.4, 0.3, 0.2, 0.4, 0.3, 0.3, 0.3, 0.3, 0.0, 0.3, 0.0, 0.0, 0.0, 0.2, 0.1, 0.0, - 0.3, 0.5, 0.4, 0.4, 0.4, 0.3, 0.2, 0.4, 0.3, 0.3, 0.3, 0.3, 0.0, 0.3, 0.0, 0.0, 0.0, 0.2, 0.1, 0.0, - 0.4, 0.5, 0.4, 0.4, 0.4, 0.3, 0.2, 0.4, 0.3, 0.3, 0.3, 0.3, 0.0, 0.3, 0.0, 0.0, 0.0, 0.2, 0.1, 0.0, - 0.5, 0.5, 0.7, 0.4, 0.4, 0.3, 0.2, 0.4, 0.4, 0.4, 0.4, 0.3, 0.0, 0.4, 0.0, 0.0, 0.0, 0.2, 0.2, 0.0, - 0.5, 0.5, 1.3, 0.9, 0.7, 0.6, 0.4, 0.7, 0.8, 0.8, 0.6, 0.4, 0.0, 0.6, 0.0, 0.0, 0.0, 0.4, 0.4, 0.0, - 0.6, 0.5, 1.2, 1.2, 0.8, 0.9, 0.6, 1.2, 1.2, 1.3, 0.9, 0.5, 0.0, 0.9, 0.0, 0.0, 0.0, 0.6, 0.6, 0.0, - 0.6, 0.5, 1.0, 1.6, 1.0, 1.2, 0.8, 1.4, 1.3, 1.1, 0.9, 0.4, 0.0, 0.7, 0.0, 0.0, 0.0, 0.8, 0.7, 0.0, - 0.7, 0.5, 0.8, 1.4, 1.0, 0.9, 0.7, 1.1, 0.7, 0.4, 0.6, 0.3, 0.0, 0.3, 0.0, 0.0, 0.0, 0.7, 0.5, 0.0, - 0.6, 0.5, 0.6, 0.6, 0.5, 0.4, 0.3, 0.5, 0.4, 0.4, 0.4, 0.3, 0.0, 0.3, 0.0, 0.0, 0.0, 0.3, 0.3, 0.0, - 0.5, 0.5, 0.5, 0.4, 0.4, 0.3, 0.2, 0.4, 0.4, 0.4, 0.3, 0.3, 0.0, 0.3, 0.0, 0.0, 0.0, 0.2, 0.2, 0.0, - - LAIM = 4.0, 4.5, 0.0, 0.0, 2.0, 0.0, 0.0, 0.2, 0.3, 0.4, 0.2, 0.0, 0.0, 0.2, 0.0, 0.0, 0.0, 1.0, 0.6, 0.0, - 4.0, 4.5, 0.0, 0.0, 2.0, 0.0, 0.0, 0.2, 0.3, 0.5, 0.3, 0.0, 0.0, 0.3, 0.0, 0.0, 0.0, 1.0, 0.6, 0.0, - 4.0, 4.5, 0.0, 0.3, 2.2, 0.3, 0.2, 0.4, 0.5, 0.6, 0.3, 0.0, 0.0, 0.3, 0.0, 0.0, 0.0, 1.1, 0.7, 0.0, - 4.0, 4.5, 0.6, 1.2, 2.6, 0.9, 0.6, 1.0, 0.8, 0.7, 0.5, 0.0, 0.0, 0.4, 0.0, 0.0, 0.0, 1.3, 0.8, 0.0, - 4.0, 4.5, 1.2, 3.0, 3.5, 2.2, 1.5, 2.4, 1.8, 1.2, 1.5, 1.0, 0.0, 1.1, 0.0, 0.0, 0.0, 1.7, 1.2, 0.0, - 4.0, 4.5, 2.0, 4.7, 4.3, 3.5, 2.3, 4.1, 3.6, 3.0, 2.9, 2.0, 0.0, 2.5, 0.0, 0.0, 0.0, 2.1, 1.8, 0.0, - 4.0, 4.5, 2.6, 4.5, 4.3, 3.5, 2.3, 4.1, 3.8, 3.5, 3.5, 3.0, 0.0, 3.2, 0.0, 0.0, 0.0, 2.1, 1.8, 0.0, - 4.0, 4.5, 1.7, 3.4, 3.7, 2.5, 1.7, 2.7, 2.1, 1.5, 2.7, 3.0, 0.0, 2.2, 0.0, 0.0, 0.0, 1.8, 1.3, 0.0, - 4.0, 4.5, 1.0, 1.2, 2.6, 0.9, 0.6, 1.0, 0.9, 0.7, 1.2, 1.5, 0.0, 1.1, 0.0, 0.0, 0.0, 1.3, 0.8, 0.0, - 4.0, 4.5, 0.5, 0.3, 2.2, 0.3, 0.2, 0.4, 0.5, 0.6, 0.3, 0.0, 0.0, 0.3, 0.0, 0.0, 0.0, 1.1, 0.7, 0.0, - 4.0, 4.5, 0.2, 0.0, 2.0, 0.0, 0.0, 0.2, 0.3, 0.5, 0.3, 0.0, 0.0, 0.3, 0.0, 0.0, 0.0, 1.0, 0.6, 0.0, - 4.0, 4.5, 0.0, 0.0, 2.0, 0.0, 0.0, 0.2, 0.3, 0.4, 0.2, 0.0, 0.0, 0.2, 0.0, 0.0, 0.0, 1.0, 0.6, 0.0, - -! LAIM = 5.1, 3.3, 0.0, 1.9, 3.0, 1.0, 0.8, 0.5, 0.5, 0.7, 0.3, 1.8, 0.0, 2.4, 0.0, 0.0, 0.0, 0.6, 0.7, 0.0, -! 5.0, 3.6, 0.0, 1.9, 2.9, 1.0, 0.6, 1.0, 1.0, 0.7, 0.45, 1.9, 0.0, 2.6, 0.0, 0.0, 0.0, 0.4, 0.4, 0.0, -! 5.1, 4.4, 0.0, 2.1, 3.3, 1.0, 0.8, 1.8, 1.7, 1.1, 0.5, 2.6, 0.0, 2.9, 0.0, 0.0, 0.0, 0.4, 0.4, 0.0, -! 5.3, 5.4, 0.6, 2.5, 4.0, 1.0, 0.9, 2.6, 2.9, 1.7, 0.55, 3.9, 0.0, 3.4, 0.0, 0.0, 0.0, 0.4, 0.4, 0.0, -! 5.9, 6.2, 1.2, 3.1, 5.0, 1.0, 1.5, 3.4, 3.6, 2.5, 0.85, 5.2, 0.0, 4.0, 0.0, 0.0, 0.0, 0.8, 1.0, 0.0, -! 6.3, 6.4, 2.0, 3.3, 5.4, 1.0, 2.1, 3.6, 3.5, 2.7, 1.85, 5.6, 0.0, 4.2, 0.0, 0.0, 0.0, 2.0, 2.3, 0.0, -! 6.4, 5.9, 2.6, 3.3, 5.4, 1.0, 2.6, 3.4, 2.9, 2.8, 2.6, 5.3, 0.0, 4.1, 0.0, 0.0, 0.0, 3.3, 3.3, 0.0, -! 6.1, 5.6, 1.7, 3.1, 5.0, 1.0, 2.4, 3.2, 2.7, 2.4, 2.25, 4.5, 0.0, 3.8, 0.0, 0.0, 0.0, 3.3, 3.0, 0.0, -! 6.0, 5.3, 1.0, 2.9, 4.8, 1.0, 2.2, 2.9, 2.4, 2.1, 1.6, 4.1, 0.0, 3.7, 0.0, 0.0, 0.0, 2.8, 3.0, 0.0, -! 5.5, 4.7, 0.5, 2.6, 4.1, 1.0, 1.6, 2.3, 1.8, 1.7, 1.1, 3.2, 0.0, 3.2, 0.0, 0.0, 0.0, 1.4, 1.4, 0.0, -! 5.2, 4.0, 0.2, 2.2, 3.4, 1.0, 1.0, 1.5, 1.4, 1.3, 0.65, 2.3, 0.0, 2.7, 0.0, 0.0, 0.0, 0.5, 0.7, 0.0, -! 5.1, 3.2, 0.0, 1.9, 3.0, 1.0, 0.9, 0.7, 0.7, 0.8, 0.4, 1.7, 0.0, 2.4, 0.0, 0.0, 0.0, 0.8, 0.7, 0.0, - - SLAREA=0.0090, 0.0200, 0.0200, 0.0258, 0.0223, 0.0227, 0.0188, 0.0227, 0.0236, 0.0060, 0.0295, 0.0200, 0.0228, 0.0223, 0.02, 0.02, 0.0422, 0.02, 0.02, 0.02, - -! Five types, one row for each type. - EPS = 0.46, 0.00, 0.00, 46.86, 30.98, 21.62, 0.11, 21.62, 22.80, 0.02, 0.815, 0.00, 41.87, 0.04, 0.0, 0.0, 2.31, 0.0, 0.0, 0.0, - 3.34, 0.00, 0.00, 0.38, 0.96, 0.92, 0.22, 0.92, 0.59, 0.05, 0.535, 0.00, 0.98, 0.09, 0.0, 0.0, 1.47, 0.0, 0.0, 0.0, - 1.85, 0.00, 0.00, 1.84, 1.84, 1.73, 1.26, 1.73, 1.37, 0.03, 0.605, 0.00, 1.82, 0.05, 0.0, 0.0, 1.70, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -/ diff --git a/case_study/base_settings/SOILPARM.TBL b/case_study/base_settings/SOILPARM.TBL deleted file mode 100644 index cfb0d545e..000000000 --- a/case_study/base_settings/SOILPARM.TBL +++ /dev/null @@ -1,59 +0,0 @@ -Soil Parameters -STAS -19,1 'BB DRYSMC F11 MAXSMC REFSMC SATPSI SATDK SATDW WLTSMC QTZ ' -1, 2.79, 0.010, -0.472, 0.339, 0.236, 0.069, 4.66E-5, 0.608E-6, 0.010, 0.92, 'SAND' -2, 4.26, 0.028, -1.044, 0.421, 0.383, 0.036, 1.41E-5, 0.514E-5, 0.028, 0.82, 'LOAMY SAND' -3, 4.74, 0.047, -0.569, 0.434, 0.383, 0.141, 5.23E-6, 0.805E-5, 0.047, 0.60, 'SANDY LOAM' -4, 5.33, 0.084, 0.162, 0.476, 0.360, 0.759, 2.81E-6, 0.239E-4, 0.084, 0.25, 'SILT LOAM' -5, 5.33, 0.084, 0.162, 0.476, 0.383, 0.759, 2.81E-6, 0.239E-4, 0.084, 0.10, 'SILT' -6, 5.25, 0.066, -0.327, 0.439, 0.329, 0.355, 3.38E-6, 0.143E-4, 0.066, 0.40, 'LOAM' -7, 6.77, 0.067, -1.491, 0.404, 0.314, 0.135, 4.45E-6, 0.990E-5, 0.067, 0.60, 'SANDY CLAY LOAM' -8, 8.72, 0.120, -1.118, 0.464, 0.387, 0.617, 2.03E-6, 0.237E-4, 0.120, 0.10, 'SILTY CLAY LOAM' -9, 8.17, 0.103, -1.297, 0.465, 0.382, 0.263, 2.45E-6, 0.113E-4, 0.103, 0.35, 'CLAY LOAM' -10, 10.73, 0.100, -3.209, 0.406, 0.338, 0.098, 7.22E-6, 0.187E-4, 0.100, 0.52, 'SANDY CLAY' -11, 10.39, 0.126, -1.916, 0.468, 0.404, 0.324, 1.34E-6, 0.964E-5, 0.126, 0.10, 'SILTY CLAY' -12, 11.55, 0.138, -2.138, 0.468, 0.412, 0.468, 9.74E-7, 0.112E-4, 0.138, 0.25, 'CLAY' -13, 5.25, 0.066, -0.327, 0.439, 0.329, 0.355, 3.38E-6, 0.143E-4, 0.066, 0.05, 'ORGANIC MATERIAL' -14, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.60, 'WATER' -15, 2.79, 0.006, -1.111, 0.20, 0.17, 0.069, 1.41E-4, 0.136E-3, 0.006, 0.07, 'BEDROCK' -16, 4.26, 0.028, -1.044, 0.421, 0.283, 0.036, 1.41E-5, 0.514E-5, 0.028, 0.25, 'OTHER(land-ice)' -17, 11.55, 0.030, -10.472, 0.468, 0.454, 0.468, 9.74E-7, 0.112E-4, 0.030, 0.60, 'PLAYA' -18, 2.79, 0.006, -0.472, 0.200, 0.17, 0.069, 1.41E-4, 0.136E-3, 0.006, 0.52, 'LAVA' -19, 2.79, 0.01, -0.472, 0.339, 0.236, 0.069, 4.66E-5, 0.608E-6, 0.01, 0.92, 'WHITE SAND' -Soil Parameters -STAS-RUC -19,1 'BB DRYSMC HC MAXSMC REFSMC SATPSI SATDK SATDW WLTSMC QTZ ' -1, 4.05, 0.045, 1.47, 0.395, 0.174, 0.121, 1.76E-4, 0.608E-6, 0.068, 0.92, 'SAND' -2, 4.38, 0.057, 1.41, 0.410, 0.179, 0.090, 1.56E-4, 0.514E-5, 0.075, 0.82, 'LOAMY SAND' -3, 4.90, 0.065, 1.34, 0.435, 0.249, 0.218, 3.47E-5, 0.805E-5, 0.114, 0.60, 'SANDY LOAM' -4, 5.30, 0.067, 1.27, 0.485, 0.369, 0.786, 7.20E-6, 0.239E-4, 0.179, 0.25, 'SILT LOAM' -5, 5.30, 0.034, 1.27, 0.485, 0.369, 0.786, 7.20E-6, 0.239E-4, 0.179, 0.10, 'SILT' -6, 5.39, 0.078, 1.21, 0.451, 0.314, 0.478, 6.95E-6, 0.143E-4, 0.155, 0.40, 'LOAM' -7, 7.12, 0.100, 1.18, 0.420, 0.299, 0.299, 6.30E-6, 0.990E-5, 0.175, 0.60, 'SANDY CLAY LOAM' -8, 7.75, 0.089, 1.32, 0.477, 0.357, 0.356, 1.70E-6, 0.237E-4, 0.218, 0.10, 'SILTY CLAY LOAM' -9, 8.52, 0.095, 1.23, 0.476, 0.391, 0.630, 2.45E-6, 0.113E-4, 0.250, 0.35, 'CLAY LOAM' -10, 10.40, 0.100, 1.18, 0.426, 0.316, 0.153, 2.17E-6, 0.187E-4, 0.219, 0.52, 'SANDY CLAY' -11, 10.40, 0.070, 1.15, 0.492, 0.409, 0.490, 1.03E-6, 0.964E-5, 0.283, 0.10, 'SILTY CLAY' -12, 11.40, 0.068, 1.09, 0.482, 0.400, 0.405, 1.28E-6, 0.112E-4, 0.286, 0.25, 'CLAY' -13, 5.39, 0.078, 1.21, 0.451, 0.314, 0.478, 6.95E-6, 0.143E-4, 0.155, 0.05, 'ORGANIC MATERIAL' -14, 0.0, 0.0, 4.18, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.00, 'WATER' -15, 4.05, 0.004, 2.03, 0.200, 0.10 , 0.121, 1.41E-4, 0.136E-3, 0.006, 0.60, 'BEDROCK' -16, 4.90, 0.065, 2.10, 0.435, 0.249, 0.218, 3.47E-5, 0.514E-5, 0.114, 0.05, 'OTHER(land-ice)' -17, 11.40, 0.030, 1.41, 0.468, 0.454, 0.468, 9.74E-7, 0.112E-4, 0.030, 0.60, 'PLAYA' -18, 4.05, 0.006, 1.41, 0.200, 0.17, 0.069, 1.41E-4, 0.136E-3, 0.060, 0.52, 'LAVA' -19, 4.05, 0.01, 1.47, 0.339, 0.236, 0.069, 1.76E-4, 0.608E-6, 0.060, 0.92, 'WHITE SAND' -Soil Parameters -ROSETTA -12,1 'theta_res theta_sat vGn_alpha vGn_n k_soil BB DRYSMC HC MAXSMC REFSMC SATPSI SATDK SATDW WLTSMC QTZ ' -1 0.098 0.459 -1.496 1.253 1.70799e-06 11.40 0.068 1.09 0.482 0.412 0.405 1.28E-6 0.112E-4 0.286 0.25 'CLAY' -2 0.079 0.442 -1.581 1.416 9.47297e-07 8.52 0.095 1.23 0.476 0.382 0.630 2.45E-6 0.113E-4 0.250 0.35 'CLAY LOAM' -3 0.061 0.399 -1.112 1.472 1.39472e-06 5.39 0.078 1.21 0.451 0.329 0.478 6.95E-6 0.143E-4 0.155 0.40 'LOAM' -4 0.049 0.390 -3.475 1.746 1.21755e-05 4.38 0.057 1.41 0.410 0.383 0.090 1.56E-4 0.514E-5 0.075 0.82 'LOAMY SAND' -5 0.053 0.375 -3.524 3.177 7.43852e-05 4.05 0.045 1.47 0.395 0.236 0.121 1.76E-4 0.608E-6 0.068 0.92 'SAND' -6 0.117 0.385 -3.342 1.208 1.31367e-06 10.40 0.100 1.18 0.426 0.338 0.153 2.17E-6 0.187E-4 0.219 0.52 'SANDY CLAY' -7 0.063 0.384 -2.109 1.330 1.52576e-06 7.12 0.100 1.18 0.420 0.314 0.299 6.30E-6 0.990E-5 0.175 0.60 'SANDY CLAY LOAM' -8 0.039 0.387 -2.667 1.449 4.43084e-06 4.90 0.065 1.34 0.435 0.383 0.218 3.47E-5 0.805E-5 0.114 0.60 'SANDY LOAM' -9 0.050 0.489 -0.658 1.679 5.06391e-06 5.30 0.034 1.27 0.485 0.383 0.786 7.20E-6 0.239E-4 0.179 0.10 'SILT' -10 0.111 0.481 -1.622 1.321 1.11298e-06 10.40 0.070 1.15 0.492 0.404 0.490 1.03E-6 0.964E-5 0.283 0.10 'SILTY CLAY' -11 0.090 0.482 -0.839 1.521 1.28673e-06 7.75 0.089 1.32 0.477 0.387 0.356 1.70E-6 0.237E-4 0.218 0.10 'SILTY CLAY LOAM' -12 0.065 0.439 -0.506 1.663 2.11099e-06 5.30 0.067 1.27 0.485 0.360 0.786 7.20E-6 0.239E-4 0.179 0.25 'SILT LOAM' diff --git a/case_study/base_settings/VEGPARM.TBL b/case_study/base_settings/VEGPARM.TBL deleted file mode 100644 index 2d53237ae..000000000 --- a/case_study/base_settings/VEGPARM.TBL +++ /dev/null @@ -1,211 +0,0 @@ -Vegetation Parameters -USGS -27,1, 'SHDFAC NROOT RS RGL HS SNUP MAXALB LAIMIN LAIMAX EMISSMIN EMISSMAX ALBEDOMIN ALBEDOMAX Z0MIN Z0MAX ZTOPV ZBOTV' -1, .10, 1, 200., 999., 999.0, 0.04, 46., 1.00, 1.00, .880, .880, .15, .15, .50, .50, 0.00, 0.00, 'Urban and Built-Up Land' -2, .80, 3, 40., 100., 36.25, 0.04, 66., 1.56, 5.68, .920, .985, .17, .23, .05, .15, 0.50, 0.01, 'Dryland Cropland and Pasture' -3, .80, 3, 40., 100., 36.25, 0.04, 66., 1.56, 5.68, .930, .985, .20, .25, .02, .10, 0.50, 0.01, 'Irrigated Cropland and Pasture' -4, .80, 3, 40., 100., 36.25, 0.04, 66., 1.00, 4.50, .920, .985, .18, .23, .05, .15, 0.50, 0.01, 'Mixed Dryland/Irrigated Cropland and Pasture' -5, .80, 3, 40., 100., 36.25, 0.04, 68., 2.29, 4.29, .920, .980, .18, .23, .05, .14, 0.50, 0.01, 'Cropland/Grassland Mosaic' -6, .80, 3, 70., 65., 44.14, 0.04, 60., 2.00, 4.00, .930, .985, .16, .20, .20, .20, 0.50, 0.01, 'Cropland/Woodland Mosaic' -7, .80, 3, 40., 100., 36.35, 0.04, 70., 0.52, 2.90, .920, .960, .19, .23, .10, .12, 0.50, 0.01, 'Grassland' -8, .70, 3, 300., 100., 42.00, 0.03, 60., 0.50, 3.66, .930, .930, .25, .30, .01, .05, 0.50, 0.10, 'Shrubland' -9, .70, 3, 170., 100., 39.18, 0.035, 65., 0.60, 2.60, .930, .950, .22, .30, .01, .06, 0.50, 0.10, 'Mixed Shrubland/Grassland' -10, .50, 3, 70., 65., 54.53, 0.04, 50., 0.50, 3.66, .920, .920, .20, .20, .15, .15, 5.00, 0.10, 'Savanna' -11, .80, 4, 100., 30., 54.53, 0.08, 58., 1.85, 3.31, .930, .930, .16, .17, .50, .50, 20.0, 11.5, 'Deciduous Broadleaf Forest' -12, .70, 4, 150., 30., 47.35, 0.08, 54., 1.00, 5.16, .930, .940, .14, .15, .50, .50, 14.0, 7.0, 'Deciduous Needleleaf Forest' -13, .95, 4, 150., 30., 41.69, 0.08, 35., 3.08, 6.48, .950, .950, .12, .12, .50, .50, 35.0, 1.0, 'Evergreen Broadleaf Forest' -14, .70, 4, 125., 30., 47.35, 0.08, 52., 5.00, 6.40, .950, .950, .12, .12, .50, .50, 17.0, 8.5, 'Evergreen Needleleaf Forest' -15, .80, 4, 125., 30., 51.93, 0.08, 53., 2.80, 5.50, .930, .970, .17, .25, .20, .50, 18.0, 10.0, 'Mixed Forest' -16, .00, 0, 100., 30., 51.75, 0.01, 70., 0.01, 0.01, .980, .980, .08, .08, 0.0001, 0.0001, 0.00, 0.00, 'Water Bodies' -17, .60, 2, 40., 100., 60.00, 0.01, 68., 1.50, 5.65, .950, .950, .14, .14, .20, .20, 0.50, 0.01, 'Herbaceous Wetland' -18, .60, 2, 100., 30., 51.93, 0.02, 50., 2.00, 5.80, .950, .950, .14, .14, .40, .40, 20.0, 11.5, 'Wooded Wetland' -19, .01, 1, 999., 999., 999.0, 0.02, 75., 0.10, 0.75, .900, .900, .38, .38, .01, .01, 0.02, 0.01, 'Barren or Sparsely Vegetated' -20, .60, 3, 150., 100., 42.00, 0.025, 68., 0.41, 3.35, .920, .920, .15, .20, .10, .10, 0.50, 0.01, 'Herbaceous Tundra' -21, .60, 3, 150., 100., 42.00, 0.025, 55., 0.41, 3.35, .930, .930, .15, .20, .30, .30, 10.0, 0.10, 'Wooded Tundra' -22, .60, 3, 150., 100., 42.00, 0.025, 60., 0.41, 3.35, .920, .920, .15, .20, .15, .15, 5.00, 0.10, 'Mixed Tundra' -23, .30, 2, 200., 100., 42.00, 0.02, 75., 0.41, 3.35, .900, .900, .25, .25, .05, .10, 0.02, 0.01, 'Bare Ground Tundra' -24, .00, 1, 999., 999., 999.0, 0.02, 82., 0.01, 0.01, .950, .950, .55, .70, 0.001, 0.001, 0.00, 0.00, 'Snow or Ice' -25, .50, 1, 40., 100., 36.25, 0.02, 75., 0.01, 0.01, .890, .890, .30, .30, .01, .01, 0.00, 0.00, 'Playa' -26, .00, 0, 999., 999., 999.0, 0.02, 75., 0.01, 0.01, .880, .880, .16, .16, .15, .15, 0.00, 0.00, 'Lava' -27, .00, 0, 999., 999., 999.0, 0.02, 75., 0.01, 0.01, .830, .830, .60, .60, .01, .01, 0.00, 0.00, 'White Sand' -TOPT_DATA -298.0 -CMCMAX_DATA -0.5E-3 -CFACTR_DATA -0.5 -RSMAX_DATA -5000.0 -BARE -19 -NATURAL -5 -Vegetation Parameters -MODIFIED_IGBP_MODIS_NOAH -20,1, 'SHDFAC NROOT RS RGL HS SNUP MAXALB LAIMIN LAIMAX EMISSMIN EMISSMAX ALBEDOMIN ALBEDOMAX Z0MIN Z0MAX ZTOPV ZBOTV' -1 .70, 4, 125., 30., 47.35, 0.08, 52., 5.00, 6.40, .950, .950, .12, .12, .50, .50, 17.0, 8.5, 'Evergreen Needleleaf Forest' -2, .95, 4, 150., 30., 41.69, 0.08, 35., 3.08, 6.48, .950, .950, .12, .12, .50, .50, 35.0, 1.0, 'Evergreen Broadleaf Forest' -3, .70, 4, 150., 30., 47.35, 0.08, 54., 1.00, 5.16, .930, .940, .14, .15, .50, .50, 14.0, 7.0, 'Deciduous Needleleaf Forest' -4, .80, 4, 100., 30., 54.53, 0.08, 58., 1.85, 3.31, .930, .930, .16, .17, .50, .50, 20.0, 11.5, 'Deciduous Broadleaf Forest' -5, .80, 4, 125., 30., 51.93, 0.08, 53., 2.80, 5.50, .930, .970, .17, .25, .20, .50, 18.0, 10.0, 'Mixed Forests' -6, .70, 3, 300., 100., 42.00, 0.03, 60., 0.50, 3.66, .930, .930, .25, .30, .01, .05, 0.50, 0.10, 'Closed Shrublands' -7, .70, 3, 170., 100., 39.18, 0.035, 65., 0.60, 2.60, .930, .950, .22, .30, .01, .06, 0.50, 0.10, 'Open Shrublands' -8, .70, 3, 300., 100., 42.00, 0.03, 60., 0.50, 3.66, .930, .930, .25, .30, .01, .05, 0.50, 0.10, 'Woody Savannas' -9, .50, 3, 70., 65., 54.53, 0.04, 50., 0.50, 3.66, .920, .920, .20, .20, .15, .15, 0.50, 0.10, 'Savannas' -10, .80, 3, 40., 100., 36.35, 0.04, 70., 0.52, 2.90, .920, .960, .19, .23, .10, .12, 0.50, 0.01, 'Grasslands' -11 .60, 2, 70., 65., 55.97 0.015 59., 1.75, 5.72, .950, .950, .14, .14, .30, .30, 0.00, 0.00, 'Permanent wetlands' -12, .80, 3, 40., 100., 36.25, 0.04, 66., 1.56, 5.68, .920, .985, .17, .23, .05, .15, 0.50, 0.01, 'Croplands' -13, .10, 1, 200., 999., 999.0, 0.04, 46., 1.00, 1.00, .880, .880, .15, .15, .50, .50, 0.00, 0.00, 'Urban and Built-Up' -14 .80, 3, 40., 100., 36.25, 0.04, 68., 2.29, 4.29, .920, .980, .18, .23, .05, .14, 0.50, 0.01, 'cropland/natural vegetation mosaic' -15, .00, 1, 999., 999., 999.0, 0.02, 82., 0.01, 0.01, .950, .950, .55, .70, 0.001, 0.001, 0.00, 0.00, 'Snow and Ice' -16, .01, 1, 999., 999., 999.0, 0.02, 75., 0.10, 0.75, .900, .900, .38, .38, .01, .01, 0.02, 0.01, 'Barren or Sparsely Vegetated' -17, .00, 0, 100., 30., 51.75, 0.01, 70., 0.01, 0.01, .980, .980, .08, .08, 0.0001, 0.0001, 0.00, 0.00, 'Water' -18, .60, 3, 150., 100., 42.00, 0.025, 55., 0.41, 3.35, .930, .930, .15, .20, .30, .30, 10.0, 0.10, 'Wooded Tundra' -19, .60, 3, 150., 100., 42.00, 0.025, 60., 0.41, 3.35, .920, .920, .15, .20, .15, .15, 5.00, 0.10, 'Mixed Tundra' -20, .30, 2, 200., 100., 42.00, 0.02, 75., 0.41, 3.35, .900, .900, .25, .25, .05, .10, 0.02, 0.01, 'Barren Tundra' -TOPT_DATA -298.0 -CMCMAX_DATA -0.5E-3 -CFACTR_DATA -0.5 -RSMAX_DATA -5000.0 -BARE -16 -NATURAL -14 -Vegetation Parameters -USGS-RUC -28,1, 'ALBEDO Z0 LEMI PC SHDFAC IFOR RS RGL HS SNUP LAI MAXALB' -1, .18, 2.0, .88, .40, .10, 9, 200., 999., 999.0, 0.04, 1.00, 40., 'Urban and Built-Up Land' -2, .17, .06, .92, .30, .80, 7, 40., 100., 36.25, 0.04, 5.68, 64., 'Dryland Cropland and Pasture' -3, .18, .075, .92, .40, .80, 7, 40., 100., 36.25, 0.04, 5.68, 64., 'Irrigated Cropland and Pasture' -4, .18, .125, .92, .40, .80, 7, 40., 100., 36.25, 0.04, 4.50, 64., 'Mixed Dryland/Irrigated Cropland and Pasture' -5, .18, .15, .92, .40, .80, 3, 40., 100., 36.25, 0.04, 4.29, 64., 'Cropland/Grassland Mosaic' -6, .16, .20, .93, .40, .80, 3, 70., 65., 44.14, 0.04, 4.00, 60., 'Cropland/Woodland Mosaic' -7, .19, .075 .92, .40, .80, 5, 40., 100., 36.35, 0.04, 2.90, 64., 'Grassland' -8, .22, .10, .88, .40, .70, 4, 300., 100., 42.00, 0.03, 3.66, 69., 'Shrubland' -9, .20, .11, .90, .40, .70, 4, 170., 100., 39.18, 0.035, 2.60, 67., 'Mixed Shrubland/Grassland' -10, .20, .15, .92, .40, .50, 5, 70., 65., 54.53, 0.04, 3.66, 45., 'Savanna' -11, .16, .70, .93, .55, .80, 3, 100., 30., 54.53, 0.08, 3.31, 58., 'Deciduous Broadleaf Forest' -12, .14, .70, .94, .55, .70, 4, 150., 30., 47.35, 0.08, 5.16, 54., 'Deciduous Needleleaf Forest' -13, .12, .70, .95, .55, .95, 2, 150., 30., 41.69, 0.08, 6.48, 32., 'Evergreen Broadleaf Forest' -14, .12, .70, .95, .55, .70, 1, 125., 30., 47.35, 0.08, 6.40, 52., 'Evergreen Needleleaf Forest' -15, .13, .70, .94, .55, .80, 2, 125., 30., 51.93, 0.08, 5.50, 53., 'Mixed Forest' -16, .08, .0001, .98, .00, .00, 9, 100., 30., 51.75, 0.01, 0.01, 70., 'Water Bodies' -17, .14, .20, .95, .55, .60, 4, 40., 100., 60.00, 0.01, 5.65, 35., 'Herbaceous Wetland' -18, .14, .40, .95, .55, .60, 4, 100., 30., 51.93, 0.02, 5.80, 30., 'Wooded Wetland' -19, .25, .05, .85, .30, .01, 5, 999., 999., 999.0, 0.02, 0.75, 69., 'Barren or Sparsely Vegetated' -20, .15, .10, .92, .30, .60, 5, 150., 100., 42.00, 0.025, 3.35, 58., 'Herbaceous Tundra' -21, .15, .15, .93, .40, .60, 5, 150., 100., 42.00, 0.025, 3.35, 55., 'Wooded Tundra' -22, .15, .10, .92, .40, .60, 5, 150., 100., 42.00, 0.025, 3.35, 55., 'Mixed Tundra' -23, .25, .065, .85, .30, .30, 5, 200., 100., 42.00, 0.02, 3.35, 65., 'Bare Ground Tundra' -24, .55, .0024, .98, .00, .00, 9, 999., 999., 999.0, 0.02, 0.01, 75., 'Snow or Ice' -25, .30, .01, .85, .30, .50, 9, 40., 100., 36.25, 0.02, 0.01, 69., 'Playa' -26, .16, .15, .85, .00, .00, 9, 999., 999., 999.0, 0.02, 0.01, 69., 'Lava' -27, .60, .01, .90, .00, .00, 9, 999., 999., 999.0, 0.02, 0.01, 69., 'White Sand' -28, .08, .0001, .98, .00, .00, 9, 100., 30., 51.75, 0.01, 0.01, 70., 'Lakes' -TOPT_DATA -298.0 -CMCMAX_DATA -0.2E-3 -CFACTR_DATA -0.5 -RSMAX_DATA -5000.0 -BARE -19 -NATURAL -5 -Vegetation Parameters -MODI-RUC -21,1, 'ALBEDO Z0 LEMI PC SHDFAC IFOR RS RGL HS SNUP LAI MAXALB' -1 .12, .70, .950, .55, .70, 1, 125., 30., 47.35, 0.08, 6.40, 52., 'Evergreen Needleleaf Forest' -2, .12, .70, .950, .55, .95, 2, 150., 30., 41.69, 0.08, 6.48, 35., 'Evergreen Broadleaf Forest' -3, .14, .70, .940, .55, .70, 4, 150., 30., 47.35, 0.08, 5.16, 54., 'Deciduous Needleleaf Forest' -4, .16, .70, .930, .55, .80, 3, 100., 30., 54.53, 0.08, 3.31, 58., 'Deciduous Broadleaf Forest' -5, .13, .70, .940, .55, .80, 2, 125., 30., 51.93, 0.08, 5.50, 53., 'Mixed Forests' -6, .22, .10, .930, .40, .70, 4, 300., 100., 42.00, 0.03, 3.66, 60., 'Closed Shrublands' -7, .20, .10, .880, .40, .70, 4, 170., 100., 39.18, 0.035, 2.60, 65., 'Open Shrublands' -8, .20, .15, .930, .40, .70, 5, 300., 100., 42.00, 0.03, 3.66, 60., 'Woody Savannas' -9, .20, .15, .920, .40, .50, 5, 70., 65., 54.53, 0.04, 3.66, 50., 'Savannas' -10, .19, .075, .920, .40, .80, 5, 40., 100., 36.35, 0.04, 2.90, 70., 'Grasslands' -11 .14, .30, .950, .40, .60, 4, 70., 65., 55.97 0.015 5.72, 59., 'Permanent wetlands' -12, .18, .15, .935, .40, .80, 7, 40., 100., 36.25, 0.04, 5.68, 66., 'Croplands' -13, .18, 2.0, .880, .40, .10, 9, 200., 999., 999.0, 0.04, 1.00, 46., 'Urban and Built-Up' -14 .16, .14, .920, .40, .80, 7, 40., 100., 36.25, 0.04, 4.29, 68., 'cropland/natural vegetation mosaic' -15, .55, .011, .980, .00, .00, 9, 999., 999., 999.0, 0.02, 0.01, 82., 'Snow and Ice' -16, .25, .065, .850, .30, .01, 5, 999., 999., 999.0, 0.02, 0.75, 75., 'Barren or Sparsely Vegetated' -17, .08, .0001, .980, .00, .00, 9, 100., 30., 51.75, 0.01, 0.01, 70., 'Water' -18, .15, .15, .930, .40, .60, 5, 150., 100., 42.00, 0.025, 3.35, 55., 'Wooded Tundra' -19, .15, .10, .920, .40, .60, 5, 150., 100., 42.00, 0.025, 3.35, 60., 'Mixed Tundra' -20, .15, .06, .900, .30, .30, 5, 200., 100., 42.00, 0.02, 3.35, 75., 'Barren Tundra' -21, .08, .0001, .980, .00, .00, 9, 100., 30., 51.75, 0.01, 0.01, 70., 'Lakes' -TOPT_DATA -298.0 -CMCMAX_DATA -0.2E-3 -CFACTR_DATA -0.5 -RSMAX_DATA -5000.0 -BARE -16 -NATURAL -14 -Vegetation Parameters -NLCD40 -40,1, 'SHDFAC NROOT RS RGL HS SNUP MAXALB LAIMIN LAIMAX EMISSMIN EMISSMAX ALBEDOMIN ALBEDOMAX Z0MIN Z0MAX ZTOPV ZBOTV' -1, .70, 4, 125., 30., 47.35, 0.08, 52., 5.00, 6.40, .950, .950, .12, .12, .50, .50, 17.00, 8.50, 'Evergreen Needleleaf Forest' -2, .95, 4, 150., 30., 41.69, 0.08, 35., 3.08, 6.48, .950, .950, .12, .12, .50, .50, 35.00, 1.00, 'Evergreen Broadleaf Forest' -3, .70, 4, 150., 30., 47.35, 0.08, 54., 1.00, 5.16, .930, .940, .14, .15, .50, .50, 14.00, 7.00, 'Deciduous Needleleaf Forest' -4, .80, 4, 100., 30., 54.53, 0.08, 58., 1.85, 3.31, .930, .930, .16, .17, .50, .50, 20.00, 11.50, 'Deciduous Broadleaf Forest' -5, .80, 4, 125., 30., 51.93, 0.08, 53., 2.80, 5.50, .930, .970, .17, .25, .20, .50, 18.00, 10.00, 'Mixed Forest' -6, .70, 3, 300., 100., 42.00, 0.03, 60., 0.50, 3.66, .930, .930, .25, .30, .01, .05, 0.50, 0.10, 'Closed Shrubland' -7, .70, 3, 170., 100., 39.18, 0.035, 65., 0.60, 2.60, .930, .950, .22, .30, .01, .06, 0.50, 0.10, 'Open Shrubland' -8, .50, 3, 70., 65., 54.53, 0.04, 50., 0.50, 3.66, .930, .930, .25, .30, .01, .05, 0.00, 0.00, 'Woody Savanna' -9, .50, 3, 70., 65., 54.53, 0.04, 50., 0.50, 3.66, .920, .920, .20, .20, .15, .15, 0.50, 0.10, 'Savanna' -10, .80, 3, 40., 100., 36.35, 0.04, 70., 0.52, 2.90, .920, .960, .19, .23, .10, .12, 0.50, 0.10, 'Grassland' -11, .60, 2, 100., 30., 51.93, 0.02, 50., 1.75, 5.72, .950, .950, .14, .14, .30, .30, 0.50, 0.10, 'Permanent Wetland' -12, .80, 3, 40., 100., 36.25, 0.04, 66., 1.50, 5.68, .920, .985, .15, .23, .05, .15, 0.50, 0.10, 'Cropland' -13, .10, 1, 200., 999., 999.0, 0.04, 46., 1.00, 1.00, .880, .880, .15, .15, .50, .50, 0.00, 0.00, 'Urban and Built-Up' -14, .80, 3, 40., 100., 36.25, 0.04, 66., 2.29, 4.29, .920, .980, .18, .23, .05, .14, 0.50, 0.10, 'Cropland / Natural Veg. Mosaic' -15, .00, 1, 999., 999., 999.0, 0.02, 82., 0.01, 0.01, .950, .950, .55, .70, 0.001, 0.001, 0.00, 0.00, 'Permanent Snow' -16, .01, 1, 999., 999., 999.0, 0.02, 75., 0.10, 0.75, .900, .900, .38, .38, .01, .01, 0.02, 0.01, 'Barren / Sparsely Vegetated' -17, .00, 0, 100., 30., 51.75, 0.01, 70., 0.01, 0.01, .980, .980, .08, .08, 0.0001, 0.0001, 0.00, 0.00, 'IGBP Water' -18, .00, 0, 999., 999., 999.0, 999., 999., 999.0, 999.0, 999., 999.0, 999.0, 999.0, 999.0, 999.0, 0.00, 0.00, 'Unclassified' -19, .00, 0, 999., 999., 999.0, 999., 999., 999.0, 999.0, 999., 999.0, 999.0, 999.0, 999.0, 999.0, 0.00, 0.00, 'Fill Value' -20, .00, 0, 999., 999., 999.0, 999., 999., 999.0, 999.0, 999., 999.0, 999.0, 999.0, 999.0, 999.0, 0.00, 0.00, 'Unclassified' -21, .00, 0, 100., 30., 51.75, 0.01, 70., 0.01, 0.01, .980, .980, .08, .08, 0.0001, 0.0001, 0.00, 0.00, 'Open Water' -22, .00, 1, 999., 999., 999.0, 0.02, 82., 0.01, 0.01, .950, .950, .55, .70, 0.001, 0.001, 0.00, 0.00, 'Perennial Ice/Snow' -23, .30, 1, 200., 999., 999.0, 0.04, 46., 1.00, 1.00, .880, .880, .20, .20, .50, .50, 0.00, 0.00, 'Developed Open Space' -24, .27, 1, 200., 999., 999.0, 0.04, 46., 1.00, 1.00, .880, .880, .20, .20, .70, .70, 0.00, 0.00, 'Developed Low Intensity' -25, .02, 1, 200., 999., 999.0, 0.04, 46., 1.00, 1.00, .880, .880, .20, .20, 1.5, 1.5, 0.00, 0.00, 'Developed Medium Intensity' -26, .11, 1, 200., 999., 999.0, 0.04, 46., 1.00, 1.00, .880, .880, .20, .20, 2.0, 2.0, 0.00, 0.00, 'Developed High Intensity' -27, .01, 1, 999., 999., 999.0, 0.02, 75., 0.10, 0.75, .900, .900, .38, .38, .01, .01, 0.02, 0.01, 'Barren Land' -28, .80, 4, 125., 30., 54.70, 0.08, 56., 1.00, 5.16, .930, .940, .14, .17, .50, .50, 20.00, 11.50, 'Deciduous Forest' -29, .95, 4, 140., 30., 44.00, 0.08, 42., 3.08, 6.48, .950, .950, .12, .12, .50, .50, 17.00, 8.50, 'Evergreen Forest' -30, .80, 4, 125., 30., 51.93, 0.08, 53., 2.80, 5.50, .930, .970, .17, .25, .20, .50, 18.00, 10.00, 'Mixed Forest' -31, .70, 3, 170., 100., 39.18, 0.035, 65., 1.00, 4.00, .930, .950, .16, .30, .01, .04, 0.50, 0.10, 'Dwarf Scrub' -32, .70, 3, 300., 100., 42.00, 0.03, 60., 0.50, 3.66, .930, .930, .22, .30, .01, .05, 0.50, 0.10, 'Shrub/Scrub' -33, .80, 3, 40., 100., 36.35, 0.04, 70., 0.52, 2.90, .920, .960, .19, .23, .10, .12, 0.50, 0.10, 'Grassland/Herbaceous' -34, .60, 2, 40., 100., 60.00, 0.01, 68., 1.50, 5.65, .950, .950, .14, .14, .20, .20, 0.50, 0.10, 'Sedge/Herbaceous' -35, .60, 2, 40., 100., 60.00, 0.01, 68., 1.00, 2.00, .950, .950, .31, .31, .01, .01, 0.00, 0.00, 'Lichens' -36, .60, 2, 40., 100., 60.00, 0.01, 68., 1.00, 2.00, .950, .950, .24, .24, .01, .01, 0.00, 0.00, 'Moss' -37, .80, 3, 40., 100., 36.25, 0.04, 66., 1.56, 5.68, .920, .985, .17, .23, .05, .15, 0.50, 0.10, 'Pasture/Hay' -38, .80, 3, 40., 100., 36.25, 0.04, 66., 1.56, 5.68, .930, .985, .20, .25, .02, .10, 0.50, 0.10, 'Cultivated Crops' -39, .60, 2, 100., 30., 51.93, 0.02, 50., 0.70, 3.50, .950, .950, .14, .14, .40, .40, 20.00, 11.50, 'Woody Wetland' -40, .60, 2, 40., 100., 60.00, 0.01, 68., 0.70, 3.50, .950, .950, .12, .12, .20, .20, 0.50, 0.10, 'Emergent Herbaceous Wetland' -TOPT_DATA -298.0 -CMCMAX_DATA -0.5E-3 -CFACTR_DATA -0.5 -RSMAX_DATA -5000.0 -BARE -16 -NATURAL -14 diff --git a/case_study/base_settings/localParamInfo.txt b/case_study/base_settings/localParamInfo.txt deleted file mode 100644 index e5a3db595..000000000 --- a/case_study/base_settings/localParamInfo.txt +++ /dev/null @@ -1,245 +0,0 @@ -! File provenance -! --------------- -! 2021-08-10: taken from the SUMMA test cases v3 distribution (WRR figures) and included as case study default file -! -! -! ======================================================================================================================= -! ======================================================================================================================= -! ===== DEFINITION OF MODEL PARAMETERS ================================================================================== -! ======================================================================================================================= -! ======================================================================================================================= -! Note: lines starting with "!" are treated as comment lines -- there is no limit on the number of comment lines. -! -! ======================================================================================================================= -! DEFINE SITE MODEL PARAMETERS -! ------------------------------------ -! the format definition defines the format of the file, which can be changed -! the delimiters "| " must be present (format a2), as these are used to check the integrety of the file -! columns are: -! 1: parameter name -! 2: default parameter value -! 3: lower parameter limit -! 4: upper parameter limit -! ======================================================================================================================= -! -! ==================================================================== -! define format string for parameter descriptions -! ==================================================================== -'(a25,1x,3(a1,1x,f12.4,1x))' ! format string (must be in single quotes) -! ==================================================================== -! boundary conditions -! ==================================================================== -upperBoundHead | -0.7500 | -100.0000 | -0.0100 -lowerBoundHead | 0.0000 | -100.0000 | -0.0100 -upperBoundTheta | 0.2004 | 0.1020 | 0.3680 -lowerBoundTheta | 0.1100 | 0.1020 | 0.3680 -upperBoundTemp | 272.1600 | 270.1600 | 280.1600 -lowerBoundTemp | 274.1600 | 270.1600 | 280.1600 -! ==================================================================== -! precipitation partitioning -! ==================================================================== -tempCritRain | 273.1600 | 272.1600 | 274.1600 -tempRangeTimestep | 2.0000 | 0.5000 | 5.0000 -frozenPrecipMultip | 1.0000 | 0.5000 | 1.5000 -! ==================================================================== -! snow properties -! ==================================================================== -snowfrz_scale | 50.0000 | 10.0000 | 1000.0000 -fixedThermalCond_snow | 0.3500 | 0.1000 | 1.0000 -! ==================================================================== -! snow albedo -! ==================================================================== -albedoMax | 0.8400 | 0.7000 | 0.9500 -albedoMinWinter | 0.5500 | 0.6000 | 1.0000 -albedoMinSpring | 0.5500 | 0.3000 | 1.0000 -albedoMaxVisible | 0.9500 | 0.7000 | 0.9500 -albedoMinVisible | 0.7500 | 0.5000 | 0.7500 -albedoMaxNearIR | 0.6500 | 0.5000 | 0.7500 -albedoMinNearIR | 0.3000 | 0.1500 | 0.4500 -albedoDecayRate | 1.0d+6 | 0.1d+6 | 5.0d+6 -albedoSootLoad | 0.3000 | 0.1000 | 0.5000 -albedoRefresh | 1.0000 | 1.0000 | 10.0000 -! ==================================================================== -! radiation transfer within snow -! ==================================================================== -radExt_snow | 20.0000 | 20.0000 | 20.0000 -directScale | 0.0900 | 0.0000 | 0.5000 -Frad_direct | 0.7000 | 0.0000 | 1.0000 -Frad_vis | 0.5000 | 0.0000 | 1.0000 -! ==================================================================== -! new snow density -! ==================================================================== -newSnowDenMin | 100.0000 | 50.0000 | 100.0000 -newSnowDenMult | 100.0000 | 25.0000 | 75.0000 -newSnowDenScal | 5.0000 | 1.0000 | 5.0000 -constSnowDen | 100.0000 | 50.0000 | 250.0000 -newSnowDenAdd | 109.0000 | 80.0000 | 120.0000 -newSnowDenMultTemp | 6.0000 | 1.0000 | 12.0000 -newSnowDenMultWind | 26.0000 | 16.0000 | 36.0000 -newSnowDenMultAnd | 1.0000 | 1.0000 | 3.0000 -newSnowDenBase | 0.0000 | 0.0000 | 0.0000 -! ==================================================================== -! snow compaction -! ==================================================================== -densScalGrowth | 0.0460 | 0.0230 | 0.0920 -tempScalGrowth | 0.0400 | 0.0200 | 0.0600 -grainGrowthRate | 2.7d-6 | 1.0d-6 | 5.0d-6 -densScalOvrbdn | 0.0230 | 0.0115 | 0.0460 -tempScalOvrbdn | 0.0800 | 0.6000 | 1.0000 -baseViscosity | 9.0d+5 | 5.0d+5 | 1.5d+6 -! ==================================================================== -! water flow through snow -! ==================================================================== -Fcapil | 0.0600 | 0.0100 | 0.1000 -k_snow | 0.0150 | 0.0050 | 0.0500 -mw_exp | 3.0000 | 1.0000 | 5.0000 -! ==================================================================== -! turbulent heat fluxes -! ==================================================================== -z0Snow | 0.0010 | 0.0010 | 10.0000 -z0Soil | 0.0100 | 0.0010 | 10.0000 -z0Canopy | 0.1000 | 0.0010 | 10.0000 -zpdFraction | 0.6500 | 0.5000 | 0.8500 -critRichNumber | 0.2000 | 0.1000 | 1.0000 -Louis79_bparam | 9.4000 | 9.2000 | 9.6000 -Louis79_cStar | 5.3000 | 5.1000 | 5.5000 -Mahrt87_eScale | 1.0000 | 0.5000 | 2.0000 -leafExchangeCoeff | 0.0100 | 0.0010 | 0.1000 -windReductionParam | 0.2800 | 0.0000 | 1.0000 -! ==================================================================== -! stomatal conductance -! ==================================================================== -Kc25 | 296.0770 | 296.0770 | 296.0770 -Ko25 | 0.2961 | 0.2961 | 0.2961 -Kc_qFac | 2.1000 | 2.1000 | 2.1000 -Ko_qFac | 1.2000 | 1.2000 | 1.2000 -kc_Ha | 79430.0000 | 79430.0000 | 79430.0000 -ko_Ha | 36380.0000 | 36380.0000 | 36380.0000 -vcmax25_canopyTop | 40.0000 | 20.0000 | 100.0000 -vcmax_qFac | 2.4000 | 2.4000 | 2.4000 -vcmax_Ha | 65330.0000 | 65330.0000 | 65330.0000 -vcmax_Hd | 220000.0000 | 149250.0000 | 149250.0000 -vcmax_Sv | 710.0000 | 485.0000 | 485.0000 -vcmax_Kn | 0.6000 | 0.0000 | 1.2000 -jmax25_scale | 2.0000 | 2.0000 | 2.0000 -jmax_Ha | 43540.0000 | 43540.0000 | 43540.0000 -jmax_Hd | 152040.0000 | 152040.0000 | 152040.0000 -jmax_Sv | 495.0000 | 495.0000 | 495.0000 -fractionJ | 0.1500 | 0.1500 | 0.1500 -quantamYield | 0.0500 | 0.0500 | 0.0500 -vpScaleFactor | 1500.0000 | 1500.0000 | 1500.0000 -cond2photo_slope | 9.0000 | 1.0000 | 10.0000 -minStomatalConductance | 2000.0000 | 2000.0000 | 2000.0000 -! ==================================================================== -! vegetation properties -! ==================================================================== -winterSAI | 1.0000 | 0.0100 | 3.0000 -summerLAI | 3.0000 | 0.0100 | 10.0000 -rootScaleFactor1 | 2.0000 | 1.0000 | 10.0000 -rootScaleFactor2 | 5.0000 | 1.0000 | 10.0000 -rootingDepth | 2.0000 | 0.0100 | 10.0000 -rootDistExp | 1.0000 | 0.0100 | 1.0000 -plantWiltPsi | -150.0000 | -500.0000 | 0.0000 -soilStressParam | 5.8000 | 4.3600 | 6.3700 -critSoilWilting | 0.0750 | 0.0000 | 1.0000 -critSoilTranspire | 0.1750 | 0.0000 | 1.0000 -critAquiferTranspire | 0.2000 | 0.1000 | 10.0000 -minStomatalResistance | 50.0000 | 10.0000 | 200.0000 -leafDimension | 0.0400 | 0.0100 | 0.1000 -heightCanopyTop | 20.0000 | 0.0500 | 100.0000 -heightCanopyBottom | 2.0000 | 0.0000 | 5.0000 -specificHeatVeg | 874.0000 | 500.0000 | 1500.0000 -maxMassVegetation | 25.0000 | 1.0000 | 50.0000 -throughfallScaleSnow | 0.5000 | 0.1000 | 0.9000 -throughfallScaleRain | 0.5000 | 0.1000 | 0.9000 -refInterceptCapSnow | 6.6000 | 1.0000 | 10.0000 -refInterceptCapRain | 1.0000 | 0.0100 | 1.0000 -snowUnloadingCoeff | 0.0000 | 0.0000 | 1.5d-6 -canopyDrainageCoeff | 0.0050 | 0.0010 | 0.0100 -ratioDrip2Unloading | 0.4000 | 0.0000 | 1.0000 -canopyWettingFactor | 0.7000 | 0.0000 | 1.0000 -canopyWettingExp | 1.0000 | 0.0000 | 1.0000 -! ==================================================================== -! soil properties -! ==================================================================== -soil_dens_intr | 2700.0000 | 500.0000 | 4000.0000 -thCond_soil | 5.5000 | 2.9000 | 8.4000 -frac_sand | 0.1600 | 0.0000 | 1.0000 -frac_silt | 0.2800 | 0.0000 | 1.0000 -frac_clay | 0.5600 | 0.0000 | 1.0000 -fieldCapacity | 0.2000 | 0.0000 | 1.0000 -wettingFrontSuction | 0.3000 | 0.1000 | 1.5000 -theta_mp | 0.4010 | 0.3000 | 0.6000 -theta_sat | 0.5500 | 0.3000 | 0.6000 -theta_res | 0.1390 | 0.0010 | 0.1000 -vGn_alpha | -0.8400 | -1.0000 | -0.0100 -vGn_n | 1.3000 | 1.0000 | 3.0000 -mpExp | 5.0000 | 1.0000 | 10.0000 -k_soil | 7.5d-06 | 1.d-07 | 100.d-07 -k_macropore | 1.0d-03 | 1.d-07 | 100.d-07 -kAnisotropic | 1.0000 | 0.0001 | 10.0000 -zScale_TOPMODEL | 2.5000 | 0.1000 | 100.0000 -compactedDepth | 1.0000 | 0.0000 | 1.0000 -aquiferScaleFactor | 0.3500 | 0.1000 | 100.0000 -aquiferBaseflowExp | 2.0000 | 1.0000 | 10.0000 -aquiferBaseflowRate | 2.0000 | 1.0000 | 10.0000 -qSurfScale | 50.0000 | 1.0000 | 100.0000 -specificYield | 0.2000 | 0.1000 | 0.3000 -specificStorage | 1.d-09 | 1.d-05 | 1.d-07 -f_impede | 2.0000 | 1.0000 | 10.0000 -soilIceScale | 0.1300 | 0.0001 | 1.0000 -soilIceCV | 0.4500 | 0.1000 | 5.0000 -! ==================================================================== -! algorithmic control parameters -! ==================================================================== -minwind | 0.1000 | 0.0010 | 1.0000 -minstep | 1.0000 | 1.0000 | 1800.0000 -maxstep | 3600.0000 | 60.0000 | 7200.0000 -be_steps | 1.0000 | 1.0000 | 512.0000 -wimplicit | 0.0000 | 0.0000 | 1.0000 -maxiter | 100.0000 | 1.0000 | 100.0000 -relConvTol_liquid | 1.0d-3 | 1.0d-5 | 1.0d-1 -absConvTol_liquid | 1.0d-6 | 1.0d-8 | 1.0d-3 -relConvTol_matric | 1.0d-6 | 1.0d-7 | 1.0d-1 -absConvTol_matric | 1.0d-6 | 1.0d-8 | 1.0d-3 -relConvTol_energy | 1.0d-2 | 1.0d-5 | 1.0d-1 -absConvTol_energy | 1.0d-0 | 1.0d-2 | 1.0d+1 -relConvTol_aquifr | 1.0d-0 | 1.0d-2 | 1.0d+1 -absConvTol_aquifr | 1.0d-5 | 1.0d-5 | 1.0d-1 -relTolTempCas | 1.0d-6 | 1.0d-10| 1.0d-1 -absTolTempCas | 1.0d-6 | 1.0d-10| 1.0d-1 -relTolTempVeg | 1.0d-6 | 1.0d-10| 1.0d-1 -absTolTempVeg | 1.0d-6 | 1.0d-10| 1.0d-1 -relTolWatVeg | 1.0d-6 | 1.0d-10| 1.0d-1 -absTolWatVeg | 1.0d-6 | 1.0d-10| 1.0d-1 -relTolTempSoilSnow | 1.0d-6 | 1.0d-10| 1.0d-1 -absTolTempSoilSnow | 1.0d-6 | 1.0d-10| 1.0d-1 -relTolWatSnow | 1.0d-6 | 1.0d-10| 1.0d-1 -absTolWatSnow | 1.0d-6 | 1.0d-10| 1.0d-1 -relTolMatric | 1.0d-6 | 1.0d-10| 1.0d-1 -absTolMatric | 1.0d-6 | 1.0d-10| 1.0d-1 -relTolAquifr | 1.0d-6 | 1.0d-10| 1.0d-1 -absTolAquifr | 1.0d-6 | 1.0d-10| 1.0d-1 -zmin | 0.0100 | 0.0050 | 0.1000 -zmax | 0.0500 | 0.0100 | 0.5000 -! --- -zminLayer1 | 0.0075 | 0.0075 | 0.0075 -zminLayer2 | 0.0100 | 0.0100 | 0.0100 -zminLayer3 | 0.0500 | 0.0500 | 0.0500 -zminLayer4 | 0.1000 | 0.1000 | 0.1000 -zminLayer5 | 0.2500 | 0.2500 | 0.2500 -! --- -zmaxLayer1_lower | 0.0500 | 0.0500 | 0.0500 -zmaxLayer2_lower | 0.2000 | 0.2000 | 0.2000 -zmaxLayer3_lower | 0.5000 | 0.5000 | 0.5000 -zmaxLayer4_lower | 1.0000 | 1.0000 | 1.0000 -! --- -zmaxLayer1_upper | 0.0300 | 0.0300 | 0.0300 -zmaxLayer2_upper | 0.1500 | 0.1500 | 0.1500 -zmaxLayer3_upper | 0.3000 | 0.3000 | 0.3000 -zmaxLayer4_upper | 0.7500 | 0.7500 | 0.7500 -! ==================================================================== -minTempUnloading | 270.16 | 260.16 | 273.16 -minWindUnloading | 0.0000 | 0.0000 | 10.000 -rateTempUnloading | 1.87d+5 | 1.0d+5 | 3.0d+5 -rateWindUnloading | 1.56d+5 | 1.0d+5 | 3.0d+5 \ No newline at end of file diff --git a/case_study/base_settings/readme.md b/case_study/base_settings/readme.md deleted file mode 100644 index 9f2fa2df6..000000000 --- a/case_study/base_settings/readme.md +++ /dev/null @@ -1,37 +0,0 @@ -# Lookup table provenance -This information cannot easily be encoded in the files themselves, due to the way files are being read. - -#### GENPARM -- 2021-08-10: Added NOAH-MP default parameters (NCAR Research Applications Laboratory, RAL, 2021) - -#### MPTABLE -- 2021-08-10: Added NOAH-MP default parameters (NCAR RAL, 2021) - -#### SOILPARM -- 2021-08-10: Added NOAH-MP default parameters (NCAR RAL, 2021) -- 2021-08-10: Added ROSETTA table values (see `ROSETTA soil parameter values`) - -#### VEGPARM -- 2021-08-10: Added NOAH-MP default parameters (NCAR RAL, 2021) - - -## ROSETTA soil parameter values -ROSETTA soil parameters are a combination of the NOAH-MP STAS and STAS-RUC tables (NCAR RAL, 2021), updated with five additional parameters derived by the ROSETTA model (U.S. Department of Agriculture: Agricultural Research Service (USDA ARS), 2021). - -Columns `BB`, `DRYSMC`, `HC`, `MAXSMC`, `SATPIS`, `SATDK`, `SATDW`, `WLTSMC` and `QTZ` are duplicated from the STAS-RUC table. - -Column `REFSMC` is duplicated from the STAS table. - -Columns `theta_res`, `theta_sat`, `vGn_alpha`, `vGn_n` and `k_soil` are duplicated/derived from table values provided by the U.S. Department of Agriculture, Agricultural Research Service, (USDA ARS, 2021) as follows: - -- `theta_res` = `x`, where x is taken from column `qr` [-]; -- `theta_sat` = `x`, where x is taken from column `qs` [-]; -- `vGn_alpha` = `-1 * 10^x * 100`, where x is taken from column `log(a)` [log(cm-1)] > [m-1] while also changing the sign of the alpha parameter so that matric head calculations use SUMMA's convention of negative values for matric head; -- `vGn_n` = `10^x`, where x is taken from column `log(n)` [-]; -- `k_soil` = `10^x / 100 / (60 * 60 * 24)`, where x is taken from column `Ks` [log(cm d-1)] > [m s-1]. - - -## References -NCAR Research Applications Laboratory, RAL, 2021. Noah-Multiparameterization Land Surface Model (Noah-MP LSM) [WWW Document]. URL https://ral.ucar.edu/solutions/products/noah-multiparameterization-land-surface-model-noah-mp-lsm (accessed 11.15.21). - -U.S. Department of Agriculture: Agricultural Research Service (USDA ARS), 2021. ROSETTA Class Average Hydraulic Parameters [WWW Document]. URL https://www.ars.usda.gov/pacific-west-area/riverside-ca/agricultural-water-efficiency-and-salinity-research-unit/docs/model/rosetta-class-average-hydraulic-parameters/ (accessed 11.15.21). diff --git a/case_study/readme.md b/case_study/readme.md deleted file mode 100644 index 38996f802..000000000 --- a/case_study/readme.md +++ /dev/null @@ -1,18 +0,0 @@ -# SUMMA case studies -This folder contains a case study to show how a typical SUMMA setup looks. The folder serves a double purpose as a way to track default versions of certain input files, such as the Noah-MP tables and spatially constant parameter files. - -For more details about SUMMA inputs, see the [online documentation](https://summa.readthedocs.io/en/latest/input_output/SUMMA_input/). - - -## Base settings -This folder contains the setting files that typically do not change for different model applications. Currently these include: -- `GENPARM.TBL`: lookup table for general parameters (legacy, currently unused) -- `MPTABLE.TBL`: lookup table for vegetation parameters -- `SOILPARM.TBL`: lookup table for soil parameters -- `VEGPARM.TBL`: lookup table for vegetation parameters -- `basinParamInfo.txt`: default values for GRU(basin)-level parameters -- `localParamInfo.txt`: default values for HRU(local)-level parameters - - -## Case studies -Instead of providing an example in the code, the user is directed to clone the Laugh-Tests github repository at https://git.cs.usask.ca/numerical_simulations_lab/laugh_tests.git. \ No newline at end of file diff --git a/ci/summa_install_utils b/ci/summa_install_utils deleted file mode 100644 index 77d3db7c6..000000000 --- a/ci/summa_install_utils +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env bash - -set -e - -export CC=/usr/bin/gcc-6 -export FC=/usr/bin/gfortran-6 - -if [ -z "$WORKDIR" ]; then - export WORKDIR=$HOME/workdir - mkdir -p $WORKDIR -fi - -if [ -z "$INSTALLDIR" ]; then - export INSTALLDIR=$HOME/installdir - mkdir -p $INSTALLDIR -fi - -function install_szip { - echo install_szip - cd $WORKDIR - wget --no-check-certificate -q http://www.hdfgroup.org/ftp/lib-external/szip/2.1.1/src/szip-2.1.1.tar.gz - tar -xzf szip-2.1.1.tar.gz - cd szip-2.1.1 - ./configure --prefix=$INSTALLDIR &> config.log - make &> make.log - make install - export CPPFLAGS="$CPPFLAGS -I${INSTALLDIR}/include" - export LDFLAGS="$LDFLAGS -L${INSTALLDIR}/lib" -} - -function install_hdf5 { - echo install_hdf5 - cd $WORKDIR - wget --no-check-certificate -q https://support.hdfgroup.org/ftp/HDF5/current/src/hdf5-1.10.5.tar.gz - tar -xzf hdf5-1.10.5.tar.gz - cd hdf5-1.10.5 - ./configure --prefix=$INSTALLDIR &> config.log - make - make install - export LIBDIR=${INSTALLDIR}/lib -} - -function install_netcdf_c { - echo install_netcdf_c - cd $WORKDIR - wget --no-check-certificate -q ftp://ftp.unidata.ucar.edu/pub/netcdf/netcdf-c-4.7.3.tar.gz - tar -xzf netcdf-c-4.7.3.tar.gz - cd netcdf-c-4.7.3 - ./configure --prefix=$INSTALLDIR &> config.log - make &> make.log - make install - export LD_LIBRARY_PATH=${INSTALLDIR}/lib -} - -function install_netcdf_fortran { - echo install_netcdf_fortran - cd $WORKDIR - wget --no-check-certificate -q ftp://ftp.unidata.ucar.edu/pub/netcdf/netcdf-fortran-4.5.2.tar.gz - tar -xzf netcdf-fortran-4.5.2.tar.gz - cd netcdf-fortran-4.5.2 - ./configure --prefix=$INSTALLDIR &> config.log - make &> make.log - make install -} - -function summa_before_install { - echo summa_before_install - # Install szip (used by hdf5) - install_szip - # Install HDF5 - install_hdf5 - # Install NetCDF-C - install_netcdf_c - # Install NetCDF fortran - install_netcdf_fortran -} - -function summa_install { - echo summa_install - cd ${TRAVIS_BUILD_DIR} - export F_MASTER=${TRAVIS_BUILD_DIR} - export FC=gfortran - export FC_EXE=gfortran-6 - export INCLUDES='-I${INSTALLDIR}/include -I/usr/include' - export LIBRARIES='-L${INSTALLDIR}/lib -lnetcdff -L/usr/lib -llapack -lblas' - make -C build/ -f Makefile &> make.log -} - -function summa_script { - echo summa_script - ${TRAVIS_BUILD_DIR}/bin/summa.exe -} - -function summa_after_success { - echo summa_after_success - echo "SUMMA build succeeded" -} - -function summa_after_failure { - echo summa_after_failure - echo "SUMMA build failed" -} diff --git a/docs/README_ngen.md b/docs/installation/README_ngen.md similarity index 100% rename from docs/README_ngen.md rename to docs/installation/README_ngen.md diff --git a/docs/README_not_ngen.md b/docs/installation/README_not_ngen.md similarity index 100% rename from docs/README_not_ngen.md rename to docs/installation/README_not_ngen.md diff --git a/test_ngen/readme.md b/test_ngen/readme.md new file mode 100644 index 000000000..0f1aef41c --- /dev/null +++ b/test_ngen/readme.md @@ -0,0 +1,13 @@ +# SUMMA case studies +This folder contains a case study to show how a typical SUMMA setup looks in the NextGen seti[. The folder serves a double purpose as a way to track default versions of certain input files, such as the Noah-MP tables and spatially constant parameter files. These files are in /settings/meta. + + +## Settings +The meta folder contains the setting files that typically do not change for different model applications. Currently these include: +- `GENPARM.TBL`: lookup table for general parameters (legacy, currently unused) +- `MPTABLE.TBL`: lookup table for vegetation parameters +- `SOILPARM.TBL`: lookup table for soil parameters +- `VEGPARM.TBL`: lookup table for vegetation parameters + +## Case studies +For other (non-NextGen) examples the user is directed to clone the Laugh-Tests github repository at https://git.cs.usask.ca/numerical_simulations_lab/laugh_tests.git. \ No newline at end of file From 1ef08efd6f0271a555dbcbddd6d4f7ad6d250d3a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 29 Jul 2024 12:00:02 -0500 Subject: [PATCH 1357/1472] update decisions --- build/source/engine/read_pinit.f90 | 2 +- docs/configuration/SUMMA_model_decisions.md | 38 +++++++++++++++++---- docs/input_output/SUMMA_input.md | 4 ++- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/build/source/engine/read_pinit.f90 b/build/source/engine/read_pinit.f90 index 88497a219..fd5c4e425 100644 --- a/build/source/engine/read_pinit.f90 +++ b/build/source/engine/read_pinit.f90 @@ -215,7 +215,7 @@ subroutine set_ida_defaults(parFallback, err, message) parFallback(iLookPARAM%idaMinStepSize)%default_val = 0 end if if (parFallback(iLookPARAM%idaMaxStepSize)%default_val < 0.99_rkind*realMissing) then - parFallback(iLookPARAM%idaMaxStepSize)%default_val = 0 ! 0 means ida's default ofinfinity + parFallback(iLookPARAM%idaMaxStepSize)%default_val = 0 ! 0 means ida's default of infinity end if if (parFallback(iLookPARAM%idaMaxErrTestFail)%default_val < 0.99_rkind*realMissing) then parFallback(iLookPARAM%idaMaxErrTestFail)%default_val = 50 ! IDA default is 10 diff --git a/docs/configuration/SUMMA_model_decisions.md b/docs/configuration/SUMMA_model_decisions.md index 7122b182d..05389068b 100644 --- a/docs/configuration/SUMMA_model_decisions.md +++ b/docs/configuration/SUMMA_model_decisions.md @@ -121,22 +121,24 @@ Ball-Berry: scaling of photosynthesis from the leaf to the canopy ## 14. num_method -Numerical method +Numerical method choice | Option | Description | |---|---| -| itertive | **TODO: Describe itertive
      [Reference](http://doi.org/)** | -| non_iter | **TODO: Describe non_iter
      [Reference](http://doi.org/)** | -| itersurf | **TODO: Describe itersurf
      [Reference](http://doi.org/)** | +| itertive | **Use homegrown SUMMA Backward Euler solver (backwards compatible naming)** | +| homegrown | **Use homegrown SUMMA Backward Euler solver** | +| kinsol | **SUNDIALS (must have installed SUNDIALS) Backward Euler solution using KINSOL, backwards Euler solver with constant step-size
      [SUNDIALS KINSOL](https://sundials.readthedocs.io/en/latest/kinsol/index.html)** | +| ida | **SUNDIALS (must have installed SUNDIALS) solution using IDA, adaptive step-size Implicit Differential-Algebraic solver
      [SUNDIALS IDA](https://sundials.readthedocs.io/en/latest/ida/index.html)** | + ## 15. fDerivMeth -Method to calculate flux derivatives +Method to calculate flux derivatives for Jacobian | Option | Description | |---|---| -| numericl | **TODO: Describe numericl
      [Reference](http://doi.org/)** | -| analytic | **TODO: Describe analytic
      [Reference](http://doi.org/)** | +| numericl | **numerical derivatives** | +| analytic | **analytical derivatives, only works with SUNDIALS num_method (kinsol or ida
      [SUNDIALS IDA](https://sundials.readthedocs.io/en/latest/kinsol/Mathematics_link.html#difference-quotient-jacobian-approximations)** | @@ -384,6 +386,7 @@ Method for new snow density | pahaut_76 | **An empirical calculation dependant on air temperature and wind speed.
      [Pahaut, 1976](http://doi.org/)** | | constDens | **A constant new snow density of 330 kg/m^3
      [Reference](http://doi.org/)** | + ## 40. snowUnload Method for unloading snow from the canopy @@ -393,3 +396,24 @@ Method for unloading snow from the canopy | meltDripUnload | **Contains a temperature unloading function where the parameter *snowUnloadingCoeff* controls the exponential unloading rate and *ratioDrip2Unloading* is the ratio of liquid water drip from the canopy to snow unloading.
      [Hedstom and Pomeroy, 1998](https://doi.org/10.1002/(SICI)1099-1085(199808/09)12:10/11<1611::AID-HYP684>3.0.CO;2-4)
      [Storck et al. 2002]( https://doi.org/10.1029/2002WR001281)** | | windUnload | **Contains temperature and wind dependent unloading functions. The rates of temperature and wind unloading are adjustable through parameters *rateTempUnloading* and *rateWindUnloading*. Both functions contain parameter thresholds for the minimum temperature and windspeed required for unloading.
      [Roesch et al. 2001](https://doi.org/10.1007/s003820100153)** | + + +## 41. nrgConserv +Choice of variable in energy equations (BE residual or IDA state variable) + +| Option | Description | +|---|---| +| closedForm | **use temperature with closed form heat capacity
      [Energy paper stub](http://doi.org/)** | +| enthalpyFormLU | **use enthalpy with soil temperature-enthalpy lookup table
      [Energy paper stub](http://doi.org/)** | +| enthalpyForm | **use enthalpy with soil temperature-enthalpy analytical solutions
      [Energy paper stub](http://doi.org/)** | + + + +## 42. aquiferIni +Choice of initial fill level for aquifer, should be used at default unless comparing solution methods + +| Option | Description | +|---|---| +| fullStart | **(default) start with initial value aquifer, usually full for cold start as easier to drain the aquifer to equilibrium than fill to equilibrium** | +| emptyStart | **start with empty aquifer, only used if comparing solution solution methods and not looking to simulate reality** | + diff --git a/docs/input_output/SUMMA_input.md b/docs/input_output/SUMMA_input.md index 62d4ce2fd..2da6648a5 100644 --- a/docs/input_output/SUMMA_input.md +++ b/docs/input_output/SUMMA_input.md @@ -139,7 +139,7 @@ The model decisions and their options or values are listed in the following tabl |[bbNumerics](../configuration/SUMMA_model_decisions.md#bbNumerics) | NoahMPsolution
      newtonRaphson | (12) Ball-Berry: iterative numerical solution method |[bbAssimFnc](../configuration/SUMMA_model_decisions.md#bbAssimFnc) | colimitation
      minFunc | (13) Ball-Berry: controls on carbon assimilation |[bbCanIntg8](../configuration/SUMMA_model_decisions.md#bbCanIntg8) | constantScaling
      laiScaling | (14) Ball-Berry: scaling of photosynthesis from the leaf to the canopy -|[num_method](../configuration/SUMMA_model_decisions.md#num_method) | itertive
      non_iter
      itersurf | (15) choice of numerical method +|[num_method](../configuration/SUMMA_model_decisions.md#num_method) | itertive
      homegrown
      kinsol
      ida| (15) choice of numerical method |[fDerivMeth](../configuration/SUMMA_model_decisions.md#fDerivMeth) | numericl
      analytic | (16) choice of method to calculate flux derivatives |[LAI_method](../configuration/SUMMA_model_decisions.md#LAI_method) | monTable
      specified | (17) choice of method to determine LAI and SAI |[cIntercept](../configuration/SUMMA_model_decisions.md#cIntercept) | sparseCanopy
      storageFunc
      notPopulatedYet | (18) choice of parameterization for canopy interception @@ -165,6 +165,8 @@ The model decisions and their options or values are listed in the following tabl |[spatial_gw](../configuration/SUMMA_model_decisions.md#spatial_gw) | localColumn
      singleBasin | (38) choice of method for spatial representation of groundwater |[subRouting](../configuration/SUMMA_model_decisions.md#subRouting) | timeDlay
      qInstant | (39) choice of method for sub-grid routing |[snowDenNew](../configuration/SUMMA_model_decisions.md#snowDenNew) | hedAndPom
      anderson
      pahaut_76
      constDens | (40) choice of method for new snow density +|[nrgConserv](../configuration/SUMMA_model_decisions.md#nrgConserv) | closedForm
      enthalpyFormLU
      enthalpyForm | (41) choice of variable in energy equations (BE residual or IDA state variable) +|[aquiferIni](../configuration/SUMMA_model_decisions.md#aquiferIni) | fullStart
      emptyStart | (40) choice of initial fill level for aquifer, should be used at default unless comparing solution methods The model decisions for each simulation are included as global attributes in [SUMMA output files](SUMMA_output.md). From 8bfb7835676fcf83bb5dd01e155b8225a1d9e892 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 29 Jul 2024 12:28:07 -0500 Subject: [PATCH 1358/1472] update laugh test notes --- test_ngen/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_ngen/readme.md b/test_ngen/readme.md index 0f1aef41c..83a516e33 100644 --- a/test_ngen/readme.md +++ b/test_ngen/readme.md @@ -10,4 +10,4 @@ The meta folder contains the setting files that typically do not change for diff - `VEGPARM.TBL`: lookup table for vegetation parameters ## Case studies -For other (non-NextGen) examples the user is directed to clone the Laugh-Tests github repository at https://git.cs.usask.ca/numerical_simulations_lab/laugh_tests.git. \ No newline at end of file +For other (non-NextGen) examples the user is directed to clone the Laugh-Tests github repository at https://git.cs.usask.ca/numerical_simulations_lab/hydrology/laugh_tests \ No newline at end of file From 0468a9040f1efff21937b87130fb5e17da95e0b9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 29 Jul 2024 12:41:18 -0500 Subject: [PATCH 1359/1472] add generate cold state to utils, old file, may want to modify --- utils/gen_coldstate.py | 237 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 utils/gen_coldstate.py diff --git a/utils/gen_coldstate.py b/utils/gen_coldstate.py new file mode 100644 index 000000000..d7169ffab --- /dev/null +++ b/utils/gen_coldstate.py @@ -0,0 +1,237 @@ +#!/usr/bin/env python +''' Create a vector cold state file for SUMMA from constant values''' +# +# Author: Andy Wood, Feb 2017 +# +# Notes: quick n dirty to generate constant initial states across a domain +# all values hardwired, just gets HRU index from an existing parameter file +# improvements: could read another cold state file to get list of variables +# to populate; or read a metadata dictionary (names, types, etc) +# +# no mapping required here -- but one could map another resolution vals, similar +# to the param mapping scripts +# +# check: nSoil and nSnow might have to be 'int' instead of 'int64' in output +# +# Requirements: run with a python (eg miniconda) 2.7 that includes netCDF4 +# +# ========================================================================= + +import sys +import os +import time +import getopt +import numpy as np +import netCDF4 as nc4 +#import xarray as xr + +######################################################################## +# Subroutines # +######################################################################## + +def getNetCDFData(fn, varname): + """Read variables available to be mapped from NetCDF """ + f = nc4.Dataset(fn,'r') + data = f.variables[varname][:] + f.close() +# ds = xr.open_dataset(fn) +# data = ds[varname] + return data + +def getOutputPolyIDs(nc_file): + outPolyIDs = getNetCDFData(nc_file, 'hruId') + print("read output outPolyIds ('hruId') from example domain file") + return outPolyIDs + + +# write variables to netcdf output file +def writeNC_state_vars(nc_out, newVarName, newVarDim, newVarType, newVarVals): + + """ Write [hru] array in netCDF4 file, and variable of + """ + + print("adding data") + ncvar = nc_out.createVariable(newVarName, newVarType, (newVarDim, 'hru',),fill_value='-999.0') + ncvar[:] = newVarVals # store data in netcdf file + + +# write dimensions and dimension variables to netcdf output file +def writeNC_dims(fn, scalarv, midSoil, midToto, ifcToto, hrus, hru_type): + """ Write [hru] array in netCDF4 file, and variable of + """ + + print("writing output file") + nc_out = nc4.Dataset(fn, 'w', format='NETCDF4') + + # Create dimensions + dim_hru = nc_out.createDimension('hru', len(hrus)) + dim_scalarv = nc_out.createDimension('scalarv', scalarv) + dim_midSoil = nc_out.createDimension('midSoil', midSoil) + dim_midToto = nc_out.createDimension('midToto', midToto) + dim_ifcToto = nc_out.createDimension('ifcToto', ifcToto) + + # --- Create HRU ID variable (can be either int or string) + if hru_type == 'str': + # string HRU (need to add string length) + max_strlen = 20 # EC + dim_str = nc_out.createDimension('strlen', max_strlen) + hruId = nc_out.createVariable('hruId', 'S1', ('hru', 'strlen'),fill_value='-999') + hruId[:] = nc4.stringtochar(np.asarray(hrus, + dtype='S{}'.format(max_strlen))) + elif hru_type == 'int64': + # integer HRU + hruId = nc_out.createVariable('hruId', 'i8', ('hru', ),fill_value='-999') + hruId[:] = hrus + #hruId[:] = np.asarray(hrus, dtype='int') + + elif hru_type == 'int': + # integer HRU + hruId = nc_out.createVariable('hruId', 'int', ('hru', ),fill_value='-999') + hruId[:] = hrus + #hruId[:] = np.asarray(hrus, dtype='int') + + else: + # not recognized + sys.exit("ERROR, hru_type not recognized: must be str, int64, or int") + + # add attribute + hruId.long_name = 'USGS HUC12 ID' + + return nc_out + # leave netcdf file open + + +############################################ +# Main # +############################################ +use = ''' +Usage: %s -[h] + -h help +''' +if __name__ == '__main__': + + def usage(): + sys.stderr.write(use % sys.argv[0]) + sys.exit(1) + try: + (opts, args) = getopt.getopt(sys.argv[1:], 'h') + except getopt.error: + usage() + + verbose = False + grid_info = False + proj_info = True + for (opt,val) in opts: + if opt == '-h': + usage() + elif opt == '-v': + verbose = True + else: + raise OptionError(opt) + usage() + + if len(args) == 3: + nc_example_name = args[0] # template file (other params) + nc_out_name = args[1] # output cold-state file + hru_type = args[2] # 'int' or 'string' + # nc_in_name = args[4] # existing cold-state file + + # hardwired to forcing formats (hru index rather than grid) + outPolyIDs=getOutputPolyIDs(nc_example_name) + nOutPolygons = len(outPolyIDs) + + # === now start to create the cold state variables using the variable template === + + # settings (hardwire for now - later read from a config file -- 4 layer + #scalarv = 1 + #midSoil = 4 + #midToto = 4 + #ifcToto = 5 + #dT = 10800 # timestep of forcings in seconds + #lyrDepth = [0.1, 0.3, 0.6, 1.0] + #lyrHeight = [0.0, 0.1, 0.4, 1.0, 2.0] + + # settings 3 layer + scalarv = 1 + midSoil = 3 + midToto = 3 + ifcToto = 4 + dT = 10800 # timestep of forcings in seconds + #dT = 3600 # timestep of forcings in seconds + lyrDepth = [0.1, 0.3, 0.6] + lyrHeight = [0.0, 0.1, 0.4, 1.0] + + # initialize netcdf file by storing dimensions and hru variable + nc_out = writeNC_dims(nc_out_name, scalarv, midSoil, midToto, ifcToto, + outPolyIDs, hru_type) + + # === now loop through variables and write + # this could be done by looping through the input state file and xferring values + # could also read vars and + + # nSoil, nSnow + newVarVals = np.full((1,nOutPolygons), midSoil, dtype='f8') + writeNC_state_vars(nc_out, 'nSoil', 'scalarv', 'f8', newVarVals) + newVarVals = np.zeros((1,nOutPolygons), dtype='f8') + writeNC_state_vars(nc_out, 'nSnow', 'scalarv', 'f8', newVarVals) + + # dT + newVarVals = np.full((1,nOutPolygons), dT) + writeNC_state_vars(nc_out, 'dt_init', 'scalarv', 'f8', newVarVals) + + # SWE, SnowDepth, SfcMeltPond, SnowAlbedo, CanopyLiq, CanopyIce + newVarVals = np.zeros((1,nOutPolygons)) + writeNC_state_vars(nc_out, 'scalarSWE', 'scalarv', 'f8', newVarVals) + writeNC_state_vars(nc_out, 'scalarSnowDepth', 'scalarv', 'f8', newVarVals) + writeNC_state_vars(nc_out, 'scalarSfcMeltPond', 'scalarv', 'f8', newVarVals) + writeNC_state_vars(nc_out, 'scalarSnowAlbedo', 'scalarv', 'f8', newVarVals) + writeNC_state_vars(nc_out, 'scalarCanopyLiq', 'scalarv', 'f8', newVarVals) + writeNC_state_vars(nc_out, 'scalarCanopyIce', 'scalarv', 'f8', newVarVals) + + # CanairTemp, CanopyTemp + newVarVals = np.full((1,nOutPolygons), 283.16) + writeNC_state_vars(nc_out, 'scalarCanairTemp', 'scalarv', 'f8', newVarVals) + writeNC_state_vars(nc_out, 'scalarCanopyTemp', 'scalarv', 'f8', newVarVals) + + # AquiferStorage + newVarVals = np.full((1,nOutPolygons), 1.0) + writeNC_state_vars(nc_out, 'scalarAquiferStorage', 'scalarv', 'f8', newVarVals) + + # layer MatricHead + newVarVals = np.full((midSoil,nOutPolygons), -1.0) + writeNC_state_vars(nc_out, 'mLayerMatricHead', 'midSoil', 'f8', newVarVals) + + # layer Temp + newVarVals = np.full((midToto,nOutPolygons), 283.16) + writeNC_state_vars(nc_out, 'mLayerTemp', 'midToto', 'f8', newVarVals) + + # layer VolFracLiq + newVarVals = np.full((midToto,nOutPolygons), 0.2) + writeNC_state_vars(nc_out, 'mLayerVolFracLiq', 'midToto', 'f8', newVarVals) + + # layer VolFracIce + newVarVals = np.full((midToto,nOutPolygons), 0.0) + writeNC_state_vars(nc_out, 'mLayerVolFracIce', 'midToto', 'f8', newVarVals) + + # layer Depth, Height + newVarVals = np.full((nOutPolygons,midToto), lyrDepth).transpose() + writeNC_state_vars(nc_out, 'mLayerDepth', 'midToto', 'f8', newVarVals) + newVarVals = np.full((nOutPolygons,ifcToto), lyrHeight).transpose() + writeNC_state_vars(nc_out, 'iLayerHeight', 'ifcToto', 'f8', newVarVals) + + nc_out.close() + + else: + usage() + + + # code for reading input data variable & attributes + # to pass to processing and write routines (instead of hardwired calls above +# f = nc4.Dataset(nc_infl, 'r') +# var_in = f.variables[varname] +# attNames = [] +# attContents = [] +# attr = var_in.ncattrs() # get attributes +# for n in range(0,len(attr)): +# attNames.extend([attr[n]]) +# attContents.extend([var_in.getncattr(attr[n])]) From a70a0973b049d4c0e743b05a9c86197870b055e0 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 29 Jul 2024 16:56:27 -0500 Subject: [PATCH 1360/1472] update readme for utils and explain changes to code --- ...eanNAmermean_MeanNAmerRMSE_differences.png | Bin 0 -> 630013 bytes .../NAmermean_NAmerRMSE_differences.png | Bin 0 -> 1516209 bytes .../SUMMA_horrendogram.png | Bin .../SUMMA_parameters_spec_order.png | Bin .../SUMMA_parameters_spec_order.pptx | Bin .../SUMMA_spatial.png | Bin .../SUMMA_temperature_profile_example.png | Bin docs/assets/listedChanges.txt | 113 ++++++++++++++++++ docs/assets/readme.md | 19 +++ test_ngen/readme.md | 2 +- utils/SUMMA_merge_restarts_into_warmState.py | 47 ++++++++ utils/readme.md | 3 + 12 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 docs/assets/MeanNAmermean_MeanNAmerRMSE_differences.png create mode 100644 docs/assets/NAmermean_NAmerRMSE_differences.png rename docs/assets/{img => code_methodology_figures}/SUMMA_horrendogram.png (100%) rename docs/assets/{img => code_methodology_figures}/SUMMA_parameters_spec_order.png (100%) rename docs/assets/{img => code_methodology_figures}/SUMMA_parameters_spec_order.pptx (100%) rename docs/assets/{img => code_methodology_figures}/SUMMA_spatial.png (100%) rename docs/assets/{img => code_methodology_figures}/SUMMA_temperature_profile_example.png (100%) create mode 100644 docs/assets/listedChanges.txt create mode 100644 docs/assets/readme.md create mode 100644 utils/SUMMA_merge_restarts_into_warmState.py create mode 100644 utils/readme.md diff --git a/docs/assets/MeanNAmermean_MeanNAmerRMSE_differences.png b/docs/assets/MeanNAmermean_MeanNAmerRMSE_differences.png new file mode 100644 index 0000000000000000000000000000000000000000..d7a27696308c7efd1451f8787ea6b3d7da6bf650 GIT binary patch literal 630013 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

      c| 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�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

      W{g?pTRU6$UY!H~>r8m~ULswxw_O17f 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 z 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%0A45VJ$J3` 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�mm*b2lgAe3r{+qWp$=UB4q$o1nmkGIDuJ04sL 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 WdHeL#(c5x`k1aXdvOmk(Rre1o$tx)U literal 0 HcmV?d00001 diff --git a/docs/assets/NAmermean_NAmerRMSE_differences.png b/docs/assets/NAmermean_NAmerRMSE_differences.png new file mode 100644 index 0000000000000000000000000000000000000000..a580c51599a6fc233c5e7ffd155d9bb0d941e28a GIT binary patch literal 1516209 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%0AZ; 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

    1. -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�V-L@sh54j_>DaOwn;Pd%(t^3#GQHoXK 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

      y5O`(PXJ1i5_Bi*^)w1}EZUsk@&faQR^X1Wz z?1h$SVHvqT>vPiMMhA0jy{Y&}s|%xqJ=$E$mnH^28%I>K1J_L9(I&NWS+v8=!)PgZt@rYM98VHww~xD=rq_ zG6FaRvBF_~TTsmSV+M|$jMx#KbG)vAK&aewhCp*ZTg6$L z{nsOLCsAmB>x*5>`s6_%$VS$YZ?*W9j=8+Yu$}+w4UVy~{`T6`wId1KSa2g3efJI$ zT1zyR{#?y2EF3NlrprlJ-DBu|GiAnEb(=*m+Njpl@iLCUn}0(H?Fin0#oOQjDGGp# z*gxU{KpBD1`+!DgxnL7KEq*M*gnd;Fz&OY;`p@H^+O@3`%JYMsPLosPr0qYU@C<{8 z^{b<^dcKk5QoPR(=MZ52qldIGUOYKsWd8X};5n7n3Y_z|&Xiz1lCI5Q%vl zFzuLI+_Q`~W+Z;wWf^nR592U-5Pr&SjV|xk_}J6jK0UUE@PR`1dn{V=?jWcj^ayM&cO(!|Q#*#yvgDV$p|ag;c_$!^29+Z% znC}8lAl=XtSG>6@1DNY&SG_c+Tg*U5i*VBD|Le2_6=)Z-#m&jOY#KWKWE!V3qo%Yb_Vy2X z!AxEINA>vEQH7AQn(TsLO&kiKd{VS_LwQiF$WiIQ6tNo*Ajd&7*%f`}8G41KyvOOE z`(AY>DbH-xDC83HjDz==x--Y3K$3LO8 zW)VH`$OWWtUWIrBd~*c5Pm-yQG$}odp4Ds#R^hPo2J1Pb=H54G-VM+G*UJm-TNsMf zhe)Vdc+q4c+qW^9@t7hpN__!7mXP^FQe#@r-SZ8%Cr1!R#=UQk(yd-B)Zg+$%X8At zHtGHFkJooEc6r+G6Q4ol!1H|HtPay^dMNs!BHV$6uJ}Z=Sf@sOr-{IuwccWJ{=+6; z5Lj6J3q5r?i+X;i0!!xQ|KBpQ0vVBBH|rsgEFF-#W4}FOsd#R)WXHVYy~kb1VEam_ zPb+R>@D6kF&RiDEoIaRE6b22MNWqj0aj2i`H6MA_}%51uA$QTyT_F+@D^CE&6~1-B&hZVzm3ANU45h(rsMw zVIIi&DK9FWtZ- zjzor;-~r~;>i2pCp2)OZ9wL7We*2qo*nCIr$6(Y)|IpImypSJ!;HTG&@=7<=hztq{ zSz{GV5el=bDY@`w8F&W>5sggrYeX#F`#R<0ze9ZRzx{mz??Uud{Bq1deQfO0PNs0` z=4aMBC=6G=qq|qK&_melImu|+P=ThcU5l29)y0-CLS{5kX18JCZ;g-?+0@-J-4}u+w#@ ztfH$@Wi}D%JFekW&Kx`(X|a3X(umwS8$Wu`%A4|MLoYRCq=7+m+CK7vIF&n0fA`i# z#WIuypZqo@=xZRQsgNnbHST3#p;&!WCFokvj$>|X>a}j#i5~ba>!jI@Rb#Lhv3IQ5 zX94^*wiMLhj?iG9HWJ=47BXV}$cLKwI8~|j_|2N}v+qdlmG0tTW7Pai!LAp^NMT@g zjCyBSO`M| z8Ce?@3bdwsi4bh&dxBI9R ziTDvP$@L#j@Vd$fyYO*Ax%DX{IK~21Z6<*_j38uLdar=*wc82f>)-f-;lhVGvQU_& z?_&OCM$FY4(1aP9>eUh^dA@eOd=44<@C}f!3;(3Gf2n=L=M1$&0bjc+aJ+b>O0jxC zIdjY|T}RECAp!Ny4jubW5zM|S5hUrD*2 zzD#?usHq{*@B`1KzV_Ey)YA`eRN0pvsnlJ<)ywRotbgML1$`#udVAw+L}eYBFQldh zY=^-FlN?85(hyK=lx|_N{s@ejzC@bSsU5}I_$`U6_l9=?BU}ngvWA8*qwE@P=yY^w z*S@OQ3r3GXrw5c}+lnKU0C}0yQ3uaXC(+_Sp5Jlw^934Zd5YVRz6Ts?SN4Yub-n}c zCukv`j2H?)gM~H&g&0~qdkn0|M?#P!$@4;i=}*5+jd#>rd&`%7-RhgM+3Ot+-9 zl_seu>3zw4|BdrK=<{%BFEDL(FW~rQ$jI^MquPe-g1s9uMtjqQnDb8YUi+CBVmi*A znmveoaFy$8Se$bTRKi1neh)?kVTeXKp0D5@smwU#C3j|3lZZsrtp0pttKzPXyLNRZ zYqlg}zr^n!bwLZ?^br@)LjufQ{{Z0_6LkAlG$`BpeU8QiBMWTC-wH1P!HrOC1_g}& z-eMJ;R;r;}V`y=0Wzc_FxK;)CigSL2BlBj_!_LEXo^5Rx5^KL>_zO=NXc=$= z<@e7a)TJf?Za%wmH&972F1A6mcSCiI>kGLh@9z&icbNWci(<5rJ;C3LliQS1{&!ve zdu(AH0;U&epmA|MUYe3VsFnvcr*XU0FVxL!B*^^<&4nHMaLnxBN_W!J$2O0jHx*OkvY{m(#};j@P6nNy;4e)_3>85)BMt{Fl`^66eM|={EoxiN{-3$piWwJ( z@|SHK7MNS?`T`l(Up?+C;#ZQ{rl^JvaAFaMV!LfzC(la4_FU(wvwhjiFA!3Y3wSk0bIx(@J||0xtVwv+U3!9_U*pi` z^?rF6?~)9WdGChcM7X{jj3~Gznjh2iC&;91%bI5iD|+fw!nu@4)GW4j3TMZpz~np> z)xB>aLru_FoRHCJ@f8xGtx}~x+0ITrHU12?l;??gU3tH0#|=Z3!P3CEwu-gyiPh?L zsNnA;_i6QqufLg+!PS?NvUDr#LjlDUiA-)aKx8kg(7tA^HDeW3XhEpRzpc$UQyXdE zs1W7JJ|Yrs{o}bh(lyb@MSUb{BE1Ge*yQoN(R;&CnB}31G#h>X2=XBRIm;TXz^spV z83;`a-yt!+l!_C*(c&znL3-BPbUm-w;SHqCFGu?J?@Igc*)HG~7~?|>^1a4FUgJ2t zG$qrH*b+Ks(djmClRx)sP@k+t2E?BKqt9H{LMpjkI!?YzR<}Jt zWf}|4{-!YcNR}9!_H*I{f)J%8vLG-8@xvqySu5m--&svkqoCa>AXl7g!{eHnzd$4u z3}uh++{yv(3wV80+ApnGF1{!2b>$-)3+{YE`6RGSwr!ehp)f9H5#lQ;aK`p4(AqOR z23K;B=JTAJZBb(nKQteMVDxuKZcQB~0-g)1*``?qUUe&E5=_z4A87eW8i8?bb{{U=NR)v8W3)ed3pr z%C2-&HR2=;uJFrLtS8PB0n-aEq?QYLZc*rOR)QjZ+6|pAm+2a|kvEhnW1spXrbYww zqW1^7dONPp`c_T!Qo;wb`SZ0_zBJ3b_@UgiAwl20=R}i@BY~n0XsxW~+-K11fJ}lv zTc~%}`@+n?td0iw6Xh_ao!prAFPIqkojRgLh)CX88WF6z9gS-PTE<+Rp&thwiU180 z-&W}Mdg%6;<#IS175&x&%%H&Eg7Y-?#=faZY_ zAZ-I*QK9}lqqLpD@_LcJi|G#*Pg3NKYVH4BbJ+#(cwFqfEI)Q=EGj861fnJgXjZ0K zJBW2d0Q5i*A9zugcMP>EBFiI5Y#Ew?UU{9pyf55~AlC{P719!vGD z;%B?n5n_JJdS>x*F+wyzu>R|xx1T=8mKAlxn`~YbkAN0@;i(ght0V^X+=&BGD zVrN!z>$X8HpI*xK8Q*C(k;*WiCJG%?%!>;=!}8uI#VacYiR(n`R8TN>h~yN;6i=*H zH#gxeKTx3XNT=W;OCg7<&Z8K-J_w>4=t;HBsoymiR?-}Np9{o)Elu+FL-_a5$6cPMv3$B`S^>h=NSl|X<1&9lo4O^;t3#pJ4J(Azs)vGxf|3#yQhk%(@I>~%FF0+ew@>^EK!cU^{k$GD zh8m#?YLK*x|NY=-06{w1s;zCs6EcE=P|v_eW5GhR>)klU0)K75b#X>Z>vnix$@{jy zVS7TtFO1W}U;?bHezXP#PHmnz-?N)VL@v^}sdAt(1G&Qn{eYRtGR^&2MSb(01Za>Z z>4_8h4LCs4CTChSr$kwSjE=(eb&Sr z%Li?inOO zbpSzGx~FKhV{P8+7)M0ha=ARsziC2BG1>dfeop)5sj(ORB3J%+hzPFjo56XOgSFAz zQhY}HnA2-b9&v#jqdW5j!w4jTkJ`^~|E;^hGlL(3Z&qPML7y z@%R^G6cjE*vU;m3pdF9I`6K6h3rr8bKfy9GEKc@h z%{D2OJU~T%c{qlCKFb7{6eqj?i><#5>Uv$@|6!1p5JgH5P!KFqN>Wrxq@<)lQo6gl zJ48AJB&EBhq?PXOl^1H+<7{W+^}en%j^lN#4c71Do;i4{nqGw< z<5F-r*GINarDul({lqtE(;d`cT^~D_yUoTxkS&qZ6_Vy`LG*RY8O+9la-6b>g0Xqi zqbxU>ZF%1r+2goqQEDwVEq#%|RYkevs13#a|eI{ic(ks$?(fP zSS}k@T;Szg9Vw(lNRLhc3N=<`N#}`w~#rL(q4p?9Ilb9AKA z_YB4q%fIFo>pbT^x@TXWhy>LNqHu;yw(peABaofK${Xgz73&5GFHozf!>6CXPFjfKV=l5dbLkKbKf;kYe=z@z3|5D$b!QiQtgbc6ZPiF1ftXlI0Hp;Tw~=A zlMyv5LgajQ$DqNreo1tglMnP5=U(JIHLWhtl}XEPL5nv1l4A-SOc%!k7^Cp3p8DSF zWy^YrxMMvi1a1AuhB3=zHiauFta^48`B9J{5$ojBnpHm5`IOkuP}ESB~Naj#NMP zR+%Lpi63dgEa>N4J{;CJcC-WDK!1;VC^&XWYB0|Wvb3>=%g+9)yi{z>1HY@6RYS_~ z8hkFA0Ugf_UtOQ2AJ(R@(=3&RVfj3h53?InwIH5wjP@rhti^E-jr_v-jrpwA``nnJ z#?O2W@NS#JVh{W0Y*KUTFFKM+N8%V%g01I?W{y7n?G^-Ft!^3Npph`oJK*26Jxk9i zOTc83{!pFhhj-aoX40l)6LxDskEv?cnIaqSCq6e?V9nQNpSw24b&)2%3g&~g#D>|F zqAy#muxq?W-ORwn+EHR`dW2UdYqcSw4&MEFc^9VU5j;8reOJ~y-Qt0|*RdG=H@}JBp$t)s?QJppYbam^-nseV2JArTVxO#NfG&`1|}0ZbMg~nr55t+%iH;yOlC3L zQ?F{WZ#=yjGf=QYL*?zg{e&l*0bLjup*sK}LG81obS+C`dH)JadnH!n&fD}cb-D~R zc~>t(Y|dXYt%IW8mU3oy;Lf^nw9$z^mcJHVDc8z=Tp!!cr)hO%tM?7Esv~eq9FvVL z3(|3q1!^I3-4Xw3wifYfn0Te6bT5JiY-Pgc=6lgeA^%T_@W0=N`^|o3dl@i^LJN)z zV)t;0i7I}(1;xB}WSDC+f#v||UeuXx(@v)qODnbLod-U6tdAv1vqf?|0${qYKk$&v z<~IMd)a!GZBWH^S76DCvkzyYZXOWU{-K?1ikYGoe-kxtaTH<_yLiJO!D|}q#uR-kt z^I!w2hiAwzzmX<98d*GPlfZn}mcsm-@v%2psAOMCrpWPZ=65wm&$xGdy_Z4G{e+;N zu3??8REfWF0*!a%YjAJEyL_d9)XXp{-c^g46|!R!*&)0(B%}}6x3t!e8%_+nAFt;d zJ)Q}u+qT5B*%Wu{abGkmDLE!-i{qNhI!u?`HJhgwT~y)-S8xn#JgPgLxxTR@tEp z`|k>v^Pz(WMCI0j18+{w9b#g-Y8UKSHRb7M$^U-Ge{W3zinwa6-I5z+is)DuoYF+U=DV% zs{YzAAoY?To;%Vf(!S}-R@X6}*%KL;O5kz1GEKHfhd089jTg@=%ZK8a7SioPh z{E;E|UeLwaEIT`iOY7h-4#h+nvGIzTls~^#T`rNd?%eoczYQR^mS-r~^JR`7h4Ew^q z+e*dDHLnIO$dMcb{r%;WXsDM7u!e=hA6;-T0znW2a&eJ?IrzJI3hTs z$LQr-JM$46Omx*vcbwa(N+zmEWywrdbU$QH3DuZ{hO=yyM?N!BL&d(Mq(*6w(8oq- z0G1ivSi`+bEQMH2%f2(m|KK|wX88njJM8!r6d^RCsAy=aYu_q_%4db&h9NKFQahPS=Q^b>vTL%l(KaPy zfgSM5+9T(8S6$RfCauPgAgV(4(88z3S~6#!ek-l&h<NznY);+@vJ3b=()p;@Z=>%N_hwaZU#WKNk6Q|EX8|3Y7^zF);tDC@Omq>u%7w3(Cob+~>CWmi2HF^S!2m1N#4+kAMG* z!lRlg36N!Oj1=lYs;$vjvA2hZho$Lb6-%X!-6^LyAZygaYeST320YE*ihfwzZ(>8< z4E%DS>73}~9={t3G8eeFwM9|vxD7_CbEN*U$x5HQzzCJ$8**?)DwyQ~*b$EZ#!*|8 zb`(DZidnwLXdIv*UWglCGY8dLaou0Xa(P|Q#gC~~xiey`(-qoqRDHe-qv_8Mqr4{~ zI5Y8u{1TIS`D(N9fAojjFImBs=6epX^)VTOCj*$qSd@NY*s5Rj;YkNkNmJOaYRp;LOV~#imx0 zo9`6w?L99`!UA93TVr{wwa{*j?Q_a7mmQHGZ%bg{pJpH&xN8H?$iibK5vNW(7j-ia zaNC7NAKr-T#L9CqPbze*DD#Zw#;faLIz~n~ftLyk!>)hE8l7%DgGb8#gIZD-iFFmn z<2p@bCV8qDHl_wErDsP=HXh((~f(TM6lx)0BkXB(#5pM}Q!>RncwU3ocRrSRVq zo%oc;RRJ=LfleQy1vIi|*eHRpVRF_$xXsgTYwD4jyv0XY^ ziR)`kYW%Kbp7kTv$7~2nlmhl-x}vyt*lm-fc2iSFQqSD9J$;eYLr)}_ar{u&!Dtl{ z*`mey&GCLPo9C+)oguY{yTy&g#P?fv{3zX1d(WnBSXy@QeGu&|nd;>GfMsckFtXip zi59JBZP0c7$#K`E#tkQwBkw*pr@sk-%1=CrPhvw;i^Lak7oqYpHk-zi+agkr?%g!V zRz7}28yj40t$xO3J;8F*nZIQy?!4my(<7nmEjp=v>V&-d$FZ$RoE+RT%@@!DBZ1mi zLZl1>%%G?=!uaqQ*7rHxYL)L4ADTc6+I|p7>tDXc;=YacYUx*lXIcL_rxC+V79~;5 zkR!tx9$>-nJW+fJ=k7F+zmri||I7(xIW`@;>i;QhS7?Acrvd!4e-h{rF&~1`1)E-8 z8_GM%|E~({-wUjOQ&9S(Oa1CKWMQA|CttsZ8;T;dePoJs*I8Xru@i7CRZB6-5Tk?d zc(hs7TtdtMstNY~`4yLSi@Hr;iD9r7gF^P6VjzuZeS7BCy(ou^GF{U!@=lJJoTncX z3A1woo_E$G*%q*2SNxt;E(o^*!H&t0D9C$a?mH$jy`h1FDH;K$|EzX_0=D=}F{{0p zrZ1)L(;*AvuR!`G8j>im6Xp-Q0&@p)eVSzu_Om`{QRcLkacD zmj?laI^1!Z_1>Y5hv__0%|`uhb@M%AK$OkB)x})ZiBbX*sAkH4)hJMrM_ijfGz^<3h)hswMJ($vE0aw-4)@|qmSF7pD?i=TOxwhv7&m~`N z5WmY)f1J&JByapPHJT|)Fg^O*U0dkcA@#p+7&t0tcEvTf)n@Xk!MjgGScfwFI;Nq5 zD60-narpPX9p9W{AriuIujFfzuld84(x(Y2s)FCArrzW4(KI(w!|az1X0{+=astg= zqecH~R54cM^p!-HG`06xn_X-FJ1AzxRr0+cc>VWp-)jR_))nq7T-yVRjN%r2%!7lRb`gn+0n6U{m|%acQ<(R-Tlyui;F+iuW$0J?rX&*C%b;j zLJVjpHFzj)*-n~c^zrQD;Ns#UWmzAXY*pptAV5_Q-uM7=I^k zj68!sTYma1I1UX|+Z`$u?rGf__>H-q{MAha=UuYjSh&{eaRoQqd&y(`E4@k_Wc!q{igVM4KS!GXS|zKpx(mW#srZ%P)&mNU7!0VRG zvkGVEKFY3l0q6nR_kU$S;cucix0yU7j9NWPd*_l;y~nwMjJZSP2U4yFZBFIObzR4w z?|-axKrY)~(Aa;d{-wrDU+-PgR_|@C8z0lg6qbeL)}`f8_S$op85HqKr&E==?NGP- zef#k!kQ-BG_=I8|pOR=h;j%NP(a!M+vDUs7Z+4!>?IlVunSOFU7`9nP^0kg!wPT{W z%+9D#E{&`ft{OAEH1_F;>|k}ue>DEN?njfkm@5eR6%qpDvImR6zaS?4ov+M+z(xMh zZ1MB=Z!%)1?pld0lN1-Pi~2+OU;PB=p-pfR0+4OGYIf^65%$o~kTHKM3{p+>z-^8w zlX~JgO6>P_A=6ls@r& zol}#k;2xCvpc6!)60->WQ7LSV@^sJL6J)FY7Hk^oXU~QYL^IVGgxh{zF9p3~d;9SF zw@s;>q7eq61^=wNdV(QqM@%-W)X7V>PEA~Li=Vd(bHBa+uJJC4!^|<;=|yY1ZGX`l zSA!krD27Z&-fVq(tzbeNR)QwW$3g(3w$20pV`u~Q(=WVQv2p( zkUk~ESGuIYK*2~ABgcIO;^>FD>eSq+)G!H>9WBQv=5&J@cgxN-8sSpn$tQ(Ji9GV` zChk$cC|~xQGq!$>u9P$T+)ZL_Zbf=6_AugFbUi*6I0h90j-s0AGDx2&JkiN zHPvw8rC%zn%i5FHHKQwd8M+Rxy0nry6G0PTthnph=4P$KXM3?{Zm`mRTT1B4EpAt~~<#ZINA<*fe-!jgJB3PxIJNxW|-7ha4h=sxU_bW{QpQk|lbMEID1_UD)N zewHO}8)BB0c5B;L!N?1ab*evZ@*D7GyNSrwtHzQrM`6EaCb8%b*vmu z##C+b{xOba&9>?X2Kz$`EXH)arFOMz{yu_A@>om!96=31&622ima@xQh*FmR3we( zFkOyTP}78n_uBLAnuk)4;mg6=oTvx|gx8otY%+_^O~htOf2RKO^9DMAhV=WX&~fkm zL>Os-iq;0u)S@w?Q6qMj{rH((ptY-RS5T=u?0GZVLmA&2ow?^ z3dEZh*Sw)iqA=qs~p{i5XdvW;r{DXVhH}*rWGUO@w1=m7nJL zLW$Q=GL)7(?aR&-ef2;TvKgDE(9VC&qzrf@{53p7MUw(29{>{IjdruMG~LW~d80Uc zP99u?LGz-xh@%&!Yv89iRRt~Cqlzzg)>aN!ZqW%Wz$^>QtwGaUud{He0LEoC&P$#> zoc^@z=b(|!o_js8XFR9kM9TIbWgN|Z`4QtseSVUuf2(PWo7s936|s7}koC8ixNE?} ze#4=SiI3C&l1(vcP-o$$-@>%T{a$DJ4=lq!PTt;CYLIG{K=RB=vN6_r-+}x^DoC(r zCH}APiS*1Xdm#$&<^5TD%lskZSZ^-J`p5OijMNr?XvQoWDiHF>C2^7&*MBu8I*>0o zFlCx|pX~}%iYa^meVW^Wqj1D_P5Iv|#lG}^$yUI^>hAQ;M^ov$%`_cbh105~rT)$T zz(!632VLx-leriFt7?MW?8G&|hqHM3895wI6j37>lbcFRyDi@%4w{#k1kg2b5p)fO z5O{S{Ev+N*z1Ypui8ued3o8v!;S^|hgzYPFP*Kg^>03Utf^bmj$@*WVHq#>`Baz9; zk^%zmrekp6s@5{&J9>LFwc0~s9lydOJyHho>BJQipx*~{ls{wr$$YJ;vNOOb2}nsr z4Gf+F84`j0_Qoa-Xpk!)=xLb{|BBHhCo3gcVE9{!1fPrOdy(Vu_XTi5cSN9L0rm;# z8CV=P``(po5$M{y!g-2=)wu{tqnp|hOu^Sx*bXQvy;_bc`Z>@m;$fBrL|P;&h9AVm zG$3x2!}a|UbWS#Y*~N@%4I^)^IEQ(8)%s)0>@&v$cd40nfoySM>s^#m?xNrY!i`az zJk(er*`YtSuw#?t3Kya{=lvcTQPm zlDQQfT13^&h2$IXj-o2$P$K`EK51U-lc*V!kzV z9Ny;dFAp9GB(}{B#4~+HNtN*EoHow1DU~vDz_IPH2yu<=ocEtjIe2H2BAA*>-ComD zf+`!^aE1&V8oXD9gA-p)L8|d)INx~YxyGc@D{cmzJLfF1w~2{wAv;Q#u3P%Nr2mI{ z+tl2Kbn(5fuPd0sg~(m|pK^_X87M6WK%`g)oSKK3^=-JQ`oGI$?K3>bZWgQ=z_$6X zr&M7(JKE|BAbiGPS5sQbx~I8@t<4e#%dzA_%Qc{kHG*xf@SxO0M2~x?!g!*MI+W+N zjSV{zFvk{BeJSSSP3x-?WbT3&Z#!S9?B*`Il{%0Nu?za>=;&Smt_V0{UR_HMsi{3h zBWYCwzTZ?3G3;@eJz$s?@A``IGrk;y5JatMPmOX6c=i4A{kDx}Mnm%2s#p{o@lkOOe;A*`owlt}$QDl>^3=l62A^{DR$ZIl z^Uk*<-21P6Da{m*e@~T1S4$kUB>vErHG~7pZj^elfHYg*qF)nw;~@Tg2j&Rp*G1t>RRm(uV16bipW6_ zO|`0P&P0{s)wI%4^x<-hzUzUDuAf-@z@uGlDK3qh`_E^RM#`d`ReNW!5M#$9VzvC=a7=Yu+Q zlyVvJH)iN6WXv6Er|PUDra5Actzk$Oqpe=aJslAwKYadr&aEQd=F*(&YjsM_P0&D# zu|&}P=`AE1Yd^?dC;DroeDB!gRRoH3v_D}CxC3CHf8bMiH18+PseJR9vA|Lk%6&`9{AG!>BX5xChVV{`Cb{wGd*J^FlM}x8 z7r|)!Hc-NPK>>uhpo~$G1Jq9uG@D7>YufhlPKHR^jUvNj6ndJycXN2PFj>||V@Uib zmIdUtqR7N23tgH@d#}IMq_yhw%xBAVp>I4q6}APWH#ZBW+rmsZ2%=(srsln0cHQFyG!FsuVPDuqXbk^}uE1Jr z5_5f{g~~4)^_88I@LF_L%I*s37p-xEJ2+AYNzm~ ze%2ZIk?}S#uDj+J+rPw3jvcpx0!#z>g>O&m+^D#CVx+h??Vb1;(j$u*`ts->r;_XM z|69(j019q|2yxQ7b`An!|DWLn_%SOFhn0bAuMc3-0u!P1a70K9eGI*N{l(25Sm(~2 zX_qM>V`5{UcWSZDlxJoxr>)Sakm{|UP2b%#kxHm%vfJ0F2w2Cq1J62=$~zQ*pyfQZ zsUQRey|DG1^yG;lEc)w6!6>fA4hs`0S=$9039b|2^k@YCL)p40-+|c17MBp0J<}>d zFv$MZLU*ax$h}wANKZ|UHFE|9tO7&;wa`z;8GU0zjgR~4L19#z4B-(@yCGo=VV$IC z3s%_s1I$%PyLJ9m15yu~IHoEbl6xxIuL{_}?if&d{${ZH@F(g|rUyT&{R1g zA`)C-XgB$z6Q=P8m({ob-QgfcSzm@)=xfR=C|;WoMNJjXmCLKvy%*khakuQ z2#4%n5{W+ZO!l-^7@Ka|>lS637nJP8&j(_6Op1D1mV;p$3kFJ_o!OfIouCA`y&8@X zs6iFAhZppiBS?g0t?BhYhGnwy(LDW=7G50PjFsU~wh95aac1oLySoe$Z@pTix7T^+m#rTY5B(Q>@$mFPZy zY)}x+3?D#lEXFr%Y7!}U9ZrTxA-j+wbR|!Vot3q}P@R8<9psCxK>8hV!lUHt{v*jR zK2z_Eg@;EA9jaQL1EP1ffN(Qx7gv~`AR1FZl-wpG6VuS30_?EW)_C;VpR9d|beayU zI6442J`mLr!(*$BVcf`0uQ8)ch@9w7co~VP{}S4D`cnkzllQWcT^!mDfkfyGIGLZ2 zDAmig4Y_a58@!+7nhsz8{GyX4LTCL*u@g9?d2=A@1@WRT9Z$kq=xJfm^j#UfsZfS2IXhA_mLykm4NEDmV~z1B(MHUQeCsovXt?-P2$Frqb*U)4T<&o`&K!>G-`SM>NPnu zf203?MujJ6O+XRRcv8^tkk$?dbUjX%PHc~&HwM-ZK?~NJflJdo8mH5)1{|Sy_!#5n z)bWki=eDF64DB@ZkiqCe@F*HZ>OBd1r+AcXZ~8ih5!Z)K57)kLmxaqEWtS}nFTSO} z@Ue(w4^q2&0K?57Ca=H8eUw^<32NG|7d!~N@Nx_J+4#ce!m)OwCmzu`-PhbPJ-s{i z#^&7?zg&MJ;wGj?;u)Mqpu;k>JxEj*{WEBZ>EMIwcE0u`(lmxRg_(_f z1EQ#;T1#%gO407{T-xl>-xzhP+K^_oKwB=zy2c$G=jm;S^;bFS=P?^c(<2~hmACSC zB$R$3QZ4nDxFL!%AR2XlBZzPJ!LJ+3rJK9FcI@3fRaUAE9E=`lSkuD#Y({)9s`k;) zX1vJ;zpq3f#zLz2Wqa7rM*qkD#xxfE*SS|^4_Znto}BoG-m^!E%NJkcz79?I-bASW zrsjzp|F3X$fEGoV(m4u|4n!YA$?uY|&>mW1z96LLv?s_1rey4UwqT-1z}1XqTpTb{ zfBXmoXg7izesQ{g3g8G*PP5t18<35wdae^fG9;eIX&HjbO8+}lHRJq z)KdKd)I5Me_uhfbtr`6P^KNl{otU$xo2mgl< zUmPN;U~Rl?!#>zCe*%KBAuHfhUlMOtoxHNL7|fv%h((xT)dHaWXIoo27lI9d2+*Fb zlR^Y2lvb+=2?+(RaNBG)8kBu1`lzIO4FscI=VTaGQ0%O3B5pKn?DUKbR3LZ{2H=vpEnr=TE&F4-wUFpvml8Iq255Wxn)1Tpl=ZMF9MA0gHNk_h+; zPeD>uT4pj;Ee!#1z&M9&mj@3X%z3n*0O?pMh)$*EtL-%(X@89VwAeTMW7C-p5&Gu& zVwyjlgV>@GOa=?#kE&WX=(v5Kbws=`{f<%fja0;;&a{eyhT+c(eS8eV3Mo|u4_6Hh ztx@vVIJc+6ghd!_CK~9VxDzGw>rxHNYPOKgvI%8GGv_g<>M73$`H+?we}{ zLCb#W_!pk1O)hlNbT_0n*dNpwUl=tCNY%QBf8!2Rv`q;D(drT~b_sIkAGDW*OLgKL zWP$TLob73}@`LIY!-FSDZ?x@su3#UHa*nJKal`M$f##vGSU<@fgsu&Fp~mbzuEtCe z2NH*i0k?buXDE;e`kI4y&^H}DC6afv4}-7}`$D_<(?60PJ%Ji6H@d@1PV!7$X5a#|HOJCda~3hQM_E+4Zo7F3|0Bg#+;b zBB~L0)|-_(ps8T^n|FwwSvO)&;j3vGcY4sd!3|Got!gQUuIIXYvylp~i{O?noeOU_ zaorB~@7h1dvtx7(sB^Cq9T*VvtPxgtJ*}X@JcC96F{X$OSpDY_&Ch^lAN$Pp~9s;=wv+2>a%BdnPVaQ1of&mGTwdC}H}>w64em zj`k#;eQjURygWP1FQ_q{;e)IfWK2vklSlADIX@WTQIdHuYNnHdw} z$MNLJ6M!$xl^#y4&-gDx%%O<5_|oBsx~cT!HjK4fd~fN2QyzH3Co#_gJbHAwH@?7M zR_t%+pPXj+;U>&d&E|_OtUvoje8nF39>NjJ*Dk!eR#XxPzWUb3`&I(C;l@U0C|~}0=f!a45bzg762Ii&#s>Fz6Mu&huP|# z^(y~K444|gs5jSJ$NgC$z28KYUf883BC(=J^Ki#>-vdlnN6Uo^s$b`-yzx?0DxIWvU+VXJ9(&SF!7LqI5z!wnal z5$rl#6pu<`ktU_RE zrr;?RL*RHR|9MP3%%1q~!thsX9e1k^tv&Oh=#H{r(c6F5c$JHjOG|@J=k#*PPOp0^ zX66|hPOy7NiaBPSA|ZaO|CooKJ{V$of9Um53>T<&ft4A)O$-!toRs3?h$_oI@aDrk zU%B7QT{N+UghR^W1(mEs0%ubw|5XQ!8aw;?=-{+pU7n*%hhV~|&LN+BSh}}ewL&p- zoD5kWfuAPg5WJB3ovin99%D{%HNO()p+-IOE`q3fcy2Sw%gb94DWKh7*}8}~t005W z9-iVp!;9q0O+6&U7Xd+qL6H3$86GYmAb<=cZsP&8gOP?Swi=r)u7!^fGt6@`>sdcP zd$PBH!1@ac3PK!k67YxJAwB?tewWB9N=u_)%TqcoI`1H@s@kJ!J0&O{%n_!|9mrUlH8ev1PO$zMBYxM^evF?VmY+iz zT3lmrAMS%Q)LVjudo63`yUxx}u#KuVbEe-W&Nr`pr>k(Ft{RF1aQ|J-;gY9=-FI|w zLMPM&_2^%JFtW;-Zf3M@_xpZ@ELt>YbH8nZLA6mLTGv!f7c1!l3 z*|A#W_Fprzqm>~xkG{X9yT)DLZgR*DnCwNt%=UGE`R%SZ&RiI8=m48*N%TsSKmn1lo-WM7Rk!{I?*M zBY=(AxYhL~5xIE2^rz3CTyyR-2qlfa8S84$Jk6y9Z_Ae~nlh3Fr!q{XIr;8wui=ZM#8`wGR6BezL@@2+Llyfp zyQhe;-AMk~CB|cizfbT&MABC~vmRewzMxMU+2J?)mSs0K1TlmMeK#nuMOqK7HV~jbtmyoDAXqM*s~zABmhaDg{I_#L)v_HxZ#YJ3FRZuhQ9r2 zs{xS+Ja$MakXp6W6`O2*4IMDzQ97=*cfWWSt`i5>#)vI<^BZ5hyN;U0c=K8nMxne04xK(u7tbE0D3?ZrqM2u+?@uH)ni4CQ2N^>VLdz!(0p5Vvxaw-dor>MO0CS ziYVTdz4}09)mvH6quXM{zTa|-|DT{RG#kYj<;knk{Od#{JIFZvSfU%ebIN~xDhV4q zUT`v+k5{LrrozuL-gu&%8StVdB&0 zPc6#AP=*ZSu5prK(0GNeH+^GeWr*DKNv}(62742Fu)CS%bL;JUreDa>z7;*Q18`#E z@GUH zufIkw5l&qFAb`4=d~^T$cPS6kU(ZC}wf^tpO1Yhx4DeDt=PnQ1Ear9kPUqjPg0iP+x#xpaA_0PMN9lM(X*G&}=2P(yX#o^oFM=Z zx-ibSDF!(QSMrkf9uA8$<;jb10im=U_Vh}_QJ9~)A@x+eC7}S5jByviHhI||5#a1v zrt`ft`|kct0@W8p4A>XmQq6!>BhS5ElxOxChxVpwsIEI6d!9O8ALW9ShYZiROIFr@ zxG>D0`63j@h$tQCv|}M%Q2qR^@gxGhH23?TozTIp6>lf#Ju6NO>S6OZ_tmnfRpy@+ zQo*~j4URC^mBhh36|7)yZpu*KuhGvs8}=m(MocSN2x?zLuI~%=CZ+U&-I?pFk&);* zS$B8$=PH%E30k^(dL^LnXjFdX05}5WyGW8cEOCef@6@Lb*}{KJ?00r{X6A>Zdbndt zmvam2|60Gt#KqNt04|6EMARW^>GN^*i1Ao&QEvuhgwlu1!K$f~SltP^QQ5Sv6?Wds zj$6QrWCXVQ3a^d6{wisp3legm6OA72A3|a?VDG1#_IS^lWO(5RL4u?m=WQtEfyW zj#S;fmayZYxPtZlS&}3uF0+yx*7Nq=+Cs8}@7sb0u^z-%#c}%{3MMACXkQVMJa9PQi{KC>~sr~|NVl>2nV!rD6iY`wYD*`7v=RJ ztOxW24@7C34KK$ON`le9QB=%RRp@g2cBA;?{9T~vF)#_2gJmiZ)IcKljN_rTF6_RmrsVi9} zm@w){a9GAH%|6D{eWUVlVEHHm?dwY~pES=Q?R13>syi}Wl~2#`kJomz*v{)KDfhZV zFP5e!KJsM{_sv}6Y;i>67kpX6XYr`lqC@&|RYW&>-FV~ddjH84QKer;@LG4h{nx^M zcKn3Fkc45eY|hF%4;M7#X&(@romQJ4KXxDUx4LIAmwS)eb$58zbq?jE{`jsu-Z{6h zQYcMYcc9MIL-MY_Kh_0Ecogo~l(OA7zKkQWymcvV&Y6{Y_GXw@SmG_4&mI1>qvOiR zp+-q*vO>yIwSz4I-NK!gH0v0l_U18w$XJK?=Fi}(IA`zK7k17l0qFyoX~JMzX!v+#=wKTeU70!M_h4*u6Va3N z=ii^vvL9zjTt33^#-C3+v<%F8jrqW(1HzQhCEMrz)9?tF% z|JRa5{YyNSs;Vle6HdLD4+Hyy2`fW) zSs5jX?P>+AJp`@mk#Je*d+N}`r0$Bg!H)S}@Rg2yyamHmLPtjj7@?i7w$8lm;pas1 z4?ht~m(tIF&{G61FHantE+;x8=}KS&A>&f@n1R81rfkr8=fkzR9PgBp+#9Dz0p@oU}VeHw(8;j34|W_f)V&NtqKY#0?rrRF{*z>$Jl3 zls1o+46B^xj*)2Dv2Ss{6&gcf$@Hm}tl*=j7}QrGTG%t>rxb!%8lMjGKTcR5TQeS4 z`rovR?D*lVqO;wl+$v;}z3z*AKXybk#u>NnwXYVNcLX^}FIk`-^YLtj3Z*sml(94E zn8B@ruen$+zKH(7{ZymHJH&=}#9f|#@68j#r6zn`+KX|MD4U8@B%;V)jUjW7zg4X& zJzDih8( zZsd12XIFGbis}0$PZ(Y9+aAhy3!yx4V8UC`Tl{JDji$S?;KBDz`ca<{2~lwdK_W)1 za)FQH?A5gIKXrTdo$?cWKbFuMu00m5v;Df`{(qlX?{xTBO1xd5A_uVwp$3+z(=@f7 z`=bQrDUF}cZZYfjhM-S%l3BeleQnvEXc=tvk)QtWWxGEOKBHIVPf_EG#SC_rEBJMu z=D*J>@k~<1{2ioe8y|fv91#P37<9Bw&^{WCmtqM@S3>< z2UXkUR+$H0_^(yW0@NLGr#8H0F5k$h3LiT>ak;ckOTJe4qs4d|I?4SR7?wMg96?r( z&P2H}opZg*z}DX`M$;peI|Kw{C3?4z5M#E~Q9j*nlJU~dEzpEQ@~zX+nBJtrMm`;R zR(7`EPW^dr!_=e>eM?IVDT{W;llCFBVwVsFHG6Le)-H0rb_oa!gb}sCA1CDa0@WJ! zYMlA+tXZCco$wTVVRkwg5PsEiOzDJNd~jfOZDTpZAKCd9Zh^5{`xk13u`rus`t;N9 z&zDPYQ&4eUor8v;XWb$)D(bDaHVx#aTxsEubN*=h0+$Cw&;4PAQdvM#^8sj3{_?9U z%x+Isy#P=-XDZpdO9|@3z&UE6ApPN30&7+0uhbAE2s^IoPG&4DthYi!Nq;}C%=h`9 zwQs`y4h9|iN9OQjeU0cm0t^f5VkG%5qtLL4en4LD^__NWh*?IYa>c6Jkb<_?J zK&<8aRY<|PFj7w1ndkiB=W1d?LiJ-)#+>uy3?sgXIg=vUA>`Fp@e1O-0BgdTb z)CYZqlg~?Sq=_uMg8z%GrRQvQrlIzjp=;=eJpFB_`H3Lvj=QftjPa3(WH5bmOf@Y# ziQ2qJ@U<~%q_%=`9?MpBG3hxAYc^27y+^CbRT_EJl8h`vh3rG+VCcDa_liZC-BtWm ztRXWgPAN-8TkdKpZ$!(o~BDfE99Zc_w%H<~I(N79E zp}r+Q@gqdOev};@X|~kx7Oi}Nv^{uI>mmY}%e!VDgys2u_**ABq;5o$DO<#SsFZTyQF497n=VV(@y@> zX>NWt*M9T}H}}Z4aovWgx!!+NU9#z_fi=e(L&%z&nuSHtai2A9Jmj@NQ%>tZy%WmV z1G2UId~U-&h3=6TA!Q~7jEh_r!~Sf$O)n&zHc$4b^_p_VejdN4;pX1>$gw8_-=mAu z9jn=ZhJ?Da^PDj$)pcxE4x8>aH5uL)GV@i%U$uRp@CJ5IU#_@kO3_zEv+rKgom^+_OEHfw;rv~PewxF%b%8Q zYRJ6&;iHQ$Q!iAjlQ>H#*Z}SORKw+n`JAJtmSbEII+Mr)@CC zZWy?644Wda_61}o!2%d8b8K8VpB*5&T;t)<%Ul@6!7u9@1UaKM+-f(jL>e`6ufZ48o&gVv_oCmrf)e>xs$A-bFO) zw|EcmbH)#v9nH7^Opw=Ss9viZpG}bCRg*;we_j^3A zFoV^+gSSJ3Dgr1~Wg|TG&(?%G8m1TfXQfNA<3ytO$fl3qhArQ3#TX~K(%|gajL*8w z&Klh@>3LVtqZ5lPDE|#pyd}qr$Di#P8_1K_)##ysZ!0o*8<~)5mvYuOY?DER)V!J5cMXA+b|) zm?2$cOn6s)zx(FnnfHa+b5Rw&bnIHs?DuS*3+xovO-$kNj$wQ+=-T7B#o?4!#AK(n zVA#^~{gpupU)5pA!gF4mYa_HUGVE;%%8#ZGclmg~Q~!zNO8U#%bDxgk^&LOr*DB5M zs#!qUD1!KSOalhnzOgg>Jf)i3A+l+~<>-fTlu#%|k7XaPa5A4W%qo?{B*j!3V=k5) z+IUVC#U$VOX^kJcd&WCe{8LWS$mXj~T+G|tFO1D8GsF_-%`YrwV?#ZcX}@zVKX7imt*I)%(<@lUmf$C6vzu?l$6F-zeGjd zWj7v2JqZ}JM%z9^+kiS21yn;2YyZy+JzYy$GZoUKi{g~{Zk@8^<-Lt{E%tcOW?QWf z`e$U&6jaXN?AEm`2_e37$Kn_B$~wF}VrIiT_|#tb#N3`uW=gKdeqwL55^Fb1DX*PW zXO%~+S*HR8*gk!@aq(d7p#q=uGD$Z&w$LLawAK4bL85HwPoEYIqq5v2j*zJio|bO8 zeDK%!+p~+gE#z=5X~O7NZ}0=gZ>l~!ur1jp8jsH_wm!f+_7+>;AtwIaN9<5;`Zmi4 zRr!ui5;acnJ8k37$Q1eLxt&6xVKnu>l3N&rX*C(B4ZG31-34TGx3U|LkFSc%PHvAY zaor(g^Ruq8k^#=es4!BFpZCI8raY` zk5Wg6h4tAjH%htGS@53TPA$c&#Y@8Zl!&Rw25whBcgE_7b;B@1|JzA<^Cb{{WFc2BpPQuOI)fQhqTU(3-wVEf1?<+q?nq?hOrhQA`CBi*9>PdRr`8y&4zt}fWB znAGW=m6@5@Wb^9PtCAzboPIfbpOJGr>nXkv4($&7YU>U3lWGkYXeMxzW*S`ib0Id? zl*3lBD+aM205xX+Y>{dGsn7cQ`U|e_{|#G+&CMfcO267CE&B^D5$lv^rs1O8nZcnV;l}P_q2M1jJlSoU_HZl`(I1ae zpFaIs5U!&hSa7DVyRh_L7mJI5Z43?Iw3(=O)wEpn<_wVtkG+6@&Tz$JAFqMcHm` z6VfT&h@_}ANT-xEh;*lPhs4kw0us`tfFL2#-3^jMcQ?}g-}Aoboc~)(mM$1Z^tog2 zt2X@}8ix}Oniyk~>jnfueJ+iV6X=K~i_Lkek22=usc1r_L96YqaTHd<=a z-}Zz5*8#qj)6XdsLuFZHV#ZEfU^$NpZkQIZJ~0GuZA63*esQW?-I=Z_nJ?(3WNbIs4la`(7u+BB zK5$}aLXqlYvkXjj^%DZ@sWZNBLHK)wH56U%eZQY8H4Q$X5SKp`C6Dx-1cN7?@}C1= zOO?E(R>ro`GanrIBeW|=7@YtRgX!iZ!_A)0*KT8{P}}ChaH-yb<53NS~?(}0UBcND~R9bgWyRgKBJKI zeXi@hBEz!I;V%}F@fHZ=)5ZQw3Dl*TFGCSFsT;r9bh_C`0CF>)HN2f-@%Mr9k(Br0 z{^hGzf_axmV9tg>9@K%R7-9>K_gHFjBBH77lmV88K?9OSz<34+2e(!jyC<{g0l)rv ztbNHd{T4Q1?<)s+2DQp9kLC6$yId3;EIRI8R*ps}MJ?N9 zqXb(JAy;S{v6R6`>E|}genEND(3cgY;#UfFfp@v1m|-NrlR!t8Gp05?n)-7Zg}*3V ziNR_fjMRPz-247M!Up*)J7@j<`mxUpHcQKkxIj6aiti?@2-UOKg82d{{PR+w5|p(` zaG&&e(7JHZ{R3r7cpS=Io@%SJ*CQ-)5Tn(pH}aRK>YR>|B-SKPZIG!b(*9uo60~m3 zn|`K)9fSkV{pBt!rw3vFJl|}cWA!Vw>_9KQZ|B?h5~wF}%q3|9XZbNU4|}{kkFN6- zrHfTn2X5hcDa|*`4-009mq@Gtl;FW6pkCVebc~sjh^BG3v0imj&#n+$tubUSYmT&Y zc^^r9MAora_U)wG$Gv>x)?TrBWQR9n)@Q++waiBWe8==1bSU_7@5zV@zndOQ&=JzG zBb?B#C1!W4Om0kfdcqn8IN{m;bHY1;9dHsido=^v1{y`h_9f!5?QP4)doZWAYFWNB zxX>3Ro?XdzYHa*zZq6n_1=uyHjw)zm98WMq#^?GBj}P3#G0@Q3j4Q5cE{~R$O74OA z!hLKjATSUO9sRF8HDHLMw%j?+G3Ei~P0zTc$=L8uH{eK|8cOvktmKt}d)M-^A(iL) z(?@64rZYSMb1i(Nt}MFTFkRCIuI=TzwPZ9Q;-jUm9}lYG;J_CR9NFqi(!s>RDFDQ? zAM1RT_q7uMvzJ9iP6pmz?2LyVA3J29Ah(|Rk!Wq9F08scoGBur z)RRxw70xB2LtW}q7g@#mH6a5xUQaW2l;<%Yi%x><9*eD*fA6#mte6P)H*-PTky3?z z)C~;ZNvWuF#b#ZU-fbE$55Ym)((WcI?fLDBl(;1HZetk9<45F+KU2SUJFYEb@czzq zE903z`{W{EKID}(;Z*=Tqq9O?wkn(5Ic^iR0bq;qc!zaSZ6j4!bB~};Nn`hk?$;!9be=k!~Rr^XfoacnxPT-J2k9h8# zt=DKRjAqPfaw(8)USxZ8LB9}FnoWkDj_iB%2n1f6@t3VHv_pGc^gZ|6)GOPx&?mq3 zlnyMDf+WucZa#9wnuJR}>}=wy;T|@cDlz6VaRaNm|d@UT98ZDo~wz8%4I}^MF;7z!Xf`H%b0kcAf zfhG}l&M)ieAAL`=<1gM4(gCicPQWVdF8RWQ5bP$%06zoF6s{h$?*3wK1}G)t3QqWR z8{A%lM%I3+mLCTD9P0v31?CP0qG%-Vqov?knoVy|{ex;hXzoDR3?=T@FAlfE`I&9@ znB@5QcJTMZ8&uT)`0=C7`Ml`IJ0~X&1Md?gVCC$#tI2X99mJTdyK;X1;zf9}{wFj) zASHoKlRw84&_$!^taAVaQ~IehQ$+iGoyCh?Si6xt}Q;xXmXJHZX;8Yx5ekn4`o98$0fgbe|j5US`Oil$NIwnEFS(v0jiw} zP?m)AlLZ>1wdJEcrQ*ztxQ3V;E|JV%VBRz!S85-$rT+sy4WX?pOUUW3NL&_pm#-ts ze7evKtFzO+zx{K3eIs@(B|fRs{1{S&A#BuJm9Qgl(YXXoYCL93x{h>ER4@aW@CwBv zH+^K~^z<9D;?$tIO#e~A+Hj?NEmi1{pj#p9o&MaU;C;tPA=5+n8BS@bM$!>jl7HuHa?Q6J<;Tcj`;T)FFv&aRu7X*}3yx@Y_kDsl@ zjZPQ_mkP|L4taEW;2?sv#-RFerulo=pn{2PnHxz37ZG60hI^g1l%5r?2glN|&+&K< zC{WDUp7HgohzRuNhnqcyo+xUOMF?cE(bFUCw5$E&kEOc&(mdIFGZr;fRhY+EOXO$- zXfS($Gyia@`LKR3g}vhv>2bc!`F=qPn@X@36#hQiJr6+Oti)#y=nO3Md0OchK_mJ5 z%OhaV=ljX0+rRr*qx#`PHw-21_t_lDI(ob#$3zDDNw=|z9###l7M(sIVo>5=+&kxR zKhyBJ_zXWY+G3VnM?1-@mhbWRl$04Jt&hSrT92E z0gUo^Tf&_qW;t()X;V%~ggY2PC3NC}E>n)xW%V1+B)roKv&($fzwZxvmtptM$!wEn zwUg@GR;W@7;x!V|+hp_9d3b(0r>_s1J09>GVw>B|f|t}TtuBKw-)cx~8>+E0@l)y1 zk5|RMtH}UA(!zf6H4BIT@2fddo5QF+JXw)(HsdePr6_!3RTQu0DMjbpG~#FKL>4o4 zzC2tp9p1lmIJ!eXlt$%&L?O61Bl3T-nNeCnu|7_!3&Wud%GGrI`aJvx2ZH&p{x`}i zt|wZ4$y8!>uSV`!Z3zsBtK!tY8>6$~>3s-$@jy0_EhB#db>Moxv6ksGgFBP;*LIxLHAIw>L`yN9}4XZ#tsh5Hl($m2BRM@Y{8yqoP9B@&a5vVxBwgs1ea@bY0o# zFqAXe056Y#eO&W)LH8beCfaqN^l|z{y+YZIgKxq@!|PMxrz)FW9FV7QCYg6pKCHe2 zKrU)T$uoO-6I#vJggvdmr}gHbKKuM)Bo?WZkLb^3?G(57J!BR~EDoyyl65dVw^f^P zDVt%};|SJvvf#fr%m`~yv_T`NuiRv9b;G&=?}%`G(nEa;$inNI&^v^rz^DK21>I=` zFBeAOrB{~!p1|t-ku5t#zO(T!y1!6;))HEEwvJt07o-J<+4-C|x%eJ{?(lnVZf+mD z`=TdfTU#64{z5}CAO~)?l+Jk73KY+Ye}NOf4|LWkB9EHSR>bwDoPhm>g3sxRpWDIg z2~gu*WmzxObAz_}JLDEvbZn0wp#{HOc2tvIX^pLe@46m&h=~Ux1>m#Z31V-#T{u!! zHwZTay^tzh2_ZH0Qnu(lQ0%<94*uM+vfRg*b!M*wd+E2gdvc137Y3IgDD0|52GA+A z3=Eq=C+8Teu=#{ z!MOVpKR;nIK6O&ijA2_g2|M{K%&^9Q+Rx2QhkAE}7%$HZsR~tkxnJ5u*~m3hbr}fN zRIqb}24CC9txaRL9=#KEskY|5U=c57{%(Bq{O>ta5>&928kKI)kb(VGcmOWSfLBzv z+HDIYCV(&{ka{g})!}eJ!9>E0B{)LS>;j=tfOy3zvAn>8Pw4c5(`^86Hr7rW8o`7v zk9gH)xlV)-HY^g5%ja;Qi!QoUfge_TB$DqQVFFl6@K5?0gXPWtGCKOio#SGc*Y|z` z72Dq1KT!hzdd=RB<~1$xvFqTl_6ZUIs3>EwV6|M-V9u%9+Rp6^MYrG;1#N*mJ=YIz z4f!mo8`@+}p(2yKtk~t3R19ia{T7msG|rN3J3d#t0n2|Z4W})sFyHmcHnf3Sn3s?68F6ftHlUpcs$h+Su`*-)3&DY`|2d*ROb-^Rq(7yQ0TUv*L`XSF8I z1sW5JHlXWO+yfr%(DOa$De$Dlr>6E#6setjY62CBA3Wmrc!82!3!P)zXzaqfEqK^9 zgX)<1#DFJWks2buov4wixn8O_{A;|28sB>+e1VF^3+#Ow@d8$|Ej?VknYVLKxCJWTlhsI84zvC*YMFVU!H=R8m}>C> z9-(`Q4y8(Hg z9>AgZ4i4I!)d6|X)Djx9;Z%uM%Frf$cUMo(Y1$`BU-Q9~Hkgb?Eu-Q*^@V(o>a(FTeUrF%f2~i}HUvBPm7|IPwR>e<(deCcN{+R&@ zH>8c|;35+OkTD%_$y-ensy+A}T?QW?ADc&)-2&5q-HOJG;kVBGKDUAMJ<*e{lzC*L z-+C6Gy`?gXSrOg9{yodG1Y&Prwm1xJF4((K)O}KKVh^n2G|zDxu2O0x{HB`~4dv05D$W z2_!X%un;{lnj%)*9F9B;V3M}6Wn8)yWx}&n9s|i z6-5=EQe%?!jw&Z6BF?V;wJiMR$&`vHz4FG}NV|2kx9iT-t$ca;@t6eDMezm$1!bZ0 z0?TYYC`sj8nW48KoWCNgP#{WnVcYKmHzYYKy)J*o50zdg!lg6yXcl5T2E>g$)nTam zjYTaYB~9Ctmour3egVD0x>cUEK(PbY95Zt;fW;I+csrW0zw>1{yT^Ag>pq^C>ia4y zBp-4Hr)?jW`+Q9~FDvqQl&;j>pRd$7m-%duwqqi9`|E3&*Q3>J)HTyq{OpS-g(Vfq>P*gL^m{sAK+yirlre+|MmNRJV=! zo(ln2-YdQOFW@Vy4!kfKgJy+sa7)@i-@M0KG)xnKNhO#faMYMkVbWteZ0FuLJ-(M!BM~$a!EI$7JA(xVpRr|5MW?iK9T}KmE&W04j=96QG@rx_15~<*6H*>WTP+G~KC%+a>tdG$E+UE_Rc;u7j#3K&4$!JU?bH4M?gN`2OW&=^du_gC} z+tqnZ_qgc-3n+4B~ji>0_gm)LZ}`^;k0}qQ<7B& zfq@$iE&&hL_&HI`?EnsG#xTd-<5vrB!dYHV+q^4+&V2>`&BHlu{bx zUNRPYTZ3e!hH8mW6{fD=D&R-9WnpC25T(HXH z6Gf6KY%zU)9*p`6bv1vR=U$M_Jm;`$x?}k86(TJV5$5!R07V;!-coYC#pG?!DvKMl zcbc(x8OTqn;rIItP_{|>vCDs+GvHbCSLS&Osi;duoJNr1+DT7J)OF#t7W>>d4^1gd ztCJ%pH}}UAUvO@=ZNc>_8ibW<7>84d7weCE{>m$x%K+Mg3Q(E4%g|02sX2AF-u#Gp zOBtP0=qjAV`cJ{?#D#31jnVTJTe^}L^pq7!`tV(%PVD<0> z2|hrb@~D*_S4)>+UV!q?#Z5DeY+_26Q&jX?C>lemKrutJ)`12{b?DXA)h$z{w6(SK zg($V(qH+O!qhN8E(Z_~GC|***Va33 z4KR1k{V7PFvPgi^fPvURcMyU@9W!^Z399JTqJm6t{9el?YLOP@-nfkwc1y08gpy*D z>I9OKP*()34=!B)5>{6dGXTRR#pOd*SJ@$FCsVl0opf9L0H*+Da;2nipGSsEu*&UU z>naz)zhSuAQpr26w}eTprUhoa48dV{>UBUG{xl>XnvOM#hfNoykn@=SA{ap zUmg)}MHx%gIu)UZiM=oXVT0%oQ3d4|vE z`MWRKhL{^WrY|<=rsFl$>Js7I9}>DmeB~O>vsSn~(Z05zT6K(`&@#klG=E^g~{8k7* zH#p!MaAI=4;cpMtlAr(n1kdL8pO?l@GJ3G>&zyNU>0`i`Q@-L!YfF_rHjCuzk&|4^ zU+;zZ?|pi8>f?VF|EY|1*YY5}xGbm*nz?V{DD=9xRPx1UIhSD2W3mR}u13k)HqwBmQwPAAQdVmU3I8y*%CNgRVjRsk zO`hTT^XDKRi5kEf0tUX<^e_)72u|2?2w7eIPTi2${uz|YVB3Mf5|)N2fcj+upFAmP zU*(AK02s1g<5 zT4N1J_aG?ooHdOY7q=CduTe2EZJYoHjtp2ZrK^7m<4VwrGF+lzV3>fkXxJu>X3_e{ zMOJrEJqH`EJUTjBkX|!swE_B5SZswU_@shcJ{JilCbe)ntR8MW87xmtVlq7?S#Wk@K}+NxKE zX?*OO&OdN)a@K*qIPH#uk(oIH+_E(@dWoRw33JXl6!==dgt%)DU4XjY2VpO}7TYjK z1OREndqlP(bD-{F13-Pew!#@o3w%@rT4h^+>Jde4F)Gi@9gVXdbVj8*9gzpe>m5{e z=Y@sDiTf+w#Es7U@q3q zLTpht=|Fa^_%G|V$77@^fv+5N`9VG^L)Lhj%|(1vH_^tyJKCRiS}#lalLu0$hG0LA zy5}8@4L_?}W3W(#UR|>nQI{j7XQMg-sXxYyTJux5z#K}N(L9+jNYe06JGfn6>Zu*& z>KH|h#Swx5^0y1z$?H{fo~9R%SEu-ox%c>dOwh%?0-wYDv&}dch#Lj8@r3v1(IZCI zMN?YJ+)?Q8N|$=Y_5Ady zo77csP!$;Ca3TfrbpdNdKMe(0pG6F{WT{|Xk0To8%WY+0NAhk1Rys=ZtJ(@5-N zZ2tcJ>(H2fA8>vfLa)I#HaSZv<1BUHz>-Uv0MILD|Fsb|6rR1B#4$ z!eRTEA*x}M-Kky7g>sCzit!gkggD_L$ti0Ta^i_yVj6owNMzu!Eghw{b}~e|ty7)d z-IzN#7m4h;^moASUxkVH_H>1|78G&%=435&R}rDIr!gxAk?sh`f3Bz=*nW%ox9BBo zAB8ln2ZvS+8oa$7$m+>Q%GuNZGF!R@02z$aM~ruDwp4c_%uBcN5vy6K)fcOW!)1O2 z5vmLCs_@Vt4|kz{P|#u%zjBC{sCVGUfCo+|m{1}}MwoNfd+7aAl|uKn8; zwS^Df!$0VJ?Uk&*4(vLxc-Oupo}L=i!aDR=I>L3QSHM#1^Q)&ZHPp(UUb57CR=eI% z8M82AbmzWRSeqOu_aWy~`eA7a|E;kr6%#p`0qNj|>^JRgjpF#q`@MJc8+I;ICA zENL5U6>a)wD!4?}X2i~fd0~G5o|53DfA`{huWEeDeaR{>EZ3L*+6(^E3M^bEyzrT- zV12~n@vkYQKIQ`gi;&wTev>ZK>)OpuN-DtCx#UTY%UNAq{s#>_YlZCH-cJ`ucd-)}DDcPI%-FAjUMBjruhQa-_RVI!CT#Hdp~tFA zg;g+z;qXec$&Lj^w}&}7(B~T-I^q)v7E)oc^6TvHtn+g0eDxLr%KB5JbP??#zwB5U z%p2@K75MUQBPgWW$JkNnU18MxStN_4SYj5tYBSppt{r}7j*4XUr5LI&k^eLmV874m z%+*+8wBAU5t4{h+q@EHSc!$#o=i<=l<`{;?TTT8LJYt%s<^a%ya20g#5B7e&AWaP@R79cZY?~N_v$Uh0=D;C0JpwD^(`=4|Es(4gc)H@tZdIaXFIOaTd6e7 zOM9seh@;*@nVGPsyxWd$Zb8R|p;wkVH28%#x8oJK0*AvaeCb}(*7U7aNA!K`J`vIG znjnAU5%O4{o;16+0g3*1A%JyKODA)s6BempTcHfw)c^f_XD~hJ@}l`?oDk8Hc7~Fj#9X|m#n#+sy@2*v;587OC->7y_rA3A<}mF@fulN)!1j` zsQ2wSbKh*7thN*j;9JAW_cVEm*gWm>+BSD&DLtoeu=cR4a~kLSe$*Hs8ivQeb3J+Y zhqCr1gbwhncejIDNgcK~TS1?Y@tZ1M0{agcg)`uW^@~WlJoL{q+}B?%xwG^jRj1Ly z6h$UXwhel10A)|8b2zBDmmbb-=Zf}`eu!Nh5$;$*Ag-XP`4Ygv2!KW|&-uFpxj%v% z>-Gj?C7xV6ra33$nDsyg^zUL=ZGXOU(xdx`8G*{WFM&u&F z(T~tY8Fh^|*3ddIk-}@YW-1$n$AO!)B1DmB>YjR$k{W<9OHFbB(i`a0X?gOkZucLi(IpB*q734u0g@ zTk6Os$wUdMzIa3Ryx4|2@z4o~fAfEtb0AIDD6cQRYw1B|Umk|wnX{iL4iRFO`Nln} z$8KJFq2QU-q9?H9PU>|*_|Lm$jOZe)=((QxWoLNix16(sJs%|5mCf{_J=ZuTb0V(g zo;$b{{}-yh5Yf~SeY3^$2&-=lTuGoo1myg;&hN|nW!z+$h!CR5o7BTEEgP_XGT1_QzQ?lXl>w0ue_rCQ?JsRXy+< zO0eZv$zW2Z|9`$aEOHQdl>5P%;(Or;7;s8Zayk>4(}4x0I~yfMi_fUI4yX&0v+Yrk zlW>zY?wSFl0`VYEJQidxz`^1STKy5f@;RH!DO~5O6Q2n`_)@B~#~aG=+eK-686Xk> ziB=#tyrc>Y6qI&1QAMy42=ywu_+ZQ)qP)v5U;cu^51@-cYK{kPr^S^%xGEZF9FKiHJ(4pjmOQK>Guy8UohB~Z#4AsF5JIOXmQsX}JfDqJYmF?L38?^^g& z9T7bWR#dnJ{d3OPFU^Zh$g6inSNRL@?6KQz%HaYHC$mz7t&tzK8x!hfyg^d~PWTAl zyNE(s<*G}ahp87q9nkwE2|1^aiR6Q3Du~l6&0is}2UcdPBEy(YRq?7RJ%-&se}Z3| zXfahyj~hrgm-+BjFw@7mm4M zG^zW{>H7;!#pBYWaJz9L>93JA5&{lKzbMfAz4a!?v#yB2LFDjifR>Iv>G!9t46um7 zSr+RfY8)zlEw*&9Op3*CYJdGeKE-ksL<3d)4!SAEd(2O|d!p{kCpNanl!`L%UIiQ2 zeG`PR4;KN}@0RW+Cgacg_1!KCFsJVOFuPKYjC`ofsUO~{YIFbhP+}nf?j>>EA9Qy< zL?2VT0R}Jx;D+$I17yoTe7U}t;UE$R=B$N%fTWHd08+>SHnS9ylR##6-ykctO#YC@%Y0E@^C)$IUGA_<2n zDj=khmjPeDH7sz{Y08l?^1@&u2FDA!P)2=ru}k%%De(!Zgil86N$xHX53Y_1kr2{m zPdteFk%dXSul?m$Aw)C}L`tv%B@4je1hug~Bt!$x0vyoPRwtIy^;GnU#J;-y1ORXF z3IM0A^SXW3`vFQBT<}7wIN2v3j|>-xW#X*4h)hr=O9Q`5ydNJog;zs;89^1CdIZ2N z^uV7UglyZ&74A>JA8LTI4DK157oRjvsj2;zT@|rpUgvbkhwfr_vLVZQschrtr9-0p zpOCZ3kV*I=UoSyATpN};g415Qn^id9F>Ss|8yp;99KCKU25Zb% z*lKwsRLxJt+nUpmTN~z(1)A;Q*7sH)-v1GP&}Qbq226P8EF~IdIA)H$u~JHg%v}6o zV|^1=&IVLf46dyT-ZXSg!-J9qSiw;htpE_ll0*M;=P6H6$uCU7AH=$QmU9noQAskZ zv{LN%^|JYCkSz=mYTbbre49JpBT@3na?LA-a30U=`$#04g${i&|vuFik^&oZpt-7A&rL}Nch0@ z=()K0l7+>b8&^_=t=Rvy5aECC(SIr{Mi^mr3^W$$TvvJqfH{HO&Mn=|!P1;GD^_(C zkK&!ZlaK0;j9m13He6Nn)J;?oQQy>@$4=V-Jw2W>Dpu4pO-Nd5fa`X>R9rD!A-tgJ45x!< zh9);Bn#vI_=Pn^IzT`49t0WBrh&4pIxUD3W#B@i-O!Or|*SoNp)>WeeKC&ObYA2~U zUXJ$`%}XTKQ3mHQ8=UPZ`F+GSlMzr{|2n;-&^rE-a3E1hjenaNkSIMvA@sbL zuSAQPr4}CeY&rEe9mecWT(Tl$F;mu6&|sH09?czkYf-g2cnah2aEB9p~PY z=Y)&JqL&;%VLC=#*El*n`#(xF=E5`d>%@ck?AGar7~07K zPYw(jI`2;_IGoZGdFI12q2H2(tz%Kjw7d|o)i+U)*`&Do-*P(0GT!AuSnlDfM+BA? z06Ad;Oeo+l*&SEkf~Wumpd|*B0WSqM7`_4`bg+QC5!BVlk}7}?fQ>T%rI)d>vG&zu z7)ESN*p!lpgBY+iQqhX+8cLe<3ZB1omW!-ATOKk@!;u;ep2T*r6 zKl51%8zmvk6PdPE#srWz8&e2=`&!TGuD-WVn@`i1UGbGv-1}7xaao9;Pd{p?@(W zqB=V_Fj*1W2hAgyxsil%V^O~{<5PINIXAti^xTA`DJSKH8Z+76_+eO|D_*>ph%vz@ zlAu>C`^wVhBU4TvXS*b0S@kZ|t?B9&+ZU@EsW&~JcAFu`h3HFX2eQ9A2E)=7NL)}c zsr&)z4(~4;2tOWC0zKnWSL-Lk|*u3`6Za;$rUXQoHF!E2|6^0DZ!l2ssumB1$ z*kbcrvi@IUG>{=>r4PAx1yXmP_GzG8hfPQUajLa|M7{#nz^_0Fj2(&?+R+9Zn*yL| z_hy5D+SZSjB+8?_6{r8VFc;U2U}?GQWZNt%(QCN)eW6AW4ASFPevdd;1A>`rnuV3s zKIf~h_ikCWc8hh5y$;Y5cDCO=ay)Y?X=!+K6)+DV8U^TG51%hY56ZVI*c-Qd4k<{ACwO!&AwWx~Y8r?R!&h#&F@ zfzCt2IgeRNus+T9n@B}Z1rpL5e3QdAn{wry7kOuY(EsL{d~9fmx+B3Eu%=qZ575#- zg=dGL~0<^PbTb3~DQ!EtEpp^0A1j83xoH9$5x+hSX} zIebC9uz_L=&cOWu@_Mlk%RofF*eN=Ng`dxs6=jQmRa)^0lqY_UUUkY!u;Ion+d@@f zQ}C1)$xIPAnNET$YarO$fS=-FmJyEc&idW9|Iut^aotk0e&I1Q)J53OBr@(Z6;(>S zXZw~3(7>Ajo(X`@KyQ`)=;$?}FE4FUUNelcyx4Q}{Iv-MXwh&}S5(l&|F?hVrG^zou&FyBq=!J#XtgsO#>^M&6RB*4 zklQ`u=T8M810YC@8gj=Dvczb3d9Nr&v~}eP^pL(?eE9F%zz!H+c%Vg}+Sm44s2v+q zO=2}zEb-Y5_0f4}XJ;39(4E>J)=l}iIEiBiwVVX^N*KmY=yxwT+HL*_W4KlzG0n|I z1oq`8Y3<Uue_* zcLP!T+_|$Y(G6st#F7FHP5WnltGoA1n-}ljQ!sF_#YZRV%&ci*Fa^~Q{tlP#EJRlR zS*K|~EB!HyHM_Y+oeSb1G~DxKoG203otWsu84W-4XPU2R%T7!6Ikeai$v9_JNg~=r z_@CS46XXeG{isdUA~S9(DwBa$YufP)6y{o)(Pxj8Z8Ht2OP$_*&969BHu(2GVP^kX zZOC*qB5T2c5ZE^!U2*;pSzT;p)jIU`%T%W^O|%cme(I8z(j~C#`Wq40cqrs-@R>kp zN0#xAE2dn5Q6r!0j;7=ee#V#)T7MZlr{cn1-cEs1(Z$L_7r7mIQ2P8#T#1_0$SIU! zLvhYNU3hy`1>`x{exco$$L{Dj`aXnh@SYBAL@D0wTOB}@`qWJS-6pHCiYVmQI5D1x z@$;hCdm=x@^KKx$8)pmh<)0c7ni{&*MQ#{@dJc;|T+mx)I{mF-h5w!bt>f9Ig47>n z{O=}tnn1Usbqs{i@XhV3fK~IJ?OFu}Ul<1l2Uz8Sq}Kv-Y6Qe}CJXPhFCzc%Itk*< za?um95FrkLDLLERfRqGdXnibaM<0~7ppz#7Mae&KVMT>0T778_6S`DakQjF^6SF_^ zO8_MXTIKhz83F?Aef_~c%aT;`XTX!n0w=rkZ*QE`Qk9(w03rfRSsC+f%?=Et39u!} zDRhZF3Va*<+sU3UDFreO)fTLD- z?=h2wr+-prMhEw?w(#srf_f7Cgag~eKx*ejl{DNO4}PG#o$>bKPkH7`L#~ujZY)}K zAKF#V0%W?G?Ha?`<=Llu*qgDZ|Z|L^iM5>eXIc)-%3KqCsZqjCp zJo0RPHwW-lLHV5^QbIa=d_ZXP8O-Gl3K?HD?u61(&yw&*{7F0369N6@B|V-iiKhQB zTFnP>m90d34Yol*8A5M_oUW$U2UH`QFBk=Ua*q03osHabkBAJQf03PqD2vSlg+|r|(rUvmc_)%@|q{t`H_ zK+=EJaum?;>dm|ANvmT5h%X>2=OKg{-s)LXN5aWAmHazDNQs8K+IoKhFw*J|NqlAx z8g6TXN5u(yHZ_X~R0@>B6tSBOWlnD@rtAtM-hJUib|Ka>E0#qi4_RHx3YU+JIm%TtImdJC~Wi33-9 zhYb@!-GB4uq}0ubn9QwgD(>FgK_Ax2_Bi979pOHYG8lhzW<{XJ#$wOSor1bx1LPAPIfRo_Ij}fje%}5l+I*uCw}k*rRJyAP>EuSz~zG?a1wb5hmXN~qJ;P|C}I7R^))%~T19+3yfeji;HVwU8AmV3EJ;T)E;h3Va|u9th= zKms+rkUpU*To5 zug!8l08?n5gdto+uJP|y|2%=ws&@$er%WHIB_6Zq!rzegI@xU2)P3AXBkiquiuT@X zZpGC%#> z)#gEXd`2Q|b*21k09sD3*T#4JnrCDNJ__++qoo-q`pEIspNxs8Q$q6|z@DaCdUf&f z*L*@sr48QrQ1h`Pn-cDw%y-DNO9x(Ql~51G$Rx$K@0*;N$DHosirfR2=LQ}m19y_J z%rx-@MipX?mO2I$=ZE7Y&XkPquybdPbi*IkyefGZd z1UJM5nIdDS4@3M?Q;uozuFM8^{o|1iDsH64?Y%t*t0m+zVbSXW*nvD_F4>{)Rds^(nhC*^R z!%H}Dt4?X_Q3{`=1nyYv1?KYAs=EJWZ#!e-6LnYCY6{Ndp7GW@rjoY)CEF3akYB#B z?Zl2*xxTr}}+O{P5w|vtd_VO9&f2{|H zEYuV*y3*ap4a6eZql`SWorccecv|S+cc5=JHzmSO*}m1sJj&pCi<>dx5GpKxg6EJQl85%t@@Pwd=x+ItZ@;PZW=G zf>Z$|Ve{6hG@bcZ{|8V30G5yVOK(geM?e6zX_b+{#cZOeS{@h6z+(UBj5LzEf-kh( zD5EaX&!J)j^eLU6{eMe*Gtd+fkDmO^L-kD9np5v*+6?u02-1W@Rga?LHP552hyaJb zkox_#$1-M&i`__=3^@*o!1xR$;&&`wZk$lvv=^A?KG7s9oAk@~_GCdn$xja$vqrs+ zUhJ~aQ#nDt=6;Oz>?Zd%+Wxe%1`~WSIdcGIWuP+eRlS9Ds1{Ioh5qkm zh6j(b_-q^Ar3tXkG-7DUAW9D8cbrVG@3xOE3C@=|IAvL;JywsFE}eI*zh0@`o6Ox^ z9B<^dAd>mtRowZJv-<1_3la#bSO&^OgyqtP!&RWT>NKuMH!d>)^KUR9S94ZX@#*Q_ zikmN3^xx^ddAf7=`u`rz#6MxNWMC|;E{g$gEify11GVL}O||)fJx>~Y+R&C$1aWjd z%UrY&b~A2mZt;X=DYDT~{WFj7J=~SeYQV(AsnmY1{VXOQ^;Ha{4>9{cST&Yf+q`;! z{{!pd0o&gbhjg{rQTy4KZaPFo%aO#y5)S?cX=R&uz-u&RRArx|b;UU-hNSSwVgFbxaw4=t|F-o9Y*{2O*J=%vHYTqwmKbkX9u^OcEtHtY!Q_8j zmt5IDy>UQ7R8cv$lKoBYyz2X($Sr@$4$>3ELHX;{oj{*8P_GHPmF5GH%@)}{^rqpK zL@fh5OBW|$3vaO=@tZUbF8HlR#FyAXqaz_YeFMXHPv3y|njoXBr4eG9F!E-*(TAtO z@(oC@Sn>oHv|DK#=kUF!lG|YdDc|0-=Kl`cbhWYT+gG(f5eH&S;6PKKq>?990R%Z} z`$!;g4km(=|H^@MOh{odm?9=B+RB$vR(tjS6UBDD;F&T9g$(Suv*8p zau9+a6dHO8+JMm}uS=rLnXV$1PCZD&;ndO&c`zLJaZq+`sr3`Gz!}XuAt2I*2L{7- zEA$-ux9In@tP(2kx%5mVPA=DZs}t6Gjg1~2;b_6d{Ew-_S5{LYXIfkUHyat0{`Da6 z1js>nCLk1EE9T4#8-)?@#V@`rt~5)D^2Nw^rx8%0MViEHSpnD7rbj6Q7O~w=vJ70+ zwFlAn$!H`2&rb@eU^yxK#XDs4$lT9T$Xu`w3>0Qp_Iee!dvbk!G)YBLU&{TerN6aR zm8(o~w%}zbBL^}eOa1x~21kX022BIrul-Gt$Gnkz+t(r7dWW>&;2u1Up&2wb7Ii>F z=Boc1!@)^k+IQKR{rhK=#d^ffIO^dX89|2ODgSr!q;xjlPpZ-YIZVf@`}Ux?`1K{a z$7O{J$I|_FenwRRc@mqVPIv_JoCqT*wI8dLzMTh-INe5$WUx^kBPBBmx?j|rWsMBa zod<4i&F!Q&=xh9k;Q*U7T?h7ffpt{~h7o&g=Y%Q~1OsmqEEfa>p-F;?9pLdWaB#$f z0p=9gC}3Q)$-!(DEW(XK*n8R_Jt`(`NDV~aWxVMwo+G1~0AM5cF0aSuk z8fb*KM2`6m_((rHEH$+K>G^}@w|EGK{Obf`(X-_o-r%)|fDjy+m=B6m{bgaZb}Ric zmz5pOA>PTut-b!s+++PhUw$mKFkbdD1F^nV?xnV&JvNBO1vXG)jRSfRB4GEz@GW?h z98OWgYdEoQCcoa5xD5y!WYpxC30TR!vH$P>T@9dH zIjUU0YEx0sf4HK}29iK(`>%vUwU+U>X*{-9AoLwfHWCoQ|BpBNKh;(z zUox1kM5v)F=IbjAM-&+hMlo@KQ7T}+ep|UJUF<<=kKLNG0&d7qCTFgh4ILR&a-fNb z=pt$FUj62Rtw`5CpNa>i0pW1~wUIVnXL~RCtzm-$fO!9;p$ftA#S8(h={DJHa%cHU zVbvD3A-Rl}YoWB2e$Uv{qw?_9fBjMed z*s|HhK_?A%4JHOSz-k$~cVS}(A#rRN@-4{qms1I~UOWpfwJj5e|0^W*{z%y1jgtve zI1OT7(X?U#o26MJSswdmeIr_f7VZzw1dX0o2eA(Gje@6)Pt?o zWj)PvSab3K)&+qo8)XKj9O`=QSi?2!%w+o>(UN6S2|&R8=YyUD#e#x&3Pv>qrJh(` zESu!n#d_8y;4ANQ4m9+&(dvoa&$WTp=IOlVcCGI-&tpvUDdaIF@C0*Rg#$N<$Ljun z-K6?YP5jU7E0EDY21GKg8k!|9S*8}sTJ+onz!>#vyM=e&-rfW9y8pjEhW*K60nptf zoaUHd!y>lY@Kgdm05)Q$owLDZ!gZ%q3F>O~+X{l)SUOje%S1wmpZ)*1`VV-n_cv|; zR+6HSkxk0pva)w2N{Z|)%9g#A6@~0g$liO;>}=V4@12$PT;Iy@#Fx>eE}4vhp^E{mF9_KdPCF*Dz ziE8q?g;FgUHLmmOIUK2Q*D}etF&nX?-hVvsz!_HvYpK6r69>@%aF>SX-v3n_jtII> z9jh^Z6D8iuq>Rg|0RmLBt+86>E+Vi28{Nlx%w;B1o)y;{HyE#z!yxXC0Xt}7B}=TB z6uFxL`AfHs_!+?VbSy#|>Kd{?&K|8QUztu(UEgW6%e*6d{2)z^l-0@M?&VfVR2SYJ zqE?;yz#p2U2gm3A*6HUFH_hNp7mV)gZ6e>F;U*`|ioFHuj)~R@L3A3~lRiy@b62vA zNjr(W_4>P*kBiu@#h0b7@_s-H-?lbkv1`PWp^}mu@k*rF6xawg_q+#$;7<`6)#Snd zMMiPvaDmKHw4qe@mjH~A|D^|lPRn{@WY;rE544$JtG+Df_7`t-bQBS96&np^dKoaY zUZ=?wx7J8&QXKjJyEXf6(a_L~i zGKtPK8rFGN;IUy1UHd<_NrIp`E}`1ERVd?@)UWYEvkzh{StWk`#RV#4Vh^$ zmZL6eC_$JF-YSjWS1ELJCglkTN)EGlpSMynZtJbfwxSRy9(5Hxwkjyo<#pob#Kc?* z61zdE?ndJv!HwnZ2*cn?ejW>K5*~>gH6}NKbQRP6k;-HxTGFCq`+(4=v9_NQuE2Ii zwVRcwZ24&qeF8BEEq^eR11gPlBZry{&Z@_EtOS~pzq&J+hyE zJj;jWdHcEqJpspq&rW}gFC6rVLQu8R`eQl(*fc9qW>~So9?|8X)s)){;u1{k1IGc- zR8U|V$i=T$)ts@_;@InvSFfvalWsWtxjpi@?L4pO(NeffSxDLc?Jb*n^i5W8s{6^( z;(>(VQECIAm(Y>#|Js3{jFSIQLZAVspi|eU|?v? zb&F_s&8##$TU&I2Al=WepOfI|Q11J%{>ys|Yadiq_$})()fk6n)8F@OMPEd6W3nph z$hC>y_uqcKT6tFy8(3{XdA>TNO@73cGG(s3c$20+zO`?XQs=|s%c)dO(#GrGh{rI$ zwAX7*xkMoP2JmM=oR8*rIX_{n{GQl zsAK$*+}*ck3GQ)zt|{cD&@Y0k(TL{$Ln|GJ0=Jb|j2?bKhLi34`J!eA*&g-?w75ObTN|M8xayK*D(Ry_ zzlryes#EsqvyB2wkq>uAbl=+zCfDZk{Jvg_=Tg>c$^7f5+7d9mwwrhha`o)bXRJ(l zE#7nqC--=P+5Y{GYUL$1{^axZ-)JiX4?nmKSw{ci)XKt{yFbvB5ca=bzmE)|dVl^B zhyVHi>zS0_D_3XDtym&~uqm|T#+JOg3;wc+4>HJ6w}k1m-pc(_9;`?ja|TgrQOcF} zXZH9J7hACRtjvYh9i6f0%xQH6TE^sc60;m{=2KO_Ow3;B)s=*Su_ylm)lYX3#yx`F zx`iDsVwbsz9?)0N{Hl#6Yc+5~|DY((w^kWz4UT5V?p}_OK|I%Z06|npqhMk zu@%vc-2V3NBVF;I$ysE+^fM`QT#aR}EghNuWa4^qqei0W__khHODRKj=-SGow7rUZ zKgWA5jrnpqxXmUCq82t%ph4B<9$lfBAdnUNZO;uI$DvQHIS=z}TK)8{YU5o{YK!p6 zX*EY21tLIC#aXK4m_~vB%%Gl2$;v#x;xO6|CRh8dA5=@n6a={1t-CB0=_8s3OuMa_NlOeKGe&Z3OFaz&eArW5QeNBoDWGLrTGpJ2NS1l|<$6 z;OlrH0h;ny7PbI+vcub9qFRyYMo}B+luT{6W|S1PMWX#bg{7~(to472QvZW7@~V$J z?kAE*p641hp`PMWB5`@Ax3vvVkS@^n%?9oH7j~Wn^qp_J5(;RI%(N7@RV1DKh=6bp zU__-W%ir?KKTu)dhw??uSN!#?x1F@S`%balP)?4?Df{cq+QzWw1Sn zcO?w_ykQrZn|Mw-brVAIy{Nd=l;L_)#PA3Zd!Ko zr95pP4?G)__H(tIg-J!gazXM-ZG2G%#Joai=`M5%abN)!4azuf`yBJS^FxQYo+#Ir z7l%dAFzoZY!3kSu0_W!KQW+T;m1=v-A~(?U42g5rJsL#I3UrfgJ+hzNMuqM8Uh^M> zjs|)(d5*tF|76yt^e7;sh9Mnjr?r~cbA2m(VQT`ZhsFzYC8*BVPn5pU{tBTb)y=QI zH6P1oG|A{EKEF}?+P>awbW2JsrDI-H9-m7C*hmFc)Ny<1Pc=!$68cfUSU7f!DilJ8 zZ+43x-?O|o>Uo=*P7Qm5;$+n8^TerHj8Tzgl8Zf1INd_LVF3}b-H zE$74&XRl2~;acf@MaRu--`Wx}4M!lQ{pb19L#dlPpd9G{?>LwJ$*y|e_o3#dz>6A1 z))-Rv3%iKh*;vl*Zg!(6u|&$3qZ-A=>)V^Tj-k*d^lT8NWo)U&59C+iB)uIxMW=+l z#!B&uG-B+yf)*SSn^xK59Y3cdNjv|?2+g0_l+LxU!?@!!ds$L-YBsdrtdbsvPc5JR zx4SLJU?L(5Dyxc$!q{r$hGc!=Z*%$v-)n56OoS|#9)lkPnR4(0q*{~u&B96zFmvFy zHX0Sq4jJ`Y-$p|tCs&jQTbkk$>xLYsf>6>nv+0CS-xzzD;IX#4w-vy2%y|W+*0%ad zo1cSc$@9mn4!1`aN%Ix6tc^lE4cH5kliW4BU7BKiK77gHbo%6~Uu?s_N@Mw_D?<`> zPTVm>n^ZE~g{2Z#v@D-`YR4%1vz=ZJ!;gdvUKu0&(z2>xS(cdNWJsPbd=R6o#(OpN zrK68^gT_FZP>M>mIpwF-9oAyP)H^Vnu8W`3pA1%vzsDnbtrim>|GSSeK=aSq zDQF<5f$RQiJFW%DJ8*=)$1#A91|4YCN-4Y*@ym``{`gi$b3cGf@;coOf(4awl(gtzRE>7vs?8QNFQeiTAO2 z+Cz^M;gS9x6RnWH{oQa~Y{Py6XLRpb>(&PT9;wv#NV_sQW(nZT%Cd|8FVNsO(nJHkGpl-V|w&de#}mt+zQ#Gk*vg{T4keiD*__}%6QLc3}xO3 zX*HfZin*2tOSjntg52yX!lt*$?Nv33;bfPye@^H}!zTC@iXs@56T`%mMTi`E?4Pxm z!NX+Y9~le(>GA0;uF|jHah6}XE(+^r$F=88JVAe4^2hprh0#@aNaNW7`F;GMD@55Y zpI_#*29WrykmrlYqKik=FLr!H z;NP>^uZJ}dfCwO1?aes8>(ybZ-&V?p3vh*ALqu%ya6 zYka!Hw$%|Mz${53>xw=fkG$TOMLAQEH2?niwuP)m-!5nzeS_kWAOhZx8F(63f36bq zeBLN5xn}(`a^80~HBzqqC^tFG7$;!z3o%YbXxqq7`k|C}PSI9D~zW27$OlcM)W6zGkEHXT8oJ(6- zI4`R5N{~P_Il!5R_T_(Es-$K_8A*j4?9HHcOrzrCs~C-|i(u0VNTT=j^6CSJSt~(K z%M7OEZ%@scI_OPSGyXf(shZfH85qzWO;`o~Hz@-y*0S~6{C%4H_dg-k7(D!ef_X0j zBR)LvS)(CGxuS$R9Hftr$Wgd1`GAXgNEkEEm; zR}?x2EDN}mx_Iu_C_$;lTzY{oNKc*p#raPV*x?;J9wE!O-A{B0)oVWZ#**7C8P8> zGHD@8X@YDKGv4RVgYX^rD2y zGqMe)(hsQ%={7$lXv>cu5tZ2BUwMprsd0g>8e#0|L(&%LJLkrY7geptk@JiDgH>ac z%RBmA9qvC_uKu6aE_)59ALo|kwWg>j5AX$fx?gn4`ppBXH#fNJlk$2y?(wesoijZZ ztY+d05OTMHv&oOn)g@5j{zV=1M{A6G{h9%$raSpJIyP{oIq$ej<<)Luho(&lY>trD znX#5L*5hJ9^J*|?5@~^R^=AXwI;N9^hx_sF?DqUJ2bdw%xg=&(33q2ae+*G!$Zc>u z3Z6#0-@vc3P8Rn<_%<-1j1QF+6|X=h7X36t;POQW9j83KLE4l-8*{l0yA5EA4`CEbk&}2?VE!$eTi7XKfo%Y z6tBwVHQTrEFK1b4L~ny5D!x?1cd}DQS$PH7%!-CxXPwP10CQT8s&Pd_E67I=Z&Q0? z>69tWw&I|lKRE5<)hEhKdbJ8;7-cu+QdklXQIVC2(oV3M(xiz@I z%UZXqVxW%Uu8y{TZqwL!Rf>~smsYaq1fqeFX3BiQnb#manqPNCjVyLR4VdCqwwya$ zbM7xnbU3LR)DgMi|I}FOt_j^B22!oUBBL6YN`glFx{AJ)uJjY zqr~K|hixE_pdRjm7tYRtkRhYYd>2vE4k~GNf$b5fQ-oJ9fX|yLGJ;D=>ZJPX=@dAQ zUbwXSliEGr7YPI4|D>l6GGy@L^RlylfMxFm2f&f7>jyjzozc-G0E8@0Bc9gf>bVKr zJpx?LQU(saQrB-iI(G|&(x@BcRL=q_gkT~FIZ%kJFp}ZJ`S&Y60Re~8p$X#G z1Q%3+GBa|1L4G6>DGRPsFHR!~IjbhS7urKIa&xcO>lW;QZ=-h6=!?o8)3V8>%WTA< z9_3wh3rv_hn|$$BM+zQ*Vi>RhOR%up2RQ_fW9(p-lGQJkvR;U4?KI#7hsdvFe8J%N zMgDvsn%fPEod_-0c2sP^`nM4pk0GcFCd|q?Z;)RSd_@OH1ItKHf4BE2I`u>;2k6P= z<>jxpP?6NgOJSH&rNRHJ>?2bURs|ckyuoQK5JuQt$vd4?9_9v`A;?WDjmCqGj$US* z{xwN+!GsE_?2U&!cMws$q_*e{jr-TH^7n-&0uYyLOf-lxCmwK~o38yM7yUKNqM;QU z)z%ShdfSBN^}#sSSF=hqg7Sx$%s*-Q;cK=DFuHIldSfrCpoZ z>j9FkPs(5Uju+jvMyKdey-P{lwfxJvIE^wT&x&HeG5f8(kBRHF4IjfDCnAC!%2&G| zN*pk5#W%>#M>Nq(3AU)m*D!fq3>(XSX3>{$MZv4R47c>Y9Z6Z~-#esn#p~P9B$4C% z&mSTm_=KEjK`MClfF7Sa5-vWKgIQ;;CpUQ&m8_ZzA1FAN#nb0D>HF%dw>^c-1rmfY zTK~PC3n)X!he$O9Eh^wg$I#%g3-k>)$LQ&7)VU&J)ZA?i`7ph*gDU&L*yixH`09cO zA3^jpkLe7PKjT33V~T_@2k~L^dXd1&7eGju`Bxv$At7&np-ci3Mrot@<7KuI%Uv1j zS#v5@2m?3;V5hpn<^RfGBv}Gmf5Jq$D_uVMsf>)?<;6LmNdaVhjo`f&#ch1|{O^I@ ze2agY%>-DJv0KcD0-J#!@x_Iyw6ux}@!;U#!s6mCY$ljh-f}!xYX(Cz5}_7<-l@-h zE11#1ODci{XM?)bK;ge_bwUsxH4uF|s#~HO#cNJM8Tfrm_QLDnpW)HSXq46*)ctz1O zFzA3QhTz|oWP}8XBt8#Sg8@ee5|~-6v43BqaA)_lOCPwD@^V=yYDTqx7$UZpDCLX!o@+Tr# zgHV&UOvS869~N$Kb8{;wDuQiDD{RUx*odNV7M!eQ<-KrZR#bG@t~omMI)|b1Ue)w@ zZu@>6SXwcw7T?%Gn@6;n_a8inaT-NRWX#7J`Bg7|ek;SO49GRMw<&YWesYvwq0fMr ztafbP`&H`=gb(%c*|Le=-7C49I|5vpCv)}c)f9vvdUIzi^N0%|6V*w3fF8JXDn4qG z{|$#H6lMP{{WEs3JS@({x{43=EmXPIzz$WIlL(>Dt049t^0bcWBrLrzEO17#xYbqL zJzM$Z=c13rrXmWtHLqW(_fN5#oufkdY1}vR`q~yZ^;UFiLRcFn^Go~;tw%yu2HE;k zoo}#hlC;L0=IUQ2p-d^Cu}$=?9PXtVx_^Q&{xzniYB%8{g6MC0Gxm0Bt5OFd~k zaObMX`F;(rR+UyAWhR-0MhGCgd|G|o=Vy%0e@ugk>4Vc__VRya$vg0RK-^|x;v>LD z4eYcb%Cwm+%(Kp`{6?t!=K(b_Vz2+Z1+Uejv?Fu?kdqu-4?Nl!h3G+iBw$C>%#0Pn zxM@U0>Qq|6FwmTs+Xx>f&wCYcmd!A(|o); ze?AUoW5z=#glw9!^740P)^RK+ZHBpx>#THkW*RPX@lfQ1PpFNDvfo>8Kn7Jk_?nqt z9wJe~y1H}338#pu8kis^yH(k3PjyCdE*A&x!w_u`91&4)$jCZDd`Zlqdkrp=3l4}s z)^GH@c?QCl=M*vnT(0{u)9l7W%p0}>GfYoyx2NLZM}mZ5$Syw&On`k4Ki2suxhAW1 zkI)er=3V9;^PWf!k8!}xiUVeM?aP;%KW z^p{q!rPU!D`uW`o0o#qX|J?h53ybJ>NXe_n4`0O{tRZ>$w2+ctT#4{FcYPvd zo!>9%d**eRU}QPAlj4L46f?9nk>rAQL(q!Lmg(ve$Eqj?MJ%5;fcwP!*nw0hv zFn7A4|GbbD|K#c~GkuMf5Rju)5~pWniS|Xk2haR+4929+B?EU*4i&d(2X?QM%7lZG zKIHARd+y0q27dmTtLpk;XehP0u6|oI)e?+vlKta#Bp3od-_IL)97gAklIHVXqf7^* zoCK&Kp7}a|ZjpOZ-6M*7+4x+dxA<9yIu4jRJ*odL(RzTEJ)-zoqHLx~);5u}7=z1P ztMQ3YAd@Tp;gp%W;$l;4skmZ}WOEA#q@ZNL2(h<@xjpcnOKpoes#RnBdax$V7sYz*06~spb5-Mg{qp?#)*31 z$l6}((U+Mqch+e52(G7TiV@Qfbvq8|tao!Uzms-(xKMijo!#NIa3+eiu_Cpm{l};~ zrU?NM4~jr%z9ne54VWe-9dwF(8X`Fq75d7h?8ehVIi(NfJFHwqYncpggPLEv#CEtW)xDb zV0R?CU0$Go{?Dv(mDa7Hs1_>5QMXG_H79~?Z>W_$_y?aKA!;yjaq$-wJr#f29t3`i zjlFg3_YHiFMRYQ1;q?%qhFh=UM7d?!#!7E!W21PgY}A|_-t-@y-O11h|;uhH}*Qq58B~XuE?LzB)Sc`(Tml zc7!rq>;k9ll7ur2u=lL1_k69FfaE6v(|k5cHI7f0S0Df{l$*3Tjc!sJ1%`- z9cRZy??-74_u*wi%_&x3k}Bn+i@4GF=W;h_Zn6~jI>|?W?#Cxh$7f1XKC>W@U{{WM@inC56{Fcg7RF5mpB0V97%bl@&R<^dN zzpB`_Zyul5L8~t!UNYI>L=q2MhG*;+k;p z!_8Zn8lJjEbB6>4RQu?*cg>nMyHKqCep~B)GX1f*+*dY4o11$sMBF?c1^Jk(d@5(r zRng*dtZIc33FWm>-}2d6zl=(!SA4EKMAfHM{yZMhpk_cyc^&&f+Z078&ZkXN-`5Y* zP2;77y(C9O9*kTt@$*v%Zm>)|4e8BQCwC%kW^oL8qxLhc{-V3!>n!R0Q1NX0cXu?u zX7Wpa-4KgpxTf{xPO<+$fx`#lh2u!eF2dQ)$)7fLwXId?-r~xFhh13rt!xSd$x*Z@ z5*>^q6@SSTso`dvzEipzecrx~*(JQD86j=4!)PJt-ilU2R<8;@bLR2)xG4WOuXo16STn3f#4MM|N~p7&4epNA&G0`8Oi0G#Z~vDq|6M zG`#Ng;D489Q?sn@yc?FUe*$&E?trvOwFxdMp&NJb8A}sdeYWydivM-|MdUq921k|2 zCTgyqC)Wc$2rg5WmYDIP9m1Xoco zO@qb}*>h;u+F0&l+D+OJg3WS?ypSGLmxwj#+W?54VU2fOW2mRZiGzY0&JxCgf`TjO z*>9KtsAPlNkv*Kp1?o@mD4r(nxpcMzMj&z=Rco@L=;-JmR^=m`hny5lOiaIyT{cHJ zc0@GjsHu0LTqXULo%-X41x&H-z?F@hY`1wQa9&=afrAI&t`Ym5chPTjY!VS4b*Pp( zZ8wym8dow%fS(Zf$+7^4UqMpDA&7uLI1Og~ztX)!!@`=OnKpC2dF`W2!~fRnXg4L5R1)d&uB zx4mW>j;eP`PS-q)Y?A5)K3fb3iklqa>BXa}zQ)J*qi-p2}3J6GA zOkO7QgzYe1i`Ocy=x4(5-FvQ3l4EtfuHbm^CdyaJ-q;Zf^m%lyBRVaS%E0E**=*&P zfz9X-62s~~&4n)DPq9^gwF|{gzjUI+sMWH2*igCr$gQzl=>B)dBlvLt2le06hc84Q z+OAI(O#GclDc&MKJ~DHuhu%bF%3{?G#qVTw)95{9ABJIM;@+VAn%TP zj~0ggAqZ(jzPgvR>(A93O)>GZ>N9*gEMLwX9Vcalx` zD?WS&7at!#pX=>v2hH0kPQw6s6&kSVggAgV{M!Jex2NYT_sRYAzRg6QC3u^J>qa@O z!ec7rCUGB#*aXe=QMy{$mWq}!=wz8St4H1ilJW*XlpHli>a~PwneKEje^L&G0-y77 zeu4SlKdW436UW)D&dYIjSHrKfuuxHbbUpt|yg4_g8*e{%Q?>Y=yn)-7X59ShC723z z)R;+o#dM5p+JfotCDCTT*5fc580N!|sQWMqMY-C(prCUtL#E*c_ya(qlfrun*cnYH z>D2O7`c+NsF3|q_-er#+VN3IPWx^CXEHwMA-Kzw2t2$((rKG~7 zHSl-GfQ54<&X8;UN|=PQ<%#BFJ9F&`iSZ3D$)(ki(nmUxb*gr+rmyM#B%k<1pcN3~ zJZ*8~%MqQj9Wm-<88*cv;mew;g;-RUZNAYXvgqL0#+djlK3Nu`Gu;CPT%=xXLX*&) zPYW42mc}K`i)?D?x$WyOW1{}KpZY}YGp73|+#ULVs9{sWY`uAuGsk;!2GW2;`%I_06+NpH5|y$d!-5Penk}6Gv;omYAG%h_A^)X1Ry~kxaH_%!(1#g zzkFebls_A0Js)FJDg5zlS)CqPpWqYde<*UXiwz{8=pu?da3F2z4_aIuE6FOzp~nE1 zCqu7unZPo*=8EFg9Q1P4%5wWJgq#i+3R)>R#}Lp3e88^AG#qYE*L7?&{F`yu7`+wP zlq|Gs$36~WwVu?{gyULbz~ynjeKltv5pdR-GY40+3Pot#P{*y5<_;@fD&m3a<9(LD zRLOUG#=g==(LMx4oWmI9VjuMygcWFrUcL;|e#2|@tM4mC69aKNJ+-$@>wByRQ+*LK zx~e#OalVZl&P!6q*+QZ>#R_bx+64*8?G3ifi~pF?{XRouY_*84YxM9vK+iEz$7vq+ zIs86;8CYD}^&Wrm$GuIuSA{LRQkmW80*<(~r}t)SS=CA1FrSN8_3t&+>1n>Nj>k#4 zLC9mfpxjp?9$>C@Rj5^7JMY&}q~q_um+z<7 zzWwI?xbQ9O#BwGu_!yD(oHalFSI%7SOPi?c&ZqqQTRhYtxB3fCQffuXeUnV`vNs9? z{}X<=-GO75IkFHfK`yx(?Va^)=gO8(3~EFd%7#h0zjAL`6sT0$<`f8N*Sj;ualY%l zS86i)YHK&a?P7F;;hQ-x8O4&I+G^|l}K*CJ%I!C!pgPIe87W#z{wc`q3k4KqS0LMbrsAWk)9rH z8-nZC<6bseT5n)2O(;|BnWSfS0O4K!eO&;#0A$SngvJ@=@-z#=6k2wn+zEr$1O+DV zS?XR%pk-<~Onn}6H8_G*A3Cymz#a1XwjkXdpnwTVlA^*H9?>{{o3EPyiNA)JJjSD* z_V$c^!NaY|U$FVwATt51a_K}wrj;xA6@U*+CgLin-Tu2!Hug~vWWmHk!!ZMH@rdpF zbPP}#Q{@s|<*UJPTl{ms!s4IGCzZLcSGt;${$}Ai+F+{{x>7?GLcHbi8`H>71&o}9 z6`J)GR+)lu`oo*pFCPy0C6OfLaB0BeS5_dU&_ZWk+s0r2zOHGF(YLFF{reBIDBYx) z-d^`d&#~-{wnKuC!a(2L6 zY$wjveUE32U<=zZ);~4D!o(6M3jKNM-G|kqPSk^P-}{&(K6ei4eDT<5`M&P8P!+de zZ{CvX-Ors#W5Y79vuCKEQatywGoI^)5<~hlUhBujYcz)*ta?N{dJpN$f|mNZ46518bT zZuW~c`4t-}Uexs2#DPtJ*ExI7Zy$?q-bf$ z;o#r^!fg$>yC#Z@n34S-7a}7gIoI5P)QoYuHB#V# zl|Ame_BQug3}++l)tsnKHAaU=)Yp$S1i8w zCDCk1_LZN)+Td3GVReZK3sI+cI{(*)EG-I0bkXK2-KCKhxo1=nHvZ%8nxa#dX!%8K zKd&6j(riChA2Xo%dl%D5Rp6wPJACwu`S43Fsu=zQ}|mCDKL(@Exw*Rh+p{?wbW_GB5tie1)f`zG_QJyX24@i4X1bMtjb zIyRf@mmWbaj^`BtUb7ykE!gQ4nz!4h)gn>~bozw(u}s=%v&w!?&}94@%)Sj7u^*-8 zp?Io0zX<<%0DtcJ`eV-0{acltel#=k|E9m6tyb+dIT%R$`${@aKh<@##~-w=@KrO2 zB@S5$RFK#jg?&m-PiKr%lCJ|pZD-+xz6)@s?=~_6X~NqrN^YajJ2MyHF~{flXXX8s zn*Lv^FfiGx&at(C{`14n+q^q~Dv+MS0j89)hQCM*x&zVXXt(oqEkgyJR6#`-^VqSY z-$%*@OYJFo`}_MR0Cjd73By+>w00lgwBiU(auc#l9=1v3i_*7LgJ!2?)x1ycA24=`n&zE8kUOYYab{`y0m}(2`Co zARLJPCR_hWnaukn80T*Bi`#@{zx>=k6l7Qt*0h85s4KD9vh<$wFY6BirP_wMA$Mn+ zZyCAbYq$j|+1-{LzyM`-#-Vy_`RC7U3)<>jV~y&%dt6BxwEZ69>`_L^4uPMNS!S^? z_lcXbRnHSCnahNp#s-T8JFqrjZ@c?wc`lQR2Z>P8$lXko=(AZdUElrtnc?hob$24q zO?=)WE;JVB)q`_I%z+Q;vV8KZRoNZGG^Yl+V?8N7Yz&-1l|KsY9HfV&mnyC~aoUeu zu*eDieKpf?Z1&Br?Na&n8|OoAw5YoLxgwd!JJXCaCkVAeJoedtOQ$P%q#Ws)T!Wih zymka!HU*H6B8$iE{rjrIzLLyKY$p^b8OOt10t~D#=vi5@-YIeSzAYN<5PGAmeC)HN zkYqjhsULBFA4q+EW#`1%`EbyOzefE@V8Xjc6nH@534l(?7ADI zd5z6{yK5XhH_13BT%91HDWxPDFHK*&*+WUG{kKHB={(H?1sx5~7q;{K);SSAEGCZ* zu(lWbCWe*Mr(1KDnvvtk7nAJHH7((W$oaLFqxk{3Hxdb)=|U_+i&o?FGLef_vEpRv zuTKRiRx7>Q&HeSx+?SfX1zx%Q*Yqseo*bHs-gP<{sq|4c6^ElV+(3xL!qRP(o z>bEnp#E!()j2<*NFJ+#m9SKM|xYTTwd|#fo$)m|I7oXY3LmvJkucnq?bOpQ7GzGgw z@OD2CR!2I;tCQ1Pv0ayDsznA}#Bpi#7$O(P^Tb+D)F;k9zQ=P^ymZR=-`a5p?9d_2 zNN`&mmxP3*x4Wk&uit^-K%iu<2?DWJCo1-%_4Z;&>CEk(YR3TjO2K5Pt*x$ZtObZ#J=K$kpNotD)(~%4GQodQfO%x zWv5=#a6O-g1Uzt@-ob2x&{d0J4ZFBQuORqwEDJy}1?L2QLi)vR44x2=7xGF;s~2u{ z?<&2eIX+@EEZrbm!m-x)xoH)b{U;}~&zf!AYFMYVX@D!82Ra7J-wv@$ckbF6-c8QX z?5&Yht?IvD`M_Y%uS;LT$}l*r;Onm{{p9s;sZT;&CoorLf=Z}j+t~IqWPPXOkW7+W$O}g-26|K2;4Jkq^rZLqBT=lxo_pRs;*M>WF)I6A5!N-fM4KJy z`Uq%qLq7U{tLs65rLTX)w-Nh6XcW!}o-S^4!bAZ$|m$6ck~(>8u4bZ zyOIr~Mu9$LgZ=msn_Yhduzu+1wlk;enSH7GG}iIZt`Tw!OR1qQrY>=TnfX_L0{k7TjT!bBfOV^N?BwR^(XXYSPf zteXGK`Ny3vffL=XN3CRkunaZ+vbWs*qw~7OW`)0eteigIBZmmzhF)yRB6bZWoGra| z1NC;hr9+!KPr~EWH=fa-p5EH5pHjE4TX~3q`Q~GM;G1G^Gd1ixFRs{rxmo#@;ne$( z3|(jUJ4!_cAbD611hwv{TSuOyQ0_MJV!tvRjJc@&MR&?%%S8K$Eur4;$!VNyF>CUt zV>q?`F7?dsNkx#J8Y1_b2#>Bo;7RXW*+E8|gr_g%9`>*lnO8Ag=RC~8es9-2<2Nn% z@2-Nb^FF{Je7K|$Bafb%oXP&Tp7xH28EaxiZ7y%fQ=t97nuwuP<(i+PL4pv0B2}qM z6JK=SNeVy)Er-$E#+lYH-kw+5ZJU|osg|r|FDVAmK9BA#F;+?zf_547|^>mXL zMM3dx)8{*buA6K57PF1+H1t})v4Br)0>v2G@nw!H41FNjPbFoyC4k)c+OI2cY;p73 ztSRJJb$4~WUG9zxJN(ttV+izz$H*Y|N~=sAFN}^(AUZS4Jb1a{?93SkRR+xG@5;tc zAazA9>xOkAzr~Dufkv3$MMhXGa;Jv1%hPg!%R2+lkC#PSIjj|}3=SiEzQmI< z;Reusq;cqG=xpZvdyq6>5P(>~8yz?cEE+j$$M@x8gBnrf2dbDZWW+hT{zjvCRH!2{X9NRtm-_OKbRezW~^3GM9(vnYi3FPs;*WwiGb8+*xl~j!;5&U!_fKR7*L4c)Dy}QRt68(WCHN`{F3!x!WAk8#t%=be_Qu!>TfryIr{YhYaOa)02XbJ8d>w61eEDdih{_*3 z=i21+;+%Mqj~f28AC|9^P`)`*s_!WwO=7gc5pR~zY56%BB z*W+Twqp*?kW2T^vX~P+~?Eb!X^?Vd2%>fpOw2O0EjQ{C?Vk!BtjXq~)DpVeSoy+s< zdq1OC-2SQzYl_8ag`a4xBig@B4p<2=~37021=! zUS~MjzzBxtUawOvt1+KJClN3WFhl`8Mh&0)0x2Cou|>GXki*jJG8dLTH)0awZws-a zQE)o)MV3qh0Tf^_wj+Ds@*q}@eH4!@wCi7m)3o>tL{aa<804CQL51@3ctO6IUw+8+ z1Hw&m+?njA8k(bzSLoOx%g4%212Eo`1$?Fwt? zYm9@VP?~Mb*EiYZmj<@IydoWp*=3f$JEYgvxOdl9zx5p1GQ^^YzrF6xceZVvaX`@3 z=qFYXL^Oq(#)sd$(7fJ3opp@qh$SqU_l-Pq=ju24Q19H6w%rX{CVam!vSs4!_KtBH zf@Wi{;#QNuq=+$GpXMg~?Uz_k&m5S0Ln5_LViR|ttUP)Y;<{Ub zGtDwX(G8H8uT^P$_T1##&iC!J_3ux!6jrOEbDXxG9}1n_UXOPw>YV3P+Fb5mj$pSM zyufH>P_~Ku*^`&3JveK{(e;eQ)#fTwWtqWl7M{_ru~09r#ba6ezy%QUXjYu5~Res@i8(+$})O{jNuTFo-Mz~zg}WC84WEW z(dJD&+8tOvp`&(izX?L_ZCC&?{F8re63(k;AauQcf8Y@tONQ7L!`L|WS>7FhXaTR) zDZV@`s|$*b#yF0+MaE;IoT7I&n))KH14Q^JZn)i$LM^EP=CBFyG8O<)k!%8#mEO5X z$@yRIp1%<`6&029$zlZ5lyOB6F}q*^NtwZMaV}#8!E1OlR*Lvum#1}?OjfbuW>x+$ zk-u_b#0P*Mc-6V({`m_4+uMzi0>TM5u1f$|u5L-eiHW!p8d*GEehi^$UpFD6ECgh= zP@6(lz&!NDd3)tw?y}5v@ygM%)$DaJahZ;BeU}3s%JFdB#oOXb7|F$kytM(cz-JK& z>5}c>ADh8VoBaySo?Q!~!`$#z%?j-g)98>5IlyWk8%@hySUWSrwn}p6GRvKweqQw) zzo#7=Gm;{2bSmQn(|;2U zxbTylUlnP>oGtZ~oxy->QtAJWG^h^Lq&UtBy0f*46NJ_S6OHMH#gr7hXybHOxbhpc z7|S)_kxnnbx}ara!!8@|g7^?5q6F9&w?Ux?oLXR^Ywf_W0NXvrtkM@b0vkClcLUw> z4Z28bFa8=^Yb-<4yaY+I+E9X8uMhdJth@t?go=ua$z)|1P@f8c4g%)NaOh@PY~Vtd zt=pF*DSxaTU1TD+|nH zLia^6O2-u{;1g>X|7>hTT%fugU15M?0ORCEifv%w1Dpp94qGwoQ}F7ov|jxLv9mDo zkd5Q#)*fwv8A*SZQr^SXLe(7S^MhgQ&9N|WlgA?GmjpWJDh#+GnuUNxwfG)2wI_tC zAT%&Q6hS^*Adi*;8uP_&E7&~FXS=Ng!o%>@t~>j_Oak1pald;AXkkPcBWL z6ql9i%ViED zOkxPW^z|eubF7EEV@yYHNr=&myLi4d<~oS;U8xMnj85N{1=XZ;GaA0bhtc>dztFSi zaC;<4zf*GbQEMMJeK1%Snmu{)V|)_A5wd!7e>erQ-#ZlR%p{0u7t>X?d$o6-(8t_N z`kug+Hh4tTE*qK8%WokZQUA&mR20W-$$jgs4&UwPfU39!XdrOskk4W0yq zX`hDKf3dQ799=>40WEbUC%cXO@!z25zq4XQ%QwH^9ih7pkUs1vj-=jKPmT00%(X$) zq_kx5r-%#OTB-jq;L*fLOdYlSL2>9F&+}?r_XsVwTg8Hhi`T8vUvL#w^S&(GKL5(Y z;=LG>xcn&f%>JP?KNKUo%CDNMs2QsN)MnDhIlHhjellz#qy8ItS)fDQDkBo%=lRE< zPJ}P)TkY$nMeJpiwEHcT~ud>10aT5%2J1+aq}t$Ku7fi(I4 z51;*xc0C^uh6PQhqu%v?pOf_d_6-N2vFDc0VZiFgXODP&oA@F1cM>TT5?-@T7|#&% zTc;Q3&m#F&h5GGLa<%w@tsdi>x%q*^>&@#27m4e!zQx7G?uRfN9EHnEB~S^Hr$zWv ztdgLX0?voPgzdN~xa$Fi!B%^GAO6g}cX0x(0`My3-@Bh84vjzwbib5|^!uBY+3}T_ z^BpAb!4CfM4eJyMWPbJ@)xy#tPfto?oipG(7>b6?8iqq9ix>v4GD$h+bA44K~-Vx{brZr5s^ zYTUf#Tl3{!5nM@LP$8+5^c8^a@~>pS^8qZKk^CDTUA+wZXN3!_v(2JZK+=hFCjipv6;dkdFCC4zy}e}fX#eLc@8EL z`Gp|;c$o>`YumyxrvoMO)CP(T$*Y~E6ZJQGl%O2)dUZJ=K-A|olFj|Y(^fbC105e4 zMPuD~JHFb3;J@95))CjKPp*cz$~`|58mHbeByGY8ijlJQXk$#UqU9I4KoO1dop#?< zf5r$_)W69lYJldy9?sF2kbL=*t?_4?TfkXS+`7IC($@4qNnQvg`2-M^R{t$$nAOOa zez;42M?6-}w$e4FH>UN)a31&nqFQzd3Gj_l-Y<_8Z>R*O#u;Cxc#P9Psfq|rR>sTB zqgs=bl8}5vp!z)yYvqdRdb}74I*y0m`CBvarJ%+8|B&_Gfn4ry+&Gn_L9#N7tPshj zC^KYbm7Tr!%uKRJ$QDBO%-$m-Sy|cSWA9b=_+7WoIp62`J%4mMr&FrW{l2gFb-k|F zzujYSop$QOq{$eveT=OYNZQ$7mvCxtq}47ev1SNeHAta z2_X7SIR=V;0IZhd+l`d6KxqcueCgQk(-$vp0-XL3%=#O&3l(@Z0P-T`H2no7G#yfx z33jewW6uG3N*`J?gJP!SJ)3dJK8MYu4>*fOI6VZo6^RaK0yanQKRhjF98G0ep zaQ50PZ#AVj1s2wyR;VhP_FMJ0B1F4tOg~)O%HvvMy-_lM|Lx(0k&d16%9~iz{z0&} zA-k(0^NB0u-f;WIQuEL=`&dk_$BK{5dhp2UcPfU%ibw($Z(@k>{ouG2BXSpi@1j-i zx(cC5csUd5<QczrSKk9r_2;dc_d4_5@3V z_n|lYqgbQwcEFvR8c_Uc(kV;uE~<94E$BNAY=162-b$jO_EGEHL+?a)rTx^qnk>0hKlAgX6))_7_B!cWMzQBk=8fH3>5+Nw z%TFC=Kc4zNm{U_(Ox2gGEpq*c^n=?mu(5{PX#=b{SQYLy!}o$mR{k2io-|Du3@j`m z5Z7yMW7AzV`y%VBC#D8@pM4p0kshb??8LVZq(}qOz#`yR$Q0t z#jBb!4`m*S^>J|Oo;=lt1lGs`y!oZGac$3~nQU^OhVfa<%;?w!XH$F?sfE{+TIZYn zht;km?$Wbj(5kGCek;95&P{?!krHN9@h)l6KJoq&v(Iin@ve_d0*;Y|d^*}?q3N~O zS5_!y=6Z9xPx4p5Oe6l8%Vy;RqnlQwunP(j7*=Z_-@mTx^(|upr1KVk ztm`hGW~pgwcaXV({K6eF{sVS?W1E{nQ}}zWOqmDOkGS?~^S=4=aP1Kq zeh%+5H){q;*XeFEz16l{O0JSIo^CaQ7sF?OmLi;!0ANvy=s{&-penS2w`}tzN;~iq z%xUHHYJqj}pI6BQCNG`iayNSV-|4`>vP61glqoXJB|juOT(?sUuak^F`re+1@48e` znKwP~h~6VBT2`9rz3Ou;h*-Y-r~;rG5$I}Q`Fqz z>agy=)u#GJ(!7as`4NE5by?bb&RfTf^O8ds1rx3m_tJ4>#T2D*8rf}1?$D+J3Nr56 zgwB^H=pVh_cLBovN_h&GIpE+8yph;1c5k4}0=}9E{&5;oFMcoGqq(*ECXbA7?JEWT zcaA^Gh2j=11f`SoTYJ&gju#@~x#Zek4hR?`?~M@&^@F&H<50}p$*s9i$wC8zJ`RS6 z+L}?JC1VXHvJO=YSKn(61@L+@EsyxCIFSGm)_i^UT%!G$X!dwrc&z(9dAYp_l+Iz% zTj_~kVHb>@FARA?pS18`j!9#mL^iD4jk5D4vb}!%)buaY#EaTJs#C2G@4BEu-Zxd@ z_jkRhEaasAlW`-96Ijf#e0;XELuZqyfFgRbzQBIR3oJChG?Ac1VCz6i?rt+Pnq|QV~Xbt#9ZB_OkOQaB_H?w z{L-X9iGF3r5`REgVe^mh3IBQ9M600t&KKxUjP*-a{#ZtP`aQ4C;jQ*>M-gtO?PAMf zht5Q|1HPdD@?wGK{#dWpT{I2#<{U#)zKlYB$@__ArZVRpNDMLX@foX!e?@vYv*+$v z*Ro#Ib^Iw7?V{n!0-Dc?|o|>$%KkIQ8c#$ zb2`pvK-f9erCCXs4!p1wQ+rZftVOq0aiuUu>bJR4z@NjS#s1r#@LhM^l$y}zKB zRAlb|~pJ!@p>`uR_LF8`Vr@o63;1}yWF)%peMcV;@{Bp?(g(oN<*ChzD%r!?nzt`&PoU9niPSM$e9h!3 z!q3*$j8n+T80_HSFjch@>1Y?>>B?#v9F*>`w7!D1=#}SPK$j{Xvr9WT+n~FAPU4cj z7?)D(L`xHoDy(!YM><5G@Ay*6+<3i@-_Kii{#V~C3G2%#Rb2H(Y~j;e_i1=$XX@2k zHncv5i0@TmcXu2IkwsxfiQQ?PeRVP?lsa1;*y^!;k`$A@W3#v|8C_xQUJ~>_BRwnr z@b8nT_?Sn|oH!3R&|pMOCzk|L+oMMxk(v%uqYXTj?Stifyc;*3pJ#h?s#nG4TRcED z`tiweY{K6~-`UAWKspN8A3LB@9uC#f zGgNahrNvzRW4~ghE|bVRJ``FO+a1r9Egl34MK|F^E_Q7<-Y9R_~#XW!ehCeilWGW&Z8%#~Lb%uI+0s*tV5r_q3~ z+Q~mwXB&n|gLJmJZZ)RA%~uey^AW+oH`O1=K+`zjb?-;k{hyAivuMxut5BEW!wP=7 zRmM#RD3=7dvenmuYy{}tpskvPm{f!=!3`HIJ17=AQOyB`qpR*aw%yJRkzmS7IYiaA zdA78Ge{`_`n{pP`8i!w}`}5S}fM}-tHSnw$or?gp)c^GeKN={9>~8@$`sI!VBc>Xj z5z5p%nO=BgDT)5F8P)6a`YJy5G_A)ptUss87%C%vCo32aPiGovN)$LfIQ){Hl0j?Q zaPKbjLjCO9$Rzsr>&G8l)-9Jt>XPUUZ{>b)y_%KFdirhFF?Urwa5XSZ1=d&2!q*FT zo*+9XEA?jYt3laI*k4oQk5pJ#bGV-9^ZC81X&cDp?qu+rdL&z*%{gi$gL84@)O$Rx zRlTDjWeLXG2hDsR;8jwaRS^>~F>%wVBTFnIr^QI9NaK*gap{^8S+`7@eN?=}7YpD} z>`stRjTm>>vUcYL)6ey8+SXb0r0$+v_MyUH{#TdlSb`0nj^cJqy7(&nY5c~-L<@s( zXgv91@z#Ma_Q1(Oq*@9u-uJ(?S9Jz4`0Rtp3aX0#hS1ARduN2PVG8^h2p#V(~%0SLy|-f|umYXFiUve_~j- zbOnE?Ov)K&pTeyTh9z=U-)Yjlz zrTRB&bmG_MZ#+c3dw^Hnt!KTnuy&0hckhwSfSV5XbMmu`7x)O|(E4~~h#SwUdvUWN z@(S}ZQnaGsj%1O5a!8ms@0p7R{DElz5k4bK-&zoTBpZz+@=ji9fUyn#_4d%>K}p*G zZEG&5c0Vv{G9#z^=nV+pnpHwW$8zZ}$L76K%BA)y#?%5J_3|Sp)2_XDK`$lgf6siO zaVCZiXd2;{M|iy#W*=|OA0}nB`fzb`hsT38$U01nu_bHdn{E2GhJTlwCg-&~=3ie~_$SALCqhl@{9C(+ zbuG1B?!3~|mHIMUj!)I+eoc{l_DHmzY%SL39lhKVe!a$*=n+>h@4Q}^P@XpMX}@%o zIBC{J_6*4?;8o5|D|bsMJsb(?87GBRfZN0l_G}VGC-ef=Y7_~3`iuY~$$Y%eM~mTI zoZEVEJlt|Tj@OITuSMdOee~1uSxkNdJcvM(4+gN>Qad=h0c1AK+Z=dr*W8B!XE;*4$dfq#PIn^? zOhUTelO*{4du`xmy!z^mKEQVN{&jJI`C!ry)}_nr z1K#P~{kS3df(VMbbzNm`)w%Rl!9jI+WX;JVkRKQXK}u2+x<=lp>RH}kX+{{ z`pUX*K`&X)^O|eo8i{cw(@QVS#`qo{U(e%Y#wIv;><}JS_Yr<2$OWvP1ii>4_`=2& z-B8k?@mpXkHwC>Qc2tXDtzBMhZZnbB1t&oj55M!L5i zp-Hu{7RkBOb`AT>_&{6B0H z2N{V%6-a7zs({EIu_I6-^ZIoqwR@s=!+Dl4Eemmp9?m!FM%Zr4bT3FvADQ9MV4bgBFk2p6<_<)SmTDiWe7k z>ql|Hrz}n1*??G+GOv==`kg;}|dv41}E#FlJP^aZ>pPrv%Z)uuUV@%ec zl~J)}uWC>P?h#st$Zbpq1iIN?P0W|lvr%D2BLrM&ACE#JtemcE__FM*@EX)9GL0ib z@s4*TcuA7OicQ@=;k0*&Kd<=rL{bV%y`Qm=TZOQLAkbc+Bo3>_*F5Y&oKVue2tGtT z*ns}pi0v!(|8r;j^Eyl^KX;Xa1S-3AJgi@6gae`=sQPAoi*g8cDvbheu8r9-P9* ze=k5wp89nQ1z8pO^+c)b&4{9;qPd}V1e=7yE_AR(h=}GbuQ8#%$Q=;5c$7asl>HQS zXeVLZPZKkLBVG3w$8P%-L=~y|g2bdZu zj+}zW!-LY4loX?h>V2NLxc?r+*=@?~_Bp)fO?cZ7U6%cMXj^1+_OJB!TTyGV4O>ME z1yWnU{;UV})|NhEZ?$JEm55wd@t_P1N&+2g4yJyyHA*lkCFNGPurhw|{v1l=vnyh? zEf`QR@#4`t=uw1+sALrg43=?!*?l4)D(EhGqb08nl)>I%vDYov`nk%IeiX&tcjl_U z&?EICx5Qvz=fcX|ZF7@U>*Ak=JB)RGS<~BIzXS)Td?~OjzigGk=`7JNFuH~1{Hw?7 z^nT_afgpY${~j%=i>pS}ZIyK&lM+6<6<7PF;H2AHe(Uk&aSdy0HEt#b=8(FddtcNS zL7_SJ^oQGAEC^JHflZLOH*TX=zZPbUG6;M2e{_(n@+Pm(p-_Nm?G#&j%a?@ z*A=c%hwD?t!vlBjggn%aXOw2xm`PdY#Yi}c{V&lRVGu(Ts=<)C<7y1x`khVHK@Rp# zpR=K z=URptiT^OJNX9_oUhXnUR-33!x6Na(qHbE`{KCy*dgy| z%6Y3dxVP_^*QA(`dIdckJ1R|=bk{gCU!Z0ze_N+J&M8Bjz1!Qa{s!CohD~v5_Bsj= z!7`#UO9n2>dSM{73bskn8VQ4-2Oml{`Gm?gnTTAE9eIdSV45>PKTmJ$8a6`<(Y!Op0SjZ7N+Ly`P?y{cz*f=~7ico* zTIhN5XIM)t`C=xx@iBU9Z0<6289=RHsEj*!LcW=$pOSC?5HEyC-&(&uyj+rD##)mb`j$HN(z-DXQCcv9FMTV(%nQ#BFrE}_`p1jyJ ztoxGf7DbX``_A&;`wSe&W2oH&Y*65;#0Ns&yCB|u+{cd0RD*fySlHMt#wESwRyqiI z8OT+l2!=fyp&Sk3cKxCXiD`r{14!#M9Y6mGnLtXcU4bP_Y@SttrN#e)>#WDC{2`TA zI$|DXUQGWbQ^_p8!^x-*9iJGZ=pYAx`a|=eiDA6EPr6}D!IVW1VEp-ojr>9z4Jc)c zhUYX(is;X)ZxgqTHLKtzS&GzPzT7_y(ybt!mS=wqWr|N)@@C~Zei?NB8V{Y3hr#Q6 z*z7o!X`iMVj84Li<=V(PRa#0L#SBSy&iz?&u%U%Yj@AI_XYO~jY zFKAnm!ItP~j|zCnTWtoXjVy_#fpPLOI6wZO5YncXM`cH_o^52^D@53d^=vAzZZ8T0i0u(avkbrPr#z?GB|wqVM-R@N<2>C6*t+hKgECm1(64)BH1$Ka;J`M_VS zkC8bML-}f|UA!kT3))EVEWX?6&9u(Gy$UTJ3T0X!Yp!W4J?o(=kBoZ^Jx;vGIk}ku z66eJjfjTKja;>qj`OQW8lTQV&YhLt+@8;@e&q#MR2>GtMp*l3*>n2?BXimLJP$S7; zUB{n%9xuW1%`Y(r%a!kC z1A(10Ns5LBoQ|a%+g%}dcgS1yt*^sET~XnTZC`lDfY?X7R%h+gqG4sXKy-Hy6rM7c zsM|L8H9FsBF}{$p&+(7=`H8P#zOFQ_erGJgkS;5Dxl-D#FhFqkaBP)0F#ng-PlGYE zdeCn?5G43nw(!Kfi;$mDrArDIOKq;;SD;Eji6jo$d0M%o6{vj)gr!uvqG76j!G!lbleD>pTBAnJG z>AdnmKHE29s`vSrazgxQU_t)*rz-Q)!&hTUfDP*kI$D2^sToplxvBHN(|V-%bZb|$ z9_%*)9~;=6Jd5MZ?O4_WOd5P^_>X_f+p5!Xaz+AM(;v1kZu_%DkxVZe!43$*aM8e* zpAQWTYLGjm8dNdv6G0Uox>ao8*J0`)`u{IO<+nfN?Odb7Ymihv&oYdO1Y)jXak~}o zUFUsAc5NqgiXZ5?WV>^pAWh-21FOX(spGC&NNX=TIg9ES_fHwziN!*3FBX_v2*<9A z$gu(!z1iJNgd=pVHq=vn@|T_7mUDx|4&j=<^Tg#l=ns1d&wFV-UyWvJ=(n1Xc;j%L zwGel#cU=V936pm(wx#m|MXUP&~Vq3D=Ce z^{}tNm3+TT>I^!#&){-C8sG52-V@JT<4?*+=@6J+Z9I>H7DHv=%9V_MSL4~WF#GID zfh6WdTgf={Kvku8Yhhj+(6*9 z0s;c2flNsSMi1~~!`u!)FKB9QJx_r15+aO~*y--7i% zG8me=+^(zAU&dRdsjc5aBeiPtinHR|=rQ@v@pk&5qN~hSJ-nAnb>Ftz#5ZkF%)Dl5 zdZB=?D5jW0pXCQ_PwY_G3v=i9?_NcPCthj(L{}#hlW#X~y&VP6&lJ0Rn|qCr)r)8N zPA(l=XOmHW-kDS8MRL>9Kqs$w7~M}jsw8n%T~))kIm zIOqgovuxK|qA#$&q#jZYdtMwgcQtcy@qb?w`M3EJDE5+B&WW*C!O$M=;DtSNn1wRv z2s;#Ixn;|P3{}Z8W8%HD-{?Tq})!KH{Xg9I@zrEgCj#qI_$Pq$MA@n!_Jw2+N| zg$IFEsQS&y2`##A|9vQb-n7io8Z1ZdR50^$_F#^OXgqRmlScgq-p9d+9_j zerdZmdq)D=mm(k0{>^5a4ks`Bmq80ky81!m?Oj^#*42aQ(|vYKS)P6Rfcu_1BOIPI zZODKoF#2d=k+w`%s2}JL*!n8%jW3{;5ajG7;Po{zjTKtuMrbKkRN{R!@FfzFs=< zL^t77POd(kF-P*2sCn0Dj$|ErILbZmZ)^62+4UwM^?lUU0!dnh>}iQ59E4){cAam7 z+@yGVee;!E!YVxY`yQ~8R*IfbGnVt>96O$Mz2Ji*q zVsMYcFMp~2?AUDmaktg`N)l`Nim7iVV>!(F$0w#}O716AJ_*vlK+S;wgafCI|3^`t zL4Ya7xK6?!_7!T1yz5{9*;4v-D&r^5nXdJ}*B+IhZrf;&r9K#R3j$?gzit?sv#Y24 zh%@*-VE2(qlkzL6bbSgzBB!^AVjI1LNFVgr&CtZLWZZo+CyV+BgpGl6?=+LtG>ay} zQ4E9{Zf~;ZLNQiLwUe5kPJK;HPU>Vj)?3D*ho36$i+^-}d-)sbhPSMI$r*a2(x1#{ zK{O6~{4OR<_mP*c5x90_IM6pkwY+-b6}B_cufL7m-HA_7Xz8e#t}5C+=j$-0_F@sx zGHCXEeH2FGmA^qc#;+M(_(LGYFg5@sJ+pCzK?suU0@Tw|5wNIZ~NVZA;LrBJTmz+ z$Sj@bs-m1#tWb~4%I+csoH)>0XB%J1K%zqk2S7&~d@V)*HQ)~|b&>XyGbo^s;}nAf zr!$2Yoo;@r$A9=k5n28m?POkP*&!(m9*#J9B8<#v7#+sdtDyL+ecx&lW zR`XhO<3>B3ka^bWUkN^PAW>9QZIj6=K!0IOItE6e;_EI@!M?0@sX+WdWJkJs0QHBTG&SVU;eI;QZ5HP4P!qGsHY~ z;i2wqO7i!TRj(!u*`J4(JbZm3C~tp0#fSoCvWpK}4ZiZ;^qT;lsi?>X99Pf_(oG%{ zic9e8i+s(_#&=?;su3f$I0;pDFO_{r{e z?{bZ)c$jgZL?XJ<-}JA8#N5^|Jcb@kc6afS@ccU3IA+~(h&h+^?W2)3$8UEI>jl-$ z-(YlW=qmMe=(f5}xbFT>HVS;K<-V}t%uoUgImGi+)Ol^J;^CiudGvP5$WonFY_LW* zFJ86h12yl%`R(c-WK3fL(7ZB@ss78={`V|*InTPX$UU#%`$lF=?Mg~eiH?C0ZcNKm z?DqJ1d2+@=E$X@1X-J!HgIC+ls^4}53bl{8-ysY#!)9RZYP67)&eyd%-{n2*<4PtV z(vr(X7L!3Y1>FV{=p%3IR9l+JSMw2}p!*FEllZHA6Eu~2sab#qr?xro51>MlKuLrZ z3K$0Rw+|+Molw2h;5X`gPeWa=_UA{U8ZK+u5dGavx0#`YCyRX_PjKqK6_1o{Sr=i( zC><*41coh5G|$OCp1-k70gh7O+jAQ>kcH$)19nN#MTuprWV$W+(s@LWrddZ40N{2Gzv|Saxp19ujfR zL;TWDjt_%sAt364m6a6-tJy!3Vn!Hn6iNIj5Wy~_nHYN=QN&BHA*&cGx4R#G)itQf zL%#)i2Ada~(P9z~fDL*JH!Ni0nBJJFi)}kul53F4id6sJ>KuM} z&`p}S-M8+j;R5Uy4$Y$KHi}V04fqZ!atg}9h|S98=C!ze(hh%7I=iSI2N8YJ-lDkz z&LSQVxxha6bu|9X-qHjsH*pS&LyvSyrUVetBeMjugoMU*uS_anYTtWf()+}J{42kQ z-^-KMFA56dstk^2*Ht_h^+j6v`+pFDzrQ>G87K|W*ocWCn5z&U!uq`l@YBW!WesuQ z!B;G%#UUQ6cNrKS90Y>@8$y@lV4dC8h%dqgzo~iC|7?>4)wF=_Uw3lAc*|u29kDuq z>`#EANS>6#r+<>UJzQp?`S^2_z(F4vDGJz~pitou!Txi-4w637MCUa=)=9eWBw`>p zK;kEA+{1qVvC6zp@z!dJ3MskEyht{hiq>a~4}A>oj9FMRUFPgEuj=he@cgFzlR{Q!WP|FeQBtk@#5`nO9kk(oB|Q}RO=9)C}zv^B#Q2ia%(G20{?tRY_ap4 zO{Ycfw6-@=#yi>6QZGugu*$a4L1dstQh6hfUi|OY%2)oWi<+;$xQiNU8r7(mw}|B% z&lMjTzPC?R?|m63?$2Lr`={Yx zzA5wQ4?cLC-C&N&{&y8D9K@g=N_sBaCW_hza|d0K6@wb^2k)%dpl#bDaa5%Kpydx& zkAujj=Q^Isj4e4TPX?!&hJfeqEtlJ7=_>MmTM2)_D{tTTOV{JUPm;NyZPTLpb13~p zPRdKAs*v;eqH&DRgMPk{p-UKLoo}A2)l;sYYxE$;biF$A#n;@7@Op8xv~ahHoK!>8 zNS=_0 z8yG3I(;@)59^%zH2lMT}2<;U|skbPsHvio-x{KdHHjY-T;WHLiT`)Lr1C?P5ctAz3 zzFq|0sHl<>ArLe=<|U?2iNF-xVG<=Z{W1pRaFmNAQ*+A z8@Qf;cMP}f!uy`!fPm~^Bdk~89Kwa|lOo{?OCi@_1NlOMEf^)cDPV6LSbD?^jbr)H zhlmdKhl_4@C6l$mG+Y_AImR!x;U#(+0qY|YD@$wa9SecwNk5%K=&o6;M+ z?(IkKev16SYutEBt$1B1gVTX24bTjor16wPU-!qF)KFB-SN2k9o}kC!x_Hy`%9`6pW@ zS(5(wcrQrTLQ(E~m$EH^)F5Dtjs`YMd~qp<{&I>Y^+V}OH@Nb0jTSVBT7@`(eCvCK z_*o&Qg5>=Y7op9KYlP!sb>*gY&P$yc=eYvUezN_5!D zX|Ky)-#sk}Yl+9R8@&?2gR9O0w(e6M8bevMet+j6E6=9ZcNx6@J{r)($nB^ATJ)ht z=|4cv3cUiZ&LFTe=>L@i)4Z<$450Ngw5HkM9zg%oAGi{t=NuMo6h!pICS=%g zR&}Vob-qjuJOp1Q@`9KZy};Z(#sCuv1(W-QJ%izPWbiK;=GA%69#q>HW$gX1d)k-c z$1k_aH^T9um^?%-x{OTlN$w;z8#WRN?gC1`%O2j1IJMFK`LI_96~Ix3q_Zuce3;f&;X-|f4+b&hT0-!Oyg15$A2edXawPBc1g^l?imG}stsKoMKeb`7N6ktw7fg$;o46W@v({4 zKDl?ayCkOdGJS#FvUa@5^NtLEEU7+OeD?=D9&`83g%g15Li76+}gEG-Ige<<=$%w6sg$i`xo!VI4-NzS0P?QA{peD{#_XWZhl z0bY1ItLbR|n34g$%p6NP%=FE*E}ot!9*t=K0kX#Ygb4Pa^`N4j+qOkPmI~$#E%!Cu z_4g0hiq#)^Rt?gWJF739>t?@deRKqVG#|D38QK7@ZIQ@WU*?a ziMmcm=-sKG0=ywa=?-kks_)yAg*upsWi|5lAYw&!cKm>5LHI*JNt=f(1}$)Wj|kod z|BjTbtj%gjj#-uH2FbAI{kYB+9&j(qDmaA@muPM)D)<$2Affp2Ll#WC+u&DpgPtq< zD8BuR*(hg%%a%B7+C^a=FB~>)YHC8aCN>@5Y7rjg9(`vG zr@XjJsdnO&0hWzu`pur=kM?6EcgdiEKN_grr1Q+CZEx#NizQm`nUtnZ&n~YSl*@^0 z`yE^_W_CLLcgr;roM?2zyg1qI^r%%X0gFu^<(((!lO;7=hV9jM;RkMZHH3ab<;8<1K>){BGb{`T(Zr<0bj}busg{XOB z3=+`6?(X8sXxVyB%RU>(vb;gkC#R-%74gD)S)_kWI=yJj2D(u(P~-%b<}D*OYoOmt z8)Lb6 z@&XNhcE@RN%Et-h>}ZzUWMHKFhVY)shhZ)O0m!x(DRfxZsMlQ72MjpFc(??uc7Ki( z9I%Cyl$2sxrD*ZsSZhNbO8PMcIFOW$@emObJ^=q2{-X^c7kv?P8OJt^VXkgXh7po;ZBGAK|zo-z3GVNZh15brIO4U%W`_N#uXKxV^q%+{^mzZToY-&U^!x?KG!`xFddIGmnU6zwgq$6)%+DB*U{tDxBbOlUT$VOI|evO`!bew&@GZ z+S}2U8ILbo?Tqqdh;0i7uvFNsUrr8g-Yl{p)#P06wD7UsH>gp$%HXwn>lJlK?+C zO!sKewf!SLnVtNG=y?blQLn~+8atUGr_;&`5qycDrrnwSLNLSj_P9KnTD!fQT3Jm0 z)%DC;{4=dzM=^bd#HVHg<(ufah|du;U{DeX-luNC(_yuqdFmeal!w!Fs2+SrZ(JY3 zzIN?1SQ)6<59@&&LI05Z-o~puNCZK|)F0o%YVuo0hb~x-ifZwJ>JZehUmEM}Um}@w zP>kuBn5aQXYd&6y^d9&@cETj>H@JMyp&F@eur0la z7S-MiS4ZHl`Ao!P`c45kAt9Vzw(r+W-QfsAOwPmG1(1zk2I5K)kiA2_-E-kT0j<44 zX0n7Ih}0qSt`+>*4K9>IrU9Jrbj>=qVv9+NqT#E1>x-wylc%h1M|%%i;gb4L2--nYFN=fP zLHx!nysZgXY7B1>D8>D5I&El_!t9R4az?bY(i*cOS0*)jHT7ndE)$}IJmtx1v!>c4 zy)&gMB;Yh2=I`w9zrJ9>9e1@cyaW@vV0?^h63H5!^S)UE*Z-99;HP${ix`Tn{Jd^F zNZd5>fv%0YHfrT}jn{${>&v%txeaROX4mSUjgg5?)6qn{ea$XFAkcKLZmy6gl`%-8 zCQ^0MF}~BNV$XI@S%p_%Cs(QtH6xBv^4TAK32cmCiB0Hr znhF)8pk!nW07}glmQK#L(C+CgLk> z=EESi0pi9$L5+p@5_#tR$`7*yJ1&J6aGDW4~D1fNPhIm31?)wGzl8fmvkW zB$A_2;y*K^ix?0{vefAQ9);f$JS}y=W?^P-?g6|H&*kMocg+TVOs_zr%X9X(HNEUX29iv$>AaN28ijQ z0w>s9o(Dsx8KYxQM88X>dbJl z8dldaAjFO5oB-c4y|F{^udamvE{S<0$p&^&=l0geAxJ}Zk!Sund=gG@kp%4^Emq^Z zD>BQw5CK#GP%B!zG#z;AJgB|EO^U<6cN>Jk_eUU3$DJWZ^bQmyHB*VOisKO8$~;Vk z<2-tBFg2c{5%}E7Uy?7w*phyD?%-|~mW>(_D9~~W)xWVwfMU}+ zrXC8ddXtQDxxCBNq>_Kd-?}N(`;8mo{gaeEWV)cdKY*D1u;OQ~DMd5yA1%w$H(s|^( z-@A8hL;3!TD*JoNnyz+_2d;R)Jq+G*f)H7B0TI}q9t_rH9U6TkN`T4&|4+C07SuGv z=cb4R0#O3M*vS`+Z18=)zFJXMjDVxIjwwcyc|h*-r{tyKmMqzJPpn95!lOI&8P)qYKsr5xfhav1dcfFh#tbRok7^N zBT%csFzk`2GsKdC$=8>$O$hoVPD%C~H=hR!8y&a@&=qANY1RpvV1yL}QG&@yNl9ay z62M^Tyfh{%D*6RjL{ORE^fB~7Sm+k4MlhI$xuK$Z%w&gEL|{vI$8u!%@_blhgB(%# zQjCx0)vA{Bst{F1&<;2^h>MDP_ng3scE0vf+(Rep$9gGj91zbq;kv7y;tGGKs4CkG zVmylzHoeEsoue7U#J!UBO|f*|zD zWV89z4rWsZ{deBBnrYsDBiKRjfs@@G(2OghDakm^^Wxdba{6rfH#?_jSmVR;UO6BS z_18|xjj)dQ4Z*l-(cZw9?1HmZCKzIx0^jkLfCDu@^QM(w$RP}-zGwDH|J2V1tE0sU zFGE6f2D4@t(wSN_Oh~Gf?iu`?o*pVQa=q5MadcnrJATSYV}tTzhdk~ z-9xV0cgFx)Ke>4ICYnc@(b((dT&S!hqdYu3X#M$`b&Y!TA-4p&AmY`u7-DI~z}@2e zs(=+r7;2f);&zYSqa?w0$gz_^>^C0H11*H>0H!@3uy8pk=KrkO3XUx#-9=1bJclk> zr(`GI4mx$>2tLLGacOC={MT~=h&_l!8cJKEy2Cg|J;E9Upd`o*@!tVf$)n|E#6$;t zY*NNEhIRF>3*d%)-e#ESInF@bV=vbotKO*eNg0cyJ5&fKcy927B2*PIMZ- zdj1%*Rw+MKW^=h?OI=zeH~`>0ZhrNry1_WD>FLT^XEwfjjA!1eF4YHTDEHYdTz;JZ zAlH8zz;tn7nOJBXaPjBw7XN~EdEbUpjmvg#-RT!aX_zeyE>O3Jzkgp3xOmVGfJ1ne zM~GiwBwqDWR`%1UPcwd3!u|*B05LZ(H-LH;#TLK6zYGdPFiE+*+H}Ya_Z66soXZ|~ z{Zl8HSbj6?H-v{=&7iW`?&MOw`Jq%L7uu-_Ly+?y=gcdqK(`*a`rUKXKlwhW6O9M5 zFyME_M67$D?y!62Wo12pw;One-^mSNCV@a!VjU#Jm_TFDloL}y>TWl5Y(aVC1!XPn-XXHS!^Y1Q<-y9 zRUFz1<-@;99vH^rNcHse1i4PcyPg=2f9@J^p>UtI2*4X4A(N%7wngoFcj#i{n+x@E zR99kVx1nTs2VG_e@>`nZARL70X8fdV&+kdg$Ie*n@BfspK%3_k82$Hi0pRq^Z z+eiq3DJmR?cbh>IkrF|^(M=CPDQ-%tYkUKfD&VoF;Eu^FM!?mCd+XMzPs=@WcIJQq zW10sKddtnB%VH!i0wk!#USxnn!to&mLT-eU3GU%IkW&Cr)E@pJ&=<0nN=EYJnqosG z;78tNG$?A=FDSNeo$mQ;(mZvNsJjJ$yW(nUYM%$m*$uBFzJrV_@&~TqD!$$qX?RZt zJP3nU;ZGKCucoLd8hG=SLM7w^N6Hb*D14482HUusoN>7lHi!**{Z{&{UtnNPuj4E- zwm@v~Gz=f;zrZ+(t+pS14~F|d7M89SxS`te)T;t_z<$SSWUC4}ESieq5A54?^p214 zO|#Cc4{q*1H1y%r@;CNOj+NQzsb26WRm8&PKJ8Dn9-mJ$bHriCD7__~rr~JQxccV@ z2k3FTn@CD<6T@0TZ@2d4Hz;TX?=t@~l;vd1sk^ZnpkMPdPA`cVSa?0x$_ zhiAVhy69?8R$Of_T~l6djfodr*T+Rx1_H_^Ou_W82})Asb}trKqtEDXc<531ou|+T z92yZP^io;qH`{^L!BqGue++DTV0r&^DGs5`Gt{*9w4&~hrug#P+vg)z*`R9*Ma-OC zs$c(8Z~#xaZXg);H{u+s=mq+YHf9^1_N|53mad5Qij~qTtjkvf%EI}&KVUa0rXqtCBJV7{EZ=2_D{1-NPX3+q&;3C@o#EWHo_-9t zE4Ew$Nlg?USm*{fg-^?T+w;_VOw;O&SG4*kW_C?z^u=i%N7`9r$~mp(l;an>V{yW_ z-gZR4Wk0vTgB>Z8X5MWTxQ3EhkY0+@qIQZ>dhqkZ%m)S<_x}vNW?9ZOmQ7AjFAPLc z(*uB^(f#w_iIS}$(D1C0_PS2>c&YTBBDQ(d500RIGgZTh6ch~w3#Xi%oWTGK9bM}s za|3;Sl0$GVc2wHVFuwiUWe3Xr25bXKSJBU3+<-`IrS>6%O6s|oox%3Zb2EQl6Z5<5 z8A-+{y#seF>q&6aO8}S?BOVsNSD6SUKhNtM7?6%Cx(dWSqlMdo;p)#QH%$nAN(4_v zWuuke@6MVI3qRg8H3@?WcyO9gN5Z4H&FNOm--y*R1gBg)YOe?Lo5R5TQ-gwG(`O*$ zXn2cBdWf2yMnbx~UUxtrbY}U;HUdliSum$R5!yz$Da`ht1-fGtO|&A`o{z@ny>Zj7 zYx5tB@N1($-^+Fu!zg>AY)bEu=d?*MA3F6hpjMj(*X}hKocK0Ax^g%8H!iNtc{CrF zNfeX0Xn6_a=-A!gMF-h7bjDNk8hHNlt=g}>CToNWMRSEp=Cpz&IK%1;Q2OkgwWVHP zehl8)c>i+YluUs)^vp9>PBqNd=3GjZ=Cb=2^_D$%3F7gZCoyf~`Rp1pK)tg}+I;kQ zmVlTK+^jsP-H_c9%9E;alkEC_8-jtp5reO`{2ZGunfvs%sjgb zeCs^4qn2~JY?Nikr?eFZZZBlEFGgSs5)u-gOgR}E?7eH+*N6RWAhFGZMQVQj1gP_$ zvWV=5P(U=fe93foq310q-w|{V=6!u8lb2g;R;Z?;$}WTe(dRh@Z<1X^*F7w>uQ3>Z z7*(kt86qD%3Cv90x7z5^$ysVFU_75+b#9yA{;2eZ_TIf9@bym|a71Qi$LS!^ff&Hq zYY&#P8SeEV#uRED-z7!qdD7@lX3XC8F?vRkF@7@AIGyLxWZhpIUx=O8^(=2Q{ypGo zt~U8d^o9Ssks3Oy`@9J(Azk`4u`*S(nz91F^_SiSrY0G(R@AvPU1^=rdvk{(ztbG4;P%<|J}jvT`5S=yWXT^98*{4e{^(Iv_wQgVr&|vz>V&17t-6%m31Z` zP)U%Hf-#F5b{TK2LSO;_zg&@sY}5FB*Z~5GA*{#k*Bi_|=#|QWUV=cXz@tjkAQ@uJt7h#CLbCYVJTQ7f4N^Z;$& z&DP%DUI0g8dYH*C2}~j$Wq`OaXeIs~1Ps&)pZ2kXmQ7qmCAM%K$o0t(I5+S>89A7O zieY0An{z|-i+!dm%_wk&j7HjK%<-SmURfZ?8kTl6BvJ^!=7JEiK5+xU0$*KG+)lfuZg@HC_&1y%7 zx%Qn{30+npVlUFHt0%Ba69SqZO}ptA#X!JU3Nk?H90Jxgz*TXj31Y;)LQ-+iPCHB8 zGw7VbOm;vAq6=DjgTa^Z2te7~-|@Xq+OocNb?M4*=hWcQj z?xu(Tug@d!98EM}upaaIImMUYm**slPms>@TiBi8|0C}$+^S65?qSqHM@1ZG2qi}a zB@~g8#sUdJx zd*A!MuIr3-u5+C^RgVf6&idpm#+Zxq^o|pK`qb2tO8@*m_4Y#4E;bn<4e!u`CZE0B zp%y*Q4sEaC$zasiV--9>p|{WAigc1~^rZDe1y3PyR-yY0ArBcs%D-qqL&7A}v<{(5 z{W6n+jKb%PJK_`rmSa4L-~Zb3R9uRXbLah&d2z4G4uz#d2>B+nO5ohN@ z9&j($?f&028=&xy(OQa9qqOBrm7QY}+lpuY64{@jl{51A2hI)0wUvtL-Ye=;B6)A$ zR9Q{5ra&^X%#Gu`$ zMBBsDKfGca5l|jHllkT2V|0jezsCF%?}pK|`64x!{>?)(lY}&UcS6Xk6O3bAy`%2R zii#EJ(enGz3(#HPb)1zDpp0t(=J*yUj&{N~8ZO=Uu(Zr^y*VaVqP#X%CL(hQz~nY( zN|fV-qbUZs$D!S)C^PcY=#}Mq$0R6JG&U*K3FxN2(@!I>vVZ^rbdX}vko(~H3|Y~1 zr9tOyq14?zX0e&<5mSwa7rT(Ui3W+Uhw`73QFdihg*{;47zw=Mk^=$)7{az+0C|mTGMS_a4ZR27F za9;Hv4r!Ip{Y5^!XW;5~d(<*8kPwBk0${d&o-#5rsQRf3wgb~x6qY{l+qv_5OX;-HTRdI=<5F$J3DfZG&adCsbv5jb zM?>AF>*{lg3r9bP9@)QI&y*&3w;F9t?z{7BmXw%J2~P}WCi~MeEquG&T5ZFAWA+Rg z9)CaNaF`q)c{NCVEoWG$=24ZIns;4dc=Z#FprQrYGjDi~u(PLBbnd{Y`&i5o37zcR zt3k4dx;KZgu-zY$*RRK7p;W%O1oN9TM74Qu+YuNXd`t$d+$}&<;$~-Woib6k#groh zE}#@*{k}9PMbKgWx?e5L^k;dk4Kk0cqFRxKWeuN%VQ#@o)GlCOoodjLBV-bR80Qsk zAh#J7DgJ;cO4pe3gB%oV_-_rIwOhyRQ_fkM@cTgM5>QeOokVFWRL%+xSrxTnx%ua; z2K5m_tEg1eEnwUAvo^r(rl3$viU*0y6orWqOW{+&QBj!?z|k|Qj|gBP|LwOi@PRe( z9(I=d$tn_9@TG2BRvM3+ARhid{%wRfAP(>7qU)?A3wiec4JNxy0&)fyMh`; zrd6hiph_O+%F|$mO-1#|Xdn4k9}HMhgDu0lR72w`_S{e{{7xtD!<mqJq<#0QyNX{{jZ1h< zdbbv(=QsH_a|Z{e16=!vNC?%KIo*6HvtP#e*yn|P89Hb3Ok3V1_|+M=pcCqP=9N$$ zNfbS<-M(Le+7S+`Jnkx|)M7#dH$^X0sEL^^E1!U9rel1#%PIE#H@jbFoWF2^RR+f% z%WR*y<7s`gL>49)R7Uv4{Z0As0;)S|*flIEs0_LSD6`A`L9jj2)KAJZBOFd}n=U-w z@SO0xA;_7MAyzN#SS)s_7~t#JNVs*qgN_v@?2^yXgu`8{_+tP=L}}Hy{RGmac2sj> z5JpftKmMAZLCALNSy>AKUC;l1LLS3tTA&VGw*Vr-@NrpT1#r$wvUpC__vL%YgU9$0m_cqCDrtP{|2j^UpL z!HY)({C(oYWn}mD{PjR8^E}sGbEH?`D$H=CicqO&E%R_wOAB_9TLvCwh8nYWxo4^8 z#)^qv!L2tv!QoKkO@)505L1JGgsAgu3>w&Na}P;)mb~+4p4m86DX)@vxe4z7T4o5R zh60*JIsmr%GS~rfSlM7IIn2kWiZfe|;ysH@n6N{t<8)67EW;Jx^o1CgoQL|U2PjX% zwnP7*e$oa$_;%|P+Fa-B&&5n=W->BHuJL)AE#PR|1k(*U>mhTKarJ|ZiHRKkveu#w?Ogum8ihLC5y;T zjDo%bvXX|fG6^|1H8b-m8F=Yt0`p*T^faEfp_kUgbkAS^o`-s)pGLJ|z-lWJWTJhsx}p@mGj+ zM#b2^{#|WPrh)h|8ONg+nm5b*4G$`NSEZ1Vzc+iuQA@+_TNOJ{5mxl{`!mCcj^W|d zG~KiRgh}N#Hw`j>G|-%%3g}9GRfk^0T^wwDvJ0I28iheQMkz~A&EBUT-qY@6TgtlX z#gW^7@j|NFps-qyqzS1dkQA`uBzeE_tY7KjIgyRz=R3n@dmV)+#&u;M9K)`D>e$4( z7XBLVyBg2F(*u=zhFVoL%+sm|)MR$)+MR$U-cX|4+J_TZfhs4+h zP`54@O_oMVh4PxUvY`ju6s@#jXRM<@HVZK?h8THWw8T{ap0&K~z$afoAy5n{84tw; zR9%{(?^65arqaO&27SUj5`}p-PK!QoiwgdB{rr|3^tWWq(<;i!K6&LyCCguYwO?Jo z|JtgTLq;3(no*PvkJGz#>m{-Fm=Jih-x&qr#=WOXu4NKUC1x;6QBy!&GojCL8B>FT zSxlktKLHRglU9@nR~E*aE&z>v#$wr-Es4#4>xm-<4z#2BHxV4B(Qxx%9jih~V$d4$ znfQD6eoIJ7n#^esw+p=;F9_4>fY}8%;d8nC0Uzs7bE=;RxDgnb-{&evfYI@_=jTBJ zQGId#zG(NA=iKM86`Envg5co*@=T&-p7)fLUQbAbLJ~3=-E@YW2HPEE-dQ!414~u3 z4f|PV6eC|IW^1R9EME8^qAFDI`tA0k4r;3V85R2C%+B#shZJbZ$7P=BHmY*c-eD-n zYxwFnnJ(7K0&TAZ=9VkenH^p&Os(^lS2#G7WP5U?hw1xb;xG8}<*1o!1tnsmoETpB zSRX4?l~KMKqn5{U&rJ35j*vIKS~Z(1HV>6~BZ`EJVvRFIk~?UO>i;;i_~0GIPOLfG zF|$FJIB|XF*6I|073L&y%0MdfH}Yi8I)jbDdvYX`ZEn`wq2Gbc!KMcJ`&N|EQZYqA zHyW>^&g}o}%7SsmG1*L$)X1 zjvo7wadOVBHXMxtiAZC*beVaLAA?f#y|>yp#}r3@q?RA442EM(uwO~sP3C>|gJm$E zZ`JjUco7vfELeSFo?sANy8N)>MZDExHx>4s>z{KIEA zhWR37UtdN1@^C{iUspNoPWque`I0r)cQ+atsO5@%*(1|@rcQez>-V%oRcY0H@4tI{ zhK_k$^?14Eq*fSmJms4Ip8PX|(o^;Bj$moh9m{RnyqV^iI|?7>`l=_qympABKK-QQ z-puPs9C)F>1hG5=GOg7Cv=f7&$yml+qE*)m?EJ}M9?M<6tZHZ&53V28fdikX>}7R( z{_>?}_#+kmmToOSDZd3y@9Y7j0A?73UIYe}ZR!2L-LFEo4Gj^AFwN|T51&Kgu7XzX zR`8;ZRLRN6G@-JZ>$T|_Dej?7qB?Xa1qHN83|Ufze~{zxa0b{=oeQc7qmb=IfpB>U z!?*Hx=*9tZ=;=-wei|4Uj{@6Gydmrc4yig~2m~q_*Q*_cBZ*D5u{L)FH8fdMQ&XRR z{!xSzXiL}P&EMNqk&qZ)c4^sd-@XE>6}NKnwbqZiCbL|RzlOZYzHTID{7Tc%DUq`- zAokNe2OqhvBkY>n>h0TnEm9H#rgC|0a_ZwZQ@riA`Rtb=8NPoi84_ZhVocpNUSb!} z9^I3$7_>k2+)BG_ZhxjxaGdMAn~Fmb8oMM?yMk4OZ=@xLTQCnj|E5DCPSJ;&K|YRS zbAr!8s@a9=GVRz8WlQ@|>@yreh^Tl>k-nTyk@=EmvcE~JI3<<5jC;szH67jE^3Swu zuk4KT2Ld)z&q?!EB}_N^&4^=Juz-rcQ59?>JF9ehnf|Q)TIp3hZ1jt2d*xOvsd69Rt{=?BrW#y|H_NOYl zbNn46jW7$>I&sAu#$Mlrl3lw_G#$O&pC%QZlwdv`G$j1VO}hA|gR@J@lPEc*o7cRX zrzjUpl~gQGr42RjBqNJC5hFlJM&@((O@CpItH?Go^5Sp3$6J^4#3|PB^w#5VX4;17 zNDoSu;(!;trpFCmY1UcN_)02Nrr`b;WnVvjpImCH^+?#!z?Vat6m%?{6g0OuK(56&pS-xMKcVfMf0%!rqkYGzQqR-7 zJmo@p-I&EfVsYzPWa6&VC9B_8OfVK4;*OumXSy-4m35I-u-SZ>aZN1qBC*CE|8-2bR? zw#KrE)%6~i|IIY1fblSknI!#ptk%JYH{GZ%lf@PZFjB$F?tjx4jw$ZS&G*0jcBKjpB8@|M~-u$Ea zvcc=$`F}SZnNt1v1g!0|Y~%0sli#>?;)Q0rufKYK#Me{s`c0A8{kqR~=G8ZGnZL|uA@&6JIGaouUvl0T{fRh} zRX)2z4A>0BW7BBPaXZ-_bnC~TuYBvkvG9DcbTiad!agKlWjmg~wc7;%-U2c`T9JAT zn?TC#|L6PP@yvVntlRy#*+Dsy?+rc~sSNb*sEG8D*E}v`rdSpfCiPkcJA3`s(B1N- z+!UGn_Cfg`aV$T`$h${mrPO4aqf7mW^bLkS&b+cqw=O>h-e7m(a7ZioYYWWcavyFcj@9WhMfCbK23aLkjy*# zA7UAjX%uDDebs{Qn%}IX+v&CSv>u|47W5$bFpi?Uf8w?UK85$4&>T0N6Y!1iufO{E zeW}7)#+FV22NEBHu9M5|*vy`eb+Nq#EJF4d+JZ>&x4N&8;+3R~h>a|9RM_-Z#h-rJ zbsg8-ifaCz`LO6oXB3`=FOna-4WzS9b|oC1inU`L6XQs{E%bK}W8uEPiO6i=zb;hp zj{>Ee87u_=Q>FQ8$Jk%)mQ9z>OP+5*K*{LT(e8ogY ztZ_-BR#g4em-@MnpMW7bK; zj}5V#zjRtW*Z;uay{TaCLM@M-)XdIdofK0;kw{sQ*pF}WsKZ|>*vRN)*Daq^`+c{@ z;HQ472&ULsRhjPdLHZ(kaTaq}##yduivE=kx9EgjxgVattsT+(^z(8G zU}DMGeAD|Bh)etVVDm{2Ez1ec$ug;%jnva^rl~rXUL!E;md4^6|96i3`LGws|JDxk z9#H-sltPM!eWx@3p^Tjr#9~Lb+BB;>6s%C{kx*#7X$u(>{GoKszy!>%_2fGYqWvqmstMGNzc0)IbeUzHYu{qxKB%#WsbcbDSpNA7js$V-x znAg{w_?=fTiLI{8um&6>E(SUEfxG2D_YM9rV8<0TP8c+9ia8tKDvt1~!~t(ba?S05 zKy7|`?P6X%pg^HHcj!$?U%f8b7;kjEWdHO<(Ys1fVp6;FsM&{?!5vZGEn81pk}_XU zs|(46Z9nlK_Qa!!=K@=1;iJG++_l=!H@@o(tacA(;4Y&ej^IfreTwBNt?!qn8Y$6c(&m)E{d;gjM#RbzRREKyFdl=>XmTHv1 zMK}|d6w{%+pDqxG@BuYT8vkW^4#B+Y_SJ+z1lt5T5!kSBk$_M%{a*k6^|5c&BYJK zHmUv&k#b)Tw~dUzr!;#InxSB<{EB<&r`$r6Wf4*1Q|3^&oSGMumYt0h~ z`X7>RsO9Kh=sY37So-qPx0Fk*eA$t>RWjKKz-iz|6&`X~1XpEWq&#YOZ5aj!-m_uh zAa<=?uK*TGXjUZ4V=t%qKt4-CXJqP`=}URZ)S);2ATl95xGdGJkCVk*!;J{ExS|S> z!#atGO^CR#@!PXmq!;}hN#HwW!phhG2)NnV#CX5+=tXdgElTEMA zni_%F6i24Kv6>048oolZtAkN%uwqj%?CInpY)NX{Uf;Hf`&D6xH!j)!dY8_B2dpc0 z^#EHjThnI#iLsFXe2&bUr;?kZ(~qJmi79{uo=L@4>JC0Uv1d7GHT6%{+IEtFkt$yhenlo_*`6c%T5!?3+1U({7dD|zjjAi)yNc}qW@gmTW*RAh~Y_8 z*uiQ_Vk|Q7&{QG0RTdqhv8DB$ye6Z4P8(Oqf3r}FHj^`aCGC+u85pk^(phe>{~hZ0XhMF{%vVXX%V48MeaD@{YebmUKeLUUX;$1IZ6 zk}_L6dF|B3El5~uy`BH~|2%p3(URh8+c`qB@D- zzAg0+Gs-_3w7mpud@0xLqCDNc^(uQX{k%#xrk_#d*1sxDG*8fIouE1N0GZqn@BzDc zHUR@YEG_Ds%u&iKBa#Z4Qdd(mkw!GpbM_wzV&zC0-eg z+K;w<*4BTKp`GNB-fqfl{yMIh#T0)CC(8453Q#-_`O zH|}eBl4#=J^3yd&i8HI^BlM+q0Q+U}_cXGUUClkro`$_Q^|Tim~%({x_)fvSCH zgV{cnQlHP-^wb&1tn6xsN`dOgXNjnGAI)S_6^HHoV5PYbm!tP8TIU>vOoqPZSDu$5 zhgt2o-P$kr(>836(hJ{oySUI-q-D7=bHIO;NKu!C%?1WuSEBGE@ zZ+*AzZ!d%h?q^|ikv@)8di~q!z$yj87tEwWY>NTThVw(PJvDOkL=Ei1)V0wr! ziRtHr1G29Om_NO<`?RcJ?^|Ln#9aD^|8F<+peFQ^ghlyj!f^} zmpfr^Ta@`EGdZYuFv!@6!>-So{S!@$(&>G*Z~AEW&oec1b@dQIa*UxO{gSBJAlXw1 zoJ;Y6cTUkyS$1y4@oXxXa`OUR05q=U&z~PW^x*UD$7>pZEYXCMfLduX3Usc6erM*< z72_sy5-*l@+7M9~3%lUost%Q5ACS%F?EH8PsSSM<>-w<|vA_&u)suhW15r8d`zbYJ zfBI34>%6x^?p@RI2I|uE=pL02MG9hzj6)s#mqd2D5?MvU-7e?x7rlLl4rRbYp_1#vv9u}Ki3Ma|kY zfej-NoG$}lEWju_$&<5{Su7v@&%MJnl92@}<@eb7=BwbZY<4&`!MrsrGDLpU7LYFc zh98$QBrTEkUL`It>fllH=khdiMP^zGG9*}l<^gY30);5MTqI>1)qxALH}Tt-YAKWe zv;5>>P~EmKea&%N4t7tMkZ!ro&-FVTIV`DMrEBsNc=Ebn$w2nqWC1V)@mS8rR46m> zbP4p5>d!x8VaL({T%t`R-yZs2N2>TJE?}~73;Ic0<7NI*OV?CFw{@ycekNecBoHq+ zjvhVQu#Rp~4_IjY*M{#Y<_EZ=$GOe}G;yu?wY}NyE4Fh7(JWo_N(=@CR1KbPbRppRxh+zyuAul} zj>-z1;H?-s0gL2+{`nmrLII-4q0=RE3LdE6ihG$i5N5dC91yhl26|P;!EaRs)=h<) zJm`sSSy!(T@HQ$ldp#oR!q1q`k2RTfW+!lHWTp=cTx-kJ(?QX;%{;3#5jDV&p%xO0 zk)h!jQP4%}VFK!Dsp=Wpd-m;9yLmGi*Pnpi=t<%|+}+&+zRdI%YhdK3D(X7;pYi^^ zd&#tGi{E@&c;rUz~ z+O1_)Jtt2S5y0+#%MN5D{9co%g(yssB0q6TCUCy7C=#vDdxv)F;TtDr;&Pe?< z@cHv(FyB~NSlEsndD}fs>=L105V!FGs& zBom&FisL>v6Z{G63bFn{5T+Cfsty{LCm6a>KTJUL?t80zxsNB{ne-(wY|8oE9WU>Do>3S zjz-n+jsP@YK)GCwgmP}K&7c&&bze_H9XeU;(cT_9`M}^Nv0;R4qn1`PO#-% z(Z<>1YUN9Ns{O)#vG{_50{3ndMy0NLECk7k-^VcfIM`fiLSiiuY-ehA1*PG|V%^-E z0S_n+U5}EW&C$+q#n4xX;9a3UD8b4qxnFGlDK0x|rpnugnsWl=qQ=?Jm2-Zh(=Nq0 z1p5whS7f|(fpzS2pOf$P+FU1p9TdNpmg^?AUl@;(Pv@Dr>v`IF5ZyrMMqS3ISufTF zu{(5h171M2nLW)CYO3tn=iI78a|ieA{++=5s#q;;Iz0#51z8_T|Cm5!lskz|{bia) zUV^aQmtzSJdSV9x3scqSM3A7g3QrBX`%54Kmg*ca&n~^1vj6vdX&x3Lt1b-O+-D^; zYa6Xx2SXZDknHz^*=(@&o!te?h%RUSWWTrq*)PTSB-$IFuh0{l!uvdO5p)7MJ^A8@ zUR1Ce<8Ys3Nt8^Y$xLFdW8gJdRiWtqLfC2gt8!1KZecd+`odNJ{>vsT+~w|$viC%D zg0Ck4PX-&}*wI@RywQyob9=2*eHFJ@D}rL*zJ&=(YNDS_45W~e)qfxTg@XU8ZubUg z6ogZ$T|miO=#q`d8KO4%fRZsaFpvsdnPgN%h>bJRo?-r4Mqa*6V&mKXQgF7&-(hct zp_V_0E7HnI6n6ZYj2)Q*s@TY2`TAmbSH5)?THR?5cF>*x1qG&%FldH3ovlR<<__b{ zRbTg#hU|s;p}9a9nw57&w?5yoD)_t;Zm#Zx)h@g@ScN{19@&H*7)7HT-RbN12-&4S zU6?@4wPCl?fJL+H%2fhGyY1DDOKr(Yl|8``5$Rx?O_VP6adKOM?I7$h5sxx#V?u#m zmcE!C%7sM`kZ6lJw`0yGsU-!YqyKb4(6CI8`Zyd070p4SNVDO|H)pHyG9$bGqV#B{ zLKCCLsm4Khn$!z!uvee@~~aW%?rjJ>sh{k88K93H+1*@QlK*erY($$Gs_M zAMoNf0VmwE*invf=EOHqVyWN82u!=QzVq0d_iYUUmgvJb{aagLyAY|_!N9zPbS9$l ze#SRjmIOBu)+~Y+LG)DHi5)v8g|c%yPJasSiYbc2UD-yynMD&EkwSJ-AXqxzK8qUo>0!K8&MWo{Qor!2;Sv$oE zRTVQGIAMExWm3uiAOQ9}BzAC%?Anj3sUxYJCLdBaF}V z`fC?(@bhekKmO^$1yVpGZd^t|fgo`=>CafTg5Ma6tv`+{?kaLfb6uLYe9bK4n7%mE zA8jIvog*?+qJk5^!%WX(_z&bp640Ju4%(wOx_i)mk%l`qj$!Ib_)M}xg@};+0p?X* z4`{i?Az%>6^5OdKw&#gy^RvTtZDmWR9kT2eXI?kUn#d6VeeBS)?4*bQUZdN`K0Pev zf3^hZfq+0^XC6uT%No2X6z_{wk2h2Mz3me{9p{Np$1W&)ORg@})$wmKGs=nX$CeuM z?<(L#B!9Q7U?xUD!IEK?h6p&Xy*awsmibD;3||)oT+07nY)Y__w8(*(G)A-^pXt!fLzIR#jEIO6Z%c=sC^}VF+ zsw2pySz0BfAlu}+E|@t?_Xt&6P9w@r!+i`Te+jyf8K@aTUlwNiuObpbaOb@Y{h04! zo)DZu`%WY$s%l`Oe0cDnQ*a1w zu_+dK75i1!m?>bxY4Uw~^^~Jq6fMVB$KvU-A6D2e$Pbj_a<9GFK6n12BayU#t5J7n z2Ch$!D|k5-A&L3-p%1R(2_axrkcU`8^eSVdKGM953#I0dbL}LkhO`ZRcviQti^=&c zuW_v+cvQMw4r7hyAceqZon?CP0R#!&T$k`Cm>tAVgZ@8Zd;4fHI^D-GBC#6{3aP#&qU_LZEtp_c?OV?O$$1Jz$=Pm@-)Bl|`P$`$mmU71ck3FqL*PNP zK_Sp}#9*X$<=L1GdgpIZb@rVPW3Q!ovGZ`pQ~)C>jDS&3?c~>y{ML-t*hU= zPtC|U^<5+ah7zmBDMN5U`SH$n-LNxXZ71K{xhN?)>}vD39Lo0#AO}331oN*A`JQcV zIIqbQsJEbE)>H5bRFOdIfKudIuJEIE3u``cWwGcGaYZ-D_ny8)d3wTmjI4rlU~9Rw z@na@@)HXvqPuEJz^%aL&YC(@WkAQ%B`Pz3m0~IGIFO8^+fVc(jU7K!$rwyCP!^+RL z-E{;{bN1sH4B=sC=vF2$eApk7jDmZuvMK6@QGan3$0r0o8@ zZ#_!%n}|I^ll@c>ol-jQ2iC&;M@Z3y4~rL3_MiP}04@>+Amxyktm9D=8xda>Qg0*v zrLUY50rq!;?lfYHF!*}wAMo$vxP`U(b+w_qb;HO6ho3VZIYS&7F7Ux+nhC-MxO5Db z*$s-{vE4brv1@P!eNZ4|reN@i``)h!mhcaBFqwq}-P|0M4zB!{1TS50&+7C+9z*Hd z<&&UrrYI-Kj<_y%NXRXA%pgb(t~2JG{Y2z?=rB0WZuj7suXX3RIXZ5%u7!g(99B~p znMInMAr|J0*Cm(n-r{_^Ggc9k8&U1>^=g^ATFN7}A|Nr=G#SHRaOIa08dP$eV#pgv z1_!%bjXV3z)wMDV%oswsd?i;;ZU`_6is;b~T0b+8zU23DwIz9!XGZ}GfX$WzaoK?F zg3PAFDWix21^?&bDWWV0L?US78^G`ZCW6i+pud{z7vjc^+8(PuRO^YXBwoGCEZ3P(!2T5dM|9q_p25&U8LI5U`C&Z3( zC$r$Kvp6*&g?+_N*7;9*A<~3nD5pGf`5fyoj4A++0Fdue2dn+DRb;@f;+7hEHiT6d&rT+z^TPMa2vLBS(WQ*A4gjDF+35(pmerswG_+ zQ@o>6&C#lol`03!D{>3zGZrZdF?)Sm-4}(5#-U*w+qHF*_O>Q!IewyeQt+_Lxi8so zb3b9{w1`8ptK|OAfQ*dxN&I||wMi&#QegH;{U_lQoPUeHk1KT1foul?^P8DxkQimG zL%`2RG#|`i(DPb!y@(VBHTnYTGj2_ylaAq?2%&FcyvoQFJ;5K;-P=QCMDR}WwMTT3 zo%afRB3aLcR!V*sjmE``_dv8x08y~!^R4Gqmf>}YXgOpT6)kBz?fZRI&)vKRJm~@| zb7r@%r}K@^1Ps}74KJ>)kbpJNemeH!GAk`wVqS?&UY8HPV@&=rqqpj`&lk|p2ul7) zZRk-Lp+(Q#yLSsQ`TF_shd_#G;`-=L+}HZ+uaZ%;l6~BtL>JT%=1>11mvvAhuhpR| z>|2`tOZgU&-D1P`$IM3yxf3+3vZ{HQzG?RdVor`z*px%fZbyJ*Ya+_7WW@KD5B<^ zTqG%v%rtR7J`!Ci;`mkZ^5w^%?CveK*4Ea3QVkyT4K9t8Fa0zA@QXCztcs@>tTb-< z$TvIjP?(nGlx`eqk|E_5y*-ny6#?A&Mda?!U20{1ef94*9p_ou8_ks7_47*Z)IMoT z_h2qnZSb?aVrGuc;nIWE<8k+Pp%XPe5`cmWP!u5VSTM{XnihkElOva=Nd* znVyKlfyy)tzarmD!^Y2_C;iO_TS@od`T0pF_75AyCojJdb<;^|2_0N~HjUF<1nzk4 z7e|m_r%RXg2~47^&@Ktoy}+m_u1fCO=&`Xe_Z!Au3M-|EGrixwdZ4bZJ$90ZVtjvG znSfyU&7Y)&izu@H3PwnHD z;*aXCxHFz>48)RCh{_1cN90)VAd;)U)6VH1i|CGoYPmzglr%dQ(VT;qDaCL};0 z1Eyc}*Fzel%;!8iz(9M5VEc&ye{jVo-pj}fa|Go6R+?V0Z^LchtL4kf&LN!w17%;! zYvx89ic7C9b{yy9%WdS%FkFGtsq3!YxpSu(ggwanM)G2oyuXfyWHnsdL3UQ_+QA2u z;N1>C6;6FFtU=@3s{4c5NKW-?f z^tm1=g=8Yj37v!;)(DuZky|_@2U)xr1h$d_*L)T$$ngmB%=JDMVPRg za2)rag!WI8G>RcfR%B7UEbt=Ad7X>8(|UdE7DTEW59cq|c#6}GE(Q?X?2*>%VlOHFY+SD`(Ez*<|d?q@w ztTWxdEEKEe-fTKA(7kD;JUTrIc}iEil`kDh+}X6Py`cs-F^+`1kHqKJ55*R{N4r?3fqx`g1AQGWNFrPs!q%lh)5|H|t^1RAx&XdQ&pp*r+!pmdKE2 zly4HL{u6=DtL2cUq2<`Mb_L)rp|@za4ocpsThWLKi{`VwuIIeu;o;fS<_W$n`D>gW z-|gFZtbc@)NU^v_)U5nu-u|)*3hlsi(kK~)#|Hf+)GaL1NhDGr=(`ZmB|1bVkdzX( zitA3%8&G=iA5*Kg!UbXTd!{ApOhiKcKlyXC$&>Y^%?mp+IGF!T4x~t;kU1$zO-~UOeo>X!i&*S`}yK0v6N$G7loX^obsmBvAF7#@FT@T<9xTB6>Cp zOVj(>zxTP#v|(5Us|>$cYa@5Qwd!n^PYjIYSBWwj$Txm>ZN@%)&^Gd!5 zKWaGy6t?HZZ93|filfyq2;M(^-?wH-%eCh7IN%1L924gKY1&gMYPG3=_C2LMA>Fzg z9wIen{<-SXadMODqwr)J(c&Jp3&*3J&%^52Wm*z{mg0z$ze`t5A))ukI#Xh)$Ica6 z7keTsKlc>c0q!B~q-4+>IE_j}DiGD@lZ2|PJtRP7l%=KrL0gaFwa3kiNIl0;TMG#k zY8eDI9~}m4Ap#z&^G)bmy_qSo_~`~P&FJpx0tC6auo1fXrO;*=xy((P$3fy06coHv zj5P8lROLoii5-jHx{4EzM~8v7h6&ol66%}chiAD5G5P-CaR>WG+g? zDU;}_{O9X7GNU@!_OvQr+VDxD$qHRfu`ulusNVo9t=28u1m-1;>Btdhlo!|!Nrll# zbuEO6&Hb2R>vl&6TeD_&$WMyy5JL$!!R+Ynp}v{NR&j(NXTv9%*W8Qh;(3wuAlH}6 z_A-_&P!9W>gJC`=n02@#t?L1TVfL$Vsx*FQ}_Gb>llm0)tBiv#50+J#F@F8>bGkUC2N9z*y# zlmPr7w6>2?Sd~D5Ozb|?Fgo?TAXWWH_-_c5D?H+?p03US%4oyErIT2;xc&} zG>P6J0+mE@f?Z5Z54~zc+mbzEkIB@uOk}@7sNf|MAR^F{MtuA{<2pK_$a?ovifF-C z!J2wKscva$(d#mdPB~WdrOKB;*WJB#8Oayg4)Zxos4k$33=NsU$_Wh8zTaah1>jVw z;)^>(T>;j$P*;;^YSW>8_MI2qwnPV-u+5iy43rOnc4iQrcz|1!t9+;(2s~EvQzBlKZs$iqLjntkLahg^|dD8*nW69_hPE|QR*Iq&R27o>MPSd(X z69%9Ghr+hwWED(L8=C=s*4s7scje-UXO+4pLK62%8n0JqLPG@s4=0zt^{?Ml)0<>*t0t4&6E(R2Cp zF4S4&5YB*u*$OQQ+tRjP=wLeF)KF;-fI+s3WO<>%YeR)drdh^Oo^pk3quhxZ0_6b| z+=5!ZF4HOV{JdIhGbqc|i9)C;cxL7WN|-Re!9+&1-$~D!Ev%I}p!= zgoNSlcjNXWUGI83&wLC+g>?d%P%BcWg!y@U?AtMjdo=;TerEoI=#WAwR$WuGFaJ~B zM)CVe2Na9iP;Y5N%8vRl=h34V2-mi-mi~Pl1WXr*5#175Z=K_q^hZ4AGH=5&e!o)p_pirBR(h^ZZa>e8US$38F>#sCb zSqp-dsc%w_O#kA}{%>XN$$IwAf3B8foWB{<{FM?sJKGI0cD?doetuI$)G;20giMST zQmO6Pd!1;$s9=4(g?mTAt$1x^^iRuutrfP%Q25C8zA$u){i5vTEJ95<8n0p;<~)=h z5akRNL7r9?$2S#c^|RyU@H!x~&DiknCZjJkPnh)<)lF~=tf29OdTv0Nv7EN%hH)I-HrP#z$xQ1~RFf0~OV&3!2a#}9Qnjb4a(74~H} zqvZ?yAEMa^m2b6ceQ0M511dSv5S;}lqd?=!+VFbYwrytpWu<*k1QOkwACe8nD~~QW zCBzkTUw6U8xI%(`9`%b9Sf!PBHJ(aIDBQTbp6tp?L%lZ>t=>ExQ^bCRA4>)@lq?(c zR&O4r@;MZwGSM1*wBMH%g@2-SJG7WCiC%=`lh^YDg~GLAhrV0TQ&GhqI4bpgQY&Zh z@bOA+s_^CTV8QOOf0R0nxR)$0IobWaTxHwcj*`Ud%VQ~@9+l?JB!~;o+KyalM_&S5 z{@GuhfDPiOapNnh!GU2g8{8^O+zP1zbFN#4tSab#>g9oj-LHrhA_azprJ}Af3At>} zmx{aku^>zkCy_d?5TS^EGOpsUJPuP`{QXCQMUbEpWd}xacTJ+tzs^|Y{vK+3$U3G; z`qHH0!0i^>5rN_kAgh+ol@Egg0$!jtm<-=rwaQpsT}@P^P{Vv)A0?I+6Z1DzIEgkv z_y|_7@}hA-WZIZ{_k>9-6`k_Lli+U=ap(3OJ9>SZ(7?NnN*gJjs1Xn%p=DG1DjRV) z6Cs2w4|AvnGpYvu0#UOCob#z+d~Ib8SaT3zi&2B%WMh+t_LBxIXj`_CoK{9{2bspN zg0&00kTTTjYZqW7j`VkH$~9xif;|djh5uq)b%0Lw+>)oqa!Sxh%(hDI+@XKnCX{B* zAOs7X@g!j48jD^i3bX?K!b*5+l>nfWJ%p@E%Cct(Kz}ik$#@NpF$*zWl&oY^jBFUMX*L-`aFX zc4C*lx(u3ESY_zkF5KAZfhZG26hH{xUjN}Rk*zf&ndL)Mv0W+lg;`VVeOlh_WXr{H z|6w&~0u(UG;F1PFT=%9Q<rrU3a{3sBu_eN(O9=ZfMj+sEyU;aOJ z(SePW!{oVaFx6A2LR4Kby`#4JB#ke@RXAn5ldOQsFj|QdDv?v`+5J`m1WN9RC`VRK zu4ch`!^(O7ygd7v=5Eui_fkBDH{&|FMQ8iJ5%Ffv5vm%<9ef#0kg5_?n%#y&LO?f~ zps%FIw3m{y9d0xkPQaC&6G-0?A)!uP%H6wd+{zmp8+C8!enraLj1FhT+#&afg{!-@ z2d7xJ-dFs7mtdYv#Z|=Rv)jO7t_>}xPrLF)Wb4VNbwr_dcHqtRYG}8x-?k8KFw09l ze}4}_&B69eM8tFv!OeoBHd=Dd0yHhO$SL&Neax+UeturGn}{|DRI)G%ryd89ibt*= zodq?UTU>HBiY?^OtLi`OWzxSDKX3e=mqs--)=9u_@4;f#>nh00JGhm9wXA&m_PYfw z0|O{W1F@{5cPGlaP`KQ>)PcXS!$XMb=hi=$3xSR}i-5GowHuav#MZ!=ycoVUlQ2yn zGB)9E4`ls!A5R6dYNl?duDIt~LHCbHL7TU_Pkekhnj*G-9^3)i|GYH55$KJ|Vt7r` zm1xJ%CJ_l@AW=v-pQ-W}m!BkgvRUT;YF%L4hOEj#s~<=H_75T^LS@_K-LKd6{)*%H z>+fWW+X3FEflp=UfvALhI)^B43$d_VgoNLMwAHU4TifxAOiTP*_AJs*EQN6<2qMKe zHu>W?r3kY`Nn42)eq{5PmMPNbz_j+v z9|YHJ<6gNX(IPs_c*N=FXYp3PmS?=NGHfrr)q^$5uhq|-U3P_xt(#CxKnHKwug2vZE;A5fpDR|~p2rRays2nGv8fa@4L0ua;G@HXjs|K_v5FC3^-!3!)uu)D777pfb zt$qT_kt65vQGjSH5>aQxC6?pIIr;h3pjzIiKz{acuWijqA{fA`1y4Ti!v_byp##)9 zLKeXJ@CD);32cIlOcGaZ{R+Ma&>A9#aVf}Khh^D^0g)cyV1hu7Hm&%|pJ6vO-$ zygXUSovqvQJLDjK6$S!h(~m5z%#9KO0AN-SU#D`h9S$Kbqyl=0G#vL|fT>%FKkBYX~Yc#poTy_u-7W2Wqcl1hb)HTrFKSkrv+8+*3I{PNWAa+{R=ir zzmSHK_hxw$NU`zYDs;a{EO6gHE1$$u zMmnG%0kSWR-0A)2S#W8$9=H4@+TNXfAhFM=nzaVHKY$U5kOD_OzlGff8fK{~Nl%Cz z0$f$=q#e3{jyZWdH75dqu3c^-)B~h*|MP(u2N>tYV&$SRg* z`}Y0!r(loJnf#$ADOa{jX0VWv-yP-6gK3^X$dL2?QL=vCy|aVQE?3tgq! zzg}NHsDA>A5UAn=R$V{sg@MWX*S~!hUybsr4vL4tu6us^0-=`+>Y7@ay9@4V8ZHb~ z?j|^iJo|BFB+1m1g14C0>%9h#9XmDwvVsQKVJKRN5e@(;Hee<-RA~888?i+C^ zdCaZP53`zu){*!V$t&Nr3Nl3P4a3C)K(((9>9URk>LZ>ZqH}`SLzqgSD6MH4r{tr> z^$_S)`Rpf~p6zI$bB;b!e{v!vWT$nxwcg{f7hY_8N2wRMH;3x%e8c=V3B3J^#CklD z*rVbQ4Ux6a--XcEoU8odZqWm;ZzC@7w|UT>T_2U$RKm3rq{blddm*5SFfa@}rzF5+ z=16#$YPLnGsQ2aABGX_F(FK|`nx}hfH@7#>SEM2D2((!G;}Z0rr%yx9>*|;4hWGEe zqdZqlE6wso+}04i(xG}pYf~frYrUcn1cA>X@)(sR>g6I?9;88jgqn%8oSdD=uN4|G z!afkXzMozo{gchlwW;GVZ$A+y8^mGdv6G;y-hbl%_C^1SQxB%r)+PhK)mgVBhA_dZ zbiYy(ofn&_wgg0l!Aal!&fsk1+m(C*T49BW8zc1PfEeDvH~P9$kiGXW_iV5DQDS4I zbfuKf`G*JRsa6UP9W9}(}E5 z8t*g?gPr?_T+I_wkOO**0>?q4()SreHC!@_PpAO15AwQ?!o#E-!% z9{JP<&i7GP)~bvMCHwi(>;p?*<|Oi?V<|VHPT!DvqDeFrfN2~tct;vu{u}N$&A1@h zQ(CW$^xIA@lRdFf6Pq93{!q6{KD7Ee+g4v`|C~ghcz*KAau$wUv0B#b{8`xvx{a#t zQ8i6X%_ldAN+6*e*F;QDMcyp*`qHIK^q|*$mlkIe!moaZCYWg)B9X!<9q%OgS)+C= z(bRWiXK;k*5lai?M}u^F5ZYpL;hR6oH#uJodZ8_X>(!5iNfQPk+kbnaqN1z_b#yMT zgZ*HP{<~niwN)hsjrGzICQ1?083-v@1lU4d;HCC}#vWSp3iGW#Y@fqw;C5MAVMLN2 zmV}=47>uriFk0L3?diwCVUVwX#A(!5C5CHQxU~z@i z^pY3K=lTl(De%LV*Db;{oFS-$f{_X#j!5EpT<6C=+pc}7wWnBWT>2>0ll~g$jTHFH zegK7=mob(>cjpu81PRo%n7P)oAg=Zf7{W-iF>0|=;MCL#KwE6|h^Y6U!&%Y%v*Ol~ zI;|?}(fR%PwioIemxAuk`M5s!`aV)9Syx?d#4N3qAWIzw{h@a-dVU0pZa8m#Xlre% zu{P}kGn|Pmh$A@U zRU+JmD9>}z2;1uZQ7Cuin3|ARBiOUnDQYAKdKuV}mZq!h4l(>ZcLA<^@0p0=jEFL1 z-Qdw^+opw(qN=@{LdxcukfO71-{n5=5G&E-=f4!}bnusvzU`NKZMtvRc`3|^1+@No z^&)xyHEx@Nb^vrkhg7!k(s}Pky11X0iHPg<%kP_lI#D5QMp0^14#lup<}U&GKrIF@ z^ab!`9;4o=&g3`Nq`ROu?-g^>{7V-R8VQqN9QNu8=P2}*;ecA57C4!h-@@u68f3lA zo!==*wi-i!d*IZm2kQ#H01Z67d=8Io`o+w)Mw?KT7}ixMW4-Mw5n%WjV6%+V5a(N0 zvPHO?L5?_%W-@V*e;nCc#yP4JOH96wKz-g`9w_&%>55yeNtUTWhKqyHn^{{HPmO8M z>Kzzn9$Q|a;jW}D!$4$^vkpH|kv$Q}C>1;>7Z}?W7ZEc@UA-!0&_CDt*m6L0J51&U zp5^8heOtTr;a}+vR^%#Z`tHG{bw*RO0)YJBc|={Kqejxgfg|UU7iK4W_AuLuhDqHv z+=rDM#<{m{+}QfhBs#$2VKnmx&Yjz4lkqE0&OtgdS5Gr)y`l9H>p>NHi|=Z2l6m0i<+}NObEtOEOTX~4S_rtzZdmJHdil|LP;!IOD7F#F=I z(N2>E>7})8$4?(s9TGI^u6tzsWlg#}#Y_WE%+yVd&+-XdaY#|Gxm;sK+p*K2ZP-Om za6iy}Dtvxn*Lh=en24{m`lEJ!KU%b8HwtoC1F;;BM4KMTc0~?y;^N|R$%b8CuKRxb@#k7i za$97^^sE2sRD^S01hWn^8G)HW_rd=>LnLGNEaFo5I1{$Qb*?JNRyz6TpwfBRyM3na z(Og!7lk%0nW5t&F2)}2A24q)X?E}5giM^ZtBt`fFT5Z_&KuQH=h!TRg-p64x*pDMj zZ1lkjTz?a$g+rxGs_Fo+jr~E|0K?TAG<|<(E1ec|h=5)vBZ7J#7_ylrU ztM0%IJ*&LUu_n!rzR`PQ`~&%cv}CA?T?USNj?aw{y5=KiXsQb034TQ&BF7=|(#hLf zfPm~&2RUg6xSduP$PxJ!$#7_cugoJP!|hOJLkM*g{+dItW-Ur_DEl!7X%3HO5dSs* zT(N){s2pohJ1${;s9+)?pefLWAR>_|e0Uk>srVyBY{i^Od7y|td{jBb)=YrJ4y6n@KDN3b zHB>?dbSCGI9(pViX%Nn-)7%q3%@Y)*e=-`Bo%R`r5F_O|+4m9M4hQ)cU^-Q43J0m* z|Mu+%*cIOat-m^m`pPU%PYn8{+?_Sg*8}%#;}cvbVgrk-hEZ0R(F_8H={5rH0iQNL z(pv@kEy6sONmRydesl|ZG_+DzIP2=c#_cO@WH-8I^0eIv@9G9Yuq9jM@J?Z_z-6A3 z&o7NoRm$D*Br{3?*`IszI-_)Z$LT3nZCk(}#dE{<>&-HHO$=(F=UYkBcWZ@>XBoVN zN8S;bMfz1Bl40-XWO$(1o2WwgEnK{{anq)IECd9rUWoHg3z0ggWEW`)^kF zqJbx=6TtTlkZ9$QY!|)xityxsF2Ee(r+JGOIc?`j;XJ$qr1t|r@XC-4pWtG2;1=W$ z9KVi7S}8w0swzA6t=X#O;-iPXcfC`>{A3LZCaZS4W~#(!fUG@=Fm>1KfLR;Vs_RCuET1XdsQhr(ss_ znO19m$!hCP^`K8$rlzK`I|>W?Zgf_sY>~6h+Lh^8GI~Xe-TIQ*J)9KQ)sxb^&>i(aDl`%O78>Yqy;n)9h>BhBoUL8aFJWLKc(~c^qa`bb_laYM)!*+3)&MHM^sgX?Y_sK z^vRlUHIi-SHuW#<+EnVD+d`o#^^2q_PH0jz_Uzen=hMSeGiiioG0-<|Qv}cxf!w~D z(L8f@B*ewV^9*sr-b4TJ7?Wx#GPFLK6l*l=IQjv>DhWTHF>uM1bB9?pkcIl-OShfU zqt;oa4`Y9@KBTHzMfVEgqkFy-!-+ZPNw^$G+w2|C_9>QwpMHe6;3By}w`2K)x9DcT z)w(K*ieRWP&--~r#Pd}%DSTkqyxF`hBrd186koCuQxC-U`oZe;x${6P88~0PdF2h~ zhM;NHNBMe}-TZ4p~x_$2*38;ooZb22%d@)H#RmXMe;f%oVy<121{>FYG0g zc6u^enDjL+4HHx()}NF_!X`ga#d8!CesK#%B_KY2-@TiE>$YuA|L`FlCnhp8|3|9R zhjcxHA_@vZ%-Okq17KAdac~jC@WB_{mX#wYcv>yPh(9OaWUHJ%+8^}lN!dgd-O}sH zZ?J;2z^Z%J2nR&Kktlu?!_UbZMDtn~!9M+7!WoRtVpp7;?RK6eGsTbpE%|{uckhHY zolLh;8IxH*aXEeaxBS(A9D|M^j1pELqcH&;lrX1*f?uro(R*+!hMMFddfoSKU8r~8 zK^@VS^SntZe(KIenAL;8%&&Jnx{02G3Lrh~0gX{@`*yPzVN_p#Yh`t9sI2Sz7w%1RbgA^91H&h0Hef;EkA|Rso+1;r zN{M+n&zXt9!2g)f1Bcvx__q1q%w4!(NOQW05Wa`g{ll)yfcN3HP!)IsM}mzqVJAMM z-6Zk{C5nz5uO@SqFqy52_^hd*nFc0T)2x2bIG@agcSf~nBYUGvxIE$VBkXCOuS-@VZiHSd$Y;evq220!p?7Rhb%aV2qC9%j-cIz9^>5-ihem~%Fr@CLOqi3 z%3aNtf+>#?4<|EWD<+)FMXONa&-2=@h#=Hz3%#Vy_$zk`->w+Q3>>0rbot09$7++V zy#+HV`8T+#l>62dobaRR1H0hfms~el2J+@j?zjy?2V^Tf{gdqGPrd%HaPjX0U{QJx zO#9Vq`Hp?SD`LUTU-W~d*3_vyd8o&=>MM~-EAOeR7@H}I0GI5<{p`Kq;rni+3k2k^3J8TKtU78_Zx zaG_&$ZgHr$BWHZL{*XZQS_aRflul4Z+|KVKE! z82bf{{Bd|F*XCt?y1Bgq`0yu}bwW3}a17wjyJqWY4L1~kB$nRypY0EJ0d zk&>^YVk^Tlo~ISMj}jTi!?S?FSop=M<2eNzub6TEIinQ*dBQ?MCEdM! zSpOTzGyp8xUpHsT{U7}I(!kZaf(iUWk1$IK%bK%4{q$3iB}#aDn8HnDM-mb%H*M%WWp7@G!c$|4v1vewIH6f@K= zR2g{kqAfPAh4e+bG#P*u9bu)_jSzigcB}2Avxbe+ivxpjC|F{~`p)UN(X)H5%oMVt zH++0{87c>fJUluc2DI}rMh%$Lc;5F+bnqEqYMN_N3oLYf%X}`Lf$uF82n0^)pN1T> z?0b%(2PYSp+!aS6=8ijtM`u76Y)0sV`xJB4Gu3dp1K3lR=QFo=`wKf3-|w50MeOj~ zhtCOpwdS5W5DZ%eZ({AO{{$8w0`ZMqBBrB)V63WJp+vk;ieNo$ev70`bCmcVuM(w_mwIEvrg1H5ORn0&Tipn5|TroY-CSSY@ z814rQWZdZ|bY=kP4C{W|G6d!0%NiJUdyC&O{J*29!)Qy}%8!MVCUuQ38{|CzfJS{q zTP0%U8JMUgSfW_>uBfQ^w9!X2F0mn@j=~f~CJ{w7DK-&{vAQmk_rM>2EXPcde;>vc zR-myKg@(`+E(gVRt7)}eUCNHq3s)zbFMzxentme!IO@Wehu6JO%<$5lI{E_5kiLmA zbY8r7o4zY(qqLcWJ|A$RzyvW^Z$oO$ zE(yqN3D(;M`+GAt^jx_>tS!NJ2A1X_W-eX6lK#=g6extn!Me5eyTCu85Q;+yFKEbD zlEyg`zp@#qf*6`V-I__<5+MOVtG^sBF$;WNUyr?sWG{>6z5>c9^rzS=!H3FD^f z?sU%8_0joN=<@}zFImkT_6t|He(d?Jzy1;-KLkgMC459BPGS7~0F;S3?n0fk;@e4@ z9#QGIO#;v4{P6@tKsUo_0LE6Ht<>`ubl z6#*2|YOCb)d<|Ti?0Xwzt7=#1J%6ka2nEM-!rf0NRNGFTB2wlh$g6$w!k=7ragLUO z4O8nQ2NRZ%2P!H=h18KFN5m;HZP>PLTUE-g`^7W>;RTPMeR&7UP$Nq8JlucX3rJmK z%WxTU^6lHV%V7j0WM8sn^;nowvx_ZR$REIDa^KtERX;oL%>*o$>%UcF-lQw$2RU4* zff*<|4Pidu?1hWO_cyk56O}PNLuGj0y?eT-gEZh{LL?wD5_P%P6r9ycNv0QFEi9eJlP3MHMwi z4nhQ#8m))GY27L(r%##^AD`nuca|<*toHoF7OAn&>0CUFN|268hItFgD+D72Ku_q; zp-Df6(%?*s*Qf8m1XM7vH0(stAmuvz94$^X1UPUiaYV7%pt9)I#y9`gL`_+wNB|ef zDU4AjyEU0rh(oh+R(oza{hk_aFKH$ol_({0H|u*$w?t zgU7ff4TB)*XrL}c8RU?&^u!nH-9Tfchn{(&c=&KR#w6lEuslY_BT5_~6#hk}efQwM z-@@ZBDR=-(z5Wm3#8fxhBYLI7@_RR|U+<4$G^q5k(ZuvIUSKqByV5ZWxQ8&^j%_Xl z4z=*NfoYRQ>M#GktBDm3E-)6DD>kD!lqvam{*WIk*C-%~x}YFwbr3+r#4|bbk}yLE zSpZogpqOoKu~0gAciS-%40x}R*Qb4F|81@QZAS+__qRpgCqFOff_}odT^vN8Evb`W zd#Odh!l5{*wN0S}i@JYmtKIHZ^M3wWq^cB6J=UNo0n7US)%O%cs4U{TIDKm7?_A7N z0IRNjG$0Z{Mtkl`Ykz)-4W9VN_lI@O`ma;?b?3}S;HX$$S%P-PVaC76AcDcZw5C8R z&UPG89(qPN7t~wG8x<}o-OTL^*S2{60xO2AexPHvDz*w*#T8 z!}f))_H%V`=_u#oxOWz%sOWoJz!agF=@)wP@GKRfx-D0CpibFg67_kn!>oh=!ZPR@GfNmoueK_)Y zq(iJ2dHAk0MnvA|2JG#?*zu%g|a zhhDbZmG3s$QCorVVlJy3K`0So)gxH41^}uI7@9re&k{+@EZI4rYlqGxt#XRcP|O{{ zJC8Qjk$D-OBzHEZ_@QsXs6Gu}(?%;rKQzKnmxx={rR-Z%x)SLw65N)ZEOa-G!ywbk z0UYrw;<+s0cdx(#dzuJP^h00$vBkpeAj)xv>;)1*oj^s7fukbTGv}}VgQ1yS5h|W$ zwhR?jy%msU&uFD5jzh9eW3z#@`qmzGYj=HGm74{^ApvkG508I-V~YXMn$&z`b1KpP-oR6NI z1UL-LX&?D~b9?O3Q>RYd0FWe&y&bHF-W}n>YCM$_FzfyCgq?900%ysx^{ioK|8=e zEhS;^QrB@6^{K3c_;b+Sw2CtP1SD~sGBJW)w!XU-FdXTJtdeA*u^4@fgh{|mn_gW8zWcbXcDQw>>#9;7f9v}L%6Tqa&!HcGX$41-2k6{FLk$l3V zt=^3@7n$q))`u#+@{ifBbLdvwp3z1(`5C>`vaP#jg$duQK4K_F6@wL0Xu|h{2jG_0 zeq6uq_dAeRO@yvryVd~$Bd&IotR>;tbUrO3u0UVY_`n^%f&2}=`EYBP6d(h`Zv^*a z8Z;3iD0~nF)XZvlII`1kZ|1YJ7JWYq>VJeb$RCXv;huSnoHY6MB7_PZ02_SaTk-&% z@0J?@0gm9Nw4>vzL_uHdNE{X~?Hk|NpAI3mS*`$@Ex=d& zSQ)PZP_gRR2|Y;kwrz!`oB-3Qp*T-KDH{)lV4T?;OaR1lYNX)<0g39egGQE-X%F*P zJvxMv=!ispB#N!4XL4@RxhM{HmT@k^3nn`_vR6X9T?=p;eWJMOyFHM^mH+w6?_^9j zF!EjF-lz~7CgraGm>noUQ56#Ac1|SP^NTCmkct!=LrREXVk?0^!iypnQejI7)4|=v zlW{JzZY<;%X+1^Sh(=R1vfyyW$UX#C1(;;xkt#tcSWGuMWpW~Ov~?vIyF-3tn23Qr z=DR$hZ7>0xw{#l@ot&2(0EB>~zCrP(w|6Ce;DALMkXm#)`}`_^1|e6bI(T6O)NT|k z*42_S1Ecx?4L^7eJqPvPQX>S64LnK(XrHr;T7dJ933kFpM78th&-)BxK0^%+p*;qE zJ8>1O0J7pOMkBzSLwX>?Mf^&PTER_8#V!l^{qDB`a^1xXqd7EWF3#yCcny%`amPS0;U;DN~G);e% zK2iz4A?4ole|d>3u;vWFc?8C%2414a*G1`Yt|6U?&`Nayk^B`Gf0&i7uLZB5xKyWJ ztCGk&cdd~bV5Y17Sj(x-=DPL+Sq!KWH^LYmhw#6s1-AkU?#D;-1Q%wD*ux{&F5|J7 z12+}}>XMi(tUJE(u;{ql>>D?4iUyLK92%e{qWh;mm%QLdeewdXwC_WnL_=hHGS&Xc zYL_n$b!#yQl!(}nCkn%cr& zGS{VK9bE7h%pR$zhQbBjUyf6(`QJvVand@y?;6tsb%-f)6RaD3X}=nNjTr)e2U1#f zD;jM!Tns*Y+aE!YJPr&UvbBe>S`ceQ`q0B<>h_AP!9^Gn1sPa0v{fS-Vg&f9=~@-T z!*pb6&o4{ZH2DVZCa4qh`oiiVWECLrfygI}iRPDWLS=(L843?1Bm9%~KAzo4RrgpZ z32^@3!!Y8O|1)_+{1VGoJ@}C^M3d7>JpL#6priqyIX&}q`qWSow+e2GgAFF-RQmhW zz=0?xA?^y)j{NP_CgI|@3IY3U5W5LI)*fn@&UffF}IlncU_K7#CFNqh=_#b z=|=ixgzu|T`ZjRmLJyT9;|LyX*;Y&n2Qho8wmtRw@+wO>NNwl1OVD(FVD;L0q8GlN z3e?#MlUjB3W^A%SK$)c*FXr`GJmN1*Qj3+!M39FF%`K4Jl8J`V2wBAPOm^LN|A^LM2)BsK#%k zU>NwZ6q3myOkzJp9AqYE06wST^uWuRF{m_hCZ3>59Q}rv)fL~3iI}K>nag)E?vcv0 zd`0^ccHpM}k;GCW!7c4ub}QqXT zHH-4_bu^#zt|JRfJ4uGlVPj7LR~;w#g(wgt%`!wKR|UP>@mg2&B+{ z6v7ABC6sD@kn?kYba6d_`z!x&wH&!LU;^GM_0M*%#Nr>k8C`h+of$m@{hibi7iX&0`p$Jf_$>uX{}cxZkLe;vJ0(QZd! zPEML0w`|TzDmt5ZIYr)7b{p0;%TX|ZGBeaP&<2bDetnIG`Y~)k8fbK z{6K6sZpNu)r7%#6yEJQVfGMtj-*xIW`q@e5CxC+=f?v=(v~I=8G?RsQqWioV!^a%? z5&tBXTub2`GBGOPei9b5kww~T=h@va5g>1L+Q3YxYU(6T6|J$QM2Nuq6eFl((2WdC zOpd1_oD~pEK&0%k$4B>)6DY*x9NmRFVgp4D+5Y?uqq&DseZs29C>A!$wJ0<9WsWDB zSCtr{anfFvlOv{@g`5sSO=-epPC`%}#C@$-L(^fE6~wAGS=ARgvs_?URWV;bMA+1E zgZ23lr;o+AI3{gav~gJ~V>>-frCAPR%A7*XYgfd!5;${2O%Mt z(Pe>gJ<@0|8ygS5$1nub5Web=t!53^eYkTx0gxDX-=kb7ar}icUT(}uyyraNFsQ}60^EX+Vy+L`QZhR@@AF+&-2>f{= zMhpv{6}am=@uBQCElsPBCGRiL34(VF(ba1L{kj7ypdvJRNpKc`B1_e;0vh?>La)*Y z3t1x#q3AHDiAv-ovJoR&^Qd~XTF+5%2Y#?&UGB=M0@cXU^?-VZmJ%}N7gF%W<1WIQ z;Iy#x7w0p}1mD)e6!cL!w!p6DQg@G}$!B#=t~duE88>!vrS9|0cJ%UjVtjp7)$($b9* z$d+hS6@f!&;uyneDSGgWxEXpY`{NczFF$|x<9ATm2C=AlLUGHlh=uQnL5H5SVY}}j znqOw!n*pdaL~q^7*#X%Z^5Nk^fo&L*s`3*py$kC-9_h?9>eZkE3GO5rtBFmY15JTg zw`oBh?vAmGRs}Ddrw#x)wyptTDAa;j7&``Bd)_Z5DQCK?14vp!Tqa3Up5HFiP&8so?$c@0q{^F$l8OF?{g)8luvTGH}KvYtAm+v=M-2#H%2l#`v;YTujqt zv~9u3uJuaS>{hah1zg>K*aN~eYH^B((Dj;Zwf7!bsejX$V`4Pdy)JXxShImz0A{1&cZa1ej)eFSI^n|A77o5 z)GkVZFavM$(^=uq79ak3{lfiyoPo6vEfv54W|eJny~GNwUJQqnp7$@6lwhi)YdjxO zSJnUa*Xgc$rwdDtO^@`IMqgjD?~C}JKi}>D<>4XgWxxJ$^l0SIKPJ~+*4wK&cwEi% zCWG(xqf>i-jhwgT&|UqgL%(D&-f6UrUb(&_p!>1^#9pE6ud#VX@v z!GJ4)UGOW8;ffB0Pfu)SrA|yv7F(J~*JD;{F2Yx-vNSumllz3p+-fvY7}(~g{stMw z;c1bBgk6j+aBez6o!A5-HbM%cD~o3sjkTM<#FQ?d9MtY*Sq`$HkBczimK$OeilLr-QB{4Vw8+n3D`>Pb zferm_A^GdX>=Q%cFRCM(g-ob3R#jCauCbK2;(Sp%ZL1si3`p-9J81+9g_}zX2A11i znepk)rhJ$Q_{qh;EWvZSF zCXvct{m$`cXqrQ!dZs%dVAgL}d4thD86CLu!>l=HgM=+Nmx1Sue=cS@R|B>@DtuH& zmM1MtBVTa0SkS?|j?ryN2x^b<8jM{~kRpiZtA%=acu3(h;~l|W54Dw#5B&6*!2JD{ zj6Qw(v=cS0SUp0XTc1nXl?WB1_L|?KsI?DXjXH^RUDq8jFp&>m8)Z+_%nJu-!j{yu zq~>!6P3gyAFqHaC-imOuiZ`4Hyk_q>$FCDVFd&9?zcs2e;rVVuO7f2Iow42UeKz~^ z4`c0o5#g8-soNtzDL%*s>Z6L$Wpt-2Q7k0zGW8yZUiAw`u` zC}NFCx8iP0r*r~hEfi@ik5b}uRBoGVX0nv&GW0akJb^c|+iN5dpw7KoumfeN!&~2_{4-W-^RzcgPzt4LyJN4!hVr3nSupd z_Q%d;2q^$HYL$JV1x0Hv-uR4TEQBya-5sjYt5LsK>nSPb=Hw9X>-V|2IRfp1k#=q+ zuR^)wzQ)H&imPE?R#sLvhWaQ$cf~N0UnL^VMn!#neTgCy^72J_Z2X73dn;~=Q5(PAZ4F+NU zhOx69(nXQxdB>xbc^4fp0$l5g^Oqme-h!^2jbJHYU|_K5;BXw+deYdxqtyiXI8TB9 zox>#7;Q)arw{QQ7p(r&PI06uYMK8v|y<+V(0xQj|)py6kJgXk3O(=(mhYUbkLTgkV zqq>*Fqbb^G*X=|CS-Yzf4iemnWh{gdfTBN)vwkGd30O2$=+$>7@K8HxR|ZZAzasrA zAiVsFhz6Rt`#YWc&nl`zHm2jVz1w09ZhNV1vM-YMK-%6%8l$O@uaFOU2O{HY*kaS? zA*2-Ijz>Vz^iu5=VU|@!OfY1E_gnN`+Bbq&XuNV~2D4ln^CiZ~%fn^lLHN{)vG%x! z;egC8%;S_wb&-GE_>DLt7`0goiA$hC00<$TJ+W@xx-lRJ#VXIdzJ2><3O`P={SeYm ze~eS-jp+sSzKVe(h@%>aKLbWFwh#ARB~I5J5v(;6)WL@X&XG-^`{-*P`Z@7Fs#jiMOr+Jhf;N9D+T|hMBtaGZ4KONTiDD zu!;dh-=w}$;#`bvP01)73Ea2$$VFWuS%HCp)JKusQ83;9Y8>BQ>X%=DUbFHRxK%p<-s{eJ=c z7x47-q>+C_DdF_5m&s-mUs{((SbwJ>s?4J}f_1jKW^)D8q@@268x__~A@5`{_Xx*r z49nW%^i$wsAy(Xmrwr`Q3`=*oXEwxfrCfTa+KaR)xt|jZvaV6aY}&dDlYFXkky+1g zNJZQW5YT?~ef)~HprH&!F}ocG<;KrSQ1hKbIYPMrZ2agL7WO4@=Kun&=(+^F&PY5-)(h_!v$e1fkK$dRwtA>Z7;!M80uCa4q`qr+@(4 zE*HE(A3KCAsN@G5g;>II5`K975CdWyNEGZjsuF+fRl;q;w3{|Q((ya%6=e7x)sh<~ zEsx?S$K*<28?r0OK?eC?ZkejJ0w-7YN)EEyga?&oaorwqlYxZ~*DLRB$l!cZ_UFw* zQQQmOPT&FtB5!;>zSx)*sHC`T=f=bSM{oSLG34IeIXwwrk=-%;;>YjlIqjBEw?*`L z`UM+G((Kcm-^KN6|Cg9)p2@+UgYp?B?^Mz0%y?LQzC=!{wfIHk6*o6xuSXR?MZ3|1 ziiEiwql(TPTU%LPn|!+9(*K;GtjgI3LJ*S8N5EWDV&qzU?PZ%tGyN;vIbv3pdf^4@>}uT(O_&lnfMsm zTl($9>H87CM3LC&{dWq1D_{6a%)!0;<qI7tX=IL=hZZ=;D}fD#GGul%e1p*>Ql5Z{sQ1yY0=5*{sY!jpLp++wNVNli3Gv?Qtm713QGy&Cq%?;?ha zZ2G)=WtJLm_vI0eoB_wegCi5+JkKaf@d-%B7JVGDm@ngTu{SqFUMVi94S zot=Z*GQlD*WWmzQ6udLlNaQWEWXW0Q#=*`i;ZmQO3$cW|IwJ<=`m{p{T|a5D)G(b8 zwT2dV88-kye_aOx-B~ZwO_Imwr(xZ8jXDH?OFai^!;fH}FjkDQ*R?za0VGvvp!II| z*h2i^{6(CXU|Wp7YX|`UAwTS)97K3@-MzjYy@e(u3>)=sSz`)ywa?GsWgr{c3qG4x zVL_X-8)}RFINK_d=Q{xBQ5l8pPV*o%{u*^3pCG-!_M zOtb;~$WZeXTv@DlpBJ-m@d3O)rKhI{De`bNsZ9ferE)5e1?KxPkS>sE8bW`NSs53X zl_nnNI}8a;Vx55s!!V(G=b5)Zi_w20U@~@gCmHLYnaeMq&H^nR*b#)fJSrWsBp`vh zPdDH>Akzj&Ap~`(Q=sSSU$lmE?W~jm%$5PJ*$7j5cNlZatSZYQYd9htNGb(M&=52q zs=^tAetDm?QQwlkM77ECYlNU+zfkdhwIA|hQLUT7XVq9rNT}!9$&TM7t|OxAfTi5U zMj1d(&+uk`vR>h4(D~uPvtW zuBxZe>=1(&ybykgP;18mwSf}iZC!Y>{C+?1ijUC{m?7m-!Y1vZI}kam-M?*@mzT#| z*8-O>IwGhY{|CaWN)>sgMAtF^*av>n!l(+;%DHI#k*A2VshV?--u8l$fx5=v)!0#l z0r@uOMSPMvFN!E@Pek%1f@d%ZloGYmfmDr5ktoyp#TIw;Vg6A^>hgek&E7|5R5U?U z|2NWR^IwS0X*59PHw7CJ0SP5hG4NG(wN+JBEi3QQ_vC77{kz_Ep4s?1{&pn!WB4)& z0wxL7gLQVL!B_TzGgz51Sgynry|EuhnZ00YH7+x|MQp_vobGctF!_y*@;VN{tuMQh zwr0-{t(;o#mA46W0}$ec277?Ml3jiz7uDGoYipP|qjK1ZCg1n%Vu%t!?3#S51DFO= zocQzwKXY*@lCGR_{i3za?K&rDtnocQ#E!Xw1rr>nw}7 zF1g_XMmgoCz8S`_QXe5x1SD=xa14hu16+?XV;V#rQ84VG4-8Tw#;|^rfFXEM9?DHw zJ{Xf-oXyb|)&e>$tn)#M2R#^lY`BF-khoLX6A2Ufw0K}QX$@HFx1b$PkdgP8_$GrZ z&(#iQcL(E{=9Bg4pJEDOMhdx4fpUVI2t8)5{KL51x*0CVaN$MRvBUc@Z~|4CzOf5p z2F(;JIqZ&rBLYfQF*g1Txr2smyYKieIXpbPWjXe9s>XC2Qc+3f_6wXz2utia`Nt{= zgWOoWn5R4fh*e*IbU@Wd&V4MX7F|FYR44;7*Fe;2m81DkLsTsu1omK$ax{J6e6I6< zC{st^v)vggTSTePe@t)6hG4YxD2a9BjyLL$e^i#P~ zMn8WJyDUAn4rUdw;FJ^#)fxE({n%+m6O*q_xKPkJ+KFXgfE$DLX`0vhIsAf34Rv;} zB;~@$gsx6&lWLfS6_zK^!jF8LP%4q3(~Cm*BU|5j3l|4i6#6 z&O(`%>NXk=RL#FRDD<>^=Lni))2aK8!7OrZDQz*qGpzu5NrT-`@ofi)3Orhkv>%v& zd!J06#E_1m0zHJ7eix7nwk!v=tKS6~Pr6Q2>e2Ku(8z=5r5=nrIK%^1UY@MyYHO&Q zzyOv**|bp+EV%}`nK5mR7!~0p%RopFCQ^dde!+L~CXa9|P?E=VvWABpUGK-HUg z2GC^N2p$f&@=@Gu8iZ43@nJI;t)nL~<_L)4D0{H`PcQU+a$e#-1tV5!gYd&U(U_*% zxBDSSMgymN0%QOWti)y|7DZI~ZB(KfQjck?fVz-0Gq51L;wJHz6AiLsK__h;00#F& z>*X05WS_?))Ybm3997><>Uj{Izu-!4Q;+oa)I&5k5-q%A9#q-qExHUZ(NDpfvIo7 zSYx^>{6QHh8kM1)6tm+j6sJO+8v~K$ItH!XA%y{qiVNvf8?ke z5W}K2)Dd3z&AXqIS9X9QErL!yLv|2RVPJM-SfbElijaIF0#yFO(mOcsW$0uL5sOsv z%29eL@uBNPH<1f8!W*_Fyf7_ouR%ejl;(?89fEA6G`w-_}a&EAno z58I{&LhoKL+z=^?2uy_qRIS5WZ8stOzo=x&@;OJD-}~maHWQi>`T#^-DbJxESK150>-w*_g3H^G zXDzYud&RYn-^4nE@Z|%>kZa84Mj-TD0p^Q0mkH8UtPx2}pmf0+xw@$3x}$DVF0!C+ zOgO*f=+WER1H<6ZPG(N)QC+bD!Ey&m(p8xfR?L=V&C^r8(_Vl6`BR|J>$@Y?HC2Vx z2W#wRG(%P@)y$YvjXdo|8zQFC6Qh-b&lF5U4#jN$dD;Ov+unUQP}YLPA}D0ewf|0f zpO?!_OE`USS$RNtTO((Hl45tjh$D`W#t2B z*MNP}XrP#oZp!WMW<8*>HdT5~Mjt%Ght(hyXeKfOirDUdV!IkTFQDuaw1hJbFM@30 z@Q&&%YhCBFgC6!Ru5Zi6$5AjJz^~_PVBPTY+KI*>_;e^{G)18DZTc!_20e&3uIbs% zB%tgfR6w9@U9r&Mcev)!)DqDX$3Y6Mb$S_q66gGV&f}(d39lJA>B)KElIfZ z$|k+kpF=RXj-$-OC1F`s=j{fSjO6J(HAB(F#L_BMIl6tMiB+k3iFppP08qaE=bs3mF75q5B$P=H1Ud^y z~Jp-k}+}ZC)u#N2e1?Uc;+oHvukh@?kWNZC+E}u<0Y>A#4 zWM$H#7-VIsB3D9&FqS|T6u2O|=h_hCI~p?5Fdgy~Rkd-oXBNJqJGdQEXef1`-Gu21 z+Es)(e#4&a`spb+Gm*ehQ(y3@>R=}|77kH6yE{D)l=EuJn$sBJ6c`6S{S4yH;&A!K zkM4-XflpzpNxKe|YzV4{@CbaL{0ji4l11pp$TwO@FZD4p>cVj*CUi`Icu1rZT8P8w z0#dsYFCO5`Nz4hlrAVc89uR@Pk!9Z05rM8gOztPH(F*(;!FHsgz)tFp=qsT=)*g0s zb?u}k9#}sv$3tK~tN{?}f!N>lsh{gBh|bY(--66c0#4e`9l!M6Y_6+`e{!j{aQ7a| zY!Qw;?~~;Dri-{R7SVW$R@!pq4T!)mK>kSUZ8ilEstMv9i|#t`TkZq~o=CODt8;Re zECB-b7?K4DVI)Q%gx-Q}>zL~69~wGxViWSCUQ7aV8BtKgj(}FDVKR`bg3hS$2BAfg zoLmtd9sLvp80pQ+WlyBqn}L7Rs@cK4AJ(SNjU+Wp07H;lXF0g9r&y=WvV)jj9W@i` zG55P$)ZX;h;bvFBA`=5cIW4;qh<_teL-NoE7E1{qhYACsv6{QE)sX zJzWz;A%5N2lnN-v?da&S7I>}9A%PAg5iu)i+?aaLZM$~us&6#H4JBzTa^TjO8fxMR zO^2DIy(Q4NgMe0#14dzb0vsSFJ>F@LWCc(g{K8_+xkpt)CjTvYVxzB7WK>{AsJk(% zu?_}O4-pACS6Xfs^&~VYQ{f$t?1a0qBYVwkz3CE;5U)1}x=zxU)_}*@l)4;`fBxBiC5s(k2{M_Pfxgk+JtsJ+4BL3P$4$gmnCb%J#}QQ` zP#OhM@#{7-EYUFm6mT>;?@l=0T{}cFY{a85tMd71)}h1!Wa_%QNgd$v6#VtM@MKzN2S)6-6O&E{X4}hyT$9h=E z<^1@O)t=zl5@d99$<{MlUHd*MKwwze?#eG_Qlg1|oqeR^b6Z;seiRNd%HneYt}#`t zBs2CV_L_~T{A606Y|Xn==ZIO@n1D5FP{G9PFSbKCWDVOM^z*_ObshVa>$3&|C_`Y} zX{h5^U?H5^cGlav;EU}bvwwS~D%A5i-w-an5w4Y{5Kq_`@NjripQv;X_iy=$SY4YZ z>CYgE7G!ddH#QT)9Q|%Av^p*v+6M)1^mtZ33dddO zXe1=_B}&+<^5zr-pAu~2{ivsf#VuYTRv*S8Ox3@?P@$^P{a zvGma$bS;K+!5uTNVbAH5F^N+(@w|e)7cf+32#sCt<=OMe{8y{r6KG$n-LNlCiP47! z(BFSsIp?6oUk(Cw2V`{u&h%`_i0xWjTwGKUys?7XcK|uuT<;f#5CL7Om_T?7E}S9w zixLI7hl1lVG6bmaF8X5vzAOQ_%>8l-iX!JhD zv{C*8!@5zRv(yLCSxV192XWaO8uu0WZOVb3&@JSm!?DYW`fgQf#=*+T~}T)2=bcgPjB%K-}ui)-$;kYvOma~B-_ z+t1GnL1;OxoKiz(;vUb5vr86+Vj*>GP^w9)6<}L|DyTx#i*cd24Ng90gglz3mZavl0M)7R$Ci@I5b2&KY)Q@ zZHy*WbM7}F9HnZEqMaQbv#3uM*YmWY0vXRbN_q-IUn^;vG0IxsuMbmqXR86G!wV!!jQRRLlu5`+;^PasP}NfOwWe9Z0Nz-uu+< zp_B^_ki>qh)V<{;_M*|WN=>U!n!PyWcM^*~Ws@(i>WItPT#v8)C=x<_o}fWCmJ}9_ zv%m5zP8uppwWpd}l^U?Z&{%sKZ`X2(uzsCD8=5efMOK{l7*`Q(ui0aA!lxZ6Bo~_N z&=PC-oH+@ba2Vy~-9_QG$X2#3 zn+_c4nrk$7&YTqhz6;{(&^Vd3rwFP&`XgH2mbTUMED#VOw@OUNPgX zq5N+2w(+fY=!9%PL{Gjta?rgMw56iNHwiKP2?^4WxfmK6CJ6l9ILEUT^E`Ut4G?Pt zTlFtENY)o$B~Q%pTqAQqr2b+Z$^m1kGBrRcEwXD1(BmmZ_hkZ9ZXf%Ia2Zm08ph?z zaSCz)9j(Gken~LMPh^pk(mWAXoo0Kpa{DkC4nmlv<{Nv0;X( zRJjpICdtcMIy>*1F>!Nf8U-?t)=Zq5cTveK|E-3Kz0lZ2^~VwkvwhgjLf> z_5SU)=RlDsMFM>MqA5c~E%jhx_3%!zS1QpPLDfjBE{w+j3>-%Xn_xkmX+SeNmCl) z>5e-GSHJAC8%hW_fY)Sse236X(!8X$xtLVY=kxR2G2p2%k0bJtET> zpyCI1Zx5De5S|2QYse{|rgn6ONoND3E;wMOhs!@AnGw@u>K%wJX*&9qg$WZ}FXoacP`iAbZJ_+nLaZnsZ*Vav&Hf5*dEeA3fPVeXTO{00#r{@!l z4lfCSPOJUs#H&n$?n`r5zk_|q=a!Z_k$d?2sT+MrDT0D%2OEtjO*n%2%oe4!fPmfw zoC!%nCflApK@+^wQzP=MOq>Qyh>@~1c8raUoouj+CHEI=PW$>WLW|v5ssXOy!ysKOdGl<5K4Vns{pl%$8Eta#VbdHP zMjHd#$CdpG0|cKTm7SF1ktobq4mw(Dj%V|=rC7xQfwL*_3|jh0Q#hE#qP8w<;fpMp%0j z!u!jbHMgXR&ZOMWCPW-4wrG(~qNy6|Vku#r zU^x1`qZw|^Y6wDfz^jU~@ioX0Qg8mQaGEu1aj%S2{GEKd+xrnK66=1p9h8KZWXMKx zCw+!br~MvTgl7LL+Aq|#LlLP4HsxTABe`s1CaVW3iErP&zzOSR7had%HD`>Rnn;GD zV{TU&hEIi-2e(~&`Nkt*GSxHu3Kti1UChXmI>ES}-)W_UpX1K+(%n;DbLyk(+lshs z6-n`?v-$Wx;UA1~Sgf8&GhXOtoFsaUOn93NLz}Y47Pmw!SDTp~IPHh^?_W|KyELq?k$(ZNzZboa@f)hUeTr;R0{G+j? zSXxG68%cvnQLE8Hdl?2TR!Wkafl`f{Ni~`bK{3;V2M$oPufk|9JWPrFWh5H>ZhLJ- z+CdZ*@$PQvLC4+FI)Cv`V&Xl(9!OJ2&({Tu7O6_SdS6!Tcliv~HUwzv>&jMvohFHM zus-g((VVEn?#N1T_c66!u-HXu2lEQhg_d!_EG1X=#_f>TBt`PoYr&K<3cIw5?SXV^ zqJE31eQ6N&@PX%XPIBM#I^b{DdCYY<5FZvzCHJnm8#`1B&0vi(1H;*RVTo6=094=k z7`c3SQhwO7%|F4teUyMH!FKc$TLc6IN}G{zuB!mej@}OCU%A((lHt+nHND8~?fd+z zM{;-!9^-TxhvUOBXbhaA{wvSi9Hn zq@$n&Ct^p#Qa5mTssYJ*enlG}bizxoWL;W?`G+JdyVXV?stV!H?X0XU;!{vWcjBts zxa`sK2ync88V!1?f$xv691vNOBlUtq9dHu^d^8WT78>VzsL(mP&n@Z!*Kt)*Z67Rb40XxzhbIo{!FP*?P1w z7|*gUpcR0ur(wLs6Y%5{WHl9g&6zZI`U-CN*${?#zLZG!YNE+Qq!%(UXtZ~4pPHHq zu$|rBkm4@ih3DLUXLr~51ae6C;M0`yq&0IQLzRty#0UJs&kwuy>T;ehcm@?-6_T7E z2Wus<&6|IL{uC(D5L$xIF14em>KB9*wsm*EAP&lm`0(*g5VPkLYdr&X8DLF#ubqw_ z7?T5`o6iRpJN_e(QUWr`oq$%kun|`rkijq@(g6h_E!U=d{Q3NY)30u*pxUJtC%9BR*Ji69tZ8Yk<26^o5{yF-)Jm|SbUunkN>uQ7 zka1S^sln}tLZlE~IM9e$`qY*HouWa!7gA$iAMKtFFSNKicYJ{MWHa5uXf#fVuOw{q zIPV`J4(ZAaaePg$&@vX~mrtFqstwcW)W)35Q4j;mM z5vR1QxKc#Fj!TX6D%k2cX3dhx@z~>JjKV;mJy9$x<}SPRFkY?TtS^gJ%LJKB&uq=) zI-A3uT;g2mQT9kda6}BHF|p4|hy}^$P2-5eeU@jOK%OAQ|9J7!L`eCG74VI2iR2>> zXnZ7kE6|c2#8)LXA(4xpKYteREmkvd@gP;Hl9cq7)Aj3O+&$rNo)!x`sm^gYv9l;J9EN5-4~MhWBW>YApjVq3?s@N7h1 zrhk4kb3fv-pu^{3_e~fp5r@`E=;p0k8klnyhwj?WfCLF=&VX)TS&0`uA>IS7gQmnM zdp)NIG*cw0chvE42(P}lhwGo-Q-`FtD;!q|m+S61uY1z3EK}^Q$J(&8NWCL_{Ya1% zQ&`=W5SOQqpk34r=7XU4Y&SG16*S^&kB5ujB6fYqrC%m0bh1txm?=kLjxe$cTdm^TDwdLdG=c!H8%>(Xq^3(7lnB@N6>!bbZ0iPKlIL_t_|+I_Z~zFSFG zW7=@SmE9YpjcnITH~tU?lZe~h@jg~NYqA*KjpHSUTpRG->>=F_?BoP`uJhL+(#T)n z>=3@^u>36A!D6Nu!zPhruz+-Ee|BJOZ0jqg!+r}3ex3JAZnX=J@R-{qbBDFqj-@qNefb$A}OwAOF^e)oM} z*L7a!d0wNdbT$8oeR~U5AliQjzzwU6`U`q;&+iwQue37n6LtQzS>?5iCt2zOFi5lS zj0P5_>_x0>0HtkI*64_=1gn9vWTMC@m6Bc;z5)&%OLD5b?JbXEXfV7`I$oY)G@V74 z7YrSqeCre2uC^Dvm1;zX$|v#hMTa1>wViXX2u1<$!#ud-J{0vK06UYL3I#9Cc?K?D zxY-TV+pX{lOSo%{IV&W>OFPGBo5;^Rm0H) z8m}WcPXS9*8%N$kTqi>(kp$2pr96rat$nLCzl*t+6mpX0_;JndZmVfM|{>jxKF@780lPl*L!M@d$p zZT0zTSi>Jf>}Hu`xNweB>6|fnbnyq^J%WNP;&HE2z%4qYz(tP?tN)ajw-*_xI;uSy zo`?Em1N<{}L+nwX@>c|egt=saako!TFPJ5{CHd!wxN>VtOnvF|Og8zbkUymq4IY3~ z%c(vtc{fQB3akHdC|t^ssoJ!FN;Frx!B;69XG zZOEod+C39Uz3y{hlF{fiPjCE)b}48pc4;xA**R#P>qB2$a!3~Wyp?n})`18I%$TOg zP7lHaeGK~rP0$0ZinTzyan5u%64DU|1J8P$?(#wylC{_tObs8s1DoZ9!ll9UhI{(} z`9&jwR9zj@H}OrjjX(`bA_Wj1(B`F}bF%6g%ggJ_;pGEC;{!p7#oK{6`8amy4^k0? zWLqrRTOYIi0+@e>>RP?l8VGp|y-M=y=8Xfb&Zr>~$#rhEdEB~{6xwMqEv3W@8iiK zpnbrqBcqcupZ*Sq9YHJ+q^iS59igpJ3l3hdSqLyCUZs_+stJsbiXTv3B@Od$bETTB z+ws;o#3H1%Fqs*GsuRVft~gZB4W}9Jg?gayWj*c^$)?fj_+mSRbTRDlrnRgh(-7|t z-dn|D!zEgv=Yf^gUa_si_(;6l?4Uc#3;s;gK2V%`FS)grZ=k!gBk*FPVR}Xe->)&r zvVZ*dhQyI_9RnmxIQRCha+H`Wpll8nH+XAM~YI zL%=8aW512ybUWHbVhm*jAsf>$W#i$}-bYw^He_f@r2~3ZcUrAd9+hl*EN?yR%m;YK zGIh459D0Ia_o-ty70o%VV>-HJ?uqICyqs1tg^^v9&pchpnED=e zAceC&p+RH(Q@Fhy`zjR3kUs~5l-5>$u}MaS>tBNwY|(d*yA1Pl6na;tz*Rv3^{-uY z%;^0_9MCVeCdB|n$zczC|NbtKxm5;2!P27mJL&{Jpx55^Z3v2UZKJ^;Sx2}lw%5Dk%p8yVWvUT94**Y{{6Y5>i8Q2i=5>)utYYTO7v7302lyKC&|{3rc~&JWL)} zBD6laCeftPHWkS}s|MuMv!_*34Z$iU>m-)Fk-{p*zDl2^>A)=ZK(1gDt%DNZ@ZB>+ z{3fmS7KomP(>&05mF}2Q6r1>X<32U8?@nUQ`EBCrs>O{mx)L!fkbrPM4yb@8V>$v&N^K_cWp8;smLa2bYxAOyzoR0;<)Ywg}Ul@b1**K796xDQ|Ez;S-xQL zp_PqezL4G8*3eBVCI}Ka)7)y3y*2du!df9N?8MrZoqxq;Wi3L1ZkwtejJ6PV3T=VW zmd&mk1knzi)k0fP7*XK(7G2e~L5*e#GOmvw4Ih&O!9$GS*uE(=WK7X!ZT*SK5L)Ka z<1v~3y3l{CG6{FoZ*Mt~>(|`jtCJRs(iBsl&D~CFR=@lQ0Dw8Y>h^9F74m9_F|Nen^ zZQ%>%&nFBfB3}>lZ)=CwFzMpgjY6Lzt-CENA?y1$z^77jUty)0KnRt{@Cuj9oyG^P z48sR-{D0u2dx<1;DVL(?3=*q})s-Pe1P3SeYHei~qcD7Qtd8zHusUBz`st`4{VAw_ zo$6TJy9-@lQAs86eOgL+MPW&Qb6rr?Upu&u*FD`6#PBYAR6^8#Q;}hQm5%5b-hvrv zVL&6aG#=TsxbDG2YU=DX3&%q@C}z0o8jUJcbt{;=n#!BjVZ_yVkNYXI1Y zs}F*~13Jx`hyt0KR%5qx4BEUl>rjgd?z;qL*%iE2CXX%B0Kh|C=d|Sd(LPCkWTMCZ z5M^U)0gr5d2~NAa4Cp16V7p7g)%rd5Ov}f$o%gLYjd=R+0p0ovY+SqydnC?KL!I%q zkovz$H6>zqSYXQ4c?qCT2QSYCpA_*E^)pS~c%`UzBh8Zv8hbi)plawM6QbcmtOHHN1I&3KKoK(k1%mKA*ckbQE*~k?yjTl(MO83^4z!c ztT`BOjJ8xl%zF4|qEW>xBjZ7$(K0*R+sW5SS|lVW)0T6rK1SGuJ@gm$Jk?c97o2lC zoGV4wp&oIc#3M3HeN6~{PHNfXWqu+b5wW9miLV9Jk}SjdG^WB7ox3WJYCH1C!+~bV zc|;5cK*^0CpBe2v@RMWl=fL1`5Y{-8x3~g4+0H#Au;B8o_;0zUE(79?o0m3vhaU-4 zC8+L2i3vQDC!y7pV2<;#2qJ<7P&d3Pec%K<0(AGCSaX(Xw{XIdf6S)L9}mk6iCkp9 zMCkB_+EQaF!yUWLfq#i6{7lPteX3VozO19_%(jA6rN9DAksML4BQ*}ePi(fL8(MTC z<9Q-r2_sgYjk-wPF)}R*3|=QA(;<@p@0Z~g<{mILtZ<3h?B z0vJ>ZT(CMx04X{Q2(C({MroMe22-3fzP*~oN_vJ8A19ED*jf3AQGt80v(xR~Ab%7M zYM_~p&@OiL#mIQk;3z@^ftndzEeYj~jvk#V_;sxa`N1e!5i>Ch{22U#X1l89dFj{GKY&ZnLU!~!<;;6&Qu*2-ioixln{=8y->IVsj5 zJx=*!-{!4IU)-oq_7xn71C@8~-3x_y;48|Uf}6jCKX(HXkL^+DShWlw4cU~lS;zI$ z*%dm#t0h2?FYLVCu}|jeQ@~F?`m#LkOaX_6NjAd>vNT}5%n%7@Bnq_v4!D|BN;yHf z0zObUuS^vgwq;`{z2wR7Y@}vWwjN?Tc(BE+T3m^!ltTzVO{6N9&(gqcQ5K&`>>qamSsaV^D-V7~>f?*?M z%|vyB1!yeIFGnaO{VKQ@n{hz?H9qJzzMfMpe#68#y?8%_23uh`Nc5Xfe0(y9#F43S zdBegLWO5!AcS zoA6QNNTy3N{hlC_#32({S7zT~Y)W%2F(N!9)}|MT@=uhYiy$09Ygejc^Vv`eza?+M zv*()fz*P1Fz@+DvpLg{>!$d{fW^C`kuDp4~s{lLW2|k_i_D8qi@PUYzIXa+S3lmX7 zpTVLt(#puKLv%CMGPS`yBf=f%%qE;Om@-=moiUMc&8p(}c#)@Jg#KJrqJcKcm?Kq5 zLtm||Hk*hjz*ZwrRGh}0haViM@ka&r20RDzQVs-m9~XeV>U4ey&rzxza~r{9BNfOo z*ZT|j_*l*ER==_XFLWBZAhe`*CbJf8Bd}68s#X)eu`bAd&9g+Fv;?nhaib?s4)7Fq z)m1hX2M}e2hFqls7yppW`9J zs8FyfHh|3-iPOaV+9R|QTCM2-jK--qP$AgQg- zu_kp4IEoTjn%)o|QKpLqVz1B{Ae($VDKnMCSK=(Ui7o^TZ{p(!B-=((VXhQf1fP*& z3ABPO{F&p)Sf~IPh>4!^kZupS2QLo0B9tpa(Ir}6UbzyRdm9$;aiX0OH|h`o9^PJP zK1-vL$c#)m6((fHjaT`}MA!ZaF zSQ5>>ag0T=L`<~6TfJ!rvoh!8#GU?BR9uF`YBLC|BE#`setxIe2s3;Y96$r~Js^D& zXj~Yh-~8?yJRCM#*z1)PQm<3Z&7NpFk?=W8I|{OEVIzDC3@+!XgE!lVO?cpW8P0#( zX0vSphtMWlyKP%V3c!6{fFHf7_wTPdf$byame>wEW1o-O3^+E2(uu zZ_2%T2n^@VpYL@%GiI!UAFxQXj7;aC88C79_5|75QK@M@v&Bd`^S9N~c^=U6ZVW#XV1b*ruP5_UP&)v>Zr1)9km`Lm!c zk$asY5JwL@C$0piXS+DyDdkKqqirt=u@9JX%iFjCEb>FR%S1)N5b?j$TA`}F2_R3( zE*%-HIHoAFj>`}UMlJ=3X4s3?J5@t=VVlY^Y8_F5)5T8$fLwg`An7^ARrL`C9d|wq zZ%MAJAJB0bjV&l{Z32vC=};ew2daqRrCi|x>Xo8iSdjW8f9ugx#A0JAd^BBt%5#jCH_}5YGSNy`MR=Fld>+D&B+yr zdzL=e+KyQGh+Yr}H+mxlmf;@Y)Q1TNtb{sJ3wWU>P8!i+py)-7u&mKfv*!9qLqLN4 zyS=;^k%?w86v*dn?!z^nLaJR2e&A3Lu|Ys>2)*%+k3dHJY`1l4;$>dGhxpq_s4Ftf_Z}G;5gBfTTQW|2>O-iVg*#1r;Lbm? z9FZZFgSTfp-1y~7VRuw}{R5mdKk;08&zRJAd>b6B#BHS10rwZF66H6|ezA|SEpgv{ zo(wX%yF%_7Lp)ZVm{uw>!JXPLPG9(dGMpSTGAOUSnueNf@Y9-{e{k! zC??*Pv!0$XFd!eqNG$o5Ox)eC7*f0ctcMh~oCW3J=BbirADseO09SUmOa7W5Gz0t5 zhsT9HrimH=@5FTl0*ZV-4+83c>x`ai;Jtwhq<-Z1T+f<^0dgnpj$`JU?kZicy4%_@(8v+Aj43{#)3q3>| zNW{XTTcvk+c$oST#1ymr*02+5+zop>Wg5Yo{0R7(gsoQ`iTqEV&^SH_yicm3*#Wfd z5QU?Vl>_7Rg6Sc;X7|BP$NE(!X8|yniKheuEFpRMI+1QDKHK#iK2L=*n*;fnO&gpv z!BT>HHxjuq2dhg+EB<8Ctu4vqs|d#M2P7AV6O8`4GK64DJy6UDI0gL$&>gF^n@BH^ zC+vfD7)DT9UUnoy2z&^d?!ocVvm>H@o}&gKef8(Tzri>ndfRE>Uz};zThC&XP#H-ftKh6L zR5i#s>`IX#+5#nOvHB;rx3{+pDAHp*SGv6ru3Gs?ii@ied=D!I|IU>o+?~T{3J=K4700DJ33N8o(t4B!wh;bhV%B~yUotbr@jpka3# zANz?K1uIe5_e((uZWT>!MH~4sh->I17D%|^ANQkrJB=q}e)|LvZ)&5!t1OV?d>@A+ z$)9lsh9Q|s{dfS3{jDc*dFkE~?i8OTDI~NXciFD3EeP68T|U!i&WNJ*u^>#MTYVT5@y z*J$bG&zx26=|1-F3jqO=wk0B;RD8t56O6(I5OYy``fpC9pbm**~uKoHx~ z&MOpW!D*y7Rk6a5T%pp}KrRI1CmbB`^;BS<2Sg(7lQ91){7D7R)LN?&Y z5I~TjDgfhpbn&~Dqr2lVEa!4y8W=tEZ%O!~1q*C;7v2KmYY;17g_)v2EH|uKvx``@ zmY4f4d_jpsE0DC}z-5GS2a)6)rcH#QtekHXYd5~B*!j0&I!;op!%=!-f=Lvzm}aLJ zL$z3>T$MeJE8q<@-wDlU5|x38=O~@7Lxfh`fYYcIrJ>U#J0YY?l$Rrx5eug$G_DUt zG+hPI!u#k&v4rJt{Hh~2v(uKLS_9w4{#;ED3sP#b>B50>7>VJEzx*#=_>6CJpC^tr z68zxg!kCYzM7V%fhejF|TuXyvMjecz%=cIEk7R(Cfj8cdR`b4GuFCe`~xaMAtmyHM+99_e0e6L*biQTI7l{dst3EMwkTpGqP)bN zeWerILGyFSux;+%O94fotcozJ&!LDWyijh*ghdxj?yj!y+DM8o_~YFX#Bsb25&TGO z64xlH>lLVa?@1U2&~;nw7)}kFn!LRAIaTstnfV_Nq9ctIWK4e8D}q!Pjed`idbkHx z9d)JAXjGMqpGuX}mbc`+tjEWre*|8JD0dt|jj!2cOYTmFQGpnDz7up;?Vqp4 z4xydi2Oal5FIxD>%D!D((iDa65%!icxVsXA7@_d;NrK(P0%0iZ^zt2C2nG8S>_sIC z5|rXZqV7(u;5yc&EV!6uwHqy*5aJv^iD$V;&@}kH0*L?|Wz2PE1iy5}0W2DxQr2p-f6Az;TV6#a)&=zj~`oH;e*_);;^ zUYfa0!l$ieI1I~Bh1;x^_;sxte(z}uzHodg)x8LAtCVQc3Qg)7u%~~qD%rtNnsI*> z^(2Y$3olU^BWx&3Ct@*bjt5w`O$F!cy+nALK;64v@kHBs^D>4cs;Z^r(_H$Tb&DS5 z+`WRxF7sFXs70AfJxLfANnXIrECq@U2yb^yGBHGlWU4-RwZ1}TNt2PouWR(|ZZ4O* zL_LQUKXd{7WD(G7BX z8uj3-@2UkA5R&q9Vtcq#wF)-#>cS!w)ft4Lz7aW-8m9H#oFJAnSTE)c=dy;DOa6n{ z5QP;LJm=SU-VV+Ykc~uT?I_L5FjuJGK5-!pfy<*T0$EsYGiJ`bW80a-w`^J4=Tj5k zbRTJ@vlssBRld{iH$1ZV^-G= zTG{(t8uhsDCci%Tuiv<=ssH&`u<7}K<}@bhhXn{D`ki3KUPb^|1XU?E2K@~}=bqT) zEXbn)>ih{YA)RJuY|^7fT}s04p;$d<#68;nglG`;wV=NP$x&R!fIkGA_t73C9xoVl zpzJ<&9UC%6Lm)-e=+_65oE`wNX2eB?bsOD3$nYRR#3i(GEGPbj`?)Lfi2*r?!+n6& z+i;}5lhB8jq93kZ*gkKcbgo~&zWsTnUKwzn2<+x7L+Ig$T4W9>;;0=wm2ry~6GI4` zGt@_YPh(jk+J6Az>qlA)#>UIz!AK~`l8pEdNne_5eod}A*`?UA`^10ERb-em5F^rB zgPlcqvQRN20zN{3);2$L4$wP^*lUb~1!Ntb){&{RdlJGK#MyS_zk)$-Mwt*`(Fjtq zUD?}S^cEp?D+tPGe0^zJ%2&X-Tc*1@J32-olA9vWM+7%(A%!<#ZD=7kM^5-!!4PWh ze;dzP@+?K})lw2-5f00tNGxO%T>3 zw3t+p=k0@RD+5boof3&^0Y4@zn&9Fv+ynkK?sP)~ISYU#^;QM{Mw=cBG@C^(7;w03 zi@`sJj340F6Q#Q;WfYaK_B7dK1XK_;)X|+UaslJo2{V!V&_nefhOpHcvwP%A+U=S! zP`i}N3ZXNQ$R}!!sD>wonkcCubAYBP61A4UBkWGdqKF0ywPXS+(#BXy?pMQ%|Mm^s z&_F$EUAR4YL3Oy=1rc^m?;IE!;s<;MiAx0ZB;RTn9G>@x-GWL;;AsL8Xe-P@nZL6c zKs(_G{jwB^tzK|tz#Zs!{0#vvJOVr7#bG@!^u!@m+{WU7i5>+EjIYHE!agjB!l`UP#D$#zf=k33ljVT<(D83n&CkR{0O@iz#%qTWreQJ$CzgO3 z)KG^o)j7E0^d|VqLDO&;SP`m$esI9I9}!D%5Qg*};;QZZ-AGPQA3r$r9Bxna)zAV? z|HL05EqRK9*A$P-h9)Dq5mAT@JgC{4^;co7^8@MXwXe@z|5&BdE(S52MGm1 z^@1E&R}d3kz4KFnQaU`gil5F=ZFE8-GxSYAg*ne1zsx0jj-r2p!_N)^aav)Qk2^>U z;`_IOPhbv#5PGqI9DHuitxb8$MkleT6A|j2Q39H`NQqle9A6Bxsm_E@8D~eQ?Qo^x zM_2RmfC_+dz>$rVyvSq&6EL=yD=L=eo?f}vNava)7;DI`jeg?nEuQ{WFjf;E+bBhk zLXzz5tXK5=s<{->(FDSnI1NNvByOu=t_xosDY<>fD%pO#IR|m)z!?Ulv<^sy8l1Y1 z0MAa4Td>7^j>&WeIMtsx*2p7T2KrfXX!D`AE@SRAD#ryn*8yaS31lneoQn%O02{&lQN4l& zW6uMC#AE=BdL@MIq>k)gJegl% zN@{=pIfZ85>9V* z1h=5r`a6l=tMl&gX%=6^>Ooi*oqP5(Et`}JbQ>7iHosg&9kVy{(yUSR8etGNV2fns zzB_!1G*E|QXfRrjvix&;qOe3*HN-1R)2U9!X*(1<%8n*c!7_v_!FNdGh`E6fUe+(; zwM}n$yLe-4ujc}tIDEp626|)0H{OZBqaXkqI}I#W9QShkSi>9s#-EC^*13CxjxO&7 z7xjSe3zR~a?V5ZCY#x(Yhc1w3A3-YbqtVhRvop@k6g56!U+MUECU?o%TV;@Ryh-lA z-a$#}j-b&8Y)r8iV}m*US?d|jw9Avx@@unArR`h;T-#207?h8XcvQ8euI3+|vHiKO z!l9@lP2j_Qpq%58_UF-jdO@`mD%$VD#o!^QIoJJ5e9fJJHuv;!}unP|q7_wV>ZirE89~^@~w>9hgC_T}C&I z4GPL4q5$E<@(x80#jnPZZ_~sJxL>&T-v+e{|AyNX*NG3HRY80ql8T~E7_2}6WAtJa z4+2_DYq959=C#r|QQmMxM5=9c2m)LSat-A#?CkEga4bfXLoFE$Dx`f)YUv>CmjPSh z&;1n)7z4kS8m>TL68Y}q@b%{ayED}K!z>v8K@M+ww~%2LJ6PQCIuM5-^DLl2)NQ}( zbQ(SphrD;J8`yw7Vg4^Kb1{w-R8ziz>AV&R^@{)jD|P<4i+sFV&8MGCzN zYIDA(;Jp(k!Pnd*Y zqYeiUM5h}tI zDi+HQJ+POT)XtKZR;wRKF3#REv-Aq8rb!xh^m!`=g6GKUgUq@n&ekfbL z_$Up;2KY>OjP#PU3MdM|upI{zfWHHj?9z;B&cR6y;U9>^p`Cnym}{1b78uQhpD{tx zhbpF;8qheS$hZ;f?_J*d#vx-kcoL%LJ59D#BO^5oxfR(mf_2wmIjx6EH*6Df81CIJ z%@~?lDV%x*Aw$95?AYeLk5+ca{WYt_L3$x0d$vKlSC0RWd}}jZ>rZ758m7H0tXG(H z@MWC;g>$%AQp3 zXLIQ}BgU&it#t${MgqQF&QIRpnWFcOpB}WeX@6gc-Pp&OPlCr19@f-zsN9rv@9!Pp zykbpx5|XU?{MfyI+wCcnkezK{%n|H5IQ2+bv9eqHhb}n;RPGH~ikj>1RlwpxcIXpZ zk@S02D=|s=>eU(We%b|rBRO%}A#~WhZ@?!caGwl!)LRPwDofp$lhpzFyy32^f@#|_24vcKbMystY5sD8>3`Arg~((pa0Jq<+$)AO zkpu~CI|gr+Qnm{774f)NlydaQ#cHx?_2MhL=$~Jauih6fOQp#POxnsUc55A}UcI0Gc)UP2Weo*-i*Ab>Mrz&`U-3gkL1l z$k`ldJ`9eMWPv^UKT+afVV420{Rm@jyw84gf2ie!y%2^ZMyl5S7-j~3JaJnt97ioi zxEdg))2t(E#<}#oTMy7cEvcnH5;@K7u4YfEI?}Ol%)vB;?*3)fEv`KcGg` zF>gd}L4&)v_yf{%dOcQlaFfHm>dma?id znTxSQB?if_7EdLGmeKq72N!`UMz#pR9e^=>u^#5$&sa_DEU{S4v5BIFv8sNH`*H=G z2dm$~sb|o31OAo5Wmv};z~8SZ1$u*s-RA?44D$hQ5Eqm>hdGu@(feS6S#h$;mj%1B zWyq8Y67`f6amPIH-$qCC$R!H(7_%FjB}hITU2;}4!Mwpke3%a9?Y{u+QP^ zgDd2p8DT(HIUw4%i`<5JVredr>%eH{TIl4?{tRqdJX~pwwznes5=^-+h%=-|k3so% zMe*RlgW>&1!IqZD_IiNbch5u@O<~CbGN$1y4fOtC+=QUSM7__SE3kO65#37jn>=sh z*!^dxxSYzqnWr0aI#$zMRwvFf!%JN!ZsbP$qE$8{{0MUHBS4mx8~;L+w~Eo z9M$nzpi1C4_r~g2qeQm1sBGeZ>PQ{w#)WfbfPlx@P+@I>qw}ADWKm}PlS)=b6kstW znH3w1+rmMO%`BrSPtl_wM*M}A2As&IO^XJ2kGWZB`qK-et*PuqHjvb`!0x|SWW?-6 zYI=au7fkA7KC!^|Cldn0febu)0yPk)w&`ms5mTP#{t%3EfM44c7P?#LRbZHKPY~|) zNAT}7(0e9sdCM^18i|u{Jw@7`!RP8n3*ea1igZ_3d{V>U5P<(=A4%$A6yMC4b^!0x zUt}Q%O-9s=xADV_K(q~j<_H0f20MX<)`Mk1-C<@4b}8{RjZa_RiF>=8uTr?uGUJQ9 zPTbLnqJr^lyXtNE>Fm#nBZh=3n2StxnHIR9HEvm!x$K#9S*BUv+jDbruk~um@q6t> z5XlvPqzQQ21EjA!FJr)DEgdIJj3aC$PJ^2N!UJ6cbiU2g(C! zY1m^6%*itfCT_;o-YTBCvVZ$;ohwC*MG}cCL*3c( zv=eCoAA;W2Ud26o_yCx1k^8+x)Dmz=Fxa)QpdwCn@136Ww+7 zzL=kA!y%^tq2v&-e^xLl(#uSl}4~&JJU*-%!A5Zx73iSN+#z zEHipG#cH}2E^5kJi(6}HlQMy+IVP@?k2uDv1Diab%@i!)D?HZ+_{?*Dohi)a<>gq(( z!3q^Aq476_mErg)a*0dtxlW$7$8}Nce*oCIjwV)pB!CR5rk2s2yP;UNCdi7@VE81c z1nisaF%vwtW|2n%5qXJLp3Jts(F{xMe6A@!l!Dv=oP?h-W7Y$bP?aAFWucJCq|b?( zLpc*U15Gso)V6I9-0w9xS}U=uPbD zmS^bc?(_J5i>V4woAJ-b)}5F*cLqN@HS7OMZ+m!tg=z7=)*b81o6DO06Z-ws)Mgjh zY>c|xaz-v(EJUm_WIV9%;6K5%1Z*t-0p~{Xwt%Rc+c)ZHZL_-tgWnQ5; zc1am5ms!N2fRHYb+^P#c_mtdvPQfuQo9 zH>9_}pGL10ow$avwJ=}u6ssrukRLbz`oLcl#?*%60mS7&O;Y3pCRYqPOA%j+;-Gox zLk=IhT-HVx-B|%hxtVCh_VD(SmlJB)yMjo}VWd>C!uIbHcy1ErJalY5JS0JZ-tXJp z^v-QZBTz+^GS)WfMe4dwKHDYpl&ao8-kVU2 z<+3y3Llh`veu$7*~~PSdx2a537OAvIRvqI_EmeM znRZGCN|JmBew5);H&=XLB1p^^aaHON&~QjYKVzK8E%qASFWS+!IB9NClO$SuKY-ip zE-5LAlD5fb4bpf3N{5(1ADmILvK>-1-mRz<9{=L>#wA7X*%vP4#b_{LjSKRnV!PX* z^1eg6mADwB#{g^z+pxzdgh48PjM}XY4-u^gugA}*|Am~Fl4wf)sp=-bB=8JqS{%85 zj$Cqbja-7YqtLb8arNq)<}!XIrL7Xq1&1>1HD@oHRqP=3efb__mN1vM2rsKEEKvb8 z$S)7CUak%YLwLtD$?7*S`Yf;?;HqJ@Wx)R^8}7q7xDWoN4nU*Jnuo!%FJyI)7e8*J zbh)ejt#MFEV2Ybu_%;b++iaXszmZdldOv)WYuLqKf6Vd%0%gdGKx3k10C0DiTnIvu zqnpvzizgI^kQppmvLphx)^E79xWi%WTvtgT2hu}W>2CK<2JS;pgrEtIuT(x_vrV74 zwHi8cDRa4Zm{NO&=gQ4LYIfUqe0qSwT@R*ont(&oS{E!>K)ie^;9!wciai>Q!$MnC zuKymA>zc-)U#&YWSN=`9%TqGWcI(lBizV=ZT&#id-mY{`(AwOje&kzpC{L7?>9`x{ z2ARXBvu=rvsNDWJ65WtgqC9)L!}k5S3igJvYUgUs%Y~I;q04@r?l!TDR{z8IVgAYv z<4wYtsHNU4Ua$m09oUR8gKC?z!GdJ6>+uwAHopO9AQq1~Afa8?*M4R{TJYJN_TmnK zaik<77dMuQm63Z=L|bAjZs~7l3lI9%4jN+`s|W|KC&x-ZfBxJu0O-;?Ffg!reyh~H z#fw||Zoe)Qc=V`h>Ld(QyLj5W&?>{=P5<@bi9k!YjWKlIH-aZc3uxgH+@N^g5um;r z=x`A-1iejpsg@Py7LXKR8SJ!pbDrNSit&KyGzE7lg&LfINYvCQeF9v=tOv|O)fA~x z9`)CG2ss>4^Hhx#au}6S7`ySt+19P}@Kk*{A<^Nt7e63*ZBVq?xLQiPSxm1;FZIIf zvAh?>b&SEmv$6#Z2*uV5%j^KJ@O9k=!`yhCGF-D!I-GKJ#Zu(DC)nH1wA(nGXEDxm z{A~KPTerrI493%>on1S6t18vO6||{wZOLOd{b77T=15` z7(D(h`3mti0OV8<4g~EIj?CT2*&>M0i(HEA$b__$Vpwii-(>PvTQT;3)4-I|=q_qg zFbx!27gBlt<93_+M^D!DXIMz;@@^2_>YV%QZY*dD$>58>z+*o0CX7&e4k|fnuAMV6 zzIQhiN%h;!yuK@u+^6MiE1l!HPv&&6t(i%G`wsC~5L8i9eg3$`&`yWdoZ!Y^mN^X? zoOc!9Zv$B5q*xpt=nm@$4oNHsSEf<9A{>O=RDr-*fj4X9k-P(W|Ievayi(I>Xad`y z;v8R=E2p_RUqus|0wTG#k%W($&s`7ejS`4-NzVh-u3?uOy2Ge&?lmrjqd8*xDSLmA z)l6{w(~J&Q9##xW>5rh%7<6gFLVR72W`A8_^?3b)>p$;5qTgFGr!U|8b?Cv`MI)ti zqCPX`DpIgq{#RrEp61SGws>`=qjS~Alz1EEZA(sqS&ng+5@i4aA*=?=E~gdk1Mt~a zGhL8IoXKn5SN(D!7Vrsa9Z20VedoPrh+D$!j&j_5td!G1DFB?R0sNFm-?@d79Xnh;RfN6agJ2NRT} z2S6|1Tt-UJ;7Wo$S6M8a`@J&g`F<(6$X7K5;kTYEKR)`jG3=FvAnMYs$;dxD53^!= zFk9*qdkj>;_(hsVRDc1n_vf@HaERonGoSV-$y}8Cjaq+z$I3Eib+F~f0!ZFoXhxrx z^&NrcA(&{#f(QHik6?Edk>PJ5qEO&e|Eb0XlDxZ7>Vc%xURhEE6j?z&`}zKg6ulvI zKZbA!(-L8%Ouv{l>na&*dBJ;i@2z$?h(>cc^u&rQ8zHbd8JWT(RG`f!8T2DP#f@g> zcW(Rsr8PV1sZ!7Pc+RtmJC5@-GT%e#YU7L!H&T#diE$ijThSj?>1Es3n6(VxWWEQk zV8SYldqQiNu383GGtowr?VxGX2H;RAp^7#l&9eya^lkEq$=d-MDOy`E7ET}Y z9vGEEG8&;V2i@=P!cGL4cSg$N?^a?ka(a5K-CDlO55Qv<+PZbC_LeS~GZx4e7gh`+ zva}(qG?IQqwCa@a6Hgx?#s0AzZugbJ=z7`EJ^7XEwNyl))|BGJAV*Ah(7qhB&IC@P zY@KajY+^Xmde!y}R*~Zum(FwMNiDM9Gb486FwFkz;jyTLF170wUFG%W7?<}B#J?-a zQ`R56cIdg<1IHB)TKiz!!Ip;t2v%!1RC>wBP;1V12S5yqR;&1}(e=5J!)OQukg8RRJhb?b%RrXmZ4X=XsvK|-MD^0QqnVgc7f>7gxMm^W!QIZW z=IZyPjdbZ}E$^C!?dpFw+TT~LyS1;ft}M-AtJcd&@}@eek=LgVJ4?&n9s-I*ods%P zffkSkcisMMp1Cwc0fLZ!2=9mN?<-p2cLbsOTU%iGKp%vBGNr^rzB^$_OY`4r-Nr|$ zJw;qu;5%?bRwS_lat(41fPa4!IWWGdMIME=o+{iABB*M4@F2G2sspu|z>F;gbaMYc zbC0P-K#c%&HmHa$3QNL#Iq@L7r=Ta35=j2%*R@Ke5UYftyV8ax)j?bkS%fH+B2Ryg zp&(dJ`%x#qM><>7DjpBv1cF>x4JEekU?%4&RziyCi#oqu$7lx?eByFI_udeo0`rN+ z#S_Deu7WZP8Y>7GeMlNJ;7%cCwVb)Z_kCcXf<~VJjNl;W@f4)h3cp$nWLma0X30cs zMiTA^4D=zEmkFvHO1jC&z;*;uLv>)Ui3V?eLrthn(}(@x%maTSIt}2WO$cV;2zXZS zA$RaA(nPdbcv$E0_T2vbtvYc{s=uoHhH2A!GEf5XA+n1L@_Bh6NpDIiMqkvgUrC(a z>u@TCH4&)W!ek#y3Io1?C%n9TT{BI5Q_v%1_8HcBBOX=ooT=vVK*t|ZL_B@pLjXn~ z+X$ZxU%O__iV7CI@nz9#(W)>RKZR$lz6CPM)j57{@CA;zEd$E32$@!LM^%YuqAT5q zn07XLEV<69;AzzvF@BO2@K-E>|3Oc?)gttLJD$3UCK$Mhu&R>G0uDiN6LE`) z+COR_PFY1o0@@{mA45Z_BmTKCQpfI8)r=T5K8g6J2(TqE`gSIR&HH~0!4A&v$>35x zZt*kAEMV4qt6dfy$915L9*@Wm3(~^5JIhfcE(4V#wnSYIZ6o(|a%TqbOb;t6|Jx9u zA5cUPiM`D3Cz|#Necb4&RfKW{{E7(bkU{owpnN4d=SVV+4lLTJ_9san%C=MwV=-^F zZrTSxPa>z4+?a4!M4`QSpnMR`VDbhaV02`Z8S?BI7P`~-H%p4VL%+r0m8o^>gzf-1 zRQHYxfcQeYjytVq@Yy44BEk`$9!%&zfhrbBxS_j2!ddJG5^@06HSsY6>`w9r5*IT^ zR!o^Ttqo0|a%c_Xx6VSolzkKR{b?jiArmAQ#wz1qbAxZu=|<{gRu^VB5@x=lau~f@ z4oFwxWjH)&WOqE9b|hUv>GNAR@VHu)G6rarSjP9<<(z@Myu7k&$X4CYaqw}-7Lb;$ zPAuP40$RE3e@GJmeQMzENhKT+dqtdF6YzP+6GIxdz34eoTNP8o_Ygl7)fL-ziWH1m z$n(<#9&;#kHL=jRjSzqfRk1`+p-DLKQL_EnXQEW;dJ-%NL)v#_f~NUHGLeiP@G}&h zvDF^|`X;#rp0{<));JY&?JK$fa&-a*K!_qh8Gv9#i6dxF(6|=RMYb;FlIecNeE(G@=v9kd!+Vb~eOjtwrlF4vVeq$~3kDyFazXsW~^7ik%_A*$OZ7 z>gQqQz3n!CooPi}=sW->(Rl*Sg({lShm}a7r`>j7|Ni(;mve#6!l6a@_%$(O^?dy{@$R1&S=>{_%J9->%J$*9A7KE`Qe9w#Y@eMM~po6|w)+0mE*?e=uO zsSpSu`%A4>1pGktrcTrgIA62@(2o5GqO2ve!Rb=F_Xt2SdTEAEUPJdeG^PHc>FxJC zJ;8uL;2^dm-swfy7-&dZrAUMSO5U^FjXf&<@kJ49=HV;y@k`JXe15^NT$X(+|7ql94+TRvAAuId$mwN#J#jIWy$PJXP zL#pg)YqqKnXbwVgXN5hUgpm@|{~x2QffO>6aaQ{q_t$k$hDW`Bn_!1}ZnE<4*Ua8c z6VJzJmm?+60K1IaTY+#%i(+0)4J^vyL+3(sf(>h^)rFPXVvm};C#Ehy_oTw?sYCw= zAs6CikYLA_!jK?x>a4|0DZz9YL3nj`_n-8Bma~85>1>^)BSESw!0SNJP)Efi_^!TQ zAMAWhKtDEN8F-W!JieDJJ0Wxm7O{o#Y~cTeo!D2cS{afr;^P$$tx<$K#7(7lgony0>;H>+yiFe)}6eIfg}>s##(hOULY8^3}F4(A=Ik$ssij|&zUvLX1lPXc{!C`4EG2c zy^BebKR!VArMi-MmF~-V|8@I1cLKT8z%#~}w_{wM3%J`6V2n$uR%am>Z0_#fq=b1r zrTDAX&D|(ph+po<0TG2b7uOnX9_!IN{p*WsS2Y(jX(Adx(k`w9HdqPZVNU2$60uB44J~Com;Q3uIKl+~{guMn z@+SU@2iPnC79`4Gy6F#w6^f)UhlkJ*YR7S1WVM3u1u`yywsj*~WZ-^;P$!}m&bV-h zs6=v1q~B8r7)C5aVSQw@1N*oI-M`)|yXgAMdI4*IN6J5eT++~jKrwyv*pgo#3f(sk z2Y@oxUm1E$V8}+J>ODYI<;z3h0PT@OA|UYif4g`sM@U{A^Z>^`ckDHqy>znz&KABF zEF128Z+Hb%HuiJziho^)_slm9BxPK{$E~HK>)#^k3hmKQ{0c{<2~G*EgpAUk|Te<%`+`oVma7 z3#0-ZKmvK~pY>?j4mk|~qX4;TDo6>#g(aYd_tH&lnmHz){H5O#{Y3^ESac+1+i^KA z&*Rc#b$7R%`k6iN=Sa~9^fMUl<9T&1SN2#vH)CY^)`iX(n7nwQ_bQoDTE^7F9T~@OT$sE~Axh16XsqEx{Hz-HOJVG+ z_Uao_%#uJy`=smy=k2@y{PV~snh}wC>SLdgnc0)FbV+cKV$!{9Q6wMvx^!yRKSfz6 zJtOy}>Vdj_dUf|C6P8eiEZC3BOKlB@?MMMPp$dXad4XtNO%5{g#o zjq_NsV#RMkl?YLbqi)B<#Ox{&z4qq?wC8=*Dv+UtD#vIZjXO1~eUaLg{@>=&7ia!- zw*^NS_ELJAw?Ot9X(bVb!h6rkCkPh-wd(oXVuR4Uze>AK0H506DAB-uSUi!{VF|ajhd?J z!}0MQv;VGmc;i;W#M|U!ehkmqvuAG_h>D2pms(zqU^9Kkjw}O+Ntz(tJ$bd0Tx!xS zu(KSZZ0zC_5>Q$s88ja~1meeMoUv&K9{r9NK(?~*bbV!SFL=MQ`tUb>idO~~CZao8)&EBs8Z&n0vDiS8V?gz+D?x zukJe!>;mq1zr$a4Dr4#LjWIb_%n7s`uc_e=UKVSOODQwZ8$W`3zp(KS6x`+rZ!g-Xy22YynW*o#-$< z`?+w+BuspG16=TPQ_~bYDnEb!hd`>JU0Vr$@A~37VQ-NbuDY`izo3GIB zCb}>jFyDH5ULn6NpVM&=X!v@X^7Xm7`4To)&ZkdHaj00@*&)22JCDylzghc(tjo`Z zpqPDWe+)Vi2HMK-&Ytbvh0fJl`Mo%|P)QUPzQ#`z?-Y`nm5PdrmR8J+h(IVApCt8+x?K$Grf({6~fn_=|vqq8rO6@$6cJ{ z?g^Bp{qX)h`KiXQMmNo!>Ab^Vbjt1V7|ZN8^;3=?dYm3YOXjdxSVY7uyyPw?r?go0 z)uQi;ix(pcT+cY@gh8N70L-lU=kn$JC+f|(*}LE$dU#-#f~BqPLeiE$y^R{;B>)+Z z*)UiCAzxKhb!`|@{$~dZ!B=|ISyxxLtY6#nk5;llzW}MrQW86P!%%;y)gT5M6-(R~ zgJvHKaG|YRonK#_XF7;!#`kJkvE*I>w0b>2`bBFuoQGQwGG17>&UE(#LTj0YN`9vY zAKbD${r!b_A;fI%&3pGwDaEljT(NbHjEwdV%xs#1!`R!Ut-XB~0IEgS*47ej$@ed` zZjz8lJUV*sQemM=YM!#Hs_J&6=+&d(t-W!9kr{uz~%(6I26%EuWQ8B3FqF0B?7{VNi4QyB_RpFNu= z&SZKEoIvC4OD3N9!YMmFU+Cjes;B6`Y<(aOCgD8rIV7a7FyLA9N2Kifwo~ohSM$gC zmK{10`lNDeyLUbUT)X#$qj5MKuk`O1s1V+hm$jDrywe|hgxs`gv3U5~s&yjEjGS65 zJ5+xPG1@u5=2!1=*xdBkDkaTysI6vfv%;o)=2b_AltRr2xz zjK57yuS2_iDpB7AbC%?=Ki4JUgt~$Je1nIa)t4!N$OMz15}Ipl+-k6L7^$=$2&A7} zVZ%p`e+K>iXkk3GYRqN0gIN0ofaGtDc;HvS>zvGB*Vf`xIvmqDDH{pLQ;@$H3iv3* z-@@3z>{qXLbvXdA$-yT$GT?pZ&UvKsD{*cwFrHjiR(3HVfe9hh2k=I581pbf)i%P~ z(gRGI#U=x}*H6K%|Fv~ZmwpQ1CAQP}H4Y?G$?b2N*m&&Te-gT$*09~hV-@dtf zdwZvdN=q*TpV%Py`n~e@^1?!1P(3Fz!10SK)?-Xxf$-yo^2sdqC?~U`9(#0 zfaO_%|6&z%?b=KoU0ph(Fm?0+uFLwivblMzhRrHuDVO^9b57tW2$MaLvF0|`%pyu& z9`094K2dTl@saqL@nw?@%T?d}}zbQ;5dcC4m0FF`A4b z)VJBNaX+Wdnl)=nF+SdmS+f>c+t@UJAqj_MSumD`r=Q=^$Sja|u5W2G3e1-TSZNi4 zA&VP4Ha=$cP1*T-s9F@M1#>~_PkevI8UWZXbn9OGVeZpHY2!WL+scI>fNpKz=HugY zNkH3hVNvM%89mHevPt=zNzT1X6Hr6${QVz;qEO#)9j2t@5?G&zo-{PPW@Tbx@(TB@ z$w;g3+qZ*K?bYy$esdH@^Kn>5LE?EqM+Kd;&6tw36EL6$4MtOJRngePQc@jwKDDXH zGm>L-!2XX69OySnsvu6te?!_*@Qxb!@fI-ZoeD6>TQ-7;!1>N z7Bhz6b~YQ@!IE59iPy-@N%BN5D79Bfoinf1^Z)y0`MB@%e(yVXN5H8)x`RHjSz{ zMzwp`{vju)Yi{``7(@Sfc<^3&hM=_zSV?oHpZYcjg#qQaC^hn3X6lh{v7xO^D7S>g zj5w%r?3h2h!2>B%bhZAT^zeASDftG|Z}Qt0 zp@Trsyj!#^H@`yWC zLBScoh87RA!79#oFw4?gG&}rlQPHBx<;#}+W@Tk{Y2YsZ(m#gv!k@fch=#+w4J&hx~3`frB%>DCl z7k@wxv&+qjkrzH95<5$3d`eKu_)X7VRa-wkMBsTxoHlhcWbzm{0()G$0@l>KI_|CH z^GQX7-!7Pp234GwV-?PNptgC=pbL~ z#Q!ZyJM)~yVzCY1<5Y_lzXA>N0xUCwIV1rH7(~vr`p&+Ob*QpF4upANJF}&9lFj+C zd3YE{K{a+zGGSnz&ukoz>K!MDNVnZ}B;`{u25Q19!6%eH49eZU*r){{DSq8NQkf)8qYFzeUX9E@@#Nv};PpMz`@oBke4aw`xnCtp z^t3r+e?&8uEt7i5&YnN}X5ec(;T(b0+qUrIH_m1z9mAM5EHx+Q`_!(Esn%gVHP&GY zuR|Pb#-(B{2lIu-m8BWDIXE{&59h-0;4{oJ5Ah`c9gP9x z!$1cgLZjw}TmYUrb;%Yhp+mbjuzvW>Ad>1Mb6Pf>hMaprmh4n_$nd|QT=WAvmLdig zs`%X3xyC6sjJod-mK_N_~n z6BC=W_tuI@jHSz#86fRn#;-jfHHa{KI#G4c7IXG?c80U&nN3>pQ&0wOvUdY@G!jJK z{H4g;tg5Q3k99T(%$+;eN^4uCmuF`$N?Oy#f-n8j2CrvWIRY8Ct^w!mJl4=;23d>R zKHaZ`0{ChHCiH!D8b`H1sX}N!l3r)*tjh>Q1GCKSn3nT5!Zp_B#)Pshp7;Hp@21P+pG5Pt#s)j1@b;u`B`Z7$|mh zmS8q8Lx75KQzx#e4K4e{kFjQg8ejqVaM0TB_3PKIL)b+@VJ;KElh^0*HNf{^ok{6= zjuElVySLpyNf`8!lH?d7DF5S68b&*#0{` zu6;v~1Lx8Oq&Y3%z~;J7tj9BX3o<%o$al>#hNCAAg3kW~@q&f|$D$B4+e~4q5WoJk zufIR1@xxe-xjnVCCn7L}n%lbQrLs|vLzbxpF^RO_1*Q}z{&Yn&LU()>X*YfD5f`Cu zr(TWOg}cw(agg@g{d|eGoh!Ta2^;-m_YWd-bslOD_Zk*^4$3RP~3|A50EJj$(MSFX@eP|12HBqZ#tbh5K6 zLG~*bxK>PET^*y_i;*L1C23EYekv}efVY*FmbO~ANMB!{KnH`rMov;(I-%#WLG}3P zlRX1v&6+jCJ++IVcV&p)>U9YL{w`Q8M55rV-;X5pnlt^5eKA+Qefx&@R6cfWC8Y)L zXWw8hH8L~Yb#S=1?qHm!d6Dy>;oA@Vk$ibW|7Tzg2M}h+Ax;KNemoWyj&nZO0F5Z& z^3FG_5?;ZHb`WfVR#aV9lhu)^1T_sT3k}Udp{GR-&JZT=Z3%IH;Td|RW7nI@je}|(N2X!2J=)^=aY3Z%R)u1N@K?5L!HN9@#I*KS)Wlavv!$%F05^tAkS`&DSQ;9kuic~NK$N@D7@TERV6%@e=M-CVAPg?R%fS%KR} zf!CB=Z+-Rx+&7W_hUd={wcCCk->!WObpsqlBi#nxsm!>%Rk18EG-p}1i4*iEzKo0v za&SyxRey#U*11UiB)1|Z5jYbX27Y+qpf^9E7_e?C-~-}EA0}U%<25_OyV+3!XU0*h zJT|e**REXvyTusQm0!-6LN^}FV<^XdmqxCK0SYHO6!+A0#P0PyStWCv(fy7>7s95|g!L8th8Q^sb;XM{)i|;V-Y`jwh zussf+oPYN9JIA8=Ui)uczkUaqC7`$Mh>1VYQhi>1dUAZw42XaUa0C91TM&~hK=8M+ z#o-{KRiwjxk}EUpXlt4#VueTUw_nvpXN-2Sv85x&P8s$6enc~P?{TOO3F~tkL6s9`; zpWX%PDV<(jUqFaJ+fgfSQu|ZQ`}bc528>Q9zeKD2$HzlPTzN&{1_on%xHW)BW27DN zn}Y6~J4b_*gH>o?W+A+c=60k6?~xyQYbOh!S#U~^zQW}pU5<5P2fN2~Me{07VxD!8 z&$1GsyNrefnNtOd;2+JiGX$RX^Yx{BRb72IVQgU61yvV~j`yQII(&7t)i_B9aduNE zgm7}&+cyUtzy)AmDWe|(Erl;q1T#eZw9L%KX7jcc19_B&>{ROwyyb|i6Y;3E4qdjH z8|Q7VLl+17mlqL8OaNrJj-s?Q1`X`+<{4xs9rV#-h&U*Jay_Rv!9ZqkYT}H<(R@yN zdKVs%Lc2D4(&xS6$-=_&E?%7n(pM?+gEciZrvay9t`!G30mWFU?1F-!P<8NGSXfYo zECdB*EEXcEky8?dgsu!h>SGfzA_$p$P@|WUSmvtgghzZ~Bycp%5PMct;WsuOv4q-| z`54l7M-Y_qw+TuFA+u9Z+e-i_jpFnq8kO+Z(TZi;w?WrS}SJ9{}e-=`Vbr4$H*1qILANo81}m-6?BQ-r`iNJ`oXtDS=%>L?;x~+6n<?WOF&3SIbw3Tqp`6N!7;`rs~a1aBCJL%p*(dS zG~EI#)3Qph$9#gAJ+c##HkSquMlH! zsX$rEZdA^22bOYugAG66(lxBPdo?sFup%Nx)#HP+1JxcJa=17WXo1JX@TZiSOQEYS zQkz8y`eR|&t^`k7I!Tu#1k5#yVyj-?mAR8(l;trz{Btyg7Wh+n2R@-yR%m`M(mwx} zNGYUr4U=VIKHbmpWg@c+Q#$Ej*-jHS=I7}W)zt+Op?&0Lql{xrAiZ3~P-e>WCA`wE z!zbWted0qioDs{VTxnWBq1=XKG3!D&oB<8Ce}bF=xIg{ju30>4{%Uu}*%^5UBQV-)Jely}fCmH{vww-YC--Rwz*b;HG{Y zcbY573uV5}I$7B=Tvf*>9A_+a^b1$pHH+LuHi%pO6%NhzncbEr*_guDmz0#WPNIzF zgO^vVEE!wE<280KA)yV|DmFNXW}-hx+7!x44kqY~nLIqoy$;gPpI`Fq8J%*|WGMzS zR~0nJq5$3u1hU)_hAz6l;!TnEa`!}JplNsJ!6CIv)aIgU`60!|@hn#?z>@PchUlD0Kf2CTy7IF{$ zHiEp!Y5o|IMe>wax92RH5UcXTJTCRSYuQ1Vb!&t)|dy4sJ%-w6yy7}W=Jpf z>cZcJW8h!?vBb33F?7G^DJ!jLMOqjgAFN4%M5_q&p>pdYYMD3-+_Y=c( zN=k(XI7T5!A=-Jh=o2(JFu0DtC_pd$ zhtdmA)=EO{Y-ni&tDj=f@g;MjqN374#M63yI2$2isgIEVV;>{>$PXAOr~TR;eKS${ zXDNV50DQEY1_uXA+qJ5sD*}=dA5{H;F4uDTGmYTJdt;OINU!JvA@@?QI}nhFbU+H* zkyliBocuYGOng^f6h>#gOW5QibYkh6&6)AZ=Wlg{@WP>-MPF|67^!aDyg3d@rRgNh(aV5Wa8{hc z>4rw_p@!oq8PE?~n67x{+g{9?IMNq<2u&kKR@REJq+!%8GwCwDNpsp`Zj_u{eHnArx@!q91TjL z0j-xPcO~RyPFcPO0!z*kKRR(dbYIdrDQVfKwk`MXmZ>nz) zn&pPCU7v?7N9XfNl@k2HE+HzILQlXpj|R19De#1K=%SMj4fZn*Cg zmJ>$+x*~5^+j@CAg(bxY9h5uSdPlCzdrUeyxS$0A40?zCOJcZ)&^7$zL*P*Tn zxqW*n=0Nd5^HGeRPuchlnd7k#4=_(=WM`M-=K=Hw4G%jsK5<%vv~DNv!dnmlNtJ@q z3+5_29UNq#IuAt7)q;wZ;*B-Bx;hiNxV`Up@6yaGPyx`nx(@y1{@l>e1`>(tCefSD zKfi@I=h@bsCFYmulM5LcmGQOb;QM^Mgc%kTZWZbw`W@mCm`livBBl%libYOR%c$xRsR*-o(5#%Lwg1Lg!UDl8}^f5T-*wDOxJ;@jj23|WfMAksI{BcFY2O;XeC&9{OZ5J z9Dk3Bd3~g^Hng;}a01~gddp9F` zIn3n%eoZlE&WhpS?v)(Y%}o;yJU%7H#baeD{ge63{L4ZB2B&1_dg=zaJwzycKZZ|P^0;%yDKMLG)C zV`U9fxu8qBurrkbwJHk!ldV0Jw5KIfN zUd@Qjdm$;wGW_R*A-$B<<)D%PZ&gC03FY^Qn%$qGt#SCnMYixq%We;EZJceTuxk8@ z0dD}1Tx@bxEPcxq&D&GQn%;;jU8kjB7(y_(6Q$@cesw%@zkbmaa42b@{?pZuTBmY{j@mZBPLPC znjmpEb;DiEMcQn6U;h-`ik3h-j7c01OZ#v{mErUm#3Ql#5ChN&mnVJv^-`Ff9Wj4^ z4gp4<1*59Ii{e)GbyO8fD48^HU_YQ5_&SQ;Gy~C{9BcJ7_jCqFHrY09DF7c91S~DS zH7>oZIE*XW#78&yeZjUaGCz*mej>`rEyU<%*t`BPsrPdt`_B zA7b|bOiCc#bGjnLj8I?)!2*hh<7S31rI}7ZK;SzLEbyg_O-z=bdiml-3En3VHsQ-L zd?9WI%L?8n5>6@P@szp74{Mhfd1VERFel-pKlax?bP^Qk51pG@$Zqd-#4u#SgN=W`hh?^Vu46l6&;HW?8Jg6xEu@u zX#ihhe=|arbuK(yGvL=gHMQ-|&Ku!jMzBs^US5*@;raOm1ua0@o#n77F(cpQ=PGjW zqA-Xv91!wc>pC4+?sQdw^JX4y~V(7cZEgB&cyV7U(kExsx8I z`5_3#LW+vZNVviAI0$-x{U>=Ar1!XD+OdJ=U3>Q~L@+Quc5EKjRslqWC*I#@0jnq5 zUczR2nP%PA9Vu0KZ9jCe>8Latoc2~(&1ojjeqhrFc_3R%)2ob;HlfAdm`_POn81pI z=l(lSG@e;tAH9eIkLvk&x2k(as(37AB_{I~Hg6_O4qjqnpW1`pGlAdKceFgn=$FT9 zMMW9VB`HB)JqVvO5Y|4u0muxK`rNQyuPeGa-W?I~IGSmg)Tozwz@^;PPurwWYdu%` z93UdTHexVyu=UVu6>W>*8L!Kpir?wj!iHNfoLWpCld!PDrAwEA3&U7B3of!jHBwdBNTA(M|-#%exk3(NQ7uwgQ zRMr|A<`rldzs2mO2oEplUQl%2{0zrCGMGy?KUDiH$cRIMSe^(2e=W~bR7`f|@dBUt zJ^Kn?2h$0i!&>}}$Pz}2e-#ssa65$+D|WO^A2X}2u73Gt@a2#vTp4)|M*@um@1zC2 zydCcDTTl>C(AvMf*@+eJ%ikFX9t#udLV6sW1@03Zlr(U0vVDty0-9J`8^oFs#F6@? zSzdv(luJ3qh@OVRPdO2CE`Q$z>f2KQ@dO}{fgzwO%_qf}Tn4Jg7G!mY#4K~HpoicV5ebmhtx z#8_z=nS)PadX>#!82@moAO%jYfvEYejgu6>j+}bZva%{zC6pHAucRhdYqUj0(_RnW zCf1^aT@ku-z-#9|RHCfi5BWcLS!W~RID$v2*0>)LjXPtxaLq#yfu3jM|7XSei_ixevk5Hy7>ohY?Cp_qEsK zEc&2^&-?pJ+XWw1bG|W;E%O;V^^J#fS5&;_s%T*C+VtmvM8QD*oDiePQ$Ihyu3wMX z*dwm{`OU|SQ1Ki@vsg#Ly6ZS6OB)*tR20x8+HU~KIRj98StU;rL!kCp-}g9?B%4`T zvgr>WdV0aj&#tbdga*2(*<|zazT4tmF7Kt=^Mgc3!NbcGAf6&~%Z(tVz2G zqyQ+4Vro7kFt@(F>m_E4eqmMVT<3Jm%d^CU} zbDN2NLs0`bv}eHN%D?VzRVWobI^R7O9v$J+5YTz9d-OElPr;)Je{PkEO2>1;--G}x zT@kWjB-g128VMbjjg69|%}gPQFBI5ZqkP=l^rVSNCSeVaV}(ZEil0PoRE`T_E*B9M zHN$Ae-Fx=XP>}X80Q-`7?@7p%WFI1IsfFH>5))%uu`?L2#!z;aq=|d??&YiiJBN)7 zt7#a(2?1bLI^D*?;zH`vUuu1xe_thizJ|B|hcn&l+1@xFWuRO)7ZJYWTlnYv#RrbH zR}~I#!2$0Z8OZ~LnLw+kaEwdOGXWO&0hkOUTz?R=z5>pLck97}@b#hR2vFHKa1+Wi zqtmd#E-Nozjtvg1SoOdGHu&4q;Jj%pa9NDzq$+6uD(~OF2+h4Ud-v^onbq9eYdr-; zj)h&_03zrSkcnnUMQW@o+li(i$hRsM7J%NZ9_plF^65MFO zi<^xM594N188tYSe6zE&YxMA>k<9Urj&`Ruo-jA}L)VNNPGJdI@BHT)g=5`0wz17S zD!NTu3zanz%I#WO91?5So{hTn>C3n^Px6x5;+|IGXSLlhBOzn%l`1wN=y zsF-Xlv&s>#UD~5>-X^AjZgX4FjT`wq#hYYOAa#>*b646b%gevwStBNHLs0^yb`YX_ z9C5h2-;C1)c?%tUk$&?RSP^vfjvRUR?j1YYN81p|0viW#M4UzhAsZo6$8Ig0RSF|n?}~B3D#S-9WZqM&N(f}-yfU$o%YAmKb#bh z?XzT^tYyXiC7fOK*p0d?4d6pukq(Yu8ra_rgvNuxYda?iW1CmbN?Y?i0^z@W@hzQt zxC4C_0AHKmf@qs}?qkP0SYNQg2s%gM`oG@jL~FYrk6HB(A2x-aAYWt03d9}GNE>VG zx6mstv!M0?|Lek4v`4-}3?e>@#6Jz_bGjmwydd@VNXI3Mj!d0V@pPLfPwyT)5;vWG z;rUmHLI9Qm(^ALQ6#X#;<0qwuscx3Kg1yHG2zt>pzY1}GKKE42m zaD)1g_HpNhqA)apkHa`XZt(VLp(`QU3RDyo5cv9&5m+)EYA-5^SzS34^|Vt$-fgvc zcXrd<&8JZCy~^MY}TdT9j4-?u5k z;m+caVnRdr-<8VPBZurMH$T6$x|#)BO)u*NI|B9no-Fa(q+duR0S&8Gt-6k3rwF0M z(_;G|VOaq441ldF%oorOXaR@S{Kk3`ak&;#!ClqOze|4h@cZbaU*(ub18jw(fn*gx zyn`A;Aszu!gB~{#4XYJEj{AKt0&;*AGCd(1;HPD~aqKTfBsSfwppf_6MQ0`a#W*M% zf6VRI#6bN5=XFb$EyG}h1>b>+_IP3T=Q0bOb<;4Mw5Jn8B=!SG;8{t@wk{=*ww2Lr zkVb!;82;{s(#~Eto33xB;=f5-p4ag|25qM&)H>p1~fU$c51Eh|s!5y)Z6F z3H$yir&Lr)p_VvRpKBMFYi((%e{a!g%%{6VC}enue)OJn%D92#48kkzGT>Rf*cS{6 z%MY`#ll28>XpkGWHg>1DG}SY62w4Sx6~|}iYh0vmzGE0|JVeQ zm2wZvrqz5f^^pm5{m{E7xHEC^$il-scyM_5ytcSb9YH8b3)cfKW9^T4QYyg{08Km%aHubVlnWa4+lLGe$EH~cu#~85^Oe`%)eNGS|;2PzC64y+bx5vfC zmYrF9WQ=f^ra=Aq#C`w%eK>KigF|Wvb`ST&M!;p|Sb3xXAV(V{TG+L_2#W-;JzjrX zoF}GH`2&ceqLn23A5>=62ceQz$=&(x2u@6nH-5ca-v}b|0%6z=sG5jQu)<40IRWF5 z=O;lS#rJ_BdWmQs_WedxgoUHn6AfsEnjq~l`bN6z4~;D?#lAt;sBKkArmU8=>FMb) zVjHOTuK?6W5rPskV*7>sF$dS{Y||gbzIF{uZ`1oAZrf4bJakQJ=6*vvd5XJNfQdt< ztcOKMKkCumt&Wzc9~3MvKHAQ=Ma^D{p^4|Q;quNVqu-zpbSk9ElZFu(PAQHku2FV$ z^?;xGrvn7zZx$t`ZV_Rl2NGFR?w5v!BXOP!YTDfm;FKwien}fy?-_5frVWDS)~#bF zP{8U{WM403_7NhDRM01MT%%Ffo`;s*%Dow0Rq1Q%{Pu0t7SAcYHqQmH5#XNW zSFk^pJ}D-t+BpPWoBf*@A9+PZMH7$$`A4XFCu~a}6=6*m0mNgBOd5hz0}oBFF#$Y! zk-|y8DUcoQLkW#u8X-6$4+jVnm8>hNtzh|Y`Xw$^qF*v8+#Y|yl1L z8{Q(kg#p^kF`r#v&;T-%q+|jJh`<}-9Ula$4-LF=O8B<+f)xGb*q@$6?#ixt1IHby z@d(@H6G~|gEEJ#-^C(+fhUS4Y0dO#Q=d=L2N@%^2S|S$>YF79;o(J_72!HtqV##?p z0haPt;XfeHF?GYT=U+ApGBnKDQTk!v)i6gB3eZYaaAl)}{{!W{UpnE&z~8296m(Yt zuZ{9fg6RWB2dY6(2o|BlB4kNqgQj}{Wv<#E9SrkdRU5A*8?2SFs|Vo`Wp@;WIs$2u zwM!JvwMOVjp))}O<%iKD?9klB?vGKzm0Uzg30E3A09}LG6bdr4aU8zsDBB`D3Oy#d zap^E7^WE5X=jl@=w6};Cd|n+-GlC(yM{V$o(yDJpaold9Gxa?#G|)v#P98`AX`xWa zap=(ffeFwv_C1>!+Cvwys<=+(>?HAB2d#31Npdivg*cZrh|_$zq$p}G^fTsT{`H9` zXdtx?-jcP{O0KM`s=9n@nF)Hfs{K^0(F2*qwF~y&zsPia)uUM_k>fL~@S4d7#D6W^ zXlL7Yen;pcrp*HboUDpUE6WN)-k(+5G=e(+#YY=F%Tn0vwVQ$$Dv6>?mm(zpn^1WDQ`fWw2} zRKbQdBw7IyMBH12mxoS|KAx&H#~meF;RPL^bM4I5f#|D@K~xuN44yrEHs8a;W8G84 zjZ&n$3(A*)mG!6@mDK`ZSK_a)nReK$QUr##O*ov9m@5DHR=p(eGkGnXcRXfT1FLt( zYsGtb!K29D1EdOO6aZmCOp6xr;HWR-1@wUqyOxO;B29XuwI`}Xk*BlsU%ZC?sbKxT z>Ywt7{>eqjf8IG@f8q8hYqA4{i8!9K44oUCgyQ?SCgzH>WPVbd;U}006$$aTd=#2C zwGn4HSQFk^!$jtWw?ziDQ(pS5I@IsFQi`hB%s)a@rS+EPvD z?lPqA(D07MDEb#i=IiRx$b^|9k0JFf&OeoF#jfSyD0iP8sWp9qR_#WFlEz>Y3OWAC z(gr@l|4{*o5D+TnQ-=E*c+k7Se69;B!r?(@oSZ7bm;Bn@%`9Y_^R%Uno(C+2CCkajS!s0;ic668M3zgaqgUz!!_* zuT(w?g&I-n5TT0XBXV2Lc5|Wzo+o7M_!9PLdL}V2WZ{lP?RWuX5U!Dau04Rchlhtr zo&nw>Hy%^#2E<=-WA)(hmfMfcC;uBqBKw*}-=BA`?+*buVM5R4TM={&UI!6jm!q8u ziOtH(T7pw7?MH>z#@x7}pCp(XyN4<0LQ*u}ZHb1~eXgu*PkH~=t|Se3lWm{K`?@T; zByIZH>E0LXcF#|0+>j(xo-SG`Z9OY}J3uimZsA!o!o_J}#O5G)7m!wFv$boXE#uYw zhD#(L)VqL&WT=-XX>){-5$8#n9eQ|J#PHN6T*v=WrF2Zn?I^*)?K@B7AG;c0dcr?`4i!b%RoC-~$UYhZ_&oG-S;xZg^#kDz4jjyBJL5jx6%Brhy zCl0NfaVX8pM@G5V4eZ$X$fc&En_h4 zp%_Bw@=?SXJLq7xLnLO`YL7B4qM2zeo+T%lm*wCFT#pdGa(a7i;QoDpb_fCoXom=w zc|>j7U#F=O>&*S_8%{N@mV}Bl^7{^{eFPCl5@HRD(M;{oL+s^+{y>nQhduQ}8TX@K z8zr2N>!)!L%)fHEZ610=_M+gs4auS#!5hb=2 z78DTLZenm9_~5x)?AxDjmkAeZ{rdGB6nC@~1mX+miE;&oN3p=YzyoKbW_?WWUPBzm zx(e6h9$*(Ogo@sj*kf<>_$3R^9pOg{n3*px*%BMz?M=zof|$xJUVRn!Rh;FMc=DVosA?;f+BJuF>$rEPxFu_W ziFF>bfUi-BU>LSV7(cxi<5i?!cPZc>~yH}>Id*t`7=ciK3LJHg462r#<)YC0K z``b7E-SfrfF^ad%V+>=~nsWRpFEMPWx3jM66EdqZbIi6183jv<2hlR#VG^fRc?aI@ z#O)wxjRBPiQi@4*VCe;jwFFz)5Bpje%}iX4u3jhXTiXzBsI>Ej&!3B-uC*~JQ8(tP z#L{Aiz!;O%QGAmgPl1&VnxE;%UR)L)OL}2_4hH~vNorSr2vQZNiBom@Uu8X}(D%eR zFBzi5!_kEkM;T&-z%Y*qj8Wf}y$(@%aU{Bf3v5CI11~Q*@D`GekV{`c(1-L<5X_sa z^UWR$I(o@{SEAnc2_8b{p3uV_m){O7 znjN5ft=#4K$rd{tT&zUxP{MRaEYq}pf)cnXn(pR;z&$}KbU$~7F)MtQ7vc5rO-O8> zQ>kBXec#4qsC1MMr^`FhU4uHm4yDOuukGk{ShIQa641QZ8<55E_tnBOE4~2~Y2XND zqc}MINxJ(XwU-hyrT*k)4WyF57t7TB2vhj0ap2aOaXQeCC=^hoLT1HkQR$FbWUF)T z0eNNT;Go55Gz4WkAwT3=V4I--uGy{>^mCl6AlMa&WMi@S{;kv6aW{r3&lMv%6+OB3 zKqmFE;`!^qF+~3D4L=#^I}Xaz#mDCQcu>3n2Rn<600$5GCBd{W<`BU4-^w2E(l?~x zE)FWL6_qAuQg}IZq^jsoC9lJ}bm}P69^7!E`Xg!G&Pnq*Hl?W>90xEnBt(el)TZDDrtjQ+Jd5`R2;< zyS;-cs{=RfI6Bj7mvQXP!=xlJbT;I=!4B0hfJGlP#P)B#A`J`j6$6NK@ZOf79q&7J zJgsMpnN>3vAmb0;LS1n0K;`Vy9{YnJWRde4e9yfk< z(OuDZJ067vJ=qiaqr`W%)I@Fn(%BihqX|1nXpJ~<*zxe-=u7b?EC^lQYX(Q&ONCJt zC+h0&U%R}F(SIZaoR;`35YGX?2a=B!!OTLdW^uz!!|g|T2uA|K>Z*sEoqqu1Ur)Vp zTkF*A>EdU{bwtVw8}qEKPo0uPVcC&`J*{VOEh;-Bi&Ml&=bsD70qoX}3X&78Vg* z6`9;wz971-hEeZKdB3zT*re;$`R39<$_^GWYK z#80A*1}Z8gd#3|{!Z<8G13w<8uAp;1p+>+d(gliur*icuX|KxYT#q6b)Br)jg9qPl z_vLGX-J0D55<^%RY-7rhIIN@^uy&l-HpP}gl~6%dC1`t+e0VN6M2G@^q6{hZDj<{m z%(`%)#HjgCV)Fu=aL4}rBky0neai;T3=NUE!NX{~kx`Kzc2#R%lki&6`3V_DcPq{h zgnzB2Ock(_-4XTYEgRe$_ZnB?ltq@PCODAI0Lw6I<&#Gpz+Dkkq!<&p&?U!v>r_K0?n6 zFLJt{A`8O=3_ZxIHa|Edj&S)W38`AlI`b)=go7Z9-4kP zl`tmlx`zVsW?Vz#m+uaPlKItN9?s#=i5wX+**lyj`}r3ozm5DJchO_f=bQD{>@7Nc z5rA^tA^Hdz)#MR9!UHeFr+O|)HE{6)C>+!`RX2R9F?YG4?qPIGalP+&HZI=I9Veu8ErL?H% zv9K!vd`W}H?2ZQTIBM{gzP`2clk=#XHA2elXvKD;*hNg*M(n$3nTADvjX&>*TVJ(z zjAWsb01W=`IsJkk9v(T=Q@J(?9V)^svqsKEL6wD=bh0)uGZaMFg@F5{w3=_eO6101 z=&|^xy&`!eO+)WRLW^}+3P2-zLK@Z>53{dN3Fb86ZXPe5D*Z%IB1_YhUr2eySpP!Sw=TKS!{|hU%(~JHCK5#~BG7PdZee5rdHX?*Qafbv;z+7bAFr z6mKBeW{4Rg`?I_uZQE|Nbs+sO62AGVITAh_q?$}reiv9+D^8{8XRg9t7tL|PI>mZE z+30lQ#A=u?Jz8%;#8e+2qsMs(+$Qco%&1}>9v)IMh0i`GG6UJy=i13cO@pS)0C67$*QTBe3y);ktW3i#3Mv>PdT9Eq{{uAO@2 z()tf9pd`Xl|A;kArXz`!QR!zaaf0PpO+GLz++^|U`tqFPxKdDFSHKym!}GsM^u(ay zb2J(dVD-^Z(2U@fJrP!~R8y+yyx zea#cp3}8vqQIO4nurb(DLg$xGnAxy>FB=8uQg(xD0ZHhvv{-?VQe0$O$zZ(evX&f} zs;cwtDh^W^sF2ETBdi8@jl?tYvnQq9$iPI7es z+k#Pt4(Rc?|M(J@8kfokC9Hd*)A{M-CgCQ7(v#DjyoFXG+h1=jL2r-v2~d?d!ery> zXjNO^G`Tyr-92K4OR})weAWZm>q~CLc|06N&Z^VAO$0-x*5Cd=GU(M@z@WF1s#Em% z<$y@Qjtb#|5aV^ZliaY(3j&Bpq(ta=JO>>VJrFx0&;#EMZ99;dFQC`A(`9mUl1#Rw zc$34cXL7iUPF?+_X>>@99aJ6e#A?e86N2p{*F=sWw66e?OxTlZYRr&g+hOjT-8W@( zLWm`4GJ!jH33{9d68NmDiWw3@0;^z#OSv9a8MtIeEsfwASAuP02mfS52lxjtp{Azx z^T4c$1qXo$27p_fYAOhT2{c+b^gu1e8J-cHF|w|;SxRfxs7vm2)Gc$z!R&|m%2j6$ zG$AaSpteV0#DHuGjBI$&1d@G@j+S3`wj9={57feW+x7xMw_=b)fwc&?0W?T?kk*(f zdQaUIP)YlPRZI+*U_YT`ZEa2PH^}N10%HdVC5?F-vQHP`70Zd*7pMw&3@xC6?}RH% z_?0WZ$PdjhQXLi>H+Z)~bplOWmjCfU)I1>FLSv53A+>~`%e2_L{FSwt&@Eu1oJXFF z{=K+>hl_##O!w6vZ+Lc{kGZ&zdrM~&+u=8xgg<*_8~wQLw0*8<52puO49hmg&J0@qfGXgME;clI>;8X9c{NmlPa zHM2)d%gDwi?>>@x^3$3Ksv>wk1VbTIq*7^S&NQdv=pWM@rtOb%t<4`?{9NWhQC2*8 zkhHKwYu1`_6l0t}k-GJAEy7bjP8 zV3=zI402nGC)#H#H zbk}9rL8OL@Po)FH+8yS^#5x0WFjMWCu{0}i=Lix2Pc>qgE#B7G+e~=_7&&EG6Qs1$CJ1s^VvO#r$ zw&v&h`dHAJpjBTC^h3ST3m7gT-vK=#D6fdfd+0oQ_} zdCP^93$$783)K*BAYpT=J?ID0{tEez_E%9ahYlsFGp22#|CYb=8Jg6L{*rj7CC&?H zv}!G>8RCEap~*K#bTd@blifS=s^wT6x7ZCW+2C=VTCxB7QR+W6`P!&@(wqC~?sYyb zFKkaqw0IDju(q=2FP7?D0Q~)@xY-;BGV}WZhD*!3i`hN%HK^W_fPL-&NF&yKBK>3n zEEtH_xQW{fzN-GBqa>c7epKoPO}xC>WoRW1Zmm{NoOjt_EYwSS_ZQ&7&J_~ik2#6=h!_nU01y09|XgH-1%@x@L)9OpI$S#r6%JF{#wfB zvCUxC&*;pD%nju{p4DfsFZ;)j|MhLed^#$>jW65M$M@@zmSYKv3O4H)2)->5AZf>( zN7!FK{rj5;E~@jCtH=7Kg-U#ndSw6edguPN`yx;h3VYIHTQzCoKQ?)P-t3d1n#0EF zXPW+%o0dF`XP8KP!s5fcZ}ivjS9^2B*PwIvzR#J4&fU$w|J3fH@}PeR^|062Umg_d zc~`WiIf_|N?)fCukM8fEock^!`W$)aBfseP(@&w%X8QBPtSc*9ye|IlB7H-^c&Xs?~5=$kG7f) zr8@X#G0eU2S3Ap;M&Rpz|7be&)CI!CH~5^(K0J(lk?ar3%3r^2IREoPVJSvcdKZ5x zBkZToH$T(71PB=N;xkJM@2`wl!9%jT&Hr za@VG1}--ec`)@1GzV=vr{_rC;%10)mnS)nzP|-@ z^U=Z7G?+yY5Dlq!adWdEYJXqUy3VI|h49DsaIR52`^F7%A9||AXw?#G6Qo*kJy*%D zOkN2+cIZf8|DWBM?7EOF&);8SpU`MSk8rzqME52xt>vJfpv6MuLI8QwkW+*IYzj=A z*o!@Szamz=8(yM-O6lNp!M=Dg9fc%*1Qfz}0A^fMyz3O86%#azU|+WpX=PYMgdZWw zf{um0#P_<)1dU0MPs>4uJ`Ke*LC^{P3Wi)^aNmJPtTrqHK)5Evw|B5`CK_14@T#xJ z&_5+_3}(4E>GgrqjUOXADB`OHzA^Awo;)K^3cV3Dis$70{)t0h|6-SlbN?&DEzo}F zyPrxcR*Y>UtSYx8|sxtsc`UD1IE#?dq8McR#BOU6o`g`n@8N}@dWRDypaGbK}!BdBj7)f z)(O{{@F75iq!c3?dq?`e7}*s^^S!?RaZ9r>%H$I@_n06LF)=pwMomuy!^qYtL-f{@ z6BA#d8m~l2`|%ruYzD7xD;%`$>FOeut>}(z0+YcXTtzwxGFS#FDJjxWM%t&_AjNKZ zX+*6KvM|rzic8^S{<-73_EjR&D;9}&uM+os4f&7bSUX9=uz(jUgRsf556~|n_J+kZ zHCnYv;j};lRDo~+NG&7YMrBCG9jxjVEp|G)d0PGNa8hVhiN- zJ>fW;M^hz|?K)zWM^ld-v;*P<;?;$!j?fa(%JVz7t*JnkG`LZ#JJh7!y}KOqB78u2 znGfV+*WttbMDUHhgkI@lm@}_J(LRVO^ckFQm799o;9#%~Msih8LzgP}5Km`or{2%( zWdD)vh|WAH9md#rt$UwA9ZiKLek!YIH>FXHuYY&@1gH`Pa-3(~K8C-vo!g!qq)DOw zbL=K+@ktv~z>Ch_v|>$!Bp|M-v7*_CBTg7rv&?*liq0Oa0dA=oCm(;54S-DeQCsrV zV%3?!m&cy27#1JFgef>aj;3hDk{`YQUyWJhhKHHGj+$Cp20*WE0!$CRM~XLEM0m5q zmXK+*09)la&VrIzfw+Jp4GLHXP6Kp?tKmnp;?ZUdRWi8cpD1ixs5SW4!dNOk)iQwn z{=JtQCjC86_UouLw}??~Ls+7*9O^$|At4GuB;5>&Y_rlC1)EwMCKQ%!3{as(uP0&}@U(U?5M|Hw z{+I`7;+CM+wQkNotA1({y^69noMdVSv)ICBAi@#-YD9$2-M$R9UrKjh0rd98J|WaC zNEy7LIYgfYEER7+hNhs(k|H>KQY5y9XX#Q{b%4j93KYUs?(g71t(FcaYfC=x9lt zzKpmkB>Pz_0TS}cpSn#^?y3d`LB`*)fe-yyBZ74QfQV?;Nl{APz0*X=no+6*{pdUh zy4P(0*`;j~{=1_6kAeDaUk8H(&d1<2ocv(B?dI)J)X&TaoTiNk*gB&VU|$5KV8{qOyPN+$1#1f(RLfEG*ctIg@of) zk>ze)nE9?#K|J|Rd`yn0{|QRNa=4c$TN1O5#b#(-Cd!_|5oSM4?BMazYHa|C)>cGs zc6#)7v~|ls&xfQH{cZ4ed)x1O(a7UArS~A|l)C+tztF~Go&J{<%Gv(vs zBL+1d0BcQ)kT@OW=i%gBSpjCrZZLFo>IQHsudPsD$+3LXru^M5mOW>yUE_zS&cf_) zfKDeIN|9&(G=Dxcexv9olh1+s@^dM2az4;h@Ve5-!97A^8Ay*H@6u4vL*E8L81kez z%_8)`FF=-!CgiF|nVFe8pxyY|)<%a1W;YGZ^j8qRiD6QA<-Zan!<@vv|KB>kAJ4A* zT;#PCcq0^Itl$U{Iy`JNNzcg)eNSnZp<^KyDqoOHxx#1<(K?`q2$nDkiO0~;kVsH+ zXVA$dFbZ~GTuyUfKNitT%2;z3x+D*cST6d+S|SWho8>g@i5@a-gy7~wS=x3Ljb;1oDRa1`5zn?da5LDfn* zP>|CkdJIlUCD`#qmWM$CWh1Pttlt~+WQfNBmZpCX2oK?yOpvi(So{d^)yT=H8n}jJ z6E9hH1JCgv%b`|2(A!3ctx#o^D|uR3iE97^hs^Y1?_^x>z#|`|AwrT!9l#w-?0t-T zFD)<65H_~?D=cb6US0JcQPaoq??PqK&gE6?e3(OHV+np5QqGs{dPv#q1%ufla?IXe zyL;Dg#TE~@WnXsH?gWAFAJy?wI;E`9LAdqL7Hw5QjK}o6FUx?vod=ZLc;_xQ zdMhy13M4cTefR*x{tCrE#BP^+-k{~N)IAG8qQsG$y&XLHlr&6MvC9Lim<&v)FHk4L z!a|6(3f={v_cmo^3RWmR`ljo?0XO0WH+np@9z#cpuZkU%?e+q3lj4oHFu}=d@4Jk^ zL!6xCjG&`nyfCrvFD)-uH8CQ<5|Hhc*uDtmWL(HQh$-`O2@QZ;EQv?0+H|hynzoWDdMq4(caizyM2C3il$z3tI8^t@-GmxP#vSU)CE2)_}*zU?t?3 zT!X=EnwC#MS~Vc1c2;ASFnC2UDl0KUvbK6Szg8PLFwnOkhbchDP;k(o*k~Oj>VZ{c zMG85XyA*)Vzw}OmxW+ZYL4Is2HKQaxu6Nn>fetg8o*U59gW1#2pE#53t>$7es3jV{ zdmcVHamP1zglUsoU}gN!-SrwTrFxn=C;h&sYA}r{HA^uD9cGIDslhmE)W-ve{Oup0 zG0=75{jl4Z-}R*T=l!oeJn#QaW47sV4u|P|jm0$sUuw?=no9yQ@7HQ024OfmI4f2G zf5#yw;b3`IwV^kBwGxoLG8L&&ah}L0T;mkMAz&+~fi=mh;yZ*0U_9-tDJA(oP zIY1#T8%Iv{+(vNY={vdApFZi5Za~dbX2YG!ykR|*D66z-)8YQ1dRSs;@nzz-ANR0mh4|$06FPMysi{rJIak%ZBjcv5rEotc zvGke>dZBS{KOlnK8nh}*m9UFJ`z%twNR@Q2@_xEuj)k8cvJU$R^qNk7@PeaRZfI(% zZMgmWPd)j<$TV~~sUs(ZNYcR!RJ2}~efAPT^kS3a*L!Yw#2_p_7X57}s1tnqn++M7 zq5_$e_8oXr&$-zBX5yDf0&XPcYVE(cqWjsRaQAk`AG4Xh0r#|43M%*{;&kcmI{js3 z;tH^wIHcqrLF5PxKIhH8H8Jt=BE*(0=MCw4W$vl3uP3NLtAttLb>!{fU6{JzkJi2R z6UffmC4D6~t49kH|M!iXBOg9oi%|hd8wOCAY{y|+sIYNkVxKx6LSNpQTv)UrxG+!% zKL-9g>Ww*YZ^LP{tL>?cjSV{n);hQ$lSnr%p-qQ2&Z`YfD8E*a9z`1;wmjCu@PKS z;7cqm_E4AUUuV%&ysT{+MR6!%o5Le(z<0W|RYM&6VAk zj1EsHuZU11exG2Pn4%>~8mv?uI(!I-%RX1X=q=llEm;w^aA!tp)Nx9Oo!UhO_m51Z zL0&={V^pjv(KMOUw`c*kk4nToN8Kio=8TbG@6l}lW0&Z4XXw>f3%a?XDfShE^CYA`groj&vg ziU#GrJb?|?ylDSR=9ml4x@UwoqimJSUpNLqnX!chJ%t#r!e3NQJ)c$q&^E{7#Wd|6 z6OUeW^nnH^F1Z!z6V$y@oGUiBYclhxKd_Xg?PkCfuoY7jcUl(gzrEE<0DIT;h$9;#yN*N#o%h2B z4`iF(puRWI9dX*OdC3Uo(BHc(M?d=J6Yq>bSR@6|ZNr>|-;P6H!IGp_fjcS@x2%NkM1E-AJDWgb|NoRklq z&hh(TnR#Hcxmu=jd30?2e9-i@f!2eJ)@-Vse0-J^-E^ z#N3i(`ycktyt+1VjKL%kR@IJT%_dC2lJUfJqX8#mqoRKq`}uF|1CW1UOEoVM6OPZUGUldGjFYe%N5I(vJA zu|DDu(#6}D1=Qc)4!S%*6o-Hn0{Ym&7P}Di61lm4dhbV3W#Oi1%3=RfL#XJhu4s3sRQM0GP z*7!~dj}MnVtYFa45hjk6a`mae)6^bgxD+rh|2`s){5OAn|43$()+wbWoWukl*)yfx z<8F6XSJJz|qXq`2;i?>Bs$Nx7!-3~UL?D=@!=MF57GaoyVX19fs(Te+$mPauUs7gvEAQy>9NV@$8Bsvu(sNe*}zs$bboXi zB$AZ&sP||MQf3WLeVNlb!jo^`-5X=fuk#buf?_caP7WweMu}1JIeV_GvcgEOu>is! ze>tDwA)99TWn!Xq@|Rnp@qd^oLCzRZ83KG73^76>Kzr55poCZyY4O1Q ze_qn72~6^^PPbQAQ`5?AB02b8s27-El9ciGm19Qa0YNZkDl*SUWCJ83v(8!{I4U=p zKZc>}A2RQ`DZb{RC=7Jh%UkU>CBDAyOng@!y%1{woi{iItApVXQG$e@($3=lTyXF5 z%^OQ`Zc`wrCh#U1>PIZ5LBB9IH=i$LtGlI6N$&0m`aQTCuqlJhE)C|AD5G^QCvY9Q z5V`Di@gqvh-SV93>KFqOTi4;3jFefwUbbofhEK4|Xq+6Sv>=d2;y;1I_OZ# zRRZ89{*5S}#0oYgT0fm|;WS8i=$<0}7lD|_+5Tu_xNdZNb%?~;Z!?OO8e5zZbJFne zE%_zm@!94vdLn7-d)d(rOkdv{K6kW<%ipt-luLCe`k=k`xYi8k?SOZL4Kc0lk#tk{ z)z6~`W?tFP&wKKOxM)ydxtoqbWqEzbt2a9J-~Rk*n4>$N0N1^k-E!&+iTkp=dv(Ec zC3EO(;-|-Tj%9SJy+3nQI;Umg^`Rd%-zToJTzYPFO)b7P{+S@6KS7M2FF64*=Y37j z37OqhO*2zJ_gmLzF~N(X1T%LI-Q&~$Se?aiwdv>H(J9>y{bK>*bai~@n1UwPG=SCk z=OlrWtP+UrY0NbSXOKs^$+!e4uz0YPv&6nLpLCz<(46G@NcC-x2*f!8$LQbW+5J?k zb@%+PqOA}4DMz2qI+=~P7St6#1_4t?K6}HObPY#}qvhd?X zulM-Vejjhv(YdC3Vx#mKCx+pXpqwjDJ)1lu3=ry|dKS-t;##&|3fkYU4gbn0W)BsZ zcI&u>ixUrCV@nEQ4CujQR+xl%wA==#bUCt%pvHvcN>zOWpimt-Tm>sVEVp=aMf95g zFV@~W9P74^AHTGe7KKtoibN4fMrBJ<*&-p7y(uFUDI-cop^{m4WF#xHB9xL9ifD+k zvwyFXy6@+Ce&6H!*Vl2}b*pe)pU?R@&-eSa-g)~D;z7P2Rn6OPi9;6Gm~x42Ia-(P zP+D)4Iz`Sxa~9b)Tki-tn1&z5V;?t9Pk3~`1C4~;qamz=ED4P~0% zy?haVcGBRrRGA*%R9}{n?Jke^1%-=ndQ|-Ux^iyKaw5lNZ&+actV$X8LtYJhB5gtj(7?7K-z2 zJJf)8-rq=FO~NApc$bYK=YJ0}ATbPxT0*y0d?C1;KC!dwfIANC&^8Sm`=kl9xLwh$ zsr>SOGGM>008#r70cb>MZBgkV8g5~+`$;8qC>M|uwJjb)j zcs{dl-L@ui_};JLImJqEmu({5^8rwyo7x7ux4vnj-oCE!G9oM|Cm*xyPqSFZL;e3u zn(RC`jWjfE+jAs%`poq2LD}!f=z)@wu59Mo0NYR5L5RQK8mYeQ(o(RdYvm@^`=crgW#2ux6LXUNw zwDdbf@#2p|(G$@lpNh8|tT6sE0Hw<{7QwPq0 zGRdRIA_RjrbKN;{{#QAX37=iqe3Oc$MKw_UBF?S^WNG;rFvedfQrDpfK3fk3Jzf!} zX)Wdq4GJJ}&KRt5G(;K>ow6Pf;utG*w7Z*(Lc(|7`PSV(>Erd->dAhj$3DF{{o9rR zSZ9kfC$u3+Z)?lH60YI?uO3||MKkLk-&#wO9f`1ud6*X@Uvs1alf>lrC3;z2UCdu{ zSWHiQfhIA0GjUq@M>AE;{evslCjFkPc_@a@{Oi1p8IxauUVMX$%#0b(I$hwPX$;8> zV}yby_dh|!553^kW=xUbBq0uCnQ#BE670{lV^%aSu;0UQGwwD5veh(#Nb?hM0Eu51S2}pnOAQCBe{0v$Hm<~P?_w&;Y zGHMa%i#T|ZO`tl>l2iqj)fN=2_mo-pZ)85o)z)X+As4m7ksuP`n(H1mw!(X4R1u40m3Qo*bmyvKb$r+ z^+$@s^Cj@fdY3ni|B6qWPgsrNei7%q_ubUQj(3w|n+pn^=o;RseLZ#Z`Kxx8WlM|R zr755!xr$DHn}`S#T+0xT?7-}pK*$3~7Ci!$%aMOYWW^McaQ03&(-mQ{h$v=1|FdH? z2nI|VCwmh>H?M2gs1f~%Sz}%R49KlRk;n|8b>AqWxDVpVAqw@*Jncw&r!$x=oJ8Iz}y}LX&|(G4b0tEyt9W2mzGR4dy`{%N-bTN z1#g$s*P!Q!E|vIyfHU^!-@zXk!VK*FzGjVlGfc%FQhX)?w;ZHF)gSuhK^DnnCgvq$ z;Sb0N#5ieB6k(q*-cSQVM9P>e{58JiB(SA~Sjk%BMiBhJ9}f{_h2p zFH0%Ek;F(Y31Tyj@bY3A;4C6q`bz~`2MNhI;vOnjjM*n8q-KDoA3uispwP|n_ELm- zmNA{8B6&n!c=}6!DOiP!aDfXF2vRr0O_1R{jYoZYjtd-v@N-F!vadxDWPPN7AE<#10S@#*-4`_{>1)zZTH@xFzW zJnjU5C;(`r7EuyAOubaOa0pSQR{(ta@&kWYn`+H-_;yPlN|OOBkGoR* zfUN8=;wEyxC+Nl2;Wo@d-^$4;*FU2GT>cO?8Fq1Z>8q=hTfg8^_SLK|G*B-+_i>9Rb_;@lEY6bNgW?GVz z^Xi_MCegG$ZO4fUpzl`0tdWQDVp`+|r4Rkan~{Plpv(y!>nGSm>4>qOS=?O*fnGtc z@z!kfaSO?y!^A?p)`4+g4+a)^&aR-wTtSlUAZpr$2)LC*i{y?Rt*w~HiNT&>V$>wf z1XFgaFOnIxSTNN+>oE!vlZ#Y;!@lATaF_<$t=$YSD6@WNC7J(G&R-u@IFq9J=+W;B z^OmyE3rLZ+J*Xz($wuF&aUZ-TamC%)YrF3ByJ(6;U;S!ZE<(h-JeU@2V+s;QtrDIO#$1+_0I4)|H|xnl}Ri zir+MDioK|=p$Wdy(7h!vkdfpuH0ETF6#U+a!yVaSw@(=x8hVX)xSr@{t|^6-gH1Q4 zrK)NPFnE$;@bAP`Mm!w$ezaNrr3DV!4wflbYL^YKg>FRk(!zsfWQV^ zK5B~5xpM}@eF8%7P3o!rijhLAvFs>A?i-d-Q^!JkB-*!uu%TDUTY-R`oIgbB2o7{H zDFBK7ANoE19B_-mcAuBy@m3o%#r4I7_J0xtNgz8QHIkkMtrtl*`GAx7`Wy_mYIW|% z)tE7iXf0;jqTxUPSDeYcd0lkD4Zkqb+#$F23>0%;u~P?)1>CrC7@|N7?1tWJO+7bnY_aC_Q&`L0#A>6Wa;sq@$`T?%B+)7=TP9FZ|EC|} z>2$Zi$@2YjGSz3dx1HN&Gpq9_Nmw=n!`-nrK*J>8f4fv|5E*5(gxLk<3}DHdz^>?R zxr=YUlUb0$(Xp@u;DCJrqsII9?+H%;8Jxbc@xypm2^9Bo*sWlC!=W3nZ1tuGfSfr* zD?!(i^m{D)tAMtiV>QC~hIAGP0jCQont9heqW$qV)#RXRz|IrZ^gXm&`Lwq$ZJ?mDUT`KOrCg3`ne zJ>wu$L@)`XJXUCY{$EOyM^tUIpLZIi1?#jr;v(t7}n&6E5BlLje$;04zzb5#=2yPFwv zsBPuq^{3*_?)dclN$s5+%lw5X?-=&3+iVa%sObQNDrdl*g;Ru`1pE;54FdH1_Nv^@ zffAFX>w(gQA4&>RhBrlSu86=(`7jcR{CI4|9|3(hO$>3Mr$e&vGIMitY`QUss)PhV z+jr;~x)?M|0Z7)3i;s^+HAQ02{SGqu?IKnVcJ|ZVopeNhq1K^ZQD3nd2?WaETZl@7 z2%^$dcQQcCBLT|tV{h+ORCXJlWu4YKiD1=+qZGzQU^07w~)!6?4Z)g-N8(?b_3!ii6A#`(I+{EPWz07RL|$oX1M$JJRaJ($%xeE@Aup zD!jfw5z16DhZ(Q=MMsg{>LoMCXSo?3zJh{qs25nx%E)QVyMwx`8@w0_D;6k$2QmLK zMJI0^gbaJD-hI;Yh&{O~B3Be~o*oFW3VDNlN>o~L>_1X-7DR9V{Th^)sz#ri&3XLzV>}&?50HHS z8ZZy=ZX=@(Q(s7;^;~6#TjhS`IS-1*Y3*nGT|*WvJ^NGns?r!EeTe#5UloarMT_*9 zxM_S8=t2aHtk2R^UCmq>TDU*{N8_;z1LKF?53Y-7Pf1As(j9boz%FX)!xXP+aDC6c zajNI&w8@}cP*v|z_pPP%KiIO_(+Wgwo)E$V7k>(b;a^Gz`B`wh}s|VN!8^3FGDq1OO{1 zD(3o`xP*iVwoPO~u*Wq0`^~PuYNfh4Lnljz>w2>n4^B#xOLadEU*n;t^F;6e{xY;z zW0sSFBzZ%IL(nvQV8-Q z*!*HXiRnK?lHH5;H!IJ!n=L+En#vMb>*rJ8O+A?C<5O_9U+I6}H2GYyZs1liWXk?v zolc2ezgR=WZJPuou*tqbX^4?8M`~o0%aAewx#nqrdS*YiVT00)ngkelE1}0u1jS2y z24c)aK`EAzME(1O1^iw=Y=<=UiXuH`)w*ifLrc@;ojw|*T|T%YF=5^ zr)hcr|L}si#BH~Awonp{nRH`eC-`S;0suCMibD=C*UO)e*FjET)2eLN)e-&@A~XF(H>hxUJ~ z1>r*sGQHLxo?b=}jZQyOaMXH%hKzonLuIEuwGdY`|AhVf3wPC>>0`@QAdoB^h?Yy5 zkgOlQayS%iLAq{mSDcJMSd~n)l=rH&uaDoGvRiCWxurCZZ$IUYe+o61(hUI}l< z4bQ3#unw=;n8XN#32Kdyu zPl#;rW?TNto|lGs1yiM7h zHUDW?#b;}QDmamVRhht_OZtWH=Hh+_E_mg~?%)<~lCTI8wP|QkIFb=`8feubZs_1&}DzS{Ndf4P^&Ct4lI&3eLRVyE(Bxxj@%xJF~ox z`iIDq|6a|{WX?>`>~F7K|1PnG|64>kY)&oFyLGvzV0*EL$F9@Q)GYH!Ux!f|Bo9yP z%Q3tPX`oDqvgI9xip40PzrDSkAJ5#y(MC!juHNb!j#IDu4wgAY>eJ zn2@WVR{6G}VJNE~YCZ!1YFtnNqPe|-|A-?Ey*Qb00Y;iMV?R2J_0ASXxla<4&HL7I z#3f{$d9e&5mFX$8jEq--kVT{U=2DJXGSE@zkM`;llz0uIc&MeIGaSk~F;=xw_vQ)# zdfT1e3Gp7PrAKSpopARfF=8sA1$mr7CLVb0$zt>8DL76_4WzoM2PMP=o?djU}Lsyvu zO=xFl#`WMKKV~k5m|94aR}upbbaJBu#emib!v`_*6@UP2&|+vtl2ZfD9gV`fNvS-X zd@FLL?sN63F*Wa}X%=5e74KWezB(tNd*~@`Uj6CTCXG8d45DMpxgWeRTQzc zkxq6?+-E&wBd)|Zh1*$6o3LTL)Cpfr@c%JB*eOO`Ix>Jdnq z@Qt!pUhYW|{}LmieYdM*+b6~&vpEmrj81jj_NhAML>uLY--I@7CKs*eG)zutMpjUO z3O+|An7a99XiMK9X~>qXVP3v46l~kJ)XlU#EBS7{9o)5(MfTu5<>nVHt)FOvZsCm^ z0Ip(e(LghV6;NgNV{|w#3}K?*R6anVRO;f__4!O!65q_D-Bc zW@B)I0SYD9ooGEhJ+B|1*68i(@kC1I#YY# ze~jT4!R_j^oSBF1_&2Zd-1gZ(=;Qa5SrN+xH(uw^yDs_Rt?RGd8jOpL=Pbf3uPvii zXv`dr+|T3IHdJ(F$JvV(=jIhJ3(#2NJ{bc##`>5X#~I}bOL zsaQd7L!+az*epiP`aXR+(`%B&bNZPel+D&7lQ70X${r!8m!Qa`dgZLTsi~<@xIBr# zij7Lf;={xb`G^Yu0eshSC^AH7(T~~4omtE2zP{xa0LMj?B(+3?49g-QS&ww`GRt!g z%c#-X6x=Gd{d?Er!cXt;@Q?8UAMyY_C6{*AqwE)31++6+Iq_B3$JRNFGXTSEhH zIIqPn-rBX&@C42fwLQc|3w@QKQz69Q{+eo_oOie8Sen0kMuKtx2gOampQ#UDX97(v zC;e`1da!lUK&<~HE7Y205HBqDG6A!8M8zj=R|T>Z@G?Zk=rQzi{?^0L4QKxr^7Z+_ zQpq#s)%|y>iu(C`(=3Q@#KOZ0!h&23s0Cr9@7MTO>2E1?DUH<}sHCo&Z5b;0ye(zK z^5B6na)2B0krW10l?uZ+#7U-Qf7i4prd!O;PFV|@yrxi5P0S5Xf#oCSGt=3^@s1Zr zeM#Lozh?zG+ZNpN_~$++7&X1@tS+|uiD@wJ*RvqDt^zJhk^q4Rh6p+cAVR>Yl`;x( z?SK1+BO;4C!|F^%K5aC=MqUzL)HtB=*MK6y3DQtLg7+Y7T2oU~bx`_YuQ(t`x`G7Mxx%V_3zRo54T3bQ4D8;tM>v_*H`;u($m2Jfr6#eP(*7m-Osc2DB# zM5Sej@FgfssIZwD`6~Hlar5x3y>OT>nTVh3M~5NoytL zW=#)4y5B$sN^GkwElk)9%Q5B8AHZdD{}<#Y69P*qO-DpnOzZ_ZN+?!dV7NNl?V3?P zGAxf|KFb9%`$8W~&7lx-2{EkX)dEv;_|~u#Fp(TaxxIJ)Y$rm7-}P(i>p!S-q!9B( zgJ`@$Xesc+5x>Ys57x<&1f~jnK?(~EU68J@E{F?Y_~wTBK;D{9?syGWk8m-_LvFCK?Ac~LQ!7+@p-OuwuU3T zgB?xO<(p$K_!fIi2W`x@y2@3T!s)kTF6+F>M1x=Vg>uffV}rsGN_G6FZKu=b_uNb^ zOamk>YdLlz^J`L^x5C4T-uodtB_J0T5W;PCA=grE%iCU*W$R&Pi_O-f7~rjyVT9ia zOI8qJ>seS>;&ZSfTz;wsvCGDdg3Js{J~lTy6W;@Y_3Px${om{lz>~C-ky}mP41&5& z(|&tA_9~~K02%~qFIOJ6?Z5$@9w+EHD@B{MGL3wzGhV)WMUR&MXllJ-qDuUTySrT% zMhv{4nEcw59`ze@17gSZ^3o9$u(ACH2e8P~939X~^ywU)a70+Y(I7rC09KZ@aDW0`|L!i;HMyrl-pw;XiEdL0k{cZp0a5J_U1!yd_dY z@Mo#R%|VWLzT00ZjFNtG1^EAwF9GI_NgUNZcGw7(-c6iM6Vc5OXHK^%7~%}nO!4Xt z1>TXNH_a?bHi?|NE%gCyjy*ro2V=)mOHw;x7>ROYD+cuvQ5rd$$peW-8qIGEmjO^r zj33TXYnI6quF*kk$=$QOCaRyM%UI>9*BM;i_UZka-Fkn}13&KX2Ld^R>=n1MTWg8Y zlO4dHR+bK;S$-g}4~CpjhQVC0D?l3Kh+l(ZL^7{lx;wZ;U(8JWh^nZ;5DlUvxETDi z>$vOR27$>u0DeLE%rtjD`kkIY?v4(F=Wj5d!Zjrs_EcO+LD*qkT~dwL0Ft=LeV2+j zr_|DZ#RDX9yCY(sASe!^5*%;)S4&plkR-NVP!9(muE@p@^?3OB!CO-$p*$@SQBgna2^N@3 zTp@(Fe49HP$pVSHBKH~xW~HFnJQo-?RJz3@Jo-nqW?Px79xJ_J1v9N-<(75nfvpvO zjn2=jI}<9aNjv#0V^N^n1e#KHZXL`+^#`w^X=*?ALn{MYH9IBryF&zbW&2ZGX$%Co z*}Bi7SqYA>SL(_m0BgBJ#}H6h_WSp5otacTQfA!<4^DXau{1GS#HMJu`KWrs zdRf`*Oc*SVTB5q(g10I$%XzTN8|ANQ;xVkLAvNz&Jg5eUMvR+<-OBD@ZUmQR!|?F% zdOU;JV#+aR#}6T8D)~5HFfzkC2CV+OA`IW-w(J1JDH!T!jAowxXJ>JG11G#(*L{3w z+dq7W%K;4@uUsiPF>09{I)b7jPK6)Md8^CZ4Rv=G_#%BNWrQe3pR8hf0ov^hoLeew z@UYloF|boIko?(&e^8EriHUC5bur_TFR@UObA3AIE%67=#|yM(B|e35XAtsRvo_{K z7f)KedldA}82cgQ0y3#nxTfNvGpgg-iC}`?C4kp3KvkIbLQbQsx-%yhLvI>Hy0KDl z>_ZW#%o%({GxAmVc?SoQj%-rm^RgpjXn*31K^m+En*swZ=5&*aLBE+#U*?D7Wjc;I zS$#+gE{4WICR{OL@N*qbQdw|-%Ct+~z^6#^PSb?2*n*F{KN1s>%_-S3CmhPcS|oFQ zLR95|nB6_6(PYc6u}^#HSJZr8cYEK#`NCNvw6l20kt6aO(hrL>^0BzH>E3YPm9YKm ztU-rvd_qFf_QSaFYTdVQB2wJ|`;2#gEI|!AeAM#ICqHrkYzlWW5&_F`s(z_yXJ={m+2xJ$A ziO&AeRX6dh$=re#JrK)@+zae*hLOnrc+=FRsW8d7aHMC=TC)i(2`(1Va9&TU0+Z4~ zzaE={(m@5u;3)BhsWv70H@;Np>8-3@!@#G@3=Eej9)_?FN%7v6rd>kDne+&p6M-&h zv4m}2CNRk-6-VQa>>M8gyzpD(_VdOvRi9VFH$JU>Mdrk$Y~Y>u6~o*sTIDe;rqps>;pxD?p1Fq}9t=o1+PC z#*|kINiC`lj1UJfuGZ|vaXe+rBI-3s=!{!kNgGH-^%o4 zP9C(*r?@TH^UE^)T$s}f@bsTY{YXvL%K|*2<%vcRaR3`AHn3NM#n|68gj(mwTYwFk zZP$kMVH9lR47ee$m$9>Hs~`IkNr`>+lV2~639@H)EG?M1eC|wAK%{q~$2{a)B{9xh z_`LJ&?2FX5C$?6r7eCt2!)|rY-LLrJ%r)2Bx+ix^^$}f`V0-4-qiTsNvgT8G#eLJ5 z&gsX{;DG86rvKQzfrL7T_yEWJT2}I8=CzW6#1S3O9C^?=364Q!$!$6b!}q(0S7%H~ zX1Kh(kDxf-Id;*FdKU-1N1DNas^2=iii}GP!fJZ#LS^nC#X=?Bl1F?TLzJ4#?@b#ZP83tXLV7_1nj;pl_UkOJ_f@<)Q_Ht- zE}Cl7F2P^{d2`pES;)r>94sOxr{Rip$gu{ij0qlmwZIUFTMl4Kamd9+n7j-1RAu6B zhJ`JY2-=hQ5-eE!H?-^q$oh3ck;{+vjm+1j(9d;-c3$wQ+QXbo_Us;8JlK8$ftr5_ zvCJhvQT2~Q>=Jf7tNsrEVrW@H!|>@EfTzK|ILlLW*7oQLLX;i3Ly4WGE3;c!>i#kb ziH3fU$A#xlzq8oOyj;cJAiYGtd^carhMnBk_OGmQ)gzT>2w$Po&Fdc9c-h2Gr9FL} zwkd|Wim~VS{;oxbHtMl@A5Bh|I%^%0jCa|oo9+mb)(4M{3CR^WW2&8JaGMt_1JUMD zQIJGc7urm`?ey4xNZ{J067^t)>$sJv?u5USCHhJABBS?lPJ>-8qYrCQ zh+f9I{1Fux{$#1Amsfm}J*=xCNmrP*Asz^TV8e!2STyrOxWk9LPefK336TBFI3HC~ z3{GDppX{WR_ay^lXc)rP*~-sPM`N&jT*^`rX-d(H{1|s9U2t`)-_f3DPmRm_0uquz1QWpzi$t)amTh49Nhgm; zDDA1c$QOgalEe&=Asg6h!)C1@xFKm_`A(;4wQiww1+Ruh5q*9Fy`!7kMC$qG{Lu`P%$mZ=$#jSJaM(j!c?5}Y z*XNbV#})PM;pQX5PD0>^VOYca$>7J zFI0H-JoRloTeV~EQhN1KtNSi~=UCM|BX0*gdp*hA&tt&At8+4aG%Ne4yu1XImx3NY zn)zc5@R`z-6Ij7GBR`N6pP;?SD>|T2bj1Zdn`KU>1_XBCVAf7UGu>bagT9Np)Z6U{ z;E5OQf{w^qX`l455jV?%ma*EVW`G-U-;hrXGkeV=ryJ$^mUYad=Ji-Bkj~{qQ50?yeH=*@y8YZ5DLy1&qvCp#=GrM}!+*hKzPaESyNa4b zAwx63%XV$(ND7u%V%F61u=47Pl^`Vb0%rbeijfgf1@prO0 zSf@zW5{Nb>)Y+|A%@Mrs(_I`K94GjDl+td-r{Fz5QcGM%VS|;2W9MoLp@vYI9vz<4 zm1^IFL_;s^xXA~`aQv|E-d+1>mOL0h;}nL8AQFA^@Vc0?n|vQa^;oUlC9MXU4!aM+ z2-yuEQNuJ4n>SDZ@yLZ-z#~Ew_i-{bFhyKv7Fh?IPbC<)xSB`FVo_&(a5NgCkK(?; zf_}wv^cZ_XDmK@KEW2ClC?pL0KaU@UWNXehbV28FhE)#wIpUfh2-LJ#Tlr@gSWfLg za4{935s1QV*{Y2MG=%!8w6c<#uxsdi1vQoftW(Wn)`6~O7lS|$&EQr@R!f^68fYS^xAJC?KhuM~k=TZ}%FjdKP-yO!=b?VJHQP8~k+!J#3Q@DmCO z4dw?e`#Vys$XL{&p2VVDGN8U&C}jQikmOz}zoW@tE*?3BN!n_u+Un_UnOVTOP=D}g zO4T6tk?5tort%SxjG~ae_BM~^8nj^hwHPGb^fp`PxDD@1n4Tp0G0N$ymFex(6KYRD zB%XF_)oZ9I;V7x_qYJM9UOG7PO_g#9V|;v`#{tIWBt0mr~X zy#7?U@k%b772Qz!zHD1V%b*0TtRaw|H-y4)xE1vv|8=ixIF*S zQ|e&LmpOYDuAxG4&q~R2%^N~J6(zpqUaX}LvP?uUm7p6^3Jc%AY2<~TJ0j4}Pk@`( zlI)le`zQ>HG>#ClGBl}F6sSYng>cXaqYR_YF&_&%y%=Gp|W;NG;JHvCUV>D3~fhg~wMCxfTEw zuSpK}2gt|5K`s9A>(@sr*@9$L`Dxkk>VQJpiC#L5!V2lpnJgsD;hR5Ln!C<4DNjNu1NAmus5UmgvFU6U;a^Xs!CEbX^ znn2C4@9klKAjX&i!=YS8AnP*d&B-2F=>A60$jXyV`fU0$d6&?n^Ld3Fmg)Z}$0}N* zNe9XxJSrCErO3J1fuV6dJwx+%$HzD7A50R4CGHz&P=xnLnNOkn-HK|5iKN|0B(f>JJgdLpx)vIunLs^|gXdIw?>!Om!D>d=~mFBm2ioB+Zx zQDy0CUGHnx48a^VOami??r8yG1bo0r-&s z_H;NH0F_vj`1t-qt!C_DjL_}Pybh098ocFIrUSr~A+EBQBd3xH@ z+*|vD3jy|jlDxp+uAMNJ)Ek>ujJ%Esnqw5rK!dfxssEtJ$p zsMwJ!_e4To$lbCm&k zlWr^mTv$Eb=FKZ5Y@NSjcmDq4MFTqiYiNrW5nLX)njObW$AJ$_xMHEbz`GO2RJw^N z5V|8Sb;e|w+B)Z2S5=XZB^H-XHd#puxvQ~kDUhX!11SZUcaAO-c3Rz>0j5VCCj zh+RZ9*8{cxDkN5!d;0E1i+ZOzgXFi6S%tV=TR<`0Oz@bSxd08Gq-8#_n&KI78P7); z90Bzki10%>QH3K^h`2#f#q!n8VJGrIg1hTx8KZ(Ik69)P@Tux40lIHD3>o^t+%N}f z(i1g;_g~b##vZ~)nYc(DH61B_>HC3JD|dtgZZ5riGJD?*a63PW3 zk~YVd`5TjFkzM**|DxO7Rwl2=hxesI{2K%$ImvePsA=ni=NzgOKfXhNIUww zzf)xilt4uDO$c*Tmfcw0WOxdI$q~PN$Y+}K>~~OPM^l(_L|lfMHt6t+10ehpR7R~3 zF~WU2QgaMHq6=R}Mi}4(`1R&6E3x~i^hihq8QB6~GVOpC6@9&+Xb~Pp^TIf1&@4en zwgx*Ejm1U=F@W~Pc0W&C0|(^3y_^-(-aDMXuw=Voc-$H6geJ|8w~|K$;7|;V{m9s4 zAUVIN3h>0N2S5y1j*e8s*clWU#(^I_v(qBb^K{UPz6%x#94MWB=8J0aT(&CJ7=pFc;%2tSGF>QK;N zF6I#m&pDw3uC~#D1fL1qM)5j`zQq8es)^hNdtQ3rLmRQ8wsj zdS}Rak+ANYtKFf~+c1}dT-6xQ5|FdzPuaH6wcN_td-!9&o@i|UE4tn`d9Mb`pz$4-=Tn{7R zrNnS@DaP*u?!iZBIUVm|1MNoj7E0K7SKw0(ch7uRLrq48=3EQ_!eodY9Vqrn8V2sT zC&eg=$;&}CCkL^9R?g_JUnC0$1lJ}n-jZWsqj;>(U?>fQA_c8I@TUImjkJF3G3S2I zV=*#3;0I6X(yzR&^sq*9zVWR30H2~(~w~lhzDz+=DTjE^4Y)Q8l(Ir z%Jj14)RG~ZXhEUMAX0_e)06e|fdMD|1O|F+#e3FRUCT1E6U1szK7;_=tLPXWT$~sZ zEvAi~{(^@QL$|nqfUUv^)et_tEod1%gH=1$omjVSoelECA$2FR1(>Hu1}!OzF}^TH zK8T&Ob+JG$=q0EN*E=B{6%7O(X7J&;{PJzC1kLX+oaw|EERXr1ILrMu@C9PG!OpnJ zg~Q|~(3a{_K+EB2k1IdC5)U12o)Qs^@vKtwXkt!p-XEwb zzXAVkCcnGSFDUj?#$alo-#kChv9)h@r4^7|md8~oTHn8M@0qLF(gOJq5<~Rwjb4j0 ze|=E<$2p-G&j3A7R8c`0ccv}1Kpat1oSdDb0evvG^pXUqJ9m~-9%(&Ekj*(Vc6lDn zGqU_oc|lDI3x)2RbjW}-%Up)p_ONwy&MVh;snXFo*O2loeAQeoE;c?|ihxwdqEp?7 zIFj-lCL}Uo#XSJk%h<-b@K4CEJtyGksHVO$H>b+L_98Dp;K#jv%P5Zx2h6UF_j|GE zU4dL&Fs)dCd)cvvKP!Pz`Ini~Kp)7}-|dI@m24p{zGW|arrZ13&Ob9aa*oo={*t;! zaL9#^xRpd}$5bpsmu7x}LP^`wzRdajy0k+zO9HQ$>+BHMh*-UX=J^}jRN8g)`l0<_ zDh=gia_U>>+Q<|5Umsi^3&-;*1&wtdSPll=3cYf>V-atzWnlWlpNETcPqEK@cUR-> zq6n^h`_vss8tSH=8igl3u?BI?yTf;dN8MnWf9pXU2NAUgbx^&%IM#h7DbLAWF~#B0 z1yvSf8@auyi-ipjc>T#@t+Z{fAWq&xJ_8Af-`(QRUWiXOsQ3qWaQ2nh7+*)zq$W{WH4V)3$j|LEC*R{QEMVSah9 zPfq6Qlc~=?0W6XHFPxKiTYFIUV<~e&hq1dI12d_>x2`;_5Esei_w*L+Qi-6CEM3fN zwwEoM3oP*!;~Q-3cOkE0;kLCPLi8v{Cc2NHKrP9mclfHN<~dCK>=904N}>Jd?f$3_ znyYsdTUYnlI_3bmhW5`8^n`=o7q7;&;O;qCIngm+1+>-$siG`;|M)AvkZ8COjZ_(k zL>#t9&3~?;qkPn8(E9yU_{=$PRX;t+6>Bct34q=%dHWGomL_gJ&LIeu|F~$DQD`?Y z`!pO%TgH)D+;Z(uNbsxLmv@?9)vEmSG8b+?`E{w_G7(1zB_AhmLFQ?PHTF~Y3 zs^7n2h#dK;<}-1HPWOA;@2S@2>0aVHUS8I6UfnqUsEdqDnC20K`aGu6J7%B49)%U* zIS$U%k;(Bnpf0fAG2Y5GiJquL|L4P~{)gyD{_#awc0JaSjk^?+rdb-;e)?AR9zQ3PGn8Ye;-cGS7;m+( z{{Q3GYRwS4G9NB$T%I-zR)ipccs|q0tSyl+_6biIl(h0UICkua+$O_bSQamS4bt%f zMUSg%dSQfm@B8)OK_W90u}zT)jYdXB4xYjnIdpo zWK;*fgDn|KgCQsA&=A6f3?o^DfW3~9T?zFYEd&n@`JH!pZV~t1B6KACoDTkZyD=BH zMfLd5wFrb~FW&PI$F3B;Z0zx@qL(jTyg*O@Q)TSU9oRq)Z#^Mw%*o|W6}ookj$>bp z7mkK%;-^|FJHpOZ@T_aWVOCWU9~Vbd#>7O;2&W}Xqr@D86e~;!pdo((X_x{20gM;? z6JuCw$dj5`ODHfWCt=KIAwyCya_%FFqmzX$`v{APW~K|PP~+P;+N7_I$s0iY`Qgm_ zbX(881i~bIx5Y5wZiU1XomUy!Sz=oc0RTKV)j0>uG^pu?^goL~#Q6N_i7A5lpcx~i z7_RMeG$*ZF>6m0Wh2_Whrk}r*eobuI_q? zEPz=tWCV(Lg_VDHQ&CX+zChpR*ntwrNU8XrJFaHa;y9E`u%48YRDdSn)x`iOv{$&{ zm)P1@dWv1V$KdhI_QcPfw@RWq-ro7lvasz>6t(7LZ$EsnH1^w$Am&l|p233cjXzO3 z9rj8x{F%ZpH~wV4gzEEWvDe&O;HJv;=t$pE$6{Zaf@NNpAN!|FE{x_C>c>*U5XRfR z;&^z8PuShvy}^)7y#kn}u*eBZA8+c;m;Pcr(g0k;dcC~72p|x`ds^Ei1uB+}{02*K zn|B-}In!tZT4QFajRZ)h1NeY4GF}OGaDWBdHS>uO6m|{n6(0f1mg2h!Rvi83S{PYD zufLiCoTnnac-9Y0Hs*k?gOoMvyu8e^^|H#((!V*=EIyn?MNu10yT*G~O1U=we#Lfc z$Htu0d%_lUZ~QO)`tCP%K;G}V?D>nLx7ZEUx6~h1DeP-y`Q=}K6~D=i+mC(~s(P}{ z7QcDrTGQmHDR*^tL3r!>|pITPZAAl$$7|I^Cys*SG2A zSTK>UK|)zs4Eq*|ifF_9(2cOIFM1&SWdf^2D+`&ieMBzKeWX1C9aHl5Mq?u*{WhV# zyTK${l6+J*DTdU`$}DA@*i0evZ$M=q(KC=83SHOAo!3k{nk*KMp%Jg=hjS1$(|vO^<#5Dj3B+oo^Tb>MEej{A3p()LvP;SxqLB; zDrF{gWYj3Pii2<6+GF=wp5WII7Z7C>6!0W*SnZJ#6@U(spOt~05MZ5_m=60rl{U?w zgs2VqH&U`TIO+Z>&k4lBJpFdCauds{r#^qp+h#E^09 z)K}pYzEX`;xymzc#~5#K9lAArKT6B4c%_-=!9S-R(*RnOr_RF(^Pug$J_lG8BZ_Rh zmjNQT8W{s#Px8HDbEAL~0+7*8(UV5f`t!E7QxPW=6<^s${aw3D1AT|`e3sh)yXz%g zCRyhQK!7~jlTDr>L{?cwQE?SWvd4HrOj$r#f_OXJqXDaqdH*70tsZ?Pv$bS}mH}t0 zqVwsJhqH5X6iPKyvmRc0>McQLJn}Gjx$Jndsa03jt3<_n1dW}F_|n~4;X~=b42pFB zRO0e{z=%}CVdKrHbHoIam8f`ehRC}WjJ`i5XE_Im0Qj7s$rL;W5_xt#Hsb@QO{YNo zJ&`ZIid3U=s1D*MP@(rE{xvVgEIP6?%HLVl`a0%s5dwHP5!BYXjH+@O%boml0Iwp z&~zy#WUp2H`gifU^rOsg$O;Z#k6OktE%@yKy6L>hs+ut>wmnM425S&7p>iwdS$|}* zp0p*BC+zZcBG%H-G`O|{cKe9;h5$3!bLTjTI1r>q^dCoB^=-bWFTekma-eI1NQtfu z*?{0_7%U*3rXuMt8XAn`4uPPV#`-5?oH6eue#zi|#1a=g$2OMwbcfpO3DFI35dCuG zU9`Q7`&QA7E5-LYQx{xojZbO1jDkbfPxjZMO%=RX#ENEz*f178de-yvr`K--79;m; zQ35Yy@1Y6yx8jGNxdG!3*`?L6y=}i{@l^s0V0Fyjg=icyF6qQuty6T7355!X?H5>x zv`kFCIH=ZG7mhL#KIYxKcL&NB=FgE8xzrzrYF*;VE}Y4@WzZ_L~ftpel$lR zy6eZThJOFE+^Up-yEJ4WPOGC2`kNzf@e(;(iqKOS3Cw)2``TKRH@UA2askMwmzugm z8fRl``^I&KSf8Z4X?_7ELcl>9G&5vCiC@_e7>sTpbiy_tFX`5wMw$R3PU4+=4F5i7 zcyC^KZ|Cjpz1WsJP4(yEWZyRkmpP7;Uw~8%F+_zZp^!mB%1%s`YhFaRJ`vxPT9Wo- z_vdTnnwxmGygD*%wWs-6iPojO#AphO5nlw=Bn-hvp3A z6{MWMd-rY}pPPL}U6>u&C)yI8LbZQN&^=Qeb*o(QECO*X8k>(z@kWA&o8gN>t3CtG%oei09|n=38oMAB6Zcp9?mPrRM2@`H{sqIT9L3L2FPB z;QiCbj}PqI!8ntIs7FfCo~_MfND+MV4~S}{8wg$*?ttUi!9R%#x!8afkNH)N0T+w| zb^aI6NHl!bz%GO&u;WB4f;^!!2)`KGe)|40Ko1Q)roU{%sAdD`S4Mb;JLcn2r3I3O z?m^?24B(*GPu*0@Hln^BJ+n)7HpNeP##ESLh?bVP^sO#B@99&0=!A$u2Z8j9 zC?w$wS+Yrv2aS!5g~h}ocdcPJ|4aU3{};wRK4|k%0<#SlgEcHg-G4Wr|vip=$-Uzq+>c*5t4(hr6qZjC%wU^bG-+RQ`Krz|V{DYrqQ|zHEj@x}PcO~2$ zba-MXA2X^liygajlAs$#o1uj=w)dq+yI|5;RQRTkG<)b=LV9aLb{hIU_Sp`}(6!{` zWL-lCfMq+aCEYPkim zN}Q>5#}o!%#^3tfPeu$ zZ7^g>*TN1Z%Bsr0rbeps_oq)oP)&j#QC7-fnk*2Pir|EhpXKq=$`fJ*&tishnbxN{ zsz2c!%38&InkH7>jQV5M5--Dy(4=Fs19CDsrdJhv)iqDoK{HldN1QbbKK8FBHg11A;N?@B^+F8q^qod{Alr1b_*B~b@Vp}K_NLvXJIUMo` z?8+b2Jon3J>=Eyt4xpn7{v}@H?d(EkECC(?ZGAdA0ztqC6YI- zdZ#&_=Um9wL-xy06PKe`yh~8@ z%Q>5%xQmag5~d`dDrT5P1L5KQ>UPBBln^u;fca%3qLAW?!I&oHK0ZSaSFGn`LkxQa zJO+Z#F8s90cJdjDMBhdX(-7AJpUFHgd<({bN3Z(q4~=8fnO8d;dQ~`sE?)!*#GCf#yQ7i5laM%{>*NedB?=pmW278Xrh2_=)vvLplO`cP zBZe?YgKjQ`sQxkZx~Tpo>1C1euo^s^9De>UlJ)MKJsFw{w1NtrQoTDkx4&#j0P@h!joqZZ^$BtqOl!Vf4o zUaDTl!&r{_JlkuFhwH>d_14S8C4m&9P>`zDw`}Mt)M%wGEm{#xU*^h@6&-K7VZ-eM z96L48IzmpzZ!!f}ofPy(cH5%NDkKs=hH~J zBb#${>B>r;-ts}eMWkr>!z#L%&P(SqUUSiXJCbGS$@b`jkw7?C@JqD>dn*tM1~{6^ z@Cy7hakNl|MERALY-$7QWfm zK55&*FR|*I?@p`I8*8Y3m@7W`DYkrDK$5R(sb20e(OIR7!u?n7I7~;subiDR3>AK2 z%}Tib!MdtrT?3Crx?UBzm3ix3+-9{!K(=7Cc;^43>O0_aeB1XQBO@{zHYqeE4Mo!^ zw3UXIP>P0@LNriP(H=@fDfP6pOUp`2rKKeeB`xi}{>K&X`~LnuAL9FM+|T`7_jR4; zaUSE+sF(Gt`GAbb1W6n61(R}#yV`kke{7ayp7zLWpVW=-ukByoeXPu6PhE2UO1Jh@ zigM_#7f-&`NQ9jOS)ksaQQ}!5=k1AKmXmkJt|ooRG;8mQRf^refPd}cWo2C>1Djj~ z3+{)C4cyw22uOaf=2*ABTSDaCTwXERbkIL1@&P*%F2Dr1X9s)WeoYnh&95tG$W%JO z@;Q&FWxMn5j*h(r+9;X%KpP5`GT2Gu*np*1J(9S>n_h9_T1Gr@-T2CgrwMh)w!H zRisBv9tz@HKjzU=yqbr4I(#Ih0PShuhS~MDTUTQ2^x+2bS}k^4x@6A^h933 zJ_M-7B8`b>-#!{h6L8SHBhqPjy1YXx1h||iqRF0@@)Y{~@bV>FK#_e3e3MuhO&FQyEJxZQC6cai^X0ncA~}jEn2lPZqrB z9-V_SY3yRy=#w$HTO#e9s$X44+0Qp~vnLEZ+;4Yw zHdRP=3=xp;J{lA-+cxp>xmVbr@Q?!9jtxFa<-HlUQo_N4jnL;j#W0D-IIN>pe~b(d zAIB^IN)&I<@Y~IzIQxzC05H`I9^@wPB!I+9<~vT{&Oq%lR)KRt!xw-Uk; z^jz=C%IL^u4Z@ZxmkDHWYhd|Hnml4!j&HOZzN7rXYl+h*z-eXJg~Zq+3Y7?MaA=w| zr(30^qDLpkz!ju=lW8XY3c|@TEg)nkksr1W1Ho-GGmt?M5RY;cIxQ@7SCj>5W~Ux) zxr;`PLdk(b@y?W0kwK3V4UAvt{>gAS#np3eTIOJ@Uu-m=JtGdbKz+3mX&sIbq85(A zi_i=JB@FaDfz!W|s>ThokL@_Jz~cr`m$XIPF10ZaKapJlc-KZiwjvoMV+5EGT&yI41S%&R$VANS<%$N` zl|q4%x^8OMg{z2)^Ny;3uRMThV^bL7U+t?OnihLH{5Gn79%Oo=zk6&3HMkdc`R+7( z>#?V}3e0}U#$KflwdS4=ANJpfB#H1;F(sVB8TIKg7g}!?ENNo+T>B%?fdYJq2a1`LdT6_o$cX_@Ju9`x;kKCu(ja>c?@68fQTJTP_gS-=~(d6yDA>av7(52;K+Gt&n_=U*z_y6iW&1c)k z!iSI2odZkk)wl(?HEulDRA6Mk?RNWtdNF%I{_Teg^GUK@d>JLS@@ZzF5Jul%$()=Ni{zNy5vU8;8Yu7t|=X< zgsLP>UsiYc#6IyXoddQ=!Refg*UeT>v}uWZ)rH(^5jYiK^JYL_xi77>ynK`6ROjY^ zbm$BjiG{|}4R||ZRdkgYr7iKkhl7n+lLv~TmOiIW<{P+8pB@75@DiDRcjC}ubewEk zS&53H#~Qjct(hE)wtK&Eu%v-TZkiwj{omOJP@Dz5vPrCu%CSU?wh!%)r$wsR@E$mz zIEz~JmHQ3U3nJXz#hq3Nr}9)0#gyF=B*QE&mb{xo$7kC;cvahpjV}Bu&z@EDoWq+K zKX%wHoC8W7ZR3HyzWtv;Ts78@k+nO5`DApmxJ{QK06#7sJnfrd|9nkV{)oif=-B`> zY#!oDb3UC(!x{YmK;+PW9Xm|7GLsF8}vVpXJ2hPE&?Wzur=~DUl%syyROY#6?$(XF2Mx4TC zT(SKSmRWU!EyNaFY7Q_$_ylFi)*J=(`CsQc6?P<^TT#dj*&4K{FR|Vih7nhQO)>e+ zLWK!~k#WA(cAuDrpIJjogJ$CuVO21RZ|R0jiykZ9Itg<_Q2dk<_FBmCcR0V@hf%*u zr_^;wcfXCx&kMyb`c04;$=?3V(2D4Og)`d8yl&xF8b6unGd%7WKh|)mxF5jA)MP^b^nY0@ki~r>{-;KDP;RU(3ibt`_UESdU)I3^eQ!}7` z*vNmW{R2foU?XYt&7KmaH<#X-_1grCTJ4M07c&x1(GtsDe3zVm^yUT2FFC4{bjjuO zI2oNZcIqV0L4#9S?|`j-vGRxyvZ=Evp4b7HLtRGwY|zb@rqErf+zys!!{U=I1p>o9 zJvn*0l&sgZM3kCxW^gi1Ws6gIa*(Aisn-ASo0~(I0-OWU5np}$lcc>jYQ&vWHe(GR zwuWHhM*N&xc(iZ*1ctw{X8PR*w92Zvd+%UK`w0_SnNrd((N|#h^2nW?5RX)+dmGbY zKkrpa)`dwS51~-vJ1?#tXbb2uwu#&6yB7uhbsHNs8H4SjwsDz2$t_WVwFAxm1S}*r z$4eT_z`lyFFycm(B0xm)nIL{)x?%_#Z*Dut(FN$%V{=^RNNqMzS~Hgye4RW}L=!yF z1mb(m`1Z}4*~c`qtS(!kPHRUc{RvH=J4m~mU@5ORgffF33WgWS_Qy7S!amA>&(89rlNa&L*Ebk<)`7g(=$PEwuHFfj_#{M zu{T3cU=_%coKZMPOw39<%&SWRX6NF0TV{vf`McH0Td7z4@M{9z{m6$oe|eVmFG;=} zy$PLS#u6E(wz;<+H_Cq}m|ju(As+?Pa_?{B;)>3#f2K^f8+HT0n z6K%4wTuJj0DG&?`hgj7B-Ml+l<#R!$oy18a(#H1B_u$wvoL8#!3pHw=ONR_QbD?;M zHk&Zycgf2To@nwma0gM)1HHI!Frjd8&R(#|!N|#qt%z7s>>w-RQUNX5fmOEKU;Fh2r9{AT3k6}6GKPIuxoGZGx z<*(>{iflB{^Xy3Urp}Hpt^rdDx09a>wiwH;rz5ZeBZxovgQg3|QvV)H5nVa&t z!&355Pzo6pd^Zk#m>DbZ%!;i&05jo#yh87;%>Q{C-uhGe{K@bZRF02TMjGz- zm8RUjku(*=?ENf&M)`n7(@FnQpJxFsW%;vCMiYOugpO`khH*Kmmb97>-yRO0Ow}wF zm0BR2VK$)*qd0;0gCW_3Q7NJ8;PIn40SH8o|K(kgSsi$Wm8czx78YzYV1PCYYPALQ zA#W@2Hks(2hov(dBuZy=vXKghMfL6^D8>f8dv9FE$ss zcd+=D@ktt7#kG3J-{C(UBRehhIxL5;a{myi+tI)={5JlsPTe0Ty*onZ6PlOYnM&cj zpEUtz+B>uijA;&18D59#2;PSlwc3w_%TG;xgwf5Ar+?N=|ms8em z7m}A{UFrmt*Xd&Z&%_ILQFup!V8F@eleoaDvS^Ot?Hewo8y37j4kZ~3XLu0{Z3zv1 zw|yj7bpRXPehkE8<(}-@axxV_} z9h7M8eB1=Ld>MG+HvABt&p}{|wSrDnl_S9(Xtf`ie)tR=@N9VKNN6p#o>nLx(YoJp z0-hDJ#~_=vKxIVIvbp2H!+8KAgSj zy!V#*%@rn+ccWzpX(h{crOIvXfezCv7BBp_#L9I>=~|$bSH0F)&|$g7*ZFx^`sLN* z>5@^Of1Z$<@(iiiWgw~2{?kId=iz5AJ9GL@D;-gXX}aMhvel?>)!_RnW{&gW#7 z3jxcnN|lhjseUc&Y>fOf7?=$hq!4Z~Q zJ{F*NYC7+E3oIV{-y zBDeikSw~ERfTQL%brk?WOG1VOh_H(LeA-b^)y+JKVc4}RLFKXqRB7A%6yXDdepF3c zp-CO1exo*WyNvmSo-g7B}bHJ|2Ze>|~mUmsgN4Cx8SNaR(3qBHF)Bc-+t)2Rx!b-q)~bTF!@zFuzf= z-}~z6%o1Z*jRB1y(`%XOkDP`V^OWdEw`_tX>_srEqb8T5Y*2<5)3aXmV=~ov8{tjW z+TS04=!n#m%}xe}2&+SU_E+t6>o8eRqpH3K3=eBhB7b?bOI$0m48BHfBRLpRu6QOGDrB||r&gr)dEOYKne!S9 zB7T-DmX!`jor~wVZt6|ztf7&^N!5QZT#Ccuiio|W`pK;h_r5Ut$Fe=O)9ti}6rGek z`+d)u7Mp3_+c(b6o4Cv*C8G-_7Q-2jYE&b>$M^D-WFRObru2|e?E&?e)DtMYEwJ5- z*^REkKTm?J8#xaA;S=0rV<+}?qJ;OtJfM5i_U$G$^PMIQ-BnN9BKoUq*mboB%c>e> zGXAH9xzA7;P^VVdIAdrM_w0A#TFK>I1^smX5V@xWA1L%XwX4BFbqcDg1rFCDRyvaE7vmc;~e z1vaFBK=GS{6dYx~zXBL|P0P?sZeU`H9DefG#KP3VMLtW%6E~Hb|JrWmBEXerDo)vubKT?O2r4z+1-+Tq$WEhd@GJ-=0W0MFOh=vts zsZB!mwPq3dXCh44c7qhJam3s1PK6rdRFAG|~$`lZl?F1VZl2MBxo^Ax4uS z>mgsC7e>WU(RzF(p0LjtN=HuNTH4S(2rIk+s3HVi9u5xQ8-HD<=KsQ(c(ts)f?L)) z>#BZd@Tr7u$@sE?K`XvWW2)93bYjqA4XgUeFcJHY@2|};kF{DYToPaMET!n)S^<8^ zPZ}A$@81_NWH1;wiEY*Jx^~vg^u4g0w(MJb0Zq}w;*#`K^K_@;L0rOkMRVb1bDBfd zqA|z4iwA9g8d{8u^TB%IH)v!lGQh3~De0u%zjyCdw=lPHLpJav;u2ML2ap8crBACM z>=wZz=NE;~q!kvYDqxL#e_=+}I3|}XDMaH8qRVHv;3G&-9vMZ&lK`r&=V+&1xeISl8sM8jjdAC5 zx1lSpjm<|5+={`Z|F5TqOdjA=eJXSXw{H8p#ZA5#Nmr(^-cdw))khb9b(E^S2vru8r#05-dvXDmlhmnsug(pL0#7^ zBKjXO9{u6!edI93IH4or_=erq7Dqq+Vb)?hxIG0x&|Y?S6SF-*r6d4OZUigptmY5E z5?Afet*y`#Ig$^~J7XRkmapca#3eamV@vsyFebR)$c&0&-;xtSn8E!7`tSP-+J7aO zs4c~RD1=Y?t3s>}Cjz|p0f>Z`#?SV45{>$->08&Dc~DtIQs4fU+FJ0atmp6Tl<*FvW|} zg<=~8c-lA<1H*khM={~VV*!}82uy}TcpO+&crjRpD;?i&9f6HoJ@uL9{#q1~_v-MX zn17Ly#{@2Vcc)WW31^gB(G^?2S$mwopbPw9%|Jh-efvh)$pb;e7`;BzGGeqI;1^LL zq?n~(=wDU5{^*dLqe|y~Yfw!s0<(7S(e~VaS@P2t+Sgz0j7&l^N9cDNB}&!z9Qu9u zi;xn1urHrl&Y^wW0`Kc>JJYlXov%~3GP(1s{pj+;heeFzsO&`s78ci~CMp@uq?#Tn zU2$a(PJ-*|v4B%HUTewYDdTojrlr8~L}KYu#xONx?C}}baQGEoHEC~eC$22;3s^>h zBRT0mAPG2kguj}^@(9|)G9g}uAJyz65bx1HGZ@xQf(;xw1@U~ePE4%h!aPzQUkff2 zvxi;D@xK+orAET;7AtG5*lu7^nvp8c*nlk^Cnx0;%+q>uUS+=|9A3;DPmf@4Qg@4o z00{vpaWWZKX@M#d4~SP&V6aj@9l@ND@W&~zwDbbwP%g;D@hto>NGS)fn4Us}DWKI8 zCYs^*WvnGWzP>;PSCOeC+^FwDdVxh5IdSV(Xub_A|Jp*XX+VjGcO*u)FMjyP3NS{C zUDfVXbGSfo2>W2K5YRuuy*NFBPD{9Vd^kIn zbFB64^&LOnGJ5*nT(M9(^5%cY(K%7E6oJc3=4@M<4yc>3Yky*Xv_bipYJ#o!=+kYU z8QZ(^;_S*ht7jDGPGvQY367pQNQ5;A160oL!<3avL)jR#`<}YQ#H!CAeUg(w5J@Pk z*uYe~Hdo-_gYhvZ;X1qi=E3OW4vf!<9&NN9L?~s9)O%?zuRzJ;48VdSy{|qoZ(6qh zhzq9OcP86hRmOyMiHZp7?Y~xtaJO)F?UFbqf|(cieA_64II)PGrG6Mp7W|^n&G4Dl zA2hgpxziXze|s_>@8`E65#8Ose;`yp3ObC9`WpYz&iWLi^#pXmUfBKp!SLlObV{$S zj*Q`<%WY461u z2Sskb6)?Lz1qu2=?Cp5$SAvxyRwr+8>@VPg!;1J@tNoYkajRzkT8kkX9ebu~6m!{` z&wV46e4Bb@reNzy4`I6ZFNEtpME};z+N3Ta1Dw;)$LyCz_>+6LSN}O`*_wB5xtzIx6+p3p11b`t* zejuJ=8bk?@a9Hw*c;@jUTTz;1IxggWI|s|}hq%_4m~%j4av04Nm}|}`@QJrBiFiP+ z0#Wv(^dgoAgi4IkhVuKyd?!v$7c4UDi-Yj#Bp3^&wWGs%y~O0vuIa$N?yNAI+pU%h z26iDx6}9PRpn;L%9B3$ET$1AY?c14G%9g@@7EflF8Yd(4g2 z9W(GY%;fIFHFzvv77E;+1=p+E5HG`N4I+QSX#GEAB*PlSG(A^?dVPNjyQ9h5M~qux zoRIe(PAqI?6W!G-2?=S`#rX&d>7$)i#1dCvMrcIf*sr@3<|Wy3cibCRm*Jp)sLH&A zx9-nT;40tN@#+=J{P+?xTV|lhTJ=D33rqOD^0KCt=E4qzjTh@f;ZYCljZ{hXIkyBr zY>rER21XhjOPCh}1@r)KNntck`e&HJ00muase^_-Z-7KgHA|?c@wYZl*%@<(CBzdG zrj6W6_Mia$x@vD7EMmh2ozz2gwo52P5{j=k<@($1p;R2+s7Q)Fu*mWS!(@?;l+>a#t=drr*b$??wW}FIol}X%@ zAGZg@VN69w{C@vtDU$yg;C|y4i6Vi0%X$<58n<$A9bn!aDi)HzjA0zvn<&!lAJO&` zbK#^p6q89Nbq7e>21`&v`9mYkzE3iDDq}DDe88Gw1<^WcJ`Zc;5ZdLmWElcV=-__6U0$u zK{l(!nFh*TDFefQRzQmUhc|AOkIG)%kPYG_338~KMf2r_!s!HC%9m1oOxL-H;2^2U zc7$PL`1OU_khGhUm+0(V96Ta^YO%m)&n$0tt3t$q@W;FsF0^TH z_$=>Uj-%80(CED~#o8iCRXCXAY^OvAa_)nT^b(Ji$jmg>qy=pJVadsW{6VcI7*}n@ z9hEjPc*S?ok0vua`zL1W#L)PAhLF5U?wRPahPXc`-5xtv)SXezk zausa*hw3ku>>om0mi(A^zs<@YvU_-&pO8#e`#7qXgD5srOuImA{|H=zvkczxTS4Yb zJy#n{oJhepCmzNW0xdwi@aX*<6uTc#u7c`;#7%yiC+eF;)s>mxK{UBg^L=m~+?XAB zYWIKn8ENF!epUNW+>zt_>Nt9kMwCdboipPoH~NXz#qmQl9~dVAtG(TNcG zHq>m3#w>E`XXk@}DC4%Zf-8;a=c6coD^stYU6hZF1wsD^^spVDU-hIIS3|VF;252u zs&bxvpkz1>VgWHfMCU|&;ZRq(fE4y((eQyMh#0scC>*c!E{FRf5Qeq~tG_GD98Uz^ zN#Zwf6OKsG{ImgxA%iT;;=v`~z$6$oz2o9Q*Vp|oQ7({E0zP0EjH}UV63HHQ_8Ibk zZ0E2AlgxB<_&CGP#CXdZtUWuYYn^jx0XUi2;q%IPcZv@GH8WX}@>54BSpjhdp~Wpi zT)YTr>K#W;5GLPhO%ORsBhYh|)%m43m_CAu5RsyF{{T7QK%sNTadst+kwPK5DS~xt zXPnp2A&KY&{Jo!X)ypxo#NRKU`J3q7kuL-ff1y$BI11rQ>6cH) zG3C`{sL8(^3r z9=T?G2-`Yw^&(Mu7(D{0(NDkmPm}iWq3P!WI*OEZdq!-c?yuyHnzVlT@%v$l*xlW& z)r8U%2Hd5uniQ0jG-PsCIN@~%U*C?vc@o(024O1k{a9Q$W%v;F;dia&yqViocmH?S z8E%F@2ThA)JeL~yA_ zzex82a4%a8$I?S)PGl2b{FN^IYgLf|Q?=qQ8n&>a{wA7O z!jauD0yL4ISCo-~kXF+1+Vt-K-6WqAFG0NDT6}brmCcD^t6Oo4I>7;n0RI(&yP{JR zpbs52HhKi=(k0OCPAe*YhCI&_r4wS0mZMp_hhhpI?8Nd;5Lqo|E;~wibf2$V6jyay zICCc(@MBmbopjJ{qs3kw%dL-=jv8h{zmLHqj0>)uReNK0=m4rX*6^G~=`V^lu?Ye6 zoy6J{4i7|>M)?07#SCdo+30yV6$Vh!gCB9GsgMkquL79!D?BZw`t?7KQ2t7X4=)Q& zq^vq>9Lm}iPwC9w7)0jhE@)$C0p1;);|XFD)!8@98Khv|U?2`EN&y;I z(hQMD5aL?zz_e@4+eqdTq`=PDs=BE2q*umUe|iMcykz-SDJk1W|1rY66Fw$&p@ng` z=uV64>WrfMSR;yv&C*t`?VjFy_FJ>;>YqC?#P+-2yxROh$?D1|dSExqb;nQ3RD6I6 z$8YSZ#{W=Y#B2RqVp441-rAU4okstnrb!Yn$P62a9(;To(eix)=Iw-Y^8y}OSjjI1 z0QdlP2?`NHl0alp;13$|?kxW5g_X*N!G+rVizzOOZo|ufCRH{b+t8_o`73$)y}d64 z1<5|%d3)8p-RO*SN@R~;tz#Or_6cOZqml%CtbBl#DmYwo0;?h}i-OBPL1Cp&J5SQ(`d@+-ktN0t(9xz0j7gOsO ziYiGzLiO%C6?J%C>d2(s-&+=b?*qa*fcX^y*GBoX64p-C{fP*|BO#`QLgB>PpgpyM zf*OJ}=?wo(Ta9ilRy>=_&Khx83w})ekM0jLK3~_%Bp^KTzAlZkU8T8AJ1*|q*rS-6 zFoWQT_uDbHiVy+Tj=MqzpY!K|EPZ+sQ|5_#OW|kb1bRyA?;uf&EHmrrlw(Sn+QOO%Ziw+5vMgfC)XDh(qxo zGH(p*;ujDgd|A}lOOZ>Zbuv?}=Xs_2|S;p;t|{NMJ@T$Hc9rtpuu@Z*Qg z2CR~yo)QOQ-OxSwO=l)Dfp+RC?Qtf3v%;|2N}1Td@dNVtEDL z6>Ptla`0L2RoU_y;mSIJdggT!<%4U5%C;v;$kYQWY2L_dzr!NlevSXQrHgie`i9HT zGrKh_qL&+X>R(E9Yi;zDWVj_0Yj`nhe&ES}FYWIIl0W;rqXnOh)*D~k(6EeT97{bI z+OU(wbuItG$)oq*yFcB9*kM7o>621Fs)B#_!t7wzv2@LVX!sl~)0=-3MR`D>XkEOl zDr#D#pI@Ua!B*ndO0zFXt=hz8mD90>8~^)3|Ncgy#IrAL%2$yu6^P%yMp>c|mQ{D6 zdY)B#tNAR`SfGIkJ#}E4&%-R_w98sZh$IATWlc-S*?TZQD?y@oX!?xAFZl!^o?Y7i zd8hyTaXUNrtJAXwFn;pc&}qVtq!CV{ZYECSxc|gI1B5L|_P);i`duV;E1Ayi(ENgi zl4i`&6XW9^yyr1SA!mDEB8pdyL;nzqnoF493p!SJ$p>^ zRuy$xbz35Gcng!ykOVqEp+wIDo z->ZJeOO+of|91En4UHGizaONjg$>ED;`w8Y7}~JmX9X&S2y)ix6L}?mLk&yd96&gY z^e_K=ahiIHC5Fa1;j6Al6fN76tY)h>m5sp|@vkNwq%vdVc4db&qoHLww+9)}V zo|T}#rYdc!&!x5fxb9f5-I6?u4U7{ZjRSht8#D5cIPsi_x^OFF15ETzCWZZ&Kdk&V z-!kCip-=h=V<#4~b=71~t1I3V?&7Q99iG;S?tT4kmo~S0!9NoQt|Fq514-f{m=EwZ zd|8rZWY!h-RPgFOBt1zDPVtONNURvYoux80uK7n;0NxgmDl^K63;HvQ`g5_3KrXiwozgX)4QzwfP~ z?@dAFpitAI=@Ng2_cCKyLZj#MitD>RCDactQ2s5@+cp^15YYk@bt=g>(99BFV1F(( z+1|H6sZN1XPoV%>ej}CnZ((7+)v>UK-C*z2Jd(-xrFn?FA7Q|7gPXk@W+&f z*7*zxYF@s4%k@3UG#c$*d-e$|S^qrry|2l@?pdvseW(AxXztw1$a=j!x9l!=Z zsTnnpueqhQKQWU%*%~tRo&QIE+xF+rwN8xn|G>J=G+T1LlAg{k>lPS`d z%)-9V4W7QeckUi1`^H0g@M1*Em$SiNw^9XFrS|Dy0#!BJCk|o`b-9$#I=z0_&?{O<+#43<-MI# z4(d4nm0OKlvTk~ZoY~r2ifermeMh&iJ$d^}0dp}A?P#z2-&I?r0wnn^x^1Fy1z`hZ z_hX>%(e;p`hk_I=LY1<1{;x@u!EOF(Jtfh@A+L6GC?C6wWq@HVtjwa}l38AAM&BvC zUOWXy@_qx)^U>c^*Wfkxha4C8?o**(5bt%%*1Psuqhfv;#lWK|UFtIW^xwO>+@G-%1=?0>x=v82YVt1R=-zSX_5RvL=5 z6&h}z*)EiKeN`RS_Lbm%!SHwc(JTiYG_o6e&-=2nvQ`PIM&bblNFl2kFaBpEgdykg z^2FCE0=1iVlriwQ$h-ADMhwtqOgJg6KY!{sAAKm#@ij$=Vl=sl*MnXt{mvX<4(wZy8VK#$`|_>y}#WbEWQWr^I5RX>5|hTjJ6dNNIBAj1Bgx&oBCy zz570}xjhKN*ohezNPIptH*6e=3FeZ?FB!@WW{iucz7^@A4;wifo+RPcW4A9QHiq9^ zZdjW?O@Y0pY0~=Nfo>2Da@SG{+T}B_tBsf*92xP80^?d?1Ji$QgtEkj`fW~>BHASh zc`G*Ws4Xr!9tGJu?nOQTaj4tY?-E1StrQ!}s-?!0LMths#61c+<&0Q@UhifZqv>dn z$+Wxvv_@!Zd|i=V?$P1?oy3b2E4J?W4k~A*RvB+Ntfzc-hIyi(i5Nd|Buj|5xB}Lr z?|3D)<3qIAb3doqf)Igi3_g0g4eO6R2cKp<_A$4s)gAK#>*>}x-+z%ueXB0JIw?w4 zFkOnq(*8Mj?sKK>3jxpaifd@%jkA?aXS4o2r2I8_!8l#ua$|m-nJ9X%tE&fce)#tU zLup|zJh5cMl3Q%v+g5(sAz-#3Li?D$fdMXt>sGH`LV?}bPG}I*tpuf_Ah(r*>HkqS zYu5~ui~pv=F||XD>oT{G(RtR{7HO+pIF$cz7^UWYF9EUpbB}ve{=U4JT+#MOuVpH? zZ>Zi9V_$M>X37;WubG;9k7L9cg^}^kl9xMLo|d929n{U}c~F?>zrln`Eenj^ols#N zSvL|~;dnX7Z*;CS!DK4RIJ>rEq$}j_^ZHu>vx!AwaKsGop)A389h$}HIP$-X5>}gk z(Q7maIQX#dF3i=h^zZ&c;+_CYeL%Zf2^(j4T2(f(_yqrd5E@kNY@0>LH^4yb;L~$9hIES2~`Ws1j2!I zQ=A}$MQ~o-+a;R8X`Y4Pcd2ouH4brZWK9^%@L8#p)@WjdEYA|&iTvxtsAN>$7q~V z54X1P9Zj7U{s>dKJvn-Mp_5nF>{5_)>xe$Z=4Dd&y|#Y1dwu3y*^+?yiSljtBaYQ3 z-3YuWQ;uHg%uIH3^jz~b7|2%Meil_dv5Xk^xir}K{EnkOOYzKWI_Q#kt?tCn)Rw`9 z0IjG7+fi2M9RrUd;s*|{ZdhtXeTuz5U51)YcDznuFkG<(uV_K|MBGlhyZ}sf;aGVA z;5}lSnMqJM!ri*-L56+T$kHg*S!ZTp@dkPI;dkQ5F3(t-?X}~`rNL>C=ZUaB(yZf6{7M3jbTcxfG$7lvji&&1b87@O6russj-SzDqnr?%) z)K@ilcEh&L<Ac&T8M1U)=%jql7Cr=pp#lGLm8U@XLO z9=42Z5&SV&Jj8&1TWlfyMxV=oGU$MT(opWqjny57Gko;se_6seYM!LhbtrxS(*21yJ7LM0y>ccdPxr9LOqY!} zZb?yG*Ev`0CKu*B8rsn&TqjJNOT=WmozDb0w!UneVqbG%sRR}k(-|0(lZn%LGx&Ub z+40!^Ekw!;oIn&$qm?1W6EU7ePVQ06CX+A;eHKK^B)t)p?nmIFyy8Nbh*`VJNvXa7 z8N}hA4Ux|=D|!-?B-~qY%wQ#aTSZ+t+^Zs!t$w);#|q6`PD+md5zC3;@BcYDA;@F) zyZrnMMvfw`85NF8G|Ra>_z$i+$G9?s`qSy>y+o0Bt*hK=@6(F7o4lu6vT;Ah1!=C! zEjKh}U;4*i-)C0ck>ltb%4XIWHS=LEE8%>@^N~b`tNL50`hiPVhGYR>p6`x%ys@sEa|E^5pmb>}t z=wy?Svl*SVq;TA`kM8wT%bL$G+@=&UnF}iHBCmSH#ZpIBz(<%5n{ucWZ!4@(x)c_Kjbdmj#hdpoEp~a@pMCh#=3w1BUEd|I}b?jN1!R0y$RV6Io37yH)jW7wL-*8glusB zN*&i7JF6rv4NXbx;Q3W7QvK{B0VR7zm{`aUXOVK-skP+T9Ct!dyL*hqO{VMHmi-J= z|03cXeLPKMwi$Lx*(EKV=ToLi{LXyCeD<;w7cSFw_FfXKGPHn`* zbez}ZT%Da{ltIF7p^mL)GmK2D4#;&{aW~BmM6B!PTYb}&rk#oY40TAub+?_&(mJMV zy-Q0knuJcivy)r-+l9QD&L{LgKZ@yL!QU({Ob)i8eR0JsbQp6*le*0|%Vhx%pTE_~ zbpB?Wi|o!=e2KEMK_2VkLU88rDZF&yn30_4irsI_r^d~nZtfBMOh5a;oi2Edva}A> zj&l5=Z*ObedL<3!1VgWV*MAr;^}6H4yY*&Al;)4PQ2&iLd^|>My*J*#fORU?UP&)K z=hORcmg-*&+ipC>2W_g;>nC3wclN(AlRCX2WuRYPKvJBtS!FFnaHLvv?WR-C%QuSZ zpYge%zSkWQ*Cv*({S`l{2x2)6BGB0ZAyb=d7BVdgA#qrM=CfWX*d%5&%zpVlqQ&PQ*%n%enA+Xl?8Y#CK%y&fWg=20cHq(~^-bnJN z30`(~SD+1l@)sN$)6GETUyoBM5~F6&Q|>~yP&4_lC=51~P2bW_>5^1EPzObF?Z(L1 z^!Jz@*iPhifSYn#HV1!<4%ha66{8d;6z1cny?v)7aQ0_IT_&tf~L!6 zy*oaT(p1K23XIx`+vProG=>kXlk<-7qtwY#q0SS2BK&|q;=o+JcI~H+AMa!9s)A1g z=G6rlihl$ttz_!_Y8I~JfCeVv`iB|SPu!|-Y*U$;nJ=i+aS=Tb@G%(uwB0-h-uOAo z^IbLsDE{K!$3DUooOoF77x%v>_rvIkz#Vi)Ka3CeHwp1F)-a3B9vc`?H9Ylj+bO2- zO{WzU)Rr7NbSRDLs=`v9JNPtYuzl0m_ci%3a)t7$)#vl{atGUK$A(M&YHJhMY9uaj zAI;@Ud1?HlT1V~1oxbb&Zq^yGt9h>}Gjv_!O>R~i;62+CET}x!J$LzXE;p4*?crVX z{A|W}|Eu}r)~riS^Y&A_(#9U8^|S;Ha8`@tf45cdm%QOOJ>Rzw(4BW~X3DBb)5fGl zvB$@@zc$`pG-g0PBs%YqgY$IxE4RzrUQ;)H|Ig|*ms7oecy6_fn$h$* zMeasVuiDY9{u<#fF};gT@vNg^*)nu?mhp{_f1;?n+@ssLrfp8FQWjZp|CqQ{$3r;0 zQ|&myL$^FWIhIto>+Id*3-|8bt3fp#i}dB#W*g~-G?So4pl_@*G&QJmsM)stR`rD- zAE`pL6>lal|EPK*2Jd*I72F1H-4$9dKI&~d1Ey)WYW~!b6mcZ+ynLILb+sSBo~}XI z!fcmhS~{o*Js_m7Nj-wee6x_Mn%W%r@oKr}^?uuq4-THeZms)zpb2t-8jue!G3R4S z8Ut>>?c0{wKRF|c@rkax?4oPt`~xG*vcRXz?oyhUgqxHNq=<@Rjz2%s*_8NmQo}i< zubqNb>=d zl=;Hx+uq*)O>^V$!~jl)b_vs1*Uci|mQ;_di&v?%Q@UIqrLI$S@M~N&U%)X{9Wl8} ziUE^HRQ-$IT!=@o_=lJU8Ek}?!>L^0bnVu&9}8$6!}#kJDD6n}x(gl{Ju`C!XkcZI z3p0AJr*-ok#L;5y5fC_=oq#n$99$rp0Ym2m0xc+{B}>Tua|y^1f<7w}!tg{N1osi- zyp``zjATNs>!G`VRRA1}i(;3XC%e84OVb>CN&%)4Yu|z2O#H&^XEuo~Z-rj&CMeFS zrHGY_06nT8K_Aou`R8I=DbExOv5$Bnu?K{MO0Hl;GYsLK-|1KUNpgP3i**JYXT?oK zHW!zb)h3w0JDON%7ZkXF@aOICzx*9M&WXeoHVvYnQgPbG)iz_tECHnoUr|t78vrD8 zC+Gvj>Pu~p;L|d=rQC0Ay#UW0jVpSHFC?cci8yPt5qai`x&P0BfeQHDfqT23*rar| zxibHP6kBZKyY=NfW-!7sV#~V*29(ePNJvO@baW6Y?Sb>DJH)vK1pEqj-F_4ry5qTB zPYmk}EJ;X&9uVTDKhqH4(r>vE?8nZ=X4T{0`B2F)B^b(g{uX8ysai7KuB*$Sy0SQD zx)k4uY{zdypDFh*8qS%WeL5i%*`@#adyR*VcfXJR3LV?wk6K16pGDSXc#ta(T8hES~|v!Js?W2*@=H? zt36YEod#cl%(q5L@Aqt(19jVWZv0QP_MvT$rA>JXsoMK;ffmPt5CC@b8Dcq7Cj@^J zvmZHG-_kJ-q!t@+r4FD%s7e^XYl?*fu&o(!m>{kWCJ`L&(^y4$CA zbj!lLhPX|S|3@K=<1`TG&`;&rq{dWfz#oGox8!UkG-Ew5q+64UL!CN}q{>*Z+0JUf zRR2m?`?oy-I51s&N?Q0eETw*N^XnBWkv};febJD*6*GnerA|XHeEOvjq5bV*<0-xK23eR5Sl*hnlE|FB8OTIV^>2VHNOR> z7kNjzriL|@_vI`JWb;0^k)^4J{!yyu&woXt&@&VAb#9b(NFL3KUXy>`WY-NUScf~sXDC06x9hYqRr z*C$0ZdqumrtTa8b44gL=Ybz_-ohMG7e9#EsDt-?BoC8bEFlz1puoZO21H%rN;Y)%+ zn$`?B?T1Y@f-lmlsnr4jq@s7#n#rFln_u%DjE%;*7hzAHoV%howT$wOxo3@^xAzMy zAt@74gipHmKROwqdz6)*Kl$rXSF9uz6<^)VRuA4h4y)~lRhmCAI!%Mv#MtNni&I-y zsg~5@p;^cT>}~sZAv@;y42bo~jUZvhn?bxqYu4L0BDCjYR-GWcEap3T9V@5r{K@K~ zHJibf(PZ{IMw}Y24oR zP)q5aIqDy3tmb^+PJhEG4f%!SW&Rve#pZ6bkX56REGw_bA*>~Y|o2!Y3y9YZ;`0)02kAXYAOHV5}m01@(se3+^ znA`f@YTr2{)aXe9D>iPV@3`*hbMZZDbcU^)blQ^`qcSc1uVe?j_kD6!=QQ7&KSxxnpPh(tL_0*PLIDwPS9yArz-~A|!yB-Vw z_(}%;nxcC%;~@!o4ocCouL-)Mgvk=l;WFL?2UOzF)ISVIgJ-*}Z4O~gEiMf60W|P8 zzFWH59uX4yb}^gu3l*I>@P^7o2N)ZTa8};Jk4`IvZ^C83Ci`~2-HEsGbtAlx2SFk= z4kE5guKn_C46rs2w4DMG_$R6^qog@*_x90qC{7+~`+QNOMN==vub1Rac7Lp$Pdw2R zs!tq>tnkzyS7m?p*(vA`iwpC7u?dgEBM zoq!QE%+|6TC~rD;@_PaOl({nUYVfi@>H4-p2lbxa%ip)H zv^?_A@QDl3eMM)!6}}N!QH7PIy8iPk`2+{cEmpPnF4A%fc*k0ed3@aB$IoqjQ_UFG z1&veLf8KgPH^bQA0CCZG7!pgTPvO<3ji9vM0IQP%&j@7N6C#ikVn6{JtG_5)wqEST zGZF|aqol-}nh${bY8ME)dInD-k`+P^gWk5Pe;7ba306b7K!sR@uCPsyYWCnmV(aVP z{0()+E7Z7+mzxS(O2&t7_r$zL*b9$sGy3zFu=}DQ5ZDi1bzLiT6~xEu^$`Yl@Pd== zkIE6IzKj8UW2#xi2ZSkAW0AR*YNVp{K&wW1hnieQR#x_|5L6r}mQH39S1t8#gU%ZQ zI>0OViM$9SbhMAQ9dbuKE`U||dVad@F2o-?}a_Imc~N)P!njOL?S$X-I`e zR)hy#Oy0zxpFzv%ce&SgD>=S>vXAfe!I&3kIj%PVaO3NWvfOXWgK9ZBTVcUQa7;xW zunJxEucqeoy}ua6J?-UVJ$~|a_gW9T`t4wQeYEqkNoOyr+1FJ5rU!cQ{kK<4`S}~a zF!Gk9n429|bbnu@z1=hAr#ye}y1o42a%VRw^qn_dkbca`y0@siq_Utn5nmkmDKy;3 zJHL9OrP4l;Yu`4HG}*atG>QXB*D~g=Z|5mp_vC!c?VO;FAzO7#<`camQDV!w!)>{{ zP2x6$|B`JAr5LhzoiGeY)nka3OtLRnM-71s@@rcMXigM)U_ek#I-^{OJss=Hi9=R* z(slR?fkDgQgif4sl`R5xol#g?gK)ie#WKkAI$pZ&;`&+vuf8eRcBGZ2vpfoitv+_0 zuuDzgir2@?yANZ_OtI-zFMlpx3Ys^fl|P!Mzp^KAAaqP}-(L&VE`{?L;q1bbfak)( zvr!?@8c~6+CYH zt#01TJlnhLh}B*^0xS2cah8_C#+OKHP^@%e$Ac73UvOK0O+6rBg0pkgocAlLv5%StN*2GxCFy&LR4Ylo|E*lL_T{_KqH0yt zB-)+oJzGC zoZcidIsVakeVzFugBZn%J+ntm{ffU2TnK*Xz5DP&P>!&G(dct`je!_3rSocSAC5aT z2-8!>e#O{(r@lG!;&yg+rsZ{kKmC8WES_FGez;igl>U=5gK{ZYW?FIIy<(mVr>^M| zu-COcvMMJ~aBVNg>|4=!5w=>Zke0X0+@7eGUR7=z!2bE8(LyIX^wPmVP6OTVbIB__ zPIQ^sPS0z+ZZ&uN%zwe|$L3Ay1$W#oX3Tw;+qEk(p=5fCy3}r6Rc5A*H3^EMk5{p9 zd2)&u4z*RU(O>cLJ@=E7X(zkXvbz)k>r77O?74;|*bvhR8LH}UGrVs2^z;uySzQi| zeXnYM`VzX{m(6C~*F^~8)PJT8W%rFOQN$w({SnQ6$w?5yTVlp;bXU1RDK1)@9*3zD zVx8M@s(muxzj}-oXhL{+IGFidmSK^%W`~(i&Oyv#``*cM_9wHVTGg_?=W?FA@71=qRlr?P#2r zT83RJhf_aTNeE}pw!BUj7*<@5y^47k!NEJlm1{(R1J6O6>5Yt=a1O}Lg5YAvghtoZ zEcgEr^&aq4_wWBWm9mm#m6c>>RZ_N$D6;pKO-AI{G>lTDWMvgu$KKm9vS-_vicnJsx+Dy6uIst3sw(Tj90r&}%>a<_hIl|x5Lh7=Lhzcs0+A2+ z|J7P|B;UMJN?~g3%_fWY5qY9Du&LWc>$^_(@TvL5{wuHcrR!1cHbhi}d$S}%6@%yd zxcpA*kvqXSU)fGaBmUvoGTloz-ii0jofKULG37b^i7=Z@5{<)>U_zoNGvB?+{<;dt zc|Tv?pr7Yov(?A1OrNZ<$Lu+FGo|}D=!oDpLT|SomPve^8vL-a{oUd@J%52DfyXouF)K4M85859K0$nnD%{bCg z5tLll(hcKLS1w9v#4Yz`bOmu{bwtiCqHJ`S&Ct9X+9zmZOpSUwRX;D7uUF1tA#);G z!_4KcG_MYNTDO4`Fu!SVF%WQJhZ&`OKtk1DLSX|IQGhr5(IfbZiQ@4!J_nI#%r8;j zy~NX?Tq~@sf=%^mAt`yyxv3lrN}HkepGvS=xr51rOYw{Co*XWDPn!!gdI9eugkcgi zj440bEv>9*%6K6RS!5#56zA(hIjx0k3Bj$K3cXxM}94b9b>(OFff>1bf-k1`rUJIUX8ymYTJ&iSYKa0B_OAI+s;o<}dkjB9EVj-Nk!X+a-we#=-+ov%>_&>k6H?~1;<`*!z4Dcy_0 z1ov*3V2Y>8#`fcgM&>T`jTnW(Apqb=uW|LPi?Cct&TMfJ8^Qj2Qj!5KKOm#P2o%-j z?@dBNp)IpDVcV>F$BAwm+kM}?Dp;OsRxgz_x0LGJwO1nr4KK+{&)&iAwW*@oEg~Gs zzkq4Id8MFG;wsG@rp}HI?%wFEtccl-C2-P%MQ;`~qS12BDW!K7>VnO$`mnKaL{i*4s~Bd&o-omQrBt^*=cU+Upk%zX#~N z|KccAY@l!kUs1wpHPB`u231%fC@(1q&=4c68ncgq*K#v9BLdFGukgfaigGbKFx=AS{L-O~jsy(vZcHvAvn?rhLNT+>Q4;}<<7;ceBCUxm zKj_=~>3h&%3%C8;BP^<=`fe3heBJ`Y0I!{~UzXuiPM)+28J*yJ%;79wI=T&UV=<^H zy+Qvr)V@H&(`}!d_3n&QfVi9y%WZ3=F7XnPUGm&T-*)noh%sH)wCNEoAKpj94m z9jM9jkqIQ2NpoZAH|eV{F1e2_ctc7uCsi%}b#to9&oRj7f&ZC&$2pblPI^6?`>pjW z#vWsWXV4k#Lao+xDC42VgD^_67%IU1JiWjWTa@WO4XHtL$9-#!)q?+6t6XI!`BR z81MiFhI}&CI!9)%m=4Wgw#^7M%Vxv~031?pt|aehtUbkBN(eX!(A=^{kXkPyERS5e z{E*R_AohvZtZAc$!t84O0K3e2?aYP=C@<2gln#}+l2fJS6?0T#cWFrjwr^U#pItiz zupOXmsiVytG7pRYS}PQp-XaOTz%Fc0&-E40p_PF@#XOub{A+Q4ePJrIOyvm^3AIB3 ziGs){;JtRE1TUU+TcfIvjq30mF5>IkU9CJ$7&>Lz0K8ZD%+#x>`?4plMW~~M55{v_ z5q@%G{1d!!1_o<8I?H+;<&dswm}zK5=XCt{tvoNE_e;(+j4Z+PU{-Ve$gkPBXaI=+ z2<`D@EpHH};+xLWjPp$CYy{1bRQkI1_32#Dw7yqaNR&lY@sJIW_D2ZzV$3 z@$&o72*h~VO?>L6Hgw?CZuju=!ZM8P0WYY;;|8palp_RdC&5560xn6uC{OQz{PXHy zZ2>3ahPWAzam}b{<>FlVe8LP|x-TqZJELz(SKE*9C3Dh&2CAhsKXD8x$a2GggS zYm6BE0Gw&lW423xlDOCkVdq_R$uy4v+O?i%a-V#8jf4nqwCq{KO3!L2_)cU*{9sm5 z`|y}h)dz}CG6yl()eurkQ;eS3LFj*vbSOO1638PhaOWGX`n1u3y6REC|9k|w)aM}l z-ns@R=Uc=za|GK2vf3qFW97phX+brm#<}FhiqISC-*AP9FN;|Srixd7^oXnI1B~p} z^YKTY6yF3VaS-$T9lPf|Ct&Hq*35gk#WkOvtEbtOprYadp!dbwB~!X`rZ#fF%qV&? zTzkqreoi`XQV=-3(+@&=J6~a7d5f1;MDn5@*jP3(3_H>&@j6uFq~)fk7o`t+KEm_- z!lnDFUryTfWOPh)VXw_xn$>o7{98gIu4g?1f?s3&6bITXXL7Myx|hExEmaIM$p4lK zepxC=k03L6K|9@^n3sz*_js=7J6!c+?FlAxHq{4gQggCe6=~>VstY z;R=esjKc&_+-3@rGDx7wA00GgOiE7Ht#Z#td4Q?8BVHuxBMZ+azNV%J1RwySs`qxE zr*mcyw&PS{VQY#!f}yrkt?!O;RXksKQd(G3_~An_F(0M3_oI5js-IxQPaCikjD7bM zhJtE{Dgp3LK};N~Iqbi_K$-QO%RikS-t2@9Z7u-59L^&|y|t~>&D7K}oU-gzsZHpt zN3o5P<_8%#%|@szv^n~rYWel4dE<$(+?o|9qZ<1)n(E^jv3G8xKi5bwqw|p)+V|q> zpD?WwFRet_#?d4Gl)W5;0X)q=iBz4T;T3RYAI;jq%`G-o%GK}w?Qp6xNG9qLdofs2 zIed)smYTOut+8TwPW+d2!;Ll)^+SZ4Jp9w^*66za2lEOVH!I3qNn->Pd&;EYH{;5c z-8_-<>6!Ysrfw9MsXG7|1Ez$DG7O%d@oNYO}c2CtmACoima4Li(p`LjD7P=J;XNW z!2^B`i^MOAU_hgz$fiDgcHxD6gs9z?C{`LB6c7N8)?R00M_mXO4}kP!ok~&54%$?u zV()R?isuX&!#+sPIC`44*ADPPjRff9fP{!!}*reEXG(CEn-43?@Pw1{xX~Hi2yZ=?^@&m*MjH z(X5@!uoO_%*kX`keakq*;FfW@Ym2um;kOK;#%RWOFLg5_)m3kE>?sA1CzXtdvh#gC z@8qsI?x9|CWMeb5g4e&25UY*8BXQxn|7k?M0{RgRO;?@#N}!rYJg^PsvpX>4*v|WS zd$+lIOMPx~ol&K14EGGdhKI)oqb;4s_7kiGByCUegnG9GP@WFDq<%9aPvp3wA+LzV zh#CJ(Cxc=ScIxjwCQU(u6FclWD18KtLwk;$)I-95>d^ zDG-l3R3=PIb(i3G9-47IeQ$BzRrnHPX^z*=A1gn)Xn(jgj0hEX1rujQJ-qmG_7O=- zzQobTOL}UB2()1IgzP> zABDGqv{TrW6SCqlPE)58A}J3$Z0ta032#2-@Z3i^#=7=)k{19V+=gqu;DSIREIG24 zScGV&IB+0r!1!cWsy!&3t{g8M0k0K4IXQW)9Fx!YF(l|#eDC)q+y>wm-HKBx_pkPT$2^eMF7N3z35}Ls6Gcv1fE$;s8F+<7K3-Qgb9Iws` zi#upjRfxVWgz-)Jsl1gSuXl~Xe&Kn1YQp`<=}tk=t>jc4P~`4jr@DeJGVF!|?!w$y z3%`lb)2K|pu(XkmA8_3!-QZiFp}t6At}Rsk-7GUl^UV#OtNNkFzxpCo93xi9RrFi@ z>zF+ph*S&RN9UAh(khcNV|Awej*Q~f>%C*^Wu2HuP1>5U8?XbC>5AIBB~HA*iBS2$ zHm#1>ySYQ+OZ}M;4p)_Acsz(XZiFu2UG_5QMC)PuWZB8_!8=1aU|h-$Rw|3zf$^Pq zC@+s!3xW=ihf-nXEDk1LUqD(od=<(7*h};oSTJnyWmu-9EV}NG+G9G|4QdebHHG+* zAK#>d=*PDa{95xIeyVa}Xr)&j9|OF}-DV%eQVkOz^a6qJsp~lu!6W1{*DfkrK8~el(sKss zA!JHc3`wW!gI<-SdffR(Do{htgNANY9+bkGn!1pf2pIyrb)b@puza$@76X=u4?aju8#3aq0u5}I4` zK%_l6?|WypYWmbnm0E>yB@Rxd3_!Fa1B zClN-I@6Fep>Z)EZ-(fO|Chr0a0q3wuM)IRQRXHlfhr&&#wa<`%CJ@IdP8wuM5i-+x|{(UJu#)q^*d({A>5s zZ0A6$%F6$*1&o9n?;;gVH{5`5b6UZin*FNuPV^GT-)aDGV}{GW)4tcEa>in_d_K2z zr9>XwHt~FUU_M4|LKGL(2hT(J6aEd*%CRygmOMuB1`vccYo}jZQ7?a57_9OmIv~K+ z>eXV2v}DK=5!0~wm*%fH$ax#{yr0l|K{g z&svqqh73?+0g_A{nT6^(vf?ybf-pr$UK50|DM9Wq$Sf>@m(rW9t$)uXk${|QNblKf zVfo+$_|nYMQakJ_+6CroIrQuREH6UDdr}8_AX5Wb8VfIPC@2SDA*(Kg5cnM}885)n zK$yglpA~rsV7C--&ySuy<%4KF1YCk(4#9>G0T1BTFL^&YY5>BICU{YbRMbpC)me*-*FJo%ke`VZ zEJ*jiEc~1URsrIHE1>{LKbZO&!LI-hZ^W4CYyf%x>m&|X`}7GyVe(*)JbcG3a5qST zNEYpBEj8-=zXw<}CRYb~24p<6fG^s--C-(@Se#a$yi%zZd zxvJj!eL8+IzT4Mj&TGMZ=r-YVcYJ&t0PW3TtHj{|4E`}-8NZ-~1!Nvf03C5cy(_3C z#?|1YHH5Plx&DP*Q&v-x%xEHHIPRhI{&`XT2h)~03Z9BmFTbLW$&0f8MxRmS92RJ? z4Q2j^?>OB)w6E?|O~ez&tu}NTL~V#M3JQYLT5pJIbT`|4-IXwsD)-7!%!ox2c8e0kvN2DB13%s>8P z-w4BreLYK0MdaCM_D2f07|75T>7BWza7l~jNsRNbCDnbY^2UO(hf<$EDbmt^LOP46 zu1qr5N`b6JQw=)6$ISub1oAWvE_%Sv-v$ReyX(w?oME#RbCf;AACSc14V@rFuZ2Kk z!1MwFS;yf&IMeO;?2O3)?@Xe>ne7mya`#FIf)|Y36G1KO{6?k2GHh6o^%r7Al7F^9 z5KtWgI%nom8!}7-YYRDzEcC+9-#`KE&f$v9h9iK)>w#28ROa`}N*Ays9Xx7?W( z4+zs)cnjQ~xs}SZg};UL+H2V8PRk(sxa_}LW01i12B91P?}O;p*L5b7Ftj3xR<4kh zaq<;HbEQrGw<81!hKCm z+;Q!#kH;W|S|>j~$a^3MC7P+) z{WKk^sez^JYYneR&eSV(c*rG%g)c-dT#${)8l(cb2?2{yaYiTNP=#G zCNl}VA+p<6bXPaFw|R@kzTSWR=Iz@l;9uO_z2NEcn10I+{8rGm&!<90&p*t-Z~uS_ z`i^uCG$1b!?a=~pjMN+SGYHs#@E}!60!>XJ$WVvg-U7w~5CUTtB;YmUfGO)9%-gq& zK{iI-pYSg8C?mc6>ANpP%rf@cjJX~TuA54CyI*E4nUwM7CMRjBY1p;xQAZ{ubOES8 z431eqdK#<+C}3Uyjc6-$cMl{b>lT}nJmJ1=7#xg(MeMe2e=r+ipv1)meRu4xAqp1Z z4ZeXpawf#!zOx$W5MQ3iy6S_uF>dIxUf~zl+b+vzl%qMwi*9q%FsRaMt;plb1=tiC z<`;?Mk1JPTG9tFefSTYBCbWs4Iga^mW&};sh#QE>5xoj@AASQ2tnJoMGzL?-zYL8z zyqC-A0pV_sywISVhWJ$blLd* z5JFFZYp;i&0m{%#>z=O^n#a$t-?#kN&>q{Zke})-LjtnUKiqu6d)Z0#$t##7w*e7PK3h)GVDP;A-`-wz$RJP z*a9JvD=a4FA*fx-aV1z}*K=oQ_Ac2A0BH&VZ^r{!$uo%Ze0>d@iEaZO<50034SdDs ziHXG8TFLtQdW3V^^l1ae6V4vHxOf7LFsf-v42ZrR-mL}LY((#)M5$r~vaka;>xfwZ z)6HO0s=4`Z2B~TRu~9c}{!{rkEYC30 zfc|JUm%h?A>F9;3-c=Enm23a!g?KXjwoXuVY{8j>&BdqIf5Hb>B&da**u}@(aQH4D z@N1{B%9akWb$_9|=bwuMcq;#`ZW>f43(uC#*jBOck~qGL3nWRJ9K_^q7l4Y%)xeeZ zJsVH@`Ow$x`ONVdQBST7E6Z7rev^9r2b8&0#Z&iQXc2h{Bq=$1 zF$jM#UZK3qfp_(?+{GC&bdQC?@?20Hu9IHS-d93I3g<>lqbfq)e?ct~beg75wU7h?oDE6ddC;tY7QVe>`f3P(8L z_Rn!65inz^fB}nQYX`=O$Q|-KAVNC_@XBRy70d$9iYMSW9*2MWEI$zAkcMq+x^(uQ z``T3`mIN;J-|!J2K}A-O!|uFBV0JExd%H*Mc!AIwS=bE;haJ#@3PD)pe}Fky5*GS0 zF1d|+u!FRUA6OTjes>6n>a6tYRq5xYM%1P z#Ij)4+v9hX<>2Xl^M##+y*wK-j@qxM{lWFRB9|L`KPPJ}v|&6$*(bsy6bZ+|S5_B%9uC``dXBQ+ zKH0b=qLpj%)t5UVXWWT31o|f)%3+v$VEl3J%DwwPZ5u!T<;&EH?;;M?yH2UJ?g@WM z4@<*IMFM(jrdBuBAK;nbT0}f*LS%>CW`Yv*l6UNKu^->?rFwj@NKv~uT!Ak)5`c;N z5qGW8SN1;l_ zwm{=D_jjQeGQq!q8U<;uAbMl=IRXo#h%w_{3IIUppq&K0g}qD6T;cTL58qsoQKh^hK-;jbWhh_OM*@Yy zy~1;5ek6R`SE#Ja-FaMsFAT>({ids%VKq-O`vMSnpN^h`0Ds2yA{q(9|CvZ4@mWn>ay1^lP8c&zYh(iYIOUH>RgUd6i$M3a+T4FM_ZBqVUb#In4i1 zA#_35?2XI|>%ahlH7pfQLGx$#CWgc4eXl2P|I=f7p54Q4VPIIE7>KQXfAd`t@iZ_B zznbp)d+CcmdSEclm^a{?-LA)n5lv!An+r7o9PtHf3j<=Ddf5#+ljp?I8XF}IFBT^0 zVpaMi9shbz;H zpt#w>GYp#7z}^Sgqyf)Y+}zv)s5&4?N`V8QR|(gnGt9x^F)_LeM{L)xuYvM^gaAV& z%Y5%%G$gaa90-*0xGF`+0~T-z%z{9Y1(@uN^{$xPL6DniSLX;#JB*kHUsr+0zUee6 z35m9Gi`wu5;FtkGlnKZ?^650ss(AM1)59T$42{^lY5;uv$*I~q#a6w0SGMKSIk+&t zv%Vfr_Vzgwhi^tj9;X_lCBoA19@*hH=Qd~3D{ ztEijJXa;+4wIBp5J`LTWW^PX38cv4DXcX-=8CZ)2L|AKR$=?je8_f zKeEZ>B<>p9pkl6CXS0*bP1WS1bp5B-#6{0lv3dl=+1GWus=CB-u$!w#;Aki54IoS9 zaW;Nu!u?}CjyuqP|FseOEX<}2v=uQ!>}}@N`uZH`ygO=23w`bhOq>ga3TI^K^M-`; zEa?N6;~bJ6uJiMuC|YM%%PJ+8zuy{B-6cu)usNTG#kCMaii54J2F8`DG|Gs;@toDG z0jkE5#p2t8SgyhOjO>kFN#j%h??AYN(2*9PRM^f01t$zR#nX{06R!yR?Hr)=c)$x^ z6t-sQHm2F%z&VKscVJc80I$UUpJW7T04N%f6u@EzkJ$+ABJAC#L$!#y!dd_1&LZ8u;R>|q$WRHNi+nKh4MFxdF<27tzy{I{ z>zRXb$f1Ew2o6okFU>>-P1&2L4qhb{#t}oiEci#WeThMGPV)fU#`3w^CX{ue&?Yn` z@qDi2qQ{#PmfXj8I;DtI)p9h&UZ9;|mlDh>4WRLZo#2g61*4W|Mx`{aN6^lLkfmfTG_J49KU`#?;Q;li?D3tObrKDMop`o z=hd#2@LXl_qP+aj>Sht`*J4K|;ZOg0pFg!Q*6E!r8=x6}v0O9ff+Gapq^tln7O7Wj zWF*(c*zlo50@BS0=3l6gmnb%}P}TN$HT)%}gNHjeu3b-WG5G^PVEWzerLpIIn_24S zR8uYd?LMT0Uc^%4+^DA((|^d>x)`}qM9DJL@HftdlX#6jYM9!rd{ru~+Fc!N>BBqc zS3`}eMyic}dJHn@AXR40R~$B>Ub*XFK*oZ;2Z$*wVVzhJ289-q2@b9!4cTN0sXl60 zmSa%k9fJtDw^WGBU@fDe^;4jn4*_g0w3~tH42D2FcUFIiy#`eI1WKXlw zwF)A*-v9-C^*cGH7ji!H0zk$Mn8K;|ebdogd^Nk*(6)Oec|dkU4#!7mpGLC%>ivn3 z$qnRpba_>NfcqjLm-m+8yxHFVA4Cu;*x4#FQRdPgBK^4on9m5z$*8$s^<$%?0%9we z*8&#B`YC>hSD`1#_(C@xvs$!uiHr%Hb9Q9h!mXjJxMunVNLXbaa-z5BTJ(6u#NxrL zvJR$(Fc54`qU3@~;vn)61_TgD)EsT=!_caAS(+*(EHW|>=2D0lM+Na_^H3O#zDnZ2 zqwN&>9ZmX2lY!T@szo5RVEZu0KW;C4td5zsQ~PHuUD&Mi=?Fzi8s!B0(u7Uh(`U{3 zq|$0L%31J=$X)+UYH=r0trer{Mws=gd1V5&D?i^wxtK5Mbsg-anpKBCKeQkLS2d|> zGV`661kFKaf>y7(g8X=mjsCV#KM(9X^U08|SHuOMr9vP@Iny&Bu-i;p7*S9aB!&*` z_`Kp|rHfA#!N0vv>4k|SDyAu325|ZG+GrlCQHz%EsTk`*TG_hwQjdL>$EErwZ22WS z7dWdSMxD6m|JNhz0F;?$FL+28_R(|q=S!vv1 z_>fI8`fEr&^zS1>@tQ^_g9zcS!TRr*^*u$pbdO|KAlbWa2uH^lq%>sQ;S%;gO2p$I z$1AN^+i0v>a-3sbJ6}XE1jc&X*Y!r&wwdDXiSxBj&(Pd$zsUdoZo@=;^K1a7ktajY z_wI^X|LJs6;C|9z5ItNnK0(qp5pasQ7myIxrh)i@!D*BJ<06UbJ?+Nx%6E-5;zjPh zU!Z?H78+EJAILlW8+{iqf_v=z6^ljdnckp}ooPK*3Okn`B1@T^-R-9V_%?I3Ep@lNAn;S+n#vn+_?9~*B`8al4d!xZ^wt&W3> zx2CovTceTwN#j0!o|buO0^fy!c{dBKD&Ext70$L9ClRSP#G13Yvq+moHC*b*1P~{@ ze2}Z|066PoA~k4)JE8l!Pz#_|TZ)v8haTu4Q4p}Z9&i?e4~pgn00*-SjbNcg%pf>S zChX=RN22T6=@C3AO=YNKU?Z-Hh*YW_@Vo_qIw-V?+u>roQIw5yhd8%JAOPMiV^LMG z5C9n$EocEjf3palZ1~1#M0e*d69~7eTC_dBi7}39?Mg8bxL1R-G?GI1f(I}pUV&NU z^xtEu%@ND4P7znv(zw9`5i(ze71=(gV(~*n1Ohv|AA#G9dfTBl^*r=<#`!_{-haLY zfxYMSGA55I2|a|%`pV&6=J~59S;&?l;>Qiivdo}rken-9(6VpWfaSkq>1 zRqL{sNFfNRn?vdApLYGWJtTTEmYA;*2=N77<1U}Qz9?R^ny|6gy!?!&{GQ{vN+Z7y zLt$M4Z@oT1V#hAL$RPBZPiAogx$HwlKU;jJ>iz5Z8N3|sza|4iSA|it!5>IC?PJLg zb_lYtz5e5se%FQ$P%C!A0pSxkPJz$VR5Z$eQnh5Z^yT#C&lRs#L?n}ML9a#>BJu-% zTPG%(WZvNxziV!5pRi}kSI3t<1DL}|uGP3DD7~wCR7#3Fw;ktc+-FKd%^)x$j(CWQ ziZ_&$q)B--Fmb$d3fFaZukDHnFV|NiPV^HBuT5z~e+RQpk|){$!H2J;`*bJ-bBxf3 z#RhO(ik+dcI3>~$HJ4kV{qvI4n_xje0U!b(WccYEpwsD#Q`SH|b%B>8!;ldM|NM4v zn%K?53ZGUmW!gjKQF{zQ;nbif*jd1Z3wiH zd(Pd#;5Paa{8%P?$2)0>!JLs$JB!1>gG!!6&pIml>HYMn`$w55?&62C$G+9B_$u;K zZEBRB@QvBpFP&j!3Do|3psqp*6KG`acg;A(OkbEQF}5g7GT_^i{tq(#o)SGsqSIe5!Bc+Ys2 zu#4z4%9EDF(_BI;|?jW!L zH1>8zVd(;GmlVOn{rS`5*X^A)%QTPHVpM9pQJ0%@FZ0LVe%Su#tooJCPi{TF(xhWb zs}_G=o?doL&r3UEiPQgk$(KDm9Uvs@L4qsazx?@47n~N$7w;qOZrAWEXs2JWsKok- zn^Gr3UDDgJVr)FoJ!}b#fL5KyGTE=(kGJp$_S>0A9iRPfIOTWrw)AvhC+NhG*W6Ojf=aTRGMLEIx2>3@y$+W`oCEezYKM&uqMy$hn-*@D%+ zKX|{-TOU#NJ3hQjdY0F?Cd<&FFa2W!3Oa1qto^WZxx*b))(c~mrKobzrW^m}LkH8B zK0Ik?B46JW+h(;`4Rb>5_Rhc|RjsyS!0;A#8lV$&q|sy?kvkcWU6vh5mPvwnSF5uG zcvrebaAH)W)wNDz{{`H})N`=?f6s{8~4C8+iQV7G#n9$OGz za|e}o{vJWqlGS9A%egzTW}L3vj$cTGlXvgT%a4ewbq)($iE!K1fKKw{D%h3&8=&MCcblG zBOCwCZ7whKwPXl?#auP^IFsB?!N|9@uRvat6ZXP})jK^?Q}d$*pUcz>|B%e6f)&E+ z|6Y544r}0)Azyg|no-c%b%UBmMn#3ZTXd5Y{=y8*7Egx@8W%7d&S25OdBfg0LQ+z0 zRLmjn=>bRkKK`rM@v$@`B`!tB!HcEeeZhp-F;~TR3px(^l1$ziDx@PPFXgwJb;O$q zLDK~}C5)|R?{U3g+8?I~6aDLxLiP~UdWx$VUv~=Qy8ZHs{BeYIf z4S+ns@bj5iw=^3zC?KN%lG6h2dx(w@hW#GjfZ#*udT5H^TFZQlDZQepc{8)TY)Mpw zQjF(svdz@0^56@nUym z8`%+83*T06)`$v;ExGx9oq*9UKhTt$?G|^m5;OjYXSLL+HDV%@iD?NTKT9TtQBRk( zrpp>Mj*EAQtxT!~1!8^?!>C{47pw5D_JYvFJt>ko%j&y90%pSE_EKx^`*+<6?Z^OL>7BoHAPgFA*jnTIBXG8fr+@!)l`k*8 zLH?Pyjx`UV#VP!M0t==A3AwCx5Ry+kz5NER$nr2#$(;sfdxJ(E{mHuGvj z6)*mWP#0t0k*re6uLwK+sj?W3=ynL1vCpe1Z243hpIm zT@38i0*|e1QHe1oB6&+&1H<|;Hntsj;2fl;Tzq}*0%~XgRkhcw`uWrQyC7qLb(=V6 zy)UmDu9Lu-1FdV6)oFz~9#xUD1s-PKyRE&@g?_xBE8KN1;_BVspQ+TRdWhTi)MMMy zl$p^V^$hpCpjpN{LqGsh)LgYKRamx$^9C2C(EBzcP#*!=jU---x^#;Hy&n#w^iSSh zL*1I=eLL93)eU)q(4fL@QlOZhPS4CBo-*}R#cu!=!19h#y}w>J)F=tUZLjr4x796q zacc%s$tyFxUkIcFo&phxKJZ~|I?wcFHy~M^N3#~6)NSHH!H3zRZ*{Lfb~2@vE8qC* zbye7dl8NC@*A4N90cUq0=wnu6gm-|Ao0)m*f|je#XYAH}cUr~o%{pvhKFt!!wOJCn zpM_OO%2O}*suhGyQ^lf&WPbpyh+4{#)Gql_W93U(IImvl1yKyBx9w7SEr`Yty&cY9tj!VNhX zgsOGu-c?VU%s+3o`)%m@WP-bn`)vQ%MH;m9408D?!UtS*psqKs{TrNHW`ExIw7HeW za)p2Tsp5)_9q1@P)&@*pM8yT|!K%j^Mw&WLAy**?mRHSo6@`-zZoL<6JGi&W%k1z4>dZ;(2kTH0BJ#%;>+>A1+ zefns=WuW+aJb&**F(c>skiS0bM)pU(Td^O#Iq4{^0j*JGkDBppig=IO`9%A>yUZgN zftHUK-DqhN{c{tUTvrYx3dlUdE&YWb8IIWRh6LJJE@tmWglr`?>!gYNPvq5uhi6<( z2iFYk$d`!&9Uj3`q&>_Z z#{WnMgu4fnFcIkSf}ZWY!P(_#gci;QH$cluXh&{{7sOA$J0H@8ho z+tlJq?mzziWm!3!YwAxw9HNY1Pt1_Oxq=*hrS^m3ZiC};s55%mIfwQ~Px`mX->|xF zEo$Pb?5Wm*d-6=BVF_0kh%W1aIaB$zRN%Tcc^{FG;GBWxN61?+)q?LN5IB(y7~Ojz zN}zVbVgnX3K`bQtldq#iaFWVI^+h*&5lOR(Z&#dueoM_jkvM_V5BJA2wlHU^Y8pw2+{fUt$LdQU+Q1>q7u7oG?3nu&}b&Tu?>*tE2= z68ho8hq1)}0Lmv+yOl<73AS>-Y-y$0t6bvb-A>a|Q#B-1#h%$Fpe%cqJ!u#FX|*Uz z^l-DwjZt4Io>%hb^_6zmeqm0Fqu1feP|HB3-ty@E3r-`N2`` zY^-f+(EYVyB%T+C7q6SB>>(yiG%M)-%!~$iHxq z?4y0#wPy24&w9^kuRDc~hnqL>b_ExRIU;+<)z=xvXaF+PrW>71`##&ZFfihf&%E_z zW;%%-Yd&dhOAqhwsW8Uy7nyQC2H1d^1pB1s{13WgDI(h52HYZFEiqg$ZWzL_wXhy{1da+ z!2G~auZt$uc=BhKCd8E7M%$@p8y=fDX&88q>pc2amS+c=e7-Ut1w8GR5&m}G??xtL zciDSDw!Nb9LeNiAjEgs6FJ@=Ro=y(;=+?1q_J7eC!Y0BrHZ5LFswrgbkz3^|+}QQV z!o`hy!j-);9vN%RiiG$YiG$ILikkgMWF&M)wzPYPSA*37&BJ*=47M98flRV2)KV_` zAR;WR79bVSjSe*BgN5eRtKeg@hX1;p4Nv;E*Hz<4L!I>f_vUG;eKFb?#YvO`fDaC&m(&`!r~*{YS5@qilkFQ#$3r~Vdo1IRFruL zpL#l05dj*2&(?du+1BhY-oSe_#jki z>iv`eP@AVF8K~tw6;BN+J*^cXIrcP{n5RDaQ5$x(3&PUG<~~zz==GU%#~hdc{-U|< zQye9Gsq)fA3PeN(|+fpSx$;v z^h%12-|IV=$m*)4b);_an$z|iIT7Zak;fjFT);5I4~?%${Kdt)VW7CZndS^^BxV0a zsVGxpZ>#prZ&~n4YD~GAKK<=!aW=sJ)JP+_#6Z6`OrUO`857BtXFEnFJJ#jsVDe(w z;5MhO#|+-`pwA|`vd_yQiVwf%d|JycRZV&`8gVL07XO)VoR*$(yPe=th8TGmY-4|pv>8VQvfsw*h z)tNYXmV=h#-g0t97KUqO5F#0num@O&_af!jHoW}{6PR5lqRrfP6MjlU|EL}J_IC&< z0~eScsJ<*u9q859V5I z-{n#|Tf2?T-dVlzb3Ui*3h}S66dPbXLYKc@w~g0}&w&M=Rd|0$_^MQ<`FKDQ?Fw)< z^izN=&fsu{)i1jz3_sV zqiCmnN+yK{&8ct@^ke|C^tA}>s=v$|!0_Cf8}QlWAn6rQc4`n~>>VVJu= zp-i;*#dmG+PNLznsyiM2#;9tP*PE#J#lo4v-k0xawCMu)S6Y1G1Y{H*vI94d0Lh@% zfDF;EC3pC3ye`+S2GfCXWY_Q+3mgtW_j0wS1}nN^BTb4u7y$Yf=H$JK4sFCjIF6v81wP-K;aE?UO7EIy`Lne zL_;ucEg`<~fgCTetK6`S*0lWxGC~2P)YtqDF-5Sdq-$y;;^%5MC^)Ocu7Cw&Fmp+I zchVEiOR)x?vuOVV4bU97+OUyI5Z9C1z z4~;oIv^C3O@E!1_txJqgR#w>SKo~RtJi9q&+Nfq@c2v_qR#1HqMl&zV+bc!F3b5=?i?XElE5yoLN1-!d5-(rMsCkC9FI|W1qbGu~s+0j~37%~k(!v7*klbc$ei})i%$R+O#ld>jU;9*%_OYK_ zLIlNqXveredFI5sA5<#PD-85r*KGdT{s5l*Qhm#V7;~QJYEbN$Cj_$%I)!Aq?%;!K zCNyXSPi0Mo&PmfyIkKxZw@JFM+gVmsRQ2eKOII#UT@CXnc0+aX%wAZ^V7^u^m_MIt z>(X6?bD%_88Gb{YH;xv?{+}cGH4+X}w(jHG;q&Sp-9%~TDZ~=vW-RhZrKGJ??6_$) zJJq^Q-fn9J5poQP`n}n7?cOJK36S0T0)istwA32CfR#hjXLf71zRg^kq_ZZHm^Z6d zeqktIC~4ih|INJidF1cVxvG&DtmVlhf9IbL-S(@zrQN~nZ-|M=D|Lb%NZI44>1+b` znE!;OhzJYRCx=46RP1u%<)am?SH^nHw~cW;d!F&Cv6vQ)DHNZgE0Li#O@WwyS@GW9 z9zN`rR#s9{>L+`ERF!@sB_*=;^~lvTS&)(H^c_|JprUrbrMG+wwwh12McfG3R{gmI z1^`g>r?((L&z^;)rE|B~4zl&PmcsuSG(R!?rc(LsBevL*Y{Nzct0t1j3HC>~#)A=O zAKX=+{X4YN6EK$+M>8#}`}3l;L#FAh{cJTE63yT^Q4hRVo70pem-rW8s&+T{4;ry6ggdB~4Ad zhHvZc`ly;71K*D9OU-T;Z- z<61@Hx8uZhMus*%BT+|wZ+}0UnV|i(dx}R*;(PP3c#%#ylTT={LS$Qjf<9q<`f1CX zFo^{!7XD3KPn7@Fd(KR&#zM})%ONvX;T(qE!LjJm!4}gxg;BIx!NUpP_k}!WF+PQA zCD?IgqC4KLpGJRjzx|2yKbiXp9KrRi3JZVa{BIq#J_Kz>$K61WKJ}bisz(V2W`W2D zLEr>Ms7V}GmH zgf`VzzeNc9pW8{hxhX?xA5?q|&NCj5yiY-+`+g`N?O_S7ThC^+P4A_CuNRv73mJc- zi(iKm+>V!o9Sa4i&nmKCGq?SAUm5Fku(G>^S}F?cnHX^zopO(}Fbs0_z&;v{xF{Jy zNc75H;{Kv+dr--zvz_)Og}0&Zf;jTb)mbSnJV@*+0D>g@@v*&{ZHckD#`IK4oBg3I z=M;(t-uf!wD0(1Z3fc_FxEBCvBC^^J#<6$r-f1)=LGKRF&}gU*tzBJgkg(d(p$K=P z4Y=<9r9Dl(wao(l8%Cw@c>9cvQn+58kbLoft?2p3P z9&-3|T^?Ik;4DH?gh zKY#uKWZS}D_Zy%7*MU(&sx(BZ3?v+TE>l<(YpGQoI^x}AmKM`LmhERs*GvpQ=ykx=reiRI6#-iObDkv)j{@T(<$51Y^33j$RL1cg+=?DGx;uX24T zwl|xP4X+;*Ti6!%$61~Ud1m^(t|J-p6Yg(9i&p<#&3vs~vIR>tU{TR|97Fl~V=W64 zo_6uu=;XW{oO@P8lYO(r`EOR1^TIR(Z ztS;IlexPvr2%+#9-!u2JU@i7m{bU<%1sWjNhkoi-PM(R7*3(e`b= zQN1j1pq}}E=d$8zu5hX2*A(Ki2c2$u4}wn(tO_XEb0vxgq(5+oQJ#Wexyp&yK?+usUTeUZ2OUiP8OF|u#wMJNsfg6E^?9;FDfOK62(VGXqP8-KdH;% zEpl@V#D4dZ2eCJZcmqeX2F_CwYWSjEEUsHd#?&qkzWdrYR>Z{rX^njoz3`*4ks{BR zM7VaR(3Rg_-1nSo+*6BF1q+kCmy~JTevQ0;QhGC@-H0vK;Em6sO>#ycj=~_=@=4`IP7hi zAKKn_g`Lc;+=3T#`N`a~EY063-1=VWouoLRIq-#_HYp<3%f1kkjqiSCn@%gpE zrfQ7cj1Ps>=mr$O3}cKqnADn|3WmqVz8f}pYYcE0ywxMl&z{xw_fyeCJ<94~vkl-> zPqm&}g$lO@I~Q-k^5G!+9dK1We_k|JFq`jdCw~CLr8h)@93HLuxeaZDI>h^UZ(b=I zy23j#ii%$Z-aVEArnCs`8Jh zy_e@0)2XL8|9A2rcD*&Nf*>IqbD}+4krJ)Gkj{3Xeq-tXW9luSs?NUmVUSc(x)DX` zl5Pb7>AZ9)-Q8UxD2O0k(s1eSmXL0wySp3Sb9KJI|GO6G3^VI;(ev47?`J1F9Xq z%9QD!Z(}gq_mFu`1&jpBdAY+3w0u^F<-9W&%0D|y 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

      |S3~a>WIMvZ0&B?vK4&k*j)`1o8p(VudwaZ6? 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%0Dii^?3dgMamPBVb4AIPL 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, makes code fail right away if not true, commit 00a10bd7 Dec 7 2022 + 05) run_oneHRU.f90 and run_onGRU.f90 correct error readout id’s, does not effect code results, commit d3904b51 Dec 6 2022 + -06) Jacobian fixes, computed in various subroutines and then included in computJacob.f90: + 1) Fix bulk heat capacity depends on frac ice/liq if updateCp, not needed it don't updateCp + 2) Fix thermal conductivity at snow soil layer interfaces depends on frac ice/liq (ssdNrgFlux) if updateCp, not needed it don't updateCp + -3) Fix soil layer and aquifer transpiration depends on canopy nrg and wat (canopy transpiration), no effect if banded Jacobian + -4) Fix aquifer recharge depends on soil drainage from interface above + -5) Fix soil infiltration at surface depends on all layers below and above water and temp, not huge effect (but some) if banded Jacobian +c-07) Jacobian, scalarCanopyLiq derivatives were getting overwritten and thus zeroed out in calculations, commit cd5002c Jul 6, 2023 + 08) Throughout, made “indian bread” terminology for NaN say it’s not a number for advised clarity, and there might be other other spaces and comments changed (e.g. tabs deleted and comments deleted or clarified), does not effect code results +c-09) flxMapping.f90 flux mapping of soil resistance as an energy variable corrected (was missing and messed up splitting), commit 315583df June 5, 2023 +c-10) runOneGRU.f90 fixed basin aquifer recharge was summing incorrectly the HRU soil drainage instead of the HRU aquifer recharge, commit cd6f07f1 June 20, 2023 (only affects basin aquifer recharge so does not influence results except this basin variable) +c-11) read_icond.f90 canopy water only initialized to be 1e-4 positive at the start of the simulation if it is smaller (through canopy liquid), commit c0f7fa26 Jan 30, 2023, and commit 0f2e9df2 Aug 15, 2023 + The canopy water was being bumped up to at least 1e-4 at the start of every substep. + 12) Build with cmake now, with build options for no Sundials (BE), Sundials, Actors, Cluster, Personal Computer, Debug, Release, NexGen, and combinations of these. + NexGen and Actors do not currently work togethere. + set(CMAKE_CONFIGURATION_TYPES BE BE_Debug BE_Cluster BE_Cluster_Debug + BE_NexGen BE_NexGen_Debug BE_NexGen_Cluster BE_NexGen_Cluster_Debug + BE_Actors BE_Actors_Debug BE_Actors_Cluster BE_Actors_Cluster_Debug + Sundials Sundials_Debug Sundials_Cluster Sundials_Cluster_Debug + Sundials_NexGen Sundials_NexGen_Debug Sundials_NexGen_Cluster Sundials_NexGen_Cluster_Debug + Sundials_Actors Sundials_Actors_Debug Sundials_Actors_Cluster Sundials_Actors_Cluster_Debug) + 13) Sundials has options of IDA and KINSOL, ACTORS, and we added BMI/NGEN -- adds new files and some compiler directives in code + New decision choices in num_method + num_method [numrec or kinsol or ida] ! (07) choice of numerical method + Choice 'itertive' is backwards compatible to numrec + For compilation under NexGen, main driver is BMIed and the code is able to read NexGen forcing + 14) Added possible parameters for adding more steps for BEXX and Sundials tolerances, commit 0619a403 May 31, 2023 + be_steps | 1.0000 | 1.0000 | 512.0000 + relTolTempCas | 1.0d-6 | 1.0d-10| 1.0d-1 + absTolTempCas | 1.0d-6 | 1.0d-10| 1.0d-1 + relTolTempVeg | 1.0d-6 | 1.0d-10| 1.0d-1 + absTolTempVeg | 1.0d-6 | 1.0d-10| 1.0d-1 + relTolWatVeg | 1.0d-6 | 1.0d-10| 1.0d-1 + absTolWatVeg | 1.0d-6 | 1.0d-10| 1.0d-1 + relTolTempSoilSnow | 1.0d-6 | 1.0d-10| 1.0d-1 + absTolTempSoilSnow | 1.0d-6 | 1.0d-10| 1.0d-1 + relTolWatSnow | 1.0d-6 | 1.0d-10| 1.0d-1 + absTolWatSnow | 1.0d-6 | 1.0d-10| 1.0d-1 + relTolMatric | 1.0d-6 | 1.0d-10| 1.0d-1 + absTolMatric | 1.0d-6 | 1.0d-10| 1.0d-1 + relTolAquifr | 1.0d-6 | 1.0d-10| 1.0d-1 + absTolAquifr | 1.0d-6 | 1.0d-10| 1.0d-1 + This is backwards compatible to give default values if not put in. + 15) ascii_util.f90 memory leak, commit 44933953 May 9, 2023 + 16) Took out all calculation of numerical derivatives in flux routines, commit 9e5b703 Jun 28, 2023 + We can do that better with Sundials and a lot of them were wrong/not completely calculated. It made some of the flux routines very long to include that. + So now model decision choice + fDerivMeth [analytic or numericl] ! (08) method used to calculate flux derivatives + refers to whether or not you want Sundials to use the provided analytical Jacobian or a finite difference one that it calculates. + (the numrec num_method choice will not have numerical derivatives as an option). commit 9e5b703, Jun 28, 2023 +c-17) Use dense matrix as default with vegetation (so transpiration derivatives are accounted for). commit 8d15e4e2, Aug 7, 2023 + 18) Soil matrix compression per layer and total (mLayerCompres and scalarSoilCompress) are now outputted as averages over the data window (kg m-2 s-1) like all fluxes are done + Soil matrix compression is used in the balance computations, so to have instantaneous values outputted did not make sense. Does not affect solution. +c-19) If split to a scalar solution, soil compressibility was outputting as 0. Refactor for BE >1 fixes this since save inner splitting steps. + Or, if wanted to fix the old code would need to modify part of varSubStep. +c-20) Need to compute dTheta_dTkCanopy off of trial canopy water instead of previous canopy water, affects Jacobian and temperature adjustment in splitting operations, commit 19fca2ba Jun 7, 2023 +c-21) Flux modification flag was not initialized in varSubstep, commit 312004fd Sep 20, 2022, and commit 0c5af7db Aug 11, 2023 +c-22) SWE mass balance error should fail based on tolerance absConvTol_liquid*iden_water*10._rkind, not 1e-6. commit ? Reza changed this around June 16, 2021. + This will not affect solution, just might fail the test (and kill, throwing the error "SWE does not balance" at a different time/run) +c-23) After new snowfall, need to update the volume fraction water in the top layer of snow from changed liq and ice if there is a layer of snow, commit 9943858b Jan 30, 2023 + This is true for canopy water and sublimation also, commit 4ff60baa Jan 30, 2023 + All layers have their water updated from their liq and ice at the start of the next step, so this just affects the water output (not the solution) +c-24) Remove post-processing that changes solution to perfectly conserve mass and push errors into the state variables + -25) The residual vector is now quadruple precision. Change was by Reza, sometime 2021. Makes a difference when residuals are large in step direction (I'm seeing differences especially in temperature) +c-26) Wrong precision for parameter used in canopy air space, fixed in Sean's refactoring + -27) Reorder terms in residual calculations to have (paramTrial - param) so if same will give a zero, and to be more like the prime construction, commit 5f5a6f1a Aug 30, 2023 +c-28) Check upper bounds for water fractions (ie 1, or saturation) in feasibility checks, so will cut step if infeasible. commit a58ec0d1, Aug 31, 2023 + Before, when the solution went over the upper bound, usually a residual was large, and because of the post-processing, the residual make the state vector very off which resulted in a failure to converge. + Now, should be more efficiently catching these errors. + 29) Added buffers so IDA can deal with small negatives as agreeing with Sundials theory on how much error is accepted, commit cf659c5e Sep 12, 2023 +c-30) Made zMax increment on temperature and matric head 10 instead of 1 in a timestep to allow for more rapid changes, such as at after a cold start (and other times comes into play, results in more stable solution) commit cbaa747b Sep 11, 2023 +c-31) Better to have large residual than NaNs (and failures) in residual, changes for canopy energy commit 19c9bc7 Aug 18, 2022, and commit cf659c5e Sep 12, 2023 +c-32) First flux call fluxes need be added to the mask in the first flux call, otherwise can delete values if splits to scalar solution and solves canopy air before canopy (and canopy fluxes), commit 3637f3a0 Oct 13, 2023, and commit 2c018c10 Oct 18, 2023, and commit b5656281 Oct 19, 2023 + 33) New variable for output, meanStepSize (seconds over data window), commit 02baeba0 Oct 17, 2023 + +AFTER tag of v4.0.0-exp-Sundials + 01) Enthalpy formulation, new decision (renamed from howHeatCap) + nrgConserv [closedForm or enthalpyFD or enthalpyFDlu] ! (30) choice of variable in energy conservation backward Euler residual + refers to if you want the numrec or kinsol residual to be computed with closed form heat capacity (does not conserve energy) + or enthalpy finite difference, with enthalpy calculated in the soil with the analytical solution or the lookup tables. + Decision of numMethod=itertive will give give set nrgConserv=closedForm for backwards compatibility. + 02) New output values of the balances, inputted to the outputControl.txt as: + balanceCasNrg | 1 + balanceVegNrg | 1 + balanceSnowNrg | 1 + balanceSoilNrg | 1 + balanceVegMass | 1 + balanceSnowMass | 1 + balanceSoilMass | 1 + balanceAqMass | 1 + The balance*Nrg are in units of W/m^3 and the balance*Mass are in units of kg/m^3/s. + 03) Cm derivatives, will be used if needCm is on in eval8summa or eval8summaPrime. + 04) Sean's refactor and object-orientated work, numerous and ongoing changes affecting conciseness and speed. +c-05) six allocation errors commits c0624a68, b2709388, 0b921c65, a23ac832, e43c766d, 6d3ac180 Feb 21, 2024 +c-06) two uninitialized variables fixes, commit 11a47b2b, 6b130053 Feb 15, 13, 2024 +c-07) snow water upper bound should be 1, not iden_ice, commit 265721b5 Feb 13, 2024 + -08) constraints now just scale the state variable they effect, commit 24bae32c Feb 13, 2024 + -09) constraint corrections for state variable changes in liq_layer vs wat_layer, commit 523774e1 Feb 15, 2024 + 10) Kyle's actors work, ongoing changes + 11) Added parameters for model control IDA, default values are fine +idaMaxOrder | 5.0 | 1.0 | 5.0 +idaMaxInternalSteps | 500.0 | 500.0 | 999999.0 +idaMaxErrTestFail | 50.0 | 10.0 | 50.0 +idaMinStepSize | 0.0 | 0.0 | 3600.0 + 13) Added decision aquiferIni, where default is fullStart as a full aquifer, but may use emptyStart for RMSE calculation. +idaMaxStepSize | 0.0 | 0.0 | 3600.0 \ No newline at end of file diff --git a/docs/assets/readme.md b/docs/assets/readme.md new file mode 100644 index 000000000..94af681d5 --- /dev/null +++ b/docs/assets/readme.md @@ -0,0 +1,19 @@ +# Explanation of figures + +Explanation of NAmermean_NAmerRMSE_differences.png and the mean of these points in figure MeanNAmermean_MeanNAmerRMSE_differences.png. These are results of runs for 6 years (52,608 hours) and 517,315 GRUs. These simulations were run to understand the nature of the code changes listed in file listedChanges.txt. + + +## The second half of the legend is what is plotted and the first half is what the RMSE is computed against. Most of them are ``be1`*`, the new BE1 code with various bug fixes taken out and the heat capacity (change #3) not updated on iterations, compared against dev, the old develop branch with a banded Jacobian, `devFix`, the old develop with the changes marked c in the change list all made, except #30 (do not increase Zmax), `devFixZ`, the old develop with the changes marked c in the change list all made and #30 (increasing Zmax), and `devFixZM`, the old develop with the changes marked c in the change list all made and #30 (increasing Zmax), as well as the as the max number of backtracks allowed till we just accept the solution increased from 5 to 100. We note that all dev* do not update heat capacity, and this makes a large substantial difference to the solution as well as improvement to the energy conservation as discussed in the energy conservation paper [ref]. + +The biggest RMSE is the blue, the new `be1` but the heat capacity not updated on iterations (change #3) compared to dev with none of the changes (and also does not update heat capacity). These are the most different solutions, as would be expected. + +Pink is the smallest RMSE, from the new `be1` with the Jacobian terms I fixed zeroed out (changes #6 and #7) with the RMSE compared to `devFix`, both solutions not updating heat capacity. + +Next smallest is usually is the red, the new `be1` with a lower precision for the residuals (the new code has quadruple precision, change #25) plotting over the with the RMSE compared to `devFix`, both solutions not updating heat capacity. (Note that the green dot should not be in the legend as it was a repeat of the red.) + +The pink and the red I see as sort of a baseline for how close we can reasonably expect the solutions to be — we can’t do better than this. We can see the mean of the solution (y-axis) changes from the blue to the red and pink solutions even through we are just changing the Jacobian and the residual precision. Note that even changing the order of the terms in the BE1 solution residual changes the solution, see change #27. + +Next are the brown and purple, purple is new `be1` where we change the constraints in how much we allow the temperature to change in a substep (Zmax)-- it was 1 degree and we changed it to be larger because it was found that was to constraining to get good convergence and stablity (change #30) (we fail if we remove the Zmax constraint completely), with the RMSE compared to `devFixZ` (`devFix` with the Zmax increased). Brown is the opposite direction (mean will be `devFixZ` and RMSE will be to `be1`). All solutions are not updating heat capacity. We see that these solutions basically plot on top of each other in the mean point plot, lending support to the idea that increasing Zmax stabilizes the solution. + +Next is the orange, the new `be1` with the Zmax increased as well as the max number of backtracks allowed till we just accept the solution increased from 5 to 100, compared to +`devFixZM` (`devFix` with the Zmax increased as well as the number of allowed backtracks). These solutions are substantially more different than the above, suggesting that increasing the number of backtracks makes the solution more unstable, but also that the changing the number of allowed backtracks changes the solution. \ No newline at end of file diff --git a/test_ngen/readme.md b/test_ngen/readme.md index 83a516e33..50f368fe1 100644 --- a/test_ngen/readme.md +++ b/test_ngen/readme.md @@ -1,5 +1,5 @@ # SUMMA case studies -This folder contains a case study to show how a typical SUMMA setup looks in the NextGen seti[. The folder serves a double purpose as a way to track default versions of certain input files, such as the Noah-MP tables and spatially constant parameter files. These files are in /settings/meta. +This folder contains a case study to show how a typical SUMMA setup looks in the NextGen setup.The folder serves a double purpose as a way to track default versions of certain input files, such as the Noah-MP tables and spatially constant parameter files. These files are in /settings/meta. ## Settings diff --git a/utils/SUMMA_merge_restarts_into_warmState.py b/utils/SUMMA_merge_restarts_into_warmState.py new file mode 100644 index 000000000..946bfd86e --- /dev/null +++ b/utils/SUMMA_merge_restarts_into_warmState.py @@ -0,0 +1,47 @@ +# Combine split domain state files (with 2 dimensions, hru and gru) +# Modified by W. Knoben (2021) from A. Wood (2020) +# ----------------------------------------------- + +import sys, glob +import pandas as pd +import xarray as xr + +# --------- arguments ----------- +''' +print("found %d args" % len(sys.argv)) +if len(sys.argv) == 5: + stateFileRoot = sys.argv[1] # eg './hstate/wbout_restart_' + startDate = sys.argv[2] # eg '19910301' + endDate = sys.argv[3] # eg '19920301' + Freq = sys.argv[4] # D (daily) or MS (monthly) +else: + print("USAGE: %s input_filepath/root startdate(YYYYMMDD) enddate frequency_of_states(D or MS)" % sys.argv[0]) + sys.exit(0) +''' +srcPath = '/project/gwf/gwf_cmt/wknoben/summaWorkflow_data/domain_Nelson/simulations/run3_be4_make_ics/SUMMA' +srcName = 'run3_be4_make_ics_restart_2017123123_*.nc' +desPath = '/project/gwf/gwf_cmt/wknoben/summaWorkflow_data/domain_Nelson/settings/SUMMA/' +desName = 'warmState.nc' + +# --------- code ----------- +# find the files +output_file_list = glob.glob(srcPath + '/' + srcName) +output_file_list.sort() + +out_ds = [xr.open_dataset(f) for f in output_file_list] +hru_vars = [] # variables that have hru dimension +gru_vars = [] # variables that have gru dimension + +for name, var in out_ds[0].variables.items(): + if 'hru' in var.dims: + hru_vars.append(name) + elif 'gru' in var.dims: + gru_vars.append(name) + +hru_ds = [ds[hru_vars] for ds in out_ds] +gru_ds = [ds[gru_vars] for ds in out_ds] +hru_merged = xr.concat(hru_ds, dim='hru') +gru_merged = xr.concat(gru_ds, dim='gru') +merged_ds = xr.merge([hru_merged, gru_merged]) + +merged_ds.load().to_netcdf(desPath + '/' + desName) \ No newline at end of file diff --git a/utils/readme.md b/utils/readme.md new file mode 100644 index 000000000..afe8e7b69 --- /dev/null +++ b/utils/readme.md @@ -0,0 +1,3 @@ +# utils folder +Helpful scripts for a variety of post-processing purposes: +- `check_bit_4_bit_withTol.py`: checks for differences in output files to a tolerance - `concat_groups_split_summa.py`: concatenate the outputs of a split domain summa run into fewer groups - `convert_summa_config_v2_v3.py`: convert SUMMA v2.x configuration to SUMMA v3.0.0 - `gen_coldstate.py`: create a vector cold state file for SUMMA from constant values - `hist_per_GRU.py`: visualize statistics per GRU as a CDF or histogram - `largest_error_attrib.py`: find GRUs with largest errors - `plot_per_GRUMult.py`: visualize statistics per GRU as a geographical map - `plot_per_GRUMultBal.py`: visualize conservation balances per GRU as a geographical map - `scat_per_GRU.py`: visualize statistics or balances per GRU as a scatter plot or heat plot - `subsetGRU.sh`: subset out a NA HRU forcing, parameter, and attribute files where GRU matches HRU - `SUMMA_merge_restarts_into_warmState.py`: combine split domain state files (with 2 dimensions, hru and gru) - `summarize_logs.py`: summarize all SUMMA logs in a folder to see if batch runs finished - `timeseries_to_statistics.py`: loads timeseries of simulated variables and computes a variety of statistics \ No newline at end of file From 37ba2aff48cf6daece167de6719b7c80db946c65 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 29 Jul 2024 17:28:54 -0500 Subject: [PATCH 1361/1472] utils, was wrong file --- utils/plot_per_GRUMult.py | 59 +++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/utils/plot_per_GRUMult.py b/utils/plot_per_GRUMult.py index 61be8fdab..9cc1e2675 100644 --- a/utils/plot_per_GRUMult.py +++ b/utils/plot_per_GRUMult.py @@ -74,12 +74,13 @@ if more_mean: # extra vars in a balance file plot_vars_exVar = ['scalarRainPlusMelt','scalarRootZoneTemp','airtemp','scalarSWE'] + #plot_vars_exVar = ['balanceCasNrg','balanceSoilNrg','balanceVegNrg','balanceSnowNrg'] viz_file_exVar = 'exVar_hrly_diff_bals_balance.nc' plt_name0_exVar = 'SUMMA-BE1 temperature heat eq.' plt_nameshort_exVar = 'BE1 temp' # identify method here plt_titl_exVar = ['rain plus melt','root zone temperature','air temperature','snow water equivalent'] leg_titl_exVar = ['$mm~y^{-1}$','$K$','$K$','$kg~m^{-2}$'] - maxes_exVar = [5000,280,280,100] + maxes_exVar = [3000,290,290,100] if one_plot: plt_name0_exVar = plt_nameshort_exVar # Specify variables in files @@ -101,7 +102,7 @@ if stat == 'kgem': maxes = [0.9,0.9,0.9,0.9,0.9,10e-3,0.9] if stat == 'mean' or stat == 'mnnz': - maxes = [100,1700,2000,8,5000,10e-3,100] #[80,1500,5e-5,8,1e-7,10e-3] + maxes = [100,1700,2000,8,4000,10e-3,100] #[80,1500,5e-5,8,1e-7,10e-3] if do_rel: maxes = [1.1,1.1,1.1,1.1,1.1,10e-3,1.1] if stat == 'amax': maxes = [240,1800,3.5,25,7.5,0.2,240] #[240,1800,1e-3,25,2e-6,0.2] @@ -387,7 +388,7 @@ def run_loop(j,var,the_max): sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) sm.set_array([]) if one_plot: - if m=='diff': + if m=='diff': # only works if diff is last on list cbr = fig.colorbar(sm, ax=axs_list[r*len(method_name):(r+1)*len(method_name)],aspect=27/nrow,location='right') cbr2 = fig.colorbar(sm2, ax=axs_list[(r+1)*len(method_name)-1:(r+1)*len(method_name)],aspect=27/nrow,location='left') cbr2.ax.yaxis.set_ticks_position('right') @@ -415,35 +416,57 @@ def format_tick(value, tick_number): if m=='diff': # Customizing the tick labels cbr.ax.yaxis.set_major_formatter(ScalarFormatter()) + + # lakes + if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) + else: # extra mean/amax variables for i,v in enumerate(plot_vars_exVar): vmin,vmax = 0, maxes_exVar[i] - if (v=='airtemp' or v== 'scalarRootZoneTemp'): vmin,vmax = 260, maxes_exVar[i] - norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) + if (v=='airtemp' or v== 'scalarRootZoneTemp' or v=='balanceSoilNrg'): + #vmin,vmax = 260, maxes_exVar[i] + my_cmap2 = copy.copy(matplotlib.cm.get_cmap('inferno_r')) # copy the default cmap + my_cmap2.set_bad(color='white') #nan color white + vmin,vmax = (273.16-(maxes_exVar[i]-273.16)),maxes_exVar[i], + norm2 = matplotlib.colors.TwoSlopeNorm(vmin=vmin, vcenter=273.16, vmax=vmax) + else: + norm=matplotlib.colors.PowerNorm(vmin=vmin,vmax=vmax,gamma=0.5) r = i//ncol + base_row c = i - (r-base_row)*ncol m = 'exVar' # Plot the data with the full extent of the bas_albers shape - bas_albers.plot(ax=axs[r,c], column=v+m, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) + if (v=='airtemp' or v== 'scalarRootZoneTemp' or v=='balanceSoilNrg'): + bas_albers.plot(ax=axs[r,c], column=v+m, edgecolor='none', legend=False, cmap=my_cmap2, norm=norm2,zorder=0) + else: + bas_albers.plot(ax=axs[r,c], column=v+m, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) stat_word0 = stat_word print(f"{'all HRU mean for '}{v+m:<35}{np.nanmean(bas_albers[v+m].values):<10.5f}{' max: '}{np.nanmax(bas_albers[v+m].values):<10.5f}") axs[r,c].set_title(plt_name[i]) axs[r,c].axis('off') axs[r,c].set_xlim(xmin, xmax) axs[r,c].set_ylim(ymin, ymax) - - sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) + + if (v=='airtemp' or v== 'scalarRootZoneTemp' or v=='balanceSoilNrg'): + sm = matplotlib.cm.ScalarMappable(cmap=my_cmap2, norm=norm2) + else: + sm = matplotlib.cm.ScalarMappable(cmap=my_cmap, norm=norm) sm.set_array([]) + if i==len(plot_vars_exVar)-1: + pad = 0.05 + elif i==len(plot_vars_exVar)-2: + pad = -0.05 + else: + pad = -0.3 if one_plot: - cbr = fig.colorbar(sm,ax=axs_list[r*ncol:(c+1)],aspect=27/nrow) + cbr = fig.colorbar(sm,ax=axs_list[r*ncol:r*ncol+c+1],aspect=27/nrow, pad=pad) else: - cbr = fig.colorbar(sm,ax=axs_list[r*ncol:(i+1)],aspect=27/nrow) + cbr = fig.colorbar(sm,ax=axs_list[r*ncol:r*ncol+c+1],aspect=27/nrow, pad=pad) cbr.ax.set_ylabel(stat_word0 + ' [{}]'.format(leg_titl_exVar[i])) - # lakes - if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) + # lakes + if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) @@ -476,15 +499,21 @@ def format_tick(value, tick_number): if one_plot: ncol = len(use_meth) nrow = len(use_vars) - print(ncol,nrow) # Set the font size: we need this to be huge so we can also make our plotting area huge, to avoid a gnarly plotting bug if 'compressed' in fig_fil: plt.rcParams.update({'font.size': 33}) - fig,axs = plt.subplots(nrow,ncol,figsize=(15*ncol,13*nrow),constrained_layout=True) + if more_mean: + fig,axs = plt.subplots(nrow,ncol,figsize=(16.9*ncol,13*nrow),constrained_layout=True) + else: + fig,axs = plt.subplots(nrow,ncol,figsize=(15*ncol,13*nrow),constrained_layout=True) + else: plt.rcParams.update({'font.size': 120}) - fig,axs = plt.subplots(nrow,ncol,figsize=(67*ncol,58*nrow),constrained_layout=True) + if more_mean: + fig,axs = plt.subplots(nrow,ncol,figsize=(80*ncol,58*nrow),constrained_layout=True) + else: + fig,axs = plt.subplots(nrow,ncol,figsize=(67*ncol,58*nrow),constrained_layout=True) axs_list = axs.ravel().tolist() fig.suptitle('hourly statistics', fontsize=40,y=1.05) From fc6a2ca74dfa4d0ff0fbcaa7745a794ad35aed74 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 30 Jul 2024 00:11:21 -0500 Subject: [PATCH 1362/1472] fixing list of changes, no code change --- docs/assets/listedChanges.txt | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/assets/listedChanges.txt b/docs/assets/listedChanges.txt index 0e00fefaa..3ce5723d6 100644 --- a/docs/assets/listedChanges.txt +++ b/docs/assets/listedChanges.txt @@ -79,14 +79,15 @@ c-30) Made zMax increment on temperature and matric head 10 instead of 1 in a ti c-31) Better to have large residual than NaNs (and failures) in residual, changes for canopy energy commit 19c9bc7 Aug 18, 2022, and commit cf659c5e Sep 12, 2023 c-32) First flux call fluxes need be added to the mask in the first flux call, otherwise can delete values if splits to scalar solution and solves canopy air before canopy (and canopy fluxes), commit 3637f3a0 Oct 13, 2023, and commit 2c018c10 Oct 18, 2023, and commit b5656281 Oct 19, 2023 33) New variable for output, meanStepSize (seconds over data window), commit 02baeba0 Oct 17, 2023 + 34) Improvements to error messages AFTER tag of v4.0.0-exp-Sundials - 01) Enthalpy formulation, new decision (renamed from howHeatCap) + 35) Enthalpy formulation, new decision (renamed from howHeatCap) nrgConserv [closedForm or enthalpyFD or enthalpyFDlu] ! (30) choice of variable in energy conservation backward Euler residual refers to if you want the numrec or kinsol residual to be computed with closed form heat capacity (does not conserve energy) or enthalpy finite difference, with enthalpy calculated in the soil with the analytical solution or the lookup tables. Decision of numMethod=itertive will give give set nrgConserv=closedForm for backwards compatibility. - 02) New output values of the balances, inputted to the outputControl.txt as: + 36) New output values of the balances, inputted to the outputControl.txt as: balanceCasNrg | 1 balanceVegNrg | 1 balanceSnowNrg | 1 @@ -96,18 +97,20 @@ AFTER tag of v4.0.0-exp-Sundials balanceSoilMass | 1 balanceAqMass | 1 The balance*Nrg are in units of W/m^3 and the balance*Mass are in units of kg/m^3/s. - 03) Cm derivatives, will be used if needCm is on in eval8summa or eval8summaPrime. - 04) Sean's refactor and object-orientated work, numerous and ongoing changes affecting conciseness and speed. -c-05) six allocation errors commits c0624a68, b2709388, 0b921c65, a23ac832, e43c766d, 6d3ac180 Feb 21, 2024 -c-06) two uninitialized variables fixes, commit 11a47b2b, 6b130053 Feb 15, 13, 2024 -c-07) snow water upper bound should be 1, not iden_ice, commit 265721b5 Feb 13, 2024 - -08) constraints now just scale the state variable they effect, commit 24bae32c Feb 13, 2024 - -09) constraint corrections for state variable changes in liq_layer vs wat_layer, commit 523774e1 Feb 15, 2024 - 10) Kyle's actors work, ongoing changes - 11) Added parameters for model control IDA, default values are fine + 37) Cm derivatives, will be used if needCm is on in eval8summa or eval8summaPrime. + 38) Sean's refactor and object-orientated work, numerous and ongoing changes affecting conciseness and speed. +c-39) six allocation errors commits c0624a68, b2709388, 0b921c65, a23ac832, e43c766d, 6d3ac180 Feb 21, 2024 +c-40) two uninitialized variables fixes, commit 11a47b2b, 6b130053 Feb 15, 13, 2024 +c-41) snow water upper bound should be 1, not iden_ice, commit 265721b5 Feb 13, 2024 + -42) constraints now just scale the state variable they effect, commit 24bae32c Feb 13, 2024 + -43) constraint corrections for state variable changes in liq_layer vs wat_layer, commit 523774e1 Feb 15, 2024 + 44) Kyle's actors work, ongoing changes + 45) Added parameters for model control IDA, default values are fine idaMaxOrder | 5.0 | 1.0 | 5.0 idaMaxInternalSteps | 500.0 | 500.0 | 999999.0 idaMaxErrTestFail | 50.0 | 10.0 | 50.0 idaMinStepSize | 0.0 | 0.0 | 3600.0 - 13) Added decision aquiferIni, where default is fullStart as a full aquifer, but may use emptyStart for RMSE calculation. -idaMaxStepSize | 0.0 | 0.0 | 3600.0 \ No newline at end of file + 46) Added decision aquiferIni, where default is fullStart as a full aquifer, but may use emptyStart for RMSE calculation. +idaMaxStepSize | 0.0 | 0.0 | 3600.0 + -47) Logistic smoother on canopy wetted fraction, makes function less abrupt and better convergence, commit 3608dc99 Jun 6, 2024 + From 16c3cddf980353ab7c9ca02bd4a7f78d484fc32c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 30 Jul 2024 00:21:35 -0500 Subject: [PATCH 1363/1472] utils --- utils/plot_per_GRUMult.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/plot_per_GRUMult.py b/utils/plot_per_GRUMult.py index 9cc1e2675..673946677 100644 --- a/utils/plot_per_GRUMult.py +++ b/utils/plot_per_GRUMult.py @@ -73,7 +73,7 @@ if stat == 'kgem': do_rel = False # don't plot relative to the benchmark simulation for KGE if more_mean: # extra vars in a balance file - plot_vars_exVar = ['scalarRainPlusMelt','scalarRootZoneTemp','airtemp','scalarSWE'] + plt_titl_exVar = ['rain plus melt','top 4m soil temperature','air temperature','snow water equivalent'] #plot_vars_exVar = ['balanceCasNrg','balanceSoilNrg','balanceVegNrg','balanceSnowNrg'] viz_file_exVar = 'exVar_hrly_diff_bals_balance.nc' plt_name0_exVar = 'SUMMA-BE1 temperature heat eq.' @@ -458,7 +458,7 @@ def format_tick(value, tick_number): elif i==len(plot_vars_exVar)-2: pad = -0.05 else: - pad = -0.3 + pad = -0.5 if one_plot: cbr = fig.colorbar(sm,ax=axs_list[r*ncol:r*ncol+c+1],aspect=27/nrow, pad=pad) else: From a69bf44c11b62ca01f67888ca044bba3a66c1472 Mon Sep 17 00:00:00 2001 From: Kyle Klenk Date: Thu, 8 Aug 2024 16:23:01 -0600 Subject: [PATCH 1364/1472] Update CMakeLists.txt FIx OpenBLAS issue where openMP is causing poor performance --- build/CMakeLists.txt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index e13e37942..077541ab7 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -80,10 +80,15 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXEC_DIR}) find_package(NetCDF REQUIRED) list(APPEND EXT_TARGETS NetCDF::NetCDF) -# OpenBLAS -set(BLA_VENDOR OpenBLAS) # MKL -find_package(OpenBLAS REQUIRED) -list(APPEND EXT_TARGETS OpenBLAS::OpenBLAS) +# OpenBLAS (MAY HAVE OpenMP enabled which slows execution) +# USE LAPACK INSTEAD TO AVOID THIS ISSUE +# set(BLA_VENDOR OpenBLAS) # MKL +# find_package(OpenBLAS REQUIRED) +# list(APPEND EXT_TARGETS OpenBLAS::OpenBLAS) + +# LAPACK +find_package(LAPACK REQUIRED) +list(APPEND EXT_TARGETS LAPACK::LAPACK) # Set compiler flags set(FLAGS_OPT $ENV{FLAGS_OPT}) # get optional user-specified flags from environment variables From e7f2b3df4bb0e797c9bacc090e5c7178e0e877b0 Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Fri, 30 Aug 2024 13:45:12 -0600 Subject: [PATCH 1365/1472] Reorg utils folder to separate dockerfiles from post processing utilities --- utils/containers/Summa-OpenWQ/Dockerfile | 0 utils/containers/Summa-generic/Dockerfile | 0 .../{ => post-processing}/SUMMA_merge_restarts_into_warmState.py | 0 utils/{ => post-processing}/check_bit_4_bit_withTol.py | 0 utils/{ => post-processing}/concat_groups_split_summa.py | 0 utils/{ => post-processing}/convert_summa_config_v2_v3.py | 0 utils/{ => post-processing}/gen_coldstate.py | 0 utils/{ => post-processing}/hist_per_GRU.py | 0 utils/{ => post-processing}/largest_error_attrib.py | 0 utils/{ => post-processing}/plot_per_GRUMult.py | 0 utils/{ => post-processing}/plot_per_GRUMultBal.py | 0 utils/{ => post-processing}/readme.md | 0 utils/{ => post-processing}/scat_per_GRU.py | 0 utils/{ => post-processing}/subsetGRU.sh | 0 utils/{ => post-processing}/summarize_logs.py | 0 utils/{ => post-processing}/timeseries_to_statistics.py | 0 16 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 utils/containers/Summa-OpenWQ/Dockerfile create mode 100644 utils/containers/Summa-generic/Dockerfile rename utils/{ => post-processing}/SUMMA_merge_restarts_into_warmState.py (100%) rename utils/{ => post-processing}/check_bit_4_bit_withTol.py (100%) rename utils/{ => post-processing}/concat_groups_split_summa.py (100%) rename utils/{ => post-processing}/convert_summa_config_v2_v3.py (100%) rename utils/{ => post-processing}/gen_coldstate.py (100%) rename utils/{ => post-processing}/hist_per_GRU.py (100%) rename utils/{ => post-processing}/largest_error_attrib.py (100%) rename utils/{ => post-processing}/plot_per_GRUMult.py (100%) rename utils/{ => post-processing}/plot_per_GRUMultBal.py (100%) rename utils/{ => post-processing}/readme.md (100%) rename utils/{ => post-processing}/scat_per_GRU.py (100%) rename utils/{ => post-processing}/subsetGRU.sh (100%) rename utils/{ => post-processing}/summarize_logs.py (100%) rename utils/{ => post-processing}/timeseries_to_statistics.py (100%) diff --git a/utils/containers/Summa-OpenWQ/Dockerfile b/utils/containers/Summa-OpenWQ/Dockerfile new file mode 100644 index 000000000..e69de29bb diff --git a/utils/containers/Summa-generic/Dockerfile b/utils/containers/Summa-generic/Dockerfile new file mode 100644 index 000000000..e69de29bb diff --git a/utils/SUMMA_merge_restarts_into_warmState.py b/utils/post-processing/SUMMA_merge_restarts_into_warmState.py similarity index 100% rename from utils/SUMMA_merge_restarts_into_warmState.py rename to utils/post-processing/SUMMA_merge_restarts_into_warmState.py diff --git a/utils/check_bit_4_bit_withTol.py b/utils/post-processing/check_bit_4_bit_withTol.py similarity index 100% rename from utils/check_bit_4_bit_withTol.py rename to utils/post-processing/check_bit_4_bit_withTol.py diff --git a/utils/concat_groups_split_summa.py b/utils/post-processing/concat_groups_split_summa.py similarity index 100% rename from utils/concat_groups_split_summa.py rename to utils/post-processing/concat_groups_split_summa.py diff --git a/utils/convert_summa_config_v2_v3.py b/utils/post-processing/convert_summa_config_v2_v3.py similarity index 100% rename from utils/convert_summa_config_v2_v3.py rename to utils/post-processing/convert_summa_config_v2_v3.py diff --git a/utils/gen_coldstate.py b/utils/post-processing/gen_coldstate.py similarity index 100% rename from utils/gen_coldstate.py rename to utils/post-processing/gen_coldstate.py diff --git a/utils/hist_per_GRU.py b/utils/post-processing/hist_per_GRU.py similarity index 100% rename from utils/hist_per_GRU.py rename to utils/post-processing/hist_per_GRU.py diff --git a/utils/largest_error_attrib.py b/utils/post-processing/largest_error_attrib.py similarity index 100% rename from utils/largest_error_attrib.py rename to utils/post-processing/largest_error_attrib.py diff --git a/utils/plot_per_GRUMult.py b/utils/post-processing/plot_per_GRUMult.py similarity index 100% rename from utils/plot_per_GRUMult.py rename to utils/post-processing/plot_per_GRUMult.py diff --git a/utils/plot_per_GRUMultBal.py b/utils/post-processing/plot_per_GRUMultBal.py similarity index 100% rename from utils/plot_per_GRUMultBal.py rename to utils/post-processing/plot_per_GRUMultBal.py diff --git a/utils/readme.md b/utils/post-processing/readme.md similarity index 100% rename from utils/readme.md rename to utils/post-processing/readme.md diff --git a/utils/scat_per_GRU.py b/utils/post-processing/scat_per_GRU.py similarity index 100% rename from utils/scat_per_GRU.py rename to utils/post-processing/scat_per_GRU.py diff --git a/utils/subsetGRU.sh b/utils/post-processing/subsetGRU.sh similarity index 100% rename from utils/subsetGRU.sh rename to utils/post-processing/subsetGRU.sh diff --git a/utils/summarize_logs.py b/utils/post-processing/summarize_logs.py similarity index 100% rename from utils/summarize_logs.py rename to utils/post-processing/summarize_logs.py diff --git a/utils/timeseries_to_statistics.py b/utils/post-processing/timeseries_to_statistics.py similarity index 100% rename from utils/timeseries_to_statistics.py rename to utils/post-processing/timeseries_to_statistics.py From 28c5ba6c81af376e82f854edefdba0cab6046da8 Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Fri, 30 Aug 2024 14:13:45 -0600 Subject: [PATCH 1366/1472] Created dockerfile to create summa container --- utils/containers/Summa-generic/Dockerfile | 37 +++++++++++++++++++ .../Summa-generic/build_docker_container.sh | 3 ++ 2 files changed, 40 insertions(+) create mode 100755 utils/containers/Summa-generic/build_docker_container.sh diff --git a/utils/containers/Summa-generic/Dockerfile b/utils/containers/Summa-generic/Dockerfile index e69de29bb..67025aae0 100644 --- a/utils/containers/Summa-generic/Dockerfile +++ b/utils/containers/Summa-generic/Dockerfile @@ -0,0 +1,37 @@ +FROM ubuntu:latest + +WORKDIR /code +RUN apt-get update -y && \ + apt-get upgrade -y && \ + DEBIAN_FRONTEND="noninteractive" apt-get install -y software-properties-common \ + libnetcdf-dev \ + libnetcdff-dev \ + liblapack-dev \ + libopenblas-dev \ + cmake \ + g++ \ + git \ + libssl-dev \ + make \ + gfortran \ + wget \ + python3-pip \ + valgrind \ + gdb &&\ + apt-get autoclean + +# Install Sundials +WORKDIR /opt +RUN wget https://github.com/LLNL/sundials/archive/refs/tags/v7.1.1.tar.gz +RUN tar -xzf v7.1.1.tar.gz +WORKDIR /opt/sundials-7.1.1 +RUN mkdir build/ +WORKDIR /opt/sundials-7.1.1/build +RUN cmake ../ -DBUILD_FORTRAN_MODULE_INTERFACE=ON \ + -DCMAKE_Fortran_COMPILER=gfortran \ + -DCMAKE_INSTALL_PREFIX=/usr/local/sundials +RUN make -j 4 +RUN make install + + +WORKDIR /code diff --git a/utils/containers/Summa-generic/build_docker_container.sh b/utils/containers/Summa-generic/build_docker_container.sh new file mode 100755 index 000000000..115639227 --- /dev/null +++ b/utils/containers/Summa-generic/build_docker_container.sh @@ -0,0 +1,3 @@ +#! /bin/bash + +docker build -t summa-generic . \ No newline at end of file From a2c827927d67cdba27f7433ec1c57aa233347f6d Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Thu, 5 Sep 2024 16:27:21 -0600 Subject: [PATCH 1367/1472] Got one succesful compile in docker --- build/CMakeLists.txt | 18 +- build/cmake/build.pc.bash | 2 +- build/source/driver/summa_driver.f90 | 28 + build/source/openwq/CMakeLists.txt | 56 + build/source/openwq/OpenWQ_hydrolink.cpp | 274 +++++ build/source/openwq/OpenWQ_hydrolink.h | 133 +++ build/source/openwq/OpenWQ_interface.cpp | 109 ++ build/source/openwq/OpenWQ_interface.h | 69 ++ build/source/openwq/openWQ.f90 | 188 +++ build/source/openwq/openWQInterface.f90 | 133 +++ build/source/openwq/summa_openWQ.f90 | 1032 +++++++++++++++++ .../source/openwq/summa_openWQ_allocspace.f90 | 204 ++++ utils/containers/Summa-OpenWQ/Dockerfile | 50 + .../Summa-OpenWQ/build_docker_container.sh | 3 + 14 files changed, 2297 insertions(+), 2 deletions(-) create mode 100644 build/source/openwq/CMakeLists.txt create mode 100644 build/source/openwq/OpenWQ_hydrolink.cpp create mode 100644 build/source/openwq/OpenWQ_hydrolink.h create mode 100644 build/source/openwq/OpenWQ_interface.cpp create mode 100644 build/source/openwq/OpenWQ_interface.h create mode 100644 build/source/openwq/openWQ.f90 create mode 100644 build/source/openwq/openWQInterface.f90 create mode 100644 build/source/openwq/summa_openWQ.f90 create mode 100644 build/source/openwq/summa_openWQ_allocspace.f90 create mode 100755 utils/containers/Summa-OpenWQ/build_docker_container.sh diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 077541ab7..385c0988b 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -27,6 +27,7 @@ set(CMAKE_CONFIGURATION_TYPES Release Debug) # Options: Enable each with cmake -DOPTION=ON option(USE_SUNDIALS "Use IDA solver from SUNDIALS suite" OFF) option(USE_NEXTGEN "Use NextGen Framework" OFF) +option(USE_OPENWQ "Use OpenWQ Framework" OFF) set(EXT_TARGETS) # list of external targets to link to # Set Default Executable Name @@ -51,7 +52,6 @@ if (USE_SUNDIALS) set(EXEC_NAME summa_sundials.exe) endif() - if (USE_NEXTGEN) message("ENABLING NEXTGEN") add_compile_definitions(NGEN_ACTIVE BMI_ACTIVE NGEN_FORCING_ACTIVE @@ -67,6 +67,12 @@ else() LIST(INSERT CMAKE_PREFIX_PATH 0 "${CMAKE_SOURCE_DIR}/cmake/") endif() +if (USE_OPENWQ) + message("ENABLING OPENWQ") + add_compile_definitions(OPENWQ_ACTIVE) +endif() + + get_filename_component(F_MASTER "${F_MASTER}" REALPATH) get_filename_component(PARENT_DIR "${PARENT_DIR}" REALPATH) get_filename_component(EXEC_DIR "${EXEC_DIR}" REALPATH) @@ -142,6 +148,15 @@ if (USE_SUNDIALS) set(SUMMA_ALL ${SUMMA_ALL} ${MODRUN_SUNDIALS} ${SOLVER_SUNDIALS}) endif() +if (USE_OPENWQ) + add_subdirectory(${F_MASTER}/build/source/openwq) + if (DEFINED OPENWQ_ERROR) + message(FATAL_ERROR "OpenWQ build failed") + endif() + set(SUMMA_ALL ${SUMMA_ALL} ${OPENWQ_COUPLER}) + list(APPEND EXT_TARGETS openWQ) +endif () + # Define version number, not working correctly set(VERSIONFILE ${DRIVER_DIR}/summaversion.inc) @@ -194,6 +209,7 @@ else() target_compile_options(summa PRIVATE ${FLAGS_ALL}) target_link_libraries(summa PUBLIC ${EXT_TARGETS} SUMMA_NOAHMP SUMMA_COMM) add_executable(${EXEC_NAME} ${MAIN_SUMMA}) + target_compile_options(${EXEC_NAME} PRIVATE ${FLAGS_ALL}) set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) target_link_libraries(${EXEC_NAME} summa ${EXT_TARGETS}) # added flags to the link step endif() diff --git a/build/cmake/build.pc.bash b/build/cmake/build.pc.bash index 8576c3e74..41783310d 100755 --- a/build/cmake/build.pc.bash +++ b/build/cmake/build.pc.bash @@ -12,5 +12,5 @@ #source $oneAPI_dir/setvars.sh # initialize environment variables for Intel oneAPI #export FLAGS_OPT="-m64;-I"${MKLROOT}/include";-flto=1;-fuse-linker-plugin" # optional compiler flags -- Intel oneMKL builds -cmake -B ../cmake_build -S ../. +cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DUSE_OPENWQ=ON cmake --build ../cmake_build --target all -j diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index db67ae4cc..9e2c09431 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -40,6 +40,15 @@ program summa_driver ! global data USE globalData, only: numtim ! number of model time steps USE globalData, only: print_step_freq + +! ! OpenWQ coupling +#ifdef OPENWQ_ACTIVE + USE summa_openwq,only:openwq_init + USE summa_openwq,only:openwq_run_time_start + USE summa_openwq,only:openwq_run_space_step + USE summa_openwq,only:openwq_run_time_end +#endif + implicit none ! ***************************************************************************** @@ -79,6 +88,11 @@ program summa_driver call summa_readRestart(summa1_struc(n), err, message) call handle_err(err, message) +#ifdef OPENWQ_ACTIVE + call openwq_init(err) + if (err /= 0) call stop_program(1, 'Problem Initializing OpenWQ') +#endif + ! ***************************************************************************** ! * model simulation ! ***************************************************************************** @@ -89,16 +103,30 @@ program summa_driver call summa_readForcing(modelTimeStep, summa1_struc(n), err, message) call handle_err(err, message) +#ifdef OPENWQ_ACTIVE + call openwq_run_time_start(summa1_struc(n)) ! Passing state volumes to openWQ +#endif + if (mod(modelTimeStep, print_step_freq) == 0)then print *, 'step ---> ', modelTimeStep endif + ! run the summa physics for one time step call summa_runPhysics(modelTimeStep, summa1_struc(n), err, message) call handle_err(err, message) +#ifdef OPENWQ_ACTIVE + call openwq_run_space_step(summa1_struc(n)) ! Passing fluxes to openWQ +#endif + ! write the model output call summa_writeOutputFiles(modelTimeStep, summa1_struc(n), err, message) call handle_err(err, message) + +#ifdef OPENWQ_ACTIVE + call openwq_run_time_end(summa1_struc(n)) +#endif + end do ! looping through time ! successful end diff --git a/build/source/openwq/CMakeLists.txt b/build/source/openwq/CMakeLists.txt new file mode 100644 index 000000000..166a77609 --- /dev/null +++ b/build/source/openwq/CMakeLists.txt @@ -0,0 +1,56 @@ +#=============================================================================== +# Compilation directives for the (optional) OpenWQ framework +#=============================================================================== + +# Check if the openwq directory exists relative to the current CMakeLists.txt +message(STATUS "Checking for openwq directory in ${CMAKE_CURRENT_SOURCE_DIR}") +if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/openwq") + message(FATAL_ERROR "OpenWQ files not found. Please check that the openwq \ + repository was cloned into build/source/openwq.") +endif() + +SET(OPENWQ_DIR ${CMAKE_CURRENT_SOURCE_DIR}/openwq CACHE INTERNAL "OPENWQ_DIR") +FILE(GLOB OPENWQ_SOURCES + ${OPENWQ_DIR}/src/*.cpp + ${OPENWQ_DIR}/src/global/*.cpp + ${OPENWQ_DIR}/src/extwatflux_ss/*.cpp + ${OPENWQ_DIR}/src/initiate/*.cpp + ${OPENWQ_DIR}/src/chem/*.cpp + ${OPENWQ_DIR}/src/watertransp/*.cpp + ${OPENWQ_DIR}/src/readjson/*.cpp + ${OPENWQ_DIR}/src/couplercalls/*.cpp + ${OPENWQ_DIR}/src/output/*.cpp + ${OPENWQ_DIR}/src/solver/*.cpp + ${OPENWQ_DIR}/src/units/*.cpp + ${OPENWQ_DIR}/src/utils/*.cpp) + +SET(OPENWQ_INCLUDES ${OPENWQ_DIR}/src/ CACHE INTERNAL "OPENWQ_INCLUDES") + +set (OPENWQ_COUPLER + ${CMAKE_CURRENT_SOURCE_DIR}/openWQ.f90 + ${CMAKE_CURRENT_SOURCE_DIR}/summa_openWQ_allocspace.f90 + ${CMAKE_CURRENT_SOURCE_DIR}/summa_openWQ.f90 + CACHE INTERNAL "OPENWQ_COUPLER") + +FIND_PACKAGE(Armadillo REQUIRED) +FIND_PACKAGE(HDF5 REQUIRED) + +SET(HYDROLINK_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/OpenWQ_hydrolink.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/OpenWQ_interface.cpp + CACHE INTERNAL "HYDROLINK_SOURCES") + +SET(CMAKE_CXX_FLAGS "-O3 -Wall -pedantic -fPIC") +ADD_LIBRARY(openWQ OBJECT ${OPENWQ_SOURCES} ${HYDROLINK_SOURCES}) + TARGET_INCLUDE_DIRECTORIES(openWQ PRIVATE + ${ARMADILLO_INCLUDE_DIR} + ${HDF5_INCLUDE_DIRS} + ${OPENWQ_INCLUDES}) + TARGET_LINK_LIBRARIES(openWQ PUBLIC + ${ARMADILLO_LIBRARIES} + ${HDF5_C_LIBRARY_hdf5}) + IF(OpenMP_CXX_FOUND) + MESSAGE("** OpenWQ **: OpenMP enabled!") + TARGET_LINK_LIBRARIES(openWQ PUBLIC OpenMP::OpenMP_CXX) + ENDIF() + diff --git a/build/source/openwq/OpenWQ_hydrolink.cpp b/build/source/openwq/OpenWQ_hydrolink.cpp new file mode 100644 index 000000000..efc1c5b7a --- /dev/null +++ b/build/source/openwq/OpenWQ_hydrolink.cpp @@ -0,0 +1,274 @@ +// Copyright 2020, Diogo Costa, diogo.pinhodacosta@canada.ca +// This file is part of OpenWQ model. + +// This program, openWQ, is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) aNCOLS later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +#include "OpenWQ_hydrolink.h" +#include "OpenWQ_interface.h" + +// Constructor +// initalize numHRUs value +CLASSWQ_openwq::CLASSWQ_openwq() {} + +// Deconstructor +CLASSWQ_openwq::~CLASSWQ_openwq() {} + +int CLASSWQ_openwq::decl( + int num_HRU, + int nCanopy_2openwq, // num layers of canopy (fixed to 1) + int nSnow_2openwq, // num layers of snow (fixed to max of 5 because it varies) + int nSoil_2openwq, // num layers of snoil (variable) + int nRunoff_2openwq, // num layers in the runoff of SUMMA + int nAquifer_2openwq, // num layers of aquifer (fixed to 1) + int nYdirec_2openwq){ // num of layers in y-dir (set to 1 because not used in summa) + + this->num_HRU = num_HRU; + + if (OpenWQ_hostModelconfig_ref->get_num_HydroComp()==0) { + + // Compartment names + // Make sure to use capital letters for compartment names + OpenWQ_hostModelconfig_ref->add_HydroComp(canopy_index_openwq,"SCALARCANOPYWAT", num_HRU, nYdirec_2openwq, nCanopy_2openwq); // Canopy + OpenWQ_hostModelconfig_ref->add_HydroComp(snow_index_openwq,"ILAYERVOLFRACWAT_SNOW", num_HRU, nYdirec_2openwq, max_snow_layers); // snow (layerd) + OpenWQ_hostModelconfig_ref->add_HydroComp(runoff_index_openwq,"RUNOFF", num_HRU, nYdirec_2openwq, nRunoff_2openwq); // Runoff + OpenWQ_hostModelconfig_ref->add_HydroComp(soil_index_openwq,"ILAYERVOLFRACWAT_SOIL", num_HRU, nYdirec_2openwq, nSoil_2openwq); // Soil (layerd) + OpenWQ_hostModelconfig_ref->add_HydroComp(aquifer_index_openwq,"SCALARAQUIFER", num_HRU, nYdirec_2openwq, nAquifer_2openwq); // Aquifer + + // External fluxes + // Make sure to use capital letters for external fluxes + OpenWQ_hostModelconfig_ref->add_HydroExtFlux(0,"PRECIP", num_HRU,nYdirec_2openwq,1); + + + OpenWQ_vars_ref = std::make_unique( + OpenWQ_hostModelconfig_ref->get_num_HydroComp(), + OpenWQ_hostModelconfig_ref->get_num_HydroExtFlux()); + + + // Dependencies + // to expand BGC modelling options + OpenWQ_hostModelconfig_ref->add_HydroDepend(0,"SM", num_HRU,nYdirec_2openwq, nSnow_2openwq + nSoil_2openwq); + OpenWQ_hostModelconfig_ref->add_HydroDepend(1,"Tair_K", num_HRU,nYdirec_2openwq, nSnow_2openwq + nSoil_2openwq); + OpenWQ_hostModelconfig_ref->add_HydroDepend(2,"Tsoil_K", num_HRU,nYdirec_2openwq, nSnow_2openwq + nSoil_2openwq); + + // Master Json + std::string master_json = std::getenv("master_json") ? std::getenv("master_json") : ""; + if (!std::filesystem::exists(master_json)) { + std::cerr << "\nERROR: Path to OpenWQ_master.json does not exist !!\n" + << "Please set the environment variable 'master_json' " + << "to the path of the OpenWQ_master.json file.\n"; + exit(EXIT_FAILURE); + } + OpenWQ_wqconfig_ref->set_OpenWQ_masterjson(master_json); + + OpenWQ_couplercalls_ref->InitialConfig( + *OpenWQ_hostModelconfig_ref, + *OpenWQ_json_ref, // create OpenWQ_json object + *OpenWQ_wqconfig_ref, // create OpenWQ_wqconfig object + *OpenWQ_units_ref, // functions for unit conversion + *OpenWQ_utils_ref, // utility methods/functions + *OpenWQ_readjson_ref, // read json files + *OpenWQ_vars_ref, + *OpenWQ_initiate_ref, // initiate modules + *OpenWQ_watertransp_ref, // transport modules + *OpenWQ_chem_ref, // biochemistry modules + *OpenWQ_extwatflux_ss_ref, // sink and source modules) + *OpenWQ_output_ref); + + } + return 0; +} + +// soilMoist_depVar does not have a value - it is passed as 0 +int CLASSWQ_openwq::openwq_run_time_start( + bool last_hru_flag, + int index_hru, + int nSnow_2openwq, + int nSoil_2openwq, + int simtime_summa[], + double soilMoist_depVar_summa_frac[], + double soilTemp_depVar_summa_K[], + double airTemp_depVar_summa_K, + double sweWatVol_stateVar_summa_m3[], + double canopyWatVol_stateVar_summa_m3, + double soilWatVol_stateVar_summa_m3[], + double aquiferWatVol_stateVar_summa_m3) { + + time_t simtime = OpenWQ_units_ref->convertTime_ints2time_t( + *OpenWQ_wqconfig_ref, + simtime_summa[0], + simtime_summa[1], + simtime_summa[2], + simtime_summa[3], + simtime_summa[4], + 0); + + int runoff_vol = 0; + + // Updating Chemistry dependencies and volumes (out of order because of looping) + + OpenWQ_hostModelconfig_ref->set_dependVar_at(1,index_hru,0,0, airTemp_depVar_summa_K); + OpenWQ_hostModelconfig_ref->set_waterVol_hydromodel_at(canopy_index_openwq,index_hru,0,0, canopyWatVol_stateVar_summa_m3); // canopy + OpenWQ_hostModelconfig_ref->set_waterVol_hydromodel_at(runoff_index_openwq,index_hru,0,0, runoff_vol); // runoff + OpenWQ_hostModelconfig_ref->set_waterVol_hydromodel_at(aquifer_index_openwq,index_hru,0,0, aquiferWatVol_stateVar_summa_m3); // aquifer + + // update Vars that rely on Snow + for (int z = 0; z < nSnow_2openwq; z++) { + OpenWQ_hostModelconfig_ref->set_waterVol_hydromodel_at(snow_index_openwq,index_hru,0,z, sweWatVol_stateVar_summa_m3[z]); // snow + } + + // Update Vars that rely on Soil + for (int z = 0; z < nSoil_2openwq; z++) { + OpenWQ_hostModelconfig_ref->set_dependVar_at(0,index_hru,0,z,soilMoist_depVar_summa_frac[z]); + OpenWQ_hostModelconfig_ref->set_dependVar_at(2,index_hru,0,z,soilTemp_depVar_summa_K[z]); + OpenWQ_hostModelconfig_ref->set_waterVol_hydromodel_at(soil_index_openwq,index_hru,0,z, soilWatVol_stateVar_summa_m3[z]); // soil + + } + + if (get_numHRU() -1 == index_hru ) { + OpenWQ_couplercalls_ref->RunTimeLoopStart( + *OpenWQ_hostModelconfig_ref, + *OpenWQ_json_ref, + *OpenWQ_wqconfig_ref, // create OpenWQ_wqconfig object + *OpenWQ_units_ref, // functions for unit conversion + *OpenWQ_utils_ref, // utility methods/functions + *OpenWQ_readjson_ref, // read json files + *OpenWQ_vars_ref, + *OpenWQ_initiate_ref, // initiate modules + *OpenWQ_watertransp_ref, // transport modules + *OpenWQ_chem_ref, // biochemistry modules + *OpenWQ_extwatflux_ss_ref, // sink and source modules) + *OpenWQ_solver_ref, + *OpenWQ_output_ref, + simtime); + } + + return 0; +} + +int CLASSWQ_openwq::openwq_run_space( + int simtime_summa[], + int source, int ix_s, int iy_s, int iz_s, + int recipient, int ix_r, int iy_r, int iz_r, + double wflux_s2r, double wmass_source) { + + // Convert Fortran Index to C++ index + ix_s -= 1; iy_s -= 1; iz_s -= 1; + ix_r -= 1; iy_r -= 1; iz_r -= 1; + + + time_t simtime = OpenWQ_units_ref->convertTime_ints2time_t( + *OpenWQ_wqconfig_ref, + simtime_summa[0], + simtime_summa[1], + simtime_summa[2], + simtime_summa[3], + simtime_summa[4], + 0); + + OpenWQ_couplercalls_ref->RunSpaceStep( + *OpenWQ_hostModelconfig_ref, + *OpenWQ_json_ref, + *OpenWQ_wqconfig_ref, // create OpenWQ_wqconfig object + *OpenWQ_units_ref, // functions for unit conversion + *OpenWQ_utils_ref, // utility methods/functions + *OpenWQ_readjson_ref, // read json files + *OpenWQ_vars_ref, + *OpenWQ_initiate_ref, // initiate modules + *OpenWQ_watertransp_ref, // transport modules + *OpenWQ_chem_ref, // biochemistry modules + *OpenWQ_extwatflux_ss_ref, // sink and source modules + *OpenWQ_solver_ref, + *OpenWQ_output_ref, + simtime, + source, ix_s, iy_s, iz_s, + recipient, ix_r, iy_r, iz_r, + wflux_s2r, wmass_source); + + return 0; +} + +int CLASSWQ_openwq::openwq_run_space_in( + int simtime_summa[], + std::string source_EWF_name, + int recipient, int ix_r, int iy_r, int iz_r, + double wflux_s2r) { + + // Convert Fortran Index to C++ index + ix_r -= 1; iy_r -= 1; iz_r -= 1; + + time_t simtime = OpenWQ_units_ref->convertTime_ints2time_t( + *OpenWQ_wqconfig_ref, + simtime_summa[0], + simtime_summa[1], + simtime_summa[2], + simtime_summa[3], + simtime_summa[4], + 0); + + OpenWQ_couplercalls_ref->RunSpaceStep_IN( + *OpenWQ_hostModelconfig_ref, + *OpenWQ_json_ref, + *OpenWQ_wqconfig_ref, + *OpenWQ_units_ref, + *OpenWQ_utils_ref, + *OpenWQ_readjson_ref, + *OpenWQ_vars_ref, + *OpenWQ_initiate_ref, + *OpenWQ_watertransp_ref, + *OpenWQ_chem_ref, + *OpenWQ_extwatflux_ss_ref, + *OpenWQ_solver_ref, + *OpenWQ_output_ref, + simtime, + source_EWF_name, + recipient, ix_r, iy_r, iz_r, + wflux_s2r); + + return 0; +} + +int CLASSWQ_openwq::openwq_run_time_end( + int simtime_summa[]) { + + time_t simtime = OpenWQ_units_ref->convertTime_ints2time_t( + *OpenWQ_wqconfig_ref, + simtime_summa[0], + simtime_summa[1], + simtime_summa[2], + simtime_summa[3], + simtime_summa[4], + 0); + + + OpenWQ_couplercalls_ref->RunTimeLoopEnd( + *OpenWQ_hostModelconfig_ref, + *OpenWQ_json_ref, + *OpenWQ_wqconfig_ref, // create OpenWQ_wqconfig object + *OpenWQ_units_ref, // functions for unit conversion + *OpenWQ_utils_ref, // utility methods/functions + *OpenWQ_readjson_ref, // read json files + *OpenWQ_vars_ref, + *OpenWQ_initiate_ref, // initiate modules + *OpenWQ_watertransp_ref, // transport modules + *OpenWQ_chem_ref, // biochemistry modules + *OpenWQ_extwatflux_ss_ref, // sink and source modules) + *OpenWQ_solver_ref, + *OpenWQ_output_ref, + simtime); + + return 0; +} + +int CLASSWQ_openwq::get_numHRU(){ + return this->num_HRU; +} diff --git a/build/source/openwq/OpenWQ_hydrolink.h b/build/source/openwq/OpenWQ_hydrolink.h new file mode 100644 index 000000000..3e0cad2a1 --- /dev/null +++ b/build/source/openwq/OpenWQ_hydrolink.h @@ -0,0 +1,133 @@ +// This program, openWQ, is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) aNCOLS later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#ifndef OPENWQ_HYDROLINK_INCLUDED +#define OPENWQ_HYDROLINK_INCLUDED + +#include "couplercalls/OpenWQ_couplercalls.hpp" +#include "global/OpenWQ_hostModelconfig.hpp" +#include "global/OpenWQ_json.hpp" +#include "global/OpenWQ_wqconfig.hpp" +#include "global/OpenWQ_vars.hpp" +#include "readjson/OpenWQ_readjson.hpp" +#include "initiate/OpenWQ_initiate.hpp" +#include "chem/OpenWQ_chem.hpp" +#include "watertransp/OpenWQ_watertransp.hpp" +#include "extwatflux_ss/OpenWQ_extwatflux_ss.hpp" +#include "units/OpenWQ_units.hpp" +#include "utils/OpenWQ_utils.hpp" +#include "solver/OpenWQ_solver.hpp" +#include "output/OpenWQ_output.hpp" +#include +#include +#include +#include + +// Global Indexes for Compartments + inline int canopy_index_openwq = 0; + inline int snow_index_openwq = 1; + inline int runoff_index_openwq = 2; + inline int soil_index_openwq = 3; + inline int aquifer_index_openwq = 4; + inline int max_snow_layers = 5; + +class CLASSWQ_openwq +{ + + // Instance Variables + private: + + std::unique_ptr OpenWQ_hostModelconfig_ref = + std::make_unique(); + std::unique_ptr OpenWQ_couplercalls_ref = + std::make_unique(); + std::unique_ptr OpenWQ_json_ref = + std::make_unique(); + std::unique_ptr OpenWQ_wqconfig_ref = + std::make_unique(); + std::unique_ptr OpenWQ_units_ref = + std::make_unique(); + std::unique_ptr OpenWQ_utils_ref = + std::make_unique(); + std::unique_ptr OpenWQ_readjson_ref = + std::make_unique(); + std::unique_ptr OpenWQ_initiate_ref = + std::make_unique(); + std::unique_ptr OpenWQ_watertransp_ref = + std::make_unique(); + std::unique_ptr OpenWQ_chem_ref = + std::make_unique(); + std::unique_ptr OpenWQ_extwatflux_ss_ref = + std::make_unique(); + std::unique_ptr OpenWQ_solver_ref = + std::make_unique(); + std::unique_ptr OpenWQ_output_ref = + std::make_unique(); + + std::unique_ptr OpenWQ_vars_ref; // Requires input from summa + + int num_HRU; + const float *hru_area; + + // Constructor + public: + CLASSWQ_openwq(); + ~CLASSWQ_openwq(); + + // Methods + void printNum() { + std::cout << "num = " << this->num_HRU << std::endl; + } + + int decl( + int num_HRU, // num HRU + int nCanopy_2openwq, // num layers of canopy (fixed to 1) + int nSnow_2openwq, // num layers of snow (fixed to max of 5 because it varies) + int nSoil_2openwq, // num layers of snoil (variable) + int nRunoff_2openwq, // num layers of runoff (fixed to 1) + int nAquifer_2openwq, // num layers of aquifer (fixed to 1) + int nYdirec_2openwq); // num of layers in y-dir (set to 1 because not used in summa) + + int openwq_run_time_start( + bool last_hru_flag, + int hru_index, + int nSnow_2openwq, + int nSoil_2openwq, + int simtime_summa[], + double soilMoist_depVar_summa_frac[], + double soilTemp_depVar_summa_K[], + double airTemp_depVar_summa_K, + double sweWatVol_stateVar_summa_m3[], + double canopyWatVol_stateVar_summa_m3, + double soilWatVol_stateVar_summa_m3[], + double aquiferWatVol_stateVar_summa_m3); + + int openwq_run_space( + int simtime_summa[], + int source, int ix_s, int iy_s, int iz_s, + int recipient, int ix_r, int iy_r, int iz_r, + double wflux_s2r, double wmass_source); + + int openwq_run_space_in( + int simtime_summa[], + std::string source_EWF_name, + int recipient, int ix_r, int iy_r, int iz_r, + double wflux_s2r); + + int openwq_run_time_end( + int simtime_summa[]); + + int get_numHRU(); + +}; +#endif \ No newline at end of file diff --git a/build/source/openwq/OpenWQ_interface.cpp b/build/source/openwq/OpenWQ_interface.cpp new file mode 100644 index 000000000..38c482613 --- /dev/null +++ b/build/source/openwq/OpenWQ_interface.cpp @@ -0,0 +1,109 @@ +#include "OpenWQ_hydrolink.h" +#include "OpenWQ_interface.h" +/** + * Below is the implementation of the C interface for SUMMA. When Summa calls a function + * the functions below are the ones that are invoked first. + * The openWQ object is then passed from Fortran to these functions so that the OpenWQ object + * can be called. The openWQ object methods are defined above. + */ +// Interface functions to create Object +CLASSWQ_openwq* create_openwq() { + return new CLASSWQ_openwq(); +} + +void delete_openwq(CLASSWQ_openwq* openWQ) { + delete openWQ; +} + +int openwq_decl( + CLASSWQ_openwq *openWQ, + int hruCount, // num HRU + int nCanopy_2openwq, // num layers of canopy (fixed to 1) + int nSnow_2openwq, // num layers of snow (fixed to max of 5 because it varies) + int nSoil_2openwq, // num layers of snoil (variable) + int nRunoff_2openwq, // num layers of runoff (fixed to 1) + int nAquifer_2openwq, // num layers of aquifer (fixed to 1) + int nYdirec_2openwq){ // num of layers in y-dir (set to 1 because not used in summa) + + return openWQ->decl( + hruCount, + nCanopy_2openwq, + nSnow_2openwq, + nSoil_2openwq, + nRunoff_2openwq, + nAquifer_2openwq, + nYdirec_2openwq); + +} + + +int openwq_run_time_start( + CLASSWQ_openwq *openWQ, + bool last_hru_flag, + int hru_index, + int nSnow_2openwq, + int nSoil_2openwq, + int simtime_summa[], + double soilMoist_depVar_summa_frac[], + double soilTemp_depVar_summa_K[], + double airTemp_depVar_summa_K, + double sweWatVol_stateVar_summa_m3[], + double canopyWatVol_stateVar_summa_m3, + double soilWatVol_stateVar_summa_m3[], + double aquiferWatVol_stateVar_summa_m3) { + + return openWQ->openwq_run_time_start( + last_hru_flag, + hru_index, + nSnow_2openwq, + nSoil_2openwq, + simtime_summa, + soilMoist_depVar_summa_frac, + soilTemp_depVar_summa_K, + airTemp_depVar_summa_K, + sweWatVol_stateVar_summa_m3, + canopyWatVol_stateVar_summa_m3, + soilWatVol_stateVar_summa_m3, + aquiferWatVol_stateVar_summa_m3); +} + + +int openwq_run_space( + CLASSWQ_openwq *openWQ, + int simtime_summa[], + int source, int ix_s, int iy_s, int iz_s, + int recipient, int ix_r, int iy_r, int iz_r, + double wflux_s2r, double wmass_source) { + + return openWQ->openwq_run_space( + simtime_summa, + source, ix_s, iy_s, iz_s, + recipient, ix_r, iy_r, iz_r, + wflux_s2r, wmass_source); +} + +int openwq_run_space_in( + CLASSWQ_openwq *openWQ, + int simtime_summa[], + char* source_EWF_name, + int recipient, int ix_r, int iy_r, int iz_r, + double wflux_s2r) { + + // convert source_EWF_name to string + std::string source_EWF_name_str(source_EWF_name); + + return openWQ->openwq_run_space_in( + simtime_summa, + source_EWF_name_str, + recipient, ix_r, iy_r, iz_r, + wflux_s2r); +} + + +int openwq_run_time_end( + CLASSWQ_openwq *openWQ, + int simtime_summa[]) { + + return openWQ->openwq_run_time_end( + simtime_summa); +} diff --git a/build/source/openwq/OpenWQ_interface.h b/build/source/openwq/OpenWQ_interface.h new file mode 100644 index 000000000..91474735f --- /dev/null +++ b/build/source/openwq/OpenWQ_interface.h @@ -0,0 +1,69 @@ +/** + * This is the C interface for SUMMA, these are the functions that are called + * by SUMMA and the iso bindings. + * These are only their definition and their actual implementation is in + * OpenWQ_hydrolink.cpp + */ + +#ifdef __cplusplus +extern "C" { + class CLASSWQ_openwq; + typedef CLASSWQ_openwq CLASSWQ_openwq; + #else + typedef struct CLASSWQ_openwq CLASSWQ_openwq; + #endif + + // Create OpenWQ Object + CLASSWQ_openwq* create_openwq(); + + // Delete OpenWQ Object + void delete_openwq(CLASSWQ_openwq* openWQ); + + // OpenWQ initalization method + int openwq_decl( + CLASSWQ_openwq *openWQ, + int hruCount, // num HRU + int nCanopy_2openwq, // num layers of canopy (fixed to 1) + int nSnow_2openwq, // num layers of snow (fixed to max of 5 because it varies) + int nSoil_2openwq, // num layers of snoil (variable) + int nRunoff_2openwq, // num layers of runoff (fixed to 1) + int nAquifer_2openwq, // num layers of aquifer (fixed to 1) + int nYdirec_2openwq); // num of layers in y-dir (set to 1 because not used in summa) + + int openwq_run_time_start( + CLASSWQ_openwq *openWQ, + bool last_hru_flag, + int index_hru, + int nSnow_2openwq, + int nSoil_2openwq, + int simtime_summa[], + double soilMoist_depVar[], + double soilTemp_K_depVar[], + double airTemp_K_depVar, + double sweWatVol_stateVar[], + double canopyWat, + double soilWatVol_stateVar[], + double aquiferStorage); + + // OpenWQ run functions, this function decides which C++ code to call + int openwq_run_space( + CLASSWQ_openwq *openWQ, + int simtime_summa[], + int source, int ix_s, int iy_s, int iz_s, + int recipient, int ix_r, int iy_r, int iz_r, + double wflux_s2r, double wmass_source); + + int openwq_run_space_in( + CLASSWQ_openwq *openWQ, + int simtime_summa[], + char* source_EWF_name, + int recipient, int ix_r, int iy_r, int iz_r, + double wflux_s2r); + + int openwq_run_time_end( + CLASSWQ_openwq *openWQ, + int simtime_summa[]); + + #ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/build/source/openwq/openWQ.f90 b/build/source/openwq/openWQ.f90 new file mode 100644 index 000000000..fd33f8cca --- /dev/null +++ b/build/source/openwq/openWQ.f90 @@ -0,0 +1,188 @@ +module openwq + + USE, intrinsic :: iso_c_binding + USE nrtype + private + public :: CLASSWQ_openwq + + include "openWQInterface.f90" + + type CLASSWQ_openwq + private + type(c_ptr) :: ptr ! pointer to openWQ class + + contains + ! procedure :: get_num => openWQ_get_num + procedure :: decl => openWQ_init + procedure :: openwq_run_time_start => openwq_run_time_start + procedure :: openwq_run_space => openwq_run_space + procedure :: openwq_run_space_in => openwq_run_space_in + procedure :: openwq_run_time_end => openwq_run_time_end + + end type + + interface CLASSWQ_openwq + procedure create_openwq + end interface + contains + function create_openwq() + implicit none + type(CLASSWQ_openwq) :: create_openwq + create_openwq%ptr = create_openwq_c() + end function + + ! supposed to be decl but needed to openWQ_decl in the interface file + ! returns integer of either a failure(-1) or success(0) + integer function openWQ_init( & + this, & ! openwq object + num_hru, & ! num HRU + nCanopy_2openwq, & ! num layers of canopy (fixed to 1) + nSnow_2openwq, & ! num layers of snow (fixed to max of 5 because it varies) + nSoil_2openwq, & ! num layers of snoil (variable) + nRunoff_2openwq, & ! num layers of runoff (fixed to 1) + nAquifer_2openwq, & ! num layers of aquifer (fixed to 1) + nYdirec_2openwq) ! num of layers in y-dir (set to 1 because not used in summa) + + implicit none + class(CLASSWQ_openwq) :: this + integer(i4b), intent(in) :: num_hru + integer(i4b), intent(in) :: nCanopy_2openwq + integer(i4b), intent(in) :: nSnow_2openwq + integer(i4b), intent(in) :: nSoil_2openwq + integer(i4b), intent(in) :: nRunoff_2openwq + integer(i4b), intent(in) :: nAquifer_2openwq + + integer(i4b), intent(in) :: nYdirec_2openwq + + openWQ_init = openwq_decl_c( & + this%ptr, & ! openwq object + num_hru, & ! num HRU + nCanopy_2openwq, & ! num layers of canopy (fixed to 1) + nSnow_2openwq, & ! num layers of snow (fixed to max of 5 because it varies) + nSoil_2openwq, & ! num layers of snoil (variable) + nRunoff_2openwq, & ! num layers of runoff (fixed to 1) + nAquifer_2openwq, & ! num layers of aquifer (fixed to 1) + nYdirec_2openwq) ! num of layers in y-dir (set to 1 because not used in summa) + + end function + + + integer function openwq_run_time_start( & + this, & + last_hru_flag, & + hru_index, & + nSnow_2openwq, & + nSoil_2openwq, & + simtime, & + soilMoist_depVar_summa_frac, & + soilTemp_depVar_summa_K, & + airTemp_depVar_summa_K, & + sweWatVol_stateVar_summa_m3, & + canopyWatVol_stateVar_summa_m3, & + soilWatVol_stateVar_summa_m3, & + aquiferWatVol_stateVar_summa_m3) + + implicit none + class(CLASSWQ_openwq) :: this + logical(1), intent(in) :: last_hru_flag + integer(i4b), intent(in) :: hru_index + integer(i4b), intent(in) :: nSnow_2openwq + integer(i4b), intent(in) :: nSoil_2openwq + integer(i4b), intent(in) :: simtime(5) ! 5 is the number of timevars + real(rkind), intent(in) :: airTemp_depVar_summa_K + real(rkind), intent(in) :: soilTemp_depVar_summa_K(nSoil_2openwq) + real(rkind), intent(in) :: soilMoist_depVar_summa_frac(nSoil_2openwq) + real(rkind), intent(in) :: canopyWatVol_stateVar_summa_m3 + real(rkind), intent(in) :: sweWatVol_stateVar_summa_m3(nSnow_2openwq) + real(rkind), intent(in) :: soilWatVol_stateVar_summa_m3(nSoil_2openwq) + real(rkind), intent(in) :: aquiferWatVol_stateVar_summa_m3 + + openwq_run_time_start = openwq_run_time_start_c( & + this%ptr, & + last_hru_flag, & + hru_index, & + nSnow_2openwq, & + nSoil_2openwq, & + simtime, & + soilMoist_depVar_summa_frac, & + soilTemp_depVar_summa_K, & + airTemp_depVar_summa_K, & + sweWatVol_stateVar_summa_m3, & + canopyWatVol_stateVar_summa_m3, & + soilWatVol_stateVar_summa_m3, & + aquiferWatVol_stateVar_summa_m3) + + end function + + integer function openwq_run_space( & + this, & + simtime, & + source,ix_s,iy_s,iz_s, & + recipient,ix_r,iy_r,iz_r, & + wflux_s2r,wmass_source) + + implicit none + class(CLASSWQ_openwq) :: this + integer(i4b), intent(in) :: simtime(5) ! 5 is the number of timevars + integer(i4b), intent(in) :: source + integer(i4b), intent(in) :: ix_s + integer(i4b), intent(in) :: iy_s + integer(i4b), intent(in) :: iz_s + integer(i4b), intent(in) :: recipient + integer(i4b), intent(in) :: ix_r + integer(i4b), intent(in) :: iy_r + integer(i4b), intent(in) :: iz_r + real(rkind), intent(in) :: wflux_s2r + real(rkind), intent(in) :: wmass_source + + openwq_run_space = openwq_run_space_c( & + this%ptr, & + simtime, & + source,ix_s,iy_s,iz_s, & + recipient,ix_r,iy_r,iz_r, & + wflux_s2r,wmass_source) + + end function + + integer function openwq_run_space_in( & + this, & + simtime, & + source_EWF_name, & + recipient,ix_r,iy_r,iz_r, & + wflux_s2r) + + implicit none + class(CLASSWQ_openwq) :: this + integer(i4b), intent(in) :: simtime(5) ! 5 is the number of timevars + integer(i4b), intent(in) :: recipient + integer(i4b), intent(in) :: ix_r + integer(i4b), intent(in) :: iy_r + integer(i4b), intent(in) :: iz_r + real(rkind), intent(in) :: wflux_s2r + character(*), intent(in) :: source_EWF_name + + openwq_run_space_in = openwq_run_space_in_c( & + this%ptr, & + simtime, & + source_EWF_name, & + recipient,ix_r,iy_r,iz_r, & + wflux_s2r) + + end function + + + integer function openwq_run_time_end( & + this, & + simtime) + + implicit none + class(CLASSWQ_openwq) :: this + integer(i4b), intent(in) :: simtime(5) ! 5 is the number of timevars + + openwq_run_time_end = openwq_run_time_end_c( & + this%ptr, & + simtime) + + end function + +end module openwq \ No newline at end of file diff --git a/build/source/openwq/openWQInterface.f90 b/build/source/openwq/openWQInterface.f90 new file mode 100644 index 000000000..7bf39a1d5 --- /dev/null +++ b/build/source/openwq/openWQInterface.f90 @@ -0,0 +1,133 @@ +! OpenWQ C Interface +! This file contains the Fortran functions that are callable from C. +! These function are mapped to the C functions in defined in OpenWQ_interface.h +! and implmeneted in OpenWQ_interface.c + + +interface + function create_openwq_c() bind(C, name="create_openwq") + + use iso_c_binding + implicit none + type(c_ptr) :: create_openwq_c + + end function + + function openwq_decl_c( & + openWQ, & + num_hru, & + nCanopy_2openwq, & + nSnow_2openwq, & + nSoil_2openwq, & + nRunoff_2openwq, & + nAquifer_2openwq, & + y_direction) bind(C, name="openwq_decl") + + use iso_c_binding + implicit none + integer(c_int) :: openwq_decl_c ! returns a return value of 0 (success) or -1 (failure) + type(c_ptr), intent(in), value :: openWQ + integer(c_int), intent(in), value :: num_hru + integer(c_int), intent(in), value :: nCanopy_2openwq + integer(c_int), intent(in), value :: nSnow_2openwq + integer(c_int), intent(in), value :: nSoil_2openwq + integer(c_int), intent(in), value :: nAquifer_2openwq + integer(c_int), intent(in), value :: nRunoff_2openwq + integer(c_int), intent(in), value :: y_direction + + end function + + function openwq_run_time_start_c(& + openWQ, & + last_hru_flag, & + hru_index, & + nSnow_2openwq, & + nSoil_2openwq, & + simtime_summa, & + soilMoist_depVar_summa_frac, & + soilTemp_depVar_summa_K, & + airTemp_depVar_summa_K, & + sweWatVol_stateVar_summa_m3, & + canopyWatVol_stateVar_summa_m3, & + soilWatVol_stateVar_summa_m3, & + aquiferWatVol_stateVar_summa_m3) bind(C, name="openwq_run_time_start") + + use iso_c_binding + implicit none + integer(c_int) :: openwq_run_time_start_c ! returns 0 (success) or -1 (failure) + type(c_ptr), intent(in), value :: openWQ + logical(c_bool), intent(in) :: last_hru_flag + integer(c_int), intent(in), value :: hru_index + integer(c_int), intent(in), value :: nSnow_2openwq + integer(c_int), intent(in), value :: nSoil_2openwq + integer(c_int), intent(in) :: simtime_summa(5) + real(c_double), intent(in) :: soilMoist_depVar_summa_frac(nSoil_2openwq) + real(c_double), intent(in) :: soilTemp_depVar_summa_K(nSoil_2openwq) + real(c_double), intent(in), value :: airTemp_depVar_summa_K + real(c_double), intent(in) :: sweWatVol_stateVar_summa_m3(nSnow_2openwq) + real(c_double), intent(in), value :: canopyWatVol_stateVar_summa_m3 + real(c_double), intent(in) :: soilWatVol_stateVar_summa_m3(nSoil_2openwq) + real(c_double), intent(in), value :: aquiferWatVol_stateVar_summa_m3 + + end function + + function openwq_run_space_c(& + openWQ, & + simtime, & + source,ix_s,iy_s,iz_s, & + recipient,ix_r,iy_r,iz_r, & + wflux_s2r, & + wmass_source) bind(C, name="openwq_run_space") + + use iso_c_binding + implicit none + integer(c_int) :: openwq_run_space_c ! returns 0 (success) or -1 (failure) + type(c_ptr), intent(in), value :: openWQ + integer(c_int), intent(in) :: simtime(5) + integer(c_int), intent(in), value :: source + integer(c_int), intent(in), value :: ix_s + integer(c_int), intent(in), value :: iy_s + integer(c_int), intent(in), value :: iz_s + integer(c_int), intent(in), value :: recipient + integer(c_int), intent(in), value :: ix_r + integer(c_int), intent(in), value :: iy_r + integer(c_int), intent(in), value :: iz_r + real(c_double), intent(in), value :: wflux_s2r + real(c_double), intent(in), value :: wmass_source + + end function + + function openwq_run_space_in_c( & + openWQ, & + simtime, & + source_EWF_name, & + recipient,ix_r,iy_r,iz_r, & + wflux_s2r) bind(C, name="openwq_run_space_in") + + USE iso_c_binding + implicit none + integer(c_int) :: openwq_run_space_in_c + type(c_ptr), intent(in), value :: openWQ + integer(c_int), intent(in) :: simtime(5) + integer(c_int), intent(in), value :: recipient + integer(c_int), intent(in), value :: ix_r + integer(c_int), intent(in), value :: iy_r + integer(c_int), intent(in), value :: iz_r + real(c_double), intent(in), value :: wflux_s2r + character(c_char), intent(in) :: source_EWF_name + + end function + + function openwq_run_time_end_c( & + openWQ, & + simtime) bind(C, name="openwq_run_time_end") + + USE iso_c_binding + implicit none + integer(c_int) :: openwq_run_time_end_c ! returns 0 (success) or -1 (failure) + type(c_ptr), intent(in), value :: openWQ + integer(c_int), intent(in) :: simtime(5) + + end function + +end interface \ No newline at end of file diff --git a/build/source/openwq/summa_openWQ.f90 b/build/source/openwq/summa_openWQ.f90 new file mode 100644 index 000000000..ad83167f1 --- /dev/null +++ b/build/source/openwq/summa_openWQ.f90 @@ -0,0 +1,1032 @@ +module summa_openwq + USE nrtype + USE openWQ,only:CLASSWQ_openwq + USE data_types,only:gru_hru_doubleVec + implicit none + private + ! Subroutines + public :: openwq_init + public :: openwq_run_time_start + public :: openwq_run_space_step + public :: openwq_run_time_end + private:: openWQ_run_time_start_inner ! inner call at the HRU level + + ! Global Data for prognostic Variables of HRUs + type(gru_hru_doubleVec),save,public :: progStruct_timestep_start ! copy of progStruct at the start of timestep for passing fluxes + type(CLASSWQ_openwq),save,public :: openwq_obj + + + contains + +! Initialize the openWQ object +subroutine openwq_init(err) + USE globalData,only:gru_struc ! gru-hru mapping structures + USE globalData,only:prog_meta + USE globalData,only:maxLayers,maxSnowLayers + USE allocspace_progStuct_module,only:allocGlobal_porgStruct ! module to allocate space for global data structures + implicit none + + ! Dummy Varialbes + integer(i4b), intent(out) :: err + + ! local variables + integer(i4b) :: hruCount + integer(i4b) :: nSoil + ! OpenWQ dimensions + integer(i4b) :: nCanopy_2openwq = 1 ! Canopy has only 1 layer + integer(i4b) :: nRunoff_2openwq = 1 ! Runoff has only 1 layer (not a summa variable - openWQ keeps track of this) + integer(i4b) :: nAquifer_2openwq = 1 ! GW has only 1 layer + integer(i4b) :: nYdirec_2openwq = 1 ! number of layers in the y-dir (not used in summa) + ! error handling + character(len=256) :: message + + ! nx -> num of HRUs) + ! ny -> 1 + ! nz -> num of layers (snow + soil) + openwq_obj = CLASSWQ_openwq() ! initalize openWQ object + + hruCount = sum( gru_struc(:)%hruCount ) + + nSoil = maxLayers - maxSnowLayers + + ! intialize openWQ + err=openwq_obj%decl( & + hruCount, & ! num HRU + nCanopy_2openwq, & ! num layers of canopy (fixed to 1) + maxSnowLayers, & ! num layers of snow (fixed to max of 5 because it varies) + nSoil, & ! num layers of snoil (variable) + nRunoff_2openwq, & ! num layers of runoff (fixed to 1) + nAquifer_2openwq, & ! num layers of aquifer (fixed to 1) + nYdirec_2openwq) ! num of layers in y-dir (set to 1 because not used in summa) + + + ! Create copy of state information, needed for passing to openWQ with fluxes that require + ! the previous time_steps volume + call allocGlobal_porgStruct(prog_meta,progStruct_timestep_start,maxSnowLayers,err,message) + +end subroutine openwq_init + +! Pass Summa State to openWQ +subroutine openwq_run_time_start(summa1_struc) + USE summa_type, only: summa1_type_dec ! master summa data type + USE var_lookup,only: iLookINDEX + implicit none + + ! Dummy Varialbes + type(summa1_type_dec), intent(in) :: summa1_struc + ! local variables + integer(i4b) :: openWQArrayIndex !index into OpenWQ's state structure + integer(i4b) :: iGRU + integer(i4b) :: iHRU + integer(i4b) :: nHRU ! number of HRUs in the GRU (used in looping) + integer(i4b) :: nSoil + integer(i4b) :: nSnow + logical(1) :: lastHRUFlag + summaVars: associate(& + progStruct => summa1_struc%progStruct , & + timeStruct => summa1_struc%timeStruct , & + attrStruct => summa1_struc%attrStruct , & + indxStruct => summa1_struc%indxStruct , & + nGRU => summa1_struc%nGRU & + ) + ! ############################ + + openWQArrayIndex = 0 + lastHRUFlag = .false. + + do iGRU=1,nGRU + nHRU = size(progStruct%gru(iGRU)%hru(:)) + do iHRU=1,nHRU + if (iGRU == nGRU .and. iHRU == nHRU )then + lastHRUFlag = .true. + end if + + nSnow = indxStruct%gru(iGRU)%hru(iHRU)%var(iLookINDEX%nSnow)%dat(1) + nSoil = indxStruct%gru(iGRU)%hru(iHRU)%var(iLookINDEX%nSoil)%dat(1) + + call openwq_run_time_start_inner(openWQArrayIndex, iGRU, iHRU, & + summa1_struc,& + nSnow, nSoil, lastHRUFlag) + + openWQArrayIndex = openWQArrayIndex + 1 + + end do ! end HRU + end do ! end GRU + + end associate summaVars +end subroutine + + +subroutine openWQ_run_time_start_inner(openWQArrayIndex, iGRU, iHRU, & + summa1_struc, nSnow, nSoil, last_hru_flag) + USE summa_type,only: summa1_type_dec ! master summa data type + USE var_lookup,only: iLookPROG ! named variables for state variables + USE var_lookup,only: iLookATTR ! named variables for real valued attribute data structure + USE var_lookup,only: iLookINDEX + USE var_lookup,only: iLookVarType ! named variables for real valued attribute data structure + USE var_lookup,only: iLookTIME ! named variables for time data structure + USE globalData,only:prog_meta + USE globalData,only:realMissing + USE multiconst,only:iden_water ! intrinsic density of liquid water (kg m-3) + implicit none + integer(i4b), intent(in) :: openWQArrayIndex !index into OpenWQ's state structure + integer(i4b), intent(in) :: iGRU + integer(i4b), intent(in) :: iHRU + type(summa1_type_dec), intent(in) :: summa1_struc + integer(i4b), intent(in) :: nSnow + integer(i4b), intent(in) :: nSoil + logical(1),intent(in) :: last_hru_flag + + ! local variables + integer(i4b) :: simtime(5) ! 5 time values yy-mm-dd-hh-min + real(rkind) :: canopyWatVol_stateVar_summa_m3 ! OpenWQ State Var + real(rkind) :: sweWatVol_stateVar_summa_m3(nSnow) ! OpenWQ State Var + real(rkind) :: soilTemp_depVar_summa_K(nSoil) ! OpenWQ State Var + real(rkind) :: soilWatVol_stateVar_summa_m3(nSoil)! OpenWQ State Var + real(rkind) :: soilMoist_depVar_summa_frac(nSoil) ! OpenWQ State Var + real(rkind) :: aquiferWatVol_stateVar_summa_m3 ! OpenWQ State Var + ! counter variables + integer(i4b) :: ilay + integer(i4b) :: iVar + integer(i4b) :: iDat + integer(i4b) :: index + integer(i4b) :: offset + ! error handling + integer(i4b) :: err + + summaVars: associate(& + progStruct => summa1_struc%progStruct , & + timeStruct => summa1_struc%timeStruct , & + hru_area_m2 => summa1_struc%attrStruct%gru(iGRU)%hru(iHRU)%var(iLookATTR%HRUarea) ,& + Tair_summa_K => summa1_struc%progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! air temperature (K) + scalarCanopyWat_summa_kg_m2 => summa1_struc%progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! canopy water (kg m-2) + mLayerDepth_summa_m => summa1_struc%progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerDepth)%dat(:) ,& ! depth of each layer (m) + mLayerVolFracWat_summa_frac => summa1_struc%progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracWat)%dat(:) ,& ! volumetric fraction of total water in each layer (-) + Tsoil_summa_K => summa1_struc%progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerTemp)%dat(:) ,& ! soil temperature (K) for each layer + AquiferStorWat_summa_m => summa1_struc%progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarAquiferStorage)%dat(1) & ! aquifer storage (m) + ) + + ! ############################ + ! Update unlayered variables and dependencies (1 layer only) + ! ############################ + + if(Tair_summa_K == realMissing) then + stop 'Error: OpenWQ requires air temperature (K)' + endif + + ! Vegetation + ! unit for volume = m3 (summa-to-openwq unit conversions needed) + ! scalarCanopyWat [kg m-2], so needs to to multiply by hru area [m2] and divide by water density + if(scalarCanopyWat_summa_kg_m2 == realMissing) then + canopyWatVol_stateVar_summa_m3 = 0._rkind + else + canopyWatVol_stateVar_summa_m3 = scalarCanopyWat_summa_kg_m2 * hru_area_m2 / iden_water + endif + + ! Aquifer + ! unit for volume = m3 (summa-to-openwq unit conversions needed) + ! scalarAquiferStorage [m], so needs to to multiply by hru area [m2] only + if(AquiferStorWat_summa_m == realMissing) then + stop 'Error: OpenWQ requires aquifer storage (m3)' + endif + aquiferWatVol_stateVar_summa_m3 = AquiferStorWat_summa_m * hru_area_m2 + + ! ############################ + ! Update layered variables and dependenecies + ! ############################ + + if (nSnow .gt. 0)then + do ilay = 1, nSnow + ! Snow + ! unit for volume = m3 (summa-to-openwq unit conversions needed) + ! mLayerVolFracIce and mLayerVolFracLiq [-], so needs to to multiply by hru area [m2] and divide by water density + ! But needs to account for both ice and liquid, and convert to liquid volumes + if(mLayerVolFracWat_summa_frac(ilay) /= realMissing) then + sweWatVol_stateVar_summa_m3(ilay) = & + mLayerVolFracWat_summa_frac(ilay) * mLayerDepth_summa_m(ilay) * hru_area_m2 + else + sweWatVol_stateVar_summa_m3(ilay) = 0._rkind + endif + enddo ! end snow layers + endif ! end snow + + + do ilay = 1, nSoil + ! Soil + ! Tsoil + ! (Summa in K) + if(Tsoil_summa_K(nSnow+ilay) == realMissing) then + stop 'Error: OpenWQ requires soil temperature (K)' + endif + soilTemp_depVar_summa_K(ilay) = Tsoil_summa_K(nSnow+ilay) + + soilMoist_depVar_summa_frac(ilay) = 0 ! TODO: Find the value for this varaibles + ! Soil + ! unit for volume = m3 (summa-to-openwq unit conversions needed) + ! mLayerMatricHead [m], so needs to to multiply by hru area [m2] + if(mLayerVolFracWat_summa_frac(nSnow+ilay) == realMissing) then + stop 'Error: OpenWQ requires soil water (m3)' + endif + soilWatVol_stateVar_summa_m3(ilay) = & + mLayerVolFracWat_summa_frac(nSnow+ilay) * hru_area_m2 * mLayerDepth_summa_m(nSnow+ilay) + enddo + + + ! Copy the prog structure + do iVar = 1, size(progStruct%gru(iGRU)%hru(iHRU)%var) + do iDat = 1, size(progStruct%gru(iGRU)%hru(iHRU)%var(iVar)%dat) + select case(prog_meta(iVar)%vartype) + case(iLookVarType%ifcSoil); + offset = 0 + case(iLookVarType%ifcToto); + offset = 0 + case default + offset = 1 + end select + do index = offset , size(progStruct%gru(iGRU)%hru(iHRU)%var(iVar)%dat) - 1 + offset + progStruct_timestep_start%gru(iGRU)%hru(iHRU)%var(iVar)%dat(index) = progStruct%gru(iGRU)%hru(iHRU)%var(iVar)%dat(index) + enddo + end do + end do + + + simtime(1) = timeStruct%var(iLookTIME%iyyy) ! Year + simtime(2) = timeStruct%var(iLookTIME%im) ! month + simtime(3) = timeStruct%var(iLookTIME%id) ! hour + simtime(4) = timeStruct%var(iLookTIME%ih) ! day + simtime(5) = timeStruct%var(iLookTIME%imin) ! minute + + err=openwq_obj%openwq_run_time_start(& + last_hru_flag, & + openWQArrayIndex, & ! total HRUs + nSnow, & + nSoil, & + simtime, & + soilMoist_depVar_summa_frac, & + soilTemp_depVar_summa_K, & + Tair_summa_K, & ! air temperature (K) + sweWatVol_stateVar_summa_m3, & + canopyWatVol_stateVar_summa_m3, & + soilWatVol_stateVar_summa_m3, & + aquiferWatVol_stateVar_summa_m3) + end associate summaVars + +end subroutine openWQ_run_time_start_inner + + +subroutine openwq_run_space_step(summa1_struc) + USE var_lookup,only: iLookPROG ! named variables for state variables + USE var_lookup,only: iLookTIME ! named variables for time data structure + USE var_lookup,only: iLookFLUX ! named varaibles for flux data + USE var_lookup,only: iLookATTR ! named variables for real valued attribute data structure + USE var_lookup,only: iLookINDEX + USE var_lookup, only: iLookTYPE ! look-up values for classification of veg, soils etc. + USE summa_type,only: summa1_type_dec ! master summa data type + USE data_types,only: var_dlength,var_i + USE globalData,only: gru_struc + USE globalData,only: data_step ! time step of forcing data (s) + USE globalData,only: realMissing + USE multiconst,only:& + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water ! intrinsic density of liquid water (kg m-3) + USE module_sf_noahmplsm,only:isWater ! Identifier for water land cover type + + implicit none + + type(summa1_type_dec), intent(in) :: summa1_struc + + + integer(i4b) :: hru_index ! needed because openWQ saves hrus as a single array + integer(i4b) :: iHRU ! variable needed for looping + integer(i4b) :: iGRU ! variable needed for looping + integer(i4b) :: iLayer ! varaible needed for looping + + integer(i4b) :: simtime(5) ! 5 time values yy-mm-dd-hh-min + integer(i4b) :: err + + ! compartment indexes in OpenWQ (defined in the hydrolink) + integer(i4b) :: canopy_index_openwq = 0 + integer(i4b) :: snow_index_openwq = 1 + integer(i4b) :: runoff_index_openwq = 2 + integer(i4b) :: soil_index_openwq = 3 + integer(i4b) :: aquifer_index_openwq = 4 + integer(i4b) :: OpenWQindex_s + integer(i4b) :: OpenWQindex_r + integer(i4b) :: iy_r + integer(i4b) :: iz_r + integer(i4b) :: iy_s + integer(i4b) :: iz_s + real(rkind) :: wflux_s2r + real(rkind) :: wmass_source + + ! Summa to OpenWQ units + ! PrecipVars + real(rkind) :: scalarRainfall_summa_m3 + real(rkind) :: scalarSnowfall_summa_m3 + real(rkind) :: scalarThroughfallRain_summa_m3 + real(rkind) :: scalarThroughfallSnow_summa_m3 + ! CanopyVars + real(rkind) :: canopyStorWat_kg_m3 + real(rkind) :: scalarCanopySnowUnloading_summa_m3 + real(rkind) :: scalarCanopyLiqDrainage_summa_m3 + real(rkind) :: scalarCanopyTranspiration_summa_m3 + real(rkind) :: scalarCanopyEvaporation_summa_m3 + real(rkind) :: scalarCanopySublimation_summa_m3 + ! runoff vars + real(rkind) :: scalarRunoffVol_m3 + real(rkind) :: scalarSurfaceRunoff_summa_m3 + real(rkind) :: scalarInfiltration_summa_m3 + ! Snow_SoilVars + real(rkind) :: mLayerLiqFluxSnow_summa_m3 + real(rkind) :: iLayerLiqFluxSoil_summa_m3 + real(rkind) :: mLayerVolFracWat_summa_m3 + real(rkind) :: scalarSnowSublimation_summa_m3 + real(rkind) :: scalarSfcMeltPond_summa_m3 + real(rkind) :: scalarGroundEvaporation_summa_m3 + real(rkind) :: scalarExfiltration_summa_m3 + real(rkind) :: mLayerBaseflow_summa_m3 + real(rkind) :: scalarSoilDrainage_summa_m3 + real(rkind) :: mLayerTranspire_summa_m3 + ! AquiferVars + real(rkind) :: scalarAquiferBaseflow_summa_m3 + real(rkind) :: scalarAquiferRecharge_summa_m3 + real(rkind) :: scalarAquiferStorage_summa_m3 + real(rkind) :: scalarAquiferTranspire_summa_m3 + + summaVars: associate(& + timeStruct => summa1_struc%timeStruct , & + fluxStruct => summa1_struc%fluxStruct , & + nGRU => summa1_struc%nGRU) + + + + simtime(1) = timeStruct%var(iLookTIME%iyyy) ! Year + simtime(2) = timeStruct%var(iLookTIME%im) ! month + simtime(3) = timeStruct%var(iLookTIME%id) ! hour + simtime(4) = timeStruct%var(iLookTIME%ih) ! day + simtime(5) = timeStruct%var(iLookTIME%imin) ! minute + + hru_index = 0 + + ! Summa does not have a y-direction, + ! so the dimension will always be 1 + iy_r = 1 + iy_s = 1 + + do iGRU=1,nGRU + do iHRU=1,gru_struc(iGRU)%hruCount + hru_index = hru_index + 1 + if (summa1_struc%typeStruct%gru(iGRU)%hru(iHRU)%var(iLookTYPE%vegTypeIndex) == isWater) cycle + + ! #################################################################### + ! Associate relevant variables + ! #################################################################### + + DomainVars: associate( & + ! General Summa info + hru_area_m2 => summa1_struc%attrStruct%gru(iGRU)%hru(iHRU)%var(iLookATTR%HRUarea) & + ) + + PrecipVars: associate( & + ! Precipitation + scalarRainfall_summa_kg_m2_s => fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%scalarRainfall)%dat(1) ,& + scalarSnowfall_summa_kg_m2_s => fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%scalarSnowfall)%dat(1) ,& + scalarThroughfallRain_summa_kg_m2_s => fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& + scalarThroughfallSnow_summa_kg_m2_s => fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%scalarThroughfallSnow)%dat(1) & + ) + + CanopyVars: associate( & + ! Canopy + scalarCanopyWat_summa_kg_m2 => progStruct_timestep_start%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyWat)%dat(1) ,& + scalarCanopySnowUnloading_summa_kg_m2_s => fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%scalarCanopySnowUnloading)%dat(1) ,& + scalarCanopyLiqDrainage_summa_kg_m2_s => fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& + scalarCanopyTranspiration_summa_kg_m2_s => fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& + scalarCanopyEvaporation_summa_kg_m2_s => fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& + scalarCanopySublimation_summa_kg_m2_s => fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%scalarCanopySublimation)%dat(1) & + ) + + RunoffVars: associate(& + scalarSurfaceRunoff_m_s => fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%scalarSurfaceRunoff)%dat(1) ,& + scalarInfiltration_m_s => fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%scalarInfiltration)%dat(1) & + ) + + Snow_SoilVars: associate(& + ! Snow + Soil - Control Volume + current_nSnow => summa1_struc%indxStruct%gru(iGRU)%hru(iHRU)%var(iLookINDEX%nSnow)%dat(1) ,& + current_nSoil => summa1_struc%indxStruct%gru(iGRU)%hru(iHRU)%var(iLookINDEX%nSoil)%dat(1) ,& + nSnow => gru_struc(iGRU)%hruInfo(iHRU)%nSnow ,& + nSoil => gru_struc(iGRU)%hruInfo(iHRU)%nSoil ,& + ! Layer depth and water frac + mLayerDepth_summa_m => progStruct_timestep_start%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerDepth)%dat(:) ,& + mLayerVolFracWat_summa_frac => progStruct_timestep_start%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracWat)%dat(:) ,& + ! Snow Fluxes + scalarSnowSublimation_summa_kg_m2_s => fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%scalarSnowSublimation)%dat(1) ,& + scalarSfcMeltPond_kg_m2 => summa1_struc%progStruct%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarSfcMeltPond)%dat(1) ,& + iLayerLiqFluxSnow_summa_m_s => fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%iLayerLiqFluxSnow)%dat(:) ,& + + ! Soil Fluxes + scalarGroundEvaporation_summa_kg_m2_s => fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%scalarGroundEvaporation)%dat(1) ,& + iLayerLiqFluxSoil_summa_m_s => fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%iLayerLiqFluxSoil)%dat(:) ,& + scalarExfiltration_summa_m_s => fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%scalarExfiltration)%dat(1) ,& + mLayerBaseflow_summa_m_s => fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%mLayerBaseflow)%dat(:) ,& + scalarSoilDrainage_summa_m_s => fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%scalarSoilDrainage)%dat(1) ,& + mLayerTranspire_summa_m_s => fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%mLayerTranspire)%dat(:) & + ) + + AquiferVars: associate(& + ! Aquifer + scalarAquiferStorage_summa_m => progStruct_timestep_start%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarAquiferStorage)%dat(1), & + scalarAquiferRecharge_summa_m_s => fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%scalarAquiferRecharge)%dat(1) , & + scalarAquiferBaseflow_summa_m_s => fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%scalarAquiferBaseflow)%dat(1) , & + scalarAquiferTranspire_summa_m_s => fluxStruct%gru(iGRU)%hru(iHRU)%var(iLookFLUX%scalarAquiferTranspire)%dat(1) & + ) + + ! #################################################################### + ! Converte associate variable units: from SUMMA to OpenWQ units + ! Here only scalar/unlayered variables + ! OpenWQ: Volume (m3), Time (sec) + ! Where: Vol in kg/m2, then convert to m3 by multipling by (hru_area_m2 / iden_water) + ! Where: Flux in kg/m2/s, then convert to m3/time_step by multiplying by (hru_area_m2 * data_step / iden_water) + ! #################################################################### + + ! PrecipVars + scalarRainfall_summa_m3 = scalarRainfall_summa_kg_m2_s * hru_area_m2 * data_step / iden_water + scalarSnowfall_summa_m3 = scalarSnowfall_summa_kg_m2_s * hru_area_m2 * data_step / iden_water + scalarThroughfallRain_summa_m3 = scalarThroughfallRain_summa_kg_m2_s * hru_area_m2 * data_step / iden_water ! flux + scalarThroughfallSnow_summa_m3 = scalarThroughfallSnow_summa_kg_m2_s * hru_area_m2 * data_step / iden_water ! flux + + ! CanopyVars + canopyStorWat_kg_m3 = scalarCanopyWat_summa_kg_m2 * hru_area_m2 / iden_water ! vol + scalarCanopySnowUnloading_summa_m3 = scalarCanopySnowUnloading_summa_kg_m2_s * hru_area_m2 * data_step / iden_water ! flux + scalarCanopyLiqDrainage_summa_m3 = scalarCanopyLiqDrainage_summa_kg_m2_s * hru_area_m2 * data_step / iden_water ! flux + scalarCanopyTranspiration_summa_m3 = scalarCanopyTranspiration_summa_kg_m2_s * hru_area_m2 * data_step / iden_water ! flux + scalarCanopyEvaporation_summa_m3 = scalarCanopyEvaporation_summa_kg_m2_s * hru_area_m2 * data_step / iden_water ! flux + scalarCanopySublimation_summa_m3 = scalarCanopySublimation_summa_kg_m2_s * hru_area_m2 * data_step / iden_water ! flux + + ! runoff vars + scalarSurfaceRunoff_summa_m3 = scalarSurfaceRunoff_m_s * hru_area_m2 * data_step + scalarInfiltration_summa_m3 = scalarInfiltration_m_s * hru_area_m2 * data_step + + + ! Snow_SoilVars (unlayered variables) + ! Other variables are layered and added below as needed + scalarSnowSublimation_summa_m3 = scalarSnowSublimation_summa_kg_m2_s * hru_area_m2 * data_step / iden_water + scalarGroundEvaporation_summa_m3 = scalarGroundEvaporation_summa_kg_m2_s * hru_area_m2 * data_step / iden_water + scalarSfcMeltPond_summa_m3 = scalarSfcMeltPond_kg_m2 * hru_area_m2 / iden_water + scalarExfiltration_summa_m3 = scalarExfiltration_summa_m_s * hru_area_m2 * data_step + scalarSoilDrainage_summa_m3 = scalarSoilDrainage_summa_m_s * hru_area_m2 * data_step + + + ! AquiferVars + scalarAquiferStorage_summa_m3 = scalarAquiferStorage_summa_m * hru_area_m2 + scalarAquiferRecharge_summa_m3 = scalarAquiferRecharge_summa_m_s * hru_area_m2 * data_step + scalarAquiferBaseflow_summa_m3 = scalarAquiferBaseflow_summa_m_s * hru_area_m2 * data_step + scalarAquiferTranspire_summa_m3 = scalarAquiferTranspire_summa_m_s * hru_area_m2 * data_step + + ! Reset Runoff (it's not tracked by SUMMA, so need to track it here) + scalarRunoffVol_m3 = 0._rkind !initialization of this variable is required to limit the runoff aggreggation to each hru. + + ! #################################################################### + ! Apply Fluxes + ! Call RunSpaceStep + ! #################################################################### + + ! -------------------------------------------------------------------- + ! %%%%%%%%%%%%%%%%%%%%%%%%%%% + ! 1 Fluxes involving the canopy + ! %%%%%%%%%%%%%%%%%%%%%%%%%%% + ! -------------------------------------------------------------------- + if(scalarCanopyWat_summa_kg_m2 /= realMissing) then + + ! ==================================================== + ! 1.1 precipitation -> canopy + ! ==================================================== + ! *Source*: + ! PRECIP (external flux, so need call openwq_run_space_in) + ! *Recipient*: canopy (only 1 z layer) + OpenWQindex_r = canopy_index_openwq + iz_r = 1 + ! *Flux*: the portion of rainfall and snowfall not throughfall + wflux_s2r = (scalarRainfall_summa_m3 - scalarThroughfallRain_summa_m3) & + + (scalarSnowfall_summa_m3 - scalarThroughfallSnow_summa_m3) + ! *Call openwq_run_space_in* if wflux_s2r not 0 + err=openwq_obj%openwq_run_space_in( & + simtime, & + 'PRECIP', & + OpenWQindex_r, hru_index, iy_r, iz_r, & + wflux_s2r) + + ! ==================================================== + ! 1.2 canopy -> upper snow layer or runoff pool + ! scalarCanopySnowUnloading + scalarCanopyLiqDrainage + ! ==================================================== + ! *Flux* + ! snow uloading + liq drainage + wflux_s2r = scalarCanopySnowUnloading_summa_m3 & + + scalarCanopyLiqDrainage_summa_m3 + ! *Source* + ! canopy (only 1 z layer) + OpenWQindex_s = canopy_index_openwq + iz_s = 1 + wmass_source = canopyStorWat_kg_m3 + ! *Recipient* depends on snow layers + if (current_nSnow .gt. 0)then + OpenWQindex_r = snow_index_openwq + iz_r = 1 ! upper layer + else + OpenWQindex_r = runoff_index_openwq + iz_r = 1 ! (has only 1 layer) + scalarRunoffVol_m3 = scalarRunoffVol_m3 + wflux_s2r; + end if + ! *Call openwq_run_space* if wflux_s2r not 0 + err=openwq_obj%openwq_run_space( & + simtime, & + OpenWQindex_s, hru_index, iy_s, iz_s, & + OpenWQindex_r, hru_index, iy_r, iz_r, & + wflux_s2r, & + wmass_source) + + ! ==================================================== + ! 1.3 canopy -> OUT (lost from model) (Evap + Subl) + ! ==================================================== + ! *Source*: + ! canopy (only 1 z layer) + OpenWQindex_s = canopy_index_openwq + iz_s = 1 + wmass_source = canopyStorWat_kg_m3 + ! *Recipient*: + ! lost from system + OpenWQindex_r = -1 + iz_r = -1 + ! *Flux* + ! transpiration + evaporation + sublimation + wflux_s2r = scalarCanopyEvaporation_summa_m3 & + + scalarCanopySublimation_summa_m3 + + ! *Call openwq_run_space* if wflux_s2r not 0 + err=openwq_obj%openwq_run_space( & + simtime, & + OpenWQindex_s, hru_index, iy_s, iz_s, & + OpenWQindex_r, hru_index, iy_r, iz_r, & + wflux_s2r, & + wmass_source) + + endif + + ! -------------------------------------------------------------------- + ! %%%%%%%%%%%%%%%%%%%%%%%%%%% + ! 2. Snow / runoff + ! %%%%%%%%%%%%%%%%%%%%%%%%%%% + ! -------------------------------------------------------------------- + ! do all the snow fluxes + + ! ==================================================== + ! 2.1 precicipitation -> upper snow/runoff layer + ! scalarThroughfallRain + scalarThroughfallSnow + ! ==================================================== + ! *Flux* + ! throughfall rain and snow + wflux_s2r = scalarThroughfallRain_summa_m3 & + + scalarThroughfallSnow_summa_m3 + if (current_nSnow .gt. 0)then + ! *Source*: + ! PRECIP (external flux, so need call openwq_run_space_in) + ! *Recipient*: + ! snow+soil (upper layer) + OpenWQindex_r = snow_index_openwq + iz_r = 1 + else + OpenWQindex_r = runoff_index_openwq + iz_r = 1 + scalarRunoffVol_m3 = scalarRunoffVol_m3 + wflux_s2r ! Needed because runoff volume is not tracked + end if + ! *Call openwq_run_space* if wflux_s2r not 0 + err=openwq_obj%openwq_run_space_in( & + simtime, & + 'PRECIP', & + OpenWQindex_r, hru_index, iy_r, iz_r, & + wflux_s2r & + ) + + ! Below fluxes only occur when there is no snow + if (current_nSnow .gt. 0)then + + ! ==================================================== + ! 2.2 snow -> OUT (lost from model) (sublimation) + ! ==================================================== + ! *Source*: + ! snow (upper layer) + OpenWQindex_s = snow_index_openwq + iz_s = 1 + mLayerVolFracWat_summa_m3 = mLayerVolFracWat_summa_frac(1) * hru_area_m2 * mLayerDepth_summa_m(1) + wmass_source = mLayerVolFracWat_summa_m3 + ! *Recipient*: + ! lost from system + OpenWQindex_r = -1 + iz_r = -1 + ! *Flux* + ! snow sublimation + wflux_s2r = scalarSnowSublimation_summa_m3 + ! *Call openwq_run_space* if wflux_s2r not 0 + err=openwq_obj%openwq_run_space( & + simtime, & + OpenWQindex_s, hru_index, iy_s, iz_s, & + OpenWQindex_r, hru_index, iy_r, iz_r, & + wflux_s2r, & + wmass_source) + + ! ==================================================== + ! 2.3 snow internal fluxes + ! ==================================================== + do iLayer = 1, nSnow-1 ! last layer of snow becomes different fluxes + ! *Source*: + ! snow(iLayer) + OpenWQindex_s = snow_index_openwq + iz_s = iLayer + mLayerVolFracWat_summa_m3 = mLayerVolFracWat_summa_frac(iLayer) * hru_area_m2 * mLayerDepth_summa_m(iLayer) + wmass_source = mLayerVolFracWat_summa_m3 + ! *Recipient*: + ! snow(iLayer+1) + OpenWQindex_r = snow_index_openwq + iz_r = iLayer + 1 + ! *Flux* + mLayerLiqFluxSnow_summa_m3 = iLayerLiqFluxSnow_summa_m_s(iLayer) * hru_area_m2 * data_step + wflux_s2r = mLayerLiqFluxSnow_summa_m3 + ! *Call openwq_run_space* if wflux_s2r not 0 + err=openwq_obj%openwq_run_space( & + simtime, & + OpenWQindex_s, hru_index, iy_s, iz_s, & + OpenWQindex_r, hru_index, iy_r, iz_r, & + wflux_s2r, & + wmass_source) + end do + + ! ==================================================== + ! 2.4 snow drainage from the last soil layer -> runoff + ! ==================================================== + ! *Flux* + mLayerLiqFluxSnow_summa_m3 = iLayerLiqFluxSnow_summa_m_s(nSnow) * hru_area_m2 * data_step + wflux_s2r = mLayerLiqFluxSnow_summa_m3 + ! *Source*: + ! snow(nSnow) + OpenWQindex_s = snow_index_openwq + iz_s = iLayer + mLayerVolFracWat_summa_m3 = mLayerVolFracWat_summa_frac(nSnow) * hru_area_m2 * mLayerDepth_summa_m(nSnow) + wmass_source = mLayerVolFracWat_summa_m3 + ! *Recipient*: + ! runoff (has one layer only) + OpenWQindex_r = runoff_index_openwq + iz_r = 1 + scalarRunoffVol_m3 = scalarRunoffVol_m3 + wflux_s2r; + ! *Call openwq_run_space* if wflux_s2r not 0 + err=openwq_obj%openwq_run_space( & + simtime, & + OpenWQindex_s, hru_index, iy_s, iz_s, & + OpenWQindex_r, hru_index, iy_r, iz_r, & + wflux_s2r, & + wmass_source) + end if + + ! ==================================================== + ! 2.5 snow without a layer -> runoff + ! scalarSfcMeltPond should be 0 if this occurs + ! ==================================================== + ! *Source* + ! snow (this is the case of snow without layer) + + ! need the if condition to protect from invalid read + ! if the size of mLayerVolFracWat_summa_frac matches the number of soil layers + ! then summa is expecting no snow for this HRU over the simulation of the model + if ((nSnow .gt. 0)) then + ! *Flux* + ! snow uloading + liq drainage + wflux_s2r = scalarSfcMeltPond_summa_m3 + ! *Source* + OpenWQindex_s = snow_index_openwq + iz_s = 1 + mLayerVolFracWat_summa_m3 = mLayerVolFracWat_summa_frac(nSnow) * hru_area_m2 * mLayerDepth_summa_m(nSnow) + wmass_source = mLayerVolFracWat_summa_m3 + ! *Recipient* + ! runoff (has one layer only) + OpenWQindex_r = runoff_index_openwq + iz_r = 1 + scalarRunoffVol_m3 = scalarRunoffVol_m3 + wflux_s2r; + ! *Call openwq_run_space* if wflux_s2r not 0 + err=openwq_obj%openwq_run_space( & + simtime, & + OpenWQindex_s, hru_index, iy_s, iz_s, & + OpenWQindex_r, hru_index, iy_r, iz_r, & + wflux_s2r, & + wmass_source) + endif + ! -------------------------------------------------------------------- + ! %%%%%%%%%%%%%%%%%%%%%%%%%%% + ! 3. runoff + ! %%%%%%%%%%%%%%%%%%%%%%%%%%% + ! -------------------------------------------------------------------- + + ! ==================================================== + ! 3.1 infiltration + ! runoff -> top layer of the soil + ! ==================================================== + ! *Flux* + wflux_s2r = scalarInfiltration_summa_m3 + ! *Source*: + ! runoff (has 1 layer only) + OpenWQindex_s = runoff_index_openwq + iz_s = 1 + wmass_source = scalarRunoffVol_m3 + ! *Recipient*: + ! soil upper layer + OpenWQindex_r = soil_index_openwq + iz_r = 1 + scalarRunoffVol_m3 = scalarRunoffVol_m3 - wflux_s2r; + ! *Call openwq_run_space* if wflux_s2r not 0 + err=openwq_obj%openwq_run_space( & + simtime, & + OpenWQindex_s, hru_index, iy_s, iz_s, & + OpenWQindex_r, hru_index, iy_r, iz_r, & + wflux_s2r, & + wmass_source) + + ! ==================================================== + ! 3.2 surface runoff + ! runoff -> OUT lost from the system + ! ==================================================== + ! *Source*: + ! runoff (has only 1 layer) + OpenWQindex_s = runoff_index_openwq + iz_s = 1 + wmass_source = scalarRunoffVol_m3 + ! *Recipient*: + ! lost from system + OpenWQindex_r = -1 + iz_r = -1 + ! *Flux* + ! wflux_s2r = scalarSurfaceRunoff_summa_m3 + wflux_s2r = scalarRunoffVol_m3 + ! *Call openwq_run_space* if wflux_s2r not 0 + err=openwq_obj%openwq_run_space( & + simtime, & + OpenWQindex_s, hru_index, iy_s, iz_s, & + OpenWQindex_r, hru_index, iy_r, iz_r, & + wflux_s2r, & + wmass_source) + + ! -------------------------------------------------------------------- + ! %%%%%%%%%%%%%%%%%%%%%%%%%%% + ! 4. soil + ! %%%%%%%%%%%%%%%%%%%%%%%%%%% + ! -------------------------------------------------------------------- + + ! ==================================================== + ! 4.1 soil fluxes + ! upper soil -> OUT (lost from system) (ground evaporation) + ! ==================================================== + ! *Source*: + ! upper soil layer + OpenWQindex_s = soil_index_openwq + iz_s = 1 + mLayerVolFracWat_summa_m3 = mLayerVolFracWat_summa_frac(nSnow+1) * hru_area_m2 * mLayerDepth_summa_m(nSnow+1) + wmass_source = mLayerVolFracWat_summa_m3 + ! *Recipient*: + ! lost from system + OpenWQindex_r = -1 + iz_r = -1 + ! *Flux* + wflux_s2r = scalarGroundEvaporation_summa_m3 + ! *Call openwq_run_space* if wflux_s2r not 0 + err=openwq_obj%openwq_run_space( & + simtime, & + OpenWQindex_s, hru_index, iy_s, iz_s, & + OpenWQindex_r, hru_index, iy_r, iz_r, & + wflux_s2r, & + wmass_source) + + ! ==================================================== + ! 4.2 exfiltration + ! Lost from the system (first soil layer) + ! ==================================================== + ! *Source*: + ! upper soil layer + OpenWQindex_s = soil_index_openwq + iz_s = 1 + mLayerVolFracWat_summa_m3 = mLayerVolFracWat_summa_frac(nSnow+1) * hru_area_m2 * mLayerDepth_summa_m(nSnow+1) + wmass_source = mLayerVolFracWat_summa_m3 + ! *Recipient*: + ! lost from system + OpenWQindex_r = -1 + iz_r = -1 + ! *Flux* + wflux_s2r = scalarExfiltration_summa_m3 + ! *Call openwq_run_space* if wflux_s2r not 0 + err=openwq_obj%openwq_run_space( & + simtime, & + OpenWQindex_s, hru_index, iy_s, iz_s, & + OpenWQindex_r, hru_index, iy_r, iz_r, & + wflux_s2r, & + wmass_source) + + ! ==================================================== + ! 4.3 mLayerBaseflow + ! Lost from the system at each soil layer + ! ==================================================== + do iLayer = 1, nSoil + + ! *Source*: + ! each soil layer + OpenWQindex_s = soil_index_openwq + !iz_s = nSnow + iLayer + iz_s = iLayer + mLayerVolFracWat_summa_m3 = mLayerVolFracWat_summa_frac(iLayer+nSnow) * hru_area_m2 * mLayerDepth_summa_m(iLayer+nSnow) + wmass_source = mLayerVolFracWat_summa_m3 + ! *Recipient*: + ! lost from system + OpenWQindex_r = -1 + iz_r = -1 + ! *Flux* + mLayerBaseflow_summa_m3 = mLayerBaseflow_summa_m_s(iLayer) * hru_area_m2 * data_step + if (iLayer == 1)then + mLayerBaseflow_summa_m3 = mLayerBaseflow_summa_m3 - scalarExfiltration_summa_m3 + endif + wflux_s2r = mLayerBaseflow_summa_m3 + ! *Call openwq_run_space* if wflux_s2r not 0 + err=openwq_obj%openwq_run_space( & + simtime, & + OpenWQindex_s, hru_index, iy_s, iz_s, & + OpenWQindex_r, hru_index, iy_r, iz_r, & + wflux_s2r, & + wmass_source) + end do + + ! ==================================================== + ! 4.4 transpiration from the soil + ! Lost from the system + ! ==================================================== + do iLayer = 1, nSoil + ! *Source*: + ! all soil layers + OpenWQindex_s = soil_index_openwq + iz_s = iLayer + mLayerVolFracWat_summa_m3 = mLayerVolFracWat_summa_frac(iLayer+nSnow) * hru_area_m2 * mLayerDepth_summa_m(iLayer+nSnow) + wmass_source = mLayerVolFracWat_summa_m3 + ! *Recipient*: + ! lost from system + OpenWQindex_r = -1 + iz_r = -1 + mLayerTranspire_summa_m3 = mLayerTranspire_summa_m_s(iLayer) * hru_area_m2 * data_step + ! *Flux* + wflux_s2r = mLayerTranspire_summa_m3 + ! *Call openwq_run_space* if wflux_s2r not 0 + err=openwq_obj%openwq_run_space( & + simtime, & + OpenWQindex_s, hru_index, iy_s, iz_s, & + OpenWQindex_r, hru_index, iy_r, iz_r, & + wflux_s2r, & + wmass_source) + end do + + ! ==================================================== + ! 4.5 soil internal fluxes + ! ==================================================== + do iLayer = 1, nSoil - 1 ! last layer of soil becomes different fluxes + ! *Source*: + ! soil layer iLayer + OpenWQindex_s = soil_index_openwq + iz_s = iLayer + mLayerVolFracWat_summa_m3 = mLayerVolFracWat_summa_frac(iLayer+nSnow) * hru_area_m2 * mLayerDepth_summa_m(iLayer+nSnow) + wmass_source = mLayerVolFracWat_summa_m3 + ! *Recipient*: + ! soi layer iLayer+1 + OpenWQindex_r = soil_index_openwq + iz_r = iLayer + 1 + ! *Flux* + ! flux between soil layer + iLayerLiqFluxSoil_summa_m3 = iLayerLiqFluxSoil_summa_m_s(iLayer) * hru_area_m2 * data_step + wflux_s2r = iLayerLiqFluxSoil_summa_m3 + ! *Call openwq_run_space* if wflux_s2r not 0 + err=openwq_obj%openwq_run_space( & + simtime, & + OpenWQindex_s, hru_index, iy_s, iz_s, & + OpenWQindex_r, hru_index, iy_r, iz_r, & + wflux_s2r, & + wmass_source) + end do + + ! ==================================================== + ! 4.6 soil Draianage into the aquifer + ! ==================================================== + ! *Source*: + ! lower soil layer + OpenWQindex_s = soil_index_openwq + iz_s = nSoil + mLayerVolFracWat_summa_m3 = mLayerVolFracWat_summa_frac(nSoil) * hru_area_m2 * mLayerDepth_summa_m(nSoil) + wmass_source = mLayerVolFracWat_summa_m3 + ! *Recipient*: + ! aquifer (has only 1 layer) + OpenWQindex_r = aquifer_index_openwq + iz_r = 1 + ! *Flux* + ! flux between soil layer (it's -1 because the first layer gets) + wflux_s2r = scalarSoilDrainage_summa_m3 + ! *Call openwq_run_space* if wflux_s2r not 0 + err=openwq_obj%openwq_run_space( & + simtime, & + OpenWQindex_s, hru_index, iy_s, iz_s, & + OpenWQindex_r, hru_index, iy_r, iz_r, & + wflux_s2r, & + wmass_source) + + ! -------------------------------------------------------------------- + ! %%%%%%%%%%%%%%%%%%%%%%%%%%% + ! 5 Aquifer Fluxes + ! %%%%%%%%%%%%%%%%%%%%%%%%%%% + ! -------------------------------------------------------------------- + + ! ==================================================== + ! 5.1 Aquifer -> OUT (lost from model) (baseflow) + ! ==================================================== + ! *Source*: + ! aquifer (only 1 z layer) + OpenWQindex_s = aquifer_index_openwq + iz_s = 1 + wmass_source = scalarAquiferStorage_summa_m3 + ! *Recipient*: + ! lost from system + OpenWQindex_r = -1 + iz_r = -1 + ! *Flux* + wflux_s2r = scalarAquiferBaseflow_summa_m3 + ! *Call openwq_run_space* if wflux_s2r not 0 + err=openwq_obj%openwq_run_space( & + simtime, & + OpenWQindex_s, hru_index, iy_s, iz_s, & + OpenWQindex_r, hru_index, iy_r, iz_r, & + wflux_s2r, & + wmass_source) + + ! ==================================================== + ! 5.2 Aquifer -> OUT (lost from model) (transpiration) + ! ==================================================== + ! *Source*: + ! aquifer (only 1 z layer) + OpenWQindex_s = aquifer_index_openwq + iz_s = 1 + wmass_source = scalarAquiferStorage_summa_m3 + ! *Recipient*: + ! lost from system + OpenWQindex_r = -1 + iz_r = -1 + ! *Flux* + wflux_s2r = scalarAquiferTranspire_summa_m3 + ! *Call openwq_run_space* if wflux_s2r not 0 + err=openwq_obj%openwq_run_space( & + simtime, & + OpenWQindex_s, hru_index, iy_s, iz_s, & + OpenWQindex_r, hru_index, iy_r, iz_r, & + wflux_s2r, & + wmass_source) + + end associate AquiferVars + end associate Snow_SoilVars + end associate RunoffVars + end associate CanopyVars + end associate PrecipVars + end associate DomainVars + + end do + end do +end associate summaVars +end subroutine openwq_run_space_step + + +subroutine openwq_run_time_end(summa1_struc) + USE summa_type, only:summa1_type_dec ! master summa data type + USE var_lookup, only:iLookTIME ! named variables for time data structure + implicit none + + ! Dummy Varialbes + type(summa1_type_dec), intent(in) :: summa1_struc + + ! Local Variables + integer(i4b) :: simtime(5) ! 5 time values yy-mm-dd-hh-min + integer(i4b) :: err ! error control + + summaVars: associate(& + timeStruct => summa1_struc%timeStruct & + ) + + simtime(1) = timeStruct%var(iLookTIME%iyyy) ! Year + simtime(2) = timeStruct%var(iLookTIME%im) ! month + simtime(3) = timeStruct%var(iLookTIME%id) ! hour + simtime(4) = timeStruct%var(iLookTIME%ih) ! day + simtime(5) = timeStruct%var(iLookTIME%imin) ! minute + + err=openwq_obj%openwq_run_time_end(simtime) ! minute + + end associate summaVars +end subroutine + + + +end module summa_openwq \ No newline at end of file diff --git a/build/source/openwq/summa_openWQ_allocspace.f90 b/build/source/openwq/summa_openWQ_allocspace.f90 new file mode 100644 index 000000000..491fe99f4 --- /dev/null +++ b/build/source/openwq/summa_openWQ_allocspace.f90 @@ -0,0 +1,204 @@ +! SUMMA - Structure for Unifying Multiple Modeling Alternatives +! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington +! +! This file is part of SUMMA +! +! For more information see: http://www.ral.ucar.edu/projects/summa +! +! This program is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see . + +module allocspace_progStuct_module + + ! data types + USE nrtype + + ! provide access to the derived types to define the data structures + USE data_types,only:& + ! final data vectors + dlength, & ! var%dat + ilength, & ! var%dat + ! no spatial dimension + var_i, & ! x%var(:) (i4b) + var_i8, & ! x%var(:) integer(8) + var_d, & ! x%var(:) (dp) + var_flagVec, & ! x%var(:)%dat (logical) + var_ilength, & ! x%var(:)%dat (i4b) + var_dlength, & ! x%var(:)%dat (dp) + ! gru dimension + gru_int, & ! x%gru(:)%var(:) (i4b) + gru_int8, & ! x%gru(:)%var(:) integer(8) + gru_double, & ! x%gru(:)%var(:) (dp) + gru_intVec, & ! x%gru(:)%var(:)%dat (i4b) + gru_doubleVec, & ! x%gru(:)%var(:)%dat (dp) + ! gru+hru dimension + gru_hru_int, & ! x%gru(:)%hru(:)%var(:) (i4b) + gru_hru_int8, & ! x%gru(:)%hru(:)%var(:) integer(8) + gru_hru_double, & ! x%gru(:)%hru(:)%var(:) (dp) + gru_hru_intVec, & ! x%gru(:)%hru(:)%var(:)%dat (i4b) + gru_hru_doubleVec ! x%gru(:)%hru(:)%var(:)%dat (dp) + + ! metadata structure + USE data_types,only:var_info ! data type for metadata + + ! access missing values + USE globalData,only:integerMissing ! missing integer + USE globalData,only:realMissing ! missing double precision number + + USE globalData,only: nTimeDelay ! number of timesteps in the time delay histogram + USE globalData,only: nBand ! number of spectral bands + + ! access variable types + USE var_lookup,only:iLookVarType ! look up structure for variable typed + USE var_lookup,only:maxvarFreq ! allocation dimension (output frequency) + + ! privacy + implicit none + private + public::allocGlobal_porgStruct + + + ! ----------------------------------------------------------------------------------------------------------------------------------- + contains + ! ************************************************************************************************ + ! public subroutine allocGlobal_progStruct: allocate space for progStruct_timestep_start + ! Modified copy of the subroutine allocGlobal() from allocspace.f90 specificly for allocating + ! the array progStruct_timestep_start + ! ************************************************************************************************ + subroutine allocGlobal_porgStruct(metaStruct,dataStruct,nSnow,err,message) + ! NOTE: safety -- ensure only used in allocGlobal + USE globalData,only: gru_struc ! gru-hru mapping structures + USE allocspace_module, only:allocLocal + implicit none + ! input + type(var_info),intent(in) :: metaStruct(:) ! metadata structure + integer(i4b),intent(in) :: nSnow + ! output + class(*),intent(out) :: dataStruct ! data structure + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local variables + logical(lgt) :: check ! .true. if structure is already allocated + integer(i4b) :: iHRU ! loop index through HRUs + integer(i4b) :: iGRU ! loop index through GRUs + integer(i4b) :: nGRU ! number of GRUs + logical(lgt) :: spatial ! spatial flag + character(len=256) :: cmessage ! error message of the downwind routine + ! initialize error control + err=0; message='allocGlobal_porgStruct/' + ! initialize allocation check + check=.false. + + ! get the number of GRUs + nGRU = size(gru_struc) + + ! * allocate GRU dimension + select type(dataStruct) + ! gru dimension only + class is (gru_int); if(allocated(dataStruct%gru))then; check=.true.; else; allocate(dataStruct%gru(nGRU),stat=err); end if + class is (gru_int8); if(allocated(dataStruct%gru))then; check=.true.; else; allocate(dataStruct%gru(nGRU),stat=err); end if + class is (gru_intVec); if(allocated(dataStruct%gru))then; check=.true.; else; allocate(dataStruct%gru(nGRU),stat=err); end if + class is (gru_double); if(allocated(dataStruct%gru))then; check=.true.; else; allocate(dataStruct%gru(nGRU),stat=err); end if + class is (gru_doubleVec); if(allocated(dataStruct%gru))then; check=.true.; else; allocate(dataStruct%gru(nGRU),stat=err); end if + ! gru+hru dimensions + class is (gru_hru_int); if(allocated(dataStruct%gru))then; check=.true.; else; allocate(dataStruct%gru(nGRU),stat=err); end if + class is (gru_hru_int8); if(allocated(dataStruct%gru))then; check=.true.; else; allocate(dataStruct%gru(nGRU),stat=err); end if + class is (gru_hru_intVec); if(allocated(dataStruct%gru))then; check=.true.; else; allocate(dataStruct%gru(nGRU),stat=err); end if + class is (gru_hru_double); if(allocated(dataStruct%gru))then; check=.true.; else; allocate(dataStruct%gru(nGRU),stat=err); end if + class is (gru_hru_doubleVec); if(allocated(dataStruct%gru))then; check=.true.; else; allocate(dataStruct%gru(nGRU),stat=err); end if + end select + + ! check errors + if(check) then; err=20; message=trim(message)//'GRU structure was unexpectedly allocated already'; return; end if + if(err/=0)then; err=20; message=trim(message)//'problem allocating GRU dimension'; return; end if + + ! * allocate HRU dimension + do iGRU=1,nGRU + ! allocate the HRU dimension + select type(dataStruct) + class is (gru_hru_int); if(allocated(dataStruct%gru(iGRU)%hru))then; check=.true.; else; allocate(dataStruct%gru(iGRU)%hru(gru_struc(iGRU)%hruCount),stat=err); end if + class is (gru_hru_int8); if(allocated(dataStruct%gru(iGRU)%hru))then; check=.true.; else; allocate(dataStruct%gru(iGRU)%hru(gru_struc(iGRU)%hruCount),stat=err); end if + class is (gru_hru_intVec); if(allocated(dataStruct%gru(iGRU)%hru))then; check=.true.; else; allocate(dataStruct%gru(iGRU)%hru(gru_struc(iGRU)%hruCount),stat=err); end if + class is (gru_hru_double); if(allocated(dataStruct%gru(iGRU)%hru))then; check=.true.; else; allocate(dataStruct%gru(iGRU)%hru(gru_struc(iGRU)%hruCount),stat=err); end if + class is (gru_hru_doubleVec); if(allocated(dataStruct%gru(iGRU)%hru))then; check=.true.; else; allocate(dataStruct%gru(iGRU)%hru(gru_struc(iGRU)%hruCount),stat=err); end if + class default ! do nothing: It is acceptable to not be any of these specified cases + end select + ! check errors + if(check) then; err=20; message=trim(message)//'HRU structure was unexpectedly allocated already'; return; end if + if(err/=0)then; err=20; message=trim(message)//'problem allocating HRU dimension'; return; end if + end do + + ! * allocate local data structures where there is a spatial dimension + gruLoop: do iGRU=1,nGRU + + ! initialize the spatial flag + spatial=.false. + + ! loop through HRUs + hruLoop: do iHRU=1,gru_struc(iGRU)%hruCount + + ! get the number of snow and soil layers + associate(& + ! nSnow => gru_struc(iGRU)%hruInfo(iHRU)%nSnow, & ! number of snow layers for each HRU + nSoil => gru_struc(iGRU)%hruInfo(iHRU)%nSoil ) ! number of soil layers for each HRU + + ! allocate space for structures WITH an HRU dimension + select type(dataStruct) + class is (gru_hru_int); call allocLocal(metaStruct,dataStruct%gru(iGRU)%hru(iHRU),nSnow,nSoil,err,cmessage); spatial=.true. + class is (gru_hru_int8); call allocLocal(metaStruct,dataStruct%gru(iGRU)%hru(iHRU),nSnow,nSoil,err,cmessage); spatial=.true. + class is (gru_hru_intVec); call allocLocal(metaStruct,dataStruct%gru(iGRU)%hru(iHRU),nSnow,nSoil,err,cmessage); spatial=.true. + class is (gru_hru_double); call allocLocal(metaStruct,dataStruct%gru(iGRU)%hru(iHRU),nSnow,nSoil,err,cmessage); spatial=.true. + class is (gru_hru_doubleVec); call allocLocal(metaStruct,dataStruct%gru(iGRU)%hru(iHRU),nSnow,nSoil,err,cmessage); spatial=.true. + class default; exit hruLoop + end select + + ! error check + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + ! end association to info in data structures + end associate + + end do hruLoop ! loop through HRUs + + ! allocate space for structures *WITHOUT* an HRU dimension + select type(dataStruct) + class is (gru_double); call allocLocal(metaStruct,dataStruct%gru(iGRU),nSnow=0,nSoil=0,err=err,message=cmessage); spatial=.true. + class is (gru_doubleVec); call allocLocal(metaStruct,dataStruct%gru(iGRU),nSnow=0,nSoil=0,err=err,message=cmessage); spatial=.true. + class default + if(.not.spatial) exit gruLoop ! no need to allocate spatial dimensions if none exist for a given variable + cycle gruLoop ! can have an HRU dimension if we get to here + end select + + ! error check + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + end do gruLoop ! loop through GRUs + + ! * allocate local data structures where there is no spatial dimension + select type(dataStruct) + class is (var_i); call allocLocal(metaStruct,dataStruct,err=err,message=cmessage) + class is (var_i8); call allocLocal(metaStruct,dataStruct,err=err,message=cmessage) + class is (var_d); call allocLocal(metaStruct,dataStruct,err=err,message=cmessage) + class is (var_ilength); call allocLocal(metaStruct,dataStruct,err=err,message=cmessage) + class is (var_dlength); call allocLocal(metaStruct,dataStruct,err=err,message=cmessage) + ! check identified the data type + class default; if(.not.spatial)then; err=20; message=trim(message)//'unable to identify derived data type'; return; end if + end select + + ! error check + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + + end subroutine allocGlobal_porgStruct + +end module allocspace_progStuct_module + \ No newline at end of file diff --git a/utils/containers/Summa-OpenWQ/Dockerfile b/utils/containers/Summa-OpenWQ/Dockerfile index e69de29bb..94d22575f 100644 --- a/utils/containers/Summa-OpenWQ/Dockerfile +++ b/utils/containers/Summa-OpenWQ/Dockerfile @@ -0,0 +1,50 @@ +FROM ubuntu:latest + +WORKDIR /code +RUN apt-get update -y && \ + apt-get upgrade -y && \ + DEBIAN_FRONTEND="noninteractive" apt-get install -y software-properties-common \ + libnetcdf-dev \ + libnetcdff-dev \ + liblapack-dev \ + libopenblas-dev \ + cmake \ + g++ \ + git \ + libssl-dev \ + make \ + gfortran \ + wget \ + python3-pip \ + valgrind \ + libboost-all-dev \ + hdf5-tools \ + gdb &&\ + apt-get autoclean + +# Install Sundials +WORKDIR /opt +RUN wget https://github.com/LLNL/sundials/archive/refs/tags/v7.1.1.tar.gz +RUN tar -xzf v7.1.1.tar.gz +WORKDIR /opt/sundials-7.1.1 +RUN mkdir build/ +WORKDIR /opt/sundials-7.1.1/build +RUN cmake ../ -DBUILD_FORTRAN_MODULE_INTERFACE=ON \ + -DCMAKE_Fortran_COMPILER=gfortran \ + -DCMAKE_INSTALL_PREFIX=/usr/local/sundials +RUN make -j 4 +RUN make install + +# Install Armadillo in conventional linux directory (/opt) +WORKDIR /opt +RUN wget http://sourceforge.net/projects/arma/files/armadillo-10.3.0.tar.xz +RUN tar -xf armadillo-10.3.0.tar.xz +WORKDIR /opt/armadillo-10.3.0 +RUN cmake . -D DETECT_HDF5=true -DCMAKE_C_FLAGS="-DH5_USE_110_API" +RUN make install +# Enable HDF5 Support +RUN sed -i '121s/^\/\/ #define ARMA_USE_HDF5/#define ARMA_USE_HDF5/' /usr/include/armadillo_bits/config.hpp + +# Set Entry Point +WORKDIR /code + diff --git a/utils/containers/Summa-OpenWQ/build_docker_container.sh b/utils/containers/Summa-OpenWQ/build_docker_container.sh new file mode 100755 index 000000000..360f55944 --- /dev/null +++ b/utils/containers/Summa-OpenWQ/build_docker_container.sh @@ -0,0 +1,3 @@ +#! /bin/bash + +docker build -t summa-openwq . \ No newline at end of file From b098ea3d88db6620f29d41a58aa810649dc1dfd3 Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Fri, 6 Sep 2024 13:02:03 -0600 Subject: [PATCH 1368/1472] Can run a synthetic test --- .gitignore | 2 ++ build/CMakeLists.txt | 6 ++++++ build/source/openwq/README.md | 0 3 files changed, 8 insertions(+) create mode 100644 build/source/openwq/README.md diff --git a/.gitignore b/.gitignore index ad29648f5..e0ede6312 100755 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,5 @@ Laugh-Tests/ # cmake_build containing cmake build objects build/cmake_build runinfo.txt +# OpenWQ source code +build/source/openwq/openwq diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 385c0988b..923ee7d76 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -70,6 +70,12 @@ endif() if (USE_OPENWQ) message("ENABLING OPENWQ") add_compile_definitions(OPENWQ_ACTIVE) + + if (USE_SUNDIALS) + set(EXEC_NAME summa_openwq_sundials.exe) + else() + set(EXEC_NAME summa_openwq.exe) + endif() endif() diff --git a/build/source/openwq/README.md b/build/source/openwq/README.md new file mode 100644 index 000000000..e69de29bb From 815290394bb70784c38738b7b354ca2608259ad9 Mon Sep 17 00:00:00 2001 From: KyleKlenk Date: Fri, 6 Sep 2024 21:58:35 +0000 Subject: [PATCH 1369/1472] Can run with sundials and non-sundials builds of summa in debug mode. Prouduction runs segfault. --- build/CMakeLists.txt | 4 ++-- build/source/openwq/CMakeLists.txt | 30 ++++++++++++++++++------------ build/source/openwq/README.md | 9 +++++++++ 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 923ee7d76..aa2040b1d 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -67,12 +67,12 @@ else() LIST(INSERT CMAKE_PREFIX_PATH 0 "${CMAKE_SOURCE_DIR}/cmake/") endif() +# OpenWQ Framework if (USE_OPENWQ) message("ENABLING OPENWQ") add_compile_definitions(OPENWQ_ACTIVE) - if (USE_SUNDIALS) - set(EXEC_NAME summa_openwq_sundials.exe) + set(EXEC_NAME summa_sundials_openwq.exe) else() set(EXEC_NAME summa_openwq.exe) endif() diff --git a/build/source/openwq/CMakeLists.txt b/build/source/openwq/CMakeLists.txt index 166a77609..bb5c76387 100644 --- a/build/source/openwq/CMakeLists.txt +++ b/build/source/openwq/CMakeLists.txt @@ -1,6 +1,11 @@ #=============================================================================== # Compilation directives for the (optional) OpenWQ framework #=============================================================================== +if(CMAKE_BUILD_TYPE MATCHES Debug) + SET(CMAKE_CXX_FLAGS "-O3 -Wall -pedantic -fPIC") +else() + SET(CMAKE_CXX_FLAGS "-O3 -Wall -pedantic ${OpenMP_CXX_FLAGS} -fPIC") +endif() # Check if the openwq directory exists relative to the current CMakeLists.txt message(STATUS "Checking for openwq directory in ${CMAKE_CURRENT_SOURCE_DIR}") @@ -34,23 +39,24 @@ set (OPENWQ_COUPLER FIND_PACKAGE(Armadillo REQUIRED) FIND_PACKAGE(HDF5 REQUIRED) +FIND_PACKAGE(OpenMP) + SET(HYDROLINK_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/OpenWQ_hydrolink.cpp ${CMAKE_CURRENT_SOURCE_DIR}/OpenWQ_interface.cpp CACHE INTERNAL "HYDROLINK_SOURCES") -SET(CMAKE_CXX_FLAGS "-O3 -Wall -pedantic -fPIC") ADD_LIBRARY(openWQ OBJECT ${OPENWQ_SOURCES} ${HYDROLINK_SOURCES}) - TARGET_INCLUDE_DIRECTORIES(openWQ PRIVATE - ${ARMADILLO_INCLUDE_DIR} - ${HDF5_INCLUDE_DIRS} - ${OPENWQ_INCLUDES}) - TARGET_LINK_LIBRARIES(openWQ PUBLIC - ${ARMADILLO_LIBRARIES} - ${HDF5_C_LIBRARY_hdf5}) - IF(OpenMP_CXX_FOUND) - MESSAGE("** OpenWQ **: OpenMP enabled!") - TARGET_LINK_LIBRARIES(openWQ PUBLIC OpenMP::OpenMP_CXX) - ENDIF() +TARGET_INCLUDE_DIRECTORIES(openWQ PRIVATE + ${ARMADILLO_INCLUDE_DIR} + ${HDF5_INCLUDE_DIRS} + ${OPENWQ_INCLUDES}) +TARGET_LINK_LIBRARIES(openWQ PUBLIC + ${ARMADILLO_LIBRARIES} + ${HDF5_C_LIBRARY_hdf5}) +IF(OpenMP_CXX_FOUND) + MESSAGE("** OpenWQ **: OpenMP enabled!") + TARGET_LINK_LIBRARIES(openWQ PUBLIC OpenMP::OpenMP_CXX) +ENDIF() diff --git a/build/source/openwq/README.md b/build/source/openwq/README.md index e69de29bb..5981471cf 100644 --- a/build/source/openwq/README.md +++ b/build/source/openwq/README.md @@ -0,0 +1,9 @@ +# OpenWQ Integration + +This directory contains the source code that enables Summa to couple to openWQ. + +To compile with openWQ support, you need to do the following steps: + - clone the openWQ repository: `git clone -b develop https://github.com/ue-hydro/openwq.git` + - The above needs to be done in build/source/openwq + - To compile Summa-OpenWQ, compile summa normally with CMake, but add the flag `-DENABLE_OPENWQ=ON` + - Compiling with openWQ support works for both Sundials and non-sundials builds of SUMMA. \ No newline at end of file From 9b422436a63ec5ad05e14147ea82fa1d06f69158 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 11 Sep 2024 13:18:46 +0900 Subject: [PATCH 1370/1472] missed a check in the Jacobian --- build/source/engine/computJacob.f90 | 4 ++-- build/source/engine/computJacobWithPrime.f90 | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index ae1736617..78598c3ea 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -453,7 +453,7 @@ subroutine computJacob(& ! (cross-derivative terms for the layer below) if(iLayer < nSnow)then - aJac(ixOffDiag(ixSnowOnlyHyd(iLayer+1),nrgState),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 + if(ixSnowOnlyHyd(iLayer+1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyHyd(iLayer+1),nrgState),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 endif ! (if there is a water state in the layer below the current layer in the given state subset) ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above @@ -804,7 +804,7 @@ subroutine computJacob(& ! (cross-derivative terms for the layer below) if(iLayer < nSnow)then - aJac(ixSnowOnlyHyd(iLayer+1),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 + if(ixSnowOnlyHyd(iLayer+1)/=integerMissing) aJac(ixSnowOnlyHyd(iLayer+1),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 endif ! (if there is a water state in the layer below the current layer in the given state subset) ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index c8d6e9937..d12e2187d 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -523,7 +523,7 @@ subroutine computJacobWithPrime(& ! (cross-derivative terms for the layer be_low) if(iLayer Date: Thu, 12 Sep 2024 11:20:05 -0600 Subject: [PATCH 1371/1472] Testing summa-openwq on older fortran compiler --- utils/containers/Summa-OpenWQ/Dockerfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/utils/containers/Summa-OpenWQ/Dockerfile b/utils/containers/Summa-OpenWQ/Dockerfile index 94d22575f..564f56d94 100644 --- a/utils/containers/Summa-OpenWQ/Dockerfile +++ b/utils/containers/Summa-OpenWQ/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:latest +FROM ubuntu:22.04 WORKDIR /code RUN apt-get update -y && \ @@ -37,9 +37,9 @@ RUN make install # Install Armadillo in conventional linux directory (/opt) WORKDIR /opt -RUN wget http://sourceforge.net/projects/arma/files/armadillo-10.3.0.tar.xz -RUN tar -xf armadillo-10.3.0.tar.xz -WORKDIR /opt/armadillo-10.3.0 +RUN wget https://sourceforge.net/projects/arma/files/armadillo-10.5.0.tar.xz +RUN tar -xf armadillo-10.5.0.tar.xz +WORKDIR /opt/armadillo-10.5.0 RUN cmake . -D DETECT_HDF5=true -DCMAKE_C_FLAGS="-DH5_USE_110_API" RUN make install # Enable HDF5 Support From 8cf1e6ffc5f4d70f3784685035f36906e3d860a4 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 18 Sep 2024 12:25:42 +0900 Subject: [PATCH 1372/1472] utils --- utils/post-processing/hist_per_GRU.py | 2 +- utils/post-processing/plot_per_GRUMult.py | 4 ++-- utils/post-processing/plot_per_GRUMultBal.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/utils/post-processing/hist_per_GRU.py b/utils/post-processing/hist_per_GRU.py index 34ac70583..0a9e4c538 100644 --- a/utils/post-processing/hist_per_GRU.py +++ b/utils/post-processing/hist_per_GRU.py @@ -90,7 +90,7 @@ def power_transform(x): leg_titl = [leg_titl[i] for i in use_vars] plot_vars2 = settings2.copy() -plt_titl2 = ['canopy air space energy balance','vegetation energy balance','snow energy balance','soil energy balance','vegetation mass balance','snow mass balance','soil mass balance','aquifer mass balance', 'wall clock time'] +plt_titl2 = ['canopy air space enthalpy balance','vegetation enthalpy balance','snow enthalpy balance','soil enthalpy balance','vegetation mass balance','snow mass balance','soil mass balance','aquifer mass balance', 'wall clock time'] leg_titl2 = ['$W~m^{-3}$'] * 4 + ['$kg~m^{-2}~s^{-1}$'] * 4 + ['$s$'] if fixed_Mass_units: leg_titl2 = ['$W~m^{-3}$'] * 4 + ['s^{-1}$'] * 3 + ['m~s^{-1}$'] + ['$s$'] plt_titl2 = [f"({chr(97+n + len(use_vars))}) {plt_titl2[i]}" for n,i in enumerate(use_vars2)] diff --git a/utils/post-processing/plot_per_GRUMult.py b/utils/post-processing/plot_per_GRUMult.py index 673946677..c92b90cc0 100644 --- a/utils/post-processing/plot_per_GRUMult.py +++ b/utils/post-processing/plot_per_GRUMult.py @@ -54,7 +54,7 @@ plt_name0=['SUMMA-BE1','SUMMA-BE16','SUMMA-BE32','SUMMA-SUNDIALS','reference solution'] plt_nameshort=plt_name0 method_name=['be1','be1cm','be1en','sundials_1en6cm','diff','ref'] -plt_name0=['BE1 common heat eq.','SUMMA-BE1 temperature heat eq.','SUMMA-BE1 mixed heat eq.','SUMMA-SUNDIALS temperature heat eq.','SUMMA-BE1 common - mixed','reference solution'] +plt_name0=['BE1 common thermo. eq..','SUMMA-BE1 temperature thermo. eq..','SUMMA-BE1 mixed thermo. eq..','SUMMA-SUNDIALS temperature thermo. eq..','SUMMA-BE1 common - mixed','reference solution'] plt_nameshort=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp','BE1 common - mixed','reference soln'] if one_plot: plt_name0 = plt_nameshort @@ -76,7 +76,7 @@ plt_titl_exVar = ['rain plus melt','top 4m soil temperature','air temperature','snow water equivalent'] #plot_vars_exVar = ['balanceCasNrg','balanceSoilNrg','balanceVegNrg','balanceSnowNrg'] viz_file_exVar = 'exVar_hrly_diff_bals_balance.nc' - plt_name0_exVar = 'SUMMA-BE1 temperature heat eq.' + plt_name0_exVar = 'SUMMA-BE1 temperature thermo. eq..' plt_nameshort_exVar = 'BE1 temp' # identify method here plt_titl_exVar = ['rain plus melt','root zone temperature','air temperature','snow water equivalent'] leg_titl_exVar = ['$mm~y^{-1}$','$K$','$K$','$kg~m^{-2}$'] diff --git a/utils/post-processing/plot_per_GRUMultBal.py b/utils/post-processing/plot_per_GRUMultBal.py index 144d67ee1..0d8648e13 100644 --- a/utils/post-processing/plot_per_GRUMultBal.py +++ b/utils/post-processing/plot_per_GRUMultBal.py @@ -46,7 +46,7 @@ method_name=['be1','be1cm','be1en','sundials_1en6cm','sundials_1en8cm'] #maybe make this an argument -plt_name0=['SUMMA-BE1 common heat eq.','SUMMA-BE1 temperature heat eq.','SUMMA-BE1 mixed heat eq.','SUMMA-SUNDIALS temperature heat eq.','reference solution'] +plt_name0=['SUMMA-BE1 common thermo. eq..','SUMMA-BE1 temperature thermo. eq..','SUMMA-BE1 mixed thermo. eq..','SUMMA-SUNDIALS temperature thermo. eq..','reference solution'] # Simulation statistics file locations settings= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime'] @@ -58,7 +58,7 @@ nbatch_hrus = 518 # number of HRUs per batch # Specify variables in files -plt_titl = ['canopy air space energy balance','vegetation energy balance','snow energy balance','soil energy balance','vegetation mass balance','snow mass balance','soil mass balance','aquifer mass balance', 'wall clock time'] +plt_titl = ['canopy air space enthalpy balance','vegetation enthalpy balance','snow enthalpy balance','soil enthalpy balance','vegetation mass balance','snow mass balance','soil mass balance','aquifer mass balance', 'wall clock time'] leg_titl = ['$W~m^{-3}$'] * 4 + ['$kg~m^{-2}~s^{-1}$'] * 4 + ['$s$'] if fixed_Mass_units: leg_titl = ['$W~m^{-3}$'] * 4 + ['s^{-1}$'] * 3 + ['m~s^{-1}$'] + ['$s$'] From a8280222028290c13dfdf6f3a614eb6c65fff457 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 18 Sep 2024 15:27:33 +0900 Subject: [PATCH 1373/1472] lower lower-bound on temp from enthalpy calculations. move around utils --- build/source/engine/enthalpyTemp.f90 | 20 ++++++++----------- utils/post-processing/readme.md | 4 ++-- .../SUMMA_merge_restarts_into_warmState.py | 0 .../convert_summa_config_v2_v3.py | 0 .../gen_coldstate.py | 0 utils/pre-processing/readme.md | 3 +++ .../subsetGRU.sh | 0 7 files changed, 13 insertions(+), 14 deletions(-) rename utils/{post-processing => pre-processing}/SUMMA_merge_restarts_into_warmState.py (100%) rename utils/{post-processing => pre-processing}/convert_summa_config_v2_v3.py (100%) rename utils/{post-processing => pre-processing}/gen_coldstate.py (100%) create mode 100644 utils/pre-processing/readme.md rename utils/{post-processing => pre-processing}/subsetGRU.sh (100%) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index e682186fc..0e8b9cdf0 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -922,7 +922,7 @@ subroutine enthalpy2T_veg(& ! and the vector of parameters, not.snow_layers vec = 0._rkind vec(1:6) = (/scalarCanopyEnthalpy, canopyDepth, specificHeatVeg, maxMassVegetation, snowfrz_scale, scalarCanopyWat/) - T = brent(diff_H_veg, T, 200._rkind, Tfreeze, vec) + T = brent(diff_H_veg, T, 0._rkind, Tfreeze, vec) ! compute Jacobian terms if(computJac)then @@ -1029,11 +1029,11 @@ subroutine enthalpy2T_snow(& if(mLayerEnthalpy>0._rkind)then T = Tfreeze+ 0.1_rkind ! need to merge layers, trigger the merge else - l_bound = diff_H_snow(200._rkind, vec) + l_bound = diff_H_snow(0._rkind, vec) if (l_bound > 0._rkind) then T = Tfreeze + 0.1_rkind ! need to merge layers, trigger the merge else - T = brent(diff_H_snow, T, 200._rkind, Tfreeze, vec) + T = brent(diff_H_snow, T, 0._rkind, Tfreeze, vec) end if endif @@ -1225,7 +1225,7 @@ subroutine enthalpy2T_soil(& ! inputs = function, lower bound, upper bound, initial point, tolerance, integer flag if want detail ! and the vector of parameters, not.snow_layer, lookup data vec(1:9) = (/mLayerEnthalpy, soil_dens_intr, vGn_alpha, vGn_n, theta_sat, theta_res, vGn_m, integral_frz_low, mLayerMatricHead/) - T = brent(diff_H_soil, T, 200._rkind, Tcrit, vec, use_lookup, lookup_data, ixControlIndex) + T = brent(diff_H_soil, T, 0._rkind, Tcrit, vec, use_lookup, lookup_data, ixControlIndex) ! compute Jacobian terms if(computJac)then @@ -1380,7 +1380,7 @@ function brent0 (fun, x1, x2, fx1, fx2, tol_x, tol_f, detail, vec, use_lookup, l if (disp == 1 ) then write(*,*) 'Brents method to find a root of f(x)' write(*,*) ' ' - write(*,*) ' i x bracketsize f(x)' + write(*,*) ' i x bracketsize f(x)' end if ! main iteration @@ -1405,7 +1405,7 @@ function brent0 (fun, x1, x2, fx1, fx2, tol_x, tol_f, detail, vec, use_lookup, l if (disp == 1) then tmp = c-b - write(*,"(' ', 1I2, 3F16.10)") i, b, abs(b-c), fb + write(*,"(' ', 1I2, 2F16.10,1F20.10)") i, b, abs(b-c), fb end if ! convergence check @@ -1593,13 +1593,9 @@ function brent (fun, x0, LowerBound, UpperBound, vec, use_lookup, lookup_data, i ! case for non convergence if (exitflag /= 1 ) then - write(*,*) ' Error (brent2) : Proper initial value for Brents method could not be found' - write(*,*) ' Change initial guess and try again. ' - write(*,*) ' You might want to try disp = 1 option too' + write(*,*) ' Error (temperature from enthalpy computation) : Proper initial value for Brents method could not be found, decrease lower temperature bound' write(*,*) ' i x1 x2 f(x1) f(x2)' write(*,"(1I4,4F17.6)") iter, a, b, fa, fb - write(*,*) ' press any key to abort the program' - read(*,*) stop else if (disp == 1) then write(*,*) ' Initial guess was found.' @@ -1692,7 +1688,7 @@ function diff_H_soil ( mLayerTemp, vec, use_lookup, lookup_data, ixControlIndex) xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution fLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) ! here fLiq is the total liquid fraction, not fraction of water fraction that is liquid - diff_H_soil = mLayerEnthTemp - iden_water * LH_fus * (volFracWat - fLiq) - mLayerEnthalpy + diff_H_soil = mLayerEnthTemp - iden_water * LH_fus * (volFracWat - fLiq) - mLayerEnthalpy end function diff_H_soil diff --git a/utils/post-processing/readme.md b/utils/post-processing/readme.md index afe8e7b69..aab2815e1 100644 --- a/utils/post-processing/readme.md +++ b/utils/post-processing/readme.md @@ -1,3 +1,3 @@ -# utils folder +# post-processing folder Helpful scripts for a variety of post-processing purposes: -- `check_bit_4_bit_withTol.py`: checks for differences in output files to a tolerance - `concat_groups_split_summa.py`: concatenate the outputs of a split domain summa run into fewer groups - `convert_summa_config_v2_v3.py`: convert SUMMA v2.x configuration to SUMMA v3.0.0 - `gen_coldstate.py`: create a vector cold state file for SUMMA from constant values - `hist_per_GRU.py`: visualize statistics per GRU as a CDF or histogram - `largest_error_attrib.py`: find GRUs with largest errors - `plot_per_GRUMult.py`: visualize statistics per GRU as a geographical map - `plot_per_GRUMultBal.py`: visualize conservation balances per GRU as a geographical map - `scat_per_GRU.py`: visualize statistics or balances per GRU as a scatter plot or heat plot - `subsetGRU.sh`: subset out a NA HRU forcing, parameter, and attribute files where GRU matches HRU - `SUMMA_merge_restarts_into_warmState.py`: combine split domain state files (with 2 dimensions, hru and gru) - `summarize_logs.py`: summarize all SUMMA logs in a folder to see if batch runs finished - `timeseries_to_statistics.py`: loads timeseries of simulated variables and computes a variety of statistics \ No newline at end of file +- `check_bit_4_bit_withTol.py`: checks for differences in output files to a tolerance - `concat_groups_split_summa.py`: concatenate the outputs of a split domain summa run into fewer groups - `hist_per_GRU.py`: visualize statistics per GRU as a CDF or histogram - `largest_error_attrib.py`: find GRUs with largest errors - `plot_per_GRUMult.py`: visualize statistics per GRU as a geographical map - `plot_per_GRUMultBal.py`: visualize conservation balances per GRU as a geographical map - `scat_per_GRU.py`: visualize statistics or balances per GRU as a scatter plot or heat plot - `summarize_logs.py`: summarize all SUMMA logs in a folder to see if batch runs finished - `timeseries_to_statistics.py`: loads timeseries of simulated variables and computes a variety of statistics \ No newline at end of file diff --git a/utils/post-processing/SUMMA_merge_restarts_into_warmState.py b/utils/pre-processing/SUMMA_merge_restarts_into_warmState.py similarity index 100% rename from utils/post-processing/SUMMA_merge_restarts_into_warmState.py rename to utils/pre-processing/SUMMA_merge_restarts_into_warmState.py diff --git a/utils/post-processing/convert_summa_config_v2_v3.py b/utils/pre-processing/convert_summa_config_v2_v3.py similarity index 100% rename from utils/post-processing/convert_summa_config_v2_v3.py rename to utils/pre-processing/convert_summa_config_v2_v3.py diff --git a/utils/post-processing/gen_coldstate.py b/utils/pre-processing/gen_coldstate.py similarity index 100% rename from utils/post-processing/gen_coldstate.py rename to utils/pre-processing/gen_coldstate.py diff --git a/utils/pre-processing/readme.md b/utils/pre-processing/readme.md new file mode 100644 index 000000000..c8937a99f --- /dev/null +++ b/utils/pre-processing/readme.md @@ -0,0 +1,3 @@ +# pre-processing folder +Helpful scripts for a variety of pre-processing purposes: +- `convert_summa_config_v2_v3.py`: convert SUMMA v2.x configuration to SUMMA v3.0.0 - `gen_coldstate.py`: create a vector cold state file for SUMMA from constant values - `subsetGRU.sh`: subset out a NA HRU forcing, parameter, and attribute files where GRU matches HRU - `SUMMA_merge_restarts_into_warmState.py`: combine split domain state files (with 2 dimensions, hru and gru) \ No newline at end of file diff --git a/utils/post-processing/subsetGRU.sh b/utils/pre-processing/subsetGRU.sh similarity index 100% rename from utils/post-processing/subsetGRU.sh rename to utils/pre-processing/subsetGRU.sh From 74aebf4818485ddbd2c734241b697ae2e7cf65e6 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 19 Sep 2024 11:48:10 +0900 Subject: [PATCH 1374/1472] fixing enthalpy --- build/source/engine/check_icond.f90 | 30 ++------- build/source/engine/coupled_em.f90 | 36 ++--------- build/source/engine/enthalpyTemp.f90 | 96 ++++++++++++---------------- build/source/engine/updateVars.f90 | 24 ++----- 4 files changed, 58 insertions(+), 128 deletions(-) diff --git a/build/source/engine/check_icond.f90 b/build/source/engine/check_icond.f90 index 71bce8ca4..4833fb342 100644 --- a/build/source/engine/check_icond.f90 +++ b/build/source/engine/check_icond.f90 @@ -183,25 +183,17 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU if(checkEnthalpy)then ! enthalpy as state variable (cold start often only has temperature) call T2enthTemp_cas(& - ! input scalarCanairTemp, & ! intent(in): canopy air temperature (K) - ! output - scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - + scalarCanairEnthalpy) ! intent(out): enthalpy of the canopy air space (J m-3) + call T2enthTemp_veg(& - ! input (heightCanopyTop-heightCanopyBottom), & ! intent(in): canopy depth (m) specificHeatVeg, & ! intent(in): specific heat of vegetation (J kg-1 K-1) maxMassVegetation, & ! intent(in): maximum mass of vegetation (kg m-2) snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) scalarCanopyTemp, & ! intent(in): canopy temperature (K) scalarTheta, & ! intent(in): canopy water content (kg m-2) - ! output - scalarCanopyEnthTemp, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + scalarCanopyEnthTemp) ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) scalarCanopyEnthalpy = scalarCanopyEnthTemp - LH_fus * scalarCanopyIce/ (heightCanopyTop-heightCanopyBottom) end if @@ -275,11 +267,9 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU ! ensure consistency among state variables call updateSnow(& - ! input mLayerTemp(iLayer), & ! intent(in): temperature (K) scalarTheta, & ! intent(in): volumetric fraction of total water (-) snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) - ! output mLayerVolFracLiq(iLayer), & ! intent(out): volumetric fraction of liquid water (-) mLayerVolFracIce(iLayer), & ! intent(out): volumetric fraction of ice (-) fLiq, & ! intent(out): fraction of liquid water (-) @@ -288,14 +278,10 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU if(checkEnthalpy)then ! enthalpy as state variable (cold start often only has temperature) call T2enthTemp_snow(& - ! input snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) mLayerTemp(iLayer), & ! intent(in): layer temperature (K) scalarTheta, & ! intent(in): volumetric total water content (-) - ! output - mLayerEnthTemp(iLayer), & ! intent(out): temperature component of enthalpy of each snow layer (J m-3) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + mLayerEnthTemp(iLayer)) ! intent(out): temperature component of enthalpy of each snow layer (J m-3) mLayerEnthalpy(iLayer) = mLayerEnthTemp(iLayer) - iden_ice * LH_fus * mLayerVolFracIce(iLayer) endif @@ -304,11 +290,9 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU ! ensure consistency among state variables call updateSoil(& - ! input mLayerTemp(iLayer), & ! intent(in): layer temperature (K) mLayerMatricHead(iLayer-nSnow), & ! intent(in): matric head (m) vGn_alpha(iSoil),vGn_n(iSoil),theta_sat(iSoil),theta_res(iSoil),vGn_m, & ! intent(in): van Genutchen soil parameters - ! output scalarTheta, & ! intent(out): volumetric fraction of total water (-) mLayerVolFracLiq(iLayer), & ! intent(out): volumetric fraction of liquid water (-) mLayerVolFracIce(iLayer), & ! intent(out): volumetric fraction of ice (-) @@ -317,7 +301,6 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU if(checkEnthalpy)then ! enthalpy as state variable (cold start often only has temperature) call T2enthTemp_soil(& - ! input use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy soil_dens_intr(iSoil), & ! intent(in): intrinsic soil density (kg m-3) vGn_alpha(iSoil),vGn_n(iSoil),theta_sat(iSoil),theta_res(iSoil),vGn_m, & ! intent(in): van Genutchen soil parameters @@ -326,10 +309,7 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU realMissing, & ! intent(in): lower value of integral (not computed) mLayerTemp(iLayer), & ! intent(in): layer temperature (K) mLayerMatricHead(iLayer-nSnow), & ! intent(in): matric head (m) - ! output - mLayerEnthTemp(iLayer), & ! intent(out): temperature component of enthalpy soil layer (J m-3) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(cmessage)//trim(cmessage); return; end if ! (check for errors) + mLayerEnthTemp(iLayer)) ! intent(out): temperature component of enthalpy soil layer (J m-3) mLayerEnthalpy(iLayer) = mLayerEnthTemp(iLayer) - iden_water * LH_fus * mLayerVolFracIce(iLayer) endif diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 626e21f5f..4557756c9 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -644,17 +644,13 @@ subroutine coupled_em(& scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) & ! mass of ice on the vegetation canopy (kg m-2) ) ! (associate local variables with model parameters) call T2enthTemp_veg(& - ! input canopyDepth, & ! intent(in): canopy depth (m) specificHeatVeg, & ! intent(in): specific heat of vegetation (J kg-1 K-1) maxMassVegetation, & ! intent(in): maximum mass of vegetation (kg m-2) snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) scalarCanopyTemp, & ! intent(in): canopy temperature (K) (scalarCanopyLiq+scalarCanopyIce), & ! intent(in): canopy water content (kg m-2) - ! output - scalarCanopyEnthTemp, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) - err,cmessage) ! intent(out): error control - if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if + scalarCanopyEnthTemp) ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) scalarCanopyEnthalpy = scalarCanopyEnthTemp - LH_fus * scalarCanopyIce/ canopyDepth ! new ice and/or temperature end associate enthalpyVeg end if ! (need to recalculate enthalpy state variable) @@ -820,9 +816,7 @@ subroutine coupled_em(& snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) mLayerTemp(iLayer), & ! intent(in): layer temperature (K) mLayerVolFracWat(iLayer), & ! intent(in): volumetric total water content (-) - mLayerEnthTemp(iLayer), & ! intent(out): temperature component of enthalpy of each snow layer (J m-3) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + mLayerEnthTemp(iLayer)) ! intent(out): temperature component of enthalpy of each snow layer (J m-3) mLayerEnthalpy(iLayer) = mLayerEnthTemp(iLayer) - iden_ice * LH_fus * mLayerVolFracIce(iLayer) end do ! looping through snow layers endif @@ -831,7 +825,6 @@ subroutine coupled_em(& ! compute enthalpy for soil layers iSoil = iLayer - nSnow call T2enthTemp_soil(& - ! input use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy soil_dens_intr(iSoil), & ! intent(in): intrinsic soil density (kg m-3) vGn_alpha(iSoil),vGn_n(iSoil),theta_sat(iSoil),theta_res(iSoil),vGn_m(iSoil), & ! intent(in): soil parameters @@ -840,10 +833,7 @@ subroutine coupled_em(& realMissing, & ! intent(in): lower value of integral (not computed) mLayerTemp(iLayer), & ! intent(in): layer temperature (K) mLayerMatricHead(iSoil), & ! intent(in): matric head (m) - ! output - mLayerEnthTemp(iLayer), & ! intent(out): temperature component of enthalpy soil layer (J m-3) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + mLayerEnthTemp(iLayer)) ! intent(out): temperature component of enthalpy soil layer (J m-3) mLayerEnthalpy(iLayer) = mLayerEnthTemp(iLayer) - iden_water * LH_fus * mLayerVolFracIce(iLayer) end do ! looping through soil layers end associate enthalpySnow @@ -951,7 +941,6 @@ subroutine coupled_em(& ! compute enthalpy of the top soil layer if changed with surface melt pond if( (enthalpyStateVec .or. computeEnthalpy) .and. nSnow==0 .and. prog_data%var(iLookPROG%scalarSWE)%dat(1)>0._rkind )then call T2enthTemp_soil(& - ! input use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy soil_dens_intr, & ! intent(in): intrinsic soil density (kg m-3) vGn_alpha(1),vGn_n(1),theta_sat(1),theta_res(1),vGn_m(1), & ! intent(in): van Genutchen soil parameters @@ -960,10 +949,7 @@ subroutine coupled_em(& realMissing, & ! intent(in): lower value of integral (not computed) prog_data%var(iLookPROG%mLayerTemp)%dat(nSnow+1), & ! intent(in): surface layer temperature (K) mLayerMatricHead(1), & ! intent(in): surface layer matric head (m) - ! output - diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(nSnow+1), & ! intent(out): temperature component of enthalpy soil layer (J m-3) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(nSnow+1)) ! intent(out): temperature component of enthalpy soil layer (J m-3) diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(nSnow+1) = diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(nSnow+1) - iden_water * LH_fus * mLayerVolFracIce(nSnow+1) end if @@ -1140,17 +1126,13 @@ subroutine coupled_em(& scalarCanopyWat = scalarCanopyLiq + scalarCanopyIce if(enthalpyStateVec .or. computeEnthalpy)then ! recompute enthalpy of the canopy if changed water and ice content call T2enthTemp_veg(& - ! input diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): canopy depth (m), send in specific value since diag_data may have changed specificHeatVeg, & ! intent(in): specific heat of vegetation (J kg-1 K-1) maxMassVegetation, & ! intent(in): maximum mass of vegetation (kg m-2) snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1), & ! intent(in): canopy temperature (K) scalarCanopyWat, & ! intent(in): canopy water content (kg m-2) - ! output - diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1), & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1)) ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) = diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) - LH_fus * scalarCanopyIce/ diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) endif end if ! (if computing the vegetation flux) @@ -1221,9 +1203,7 @@ subroutine coupled_em(& snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) prog_data%var(iLookPROG%mLayerTemp)%dat(iLayer), & ! intent(in): layer temperature (K) mLayerVolFracWat(iLayer), & ! intent(in): volumetric total water content (-) - diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(iLayer), & ! intent(out): temperature component of enthalpy of each snow layer (J m-3) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(iLayer)) ! intent(out): temperature component of enthalpy of each snow layer (J m-3) diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(iLayer) = diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(iLayer) - iden_ice * LH_fus * mLayerVolFracIce(iLayer) end do ! looping through snow layers endif @@ -1377,9 +1357,7 @@ subroutine coupled_em(& snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) prog_data%var(iLookPROG%mLayerTemp)%dat(1), & ! temperature of the top layer (K) prog_data%var(iLookPROG%mLayerVolFracWat)%dat(1), & ! intent(in): volumetric total water content (-) - diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(1), & ! intent(out): temperature component of enthalpy of each snow layer (J m-3) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(1)) ! intent(out): temperature component of enthalpy of each snow layer (J m-3) diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(1) = diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(1) - iden_ice * LH_fus * prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1) end if end if diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 0e8b9cdf0..1e77b7fdf 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -427,8 +427,7 @@ end function T2enthalpy_snwWat ! ************************************************************************************************************************ subroutine T2enthTemp_cas(& scalarCanairTemp, & ! intent(in): canopy air temperature (K) - scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - err,message) ! intent(out): error control + scalarCanairEnthalpy) ! intent(out): enthalpy of the canopy air space (J m-3) ! ------------------------------------------------------------------------------------------------------------------------- implicit none ! delare dummy variables @@ -437,13 +436,7 @@ subroutine T2enthTemp_cas(& real(rkind),intent(in) :: scalarCanairTemp ! canopy air temperature (K) ! output: enthalpy real(rkind),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="T2enthTemp_cas/" - scalarCanairEnthalpy = Cp_air * iden_air * (scalarCanairTemp - Tfreeze) end subroutine T2enthTemp_cas @@ -458,8 +451,7 @@ subroutine T2enthTemp_veg(& snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) scalarCanopyTemp, & ! intent(in): canopy temperature (K) scalarCanopyWat, & ! intent(in): canopy total water (kg m-2) - scalarCanopyEnthTemp, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) - err,message) ! intent(out): error control + scalarCanopyEnthTemp) ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) ! ------------------------------------------------------------------------------------------------------------------------- implicit none ! delare dummy variables @@ -473,9 +465,6 @@ subroutine T2enthTemp_veg(& real(rkind),intent(in) :: scalarCanopyWat ! canopy total water (kg m-2) ! output: enthalpy real(rkind),intent(out) :: scalarCanopyEnthTemp ! temperature component of enthalpy of the vegetation canopy (J m-3) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------------------------------- ! declare local variables real(rkind) :: diffT ! temperature difference of temp from Tfreeze @@ -485,9 +474,6 @@ subroutine T2enthTemp_veg(& real(rkind) :: enthLiq ! enthalpy of the liquid region (J m-3) real(rkind) :: enthIce ! enthalpy of the ice region (J m-3) ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="T2enthTemp_veg/" - diffT = scalarCanopyTemp - Tfreeze enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth @@ -511,8 +497,7 @@ subroutine T2enthTemp_snow(& snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) mLayerTemp, & ! intent(in): layer temperature (K) mLayerVolFracWat, & ! intent(in): volumetric total water content (-) - mLayerEnthTemp, & ! intent(out): temperature component of enthalpy of each snow layer (J m-3) - err,message) ! intent(out): error control + mLayerEnthTemp) ! intent(out): temperature component of enthalpy of each snow layer (J m-3) ! ------------------------------------------------------------------------------------------------------------------------- implicit none ! delare dummy variables @@ -523,9 +508,6 @@ subroutine T2enthTemp_snow(& real(rkind),intent(in) :: mLayerVolFracWat ! volumetric total water content (-) ! output: enthalpy real(rkind),intent(out) :: mLayerEnthTemp ! temperature component of enthalpy of each snow layer (J m-3) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------------------------------- ! declare local variables real(rkind) :: diffT ! temperature difference of temp from Tfreeze @@ -535,9 +517,6 @@ subroutine T2enthTemp_snow(& real(rkind) :: enthIce ! enthalpy of the ice region (J m-3) real(rkind) :: enthAir ! enthalpy of air (J m-3) ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="T2enthTemp_snow/" - diffT = mLayerTemp - Tfreeze ! diffT<0._rkind because snow is frozen if(diffT==0._rkind)then ! only need for upper bound @@ -572,8 +551,7 @@ subroutine T2enthTemp_soil(& integral_frz_low0, & ! intent(in): integral_frz_low if computed outside, else realMissing mLayerTemp, & ! intent(in): layer temperature (K) mLayerMatricHead, & ! intent(in): total water matric potential (m) - mLayerEnthTemp, & ! intent(out): temperature component of enthalpy soil layer (J m-3) - err,message) ! intent(out): error control + mLayerEnthTemp) ! intent(out): temperature component of enthalpy soil layer (J m-3) ! ------------------------------------------------------------------------------------------------------------------------- ! downwind routines USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists @@ -598,11 +576,9 @@ subroutine T2enthTemp_soil(& real(rkind),intent(in) :: mLayerMatricHead ! total water matric potential (m) ! output: enthalpy real(rkind),intent(out) :: mLayerEnthTemp ! temperature component of enthalpy of soil layer (J m-3) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------------------------------- ! declare local variables + integer(i4b) :: err ! error code character(len=128) :: cmessage ! error message in downwind routine real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) real(rkind) :: volFracWat ! volumetric fraction of total water, liquid+ice (-) @@ -623,7 +599,7 @@ subroutine T2enthTemp_soil(& real(rkind) :: enthAir ! enthalpy of air (J m-3) ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control - err=0; message="T2enthTemp_soil/" + err=0; cmessage="T2enthTemp_soil/" Tcrit = crit_soilT( mLayerMatricHead ) volFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) @@ -656,14 +632,14 @@ subroutine T2enthTemp_soil(& integral_frz_low = integral_frz_low0 else call splint(Tk,Ly,L2,Tcrit,integral_frz_low,dL,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + if(err/=0) then; cmessage="T2enthTemp_soil/"//trim(cmessage); print*, cmessage; return; end if ! should does not fail, print message to be safe endif else ! Tcrit=Tfreeze, i.e. mLayerMatricHeadTrial(ixControlIndex)>0 integral_frz_low = 0._rkind end if ! get the upper limit of the integral call splint(Tk,Ly,L2,mlayerTemp,integral_frz_upp,dL,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + if(err/=0) then; cmessage="T2enthTemp_soil/"//trim(cmessage); print*, cmessage; return; end if ! should not fail, print message to be safe end associate lookVars @@ -884,6 +860,7 @@ subroutine enthalpy2T_veg(& character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------------------------------- ! declare local variables + character(len=256) :: cmessage ! error message of downwind routine real(rkind) :: T ! temperature (K) real(rkind) :: H ! enthalpy (J m-3) real(rkind) :: diffT ! temperature difference of temp from Tfreeze @@ -922,7 +899,8 @@ subroutine enthalpy2T_veg(& ! and the vector of parameters, not.snow_layers vec = 0._rkind vec(1:6) = (/scalarCanopyEnthalpy, canopyDepth, specificHeatVeg, maxMassVegetation, snowfrz_scale, scalarCanopyWat/) - T = brent(diff_H_veg, T, 0._rkind, Tfreeze, vec) + T = brent(diff_H_veg, T, 0._rkind, Tfreeze, vec, err, cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! compute Jacobian terms if(computJac)then @@ -995,6 +973,7 @@ subroutine enthalpy2T_snow(& character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------------------------------- ! declare local variables + character(len=256) :: cmessage ! error message of downwind routine real(rkind) :: T ! temperature (K) real(rkind) :: H ! enthalpy (J m-3) real(rkind) :: diffT ! temperature difference of temp from Tfreeze @@ -1033,7 +1012,8 @@ subroutine enthalpy2T_snow(& if (l_bound > 0._rkind) then T = Tfreeze + 0.1_rkind ! need to merge layers, trigger the merge else - T = brent(diff_H_snow, T, 0._rkind, Tfreeze, vec) + T = brent(diff_H_snow, T, 0._rkind, Tfreeze, vec, err, cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif end if endif @@ -1128,7 +1108,7 @@ subroutine enthalpy2T_soil(& character(*),intent(out) :: message ! error message ! ------------------------------------------------------------------------------------------------------------------------- ! declare local variables - character(len=128) :: cmessage ! error message in downwind routine + character(len=256) :: cmessage ! error message in downwind routine real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) real(rkind) :: volFracWat ! volumetric fraction of total water, liquid+ice (-) real(rkind) :: diff0 ! temperature difference of Tcrit from Tfreeze @@ -1225,7 +1205,8 @@ subroutine enthalpy2T_soil(& ! inputs = function, lower bound, upper bound, initial point, tolerance, integer flag if want detail ! and the vector of parameters, not.snow_layer, lookup data vec(1:9) = (/mLayerEnthalpy, soil_dens_intr, vGn_alpha, vGn_n, theta_sat, theta_res, vGn_m, integral_frz_low, mLayerMatricHead/) - T = brent(diff_H_soil, T, 0._rkind, Tcrit, vec, use_lookup, lookup_data, ixControlIndex) + T = brent(diff_H_soil, T, 0._rkind, Tcrit, vec, err, cmessage, use_lookup, lookup_data, ixControlIndex) + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! compute Jacobian terms if(computJac)then @@ -1311,7 +1292,7 @@ end function hyp_2F1_real !---------------------------------------------------------------------- ! private function: Brent's method to find a root of a function !---------------------------------------------------------------------- -function brent0 (fun, x1, x2, fx1, fx2, tol_x, tol_f, detail, vec, use_lookup, lookup_data, ixControlIndex) +function brent0 (fun, x1, x2, fx1, fx2, tol_x, tol_f, detail, vec, err, message, use_lookup, lookup_data, ixControlIndex) ! ! Description of algorithm: ! Find a root of function f(x) given intial bracketing interval [a,b] @@ -1347,12 +1328,17 @@ function brent0 (fun, x1, x2, fx1, fx2, tol_x, tol_f, detail, vec, use_lookup, l logical(lgt), intent(in), optional :: use_lookup type(zLookup),intent(in), optional :: lookup_data integer(i4b), intent(in), optional :: ixControlIndex + integer(i4b),intent(out) :: err + character(*),intent(out) :: message integer :: i, exitflag, disp real(rkind) :: a, b, c, diff,e, fa, fb, fc, p, q, r, s, tol1, xm, tmp real(rkind), parameter :: EPS = epsilon(a) integer, parameter :: imax = 100 ! maximum number of iteration + ! initialize error control + err=0; message='' + exitflag = 0 if (detail /= 0) then disp = 1 @@ -1368,12 +1354,10 @@ function brent0 (fun, x1, x2, fx1, fx2, tol_x, tol_f, detail, vec, use_lookup, l fb = fx2 fc = fx2 - ! check sign + ! check sign, should be fine since checked in main function brent if ( (fa>0. .and. fb>0. ) .or. (fa>0. .and. fb>0. )) then write(*,*) 'Error (brent0.f90): Root must be bracketed by two inputs' write(*, "(' x1 = ', 1F8.4, ' x2 = ', 1F8.4, ' f(x1) = ', 1F15.4, ' f(x2) = ', 1F15.4)") a,b,fa,fb - write(*,*) 'press any key to halt the program' - read(*,*) stop end if @@ -1472,6 +1456,7 @@ function brent0 (fun, x1, x2, fx1, fx2, tol_x, tol_f, detail, vec, use_lookup, l write(*,*) ' ' write(*,*) 'final value:' write(*,"('x = ' ,1F6.4, ': f(x1) = ' , 1F6.4 )" ) b, fb + err = 20;message = trim(message)//'convergence was not attained';return else if( disp == 1) then write(*,*) 'Brents method was converged.' write(*,*) '' @@ -1484,7 +1469,7 @@ end function brent0 !---------------------------------------------------------------------- ! private function: Find an initial guess of bracket and call brent0 !---------------------------------------------------------------------- - function brent (fun, x0, LowerBound, UpperBound, vec, use_lookup, lookup_data, ixControlIndex) + function brent (fun, x0, LowerBound, UpperBound, vec, err, message, use_lookup, lookup_data, ixControlIndex) ! ! Inputs ! fun: function to evaluate @@ -1500,6 +1485,8 @@ function brent (fun, x0, LowerBound, UpperBound, vec, use_lookup, lookup_data, i logical(lgt), intent(in), optional :: use_lookup type(zLookup),intent(in), optional :: lookup_data integer(i4b), intent(in), optional :: ixControlIndex + integer(i4b),intent(out) :: err + character(*),intent(out) :: message real(rkind) :: a , b , olda, oldb, fa, fb, folda, foldb real(rkind), parameter :: sqrt2 = sqrt(2.0_d)! change in dx @@ -1508,6 +1495,11 @@ function brent (fun, x0, LowerBound, UpperBound, vec, use_lookup, lookup_data, i integer :: iter, exitflag, disp real(rkind) :: sgn real(rkind), parameter :: tol_x = 1.e-5_rkind, tol_f = 1.e0_rkind + character(LEN=256):: cmessage ! error message of downwind routine + + ! initialize error control + err=0; message='brent/' + a = x0 ! lower bracket b = x0 ! upper bracket @@ -1593,10 +1585,11 @@ function brent (fun, x0, LowerBound, UpperBound, vec, use_lookup, lookup_data, i ! case for non convergence if (exitflag /= 1 ) then - write(*,*) ' Error (temperature from enthalpy computation) : Proper initial value for Brents method could not be found, decrease lower temperature bound' + write(*,*) ' Error (temperature from enthalpy computation): Proper initial value for Brents method could not be found in between bounds' write(*,*) ' i x1 x2 f(x1) f(x2)' write(*,"(1I4,4F17.6)") iter, a, b, fa, fb - stop + write(*,*) 'vec=',vec + err = 20;message = trim(message)//'proper initial value could not be found'; return else if (disp == 1) then write(*,*) ' Initial guess was found.' write(*,*) '' @@ -1604,10 +1597,11 @@ function brent (fun, x0, LowerBound, UpperBound, vec, use_lookup, lookup_data, i ! call brent0 if(present(use_lookup))then - brent = brent0(fun, a, b, fa, fb, tol_x, tol_f, detail, vec, use_lookup, lookup_data, ixControlIndex) + brent = brent0(fun, a, b, fa, fb, tol_x, tol_f, detail, vec, err, cmessage, use_lookup, lookup_data, ixControlIndex) else - brent = brent0(fun, a, b, fa, fb, tol_x, tol_f, detail, vec) + brent = brent0(fun, a, b, fa, fb, tol_x, tol_f, detail, vec, err, cmessage) end if + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif end function brent @@ -1621,8 +1615,6 @@ function diff_H_veg ( scalarCanopyTemp, vec) real(rkind) , intent(IN) :: scalarCanopyTemp, vec(8) real(rkind) :: scalarCanopyEnthalpy, scalarCanopyEnthTemp, scalarCanopyWat, scalarCanopyIce real(rkind) :: canopyDepth, specificHeatVeg, maxMassVegetation, snowfrz_scale, fLiq - integer(i4b) :: err - character(256) :: cmessage scalarCanopyEnthalpy = vec(1) canopyDepth = vec(2) @@ -1632,7 +1624,7 @@ function diff_H_veg ( scalarCanopyTemp, vec) scalarCanopyWat = vec(6) call T2enthTemp_veg(canopyDepth, specificHeatVeg, maxMassVegetation, snowfrz_scale, scalarCanopyTemp, & - scalarCanopyWat, scalarCanopyEnthTemp, err, cmessage) + scalarCanopyWat, scalarCanopyEnthTemp) fLiq = fracliquid(scalarCanopyTemp, snowfrz_scale) diff_H_veg = scalarCanopyEnthTemp - LH_fus * scalarCanopyWat* (1._rkind - fLiq)/ canopyDepth - scalarCanopyEnthalpy @@ -1644,14 +1636,12 @@ function diff_H_snow ( mLayerTemp, vec) real(rkind) :: diff_H_snow real(rkind) , intent(IN) :: mLayerTemp, vec(9) real(rkind) :: mLayerEnthalpy, mLayerEnthTemp, mLayerVolFracWat, mLayerVolFracIce, snowfrz_scale, fLiq - integer(i4b) :: err - character(256) :: cmessage mLayerEnthalpy = vec(1) snowfrz_scale = vec(2) mLayerVolFracWat = vec(3) - call T2enthTemp_snow(snowfrz_scale, mLayerTemp, mLayerVolFracWat, mLayerEnthTemp, err, cmessage) + call T2enthTemp_snow(snowfrz_scale, mLayerTemp, mLayerVolFracWat, mLayerEnthTemp) fLiq = fracliquid(mLayerTemp, snowfrz_scale) diff_H_snow = mLayerEnthTemp - iden_water * LH_fus * mLayerVolFracWat * (1._rkind - fLiq) - mLayerEnthalpy @@ -1667,8 +1657,6 @@ function diff_H_soil ( mLayerTemp, vec, use_lookup, lookup_data, ixControlIndex) integer(i4b), intent(in) :: ixControlIndex real(rkind) :: mLayerEnthalpy, mLayerEnthTemp, mLayerMatricHead, volFracWat, xConst, mLayerPsiLiq, fLiq real(rkind) :: soil_dens_intr, vGn_alpha, vGn_n, theta_sat, theta_res, vGn_m, integral_frz_low - integer(i4b) :: err - character(256) :: cmessage mLayerEnthalpy = vec(1) soil_dens_intr = vec(2) @@ -1682,7 +1670,7 @@ function diff_H_soil ( mLayerTemp, vec, use_lookup, lookup_data, ixControlIndex) call T2enthTemp_soil(use_lookup, soil_dens_intr, vGn_alpha, vGn_n, theta_sat, theta_res, vGn_m, & ixControlIndex, lookup_data, integral_frz_low, mLayerTemp, mLayerMatricHead, & - mLayerEnthTemp, err, cmessage) + mLayerEnthTemp) volFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 index d9d0e6518..ed9ea7dc7 100644 --- a/build/source/engine/updateVars.f90 +++ b/build/source/engine/updateVars.f90 @@ -338,12 +338,8 @@ subroutine updateVars(& if(ixDomainType==iname_cas)then if(computeEnthTemp)then call T2enthTemp_cas(& - ! input scalarCanairTempTrial, & ! intent(in): canopy air temperature (K) - ! output - scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + scalarCanairEnthalpyTrial) ! intent(out): enthalpy of the canopy air space (J m-3) else scalarCanairEnthalpyTrial = realMissing endif @@ -668,38 +664,29 @@ subroutine updateVars(& if(ixDomainType==iname_veg)then if(computeEnthTemp)then call T2enthTemp_veg(& - ! input canopyDepth, & ! intent(in): canopy depth (m) specificHeatVeg, & ! intent(in): specific heat of vegetation (J kg-1 K-1) maxMassVegetation, & ! intent(in): maximum mass of vegetation (kg m-2) snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) scalarCanopyTempTrial, & ! intent(in): canopy temperature (K) scalarCanopyWatTrial, & ! intent(in): canopy water content (kg m-2) - ! output - scalarCanopyEnthTempTrial, & ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + scalarCanopyEnthTempTrial) ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) else scalarCanopyEnthTempTrial = realMissing endif elseif(ixDomainType==iname_snow)then if(computeEnthTemp)then call T2enthTemp_snow(& - ! input snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) mLayerTempTrial(iLayer), & ! intent(in): layer temperature (K) mLayerVolFracWatTrial(iLayer), & ! intent(in): volumetric total water content (-) - ! output - mLayerEnthTempTrial(iLayer), & ! intent(out): temperature component of enthalpy of each snow layer (J m-3) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + mLayerEnthTempTrial(iLayer)) ! intent(out): temperature component of enthalpy of each snow layer (J m-3) else mLayerEnthTempTrial(iLayer) = realMissing endif elseif(ixDomainType==iname_soil)then if(computeEnthTemp)then call T2enthTemp_soil(& - ! input use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy soil_dens_intr(ixControlIndex), & ! intent(in): intrinsic soil density (kg m-3) vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in): soil parameters @@ -708,10 +695,7 @@ subroutine updateVars(& realMissing, & ! intent(in): lower value of integral (not computed) mLayerTempTrial(iLayer), & ! intent(in): layer temperature (K) mLayerMatricHeadTrial(ixControlIndex), & ! intent(in): matric head (m) - ! output - mLayerEnthTempTrial(iLayer), & ! intent(out): temperature component of enthalpy soil layer (J m-3) - err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + mLayerEnthTempTrial(iLayer)) ! intent(out): temperature component of enthalpy soil layer (J m-3) else mLayerEnthTempTrial(iLayer) = realMissing endif From e304a456c87ce988344c33b7c61582f13e191bc9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 19 Sep 2024 22:15:02 +0900 Subject: [PATCH 1375/1472] enthalpy as state variable fixes --- build/source/engine/enthalpyTemp.f90 | 47 +++++++++++++++------------ utils/post-processing/hist_per_GRU.py | 15 +++------ 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 1e77b7fdf..09e2abf1e 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -884,14 +884,15 @@ subroutine enthalpy2T_veg(& err=0; message="enthalpy2T_veg/" ! ***** get temperature if unfrozen vegetation - T = scalarCanopyEnthalpy * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWat ) + Tfreeze - if(computJac)then - dT_dEnthalpy = canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWat ) - dT_dWat = -Cp_water * scalarCanopyEnthalpy * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWat )**2_i4b - endif - + if (scalarCanopyEnthalpy>=0)then + T = scalarCanopyEnthalpy * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWat ) + Tfreeze + if(computJac)then + dT_dEnthalpy = canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWat ) + dT_dWat = -Cp_water * scalarCanopyEnthalpy * canopyDepth / ( specificHeatVeg * maxMassVegetation + Cp_water * scalarCanopyWat )**2_i4b + endif + ! ***** iterate to find temperature if ice exists - if( T 0._rkind) then T = Tfreeze + 0.1_rkind ! need to merge layers, trigger the merge else - T = brent(diff_H_snow, T, 0._rkind, Tfreeze, vec, err, cmessage) + T = brent(diff_H_snow, T, -1.e6_rkind, Tfreeze, vec, err, cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif end if endif @@ -1109,7 +1110,8 @@ subroutine enthalpy2T_soil(& ! ------------------------------------------------------------------------------------------------------------------------- ! declare local variables character(len=256) :: cmessage ! error message in downwind routine - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind) :: Tcrit ! temperature above which all water is unfrozen (K) + real(rkind) :: entCrit ! enthalpy above which all water is unfrozen (J m-3) real(rkind) :: volFracWat ! volumetric fraction of total water, liquid+ice (-) real(rkind) :: diff0 ! temperature difference of Tcrit from Tfreeze real(rkind) :: dTcrit_dPsi0 ! derivative of temperature where all water is unfrozen (K) with matric head @@ -1126,7 +1128,7 @@ subroutine enthalpy2T_soil(& real(rkind) :: arg ! argument of soil hypergeometric function real(rkind) :: gauss_hg_T ! soil hypergeometric function result real(rkind) :: vec(9) ! vector of parameters for the enthalpy function - ! variable derivatives + ! variable derivatives real(rkind) :: dvolFracWat_dPsi0 ! derivative of the soil water content w.r.t. matric head real(rkind) :: dintegral_unf_dWat ! derivative of integral of unfrozen soil water content with water content real(rkind) :: dintegral_frz_low_dWat ! derivative of integral of frozen soil water content with water content @@ -1152,19 +1154,22 @@ subroutine enthalpy2T_soil(& volFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) dTcrit_dPsi0 = merge(gravity*Tfreeze/LH_fus,0._rkind,mLayerMatricHead<=0._rkind) dvolFracWat_dPsi0 = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + entCrit = ( iden_water * Cp_water * volFracWat + soil_dens_intr * Cp_soil * (1._rkind - theta_sat) & + + iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) ) * (Tcrit - Tfreeze) ! ***** get temperature if unfrozen soil - T = mLayerEnthalpy / ( iden_water * Cp_water * volFracWat + soil_dens_intr * Cp_soil * (1._rkind - theta_sat) & - + iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) ) + Tfreeze - if(computJac)then - dT_dEnthalpy = 1._rkind / ( iden_water * Cp_water * volFracWat + soil_dens_intr*Cp_soil*(1._rkind - theta_sat) & - + iden_air*Cp_air*(1._rkind - theta_sat - volFracWat) ) - dT_dWat = -iden_water * Cp_water * dvolFracWat_dPsi0 * mLayerEnthalpy / ( iden_water * Cp_water * volFracWat & - + soil_dens_intr * Cp_soil * (1._rkind - theta_sat) + iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) )**2_i4b - endif + if (mLayerEnthalpy>=entCrit )then + T = mLayerEnthalpy / ( iden_water * Cp_water * volFracWat + soil_dens_intr * Cp_soil * (1._rkind - theta_sat) & + + iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) ) + Tfreeze + if(computJac)then + dT_dEnthalpy = 1._rkind / ( iden_water * Cp_water * volFracWat + soil_dens_intr*Cp_soil*(1._rkind - theta_sat) & + + iden_air*Cp_air*(1._rkind - theta_sat - volFracWat) ) + dT_dWat = -iden_water * Cp_water * dvolFracWat_dPsi0 * mLayerEnthalpy / ( iden_water * Cp_water * volFracWat & + + soil_dens_intr * Cp_soil * (1._rkind - theta_sat) + iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) )**2_i4b + endif ! ***** iterate to find temperature if ice exists - if( T Date: Thu, 19 Sep 2024 22:53:35 +0900 Subject: [PATCH 1376/1472] enthalpy state var, make it bail with -1e6 as the temp if can't bracket a root --- build/source/engine/enthalpyTemp.f90 | 45 +++++++++++++++------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 09e2abf1e..0fb42a095 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -861,7 +861,7 @@ subroutine enthalpy2T_veg(& ! ------------------------------------------------------------------------------------------------------------------------- ! declare local variables character(len=256) :: cmessage ! error message of downwind routine - real(rkind) :: T ! temperature (K) + real(rkind) :: T,T_out ! temperature (K) real(rkind) :: H ! enthalpy (J m-3) real(rkind) :: diffT ! temperature difference of temp from Tfreeze real(rkind) :: integral ! integral of snow freezing curve @@ -900,7 +900,8 @@ subroutine enthalpy2T_veg(& ! and the vector of parameters, not.snow_layers vec = 0._rkind vec(1:6) = (/scalarCanopyEnthalpy, canopyDepth, specificHeatVeg, maxMassVegetation, snowfrz_scale, scalarCanopyWat/) - T = brent(diff_H_veg, T, -1.e6_rkind, Tfreeze, vec, err, cmessage) + call brent(diff_H_veg, T, T_out, -1.e6_rkind, Tfreeze, vec, err, cmessage) + T = T_out if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! compute Jacobian terms @@ -975,7 +976,7 @@ subroutine enthalpy2T_snow(& ! ------------------------------------------------------------------------------------------------------------------------- ! declare local variables character(len=256) :: cmessage ! error message of downwind routine - real(rkind) :: T ! temperature (K) + real(rkind) :: T,T_out ! temperature (K) real(rkind) :: H ! enthalpy (J m-3) real(rkind) :: diffT ! temperature difference of temp from Tfreeze real(rkind) :: integral ! integral of snow freezing curve @@ -1013,8 +1014,9 @@ subroutine enthalpy2T_snow(& if (l_bound > 0._rkind) then T = Tfreeze + 0.1_rkind ! need to merge layers, trigger the merge else - T = brent(diff_H_snow, T, -1.e6_rkind, Tfreeze, vec, err, cmessage) + call brent(diff_H_snow, T, T_out, -1.e6_rkind, Tfreeze, vec, err, cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + T = T_out end if endif @@ -1120,7 +1122,7 @@ subroutine enthalpy2T_soil(& real(rkind) :: integral_frz_low ! lower limit of integral of frozen soil water content (from Tfreeze to Tcrit) real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) - real(rkind) :: T ! temperature (K) + real(rkind) :: T, T_out ! temperature (K) real(rkind) :: H ! enthalpy (J m-3) real(rkind) :: diffT ! temperature difference of temp from Tfreeze real(rkind) :: fLiq ! fraction liquid water @@ -1210,8 +1212,9 @@ subroutine enthalpy2T_soil(& ! inputs = function, lower bound, upper bound, initial point, tolerance, integer flag if want detail ! and the vector of parameters, not.snow_layer, lookup data vec(1:9) = (/mLayerEnthalpy, soil_dens_intr, vGn_alpha, vGn_n, theta_sat, theta_res, vGn_m, integral_frz_low, mLayerMatricHead/) - T = brent(diff_H_soil, T, -1.e6_rkind, Tcrit, vec, err, cmessage, use_lookup, lookup_data, ixControlIndex) + call brent(diff_H_soil, T, T_out, -1.e6_rkind, Tcrit, vec, err, cmessage, use_lookup, lookup_data, ixControlIndex) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + T = T_out ! compute Jacobian terms if(computJac)then @@ -1472,9 +1475,9 @@ function brent0 (fun, x1, x2, fx1, fx2, tol_x, tol_f, detail, vec, err, message, end function brent0 !---------------------------------------------------------------------- -! private function: Find an initial guess of bracket and call brent0 +! private subroutine: Find an initial guess of bracket and call brent0 !---------------------------------------------------------------------- - function brent (fun, x0, LowerBound, UpperBound, vec, err, message, use_lookup, lookup_data, ixControlIndex) + subroutine brent (fun, x0, brent_out, LowerBound, UpperBound, vec, err, message, use_lookup, lookup_data, ixControlIndex) ! ! Inputs ! fun: function to evaluate @@ -1482,11 +1485,11 @@ function brent (fun, x0, LowerBound, UpperBound, vec, err, message, use_lookup, ! LowerBound, UpperBound : Lower and upper bound of the function implicit none - real(rkind) :: brent integer, parameter :: d = rkind - real(rkind), intent(IN) :: x0, vec(9) + real(rkind), intent(in) :: x0, vec(9) real(rkind), external :: fun - real(rkind), intent(IN) :: LowerBound, UpperBound + real(rkind), intent(out) :: brent_out + real(rkind), intent(in) :: LowerBound, UpperBound logical(lgt), intent(in), optional :: use_lookup type(zLookup),intent(in), optional :: lookup_data integer(i4b), intent(in), optional :: ixControlIndex @@ -1525,8 +1528,7 @@ function brent (fun, x0, LowerBound, UpperBound, vec, err, message, use_lookup, fb = sgn if(abs(sgn) <= tol_f ) then ! if solution didn't change, initial guess is the solution - brent = x0 - return + brent_out = x0; return end if ! set initial change dx @@ -1590,11 +1592,12 @@ function brent (fun, x0, LowerBound, UpperBound, vec, err, message, use_lookup, ! case for non convergence if (exitflag /= 1 ) then - write(*,*) ' Error (temperature from enthalpy computation): Proper initial value for Brents method could not be found in between bounds' - write(*,*) ' i x1 x2 f(x1) f(x2)' - write(*,"(1I4,4F17.6)") iter, a, b, fa, fb - write(*,*) 'vec=',vec - err = 20;message = trim(message)//'proper initial value could not be found'; return + !write(*,*) ' Error (temperature from enthalpy computation): Proper initial value for Brents method could not be found in between bounds' + !write(*,*) ' i x1 x2 f(x1) f(x2)' + !write(*,"(1I4,4F17.6)") iter, a, b, fa, fb + !write(*,*) 'vec=',vec + !err = 20;message = trim(message)//'proper initial value could not be found'; return + brent_out = LowerBound; return ! if bracket is not found, use lower bound else if (disp == 1) then write(*,*) ' Initial guess was found.' write(*,*) '' @@ -1602,13 +1605,13 @@ function brent (fun, x0, LowerBound, UpperBound, vec, err, message, use_lookup, ! call brent0 if(present(use_lookup))then - brent = brent0(fun, a, b, fa, fb, tol_x, tol_f, detail, vec, err, cmessage, use_lookup, lookup_data, ixControlIndex) + brent_out = brent0(fun, a, b, fa, fb, tol_x, tol_f, detail, vec, err, cmessage, use_lookup, lookup_data, ixControlIndex) else - brent = brent0(fun, a, b, fa, fb, tol_x, tol_f, detail, vec, err, cmessage) + brent_out = brent0(fun, a, b, fa, fb, tol_x, tol_f, detail, vec, err, cmessage) end if if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - end function brent + end subroutine brent !---------------------------------------------------------------------- ! private functions for temperature to enthalpy conversion for Brent's method From a943ec8e84c450348c017e97cc4dcabfe111cc8a Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 19 Sep 2024 23:33:16 +0900 Subject: [PATCH 1377/1472] change lower bound to 0 --- build/source/engine/enthalpyTemp.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 0fb42a095..c5222b287 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -900,7 +900,7 @@ subroutine enthalpy2T_veg(& ! and the vector of parameters, not.snow_layers vec = 0._rkind vec(1:6) = (/scalarCanopyEnthalpy, canopyDepth, specificHeatVeg, maxMassVegetation, snowfrz_scale, scalarCanopyWat/) - call brent(diff_H_veg, T, T_out, -1.e6_rkind, Tfreeze, vec, err, cmessage) + call brent(diff_H_veg, T, T_out, 0._rkind, Tfreeze, vec, err, cmessage) T = T_out if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -1014,7 +1014,7 @@ subroutine enthalpy2T_snow(& if (l_bound > 0._rkind) then T = Tfreeze + 0.1_rkind ! need to merge layers, trigger the merge else - call brent(diff_H_snow, T, T_out, -1.e6_rkind, Tfreeze, vec, err, cmessage) + call brent(diff_H_snow, T, T_out, 0._rkind, Tfreeze, vec, err, cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif T = T_out end if @@ -1212,7 +1212,7 @@ subroutine enthalpy2T_soil(& ! inputs = function, lower bound, upper bound, initial point, tolerance, integer flag if want detail ! and the vector of parameters, not.snow_layer, lookup data vec(1:9) = (/mLayerEnthalpy, soil_dens_intr, vGn_alpha, vGn_n, theta_sat, theta_res, vGn_m, integral_frz_low, mLayerMatricHead/) - call brent(diff_H_soil, T, T_out, -1.e6_rkind, Tcrit, vec, err, cmessage, use_lookup, lookup_data, ixControlIndex) + call brent(diff_H_soil, T, T_out, 0._rkind, Tcrit, vec, err, cmessage, use_lookup, lookup_data, ixControlIndex) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif T = T_out From 5c8f5f99c600736d4b6c7ea41cc83a93af2d340f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 20 Sep 2024 14:17:57 +0900 Subject: [PATCH 1378/1472] change lower bound error --- build/source/engine/enthalpyTemp.f90 | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index c5222b287..8120384bb 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -1592,12 +1592,15 @@ subroutine brent (fun, x0, brent_out, LowerBound, UpperBound, vec, err, message, ! case for non convergence if (exitflag /= 1 ) then - !write(*,*) ' Error (temperature from enthalpy computation): Proper initial value for Brents method could not be found in between bounds' - !write(*,*) ' i x1 x2 f(x1) f(x2)' - !write(*,"(1I4,4F17.6)") iter, a, b, fa, fb - !write(*,*) 'vec=',vec - !err = 20;message = trim(message)//'proper initial value could not be found'; return - brent_out = LowerBound; return ! if bracket is not found, use lower bound + if (a==LowerBound .and. fa>0 .and. fb>0)then + brent_out = LowerBound; return ! if bracket is not found, use lower bound since true temperature and enthalpy is very low, LowerBound is close enough + else + write(*,*) ' Error (temperature from enthalpy computation): Proper initial value for Brents method could not be found in between bounds' + write(*,*) ' i x1 x2 f(x1) f(x2)' + write(*,"(1I4,4F17.6)") iter, a, b, fa, fb + write(*,*) 'vec=',vec + err = 20;message = trim(message)//'proper initial value could not be found'; return + endif else if (disp == 1) then write(*,*) ' Initial guess was found.' write(*,*) '' From 48630a54923b2ca57b3c13bf61e8d6f1f2460fd7 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 23 Sep 2024 21:49:24 +0900 Subject: [PATCH 1379/1472] vapor pressure 0 will give a nan inside stomata --- build/source/engine/conv_funcs.f90 | 12 ++++++------ build/source/engine/enthalpyTemp.f90 | 6 +++--- build/source/noah-mp/module_sf_noahmplsm.F | 11 ++++++++--- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/build/source/engine/conv_funcs.f90 b/build/source/engine/conv_funcs.f90 index 4ff4fb22a..5078b8d61 100644 --- a/build/source/engine/conv_funcs.f90 +++ b/build/source/engine/conv_funcs.f90 @@ -80,16 +80,16 @@ subroutine satVapPress(TC, SVP, dSVP_dT) real(rkind), parameter :: X1 = 17.27_rkind real(rkind), parameter :: X2 = 237.30_rkind ! local (use to test derivative calculations) -real(rkind),parameter :: dx = 1.e-8_rkind ! finite difference increment -logical(lgt),parameter :: testDeriv=.false. ! flag to test the derivative +real(rkind),parameter :: dx = 1.e-8_rkind ! finite difference increment +logical(lgt),parameter :: testDeriv=.false. ! flag to test the derivative !--------------------------------------------------------------------------------------------------- ! Units note : Pa = N m-2 = kg m-1 s-2 ! SATVPFRZ= 610.8 ! Saturation water vapour pressure at 273.16K (Pa) -if(X2 + TC < 0)then - !print*, "error, canopy temperature is very low, satVapPress=Inf" !will fail as SVP=inf - SVP = 0._rkind - dSVP_dT = 0._rkind +if(X2 + TC <= 0.0_rkind)then ! will fail if divide by 0, but will blow up if negative top and bottom of fraction + !print*, "error, canopy temperature is very low, satVapPress=Inf" + SVP = tiny(1.0_rkind) + dSVP_dT = tiny(1.0_rkind) else SVP = SATVPFRZ * EXP( (X1*TC)/(X2 + TC) ) ! Saturated Vapour Press (Pa) dSVP_dT = SVP * (X1/(X2 + TC) - X1*TC/(X2 + TC)**2_i4b) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 8120384bb..a7c8f0b05 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -900,7 +900,7 @@ subroutine enthalpy2T_veg(& ! and the vector of parameters, not.snow_layers vec = 0._rkind vec(1:6) = (/scalarCanopyEnthalpy, canopyDepth, specificHeatVeg, maxMassVegetation, snowfrz_scale, scalarCanopyWat/) - call brent(diff_H_veg, T, T_out, 0._rkind, Tfreeze, vec, err, cmessage) + call brent(diff_H_veg, T, T_out, -1.e3_rkind, Tfreeze, vec, err, cmessage) T = T_out if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -1014,7 +1014,7 @@ subroutine enthalpy2T_snow(& if (l_bound > 0._rkind) then T = Tfreeze + 0.1_rkind ! need to merge layers, trigger the merge else - call brent(diff_H_snow, T, T_out, 0._rkind, Tfreeze, vec, err, cmessage) + call brent(diff_H_snow, T, T_out, -1.e3_rkind, Tfreeze, vec, err, cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif T = T_out end if @@ -1212,7 +1212,7 @@ subroutine enthalpy2T_soil(& ! inputs = function, lower bound, upper bound, initial point, tolerance, integer flag if want detail ! and the vector of parameters, not.snow_layer, lookup data vec(1:9) = (/mLayerEnthalpy, soil_dens_intr, vGn_alpha, vGn_n, theta_sat, theta_res, vGn_m, integral_frz_low, mLayerMatricHead/) - call brent(diff_H_soil, T, T_out, 0._rkind, Tcrit, vec, err, cmessage, use_lookup, lookup_data, ixControlIndex) + call brent(diff_H_soil, T, T_out, -1.e3_rkind, Tcrit, vec, err, cmessage, use_lookup, lookup_data, ixControlIndex) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif T = T_out diff --git a/build/source/noah-mp/module_sf_noahmplsm.F b/build/source/noah-mp/module_sf_noahmplsm.F index bb21f2901..b1ca0eac0 100644 --- a/build/source/noah-mp/module_sf_noahmplsm.F +++ b/build/source/noah-mp/module_sf_noahmplsm.F @@ -1520,7 +1520,7 @@ END SUBROUTINE TWOSTREAM ! ================================================================================================== ! ---------------------------------------------------------------------- SUBROUTINE STOMATA (VEGTYP ,MPE ,APAR ,FOLN ,ILOC , JLOC, & !in - TV ,EI ,EA ,SFCTMP ,SFCPRS , & !in + TV ,EI0 ,EA ,SFCTMP ,SFCPRS , & !in O2 ,CO2 ,IGS ,BTRAN ,RB , & !in RS ,PSN ) !out ! -------------------------------------------------------------------------------------------------- @@ -1537,7 +1537,7 @@ SUBROUTINE STOMATA (VEGTYP ,MPE ,APAR ,FOLN ,ILOC , JLOC, & !in REAL(rkind), INTENT(IN) :: MPE !prevents division by zero errors REAL(rkind), INTENT(IN) :: TV !foliage temperature (k) - REAL(rkind), INTENT(IN) :: EI !vapor pressure inside leaf (sat vapor press at tv) (pa) + REAL(rkind), INTENT(IN) :: EI0 !vapor pressure inside leaf (sat vapor press at tv) (pa) REAL(rkind), INTENT(IN) :: EA !vapor pressure of canopy air (pa) REAL(rkind), INTENT(IN) :: APAR !par absorbed per unit lai (w/m2) REAL(rkind), INTENT(IN) :: O2 !atmospheric o2 concentration (pa) @@ -1569,6 +1569,7 @@ SUBROUTINE STOMATA (VEGTYP ,MPE ,APAR ,FOLN ,ILOC , JLOC, & !in REAL(rkind) :: F2 !generic temperature inhibition (statement function) REAL(rkind) :: TC !foliage temperature (degree Celsius) REAL(rkind) :: CS !co2 concentration at leaf surface (pa) + REAL(rkind) :: EI !vapor pressure inside leaf (sat vapor press at tv) (pa) REAL(rkind) :: KC !co2 Michaelis-Menten constant (pa) REAL(rkind) :: KO !o2 Michaelis-Menten constant (pa) REAL(rkind) :: A,B,C,Q !intermediate calculations for RS @@ -1598,7 +1599,11 @@ SUBROUTINE STOMATA (VEGTYP ,MPE ,APAR ,FOLN ,ILOC , JLOC, & !in ! initialize RS=RSMAX and PSN=0 because will only do calculations ! for APAR > 0, in which case RS <= RSMAX and PSN >= 0 - + if (EI0 Date: Tue, 24 Sep 2024 00:32:39 +0900 Subject: [PATCH 1380/1472] fix overwrite of cmake prefix path --- build/cmake/build.cluster.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/cmake/build.cluster.bash b/build/cmake/build.cluster.bash index 4a1b4f1bf..ddeb53f54 100755 --- a/build/cmake/build.cluster.bash +++ b/build/cmake/build.cluster.bash @@ -8,7 +8,7 @@ module load openblas/0.3.24 module load openmpi/4.1.5 module load netcdf-fortran/4.6.1 -export CMAKE_PREFIX_PATH=../../../sundials/instdir/ +export SUNDIALS_PATH=../../../sundials/instdir/ export FLAGS_OPT="-flto=1;-fuse-linker-plugin" From 5c8128a27d98a31afa4abc43c17daa27620c3549 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 24 Sep 2024 16:42:30 +0900 Subject: [PATCH 1381/1472] lower bound 0, change merge criterion to be just on enthalpy --- build/source/engine/enthalpyTemp.f90 | 19 +++++++------------ build/source/engine/summaSolve4ida.f90 | 2 +- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index a7c8f0b05..0b72d216e 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -900,7 +900,7 @@ subroutine enthalpy2T_veg(& ! and the vector of parameters, not.snow_layers vec = 0._rkind vec(1:6) = (/scalarCanopyEnthalpy, canopyDepth, specificHeatVeg, maxMassVegetation, snowfrz_scale, scalarCanopyWat/) - call brent(diff_H_veg, T, T_out, -1.e3_rkind, Tfreeze, vec, err, cmessage) + call brent(diff_H_veg, T, T_out, 0._rkind, Tfreeze, vec, err, cmessage) T = T_out if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -1008,18 +1008,13 @@ subroutine enthalpy2T_snow(& vec = 0._rkind vec(1:3) = (/mLayerEnthalpy, snowfrz_scale, mLayerVolFracWat/) if(mLayerEnthalpy>0._rkind)then - T = Tfreeze+ 0.1_rkind ! need to merge layers, trigger the merge + T = Tfreeze - 1.e-6_rkind ! need to merge layers, don't iterate to find the temperature else - l_bound = diff_H_snow(0._rkind, vec) - if (l_bound > 0._rkind) then - T = Tfreeze + 0.1_rkind ! need to merge layers, trigger the merge - else - call brent(diff_H_snow, T, T_out, -1.e3_rkind, Tfreeze, vec, err, cmessage) - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - T = T_out - end if + call brent(diff_H_snow, T, T_out, 0._rkind, Tfreeze, vec, err, cmessage) + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + T = T_out endif - + ! compute Jacobian terms if(computJac)then ! NOTE: dintegral_dT = fLiq @@ -1212,7 +1207,7 @@ subroutine enthalpy2T_soil(& ! inputs = function, lower bound, upper bound, initial point, tolerance, integer flag if want detail ! and the vector of parameters, not.snow_layer, lookup data vec(1:9) = (/mLayerEnthalpy, soil_dens_intr, vGn_alpha, vGn_n, theta_sat, theta_res, vGn_m, integral_frz_low, mLayerMatricHead/) - call brent(diff_H_soil, T, T_out, -1.e3_rkind, Tcrit, vec, err, cmessage, use_lookup, lookup_data, ixControlIndex) + call brent(diff_H_soil, T, T_out, 0._rkind, Tcrit, vec, err, cmessage, use_lookup, lookup_data, ixControlIndex) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif T = T_out diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 67117a9ee..29e862888 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -493,7 +493,7 @@ subroutine summaSolve4ida(& ! loop through non-missing energy state variables in the snow domain to see if need to merge do concurrent (i=1:nSnow,ixSnowOnlyNrg(i)/=integerMissing) if(model_decisions(iLookDECISIONS%nrgConserv)%iDecision.ne.closedForm)then !using enthalpy as state variable - if (eqns_data%mLayerTempTrial(i) > Tfreeze .or. stateVec(ixSnowOnlyNrg(i)) > 0._rkind) tooMuchMelt = .true. !need to merge + if (stateVec(ixSnowOnlyNrg(i)) > 0._rkind) tooMuchMelt = .true. !need to merge else if (stateVec(ixSnowOnlyNrg(i)) > Tfreeze) tooMuchMelt = .true. !need to merge endif From 503457abd80eb8a0f2be2eab0ed2d6988df00531 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 24 Sep 2024 17:02:14 +0900 Subject: [PATCH 1382/1472] fix nan Jacobian error message --- build/source/engine/computJacob.f90 | 6 ++---- build/source/engine/computJacobWithPrime.f90 | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index 78598c3ea..c1df88b30 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -985,7 +985,7 @@ subroutine computJacob(& ! ********************************************************************************************************************************************************* ! print the Jacobian - if(globalPrintFlag)then + if(globalPrintFlag .or. any(isNan(aJac)))then select case(ixMatrix) case(ixBandMatrix) print*, '** banded analytical Jacobian:' @@ -1003,9 +1003,7 @@ subroutine computJacob(& endif if(any(isNan(aJac)))then - print *, '******************************* WE FOUND NAN IN JACOBIAN ************************************' - stop 1 - message=trim(message)//'we found NaN' + message=trim(message)//'NaN in Jacobian' err=20; return endif diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index d12e2187d..810ca8be7 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -1113,7 +1113,7 @@ subroutine computJacobWithPrime(& deallocate(watRows,nrgRows) ! print the Jacobian - if(globalPrintFlag)then + if(globalPrintFlag .or. any(isNan(aJac)))then select case(ixMatrix) case(ixBandMatrix) print*, '** banded analytical Jacobian:' @@ -1131,9 +1131,7 @@ subroutine computJacobWithPrime(& endif if(any(isNan(aJac)))then - print *, '******************************* WE FOUND NAN IN JACOBIAN ************************************' - stop 1 - message=trim(message)//'we found NaN' + message=trim(message)//'NaN in Jacobian' err=20; return endif From a0ea6d06eefa1223549b2009e50916128e64cca9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 24 Sep 2024 22:19:14 +0900 Subject: [PATCH 1383/1472] make bracketing smarter --- build/source/engine/enthalpyTemp.f90 | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 0b72d216e..d9695fcd5 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -1495,7 +1495,7 @@ subroutine brent (fun, x0, brent_out, LowerBound, UpperBound, vec, err, message, real(rkind), parameter :: sqrt2 = sqrt(2.0_d)! change in dx integer, parameter :: maxiter = 40, detail = 0 real(rkind) :: dx ! change in bracket - integer :: iter, exitflag, disp + integer :: iter, exitflag, disp, exita, exitb real(rkind) :: sgn real(rkind), parameter :: tol_x = 1.e-5_rkind, tol_f = 1.e0_rkind character(LEN=256):: cmessage ! error message of downwind routine @@ -1507,6 +1507,8 @@ subroutine brent (fun, x0, brent_out, LowerBound, UpperBound, vec, err, message, a = x0 ! lower bracket b = x0 ! upper bracket exitflag = 0 ! flag to see we found the bracket + exita = 0 + exitb = 0 if(present(use_lookup))then sgn = fun(x0, vec, use_lookup, lookup_data, ixControlIndex) ! sign of initial guess @@ -1544,13 +1546,17 @@ subroutine brent (fun, x0, brent_out, LowerBound, UpperBound, vec, err, message, ! main loop to extend a and b do iter = 1, maxiter - ! update boundary + ! update boundary, function is monotonically increasing + if (fa<=0) exita = 1 + if (fb<=0)then; a = b;fa = fb; exita = 1; endif + if (fb>=0) exitb = 1 + if (fa>=0)then; b = a; fb = fa; exitb = 1; endif olda = a oldb = b folda = fa foldb = fb - a = a - dx - b = b + dx + if (exita/= 1) a = a - dx + if (exitb/= 1) b = b + dx dx = dx * sqrt2 ! boundary check @@ -1558,14 +1564,14 @@ subroutine brent (fun, x0, brent_out, LowerBound, UpperBound, vec, err, message, if (b > UpperBound ) b = UpperBound if(present(use_lookup))then - fa = fun(a, vec, use_lookup, lookup_data, ixControlIndex) - fb = fun(b, vec, use_lookup, lookup_data, ixControlIndex) + if (exita/= 1) fa = fun(a, vec, use_lookup, lookup_data, ixControlIndex) + if (exitb/= 1) fb = fun(b, vec, use_lookup, lookup_data, ixControlIndex) else - fa = fun(a, vec) - fb = fun(b, vec) + if (exita/= 1) fa = fun(a, vec) + if (exitb/= 1) fb = fun(b, vec) end if - if (disp == 1) write(*,"(1I4,4F17.6)") iter, a, b, fa, fb + if (disp == 1) write(*,"(3I4,4F17.6)") exita,exitb,iter, a, b, fa, fb ! check if sign of functions changed or not if (( (sgn >= 0 ) .and. (fa <= 0) ) .or. & From 9f84ec0dd40141776d619ed3a7eda28a39d4c837 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 24 Sep 2024 23:01:31 +0900 Subject: [PATCH 1384/1472] fixing brackets --- build/source/engine/enthalpyTemp.f90 | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index d9695fcd5..444e851dc 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -894,6 +894,7 @@ subroutine enthalpy2T_veg(& ! ***** iterate to find temperature if ice exists else T = min(scalarCanopyTemp,Tfreeze) ! initial guess + T = max(T,200._rkind) ! don't give too cold of an initial guess ! find the root of the function ! inputs = function, lower bound, upper bound, initial point, tolerance, integer flag if want detail @@ -1000,7 +1001,8 @@ subroutine enthalpy2T_snow(& err=0; message="enthalpy2T_snow/" ! ***** iterate to find temperature, ice always exists - T = mLayerTemp ! initial guess, will be less than Tfreeze since was a solution + T = mLayerTemp ! initial guess, will be less than Tfreeze since was a solution + T = max(T,200._rkind) ! don't give too cold of an initial guess ! find the root of the function ! inputs = function, lower bound, upper bound, initial point, tolerance, integer flag if want detail @@ -1168,6 +1170,7 @@ subroutine enthalpy2T_soil(& ! ***** iterate to find temperature if ice exists else T = min(mLayerTemp,Tcrit) ! initial guess + T = max(T,200._rkind) ! don't give too cold of an initial guess ! *** compute integral of mLayerPsiLiq from Tfreeze to layer temperature ! get the unfrozen water content of enthalpy @@ -1529,10 +1532,10 @@ subroutine brent (fun, x0, brent_out, LowerBound, UpperBound, vec, err, message, end if ! set initial change dx - if (abs(x0)<0.00000002_rkind) then - dx = 1.0_rkind/50.0_rkind + if (abs(x0)<240._rkind) then ! a very cold temperature + dx = 2.0_rkind/50.0_rkind * UpperBound else - dx = 1.0_rkind/50.0_rkind * x0 + dx = 1.0_rkind/50.0_rkind * UpperBound end if if (disp == 1) then @@ -1571,7 +1574,7 @@ subroutine brent (fun, x0, brent_out, LowerBound, UpperBound, vec, err, message, if (exitb/= 1) fb = fun(b, vec) end if - if (disp == 1) write(*,"(3I4,4F17.6)") exita,exitb,iter, a, b, fa, fb + if (disp == 1) write(*,"(1I4,4F17.6)") iter, a, b, fa, fb ! check if sign of functions changed or not if (( (sgn >= 0 ) .and. (fa <= 0) ) .or. & From 4e8e9a9ea3acc050c7347c15790c40e2ed9e5a10 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 25 Sep 2024 13:34:00 +0900 Subject: [PATCH 1385/1472] debugging bracket --- build/source/engine/enthalpyTemp.f90 | 11 ++++++----- build/source/engine/summaSolve4ida.f90 | 3 ++- build/source/engine/summaSolve4kinsol.f90 | 3 ++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 444e851dc..396c372a1 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -1504,8 +1504,7 @@ subroutine brent (fun, x0, brent_out, LowerBound, UpperBound, vec, err, message, character(LEN=256):: cmessage ! error message of downwind routine ! initialize error control - err=0; message='brent/' - + err=0; message='brent/' a = x0 ! lower bracket b = x0 ! upper bracket @@ -1546,7 +1545,7 @@ subroutine brent (fun, x0, brent_out, LowerBound, UpperBound, vec, err, message, write(*,*) ' i x1 x2 f(x1) f(x2)' write(*,"(1I4,4F17.6)") 0, a, b, fa, fb end if - + if (a<200)disp = 1 ! main loop to extend a and b do iter = 1, maxiter ! update boundary, function is monotonically increasing @@ -1563,8 +1562,8 @@ subroutine brent (fun, x0, brent_out, LowerBound, UpperBound, vec, err, message, dx = dx * sqrt2 ! boundary check - if (a < LowerBound ) a = LowerBound - if (b > UpperBound ) b = UpperBound + if (exita/= 1 .and. a < LowerBound ) a = LowerBound + if (exitb/= 1 .and. b > UpperBound ) b = UpperBound if(present(use_lookup))then if (exita/= 1) fa = fun(a, vec, use_lookup, lookup_data, ixControlIndex) @@ -1582,12 +1581,14 @@ subroutine brent (fun, x0, brent_out, LowerBound, UpperBound, vec, err, message, ! use a and olda as bracket b = olda fb = folda + if (disp == 1) write(*,"(1I4,4F17.6)") iter, a, b, fa, fb exitflag = 1 exit else if (( (sgn >= 0 ) .and. (fb <= 0 ) ) .or. & ( (sgn <= 0 ) .and. (fb >= 0 ) )) then ! sign of b changed a = oldb fa = foldb + if (disp == 1) write(*,"(1I4,4F17.6)") iter, a, b, fa, fb exitflag = 1 exit end if diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 29e862888..9decef204 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -483,7 +483,8 @@ subroutine summaSolve4ida(& ! early return if IDASolve failed if( retvalr < 0 )then idaSucceeds = .false. - call getErrMessage(retvalr,cmessage) + if (eqns_data%err/=0)then; message=trim(message)//trim(eqns_data%message); return; endif !fail from summa problem + call getErrMessage(retvalr,cmessage) ! fail from solver problem message=trim(message)//trim(cmessage) !if(retvalr==-1) err = -20 ! max iterations failure, exit and reduce the data window time in varSubStep exit diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index be45a0714..d2aea62f6 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -350,7 +350,8 @@ subroutine summaSolve4kinsol(& ! check if KINSol failed if( retvalr < 0 )then kinsolSucceeds = .false. - call getErrMessage(retvalr,cmessage) + if (eqns_data%err/=0)then; message=trim(message)//trim(eqns_data%message); return; endif !fail from summa problem + call getErrMessage(retvalr,cmessage) ! fail from solver problem message=trim(message)//trim(cmessage) if(retvalr==-6) err = -20 ! max iterations failure, exit and reduce the data window time in varSubStep else From 6ae495ab6c9b2d6ad0c9254f5c8892e874d29220 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 25 Sep 2024 14:47:57 +0900 Subject: [PATCH 1386/1472] debug brackets --- build/source/engine/enthalpyTemp.f90 | 37 +++++++++++++++++----------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 396c372a1..46a08bd9e 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -1210,7 +1210,11 @@ subroutine enthalpy2T_soil(& ! inputs = function, lower bound, upper bound, initial point, tolerance, integer flag if want detail ! and the vector of parameters, not.snow_layer, lookup data vec(1:9) = (/mLayerEnthalpy, soil_dens_intr, vGn_alpha, vGn_n, theta_sat, theta_res, vGn_m, integral_frz_low, mLayerMatricHead/) - call brent(diff_H_soil, T, T_out, 0._rkind, Tcrit, vec, err, cmessage, use_lookup, lookup_data, ixControlIndex) + if (Tcrit>0._rkind) then + call brent(diff_H_soil, T, T_out, 0._rkind, Tcrit, vec, err, cmessage, use_lookup, lookup_data, ixControlIndex) + else + T_out = 0._rkind ! bail with a low temperature + end if if(err/=0)then; message=trim(message)//trim(cmessage); return; endif T = T_out @@ -1496,7 +1500,7 @@ subroutine brent (fun, x0, brent_out, LowerBound, UpperBound, vec, err, message, real(rkind) :: a , b , olda, oldb, fa, fb, folda, foldb real(rkind), parameter :: sqrt2 = sqrt(2.0_d)! change in dx - integer, parameter :: maxiter = 40, detail = 0 + integer, parameter :: maxiter = 20, detail = 0 real(rkind) :: dx ! change in bracket integer :: iter, exitflag, disp, exita, exitb real(rkind) :: sgn @@ -1525,6 +1529,7 @@ subroutine brent (fun, x0, brent_out, LowerBound, UpperBound, vec, err, message, end if fa = sgn fb = sgn + if (vec(1)< -1391120611) disp =1 if(abs(sgn) <= tol_f ) then ! if solution didn't change, initial guess is the solution brent_out = x0; return @@ -1532,9 +1537,9 @@ subroutine brent (fun, x0, brent_out, LowerBound, UpperBound, vec, err, message, ! set initial change dx if (abs(x0)<240._rkind) then ! a very cold temperature - dx = 2.0_rkind/50.0_rkind * UpperBound + dx = 2.0_rkind/50.0_rkind * Tfreeze else - dx = 1.0_rkind/50.0_rkind * UpperBound + dx = 1.0_rkind/50.0_rkind * Tfreeze end if if (disp == 1) then @@ -1545,25 +1550,27 @@ subroutine brent (fun, x0, brent_out, LowerBound, UpperBound, vec, err, message, write(*,*) ' i x1 x2 f(x1) f(x2)' write(*,"(1I4,4F17.6)") 0, a, b, fa, fb end if - if (a<200)disp = 1 + ! main loop to extend a and b do iter = 1, maxiter ! update boundary, function is monotonically increasing if (fa<=0) exita = 1 - if (fb<=0)then; a = b;fa = fb; exita = 1; endif + if (fb<=0)then; a = b; fa = fb; exita = 1; endif if (fb>=0) exitb = 1 if (fa>=0)then; b = a; fb = fa; exitb = 1; endif olda = a oldb = b folda = fa foldb = fb - if (exita/= 1) a = a - dx - if (exitb/= 1) b = b + dx + if (exita/= 1)then + a = a - dx + if (a < LowerBound ) a = LowerBound + endif + if (exitb/= 1)then + b = b + dx + if (b > UpperBound ) b = UpperBound + endif dx = dx * sqrt2 - - ! boundary check - if (exita/= 1 .and. a < LowerBound ) a = LowerBound - if (exitb/= 1 .and. b > UpperBound ) b = UpperBound if(present(use_lookup))then if (exita/= 1) fa = fun(a, vec, use_lookup, lookup_data, ixControlIndex) @@ -1572,6 +1579,8 @@ subroutine brent (fun, x0, brent_out, LowerBound, UpperBound, vec, err, message, if (exita/= 1) fa = fun(a, vec) if (exitb/= 1) fb = fun(b, vec) end if + if (a==LowerBound) exita = 1 + if (b==UpperBound) exitb = 1 if (disp == 1) write(*,"(1I4,4F17.6)") iter, a, b, fa, fb @@ -1581,14 +1590,14 @@ subroutine brent (fun, x0, brent_out, LowerBound, UpperBound, vec, err, message, ! use a and olda as bracket b = olda fb = folda - if (disp == 1) write(*,"(1I4,4F17.6)") iter, a, b, fa, fb + if (disp == 1) write(*,"(1A4,4F17.6)") "end ", a, b, fa, fb exitflag = 1 exit else if (( (sgn >= 0 ) .and. (fb <= 0 ) ) .or. & ( (sgn <= 0 ) .and. (fb >= 0 ) )) then ! sign of b changed a = oldb fa = foldb - if (disp == 1) write(*,"(1I4,4F17.6)") iter, a, b, fa, fb + if (disp == 1) write(*,"(1A4,4F17.6)") "end ", a, b, fa, fb exitflag = 1 exit end if From fabb57fe28e9fffa13974db2c018439a3d46b783 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 25 Sep 2024 15:50:46 +0900 Subject: [PATCH 1387/1472] fixing bracketing --- build/cmake/build.cluster.bash | 2 +- build/cmake/build.mac.bash | 2 +- build/source/engine/enthalpyTemp.f90 | 7 +++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/build/cmake/build.cluster.bash b/build/cmake/build.cluster.bash index ddeb53f54..e854096b4 100755 --- a/build/cmake/build.cluster.bash +++ b/build/cmake/build.cluster.bash @@ -12,5 +12,5 @@ export SUNDIALS_PATH=../../../sundials/instdir/ export FLAGS_OPT="-flto=1;-fuse-linker-plugin" -cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON +cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DCMAKE_BUILD_TYPE=Release cmake --build ../cmake_build --target all -j diff --git a/build/cmake/build.mac.bash b/build/cmake/build.mac.bash index 68b5dd5e3..143b8feec 100755 --- a/build/cmake/build.mac.bash +++ b/build/cmake/build.mac.bash @@ -8,5 +8,5 @@ export FC=/opt/local/bin/gfortran # Fortran compiler family #export FLAGS_OPT="-flto=1" # -flto=1 is slow to compile, but might want to use -cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON +cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DCMAKE_BUILD_TYPE=Release cmake --build ../cmake_build --target all -j diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index 46a08bd9e..eb6adff1c 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -1504,7 +1504,7 @@ subroutine brent (fun, x0, brent_out, LowerBound, UpperBound, vec, err, message, real(rkind) :: dx ! change in bracket integer :: iter, exitflag, disp, exita, exitb real(rkind) :: sgn - real(rkind), parameter :: tol_x = 1.e-5_rkind, tol_f = 1.e0_rkind + real(rkind), parameter :: tol_x = 1.e-5_rkind, tol_f = 1.e0_rkind ! maybe these should be tied to the state variable tolerances character(LEN=256):: cmessage ! error message of downwind routine ! initialize error control @@ -1529,7 +1529,6 @@ subroutine brent (fun, x0, brent_out, LowerBound, UpperBound, vec, err, message, end if fa = sgn fb = sgn - if (vec(1)< -1391120611) disp =1 if(abs(sgn) <= tol_f ) then ! if solution didn't change, initial guess is the solution brent_out = x0; return @@ -1590,14 +1589,12 @@ subroutine brent (fun, x0, brent_out, LowerBound, UpperBound, vec, err, message, ! use a and olda as bracket b = olda fb = folda - if (disp == 1) write(*,"(1A4,4F17.6)") "end ", a, b, fa, fb exitflag = 1 exit else if (( (sgn >= 0 ) .and. (fb <= 0 ) ) .or. & ( (sgn <= 0 ) .and. (fb >= 0 ) )) then ! sign of b changed a = oldb fa = foldb - if (disp == 1) write(*,"(1A4,4F17.6)") "end ", a, b, fa, fb exitflag = 1 exit end if @@ -1608,6 +1605,8 @@ subroutine brent (fun, x0, brent_out, LowerBound, UpperBound, vec, err, message, if (exitflag /= 1 ) then if (a==LowerBound .and. fa>0 .and. fb>0)then brent_out = LowerBound; return ! if bracket is not found, use lower bound since true temperature and enthalpy is very low, LowerBound is close enough + elseif (b==UpperBound .and. fa<0 .and. fb<0)then ! fb will be a small negative value but should be zero to tolerances + brent_out = UpperBound; return ! if bracket is not found, use upper bound since true temperature is very close to upper bound, UpperBound is close enough else write(*,*) ' Error (temperature from enthalpy computation): Proper initial value for Brents method could not be found in between bounds' write(*,*) ' i x1 x2 f(x1) f(x2)' From 91219ac4fc7f3b08114f4db29aedec3a9701032f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 26 Sep 2024 17:06:12 +0900 Subject: [PATCH 1388/1472] build scripts --- build/cmake/build.cluster.bash | 3 +-- build/cmake/build.mac.bash | 2 ++ build/cmake/build_actors.mac.bash | 2 ++ build/cmake/build_ngen.cluster.bash | 1 + build/cmake/build_ngen.mac.bash | 1 + 5 files changed, 7 insertions(+), 2 deletions(-) diff --git a/build/cmake/build.cluster.bash b/build/cmake/build.cluster.bash index ddeb53f54..c3d3ea9d5 100755 --- a/build/cmake/build.cluster.bash +++ b/build/cmake/build.cluster.bash @@ -8,9 +8,8 @@ module load openblas/0.3.24 module load openmpi/4.1.5 module load netcdf-fortran/4.6.1 -export SUNDIALS_PATH=../../../sundials/instdir/ - export FLAGS_OPT="-flto=1;-fuse-linker-plugin" +export SUNDIALS_PATH=../../../sundials/instdir/ cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON cmake --build ../cmake_build --target all -j diff --git a/build/cmake/build.mac.bash b/build/cmake/build.mac.bash index 68b5dd5e3..1f427e420 100755 --- a/build/cmake/build.mac.bash +++ b/build/cmake/build.mac.bash @@ -8,5 +8,7 @@ export FC=/opt/local/bin/gfortran # Fortran compiler family #export FLAGS_OPT="-flto=1" # -flto=1 is slow to compile, but might want to use +export SUNDIALS_DIR=../../../sundials/instdir/ + cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON cmake --build ../cmake_build --target all -j diff --git a/build/cmake/build_actors.mac.bash b/build/cmake/build_actors.mac.bash index cc617457b..0770cf5e4 100755 --- a/build/cmake/build_actors.mac.bash +++ b/build/cmake/build_actors.mac.bash @@ -8,5 +8,7 @@ export FC=/opt/local/bin/gfortran # Fortran compiler family #export FLAGS_OPT="-flto=1" # -flto=1 is slow to compile, but might want to use +export SUNDIALS_DIR=../../../sundials/instdir/ + cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DUSE_ACTORS=ON cmake --build ../cmake_build --target all -j diff --git a/build/cmake/build_ngen.cluster.bash b/build/cmake/build_ngen.cluster.bash index 99b615087..6dc43b1bc 100755 --- a/build/cmake/build_ngen.cluster.bash +++ b/build/cmake/build_ngen.cluster.bash @@ -9,6 +9,7 @@ module load openmpi/4.1.5 module load netcdf-fortran/4.6.1 export FLAGS_OPT="-flto=1;-fuse-linker-plugin" +export SUNDIALS_PATH=../../../sundials/instdir/ # for NextGen module load boost diff --git a/build/cmake/build_ngen.mac.bash b/build/cmake/build_ngen.mac.bash index 5158134f7..ae5aff0a7 100755 --- a/build/cmake/build_ngen.mac.bash +++ b/build/cmake/build_ngen.mac.bash @@ -9,6 +9,7 @@ export FC=/opt/local/bin/gfortran # Fortran compiler #export FLAGS_OPT="-flto=1" # -flto=1 is slow to compile, but might want to use export C_INCLUDE_PATH=/opt/local/include export CPLUS_INCLUDE_PATH=/opt/local/include +export SUNDIALS_DIR=../../../sundials/instdir/ cmake -B extern/summa/cmake_build -S extern/summa -DUSE_NEXTGEN=ON cmake --build extern/summa/cmake_build --target all -j From 500dc66eac3d0a14ec121b7a2fd4f1ef9352ee3f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 26 Sep 2024 22:03:05 +0900 Subject: [PATCH 1389/1472] not working --- build/CMakeLists.txt | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index aa2040b1d..755dda55e 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -76,7 +76,7 @@ if (USE_OPENWQ) else() set(EXEC_NAME summa_openwq.exe) endif() -endif() +endif() get_filename_component(F_MASTER "${F_MASTER}" REALPATH) @@ -92,15 +92,31 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXEC_DIR}) find_package(NetCDF REQUIRED) list(APPEND EXT_TARGETS NetCDF::NetCDF) -# OpenBLAS (MAY HAVE OpenMP enabled which slows execution) -# USE LAPACK INSTEAD TO AVOID THIS ISSUE -# set(BLA_VENDOR OpenBLAS) # MKL -# find_package(OpenBLAS REQUIRED) -# list(APPEND EXT_TARGETS OpenBLAS::OpenBLAS) +if (APPLE) + # Set link directories and include directories from environment variables + set(LINK_DIRS $ENV{LINK_DIRS}) + set(INCLUDES_DIRS $ENV{INCLUDES_DIRS}) + set(LIBRARY_LINKS $ENV{LIBRARY_LINKS}) + +message(STATUS "LINK_DIRS is set to: ${LINK_DIRS} ${INCLUDES_DIRS} ${LIBRARY_LINKS}") + + # Add link directories + link_directories(${LINK_DIRS}) -# LAPACK -find_package(LAPACK REQUIRED) -list(APPEND EXT_TARGETS LAPACK::LAPACK) + # Set include directories + #include_directories(${INCLUDES_DIRS}) + +else() + # OpenBLAS (MAY HAVE OpenMP enabled which slows execution) + # USE LAPACK INSTEAD TO AVOID THIS ISSUE + # set(BLA_VENDOR OpenBLAS) # MKL + # find_package(OpenBLAS REQUIRED) + # list(APPEND EXT_TARGETS OpenBLAS::OpenBLAS) + + # LAPACK + find_package(LAPACK REQUIRED) + list(APPEND EXT_TARGETS LAPACK::LAPACK) +endif() # Set compiler flags set(FLAGS_OPT $ENV{FLAGS_OPT}) # get optional user-specified flags from environment variables @@ -189,7 +205,8 @@ target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) # Build SUMMA_COMM Object add_library(SUMMA_COMM OBJECT ${COMM_ALL}) target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) -target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP ${EXT_TARGETS}) # added flags to the link step +target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES_DIRS}) +target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP ${EXT_TARGETS} ${LIBRARY_LINKS}) # added flags to the link step # For NextGen, build SUMMA Shared Library and add the outside BMI libraries if(USE_NEXTGEN) @@ -213,9 +230,10 @@ if(USE_NEXTGEN) else() add_library(summa SHARED ${SUMMA_ALL}) target_compile_options(summa PRIVATE ${FLAGS_ALL}) - target_link_libraries(summa PUBLIC ${EXT_TARGETS} SUMMA_NOAHMP SUMMA_COMM) + target_include_directories(summa PUBLIC ${INCLUDES_DIRS}) + target_link_libraries(summa PUBLIC ${EXT_TARGETS} SUMMA_NOAHMP SUMMA_COMM ${LIBRARY_LINKS}) add_executable(${EXEC_NAME} ${MAIN_SUMMA}) target_compile_options(${EXEC_NAME} PRIVATE ${FLAGS_ALL}) set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) - target_link_libraries(${EXEC_NAME} summa ${EXT_TARGETS}) # added flags to the link step + target_link_libraries(${EXEC_NAME} summa ${EXT_TARGETS} ${LIBRARY_LINKS}) # added flags to the link step endif() From f32909aed58c23b3a932c529a5074cf2b265a4ea Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 26 Sep 2024 22:54:56 +0900 Subject: [PATCH 1390/1472] need to get the -llapack or is slow --- build/CMakeLists.txt | 30 ++++++++++++++++++------------ build/cmake/build.mac.bash | 3 +++ build/cmake/build_actors.mac.bash | 3 +++ build/cmake/build_ngen.mac.bash | 3 +++ 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 755dda55e..c69ccfbca 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -92,19 +92,15 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXEC_DIR}) find_package(NetCDF REQUIRED) list(APPEND EXT_TARGETS NetCDF::NetCDF) -if (APPLE) - # Set link directories and include directories from environment variables +if(APPLE) + # Set link directories and include directories from environment variables to get most optimized lapack set(LINK_DIRS $ENV{LINK_DIRS}) set(INCLUDES_DIRS $ENV{INCLUDES_DIRS}) set(LIBRARY_LINKS $ENV{LIBRARY_LINKS}) -message(STATUS "LINK_DIRS is set to: ${LINK_DIRS} ${INCLUDES_DIRS} ${LIBRARY_LINKS}") - # Add link directories link_directories(${LINK_DIRS}) - - # Set include directories - #include_directories(${INCLUDES_DIRS}) + include_directories(${INCLUDES_DIRS}) else() # OpenBLAS (MAY HAVE OpenMP enabled which slows execution) @@ -205,8 +201,11 @@ target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) # Build SUMMA_COMM Object add_library(SUMMA_COMM OBJECT ${COMM_ALL}) target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) -target_include_directories(SUMMA_COMM PRIVATE ${INCLUDES_DIRS}) -target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP ${EXT_TARGETS} ${LIBRARY_LINKS}) # added flags to the link step +if(LIBRARY_LINKS) + target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP ${EXT_TARGETS} ${LIBRARY_LINKS}) +else() + target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP ${EXT_TARGETS}) +endif() # For NextGen, build SUMMA Shared Library and add the outside BMI libraries if(USE_NEXTGEN) @@ -230,10 +229,17 @@ if(USE_NEXTGEN) else() add_library(summa SHARED ${SUMMA_ALL}) target_compile_options(summa PRIVATE ${FLAGS_ALL}) - target_include_directories(summa PUBLIC ${INCLUDES_DIRS}) - target_link_libraries(summa PUBLIC ${EXT_TARGETS} SUMMA_NOAHMP SUMMA_COMM ${LIBRARY_LINKS}) + if(LIBRARY_LINKS) + target_link_libraries(summa PUBLIC ${EXT_TARGETS} SUMMA_NOAHMP SUMMA_COMM ${LIBRARY_LINKS}) + else() + target_link_libraries(summa PUBLIC ${EXT_TARGETS} SUMMA_NOAHMP SUMMA_COMM ) + endif() add_executable(${EXEC_NAME} ${MAIN_SUMMA}) target_compile_options(${EXEC_NAME} PRIVATE ${FLAGS_ALL}) set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) - target_link_libraries(${EXEC_NAME} summa ${EXT_TARGETS} ${LIBRARY_LINKS}) # added flags to the link step + if(LIBRARY_LINKS) + target_link_libraries(${EXEC_NAME} summa ${EXT_TARGETS} ${LIBRARY_LINKS}) + else() + target_link_libraries(${EXEC_NAME} summa ${EXT_TARGETS}) + endif() endif() diff --git a/build/cmake/build.mac.bash b/build/cmake/build.mac.bash index 1f427e420..e72d959e3 100755 --- a/build/cmake/build.mac.bash +++ b/build/cmake/build.mac.bash @@ -9,6 +9,9 @@ export FC=/opt/local/bin/gfortran # Fortran compiler #export FLAGS_OPT="-flto=1" # -flto=1 is slow to compile, but might want to use export SUNDIALS_DIR=../../../sundials/instdir/ +export LINK_DIRS=/opt/local/lib # Link directories for cmake +export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for INCLUDES \ +export LIBRARY_LINKS='-llapack' # list of library links cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON cmake --build ../cmake_build --target all -j diff --git a/build/cmake/build_actors.mac.bash b/build/cmake/build_actors.mac.bash index 0770cf5e4..2302b59fc 100755 --- a/build/cmake/build_actors.mac.bash +++ b/build/cmake/build_actors.mac.bash @@ -9,6 +9,9 @@ export FC=/opt/local/bin/gfortran # Fortran compiler #export FLAGS_OPT="-flto=1" # -flto=1 is slow to compile, but might want to use export SUNDIALS_DIR=../../../sundials/instdir/ +export LINK_DIRS=/opt/local/lib # Link directories for cmake +export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for INCLUDES \ +export LIBRARY_LINKS='-llapack' # list of library links cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DUSE_ACTORS=ON cmake --build ../cmake_build --target all -j diff --git a/build/cmake/build_ngen.mac.bash b/build/cmake/build_ngen.mac.bash index ae5aff0a7..93979515e 100755 --- a/build/cmake/build_ngen.mac.bash +++ b/build/cmake/build_ngen.mac.bash @@ -10,6 +10,9 @@ export FC=/opt/local/bin/gfortran # Fortran compiler export C_INCLUDE_PATH=/opt/local/include export CPLUS_INCLUDE_PATH=/opt/local/include export SUNDIALS_DIR=../../../sundials/instdir/ +export LINK_DIRS=/opt/local/lib # Link directories for cmake +export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for INCLUDES \ +export LIBRARY_LINKS='-llapack' # list of library links cmake -B extern/summa/cmake_build -S extern/summa -DUSE_NEXTGEN=ON cmake --build extern/summa/cmake_build --target all -j From cc523d44550aaebaaa10c2c06400fd5ce3563095 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 27 Sep 2024 07:16:53 +0900 Subject: [PATCH 1391/1472] comment in CmakeLists --- build/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index c69ccfbca..785afd2a7 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -105,6 +105,8 @@ if(APPLE) else() # OpenBLAS (MAY HAVE OpenMP enabled which slows execution) # USE LAPACK INSTEAD TO AVOID THIS ISSUE + # set($ENV{OMP_NUM_THREADS} 1) + # message("\nOpenMp number of threads (OMP_NUM_THREADS) =1, to ward off bad behavior assuming not using multithreading") # set(BLA_VENDOR OpenBLAS) # MKL # find_package(OpenBLAS REQUIRED) # list(APPEND EXT_TARGETS OpenBLAS::OpenBLAS) From 5de103d4571a02a2abcd523a1e94c53201e7cff3 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 27 Sep 2024 08:55:13 +0900 Subject: [PATCH 1392/1472] computFlux needs err quitting done after every subroutine --- build/source/engine/computFlux.f90 | 20 ++++++++++---------- build/source/engine/vegNrgFlux.f90 | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 9b53ce37d..54d32bf0c 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -223,7 +223,7 @@ subroutine computFlux(& ! initialize error control err=0; message='computFlux/' - call initialize_computFlux ! Preliminary operations to start routine + call initialize_computFlux; if(err/=0)then; return; endif ! Preliminary operations to start routine ! *** CALCULATE ENERGY FLUXES OVER VEGETATION *** associate(& @@ -235,7 +235,7 @@ subroutine computFlux(& if (doVegNrgFlux) then ! if necessary, calculate the energy fluxes over vegetation call initialize_vegNrgFlux call vegNrgFlux(in_vegNrgFlux,type_data,forc_data,mpar_data,indx_data,prog_data,diag_data,flux_data,bvar_data,model_decisions,out_vegNrgFlux) - call finalize_vegNrgFlux + call finalize_vegNrgFlux; if(err/=0)then; return; endif end if end associate @@ -244,7 +244,7 @@ subroutine computFlux(& if (nSnowSoilNrg>0) then ! if necessary, calculate energy fluxes at layer interfaces through the snow and soil domain call initialize_ssdNrgFlux call ssdNrgFlux(in_ssdNrgFlux,mpar_data,indx_data,prog_data,diag_data,flux_data,io_ssdNrgFlux,out_ssdNrgFlux) - call finalize_ssdNrgFlux + call finalize_ssdNrgFlux; if(err/=0)then; return; endif end if end associate @@ -253,7 +253,7 @@ subroutine computFlux(& if (ixVegHyd/=integerMissing) then ! if necessary, calculate liquid water fluxes through vegetation call initialize_vegLiqFlux call vegLiqFlux(in_vegLiqFlux,mpar_data,diag_data,out_vegLiqFlux) - call finalize_vegLiqFlux + call finalize_vegLiqFlux; if(err/=0)then; return; endif end if end associate @@ -262,7 +262,7 @@ subroutine computFlux(& if (nSnowOnlyHyd>0) then ! if necessary, compute liquid fluxes through snow call initialize_snowLiqFlx call snowLiqFlx(in_snowLiqFlx,indx_data,mpar_data,prog_data,diag_data,io_snowLiqFlx,out_snowLiqFlx) - call finalize_snowLiqFlx + call finalize_snowLiqFlx; if(err/=0)then; return; endif else call soilForcingNoSnow ! define forcing for the soil domain for the case of no snow layers end if @@ -273,7 +273,7 @@ subroutine computFlux(& if (nSoilOnlyHyd>0) then ! if necessary, calculate the liquid flux through soil call initialize_soilLiqFlx call soilLiqFlx(in_soilLiqFlx,mpar_data,indx_data,prog_data,diag_data,flux_data,io_soilLiqFlx,out_soilLiqFlx) - call finalize_soilLiqFlx + call finalize_soilLiqFlx; if(err/=0)then; return; endif end if end associate @@ -283,9 +283,9 @@ subroutine computFlux(& if (local_ixGroundwater/=qbaseTopmodel) then ! set baseflow fluxes to zero if the topmodel baseflow routine is not used call zeroBaseflowFluxes else ! compute the baseflow flux for topmodel-ish shallow groundwater - call initialize_groundwatr + call initialize_groundwatr; if(err/=0)then; return; endif call groundwatr(in_groundwatr,attr_data,mpar_data,prog_data,diag_data,flux_data,io_groundwatr,out_groundwatr) - call finalize_groundwatr + call finalize_groundwatr; if(err/=0)then; return; endif end if call computeBaseflowRunoff ! compute total baseflow from soil and runoff end if @@ -297,14 +297,14 @@ subroutine computFlux(& if (local_ixGroundwater==bigBucket) then ! compute fluxes for the big bucket call initialize_bigAquifer call bigAquifer(in_bigAquifer,mpar_data,diag_data,io_bigAquifer,out_bigAquifer) - call finalize_bigAquifer + call finalize_bigAquifer; if(err/=0)then; return; endif else ! if no aquifer, then fluxes are zero call zeroAquiferFluxes end if ! end check aquifer model decision end if ! if computing aquifer fluxes end associate - call finalize_computFlux ! final operations to prep for end of routine + call finalize_computFlux; if(err/=0)then; return; endif ! final operations to prep for end of routine contains diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index bfcb5ceba..81ab70cdf 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -1533,9 +1533,9 @@ subroutine aeroResist(& funcLAI = cd_CM*exposedVAI zeroPlaneDisplacement = 1.1_rkind*heightCanopyTopAboveSnow*log(1._rkind + sqrt(sqrt(funcLAI))) if (funcLAI < 0.2_rkind) then - z0Canopy = z0Ground + 0.3_rkind*heightCanopyTopAboveSnow*sqrt(funcLAI) + z0Canopy = z0Ground + 0.3_rkind*heightCanopyTopAboveSnow*sqrt(funcLAI) else - z0Canopy = 0.3_rkind*heightCanopyTopAboveSnow*(1._rkind - zeroPlaneDisplacement/heightCanopyTopAboveSnow) + z0Canopy = 0.3_rkind*heightCanopyTopAboveSnow*(1._rkind - zeroPlaneDisplacement/heightCanopyTopAboveSnow) end if ! constant parameters dependent on the vegetation type case(vegTypeTable) From 75f35b93fd48f3b71a51a1353d17cfbf63af761c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 30 Sep 2024 17:58:50 +0900 Subject: [PATCH 1393/1472] fixing mac omp threading --- build/CMakeLists.txt | 11 ++--------- build/cmake/build.mac.bash | 5 +---- build/cmake/build_actors.mac.bash | 2 -- build/cmake/build_ngen.mac.bash | 2 -- 4 files changed, 3 insertions(+), 17 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 785afd2a7..5db2e0170 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -93,20 +93,13 @@ find_package(NetCDF REQUIRED) list(APPEND EXT_TARGETS NetCDF::NetCDF) if(APPLE) - # Set link directories and include directories from environment variables to get most optimized lapack - set(LINK_DIRS $ENV{LINK_DIRS}) - set(INCLUDES_DIRS $ENV{INCLUDES_DIRS}) + # Get most optimized lapack set(LIBRARY_LINKS $ENV{LIBRARY_LINKS}) - # Add link directories - link_directories(${LINK_DIRS}) - include_directories(${INCLUDES_DIRS}) - else() # OpenBLAS (MAY HAVE OpenMP enabled which slows execution) # USE LAPACK INSTEAD TO AVOID THIS ISSUE - # set($ENV{OMP_NUM_THREADS} 1) - # message("\nOpenMp number of threads (OMP_NUM_THREADS) =1, to ward off bad behavior assuming not using multithreading") + # message("\nRun export OPENBLAS_NUM_THREADS=1, to ward off bad behavior assuming not using multithreading") # set(BLA_VENDOR OpenBLAS) # MKL # find_package(OpenBLAS REQUIRED) # list(APPEND EXT_TARGETS OpenBLAS::OpenBLAS) diff --git a/build/cmake/build.mac.bash b/build/cmake/build.mac.bash index d9343e2a0..7dc62df90 100755 --- a/build/cmake/build.mac.bash +++ b/build/cmake/build.mac.bash @@ -9,10 +9,7 @@ export FC=/opt/local/bin/gfortran # Fortran compiler #export FLAGS_OPT="-flto=1" # -flto=1 is slow to compile, but might want to use export SUNDIALS_DIR=../../../sundials/instdir/ -export LINK_DIRS=/opt/local/lib # Link directories for cmake -export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for INCLUDES \ -export LIBRARY_LINKS='-llapack' # list of library links +export LIBRARY_LINKS='-llapack' # list of library links -cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DCMAKE_BUILD_TYPE=Release cmake --build ../cmake_build --target all -j diff --git a/build/cmake/build_actors.mac.bash b/build/cmake/build_actors.mac.bash index 2302b59fc..41b95a0b2 100755 --- a/build/cmake/build_actors.mac.bash +++ b/build/cmake/build_actors.mac.bash @@ -9,8 +9,6 @@ export FC=/opt/local/bin/gfortran # Fortran compiler #export FLAGS_OPT="-flto=1" # -flto=1 is slow to compile, but might want to use export SUNDIALS_DIR=../../../sundials/instdir/ -export LINK_DIRS=/opt/local/lib # Link directories for cmake -export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for INCLUDES \ export LIBRARY_LINKS='-llapack' # list of library links cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DUSE_ACTORS=ON diff --git a/build/cmake/build_ngen.mac.bash b/build/cmake/build_ngen.mac.bash index 93979515e..42f171c89 100755 --- a/build/cmake/build_ngen.mac.bash +++ b/build/cmake/build_ngen.mac.bash @@ -10,8 +10,6 @@ export FC=/opt/local/bin/gfortran # Fortran compiler export C_INCLUDE_PATH=/opt/local/include export CPLUS_INCLUDE_PATH=/opt/local/include export SUNDIALS_DIR=../../../sundials/instdir/ -export LINK_DIRS=/opt/local/lib # Link directories for cmake -export INCLUDES_DIRS='/opt/local/include;/opt/local/lib' # directories for INCLUDES \ export LIBRARY_LINKS='-llapack' # list of library links cmake -B extern/summa/cmake_build -S extern/summa -DUSE_NEXTGEN=ON From d2534c512f13b3b4d64b15018389a09a772e08b7 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 30 Sep 2024 18:35:00 +0900 Subject: [PATCH 1394/1472] don't send out error message if negative --- build/source/engine/systemSolv.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 908f32a83..1553c1a94 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -421,7 +421,7 @@ subroutine initial_function_evaluations ! set flag and error codes for too much melt if (-volEnthalpy < flux_init%var(iLookFLUX%mLayerNrgFlux)%dat(1)*dt_cur) then tooMuchMelt = .true. - message=trim(message)//'net flux in the top snow layer can melt all the snow in the top layer' + !message=trim(message)//'net flux in the top snow layer can melt all the snow in the top layer' err=-20; return ! negative error code to denote a warning end if end if From 385e77b878425dd7b21f4a8501dc05879953e58b Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 1 Oct 2024 00:23:34 +0900 Subject: [PATCH 1395/1472] make enthalpy progressive variable so can be in restart file --- build/source/driver/summa_restart.f90 | 5 +- build/source/dshare/get_ixname.f90 | 7 +- build/source/dshare/popMetadat.f90 | 7 +- build/source/dshare/var_lookup.f90 | 11 +-- build/source/engine/check_icond.f90 | 117 ++++++++++++++------------ build/source/engine/computResid.f90 | 2 +- build/source/engine/coupled_em.f90 | 16 ++-- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/getVectorz.f90 | 6 +- build/source/engine/systemSolv.f90 | 2 +- build/source/engine/varSubstep.f90 | 6 +- build/source/netcdf/read_icond.f90 | 79 +++++++++-------- 12 files changed, 143 insertions(+), 117 deletions(-) diff --git a/build/source/driver/summa_restart.f90 b/build/source/driver/summa_restart.f90 index 9196207c5..677b4b0e0 100644 --- a/build/source/driver/summa_restart.f90 +++ b/build/source/driver/summa_restart.f90 @@ -95,8 +95,9 @@ subroutine summa_readRestart(summa1_struc, err, message) character(LEN=256) :: restartFile ! restart file name integer(i4b) :: iGRU,iHRU ! looping variables logical(lgt) :: checkEnthalpy ! flag if checking enthalpy for consistency + logical(lgt) :: no_icond_enth ! flag that enthalpy not in initial conditions logical(lgt) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use analytical solution - real(rkind) :: aquifer_start ! initial aquifer storage + real(rkind) :: aquifer_start ! initial aquifer storage ! --------------------------------------------------------------------------------------- ! associate to elements in the data structure summaVars: associate(& @@ -147,6 +148,7 @@ subroutine summa_readRestart(summa1_struc, err, message) progStruct, & ! intent(inout): model prognostic variables bvarStruct, & ! intent(inout): model basin (GRU) variables indxStruct, & ! intent(inout): model indices + no_icond_enth, & ! intent(in): flag that enthalpy not in initial conditions err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif @@ -162,6 +164,7 @@ subroutine summa_readRestart(summa1_struc, err, message) indxStruct, & ! intent(in): layer indexes lookupStruct, & ! intent(in): lookup tables checkEnthalpy, & ! intent(in): flag if need to start with consistent enthalpy + no_icond_enth, & ! intent(in): flag that enthalpy not in initial conditions use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 1145d5893..646ec6efb 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -464,6 +464,10 @@ function get_ixProg(varName) case('mLayerVolFracLiq' ); get_ixProg = iLookPROG%mLayerVolFracLiq ! volumetric fraction of liquid water in each layer (-) case('mLayerVolFracWat' ); get_ixProg = iLookPROG%mLayerVolFracWat ! volumetric fraction of total water in each layer (-) case('mLayerMatricHead' ); get_ixProg = iLookPROG%mLayerMatricHead ! matric head of water in the soil (m) + ! enthalpy + case('scalarCanairEnthalpy' ); get_ixProg = iLookPROG%scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) + case('scalarCanopyEnthalpy' ); get_ixProg = iLookPROG%scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) + case('mLayerEnthalpy' ); get_ixProg = iLookPROG%mLayerEnthalpy ! enthalpy of the snow+soil layers (J m-3) ! other state variables case('scalarAquiferStorage' ); get_ixProg = iLookPROG%scalarAquiferStorage ! relative aquifer storage -- above bottom of the soil profile (m) case('scalarSurfaceTemp' ); get_ixProg = iLookPROG%scalarSurfaceTemp ! surface temperature (K) @@ -515,11 +519,8 @@ function get_ixDiag(varName) case('mLayerThermalC' ); get_ixDiag = iLookDIAG%mLayerThermalC ! thermal conductivity at the mid-point of each layer (W m-1 K-1) case('iLayerThermalC' ); get_ixDiag = iLookDIAG%iLayerThermalC ! thermal conductivity at the interface of each layer (W m-1 K-1) ! enthalpy - case('scalarCanairEnthalpy' ); get_ixDiag = iLookDIAG%scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) case('scalarCanopyEnthTemp' ); get_ixDiag = iLookDIAG%scalarCanopyEnthTemp ! temperature component of enthalpy of the vegetation canopy (J m-3) - case('scalarCanopyEnthalpy' ); get_ixDiag = iLookDIAG%scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) case('mLayerEnthTemp' ); get_ixDiag = iLookDIAG%mLayerEnthTemp ! temperature component of enthalpy of the snow+soil layers (J m-3) - case('mLayerEnthalpy' ); get_ixDiag = iLookDIAG%mLayerEnthalpy ! enthalpy of the snow+soil layers (J m-3) case('scalarTotalSoilEnthalpy' ); get_ixDiag = iLookDIAG%scalarTotalSoilEnthalpy ! total enthalpy of the soil column (J m-3) case('scalarTotalSnowEnthalpy' ); get_ixDiag = iLookDIAG%scalarTotalSnowEnthalpy ! total enthalpy of the snow column (J m-3) ! forcing diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index ae150953e..cebd0bc7f 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -333,6 +333,10 @@ subroutine popMetadat(err,message) prog_meta(iLookPROG%mLayerVolFracLiq) = var_info('mLayerVolFracLiq' , 'volumetric fraction of liquid water in each layer' , '-' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) prog_meta(iLookPROG%mLayerVolFracWat) = var_info('mLayerVolFracWat' , 'volumetric fraction of total water in each layer' , '-' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) prog_meta(iLookPROG%mLayerMatricHead) = var_info('mLayerMatricHead' , 'matric head of water in the soil' , 'm' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) + ! enthalpy + prog_meta(iLookPROG%scalarCanairEnthalpy) = var_info('scalarCanairEnthalpy' , 'enthalpy of the canopy air space' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + prog_meta(iLookPROG%scalarCanopyEnthalpy) = var_info('scalarCanopyEnthalpy' , 'enthalpy of the vegetation canopy' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + prog_meta(iLookPROG%mLayerEnthalpy) = var_info('mLayerEnthalpy' , 'enthalpy of the snow+soil layers' , 'J m-3' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) ! other state variables prog_meta(iLookPROG%scalarAquiferStorage) = var_info('scalarAquiferStorage' , 'water required to bring aquifer to the bottom of the soil profile', 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) prog_meta(iLookPROG%scalarSurfaceTemp) = var_info('scalarSurfaceTemp' , 'surface temperature (just a copy of the upper-layer temperature)' , 'K' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) @@ -369,11 +373,8 @@ subroutine popMetadat(err,message) diag_meta(iLookDIAG%mLayerThermalC) = var_info('mLayerThermalC' , 'thermal conductivity at the mid-point of each layer' , 'W m-1 K-1' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%iLayerThermalC) = var_info('iLayerThermalC' , 'thermal conductivity at the interface of each layer' , 'W m-1 K-1' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) ! enthalpy - diag_meta(iLookDIAG%scalarCanairEnthalpy) = var_info('scalarCanairEnthalpy' , 'enthalpy of the canopy air space' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarCanopyEnthTemp) = var_info('scalarCanopyEnthTemp' , 'temperature component of enthalpy of the vegetation canopy' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%scalarCanopyEnthalpy) = var_info('scalarCanopyEnthalpy' , 'enthalpy of the vegetation canopy' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%mLayerEnthTemp) = var_info('mLayerEnthTemp' , 'temperature component of enthalpy of the snow+soil layers' , 'J m-3' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) - diag_meta(iLookDIAG%mLayerEnthalpy) = var_info('mLayerEnthalpy' , 'enthalpy of the snow+soil layers' , 'J m-3' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarTotalSoilEnthalpy) = var_info('scalarTotalSoilEnthalpy' , 'total enthalpy of the soil column' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diag_meta(iLookDIAG%scalarTotalSnowEnthalpy) = var_info('scalarTotalSnowEnthalpy' , 'total enthalpy of the snow column' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! forcing diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index fe6660336..8fee9b286 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -357,6 +357,10 @@ MODULE var_lookup integer(i4b) :: mLayerVolFracLiq = integerMissing ! volumetric fraction of liquid water in each layer (-) integer(i4b) :: mLayerVolFracWat = integerMissing ! volumetric fraction of total water in each layer (-) integer(i4b) :: mLayerMatricHead = integerMissing ! matric head of water in the soil (m) + ! enthalpy + integer(i4b) :: scalarCanairEnthalpy = integerMissing ! enthalpy of the canopy air space (J m-3) + integer(i4b) :: scalarCanopyEnthalpy = integerMissing ! enthalpy of the vegetation canopy (J m-3) + integer(i4b) :: mLayerEnthalpy = integerMissing ! enthalpy of the snow+soil layers (J m-3) ! other state variables integer(i4b) :: scalarAquiferStorage = integerMissing ! relative aquifer storage -- above bottom of the soil profile (m) integer(i4b) :: scalarSurfaceTemp = integerMissing ! surface temperature (K) @@ -396,11 +400,8 @@ MODULE var_lookup integer(i4b) :: mLayerThermalC = integerMissing ! thermal conductivity at the mid-point of each layer (W m-1 K-1) integer(i4b) :: iLayerThermalC = integerMissing ! thermal conductivity at the interface of each layer (W m-1 K-1) ! enthalpy - integer(i4b) :: scalarCanairEnthalpy = integerMissing ! enthalpy of the canopy air space (J m-3) integer(i4b) :: scalarCanopyEnthTemp = integerMissing ! temperature component of enthalpy of the vegetation canopy (J m-3) - integer(i4b) :: scalarCanopyEnthalpy = integerMissing ! enthalpy of the vegetation canopy (J m-3) integer(i4b) :: mLayerEnthTemp = integerMissing ! temperature component of enthalpy of the snow+soil layers (J m-3) - integer(i4b) :: mLayerEnthalpy = integerMissing ! enthalpy of the snow+soil layers (J m-3) integer(i4b) :: scalarTotalSoilEnthalpy = integerMissing ! total enthalpy of the soil column (J m-3) integer(i4b) :: scalarTotalSnowEnthalpy = integerMissing ! total enthalpy of the snow column (J m-3) ! forcing @@ -922,7 +923,7 @@ MODULE var_lookup ! named variables: model prognostic (state) variables type(iLook_prog), public,parameter :: iLookPROG =iLook_prog ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& - 21) + 21, 22, 23, 24) ! named variables: model diagnostic variables type(iLook_diag), public,parameter :: iLookDIAG =iLook_diag ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& @@ -935,7 +936,7 @@ MODULE var_lookup 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,& 91, 92, 93, 94, 95, 96, 97, 98, 99,100,& 101,102,103,104,105,106,107,108,109,110,& - 111,112,113,114) + 111) ! named variables: model fluxes type(iLook_flux), public,parameter :: iLookFLUX =iLook_flux ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& diff --git a/build/source/engine/check_icond.f90 b/build/source/engine/check_icond.f90 index 4833fb342..ab052a426 100644 --- a/build/source/engine/check_icond.f90 +++ b/build/source/engine/check_icond.f90 @@ -33,15 +33,16 @@ module check_icond_module ! ************************************************************************************************ ! public subroutine check_icond: read model initial conditions ! ************************************************************************************************ - subroutine check_icond(nGRU, & ! number of GRUs and HRUs - progData, & ! model prognostic (state) variables - diagData, & ! model diagnostic variables - mparData, & ! model parameters - indxData, & ! layer index data - lookupData, & ! lookup table data - checkEnthalpy, & ! flag if to check enthalpy for consistency - use_lookup, & ! flag to use the lookup table for soil enthalpy - err,message) ! error control + subroutine check_icond(nGRU, & ! intent(in): number of GRUs and HRUs + progData, & ! intent(inout): model prognostic (state) variables + diagData, & ! intent(inout): model diagnostic variables + mparData, & ! intent(in): model parameters + indxData, & ! intent(in): layer index data + lookupData, & ! intent(in): lookup table data + checkEnthalpy, & ! intent(in): flag if to check enthalpy for consistency + no_icond_enth, & ! intent(in): flag that enthalpy not in initial conditions + use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy + err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------- ! modules USE nrtype @@ -73,34 +74,35 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU ! -------------------------------------------------------------------------------------------------------- ! variable declarations ! dummies - integer(i4b),intent(in) :: nGRU ! number of grouped response units - type(gru_hru_doubleVec),intent(inout) :: diagData ! diagnostic vars - type(gru_hru_doubleVec),intent(inout) :: progData ! prognostic vars - type(gru_hru_doubleVec),intent(in) :: mparData ! parameters - type(gru_hru_intVec),intent(in) :: indxData ! layer indexes - type(gru_hru_z_vLookup),intent(in) :: lookupData ! lookup table data - logical(lgt),intent(in) :: checkEnthalpy ! if true either need enthTemp as starting residual value, or for state variable initialization - logical(lgt),intent(in) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! returned error message + integer(i4b),intent(in) :: nGRU ! number of grouped response units + type(gru_hru_doubleVec),intent(inout) :: diagData ! diagnostic vars + type(gru_hru_doubleVec),intent(inout) :: progData ! prognostic vars + type(gru_hru_doubleVec),intent(in) :: mparData ! parameters + type(gru_hru_intVec),intent(in) :: indxData ! layer indexes + type(gru_hru_z_vLookup),intent(in) :: lookupData ! lookup table data + logical(lgt),intent(in) :: checkEnthalpy ! if true either need enthTemp as starting residual value, or for state variable initialization + logical(lgt),intent(in) :: no_icond_enth ! if true, no enthalpy in icond file + logical(lgt),intent(in) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use hypergeometric function + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! returned error message ! locals - character(len=256) :: cmessage ! downstream error message - integer(i4b) :: i,iGRU,iHRU ! loop index + character(len=256) :: cmessage ! downstream error message + integer(i4b) :: i,iGRU,iHRU ! loop index ! temporary variables for realism checks - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: iSoil ! index of soil layer - real(rkind) :: fLiq ! fraction of liquid water on the vegetation canopy (-) - real(rkind) :: vGn_m ! van Genutchen "m" parameter (-) - real(rkind) :: tWat ! total water on the vegetation canopy (kg m-2) - real(rkind) :: scalarTheta ! liquid water equivalent of total water [liquid water + ice] (-) - real(rkind) :: h1,h2 ! used to check depth and height are consistent - real(rkind) :: kappa ! constant in the freezing curve function (m K-1) - integer(i4b) :: nSoil ! number of soil layers - integer(i4b) :: nSnow ! number of snow layers - integer(i4b) :: nLayers ! total number of layers - integer(i4b) :: nState ! total number of states - real(rkind),parameter :: xTol=1.e-10_rkind ! small tolerance to address precision issues - real(rkind),parameter :: canIceTol=1.e-3_rkind ! small tolerance to allow existence of canopy ice for above-freezing temperatures (kg m-2) + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: iSoil ! index of soil layer + real(rkind) :: fLiq ! fraction of liquid water on the vegetation canopy (-) + real(rkind) :: vGn_m ! van Genutchen "m" parameter (-) + real(rkind) :: tWat ! total water on the vegetation canopy (kg m-2) + real(rkind) :: scalarTheta ! liquid water equivalent of total water [liquid water + ice] (-) + real(rkind) :: h1,h2 ! used to check depth and height are consistent + real(rkind) :: kappa ! constant in the freezing curve function (m K-1) + integer(i4b) :: nSoil ! number of soil layers + integer(i4b) :: nSnow ! number of snow layers + integer(i4b) :: nLayers ! total number of layers + integer(i4b) :: nState ! total number of states + real(rkind),parameter :: xTol=1.e-10_rkind ! small tolerance to address precision issues + real(rkind),parameter :: canIceTol=1.e-3_rkind ! small tolerance to allow existence of canopy ice for above-freezing temperatures (kg m-2) ! -------------------------------------------------------------------------------------------------------- ! Start procedure here @@ -137,11 +139,11 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU associate(& ! state variables in the canopy air space scalarCanairTemp => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! canopy air temperature (K) - scalarCanairEnthalpy => diagData%gru(iGRU)%hru(iHRU)%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! canopy air enthalpy (J m-3) + scalarCanairEnthalpy => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanairEnthalpy)%dat(1) ,& ! canopy air enthalpy (J m-3) ! state variables in the vegetation canopy scalarCanopyTemp => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! canopy temperature (K) scalarCanopyEnthTemp => diagData%gru(iGRU)%hru(iHRU)%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! canopy temperature component of enthalpy (J m-3) - scalarCanopyEnthalpy => diagData%gru(iGRU)%hru(iHRU)%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! canopy enthalpy (J m-3) + scalarCanopyEnthalpy => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyEnthalpy)%dat(1) ,& ! canopy enthalpy (J m-3) scalarCanopyLiq => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! mass of liquid water on the vegetation canopy (kg m-2) scalarCanopyIce => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! mass of ice on the vegetation canopy (kg m-2) heightCanopyTop => mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%heightCanopyTop)%dat(1) ,& ! height of the top of the canopy layer (m) @@ -151,7 +153,7 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU ! state variables in the snow+soil domain mLayerTemp => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerTemp)%dat ,& ! temperature (K) mLayerEnthTemp => diagData%gru(iGRU)%hru(iHRU)%var(iLookDIAG%mLayerEnthTemp)%dat ,& ! temperature component of enthalpy (J m-3) - mLayerEnthalpy => diagData%gru(iGRU)%hru(iHRU)%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! enthalpy (J m-3) + mLayerEnthalpy => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerEnthalpy)%dat ,& ! enthalpy (J m-3) mLayerVolFracLiq => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! volumetric fraction of liquid water in each snow layer (-) mLayerVolFracIce => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracIce)%dat ,& ! volumetric fraction of ice in each snow layer (-) mLayerMatricHead => progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerMatricHead)%dat ,& ! matric head (m) @@ -181,12 +183,13 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU end if scalarTheta = scalarCanopyIce + scalarCanopyLiq - if(checkEnthalpy)then ! enthalpy as state variable (cold start often only has temperature) - call T2enthTemp_cas(& + if(checkEnthalpy)then ! enthalpy as state variable or in residual + if(no_icond_enth)then ! no enthalpy in icond file + call T2enthTemp_cas(& scalarCanairTemp, & ! intent(in): canopy air temperature (K) scalarCanairEnthalpy) ! intent(out): enthalpy of the canopy air space (J m-3) - call T2enthTemp_veg(& + call T2enthTemp_veg(& (heightCanopyTop-heightCanopyBottom), & ! intent(in): canopy depth (m) specificHeatVeg, & ! intent(in): specific heat of vegetation (J kg-1 K-1) maxMassVegetation, & ! intent(in): maximum mass of vegetation (kg m-2) @@ -194,7 +197,10 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU scalarCanopyTemp, & ! intent(in): canopy temperature (K) scalarTheta, & ! intent(in): canopy water content (kg m-2) scalarCanopyEnthTemp) ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) - scalarCanopyEnthalpy = scalarCanopyEnthTemp - LH_fus * scalarCanopyIce/ (heightCanopyTop-heightCanopyBottom) + scalarCanopyEnthalpy = scalarCanopyEnthTemp - LH_fus * scalarCanopyIce/ (heightCanopyTop-heightCanopyBottom) + else ! enthalpy is in the icond file + scalarCanopyEnthTemp = scalarCanopyEnthalpy + LH_fus * scalarCanopyIce/ (heightCanopyTop-heightCanopyBottom) + end if end if ! number of layers @@ -235,7 +241,6 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU ! ***** soil case(iname_soil) - ! (check liquid water) if(mLayerVolFracLiq(iLayer) < theta_res(iSoil)-xTol)then; write(message,'(a,1x,i0)') trim(message)//'cannot initialize the model with volumetric fraction of liquid water < theta_res: layer = ',iLayer; err=20; return; end if if(mLayerVolFracLiq(iLayer) > theta_sat(iSoil)+xTol)then; write(message,'(a,1x,i0)') trim(message)//'cannot initialize the model with volumetric fraction of liquid water > theta_sat: layer = ',iLayer; err=20; return; end if @@ -276,13 +281,17 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) - if(checkEnthalpy)then ! enthalpy as state variable (cold start often only has temperature) - call T2enthTemp_snow(& + if(checkEnthalpy)then ! enthalpy as state variable or in residual + if(no_icond_enth)then ! no enthalpy in icond file + call T2enthTemp_snow(& snowfrz_scale, & ! intent(in): scaling parameter for the snow freezing curve (K-1) mLayerTemp(iLayer), & ! intent(in): layer temperature (K) scalarTheta, & ! intent(in): volumetric total water content (-) mLayerEnthTemp(iLayer)) ! intent(out): temperature component of enthalpy of each snow layer (J m-3) - mLayerEnthalpy(iLayer) = mLayerEnthTemp(iLayer) - iden_ice * LH_fus * mLayerVolFracIce(iLayer) + mLayerEnthalpy(iLayer) = mLayerEnthTemp(iLayer) - iden_ice * LH_fus * mLayerVolFracIce(iLayer) + else + mLayerEnthTemp(iLayer) = mLayerEnthalpy(iLayer) + iden_ice * LH_fus * mLayerVolFracIce(iLayer) + end if endif ! ** soil @@ -299,8 +308,9 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(cmessage)//trim(cmessage); return; end if ! (check for errors) - if(checkEnthalpy)then ! enthalpy as state variable (cold start often only has temperature) - call T2enthTemp_soil(& + if(checkEnthalpy)then ! enthalpy as state variable or in residual + if(no_icond_enth)then ! no enthalpy in icond file + call T2enthTemp_soil(& use_lookup, & ! intent(in): flag to use the lookup table for soil enthalpy soil_dens_intr(iSoil), & ! intent(in): intrinsic soil density (kg m-3) vGn_alpha(iSoil),vGn_n(iSoil),theta_sat(iSoil),theta_res(iSoil),vGn_m, & ! intent(in): van Genutchen soil parameters @@ -310,7 +320,10 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU mLayerTemp(iLayer), & ! intent(in): layer temperature (K) mLayerMatricHead(iLayer-nSnow), & ! intent(in): matric head (m) mLayerEnthTemp(iLayer)) ! intent(out): temperature component of enthalpy soil layer (J m-3) - mLayerEnthalpy(iLayer) = mLayerEnthTemp(iLayer) - iden_water * LH_fus * mLayerVolFracIce(iLayer) + mLayerEnthalpy(iLayer) = mLayerEnthTemp(iLayer) - iden_water * LH_fus * mLayerVolFracIce(iLayer) + else + mLayerEnthTemp(iLayer) = mLayerEnthalpy(iLayer) + iden_water * LH_fus * mLayerVolFracIce(iLayer) + end if endif case default; err=10; message=trim(message)//'unknown case for model layer'; return @@ -323,9 +336,9 @@ subroutine check_icond(nGRU, & ! number of GRUs and HRU ! if snow layers exist, compute snow depth and SWE if(nSnow > 0)then - progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarSWE)%dat(1) = sum( (progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow)*iden_water + & - progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice) * & - progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) + progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%scalarSWE)%dat(1) = sum( (progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracLiq)%dat(1:nSnow)*iden_water + & + progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerVolFracIce)%dat(1:nSnow)*iden_ice) * & + progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerDepth)%dat(1:nSnow) ) end if ! if snow layers exist ! check that the layering is consistent diff --git a/build/source/engine/computResid.f90 b/build/source/engine/computResid.f90 index 3e756d675..f55889544 100644 --- a/build/source/engine/computResid.f90 +++ b/build/source/engine/computResid.f90 @@ -175,7 +175,7 @@ subroutine computResid(& mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) ! enthalpy terms - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(in): [dp] enthalpy of the canopy air space (J m-3) + scalarCanairEnthalpy => prog_data%var(iLookPROG%scalarCanairEnthalpy)%dat(1) ,& ! intent(in): [dp] enthalpy of the canopy air space (J m-3) scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! intent(in): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) mLayerEnthTemp => diag_data%var(iLookDIAG%mLayerEnthTemp)%dat ,& ! intent(in): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) ! model state variables (aquifer) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 4557756c9..44b4d4c09 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -639,7 +639,7 @@ subroutine coupled_em(& ! state variables in the vegetation canopy scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! canopy temperature (K) scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! canopy temperature component of enthalpy (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpy => prog_data%var(iLookPROG%scalarCanopyEnthalpy)%dat(1) ,& ! enthalpy of the vegetation canopy (J m-3) scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! mass of liquid water on the vegetation canopy (kg m-2) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) & ! mass of ice on the vegetation canopy (kg m-2) ) ! (associate local variables with model parameters) @@ -794,7 +794,7 @@ subroutine coupled_em(& ! variables in the snow and soil domains mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! temperature (K) mLayerEnthTemp => diag_data%var(iLookDIAG%mLayerEnthTemp)%dat ,& ! temperature component of enthalpy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! enthalpy (J m-3) + mLayerEnthalpy => prog_data%var(iLookPROG%mLayerEnthalpy)%dat ,& ! enthalpy (J m-3) mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! volumetric fraction of total water in each snow layer (-) mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! volumetric fraction of liquid water (-) mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! volumetric fraction of ice in each snow layer (-) @@ -950,7 +950,7 @@ subroutine coupled_em(& prog_data%var(iLookPROG%mLayerTemp)%dat(nSnow+1), & ! intent(in): surface layer temperature (K) mLayerMatricHead(1), & ! intent(in): surface layer matric head (m) diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(nSnow+1)) ! intent(out): temperature component of enthalpy soil layer (J m-3) - diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(nSnow+1) = diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(nSnow+1) - iden_water * LH_fus * mLayerVolFracIce(nSnow+1) + prog_data%var(iLookPROG%mLayerEnthalpy)%dat(nSnow+1) = diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(nSnow+1) - iden_water * LH_fus * mLayerVolFracIce(nSnow+1) end if ! compute the liquid water matric potential (m) @@ -1133,7 +1133,7 @@ subroutine coupled_em(& prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1), & ! intent(in): canopy temperature (K) scalarCanopyWat, & ! intent(in): canopy water content (kg m-2) diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1)) ! intent(out): temperature component of enthalpy of the vegetation canopy (J m-3) - diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) = diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) - LH_fus * scalarCanopyIce/ diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) + prog_data%var(iLookPROG%scalarCanopyEnthalpy)%dat(1) = diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) - LH_fus * scalarCanopyIce/ diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) endif end if ! (if computing the vegetation flux) @@ -1204,7 +1204,7 @@ subroutine coupled_em(& prog_data%var(iLookPROG%mLayerTemp)%dat(iLayer), & ! intent(in): layer temperature (K) mLayerVolFracWat(iLayer), & ! intent(in): volumetric total water content (-) diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(iLayer)) ! intent(out): temperature component of enthalpy of each snow layer (J m-3) - diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(iLayer) = diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(iLayer) - iden_ice * LH_fus * mLayerVolFracIce(iLayer) + prog_data%var(iLookPROG%mLayerEnthalpy)%dat(iLayer) = diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(iLayer) - iden_ice * LH_fus * mLayerVolFracIce(iLayer) end do ! looping through snow layers endif endif @@ -1358,7 +1358,7 @@ subroutine coupled_em(& prog_data%var(iLookPROG%mLayerTemp)%dat(1), & ! temperature of the top layer (K) prog_data%var(iLookPROG%mLayerVolFracWat)%dat(1), & ! intent(in): volumetric total water content (-) diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(1)) ! intent(out): temperature component of enthalpy of each snow layer (J m-3) - diag_data%var(iLookDIAG%mLayerEnthalpy)%dat(1) = diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(1) - iden_ice * LH_fus * prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1) + prog_data%var(iLookPROG%mLayerEnthalpy)%dat(1) = diag_data%var(iLookDIAG%mLayerEnthTemp)%dat(1) - iden_ice * LH_fus * prog_data%var(iLookPROG%mLayerVolFracIce)%dat(1) end if end if @@ -1421,7 +1421,7 @@ subroutine coupled_em(& scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! canopy ice content (kg m-2) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! ice content of the vegetation canopy (kg m-2) scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! temperature component of enthalpy of the vegetation canopy (K) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpy => prog_data%var(iLookPROG%scalarCanopyEnthalpy)%dat(1) ,& ! enthalpy of the vegetation canopy (J m-3) ! state variables in the snow+soil domains scalarSWE => prog_data%var(iLookPROG%scalarSWE)%dat(1) ,& ! snow water equivalent (kg m-2) mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! depth of each layer (m) @@ -1431,7 +1431,7 @@ subroutine coupled_em(& scalarTotalSoilIce => diag_data%var(iLookDIAG%scalarTotalSoilIce)%dat(1) ,& ! total ice in the soil column (kg m-2) scalarTotalSoilLiq => diag_data%var(iLookDIAG%scalarTotalSoilLiq)%dat(1) ,& ! total liquid water in the soil column (kg m-2) mLayerEnthTemp => diag_data%var(iLookDIAG%mLayerEnthTemp)%dat ,& ! temperature component of enthalpy of each snow+soil layer (K) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! enthalpy of each snow+soil layer (J m-3) + mLayerEnthalpy => prog_data%var(iLookPROG%mLayerEnthalpy)%dat ,& ! enthalpy of each snow+soil layer (J m-3) scalarTotalSoilEnthalpy => diag_data%var(iLookDIAG%scalarTotalSoilEnthalpy)%dat(1) ,& ! total enthalpy of the soil column (J m-3) scalarTotalSnowEnthalpy => diag_data%var(iLookDIAG%scalarTotalSnowEnthalpy)%dat(1) ,& ! total enthalpy of the snow column (J m-3) ! state variables in the aquifer diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index cecedf6d5..3f54882f4 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -256,7 +256,7 @@ subroutine eval8summa(& mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) ! enthalpy from the previous solution - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(in): [dp] enthalpy of the canopy air space (J m-3) + scalarCanairEnthalpy => prog_data%var(iLookPROG%scalarCanairEnthalpy)%dat(1) ,& ! intent(in): [dp] enthalpy of the canopy air space (J m-3) scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! intent(in): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) mLayerEnthTemp => diag_data%var(iLookDIAG%mLayerEnthTemp)%dat ,& ! intent(in): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) ! soil compression diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index 12c911d2e..0759bb692 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -135,14 +135,14 @@ subroutine popStateVec(& fixedLength: associate(& ! model states for the vegetation canopy scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in) : [dp] temperature of the canopy air space (K) - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(in) : [dp] enthalpy of the canopy air space (J m-3) + scalarCanairEnthalpy => prog_data%var(iLookPROG%scalarCanairEnthalpy)%dat(1) ,& ! intent(in) : [dp] enthalpy of the canopy air space (J m-3) scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in) : [dp] temperature of the vegetation canopy (K) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(in) : [dp] enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpy => prog_data%var(iLookPROG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(in) : [dp] enthalpy of the vegetation canopy (J m-3) scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in) : [dp] mass of total water on the vegetation canopy (kg m-2) scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in) : [dp] mass of liquid water on the vegetation canopy (kg m-2) ! model state variable vectors for the snow-soil layers mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in) : [dp(:)] temperature of each snow/soil layer (K) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(in) : [dp(:)] enthalpy of each snow+soil layer (J m-3) + mLayerEnthalpy => prog_data%var(iLookPROG%mLayerEnthalpy)%dat ,& ! intent(in) : [dp(:)] enthalpy of each snow+soil layer (J m-3) mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in) : [dp(:)] volumetric fraction of total water (-) mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in) : [dp(:)] volumetric fraction of liquid water (-) mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in) : [dp(:)] matric head (m) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 1553c1a94..8cb11fa6a 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -489,7 +489,7 @@ subroutine initial_flux_and_residual_vectors_prime associate(& nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) , & ! intent(in): [i4b] number of snow layers nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) , & ! intent(in): [i4b] number of soil layers - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1), & ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-2) + scalarCanopyEnthalpy => prog_data%var(iLookPROG%scalarCanopyEnthalpy)%dat(1), & ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-2) scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) , & ! intent(inout): [dp] temperature of the vegetation canopy (K) scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) , & ! intent(inout): [dp] total water content of the vegetation canopy (kg m-2) mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat , & ! intent(inout): [dp(:)] temperature of each snow/soil layer (K) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 05221beac..dd1c82762 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -788,10 +788,10 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(inout): [dp(:)] matric potential of liquid water (m) ! enthalpy - scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-3) + scalarCanairEnthalpy => prog_data%var(iLookPROG%scalarCanairEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => prog_data%var(iLookPROG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-3) scalarCanopyEnthTemp => diag_data%var(iLookDIAG%scalarCanopyEnthTemp)%dat(1) ,& ! intent(inout): [dp] temperature component of enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(inout): [dp(:)] enthalpy of the snow+soil layers (J m-3) + mLayerEnthalpy => prog_data%var(iLookPROG%mLayerEnthalpy)%dat ,& ! intent(inout): [dp(:)] enthalpy of the snow+soil layers (J m-3) mLayerEnthTemp => diag_data%var(iLookDIAG%mLayerEnthTemp)%dat ,& ! intent(inout): [dp(:)] temperature component of enthalpy of the snow+soil layers (J m-3) ! model state variables (aquifer) scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(inout): [dp(:)] storage of water in the aquifer (m) diff --git a/build/source/netcdf/read_icond.f90 b/build/source/netcdf/read_icond.f90 index 5b60bf47d..bc96f2e3e 100644 --- a/build/source/netcdf/read_icond.f90 +++ b/build/source/netcdf/read_icond.f90 @@ -142,6 +142,7 @@ subroutine read_icond(iconFile, & ! intent(in): name of progData, & ! intent(inout): model prognostic variables bvarData, & ! intent(inout): model basin (GRU) variables indxData, & ! intent(inout): model indices + no_icond_enth, & ! intent(out): flag that enthalpy variables are not in the file err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------- ! modules @@ -171,41 +172,42 @@ subroutine read_icond(iconFile, & ! intent(in): name of ! -------------------------------------------------------------------------------------------------------- ! variable declarations ! dummies - character(*) ,intent(in) :: iconFile ! name of netcdf file containing the initial conditions - integer(i4b) ,intent(in) :: nGRU ! number of grouped response units in simulation domain - type(gru_hru_doubleVec),intent(in) :: mparData ! model parameters - type(gru_hru_doubleVec),intent(inout) :: progData ! model prognostic variables - type(gru_doubleVec) ,intent(inout) :: bvarData ! model basin (GRU) variables - type(gru_hru_intVec) ,intent(inout) :: indxData ! model indices - integer(i4b) ,intent(out) :: err ! error code - character(*) ,intent(out) :: message ! returned error message + character(*) ,intent(in) :: iconFile ! name of netcdf file containing the initial conditions + integer(i4b) ,intent(in) :: nGRU ! number of grouped response units in simulation domain + type(gru_hru_doubleVec),intent(in) :: mparData ! model parameters + type(gru_hru_doubleVec),intent(inout) :: progData ! model prognostic variables + type(gru_doubleVec) ,intent(inout) :: bvarData ! model basin (GRU) variables + type(gru_hru_intVec) ,intent(inout) :: indxData ! model indices + logical ,intent(out) :: no_icond_enth ! flag that enthalpy variables are not in the file + integer(i4b) ,intent(out) :: err ! error code + character(*) ,intent(out) :: message ! returned error message ! locals - character(len=256) :: cmessage ! downstream error message - integer(i4b) :: fileHRU ! number of HRUs in file - integer(i4b) :: fileGRU ! number of GRUs in file - integer(i4b) :: iVar, i ! loop indices - integer(i4b),dimension(1) :: ndx ! intermediate array of loop indices - integer(i4b) :: iGRU ! loop index - integer(i4b) :: iHRU ! loop index - integer(i4b) :: dimID ! varible dimension ids - integer(i4b) :: ncVarID ! variable ID in netcdf file - character(256) :: dimName ! not used except as a placeholder in call to inq_dim function - integer(i4b) :: dimLen ! data dimensions - integer(i4b) :: ncID ! netcdf file ID - integer(i4b) :: ixFile ! index in file - integer(i4b) :: iHRU_local ! index of HRU in the data subset - integer(i4b) :: iHRU_global ! index of HRU in the netcdf file - real(rkind),allocatable :: varData(:,:) ! variable data storage - integer(i4b) :: nSoil, nSnow, nToto ! # layers - integer(i4b) :: nTDH ! number of points in time-delay histogram - integer(i4b) :: iLayer,jLayer ! layer indices - integer(i4b),parameter :: nBand=2 ! number of spectral bands - integer(i4b) :: nProgVars ! number of prognostic variables written to state file - character(len=32),parameter :: scalDimName ='scalarv' ! dimension name for scalar data - character(len=32),parameter :: midSoilDimName='midSoil' ! dimension name for soil-only layers - character(len=32),parameter :: midTotoDimName='midToto' ! dimension name for layered varaiables - character(len=32),parameter :: ifcTotoDimName='ifcToto' ! dimension name for layered varaiables - character(len=32),parameter :: tdhDimName ='tdh' ! dimension name for time-delay basin variables + character(len=256) :: cmessage ! downstream error message + integer(i4b) :: fileHRU ! number of HRUs in file + integer(i4b) :: fileGRU ! number of GRUs in file + integer(i4b) :: iVar, i ! loop indices + integer(i4b),dimension(1) :: ndx ! intermediate array of loop indices + integer(i4b) :: iGRU ! loop index + integer(i4b) :: iHRU ! loop index + integer(i4b) :: dimID ! varible dimension ids + integer(i4b) :: ncVarID ! variable ID in netcdf file + character(256) :: dimName ! not used except as a placeholder in call to inq_dim function + integer(i4b) :: dimLen ! data dimensions + integer(i4b) :: ncID ! netcdf file ID + integer(i4b) :: ixFile ! index in file + integer(i4b) :: iHRU_local ! index of HRU in the data subset + integer(i4b) :: iHRU_global ! index of HRU in the netcdf file + real(rkind),allocatable :: varData(:,:) ! variable data storage + integer(i4b) :: nSoil, nSnow, nToto ! # layers + integer(i4b) :: nTDH ! number of points in time-delay histogram + integer(i4b) :: iLayer,jLayer ! layer indices + integer(i4b),parameter :: nBand=2 ! number of spectral bands + integer(i4b) :: nProgVars ! number of prognostic variables written to state file + character(len=32),parameter :: scalDimName ='scalarv' ! dimension name for scalar data + character(len=32),parameter :: midSoilDimName='midSoil' ! dimension name for soil-only layers + character(len=32),parameter :: midTotoDimName='midToto' ! dimension name for layered varaiables + character(len=32),parameter :: ifcTotoDimName='ifcToto' ! dimension name for layered varaiables + character(len=32),parameter :: tdhDimName ='tdh' ! dimension name for time-delay basin variables ! -------------------------------------------------------------------------------------------------------- ! Start procedure here @@ -223,6 +225,7 @@ subroutine read_icond(iconFile, & ! intent(in): name of err = nf90_inquire_dimension(ncID,dimID,len=fileHRU); if(err/=nf90_noerr)then; message=trim(message)//'problem reading hru dimension/'//trim(nf90_strerror(err)); return; end if ! loop through prognostic variables + no_icond_enth=.false. do iVar = 1,size(prog_meta) ! skip variables that are computed later @@ -233,8 +236,12 @@ subroutine read_icond(iconFile, & ! intent(in): name of prog_meta(iVar)%varName=='mLayerHeight' ) cycle ! get variable id - err = nf90_inq_varid(ncID,trim(prog_meta(iVar)%varName),ncVarID); call netcdf_err(err,message) - if(err/=0)then + err = nf90_inq_varid(ncID,trim(prog_meta(iVar)%varName),ncVarID) + if(err/=nf90_noerr)then + if(prog_meta(iVar)%varName=='scalarCanairEnthalpy' .or. & + prog_meta(iVar)%varName=='scalarCanopyEnthalpy' .or. & + prog_meta(iVar)%varName=='mLayerEnthalpy' ) err=nf90_noerr; no_icond_enth=.true.; cycle ! skip enthalpy variables if not in file + call netcdf_err(err,message) message=trim(message)//': problem with getting variable id, var='//trim(prog_meta(iVar)%varName) return endif From b40d091e8b77916c32d3eaa4bf86ee75538f38ea Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 1 Oct 2024 01:29:31 +0900 Subject: [PATCH 1396/1472] Fix bug with soilRelHumidity going more than 1 --- build/source/engine/vegNrgFlux.f90 | 35 +++++++++++++++++------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 81ab70cdf..8e101b291 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -808,7 +808,12 @@ subroutine vegNrgFlux(& !scalarSoilResistance = scalarGroundSnowFraction*0._rkind + (1._rkind - scalarGroundSnowFraction)*exp(8.25_rkind - 6.0_rkind*soilEvapFactor) ! Niu adjustment to decrease resitance for wet soil ! relative humidity in the soil pores [0-1] if (mLayerMatricHead(1) > -1.e+6_rkind) then ! avoid problems with numerical precision when soil is very dry - soilRelHumidity_noSnow = exp( (mLayerMatricHead(1)*gravity) / (groundTempTrial*R_wv) ) + if (groundTempTrial /= 0._rkind) then + soilRelHumidity_noSnow = exp( (mLayerMatricHead(1)*gravity) / (groundTempTrial*R_wv) ) + if (soilRelHumidity_noSnow > 1._rkind) then; soilRelHumidity_noSnow = 1._rkind; end if + else + soilRelHumidity_noSnow = 1._rkind + end if ! end if ground temperature is positive else soilRelHumidity_noSnow = 0._rkind end if ! end if matric head is very low @@ -1791,25 +1796,25 @@ subroutine soilResist(& aquiferTranspireLimitFac, & ! intent(out): transpiration limiting factor for the aquifer (-) err,message) ! intent(out): error control ! ----------------------------------------------------------------------------------------------------------------------------------------- - USE mDecisions_module, only: NoahType,CLM_Type,SiB_Type ! options for the choice of function for the soil moisture control on stomatal resistance - USE mDecisions_module, only: bigBucket ! named variable that defines the "bigBucket" groundwater parameterization + USE mDecisions_module, only: NoahType,CLM_Type,SiB_Type ! options for the choice of function for the soil moisture control on stomatal resistance + USE mDecisions_module, only: bigBucket ! named variable that defines the "bigBucket" groundwater parameterization implicit none ! input (model decisions) - integer(i4b),intent(in) :: ixSoilResist ! choice of function for the soil moisture control on stomatal resistance - integer(i4b),intent(in) :: ixGroundwater ! choice of groundwater representation + integer(i4b),intent(in) :: ixSoilResist ! choice of function for the soil moisture control on stomatal resistance + integer(i4b),intent(in) :: ixGroundwater ! choice of groundwater representation ! input (variables) - real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head in each layer (m) - real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid water in each layer (-) - real(rkind),intent(in) :: scalarAquiferStorage ! aquifer storage (m) + real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head in each layer (m) + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid water in each layer (-) + real(rkind),intent(in) :: scalarAquiferStorage ! aquifer storage (m) ! input (diagnostic variables) - real(rkind),intent(in) :: mLayerRootDensity(:) ! root density in each layer (-) - real(rkind),intent(in) :: scalarAquiferRootFrac ! fraction of roots below the lowest unsaturated layer (-) + real(rkind),intent(in) :: mLayerRootDensity(:) ! root density in each layer (-) + real(rkind),intent(in) :: scalarAquiferRootFrac ! fraction of roots below the lowest unsaturated layer (-) ! input (parameters) - real(rkind),intent(in) :: plantWiltPsi ! matric head at wilting point (m) - real(rkind),intent(in) :: soilStressParam ! parameter in the exponential soil stress function (-) - real(rkind),intent(in) :: critSoilWilting ! critical vol. liq. water content when plants are wilting (-) - real(rkind),intent(in) :: critSoilTranspire ! critical vol. liq. water content when transpiration is limited (-) - real(rkind),intent(in) :: critAquiferTranspire ! critical aquifer storage value when transpiration is limited (m) + real(rkind),intent(in) :: plantWiltPsi ! matric head at wilting point (m) + real(rkind),intent(in) :: soilStressParam ! parameter in the exponential soil stress function (-) + real(rkind),intent(in) :: critSoilWilting ! critical vol. liq. water content when plants are wilting (-) + real(rkind),intent(in) :: critSoilTranspire ! critical vol. liq. water content when transpiration is limited (-) + real(rkind),intent(in) :: critAquiferTranspire ! critical aquifer storage value when transpiration is limited (m) ! output real(rkind),intent(out) :: wAvgTranspireLimitFac ! intent(out): weighted average of the transpiration limiting factor (-) real(rkind),intent(out) :: mLayerTranspireLimitFac(:) ! intent(out): transpiration limiting factor in each layer (-) From 06380e91d70e5721b67f23f76c32636f3efb85c8 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 1 Oct 2024 10:17:48 +0900 Subject: [PATCH 1397/1472] fix scalarSoilRelHumidity for cold --- build/source/engine/vegNrgFlux.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 8e101b291..61071342a 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -808,7 +808,7 @@ subroutine vegNrgFlux(& !scalarSoilResistance = scalarGroundSnowFraction*0._rkind + (1._rkind - scalarGroundSnowFraction)*exp(8.25_rkind - 6.0_rkind*soilEvapFactor) ! Niu adjustment to decrease resitance for wet soil ! relative humidity in the soil pores [0-1] if (mLayerMatricHead(1) > -1.e+6_rkind) then ! avoid problems with numerical precision when soil is very dry - if (groundTempTrial /= 0._rkind) then + if (groundTempTrial < 0._rkind) then soilRelHumidity_noSnow = exp( (mLayerMatricHead(1)*gravity) / (groundTempTrial*R_wv) ) if (soilRelHumidity_noSnow > 1._rkind) then; soilRelHumidity_noSnow = 1._rkind; end if else From fd4f5b85e4d7c4d8ae1a137577ff2a4ca34b0049 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 1 Oct 2024 23:30:30 +0900 Subject: [PATCH 1398/1472] utils to include enthalpy form --- utils/post-processing/hist_per_GRU.py | 26 +++++--- utils/post-processing/largest_error_attrib.py | 65 ++++++++++++------- utils/post-processing/plot_per_GRUMult.py | 28 ++++---- utils/post-processing/plot_per_GRUMultBal.py | 12 ++-- utils/post-processing/scat_per_GRU.py | 18 +++-- 5 files changed, 84 insertions(+), 65 deletions(-) diff --git a/utils/post-processing/hist_per_GRU.py b/utils/post-processing/hist_per_GRU.py index 57a3bffcb..dd559daef 100644 --- a/utils/post-processing/hist_per_GRU.py +++ b/utils/post-processing/hist_per_GRU.py @@ -38,8 +38,8 @@ #plt_name=['BE1','IDAe-4','BE4','BE8','BE16','BE32','IDAe-6'] #maybe make this an argument #method_name=['be1','be16','be32','sundials_1en6'] #maybe make this an argument #plt_name=['BE1','BE16','BE32','SUNDIALS'] #maybe make this an argument -method_name=['be1','be1cm','be1en','sundials_1en6cm'] -plt_name=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp'] +method_name=['be1','be1cm','be1en','sundials_1en6cm','sundials_1en6en'] +plt_name=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp', 'SUNDIALS enth'] method_name2=method_name+['sundials_1en8cm'] plt_name2=plt_name+['reference solution'] @@ -52,17 +52,23 @@ def power_transform(x): return x ** 0.5 # Adjust the exponent as needed # Simulation statistics file locations -use_vars = [] -rep = [] # mark the repeats -#use_vars = [1] -#rep = [0] # mark the repeats +#use_vars = [] +#rep = [] # mark the repeats +use_vars = [1] +rep = [0] # mark the repeats +#use_vars = [0,1,2,3,4,5] +#rep = [0,0,0,0,0,0] # mark the repeats settings0= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] settings = [settings0[i] for i in use_vars] -use_vars2 = [0,0,1,1,2,2] -rep2 = [1,2,1,2,1,2] # mark the repeats -#use_vars2 = [8,3,3] -#rep2 = [0,1,2] # mark the repeats +#use_vars2 = [0,0,1,1,2,2] +#rep2 = [1,2,1,2,1,2] # mark the repeats +#use_vars2 = [4,4,5,5,6,6,7,7] +#rep2 = [1,2,1,2,1,2,1,2] # mark the repeats +use_vars2 = [8,3,3] +rep2 = [0,1,2] # mark the repeats +#use_vars2 = [] +#rep2 = [] # mark the repeats settings20= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime'] settings2 = [settings20[i] for i in use_vars2] diff --git a/utils/post-processing/largest_error_attrib.py b/utils/post-processing/largest_error_attrib.py index 11921804b..6ee874d61 100644 --- a/utils/post-processing/largest_error_attrib.py +++ b/utils/post-processing/largest_error_attrib.py @@ -7,14 +7,15 @@ from pathlib import Path nBig = 10 -do_rel = True # plot relative to the benchmark simulation +do_rel = True # stat relative to the benchmark simulation +do_var = False # do vars, if False do bals -run_local = False +run_local = True if run_local: top_fold = '/Users/amedin/Research/USask/test_py/' attr_fold = '/Users/amedin/Research/USask/test_py/settings/' - method_name= 'be1' - stat = 'rmnz' + method_name= 'sundials_1en6cm' + stat = 'mean' else: import sys top_fold = '/home/avanb/scratch/' @@ -22,36 +23,51 @@ method_name = sys.argv[1] stat = sys.argv[2] -des_dir = top_fold + 'statistics' +des_dir = top_fold + 'statistics_en' des_dir = Path(des_dir) -settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] -viz_fil = method_name + '_hrly_diff_stats_{}.nc' -viz_fil = viz_fil.format(','.join(settings)) -src_file = des_dir / viz_fil -plot_vars = settings.copy() -short_name= ['SWE ', - 'soilWat ', - 'ET ', - 'canWat ', - 'runoff '] +if do_var: + settings= ['scalarSWE','scalarTotalSoilWat','scalarTotalET','scalarCanopyWat','averageRoutedRunoff','wallClockTime'] + viz_fil = method_name + '_hrly_diff_stats_{}.nc' + viz_fil = viz_fil.format(','.join(settings)) + src_file = des_dir / viz_fil + plot_vars = settings.copy() + short_name= ['SWE ', + 'soilWat ', + 'ET ', + 'canWat ', + 'runoff '] +else: + do_rel = False + settings= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime'] + viz_fil = method_name + '_hrly_diff_bals_balance.nc' + src_file = des_dir / viz_fil + plot_vars = settings.copy() + short_name= ['casNrg ', + 'vegNrg ', + 'snowNrg ', + 'soilNrg ', + 'vegMass ', + 'snowMass', + 'soilMass', + 'aqMass '] attr_fil = Path(attr_fold) / 'attributes.nc' # Open the netCDF file with RMSE data summa = xr.open_dataset(src_file) -if stat == 'rmse' or stat == 'kgem': statr = 'mean_ben' -if stat == 'rmnz': statr = 'mnnz_ben' -if stat == 'maxe': statr = 'amax_ben' +if stat == 'rmse' or stat == 'kgem' or stat == 'mean': statr = 'mean_ben' +if stat == 'rmnz' or stat == 'mnnz': statr = 'mnnz_ben' +if stat == 'maxe' or stat == 'amax': statr = 'amax_ben' for var in plot_vars: # Get the variable from the netCDF file stat0 = stat if var == 'wallClockTime': - if stat == 'rmse' or stat == 'kgem': stat0 = 'mean' - if stat == 'rmnz': stat0 = 'mnnz' - if stat == 'maxe': stat0 = 'amax' + if stat == 'rmse' or stat == 'kgem' or stat == 'mean': stat0 = 'mean' + if stat == 'rmnz' or stat == 'mnnz': stat0 = 'mnnz' + if stat == 'maxe' or stat == 'amax': stat0 = 'amax' s = summa[var].sel(stat=stat0) if do_rel: s_rel = summa[var].sel(stat=statr) @@ -83,9 +99,9 @@ for i,var0 in enumerate(plot_vars[:-1]): print(f"{short_name[i]}: [{' '.join(f'{val:8.1e}' for val in raw_vals[var0].values)}]") var0 = 'wallClockTime' - if stat == 'rmse' or stat == 'kgem': stat00 = 'mean' - if stat == 'rmnz': stat00 = 'mnnz' - if stat == 'maxe': stat00 = 'amax' + if stat == 'rmse' or stat == 'kgem' or stat == 'mean': stat00 = 'mean' + if stat == 'rmnz' or stat == 'mnnz': stat00 = 'mnnz' + if stat == 'maxe' or stat == 'amax': stat00 = 'amax' raw_vals = summa.sel(stat=stat00, hru=hru_big) print("wall"f"{stat00}: [{' '.join(f'{val:8.1e}' for val in raw_vals[var0].values)}]") @@ -108,7 +124,6 @@ lon_big = attr['longitude'][mask].values[h_ind] # Print the attributes of the largest nBig values - print(" hryhhh : [", " ".join([f"{val:8d}" for val in h]), "]", sep="") print("HRU vals: [", " ".join([f"{val:8d}" for val in hru_big]), "]", sep="") print("vegType : [", " ".join([f"{val:8d}" for val in vegType_big]), "]", sep="") print("latitude: [", " ".join([f"{val:8.2f}" for val in lat_big]), "]", sep="") diff --git a/utils/post-processing/plot_per_GRUMult.py b/utils/post-processing/plot_per_GRUMult.py index c92b90cc0..311bb9b2e 100644 --- a/utils/post-processing/plot_per_GRUMult.py +++ b/utils/post-processing/plot_per_GRUMult.py @@ -33,10 +33,10 @@ from matplotlib.ticker import ScalarFormatter -do_rel = False # true is plot relative to the benchmark simulation -one_plot = True # true is one plot, false is multiple plots (one per variable) +do_rel = True # true is plot relative to the benchmark simulation +one_plot = False # true is one plot, false is multiple plots (one per variable) run_local = False # true is run on local machine (only does testing), false is run on cluster -more_mean = True # true is plot mean/amax extra variables in a balance file +more_mean = False # true is plot mean/amax extra variables in a balance file if run_local: stat = 'mean' @@ -53,9 +53,9 @@ method_name=['be1','be16','be32','sundials_1en6','ref'] plt_name0=['SUMMA-BE1','SUMMA-BE16','SUMMA-BE32','SUMMA-SUNDIALS','reference solution'] plt_nameshort=plt_name0 -method_name=['be1','be1cm','be1en','sundials_1en6cm','diff','ref'] -plt_name0=['BE1 common thermo. eq..','SUMMA-BE1 temperature thermo. eq..','SUMMA-BE1 mixed thermo. eq..','SUMMA-SUNDIALS temperature thermo. eq..','SUMMA-BE1 common - mixed','reference solution'] -plt_nameshort=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp','BE1 common - mixed','reference soln'] +method_name=['be1','be1cm','be1en','sundials_1en6cm','sundials_1en6en','diff','ref'] +plt_name0=['BE1 common thermo. eq.','SUMMA-BE1 temperature thermo. eq.','SUMMA-BE1 mixed thermo. eq.','SUMMA-SUNDIALS temperature thermo. eq.','SUMMA-SUNDIALS enthalpy thermo. eq.','SUMMA-BE1 common - mixed','reference solution'] +plt_nameshort=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp','SUNDIALS enth','BE1 common - mixed','reference soln'] if one_plot: plt_name0 = plt_nameshort @@ -74,11 +74,10 @@ if more_mean: # extra vars in a balance file plt_titl_exVar = ['rain plus melt','top 4m soil temperature','air temperature','snow water equivalent'] - #plot_vars_exVar = ['balanceCasNrg','balanceSoilNrg','balanceVegNrg','balanceSnowNrg'] + plot_vars_exVar = ['scalarRainPlusMelt','scalarRootZoneTemp','airtemp','scalarSWE'] viz_file_exVar = 'exVar_hrly_diff_bals_balance.nc' - plt_name0_exVar = 'SUMMA-BE1 temperature thermo. eq..' + plt_name0_exVar = 'SUMMA-BE1 temperature thermo. eq.' plt_nameshort_exVar = 'BE1 temp' # identify method here - plt_titl_exVar = ['rain plus melt','root zone temperature','air temperature','snow water equivalent'] leg_titl_exVar = ['$mm~y^{-1}$','$K$','$K$','$kg~m^{-2}$'] maxes_exVar = [3000,290,290,100] if one_plot: plt_name0_exVar = plt_nameshort_exVar @@ -397,6 +396,7 @@ def run_loop(j,var,the_max): else: # will be wonky with m=='diff' choice cbr = fig.colorbar(sm, ax=axs_list,aspect=27/3*nrow) + if m=='diff': cbr2 = fig.colorbar(sm2, ax=axs_list,aspect=27/3*nrow) if stat == 'kgem': cbr.ax.set_ylabel(stat_word0) else: @@ -478,7 +478,7 @@ def format_tick(value, tick_number): else: use_vars = [0,1,2,3,4,5] use_vars = [1,5] - use_meth = [0,1,2,3] + use_meth = [0,1,2,3,4,6] use_vars_exVar = [3,0,2,1] if more_mean: use_vars = ['exVar'] + use_vars # 'exVar' is the extra variables in a balance file, all same method @@ -520,11 +520,11 @@ def format_tick(value, tick_number): plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines else: - #size hardwired to 2x2 for now + #size hardwired to 3x2 for now ncol = 2 - nrow = 2 - if len(method_name)>4: - print('Too many methods for 2x2 plot') + nrow = 3 + if len(method_name)>6: + print('Too many methods for 3x2 plot') sys.exit() base_row = 0 diff --git a/utils/post-processing/plot_per_GRUMultBal.py b/utils/post-processing/plot_per_GRUMultBal.py index 0d8648e13..39a021797 100644 --- a/utils/post-processing/plot_per_GRUMultBal.py +++ b/utils/post-processing/plot_per_GRUMultBal.py @@ -45,8 +45,8 @@ viz_dir = Path('/home/avanb/scratch/statistics') -method_name=['be1','be1cm','be1en','sundials_1en6cm','sundials_1en8cm'] #maybe make this an argument -plt_name0=['SUMMA-BE1 common thermo. eq..','SUMMA-BE1 temperature thermo. eq..','SUMMA-BE1 mixed thermo. eq..','SUMMA-SUNDIALS temperature thermo. eq..','reference solution'] +method_name=['be1','be1cm','be1en','sundials_1en6cm','sundials_1en6en','sundials_1en8cm'] #maybe make this an argument +plt_name0=['SUMMA-BE1 common thermo. eq.','SUMMA-BE1 temperature thermo. eq.','SUMMA-BE1 mixed thermo. eq.','SUMMA-SUNDIALS temperature thermo. eq.','SUMMA-SUNDIALS enthalpy thermo. eq.','reference solution'] # Simulation statistics file locations settings= ['balanceCasNrg','balanceVegNrg','balanceSnowNrg','balanceSoilNrg','balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','wallClockTime'] @@ -259,7 +259,7 @@ def run_loop(j,var,the_max): else: use_vars = [0,1,2,3,4,5,6,7] use_vars = [3] - use_meth = [0,1,2,3] + use_meth = [0,1,2,3,4,5] plot_vars = [plot_vars[i] for i in use_vars] plt_titl = [plt_titl[i] for i in use_vars] leg_titl = [leg_titl[i] for i in use_vars] @@ -285,9 +285,9 @@ def run_loop(j,var,the_max): else: #size hardwired to 2x2 for now ncol = 2 - nrow = 2 - if len(method_name)>4: - print('Too many methods for 2x2 plot') + nrow = 3 + if len(method_name)>6: + print('Too many methods for 3x2 plot') sys.exit() base_row = 0 diff --git a/utils/post-processing/scat_per_GRU.py b/utils/post-processing/scat_per_GRU.py index 5daf64ec5..50c42a96c 100644 --- a/utils/post-processing/scat_per_GRU.py +++ b/utils/post-processing/scat_per_GRU.py @@ -23,15 +23,14 @@ from matplotlib.colors import ListedColormap do_rel = False # true is plot relative to the benchmark simulation -do_heat = True # true is plot heatmaps instead of scatterplots +do_heat = False # true is plot heatmaps instead of scatterplots run_local = True # true is run on local machine, false is run on cluster inferno_col= True # Set to True if want to match geographic plots, False if want rainbow colormap (does not matter if do_heat is False) fixed_Mass_units = False # true is convert mass balance units to kg m-2 s-1, if ran new code with depth in calculation - # which statistics to plot, can do both -do_vars = True -do_balance = False +do_vars = False +do_balance = True if run_local: stat = 'mnnz' @@ -46,8 +45,8 @@ #plt_name=['BE1','IDAe-4','BE4','BE8','BE16','BE32','IDAe-6'] method_name=['be1','be16','be32','sundials_1en6'] plt_name=['BE1','BE16','BE32','SUNDIALS'] -method_name=['be1','be1cm','be1en','sundials_1en6cm'] -plt_name=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp'] +method_name=['be1','be1cm','be1en','sundials_1en6cm','sundials_1en6en'] +plt_name=['BE1 common','BE1 temp','BE1 mixed','SUNDIALS temp','SUNDIALS enth'] method_name2=method_name+['sundials_1en8cm'] plt_name2=plt_name+['reference solution'] @@ -323,10 +322,10 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): method_name2 = np.copy(method_name20) plt_name2 = np.copy(plt_name20) - if stat == 'rmse' or stat == 'kgem': + if stat == 'rmse' or stat == 'kgem' or stat == 'mean': stat0 = 'mean' wordx = ' mean' - if stat == 'rmnz': + if stat == 'rmnz' or stat == 'mnnz': stat0 = 'mean' wordx = ' mean' # no 0s' if stat == 'maxe': @@ -545,7 +544,7 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): logy = np.ones(len(use_vars)) # log scale y axis else: use_vars = [0,1,2,3] - use_meth = [0,1,2,3,4] + use_meth = [0,1,2,3,4,5] logx = np.zeros(len(use_vars)) # no log scale x axis logx = np.ones(len(use_vars)) # log scale x axis logy = np.ones(len(use_vars)) # log scale y axis @@ -564,7 +563,6 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): leg_titl0 =['$kg~m^{-2}~s^{-1}$'] * 4 + ['$num$'] if fixed_Mass_units: leg_titl0 = ['s^{-1}$'] * 3 + ['m~s^{-1}$'] + ['$num$'] - plot_vars = [plot_vars[i] for i in use_vars] comp_vars = [comp_vars[i] for i in use_vars] plt_titl = [f"({chr(97+n)}) {plt_titl[i]}" for n,i in enumerate(use_vars)] From ff83e896d725dd22c10a5049ff33b0c44bb2f952 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 2 Oct 2024 00:43:43 +0900 Subject: [PATCH 1399/1472] utils should be on kg/ m3 /s now --- utils/post-processing/hist_per_GRU.py | 2 +- utils/post-processing/plot_per_GRUMultBal.py | 2 +- utils/post-processing/scat_per_GRU.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/post-processing/hist_per_GRU.py b/utils/post-processing/hist_per_GRU.py index dd559daef..b0963d29d 100644 --- a/utils/post-processing/hist_per_GRU.py +++ b/utils/post-processing/hist_per_GRU.py @@ -90,7 +90,7 @@ def power_transform(x): plot_vars2 = settings2.copy() plt_titl2 = ['canopy air space enthalpy balance','vegetation enthalpy balance','snow enthalpy balance','soil enthalpy balance','vegetation mass balance','snow mass balance','soil mass balance','aquifer mass balance', 'wall clock time'] -leg_titl2 = ['$W~m^{-3}$'] * 4 + ['$kg~m^{-2}~s^{-1}$'] * 4 + ['$s$'] +leg_titl2 = ['$W~m^{-3}$'] * 4 + ['$kg~m^{-3}~s^{-1}$'] * 4 + ['$s$'] if fixed_Mass_units: leg_titl2 = ['$W~m^{-3}$'] * 4 + ['s^{-1}$'] * 3 + ['m~s^{-1}$'] + ['$s$'] plt_titl2 = [f"({chr(97+n + len(use_vars))}) {plt_titl2[i]}" for n,i in enumerate(use_vars2)] leg_titl2 = [leg_titl2[i] for i in use_vars2] diff --git a/utils/post-processing/plot_per_GRUMultBal.py b/utils/post-processing/plot_per_GRUMultBal.py index 39a021797..001d66c1e 100644 --- a/utils/post-processing/plot_per_GRUMultBal.py +++ b/utils/post-processing/plot_per_GRUMultBal.py @@ -59,7 +59,7 @@ # Specify variables in files plt_titl = ['canopy air space enthalpy balance','vegetation enthalpy balance','snow enthalpy balance','soil enthalpy balance','vegetation mass balance','snow mass balance','soil mass balance','aquifer mass balance', 'wall clock time'] -leg_titl = ['$W~m^{-3}$'] * 4 + ['$kg~m^{-2}~s^{-1}$'] * 4 + ['$s$'] +leg_titl = ['$W~m^{-3}$'] * 4 + ['$kg~m^{-3}~s^{-1}$'] * 4 + ['$s$'] if fixed_Mass_units: leg_titl = ['$W~m^{-3}$'] * 4 + ['s^{-1}$'] * 3 + ['m~s^{-1}$'] + ['$s$'] fig_fil= '_hrly_balance_{}_compressed.png' diff --git a/utils/post-processing/scat_per_GRU.py b/utils/post-processing/scat_per_GRU.py index 50c42a96c..f5537869b 100644 --- a/utils/post-processing/scat_per_GRU.py +++ b/utils/post-processing/scat_per_GRU.py @@ -560,7 +560,7 @@ def run_loopb(i,var,comp,lx,ly,leg_t,leg_t0,plt_t,repy): comp_vars = ['balanceVegMass','balanceSnowMass','balanceSoilMass','balanceAqMass','numberFluxCalc'] plt_titl = ['vegetation balance','snow balance','soil balance', 'canopy air space and aquifer balance', 'wall clock time'] leg_titl = ['$W~m^{-3}$'] * 4 + ['$s$'] - leg_titl0 =['$kg~m^{-2}~s^{-1}$'] * 4 + ['$num$'] + leg_titl0 =['$kg~m^{-3}~s^{-1}$'] * 4 + ['$num$'] if fixed_Mass_units: leg_titl0 = ['s^{-1}$'] * 3 + ['m~s^{-1}$'] + ['$num$'] plot_vars = [plot_vars[i] for i in use_vars] From 4dbfea44c2e70dc3bccc704ef7a9ad63eb0f4dc5 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 2 Oct 2024 01:39:12 +0900 Subject: [PATCH 1400/1472] need to calculate balances for veg only if computed veg --- build/source/engine/coupled_em.f90 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 44b4d4c09..4ab2be3f0 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1230,8 +1230,10 @@ subroutine coupled_em(& ! sum the balance of energy and water per state innerBalance(1) = innerBalance(1) + diag_data%var(iLookDIAG%balanceCasNrg)%dat(1)*dt_wght ! W m-3 - innerBalance(2) = innerBalance(2) + diag_data%var(iLookDIAG%balanceVegNrg)%dat(1)*dt_wght ! W m-3 - innerBalance(3) = innerBalance(3) + diag_data%var(iLookDIAG%balanceVegMass)%dat(1)*dt_wght/diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ! kg m-3 s-1 + if(computeVegFlux)then + innerBalance(2) = innerBalance(2) + diag_data%var(iLookDIAG%balanceVegNrg)%dat(1)*dt_wght ! W m-3 + innerBalance(3) = innerBalance(3) + diag_data%var(iLookDIAG%balanceVegMass)%dat(1)*dt_wght/diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ! kg m-3 s-1 + endif innerBalance(4) = innerBalance(4) + diag_data%var(iLookDIAG%balanceAqMass)%dat(1)*dt_wght * iden_water ! kg m-2 s-1 (no depth to aquifer) innerBalanceLayerNrg(:) = innerBalanceLayerNrg(:) + diag_data%var(iLookDIAG%balanceLayerNrg)%dat(:)*dt_wght ! W m-3 innerBalanceLayerMass(:) = innerBalanceLayerMass(:) + diag_data%var(iLookDIAG%balanceLayerMass)%dat(:)*dt_wght * iden_water ! kg m-3 s-1 From bd82132643050c7424e1ef44b4092a5c2ef2ec93 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 2 Oct 2024 02:18:01 +0900 Subject: [PATCH 1401/1472] make balances missing if not compute --- build/source/engine/coupled_em.f90 | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 4ab2be3f0..809ccb213 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -250,7 +250,10 @@ subroutine coupled_em(& real(rkind) :: meanLatHeatCanopyEvap ! timestep-average latent heat flux for evaporation from the canopy to the canopy air space (W m-2) real(rkind) :: meanSenHeatCanopy ! timestep-average sensible heat flux from the canopy to the canopy air space (W m-2) ! balance checks + logical(lgt) :: bal_veg ! flag to denote if computed a vegetation balance logical(lgt) :: bal_snow ! flag to denote if computed a snow balance + logical(lgt) :: bal_soil ! flag to denote if computed a soil balance + logical(lgt) :: bal_aq ! flag to denote if computed an aquifer balance integer(i4b) :: iVar ! loop through model variables real(rkind) :: balanceSoilCompress ! total soil compression (kg m-2) real(rkind) :: scalarCanopyWatBalError! water balance error for the vegetation canopy (kg m-2) @@ -682,8 +685,11 @@ subroutine coupled_em(& nsub = 0 nsub_success = 0 - ! initialize if used a snow balance + ! initialize if used a balance + bal_veg = .false. bal_snow = .false. + bal_soil = .false. + bal_aq = .false. ! loop through sub-steps substeps: do ! continuous do statement with exit clause (alternative to "while") @@ -1229,10 +1235,11 @@ subroutine coupled_em(& if (nSnow>0) innerEffRainfall = innerEffRainfall + ( flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) + flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) )*dt_wght ! sum the balance of energy and water per state - innerBalance(1) = innerBalance(1) + diag_data%var(iLookDIAG%balanceCasNrg)%dat(1)*dt_wght ! W m-3 if(computeVegFlux)then + innerBalance(1) = innerBalance(1) + diag_data%var(iLookDIAG%balanceCasNrg)%dat(1)*dt_wght ! W m-3 innerBalance(2) = innerBalance(2) + diag_data%var(iLookDIAG%balanceVegNrg)%dat(1)*dt_wght ! W m-3 innerBalance(3) = innerBalance(3) + diag_data%var(iLookDIAG%balanceVegMass)%dat(1)*dt_wght/diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ! kg m-3 s-1 + bal_veg = .true. endif innerBalance(4) = innerBalance(4) + diag_data%var(iLookDIAG%balanceAqMass)%dat(1)*dt_wght * iden_water ! kg m-2 s-1 (no depth to aquifer) innerBalanceLayerNrg(:) = innerBalanceLayerNrg(:) + diag_data%var(iLookDIAG%balanceLayerNrg)%dat(:)*dt_wght ! W m-3 @@ -1258,8 +1265,10 @@ subroutine coupled_em(& lyr_wght = prog_data%var(iLookPROG%mLayerDepth)%dat(iLayer) / sum( prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers) ) diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) + innerBalanceLayerNrg(iLayer)*lyr_wght diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) + innerBalanceLayerMass(iLayer)*lyr_wght + bal_soil = .true. end select end do + if (model_decisions(iLookDECISIONS%groundwatr)%iDecision == bigBucket) bal_aq = .true. ! aquifer does not change existance with time steps if(do_outer)then deallocate(innerBalanceLayerNrg) @@ -1451,10 +1460,22 @@ subroutine coupled_em(& diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = meanBalance(6) ! W m-3 diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = meanBalance(7) ! kg m-3 s-1 will be realMissing if no snow during data step diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = meanBalance(8) ! kg m-3 s-1 + if(.not.bal_veg)then ! will be 0, make realMissing + diag_data%var(iLookDIAG%balanceCasNrg)%dat(1) = realMissing + diag_data%var(iLookDIAG%balanceVegNrg)%dat(1) = realMissing + diag_data%var(iLookDIAG%balanceVegMass)%dat(1) = realMissing + endif if (.not.bal_snow)then ! will be 0, make realMissing diag_data%var(iLookDIAG%balanceSnowNrg)%dat(1) = realMissing diag_data%var(iLookDIAG%balanceSnowMass)%dat(1) = realMissing endif + if (.not.bal_soil)then ! will be 0, make realMissing + diag_data%var(iLookDIAG%balanceSoilNrg)%dat(1) = realMissing + diag_data%var(iLookDIAG%balanceSoilMass)%dat(1) = realMissing + endif + if (.not.bal_aq)then ! will be 0, make realMissing + diag_data%var(iLookDIAG%balanceAqMass)%dat(1) = realMissing + endif ! ----- ! * balance checks for the canopy... From cd8f61ec090da71304e5b708685f768ae46b268c Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 2 Oct 2024 15:31:03 +0900 Subject: [PATCH 1402/1472] utils --- utils/post-processing/plot_per_GRUMult.py | 11 ++++------- utils/post-processing/plot_per_GRUMultBal.py | 6 +++--- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/utils/post-processing/plot_per_GRUMult.py b/utils/post-processing/plot_per_GRUMult.py index 311bb9b2e..e7d9f27b4 100644 --- a/utils/post-processing/plot_per_GRUMult.py +++ b/utils/post-processing/plot_per_GRUMult.py @@ -35,7 +35,7 @@ do_rel = True # true is plot relative to the benchmark simulation one_plot = False # true is one plot, false is multiple plots (one per variable) -run_local = False # true is run on local machine (only does testing), false is run on cluster +run_local = True # true is run on local machine (only does testing), false is run on cluster more_mean = False # true is plot mean/amax extra variables in a balance file if run_local: @@ -229,9 +229,6 @@ def make_default_path(suffix): if do_rel: s_rel = np.fabs(summa[method_name[0]][plot_var].sel(stat=statr)) if calc[i]: - if stat != 'mean' and stat != 'mnnz': - print('Only mean and mnnz are supported for calculated variables') - sys.exit() if do_rel: s_rel = s_rel.where(summa[method_name[0]][plot_var].sel(stat='mnnz_ben') > melt_thresh*summa[method_name[0]][plot_var].sel(stat='mean_ben')) @@ -520,9 +517,9 @@ def format_tick(value, tick_number): plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines else: - #size hardwired to 3x2 for now - ncol = 2 - nrow = 3 + #size hardwired to 2x3 for now + ncol = 3 + nrow = 2 if len(method_name)>6: print('Too many methods for 3x2 plot') sys.exit() diff --git a/utils/post-processing/plot_per_GRUMultBal.py b/utils/post-processing/plot_per_GRUMultBal.py index 001d66c1e..2ce1e8c30 100644 --- a/utils/post-processing/plot_per_GRUMultBal.py +++ b/utils/post-processing/plot_per_GRUMultBal.py @@ -283,9 +283,9 @@ def run_loop(j,var,the_max): plt.rcParams['patch.antialiased'] = False # Prevents an issue with plotting distortion along the 0 degree latitude and longitude lines else: - #size hardwired to 2x2 for now - ncol = 2 - nrow = 3 + #size hardwired to 2x3 for now + ncol = 3 + nrow = 4 if len(method_name)>6: print('Too many methods for 3x2 plot') sys.exit() From 04b493918a0a52478d9b3af45b6b4349635c8806 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 2 Oct 2024 18:03:32 +0900 Subject: [PATCH 1403/1472] utils --- utils/post-processing/plot_per_GRUMult.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/utils/post-processing/plot_per_GRUMult.py b/utils/post-processing/plot_per_GRUMult.py index e7d9f27b4..c2ff4f513 100644 --- a/utils/post-processing/plot_per_GRUMult.py +++ b/utils/post-processing/plot_per_GRUMult.py @@ -35,7 +35,7 @@ do_rel = True # true is plot relative to the benchmark simulation one_plot = False # true is one plot, false is multiple plots (one per variable) -run_local = True # true is run on local machine (only does testing), false is run on cluster +run_local = False # true is run on local machine (only does testing), false is run on cluster more_mean = False # true is plot mean/amax extra variables in a balance file if run_local: @@ -366,11 +366,16 @@ def run_loop(j,var,the_max): bas_albers.plot(ax=axs[r,c], column=var+m, edgecolor='none', legend=False, cmap=my_cmap2, norm=norm2,zorder=0) stat_word0 = stat_word+' difference' stat_word2 = stat_word + plt_nm = plt_name[i] + elif m=='ref' and var != 'wallClockTime': + # only plot wallClockTime for the reference solution + plt_nm ='' else: bas_albers.plot(ax=axs[r,c], column=var+m, edgecolor='none', legend=False, cmap=my_cmap, norm=norm,zorder=0) stat_word0 = stat_word + plt_nm = plt_name[i] print(f"{'all HRU mean for '}{var+m:<35}{np.nanmean(bas_albers[var+m].values):<10.5f}{' max: '}{np.nanmax(bas_albers[var+m].values):<10.5f}") - axs[r,c].set_title(plt_name[i]) + axs[r,c].set_title(plt_nm) axs[r,c].axis('off') axs[r,c].set_xlim(xmin, xmax) axs[r,c].set_ylim(ymin, ymax) From ebfe0c20b14565a7e98d8d888eb78b8213c2d823 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 3 Oct 2024 10:45:06 +0900 Subject: [PATCH 1404/1472] utils --- utils/post-processing/plot_per_GRUMult.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/utils/post-processing/plot_per_GRUMult.py b/utils/post-processing/plot_per_GRUMult.py index c2ff4f513..19346a2d7 100644 --- a/utils/post-processing/plot_per_GRUMult.py +++ b/utils/post-processing/plot_per_GRUMult.py @@ -420,7 +420,11 @@ def format_tick(value, tick_number): cbr.ax.yaxis.set_major_formatter(ScalarFormatter()) # lakes - if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) + if m=='ref' and var != 'wallClockTime': + # only plot wallClockTime for the reference solution + plt_nm ='' + else: + if plot_lakes: large_lakes_albers.plot(ax=axs[r,c], color=lake_col, zorder=1) else: # extra mean/amax variables for i,v in enumerate(plot_vars_exVar): From 7dc924b16e6a1ff28ee8f3dc42390eace8d70a76 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 5 Oct 2024 03:29:52 -0600 Subject: [PATCH 1405/1472] Updates to parent CMakeLists.txt and build.pc.bash -- LIBRARY_LINKS environment variable has been restored for non-apple builds. --- build/CMakeLists.txt | 5 +++-- build/cmake/build.pc.bash | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 5db2e0170..d9dff6cb0 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -105,8 +105,9 @@ else() # list(APPEND EXT_TARGETS OpenBLAS::OpenBLAS) # LAPACK - find_package(LAPACK REQUIRED) - list(APPEND EXT_TARGETS LAPACK::LAPACK) + set(LIBRARY_LINKS $ENV{LIBRARY_LINKS}) # added for compatibility of PC builds + find_package(LAPACK REQUIRED) + list(APPEND EXT_TARGETS LAPACK::LAPACK) endif() # Set compiler flags diff --git a/build/cmake/build.pc.bash b/build/cmake/build.pc.bash index 41783310d..bf9c0c041 100755 --- a/build/cmake/build.pc.bash +++ b/build/cmake/build.pc.bash @@ -12,5 +12,5 @@ #source $oneAPI_dir/setvars.sh # initialize environment variables for Intel oneAPI #export FLAGS_OPT="-m64;-I"${MKLROOT}/include";-flto=1;-fuse-linker-plugin" # optional compiler flags -- Intel oneMKL builds -cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DUSE_OPENWQ=ON +cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DUSE_OPENWQ=OFF #-DCMAKE_BUILD_TYPE=Debug cmake --build ../cmake_build --target all -j From d8953b3ea28ac5d513c5797ec134a2928bce06a3 Mon Sep 17 00:00:00 2001 From: seantrim Date: Mon, 7 Oct 2024 17:53:31 -0600 Subject: [PATCH 1406/1472] Build updates -- expanded debug build compiler options. --- build/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index d9dff6cb0..b2022a096 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -115,9 +115,9 @@ set(FLAGS_OPT $ENV{FLAGS_OPT}) # get optional user-specified flags from environm if(CMAKE_BUILD_TYPE MATCHES Debug) message("\nSetting SUMMA Debug Options") add_compile_definitions(DEBUG) - set(FLAGS_NOAH -g -O0 -fbacktrace -fbounds-check -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) - set(FLAGS_ALL -g -O0 -fbacktrace -fbounds-check -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp ${FLAGS_OPT}) - set(FLAGS_CXX -g -O0 -fbounds-check -Wfatal-errors -std=c++17 ${FLAGS_OPT}) + set(FLAGS_NOAH -g -O3 -fbacktrace -fcheck=all,no-array-temps -ffpe-trap=invalid,zero,overflow,underflow -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) + set(FLAGS_ALL -g -O3 -fbacktrace -fcheck=all,no-array-temps -ffpe-trap=invalid,zero,overflow,underflow -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp ${FLAGS_OPT}) + set(FLAGS_CXX -g -O3 -fcheck=all,no-array-temps -ffpe-trap=invalid,zero,overflow,underflow -Wfatal-errors -std=c++17 ${FLAGS_OPT}) else() message("\nSetting SUMMA Release Options") set(FLAGS_NOAH -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) From b67a1019d6decfc5f07726897bbc637548e33603 Mon Sep 17 00:00:00 2001 From: seantrim Date: Mon, 7 Oct 2024 22:35:34 -0600 Subject: [PATCH 1407/1472] Updated soil_utils to avoid division by zero and unintended complex values. --- build/source/engine/soil_utils.f90 | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/build/source/engine/soil_utils.f90 b/build/source/engine/soil_utils.f90 index 4849ef5aa..3bd799a96 100644 --- a/build/source/engine/soil_utils.f90 +++ b/build/source/engine/soil_utils.f90 @@ -349,7 +349,7 @@ end function dTheta_dPsi ! ****************************************************************************************************************************** -! public function dPsi_dTheta: compute the derivative of the soil water characteristic (m-1) +! public function dPsi_dTheta: compute the derivative of the soil water characteristic (m) ! ****************************************************************************************************************************** function dPsi_dTheta(volFracLiq,alpha,theta_res,theta_sat,n,m) implicit none @@ -365,20 +365,26 @@ function dPsi_dTheta(volFracLiq,alpha,theta_res,theta_sat,n,m) real(rkind) :: y1,d1 ! 1st function and derivative real(rkind) :: y2,d2 ! 2nd function and derivative real(rkind) :: theta_e ! effective soil moisture + real(rkind),parameter :: theta_e_min=0.001_rkind ! minimum effective soil moisture + real(rkind),parameter :: y1_min=1.e-15_rkind ! minimum y1 value (to avoid division by zero and complex values) + ! check if less than saturation if(volFracLiq < theta_sat)then - ! compute effective water content - theta_e = max(0.001,(volFracLiq - theta_res) / (theta_sat - theta_res)) - ! compute the 1st function and derivative - y1 = theta_e**(-1._rkind/m) - 1._rkind - d1 = (-1._rkind/m)*theta_e**(-1._rkind/m - 1._rkind) / (theta_sat - theta_res) - ! compute the 2nd function and derivative - y2 = y1**(1._rkind/n) - d2 = (1._rkind/n)*y1**(1._rkind/n - 1._rkind) - ! compute the final function value - dPsi_dTheta = d1*d2/alpha + ! compute effective water content + theta_e = max(theta_e_min,(volFracLiq - theta_res) / (theta_sat - theta_res)) + ! compute the 1st function and derivative + y1 = theta_e**(-1._rkind/m) - 1._rkind + d1 = (-1._rkind/m)*theta_e**(-1._rkind/m - 1._rkind) / (theta_sat - theta_res) + ! compute the 2nd function and derivative + ! note: impose a minimum value for y1 to avoid divison by zero and complex values + !y2 = y1**(1._rkind/n) ! original expression + !d2 = (1._rkind/n)*y1**(1._rkind/n - 1._rkind) ! original expression + y2 = max(y1_min,y1)**(1._rkind/n) + d2 = (1._rkind/n)*max(y1_min,y1)**(1._rkind/n - 1._rkind) ! impose a minimum value for y1 to avoid divison by zero and complex values + ! compute the final function value + dPsi_dTheta = d1*d2/alpha else - dPsi_dTheta = 0._rkind + dPsi_dTheta = 0._rkind end if end function dPsi_dTheta From 819b4599b0d993844dbc321262eb42b268117904 Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 8 Oct 2024 00:55:06 -0600 Subject: [PATCH 1408/1472] Updates to debug build - optimization level changed for best debugger output and floating-point exception traps removed (those can be introduced in ). --- build/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index b2022a096..a9a9ed5b1 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -115,9 +115,9 @@ set(FLAGS_OPT $ENV{FLAGS_OPT}) # get optional user-specified flags from environm if(CMAKE_BUILD_TYPE MATCHES Debug) message("\nSetting SUMMA Debug Options") add_compile_definitions(DEBUG) - set(FLAGS_NOAH -g -O3 -fbacktrace -fcheck=all,no-array-temps -ffpe-trap=invalid,zero,overflow,underflow -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) - set(FLAGS_ALL -g -O3 -fbacktrace -fcheck=all,no-array-temps -ffpe-trap=invalid,zero,overflow,underflow -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp ${FLAGS_OPT}) - set(FLAGS_CXX -g -O3 -fcheck=all,no-array-temps -ffpe-trap=invalid,zero,overflow,underflow -Wfatal-errors -std=c++17 ${FLAGS_OPT}) + set(FLAGS_NOAH -g -Og -fbacktrace -fcheck=all,no-array-temps -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) + set(FLAGS_ALL -g -Og -fbacktrace -fcheck=all,no-array-temps -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp ${FLAGS_OPT}) + set(FLAGS_CXX -g -Og -fcheck=all,no-array-temps -Wfatal-errors -std=c++17 ${FLAGS_OPT}) else() message("\nSetting SUMMA Release Options") set(FLAGS_NOAH -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) From 540c7d2dbe31b5d3b243e198cf359a600fbc51b2 Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 8 Oct 2024 02:13:48 -0600 Subject: [PATCH 1409/1472] Updates to avoid a segmentation fault within stateFilter. --- build/source/engine/opSplittin.f90 | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 0c73a4019..e211db12b 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -1394,9 +1394,10 @@ subroutine split_select_compute_stateMask(split_select,indx_data,err,cmessage,me iStateSplit => split_select % iStateSplit ) call in_stateFilter % initialize(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) end associate - associate(stateMask => split_select % stateMask) - call stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) - end associate + !associate(stateMask => split_select % stateMask) + ! call stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) + !end associate + call stateFilter(in_stateFilter,indx_data,split_select % stateMask,out_stateFilter) associate(nSubset => split_select % nSubset) call out_stateFilter % finalize(nSubset,err,cmessage) end associate @@ -1415,7 +1416,8 @@ subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) type(in_type_stateFilter),intent(in) :: in_stateFilter ! indices type(var_ilength),intent(in) :: indx_data ! indices for a local HRU ! output - logical(lgt),intent(out) :: stateMask(:) ! mask defining desired state variables + !logical(lgt),intent(out) :: stateMask(:) ! mask defining desired state variables + logical(lgt),allocatable,intent(inout) :: stateMask(:) ! mask defining desired state variables type(out_type_stateFilter),intent(out) :: out_stateFilter ! number of selected state variables for a given split and error control ! local integer(i4b),allocatable :: ixSubset(:) ! list of indices in the state subset @@ -1456,7 +1458,8 @@ subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) subroutine fullyCoupled_stateMask ! *** Get fully coupled stateMask *** - stateMask(:) = .true. ! use all state variables + !stateMask(:) = .true. ! use all state variables + stateMask = .true. ! use all state variables end subroutine fullyCoupled_stateMask subroutine stateTypeSplit_stateMask From cfed33fff2b550eeb929900d71032678f40d58c6 Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 8 Oct 2024 02:18:53 -0600 Subject: [PATCH 1410/1472] Update to soil_utils - parameter used to avoid floating-point exception is now proportional to machine epsilon. --- build/source/engine/soil_utils.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/soil_utils.f90 b/build/source/engine/soil_utils.f90 index 3bd799a96..3fc6feb71 100644 --- a/build/source/engine/soil_utils.f90 +++ b/build/source/engine/soil_utils.f90 @@ -365,8 +365,8 @@ function dPsi_dTheta(volFracLiq,alpha,theta_res,theta_sat,n,m) real(rkind) :: y1,d1 ! 1st function and derivative real(rkind) :: y2,d2 ! 2nd function and derivative real(rkind) :: theta_e ! effective soil moisture - real(rkind),parameter :: theta_e_min=0.001_rkind ! minimum effective soil moisture - real(rkind),parameter :: y1_min=1.e-15_rkind ! minimum y1 value (to avoid division by zero and complex values) + real(rkind),parameter :: theta_e_min=0.001_rkind ! minimum effective soil moisture + real(rkind),parameter :: y1_min=10._rkind*epsilon(1._rkind) ! minimum y1 value (to avoid division by zero and complex values) ! check if less than saturation if(volFracLiq < theta_sat)then From dba01ed903806e3706a80a20dddbe1c7140b68f8 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 8 Oct 2024 23:15:46 +0900 Subject: [PATCH 1411/1472] use openBLAS and fix threading to 1 --- build/CMakeLists.txt | 19 +++---------------- build/cmake/build.mac.bash | 1 - build/cmake/build_actors.mac.bash | 1 - build/cmake/build_ngen.mac.bash | 1 - build/source/engine/matrixOper.f90 | 2 ++ 5 files changed, 5 insertions(+), 19 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 5db2e0170..bd7947a4d 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -92,22 +92,9 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXEC_DIR}) find_package(NetCDF REQUIRED) list(APPEND EXT_TARGETS NetCDF::NetCDF) -if(APPLE) - # Get most optimized lapack - set(LIBRARY_LINKS $ENV{LIBRARY_LINKS}) - -else() - # OpenBLAS (MAY HAVE OpenMP enabled which slows execution) - # USE LAPACK INSTEAD TO AVOID THIS ISSUE - # message("\nRun export OPENBLAS_NUM_THREADS=1, to ward off bad behavior assuming not using multithreading") - # set(BLA_VENDOR OpenBLAS) # MKL - # find_package(OpenBLAS REQUIRED) - # list(APPEND EXT_TARGETS OpenBLAS::OpenBLAS) - - # LAPACK - find_package(LAPACK REQUIRED) - list(APPEND EXT_TARGETS LAPACK::LAPACK) -endif() +# OpenBLAS +find_package(OpenBLAS REQUIRED) +list(APPEND EXT_TARGETS OpenBLAS::OpenBLAS) # Set compiler flags set(FLAGS_OPT $ENV{FLAGS_OPT}) # get optional user-specified flags from environment variables diff --git a/build/cmake/build.mac.bash b/build/cmake/build.mac.bash index 7dc62df90..7acaab1a5 100755 --- a/build/cmake/build.mac.bash +++ b/build/cmake/build.mac.bash @@ -9,7 +9,6 @@ export FC=/opt/local/bin/gfortran # Fortran compiler #export FLAGS_OPT="-flto=1" # -flto=1 is slow to compile, but might want to use export SUNDIALS_DIR=../../../sundials/instdir/ -export LIBRARY_LINKS='-llapack' # list of library links cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DCMAKE_BUILD_TYPE=Release cmake --build ../cmake_build --target all -j diff --git a/build/cmake/build_actors.mac.bash b/build/cmake/build_actors.mac.bash index 41b95a0b2..0770cf5e4 100755 --- a/build/cmake/build_actors.mac.bash +++ b/build/cmake/build_actors.mac.bash @@ -9,7 +9,6 @@ export FC=/opt/local/bin/gfortran # Fortran compiler #export FLAGS_OPT="-flto=1" # -flto=1 is slow to compile, but might want to use export SUNDIALS_DIR=../../../sundials/instdir/ -export LIBRARY_LINKS='-llapack' # list of library links cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DUSE_ACTORS=ON cmake --build ../cmake_build --target all -j diff --git a/build/cmake/build_ngen.mac.bash b/build/cmake/build_ngen.mac.bash index 42f171c89..ae5aff0a7 100755 --- a/build/cmake/build_ngen.mac.bash +++ b/build/cmake/build_ngen.mac.bash @@ -10,7 +10,6 @@ export FC=/opt/local/bin/gfortran # Fortran compiler export C_INCLUDE_PATH=/opt/local/include export CPLUS_INCLUDE_PATH=/opt/local/include export SUNDIALS_DIR=../../../sundials/instdir/ -export LIBRARY_LINKS='-llapack' # list of library links cmake -B extern/summa/cmake_build -S extern/summa -DUSE_NEXTGEN=ON cmake --build extern/summa/cmake_build --target all -j diff --git a/build/source/engine/matrixOper.f90 b/build/source/engine/matrixOper.f90 index 27939b484..5e468dd1d 100644 --- a/build/source/engine/matrixOper.f90 +++ b/build/source/engine/matrixOper.f90 @@ -173,6 +173,8 @@ subroutine lapackSolv(ixMatrix,nState,aJac,rVec,xInc,err,message) case default; err=20; message=trim(message)//'unable to identify option for the type of matrix' end select + call openblas_set_num_threads(1) ! set the number of threads to 1 + ! form the rhs matrix ! NOTE: copy the vector here to ensure that the residual vector is not overwritten rhs(:,1) = rVec(:) From 5cc7151f4efb0be5255f7425ff0e4d8e2dd40193 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 9 Oct 2024 18:53:46 +0900 Subject: [PATCH 1412/1472] fixing restart file to have hruId and gruId --- build/source/dshare/data_types.f90 | 10 +++---- build/source/dshare/get_ixname.f90 | 2 ++ build/source/dshare/popMetadat.f90 | 2 ++ build/source/dshare/var_lookup.f90 | 8 +++-- build/source/engine/allocspace.f90 | 6 ++-- build/source/engine/coupled_em.f90 | 2 +- build/source/engine/ffile_info.f90 | 4 +-- build/source/engine/nrtype.f90 | 1 + build/source/engine/read_attrb.f90 | 18 +++++++----- build/source/engine/read_param.f90 | 4 +-- build/source/engine/run_oneGRU.f90 | 4 +-- build/source/engine/run_oneHRU.f90 | 4 +-- build/source/netcdf/def_output.f90 | 25 ++++++++-------- build/source/netcdf/modelwrite.f90 | 19 +++++------- .../source/openwq/summa_openWQ_allocspace.f90 | 6 ++-- utils/pre-processing/subsetGRU.sh | 29 +++++++------------ 16 files changed, 73 insertions(+), 71 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index fdf00eedf..40ab5436e 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -109,14 +109,14 @@ MODULE data_types type, public :: hru_info integer(i4b) :: hru_nc ! index of the hru in the netcdf file integer(i4b) :: hru_ix ! index of the hru in the run domain - integer(8) :: hru_id ! id (non-sequential number) of the hru + integer(i8b) :: hru_id ! id (non-sequential number) of the hru integer(i4b) :: nSnow ! number of snow layers integer(i4b) :: nSoil ! number of soil layers endtype hru_info ! define mapping from GRUs to the HRUs type, public :: gru2hru_map - integer(8) :: gru_id ! id of the gru + integer(i8b) :: gru_id ! id of the gru integer(i4b) :: hruCount ! total number of hrus in the gru type(hru_info), allocatable :: hruInfo(:) ! basic information of HRUs within the gru integer(i4b) :: gru_nc ! index of gru in the netcdf file @@ -164,7 +164,7 @@ MODULE data_types endtype ilength ! ** integer type (8 byte) type, public :: i8length - integer(8),allocatable :: dat(:) ! dat(:) + integer(i8b),allocatable :: dat(:) ! dat(:) endtype i8length ! ** logical type type, public :: flagVec @@ -201,7 +201,7 @@ MODULE data_types endtype var_i ! ** integer type of fixed length (8 byte) type, public :: var_i8 - integer(8),allocatable :: var(:) ! var(:) + integer(i8b),allocatable :: var(:) ! var(:) endtype var_i8 ! ** double precision type of fixed length @@ -214,7 +214,7 @@ MODULE data_types endtype hru_i ! ** integer type of fixed length (8 byte) type, public :: hru_i8 - integer(8),allocatable :: hru(:) ! hru(:) + integer(i8b),allocatable :: hru(:) ! hru(:) endtype hru_i8 ! define derived types to hold JUST the HRU dimension diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index 646ec6efb..e82d16208 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -215,6 +215,8 @@ function get_ixId(varName) ! get the index of the named variables select case(trim(varName)) case('hruId' ); get_ixId = iLookID%hruId ! id defining HRU index + case('gruId' ); get_ixId = iLookID%gruId ! id defining GRU index + case('hru2gruId' ); get_ixId = iLookID%hru2gruId ! id defining the GRU to which the HRU belongs ! get to here if cannot find the variable case default get_ixId = integerMissing diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index cebd0bc7f..223e1117a 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -94,6 +94,8 @@ subroutine popMetadat(err,message) ! * hru and gru ID data... ! --------------------- id_meta(iLookID%hruId) = var_info('hruId' , 'ID defining the hydrologic response unit' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + id_meta(iLookID%gruId) = var_info('gruId' , 'ID defining the grouped response unit' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + id_meta(iLookID%hru2gruId) = var_info('hru2gruId' , 'ID defining the GRU to which the HRU belongs' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! ----- ! * site characteristics... ! ------------------------- diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index 8fee9b286..ebe63128a 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -28,7 +28,7 @@ MODULE var_lookup private ! local variables integer(i4b),parameter :: ixVal =1 ! an example 4 byte integer - integer(8),parameter :: ix8Val=2 ! an example 8 byte integer + integer(i8b),parameter :: ix8Val=2 ! an example 8 byte integer integer(i4b),parameter :: iLength =storage_size(ixVal) ! size of the example 4 byte integer integer(i4b),parameter :: i8Length=storage_size(ix8Val) ! size of the example 8 byte integer @@ -131,7 +131,9 @@ MODULE var_lookup end type iLook_type type, public :: iLook_id - integer(8) :: hruId = integerMissing ! ID label defining hydrologic response unit (-) + integer(i8b) :: hruId = integerMissing ! ID label defining hydrologic response unit (-) + integer(i8b) :: gruId = integerMissing ! ID label defining grouped response unit (-) + integer(i8b) :: hru2gruId = integerMissing ! ID label defining GRU to which HRU belongs (-) end type iLook_id ! *********************************************************************************************************** @@ -900,7 +902,7 @@ MODULE var_lookup ! named variables: soil and vegetation types type(iLook_type), public,parameter :: iLookTYPE =iLook_type ( 1, 2, 3, 4) ! named variables: hru and gru IDs and associated information - type(iLook_id), public,parameter :: iLookID =iLook_id ( 1) + type(iLook_id), public,parameter :: iLookID =iLook_id ( 1, 2, 3) ! named variables: model parameters type(iLook_param), public,parameter :: iLookPARAM =iLook_param ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& diff --git a/build/source/engine/allocspace.f90 b/build/source/engine/allocspace.f90 index 3c35318fa..8aa972ef0 100644 --- a/build/source/engine/allocspace.f90 +++ b/build/source/engine/allocspace.f90 @@ -30,20 +30,20 @@ module allocspace_module ilength, & ! var%dat ! no spatial dimension var_i, & ! x%var(:) (i4b) - var_i8, & ! x%var(:) integer(8) + var_i8, & ! x%var(:) (i8b) var_d, & ! x%var(:) (rkind) var_flagVec, & ! x%var(:)%dat (logical) var_ilength, & ! x%var(:)%dat (i4b) var_dlength, & ! x%var(:)%dat (rkind) ! gru dimension gru_int, & ! x%gru(:)%var(:) (i4b) - gru_int8, & ! x%gru(:)%var(:) integer(8) + gru_int8, & ! x%gru(:)%var(:) (i8b) gru_double, & ! x%gru(:)%var(:) (rkind) gru_intVec, & ! x%gru(:)%var(:)%dat (i4b) gru_doubleVec, & ! x%gru(:)%var(:)%dat (rkind) ! gru+hru dimension gru_hru_int, & ! x%gru(:)%hru(:)%var(:) (i4b) - gru_hru_int8, & ! x%gru(:)%hru(:)%var(:) integer(8) + gru_hru_int8, & ! x%gru(:)%hru(:)%var(:) (i8b) gru_hru_double, & ! x%gru(:)%hru(:)%var(:) (rkind) gru_hru_intVec, & ! x%gru(:)%hru(:)%var(:)%dat (i4b) gru_hru_doubleVec, & ! x%gru(:)%hru(:)%var(:)%dat (rkind) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 809ccb213..7e4cffc54 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -158,7 +158,7 @@ subroutine coupled_em(& implicit none - integer(8),intent(in) :: hruId ! hruId + integer(i8b),intent(in) :: hruId ! hruId real(rkind),intent(inout) :: dt_init ! used to initialize the size of the sub-step integer(i4b),intent(in) :: dt_init_factor ! Used to adjust the length of the timestep in the event of a failure logical(lgt),intent(inout) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) diff --git a/build/source/engine/ffile_info.f90 b/build/source/engine/ffile_info.f90 index e0e7dea11..37d3f03df 100644 --- a/build/source/engine/ffile_info.f90 +++ b/build/source/engine/ffile_info.f90 @@ -73,8 +73,8 @@ subroutine ffile_info(nGRU,err,message) integer(i4b) :: file_nHRU ! number of HRUs in current forcing file integer(i4b) :: nForcing ! number of forcing variables integer(i4b) :: iGRU,localHRU_ix ! index of GRU and HRU - integer(8) :: ncHruId(1) ! hruID from the forcing files - real(rkind) :: dataStep_iFile ! data step for a given forcing data file + integer(i8b) :: ncHruId(1) ! hruID from the forcing files + real(rkind) :: dataStep_iFile ! data step for a given forcing data file logical(lgt) :: xist ! .TRUE. if the file exists ! Start procedure here diff --git a/build/source/engine/nrtype.f90 b/build/source/engine/nrtype.f90 index c6ea58b9d..e3f1ac3dd 100644 --- a/build/source/engine/nrtype.f90 +++ b/build/source/engine/nrtype.f90 @@ -2,6 +2,7 @@ MODULE nrtype IMPLICIT NONE SAVE ! data types + INTEGER, PARAMETER :: I8B = SELECTED_INT_KIND(18) INTEGER, PARAMETER :: I4B = SELECTED_INT_KIND(9) INTEGER, PARAMETER :: I2B = SELECTED_INT_KIND(4) INTEGER, PARAMETER :: I1B = SELECTED_INT_KIND(2) diff --git a/build/source/engine/read_attrb.f90 b/build/source/engine/read_attrb.f90 index 44d260608..efdab4a83 100644 --- a/build/source/engine/read_attrb.f90 +++ b/build/source/engine/read_attrb.f90 @@ -53,8 +53,8 @@ subroutine read_dimension(attrFile,fileGRU,fileHRU,nGRU,nHRU,err,message,startGR integer(i4b) :: sGRU ! starting GRU integer(i4b) :: iHRU ! HRU couinting index integer(i4b) :: iGRU ! GRU loop index - integer(8),allocatable :: gru_id(:),hru_id(:)! read gru/hru IDs in from attributes file - integer(8),allocatable :: hru2gru_id(:) ! read hru->gru mapping in from attributes file + integer(i8b),allocatable :: gru_id(:),hru_id(:)! read gru/hru IDs in from attributes file + integer(i8b),allocatable :: hru2gru_id(:) ! read hru->gru mapping in from attributes file integer(i4b),allocatable :: hru_ix(:) ! hru index for search ! define variables for NetCDF file operation @@ -201,7 +201,7 @@ subroutine read_attrb(attrFile,nGRU,attrStruct,typeStruct,idStruct,err,message) USE netcdf_util_module,only:netcdf_err ! netcdf error handling function ! provide access to derived data types USE data_types,only:gru_hru_int ! x%gru(:)%hru(:)%var(:) (i4b) - USE data_types,only:gru_hru_int8 ! x%gru(:)%hru(:)%var(:) integer(8) + USE data_types,only:gru_hru_int8 ! x%gru(:)%hru(:)%var(:) (i8b) USE data_types,only:gru_hru_double ! x%gru(:)%hru(:)%var(:) (rkind) ! provide access to global data USE globalData,only:gru_struc ! gru-hru mapping structure @@ -240,8 +240,8 @@ subroutine read_attrb(attrFile,nGRU,attrStruct,typeStruct,idStruct,err,message) integer(i4b),parameter :: numerical=102 ! named variable to denote numerical data integer(i4b),parameter :: idrelated=103 ! named variable to denote ID related data integer(i4b) :: categorical_var(1) ! temporary categorical variable from local attributes netcdf file - real(rkind) :: numeric_var(1) ! temporary numeric variable from local attributes netcdf file - integer(8) :: idrelated_var(1) ! temporary ID related variable from local attributes netcdf file + real(rkind) :: numeric_var(1) ! temporary numeric variable from local attributes netcdf file + integer(i8b) :: idrelated_var(1) ! temporary ID related variable from local attributes netcdf file ! define mapping variables @@ -341,8 +341,12 @@ subroutine read_attrb(attrFile,nGRU,attrStruct,typeStruct,idStruct,err,message) end do end do - ! for mapping varibles, do nothing (information read above) - case('hru2gruId','gruId'); cycle + ! for mapping varibles, do nothing (information read above in read_dimension) + case('hru2gruId','gruId') + ! get the index of the variable + varType = idrelated + varIndx = get_ixId(varName) + checkId(varIndx) = .true. ! check that variables are what we expect case default; message=trim(message)//'unknown variable ['//trim(varName)//'] in local attributes file'; err=20; return diff --git a/build/source/engine/read_param.f90 b/build/source/engine/read_param.f90 index 110bf0352..5fe0dcc80 100644 --- a/build/source/engine/read_param.f90 +++ b/build/source/engine/read_param.f90 @@ -89,8 +89,8 @@ subroutine read_param(iRunMode,checkHRU,startGRU,nHRU,nGRU,idStruct,mparStruct,b integer(i4b) :: idim_list(2) ! list of dimension ids ! data in the netcdf file integer(i4b) :: parLength ! length of the parameter data - integer(8),allocatable :: hruId(:) ! HRU identifier in the file - real(rkind),allocatable :: parVector(:) ! model parameter vector + integer(i8b),allocatable :: hruId(:) ! HRU identifier in the file + real(rkind),allocatable :: parVector(:) ! model parameter vector logical :: fexist ! inquire whether the parmTrial file exists integer(i4b) :: fHRU ! index of HRU in input file diff --git a/build/source/engine/run_oneGRU.f90 b/build/source/engine/run_oneGRU.f90 index 25e4a95c2..5ea1c9ac7 100644 --- a/build/source/engine/run_oneGRU.f90 +++ b/build/source/engine/run_oneGRU.f90 @@ -35,7 +35,7 @@ module run_oneGRU_module var_dlength, & ! x%var(:)%dat (rkind) ! hru dimension hru_int, & ! x%hru(:)%var(:) (i4b) - hru_int8, & ! x%hru(:)%var(:) integer(8) + hru_int8, & ! x%hru(:)%var(:) (i8b) hru_double, & ! x%hru(:)%var(:) (rkind) hru_intVec, & ! x%hru(:)%var(:)%dat (i4b) hru_doubleVec, & ! x%hru(:)%var(:)%dat (rkind) @@ -131,7 +131,7 @@ subroutine run_oneGRU(& logical(lgt) :: computeVegFluxFlag ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) ! initialize error control - err=0; write(message, '(A21,I0,A10,I0,A2)' ) 'run_oneGRU (gru nc = ',gruInfo%gru_nc -1,', gruId = ',gruInfo%gru_id,')/' !netcdf index starts with 0 if want to subset + err=0; write(message, '(A21,I0,A10,I0,A2)' ) 'run_oneGRU (gru nc = ',gruInfo%gru_nc,', gruId = ',gruInfo%gru_id,')/' ! ----- basin initialization -------------------------------------------------------------------------------------------- diff --git a/build/source/engine/run_oneHRU.f90 b/build/source/engine/run_oneHRU.f90 index 6a6cbb30b..1f167b720 100644 --- a/build/source/engine/run_oneHRU.f90 +++ b/build/source/engine/run_oneHRU.f90 @@ -118,7 +118,7 @@ subroutine run_oneHRU(& ! model control integer(i4b) , intent(in) :: hru_nc ! hru index in netcdf - integer(8) , intent(in) :: hruId ! hruId + integer(i8b) , intent(in) :: hruId ! hruId real(rkind) , intent(inout) :: dt_init ! used to initialize the length of the sub-step for each HRU logical(lgt) , intent(inout) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (false=no, true=yes) integer(i4b) , intent(inout) :: nSnow,nSoil,nLayers ! number of snow and soil layers @@ -146,7 +146,7 @@ subroutine run_oneHRU(& real(rkind) , allocatable :: zSoilReverseSign(:) ! height at bottom of each soil layer, negative downwards (m) ! initialize error control - err=0; write(message, '(A21,I0,A10,I0,A2)' ) 'run_oneHRU (hru nc = ',hru_nc -1 ,', hruId = ',hruId,')/' !netcdf index starts with 0 if want to subset + err=0; write(message, '(A21,I0,A10,I0,A2)' ) 'run_oneHRU (hru nc = ',hru_nc ,', hruId = ',hruId,')/' ! ----- hru initialization --------------------------------------------------------------------------------------------- diff --git a/build/source/netcdf/def_output.f90 b/build/source/netcdf/def_output.f90 index 95c33fd97..217debb26 100644 --- a/build/source/netcdf/def_output.f90 +++ b/build/source/netcdf/def_output.f90 @@ -30,6 +30,7 @@ module def_output_module implicit none private public :: def_output +public :: write_hru_info ! define dimension names character(len=32),parameter :: gru_DimName = 'gru' ! dimension name for the GRUs @@ -221,8 +222,8 @@ subroutine ini_create(nGRU,nHRU,nSoil,infile,ncid,err,message) message='iCreate[create]'; call netcdf_err(err,message); if (err/=0) return ! create dimensions - err = nf90_def_dim(ncid, trim( gru_DimName), nGRU, gru_DimID); message='iCreate[GRU]'; call netcdf_err(err,message); if (err/=0) return - err = nf90_def_dim(ncid, trim( hru_DimName), nHRU, hru_DimID); message='iCreate[HRU]'; call netcdf_err(err,message); if (err/=0) return + err = nf90_def_dim(ncid, trim( gru_DimName), nGRU, gru_DimID); message='iCreate[gru]'; call netcdf_err(err,message); if (err/=0) return + err = nf90_def_dim(ncid, trim( hru_DimName), nHRU, hru_DimID); message='iCreate[hru]'; call netcdf_err(err,message); if (err/=0) return err = nf90_def_dim(ncid, trim(timestep_DimName), nf90_unlimited, timestep_DimID); message='iCreate[time]'; call netcdf_err(err,message); if (err/=0) return err = nf90_def_dim(ncid, trim( depth_DimName), nSoil, depth_DimID); message='iCreate[depth]'; call netcdf_err(err,message); if (err/=0) return err = nf90_def_dim(ncid, trim( scalar_DimName), scalarLength, scalar_DimID); message='iCreate[scalar]'; call netcdf_err(err,message); if (err/=0) return @@ -423,7 +424,7 @@ subroutine def_variab(ncid,iFreq,spatialDesire,timeDesire,metaData,ivtype,err,me end subroutine def_variab ! ********************************************************************************************************** - ! internal subroutine write_hru_info: write HRU dimension and IDs + ! public subroutine write_hru_info: write HRU dimension and IDs ! ********************************************************************************************************** subroutine write_hru_info(ncid, err, message) use globalData,only:gru_struc ! gru-hru mapping structures @@ -436,9 +437,9 @@ subroutine write_hru_info(ncid, err, message) integer(i4b) :: iHRU ! local HRU index integer(i4b) :: iGRU ! GRU index integer(i4b) :: hruVarID ! hru varID in netcdf file - integer(i4b) :: gruVarID ! hru varID in netcdf file - integer(i4b) :: hruIdVarID ! hruId varID in netcdf file - integer(i4b) :: gruIdVarID ! gruId varID in netcdf file + integer(i4b) :: gruVarID ! gru varID in netcdf file + integer(i4b) :: hruIdVarID ! hruId varID in netcdf file, non-sequential HRU ID + integer(i4b) :: gruIdVarID ! gruId varID in netcdf file, non-sequential GRU ID ! initialize error control err=0; message='write_hru_info/' @@ -447,22 +448,22 @@ subroutine write_hru_info(ncid, err, message) err = nf90_redef(ncid); call netcdf_err(err, message); if (err/=nf90_NoErr) return ! define HRU var - err = nf90_def_var(ncid, trim(hru_DimName), nf90_int64, hru_DimID, hruVarID, deflate_level=outputCompressionLevel); if (err/=nf90_NoErr) then; message=trim(message)//'nf90_define_hruVar' ; call netcdf_err(err,message); return; end if + err = nf90_def_var(ncid, trim(hru_DimName), nf90_int, (/hru_DimID/), hruVarID, deflate_level=outputCompressionLevel); if (err/=nf90_NoErr) then; message=trim(message)//'nf90_define_hruVar' ; call netcdf_err(err,message); return; end if err = nf90_put_att(ncid, hruVarID, 'long_name', 'hruId in the input file'); if (err/=nf90_NoErr) then; message=trim(message)//'write_hruVar_longname'; call netcdf_err(err,message); return; end if err = nf90_put_att(ncid, hruVarID, 'units', '-' ); if (err/=nf90_NoErr) then; message=trim(message)//'write_hruVar_unit'; call netcdf_err(err,message); return; end if ! define GRU var - err = nf90_def_var(ncid, trim(gru_DimName), nf90_int64, gru_DimID, gruVarID, deflate_level=outputCompressionLevel); if (err/=nf90_NoErr) then; message=trim(message)//'nf90_define_gruVar' ; call netcdf_err(err,message); return; end if + err = nf90_def_var(ncid, trim(gru_DimName), nf90_int, (/gru_DimID/), gruVarID, deflate_level=outputCompressionLevel); if (err/=nf90_NoErr) then; message=trim(message)//'nf90_define_gruVar' ; call netcdf_err(err,message); return; end if err = nf90_put_att(ncid, gruVarID, 'long_name', 'gruId in the input file'); if (err/=nf90_NoErr) then; message=trim(message)//'write_gruVar_longname'; call netcdf_err(err,message); return; end if err = nf90_put_att(ncid, gruVarID, 'units', '-' ); if (err/=nf90_NoErr) then; message=trim(message)//'write_gruVar_unit'; call netcdf_err(err,message); return; end if ! define hruId var - err = nf90_def_var(ncid, 'hruId', nf90_int64, hru_DimID, hruIdVarID, deflate_level=outputCompressionLevel); if (err/=nf90_NoErr) then; message=trim(message)//'nf90_define_hruIdVar' ; call netcdf_err(err,message); return; end if + err = nf90_def_var(ncid, 'hruId', nf90_int64, (/hru_DimID/), hruIdVarID, deflate_level=outputCompressionLevel); if (err/=nf90_NoErr) then; message=trim(message)//'nf90_define_hruIdVar' ; call netcdf_err(err,message); return; end if err = nf90_put_att(ncid, hruIdVarID, 'long_name', 'ID defining the hydrologic response unit'); if (err/=nf90_NoErr) then; message=trim(message)//'write_hruIdVar_longname'; call netcdf_err(err,message); return; end if err = nf90_put_att(ncid, hruIdVarID, 'units', '-' ); if (err/=nf90_NoErr) then; message=trim(message)//'write_hruIdVar_unit'; call netcdf_err(err,message); return; end if ! define gruId var - err = nf90_def_var(ncid, 'gruId', nf90_int64, gru_DimID, gruIdVarID, deflate_level=outputCompressionLevel); if (err/=nf90_NoErr) then; message=trim(message)//'nf90_define_gruIdVar' ; call netcdf_err(err,message); return; end if + err = nf90_def_var(ncid, 'gruId', nf90_int64, (/gru_DimID/), gruIdVarID, deflate_level=outputCompressionLevel); if (err/=nf90_NoErr) then; message=trim(message)//'nf90_define_gruIdVar' ; call netcdf_err(err,message); return; end if err = nf90_put_att(ncid, gruIdVarID, 'long_name', 'ID defining the grouped (basin) response unit'); if (err/=nf90_NoErr) then; message=trim(message)//'write_gruIdVar_longname'; call netcdf_err(err,message); return; end if err = nf90_put_att(ncid, gruIdVarID, 'units', '-' ); if (err/=nf90_NoErr) then; message=trim(message)//'write_gruIdVar_unit'; call netcdf_err(err,message); return; end if @@ -473,14 +474,14 @@ subroutine write_hru_info(ncid, err, message) do iGRU = 1, size(gru_struc) ! GRU info - err = nf90_put_var(ncid, gruVarID, gru_struc(iGRU)%gru_id, start=(/iGRU/)) + err = nf90_put_var(ncid, gruVarID, gru_struc(iGRU)%gru_nc, start=(/iGRU/)) if (err/=nf90_NoErr) then; message=trim(message)//'nf90_write_gruVar'; call netcdf_err(err,message); return; end if err = nf90_put_var(ncid, gruIdVarID, gru_struc(iGRU)%gru_id, start=(/iGRU/)) if (err/=nf90_NoErr) then; message=trim(message)//'nf90_write_gruIdVar'; call netcdf_err(err,message); return; end if ! HRU info do iHRU = 1, gru_struc(iGRU)%hruCount - err = nf90_put_var(ncid, hruVarID, gru_struc(iGRU)%hruInfo(iHRU)%hru_id, start=(/gru_struc(iGRU)%hruInfo(iHRU)%hru_ix/)) + err = nf90_put_var(ncid, hruVarID, gru_struc(iGRU)%hruInfo(iHRU)%hru_nc, start=(/gru_struc(iGRU)%hruInfo(iHRU)%hru_ix/)) if (err/=nf90_NoErr) then; message=trim(message)//'nf90_write_hruVar'; call netcdf_err(err,message); return; end if err = nf90_put_var(ncid, hruIdVarID, gru_struc(iGRU)%hruInfo(iHRU)%hru_id, start=(/gru_struc(iGRU)%hruInfo(iHRU)%hru_ix/)) if (err/=nf90_NoErr) then; message=trim(message)//'nf90_write_hruIdVar'; call netcdf_err(err,message); return; end if diff --git a/build/source/netcdf/modelwrite.f90 b/build/source/netcdf/modelwrite.f90 index d6e236c84..b59f9fb3a 100644 --- a/build/source/netcdf/modelwrite.f90 +++ b/build/source/netcdf/modelwrite.f90 @@ -33,9 +33,6 @@ module modelwrite_module ! provide access to global data USE globalData,only:gru_struc ! gru->hru mapping structure -! netcdf deflate level -USE globalData,only: outputCompressionLevel - ! provide access to the derived types to define the data structures USE data_types,only:& ! final data vectors @@ -43,7 +40,7 @@ module modelwrite_module ilength, & ! var%dat ! no spatial dimension var_i, & ! x%var(:) (i4b) - var_i8, & ! x%var(:) integer(8) + var_i8, & ! x%var(:) (i8b) var_d, & ! x%var(:) (dp) var_ilength, & ! x%var(:)%dat (i4b) var_dlength, & ! x%var(:)%dat (dp) @@ -57,7 +54,7 @@ module modelwrite_module gru_doubleVec, & ! x%gru(:)%var(:)%dat (dp) ! gru+hru dimension gru_hru_int, & ! x%gru(:)%hru(:)%var(:) (i4b) - gru_hru_int8, & ! x%gru(:)%hru(:)%var(:) integer(8) + gru_hru_int8, & ! x%gru(:)%hru(:)%var(:) (i8b) gru_hru_double, & ! x%gru(:)%hru(:)%var(:) (dp) gru_hru_intVec, & ! x%gru(:)%hru(:)%var(:)%dat (i4b) gru_hru_doubleVec ! x%gru(:)%hru(:)%var(:)%dat (dp) @@ -467,6 +464,7 @@ subroutine writeRestart(filename, & ! intent(in): name of restart file USE netcdf_util_module,only:nc_file_close ! close netcdf file USE netcdf_util_module,only:nc_file_open ! open netcdf file USE globalData,only:nTimeDelay ! number of timesteps in the time delay histogram + USE def_output_module,only: write_hru_info ! write HRU information to netcdf file implicit none ! -------------------------------------------------------------------------------------------------------- @@ -493,7 +491,6 @@ subroutine writeRestart(filename, & ! intent(in): name of restart file integer(i4b),allocatable :: ncVarID(:) ! netcdf variable id integer(i4b) :: ncSnowID ! index variable id integer(i4b) :: ncSoilID ! index variable id - integer(i4b) :: nSoil ! number of soil layers integer(i4b) :: nSnow ! number of snow layers integer(i4b) :: maxSnow ! maximum number of snow layers @@ -502,7 +499,6 @@ subroutine writeRestart(filename, & ! intent(in): name of restart file integer(i4b),parameter :: nSpectral=2 ! number of spectal bands integer(i4b),parameter :: nScalar=1 ! size of a scalar integer(i4b) :: nProgVars ! number of prognostic variables written to state file - integer(i4b) :: hruDimID ! variable dimension ID integer(i4b) :: gruDimID ! variable dimension ID integer(i4b) :: tdhDimID ! variable dimension ID @@ -514,7 +510,6 @@ subroutine writeRestart(filename, & ! intent(in): name of restart file integer(i4b) :: ifcSnowDimID ! variable dimension ID integer(i4b) :: ifcSoilDimID ! variable dimension ID integer(i4b) :: ifcTotoDimID ! variable dimension ID - character(len=32),parameter :: hruDimName ='hru' ! dimension name for HRUs character(len=32),parameter :: gruDimName ='gru' ! dimension name for GRUs character(len=32),parameter :: tdhDimName ='tdh' ! dimension name for time-delay basin variables @@ -526,7 +521,6 @@ subroutine writeRestart(filename, & ! intent(in): name of restart file character(len=32),parameter :: ifcSnowDimName='ifcSnow' ! dimension name for snow-only layers character(len=32),parameter :: ifcSoilDimName='ifcSoil' ! dimension name for soil-only layers character(len=32),parameter :: ifcTotoDimName='ifcToto' ! dimension name for layered variables - integer(i4b) :: cHRU ! count of HRUs integer(i4b) :: iHRU ! index of HRUs integer(i4b) :: iGRU ! index of GRUs @@ -549,12 +543,12 @@ subroutine writeRestart(filename, & ! intent(in): name of restart file maxSnow = maxSnowLayers ! create file - err = nf90_create(trim(filename),nf90_classic_model,ncid) + err = nf90_create(trim(filename),NF90_NETCDF4,ncid) message='iCreate[create]'; call netcdf_err(err,message); if(err/=0)return ! define dimensions - err = nf90_def_dim(ncid,trim(hruDimName) ,nHRU , hruDimID); message='iCreate[hru]' ; call netcdf_err(err,message); if(err/=0)return err = nf90_def_dim(ncid,trim(gruDimName) ,nGRU , gruDimID); message='iCreate[gru]' ; call netcdf_err(err,message); if(err/=0)return + err = nf90_def_dim(ncid,trim(hruDimName) ,nHRU , hruDimID); message='iCreate[hru]' ; call netcdf_err(err,message); if(err/=0)return err = nf90_def_dim(ncid,trim(tdhDimName) ,nTimeDelay , tdhDimID); message='iCreate[tdh]' ; call netcdf_err(err,message); if(err/=0)return err = nf90_def_dim(ncid,trim(scalDimName) ,nScalar , scalDimID); message='iCreate[scalar]' ; call netcdf_err(err,message); if(err/=0)return err = nf90_def_dim(ncid,trim(specDimName) ,nSpectral , specDimID); message='iCreate[spectral]'; call netcdf_err(err,message); if(err/=0)return @@ -683,6 +677,9 @@ subroutine writeRestart(filename, & ! intent(in): name of restart file end do ! iGRU loop + ! write HRU dimension and ID for file + call write_hru_info(ncid, err, cmessage); if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + ! close file call nc_file_close(ncid,err,cmessage) if(err/=0)then;message=trim(message)//trim(cmessage);return;end if diff --git a/build/source/openwq/summa_openWQ_allocspace.f90 b/build/source/openwq/summa_openWQ_allocspace.f90 index 491fe99f4..546f03e1a 100644 --- a/build/source/openwq/summa_openWQ_allocspace.f90 +++ b/build/source/openwq/summa_openWQ_allocspace.f90 @@ -30,20 +30,20 @@ module allocspace_progStuct_module ilength, & ! var%dat ! no spatial dimension var_i, & ! x%var(:) (i4b) - var_i8, & ! x%var(:) integer(8) + var_i8, & ! x%var(:) (i8b) var_d, & ! x%var(:) (dp) var_flagVec, & ! x%var(:)%dat (logical) var_ilength, & ! x%var(:)%dat (i4b) var_dlength, & ! x%var(:)%dat (dp) ! gru dimension gru_int, & ! x%gru(:)%var(:) (i4b) - gru_int8, & ! x%gru(:)%var(:) integer(8) + gru_int8, & ! x%gru(:)%var(:) (i8b) gru_double, & ! x%gru(:)%var(:) (dp) gru_intVec, & ! x%gru(:)%var(:)%dat (i4b) gru_doubleVec, & ! x%gru(:)%var(:)%dat (dp) ! gru+hru dimension gru_hru_int, & ! x%gru(:)%hru(:)%var(:) (i4b) - gru_hru_int8, & ! x%gru(:)%hru(:)%var(:) integer(8) + gru_hru_int8, & ! x%gru(:)%hru(:)%var(:) (i8b) gru_hru_double, & ! x%gru(:)%hru(:)%var(:) (dp) gru_hru_intVec, & ! x%gru(:)%hru(:)%var(:)%dat (i4b) gru_hru_doubleVec ! x%gru(:)%hru(:)%var(:)%dat (dp) diff --git a/utils/pre-processing/subsetGRU.sh b/utils/pre-processing/subsetGRU.sh index 9c8fbf7d3..74b86d96d 100755 --- a/utils/pre-processing/subsetGRU.sh +++ b/utils/pre-processing/subsetGRU.sh @@ -2,7 +2,7 @@ # Subset out a NA HRU forcing, parameter, and attribute files where GRU matches HRU # # Inside forcingFile_out need to change forcingPath to = desForcingPath -# Inside forcingFile_out need to change initConditionFile, attributeFile, trialParamFile to = _${GRU_file} versions +# Inside forcingFile_out need to change initConditionFile, attributeFile, trialParamFile to = _${GRU_id} versions # written originally by A. Van Beusekom 2023, hardwired paths run on Copernicus module load StdEnv/2020 @@ -10,22 +10,15 @@ module load gcc/9.3.0 module load nco/5.0.6 # GRU want to subset, change to do another GRU -# 155th basin in the slurm batch run, offset-th script (offset is SLURM_ARRAY_TASK_ID -# could also just set this as GRU_file from G* on output nc files (here the equation output is 487981). +#just set this as GRU_nc from G* on output error (here the equation output is 487981). -offset=942 gruCount=518 -# If know basin number in batch, add here, or comment out and use next if know GRU_file from the failure message id -#nbasin_slurm=25 -#GRU_file=$((nbasin_slurm + gruCount*offset)) +GRU_nc=487981 -GRU_file=487980 -nbasin_slurm=$((GRU_file-gruCount*offset)) - -GRU_id=$GRU_file +GRU_id=$GRU_nc -1 HRU_id=$GRU_id -echo "file name id is ${GRU_file}, GRU id is ${GRU_id}, HRU id is ${HRU_id}, number in batch is ${nbasin_slurm}" +echo "HRU_nc and GRU nc are ${GRU_nc}, HRU and GRU id are ${HRU_id}" # top paths, change these to yours homeDir=/globalhome/gwu479/HPC/ @@ -43,11 +36,11 @@ attributeFile_in=${settingsPath}attributes.nc trialParamFile_in=${settingsPath}trialParams.nc # out paths, probably won't change -fileManager_out=${homePath}fileManager_${GRU_file}.txt -initConditionFile_out=${settingsPath}coldState_${GRU_file}.nc -attributeFile_out=${settingsPath}attributes_${GRU_file}.nc -trialParamFile_out=${settingsPath}trialParams_${GRU_file}.nc -desForcingPath=${desforceDir}summaWorkflow_data/domain_NorthAmerica/forcing/4_SUMMA_input_${GRU_file}/ +fileManager_out=${homePath}fileManager_${GRU_id}.txt +initConditionFile_out=${settingsPath}coldState_${GRU_id}.nc +attributeFile_out=${settingsPath}attributes_${GRU_id}.nc +trialParamFile_out=${settingsPath}trialParams_${GRU_id}.nc +desForcingPath=${desforceDir}summaWorkflow_data/domain_NorthAmerica/forcing/4_SUMMA_input_${GRU_id}/ # set up directory and new file Manager (will have to change things in it manually as above) mkdir -p "$desForcingPath" @@ -71,5 +64,5 @@ done cd $homePath # write summa command call file -runFile=${homePath}run_${GRU_file}.sh +runFile=${homePath}run_${GRU_id}.sh echo "${summa_exe} -p never -s _testSumma -m ${fileManager_out} -r e" > $runFile From a0a0e1d436c82944c941a112d74b3e54176b47c5 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 9 Oct 2024 05:47:15 -0600 Subject: [PATCH 1413/1472] Added -DSPECIFY_LAPACK_LINKS build option so that user-specified LAPACK library links may be used instead of automatic detection (which is the default). --- build/CMakeLists.txt | 27 ++++++++++++++++----------- build/cmake/build.mac.bash | 2 +- build/cmake/build.pc.bash | 3 ++- build/cmake/build_actors.mac.bash | 2 +- build/cmake/build_ngen.mac.bash | 2 +- 5 files changed, 21 insertions(+), 15 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index a9a9ed5b1..bc13f7be0 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -92,20 +92,25 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXEC_DIR}) find_package(NetCDF REQUIRED) list(APPEND EXT_TARGETS NetCDF::NetCDF) -if(APPLE) - # Get most optimized lapack - set(LIBRARY_LINKS $ENV{LIBRARY_LINKS}) - +# Find LAPACK with user-specified links or automatic detection +option(SPECIFY_LAPACK_LINKS "Use specified LAPACK links" OFF) +if(SPECIFY_LAPACK_LINKS) + message("Using LAPACK links from LIBRARY_LINKS environment variable") + if(DEFINED ENV{LIBRARY_LINKS}) + set(LIBRARY_LINKS $ENV{LIBRARY_LINKS}) + else() + message(FATAL_ERROR "LIBRARY_LINKS environment variable not found. Set this variable or try -DSPECIFY_LAPACK_LINKS=OFF in build file.") + endif() else() + message("Automatically detect LAPACK links") # OpenBLAS (MAY HAVE OpenMP enabled which slows execution) # USE LAPACK INSTEAD TO AVOID THIS ISSUE - # message("\nRun export OPENBLAS_NUM_THREADS=1, to ward off bad behavior assuming not using multithreading") + # message("\nRun export OPENBLAS_NUM_THREADS=1, to ward off bad behavior assuming not using multithreading") # set(BLA_VENDOR OpenBLAS) # MKL # find_package(OpenBLAS REQUIRED) # list(APPEND EXT_TARGETS OpenBLAS::OpenBLAS) # LAPACK - set(LIBRARY_LINKS $ENV{LIBRARY_LINKS}) # added for compatibility of PC builds find_package(LAPACK REQUIRED) list(APPEND EXT_TARGETS LAPACK::LAPACK) endif() @@ -115,13 +120,13 @@ set(FLAGS_OPT $ENV{FLAGS_OPT}) # get optional user-specified flags from environm if(CMAKE_BUILD_TYPE MATCHES Debug) message("\nSetting SUMMA Debug Options") add_compile_definitions(DEBUG) - set(FLAGS_NOAH -g -Og -fbacktrace -fcheck=all,no-array-temps -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) - set(FLAGS_ALL -g -Og -fbacktrace -fcheck=all,no-array-temps -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp ${FLAGS_OPT}) - set(FLAGS_CXX -g -Og -fcheck=all,no-array-temps -Wfatal-errors -std=c++17 ${FLAGS_OPT}) + set(FLAGS_NOAH -g -Og -fbacktrace -fcheck=all,no-array-temps -Wfatal-errors -ffree-line-length-none -fmax-errors=0 -fPIC -ffree-form ${FLAGS_OPT}) + set(FLAGS_ALL -g -Og -fbacktrace -fcheck=all,no-array-temps -Wfatal-errors -ffree-line-length-none -fmax-errors=0 -fPIC -cpp ${FLAGS_OPT}) + set(FLAGS_CXX -g -Og -fbacktrace -fcheck=all,no-array-temps -Wfatal-errors -std=c++17 ${FLAGS_OPT}) else() message("\nSetting SUMMA Release Options") - set(FLAGS_NOAH -O3 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors ${FLAGS_OPT}) - set(FLAGS_ALL -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors -cpp ${FLAGS_OPT}) + set(FLAGS_NOAH -O3 -Wfatal-errors -ffree-line-length-none -fmax-errors=0 -fPIC -ffree-form ${FLAGS_OPT}) + set(FLAGS_ALL -O3 -Wfatal-errors -ffree-line-length-none -fmax-errors=0 -fPIC -cpp ${FLAGS_OPT}) set(FLAGS_CXX -O3 -Wfatal-errors -std=c++17 ${FLAGS_OPT}) endif() diff --git a/build/cmake/build.mac.bash b/build/cmake/build.mac.bash index 7dc62df90..db33b5ce2 100755 --- a/build/cmake/build.mac.bash +++ b/build/cmake/build.mac.bash @@ -11,5 +11,5 @@ export FC=/opt/local/bin/gfortran # Fortran compiler export SUNDIALS_DIR=../../../sundials/instdir/ export LIBRARY_LINKS='-llapack' # list of library links -cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DCMAKE_BUILD_TYPE=Release +cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DSPECIFY_LAPACK_LINKS=ON -DCMAKE_BUILD_TYPE=Release cmake --build ../cmake_build --target all -j diff --git a/build/cmake/build.pc.bash b/build/cmake/build.pc.bash index bf9c0c041..73d85f24d 100755 --- a/build/cmake/build.pc.bash +++ b/build/cmake/build.pc.bash @@ -10,7 +10,8 @@ # PC Example using Ubuntu: Intel oneMKL builds (see https://www.intel.com/content/www/us/en/developer/tools/oneapi/onemkl-link-line-advisor.html) #oneAPI_dir=/opt/intel/oneapi # Intel oneAPI main directory #source $oneAPI_dir/setvars.sh # initialize environment variables for Intel oneAPI +#export LIBRARY_LINKS="-m64;-Wl,--start-group ${MKLROOT}/lib/libmkl_gf_lp64.a ${MKLROOT}/lib/libmkl_sequential.a ${MKLROOT}/lib/libmkl_core.a -Wl,--end-group;-lpthread;-lm;-ldl" # static sequential library (i.e., no multithreading) #export FLAGS_OPT="-m64;-I"${MKLROOT}/include";-flto=1;-fuse-linker-plugin" # optional compiler flags -- Intel oneMKL builds -cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DUSE_OPENWQ=OFF #-DCMAKE_BUILD_TYPE=Debug +cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DUSE_OPENWQ=OFF -DSPECIFY_LAPACK_LINKS=ON #-DCMAKE_BUILD_TYPE=Debug cmake --build ../cmake_build --target all -j diff --git a/build/cmake/build_actors.mac.bash b/build/cmake/build_actors.mac.bash index 41b95a0b2..61c38db60 100755 --- a/build/cmake/build_actors.mac.bash +++ b/build/cmake/build_actors.mac.bash @@ -11,5 +11,5 @@ export FC=/opt/local/bin/gfortran # Fortran compiler export SUNDIALS_DIR=../../../sundials/instdir/ export LIBRARY_LINKS='-llapack' # list of library links -cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DUSE_ACTORS=ON +cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DUSE_ACTORS=ON -DSPECIFY_LAPACK_LINKS=ON cmake --build ../cmake_build --target all -j diff --git a/build/cmake/build_ngen.mac.bash b/build/cmake/build_ngen.mac.bash index 42f171c89..eb489f4b8 100755 --- a/build/cmake/build_ngen.mac.bash +++ b/build/cmake/build_ngen.mac.bash @@ -12,7 +12,7 @@ export CPLUS_INCLUDE_PATH=/opt/local/include export SUNDIALS_DIR=../../../sundials/instdir/ export LIBRARY_LINKS='-llapack' # list of library links -cmake -B extern/summa/cmake_build -S extern/summa -DUSE_NEXTGEN=ON +cmake -B extern/summa/cmake_build -S extern/summa -DUSE_NEXTGEN=ON -DSPECIFY_LAPACK_LINKS=ON cmake --build extern/summa/cmake_build --target all -j cmake -B cmake_build -S . -DBoost_INCLUDE_DIR=/opt/local/libexec/boost/1.81/include -DNGEN_WITH_BMI_FORTRAN=ON -DPython_NumPy_INCLUDE_DIR=/opt/local/bin/python -DNGEN_WITH_MPI:BOOL=OFF -DNGEN_WITH_PYTHON:BOOL=OFF -DNGEN_WITH_BMI_C:BOOL=ON -DNGEN_WITH_BMI_FORTRAN:BOOL=ON -DNGEN_WITH_NETCDF:BOOL=OFF From a834707e6845f8802be11bb5014bf0db4b57e031 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 10 Oct 2024 10:57:35 +0900 Subject: [PATCH 1414/1472] a bit more removed for openBLAS default --- build/CMakeLists.txt | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index bd7947a4d..b3256343d 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -93,6 +93,7 @@ find_package(NetCDF REQUIRED) list(APPEND EXT_TARGETS NetCDF::NetCDF) # OpenBLAS +set(BLA_VENDOR OpenBLAS) find_package(OpenBLAS REQUIRED) list(APPEND EXT_TARGETS OpenBLAS::OpenBLAS) @@ -183,11 +184,7 @@ target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) # Build SUMMA_COMM Object add_library(SUMMA_COMM OBJECT ${COMM_ALL}) target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) -if(LIBRARY_LINKS) - target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP ${EXT_TARGETS} ${LIBRARY_LINKS}) -else() - target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP ${EXT_TARGETS}) -endif() +target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP ${EXT_TARGETS}) # For NextGen, build SUMMA Shared Library and add the outside BMI libraries if(USE_NEXTGEN) @@ -211,17 +208,10 @@ if(USE_NEXTGEN) else() add_library(summa SHARED ${SUMMA_ALL}) target_compile_options(summa PRIVATE ${FLAGS_ALL}) - if(LIBRARY_LINKS) - target_link_libraries(summa PUBLIC ${EXT_TARGETS} SUMMA_NOAHMP SUMMA_COMM ${LIBRARY_LINKS}) - else() - target_link_libraries(summa PUBLIC ${EXT_TARGETS} SUMMA_NOAHMP SUMMA_COMM ) - endif() + target_link_libraries(summa PUBLIC ${EXT_TARGETS} SUMMA_NOAHMP SUMMA_COMM ) add_executable(${EXEC_NAME} ${MAIN_SUMMA}) target_compile_options(${EXEC_NAME} PRIVATE ${FLAGS_ALL}) set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) - if(LIBRARY_LINKS) - target_link_libraries(${EXEC_NAME} summa ${EXT_TARGETS} ${LIBRARY_LINKS}) - else() - target_link_libraries(${EXEC_NAME} summa ${EXT_TARGETS}) - endif() + target_link_libraries(${EXEC_NAME} summa ${EXT_TARGETS}) + endif() From b7a3239d9fee341eeaaf7580e81e7b598060822d Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 9 Oct 2024 21:34:03 -0600 Subject: [PATCH 1415/1472] Added ifx compoiler flags for release builds -- some derived type definitions will have to be updated before compiling with ifx is successful. --- build/CMakeLists.txt | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index bc13f7be0..1f3be16d8 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -124,10 +124,16 @@ if(CMAKE_BUILD_TYPE MATCHES Debug) set(FLAGS_ALL -g -Og -fbacktrace -fcheck=all,no-array-temps -Wfatal-errors -ffree-line-length-none -fmax-errors=0 -fPIC -cpp ${FLAGS_OPT}) set(FLAGS_CXX -g -Og -fbacktrace -fcheck=all,no-array-temps -Wfatal-errors -std=c++17 ${FLAGS_OPT}) else() - message("\nSetting SUMMA Release Options") - set(FLAGS_NOAH -O3 -Wfatal-errors -ffree-line-length-none -fmax-errors=0 -fPIC -ffree-form ${FLAGS_OPT}) - set(FLAGS_ALL -O3 -Wfatal-errors -ffree-line-length-none -fmax-errors=0 -fPIC -cpp ${FLAGS_OPT}) - set(FLAGS_CXX -O3 -Wfatal-errors -std=c++17 ${FLAGS_OPT}) + message("\nSetting SUMMA Release Options") + if (CMAKE_Fortran_COMPILER MATCHES gfortran) + set(FLAGS_NOAH -O3 -Wfatal-errors -ffree-line-length-none -fmax-errors=0 -fPIC -ffree-form ${FLAGS_OPT}) + set(FLAGS_ALL -O3 -Wfatal-errors -ffree-line-length-none -fmax-errors=0 -fPIC -cpp ${FLAGS_OPT}) + set(FLAGS_CXX -O3 -Wfatal-errors -std=c++17 ${FLAGS_OPT}) + else(CMAKE_Fortran_COMPILER MATCHES ifx) # ifx does not currently compile SUMMA -- derived type modifications needed in source code + set(FLAGS_NOAH -O3 -fPIC -free ${FLAGS_OPT}) + set(FLAGS_ALL -O3 -fPIC -cxxlib ${FLAGS_OPT}) + set(FLAGS_CXX -O3 -std=c++17 ${FLAGS_OPT}) + endif() endif() #========================================================================================= From d9f5e7842736202491ff4243fc3b7031b52d57e0 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 9 Oct 2024 21:51:28 -0600 Subject: [PATCH 1416/1472] Minor change to opSplittin for array assignment. --- build/source/engine/opSplittin.f90 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index e211db12b..163899950 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -1458,8 +1458,7 @@ subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) subroutine fullyCoupled_stateMask ! *** Get fully coupled stateMask *** - !stateMask(:) = .true. ! use all state variables - stateMask = .true. ! use all state variables + stateMask(:) = .true. ! use all state variables end subroutine fullyCoupled_stateMask subroutine stateTypeSplit_stateMask From 941b4caf6d6e322e786982c67d7b016e772c9ea7 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 9 Oct 2024 22:40:14 -0600 Subject: [PATCH 1417/1472] Enabled LIBRARY_LINKS environment variable in link stages for SUMMA objects. --- build/CMakeLists.txt | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 21c5d16eb..544edbb3a 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -103,6 +103,7 @@ if(SPECIFY_LAPACK_LINKS) endif() else() message("Automatically detect LAPACK links") + set(LIBRARY_LINKS "") ## LAPACK #find_package(LAPACK REQUIRED) @@ -207,34 +208,32 @@ target_compile_options(SUMMA_NOAHMP PRIVATE ${FLAGS_NOAH}) # Build SUMMA_COMM Object add_library(SUMMA_COMM OBJECT ${COMM_ALL}) target_compile_options(SUMMA_COMM PRIVATE ${FLAGS_ALL}) -target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP ${EXT_TARGETS}) +target_link_libraries(SUMMA_COMM PUBLIC SUMMA_NOAHMP ${EXT_TARGETS} ${LIBRARY_LINKS}) # For NextGen, build SUMMA Shared Library and add the outside BMI libraries if(USE_NEXTGEN) - if(WIN32) - add_library(summabmi ${SUMMA_ALL}) - else() - add_library(summabmi SHARED ${SUMMA_ALL}) - endif() - target_compile_options(summabmi PRIVATE ${FLAGS_ALL}) + if(WIN32) + add_library(summabmi ${SUMMA_ALL}) + else() + add_library(summabmi SHARED ${SUMMA_ALL}) + endif() + target_compile_options(summabmi PRIVATE ${FLAGS_ALL}) target_link_libraries(summabmi PUBLIC ${EXT_TARGETS} iso_c_bmi SUMMA_NOAHMP SUMMA_COMM) - set_target_properties(summabmi PROPERTIES VERSION ${PROJECT_VERSION}) - include(GNUInstallDirs) - - install(TARGETS summabmi - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - configure_file(summabmi.pc.in summabmi.pc @ONLY) - install(FILES ${CMAKE_BINARY_DIR}/summabmi.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) + set_target_properties(summabmi PROPERTIES VERSION ${PROJECT_VERSION}) + include(GNUInstallDirs) + install(TARGETS summabmi + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + configure_file(summabmi.pc.in summabmi.pc @ONLY) + install(FILES ${CMAKE_BINARY_DIR}/summabmi.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) else() add_library(summa SHARED ${SUMMA_ALL}) target_compile_options(summa PRIVATE ${FLAGS_ALL}) - target_link_libraries(summa PUBLIC ${EXT_TARGETS} SUMMA_NOAHMP SUMMA_COMM ) - add_executable(${EXEC_NAME} ${MAIN_SUMMA}) - target_compile_options(${EXEC_NAME} PRIVATE ${FLAGS_ALL}) - set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) - target_link_libraries(${EXEC_NAME} summa ${EXT_TARGETS}) - + target_link_libraries(summa PUBLIC ${EXT_TARGETS} SUMMA_NOAHMP SUMMA_COMM ${LIBRARY_LINKS}) + add_executable(${EXEC_NAME} ${MAIN_SUMMA}) + target_compile_options(${EXEC_NAME} PRIVATE ${FLAGS_ALL}) + set_property(TARGET ${EXEC_NAME} PROPERTY LINKER_LANGUAGE Fortran) + target_link_libraries(${EXEC_NAME} summa ${EXT_TARGETS} ${LIBRARY_LINKS}) endif() From 3a2108ffe5957c8379278d42fe3741d71b7a7edd Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 10 Oct 2024 01:27:05 -0600 Subject: [PATCH 1418/1472] Update to CMake so that serial OpenBLAS is loaded using find_package - disabled call to openblas_set_num_threads as it is no longer needed. --- build/CMakeLists.txt | 17 +++++++++-------- build/source/engine/matrixOper.f90 | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 544edbb3a..daed0569b 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -111,23 +111,24 @@ else() # OpenBLAS set(BLA_VENDOR OpenBLAS) - find_package(OpenBLAS REQUIRED) + #find_package(OpenBLAS REQUIRED) + find_package(OpenBLAS REQUIRED COMPONENTS serial) list(APPEND EXT_TARGETS OpenBLAS::OpenBLAS) endif() # Set compiler flags set(FLAGS_OPT $ENV{FLAGS_OPT}) # get optional user-specified flags from environment variables if(CMAKE_BUILD_TYPE MATCHES Debug) - message("\nSetting SUMMA Debug Options") - add_compile_definitions(DEBUG) - set(FLAGS_NOAH -g -Og -fbacktrace -fcheck=all,no-array-temps -Wfatal-errors -ffree-line-length-none -fmax-errors=0 -fPIC -ffree-form ${FLAGS_OPT}) - set(FLAGS_ALL -g -Og -fbacktrace -fcheck=all,no-array-temps -Wfatal-errors -ffree-line-length-none -fmax-errors=0 -fPIC -cpp ${FLAGS_OPT}) - set(FLAGS_CXX -g -Og -fbacktrace -fcheck=all,no-array-temps -Wfatal-errors -std=c++17 ${FLAGS_OPT}) + message("\nSetting SUMMA Debug Options") + add_compile_definitions(DEBUG) + set(FLAGS_NOAH -g -Og -fbacktrace -fcheck=all,no-array-temps -Wfatal-errors -ffree-line-length-none -fPIC -ffree-form ${FLAGS_OPT}) + set(FLAGS_ALL -g -Og -fbacktrace -fcheck=all,no-array-temps -Wfatal-errors -ffree-line-length-none -fPIC -cpp ${FLAGS_OPT}) + set(FLAGS_CXX -g -Og -fbacktrace -fcheck=all,no-array-temps -Wfatal-errors -std=c++17 ${FLAGS_OPT}) else() message("\nSetting SUMMA Release Options") if (CMAKE_Fortran_COMPILER MATCHES gfortran) - set(FLAGS_NOAH -O3 -Wfatal-errors -ffree-line-length-none -fmax-errors=0 -fPIC -ffree-form ${FLAGS_OPT}) - set(FLAGS_ALL -O3 -Wfatal-errors -ffree-line-length-none -fmax-errors=0 -fPIC -cpp ${FLAGS_OPT}) + set(FLAGS_NOAH -O3 -Wfatal-errors -ffree-line-length-none -fPIC -ffree-form ${FLAGS_OPT}) + set(FLAGS_ALL -O3 -Wfatal-errors -ffree-line-length-none -fPIC -cpp ${FLAGS_OPT}) set(FLAGS_CXX -O3 -Wfatal-errors -std=c++17 ${FLAGS_OPT}) else(CMAKE_Fortran_COMPILER MATCHES ifx) # ifx does not currently compile SUMMA -- derived type modifications needed in source code set(FLAGS_NOAH -O3 -fPIC -free ${FLAGS_OPT}) diff --git a/build/source/engine/matrixOper.f90 b/build/source/engine/matrixOper.f90 index 5e468dd1d..0c2307bec 100644 --- a/build/source/engine/matrixOper.f90 +++ b/build/source/engine/matrixOper.f90 @@ -173,7 +173,7 @@ subroutine lapackSolv(ixMatrix,nState,aJac,rVec,xInc,err,message) case default; err=20; message=trim(message)//'unable to identify option for the type of matrix' end select - call openblas_set_num_threads(1) ! set the number of threads to 1 + !call openblas_set_num_threads(1) ! set the number of threads to 1 ! form the rhs matrix ! NOTE: copy the vector here to ensure that the residual vector is not overwritten From 35ce7b57369b28fa96dc9c80e40113a916fad513 Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 11 Oct 2024 03:28:46 -0600 Subject: [PATCH 1419/1472] Removed (or commented out) unused variables detected by gcc 13.2.0. --- build/source/engine/check_icond.f90 | 6 +++--- build/source/engine/coupled_em.f90 | 4 ++-- build/source/engine/eval8summa.f90 | 6 +++--- build/source/engine/eval8summaWithPrime.f90 | 2 +- build/source/engine/opSplittin.f90 | 20 +++++++++++++++----- build/source/engine/soilLiqFlx.f90 | 2 +- build/source/engine/soil_utilsAddPrime.f90 | 2 +- build/source/engine/ssdNrgFlux.f90 | 2 +- build/source/engine/summaSolve4ida.f90 | 6 +++--- build/source/engine/summaSolve4kinsol.f90 | 4 ++-- build/source/engine/updateVars.f90 | 2 +- build/source/engine/vegNrgFlux.f90 | 2 +- build/source/netcdf/read_icond.f90 | 4 ++-- 13 files changed, 36 insertions(+), 26 deletions(-) diff --git a/build/source/engine/check_icond.f90 b/build/source/engine/check_icond.f90 index ab052a426..9dfb0de20 100644 --- a/build/source/engine/check_icond.f90 +++ b/build/source/engine/check_icond.f90 @@ -87,20 +87,20 @@ subroutine check_icond(nGRU, & ! intent(in): number character(*),intent(out) :: message ! returned error message ! locals character(len=256) :: cmessage ! downstream error message - integer(i4b) :: i,iGRU,iHRU ! loop index + integer(i4b) :: iGRU,iHRU ! loop index ! temporary variables for realism checks integer(i4b) :: iLayer ! index of model layer integer(i4b) :: iSoil ! index of soil layer real(rkind) :: fLiq ! fraction of liquid water on the vegetation canopy (-) real(rkind) :: vGn_m ! van Genutchen "m" parameter (-) - real(rkind) :: tWat ! total water on the vegetation canopy (kg m-2) + !real(rkind) :: tWat ! total water on the vegetation canopy (kg m-2) real(rkind) :: scalarTheta ! liquid water equivalent of total water [liquid water + ice] (-) real(rkind) :: h1,h2 ! used to check depth and height are consistent real(rkind) :: kappa ! constant in the freezing curve function (m K-1) integer(i4b) :: nSoil ! number of soil layers integer(i4b) :: nSnow ! number of snow layers integer(i4b) :: nLayers ! total number of layers - integer(i4b) :: nState ! total number of states + !integer(i4b) :: nState ! total number of states real(rkind),parameter :: xTol=1.e-10_rkind ! small tolerance to address precision issues real(rkind),parameter :: canIceTol=1.e-3_rkind ! small tolerance to allow existence of canopy ice for above-freezing temperatures (kg m-2) ! -------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 7e4cffc54..f4fa7851e 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -208,9 +208,9 @@ subroutine coupled_em(& real(rkind) :: dCanopyWetFraction_dT ! derivative in wetted fraction w.r.t. canopy temperature (K-1) real(rkind),parameter :: varNotUsed1=-9999._rkind ! variables used to calculate derivatives (not needed here) real(rkind),parameter :: varNotUsed2=-9999._rkind ! variables used to calculate derivatives (not needed here) - integer(i4b) :: iSnow ! index of snow layers + !integer(i4b) :: iSnow ! index of snow layers integer(i4b) :: iLayer ! index of model layers - real(rkind) :: massLiquid ! mass liquid water (kg m-2) + !real(rkind) :: massLiquid ! mass liquid water (kg m-2) real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) integer(i4b) :: ixSolution ! solution method used by opSplittin diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 3f54882f4..0aefbf1f6 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -209,13 +209,13 @@ subroutine eval8summa(& real(rkind),dimension(nLayers) :: mLayerEnthTempTrial ! trial vector of temperature component of enthalpy for snow+soil layers (J m-3) ! other local variables logical(lgt) :: checkLWBalance ! flag to check longwave balance - integer(i4b) :: iLayer ! index of model layer in the snow+soil domain + !integer(i4b) :: iLayer ! index of model layer in the snow+soil domain integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine real(rkind),dimension(nState) :: rVecScaled ! scaled residual vector character(LEN=256) :: cmessage ! error message of downwind routine - real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy - real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil + !real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy + !real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil logical(lgt) :: updateStateCp ! flag to indicate if we update Cp at each step for LHS, set with nrgConserv choice and updateCp_closedForm flag logical(lgt) :: updateFluxCp ! flag to indicate if we update Cp at each step for RHS, set with nrgConserv choice and updateCp_closedForm flag logical(lgt) :: needStateCm ! flag to indicate if the energy equation contains LHS Cm = dH_T/dTheta_m,, set with nrgConserv choice and needStateCm_closedForm flag diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index fdf1875c3..8151c331a 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -206,7 +206,7 @@ subroutine eval8summaWithPrime(& real(rkind) :: scalarCanopyNrgPrime ! prime value for energy of the vegetation canopy real(rkind),dimension(nLayers) :: mLayerNrgPrime ! prime vector of energy of each snow and soil layer ! other local variables - integer(i4b) :: iLayer ! index of model layer in the snow+soil domain + !integer(i4b) :: iLayer ! index of model layer in the snow+soil domain integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine character(LEN=256) :: cmessage ! error message of downwind routine diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 163899950..39b42b620 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -275,7 +275,6 @@ subroutine opSplittin(& integer(i4b) :: iOffset ! offset to account for different indices in the soil domain integer(i4b) :: iMin(1),iMax(1) ! bounds of a given vector integer(i4b) :: iLayer,jLayer ! index of model layer - integer(i4b) :: iSoil ! index of soil layer integer(i4b) :: iVar ! index of variables in data structures logical(lgt) :: firstSuccess ! flag to define the first success logical(lgt) :: firstFluxCall ! flag to define the first flux call @@ -335,9 +334,9 @@ subroutine opSplittin(& logical(lgt) :: addFirstFlux ! flag to add the first flux to the mask ! splitting method control variables logical(lgt) :: exit_split_select,cycle_split_select ! control for split_select loop - logical(lgt) :: exit_coupling,exit_stateTypeSplitting,exit_stateThenDomain,exit_domainSplit,exit_solution,exit_stateSplit - logical(lgt) :: cycle_coupling,cycle_stateTypeSplitting,cycle_stateThenDomain,cycle_domainSplit,cycle_solution,cycle_stateSplit - integer(i4b) :: iSplit,nSplit + logical(lgt) :: exit_coupling,exit_stateThenDomain,exit_solution + logical(lgt) :: cycle_coupling,cycle_stateThenDomain,cycle_domainSplit,cycle_solution + integer(i4b) :: iSplit integer(i4b),parameter :: maxSplit=500 ! >= max number of splitting methods (controls upper limit of split_select loop) ! ------------------------ classes for subroutine arguments (classes defined in data_types module) ------------------------ ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** @@ -1384,6 +1383,7 @@ subroutine split_select_compute_stateMask(split_select,indx_data,err,cmessage,me type(in_type_stateFilter) :: in_stateFilter ! indices type(out_type_stateFilter) :: out_stateFilter ! number of selected state variables for a given split and error control + err=0 ! SJT TEST return_flag=.false. ! initialize flag associate(& ixCoupling => split_select % ixCoupling ,& @@ -1397,11 +1397,15 @@ subroutine split_select_compute_stateMask(split_select,indx_data,err,cmessage,me !associate(stateMask => split_select % stateMask) ! call stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) !end associate + !print *, "SJT 0",err call stateFilter(in_stateFilter,indx_data,split_select % stateMask,out_stateFilter) + print *, "SJT 4.1",out_stateFilter%err associate(nSubset => split_select % nSubset) call out_stateFilter % finalize(nSubset,err,cmessage) end associate + print *, "SJT 5",err if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! error control + print *, "SJT 6" end subroutine split_select_compute_stateMask @@ -1417,7 +1421,8 @@ subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) type(var_ilength),intent(in) :: indx_data ! indices for a local HRU ! output !logical(lgt),intent(out) :: stateMask(:) ! mask defining desired state variables - logical(lgt),allocatable,intent(inout) :: stateMask(:) ! mask defining desired state variables + !logical(lgt),allocatable,intent(inout) :: stateMask(:) ! mask defining desired state variables + logical(lgt),intent(inout) :: stateMask(:) ! mask defining desired state variables type(out_type_stateFilter),intent(out) :: out_stateFilter ! number of selected state variables for a given split and error control ! local integer(i4b),allocatable :: ixSubset(:) ! list of indices in the state subset @@ -1454,11 +1459,16 @@ subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) nSubset = count(stateMask) end associate + print *, "SJT 4" + contains subroutine fullyCoupled_stateMask ! *** Get fully coupled stateMask *** + print *, "SJT1:",stateMask + print *, "SJT2:",size(stateMask) stateMask(:) = .true. ! use all state variables + print *, "SJT3:",stateMask end subroutine fullyCoupled_stateMask subroutine stateTypeSplit_stateMask diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index a63b41031..e99049eac 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -140,7 +140,7 @@ subroutine soilLiqFlx(& integer(i4b) :: ixIce ! index of the lowest soil layer that contains ice real(rkind),dimension(0:in_soilLiqFlx % nSoil) :: iLayerHeight ! height of the layer interfaces (m) ! compute fluxes and derivatives at layer interfaces - real(rkind) :: scalardPsi_dTheta ! derivative in soil water characteristix, used for perturbations when computing numerical derivatives + !real(rkind) :: scalardPsi_dTheta ! derivative in soil water characteristix, used for perturbations when computing numerical derivatives ! ------------------------------------------------------------------------------------------------------------------------------------------------- nSoil = in_soilLiqFlx % nSoil ! get number of soil layers from input arguments diff --git a/build/source/engine/soil_utilsAddPrime.f90 b/build/source/engine/soil_utilsAddPrime.f90 index dceb711c2..5a7ee6521 100644 --- a/build/source/engine/soil_utilsAddPrime.f90 +++ b/build/source/engine/soil_utilsAddPrime.f90 @@ -96,7 +96,7 @@ subroutine liquidHeadPrime(& real(rkind) :: effSat ! effective saturation (-) real(rkind) :: dPsiLiq_dEffSat ! derivative in liquid water matric potential w.r.t. effective saturation (m) real(rkind) :: dEffSat_dTemp ! derivative in effective saturation w.r.t. temperature (K-1) - real(rkind) :: dEffSat_dFracLiq ! derivative in effective saturation w.r.t. liquid water fraction (-) + !real(rkind) :: dEffSat_dFracLiq ! derivative in effective saturation w.r.t. liquid water fraction (-) real(rkind) :: effSatPrime ! effective saturation time derivative (-) ! ------------------------------------------------------------------------------------------------------------------------------ ! initialize error control diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index 92e2a2341..14cba85a3 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -102,7 +102,7 @@ subroutine ssdNrgFlux(& type(out_type_ssdNrgFlux),intent(inout) :: out_ssdNrgFlux ! output ssdNrgFlux arguments ! ------------------------------------------------------------------------------------------------------------------------------------------------------ ! local variables - character(LEN=256) :: cmessage ! error message of downwind routine + !character(LEN=256) :: cmessage ! error message of downwind routine integer(i4b) :: nLayers ! number of model layers integer(i4b) :: iLayer ! index of model layers integer(i4b) :: ixLayerDesired(1) ! layer desired (scalar solution) diff --git a/build/source/engine/summaSolve4ida.f90 b/build/source/engine/summaSolve4ida.f90 index 9decef204..572f63a1a 100644 --- a/build/source/engine/summaSolve4ida.f90 +++ b/build/source/engine/summaSolve4ida.f90 @@ -213,10 +213,10 @@ subroutine summaSolve4ida(& ! -------------------------------------------------------------------------------------------------------------------------------- type(N_Vector), pointer :: sunvec_y ! sundials solution vector type(N_Vector), pointer :: sunvec_yp ! sundials derivative vector - type(N_Vector), pointer :: sunvec_av ! sundials tolerance vector + !type(N_Vector), pointer :: sunvec_av ! sundials tolerance vector type(SUNMatrix), pointer :: sunmat_A ! sundials matrix type(SUNLinearSolver), pointer :: sunlinsol_LS ! sundials linear solver - type(SUNNonLinearSolver), pointer :: sunnonlin_NLS ! sundials nonlinear solver + !type(SUNNonLinearSolver), pointer :: sunnonlin_NLS ! sundials nonlinear solver type(c_ptr) :: ida_mem ! IDA memory type(c_ptr) :: sunctx ! SUNDIALS simulation context type(data4ida), target :: eqns_data ! IDA type @@ -1000,4 +1000,4 @@ subroutine getErrMessage(retval,message) end subroutine getErrMessage -end module summaSolve4ida_module \ No newline at end of file +end module summaSolve4ida_module diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index d2aea62f6..525ec43a6 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -205,7 +205,7 @@ subroutine summaSolve4kinsol(& logical(lgt) :: feasible ! feasibility flag integer(c_long) :: mu, lu ! in banded matrix mode in SUNDIALS type integer(c_long) :: nState ! total number of state variables in SUNDIALS type - integer(i4b) :: iVar, i ! indices + !integer(i4b) :: iVar, i ! indices character(LEN=256) :: cmessage ! error message of downwind routine logical(lgt) :: use_fdJac ! flag to use finite difference Jacobian, controlled by decision fDerivMeth logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true @@ -534,4 +534,4 @@ subroutine getErrMessage(retval,message) end subroutine getErrMessage -end module summaSolve4kinsol_module \ No newline at end of file +end module summaSolve4kinsol_module diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 index ed9ea7dc7..2283f4a93 100644 --- a/build/source/engine/updateVars.f90 +++ b/build/source/engine/updateVars.f90 @@ -181,7 +181,7 @@ subroutine updateVars(& real(rkind) :: scalarVolFracIce ! volumetric fraction of ice (-) real(rkind) :: Tcrit ! critical soil temperature below which ice exists (K) real(rkind) :: xTemp ! temporary temperature (K) - real(rkind) :: fLiq ! fraction of liquid water (-) + !real(rkind) :: fLiq ! fraction of liquid water (-) real(rkind) :: effSat ! effective saturation (-) real(rkind) :: avPore ! available pore space (-) character(len=256) :: cMessage ! error message of downwind routine diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 61071342a..1b1b5eff9 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -179,7 +179,7 @@ subroutine vegNrgFlux(& real(rkind) :: dSVPGround_dGroundTemp ! derivative in ground saturated vapor pressure w.r.t. ground temperature (Pa/K) ! wetted canopy area real(rkind) :: fracLiquidCanopy ! fraction of liquid water in the canopy (-) - real(rkind) :: canopyWetFraction ! trial value of the canopy wetted fraction (-) + !real(rkind) :: canopyWetFraction ! trial value of the canopy wetted fraction (-) real(rkind) :: dCanopyWetFraction_dWat ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) real(rkind) :: dCanopyWetFraction_dT ! derivative in wetted fraction w.r.t. canopy temperature (K-1) ! longwave radiation diff --git a/build/source/netcdf/read_icond.f90 b/build/source/netcdf/read_icond.f90 index bc96f2e3e..c491fca3a 100644 --- a/build/source/netcdf/read_icond.f90 +++ b/build/source/netcdf/read_icond.f90 @@ -64,7 +64,7 @@ subroutine read_icond_nlayers(iconFile,nGRU,indx_meta,err,message) integer(i4b) :: fileHRU ! number of HRUs in netcdf file integer(i4b) :: snowID, soilID ! netcdf variable ids integer(i4b) :: iGRU, iHRU ! loop indexes - integer(i4b) :: iHRU_local ! index of HRU in the data subset + !integer(i4b) :: iHRU_local ! index of HRU in the data subset integer(i4b) :: iHRU_global ! index of HRU in the netcdf file integer(i4b),allocatable :: snowData(:) ! number of snow layers in all HRUs integer(i4b),allocatable :: soilData(:) ! number of soil layers in all HRUs @@ -202,7 +202,7 @@ subroutine read_icond(iconFile, & ! intent(in): name of integer(i4b) :: nTDH ! number of points in time-delay histogram integer(i4b) :: iLayer,jLayer ! layer indices integer(i4b),parameter :: nBand=2 ! number of spectral bands - integer(i4b) :: nProgVars ! number of prognostic variables written to state file + !integer(i4b) :: nProgVars ! number of prognostic variables written to state file character(len=32),parameter :: scalDimName ='scalarv' ! dimension name for scalar data character(len=32),parameter :: midSoilDimName='midSoil' ! dimension name for soil-only layers character(len=32),parameter :: midTotoDimName='midToto' ! dimension name for layered varaiables From c0eba737c525a13a95a11dce3f8775f7a3351ba7 Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 11 Oct 2024 03:58:17 -0600 Subject: [PATCH 1420/1472] Removed non-conforming tab characters - replaced with standard space characters. --- build/source/engine/check_icond.f90 | 2 +- build/source/engine/ssdNrgFlux.f90 | 36 ++++++++++++++--------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/build/source/engine/check_icond.f90 b/build/source/engine/check_icond.f90 index 9dfb0de20..0f3729230 100644 --- a/build/source/engine/check_icond.f90 +++ b/build/source/engine/check_icond.f90 @@ -179,7 +179,7 @@ subroutine check_icond(nGRU, & ! intent(in): number err=20; return else if(scalarCanopyIce > 0._rkind .and. scalarCanopyTemp > Tfreeze)then ! if here, ice content < threshold. Could be sublimation on previous timestep or simply wrong input. Print a warning - write(*,'(A,E22.16,A,F7.3,A,F7.3,A)') 'Warning: canopy ice content in restart file (=',scalarCanopyIce,') > 0 when canopy temperature (=',scalarCanopyTemp,') > Tfreeze (=',Tfreeze,'). Continuing.',NEW_LINE('a') + write(*,'(A,E22.16,A,F7.3,A,F7.3,A)') 'Warning: canopy ice content in restart file (=',scalarCanopyIce,') > 0 when canopy temperature (=',scalarCanopyTemp,') > Tfreeze (=',Tfreeze,'). Continuing.',NEW_LINE('a') end if scalarTheta = scalarCanopyIce + scalarCanopyLiq diff --git a/build/source/engine/ssdNrgFlux.f90 b/build/source/engine/ssdNrgFlux.f90 index 14cba85a3..64a7a1793 100644 --- a/build/source/engine/ssdNrgFlux.f90 +++ b/build/source/engine/ssdNrgFlux.f90 @@ -234,29 +234,29 @@ subroutine ssdNrgFlux(& dFlux_dWatBelow(nLayers) = -huge(lowerBoundTemp) ! don't expect this to be used, so deliberately set to a ridiculous value to cause problems ! ***** the upper boundary, always do - select case(ix_bcUpprTdyn) + select case(ix_bcUpprTdyn) - ! * prescribed temperature at the upper boundary - case(prescribedTemp) - dz = mLayerHeight(1)*0.5_rkind - dFlux_dWatBelow(0) = -dThermalC_dWatBelow(0) * ( mLayerTempTrial(1) - upperBoundTemp )/dz - dFlux_dTempBelow(0) = -dThermalC_dTempBelow(0) * ( mLayerTempTrial(1) - upperBoundTemp )/dz - iLayerThermalC(0)/dz + ! * prescribed temperature at the upper boundary + case(prescribedTemp) + dz = mLayerHeight(1)*0.5_rkind + dFlux_dWatBelow(0) = -dThermalC_dWatBelow(0) * ( mLayerTempTrial(1) - upperBoundTemp )/dz + dFlux_dTempBelow(0) = -dThermalC_dTempBelow(0) * ( mLayerTempTrial(1) - upperBoundTemp )/dz - iLayerThermalC(0)/dz - ! * zero flux at the upper boundary - case(zeroFlux) - dFlux_dWatBelow(0) = 0._rkind - dFlux_dTempBelow(0) = 0._rkind + ! * zero flux at the upper boundary + case(zeroFlux) + dFlux_dWatBelow(0) = 0._rkind + dFlux_dTempBelow(0) = 0._rkind - ! * compute flux inside vegetation energy flux routine, use here - case(energyFlux) - dFlux_dWatBelow(0) = 0._rkind !dGroundNetFlux_dGroundWat, does not exist in vegNrgFlux - dFlux_dTempBelow(0) = dGroundNetFlux_dGroundTemp + ! * compute flux inside vegetation energy flux routine, use here + case(energyFlux) + dFlux_dWatBelow(0) = 0._rkind !dGroundNetFlux_dGroundWat, does not exist in vegNrgFlux + dFlux_dTempBelow(0) = dGroundNetFlux_dGroundTemp - case default; err=20; message=trim(message)//'unable to identify upper boundary condition for thermodynamics'; return + case default; err=20; message=trim(message)//'unable to identify upper boundary condition for thermodynamics'; return - end select ! end identifying the upper boundary condition for thermodynamics - !dGroundNetFlux_dGroundWat = dFlux_dWatBelow(0) ! this is true, but since not used in vegNrgFlux do not define - dGroundNetFlux_dGroundTemp = dFlux_dTempBelow(0) ! need this in vegNrgFlux + end select ! end identifying the upper boundary condition for thermodynamics + !dGroundNetFlux_dGroundWat = dFlux_dWatBelow(0) ! this is true, but since not used in vegNrgFlux do not define + dGroundNetFlux_dGroundTemp = dFlux_dTempBelow(0) ! need this in vegNrgFlux ! loop through INTERFACES... do iLayer=ixTop,ixBot From 83687a895b52f6b437d164505c0680de77ac1bfe Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 11 Oct 2024 22:11:11 +0900 Subject: [PATCH 1421/1472] bug in enthalpy restart --- build/source/netcdf/read_icond.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/netcdf/read_icond.f90 b/build/source/netcdf/read_icond.f90 index bc96f2e3e..6946d393c 100644 --- a/build/source/netcdf/read_icond.f90 +++ b/build/source/netcdf/read_icond.f90 @@ -240,7 +240,7 @@ subroutine read_icond(iconFile, & ! intent(in): name of if(err/=nf90_noerr)then if(prog_meta(iVar)%varName=='scalarCanairEnthalpy' .or. & prog_meta(iVar)%varName=='scalarCanopyEnthalpy' .or. & - prog_meta(iVar)%varName=='mLayerEnthalpy' ) err=nf90_noerr; no_icond_enth=.true.; cycle ! skip enthalpy variables if not in file + prog_meta(iVar)%varName=='mLayerEnthalpy' )then; err=nf90_noerr; no_icond_enth=.true.; cycle; endif ! skip enthalpy variables if not in file call netcdf_err(err,message) message=trim(message)//': problem with getting variable id, var='//trim(prog_meta(iVar)%varName) return From 7fd55a16f65061b52470845c53967bc9ca4767e1 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 12 Oct 2024 05:28:34 -0600 Subject: [PATCH 1422/1472] Added initialize_coupled_em internal subroutine to remedy potential use of uninitialized variables in computations. --- build/source/engine/coupled_em.f90 | 36 +++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index f4fa7851e..5efaeaed1 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -313,6 +313,9 @@ subroutine coupled_em(& ! check if the aquifer is included includeAquifer = (model_decisions(iLookDECISIONS%groundwatr)%iDecision==bigBucket) + ! initialize variables + call initialize_coupled_em + ! initialize the numerix tracking variables indx_data%var(iLookINDEX%numberFluxCalc )%dat(1) = 0 ! number of flux calculations (-) indx_data%var(iLookINDEX%numberStateSplit )%dat(1) = 0 ! number of state splitting solutions (-) @@ -911,6 +914,7 @@ subroutine coupled_em(& ! save volumetric ice content at the start of the step ! NOTE: used for volumetric loss due to melt-freeze + if (allocated(mLayerVolFracIceInit)) deallocate(mLayerVolFracIceInit) ! prep for potential size change allocate(mLayerVolFracIceInit(nLayers)); mLayerVolFracIceInit = prog_data%var(iLookPROG%mLayerVolFracIce)%dat ! make sure have consistent state variables to start, later done in updateVars @@ -988,8 +992,10 @@ subroutine coupled_em(& end do innerEffRainfall = 0._rkind ! mean total effective rainfall over snow innerSoilCompress = 0._rkind ! mean total soil compression - innerBalance = 0._rkind ! mean total balance + innerBalance = 0._rkind ! mean total balance array + if (allocated(innerBalanceLayerNrg)) deallocate(innerBalanceLayerNrg) allocate(innerBalanceLayerNrg(nLayers)); innerBalanceLayerNrg = 0._rkind ! mean total balance of energy in layers + if (allocated(innerBalanceLayerMass)) deallocate(innerBalanceLayerMass) ! deallocate if already allocated to permit size change allocate(innerBalanceLayerMass(nLayers)); innerBalanceLayerMass = 0._rkind ! mean total balance of mass in layers sumStepSize= 0._rkind ! initialize the sum of the step sizes @@ -1677,6 +1683,34 @@ subroutine coupled_em(& ! get the elapsed time diag_data%var(iLookDIAG%wallClockTime)%dat(1) = elapsed_time +contains + + subroutine initialize_coupled_em + ! *** Initialize steps for coupled_em subroutine *** + ! Notes: - created to ensure certain variables are initialized prior to use in calculations + ! - based on warnings from the SUMMA debug build (e.g., -Wall flag) + ! - additional initial operations may be added here in the future + + ! initialize variables + innerEffRainfall=0._rkind ! inner step average effective rainfall into snow (kg m-2 s-1) + sumCanopySublimation=0._rkind ! sum of sublimation from the vegetation canopy (kg m-2 s-1) over substep + sumLatHeatCanopyEvap=0._rkind ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) over substep + sumSenHeatCanopy=0._rkind ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) over substep + sumSnowSublimation=0._rkind ! sum of sublimation from the snow surface (kg m-2 s-1) over substep + sumStepSize=0._rkind ! sum solution step for the data step + innerBalance = 0._rkind ! mean total balance array + + ! get initial value of nLayers + nSnow = count(indx_data%var(iLookINDEX%layerType)%dat==iname_snow) + nSoil = count(indx_data%var(iLookINDEX%layerType)%dat==iname_soil) + nLayers = nSnow + nSoil + + ! allocate and initialize using the initial value of nLayers + allocate(innerBalanceLayerMass(nLayers)); innerBalanceLayerMass = 0._rkind ! mean total balance of mass in layers + allocate(innerBalanceLayerNrg(nLayers)); innerBalanceLayerNrg = 0._rkind ! mean total balance of energy in layers + allocate(mLayerVolFracIceInit(nLayers)); mLayerVolFracIceInit = prog_data%var(iLookPROG%mLayerVolFracIce)%dat ! volume fraction of water ice + end subroutine initialize_coupled_em + end subroutine coupled_em From d61499100fe5ba23fa153dd0383a1ef37a1a1e5a Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 12 Oct 2024 05:43:55 -0600 Subject: [PATCH 1423/1472] Initialization added for error code. --- build/source/engine/read_pinit.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/source/engine/read_pinit.f90 b/build/source/engine/read_pinit.f90 index fd5c4e425..c70b561a7 100644 --- a/build/source/engine/read_pinit.f90 +++ b/build/source/engine/read_pinit.f90 @@ -185,6 +185,7 @@ subroutine set_ida_defaults(parFallback, err, message) integer(i4b), dimension(7) :: absTol_paramIndx = [iLookPARAM%absTolTempCas, iLookPARAM%absTolTempVeg, iLookPARAM%absTolWatVeg, & iLookPARAM%absTolTempSoilSnow, iLookPARAM%absTolWatSnow, iLookPARAM%absTolMatric, & iLookPARAM%absTolAquifr] + err=0 ! initialize error code message="set_ida_defaults/" ! Relative Tolerances From b3d2e771348764df57112803844afb45ab872f55 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 12 Oct 2024 05:56:56 -0600 Subject: [PATCH 1424/1472] Initialize derivative value. --- build/source/engine/canopySnow.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/source/engine/canopySnow.f90 b/build/source/engine/canopySnow.f90 index 50ae2b946..555838eec 100644 --- a/build/source/engine/canopySnow.f90 +++ b/build/source/engine/canopySnow.f90 @@ -159,6 +159,7 @@ subroutine canopySnow(& ! get a trial value for canopy storage scalarCanopyIceIter = scalarCanopyIce + unloadingDeriv=0._rkind ! initialize value do iter=1,maxiter ! ** compute unloading if(ixSnowUnload==meltDripUnload)then From 76d0e108d1829b9209437f1ee34f2922e8a5fc88 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 12 Oct 2024 06:06:23 -0600 Subject: [PATCH 1425/1472] Removed unused variables from opSplittin. --- build/source/engine/opSplittin.f90 | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 39b42b620..fef610683 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -295,9 +295,6 @@ subroutine opSplittin(& real(rkind),parameter :: dtmin_scalar=10._rkind ! minimum time step for the scalar solution (seconds) real(rkind) :: dt_min ! minimum time step (seconds) real(rkind) :: dtInit ! initial time step (seconds) - ! explicit error tolerance (depends on state type split, so defined here) - real(rkind),parameter :: errorTolLiqFlux=0.01_rkind ! error tolerance in the explicit solution (liquid flux) - real(rkind),parameter :: errorTolNrgFlux=10._rkind ! error tolerance in the explicit solution (energy flux) ! number of substeps taken for a given split integer(i4b) :: nSubsteps ! number of substeps taken for a given split ! named variables defining the coupling and solution method @@ -340,7 +337,7 @@ subroutine opSplittin(& integer(i4b),parameter :: maxSplit=500 ! >= max number of splitting methods (controls upper limit of split_select loop) ! ------------------------ classes for subroutine arguments (classes defined in data_types module) ------------------------ ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** - type(in_type_stateFilter) :: in_stateFilter; type(out_type_stateFilter) :: out_stateFilter; ! stateFilter arguments + !type(in_type_stateFilter) :: in_stateFilter; type(out_type_stateFilter) :: out_stateFilter; ! stateFilter arguments type(in_type_indexSplit) :: in_indexSplit; type(out_type_indexSplit) :: out_indexSplit; ! indexSplit arguments type(in_type_varSubstep) :: in_varSubstep; type(io_type_varSubstep) :: io_varSubstep; type(out_type_varSubstep) :: out_varSubstep; ! varSubstep arguments ! ------------------------------------------------------------------------------------------------------------------------- @@ -835,13 +832,13 @@ subroutine get_nStateSplit(ixSolution_value) end subroutine get_nStateSplit ! **** stateFilter **** - subroutine initialize_stateFilter - call in_stateFilter % initialize(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) - end subroutine initialize_stateFilter - - subroutine finalize_stateFilter - call out_stateFilter % finalize(nSubset,err,cmessage) - end subroutine finalize_stateFilter +! subroutine initialize_stateFilter +! call in_stateFilter % initialize(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) +! end subroutine initialize_stateFilter +! +! subroutine finalize_stateFilter +! call out_stateFilter % finalize(nSubset,err,cmessage) +! end subroutine finalize_stateFilter ! **** indexSplit **** subroutine initialize_indexSplit From 77f762880ff6c1049ebb7b51951d2f543fae0385 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 15 Oct 2024 17:40:05 +0900 Subject: [PATCH 1426/1472] fixing build for mac --- build/CMakeLists.txt | 9 ++------- build/cmake/build.cluster.bash | 2 +- build/cmake/build.mac.bash | 2 +- build/cmake/build_actors.cluster.bash | 2 +- build/cmake/build_actors.mac.bash | 4 ++-- build/cmake/build_ngen.cluster.bash | 2 +- build/cmake/build_ngen.mac.bash | 3 ++- 7 files changed, 10 insertions(+), 14 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index daed0569b..bb86cb7f3 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -92,7 +92,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXEC_DIR}) find_package(NetCDF REQUIRED) list(APPEND EXT_TARGETS NetCDF::NetCDF) -# Find LAPACK with user-specified links or automatic detection +# Find LAPACK with user-specified links or automatic detection option(SPECIFY_LAPACK_LINKS "Use specified LAPACK links" OFF) if(SPECIFY_LAPACK_LINKS) message("Using LAPACK links from LIBRARY_LINKS environment variable") @@ -105,13 +105,8 @@ else() message("Automatically detect LAPACK links") set(LIBRARY_LINKS "") - ## LAPACK - #find_package(LAPACK REQUIRED) - #list(APPEND EXT_TARGETS LAPACK::LAPACK) - # OpenBLAS set(BLA_VENDOR OpenBLAS) - #find_package(OpenBLAS REQUIRED) find_package(OpenBLAS REQUIRED COMPONENTS serial) list(APPEND EXT_TARGETS OpenBLAS::OpenBLAS) endif() @@ -130,7 +125,7 @@ else() set(FLAGS_NOAH -O3 -Wfatal-errors -ffree-line-length-none -fPIC -ffree-form ${FLAGS_OPT}) set(FLAGS_ALL -O3 -Wfatal-errors -ffree-line-length-none -fPIC -cpp ${FLAGS_OPT}) set(FLAGS_CXX -O3 -Wfatal-errors -std=c++17 ${FLAGS_OPT}) - else(CMAKE_Fortran_COMPILER MATCHES ifx) # ifx does not currently compile SUMMA -- derived type modifications needed in source code + else(CMAKE_Fortran_COMPILER MATCHES ifx) # ifx does not currently compile SUMMA -- derived type modifications needed in source code set(FLAGS_NOAH -O3 -fPIC -free ${FLAGS_OPT}) set(FLAGS_ALL -O3 -fPIC -cxxlib ${FLAGS_OPT}) set(FLAGS_CXX -O3 -std=c++17 ${FLAGS_OPT}) diff --git a/build/cmake/build.cluster.bash b/build/cmake/build.cluster.bash index 5ec005e8c..80216ff58 100755 --- a/build/cmake/build.cluster.bash +++ b/build/cmake/build.cluster.bash @@ -11,5 +11,5 @@ module load netcdf-fortran/4.6.1 export FLAGS_OPT="-flto=1;-fuse-linker-plugin" export SUNDIALS_PATH=../../../sundials/instdir/ -cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DCMAKE_BUILD_TYPE=Release +cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DCMAKE_BUILD_TYPE=Release -DSPECIFY_LAPACK_LINKS=OFF cmake --build ../cmake_build --target all -j diff --git a/build/cmake/build.mac.bash b/build/cmake/build.mac.bash index 0971ec18f..78cbb39de 100755 --- a/build/cmake/build.mac.bash +++ b/build/cmake/build.mac.bash @@ -7,7 +7,7 @@ # Mac Example using MacPorts: export FC=/opt/local/bin/gfortran # Fortran compiler family #export FLAGS_OPT="-flto=1" # -flto=1 is slow to compile, but might want to use - +export LIBRARY_LINKS='-llapack' # list of library links export SUNDIALS_DIR=../../../sundials/instdir/ cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DSPECIFY_LAPACK_LINKS=ON -DCMAKE_BUILD_TYPE=Release diff --git a/build/cmake/build_actors.cluster.bash b/build/cmake/build_actors.cluster.bash index df20ad6d6..b6cfdb369 100755 --- a/build/cmake/build_actors.cluster.bash +++ b/build/cmake/build_actors.cluster.bash @@ -14,5 +14,5 @@ module load caf export FLAGS_OPT="-flto=1;-fuse-linker-plugin" export SUNDIALS_PATH=/globalhome/kck540/HPC/Libraries/sundials/v7.0/instdir -cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DUSE_ACTORS=ON +cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DUSE_ACTORS=ON -DSPECIFY_LAPACK_LINKS=OFF -DCMAKE_BUILD_TYPE=Release cmake --build ../cmake_build --target all -j diff --git a/build/cmake/build_actors.mac.bash b/build/cmake/build_actors.mac.bash index 89468ab7c..41194c20b 100755 --- a/build/cmake/build_actors.mac.bash +++ b/build/cmake/build_actors.mac.bash @@ -7,8 +7,8 @@ # Mac Example using MacPorts: export FC=/opt/local/bin/gfortran # Fortran compiler family #export FLAGS_OPT="-flto=1" # -flto=1 is slow to compile, but might want to use - +export LIBRARY_LINKS='-llapack' # list of library links export SUNDIALS_DIR=../../../sundials/instdir/ -cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DUSE_ACTORS=ON -DSPECIFY_LAPACK_LINKS=ON +cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DUSE_ACTORS=ON -DSPECIFY_LAPACK_LINKS=OFF -DCMAKE_BUILD_TYPE=Release cmake --build ../cmake_build --target all -j diff --git a/build/cmake/build_ngen.cluster.bash b/build/cmake/build_ngen.cluster.bash index 6dc43b1bc..6d0aabcf7 100755 --- a/build/cmake/build_ngen.cluster.bash +++ b/build/cmake/build_ngen.cluster.bash @@ -18,7 +18,7 @@ module load udunits/2.2.28 cmake -B extern/iso_c_fortran_bmi/cmake_build -S extern/iso_c_fortran_bmi cmake --build extern/iso_c_fortran_bmi/cmake_build --target all -cmake -B extern/summa/cmake_build -S extern/summa -DUSE_NEXTGEN=ON +cmake -B extern/summa/cmake_build -S extern/summa -DUSE_NEXTGEN=ON -DSPECIFY_LAPACK_LINKS=OFF -DCMAKE_BUILD_TYPE=Release cmake --build extern/summa/cmake_build --target all -j cmake -B cmake_build -S . -DNGEN_WITH_MPI:BOOL=OFF -DNGEN_WITH_PYTHON:BOOL=OFF -DNGEN_WITH_BMI_C:BOOL=ON -DNGEN_WITH_BMI_FORTRAN:BOOL=ON -DNGEN_WITH_NETCDF:BOOL=OFF diff --git a/build/cmake/build_ngen.mac.bash b/build/cmake/build_ngen.mac.bash index 4fdc39cc7..9ab499030 100755 --- a/build/cmake/build_ngen.mac.bash +++ b/build/cmake/build_ngen.mac.bash @@ -9,9 +9,10 @@ export FC=/opt/local/bin/gfortran # Fortran compiler #export FLAGS_OPT="-flto=1" # -flto=1 is slow to compile, but might want to use export C_INCLUDE_PATH=/opt/local/include export CPLUS_INCLUDE_PATH=/opt/local/include +export LIBRARY_LINKS='-llapack' # list of library links export SUNDIALS_DIR=../../../sundials/instdir/ -cmake -B extern/summa/cmake_build -S extern/summa -DUSE_NEXTGEN=ON -DSPECIFY_LAPACK_LINKS=ON +cmake -B extern/summa/cmake_build -S extern/summa -DUSE_NEXTGEN=ON -DSPECIFY_LAPACK_LINKS=ON -DCMAKE_BUILD_TYPE=Release cmake --build extern/summa/cmake_build --target all -j cmake -B cmake_build -S . -DBoost_INCLUDE_DIR=/opt/local/libexec/boost/1.81/include -DNGEN_WITH_BMI_FORTRAN=ON -DPython_NumPy_INCLUDE_DIR=/opt/local/bin/python -DNGEN_WITH_MPI:BOOL=OFF -DNGEN_WITH_PYTHON:BOOL=OFF -DNGEN_WITH_BMI_C:BOOL=ON -DNGEN_WITH_BMI_FORTRAN:BOOL=ON -DNGEN_WITH_NETCDF:BOOL=OFF From 01728e0302a21cbc3b4a27d53649aa95505835fe Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 15 Oct 2024 17:45:36 +0900 Subject: [PATCH 1427/1472] fixing cmake for mac --- build/cmake/build_actors.mac.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/cmake/build_actors.mac.bash b/build/cmake/build_actors.mac.bash index 41194c20b..22d1506bb 100755 --- a/build/cmake/build_actors.mac.bash +++ b/build/cmake/build_actors.mac.bash @@ -10,5 +10,5 @@ export FC=/opt/local/bin/gfortran # Fortran compiler export LIBRARY_LINKS='-llapack' # list of library links export SUNDIALS_DIR=../../../sundials/instdir/ -cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DUSE_ACTORS=ON -DSPECIFY_LAPACK_LINKS=OFF -DCMAKE_BUILD_TYPE=Release +cmake -B ../cmake_build -S ../. -DUSE_SUNDIALS=ON -DUSE_ACTORS=ON -DSPECIFY_LAPACK_LINKS=ON -DCMAKE_BUILD_TYPE=Release cmake --build ../cmake_build --target all -j From d4ef8c9f342b19ba43d486ffeeacb0cb8145e430 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 16 Oct 2024 23:30:51 +0900 Subject: [PATCH 1428/1472] spaces --- build/source/engine/volicePack.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/source/engine/volicePack.f90 b/build/source/engine/volicePack.f90 index 4aef78867..845323066 100644 --- a/build/source/engine/volicePack.f90 +++ b/build/source/engine/volicePack.f90 @@ -162,7 +162,7 @@ subroutine newsnwfall(& implicit none ! input: model control real(rkind),intent(in) :: dt ! time step (seconds) - logical(lgt),intent(in) :: snowLayers ! logical flag if snow layers exist + logical(lgt),intent(in) :: snowLayers ! logical flag if snow layers exist real(rkind),intent(in) :: fc_param ! freeezing curve parameter for snow (K-1) ! input: diagnostic scalar variables real(rkind),intent(in) :: scalarSnowfallTemp ! computed temperature of fresh snow (K) @@ -177,8 +177,8 @@ subroutine newsnwfall(& real(rkind),intent(inout) :: surfaceLayerVolFracIce ! volumetric fraction of ice in surface layer (-) real(rkind),intent(inout) :: surfaceLayerVolFracLiq ! volumetric fraction of liquid water in surface layer (-) ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message ! define local variables real(rkind) :: newSnowfall ! new snowfall -- throughfall and unloading (kg m-2 s-1) real(rkind) :: newSnowDepth ! new snow depth (m) From c3eab8583ec5230a7305dbb2d10edefbd83515f3 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 17 Oct 2024 07:11:06 -0600 Subject: [PATCH 1429/1472] Added checks for memory allocation and updated syntax to avoid unintentional automatic array allocation in opSplittin. --- build/source/engine/opSplittin.f90 | 79 +++++++++++++----------------- 1 file changed, 35 insertions(+), 44 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index fef610683..93441ce02 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -142,6 +142,7 @@ module opSplittin_module integer(i4b) :: ixSolution integer(i4b) :: iStateSplit ! variables for specifying the split + integer(i4b) :: nState ! # of state variables integer(i4b) :: nSubset ! number of selected state variables for a given split type(var_flagVec) :: fluxMask ! integer mask defining model fluxes logical(lgt),allocatable :: stateMask(:) ! mask defining desired state variables @@ -337,14 +338,13 @@ subroutine opSplittin(& integer(i4b),parameter :: maxSplit=500 ! >= max number of splitting methods (controls upper limit of split_select loop) ! ------------------------ classes for subroutine arguments (classes defined in data_types module) ------------------------ ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** - !type(in_type_stateFilter) :: in_stateFilter; type(out_type_stateFilter) :: out_stateFilter; ! stateFilter arguments type(in_type_indexSplit) :: in_indexSplit; type(out_type_indexSplit) :: out_indexSplit; ! indexSplit arguments type(in_type_varSubstep) :: in_varSubstep; type(io_type_varSubstep) :: io_varSubstep; type(out_type_varSubstep) :: out_varSubstep; ! varSubstep arguments ! ------------------------------------------------------------------------------------------------------------------------- type(split_select_type) :: split_select ! class object for selecting operator splitting methods ! *** Initialize Split Selector Object *** - call initialize_split_select + call initialize_split_select; if (return_flag) return call initialize_split_coupling; if (return_flag) return split_select_loop: do iSplit=1,maxSplit ! coupling begins call initialize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop; if (return_flag) return @@ -399,12 +399,25 @@ subroutine opSplittin(& subroutine initialize_split_select ! *** Initialize split_select class object *** + + ! initialize # of state variables + split_select % nState = nState ! allocate data components - allocate(split_select % stateMask(1:nState)) ! allocate split_select components - + allocate(split_select % stateMask(1:nState),STAT=err) ! allocate split_select components + + ! check for allocation errors + if (err/=0) then + message=trim(message)//'allocation error in initialize_split_select routine for split_select % stateMask' + return_flag=.true.; return + else + return_flag=.false. + end if + + ! initialize split_select % stateMask to default initial case + split_select % stateMask(1:nState) = .true. + ! initialize flags - return_flag=.false. exit_split_select=.false. cycle_split_select=.false. call split_select % initialize_flags ! initialize control flags @@ -831,15 +844,6 @@ subroutine get_nStateSplit(ixSolution_value) end select end subroutine get_nStateSplit - ! **** stateFilter **** -! subroutine initialize_stateFilter -! call in_stateFilter % initialize(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) -! end subroutine initialize_stateFilter -! -! subroutine finalize_stateFilter -! call out_stateFilter % finalize(nSubset,err,cmessage) -! end subroutine finalize_stateFilter - ! **** indexSplit **** subroutine initialize_indexSplit call in_indexSplit % initialize(nSnow,nSoil,nLayers,nSubset) @@ -1370,17 +1374,17 @@ end function split_select_logic_finalize_stateTypeSplitting subroutine split_select_compute_stateMask(split_select,indx_data,err,cmessage,message,return_flag) ! *** Get the mask for the state subset *** - class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector type(var_ilength),intent(in) :: indx_data ! indices for a local HRU - integer(i4b),intent(out) :: err ! intent(out): error code - character(*),intent(out) :: cmessage ! intent(out): error message - character(*),intent(out) :: message ! error message - logical(lgt),intent(out) :: return_flag + integer(i4b),intent(out) :: err ! intent(out): error code + character(*),intent(out) :: cmessage ! intent(out): error message + character(*),intent(out) :: message ! error message + logical(lgt),intent(out) :: return_flag ! return flag ! local variables type(in_type_stateFilter) :: in_stateFilter ! indices type(out_type_stateFilter) :: out_stateFilter ! number of selected state variables for a given split and error control - err=0 ! SJT TEST + err=0 ! initialize error code return_flag=.false. ! initialize flag associate(& ixCoupling => split_select % ixCoupling ,& @@ -1391,35 +1395,26 @@ subroutine split_select_compute_stateMask(split_select,indx_data,err,cmessage,me iStateSplit => split_select % iStateSplit ) call in_stateFilter % initialize(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) end associate - !associate(stateMask => split_select % stateMask) - ! call stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) - !end associate - !print *, "SJT 0",err call stateFilter(in_stateFilter,indx_data,split_select % stateMask,out_stateFilter) - print *, "SJT 4.1",out_stateFilter%err associate(nSubset => split_select % nSubset) call out_stateFilter % finalize(nSubset,err,cmessage) end associate - print *, "SJT 5",err if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! error control - print *, "SJT 6" end subroutine split_select_compute_stateMask ! ********************************************************************************************************** ! private subroutine stateFilter: get a mask for the desired state variables ! ********************************************************************************************************** -subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) - + subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) USE indexState_module,only:indxSubset ! get state indices implicit none ! input type(in_type_stateFilter),intent(in) :: in_stateFilter ! indices type(var_ilength),intent(in) :: indx_data ! indices for a local HRU + ! input-output + logical(lgt),intent(inout) :: stateMask(:) ! mask defining desired state variables ! output - !logical(lgt),intent(out) :: stateMask(:) ! mask defining desired state variables - !logical(lgt),allocatable,intent(inout) :: stateMask(:) ! mask defining desired state variables - logical(lgt),intent(inout) :: stateMask(:) ! mask defining desired state variables type(out_type_stateFilter),intent(out) :: out_stateFilter ! number of selected state variables for a given split and error control ! local integer(i4b),allocatable :: ixSubset(:) ! list of indices in the state subset @@ -1441,13 +1436,14 @@ subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) case(stateTypeSplit) ! initial split by state type call stateTypeSplit_stateMask; if (return_flag) return ! get stateMask for state split method -- return if error ! check - case default; err=20; message=trim(message)//'unable to identify coupling method'; return - end select ! selecting solution method + case default; err=20; message=trim(message)//'unable to identify coupling method'; return_flag=.true.; return + end select ! selecting solution method + + ! initialize ixSubset + allocate(ixSubset(1_i4b),STAT=err) + if (err/=0) then; message=trim(message)//'allocation error in stateFilter for ixSubset'; return_flag=.true.; return; end if + ixSubset = 0._rkind end associate - - ! initialize ixSubset - allocate(ixSubset(1_i4b)) - ixSubset = 0._rkind call identify_scalar_solutions; if (return_flag) return ! identify scalar solutions -- return if error occurs @@ -1456,16 +1452,11 @@ subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) nSubset = count(stateMask) end associate - print *, "SJT 4" - contains subroutine fullyCoupled_stateMask ! *** Get fully coupled stateMask *** - print *, "SJT1:",stateMask - print *, "SJT2:",size(stateMask) stateMask(:) = .true. ! use all state variables - print *, "SJT3:",stateMask end subroutine fullyCoupled_stateMask subroutine stateTypeSplit_stateMask @@ -1524,7 +1515,7 @@ subroutine stateTypeSplit_subDomain_stateMask iStateTypeSplit => in_stateFilter % iStateTypeSplit ,& ! intent(in): [i4b] index of the state type split err => out_stateFilter % err ,& ! intent(out): error code message => out_stateFilter % cmessage ) ! intent(out): error message - stateMask=.false. ! initialize state mask + stateMask(:)=.false. ! initialize state mask select case(iStateTypeSplit) ! define mask for energy case(nrgSplit); call stateTypeSplit_subDomain_nrgSplit_stateMask; if (return_flag) return From 1e8b44daf790c18a07dc77abbcc7f48be982f3af Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 17 Oct 2024 07:14:02 -0600 Subject: [PATCH 1430/1472] Updates to compiler flags: debug checks were expanded, frecursive added to gfortran release builds, and Intel flags updated for the future. --- build/CMakeLists.txt | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index bb86cb7f3..5a993356f 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -116,18 +116,20 @@ set(FLAGS_OPT $ENV{FLAGS_OPT}) # get optional user-specified flags from environm if(CMAKE_BUILD_TYPE MATCHES Debug) message("\nSetting SUMMA Debug Options") add_compile_definitions(DEBUG) - set(FLAGS_NOAH -g -Og -fbacktrace -fcheck=all,no-array-temps -Wfatal-errors -ffree-line-length-none -fPIC -ffree-form ${FLAGS_OPT}) - set(FLAGS_ALL -g -Og -fbacktrace -fcheck=all,no-array-temps -Wfatal-errors -ffree-line-length-none -fPIC -cpp ${FLAGS_OPT}) - set(FLAGS_CXX -g -Og -fbacktrace -fcheck=all,no-array-temps -Wfatal-errors -std=c++17 ${FLAGS_OPT}) + # Notes: - optimization -Og is selected here to maximize information displayed in debugging software (e.g., gdb) + # - however, it may be useful to vary the optimization level to test for optimization related issues + set(FLAGS_NOAH -g -Og -fbacktrace -Wall -fcheck=all,no-array-temps -Wfatal-errors -ffree-line-length-none -fPIC -ffree-form ${FLAGS_OPT}) + set(FLAGS_ALL -g -Og -fbacktrace -Wall -fcheck=all,no-array-temps -Wfatal-errors -ffree-line-length-none -fPIC -cpp -frecursive ${FLAGS_OPT}) + set(FLAGS_CXX -g -Og -fbacktrace -Wall -fcheck=all,no-array-temps -Wfatal-errors -std=c++17 ${FLAGS_OPT}) else() message("\nSetting SUMMA Release Options") if (CMAKE_Fortran_COMPILER MATCHES gfortran) - set(FLAGS_NOAH -O3 -Wfatal-errors -ffree-line-length-none -fPIC -ffree-form ${FLAGS_OPT}) - set(FLAGS_ALL -O3 -Wfatal-errors -ffree-line-length-none -fPIC -cpp ${FLAGS_OPT}) + set(FLAGS_NOAH -O3 -Wfatal-errors -ffree-line-length-none -fPIC -ffree-form ${FLAGS_OPT}) + set(FLAGS_ALL -O3 -Wfatal-errors -ffree-line-length-none -fPIC -cpp -frecursive ${FLAGS_OPT}) set(FLAGS_CXX -O3 -Wfatal-errors -std=c++17 ${FLAGS_OPT}) else(CMAKE_Fortran_COMPILER MATCHES ifx) # ifx does not currently compile SUMMA -- derived type modifications needed in source code - set(FLAGS_NOAH -O3 -fPIC -free ${FLAGS_OPT}) - set(FLAGS_ALL -O3 -fPIC -cxxlib ${FLAGS_OPT}) + set(FLAGS_NOAH -O3 -heap-arrays -fPIC -free ${FLAGS_OPT}) + set(FLAGS_ALL -O3 -heap-arrays -fPIC -cxxlib ${FLAGS_OPT}) set(FLAGS_CXX -O3 -std=c++17 ${FLAGS_OPT}) endif() endif() From 141b58036823aa79744efb701d152b3a0a01bf6f Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 18 Oct 2024 11:08:43 +0900 Subject: [PATCH 1431/1472] remove commented out variables --- build/source/engine/check_icond.f90 | 2 -- build/source/engine/conv_funcs.f90 | 3 --- build/source/engine/coupled_em.f90 | 2 -- build/source/engine/eval8summa.f90 | 3 --- build/source/engine/eval8summaWithPrime.f90 | 1 - build/source/engine/soilLiqFlx.f90 | 2 -- build/source/engine/soil_utilsAddPrime.f90 | 1 - build/source/engine/summaSolve4kinsol.f90 | 1 - build/source/engine/updateVars.f90 | 1 - build/source/engine/vegNrgFlux.f90 | 1 - build/source/netcdf/read_icond.f90 | 2 -- 11 files changed, 19 deletions(-) diff --git a/build/source/engine/check_icond.f90 b/build/source/engine/check_icond.f90 index 0f3729230..a322b7bd6 100644 --- a/build/source/engine/check_icond.f90 +++ b/build/source/engine/check_icond.f90 @@ -93,14 +93,12 @@ subroutine check_icond(nGRU, & ! intent(in): number integer(i4b) :: iSoil ! index of soil layer real(rkind) :: fLiq ! fraction of liquid water on the vegetation canopy (-) real(rkind) :: vGn_m ! van Genutchen "m" parameter (-) - !real(rkind) :: tWat ! total water on the vegetation canopy (kg m-2) real(rkind) :: scalarTheta ! liquid water equivalent of total water [liquid water + ice] (-) real(rkind) :: h1,h2 ! used to check depth and height are consistent real(rkind) :: kappa ! constant in the freezing curve function (m K-1) integer(i4b) :: nSoil ! number of soil layers integer(i4b) :: nSnow ! number of snow layers integer(i4b) :: nLayers ! total number of layers - !integer(i4b) :: nState ! total number of states real(rkind),parameter :: xTol=1.e-10_rkind ! small tolerance to address precision issues real(rkind),parameter :: canIceTol=1.e-3_rkind ! small tolerance to allow existence of canopy ice for above-freezing temperatures (kg m-2) ! -------------------------------------------------------------------------------------------------------- diff --git a/build/source/engine/conv_funcs.f90 b/build/source/engine/conv_funcs.f90 index 5078b8d61..bf5359ea1 100644 --- a/build/source/engine/conv_funcs.f90 +++ b/build/source/engine/conv_funcs.f90 @@ -58,7 +58,6 @@ function vapPress(q,p) real(rkind) :: vapPress ! vapor pressure (Pa) ! local real(rkind) :: w ! mixing ratio -!real(rkind),parameter :: w_ratio = 0.622_rkind ! molecular weight ratio of water to dry air (-) w = q / (1._rkind - q) ! mixing ratio (-) vapPress = (w/(w + w_ratio))*p ! vapor pressure (Pa) end function vapPress @@ -257,7 +256,6 @@ FUNCTION SPHM2RELHM(SPHM, PRESS, TAIR) real(rkind) :: VPSAT ! vapour pressure at T (Pa) real(rkind) :: TDCEL ! Dewpt in celcius (C) -!real(rkind) :: DUM ! Intermediate ! Units note : Pa = N m-2 = kg m-1 s-2 ! SATVPFRZ= 610.8 ! Saturation water vapour pressure at 273.16K (Pa) @@ -288,7 +286,6 @@ FUNCTION RELHM2SPHM(RELHM, PRESS, TAIR) real(rkind) :: PVP ! Partial vapour pressure at T (Pa) real(rkind) :: TDCEL ! Dewpt in celcius (C) -!real(rkind) :: DUM ! Intermediate ! Units note : Pa = N m-2 = kg m-1 s-2 ! SATVPFRZ= 610.8 ! Saturation water vapour pressure at 273.16K (Pa) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 5efaeaed1..d29d52d10 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -208,9 +208,7 @@ subroutine coupled_em(& real(rkind) :: dCanopyWetFraction_dT ! derivative in wetted fraction w.r.t. canopy temperature (K-1) real(rkind),parameter :: varNotUsed1=-9999._rkind ! variables used to calculate derivatives (not needed here) real(rkind),parameter :: varNotUsed2=-9999._rkind ! variables used to calculate derivatives (not needed here) - !integer(i4b) :: iSnow ! index of snow layers integer(i4b) :: iLayer ! index of model layers - !real(rkind) :: massLiquid ! mass liquid water (kg m-2) real(rkind) :: superflousSub ! superflous sublimation (kg m-2 s-1) real(rkind) :: superflousNrg ! superflous energy that cannot be used for sublimation (W m-2 [J m-2 s-1]) integer(i4b) :: ixSolution ! solution method used by opSplittin diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 0aefbf1f6..dfc8dc912 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -209,13 +209,10 @@ subroutine eval8summa(& real(rkind),dimension(nLayers) :: mLayerEnthTempTrial ! trial vector of temperature component of enthalpy for snow+soil layers (J m-3) ! other local variables logical(lgt) :: checkLWBalance ! flag to check longwave balance - !integer(i4b) :: iLayer ! index of model layer in the snow+soil domain integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine real(rkind),dimension(nState) :: rVecScaled ! scaled residual vector character(LEN=256) :: cmessage ! error message of downwind routine - !real(rkind) :: scalarCanopyCmTrial ! trial value of Cm for the canopy - !real(rkind),dimension(nLayers) :: mLayerCmTrial ! trial vector of Cm for snow+soil logical(lgt) :: updateStateCp ! flag to indicate if we update Cp at each step for LHS, set with nrgConserv choice and updateCp_closedForm flag logical(lgt) :: updateFluxCp ! flag to indicate if we update Cp at each step for RHS, set with nrgConserv choice and updateCp_closedForm flag logical(lgt) :: needStateCm ! flag to indicate if the energy equation contains LHS Cm = dH_T/dTheta_m,, set with nrgConserv choice and needStateCm_closedForm flag diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 8151c331a..0d3c83a1b 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -206,7 +206,6 @@ subroutine eval8summaWithPrime(& real(rkind) :: scalarCanopyNrgPrime ! prime value for energy of the vegetation canopy real(rkind),dimension(nLayers) :: mLayerNrgPrime ! prime vector of energy of each snow and soil layer ! other local variables - !integer(i4b) :: iLayer ! index of model layer in the snow+soil domain integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine character(LEN=256) :: cmessage ! error message of downwind routine diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index e99049eac..d93f5b362 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -139,8 +139,6 @@ subroutine soilLiqFlx(& integer(i4b) :: nRoots ! number of soil layers with roots integer(i4b) :: ixIce ! index of the lowest soil layer that contains ice real(rkind),dimension(0:in_soilLiqFlx % nSoil) :: iLayerHeight ! height of the layer interfaces (m) - ! compute fluxes and derivatives at layer interfaces - !real(rkind) :: scalardPsi_dTheta ! derivative in soil water characteristix, used for perturbations when computing numerical derivatives ! ------------------------------------------------------------------------------------------------------------------------------------------------- nSoil = in_soilLiqFlx % nSoil ! get number of soil layers from input arguments diff --git a/build/source/engine/soil_utilsAddPrime.f90 b/build/source/engine/soil_utilsAddPrime.f90 index 5a7ee6521..c162ee928 100644 --- a/build/source/engine/soil_utilsAddPrime.f90 +++ b/build/source/engine/soil_utilsAddPrime.f90 @@ -96,7 +96,6 @@ subroutine liquidHeadPrime(& real(rkind) :: effSat ! effective saturation (-) real(rkind) :: dPsiLiq_dEffSat ! derivative in liquid water matric potential w.r.t. effective saturation (m) real(rkind) :: dEffSat_dTemp ! derivative in effective saturation w.r.t. temperature (K-1) - !real(rkind) :: dEffSat_dFracLiq ! derivative in effective saturation w.r.t. liquid water fraction (-) real(rkind) :: effSatPrime ! effective saturation time derivative (-) ! ------------------------------------------------------------------------------------------------------------------------------ ! initialize error control diff --git a/build/source/engine/summaSolve4kinsol.f90 b/build/source/engine/summaSolve4kinsol.f90 index 525ec43a6..6b17b937d 100644 --- a/build/source/engine/summaSolve4kinsol.f90 +++ b/build/source/engine/summaSolve4kinsol.f90 @@ -205,7 +205,6 @@ subroutine summaSolve4kinsol(& logical(lgt) :: feasible ! feasibility flag integer(c_long) :: mu, lu ! in banded matrix mode in SUNDIALS type integer(c_long) :: nState ! total number of state variables in SUNDIALS type - !integer(i4b) :: iVar, i ! indices character(LEN=256) :: cmessage ! error message of downwind routine logical(lgt) :: use_fdJac ! flag to use finite difference Jacobian, controlled by decision fDerivMeth logical(lgt),parameter :: offErrWarnMessage = .true. ! flag to turn IDA warnings off, default true diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 index 2283f4a93..ece1c2d14 100644 --- a/build/source/engine/updateVars.f90 +++ b/build/source/engine/updateVars.f90 @@ -181,7 +181,6 @@ subroutine updateVars(& real(rkind) :: scalarVolFracIce ! volumetric fraction of ice (-) real(rkind) :: Tcrit ! critical soil temperature below which ice exists (K) real(rkind) :: xTemp ! temporary temperature (K) - !real(rkind) :: fLiq ! fraction of liquid water (-) real(rkind) :: effSat ! effective saturation (-) real(rkind) :: avPore ! available pore space (-) character(len=256) :: cMessage ! error message of downwind routine diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 1b1b5eff9..65ba03cab 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -179,7 +179,6 @@ subroutine vegNrgFlux(& real(rkind) :: dSVPGround_dGroundTemp ! derivative in ground saturated vapor pressure w.r.t. ground temperature (Pa/K) ! wetted canopy area real(rkind) :: fracLiquidCanopy ! fraction of liquid water in the canopy (-) - !real(rkind) :: canopyWetFraction ! trial value of the canopy wetted fraction (-) real(rkind) :: dCanopyWetFraction_dWat ! derivative in wetted fraction w.r.t. canopy total water (kg-1 m2) real(rkind) :: dCanopyWetFraction_dT ! derivative in wetted fraction w.r.t. canopy temperature (K-1) ! longwave radiation diff --git a/build/source/netcdf/read_icond.f90 b/build/source/netcdf/read_icond.f90 index dbc190324..4a1a26be3 100644 --- a/build/source/netcdf/read_icond.f90 +++ b/build/source/netcdf/read_icond.f90 @@ -64,7 +64,6 @@ subroutine read_icond_nlayers(iconFile,nGRU,indx_meta,err,message) integer(i4b) :: fileHRU ! number of HRUs in netcdf file integer(i4b) :: snowID, soilID ! netcdf variable ids integer(i4b) :: iGRU, iHRU ! loop indexes - !integer(i4b) :: iHRU_local ! index of HRU in the data subset integer(i4b) :: iHRU_global ! index of HRU in the netcdf file integer(i4b),allocatable :: snowData(:) ! number of snow layers in all HRUs integer(i4b),allocatable :: soilData(:) ! number of soil layers in all HRUs @@ -202,7 +201,6 @@ subroutine read_icond(iconFile, & ! intent(in): name of integer(i4b) :: nTDH ! number of points in time-delay histogram integer(i4b) :: iLayer,jLayer ! layer indices integer(i4b),parameter :: nBand=2 ! number of spectral bands - !integer(i4b) :: nProgVars ! number of prognostic variables written to state file character(len=32),parameter :: scalDimName ='scalarv' ! dimension name for scalar data character(len=32),parameter :: midSoilDimName='midSoil' ! dimension name for soil-only layers character(len=32),parameter :: midTotoDimName='midToto' ! dimension name for layered varaiables From 724336e7ba55b04941ef7db12faa1a2d8d4ca673 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 18 Oct 2024 15:59:24 +0900 Subject: [PATCH 1432/1472] get nSubset --- build/source/engine/opSplittin.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 93441ce02..a23946af8 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -965,6 +965,7 @@ subroutine validate_split cycle_domainSplit=.false. cycle_solution=.false. return_flag=.false. + nSubset = split_select % nSubset ! check that state variables exist if (nSubset==0) then From 5e72a989200364412066ac5c14bdb05f709991ff Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 18 Oct 2024 04:09:09 -0600 Subject: [PATCH 1433/1472] Added initialize and finalize routines for summa_driver.f90. --- build/source/driver/summa_driver.f90 | 117 ++++++++++++++++++++------- 1 file changed, 88 insertions(+), 29 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 9e2c09431..71dae178a 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -64,35 +64,40 @@ program summa_driver integer(i4b) :: err=0 ! error code character(len=1024) :: message='' ! error message - ! ***************************************************************************** - ! * preliminaries - ! ***************************************************************************** - - ! allocate space for the master summa structure - allocate(summa1_struc(n), stat=err) - if(err/=0) call stop_program(1, 'problem allocating master summa structure') - - ! ***************************************************************************** - ! * model setup/initialization - ! ***************************************************************************** +!!! -------------------- Begin Initialize -------------------- !!! + call initialize_summa_driver - ! declare and allocate summa data structures and initialize model state to known values - call summa_initialize(summa1_struc(n), err, message) - call handle_err(err, message) - - ! initialize parameter data structures (e.g. vegetation and soil parameters) - call summa_paramSetup(summa1_struc(n), err, message) - call handle_err(err, message) +! ! ***************************************************************************** +! ! * preliminaries +! ! ***************************************************************************** +! +! ! allocate space for the master summa structure +! allocate(summa1_struc(n), stat=err) +! if(err/=0) call stop_program(1, 'problem allocating master summa structure') - ! read restart data and reset the model state - call summa_readRestart(summa1_struc(n), err, message) - call handle_err(err, message) +! ! ***************************************************************************** +! ! * model setup/initialization +! ! ***************************************************************************** +! +! ! declare and allocate summa data structures and initialize model state to known values +! call summa_initialize(summa1_struc(n), err, message) +! call handle_err(err, message) +! +! ! initialize parameter data structures (e.g. vegetation and soil parameters) +! call summa_paramSetup(summa1_struc(n), err, message) +! call handle_err(err, message) +! +! ! read restart data and reset the model state +! call summa_readRestart(summa1_struc(n), err, message) +! call handle_err(err, message) -#ifdef OPENWQ_ACTIVE - call openwq_init(err) - if (err /= 0) call stop_program(1, 'Problem Initializing OpenWQ') -#endif +!#ifdef OPENWQ_ACTIVE +! call openwq_init(err) +! if (err /= 0) call stop_program(1, 'Problem Initializing OpenWQ') +!#endif +!!! -------------------- End Initialize -------------------- !!! +!!! -------------------- Begin Update -------------------- !!! ! ***************************************************************************** ! * model simulation ! ***************************************************************************** @@ -128,11 +133,65 @@ program summa_driver #endif end do ! looping through time +!!! -------------------- End Update -------------------- !!! + +!!! -------------------- Begin Finalize -------------------- !!! + call finalize_summa_driver + +! ! successful end +! call stop_program(0, 'finished simulation successfully.') +! +! ! to prevent exiting before HDF5 has closed +! call sleep(2) +!!! -------------------- End Finalize -------------------- !!! + +contains + + subroutine initialize_summa_driver + ! *** Initial operations for SUMMA driver program *** + + ! ***************************************************************************** + ! * preliminaries + ! ***************************************************************************** + + ! allocate space for the master summa structure + allocate(summa1_struc(n), stat=err) + if(err/=0) call stop_program(1, 'problem allocating master summa structure') + + ! ***************************************************************************** + ! * model setup/initialization + ! ***************************************************************************** + + ! declare and allocate summa data structures and initialize model state to known values + call summa_initialize(summa1_struc(n), err, message) + call handle_err(err, message) + + ! initialize parameter data structures (e.g. vegetation and soil parameters) + call summa_paramSetup(summa1_struc(n), err, message) + call handle_err(err, message) + + ! read restart data and reset the model state + call summa_readRestart(summa1_struc(n), err, message) + call handle_err(err, message) + +#ifdef OPENWQ_ACTIVE + call openwq_init(err) + if (err /= 0) call stop_program(1, 'Problem Initializing OpenWQ') +#endif + end subroutine initialize_summa_driver + + subroutine update_summa_driver + ! *** Update operations for SUMMA driver program *** + + end subroutine update_summa_driver - ! successful end - call stop_program(0, 'finished simulation successfully.') + subroutine finalize_summa_driver + ! *** Final operations for SUMMA driver program *** + ! successful end + call stop_program(0, 'finished simulation successfully.') - ! to prevent exiting before HDF5 has closed - call sleep(2) + ! to prevent exiting before HDF5 has closed + call sleep(2) + end subroutine finalize_summa_driver end program summa_driver From 52fe244b5362e22738736dfb5e87dfa131bca470 Mon Sep 17 00:00:00 2001 From: Diogo Costa Date: Fri, 18 Oct 2024 13:07:50 +0100 Subject: [PATCH 1434/1472] now the user needs to create a file named "openwq_mainJSONFile_fullPath.txt" that contains the full path to the main entry input file for openwq --- build/source/openwq/OpenWQ_hydrolink.cpp | 41 +++++++++++++++++++++--- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/build/source/openwq/OpenWQ_hydrolink.cpp b/build/source/openwq/OpenWQ_hydrolink.cpp index efc1c5b7a..fb7daa838 100644 --- a/build/source/openwq/OpenWQ_hydrolink.cpp +++ b/build/source/openwq/OpenWQ_hydrolink.cpp @@ -33,6 +33,7 @@ int CLASSWQ_openwq::decl( int nYdirec_2openwq){ // num of layers in y-dir (set to 1 because not used in summa) this->num_HRU = num_HRU; + std::string msg_string; // interactive message to print if (OpenWQ_hostModelconfig_ref->get_num_HydroComp()==0) { @@ -61,13 +62,43 @@ int CLASSWQ_openwq::decl( OpenWQ_hostModelconfig_ref->add_HydroDepend(2,"Tsoil_K", num_HRU,nYdirec_2openwq, nSnow_2openwq + nSoil_2openwq); // Master Json - std::string master_json = std::getenv("master_json") ? std::getenv("master_json") : ""; - if (!std::filesystem::exists(master_json)) { - std::cerr << "\nERROR: Path to OpenWQ_master.json does not exist !!\n" - << "Please set the environment variable 'master_json' " - << "to the path of the OpenWQ_master.json file.\n"; + // read location from file: openwq_mainJSONFile_fullPath.txt + std::string master_json; //string + std::fstream fileStream; //file stream object + fileStream.open("openwq_mainJSONFile_fullPath.txt"); //open your word list + + // check if openwq_mainJSONFile_fullPath.txt exists + if (!fileStream) { + + // Create Error Message + msg_string = + " ERROR: The 'openwq_mainJSONFile_fullPath.txt' has not been found. This file needs to exist in the directory where the openWQ executable is located, and it needs to contain the full path to the main/entry input file for OpenWQ. The simulation aborted has been!"; + + // Print it (Console and/or Log file) + std::cout << msg_string << std::endl; + exit(EXIT_FAILURE); + + // if yes, get the master file fullpath and check if it also exists + }else{ + // read openwq master file location + std::getline(fileStream, master_json); + + // Check if master file exists + if (!std::filesystem::exists(master_json)) { + + // Create Error Message + msg_string = + " ERROR: The full path to the main/entry input file for OpenWQ that is provided in 'openwq_mainJSONFile_fullPath.txt' has not been found. The simulation aborted has been!"; + + // Print it (Console and/or Log file) + std::cout << msg_string << std::endl; + + exit(EXIT_FAILURE); + } + } + OpenWQ_wqconfig_ref->set_OpenWQ_masterjson(master_json); OpenWQ_couplercalls_ref->InitialConfig( From a649d907b877c332b3b45cfb73fd5dcc5bfc0108 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 19 Oct 2024 05:48:59 -0600 Subject: [PATCH 1435/1472] Added update step for summa_driver.f90. --- build/source/driver/summa_driver.f90 | 120 ++++++++------------------- 1 file changed, 36 insertions(+), 84 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index 71dae178a..d62b148b3 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -64,104 +64,24 @@ program summa_driver integer(i4b) :: err=0 ! error code character(len=1024) :: message='' ! error message -!!! -------------------- Begin Initialize -------------------- !!! + ! Initialize call initialize_summa_driver -! ! ***************************************************************************** -! ! * preliminaries -! ! ***************************************************************************** -! -! ! allocate space for the master summa structure -! allocate(summa1_struc(n), stat=err) -! if(err/=0) call stop_program(1, 'problem allocating master summa structure') - -! ! ***************************************************************************** -! ! * model setup/initialization -! ! ***************************************************************************** -! -! ! declare and allocate summa data structures and initialize model state to known values -! call summa_initialize(summa1_struc(n), err, message) -! call handle_err(err, message) -! -! ! initialize parameter data structures (e.g. vegetation and soil parameters) -! call summa_paramSetup(summa1_struc(n), err, message) -! call handle_err(err, message) -! -! ! read restart data and reset the model state -! call summa_readRestart(summa1_struc(n), err, message) -! call handle_err(err, message) - -!#ifdef OPENWQ_ACTIVE -! call openwq_init(err) -! if (err /= 0) call stop_program(1, 'Problem Initializing OpenWQ') -!#endif -!!! -------------------- End Initialize -------------------- !!! - -!!! -------------------- Begin Update -------------------- !!! - ! ***************************************************************************** - ! * model simulation - ! ***************************************************************************** - ! loop through time - do modelTimeStep=1,numtim + ! Update + call update_summa_driver - ! read model forcing data - call summa_readForcing(modelTimeStep, summa1_struc(n), err, message) - call handle_err(err, message) - -#ifdef OPENWQ_ACTIVE - call openwq_run_time_start(summa1_struc(n)) ! Passing state volumes to openWQ -#endif - - if (mod(modelTimeStep, print_step_freq) == 0)then - print *, 'step ---> ', modelTimeStep - endif - - ! run the summa physics for one time step - call summa_runPhysics(modelTimeStep, summa1_struc(n), err, message) - call handle_err(err, message) - -#ifdef OPENWQ_ACTIVE - call openwq_run_space_step(summa1_struc(n)) ! Passing fluxes to openWQ -#endif - - ! write the model output - call summa_writeOutputFiles(modelTimeStep, summa1_struc(n), err, message) - call handle_err(err, message) - -#ifdef OPENWQ_ACTIVE - call openwq_run_time_end(summa1_struc(n)) -#endif - - end do ! looping through time -!!! -------------------- End Update -------------------- !!! - -!!! -------------------- Begin Finalize -------------------- !!! + ! Finalize call finalize_summa_driver -! ! successful end -! call stop_program(0, 'finished simulation successfully.') -! -! ! to prevent exiting before HDF5 has closed -! call sleep(2) -!!! -------------------- End Finalize -------------------- !!! - contains subroutine initialize_summa_driver ! *** Initial operations for SUMMA driver program *** - ! ***************************************************************************** - ! * preliminaries - ! ***************************************************************************** - ! allocate space for the master summa structure allocate(summa1_struc(n), stat=err) if(err/=0) call stop_program(1, 'problem allocating master summa structure') - ! ***************************************************************************** - ! * model setup/initialization - ! ***************************************************************************** - ! declare and allocate summa data structures and initialize model state to known values call summa_initialize(summa1_struc(n), err, message) call handle_err(err, message) @@ -183,6 +103,38 @@ end subroutine initialize_summa_driver subroutine update_summa_driver ! *** Update operations for SUMMA driver program *** + ! loop through time + do modelTimeStep=1,numtim + + ! read model forcing data + call summa_readForcing(modelTimeStep, summa1_struc(n), err, message) + call handle_err(err, message) + +#ifdef OPENWQ_ACTIVE + call openwq_run_time_start(summa1_struc(n)) ! Passing state volumes to openWQ +#endif + + if (mod(modelTimeStep, print_step_freq) == 0)then + print *, 'step ---> ', modelTimeStep + endif + + ! run the summa physics for one time step + call summa_runPhysics(modelTimeStep, summa1_struc(n), err, message) + call handle_err(err, message) + +#ifdef OPENWQ_ACTIVE + call openwq_run_space_step(summa1_struc(n)) ! Passing fluxes to openWQ +#endif + + ! write the model output + call summa_writeOutputFiles(modelTimeStep, summa1_struc(n), err, message) + call handle_err(err, message) + +#ifdef OPENWQ_ACTIVE + call openwq_run_time_end(summa1_struc(n)) +#endif + + end do ! looping through time end subroutine update_summa_driver subroutine finalize_summa_driver From e376278e0a48f994619994755385963b9b1ce502 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 22 Oct 2024 02:09:01 +0900 Subject: [PATCH 1436/1472] removing arguments that are unnecessary --- build/source/engine/computJacobWithPrime.f90 | 3 --- build/source/engine/computThermConduct.f90 | 10 ---------- build/source/engine/enthalpyTemp.f90 | 8 ++------ build/source/engine/eval8summa.f90 | 6 ------ build/source/engine/eval8summaWithPrime.f90 | 11 ----------- build/source/engine/getVectorz.f90 | 4 ---- build/source/engine/read_param.f90 | 2 +- build/source/engine/soilLiqFlx.f90 | 3 --- build/source/engine/systemSolv.f90 | 1 - build/source/engine/varSubstep.f90 | 4 ---- 10 files changed, 3 insertions(+), 49 deletions(-) diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index 810ca8be7..a82164bcf 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -111,7 +111,6 @@ subroutine computJacobWithPrime(& ixRichards, & ! intent(in): choice of option for Richards' equation enthalpyStateVec, & ! intent(in): flag if enthalpy is state variable ! input: data structures - model_decisions, & ! intent(in): model decisions indx_data, & ! intent(in): index data prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU @@ -144,7 +143,6 @@ subroutine computJacobWithPrime(& integer(i4b),intent(in) :: ixRichards ! choice of option for Richards' equation logical(lgt),intent(in) :: enthalpyStateVec ! flag if enthalpy is state variable ! input: data structures - type(model_options),intent(in) :: model_decisions(:) ! model decisions type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU @@ -1206,7 +1204,6 @@ integer(c_int) function computJacob4ida(t, cj, sunvec_y, sunvec_yp, sunvec_r, & eqns_data%model_decisions(iLookDECISIONS%f_Richards)%iDecision, & ! intent(in): choice of option for Richards' equation eqns_data%model_decisions(iLookDECISIONS%nrgConserv)%iDecision.ne.closedForm, & ! intent(in): flag if enthalpy is state variable ! input: data structures - eqns_data%model_decisions, & ! intent(in): model decisions eqns_data%indx_data, & ! intent(in): index data eqns_data%prog_data, & ! intent(in): model prognostic variables for a local HRU eqns_data%diag_data, & ! intent(in): model diagnostic variables for a local HRU diff --git a/build/source/engine/computThermConduct.f90 b/build/source/engine/computThermConduct.f90 index 334d6cd97..15ec191a3 100644 --- a/build/source/engine/computThermConduct.f90 +++ b/build/source/engine/computThermConduct.f90 @@ -72,12 +72,8 @@ module computThermConduct_module ! ********************************************************************************************************** subroutine computThermConduct(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux nLayers, & ! intent(in): total number of layers - canopyDepth, & ! intent(in): canopy depth (m) ! input: state variables - scalarCanopyIce, & ! intent(in): canopy ice content (kg m-2) - scalarCanopyLiquid, & ! intent(in): canopy liquid water content (kg m-2) mLayerTemp, & ! intent(in): temperature at the current iteration (K) mLayerMatricHead, & ! intent(in): matric head at the current iteration(m) mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) @@ -107,12 +103,8 @@ subroutine computThermConduct(& implicit none ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain - real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) ! input: trial model state variables - real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) - real(rkind),intent(in) :: scalarCanopyLiquid real(rkind),intent(in) :: mLayerTemp(:) ! temperature in each layer at the current iteration (m) real(rkind),intent(in) :: mLayerMatricHead(:) ! matric head in each layer at the current iteration (m) real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice at the current iteration (-) @@ -184,8 +176,6 @@ subroutine computThermConduct(& mLayerHeight => prog_data%var(iLookPROG%mLayerHeight)%dat, & ! intent(in): [dp(:)] height at the mid-point of each layer (m) iLayerHeight => prog_data%var(iLookPROG%iLayerHeight)%dat, & ! intent(in): [dp(:)] height at the interface of each layer (m) ! input: heat capacity and thermal conductivity - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): [dp] specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): [dp] maximum mass of vegetation (kg m-2) fixedThermalCond_snow => mpar_data%var(iLookPARAM%fixedThermalCond_snow)%dat(1), & ! intent(in): [dp] temporally constant thermal conductivity of snow (W m-1 K-1) ! input: depth varying soil parameters iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): [dp(:)] intrinsic density of soil (kg m-3) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index eb6adff1c..b6da0476d 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -862,7 +862,6 @@ subroutine enthalpy2T_veg(& ! declare local variables character(len=256) :: cmessage ! error message of downwind routine real(rkind) :: T,T_out ! temperature (K) - real(rkind) :: H ! enthalpy (J m-3) real(rkind) :: diffT ! temperature difference of temp from Tfreeze real(rkind) :: integral ! integral of snow freezing curve real(rkind) :: fLiq ! fraction liquid @@ -978,12 +977,10 @@ subroutine enthalpy2T_snow(& ! declare local variables character(len=256) :: cmessage ! error message of downwind routine real(rkind) :: T,T_out ! temperature (K) - real(rkind) :: H ! enthalpy (J m-3) real(rkind) :: diffT ! temperature difference of temp from Tfreeze real(rkind) :: integral ! integral of snow freezing curve real(rkind) :: fLiq ! fraction liquid real(rkind) :: vec(9) ! vector of parameters for the enthalpy function - real(rkind) :: l_bound ! lower bound for the enthalpy function ! variable derivatives real(rkind) :: dT_dEnthalpy ! derivative of temperature with enthalpy state variable real(rkind) :: dT_dWat ! derivative of temperature with water state variable @@ -1120,7 +1117,6 @@ subroutine enthalpy2T_soil(& real(rkind) :: xConst ! constant in the freezing curve function (m K-1) real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) real(rkind) :: T, T_out ! temperature (K) - real(rkind) :: H ! enthalpy (J m-3) real(rkind) :: diffT ! temperature difference of temp from Tfreeze real(rkind) :: fLiq ! fraction liquid water real(rkind) :: integral_frz_upp ! upper limit of integral of frozen soil water content (from Tfreeze to soil temperature) @@ -1637,7 +1633,7 @@ function diff_H_veg ( scalarCanopyTemp, vec) implicit none real(rkind) :: diff_H_veg real(rkind) , intent(IN) :: scalarCanopyTemp, vec(8) - real(rkind) :: scalarCanopyEnthalpy, scalarCanopyEnthTemp, scalarCanopyWat, scalarCanopyIce + real(rkind) :: scalarCanopyEnthalpy, scalarCanopyEnthTemp, scalarCanopyWat real(rkind) :: canopyDepth, specificHeatVeg, maxMassVegetation, snowfrz_scale, fLiq scalarCanopyEnthalpy = vec(1) @@ -1659,7 +1655,7 @@ function diff_H_snow ( mLayerTemp, vec) implicit none real(rkind) :: diff_H_snow real(rkind) , intent(IN) :: mLayerTemp, vec(9) - real(rkind) :: mLayerEnthalpy, mLayerEnthTemp, mLayerVolFracWat, mLayerVolFracIce, snowfrz_scale, fLiq + real(rkind) :: mLayerEnthalpy, mLayerEnthTemp, mLayerVolFracWat, snowfrz_scale, fLiq mLayerEnthalpy = vec(1) snowfrz_scale = vec(2) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index dfc8dc912..6db034003 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -356,8 +356,6 @@ subroutine eval8summa(& call varExtract(& ! input stateVec, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) @@ -465,12 +463,8 @@ subroutine eval8summa(& ! update thermal conductivity call computThermConduct(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux nLayers, & ! intent(in): total number of layers - canopyDepth, & ! intent(in): canopy depth (m) ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value of canopy liquid water (kg m-2) mLayerTempTrial, & ! intent(in): trial temperature of layer temperature (K) mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (m) mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 0d3c83a1b..255986013 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -56,7 +56,6 @@ subroutine eval8summaWithPrime(& nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers - nState, & ! intent(in): total number of state variables insideSUN, & ! intent(in): flag to indicate if we are inside Sundials solver firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call @@ -123,7 +122,6 @@ subroutine eval8summaWithPrime(& integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers - integer,intent(in) :: nState ! total number of state variables logical(lgt),intent(in) :: insideSUN ! flag to indicate if we are inside Sundials solver logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(inout) :: firstFluxCall ! flag to indicate if we are processing the first flux call @@ -337,8 +335,6 @@ subroutine eval8summaWithPrime(& call varExtract(& ! input stateVec, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy scalarCanairNrgTrial, & ! intent(inout): trial value of energy of the canopy air space, temperature (K) or enthalpy (J m-3) @@ -374,8 +370,6 @@ subroutine eval8summaWithPrime(& call varExtract(& ! input stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy scalarCanairNrgPrime, & ! intent(inout): derivative of energy of the canopy air space, temperature (K s-1) or enthalpy (W m-3) @@ -523,12 +517,8 @@ subroutine eval8summaWithPrime(& ! update thermal conductivity call computThermConduct(& ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux nLayers, & ! intent(in): total number of layers - canopyDepth, & ! intent(in): canopy depth (m) ! input: state variables - scalarCanopyIceTrial, & ! intent(in): trial value for mass of ice on the vegetation canopy (kg m-2) - scalarCanopyLiqTrial, & ! intent(in): trial value of canopy liquid water (kg m-2) mLayerTempTrial, & ! intent(in): trial temperature of layer temperature (K) mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (m) mLayerVolFracIceTrial, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) @@ -755,7 +745,6 @@ integer(c_int) function eval8summa4ida(tres, sunvec_y, sunvec_yp, sunvec_r, user eqns_data%nSnow, & ! intent(in): number of snow layers eqns_data%nSoil, & ! intent(in): number of soil layers eqns_data%nLayers, & ! intent(in): number of layers - eqns_data%nState, & ! intent(in): number of state variables in the current subset .true., & ! intent(in): inside SUNDIALS solver eqns_data%firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step eqns_data%firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index 0759bb692..eb12e28d8 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -552,8 +552,6 @@ end subroutine checkFeas subroutine varExtract(& ! input stateVec, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy scalarCanairNrgTrial, & ! intent(inout): trial value of canopy air energy, temperature (K) or enthalpy (J m-3) @@ -575,8 +573,6 @@ subroutine varExtract(& implicit none ! input real(rkind),intent(in) :: stateVec(:) ! model state vector (mixed units) - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers ! output: variables for the vegetation canopy real(rkind),intent(inout) :: scalarCanairNrgTrial ! trial value of canopy air energy, temperature (K) or enthalpy (J m-3) diff --git a/build/source/engine/read_param.f90 b/build/source/engine/read_param.f90 index 5fe0dcc80..227e2464d 100644 --- a/build/source/engine/read_param.f90 +++ b/build/source/engine/read_param.f90 @@ -206,7 +206,7 @@ subroutine read_param(iRunMode,checkHRU,startGRU,nHRU,nGRU,idStruct,mparStruct,b iGRU=index_map(1)%gru_ix localHRU_ix=index_map(1)%localHRU_ix if(hruId(checkHRU)/=idStruct%gru(iGRU)%hru(localHRU_ix)%var(iLookID%hruId))then - write(message,'(a,i0,a,i0,a)') trim(message)//'mismatch for HRU ', idStruct%gru(iGRU)%hru(localHRU_ix)%var(iLookID%hruId), '(param HRU = ', hruId(iHRU), ')' + write(message,'(a,i0,a,i0,a)') trim(message)//'mismatch for HRU ', idStruct%gru(iGRU)%hru(localHRU_ix)%var(iLookID%hruId), '(param HRU = ', hruId(checkHRU), ')' err=20; return endif diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index d93f5b362..b3369c970 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -325,7 +325,6 @@ subroutine soilLiqFlx(& deriv_desired, & ! intent(in): flag indicating if derivatives are desired ixRichards, & ! intent(in): index defining the option for Richards' equation (moisture or mixdform) ! input: state variables - mLayerTempTrial(iSoil), & ! intent(in): temperature (K) mLayerMatricHeadLiqTrial(iSoil), & ! intent(in): liquid matric head in each layer (m) mLayerVolFracLiqTrial(iSoil), & ! intent(in): volumetric liquid water content in each soil layer (-) mLayerVolFracIceTrial(iSoil), & ! intent(in): volumetric ice content in each soil layer (-) @@ -549,7 +548,6 @@ subroutine diagv_node(& deriv_desired, & ! intent(in): flag indicating if derivatives are desired ixRichards, & ! intent(in): index defining the option for Richards' equation (moisture or mixdform) ! input: state variables - scalarTempTrial, & ! intent(in): temperature (K) scalarMatricHeadLiqTrial, & ! intent(in): liquid matric head in a given layer (m) scalarVolFracLiqTrial, & ! intent(in): volumetric liquid water content in a given soil layer (-) scalarVolFracIceTrial, & ! intent(in): volumetric ice content in a given soil layer (-) @@ -600,7 +598,6 @@ subroutine diagv_node(& logical(lgt),intent(in) :: deriv_desired ! flag indicating if derivatives are desired integer(i4b),intent(in) :: ixRichards ! index defining the option for Richards' equation (moisture or mixdform) ! input: state and diagnostic variables - real(rkind),intent(in) :: scalarTempTrial ! temperature in each layer (K) real(rkind),intent(in) :: scalarMatricHeadLiqTrial ! liquid matric head in each layer (m) real(rkind),intent(in) :: scalarVolFracLiqTrial ! volumetric fraction of liquid water in a given layer (-) real(rkind),intent(in) :: scalarVolFracIceTrial ! volumetric fraction of ice in a given layer (-) diff --git a/build/source/engine/systemSolv.f90 b/build/source/engine/systemSolv.f90 index 8cb11fa6a..4b6b400c2 100644 --- a/build/source/engine/systemSolv.f90 +++ b/build/source/engine/systemSolv.f90 @@ -503,7 +503,6 @@ subroutine initial_flux_and_residual_vectors_prime nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers - nState, & ! intent(in): total number of state variables in the current subset .false., & ! intent(in): not inside Sundials solver firstSubStep, & ! intent(in): flag to indicate if we are processing the first sub-step firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index dd1c82762..356d018f7 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -847,8 +847,6 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec call varExtract(& ! input stateVecTrial, & ! intent(in): model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy scalarCanairNrgTrial, & ! intent(inout): trial value of energy of the canopy air space, temperature (K) or enthalpy (J m-3) @@ -906,8 +904,6 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec call varExtract(& ! input stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) - diag_data, & ! intent(in): model diagnostic variables for a local HRU - prog_data, & ! intent(in): model prognostic variables for a local HRU indx_data, & ! intent(in): indices defining model states and layers ! output: variables for the vegetation canopy scalarCanairNrgPrime, & ! intent(inout): derivative of energy of the canopy air space, temperature (K s-1) or enthalpy (W m-3) From 9f5b5c162aa488bdd4ec14c276428d5fbc8fa815 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 22 Oct 2024 12:48:03 +0900 Subject: [PATCH 1437/1472] name of openWQ module misspelled --- build/source/openwq/summa_openWQ.f90 | 4 ++-- build/source/openwq/summa_openWQ_allocspace.f90 | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build/source/openwq/summa_openWQ.f90 b/build/source/openwq/summa_openWQ.f90 index ad83167f1..e8eeb6974 100644 --- a/build/source/openwq/summa_openWQ.f90 +++ b/build/source/openwq/summa_openWQ.f90 @@ -23,7 +23,7 @@ subroutine openwq_init(err) USE globalData,only:gru_struc ! gru-hru mapping structures USE globalData,only:prog_meta USE globalData,only:maxLayers,maxSnowLayers - USE allocspace_progStuct_module,only:allocGlobal_porgStruct ! module to allocate space for global data structures + USE allocspace_progStuct_module,only:allocGlobal_progStruct ! module to allocate space for global data structures implicit none ! Dummy Varialbes @@ -62,7 +62,7 @@ subroutine openwq_init(err) ! Create copy of state information, needed for passing to openWQ with fluxes that require ! the previous time_steps volume - call allocGlobal_porgStruct(prog_meta,progStruct_timestep_start,maxSnowLayers,err,message) + call allocGlobal_progStruct(prog_meta,progStruct_timestep_start,maxSnowLayers,err,message) end subroutine openwq_init diff --git a/build/source/openwq/summa_openWQ_allocspace.f90 b/build/source/openwq/summa_openWQ_allocspace.f90 index 546f03e1a..40aca8d04 100644 --- a/build/source/openwq/summa_openWQ_allocspace.f90 +++ b/build/source/openwq/summa_openWQ_allocspace.f90 @@ -65,7 +65,7 @@ module allocspace_progStuct_module ! privacy implicit none private - public::allocGlobal_porgStruct + public::allocGlobal_progStruct ! ----------------------------------------------------------------------------------------------------------------------------------- @@ -75,7 +75,7 @@ module allocspace_progStuct_module ! Modified copy of the subroutine allocGlobal() from allocspace.f90 specificly for allocating ! the array progStruct_timestep_start ! ************************************************************************************************ - subroutine allocGlobal_porgStruct(metaStruct,dataStruct,nSnow,err,message) + subroutine allocGlobal_progStruct(metaStruct,dataStruct,nSnow,err,message) ! NOTE: safety -- ensure only used in allocGlobal USE globalData,only: gru_struc ! gru-hru mapping structures USE allocspace_module, only:allocLocal @@ -95,7 +95,7 @@ subroutine allocGlobal_porgStruct(metaStruct,dataStruct,nSnow,err,message) logical(lgt) :: spatial ! spatial flag character(len=256) :: cmessage ! error message of the downwind routine ! initialize error control - err=0; message='allocGlobal_porgStruct/' + err=0; message='allocGlobal_progStruct/' ! initialize allocation check check=.false. @@ -198,7 +198,7 @@ subroutine allocGlobal_porgStruct(metaStruct,dataStruct,nSnow,err,message) ! error check if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if - end subroutine allocGlobal_porgStruct + end subroutine allocGlobal_progStruct end module allocspace_progStuct_module \ No newline at end of file From 35afb25ba14c05f70d6d6b2baa5092d2324f3c96 Mon Sep 17 00:00:00 2001 From: seantrim Date: Mon, 21 Oct 2024 23:00:42 -0600 Subject: [PATCH 1438/1472] Fail-safe to avoid an infinite loop for unforeseen errors in split_select_loop is now handled via the split_select object (instead of a do loop counter). --- build/source/engine/opSplittin.f90 | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index a23946af8..c616c7467 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -135,6 +135,7 @@ module opSplittin_module type, public :: split_select_type ! class for selecting operator splitting methods ! opSplittin indices (in order) + integer(i4b) :: iSplit ! iteration counter for split_select_loop integer(i4b) :: ixCoupling integer(i4b) :: iStateTypeSplit integer(i4b) :: ixStateThenDomain ! 1=state type split; 2=domain split within a given state type @@ -159,6 +160,7 @@ module opSplittin_module procedure :: get_stateMask => split_select_compute_stateMask ! compute stateMask and nSubset and load into class object + procedure :: advance_iSplit => split_select_advance_iSplit ! advance coupling iterator procedure :: advance_ixCoupling => split_select_advance_ixCoupling ! advance coupling iterator procedure :: advance_iStateTypeSplit => split_select_advance_iStateTypeSplit ! advance stateTypeSplitting iterator procedure :: advance_ixStateThenDomain => split_select_advance_ixStateThenDomain ! advance stateThenDomain iterator @@ -334,7 +336,6 @@ subroutine opSplittin(& logical(lgt) :: exit_split_select,cycle_split_select ! control for split_select loop logical(lgt) :: exit_coupling,exit_stateThenDomain,exit_solution logical(lgt) :: cycle_coupling,cycle_stateThenDomain,cycle_domainSplit,cycle_solution - integer(i4b) :: iSplit integer(i4b),parameter :: maxSplit=500 ! >= max number of splitting methods (controls upper limit of split_select loop) ! ------------------------ classes for subroutine arguments (classes defined in data_types module) ------------------------ ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** @@ -346,7 +347,7 @@ subroutine opSplittin(& ! *** Initialize Split Selector Object *** call initialize_split_select; if (return_flag) return call initialize_split_coupling; if (return_flag) return - split_select_loop: do iSplit=1,maxSplit ! coupling begins + split_select_loop: do ! coupling begins call initialize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop; if (return_flag) return if (split_select % stateTypeSplitting) then ! stateTypeSplitting begins call initialize_split_stateThenDomain @@ -390,7 +391,7 @@ subroutine opSplittin(& end if ! stateThenDomain ends call finalize_split_stateThenDomain; if (return_flag) return end if ! stateTypeSplitting ends - call finalize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop + call finalize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop; if (return_flag) return end do split_select_loop ! coupling ends call finalize_split_coupling; if (return_flag) return @@ -400,6 +401,9 @@ subroutine opSplittin(& subroutine initialize_split_select ! *** Initialize split_select class object *** + ! initizlaize iteration counter for split_select_loop + split_select % iSplit = 1 + ! initialize # of state variables split_select % nState = nState @@ -549,13 +553,14 @@ subroutine finalize_split_stateTypeSplitting end if call split_select % advance_ixCoupling end if + call split_select % advance_iSplit ! advance iteration counter for split_select_loop + if (split_select % iSplit.ge.maxSplit) then ! check for errors + err=20; message=trim(message)//'split_select loop exceeded max number of iterations'; return_flag=.true.; return + end if end subroutine finalize_split_stateTypeSplitting subroutine finalize_split_coupling ! *** Finalize steps for coupling split method *** - if (iSplit.gt.maxSplit) then ! check for errors - err=20; message=trim(message)//'split_select loop exceeded max number of iterations'; return_flag=.true.; return - end if call finalize_coupling; if (return_flag) return ! check variables and fluxes, and apply step halving if needed end subroutine finalize_split_coupling @@ -1200,6 +1205,12 @@ subroutine split_select_initialize_flags(split_select) split_select % stateSplit=.false. end subroutine split_select_initialize_flags +subroutine split_select_advance_iSplit(split_select) + ! *** Advance index for coupling split method *** + class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector + split_select % iSplit = split_select % iSplit + 1 +end subroutine split_select_advance_iSplit + subroutine split_select_advance_ixCoupling(split_select) ! *** Advance index for coupling split method *** class(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector From be57e3b369a05ec00e61b70de8ed6ccbb4e885db Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 23 Oct 2024 04:58:54 -0600 Subject: [PATCH 1439/1472] Applied initialize-update-finalize sequence to opSplittin module subroutine via internal subroutines. --- build/source/engine/opSplittin.f90 | 122 +++++++++++++++++------------ 1 file changed, 72 insertions(+), 50 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index c616c7467..b2e004e95 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -344,59 +344,81 @@ subroutine opSplittin(& ! ------------------------------------------------------------------------------------------------------------------------- type(split_select_type) :: split_select ! class object for selecting operator splitting methods - ! *** Initialize Split Selector Object *** - call initialize_split_select; if (return_flag) return - call initialize_split_coupling; if (return_flag) return - split_select_loop: do ! coupling begins - call initialize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop; if (return_flag) return - if (split_select % stateTypeSplitting) then ! stateTypeSplitting begins - call initialize_split_stateThenDomain - if (split_select % stateThenDomain) then ! stateThenDomain begins - call initialize_split_domainSplit; if (return_flag) return - if (split_select % domainSplit) then ! domainSplit begins - call initialize_split_solution - if (split_select % solution) then ! solution begins - call initialize_split_stateSplit; if (return_flag) return - if (split_select % stateSplit) then ! stateSplit begins - call update_stateMask; if (return_flag) return ! get the mask for the state subset - return for a non-zero error code - call validate_split ! verify that the split is valid - if (cycle_domainSplit) cycle split_select_loop ! if needed, proceed to next iteration of domainSplit method - if (cycle_solution) cycle split_select_loop ! if needed, proceed to next iteration of solution method - if (return_flag) return ! return for a non-zero error code - - call save_recover ! save/recover copies of variables and fluxes - - call get_split_indices; if (return_flag) return ! get indices for a given split - return for a non-zero error code - call update_fluxMask; if (return_flag) return ! define the mask for the fluxes used - return for a non-zero error code - - call solve_subset; if (return_flag) return ! solve variable subset for one time step - return for a positive error code - call assess_solution; if (return_flag) return ! is solution a success or failure? - return for a recovering solution - - call try_other_solution_methods ! if solution failed to converge, try other splitting methods - if (cycle_coupling) cycle split_select_loop ! if needed, proceed to next iteration of coupling method - if (cycle_stateThenDomain) cycle split_select_loop ! if needed, proceed to next iteration of stateThenDomain method - if (cycle_solution) cycle split_select_loop ! if needed, proceed to next iteration of solution method - - call confirm_variable_updates; if (return_flag) return ! check that state variables are updated - return if error - - call success_check ! check for success - call check_exit_stateThenDomain ! check exit criterion for stateThenDomain split - call check_exit_solution; if (return_flag) return ! check exit criterion for solution split - return if error - end if ! stateSplit ends - call finalize_split_stateSplit - end if ! solution ends - call finalize_split_solution - end if ! domainSplit ends - call finalize_split_domainSplit - end if ! stateThenDomain ends - call finalize_split_stateThenDomain; if (return_flag) return - end if ! stateTypeSplitting ends - call finalize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop; if (return_flag) return - end do split_select_loop ! coupling ends - call finalize_split_coupling; if (return_flag) return + call initialize_opSplittin; if (return_flag) return + + call update_opSplittin; if (return_flag) return + + call finalize_opSplittin; if (return_flag) return contains + subroutine initialize_opSplittin + ! *** Initial operations for opSplittin *** + call initialize_split_select; if (return_flag) return ! initialize split selector object (split_select) + call initialize_split_coupling; if (return_flag) return ! prep for first iteration of update_opSplittin + end subroutine initialize_opSplittin + + subroutine update_opSplittin + ! *** Update operations for opSplittin *** + split_select_loop: do ! coupling begins + call initialize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop; if (return_flag) return + if (split_select % stateTypeSplitting) then ! stateTypeSplitting begins + call initialize_split_stateThenDomain + if (split_select % stateThenDomain) then ! stateThenDomain begins + call initialize_split_domainSplit; if (return_flag) return + if (split_select % domainSplit) then ! domainSplit begins + call initialize_split_solution + if (split_select % solution) then ! solution begins + call initialize_split_stateSplit; if (return_flag) return + if (split_select % stateSplit) then ! stateSplit begins + !! create split + call update_stateMask; if (return_flag) return ! get the mask for the state subset - return for a non-zero error code + call validate_split ! verify that the split is valid + if (cycle_domainSplit) cycle split_select_loop ! if needed, proceed to next iteration of domainSplit method + if (cycle_solution) cycle split_select_loop ! if needed, proceed to next iteration of solution method + if (return_flag) return ! return for a non-zero error code + + call save_recover ! save/recover copies of variables and fluxes + + call get_split_indices; if (return_flag) return ! get indices for a given split - return for a non-zero error code + call update_fluxMask; if (return_flag) return ! define the mask for the fluxes used - return for a non-zero error code + !! end create split + + !! solve subset + call solve_subset; if (return_flag) return ! solve variable subset for one time step - return for a positive error code + !! end solve subset + + !! validate solution + call assess_solution; if (return_flag) return ! is solution a success or failure? - return for a recovering solution + + call try_other_solution_methods ! if solution failed to converge, try other splitting methods + if (cycle_coupling) cycle split_select_loop ! if needed, proceed to next iteration of coupling method + if (cycle_stateThenDomain) cycle split_select_loop ! if needed, proceed to next iteration of stateThenDomain method + if (cycle_solution) cycle split_select_loop ! if needed, proceed to next iteration of solution method + + call confirm_variable_updates; if (return_flag) return ! check that state variables are updated - return if error + + call success_check ! check for success + call check_exit_stateThenDomain ! check exit criterion for stateThenDomain split + call check_exit_solution; if (return_flag) return ! check exit criterion for solution split - return if error + !! end validate solution + end if ! stateSplit ends + call finalize_split_stateSplit + end if ! solution ends + call finalize_split_solution + end if ! domainSplit ends + call finalize_split_domainSplit + end if ! stateThenDomain ends + call finalize_split_stateThenDomain; if (return_flag) return + end if ! stateTypeSplitting ends + call finalize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop; if (return_flag) return + end do split_select_loop ! coupling ends + end subroutine update_opSplittin + + subroutine finalize_opSplittin + ! *** Final operations for opSplittin *** + call finalize_split_coupling; if (return_flag) return + end subroutine finalize_opSplittin subroutine initialize_split_select ! *** Initialize split_select class object *** From 84a8c6402c8ee0f27fd71706c16c216949eede90 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 23 Oct 2024 06:30:47 -0600 Subject: [PATCH 1440/1472] Added initialize_split, update_split, and finalize_split internal subroutines to modularize update_opSplittin routine in opSplittin.f90 -- work in progress. --- build/source/engine/opSplittin.f90 | 58 +++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index b2e004e95..00d08bb10 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -371,7 +371,7 @@ subroutine update_opSplittin if (split_select % solution) then ! solution begins call initialize_split_stateSplit; if (return_flag) return if (split_select % stateSplit) then ! stateSplit begins - !! create split + !! create split -- call initialize_split call update_stateMask; if (return_flag) return ! get the mask for the state subset - return for a non-zero error code call validate_split ! verify that the split is valid if (cycle_domainSplit) cycle split_select_loop ! if needed, proceed to next iteration of domainSplit method @@ -384,23 +384,27 @@ subroutine update_opSplittin call update_fluxMask; if (return_flag) return ! define the mask for the fluxes used - return for a non-zero error code !! end create split - !! solve subset - call solve_subset; if (return_flag) return ! solve variable subset for one time step - return for a positive error code + !! solve subset -- call update_split -- draft done + call update_split; if (return_flag) return + !call solve_subset; if (return_flag) return ! solve variable subset for one time step - return for a positive error code !! end solve subset - !! validate solution - call assess_solution; if (return_flag) return ! is solution a success or failure? - return for a recovering solution + !! validate solution -- call finalize_split -- draft done + call finalize_split; if (return_flag) return + if (any([cycle_coupling,cycle_stateThenDomain,cycle_solution])) cycle split_select_loop ! if needed, proceed to next split - call try_other_solution_methods ! if solution failed to converge, try other splitting methods - if (cycle_coupling) cycle split_select_loop ! if needed, proceed to next iteration of coupling method - if (cycle_stateThenDomain) cycle split_select_loop ! if needed, proceed to next iteration of stateThenDomain method - if (cycle_solution) cycle split_select_loop ! if needed, proceed to next iteration of solution method + !call assess_solution; if (return_flag) return ! is solution a success or failure? - return for a recovering solution - call confirm_variable_updates; if (return_flag) return ! check that state variables are updated - return if error + !call try_other_solution_methods ! if solution failed to converge, try other splitting methods + !if (cycle_coupling) cycle split_select_loop ! if needed, proceed to next iteration of coupling method + !if (cycle_stateThenDomain) cycle split_select_loop ! if needed, proceed to next iteration of stateThenDomain method + !if (cycle_solution) cycle split_select_loop ! if needed, proceed to next iteration of solution method - call success_check ! check for success - call check_exit_stateThenDomain ! check exit criterion for stateThenDomain split - call check_exit_solution; if (return_flag) return ! check exit criterion for solution split - return if error + !call confirm_variable_updates; if (return_flag) return ! check that state variables are updated - return if error + + !call success_check ! check for success + !call check_exit_stateThenDomain ! check exit criterion for stateThenDomain split + !call check_exit_solution; if (return_flag) return ! check exit criterion for solution split - return if error !! end validate solution end if ! stateSplit ends call finalize_split_stateSplit @@ -420,6 +424,34 @@ subroutine finalize_opSplittin call finalize_split_coupling; if (return_flag) return end subroutine finalize_opSplittin + subroutine initialize_split + ! *** Initialize logical masks for selected splitting method *** + end subroutine initialize_split + + subroutine update_split + ! *** Update solution for selected splitting method *** + call solve_subset; if (return_flag) return ! solve variable subset for one time step - return for a positive error code + end subroutine update_split + + subroutine finalize_split + ! *** Finalize solution for selected splitting method *** + call assess_solution; if (return_flag) return ! is solution a success or failure? - return for a recovering solution + + call try_other_solution_methods ! if solution failed to converge, try other splitting methods + if (cycle_coupling) return ! if needed, proceed to next iteration of coupling method + if (cycle_stateThenDomain) return ! if needed, proceed to next iteration of stateThenDomain method + if (cycle_solution) return ! if needed, proceed to next iteration of solution method + !if (cycle_coupling) cycle split_select_loop ! if needed, proceed to next iteration of coupling method + !if (cycle_stateThenDomain) cycle split_select_loop ! if needed, proceed to next iteration of stateThenDomain method + !if (cycle_solution) cycle split_select_loop ! if needed, proceed to next iteration of solution method + + call confirm_variable_updates; if (return_flag) return ! check that state variables are updated - return if error + + call success_check ! check for success + call check_exit_stateThenDomain ! check exit criterion for stateThenDomain split + call check_exit_solution; if (return_flag) return ! check exit criterion for solution split - return if error + end subroutine finalize_split + subroutine initialize_split_select ! *** Initialize split_select class object *** From 298c9c2cd31875f7c2a60565ce52407188adf9b0 Mon Sep 17 00:00:00 2001 From: seantrim Date: Thu, 24 Oct 2024 01:45:47 -0600 Subject: [PATCH 1441/1472] Implementation of initialize_split, update_split, and finalize_split internal subroutines in opSplittin is now complete and tested. --- build/source/engine/opSplittin.f90 | 54 +++++++++--------------------- 1 file changed, 16 insertions(+), 38 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 00d08bb10..e8f6f863d 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -371,41 +371,15 @@ subroutine update_opSplittin if (split_select % solution) then ! solution begins call initialize_split_stateSplit; if (return_flag) return if (split_select % stateSplit) then ! stateSplit begins - !! create split -- call initialize_split - call update_stateMask; if (return_flag) return ! get the mask for the state subset - return for a non-zero error code - call validate_split ! verify that the split is valid - if (cycle_domainSplit) cycle split_select_loop ! if needed, proceed to next iteration of domainSplit method - if (cycle_solution) cycle split_select_loop ! if needed, proceed to next iteration of solution method - if (return_flag) return ! return for a non-zero error code - - call save_recover ! save/recover copies of variables and fluxes - - call get_split_indices; if (return_flag) return ! get indices for a given split - return for a non-zero error code - call update_fluxMask; if (return_flag) return ! define the mask for the fluxes used - return for a non-zero error code - !! end create split - - !! solve subset -- call update_split -- draft done - call update_split; if (return_flag) return - !call solve_subset; if (return_flag) return ! solve variable subset for one time step - return for a positive error code - !! end solve subset - - !! validate solution -- call finalize_split -- draft done - call finalize_split; if (return_flag) return - if (any([cycle_coupling,cycle_stateThenDomain,cycle_solution])) cycle split_select_loop ! if needed, proceed to next split - !call assess_solution; if (return_flag) return ! is solution a success or failure? - return for a recovering solution + call initialize_split; if (return_flag) return + if (any([cycle_domainSplit,cycle_solution])) cycle split_select_loop - !call try_other_solution_methods ! if solution failed to converge, try other splitting methods - !if (cycle_coupling) cycle split_select_loop ! if needed, proceed to next iteration of coupling method - !if (cycle_stateThenDomain) cycle split_select_loop ! if needed, proceed to next iteration of stateThenDomain method - !if (cycle_solution) cycle split_select_loop ! if needed, proceed to next iteration of solution method + call update_split; if (return_flag) return - !call confirm_variable_updates; if (return_flag) return ! check that state variables are updated - return if error + call finalize_split; if (return_flag) return + if (any([cycle_coupling,cycle_stateThenDomain,cycle_solution])) cycle split_select_loop ! if needed, proceed to next split - !call success_check ! check for success - !call check_exit_stateThenDomain ! check exit criterion for stateThenDomain split - !call check_exit_solution; if (return_flag) return ! check exit criterion for solution split - return if error - !! end validate solution end if ! stateSplit ends call finalize_split_stateSplit end if ! solution ends @@ -413,7 +387,7 @@ subroutine update_opSplittin end if ! domainSplit ends call finalize_split_domainSplit end if ! stateThenDomain ends - call finalize_split_stateThenDomain; if (return_flag) return + call finalize_split_stateThenDomain; if (return_flag) return end if ! stateTypeSplitting ends call finalize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop; if (return_flag) return end do split_select_loop ! coupling ends @@ -426,6 +400,15 @@ end subroutine finalize_opSplittin subroutine initialize_split ! *** Initialize logical masks for selected splitting method *** + call update_stateMask; if (return_flag) return ! get the mask for the state subset - return for a non-zero error code + call validate_split ! verify that the split is valid + if (any([cycle_domainSplit,cycle_solution])) return ! if needed, proceed to next iteration of domainSplit method + if (return_flag) return ! return for a non-zero error code + + call save_recover ! save/recover copies of variables and fluxes + + call get_split_indices; if (return_flag) return ! get indices for a given split - return for a non-zero error code + call update_fluxMask; if (return_flag) return ! define the mask for the fluxes used - return for a non-zero error code end subroutine initialize_split subroutine update_split @@ -438,12 +421,7 @@ subroutine finalize_split call assess_solution; if (return_flag) return ! is solution a success or failure? - return for a recovering solution call try_other_solution_methods ! if solution failed to converge, try other splitting methods - if (cycle_coupling) return ! if needed, proceed to next iteration of coupling method - if (cycle_stateThenDomain) return ! if needed, proceed to next iteration of stateThenDomain method - if (cycle_solution) return ! if needed, proceed to next iteration of solution method - !if (cycle_coupling) cycle split_select_loop ! if needed, proceed to next iteration of coupling method - !if (cycle_stateThenDomain) cycle split_select_loop ! if needed, proceed to next iteration of stateThenDomain method - !if (cycle_solution) cycle split_select_loop ! if needed, proceed to next iteration of solution method + if (any([cycle_coupling,cycle_stateThenDomain,cycle_solution])) return ! if needed, proceed to next split call confirm_variable_updates; if (return_flag) return ! check that state variables are updated - return if error From 3e8062fac8aad52f1a08ae121891f7dce94fc2e1 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 25 Oct 2024 08:46:32 +0900 Subject: [PATCH 1442/1472] fixing initialize_coupled_em --- build/source/engine/coupled_em.f90 | 61 ++++++++++++++---------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index d29d52d10..da098663f 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -314,13 +314,6 @@ subroutine coupled_em(& ! initialize variables call initialize_coupled_em - ! initialize the numerix tracking variables - indx_data%var(iLookINDEX%numberFluxCalc )%dat(1) = 0 ! number of flux calculations (-) - indx_data%var(iLookINDEX%numberStateSplit )%dat(1) = 0 ! number of state splitting solutions (-) - indx_data%var(iLookINDEX%numberDomainSplitNrg )%dat(1) = 0 ! number of domain splitting solutions for energy (-) - indx_data%var(iLookINDEX%numberDomainSplitMass)%dat(1) = 0 ! number of domain splitting solutions for mass (-) - indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1) = 0 ! number of scalar solutions (-) - ! link canopy depth to the information in the data structure canopy: associate(& snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ,& ! scaling parameter for the snow freezing curve (K-1) @@ -345,15 +338,6 @@ subroutine coupled_em(& firstInnerStep = .true. lastInnerStep = .false. - ! count the number of snow and soil layers - ! NOTE: need to recompute the number of snow and soil layers at the start of each sub-step because the number of layers may change - ! (nSnow and nSoil are shared in the data structure) - nSnow = count(indx_data%var(iLookINDEX%layerType)%dat==iname_snow) - nSoil = count(indx_data%var(iLookINDEX%layerType)%dat==iname_soil) - - ! compute the total number of snow and soil layers - nLayers = nSnow + nSoil - ! create temporary data structures for prognostic variables call resizeData(prog_meta(:),prog_data,prog_temp,err=err,message=cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif @@ -380,22 +364,7 @@ subroutine coupled_em(& do iVar=1,size(averageFlux_meta) flux_mean%var(iVar)%dat(:) = 0._rkind end do - meanCanopySublimation = 0._rkind ! mean canopy sublimation - meanLatHeatCanopyEvap = 0._rkind ! mean latent heat flux for evaporation from the canopy - meanSenHeatCanopy = 0._rkind ! mean sensible heat flux from the canopy - effRainfall = 0._rkind ! mean total effective rainfall over snow - - diag_data%var(iLookDIAG%meanStepSize)%dat(1) = 0._rkind ! mean step size over data_step - - ! Need mean soil compression for balance checks but it is not in flux structure so handle differently - ! This will be a problem if nSoil changes (currently not possible)-- then might need to not keep the average - allocate(meanSoilCompress(nSoil)) - allocate(innerSoilCompress(nSoil)) - meanSoilCompress = 0._rkind ! mean total soil compression - - ! initialize the balance checks - meanBalance = 0._rkind - + ! associate local variables with information in the data structures associate(& ! model decisions @@ -1707,6 +1676,34 @@ subroutine initialize_coupled_em allocate(innerBalanceLayerMass(nLayers)); innerBalanceLayerMass = 0._rkind ! mean total balance of mass in layers allocate(innerBalanceLayerNrg(nLayers)); innerBalanceLayerNrg = 0._rkind ! mean total balance of energy in layers allocate(mLayerVolFracIceInit(nLayers)); mLayerVolFracIceInit = prog_data%var(iLookPROG%mLayerVolFracIce)%dat ! volume fraction of water ice + + ! initialize the numerix tracking variables + indx_data%var(iLookINDEX%numberFluxCalc )%dat(1) = 0 ! number of flux calculations (-) + indx_data%var(iLookINDEX%numberStateSplit )%dat(1) = 0 ! number of state splitting solutions (-) + indx_data%var(iLookINDEX%numberDomainSplitNrg )%dat(1) = 0 ! number of domain splitting solutions for energy (-) + indx_data%var(iLookINDEX%numberDomainSplitMass)%dat(1) = 0 ! number of domain splitting solutions for mass (-) + indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1) = 0 ! number of scalar solutions (-) + + ! initialize surface melt pond + sfcMeltPond = 0._rkind ! change in storage associated with the surface melt pond (kg m-2) + + ! initialize average over data_step (averaged over substep in varSubStep) + meanCanopySublimation = 0._rkind ! mean canopy sublimation + meanLatHeatCanopyEvap = 0._rkind ! mean latent heat flux for evaporation from the canopy + meanSenHeatCanopy = 0._rkind ! mean sensible heat flux from the canopy + effRainfall = 0._rkind ! mean total effective rainfall over snow + + diag_data%var(iLookDIAG%meanStepSize)%dat(1) = 0._rkind ! mean step size over data_step + + ! Need mean soil compression for balance checks but it is not in flux structure so handle differently + ! This will be a problem if nSoil changes (currently not possible)-- then might need to not keep the average + allocate(meanSoilCompress(nSoil)) + allocate(innerSoilCompress(nSoil)) + meanSoilCompress = 0._rkind ! mean total soil compression + + ! initialize the balance checks + meanBalance = 0._rkind + end subroutine initialize_coupled_em end subroutine coupled_em From 57620aeecc7c5217ee98e11a30ccce5c18cddc75 Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 25 Oct 2024 00:55:28 -0600 Subject: [PATCH 1443/1472] Simplified treatment of logical flags for control of main loop in opSplittin (split_select_loop). --- build/source/engine/opSplittin.f90 | 44 ++++++++++++++++++------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index e8f6f863d..07c9c5a37 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -372,13 +372,11 @@ subroutine update_opSplittin call initialize_split_stateSplit; if (return_flag) return if (split_select % stateSplit) then ! stateSplit begins - call initialize_split; if (return_flag) return - if (any([cycle_domainSplit,cycle_solution])) cycle split_select_loop + call initialize_split; if (return_flag) return; if (cycle_initialize_split()) cycle split_select_loop call update_split; if (return_flag) return - call finalize_split; if (return_flag) return - if (any([cycle_coupling,cycle_stateThenDomain,cycle_solution])) cycle split_select_loop ! if needed, proceed to next split + call finalize_split; if (return_flag) return; if (cycle_finalize_split()) cycle split_select_loop end if ! stateSplit ends call finalize_split_stateSplit @@ -400,15 +398,14 @@ end subroutine finalize_opSplittin subroutine initialize_split ! *** Initialize logical masks for selected splitting method *** - call update_stateMask; if (return_flag) return ! get the mask for the state subset - return for a non-zero error code - call validate_split ! verify that the split is valid - if (any([cycle_domainSplit,cycle_solution])) return ! if needed, proceed to next iteration of domainSplit method - if (return_flag) return ! return for a non-zero error code + call update_stateMask; if (return_flag) return ! get the mask for the state subset - return for a non-zero error code + call validate_split; if (return_flag) return ! verify that the split is valid + if (cycle_initialize_split()) return ! if needed, proceed to next split - call save_recover ! save/recover copies of variables and fluxes + call save_recover ! save/recover copies of variables and fluxes - call get_split_indices; if (return_flag) return ! get indices for a given split - return for a non-zero error code - call update_fluxMask; if (return_flag) return ! define the mask for the fluxes used - return for a non-zero error code + call get_split_indices; if (return_flag) return ! get indices for a given split - return for a non-zero error code + call update_fluxMask; if (return_flag) return ! define the mask for the fluxes used - return for a non-zero error code end subroutine initialize_split subroutine update_split @@ -418,18 +415,29 @@ end subroutine update_split subroutine finalize_split ! *** Finalize solution for selected splitting method *** - call assess_solution; if (return_flag) return ! is solution a success or failure? - return for a recovering solution + call assess_solution; if (return_flag) return ! is solution a success or failure? - return for a recovering solution - call try_other_solution_methods ! if solution failed to converge, try other splitting methods - if (any([cycle_coupling,cycle_stateThenDomain,cycle_solution])) return ! if needed, proceed to next split + call try_other_solution_methods; if (cycle_finalize_split()) return ! if solution failed to converge, try other splitting methods - call confirm_variable_updates; if (return_flag) return ! check that state variables are updated - return if error + call confirm_variable_updates; if (return_flag) return ! check that state variables are updated - return if error - call success_check ! check for success - call check_exit_stateThenDomain ! check exit criterion for stateThenDomain split - call check_exit_solution; if (return_flag) return ! check exit criterion for solution split - return if error + call success_check ! check for success + call check_exit_stateThenDomain ! check exit criterion for stateThenDomain split + call check_exit_solution; if (return_flag) return ! check exit criterion for solution split - return if error end subroutine finalize_split + function cycle_initialize_split() result(cycle_flag) + ! *** Compute loop cycle flag for initialize_split *** + logical(lgt) :: cycle_flag + cycle_flag=any([cycle_domainSplit,cycle_solution]) + end function cycle_initialize_split + + function cycle_finalize_split() result(cycle_flag) + ! *** Compute loop cycle flag for finalize_split *** + logical(lgt) :: cycle_flag + cycle_flag=any([cycle_coupling,cycle_stateThenDomain,cycle_solution]) + end function cycle_finalize_split + subroutine initialize_split_select ! *** Initialize split_select class object *** From 748f8011bce468e136a88d37041fb40638d6f5e1 Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 25 Oct 2024 07:50:31 -0600 Subject: [PATCH 1444/1472] Refactoring the main loop in opSplittin using the initialize-update-finalize strategy -- work in progress. --- build/source/engine/opSplittin.f90 | 85 +++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 07c9c5a37..0922c8e9c 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -358,7 +358,7 @@ subroutine initialize_opSplittin call initialize_split_coupling; if (return_flag) return ! prep for first iteration of update_opSplittin end subroutine initialize_opSplittin - subroutine update_opSplittin + subroutine update_opSplittin_OG ! -------------------------------------------------- SJT: Take out ! *** Update operations for opSplittin *** split_select_loop: do ! coupling begins call initialize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop; if (return_flag) return @@ -389,6 +389,49 @@ subroutine update_opSplittin end if ! stateTypeSplitting ends call finalize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop; if (return_flag) return end do split_select_loop ! coupling ends + end subroutine update_opSplittin_OG + + subroutine update_opSplittin + ! *** Update operations for opSplittin *** + split_select_loop: do ! coupling begins + + call initialize_split_solve; if (exit_split_select) exit split_select_loop; if (return_flag) return + + call update_split_solve; if (return_flag) return; if (cycle_split_select) cycle split_select_loop + + call finalize_split_solve; if (exit_split_select) exit split_select_loop; if (return_flag) return + + !call initialize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop; if (return_flag) return + + !if (split_select % stateTypeSplitting) then ! stateTypeSplitting begins + ! call initialize_split_stateThenDomain + ! if (split_select % stateThenDomain) then ! stateThenDomain begins + ! call initialize_split_domainSplit; if (return_flag) return + ! if (split_select % domainSplit) then ! domainSplit begins + ! call initialize_split_solution + ! if (split_select % solution) then ! solution begins + ! call initialize_split_stateSplit; if (return_flag) return + ! if (split_select % stateSplit) then ! stateSplit begins + + ! call initialize_split; if (return_flag) return; if (cycle_initialize_split()) cycle split_select_loop + + ! call update_split; if (return_flag) return + + ! call finalize_split; if (return_flag) return; if (cycle_finalize_split()) cycle split_select_loop + + ! end if ! stateSplit ends + ! call finalize_split_stateSplit + ! end if ! solution ends + ! call finalize_split_solution + ! end if ! domainSplit ends + ! call finalize_split_domainSplit + ! end if ! stateThenDomain ends + ! call finalize_split_stateThenDomain; if (return_flag) return + !end if ! stateTypeSplitting ends + + !call finalize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop; if (return_flag) return + + end do split_select_loop ! coupling ends end subroutine update_opSplittin subroutine finalize_opSplittin @@ -396,6 +439,46 @@ subroutine finalize_opSplittin call finalize_split_coupling; if (return_flag) return end subroutine finalize_opSplittin + subroutine initialize_split_solve + ! *** Initial operations for solving the selected split *** + call initialize_split_stateTypeSplitting; if (exit_split_select.or.return_flag) return + cycle_split_select=.false. ! initialize flag for cycle control of split_select_loop + end subroutine initialize_split_solve + + subroutine update_split_solve + ! *** Update operations for solving the selected split *** + if (split_select % stateTypeSplitting) then ! stateTypeSplitting begins + call initialize_split_stateThenDomain + if (split_select % stateThenDomain) then ! stateThenDomain begins + call initialize_split_domainSplit; if (return_flag) return + if (split_select % domainSplit) then ! domainSplit begins + call initialize_split_solution + if (split_select % solution) then ! solution begins + call initialize_split_stateSplit; if (return_flag) return + if (split_select % stateSplit) then ! stateSplit begins + + call initialize_split; if (return_flag) return; if (cycle_initialize_split()) then; cycle_split_select=.true.; return; end if + + call update_split; if (return_flag) return + + call finalize_split; if (return_flag) return; if (cycle_finalize_split()) then; cycle_split_select=.true.; return; end if + + end if ! stateSplit ends + call finalize_split_stateSplit + end if ! solution ends + call finalize_split_solution + end if ! domainSplit ends + call finalize_split_domainSplit + end if ! stateThenDomain ends + call finalize_split_stateThenDomain; if (return_flag) return + end if ! stateTypeSplitting ends + end subroutine update_split_solve + + subroutine finalize_split_solve + ! *** Final operations for solving the selected split *** + call finalize_split_stateTypeSplitting; if (exit_split_select.or.return_flag) return + end subroutine finalize_split_solve + subroutine initialize_split ! *** Initialize logical masks for selected splitting method *** call update_stateMask; if (return_flag) return ! get the mask for the state subset - return for a non-zero error code From a7ec124736fd90db95b54b0494a687cdb1f75258 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 26 Oct 2024 05:26:22 -0600 Subject: [PATCH 1445/1472] Removed extraneous code and updated comments related to refactoring in opSplittin.f90 --- build/source/engine/opSplittin.f90 | 114 +++++++---------------------- 1 file changed, 27 insertions(+), 87 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 0922c8e9c..a0be8b3f8 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -344,11 +344,14 @@ subroutine opSplittin(& ! ------------------------------------------------------------------------------------------------------------------------- type(split_select_type) :: split_select ! class object for selecting operator splitting methods + ! set up split_select object and prepare for split_select_loop call initialize_opSplittin; if (return_flag) return - + + ! execute split_select_loop call update_opSplittin; if (return_flag) return - call finalize_opSplittin; if (return_flag) return + ! validation and error control + call finalize_opSplittin; if (return_flag) return contains @@ -358,80 +361,17 @@ subroutine initialize_opSplittin call initialize_split_coupling; if (return_flag) return ! prep for first iteration of update_opSplittin end subroutine initialize_opSplittin - subroutine update_opSplittin_OG ! -------------------------------------------------- SJT: Take out - ! *** Update operations for opSplittin *** - split_select_loop: do ! coupling begins - call initialize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop; if (return_flag) return - if (split_select % stateTypeSplitting) then ! stateTypeSplitting begins - call initialize_split_stateThenDomain - if (split_select % stateThenDomain) then ! stateThenDomain begins - call initialize_split_domainSplit; if (return_flag) return - if (split_select % domainSplit) then ! domainSplit begins - call initialize_split_solution - if (split_select % solution) then ! solution begins - call initialize_split_stateSplit; if (return_flag) return - if (split_select % stateSplit) then ! stateSplit begins - - call initialize_split; if (return_flag) return; if (cycle_initialize_split()) cycle split_select_loop - - call update_split; if (return_flag) return - - call finalize_split; if (return_flag) return; if (cycle_finalize_split()) cycle split_select_loop - - end if ! stateSplit ends - call finalize_split_stateSplit - end if ! solution ends - call finalize_split_solution - end if ! domainSplit ends - call finalize_split_domainSplit - end if ! stateThenDomain ends - call finalize_split_stateThenDomain; if (return_flag) return - end if ! stateTypeSplitting ends - call finalize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop; if (return_flag) return - end do split_select_loop ! coupling ends - end subroutine update_opSplittin_OG - subroutine update_opSplittin ! *** Update operations for opSplittin *** - split_select_loop: do ! coupling begins - + ! Note: first loop iteration is the fully coupled method, followed by operator splitting methods if required + split_select_loop: do + ! initialize variables call initialize_split_solve; if (exit_split_select) exit split_select_loop; if (return_flag) return - + ! update the trial solution via fully coupled method or operator splitting call update_split_solve; if (return_flag) return; if (cycle_split_select) cycle split_select_loop - + ! validate the trial solution call finalize_split_solve; if (exit_split_select) exit split_select_loop; if (return_flag) return - - !call initialize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop; if (return_flag) return - - !if (split_select % stateTypeSplitting) then ! stateTypeSplitting begins - ! call initialize_split_stateThenDomain - ! if (split_select % stateThenDomain) then ! stateThenDomain begins - ! call initialize_split_domainSplit; if (return_flag) return - ! if (split_select % domainSplit) then ! domainSplit begins - ! call initialize_split_solution - ! if (split_select % solution) then ! solution begins - ! call initialize_split_stateSplit; if (return_flag) return - ! if (split_select % stateSplit) then ! stateSplit begins - - ! call initialize_split; if (return_flag) return; if (cycle_initialize_split()) cycle split_select_loop - - ! call update_split; if (return_flag) return - - ! call finalize_split; if (return_flag) return; if (cycle_finalize_split()) cycle split_select_loop - - ! end if ! stateSplit ends - ! call finalize_split_stateSplit - ! end if ! solution ends - ! call finalize_split_solution - ! end if ! domainSplit ends - ! call finalize_split_domainSplit - ! end if ! stateThenDomain ends - ! call finalize_split_stateThenDomain; if (return_flag) return - !end if ! stateTypeSplitting ends - - !call finalize_split_stateTypeSplitting; if (exit_split_select) exit split_select_loop; if (return_flag) return - - end do split_select_loop ! coupling ends + end do split_select_loop end subroutine update_opSplittin subroutine finalize_opSplittin @@ -447,31 +387,31 @@ end subroutine initialize_split_solve subroutine update_split_solve ! *** Update operations for solving the selected split *** - if (split_select % stateTypeSplitting) then ! stateTypeSplitting begins + ! Note: split_select data components are used to select the splitting method + if (split_select % stateTypeSplitting) then ! stateTypeSplitting method begins call initialize_split_stateThenDomain - if (split_select % stateThenDomain) then ! stateThenDomain begins - call initialize_split_domainSplit; if (return_flag) return - if (split_select % domainSplit) then ! domainSplit begins + if (split_select % stateThenDomain) then ! stateThenDomain method begins + call initialize_split_domainSplit; if (return_flag) return + if (split_select % domainSplit) then ! domainSplit method begins call initialize_split_solution - if (split_select % solution) then ! solution begins + if (split_select % solution) then ! solution method begins call initialize_split_stateSplit; if (return_flag) return - if (split_select % stateSplit) then ! stateSplit begins - + if (split_select % stateSplit) then ! stateSplit method begins + ! define masks for selected splitting method call initialize_split; if (return_flag) return; if (cycle_initialize_split()) then; cycle_split_select=.true.; return; end if - + ! update trial solution for selected splitting method call update_split; if (return_flag) return - + ! validate trial solution call finalize_split; if (return_flag) return; if (cycle_finalize_split()) then; cycle_split_select=.true.; return; end if - - end if ! stateSplit ends + end if ! stateSplit method ends call finalize_split_stateSplit - end if ! solution ends + end if ! solution method ends call finalize_split_solution - end if ! domainSplit ends + end if ! domainSplit method ends call finalize_split_domainSplit - end if ! stateThenDomain ends - call finalize_split_stateThenDomain; if (return_flag) return - end if ! stateTypeSplitting ends + end if ! stateThenDomain method ends + call finalize_split_stateThenDomain; if (return_flag) return + end if ! stateTypeSplitting method ends end subroutine update_split_solve subroutine finalize_split_solve From 6bfe64c86bc19696170db21ca83a9055189dd9e6 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 26 Oct 2024 06:12:03 -0600 Subject: [PATCH 1446/1472] Updated location of error control for max iterations of split_select_loop in opSplittin.f90. --- build/source/engine/opSplittin.f90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index a0be8b3f8..0b80491cf 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -336,7 +336,6 @@ subroutine opSplittin(& logical(lgt) :: exit_split_select,cycle_split_select ! control for split_select loop logical(lgt) :: exit_coupling,exit_stateThenDomain,exit_solution logical(lgt) :: cycle_coupling,cycle_stateThenDomain,cycle_domainSplit,cycle_solution - integer(i4b),parameter :: maxSplit=500 ! >= max number of splitting methods (controls upper limit of split_select loop) ! ------------------------ classes for subroutine arguments (classes defined in data_types module) ------------------------ ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** type(in_type_indexSplit) :: in_indexSplit; type(out_type_indexSplit) :: out_indexSplit; ! indexSplit arguments @@ -416,7 +415,11 @@ end subroutine update_split_solve subroutine finalize_split_solve ! *** Final operations for solving the selected split *** + integer(i4b),parameter :: maxSplit=500 ! >= max number of splitting methods (controls upper limit of split_select loop) call finalize_split_stateTypeSplitting; if (exit_split_select.or.return_flag) return + if (split_select % iSplit.ge.maxSplit) then ! check for errors - execute fail-safe if needed + err=20; message=trim(message)//'split_select loop exceeded max number of iterations'; return_flag=.true.; return + end if end subroutine finalize_split_solve subroutine initialize_split @@ -617,9 +620,6 @@ subroutine finalize_split_stateTypeSplitting call split_select % advance_ixCoupling end if call split_select % advance_iSplit ! advance iteration counter for split_select_loop - if (split_select % iSplit.ge.maxSplit) then ! check for errors - err=20; message=trim(message)//'split_select loop exceeded max number of iterations'; return_flag=.true.; return - end if end subroutine finalize_split_stateTypeSplitting subroutine finalize_split_coupling From ecb74c561b54a18f0c73786f5dc543b293859428 Mon Sep 17 00:00:00 2001 From: seantrim Date: Sat, 26 Oct 2024 06:51:21 -0600 Subject: [PATCH 1447/1472] Added intent attribute for firstInnerStep dummy variable in opSplittin. --- build/source/engine/opSplittin.f90 | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 0b80491cf..fd2bb1a01 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -249,6 +249,7 @@ subroutine opSplittin(& real(rkind),intent(in) :: dt ! time step (seconds) real(rkind),intent(in) :: whole_step ! length of whole step for surface drainage and average flux logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(in) :: firstInnerStep ! flag to denote if the first time step in maxstep subStep logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) ! input/output: data structures type(var_i),intent(in) :: type_data ! type of vegetation and soil @@ -327,15 +328,13 @@ subroutine opSplittin(& logical(lgt) :: failedMinimumStep ! flag to denote failure of substepping for a given split integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) integer(i4b) :: nCoupling - logical(lgt) :: firstInnerStep ! flag to denote if the first time step in maxstep subStep ! mean steps real(rkind) :: mean_step_state ! mean step over the state (with or without domain splits) real(rkind) :: mean_step_solution ! mean step for a solution (scalar or vector) logical(lgt) :: addFirstFlux ! flag to add the first flux to the mask ! splitting method control variables - logical(lgt) :: exit_split_select,cycle_split_select ! control for split_select loop - logical(lgt) :: exit_coupling,exit_stateThenDomain,exit_solution - logical(lgt) :: cycle_coupling,cycle_stateThenDomain,cycle_domainSplit,cycle_solution + logical(lgt) :: exit_split_select,exit_coupling,exit_stateThenDomain,exit_solution + logical(lgt) :: cycle_split_select,cycle_coupling,cycle_stateThenDomain,cycle_domainSplit,cycle_solution ! ------------------------ classes for subroutine arguments (classes defined in data_types module) ------------------------ ! ** intent(in) arguments ** || ** intent(inout) arguments ** || ** intent(out) arguments ** type(in_type_indexSplit) :: in_indexSplit; type(out_type_indexSplit) :: out_indexSplit; ! indexSplit arguments From 1fae3ef5cc79feb11e756dddd2db45a812d0c6ff Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 29 Oct 2024 01:30:20 -0600 Subject: [PATCH 1448/1472] Removed unused module use statements and parameterms in opSplittin.f90. --- build/source/engine/opSplittin.f90 | 35 +++++------------------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index fd2bb1a01..5d023e9c6 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -28,14 +28,6 @@ module opSplittin_module ! access missing values USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing double precision number -USE globalData,only:quadMissing ! missing quadruple precision number - -! domain types -USE globalData,only:iname_cas ! named variables for the canopy air space -USE globalData,only:iname_veg ! named variables for vegetation -USE globalData,only:iname_snow ! named variables for snow -USE globalData,only:iname_soil ! named variables for soil ! state variable type USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space @@ -50,21 +42,15 @@ module opSplittin_module USE globalData,only:iname_watAquifer ! named variable defining the water storage in the aquifer ! global metadata -USE globalData,only:flux_meta ! metadata on the model fluxes -USE globalData,only:diag_meta ! metadata on the model diagnostic variables -USE globalData,only:prog_meta ! metadata on the model prognostic variables -USE globalData,only:deriv_meta ! metadata on the model derivatives -USE globalData,only:flux2state_orig ! metadata on flux-to-state mapping (original state variables) -USE globalData,only:flux2state_liq ! metadata on flux-to-state mapping (liquid water state variables) +USE globalData,only:flux_meta ! metadata on the model fluxes +USE globalData,only:diag_meta ! metadata on the model diagnostic variables +USE globalData,only:prog_meta ! metadata on the model prognostic variables +USE globalData,only:deriv_meta ! metadata on the model derivatives +USE globalData,only:flux2state_orig ! metadata on flux-to-state mapping (original state variables) +USE globalData,only:flux2state_liq ! metadata on flux-to-state mapping (liquid water state variables) ! provide access to indices that define elements of the data structures -USE var_lookup,only:iLookATTR ! named variables for structure elements -USE var_lookup,only:iLookTYPE ! named variables for structure elements -USE var_lookup,only:iLookPROG ! named variables for structure elements -USE var_lookup,only:iLookDIAG ! named variables for structure elements USE var_lookup,only:iLookFLUX ! named variables for structure elements -USE var_lookup,only:iLookFORCE ! named variables for structure elements -USE var_lookup,only:iLookPARAM ! named variables for structure elements USE var_lookup,only:iLookINDEX ! named variables for structure elements USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure @@ -125,12 +111,6 @@ module opSplittin_module integer(i4b),parameter :: nStateTypes=2 ! number of state types (energy, water) integer(i4b),parameter :: nDomains=4 ! number of domains (vegetation, snow, soil, and aquifer) -! control parameters -real(rkind),parameter :: valueMissing=-9999._rkind ! missing value -real(rkind),parameter :: verySmall=1.e-12_rkind ! a very small number (used to check consistency) -real(rkind),parameter :: veryBig=1.e+20_rkind ! a very big number -real(rkind),parameter :: dx = 1.e-8_rkind ! finite difference increment - ! class definitions type, public :: split_select_type ! class for selecting operator splitting methods @@ -189,7 +169,6 @@ module opSplittin_module contains - ! ********************************************************************************************************** ! public subroutine opSplittin: run the coupled energy-mass model for one timestep ! @@ -235,8 +214,6 @@ subroutine opSplittin(& ! population/extraction of state vectors USE indexState_module,only:indexSplit ! get state indices USE varSubstep_module,only:varSubstep ! complete substeps for a given split - ! identify name of variable type (for error message) - USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages implicit none ! --------------------------------------------------------------------------------------- ! * dummy variables From 206d6005552b2b0ebea3d300801d4b3d85fdbeb1 Mon Sep 17 00:00:00 2001 From: seantrim Date: Tue, 29 Oct 2024 01:43:49 -0600 Subject: [PATCH 1449/1472] Cosmetic updates to summa_driver.f90. --- build/source/driver/summa_driver.f90 | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/build/source/driver/summa_driver.f90 b/build/source/driver/summa_driver.f90 index d62b148b3..b9ea54eaa 100644 --- a/build/source/driver/summa_driver.f90 +++ b/build/source/driver/summa_driver.f90 @@ -19,10 +19,9 @@ ! along with this program. If not, see . program summa_driver - ! driver program for summa simulations - ! ***************************************************************************** - ! * use desired modules - ! ***************************************************************************** + ! **** Driver program for SUMMA simulations **** + + ! * module access * ! data types USE nrtype ! variable types, etc. USE summa_type, only: summa1_type_dec ! master summa data type @@ -41,7 +40,7 @@ program summa_driver USE globalData, only: numtim ! number of model time steps USE globalData, only: print_step_freq -! ! OpenWQ coupling + ! OpenWQ coupling #ifdef OPENWQ_ACTIVE USE summa_openwq,only:openwq_init USE summa_openwq,only:openwq_run_time_start @@ -51,9 +50,7 @@ program summa_driver implicit none - ! ***************************************************************************** - ! * variable definitions - ! ***************************************************************************** + ! * driver variables * ! define the master summa data structure type(summa1_type_dec), allocatable :: summa1_struc(:) ! define parameters for the model simulation @@ -80,7 +77,7 @@ subroutine initialize_summa_driver ! allocate space for the master summa structure allocate(summa1_struc(n), stat=err) - if(err/=0) call stop_program(1, 'problem allocating master summa structure') + if (err/=0) call stop_program(1, 'problem allocating master summa structure') ! declare and allocate summa data structures and initialize model state to known values call summa_initialize(summa1_struc(n), err, message) @@ -114,9 +111,9 @@ subroutine update_summa_driver call openwq_run_time_start(summa1_struc(n)) ! Passing state volumes to openWQ #endif - if (mod(modelTimeStep, print_step_freq) == 0)then + if (mod(modelTimeStep, print_step_freq) == 0) then print *, 'step ---> ', modelTimeStep - endif + end if ! run the summa physics for one time step call summa_runPhysics(modelTimeStep, summa1_struc(n), err, message) @@ -134,7 +131,7 @@ subroutine update_summa_driver call openwq_run_time_end(summa1_struc(n)) #endif - end do ! looping through time + end do ! end looping through time end subroutine update_summa_driver subroutine finalize_summa_driver From de21dbf11d51ad75e98d57d070e7e5c64aea3a03 Mon Sep 17 00:00:00 2001 From: seantrim Date: Wed, 30 Oct 2024 02:32:21 -0600 Subject: [PATCH 1450/1472] Updated stateFilter argument list to utilize split_select object. --- build/source/engine/opSplittin.f90 | 44 +++++++++++++++--------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 5d023e9c6..86fb0337c 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -1446,7 +1446,7 @@ subroutine split_select_compute_stateMask(split_select,indx_data,err,cmessage,me iStateSplit => split_select % iStateSplit ) call in_stateFilter % initialize(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) end associate - call stateFilter(in_stateFilter,indx_data,split_select % stateMask,out_stateFilter) + call stateFilter(in_stateFilter,indx_data,split_select,out_stateFilter) associate(nSubset => split_select % nSubset) call out_stateFilter % finalize(nSubset,err,cmessage) end associate @@ -1457,14 +1457,14 @@ end subroutine split_select_compute_stateMask ! ********************************************************************************************************** ! private subroutine stateFilter: get a mask for the desired state variables ! ********************************************************************************************************** - subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) + subroutine stateFilter(in_stateFilter,indx_data,split_select,out_stateFilter) USE indexState_module,only:indxSubset ! get state indices implicit none ! input type(in_type_stateFilter),intent(in) :: in_stateFilter ! indices type(var_ilength),intent(in) :: indx_data ! indices for a local HRU ! input-output - logical(lgt),intent(inout) :: stateMask(:) ! mask defining desired state variables + type(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector ! output type(out_type_stateFilter),intent(out) :: out_stateFilter ! number of selected state variables for a given split and error control ! local @@ -1500,14 +1500,14 @@ subroutine stateFilter(in_stateFilter,indx_data,stateMask,out_stateFilter) ! get the number of selected state variables associate(nSubset => out_stateFilter % nSubset) ! intent(out): number of selected state variables for a given split - nSubset = count(stateMask) + nSubset = count(split_select % stateMask) end associate contains subroutine fullyCoupled_stateMask ! *** Get fully coupled stateMask *** - stateMask(:) = .true. ! use all state variables + split_select % stateMask(:) = .true. ! use all state variables end subroutine fullyCoupled_stateMask subroutine stateTypeSplit_stateMask @@ -1547,14 +1547,15 @@ end subroutine stateTypeSplit_fullDomain_stateMask subroutine stateTypeSplit_fullDomain_nrgSplit_stateMask ! *** Get state type full domain energy split stateMask *** associate(ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat) ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) - stateMask = (ixStateType==iname_nrgCanair .or. ixStateType==iname_nrgCanopy .or. ixStateType==iname_nrgLayer) + split_select % stateMask = (ixStateType==iname_nrgCanair .or. ixStateType==iname_nrgCanopy .or. ixStateType==iname_nrgLayer) end associate end subroutine stateTypeSplit_fullDomain_nrgSplit_stateMask subroutine stateTypeSplit_fullDomain_massSplit_stateMask ! *** Get state type full domain mass split stateMask *** associate(ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat) ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) - stateMask = (ixStateType==iname_liqCanopy .or. ixStateType==iname_liqLayer .or. ixStateType==iname_lmpLayer .or. ixStateType==iname_watAquifer) + split_select % stateMask = (ixStateType==iname_liqCanopy .or. ixStateType==iname_liqLayer .or. & + & ixStateType==iname_lmpLayer .or. ixStateType==iname_watAquifer) end associate end subroutine stateTypeSplit_fullDomain_massSplit_stateMask @@ -1566,13 +1567,12 @@ subroutine stateTypeSplit_subDomain_stateMask iStateTypeSplit => in_stateFilter % iStateTypeSplit ,& ! intent(in): [i4b] index of the state type split err => out_stateFilter % err ,& ! intent(out): error code message => out_stateFilter % cmessage ) ! intent(out): error message - stateMask(:)=.false. ! initialize state mask + split_select % stateMask(:)=.false. ! initialize state mask select case(iStateTypeSplit) ! define mask for energy case(nrgSplit); call stateTypeSplit_subDomain_nrgSplit_stateMask; if (return_flag) return ! define mask for water case(massSplit); call stateTypeSplit_subDomain_massSplit_stateMask; if (return_flag) return - ! check case default; err=20; message=trim(message)//'unable to identify the state type'; return_flag=.true.; return end select ! (split based on state type) @@ -1602,9 +1602,9 @@ subroutine stateTypeSplit_subDomain_nrgSplit_vegSplit_stateMask ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain - if (ixNrgCanair(1)/=integerMissing) stateMask(ixNrgCanair) = .true. ! energy of the canopy air space - if (ixNrgCanopy(1)/=integerMissing) stateMask(ixNrgCanopy) = .true. ! energy of the vegetation canopy - stateMask(ixNrgLayer(1)) = .true. ! energy of the upper-most layer in the snow+soil domain + if (ixNrgCanair(1)/=integerMissing) split_select % stateMask(ixNrgCanair) = .true. ! energy of the canopy air space + if (ixNrgCanopy(1)/=integerMissing) split_select % stateMask(ixNrgCanopy) = .true. ! energy of the vegetation canopy + split_select % stateMask(ixNrgLayer(1)) = .true. ! energy of the upper-most layer in the snow+soil domain end associate end subroutine stateTypeSplit_subDomain_nrgSplit_vegSplit_stateMask @@ -1613,7 +1613,7 @@ subroutine stateTypeSplit_subDomain_nrgSplit_snowSplit_stateMask nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain ! *** Get state type subdomain energy snow split *** - if (nSnow>1) stateMask(ixNrgLayer(2:nSnow)) = .true. ! NOTE: (2:) because the top layer in the snow+soil domain included in vegSplit + if (nSnow>1) split_select % stateMask(ixNrgLayer(2:nSnow)) = .true. ! NOTE: (2:) because the top layer in the snow+soil domain included in vegSplit end associate end subroutine stateTypeSplit_subDomain_nrgSplit_snowSplit_stateMask @@ -1623,7 +1623,7 @@ subroutine stateTypeSplit_subDomain_nrgSplit_soilSplit_stateMask nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain - stateMask(ixNrgLayer(max(2,nSnow+1):nLayers)) = .true. ! NOTE: max(2,nSnow+1) gives second layer unless more than 2 snow layers + split_select % stateMask(ixNrgLayer(max(2,nSnow+1):nLayers)) = .true. ! NOTE: max(2,nSnow+1) gives second layer unless more than 2 snow layers end associate end subroutine stateTypeSplit_subDomain_nrgSplit_soilSplit_stateMask @@ -1647,7 +1647,7 @@ end subroutine stateTypeSplit_subDomain_massSplit_stateMask subroutine stateTypeSplit_subDomain_massSplit_vegSplit_stateMask ! *** Get mass state vegetation subdomain split stateMask *** associate(ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain - if (ixHydCanopy(1)/=integerMissing) stateMask(ixHydCanopy) = .true. ! hydrology of the vegetation canopy + if (ixHydCanopy(1)/=integerMissing) split_select % stateMask(ixHydCanopy) = .true. ! hydrology of the vegetation canopy end associate end subroutine stateTypeSplit_subDomain_massSplit_vegSplit_stateMask @@ -1656,7 +1656,7 @@ subroutine stateTypeSplit_subDomain_massSplit_snowSplit_stateMask associate(& nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain - stateMask(ixHydLayer(1:nSnow)) = .true. ! snow hydrology + split_select % stateMask(ixHydLayer(1:nSnow)) = .true. ! snow hydrology end associate end subroutine stateTypeSplit_subDomain_massSplit_snowSplit_stateMask @@ -1666,14 +1666,14 @@ subroutine stateTypeSplit_subDomain_massSplit_soilSplit_stateMask nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain - stateMask(ixHydLayer(nSnow+1:nLayers)) = .true. ! soil hydrology + split_select % stateMask(ixHydLayer(nSnow+1:nLayers)) = .true. ! soil hydrology end associate end subroutine stateTypeSplit_subDomain_massSplit_soilSplit_stateMask subroutine stateTypeSplit_subDomain_massSplit_aquiferSplit_stateMask ! *** Get mass state aquifer subdomain split stateMask *** associate(ixWatAquifer => indx_data%var(iLookINDEX%ixWatAquifer)%dat) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for water storage in the aquifer - if (ixWatAquifer(1)/=integerMissing) stateMask(ixWatAquifer) = .true. ! aquifer storage + if (ixWatAquifer(1)/=integerMissing) split_select % stateMask(ixWatAquifer) = .true. ! aquifer storage end associate end subroutine stateTypeSplit_subDomain_massSplit_aquiferSplit_stateMask @@ -1687,13 +1687,13 @@ subroutine identify_scalar_solutions message => out_stateFilter % cmessage ) ! intent(out): error message if (ixSolution==scalar) then ! get the subset of indices - call indxSubset(ixSubset, ixAllState, stateMask, err, cmessage) + call indxSubset(ixSubset, ixAllState, split_select % stateMask, err, cmessage) if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! get the mask - stateMask(:) = .false. - stateMask( ixSubset(iStateSplit) ) = .true. + split_select % stateMask(:) = .false. + split_select % stateMask( ixSubset(iStateSplit) ) = .true. ! check - if (count(stateMask)/=1) then + if (count(split_select % stateMask)/=1) then message=trim(message)//'expect size=1 (scalar)' err=20; return_flag=.true.; return end if From a8e71d9218dd95ced3e206d5c25001110fffd3fa Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 1 Nov 2024 02:32:38 -0600 Subject: [PATCH 1451/1472] Removed nSubset data component from out_stateFilter object -- code using this object was adapted to use the split_select % nSubset data component. --- build/source/dshare/data_types.f90 | 5 +---- build/source/engine/opSplittin.f90 | 8 ++------ 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 40ab5436e..26fab552b 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -591,7 +591,6 @@ MODULE data_types end type in_type_stateFilter type, public :: out_type_stateFilter ! class for intent(out) arguments in stateFilter call - integer(i4b) :: nSubset ! intent(out): number of selected state variables for a given split integer(i4b) :: err ! intent(out): error code character(len=len_msg) :: cmessage ! intent(out): error message contains @@ -1391,12 +1390,10 @@ subroutine initialize_in_stateFilter(in_stateFilter,ixCoupling,ixSolution,ixStat in_stateFilter % iStateSplit = iStateSplit ! intent(in): index of the layer split end subroutine initialize_in_stateFilter - subroutine finalize_out_stateFilter(out_stateFilter,nSubset,err,cmessage) + subroutine finalize_out_stateFilter(out_stateFilter,err,cmessage) class(out_type_stateFilter),intent(in) :: out_stateFilter ! class object for intent(out) stateFilter arguments - integer(i4b),intent(out) :: nSubset ! intent(out): number of selected state variables for a given split integer(i4b),intent(out) :: err ! intent(out): error code character(*),intent(out) :: cmessage ! intent(out): error message - nSubset = out_stateFilter % nSubset ! intent(out): number of selected state variables for a given split err = out_stateFilter % err ! intent(out): error code cmessage = out_stateFilter % cmessage ! intent(out): error message end subroutine finalize_out_stateFilter diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 86fb0337c..4edecd246 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -1447,9 +1447,7 @@ subroutine split_select_compute_stateMask(split_select,indx_data,err,cmessage,me call in_stateFilter % initialize(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) end associate call stateFilter(in_stateFilter,indx_data,split_select,out_stateFilter) - associate(nSubset => split_select % nSubset) - call out_stateFilter % finalize(nSubset,err,cmessage) - end associate + call out_stateFilter % finalize(err,cmessage) if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! error control end subroutine split_select_compute_stateMask @@ -1499,9 +1497,7 @@ subroutine stateFilter(in_stateFilter,indx_data,split_select,out_stateFilter) call identify_scalar_solutions; if (return_flag) return ! identify scalar solutions -- return if error occurs ! get the number of selected state variables - associate(nSubset => out_stateFilter % nSubset) ! intent(out): number of selected state variables for a given split - nSubset = count(split_select % stateMask) - end associate + split_select % nSubset = count(split_select % stateMask) contains From 17ceb25f25d5faf5b195fd4f3d58772cf66a9c2c Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 1 Nov 2024 05:33:12 -0600 Subject: [PATCH 1452/1472] Removed in_type_stateFilter class and in_stateFilter object -- split_select object now used instead. --- build/source/dshare/data_types.f90 | 27 ---------- build/source/engine/opSplittin.f90 | 85 +++++++++++++----------------- 2 files changed, 37 insertions(+), 75 deletions(-) diff --git a/build/source/dshare/data_types.f90 b/build/source/dshare/data_types.f90 index 26fab552b..9fe0727a0 100644 --- a/build/source/dshare/data_types.f90 +++ b/build/source/dshare/data_types.f90 @@ -579,17 +579,6 @@ MODULE data_types ! Define classes used to simplify calls to the subrotuines in opSplittin ! *********************************************************************************************************** ! ** stateFilter - type, public :: in_type_stateFilter ! class for intent(in) arguments in stateFilter call - integer(i4b) :: ixCoupling ! intent(in): index of coupling method (1,2) - integer(i4b) :: ixSolution ! intent(in): index of solution method (1,2) - integer(i4b) :: ixStateThenDomain ! intent(in): switch between full domain and sub domains - integer(i4b) :: iStateTypeSplit ! intent(in): index of the state type split - integer(i4b) :: iDomainSplit ! intent(in): index of the domain split - integer(i4b) :: iStateSplit ! intent(in): index of the layer split - contains - procedure :: initialize => initialize_in_stateFilter - end type in_type_stateFilter - type, public :: out_type_stateFilter ! class for intent(out) arguments in stateFilter call integer(i4b) :: err ! intent(out): error code character(len=len_msg) :: cmessage ! intent(out): error message @@ -1374,22 +1363,6 @@ end subroutine finalize_out_bigAquifer ! **** end bigAquifer **** ! **** stateFilter **** - subroutine initialize_in_stateFilter(in_stateFilter,ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) - class(in_type_stateFilter),intent(out) :: in_stateFilter ! class object for intent(in) stateFilter arguments - integer(i4b),intent(in) :: ixCoupling ! intent(in): index of coupling method (1,2) - integer(i4b),intent(in) :: ixSolution ! intent(in): index of solution method (1,2) - integer(i4b),intent(in) :: ixStateThenDomain ! intent(in): switch between full domain and sub domains - integer(i4b),intent(in) :: iStateTypeSplit ! intent(in): index of the state type split - integer(i4b),intent(in) :: iDomainSplit ! intent(in): index of the domain split - integer(i4b),intent(in) :: iStateSplit ! intent(in): index of the layer split - in_stateFilter % ixCoupling = ixCoupling ! intent(in): index of coupling method (1,2) - in_stateFilter % ixSolution = ixSolution ! intent(in): index of solution method (1,2) - in_stateFilter % ixStateThenDomain = ixStateThenDomain ! intent(in): switch between full domain and sub domains - in_stateFilter % iStateTypeSplit = iStateTypeSplit ! intent(in): index of the state type split - in_stateFilter % iDomainSplit = iDomainSplit ! intent(in): index of the domain split - in_stateFilter % iStateSplit = iStateSplit ! intent(in): index of the layer split - end subroutine initialize_in_stateFilter - subroutine finalize_out_stateFilter(out_stateFilter,err,cmessage) class(out_type_stateFilter),intent(in) :: out_stateFilter ! class object for intent(out) stateFilter arguments integer(i4b),intent(out) :: err ! intent(out): error code diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 4edecd246..9b7e5c668 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -69,7 +69,7 @@ module opSplittin_module var_dlength, & ! data vector with variable length dimension (rkind) zLookup, & ! lookup tables model_options, & ! defines the model decisions - in_type_statefilter,out_type_statefilter, & ! classes for stateFilter objects + out_type_stateFilter, & ! classes for stateFilter objects in_type_indexSplit,out_type_indexSplit, & ! classes for indexSplit objects in_type_varSubstep,io_type_varSubstep,out_type_varSubstep ! classes for varSubstep objects @@ -1432,21 +1432,11 @@ subroutine split_select_compute_stateMask(split_select,indx_data,err,cmessage,me character(*),intent(out) :: message ! error message logical(lgt),intent(out) :: return_flag ! return flag ! local variables - type(in_type_stateFilter) :: in_stateFilter ! indices type(out_type_stateFilter) :: out_stateFilter ! number of selected state variables for a given split and error control err=0 ! initialize error code return_flag=.false. ! initialize flag - associate(& - ixCoupling => split_select % ixCoupling ,& - ixSolution => split_select % ixSolution ,& - ixStateThenDomain => split_select % ixStateThenDomain ,& - iStateTypeSplit => split_select % iStateTypeSplit ,& - iDomainSplit => split_select % iDomainSplit ,& - iStateSplit => split_select % iStateSplit ) - call in_stateFilter % initialize(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit) - end associate - call stateFilter(in_stateFilter,indx_data,split_select,out_stateFilter) + call stateFilter(indx_data,split_select,out_stateFilter) call out_stateFilter % finalize(err,cmessage) if (err/=0) then; message=trim(message)//trim(cmessage); return_flag=.true.; return; end if ! error control end subroutine split_select_compute_stateMask @@ -1455,11 +1445,10 @@ end subroutine split_select_compute_stateMask ! ********************************************************************************************************** ! private subroutine stateFilter: get a mask for the desired state variables ! ********************************************************************************************************** - subroutine stateFilter(in_stateFilter,indx_data,split_select,out_stateFilter) + subroutine stateFilter(indx_data,split_select,out_stateFilter) USE indexState_module,only:indxSubset ! get state indices implicit none ! input - type(in_type_stateFilter),intent(in) :: in_stateFilter ! indices type(var_ilength),intent(in) :: indx_data ! indices for a local HRU ! input-output type(split_select_type),intent(inout) :: split_select ! class object for operator splitting selector @@ -1471,7 +1460,7 @@ subroutine stateFilter(in_stateFilter,indx_data,split_select,out_stateFilter) logical(lgt) :: return_flag ! flag to indicate a return ! ---------------------------------------------------------------------------------------------------------------------------------------------------- ! data structures - associate(ixCoupling => in_stateFilter % ixCoupling,& ! intent(in): [i4b] index of coupling method (1,2) + associate(ixCoupling => split_select % ixCoupling ,& ! intent(in): [i4b] index of coupling method (1,2) err => out_stateFilter % err ,& ! intent(out): error code message => out_stateFilter % cmessage ) ! intent(out): error message @@ -1511,9 +1500,9 @@ subroutine stateTypeSplit_stateMask return_flag=.false. ! initialize flag ! switch between full domain and sub domains associate(& - ixStateThenDomain => in_stateFilter % ixStateThenDomain ,& ! intent(in): [i4b] switch between full domain and sub domains - err => out_stateFilter % err ,& ! intent(out): error code - message => out_stateFilter % cmessage ) ! intent(out): error message + ixStateThenDomain => split_select % ixStateThenDomain,& ! intent(in): [i4b] switch between full domain and sub domains + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message select case(ixStateThenDomain) ! split into energy and mass case(fullDomain); call stateTypeSplit_fullDomain_stateMask; if (return_flag) return @@ -1529,9 +1518,9 @@ end subroutine stateTypeSplit_stateMask subroutine stateTypeSplit_fullDomain_stateMask ! *** Get full domain stateMask *** return_flag=.false. ! initialize flag - associate(iStateTypeSplit => in_stateFilter % iStateTypeSplit ,& ! intent(in): [i4b] index of the state type split - err => out_stateFilter % err ,& ! intent(out): error code - message => out_stateFilter % cmessage ) ! intent(out): error message + associate(iStateTypeSplit => split_select % iStateTypeSplit,& ! intent(in): [i4b] index of the state type split + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message select case(iStateTypeSplit) case(nrgSplit); call stateTypeSplit_fullDomain_nrgSplit_stateMask case(massSplit); call stateTypeSplit_fullDomain_massSplit_stateMask @@ -1560,9 +1549,9 @@ subroutine stateTypeSplit_subDomain_stateMask return_flag=.false. ! initialize flag ! define state mask associate(& - iStateTypeSplit => in_stateFilter % iStateTypeSplit ,& ! intent(in): [i4b] index of the state type split - err => out_stateFilter % err ,& ! intent(out): error code - message => out_stateFilter % cmessage ) ! intent(out): error message + iStateTypeSplit => split_select % iStateTypeSplit,& ! intent(in): [i4b] index of the state type split + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message split_select % stateMask(:)=.false. ! initialize state mask select case(iStateTypeSplit) ! define mask for energy @@ -1579,9 +1568,9 @@ subroutine stateTypeSplit_subDomain_nrgSplit_stateMask ! *** Get subdomain energy split stateMask *** return_flag=.false. ! initialize flag associate(& - iDomainSplit => in_stateFilter % iDomainSplit ,& ! intent(in): [i4b] index of the domain split - err => out_stateFilter % err ,& ! intent(out): error code - message => out_stateFilter % cmessage ) ! intent(out): error message + iDomainSplit => split_select % iDomainSplit,& ! intent(in): [i4b] index of the domain split + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message select case(iDomainSplit) case(vegSplit); call stateTypeSplit_subDomain_nrgSplit_vegSplit_stateMask ! vegetation subdomain case(snowSplit); call stateTypeSplit_subDomain_nrgSplit_snowSplit_stateMask ! snow subdomain @@ -1595,9 +1584,9 @@ end subroutine stateTypeSplit_subDomain_nrgSplit_stateMask subroutine stateTypeSplit_subDomain_nrgSplit_vegSplit_stateMask ! *** Get state type subdomain energy vegetation split *** associate(& - ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain - ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain - ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain + ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain if (ixNrgCanair(1)/=integerMissing) split_select % stateMask(ixNrgCanair) = .true. ! energy of the canopy air space if (ixNrgCanopy(1)/=integerMissing) split_select % stateMask(ixNrgCanopy) = .true. ! energy of the vegetation canopy split_select % stateMask(ixNrgLayer(1)) = .true. ! energy of the upper-most layer in the snow+soil domain @@ -1606,8 +1595,8 @@ end subroutine stateTypeSplit_subDomain_nrgSplit_vegSplit_stateMask subroutine stateTypeSplit_subDomain_nrgSplit_snowSplit_stateMask associate(& - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain ! *** Get state type subdomain energy snow split *** if (nSnow>1) split_select % stateMask(ixNrgLayer(2:nSnow)) = .true. ! NOTE: (2:) because the top layer in the snow+soil domain included in vegSplit end associate @@ -1616,9 +1605,9 @@ end subroutine stateTypeSplit_subDomain_nrgSplit_snowSplit_stateMask subroutine stateTypeSplit_subDomain_nrgSplit_soilSplit_stateMask ! *** Get state type subdomain energy soil split *** associate(& - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers - ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1),& ! intent(in): [i4b] total number of layers + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain split_select % stateMask(ixNrgLayer(max(2,nSnow+1):nLayers)) = .true. ! NOTE: max(2,nSnow+1) gives second layer unless more than 2 snow layers end associate end subroutine stateTypeSplit_subDomain_nrgSplit_soilSplit_stateMask @@ -1627,9 +1616,9 @@ subroutine stateTypeSplit_subDomain_massSplit_stateMask ! *** Get subdomain mass split stateMask *** return_flag=.false. ! initialize flag associate(& - iDomainSplit => in_stateFilter % iDomainSplit ,& ! intent(in): [i4b] index of the domain split - err => out_stateFilter % err ,& ! intent(out): error code - message => out_stateFilter % cmessage ) ! intent(out): error message + iDomainSplit => split_select % iDomainSplit,& ! intent(in): [i4b] index of the domain split + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message select case(iDomainSplit) case(vegSplit); call stateTypeSplit_subDomain_massSplit_vegSplit_stateMask ! vegetation subdomain case(snowSplit); call stateTypeSplit_subDomain_massSplit_snowSplit_stateMask ! snow subdomain @@ -1650,8 +1639,8 @@ end subroutine stateTypeSplit_subDomain_massSplit_vegSplit_stateMask subroutine stateTypeSplit_subDomain_massSplit_snowSplit_stateMask ! *** Get mass state snow subdomain split stateMask *** associate(& - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain split_select % stateMask(ixHydLayer(1:nSnow)) = .true. ! snow hydrology end associate end subroutine stateTypeSplit_subDomain_massSplit_snowSplit_stateMask @@ -1659,9 +1648,9 @@ end subroutine stateTypeSplit_subDomain_massSplit_snowSplit_stateMask subroutine stateTypeSplit_subDomain_massSplit_soilSplit_stateMask ! *** Get mass state soil subdomain split stateMask *** associate(& - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers - ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1),& ! intent(in): [i4b] total number of layers + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ) ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain split_select % stateMask(ixHydLayer(nSnow+1:nLayers)) = .true. ! soil hydrology end associate end subroutine stateTypeSplit_subDomain_massSplit_soilSplit_stateMask @@ -1676,11 +1665,11 @@ end subroutine stateTypeSplit_subDomain_massSplit_aquiferSplit_stateMask subroutine identify_scalar_solutions ! *** Identify scalar solutions *** return_flag=.false. ! initialize flag - associate(ixAllState => indx_data%var(iLookINDEX%ixAllState)%dat ,& ! intent(in): [i4b(:)] list of indices for all model state variables (1,2,3,...nState) - ixSolution => in_stateFilter % ixSolution ,& ! intent(in): [i4b] index of solution method (1,2) - iStateSplit => in_stateFilter % iStateSplit ,& ! intent(in): [i4b] index of the layer split - err => out_stateFilter % err ,& ! intent(out): error code - message => out_stateFilter % cmessage ) ! intent(out): error message + associate(ixAllState => indx_data%var(iLookINDEX%ixAllState)%dat,& ! intent(in): [i4b(:)] list of indices for all model state variables (1,2,3,...nState) + ixSolution => split_select % ixSolution ,& ! intent(in): [i4b] index of solution method (1,2) + iStateSplit => split_select % iStateSplit ,& ! intent(in): [i4b] index of the layer split + err => out_stateFilter % err ,& ! intent(out): error code + message => out_stateFilter % cmessage ) ! intent(out): error message if (ixSolution==scalar) then ! get the subset of indices call indxSubset(ixSubset, ixAllState, split_select % stateMask, err, cmessage) From 36a08ff5952c6cc22566ecd773bb7d0387de3958 Mon Sep 17 00:00:00 2001 From: seantrim Date: Fri, 15 Nov 2024 07:09:58 -0600 Subject: [PATCH 1453/1472] Updated the initialize-update-finalize structure within summaSolve4homegrown. --- build/source/engine/summaSolve4homegrown.f90 | 34 +++++++++++++------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/build/source/engine/summaSolve4homegrown.f90 b/build/source/engine/summaSolve4homegrown.f90 index 062d86367..fa4cf1219 100644 --- a/build/source/engine/summaSolve4homegrown.f90 +++ b/build/source/engine/summaSolve4homegrown.f90 @@ -215,17 +215,12 @@ subroutine summaSolve4homegrown(& ! ***** Compute the Newton Step ***** - call initialize_summaSolve4homegrown; if (return_flag) return ! initial setup -- return if error - - call update_Jacobian; if (return_flag) return ! compute Jacobian for Newton step -- return if error - - call solve_linear_system; if (return_flag) return ! solve the linear system for the Newton step -- return if error - - call refine_Newton_step; if (return_flag) return ! refine Newton step if needed -- return if error - - associate(err => out_SS4HG % err,message => out_SS4HG % message) - if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors - end associate + ! initial setup including computing the Jacobian -- return if error + call initialize_summaSolve4homegrown; if (return_flag) return + ! compute the Newton step -- return if error + call update_summaSolve4homegrown; if (return_flag) return + ! final check for errors + call finalize_summaSolve4homegrown; if (return_flag) return contains @@ -258,8 +253,25 @@ subroutine initialize_summaSolve4homegrown ! initialize the global print flag globalPrintFlagInit=globalPrintFlag + + ! compute the Jacobian + call update_Jacobian; if (return_flag) return ! compute Jacobian for Newton step -- return if error end subroutine initialize_summaSolve4homegrown + subroutine update_summaSolve4homegrown + ! *** Update steps for the summaSolve4homegrown algorithm (computing the Newton step) *** + call solve_linear_system; if (return_flag) return ! solve the linear system for the Newton step -- return if error + + call refine_Newton_step; if (return_flag) return ! refine Newton step if needed -- return if error + end subroutine update_summaSolve4homegrown + + subroutine finalize_summaSolve4homegrown + ! *** Final steps for the summaSolve4homegrown algorithm (computing the Newton step) *** + associate(err => out_SS4HG % err,message => out_SS4HG % message) + if (err/=0) then; message=trim(message)//trim(cmessage); return; end if ! check for errors + end associate + end subroutine finalize_summaSolve4homegrown + subroutine update_Jacobian ! *** Update Jacobian used for Newton step *** From a09eb16a5b9cf3ee77e1c51c9ab1e20654228722 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 5 Dec 2024 11:31:35 +0900 Subject: [PATCH 1454/1472] capping functions in canopy energy so negative temps inside solver do not blow up --- build/source/engine/vegNrgFlux.f90 | 72 +++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index 65ba03cab..e082ba4ae 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -1289,11 +1289,14 @@ subroutine longwaveBal(& ! compute longwave fluxes from canopy and the ground ! NOTE: emc should be set to zero when not computing canopy fluxes if (computeVegFlux) then - LWRadCanopy = emc*sb*canopyTemp**4_i4b ! longwave radiation emitted from the canopy (W m-2) + LWRadCanopy = emc*sb*canopyTemp**4_i4b ! longwave radiation emitted from the canopy (W m-2) else LWRadCanopy = 0._rkind end if - LWRadGround = emg*sb*groundTemp**4_i4b ! longwave radiation emitted at the ground surface (W m-2) + LWRadGround = emg*sb*groundTemp**4_i4b ! longwave radiation emitted at the ground surface (W m-2) + ! cap function to prevent blowing up + if (canopyTemp<0._rkind) LWRadCanopy = 0._rkind + if (groundTemp<0._rkind) LWRadGround = 0._rkind ! compute fluxes originating from the atmosphere LWRadUbound2Canopy = (emc + (1._rkind - emc)*(1._rkind - emg)*emc)*LWRadUbound ! downward atmospheric longwave radiation absorbed by the canopy (W m-2) @@ -1341,6 +1344,10 @@ subroutine longwaveBal(& ! compute initial derivatives dLWRadCanopy_dTCanopy = 4._rkind*emc*sb*canopyTemp**3_i4b dLWRadGround_dTGround = 4._rkind*emg*sb*groundTemp**3_i4b + ! cap function to prevent blowing up + if (canopyTemp<0) dLWRadCanopy_dTCanopy = 0._rkind + if (groundTemp<0) dLWRadGround_dTGround = 0._rkind + ! compute analytical derivatives dLWNetCanopy_dTCanopy = (emc*(1._rkind - emg) - 2._rkind)*dLWRadCanopy_dTCanopy ! derivative in net canopy radiation w.r.t. canopy temperature (W m-2 K-1) dLWNetGround_dTGround = -dLWRadGround_dTGround ! derivative in net ground radiation w.r.t. ground temperature (W m-2 K-1) @@ -2167,6 +2174,15 @@ subroutine turbFluxes(& dGroundCondSH_dCanopyTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy temperature dGroundCondSH_dGroundTemp = -dGroundResistance_dTGround/groundResistance**2_i4b ! derivative in ground conductance w.r.t. ground temperature endif + if(groundConductanceSH <= 0._rkind) then + dGroundCondSH_dCanairTemp = 0._rkind + dGroundCondSH_dCanopyTemp = 0._rkind + dGroundCondSH_dGroundTemp = 0._rkind + end if + if(canopyConductance <= 0._rkind) then + dCanopyCond_dCanairTemp = 0._rkind + dCanopyCond_dCanopyTemp = 0._rkind + end if ! compute derivatives in individual conductances for latent heat w.r.t. canopy temperature (m s-1 K-1) if (computeVegFlux) then @@ -2178,6 +2194,11 @@ subroutine turbFluxes(& dGroundCondLH_dCanopyTemp = 0._rkind ! derivative in ground conductance w.r.t. canopy temperature dGroundCondLH_dGroundTemp = -dGroundResistance_dTGround/(groundResistance+soilResistance)**2_i4b ! derivative in ground conductance w.r.t. ground temperature end if + if(groundConductanceLH <= 0._rkind) then + dGroundCondLH_dCanairTemp = 0._rkind + dGroundCondLH_dCanopyTemp = 0._rkind + dGroundCondLH_dGroundTemp = 0._rkind + end if ! ***** ! * compute sensible and latent heat fluxes, and derivatives... @@ -2192,11 +2213,18 @@ subroutine turbFluxes(& ! compute sensible heat flux from the canopy air space to the atmosphere ! NOTE: canairTemp is a state variable senHeatTotal = -volHeatCapacityAir*canopyConductance*(canairTemp - airtemp) + if (canairTemp<0._rkind) senHeatTotal = volHeatCapacityAir*canopyConductance*airtemp ! cap function to prevent blowing up ! compute fluxes senHeatCanopy = -volHeatCapacityAir*leafConductance*(canopyTemp - canairTemp) ! positive downwards latHeatCanopyEvap = -latHeatSubVapCanopy*latentHeatConstant*evapConductance*(satVP_CanopyTemp - VP_CanopyAir) ! positive downwards latHeatCanopyTrans = -LH_vap*latentHeatConstant*transConductance*(satVP_CanopyTemp - VP_CanopyAir) ! positive downwards + if (canopyTemp<0._rkind) then ! cap function to prevent blowing up + senHeatCanopy = volHeatCapacityAir*leafConductance*canairTemp + if (canairTemp<0._rkind) senHeatCanopy = 0._rkind + else if (canairTemp<0._rkind) then + senHeatCanopy = -volHeatCapacityAir*leafConductance*canopyTemp + end if ! * no vegetation, so fluxes are zero else senHeatCanopy = 0._rkind @@ -2208,10 +2236,17 @@ subroutine turbFluxes(& if (computeVegFlux) then senHeatGround = -volHeatCapacityAir*groundConductanceSH*(groundTemp - canairTemp) ! positive downwards latHeatGround = -latHeatSubVapGround*latentHeatConstant*groundConductanceLH*(satVP_GroundTemp*soilRelHumidity - VP_CanopyAir) ! positive downwards + if (groundTemp<0._rkind) then ! cap function to prevent blowing up + senHeatGround = volHeatCapacityAir*groundConductanceSH*canairTemp + if (canairTemp<0._rkind) senHeatGround = 0._rkind + else if (canairTemp<0._rkind) then + senHeatGround = -volHeatCapacityAir*groundConductanceSH*groundTemp + end if else senHeatGround = -volHeatCapacityAir*groundConductanceSH*(groundTemp - airtemp) ! positive downwards latHeatGround = -latHeatSubVapGround*latentHeatConstant*groundConductanceLH*(satVP_GroundTemp*soilRelHumidity - VPair) ! positive downwards senHeatTotal = senHeatGround + if (groundTemp<0._rkind) senHeatGround = volHeatCapacityAir*groundConductanceSH*airtemp ! cap function to prevent blowing up end if ! compute latent heat flux from the canopy air space to the atmosphere @@ -2245,16 +2280,40 @@ subroutine turbFluxes(& dSenHeatTotal_dTCanair = -volHeatCapacityAir*canopyConductance - volHeatCapacityAir*dCanopyCond_dCanairTemp*(canairTemp - airtemp) dSenHeatTotal_dTCanopy = -volHeatCapacityAir*dCanopyCond_dCanopyTemp*(canairTemp - airtemp) dSenHeatTotal_dTGround = 0._rkind + if (canairTemp<0._rkind) then ! cap function to prevent blowing up + dSenHeatTotal_dTCanair = volHeatCapacityAir*dCanopyCond_dCanairTemp*airtemp + dSenHeatTotal_dTCanopy = volHeatCapacityAir*dCanopyCond_dCanopyTemp*airtemp + end if ! sensible heat from the canopy to the canopy air space dSenHeatCanopy_dTCanair = volHeatCapacityAir*leafConductance dSenHeatCanopy_dTCanopy = -volHeatCapacityAir*leafConductance dSenHeatCanopy_dTGround = 0._rkind + if (canopyTemp<0._rkind) then ! cap function to prevent blowing up + dSenHeatCanopy_dTCanopy = 0._rkind + if (canairTemp<0._rkind) dSenHeatCanopy_dTCanair = 0._rkind + else if (canairTemp<0._rkind) then + dSenHeatCanopy_dTCanair = 0._rkind + end if ! sensible heat from the ground to the canopy air space dSenHeatGround_dTCanair = -volHeatCapacityAir*dGroundCondSH_dCanairTemp*(groundTemp - canairTemp) + volHeatCapacityAir*groundConductanceSH dSenHeatGround_dTCanopy = -volHeatCapacityAir*dGroundCondSH_dCanopyTemp*(groundTemp - canairTemp) dSenHeatGround_dTGround = -volHeatCapacityAir*dGroundCondSH_dGroundTemp*(groundTemp - canairTemp) - volHeatCapacityAir*groundConductanceSH + if (groundTemp<0._rkind) then ! cap function to prevent blowing up + dSenHeatGround_dTCanair = volHeatCapacityAir*dGroundCondSH_dCanairTemp*canairTemp + volHeatCapacityAir*groundConductanceSH + dSenHeatGround_dTCanopy = volHeatCapacityAir*dGroundCondSH_dCanopyTemp*canairTemp + dSenHeatGround_dTGround = volHeatCapacityAir*dGroundCondSH_dGroundTemp*canairTemp + if (canairTemp<0._rkind) then + dSenHeatGround_dTCanair = 0._rkind + dSenHeatGround_dTCanopy = 0._rkind + dSenHeatGround_dTGround = 0._rkind + endif + else if (canairTemp<0._rkind) then + dSenHeatGround_dTCanair = -volHeatCapacityAir*dGroundCondSH_dCanairTemp*groundTemp + dSenHeatGround_dTCanopy = -volHeatCapacityAir*dGroundCondSH_dCanopyTemp*groundTemp + dSenHeatGround_dTGround = -volHeatCapacityAir*dGroundCondSH_dGroundTemp*groundTemp - volHeatCapacityAir*groundConductanceSH + end if ! latent heat associated with canopy evaporation ! initial calculations @@ -2510,12 +2569,21 @@ subroutine bulkRichardson(& ! compute local variables T_grad = airtemp - sfcTemp T_mean = 0.5_rkind*(airtemp + sfcTemp) + if (sfcTemp < 0._rkind) then ! cap function to prevent blowing up + T_grad = airtemp + T_mean = 0.5_rkind*airtemp + endif RiMult = (gravity*mHeight)/(windspd*windspd) ! compute the Richardson number RiBulk = (T_grad/T_mean) * RiMult ! compute the derivative in the Richardson number dRiBulk_dAirTemp = RiMult/T_mean - RiMult*T_grad/(0.5_rkind*((airtemp + sfcTemp)**2_i4b)) dRiBulk_dSfcTemp = -RiMult/T_mean - RiMult*T_grad/(0.5_rkind*((airtemp + sfcTemp)**2_i4b)) + ! cap function to prevent blowing up + if (sfcTemp < 0._rkind) then + dRiBulk_dAirTemp = 0._rkind + dRiBulk_dSfcTemp = 0._rkind + endif end subroutine bulkRichardson From e928e4fc07d0449dea34cfac260b7313ae823486 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 6 Dec 2024 07:45:27 +0900 Subject: [PATCH 1455/1472] possible for dh to be 0 and cause Nan here --- build/source/engine/enthalpyTemp.f90 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index b6da0476d..e56f6ea9f 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -372,10 +372,11 @@ subroutine enthalpy2T_snwWat(Hy,BulkDenWater,fc_param,Tk,err,message) ! compute derivative of dT dh = (f1 - f0)/dT ! compute change in T - dT = -f1/dh + dT = -f1/dh ! could be infinite if dh=0 (i.e., f1 and f0 are within machine precision) ! exit if converged - if(abs(dT) Date: Wed, 8 Jan 2025 10:57:35 +0900 Subject: [PATCH 1456/1472] expanded veg table size for Ignacio's Plumber data --- build/source/driver/summa_setup.f90 | 2 +- build/source/noah-mp/module_sf_noahlsm.F | 2 +- build/source/noah-mp/module_sf_noahmplsm.F | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/source/driver/summa_setup.f90 b/build/source/driver/summa_setup.f90 index 3663ec1f5..f59a8f57a 100644 --- a/build/source/driver/summa_setup.f90 +++ b/build/source/driver/summa_setup.f90 @@ -469,7 +469,7 @@ SUBROUTINE SOIL_VEG_GEN_PARM(FILENAME_VEGTABLE, FILENAME_SOILTABLE, FILENAME_GEN SIZE(ALBEDOMAXTBL) < LUCATS .OR. & SIZE(EMISSMINTBL ) < LUCATS .OR. & SIZE(EMISSMAXTBL ) < LUCATS ) THEN - CALL wrf_error_fatal('Table sizes too small for value of LUCATS in module_sf_noahdrv.F') + CALL wrf_error_fatal('Table sizes too small for value of LUCATS in module_sf_noahdrv.F, expand NLUS and MVT parameters to size of vegetation table and recompile') ENDIF IF(LUTYPE.EQ.MMINLU)THEN diff --git a/build/source/noah-mp/module_sf_noahlsm.F b/build/source/noah-mp/module_sf_noahlsm.F index e3fc5166b..8fed4d756 100644 --- a/build/source/noah-mp/module_sf_noahlsm.F +++ b/build/source/noah-mp/module_sf_noahlsm.F @@ -11,7 +11,7 @@ MODULE module_sf_noahlsm ! VEGETATION PARAMETERS INTEGER :: LUCATS , BARE INTEGER :: NATURAL - integer, PARAMETER :: NLUS=50 + integer, PARAMETER :: NLUS=200 CHARACTER(LEN=256) LUTYPE INTEGER, DIMENSION(1:NLUS) :: NROTBL real, dimension(1:NLUS) :: SNUPTBL, RSTBL, RGLTBL, HSTBL, & diff --git a/build/source/noah-mp/module_sf_noahmplsm.F b/build/source/noah-mp/module_sf_noahmplsm.F index b1ca0eac0..6e924f5a9 100644 --- a/build/source/noah-mp/module_sf_noahmplsm.F +++ b/build/source/noah-mp/module_sf_noahmplsm.F @@ -205,7 +205,7 @@ MODULE NOAHMP_VEG_PARAMETERS IMPLICIT NONE INTEGER, PARAMETER :: MAX_VEG_PARAMS = 33 - INTEGER, PARAMETER :: MVT = 27 + INTEGER, PARAMETER :: MVT = 200 INTEGER, PARAMETER :: MBAND = 2 INTEGER, PRIVATE :: ISURBAN From 53c79aef5faf3184b7d1679d3f34a95258e753d5 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 10 Jan 2025 11:24:27 +0900 Subject: [PATCH 1457/1472] rooting depth > soil depth now makes warning --- build/source/dshare/globalData.f90 | 8 +-- build/source/engine/check_icond.f90 | 9 +++- build/source/engine/soilLiqFlx.f90 | 79 +++++++++++++++-------------- 3 files changed, 51 insertions(+), 45 deletions(-) diff --git a/build/source/dshare/globalData.f90 b/build/source/dshare/globalData.f90 index 6206d2bc7..ab45b4a0d 100644 --- a/build/source/dshare/globalData.f90 +++ b/build/source/dshare/globalData.f90 @@ -273,9 +273,9 @@ MODULE globalData real(rkind),save,public :: tmZoneOffsetFracDay ! time zone offset in fractional days integer(i4b),save,public :: yearLength ! number of days in the current year ! define fixed dimensions - integer(i4b),parameter,public :: nBand=2 ! number of spectral bands - integer(i4b),parameter,public :: nTimeDelay=2000 ! number of time steps in the time delay histogram (default: ~1 season = 24*365/4) + integer(i4b),parameter,public :: nBand=2 ! number of spectral bands + integer(i4b),parameter,public :: nTimeDelay=2000 ! number of time steps in the time delay histogram (default: ~1 season = 24*365/4) ! printing step frequency - integer(i4b),parameter,public :: print_step_freq = 1000 - character(len=1024),public,save :: fname ! temporary filename + integer(i4b),parameter,public :: print_step_freq = 1000 + character(len=1024),public,save :: fname ! temporary filename END MODULE globalData diff --git a/build/source/engine/check_icond.f90 b/build/source/engine/check_icond.f90 index a322b7bd6..1282a8e51 100644 --- a/build/source/engine/check_icond.f90 +++ b/build/source/engine/check_icond.f90 @@ -22,8 +22,8 @@ module check_icond_module USE nrtype ! access missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing double precision number +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing double precision number implicit none private @@ -331,6 +331,11 @@ subroutine check_icond(nGRU, & ! intent(in): number ! end association to variables in the data structures end associate + + ! check rooting depth, a depth that is greater than the total soil depth is meaningless + if (mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%rootingDepth)%dat(1)>sum(progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers))) then + write(message,'(a,1x,i0)') trim(message)//'Warning: rooting depth > total soil depth, so rooting depth will be set to total soil depth' + end if ! if snow layers exist, compute snow depth and SWE if(nSnow > 0)then diff --git a/build/source/engine/soilLiqFlx.f90 b/build/source/engine/soilLiqFlx.f90 index b3369c970..f0bb59e19 100644 --- a/build/source/engine/soilLiqFlx.f90 +++ b/build/source/engine/soilLiqFlx.f90 @@ -889,43 +889,44 @@ subroutine surfaceFlx(& real(rkind) :: fpart1,fpart2 ! different parts of a function real(rkind) :: dpart1(1:nSoil),dpart2(1:nSoil) ! derivatives for different parts of a function real(rkind) :: dfracCap(1:nSoil),dfInfRaw(1:nSoil) ! derivatives for different parts of a function + real(rkind) :: total_soil_depth ! total depth of soil (m) ! head boundary condition - real(rkind) :: cFlux ! capillary flux (m s-1) - real(rkind) :: dNum ! numerical derivative + real(rkind) :: cFlux ! capillary flux (m s-1) + real(rkind) :: dNum ! numerical derivative ! simplified Green-Ampt infiltration - real(rkind) :: rootZoneLiq ! depth of liquid water in the root zone (m) - real(rkind) :: rootZoneIce ! depth of ice in the root zone (m) - real(rkind) :: availCapacity ! available storage capacity in the root zone (m) - real(rkind) :: depthWettingFront ! depth to the wetting front (m) - real(rkind) :: hydCondWettingFront ! hydraulic conductivity at the wetting front (m s-1) + real(rkind) :: rootZoneLiq ! depth of liquid water in the root zone (m) + real(rkind) :: rootZoneIce ! depth of ice in the root zone (m) + real(rkind) :: availCapacity ! available storage capacity in the root zone (m) + real(rkind) :: depthWettingFront ! depth to the wetting front (m) + real(rkind) :: hydCondWettingFront ! hydraulic conductivity at the wetting front (m s-1) ! saturated area associated with variable storage capacity - real(rkind) :: fracCap ! fraction of pore space filled with liquid water and ice (-) - real(rkind) :: fInfRaw ! infiltrating area before imposing solution constraints (-) - real(rkind),parameter :: maxFracCap=0.995_rkind ! maximum fraction capacity -- used to avoid numerical problems associated with an enormous derivative - real(rkind),parameter :: scaleFactor=0.000001_rkind ! scale factor for the smoothing function (-) - real(rkind),parameter :: qSurfScaleMax=1000._rkind ! maximum surface runoff scaling factor (-) + real(rkind) :: fracCap ! fraction of pore space filled with liquid water and ice (-) + real(rkind) :: fInfRaw ! infiltrating area before imposing solution constraints (-) + real(rkind),parameter :: maxFracCap=0.995_rkind ! maximum fraction capacity -- used to avoid numerical problems associated with an enormous derivative + real(rkind),parameter :: scaleFactor=0.000001_rkind ! scale factor for the smoothing function (-) + real(rkind),parameter :: qSurfScaleMax=1000._rkind ! maximum surface runoff scaling factor (-) ! fraction of impermeable area associated with frozen ground - real(rkind) :: alpha ! shape parameter in the Gamma distribution - real(rkind) :: xLimg ! upper limit of the integral + real(rkind) :: alpha ! shape parameter in the Gamma distribution + real(rkind) :: xLimg ! upper limit of the integral ! derivatives - real(rkind) :: dVolFracLiq_dWat(1:nSoil) ! derivative in vol fraction of liquid w.r.t. water state variable in root layers - real(rkind) :: dVolFracIce_dWat(1:nSoil) ! derivative in vol fraction of ice w.r.t. water state variable in root layers - real(rkind) :: dVolFracLiq_dTk(1:nSoil) ! derivative in vol fraction of liquid w.r.t. temperature in root layers - real(rkind) :: dVolFracIce_dTk(1:nSoil) ! derivative in vol fraction of ice w.r.t. temperature in root layers - real(rkind) :: dRootZoneLiq_dWat(1:nSoil) ! derivative in vol fraction of scalar root zone liquid w.r.t. water state variable in root layers - real(rkind) :: dRootZoneIce_dWat(1:nSoil) ! derivative in vol fraction of scalar root zone ice w.r.t. water state variable in root layers - real(rkind) :: dRootZoneLiq_dTk(1:nSoil) ! derivative in vol fraction of scalar root zone liquid w.r.t. temperature in root layers - real(rkind) :: dRootZoneIce_dTk(1:nSoil) ! derivative in vol fraction of scalar root zone ice w.r.t. temperature in root layers - real(rkind) :: dDepthWettingFront_dWat(1:nSoil) ! derivative in scalar depth of wetting front w.r.t. water state variable in root layers - real(rkind) :: dDepthWettingFront_dTk(1:nSoil) ! derivative in scalar depth of wetting front w.r.t. temperature in root layers - real(rkind) :: dxMaxInfilRate_dWat(1:nSoil) ! derivative in scalar max infiltration rate w.r.t. water state variable in root layers - real(rkind) :: dxMaxInfilRate_dTk(1:nSoil) ! derivative in scalar max infiltration rate w.r.t. temperature in root layers - real(rkind) :: dInfilArea_dWat(0:nSoil) ! derivative in scalar infiltration rate w.r.t. water state variable in canopy or snow and root layers - real(rkind) :: dInfilArea_dTk(0:nSoil) ! derivative in scalar infiltration rate w.r.t. temperature in canopy or snow and root layers - real(rkind) :: dFrozenArea_dWat(0:nSoil) ! derivative in scalar frozen area w.r.t. water state variable in canopy or snow and root layers - real(rkind) :: dFrozenArea_dTk(0:nSoil) ! derivative in scalar frozen area w.r.t. temperature in canopy or snow and root layers - real(rkind) :: dInfilRate_dWat(0:nSoil) ! derivative in scalar infiltration rate w.r.t. water state variable in canopy or snow and root layers - real(rkind) :: dInfilRate_dTk(0:nSoil) ! derivative in scalar infiltration rate w.r.t. temperature in canopy or snow and root layers + real(rkind) :: dVolFracLiq_dWat(1:nSoil) ! derivative in vol fraction of liquid w.r.t. water state variable in root layers + real(rkind) :: dVolFracIce_dWat(1:nSoil) ! derivative in vol fraction of ice w.r.t. water state variable in root layers + real(rkind) :: dVolFracLiq_dTk(1:nSoil) ! derivative in vol fraction of liquid w.r.t. temperature in root layers + real(rkind) :: dVolFracIce_dTk(1:nSoil) ! derivative in vol fraction of ice w.r.t. temperature in root layers + real(rkind) :: dRootZoneLiq_dWat(1:nSoil) ! derivative in vol fraction of scalar root zone liquid w.r.t. water state variable in root layers + real(rkind) :: dRootZoneIce_dWat(1:nSoil) ! derivative in vol fraction of scalar root zone ice w.r.t. water state variable in root layers + real(rkind) :: dRootZoneLiq_dTk(1:nSoil) ! derivative in vol fraction of scalar root zone liquid w.r.t. temperature in root layers + real(rkind) :: dRootZoneIce_dTk(1:nSoil) ! derivative in vol fraction of scalar root zone ice w.r.t. temperature in root layers + real(rkind) :: dDepthWettingFront_dWat(1:nSoil) ! derivative in scalar depth of wetting front w.r.t. water state variable in root layers + real(rkind) :: dDepthWettingFront_dTk(1:nSoil) ! derivative in scalar depth of wetting front w.r.t. temperature in root layers + real(rkind) :: dxMaxInfilRate_dWat(1:nSoil) ! derivative in scalar max infiltration rate w.r.t. water state variable in root layers + real(rkind) :: dxMaxInfilRate_dTk(1:nSoil) ! derivative in scalar max infiltration rate w.r.t. temperature in root layers + real(rkind) :: dInfilArea_dWat(0:nSoil) ! derivative in scalar infiltration rate w.r.t. water state variable in canopy or snow and root layers + real(rkind) :: dInfilArea_dTk(0:nSoil) ! derivative in scalar infiltration rate w.r.t. temperature in canopy or snow and root layers + real(rkind) :: dFrozenArea_dWat(0:nSoil) ! derivative in scalar frozen area w.r.t. water state variable in canopy or snow and root layers + real(rkind) :: dFrozenArea_dTk(0:nSoil) ! derivative in scalar frozen area w.r.t. temperature in canopy or snow and root layers + real(rkind) :: dInfilRate_dWat(0:nSoil) ! derivative in scalar infiltration rate w.r.t. water state variable in canopy or snow and root layers + real(rkind) :: dInfilRate_dTk(0:nSoil) ! derivative in scalar infiltration rate w.r.t. temperature in canopy or snow and root layers ! initialize error control err=0; message="surfaceFlx/" @@ -1041,21 +1042,22 @@ subroutine surfaceFlx(& end if ! define the depth to the wetting front (m) and derivatives - depthWettingFront = (rootZoneLiq/availCapacity)*rootingDepth - dDepthWettingFront_dWat(:)=( dRootZoneLiq_dWat(:)*rootingDepth + dRootZoneIce_dWat(:)*depthWettingFront )/availCapacity - dDepthWettingFront_dTk(:) =( dRootZoneLiq_dTk(:)*rootingDepth + dRootZoneIce_dTk(:)*depthWettingFront )/availCapacity + total_soil_depth = sum(mLayerDepth) + depthWettingFront = (rootZoneLiq/availCapacity)*min(rootingDepth, total_soil_depth) + dDepthWettingFront_dWat(:)=( dRootZoneLiq_dWat(:)*min(rootingDepth, total_soil_depth) + dRootZoneIce_dWat(:)*depthWettingFront )/availCapacity + dDepthWettingFront_dTk(:) =( dRootZoneLiq_dTk(:) *min(rootingDepth, total_soil_depth) + dRootZoneIce_dTk(:)*depthWettingFront )/availCapacity ! define the hydraulic conductivity at depth=depthWettingFront (m s-1) - hydCondWettingFront = surfaceSatHydCond * ( (1._rkind - depthWettingFront/sum(mLayerDepth))**(zScale_TOPMODEL - 1._rkind) ) + hydCondWettingFront = surfaceSatHydCond * ( (1._rkind - depthWettingFront/total_soil_depth)**(zScale_TOPMODEL - 1._rkind) ) ! define the maximum infiltration rate (m s-1) and derivatives xMaxInfilRate = hydCondWettingFront*( (wettingFrontSuction + depthWettingFront)/depthWettingFront ) ! maximum infiltration rate (m s-1) fPart1 = hydCondWettingFront fPart2 = (wettingFrontSuction + depthWettingFront)/depthWettingFront - dPart1(:) = surfaceSatHydCond*(zScale_TOPMODEL - 1._rkind) * ( (1._rkind - depthWettingFront/sum(mLayerDepth))**(zScale_TOPMODEL - 2._rkind) ) * (-dDepthWettingFront_dWat(:))/sum(mLayerDepth) + dPart1(:) = surfaceSatHydCond*(zScale_TOPMODEL - 1._rkind) * ( (1._rkind - depthWettingFront/total_soil_depth)**(zScale_TOPMODEL - 2._rkind) ) * (-dDepthWettingFront_dWat(:))/total_soil_depth dPart2(:) = -dDepthWettingFront_dWat(:)*wettingFrontSuction / (depthWettingFront**2_i4b) dxMaxInfilRate_dWat(:) = fPart1*dpart2(:) + fPart2*dPart1(:) - dPart1(:) = surfaceSatHydCond*(zScale_TOPMODEL - 1._rkind) * ( (1._rkind - depthWettingFront/sum(mLayerDepth))**(zScale_TOPMODEL - 2._rkind) ) * (-dDepthWettingFront_dTk(:))/sum(mLayerDepth) + dPart1(:) = surfaceSatHydCond*(zScale_TOPMODEL - 1._rkind) * ( (1._rkind - depthWettingFront/total_soil_depth)**(zScale_TOPMODEL - 2._rkind) ) * (-dDepthWettingFront_dTk(:))/total_soil_depth dPart2(:) = -dDepthWettingFront_dTk(:)*wettingFrontSuction / (depthWettingFront**2_i4b) dxMaxInfilRate_dTk(:) = fPart1*dpart2(:) + fPart2*dPart1(:) @@ -1105,7 +1107,6 @@ subroutine surfaceFlx(& dFrozenArea_dWat(0) = 0._rkind dFrozenArea_dTk(0) = 0._rkind - if (xMaxInfilRate < scalarRainPlusMelt) then ! = dxMaxInfilRate_d, dependent on layers not at surface dInfilRate_dWat(0) = 0._rkind dInfilRate_dTk(0) = 0._rkind From 0fc37a8d2be170971f73b067f12f780fc5b92db0 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 10 Jan 2025 11:34:45 +0900 Subject: [PATCH 1458/1472] warning message --- build/source/engine/check_icond.f90 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/build/source/engine/check_icond.f90 b/build/source/engine/check_icond.f90 index 1282a8e51..84b16f273 100644 --- a/build/source/engine/check_icond.f90 +++ b/build/source/engine/check_icond.f90 @@ -95,6 +95,7 @@ subroutine check_icond(nGRU, & ! intent(in): number real(rkind) :: vGn_m ! van Genutchen "m" parameter (-) real(rkind) :: scalarTheta ! liquid water equivalent of total water [liquid water + ice] (-) real(rkind) :: h1,h2 ! used to check depth and height are consistent + real(rkind) :: d1,d2 ! used to check rooting depth is reasonable real(rkind) :: kappa ! constant in the freezing curve function (m K-1) integer(i4b) :: nSoil ! number of soil layers integer(i4b) :: nSnow ! number of snow layers @@ -331,10 +332,12 @@ subroutine check_icond(nGRU, & ! intent(in): number ! end association to variables in the data structures end associate - + ! check rooting depth, a depth that is greater than the total soil depth is meaningless - if (mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%rootingDepth)%dat(1)>sum(progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers))) then - write(message,'(a,1x,i0)') trim(message)//'Warning: rooting depth > total soil depth, so rooting depth will be set to total soil depth' + d1 = sum(progData%gru(iGRU)%hru(iHRU)%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers)) + d2 = mparData%gru(iGRU)%hru(iHRU)%var(iLookPARAM%rootingDepth)%dat(1) + if (d2>d1) then + write(*,'(a,f5.3,a,f5.3,a)') 'Warning: rooting depth ', d2,' > total soil depth ',d1,', so rooting depth will be set to total soil depth' end if ! if snow layers exist, compute snow depth and SWE From 8b276db9f8f833ecee74eae05fc4c794d112eca4 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Sat, 11 Jan 2025 01:25:23 +0900 Subject: [PATCH 1459/1472] remove unnecessary module --- build/source/engine/getVectorz.f90 | 4 ---- 1 file changed, 4 deletions(-) diff --git a/build/source/engine/getVectorz.f90 b/build/source/engine/getVectorz.f90 index eb12e28d8..ba0c0a6bf 100644 --- a/build/source/engine/getVectorz.f90 +++ b/build/source/engine/getVectorz.f90 @@ -72,10 +72,6 @@ module getVectorz_module USE var_lookup,only:iLookPARAM ! named variables for structure elements USE var_lookup,only:iLookINDEX ! named variables for structure elements -! provide access to routines to update states -USE updatState_module,only:updateSnow ! update snow states -USE updatState_module,only:updateSoil ! update soil states - ! provide access to functions for the constitutive functions and derivatives USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) From a8677adf1bfcd6cfeea9f08fc029f4bb337f1d28 Mon Sep 17 00:00:00 2001 From: Ignacio Aguirre Date: Sun, 19 Jan 2025 12:25:04 -0700 Subject: [PATCH 1460/1472] Change minstep to be read from the parameters file --- build/source/engine/coupled_em.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index da098663f..71f132b49 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -435,7 +435,7 @@ subroutine coupled_em(& ! NOTE - temporary assignment of minstep to foce something reasonable ! changing the maxstep parameter will make the outer and inner loop computations here in coupled_em happen more frequently ! changing the be_steps parameter will make the inner loop computations in opSplittin happen more frequently (e.g. be_steps = 32.0 give BE32) - minstep = 10._rkind ! mpar_data%var(iLookPARAM%minstep)%dat(1) ! minimum time step (s) + minstep = mpar_data%var(iLookPARAM%minstep)%dat(1) ! minimum time step (s) maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) maxstep_op = mpar_data%var(iLookPARAM%maxstep)%dat(1)/NINT(mpar_data%var(iLookPARAM%be_steps)%dat(1)) ! maximum time step (s) to run opSplittin over From bcb5f6b5740ccdba89277570a74eb540be086ade Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 22 Jan 2025 11:16:21 +0900 Subject: [PATCH 1461/1472] put in check for ground and canopy roughnesses --- build/source/engine/vegNrgFlux.f90 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/build/source/engine/vegNrgFlux.f90 b/build/source/engine/vegNrgFlux.f90 index e082ba4ae..5399e86f1 100644 --- a/build/source/engine/vegNrgFlux.f90 +++ b/build/source/engine/vegNrgFlux.f90 @@ -569,7 +569,7 @@ subroutine vegNrgFlux(& end if ! end if the first flux call ! compute the roughness length of the ground (ground below the canopy or non-vegetated surface) - z0Ground = z0soil*(1._rkind - scalarGroundSnowFraction) + z0Snow*scalarGroundSnowFraction ! roughness length (m) + z0Ground = z0Soil*(1._rkind - scalarGroundSnowFraction) + z0Snow*scalarGroundSnowFraction ! roughness length (m) ! compute the total vegetation area index (leaf plus stem) VAI = scalarLAI + scalarSAI ! vegetation area index @@ -1642,6 +1642,12 @@ subroutine aeroResist(& tmp1 = exp(-windReductionFactor* z0Ground/heightCanopyTopAboveSnow) tmp2 = exp(-windReductionFactor*(z0Canopy+zeroPlaneDisplacement)/heightCanopyTopAboveSnow) groundResistanceNeutral = ( heightCanopyTopAboveSnow*exp(windReductionFactor) / (windReductionFactor*eddyDiffusCanopyTop) ) * (tmp1 - tmp2) ! s m-1 + ! check that (tmp1 - tmp2) is positive + if ((z0Ground > z0Canopy + zeroPlaneDisplacement)) then + write(*,'(a,3(f9.5,a))') 'z0Ground > z0Canopy + zeroPlaneDisplacement (', z0Ground,' >',z0Canopy,' +',zeroPlaneDisplacement,' )' + message=trim(message)//'ground roughness is greater than canopy roughness plus zero plane displacement' + err=20; return + end if ! case 2: logarithmic profile from snow depth plus roughness height to bottom of the canopy ! NOTE: heightCanopyBottomAboveSnow>z0Ground+xTolerance else From 829615c325d58408cad6264e3a47858adbfe9fe7 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 22 Jan 2025 13:01:03 +0900 Subject: [PATCH 1462/1472] fixing param defs and so be_steps only applies to BE solvers --- build/source/dshare/get_ixname.f90 | 52 +++++++++++++-------------- build/source/dshare/popMetadat.f90 | 56 +++++++++++++++--------------- build/source/dshare/var_lookup.f90 | 50 +++++++++++++------------- build/source/engine/coupled_em.f90 | 12 +++++-- 4 files changed, 89 insertions(+), 81 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index e82d16208..dfc08ee37 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -289,7 +289,7 @@ function get_ixParam(varName) ! turbulent heat fluxes case('z0Snow' ); get_ixParam = iLookPARAM%z0Snow ! roughness length of snow (m) case('z0Soil' ); get_ixParam = iLookPARAM%z0Soil ! roughness length of bare soil below the canopy (m) - case('z0Canopy' ); get_ixParam = iLookPARAM%z0Canopy ! roughness length of the canopy (m) + case('z0Canopy' ); get_ixParam = iLookPARAM%z0Canopy ! roughness length of the canopy (m), only used if decision veg_traits==vegTypeTable case('zpdFraction' ); get_ixParam = iLookPARAM%zpdFraction ! zero plane displacement / canopy height (-) case('critRichNumber' ); get_ixParam = iLookPARAM%critRichNumber ! critical value for the bulk Richardson number (-) case('Louis79_bparam' ); get_ixParam = iLookPARAM%Louis79_bparam ! parameter in Louis (1979) stability function (-) @@ -380,33 +380,33 @@ function get_ixParam(varName) case('soilIceCV' ); get_ixParam = iLookPARAM%soilIceCV ! CV of depth of soil ice, used to get frozen fraction (-) ! algorithmic control parameters case('minwind' ); get_ixParam = iLookPARAM%minwind ! minimum wind speed (m s-1) - case('minstep' ); get_ixParam = iLookPARAM%minstep ! minimum length of the time step homegrown, not currently used - case('maxstep' ); get_ixParam = iLookPARAM%maxstep ! maximum length of the time step homegrown - case('be_steps' ); get_ixParam = iLookPARAM%be_steps ! minimum number of substeps to take in a maxstep homegrown + case('minstep' ); get_ixParam = iLookPARAM%minstep ! minimum length of the time step homegrown + case('maxstep' ); get_ixParam = iLookPARAM%maxstep ! maximum length of the time step (data window) + case('be_steps' ); get_ixParam = iLookPARAM%be_steps ! number of equal substeps to dividing the data window for BE case('wimplicit' ); get_ixParam = iLookPARAM%wimplicit ! weight assigned to start-of-step fluxes homegrown, not currently used case('maxiter' ); get_ixParam = iLookPARAM%maxiter ! maximum number of iterations homegrown and kinsol - case('relConvTol_liquid' ); get_ixParam = iLookPARAM%relConvTol_liquid ! relative convergence tolerance for vol frac liq water (-) homegrown - case('absConvTol_liquid' ); get_ixParam = iLookPARAM%absConvTol_liquid ! absolute convergence tolerance for vol frac liq water (-) homegrown - case('relConvTol_matric' ); get_ixParam = iLookPARAM%relConvTol_matric ! relative convergence tolerance for matric head (-) homegrown - case('absConvTol_matric' ); get_ixParam = iLookPARAM%absConvTol_matric ! absolute convergence tolerance for matric head (m) homegrown - case('relConvTol_energy' ); get_ixParam = iLookPARAM%relConvTol_energy ! relative convergence tolerance for energy (-) homegrown - case('absConvTol_energy' ); get_ixParam = iLookPARAM%absConvTol_energy ! absolute convergence tolerance for energy (J m-3) homegrown - case('relConvTol_aquifr' ); get_ixParam = iLookPARAM%relConvTol_aquifr ! relative convergence tolerance for aquifer storage (-) homegrown - case('absConvTol_aquifr' ); get_ixParam = iLookPARAM%absConvTol_aquifr ! absolute convergence tolerance for aquifer storage (m) homegrown - case('relTolTempCas' ); get_ixParam = iLookPARAM%relTolTempCas ! relative error tolerance for canopy temperature state variable - case('absTolTempCas' ); get_ixParam = iLookPARAM%absTolTempCas ! absolute error tolerance for canopy temperature state variable - case('relTolTempVeg' ); get_ixParam = iLookPARAM%relTolTempVeg ! relative error tolerance for vegitation temp state var - case('absTolTempVeg' ); get_ixParam = iLookPARAM%absTolTempVeg ! absolute error tolerance for vegitation temp state var - case('relTolWatVeg' ); get_ixParam = iLookPARAM%relTolWatVeg ! relative error tolerance for vegitation hydrology - case('absTolWatVeg' ); get_ixParam = iLookPARAM%absTolWatVeg ! absolute error tolerance for vegitation hydrology - case('relTolTempSoilSnow' ); get_ixParam = iLookPARAM%relTolTempSoilSnow ! relative error tolerance for snow+soil energy - case('absTolTempSoilSnow' ); get_ixParam = iLookPARAM%absTolTempSoilSnow ! absolute error tolerance for snow+soil energy - case('relTolWatSnow' ); get_ixParam = iLookPARAM%relTolWatSnow ! relative error tolerance for snow hydrology - case('absTolWatSnow' ); get_ixParam = iLookPARAM%absTolWatSnow ! absolute error tolerance for snow hydrology - case('relTolMatric' ); get_ixParam = iLookPARAM%relTolMatric ! relative error tolerance for matric head - case('absTolMatric' ); get_ixParam = iLookPARAM%absTolMatric ! absolute error tolerance for matric head - case('relTolAquifr' ); get_ixParam = iLookPARAM%relTolAquifr ! relative error tolerance for aquifer hydrology - case('absTolAquifr' ); get_ixParam = iLookPARAM%absTolAquifr ! absolute error tolerance for aquifer hydrology + case('relConvTol_liquid' ); get_ixParam = iLookPARAM%relConvTol_liquid ! BE relative convergence tolerance for vol frac liq water (-) homegrown + case('absConvTol_liquid' ); get_ixParam = iLookPARAM%absConvTol_liquid ! BE absolute convergence tolerance for vol frac liq water (-) homegrown + case('relConvTol_matric' ); get_ixParam = iLookPARAM%relConvTol_matric ! BE relative convergence tolerance for matric head (-) homegrown + case('absConvTol_matric' ); get_ixParam = iLookPARAM%absConvTol_matric ! BE absolute convergence tolerance for matric head (m) homegrown + case('relConvTol_energy' ); get_ixParam = iLookPARAM%relConvTol_energy ! BE relative convergence tolerance for energy (-) homegrown + case('absConvTol_energy' ); get_ixParam = iLookPARAM%absConvTol_energy ! BE absolute convergence tolerance for energy (J m-3) homegrown + case('relConvTol_aquifr' ); get_ixParam = iLookPARAM%relConvTol_aquifr ! BE relative convergence tolerance for aquifer storage (-) homegrown + case('absConvTol_aquifr' ); get_ixParam = iLookPARAM%absConvTol_aquifr ! BE absolute convergence tolerance for aquifer storage (m) homegrown + case('relTolTempCas' ); get_ixParam = iLookPARAM%relTolTempCas ! IDA relative error tolerance for canopy temperature state variable + case('absTolTempCas' ); get_ixParam = iLookPARAM%absTolTempCas ! IDA absolute error tolerance for canopy temperature state variable + case('relTolTempVeg' ); get_ixParam = iLookPARAM%relTolTempVeg ! IDA relative error tolerance for vegitation temp state var + case('absTolTempVeg' ); get_ixParam = iLookPARAM%absTolTempVeg ! IDA absolute error tolerance for vegitation temp state var + case('relTolWatVeg' ); get_ixParam = iLookPARAM%relTolWatVeg ! IDA relative error tolerance for vegitation hydrology + case('absTolWatVeg' ); get_ixParam = iLookPARAM%absTolWatVeg ! IDA absolute error tolerance for vegitation hydrology + case('relTolTempSoilSnow' ); get_ixParam = iLookPARAM%relTolTempSoilSnow ! IDA relative error tolerance for snow+soil energy + case('absTolTempSoilSnow' ); get_ixParam = iLookPARAM%absTolTempSoilSnow ! IDA absolute error tolerance for snow+soil energy + case('relTolWatSnow' ); get_ixParam = iLookPARAM%relTolWatSnow ! IDA relative error tolerance for snow hydrology + case('absTolWatSnow' ); get_ixParam = iLookPARAM%absTolWatSnow ! IDA absolute error tolerance for snow hydrology + case('relTolMatric' ); get_ixParam = iLookPARAM%relTolMatric ! IDA relative error tolerance for matric head + case('absTolMatric' ); get_ixParam = iLookPARAM%absTolMatric ! IDA absolute error tolerance for matric head + case('relTolAquifr' ); get_ixParam = iLookPARAM%relTolAquifr ! IDA relative error tolerance for aquifer hydrology + case('absTolAquifr' ); get_ixParam = iLookPARAM%absTolAquifr ! IDA absolute error tolerance for aquifer hydrology case('idaMaxOrder' ); get_ixParam = iLookPARAM%idaMaxOrder ! maximum order for IDA case('idaMaxInternalSteps' ); get_ixParam = iLookPARAM%idaMaxInternalSteps ! maximum number of internal steps for IDA before tout case('idaInitStepSize' ); get_ixParam = iLookPARAM%idaInitStepSize ! initial step size for IDA diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 223e1117a..7dc93c252 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -164,7 +164,7 @@ subroutine popMetadat(err,message) ! turbulent heat fluxes mpar_meta(iLookPARAM%z0Snow) = var_info('z0Snow' , 'roughness length of snow' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%z0Soil) = var_info('z0Soil' , 'roughness length of bare soil below the canopy' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%z0Canopy) = var_info('z0Canopy' , 'roughness length of the canopy' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%z0Canopy) = var_info('z0Canopy' , 'roughness length of the canopy, only used if decision veg_traits==vegTypeTable', 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%zpdFraction) = var_info('zpdFraction' , 'zero plane displacement / canopy height' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%critRichNumber) = var_info('critRichNumber' , 'critical value for the bulk Richardson number' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%Louis79_bparam) = var_info('Louis79_bparam' , 'parameter in Louis (1979) stability function' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) @@ -256,33 +256,33 @@ subroutine popMetadat(err,message) mpar_meta(iLookPARAM%soilIceCV) = var_info('soilIceCV' , 'CV of depth of soil ice, used to get frozen fraction' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! algorithmic control parameters mpar_meta(iLookPARAM%minwind) = var_info('minwind' , 'minimum wind speed' , 'm s-1' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%minstep) = var_info('minstep' , 'minimum length of the time step homegrown, not currently used' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%maxstep) = var_info('maxstep' , 'maximum length of the time step homegrown' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%be_steps) = var_info('be_steps' , 'minimum number of substeps to take in a maxstep homegrown' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%wimplicit) = var_info('wimplicit' , 'weight assigned to the start-of-step fluxes ,homegrown, not currently used', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%maxiter) = var_info('maxiter' , 'maximum number of iterations homegrown and kinsol' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_liquid) = var_info('relConvTol_liquid' , 'relative convergence tolerance for vol frac liq water homegrown' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_liquid) = var_info('absConvTol_liquid' , 'absolute convergence tolerance for vol frac liq water homegrown' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_matric) = var_info('relConvTol_matric' , 'relative convergence tolerance for matric head homegrown' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_matric) = var_info('absConvTol_matric' , 'absolute convergence tolerance for matric head homegrown' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_energy) = var_info('relConvTol_energy' , 'relative convergence tolerance for energy homegrown' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_energy) = var_info('absConvTol_energy' , 'absolute convergence tolerance for energy homegrown' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relConvTol_aquifr) = var_info('relConvTol_aquifr' , 'relative convergence tolerance for aquifer storage homegrown' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absConvTol_aquifr) = var_info('absConvTol_aquifr' , 'absolute convergence tolerance for aquifer storage homegrown' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relTolTempCas) = var_info('relTolTempCas' , 'relative error tolerance for canopy temperature state variable' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absTolTempCas) = var_info('absTolTempCas' , 'absolute error tolerance for canopy temperature state variable' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relTolTempVeg) = var_info('relTolTempVeg' , 'relative error tolerance for vegitation temp state var' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absTolTempVeg) = var_info('absTolTempVeg' , 'absolute error tolerance for vegitation temp state var' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relTolWatVeg) = var_info('relTolWatVeg' , 'absolute error tolerance for vegitation hydrology' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absTolWatVeg) = var_info('absTolWatVeg' , 'relative error tolerance for vegitation hydrology' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relTolTempSoilSnow) = var_info('relTolTempSoilSnow' , 'relative error tolerance for snow+soil energy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absTolTempSoilSnow) = var_info('absTolTempSoilSnow' , 'absolute error tolerance for snow+soil energy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relTolWatSnow) = var_info('relTolWatSnow' , 'relative error tolerance for snow hydrology' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absTolWatSnow) = var_info('absTolWatSnow' , 'absolute error tolerance for snow hydrology' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relTolMatric) = var_info('relTolMatric' , 'relative error tolerance for matric head' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absTolMatric) = var_info('absTolMatric' , 'absolute error tolerance for matric head' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%relTolAquifr) = var_info('relTolAquifr' , 'relative error tolerance for aquifer hydrology' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) - mpar_meta(iLookPARAM%absTolAquifr) = var_info('absTolAquifr' , 'absolute error tolerance for aquifer hydrology' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%minstep) = var_info('minstep' , 'minimum length of the time step homegrown' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%maxstep) = var_info('maxstep' , 'maximum length of the time step (data window)' , 's' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%be_steps) = var_info('be_steps' , 'number of equal substeps to dividing the data window for BE' ,'-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%wimplicit) = var_info('wimplicit' , 'weight assigned to the start-of-step fluxes ,homegrown, not currently used', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%maxiter) = var_info('maxiter' , 'maximum number of iterations homegrown and kinsol' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_liquid) = var_info('relConvTol_liquid' , 'BE relative convergence tolerance for vol frac liq water homegrown', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_liquid) = var_info('absConvTol_liquid' , 'BE absolute convergence tolerance for vol frac liq water homegrown', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_matric) = var_info('relConvTol_matric' , 'BE relative convergence tolerance for matric head homegrown' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_matric) = var_info('absConvTol_matric' , 'BE absolute convergence tolerance for matric head homegrown' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_energy) = var_info('relConvTol_energy' , 'BE relative convergence tolerance for energy homegrown' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_energy) = var_info('absConvTol_energy' , 'BE absolute convergence tolerance for energy homegrown' , 'J m-3' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relConvTol_aquifr) = var_info('relConvTol_aquifr' , 'BE relative convergence tolerance for aquifer storage homegrown' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absConvTol_aquifr) = var_info('absConvTol_aquifr' , 'BE absolute convergence tolerance for aquifer storage homegrown' , 'm' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relTolTempCas) = var_info('relTolTempCas' , 'IDA relative error tolerance for canopy temperature state variable', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absTolTempCas) = var_info('absTolTempCas' , 'IDA absolute error tolerance for canopy temperature state variable', '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relTolTempVeg) = var_info('relTolTempVeg' , 'IDA relative error tolerance for vegitation temp state var' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absTolTempVeg) = var_info('absTolTempVeg' , 'IDA absolute error tolerance for vegitation temp state var' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relTolWatVeg) = var_info('relTolWatVeg' , 'IDA relative error tolerance for vegitation hydrology' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absTolWatVeg) = var_info('absTolWatVeg' , 'IDA absolute error tolerance for vegitation hydrology' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relTolTempSoilSnow) = var_info('relTolTempSoilSnow' , 'IDA relative error tolerance for snow+soil energy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absTolTempSoilSnow) = var_info('absTolTempSoilSnow' , 'IDA absolute error tolerance for snow+soil energy' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relTolWatSnow) = var_info('relTolWatSnow' , 'IDA relative error tolerance for snow hydrology' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absTolWatSnow) = var_info('absTolWatSnow' , 'IDA absolute error tolerance for snow hydrology' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relTolMatric) = var_info('relTolMatric' , 'IDA relative error tolerance for matric head' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absTolMatric) = var_info('absTolMatric' , 'IDA absolute error tolerance for matric head' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%relTolAquifr) = var_info('relTolAquifr' , 'IDA relative error tolerance for aquifer hydrology' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) + mpar_meta(iLookPARAM%absTolAquifr) = var_info('absTolAquifr' , 'IDA absolute error tolerance for aquifer hydrology' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%idaMaxOrder) = var_info('idaMaxOrder' , 'maximum order for IDA' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%idaMaxInternalSteps) = var_info('idaMaxInternalSteps' , 'maximum number of internal steps for IDA before tout' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) mpar_meta(iLookPARAM%idaInitStepSize) = var_info('idaInitStepSize' , 'initial step size for IDA' , '-' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index ebe63128a..e97bbca07 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -194,7 +194,7 @@ MODULE var_lookup ! turbulent heat fluxes integer(i4b) :: z0Snow = integerMissing ! roughness length of snow (m) integer(i4b) :: z0Soil = integerMissing ! roughness length of bare soil below the canopy (m) - integer(i4b) :: z0Canopy = integerMissing ! roughness length of the canopy (m) + integer(i4b) :: z0Canopy = integerMissing ! roughness length of the canopy (m), only used if decision veg_traits==vegTypeTable integer(i4b) :: zpdFraction = integerMissing ! zero plane displacement / canopy height (-) integer(i4b) :: critRichNumber = integerMissing ! critical value for the bulk Richardson number (-) integer(i4b) :: Louis79_bparam = integerMissing ! parameter in Louis (1979) stability function (-) @@ -286,32 +286,32 @@ MODULE var_lookup ! algorithmic control parameters integer(i4b) :: minwind = integerMissing ! minimum wind speed (m s-1) integer(i4b) :: minstep = integerMissing ! minimum length of the time step - integer(i4b) :: maxstep = integerMissing ! maximum length of the time step - integer(i4b) :: be_steps = integerMissing ! minimum number of substeps to take in a maxstep homegrown + integer(i4b) :: maxstep = integerMissing ! maximum length of the time step (data window) + integer(i4b) :: be_steps = integerMissing ! number of equal substeps to dividing the data window for BE integer(i4b) :: wimplicit = integerMissing ! weight assigned to the start-of-step fluxes integer(i4b) :: maxiter = integerMissing ! maximum number of iterations homegrown and kinsol - integer(i4b) :: relConvTol_liquid = integerMissing ! relative convergence tolerance for vol frac liq water (-) - integer(i4b) :: absConvTol_liquid = integerMissing ! absolute convergence tolerance for vol frac liq water (-) - integer(i4b) :: relConvTol_matric = integerMissing ! relative convergence tolerance for matric head (-) - integer(i4b) :: absConvTol_matric = integerMissing ! absolute convergence tolerance for matric head (m) - integer(i4b) :: relConvTol_energy = integerMissing ! relative convergence tolerance for energy (-) - integer(i4b) :: absConvTol_energy = integerMissing ! absolute convergence tolerance for energy (J m-3) - integer(i4b) :: relConvTol_aquifr = integerMissing ! relative convergence tolerance for aquifer storage (-) - integer(i4b) :: absConvTol_aquifr = integerMissing ! absolute convergence tolerance for aquifer storage (J m-3) - integer(i4b) :: relTolTempCas = integerMissing ! relative error tolerance for canopy temperature state variable - integer(i4b) :: absTolTempCas = integerMissing ! absolute error tolerance for canopy temperature state variable - integer(i4b) :: relTolTempVeg = integerMissing ! relative error tolerance for vegitation temp state var - integer(i4b) :: absTolTempVeg = integerMissing ! absolute error tolerance for vegitation temp state var - integer(i4b) :: relTolWatVeg = integerMissing ! relative error tolerance for vegitation hydrology - integer(i4b) :: absTolWatVeg = integerMissing ! absolute error tolerance for vegitation hydrology - integer(i4b) :: relTolTempSoilSnow = integerMissing ! relative error tolerance for snow+soil energy - integer(i4b) :: absTolTempSoilSnow = integerMissing ! absolute error tolerance for snow+soil energy - integer(i4b) :: relTolWatSnow = integerMissing ! relative error tolerance for snow hydrology - integer(i4b) :: absTolWatSnow = integerMissing ! absolute error tolerance for snow hydrology - integer(i4b) :: relTolMatric = integerMissing ! relative error tolerance for matric head - integer(i4b) :: absTolMatric = integerMissing ! absolute error tolerance for matric head - integer(i4b) :: relTolAquifr = integerMissing ! relative error tolerance for aquifer hydrology - integer(i4b) :: absTolAquifr = integerMissing ! absolute error tolerance for aquifer hydrology + integer(i4b) :: relConvTol_liquid = integerMissing ! BE relative convergence tolerance for vol frac liq water homegrown (-) + integer(i4b) :: absConvTol_liquid = integerMissing ! BE absolute convergence tolerance for vol frac liq water homegrown (-) + integer(i4b) :: relConvTol_matric = integerMissing ! BE relative convergence tolerance for matric head homegrown (-) + integer(i4b) :: absConvTol_matric = integerMissing ! BE absolute convergence tolerance for matric head homegrown (m) + integer(i4b) :: relConvTol_energy = integerMissing ! BE relative convergence tolerance for energy homegrown (-) + integer(i4b) :: absConvTol_energy = integerMissing ! BE absolute convergence tolerance for energy homegrown (J m-3) + integer(i4b) :: relConvTol_aquifr = integerMissing ! BE relative convergence tolerance for aquifer storage homegrown (-) + integer(i4b) :: absConvTol_aquifr = integerMissing ! BE absolute convergence tolerance for aquifer storage homegrown (J m-3) + integer(i4b) :: relTolTempCas = integerMissing ! IDA relative error tolerance for canopy temperature state variable + integer(i4b) :: absTolTempCas = integerMissing ! IDA absolute error tolerance for canopy temperature state variable + integer(i4b) :: relTolTempVeg = integerMissing ! IDA relative error tolerance for vegitation temp state var + integer(i4b) :: absTolTempVeg = integerMissing ! IDA absolute error tolerance for vegitation temp state var + integer(i4b) :: relTolWatVeg = integerMissing ! IDA relative error tolerance for vegitation hydrology + integer(i4b) :: absTolWatVeg = integerMissing ! IDA absolute error tolerance for vegitation hydrology + integer(i4b) :: relTolTempSoilSnow = integerMissing ! IDA relative error tolerance for snow+soil energy + integer(i4b) :: absTolTempSoilSnow = integerMissing ! IDA absolute error tolerance for snow+soil energy + integer(i4b) :: relTolWatSnow = integerMissing ! IDA relative error tolerance for snow hydrology + integer(i4b) :: absTolWatSnow = integerMissing ! IDA absolute error tolerance for snow hydrology + integer(i4b) :: relTolMatric = integerMissing ! IDA relative error tolerance for matric head + integer(i4b) :: absTolMatric = integerMissing ! IDA absolute error tolerance for matric head + integer(i4b) :: relTolAquifr = integerMissing ! IDA relative error tolerance for aquifer hydrology + integer(i4b) :: absTolAquifr = integerMissing ! IDA absolute error tolerance for aquifer hydrology integer(i4b) :: idaMaxOrder = integerMissing ! maximum order for IDA integer(i4b) :: idaMaxInternalSteps = integerMissing ! maximum number of internal steps for IDA before tout integer(i4b) :: idaInitStepSize = integerMissing ! initial step size for IDA diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 71f132b49..1f749081a 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -281,6 +281,7 @@ subroutine coupled_em(& real(rkind) :: mean_step_dt_sub ! mean solution step for the sub-step real(rkind) :: sumStepSize ! sum solution step for the data step ! outer loop control + integer(i4b) :: be_steps ! number of substeps for a BE solver logical(lgt) :: firstInnerStep ! flag to denote if the first time step in maxstep subStep logical(lgt) :: lastInnerStep ! flag to denote if the last time step in maxstep subStep logical(lgt) :: do_outer ! flag to denote if doing the outer steps surrounding the call to opSplittin @@ -289,7 +290,6 @@ subroutine coupled_em(& logical(lgt) :: computeEnthalpy ! flag to compute enthalpy regardless of the model decision logical(lgt) :: enthalpyStateVec ! flag if enthalpy is a state variable (IDA) logical(lgt) :: use_lookup ! flag to use the lookup table for soil enthalpy, otherwise use analytical solution - ! ---------------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="coupled_em/" @@ -391,6 +391,14 @@ subroutine coupled_em(& case default; err=20; message=trim(message)//'expect num_method to be ida, kinsol, or homegrown (or itertive, which is homegrown)'; return end select + ! set the number of substeps for a BE solver + be_steps = NINT(mpar_data%var(iLookPARAM%be_steps)%dat(1)) ! number of substeps for a BE solver + if (be_steps < 1) then + message=trim(message)//'expect be_steps to be greater than 0' + err=20; return + end if + if (ixNumericalMethod == ida) be_steps = 1_i4b ! IDA does not use substeps + ! set the flag to compute enthalpy, may want to have this true always if want to output enthalpy computeEnthalpy = .false. enthalpyStateVec = .false. @@ -437,7 +445,7 @@ subroutine coupled_em(& ! changing the be_steps parameter will make the inner loop computations in opSplittin happen more frequently (e.g. be_steps = 32.0 give BE32) minstep = mpar_data%var(iLookPARAM%minstep)%dat(1) ! minimum time step (s) maxstep = mpar_data%var(iLookPARAM%maxstep)%dat(1) ! maximum time step (s) - maxstep_op = mpar_data%var(iLookPARAM%maxstep)%dat(1)/NINT(mpar_data%var(iLookPARAM%be_steps)%dat(1)) ! maximum time step (s) to run opSplittin over + maxstep_op = mpar_data%var(iLookPARAM%maxstep)%dat(1)/be_steps ! maximum time step (s) to run opSplittin over ! compute the number of layers with roots nLayersRoots = count(prog_data%var(iLookPROG%iLayerHeight)%dat(nSnow:nLayers-1) < mpar_data%var(iLookPARAM%rootingDepth)%dat(1)-verySmall) From 794fa4bc7335e85eaceeb52b8c0379b874e21195 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Wed, 22 Jan 2025 19:08:09 +0900 Subject: [PATCH 1463/1472] prints --- build/source/engine/coupled_em.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 1f749081a..65277dd85 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -1565,7 +1565,7 @@ subroutine coupled_em(& ! check the soil water balance scalarSoilWatBalError = scalarTotalSoilWat - (balanceSoilWater0 + (balanceSoilInflux + balanceSoilET - balanceSoilBaseflow - balanceSoilDrainage - balanceSoilCompress) ) - if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance_ds)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues + !if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance_ds)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues write(*,*) 'solution method = ', ixSolution write(*,'(a,1x,f20.10)') 'data_step = ', data_step write(*,'(a,1x,f20.10)') 'balanceSoilCompress = ', balanceSoilCompress @@ -1578,6 +1578,7 @@ subroutine coupled_em(& write(*,'(a,1x,f20.10)') 'balanceSoilDrainage = ', balanceSoilDrainage write(*,'(a,1x,f20.10)') 'balanceSoilET = ', balanceSoilET write(*,'(a,1x,f20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError + if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance_ds)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues message=trim(message)//'soil hydrology does not balance' err=20; return end if From 65fa06f047cdc538f4fc9d36c20cb2d49b472019 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Thu, 23 Jan 2025 13:32:05 +0900 Subject: [PATCH 1464/1472] prints --- build/source/engine/computFlux.f90 | 8 ++++---- build/source/engine/coupled_em.f90 | 2 +- build/source/engine/varSubstep.f90 | 15 +++++++++++++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 54d32bf0c..8573aa911 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -613,7 +613,7 @@ end subroutine finalize_snowLiqFlx ! **** soilLiqFlx **** subroutine initialize_soilLiqFlx - call in_soilLiqFlx%initialize(nsnow,nSoil,nlayers,firstSplitOper,scalarSolution,firstFluxCall,& + call in_soilLiqFlx%initialize(nSnow,nSoil,nlayers,firstSplitOper,scalarSolution,firstFluxCall,& mLayerTempTrial,mLayerMatricHeadTrial,mLayerMatricHeadLiqTrial,mLayerVolFracLiqTrial,mLayerVolFracIceTrial,& above_soilLiqFluxDeriv,above_soildLiq_dTk,above_soilFracLiq,flux_data,deriv_data) call io_soilLiqFlx%initialize(nsoil,dHydCond_dMatric,flux_data,diag_data,deriv_data) @@ -627,14 +627,14 @@ subroutine finalize_soilLiqFlx associate(& mLayerLiqFluxSoil => flux_data%var(iLookFLUX%mLayerLiqFluxSoil)%dat, & ! intent(out): [dp] net liquid water flux for each soil layer (s-1) - iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat, & ! intent(out): [dp(0:)] vertical liquid water flux at soil layer interfaces (-) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat, & ! intent(out): [dp(0:)] vertical liquid water flux at soil layer interfaces (m s-1) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) scalarMaxInfilRate => flux_data%var(iLookFLUX%scalarMaxInfilRate)%dat(1), & ! intent(out): [dp] maximum infiltration rate (m s-1) scalarRainPlusMelt => flux_data%var(iLookFLUX%scalarRainPlusMelt)%dat(1), & ! intent(out): [dp] rain plus melt (m s-1) scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1), & ! intent(out): [dp] soil control on infiltration, zero or one scalarInfilArea => diag_data%var(iLookDIAG%scalarInfilArea )%dat(1), & ! intent(out): [dp] fraction of unfrozen area where water can infiltrate (-) scalarFrozenArea => diag_data%var(iLookDIAG%scalarFrozenArea )%dat(1), & ! intent(out): [dp] fraction of area that is considered impermeable due to soil ice (-) - scalarSoilDrainage => flux_data%var(iLookFLUX%scalarSoilDrainage)%dat(1) ) ! intent(out): [dp] drainage from the soil profile (m s-1) + scalarSoilDrainage => flux_data%var(iLookFLUX%scalarSoilDrainage)%dat(1) ) ! intent(out): [dp] drainage from the soil profile (m s-1) ! calculate net liquid water fluxes for each soil layer (s-1) do iLayer=1,nSoil diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 65277dd85..a4b5e0ef0 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -271,7 +271,7 @@ subroutine coupled_em(& real(rkind),allocatable :: innerBalanceLayerMass(:) ! inner step balances for domain with multiple layers real(rkind),allocatable :: innerBalanceLayerNrg(:) ! inner step balances for domain with multiple layers ! test balance checks - logical(lgt),parameter :: printBalance=.false. ! flag to print the balance checks + logical(lgt),parameter :: printBalance=.true. ! flag to print the balance checks real(rkind),allocatable :: liqSnowInit(:) ! volumetric liquid water conetnt of snow at the start of the time step real(rkind),allocatable :: liqSoilInit(:) ! soil moisture at the start of the time step ! timing information diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 356d018f7..9294554c6 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -527,7 +527,7 @@ subroutine varSubstep(& endif ! print progress - if(globalPrintFlag)& + !if(globalPrintFlag)& write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt ! increment fluxes @@ -539,7 +539,12 @@ subroutine varSubstep(& if(count(ixLayerActive/=integerMissing)==nLayers)then flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght fluxCount%var(iVar)%dat(:) = fluxCount%var(iVar)%dat(:) + 1 - + if (iVar==iLookFLUX%scalarSoilDrainage) then + print*, 'no Split scalarSoilDrainage = ', flux_temp%var(iVar)%dat(1)*iden_water*1800._rkind, flux_mean%var(iVar)%dat(1)*iden_water*1800._rkind + endif + if (iVar== iLookFLUX%iLayerLiqFluxSoil) then + print*, 'no Split iLayerLiqFluxSoil = ', flux_temp%var(iVar)%dat(nSoil)*iden_water*1800._rkind, flux_mean%var(iVar)%dat(nSoil)*iden_water*1800._rkind + endif ! ** domain splitting else ixMin=lbound(flux_data%var(iVar)%dat) @@ -556,6 +561,12 @@ subroutine varSubstep(& fluxCount%var(iVar)%dat(ixLayer) = fluxCount%var(iVar)%dat(ixLayer) + 1 endif end do + if (iVar==iLookFLUX%scalarSoilDrainage) then + print*, 'scalarSoilDrainage = ', fluxMask%var(iVar)%dat(1),flux_temp%var(iVar)%dat(1)*iden_water*1800._rkind, flux_mean%var(iVar)%dat(1)*iden_water*1800._rkind + endif + if (iVar== iLookFLUX%iLayerLiqFluxSoil) then + print*, 'iLayerLiqFluxSoil = ', fluxMask%var(iVar)%dat(nSoil),flux_temp%var(iVar)%dat(nSoil)*iden_water*1800._rkind, flux_mean%var(iVar)%dat(nSoil)*iden_water*1800._rkind,ixMin(1),ixMax(1),nSoil + endif endif ! (domain splitting) endif ! (if the flux is desired) From 5d56e408d17d828df5c160ded934858cb06cd9a5 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Fri, 24 Jan 2025 21:50:23 +0900 Subject: [PATCH 1465/1472] bug fix for Cm term --- build/source/dshare/get_ixname.f90 | 1 + build/source/dshare/popMetadat.f90 | 1 + build/source/dshare/var_lookup.f90 | 3 +- build/source/engine/computHeatCap.f90 | 85 +++++++++++++------- build/source/engine/computJacob.f90 | 9 ++- build/source/engine/computJacobWithPrime.f90 | 15 ++-- build/source/engine/computResid.f90 | 12 +-- build/source/engine/computResidWithPrime.f90 | 14 ++-- build/source/engine/enthalpyTemp.f90 | 12 +-- build/source/engine/eval8summa.f90 | 51 +++++++----- build/source/engine/eval8summaWithPrime.f90 | 49 ++++++----- build/source/engine/varSubstep.f90 | 4 +- 12 files changed, 158 insertions(+), 98 deletions(-) diff --git a/build/source/dshare/get_ixname.f90 b/build/source/dshare/get_ixname.f90 index dfc08ee37..57498fea7 100644 --- a/build/source/dshare/get_ixname.f90 +++ b/build/source/dshare/get_ixname.f90 @@ -804,6 +804,7 @@ function get_ixDeriv(varName) case('dNrgFlux_dTempAbove' ); get_ixDeriv = iLookDERIV%dNrgFlux_dTempAbove ! derivatives in the flux w.r.t. temperature in the layer above (J m-2 s-1 K-1) case('dNrgFlux_dTempBelow' ); get_ixDeriv = iLookDERIV%dNrgFlux_dTempBelow ! derivatives in the flux w.r.t. temperature in the layer below (J m-2 s-1 K-1) ! energy derivatives that might be treated as constant if Cm not updated + case('dCm_dPsi0' ); get_ixDeriv = iLookDERIV%dCm_dPsi0 ! derivative in Cm w.r.t. matric potential (J kg-1) case('dCm_dTk' ); get_ixDeriv = iLookDERIV%dCm_dTk ! derivative in Cm w.r.t. temperature (J kg K-2) case('dCm_dTkCanopy' ); get_ixDeriv = iLookDERIV%dCm_dTkCanopy ! derivative in Cm w.r.t. canopy temperature (J kg K-2) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. water state in layers above and below diff --git a/build/source/dshare/popMetadat.f90 b/build/source/dshare/popMetadat.f90 index 7dc93c252..a2af5d6b6 100644 --- a/build/source/dshare/popMetadat.f90 +++ b/build/source/dshare/popMetadat.f90 @@ -626,6 +626,7 @@ subroutine popMetadat(err,message) deriv_meta(iLookDERIV%dThermalC_dWatAbove) = var_info('dThermalC_dWatAbove' , 'derivative in the thermal conductivity w.r.t. water in the layer above', 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dThermalC_dWatBelow) = var_info('dThermalC_dWatBelow' , 'derivative in the thermal conductivity w.r.t. water in the layer above', 'unknown' , get_ixVarType('ifcToto'), iMissVec, iMissVec, .false.) ! energy derivatives that might be treated as constant if Cm not updated + deriv_meta(iLookDERIV%dCm_dPsi0) = var_info('dCm_dPsi0' , 'derivative in Cm w.r.t. matric potential' , 'J kg K-1' , get_ixVarType('midSoil'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dCm_dTk) = var_info('dCm_dTk' , 'derivative in Cm w.r.t. temperature' , 'J kg K-2' , get_ixVarType('midToto'), iMissVec, iMissVec, .false.) deriv_meta(iLookDERIV%dCm_dTkCanopy) = var_info('dCm_dTkCanopy' , 'derivative in Cm w.r.t. canopy temperature' , 'J kg K-2' , get_ixVarType('scalarv'), iMissVec, iMissVec, .false.) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below diff --git a/build/source/dshare/var_lookup.f90 b/build/source/dshare/var_lookup.f90 index e97bbca07..bacf2513b 100644 --- a/build/source/dshare/var_lookup.f90 +++ b/build/source/dshare/var_lookup.f90 @@ -659,6 +659,7 @@ MODULE var_lookup integer(i4b) :: dThermalC_dWatAbove = integerMissing ! derivative in the thermal conductivity w.r.t. water state in the layer above integer(i4b) :: dThermalC_dWatBelow = integerMissing ! derivative in the thermal conductivity w.r.t. water state in the layer above ! energy derivatives that might be treated as constant if Cm not updated + integer(i4b) :: dCm_dPsi0 = integerMissing ! derivative in heat capacity w.r.t. matric potential (J kg-1) integer(i4b) :: dCm_dTk = integerMissing ! derivative in heat capacity w.r.t. temperature (J kg-1 K-2) integer(i4b) :: dCm_dTkCanopy = integerMissing ! derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below @@ -958,7 +959,7 @@ MODULE var_lookup 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,& 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,& 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,& - 81) + 81, 82) ! named variables: model indices type(iLook_index), public,parameter :: iLookINDEX =ilook_index ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,& 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,& diff --git a/build/source/engine/computHeatCap.f90 b/build/source/engine/computHeatCap.f90 index 8d74b5146..dc66397c4 100644 --- a/build/source/engine/computHeatCap.f90 +++ b/build/source/engine/computHeatCap.f90 @@ -33,16 +33,12 @@ module computHeatCap_module USE var_lookup,only:iLookPARAM,iLookDIAG,iLookINDEX ! named variables for structure elements ! physical constants -USE multiconst,only:& - Tfreeze, & ! freezing point of water (K) - iden_air, & ! intrinsic density of air (kg m-3) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water, & ! intrinsic density of water (kg m-3) - ! specific heat - Cp_air, & ! specific heat of air (J kg-1 K-1) - Cp_ice, & ! specific heat of ice (J kg-1 K-1) - Cp_soil, & ! specific heat of soil (J kg-1 K-1) - Cp_water ! specific heat of liquid water (J kg-1 K-1) +USE multiconst,only: gravity, & ! gravitational acceleration (m s-1) + Tfreeze, & ! freezing point of water (K) + Cp_soil,Cp_water,Cp_ice,Cp_air,& ! specific heat of soil, water and ice (J kg-1 K-1) + iden_water,iden_ice,iden_air,& ! intrinsic density of water and ice (kg m-3) + LH_fus ! latent heat of fusion (J kg-1) + ! named variables to describe the state variable type USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy @@ -343,7 +339,9 @@ end subroutine computHeatCapAnalytic ! NOTE: computing on whole vector, could just compute on state subset ! ********************************************************************************************************** subroutine computCm(& + be_solver, & ! intent(in): flag for BE solver, need to include latent heat part in Jacobian Cm if true ! input: state variables + canopyDepth, & ! intent(in): depth of the vegetation canopy (m) scalarCanopyTemp, & ! intent(in): value of canopy temperature (K) mLayerTemp, & ! intent(in): vector of temperature (K) mLayerMatricHead, & ! intent(in): vector of total water matric potential (-) @@ -352,16 +350,23 @@ subroutine computCm(& indx_data, & ! intent(in): model layer indices ! output scalarCanopyCm, & ! intent(inout): Cm for vegetation (J kg K-1) + scalarCanopyCm_noLH, & ! intent(inout): Cm without latent heat part for vegetation (J kg K-1) mLayerCm, & ! intent(inout): Cm for soil and snow (J kg K-1) + mLayerCm_noLH, & ! intent(inout): Cm without latent heat part for soil and snow (J kg K-1) + dCm_dPsi0, & ! intent(inout): derivative in Cm w.r.t. matric potential (J kg) dCm_dTk, & ! intent(inout): derivative in Cm w.r.t. temperature (J kg K-2) dCm_dTkCanopy, & ! intent(inout): derivative in Cm w.r.t. temperature (J kg K-2) ! output: error control err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------------- ! provide access to external subroutines - USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) + USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists (soil) ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: state variables + logical(i4b),intent(in) :: be_solver ! flag for BE solver, need to include latent heat part in Jacobian Cm if true + real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) real(rkind),intent(in) :: scalarCanopyTemp ! value of canopy temperature (K) real(rkind),intent(in) :: mLayerTemp(:) ! vector of temperature (K) real(rkind),intent(in) :: mLayerMatricHead(:) ! vector of total water matric potential (-) @@ -369,8 +374,11 @@ subroutine computCm(& type(var_dlength),intent(in) :: mpar_data ! model parameters type(var_ilength),intent(in) :: indx_data ! model layer indices ! output: Cm and derivatives - real(qp),intent(inout) :: scalarCanopyCm ! Cm for vegetation (J kg K-1) - real(qp),intent(inout) :: mLayerCm(:) ! Cm for soil and snow (J kg K-1) + real(rkind),intent(inout) :: scalarCanopyCm ! Cm for vegetation (J kg K-1) use for LHS + real(rkind),intent(inout) :: scalarCanopyCm_noLH ! Cm without latent heat part for vegetation (J kg K-1) use for RHS + real(rkind),intent(inout) :: mLayerCm(:) ! Cm for soil and snow (J kg K-1) + real(rkind),intent(inout) :: mLayerCm_noLH(:) ! Cm without latent heat part for soil and snow (J kg K-1) + real(rkind),intent(inout) :: dCm_dPsi0(:) ! derivative in Cm w.r.t. matric potential (J kg) real(rkind),intent(inout) :: dCm_dTk(:) ! derivative in Cm w.r.t. temperature (J kg K-2) real(rkind),intent(inout) :: dCm_dTkCanopy ! derivative in Cm w.r.t. temperature (J kg K-2) ! output: error control @@ -384,9 +392,12 @@ subroutine computCm(& integer(i4b) :: ixDomainType ! name of a given model domain integer(i4b) :: ixControlIndex ! index within a given model domain real(rkind) :: diffT ! temperature difference from Tfreeze + real(rkind) :: diff0 ! temperature difference Tcrit from Tfreeze real(rkind) :: integral ! integral of snow freezing curve + real(rkind) :: fLiq ! fraction of liquid water + real(rkind) :: dfLiq_dT ! derivative of fraction of liquid water with temperature real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - real(rkind) :: d_integral_dTk ! derivative of integral with temperature + real(rkind) :: dTcrit_dPsi0 ! derivative of critical temperature with matric potential ! -------------------------------------------------------------------------------------------------------------------------------- ! associate variables in data structure associate(& @@ -437,39 +448,57 @@ subroutine computCm(& ! Note that scalarCanopyCm/iden_water is computed diffT = scalarCanopyTemp - Tfreeze if(diffT>=0._rkind)then - scalarCanopyCm = Cp_water * diffT + scalarCanopyCm_noLH = Cp_water * diffT + scalarCanopyCm = scalarCanopyCm_noLH ! derivatives dCm_dTkCanopy = Cp_water else integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - scalarCanopyCm = Cp_water * integral + Cp_ice * (diffT - integral) + fLiq = fracLiquid(scalarCanopyTemp,snowfrz_scale) + scalarCanopyCm_noLH = Cp_water * integral + Cp_ice * (diffT - integral) + scalarCanopyCm = scalarCanopyCm_noLH + if (be_solver) scalarCanopyCm = scalarCanopyCm_noLH - LH_fus * (1._rkind - fLiq) / canopyDepth ! LH_fus is term is not the Jacobian for the RHS ! derivatives - d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) - dCm_dTkCanopy = Cp_water * d_integral_dTk + Cp_ice * (1._rkind - d_integral_dTk) + dfLiq_dT = dFracLiq_dTk(scalarCanopyTemp,snowfrz_scale) + dCm_dTkCanopy = Cp_water * fLiq + Cp_ice * (1._rkind - fLiq) + if (be_solver) dCm_dTkCanopy = dCm_dTkCanopy + LH_fus * dfLiq_dT / canopyDepth ! LH_fus is term is not in the Jacobian for the LHS end if case(iname_snow) diffT = mLayerTemp(iLayer) - Tfreeze + fLiq = fracLiquid(mLayerTemp(iLayer),snowfrz_scale) integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - mLayerCm(iLayer) = (iden_water * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( diffT - integral ) & - + (iden_water * Cp_water - iden_air * Cp_air) * integral + mLayerCm_noLH(iLayer) = (iden_water * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( diffT - integral ) & + + (iden_water * Cp_water - iden_air * Cp_air) * integral + mLayerCm(iLayer) = mLayerCm_noLH(iLayer) + if (be_solver) mLayerCm(iLayer) = mLayerCm_noLH(iLayer) - LH_fus * iden_water * (1._rkind - fLiq) ! LH_fus is term is already in the Jacobian for the LHS ! derivatives - d_integral_dTk = 1._rkind / (1._rkind + (snowfrz_scale * diffT)**2_i4b) - dCm_dTk(iLayer) = (iden_water * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( 1._rkind - d_integral_dTk ) & - + (iden_water * Cp_water - iden_air * Cp_air) * d_integral_dTk + dfLiq_dT = dFracLiq_dTk(mLayerTemp(iLayer),snowfrz_scale) + dCm_dTk(iLayer) = (iden_water * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( 1._rkind -fLiq ) & + + (iden_water * Cp_water - iden_air * Cp_air) * fLiq + if (be_solver) dCm_dTk(iLayer) = dCm_dTk(iLayer) + LH_fus * iden_water * dfLiq_dT ! LH_fus is term is not in the Jacobian for the LHS case(iname_soil) diffT = mLayerTemp(iLayer) - Tfreeze Tcrit = crit_soilT( mLayerMatricHead(ixControlIndex) ) + diff0 = Tcrit - Tfreeze if( mLayerTemp(iLayer)>=Tcrit)then - mLayerCm(iLayer) = (iden_water * Cp_water - iden_air * Cp_air) * diffT + mLayerCm_noLH(iLayer) = (-iden_air * Cp_air + iden_water * Cp_water) * diffT + mLayerCm(iLayer) = mLayerCm_noLH(iLayer) ! derivatives - dCm_dTk(iLayer) = (iden_water * Cp_water - iden_air * Cp_air) + dCm_dTk(iLayer) = -iden_air * Cp_air + iden_water * Cp_water + dCm_dPsi0(ixControlIndex) = 0._rkind else - mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air) * diffT - ! derivatives - dCm_dTk(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air) + mLayerCm_noLH(iLayer) = -iden_air * Cp_air * diffT + iden_ice * Cp_ice * (mLayerTemp(iLayer)-Tcrit) & + + iden_water * Cp_water * diff0 + mLayerCm(iLayer) = mLayerCm_noLH(iLayer) + if (be_solver) mLayerCm(iLayer) = mLayerCm_noLH(iLayer) - LH_fus * iden_water ! LH_fus is term is already in the Jacobian for the LHS + ! derivatives, note that does not matter if be_solver is true or false + dTcrit_dPsi0 = merge(gravity*Tfreeze/LH_fus,0._rkind,mLayerMatricHead(ixControlIndex)<=0._rkind) + dCm_dTk(iLayer) = -iden_air * Cp_air + iden_ice * Cp_ice + dCm_dPsi0(ixControlIndex) = (-iden_ice * Cp_ice + iden_water * Cp_water) * dTcrit_dPsi0 endif + end select end if ! if an energy layer diff --git a/build/source/engine/computJacob.f90 b/build/source/engine/computJacob.f90 index c1df88b30..a86abe2dd 100644 --- a/build/source/engine/computJacob.f90 +++ b/build/source/engine/computJacob.f90 @@ -235,6 +235,7 @@ subroutine computJacob(& dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. temperature ! derivative in Cm w.r.t. relevant state variables + dCm_dPsi0 => deriv_data%var(iLookDERIV%dCm_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in heat capacity w.r.t. matric potential (J kg-1) dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk )%dat ,& ! intent(in): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) ! derivatives in time @@ -614,8 +615,9 @@ subroutine computJacob(& endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerdTemp_dt(jLayer) + mLayerCm(iLayer) * dVolTot_dPsi0(iLayer) & - + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerdTemp_dt(jLayer) & + + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) + dCm_dPsi0(iLayer) * mLayerdWat_dt(jLayer) & + + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(ixOffDiag(nrgState,watState),watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water + aJac(ixOffDiag(nrgState,watState),watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content endif @@ -944,7 +946,8 @@ subroutine computJacob(& endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerdTemp_dt(jLayer) + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) & + aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerdTemp_dt(jLayer) & + + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) + dCm_dPsi0(iLayer) * mLayerdWat_dt(jLayer) & + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(nrgState,watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content diff --git a/build/source/engine/computJacobWithPrime.f90 b/build/source/engine/computJacobWithPrime.f90 index a82164bcf..498d92b0b 100644 --- a/build/source/engine/computJacobWithPrime.f90 +++ b/build/source/engine/computJacobWithPrime.f90 @@ -281,6 +281,7 @@ subroutine computJacobWithPrime(& dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(in): [dp(:)] derivative in bulk heat capacity w.r.t. temperature dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in bulk heat capacity w.r.t. temperature ! derivative in Cm w.r.t. relevant state variables + dCm_dPsi0 => deriv_data%var(iLookDERIV%dCm_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in heat capacity w.r.t. matric potential (J kg-1) dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk )%dat ,& ! intent(in): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy )%dat(1) ,& ! intent(in): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) ! derivatives of temperature if enthalpy is the state variable @@ -682,11 +683,12 @@ subroutine computJacobWithPrime(& endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) * cj & - + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + mLayerCm(jLayer) * d2VolTot_dPsi02(iLayer) * mLayerMatricHeadPrime(iLayer) + aJac(ixOffDiag(nrgState,watState),watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) & + + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) * cj + dCm_dPsi0(iLayer) * mLayerVolFracWatPrime(jLayer) & + + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + mLayerCm(jLayer) * d2VolTot_dPsi02(iLayer) * mLayerMatricHeadPrime(iLayer) if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(ixOffDiag(nrgState,watState),watState) = -LH_fu0*iden_water * dVolTot_dPsi0(iLayer) * cj & - - LH_fu0*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_dPsi02(iLayer) + aJac(ixOffDiag(nrgState,watState),watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + - LH_fu0*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_dPsi02(iLayer) + aJac(ixOffDiag(nrgState,watState),watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content endif ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above @@ -1015,11 +1017,12 @@ subroutine computJacobWithPrime(& endif ! - include derivatives in energy fluxes w.r.t. with respect to water for current layer - aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) * cj & - + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + mLayerCm(jLayer) * d2VolTot_dPsi02(iLayer) * mLayerMatricHeadPrime(iLayer) + aJac(nrgState,watState) = dVolHtCapBulk_dPsi0(iLayer) * mLayerTempPrime(jLayer) & + + mLayerCm(jLayer) * dVolTot_dPsi0(iLayer) * cj + dCm_dPsi0(iLayer) * mLayerVolFracWatPrime(jLayer) & + + (dt/mLayerDepth(jLayer))*(-dNrgFlux_dWatBelow(jLayer-1) + dNrgFlux_dWatAbove(jLayer)) + mLayerCm(jLayer) * d2VolTot_dPsi02(iLayer) * mLayerMatricHeadPrime(iLayer) if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(nrgState,watState) = -LH_fu0*iden_water * dVolTot_dPsi0(iLayer) * cj & - - LH_fu0*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_dPsi02(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content + - LH_fu0*iden_water * mLayerMatricHeadPrime(iLayer) * d2VolTot_dPsi02(iLayer) + aJac(nrgState,watState) ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content endif ! - include derivatives of heat capacity w.r.t water fluxes for surrounding layers starting with layer above diff --git a/build/source/engine/computResid.f90 b/build/source/engine/computResid.f90 index f55889544..48c9940a1 100644 --- a/build/source/engine/computResid.f90 +++ b/build/source/engine/computResid.f90 @@ -97,8 +97,8 @@ subroutine computResid(& mLayerVolFracWatTrial, & ! intent(in): trial value for the volumetric water in each snow and soil layer (-) mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liq in each snow and soil layer (-) ! input: enthalpy terms - scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (J kg K-1) - mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (J kg K-1) + scalarCanopyCm_noLHTrial, & ! intent(in): Cm without latent heat part for vegetation canopy (J kg K-1) + mLayerCm_noLHTrial, & ! intent(in): Cm without latent heat part for each snow and soil layer (J kg K-1) scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) scalarCanopyEnthTempTrial, & ! intent(in): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) mLayerEnthTempTrial, & ! intent(in): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) @@ -135,8 +135,8 @@ subroutine computResid(& real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial value for the volumetric water in each snow and soil layer (-) real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value for the volumetric water in each snow and soil layer (-) ! input: enthalpy terms - real(qp),intent(in) :: scalarCanopyCmTrial ! Cm of vegetation canopy (-) - real(qp),intent(in) :: mLayerCmTrial(:) ! Cm of each snow and soil layer (-) + real(rkind),intent(in) :: scalarCanopyCm_noLHTrial ! Cm without latent heat part for vegetation canopy (-) + real(rkind),intent(in) :: mLayerCm_noLHTrial(:) ! Cm without latent heat part for each snow and soil layer (-) real(rkind),intent(in) :: scalarCanairEnthalpyTrial ! trial value for enthalpy of the canopy air space (J m-3) real(rkind),intent(in) :: scalarCanopyEnthTempTrial ! trial value for temperature component of enthalpy of the vegetation canopy (J m-3) real(rkind),intent(in) :: mLayerEnthTempTrial(:) ! trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) @@ -252,7 +252,7 @@ subroutine computResid(& if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = ( scalarCanopyEnthTempTrial - scalarCanopyEnthTemp ) - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) else if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg)*( scalarCanairTempTrial - scalarCanairTemp ) - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) - if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg)*( scalarCanopyTempTrial - scalarCanopyTemp ) + scalarCanopyCmTrial*( scalarCanopyWatTrial - scalarCanopyWat )/canopyDepth & + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg)*( scalarCanopyTempTrial - scalarCanopyTemp ) + scalarCanopyCm_noLHTrial*( scalarCanopyWatTrial - scalarCanopyWat )/canopyDepth & - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) endif ! --> mass balance @@ -268,7 +268,7 @@ subroutine computResid(& if(mixdformNrg)then rVec( ixSnowSoilNrg(iLayer) ) = ( mLayerEnthTempTrial(iLayer) - mLayerEnthTemp(iLayer) ) - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) else - rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) )*( mLayerTempTrial(iLayer) - mLayerTemp(iLayer) ) + mLayerCmTrial(iLayer)*( mLayerVolFracWatTrial(iLayer) - mLayerVolFracWat(iLayer) ) & + rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) )*( mLayerTempTrial(iLayer) - mLayerTemp(iLayer) ) + mLayerCm_noLHTrial(iLayer)*( mLayerVolFracWatTrial(iLayer) - mLayerVolFracWat(iLayer) ) & - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) endif end do ! looping through non-missing energy state variables in the snow+soil domain diff --git a/build/source/engine/computResidWithPrime.f90 b/build/source/engine/computResidWithPrime.f90 index aac4f5e3a..d98df9a20 100644 --- a/build/source/engine/computResidWithPrime.f90 +++ b/build/source/engine/computResidWithPrime.f90 @@ -78,8 +78,8 @@ subroutine computResidWithPrime(& mLayerVolFracWatPrime, & ! intent(in): prime vector of the volumetric water in each snow and soil layer (s-1) mLayerVolFracLiqPrime, & ! intent(in): prime vector of the volumetric liq in each snow and soil layer (s-1) ! input: enthalpy terms - scalarCanopyCmTrial, & ! intent(in): Cm of vegetation canopy (J kg K-1) - mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (J kg K-1) + scalarCanopyCm_noLHTrial, & ! intent(in): Cm without latent heat part for vegetation canopy (J kg K-1) + mLayerCm_noLHTrial, & ! intent(in): Cm without latent heat part for each snow and soil layer (J kg K-1) scalarCanairEnthalpyPrime, & ! intent(in): prime value for the enthalpy of the canopy air space (W m-3) scalarCanopyEnthalpyPrime, & ! intent(in): prime value for the of enthalpy of the vegetation canopy (W m-3) mLayerEnthalpyPrime, & ! intent(in): prime vector of the of enthalpy of each snow and soil layer (W m-3) @@ -99,7 +99,7 @@ subroutine computResidWithPrime(& integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers in the snow+soil domain - logical(lgt),intent(in) :: enthalpyStateVec ! flag if enthalpy is state variable + logical(lgt),intent(in) :: enthalpyStateVec ! flag if enthalpy is state variable ! input: flux vectors real(qp),intent(in) :: sMul(:) ! NOTE: qp ! state vector multiplier (used in the residual calculations) real(rkind),intent(in) :: fVec(:) ! flux vector @@ -116,8 +116,8 @@ subroutine computResidWithPrime(& real(rkind),intent(in) :: mLayerVolFracWatPrime(:) ! prime vector of the volumetric water in each snow and soil layer (s-1) real(rkind),intent(in) :: mLayerVolFracLiqPrime(:) ! prime vector of the volumetric water in each snow and soil layer (s-1) ! input: enthalpy terms - real(qp),intent(in) :: scalarCanopyCmTrial ! Cm of vegetation canopy (-) - real(qp),intent(in) :: mLayerCmTrial(:) ! Cm of each snow and soil layer (-) + real(rkind),intent(in) :: scalarCanopyCm_noLHTrial ! Cm without latent heat part for vegetation canopy (-) + real(rkind),intent(in) :: mLayerCm_noLHTrial(:) ! Cm without latent heat part for each snow and soil layer (-) real(rkind),intent(in) :: scalarCanairEnthalpyPrime ! prime value for enthalpy of the canopy air space (W m-3) real(rkind),intent(in) :: scalarCanopyEnthalpyPrime ! prime value for enthalpy of the vegetation canopy (W m-3) real(rkind),intent(in) :: mLayerEnthalpyPrime(:) ! prime vector of enthalpy of each snow and soil layer (W m-3) @@ -219,7 +219,7 @@ subroutine computResidWithPrime(& if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = scalarCanopyEnthalpyPrime - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) else if(ixCasNrg/=integerMissing) rVec(ixCasNrg) = sMul(ixCasNrg) * scalarCanairTempPrime - ( fVec(ixCasNrg)*dt + rAdd(ixCasNrg) ) - if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime + scalarCanopyCmTrial * scalarCanopyWatPrime/canopyDepth & + if(ixVegNrg/=integerMissing) rVec(ixVegNrg) = sMul(ixVegNrg) * scalarCanopyTempPrime + scalarCanopyCm_noLHTrial * scalarCanopyWatPrime/canopyDepth & - ( fVec(ixVegNrg)*dt + rAdd(ixVegNrg) ) endif ! --> mass balance @@ -234,7 +234,7 @@ subroutine computResidWithPrime(& if(enthalpyStateVec)then rVec( ixSnowSoilNrg(iLayer) ) = mLayerEnthalpyPrime(iLayer) - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) else - rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) ) * mLayerTempPrime(iLayer) + mLayerCmTrial(iLayer) * mLayerVolFracWatPrime(iLayer) & + rVec( ixSnowSoilNrg(iLayer) ) = sMul( ixSnowSoilNrg(iLayer) ) * mLayerTempPrime(iLayer) + mLayerCm_noLHTrial(iLayer) * mLayerVolFracWatPrime(iLayer) & - ( fVec( ixSnowSoilNrg(iLayer) )*dt + rAdd( ixSnowSoilNrg(iLayer) ) ) endif end do ! looping through non-missing energy state variables in the snow+soil domain diff --git a/build/source/engine/enthalpyTemp.f90 b/build/source/engine/enthalpyTemp.f90 index e56f6ea9f..8a2cfc47e 100644 --- a/build/source/engine/enthalpyTemp.f90 +++ b/build/source/engine/enthalpyTemp.f90 @@ -616,7 +616,7 @@ subroutine T2enthTemp_soil(& else ! *** compute integral of mLayerPsiLiq from Tfreeze to layer temperature ! get the unfrozen water content - integral_unf = ( Tcrit - Tfreeze ) * volFracWat + integral_unf = diff0 * volFracWat ! get the frozen water content if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature @@ -1147,11 +1147,12 @@ subroutine enthalpy2T_soil(& err=0; message="enthalpy2T_soil/" Tcrit = crit_soilT(mLayerMatricHead) + diff0 = Tcrit - Tfreeze volFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) dTcrit_dPsi0 = merge(gravity*Tfreeze/LH_fus,0._rkind,mLayerMatricHead<=0._rkind) dvolFracWat_dPsi0 = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) entCrit = ( iden_water * Cp_water * volFracWat + soil_dens_intr * Cp_soil * (1._rkind - theta_sat) & - + iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) ) * (Tcrit - Tfreeze) + + iden_air * Cp_air * (1._rkind - theta_sat - volFracWat) ) * diff0 ! ***** get temperature if unfrozen soil if (mLayerEnthalpy>=entCrit )then @@ -1171,11 +1172,10 @@ subroutine enthalpy2T_soil(& ! *** compute integral of mLayerPsiLiq from Tfreeze to layer temperature ! get the unfrozen water content of enthalpy - integral_unf = ( Tcrit - Tfreeze ) * volFracWat ! unfrozen water content - if(computJac) dintegral_unf_dWat = dTcrit_dPsi0 * volFracWat + ( Tcrit - Tfreeze ) * dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + integral_unf = diff0 * volFracWat ! unfrozen water content + if(computJac) dintegral_unf_dWat = dTcrit_dPsi0 * volFracWat + diff0 * dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - ! get the frozen water content of enthalpy, statrt with lower limit of the integral - diff0 = Tcrit - Tfreeze + ! get the frozen water content of enthalpy, start with lower limit of the integral if (diff0<0._rkind)then if(use_lookup)then ! cubic spline interpolation for integral of mLayerPsiLiq from Tfreeze to layer temperature diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index 6db034003..b19d517c6 100644 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -212,6 +212,8 @@ subroutine eval8summa(& integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine real(rkind),dimension(nState) :: rVecScaled ! scaled residual vector + real(rkind) :: scalarCanopyCm_noLHTrial ! trial value of Cm for the canopy without latent heat part + real(rkind),dimension(nLayers) :: mLayerCm_noLHTrial ! trial vector of Cm for snow+soil without latent heat part character(LEN=256) :: cmessage ! error message of downwind routine logical(lgt) :: updateStateCp ! flag to indicate if we update Cp at each step for LHS, set with nrgConserv choice and updateCp_closedForm flag logical(lgt) :: updateFluxCp ! flag to indicate if we update Cp at each step for RHS, set with nrgConserv choice and updateCp_closedForm flag @@ -273,6 +275,7 @@ subroutine eval8summa(& dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + dCm_dPsi0 => deriv_data%var(iLookDERIV%dCm_dPsi0)%dat ,& ! intent(out): [dp(:)] derivative in heat capacity w.r.t. matric potential (J kg-1) dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk)%dat ,& ! intent(out): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) ! mapping @@ -282,7 +285,7 @@ subroutine eval8summa(& heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(out): [dp] volumetric heat capacity of vegetation canopy mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(out): [dp(:)] heat capacity for snow and soil ! Cm - canopyCmTrial => diag_data%var(iLookDIAG%scalarCanopyCm)%dat(1) ,& ! intent(out): [dp] Cm of the canopy + scalarCanopyCmTrial => diag_data%var(iLookDIAG%scalarCanopyCm)%dat(1) ,& ! intent(out): [dp] Cm of the canopy mLayerCmTrial => diag_data%var(iLookDIAG%mLayerCm)%dat & ! intent(out): [dp(:)] Cm of snow and soil ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- @@ -496,24 +499,32 @@ subroutine eval8summa(& if(needStateCm)then ! compute C_m call computCm(& + .true. , & ! intent(in): flag to denote BE solver, need to include latent heat part in Jacobian Cm ! input: state variables - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (-) - ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices + canopyDepth, & ! intent(in): canopy depth (m) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (-) + ! input data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices ! output - canopyCmTrial, & ! intent(inout): Cm for vegetation (J kg K-1) - mLayerCmTrial, & ! intent(inout): Cm for soil and snow (J kg K-1) - dCm_dTk, & ! intent(inout): derivative in Cm w.r.t. temperature (J kg K-2) - dCm_dTkCanopy, & ! intent(inout): derivative in Cm w.r.t. temperature (J kg K-2) - err,cmessage) ! intent(inout): error control + scalarCanopyCmTrial, & ! intent(inout): Cm for vegetation (J kg K-1) + scalarcanopyCm_noLHTrial, & ! intent(inout): Cm without latent heat part for vegetation (J kg K-1) + mLayerCmTrial, & ! intent(inout): Cm for soil and snow (J kg K-1) + mLayerCm_noLHTrial, & ! intent(inout): Cm without latent heat part for soil and snow (J kg K-1) + dCm_dPsi0, & ! intent(inout): derivative in Cm w.r.t. matric potential (J kg) + dCm_dTk, & ! intent(inout): derivative in Cm w.r.t. temperature (J kg K-2) + dCm_dTkCanopy, & ! intent(inout): derivative in Cm w.r.t. temperature (J kg K-2) + err,cmessage) ! intent(inout): error control else - canopyCmTrial = 0._qp - mLayerCmTrial = 0._qp - dCm_dTk = 0._rkind - dCm_dTkCanopy = 0._rkind + scalarCanopyCmTrial = 0._rkind + scalarCanopyCm_noLHTrial = 0._rkind + mLayerCmTrial = 0._rkind + mLayerCm_noLHTrial = 0._rkind + dCm_dPsi0 = 0._rkind + dCm_dTk = 0._rkind + dCm_dTkCanopy = 0._rkind endif ! needStateCm ! save the number of flux calls per time step @@ -603,21 +614,21 @@ subroutine eval8summa(& ! input: flux vectors sMul, & ! intent(in): state vector multiplier (used in the residual calculations) fluxVec, & ! intent(in): flux vector - ! input: state variables (a lready disaggregated into scalars and vectors) + ! input: state variables (already disaggregated into scalars and vectors) scalarCanairTempTrial, & ! intent(in): trial value for the temperature of the canopy air space (K) scalarCanopyTempTrial, & ! intent(in): trial value for the temperature of the vegetation canopy (K) scalarCanopyWatTrial, & ! intent(in): trial value for the water on the vegetation canopy (kg m-2) mLayerTempTrial, & ! intent(in): trial value for the temperature of each snow and soil layer (K) scalarAquiferStorageTrial, & ! intent(in): trial value of storage of water in the aquifer (m) - ! input: diagnostic variabl es defining the liquid water and ice content (function of state variables) + ! input: diagnostic variables defining the liquid water and ice content (function of state variables) scalarCanopyIceTrial, & ! intent(in): trial value for the ice on the vegetation canopy (kg m-2) scalarCanopyLiqTrial, & ! intent(in): trial value for the liq on the vegetation canopy (kg m-2) mLayerVolFracIceTrial, & ! intent(in): trial value for the volumetric ice in each snow and soil layer (-) mLayerVolFracWatTrial, & ! intent(in): trial value for the volumetric water in each snow and soil layer (-) mLayerVolFracLiqTrial, & ! intent(in): trial value for the volumetric liq in each snow and soil layer (-) ! input: enthalpy terms - canopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) - mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) + scalarCanopyCm_noLHTrial, & ! intent(in): Cm without latent heat part for vegetation canopy (-) + mLayerCm_noLHTrial, & ! intent(in): Cm without latent heat part for each snow and soil layer (-) scalarCanairEnthalpyTrial, & ! intent(in): trial value for enthalpy of the canopy air space (J m-3) scalarCanopyEnthTempTrial, & ! intent(in): trial value for temperature component of enthalpy of the vegetation canopy (J m-3) mLayerEnthTempTrial, & ! intent(in): trial vector of temperature component of enthalpy of each snow+soil layer (J m-3) diff --git a/build/source/engine/eval8summaWithPrime.f90 b/build/source/engine/eval8summaWithPrime.f90 index 255986013..cee1e6ee5 100644 --- a/build/source/engine/eval8summaWithPrime.f90 +++ b/build/source/engine/eval8summaWithPrime.f90 @@ -206,6 +206,8 @@ subroutine eval8summaWithPrime(& ! other local variables integer(i4b) :: jState(1) ! index of model state for the scalar solution within the soil domain integer(i4b) :: ixBeg,ixEnd ! index of indices for the soil compression routine + real(rkind) :: scalarCanopyCm_noLHTrial ! trial value of Cm for the canopy without latent heat part + real(rkind),dimension(nLayers) :: mLayerCm_noLHTrial ! trial vector of Cm for snow+soil without latent heat part character(LEN=256) :: cmessage ! error message of downwind routine logical(lgt) :: updateStateCp ! flag to indicate if we update Cp at each step for LHS, set with nrgConserv choice and updateCp_closedForm flag logical(lgt) :: updateFluxCp ! flag to indicate if we update Cp at each step for RHS, set with nrgConserv choice and updateCp_closedForm flag @@ -249,6 +251,7 @@ subroutine eval8summaWithPrime(& dThermalC_dWatBelow => deriv_data%var(iLookDERIV%dThermalC_dWatBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. water state in the layer above dThermalC_dTempAbove => deriv_data%var(iLookDERIV%dThermalC_dTempAbove)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above dThermalC_dTempBelow => deriv_data%var(iLookDERIV%dThermalC_dTempBelow)%dat ,& ! intent(out): [dp(:)] derivative in the thermal conductivity w.r.t. energy state in the layer above + dCm_dPsi0 => deriv_data%var(iLookDERIV%dCm_dPsi0)%dat ,& ! intent(out): [dp(:)] derivative in heat capacity w.r.t. matric potential (J kg-1 K-1) dCm_dTk => deriv_data%var(iLookDERIV%dCm_dTk)%dat ,& ! intent(out): [dp(:)] derivative in heat capacity w.r.t. temperature (J kg-1 K-2) dCm_dTkCanopy => deriv_data%var(iLookDERIV%dCm_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in heat capacity w.r.t. canopy temperature (J kg-1 K-2) ! mapping @@ -258,9 +261,9 @@ subroutine eval8summaWithPrime(& heatCapVegTrial => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(out): [dp] volumetric heat capacity of vegetation canopy mLayerHeatCapTrial => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(out): [dp(:)] heat capacity for snow and soil ! Cm - canopyCmTrial => diag_data%var(iLookDIAG%scalarCanopyCm)%dat(1) ,& ! intent(out): [dp] Cm of the canopy - mLayerCmTrial => diag_data%var(iLookDIAG%mLayerCm)%dat & ! intent(out): [dp(:)] Cm of snow and soil - ) ! association to variables in the data structures + scalarCanopyCmTrial => diag_data%var(iLookDIAG%scalarCanopyCm)%dat(1) ,& ! intent(out): [dp] Cm of the canopy + mLayerCmTrial => diag_data%var(iLookDIAG%mLayerCm)%dat & ! intent(out): [dp(:)] Cm of snow and soil + ) ! association to variables in the data structures ! -------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message="eval8summaWithPrime/" @@ -551,24 +554,32 @@ subroutine eval8summaWithPrime(& if(needStateCm)then ! compute C_m call computCm(& + .false. , & ! intent(in): flag to denote not BE solver, do not include latent heat part in Jacobian Cm ! input: state variables - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) - mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (-) + canopyDepth, & ! intent(in): canopy depth (m) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + mLayerTempTrial, & ! intent(in): trial value of layer temperature (K) + mLayerMatricHeadTrial, & ! intent(in): trial value for total water matric potential (-) ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices ! output - canopyCmTrial, & ! intent(inout): Cm for vegetation (J kg K-1) - mLayerCmTrial, & ! intent(inout): Cm for soil and snow (J kg K-1) - dCm_dTk, & ! intent(inout): derivative in Cm w.r.t. temperature (J kg K-2) - dCm_dTkCanopy, & ! intent(inout): derivative in Cm w.r.t. temperature (J kg K-2) - err,cmessage) ! intent(inout): error control + scalarCanopyCmTrial, & ! intent(inout): Cm for vegetation (J kg K-1) + scalarCanopyCm_noLHTrial, & ! intent(inout): Cm without latent heat part for vegetation (J kg K-1) + mLayerCmTrial, & ! intent(inout): Cm for soil and snow (J kg K-1) + mLayerCm_noLHTrial, & ! intent(inout): Cm without latent heat part for soil and snow (J kg K-1) + dCm_dPsi0, & ! intent(inout): derivative in Cm w.r.t. matric potential (J kg) + dCm_dTk, & ! intent(inout): derivative in Cm w.r.t. temperature (J kg K-2) + dCm_dTkCanopy, & ! intent(inout): derivative in Cm w.r.t. temperature (J kg K-2) + err,cmessage) ! intent(inout): error control else - canopyCmTrial = 0._qp - mLayerCmTrial = 0._qp - dCm_dTk = 0._rkind - dCm_dTkCanopy = 0._rkind + scalarCanopyCmTrial = 0._rkind + scalarCanopyCm_noLHTrial = 0._rkind + mLayerCmTrial = 0._rkind + mLayerCm_noLHTrial = 0._rkind + dCm_dPsi0 = 0._rkind + dCm_dTk = 0._rkind + dCm_dTkCanopy = 0._rkind endif ! needStateCm ! save the number of flux calls per time step @@ -669,8 +680,8 @@ subroutine eval8summaWithPrime(& mLayerVolFracWatPrime, & ! intent(in): prime vector of the volumetric water in each snow and soil layer (s-1) mLayerVolFracLiqPrime, & ! intent(in): prime vector of the volumetric liq in each snow and soil layer (s-1) ! input: enthalpy terms - canopyCmTrial, & ! intent(in): Cm of vegetation canopy (-) - mLayerCmTrial, & ! intent(in): Cm of each snow and soil layer (-) + scalarCanopyCm_noLHTrial, & ! intent(in): Cm without latent heat part for vegetation canopy (-) + mLayerCm_noLHTrial, & ! intent(in): Cm without latent heat part for each snow and soil layer (-) scalarCanairEnthalpyPrime, & ! intent(in): prime value for the enthalpy of the canopy air space (W m-3) scalarCanopyEnthalpyPrime, & ! intent(in): prime value for the of enthalpy of the vegetation canopy (W m-3) mLayerEnthalpyPrime, & ! intent(in): prime vector of the of enthalpy of each snow and soil layer (W m-3) diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 356d018f7..bef5d71ac 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -967,10 +967,10 @@ subroutine updateProg(dt,nSnow,nSoil,nLayers,untappedMelt,stateVecTrial,stateVec mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - mLayerTempPrime, & ! + mLayerTempPrime, & ! intent(inout): Prime vector of layer temperature (K) mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) - mLayerVolFracIcePrime, & ! + mLayerVolFracIcePrime, & ! intent(inout): Prime vector of volumetric ice water content (-) mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) ! output: error control From 6c0d0a5ec95a90a7c54beffd92e1ca553d37d441 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 27 Jan 2025 23:22:18 +0900 Subject: [PATCH 1466/1472] these fixes should sort scalar splitting for everything but scalarSoilBaseflow. --- build/source/engine/coupled_em.f90 | 2 +- build/source/engine/mDecisions.f90 | 2 +- build/source/engine/opSplittin.f90 | 9 +++++++-- build/source/engine/varSubstep.f90 | 16 ++++++++-------- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index a4b5e0ef0..2618c9917 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -218,7 +218,7 @@ subroutine coupled_em(& logical(lgt) :: tooMuchSublim ! flag to denote that there was too much sublimation in a given time step logical(lgt) :: doLayerMerge ! flag to denote the need to merge snow layers logical(lgt) :: pauseFlag ! flag to pause execution - logical(lgt),parameter :: backwardsCompatibility=.true. ! flag to denote a desire to ensure backwards compatibility with previous branches + logical(lgt),parameter :: backwardsCompatibility=.false. ! flag to denote a desire to ensure backwards compatibility with previous branches for end of time step flux only logical(lgt) :: checkMassBalance_ds ! flag to check the mass balance over the data step type(var_ilength) :: indx_temp ! temporary model index variables saved only on outer loop type(var_ilength) :: indx_temp0 ! temporary model index variables saved every time diff --git a/build/source/engine/mDecisions.f90 b/build/source/engine/mDecisions.f90 index bf9f3398c..74512d126 100644 --- a/build/source/engine/mDecisions.f90 +++ b/build/source/engine/mDecisions.f90 @@ -680,7 +680,7 @@ subroutine mDecisions(err,message) select case(model_decisions(iLookDECISIONS%groundwatr)%iDecision) case(qbaseTopmodel) if(model_decisions(iLookDECISIONS%hc_profile)%iDecision /= powerLaw_profile)then - message=trim(message)//'power-law transmissivity profile must be selected when using topmodel baseflow option' + message=trim(message)//'power-law hydraulic conductivity profile must be selected when using topmodel baseflow option' err=20; return end if end select diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 9b7e5c668..6cd1a9a9e 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -1204,7 +1204,12 @@ subroutine update_fluxMask if (iStateTypeSplit==massSplit .and. flux_meta(iVar)%vartype==iLookVarType%scalarv) then select case(iDomainSplit) case(snowSplit); if(iLayer==nSnow) fluxMask%var(iVar)%dat = desiredFlux - case(soilSplit); if(iLayer==nSnow+1) fluxMask%var(iVar)%dat = desiredFlux + case(soilSplit); + if(iVar==iLookFLUX%scalarSoilDrainage .or. iLookFLUX%scalarAquiferRecharge) then ! soil drainage changes with the bottom layer + if(iLayer==nLayers) fluxMask%var(iVar)%dat = desiredFlux + else ! other scalar variables in the soil domain change with the surface layer + if(iLayer==nSnow+1) fluxMask%var(iVar)%dat = desiredFlux + end if end select end if ! if hydrology split and scalar @@ -1213,7 +1218,7 @@ subroutine update_fluxMask end do ! end looping through layers case(aquiferSplit) ! fluxes through aquifer - fluxMask%var(iVar)%dat(:) = desiredFlux ! only would be firstFluxCall variables, no aquifer fluxes + fluxMask%var(iVar)%dat(:) = desiredFlux case default; err=20; message=trim(message)//'unable to identify split based on domain type'; return_flag=.true.; return ! check end select ! domain split end if ! end if flux is desired diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 9294554c6..8133fe18a 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -539,11 +539,11 @@ subroutine varSubstep(& if(count(ixLayerActive/=integerMissing)==nLayers)then flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght fluxCount%var(iVar)%dat(:) = fluxCount%var(iVar)%dat(:) + 1 - if (iVar==iLookFLUX%scalarSoilDrainage) then - print*, 'no Split scalarSoilDrainage = ', flux_temp%var(iVar)%dat(1)*iden_water*1800._rkind, flux_mean%var(iVar)%dat(1)*iden_water*1800._rkind + if (iVar==iLookFLUX%scalarSoilBaseflow) then + print*, 'no Split scalarSoilBaseflow = ', flux_temp%var(iVar)%dat(1)*iden_water*1800._rkind, flux_mean%var(iVar)%dat(1)*iden_water*1800._rkind endif - if (iVar== iLookFLUX%iLayerLiqFluxSoil) then - print*, 'no Split iLayerLiqFluxSoil = ', flux_temp%var(iVar)%dat(nSoil)*iden_water*1800._rkind, flux_mean%var(iVar)%dat(nSoil)*iden_water*1800._rkind + if (iVar== iLookFLUX%mLayerBaseflow) then + print*, 'no Split mLayerBaseflow) = ', flux_temp%var(iVar)%dat(nSoil)*iden_water*1800._rkind, flux_mean%var(iVar)%dat(nSoil)*iden_water*1800._rkind endif ! ** domain splitting else @@ -561,11 +561,11 @@ subroutine varSubstep(& fluxCount%var(iVar)%dat(ixLayer) = fluxCount%var(iVar)%dat(ixLayer) + 1 endif end do - if (iVar==iLookFLUX%scalarSoilDrainage) then - print*, 'scalarSoilDrainage = ', fluxMask%var(iVar)%dat(1),flux_temp%var(iVar)%dat(1)*iden_water*1800._rkind, flux_mean%var(iVar)%dat(1)*iden_water*1800._rkind + if (iVar==iLookFLUX%scalarSoilBaseflow) then + print*, 'yes Split scalarSoilBaseflow = ', fluxMask%var(iVar)%dat(1),flux_temp%var(iVar)%dat(1)*iden_water*1800._rkind, flux_mean%var(iVar)%dat(1)*iden_water*1800._rkind endif - if (iVar== iLookFLUX%iLayerLiqFluxSoil) then - print*, 'iLayerLiqFluxSoil = ', fluxMask%var(iVar)%dat(nSoil),flux_temp%var(iVar)%dat(nSoil)*iden_water*1800._rkind, flux_mean%var(iVar)%dat(nSoil)*iden_water*1800._rkind,ixMin(1),ixMax(1),nSoil + if (iVar== iLookFLUX%mLayerBaseflow) then + print*, 'yes Split mLayerBaseflow) = ', fluxMask%var(iVar)%dat(nSoil),flux_temp%var(iVar)%dat(nSoil)*iden_water*1800._rkind, flux_mean%var(iVar)%dat(nSoil)*iden_water*1800._rkind,ixMin(1),ixMax(1),nSoil endif endif ! (domain splitting) From f23dacb514b733d05ce78a9cc0004fc3cde711db Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Mon, 27 Jan 2025 23:23:04 +0900 Subject: [PATCH 1467/1472] forcing baseflow to be a value --- build/source/engine/groundwatr.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/groundwatr.f90 b/build/source/engine/groundwatr.f90 index 26dd6ed11..a98205207 100644 --- a/build/source/engine/groundwatr.f90 +++ b/build/source/engine/groundwatr.f90 @@ -164,7 +164,7 @@ subroutine groundwatr(& else; exit; end if ! only consider saturated layer at the bottom of the soil profile end do ! end looping through soil layers end if - + ixSaturation = 8 ! check for an early return (no layers are "active") if (ixSaturation > nSoil) then scalarExfiltration = 0._rkind ! exfiltration from the soil profile (m s-1) @@ -336,7 +336,7 @@ subroutine computeBaseflow(& end if ! compute the outflow from each layer (m3 s-1) - mLayerColumnOutflow(1:nSoil) = trSoil(1:nSoil)*tan_slope*contourLength + mLayerColumnOutflow(1:nSoil) = trSoil(1:nSoil)*tan_slope*contourLength + 0.001 ! compute total column inflow and total column outflow (m s-1) totalColumnInflow = sum(mLayerColumnInflow(1:nSoil))/HRUarea From 1b172f45247c4f7e9bbfaff11c1843527817b3b9 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 28 Jan 2025 07:59:37 +0900 Subject: [PATCH 1468/1472] flxMapping --- build/source/dshare/flxMapping.f90 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build/source/dshare/flxMapping.f90 b/build/source/dshare/flxMapping.f90 index 56d486ec5..780ba42f7 100644 --- a/build/source/dshare/flxMapping.f90 +++ b/build/source/dshare/flxMapping.f90 @@ -122,6 +122,7 @@ subroutine flxMapping(err,message) flux2state_orig(iLookFLUX%scalarCanopyTranspiration) = flux2state(state1=iname_nrgCanopy, state2=iname_nrgLayer) flux2state_orig(iLookFLUX%scalarCanopyEvaporation) = flux2state(state1=iname_nrgCanopy, state2=integerMissing) flux2state_orig(iLookFLUX%scalarGroundEvaporation) = flux2state(state1=iname_nrgCanopy, state2=iname_nrgLayer) + flux2state_orig(iLookFLUX%scalarAquiferTranspire) = flux2state(state1=iname_watCanopy, state2=integerMissing) flux2state_orig(iLookFLUX%mLayerTranspire) = flux2state(state1=iname_matLayer, state2=integerMissing) ! liquid and solid water fluxes through the canopy @@ -160,8 +161,9 @@ subroutine flxMapping(err,message) flux2state_orig(iLookFLUX%scalarSoilBaseflow) = flux2state(state1=iname_matLayer, state2=integerMissing) flux2state_orig(iLookFLUX%scalarSoilDrainage) = flux2state(state1=iname_matLayer, state2=integerMissing) flux2state_orig(iLookFLUX%scalarAquiferRecharge) = flux2state(state1=iname_matLayer, state2=integerMissing) - flux2state_orig(iLookFLUX%scalarAquiferTranspire) = flux2state(state1=iname_matLayer, state2=integerMissing) - flux2state_orig(iLookFLUX%scalarAquiferBaseflow) = flux2state(state1=iname_matLayer, state2=integerMissing) + + ! liquid water fluxes for the aquifer domain + flux2state_orig(iLookFLUX%scalarAquiferBaseflow) = flux2state(state1=iname_watAquifer, state2=integerMissing) ! derived variables flux2state_orig(iLookFLUX%scalarTotalET) = flux2state(state1=iname_nrgCanopy, state2=iname_nrgLayer) From 1621b5a55483a028ccb2aa5748501946e070ed68 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 28 Jan 2025 08:19:23 +0900 Subject: [PATCH 1469/1472] fix typos, deal with baseflow --- build/source/dshare/flxMapping.f90 | 31 +++++++++++++++--------------- build/source/engine/opSplittin.f90 | 3 ++- build/source/engine/varSubstep.f90 | 2 +- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/build/source/dshare/flxMapping.f90 b/build/source/dshare/flxMapping.f90 index 780ba42f7..560160e9f 100644 --- a/build/source/dshare/flxMapping.f90 +++ b/build/source/dshare/flxMapping.f90 @@ -7,24 +7,25 @@ module flxMapping_module subroutine flxMapping(err,message) USE nrtype ! data types - USE data_types, only: var_info ! data type for metadata structure - USE data_types, only: flux2state ! data type for extended metadata structure, for flux-to-state mapping + USE data_types, only: var_info ! data type for metadata structure + USE data_types, only: flux2state ! data type for extended metadata structure, for flux-to-state mapping ! structures of named variables - USE var_lookup, only: iLookFLUX ! named variables for local flux variables + USE var_lookup, only: iLookFLUX ! named variables for local flux variables ! metadata structures - USE globalData, only: flux_meta ! data structure for model fluxes - USE globalData, only: flux2state_orig ! data structure for flux-to-state mapping (original state variables) - USE globalData, only: flux2state_liq ! data structure for flux-to-state mapping (liquid water state variables) + USE globalData, only: flux_meta ! data structure for model fluxes + USE globalData, only: flux2state_orig ! data structure for flux-to-state mapping (original state variables) + USE globalData, only: flux2state_liq ! data structure for flux-to-state mapping (liquid water state variables) ! named variables to describe the state variable type - USE globalData, only: iname_nrgCanair ! named variable defining the energy of the canopy air space - USE globalData, only: iname_nrgCanopy ! named variable defining the energy of the vegetation canopy - USE globalData, only: iname_watCanopy ! named variable defining the mass of total water on the vegetation canopy - USE globalData, only: iname_liqCanopy ! named variable defining the mass of liquid water on the vegetation canopy - USE globalData, only: iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers - USE globalData, only: iname_watLayer ! named variable defining the total water state variable for snow+soil layers - USE globalData, only: iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers - USE globalData, only: iname_matLayer ! named variable defining the matric head state variable for soil layers - USE globalData, only: iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers + USE globalData, only: iname_nrgCanair ! named variable defining the energy of the canopy air space + USE globalData, only: iname_nrgCanopy ! named variable defining the energy of the vegetation canopy + USE globalData, only: iname_watCanopy ! named variable defining the mass of total water on the vegetation canopy + USE globalData, only: iname_liqCanopy ! named variable defining the mass of liquid water on the vegetation canopy + USE globalData, only: iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers + USE globalData, only: iname_watLayer ! named variable defining the total water state variable for snow+soil layers + USE globalData, only: iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers + USE globalData, only: iname_matLayer ! named variable defining the matric head state variable for soil layers + USE globalData, only: iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers + USE globalData, only: iname_watAquifer ! named variable defining the total water in the aquifer ! access missing values USE globalData,only:integerMissing ! missing integer implicit none diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 6cd1a9a9e..98c9c4188 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -1205,7 +1205,8 @@ subroutine update_fluxMask select case(iDomainSplit) case(snowSplit); if(iLayer==nSnow) fluxMask%var(iVar)%dat = desiredFlux case(soilSplit); - if(iVar==iLookFLUX%scalarSoilDrainage .or. iLookFLUX%scalarAquiferRecharge) then ! soil drainage changes with the bottom layer + if(iVar==iLookFLUX%scalarSoilDrainage .or. iVar==iLookFLUX%scalarAquiferRecharge & ! soil drainage, aq recharge changes with the bottom layer + .or. iVar==iLookFLUX%scalarSoilBaseflow) then ! soil baseflow changes with all layers, so compute after bottom layer if(iLayer==nLayers) fluxMask%var(iVar)%dat = desiredFlux else ! other scalar variables in the soil domain change with the surface layer if(iLayer==nSnow+1) fluxMask%var(iVar)%dat = desiredFlux diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 8133fe18a..9ba7b7240 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -519,7 +519,7 @@ subroutine varSubstep(& ! scalar compression if(.not.scalarSolution .or. iStateSplit==nSoil)& sumSoilCompress = sumSoilCompress + dtSubstep*diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression - ! vector compression + ! vector compression do iSoil=1,nSoil if(ixSoilOnlyHyd(iSoil)/=integerMissing)& sumLayerCompress(iSoil) = sumLayerCompress(iSoil) + dtSubstep*diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) ! soil compression in layers From 4d7e33c1912d7e4dca034ac9096b475f7e9701bc Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 28 Jan 2025 08:19:39 +0900 Subject: [PATCH 1470/1472] Revert "forcing baseflow to be a value" This reverts commit f23dacb514b733d05ce78a9cc0004fc3cde711db. --- build/source/engine/groundwatr.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/source/engine/groundwatr.f90 b/build/source/engine/groundwatr.f90 index a98205207..26dd6ed11 100644 --- a/build/source/engine/groundwatr.f90 +++ b/build/source/engine/groundwatr.f90 @@ -164,7 +164,7 @@ subroutine groundwatr(& else; exit; end if ! only consider saturated layer at the bottom of the soil profile end do ! end looping through soil layers end if - ixSaturation = 8 + ! check for an early return (no layers are "active") if (ixSaturation > nSoil) then scalarExfiltration = 0._rkind ! exfiltration from the soil profile (m s-1) @@ -336,7 +336,7 @@ subroutine computeBaseflow(& end if ! compute the outflow from each layer (m3 s-1) - mLayerColumnOutflow(1:nSoil) = trSoil(1:nSoil)*tan_slope*contourLength + 0.001 + mLayerColumnOutflow(1:nSoil) = trSoil(1:nSoil)*tan_slope*contourLength ! compute total column inflow and total column outflow (m s-1) totalColumnInflow = sum(mLayerColumnInflow(1:nSoil))/HRUarea From 13648e25e82e8eeca4e8f82343c9640496c05abd Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 28 Jan 2025 08:23:42 +0900 Subject: [PATCH 1471/1472] reverse all prints --- build/source/engine/computFlux.f90 | 8 ++++---- build/source/engine/coupled_em.f90 | 5 ++--- build/source/engine/varSubstep.f90 | 14 +------------- 3 files changed, 7 insertions(+), 20 deletions(-) diff --git a/build/source/engine/computFlux.f90 b/build/source/engine/computFlux.f90 index 8573aa911..54d32bf0c 100644 --- a/build/source/engine/computFlux.f90 +++ b/build/source/engine/computFlux.f90 @@ -613,7 +613,7 @@ end subroutine finalize_snowLiqFlx ! **** soilLiqFlx **** subroutine initialize_soilLiqFlx - call in_soilLiqFlx%initialize(nSnow,nSoil,nlayers,firstSplitOper,scalarSolution,firstFluxCall,& + call in_soilLiqFlx%initialize(nsnow,nSoil,nlayers,firstSplitOper,scalarSolution,firstFluxCall,& mLayerTempTrial,mLayerMatricHeadTrial,mLayerMatricHeadLiqTrial,mLayerVolFracLiqTrial,mLayerVolFracIceTrial,& above_soilLiqFluxDeriv,above_soildLiq_dTk,above_soilFracLiq,flux_data,deriv_data) call io_soilLiqFlx%initialize(nsoil,dHydCond_dMatric,flux_data,diag_data,deriv_data) @@ -627,14 +627,14 @@ subroutine finalize_soilLiqFlx associate(& mLayerLiqFluxSoil => flux_data%var(iLookFLUX%mLayerLiqFluxSoil)%dat, & ! intent(out): [dp] net liquid water flux for each soil layer (s-1) - iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat, & ! intent(out): [dp(0:)] vertical liquid water flux at soil layer interfaces (m s-1) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat, & ! intent(out): [dp(0:)] vertical liquid water flux at soil layer interfaces (-) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat, & ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) scalarMaxInfilRate => flux_data%var(iLookFLUX%scalarMaxInfilRate)%dat(1), & ! intent(out): [dp] maximum infiltration rate (m s-1) scalarRainPlusMelt => flux_data%var(iLookFLUX%scalarRainPlusMelt)%dat(1), & ! intent(out): [dp] rain plus melt (m s-1) scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl )%dat(1), & ! intent(out): [dp] soil control on infiltration, zero or one scalarInfilArea => diag_data%var(iLookDIAG%scalarInfilArea )%dat(1), & ! intent(out): [dp] fraction of unfrozen area where water can infiltrate (-) scalarFrozenArea => diag_data%var(iLookDIAG%scalarFrozenArea )%dat(1), & ! intent(out): [dp] fraction of area that is considered impermeable due to soil ice (-) - scalarSoilDrainage => flux_data%var(iLookFLUX%scalarSoilDrainage)%dat(1) ) ! intent(out): [dp] drainage from the soil profile (m s-1) + scalarSoilDrainage => flux_data%var(iLookFLUX%scalarSoilDrainage)%dat(1) ) ! intent(out): [dp] drainage from the soil profile (m s-1) ! calculate net liquid water fluxes for each soil layer (s-1) do iLayer=1,nSoil diff --git a/build/source/engine/coupled_em.f90 b/build/source/engine/coupled_em.f90 index 2618c9917..7d7f260cc 100644 --- a/build/source/engine/coupled_em.f90 +++ b/build/source/engine/coupled_em.f90 @@ -271,7 +271,7 @@ subroutine coupled_em(& real(rkind),allocatable :: innerBalanceLayerMass(:) ! inner step balances for domain with multiple layers real(rkind),allocatable :: innerBalanceLayerNrg(:) ! inner step balances for domain with multiple layers ! test balance checks - logical(lgt),parameter :: printBalance=.true. ! flag to print the balance checks + logical(lgt),parameter :: printBalance=.false. ! flag to print the balance checks real(rkind),allocatable :: liqSnowInit(:) ! volumetric liquid water conetnt of snow at the start of the time step real(rkind),allocatable :: liqSoilInit(:) ! soil moisture at the start of the time step ! timing information @@ -1565,7 +1565,7 @@ subroutine coupled_em(& ! check the soil water balance scalarSoilWatBalError = scalarTotalSoilWat - (balanceSoilWater0 + (balanceSoilInflux + balanceSoilET - balanceSoilBaseflow - balanceSoilDrainage - balanceSoilCompress) ) - !if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance_ds)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues + if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance_ds)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues write(*,*) 'solution method = ', ixSolution write(*,'(a,1x,f20.10)') 'data_step = ', data_step write(*,'(a,1x,f20.10)') 'balanceSoilCompress = ', balanceSoilCompress @@ -1578,7 +1578,6 @@ subroutine coupled_em(& write(*,'(a,1x,f20.10)') 'balanceSoilDrainage = ', balanceSoilDrainage write(*,'(a,1x,f20.10)') 'balanceSoilET = ', balanceSoilET write(*,'(a,1x,f20.10)') 'scalarSoilWatBalError = ', scalarSoilWatBalError - if(abs(scalarSoilWatBalError) > absConvTol_liquid*iden_water*10._rkind .and. checkMassBalance_ds)then ! NOTE: kg m-2, so need coarse tolerance to account for precision issues message=trim(message)//'soil hydrology does not balance' err=20; return end if diff --git a/build/source/engine/varSubstep.f90 b/build/source/engine/varSubstep.f90 index 9ba7b7240..1e2661594 100644 --- a/build/source/engine/varSubstep.f90 +++ b/build/source/engine/varSubstep.f90 @@ -527,7 +527,7 @@ subroutine varSubstep(& endif ! print progress - !if(globalPrintFlag)& + if(globalPrintFlag)& write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt ! increment fluxes @@ -539,12 +539,6 @@ subroutine varSubstep(& if(count(ixLayerActive/=integerMissing)==nLayers)then flux_mean%var(iVar)%dat(:) = flux_mean%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght fluxCount%var(iVar)%dat(:) = fluxCount%var(iVar)%dat(:) + 1 - if (iVar==iLookFLUX%scalarSoilBaseflow) then - print*, 'no Split scalarSoilBaseflow = ', flux_temp%var(iVar)%dat(1)*iden_water*1800._rkind, flux_mean%var(iVar)%dat(1)*iden_water*1800._rkind - endif - if (iVar== iLookFLUX%mLayerBaseflow) then - print*, 'no Split mLayerBaseflow) = ', flux_temp%var(iVar)%dat(nSoil)*iden_water*1800._rkind, flux_mean%var(iVar)%dat(nSoil)*iden_water*1800._rkind - endif ! ** domain splitting else ixMin=lbound(flux_data%var(iVar)%dat) @@ -561,12 +555,6 @@ subroutine varSubstep(& fluxCount%var(iVar)%dat(ixLayer) = fluxCount%var(iVar)%dat(ixLayer) + 1 endif end do - if (iVar==iLookFLUX%scalarSoilBaseflow) then - print*, 'yes Split scalarSoilBaseflow = ', fluxMask%var(iVar)%dat(1),flux_temp%var(iVar)%dat(1)*iden_water*1800._rkind, flux_mean%var(iVar)%dat(1)*iden_water*1800._rkind - endif - if (iVar== iLookFLUX%mLayerBaseflow) then - print*, 'yes Split mLayerBaseflow) = ', fluxMask%var(iVar)%dat(nSoil),flux_temp%var(iVar)%dat(nSoil)*iden_water*1800._rkind, flux_mean%var(iVar)%dat(nSoil)*iden_water*1800._rkind,ixMin(1),ixMax(1),nSoil - endif endif ! (domain splitting) endif ! (if the flux is desired) From bcf1488c572c3cb3259f2236c8f7455fcdcf9675 Mon Sep 17 00:00:00 2001 From: ashleymedin Date: Tue, 28 Jan 2025 20:35:38 +0900 Subject: [PATCH 1472/1472] missed param --- build/source/engine/opSplittin.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/source/engine/opSplittin.f90 b/build/source/engine/opSplittin.f90 index 98c9c4188..7003b4f8c 100644 --- a/build/source/engine/opSplittin.f90 +++ b/build/source/engine/opSplittin.f90 @@ -53,6 +53,7 @@ module opSplittin_module USE var_lookup,only:iLookFLUX ! named variables for structure elements USE var_lookup,only:iLookINDEX ! named variables for structure elements USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure +USE var_lookup,only:iLookPARAM ! named variables for elements of the parameter structure ! look up structure for variable types USE var_lookup,only:iLookVarType

      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

      c;HP93~7FdMA2V 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

    2. xjafS!MFe09Gw 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 z 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